From 73a42ce9410a0cc0c4a91469e6ace2858e26355d Mon Sep 17 00:00:00 2001 From: Leorize Date: Thu, 7 May 2020 14:43:53 -0500 Subject: [PATCH 0001/3768] nativesockets: add missing inheritable pass-through --- lib/pure/nativesockets.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim index 8d0d6426d..45ea93235 100644 --- a/lib/pure/nativesockets.nim +++ b/lib/pure/nativesockets.nim @@ -231,7 +231,7 @@ proc createNativeSocket*(domain: Domain = AF_INET, ## ## `inheritable` decides if the resulting SocketHandle can be inherited ## by child processes. - createNativeSocket(toInt(domain), toInt(sockType), toInt(protocol)) + createNativeSocket(toInt(domain), toInt(sockType), toInt(protocol), inheritable) proc newNativeSocket*(domain: Domain = AF_INET, sockType: SockType = SOCK_STREAM, From ee15437aedcf7c4f29f75101a6af7bc454830cda Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 7 May 2020 15:01:46 -0700 Subject: [PATCH 0002/3768] fix root cause of https://github.com/dom96/choosenim/issues/193; config/config.nims should get installed --- compiler/installer.ini | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/compiler/installer.ini b/compiler/installer.ini index 939b328d0..fdb717b61 100644 --- a/compiler/installer.ini +++ b/compiler/installer.ini @@ -35,9 +35,8 @@ App: Console License: "copying.txt" [Config] -Files: "config/nim.cfg" -Files: "config/nimdoc.cfg" -Files: "config/nimdoc.tex.cfg" +Files: "config/*.cfg" +Files: "config/config.nims" [Documentation] ; Files: "doc/*.html" From de27910ab72dca681188953ae963802520271e43 Mon Sep 17 00:00:00 2001 From: hlaaftana <10591326+hlaaftana@users.noreply.github.com> Date: Fri, 8 May 2020 08:54:46 +0300 Subject: [PATCH 0003/3768] make `from` an operator (#14241) --- changelog.md | 2 ++ compiler/layouter.nim | 2 +- compiler/lexer.nim | 2 +- compiler/parser.nim | 8 ++++--- doc/grammar.txt | 2 +- doc/manual.rst | 32 ++++++++++++++-------------- tests/parser/tstatementoperators.nim | 12 +++++++++++ 7 files changed, 38 insertions(+), 22 deletions(-) create mode 100644 tests/parser/tstatementoperators.nim diff --git a/changelog.md b/changelog.md index 9ae50f1f3..32d7eba55 100644 --- a/changelog.md +++ b/changelog.md @@ -120,6 +120,8 @@ with writing typed macros. Old behavior for backwards compatiblity can be restored with command line switch `--useVersion:1.0`. +- The keyword `from` is now usable as an operator. + ## Compiler changes - Specific warnings can now be turned into errors via `--warningAsError[X]:on|off`. diff --git a/compiler/layouter.nim b/compiler/layouter.nim index e137ee8a0..2477458f1 100644 --- a/compiler/layouter.nim +++ b/compiler/layouter.nim @@ -327,7 +327,7 @@ const splitters = openPars + {tkComma, tkSemiColon} # do not add 'tkColon' here! oprSet = {tkOpr, tkDiv, tkMod, tkShl, tkShr, tkIn, tkNotin, tkIs, - tkIsnot, tkNot, tkOf, tkAs, tkDotDot, tkAnd, tkOr, tkXor} + tkIsnot, tkNot, tkOf, tkAs, tkFrom, tkDotDot, tkAnd, tkOr, tkXor} template goodCol(col): bool = col >= em.maxLineLen div 2 diff --git a/compiler/lexer.nim b/compiler/lexer.nim index e91a6415a..bd71a6ad5 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -967,7 +967,7 @@ proc getPrecedence*(tok: TToken, strongSpaces: bool): int = of '?': result = 2 else: considerAsgn(2) of tkDiv, tkMod, tkShl, tkShr: result = 9 - of tkIn, tkNotin, tkIs, tkIsnot, tkOf, tkAs: result = 5 + of tkIn, tkNotin, tkIs, tkIsnot, tkOf, tkAs, tkFrom: result = 5 of tkDotDot: result = 6 of tkAnd: result = 4 of tkOr, tkXor, tkPtr, tkRef: result = 3 diff --git a/compiler/parser.nim b/compiler/parser.nim index 4ea751057..9d1f6f97a 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -269,7 +269,8 @@ proc isRightAssociative(tok: TToken): bool {.inline.} = proc isOperator(tok: TToken): bool = ## Determines if the given token is an operator type token. tok.tokType in {tkOpr, tkDiv, tkMod, tkShl, tkShr, tkIn, tkNotin, tkIs, - tkIsnot, tkNot, tkOf, tkAs, tkDotDot, tkAnd, tkOr, tkXor} + tkIsnot, tkNot, tkOf, tkAs, tkFrom, tkDotDot, tkAnd, + tkOr, tkXor} proc isUnary(p: TParser): bool = ## Check if the current parser token is a unary operator @@ -294,7 +295,7 @@ proc checkBinary(p: TParser) {.inline.} = #| #| operator = OP0 | OP1 | OP2 | OP3 | OP4 | OP5 | OP6 | OP7 | OP8 | OP9 #| | 'or' | 'xor' | 'and' -#| | 'is' | 'isnot' | 'in' | 'notin' | 'of' +#| | 'is' | 'isnot' | 'in' | 'notin' | 'of' | 'as' | 'from' #| | 'div' | 'mod' | 'shl' | 'shr' | 'not' | 'static' | '..' #| #| prefixOperator = operator @@ -555,7 +556,7 @@ proc parsePar(p: var TParser): PNode = optInd(p, result) flexComment(p, result) if p.tok.tokType in {tkDiscard, tkInclude, tkIf, tkWhile, tkCase, - tkTry, tkDefer, tkFinally, tkExcept, tkFor, tkBlock, + tkTry, tkDefer, tkFinally, tkExcept, tkBlock, tkConst, tkLet, tkWhen, tkVar, tkFor, tkMixin}: # XXX 'bind' used to be an expression, so we exclude it here; @@ -2193,6 +2194,7 @@ proc complexOrSimpleStmt(p: var TParser): PNode = #| | blockStmt | staticStmt | deferStmt | asmStmt #| | 'proc' routine #| | 'method' routine + #| | 'func' routine #| | 'iterator' routine #| | 'macro' routine #| | 'template' routine diff --git a/doc/grammar.txt b/doc/grammar.txt index 06eebbc1b..9d952d372 100644 --- a/doc/grammar.txt +++ b/doc/grammar.txt @@ -6,7 +6,7 @@ colon = ':' COMMENT? colcom = ':' COMMENT? operator = OP0 | OP1 | OP2 | OP3 | OP4 | OP5 | OP6 | OP7 | OP8 | OP9 | 'or' | 'xor' | 'and' - | 'is' | 'isnot' | 'in' | 'notin' | 'of' | 'as' + | 'is' | 'isnot' | 'in' | 'notin' | 'of' | 'as' | 'from' | | 'div' | 'mod' | 'shl' | 'shr' | 'not' | 'static' | '..' prefixOperator = operator optInd = COMMENT? IND? diff --git a/doc/manual.rst b/doc/manual.rst index 85b307c80..02ccace2c 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -565,7 +565,7 @@ following characters:: defined here.) These keywords are also operators: -``and or not xor shl shr div mod in notin is isnot of as``. +``and or not xor shl shr div mod in notin is isnot of as from``. `.`:tok: `=`:tok:, `:`:tok:, `::`:tok: are not available as general operators; they are used for other notational purposes. @@ -638,21 +638,21 @@ has the second lowest precedence. Otherwise precedence is determined by the first character. -================ ================================================== ================== =============== -Precedence level Operators First character Terminal symbol -================ ================================================== ================== =============== - 10 (highest) ``$ ^`` OP10 - 9 ``* / div mod shl shr %`` ``* % \ /`` OP9 - 8 ``+ -`` ``+ - ~ |`` OP8 - 7 ``&`` ``&`` OP7 - 6 ``..`` ``.`` OP6 - 5 ``== <= < >= > != in notin is isnot not of as`` ``= < > !`` OP5 - 4 ``and`` OP4 - 3 ``or xor`` OP3 - 2 ``@ : ?`` OP2 - 1 *assignment operator* (like ``+=``, ``*=``) OP1 - 0 (lowest) *arrow like operator* (like ``->``, ``=>``) OP0 -================ ================================================== ================== =============== +================ ======================================================= ================== =============== +Precedence level Operators First character Terminal symbol +================ ======================================================= ================== =============== + 10 (highest) ``$ ^`` OP10 + 9 ``* / div mod shl shr %`` ``* % \ /`` OP9 + 8 ``+ -`` ``+ - ~ |`` OP8 + 7 ``&`` ``&`` OP7 + 6 ``..`` ``.`` OP6 + 5 ``== <= < >= > != in notin is isnot not of as from`` ``= < > !`` OP5 + 4 ``and`` OP4 + 3 ``or xor`` OP3 + 2 ``@ : ?`` OP2 + 1 *assignment operator* (like ``+=``, ``*=``) OP1 + 0 (lowest) *arrow like operator* (like ``->``, ``=>``) OP0 +================ ======================================================= ================== =============== Whether an operator is used a prefix operator is also affected by preceding diff --git a/tests/parser/tstatementoperators.nim b/tests/parser/tstatementoperators.nim new file mode 100644 index 000000000..d2b85bb4b --- /dev/null +++ b/tests/parser/tstatementoperators.nim @@ -0,0 +1,12 @@ +discard """ + nimout: ''' +Infix + Ident "from" + Ident "a" + Ident "b" +''' +""" + +from macros import dumpTree + +dumpTree(a from b) \ No newline at end of file From 120ea906fbd0fc70b366a85c25713bd28673872c Mon Sep 17 00:00:00 2001 From: Clyybber Date: Fri, 8 May 2020 15:54:34 +0200 Subject: [PATCH 0004/3768] Fix #14270 and add testcases (#14276) --- lib/system/repr_v2.nim | 8 ++++++-- tests/arc/trepr.nim | 30 ++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/lib/system/repr_v2.nim b/lib/system/repr_v2.nim index fa04ba5a9..407a06844 100644 --- a/lib/system/repr_v2.nim +++ b/lib/system/repr_v2.nim @@ -106,8 +106,12 @@ proc repr*[T: tuple|object](x: T): string = proc repr*[T](x: ref T | ptr T): string = if isNil(x): return "nil" - result = $typeof(x) - reprObject(result, x[]) + when T is object: + result = $typeof(x) + reprObject(result, x[]) + else: + result = when typeof(x) is ref: "ref " else: "ptr " + result.add repr(x[]) proc collectionToRepr[T](x: T, prefix, separator, suffix: string): string = result = prefix diff --git a/tests/arc/trepr.nim b/tests/arc/trepr.nim index 7a92112ed..e24c112ac 100644 --- a/tests/arc/trepr.nim +++ b/tests/arc/trepr.nim @@ -3,6 +3,12 @@ discard """ nimout: '''(a: true, n: doAssert) Table[system.string, trepr.MyType](data: @[], counter: 0) nil +''' + output: ''' +nil +2 +Obj(member: ref @[hello]) +ref (member: ref @[hello]) ''' """ import tables @@ -32,3 +38,27 @@ macro dumpSym(a: typed) = dumpSym(doAssert) +# bug 13731 + +import os +var a: File +echo repr a + +# bug 13872 + +echo repr(2'u16) + +# bug 14270 + +type + Obj = ref object + member: ref seq[string] + +var c = Obj(member: new seq[string]) +c.member[] = @["hello"] +echo c.repr + +var c2 = new tuple[member: ref seq[string]] +c2.member = new seq[string] +c2.member[] = @["hello"] +echo c2.repr From 363b7ff3b80d2b50cda901d1f6e6b01e67747ed2 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 8 May 2020 07:13:07 -0700 Subject: [PATCH 0005/3768] fix https://github.com/nim-lang/Nim/issues/14275 querySetting(nimcacheDir) works even if implicitly set (#14277) --- compiler/vmops.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/vmops.nim b/compiler/vmops.nim index ef36419a7..54b2877e7 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -112,7 +112,7 @@ when defined(nimHasInvariant): of arguments: result = conf.arguments of outFile: result = conf.outFile.string of outDir: result = conf.outDir.string - of nimcacheDir: result = conf.nimcacheDir.string + of nimcacheDir: result = conf.getNimcacheDir().string of projectName: result = conf.projectName of projectPath: result = conf.projectPath.string of projectFull: result = conf.projectFull.string From 4bd77cf045e5563ea1150d5106fec1f6f66d5e2d Mon Sep 17 00:00:00 2001 From: Clyybber Date: Fri, 8 May 2020 16:13:53 +0200 Subject: [PATCH 0006/3768] Fix typo --- doc/manual_experimental.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual_experimental.rst b/doc/manual_experimental.rst index 45732fae2..1398d1a9b 100644 --- a/doc/manual_experimental.rst +++ b/doc/manual_experimental.rst @@ -104,7 +104,7 @@ cannot have the type ``void``. Covariance ========== -Covariance in Nim can be introduced only though pointer-like types such +Covariance in Nim can be introduced only through pointer-like types such as ``ptr`` and ``ref``. Sequence, Array and OpenArray types, instantiated with pointer-like types will be considered covariant if and only if they are also immutable. The introduction of a ``var`` modifier or additional From 411be506a30a701ab30138c576e21bd15dfaffe4 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 8 May 2020 09:54:16 -0700 Subject: [PATCH 0007/3768] --hint:processing (+friends) is now supported and means `--hint:processing:on`, like all other bool flags (#14271) --- changelog.md | 3 +++ compiler/commands.nim | 8 +++++--- lib/system.nim | 2 +- tests/newconfig/tfoo.nim | 2 +- tests/newconfig/tfoo.nims | 21 ++++++++++++++++++++- 5 files changed, 30 insertions(+), 6 deletions(-) diff --git a/changelog.md b/changelog.md index 32d7eba55..cdd6c031a 100644 --- a/changelog.md +++ b/changelog.md @@ -137,6 +137,9 @@ nim r compiler/nim.nim --fullhelp # no recompilation nim r --nimcache:/tmp main # binary saved to /tmp/main ``` +- `--hint:processing` is now supported and means `--hint:processing:on` + (likewise with other hints and warnings), which is consistent with all other bool flags. + (since 1.3.3). ## Tool changes diff --git a/compiler/commands.nim b/compiler/commands.nim index 949d6cae1..e980d64fc 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -176,7 +176,7 @@ proc expectNoArg(conf: ConfigRef; switch, arg: string, pass: TCmdLinePass, info: proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass, info: TLineInfo; orig: string; conf: ConfigRef) = - var id = "" # arg = key:val or [key]:val; with val=on|off + var id = "" # arg = key or [key] or key:val or [key]:val; with val=on|off var i = 0 var n = hintMin var isBracket = false @@ -190,7 +190,8 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass, if i < arg.len and arg[i] == ']': inc(i) else: invalidCmdLineOption(conf, pass, orig, info) - if i < arg.len and (arg[i] in {':', '='}): inc(i) + if i == arg.len: discard + elif i < arg.len and (arg[i] in {':', '='}): inc(i) else: invalidCmdLineOption(conf, pass, orig, info) if state == wHint: let x = findStr(lineinfos.HintsToStr, id) @@ -201,7 +202,8 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass, if x >= 0: n = TNoteKind(x + ord(warnMin)) else: localError(conf, info, "unknown warning: " & id) - let val = substr(arg, i).normalize + var val = substr(arg, i).normalize + if val == "": val = "on" if val notin ["on", "off"]: localError(conf, info, errOnOrOffExpectedButXFound % arg) elif n notin conf.cmdlineNotes or pass == passCmd1: diff --git a/lib/system.nim b/lib/system.nim index 3f5015542..8878277b7 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2068,7 +2068,7 @@ const ## is the minor number of Nim's version. ## Odd for devel, even for releases. - NimPatch* {.intdefine.}: int = 1 + NimPatch* {.intdefine.}: int = 3 ## is the patch number of Nim's version. import system/dollars diff --git a/tests/newconfig/tfoo.nim b/tests/newconfig/tfoo.nim index f332cd6d4..6654202c5 100644 --- a/tests/newconfig/tfoo.nim +++ b/tests/newconfig/tfoo.nim @@ -1,5 +1,5 @@ discard """ - cmd: "nim default $file" + cmd: "nim default --hint:cc:off --hint:cc $file" output: '''hello world! 0.5''' nimout: '''[NimScript] exec: gcc -v''' """ diff --git a/tests/newconfig/tfoo.nims b/tests/newconfig/tfoo.nims index 9e6405a27..4186ee047 100644 --- a/tests/newconfig/tfoo.nims +++ b/tests/newconfig/tfoo.nims @@ -7,7 +7,26 @@ exec "gcc -v" --path: "../friends" warning("uninit", off) -hint("processing", off) + +block: # supported syntaxes for hint,warning,switch + --hint:processing + hint("processing", on) + hint("processing", off) + switch("hint", "processing") + switch("hint", "processing:on") + switch("hint", "processing:off") + switch("hint", "[processing]") + switch("hint", "[processing]:on") + switch("hint", "[processing]:off") # leave it off + + --warning:UnusedImport + switch("warning", "UnusedImport:off") + switch("warning", "UnusedImport:on") + switch("warning", "[UnusedImport]:off") + switch("warning", "[UnusedImport]:on") + switch("warning", "[UnusedImport]") + switch("warning", "UnusedImport") # leave it on + #--verbosity:2 patchFile("stdlib", "math", "mymath") From 926ae2ec6451a2c37a6e7f9c0f0a5c8f428fd5f3 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Fri, 8 May 2020 18:48:44 -0600 Subject: [PATCH 0008/3768] Add ability for rst parser to continue on errors --- compiler/commands.nim | 2 ++ compiler/docgen.nim | 14 ++++++++-- compiler/options.nim | 1 + doc/advopt.txt | 1 + lib/packages/docutils/rst.nim | 51 ++++++++++++++++++++++++++++------- 5 files changed, 58 insertions(+), 11 deletions(-) diff --git a/compiler/commands.nim b/compiler/commands.nim index 949d6cae1..9a7052009 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -437,6 +437,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; conf.docSeeSrcUrl = arg of "docroot": conf.docRoot = if arg.len == 0: "@default" else: arg + of "docignoreparseerrors": + conf.docIgnoreParseErrors = true of "mainmodule", "m": discard "allow for backwards compatibility, but don't do anything" of "define", "d": diff --git a/compiler/docgen.nim b/compiler/docgen.nim index a42be9a9c..b1cde838d 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -146,8 +146,13 @@ proc parseRst(text, filename: string, rstOptions: RstParseOptions; conf: ConfigRef): PRstNode = declareClosures() + var msgHandler: MsgHandler + if conf.docIgnoreParseErrors: + msgHandler = rst.noExceptionMsgHandler + else: + msgHandler = compilerMsgHandler result = rstParse(text, filename, line, column, hasToc, rstOptions, - docgenFindFile, compilerMsgHandler) + docgenFindFile, msgHandler) proc getOutFile2(conf: ConfigRef; filename: RelativeFile, ext: string, dir: RelativeDir; guessTarget: bool): AbsoluteFile = @@ -170,9 +175,14 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef, result.module = module result.conf = conf result.cache = cache + var msgHandler: MsgHandler + if conf.docIgnoreParseErrors: + msgHandler = rst.noExceptionMsgHandler + else: + msgHandler = defaultMsgHandler initRstGenerator(result[], (if conf.cmd != cmdRst2tex: outHtml else: outLatex), conf.configVars, filename.string, {roSupportRawDirective, roSupportMarkdown}, - docgenFindFile, compilerMsgHandler) + docgenFindFile, msgHandler) if conf.configVars.hasKey("doc.googleAnalytics"): result.analytics = """ diff --git a/compiler/options.nim b/compiler/options.nim index 5475bc07b..a9b3be519 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -274,6 +274,7 @@ type docSeeSrcUrl*: string # if empty, no seeSrc will be generated. \ # The string uses the formatting variables `path` and `line`. docRoot*: string ## see nim --fullhelp for --docRoot + docIgnoreParseErrors*: bool ## continue parsing docs if errors occur # the used compiler cIncludes*: seq[AbsoluteDir] # directories to search for included files diff --git a/doc/advopt.txt b/doc/advopt.txt index c877b02e9..42ff18cd2 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -79,6 +79,7 @@ Advanced options: --docSeeSrcUrl:url activate 'see source' for doc and doc2 commands (see doc.item.seesrc in config/nimdoc.cfg) --docInternal also generate documentation for non-exported symbols + --docIgnoreParseErrors continue parsing docs if rst errors occur --lineDir:on|off generation of #line directive on|off --embedsrc:on|off embeds the original source code as comments in the generated output diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 50b855662..56772ac89 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -312,6 +312,14 @@ proc defaultMsgHandler*(filename: string, line, col: int, msgkind: MsgKind, if mc == mcError: raise newException(EParseError, message) else: writeLine(stdout, message) +proc noExceptionMsgHandler*(filename: string, line, col: int, msgkind: MsgKind, + arg: string) {.procvar.} = + let mc = msgkind.whichMsgClass + let a = messages[msgkind] % arg + let message = "$1($2, $3) $4: $5" % [filename, $line, $col, $mc, a] + if mc == mcError: writeLine(stderr, message) + else: writeLine(stdout, message) + proc defaultFindFile*(filename: string): string {.procvar.} = if existsFile(filename): result = filename else: result = "" @@ -755,13 +763,23 @@ proc parseUntil(p: var RstParser, father: PRstNode, postfix: string, inc(p.idx) if p.tok[p.idx].kind == tkIndent: rstMessage(p, meExpected, postfix, line, col) + add(father, newLeaf(p)) + inc(p.idx) break of tkWhite: add(father, newRstNode(rnLeaf, " ")) inc(p.idx) - else: rstMessage(p, meExpected, postfix, line, col) + else: + rstMessage(p, meExpected, postfix, line, col) + add(father, newLeaf(p)) + inc(p.idx) + break proc parseMarkdownCodeblock(p: var RstParser): PRstNode = + let backupIndex = p.idx + + # skip ``` + inc(p.idx) var args = newRstNode(rnDirArg) if p.tok[p.idx].kind == tkWord: add(args, newLeaf(p)) @@ -773,7 +791,10 @@ proc parseMarkdownCodeblock(p: var RstParser): PRstNode = case p.tok[p.idx].kind of tkEof: rstMessage(p, meExpected, "```") - break + p.idx = backupIndex + result = newLeaf(p) + inc(p.idx) + return of tkPunct: if p.tok[p.idx].symbol == "```": inc(p.idx) @@ -817,36 +838,47 @@ proc parseMarkdownLink(p: var RstParser; father: PRstNode): bool = result = true proc parseInline(p: var RstParser, father: PRstNode) = + template addOrBackup(endTok) = + let prevTok = if (p.idx-1) < len(p.tok): p.tok[p.idx-1].symbol else: "" + + if p.idx >= len(p.tok) - 1 and prevTok != endTok: + # we failed, so + p.idx = backupIndex + add(father, newLeaf(p)) + inc(p.idx) + else: + add(father, n) + + let backupIndex = p.idx case p.tok[p.idx].kind of tkPunct: if isInlineMarkupStart(p, "***"): var n = newRstNode(rnTripleEmphasis) parseUntil(p, n, "***", true) - add(father, n) + addOrBackup("***") elif isInlineMarkupStart(p, "**"): var n = newRstNode(rnStrongEmphasis) parseUntil(p, n, "**", true) - add(father, n) + addOrBackup("**") elif isInlineMarkupStart(p, "*"): var n = newRstNode(rnEmphasis) parseUntil(p, n, "*", true) - add(father, n) + addOrBackup("*") elif roSupportMarkdown in p.s.options and p.tok[p.idx].symbol == "```": - inc(p.idx) add(father, parseMarkdownCodeblock(p)) elif isInlineMarkupStart(p, "``"): var n = newRstNode(rnInlineLiteral) parseUntil(p, n, "``", false) - add(father, n) + addOrBackup("``") elif isInlineMarkupStart(p, "`"): var n = newRstNode(rnInterpretedText) parseUntil(p, n, "`", true) n = parsePostfix(p, n) - add(father, n) + addOrBackup("`") elif isInlineMarkupStart(p, "|"): var n = newRstNode(rnSubstitutionReferences) parseUntil(p, n, "|", false) - add(father, n) + addOrBackup("|") elif roSupportMarkdown in p.s.options and p.tok[p.idx].symbol == "[" and p.tok[p.idx+1].symbol != "[" and parseMarkdownLink(p, father): @@ -1773,6 +1805,7 @@ proc parseDotDot(p: var RstParser): PRstNode = b = dirImage(p) else: rstMessage(p, meInvalidDirective, p.tok[p.idx].symbol) + inc(p.idx) setSub(p, addNodes(a), b) elif match(p, p.idx, " ["): # footnotes, citations From 15520a91e96cfa42ce3254c74c792d81de3c03f0 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Fri, 8 May 2020 18:52:00 -0600 Subject: [PATCH 0009/3768] Add ignore parse errors to changelog --- changelog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/changelog.md b/changelog.md index 9ae50f1f3..ab96b7e51 100644 --- a/changelog.md +++ b/changelog.md @@ -138,3 +138,5 @@ ## Tool changes +- The `nim doc` command now supports ignoring RST errors when parsing + by using the `--docIgnoreParseErrors` flag From edcad004eea459f7978b181f4bbf231b70ebb755 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Fri, 8 May 2020 20:12:05 -0600 Subject: [PATCH 0010/3768] Add test for ignore parse errors --- tests/nimdoc/invalidrst.nim | 4 ++++ tests/trunner.nim | 28 +++++++++++++++++++--------- 2 files changed, 23 insertions(+), 9 deletions(-) create mode 100644 tests/nimdoc/invalidrst.nim diff --git a/tests/nimdoc/invalidrst.nim b/tests/nimdoc/invalidrst.nim new file mode 100644 index 000000000..fc2b2ebd5 --- /dev/null +++ b/tests/nimdoc/invalidrst.nim @@ -0,0 +1,4 @@ +proc foo*() = + ## a ***bunch of foo ``invalid'' breaking symbols *** + ## ```more symbols ** *** * | ` [ + discard diff --git a/tests/trunner.nim b/tests/trunner.nim index f71c324a2..461362baf 100644 --- a/tests/trunner.nim +++ b/tests/trunner.nim @@ -6,7 +6,7 @@ discard """ ## tests that don't quite fit the mold and are easier to handle via `execCmdEx` ## A few others could be added to here to simplify code. -import std/[strformat,os,osproc] +import std/[strformat,os,osproc,unittest] const nim = getCurrentCompilerExe() @@ -15,8 +15,9 @@ const mode = elif defined(cpp): "cpp" else: static: doAssert false +const testsDir = currentSourcePath().parentDir + proc runCmd(file, options = ""): auto = - const testsDir = currentSourcePath().parentDir let fileabs = testsDir / file.unixToNativePath doAssert fileabs.existsFile, fileabs let cmd = fmt"{nim} {mode} {options} --hints:off {fileabs}" @@ -55,11 +56,11 @@ ret=[s1:foobar s2:foobar age:25 pi:3.14] else: # don't run twice the same test import std/[strutils] - template check(msg) = doAssert msg in output, output + template checkrunner(msg) = doAssert msg in output, output block: # mstatic_assert let (output, exitCode) = runCmd("ccgbugs/mstatic_assert.nim", "-d:caseBad") - check "sizeof(bool) == 2" + checkrunner "sizeof(bool) == 2" doAssert exitCode != 0 block: # ABI checks @@ -72,11 +73,11 @@ else: # don't run twice the same test # on platforms that support _StaticAssert natively, errors will show full context, eg: # error: static_assert failed due to requirement 'sizeof(unsigned char) == 8' # "backend & Nim disagree on size for: BadImportcType{int64} [declared in mabi_check.nim(1, 6)]" - check "sizeof(unsigned char) == 8" - check "sizeof(struct Foo2) == 1" - check "sizeof(Foo5) == 16" - check "sizeof(Foo5) == 3" - check "sizeof(struct Foo6) == " + checkrunner "sizeof(unsigned char) == 8" + checkrunner "sizeof(struct Foo2) == 1" + checkrunner "sizeof(Foo5) == 16" + checkrunner "sizeof(Foo5) == 3" + checkrunner "sizeof(struct Foo6) == " doAssert exitCode != 0 import streams @@ -103,3 +104,12 @@ else: # don't run twice the same test var (output, exitCode) = execCmdEx(cmd) output.stripLineEnd doAssert output == expected + + block: # nim doc --docIgnoreParseErrors + const buildDir = testsDir.parentDir / "build" + let file = testsDir / "nimdoc" / "invalidrst.nim" + let cmd = fmt"{nim} doc --docIgnoreParseErrors --hints:off {file}" + let (output, exitCode) = execCmdEx(cmd) + let expected = &"{file}(2, 4) Error: '***' expected\n{file}(2, 20) Error: '``' expected\n{file}(3, 32) Error: '```' expected\n" + checkrunner expected + check fileExists("invalidrst.html") From c9c0e724ae3b030a785623a8e698796ebfcdbe02 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Fri, 8 May 2020 21:29:50 -0600 Subject: [PATCH 0011/3768] Rename invalidrst -> minvalidrst --- tests/nimdoc/{invalidrst.nim => minvalidrst.nim} | 0 tests/trunner.nim | 5 ++--- 2 files changed, 2 insertions(+), 3 deletions(-) rename tests/nimdoc/{invalidrst.nim => minvalidrst.nim} (100%) diff --git a/tests/nimdoc/invalidrst.nim b/tests/nimdoc/minvalidrst.nim similarity index 100% rename from tests/nimdoc/invalidrst.nim rename to tests/nimdoc/minvalidrst.nim diff --git a/tests/trunner.nim b/tests/trunner.nim index 461362baf..85074144d 100644 --- a/tests/trunner.nim +++ b/tests/trunner.nim @@ -106,10 +106,9 @@ else: # don't run twice the same test doAssert output == expected block: # nim doc --docIgnoreParseErrors - const buildDir = testsDir.parentDir / "build" - let file = testsDir / "nimdoc" / "invalidrst.nim" + let file = testsDir / "nimdoc" / "minvalidrst.nim" let cmd = fmt"{nim} doc --docIgnoreParseErrors --hints:off {file}" let (output, exitCode) = execCmdEx(cmd) let expected = &"{file}(2, 4) Error: '***' expected\n{file}(2, 20) Error: '``' expected\n{file}(3, 32) Error: '```' expected\n" checkrunner expected - check fileExists("invalidrst.html") + check fileExists("minvalidrst.html") From 8e93105606c198fdb0d0e1e3553c1cc14579cf7d Mon Sep 17 00:00:00 2001 From: Clyybber Date: Sat, 9 May 2020 18:22:25 +0200 Subject: [PATCH 0012/3768] Fix #14269 (#14286) --- compiler/dfa.nim | 3 +- tests/arc/tmovebug.nim | 104 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 105 insertions(+), 2 deletions(-) diff --git a/compiler/dfa.nim b/compiler/dfa.nim index f6566f77b..0db89579b 100644 --- a/compiler/dfa.nim +++ b/compiler/dfa.nim @@ -635,7 +635,8 @@ proc genUse(c: var Con; orig: PNode) = proc aliases*(obj, field: PNode): bool = var n = field var obj = obj - while obj.kind in {nkHiddenSubConv, nkHiddenStdConv, nkObjDownConv, nkObjUpConv}: + while obj.kind in {nkHiddenSubConv, nkHiddenStdConv, nkObjDownConv, nkObjUpConv, + nkAddr, nkHiddenAddr, nkDerefExpr, nkHiddenDeref}: obj = obj[0] while true: if sameTrees(obj, n): return true diff --git a/tests/arc/tmovebug.nim b/tests/arc/tmovebug.nim index 95bfb8202..6ed10c403 100644 --- a/tests/arc/tmovebug.nim +++ b/tests/arc/tmovebug.nim @@ -3,6 +3,22 @@ discard """ output: '''5 (w: 5) (w: -5) +c.text = hello +c.text = hello +p.text = hello +p.toks = @["hello"] +c.text = hello +c[].text = hello +pA.text = hello +pA.toks = @["hello"] +c.text = hello +c.text = hello +pD.text = hello +pD.toks = @["hello"] +c.text = hello +c.text = hello +pOD.text = hello +pOD.toks = @["hello"] ''' """ @@ -96,4 +112,90 @@ proc procStat() = let cols = line.splitWhitespace(maxSplit=1) let x = cols[0] let (nm, rest) = (cols[0], cols[1]) -procStat() \ No newline at end of file +procStat() + + +# bug #14269 + +import sugar, strutils + +type + Cursor = object + text: string + Parsed = object + text: string + toks: seq[string] + +proc tokenize(c: var Cursor): seq[string] = + dump c.text + return c.text.splitWhitespace() + +proc parse(): Parsed = + var c = Cursor(text: "hello") + dump c.text + return Parsed(text: c.text, toks: c.tokenize) # note: c.tokenized uses c.text + +let p = parse() +dump p.text +dump p.toks + + +proc tokenizeA(c: ptr Cursor): seq[string] = + dump c[].text + return c[].text.splitWhitespace() + +proc parseA(): Parsed = + var c = Cursor(text: "hello") + dump c.text + return Parsed(text: c.text, toks: c.addr.tokenizeA) # note: c.tokenized uses c.text + +let pA = parseA() +dump pA.text +dump pA.toks + + +proc tokenizeD(c: Cursor): seq[string] = + dump c.text + return c.text.splitWhitespace() + +proc parseD(): Parsed = + var c = cast[ptr Cursor](alloc0(sizeof(Cursor))) + c[] = Cursor(text: "hello") + dump c.text + return Parsed(text: c.text, toks: c[].tokenizeD) # note: c.tokenized uses c.text + +let pD = parseD() +dump pD.text +dump pD.toks + +# Bug would only pop up with owned refs +proc tokenizeOD(c: Cursor): seq[string] = + dump c.text + return c.text.splitWhitespace() + +proc parseOD(): Parsed = + var c = new Cursor + c[] = Cursor(text: "hello") + dump c.text + return Parsed(text: c.text, toks: c[].tokenizeOD) # note: c.tokenized uses c.text + +let pOD = parseOD() +dump pOD.text +dump pOD.toks + +when false: + # Bug would only pop up with owned refs and implicit derefs, but since they don't work together.. + {.experimental: "implicitDeref".} + proc tokenizeOHD(c: Cursor): seq[string] = + dump c.text + return c.text.splitWhitespace() + + proc parseOHD(): Parsed = + var c = new Cursor + c[] = Cursor(text: "hello") + dump c.text + return Parsed(text: c.text, toks: c.tokenizeOHD) # note: c.tokenized uses c.text + + let pOHD = parseOHD() + dump pOHD.text + dump pOHD.toks From dda6f3f6d42ec95a287f2467b976d8cbbc9c1624 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 10 May 2020 00:23:01 -0700 Subject: [PATCH 0013/3768] `nim doc -r main` and `nim rst2html -r main` now call openDefaultBrowser (#14285) --- changelog.md | 1 + compiler/nim.nim | 19 +++++++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/changelog.md b/changelog.md index cdd6c031a..7907432a7 100644 --- a/changelog.md +++ b/changelog.md @@ -140,6 +140,7 @@ - `--hint:processing` is now supported and means `--hint:processing:on` (likewise with other hints and warnings), which is consistent with all other bool flags. (since 1.3.3). +- `nim doc -r main` and `nim rst2html -r main` now call openDefaultBrowser ## Tool changes diff --git a/compiler/nim.nim b/compiler/nim.nim index 80d9b66b6..dba5b8bc3 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -24,7 +24,8 @@ import idents, lineinfos, cmdlinehelper, pathutils -include nodejs +from browsers import openDefaultBrowser +from nodejs import findNodeJs when hasTinyCBackend: import tccgen @@ -86,11 +87,21 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = if conf.cmd == cmdRun: tccgen.run(conf, conf.arguments) if optRun in conf.globalOptions: - var ex = quoteShell conf.absOutFile - if conf.cmd == cmdCompileToJS: + let output = conf.absOutFile + let ex = quoteShell output + case conf.cmd + of cmdCompileToJS: execExternalProgram(conf, findNodeJs() & " " & ex & ' ' & conf.arguments) - else: + of cmdDoc, cmdRst2html: + if conf.arguments.len > 0: + # reserved for future use + rawMessage(conf, errGenerated, "'$1 cannot handle arguments" % [$conf.cmd]) + openDefaultBrowser($output) + of cmdCompileToC, cmdCompileToCpp, cmdCompileToOC: execExternalProgram(conf, ex & ' ' & conf.arguments) + else: + # support as needed + rawMessage(conf, errGenerated, "'$1 cannot handle --run" % [$conf.cmd]) when declared(GC_setMaxPause): GC_setMaxPause 2_000 From 7c24250a575b4d441ba6d7301714cbb246425fb6 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 10 May 2020 15:56:00 -0700 Subject: [PATCH 0014/3768] diable nimx (CI failure) refs https://github.com/timotheecour/Nim/issues/167 (#14293) --- testament/important_packages.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 243856d5e..4186ce30d 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -96,7 +96,7 @@ pkg2 "nimsl", true pkg2 "nimsvg" # pkg2 "nimterop", true pkg2 "nimwc", true, "nim c nimwc.nim" -pkg2 "nimx", true, "nim c --threads:on test/main.nim" +# pkg2 "nimx", true, "nim c --threads:on test/main.nim" pkg2 "nitter", true, "nim c src/nitter.nim", "https://github.com/zedeus/nitter" pkg2 "norm", true, "nim c -r tests/tsqlite.nim" pkg2 "npeg", false, "nimble testarc" From 8018449319746f04a0c6436f1a0a3a5c6798d95c Mon Sep 17 00:00:00 2001 From: Andy Davidoff Date: Mon, 11 May 2020 02:59:31 -0400 Subject: [PATCH 0015/3768] simple typo in locks.nim (#14297) --- lib/core/locks.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/locks.nim b/lib/core/locks.nim index 42d67388f..7e2f4ad34 100644 --- a/lib/core/locks.nim +++ b/lib/core/locks.nim @@ -50,7 +50,7 @@ proc initCond*(cond: var Cond) {.inline.} = initSysCond(cond) proc deinitCond*(cond: var Cond) {.inline.} = - ## Frees the resources associated with the lock. + ## Frees the resources associated with the condition variable. deinitSysCond(cond) proc wait*(cond: var Cond, lock: var Lock) {.inline.} = From d11cb9d49596957e9fa097110cf19e9caf085592 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 11 May 2020 02:14:21 -0700 Subject: [PATCH 0016/3768] fix a critical bug in windows.osproc leading to resource leaks and blocking IO [backport] (#14296) --- changelog.md | 3 +++ lib/pure/osproc.nim | 6 +++++- lib/windows/winlean.nim | 2 ++ tests/stdlib/tosproc.nim | 26 ++++++++++++++++++++++++++ 4 files changed, 36 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 7907432a7..f1ffb4a3a 100644 --- a/changelog.md +++ b/changelog.md @@ -86,6 +86,9 @@ an uninitialized `DateTime`, the exceptions are `==` and `$` (which returns `"Uninitialized DateTime"`). The proc `times.isInitialized` has been added which can be used to check if a `DateTime` has been initialized. +- 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). + ## 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 diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 5839349fe..f90b310ea 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -441,7 +441,11 @@ when defined(Windows) and not defined(useNimRtl): handle: Handle atTheEnd: bool - proc hsClose(s: Stream) = discard # nothing to do here + proc hsClose(s: Stream) = + # xxx here + elsewhere: check instead of discard; ignoring errors leads to + # hard to track bugs + discard FileHandleStream(s).handle.closeHandle + proc hsAtEnd(s: Stream): bool = return FileHandleStream(s).atTheEnd proc hsReadData(s: Stream, buffer: pointer, bufLen: int): int = diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index 9beccc0eb..bd4cfdca7 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -31,6 +31,8 @@ type ULONG* = int32 PULONG* = ptr int WINBOOL* = int32 + ## `WINBOOL` uses opposite convention as posix, !=0 meaning success. + # xxx this should be distinct int32, distinct would make code less error prone DWORD* = int32 PDWORD* = ptr DWORD LPINT* = ptr int32 diff --git a/tests/stdlib/tosproc.nim b/tests/stdlib/tosproc.nim index fc65606f6..73cec0cb7 100644 --- a/tests/stdlib/tosproc.nim +++ b/tests/stdlib/tosproc.nim @@ -93,3 +93,29 @@ else: removeFile(exePath) except OSError: discard + + import std/streams + block: # test for startProcess (more tests needed) + # bugfix: windows stdin.close was a noop and led to blocking reads + proc startProcessTest(command: string, options: set[ProcessOption] = { + poStdErrToStdOut, poUsePath}, input = ""): tuple[ + output: TaintedString, + exitCode: int] {.tags: + [ExecIOEffect, ReadIOEffect, RootEffect], gcsafe.} = + var p = startProcess(command, options = options + {poEvalCommand}) + var outp = outputStream(p) + if input.len > 0: inputStream(p).write(input) + close inputStream(p) + result = (TaintedString"", -1) + var line = newStringOfCap(120).TaintedString + while true: + if outp.readLine(line): + result[0].string.add(line.string) + result[0].string.add("\n") + else: + result[1] = peekExitCode(p) + if result[1] != -1: break + close(p) + + var result = startProcessTest("nim r --hints:off -", options = {}, input = "echo 3*4") + doAssert result == ("12\n", 0) From 9502e39b634eea8e04f07ddc110b466387f42322 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 11 May 2020 03:01:18 -0700 Subject: [PATCH 0017/3768] `nim doc --backend:js`, `nim doc --doccmd:-d:foo`, `nim r --backend:js`, `--doccmd:skip` + other improvements (#14278) * `nim doc --backend:js|cpp...` `nim doc --doccmd:'-d:foo --threads:on'` `nim r --backend:cpp...` (implies --run --usenimcache) * --usenimcache works with all targets * --docCmd:skip now skips compiling snippets; 50X speedup for doc/manual.rst --- compiler/ccgthreadvars.nim | 2 +- compiler/cgen.nim | 8 ++-- compiler/cmdlinehelper.nim | 3 +- compiler/commands.nim | 11 ++++- compiler/docgen.nim | 29 +++++++++----- compiler/extccomp.nim | 24 +++++------ compiler/lambdalifting.nim | 6 +-- compiler/main.nim | 69 ++++++++++++++++++-------------- compiler/nim.nim | 12 +++--- compiler/options.nim | 32 ++++++++++++--- compiler/pragmas.nim | 8 ++-- compiler/semexprs.nim | 2 +- compiler/sizealignoffsetimpl.nim | 2 +- compiler/transf.nim | 4 +- compiler/vmops.nim | 5 ++- doc/advopt.txt | 11 ++++- doc/basicopt.txt | 6 ++- lib/std/compilesettings.nim | 2 + tests/nimdoc/m13129.nim | 36 +++++++++++++++++ tests/nimdoc/readme.md | 2 + tests/trunner.nim | 37 ++++++++++++----- tests/vm/tcompilesetting.nim | 1 + 22 files changed, 213 insertions(+), 99 deletions(-) create mode 100644 tests/nimdoc/m13129.nim create mode 100644 tests/nimdoc/readme.md diff --git a/compiler/ccgthreadvars.nim b/compiler/ccgthreadvars.nim index 3701f337d..8bf5e573f 100644 --- a/compiler/ccgthreadvars.nim +++ b/compiler/ccgthreadvars.nim @@ -46,7 +46,7 @@ proc generateThreadLocalStorage(m: BModule) = proc generateThreadVarsSize(m: BModule) = if m.g.nimtv != nil: - let externc = if m.config.cmd == cmdCompileToCpp or + let externc = if m.config.backend == backendCpp or sfCompileToCpp in m.module.flags: "extern \"C\" " else: "" m.s[cfsProcs].addf( diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 9e356206f..c32e86a88 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -279,7 +279,7 @@ proc genProc(m: BModule, prc: PSym) proc raiseInstr(p: BProc): Rope template compileToCpp(m: BModule): untyped = - m.config.cmd == cmdCompileToCpp or sfCompileToCpp in m.module.flags + m.config.backend == backendCpp or sfCompileToCpp in m.module.flags proc getTempName(m: BModule): Rope = result = m.tmpBase & rope(m.labels) @@ -1066,11 +1066,11 @@ proc genProcAux(m: BModule, prc: PSym) = proc requiresExternC(m: BModule; sym: PSym): bool {.inline.} = result = (sfCompileToCpp in m.module.flags and sfCompileToCpp notin sym.getModule().flags and - m.config.cmd != cmdCompileToCpp) or ( + m.config.backend != backendCpp) or ( sym.flags * {sfInfixCall, sfCompilerProc, sfMangleCpp} == {} and sym.flags * {sfImportc, sfExportc} != {} and sym.magic == mNone and - m.config.cmd == cmdCompileToCpp) + m.config.backend == backendCpp) proc genProcPrototype(m: BModule, sym: PSym) = useHeader(m, sym) @@ -1867,7 +1867,7 @@ proc writeHeader(m: BModule) = proc getCFile(m: BModule): AbsoluteFile = let ext = if m.compileToCpp: ".nim.cpp" - elif m.config.cmd == cmdCompileToOC or sfCompileToObjc in m.module.flags: ".nim.m" + elif m.config.backend == backendObjc or sfCompileToObjc in m.module.flags: ".nim.m" else: ".nim.c" result = changeFileExt(completeCfilePath(m.config, withPackageName(m.config, m.cfilename)), ext) diff --git a/compiler/cmdlinehelper.nim b/compiler/cmdlinehelper.nim index d5c743fc6..dd74f54dc 100644 --- a/compiler/cmdlinehelper.nim +++ b/compiler/cmdlinehelper.nim @@ -78,7 +78,8 @@ proc loadConfigsAndRunMainCommand*(self: NimProg, cache: IdentCache; conf: Confi # XXX This is hacky. We need to find a better way. case conf.command of "cpp", "compiletocpp": - conf.cmd = cmdCompileToCpp + conf.backend = backendCpp + conf.cmd = cmdCompileToBackend else: discard diff --git a/compiler/commands.nim b/compiler/commands.nim index e980d64fc..2ab79f14a 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -434,11 +434,18 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "outdir": expectArg(conf, switch, arg, pass, info) conf.outDir = processPath(conf, arg, info, notRelativeToProj=true) + of "usenimcache": + processOnOffSwitchG(conf, {optUseNimcache}, arg, pass, info) of "docseesrcurl": expectArg(conf, switch, arg, pass, info) conf.docSeeSrcUrl = arg of "docroot": conf.docRoot = if arg.len == 0: "@default" else: arg + of "backend", "b": + let backend = parseEnum(arg.normalize, TBackend.default) + if backend == TBackend.default: localError(conf, info, "invalid backend: '$1'" % arg) + conf.backend = backend + of "doccmd": conf.docCmd = arg of "mainmodule", "m": discard "allow for backwards compatibility, but don't do anything" of "define", "d": @@ -495,7 +502,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; defineSymbol(conf.symbols, "gcmarkandsweep") of "destructors", "arc": conf.selectedGC = gcArc - if conf.cmd != cmdCompileToCpp: + if conf.backend != backendCpp: conf.exc = excGoto defineSymbol(conf.symbols, "gcdestructors") defineSymbol(conf.symbols, "gcarc") @@ -506,7 +513,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; defineSymbol(conf.symbols, "nimV2") of "orc": conf.selectedGC = gcOrc - if conf.cmd != cmdCompileToCpp: + if conf.backend != backendCpp: conf.exc = excGoto defineSymbol(conf.symbols, "gcdestructors") defineSymbol(conf.symbols, "gcorc") diff --git a/compiler/docgen.nim b/compiler/docgen.nim index a42be9a9c..c01d5633f 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -22,6 +22,7 @@ import const exportSection = skField htmldocsDir = RelativeDir"htmldocs" + docCmdSkip = "skip" type TSections = array[TSymKind, Rope] @@ -196,6 +197,7 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef, initStrTable result.types result.onTestSnippet = proc (gen: var RstGenerator; filename, cmd: string; status: int; content: string) = + if conf.docCmd == docCmdSkip: return inc(gen.id) var d = TDocumentor(gen) var outp: AbsoluteFile @@ -444,21 +446,26 @@ proc testExample(d: PDoc; ex: PNode) = d.examples.add "import r\"" & outp.string & "\"\n" proc runAllExamples(d: PDoc) = - if d.examples.len == 0: return + let docCmd = d.conf.docCmd + let backend = d.conf.backend + # This used to be: `let backend = if isDefined(d.conf, "js"): "js"` (etc), however + # using `-d:js` (etc) cannot work properly, eg would fail with `importjs` + # since semantics are affected by `config.backend`, not by isDefined(d.conf, "js") + if d.examples.len == 0 or docCmd == docCmdSkip: return let outputDir = d.conf.getNimcacheDir / RelativeDir"runnableExamples" let outp = outputDir / RelativeFile(extractFilename(d.filename.changeFileExt"" & "_examples.nim")) writeFile(outp, d.examples) - let backend = if isDefined(d.conf, "js"): "js" - elif isDefined(d.conf, "cpp"): "cpp" - elif isDefined(d.conf, "objc"): "objc" - else: "c" - if os.execShellCmd(os.getAppFilename() & " " & backend & - " --warning[UnusedImport]:off" & - " --path:" & quoteShell(d.conf.projectPath) & - " --nimcache:" & quoteShell(outputDir) & - " -r " & quoteShell(outp)) != 0: - quit "[Examples] failed: see " & outp.string + let cmd = "$nim $backend -r --warning:UnusedImport:off --path:$path --nimcache:$nimcache $docCmd $file" % [ + "nim", os.getAppFilename(), + "backend", $d.conf.backend, + "path", quoteShell(d.conf.projectPath), + "nimcache", quoteShell(outputDir), + "file", quoteShell(outp), + "docCmd", docCmd, + ] + if os.execShellCmd(cmd) != 0: + quit "[runnableExamples] failed: generated file: '$1' cmd: $2" % [outp.string, cmd] else: # keep generated source file `outp` to allow inspection. rawMessage(d.conf, hintSuccess, ["runnableExamples: " & outp.string]) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 7c255484d..3c2435bec 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -307,14 +307,12 @@ proc getConfigVar(conf: ConfigRef; c: TSystemCC, suffix: string): string = # use ``cpu.os.cc`` for cross compilation, unless ``--compileOnly`` is given # for niminst support let fullSuffix = - if conf.cmd == cmdCompileToCpp: - ".cpp" & suffix - elif conf.cmd == cmdCompileToOC: - ".objc" & suffix - elif conf.cmd == cmdCompileToJS: - ".js" & suffix + case conf.backend + of backendCpp, backendJs, backendObjc: "." & $conf.backend & suffix + of backendC: suffix else: - suffix + doAssert false + "" if (conf.target.hostOS != conf.target.targetOS or conf.target.hostCPU != conf.target.targetCPU) and optCompileOnly notin conf.globalOptions: @@ -481,10 +479,10 @@ proc needsExeExt(conf: ConfigRef): bool {.inline.} = (conf.target.hostOS == osWindows) proc useCpp(conf: ConfigRef; cfile: AbsoluteFile): bool = - conf.cmd == cmdCompileToCpp and not cfile.string.endsWith(".c") + conf.backend == backendCpp and not cfile.string.endsWith(".c") proc envFlags(conf: ConfigRef): string = - result = if conf.cmd == cmdCompileToCpp: + result = if conf.backend == backendCpp: getEnv("CXXFLAGS") else: getEnv("CFLAGS") @@ -535,7 +533,7 @@ proc ccHasSaneOverflow*(conf: ConfigRef): bool = proc getLinkerExe(conf: ConfigRef; compiler: TSystemCC): string = result = if CC[compiler].linkerExe.len > 0: CC[compiler].linkerExe - elif optMixedMode in conf.globalOptions and conf.cmd != cmdCompileToCpp: CC[compiler].cppCompiler + elif optMixedMode in conf.globalOptions and conf.backend != backendCpp: CC[compiler].cppCompiler else: getCompilerExe(conf, compiler, AbsoluteFile"") proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile, @@ -626,8 +624,10 @@ proc footprint(conf: ConfigRef; cfile: Cfile): SecureHash = getCompileCFileCmd(conf, cfile)) proc externalFileChanged(conf: ConfigRef; cfile: Cfile): bool = - if conf.cmd notin {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToLLVM, cmdNone}: - return false + case conf.backend + of backendInvalid: doAssert false + of backendJs: return false # pre-existing behavior, but not sure it's good + else: discard var hashFile = toGeneratedFile(conf, conf.withPackageName(cfile.cname), "sha1") var currentHash = footprint(conf, cfile) diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index 01def12ad..fc1ad5e40 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -235,7 +235,7 @@ template isIterator*(owner: PSym): bool = proc liftingHarmful(conf: ConfigRef; owner: PSym): bool {.inline.} = ## lambda lifting can be harmful for JS-like code generators. let isCompileTime = sfCompileTime in owner.flags or owner.kind == skMacro - result = conf.cmd == cmdCompileToJS and not isCompileTime + result = conf.backend == backendJs and not isCompileTime proc createTypeBoundOpsLL(g: ModuleGraph; refType: PType; info: TLineInfo; owner: PSym) = createTypeBoundOps(g, nil, refType.lastSon, info) @@ -846,14 +846,14 @@ proc liftIterToProc*(g: ModuleGraph; fn: PSym; body: PNode; ptrType: PType): PNo fn.typ.callConv = oldCC proc liftLambdas*(g: ModuleGraph; fn: PSym, body: PNode; tooEarly: var bool): PNode = - # XXX gCmd == cmdCompileToJS does not suffice! The compiletime stuff needs + # XXX backend == backendJs does not suffice! The compiletime stuff needs # the transformation even when compiling to JS ... # However we can do lifting for the stuff which is *only* compiletime. let isCompileTime = sfCompileTime in fn.flags or fn.kind == skMacro if body.kind == nkEmpty or ( - g.config.cmd == cmdCompileToJS and not isCompileTime) or + g.config.backend == backendJs and not isCompileTime) or fn.skipGenericOwner.kind != skModule: # ignore forward declaration: diff --git a/compiler/main.nim b/compiler/main.nim index c94af24d5..00bc579df 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -119,8 +119,7 @@ when not defined(leanCompiler): let conf = graph.config conf.exc = excCpp - if conf.outDir.isEmpty: - conf.outDir = conf.projectPath + setOutDir(conf) if conf.outFile.isEmpty: conf.outFile = RelativeFile(conf.projectName & ".js") @@ -128,7 +127,6 @@ when not defined(leanCompiler): setTarget(graph.config.target, osJS, cpuJS) #initDefines() defineSymbol(graph.config.symbols, "ecmascript") # For backward compatibility - defineSymbol(graph.config.symbols, "js") semanticPasses(graph) registerPass(graph, JSgenPass) compileProject(graph) @@ -190,25 +188,46 @@ proc mainCommand*(graph: ModuleGraph) = conf.lastCmdTime = epochTime() conf.searchPaths.add(conf.libpath) setId(100) - template handleC() = - conf.cmd = cmdCompileToC - if conf.exc == excNone: conf.exc = excSetjmp - defineSymbol(graph.config.symbols, "c") - commandCompileToC(graph) + + ## Calling `setOutDir(conf)` unconditionally would fix regression + ## https://github.com/nim-lang/Nim/issues/6583#issuecomment-625711125 + when false: setOutDir(conf) + if optUseNimcache in conf.globalOptions: setOutDir(conf) + + template handleBackend(backend2: TBackend) = + conf.backend = backend2 + conf.cmd = cmdCompileToBackend + defineSymbol(graph.config.symbols, $backend2) + case backend2 + of backendC: + if conf.exc == excNone: conf.exc = excSetjmp + commandCompileToC(graph) + of backendCpp: + if conf.exc == excNone: conf.exc = excCpp + commandCompileToC(graph) + of backendObjc: + commandCompileToC(graph) + of backendJs: + when defined(leanCompiler): + globalError(conf, unknownLineInfo, "compiler wasn't built with JS code generator") + else: + if conf.hcrOn: + # XXX: At the moment, system.nim cannot be compiled in JS mode + # with "-d:useNimRtl". The HCR option has been processed earlier + # and it has added this define implictly, so we must undo that here. + # A better solution might be to fix system.nim + undefSymbol(conf.symbols, "useNimRtl") + commandCompileToJS(graph) + of backendInvalid: doAssert false + case conf.command.normalize - of "c", "cc", "compile", "compiletoc": handleC() # compile means compileToC currently - of "cpp", "compiletocpp": - conf.cmd = cmdCompileToCpp - if conf.exc == excNone: conf.exc = excCpp - defineSymbol(graph.config.symbols, "cpp") - commandCompileToC(graph) - of "objc", "compiletooc": - conf.cmd = cmdCompileToOC - defineSymbol(graph.config.symbols, "objc") - commandCompileToC(graph) + of "c", "cc", "compile", "compiletoc": handleBackend(backendC) # compile means compileToC currently + of "cpp", "compiletocpp": handleBackend(backendCpp) + of "objc", "compiletooc": handleBackend(backendObjc) + of "js", "compiletojs": handleBackend(backendJs) of "r": # different from `"run"`! conf.globalOptions.incl {optRun, optUseNimcache} - handleC() + handleBackend(conf.backend) of "run": conf.cmd = cmdRun when hasTinyCBackend: @@ -216,18 +235,6 @@ proc mainCommand*(graph: ModuleGraph) = commandCompileToC(graph) else: rawMessage(conf, errGenerated, "'run' command not available; rebuild with -d:tinyc") - of "js", "compiletojs": - when defined(leanCompiler): - quit "compiler wasn't built with JS code generator" - else: - conf.cmd = cmdCompileToJS - if conf.hcrOn: - # XXX: At the moment, system.nim cannot be compiled in JS mode - # with "-d:useNimRtl". The HCR option has been processed earlier - # and it has added this define implictly, so we must undo that here. - # A better solution might be to fix system.nim - undefSymbol(conf.symbols, "useNimRtl") - commandCompileToJS(graph) of "doc0": when defined(leanCompiler): quit "compiler wasn't built with documentation generator" diff --git a/compiler/nim.nim b/compiler/nim.nim index dba5b8bc3..b48fce0b8 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -88,17 +88,19 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = tccgen.run(conf, conf.arguments) if optRun in conf.globalOptions: let output = conf.absOutFile - let ex = quoteShell output case conf.cmd - of cmdCompileToJS: - execExternalProgram(conf, findNodeJs() & " " & ex & ' ' & conf.arguments) + of cmdCompileToBackend: + var cmdPrefix = "" + case conf.backend + of backendC, backendCpp, backendObjc: discard + of backendJs: cmdPrefix = findNodeJs() & " " + else: doAssert false, $conf.backend + execExternalProgram(conf, cmdPrefix & output.quoteShell & ' ' & conf.arguments) of cmdDoc, cmdRst2html: if conf.arguments.len > 0: # reserved for future use rawMessage(conf, errGenerated, "'$1 cannot handle arguments" % [$conf.cmd]) openDefaultBrowser($output) - of cmdCompileToC, cmdCompileToCpp, cmdCompileToOC: - execExternalProgram(conf, ex & ' ' & conf.arguments) else: # support as needed rawMessage(conf, errGenerated, "'$1 cannot handle --run" % [$conf.cmd]) diff --git a/compiler/options.nim b/compiler/options.nim index 5475bc07b..3e6c7989f 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -104,12 +104,26 @@ const harmlessOptions* = {optForceFullMake, optNoLinking, optRun, optUseColors, optStdout} +type + TBackend* = enum + backendInvalid = "" # for parseEnum + backendC = "c" + backendCpp = "cpp" # was cmdCompileToCpp + backendJs = "js" # was cmdCompileToJS + backendObjc = "objc" # was cmdCompileToOC + # backendNimscript = "nimscript" # this could actually work + # backendLlvm = "llvm" # probably not well supported; was cmdCompileToLLVM + type TCommands* = enum # Nim's commands # **keep binary compatible** - cmdNone, cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, - cmdCompileToJS, - cmdCompileToLLVM, cmdInterpret, cmdPretty, cmdDoc, + cmdNone, + cmdCompileToC, # deadcode + cmdCompileToCpp, # deadcode + cmdCompileToOC, # deadcode + cmdCompileToJS, # deadcode + cmdCompileToLLVM, # deadcode + cmdInterpret, cmdPretty, cmdDoc, cmdGenDepend, cmdDump, cmdCheck, # semantic checking for whole project cmdParse, # parse a single file (for debugging) @@ -121,6 +135,7 @@ type cmdInteractive, # start interactive session cmdRun, # run the project via TCC backend cmdJsonScript # compile a .json build file + cmdCompileToBackend, # compile to backend in TBackend TStringSeq* = seq[string] TGCMode* = enum # the selected GC gcUnselected, gcNone, gcBoehm, gcRegions, gcMarkAndSweep, gcArc, gcOrc, @@ -208,6 +223,7 @@ type ## fields marked with '*' are subject to ## the incremental compilation mechanisms ## (+) means "part of the dependency" + backend*: TBackend target*: Target # (+) linesCompiled*: int # all lines that have been compiled options*: TOptions # (+) @@ -274,6 +290,7 @@ type docSeeSrcUrl*: string # if empty, no seeSrc will be generated. \ # The string uses the formatting variables `path` and `line`. docRoot*: string ## see nim --fullhelp for --docRoot + docCmd*: string ## see nim --fullhelp for --docCmd # the used compiler cIncludes*: seq[AbsoluteDir] # directories to search for included files @@ -402,7 +419,7 @@ proc newConfigRef*(): ConfigRef = cIncludes: @[], # directories to search for included files cLibs: @[], # directories to search for lib files cLinkedLibs: @[], # libraries to link - + backend: backendC, externalToLink: @[], linkOptionsCmd: "", compileOptionsCmd: @[], @@ -522,7 +539,10 @@ proc setConfigVar*(conf: ConfigRef; key, val: string) = conf.configVars[key] = val proc getOutFile*(conf: ConfigRef; filename: RelativeFile, ext: string): AbsoluteFile = - conf.outDir / changeFileExt(filename, ext) + # explains regression https://github.com/nim-lang/Nim/issues/6583#issuecomment-625711125 + # Yet another reason why "" should not mean "."; `""/something` should raise + # instead of implying "" == "." as it's bug prone. + result = conf.outDir / changeFileExt(filename, ext) proc absOutFile*(conf: ConfigRef): AbsoluteFile = if false: @@ -615,7 +635,7 @@ proc getNimcacheDir*(conf: ConfigRef): AbsoluteDir = # XXX projectName should always be without a file extension! result = if not conf.nimcacheDir.isEmpty: conf.nimcacheDir - elif conf.cmd == cmdCompileToJS: + elif conf.backend == backendJs: conf.projectPath / genSubDir else: AbsoluteDir(getOsCacheDir() / splitFile(conf.projectName).name & diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 465608b10..021a8b339 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -179,7 +179,7 @@ proc processImportCpp(c: PContext; s: PSym, extname: string, info: TLineInfo) = incl(s.flags, sfImportc) incl(s.flags, sfInfixCall) excl(s.flags, sfForward) - if c.config.cmd == cmdCompileToC: + if c.config.backend == backendC: let m = s.getModule() incl(m.flags, sfCompileToCpp) incl c.config.globalOptions, optMixedMode @@ -797,8 +797,8 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of wExportc, wExportCpp: makeExternExport(c, sym, getOptionalStr(c, it, "$1"), it.info) if k == wExportCpp: - if c.config.cmd != cmdCompileToCpp: - localError(c.config, it.info, "exportcpp requires `nim cpp`, got " & $c.config.cmd) + if c.config.backend != backendCpp: + localError(c.config, it.info, "exportcpp requires `cpp` backend, got " & $c.config.backend) else: incl(sym.flags, sfMangleCpp) incl(sym.flags, sfUsed) # avoid wrong hints @@ -819,7 +819,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of wImportCpp: processImportCpp(c, sym, getOptionalStr(c, it, "$1"), it.info) of wImportJs: - if c.config.cmd != cmdCompileToJS: + if c.config.backend != backendJs: localError(c.config, it.info, "`importjs` pragma requires the JavaScript target") let name = getOptionalStr(c, it, "$1") incl(sym.flags, sfImportc) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 56b6076d5..43fd1b69c 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -660,7 +660,7 @@ proc hasUnresolvedArgs(c: PContext, n: PNode): bool = return false proc newHiddenAddrTaken(c: PContext, n: PNode): PNode = - if n.kind == nkHiddenDeref and not (c.config.cmd == cmdCompileToCpp or + if n.kind == nkHiddenDeref and not (c.config.backend == backendCpp or sfCompileToCpp in c.module.flags): checkSonsLen(n, 1, c.config) result = n[0] diff --git a/compiler/sizealignoffsetimpl.nim b/compiler/sizealignoffsetimpl.nim index dc3fefeea..9d12297ea 100644 --- a/compiler/sizealignoffsetimpl.nim +++ b/compiler/sizealignoffsetimpl.nim @@ -346,7 +346,7 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) = while st.kind in skipPtrs: st = st[^1] computeSizeAlign(conf, st) - if conf.cmd == cmdCompileToCpp: + if conf.backend == backendCpp: OffsetAccum( offset: int(st.size) - int(st.paddingAtEnd), maxAlign: st.align diff --git a/compiler/transf.nim b/compiler/transf.nim index cbf904255..bd9f567ed 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -411,7 +411,7 @@ proc transformYield(c: PTransf, n: PNode): PNode = proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PNode = result = transformSons(c, n) - if c.graph.config.cmd == cmdCompileToCpp or sfCompileToCpp in c.module.flags: return + if c.graph.config.backend == backendCpp or sfCompileToCpp in c.module.flags: return var n = result case n[0].kind of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64: @@ -447,7 +447,7 @@ proc generateThunk(c: PTransf; prc: PNode, dest: PType): PNode = # we cannot generate a proper thunk here for GC-safety reasons # (see internal documentation): - if c.graph.config.cmd == cmdCompileToJS: return prc + if c.graph.config.backend == backendJs: return prc result = newNodeIT(nkClosure, prc.info, dest) var conv = newNodeIT(nkHiddenSubConv, prc.info, dest) conv.add(newNodeI(nkEmpty, prc.info)) diff --git a/compiler/vmops.nim b/compiler/vmops.nim index 54b2877e7..27d1ef479 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -121,6 +121,7 @@ when defined(nimHasInvariant): of linkOptions: result = conf.linkOptions of compileOptions: result = conf.compileOptions of ccompilerPath: result = conf.cCompilerPath + of backend: result = $conf.backend proc querySettingSeqImpl(conf: ConfigRef, switch: BiggestInt): seq[string] = template copySeq(field: untyped): untyped = @@ -215,7 +216,7 @@ proc registerAdditionalOps*(c: PCtx) = proc hashVmImpl(a: VmArgs) = var res = hashes.hash(a.getString(0), a.getInt(1).int, a.getInt(2).int) - if c.config.cmd == cmdCompileToJS: + if c.config.backend == backendJs: # emulate JS's terrible integers: res = cast[int32](res) setResult(a, res) @@ -232,7 +233,7 @@ proc registerAdditionalOps*(c: PCtx) = bytes[i] = byte(arr[i].intVal and 0xff) var res = hashes.hash(bytes, sPos, ePos) - if c.config.cmd == cmdCompileToJS: + if c.config.backend == backendJs: # emulate JS's terrible integers: res = cast[int32](res) setResult(a, res) diff --git a/doc/advopt.txt b/doc/advopt.txt index c877b02e9..a668be5c0 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -5,6 +5,7 @@ Advanced commands: //js compile project to Javascript //e run a Nimscript file //rst2html convert a reStructuredText file to HTML + use `--docCmd:skip` to skip compiling snippets //rst2tex convert a reStructuredText file to TeX //jsondoc extract the documentation to a json file //ctags create a tags file @@ -29,6 +30,8 @@ Runtime checks (see -x): Advanced options: -o:FILE, --out:FILE set the output filename --outdir:DIR set the path where the output file will be written + --usenimcache will use `outdir=$$nimcache`, whichever it resolves + to after all options have been processed --stdout:on|off output to stdout --colors:on|off turn compiler messages coloring on|off --listFullPaths:on|off list full paths in messages @@ -69,13 +72,17 @@ Advanced options: --clib:LIBNAME link an additional C library (you should omit platform-specific extensions) --project document the whole project (doc2) - --docRoot:path nim doc --docRoot:/foo --project --outdir:docs /foo/sub/main.nim + --docRoot:path `nim doc --docRoot:/foo --project --outdir:docs /foo/sub/main.nim` generates: docs/sub/main.html if path == @pkg, will use nimble file enclosing dir - if path == @path, will use first matching dir in --path + if path == @path, will use first matching dir in `--path` if path == @default (the default and most useful), will use best match among @pkg,@path. if these are nonexistant, will use project path + -b, --backend:c|cpp|js|objc sets backend to use with commands like `nim doc` or `nim r` + --docCmd:cmd if `cmd == skip`, skips runnableExamples + else, runs runnableExamples with given options, eg: + `--docCmd:"-d:foo --threads:on"` --docSeeSrcUrl:url activate 'see source' for doc and doc2 commands (see doc.item.seesrc in config/nimdoc.cfg) --docInternal also generate documentation for non-exported symbols diff --git a/doc/basicopt.txt b/doc/basicopt.txt index cc808a077..cdd8ab9be 100644 --- a/doc/basicopt.txt +++ b/doc/basicopt.txt @@ -4,8 +4,10 @@ Command: //compile, c compile project with default code generator (C) - //r compile & run $nimcach/projname [arguments] - //doc generate the documentation for inputfile + //r compile to $nimcache/projname, run with [arguments] + using backend specified by `--backend` (default: c) + //doc generate the documentation for inputfile for + backend specified by `--backend` (default: c) Arguments: arguments are passed to the program being run (if --run option is selected) diff --git a/lib/std/compilesettings.nim b/lib/std/compilesettings.nim index 12204658d..b9b13175d 100644 --- a/lib/std/compilesettings.nim +++ b/lib/std/compilesettings.nim @@ -29,6 +29,8 @@ type linkOptions, ## additional options passed to the linker compileOptions, ## additional options passed to the C/C++ compiler ccompilerPath ## the path to the C/C++ compiler + backend ## the backend (eg: c|cpp|objc|js); both `nim doc --backend:js` + ## and `nim js` would imply backend=js MultipleValueSetting* {.pure.} = enum ## \ ## settings resulting in a seq of string values diff --git a/tests/nimdoc/m13129.nim b/tests/nimdoc/m13129.nim new file mode 100644 index 000000000..df4b5a3f5 --- /dev/null +++ b/tests/nimdoc/m13129.nim @@ -0,0 +1,36 @@ +when defined(cpp): + {.push header: "".} + type + Vector[T] {.importcpp: "std::vector".} = object +elif defined(js): + proc endsWith*(s, suffix: cstring): bool {.noSideEffect,importjs: "#.endsWith(#)".} +elif defined(c): + proc c_printf*(frmt: cstring): cint {. + importc: "printf", header: "", varargs, discardable.} + +proc main*() = + runnableExamples: + import std/compilesettings + doAssert not defined(m13129Foo1) + doAssert defined(m13129Foo2) + doAssert not defined(nimdoc) + echo "ok2: backend: " & querySetting(backend) + # echo defined(c), defined(js), + +import std/compilesettings +when defined nimdoc: + # import std/compilesettings + static: + doAssert defined(m13129Foo1) + doAssert not defined(m13129Foo2) + echo "ok1:" & querySetting(backend) + +when isMainModule: + when not defined(js): + import std/os + let cache = querySetting(nimcacheDir) + doAssert cache.len > 0 + let app = getAppFilename() + doAssert app.isRelativeTo(cache) + doAssert querySetting(projectFull) == currentSourcePath + echo "ok3" diff --git a/tests/nimdoc/readme.md b/tests/nimdoc/readme.md new file mode 100644 index 000000000..64b16c2a5 --- /dev/null +++ b/tests/nimdoc/readme.md @@ -0,0 +1,2 @@ +the html validation is tested by nimdoc/tester.nim +the runnableExamples + nim doc logic (across backend) is tested here diff --git a/tests/trunner.nim b/tests/trunner.nim index f71c324a2..e6a8a2077 100644 --- a/tests/trunner.nim +++ b/tests/trunner.nim @@ -6,7 +6,7 @@ discard """ ## tests that don't quite fit the mold and are easier to handle via `execCmdEx` ## A few others could be added to here to simplify code. -import std/[strformat,os,osproc] +import std/[strformat,os,osproc,unittest] const nim = getCurrentCompilerExe() @@ -15,8 +15,9 @@ const mode = elif defined(cpp): "cpp" else: static: doAssert false +const testsDir = currentSourcePath().parentDir + proc runCmd(file, options = ""): auto = - const testsDir = currentSourcePath().parentDir let fileabs = testsDir / file.unixToNativePath doAssert fileabs.existsFile, fileabs let cmd = fmt"{nim} {mode} {options} --hints:off {fileabs}" @@ -55,11 +56,11 @@ ret=[s1:foobar s2:foobar age:25 pi:3.14] else: # don't run twice the same test import std/[strutils] - template check(msg) = doAssert msg in output, output + template check2(msg) = doAssert msg in output, output block: # mstatic_assert let (output, exitCode) = runCmd("ccgbugs/mstatic_assert.nim", "-d:caseBad") - check "sizeof(bool) == 2" + check2 "sizeof(bool) == 2" doAssert exitCode != 0 block: # ABI checks @@ -72,11 +73,11 @@ else: # don't run twice the same test # on platforms that support _StaticAssert natively, errors will show full context, eg: # error: static_assert failed due to requirement 'sizeof(unsigned char) == 8' # "backend & Nim disagree on size for: BadImportcType{int64} [declared in mabi_check.nim(1, 6)]" - check "sizeof(unsigned char) == 8" - check "sizeof(struct Foo2) == 1" - check "sizeof(Foo5) == 16" - check "sizeof(Foo5) == 3" - check "sizeof(struct Foo6) == " + check2 "sizeof(unsigned char) == 8" + check2 "sizeof(struct Foo2) == 1" + check2 "sizeof(Foo5) == 16" + check2 "sizeof(Foo5) == 3" + check2 "sizeof(struct Foo6) == " doAssert exitCode != 0 import streams @@ -103,3 +104,21 @@ else: # don't run twice the same test var (output, exitCode) = execCmdEx(cmd) output.stripLineEnd doAssert output == expected + + block: # nim doc --backend:$backend --doccmd:$cmd + # test for https://github.com/nim-lang/Nim/issues/13129 + # test for https://github.com/nim-lang/Nim/issues/13891 + const buildDir = testsDir.parentDir / "build" + const nimcache = buildDir / "nimcacheTrunner" + # `querySetting(nimcacheDir)` would also be possible, but we thus + # avoid stomping on other parallel tests + let file = testsDir / "nimdoc/m13129.nim" + for backend in fmt"{mode} js".split: + let cmd = fmt"{nim} doc -b:{backend} --nimcache:{nimcache} -d:m13129Foo1 --doccmd:'-d:m13129Foo2 --hints:off' --usenimcache --hints:off {file}" + check execCmdEx(cmd) == (&"ok1:{backend}\nok2: backend: {backend}\n", 0) + # checks that --usenimcache works with `nim doc` + check fileExists(nimcache / "m13129.html") + + block: # mak sure --backend works with `nim r` + let cmd = fmt"{nim} r --backend:{mode} --hints:off --nimcache:{nimcache} {file}" + check execCmdEx(cmd) == ("ok3\n", 0) diff --git a/tests/vm/tcompilesetting.nim b/tests/vm/tcompilesetting.nim index 79527d584..98565ab94 100644 --- a/tests/vm/tcompilesetting.nim +++ b/tests/vm/tcompilesetting.nim @@ -17,3 +17,4 @@ static: doAssert "myNimCache" in nc doAssert "myNimblePath" in np[0] +doAssert querySetting(backend) == "c" From 14f76f721da663c9ef15d4adf51ab4c87eda8366 Mon Sep 17 00:00:00 2001 From: Jovial Joe Jayarson Date: Mon, 11 May 2020 17:20:40 +0530 Subject: [PATCH 0018/3768] refactor: renamed readme to readme.md (#14283) --- compiler/readme.md | 7 +++++++ compiler/readme.txt | 5 ----- 2 files changed, 7 insertions(+), 5 deletions(-) create mode 100644 compiler/readme.md delete mode 100644 compiler/readme.txt diff --git a/compiler/readme.md b/compiler/readme.md new file mode 100644 index 000000000..ac47508dc --- /dev/null +++ b/compiler/readme.md @@ -0,0 +1,7 @@ +## Nim Compiler + +- This directory contains the Nim compiler written in Nim. +- Note that this code has been translated from a bootstrapping version written in Pascal. +- So the code is **not** a poster child of good Nim code. + +See [Internals of the Nim Compiler](https://nim-lang.org/docs/intern.html) for more information. diff --git a/compiler/readme.txt b/compiler/readme.txt deleted file mode 100644 index ccfad7748..000000000 --- a/compiler/readme.txt +++ /dev/null @@ -1,5 +0,0 @@ -This directory contains the Nim compiler written in Nim. Note that this -code has been translated from a bootstrapping version written in Pascal, so -the code is **not** a poster child of good Nim code. - -See https://nim-lang.org/docs/intern.html for more information. From 73c9d2c9d745e4396dbfc9e9422acdfa7809b705 Mon Sep 17 00:00:00 2001 From: Antonis Date: Mon, 11 May 2020 12:45:55 +0300 Subject: [PATCH 0019/3768] Fix for --styleCheck:error --- lib/system/mm/boehm.nim | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/system/mm/boehm.nim b/lib/system/mm/boehm.nim index b1afe59fc..362d2d470 100644 --- a/lib/system/mm/boehm.nim +++ b/lib/system/mm/boehm.nim @@ -53,14 +53,14 @@ when not defined(useNimRtl): proc realloc0Impl(p: pointer, oldSize, newSize: Natural): pointer = result = boehmRealloc(p, newSize) if result == nil: raiseOutOfMem() - if newsize > oldsize: - zeroMem(cast[pointer](cast[int](result) + oldsize), newsize - oldsize) + if newSize > oldSize: + zeroMem(cast[pointer](cast[int](result) + oldSize), newSize - oldSize) proc deallocImpl(p: pointer) = boehmDealloc(p) proc allocSharedImpl(size: Natural): pointer = allocImpl(size) proc allocShared0Impl(size: Natural): pointer = alloc0Impl(size) - proc reallocSharedImpl(p: pointer, newsize: Natural): pointer = reallocImpl(p, newsize) - proc reallocShared0Impl(p: pointer, oldsize, newsize: Natural): pointer = realloc0Impl(p, oldsize, newsize) + proc reallocSharedImpl(p: pointer, newSize: Natural): pointer = reallocImpl(p, newSize) + proc reallocShared0Impl(p: pointer, oldSize, newSize: Natural): pointer = realloc0Impl(p, oldSize, newSize) proc deallocSharedImpl(p: pointer) = deallocImpl(p) when hasThreadSupport: From 3616b0698baf230fec7a60cd90c72b979e582179 Mon Sep 17 00:00:00 2001 From: Antonis Date: Mon, 11 May 2020 14:51:50 +0300 Subject: [PATCH 0020/3768] fix closure env check --- compiler/injectdestructors.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index ea1a97e24..9ed3d4189 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -382,7 +382,8 @@ proc sinkParamIsLastReadCheck(c: var Con, s: PNode) = localError(c.graph.config, c.otherRead.info, "sink parameter `" & $s.sym.name.s & "` is already consumed at " & toFileLineCol(c. graph.config, s.info)) -proc isClosureEnv(n: PNode): bool = n.kind == nkSym and n.sym.name.s[0] == ':' +proc isClosureEnv(n: PNode): bool = + n.kind == nkDotExpr and n[0].kind == nkHiddenDeref and n[0][0].typ.kind == tyRef proc passCopyToSink(n: PNode; c: var Con): PNode = result = newNodeIT(nkStmtListExpr, n.info, n.typ) From 517dd800f8e621aae0221f15c4ad7ad011c910b5 Mon Sep 17 00:00:00 2001 From: Antonis Date: Mon, 11 May 2020 14:59:51 +0300 Subject: [PATCH 0021/3768] better error message --- compiler/injectdestructors.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 9ed3d4189..09c072da7 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -398,7 +398,7 @@ proc passCopyToSink(n: PNode; c: var Con): PNode = if isLValue(n) and not isClosureEnv(n) and n.typ.skipTypes(abstractInst).kind != tyRef and c.inSpawn == 0: message(c.graph.config, n.info, hintPerformance, ("passing '$1' to a sink parameter introduces an implicit copy; " & - "use 'move($1)' to prevent it") % $n) + "if possible, rearrange your program's control flow to prevent it") % $n) else: if c.graph.config.selectedGC in {gcArc, gcOrc}: assert(not containsGarbageCollectedRef(n.typ)) From 03c146cd93ba48f7e8cab05e6214c8675d0dd1f9 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 11 May 2020 16:25:56 +0200 Subject: [PATCH 0022/3768] do not track 'raise Defect' in the .raises: [] clause anymore (#14298) * do not track 'raise Defect' in the .raises: [] clause anymore * --panics:on maps 'raise Defect' to an unrecoverable fatal error * make tests green again * update the documentation too --- changelog.md | 33 +++++++++++++++++++++++++++++---- compiler/sempass2.nim | 5 ++++- compiler/types.nim | 11 +++++++++++ doc/manual.rst | 22 ++++++++++++++++++++++ lib/system/excpt.nim | 7 ++++++- tests/effects/teffects7.nim | 4 ++-- 6 files changed, 74 insertions(+), 8 deletions(-) diff --git a/changelog.md b/changelog.md index f1ffb4a3a..9bf372dc4 100644 --- a/changelog.md +++ b/changelog.md @@ -46,11 +46,11 @@ used must be castable to `ptr string`, any incompatible pointer type will not work. The `lexbase` and `streams` modules used to fail to compile on NimScript due to a bug, but this has been fixed. - + The following modules now compile on both JS and NimScript: `parsecsv`, `parsecfg`, `parsesql`, `xmlparser`, `htmlparser` and `ropes`. Additionally supported for JS is `cstrutils.startsWith` and `cstrutils.endsWith`, for - NimScript: `json`, `parsejson`, `strtabs` and `unidecode`. + NimScript: `json`, `parsejson`, `strtabs` and `unidecode`. - Added `streams.readStr` and `streams.peekStr` overloads to accept an existing string to modify, which avoids memory @@ -68,7 +68,7 @@ - `sugar.=>` and `sugar.->` changes: Previously `(x, y: int)` was transformed into `(x: auto, y: int)`, it now becomes `(x: int, y: int)` in consistency with regular proc definitions (although you cannot use semicolons). - + Pragmas and using a name are now allowed on the lefthand side of `=>`. Here is an aggregate example of these changes: ```nim @@ -90,7 +90,8 @@ hangs if a process had both reads from stdin and writes (eg to stdout). ## 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: +- In the 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 type MyObj = object @@ -124,6 +125,30 @@ with command line switch `--useVersion:1.0`. - The keyword `from` is now usable as an operator. +- Exceptions inheriting from `system.Defect` are no longer tracked with + the `.raises: []` exception tracking mechanism. This is more consistent with the + built-in operations. The following always used to compile (and still does): + +```nim + +proc mydiv(a, b): int {.raises: [].} = + a div b # can raise an DivByZeroDefect + +``` + + Now also this compiles: + +```nim + +proc mydiv(a, b): int {.raises: [].} = + if b == 0: raise newException(DivByZeroDefect, "division by zero") + else: result = a div b + +``` + + The reason for this is that `DivByZeroDefect` inherits from `Defect` and + with `--panics:on` `Defects` become unrecoverable errors. + ## Compiler changes diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index e1521475a..a7b01a75e 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -315,7 +315,10 @@ proc addEffect(a: PEffects, e, comesFrom: PNode) = # we only track the first node that can have the effect E in order # to safe space and time. if sameType(a.graph.excType(aa[i]), a.graph.excType(e)): return - throws(a.exc, e, comesFrom) + + if e.typ != nil: + if optNimV1Emulation in a.config.globalOptions or not isDefectException(e.typ): + throws(a.exc, e, comesFrom) proc addTag(a: PEffects, e, comesFrom: PNode) = var aa = a.tags diff --git a/compiler/types.nim b/compiler/types.nim index 241229df6..709e5bb2e 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -1672,6 +1672,17 @@ proc isException*(t: PType): bool = t = skipTypes(t[0], abstractPtrs) return false +proc isDefectException*(t: PType): bool = + var t = t.skipTypes(abstractPtrs) + while t.kind == tyObject: + if t.sym != nil and t.sym.owner != nil and + sfSystemModule in t.sym.owner.flags and + t.sym.name.s == "Defect": + return true + if t[0] == nil: break + t = skipTypes(t[0], abstractPtrs) + return false + proc isSinkTypeForParam*(t: PType): bool = # a parameter like 'seq[owned T]' must not be used only once, but its # elements must, so we detect this case here: diff --git a/doc/manual.rst b/doc/manual.rst index 02ccace2c..6feb8aebb 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -4338,6 +4338,28 @@ Rules 1-2 ensure the following works: So in many cases a callback does not cause the compiler to be overly conservative in its effect analysis. +Exceptions inheriting from ``system.Defect`` are not tracked with +the ``.raises: []`` exception tracking mechanism. This is more consistent with the +built-in operations. The following code is valid:: + +.. code-block:: nim + + proc mydiv(a, b): int {.raises: [].} = + a div b # can raise an DivByZeroDefect + +And so is:: + +.. code-block:: nim + + proc mydiv(a, b): int {.raises: [].} = + if b == 0: raise newException(DivByZeroDefect, "division by zero") + else: result = a div b + + +The reason for this is that ``DivByZeroDefect`` inherits from ``Defect`` and +with ``--panics:on`` Defects become unrecoverable errors. +(Since version 1.4 of the language.) + Tag tracking ------------ diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index 747b145ae..bbb890cd4 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -406,7 +406,7 @@ proc reportUnhandledError(e: ref Exception) {.nodestroy.} = when hostOS != "any": reportUnhandledErrorAux(e) else: - discard() + discard () proc nimLeaveFinally() {.compilerRtl.} = when defined(cpp) and not defined(noCppExceptions) and not gotoBasedExceptions: @@ -434,6 +434,11 @@ when gotoBasedExceptions: quit(1) proc raiseExceptionAux(e: sink(ref Exception)) {.nodestroy.} = + when defined(nimPanics): + if e of Defect: + reportUnhandledError(e) + quit(1) + if localRaiseHook != nil: if not localRaiseHook(e): return if globalRaiseHook != nil: diff --git a/tests/effects/teffects7.nim b/tests/effects/teffects7.nim index aee5e027d..73865b18d 100644 --- a/tests/effects/teffects7.nim +++ b/tests/effects/teffects7.nim @@ -1,5 +1,5 @@ discard """ - errormsg: "can raise an unlisted exception: ref FloatingPointDefect" + errormsg: "can raise an unlisted exception: ref ValueError" line: 10 """ @@ -7,7 +7,7 @@ proc foo() {.raises: [].} = try: discard except KeyError: - raise newException(FloatingPointDefect, "foo") + raise newException(ValueError, "foo") except Exception: discard From fee71d466cdb7cda96b55592abbf7d433639ab51 Mon Sep 17 00:00:00 2001 From: cooldome Date: Mon, 11 May 2020 18:10:54 +0100 Subject: [PATCH 0023/3768] fix #14294 (#14301) * fix #14294 * fix orc as well Co-authored-by: cooldome --- compiler/liftdestructors.nim | 7 ++++++- tests/destructor/tarc.nim | 26 +++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 6f4e5828d..d70b968a8 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -448,7 +448,8 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = case c.kind of attachedAsgn, attachedDeepCopy: - doAssert t.assignment != nil + if t.assignment == nil: + return # protect from recursion body.add newHookCall(c.g, t.assignment, x, y) of attachedSink: # we always inline the move for better performance: @@ -465,8 +466,12 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = doAssert t.destructor != nil body.add destructorCall(c, t.destructor, x) of attachedTrace: + if t.attachedOps[c.kind] == nil: + return # protect from recursion body.add newHookCall(c.g, t.attachedOps[c.kind], x, y) of attachedDispose: + if t.attachedOps[c.kind] == nil: + return # protect from recursion body.add newHookCall(c.g, t.attachedOps[c.kind], x, nil) proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = diff --git a/tests/destructor/tarc.nim b/tests/destructor/tarc.nim index 1acc95fad..12ea46b2c 100644 --- a/tests/destructor/tarc.nim +++ b/tests/destructor/tarc.nim @@ -6,7 +6,9 @@ Success Hello 1 2 -0''' +0 +List +''' cmd: '''nim c --gc:arc $file''' """ @@ -145,3 +147,25 @@ proc bug13105 = bug13105() echo getOccupiedMem() - startMem + + +#------------------------------------------------------------------------------ +# issue #14294 + +import tables + +type + TagKind = enum + List = 0, Compound + + Tag = object + case kind: TagKind + of List: + values: seq[Tag] + of Compound: + compound: Table[string, Tag] + +var a = Tag(kind: List) +var b = a +echo a.kind +var c = a From a081b10b54ff78539e49d320ca12aabc20fc3ad8 Mon Sep 17 00:00:00 2001 From: b3liever <43617260+b3liever@users.noreply.github.com> Date: Mon, 11 May 2020 20:12:25 +0300 Subject: [PATCH 0024/3768] small refactoring (#14303) Co-authored-by: Antonis --- compiler/injectdestructors.nim | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 09c072da7..009c683ae 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -382,8 +382,9 @@ proc sinkParamIsLastReadCheck(c: var Con, s: PNode) = localError(c.graph.config, c.otherRead.info, "sink parameter `" & $s.sym.name.s & "` is already consumed at " & toFileLineCol(c. graph.config, s.info)) -proc isClosureEnv(n: PNode): bool = - n.kind == nkDotExpr and n[0].kind == nkHiddenDeref and n[0][0].typ.kind == tyRef +proc isCapturedVar(n: PNode): bool = + n.kind == nkDotExpr and n[0].kind == nkHiddenDeref and + n[0][0].kind == nkSym and n[0][0].sym.name.s[0] == ':' proc passCopyToSink(n: PNode; c: var Con): PNode = result = newNodeIT(nkStmtListExpr, n.info, n.typ) @@ -395,7 +396,7 @@ proc passCopyToSink(n: PNode; c: var Con): PNode = var m = genCopy(c, tmp, n) m.add p(n, c, normal) result.add m - if isLValue(n) and not isClosureEnv(n) and n.typ.skipTypes(abstractInst).kind != tyRef and c.inSpawn == 0: + if isLValue(n) and not isCapturedVar(n) and n.typ.skipTypes(abstractInst).kind != tyRef and c.inSpawn == 0: message(c.graph.config, n.info, hintPerformance, ("passing '$1' to a sink parameter introduces an implicit copy; " & "if possible, rearrange your program's control flow to prevent it") % $n) From 0f344a70c992594c7d6b794a6c2dd6c4b5b14685 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 11 May 2020 19:12:40 +0200 Subject: [PATCH 0025/3768] fixes #13946 (#14302) --- lib/pure/parsexml.nim | 14 ++++---------- lib/pure/strtabs.nim | 2 +- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/lib/pure/parsexml.nim b/lib/pure/parsexml.nim index a8e7aaab8..6ec77ebdb 100644 --- a/lib/pure/parsexml.nim +++ b/lib/pure/parsexml.nim @@ -299,21 +299,15 @@ template piRest*(my: XmlParser): string = assert(my.kind == xmlPI) my.b -proc rawData*(my: var XmlParser): string {.inline.} = +proc rawData*(my: var XmlParser): lent string {.inline.} = ## returns the underlying 'data' string by reference. ## This is only used for speed hacks. - when defined(gcDestructors): - result = move(my.a) - else: - shallowCopy(result, my.a) + result = my.a -proc rawData2*(my: var XmlParser): string {.inline.} = +proc rawData2*(my: var XmlParser): lent string {.inline.} = ## returns the underlying second 'data' string by reference. ## This is only used for speed hacks. - when defined(gcDestructors): - result = move(my.b) - else: - shallowCopy(result, my.b) + result = my.b proc getColumn*(my: XmlParser): int {.inline.} = ## get the current column the parser has arrived at. diff --git a/lib/pure/strtabs.nim b/lib/pure/strtabs.nim index 375f212cd..26811cc65 100644 --- a/lib/pure/strtabs.nim +++ b/lib/pure/strtabs.nim @@ -228,7 +228,7 @@ proc enlarge(t: StringTableRef) = var n: KeyValuePairSeq newSeq(n, len(t.data) * growthFactor) for i in countup(0, high(t.data)): - if t.data[i].hasValue: rawInsert(t, n, t.data[i].key, t.data[i].val) + if t.data[i].hasValue: rawInsert(t, n, move t.data[i].key, move t.data[i].val) swap(t.data, n) proc `[]=`*(t: StringTableRef, key, val: string) {. From 76f7e958141eabc6dc317189fa12c123584e1b6e Mon Sep 17 00:00:00 2001 From: slangmgh <37659406+slangmgh@users.noreply.github.com> Date: Tue, 12 May 2020 01:17:37 +0800 Subject: [PATCH 0026/3768] Fix #14289 (#14304) [backport] --- lib/pure/asyncdispatch.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 1421db2e8..91778a5bc 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -1000,10 +1000,11 @@ when defined(windows) or defined(nimdoc): proc addProcess*(pid: int, cb: Callback) = ## Registers callback ``cb`` to be called when process with process ID ## ``pid`` exited. + const NULL = Handle(0) let p = getGlobalDispatcher() let procFlags = SYNCHRONIZE var hProcess = openProcess(procFlags, 0, pid.DWORD) - if hProcess == INVALID_HANDLE_VALUE: + if hProcess == NULL: raiseOSError(osLastError()) var pcd = cast[PostCallbackDataPtr](allocShared0(sizeof(PostCallbackData))) From 86669ef5b4c9711d81e961c2f68a05aa4f4d9ece Mon Sep 17 00:00:00 2001 From: Kaushal Modi Date: Mon, 11 May 2020 14:45:35 -0400 Subject: [PATCH 0027/3768] Make --backend:cpp|js work for :test: code-blocks as well (#14306) Continues https://github.com/nim-lang/Nim/commit/9502e39b634eea8e04f07ddc110b466387f42322 Ref: - https://github.com/nim-lang/Nim/commit/9502e39b634eea8e04f07ddc110b466387f42322#commitcomment-39087584 - https://github.com/nim-lang/Nim/pull/14278 Fixes https://github.com/nim-lang/Nim/issues/13129 . --- compiler/docgen.nim | 3 ++- lib/packages/docutils/rstgen.nim | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index c01d5633f..d774fd508 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -216,7 +216,8 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef, # Include the current file if we're parsing a nim file let importStmt = if d.isPureRst: "" else: "import \"$1\"\n" % [d.filename.replace("\\", "/")] writeFile(outp, importStmt & content) - let c = if cmd.startsWith("nim "): os.getAppFilename() & cmd.substr(3) + let c = if cmd.startsWith("nim "): os.getAppFilename() & " " & $conf.backend & cmd.substr("nim ".len) + elif cmd.startsWith("nim "): os.getAppFilename() & cmd.substr("nim".len) else: cmd let c2 = c % quoteShell(outp) rawMessage(conf, hintExecuting, c2) diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 13dff68e9..edd987a76 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -865,7 +865,7 @@ proc parseCodeBlockField(d: PDoc, n: PRstNode, params: var CodeBlockParams) = of "test": params.testCmd = n.getFieldValue.strip if params.testCmd.len == 0: - params.testCmd = "nim c -r $1" + params.testCmd = "nim -r $1" # The nim backend is auto-set in docgen.nim else: params.testCmd = unescape(params.testCmd) of "status", "exitcode": From a39123c93c9d2976300f1b8eba09d40b16f1c46c Mon Sep 17 00:00:00 2001 From: cooldome Date: Mon, 11 May 2020 22:11:20 +0100 Subject: [PATCH 0028/3768] fix #14219 (#14225) Co-authored-by: cooldome --- compiler/injectdestructors.nim | 2 +- tests/arc/amodule.nim | 10 ++++++++++ tests/arc/tamodule.nim | 6 ++++++ 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 tests/arc/amodule.nim create mode 100644 tests/arc/tamodule.nim diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 009c683ae..431f9539f 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -1064,7 +1064,7 @@ proc injectDefaultCalls(n: PNode, c: var Con) = proc extractDestroysForTemporaries(c: Con, destroys: PNode): PNode = result = newNodeI(nkStmtList, destroys.info) for i in 0.. Date: Mon, 11 May 2020 23:27:47 -0700 Subject: [PATCH 0029/3768] properly fixes #13758 so that `import std/macros` stays legal (#14291) * properly fix https://github.com/nim-lang/Nim/issues/12389 * use --lib:lib in koch.nim.cfg instead * third time is the charm --- compiler/nim.nim | 2 +- koch.nim | 12 +++++++++--- koch.nim.cfg | 8 +++++++- lib/pure/typetraits.nim | 3 +-- 4 files changed, 18 insertions(+), 7 deletions(-) diff --git a/compiler/nim.nim b/compiler/nim.nim index b48fce0b8..2b0d78dd4 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -24,7 +24,7 @@ import idents, lineinfos, cmdlinehelper, pathutils -from browsers import openDefaultBrowser +from std/browsers import openDefaultBrowser from nodejs import findNodeJs when hasTinyCBackend: diff --git a/koch.nim b/koch.nim index 8b7eecf7c..3ec40534b 100644 --- a/koch.nim +++ b/koch.nim @@ -27,8 +27,10 @@ when defined(amd64) and defined(windows) and defined(vcc): when defined(i386) and defined(windows) and defined(vcc): {.link: "icons/koch-i386-windows-vcc.res".} -import - os, strutils, parseopt, osproc +import std/[os, strutils, parseopt, osproc] + # Using `std/os` instead of `os` to fail early if config isn't set up properly. + # If this fails with: `Error: cannot open file: std/os`, see + # https://github.com/nim-lang/Nim/pull/14291 for explanation + how to fix. import tools / kochdocs import tools / deps @@ -318,9 +320,13 @@ proc boot(args: string) = let ret = execCmdEx(nimStart & " --version") doAssert ret.exitCode == 0 let version = ret.output.splitLines[0] + # remove these when csources get updated + template addLib() = + extraOption.add " --lib:lib" # see https://github.com/nim-lang/Nim/pull/14291 if version.startsWith "Nim Compiler Version 0.19.0": extraOption.add " -d:nimBoostrapCsources0_19_0" - # remove this when csources get updated + addLib() + elif version.startsWith "Nim Compiler Version 0.20.0": addLib() # in order to use less memory, we split the build into two steps: # --compileOnly produces a $project.json file and does not run GCC/Clang. diff --git a/koch.nim.cfg b/koch.nim.cfg index 1d7acf579..c624b9577 100644 --- a/koch.nim.cfg +++ b/koch.nim.cfg @@ -1 +1,7 @@ --d:booting \ No newline at end of file +-d:booting + +@if openbsd: + # because openbsd getAppFilename was broken in v0.20.0 + # workaround see https://github.com/nim-lang/Nim/pull/14291 + --lib:lib +@end diff --git a/lib/pure/typetraits.nim b/lib/pure/typetraits.nim index 0ac6d0d1c..9a215a353 100644 --- a/lib/pure/typetraits.nim +++ b/lib/pure/typetraits.nim @@ -93,8 +93,7 @@ since (1, 1): type StaticParam*[value: static type] = object ## used to wrap a static value in `genericParams` -# NOTE: See https://github.com/nim-lang/Nim/issues/13758 - `import std/macros` does not work on OpenBSD -import macros +import std/macros macro genericParamsImpl(T: typedesc): untyped = # auxiliary macro needed, can't do it directly in `genericParams` From 55446c05a4ed726a6aa26bdf5f0da1db5ad7d5f1 Mon Sep 17 00:00:00 2001 From: Avahe Kellenberger Date: Tue, 12 May 2020 02:28:10 -0400 Subject: [PATCH 0030/3768] Added a reference to ternary operators. (#14309) `if expressions` are similar to ternary operators in other languages. In order to make this easier to find in the manual, I've added a sentence about it. --- doc/manual.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/manual.rst b/doc/manual.rst index 6feb8aebb..82678f40e 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -3128,6 +3128,7 @@ If expression ------------- An `if expression` is almost like an if statement, but it is an expression. +This feature is similar to `ternary operators` in other languages. Example: .. code-block:: nim From 2d7ccf09282ba7f7156b2f2d9893cb083cd18549 Mon Sep 17 00:00:00 2001 From: cooldome Date: Tue, 12 May 2020 10:23:05 +0100 Subject: [PATCH 0031/3768] fix #14312 --- compiler/liftdestructors.nim | 15 +++++++++------ tests/arc/tcaseobj.nim | 15 +++++++++++++-- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index d70b968a8..2a7301f24 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -62,9 +62,18 @@ proc newAsgnStmt(le, ri: PNode): PNode = result[0] = le result[1] = ri +proc genBuiltin(g: ModuleGraph; magic: TMagic; name: string; i: PNode): PNode = + result = newNodeI(nkCall, i.info) + result.add createMagic(g, name, magic).newSymNode + result.add i + proc defaultOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = if c.kind in {attachedAsgn, attachedDeepCopy, attachedSink}: body.add newAsgnStmt(x, y) + elif c.kind == attachedDestructor and c.addMemReset: + let call = genBuiltin(c.g, mDefault, "default", x) + call.typ = t + body.add newAsgnStmt(x, call) proc genAddr(g: ModuleGraph; x: PNode): PNode = if x.kind == nkHiddenDeref: @@ -74,12 +83,6 @@ proc genAddr(g: ModuleGraph; x: PNode): PNode = result = newNodeIT(nkHiddenAddr, x.info, makeVarType(x.typ.owner, x.typ)) result.add x - -proc genBuiltin(g: ModuleGraph; magic: TMagic; name: string; i: PNode): PNode = - result = newNodeI(nkCall, i.info) - result.add createMagic(g, name, magic).newSymNode - result.add i - proc genWhileLoop(c: var TLiftCtx; i, dest: PNode): PNode = result = newNodeI(nkWhileStmt, c.info, 2) let cmp = genBuiltin(c.g, mLtI, "<", i) diff --git a/tests/arc/tcaseobj.nim b/tests/arc/tcaseobj.nim index dd02675cc..107cf9fc1 100644 --- a/tests/arc/tcaseobj.nim +++ b/tests/arc/tcaseobj.nim @@ -194,8 +194,6 @@ proc test_myobject = test_myobject() - - #------------------------------------------------ # bug #14244 @@ -212,3 +210,16 @@ proc init(): RocksDBResult[string] = result.value = "ok" echo init() + + +#------------------------------------------------ +# bug #14312 + +type MyObj = object + case kind: bool + of false: x0: int # would work with a type like seq[int]; value would be reset + of true: x1: string + +var a = MyObj(kind: false, x0: 1234) +a.kind = true +doAssert(a.x1 == "") From dfae796ac3e0db69af40bec11a7d7ad7c4d00b4b Mon Sep 17 00:00:00 2001 From: cooldome Date: Tue, 12 May 2020 11:45:14 +0100 Subject: [PATCH 0032/3768] fix test --- compiler/liftdestructors.nim | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 2a7301f24..6f7b285ed 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -143,9 +143,6 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool) fillBody(c, f.typ, body, x.dotField(f), b) of nkNilLit: discard of nkRecCase: - let oldfilterDiscriminator = c.filterDiscriminator - if c.filterDiscriminator == n[0].sym: - c.filterDiscriminator = nil # we have found the case part, proceed as normal # XXX This is only correct for 'attachedSink'! var localEnforceDefaultOp = enforceDefaultOp if c.kind == attachedSink: @@ -159,6 +156,11 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool) # copy the selector: fillBodyObj(c, n[0], body, x, y, enforceDefaultOp = false) + + let oldfilterDiscriminator = c.filterDiscriminator + if c.filterDiscriminator == n[0].sym: + c.filterDiscriminator = nil # we have found the case part, proceed as normal + # we need to generate a case statement: var caseStmt = newNodeI(nkCaseStmt, c.info) # XXX generate 'if' that checks same branches From 4277ab470a20f429605fbf238e485ea24b01706a Mon Sep 17 00:00:00 2001 From: cooldome Date: Tue, 12 May 2020 12:45:34 +0100 Subject: [PATCH 0033/3768] fix one motr dicriminator bug --- compiler/liftdestructors.nim | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 6f7b285ed..5faba79f9 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -154,8 +154,9 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool) c.kind = prevKind localEnforceDefaultOp = true - # copy the selector: - fillBodyObj(c, n[0], body, x, y, enforceDefaultOp = false) + if c.kind != attachedDestructor: + # copy the selector before case stmt, but destroy after case stmt + fillBodyObj(c, n[0], body, x, y, enforceDefaultOp = false) let oldfilterDiscriminator = c.filterDiscriminator if c.filterDiscriminator == n[0].sym: @@ -179,6 +180,10 @@ proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool) caseStmt.add(branch) if emptyBranches != n.len-1: body.add(caseStmt) + + if c.kind == attachedDestructor: + # destructor for selector is done after case stmt + fillBodyObj(c, n[0], body, x, y, enforceDefaultOp = false) c.filterDiscriminator = oldfilterDiscriminator of nkRecList: for t in items(n): fillBodyObj(c, t, body, x, y, enforceDefaultOp) From 06dfd316127fb2ec05ff69942abd1e279156ac5c Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 12 May 2020 15:46:24 +0200 Subject: [PATCH 0034/3768] fixes #13881 * fixes #13881 * documented changed requirements for system.onThreadDestruction * destructors.rst: update the documentation --- changelog.md | 3 +++ compiler/condsyms.nim | 1 + doc/destructors.rst | 47 ++++++++++++++++++++++++++++++++++--- lib/system.nim | 4 ++-- lib/system/excpt.nim | 8 +++++++ lib/system/gc.nim | 7 +++--- lib/system/gc_interface.nim | 6 ++--- lib/system/gc_ms.nim | 7 +++--- lib/system/threads.nim | 24 ++++++++++++------- 9 files changed, 84 insertions(+), 23 deletions(-) diff --git a/changelog.md b/changelog.md index 9bf372dc4..af6217324 100644 --- a/changelog.md +++ b/changelog.md @@ -89,6 +89,9 @@ - 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: []`. + + ## Language changes - In the 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: diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 5333e639c..7b05faa87 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -117,3 +117,4 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimNewIntegerOps") defineSymbol("nimHasInvariant") defineSymbol("nimHasStacktraceMsgs") + defineSymbol("nimDoesntTrackDefects") diff --git a/doc/destructors.rst b/doc/destructors.rst index 2b1222778..e7ebd93ea 100644 --- a/doc/destructors.rst +++ b/doc/destructors.rst @@ -469,10 +469,51 @@ for expressions of type ``lent T`` or of type ``var T``. echo t[0] # accessor does not copy the element! +The .cursor annotation +====================== + +Under the ``--gc:arc|orc`` modes Nim's `ref` type is implemented via the same runtime +"hooks" and thus via reference counting. This means that cyclic structures cannot be freed +immediately (``--gc:orc`` ships with a cycle collector). With the ``.cursor`` annotation +one can break up cycles declaratively: + +.. code-block:: nim + + type + Node = ref object + left: Node # owning ref + right {.cursor.}: Node # non-owning ref + +But please notice that this is not C++'s weak_ptr, it means the right field is not +involved in the reference counting, it is a raw pointer without runtime checks. + +Automatic reference counting also has the disadvantage that it introduces overhead +when iterating over linked structures. The ``.cursor`` annotation can also be used +to avoid this overhead: + +.. code-block:: nim + + var it {.cursor.} = listRoot + while it != nil: + use(it) + it = it.next + + +In fact, ``.cursor`` more generally prevents object construction/destruction pairs +and so can also be useful in other contexts. The alternative solution would be to +use raw pointers (``ptr``) instead which is more cumbersome and also more dangerous +for Nim's evolution: Later on the compiler can try to prove ``.cursor`` annotations +to be safe, but for ``ptr`` the compiler has to remain silent about possible +problems. + Owned refs ========== +**Note**: The ``owned`` type constructor is only available with +the ``--newruntime`` compiler switch and is experimental. + + Let ``W`` be an ``owned ref`` type. Conceptually its hooks look like: .. code-block:: nim @@ -568,14 +609,14 @@ used to specialize the object traversal in order to avoid deep recursions: type Node = ref object x, y: int32 - left, right: owned Node + left, right: Node type Tree = object - root: owned Node + root: Node proc `=destroy`(t: var Tree) {.nodestroy.} = # use an explicit stack so that we do not get stack overflows: - var s: seq[owned Node] = @[t.root] + var s: seq[Node] = @[t.root] while s.len > 0: let x = s.pop if x.left != nil: s.add(x.left) diff --git a/lib/system.nim b/lib/system.nim index 8878277b7..8ce79aa2f 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -692,7 +692,7 @@ proc len*(x: string): int {.magic: "LengthStr", noSideEffect.} proc len*(x: cstring): int {.magic: "LengthStr", noSideEffect.} ## Returns the length of a compatible string. This is sometimes ## an O(n) operation. - ## + ## ## **Note:** On the JS backend this currently counts UTF-16 code points ## instead of bytes at runtime (not at compile time). For now, if you ## need the byte length of the UTF-8 encoding, convert to string with @@ -2093,7 +2093,7 @@ when not defined(js): when hasAlloc: when not defined(gcRegions) and not usesDestructors: - proc initGC() {.gcsafe.} + proc initGC() {.gcsafe, raises: [].} proc initStackBottom() {.inline, compilerproc.} = # WARNING: This is very fragile! An array size of 8 does not work on my diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index bbb890cd4..fd1a73b2d 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -493,6 +493,14 @@ proc reraiseException() {.compilerRtl.} = else: raiseExceptionAux(currException) +proc threadTrouble() = + # also forward declared, it is 'raises: []' hence the try-except. + try: + if currException != nil: reportUnhandledError(currException) + except: + discard + quit 1 + proc writeStackTrace() = when hasSomeStackTrace: var s = "" diff --git a/lib/system/gc.nim b/lib/system/gc.nim index 2be16ef90..304eda19a 100644 --- a/lib/system/gc.nim +++ b/lib/system/gc.nim @@ -843,9 +843,10 @@ when not defined(useNimRtl): proc GC_disable() = inc(gch.recGcLock) proc GC_enable() = - if gch.recGcLock <= 0: - raise newException(AssertionDefect, - "API usage error: GC_enable called but GC is already enabled") + when defined(nimDoesntTrackDefects): + if gch.recGcLock <= 0: + raise newException(AssertionDefect, + "API usage error: GC_enable called but GC is already enabled") dec(gch.recGcLock) proc GC_setStrategy(strategy: GC_Strategy) = diff --git a/lib/system/gc_interface.nim b/lib/system/gc_interface.nim index 9183e6d48..84145f33a 100644 --- a/lib/system/gc_interface.nim +++ b/lib/system/gc_interface.nim @@ -14,7 +14,7 @@ when hasAlloc: gcOptimizeSpace ## optimize for memory footprint when hasAlloc and not defined(js) and not usesDestructors: - proc GC_disable*() {.rtl, inl, benign.} + proc GC_disable*() {.rtl, inl, benign, raises: [].} ## Disables the GC. If called `n` times, `n` calls to `GC_enable` ## are needed to reactivate the GC. ## @@ -22,7 +22,7 @@ when hasAlloc and not defined(js) and not usesDestructors: ## the mark and sweep phase with ## `GC_disableMarkAndSweep <#GC_disableMarkAndSweep>`_. - proc GC_enable*() {.rtl, inl, benign.} + proc GC_enable*() {.rtl, inl, benign, raises: [].} ## Enables the GC again. proc GC_fullCollect*() {.rtl, benign.} @@ -54,7 +54,7 @@ when hasAlloc and not defined(js) and not usesDestructors: proc GC_unref*(x: string) {.magic: "GCunref", benign.} ## See the documentation of `GC_ref <#GC_ref,string>`_. - proc nimGC_setStackBottom*(theStackBottom: pointer) {.compilerRtl, noinline, benign.} + proc nimGC_setStackBottom*(theStackBottom: pointer) {.compilerRtl, noinline, benign, raises: [].} ## Expands operating GC stack range to `theStackBottom`. Does nothing ## if current stack bottom is already lower than `theStackBottom`. diff --git a/lib/system/gc_ms.nim b/lib/system/gc_ms.nim index eda220901..0b9745f1e 100644 --- a/lib/system/gc_ms.nim +++ b/lib/system/gc_ms.nim @@ -495,9 +495,10 @@ when not defined(useNimRtl): proc GC_disable() = inc(gch.recGcLock) proc GC_enable() = - if gch.recGcLock <= 0: - raise newException(AssertionDefect, - "API usage error: GC_enable called but GC is already enabled") + when defined(nimDoesntTrackDefects): + if gch.recGcLock <= 0: + raise newException(AssertionDefect, + "API usage error: GC_enable called but GC is already enabled") dec(gch.recGcLock) proc GC_setStrategy(strategy: GC_Strategy) = discard diff --git a/lib/system/threads.nim b/lib/system/threads.nim index b3a4e32e4..34a2d2ccf 100644 --- a/lib/system/threads.nim +++ b/lib/system/threads.nim @@ -89,9 +89,9 @@ type data: TArg var - threadDestructionHandlers {.rtlThreadVar.}: seq[proc () {.closure, gcsafe.}] + threadDestructionHandlers {.rtlThreadVar.}: seq[proc () {.closure, gcsafe, raises: [].}] -proc onThreadDestruction*(handler: proc () {.closure, gcsafe.}) = +proc onThreadDestruction*(handler: proc () {.closure, gcsafe, raises: [].}) = ## Registers a *thread local* handler that is called at the thread's ## destruction. ## @@ -105,7 +105,10 @@ template afterThreadRuns() = threadDestructionHandlers[i]() when not defined(boehmgc) and not hasSharedHeap and not defined(gogc) and not defined(gcRegions): - proc deallocOsPages() {.rtl.} + proc deallocOsPages() {.rtl, raises: [].} + +proc threadTrouble() {.raises: [].} + ## defined in system/excpt.nim when defined(boehmgc): type GCStackBaseProc = proc(sb: pointer, t: pointer) {.noconv.} @@ -116,7 +119,7 @@ when defined(boehmgc): proc boehmGC_unregister_my_thread() {.importc: "GC_unregister_my_thread", boehmGC.} - proc threadProcWrapDispatch[TArg](sb: pointer, thrd: pointer) {.noconv.} = + proc threadProcWrapDispatch[TArg](sb: pointer, thrd: pointer) {.noconv, raises: [].} = boehmGC_register_my_thread(sb) try: let thrd = cast[ptr Thread[TArg]](thrd) @@ -124,11 +127,13 @@ when defined(boehmgc): thrd.dataFn() else: thrd.dataFn(thrd.data) + except: + threadTrouble() finally: afterThreadRuns() boehmGC_unregister_my_thread() else: - proc threadProcWrapDispatch[TArg](thrd: ptr Thread[TArg]) = + proc threadProcWrapDispatch[TArg](thrd: ptr Thread[TArg]) {.raises: [].} = try: when TArg is void: thrd.dataFn() @@ -139,21 +144,22 @@ else: var x: TArg deepCopy(x, thrd.data) thrd.dataFn(x) + except: + threadTrouble() finally: afterThreadRuns() -proc threadProcWrapStackFrame[TArg](thrd: ptr Thread[TArg]) = +proc threadProcWrapStackFrame[TArg](thrd: ptr Thread[TArg]) {.raises: [].} = when defined(boehmgc): boehmGC_call_with_stack_base(threadProcWrapDispatch[TArg], thrd) elif not defined(nogc) and not defined(gogc) and not defined(gcRegions) and not usesDestructors: - var p {.volatile.}: proc(a: ptr Thread[TArg]) {.nimcall, gcsafe.} = - threadProcWrapDispatch[TArg] + var p {.volatile.}: pointer # init the GC for refc/markandsweep nimGC_setStackBottom(addr(p)) initGC() when declared(threadType): threadType = ThreadType.NimThread - p(thrd) + threadProcWrapDispatch[TArg](thrd) when declared(deallocOsPages): deallocOsPages() else: threadProcWrapDispatch(thrd) From de74362213baeb28d52c05c71cd9b8389ae69060 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 12 May 2020 07:19:03 -0700 Subject: [PATCH 0035/3768] fix #14314 do not analyze importc procs for effects (#14319) --- compiler/lineinfos.nim | 4 ++-- compiler/sempass2.nim | 7 ++++++- tests/misc/mimportc.nim | 26 ++++++++++++++++++++++++++ tests/trunner.nim | 14 ++++++++++---- 4 files changed, 44 insertions(+), 7 deletions(-) create mode 100644 tests/misc/mimportc.nim diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index eea55defc..d6c863842 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -23,7 +23,7 @@ type errGeneralParseError, errNewSectionExpected, errInvalidDirectiveX, - errProveInit, + errProveInit, # deadcode errGenerated, errUser, warnCannotOpenFile, @@ -66,7 +66,7 @@ const errGeneralParseError: "general parse error", errNewSectionExpected: "new section expected", errInvalidDirectiveX: "invalid directive: '$1'", - errProveInit: "Cannot prove that '$1' is initialized.", + errProveInit: "Cannot prove that '$1' is initialized.", # deadcode errGenerated: "$1", errUser: "$1", warnCannotOpenFile: "cannot open '$1'", diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index a7b01a75e..862123eab 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -1151,12 +1151,17 @@ proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects; c: PC t.config = g.config t.c = c +proc hasRealBody(s: PSym): bool = + ## also handles importc procs with runnableExamples, which requires `=`, + ## which is not a real implementation, refs #14314 + result = {sfForward, sfImportc} * s.flags == {} + proc trackProc*(c: PContext; s: PSym, body: PNode) = let g = c.graph var effects = s.typ.n[0] if effects.kind != nkEffectList: return # effects already computed? - if sfForward in s.flags: return + if not s.hasRealBody: return if effects.len == effectListLen: return var t: TEffects diff --git a/tests/misc/mimportc.nim b/tests/misc/mimportc.nim new file mode 100644 index 000000000..602c6372d --- /dev/null +++ b/tests/misc/mimportc.nim @@ -0,0 +1,26 @@ +#[ +this test will grow with more importc+importcpp tests; see driver in trunner.nim +]# + +{.emit:""" +struct A { + static int fun0(int a){ + return a; + } + static int& fun1(int& a){ + return a; + } +}; +""".} + +proc fun0*(a: cint): int {.importcpp:"A::$1(@)".} +proc fun1*(a: var cint): var int {.importcpp:"A::$1(@)".} = + ## some comment; this test is for #14314 + runnableExamples: discard + +proc main()= + var a = 10.cint + doAssert fun0(a) == a + doAssert fun1(a).addr == a.addr + echo "witness" +main() diff --git a/tests/trunner.nim b/tests/trunner.nim index e6a8a2077..bd74a8405 100644 --- a/tests/trunner.nim +++ b/tests/trunner.nim @@ -16,6 +16,10 @@ const mode = else: static: doAssert false const testsDir = currentSourcePath().parentDir +const buildDir = testsDir.parentDir / "build" +const nimcache = buildDir / "nimcacheTrunner" + # `querySetting(nimcacheDir)` would also be possible, but we thus + # avoid stomping on other parallel tests proc runCmd(file, options = ""): auto = let fileabs = testsDir / file.unixToNativePath @@ -108,10 +112,6 @@ else: # don't run twice the same test block: # nim doc --backend:$backend --doccmd:$cmd # test for https://github.com/nim-lang/Nim/issues/13129 # test for https://github.com/nim-lang/Nim/issues/13891 - const buildDir = testsDir.parentDir / "build" - const nimcache = buildDir / "nimcacheTrunner" - # `querySetting(nimcacheDir)` would also be possible, but we thus - # avoid stomping on other parallel tests let file = testsDir / "nimdoc/m13129.nim" for backend in fmt"{mode} js".split: let cmd = fmt"{nim} doc -b:{backend} --nimcache:{nimcache} -d:m13129Foo1 --doccmd:'-d:m13129Foo2 --hints:off' --usenimcache --hints:off {file}" @@ -122,3 +122,9 @@ else: # don't run twice the same test block: # mak sure --backend works with `nim r` let cmd = fmt"{nim} r --backend:{mode} --hints:off --nimcache:{nimcache} {file}" check execCmdEx(cmd) == ("ok3\n", 0) + + block: # some importc tests + # issue #14314 + let file = testsDir / "misc/mimportc.nim" + let cmd = fmt"{nim} r -b:cpp --hints:off --nimcache:{nimcache} --warningAsError:ProveInit {file}" + check execCmdEx(cmd) == ("witness\n", 0) From dddfbcf211cd46453b78a9d207347a158311f85e Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 12 May 2020 10:19:31 -0700 Subject: [PATCH 0036/3768] close #13071 by adding test: nim cpp -r --gc:arc` segfaults on caught AssertionError (#14323) --- tests/arc/texceptions.nim | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 tests/arc/texceptions.nim diff --git a/tests/arc/texceptions.nim b/tests/arc/texceptions.nim new file mode 100644 index 000000000..c55b463fc --- /dev/null +++ b/tests/arc/texceptions.nim @@ -0,0 +1,16 @@ +discard """ + cmd: "nim cpp --gc:arc $file" +""" + +block: # issue #13071 + type MyExcept = object of CatchableError + proc gun()= + raise newException(MyExcept, "foo:") + proc fun()= + var a = "" + try: + gun() + except Exception as e: + a = e.msg & $e.name # was segfaulting here for `nim cpp --gc:arc` + doAssert a == "foo:MyExcept" + fun() From 09ea8f9cf8ee796bdd5f8a803d30216e7aefa6ba Mon Sep 17 00:00:00 2001 From: b3liever <43617260+b3liever@users.noreply.github.com> Date: Tue, 12 May 2020 21:09:56 +0300 Subject: [PATCH 0037/3768] fix detecting closure env for nested asts (#14326) Co-authored-by: b3liever --- compiler/injectdestructors.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 431f9539f..44409c760 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -26,7 +26,7 @@ import strutils, options, dfa, lowerings, tables, modulegraphs, msgs, lineinfos, parampatterns, sighashes, liftdestructors -from trees import exprStructuralEquivalent +from trees import exprStructuralEquivalent, getRoot from algorithm import reverse const @@ -383,8 +383,8 @@ proc sinkParamIsLastReadCheck(c: var Con, s: PNode) = "` is already consumed at " & toFileLineCol(c. graph.config, s.info)) proc isCapturedVar(n: PNode): bool = - n.kind == nkDotExpr and n[0].kind == nkHiddenDeref and - n[0][0].kind == nkSym and n[0][0].sym.name.s[0] == ':' + let root = getRoot(n) + if root != nil: result = root.name.s[0] == ':' proc passCopyToSink(n: PNode; c: var Con): PNode = result = newNodeIT(nkStmtListExpr, n.info, n.typ) From 82f008158cbc023c5e20103bfb4bda317be52fb2 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 12 May 2020 20:12:55 +0200 Subject: [PATCH 0038/3768] cycle collector: make it threadsafe --- lib/system/cyclicrefs_bacon.nim | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/system/cyclicrefs_bacon.nim b/lib/system/cyclicrefs_bacon.nim index aeee6e3d9..5f0197384 100644 --- a/lib/system/cyclicrefs_bacon.nim +++ b/lib/system/cyclicrefs_bacon.nim @@ -89,15 +89,15 @@ proc nimTraceRefDyn(q: pointer; env: pointer) {.compilerRtl.} = j.traceStack.add(head p[], cast[ptr PNimType](p[])[]) var - roots: CellSeq + roots {.threadvar.}: CellSeq proc unregisterCycle(s: Cell) = # swap with the last element. O(1) let idx = s.rootIdx - if idx >= roots.len or idx < 0: - cprintf("[Bug!] %ld\n", idx) - quit 1 - + when false: + if idx >= roots.len or idx < 0: + cprintf("[Bug!] %ld\n", idx) + quit 1 roots.d[idx] = roots.d[roots.len-1] roots.d[idx][0].rootIdx = idx dec roots.len From 9acbf99efb42d7d3f98f3d1d6677923fc4dd3a28 Mon Sep 17 00:00:00 2001 From: PMunch Date: Tue, 12 May 2020 20:28:18 +0200 Subject: [PATCH 0039/3768] Allow let to not have value when using importc (#14258) * Allow let to not have value when using importc This allows a let statement with the `{.importc.}` pragma to not be initialised with a value. This allows us to declare C constants as Nim lets without putting the value in the Nim code (which can lead to errors, and requires us to go looking for the value). Fixes #14253 * Proper fix and documentation + changelog entry * Improve testcase with one from timotheecour * Add test to verify it working with macros --- changelog.md | 5 ++--- compiler/semstmts.nim | 5 +++-- doc/manual.rst | 15 +++++++++++++++ tests/let/timportc.nim | 24 ++++++++++++++++++++++++ tests/let/timportc2.nim | 8 ++++++++ 5 files changed, 52 insertions(+), 5 deletions(-) create mode 100644 tests/let/timportc.nim create mode 100644 tests/let/timportc2.nim diff --git a/changelog.md b/changelog.md index af6217324..d54ed4c8c 100644 --- a/changelog.md +++ b/changelog.md @@ -122,11 +122,11 @@ deallocShared(x.val) x.val = nil ``` - - getImpl() on enum type symbols now returns field syms instead of idents. This helps with writing typed macros. Old behavior for backwards compatiblity can be restored with command line switch `--useVersion:1.0`. - +- ``let`` statements can now be used without a value if declared with + ``importc``/``importcpp``/``importjs``/``importobjc``. - The keyword `from` is now usable as an operator. - Exceptions inheriting from `system.Defect` are no longer tracked with the `.raises: []` exception tracking mechanism. This is more consistent with the @@ -152,7 +152,6 @@ proc mydiv(a, b): int {.raises: [].} = The reason for this is that `DivByZeroDefect` inherits from `Defect` and with `--panics:on` `Defects` become unrecoverable errors. - ## Compiler changes - Specific warnings can now be turned into errors via `--warningAsError[X]:on|off`. diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 0e018dd0b..3faa32808 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -531,8 +531,6 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = # special type inference rule: 'var it = ownedPointer' is turned # into an unowned pointer. typ = typ.lastSon - else: - if symkind == skLet: localError(c.config, a.info, errLetNeedsInit) # this can only happen for errornous var statements: if typ == nil: continue @@ -620,6 +618,9 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = defaultConstructionError(c, v.typ, v.info) else: checkNilable(c, v) + # allow let to not be initialised if imported from C: + if v.kind == skLet and sfImportc notin v.flags: + localError(c.config, a.info, errLetNeedsInit) if sfCompileTime in v.flags: var x = newNodeI(result.kind, v.info) x.add result[i] diff --git a/doc/manual.rst b/doc/manual.rst index 82678f40e..8d4439644 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -2679,6 +2679,11 @@ nor can their address be taken. They cannot be assigned new values. For let variables the same pragmas are available as for ordinary variables. +As ``let`` statements are immutable after creation they need to define a value +when they are declared. The only exception to this is if the ``{.importc.}`` +pragma (or any of the other ``importX`` pragmas) is applied, in this case the +value is expected to come from native code, typically a C/C++ ``const``. + Tuple unpacking --------------- @@ -7090,6 +7095,16 @@ spelled*: .. code-block:: proc printf(formatstr: cstring) {.header: "", importc: "printf", varargs.} +When ``importc`` is applied to a ``let`` statement it can omit its value which +will then be expected to come from C. This can be used to import a C ``const``: + +.. code-block:: + {.emit: "const int cconst = 42;".} + + let cconst {.importc, nodecl.}: cint + + assert cconst == 42 + Note that this pragma has been abused in the past to also work in the js backend for js objects and functions. : Other backends do provide the same feature under the same name. Also, when the target language diff --git a/tests/let/timportc.nim b/tests/let/timportc.nim new file mode 100644 index 000000000..85244da9f --- /dev/null +++ b/tests/let/timportc.nim @@ -0,0 +1,24 @@ +discard """ +targets: "c cpp js" +""" + +when defined(c) or defined(cpp): + {.emit:""" + const int TEST1 = 123; + #define TEST2 321 + """.} + +when defined(js): + {.emit:""" + const TEST1 = 123; + const TEST2 = 321; // JS doesn't have macros, so we just duplicate + """.} + +let + TEST0 = 1 + TEST1 {.importc, nodecl.}: cint + TEST2 {.importc, nodecl.}: cint + +doAssert TEST0 == 1 +doAssert TEST1 == 123 +doAssert TEST2 == 321 diff --git a/tests/let/timportc2.nim b/tests/let/timportc2.nim new file mode 100644 index 000000000..964305923 --- /dev/null +++ b/tests/let/timportc2.nim @@ -0,0 +1,8 @@ +discard """ + errormsg: "'let' symbol requires an initialization" + line: "7" +""" + +# Test that this still works when not annotated with importc +let test: cint +echo test From 1648f1dd9955c848f8fbaf46a89798ece21460cc Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 13 May 2020 02:05:47 -0700 Subject: [PATCH 0040/3768] fix #14320 (tasyncawait.nim is recently very flaky) + avoid hardcoding service ports everywhere + flakyAssert (#14327) * hotfix #14320 tasyncawait.nim is recently very flaky * fix #14327 * add flakyAssert --- testament/lib/readme.md | 4 ++++ testament/lib/stdtest/netutils.nim | 12 ++++++++++ testament/lib/stdtest/testutils.nim | 25 ++++++++++++++++++++ tests/arc/tasyncawait.nim | 21 ++++++----------- tests/async/tasyncawait.nim | 27 +++++++-------------- tests/async/tasyncawait_cyclebreaker.nim | 21 ++++++----------- tests/async/tasyncsend4757.nim | 20 ++++++++-------- tests/async/tasyncssl.nim | 30 ++++++++++++++---------- tests/async/twinasyncrw.nim | 29 +++++++---------------- tests/config.nims | 5 +++- 10 files changed, 102 insertions(+), 92 deletions(-) create mode 100644 testament/lib/readme.md create mode 100644 testament/lib/stdtest/netutils.nim create mode 100644 testament/lib/stdtest/testutils.nim diff --git a/testament/lib/readme.md b/testament/lib/readme.md new file mode 100644 index 000000000..20e866338 --- /dev/null +++ b/testament/lib/readme.md @@ -0,0 +1,4 @@ +This directory contains helper files used by several tests, to avoid +code duplication, akin to a std extension tailored for testament. + +Some of these could later migrate to stdlib. diff --git a/testament/lib/stdtest/netutils.nim b/testament/lib/stdtest/netutils.nim new file mode 100644 index 000000000..eb913a56a --- /dev/null +++ b/testament/lib/stdtest/netutils.nim @@ -0,0 +1,12 @@ +import std/[nativesockets, asyncdispatch, os] + +proc bindAvailablePort*(handle: SocketHandle, port = Port(0)): Port = + block: + var name: Sockaddr_in + name.sin_family = typeof(name.sin_family)(toInt(AF_INET)) + name.sin_port = htons(uint16(port)) + name.sin_addr.s_addr = htonl(INADDR_ANY) + if bindAddr(handle, cast[ptr SockAddr](addr(name)), + sizeof(name).Socklen) < 0'i32: + raiseOSError(osLastError()) + result = getLocalAddr(handle, AF_INET)[1] diff --git a/testament/lib/stdtest/testutils.nim b/testament/lib/stdtest/testutils.nim new file mode 100644 index 000000000..8083a63e8 --- /dev/null +++ b/testament/lib/stdtest/testutils.nim @@ -0,0 +1,25 @@ +import std/private/miscdollars + +template flakyAssert*(cond: untyped, msg = "", notifySuccess = true) = + ## API to deal with flaky or failing tests. This avoids disabling entire tests + ## altogether so that at least the parts that are working are kept being + ## tested. This also avoids making CI fail periodically for tests known to + ## be flaky. Finally, for known failures, passing `notifySuccess = true` will + ## log that the test succeeded, which may indicate that a bug was fixed + ## "by accident" and should be looked into. + const info = instantiationInfo(-1, true) + const expr = astToStr(cond) + if cond and not notifySuccess: + discard # silent success + else: + var msg2 = "" + toLocation(msg2, info.filename, info.line, info.column) + if cond: + # a flaky test is failing, we still report it but we don't fail CI + msg2.add " FLAKY_SUCCESS " + else: + # a previously failing test is now passing, a pre-existing bug might've been + # fixed by accidend + msg2.add " FLAKY_FAILURE " + msg2.add $expr & " " & msg + echo msg2 diff --git a/tests/arc/tasyncawait.nim b/tests/arc/tasyncawait.nim index 7135f4173..f29b8d2b2 100644 --- a/tests/arc/tasyncawait.nim +++ b/tests/arc/tasyncawait.nim @@ -3,7 +3,8 @@ discard """ cmd: "nim c --gc:orc $file" """ -import asyncdispatch, asyncnet, nativesockets, net, strutils, os +import asyncdispatch, asyncnet, nativesockets, net, strutils +from stdtest/netutils import bindAvailablePort var msgCount = 0 @@ -44,24 +45,16 @@ proc readMessages(client: AsyncFD) {.async.} = else: doAssert false -proc createServer(port: Port) {.async.} = - var server = createAsyncNativeSocket() - block: - var name: Sockaddr_in - name.sin_family = typeof(name.sin_family)(toInt(AF_INET)) - name.sin_port = htons(uint16(port)) - name.sin_addr.s_addr = htonl(INADDR_ANY) - if bindAddr(server.SocketHandle, cast[ptr SockAddr](addr(name)), - sizeof(name).Socklen) < 0'i32: - raiseOSError(osLastError()) - +proc createServer(server: AsyncFD) {.async.} = discard server.SocketHandle.listen() while true: asyncCheck readMessages(await accept(server)) proc main = - asyncCheck createServer(Port(10335)) - asyncCheck launchSwarm(Port(10335)) + let server = createAsyncNativeSocket() + let port = bindAvailablePort(server.SocketHandle) + asyncCheck createServer(server) + asyncCheck launchSwarm(port) while true: poll() if clientCount == swarmSize: break diff --git a/tests/async/tasyncawait.nim b/tests/async/tasyncawait.nim index a52b0953d..aec4ce523 100644 --- a/tests/async/tasyncawait.nim +++ b/tests/async/tasyncawait.nim @@ -1,8 +1,5 @@ -discard """ - output: "2000" -""" -import asyncdispatch, asyncnet, nativesockets, net, strutils, os - +import asyncdispatch, asyncnet, nativesockets, net, strutils +from stdtest/netutils import bindAvailablePort var msgCount = 0 const @@ -42,26 +39,18 @@ proc readMessages(client: AsyncFD) {.async.} = else: doAssert false -proc createServer(port: Port) {.async.} = - var server = createAsyncNativeSocket() - block: - var name: Sockaddr_in - name.sin_family = typeof(name.sin_family)(toInt(AF_INET)) - name.sin_port = htons(uint16(port)) - name.sin_addr.s_addr = htonl(INADDR_ANY) - if bindAddr(server.SocketHandle, cast[ptr SockAddr](addr(name)), - sizeof(name).Socklen) < 0'i32: - raiseOSError(osLastError()) - +proc createServer(server: AsyncFD) {.async.} = discard server.SocketHandle.listen() while true: asyncCheck readMessages(await accept(server)) -asyncCheck createServer(Port(10335)) -asyncCheck launchSwarm(Port(10335)) +let server = createAsyncNativeSocket() +let port = bindAvailablePort(server.SocketHandle) +asyncCheck createServer(server) +asyncCheck launchSwarm(port) while true: poll() if clientCount == swarmSize: break assert msgCount == swarmSize * messagesToSend -echo msgCount +doAssert msgCount == 2000 diff --git a/tests/async/tasyncawait_cyclebreaker.nim b/tests/async/tasyncawait_cyclebreaker.nim index 0304d4b82..2229d99c0 100644 --- a/tests/async/tasyncawait_cyclebreaker.nim +++ b/tests/async/tasyncawait_cyclebreaker.nim @@ -2,7 +2,8 @@ discard """ output: "20000" cmd: "nim c -d:nimTypeNames -d:nimCycleBreaker $file" """ -import asyncdispatch, asyncnet, nativesockets, net, strutils, os +import asyncdispatch, asyncnet, nativesockets, net, strutils +from stdtest/netutils import bindAvailablePort var msgCount = 0 @@ -43,23 +44,15 @@ proc readMessages(client: AsyncFD) {.async.} = else: doAssert false -proc createServer(port: Port) {.async.} = - var server = createAsyncNativeSocket() - block: - var name: Sockaddr_in - name.sin_family = typeof(name.sin_family)(toInt(AF_INET)) - name.sin_port = htons(uint16(port)) - name.sin_addr.s_addr = htonl(INADDR_ANY) - if bindAddr(server.SocketHandle, cast[ptr SockAddr](addr(name)), - sizeof(name).Socklen) < 0'i32: - raiseOSError(osLastError()) - +proc createServer(server: AsyncFD) {.async.} = discard server.SocketHandle.listen() while true: asyncCheck readMessages(await accept(server)) -asyncCheck createServer(Port(10335)) -asyncCheck launchSwarm(Port(10335)) +let server = createAsyncNativeSocket() +let port = bindAvailablePort(server.SocketHandle) +asyncCheck createServer(server) +asyncCheck launchSwarm(port) while true: poll() GC_collectZct() diff --git a/tests/async/tasyncsend4757.nim b/tests/async/tasyncsend4757.nim index a87c5df95..29873a905 100644 --- a/tests/async/tasyncsend4757.nim +++ b/tests/async/tasyncsend4757.nim @@ -1,24 +1,24 @@ -discard """ -output: "Finished" -""" - import asyncdispatch, asyncnet -proc createServer(port: Port) {.async.} = +var port: Port +proc createServer() {.async.} = var server = newAsyncSocket() server.setSockOpt(OptReuseAddr, true) - bindAddr(server, port) + bindAddr(server) + port = getLocalAddr(server)[1] server.listen() while true: let client = await server.accept() discard await client.recvLine() -asyncCheck createServer(10335.Port) +asyncCheck createServer() +var done = false proc f(): Future[void] {.async.} = - let s = newAsyncNativeSocket() - await s.connect("localhost", 10335.Port) + let s = createAsyncNativeSocket() + await s.connect("localhost", port) await s.send("123") - echo "Finished" + done = true waitFor f() +doAssert done diff --git a/tests/async/tasyncssl.nim b/tests/async/tasyncssl.nim index 88a5eb32e..c948ee9b7 100644 --- a/tests/async/tasyncssl.nim +++ b/tests/async/tasyncssl.nim @@ -1,16 +1,12 @@ discard """ cmd: "nim $target --hints:on --define:ssl $options $file" - output: "500" - disabled: "windows" - target: c - action: compile """ -# XXX, deactivated - -import asyncdispatch, asyncnet, net, strutils, os +import asyncdispatch, asyncnet, net, strutils +import stdtest/testutils when defined(ssl): + var port0: Port var msgCount = 0 const @@ -45,25 +41,33 @@ when defined(ssl): else: doAssert false - proc createServer(port: Port) {.async.} = + proc createServer() {.async.} = let serverContext = newContext(verifyMode = CVerifyNone, certFile = "tests/testdata/mycert.pem", keyFile = "tests/testdata/mycert.pem") var server = newAsyncSocket() serverContext.wrapSocket(server) server.setSockOpt(OptReuseAddr, true) - bindAddr(server, port) + bindAddr(server) + port0 = getLocalAddr(server)[1] server.listen() while true: let client = await accept(server) serverContext.wrapConnectedSocket(client, handshakeAsServer) asyncCheck readMessages(client) - asyncCheck createServer(Port(10335)) - asyncCheck launchSwarm(Port(10335)) + asyncCheck createServer() + asyncCheck launchSwarm(port0) while true: poll() if clientCount == swarmSize: break - assert msgCount == swarmSize * messagesToSend - echo msgCount + template cond(): bool = msgCount == swarmSize * messagesToSend + when defined(windows): + # currently: msgCount == 0 + flakyAssert cond() + elif defined(linux) and int.sizeof == 8: + # currently: msgCount == 10 + flakyAssert cond() + assert msgCount > 0 + else: assert cond(), $msgCount diff --git a/tests/async/twinasyncrw.nim b/tests/async/twinasyncrw.nim index 352c53b41..69b092607 100644 --- a/tests/async/twinasyncrw.nim +++ b/tests/async/twinasyncrw.nim @@ -1,9 +1,6 @@ -discard """ - output: "5000" -""" when defined(windows): import asyncdispatch, nativesockets, net, strutils, os, winlean - + from stdtest/netutils import bindAvailablePort var msgCount = 0 const @@ -228,29 +225,19 @@ when defined(windows): else: doAssert false - proc createServer(port: Port) {.async.} = - var server = createNativeSocket() - setBlocking(server, false) - block: - var name = Sockaddr_in() - name.sin_family = toInt(Domain.AF_INET).uint16 - name.sin_port = htons(uint16(port)) - name.sin_addr.s_addr = htonl(INADDR_ANY) - if bindAddr(server, cast[ptr SockAddr](addr(name)), - sizeof(name).Socklen) < 0'i32: - raiseOSError(osLastError()) - + proc createServer(server: SocketHandle) {.async.} = discard server.listen() while true: asyncCheck readMessages(await winAccept(AsyncFD(server))) - asyncCheck createServer(Port(10335)) - asyncCheck launchSwarm(Port(10335)) + var server = createNativeSocket() + setBlocking(server, false) + let port = bindAvailablePort(server) + asyncCheck createServer(server) + asyncCheck launchSwarm(port) while true: poll() if clientCount == swarmSize: break assert msgCount == swarmSize * messagesToSend - echo msgCount -else: - echo(5000) + doAssert msgCount == 5000 diff --git a/tests/config.nims b/tests/config.nims index cd4ee4b08..e91c3aa4f 100644 --- a/tests/config.nims +++ b/tests/config.nims @@ -1,4 +1,7 @@ -switch("path", "$nim/testament/lib") # so we can `import stdtest/foo` in this dir +switch("path", "$lib/../testament/lib") + # so we can `import stdtest/foo` inside tests + # Using $lib/../ instead of $nim/ so you can use a different nim to run tests + # during local testing, eg nim --lib:lib. ## prevent common user config settings to interfere with testament expectations ## Indifidual tests can override this if needed to test for these options. From 041ee92bba0ba3f361218eb20ceeeee6eec85f91 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 13 May 2020 04:45:36 -0700 Subject: [PATCH 0041/3768] `osproc.execCmdEx` now takes an optional `input` for stdin, `env`, workingDir (#14211) * `osproc.execCmdEx` now takes an optional `input` for stdin * execCmdEx now also takes an optional ``workingDir` and `env` --- changelog.md | 4 ++++ lib/pure/osproc.nim | 41 +++++++++++++++++++++++++++++----------- lib/system.nim | 3 ++- tests/stdlib/tosproc.nim | 15 ++++++++++++++- 4 files changed, 50 insertions(+), 13 deletions(-) diff --git a/changelog.md b/changelog.md index d54ed4c8c..d6be5a9b7 100644 --- a/changelog.md +++ b/changelog.md @@ -92,6 +92,10 @@ - The callback that is passed to `system.onThreadDestruction` must now be `.raises: []`. +- `osproc.execCmdEx` now takes an optional `input` for stdin. +- `osproc.execCmdEx` now takes an optional `input` for stdin, `workingDir` and `env` + parameters. + ## Language changes - In the 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: diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index f90b310ea..3e3391d52 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -1438,12 +1438,16 @@ elif not defined(useNimRtl): proc execCmdEx*(command: string, options: set[ProcessOption] = { - poStdErrToStdOut, poUsePath}): tuple[ + poStdErrToStdOut, poUsePath}, env: StringTableRef = nil, + workingDir = "", input = ""): tuple[ output: TaintedString, exitCode: int] {.tags: [ExecIOEffect, ReadIOEffect, RootEffect], gcsafe.} = - ## A convenience proc that runs the `command`, grabs all its output and - ## exit code and returns both. + ## A convenience proc that runs the `command`, and returns its `output` and + ## `exitCode`. `env` and `workingDir` params behave as for `startProcess`. + ## If `input.len > 0`, it is passed as stdin. + ## Note: this could block if `input.len` is greater than your OS's maximum + ## pipe buffer size. ## ## See also: ## * `execCmd proc <#execCmd,string>`_ @@ -1452,17 +1456,32 @@ proc execCmdEx*(command: string, options: set[ProcessOption] = { ## * `execProcess proc ## <#execProcess,string,string,openArray[string],StringTableRef,set[ProcessOption]>`_ ## - ## Example: - ## - ## .. code-block:: Nim - ## let (outp, errC) = execCmdEx("nim c -r mytestfile.nim") + runnableExamples: + var result = execCmdEx("nim r --hints:off -", options = {}, input = "echo 3*4") + import strutils, strtabs + stripLineEnd(result[0]) ## portable way to remove trailing newline, if any + doAssert result == ("12", 0) + doAssert execCmdEx("ls --nonexistant").exitCode != 0 + when defined(posix): + assert execCmdEx("echo $FO", env = newStringTable({"FO": "B"})) == ("B\n", 0) + assert execCmdEx("echo $PWD", workingDir = "/") == ("/\n", 0) - var p = startProcess(command, options = options + {poEvalCommand}) + when (NimMajor, NimMinor, NimPatch) < (1, 3, 5): + doAssert input.len == 0 + doAssert workingDir.len == 0 + doAssert env == nil + + var p = startProcess(command, options = options + {poEvalCommand}, + workingDir = workingDir, env = env) var outp = outputStream(p) - # There is no way to provide input for the child process - # anymore. Closing it will create EOF on stdin instead of eternal - # blocking. + if input.len > 0: + # There is no way to provide input for the child process + # anymore. Closing it will create EOF on stdin instead of eternal + # blocking. + # Writing in chunks would require a selectors (eg kqueue/epoll) to avoid + # blocking on io. + inputStream(p).write(input) close inputStream(p) result = (TaintedString"", -1) diff --git a/lib/system.nim b/lib/system.nim index 8ce79aa2f..6b24bdcb8 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2068,8 +2068,9 @@ const ## is the minor number of Nim's version. ## Odd for devel, even for releases. - NimPatch* {.intdefine.}: int = 3 + NimPatch* {.intdefine.}: int = 5 ## is the patch number of Nim's version. + ## Odd for devel, even for releases. import system/dollars export dollars diff --git a/tests/stdlib/tosproc.nim b/tests/stdlib/tosproc.nim index 73cec0cb7..36afd6a23 100644 --- a/tests/stdlib/tosproc.nim +++ b/tests/stdlib/tosproc.nim @@ -1,7 +1,7 @@ # test the osproc module import stdtest/specialpaths -import "../.." / compiler/unittest_light +import "$nim" / compiler/unittest_light when defined(case_testfile): # compiled test file for child process from posix import exitnow @@ -119,3 +119,16 @@ else: var result = startProcessTest("nim r --hints:off -", options = {}, input = "echo 3*4") doAssert result == ("12\n", 0) + + import std/strtabs + block execProcessTest: + var result = execCmdEx("nim r --hints:off -", options = {}, input = "echo 3*4") + stripLineEnd(result[0]) + doAssert result == ("12", 0) + doAssert execCmdEx("ls --nonexistant").exitCode != 0 + when false: + # bug: on windows, this raises; on posix, passes + doAssert execCmdEx("nonexistant").exitCode != 0 + when defined(posix): + doAssert execCmdEx("echo $FO", env = newStringTable({"FO": "B"})) == ("B\n", 0) + doAssert execCmdEx("echo $PWD", workingDir = "/") == ("/\n", 0) From a3719df8b327ce33d4fdbfc7be06b058e9a2ea48 Mon Sep 17 00:00:00 2001 From: Araq Date: Wed, 13 May 2020 14:29:37 +0200 Subject: [PATCH 0042/3768] fixes #14331 --- lib/pure/quitprocs.nim | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 lib/pure/quitprocs.nim diff --git a/lib/pure/quitprocs.nim b/lib/pure/quitprocs.nim new file mode 100644 index 000000000..76bbcdb2b --- /dev/null +++ b/lib/pure/quitprocs.nim @@ -0,0 +1,28 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2016 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## ``system.addQuitProc`` is nice and very useful but due to its C-based +## implementation it doesn't support closures which limits its usefulness. +## This module fixes this. Later versions of this module will also +## support the JavaScript backend. + +var + gClosures: seq[proc () {.closure.}] + +proc callClosures() {.noconv.} = + for i in countdown(gClosures.len-1, 0): + gClosures[i]() + +proc addQuitClosure*(cl: proc () {.closure.}) = + ## Like ``system.addQuitProc`` but it supports closures. + if gClosures.len == 0: + addQuitProc(callClosures) + gClosures = @[cl] + else: + gClosures.add(cl) From 3573a4f9c13ff2f68afbd1931fc06cc463eb1748 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 13 May 2020 08:19:44 -0700 Subject: [PATCH 0043/3768] no more guessing where compiler msgs came from (#14317) --- changelog.md | 3 +++ compiler/lineinfos.nim | 8 ++++--- compiler/msgs.nim | 48 ++++++++++++++++++++++++++++-------------- 3 files changed, 40 insertions(+), 19 deletions(-) diff --git a/changelog.md b/changelog.md index d6be5a9b7..a93a7e651 100644 --- a/changelog.md +++ b/changelog.md @@ -175,6 +175,9 @@ proc mydiv(a, b): int {.raises: [].} = (likewise with other hints and warnings), which is consistent with all other bool flags. (since 1.3.3). - `nim doc -r main` and `nim rst2html -r main` now call openDefaultBrowser +- new hint: `--hint:msgOrigin` will show where a compiler msg (hint|warning|error) was generated; this + helps in particular when it's non obvious where it came from either because multiple locations generate + the same message, or because the message involves runtime formatting. ## Tool changes diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index d6c863842..2fd5a5a60 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -53,7 +53,8 @@ type hintSource, hintPerformance, hintStackTrace, hintGCStats, hintGlobalVar, hintExpandMacro, hintUser, hintUserRaw, - hintExtendedContext + hintExtendedContext, + hintMsgOrigin, # since 1.3.5 const MsgKindToStr*: array[TMsgKind, string] = [ @@ -140,6 +141,7 @@ const hintUser: "$1", hintUserRaw: "$1", hintExtendedContext: "$1", + hintMsgOrigin: "$1", ] const @@ -166,7 +168,7 @@ const "ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf", "Path", "CondTrue", "CondFalse", "Name", "Pattern", "Exec", "Link", "Dependency", "Source", "Performance", "StackTrace", "GCStats", "GlobalVar", "ExpandMacro", - "User", "UserRaw", "ExtendedContext", + "User", "UserRaw", "ExtendedContext", "MsgOrigin", ] const @@ -192,7 +194,7 @@ proc computeNotesVerbosity(): array[0..3, TNoteKinds] = result[2] = result[3] - {hintStackTrace, warnUninit, hintExtendedContext} result[1] = result[2] - {warnProveField, warnProveIndex, warnGcUnsafe, hintPath, hintDependency, hintCodeBegin, hintCodeEnd, - hintSource, hintGlobalVar, hintGCStats} + hintSource, hintGlobalVar, hintGCStats, hintMsgOrigin} result[0] = result[1] - {hintSuccessX, hintSuccess, hintConf, hintProcessing, hintPattern, hintExecuting, hintLinking, hintCC} diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 43ed8f2a8..a43977606 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -12,6 +12,8 @@ import lineinfos, pathutils import std/private/miscdollars +type InstantiationInfo = typeof(instantiationInfo()) + proc toCChar*(c: char; result: var string) = case c of '\0'..'\x1F', '\x7F'..'\xFF': @@ -255,6 +257,9 @@ proc toLinenumber*(info: TLineInfo): int {.inline.} = proc toColumn*(info: TLineInfo): int {.inline.} = result = info.col +proc toFileLineCol(info: InstantiationInfo): string {.inline.} = + result.toLocation(info.filename, info.line, info.column + ColOffset) + proc toFileLineCol*(conf: ConfigRef; info: TLineInfo): string {.inline.} = result.toLocation(toMsgFilename(conf, info), info.line.int, info.col.int + ColOffset) @@ -487,7 +492,7 @@ proc formatMsg*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string): s conf.toFileLineCol(info) & " " & title & getMessageStr(msg, arg) proc liMessage(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string, - eh: TErrorHandling) = + eh: TErrorHandling, info2: InstantiationInfo) {.noinline.} = var title: string color: ForegroundColor @@ -533,36 +538,47 @@ proc liMessage(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string, styledMsgWriteln(styleBright, x, resetStyle, color, title, resetStyle, s) if conf.hasHint(hintSource): conf.writeSurroundingSrc(info) + if conf.hasHint(hintMsgOrigin): + styledMsgWriteln(styleBright, toFileLineCol(info2), resetStyle, " compiler msg initiated here", KindColor, KindFormat % HintsToStr[ord(hintMsgOrigin) - ord(hintMin)], resetStyle) + handleError(conf, msg, eh, s) -proc fatal*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") = +template fatal*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") = # this fixes bug #7080 so that it is at least obvious 'fatal' # was executed. conf.m.errorOutputs = {eStdOut, eStdErr} - liMessage(conf, info, msg, arg, doAbort) + const info2 = instantiationInfo(-1, fullPaths = true) + liMessage(conf, info, msg, arg, doAbort, info2) -proc globalError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") = - liMessage(conf, info, msg, arg, doRaise) +template globalError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") = + const info2 = instantiationInfo(-1, fullPaths = true) + liMessage(conf, info, msg, arg, doRaise, info2) -proc globalError*(conf: ConfigRef; info: TLineInfo, arg: string) = - liMessage(conf, info, errGenerated, arg, doRaise) +template globalError*(conf: ConfigRef; info: TLineInfo, arg: string) = + const info2 = instantiationInfo(-1, fullPaths = true) + liMessage(conf, info, errGenerated, arg, doRaise, info2) -proc localError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") = - liMessage(conf, info, msg, arg, doNothing) +template localError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") = + const info2 = instantiationInfo(-1, fullPaths = true) + liMessage(conf, info, msg, arg, doNothing, info2) -proc localError*(conf: ConfigRef; info: TLineInfo, arg: string) = - liMessage(conf, info, errGenerated, arg, doNothing) +template localError*(conf: ConfigRef; info: TLineInfo, arg: string) = + const info2 = instantiationInfo(-1, fullPaths = true) + liMessage(conf, info, errGenerated, arg, doNothing, info2) -proc localError*(conf: ConfigRef; info: TLineInfo, format: string, params: openArray[string]) = - localError(conf, info, format % params) +template localError*(conf: ConfigRef; info: TLineInfo, format: string, params: openArray[string]) = + const info2 = instantiationInfo(-1, fullPaths = true) + liMessage(conf, info, errGenerated, format % params, doNothing, info2) -proc message*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") = - liMessage(conf, info, msg, arg, doNothing) +template message*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") = + const info2 = instantiationInfo(-1, fullPaths = true) + liMessage(conf, info, msg, arg, doNothing, info2) proc internalError*(conf: ConfigRef; info: TLineInfo, errMsg: string) = if conf.cmd == cmdIdeTools and conf.structuredErrorHook.isNil: return + const info2 = instantiationInfo(-1, fullPaths = true) writeContext(conf, info) - liMessage(conf, info, errInternal, errMsg, doAbort) + liMessage(conf, info, errInternal, errMsg, doAbort, info2) proc internalError*(conf: ConfigRef; errMsg: string) = if conf.cmd == cmdIdeTools and conf.structuredErrorHook.isNil: return From 79fe87500ff550bba5a2145d2cc405c9e0c944af Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 13 May 2020 22:17:38 +0200 Subject: [PATCH 0044/3768] fixes #13935 --- lib/system/mm/malloc.nim | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/system/mm/malloc.nim b/lib/system/mm/malloc.nim index 0525b0951..883baaeb4 100644 --- a/lib/system/mm/malloc.nim +++ b/lib/system/mm/malloc.nim @@ -1,4 +1,6 @@ +{.push stackTrace: off.} + proc allocImpl(size: Natural): pointer = c_malloc(size.csize_t) @@ -81,3 +83,4 @@ proc dealloc(r: var MemRegion, p: pointer) = dealloc(p) proc deallocOsPages(r: var MemRegion) = discard proc deallocOsPages() = discard +{.pop.} From f3e17e7c776dff9c4557fabe97694612fa86d237 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 13 May 2020 22:29:02 +0200 Subject: [PATCH 0045/3768] fixes #13104 [backport] --- compiler/installer.ini | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/installer.ini b/compiler/installer.ini index fdb717b61..ed815d2eb 100644 --- a/compiler/installer.ini +++ b/compiler/installer.ini @@ -88,6 +88,7 @@ Files: "bin/vccexe.exe" Files: "bin/nimgrab.exe" Files: "bin/nimpretty.exe" Files: "bin/testament.exe" +Files: "bin/nim-gdb.bat" Files: "koch.exe" Files: "finish.exe" @@ -111,6 +112,8 @@ Files: "bin/*.dll" [UnixBin] Files: "bin/nim" +Files: "bin/nim-gdb" +Files: "bin/nim-gdb.bash" [Unix] From 8a339d11787ab782557cf87fc2e4709fbff96a01 Mon Sep 17 00:00:00 2001 From: Araq Date: Wed, 13 May 2020 18:01:00 +0200 Subject: [PATCH 0046/3768] fixes #13862 --- lib/system/mmdisp.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim index 1cdf37604..5cdac2991 100644 --- a/lib/system/mmdisp.nim +++ b/lib/system/mmdisp.nim @@ -83,7 +83,7 @@ else: when not usesDestructors: include "system/cellsets" - when not leakDetector and not useCellIds: + when not leakDetector and not useCellIds and not defined(nimV2): sysAssert(sizeof(Cell) == sizeof(FreeCell), "sizeof FreeCell") when compileOption("gc", "v2"): include "system/gc2" From 82870e0737678256c83f8fc0f2d92293f9f214fe Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 13 May 2020 22:53:41 +0200 Subject: [PATCH 0047/3768] fixes #13998 [backport:1.2] --- compiler/ccgstmts.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index c7c1569b0..20780f734 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -981,7 +981,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) = inc(p.labels, 2) let etmp = p.labels - lineCg(p, cpsStmts, "std::exception_ptr T$1_ = nullptr;", [etmp]) + p.procSec(cpsInit).add(ropecg(p.module, "\tstd::exception_ptr T$1_ = nullptr;", [etmp])) let fin = if t[^1].kind == nkFinally: t[^1] else: nil p.nestedTryStmts.add((fin, false, 0.Natural)) From 5fb40af57ef66618d2003dae1091d80e8f026a1c Mon Sep 17 00:00:00 2001 From: cooldome Date: Wed, 13 May 2020 22:57:05 +0100 Subject: [PATCH 0048/3768] fix #14333 (#14336) Co-authored-by: cooldome --- compiler/semtypes.nim | 6 ++---- tests/arc/tarcmisc.nim | 18 +++++++++++++++++- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 275ad8a07..1aabe435f 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -942,10 +942,8 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType = t.rawAddSonNoPropagationOfTypeFlags result result = t else: discard - #if result.kind == tyRef and c.config.selectedGC == gcDestructors: - # result.flags.incl tfHasAsgn - # XXX Something like this is a good idea but it should be done - # in sempass2! + if result.kind == tyRef and c.config.selectedGC in {gcArc, gcOrc}: + result.flags.incl tfHasAsgn proc findEnforcedStaticType(t: PType): PType = # This handles types such as `static[T] and Foo`, diff --git a/tests/arc/tarcmisc.nim b/tests/arc/tarcmisc.nim index 79c947e82..24f02959c 100644 --- a/tests/arc/tarcmisc.nim +++ b/tests/arc/tarcmisc.nim @@ -3,6 +3,7 @@ discard """ 123xyzabc destroyed: false destroyed: false +1 closed destroying variable ''' @@ -118,4 +119,19 @@ proc free*(game: Game) = proc newGame*(): Game = new(result, free) -var game*: Game \ No newline at end of file +var game*: Game + + +#------------------------------------------------------------------------------ +# issue #14333 + +type + SimpleLoop = object + + Lsg = object + loops: seq[ref SimpleLoop] + root: ref SimpleLoop + +var lsg: Lsg +lsg.loops.add lsg.root +echo lsg.loops.len \ No newline at end of file From a2a3fa10397bc96020a19936daf76b12e9ba9763 Mon Sep 17 00:00:00 2001 From: Kaushal Modi Date: Thu, 14 May 2020 01:27:57 -0400 Subject: [PATCH 0049/3768] Fail quickly if re or nre module is attempted to be compiled with js [backport] (#14341) Fixes https://github.com/nim-lang/Nim/issues/14338 . --- lib/impure/nre.nim | 3 +++ lib/impure/re.nim | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/impure/nre.nim b/lib/impure/nre.nim index 31ceede50..98965d8d2 100644 --- a/lib/impure/nre.nim +++ b/lib/impure/nre.nim @@ -6,6 +6,9 @@ # distribution, for details about the copyright. # +when defined(js): + {.error: "This library needs to be compiled with a c-like backend, and depends on PCRE.".} + ## What is NRE? ## ============ ## diff --git a/lib/impure/re.nim b/lib/impure/re.nim index 809180774..0ad4e3727 100644 --- a/lib/impure/re.nim +++ b/lib/impure/re.nim @@ -7,6 +7,9 @@ # distribution, for details about the copyright. # +when defined(js): + {.error: "This library needs to be compiled with a c-like backend, and depends on PCRE.".} + ## Regular expression support for Nim. ## ## This module is implemented by providing a wrapper around the @@ -678,4 +681,3 @@ when isMainModule: doAssert replace("bar", re"^", "foo") == "foobar" doAssert replace("foo", re"", "-") == "-foo" doAssert replace("foo", re"$", "bar") == "foobar" - From 278b4582f5c7cc3471bccc4ba587030edf7a50e2 Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 14 May 2020 08:51:47 +0200 Subject: [PATCH 0050/3768] fixes #14340 --- compiler/vm.nim | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/vm.nim b/compiler/vm.nim index 3ac075f81..c15f9e268 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -453,7 +453,7 @@ proc opConv(c: PCtx; dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): dest.intVal = cast[BiggestInt](value) of tyBool: dest.ensureKind(rkInt) - dest.intVal = + dest.intVal = case skipTypes(srctyp, abstractRange).kind of tyFloat..tyFloat64: int(src.floatVal != 0.0) else: int(src.intVal != 0) @@ -668,7 +668,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = elif src.kind notin {nkEmpty..nkFloat128Lit} and idx <% src.len: regs[ra].node = src[idx] else: - stackTrace(c, tos, pc, formatErrorIndexBound(idx, src.len-1)) + stackTrace(c, tos, pc, formatErrorIndexBound(idx, src.safeLen-1)) of opcLdArrAddr: # a = addr(b[c]) decodeBC(rkNodeAddr) @@ -679,7 +679,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = if src.kind notin {nkEmpty..nkTripleStrLit} and idx <% src.len: regs[ra].nodeAddr = addr src.sons[idx] else: - stackTrace(c, tos, pc, formatErrorIndexBound(idx, src.len-1)) + stackTrace(c, tos, pc, formatErrorIndexBound(idx, src.safeLen-1)) of opcLdStrIdx: decodeBC(rkInt) let idx = regs[rc].intVal.int @@ -703,7 +703,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = elif idx <% arr.len: writeField(arr[idx], regs[rc]) else: - stackTrace(c, tos, pc, formatErrorIndexBound(idx, arr.len-1)) + stackTrace(c, tos, pc, formatErrorIndexBound(idx, arr.safeLen-1)) of opcLdObj: # a = b.c decodeBC(rkNode) From 80c360fceb367e8b39dd8534313001e5cef4fa7f Mon Sep 17 00:00:00 2001 From: hlaaftana <10591326+hlaaftana@users.noreply.github.com> Date: Fri, 15 May 2020 11:07:30 +0300 Subject: [PATCH 0051/3768] fix #14350, cstrings in JS init as null (#14355) * fix #14350, cstrings in JS init as null --- compiler/jsgen.nim | 4 +--- tests/js/tmangle.nim | 4 ++-- tests/js/trepr.nim | 2 +- tests/system/tdollars.nim | 7 +++++++ 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index c4ee36dbf..6aacf818a 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -1690,9 +1690,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope = result = putToSeq("null", indirect) of tySequence, tyString: result = putToSeq("[]", indirect) - of tyCString: - result = putToSeq("\"\"", indirect) - of tyOpt, tyProc: + of tyCString, tyOpt, tyProc: result = putToSeq("null", indirect) of tyStatic: if t.n != nil: diff --git a/tests/js/tmangle.nim b/tests/js/tmangle.nim index 393043722..caaa15fa1 100644 --- a/tests/js/tmangle.nim +++ b/tests/js/tmangle.nim @@ -62,8 +62,8 @@ block: result = result and obj1.`&&`.addr[] == "bar".cstring result = result and obj2.`if` == 0 result = result and obj2.`for` == 0 - result = result and obj2.`==`.len == 0 - result = result and obj2.`&&`.len == 0 + result = result and obj2.`==`.isNil + result = result and obj2.`&&`.isNil echo test() # Test codegen for fields with uppercase letters: diff --git a/tests/js/trepr.nim b/tests/js/trepr.nim index ffa9d4de0..eb285f70d 100644 --- a/tests/js/trepr.nim +++ b/tests/js/trepr.nim @@ -322,7 +322,7 @@ o = [Field0 = [a = "", b = @[]], Field1 = ""], p = nil, -q = ""] +q = nil] """) doAssert(repr(cc) == """ [a = 12, diff --git a/tests/system/tdollars.nim b/tests/system/tdollars.nim index 478a11b94..6ddec911f 100644 --- a/tests/system/tdollars.nim +++ b/tests/system/tdollars.nim @@ -70,3 +70,10 @@ block: # `$`(SomeInteger) testType uint64 testType int64 testType BiggestInt + +block: # #14350 for JS + var cstr: cstring + doAssert cstr == cstring(nil) + doAssert cstr == nil + doAssert cstr.isNil + doAssert cstr != cstring("") From 105079e88ab88626ab663fa6674e9a5f08c71084 Mon Sep 17 00:00:00 2001 From: Danil Yarantsev <21169548+Yardanico@users.noreply.github.com> Date: Fri, 15 May 2020 17:26:03 +0300 Subject: [PATCH 0052/3768] Speed up testing of some packages (#14358) --- testament/important_packages.nim | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 4186ce30d..cdb98745c 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -21,8 +21,8 @@ pkg1 "bigints" pkg1 "binaryheap", false, "nim c -r binaryheap.nim" pkg1 "BipBuffer" # pkg1 "blscurve", true # pending https://github.com/status-im/nim-blscurve/issues/39 -pkg1 "bncurve", true, "nim c -r tests/tvectors.nim" -pkg1 "brainfuck", true, "nim c -r tests/compile.nim" +pkg1 "bncurve", true +pkg1 "brainfuck", true, "nim c -d:release -r tests/compile.nim" pkg1 "bump", true, "nim c --gc:arc -r tests/tbump.nim", "https://github.com/disruptek/bump" pkg1 "c2nim", false, "nim c testsuite/tester.nim" pkg1 "cascade" @@ -42,7 +42,7 @@ pkg1 "delaunay" pkg1 "docopt" pkg1 "easygl", true, "nim c -o:egl -r src/easygl.nim", "https://github.com/jackmott/easygl" pkg1 "elvis" -pkg1 "fidget", true, "nim c -r tests/runNative.nim" +pkg1 "fidget", true, "nim c -d:release -r tests/runNative.nim" pkg1 "fragments", false, "nim c -r fragments/dsl.nim" pkg1 "gara" pkg1 "ggplotnim", true, "nim c -d:noCairo -r tests/tests.nim" @@ -108,7 +108,7 @@ pkg2 "patty" pkg2 "plotly", true, "nim c --oldgensym:on examples/all.nim" pkg2 "pnm" pkg2 "polypbren" -pkg2 "prologue", true, "nim c -r tests/test_compile/test_compile.nim" +pkg2 "prologue", true, "nim c -d:release -r tests/test_compile/test_compile.nim" pkg2 "protobuf", true, "nim c -o:protobuff -r src/protobuf.nim" pkg2 "pylib" pkg2 "rbtree" @@ -133,8 +133,8 @@ pkg2 "termstyle" pkg2 "timeit" pkg2 "timezones" pkg2 "tiny_sqlite" -pkg2 "unicodedb" -pkg2 "unicodeplus", true +pkg2 "unicodedb", false, "nim c -d:release -r tests/tests.nim" +pkg2 "unicodeplus", true, "nim c -d:release -r tests/tests.nim" pkg2 "unpack" pkg2 "websocket", false, "nim c websocket.nim" # pkg2 "winim", true From 13cfaf5fd55796bc671737a73f35643626c98b6e Mon Sep 17 00:00:00 2001 From: PMunch Date: Fri, 15 May 2020 19:32:52 +0200 Subject: [PATCH 0053/3768] Improve nimeval, changes some defaults (#14351) * Improve nimeval, changes some defaults This improves nimeval to make it easier to use NimScript as an embedded scripting languages. It does change some of the defaults, so in case anything used the old version and depended on the old behaviour these would have to be changed. * Remove dependency on nimhcr for nimscript --- compiler/nimeval.nim | 10 +++++++--- lib/system.nim | 2 +- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/compiler/nimeval.nim b/compiler/nimeval.nim index 882e10fbb..09d1eb1da 100644 --- a/compiler/nimeval.nim +++ b/compiler/nimeval.nim @@ -99,14 +99,16 @@ proc findNimStdLibCompileTime*(): string = proc createInterpreter*(scriptName: string; searchPaths: openArray[string]; - flags: TSandboxFlags = {}): Interpreter = + flags: TSandboxFlags = {}, + defines = @[("nimscript", "true")], + registerOps = true): Interpreter = var conf = newConfigRef() var cache = newIdentCache() var graph = newModuleGraph(cache, conf) connectCallbacks(graph) initDefines(conf.symbols) - defineSymbol(conf.symbols, "nimscript") - defineSymbol(conf.symbols, "nimconfig") + for define in defines: + defineSymbol(conf.symbols, define[0], define[1]) registerPass(graph, semPass) registerPass(graph, evalPass) @@ -119,6 +121,8 @@ proc createInterpreter*(scriptName: string; var vm = newCtx(m, cache, graph) vm.mode = emRepl vm.features = flags + if registerOps: + vm.registerAdditionalOps() # Required to register parts of stdlib modules graph.vm = vm graph.compileSystemModule() result = Interpreter(mainModule: m, graph: graph, scriptName: scriptName) diff --git a/lib/system.nim b/lib/system.nim index 6b24bdcb8..098ad08be 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -3026,5 +3026,5 @@ export widestrs import system/io export io -when not defined(createNimHcr): +when not defined(createNimHcr) and not defined(nimscript): include nimhcr From 9f78f116b2276a367f371ece550f669d64dbe594 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Fri, 15 May 2020 20:24:43 +0200 Subject: [PATCH 0054/3768] New "ping-pong" DFA (#14322) * New ping-pong analysis * Add testcase for #13456 * Remove debugging leftover * Unquote "unstructured controlflow" * Fix typo * Fix exponential complexity in edge cases * Add sanity testcase * Fix --- compiler/dfa.nim | 63 +++++++------------- compiler/injectdestructors.nim | 70 ++++++++++++---------- tests/arc/tmovebug.nim | 103 +++++++++++++++++++++++++++++++++ 3 files changed, 163 insertions(+), 73 deletions(-) diff --git a/compiler/dfa.nim b/compiler/dfa.nim index 0db89579b..67a9e26d8 100644 --- a/compiler/dfa.nim +++ b/compiler/dfa.nim @@ -35,11 +35,11 @@ from patterns import sameTrees type InstrKind* = enum - goto, fork, join, def, use + goto, fork, def, use Instr* = object n*: PNode # contains the def/use location. case kind*: InstrKind - of goto, fork, join: dest*: int + of goto, fork: dest*: int else: discard ControlFlowGraph* = seq[Instr] @@ -47,8 +47,6 @@ type TPosition = distinct int TBlock = object - joins: seq[Instr] - forks: seq[TPosition] case isTryBlock: bool of false: label: PSym @@ -72,7 +70,7 @@ proc codeListing(c: ControlFlowGraph, result: var string, start=0; last = -1) = var jumpTargets = initIntSet() let last = if last < 0: c.len-1 else: min(last, c.len-1) for i in start..last: - if c[i].kind in {goto, fork, join}: + if c[i].kind in {goto, fork}: jumpTargets.incl(i+c[i].dest) var i = start while i <= last: @@ -83,7 +81,7 @@ proc codeListing(c: ControlFlowGraph, result: var string, start=0; last = -1) = case c[i].kind of def, use: result.add renderTree(c[i].n) - of goto, fork, join: + of goto, fork: result.add "L" result.addInt c[i].dest+i result.add("\t#") @@ -102,7 +100,6 @@ proc echoCfg*(c: ControlFlowGraph; start=0; last = -1) {.deprecated.} = proc forkI(c: var Con; n: PNode): TPosition = result = TPosition(c.code.len) c.code.add Instr(n: n, kind: fork, dest: 0) - c.blocks[^1].forks.add result proc gotoI(c: var Con; n: PNode): TPosition = result = TPosition(c.code.len) @@ -110,6 +107,24 @@ proc gotoI(c: var Con; n: PNode): TPosition = #[ +Join is no more +=============== +Instead of generating join instructions we adapt our traversal of the CFG. + +When encountering a fork we split into two paths, we follow the path +starting at "pc + 1" until it encounters the joinpoint: "pc + forkInstr.dest". +If we encounter gotos that would jump further than the current joinpoint, +as can happen with gotos generated by unstructured controlflow such as break, raise or return, +we simply suspend following the current path, and follow the other path until the new joinpoint +which is simply the instruction pointer returned to us by the now suspended path. +If the path we are following now, also encounters a goto that exceeds the joinpoint +we repeat the process; suspending the current path and evaluating the other one with a new joinpoint. +If we eventually reach a common joinpoint we join the two paths. +This new "ping-pong" approach has the obvious advantage of not requiring join instructions, as such +cutting down on the CFG size but is also mandatory for correctly handling complicated cases +of unstructured controlflow. + + Design of join ============== @@ -258,10 +273,6 @@ duplicate the 'join' instructions on breaks and return exits! ]# -proc joinI(c: var Con; fromFork: TPosition; n: PNode) = - let dist = fromFork.int - c.code.len - c.code.add Instr(n: n, kind: join, dest: dist) - proc genLabel(c: Con): TPosition = result = TPosition(c.code.len) @@ -289,10 +300,6 @@ proc popBlock(c: var Con; oldLen: int) = exits.add c.gotoI(newNode(nkEmpty)) for e in exits: c.patch e - for j in c.blocks[oldLen].joins: - var patchedJ = j - patchedJ.dest -= c.code.len - c.code.add patchedJ c.blocks.setLen(oldLen) template withBlock(labl: PSym; body: untyped) {.dirty.} = @@ -353,7 +360,6 @@ when true: c.gen(n[1]) else: withBlock(nil): - let oldForksLen = c.blocks[^1].forks.len var endings: array[3, TPosition] for i in 0..2: c.gen(n[0]) @@ -362,8 +368,6 @@ when true: for i in countdown(endings.high, 0): let endPos = endings[i] c.patch(endPos) - c.joinI(c.blocks[^1].forks.pop(), n) - doAssert(c.blocks[^1].forks.len == oldForksLen) else: @@ -374,7 +378,6 @@ else: # body # jmp lab1 # lab2: - let oldForksLen = c.blocks[^1].forks.len let lab1 = c.genLabel withBlock(nil): if isTrue(n[0]): @@ -386,15 +389,11 @@ else: c.gen(n[1]) c.jmpBack(n, lab1) c.patch(lab2) - setLen(c.blocks[^1].forks, oldForksLen) template forkT(n, body) = - let oldLen = c.blocks[^1].forks.len let lab1 = c.forkI(n) body c.patch(lab1) - c.joinI(lab1, n) - setLen(c.blocks[^1].forks, oldLen) proc genIf(c: var Con, n: PNode) = #[ @@ -433,7 +432,6 @@ proc genIf(c: var Con, n: PNode) = join F1 ]# - let oldLen = c.blocks[^1].forks.len var endings: seq[TPosition] = @[] for i in 0..= c.g.len or min(variantA, variantB) >= until: + break + if variantA < variantB: + variantA = isLastRead(location, c, variantA, min(variantB, until)) + else: + variantB = isLastRead(location, c, variantB, min(variantA, until)) pc = min(variantA, variantB) - of InstrKind.join: - let dest = pc + c.g[pc].dest - if dest == comesFrom: return pc + 1 - inc pc return pc proc isLastRead(n: PNode; c: var Con): bool = @@ -114,12 +116,12 @@ proc isLastRead(n: PNode; c: var Con): bool = # ensure that we don't find another 'use X' instruction. if instr+1 >= c.g.len: return true - result = isLastRead(n, c, instr+1, -1) >= 0 + result = isLastRead(n, c, instr+1, int.high) >= 0 dbg: echo "ugh ", c.otherRead.isNil, " ", result -proc isFirstWrite(location: PNode; c: var Con; pc, comesFrom: int; instr: int): int = +proc isFirstWrite(location: PNode; c: var Con; pc, until: int): int = var pc = pc - while pc < instr: + while pc < until: case c.g[pc].kind of def: if defInstrTargets(c.g[pc], location): @@ -134,15 +136,17 @@ proc isFirstWrite(location: PNode; c: var Con; pc, comesFrom: int; instr: int): pc = pc + c.g[pc].dest of fork: # every branch must not contain a def/use of our location: - let variantA = isFirstWrite(location, c, pc+1, pc, instr) - if variantA < 0: return -1 - var variantB = isFirstWrite(location, c, pc + c.g[pc].dest, pc, instr + c.g[pc].dest) - if variantB < 0: return -1 + var variantA = pc + 1 + var variantB = pc + c.g[pc].dest + while variantA != variantB: + if min(variantA, variantB) < 0: return -1 + if max(variantA, variantB) > until: + break + if variantA < variantB: + variantA = isFirstWrite(location, c, variantA, min(variantB, until)) + else: + variantB = isFirstWrite(location, c, variantB, min(variantA, until)) pc = min(variantA, variantB) - of InstrKind.join: - let dest = pc + c.g[pc].dest - if dest == comesFrom: return pc + 1 - inc pc return pc proc isFirstWrite(n: PNode; c: var Con): bool = @@ -161,10 +165,10 @@ proc isFirstWrite(n: PNode; c: var Con): bool = # ensure that we don't find another 'def/use X' instruction. if instr == 0: return true - result = isFirstWrite(n, c, 0, -1, instr) >= 0 + result = isFirstWrite(n, c, 0, instr) >= 0 proc initialized(code: ControlFlowGraph; pc: int, - init, uninit: var IntSet; comesFrom: int): int = + init, uninit: var IntSet; until: int): int = ## Computes the set of definitely initialized variables across all code paths ## as an IntSet of IDs. var pc = pc @@ -173,20 +177,22 @@ proc initialized(code: ControlFlowGraph; pc: int, of goto: pc = pc + code[pc].dest of fork: - let target = pc + code[pc].dest var initA = initIntSet() var initB = initIntSet() - let pcA = initialized(code, pc+1, initA, uninit, pc) - discard initialized(code, target, initB, uninit, pc) + var variantA = pc + 1 + var variantB = pc + code[pc].dest + while variantA != variantB: + if max(variantA, variantB) > until: + break + if variantA < variantB: + variantA = initialized(code, variantA, initA, uninit, min(variantB, until)) + else: + variantB = initialized(code, variantB, initB, uninit, min(variantA, until)) + pc = min(variantA, variantB) # we add vars if they are in both branches: for v in initA: if v in initB: init.incl v - pc = pcA+1 - of InstrKind.join: - let target = pc + code[pc].dest - if comesFrom == target: return pc - inc pc of use: let v = code[pc].n.sym if v.kind != skParam and v.id notin init: @@ -1040,7 +1046,7 @@ proc computeUninit(c: var Con) = c.uninitComputed = true c.uninit = initIntSet() var init = initIntSet() - discard initialized(c.g, pc = 0, init, c.uninit, comesFrom = -1) + discard initialized(c.g, pc = 0, init, c.uninit, int.high) proc injectDefaultCalls(n: PNode, c: var Con) = case n.kind diff --git a/tests/arc/tmovebug.nim b/tests/arc/tmovebug.nim index 6ed10c403..39f1c8bf4 100644 --- a/tests/arc/tmovebug.nim +++ b/tests/arc/tmovebug.nim @@ -19,6 +19,23 @@ c.text = hello c.text = hello pOD.text = hello pOD.toks = @["hello"] +fff +fff +2 +fff +fff +2 +fff +fff +2 +mmm +fff +fff +fff +3 +mmm +sink me (sink) +assign me (not sink) ''' """ @@ -199,3 +216,89 @@ when false: let pOHD = parseOHD() dump pOHD.text dump pOHD.toks + +# bug #13456 + +iterator combinations[T](s: openarray[T], k: int): seq[T] = + let n = len(s) + assert k >= 0 and k <= n + var pos = newSeq[int](k) + var current = newSeq[T](k) + for i in 0..k-1: + pos[k-i-1] = i + var done = false + while not done: + for i in 0..k-1: + current[i] = s[pos[k-i-1]] + yield current + var i = 0 + while i < k: + pos[i] += 1 + if pos[i] < n-i: + for j in 0..i-1: + pos[j] = pos[i] + i - j + break + i += 1 + if i >= k: + break + +type + UndefEx = object of Exception + +proc main2 = + var delayedSyms = @[1, 2, 3] + var unp: seq[int] + block myb: + for a in 1 .. 2: + if delayedSyms.len > a: + unp = delayedSyms + for t in unp.combinations(a + 1): + try: + var h = false + for k in t: + echo "fff" + if h: continue + if true: + raise newException(UndefEx, "forward declaration") + break myb + except UndefEx: + echo t.len + echo "mmm" + +main2() + + + +type ME = object + who: string + +proc `=`(x: var ME, y: ME) = + if x.who.len > 0: echo "assign ",x.who + +proc `=sink`(x: var ME, y: ME) = + if x.who.len > 0: echo "sink ",x.who + +var dump: ME +template use(x) = dump = x +template def(x) = x = dump + +var c = true + +proc shouldSink() = + var x = ME(who: "me (sink)") + use(x) # we analyse this + if c: def(x) + else: def(x) + use(x) # ok, with the [else] part. + +shouldSink() + +dump = ME() + +proc shouldNotSink() = + var x = ME(who: "me (not sink)") + use(x) # we analyse this + if c: def(x) + use(x) # Not ok without the '[else]' + +shouldNotSink() From ce0552c1008c8292c550e7a973c580f416c005ca Mon Sep 17 00:00:00 2001 From: Bung <535670947@qq.com> Date: Sat, 16 May 2020 03:00:06 +0800 Subject: [PATCH 0055/3768] fix #9771 (#14357) * fix #9771 * map SQLLEN SQLULEN * fix proc params take TSqlLen Co-authored-by: bung87 --- lib/impure/db_odbc.nim | 10 +++++----- lib/wrappers/odbcsql.nim | 26 ++++++++++++++------------ 2 files changed, 19 insertions(+), 17 deletions(-) diff --git a/lib/impure/db_odbc.nim b/lib/impure/db_odbc.nim index 7370adbf3..9ff050bfa 100644 --- a/lib/impure/db_odbc.nim +++ b/lib/impure/db_odbc.nim @@ -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) @@ -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: [].} = @@ -489,7 +489,7 @@ 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 @@ -497,7 +497,7 @@ proc open*(connection, user, password, database: string): DbConn {. 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) diff --git a/lib/wrappers/odbcsql.nim b/lib/wrappers/odbcsql.nim index be56b75d3..393f29ad0 100644 --- a/lib/wrappers/odbcsql.nim +++ b/lib/wrappers/odbcsql.nim @@ -45,8 +45,10 @@ type SqlHDBC* = SqlHandle SqlHStmt* = SqlHandle SqlHDesc* = SqlHandle - TSqlInteger* = int - SqlUInteger* = int + TSqlInteger* = int32 + SqlUInteger* = int32 + TSqlLen* = int64 + TSqlULen* = uint64 SqlPointer* = pointer TSqlReal* = cfloat TSqlDouble* = cdouble @@ -666,7 +668,7 @@ proc SQLAllocHandle*(HandleType: TSqlSmallInt, InputHandle: SqlHandle, OutputHandlePtr: var SqlHandle): TSqlSmallInt{. dynlib: odbclib, importc.} proc SQLSetEnvAttr*(EnvironmentHandle: SqlHEnv, Attribute: TSqlInteger, - Value: TSqlInteger, StringLength: TSqlInteger): TSqlSmallInt{. + Value: SqlPointer, StringLength: TSqlInteger): TSqlSmallInt{. dynlib: odbclib, importc.} proc SQLGetEnvAttr*(EnvironmentHandle: SqlHEnv, Attribute: TSqlInteger, Value: SqlPointer, BufferLength: TSqlInteger, @@ -716,14 +718,14 @@ proc SQLNumResultCols*(StatementHandle: SqlHStmt, ColumnCount: var TSqlSmallInt) proc SQLDescribeCol*(StatementHandle: SqlHStmt, ColumnNumber: SqlUSmallInt, ColumnName: PSQLCHAR, BufferLength: TSqlSmallInt, NameLength: var TSqlSmallInt, DataType: var TSqlSmallInt, - ColumnSize: var SqlUInteger, + ColumnSize: var TSQLULEN, DecimalDigits: var TSqlSmallInt, Nullable: var TSqlSmallInt): TSqlSmallInt{. dynlib: odbclib, importc.} proc SQLFetchScroll*(StatementHandle: SqlHStmt, FetchOrientation: TSqlSmallInt, FetchOffset: TSqlInteger): TSqlSmallInt{.dynlib: odbclib, importc.} proc SQLExtendedFetch*(hstmt: SqlHStmt, fFetchType: SqlUSmallInt, - irow: TSqlInteger, pcrow: PSQLUINTEGER, + irow: TSqlInteger, pcrow: var TSQLULEN, rgfRowStatus: PSQLUSMALLINT): TSqlSmallInt{.dynlib: odbclib, importc.} proc SQLGetData*(StatementHandle: SqlHStmt, ColumnNumber: SqlUSmallInt, @@ -741,13 +743,13 @@ proc SQLGetInfo*(ConnectionHandle: SqlHDBC, InfoType: SqlUSmallInt, InfoValue: SqlPointer, BufferLength: TSqlSmallInt, StringLength: PSQLSMALLINT): TSqlSmallInt{.dynlib: odbclib, importc.} -proc SQLBulkOperations*(StatementHandle: SqlHStmt, Operation: TSqlSmallInt): TSqlSmallInt{. +proc SQLBulkOperations*(StatementHandle: SqlHStmt, Operation: SqlUSmallInt): TSqlSmallInt{. dynlib: odbclib, importc.} proc SQLPutData*(StatementHandle: SqlHStmt, Data: SqlPointer, - StrLen_or_Ind: TSqlInteger): TSqlSmallInt{.dynlib: odbclib, importc.} + StrLen_or_Ind: TSQLLEN): TSqlSmallInt{.dynlib: odbclib, importc.} proc SQLBindCol*(StatementHandle: SqlHStmt, ColumnNumber: SqlUSmallInt, TargetType: TSqlSmallInt, TargetValue: SqlPointer, - BufferLength: TSqlInteger, StrLen_or_Ind: PSQLINTEGER): TSqlSmallInt{. + BufferLength: TSqlLEN, StrLen_or_Ind: PSQLINTEGER): TSqlSmallInt{. dynlib: odbclib, importc.} proc SQLSetPos*(hstmt: SqlHStmt, irow: SqlUSmallInt, fOption: SqlUSmallInt, fLock: SqlUSmallInt): TSqlSmallInt{.dynlib: odbclib, importc.} @@ -770,13 +772,13 @@ proc SQLGetCursorName*(StatementHandle: SqlHStmt, CursorName: PSQLCHAR, proc SQLSetCursorName*(StatementHandle: SqlHStmt, CursorName: PSQLCHAR, NameLength: TSqlSmallInt): TSqlSmallInt{.dynlib: odbclib, importc.} -proc SQLRowCount*(StatementHandle: SqlHStmt, RowCount: var TSqlInteger): TSqlSmallInt{. +proc SQLRowCount*(StatementHandle: SqlHStmt, RowCount: var TSQLLEN): TSqlSmallInt{. dynlib: odbclib, importc.} proc SQLBindParameter*(hstmt: SqlHStmt, ipar: SqlUSmallInt, fParamType: TSqlSmallInt, fCType: TSqlSmallInt, - fSqlType: TSqlSmallInt, cbColDef: SqlUInteger, + fSqlType: TSqlSmallInt, cbColDef: TSQLULEN, ibScale: TSqlSmallInt, rgbValue: SqlPointer, - cbValueMax: TSqlInteger, pcbValue: PSQLINTEGER): TSqlSmallInt{. + cbValueMax: TSQLLEN, pcbValue: var TSQLLEN): TSqlSmallInt{. dynlib: odbclib, importc.} proc SQLFreeStmt*(StatementHandle: SqlHStmt, Option: SqlUSmallInt): TSqlSmallInt{. dynlib: odbclib, importc.} @@ -784,7 +786,7 @@ proc SQLColAttribute*(StatementHandle: SqlHStmt, ColumnNumber: SqlUSmallInt, FieldIdentifier: SqlUSmallInt, CharacterAttribute: PSQLCHAR, BufferLength: TSqlSmallInt, StringLength: PSQLSMALLINT, - NumericAttribute: SqlPointer): TSqlSmallInt{. + NumericAttribute: TSQLLEN): TSqlSmallInt{. dynlib: odbclib, importc.} proc SQLEndTran*(HandleType: TSqlSmallInt, Handle: SqlHandle, CompletionType: TSqlSmallInt): TSqlSmallInt{.dynlib: odbclib, From bf0e1c696f6be96026d72838010ef8e63bd13238 Mon Sep 17 00:00:00 2001 From: Kaushal Modi Date: Fri, 15 May 2020 17:37:24 -0400 Subject: [PATCH 0056/3768] Remove the uses of {.procvar.} pragma (#14359) This pragma did nothing. Ref: - https://github.com/nim-lang/Nim/issues/2172#issuecomment-383276469 - https://github.com/nim-lang/Nim/issues/12975 --- lib/packages/docutils/rst.nim | 4 +- lib/pure/asyncftpclient.nim | 4 +- lib/pure/rationals.nim | 2 +- lib/pure/strmisc.nim | 7 ++- lib/pure/strutils.nim | 48 +++++++++---------- lib/pure/unicode.nim | 36 +++++++------- lib/system.nim | 4 +- nimsuggest/sexp.nim | 14 +++--- tests/generics/t6137.nim | 2 +- .../argument_parser/argument_parser.nim | 4 +- .../keineschweine/lib/client_helpers.nim | 6 +-- tools/nimgrab.nim | 2 +- tools/urldownloader.nim | 2 +- 13 files changed, 67 insertions(+), 68 deletions(-) diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 50b855662..07cb3eccc 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -305,14 +305,14 @@ proc whichMsgClass*(k: MsgKind): MsgClass = else: assert false, "msgkind does not fit naming scheme" proc defaultMsgHandler*(filename: string, line, col: int, msgkind: MsgKind, - arg: string) {.procvar.} = + arg: string) = let mc = msgkind.whichMsgClass let a = messages[msgkind] % arg let message = "$1($2, $3) $4: $5" % [filename, $line, $col, $mc, a] if mc == mcError: raise newException(EParseError, message) else: writeLine(stdout, message) -proc defaultFindFile*(filename: string): string {.procvar.} = +proc defaultFindFile*(filename: string): string = if existsFile(filename): result = filename else: result = "" diff --git a/lib/pure/asyncftpclient.nim b/lib/pure/asyncftpclient.nim index 7bbfc2a35..132682b23 100644 --- a/lib/pure/asyncftpclient.nim +++ b/lib/pure/asyncftpclient.nim @@ -322,7 +322,7 @@ proc getFile(ftp: AsyncFtpClient, file: File, total: BiggestInt, assertReply(await(ftp.expectReply()), "226") proc defaultOnProgressChanged*(total, progress: BiggestInt, - speed: float): Future[void] {.nimcall, gcsafe, procvar.} = + speed: float): Future[void] {.nimcall, gcsafe.} = ## Default FTP ``onProgressChanged`` handler. Does nothing. result = newFuture[void]() #echo(total, " ", progress, " ", speed) @@ -358,7 +358,7 @@ proc doUpload(ftp: AsyncFtpClient, file: File, var countdownFut = sleepAsync(1000) var sendFut: Future[void] = nil while ftp.dsockConnected: - if sendFut == nil or sendFut.finished: + if sendFut == nil or sendFut.finished: # TODO: Async file reading. let len = file.readBuffer(addr(data[0]), 4000) setLen(data, len) diff --git a/lib/pure/rationals.nim b/lib/pure/rationals.nim index d4b1485f5..c7af91edd 100644 --- a/lib/pure/rationals.nim +++ b/lib/pure/rationals.nim @@ -225,7 +225,7 @@ proc `/=`*[T](x: var Rational[T], y: T) = x.den *= y reduce(x) -proc cmp*(x, y: Rational): int {.procvar.} = +proc cmp*(x, y: Rational): int = ## Compares two rationals. (x - y).num diff --git a/lib/pure/strmisc.nim b/lib/pure/strmisc.nim index 02bbdbcf4..061c2063b 100644 --- a/lib/pure/strmisc.nim +++ b/lib/pure/strmisc.nim @@ -12,8 +12,7 @@ import strutils -proc expandTabs*(s: string, tabSize: int = 8): string {.noSideEffect, - procvar.} = +proc expandTabs*(s: string, tabSize: int = 8): string {.noSideEffect.} = ## Expand tab characters in `s` replacing them by spaces. ## ## The amount of inserted spaces for each tab character is the difference @@ -53,7 +52,7 @@ proc expandTabs*(s: string, tabSize: int = 8): string {.noSideEffect, proc partition*(s: string, sep: string, right: bool = false): (string, string, string) - {.noSideEffect, procvar.} = + {.noSideEffect.} = ## Split the string at the first or last occurrence of `sep` into a 3-tuple ## ## Returns a 3 string tuple of (beforeSep, `sep`, afterSep) or @@ -72,7 +71,7 @@ proc partition*(s: string, sep: string, return if right: ("", "", s) else: (s, "", "") proc rpartition*(s: string, sep: string): (string, string, string) - {.noSideEffect, procvar.} = + {.noSideEffect.} = ## Split the string at the last occurrence of `sep` into a 3-tuple ## ## Returns a 3 string tuple of (beforeSep, `sep`, afterSep) or diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index bb68085aa..7c00de2d2 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -119,7 +119,7 @@ const ## doAssert "01234".find(invalid) == -1 ## doAssert "01A34".find(invalid) == 2 -proc isAlphaAscii*(c: char): bool {.noSideEffect, procvar, +proc isAlphaAscii*(c: char): bool {.noSideEffect, rtl, extern: "nsuIsAlphaAsciiChar".} = ## Checks whether or not character `c` is alphabetical. ## @@ -131,7 +131,7 @@ proc isAlphaAscii*(c: char): bool {.noSideEffect, procvar, doAssert isAlphaAscii('8') == false return c in Letters -proc isAlphaNumeric*(c: char): bool {.noSideEffect, procvar, +proc isAlphaNumeric*(c: char): bool {.noSideEffect, rtl, extern: "nsuIsAlphaNumericChar".} = ## Checks whether or not `c` is alphanumeric. ## @@ -142,7 +142,7 @@ proc isAlphaNumeric*(c: char): bool {.noSideEffect, procvar, doAssert isAlphaNumeric(' ') == false return c in Letters+Digits -proc isDigit*(c: char): bool {.noSideEffect, procvar, +proc isDigit*(c: char): bool {.noSideEffect, rtl, extern: "nsuIsDigitChar".} = ## Checks whether or not `c` is a number. ## @@ -152,7 +152,7 @@ proc isDigit*(c: char): bool {.noSideEffect, procvar, doAssert isDigit('8') == true return c in Digits -proc isSpaceAscii*(c: char): bool {.noSideEffect, procvar, +proc isSpaceAscii*(c: char): bool {.noSideEffect, rtl, extern: "nsuIsSpaceAsciiChar".} = ## Checks whether or not `c` is a whitespace character. runnableExamples: @@ -161,7 +161,7 @@ proc isSpaceAscii*(c: char): bool {.noSideEffect, procvar, doAssert isSpaceAscii('\t') == true return c in Whitespace -proc isLowerAscii*(c: char): bool {.noSideEffect, procvar, +proc isLowerAscii*(c: char): bool {.noSideEffect, rtl, extern: "nsuIsLowerAsciiChar".} = ## Checks whether or not `c` is a lower case character. ## @@ -176,7 +176,7 @@ proc isLowerAscii*(c: char): bool {.noSideEffect, procvar, doAssert isLowerAscii('7') == false return c in {'a'..'z'} -proc isUpperAscii*(c: char): bool {.noSideEffect, procvar, +proc isUpperAscii*(c: char): bool {.noSideEffect, rtl, extern: "nsuIsUpperAsciiChar".} = ## Checks whether or not `c` is an upper case character. ## @@ -192,7 +192,7 @@ proc isUpperAscii*(c: char): bool {.noSideEffect, procvar, return c in {'A'..'Z'} -proc toLowerAscii*(c: char): char {.noSideEffect, procvar, +proc toLowerAscii*(c: char): char {.noSideEffect, rtl, extern: "nsuToLowerAsciiChar".} = ## Returns the lower case version of character ``c``. ## @@ -216,7 +216,7 @@ template toImpl(call) = for i in 0..len(s) - 1: result[i] = call(s[i]) -proc toLowerAscii*(s: string): string {.noSideEffect, procvar, +proc toLowerAscii*(s: string): string {.noSideEffect, rtl, extern: "nsuToLowerAsciiStr".} = ## Converts string `s` into lower case. ## @@ -230,7 +230,7 @@ proc toLowerAscii*(s: string): string {.noSideEffect, procvar, doAssert toLowerAscii("FooBar!") == "foobar!" toImpl toLowerAscii -proc toUpperAscii*(c: char): char {.noSideEffect, procvar, +proc toUpperAscii*(c: char): char {.noSideEffect, rtl, extern: "nsuToUpperAsciiChar".} = ## Converts character `c` into upper case. ## @@ -250,7 +250,7 @@ proc toUpperAscii*(c: char): char {.noSideEffect, procvar, else: result = c -proc toUpperAscii*(s: string): string {.noSideEffect, procvar, +proc toUpperAscii*(s: string): string {.noSideEffect, rtl, extern: "nsuToUpperAsciiStr".} = ## Converts string `s` into upper case. ## @@ -264,7 +264,7 @@ proc toUpperAscii*(s: string): string {.noSideEffect, procvar, doAssert toUpperAscii("FooBar!") == "FOOBAR!" toImpl toUpperAscii -proc capitalizeAscii*(s: string): string {.noSideEffect, procvar, +proc capitalizeAscii*(s: string): string {.noSideEffect, rtl, extern: "nsuCapitalizeAscii".} = ## Converts the first character of string `s` into upper case. ## @@ -299,7 +299,7 @@ proc nimIdentNormalize*(s: string): string = inc j if j != s.len: setLen(result, j) -proc normalize*(s: string): string {.noSideEffect, procvar, +proc normalize*(s: string): string {.noSideEffect, rtl, extern: "nsuNormalize".} = ## Normalizes the string `s`. ## @@ -323,7 +323,7 @@ proc normalize*(s: string): string {.noSideEffect, procvar, if j != s.len: setLen(result, j) proc cmpIgnoreCase*(a, b: string): int {.noSideEffect, - rtl, extern: "nsuCmpIgnoreCase", procvar.} = + rtl, extern: "nsuCmpIgnoreCase".} = ## Compares two strings in a case insensitive manner. Returns: ## ## | 0 if a == b @@ -345,7 +345,7 @@ proc cmpIgnoreCase*(a, b: string): int {.noSideEffect, # thus we compile without checks here proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect, - rtl, extern: "nsuCmpIgnoreStyle", procvar.} = + rtl, extern: "nsuCmpIgnoreStyle".} = ## Semantically the same as ``cmp(normalize(a), normalize(b))``. It ## is just optimized to not allocate temporary strings. This should ## NOT be used to compare Nim identifier names. @@ -1096,7 +1096,7 @@ proc intToStr*(x: int, minchars: Positive = 1): string {.noSideEffect, if x < 0: result = '-' & result -proc parseInt*(s: string): int {.noSideEffect, procvar, +proc parseInt*(s: string): int {.noSideEffect, rtl, extern: "nsuParseInt".} = ## Parses a decimal integer value contained in `s`. ## @@ -1107,7 +1107,7 @@ proc parseInt*(s: string): int {.noSideEffect, procvar, if L != s.len or L == 0: raise newException(ValueError, "invalid integer: " & s) -proc parseBiggestInt*(s: string): BiggestInt {.noSideEffect, procvar, +proc parseBiggestInt*(s: string): BiggestInt {.noSideEffect, rtl, extern: "nsuParseBiggestInt".} = ## Parses a decimal integer value contained in `s`. ## @@ -1116,7 +1116,7 @@ proc parseBiggestInt*(s: string): BiggestInt {.noSideEffect, procvar, if L != s.len or L == 0: raise newException(ValueError, "invalid integer: " & s) -proc parseUInt*(s: string): uint {.noSideEffect, procvar, +proc parseUInt*(s: string): uint {.noSideEffect, rtl, extern: "nsuParseUInt".} = ## Parses a decimal unsigned integer value contained in `s`. ## @@ -1125,7 +1125,7 @@ proc parseUInt*(s: string): uint {.noSideEffect, procvar, if L != s.len or L == 0: raise newException(ValueError, "invalid unsigned integer: " & s) -proc parseBiggestUInt*(s: string): BiggestUInt {.noSideEffect, procvar, +proc parseBiggestUInt*(s: string): BiggestUInt {.noSideEffect, rtl, extern: "nsuParseBiggestUInt".} = ## Parses a decimal unsigned integer value contained in `s`. ## @@ -1134,7 +1134,7 @@ proc parseBiggestUInt*(s: string): BiggestUInt {.noSideEffect, procvar, if L != s.len or L == 0: raise newException(ValueError, "invalid unsigned integer: " & s) -proc parseFloat*(s: string): float {.noSideEffect, procvar, +proc parseFloat*(s: string): float {.noSideEffect, rtl, extern: "nsuParseFloat".} = ## Parses a decimal floating point value contained in `s`. ## @@ -1147,7 +1147,7 @@ proc parseFloat*(s: string): float {.noSideEffect, procvar, if L != s.len or L == 0: raise newException(ValueError, "invalid float: " & s) -proc parseBinInt*(s: string): int {.noSideEffect, procvar, +proc parseBinInt*(s: string): int {.noSideEffect, rtl, extern: "nsuParseBinInt".} = ## Parses a binary integer value contained in `s`. ## @@ -1176,7 +1176,7 @@ proc parseOctInt*(s: string): int {.noSideEffect, if L != s.len or L == 0: raise newException(ValueError, "invalid oct integer: " & s) -proc parseHexInt*(s: string): int {.noSideEffect, procvar, +proc parseHexInt*(s: string): int {.noSideEffect, rtl, extern: "nsuParseHexInt".} = ## Parses a hexadecimal integer value contained in `s`. ## @@ -1202,7 +1202,7 @@ proc generateHexCharToValueMap(): string = const hexCharToValueMap = generateHexCharToValueMap() -proc parseHexStr*(s: string): string {.noSideEffect, procvar, +proc parseHexStr*(s: string): string {.noSideEffect, rtl, extern: "nsuParseHexStr".} = ## Convert hex-encoded string to byte string, e.g.: ## @@ -2918,12 +2918,12 @@ iterator tokenize*(s: string, seps: set[char] = Whitespace): tuple[ break i = j -proc isEmptyOrWhitespace*(s: string): bool {.noSideEffect, procvar, rtl, +proc isEmptyOrWhitespace*(s: string): bool {.noSideEffect, rtl, extern: "nsuIsEmptyOrWhitespace".} = ## Checks if `s` is empty or consists entirely of whitespace characters. result = s.allCharsInSet(Whitespace) -proc isNilOrWhitespace*(s: string): bool {.noSideEffect, procvar, rtl, +proc isNilOrWhitespace*(s: string): bool {.noSideEffect, rtl, extern: "nsuIsNilOrWhitespace", deprecated: "use isEmptyOrWhitespace instead".} = ## Alias for isEmptyOrWhitespace diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim index 2d54aaf31..497d5e465 100644 --- a/lib/pure/unicode.nim +++ b/lib/pure/unicode.nim @@ -470,7 +470,7 @@ proc binarySearch(c: RuneImpl, tab: openArray[int], len, stride: int): int = return t return -1 -proc toLower*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} = +proc toLower*(c: Rune): Rune {.rtl, extern: "nuc$1".} = ## Converts ``c`` into lower case. This works for any rune. ## ## If possible, prefer ``toLower`` over ``toUpper``. @@ -488,7 +488,7 @@ proc toLower*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} = return Rune(c + toLowerSinglets[p+1] - 500) return Rune(c) -proc toUpper*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} = +proc toUpper*(c: Rune): Rune {.rtl, extern: "nuc$1".} = ## Converts ``c`` into upper case. This works for any rune. ## ## If possible, prefer ``toLower`` over ``toUpper``. @@ -506,7 +506,7 @@ proc toUpper*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} = return Rune(c + toUpperSinglets[p+1] - 500) return Rune(c) -proc toTitle*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} = +proc toTitle*(c: Rune): Rune {.rtl, extern: "nuc$1".} = ## Converts ``c`` to title case. ## ## See also: @@ -519,7 +519,7 @@ proc toTitle*(c: Rune): Rune {.rtl, extern: "nuc$1", procvar.} = return Rune(c + toTitleSinglets[p+1] - 500) return Rune(c) -proc isLower*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} = +proc isLower*(c: Rune): bool {.rtl, extern: "nuc$1".} = ## Returns true if ``c`` is a lower case rune. ## ## If possible, prefer ``isLower`` over ``isUpper``. @@ -537,7 +537,7 @@ proc isLower*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} = if p >= 0 and c == toUpperSinglets[p]: return true -proc isUpper*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} = +proc isUpper*(c: Rune): bool {.rtl, extern: "nuc$1".} = ## Returns true if ``c`` is a upper case rune. ## ## If possible, prefer ``isLower`` over ``isUpper``. @@ -557,7 +557,7 @@ proc isUpper*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} = if p >= 0 and c == toLowerSinglets[p]: return true -proc isAlpha*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} = +proc isAlpha*(c: Rune): bool {.rtl, extern: "nuc$1".} = ## Returns true if ``c`` is an *alpha* rune (i.e., a letter). ## ## See also: @@ -576,7 +576,7 @@ proc isAlpha*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} = if p >= 0 and c == alphaSinglets[p]: return true -proc isTitle*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} = +proc isTitle*(c: Rune): bool {.rtl, extern: "nuc$1".} = ## Returns true if ``c`` is a Unicode titlecase code point. ## ## See also: @@ -587,7 +587,7 @@ proc isTitle*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} = ## * `isWhiteSpace proc <#isWhiteSpace,Rune>`_ return isUpper(c) and isLower(c) -proc isWhiteSpace*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} = +proc isWhiteSpace*(c: Rune): bool {.rtl, extern: "nuc$1".} = ## Returns true if ``c`` is a Unicode whitespace code point. ## ## See also: @@ -600,7 +600,7 @@ proc isWhiteSpace*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} = if p >= 0 and c >= spaceRanges[p] and c <= spaceRanges[p+1]: return true -proc isCombining*(c: Rune): bool {.rtl, extern: "nuc$1", procvar.} = +proc isCombining*(c: Rune): bool {.rtl, extern: "nuc$1".} = ## Returns true if ``c`` is a Unicode combining code unit. ## ## See also: @@ -627,7 +627,7 @@ template runeCheck(s, runeProc) = fastRuneAt(s, i, rune, doInc = true) result = runeProc(rune) and result -proc isAlpha*(s: string): bool {.noSideEffect, procvar, +proc isAlpha*(s: string): bool {.noSideEffect, rtl, extern: "nuc$1Str".} = ## Returns true if ``s`` contains all alphabetic runes. runnableExamples: @@ -635,7 +635,7 @@ proc isAlpha*(s: string): bool {.noSideEffect, procvar, doAssert a.isAlpha runeCheck(s, isAlpha) -proc isSpace*(s: string): bool {.noSideEffect, procvar, +proc isSpace*(s: string): bool {.noSideEffect, rtl, extern: "nuc$1Str".} = ## Returns true if ``s`` contains all whitespace runes. runnableExamples: @@ -656,21 +656,21 @@ template convertRune(s, runeProc) = rune = runeProc(rune) fastToUTF8Copy(rune, result, resultIndex, doInc = true) -proc toUpper*(s: string): string {.noSideEffect, procvar, +proc toUpper*(s: string): string {.noSideEffect, rtl, extern: "nuc$1Str".} = ## Converts ``s`` into upper-case runes. runnableExamples: doAssert toUpper("abγ") == "ABΓ" convertRune(s, toUpper) -proc toLower*(s: string): string {.noSideEffect, procvar, +proc toLower*(s: string): string {.noSideEffect, rtl, extern: "nuc$1Str".} = ## Converts ``s`` into lower-case runes. runnableExamples: doAssert toLower("ABΓ") == "abγ" convertRune(s, toLower) -proc swapCase*(s: string): string {.noSideEffect, procvar, +proc swapCase*(s: string): string {.noSideEffect, rtl, extern: "nuc$1".} = ## Swaps the case of runes in ``s``. ## @@ -692,7 +692,7 @@ proc swapCase*(s: string): string {.noSideEffect, procvar, rune = rune.toUpper() fastToUTF8Copy(rune, result, resultIndex, doInc = true) -proc capitalize*(s: string): string {.noSideEffect, procvar, +proc capitalize*(s: string): string {.noSideEffect, rtl, extern: "nuc$1".} = ## Converts the first character of ``s`` into an upper-case rune. runnableExamples: @@ -759,7 +759,7 @@ proc translate*(s: string, replacements: proc(key: string): string): string {. let word = s[wordStart .. ^1] result.add(replacements(word)) -proc title*(s: string): string {.noSideEffect, procvar, +proc title*(s: string): string {.noSideEffect, rtl, extern: "nuc$1".} = ## Converts ``s`` to a unicode title. ## @@ -821,7 +821,7 @@ proc toRunes*(s: string): seq[Rune] = for r in s.runes: result.add(r) -proc cmpRunesIgnoreCase*(a, b: string): int {.rtl, extern: "nuc$1", procvar.} = +proc cmpRunesIgnoreCase*(a, b: string): int {.rtl, extern: "nuc$1".} = ## Compares two UTF-8 strings and ignores the case. Returns: ## ## | 0 if a == b @@ -1226,7 +1226,7 @@ proc isUpper*(s: string, skipNonAlpha: bool): bool {. ## an empty string. runeCaseCheck(s, isUpper, skipNonAlpha) -proc isTitle*(s: string): bool {.noSideEffect, procvar, rtl, extern: "nuc$1Str", +proc isTitle*(s: string): bool {.noSideEffect, rtl, extern: "nuc$1Str", deprecated: "Deprecated since version 0.20 since its semantics are unclear".} = ## **Deprecated since version 0.20 since its semantics are unclear** ## diff --git a/lib/system.nim b/lib/system.nim index 098ad08be..e5b359c70 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -876,7 +876,7 @@ proc `of`*[T, S](x: T, y: S): bool {.magic: "Of", noSideEffect.} ## assert(FloatingPointDefect of Exception) ## assert(DivByZeroDefect of Exception) -proc cmp*[T](x, y: T): int {.procvar.} = +proc cmp*[T](x, y: T): int = ## Generic compare proc. ## ## Returns: @@ -894,7 +894,7 @@ proc cmp*[T](x, y: T): int {.procvar.} = if x < y: return -1 return 1 -proc cmp*(x, y: string): int {.noSideEffect, procvar.} +proc cmp*(x, y: string): int {.noSideEffect.} ## Compare proc for strings. More efficient than the generic version. ## ## **Note**: The precise result values depend on the used C runtime library and diff --git a/nimsuggest/sexp.nim b/nimsuggest/sexp.nim index 654640309..a4368a558 100644 --- a/nimsuggest/sexp.nim +++ b/nimsuggest/sexp.nim @@ -290,7 +290,7 @@ proc raiseParseErr*(p: SexpParser, msg: string) {.noinline, noreturn.} = ## raises an `ESexpParsingError` exception. raise newException(SexpParsingError, errorMsgExpected(p, msg)) -proc newSString*(s: string): SexpNode {.procvar.}= +proc newSString*(s: string): SexpNode = ## Creates a new `SString SexpNode`. result = SexpNode(kind: SString, str: s) @@ -298,27 +298,27 @@ proc newSStringMove(s: string): SexpNode = result = SexpNode(kind: SString) shallowCopy(result.str, s) -proc newSInt*(n: BiggestInt): SexpNode {.procvar.} = +proc newSInt*(n: BiggestInt): SexpNode = ## Creates a new `SInt SexpNode`. result = SexpNode(kind: SInt, num: n) -proc newSFloat*(n: float): SexpNode {.procvar.} = +proc newSFloat*(n: float): SexpNode = ## Creates a new `SFloat SexpNode`. result = SexpNode(kind: SFloat, fnum: n) -proc newSNil*(): SexpNode {.procvar.} = +proc newSNil*(): SexpNode = ## Creates a new `SNil SexpNode`. result = SexpNode(kind: SNil) -proc newSCons*(car, cdr: SexpNode): SexpNode {.procvar.} = +proc newSCons*(car, cdr: SexpNode): SexpNode = ## Creates a new `SCons SexpNode` result = SexpNode(kind: SCons, car: car, cdr: cdr) -proc newSList*(): SexpNode {.procvar.} = +proc newSList*(): SexpNode = ## Creates a new `SList SexpNode` result = SexpNode(kind: SList, elems: @[]) -proc newSSymbol*(s: string): SexpNode {.procvar.} = +proc newSSymbol*(s: string): SexpNode = result = SexpNode(kind: SSymbol, symbol: s) proc newSSymbolMove(s: string): SexpNode = diff --git a/tests/generics/t6137.nim b/tests/generics/t6137.nim index abf02a756..991f39dd1 100644 --- a/tests/generics/t6137.nim +++ b/tests/generics/t6137.nim @@ -16,7 +16,7 @@ let # define a vector of length 3 x: vector[3] = [1.0, 3.0, 5.0] -proc vectFunc[T](x: vector[T]): vector[T] {.procvar.} = +proc vectFunc[T](x: vector[T]): vector[T] = # Define a vector function result = 2.0*x diff --git a/tests/manyloc/argument_parser/argument_parser.nim b/tests/manyloc/argument_parser/argument_parser.nim index 9448fa7e5..459e38674 100644 --- a/tests/manyloc/argument_parser/argument_parser.nim +++ b/tests/manyloc/argument_parser/argument_parser.nim @@ -102,7 +102,7 @@ type # - Tparam_kind procs -proc `$`*(value: Tparam_kind): string {.procvar.} = +proc `$`*(value: Tparam_kind): string = ## Stringifies the type, used to generate help texts. case value: of PK_EMPTY: result = "" @@ -137,7 +137,7 @@ proc new_parameter_specification*(consumes = PK_EMPTY, # - Tparsed_parameter procs -proc `$`*(data: Tparsed_parameter): string {.procvar.} = +proc `$`*(data: Tparsed_parameter): string = ## Stringifies the value, mostly for debug purposes. ## ## The proc will display the value followed by non string type in brackets. diff --git a/tests/manyloc/keineschweine/lib/client_helpers.nim b/tests/manyloc/keineschweine/lib/client_helpers.nim index 0ebb4ade1..5f74aef85 100644 --- a/tests/manyloc/keineschweine/lib/client_helpers.nim +++ b/tests/manyloc/keineschweine/lib/client_helpers.nim @@ -69,7 +69,7 @@ proc updateFileProgress*() = downloadProgress.setString($currentFileTransfer.pos & '/' & $currentFileTransfer.fullLen) ## HFileTransfer -proc handleFilePartRecv*(serv: PServer; buffer: PBuffer) {.procvar.} = +proc handleFilePartRecv*(serv: PServer; buffer: PBuffer) = var f = readScFileTransfer(buffer) updateFileProgress() @@ -110,7 +110,7 @@ proc saveCurrentFile() = echo "Write file" ## HChallengeResult -proc handleFileChallengeResult*(serv: PServer; buffer: PBuffer) {.procvar.} = +proc handleFileChallengeResult*(serv: PServer; buffer: PBuffer) = var res = readScChallengeResult(buffer).status echo "got challnege result: ", res if res and currentFileTransfer.readyToSave: @@ -122,7 +122,7 @@ proc handleFileChallengeResult*(serv: PServer; buffer: PBuffer) {.procvar.} = echo "REsetting current file" ## HFileCHallenge -proc handleFileChallenge*(serv: PServer; buffer: PBuffer) {.procvar.} = +proc handleFileChallenge*(serv: PServer; buffer: PBuffer) = var challenge = readScFileChallenge(buffer) path = expandPath(challenge) diff --git a/tools/nimgrab.nim b/tools/nimgrab.nim index 937a5dcd4..ee5eced1e 100644 --- a/tools/nimgrab.nim +++ b/tools/nimgrab.nim @@ -5,7 +5,7 @@ when defined(windows): proc syncDownload(url, file: string) = proc progress(status: DownloadStatus, progress: uint, total: uint, - message: string) {.procvar, gcsafe.} = + message: string) {.gcsafe.} = echo "Downloading " & url let t = total.BiggestInt if t != 0: diff --git a/tools/urldownloader.nim b/tools/urldownloader.nim index b29a6c138..73e4034c9 100644 --- a/tools/urldownloader.nim +++ b/tools/urldownloader.nim @@ -418,7 +418,7 @@ proc downloadToFile*(szUrl: string, szFileName: string, when isMainModule: proc progress(status: DownloadStatus, progress: uint, progressMax: uint, - message: string) {.procvar,gcsafe.} = + message: string) {.gcsafe.} = const downset: set[DownloadStatus] = {statusBeginDownloading, statusDownloading, statusEndDownloading} if status in downset: From c32e1378eb7d62d59c9e24322c282556c821030a Mon Sep 17 00:00:00 2001 From: Bung <535670947@qq.com> Date: Sat, 16 May 2020 18:04:39 +0800 Subject: [PATCH 0057/3768] docfix: fix wrong link in doc/manual.rst (#14367) --- doc/manual.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual.rst b/doc/manual.rst index 8d4439644..4e477f6ff 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -6801,7 +6801,7 @@ Produces: ImportJs pragma --------------- -Similar to the `importcpp pragma for C++ <#foreign-function-interface-importc-pragma>`_, +Similar to the `importcpp pragma for C++ <#implementation-specific-pragmas-importcpp-pragma>`_, the ``importjs`` pragma can be used to import Javascript methods or symbols in general. The generated code then uses the Javascript method calling syntax: ``obj.method(arg)``. From c777f2fb608e1f42daf31d314dda0050f6354acd Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 16 May 2020 04:09:18 -0700 Subject: [PATCH 0058/3768] fix some issues with --backend (#14363) * fix some issues with --backend * fix https://github.com/timotheecour/Nim/issues/175; improve upon #14306 --- changelog.md | 10 ++++- compiler/cmdlinehelper.nim | 8 ---- compiler/commands.nim | 4 -- compiler/docgen.nim | 21 ++++++--- compiler/extccomp.nim | 15 ++++--- compiler/main.nim | 77 ++++++++++++++++++++------------ compiler/options.nim | 4 +- lib/packages/docutils/rstgen.nim | 2 +- tests/misc/mbackend.nim | 31 +++++++++++++ tests/nimdoc/m13129.nim | 2 + tests/trunner.nim | 10 +++++ 11 files changed, 125 insertions(+), 59 deletions(-) create mode 100644 tests/misc/mbackend.nim diff --git a/changelog.md b/changelog.md index a93a7e651..986186356 100644 --- a/changelog.md +++ b/changelog.md @@ -160,8 +160,8 @@ proc mydiv(a, b): int {.raises: [].} = - Specific warnings can now be turned into errors via `--warningAsError[X]:on|off`. - The `define` and `undef` pragmas have been de-deprecated. -- New command: `nim r main.nim [args...]` which compiles and runs main.nim, saving - the binary to $nimcache/main$exeExt, using the same logic as `nim c -r` to +- New command: `nim r main.nim [args...]` which compiles and runs main.nim, and implies `--usenimcache` + so that output is saved to $nimcache/main$exeExt, using the same logic as `nim c -r` to avoid recompiling when sources don't change. This is now the preferred way to run tests, avoiding the usual pain of clobbering your repo with binaries or using tricky gitignore rules on posix. Example: @@ -178,6 +178,12 @@ proc mydiv(a, b): int {.raises: [].} = - new hint: `--hint:msgOrigin` will show where a compiler msg (hint|warning|error) was generated; this helps in particular when it's non obvious where it came from either because multiple locations generate the same message, or because the message involves runtime formatting. +- new flag `--backend:js|c|cpp|objc (or -b:js etc), to change backend; can be used with any command + (eg nim r, doc, check etc); safe to re-assign. +- new flag `--doccmd:cmd` to pass additional flags for runnableExamples, eg: `--doccmd:-d:foo --threads` + use `--doccmd:skip` to skip runnableExamples and rst test snippets. +- new flag `--usenimcache` to output to nimcache (whatever it resolves to after all commands are processed) + and avoids polluting both $pwd and $projectdir. It can be used with any command. ## Tool changes diff --git a/compiler/cmdlinehelper.nim b/compiler/cmdlinehelper.nim index dd74f54dc..4777af8df 100644 --- a/compiler/cmdlinehelper.nim +++ b/compiler/cmdlinehelper.nim @@ -75,14 +75,6 @@ proc loadConfigsAndRunMainCommand*(self: NimProg, cache: IdentCache; conf: Confi # now process command line arguments again, because some options in the # command line can overwrite the config file's settings extccomp.initVars(conf) - # XXX This is hacky. We need to find a better way. - case conf.command - of "cpp", "compiletocpp": - conf.backend = backendCpp - conf.cmd = cmdCompileToBackend - else: - discard - self.processCmdLine(passCmd2, "", conf) if conf.command == "": rawMessage(conf, errGenerated, "command missing") diff --git a/compiler/commands.nim b/compiler/commands.nim index 2ab79f14a..340180378 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -502,8 +502,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; defineSymbol(conf.symbols, "gcmarkandsweep") of "destructors", "arc": conf.selectedGC = gcArc - if conf.backend != backendCpp: - conf.exc = excGoto defineSymbol(conf.symbols, "gcdestructors") defineSymbol(conf.symbols, "gcarc") incl conf.globalOptions, optSeqDestructors @@ -513,8 +511,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; defineSymbol(conf.symbols, "nimV2") of "orc": conf.selectedGC = gcOrc - if conf.backend != backendCpp: - conf.exc = excGoto defineSymbol(conf.symbols, "gcdestructors") defineSymbol(conf.symbols, "gcorc") incl conf.globalOptions, optSeqDestructors diff --git a/compiler/docgen.nim b/compiler/docgen.nim index d774fd508..b594921ae 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -216,13 +216,20 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef, # Include the current file if we're parsing a nim file let importStmt = if d.isPureRst: "" else: "import \"$1\"\n" % [d.filename.replace("\\", "/")] writeFile(outp, importStmt & content) - let c = if cmd.startsWith("nim "): os.getAppFilename() & " " & $conf.backend & cmd.substr("nim ".len) - elif cmd.startsWith("nim "): os.getAppFilename() & cmd.substr("nim".len) - else: cmd - let c2 = c % quoteShell(outp) - rawMessage(conf, hintExecuting, c2) - if execShellCmd(c2) != status: - rawMessage(conf, errGenerated, "executing of external program failed: " & c2) + + proc interpSnippetCmd(cmd: string): string = + # backward compatibility hacks; interpolation commands should explicitly use `$` + if cmd.startsWith "nim ": result = "$nim " & cmd[4..^1] + else: result = cmd + result = result.replace("$1", "$options") % [ + "nim", os.getAppFilename().quoteShell, + "backend", $d.conf.backend, + "options", outp.quoteShell, + ] + let cmd = cmd.interpSnippetCmd + rawMessage(conf, hintExecuting, cmd) + if execShellCmd(cmd) != status: + rawMessage(conf, errGenerated, "executing of external program failed: " & cmd) result.emitted = initIntSet() result.destFile = getOutFile2(conf, presentationPath(conf, filename), outExt, htmldocsDir, false) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 3c2435bec..4c5b195ab 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -306,13 +306,14 @@ proc isVSCompatible*(conf: ConfigRef): bool = proc getConfigVar(conf: ConfigRef; c: TSystemCC, suffix: string): string = # use ``cpu.os.cc`` for cross compilation, unless ``--compileOnly`` is given # for niminst support - let fullSuffix = - case conf.backend - of backendCpp, backendJs, backendObjc: "." & $conf.backend & suffix - of backendC: suffix - else: - doAssert false - "" + var fullSuffix = suffix + case conf.backend + of backendCpp, backendJs, backendObjc: fullSuffix = "." & $conf.backend & suffix + of backendC: discard + of backendInvalid: + # during parsing of cfg files; we don't know the backend yet, no point in + # guessing wrong thing + return "" if (conf.target.hostOS != conf.target.targetOS or conf.target.hostCPU != conf.target.targetCPU) and optCompileOnly notin conf.globalOptions: diff --git a/compiler/main.nim b/compiler/main.nim index 00bc579df..21c0695f6 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -114,8 +114,10 @@ proc commandJsonScript(graph: ModuleGraph) = let proj = changeFileExt(graph.config.projectFull, "") extccomp.runJsonBuildInstructions(graph.config, proj) -when not defined(leanCompiler): - proc commandCompileToJS(graph: ModuleGraph) = +proc commandCompileToJS(graph: ModuleGraph) = + when defined(leanCompiler): + globalError(graph.config, unknownLineInfo, "compiler wasn't built with JS code generator") + else: let conf = graph.config conf.exc = excCpp @@ -194,47 +196,65 @@ proc mainCommand*(graph: ModuleGraph) = when false: setOutDir(conf) if optUseNimcache in conf.globalOptions: setOutDir(conf) - template handleBackend(backend2: TBackend) = - conf.backend = backend2 - conf.cmd = cmdCompileToBackend - defineSymbol(graph.config.symbols, $backend2) - case backend2 + proc customizeForBackend(backend: TBackend) = + ## Sets backend specific options but don't compile to backend yet in + ## case command doesn't require it. This must be called by all commands. + if conf.backend == backendInvalid: + # only set if wasn't already set, to allow override via `nim c -b:cpp` + conf.backend = backend + + defineSymbol(graph.config.symbols, $conf.backend) + case conf.backend of backendC: if conf.exc == excNone: conf.exc = excSetjmp - commandCompileToC(graph) of backendCpp: if conf.exc == excNone: conf.exc = excCpp - commandCompileToC(graph) - of backendObjc: - commandCompileToC(graph) + of backendObjc: discard of backendJs: - when defined(leanCompiler): - globalError(conf, unknownLineInfo, "compiler wasn't built with JS code generator") - else: - if conf.hcrOn: - # XXX: At the moment, system.nim cannot be compiled in JS mode - # with "-d:useNimRtl". The HCR option has been processed earlier - # and it has added this define implictly, so we must undo that here. - # A better solution might be to fix system.nim - undefSymbol(conf.symbols, "useNimRtl") - commandCompileToJS(graph) + if conf.hcrOn: + # XXX: At the moment, system.nim cannot be compiled in JS mode + # with "-d:useNimRtl". The HCR option has been processed earlier + # and it has added this define implictly, so we must undo that here. + # A better solution might be to fix system.nim + undefSymbol(conf.symbols, "useNimRtl") + of backendInvalid: doAssert false + if conf.selectedGC in {gcArc, gcOrc} and conf.backend != backendCpp: + conf.exc = excGoto + + var commandAlreadyProcessed = false + + proc compileToBackend(backend: TBackend, cmd = cmdCompileToBackend) = + commandAlreadyProcessed = true + conf.cmd = cmd + customizeForBackend(backend) + case conf.backend + of backendC: commandCompileToC(graph) + of backendCpp: commandCompileToC(graph) + of backendObjc: commandCompileToC(graph) + of backendJs: commandCompileToJS(graph) of backendInvalid: doAssert false + ## process all backend commands case conf.command.normalize - of "c", "cc", "compile", "compiletoc": handleBackend(backendC) # compile means compileToC currently - of "cpp", "compiletocpp": handleBackend(backendCpp) - of "objc", "compiletooc": handleBackend(backendObjc) - of "js", "compiletojs": handleBackend(backendJs) + of "c", "cc", "compile", "compiletoc": compileToBackend(backendC) # compile means compileToC currently + of "cpp", "compiletocpp": compileToBackend(backendCpp) + of "objc", "compiletooc": compileToBackend(backendObjc) + of "js", "compiletojs": compileToBackend(backendJs) of "r": # different from `"run"`! conf.globalOptions.incl {optRun, optUseNimcache} - handleBackend(conf.backend) + compileToBackend(backendC) of "run": - conf.cmd = cmdRun when hasTinyCBackend: extccomp.setCC(conf, "tcc", unknownLineInfo) - commandCompileToC(graph) + if conf.backend notin {backendC, backendInvalid}: + rawMessage(conf, errGenerated, "'run' requires c backend, got: '$1'" % $conf.backend) + compileToBackend(backendC, cmd = cmdRun) else: rawMessage(conf, errGenerated, "'run' command not available; rebuild with -d:tinyc") + else: customizeForBackend(backendC) # fallback for other commands + + ## process all other commands + case conf.command.normalize of "doc0": when defined(leanCompiler): quit "compiler wasn't built with documentation generator" @@ -383,6 +403,7 @@ proc mainCommand*(graph: ModuleGraph) = of "jsonscript": conf.cmd = cmdJsonScript commandJsonScript(graph) + elif commandAlreadyProcessed: discard # already handled else: rawMessage(conf, errGenerated, "invalid command: " & conf.command) diff --git a/compiler/options.nim b/compiler/options.nim index 3e6c7989f..194d635b1 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -223,7 +223,7 @@ type ## fields marked with '*' are subject to ## the incremental compilation mechanisms ## (+) means "part of the dependency" - backend*: TBackend + backend*: TBackend # set via `nim x` or `nim --backend:x` target*: Target # (+) linesCompiled*: int # all lines that have been compiled options*: TOptions # (+) @@ -419,7 +419,7 @@ proc newConfigRef*(): ConfigRef = cIncludes: @[], # directories to search for included files cLibs: @[], # directories to search for lib files cLinkedLibs: @[], # libraries to link - backend: backendC, + backend: backendInvalid, externalToLink: @[], linkOptionsCmd: "", compileOptionsCmd: @[], diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index edd987a76..816d724ef 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -865,7 +865,7 @@ proc parseCodeBlockField(d: PDoc, n: PRstNode, params: var CodeBlockParams) = of "test": params.testCmd = n.getFieldValue.strip if params.testCmd.len == 0: - params.testCmd = "nim -r $1" # The nim backend is auto-set in docgen.nim + params.testCmd = "$nim r --backend:$backend $options" # see `interpSnippetCmd` else: params.testCmd = unescape(params.testCmd) of "status", "exitcode": diff --git a/tests/misc/mbackend.nim b/tests/misc/mbackend.nim new file mode 100644 index 000000000..e38eb2d3e --- /dev/null +++ b/tests/misc/mbackend.nim @@ -0,0 +1,31 @@ +#[ +We can't merge this test inside a `when defined(cpp)` because some bug that was +fixed would not trigger in that case. +]# + +import std/compilesettings +import std/unittest + +static: + ## bugfix 1: this used to CT error with: Error: unhandled exception: mimportcpp.nim(6, 18) `defined(cpp)` + doAssert defined(cpp) + doAssert querySetting(backend) == "cpp" + + ## checks that `--backend:c` has no side effect (ie, can be overridden by subsequent commands) + doAssert not defined(c) + doAssert not defined(js) + doAssert not defined(js) + +type + std_exception {.importcpp: "std::exception", header: "".} = object +proc what(s: std_exception): cstring {.importcpp: "((char *)#.what())".} + +var isThrown = false +try: + ## bugfix 2: this used to CT error with: Error: only a 'ref object' can be raised + raise std_exception() +except std_exception as ex: + doAssert ex.what().len > 0 + isThrown = true + +doAssert isThrown diff --git a/tests/nimdoc/m13129.nim b/tests/nimdoc/m13129.nim index df4b5a3f5..95d072d52 100644 --- a/tests/nimdoc/m13129.nim +++ b/tests/nimdoc/m13129.nim @@ -1,3 +1,5 @@ +# issue #13129 + when defined(cpp): {.push header: "".} type diff --git a/tests/trunner.nim b/tests/trunner.nim index bd74a8405..e89606bf7 100644 --- a/tests/trunner.nim +++ b/tests/trunner.nim @@ -123,6 +123,16 @@ else: # don't run twice the same test let cmd = fmt"{nim} r --backend:{mode} --hints:off --nimcache:{nimcache} {file}" check execCmdEx(cmd) == ("ok3\n", 0) + block: # further issues with `--backend` + let file = testsDir / "misc/mbackend.nim" + var cmd = fmt"{nim} doc -b:cpp --hints:off --nimcache:{nimcache} {file}" + check execCmdEx(cmd) == ("", 0) + cmd = fmt"{nim} check -b:c -b:cpp --hints:off --nimcache:{nimcache} {file}" + check execCmdEx(cmd) == ("", 0) + # issue https://github.com/timotheecour/Nim/issues/175 + cmd = fmt"{nim} c -b:js -b:cpp --hints:off --nimcache:{nimcache} {file}" + check execCmdEx(cmd) == ("", 0) + block: # some importc tests # issue #14314 let file = testsDir / "misc/mimportc.nim" From 390ff428f0787ea882b0b7e4dbefa37e0b676804 Mon Sep 17 00:00:00 2001 From: Bung <535670947@qq.com> Date: Sat, 16 May 2020 21:50:04 +0800 Subject: [PATCH 0059/3768] add SqlPrepared api fix #13559 (#14365) Co-authored-by: bung87 --- lib/impure/db_sqlite.nim | 101 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 101 insertions(+) diff --git a/lib/impure/db_sqlite.nim b/lib/impure/db_sqlite.nim index 810638fb8..c7ace0171 100644 --- a/lib/impure/db_sqlite.nim +++ b/lib/impure/db_sqlite.nim @@ -124,6 +124,7 @@ type ## converted to an empty string. InstantRow* = PStmt ## A handle that can be used to get a row's column ## text on demand. + SqlPrepared* = distinct PStmt ## a identifier for the prepared queries proc dbError*(db: DbConn) {.noreturn.} = ## Raises a `DbError` exception. @@ -164,6 +165,11 @@ proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string = else: add(result, c) +proc prepare*(db: DbConn; q: string): SqlPrepared = + if prepare_v2(db, q, q.len.cint,result.PStmt, nil) != SQLITE_OK: + discard finalize(result.PStmt) + dbError(db) + proc tryExec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): bool {. tags: [ReadDbEffect, WriteDbEffect].} = @@ -188,6 +194,16 @@ proc tryExec*(db: DbConn, query: SqlQuery, discard finalize(stmt) result = false +proc tryExec*(db: DbConn, stmtName: SqlPrepared, + args: varargs[string, `$`]): bool {. + tags: [ReadDbEffect, WriteDbEffect].} = + let x = step(stmtName.PStmt) + if x in {SQLITE_DONE, SQLITE_ROW}: + result = true + else: + discard finalize(stmtName.PStmt) + result = false + proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {. tags: [ReadDbEffect, WriteDbEffect].} = ## Executes the query and raises a `DbError` exception if not successful. @@ -206,6 +222,10 @@ proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {. ## db.close() if not tryExec(db, query, args): dbError(db) +proc exec*(db: DbConn, stmtName: SqlPrepared, + args: varargs[string]) {.tags: [ReadDbEffect, WriteDbEffect].} = + if not tryExec(db, stmtName, args): dbError(db) + proc newRow(L: int): Row = newSeq(result, L) for i in 0..L-1: result[i] = "" @@ -216,6 +236,11 @@ proc setupQuery(db: DbConn, query: SqlQuery, var q = dbFormat(query, args) if prepare_v2(db, q, q.len.cint, result, nil) != SQLITE_OK: dbError(db) +proc setupQuery(db: DbConn, stmtName: SqlPrepared, + args: varargs[string]): PStmt = + assert(not db.isNil, "Database not connected.") + if not tryExec(db, stmtName, args): dbError(db) + proc setRow(stmt: PStmt, r: var Row, cols: cint) = for col in 0'i32..cols-1: setLen(r[col], column_bytes(stmt, col)) # set capacity @@ -264,6 +289,18 @@ iterator fastRows*(db: DbConn, query: SqlQuery, finally: if finalize(stmt) != SQLITE_OK: dbError(db) +iterator fastRows*(db: DbConn, stmtName: SqlPrepared, + args: varargs[string, `$`]): Row {.tags: [ReadDbEffect,WriteDbEffect].} = + var stmt = setupQuery(db, stmtName, args) + var L = (column_count(stmt)) + var result = newRow(L) + try: + while step(stmt) == SQLITE_ROW: + setRow(stmt, result, L) + yield result + except: + dbError(db) + iterator instantRows*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): InstantRow {.tags: [ReadDbEffect].} = @@ -304,6 +341,16 @@ iterator instantRows*(db: DbConn, query: SqlQuery, finally: if finalize(stmt) != SQLITE_OK: dbError(db) +iterator instantRows*(db: DbConn, stmtName: SqlPrepared, + args: varargs[string, `$`]): InstantRow + {.tags: [ReadDbEffect,WriteDbEffect].} = + var stmt = setupQuery(db, stmtName, args) + try: + while step(stmt) == SQLITE_ROW: + yield stmt + except: + dbError(db) + proc toTypeKind(t: var DbType; x: int32) = case x of SQLITE_INTEGER: @@ -446,6 +493,12 @@ proc getAllRows*(db: DbConn, query: SqlQuery, for r in fastRows(db, query, args): result.add(r) +proc getAllRows*(db: DbConn, stmtName: SqlPrepared, + args: varargs[string, `$`]): seq[Row] {.tags: [ReadDbEffect,WriteDbEffect].} = + result = @[] + for r in fastRows(db, stmtName, args): + result.add(r) + iterator rows*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} = ## Similar to `fastRows iterator <#fastRows.i,DbConn,SqlQuery,varargs[string,]>`_, @@ -473,6 +526,10 @@ iterator rows*(db: DbConn, query: SqlQuery, ## db.close() for r in fastRows(db, query, args): yield r +iterator rows*(db: DbConn, stmtName: SqlPrepared, + args: varargs[string, `$`]): Row {.tags: [ReadDbEffect,WriteDbEffect].} = + for r in fastRows(db, stmtName, args): yield r + proc getValue*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): string {.tags: [ReadDbEffect].} = ## Executes the query and returns the first column of the first row of the @@ -509,6 +566,19 @@ proc getValue*(db: DbConn, query: SqlQuery, result = "" if finalize(stmt) != SQLITE_OK: dbError(db) +proc getValue*(db: DbConn, stmtName: SqlPrepared, + args: varargs[string, `$`]): string {.tags: [ReadDbEffect,WriteDbEffect].} = + var stmt = setupQuery(db, stmtName, args) + if step(stmt) == SQLITE_ROW: + let cb = column_bytes(stmt, 0) + if cb == 0: + result = "" + else: + result = newStringOfCap(cb) + add(result, column_text(stmt, 0)) + else: + result = "" + proc tryInsertID*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): int64 {.tags: [WriteDbEffect], raises: [].} = @@ -590,6 +660,12 @@ proc execAffectedRows*(db: DbConn, query: SqlQuery, exec(db, query, args) result = changes(db) +proc execAffectedRows*(db: DbConn, stmtName: SqlPrepared, + args: varargs[string, `$`]): int64 {. + tags: [ReadDbEffect, WriteDbEffect].} = + exec(db, stmtName, args) + result = changes(db) + proc close*(db: DbConn) {.tags: [DbEffect].} = ## Closes the database connection. ## @@ -637,11 +713,23 @@ proc setEncoding*(connection: DbConn, encoding: string): bool {. exec(connection, sql"PRAGMA encoding = ?", [encoding]) result = connection.getValue(sql"PRAGMA encoding") == encoding +proc finalize*(sqlPrepared:SqlPrepared){.discardable.} = + discard finalize(sqlPrepared.PStmt) + when not defined(testing) and isMainModule: var db = open("db.sql", "", "", "") exec(db, sql"create table tbl1(one varchar(10), two smallint)", []) exec(db, sql"insert into tbl1 values('hello!',10)", []) exec(db, sql"insert into tbl1 values('goodbye', 20)", []) + var p1 = db.prepare "create table tbl2(one varchar(10), two smallint)" + exec(db, p1, []) + finalize(p1) + var p2 = db.prepare "insert into tbl2 values('hello!',10)" + exec(db, p2, []) + finalize(p2) + var p3 = db.prepare "insert into tbl2 values('goodbye', 20)" + exec(db, p3, []) + finalize(p3) #db.query("create table tbl1(one varchar(10), two smallint)") #db.query("insert into tbl1 values('hello!',10)") #db.query("insert into tbl1 values('goodbye', 20)") @@ -649,5 +737,18 @@ when not defined(testing) and isMainModule: echo(r[0], r[1]) for r in db.instantRows(sql"select * from tbl1", []): echo(r[0], r[1]) + var p4 = db.prepare "select * from tbl2" + for r in db.rows(p4, []): + echo(r[0], r[1]) + finalize(p4) + var p5 = db.prepare "select * from tbl2" + for r in db.instantRows(p5, []): + echo(r[0], r[1]) + finalize(p5) + + for r in db.rows(sql"select * from tbl2", []): + echo(r[0], r[1]) + for r in db.instantRows(sql"select * from tbl2", []): + echo(r[0], r[1]) db_sqlite.close(db) From 7361e2627ba91fcb92f37720047a80c231832f22 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sat, 16 May 2020 14:50:41 +0100 Subject: [PATCH 0060/3768] Emscripten: disable epoll (#14361) Epoll is not supported by epoll. --- lib/pure/selectors.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim index a2329a2dc..c475c1b4f 100644 --- a/lib/pure/selectors.nim +++ b/lib/pure/selectors.nim @@ -34,7 +34,7 @@ const hasThreadSupport = compileOption("threads") and defined(threadsafe) const ioselSupportedPlatform* = defined(macosx) or defined(freebsd) or defined(netbsd) or defined(openbsd) or defined(dragonfly) or - (defined(linux) and not defined(android)) + (defined(linux) and not defined(android) and not defined(emscripten)) ## This constant is used to determine whether the destination platform is ## fully supported by ``ioselectors`` module. @@ -320,7 +320,7 @@ else: # Anything higher is the time to wait in milliseconds. doAssert(timeout >= -1, "Cannot select with a negative value, got " & $timeout) - when defined(linux): + when defined(linux) and not defined(emscripten): include ioselects/ioselectors_epoll elif bsdPlatform: include ioselects/ioselectors_kqueue From 7f377da1d2191bdf73e4ce8961fb982b2da253cb Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 16 May 2020 17:03:25 +0200 Subject: [PATCH 0061/3768] fixes #14370 (#14371) --- changelog.md | 2 +- lib/system/excpt.nim | 2 +- lib/system/threads.nim | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index 986186356..bb87f817c 100644 --- a/changelog.md +++ b/changelog.md @@ -90,7 +90,7 @@ 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. - `osproc.execCmdEx` now takes an optional `input` for stdin, `workingDir` and `env` diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index fd1a73b2d..089048163 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -345,7 +345,7 @@ else: proc stackTraceAvailable*(): bool = result = false var onUnhandledException*: (proc (errorMsg: string) {. - nimcall.}) ## Set this error \ + nimcall, gcsafe.}) ## Set this error \ ## handler to override the existing behaviour on an unhandled exception. ## ## The default is to write a stacktrace to ``stderr`` and then call ``quit(1)``. diff --git a/lib/system/threads.nim b/lib/system/threads.nim index 34a2d2ccf..6b858c4bb 100644 --- a/lib/system/threads.nim +++ b/lib/system/threads.nim @@ -107,7 +107,7 @@ template afterThreadRuns() = when not defined(boehmgc) and not hasSharedHeap and not defined(gogc) and not defined(gcRegions): proc deallocOsPages() {.rtl, raises: [].} -proc threadTrouble() {.raises: [].} +proc threadTrouble() {.raises: [], gcsafe.} ## defined in system/excpt.nim when defined(boehmgc): From 40ac19572a86b5bfa7b57cf0482ae3a30432176a Mon Sep 17 00:00:00 2001 From: John <50754967+j-bm@users.noreply.github.com> Date: Sat, 16 May 2020 12:56:52 -0700 Subject: [PATCH 0062/3768] add OpenBSD MAP_STACK for coroutines (#14353) --- lib/system/osalloc.nim | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/system/osalloc.nim b/lib/system/osalloc.nim index aa86ee379..32d3b166d 100644 --- a/lib/system/osalloc.nim +++ b/lib/system/osalloc.nim @@ -196,7 +196,13 @@ elif defined(posix) and not defined(StandaloneHeapSize): PROT_READ = 1 # page can be read PROT_WRITE = 2 # page can be written - when defined(macosx) or defined(bsd): + when defined(netbsd) or defined(openbsd): + # OpenBSD security for setjmp/longjmp coroutines + var MAP_STACK {.importc: "MAP_STACK", header: "".}: cint + else: + const MAP_STACK = 0 # avoid sideeffects + + when defined(macosx) or defined(freebsd): const MAP_ANONYMOUS = 0x1000 const MAP_PRIVATE = 0x02 # Changes are private elif defined(solaris): @@ -210,7 +216,7 @@ elif defined(posix) and not defined(StandaloneHeapSize): elif defined(haiku): const MAP_ANONYMOUS = 0x08 const MAP_PRIVATE = 0x02 - else: + else: # posix including netbsd or openbsd var MAP_ANONYMOUS {.importc: "MAP_ANONYMOUS", header: "".}: cint MAP_PRIVATE {.importc: "MAP_PRIVATE", header: "".}: cint @@ -222,13 +228,13 @@ elif defined(posix) and not defined(StandaloneHeapSize): proc osAllocPages(size: int): pointer {.inline.} = result = mmap(nil, cast[csize_t](size), PROT_READ or PROT_WRITE, - MAP_PRIVATE or MAP_ANONYMOUS, -1, 0) + MAP_ANONYMOUS or MAP_PRIVATE or MAP_STACK, -1, 0) if result == nil or result == cast[pointer](-1): raiseOutOfMem() proc osTryAllocPages(size: int): pointer {.inline.} = result = mmap(nil, cast[csize_t](size), PROT_READ or PROT_WRITE, - MAP_PRIVATE or MAP_ANONYMOUS, -1, 0) + MAP_ANONYMOUS or MAP_PRIVATE or MAP_STACK, -1, 0) if result == cast[pointer](-1): result = nil proc osDeallocPages(p: pointer, size: int) {.inline.} = From 42e80829c7de260b1c88032b1428ade59c4b18d0 Mon Sep 17 00:00:00 2001 From: Frank Schmitt Date: Sun, 17 May 2020 12:44:36 +0200 Subject: [PATCH 0063/3768] docs: fix syntax error in hotCodeReloading example (fixes #14380) (#14381) Co-authored-by: Frank Schmitt --- doc/hcr.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/hcr.rst b/doc/hcr.rst index 9bf88f6ef..f6db0ddfd 100644 --- a/doc/hcr.rst +++ b/doc/hcr.rst @@ -76,7 +76,7 @@ To install SDL2 you can use ``nimble install sdl2``. discard renderer.clear() discard renderer.setDrawColor(255, 128, 128, 0) - var rect: Rect = (x: posX - 25, y: posY - 25, w: 50.cint, h: 50.cint) + var rect = Rect(x: posX - 25, y: posY - 25, w: 50.cint, h: 50.cint) discard renderer.fillRect(rect) delay(16) renderer.present() From 51578be296ef06013993b4377bde7a0322c0ccb7 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 17 May 2020 04:28:18 -0700 Subject: [PATCH 0064/3768] close #12746; minor cleanup (#14379) --- tests/nimdoc/readme.md | 5 +++-- .../{magics => nimdoc}/trunnableexamples.nim | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) rename tests/{magics => nimdoc}/trunnableexamples.nim (74%) diff --git a/tests/nimdoc/readme.md b/tests/nimdoc/readme.md index 64b16c2a5..40b5841eb 100644 --- a/tests/nimdoc/readme.md +++ b/tests/nimdoc/readme.md @@ -1,2 +1,3 @@ -the html validation is tested by nimdoc/tester.nim -the runnableExamples + nim doc logic (across backend) is tested here +## links +* $nim/nimdoc/tester.nim: tests html validation +* $nim/tests/nimdoc/: tests `runnableExamples` + `nim doc` logic diff --git a/tests/magics/trunnableexamples.nim b/tests/nimdoc/trunnableexamples.nim similarity index 74% rename from tests/magics/trunnableexamples.nim rename to tests/nimdoc/trunnableexamples.nim index e6b2b70f8..e3ae6b9cb 100644 --- a/tests/magics/trunnableexamples.nim +++ b/tests/nimdoc/trunnableexamples.nim @@ -47,6 +47,25 @@ proc fun*() = doAssert declared(isAlphaAscii) echo "foo7" +when true: # issue #12746 + # this proc on its own works fine with `nim doc` + proc goodProc*() = + runnableExamples: + try: + discard + except: + # just the general except will work + discard + + # FIXED: this proc fails with `nim doc` + proc badProc*() = + runnableExamples: + try: + discard + except IOError: + # specifying Error is culprit + discard + # also check for runnableExamples at module scope runnableExamples: block: From 69cc1ddc4d6aa55e717ef6d57401577d4825def8 Mon Sep 17 00:00:00 2001 From: Miran Date: Mon, 18 May 2020 09:03:01 +0200 Subject: [PATCH 0065/3768] install gtk3 on osx for package testing (#14388) --- .github/workflows/ci_packages.yml | 2 +- testament/important_packages.nim | 7 ------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/.github/workflows/ci_packages.yml b/.github/workflows/ci_packages.yml index 53e698022..6cf41aa8e 100644 --- a/.github/workflows/ci_packages.yml +++ b/.github/workflows/ci_packages.yml @@ -36,7 +36,7 @@ jobs: valgrind libc6-dbg libblas-dev - name: 'Install dependencies (macOS)' if: runner.os == 'macOS' - run: brew install boehmgc make sfml + run: brew install boehmgc make sfml gtk+3 - name: 'Install dependencies (Windows)' if: runner.os == 'Windows' shell: bash diff --git a/testament/important_packages.nim b/testament/important_packages.nim index cdb98745c..1d3b1917c 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -1,5 +1,3 @@ -import os - template pkg1(name: string; hasDeps = false; cmd = "nimble test"; url = ""): untyped = packages1.add((name, cmd, hasDeps, url)) @@ -75,11 +73,6 @@ pkg2 "neo", true, "nim c -d:blas=openblas tests/all.nim" pkg2 "nesm" # pkg2 "nico", true pkg2 "nicy", false, "nim c -r src/nicy.nim" -when defined(osx): - # xxx: do this more generally by installing non-nim dependencies automatically - # as specified in nimble file and calling `distros.foreignDepInstallCmd`, but - # it currently would fail work if a package is already installed. - doAssert execShellCmd("brew ls --versions gtk+3 || brew install gtk+3") == 0 pkg2 "nigui", false, "nim c -o:niguii -r src/nigui.nim" pkg2 "NimData", true, "nim c -o:nimdataa src/nimdata.nim" pkg2 "nimes", true, "nim c src/nimes.nim" From b11ff518faec4efe4d5b6e2151d07bf19e26eae0 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 18 May 2020 00:38:12 -0700 Subject: [PATCH 0066/3768] fix #12293 findNimStdLibCompileTime should not break with nimble install compiler (#14334) * fix #12293 findNimStdLibCompileTime should not break with nimble install compiler * address comment to unblock PR --- compiler/nimeval.nim | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/compiler/nimeval.nim b/compiler/nimeval.nim index 09d1eb1da..11e899cae 100644 --- a/compiler/nimeval.nim +++ b/compiler/nimeval.nim @@ -81,6 +81,9 @@ proc findNimStdLib*(): string = ## Returns "" on failure. try: let nimexe = os.findExe("nim") + # this can't work with choosenim shims, refs https://github.com/dom96/choosenim/issues/189 + # it'd need `nim dump --dump.format:json . | jq -r .libpath` + # which we should simplify as `nim dump --key:libpath` if nimexe.len == 0: return "" result = nimexe.splitPath()[0] /../ "lib" if not fileExists(result / "system.nim"): @@ -93,8 +96,8 @@ proc findNimStdLib*(): string = proc findNimStdLibCompileTime*(): string = ## Same as ``findNimStdLib`` but uses source files used at compile time, ## and asserts on error. - const sourcePath = currentSourcePath() - result = sourcePath.parentDir.parentDir / "lib" + const exe = getCurrentCompilerExe() + result = exe.splitFile.dir.parentDir / "lib" doAssert fileExists(result / "system.nim"), "result:" & result proc createInterpreter*(scriptName: string; From bc42e9aa9877e341668f5278bddda73f403d01e4 Mon Sep 17 00:00:00 2001 From: cooldome Date: Mon, 18 May 2020 08:38:36 +0100 Subject: [PATCH 0067/3768] fix #14369 (#14386) * fix #14369 * empty commit --- compiler/ccgstmts.nim | 1 + tests/exception/tcpp_imported_exc2.nim | 10 ++++++++++ 2 files changed, 11 insertions(+) create mode 100644 tests/exception/tcpp_imported_exc2.nim diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 20780f734..ca6e51f29 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -727,6 +727,7 @@ proc genRaiseStmt(p: BProc, t: PNode) = initLocExprSingleUse(p, t[0], a) finallyActions(p) var e = rdLoc(a) + discard getTypeDesc(p.module, t[0].typ) var typ = skipTypes(t[0].typ, abstractPtrs) # XXX For reasons that currently escape me, this is only required by the new # C++ based exception handling: diff --git a/tests/exception/tcpp_imported_exc2.nim b/tests/exception/tcpp_imported_exc2.nim new file mode 100644 index 000000000..ff299ea3d --- /dev/null +++ b/tests/exception/tcpp_imported_exc2.nim @@ -0,0 +1,10 @@ +discard """ +targets: "cpp" +output: "" +""" +#issue #14369 case 2 +type RuntimeError {.requiresInit, importcpp: "std::runtime_error", header: "".} = object + +proc initRuntimeError(a: cstring): RuntimeError {.importcpp: "std::runtime_error(@)", constructor.} +try: raise initRuntimeError("foo2") +except: discard From 5beed56d0a5d9827680b34e7964da2031611dd6d Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 18 May 2020 02:03:34 -0700 Subject: [PATCH 0068/3768] fix #14174 do not collapse pragma inside runnableExamples (#14385) --- compiler/docgen.nim | 39 ++++++++++++++++++++------------------- compiler/renderer.nim | 2 +- 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index b594921ae..6123d49cf 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -370,6 +370,7 @@ proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var Rope; renderFlags: TRe var kind = tkEof var tokenPos = 0 var procTokenPos = 0 + template escLit(): untyped = rope(esc(d.target, literal)) while true: getNextTok(r, kind, literal) inc tokenPos @@ -378,7 +379,7 @@ proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var Rope; renderFlags: TRe break of tkComment: dispA(d.conf, result, "$1", "\\spanComment{$1}", - [rope(esc(d.target, literal))]) + [escLit]) of tokKeywordLow..tokKeywordHigh: if kind in {tkProc, tkMethod, tkIterator, tkMacro, tkTemplate, tkFunc, tkConverter}: procTokenPos = tokenPos @@ -386,53 +387,53 @@ proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var Rope; renderFlags: TRe [rope(literal)]) of tkOpr: dispA(d.conf, result, "$1", "\\spanOperator{$1}", - [rope(esc(d.target, literal))]) + [escLit]) of tkStrLit..tkTripleStrLit: dispA(d.conf, result, "$1", - "\\spanStringLit{$1}", [rope(esc(d.target, literal))]) + "\\spanStringLit{$1}", [escLit]) of tkCharLit: dispA(d.conf, result, "$1", "\\spanCharLit{$1}", - [rope(esc(d.target, literal))]) + [escLit]) of tkIntLit..tkUInt64Lit: dispA(d.conf, result, "$1", - "\\spanDecNumber{$1}", [rope(esc(d.target, literal))]) + "\\spanDecNumber{$1}", [escLit]) of tkFloatLit..tkFloat128Lit: dispA(d.conf, result, "$1", - "\\spanFloatNumber{$1}", [rope(esc(d.target, literal))]) + "\\spanFloatNumber{$1}", [escLit]) of tkSymbol: let s = getTokSym(r) # -2 because of the whitespace in between: if procTokenPos == tokenPos-2 and procLink != nil: dispA(d.conf, result, "$1", - "\\spanIdentifier{$1}", [rope(esc(d.target, literal)), procLink]) + "\\spanIdentifier{$1}", [escLit, procLink]) elif s != nil and s.kind in {skType, skVar, skLet, skConst} and sfExported in s.flags and s.owner != nil and belongsToPackage(d.conf, s.owner) and d.target == outHtml: let external = externalDep(d, s.owner) result.addf "$3", [rope changeFileExt(external, "html"), rope literal, - rope(esc(d.target, literal))] + escLit] else: dispA(d.conf, result, "$1", - "\\spanIdentifier{$1}", [rope(esc(d.target, literal))]) + "\\spanIdentifier{$1}", [escLit]) of tkSpaces, tkInvalid: result.add(literal) of tkCurlyDotLe: - dispA(d.conf, result, "" & # This span is required for the JS to work properly + template fun(s) = dispA(d.conf, result, s, "\\spanOther{$1}", [escLit]) + if renderRunnableExamples in renderFlags: fun "$1" + else: fun: "" & # This span is required for the JS to work properly """{...} $1 -""".replace("\n", ""), # Must remove newlines because wrapped in a
-                    "\\spanOther{$1}",
-                  [rope(esc(d.target, literal))])
+""".replace("\n", "")  # Must remove newlines because wrapped in a 
     of tkCurlyDotRi:
-      dispA(d.conf, result, """
+      template fun(s) = dispA(d.conf, result, s, "\\spanOther{$1}", [escLit])
+      if renderRunnableExamples in renderFlags: fun "$1"
+      else: fun """
 
 $1
-""".replace("\n", ""),
-                    "\\spanOther{$1}",
-                  [rope(esc(d.target, literal))])
+""".replace("\n", "")
     of tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, tkCurlyRi,
        tkBracketDotLe, tkBracketDotRi, tkParDotLe,
        tkParDotRi, tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot,
@@ -440,7 +441,7 @@ proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var Rope; renderFlags: TRe
        tkGStrLit, tkGTripleStrLit, tkInfixOpr, tkPrefixOpr, tkPostfixOpr,
        tkBracketLeColon:
       dispA(d.conf, result, "$1", "\\spanOther{$1}",
-            [rope(esc(d.target, literal))])
+            [escLit])
 
 proc testExample(d: PDoc; ex: PNode) =
   if d.conf.errorCounter > 0: return
@@ -525,7 +526,7 @@ proc getAllRunnableExamplesRec(d: PDoc; n, orig: PNode; dest: var Rope) =
       for b in body:
         if i > 0: dest.add "\n"
         inc i
-        nodeToHighlightedHtml(d, b, dest, {}, nil)
+        nodeToHighlightedHtml(d, b, dest, {renderRunnableExamples}, nil)
       dest.add(d.config.getOrDefault"doc.listing_end" % id)
   else: discard
   for i in 0..
Date: Mon, 18 May 2020 16:15:10 +0100
Subject: [PATCH 0069/3768] docs:getCurrentException() and
 getCurrentExceptionMsg() are not available for imported exceptions (#14392)

---
 doc/manual.rst | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/doc/manual.rst b/doc/manual.rst
index 4e477f6ff..80029fca4 100644
--- a/doc/manual.rst
+++ b/doc/manual.rst
@@ -4258,7 +4258,9 @@ caught by reference. Example:
   except std_exception as ex:
     echo ex.what()
 
-
+**Note:** `getCurrentException()` and `getCurrentExceptionMsg()` are not available 
+for imported exceptions. You need to use `except ImportedException as x:` syntax 
+and rely on functionality of `x` object to get exception details.
 
 Effect system
 =============

From 2627e1e9459379c9bc180890980e571ad7adf203 Mon Sep 17 00:00:00 2001
From: Timothee Cour 
Date: Mon, 18 May 2020 09:10:30 -0700
Subject: [PATCH 0070/3768] refs #14369 improve docs for importcpp exceptions
 (#14391)

* refs #14369 improve docs for importcpp exceptions

* address comments
---
 doc/manual.rst | 34 ++++++++++++++++++++++++++++------
 1 file changed, 28 insertions(+), 6 deletions(-)

diff --git a/doc/manual.rst b/doc/manual.rst
index 80029fca4..759042078 100644
--- a/doc/manual.rst
+++ b/doc/manual.rst
@@ -4247,16 +4247,38 @@ It is possible to raise/catch imported C++ exceptions. Types imported using
 caught by reference. Example:
 
 .. code-block:: nim
+    :test: "nim cpp -r $1"
 
   type
-    std_exception {.importcpp: "std::exception", header: "".} = object
+    CStdException {.importcpp: "std::exception", header: "", inheritable.} = object
+      ## does not inherit from `RootObj`, so we use `inheritable` instead
+    CRuntimeError {.requiresInit, importcpp: "std::runtime_error", header: "".} = object of CStdException
+      ## `CRuntimeError` has no default constructor => `requiresInit`
+  proc what(s: CStdException): cstring {.importcpp: "((char *)#.what())".}
+  proc initRuntimeError(a: cstring): CRuntimeError {.importcpp: "std::runtime_error(@)", constructor.}
+  proc initStdException(): CStdException {.importcpp: "std::exception()", constructor.}
 
-  proc what(s: std_exception): cstring {.importcpp: "((char *)#.what())".}
+  proc fn() =
+    let a = initRuntimeError("foo")
+    doAssert $a.what == "foo"
+    var b: cstring
+    try: raise initRuntimeError("foo2")
+    except CStdException as e:
+      doAssert e is CStdException
+      b = e.what()
+    doAssert $b == "foo2"
 
-  try:
-    raise std_exception()
-  except std_exception as ex:
-    echo ex.what()
+    try: raise initStdException()
+    except CStdException: discard
+
+    try: raise initRuntimeError("foo3")
+    except CRuntimeError as e:
+      b = e.what()
+    except CStdException:
+      doAssert false
+    doAssert $b == "foo3"
+
+  fn()
 
 **Note:** `getCurrentException()` and `getCurrentExceptionMsg()` are not available 
 for imported exceptions. You need to use `except ImportedException as x:` syntax 

From ac65986aae77e47d4f2ae99acc8048fe3d637921 Mon Sep 17 00:00:00 2001
From: Clyybber 
Date: Mon, 18 May 2020 18:43:06 +0200
Subject: [PATCH 0071/3768] Fix #14394 (#14395)

---
 lib/pure/hashes.nim | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim
index cd0841d2e..1024ce26f 100644
--- a/lib/pure/hashes.nim
+++ b/lib/pure/hashes.nim
@@ -97,7 +97,7 @@ proc hiXorLo(a, b: uint64): uint64 {.inline.} =
     when Hash.sizeof < 8:
       result = hiXorLoFallback64(a, b)
     elif defined(gcc) or defined(llvm_gcc) or defined(clang):
-      {.emit: """__uint128_t r = a; r *= b; `result` = (r >> 64) ^ r;""".}
+      {.emit: """__uint128_t r = `a`; r *= `b`; `result` = (r >> 64) ^ r;""".}
     elif defined(windows) and not defined(tcc):
       proc umul128(a, b: uint64, c: ptr uint64): uint64 {.importc: "_umul128", header: "intrin.h".}
       var b = b

From 27741d6a5c9ec87a6938b2928ae54b22bc93d9be Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C8=98tefan=20Talpalaru?= 
Date: Mon, 18 May 2020 23:44:04 +0200
Subject: [PATCH 0072/3768] fix #14364 (#14372) [backport:1.2]

Turn on the TLS emulation when using Boehm, since it doesn't scan the
real TLS.
---
 compiler/commands.nim | 1 +
 1 file changed, 1 insertion(+)

diff --git a/compiler/commands.nim b/compiler/commands.nim
index 340180378..e2e1a4558 100644
--- a/compiler/commands.nim
+++ b/compiler/commands.nim
@@ -493,6 +493,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo;
       of "boehm":
         conf.selectedGC = gcBoehm
         defineSymbol(conf.symbols, "boehmgc")
+        incl conf.globalOptions, optTlsEmulation # Boehm GC doesn't scan the real TLS
       of "refc":
         conf.selectedGC = gcRefc
       of "v2":

From e909486e5cde5a4a77cd6f21b42fc9ab38ec2ae6 Mon Sep 17 00:00:00 2001
From: Timothee Cour 
Date: Tue, 19 May 2020 00:41:31 -0700
Subject: [PATCH 0073/3768] trunner was not actually being tested in non-CTFFI
 mode; minor testament cleanups (#14377)

* use check
* trunner now works with cpp
* cleanup: move compiler/unittest_light => stdtest/unittest_light
* fix tests/readme.md
* remove deadcode references to rodfiles
* fix for windows
---
 compiler/rodimpl.nim                          |  2 ++
 koch.nim                                      |  2 +-
 lib/system/io.nim                             |  2 +-
 testament/categories.nim                      |  5 ----
 .../lib/stdtest}/unittest_light.nim           |  0
 tests/compiler/tasciitables.nim               |  2 +-
 tests/compiler/tunittest_light.nim            |  2 +-
 tests/misc/mbackend.nim                       |  1 -
 tests/misc/msizeof5.nim                       |  2 +-
 tests/misc/tparseopt.nim                      |  2 +-
 tests/{ => misc}/trunner.nim                  | 26 +++++++++++--------
 tests/readme.md                               | 13 ++++------
 tests/stdlib/tosproc.nim                      |  3 +--
 tests/stdlib/tstackframes.nim                 |  2 +-
 14 files changed, 30 insertions(+), 34 deletions(-)
 rename {compiler => testament/lib/stdtest}/unittest_light.nim (100%)
 rename tests/{ => misc}/trunner.nim (84%)

diff --git a/compiler/rodimpl.nim b/compiler/rodimpl.nim
index 8ee7ae564..417241930 100644
--- a/compiler/rodimpl.nim
+++ b/compiler/rodimpl.nim
@@ -916,6 +916,8 @@ proc loadNode*(g: ModuleGraph; module: PSym): PNode =
   replay(g, module, result)
 
 proc setupModuleCache*(g: ModuleGraph) =
+  ## historical note: there used to be a `rodfiles` dir with special tests
+  ## for incremental compilation via symbol files. This was likely replaced by ic.
   if g.config.symbolFiles == disabledSf: return
   g.recordStmt = recordStmt
   let dbfile = getNimcacheDir(g.config) / RelativeFile"rodfiles.db"
diff --git a/koch.nim b/koch.nim
index 3ec40534b..0391c3c4a 100644
--- a/koch.nim
+++ b/koch.nim
@@ -554,7 +554,7 @@ proc runCI(cmd: string) =
         # no need to bootstrap with koch boot (would be slower)
         let backend = if doUseCpp(): "cpp" else: "c"
         execFold("build with -d:nimHasLibFFI", "nim $1 -d:release -d:nimHasLibFFI -o:$2 compiler/nim.nim" % [backend, nimFFI])
-        execFold("test with -d:nimHasLibFFI", "$1 $2 -r testament/testament --nim:$1 r tests/trunner.nim" % [nimFFI, backend])
+        execFold("test with -d:nimHasLibFFI", "$1 $2 -r testament/testament --nim:$1 r tests/misc/trunner.nim -d:nimTrunnerFfi" % [nimFFI, backend])
 
     execFold("Run nimdoc tests", "nim c -r nimdoc/tester")
     execFold("Run nimpretty tests", "nim c -r nimpretty/tester.nim")
diff --git a/lib/system/io.nim b/lib/system/io.nim
index ab415f40c..84bebba28 100644
--- a/lib/system/io.nim
+++ b/lib/system/io.nim
@@ -733,7 +733,7 @@ when declared(stdout):
         releaseSys echoLock
 
 
-when defined(windows) and not defined(nimscript):
+when defined(windows) and not defined(nimscript) and not defined(js):
   # work-around C's sucking abstraction:
   # BUGFIX: stdin and stdout should be binary files!
   proc c_setmode(handle, mode: cint) {.
diff --git a/testament/categories.nim b/testament/categories.nim
index d60b89e33..114949ce8 100644
--- a/testament/categories.nim
+++ b/testament/categories.nim
@@ -32,7 +32,6 @@ const
     "nimble-packages-1",
     "nimble-packages-2",
     "niminaction",
-    "rodfiles",
     "threads",
     "untestable",
     "stdlib",
@@ -671,10 +670,6 @@ proc processCategory(r: var TResults, cat: Category,
                      options, testsDir: string,
                      runJoinableTests: bool) =
   case cat.string.normalize
-  of "rodfiles":
-    when false:
-      compileRodFiles(r, cat, options)
-      runRodFiles(r, cat, options)
   of "ic":
     when false:
       icTests(r, testsDir, cat, options)
diff --git a/compiler/unittest_light.nim b/testament/lib/stdtest/unittest_light.nim
similarity index 100%
rename from compiler/unittest_light.nim
rename to testament/lib/stdtest/unittest_light.nim
diff --git a/tests/compiler/tasciitables.nim b/tests/compiler/tasciitables.nim
index 0a5ee0f05..80d648508 100644
--- a/tests/compiler/tasciitables.nim
+++ b/tests/compiler/tasciitables.nim
@@ -1,4 +1,4 @@
-import compiler/unittest_light
+import stdtest/unittest_light
 import compiler/asciitables
 
 import strformat
diff --git a/tests/compiler/tunittest_light.nim b/tests/compiler/tunittest_light.nim
index 5cad6997f..2951cc664 100644
--- a/tests/compiler/tunittest_light.nim
+++ b/tests/compiler/tunittest_light.nim
@@ -1,4 +1,4 @@
-import compiler/unittest_light
+import stdtest/unittest_light
 
 proc testAssertEquals() =
   assertEquals("foo", "foo")
diff --git a/tests/misc/mbackend.nim b/tests/misc/mbackend.nim
index e38eb2d3e..b6578a212 100644
--- a/tests/misc/mbackend.nim
+++ b/tests/misc/mbackend.nim
@@ -4,7 +4,6 @@ fixed would not trigger in that case.
 ]#
 
 import std/compilesettings
-import std/unittest
 
 static:
   ## bugfix 1: this used to CT error with: Error: unhandled exception: mimportcpp.nim(6, 18) `defined(cpp)`
diff --git a/tests/misc/msizeof5.nim b/tests/misc/msizeof5.nim
index c833d1e43..63573a705 100644
--- a/tests/misc/msizeof5.nim
+++ b/tests/misc/msizeof5.nim
@@ -116,7 +116,7 @@ when defined caseBad:
     ensureCgen Foo5
 
   block:
-    type Foo6{.importc, completeStruct.} = object
+    type Foo6{.importc:"struct Foo6", completeStruct.} = object
       a1: cint
       # a2: bool # missing this should trigger assert fail
       a3: cfloat
diff --git a/tests/misc/tparseopt.nim b/tests/misc/tparseopt.nim
index 25ce77816..a3dfcbb26 100644
--- a/tests/misc/tparseopt.nim
+++ b/tests/misc/tparseopt.nim
@@ -71,7 +71,7 @@ else:
 
   import osproc, os, strutils
   from stdtest/specialpaths import buildDir
-  import "../.." / compiler/unittest_light
+  import stdtest/unittest_light
 
   block: # fix #9951
     template runTest(parseoptCustom) =
diff --git a/tests/trunner.nim b/tests/misc/trunner.nim
similarity index 84%
rename from tests/trunner.nim
rename to tests/misc/trunner.nim
index e89606bf7..bfe331c9c 100644
--- a/tests/trunner.nim
+++ b/tests/misc/trunner.nim
@@ -5,6 +5,7 @@ discard """
 
 ## tests that don't quite fit the mold and are easier to handle via `execCmdEx`
 ## A few others could be added to here to simplify code.
+## Note: this test is a bit slow but tests a lot of things; please don't disable.
 
 import std/[strformat,os,osproc,unittest]
 
@@ -15,7 +16,7 @@ const mode =
   elif defined(cpp): "cpp"
   else: static: doAssert false
 
-const testsDir = currentSourcePath().parentDir
+const testsDir = currentSourcePath.parentDir.parentDir
 const buildDir = testsDir.parentDir / "build"
 const nimcache = buildDir / "nimcacheTrunner"
   # `querySetting(nimcacheDir)` would also be possible, but we thus
@@ -28,7 +29,7 @@ proc runCmd(file, options = ""): auto =
   result = execCmdEx(cmd)
   when false:  echo result[0] & "\n" & result[1] # for debugging
 
-when defined(nimHasLibFFIEnabled):
+when defined(nimTrunnerFfi):
   block: # mevalffi
     when defined(openbsd):
       #[
@@ -65,7 +66,7 @@ else: # don't run twice the same test
   block: # mstatic_assert
     let (output, exitCode) = runCmd("ccgbugs/mstatic_assert.nim", "-d:caseBad")
     check2 "sizeof(bool) == 2"
-    doAssert exitCode != 0
+    check exitCode != 0
 
   block: # ABI checks
     let file = "misc/msizeof5.nim"
@@ -82,12 +83,11 @@ else: # don't run twice the same test
       check2 "sizeof(Foo5) == 16"
       check2 "sizeof(Foo5) == 3"
       check2 "sizeof(struct Foo6) == "
-      doAssert exitCode != 0
+      check exitCode != 0
 
   import streams
   block: # stdin input
-    let nimcmd = fmt"{nim} r --hints:off - -firstparam '-second param'"
-    let inputcmd = "import os; echo commandLineParams()"
+    let nimcmd = fmt"""{nim} r --hints:off - -firstparam "-second param" """
     let expected = """@["-firstparam", "-second param"]"""
     block:
       let p = startProcess(nimcmd, options = {poEvalCommand})
@@ -98,23 +98,27 @@ else: # don't run twice the same test
       doAssert p.waitForExit == 0
       doAssert error.len == 0, $error
       output.stripLineEnd
-      doAssert output == expected
+      check output == expected
       p.errorStream.close
       p.outputStream.close
 
     block:
-      when defined(posix):
-        let cmd = fmt"echo 'import os; echo commandLineParams()' | {nimcmd}"
+      when defined posix:
+        # xxx on windows, `poEvalCommand` should imply `/cmd`, (which should
+        # make this work), but currently doesn't
+        let cmd = fmt"""echo "import os; echo commandLineParams()" | {nimcmd}"""
         var (output, exitCode) = execCmdEx(cmd)
         output.stripLineEnd
-        doAssert output == expected
+        check output == expected
+        doAssert exitCode == 0
 
   block: # nim doc --backend:$backend --doccmd:$cmd
     # test for https://github.com/nim-lang/Nim/issues/13129
     # test for https://github.com/nim-lang/Nim/issues/13891
     let file = testsDir / "nimdoc/m13129.nim"
     for backend in fmt"{mode} js".split:
-      let cmd = fmt"{nim} doc -b:{backend} --nimcache:{nimcache} -d:m13129Foo1 --doccmd:'-d:m13129Foo2 --hints:off' --usenimcache --hints:off {file}"
+      # pending #14343 this fails on windows: --doccmd:"-d:m13129Foo2 --hints:off"
+      let cmd = fmt"""{nim} doc -b:{backend} --nimcache:{nimcache} -d:m13129Foo1 "--doccmd:-d:m13129Foo2 --hints:off" --usenimcache --hints:off {file}"""
       check execCmdEx(cmd) == (&"ok1:{backend}\nok2: backend: {backend}\n", 0)
     # checks that --usenimcache works with `nim doc`
     check fileExists(nimcache / "m13129.html")
diff --git a/tests/readme.md b/tests/readme.md
index b9b8abd39..234ee4efe 100644
--- a/tests/readme.md
+++ b/tests/readme.md
@@ -2,9 +2,8 @@ This directory contains the test cases.
 
 Each test must have a filename of the form: ``t*.nim``
 
-**Note:** Tests are only compiled by default. In order to get the tester to
-execute the compiled binary, you need to specify a spec with an ``action`` key
-(see below for details).
+**Note:** Testament is only aware of tests under a directory (eg `tests/foo/`) and will ignore
+top-level tests like `tests/tbar.nim`.
 
 # Specs
 
@@ -16,7 +15,7 @@ Each test can contain a spec in a ``discard """ ... """`` block.
 
 Specifies what action this test should take.
 
-**Default: compile**
+**Default: run**
 
 Options:
 
@@ -50,10 +49,8 @@ discard """
 # Categories
 
 Each folder under this directory represents a test category, which can be
-tested by running `koch tests cat `.
-
-The folder ``rodfiles`` contains special tests that test incremental
-compilation via symbol files.
+tested by running `koch tests pcat ` (or `cat` to avoid parallel
+testing, which is slower).
 
 The folder ``dll`` contains simple DLL tests.
 
diff --git a/tests/stdlib/tosproc.nim b/tests/stdlib/tosproc.nim
index 36afd6a23..b5328c9eb 100644
--- a/tests/stdlib/tosproc.nim
+++ b/tests/stdlib/tosproc.nim
@@ -1,7 +1,6 @@
 # test the osproc module
 
-import stdtest/specialpaths
-import "$nim" / compiler/unittest_light
+import stdtest/[specialpaths, unittest_light]
 
 when defined(case_testfile): # compiled test file for child process
   from posix import exitnow
diff --git a/tests/stdlib/tstackframes.nim b/tests/stdlib/tstackframes.nim
index be66eb836..618ff7b92 100644
--- a/tests/stdlib/tstackframes.nim
+++ b/tests/stdlib/tstackframes.nim
@@ -1,5 +1,5 @@
 import std/[strformat,os,osproc]
-import "$nim/compiler/unittest_light" # works even if moved by megatest
+import stdtest/unittest_light
 
 proc main(opt: string, expected: string) =
   const nim = getCurrentCompilerExe()

From b35d370d885b07d3f4eca527197f42f532bdcf64 Mon Sep 17 00:00:00 2001
From: Andreas Rumpf 
Date: Tue, 19 May 2020 21:42:53 +0200
Subject: [PATCH 0074/3768] specialize genericReset (#14398)

* progress
* make tests green
* maybe we also want to reset pointers, dunno
* progress
* cleanup; fixes #13879 [backport:1.2]
---
 compiler/ccgexprs.nim | 46 ++++++++++-----------
 compiler/ccgreset.nim | 94 +++++++++++++++++++++++++++++++++++++++++++
 compiler/cgen.nim     |  8 +++-
 lib/system/gc_ms.nim  |  2 +-
 4 files changed, 124 insertions(+), 26 deletions(-)
 create mode 100644 compiler/ccgreset.nim

diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim
index 0b9958ee4..ec54081a2 100644
--- a/compiler/ccgexprs.nim
+++ b/compiler/ccgexprs.nim
@@ -1194,9 +1194,11 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) =
 proc genReset(p: BProc, n: PNode) =
   var a: TLoc
   initLocExpr(p, n[1], a)
-  linefmt(p, cpsStmts, "#genericReset((void*)$1, $2);$n",
-          [addrLoc(p.config, a),
-          genTypeInfo(p.module, skipTypes(a.t, {tyVar}), n.info)])
+  specializeReset(p, a)
+  when false:
+    linefmt(p, cpsStmts, "#genericReset((void*)$1, $2);$n",
+            [addrLoc(p.config, a),
+            genTypeInfo(p.module, skipTypes(a.t, {tyVar}), n.info)])
 
 proc genDefault(p: BProc; n: PNode; d: var TLoc) =
   if d.k == locNone: getTemp(p, n.typ, d, needsInit=true)
@@ -2793,6 +2795,21 @@ proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo): Rope =
   else:
     globalError(p.config, info, "cannot create null element for: " & $t.kind)
 
+proc caseObjDefaultBranch(obj: PNode; branch: Int128): int =
+  for i in 1 ..< obj.len:
+    for j in 0 .. obj[i].len - 2:
+      if obj[i][j].kind == nkRange:
+        let x = getOrdValue(obj[i][j][0])
+        let y = getOrdValue(obj[i][j][1])
+        if branch >= x and branch <= y:
+          return i
+      elif getOrdValue(obj[i][j]) == branch:
+        return i
+    if obj[i].len == 1:
+      # else branch
+      return i
+  assert(false, "unreachable")
+
 proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: PNode,
                      result: var Rope; count: var int;
                      isConst: bool, info: TLineInfo) =
@@ -2815,31 +2832,14 @@ proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: PNode,
           branch = getOrdValue(constOrNil[i])
           break
 
-    var selectedBranch = -1
-    block branchSelection:
-      for i in 1 ..< obj.len:
-        for j in 0 .. obj[i].len - 2:
-          if obj[i][j].kind == nkRange:
-              let x = getOrdValue(obj[i][j][0])
-              let y = getOrdValue(obj[i][j][1])
-              if branch >= x and branch <= y:
-                selectedBranch = i
-                break branchSelection
-          elif getOrdValue(obj[i][j]) == branch:
-            selectedBranch = i
-            break branchSelection
-        if obj[i].len == 1:
-          # else branch
-          selectedBranch = i
-    assert(selectedBranch >= 1)
-
+    let selectedBranch = caseObjDefaultBranch(obj, branch)
     result.add "{"
     var countB = 0
     let b = lastSon(obj[selectedBranch])
     # designated initilization is the only way to init non first element of unions
     # branches are allowed to have no members (b.len == 0), in this case they don't need initializer
-    if  b.kind == nkRecList and b.len > 0:
-      result.add "._" &  mangleRecFieldName(p.module, obj[0].sym) & "_" & $selectedBranch & " = {"
+    if b.kind == nkRecList and b.len > 0:
+      result.add "._" & mangleRecFieldName(p.module, obj[0].sym) & "_" & $selectedBranch & " = {"
       getNullValueAux(p, t,  b, constOrNil, result, countB, isConst, info)
       result.add "}"
     elif b.kind == nkSym:
diff --git a/compiler/ccgreset.nim b/compiler/ccgreset.nim
new file mode 100644
index 000000000..ef1505f57
--- /dev/null
+++ b/compiler/ccgreset.nim
@@ -0,0 +1,94 @@
+#
+#
+#           The Nim Compiler
+#        (c) Copyright 2020 Andreas Rumpf
+#
+#    See the file "copying.txt", included in this
+#    distribution, for details about the copyright.
+#
+
+# included from cgen.nim
+
+## Code specialization instead of the old, incredibly slow 'genericReset'
+## implementation.
+
+proc specializeResetT(p: BProc, accessor: Rope, typ: PType)
+
+proc specializeResetN(p: BProc, accessor: Rope, n: PNode;
+                     typ: PType) =
+  if n == nil: return
+  case n.kind
+  of nkRecList:
+    for i in 0..
Date: Wed, 20 May 2020 00:41:57 +0200
Subject: [PATCH 0075/3768] fixes #14126 [backport:1.2] (#14390)

* fixes #14126 [backport:1.2]

* used more logic to optimize it further; updated Nimble version
---
 compiler/ccgcalls.nim          | 29 ++++++++++++++++----
 koch.nim                       |  2 +-
 tests/ccgbugs/tcodegenbug1.nim | 48 +++++++++++++++++++++++++++++++---
 3 files changed, 70 insertions(+), 9 deletions(-)

diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim
index 893d597bd..8f9d879df 100644
--- a/compiler/ccgcalls.nim
+++ b/compiler/ccgcalls.nim
@@ -21,11 +21,30 @@ proc canRaiseDisp(p: BProc; n: PNode): bool =
     # we have to be *very* conservative:
     result = canRaiseConservative(n)
 
-proc leftAppearsOnRightSide(le, ri: PNode): bool =
+proc preventNrvo(p: BProc; le, ri: PNode): bool =
+  proc locationEscapes(p: BProc; le: PNode): bool =
+    var n = le
+    while true:
+      # do NOT follow nkHiddenDeref here!
+      case n.kind
+      of nkSym:
+        # we don't own the location so it escapes:
+        return n.sym.owner != p.prc
+      of nkDotExpr, nkBracketExpr, nkObjUpConv, nkObjDownConv,
+          nkCheckedFieldExpr:
+        n = n[0]
+      of nkHiddenStdConv, nkHiddenSubConv, nkConv:
+        n = n[1]
+      else:
+        # cannot analyse the location; assume the worst
+        return true
+
   if le != nil:
     for i in 1.. 0 or locationEscapes(p, le))
 
 proc hasNoInit(call: PNode): bool {.inline.} =
   result = call[0].kind == nkSym and sfNoInit in call[0].sym.flags
@@ -51,7 +70,7 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc,
     if isInvalidReturnType(p.config, typ[0]):
       if params != nil: pl.add(~", ")
       # beware of 'result = p(result)'. We may need to allocate a temporary:
-      if d.k in {locTemp, locNone} or not leftAppearsOnRightSide(le, ri):
+      if d.k in {locTemp, locNone} or not preventNrvo(p, le, ri):
         # Great, we can use 'd':
         if d.k == locNone: getTemp(p, typ[0], d, needsInit=true)
         elif d.k notin {locTemp} and not hasNoInit(ri):
@@ -150,7 +169,7 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode): Rope =
       result = "($4*)($1)+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c), dest]
     of tyString, tySequence:
       let atyp = skipTypes(a.t, abstractInst)
-      if formalType.skipTypes(abstractInst).kind == tyVar and atyp.kind == tyString and 
+      if formalType.skipTypes(abstractInst).kind == tyVar and atyp.kind == tyString and
           optSeqDestructors in p.config.globalOptions:
         linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
       if atyp.kind == tyVar and not compileToCpp(p.module):
@@ -166,7 +185,7 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode): Rope =
       result = "$1, $1Len_0" % [rdLoc(a)]
     of tyString, tySequence:
       let ntyp = skipTypes(n.typ, abstractInst)
-      if formalType.skipTypes(abstractInst).kind == tyVar and ntyp.kind == tyString and 
+      if formalType.skipTypes(abstractInst).kind == tyVar and ntyp.kind == tyString and
           optSeqDestructors in p.config.globalOptions:
         linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)])
       if ntyp.kind == tyVar and not compileToCpp(p.module):
@@ -292,7 +311,7 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) =
     if isInvalidReturnType(p.config, typ[0]):
       if ri.len > 1: pl.add(~", ")
       # beware of 'result = p(result)'. We may need to allocate a temporary:
-      if d.k in {locTemp, locNone} or not leftAppearsOnRightSide(le, ri):
+      if d.k in {locTemp, locNone} or not preventNrvo(p, le, ri):
         # Great, we can use 'd':
         if d.k == locNone:
           getTemp(p, typ[0], d, needsInit=true)
diff --git a/koch.nim b/koch.nim
index 0391c3c4a..ee7c8006a 100644
--- a/koch.nim
+++ b/koch.nim
@@ -10,7 +10,7 @@
 #
 
 const
-  NimbleStableCommit = "6f2b1ace922e565f7f48a69648e16a002a5d15e3" # master
+  NimbleStableCommit = "63695f490728e3935692c29f3d71944d83bb1e83" # master
 
 when not defined(windows):
   const
diff --git a/tests/ccgbugs/tcodegenbug1.nim b/tests/ccgbugs/tcodegenbug1.nim
index ebdc57144..11846ff95 100644
--- a/tests/ccgbugs/tcodegenbug1.nim
+++ b/tests/ccgbugs/tcodegenbug1.nim
@@ -3,7 +3,10 @@ discard """
 obj.inner.id = 7
 id = 7
 obj = (inner: (kind: Just, id: 7))
-2'''
+2
+(a: "1", b: "2", c: "3")
+caught
+(a: "1", b: "", c: "3")'''
 """
 
 # bug #6960
@@ -129,12 +132,51 @@ import macros
 func myfunc(obj: MyObject): MyResult {.raises: [].} =
   template index: auto =
     case obj.kind:
-      of Float: $obj.index 
+      of Float: $obj.index
       of Fixed: "Fixed"
   macro to_str(a: untyped): string =
-    result = newStrLitNode(a.repr)  
+    result = newStrLitNode(a.repr)
   result.val[0] = index
   result.val[1] = to_str(obj.kind + Ola)
 
 let x = MyObject(someInt: 10, kind: Fixed)
 echo myfunc(x).val.len
+
+# bug #14126
+
+type X = object
+  a, b, c: string
+
+proc f(): X =
+  result.a = "a"
+  result.b = "b"
+  raise (ref ValueError)()
+
+proc ohmanNoNRVO =
+  var x: X
+  x.a = "1"
+  x.b = "2"
+  x.c = "3"
+
+  try:
+    x = f()
+  except:
+    discard
+
+  echo x
+  doAssert x.c == "3", "shouldn't modify x if f raises"
+
+ohmanNoNRVO()
+
+proc ohmanNoNRVO2(x: var X) =
+  x.a = "1"
+  x.c = "3"
+  x = f()
+
+var xgg: X
+try:
+  ohmanNoNRVO2(xgg)
+except:
+  echo "caught"
+echo xgg
+doAssert xgg.c == "3", "this assert will fail"

From 1450924b1e68ad3cd2dc8db2c54f9741315ca212 Mon Sep 17 00:00:00 2001
From: Danil Yarantsev <21169548+Yardanico@users.noreply.github.com>
Date: Wed, 20 May 2020 10:28:47 +0300
Subject: [PATCH 0076/3768] Fix some typos in the manual [backport] (#14399)

---
 doc/manual.rst | 20 ++++++++++----------
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/doc/manual.rst b/doc/manual.rst
index 759042078..8b27c7819 100644
--- a/doc/manual.rst
+++ b/doc/manual.rst
@@ -148,7 +148,7 @@ It is mostly accurate to picture semantic analysis proceeding top to bottom and
 left to right in the source code, with compile-time code execution interleaved
 when necessary to compute values that are required for subsequent semantic
 analysis. We will see much later in this document that macro invocation not only
-requires this interleaving, but also creates a situation where semantic analyis
+requires this interleaving, but also creates a situation where semantic analysis
 does not entirely proceed top to bottom and left to right.
 
 
@@ -792,7 +792,7 @@ features supported at compile time (as detailed in the next section below).
 Within such a code block, it is possible to declare variables and then later
 read and update them, or declare variables and pass them to procedures that
 modify them. However, the code in such a block must still adhere to the
-retrictions listed above for referencing values and operations outside the
+restrictions listed above for referencing values and operations outside the
 block.
 
 The ability to access and modify compile-time variables adds flexibility to
@@ -1296,7 +1296,7 @@ A parameter ``A`` may be an *open array*, in which case it is indexed by
 integers from 0 to ``len(A)-1``. An array expression may be constructed by the
 array constructor ``[]``. The element type of this array expression is
 inferred from the type of the first element. All other elements need to be
-implicitly convertable to this type.
+implicitly convertible to this type.
 
 Sequences are similar to arrays but of dynamic length which may change
 during runtime (like strings). Sequences are implemented as growable arrays,
@@ -3837,7 +3837,7 @@ as there are components in the tuple. The i'th iteration variable's type is
 the type of the i'th component. In other words, implicit tuple unpacking in a
 for loop context is supported.
 
-Implict items/pairs invocations
+Implicit items/pairs invocations
 -------------------------------
 
 If the for loop expression ``e`` does not denote an iterator and the for loop
@@ -4231,7 +4231,7 @@ Exception hierarchy
 The exception tree is defined in the `system `_ module.
 Every exception inherits from ``system.Exception``. Exceptions that indicate
 programming bugs inherit from ``system.Defect`` (which is a subtype of ``Exception``)
-and are stricly speaking not catchable as they can also be mapped to an operation
+and are strictly speaking not catchable as they can also be mapped to an operation
 that terminates the whole process. If panics are turned into exceptions, these
 exceptions inherit from `Defect`.
 
@@ -4566,8 +4566,8 @@ more complex type classes:
 
 Whilst the syntax of type classes appears to resemble that of ADTs/algebraic data
 types in ML-like languages, it should be understood that type classes are static
-constraints to be enforced at type instantations. Type classes are not really
-types in themsleves, but are instead a system of providing generic "checks" that
+constraints to be enforced at type instantiations. Type classes are not really
+types in themselves, but are instead a system of providing generic "checks" that
 ultimately *resolve* to some singular type. Type classes do not allow for
 runtime type dynamism, unlike object variants or methods.
 
@@ -4729,7 +4729,7 @@ instantiation. The following is not allowed:
   # not valid: 'T' is not inferred to be of type 'var int'
   g(v, i)
 
-  # also not allowed: explict instantiation via 'var int'
+  # also not allowed: explicit instantiation via 'var int'
   g[var int](v, i)
 
 
@@ -5219,7 +5219,7 @@ tree (AST) of the code that is passed to it. The macro can then do
 transformations on it and return the transformed AST. This can be used to
 add custom language features and implement `domain specific languages`:idx:.
 
-Macro invocation is a case where semantic analyis does **not** entirely proceed
+Macro invocation is a case where semantic analysis does **not** entirely proceed
 top to bottom and left to right. Instead, semantic analysis happens at least
 twice:
 
@@ -6452,7 +6452,7 @@ and instead the generated code should contain an ``#include``:
     PFile {.importc: "FILE*", header: "".} = distinct pointer
       # import C's FILE* type; Nim will treat it as a new pointer type
 
-The ``header`` pragma always expects a string constant. The string contant
+The ``header`` pragma always expects a string constant. The string constant
 contains the header file: As usual for C, a system header file is enclosed
 in angle brackets: ``<>``. If no angle brackets are given, Nim
 encloses the header file in ``""`` in the generated C code.

From 4ae341353de5c58dc339e47b0eec2bbb4649dc10 Mon Sep 17 00:00:00 2001
From: alaviss 
Date: Wed, 20 May 2020 07:42:55 +0000
Subject: [PATCH 0077/3768] asyncdispatch, asyncnet: add inheritance control
 (#14362)

* asyncdispatch, asyncnet: add inheritance control

* asyncnet, asyncdispatch: cleanup
---
 changelog.md               | 17 ++++-----
 lib/pure/asyncdispatch.nim | 71 ++++++++++++++++++++++++++++++--------
 lib/pure/asyncnet.nim      | 44 +++++++++++++++++------
 tests/stdlib/tfdleak.nim   | 36 ++++++++++++++++---
 4 files changed, 130 insertions(+), 38 deletions(-)

diff --git a/changelog.md b/changelog.md
index bb87f817c..88619b1c2 100644
--- a/changelog.md
+++ b/changelog.md
@@ -13,20 +13,21 @@
 - `deques.peekFirst` and `deques.peekLast` now have `var Deque[T] -> var T` overloads.
 - File handles created from high-level abstractions in the stdlib will no longer
   be inherited by child processes. In particular, these modules are affected:
-  `system`, `nativesockets`, `net` and `selectors`.
+  `asyncdispatch`, `asyncnet`, `system`, `nativesockets`, `net` and `selectors`.
 
-  For `net` and `nativesockets`, an `inheritable` flag has been added to all
-  `proc`s that create sockets, allowing the user to control whether the
-  resulting socket is inheritable. This flag is provided to ease the writing of
-  multi-process servers, where sockets inheritance is desired.
+  For `asyncdispatch`, `asyncnet`, `net` and `nativesockets`, an `inheritable`
+  flag has been added to all `proc`s that create sockets, allowing the user to
+  control whether the resulting socket is inheritable. This flag is provided to
+  ease the writing of multi-process servers, where sockets inheritance is
+  desired.
 
   For a transistion period, define `nimInheritHandles` to enable file handle
   inheritance by default. This flag does **not** affect the `selectors` module
   due to the differing semantics between operating systems.
 
-  `system.setInheritable` and `nativesockets.setInheritable` is also introduced
-  for setting file handle or socket inheritance. Not all platform have these
-  `proc`s defined.
+  `asyncdispatch.setInheritable`, `system.setInheritable` and
+  `nativesockets.setInheritable` is also introduced for setting file handle or
+  socket inheritance. Not all platform have these `proc`s defined.
 
 - The file descriptors created for internal bookkeeping by `ioselector_kqueue`
   and `ioselector_epoll` will no longer be leaked to child processes.
diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim
index 91778a5bc..0800cb638 100644
--- a/lib/pure/asyncdispatch.nim
+++ b/lib/pure/asyncdispatch.nim
@@ -228,6 +228,16 @@ proc initCallSoonProc =
   if asyncfutures.getCallSoonProc().isNil:
     asyncfutures.setCallSoonProc(callSoon)
 
+template implementSetInheritable() {.dirty.} =
+  when declared(setInheritable):
+    proc setInheritable*(fd: AsyncFD, inheritable: bool): bool =
+      ## Control whether a file handle can be inherited by child processes.
+      ## Returns ``true`` on success.
+      ##
+      ## This procedure is not guaranteed to be available for all platforms.
+      ## Test for availability with `declared()`_.
+      fd.FileHandle.setInheritable(inheritable)
+
 when defined(windows) or defined(nimdoc):
   import winlean, sets, hashes
   type
@@ -695,7 +705,8 @@ when defined(windows) or defined(nimdoc):
           retFuture.complete(bytesReceived)
     return retFuture
 
-  proc acceptAddr*(socket: AsyncFD, flags = {SocketFlag.SafeDisconn}):
+  proc acceptAddr*(socket: AsyncFD, flags = {SocketFlag.SafeDisconn},
+                   inheritable = defined(nimInheritHandles)):
       owned(Future[tuple[address: string, client: AsyncFD]]) =
     ## Accepts a new connection. Returns a future containing the client socket
     ## corresponding to that connection and the remote address of the client.
@@ -704,6 +715,9 @@ when defined(windows) or defined(nimdoc):
     ## The resulting client socket is automatically registered to the
     ## dispatcher.
     ##
+    ## If ``inheritable`` is false (the default), the resulting client socket will
+    ## not be inheritable by child processes.
+    ##
     ## The ``accept`` call may result in an error if the connecting socket
     ## disconnects during the duration of the ``accept``. If the ``SafeDisconn``
     ## flag is specified then this error will not be raised and instead
@@ -711,7 +725,7 @@ when defined(windows) or defined(nimdoc):
     verifyPresence(socket)
     var retFuture = newFuture[tuple[address: string, client: AsyncFD]]("acceptAddr")
 
-    var clientSock = createNativeSocket()
+    var clientSock = createNativeSocket(inheritable = inheritable)
     if clientSock == osInvalidSocket: raiseOSError(osLastError())
 
     const lpOutputLen = 1024
@@ -788,6 +802,8 @@ when defined(windows) or defined(nimdoc):
 
     return retFuture
 
+  implementSetInheritable()
+
   proc closeSocket*(socket: AsyncFD) =
     ## Closes a socket and ensures that it is unregistered.
     socket.SocketHandle.close()
@@ -1090,6 +1106,9 @@ else:
   import selectors
   from posix import EINTR, EAGAIN, EINPROGRESS, EWOULDBLOCK, MSG_PEEK,
                     MSG_NOSIGNAL
+  when declared(posix.accept4):
+    from posix import accept4, SOCK_CLOEXEC
+
   const
     InitCallbackListSize = 4         # initial size of callbacks sequence,
                                      # associated with file/socket descriptor.
@@ -1263,6 +1282,8 @@ else:
       # descriptor was unregistered in callback via `unregister()`.
       discard
 
+  implementSetInheritable()
+
   proc closeSocket*(sock: AsyncFD) =
     let selector = getGlobalDispatcher().selector
     if sock.SocketHandle notin selector:
@@ -1484,7 +1505,8 @@ else:
     addRead(socket, cb)
     return retFuture
 
-  proc acceptAddr*(socket: AsyncFD, flags = {SocketFlag.SafeDisconn}):
+  proc acceptAddr*(socket: AsyncFD, flags = {SocketFlag.SafeDisconn},
+                   inheritable = defined(nimInheritHandles)):
       owned(Future[tuple[address: string, client: AsyncFD]]) =
     var retFuture = newFuture[tuple[address: string,
         client: AsyncFD]]("acceptAddr")
@@ -1492,8 +1514,21 @@ else:
       result = true
       var sockAddress: Sockaddr_storage
       var addrLen = sizeof(sockAddress).SockLen
-      var client = accept(sock.SocketHandle,
-                          cast[ptr SockAddr](addr(sockAddress)), addr(addrLen))
+      var client =
+        when declared(accept4):
+          accept4(sock.SocketHandle, cast[ptr SockAddr](addr(sockAddress)),
+                  addr(addrLen), if inheritable: 0 else: SOCK_CLOEXEC)
+        else:
+          accept(sock.SocketHandle, cast[ptr SockAddr](addr(sockAddress)),
+                 addr(addrLen))
+      when declared(setInheritable) and not declared(accept4):
+        if client != osInvalidSocket and not setInheritable(client, inheritable):
+          # Set failure first because close() itself can fail,
+          # altering osLastError().
+          retFuture.fail(newOSError(osLastError()))
+          close client
+          return false
+
       if client == osInvalidSocket:
         let lastError = osLastError()
         assert lastError.int32 != EWOULDBLOCK and lastError.int32 != EAGAIN
@@ -1578,8 +1613,9 @@ proc poll*(timeout = 500) =
   ## `epoll`:idx: or `kqueue`:idx: primitive only once.
   discard runOnce(timeout)
 
-template createAsyncNativeSocketImpl(domain, sockType, protocol) =
-  let handle = createNativeSocket(domain, sockType, protocol)
+template createAsyncNativeSocketImpl(domain, sockType, protocol: untyped,
+                                     inheritable = defined(nimInheritHandles)) =
+  let handle = createNativeSocket(domain, sockType, protocol, inheritable)
   if handle == osInvalidSocket:
     return osInvalidSocket.AsyncFD
   handle.setBlocking(false)
@@ -1589,13 +1625,15 @@ template createAsyncNativeSocketImpl(domain, sockType, protocol) =
   register(result)
 
 proc createAsyncNativeSocket*(domain: cint, sockType: cint,
-                           protocol: cint): AsyncFD =
-  createAsyncNativeSocketImpl(domain, sockType, protocol)
+                              protocol: cint,
+                              inheritable = defined(nimInheritHandles)): AsyncFD =
+  createAsyncNativeSocketImpl(domain, sockType, protocol, inheritable)
 
 proc createAsyncNativeSocket*(domain: Domain = Domain.AF_INET,
-                           sockType: SockType = SOCK_STREAM,
-                           protocol: Protocol = IPPROTO_TCP): AsyncFD =
-  createAsyncNativeSocketImpl(domain, sockType, protocol)
+                              sockType: SockType = SOCK_STREAM,
+                              protocol: Protocol = IPPROTO_TCP,
+                              inheritable = defined(nimInheritHandles)): AsyncFD =
+  createAsyncNativeSocketImpl(domain, sockType, protocol, inheritable)
 
 proc newAsyncNativeSocket*(domain: cint, sockType: cint,
     protocol: cint): AsyncFD {.deprecated: "use createAsyncNativeSocket instead".} =
@@ -1824,12 +1862,17 @@ proc withTimeout*[T](fut: Future[T], timeout: int): owned(Future[bool]) =
   return retFuture
 
 proc accept*(socket: AsyncFD,
-    flags = {SocketFlag.SafeDisconn}): owned(Future[AsyncFD]) =
+             flags = {SocketFlag.SafeDisconn},
+             inheritable = defined(nimInheritHandles)): owned(Future[AsyncFD]) =
   ## Accepts a new connection. Returns a future containing the client socket
   ## corresponding to that connection.
+  ##
+  ## If ``inheritable`` is false (the default), the resulting client socket
+  ## will not be inheritable by child processes.
+  ##
   ## The future will complete when the connection is successfully accepted.
   var retFut = newFuture[AsyncFD]("accept")
-  var fut = acceptAddr(socket, flags)
+  var fut = acceptAddr(socket, flags, inheritable)
   fut.callback =
     proc (future: Future[tuple[address: string, client: AsyncFD]]) =
       assert future.finished
diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim
index 93334d0e2..7ccb469cf 100644
--- a/lib/pure/asyncnet.nim
+++ b/lib/pure/asyncnet.nim
@@ -129,12 +129,17 @@ type
   AsyncSocket* = ref AsyncSocketDesc
 
 proc newAsyncSocket*(fd: AsyncFD, domain: Domain = AF_INET,
-    sockType: SockType = SOCK_STREAM,
-    protocol: Protocol = IPPROTO_TCP, buffered = true): owned(AsyncSocket) =
+                     sockType: SockType = SOCK_STREAM,
+                     protocol: Protocol = IPPROTO_TCP,
+                     buffered = true,
+                     inheritable = defined(nimInheritHandles)): owned(AsyncSocket) =
   ## Creates a new ``AsyncSocket`` based on the supplied params.
   ##
   ## The supplied ``fd``'s non-blocking state will be enabled implicitly.
   ##
+  ## If ``inheritable`` is false (the default), the supplied ``fd`` will not
+  ## be inheritable by child processes.
+  ##
   ## **Note**: This procedure will **NOT** register ``fd`` with the global
   ## async dispatcher. You need to do this manually. If you have used
   ## ``newAsyncNativeSocket`` to create ``fd`` then it's already registered.
@@ -142,6 +147,8 @@ proc newAsyncSocket*(fd: AsyncFD, domain: Domain = AF_INET,
   new(result)
   result.fd = fd.SocketHandle
   fd.SocketHandle.setBlocking(false)
+  if not fd.SocketHandle.setInheritable(inheritable):
+    raiseOSError(osLastError())
   result.isBuffered = buffered
   result.domain = domain
   result.sockType = sockType
@@ -150,15 +157,19 @@ proc newAsyncSocket*(fd: AsyncFD, domain: Domain = AF_INET,
     result.currPos = 0
 
 proc newAsyncSocket*(domain: Domain = AF_INET, sockType: SockType = SOCK_STREAM,
-    protocol: Protocol = IPPROTO_TCP, buffered = true): owned(AsyncSocket) =
+                     protocol: Protocol = IPPROTO_TCP, buffered = true,
+                     inheritable = defined(nimInheritHandles)): owned(AsyncSocket) =
   ## Creates a new asynchronous socket.
   ##
   ## This procedure will also create a brand new file descriptor for
   ## this socket.
-  let fd = createAsyncNativeSocket(domain, sockType, protocol)
+  ##
+  ## If ``inheritable`` is false (the default), the new file descriptor will not
+  ## be inheritable by child processes.
+  let fd = createAsyncNativeSocket(domain, sockType, protocol, inheritable)
   if fd.SocketHandle == osInvalidSocket:
     raiseOSError(osLastError())
-  result = newAsyncSocket(fd, domain, sockType, protocol, buffered)
+  result = newAsyncSocket(fd, domain, sockType, protocol, buffered, inheritable)
 
 proc getLocalAddr*(socket: AsyncSocket): (string, Port) =
   ## Get the socket's local address and port number.
@@ -173,16 +184,20 @@ proc getPeerAddr*(socket: AsyncSocket): (string, Port) =
   getPeerAddr(socket.fd, socket.domain)
 
 proc newAsyncSocket*(domain, sockType, protocol: cint,
-    buffered = true): owned(AsyncSocket) =
+                     buffered = true,
+                     inheritable = defined(nimInheritHandles)): owned(AsyncSocket) =
   ## Creates a new asynchronous socket.
   ##
   ## This procedure will also create a brand new file descriptor for
   ## this socket.
-  let fd = createAsyncNativeSocket(domain, sockType, protocol)
+  ##
+  ## If ``inheritable`` is false (the default), the new file descriptor will not
+  ## be inheritable by child processes.
+  let fd = createAsyncNativeSocket(domain, sockType, protocol, inheritable)
   if fd.SocketHandle == osInvalidSocket:
     raiseOSError(osLastError())
   result = newAsyncSocket(fd, Domain(domain), SockType(sockType),
-                          Protocol(protocol), buffered)
+                          Protocol(protocol), buffered, inheritable)
 
 when defineSsl:
   proc getSslError(handle: SslPtr, err: cint): cint =
@@ -443,13 +458,18 @@ proc send*(socket: AsyncSocket, data: string,
   else:
     await send(socket.fd.AsyncFD, data, flags)
 
-proc acceptAddr*(socket: AsyncSocket, flags = {SocketFlag.SafeDisconn}):
+proc acceptAddr*(socket: AsyncSocket, flags = {SocketFlag.SafeDisconn},
+                 inheritable = defined(nimInheritHandles)):
       owned(Future[tuple[address: string, client: AsyncSocket]]) =
   ## Accepts a new connection. Returns a future containing the client socket
   ## corresponding to that connection and the remote address of the client.
+  ##
+  ## If ``inheritable`` is false (the default), the resulting client socket will
+  ## not be inheritable by child processes.
+  ##
   ## The future will complete when the connection is successfully accepted.
   var retFuture = newFuture[tuple[address: string, client: AsyncSocket]]("asyncnet.acceptAddr")
-  var fut = acceptAddr(socket.fd.AsyncFD, flags)
+  var fut = acceptAddr(socket.fd.AsyncFD, flags, inheritable)
   fut.callback =
     proc (future: Future[tuple[address: string, client: AsyncFD]]) =
       assert future.finished
@@ -458,7 +478,7 @@ proc acceptAddr*(socket: AsyncSocket, flags = {SocketFlag.SafeDisconn}):
       else:
         let resultTup = (future.read.address,
                          newAsyncSocket(future.read.client, socket.domain,
-                         socket.sockType, socket.protocol, socket.isBuffered))
+                         socket.sockType, socket.protocol, socket.isBuffered, inheritable))
         retFuture.complete(resultTup)
   return retFuture
 
@@ -466,6 +486,8 @@ proc accept*(socket: AsyncSocket,
     flags = {SocketFlag.SafeDisconn}): owned(Future[AsyncSocket]) =
   ## Accepts a new connection. Returns a future containing the client socket
   ## corresponding to that connection.
+  ## If ``inheritable`` is false (the default), the resulting client socket will
+  ## not be inheritable by child processes.
   ## The future will complete when the connection is successfully accepted.
   var retFut = newFuture[AsyncSocket]("asyncnet.accept")
   var fut = acceptAddr(socket, flags)
diff --git a/tests/stdlib/tfdleak.nim b/tests/stdlib/tfdleak.nim
index 08ef06da3..c4f144db5 100644
--- a/tests/stdlib/tfdleak.nim
+++ b/tests/stdlib/tfdleak.nim
@@ -4,13 +4,15 @@ discard """
   matrix: "; -d:nimInheritHandles"
 """
 
-import os, osproc, strutils, nativesockets, net, selectors, memfiles
+import os, osproc, strutils, nativesockets, net, selectors, memfiles,
+       asyncdispatch, asyncnet
 when defined(windows):
   import winlean
 else:
   import posix
 
-proc leakCheck(f: int | FileHandle | SocketHandle, msg: string, expectLeak = defined(nimInheritHandles)) =
+proc leakCheck(f: AsyncFD | int | FileHandle | SocketHandle, msg: string,
+               expectLeak = defined(nimInheritHandles)) =
   discard startProcess(
     getAppFilename(),
     args = @[$f.int, msg, $expectLeak],
@@ -40,14 +42,14 @@ proc main() =
     let sock = createNativeSocket()
     defer: close sock
     leakCheck(sock, "createNativeSocket()")
-    if sock.setInheritable(true):
-      leakCheck(sock, "createNativeSocket()", true)
+    if sock.setInheritable(not defined(nimInheritHandles)):
+      leakCheck(sock, "createNativeSocket()", not defined(nimInheritHandles))
     else:
       raiseOSError osLastError()
 
     let server = newSocket()
     defer: close server
-    server.bindAddr()
+    server.bindAddr(address = "127.0.0.1")
     server.listen()
     let (_, port) = server.getLocalAddr
 
@@ -74,6 +76,30 @@ proc main() =
       leakCheck(mf.mapHandle, "memfiles.open().mapHandle", false)
     else:
       leakCheck(mf.handle, "memfiles.open().handle", false)
+
+    let sockAsync = createAsyncNativeSocket()
+    defer: closeSocket sockAsync
+    leakCheck(sockAsync, "createAsyncNativeSocket()")
+    if sockAsync.setInheritable(not defined(nimInheritHandles)):
+      leakCheck(sockAsync, "createAsyncNativeSocket()", not defined(nimInheritHandles))
+    else:
+      raiseOSError osLastError()
+
+    let serverAsync = newAsyncSocket()
+    defer: close serverAsync
+    serverAsync.bindAddr(address = "127.0.0.1")
+    serverAsync.listen()
+    let (_, portAsync) = serverAsync.getLocalAddr
+
+    leakCheck(serverAsync.getFd, "newAsyncSocket()")
+
+    let clientAsync = newAsyncSocket()
+    defer: close clientAsync
+    waitFor clientAsync.connect("127.0.0.1", portAsync)
+
+    let inputAsync = waitFor serverAsync.accept()
+
+    leakCheck(inputAsync.getFd, "accept() async")
   else:
     let
       fd = parseInt(paramStr 1)

From 3d20f141939b0cb76079a9b0ae7784f7877f1698 Mon Sep 17 00:00:00 2001
From: Timothee Cour 
Date: Wed, 20 May 2020 00:45:34 -0700
Subject: [PATCH 0078/3768] fix #10731 ; `runnableExamples "-b:cpp --run:off":
 code` works (#14384)

* runnableExamples "-b:cpp -r:off": code
---
 changelog.md                                 |  2 +
 compiler/docgen.nim                          | 86 +++++++++++++-------
 lib/system.nim                               | 11 ++-
 nimdoc/tester.nim                            |  5 +-
 nimdoc/testproject/expected/testproject.html |  2 +-
 tests/nimdoc/trunnableexamples.nim           | 35 +++++++-
 6 files changed, 104 insertions(+), 37 deletions(-)

diff --git a/changelog.md b/changelog.md
index 88619b1c2..0e1af73c1 100644
--- a/changelog.md
+++ b/changelog.md
@@ -185,6 +185,8 @@ proc mydiv(a, b): int {.raises: [].} =
   use `--doccmd:skip` to skip runnableExamples and rst test snippets.
 - new flag `--usenimcache` to output to nimcache (whatever it resolves to after all commands are processed)
   and avoids polluting both $pwd and $projectdir. It can be used with any command.
+- `runnableExamples "-b:cpp -r:off": code` is now supported, allowing to override how an example is compiled and run,
+  for example to change backend or compile only.
 
 ## Tool changes
 
diff --git a/compiler/docgen.nim b/compiler/docgen.nim
index 6123d49cf..8b5201029 100644
--- a/compiler/docgen.nim
+++ b/compiler/docgen.nim
@@ -17,7 +17,7 @@ import
   packages/docutils/rst, packages/docutils/rstgen,
   json, xmltree, cgi, trees, types,
   typesrenderer, astalgo, lineinfos, intsets,
-  pathutils, trees
+  pathutils, trees, tables
 
 const
   exportSection = skField
@@ -26,6 +26,12 @@ const
 
 type
   TSections = array[TSymKind, Rope]
+  ExampleGroup = ref object
+    ## a group of runnableExamples with same rdoccmd
+    rdoccmd: string ## from 1st arg in `runnableExamples(rdoccmd): body`
+    docCmd: string ## from user config, eg --doccmd:-d:foo
+    code: string ## contains imports; each import contains `body`
+    index: int ## group index
   TDocumentor = object of rstgen.RstGenerator
     modDesc: Rope       # module description
     module: PSym
@@ -44,7 +50,7 @@ type
                     # already. See bug #3655
     destFile*: AbsoluteFile
     thisDir*: AbsoluteDir
-    examples: string
+    exampleGroups: OrderedTable[string, ExampleGroup]
     wroteCss*: bool
 
   PDoc* = ref TDocumentor ## Alias to type less.
@@ -443,54 +449,72 @@ proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var Rope; renderFlags: TRe
       dispA(d.conf, result, "$1", "\\spanOther{$1}",
             [escLit])
 
-proc testExample(d: PDoc; ex: PNode) =
+proc exampleOutputDir(d: PDoc): AbsoluteDir = d.conf.getNimcacheDir / RelativeDir"runnableExamples"
+
+proc writeExample(d: PDoc; ex: PNode, rdoccmd: string) =
   if d.conf.errorCounter > 0: return
-  let outputDir = d.conf.getNimcacheDir / RelativeDir"runnableExamples"
+  let outputDir = d.exampleOutputDir
   createDir(outputDir)
   inc d.exampleCounter
+  # PRTEMP
   let outp = outputDir / RelativeFile(extractFilename(d.filename.changeFileExt"" &
       "_examples" & $d.exampleCounter & ".nim"))
   #let nimcache = outp.changeFileExt"" & "_nimcache"
   renderModule(ex, d.filename, outp.string, conf = d.conf)
-  d.examples.add "import r\"" & outp.string & "\"\n"
+  if rdoccmd notin d.exampleGroups: d.exampleGroups[rdoccmd] = ExampleGroup(rdoccmd: rdoccmd, docCmd: d.conf.docCmd, index: d.exampleGroups.len)
+  d.exampleGroups[rdoccmd].code.add "import r\"$1\"\n" % outp.string
 
 proc runAllExamples(d: PDoc) =
-  let docCmd = d.conf.docCmd
   let backend = d.conf.backend
   # This used to be: `let backend = if isDefined(d.conf, "js"): "js"` (etc), however
   # using `-d:js` (etc) cannot work properly, eg would fail with `importjs`
   # since semantics are affected by `config.backend`, not by isDefined(d.conf, "js")
-  if d.examples.len == 0 or docCmd == docCmdSkip: return
-  let outputDir = d.conf.getNimcacheDir / RelativeDir"runnableExamples"
-  let outp = outputDir / RelativeFile(extractFilename(d.filename.changeFileExt"" &
-      "_examples.nim"))
-  writeFile(outp, d.examples)
-  let cmd = "$nim $backend -r --warning:UnusedImport:off --path:$path --nimcache:$nimcache $docCmd $file" % [
-    "nim", os.getAppFilename(),
-    "backend", $d.conf.backend,
-    "path", quoteShell(d.conf.projectPath),
-    "nimcache", quoteShell(outputDir),
-    "file", quoteShell(outp),
-    "docCmd", docCmd,
-  ]
-  if os.execShellCmd(cmd) != 0:
-    quit "[runnableExamples] failed: generated file: '$1' cmd: $2" % [outp.string, cmd]
-  else:
-    # keep generated source file `outp` to allow inspection.
-    rawMessage(d.conf, hintSuccess, ["runnableExamples: " & outp.string])
-    removeFile(outp.changeFileExt(ExeExt))
+  let outputDir = d.exampleOutputDir
+  for _, group in d.exampleGroups:
+    if group.docCmd == docCmdSkip: continue
+    let outp = outputDir / RelativeFile("$1_group$2_examples.nim" % [d.filename.splitFile.name, $group.index])
+    group.code = "# autogenerated by docgen\n# source: $1\n# rdoccmd: $2\n$3" % [d.filename, group.rdoccmd, group.code]
+    writeFile(outp, group.code)
+    # most useful semantics is that `docCmd` comes after `rdoccmd`, so that we can (temporarily) override
+    # via command line
+    let cmd = "$nim $backend -r --warning:UnusedImport:off --path:$path --nimcache:$nimcache $rdoccmd $docCmd $file" % [
+      "nim", os.getAppFilename(),
+      "backend", $d.conf.backend,
+      "path", quoteShell(d.conf.projectPath),
+      "nimcache", quoteShell(outputDir),
+      "file", quoteShell(outp),
+      "rdoccmd", group.rdoccmd,
+      "docCmd", group.docCmd,
+    ]
+    if os.execShellCmd(cmd) != 0:
+      quit "[runnableExamples] failed: generated file: '$1' group: '$2' cmd: $3" % [outp.string, $group[], cmd]
+    else:
+      # keep generated source file `outp` to allow inspection.
+      rawMessage(d.conf, hintSuccess, ["runnableExamples: " & outp.string])
+      # removeFile(outp.changeFileExt(ExeExt)) # it's in nimcache, no need to remove
 
+proc prepareExample(d: PDoc; n: PNode): string =
+  ## returns `rdoccmd` for this runnableExamples
+  var rdoccmd = ""
+  if n.len < 2 or n.len > 3: globalError(d.conf, n.info, "runnableExamples invalid")
+  if n.len == 3:
+    let n1 = n[1]
+    # xxx this should be evaluated during sempass
+    if n1.kind notin nkStrKinds: globalError(d.conf, n1.info, "string litteral expected")
+    rdoccmd = n1.strVal
 
-proc prepareExamples(d: PDoc; n: PNode) =
   var docComment = newTree(nkCommentStmt)
   let loc = d.conf.toFileLineCol(n.info)
-  docComment.comment = "autogenerated by docgen from " & loc
+
+  docComment.comment = "autogenerated by docgen\nloc: $1\nrdoccmd: $2" % [loc, rdoccmd]
   var runnableExamples = newTree(nkStmtList,
       docComment,
       newTree(nkImportStmt, newStrNode(nkStrLit, d.filename)))
   runnableExamples.info = n.info
+
   for a in n.lastSon: runnableExamples.add a
-  testExample(d, runnableExamples)
+  writeExample(d, runnableExamples, rdoccmd)
+  result = rdoccmd
   when false:
     proc extractImports(n: PNode; result: PNode) =
       if n.kind in {nkImportStmt, nkImportExceptStmt, nkFromStmt}:
@@ -510,9 +534,11 @@ proc getAllRunnableExamplesRec(d: PDoc; n, orig: PNode; dest: var Rope) =
   of nkCallKinds:
     if isRunnableExamples(n[0]) and
         n.len >= 2 and n.lastSon.kind == nkStmtList:
-      prepareExamples(d, n)
+      let rdoccmd = prepareExample(d, n)
+      var msg = "Example:"
+      if rdoccmd.len > 0: msg.add " cmd: " & rdoccmd
       dispA(d.conf, dest, "\n

$1

\n", - "\n\\textbf{$1}\n", [rope"Examples:"]) + "\n\\textbf{$1}\n", [msg.rope]) inc d.listingCounter let id = $d.listingCounter dest.add(d.config.getOrDefault"doc.listing_start" % [id, "langNim"]) diff --git a/lib/system.nim b/lib/system.nim index e5b359c70..85be43cba 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -117,7 +117,7 @@ else: Ordinal* = OrdinalImpl | uint | uint64 when defined(nimHasRunnableExamples): - proc runnableExamples*(body: untyped) {.magic: "RunnableExamples".} + proc runnableExamples*(rdoccmd = "", body: untyped) {.magic: "RunnableExamples".} ## A section you should use to mark `runnable example`:idx: code with. ## ## - In normal debug and release builds code within @@ -139,10 +139,15 @@ when defined(nimHasRunnableExamples): ## assert double(5) == 10 ## block: ## at block scope ## defer: echo "done" - ## ## result = 2 * x + ## runnableExamples "-d:foo -b:cpp": + ## import std/compilesettings + ## doAssert querySetting(backend) == "cpp" + ## runnableExamples "-r:off": ## this one is only compiled + ## import std/browsers + ## openDefaultBrowser "https://forum.nim-lang.org/" else: - template runnableExamples*(body: untyped) = + template runnableExamples*(doccmd = "", body: untyped) = discard proc declared*(x: untyped): bool {.magic: "Defined", noSideEffect, compileTime.} diff --git a/nimdoc/tester.nim b/nimdoc/tester.nim index a1500455e..58272a9b6 100644 --- a/nimdoc/tester.nim +++ b/nimdoc/tester.nim @@ -24,12 +24,13 @@ proc testNimDoc(prjDir, docsDir: string; switches: NimSwitches; fixup = false) = nimBuildIndexSwitches = switches.buildIndex.join(" ") putEnv("SOURCE_DATE_EPOCH", "100000") + const nimExe = getCurrentCompilerExe() # so that `bin/nim_temp r nimdoc/tester.nim` works if nimDocSwitches != "": - exec("nim doc $1" % [nimDocSwitches]) + exec("$1 doc $2" % [nimExe, nimDocSwitches]) if nimBuildIndexSwitches != "": - exec("nim buildIndex $1" % [nimBuildIndexSwitches]) + exec("$1 buildIndex $2" % [nimExe, nimBuildIndexSwitches]) for expected in walkDirRec(prjDir / "expected/"): let produced = expected.replace('\\', '/').replace("/expected/", "/$1/" % [docsDir]) diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html index 77f178265..4eb8ed44c 100644 --- a/nimdoc/testproject/expected/testproject.html +++ b/nimdoc/testproject/expected/testproject.html @@ -183,7 +183,7 @@ function main() {

This is the top level module. -

Examples:

+

Example:

import
   subdir / subdir_b / utils
 
diff --git a/tests/nimdoc/trunnableexamples.nim b/tests/nimdoc/trunnableexamples.nim
index e3ae6b9cb..73cfacd43 100644
--- a/tests/nimdoc/trunnableexamples.nim
+++ b/tests/nimdoc/trunnableexamples.nim
@@ -1,5 +1,5 @@
 discard """
-cmd: "nim doc $file"
+cmd: "nim doc --doccmd:-d:testFooExternal --hints:off $file"
 action: "compile"
 nimout: '''
 foo1
@@ -66,6 +66,39 @@ when true: # issue #12746
         # specifying Error is culprit
         discard
 
+when true: # runnableExamples with rdoccmd
+  runnableExamples "-d:testFoo -d:testBar":
+    doAssert defined(testFoo) and defined(testBar)
+    doAssert defined(testFooExternal)
+  runnableExamples "-d:testFoo2":
+    doAssert defined(testFoo2)
+    doAssert not defined(testFoo) # doesn't get confused by other examples
+
+  ## all these syntaxes work too
+  runnableExamples("-d:testFoo2"): discard
+  runnableExamples(): discard
+  runnableExamples: discard
+  runnableExamples "-r:off": # issue #10731
+    doAssert false ## we compile only (-r:off), so this won't be run
+  runnableExamples "-b:js":
+    import std/compilesettings
+    proc startsWith*(s, prefix: cstring): bool {.noSideEffect, importjs: "#.startsWith(#)".}
+    doAssert querySetting(backend) == "js"
+  runnableExamples "-b:cpp":
+    static: doAssert defined(cpp)
+    type std_exception {.importcpp: "std::exception", header: "".} = object
+
+  proc fun2*() =
+    runnableExamples "-d:foo": discard # checks that it also works inside procs
+
+  when false: # future work
+    # passing non-string-litterals (for reuse)
+    const a = "-b:cpp"
+    runnableExamples(a): discard
+
+    # passing seq (to run with multiple compilation options)
+    runnableExamples(@["-b:cpp", "-b:js"]): discard
+
 # also check for runnableExamples at module scope
 runnableExamples:
   block:

From 7fe4c66f3598d9c87fe93e60f5f123857779980c Mon Sep 17 00:00:00 2001
From: Clyybber 
Date: Wed, 20 May 2020 12:54:04 +0200
Subject: [PATCH 0079/3768] Small improvements for string and char repr with
 gc:arc (#14400)

* Small improvements for string and char repr with gc:arc

* Fix test
---
 lib/system/repr_v2.nim          | 14 ++++++++------
 tests/arc/trepr.nim             |  4 ++--
 tests/destructor/tfinalizer.nim |  2 +-
 3 files changed, 11 insertions(+), 9 deletions(-)

diff --git a/lib/system/repr_v2.nim b/lib/system/repr_v2.nim
index 407a06844..be1a4cf3a 100644
--- a/lib/system/repr_v2.nim
+++ b/lib/system/repr_v2.nim
@@ -27,21 +27,23 @@ proc repr*(x: bool): string {.magic: "BoolToStr", noSideEffect.}
   ## repr for a boolean argument. Returns `x`
   ## converted to the string "false" or "true".
 
-proc repr*(x: char): string {.magic: "CharToStr", noSideEffect.}
+proc repr*(x: char): string {.noSideEffect.} =
   ## repr for a character argument. Returns `x`
   ## converted to a string.
   ##
   ## .. code-block:: Nim
   ##   assert repr('c') == "c"
+  '\'' & $x & '\''
 
-proc repr*(x: cstring): string {.magic: "CStrToStr", noSideEffect.}
+proc repr*(x: cstring): string {.noSideEffect.} =
   ## repr for a CString argument. Returns `x`
-  ## converted to a string.
+  ## converted to a quoted string.
+  '"' & $x & '"'
 
-proc repr*(x: string): string {.magic: "StrToStr", noSideEffect.}
+proc repr*(x: string): string {.noSideEffect.} =
   ## repr for a string argument. Returns `x`
-  ## as it is. This operator is useful for generic code, so
-  ## that ``$expr`` also works if ``expr`` is already a string.
+  ## but quoted.
+  '"' & x & '"'
 
 proc repr*[Enum: enum](x: Enum): string {.magic: "EnumToStr", noSideEffect.}
   ## repr for an enumeration argument. This works for
diff --git a/tests/arc/trepr.nim b/tests/arc/trepr.nim
index e24c112ac..a870b40b7 100644
--- a/tests/arc/trepr.nim
+++ b/tests/arc/trepr.nim
@@ -7,8 +7,8 @@ nil
   output: '''
 nil
 2
-Obj(member: ref @[hello])
-ref (member: ref @[hello])
+Obj(member: ref @["hello"])
+ref (member: ref @["hello"])
 '''
 """
 import tables
diff --git a/tests/destructor/tfinalizer.nim b/tests/destructor/tfinalizer.nim
index 02a5cef7e..eb2cd09af 100644
--- a/tests/destructor/tfinalizer.nim
+++ b/tests/destructor/tfinalizer.nim
@@ -1,6 +1,6 @@
 discard """
   cmd: "nim c --gc:arc $file"
-  output: '''Foo(field: Dick Laurent, k: ka, x: 0.0)
+  output: '''Foo(field: "Dick Laurent", k: ka, x: 0.0)
 Nobody is dead
 Dick Laurent is dead'''
 """

From 6969a468ceaab7384d7448bfd88e47a5b24c3a97 Mon Sep 17 00:00:00 2001
From: Clyybber 
Date: Wed, 20 May 2020 13:04:52 +0200
Subject: [PATCH 0080/3768] Remove #PRTEMP leftover comment

---
 compiler/docgen.nim | 1 -
 1 file changed, 1 deletion(-)

diff --git a/compiler/docgen.nim b/compiler/docgen.nim
index 8b5201029..a67dc48ec 100644
--- a/compiler/docgen.nim
+++ b/compiler/docgen.nim
@@ -456,7 +456,6 @@ proc writeExample(d: PDoc; ex: PNode, rdoccmd: string) =
   let outputDir = d.exampleOutputDir
   createDir(outputDir)
   inc d.exampleCounter
-  # PRTEMP
   let outp = outputDir / RelativeFile(extractFilename(d.filename.changeFileExt"" &
       "_examples" & $d.exampleCounter & ".nim"))
   #let nimcache = outp.changeFileExt"" & "_nimcache"

From e600ddc555201f29576397d560aab5350456c650 Mon Sep 17 00:00:00 2001
From: Timothee Cour 
Date: Wed, 20 May 2020 18:34:14 -0700
Subject: [PATCH 0081/3768] fix comment from
 https://github.com/nim-lang/Nim/commit/e909486e5cde5a4a77cd6f21b42fc9ab38ec2ae6#r39287564
 (#14412)

---
 compiler/rodimpl.nim | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/compiler/rodimpl.nim b/compiler/rodimpl.nim
index 417241930..52b4d1f85 100644
--- a/compiler/rodimpl.nim
+++ b/compiler/rodimpl.nim
@@ -916,8 +916,8 @@ proc loadNode*(g: ModuleGraph; module: PSym): PNode =
   replay(g, module, result)
 
 proc setupModuleCache*(g: ModuleGraph) =
-  ## historical note: there used to be a `rodfiles` dir with special tests
-  ## for incremental compilation via symbol files. This was likely replaced by ic.
+  # historical note: there used to be a `rodfiles` dir with special tests
+  # for incremental compilation via symbol files. This was likely replaced by ic.
   if g.config.symbolFiles == disabledSf: return
   g.recordStmt = recordStmt
   let dbfile = getNimcacheDir(g.config) / RelativeFile"rodfiles.db"

From 5caaa4bf6fd95cf8252dd86669b67d02aec903db Mon Sep 17 00:00:00 2001
From: Timothee Cour 
Date: Thu, 21 May 2020 02:01:43 -0700
Subject: [PATCH 0082/3768] fix #14404 foldr had the classic multiple
 evaluation bug (#14413)

---
 lib/pure/collections/sequtils.nim | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim
index afb225884..a89d2c572 100644
--- a/lib/pure/collections/sequtils.nim
+++ b/lib/pure/collections/sequtils.nim
@@ -843,8 +843,7 @@ template foldl*(sequence, operation, first): untyped =
       digits = foldl(numbers, a & (chr(b + ord('0'))), "")
     assert digits == "0815"
 
-  var result: typeof(first)
-  result = first
+  var result: typeof(first) = first
   for x in items(sequence):
     let
       a {.inject.} = result
@@ -883,11 +882,11 @@ template foldr*(sequence, operation: untyped): untyped =
     assert multiplication == 495, "Multiplication is (5*(9*(11)))"
     assert concatenation == "nimiscool"
 
-  let s = sequence
-  assert s.len > 0, "Can't fold empty sequences"
-  var result: typeof(s[0])
-  result = sequence[s.len - 1]
-  for i in countdown(s.len - 2, 0):
+  let s = sequence # xxx inefficient, use {.evalonce.} pending #13750
+  let n = s.len
+  assert n > 0, "Can't fold empty sequences"
+  var result = s[n - 1]
+  for i in countdown(n - 2, 0):
     let
       a {.inject.} = s[i]
       b {.inject.} = result
@@ -1436,6 +1435,7 @@ when isMainModule:
     assert subtraction == 7, "Subtraction is (5-(9-(11)))"
     assert multiplication == 495, "Multiplication is (5*(9*(11)))"
     assert concatenation == "nimiscool"
+    doAssert toSeq(1..3).foldr(a + b) == 6 # issue #14404
 
   block: # mapIt + applyIt test
     counter = 0

From f71f8b0239e3d6aff75390075bba13a62c292422 Mon Sep 17 00:00:00 2001
From: Mamy Ratsimbazafy 
Date: Thu, 21 May 2020 17:52:40 +0200
Subject: [PATCH 0083/3768] The whole options module should be inline (#14417)
 [backport:1.2]

* The whole options module should be inline

* Use inline per proc and tag `lent` where appropriate

* Remove lent annotation (failing at compiletime)
---
 build.out            | Bin 0 -> 430264 bytes
 lib/pure/options.nim |  29 ++++++++++++++---------------
 2 files changed, 14 insertions(+), 15 deletions(-)
 create mode 100755 build.out

diff --git a/build.out b/build.out
new file mode 100755
index 0000000000000000000000000000000000000000..9edb996b0c494271d8d4f910ce04ee6ae72f2b6d
GIT binary patch
literal 430264
zcmeFad3;n=(l_2g8qoneE@;$I<2HgTDyXPPTv{6yjSB7xiXe=rh>4~
z;+}CM;~Ez<`Ur$&(otu$-Nv{_1idk+(LsqTzwfu|oV#>)BJcCp@BMsUXGq_BYB^PP
z>eQ*T+UxT*+eo0qv;JLqlHhA{eO2}W*b@$)t{S{(=L&bVex-c)zeeUmq)0C_I
zwQ#q-sixMP-_|xV4TbElpN_T}caGE=8h3Hv%4ujt()?$Zp!yWc`5$o{QLg0aBn@8uO2Tv}NnjHg}|x^P0pgiEeqA#pFRyo5DSyh|<(O}uj27J%NI;X8eHHrPCl90_k{Tnv2hyd`W1c
z1)$E!(;zGmE@ogofLGHhc)2exFPNg?mJAer%<&_SICAWMd+$3WWG}s|eGhaC_uG3Y
zU8k4)Pk%SSb#GIm)&v
zz&8lpW$r7tnYvd$Do>N};4=s=9lsB33
zbWZt-rxbr)PWdWRUYAq8+LSlsl&>-6jXCANoARcd^8O84Z{M7LX75`s)&9+~Q8C4A
zp*COnCR!wa9bft8zVc3AIb*KC%=D(7u
zD*uMKH%%^(QeU~v1qc}EE8jpc_0M2mxpLI?nZ5{sG^#k5Bq_$_}{w>}!l(sj?{j0oZ2yJhW`=z|+5Yk>J
z_b>6Dp|m|M_s{U2A+$X%_mA?Pp|gFe+&{p3hRpVHa(@@^87kY$<^C4lGeowR$^8wy
zXJ~96Ece&)o*}WlRPL|lJwsu8vD{DNJ%_UPklg>3_Y8^coxh`C*YkPLP}trk_h<5+
zA+S9w_b2n7p|8D3?vLd?hrsp*xj%yU41w)+a(^)I8T#7Oa=#z%8S>iWa=$z88S2`n
z%Kc8fXNYSbC->X%o}sP19QSO`eZL-=c=Oy?VnwWaWoP;5<5S`1$Mg=xQvFBB&+w;L
z_X@2WumUR#YtpgQ@X25hONFUdY*<&t0Jy>;z~~Aat!sfSz
z9!qq_mV7fJwxnx=SW$EA&EG4w0E4-PLEm+46C*nu<wDZ|{QW9u{kcLNZ4p=_t>rlQ1B0Vc|KiX{hBlK{A}M6v#a
zFEb^?o;Wr_yqxG@mn7Y`YENW$-JDg&svcz!P%M>>i-oQt|$t;RG&l1gBAtJ<#*2RV`joi{y8(r)?3Hr$M(Z#uf
z{{D+k&~n(cG2fn_DOcL_OItxfAwT#{?3rDz(!6AmX`nmd+6VV3edKM(34X_AXW((`j
z@BQ@0l5XQk+H{8T!-A4Ni)Nmq_xnF
zw|Es62nqvKIsSD5720|4gRGD%w_>dRZlaSXN>b5MQWz
z2n>>A8e)kfN@CSjjiCz0PIL`*6PjdSHA&~`wjW0u4sX=L{vWb1HQ=A}GjtQFFyky~
zL2t^!k@BpvbC<8py6M`jS`n@Ck*%{}o2`S1DIkYUAU|c1jr<%)&G^twP0PHRrdi^G
zHO)0Oaqfzmq88cIw5K-`4MwtvFJb9aO4U
zm&zEHsO$u#0}A{b)d>oWO+s-uD)F$3UF_p-LnQt~
zu^+-JFtMg7);Jdn6(bW7)kd`iD*lxzCrD3Zc;v83DRYPUt=9Nr!8rJHeSRBv2qS4@
zM=!f+<6~7tiI5c5wDGf+2;=-{wDEpxjGRCjr~0HVN`4zxT2O&DKI_wf#*czKHRgzi
zK12~r8xOWj*if9|5N*34^=d=CgN~QBjen5hoHkzZNuc7(4CzWUfSERa$&%)@@dLIz
z*v3s;1={!;7t7bilYR*7#++x_y6A4#)H>0Hr@v!bj1HVV&mawbrTy0
z0#GP67LyLW5!
zjf6nf<5-W#=EDDr
zDaQq0|8dLg<4O8%xv@e)Npq$g^&YY-A%}|fpK>&>&XulkZ@XV>qAimr_9Wq^I;N;N
z(BV-G_aHjeNA#o?9chbfk1NV@mf;*m4)pt+wTNx~qIT2Yeo>wj^G3_QpdtT?nFa@3
z$_eDL7Fp``X4Rid9x?`Vn_kW%g(fH0b*!KLCVPVY_k^D8_okUPe5-;rTjc7hx=_Vm
zAhM<_Toge=|H3iU&gF3biI*;RDuWu!Ksztj>IQ*}=Ppd1GtM*02u9b5+%sqdlapX2
zizYfWzzA_XQS96bVbdT9tbSbnRjx_$L(%!ljO}!RISWdrW&S~-3%djaC!(M-l+oi
zGsv0fZK6UlTFZu>>#SRsh~8FRbz3Mh`*`+5kldvt?;r1a<~*UvMCbi8b`V_314}_Dp
zVu(QdRAoaI2LUt7`FBmzReNd?+m}VQDPO`&n*Ysod(v}XLDNjs5sih7p(*JI|Bsph
z9AMI2Ko(0Z0Yg;zfmw-IzNc!KV;Q~W1*w5fU)R1G&CV%uQ>#k4mn&&nB?*6c6m7ml
z_-W@!4S$?&@cl}PBE;`2apy5sO%of#_X%;vxk7MnV?`e-?s9`0ONOs9Nc(<{>FXVs
zal*Hvj(_F{b|yC6&=`m*yi7$l=FBe(?C!e3kQM^VXyqc&=3dfx$5@c-ZQ>}2?}yCA&wPd-hk%McRA~RN!Dhf-v}!KJABPyOoWtA2+!E4tU?
zS)hfEds|w_jsvdh90MWHI7^%gSB_#7Q|;RodW3~q*P|5>Vka2oq~VI9_0Dgaw?hA*
z!+rLNUky53zA8I;s+h}e!cqJ!QN>k(2Oglp*WL^Y?yQMav4Y<6K(H{54n!L(Mz94p
zRroUjcp0;f27-As0-SFRfTe47JePKgZIYg&?3$1NSlMc2$((wq<&FwbU(VXmt#1aE
z<>?C$g*>7LbaF`x&5qJ4JDrgrtajj;3{bu5fm}P3QPC2GKN^6Sl~_CQ$Og!hK`=(j
zXk*8-X=g*7FE`}2ddEjTLzJV{3#?CFuRLjpLwlTD+yLWH)N3^4MJ%$!jClP(K;e;h
zNm_y_v`Z`Axnp_Xc(*S|8A>VgDS81rlmR7KW&AI#yOlG(T
z%z-;FXA1vv(S
z_^o1AhWh!4%*xPDnt->$qY=xZbuqItv>ilMdj-RE(RzydrB+2zzwp%xqvUk^d;aa&
z%$!nL;3F_g#t+ycE2eu&r2$xd$b#*DYj(A4DdES?5)q4(A1rCtGtRE!_G;JZXQ?gtnVuV`PrBM{3U6Y>Cj!
zk>|6J3oYIovp6>^=0NH;3$i@~L?a$~Dr$8Z_;k5=3>1GqYn
zJRu0jD8ev{U{=|j$8}`@)(JXX`aPd@rXpHkj#8PQei1v_jnZ1z7F6j%CKY
zV-$Y4rKAy;boJ4yuwJvYS1h>&8>O+jZ3L!hZ-1rJGcF>`#nQr49VKGF)0EFoN9&3S
zHF2h5a*RIkRIcqt|Ho&$ZX)e>+7Ptc!-gl-$?vHDeMS0!8)@%TL8e+Fy3>-be$?0b
z^uIQG1Owf&cXI_Q?CKLep^7d4f+n6kblXv)8<h6?M&)M
zs^jydD(rUKdy;*2WKY)mvN_m*|5524_Yr-dMR(dF5qMOcuE?zY4xL*XcL7^j8n=>3
z(hz%k?#{Pz4Ov*^OVnp*%o)};1&uibBHIRjR)?=qK7s{Wbg(V*jkCAQY0!QUw9q))
z`KC|M9^&&W%9(=hWo=SW&?HvQ(0ARjb|;mr>j={zzSZNM74)$OvdMmAHscfn*)EFn
zqQPm7KB?V4iOElLOxncwTM-;;qfbJ?Cv}zf&)O*ECHS>SeK4W}Nmp|Xo;Us-C*gTl
zt?pR2It(<@yn*UoMk;`i9(jHYVm1bX@o(9YxcxxPanjCx*mDIWZ#29%>sou(YIb8R
zupY!&QDVVh{Nx9KGu75KdvjS&F1^Ka+N&l8E?_IBi|d@?CPUX;a|D60hdKQ`8DP^1S?r6(cp5lLcB~ma;n1ijb@k9tg|-0A(yuQ%;sZyjBsnaqB!%>r@2b
z_rnydwuYtgg#wGz^fHxHEA1yERL|y`qjG335L%W(O8yuryVzGz`D|ryuVDZcMXW^4
z>A)pIjUrs*AyAE7*C7TiDwEd8ICHQG4TZ)TlIxXZq$^Yu02onuCd&uoQgAidcmP9r
zZ4GfCB7cmCBaMhRD7&8z^V%}0MPK@g4AwhtQOzXQN(oKH)lFrRdYHOp4E!5M?S!CG
zS;KdZ$7E^_lVo|{p!9cYV-gR(G~Gp?D<7Bea%4tU5y5cnR=UR
zjwT=UhB!{gVk+Le7`DKU1Ej4L7%W8;?YP`=&8C#aJHy?W5l3D0?e>pAn{NM){ACA{
zUYl0@hg^Kq@Yu%#x0@AuoeM>AbyF$gw-Dc=;0q17xu(p>o5rRB{Ud@1Duob~Fcow=
zXp|0U^auueMU)WZ!xg1&)q2()>MDi6q)KrWGTr`^{QU?2QtaCm{|y)4^f|2k1A2!-
zpEl6un!{CU?bTR1N{+D!@D-Rl*9sg85KR@p0~!XIZs6|v#awLF!X=2*YzWa<_gz}|
zP*1Xhz%`cC#SO3GM>uKM{m<;(N>gNMxWVZ~`Fj-l`5~@t>R{Sp5Oa$(d#{3*Td*Bo
zI4C2g?8=#$-QYI)`u$3Bvmt4&IY#XR3cnYA-$~mL2HWsQZNt+8**ax$f~{3*lLqV9
zFx}BR4f{9?u;U0j(lv8a&f8ygV}14N^1_QsX>~e=_xG%2P&52GXPP|}6KTpM!(uA@
zs702s$vcEG9S5z@@|dvpa<+kJaM0l5O_5)yaa_D9oR|Er6;Lp9+0A72o`oWqJFWN=
zL-*UfTRRWfC1-wNCoNkC_RXxtgdZnzB`&WH{W&=OTj31MGNpZ3
zr9Iw5u15)D`8=#nqvy%OWX?H;YDYIJISF2YJb6(Fc3?NXj!T}ln^rqTbOF}#h}QDO
zfv!!!y;%H0JO**Bn0WIQZ&3lfwb|H?+|RJ66cbwB8bA#(c^?rQE`K8@(7YD^ABsOd
zh!2^k>oP4OwLB~IsNx+Gz@w;X*aM1F=bt$@NR_vtwAM)en3DDO$cPW5pM@01gD%Eq
zD$0VLb#$}WwyO(wgGTi`Ht8b=^wbU3;A9Pq=yn5h^yM-`1Q%S}x-Gqy{eR`B{bdG#%D=78mP}eCiBGA(Hu~|!dW4}2$
zI^7$$;)qKc5r5ak-fr75n}|L~U*X{hkl$j-eVUt9$}0`1y6U-5#RSqIl9A?I14u@n
zgEeO8(mNbQ9Kft+9&Gt?aYDp`92`ZlhO#0YD|!_|Zo<(rc~llDPqeZ912C6>(WJF>
z4RIB#Pc{@IqJSE_LfGkliQ3rivtuWu=u$VVGg2&39?y9^NQvqFj7lo9RKart;DGIp
zO7{lW2b8PTZJCmtn@9Esx^WzQ0vokZpfQ#VhNqjC`Fy1mzQU6?ov4V;*d@Q_jLvDm
zgr&N`r3=5E`0aF`r&LNj_hX5wtJ*>pn*yhek>wt?exkV}%;te1`E(BbDkHyvSok(`6%o0$?$o5
zVy_b0v44ss!((v0E(@!X$>EB+wTC)mXP~Brj+By&^D`X~h({=5r|~>ZqW6RJ<{_+c
z?1=fYZ|ccD)H-cJ+26(z0_t%CM4Q$ZvPAIh!_h}Wp~I!Ky~ug}OURHy%;;I}ELb8f
zC^3Bn0jcm(<4dx}#}#QgQ1wE*>V;5cf1H(RT&D$+YDQns_XaB$!|7h}T;VAXW*wq`U+EB5&!99%v-(cy8*2S?hHqYf2Sqz$vm!ed1
z4vwXbv4oU3Z+={0Y-(KW6Kya%cA|1+?o)ZmQ7gg7(dXhG0R=m_*VT$ahQ5wx@{(iN
z-@CN?qC-D~4&4KuS@l^Ez~V?W58MiDE(}rA(?0Qida}>!&xn0A%YmJKQac@Ih?=AG
zX&}VI_;lLJ91xrzubLmK#F;V=f@nM}nvUF$wYT}|-1Eh`BQqSB
znf%GD6Xy#HklB$}TVoF{2NtuYzFO05g-BvL*|k-H2BUQXQ((|-q;zMybbxY9*Cmfj
zU^{2|2hvqBiwsU#>R)#I!>rW}Y`Mh3N$^js?2VJ3xu@eNKR*9myC7Y2)t)`M=Kf$x
z8FyD18!fU~l?jIiu})or*~92>aGGmoqHdl|jEA@xCAXOGvQ$IvmQXX!6r#FoJ36g^
z#BY5_9BzRWcgP(zb#S>W;fdlXWzZ{Uy?yC$Abs11ldG%HM-=UP4V}Hdd@o-=>+Dn{yh(dqf
z-POs6(BO&Ch$=zDT(Yek%X<+h~2Pp<3I=>{RLzVfxE^}>DeP}t3zTxy~EfC7T
ze$jPZ^X7_tH@!jIk8XOHWe$gfiNjt2Je}j7JMBhA4>11_~qSHa2RsknjI$actJRg5$U??=ZDN?f|5rFuNB8~_m
zqCdgC4vzi`tbp=X@!(w;Dl6*a|%X?f%X{bj(_)xSiV^-?e%2S)pz
zYikSe$eTO+sZD@?Ss85SG2rqkL}KV;4GR_F*WH4uCsoQ}f;ZzBVlr!oEN@v%|7x`9
zsu`yj&m82eK}*}VGkX*ZHYuMQTt3mbF`pRS)QpT{sqkWl!9Q<5V~NrqYw4>OmP40N
z<=GT+sRH-1Krgn5!r(fN$KDeNX%g0SRT_lLlyJ>o0(NK?mn-TRB$EGAak4=ixSKSW
zXVZ-0)w_5y(73HD+dD5&In>*txYxM2+K9Q_B#O9idZCoh_;VVjbq)TWj!O9*u
zTXcE-!B1UU*V{@`?2;Jm%ZjA|!1Nu(_-0qv3c@W@#>_%I@?FLFR}Mx}Yd3@Tdx|u_
z8zeEnIF8z8Sb1OZuXOP_kYEUaL}t_XYzxXn>qHbLc4*D_e-|39R1kVT3xq
z@&<xWriK>I_U{>*w*8bBjR%s4CA#Tq9$@sOVLh`N%5`3ZrEiQp+vv^%8
zjD#uWR#uLr7U|wqM?ZLx><-ED8yJ16jAnX_wDIQZ2qbp81>4qksQOItPjvB_2x(@z
zLP0|esJZ3@v4;)&oI=1{kLvP2hBFn}wG+lGvC|hgyjLr`5zf1%Wy(T6yOw5
zz?zVFhzHOtVf-*%
zho*2I&BAu3nw;b?`HVNhE=PFjX_5AE?Jyk6-r33M1QF>gUi%|WvSi1eOcKF@
zHH1_BAtYbu4Wb(@5x1te%BJQR*=o!I%;OaEe2dA0VUe18+7VEB_z&%Gp`N4HFDfSv
zy!{FsUV(9GIVi6EWR!C4XX#*6J-JtB4vV3m6tjX86!G&?>%>09Q9$aKCDw_Gwa~*t
zH_=R$b{_OP9gtloI5p|tu>Ph#M!PgkeX6YnHPfMj-uKXhYML`ura=~&eq)c|je?pC
z8Zs_y%5^Mbm$Q^;%?_Trfx~uZEBqY~o`2YGNS2nyVLPTRA+uWgrdZ#c%iHKXu?COk
z!GO-j|1RK$-#zKsX89lV%)#3IX`6$sWCd+g`+(S{j#gv4ZLWPK>=DtiOb;D{-Xi_%
zWt?(?fiSa_KwC%3EtK-L?Oj8mPa`nyVfz?-S*NZgiurdB6AZkuc54M+8w9%p?Y0UY
z?*}I-nRw?83XKM!oRf@#G#nbGfw#ly49Z-p*jstnz#=adbVmhuZRd7m8?K;>bQ@iQ
zG=s=X+8W
z@cD*1RE|e(+%v*Mcvb`|n*&+9SZcH&BEq4cOM^BL0}G0bM7=zW25c8WlX`c{sM;xo
z0c7>moaTDx&u+8hgsIbq_HmBH4zBpMSRuIDzBBeR*Jr&juDz8SPqlSGNXVGe%NN}b
zu>rY9#Xr&_qj7YHuDWdpSzxCYU*NTs8!_-VV5WHwws7ekfky|bAM?q_pby_Ev|jh6
z_7uvO-PZM!0`hHdpAU>33atsgPBijdEZputyawQp_XdO%Orl2Q)C
zm6w>Q*ea#JrDyuo4Z2CiRg%#yvIJ(9y3gtbQVG@f>*6s
zSNu}z-NqwFuT8t#TN>%kVvqe>rhtQ-|BoC80sro@g15CV(McZLx+f?33EoSa?xK-r
zhDDZaATUO)u^B6{cUA0i51T`~)%PH((1`unJRiTm-lsx2d>o!uXw7~HPyE#-J&Av?
z5g)G35VF`JciRWoW@Rs&y>QDy68<*dC*e3NAyO|WH>$wPRcC0gr=gFqmBT=EhG0&R
zzFQ6bo=Q5vA~TdH(IeRFlA}<|>#-$Ypzpx;Mc>6{EfFWQ4%n*1ty{UC4X=PzY{b?yRqjLLR}}NpEq#i+UB~*}L-0G(#XnN~r#yU&G~V3#V+E&!
z;9``9oOReX%yZl?9`@W~c^qz03&)M;50}?z-=;h4Z<#}UG7h@KtUJXWG}i&f@!F$)
zAK)s8S`aKV(Rn1sZ=Lzgo}j32ThtUHZfX=l31h2_`Wt_&CNEdaf8=7yf{S3D@~I5N
z66{Y>?7vxTKCK_AS*x{YPgdwT7RpFIeBHVYLo-LSflg8IVIH^^8&-Wo9A!?#%Wdq4
zGFtJrbn&>00Jrx<+7_u&$4qLTJynt0x3ET$EeDuh9IFd2h_P%>SIosOCW4Wj-wj(j
zLwx8A#kt$X$*Z~!YZ&b}K;d-zZ2qMTXDQVLmx@7=Q>98HbjEqzBzI>k>cK85Oq5FV
z0IdW^=BC4zR_#fy=O}8#Ma`3C3&>)ed8{I@*xZ@{%IV5FS)H%Y*L+9WZ9w2vDc*e^9+R8~
zXHyj#F*taR=ZC-hgzjOaG7(1NzvW#}@H7s?QD8ZboeB@I$P)7y{u^v!^pj5T{=2E$
z+-NLW&DwjDnmUlsQ|9+dQ
zi!a_V#Q()2i@MzZZKxbWZHg4$$AhCO%y6g^tPK=nWpSQ4xaTIOF_FFF{B*}y>9l>6
zrop2D*Rm2cGJa1kwwB&d(Go$lVkVm0^dn8Np(z24HEv%eJK0a>n@(<|;QfPOnNB(n
zEG%IB#Qi>Bo7#Qmf2u|c8vj3$o;t}cII9Wc*4a)nQ6>7jN94`15ue|~B9;VF4HXL0=s^E@I
z+=d5p*-#|$RE7TALTyZtC9M3gcQDQnLPv(ZZl=mzn6Em$UX~b!5FeZ>e{rv`iN>Lc
z3Y;cdu3ehVbD$?xff!bEvouemZJvJGS$kO`Hbb~|Adt+Ix4$BM*59>+i3pW4ACeRi
z=ZOMfo_Z*zp(0BBP#(0rFBis&|3@?i!=T1%?(sBUj$EXtW!ZPM_fqrWwi?t-{esau
znHJ66tYJ(w%MTWTZo{Fw@y>6g)f##^6|9Lh5Z5yuAmW
z$JFT2-;7%V<dpwhVY#>@9moF{W}U>yz;+7j;HukqREdzo1FW*><7c|yjrCgRnX=25`3Vk>Ss>!gm
z)_#f<&qcy$M`Ni=?XO5@M9;|8a!ZuM&a3;LA=lY^D)Ktt9XT?FQ)4#{HB%VorT1OtCjWYcb^vAhSaxJkKXz9!iSDSBDn}^Iazi7}z1k`zU9nZ}FI6t)gRNTS5LS7l
zlGo%^nVJfpLsqZ5KvE~)3uI;>M=Mdep9oSdF%zF-6ufT^_|;!AqBcBNpxy7d-EH?-
zt1%`Z=yvxmJ$=jBP1>i}@E`W+so@7<`UW*?!BaX*r=|&ySR#GRox4aK`Ip4sI$Gwx
ziuyN;I;*Nq9>AV6dIRP4gCd-15oq;Pc?3gMED~Faot@rxtoTte2fLW&S->&%`Z5H0
zGljI~TE*;RF{`W6m~_V&%^^~kLY=Vn5A!e~zSF`Mvh++>g=ppq3o^D~I8h0|iZ%gA%5aCv|A5!5v47
zI1xoI4j@Auo`wOh%@q9I20oQ2t%NP|DJ9nCiuGC$YhZ!K*-A10o{KpwS~ue`vf5e^
z;vT{u+y4#h>6-Acy^J`SusBE
zWvqdIiDn%!1COy|<;y-_D^a#ld&N9+j$s$2{D-B?!{0+5)J4(>8|IB#=fE;)BPHA!5{*IF4%?V((@vRpFenF3i`fi^l=ypFLF
z+9M~&)}T=^)gW)jN{&LQ8wJd3jk9|y+omGxFgXo?aSSKw_xQQ4B8^&%%28W2EXlwV%=>S}avE&hKnQL9oXT5li8PWHLl%rY(voeu+t5Jl
z1&X~QWVPe;uBcVPO3okliOkBNg
zgAnRzMLh^8Xhn>>Bk{^jYJqJDcmsc<=m?@&Jq`A6C4D+b>aJ^$4{Xg0=j0MZ6T7Z~
z7i+P`tAk`E2$HfKLb4J=Rtz$Gbo~t_JJKVYCq2{Ml)%HS!A%LC{;y#UMPX?I)Y{u4
zM@1%g;x350Ap$me8zOHiaprf|NPko}9!scsl2oYL>}VuB>h;>sJ9HJy5AwIP@?$MI
zip-A2fwo8{wCLA?9gU$RenpA<`!Q3wn}Kg()xv$AnpAHq)qB6W`eHos_BGy7aAOc$
zjM9*^8}^rB+2E%0CMlo0kFyDQ{=UZk8nEcSXIbVDhm3%1@U^Pw&e*4vECWnk>VO6&hYN22kI23cf!WUaO7>?JX$88b&NA63h
z3Z9e6#@1%}B4HoFj)BuMoX)(D&DcC^HYHtay3aYBE@Qxga{T0*^)rnGG;41wXF;=`
zJX7s3Q1htwezEOuPVVV}XOQl$2+w;6B7l2}RUW%=2LlyOPq>134<)|JBUW@g`p|~&
zZt~!tKDJRzU))QvkITn4=d6LVx8m$(ajF+`bjQmL_`xbN*+&ujScJe?X02Ucu%W}k
zCNO&}=TvhvPDcAGqlG`KWtwZwXGI7^aY(It%D{3K<|DNN#JNi1=-de35CtE^)>@1d
z3P&^hQ6e>Rm=IO%uT`DwRyCegHP=kk8kNsG!iPm*!XJ6p87hlBj}x1uh(FA~xR*N>
zV`~wE?K2#(!}#(ifvPOT7raQ#YNTh#cvD*aa`0$&E7_wBUGG_
z5hq{ed-xU;$bFOZq4mo1RL`rzHVhwe%y7S40{v#eBE1btDQOG4?UgwWmtKh%^e~T%_
z>pxgS6!78(5IKV%EHF@QRpSm+=JhLu&*7Y)A&%qI1-2&W$W9*|_Me%qekDmA_CG8#
z$Izs#zN39$P#HLO(~)_z01|fQbeZ0nf&JpVp>@J+pTtvbqEh%s^n8sgl=?nfM?tB7
zy;7w8>|*i1M7j}kJ-Wg1inP5!3aruv76wxo
z(E^{(m6xrIQu0sN*ydGPxa
zpInGV({?_FBaM2RlDzl5>K0gEMkB~dyVg`zic_#f+*P>;rdkxQS`?~0luDegEN(L_
zwBWxUQMlQ)!usK8Oke`%gf_a{gtfvv*I%qdE4%hMFt3tQ!#Qv=PadY|1P&B
zjf#-?
z8>^Vd`Z2-ibHiwyg7@%)z3LVMt3%(5b??0e0^IDGz5rK_0M}SJB)_gbk-_avd^iHR
zLn}>zx^qJE4wq{r`dY<$$RcY}WE491Q;N!mO$>2|3rlMW0L-YO?*-
zm<>Q_gl=pcSUBU%S_bEH4=&`s+V_Y~bEf?#&p~wZj%z&fn_^w5Sa$}oP_0<&
zRD^t|2z$b5kOq#XJJ)!vxI7M1)xuN-PCuThU7e;HYU?Q2iq%uqREOx&U0;iA=3tlM
z5H569afgf?ks3L*f*KD~q6R~x1kpO0e}sbO
zdLRTB^mUVimnrxP4_s@XV%~#(MVE*6y0Q&+QN=&n!-svO6|*B1w1)>madLp4S0gdtpA^+;MW4+vVj;b>t|d^`co9)Q4gU$cfTsI
z$pXb>3Rq_n-``u+ZAL4f^F2P4bRaB&B{>=d1my$US3xrg%cm;-em&z;!qXJ9ZvYc2
z=$nsgZec$F+Ubh+*;lS1{?8QIh0{OqIYar}>G6R}phb$cnllxAwF~C<0$W?U$FARC
zV8*wKaX55SJ3ODK6fu{Aj){~Pb)B!Ey*v;c$!>@sDTd1p%5|m*O7l~@t0yYQOUFKh
z`0|vHn{P~1%r`vDM%}DXmUtG337-frdEzoYYYnL`RJuF-bQDD<)-wj67KrAm{3*|%
z?j~(INeRz&2?K~5@$6JHHg)qt#}m`N(4l8c5IPE+qu*V_Aw$Q{j!_@-vadavaf*Zo+xNP9e09bp4sK~>MWNLAgRdZogfS!DJ*Sy13FhwjLn
zHADdub0p(SFQ_Og-nxhP9Ps-W*r)z#1h3wN@bng_uJh!fTFqmY)*G0>13t2(Z3B8^
z+ZXvpJwQpPT2he^GTSf3QP3Du@&*(U%fEgpsv2`YA@MPj#Mi8XBwa#d;HgX3&>CprjBjYN0uSd!uSm
zDvW+oMss-we&lYHDZYy=2?tC57?!i7RTPsGS7RLs5x+<>RWlG=jOoa55mpDJ=FPNb
zf__~BPq2hf+~!QTzQOY`6h0&qBX4*$6U_~1Nv
zWdMFq9(;@kUsPsneB~XUjq72ibaTxwYS_7A*nn%Cb3a{!a2S<8+G!89rq304B}R_i
zM(yKO*l{acG|kTH|Ip)2m-%AKIF1>)ofPk!`BKJ1ju&ry-qX;y*93MqZ;FJp2TV#pY7-~!gU8mrL2ZmTk53vqCJ4xj*;yt1`6I`6|2cO`a0NxZDON8Ix
zC6MMTQkg~a=2#g0GR`$CedVs`hkk+eH>xBo9^Nome=EQcj_-
zQjB|o7_sUWoKmBD4Q7^cwc=H{c$i^`@)GU#T8kkC+O^+UK)dWEx!UD>Z_`RwvrTOk
z1vR_$eAR5Z%CoY~IFQJ~V;3)dge>c6G?jC9Az_a63)78*4)=taZTS}zX2p3b%quF)
zX%<;iibXbR&x9c8C~zW4EM8UYK?d6_`Z5V|DegmN5m@fsk!|&w5_W#->MVZw0F&$Uju*k^pvw{T`%uHB#EBfG-zFR1f3<3wkT5x_9ZU3Xg|(KCy5su$BocCekJ
zb?i3IziyD2f&LRbN7OK7{X|@#RZzs=KI%!t<8nk?sL6bbB?>P89Kv~%nT{<|%=?0v
z=#Ap#4UfaC`gEhkR~PxTC`BK3&}3a`F^tIeNYEu8z&AI?V2%2^P#ioGgvP9dr3$^i
zWr_9)s=i;3-J=}ML07@_haod;k>!L5L#F2PNnGxjwOU`S$gQ6HHu{os98AnI{lfX4
z!DM}@Vn6C(!*yi^+uG?SW(BZl#{!uYV!?~DT;&nuu%}G(&^eq(YYTYr&42X_yF3oV
z7I-cDGs>Ix+00f`(0d1C9hXW}s7$N8u2gH*cj^ePRB&@HnC^@jlD>_{qWFabco
z78Pwgz>i>jdb)ys{=|(MkgB`{65yl@CTni%${9~H9{j5(>|*p_Mt>MUmJRqMKLQZt
zDA&q{S~;qt^NQL>F;gYf3?i2Xn3t7saDBxrQT|OC93McD)vOXHd0hebm^LZbJo7n*W%%*Bre+=)I|a=Ds-Qt`Q7dB6~>p$E)rSRqiV8z_6n8
zV^oP?ljY;T)m)kP^|Ai%m?;742$^EOH>Pgw*#d
zXsicnh%IsO0Yx39)LVDt9L>bCIwjcKMN+K%n#+Nd*GX~tO^qjuVPATBD-MU+6^Ci#d_GYsc{e_C0j5qYl}_&gE6P%<
ztbA@St1r(d>x({=^6dbNn2=&X&d#44_Xv#-nuXdK9paSlN0
z18?kt1y*7}tr&V_Xl}hx2B??ZopYsLCFBCK7Vn*xGsyO2lZM^)i
zG~JhqFeQK>yUG4piy87AMIK?1nLJmPq9MUlXq36X-!^m%O?T`kF{wkTi!61IWKN%}
z3mf;VvvQ2v2;(*+vbE92#iJ6~y+Jrtw_;8_``e;Wh!=DCK1X`zIrtNqvpMd#bh)vV
z7UAQbxPVEzXn(RW<9~|@ni&q35*o&^CV*uR4SiWAXFO-p&QINdMXRvLMB6-8=0F&?
zq%zSaQ{IHhWm{y9ywO!QuH?ZYRy+_jw0g4_XmcneloLQISq=ibYUFwTYht#Am$Prm
z_Z)g(_LkQ-IG#MYHRLG!!Aw!s!FOY+tb-n*L&x*jgicFOWt(=cH^YQ7>@*B%J)a6K
z>~U((PGirVVakr()7|3tg;;2MvHdsn`08j=QTu#^SbX{pDLZN)T*kZ0G_}PtSyeVv
zv9V}0WfMEX%b>>%9H_dg1tZ*W)R0D5
z6Fg*7@sJ`oe$60Otfk^NPVefjlF0P~P>g74-=UZa-}A->p(H-@hc~qhNaGqodtDoB
z$ZA?$$G8_WH~Dm!fi$5#OH~kQqhrwq?Ql$*qs^GZ`Xg{5$r$|L3Ap6Ou{&dHf0FZ)
z33#4~h+vgPcX4JicgR9Jb|mY6pezGrF+gpm%@2ep0IL&NrbmzI1i00Jf4WA%80bY0
zFo=NxM`n@sVNzG)g-A>v>?brm*C>X$fm%t9aOo^5)zxmt3cZI{8pxB(VkBgR^Nz8C
z;>0m0ie0LLTYQUGTmKGC2){NPfeFrpFk-X6WoO@ZD^G9Mmna%Y3hn
zDohP0|Fb6_%HU-nY@t;&5!Np}WbGsD3^2sGSBV;EaEwB+M-DNy1%KLf6YVJAca~|6j0Ci*o
z485qxSh2$Zd0F^Ha03dyN6XnQIzbCw>_5AyBJ7MMEj-*1peGb%1Ne18{CxyJic-3M
zG%LAWD*P3WwxI}3i&IXNQRDnTPI1jCRWqnJxtOrIEGZU~kNGak19{N$zRr%=kOotr
z2j7kzKFMnYT_nuk%DquT7+vH^+o@3<%@d574;=+V_;Qfh$W%del_e4vVeFXmEF8X<
zI((y3gB@JR2Bm{X4=>-X!qjM-_TACMnn2%F!YYF9j
zs)%#|nJ-g?EXcln=!*0oFqD+^M&uv0(VMW&aFZ_7mjD5N
z!0sss7bybb1#xPlbDTBUzs2>O-0<0Yg4-ZHzVl#;hGsU$0MB#<9Om*P;2^M=tfQsE
zj;qC!?4{^t8Rs|T%eB!b<5f@M{Wy=X3j+Ta{-(l>)+T&=P6fYD-XM+2aSEhwYxB^m
z4ZN?xLb;h~uTutBx)na96|xb{xxLXyfC8WR;0Nn%sMF@oI8(I>+FPPs<)=2gryUo>DJ(5wRt2e~%I4~WXRpsy$+DvX1Io!X*
zxY4l#_;I)>eB_g9U$o23%*GWS4VpAovgOWN+*3z-u}oS*?G070Q-l7+BeJHdr|DzN
zbQtcWn=|g>$WaRWz$ifb1pEDE$Gc{jwYrmx6AR@)U1C8glhTA9+seonbO6R`k-$Th
z*zlCk&P!p^p6*g2@%5WzC&zr6UVjq8rX5e@bL_4W<%B+*;pc>eAfqPawbi`@)L`U(o*{+HQ%?+?1P^^
z?HXiH%YN((V=io^pBV}~UXSS<0A}EOW_#LK-y4&O#?^94WM5<>mfH{P&}wn`b*(`P
z@cFCo!Kh^;tJQZ*t%)<89D?*QacNETwa8QEJqB2#8^!JrH;MG8c*P*0DO;;v>8u^jU`FE8-
z3iLS@O*ZT^yT+#HZbzF&8NN+GGIb#CYRp~9A&&vy4JmUm*s(Vt=!v~Q`&KZRAT+6*
z64epN1pJ^fXacxJ!CD2pX&cOc!Mg>_@J}Y2O)tSn5J{55{W}4#7Ojffw?nOR<`jL{
z@Ch{Qt$!9(vZ$xkUr%yO)=09oO5$bGNu!FfqKLb>YSRWbjbwu9)IXE#h*-bsyJHwCOcaFtHXR22*Av
zrFqXSNFsx1X4{6lK^2?H!VoG(lw>fZZdww_JD86v73JOIvMH+YoJVEsey||
zvdY=Sn0eukRE4Y8bw-P8yP(BIWZEO6Rw#~ts2L|g)Tz}Ocx**ha5t0BfFqS18h(_M
z*EgJ-u-X=^Yyc_)6L>5XqJov%eZ-;k7x7Nyi5GhN`718GO1h==Ln@RQ}y(s&=
zfZ4XKrZO0l7HQ%P!%k&oNQH^T{3ay5hQuCB%RCxW4tQry02RKgH2A^NsAm`~9vOu{?t)pyY39)Y9d1x1^=MPG52
z%u0n%d%+EEr|4xj!EG2G4ur|F<{81iSY)$00EKaYAcBNuI-3Kx{!F8el;)Aa)m|{F
zgSVFYNYtUe#{^t517)R!UCige1g9-v+VFhK#7+(XGS3FyXhHf0UNjJ!I%^&Pyu?CL
zgm6t>XW1go2wu8yqW*{Fw%A&3GU8osy<$s#8WBsp8XE-1Kr9CBFs0qWrA6^Y*&ktl
ze9E4uXJ+igYiU<}T`tjr)9ghpMNPQO4r-}x8k|_t0oCz|x}}LMVOhY~t@x{fI088hT{@w*xW$Y#`
zpUwr(kuHM1MeQp28uVcb+|vSmO)a0QfpD)f6v>xKM=0{T=X}zc(XC8DD=kR3_(2Wz
zUGLFW|5BDB-EuAb`fG882C$(=YJh<7M=0MLUA|n=^go5FnJF@WXcl{yR5bgHJD$%o
z?Ce451s22^(jn2t$ee#^eYT!myn5!rzzt-3q_W+}fSRM%qaV0B$qo*7Z;M`yJP$k7
zMz42)=*qIa11q%Py}Avt)KqjLkFpA_KMIsTMV=qMUvcf|z98_RuK8nofPLy%tfh^)
zUXUPDxyrQaX}2YCp87B7q-m$>i~>2p1&8=@fN>a63#{Q^{H!ozO0=C`b`v!=*eBRV
z<^@QOG4A402|meZQz7`g
z+;v-Xs(x{HL)3b**2>=8gi7g3nub?b%?nj*gc{}rVptX>-t5MmXrrwTiMB-M$a@AQ
zm5Vl^tw4)SUw3KDOfssQN*EfeyMCH?cI0nbLaC^+o#3q7g_^Ts{7VhJ?|#*|1_#Q~5^X>Sez#HxUl?eNj1?GnC7Yop
zZ9Yx7?5qjv&HmcFS;$tX4W{*pHOY^d8hW7->lO%ea$N3(FL#t?B+GTQHN805LXVPeq)XV=$nZm
z!S6AF12#crm3L`J_$5L2l2WuQLyAaY5>lC=513KM$qc6y6bnD$DJZdeCBx?m9L5Td
z_LW;h6sL6Lboh=Ke!3yNT)h3_{D8__t^<
zn<+K?sXL+Fx2#<^l`skoI16QJX5QT{JjkRztMrh7=+Ndmsu*4B1XlX2Q
zaoq~RKjYMY1`tNGeWkwehE_swIlNH|`+xs9G)N8jNPdRyDkwO_8b+|c6LIRgC&L%t2Wjz0Yd>gn_&pnh968|;ONK`i0139|dcC`yQ4Ax(yTYf=1=L&dc?(&#
zwe+mj#pvbOO5iI5a)u)3VF_IFVuI$H2)q>wW#KpP6&WJ4ncKutx;22R4<)j>MV>Fx
zKz1BkN|@DY{ek%S2B$28YJZTo7ZCkES)$bi(&T{2tj8`o|YeclXBhv3Kt(o(4|x>oDzk2ECun0RkjM}JnsqYf~W_9(Z_%Ft#lISrw?z|p!|
z+zQeb^t4jcQ^Dz?-uQ<`S;PocZW2o|#AlokM%j*YS*PNn=xPp$IxTD?qx?|`l(LNS
zi!R%vTA9b8SbFiS6TtxMo8*5jmq{^iU4&(Eal?#D0|8P)x4ctB`6$w;5b_>%D}6~|
zIJ6aC>oI}Y^2~;F+KXcjp}-vEs;fFRjp{5Y
z!a7~&r^%^*r_sHJY^krU{yjflYUmZF`f~&?{GRq&EQKcryvA#C
zwEqsx(ceKf;6vU^qz83v^!Iqx@1cq-giZJb50~%X7%)GGyDna}4vRI4`#%PkS%Zv%
z$8)JH8GexE@OwAKx*GaWNkQ5bT~n9As#y$Hp2X$o7%&wUT@qXwAJufc24yr18WmAx
z6L4WG0YTr!FSlMlA!4bonVHJS*v&;6oYib9rJ3Gvkb2HQOoUM3ujh$%BD1G3NLqfW
zp{s62Igm3zmQ?^tFJ0q=uhc!XJ|v>YVyV6_#O_oUTuvATUuxkVHKtPLyf&kOT`w#M
zE&&)x6U_R*N)zF7MMxL~n*}3Hc8V?w>%0Sp
zgUYl7%aIPJNXsCJ#fI0sOhnA+&-R2JZQh9lRV
zvXMFeRQ_-~cH;St{ZB_5rj
z&^J9OmeyU7}lSiZ#ag|K8A4BQ87qiUQ02u##*x7$pOL|z;!g&4!J?*xl9!ZP(3wO}2j
z*I;*|5NH}{HcbcgzwYaPq$^izjJWEDvCZo9)(rGVatVI7MyZqI<13O{R+&D4Asb_9TTQ1ERz;F%Z>
zoXraf`SUJ5A*EZW(c&%KSVP)*2H5JFEZB=JlHqq3A%E~rwV=j-~
zB?;8^CK4aD4Cb%K6Ji}peJOo6QD&4nIHSz3xAxTL$8N=Q%ZsJm@6d*Q%@Sy&>5ECM
zPAK-ru#Q(vIeQ;2r+62`?dgo}8{IbB+1Jif;f(VNXHXB!6^SU^9GT|9%%QPUktu$d*0C8(*CVV**|Jm1cL-u_O-
zdB=zIzj)rhqeLR<-OB8G!%UuEe|>~~em%HG`^=6$`xe8#U9hh@4kGI_4N#E7oQ_kS
zX6@mQhY&VQh?upyj@2a=PQr)m9Yu-;L%TQzCl?&7b4`VvBtMAM{L0S3sI#OEZykxYG}l}PAd8SmWt<0&
zBN>b03k49V;lobNHPif9#m5tiMPSY!!(6_{E}XFS#OGjMf)RF13OaUnrQB_~r(VE*K+gPI@H9j}Cc26)*-@EAU{9
z=+v+sGcu%8_dw1xz%3~2#4nbZxUN8&D)^INg30G{PJV8}jXg1p~~xeqn)(^XwX^8U3{56SYZLOpo#nyQIH^
zunY_^VAurS8+`~H>>GO8U|G6An>+Nhxi&5|^q_v3O}z3ls;Z8*ap(~_QT<-FdR*TR
zOImf@+QUaa&J}=Y7fHP50|rA`9a+1V8}J_F=8kIja-+(ObIg-g;8$2XhL^Iz)ZGeM
zy;{XEhlZVJ4iyiC!!p&4PvwY>qeMIj1Q+LVjaIy(Uy6BI-86}{3>zUW08qyH^bvEN
zOiw4ZFoF{9XE4aXgTzlKlJ1oe!rSKPmr4hAi@s;0e~Agjm!|jP&<`-aVC3thDpG?H
zWWg{Izthyp7x8pP?!{MIzKBFmU*
zzMYvBHDQUMEW}s5<8NeCR+-6hJyxy{GxN<)#UhsUG&Fz)sLadQQShnKhC?%7QtYcr^}d!n*-8Lw;$w6
z4{nI8!|-+#nrE#VWi1%$)sMB&AL3O%h{=91Ci|f_x&{@jQOoh|EMzi{LlM6rv_>fn
zc(8z>*jzO%QA@B!`O+E=AH(IZS`2XKZY)e)dXdbM5;lzv)W<@Al*glC}W
z1)iFm3a84uK-MW)qx8DwXdBIkQrtE#?hB(h9~@1u!h`S8Hr0mNr(jg)Otl?ZQFWP`
z9?+}(YSf(uk%f2WR{B|9r4yvm&rs=S>shH`>Q)-gsT4*HYPuq?(oT+n(G{q4#d=n1
zn7Wmwdj&OZ%dPbDyh{Hjm41#&KVQ#E4O6$$ZF4F`cM58{GOyA;Qt3)mx^g`$HB8+~
z`NCs=n`Uz>U6ohq%^0z3qpMKqs`aeYFm)^4H>c8;+)5GYa=iD?Qt20{)I_mA^j^c%
zt@LGd0>7qBxs^%)4s@CMn9%wIaU~zz?y<`lrnb_KV_9Ps9kc|$2zrux>~0Wt>Z`xNTqp018Mv3B>l0D1FnF{Y}iM;nCthkm5hMcFCw>MxO;@q;Ih}g(X&ObN3
zVhi8g$qxSBK+60H(RF>Eseilzn;rd-HM(}QX(}_`9!{-$@!hsp@O<-F<>|D)X56$j
zCJO*r>lGZjum&#w;fcT&9#!Z5FIp|?v~z;YDW#_r@v%x}@!>-mr|X|0I2+z$U-;Pb
z8#?H-g$*jyqa)$Y%U$$&8jiKarY)|)GBB_}r)S%FU2p=LSTGKR-cN3k?6XwkPBCD$W96&1ER?a>2i^~L*Tz!)xU>$D2c)mz`VcBg^xjnR
zcKk{6BOP&F8I5<`m{vHpN?5Brdlkg^l18o*hO?^Daa_!}3^ZvR=fo`5*#V7F2aG1c
zz#JRy%R1|kbJxiUFm#Y;6S(8_AzN>rRy_~0Lz;R$0Y<6ag{Kex2B|Z>F*bk`XB$9m
zu9;~}GcUw1AIdFE!&0AVW{PQ8sHXA2%%c6KsbnRx@nlWo6YXZ|oU=kF+G1DM%viY=
zBcIr=eUte~;q|#ZR7mvN6ja+5wNX6tqBeky}|0^^UL-{dC0y0H5vm8!x&F;|D(9r4Lv
zg?sobgl+D8n~XKUYI7hoblV(naPZ0%W5wct7!qiuISzvu=&^aqBnol4C&(o=^fIGN
z*Z;@do4`j|B=6(lcm)qkJW#{~6b}SZJXk?M5gg5`xVVbP5R2wo5`j3N+}ok3BF;(>R(-xyrcRf!k;o~Nq&z2=>OyX^P>=c9Sw>8`G>uCA`G
zuCDI4r;HD@N0p~%TwA>PJEtd?qWK~~F^z^IK%2+oTCYScm;wE3aeHx~U6#eP++B!7
z2@IW2%@pj|7q0N6puI(>^Ym(B@+%MxgpL^fQ%nUDS%C+O9=>L@_VB0Jti?G<1dZ+v
z;SSgy(DM=!w+-uu_Z}yS#3EXV03sO-TiAL$!(lh6MD3<@pO-Gv8m>bPO((Kh%@Bx%
zyP4vz(6c}Z@QQ!BfHd0To10!6Oad#Mo5qk0-htdHs60q)bU(!Qahiq?bOlr+P|N#i
z@37*myXQ&~s_;%rA58f=i@7scCOxoC5-|fmziq>9w(a0W4_FR;TcR9dbYt`R*+FPw
z(++5_vl1DFPY>D+=VrB`=ck74hH#)d7Laxq>BE_0QV4zi!1{J2?cA}b9@^YrI1WRH
zK&awA19HwdvYKM!Ou0s0^H^Pjfz%zocTB?vY;GG~?8iJ^z%h=#=L80;z&RPeoBFb8
zLzx5L$>_Z`i=yuIWuo;T_(!aqt}G_;RY1ZchJ{FQ_W&(^>xx?JL83Rk$5Qwa+8-Og
zS%CNv`K{B<%RD~)I=&@7bzY0UhgE70$t<_PoZwC55poZsD5ssrg1LGHcY78dq}jLF
zY-{b(CCbG-WbnhgD2vb)M6BD3Meb93#WeBGO#@BrjV8hzEUC}OG_~cP0Ra>=n3myq
z5V7$5c#sd@2NmMT#cb-^exAWwLS`RiW5~A-QZg(`LE5?YdXLE`U)>UuYaA?vMkz-TyePLT#%pp?F>J6
zOD+4jpZi`Qw@`DZ___ZK
zY<`y#DCR~`bRo>621RFusjw)F7^_7A{dkR2;GRd4RE}%a6Ycz)5%OQ{9wW-HZQ_Y?
zg|lQ-+k%DCUlgo)j?$6!=_PJ>%z{9RstD3qy
z;??J1SLg|h^^ykAjwh#9{*MKteVVO-L
z^_&+jv%-}5sw>LWnKJE78HB`0J-x$aJ~CzIv5eeOhULhWfX
zWv)k=rqe4)SqD=B`~6blrErN;FIEl=V~M>vc2+MeG@$o{K`$|&djOPhWZd=3S%20&
z?p2JB%b}2TxSZR@GuV1_OaUQ#`v7(q*V^v$t%dG-WUe%s9htc~GiPchHB5uaY$obW
zm|3ow4an3LQo7H3%!F&Jz`aN_{SCWxpM}ii{No;{nX6G!9sKD&GnfgDgiA3sv)Pxo
z5j1Hc0tfYTx6X+)`@a4Md6!QU9`gg5=x_Pgr%(+F)%u0Zq8U~QIQT9M5Fo1sXYoJ2
zgxU;a1s87Z)CK7hUl#Vd6}0k{k+mIL);VWCD2SS6MEk0!*iR1Xoc;aepw8)xa{ACX4Raf3+;KYRdg9)RfEG>Goj5(;cTEP1O{C^_2Np)Rg5Q07Xrad&13E
z9%f^Q*Tfuyn(>HKYL7t`SPD^-GcCA)y5B@2rBzaUWl
zfT2~3^lq9CA_C`AitGtOnG|?n2QBej5pui2HY~i$d_jsvp<(Gh?_8+*r+!By^9&1<
zb*a5Tk_2jnfqF=xlt&6R-9o7^r-5~xiDCUaZZl3iz`98+&utY
zfaLX#j0v*7mXT4euX#qsmfwOTqde@$IPP4Gj8|E^KQey)OK@cDET&P*BjbtL!sxh>
zF_EJV+S(it{6f4H5QX5#c#Vnaj`QWw=MgcIvhJFfss201jEqxpCvejYSGiii_*31nSI_d!-AI(iW9~hOYN8uOL3WsXhlAYCLe9r1zo@Vxs!gqQOR6n-^
z`9YlgG9>$huj>X_
z;K%!jIt51YA%1eO+HQVwu-Yv`)sBca7%Igjr+y-R%`@gZe+^DV#{BIgFy`N7?f#fQ
z5^4TyI7DQ-yok|r?RGI^
z{;xQ#YlcpGVC{L~<3AhHIZsxT-W-K|m!BN$oE!Y)VCP(lWM3vmq?+*!kX+rft+YmZ
zXByTw()L*0b4`;l%F{jk0tFP-Ii`@>y@`1Mq7erayDPvTixoNsCp{i1A6DcRca-+!
z=$w7lc#I3jlj?R}F7@a7vNf_r*;+{BA`ov1`Ua~#+S``1j_N_^A#6YaVu(U)8HVt#
z=WNTJrq2lj*drA76JLJHF{05Q1;rH%U()DCx%`LrMtwrzA2;w`Kp{5!aOP>Tdsw-I
z7-HLv@q@bUwx{Pz2aOdxrwC|OX8(p2pe1xO&M$O0YdcbFJ0e7#9C)b|uOI`$K`1m3
z6xdS>*@+Te*!VivgpGsvmw5iDcz*T!R9mU{idG|+%$RMS_2#OUSHj?3P1xXUVl=nt
z+<1{Ed!@FtIU{kY&Ei&Sf1J^pT%*-k^q2VPOVM@JM0*TD(#{EfR<*ZprDxT&!3fxr
zo+%MF9jm2wUSBC;)1(DHn;u3%VbCzeFds&y45hL}X{*M;=t0RfEn0Ai*H|`%EHj3@
zN(uU)u9nC#kR*Pbu5b(%`dctR))ui0fL72KA$k#^(OfC0EF`x=vMe0>Ts?v{W!=kd
zl6I?`mEY#}pYXgt``>{vSSK|tI7Mdmr-sR6Dx#R9cM*Wq-L+9PVg}mQqa%PIas=kU
zFF~F?p$3Rbta(wWy(y%eC|khF5CZr|bJ=b}y7gv-eP7IBXz4N`tza~k3Fd-h@tria
zE2ck$Y!GUT`_&g0{^@x=e%Lcn9X!`)Sl+%kRd0_r4#}{jWM~n%*g{1{5jD>Ydqs-0
z2s5-u5?8ep^Qsn+@>`Vd-95U;%`UhSam>hlmUQCZc+X{24%dxjq2s=Mg8-8&dMJPwsep5J?78R34iR9L=k5qtWi3;*
zy&oAe%tiy&xZK-_Y&MkAK=3ykE?WW=R#+XD;ARD?2M7ZY+4S1A(V
zF_j2$C0A>dk{gF=a77mm9u$?!$JU3DlWFIEu^8%sU~mdWI`a22Zwv;4rG`vHy8aJ@1(Q){RClp_|s)kjj)-!b7{pJ1|jDLoHsfR|=Pc
z-*rI1p^G70oThTgx??{@-N?zx=7BTZ0{i?lkp`B%{sNd&s%Z!@EL9BK7!0;AE73Jn
zFOlvvV>H;K_drSjJd(ihDePk3bB9yEfBTRX(oe+|{$0ZT{s9*{?h+m>HYP(9gm&%U
z%snXTtEy^pgw(%i+W%MK+fqxk9>k)esx#Bt6Ute#v(y$glR%Zt(rFD}(SBG*lf}L!
z2mloj;1R}CSl;#e^pMD3yI7iSdjcJU3obml4~xDQhx8+czym=EL?nAZQ6fAFOr0_i
zlJu1FdpV~2`aOSnXZxUCz~BvxBfSItX=I42{G6
zF`J-0V<~MsfpL@!#p|#G42ey+*s0Wmk@&F6$W?3C6<@c6!|(LN&+sme8HUACZLR_P
zlN~~7=TB!E^(sRyI&1#+ku;)yIfW$c>}G*0oOU;0ITYAes#k~tAL8>QApCu$FZh!N
z1tZl^nfdn7@-s~R70#1{(0!!`@mlFl-&9)~*Z{ZtN-MOwoBh%^9LwtZ94t-4H3{97
z@;4N4qyf|g!y(|7+o#E@uHv#K(5?ACs745EQtUcte;H)eQvhuY
ziq91CA`cSNzW-63&oy^wPA)#G^Kv_>6X4=TkSWRFsV7aMtTMel(bKBegXeviJA=J!m&)F=F
zq5g@Q@grEyU?1;c7rO?%Z|UqV!eH*(;SPXNxI%a@s9a((TKrU7g#@imt88QdKJytV
zgRa@Zt3_v)5U%klVG*tB`lSlrc#dha*sM?qGMTBum--)tPo42ZDDt8fnP-b+6V*t8
zIM<;XkBkk2LHlh}{F*=IB_*?fiu&+8ZXy=PFTLhHdB5
zS5NxFzT`(3k%q>|m9%N6(GE+?C^P`Mud-cfXPKX?qO9LiHQ&VS4k=F6VX{uL8i20?
zxcw$(1B)
zKst;d{u#m*`iN)H4|4r!h7dno-7dc_uvt2&;0JkB>$Y|7>D4I%tC~!FSIODjX4z$=
z6;)1tL1FB2qev^qxp7Iz`pOFP$L%E>8rX_D%F~jNAtE=aqEa(N=bdVY6i=}x(9xAm
zy-C#jO4OLMJf>^UMn-(L8%nr4VV}1Wl>|%=$8GbeH!R9liD?xdqnOnyI2vYKYCr($
zkBo-9-%@#Kt&`N%7MNS8egnV6BEhXFh#~0mPXYvW#q8;#q0OuZ#wd0-_+4`sb_TL=
z*V`=NVsq1`9AgTF1{#-fg|jmaD45^it+N&1;r8)jsa@0remDip0J6%*u!Rp{EO7sY
z9NBm<@i*Q#;cW~KZlK_S%{8r>dI?9Nnj1#%1Ol7oRN|Tfz45MD9f8=NeHT~HtdYZ=
zYdk|XpCkEvRmf!ZP|LkoRv*X^dMjRl=*p3lb~>3tID_?wx;wYVZf%d_Fq_~1u}WRj
zR+UIPm`OLAq)PYKwXnC@MUhiE4Gn=F89{r77ed{iYl1(kRuT!HaI!c0Vr1kh#OP3b
zGn)y)CmUc8d_O-M?6xawzM^pAZwT`VDMQ+5RvDhnx~wwHDxN}Hsaitb@)EmfTJPqITxtSb-aE6budKdlVn>~C-m
zCeEk1$_36B73T$ca9){%b8-l0JA?CI*a>aK7OvoYNpWtI2j?C+IQxfit~gfPemij%
z8l1}%=OP$@m~dAbPI+yA?WJhjznmOw`#lEdk;FM4y~wt|tT->qgL7OC&Rat`FETj4
z!)9tD{8obV6~(!I9-MpU;5;mZ^I(JXZsOd=;CxkazHnlE+uy`qRalmqCI#Ex#^5}f
zI2obDwl7zlm*&BFT@KFaA)L#P(LVa|XW+cZ;H+1iJLbXJD+lM05Y9Uc&U=Y-dxP^e
z#hE=JzU}WUjkf)pbg=Cg7@Wru=L^P;cwKS+!{97jIQ)JiPdXG|rB5Nng9LVP-VUUV
z6HM_pwD>`JNIEiyq%%S!eSfsFZvIcuZRm-&6y4{4))pF#10kF-sLBO$Wq-oZ{7A!}`hIWtz3d&KvRx4u(&l!Ct_Tkqu+r
zFSD@R7NYAueYKI)@0Ii$hnac=8C~{72BK{^1`8UOJw98?WxD``U4nqo_LaQL_6=tb
zz!6?g0|v|A^299CzecOv-c(sSZdSqAy_nIg8EgNfv|!N90)i?1w
zT>w7gn)&p=Y3N?}c9pOzTk>0mpF
z44)X+$S1KS>uwv@$Y0(JG&1H`OedXZ*|^y<#pi3>-JE#s;xJ&G!zia3Qac7$YPT1`
zAK2}DoWB?{p5&ts!4#}b2{*)6B5S5ZO{IG#Xh%oaM9uTVgOqob1yjr6yVc`_HO=a<
z^}b?|@{{2g@RBe0le-6!Pe*bfCWfnm+y%i$+6Xf3Q`??cvA>YhxM7+NQrvqW0+#l^
zRyc!3qu~gn9l)hpk@;0F0@rfD(w3!cEY_ZS${_d^gHMp>tUS3QAVx*t3S;uHDCCC>s)j&&Eu+fv8Mh(*wfbr{At#3E
z9X~nPU(fr=!Tx#}$$qsX;`?hqG2kSG=|Fv5Ne1Prsb@G3NBXND`o_KT8f5qZW}o*D
z_LnR3RLlK!7CSt?zYgFi@%!s$8Z^-Wvul``?s&dD`dlFu;@&rU8+KkD(_d|0_xcO#
z;lb!2ST>=xKjJiTHSQXyN^v`MLX{Xf_@7Juy|j1v36zzf+J$TqwrO`!qTcF%Ie)Ii
zu3vk>l4>1w2BZf*0-fkS&p!`=x9%Aj5N=x>iLld4)|q-h+X7JnsIK$8@8UD
zN<8^5<6Mt7ToCA-e#k!tRtSP
z{G1~dx{&zOPJ>K#&ZhdSGao5ubFK6gK$^x6H?JAzB)shmj3?m9e;Fr%w>BB)gM?P~
zE}lim0P-D?19Wd71;4J*s$SJ!AMufLX8#e7thC)!)dPi;F+K6N6EeHwsa18xTkDK-
zFUqo2_u$z{t6IIkRCSvo-%hK#Re#;jN6JZPRjq+T3T2&jD5O;tAaQG8{0%8u)i-#<
zywCX&hzvlCia#j^f8(%cFp?t=?W}GbE
zx*%l!P*ujc96wl=d8R8W
zdrR316}r8aJ(s_-PW=&10VY5cd=PJx+5<>&%6S1l@oNenDd%gV$U1{Th3Trp%xlIO
zgttxreNd}81V1y*KKM&)LlDk~@&hGtJ0K%%+u+$1l_LLpE!074E0HO|X{Wzd@{w|G
zK8o#n2|rjii)VK&J9a;5*J6b}Ov}#audLI4h*Q9R$~Yen)^_y(8esbcKT+??e59O?
zM3HqK0u^R#&cdt8#caHBY`(UyRC6yW%{r5TpZ_w>NW8I$=di|
zs77mk3J;bC=ndvI<4nXGbx3c*q@3&cz-_@L(qk3;1wDs{p&^Y|rC}1@NW&G{0p$wn
zcr<(n=94pT;|av0G(Ty|1MBtXHRF`wjWp~-n3OY)4}|qh6&gq&pxH)4gKbAf*i9P#
zt-UirK_5en+B-KI8p@Q0ziU3*X!vlgdCfRvH)-fjn3QuRA8E&Bpjk=?1PyE>)}17W
z?ci%hSV@^=Bed2MytP5fZFm}zr{PuE#%?FcgOp_UDM0u2NN#6HE>Mz%j;upw84
zii#{D$$f|;C4}JDgW`jdJ_F*Z$TKuhXtdz1cq0wZ^^$_@Q-HpU(Vz`@!i<(jbbPQq
z$fs-@8rUa_iYy@w`x7Q5eTrX)iSO6SCEI9du{HZngGa;JhK5s=
zhRaO8kBa*I%*4f^pVfEoU8XEBgs=sLd6{b8uIYt!~*-aXD
zAxz4-ln>0)N`sLHw$adF+mUguK_Y3`Lr3Ho7R)*?phoT1I}Hs}m4>r4pKUaJ;22dJ
z$ZpcmnJ_8mB0lg@512;yW#oZvGzD!tG7eeEf*<#gf|uiG#yJmvWmZ`oEpOToF3&dX
zs$#+NXv2P98`_&Tw9+=5rupQNA%$aH+i)@7NXmAENjVqrfe*Vf8WZ*ye`Ou=$P~1E
z$VgZ0j!eQR=g!3&M>OT$kW8V|dZ-FW@+I12qqiR!WZ9m?8(V)Zo|cZRbe5@^eX6L)
zQj**aPk@Tv#)QELK$UB2m|Ay;n8|B#(285eJ%Z__pFT;~I2HzY;T%lbaKA~;%`KGIxSLxxVFB9i
zvV_FL`JMubl)%j`3!jlF0=rnMaOwqyT*%+Chunt
z*v4)=p3I~xvLcQr`;EaeJ}#r*7475;rA>~Et8$^TbS7!#C39i2=R#3Xa6VF2ym7d)
z0)09H@PXTJ8Wi9O1K{1_w|?2d3Z1;-bfPx1#CEz-HqfGy#~u9;USFXsJc>
z_-{O}H>_())EL%{7+$u(D=(aBbcjf%p_HyB-H
zVk)mQRa=p&C{Q9Us@p77byaw{_7&vAZSXamo#DXiM0L2~?G?V-R+B1f>Mp*16O#G!
zp<&5hs4M^vQoT6jR+sYp390~@Ry8{kV@?94Gc;m
z-LyR=(mf;#5w!dGXlH{s_g3skLENxwF^;<~1MFQ@=P&fUbN)9V5AU95*6)stxLLo5
z1wym_+P!jS{iO*00V)2h|E|Ck6sMeeyxCd5Y}F{ZPOEZf`1=M38#u#P>ua7FeqV4A
z?5n|`5=GsEPwWf$g+$Af!#y=?n9Yj)7CQGJCp~Ml{yth_mr#k=V?(ue4>EYcX=EO+
zHG7cXE@1aCj8jIf*@K)=H`Ws*Y(%xZ?RSU`I)NH%Tm-!%K1+@{T^}$
z$C6v;Is-N-2~pfSB%|0l3W|QY4HY599W2HBD#Z&9*{Bp(J{QN}I=AhBEyEPIX^G;u
zCYe!0icd#KhwykmrFip=OYw*E<0)=4V3#n(ZCj#v_aMa&LeUDvvaCrvr}o>ZJl-b{
z#jOV%8m8E3iQ>y`bP0L92CNl|%?{G6z8jU|t8fAA^hcrg0F{WJ4pwUIWU$^bJSKpO>ghnm^H>DWdP4yA_3_j+?#1cEEr$sLDr=%g
zDgIg;m;gp_(<**{{T!u%+IKfY4k-kIk^S|WpHNX?SJvDC`Sl;Xp7YK-MV3F+R$&4l
z&shtaFtV2}XO3kT9!cJ+2MV(T>xP^cG(Kj>=OFx|{@=J1914>VvV$2lNP+4=Ap+GG
zH11=GI9!SN0v0hBPFSID3^qLTbUYDMYeED){f)EixF4)UoU?I>I4BPh)Ko$QJu8j7
zSt5og5$_+gQ8_VpUOXqLe1r&kXd3Ur9~q%XC=n-bTq1hJ5+V9zGs-0|MQhqg2uLUa
zZyjj3V5HV<_>t{^6%e`NX2Fx*IJJi>^ypb}YU)@RJh}GLGm4rcc9dUB%!pn41FFm>
zszH(1OM}o;DAeCCr5ATt0kL;*fNz;Cov
zs*&sEW^7*yg(ZtIhTh1@^V*n`=UZa@*<+^AYPqYO%J6EZvxciw!ku-c<8EPpnKj4}
zO4(7CGF+#x*ZhWrMZi&t?NNhGba*OJUFpvIUcj+@3m=ZV_3?nDuv3fmLwC6na68rk%;Yc!&Mof+p_F38s&@x+~w*D4)j_
z+q?Tkc(n0+U^B4Rifxe0PlZW)Zb|MQO*_N>ca&_OkCFk4uizwKBho$@E^!$JCla{~#B17`?>w#~5@>WH=
zWrN(dz%JHH>lz^w%RP}fi59MNHyO}c910{>9SR=VCH;dWhgVe*kvo7S8>ek$6t(Fkm4Qe&ISXwRLJdPV>vJ5Lso(x-wgY9XhWd1%@
z2Ck+I7}tA#K4AI?ib9X;gb(GOC`29d>VOX73tw*$5v_L_%eFqI(4Dtoq5$l|lr@hE
znFwEs69v+&qQJNEBsl?b<|B_dhh-xo&f9HB8-BkyAE<4}BhDmN+u)I1avb}aUKr^0
z6+N_{gFO*mg-0ZO*!ZA?Gh9ei2aN%sn_$p+y5&F%zzP6xt_4_MXa0T(@9_vJi7!3Gs@Ya+AO}6OV^`oxcy{Cxoy@06`bn5a+?+I
z{m8)Ic|)4@`v?9Z4@z(48Th1Mo8`5nR)n2dk%50Q6o@yqH4Tta
zzesUSGPvxhZyF@Ii#2ytGNwNoYW|_VSyjEuMeE%+1I|y
ze9Nu!xXgbjig%fRfOHVs>~EAWGbw?M@~O8A2Yr|MEP*K~PC0ksE#fkdKOi(g8%85P
z<+m|r8Q24@&LrlD&KZK0Dhiz97w}x>M`+eSKdU~WNj+>*z7r9rCUC!ec5GwDu1(yT--s#-ZadQg_lS|lT8^@D9_5qG}d|!KX>^UOO
zDc-_4qFST6dJ$FBhhKzsJVrfNr%PeH@?=rp=lP+s#9SE~iMhIQg~*IK1x?2ZTaHq;
zytjLJc;tEyVORw%p1$w{pem%ob9&xdgg0k?qmEC^iHh*3`+X5!QksGgmXww!ZpvLD
z-lQ~8c#bvXZ~%k<5wG9ZzoPEI3h@}YTTp0wTS)d-v+iqu0hnG3V^@rxU>&PPKI;~4
zc4_pAIP95*nteC}PPvn6){{eZK)-MBfU0Vwh*%%TXWCh8vuGw$wk9t%0EUJUK>&IP
z@_XyVNZ{pgEjTUFBQm*djzbN`WhkK<_#P#C=cdrLYkNeRU!Ak$iKzC$W2E^=O;k5>
z3|G3#f03jd&2{Cyez&)D%*fqMx`4g@g}&z5X4I?$}lUiY`h!xOdL24$kNW{R{TBijHm?`Nfn`9lk*qy%x
zgLRR6HOiy27DWl&BS`2l_|I!|M^Rs&$U|)fu|&>N-+8?#xU{pC1*ZPc??M&g1jr`)
zV`w)4l!_&EhOVkQ%C%f#e%{sV+{g4aPv>?v#Js43W`YHl9xTw)RfuiiT{9xoDY-rH
zC51l8Knu$wCH91B0!ztFRerltr!3QgI|r))dWbBI2=|xfnD)-TXZ`KnN)b|+7k5?F
z!s<|4M1}Q04al*2ss4H;=qA6Asw%W*VG;8{T@BS9^^Wuqz5sC|kx&OmZg^OG_xdTl
zOlu;g+xwNqP+Gc1D6RioDg3I7*DQ}h689Nc=M#tD=qdLJl+BTw5joVwZvuU+HIc$s
zcJV5Wq3}a`u>*?XA=Ls^fkJaMDQT1Jh{I+M80v1$Hj=9mKloF31sIuUybbipeAS|l
z?ec?_OuG`DVS%MyU&L{N3q>4bu(kUS6v^RV*C0_N@^7L&tQC-deQaH^gx#$!`TB3P
zhud4=vT@Y~qxbZrRNOpS_ZO8y2puPXd^AnR48?r4BFOG+dP|2QeBk&lo$Vi!hJufW
z#2LS#oiX@`=vzf}Z3q$cc&f-9A*OUh6?M2X*iX4?Xtp9EE56&YB~iMSZf-Ly?ZL&G
zHc*43a7zzFMO&Tjcm~qcnC3CYGX;H~gK+7A%RA#7+VG>KTfwryoiKq14=$cSMv$9j
zfzKoDL@@v>lJrYsf5egvxWj8TvBREBOm}=~CqDYT%?J28;UbK@erQOJ`SQ=A{@i3P5e{J!=b*SlD-hfp5EML9rzV5k8LCX_QH|;1R
z!d%~VP@qC&x_<`QpzG@KTDa>rRFR94^
zY4_(RO%OzAQ3S<;V5T6LSpedBOA@{SFtc#p>#$VORoKd;jbE6gS&*N`@xAjhW<0lm
zrMH>}Q-H+H&~z|yVRae369dZ}j?pNQ4K*Gom--$kDnlEQd
zO0AqhFO=uX+F2@Kl(mbuYf09&WMzTn-8fZ&ef>gK{OYgOtetHZV_|tAV?V^jr{%&!
z4A$`q)ZEcjYBzv!?aTI%m2zI+D-x*-r`M-R_13z9f;ZoQ=CTiBCD+2ClVp{3o1Mg{
z(o6UOry6#j*_JB!sezm_XL|;wtyt+p$8#0;Ghw-g_CycEIO!uk{0b`
zie{4Au_PR;ih&!Q1_Si6cWsN_-wc=WfgWU005TjqsleLhk53`Drz%R#@RiN(vZ+P_
z;8W#8_0qYf>lHk*ubAu#xUc}cdp=TZ129?G1~loJ0aPz#`qj(6BkK|M=GR!5{Be8b
z&Sd4z&s%E`wn#|#972u{5|WRTQfxyxsddQU87Z;ZNl8(L>QzSb>SSgtoV+dCFt8LN
zQkcGYYhDO@b>W2h*uCY;O_U8x6}>i5emC)0*Nbo1~0Q>KnQ85Ytf$0RG^p
znA)D5=-n41dkio~Dw2|}6}B5pki7Uf4ipQF1L>*WKcWB{d8;<^(V|#k3h6Yxu0G^E
zYkQtlTP#whLjgFaD9%$OI7bjFI#Pz^2x!f@f>Sf3MU88=)y|uyhzks2zflW!he**>
z-gPJnN15~0AS8mW=~{eo;Rdudm#$7Bx=sPzjb|IWZd1e~4PtzKXC$(%BD`I5kB+Z4
zv}>H~ox_e9^m$g843EWkD9#_ZG-7NuOTGb3a=DxKdsX9f39`_XJw?wv}~
zxrQWYOXjGy9MYna%H{PV=SZWC43Ky;ChyWp)*{{;Qz;JAZI3*EW{6R&{e@?uRS65S
z_zb*s{Aug^ynv%hsbUmxG$ktSK$3ZvPeJ$U#GKnV~d3wV54A
zok%14h4tu1xtLPAK+;QgK#rLx3Bc{NBy-eA;hPThc5X3DS2I}e)jEzhbwC6Jxxth>
zfl97UPhI5JKqaQ72~>*20_2}SpD3YBMJCY9A;Ro@_;M%EZ_v)hv$oX1cfYpuf=y#(
zDLjkz1+&6~Z{ZnIw`S-?@aUnLigO!-6P;FSXOssttGT^ax88v}mQ`-3_7`C7h1*gj
z%xW;n@C{?t{<{!0sGX%G{nd~JHOric_7@E6A6s)y7k&&AMhQQRK+V=l3T(RrH-|r{
zS)aGF^1K5|`?@`LVjwv(F%T9M^YMEo25HWJz}CU#`Pqtdo`H!W!t!yWBL|I1ap_F{
zH4{Ui30IhSxQCQ8%QpER3(ManCOVI>>{och<(#PU2~)qqiGMZ|*~3cjj)q>N@DO?w
zoyBx+2yS7QoC?OS&lLJkd+nwhq}QZYZx+)IvU~ISW-*<4g0tANQ$)m>5kHHm@CO@L
zrA0IY?7*OVL|L`Et?gQbox^2;VF6wiGIH+)@h%08}jFcCKbUYO?I~BJEoL
zNNpRWNk(ep_3)p_ELfwMPq39DCLZ7UkT39n^v5)}kI5}vKy^`2GSKht^-qvDoGeT<
z9I0$NEHXPLO4l%R*edak43~Ws?BN$0_ES
z{%>Xbe@D>I{-5oAtduF3E%?wb_WDg7m9q=)gmuJhl?4y5Fxd{{(n>fy!r=oQ$=or2
z|B0Z?d=X6gN*a)BIcZ6emd_<{S#iaR1UB#JdXOTq0QoJaEB{VLMJ%VA5KqtozFe_+
z>T_^r2a%blmD~FjD7W3sKLaj|!dgw=-lSVu+CZ&z$q7D1DOvtCK7}Y=B7Rm8FI?AJ
z){hpk5z_Elyf1n%Y^Zfq#G1hmq$KL+E5eHn!c1}}lxo@TNK}c@K|qKRAI9i7lr!LZ
zrZ3bAiv!yP#urNV7LZWcfD3(8(t5O4jAyo)m{Hz0;YN&s3o9<;s=^zlVOYgy4jO~i
zKwPQQ#$b;iE@QuZCHjpyAhrY!&g7}%?2?EHME;e|X~c?>NXWQSVS1`M@+#|MNa
zQPp_BS~HJ5r#)Qp+XjeCu3q~D<19c%{f_U
z>-U8K)KW+nXcLzHs!hlwcLuwt)M`DY#uP{+AF)cUR+JjERjGM^VWpN6Wf-H>mj5Zh
zX)2TTE489=evw$6^xKmEJ`V@Kz(-~|5wow;`QQ$0Qpb4R7yYX!$
zE4{t3vQ@pYbLpl^K8uDzR1EzLJr@y&XSHS9QxhANzXELn+E9AD+fnB$Jb{&J0hNoC!0`{14G1*Dt1`)_PEtw|?V
zd*ORape2PGlyNaVqYMH-$h(r+#rTvi<2NzWX5HdX5yMi#CUZkIvgzDqtk-qdtr`ZI
z`L1xQ7EWkrM
zAXy}@#p&c~0)nv1>+#GcUHl|SHu;8mLn6yvxT%chn|y?sWDlh8Ve}t;=)VtJp;}?a)E+-2tNLh5^maa^TN*(#X7*p8YJ|tA1t9{jv;i67TMG%K_F5!a0xQTbgWR=s)hW?0dao`n?s1=s=|2_9Cq%2
zeMfJbleSOEH74!cddR$tAfAJemiy`nU!=d{ATPhj&u5f|_Yri9-K=@LfCz{gaoU{1
zd1;RP@?%>U$rmsy|7VroJK7VyQf1zxe+M|+67w!VQJ>(Ogn5y6umM)EwW~)3u~Qm~
z%_VGDAks2PCmWa;+6KAtNHRz>D3aqTlVJ6=xawX~IbYzoUi~$WdzKHI5>aY==r>NI
zUn~cV+L?*~x~ESsHLPUTYPzV{P~F}k;x5ROSKavj;*mj$V|b2My#
znESw{%XkoCYrbuX*yEL0Z=N<^K)Tu%3eMAZN|s2o#hUw#jB?@upEdCa}xkfQrW
zc|2qIs0kaD4=R-5bB^5NO{^&$x2B*3A64fU78tyssj?DCtkLyC2dK64M3C_{#SWDM
zixW%euV%bAo4aBC%STeH&DA4_RvHD_El*6yoz%Bggfyl$w+)OnrXE>})>&T=Aj)wr
z9fjWd#&D%<0qZCz*%je(xSE4k2b8lZ@YqlV)ye2#nAaAdT(_O{oKgKG#dMRwWY==D
zaf=@8opqsZtqgbT7ZQ|V_v|9x9vc5a8F)s4k*ZeLWUY5wuLa|33JNEEK#hU+Rch{!
z|FZ34tDt+eRl%Sr5=oaquSm;-$`?%*FtzCKyOfeLyrytrVnT66T5{5tL2(Z)FK+cM
zKU%chee@FZ=^xN?v(oZ6pBBVIA3`c`(VSs6hvY#XCqw(lii_hwoK~|qpy8rx>RJh*
zV$o?@w4*JGzGWX(y8CBIU4jD6!z%~bs|eq_*pp*l$e^)GjhN*M*K;aS6s|d6x1<67
zlwm0GbYcAUH0jdLIKP0W7|Jy3FE*=e0a>R3Z^A^+Vy6;gBjp=nI2dKZH+-&PsgPQ&G7QVc_(55HgZj5UJwF~R#MwdRi-F6O5_5Uq;~DZ
zCKVH_1ZtvwC9Ks#gZi^WrCy{W>~m-9QMy5~^)uMK?q=oFE8RaWp(`daJu2Zp{VgEj
zbyNhY1*bs5utd_e^HjQ%a^?I36)(zp=Bt+E{DK#NEU>4FooCAW&9+e40@hP7n*Aa*
z%3LshOME)%x5m~5?hF_WX86w4CZ1%A`dF($xQwJ*734r4q|#_7uJ?fi@?!Pk|dEwsRwHmOV)qeD+v>
zY$z(e(wRhOJe)pe5zgC`(y!dGa3bbAyuW}w!;o+uH9&+Dsfo%)dQC;~-mcj0G}tg~
zt7O{D&ruXNy^eqv_RI)d7M*p6q8pk^T*MVR3Iv4do7G>$vEE?a%wn}+BtzjC33Bm3
zn(f@@LA5nn?OR_O8KDMD_c?eK5BX%>+jbR5C`Fww)5SQsG8}W$W++dq%7%&q0$(O8
zd%ksXMcaDh!QupdU^rzPU|^Aop~zp{x;WMLC9o=Z22Ee^eadH8Sos`a%jGJc?M;@|AM3$P`IG{BT+UFy)aT^{(v`fRKnRhERNj=9pt|b++3!?WRdh7VMF5bx
zuk2iE)YaFYn!f%$>I&SYx_Zo(_0`qgCdrOQ=+ek&%vD#-e-J6GH&{=%So5i?3FxYb
zy4uStBkHRBOI252K{*5Rzfe~XZb{y?sIKnO$=|B0J3k39v~a>b7&t+7HO^#P25pGC
zdbbgl#84WbnYM|}rvw~a=RCi8519635wW}2^6<)23
z#)UYO3L0;;V{@eP=;IHyx9!-3ff6wU(S-KJsd{^k_0R)Mm0YE)q-&5IzfiH=YOtaE
zv<(zm(+0bues>#zl~C$$N3iq#aDWq~ooj;$C}4AnbZEJY4E*&;c7UA>Er&fUe__DqjU|Ft{r
zL?t7?JMM(yvQ?*8H)&7}y8G9m%^kITEneUJNMvd$8d+zW4>lBIhmY3qUYL!UbD=hW
zwgr~m%9GJg)#^69miKqnU7}FehoH=!W~U~Hx7)Fcp+d4QKB$b<)YkfeR-DeSFrV3MkE<^!MFXqQ$CdGiSpQ(jNIR>osK@s4V0x|W(f|;!
z;%Ggz(ehk|dioy0M$B?8KK1967pCPRSd^WpEEw+rV3om%3z1!M2Uxc{1631=7yF<_
z7nKdyB)D5bf(b+~b<@tM$M7W)=bAb;SR3@-S1JR)p$!ff83o0RCr3~U
zCO@<8T=2%Naau^810TM&qcH32^hVo)Q%G&r*#WOO4u}uIb;cV%l{REP
zW5b+n@iXIWg1@bhxb`htM$QF%Xn7XODUYxUXL}Ukzl^gf-te*1#VEv&FfPPXN~N8T
zRtUOUqDwntq*2b}`s)QgQchbwvd*`FG*$h}yk?wl@W!fE;i*-9h&NPq0}9Fa9`P(f
z4v;^I9H1LVbQob;)fM`yoR5?<<4vh514vWV3+6TBEWsPAT8yVw^#tBf)iEf`s*3UK
zq*bMm19XEG`IGOn=bQohYbYNn=TNQc4nUf!s?2M~xeafuY8sweRXN^JRT0Xvst$N|
z)vC@y4$!qz>92G6NI8$AaKMFC&|ky(!1n-A7KNSw
zq#?7$yk?xo@J2G{;K_d(ry6e{vlGg)s*c2=RgFOo&=o53+INNdo9eI5e59Nmh0HDR
zgJqdFOv_$XFZpd0`bI7Lo6kRleW31tqlVV#*qgMb4kSG7e5k*+CyK1I4}O_u>}_5%
zP7l2Cdx$HROEtT*MA|8XXAMN{i(gjX*h^BlnZ@8A@2iDfF;O51_otXFJh68*s67;H
zgR#z>wVYifxlV?xRhovw&D&))4!nNeLKY+rYmQGQ{=}7?BgaSq7Bpb#g%q
zq**MGOIFHsLj&v|`YP^TNJ_*2!<@D5{K9YQxKmw=skr`1N7U^=Q%drR3wtDH3xtbp
zhIQW+7A0!2vwUqGx{Q<7w$_H2e8O6ZXQ{JB#HvxbwC(KE)}=+-Zo!0XX~U$MsPXRq
z8fK>I?I8PT;>x$BwJWoUDQuxySD8h8ci;arVpS4T*tG2x`XK{tQCFik#2R~$^+~gg
zGqjTyxH3`zCMnI)tc&O?uK@T{L?#t=cnRR&6`r*i??bD2?{RKWF6VYrLfEUHzokuPyV%aKS$zskuJpj?U|6_E9V73{_}(AyuEa1<0)y7y
zo5y?R2ELOHbSa^v
zMZeUuTIxPktt$;E5WMM?dJAuzk+Q;+$~gbT8&vB!l%;yS3Qt4C9moL@qlq=`IKmg_
z68$xvkCe<=DD)^Gjrx1Yyk?w-@kVoL+sDORnu(toXFC3}nhH}5KCg>6s@F3W;|%~n
zKI=9ZtIR9rf4otl4kk>>xs{K!^UTkpQ18O8jPu27ivLqQrF_~sQK3IT3ZN5j$_()~
zf8o2uc);a`f96Os
z?8W$%apvF$%RY>!bYc3GOsHt26WwH!qeLn
zdX|>GmA_y|pb%FA8D|H)u}z;Sl?UP{ezoNz<($dtv(DwnGmO5}yk?wHcq5~CP!j*f
z@@eM+33hQt01ZDHCcI^+ZtNxMIQEgWy_DocV
zf;ylg+XtK_TVe00BJl{OjOx^D#VZ;R4*@lrdLC)1*H*Zju1+PMbP=BepeLj8Ne2)0
zw3GLBVsW+W4sNcdoTlWlb;lS=<25A^b8%XY20+dd_HM`RY#|#(jb$`)zaUiFX?{%`
zkE9w7GG=|LSs&P}GK8I!IBsWb(G;hRM-p3JvL2Vc%fCQJ%g0HZy8y(CovF-$N8{uBaQScp_i+YOis
zebAp3boUVGg>|>~nYg15^s54`TdwqLrTa@yk*41?yTQw57cT6e)mOv5rGIj
zWkIJhBUQh+p>?bJUUyD;c!!i=ZR`cXtjvb)Lusw9gsV(ZY@3RRJuL@tAfWuRI1HKk
z;*@cYhqesbXz2ldX>6xX60rqYZ8dinlWT=3XL};L7v6to=^aRz?xQ-2=dD+BJDLZm
zLc61pj$XZ!?P#7U$s=wkBjR@O(To=z3EnMkXrDp>3JBa?x&zm0DfZM51F&g*N?;nX
zjib)lO_aVr82S{2l^&vFAMvyy`e>^Vkd>Z$F_9})f=Z-1?rVO<3I=gl>9P2D6b1Me
zhk$(P*+R4LHQ8v(Y(KMuW+uI|l$XVBkq@z@LYy0dxUlY`mzt6)OQf?ZsLCt1iFhxw?Ynr7GYnelc{gj@BEhJH=XOPG0S{*$M;|Q0C9}!6Et4Um+
z2^5c~V&fO_j?aVnj1EHqbHc=n4ufiR+&j+9mrYN0+4DubPdfFsyT6Ud9YAuqoQRz4
zNF3?$1lCI?fiI6F)*|eZk)^!paV<8K*6M~59iJ}uS&O^ji3k{mL#>5*K#=mpIzHF6
zuvpI~RvTu?vuR(h>VR`Kw?B#(H?-A*k66vaF!2`j2mK2x=d#3VT#h)dS-F8i9yj_Y
zR*!$$+eq;DEzX>q#~wNY8FBW|J}eNjhdP^-fIakC--tc*;~bWvJ+umM)(#ZuYA#NF
zTbz1{A{oDVaeyys`&p@p6*U+%-c6~0(Jk?{%XDCX;%an-2uXE-E%Z7w+C}>CY49n`^?sh^$))dj!{m
zmyTOhP{PQxb6-ScTDxPt$TVC)$D)6?^>A!t2hk{(aW+P?e|yO=fLj=HVyZgk&7daM0V$xPyxg8Zc`Av7pmHttsqGXDi+{o)}SWp{0IS#qj2i_&tIZDFjm
z6vJYXuYw@Ald`0jEf9N4tOkG8@1U@)eAt*9R?QB3XN7$eQih=j4#5z_<*Oy9P-&Zf
zsI{45>0qODZmXt#Vg&A_rKei}+M#-9G8{$4RNOTbCInpLK5U3Jzxv@ez^4gb&5d^9
zJA(pUI0F2G1ja3yF45Y^zlBQ;*|Gdc1$7>g*Hf0J~<0|JO0}|anuw?pBg5{9f
zErFqe^BcPmvCJ0X8FYKU1=p>rW^Jj}e@rzWo5~!UT8_buNY_!ExNP`_ekv=K=1yv^
zoJ>+W+0M7JEw!^NeHz(B6)F@Ra2TIyXNE1L))kdY7n$bt9>|n@u;7mhTVi3s$U&O)
z7n@|RWrP~XMmNs;CF&1Xh(QsEY{870XSPNc6UKbVd?z19nhs?gvjNYTRnF!mU0Kw8
zQT*eK*XSxRXkFuPQ0{`ex7J^00nDlmxFK?Ob3b}Eq6S%HH0Psk>|%5#D%)Rijn2iz
zPz#}%6IdX8TjIfS?q4$_l}4f>ATMGk7aqo;JQ-TZtU*zEU|MGlxNjc)SRRN`DNHV+
z)dV8{phPbJr*cF>cKrG`CZXpaN)E*nN##ekZzf?NPZIm5Fgck-{%@$NADEY_
zvO2Yh%vcg`j}=BvQrk<(Q^`75`8aWjkt~`ASTQx;hjfvwz6x-@2N2uIF!F+@Zfc>)
z@IP@$_5Ebi2H&IMwz8n@r)UceTGc$})K1Tczos&*e9kFk)49Hf^Li!^mCDDV!na=E
z-QQE4_V9>jUug^SeI~iLl0+qQ*$duyi!tcu%r)X-4x&I=VQe?7H5ss-Zi*aD|9&zSrydYU9sqdSdPWe*D@E?wfM2P
z)g1^=4+@sj5OW@4nN&J%Qb7q+v?mU+L_r87=*jq1U}Q$Z-5?=FfpE8^R0X*yYi=W7
z__87kE#N-$y-bsnqj5-&_Y3rOH97a+OfvnI=hFY^6-Zeg`)vNd!Ir=zWAys~TWCG3
ziP7(^Y*AoE=d5eQiN=tAZ>Y1w)MNXG==WUZK0O!F#?+Yy^xFXC{wBIJtlZ_>P`+~i
zZc(iCZ%jf{eJR@U%DtetpD6eLOSS%!gha>E6Q|Z^2n+2P(N(t?3`VWjEVS~HN3Box
z05(LRqH6sw(15fW8lQ@&^}Yt}?@{YFZpyAOYW>yK@}ny1eaQClhI)(Z&k0HZ2k|ww
zi4ZL4hKN_@$8S(kz#=wZEITStNTf3D9AuNk_u_xCtecVw-h^kMoi>ovZa%5I+A3m6
zJqD!O^`QA;+3E$pf?^;7n)`uGvfoegzF2k@%IiWgOz1xj3h;CoRvERqmiMcQmHd3e
zRuD_%1OF0!)-O?NW?5j_nC6=L%zN>rofC1(i2D^lIj4kH&|hFT+FLS?;F2eom`b0R
zqb&YdVs3
z>*v@FptPM&>7(;~(PX9JIDql1R7^fs3QK)pk3!o2v0p5E9A$IlX3qYWk0%sgw~7lt
z$WA$y%cbE%>Bp*vJSbEe8he(Kz|h#;o)OaXgvKsu64L98E8vG(1I&XgvLcjI+w$&v
z9l5lkdm(XR*j@4jTv@H~Aux4ifiq&5&-hsJ`ov>))rN%Ec(Pl;i26P@
zUzd3vYY)I*0OSzZkSD2e#|F8^~P`Eklxxt<4*ZxUQi@*TgF`^
zSw`dYai2G;FozrfzjlKW}r
zKwHQ^C5FDGm30biP|9>rj?3*bqddCIFnx{=s4r`1B|FITS`mR};GQa6YpU=t;5@Ma8`$nJ_HqjH0c!VBYLib1bJ;`=B@pM4$$eP+YdFTeNcbYkyqXn6
zL(RbPS8bm4-T6E1>}%1MjVsmLBp{U7)(Ed_DTolML^TQOt0f1hJcA%vQCYlZfxC;%
zH4DD}iR!}1M
zOz#CNB8eOFczPLJ;+ifn<~ZuJ?jewrask5eWx^VV#z9z*knkQ#+D}gy!Z!|SzwQHR
z**K^kBPu%+#x)ZzlHA6N!r^#T!S5}hqNz353~=xM76{=@j##vza0WA|g9G3xi7qu6
zSp9-`J(YJ09^a_Et3x!*M&exwQN?hlqD7uPr98vo8)YB7yp*KBHH-1XhWFQoZ(-!w
z=n)MCfCL5uLt;32kp$$L3@Ow&8@mCO?tP!dm;?9hZOwt=vL#RjY6|4)&WuVhO{@27
zJjD{TPIwi!-LVm7k-yFY%i%=&fn~ZWk7`S^sb**WdNb?Snc}<~L}&enKjExDt#sTp
zo%MH_i&=j+7&XQrWM=(|!Fsd)CYNy#k2KkK)*t&Yl<4MfQHh#J1ks5Pzhm)Qb!eTw
zds+Nxxpau?0c{Engx;2kxk+TNnz?pTJ5ldV})_o81#q2(seSw#qkN!7l-eMpd
zz8XLBex_VY!y8&Z}2sNa3yV#HE^
zvq73s%N#c6N_Hjg*Rh3kLacRn0$Csv!#})Z+|j2j>zp^+i^M$#uiT@}IDf$#<9E--
zlm9Z#@pxn4Ycw^zeYvCX8$T(-=FJnMY
z$CLk%KcE~t@hN9G!Eld?2!Qh^{WXG*l(QI*jPqywWm)F2Qb3nZl(Iq}u2a#nLs%&5
zG_ZbvWt?XWC6_ZJ<*dL@l)ayil(S%#K);TMAk8|<&1=S~$6F_WUP+jg^Aa8z=Q;dk
zyI%Q_0_?mZiF*PWY3qh3{~>>Z7TQ^B`&Gow*
z%Osvfmn7;3Z!TT_j4i;y2n(|W$B3V8)mVnIve`03!EP~NnZ$fpw)}W0H>~EDj+aNWD|5lfjzvOj;c#0mnpk3hTyrfanC8b
zXWcL100F{hqaL!KclDGOrR2Z5sP5ZGM2Y1;8HG$e2l?67RuD_%8L;6f$xA3mY3FS%w-WMHt1&pe
zCc$60dky@B)in1w7K0Ht^KfK1IPt~#3+sGhuCVxHiCM1U5%tDjc(etU9`wC`@&3Y{
z473SdLZAC3TJ#rw`jBTCU<~>G!WvMM)85Q3>u>Mdijd5oXmQ83_ahCHsG*p%6O6y`
z&we4D_R;?Q{=!W)^rI#^@$a+&lpgF;`oV)OSs{ItQX2(d;{$sXl6A5E!jUMO!^dlP
zj`A^HEzi=L$je)2w?yHs44e!ohKJNnIxA3UjwT&rlkAAYW$aW8bsB}5qZn4;JM*51
z=NWHpd@`q4^s!yO%Gk?f8`^Y%1s2QA7uZjsveBU!gRR}}peu)ehX#piL9t$@6_9_w
z%xZ~$Cm6!MQNmuez?_yw?+MPTlvX6F+0nmHZ7{e?;;FJOoIG}=v|16QEdm|9T%qml
z^^ZvdqQ?~xm`;U*-;idrH278#9T7qVJ)SCZJA&98mh{}w_7iVm7#mz{s*Cwz#Y7kL
zrTUu3#eCEnk)^uRm4d+rxNx2`@t(E?6Y2_+%XC3icZNcAwdg9GivhvL;xvSF4&TwJ
zD@4gU$Kfacfem$MYr!uckUk+Dl{RaHX1#2(GKq3kq(!3yTk
zCwMbb+;W*eog{PNHclUn5a{+;#v$YCmq+}w=+LYo$-uZY*rYkJ#j{GNAZw4gQ
zc7+&3zR&mwoeJvyqRm(|Q~6|^7H~91S8nCumD_Wxh+YqeG(H3AqybfTu422)V9Pl3
z?t%@@Rox3%EmERFe&BaH5sBvqHb;lWoJJTY5H@RjbK*D$?Ld|Vzk|?K4B%ABYH-R$
zyYc_f)u^x8M1I}@tI{59wn5Q^E*rPHVD!-d1TZ>f7_QaBt41<5OVX$!LDLGD<@itq
zFiDDhgk6fYqVoYY6``fJ64>LZ)#Zur6_iF~Gj53fUhdK+uvc4i=7^#q}1e=T-U0}k*nz`at$KL6mbpBqU%*{=A@|GMAgE9L=I3mXvjq5L*H4swmJ~R
zjZ`AKTOul)k~=USFz(SC&V_tu^QhqW^H>r3d!CLkQyeCVQPGjIa966H-7$--us
zQoXC8I{zpy4Uh7-Tp8s^#SM0Em{DGTv0|&3;fY&0YKE)wLpqVg{}VR-zunf(9GEyp
zVWd{&%JIvJjT7ksKH_>*6O*hM&c)EZmrF&aGWY%tbdx`f>^)LOfxX+4PYLz{zby7org>bj+nb!LVaBT
zbxF&ptBKkwkP7E0f%b>YGwePQdrt-Ja<4Ea5LOtS@}QmC2R8d&{G5XUS5a3u?ldw4
z*SYc{)rz)D&1gKmyP&LYq{fticZ2eE#NFYJaT%3^4jQ?Y2&U|tOjOW52GsbDYdu-L
z4Z$8Ew=r+GhcMw2@>^@QO}k5-)bQx(Rd;!HLMdk5tKXHns@=Yf8ljyTx%TuJ5aZk7
zxg)f{Qby?s=B7}E)9)d$&wepNj(8Qzyw!$eim+h8YYEZb!@
z$V7Eane4q`ly+iy1LfJJ?94eUr86rVF^7qy$sE0~H;G9PJiG)xg~MT$fhiDI{Gaal
z^=P_uY0L1}(|1xhh82mXw}XIm$K&PE=L9}L&J`cPx%C2LGiXOeyn=6S;f>6)RWUcO
z{8f<1M^BbP1|SZ39GCxgQ9*j(Yk0V?IQVnCi9F#jc3WcL~Rf^D2I3oI3pFj=(0YB;~w@pVmCz!26E2M8zQfKe+FxH|zy>1J*?Qub?MWMja#LJsQv>X1c1>XGl@7xs-sJ{S_T9%?g%bA
zet<^+BDQU<(Vbm3UAImG2=FWlUUp$jpL*uw>^EFi?jb$(2s
zka3;&wMh}zc`qdwb9I>1Dks39WuF~r1lexD=A4}$`hM62}Xw$D#O;@P(;e9|e
z=sKT@BAC*{TUKp?M77ZE?`Q?&-ymC8EMc;75a6FV!?1avklB-Lp5oCAqX=FSvnTm=
zo~?&d+6MS1q~`Lkk56W!MITG%4!T29Ljgc{+uf!o{X(k7AdB8^6GE407SITTB1zP^
zLzGeqX2^vwr%ja7__1`e)<&jQ_!Y+pVbAPP?_G0iV&00=s*9p#>Ih(PRG2D6Dk8xCI90W}qiW6vwuXaxJ%ipHigc
z*rpbLZ2$H%#Js4idH`Mwg$3Xw?-P4j_HL#}dg~Y(daXW*W
zoc5NtincdiN|q{8wz;FlAKT^xkDnsP>{_`un>NSqT0NnDF$ga-D`-6zYKsd8AXu$z
zm}L=AA;VFcXV>a)K?fTU9iojUBC~6CRtOROp+#;x5Szo2k^;*T9-NR{gHj7HNn+SN
zRUnV4&A^^oTAutK&!BwhuGalpQ!RM)jK1dC)!Kn;taPGuVRyC_RByxO6m~_f*4gQ5
zQ#z9}Qz@63wCrBRqb~ec5;&s9gRDE
za+A+0X(yW;O^#^ra%pktOt#y6;EJz?1@d_$^hc@oM{=?ZnWY}#`I;y_w4qHDU~S?9
zSoFZ!Q?4n~y|15_!wd&0*C{DKPf}7^?4Z7AN!a3htv(?kcDAN6Bt
z%2Chl(qAy}{vd&c+_Q-3bbcJmE1ZuDQ{km
zOtUqmGx^uZG+#~gK4$aK9}`rN9=qv(EYq+5lE1}9G|UGM_cWxmDAQc-hGqJtlzaUDls4zgoRx2@qZx~kNljUO9OhfAQwGbkc+NSUB}jx&g5Ss
z7lGzkxiH^axb4ROv0Oa+Q~nm0C$6W6rc^|?G^G4ax%hlp9=WJcv`?q3T;>x(I{4DycqZj6ByQXug`qDn3%`)<;9E-yFjOq3ynI#HL_}o&5LvugllL*nEs9C5=5Mo9h}I05(v2*d
z4=lc!G&-zes!YK!=n)Ro9t|o0p(X}s=)Qc_Z@gl3n6JOyZi*T=pq4iuV+=b3jfq+m
z*zdfE-cDnC`bPa!ZR;R|Ig{K2L~N+qAuJ?_kN*o9-x=$tylQ7(k#UluG9DnR$ru;r
ztCS~B&<<#^UvnkBXT}#spMU0axdjF%$P09;w}jBZuCY8*nbI8qbgKEB?mx?voJgk*
zLrWsp>c+U+r~NZW*mmM_xXye8a->0yq)H%^b)5P7>QIxFN%B)&8`|ZBMVIqoy4?L=
zTBbd|YoW^rjmd9)o;D`G)jDEKx4dF>RaA$trKL0Z*XWQy(?2$XWj|k4H*83Vv_}XJ(hqR?x$1
zHNC^uOiy=gx|WYVhX_>GUA;gyN8OLL=JrDG&{D+TV($n(pi?l%4TbycOzeu`BG1$*
zAc}sqVO`g-
zu%n`4iGmPBbTt+f3yKBBf{OSmDkv5-8Wysw3sDJTkD`DbMIj<3Sh9#MHbk*tzai+0
zohJN0pP6&+J$H8#ec$i*`Tu^;kLMwG_RgGhX6DSy>F3&XrSn;0-pKtBA!@f*in(bdP0r#+>??R$di}20
z`YnpkNf03k=l{a{*LT!{r`;raSY{&`B`MxV+$^%LT+H{8g~S^cNH9}`ZiIsPvIRHg
zon*2FFQcfNTSPaVw9Er7GL&Ga`cOA(aQlSJId@j_%?x=>{1|3cGN1L@OZp>xQ8Hg%
zkK9HPa;Q%zwWC6$X-dDOR>9-Wn(ve1e3
zgrLMy)bW`ystKEevg^Rv6jY`=h03Cvf{^^&X;cGxtjP+kIDi^&+JAwJ<$KD(tETdb
zKPC3mRhzl7+Few>H*e6YRlS^B5PyyDA^)yMeuFPgRrIH{x}$0Va>--p>xMaexg_O|
zc~a3rV?HIZo8~ao=3tlcOGlUM-hlEPx@-BP=&q8w4s?fG8%1%Qj}paIT00c|?-MJJ
z7~w#&88O1&Ul!g3VJ>hHcrGaUFTB8IBE8RUBGWw=mI-0tp(BL>-g=1L0_NkOVVyPX
zQaC)oy|`J|+G7$ITD<*H6f3*<5Q}BUf=rthe@2DdN=C$%w+G5wIZkh1b&^-y>7bRo
z9Lf{t*h8z}`%zlS5KR{@|A7&Yp|hFwSgQlqh$rV&JQjk~HY*s5
zvO|3Cd+Gc6U(P
zKjwR?LZv~*Yn1RnLx?792EMg{j@TH6y3#e*6pU$4lCYa&J6l)wLuYIMiIUr;UXz-VX7yl5`D>(THvBx9ih|@1cVA%F+IoBPBheO5e9^&-baegXB?o
zxKO42-(Rrzfz)o)^#2}dB;tmU(lwh3#(YbIKmnVS;2lfg+@%s_BYGd3tj&HZ%(?kpK6_?l@Qg0)t
z1bLxB2aL>oIcBI$guDH>
zyP4m9*z+pvb8fNnFS~4Bpw`&OA>6WHqMRx~Hs;GQyBMt4%S+v8gKR1C7rTugL!`UX
zyBu_t4QQk(F<+j!?`k!r
zn%MNblF^e!@kGt^^Xt8GJdS`tO)r>TYi3c%UG$SgGZ=#si%ZSLYJ-%!8R`P1e)Gme
z>Gsb>dBdC+P*FzH?;wi*_D856+2<79!Pb36V0yQ%>d(4J%*Xs861`Y0kBTZhvJK;B
zR3kE`VZ;?|V07@^hcpbCT7)(w0QJ@??`P8`<65GQw@~YcD?!(gpc&)uBLDERIJS36
znCzo8%?u5O?8N8*UalMJs{}s`*E&-@n=#$6>P@b?%|-*)cosZQi?BNoSiFfUM`^a@
zCR>Qs3P>wjOtDv6Y%)&u70m9lndxD3`v8Nm+^2;5ak2EmnxL5
zxw2qPxdA3VFTlVN%}du@Fk(R6RFmH^TJYK{O|VN9?e{M@8jn>YFp|akn^Kk&Vs9THPc+JdGr_ej1WYZ2g}~Cd4Y)M%g%R
z_vwhp;XWPFs`Vge9}K+17@sDv8@P?HS;`bEMUIxE#>~#{`-f_k+R)yCHIlgl3@gPM
zYWwCXQJW&fX6mZ3qfsJ@oEA-a&tDc8i;3S&P5d5WtZJYdO~Me?#BnBI6Z)3Y3YE#P
zlKJvZ=Ale3O?vIiZ$1fHCtpZX7XOk=La;M6*Mp+P%guVW(%{fW!`qlMRNCW+ew87v
z*5Y;qmYLSEo7qE}84(4uE69FME>y>E!izPbQGPMYhTLbV
z+&xW&fSYOOTdDEQth$R_3H6zwDWf7wK~0{5-5s2op66*_HHb
zcyq0pqA!&JvpVKL(Tp?0(U8ybHVyO}U%3m#nB_G)R4~VFB$keRlbW{v(Vw}iN!`5)?Z@G)|riUs#>}Mk$H;71cD<`s2}eXp$3aC
zbj+11dn__$7wE3b8nU0#Ju5w(Iaq}mgJ|6Qb9
z-YfOalSQ@@lHaRWL0r1p&NPA@=p*3}O?bXd=!7xN+?jdD*dHlOqrdP%qYya)E;|v4
zHI4qm0ah512-!o3ZrZ%Ot_3=ZBsPVAdy&mbvoX^eAh=k$do?7`O2_OgXLM-3s+B?I
zqE4}Aq%#fB4u%Wc0ClFe|GojbfOFWv*n)}b4#pobhf9^C1EL)L@3a6bACq#mX}}CT
z1R1Kuy%JR?#w+=~Fde>xSWsQ2RQFq|RE4w%IN}@htz}UsW@p49QRZ~GHZyJq=zGpK8{M1fa3RWW
zG)g`a24;vWCQVP?n7!~JJdDRu3%ZpJPKwiFa&_~H8=n-vE``FgC&dqX-SJvZc}u&p
zqr8nwiZ_>6GAT|D>e!A-UHnVy;=S{YOO#@st6;(>3^%B$D-=7+VuLAjP|1{>o^wZz
z&bq-FABv)M>L;V7%Cx2~;)T?hhG=NMykRX#!#ebK(MINc`R=c|+L&pyQ6O(+zMR>j
zo8{NaD-&-r=0Dzb=09X`{?De&KOZa#l=%+@8=C)k6-+|4kE?12T4K{}8NoXeiovIz
z6)A{6XsY4~rSD?t&5|0Z`;!{xzk_r)xRUly<|CEnuHjM+Sf
zI)vh$){;8elC-^VZq#Is73LeP%*N}2!D6Jt!a>>Npp?0kN&yBI+G{+xE;gzjo6K^6{+lare2#bCi!(NL44@FOWQ^BqO3E
z7%}A4^&|=;&nwBvQIcjP=^!M*`h#8An0zo6Hcr2mwWqDJ*=Cw*+Y1<7*pT)+)%F0%
zRTnm9YeD{?fZb8hG0MqXI0%3Iwh76RFQIUGqzcEMMe4NwX0^MpL8L~S-@g!QElw6T
zB%w|vT%uh&LAnbYC-FT*e?d7q-EyRI98sn3TlVJrRNFD~D7;vx(tgMLgN2Pbn!cS)
zZ+jUjv7FXk-HE|%p2z}gyVF(kG+mA>764qFktT#Kvv!6D&FiV
z?6&8KYcPawuV4cq91Bx4;9zZIl^WuAXIs~b)wb2OrshsQhH9aqTDTXz?x8rX41o>j
zx3$-pDl8O=w11RrTf&t~0Zyk1&oc*R7ob@2&aqq#U?yDCNENm<>388If~+QEE7W2k
zsq%3p3twWHIeOZ*&*H#LgB75Q6S{7lre7sIG_(Y?1&ZRz2z`BbvYPS^Gs5H5
zOyM4=Jrvc+qHtRR7JOvK39x%AHs8VK-Mj@{c;=R7;bu!v1gcp6=EhA0a|TSJ+?+V~
z^GjD0Bo=+JQodk=vzIrM8?zF4uw|bYG&GXwSY*l!%VN`+x{229A7=*DEtbIb9>&VM
zc^Z~|8(qw|ow6b9(`I)59PsLBXA
zBWj$w9L!q?5HTmVN{9M8kjGH{P?q$G7ku&8CjVQU&dmzYN5Af9tAFkr1wPgCsG&_~1l68H
zL))SR>R`Esg!e5mJuX#vpZrYw2Y?w;eWp}TS*oGYJBn(>~Mw6c{%2ZLW@opm_Jnt9++7
z#`Bx@bNr#m4|XyqGwv(~WpD*spKOM&(?Y5F`00&l{vJlfRM8V_&=4=2hvV^o;zd?v
zpBWaH_PP`|%|rhcGJ3lMWI(F%_5j4sPwa{(|J8VHNrv)qyy8IKz8OZ?@h7Cyt1
z|9~&*!tR&1lw>I{E!=vw`s*t`65a)P)Oa7@FVhmoLP4FcX_t^Jojc@f2mv)vBukYj9%#UF`me+gHqS&ufBYwy)*D&
z*Aw7r2CVT00BVPfHy$ksPr}a{?+EcUjn-X`lo2
zKmDjg8x-*$d{n}7*;_U=WH0HhJJ#WRzJm(yaoS>^2Kp{P?VY>tvJ~%&BYl_G%p0fg
zG679ELR{E)sT3O4)MP+bx23BjK;epmnc1%Ln6@W$l_zY=hA@Kq%n0LqqyOSh!k;Yn
zI1CFPfj2f%)Gc9Ahc{Z(Li7Rry>b=x?n0-i!3?{UisQX($vTF`)i&Bd^=EMy*k(&M
z*e$MsDF1rc4i0U!m^!q&{;9c&S-ZfIs)1rI4U1{gXfe03m{qx2_&eQVx%aZ-Rh28nuX)HksG>iYmZEUO?1o~Xp7#*ao2``H1ZW|UH`5*)M#s(~p
zU01ddRoZc={+_I*ycU)icVb{}v6)#*%V&d!^h>q9=@(FWx9zl9*vC6VZuax8z&q(l
zEZBv)qLk(gMo9mImhzZwI+9*r(4zf)`DC^rY5%#U(*9P@(t^GX3AZC5D1J0q8CG5i
zDgNA%?#yEX(yzGKWc96Pbx%n64hfOfBPOdlr3f0dr1umlQiUhh1Nr3W%fiZ@~m73`Un<EiG#S-i`FMs>cjs&WI)vVT^ChRUoN={qAC7NrGc^g#p
z(I=szyJ;yyBQ!Pt&auD2d9hUxSQKj>#Ih}$Q9l8rHn1SOQ
zajcSZ(z%dwW567y7JK4jm#OQFpZ=x6(fdq5
zsoph@law|yY@7@){}(s;T^X2ns!^ZoChgc0m$jm_|BGG>OE)Q}n|!Umqi!mb9>3MXocrKoGkTYsuO)`=y*hH
zRQ$jCq2hl=i(Gdro0_^C`Pcsp;UufwzO5?olhW5p5U|U#8_^$55iQMtY)J*Hz~D^q
z$6#Gep<@z6T4=zU$%X{P=UQSc@4!=OcQe;1>PUmCiB%vb#>!y0TlQ+g9SyMxboMLM
zh+|B#UQ@IVQcMm~;OiIsNR|0gBGPs4m9KBmBf`(g$W0;5ups^iLn$)dpqb8-0Ll?}
ztQGH9!dYwpa@W$$^q>zhYV?1S`MlT^^`I$8DOs=s4k51(v!E2VMe^0sXG+Ti|1wnM
zQMTnZ8PTzHO_N(XqR=f$*W{0AR^J-8Sa+^(fxn&VJ^bC7hB|Yp#{L_FQoNb%ofa*Q
zZBO`A!rjG5ZwlUv$!W8F=xSQAcN^ZK(Xn$0KVy-C*?P+SJTwJxTFCF6Ny1cN6G%)$
zUu+0X`RgrVn6|`uWvD(fuZ)z)?#Lo`{c&&b0){iJ8F)XpW8)?*;6+hp_O}@(y|+k|
zDy+bS08{rggr?QKA|#wDx#8YiN*k){gn(i|2b4KFEOYfcssPjK?ivt!15H+4O;)DW
z%?~LKQLf%SR=Ii%S=CL0HAqD@Q;lARm@67h;ggs$T?77K<6dxykBIXxN?@;rbxp=>
zh+nS1+4jx+ORaJblSin=4LwzjPuNyFRdhWQpQ?#{WZr>_I#Pe*qZ}yRV{Ul40DQs=
z{Lk=Fi){aYA@qM8B4qpj&nJpZ=uCJ?1siCoTj*yqYFnx<&b7qSQa8`@&&tI?p38ys
zNYP(dk=Z51c}oUi-&`s%42hs-r&HP)v1<)du0~4ex>iFHYDMbM&Xoj`%8@$ed;xafEG%Bo1DGAevn^SlaMQ(-k)SbAjN$@a)%J
zvti$jq<}$wdxf~bl$zK?M$+0PYWDA1M$)lLyb;u!e5Sk?EUK}L(EuVdxm_yd-DXp;
zf7=s-h149(W{{w@8s{R3EwaZ{rDNa2NEhjQ;KYjdJ(P@n4@;KO_oy^+t&i;Z)>G6h
zKHRz(n@A4K8GyqL2XOO=GUL2U>6(rOzT%`BW|mb{E0{a16alg
zvw(9-bo!^T+J%=>BgHG3I`e7#hXr`A79gx1Ara#n8zq#Rm)qN5Ek2mRym|phyA-$a6N_l@uJT~&%
z^iM_8?_=dh->zdorCDo&!2=%-;qG!s~^wY(J58W|?N&B*D!f!YC`xj?>>&C28p
zE;Y!(N8}Lhz-G%XCx~9=6u<8CU=g;#-wH{~>-T08Rcp>ar;F8I3Kk5QNJ?#tUsv{~
z2&L?h!@Q&s-S%WFGkdb^6uaHn_tD=(mUtVhWQp%m_LTR#DY3D*03VgU>w8C`T`Qc8
zru7Kab$MM+{`EWmXj;&WbBeFx`-{P})8K~Z8(uv|WM+_DtY8C8Sg~FkP6sxU(#3a*jG~N
znD8+-E1%6Aa_Dd46Pc`L_rY3(Y%a9f=VWtSo#@Yos@mJs1;>YpR08l8OblUzoMXJW
zS@}6;!Z?+&)_YnOr@5#1F45Ac)bqoFa~S0(e%8&(-JAsGX5}62D{GL!&C1QdC<5`q
z%*(!?eB4oK117n3$mHb`jI|Q8J?CIB$r^u6Re882W>t!-S77Yq-`)dDN25TRWr{nw
z%t(;RuHU3)N{xMq2nc^`AV5v$o?$TahBEWKB^F!zyPK8A80wnXLJp!4lEM^wzzE7>
z3#m;VeecX-Ob6_Ew-|&wd$V#GxXF@tzZs75Mc&+2@}Oo)nd=cO8(7(S~`QS)KoZ4;Wj|8(>D%oMWqY|-4s3gZq%-|z}
zcB-?24VXbCcFD4S4$+YFYagRO2`_zGX^WjI+;txvICedYy#tKD*-5ExWjd;7((jSU
zp_<7v(M&oAD{+?(aZ`6Tsdq4`yKCy5fDx7@LV
zDl-RL*Y`H{j#JTjBhEqnqQ}o2QaAS}RUa+rBTHqQf3f>Ms7Q8H*;h>a7hwfV%1ZR@
zjJ_r=qi8MG0@OfMv>qezr$B-%UG;wSpX=&1wG#ULLThedG&|@n
zlPK(x?h~8R*lv)+ucKUCOrrI3Fn2ZXtpo4%L1D7iaCdgg6jCo@3Jki$6W_e_=T
zR8&=T$M4Zo1G#Tq)tRbJ%;%Q)Yzuj;NUEs}QXi|}6v$R?q^?9tUY!V4zacju#mbM=
zitA{IFE03Z^)Cgj<+P}O%t_Y2%<<8roYDHFpjB}P{1KvG3eH)?1l=$;XG!L)@msxw
z57>xc=I#d$q)W9B<>kR?uc-FF+aF1SGmYgj<>XGAG8L-|I$^75&zx#%h16t!xf*!6
zIg>odCNk4g`^u;MaCA7ljOY%zZ$TP6ThyL56>^@7p(=7_e|l68^rtrpbF2zImzI<+
zb(Pzt{wAg(n{&*c^i+}2uGyK_9We7+FtI5Nir0ax#|jK*V0+d50*J~wUPXS$q;$&C
za#DpIH!@R9JEE+*mrXKrDEU&8tR{|oOmX@%;IuQ_8GMS_a^Bf^YQ{~UOjYt2|K{U}
z7N6Q2pF-Bm+2*EMtjY8>f`!c-U=T^nG1$`ieP9iFa-}P?m6`FdfP(lq>KcWdWaK~0
z7)x5Vl>_Mr(^z@Q3ON{(Jf#%_m#G^JQs4ELaUg5%0u48-Ly_BZd}j_ctwuo{g_fJc
z+3hVKj&RUqgTnTw&6m#S0|yc&7@^Er?j^7yggttIGTBEY;okY+w*=6U-Q8CxuZ{bg
zqx;AQb!!$f4mVm|GkQW3G4z#OA~_#5E_iyI2m>a(bNE
zQW@s(adV4x3TaFk163J(rgLEB=AiQY?wQIE8--z739OB_bZ#f8-wL%OoA4Pl
z$lDoeFjwTvu3*glB+JaQU_#uaHV0t4Wu)r|`#MsP%*(XpjIqaiBW8>@JueC&f!Q4Rz#is_{2CMtm;p#vt`>P@m`&hzFyl-&biNI(
zCl)Xji@l)MI>kUT{W3Z!tI2eVYX;{eox_DrKFJDJK)6bb?;(R{s9dIc8<}-ZR*1+t
z2Y2W=M+s*BS9PHb+@fQ*^8Q+X8;z}5`;pqoTr1AD;(u}aJbLJF9k==*o>RkvRl{E$
z9MrneFnzmV%vD4Ok{kanm~a+fBlY>3da6yGid6^A{a?$W5_hG16<^rPp40y8It96c
z`Iy6~XWM+75@8U#Q4tBO=7c34U@1|6)9UYXAOdBxE<#=%^4$y*Y1MA4|6>tIklTgI
z|2GF2>8(j$hqO{tV$!$ZXs6jTRBjHWK4(*;Vv7TbTd07xic>`&8~}Dc>Llzejb*Gz><6(0CIQE5MpWd5BW=NJWapAz(j
z&1S>we(-cHU@@ZG5ehaiQd*>!K-FHZ)!D`p>+m+<>e1hntAF9aG^Bw`zqu?>M-k(4#D^?41aE=QO{>eA60RgGGGHr+8p;o$;aa{XzQSg8fg_Jqf`!b(f<&sf#Sfkf`0$UKYW!U^x!dN#kl2OY7tqY``>8Wp-^
z!_mZptrV{FHKRrJSJ7bboA+Eje-O7%L08YBMrdGdcIV>$2+u#FA}}ypeHo{;aBL{C
z4!5EWc&aRxV~gqop-=3)zxZDRihcS632`$~E<<%~jf$~mzaWuVp$^7UMP(hE=J}WQ
zmbg&`nXsj;?ZJ2_=lo(-1+&3Vf7S)T8D;&O2@MX2__J6Lm5{O6a{8n|RGjq5Hjy6@
zdJsOuk3yzu$uIT^tpL|(P3REcyf2c>Acm5dWupB|BAs_i745MvLfJ33fR;y;K*y|L
zf1BIPlipMl0Q$Vkin@zL?0?PgMi8;lC#)EE=JuZf;Ai7W@-
zc15b;%7GZ(2TC`_(5YGrKL;7*nvLls9jtA~Og~iOGXhTT%BwFH3;akiMbT_#n{2L(
zX0ud@TZP&1-thVpB&UxR`Ku(QwAdY$D!QF~SB<8$lVP-z9j2CPf>(kBU`jd;wVxSWq*4F+^{<$W9+)0j$42k(dF%u1@{uI%?EY7C99>y3=CoYz~
zMv8#yu8}@-k#LQ-vC6vj2r%}R$uG5-E&&4r?t%=HUOT1el;Z=|{9jheX
zAEPR$WQ`Xm+g?u`zR+tba{_vl#*U|I@hkSS0bnfF>b;?$$8tSvHMogCHnwEQSWL3S
z#;SjGuZfL=RBiWT)=6)FJ`x|J0RO_{=t|>2Ixaf{6dUAG5I>NmLw}Dd7iSqR7W9D(
z@XYtyL%hYv4m39Y1rdF^rrE=!@$ZKRqt+$%nL*(aV;x9d4r(dN3`s==Iy&o=G{wR{@)BUTAH
zjr+2JmJTU{jRU1b(kZS&7<>-Je~xx2sBM
zb-&qa$XT_&y%C;>{8O2FHB2B?ioH1ZBcwC9p-9;pf_^2PUtRCdTDh|{HmBTg3*J!+
zI1d?Z0jkDl@ZLa2{tMLy*`B{!U|2aUvPjr|Oe}G@hKbv40$x5Z-+_m}GO~~CNR7+f
zLdv_?P}Rg9%ddMDGx$;+*Ju{U21uOmmTcGuWj6JmS~qXy;k@zQv<6PByzAqb9^&ux
zlF`%6mmfxGW*c_35sf!Da3M>Kc|YtkEsH%?hY^65eXWwe9gxS$WnjBN%fJrfT}9av
zX|Ge_N=uw`;}t6gt7Q4|^{v|LHOapsNzhKhtW`Z-K#c8M+!`jYa!wvM8!zqv0$jWi
zN>ljtMv-O+3K31-sHJV%#R#8Ei|RWWlaZNzv+Fr1Fz1+zi;g{NM@y~Vq{Wq+;!vx3
zb?hq&FE=alngBVCwF*)HW9ggo8WB-XMEXN8Z3UAw$cMLRkEsb=GW=IKK|1X@!hY}=3E@-FT7+d
z5HF*5L*%3;J1~-rBBP5dk%e`OPd?Cf}7YD{3slV;24!u7IkE#ihC@tGGvo38sL3_PVRKl3w>qd}yz`#~`sTGqezcy<3wv
zHOY}{e%+JC^eZK!9Np@*ECA=SY`jc*C$JD*HuL&Oxrkbk^*qz=MmyjAtdXMgZ3?S3
zo$tVYse9>suP3XhU{iX+`m|IIOc|(k%GebF%A3_f8N+6DZ#rT4fU^jKWLN)WJ|ed?lF!
zllQ2~@91dV$lKbKO(vU|vU4)|UGq@QpAbe_4KTSCm;{5fgeuA)Zh#UdW0hp*US+3=
zVMm8-*m;#S-zd`+8|^}IQ?41dsmF;8-KRNJ?_k4cJiq=jI|%QuRdS&*Fxv(F+Y!yO
z)5QPEiaDbB6J0k6p$}+L1Hz*6%Kg)-#R21)+e!W`u?~1@ap|f{*}S-&PXB;v81d~I
zCwM+})Al03VrZorTys2uUL5AwX0k$WSqSF=2HhY?$lg9&8}(e#{W)#wA%ONu(qqLN;Fieu(GT8L;t7nRpEN>3fSMp{}4vfVmJ3mq^9
zq~v~h5Im=L(9bfP^yY4%nbU`!A)MjpQ>>%kVF>ByaYLBlL$=$y_!jEiJ5Q)^Fd(cQ
z@f>qu+~N+_kz=U9Rn0by?2Ip*Z5oMoGwok|DI_Up0Ab@S?ZZH+WHT~$}v5?w=%%?>b3TbSn+6IV1n-c&%Wn65(X&6lja34A2HJ*mmG*9#A$
zv>pbm@eTt-r5%bV|J8VV0ix2@@TXPU8Ys=np|s1PVWYHMBvjhTAc070idAVKEB54f
zDB|fKHK;V&mQh-c0`R?6aBGwXe4wea4
z@R!CD+D?sx0~+C<8Xf4!__dZbl<;;T>##FCOsqKAfHhtrAjXO>RG<5kH0AxgLSjV^
z{z`kj@#sKq(q2!1S`obf@uoD=@E;Toh&)UcyYX%ZD>YsPeo&%2@#H_?w_PT+aJ%xj
zppE<*tH18%BjF8{v
zI6|q<)U;RdSK3=n&l4$L07NO?)_QqIjNE%jH93%4N_+ppqYH&edzS%Jg$x5k%RFtp
z6f=aR2%gM8;XQ=hYP@3nAX70sjX<0KEYydSEaknqt%%n{e;vn1!W$-Od*KJu5@!Tz
zzE;w*0YTcynwISce)Z-f;T^$<!SRsmMn)w-_~u^LczEypvED_FA)~_b`k<
zSP?#|V|XW9c=6f<@|)|@*pWcwx-@nW6G-f1R7VK(jyG?di`Uv-l5z3co6eka>JCnJN%vBaW0G2^Dr0qMZ)rJVRm70O96tQ5JGB`(eE5}pC6hrI
zfSJRGXD|oZJ{4Uyxb^CwlhG)vwVE$kea9A&lT{2tFx`OjWx$%=;R8+r))n#?Z(n5{
z>BFap%;H;=(PdXTjP}RqxGGzpH-=2!Y75B8D!5&Nt%H>NzVIka`A=
zypr`LN_~%^{*RCJ4R(t`kKP{X+YiNL$$R#2NBIrNdzxlQ3u$We&Z+!C*xv#PW>OQB
z7;hdKO)3jOb~Um;76+lJinx{_qub;~kCjgL1rcqF|g=$-`OZAg1J
zyh%0Wjw|04iYh7Kdj|(v%^~3X>!d1zxMB{4-|r+c1HgoRWgXTW3g5&Wa>SLru5jw1
z0e3wjWb=KSeNKznN7=OVH;-9jDK|XA>K+Qe1WB^gKj~i%Q;q2FSuKG2>tV~v$=IEk
zU#AJ1lc`%-;?mLhuyxkS)Ol*eGsSLUH~bPArskQ!YW0CP4>Gl-zR!t-TqjeniFiZW
zzXrBuGBWqk-4@9R%>YiOo(5*KRC4R(j!L>AlKFpkA>@@v0atYH-f4woXeDv?PI3VL
z$JPWE`zTr+do?L`1!o7ze#$2`eT-!}r>47byAb9!gNRUqto>X0R-*u>1fmVb^kIQy
zN$*~C8Un6%Y~%V-1I9Tk+?Q$gMK{nkPvEU*w8T_l8|hW0{We&Q@xR0SEOl?=GPR@2
zRN1yJQ!q|w^ylKl5~v!rm+NxV<3bKwDA)jptNsX^`&Yn@FwNr3KI<&-w44w-JHu`
zqofopAci}#KnQiQ5lj^wwgRALWrZy%XT-V|yHFzRzoV^KCD_DX*@@EkDN~-OhB-|k
zn&KoBRc29*oI>Jy1X7IG6sOx1I-`ucuVf1c2+WfB=i$ENDH2|QN^08eZCYCzrkaEM
zuG~dFnQHD9+~@-r2*?_kXxaxGSh)7~ksk6{iTH1RO0
zgOT!mV6#5IbI3cwV%o>PS9PMqfU3F*2qDes?JCU)hWH;(OSi@)9x#=1Xk`jDn$mol
z+Cr^X;Ad9cdQW>#_5!2T9z1gdIvRC1apX{k@9kj4Vi_-A3v4jD>ulpifen_f>c%D*
zZ7D)!^ipQH+6v?VoNNypIwZ3d?i{qj|Cii=hjsSfomO}HR{v=O_kOr}a&Nm$6P1EG
z$rwSkz_u1U<}BALTWfY7vo*uDCjJ>9$<=nB)
z3z$T|9PP^?A&cYee&?JlUW&8tHL>~P+2$@
z7JI2=^h)*UL!k>Y3q3~iT!5SwL^eRrD;YA6JE-Sr{s-zPG{O#Q9qcRw(ZcMc
zTKqf|uh2_nvHAW}a;a%-erDsc^n9nP2LFQ&(*5BM&6VLU&>Z&jCXb)nh8ER#iH4#V
zO(OZ6MYI+ULD<39DInDx5xpQjmUf41l({v+v^C(j%9=U
z<-Lt>$RKA8Y<7+Ll9ddGWv1;bAZ?PQcaU4k#ZDu1(!Fw?-G&xZgf#sL{P(B)Tt5F
zAQ#o$LCMVKn!g)QT^MfJeA(<$rXOaT&;7V{ETg#@ZxSFNNOR+Zgds1}Y_=H`=-N!T
zX7@1rhkP8=C?9Q5h^=^XdZmV*jdmL{UYU*3_2!jEGPEU;G0QRu(Q`^h&&eCPH3ka&vgD)3{ez0`|WD_B3t+V!^$h&2+C>X9iW6^iHpL
zLfKwT+)#F;i`qJZ>gJ+;#R@52$FKaMwSr+@RMlbUv>3=eM`3YCJLB_1nONRl0vH!E~$QQcBnKjha>~_xG
z9cihT#s!{eMlXK`yN>ZOzUb)X2TTYzI(m88q(x_-Z1@RoGRe^ynRRNQ
zWW@jPo!GF~2G^B`xu~rpsKGAk*Y$3y0WNA;1l89?&5xk=cTtZ-P#s;=xCm-X7j=0A
z^~ZOPTqj3R-?*qlfI=Ml*nPK4g#1GnwM7KA$VIKf1w*D8D1g2Y&%wKPyKV*35b<2P8wo~xg92f;2GHbos5|lcGG+H
zt?-8af;Z_+@1v>pw?*-M6B#x2IWY*i=umR}mp!8WQyHaz=i9P#4p;hqssdK&NcLb$
z%ut{3T7HdHvk4%{V&Y}&p)t1@CB2`%a*XYOKf-D&eXRuiMFN%X!#X46Z&91ZD&Ub%
zls_JXgfNpF;#qpoMt6onx46G%6Pm29xJ!Vl$Br1LSZ5@{zGrYLBiEjaSz$2#q4Oj~
zCF)8h6xpsLWl31p6pPX|J8y_ZCA|wi!id=%KRBJRyK5dnP6qQc$q?5>knLVUTG)ad
zKaguTLC{;givK4Kj^DW
zyc%%Xa+FZUDs@dq7elr>SXE{An+@1j>+l~h`)uEdOoYom=EzD*lMnc7ZY09OkGJuI
z483A<5cX+LBfPs3)qV{+B6Tw`uToyeja4!w8&g^SG`ZqZl1Gt~y64u&clHZS^3XK>
z`*>I^Yv>rIPlsIG#FC-cj-Vb+804)bCU}GJlmFX8B2$iYT;zi<92eR8*U&|h
zUbTz*^%ocQw2N96LEY=3=0{KyUDP8$f%#JR-Gm5viHo|%L5cTfF3+HS=Z$3p;Mo0g
zIQ9)VK2)q%Qu(bKbL=;8YZ>O?!|f$If3ALOKD79RliGah>u&QZM^~i7cZ|MHSLA%w
z=aw2Y&I_~R&I`CGx+EqFmmubrsU>o<3Q-v6o*|reWH!AIS&S2>{mHV?8gngJ`K;er
zET)B)@K=F~>W}#hLU2Y0B>n)37%V|XC-H794veG0X~_dy2fDo96hNscLUcdl=nqA8
z!FXi8a&?JGWW91m@v!R1jjiOf8BQydnuBCxaKpSX%<0FwnW^;nql>4DjtMwVdLuq_
z%=*yPq3t#()F7=4woK`*8#9m@diz=+6qjO<=uVU99}D&D_Kr}m=uK;gA5o!J9qkA;
zeYqpl1^*6(N_vxA)G-m%tuCr-1a*arY8^qH?V|qp$t|ncMJ)#kZrsa#myVG4a8XY=
zsQ<=|*Q%CTOFOr0td^SaW;xf5&oT|R`AESxTZO63HQ%-7RZZ*}xME~PaWV$hWwB>4
zntw*4^w~0%YWx)+i|m)>uXf{9ecOoN+Sj;Dkkgith0te8hCIU?-NIsap=CuYFG(WF}t1o}%u3|51TGHRuVas$!|*`7J^%WxMx-wc+Z=
zB4zS+n*g^L+$amn3;pjw29XwmANCi6&0?)WUXtFCpE$NXc2#I^4YK)^>Vs^yv!&*Y
zLVw~SGrLIhin94#b0e3-=HU&nIrPXtWmpHr#o56u~X@ZZV7r-neFZ*n58{|pk7i%&DWxnQM(9Lj{HpD@|7Rl8io3uD<2lTN-`qMkE}F&gfgN3NwG48KMS&(6^k^SNfJG3RQ!B%S!?#T6Pr5Xy0h%%AZl68cx4dQrs=fWO4fZYXGa5(VvKTpqeao|+kiO+z
zN5FPj@uqJ%ET|!`v*Ch$%Ql43w|o)KfrB~u_{o3lTNdDwHn{b~%qFWxAID#A!ayPL
zmuLOZUy|NX7d0t@;&>%Ub$tYNq>DN?g6ig?j*OsocTrt{f@s^h@7hGj+qtNkc~0NH0yn$aKvYh@OL^y*1W;sWZcdsDl3cO!+ungv5fHQEiy!i^
zWurW-%$ukF22z%_{R8le#80(_LY%w3+fYJ@(ek{%rqu
z=9la@1p-w9fmL&yS3bF!$d@<*x{1!4G=qG2Hzj)ca&{f>qvcVa-EGl1?QHW*dCrCR
z`se*;TwUEO!qu&-*uO8iDUPe?41I*Ip}-xULM?wtKG
zl;rHq8mpOJi!*9w5}HTWj2VpW=%hA-vF7HL?5j;y43a8t5wPLLlm4Y
zv&iC^S;FZwn89#iS<1*Z;N!JwXuucc
zH&*R2T)S4Bij@aPJRU$>!y4LNW5^y(>Oy_Bt$mfEWabJWy|yX|?%Tc`h_f>18tcIjdq~V?hytfF1F@6YA{S|h;rVP*O;7xaN`@~
z4CfkNG;@S$6{@#QEgLjWrv?@)W;cUrxbuOi)65w{AA!v-kO^;bw@WRGjRxl>n(f<|
zs03!2e;OIF!FhMG83gC(0QLCpfj*Mn*6%p&_M#PGyP5QMby3qIsO~N*8A0vqqJ}%D
zoDK0)%02bo)CfYA!Mig8{&%?fETbX*0IQ0W+&07qyq3EmKKsx>-Ude|XKUq1{hMra
z^Dzi~5M$cjlN@5x_VzNbgxg5l`+Ly#I&=H`pqg5-ijO=L)t%sOC)Bv?c6tll7~JpP
zfv<4Kdwpk#DW*~W=r5IC>C=8anqz-Ia0efVHdbe2zpKUqb2nAE{#S|hKa>{zFrKde
zEz5VYj7>&z73)@R&#}d4Q!ma
zmZLMySfvW*y{bZOI1zi#-p2bZ1+P2UD!90zV2j2koa#J5KTVSzZOg1SQr;i9f{wKhKRX;WvLS32ntnHf10u_V&{8^uCQOM0`-%8haFNi(D~ojaUh`%VnE
zseUu{Ov4eIYU}YeA5N<9HP1EaBxrDI1eCH8guVGS{uK!6^}D)lq-P5@hcjRd-QX|&
z*$_2L<7Ef^FKK*OXb@x8HKy?)3svK$IXlB4HqF^l_7$45pj&#kG>dXKu(W8!$1uElv+Ok|@i814nH=+-X_6TCju
z3_92hbcNF24{GwSLh)HbK6pSNB<|-*JB?Kv+yfA<=`M9qYd>*OZ@Q>29Mu2JS(mE}
zRO_*88dK}u^CQmM(IGa@x}|xQ!&%p2tk5RmM-$qiQTMowZ<$d=8Rc
ziK84C4BbfQ{ZuLMx0Kd-#l6ETdaLv6y(*9*P~aN^D+7;a3v4}lgbYljdHlSNT&Df=
zt?V|D{sLfo|
z_aC{b)}T!fGJG?F`q4$rjG#VsQ56wX+C|+ELA~Ij&WoTPc2P$~P?KHMUO*M&-dOit
z+X(qM7q#v~SJn|O>PrXJU}yN5U&Zj~XICoNKsTg+RoE$)5jpN3auQ6_KHfg|73~qL
zCzGlIR}oyp-`&ufVtY0J0K?g)7tZu
z!SOb|9WZInv)^ZbZSp@u>MY~jGUPGob$rFKrCUA-Ro|d~=V*D20g21
zhx#?Mk6-*8%6gBo|E7Iq^jjhN-4%@I(r>@L1N|nwOBXr3zxsa2d(s==qGm);eO*+_
zK{Y7xtWe?|Ew?!(-uR+KwP2+d+c7vy@K>wUcI+&dAiJOH%v^r<{-Ve5Eta19Dj(D+MKLE#k}%AA*r2udV(3*NSd*hUAw!Z{$cYS^&kC?
ztNu|gYM%&dh>L0!L7n2Fet+A|@NgIPX#~~PMJ%J?GkZ2i?dp;lXi0|;I?^A1-$-N
zs4wwdOuaUKg^j~uaibjW1r9SpV|$YTJ}O5pM}j1mzdp2U!0$g}YDlQIS!g_9l?|wE
z(z9aTK}%x}UfgUFtsoy2QPj<
zPCF!*Db-r(Q-gCv-vxLj9qh?lo7@PwU`ad1G-sG9;dP{L5zds%C
zvs~}P&W@I^bD26W!qnh@a5;j*;oCnnX64U3mF0T(n*`SN)`29K>+Kfu`_Ei&rDn{g
zrhB7IZHE%!6Pd2JnMw5DxL*2uqXm3F6B~y7Ke#D4>Uxh_lBnyQR~&J@n*%Dx^>%0x
zalPYE0$uM43|ynG*T<$#6~(>*8&?`O{E8=YyWYZ`9HY2wj$^5XY1fF)a8cVwP)E6_
zO*L++16Wv7hg^PLyD7fCH*$!*>M#wj~s9PLVgV%}WVSaFhnHxNj34-1K
zRc>AfMe^#88FWlPr-1qtQ9q~Tv
zd*44HxRk{HrrPCyY)}E}^xY`o4b%*S;k5;RGO3_DH
zwA?dyn)U%Ou5MfT2)v+2?Z_ka2D)7WOyO8!kW)8Y9w&P)@VV?(43)gHPD+v0x!#v=
z+EjNRO2J2Vc!%A|K@M%lJgj;5&dU2TVvzTT@I9ON3%I2yBk!xvMtJROGVT#(y#9G4
zPZfUqkmiDo=H|oA+_vj7w{Rdbz_zyzP!?+~a`Y*-o#T&<1ec}oe-m8Rebtyx9{E_L
zN(N~<>BCXjUCo@+Gg>9j*t{~Uq@__?c9r~EsRBQMHBzg#(X8mG)Tdt6V3qt#m7%(`
ztK?&&%p;tjtW1)N@iLCqJkhmf0%fMq6f6-Xgv_=VR
zQcV!sN7Dr!^Yal}H7{z_++_2TsyR25ZutYMy5qA{w}=?{-mrs0)g8|YXXU$FGf1==
zmBaO$x@W@*Iy)@oMJf*!RGd}HeMYUGQ_6R;dpSobC!V9F@DuPN$dO?Yr#yoqQiUz<
zQ?C1B*-gEuQ%;6=A7HXVh%09u7iPL0HHNx5
zG%M3h_o^<3I+-5pWV-NN7<*5XBfiat8bfQJYWwk1QWYilX!1FK+N!`=KW$>SK26=^
z<%e=;-rvS*-k(^F0b$e4vCh@d@pH^|M!Y3Q(Idk?l+aOASu??X0I_%01>vm?s)
zTjGU(VHN(&W}a8A!k_J#@vhAH4rJ^vxBzO$w`?}mX0>+%LYD5L5*y^fA50E#`M6=k
z@-PMWPBf7B+SXQFcva`b=nd#-f$st6PmzDG$CI1oC1V>BUKap(hmM%HFF^l#mXY+X
zzp+VPuv@Wum%O}ewUjKULB-ds7T%UElV8n##;G{
zijsKvf9yt$7M+W*oF
z*_V?4me1k+WTknO@OXtU#AK*{v%*&szFOf&2w$x5>x9oz_(Q@cD*O%Mz6yVjB@F)%
zg?}f!x57>50NzRAZ3%Cq@GgWmSNH(J>o^ycD(XY{CxuTU{I$YY6JDzDt%R2-+!3pE
z{sM*fA^g0;hY_Bp@UevNRd^ub2?}3A_%?;FC48;Ial)4=JcIDL3eO>Yio!L7k5+gk
z)+zkM6kbbsKZToNvw^>h!tDvSQ+N-;%@r;rypa=NsUm(F&i_T>Qwe{s@CAfFRro5x
z?kh5HizTH%ukFI9Lj;Ux-RNqB+6cM^VH;RPQ8o~H2Igzr`OGr|)Tt|xq(!abJ*
zzE#X@Job0Rd_MscNFgU72sDCev9yIg_je4LgAH!A2jecfbUfJUfdey
zk5%|_!Z#>fMfeJZ(}XWjcq!r275OR)h~xn75kxdn>#j
z;hhvNBD{^lg9&f0a3$e&JBsW(;(A{HCxyEb{#xOlgqJFO9N{GjpGkOu!j}?$p0GB#
zW>3$z@4Ibr=dZObMyvj+^u6MI1Fa8u9P?-|zlbTEg!ecxrSpZBhX$1ft#$srpw-;b
zFGWYp8yn{Ce>y6_VyHmur6Cq=erGiPZ1c}#F6Jk0Dd(#>TcY48Xs^4FCaDkXMTvJj
z6WnWwoR3DHAJ)3^tbjwXZ((R&Qkt0#4er0V1L9zp70*^aNZ2_881F*yQ6L|QcsD+@
zIPPUYaYNj!;w-MXr;rt-X;#;}ycquvX7OL_;Ia(V1>4=vX{cBz@+o708BZ*FK?
z0bW-wJmUL;o9eeHPQS7V|G=V@t<#u?ZHZb=dhZEgg&=|t($=6dS5HCb?W(Y{Fc>F$
zpSZGkZ-NNJnRBL-=d`~OW@tn=SAAUeZXk`>*r7aRe4{o#>T{%w9F5pxIi~^*LKCT;
z%;g`g!ki&SbBbZ=XXC_-`suFqBM())e7m-08qD>w0vy)7cq4l3b%XiWus=?vJ@uV{
zeg9+XBH^vL8@VUie#M6}RHn3hhO|)M8^HUA-52Nsd;X}tdqb}MpGF4Q@rwQS?Vvc5
zH#QtI*@a=Un)r#(6%5cjnk=LWpM@Ay{#UnR?NX2hv*jTJ%Yyj(?U03Xie19Yt!a$R5T1m)5d$cz7^Pm
z^&TxM`i19sNtXBcAB*=WLgkEj&--r%IYZvJZdX0AeyRz(_0wPLM;@wp@t(4&@Sd()
z<@6q$`l7T?hwLL(=luflU|SDP!qfaTp?kw-CUiF=O)@SIhVFw~2F6#Wd7kOysS=f|
zYG*77BwBEx#NDD7p;*6~KA4F2LCc1daK(J2<1>FWAwOjhY5%l)gpL?+pbuD!*Lf23
zp(Q<{EG%_&r-+9XG2cNXy&vvE!cu%9b<8|?k;!7JQr_iIrizNl=Tx|_A-Y5p
zV}>_Az_%5Wl|ib_Umyr2M<&0N1Pq%C4(5kdP~&}kr`&NMBX@I!)&n8l|9N>V4u!-2ycS^mV{liU97^%BN4CVFRd)oDD%u7G$!*4NhuCtg=7V(
zNwg>>HkWLD6DjwBgqF*&atGmzutMJXeRwv?R~cG{l_7>UBdiFys`#;d2`jyXutE@#
zuyP-oc>l9I16lg2r5xq5*Yb}tvgBO}T8$cdsC69rwMHrvUfxazDzM>2i_8cwWm(}R
zfjzLW97U+|R9~iXz9!5WocxOnnR+^3n9GAPO2dNL6|EGCL?!H1qJ`W`)!GNCS+FKG(mm*NAhrbrWNx`a7{
z%jYY>W%tp6C74jNnM0J0uRxVha%ALNFxhrbDc|cN<;$7v*7s9dp+V3QQVBYQ=o6*Y
zEl|%bm=n~RnkzwvnZtDCkt%!y{1CZ0qi?c8Qz}wD;>Rf=Ip{{;grA5GmaTMrjRli!
zW74(oj^;zJS-et%KWV_BkP3(LLO9$C9BySetba*3Wai*d9;w0-!{J53NQI_Uq;hz?
zawz2BaQThGAtx+NSez!?xFpilc-81J;B=%L7RQEo|I=YXY7-W_ycU=mlC8-Z7MnUW
zxc_bp$2DK%TlVs
z8xC4GEDpz%hN%w|7H7W_sKACrEixl4mS=^dg3Jr_9EebSeRQkMz>Zy+4s*&duF+N1p#IMu1*LJ?N<=z55
z)Q`tbV5B3yWWG=p#9M@&Ds%l(?=UY!I{$uX>cnt5!8qe~AyJKY5K72!#Uv)&l6+xV&2bqb3>l_-i|F?#fJcO|8j2*O+<&lVY<}cNbamKE0R`EO(2WKQ%
zE@wPE
z?qVzw<%}~{y0Wx9MN)h9=nc5e_zgpZs=zqoUJC<_TW8cFGn{csmNR}QJ4)k9X)CwXWEh%?3~!^HO5w4sqc$`c{`aK_V+u`c)WKGBN@
zi!(m-XO=UL2vdP>55pRraeKmW#+S!ZQQj8#89JlC1BUH&hj@8;erpckle%;T@h8C!u^{dmy?${#9i`ec&c45`W@oEgc_a8{c%i#DYn0M|Px=
zePmXyc7e31#G(ZjB58r;g|xs;&;lcG5o_r4Ld|T!y*yHd6YgO}rTwqV`L;q+DpD=-
z4MAA)TMYRu=dDOIVJswiXj~u>?iHg%TSO!(vtp#<dcP1Pfk$g?D9mR-0Co-}i(EK89}KHDKjnMYrP{M7n!)f}a6J>RwdVaxz@Q(I&FA<7zxH~!UPW#Yvqe11AF
zNLJ(hhW0oke9jH={?-=;g_`hr&dWh+aMLVj`0Vb`VE*%_P@{Vg4mFyHPf;z&BN4CU
zFO|`R&mZT8#bHuG!zalKQt({s1!jns7jh0in|q^{%kcRO;f(MpnVIl8!O${%-ezbs
z!l#gH&Afu!d_j1o5LO5x5>+5`1Ep6YF?n2A0Bops-{?b9-BcBEd
zO#t;@3Y1_2s1}+LKr6EX=%cu|09K_4RbM24P6%^`KxH?m#yE#`6JfWeW@t^xqo>rA
z1kj`IKmhgEB7ovEP?&)|J$sNhG^|4e&~1@%7(jPFTLS30zhni_gTho`=ez5bHO_E;
zLKv2QgG{%0HTcPY{s-Zp8u#GRL;f9#hmyBMx2ISBNPII~+yU3plIG2j2GRJAvzp|=
zu6PfhA;XotBzx2$OBKE|3M{0G+9OAQd~-NlI^GQp4A}sKdD7jIH~4h{y9{7=$o&i&
z-Z%VNI$y|TRfQ1oPnJl%cMtQamXvY6L1E)DOXhdhOr~ijLMxdF4KleHnLyN?fG_jP
zNTtH!_H|0}MO9!)va_m?{h3XLXUN}U@z<}r5{2>3CQa8~(@k^ICB1=^9$iaT#tDQ`
z@Q=5Ud#?w6sut`FAasWP^1y{~{H4X2
z==ko74!3a@ht4I*a?PuM9Vz$zYgIHx$8y3M(NQup(Q&w;WpupA&}KwOA=l{m=uZ+I
zdkSHNAR^JR3F^jQE?N{n+@$q*WQ4uXZw5=L!aopQsPy%6HMEeMnE2rff#^0SYEc<6
zaYj~5EU%WBs0dY>D%Er)JZ5_aq7Qmr3(sNy9icEggO|KV?liP@`C
zW?p@OQ*){K3Nx>M^k~$e`Dy0WA6>OUmqdM?SCxCgi(a&PvcFt_C()!17LLdik*
z@X&x^)7!c{)P0S&5siIDZ|j5*?^m7~q&D&7r{{v2gC08Mj3-MRnxyyI)ezpyNK_Ga
z?RB;CFAwa}<1ei&6Hjh&vy$QvPe_()uzKh7A#Z30Ou39FeYD)j%n^CFGe?~bE#pZW
zLz@v#gj`j8+DeHh^;c1t3PD8T$t&nk_{ng}&J0$6b!BP!n56b5gI2?Z_O%XqFHnJr
zD>uyyq_S~Ei_C~CGqd7K_jwXm6rsvf<(jy%OPDj{?ns7AJ+%_%oSCCM?Q6+HD?sAP
zvT^WlZ((uayo{fwk+AimY`4{4c^V?5$-M#ybl?
z`7bhaWWvvFP9`e&JsN)AdnQmRg4>!ffsmw7|_$XpM^LY|Hk3m?*UUnNA&FuYUPO?DE)*E}MDi)kRbJW_>E8@8@uA351}nxs^udeuNdm^W$PzbN3y1g=?O
zt<`ujq|XRkcZGQWhtq=8CU6x#?JBWz&cM~$p-Flzu28FEL%Zo;fZf2=S%s4ab_elS
zByiO{6&8nmO4<=3S+2nKNu=DnSQf0Efoq(W8wp&Jndt~!VrW?zXB*m#z$N6W;`_dn
zz;&PyRtO>zxVA$N$$$UaKo%3Y4s+RSx$1JN!ut_j4>xd)ek(|30@tfg1}dy|kZxD=tviv+HrVb0*>LNa6m*BQc`6S%r*J;|e|)RP3RPNgvVSL+)KTt|g9
zh`{yJFgI}J^^?HWV{lc7T;v-NsY~zF)
zb7zuBmg)AsnFAQ&OXdqzuF3QKFfR-j>#?WFZ+(7XCdREl3x%!muDB-4t>=Y!ztzb>
zYU9@DhVnsGU2?khVGd2wJNUBDT)Gf;-MYJ)i#)KuBg?J-JtI&jLK}-ie}rVY+`37~
zIqHAjP%W2kJ(F<8_(n1_Ze4C@>DFTmZH8M5xw`dhUx{0vErb<pR1oA@}W<
zhV@iJ*sZ5xttWYC1&CW0-vSd`^+RKB{jabF;nwYuaX9z0*RkT(7p%x~>qEm-V6E;F
zWo<{mpA&{#-^^)>w6_F5#jS(sn)}qJ2g!~)-5R1&8=wBnkvihjuVx`kR@*pPrQ#)S
z_vJj+vcA*q%j7Z8$f+DPMe@ER%)2H&(s(&@f>oD=UOuaYUQT?;e4)za1@9*6&NNv1))MJ6oL5^-Yz5FzX2J62Uht|`Vuxma2
z)q3QSh!5m1)ea51IOT2WW+lbJ%So2Y%R7dg!yJ|l(Q@hKO9*FpIeE9Ej{lFZD-X14
z`u>VT$s=?tV^N5tzPMy~NrpnmP%=biN=O+K@l;pux$aNC+$7%yAt4H-!mA9umpK^<
zMTm~E$Pkr&pU+zBoW0MzSN+l6=d88YUVE**KYQ)rp7*5ja+HrZ-hxps>^jOnUoNBk
z24Tz#Au-CYLbwv9q!-GzgpKl{ep|}gO6i~@c>Pg6|Ml1eHp;htG`4s&%C*XZQNFNf
zl)uVFZZ@L?HJ%#RRvl9xi6x`mlP__88q2)zr{3x(Nz?%{%KP4eF19u1-haQwGt#M#
zQT`tYcBA|_T=l_#y>(g9C_lrMf@)iuY8>T%UJTVxXpCH?2v*|JMtQg%6RMt%{lcl$
zosTuq6(ILV$ZK89&d0uWRtTGsQ4UC^BYX+ZxH@)*osZ3N%#gx+`6h5ml*PRsiG1{Y
zZ2BW`K~;uSEHQ*h_8H^_I^^*A{`uHR4+uBKLVHPa=>fOVOSR#vyic6XYe^+)cYHz!
z5&K2PexLVNTJ$Z#obdLm;uhKY*s4jc6X51#)C}B6v~2jS#wmP88|1Z|kI4)Fd~Afp
zSPat#Gw*tkJ0H7rw6u{Gp^cK{(w`31HhzA+v{5B1QQNpc2od{d#y(BnrnZy1eD={E
zJ50B4&$!igKK2W)b8)eVXbZ}|#^3{d!~dXeydM7w-h^km+d77wFS=qiL09k;EPTc~
zdiZnaxKhK;qbIsbYcuUjVrNs2CmZb42Z~uhy*2aBqE(ssYC=h>G7I^Xz|MCjxazV}
z9k7#ZrOwAzB>Oac#Lm9PTY#Oyu8y3v6ei9FM+jqH2np=$
zhTtds#BG(?`Pi|(dZ`u?A(j1j5j6|;;FIpz44W4l!O(Nz_}B;$dTONw=((r}Jx4rA
zO*2YR^Ci%8fRhX*`cW72)CHoKxAa-0o{~geAn4ihMxf`y&w-x*dOmiN^C8f4?S(#i
z2Cxk}ZHFaA=()2i1=SXy6jkGq%pB%X=)B8ls9-W4`7eClZFSiB*n6&dsJRb%$LYcu^Yy9kcr_X85d$s4FE@dDI6;SLVW
z;5ZV8R|~uB)G){9WOdVIpp(;F=zQ!>uUs~0&(@nW4rCq`zatwg!7g}I
zD`adZALnE3ewAJry{H#1nNZ?u4=VMIleC)to;)!&S~_Y8e*ba4R>AouWZp-|fqMQT3I19GpR_uQET{RRGOP~h
zNVZbvW3PI;;r!d`B{~k&dK241BFxs0-Hey$c)alzprf!WI*yQw*THVWm={6<9s9yJ
z;Y)5mZj)NRMMAxDb>JW9^ECQrv*V^4CeFa-t64F^M6Id-6PFfY;=sqGH!4Amrbe}h
z@;oOQ?LGTE=btXj`~Im=|45>S7fft82qSgIC&0viJs(SZp37zCuSN6F%zgVJwMRQ2
z8}YuKk1Zq*0%0aRAN#)@;=T)L;t)9r$6-;&%4H`0rZN|Ei^LvHX1hq{6!K&Lp78Wz;?gz%$2ol=woAzYc(cco4X*3y9DF(R
zzJuHAq$deBa9oRQ03PIvlIpmD1GZ92yJJ27WP|nRXuTYOtC=qdPpEqop1fzg9DuJG
zZ@~Z*b{&9Q;`_&794m}@AtVOicW`rfq}w^OrQIaIEoEm*>7W|CI=(bG`R0Y#;x-Ul
zkB)mtG!V7Qf`PcAXdte7SO%gJ)Od-3_&%=eTHix+-s<7}G>>`TPhHeclBfe@Adb8m
zU99!Tn_JpVb?Rf@>;b`UY1a>35~K3rcZ-6P8(k@=*2+|4aI!n|C{*2xh6?KAk^jOL
zS7Tkr$uZmoISjg`UmyYFF?bAh5x^=&Hwq7*kd-r<~XLB;4MT?1q*-&9X=fN
zcs$u)$L`K^0rS4+nyTj{!TsW*5%#uRaj`{?REH5pwo)VPi$uK-o~`wAgyono7-7^s
z8evx(FGpA(<1H9r!mcCiVSJ?+jO~RnFNDMhy9bUAmr4)tH%~hH>XrR^7F7x^2CqND
zhCd%$+(y_N_(hM6gJ^_ll?5X#EE-{%@iM}cpvFs#uxp%TXmSM=vhcr`DCdR$t<+DF
zr~_n#?b9D!?2W~n8(}9q4`PJRarjtEGXbC&N$Q2Px)Ur{rsIWIZ
zjoY0%F;3Bm(K`g`;)K*>u7j80_q=dJT`44oAjjiUSf$V_s)ei6_UHz$3AysEuZ8)y
z>k{`E8{tbFQ%&$4qLzXYKFiU=2JPe0Hp0(!O@ozsmK@<}j|bn0Kg0E$7R>u2yqoqM
zNw7UyG{V2XC$=Qq$LcV`$yREFZ%ouXtDDx#5&jhO1tVO9SrD5wUXJkFjkjQg3%ic+
zc?)HP_YlUs5E3JN=D8T*f4KWBHius5t5?=gN(WWo^+))_(_)L;2w(Mou?eCPu2mL{
z@HIsv{E@LT!j+)LON{WGlZZ5Kj%S=
z@SPyo&7m#Z$q4UNTQtHCcBP=&%F|ReZjUZy9wYqb9yC<20FV6VZdBThj5S{4wAI+U
z;y6Dh7uyq|?{o~Y_(sOv^qIb66-Nn(2uPdIhdiO#Jj)`fI#_RIUlmMMzYDNBcQ?c*`LWrUo=TdL!$+
zS)1uy5|1A9c(TESuG*QnsJ@SRzn>3gt8n_vq$*RvM62V_L+3fq7bVpJkH}UEkFJ(`
zDA6A5epWB>Xg94lv8bl*QN*$S6xESN1qX3VDT}|9pzdBqajCmm>@FWeK6rgR+VQElnBmbmcf}Tu@JOpHz@v3Vc(gNj#_^*}A*k`xxVE_7
zSQSe~yZ=1d`RNDdeLq#IpCnNS2p&D(2czPgH#dhzOP%@{3&SAT;nDrA1&`i&wFr+U
zxl&N=Y*Vcn@?Dt60QgSs3j}FA@}J(d2v5RFN>6N#;hM@t_Lu4siz{}rTQ9Q{n?qe~
z;nif6E&Cf0c7+qpiv3|HHrqI6NLleiEHI3++2AON*!0BapDG#jq+-wulim#F3mNqO
ziOmB;g_~lby(IX1qnGHQZ+HRU=0%hdwMYJWzR&)&v482kl@?7!cox2MCU~_U*ojTr
z>j(I)bkq#|df2kzCZY{4aZ)S%E}F09#718DCpITrY{$82kRlzL9fAHnms+9iM%r5bRBDczp-(X{+tT<`~>rL7VKv
zriud4t=?Zo-FRX%5^utZ?pBJyXFXS}CfF4`1@IY;jKzjW9~4(=@OjsrF%{bMD+!;M
zc|6#^?hM6jA#gVH&Z1SBEp%8)f~FL~=Wf0zsSfZ-wo)fH2RY5*z3@b>m+-lad306(
zTR7A`g3p(Ym+(2=cnjcD*wvA1_!B?GKf;(7LIOS;BT@_Z>lT~NPHgV++miZDBBrt<
zMM&^9pLA1X(PMEz1JK5q*ya&{YNZ7Ly2a-xnfRvE;!)HzqXacy0zh9L5lcpcUN}K5
z#=id~^S-AVs;4AT7YKk}djSBn@d5zozn<7U;M50z9t*(^K+oD&0QBzaA^`2nsI2|Pu=HsvnvuS#e+up~Nm
z@py2aXH{^_9mc#r<|_3>KoV?~^GU0-;BsSyr)`GS0UgO!>cpmDqG@j)t4$+1&Sky;
z9YvTecE=kp(eb~=TY!$juIRXUj-cZe!k8CA0v(&fH(`D4>~yf5*bGXTWj%wWTzW=d
zb>RLJ;-(uW)^$yfFj1>2z{Gk*m^e2pn5YCbni|!D$|<*dMhzx)R6Ef>qnY>pGe-R*
zi5gxo@xpU3QtQqKCjRS*&2Xna){hmxqj~6C*K_a19_@5x@hm&BsU#0Bz%k+J%JGfj
zzH4jEw!X~t8z<3%3r=MqP#$y{S2YzSp#
zQ@2P`et{(EUQF~;J-=ZUS1H^T)l#O^r+S7u$xwAwZ|If7P_<5OE7YDI6N0<%<8XCd
zl+C7=b)h!Fawhr#jg}GxXUM~VkE{~3$_oivx5r9#dkkS3{;O+=%_5uu9Sh~sOP~*l
z=QD)L=Cz~}wWoIyg1xB?7ayvs{}5|fFUn?9C##01I1PvO20LON@IXUh6%FM@k%k>r
z!!5{-37ttUoi+{UFxJTCwWJbN!-+z$H~7ORrXfb11%vB{#+|(;7>ekuAQ;qpMS4Eg
zwk2vwz}?VW9^o-Ar)_Mtd?=fEhcho%!2B#S>;qDRREr2^=R}+6dL*Q0y3b$_|y%K^nY@W*Q1EcqlGru-D+0*y0iFX_W=A*RTloYBA@c=O{sqmw>%z
zhs2W6?x&7&ewx6%@2BSKCrQ)+0(+OAiSgUurOmCS?{?}VN;wRIT`1UPcL@dWc)lnU
z?BPm5wR)ynGvqfM3Dqz}$91Big75Iie~I5M7&`9ggv{FpJmIR7P^Kz)AK@cDUA7pB
z{s`ecegB5Of0KzC%5ZX_XDB5YPEK=#n#@4M$zd#?O%0c%{1QphmoibD-1#)(B)Jiq
zFtO02_`7D^oOpockPBe!-tFT4VPM(XW5Q1k;3(Py4QLl-v#Bo_vf=kp61mA)!4W(+
z#~C~QiXYpPRiH>-2o(8W4V!tjfF*^aLrIcL_lG|8#6ey8Hm@a>sN*Ch1bb5(KGqg|
zxbhdNRh#J;WwWVMRm0Pqh7Z6@(2zXPP*_Dnc~PX{&Z;4WLqkb&>F|8j@a|LiHm@a>
zs2W};1bc&@^QwjnQ4B2K#T^48Hr~_(-4Lu5faNoej&F3M7VRDu8+N-XE)7CONw7T7
zmb)_V55g|mI7#s1WIkzi78`zvt8}I;Y{2ROmSighmcJ$HedchjmtZ-G
z`2w&MVHO(>GhTw_jmBF5mcp(kenIYE2d4^SUI+=WyaYjG_*2KYEe4j|ef7$=kfrU)$eccq}(XK7Wf8S?Kj
zk0Cm+oQ4Ws#v}ig0?Udqt8f^OONfrzE>OW$ve;4
zyp~j=4uwDn_NF$>9{@g_x!Ll6bhg$CC{jAAadJb
zJS1BwJY1Qmcl^OxFY)ld%opIH2s1pq)_94BmlfpIT6LQUpc!?4%vp#x=t!Y^88lR7;ssYvVpnGT`lpvtXV<`^FwMcstr-s?CgnY1smFmWfd|
zn`$og2YX5V0&gdu06Dl}LQf2@7FGcdc_HB8hkG>_3%pS{x_~6P^!X>VE8)Hw18-bE
z@rc^9_Z5P&gYSiXz=z9cky`j&O_a^1Zqo_
z4h<#ArRSWa8jd@bZ}VDGiK^iUA=sPRu#sx$g?nGfNwtN0ePF%9YT{bC935OJjCmm>;B6c{9{zJ!
z+!lkk%Y3txHI>rAAn^L|_Qc(BF@v|&SH>oY;7zM6fVWmf@b=VA0&hxC<0arN?%bPi+p~209M{-gbpx7w#QcCh&IYqebv`q$>s0
zer&C(HA8*{^B8(}$Q6j-9X#@%!dv(y#;2Y$FVT@2-5}k_N6g3AXC%AzGCOB}+ZiGZ
zcril`8b3!Q86NI7?(Cd-f@6l1+1!Ftf%Bs*_E#8_+Ltp&UnxLCDh9nU$)J}P0!#im
zbK8rAn_@BOCBc2y6Lru}Dd*d~mQh^%6d>*LoA@%+x*FT0Gl$
z37=hzw*WqcT^(6Dk+2jr6UMv{67YE&!p3mkf8w^-IrDzLdZ{xcwk%t`A1xA`!YAEa
z>^CwlXaM@mWwAXY0M$wh0JL2ZfcEF^bT*>|HD3Zi&vufb#A(#U;=@j&mlq#~>M2Ro
z1p=Vkb^w4rGZ_H-ujkBdoeu$^pX}=c=nvZnfVP}a1fYKMY7dmRrJ-{(Ld9Bg{G5O>szfBikST>AcfphB*E
z93+O__~98q4JH_>0R``r09M}MsTD&8sN4D^f<|x!iNmXfU5<#EBYjXCQGcnEbE3-O
z8&uY@Gc5gW7ZGxx3K3orB0LasAcUZ)UEA`Ud3$oHzwMm)(M#i=q`wUO0gRi{%jN&~
z@P9aMcT`q%&U}?u4(I4>y*Xni=27wU2U7cBQ%iLQI-Q+};hecK|KNgqQ7^2$y6@3mS+m81y}()H5%3nroB(R>J#omaNfW=1hz9XJD4bIm|m)yimE*n=r5^
z5t|oGZ1dtqd^XPvv|gnRchcaxG+Glibbsa{>&@2E(8f_iRT)FatrA*JEQyXQ-xI6UX+6`$L3@T2%oiwlBiO9^8b+?z66$rH~rc_7FQd$!NxrdprLe
z#JumH>FOUz)bN6df3(L)9s4ja@n6rGck?`lFUS2s2V;&u0I5CNIdlI9?VPzEd2s!f
z3D23A){FbDout%yiffCesc9R_aisRRTxR~y-bLw)=?8tMG2@#EOTu#`^vIDDRW4+b
zODzJWOVxW|3wP%Zlmuk2A$kozq}p1et;>%tQpNTjTX=+QkYOmCOZO1TwP72Cd!@fV
z_oLr}@*An+hQ@KFw&9#2Gqu(pjK^$h=>aUMMD6LH3c=pghW(vJ7Ha7^u&q=_qI^N9m2w(~S8N@($G~LP
zg>e^$b*GdBllwd#{Qjw#Hk4p;2lIYs9mrN;myrp6X3Qi3CS80{QXRmAY^7GJ=U?P>
z!{5KMdI=`=wcZ4nQ1>X*`fg9vk^J-h;Wdo#i&(ZY^6;VP^e2ql+)A~^sgD?7AOyRW>ac(6@K=s6
z<%X)KqTGSNGaTl2)C^I+_Y9~N7^1xI5wgK?3|n*Qtwb_@QI0Ld(ugwF
zE0!zYsuPU+2g#<;=_zwv#HTiQi-;*5Q4p_4VyZR3{k#z=Bx?QC|`gmuQ__yq;A|ELzGL;i~Bdm
z{`8WFa;(RL`{ui$akeCA%e>!HM``0E!F5PJB@pG0b6s`#fDS?6C}AswDD|D@FzRc&
zYQ031Sh!bysQ(QTqF4
zDcfC22g?xz`iS!M&9Mm#QP$%4s{m27$^t~`QiLeeE)hggf*LP@C=WWxX!m{i!EofK
zk<9ykny7w~L>(ZAa(X*-`n6*=hbY%O4+2s4gkXm#?GS*YFJEzQ5u$W(rJ&lHrm7kd
zWhL_{lxa>w1@GgL|7`E;8=UOx-q&Y5<9ENYsH68O82gajdYSEgEpmnk>yuGnk5pLzh_=Cz~}wMX73goynlV;}0hl@?VX`V6nwD{hhPeZ4Qog$l5^tbwYn
zMO`c#K15^U58C1xZIIWp_a!gc+jV
z^YJD;T6&o*G7LT+b;W9eKbjZ8=Lkm+mu`^1h~b@#?%O>c%+O|bN%*Ym@!%d*O8Y(o
zvNg>6!?K&2P!jxPmQM-zoOQOV4m+s|pJXew_w{C?-Vs(W;d8Lon}AOdX7Jh5cnP0f
zjkf?kg2V=B)jVR
zxS#>(b2wlt08p*806@DH0q6j3L}xQfQ1c}Kw5O8{CC;QSHqo6ZdU<{Y}LSlIKqp&g7
zCa?(AfP#NZ%qVa0bRa_psN4214$NRViTJo6*=5^`Inr;~Rs25P$$3%b@C_>4>Tp>4
z`?Ls|s6vESga{9Ww1W^dwK?dO!7y^Ezisbp%9(Lb(!H;Xz_KZQhWvjV{tut8kIIVn
zz6N^bvccYLy*Z;X^Qd^1+@T1z#G_gvTBT#}Yghh3hIrY2eZBA%EH#VvzP599guU0r
zxbvLG&^@|2&9zCGdf|iMqcdVcP3A1@YtaLDwlx_-k#iKM-m9gNmUP89NA7;`4@D1<`Ra^FBJx
z*1Z);sxtHWq}4ItLfaqoMP*nWf=jZM+WVU7>4y6sRxi=Fpp8#=H;`=(quKWB8c6a$|d6WeN3`Fu2U6J07MET!2rk!!$
z_xWu8fb>P!%8EY{a5?>;?=)z5_h9ILK0^-`VQbrKJlWYiML?IOO!QL38FA3L4~AY?
z!-l8;Te}|Q2~dKqT~i(bf2WFz`w0sKlj^I-VglY&s)Zo+S+xF$An)K
ze-|Tkg~j9S(bqFCVQdquU7K)Z!4+2>OQ4l9XeoZH>NhNCEe_IIl
zrZ&95X=K6IeoiBNucS#)@U@Mjhv%=2LD1mkiBp^-amHH`UNRmJzW=eE>%P}9?|0vj
zt;%L3!4FLMq^>b|Inoy;)d60}R%-2fYFDQleg|pw5?xBa7hzWu?=V8(WwJ2lg^+-kryHaHo#?K-*xL0Ozb$3GrF3uyc>T5OFa2W^7|a|l
zcZnp%j$lTsEP$EbMKH6ryTFVR)Oc!KgPG-W2Plf(mu&0&^cM5JpVq0LBvA(l%-pvZ
zI{o2yZEo#4+o_MJpf3cwwd?giNa*$Su%ggwm@5UB9BQgHL%tRB7yxhVNJ9lFJn~+F~nlW0Fb;_2Aqr>}mj!kcfvB5J?0x`b!2>4zq1Jzvm+txZX
zf5KI%(z{(#y<)lY#{YrwUfvXsFhh)y9uq2cvDk$c8?%#2oGggp|JkV{%LOr527^kH
zT)Hi)p~0SKteQY+MUM=!QSA{Q#p+cF@BfxJLxAi!7RkO1-o4zIeNJN>bO0I
z7&pn`k@hm2&z3}tvpk+`aB4l+(-r~8GVk}qdfGTisxl4vlt7Gnz9^{}ulWw+muiFN}F1BoHHuP8l|m9_@d_
z>{h=mWd}*=;3Dw)h%xW7*aU_c!HF>rMu?$R79d8yBE)#*3_%PfsPPhr@sym!M2PX&
z*3M54GVlATsrpG0b$}p7pFPm&14I=6{)MAEo%%qGb`b0kqthxuj9YFiLX2*%6kJl*
zR3l>i5kNHzfCpszBKR7Q4l%+%ani24T|e>6-R*YmyocD8(GJ~_hEqTQ|&ju38&
zh4zx*_eRZi(9dNQgzKFiQG4WLLWtPUH}-qHx6-1zY|$;-Lx}bR+wJ=L__&9GFgt2S
zZDzh@!>4IXEErfZZmaRN>~_fuf46I*7v0{UjBed-SD*IMMplG2N|H-|+)Uf}DG&T`
zo6RF?8($GZ#Jf5%8>yInsW=USZ&zSv&7)$L=b8+W^w
z<4t&)+axpie8Cl~2?m3w06s@Mdiec!ais>I?d7l{g3qNT;j@v)gTITor5l!YnfHg~
zLbZ}4xQNH61bn_BM>aOXGpr8qNw!kEUCR>nj;^cq5bIW72EC7a~^9;>KHI}$(Dt6m;^`gNq4($>k}Kn
z0QB{av7;jZ)k+HhbYKyHR&eVXr@KN>^CbYZzmp6lE}M2Ro1p=VW
zn*u;zuh`sf*9p#t0MON2_yAhxD*@0WZ!QAR9b74>_6kZ-HSTuJWF8};M?D%Un1o0E
z3#YjiwC#4i<(h|@a|m&Zce@^UJP>yEm;bWcRpGJa(ob%xix8{)K1d8NyB9XD%{0dZ
zBRGohYcrO{2Tf(l2|6%jfV#)F;J^$nB5`=Nu**i3uz@~u8}WN9C+BFD!#Akx#C>7u
z4SS1_y;X?tiV)#}kWLVSrcOc_S{ZCbF7>zVcD;B^+>>;->q{_hN`D~#FTnrdf~@ie-7Y4!+cgef
z6fgs=*J>Vg(jexPe_TV4VIHzp1k&pc#G^D+l`(YOHKFB-lIZxy(Xqm)V$DXag1cQS
znfK9gk?y8Qf?KhC(&`8UU2?%wzNieV13Hqe)Na>YPdDuUTfIcbtF+$4ZkGr%bnI%p
zM8}TCTY!$juIN~C1HBzI5yrd_66kmv#zQ#E-D}yTmT#6&?_3U=T)GqXjcD{G?)w*p
ziPMjYtr%gVR#kwBgNrcns?O3Im7qpbquMgGhm(wEblc$kb3F6De^#h}BvHc)CT_Pg
zM(XriHn-dLAJ22SO!IZ{9GbZ|c4e{mB#*Y+RdJo|cC{i;HX{jlyEbhV_ubiQ!4r;*
zO)~Embdd1*Z7DXhLN=xHA(LEY4U%}XYY5$t3x@Jw8~YvPz-QQyNY}=}uxd;dLwG(grNA$i96P
zc7-K@Wt_*84IcQ*b=)lTe#fm~tFjqM@bgPPC4l8jUsQ(G0a(aZYSnt_5plo6fN5s+
z5?FTDdK17R!VFl}u2UTeEUTD@GYWu3*ww`Q4H96PD~x#|B!DHo6@cZ!x^Y`#OXI}Zq>TTsgHPI2n4%T>)lHw=vr`1QP4Hkm4ZvU
znrh9E@5np`z?c89YX)ub$bX4_t``-Ds?)LMfHzyL_V8v~dM6h=-5d!%CkPVyT|Fc+
zc(F&sH~2J;B;}15TV&IWDeSkHOZUVF^FPFrMwiHMa&hC9$ZK@1Lx>c4YIR#8&vRA}
z*O5`yNc9oJhX-sSdJx5IiTq#33k>`6(sm*kW
znt`wQTQCLAER!{atNp-+#vXxpQ&+&A_-#@W>iPe{Dy$P(Q?$HioXX7PS
zA7#7+SS{@8$Xod9K-{Yq#=H;`Sp5XT=kPgqm&TUJyZY*-Ze_@wEnDy_li)@Pz~7|<
z&p$IZf}!=pva6gxYpt{Ztt*Srx)(R8aTikvYQ6+ocXpDY#Ie-HJe3x`yd`o!^^_#)
z0zvDI^?=q758B)kd2i=Kp!M6o_-Ot4VnORB{fp50$AoI*Q7T%M{g-(d>X5$#t#8L8
z|AlumBGl&D5_yV~fto)>gj>8s9^rTZ{U<{o0DbX=kX{~JE1?l%_@g)P}$DA
zz|x=WEJ7|)A;K#{ga<;JK?s_+4u{!^Us>uOs$#=$(6$n`VQ3oW|G5_$T;C6>tR+s3+u
zr+*yd&dV169~4uqNq;Nhbyut=)7_THk8?uLrQ5+|RIbxkTq2W*B{CCRA`ja+US_OU
zX*W7)aAYgACTi&J%tKZ+_Ym;+Z=!~(eGMJ|Y~yRWt|U5s;PGUGcYf9?IN!X&ypN8d
zF2N+h3WiTwoh^|oeNh=!2XrJ`sU`A5o&~c(cdM7^c#_teKt~Z~k?j7)OLW}Bcni=`
z*cBanT}E#Qzx~9<4u5917k}^
zOJuF8024L-^OEP>us5~gJY9B&ky;4eqHH$RSvBn9G<=ppHZVsu
z6jsqtUWkVNccw=r9UesH__;O*)3n2l<7ybq?-sacXbhfQ5@lG<@J!X~j)7{MbeyT^$04SNL0|b=M{s$=X3~s<276f&nn^VB7%w9%@dW&wDVEDA9osaLBPXTd}C8ly}d(q
zd4u0;2&$J~D^q>?IwlI6qj+lvpS~o$D&IxRb2OPw?VV0FnE{H$dEv$?SqjNrphMB$
zDurHAEnKDk9@vk5Gs<7UYB}8SS=>(zAZi^G{{9XY0tIW?#~nRf|5jYu0OF*Uad*W~
zu2&Ku_VIYI|M!i0m(kkJ%=;a$p>{k;@XcU8B>>`6*=CIZg4LmalC2a#{FJEo@s(OH
z0pcO%3jjidS+sVG@e&}eG2Q}z5OxKKX%`4UoG6TWAtV6e1jP5@w?DO!ysh(@mqK%9Dh5kTziNww+|Td5xo`RAbHKc^%N9lIV*%~$S4~@
zRT#nH-ap1Zw0*$495bX$jG_VDnu)UbK8j#~?gQQ-$Fo93&>~C{5-`UOIefnV!?0Z&
z!(sTco6ee&H6Qjs8+90oGACS4kK432|?mr_L_q%<-!&1^lRs;`8l1o3eLEAWu
z_j$8{E@sLf)pn`H{WVXTRKIM_>8VEsbp-@YUw7)!E>G6ac=nc~U9H
z8jCk!3%7k}*qL?3YJxq$Q-GaUI(j&-CN{NUXIV2>X>F!qN$mV?PsfuDzWzcxKCyEN
z^ZuBeuO^hFDzlJJ3G5v2i<0Vqon$Mu5BRL7TQ<1T>Lqqwr1d6(E9xEvSH~DHvGWk)
zEx=A;S4UoV4lya%Rv7a_NMPswKfv%$hOy~vA8;pMz0`*9X^OJy&zS^&@JUZ?&gu{w
z!O-);Jz{%C=&6+!py!k#^z6ug2|YQ6+|9^)iKi6f|sg~4q^FK^AeNIfNqx&t!g{20LtI_(ZUiqLaqLbcH-7468}#XJhNmirXJO?c$L
zaD?p8$lk&Z3X2n+4AguuBKzWfz*`*;Vzo}t2a#FvK45o`EtkH18C1xXcY(z4r~$As
z7Ri`hs0Qr#A1)!PTG;iL3#TQ}FIXuZrk#`X3*&FZYh=Lh
zJhw(D^!_Gn3seP5NWo{)$tyyH2SS=a2%6d%^vYlkxzyi{)gbrm9`_`@Dl-}^o6?o?
z{}B8ij{N{y6zv0^b*v4?e@*R!@A0Tsh;}Tn4_HUeIO7-f!YNob
z7G0HDyIZVV*!k6X@XXghxqR1{YE80zz?WUInoJMd2bjRQKbNkz4GYzVN50FzlSJ$T
zGO>L?{tU#V**@R^Ck?J^&c<0o8(BkVVownbtrIm=?Q7_`S~eb|aK33tbbQO>!M*lR
zwF;u+9Oiv=+~R9^9tVg_u)JcDK*tJSREE_79m!T|AMifUg1G-}^%5PA)p`@?D8ekV
z+{<{0jw$0UKu2L$bnJenpyQ99urYZdB+&5?EQi9orMvk1fWP@|3AX1F3g?G`I&kXY
zanlVGGfm_E5MiQLRe*`pi!kxnmV${&P@^RHN0Tr
zm#cwBnVy^52i)3gZ7#FI_5uIoPKZ6)K4AB5whtKUKAm?x38@dqKH%XHMfj@SmNSP4
z2O6*r42=!Yhs2KGK@|Sl1>YJ`$KifLPFlAmI6_)k{RP+o|J{yyM7sv(6#2}yG47fr+QjD&Fb?-5nQj1ZI^8m3f3?kiZR
zx~+VBAIZ
zmjq*nPudfRKNu@(aX}6i<0cqv!0G_TWGl7I-`Z(TyML(l5{zdsU$D#SUH!Y7%*GUJ}<-S?Uc9GJ-li*dX)j;)0_q|mE
z?f4`YQhZk1*zikN}Ryum%Wg
zK8a0dz;TeTUfGKGXo}!w@cO_psBPRq3^-=t?w?_21RPpr0dUMO0*>o;72r^U8ZQAH
zy_{rdat;+TfImf)^P;(d>L*E#5)6Q(yuzHCOy|ms0I#Bm$2dvz9i4OlU-U4(Gc14Fl#|t{t6UMv{66kQx
zQlP_>k7CmqIyCgvD|_`Vnj*LuygoXdC0`wurD3%HwQX$82pzP_0(4kdgbv-g8I8}n
zYFHtK66kP@lMGFcphAWYZA3W_9V*pNlBff8|LaS1u?J7y96IdkJP34n{Y@VoK7LB~
zzm6+Hhm{G{Mx#`O4tFsRbZEVp_6=^rBmV&%!vDimucrqiIO)67gD3e|o8jt_yoMI_
zG{(5uRTVx*M%h0803$eD$?YIDS$$>h>X<=Tvnm$2F3RGZRxm(M502BN0jXF>2$L)%
z|xqG4^y`*$&P}aN>KA9(6f${j0UY=;5@aOdEZmb
z)l-tF3j{r9EdzRM@cLeI&r
z6jbYJs_}=mU71Hu|4DWmf+O(Af8mK(d|1tPdeGO&K+V^`U+VOrgW~}dZv}l2nR$WQ
zj@pPO9$PMb?CVe=S3VXJ!;RfxWBhR@*oSIB!Qps~F~T=^l6V2?E?p%X|Cfwj8TUW&qF&g4>k_92Gq;R&3+q1_yK;dB%A4xMRBMu*9^B@N)nv}G-NQbd
z`*Z0}f6;kl!%POAB;xddiJczo+Xi$skewd1bJEbbuU>Tx{g`<)^rknZp|9al8mh$&
z9iL5T*|8)#<~$z!{VS`2k>xGS`(v)D?oLaByPSN|>I@w_`Jytc4(Ld>Ql|&qWiu~A
z#~rL*qT`lYZvq`fn5{d%enrhdbX>wbCjElQQrHz8n;a?V_>3^-g^)nU4Aw&7Y0_Q%
z(}NfMwgkPoe#)hHS)&ddz|VDQ(+v|lW0=}J6k(!PRe*_0i!gD^?W8vA^zsAP{E4(}T(uao@Ea`RhHI+M>60?7W687HiQwf?N2X_fQW$JEH5k
zg-q~iIVAClaUFYkE?B{vsQ3T|IdDf7i4AUflg;xD_LD?VcBTl@72`u2STJ~k%ZsYi
zlT1`4({-UU@;nZta6At9P@qx^RJj6^6uBO71x%8i9^B>#HJM9n{2$Bm*;FS<%3Da1
zZplQ|`wD?~rEphNOPNxq2Q8gs!0$6ZLCZq{Bv$>ks@EoGTM$Nl8;71_qHH$xh^$0{
zN15m?;<69LuH?a7AgmHn%L@sq_rdAGJ}?ZsMb)noZc;dUfF!x}TGR5fEYXYZk{2kPmI*U20)a}rgl^fcXAqD$sikl#zi$0R?$#ih=%@;lUMyB8d5l_
zlq8qFz%=YFisZGV5>>;_s-dt$!zFH^ZlUTVxkV(9RTCVKRZ>ByI?U0-j~|itd8Y?0
zH^ki@uIW+|jJNQ3@IBa<;UQZ-{>;2T*r#jbB&o{G=96}51LK+4s({7p^nld?jLBAN
znZGbm?=Y*EV0@$2n*d`GW?5u0sWk_Y%gu5E5YA>J5PLD+^-N
zb<$qFzpq}|XD`td!G?v&W&Tz2Szmvd|J>iP2_jI|Dhoh)MG+_uKtzC^qXhAl8P`C$
zr;`j#&ZI)-rxQgvZ<#+|{UnJxKtOr>#TdWO9l5z>{vpnTh@qCh=m)B+ACf?|)geWJ
z>c)g>(@=`4HG`UuFprLXyxfHdM&XhF5~l}-hqFB)+N<7C?B3?D*rfAVIK1Hv8JH`G
zyjA&-3BI0yB*yNe>CRlxlD|sBLYy4ys$1ivbrz+Tk
zYoe;uo=g(hy`9rFT=%;0%KE7)RoC$vY_D+Yn-GKTT1TkKTxziWqAZ3|FG*7VKS|Q#
zn5bs0ad=xP+!fVQrW9;n?IZ)X{|sTXq0^wHw!vV#i^qfybfU|Maq#86D4R{~F7*d{
zNd2;b*+V8N;Q?$5tAHYN42p)&_hEbSj{=Gm4y#L&OKF>!ht0;vuCW;UYW%7w
z!cqJVyd-R&G0sqGzBFtcWg7Ffz?=#*4*cNs*@dt8eI_NKqc_AcV`(Z4z!fofrrZd>S
z);CL8ODP@P0Nxj+ckBMbO!vvvmk9{DU_EXCn#GmT>@d6lw!HKsL^S+O2V8-WP)1{zCiF0^5Vy
z6v6h1t`t;TH$zoxhWwArqhse@rhS7?@W_9qV7rbdL>vCD!gjZ{u}L-LmNN|e_-GCK
zUsb*ylU$}Dk{Gr#^H5zbxSBud!7u#CQJZOsgq6d{@&?zjNXS-;pzKT$q~O@<7b)1B
z^s0O-CioEvlDLKzT0;Y^fh5$>9*NZ;Z}1}$60+4IC_7UGX$>`NSVM3qS9VpYG!t#K
zO=4@kM)Tum;TxRzs)C)hKqFVcW`|c@0h462!xN5Blex@hhqq<W;dCb%v%@D}L(7YvfR@@2n;j1Dm_UfO#c_8(%4SmsN&P`vsb8?S
zJy&lC0Ck^SSOt6Kh0G2e5Y%^|H_^Lhd@Hj9g~Pd$DqtTRU1=LPkr%Z`9X*|nLT
zC1;1$9uMySJ&X3+?63>-KI$}A{UyQuEIw&ZAP{0SuE3@neX3W2SRI0TvXz=0)+XwG
zdb-xj+2LX43uXrqX0yW`#>?5^2II|j+w{XWU20$5gnzKVu&aq@wvwQ}t1#w;keD5w
zug2`~$QlnzFM*Pk68*daE7%?`_dj!h8F4q9cw?69V2c6jh_baBL+
zIy*=qHLlGL!<}Tbd+0Rhr$Nm7erl(Fl0+RKv%?WJ7{AL8+T84Lq4OYShk6j~W`|uz
z$?R~}enqoGD_07tefo^5)(rV~nMcRI<^|d}cnOdES88_n+H0RS{9~OR4wjX%^eUYl
zCRRfj?&=YF@$)<;xlA`CF?L^0cjkiq)(Rs@=%_uBuyPn#-emK91MC(-*_jm7H5WY3
zC6Z#~SDYkN7X$4Mo|5=`DP+x+*I5C({zE)dUc>91JYFRjUjOA9RFk>f@Vcw4%u=UH
zQhtym>9$N%m&S;uD}}qFTFR8d>j_RW^s4#-dQEdHeZ%V^9usCX^cArRE=We%Z0Z><
z#)5A0V8+RFt<|p9N
zUId`EncgMw`WBA|`1ceXWq5rB^F9vrRQ)BvSBLnd9ntW*oi9qN174G@6kZ?aG)K?a
z_@vfLy#9sx0=%Z~(Rtzf#!I|jV7vu*E$nLIfS*{!@4JLCFN6eM9|EI>N9+C^4R3h;
zsNa^d9#T5k2E0CAui`arHG$!E8(CorxI}oZRTkj&x+1*(?l-|}C8+UKvf=dyvNE*E
zFnH$)=ciYg_x;pQ{UnJxK=8V1KE`jG{Wpi#&p7pg*F7QF;dTGJ1g{_2s|c?LyHc?9
z0j63rM8Bi{DWM{IDld2XPbnf;e
zrDsO@LQc>cpOYY6NT_iPIHX2w1dF^p-$1O^HM0^=m?2)_YD`i2YWJrJY=+p~b9*j5
znXI|;W}m^6%Vc9qb^vXLSmP{Zz0zigC5}*&>1Q*LoKO9
z)o`K^><#{MnQG|G5bNc3kKjU0Fz&gc8R9cX58K`yyT)dSp|a*tRKYJ)O3o1HdOX?S
ztjFLen;|+g?<3DBZJZ?dk{6$}ClCxVBDeQNNp+YZ$X05G*vn~-p7GhES}$ja_n0r3
zA*g#aLrgPX&Jd3nZ@~;9>}uk#cb6Grh%n}bkeDHkg;B#@-A0?u5X1bolpQLigA{n#
zR&+c0D&2xDV-wg6(LmO4i5WtxESMp-_yQ%X>UF7FG<-#!5j@GS{aI!RC8+TdGsLrB
zvD#>ccK`*)@aj2xa(JYKGX+6QT|OLT8BHU&ale$HL*e&q7#LW+KtH
zDnEruE;AiTJVWH421hRFzDgKLLPwpAgyq3J-)HV8%sIZp$Q5S%KwX}yrH<@O*5PJ|
zzrLaL%pzY%3$1a~_fjKCsBtM0sWB_ez$m`K454++T!<&k5btqCrl@?%cPOu|w;AGZ
z*Lpzk46uebur6h?so^7!NeMPJyzB@ynJa8+xJFihsezJ|pCL*5OeU(tC$DhvK<t$0zxyOV
z-w}zj+0-$rVMnLo<+AxEJkU^BMMLIP!yPd-?8wdr;rpnD6b=m~$)(4dhU1?T4Yi~a
zRl|%B>1|MG{>Km09&c4VQZ&3ddB?mS}&)D8O#?<
z4I<1od+s-0P7RgDTQD^UyP9~RT!0QP62`m`5>rDD7&YAVcx*bG8ZP(EQnrhf4h{gX
zKQ%nb>m6zWn;OhTS08O$}{sld0jdRMFIMlq&_*e)^xP)(rVCnaAiD
z_9zV%yo*Qv)2Si+3gc7nYA@B1x>T%E8~(#b94q3k4%yAb^-vgq|Bf?6xHT7ja#{BQ
zEbqehlVg9>SLTV18B%)URKx`Kj!3DA&=K`%@)=;ejLlK-Wbc8i3Y
zVln6?$)&r`Vi&;qAH$7oUP~%bd*mn~MC>OT`w;J~w214&@Uq9^7Hz97dKU`^0H^Gz
zBHW9bf#Gf0a15=7HIueMUdvr=dEwvHzLHBjivurW-gP2(SG((b(neNSBHWIMK_y0#m7;l0;n~
z0NP+00CZXt0O$q({L5YKcFu7~A_BkXl+JO;jmJ21nY^`X;Tn`C#j7fU=T*k6|fVWtv
zHtbf(z@9|h)n+0yIHLPKL04&ZS9^q$21g#2cMUy&dB}Q8wkm=qc$9`}aYM%-p=Cu$
zbo}xI*AlG%@7F4L7x+EqeROQ6cY!6T%CzT`R!10sQAYcsGOP~hNVZaUwV&{G!|&^?
zUZUeUT5kd!MVJMbhZ`@^v9<9Qprf!WI$kMvp@X`@m={6<9Z$mAEu7?b(>AGtw@s+m
z5=?O5({HE)Uv)=JFKg2u!qq#B4NmzHCTdj$nAo%k6VLfvFi{C=G&QOPmB%{CXzx+?
zIsdd}-uF*O^^YWKc)`R!pTJ0cXeVIezuwi}-Sb>7GjTmUhrU(z5W2lR+Fk7{w}rN2
z(9$X7$!fy8+HW?30aWp?)q?Hbk4<6(?AoxKO!Hc#C2(Xd{ildvqP|(=MB!V|u;JW#
zv^#V>n4ZJ^u!(d~o}@17_ZODOW7fnU?!?z*dh5^>VU+XjGLA{Ut*91SsUPnC?lmb_
zKCc#b4KS9id53E&7CEMx;47eQ!PT(ojvj6`I4*5N_Drv-_`E^MA$yR=gYUrK>$=45
z%==wp`ZwYxNpPc=Pl+M>^?S~;_)`ozWXV=)J@`kW-sz*XUJltunJ-umiZC0pBaD|r
zcChgl3|V1U*UhdcTZN|!V_pb}Av+mMy>Rt|vFU6*c#f}LS$!!T3<0l>NDXJky1joJ
zx+~s|O%M%Tt+HU~wkjIB|KnbBHlqYJUSjCp=_I4w756wlUC+Glr}^q9NpPbVPcn3m
zo{TQGV*Aal2m3e=Vy0~X!EQac$3O{ZdTdh^&a`%=pxW~PQ`Pvh{`Z;3fVfsRI)b@)
zT`LL3^gh~iF=iyMja=LR!*{QXOB-tJ
z?ll!Z(I|-;>m>S!Hm-lYTV2kH{#)k#-ndYEqa@fx;!^@Op7upab=*Y)TPf6-@9CBe
zhFHBsjR9J3Vxp(+(K6^X<0WczGTs8z5Ozh4VOt4m>>`YLAtX>^@K~V6oP2CLLyf(B
z^~yfT(iA~g@cO86$q#WcLyafjjQeSX8d_xmYP2sxjmth3)KG#NFM%4}oMdQnG8M8#
zPzO=YTLdjtKS`nv5Y*Uu61v!vn5qB$Piwb!9t3K9beE4BtF9E(Xuf3;YW$v1?QxW%
zYDA5Pm|rN<1b}W0el?-6VpFI^joL)#cLPGC|=jKaQ=8c#9Fi2(Lml66RA9
zuJj1mRJt#IDTnx&$q}#aFyid3z@+?Gd654ok3Ux)yaEHA;x-d(o4m2dgcTHw%Qiqz
z7-jMIP5baid9~rh1q3jmaOFb=*@a?`Ga=$7Y&|z*d2l~jE!-4|2}6=xdeeL*wqOXK
z&1*>|YLDJl2==Bn9DBQ%XHBFGe(?}xv%!(3S3T8>6zC-kqL;i7y&6F;1Z$$#PO2A0
zLN7^j>Bmj4EW4>YWG;BYnB$&1KcyRx*QhOr7Y!~KzFxx_R3M7F}MSD6caKSFB4qtlG5QuE0z-(=z
z-ls=uy#%v|nJ)-|L|AnH(Rc}FHyCdLm!smZ
zFGu_|<_ktVb&tZAdyJPOez@@#jCf(!5&znH8S&={V_pb}5&sF6cH!UdYKe{bzP?$?
z8cFHkY4G|Z{;{uOi`$6*0W)C1h}S9$MtrxT5&!r*GUAn>##7_ke#N~`GTNOT=KOR!
z^S+;2sh=cK2gr!;{2;p64|O&-;;(WZ#E5SU!EVI2x>!bhpTCPn{9&#XR15D=)fm1k
zV;%!yXcY|=yn#m<@pYo_eEg{6_=5Sd*}!%rmYDo!z3V6CY47vM_OFfgS)OGQV|08G
z!9@Ku&sB&0I4&BYWT=;VdlrrL9!^e8=3*P`!iG{8FXC9|+lp$Tl^W}foCNMC&w(Ra
z-W2y88|$lG18aghP^w^df6vjw7MI7RZLAOS+Jw6ZCCB=y9uM}fE7VaO>qjx~_n~&$
zha|zCWYJi!>x+`=xF`89V?E_GM_upTuJv-Pzs7vQSf}pMSbxNLIo2OA-h#0%>^jz$
z{3&C7fH3BTkQnQ~=P=f{m%b<+$j17Oep|{~N$KDX@cLtY&gZekZLDv?$SfG^T4ljl
z?_D(3UwT8vx)RiQiLw5elZ^RA{py9{82R4Pj;oC+J>R38prw?<}o1d&B$0^jz|9Em&ty+KRWIH9yvtB
zOT{Eu=j*bOo1Wyg0bdU#F`azjtPnOOqa1f0fputj^2pd3cG~@%V}_K$Q2<|?sfe=K
z;2&A%>1p>98ljVlcwU$!o|hNskjcMm(&a_clM7{U1F6<2uJ4b50iESPcW-Fu1jhEPYzVQ}dr?9JM9^#imvcbW^
zm={6Q*X*?WNMF6wR9PgJeRDG{5=`ckZV%t$_Wuk$UxDq-2oQn1S)%Z|
zP`@Jd9QwMTrxMhB3G}?&Nrn=AsEcj%bQirmPfbxzNun+g^xR_<(DRjbK+g;Q?Xo&*!{$baEfcOPU29U%){
z^HB4rSd|r@c0cNP5Kr}oK6N0>?KSVHqK0~G*q^uwD&)$y!0I--
z@3^kSN=W7#jTfM9hldD^!P_JbuNHPWs1`QRN9Z0#Hz(%_mBTlv?D?nB#=pjjkb6{!
z@QM)OfskV$1WjFy^s$4AV{OH(S#eL&)9%wCXj8hQ{C_b14__UC5npuLeY;l<
z-#umP%^CYJkBVzZLQC=Gr7X?OrLjhIFGRbFN05K`rP0TzV9)VoV$xR&4^+zna<(eX>>
zeRS-ir>Tu>WKA5*@G5dK2gjSIiSkRDv2!B_k}N56^Ov(cV+9cm6q+dEY-h)jyJ`;RO@xXE9Qr
zUJFe8*VFEUJkQ~0N*mxgG_%>QXr?{dY4^|{*6V3^C3z4aFyU$UpE!OtldRDp(&@R_
zBu21R-z#2=w5hhkGo^@NqW*j~78N$*Pt9?6hxgU-DI6vVAs0I#HJJ-_0Oo}opRABV
za+UicAXBB#E2@R7)Z(v+lLf@9GZl8+4`bIVJX}HSSJ%#(;C`%=3l@Ki9X;Il+_>bTJZwo)5CO`YcGdW#2X
zy&R%7%oi;FsC%^dn{2!sqGOG>V2BF44$)7)lOZ}t81q6%4AE{_;)XlA9X4D1-R!re
zthJO5T7p+cpoT9mW07zEvY}dcdfb_!p{i9D4AsF!Lv`*;GE|kI#!C#bd^UBHB;`FNNuSF^e+be9(QKt~S5!-xQa=Pa*Gb0c
zeQgR%d&Q-3Z@|c5vkvu`@L71cg{_C~?W1fqwUq1QZ19OJlLb9GjbLkq2f>rD${Lh8
zj*H>*{oiQMorHdaKO3Yzmn4@yWCFWkZFu+{5QXorctjl)dkR5If$y11MD>o5S_tu?
zY&LbGYS`InIFa!_fs2biX
z1bb5(Zl@Z0r(R2BzO+-Xn{O?G##xRYHaJ__=lu}mT;MI5V3)=hmW0N%$CC}(UJXat
zilGJb{*ddYjgzD*)00oy5s3m_a>4gcx$1BiQll5Lm4e2NiF#*UrS%dTpJKiM8bz2z
zFInRyG~RB!1<)w$YT|h-1sZz@V_paeXzYd6Z1~3wvFQvNFZ9(bYbd3I|A5zr#)tXQ
zF*Sifj)O$
zk^k(6AfI4->JLHQ(~%l&js4C??E4@zBDSQ!fpqb%-;%F0-`#{Ta~0UA;<=!HoJy}Xb?@Ba{FzwyFNu^9A{;gE=
zXY7X~c#o((a-DeQ+jW08#z;V7u)HXgMgou5P
zvFGG%>TfPFO_s7>@3F&lTU`|U(YD6k6C{lSgJ*4x+GitmtHInf4R6ABZb!``%Q3E4
zO>iW53L?wFjvl^!T3o3`mb*Ugn|e`6`1}L6=^}{5KJew*_i53U%sY$XBW5}*B>^ZG
z!RJ%Hs0^z^WJ$JCTVr!Q-EjZW>Lq+$rS&Eje+l){na#7FanSK7vAfZ+dC*OJndwl
z=F_pVE#4Zday$@rI`k>IHP+W-%cbwW1S;gpFNMUg%O9{Yetrh~Pz~7eG%lg>$Gya2
zL34%-P`3r38?o$vNE}`*?6RvRY@lCpmH2(2le4|b;Tu#oWgOc0`A8A++jSyDctwct
zKuAjnK~paQy)yWiTS3ig8o#rHJ0;5
zNp(O+vX$Bzo9J0E8=PnL5*<&|dK2hK-J^B)LB>mT+}n5y&{5bG9s9~{?clFIY)oDV
z33Tj+C3X0qyPINLV|5bh$+|n2ZuviT;GB7Je=tn^@5I>B5hiL?1(-Oh2oq16D!ow&
zYBcqs1(ofcWVH8SHp4J+Uulwui38O?lBnSY6Mw!5BlW*4Hn%mlv*$VdwcU*{Hu~09
z+y=5o+ZyY;)V9X@kq5sNWx}noK3m0o_s=@W;)8Co_S1MESN<24+~LOOV!PRbd#vBO
z)Q4a~BR^uIUo!h$elg4NVIc+l;x?uxVPvD0b3FlSH(PRd^a$DDE{e>h9~nzG)rKb{
zk{bc7$88yF{fNq|?-k3H-v|D17YtV$mbRW==`{^sQ?RHGnzvvza1Xm?6Xqoz^}i?o
zz-STPWruWSiaOZQ
z!#huiOB+snD8LjgLRXZ;iRm5>e&>6kHk3Fqo_QZ9W^1e|3H}xXpVT!R8t~brzNieV
z15S{w)FO27c)BbKD-X7Mi4*&3y$PHUVHQPgZM<~G-@Vis1vnw>YT_g1es=J_Fy@7j
z#0kus;UIUfrIYrTPyDu&WuJ+qzsBxIm2J
z3)Ld@z!kC^3zpfFj3gJKGN8#K^xhj~Y>_P5OW=F6(?$E(+;FUT`p5YU7pYogiX^P)
z>vi-!Vw$NFktN#9ioIcnvn{0;p;>o0{f2eARSeE&=*x!I(kSwtGRRsq`|DhefI~9MKA&;8BUT^i
zL!3DG`R#3Xvi$iQJ5%jGmo2whv}s7_KBu|jEDTJ&$g$`|aqU=?FMBB~NmwvqNmJydp!s9-BmSXr`lWo)KMdf=CjN!W3qm-d$q^wz&MaiHan6zUwWRTJkk4ine3^d~MA77myf
zC}T~{Ri@7o;HRQ8(tKD%#A-e
zfqkSOO}bYf#*dmRo{9MdX%da@-N5TMHI8o68Y~n
z{M*p!JdE085&GUTcRRUh9O*KeiOQHOuF3cmsePcDTV(}ny38VU!M|lI)bnS~XY^Q}
zU=ezSlSjkf&a^v^UTePedq)FRBu(TvV)b(;yn|KIs
z)N$jyql~V+<2!#9wH=7ah|475@s4M*d~ay48oPKnq!*#H++_(IDVF?;``mqls=(fl
z+3i95#Dj0PE6VbTDyul3xVni?d|B)uFPy6gWi*dZeBvfMWs33eSY;$UUi!qcGJ5L4uP0lC-r%Uus*_O`*T5KmF~!ukdJ+20%X$$yLd4;N&;sHXp+Bib
z=)JPFTZ_=$te6|V{iAI+y$C(RmGQzO{vwyvfsF#xBJ{J>NXY5K6C#0fu_vX>qZgsu
zUFK1QUW7jDA~^pel3d*9_{FLMUt*S%co90sCAN4_TjDn?rMv1(^&)hlD-zF19T(YF
zWTO|MDdJkrN8QhA51T98u9OkxlfJZ1XRX#kF*{m##Fj}yQjw|=S4%>N@cy5pQ9m`b
zyy_U6zrTxo+$`#0hi_=n&n}Pl6Qgf*+8B>Y68eeLTyc2+Z;UFaXop9OYj-;wBTB{B
z;|1^}RDh})(KT>OyJR=g>q?m&enLb`v+L0OLu;KZ~8n
zq|y$<=ma}hmyCyUhe0hu=PZ`pSg=fdWF)x=l>xHTyPt#}ceRWyl0|z7i~9^NRlevJ
z@i>n^dg3Bgi@Z`2R`fG;^eL`TS)#{Tu{Y#78)bSC`s^ZipAK5Z*gQ8wUpCB@Mv?cF
zK?ci3sJQYx2_2)i3q)tn5!X&5au%TjIMqjQ7a37T35)wYGDVf~aWOYuxJ;$2NL9w$
zBwAq{W4c{n#+&bc0KAv=u4bML<{w&RRTAP1c-N`{38ZpD|0a+
z4W~5O!@I!|s}BT;6X!lF9lqg(o_40%eYU&a$&|kMf!%n-e1db`!sqQ-;Kbqk59gqm
z-W&RtxOObcIUZ#~37i2EcOLh7k2@)u9qv;^OSA}G=hBVuKj?gg`<$%u_3YsZ-7WVy
zSjQFav#*XD=RRe0Rgu$Q5N;`Ou#C7&5+3*2@nQ`BaGq^Cy$C(Rt=HgrPLC$?x?>Ak9UggAL5jqron9{k9>>a9-8t;m>
zxX(4iQDO0*GfA=GthcZ+PeQSePy+?i#l`|puB62RM~W!}>K;8+IE_FnLGmP2L|0Ej
zMKt({qow*)PCC2gYJ08-A+xsEQpW9<$wXce%(c(Sxavu$PUIL8p{VD;&kw8-u~dEQ
zMd;1f*)2&u30*)Gv(FXs-wOV1xbrNuXtD^s*d@0x&_+y~%{f!KfFIAL
zZRuOR2p#{dUW8_dI1A@W5R1@%IL{PQR2Ml;wstii*gSU^x(R>O@SHgIxIo?`FM=_w
z+*hDA|FFXDXsIfdH>Js~RK%$MxFRla*+m)bF+~h=i2Avg=pHyi>_L!0=uXk~MCk12
zB$w#jfT&CJ#e-hD3at;K%j&M#Pi6Cl6RAG1gZ+TGdCOA{-_W{?EvMYKfAzL+Q9r?OSP
zqGvuRuH%+{GBMpVuh(%!&-|T^8`m>sbk#He{3qEn$H|DxB;o0qE6%59e&-xQ(aXL|
z-0B^Ch?EXg5m$9QHEUV9(zdwnp&wI?aXnOJ8P`L%H0h!DE|fh~5z2U;9=ga$nR3r#
zdS%tq<>K01Py19oNl+Cad+6W`X<{G$CE2oXic^COsE3lUv+O(RL^*3ISl?vU($kSb
zwGYo$s-3{~o8p=dQ788^0-LxMKfUbR!J0-b`(6{P8)wE|T71VcyVfq+K6+*>{T*nN(I|7@go+>vCb>Ub&`M%f7`|NxvajrW!JmT=vO;
zCduqy7;YC
z@i+a>ZebAXtzz)%kG^cUK^jHgQwA9Ov+R4!wRdozckjSi_6@FWI|`ZX-^d`<&tM4Szk!&Q!b4qh~pp@;6cwy3g;faN;oj
zXFA>TLviiymRpnwCGfOH+?IhSq=12c5K!hKFv`FbWcLU(J~
zcdm{r+~?UkZk+p+(N#rWwGI<=4p2s1CJB%GyfPQVKjoZy(aXN$-Rd1NhDbbneECcP
zfpf&2a-b9Mux+j#=zW*l_EesBp|V8$;f-xg9BAP@q3K*jC?SsnJ=aMYC58%J^b}`+
z)XO!+*`aDmf~o@PKwFH(f!_B_vSr^XP95Svw+(bV&<~E44)oY3nmEv}JgVJ`lu%79
z`)(1}^oV|gg`oo1aw~ofOPq!FA<8eTanc~?M_8seUG~j);*i)V^l3q2&YN0#*;nX_
zwz$v20jRKe&?-`F7%rZ*U{z3aJXA6#rSTxm@=Slr%Ob~3{(m7+UI0+xnh&i
z;7irMPMVX>c`6+dLS{#CSfSya0-4AeDiIl1CL-gI$VntZQD0=yK0k1Th^6XVFZ=G8
zX}2V`?E8j@W}n^i-#7TT;r<>NwaK!t$R)QhaD*tmu8af4H5orBPbmbx@2@I@MyDsJ
zEc*_AO}0WSAgy8D6>{0v@EMEL&)PTa77Jcyq-!l4pHf?Y1a^0wt#-qiy=3GJ=A%M~
zK*OWzOHoe11*G0^UFJ`GiEZJ~eZ?bAYk6`8RZ7%#k4DXxUmQ1L!AGLq3`#ND5zggo
zF4MTI4_wXFVTAk+6uIU_EI*aVz*SX6UE);Knx*~Nqyj-;)>XhfTZ@N
zfW?80>h3M!j5Y_p0D6c-HwQlBQWa_sSBDbyJEh{sD(V@eFMjobHe88>10u%-f!7o-
ze6`eXb6}UgtPi};)saNKUc^}tcv}F6BXX#d1%Z#m-R8i*feuQCPqFwVQ6_`$y?Jkf%{4prO_XArm`@VdSeupp2wWrcl&tl(tD
zE)Jv~E)Ba?#ePW1){DE%fg(T|Hd9=Y(?4Z@3tXULcNVZ9kcFDW9tUV^jMbO*fjq86
zp~isXS05P6RSN9`S=l~7@gwoNBUJlPiFZ`0OjUQe0u}_0K$z4{1$4vg%%!ql23I1h
zlR@#T4;;mn&^oMyGh}BEoUSEwI!gF7Ei4jI!p8{V`oLslg`N{Z@guoyZKa+UiBuN{
zX5nBU_+C#oiZ@vQyOEY5@xZqF(9)w{s(8
zZw0meI<%Ad-lSr0RI&@j-R8hP_NYWA^?|>OEA(nDO1mKNnb^x(9M~XWL13K7eskbK
zKqk65@JD@FA6U(mu-|>4_|*q)zKq
zFKr_=@250xtu*hc?hX^MAaDi3q_zamnl09s^?@6>5-zEqh_fItAD{#BuPEmm3#DgT
zC8tpLhgcZau88R
z2^49HnBxAo-)|Q9%>ut!;5Q5WW`W-<@S6piYk?MWc4!!yYa*eF?uFH5k
z8!?Pbot)9k^oqh2Ris%Hu2kt&N6NE{i!UCMZJ^8I#)e9a+M4S8@Q_e>
zxq;5ZhtyV9he}3;hs-Q04;d6}SYf2FTxZzK{7OU;GRy3mnoxD5EL@orDhU-ujOxO&
znve<@za=

FMe)pXdJNe8EXx`FQ)r(Z;bly2h-c!ph2UBshcq5(?Hts>>>;1S8>K3H2sp{?-rU z`r&CO8)cQUw-(k+sT93d_MD+3M@xG(vwf6>Wv3iDdT4cZxY{Tx4HZr0(lA0ey}@Ob zd4(maKAM|;Bvf4(3F{Wv+L#iG45Nxi6jm1Niq`GoI4@?63ezeIi>3x6)rCc&$>OST zI=#AZa(Sq=j_U@~Xwf2tU#kAKjQa)l^E=?b{I&XD{#my8o`<92XWhr+3Pm^J57iMh zW=)t-P0it&i-RTA;fjIrHc!>@HD+CMi8n^PKlr8oHeO!r=(>OLe7rx6=e^pwQ&QI* zb&$p^*&hZ5Yb#4-Q!NgL>WV^DV%$8Ew3?4#;gg3E6O5~P;r-Fw{G3yB)r<3pl>y1<&7LZa#a4n;K)imNOf%$ zi3BIl3=WA@m!Cc)K~jUXyhb6PH+1*~Bv4)+no?MfHwjl4P6-7Ii;6-uHQj<4*(g>~ z6;Z}5RLwkCSyp~_e9>ge3G~YzJQfABCs&56E64ynE6OShMH5M&-!WQ0jY7X+V}>Gb zPZ)!gGhCfOR7%E=pTa)5xlZxIRl?CG&RZmqsq@z;@*g}d&yoKx&0`#?)7L1{A2U)E za7ugOwDV|${sT^SX(11RGMvI zaQKjkp-M42$d)HsOKpU}U|CJDu)I1{SUfYR_+p)s7f#c+8nepiHg&-;X@pCHlf(3) zny$e}DXE7mBP;+y)goolfOu5JFMj@}ky5J?C+6@FJGj()-xXu=-3*wN(#$s+!5l(2h0Ci znZ*0=fa2k4n!`1;Exi~nZlsRyam5*@QEPTXZ&26$WSmx7c zt)Gg^YN`q&MWts3tt;i27{|P+9j$LQ(!W&i3{Sp{Rk-kK=pZ`0j>klObWu@lRavO` zyii5+*X8BqVYStu7j(_Ed!ao{s-Nxq8O$D>*wry8fG4e!Z_m$?`&x;h~73hszqH zim`g67*u6x#2l=Q!CD5hqa~J}4Ef_KYndkyBaazX`4ACgoJ?^CiN)_&(Ro0OhWWBZ z%PM4AI3`j}pj;VOi*dTPN)(})`iE=CSGU-Tn#uIZio&WYswXd0SY1?Vl*_OZM)$nh zvWS>&3?}+iwH6;;8yQ_9drPzPuc#3HST-L$_oxU}gsW$^Ho`Ts#A7gH4LAAKGhO$T ze@Jb0waB}?T!tHk#l<5+g;lv_n6NrDtu|B>8C@eST4U5!F(`+|REMU^E~IK}Ol?Hi zvD~PsDld!3n7yphkE|4%cA|+6#v~*y>HP3{h3wqaxg-p>3=6A8q-yk@9+L5fv4B)D zdx@4kxNM3H7T2zNE-$Nz&=L%RW2LZ&FoN!A#~vJxM8Xw@tfK7c<)gyI>`zV&%}@#m zy{L#8y8NOZ6Ki3xmMb?(=%=`?XV+&{K`qKeJE7=p2 zUar~gRE=Hi>rCt^8>>!Pv>|=z?);;1dRUz5%qgoWsxA}jq)Ij1HLst_JZnPb3|)hn z9MdsRJ=riG?GZCed;a|YMf@A}|8BWjosNZq6LH0TWoX9e$(I=l#t!M3V8U1PFX`Vg0Gr_39q~&VlVJsu9{BRzw z@z>tJAFH;7^zOYenrVaBUC{T;_@`9T56UWW0rDTw9B9#FL;QnHM7g!~MukU;Sn8+u zTjc&iF&izV!E&03*mo#oQ?fY7+NLmA9GYA^g@s*Nd2x`*SC1aSlG;kfNtP)>u%=75 z;6>qDuDO>}EhbgL89b2*o?bid2~VL|#ENnaA0ldgY-kGJDpZ|cDpr`e3`phY6;@9TRT~qA54l7r zB9?j8Iu=tw5iN*gV!vQIa~d&#oJp49Ahk!{iTD~L{*j5j)I}3A7n{LFjC>J8j<32V z-TDsv&r2O;KT0h|#x@Eqder zKg7%*`G3@^n0YV%f6xEt+!r(N;{U7v_{;zQCV|{w(h>iE!3TS-YYT5r5N2`zr{b{x z50y#uz-G2zdwiBnm`-AhDl5;HTc5huGm0^K4zbsLI#?`H@Feo#V@1srW**@pc~j1w zmmwa{$vgQ}hIu6O_v)HZUSXt`rEI8pl)Mm2PnX_fs>4&N3o97(r*t31Lv-=Kcw}D1 zpx9l?*G9_9^)t%q+R9LFsPbpa*H)GlF%6W()Uw%3K1pSCNr@P8iJyiJL%)7eV?Ml!4tjDzO`uhL(d5NaaX?0U#=}${yj1`Oh z3&i%WobwowaBg@;s5-l*h!1uZRaA|f!UF{s{xew}i-qPW`FMe?j$*OE5!-zI_r%Qa z`15oBNNG&^|6;zu>5e1r_xHa$e*AU6pJL|!#Q!H7s@JhPW3d|>EMKo2pP~s(oYI zK~g2Yr3qDqi%Rp$)HxZKeUjQaLVRUqd1VYM3zZl5P|(x3 zJX{$n7D<$JKt=)_> z&gdE(fCXDey2-T?+vJ(SNVj4-2h$D5d}TGb=Ca~n-0o%jh^s%@ekY6@~1KJ`m(FFV<=a}o&z6Q zT3(|qA7KwU$mXbLoht71lC>+x$f}rAu!Y*WrgK-h+c||jNu8Vx*5iGRKO=oM2`7iD zUFkD73yKHco$JV_^LX*kWbse2_$S=i%32jG{(M0XYl5ORYQj&pRh1V___z})@$>&4 zUhyV(!*z(pELS^7+MXEblf*+s9u&a}ch_ibU;={mm&m#7O@e!rmI zDZeICXr6qN^?bK(29BZm>BW!3xCe1mx!L;Z@sf#w^MqQC?aeH&|G2Inp;?b(WDn%= zO=WRmb@4E@bf+Jcm6WNG(Ut$tZy)YuzkE`n=307fEB6G9im5qeQkq`U3eUo4D&&iSj*H2p;I=N`U_Z|~mHG<}~Zt~)e2KW7`# zZ4}paeEl7mNo$GJh&l+0ZHz&k(tCC~%MpmT_bKI(Gac4wQXi*G@$vy3S!32Ig%wrq z1P;)=#;jAO@kGiMp$T7D6|Qmd@%B6AB)4`=ub1i|jlWL&_#Zr<7u5P|%sNn2U{PU> zU4JKa3bLgmYVss^O)9~Iw7kZw%$liXRXpkoc2|`izy8YSRH0}gLG`Mee9A3P4An-e zY9sPgAy2{Oj8vw4qE25U`W=`l*&zFE;vBV7EB&O*?lRcb)THF`T3(}+S99OfiGP-m zbKUC5E|8N7?lnqzp*qiinrfg4w+;vEv@{C)b{FLr1{U#}@QRilNbrYgd5u|?e;hq7 zf6Tc2iMbb84{&t6c!*Emk{>cUSGv{s{#41Ep>9_l-(L#LXB5tKEfU)43^m&NjT$}C z%EF^keE%9aP|>o$%vx^{WsUGt$aW+V{T?S{;!!Y zvrQfM!lGK)G|w(p>u9!J6R+WoO=vnk?Rh;$$ADEO=c>yeZ1fq)N7eCTRf;|F6vNdiL)5Gw1*61@m9< z{za-l!$-7FtN;x!kVCxiC;f(w6VfTt?8BCrZO}(vwXd&t^rNT z#~PXEgyQ8HoWv;FPSfm-`1!Gxu?rMG|H|X@9qxCITj(@3X2rjd7Qg-#YD_>KtK4n0 zyhip%rKUkkfr~V+v6=SigMIq@*r!`rjmtLi<(Uv?vv}SrM_e@fHvat6IKLO4wM2aS zK|3XN5#PRQsE@cPp4&_HoyKO`uYXr|U7OfX%5&riK^cyOl2?dAA&*XIF-C@_q21Ffk5S=IT z-CQbIgM-FjH-AoRS`Vr0&n!sK>ge(7)KA}p^&@Q0X_`nEuZwk+-Y~4uUt=@NFOGNi zYSKQ$I|5P%X0#j*6l#c`@5#S|zSEeMAig~oc?9)MnisQpBa9~y`sH=F$(q;Lyz=#; z|MhBGKHC?FA>O{x8ERlSXUSL|FCpL8D&4QtzIri!B(=XNfox;Ke4Yw8MBi!5a-yjU_;u{dA*=-U zwdPXECV=tnpEmqy3%H zbpGnJB~>(AtDjpq^gLecs?pUx$S7bSUW5#hz>>C_w!8gA$~fpw^v)rs_+L&8&YhzBZkIql@iyRMFht zTa_w){_T}Xs=CxX{`ncNZCn09zRjoBi%xmtuYQ&Jr`V22;NPlvhlbbjL~G}(74bQ% z`}py}lCesDz82Hiy!z=)|Lom#e2f1!ipWaudsvu-J+%gjVZIL5*i8NCAAOqcf5_f} z<+^l3iwt3zV8--;VzYpunty|XS_gyEgQo_2iPpt+P|Y%W^>#*T>->*K8eR4Kv(rpk%?ayQ}vGCxk_KXkS>%+psIn`s~NPtbm>_P-zN z$OjQ@s`5H|)0JNQe6ggwFmj&D=UJc6)7U)y`%=HXo7zvce`;6dnZavbqj`acPw+;> zYua+ilyQ0`Sa@i=p6o^4{*$B`*V^O9ADy3W-G(t!r?0V@2wpeAKN=NJulTWPTyape~Ht#y~X~P_xVYD|EegQIXNWXsTaE~Vp=9+bn4m3-;5_| zeKj_*UtF3^j1=F#>>PFbieIl->p9CzDf&WVR?oNyH)vjC^V(-W_RstJ^iNm&^p@3b zDKdB{pVlcXt8iQOL}AteT0f2Zv7b+xO{{8Jr|I`2wSCE38TeP}zx~Ag#m7D_U-NVA zBYlio8u>RouiY#=D*OG?Jd@b(P@RuP&;Fu42sq|$a&?dn(U_IT`dNSBOfrR<*Vw%F z*nhPbZGw-^JjhXbMhC3*X6a~GJT=3ndKMHw-T(6I6x9E$%s@` zscxrkt0H_;&>k?`>HIZj)wpDmGz&}pCf_I3@>VAMZ6cTIlr%Q4`~$@Lx~YHH^P0x#nzCIz}|0(RRqo3G+X{w*ho2GC{hEM0fLdCj%G&Zw- zc)pQf|5^Gu(GrzH%DIvanx}xp3ZHWZppI@g3`6!flIf?_{;sa3K;@$;!=Pj<| z{nsc_U*fb;v8uCQ!#;g_@cb>o{!n^lcygHU6JwoDu6#Y)eev@vOUCNEzU5cN7p2cg zHps-MFZCG1CqcuN(g4=pxW;C+k3RDGXw&CIl!dQXlapfsowH+t`1z9~qbzWou279x zymch2Ulm_`V3n^(aB4L;Y0#iaI<6be(>EHMRlc4*@z3&g1o1?VBT-@*URFLSNZR>2 zeT~gZzfVtbKBQ^?u5vvvv_8nTf2Axu*Ig$uB>*X}v$nwvO|6GW7Fb-g!mlYO?seLS|A`NW@(6`wKVh~|tYB@x@o0}H8RR`yJH1>n)D%J;HnmQ^%O2Z2J9jTCEt;y1zy9L0 z^!h}g_;8i@w21x`m;Q4=MvG%p&s=eoVSK|vu&z(k+{SfAzo@w$Jh6Y&+y^cJJMh4C zHP|1_8xS?egE6oUTredKLV@3 z!_VeVm4kKQTJRHa8)$-Gfgff^&2}6J+B_s`_5fStAP;s2tH7n;5-@cr^5C)HHn1N2 z3JecJ{#f({dw_d}BM;s(0(r3aNaVryz_noH9OS|4&P5)4JQw+nhH={{(AVD~)A50-)Zz+1r%3?wnIKiEDW`+=*$Ixue>_5;UWfIN8gg~)>k zj7J_k%dVHkOzMN*Ms*KAP?qVf;@QHrO5LKC+?q!JlJm% z^5EBC9XO{Dd9cG|$gOIr88u70844hmi*d zRUr?SRU^;-!XLo?VA~qx!Fn(Pz8=AT;2&$F<~nfj^r*QVoC)p)?*ZF0bZ-WGgJWk# z&3y3SS;&KTflI;gwaA0l%t0Rfd@l0ff!86=M$4!3kq6&gfIK+kdgQ_7i;xE=EJhw| z1h<3jmmm+0`91PH@C__Q9^4D&gVSz79&EoHdGLVSkOx1#9eME37082A??j#r#iF~B z2UG4t9&7|7V7L2`2lw!2ZP$SVA4DGfa4quSvd54=#W1Eng*=%1H1gmlU<5quPsoGk zJ%>DagXNo$2akLSc`&ygdGOTD$b(OV+rd9>K^{EkW#lvX zTMn-v4`ze;VA)@h2j{u8W;g{ zzC|9KxEFcwb#Ob_>O17Yyzh}e9e?v9^5Ezw^5CkUkOv=rR|9r!4?9o!D?1&!30*}gmJg1x~dftZ;Oz5+(T zSqC5w&Q3!fyyZaT!TZ6lzylA8neBQQ#+PkkW)JYwgJb3xu;`GOSp`mL7c-ZD#-TBD zEjaqHn7IvX0KWo9v`3x|+TVdaz{5Kr4}JhvfyU9ugV%v;!H&lw4<6GId2m<|`Cf+c z4%h>nb0YHKk6;yOc0wL}Ap?0Z+y!~?CGab7H`s>f{!CbKCX)&`LyaHSV zUfMNgt^pgstzeh#F>?=?1Gec+`{aAgUBP)^F4zc`gYWV^=SASMGh*gya8&P@xdr?Y zYyhYAiJ57AC|}>0nE|%$7c)nI?fb{fVsIfi2fSxM%v=RtJTPW%0&hAqX6^#hGGnH} z8RM(KAh>K$%*+Ds0}H^8XT{7};A`LtaO~iixdCiFEN1QmSBylyANDv0dGMTE?}l1UL)aH5z&FKVy*x=jS00ZUJLp>-?D6p+Da}1N(!=jEkA$!P6(i%sTM+OJnA8 zun)Li@DpR^4$uVmfq$BW`~bt~QHVS^Y%=m-8CVDI0+)l|7au*9Hy!!XZgI$**pJ^EX1p9-Nmmv?1y#;wN1}+D0zZH3K=yK%2e}VhJ&u>G1 z5cPfs^57_NJU9!i1K$UigFmf69xUP8y*t2`_aF}jHz0qOVO$9I2d@IhgKs~FJXrA} z^599EkOwb(33>1ta36TW7Ua)n{`6Pm!S#Pb9vrwGd2r*K$b;Q?AP)`)cYq&*`@oTJ zA)jR!r~NBtW`a+>P5T311!sYScE!w@eOH}ZpNmmeb!mVSaf_$D|B{1TiEzSw{~cn{yD+z3ATCGud4 zuaF2^|FZ~vIa5=aOd>=G&(EB^&!4JVKu;54J!4Wa! z!843Tb0zpLxDmXrMWeY3Y|nQ!jiJjBj99a50z*egl?+XCB{Z zE&>lcvC&*3uv4SC6?`=50DE79JUAKba6a;2 zf3V%P$bvx)XVD{9VX{--C5vkGqivXM*d&KkptYcqgNvj-Urr!^BzPV9Q+XSBK#5L!D}8v9!z}_`EiU>Pc@pE;E?r=W&v3GOrtpq zZ2xSdxdQC^0)7J=4(C0mdOXTFR)xE5Rq?f^G}d9NT3R)WTO=0ji*O#LhJ;B(+4u=iHv z!OgEC57xbgJQ&-CJlOZ|$X~>I5e$MagIVBia1uE4b>zYH?Z|@*{)s%e3fu+$1vD%WN_&&G` zjDf~P_9xy$9_$5XfgkKb9vuDw^5A4}C3q#c5nK-L0-pelN$3v-!B4>~u;quygC~Hq z!GYjPungP?&H;CUcY{VD^RC^EW;(bQ%mM!j7K0yybHElKG46pK!A)R4a5p#>Y&jV} z38sT9z#Q;tuo!$DoCAIit^(6OMjku~+zp-uwk$G?i@_Hy9>T~450bd{=Vm<_>gKfV= z9_#`ZgE`ey!312!GD9|!`*>zkOyx9SAlE5P2lt2 zArI~ZTTWp-1k=I8zDFKB9V`Y%fOEhZ;41KDa1(gse~gG z;N3Cg!ABdB2VVgEPX9jzAN6aGvp#IUh7y{A8{Mt5be5w}3NJ ze=-}uf|fs-X_s*iW`H+?Bfy8jQt*TWkO$LSArI!KArFSZ2Jm_?Z7Tj4%m6onBfz)8 zQt*3lK6vPX$b((ME#Ppl0h|t|m1AEp16%`+0MBcUJoq&@A8d0F^5DbZ7H|vL0DcIj zRbU1%16f;`v@ECma}`QR*Y zHF#<}@MQuv}z|Ho&S6D|H|Xo%O;-Dw%xF{9nLxApc!eijkAwA^VBoaI}vIK zS%xU%#9-8-=8`~%?CaFeKwy7KjfsSd6$R3&E^Z> zpNBsZw9B7*XbTZRlzA56Pp3ysT#3VTDY)fufs>nZ9pTo&<-rZ)+D^AtW-INs9lip- zo0~6l_`UE~z%v9m@+p&Sz1yeI2jTtdtvCE3r})=fK71?q-Q;8Im-?P8x6nTV|JTm` z{8IQEyZD!H9sC3Ee&yQ^|1`W``S!xc;Qh+iK9x1{ss82b4L=0luU+QDzXtEuE+g<4 zp61^!m%?v?_iLBy;FoswZ`O`E{;6tADoqo6iegnLpf7}4S4t}Xe{zhBBo$%sW zt6x3G;GcjuJ@P-=@*NLgPQ)|W%^v;*o6m$FFd%Av?%^A3z5xD!fl>27Pd&G^>v(h`;@2 z!T%e+(o_C2r~L5!hepjkv{T1Ep9Dk(qJM6HZ+Wi2pV|pO6aIdWe)rh=#o$eNKR?y+ zK<3WpMa`Wa{ob_o%Y?7zS^ce^^l!G)FMuC3I%;-x`hi z)c%D}*Z}_s{Cao(TnpRmfIDYw)NJF?y_K!|KKQ!hUoB~w|9JQ-;WONPk;B))&xJq5%@;cSa`=Vt3q7`*XWMQ){P!0`&1XIQ zQ#QW?{-%rk`_4Z2l#8S0-|+!X+uC}7&_LL)!$J7QN&aJ8fB5?gsT+@dOKkg&hyQwV z)a>ZtkFxnX_|J=?<}MHaw#_exe>fC1zi`_pt8edxxXulb-a~+UYNce;NKu5C3nQUk|^$Dr#Qov0t5Sza8-3O^cd;@bGup z{66@)nyC4WC;iXu^gA3(|A|D+4?Xhl+VcJ351vl@_V6d!{CM~yW-y2G@Z)U04t{hU z=T|)Zw>G~VzVqc#^KOstyWRGE>*4ReDr!bO`hRchzXSg2*-^8nn=hBNwEsT%o8kZL zslPwj_1B>-`_|V+%~2lxM%w!Ihrg6_Fb{b8&nmnBjEDc^`l$K1JO7l2ZT;%tXD*DI z?=uf`+Q-h8B7$fi%i#wuiJEVCvH(HocDRi z(|_)_`_Fp#c6a*EJ$As~4&T>Pe?9H`+Xuh)uBiF0C;zwX@^xUfUwOCx+^RqP+wgvC zl=1Knt)%~X(!a}2zYf0JJyEmX!#{8H%i*`f`?cTo@E^hZ^}ikPpWhobPw=$wW9;_5 z4}Q<8s5!!8-yybrI~>X$$ZCK4^@l(80spznc=);pIalM!{}4O>I{5N6j9;GeKjoAk zzSYA~^Bxbs!sge*&xik;r+#0t>vsoyzel6yB_4jf&F_PctcCaJH{RB-!(r?vJsvgJ zc=G?FoqvD$K~J!T_N0HAo&I?EMeCA`|L{xUXL!XuL0V(Y&T{tNhvJo@L``gb@S|NFH6_|qT$dw9RSpYiaK^{hQS>Cd*) zuY-S!b9E~``rT^lw;X=lGt6H+@{4Tw_3-u2M$JhceuB;KfIr~5sL5j*$G@grWT(Fm zJ_p{<|8!tu@KyL(9{)JQ_K*GHyS%{M)06)bcK+kxpMNoGp6t$FEp27})xp=p-|3NG zZp$x+AM%p_{A)dY-+Ja3?(}Ck?PCZ0Sa`Y3Y41HwbIK3D8va;M{zuyRcQ}IQIGj_g zai^bBY0LMA{~o@No3C>69}j=fmZ+KQNq@MVejR+jm!swokA2Uw?YkWQ;#Z^QpWNxE zth4i95C1LvIFJ7tWBaci@EL!PnoRSY`XA=#w-5es`0;K&MQy%{{@3A1_Q2o^Jp6@r z{{7*vgHLz!6P@(O!!LyQ+q(_yb+q5IG zeMFu1=bvZZ;@rABeXfP>`EZZG6?w{jshxHN{_B55&5u3&`!>H6{%OwY{^(9SvnH@y=zFZamT+Vb1sKYN$CwMYJ2TYfM6i|_gG>$N|cXD_>==4em)=h*3scMN8{ zA2o0Dq`%ZoKOg=pct779f$#Ala|^e;a!t~HOW}vXFZ1;6C3fFl2mkEHjQwu;lvnKh zx5Hn?8RX;LymI|A{k`y|@YlKdlxuAH_Q!BG3x1iW{7anj!{>Y&HGlHR|6t4K!*}{O zZQtYDI@`W20^f}@(6@Tzoqdy~@b|(0(ZkzDq%eLMU$@BxqhhOPfz_$gnp z_Vui-{%o(U+8@il&-YRDVNd?6?fiSg-wJ<$hgZ&tre);AzY6b}yAUqgZWY`%xUfgo zc_y|5{)GP|-t!UiYvC`0_ZyeC!9NFojwk)0w(eiSe**uZhkw`R#d|0Z?u(k&d-%CF zFWygS|3lQw_V|rV+b@atR{C)MoNOGweSs8L<%jP9-^Eim>2}>Lfgccynlw>IKHrgF z3*QTVhMP}u=8fCn7r?*o;djaeMBRJ^e@qvRto)xmvd);>75;ao z|C~4%zO@na)vX-95Z-UhSpC;#8sy03wM2EKIy-M7H4PVvvX0p5i7%R3Ec z@m*>xVcCV96&6 ze>EsXW`~%0lw1GQ!<_QNpK)x=_ZdVM{3`e?gP%1|xnd_`^MWP;Ko!sGaZ+cZ!)`d&<7YE_)3AcPCSJgP*1Q zSf#tjzvGFV35Ng3&8O^=5d@zJKkyWPz5w2Yf5s#4JfE2be_Ln&wz~rUEBHkHLgc>z z{$t)rImw;=rH=kP;Xj4<>l-onEZ$v7l)k9Dj-6Oz@h(fEcGVy5RJd|?-YHXT+l+^w z4i7lC)%#;Xg6bgZ;BR9e-nM<}($-3Q)jn1b=kwk%a~e_XI4NoqQe-iJ=_@!R}e=Pjvo_R*CJ`Vw-2BOQ``rNFY6R~Hh((^&ep4qLWa+;Xet4e0{xSGl z;QjRP$l~q>_)Jg!{p|cR;U63qGyl`Vo&PuXSY7~s^@TCt=c}{e+l-I-?m?`8Uk1OJ z z&98ueeqqdfx}{tH$L)302KezyV&*j-{z{wQDe~uCvU#a)`KxXJ5`zye_xBGSF?<-_ z&p%|sPl3PNW53&N`xU@{3}58o)c_SFY!-YM-dX#=&5Lt7J7f~F4Xh;2C&XFkse^fT z9c+Xjdq?6iT;#C}ek6RA$3|17jYPJho?3Rn&+sms-#j24ei8g6Az-!RlnHh@bKtka z`>lD3;WsDY=fJ-J@7E7j!M_DhQJnmX92;$de;?j&O}QJs$4dWtY^^i!N? z$?5Q`;Rkx^ppRV#Iq)ys6Em;y$WOQJQw)FHy)pB3Pdk6rZs&90|F+71oL>bWywAUU zo8ZIne(iKO{1o`@9{v7i>(}x$#_9WG=DQw0@s{l;(&68Fh<8Ih z{%||}V)!56{pMkF;Kw}dU%plFcf?2*6DmQRPj1%9BLua&gyKRNJg;YYgplpI^W82&BzEKmMt+WF6c|6y&+_Z-+N z_&XkpnKM0oIAZ&`P4Ii4bKrNvKj^WaGf!CsU-IX~^OPVmo8WiD|J~icMqHbkx=D61 zSw{`Tx#{_sd6uV*53t*K8k2}e>(SAjM(SsFAI*Tj>gAYuyGO@mvW|rgBjA64|JqXr zdt@C55#eXciBr$J!4)FF_M@eCJ6QyO_*Q>Ex(2>2yq}-j3V#W_Uq9IcKTf3Y(b0M4 z)22K9@YTfQmC(N{{O{o(^62l(2Xo<%f6c$0l*9iKez8a1na?hQZ~r&{cCrS3H@sgv z*$Q8|Epa;$`tO0?1MjDQn;znsiT`}IEBqe#MDy7o>F2_~_j=5{z}THDchZkMzG#{4ieRAW_fTi8Glve)EaF@H654<`eCEv2KI+^Y^{s?|}EKqkQ-W z;Qi_-0{;bk*i!~)4}2;7p!faz@jCcE@HHMAR@ye)4nLoF`k(fs|F~TTd*M&`$iE-7 zKZA8G{4$Tc>-;mwzkV8l@BN9tpI8e29K4^OSOQL>giZ;9){-9QkzUi-rFMx0D>4yQkAMSu3b|CME9_lJ%YP6jwV344F@TVNp zXrAwxQ{;Nq1s(hFp3U)%zUzWa_;cX>=KTfmL*f1U(Jc6h@P7Sh1$+g(Uq9La|2Vu~ zKiUc3;so9er@VGOyZ67~e?b03x35d>XwREE@__P3n!l!7qgO z%fDlP_OIZlc+#J2r=JObVrPH80KOESY@GJz+TWQ4e-*snIJ5%(I`~viAOFd&-wp7W zb!jx`xzkU%+SYF;{5JRukB>RY9w%b(JG$Z{JU$?5^Bo6JzkKWC8c+IH+UaM)AJfyn ze;2^_hWG2=v)~8A`}OY?@Ylin&1E*guZ8dI@v)t3AG;I&>AsETzdZW?)7C!*KdoP* z?|iT$jw=Sg%p>oNgPHKt`uocl2zhwFac~y=yaA2=`@isq3~clr2RFdag8#~+pKJdY zKKIPVgyWzXV`A`UXZrIU&*WM2phn;Q;Y|3o@P21}3g9=K#Wy-U`Z?!NX2CB#yU}cW z5D~0#IQ2k#|9%DhHRm+?zJId;ejof@p7d|C>vt#o^mF~&XAFJ`ykGn3n92LE@P6y| zO!&8Q{rzVF{5JUQ9{rqsms#+0&g0u8p8k2O-Tzj=8|OEgZ+ZIvc6*H90RJbxm9xl` zzO!$!6aM7$%!S^D&S78aw@T__gqJ zJbmmcyN~6-x3BZ}v&Hb;;Qi(SbKo=K{pJCy;ID@Fv+pMOS@3@CV>kTQ@DH`1fOa3h z-zooKzDGXZJ%QJ z$KhY`@Gsf?9QcCY`S+hy@YCV_<|&)t?}qoAr|gFR%T@mM+j1!5M-o0A{>dbK4*c3A zd@=k(N%%SN_b1_3!QYdF-vqw`-miV^-cSB5hb7NH9bV+`*MD;051#FB-(vW*B>Wus z)Fk{W_@9!MAO43V{BHQYN%)p*rhb`(Plx|Eyq|q@;FINF44*9jIs3_fmCj#&Z^!oE z583{C6a3|T6VcCq?}q=&HU9O}as=%^37-!CWD-6Her*!I82+Io{2ciElkltH?@7XM zf?tt@-wl6j622vla|37-K!CJ8?R{@f&dDf}>aKO4{APyVa-lmC|e0fVty~I!!FB%f^Wo1=!mozUO~P-1AD)D7 zfFBI+XTP-blINcRFY=$_nIliOeeekQ-_Q47qm;s*z932Y;WOa<);O!-Uq~Xq1^&4t zd;|RYBz)Q^_NtTc8SsxJ;YYwf0PkntQut)~&)4~1<+1POwtZK_AGXkc4zdOQckq7g zvjKi)68W^zy!(+vJ_CLi{1Kk^-Og^`BjEpegMa>|@Y|B`^Wp!RgkKF`pM>86|9ld@ z0e%B~cX$7Hts&CJB+owsUgST=EuV6g?LS7qpL%1X>E~Zc;SX5CcRM}u@7eP6;a`Ej z#lzoZ^Q+;rZfZ1Jr4YgPFDdpKd<%T%-}4=CPy1S0&-M!cR@Y?}0B#!neui{j?-}SNMV?d@lTj@P0m~96nk8i}sWM8lC?=?lzv{ zd=q0U{5#~o%ERAn`;a{%{k#0@ugy5h2k)1DSNM05$mhcENWz!H|04;%2>vyAzd67f z_|m)m_1g;nDZHN#-2*>yWs>q=z#0MGxBT#LCy~#EeR-M0}{rAA%l0?5Y z7js4!-p~GB;SX5lZ~t8QbMN!Fe>wcHB>W=y>?Hge_{=2yR``BN_&xBw;Qj31W!k1Z9B(W9(AEC@9{BYS zH2QuMu+1fW8~&k2^WhdKV6QP&JM%yIEo<;^9^U!xNG^P*NBrxj9R4`?I#2m(?D8#w zuZ3^p=7&pKwy!nth43t89Q{(xwdJ?M-vpoK=C4rlLBjUHKL#I|OaZL&wKTYM;@dQ$4R7*0$Y^DI?o?}eX9wv7xc=?;n+1Ne zz;71#%>ut!;5Q5WW`W-<@S6pGv%qf__}{X?#=}(3RT^inZYcv+KA>=;MoYgB9#!EV zY22gH;`i$NpEMr4R>c=fN%5PYFRXicmWOcbzABEtLf^}^qDVk2sQ4YMz*s#+rGK=( zpH!h%S;y-8G>yk-EY*C+IR3kz)Uj^)Tp!L_tF0I7Lw*{JEb&~FUuy-%V_Lt1^nC}7 zM`*M_i}7GXr7RxriC>kzXs>RKRvM4e*q}4A!qW~^gjKFxI)5E3|E89R!C$)x>m+2< zpY;8DEoka{EpB|J?=RE*5BlD=yH4zX8n)>AiqE&NZU@6OUZAl=<8+PlG~S}|evMCQ ztk?L4#*a0AuknCGRnA9hJVj$)jl(ovps_^bbdB>g-lFk-jZbN;*BD>E`1`bJR*Up9 zRy(jlJ6a9P3fK=?@)iysGGt&dWB90XU4lJ(bnDrz$A07m&3hbtld);UR3pXMlrTKi zXh;~|(y;oUJGE4Lqt)-c;RhI2|MZ5pGOTv&4No(y_U{cp(6IWIH@vlB^*?Voyz)rAvgx**Sz8FjMe{26n>~N;+sU_hZ&YX^rmyTVfjIC zczdHNL4Qj%jxZAS!&Ku)V@J4YyG%9kUfR!j)5kj|Xve9>(Z=kA;g;8SQW5^DrSY}t zgs3ageiMZsPI!tKE%f+i$qD}~!h6JpXXtQiJhbBTY7MRYXk&}k)AE}fw1KC$Pyh4Ycc%%w=P>0{5!~d$o@6zF`b$FQ`(BwB5;r>B* zqVhHn-oiM<*fmYHXRBOk_zMwkRC~iSba*f>e1s0qiVH8*;gjOR=j-sM^LBbbLMDsKc%DeoQ)v?DIY0(hfCVJBagI zB0Lxu-dl%f#f9hT@JTw{^7B@C!^bPdswcyqPDcbmGhVkPf%XYqi@K zb$EQctqiJk;@j;}C#vxHcDqW4$G6+1omBkzc6-)ID%@(f)!0YWqxiO@_*wegt;4TN zSK;y*BA1(Vc*m1fxO_*A%dI;6jxH*^NP+PV;fd<^Yr;i6&aPGUYt>6TZbkUU>E3$p zuETf5g^$wV{LyEbqws6uSE<8$=y1#aH|X#&ap8~X@T$1**L3)jxbQD@_}aMewhR+O z|7~&M-E_E7haf+zyrXn@4;^mlS*gRv#D(9W!)NPotGtis@Qrcdujz0@er${H{L9?= zUW*RT(&1J*ZCL=wdePyQp51i#Mjbvzr!z{22QT-Qt5SzgiVMF%hp&tae?*7x(&1M6 zuj%lt|A(^k0F$h!`hKg3D2j`(A}GS3AczW`gCb7n?&%z7x>b5Q=bT4{HKQOdBFO-& z0-`9chzN+fhyhV_6+}c{T?4NefE5)nf9Kx%*X(vN-Pdm(9%lA;PoFw<>eQ)l@2!3# z)6eaE7<@bU66`sQ77&vCJHff0Q@~a77oezA%iMnOW$;b^gRg^g{pW#i2j})&3%*Ox z^F8oA0)H4>_2$`n(XBMu{$+um0=`|~LGWGRoA!h65%_uF`viV1__i~dJzUTCz|{hO z7(6WS!)Spf`M)FZQ^0o!JP5u^;4Sby0zVIYpTMsL-*%Q@Ke$@p4}*sVei*GoW%~tw z3iwX&lX(1q?*+dayv60WGkbWyJCDnQ^Zv!hqaVMLDGHc9_`y%6?hWrKKYd0}42EQ5n`{0<)svZD; zSdi!IrVS}U(GQ=0KI3p!uaj?M9L>Br&ph$FjPrIs74omTlW}xol>_|H`xrl-{Zd82 zPX*`sFaz!ee-Y#>;05p_z`NiZ;KzZ#1N=ka+&`ZJf6h;tAYTvidGgi&%Q(VU^#jPC z^s8CReyNV7b+XjozxEgd@^|ylpF!}SJe36$(uC>^@PnVm_+U_(PP-j+`xb75rj&+=lbuO zqo3gSG>M2q7EzZW$<;_$;)k5w=VZ*;Jn;#qFyhP zzs!99y7*D>yTN&W=6U$dZKhw$!_UACpM9{0&wu_7&hz<2^l{0bS9F;ko+tVI$|k=I zv)>**4|xduLg?q~^tbkz9-cq>JoA%YY0c^-~}j|I;C|9)^2^l<-w8=U)}`|Xe5qTkkUVdZkabx`gEd?)l@MqJ82 z>*pi?u#c&}0N({G=I{)0IiFm_;_z(fc@y|v@Ezcnf^WZgR{jR!l0BcigV{fZJa3n!?`&PK z_kq*L<)6LKvvDVrKNtL98lWZp2Y-)o-mkZTFN1p_ZvlTd_%i#L$`1bR?=!&HcNOq+ z_AriZAk_}=gMP&LAD~Ct7nbbVcMk*dS?yFd&#OPZkMV0@56`Rb{3+vNUOn}G#>KpP z3%Hn9KmETpE1t!>H}n_6wiAvVDY)cE~^fD0pq*Dx53U6z*QH{#dUeLwhi@FmE9j=1E{GvVMS`2FC&2j_W%$JN0Pu?mU(WeCp8 z{X6u$7yO0r1J9pVKgeA?qoUK}3{;Cy^M zmKs8`^Ml~L-US6nS2NQGVl&KZ{IJ2?-2O? z;JXC=5AZz#f5FkrPSs9UF1N=5z7EdI<>S;_scb+N z{qF_m{^#+3Ew~u}FFKg%;dZW({Ze}wz~!^Z$){(6U;T6@|3&zj$6H1|u=i~A9)s&7yHr4hcV9k5uXRX;Mt7xIOp+s z&Py2Q@yW;EyTSSRdnNqvi2Mb{bbA|q_#F89DNGOCp}L>=R&o9)aVgH1KF;EY`+plP zz~ymP;HQAE3takcPRVaya32-s4ZUz_Q z>Sb=Ghu7!C#ctU1S@0!rF8>4KTlwubvOL`vyMp1;M@;)fiL5^K{NdGD3=H4{y&`lc#>-Wb0&vC zUM+LFKLz<^@DTW3L4ON;3C}00A^%?Rb#QLar@{Gijr33X=R5R=WX}Z8y>NSeCv%D` z=%IhgKTki7$!{0rPm(!3w*YzGj&AU!PtC?*349rx$KgA`*99)!&z9PKJ2=;W8~83k z{+Gn1Jn+#V>vz1rt6t3bE#N#qycGN{a6XR;gD;0>^|ZiWCvZN#jKIY_@BlckFCPcD zMVWpR{LI&DZg5^-p07UkdL}RCtNX#leD#bsFnOM@c%E;A^E}VTubc8ro{vX-zEmhM zE{@FJ|pt zXfXab`1Rnb>8w0oH(dtK{mj=*_kwf3@paR28%)pglUM;?gZ}Ir8RzlF*K4=Fm2tja z`yJ#1Z)03O)0$Kr_7Z0QDeqwX7T9kjF0E6xU&_XjFzaTjq|BeI+JSz`<82K2Js)B6 zoPQAVy9D_!L4Kbg|1tUl>w>(S_*UcE>t%VG59~Tf*39*6 zfG>kzjdCvm-zUg_k?Z*!(_@DGJ>cu$T+d&?w_V5NdAZL$f#siN@Co$jz;}c5JRAkz zC+O*dFMXcr??BHE@SWgG;GgCC!MQ!(1K%s~hrm_WGyPo8GhfQ=UlRDs!I#0gJuBe5 zz` zoR^yg-wnPU^4qzdFEahS9WMvp4$kfQ3b^VWaRxgQ<_-viFu<%pA+ z{rd!N1Yi0JEB9>pEeXCG+zdVfSABI>{vz<5;M|@Y!1sW2d+yXFGTjd^r5d;`}5z6AaEfbRsafIkMl?KY-| z*Y}85F#DImcS7C>z8BmBp5*dhpY_iOd>woT2hPjA0etBjOb;*j9`N1ZyxhmQ z{5NOIJ>r$HAN&$-Klom7?uR7!`nQ-KErL*{}LO<0r{iRVpL+GWZgB5_}ze0zLxYEAWfB{2r!%JLGTR z^5DGOd%%}|Fl*0aTn~5$dXCUA`}Ydm2p;|+)3fP+@a^DS&j?&~7nA4px`@kzb31PU z-!1TaxSqS2o+a4%82A!6*MEeT*}qeeH-hg5=jA5BRX>{5KLTF@=lU<=`UUwLxPEYQ zul#cl*MASwzYhKw_&Rt6{0N<7|52)KpE_73+rVEAz9jHvaJ9e_;L8H9frkZt7IA5P zefNjidW5g9`TF+}@HXuEGX0k1&-e(F=kf4O@Dcbmkbe~XxXYOQC&7=Vb(N&&4)FJa z^XHmYFK6=m!1+4Vy_4}zf%A3ZAHn%Lkv}i=zK=5b<01bM)c5n?yuN(B{5^1PKVL6T zKgRU%^)lagJ?!I*^K~oV2RQBu#>IVrHEUxGc`!QTiwe*yjvaBk;+z>m0+>ED3- z@p|UZ9pGHv4t@>z&5*Bw7q4P^#^5`^?+3pC>)-3a+tS7b{d^Acd>`YAYZ&M2YkoiF zXW*3f<)5FBnAGmi{4|pUuiEC2B4$36^Bj{(R(Z-@Tt1pOZezZ3ji$bS($^|@I)?*Six z?}GfZjI6%LUpFiN3h>tooIlT&20spZe2{-5I1Oj=PYwK2f}VGSe?#C`gYOmix4{1a zej)Tf1inKG4Ep&Y_zS3mNbR1xp7A@t`M%97zQ8z-E52`20{23m@7sJ9d>QfZTIhe| zhFQOz0sf3H&hj^dpCEAQyB#Gvmj(VM@Yf0aKJbyi{|0`(z>hRD{htQs?P3D|sUV*O ze@x&*@WXFp_2TjNKJbr#^L}wH_>BU;6Z|fL{{j4AfgfsN_CNimS%01ke!RfF;I9>U z1w0MT{qQdEcMI}YfnOo;Z-9SQ;17V`2fhn&awr9u)V`10GHd_K!JqNvS?&WrLEv@p zWr3dq{yKqQ13nV?cfijV`0v3#E$~BM&Fue{z+VpjOK|Q_ANb*4nXOkH{1pN}2iy(* z82a5V@Yj5GR?i;rHG%H~?+bj}Y0Uof1g-}Eq`<@AUl(`>{O1DS0sePz?uT9AFWW8H z5AG27KJbjdx4nkhzaeln_=N%wgYOb}2mCt%-vR!A0^bGxq+131!H)*#_1y>V6Xds@ z&g?IN{}b)22mj62X7$IxpYhFEJ^(*K;2!{A7WnnxuM_y);3IHu|DVCH6y%@#T4w)l zaQS;5s17FZU)(-x=PLLO-=5`T@E-{LV(>o*{EOgEyJJ?*kHKFo@V|n;2AtRH`OD1y zh9GYOe;2s?eG62tGr-^f-Pv-_1h?Ed%P#}3?wRFZ0e`!|e+GWJ!2b{8iv@SFxWf!M_6jPw+dzZ~h_E^K0-wfnRnP;}?M+X=8Tk z?`B;7egeAo8t_-&!#ICmrUbrvFXJzSo_B)x1b!9xdjf8e))^ZGu^&g^vm zwT@OKOR0q`pX{!|aM^Q!_s0sKC2-i}W2r~hu&o+9}1 z;MbyE&H?|SAb$<`LjwN}_#wZa)&G0&mk9h&S`bO|*wY36a`2SEec%IdUava%#|8Ow zz`r8!Yrua3&hyVb;ITi<+W#1M3!JYLk6vN+S00{~KMnkC0)IXDWdeT__)P-;F!@Zw9p-5q|aEPvqtwvxYu>Zq6jSha_$>hbnsZrszc`5fu7ZE|`|RI-lsf|N#aV&xWS3PR z0^fEa8NtHt6{!_~ox;{Cx1IhFN|8{W1n_ z1wS5K^K}N^gmQJ@$Nz%ygHWym{3Qo7{l9{q9Qb>_!~~1%vg)nif4P_mejD;12fr)K z#?Sa4cHzkpW`8@%_*2*~)rsJ@)R^39XfGdl^efDeH$r~~{G3}Dcm%u& zzVBfMjNli8zX9*pP+E|Gz6}2R_cKFIfIYtif68xIx%7RC!VeSh&)m-BZ-o9U zz_qVr{7UvqbqDxOBL>ot|2_Dba6FI0L*vZOb*C}~$ z|C^Y@J`DRW0RO}bn1Wx!o-cscka!~S&wb!O`WMqPLAk#K|Fxch_kuq?!R){K_Y8!f zUjzP$e=&X={QL&+3;xdZe+Kf~!B77+livpY=Yd<@O#V1-Klp2oWPr>=p+ z4fsFyFknZ04^A@sf1YFd?}Pj+z`yk|rXR~NRT$j+T*hyL{2Acixr}i=`=#oFfA?CZ zpSSz@;CJFZ;`Y;+qTS%;z3ktwg8qHrqdOTGf&UBKvYUbPQSPy;tX?<1ft7nA{BHyQ z^c$Jr(O6)tfai8HUSuCrCBYwf9pfj1Z-C$DXMpF)^TA69%s%YA3jEQJGw@CD+rh7_ zFz|fnf1LB}jPv+Eni7#T{=R07*>g4G{B-bd;K9<LqPwbY0RO<-m|T#3Om#Q-*DMS?z%Hx)3jUSL8Q}ATV^geN zpS+UEeZo=x>2P$HKsAu;+a6mttVK5PCij{*;GUy?A`y z4SwN(DL^`=FA!sPo{fg8bp2HxAtz;)nHrw>Z;<~V*< z|8d}V+{y&kQSPbW51z#g=i5~&aPPO7Akv3w3|_pM=|MQE&IDijB-8VD_~FCgU%7%cpw;+B3;6F03 zdSMx+YJ*?6&J4k@s(LT@wOYo}Y^u+LKi*{g3+$KbKJY*Nmg)Z>_+#LQU&X+&sKzmG zVD|s7naT5hw+wz;l<^JZ2Osz+@ZRzD(BA+LVm!*TkEzZDKk_R~@Ezb6fM1J*_bc$5 zz?(R4>R9OiEx4AVOZxdB{CVgZ%>F6H**fG;1wU+qDaJ6PN`k-c^Gxs!C|CM!A}Ma~ z!1{|nx9~CWV~b4wIQZ>5;NAcONUy2~z@HEMX}FPp4$Cn8XWz#D{Ymf_f$w;T0p2f8 z2S0R!@yp@=47mCx#!rHt8u(v-#6SZ6IS2e&EKpjg?=J9vz)tSBd%!>Y6Q+m4M*jH~ z_!oAt`WoSfr>?VlxoG^6etvK;`+e#<L&2J zu4nul_Dgj?_&G;21-}OWC-|RtGX68@e_@{4{}jxJuLQS&|Lz^Ez&!l94*s<7GeJII zdNcUNzhNBBrur!OA3x6w`6cv!75shgXMnG#egppA-!b_=!v24OzX|dA3hH+e z7a{$_E~JH{-Z{rD}s;^FGF(0sZd>e>FBv+|Yj&`0JZY{;)&Yh1g?_&Qnx{vOop80dK&xVg^C<#G5<@aJKmy8-eSf}c$JTl%S^ zUS9-nM;JecsZ-qx{_r|$aQS-yN#u9nCt&@;^VOlW4w3Bt3?{tWm^#%9!B4!H75I9T zdn$2hekGpAZi9U7DNOJ)(9;C3-^TdQp2{w~AN(H}DEZnEL^V`@CNAyfis$4%jRq{Ky`KC$rpE+3 zj|9J^#|nHD<-QvH1z6v`7~D(zNn4z6pMm^)?qGr+;eG)Bp9$j_##HYFe;pilEaX29 z{<#O3{QrU90e&MUo>;D{{sjK_7?a2LrRqp(IH|tss~Pto51$79cQxbRV!u>pfPVrT z&o_f_fIs^TX2|-X?7~j)%T8yU$N!hW-*yS(??>GJ0Q@6gWuN~r^#2L`&aW`}A0i%} z(O~u$?_h!_K;8iUn!}hppC^>SZ~Y<@Y_rR%*g2A(L-%j+L(ln;zh^rGcsxsWE4cM# zjHe<0Ao#VYFa1;gIgHjJlApB=R^VSz?$O}C73NDW@a2E9it=%x27V|e2(N@6J_7y@ zOmIGdaz6#W?{Ox`=aY94Km5sbZqGRf%aY5Gf0Xm980YQvf|it(o~7DZW_b_Ir8=3o zr042SFnLr-$6S_1^;RpZH@YF8OmO7L;6meUAK@kl**Q+4^1qz76MNa6Pwz?}Z;Ye~k0@F+IyL?YUi* zYUzn)s?@{}m)@tw_grv24vCZf%O9Wh^GDw%>7gJOzQ3a=%RgIn(he3^NI$Ccq305z z9lr>^e(tQD_Yjxj;WI)!s3>1c_Uu9Ya61j)dxiK+aK2+!e;@o_!JZ4iw+sHfiR&59 z#>0=n*9WuwG4R{bkGP$$pb5NW|2CoAb>h$2qFn}%-`k(n|9(lH{1d=D_L=zX<#Y6W z9(o=}zrgfXbqo0N&RPH52VTLsUfljC4Ve8M^dsKCwBV}CXZ3`^w+s54;ExObc@J@^ zUdR57<$(uKug^h#8TM}jzgv>u!fy{l{t(D>`(H9-_U{nlc?G;9@Fw^^2jg5Fy@v1io8n zmq)?(3HkYXV`k6UA7$;!%QX|w`YLw5cIzVeECSGf1SoH>E{FB z6Cr=z1imEn>wCbr3vqQQ&BNq=C$!^hIfp!tt1|fGLVeExe^AH|yTL0$JN^b-E%^V1 zQ)bV0!Jgygaqx-X4`n0yt>QchJsx3PJ5%OIs+Q0$%d$6Bs-2KugFU?eeiwYNApd*t zlTcqS|NISR|4t$A>A`ovZ`_}8@I6Aibck4PmsSA{6T^L1$?j2UN3(mvvXaD zpVNtNRc@HePiEt`34Rj#FZaWx;2j|zzQDO)=l$Tjgns%s_}N07Join^{)&({)Zj-# z54Y#_;7iEcyxljzw+s3CQt+L^yzvI`icqiLfv*ew?vOVlo-dxw|0jT}gnFgH)q?)B zh)a3(Dm*87GvfAY$S(kkDzDwxWmxDhjlzSI= zM=1AcXEA$Jm(2R-rQkbx%^YNqkZ9rBhfCe0N)AyoX5cT3jL@Dz61KX{H0uvFn_xldow#ot##&o<;q zuID@8D#356cQAW)3+?z)@LhtupK~D(_qZNG{*%O)wy^U?$nO>M-aha(!EYzLli9gP z@Z0H^s#M3(I&>%IAw1q*2l?%S{`blJNR{|KsUIf3l|Qe8o*jaod%-7yAO1Z@&*ASP z`|11;{(YSX!6!=+r+D5j^j{ZosohV!owYk3*E*0lBTk+KJs$xN3-jb}fG@*No}V8A z-z((vL(gXRm=OMrozIG=49{{J5M?LxUveh;%}9rG)0 z=ZWAdLEZuO3i2HxpPVPlL+F5CAA|huLj3<2{3Ibx{ssQHz+XZefKq>n-^u!m z^gWl-Pk^{&|88L(RRcdn@XzJoR||ROCN7WoKZL1Q{Ty6{bIzB+{|UZVm{%P2US`jd zFz+&g?-b_Aaqw-hljrjZ_&y>3e1_{0_VM<;P8!!}9|_N~!%Wqc^7ys?dxuZOh3}9a zseMz}FA9U-1^qjOarQ5gJo!I|_gE>7%Rk4Q$Ld=V`rWH#PUk5NkqOZ;vXI{`tVhlS ze_V*qE5XA;e!Crfr_f&i2A>G=`NH=xJNLlPyr0^@*M)e z&)dPxLL5HA^}ugj|8sV*b`4yykO@yiEouB?~^%w zF9YIa8SVaa=m`t@pLag9b6Mz*TF!;~rond#`F{+q7V3Kq_~SzR{t$e-&`%#BzE!;r zdOx#sx6tk{2frQphsVh(_?jSp9{AOWPcDBm_+B9o+zYM}?Eeq=y@Edt7ce`|7V(ps3;Fq8@V$aPe+GY0$j{q8!0bFy z;I9C`8hW^$Yv7Ly^%@YD?Ej}QFWL$DC1D=&Rq#Eqe;ew%7yL${UQfM{**Ou~@zvn> z3h~wk-y_uP1K{g|{oe-PCHU=O@O?sl`=5)LJxjv4uL0jH?3V_?w+r)=2KchDpYv|c zg?@1r_}N09{{i^AFs?lezEkk?|6I)MU&s7```-fI5%O~r{A!`x^TAbuKfeLKL+CGm zllhUVim)zt(Iw2Dj-cNKz8CGz$Jr+MHi3TtdZ_vli#-1q z#=B#{mxT5`P2yXucVfh!gV;XsR|R_Zz)tS}cX9o~{?!%W4+`;l#~l6lLw*&-n)UHlg4B16(EC&pPTdmFmS?#Djsj)K7N`{Uru|yAbDRgD(s5 za4q;Q*w4qid%3(|=f61@{CxD~%${ZBRj$Vleu%J+&VZ|gy!2-9C1D@+L&T+V_Pf7e z^8_^txRuKb?f4t;eFA^hPWVm8V=tFD<+0sDe|#S0zBRh!lZJ=deQ75gTT3jWh7_(!YYAFV?Dv<7j9Rj zzwDJ_zD_d(Qb7Y>G3W6)TybqEoO32qdXvkN<8#1Hwp{eK3dKn#lMjSUJ?m;j?b}GL zg_C8QIvv&VlB#`cK40wjtGPxolX0f|o$`iRuj{N>vW1pIo$joJjC^({>7|-9uyfxwX)Z@?6)R?Y z`AQ};QMWBwe_!33=BkF+v>EDK^3}MXw^(D5#!UlhT~de*tAi}Hm#xvxlgqOGWKb6f z1>D-6&DxEbVojfVb1fjvKWS#=FSb;@?0B_T9TZjVzO_Lg4O4UWGZ{}HR9Wq%G?74- z=Gj_Xeq|hUq`6h?e!MsRFT^(uq?w<6*z3t*z7kF}%pJW>Yfk$uxwV#akjtj?+=XOX ztT>PbHzp;YE!7$fEqPnEQ?K;wp}MA2ZWfzuT6&RSy4Y)zq-ulx&SYHuQKb~<=K5o2 zyg3c54zf*0k%!a)!S67qSJlmFx?pl;jjg<6v`P`15Af;@JB93^815BEvFZkO087qb zqnNIbP0?(_Vb@ptHhmL-llsCG)uB~jz z=CeLtAxSPuZW~c$=80}9EflHawe!|dBiJs`2Rh|)CD?QXR>NAIzdH$bE!jcc#4AsW zMWvSA)ZFenm~I4FAsTQF2jQ5to{L&?mRuv)jBkwE-0G}UGisS=snRleMy*IZY|WXo zwYFx++q9eyWP23t6GJTL59)?Vo4xPM8cjj1x)tU^d0L~!+lfAuy3;t z_10Fq7`E;gl{NOvZkuy;Iu5E$HeED3&}BDb`iQDm?8~8>57`~7J&iHt(ARVYS}Nx} zx+!;XXV|YujYseRp+gq(!J`gYSdDR6BRP{HCjl&i(t@;=C< z-ZiIlD?LljoA7ypmb_0th#P`jT-w5k4YK*VtvwjDo2o4RubTI^@|n!Y*bPPdwAS8e zcs!MmU$c_3+N1n~(t>>M!b)pax$T)kwJ#;>OeWe-X^Yl6)y_F;I4U_)dKz_^dGDI_ zWz6hI8Y`uI*>1_Jz12kD+Ky^1`G};j*jlJ>vk%T{jIOjip&Y45>RQDhsddz&<-l}& zK>OH|+0}AAk~HP|X;ZgcPfcjsMa`=^tFEx(FGr@SrZt(0ta_@Un9UZSS`Sd>o0u)x zMxgBqcUF5&O8*mWe!7z6J!@8*C*VyMQnf-lwc4-8oAF4pZLmdz_;2S1U014K9vEDu z6>n_ZX*Fx(1BNS1|aShBWOEk3C_Om)g+gLJZB$p)&m11sXTWGjQ=25s4m?Q6!-&>600ht;hk zCF`?J+J1F|B1s)i2TL@Bhi#n$yPb7l?M*1+yNO}eSx+P^`SH-(tMisv7{cmP6eh>| zjNuf8`L!XVqit5~?$NZiUmOmq4ZguggS(?TqG>Ld7`Cc;8jY~+gOBwGy#qfa*}USW zS(znQ(K>Xkcp(ur#og1cIcv?Z<;2{}v|QY;w3uyjLnNJvqG`~w<@RL6DE1G;X!dM*}S%Wp59m~5Yb2O;Q0}Be!XEbVZBkUSfn?-KbY>0XK zE7e3IPveTmR!C6l^Nj1UxHwQ)vWAg96bU!n-M&?8sJ4UViYsN64KN0Cj`6r=aHZ4+ zOHNbX&|3S}bR@&pIP&siKRnYbjh<(9P4J%V_rpR3+Q5YkEOptVW9REo&GW1c20wREvd_lmOJYR8gqM!dsfDeNm0 zw1)A}>EubB-9?bbFdpudK}+o(1^og0>0QU#M!#B$M+z4aw;D3<;vY$EnJ$~ zHCkOFrr|?yp*^7gNNPeG0Jfwpu(}bW1@2lfP^~woelE5-w$T<_v+k&trS)JST#Xow zbxS_4t2Pb6ysNGWlxa-fY#`oZwB1AZ2eXX>8q6Nl^|Cfvl?`ikXEofJq^yNTnP=~L zS6Nb;clCg*Bv0c@jo^yEXAPzYRpTm6(Eag-+sy`#c`Dcj&AC3Nl z0Sz+;R65br*&|^}N?yO!+is*o{YlVEH+@*O=3Qk$IPWUklAL#yMd-Y%xF0n4G7G`E zmswhxdwH?ln#G1|6f9?bYhKw}%u(j}8~1VMetL1mv@A&GetL0h;vUc3PcN>wcCpux zQ|2kh{h7I!7h8_IGjl(^*mB&vnfGbjdYN|_cW&lg#*LY|mlroEZp+O5G)uK}FE93* zVR6~o{llr8vqFWQ+tbS#2ccRvNITLiy(CS}511DpFz8lBt-ODAC9NNIriI8_-d;7? zS$dmi>*C1O?;kU|)-{^(Omqfc!j&>vvTNq>if%$PB=!Uc4F`BSB5KzNHEwHpXf1Ib9u&hJ%Po+VQ2QCp6=LAyK6*tXiuVF+?2tc6buv3(N`cEs6RLT&RG+fAmuU2-DC8y`CGy41syCgY5Ee$#vDn`R zbw-c5-Mp$8O!APjsJ2E)%jT-IEpLb&I1AJ}f_;51kf`^^mDRkytuF*DIqAt6d3HAM zD)ZpHs~9-uUS_VDdzpEE?q!9tS)Q5uX*M#~q{$pZn>>KF4kCd7suVu9$wYa>L>lGc0Z~1MB7UOv4TJxtAB4 zhTGZmK8?G%b1yHB7SrOg&1~42r`)`_-sZ(+TNZy>qh=Gwd4+4#i;dH$7q_WKz1TdB zdT}&s)QcldqfxLA&pFMlA6A`nuPfvU4W@B(Kd;c&HF%C^{^u3^i@DnT&nx&(ui$^o ziRS88=+hd7POZUnEc5j*?mHSh$1(Twc#dNJb%p*xU(G9x=yN}BR`8!?aZ=ML^-s&< z{-agszgmUiU#k%RTCDx%)nALn-`wj;gSbNf($ej$MgG?+#J^TyK-1zmt-1E=75r~t z%h$P|H!RMtT7~|l#dBcu^(*)X&wJ+A>Iy~n%w|;m|aQ^kh^8=m2_@^@~eBQjc z|LdrK1gp(Vs3bomn6X>!9;eQ-1=LfdKS5GY!`Bv2yA@n)=;CRhLDixcbLh#r72Vnj z-N5M4?Z=B=<6&x0@uk*U_D&>Q{%=%hG<1tC zC|hFcjDosmz0fSvO;c^zVW-<%(fF8a*}QQh?=-De41+gxhDN1ykYmL>3$tO zFq6sD1~sE;)mky=%gJynYfafSbxUs3o_?F&Aj&i~p_#am zF|?>add_`|j|BSkuxrgf&`~(&%c%kF!X))cdd$JlEc3fvi?m{kbY8E;9_ZZ5i$h&u z%AznMp$P|X&zP-~wj2D~R=J(1PSurGsqIZ|Hq)@xr^mG9`>=s_qwJhUsyf$IT4;1F`Eo-2Uxk^%JO_`f&+{0bHlKTW@gRz4 z$>)B0@erf10#TR%DdcyP{4$rV-Rrf-|6g&fFfUR_`wFWXg-MaZoJe6xWLccb6&6$q z>m?eQ7WG>V4N^#A)5b93HwH&0pSRnJw952krG7+D#Ia3V1ucswO&Ya=6*NvPv{GSG zrcqe5Y4GyYynxr>p4|NF3ic^1`ZNlAvl`rZn_s?#4So5rHB1jZ(n2RH%q%qu+prqk=bCGzLXy|u4W#*>UpxwE6jrtxg_WyDVJ}ysuyWNX%zQNp z8C|20(lrWOvKob)ufZEebN#C@^VTS=j5W0KrFShdnYDVG9*!`!t=>{8YEEc1O{*^7 z{O=M4y`{4-Mio}C8im!XMj_2>%nFH0VRKnSs{`2wdS||zP3NXDXMdz0nXNj1GvwNw zf-bD0W%1~$RoH;msux#5tFVC9Dr`_|6*j1~3b|ZMxx7@R_krlS-eP}B&pwsa*~rSI zS4i3{dAG?N+nn<+a+6jeWoz-I!@OwM;(gS)*A@1uwF)zDE#5bsuV0~=6tb2oT$5@TD(F}s#V&xl+U%&w%PrT&2dKV z1NO0h5Hk&G{!zV8kGwbO4UnKw-QE9=6 zc^K)8P5RBKW^QhG3a*gb)b4Z#p`6t;Y^g2T6}#Z&{=m?#^6j4iowaYF5@ z+bmM{v$Yl7nB*ws#%wEv9zC28@daud_RRt}{oKs1_G9j0(3dQ@eWjwF9(k`6r~jRs z6_x~Ag&Trela3`ub9~@cH)@fAwK3^W=|R@2$(q$Y(bifo;IOuvBifuWYKqQeVxy*} zr(yq_*``+E7KK(}5YQ^zVALvX0cjN`>{^8xyH?>Ah*n`ruT>gB6z2RogTupgts$KH9n$A$XS=DTIN?vPi!>|&W zwxg+(ezHMNum_?;dS&3ht2K>o+{JTB`!B1lu=3C;tUPoIx1)3lD-)fTi|MM2zPg`B;RIS@Cgle?q+Dhj_HLKaSvYBTWt699) zr=uOU#h+KW;h}ItQm3%-rc+q5>1atdyZKKOB~m&yg{u1Qz*}|1niG4}l4Vbi&TVnp z!DBP8N-gTFyWB>*J?EUX>E)QU@oGD^8MfTz4x1(%(L~*$gm2BJ9fxCUp@iwb%Qgzz zeL98tfzGmcqeG`Kf6yt+A9OT75MH%itqv*_Z1kqC^p`!GuM`&sg&I$X&hxgJUi?v5 z-x#HLe-`g->I@o^S-iETGg?SSIzP@8wFaCSXOLbSQT6F;hN6=W-k@i8YkHF}SF8?o zqY}Lg)yoYdx(&ClOfJaNd1%=U51n6?GIaZvoYqO{G1f2YOU<-ZRqV9$l{mcwD7{M6 zF4%jkYyEu4T?{5G+I(l2uG8yb^qK^{3Y^I{lMUJqt##9CjnGA@eXW@(%qTCs|m~=;(!FcKjWG zlfz4Av^BcJ zSinrBVkR@<_E`+1 z$&7sgohjY+8&?P6P=a24GbYL!vsJ~;p`&+vijlS8Bv&wX%+vem}0SCkHZp%?GTVN*-CJWWNbuAx8LubK0c zoGGBC_eSM+V#%$oLbg|+GhgT>tyrg+ua>IPRz)&t_j&atpV6HPPa^c*cBkfzNv~Q< zk+#_w(WW~dYib&1ZMrllmUI;<;mfxr>Ag*yt)dYM#A&9}BHGEx9PY7nwI#v$7H*k0BX)Tp_Kqm=D9eQsf5u!s26Ln`{u+kr`Ib~IH zFNp_&vFS+PHBY0yhR0CUBhAbp8ZavI*=<%?|_zL!Hv2; z8*eA=vA9#y?v$J(3i@KVKtbGV)6ox^LQG?G>5`KgouRQ3Xf`X!6<@3qp(vzRFY^QW z1VehJe$7*D)0rWimcQ*Wxu^D;hF(T0(tSxCV#*uJWYVd+Mqk&_SuE*f!43C1*I;7KhEIHR&+`3}UXG4Cyw>{Oibv7LZ0WUK?zdTl&e5#L=?n3qC zPL6XnG8vy~7%^t!zR{{N)Cwid&OkdDj8KrX1BB!=T&?s@@;w$UAfn&y==UiaqtT8H{=wp-|ZzHhG6u6`h_bA0i{4mJsh{rQ<8| zPFp1$)94Cx{*ZUA9Wqoq32R3VK6;6@=%K)t&OfMx#{OKuR9wrZ%g&XaXVqV<2I-7L zsnMk80ovxyBwbi@TD@7*xbA7t3lSmdXur*lSj$#}$&K3TaA>6rsUKx04qcW^-fZDoN#~68l5{s*I&Z1ci2J+s>RQV08zTJboddSe-o@yMv!$RvyCCS!)mAZ>@;0tXi^$g4Z14Lj*C8HL`8?u1n6B3 z>9&eBKd6qRJ}w>e(;S9Ot8TYRrw+$gRy*xPt1X%fcgyFSE{atBmJMg0I8C8w zI9Ok^(7OrKCg}$DUFR;D27JGUa8ThV-s5a zYHToV6z%dkGM0SOTS_>c#&pSO(-mR~_t>2G(@`Z-2Bx?DZBywy9~FJlogJ3R7bDv5 z)2Q9uQLwmXrDG#v-GW5@y^9lf6!jEga!fnvt=Sq`rY#Pfy<{NP3`gjuxM?gEP5GqSq=Jn4cw0&& zlwDYRhv+a)t8@zN{?-lDM$t8Iz*lUg1G=?Z#9gk};$pb+VxR2L)Tjz3K}4 zQqjI6pK{r2X@7u@>q(RUXprv>60M5dISO)rrjt&yX1lqQpvizgvYH&%b%usvRFXOY z4d-;A2_lo`OD#l2rx9wn`Vp&jlus3OEtkPxTHP3{Cd~#r$Aa>pHkD7&2HtesDi+=8 zWHr$=X{p~)9j%1|oxVwRrB{sPcZGYyRxwm6QLdt`lU9#5bpz^LyBp~it==fD-wI9g zIfe_ooytcHav)7e=##Qx{2UeOcp){tb2J&`)4E7#&DXb?nt^;}k|@yw2~xe}vx?-L z?dr9gfh-OF6UyFeuF}A2UFlh)+2+I@q8F+na@@%sd2v5hNwuIkiFKFxS-qNV|;*rFq5rNE2n%}I5!>Ksn(*+#`QZnre4RZ17qc|=}!GSHt+D(+IK zP;>MPd8a$np$u4HzG5$R9WakMa8&b7VlHpdn$-7w$)VpkEanDPZ&_8GNHaegzgD}Q zvR4z#RVry+J{k5@yarh_pYfDkW!EqtX{^x6DS2Zm78~~pJwrr=gW)!lerZ%`k>3ns zXK7<Gaz{un8}z&p}=?)w3%}aekdtDJ zT6gPt%FO&@!xr!Rw1Mz2n9loB#)PqHDy&GUYJYzVwxuAKeJag;*`$Nw-CxX(iqv-~ zCgnrQqBwp!Ug;IH1%EqFCo;%qLa_x7OMm3^P)R#Syzj`Cn2ss#1tZv(c?5UMhBkgE4GR(Ybc^ zV1;sIE9!4`SAF#{#g%-h65dH16*TlznXzuO76x5?+*(K{YKh6_%$W7kMsr;D^b3`E zzZaof39j1EJeCJbO0ASC=^Uy|rY{{-QSrC(u417)-WVCC;lOdp z8n3ov$(5L=jN_*HSM2A$4YgIj@Go#YsM4_mOX;=Ll zu|kX{X$LGd^oCM+;HsqbuIwn9O^qFuXo;phY{@o=wMy}|k;y$-YdfYcN7IrUZFI}3 zO0`^37|AHr(SUy7Nr&p{#Aa*wY=%gX)@GAlb7K%~+Q+Jfbi=Hl476h-b8WJgUYXF4 z=E~O!dg_OCzFCaMFP^?=VjZT*yes8x72BRhZDJ0sjk+_LB=57@z0tvN%@m9UyndSU z>XKF(?}mA#NeWJykyI%n+zx-RJ56ki=>)D^Z{%v^UC*5Yk8 zsAV##*h+e(Tuc}e32ioOb_Yj&-Rctt)B{Eod3`I7r39q2 z6u1K$Dc#Z0bFrREiRR&S2X{5P5*+)6K3W9z2XxYWu-c^G2ln=M&A;l&c6@$Kt>c=k zBx_AyIZ>v`1D&APAB6g{qiO!T^$M5z-ps~DsS+|4X}cik&K6QbdhR%Ept+)fwl1iP zdT2r_tDCEhR(+JY4ouMU0(t*B+Z!JBd(&>cPoY1VWXn2lq^Fg(eFoHbWjBR%u4r%7 z=Svi-=3zAMo>apHnjXND(fwODSxrCbiAS?=-qxlaN7ErX|njg3{J5&|-J)z|7~Cv;{|d9hJH@>yL(d1+_;?sL{~s zKts(Z*Vih4p#jY(JMI;?!QPH+IF0&#-9_Vg*ln`1QCC{5SX%`?CQ+j#jB(S->cHXe ztyTuj%37>VGdZK=4{6<(qWc2*NX4-ljg?b%Psz8M@46cO2raHD2yN{)9q35i9}{%; zx73@d?n&u{Hhn#>&rde`c2hQ`@rHbZkUP6U8IXndoQdabJoG~LH99*f-*H=W!+Ld9 zH*vOX)>#?p34+$N8uA6p3A1LyS#p)5x)B9`C0`F#8;)wfBSjChs!uy}2D4gkG;O3> zI<3`7XWP-njo$CKvOOz*ReA(o>uWpGYb!RrzeyVlVUOS4q?vzte~+_j$K6^rT6K2D z*;QN7<}`bQRe89Q4>e2~>A-`Ot5zPbsA;FezoF0N)ohTU?j~kD-3cHx22qs{`^P z4e4Wf#T1~Ei|G_wc`q_bPeJzzzDb{E%${B(U+~1}xqO=U#M^#pPs-ZQSF7?W=Ls8g zZR?sXZAyimXtuy|AdPzzd-8e9ol>=5AJpmK#9nJ9ykW@ez5YS8ERX6^ z%p*f5;?qgMtf!+_y}q$6Y|^J)^feb~cB(>_TwbB6iFz2D>Y|-+qDuDxY`PVivr9RO zue#~<|o?pKW^-7rzsm&?^?jAl2oSEWuMtr%(hS6g?Kx`kL?Q%sfAY5Qu(U8Ua2 z_8G8nrW1_m%QNZ`bGK%m7!AF`P?|qej+J&90`0<(zNLdN7i9NP7*jasdo(_`veM$) zy`5m!+i~h0(_+6w$69XKYaL^Qx&uv8 z^7W2%1~*NU@>5T=77nI$&0ZnvX;!^G=i25lPAA{)-`YN)NjLn1{7Sc3t5A>frs>S$ zbihrM7kPSPr&Fg%u|HLhw#I=vO>*LP+Wj)n@us76p9W8C%2nNEb=2$27o($olQwW& z2~Wr=Y%A$DYBXi;~2N-sjw<{%wxscA(ubU)kEP341m zhxLj3)<(Kd5o!BN`9?kGNcVer51j^Qqz4M5xM8s-p9+W(Et2NrTZ8R|NXphFaU|@Bvm{vz<`_Y~1YMffyM!`+>1AIuwrw#@*fKm5ejnX-y zqfj+l^cFV;MyqpUt&6Rw{kx))QEW*YqmJ7sIvaF6-po>>FRkLFInWb!GX1G=N;@jJ z%;99Oq>09SPC5pa2DV|3{`1I(+u5u3YMS;A>9F-|qvz60lDddPlci~>lm*x+(#cRl zN6$d#2A)95R<$Q>hCyIWLs3pUIK{3M({en?UAUAmrW-5P0o@Jn71etBejb0p-KP}? z9n5~f?qA**^m|h)5rf0)8$`O%6^}V)5~rdqAMH!ip`z7%sWtGro5{3s)wzF$$?wP< z;9Tk_a(APf6XwRMmWF6;%aHbDCmu`ATGG>JtF36ZC8b!Kx2tRD+O|>8R!qfKU2#Ko zjrKJ8Tr)&R)k~Wj-OAML&aP#{bX&{c?HQA1J9*0`P1dL|>wwZ}Zb zDQeEs;c5Pjc+5;s{~5?lbY?l7T*t=xrsOm^2U8=ugNcmOgBxxhN0iM=WCP{#z$=A0 z+k2uTbDG^&(&l#zQk8Udqe~C5jXZP?qI5ey|vg>)8goFCmShb8e#QgeH`mM_2K6=m=etw$V*= zbB#f(KXujUPTQ6nne*0sG_#RMavObfnRJhj7PEuoY9gPU`aHBIvW4{yb=Z=pd8~YE zoPuKPR<+*zSX|9GQQwr1C;&(^$gjaNEf{E0i&i*}@> ziMzZ@gl)4Y%(i+lqa|NoacH~AVBcrfYr5XPKSEQIxR>rF(Mh2tdeXG6Pq`z;R?$h{ zfaVYT#u;fn({AN8blWa%HJQ_E(px4pL&~StNSsFWqQg!nSDGkG(3h{}Gx`2d{t`Dn zjUNpA-ay!&$%JB^GHru|I=xl1wDySozt*lD)_o?chvzOJV4(pCicexCX!b8V@#{(y zn|GY|!*SwBkj=x1llT=sk~kFwLQ8|Tph6T|p|6AnZAn3~1qBV#ifS9gRumvnA(r3x zac^!uC)a<8NcWBt`^=1I&YU^#q0G7r{o8g9N4&5Pk1{mRb6c_ick4Pa>LEusW=>nP zj8Pp+^_A?;+GS1IdPr~|vW2R7`u4Z$ZLw4){szd5mF*4U=N9nd2@J~zp`5eqMb*5e z&U$@u?KR|0rxO&wlw=waY&MymD}2sKu`!Pco(Uaf*)hLmuG+pKzbwllryA0>vU`=F zvJ8D88u~|B^XECsd^46L8SLltC6d#h2fEanY3;LGsT8<9@~3Tc8{dR_;{bPm^==Me zi+}Y2Al6v9uU4A6B7xoxiuTOjG=%eh3oKF{$F%0CB~((ruO+0QF~?8q&Z?|1CB6Y6 zZ_%p_uA3FpKVuneOLcOlpP?R1yD@RR^Rd&cqY*52a9N2xamrCp&%1$<=XuDC>IR*zbzLsOXth;+D9G0ti_EYgdM;ghy;Y@4L)`c468w)YheHFY$#QcM?o@B5AWEGw zIRjB5+2RTDw0!UVTSRz~M|&gd%#Veui34gb7t_5?0;VdXk0jS~#7p6TwlE?3L>5PV z-!06JWqITyKK%NB6I{E74u~Er)JNd z!}emWz-j>LJb^w}_g3YISPP#T_?599?oS_&fDpA|7K+SvKXhudlHWBYx@RhL+1)b<68)eB7hs%w*1CIOPRST1RFho5dn>)ucjqpfJ zL)S2YOHS!*L8`aT3RVfwdJw<(ScP?;=!YZeEtJ!t*AoN9F&ebuKnT|_O7*PFAX$XH ze-OSO4}7tHG-!2vz%iMZtEuQbUvg%oOW-_@3sa-ctzE6=4@*1Ec1m4lK(-8H5iHe^ zR}I^zS`M~lea{?lDwAAgDXb5#0+z~xy~8Cc>N?nmu~RmX(_5-@vy;EHwcH)}Dh?b;CbG}Rk3FUWSZq(; zKV*=5rYhpZPmfsju^U+MrYV+=<2Br5pa@y{aLui?u_=B;aFAO)Q>P2hdX#aWz1hKT zC@}jHlaTyup<2`Y9+}G>=e#>C*&;Q7WhG@Mc$R0mkZiv!^5vI>ou~T~ryta>GrvDck%MOf89fSjduB^46_BO)AG^J{9QJ^6;yz)O!=1F+#9E{gIym3|#x;1X=Un{=j znk^Pv@?2d*2^F>U=tz)+*LuqeNcoE&%j3(?%wd$uc z&i#s7J~?c~lIXqImg?)FuAJzeV&fgtM)w7g;31wHhdtc$#^Yj;<9 zt)X~4_Kg(`J!zfhUnq(v<2UW^3L_nRP?y`nt!X&icHtrp4}^~^o+GwAt!|Es2c#qy z*KK#C9j3fHI7w~4U>;zN%v3c|tvWC@zLA-bLw!|qfvZ~{S*<-(dcjUwDFLK9aOD-^ z#ZN>rCmNIrHNF;^t6T9|Z)-OPcxa_h``gdpC1a)_@Pb{rd2BIoodV{H&=L$~y9l-> zw%uglBIDgP{h%Zw)O+19AaX0!t3s%OA~;WRb`sqf0AJ;$~g zoY`mS%3uI~Fl2D%EA22R63yGVrBBiehg})+@tmGfd8pKVZMqx`h4Rs#nau(Y<4Bv? z7nWs0px}C(wvLoD=B`Um<8~ut{B5UW8iP2(koYJU$S^h|FKZeEhiPTZffp|pLf57_ zc$Y2q$vKoTaB0gK2;X~q12Xd==?Sc^UaDWHO5PjpuHsr7_|-0!W%e0(wpV0YO*jbU z32|qh+~c0ydo=)NKq4j@iwB1@#GC@UKFzrQua{MyQWCyg~EFwWaUGFQA|tk@n$#vRof%b#qd99BZ;44ZYJvNQ@kz z(*D6_H@{(=9ycFLp3aahT^+i@9-ND1(cM)yPzhU`GGziK#4NYnbDfPw+l~CO4>lHV z6VhPD!AUF?Do@59Z`8V!Il3J*nVLPYx5FZwx5m@-)y?s2#HM%6WogtLjnA6v!H%_C zI(-V~RfWjGk2jJd_FXvR*DL*V1DeBg6Q}&d$oS|iv3br=4d`Uw69OXe>WCbGJ9_Bb z+xDP%hhDj?a$;X=7RI%1yNlH7Vap?soHA70=0cg7^~q{4t>NJYwEU3MLo&Ru2C1QJ z{UU_#(RR09U1x>hTopSIbv~|v3h5HeoSrW&$@W;> zpGSvyMMUt!#*kQz@Nm#I>n{u1*TMS_o0DA!7hO~AoRSs$;M6XlTe{jJtTp|BJq(qx zCI;n98-G3<>w_EynOLMeO*HPvjJ6^stV~J(Oh^_Egi~V9Ug$&Oml5%w z&1H}FcFeeaan8`uG1qQ?iIo|v2Z7%>!P9^V^ga5Ms*St7F^g&g`21Cl7*43!h9pLt zU3=#AO~lz*PzS8(j?trrt7{DlnK;(&@B5WPWC-6GsgMfh?5Z-y7PmFpQiGWthm;5< zD?CtN_1@00tt;#jtHrCF^S!BZY*tO!U&wa<5vIpfGyUK`^mk6g48k+P(w3z@mhhBc z8Tqs~WD|M7E>=`Z4H#WfF7C|fTq^YobY!nEk4ZiTk3Yo z^GIz@k*yx`l&~xx+oe2&jQ(MGY>O{2N0E4g*IB)3odL4v`H)304#;jN2HLi^P|wbJ zRpvCrhot}s)JrTGcEloFtIN*bZ*Brfhm+zsFAkoE72+ZN&`d?;wh{s;z$qMTsP5dBr{*16XV~E6 z9z-}wgzcxFc=}myd_x0n(3#D%6i-&%_}sXPCX%5PkF|2MqguOSH4k1CqOzlIU@U7p zJ$`x|`C?s#J<|1Nb%ioOeqdNJt@j8XcDYKmsdHVyQEB~(K+A2{>-O>q*8`1$QWI;w zSHZcImX8BcXa1!4K(EarO)W#@BD!Sl*V^QAG)~N=v{=@?w&=hG(ma(d1Mv&QeYo8Y zYM?#t+NWpJPUTcog$g`evh2QTRDx%?m?V{}k|5v^TyNjy_W{)(?!bUR_$S{7q|;e z+z(<*x=8s=VPiu+;i1=mSx!!tR39`5XYpn(CEg5aO$g98MNRCW$)Mr5zGT+p4c}`r z_S>fK;&_JJEb#SR<^$$3qt@ zwr`1Bx)~LQx)5{g)ze5GUDo}{WZS8C7y*)q@&Gp(Cf-E!qhKAqGNNHi{z*9)r@kUN z@E3z3$!XLC&I*(XHR>&?Sx;Q$14IR9>3ggRE?0z#eM0oMQ5>ndyy%WB?wYJ6c>&L= z7PYh$<0uX&qqS;m2Iy}1F4JnTa>}lONE^7{Oo~c%wJs$$9~>f^Ny14(4_?60A~;V8 z#4Ip-6Xr4=6p)lw6E~G<%`5;t!amgm#2o85ow<(i=v@nbXPa_GCPHdtID$_owyH2X zwK3etokn4=sHNtW8*oS##`V2eYkNDR57@CMnjol`Hf|dVYTphGqcF#qY+jt5jtb-I zK?xBL=X!S-n_AP)LQI5Hg2+#N5rlYud!j}UJxrpo(i`e5u<>#`E?30W#F>GbA~5#aMXb}zup+xMS38atz~0xiU;UUqFSE3e`0<5 zU?h`wdQ#I12-(tgV~N9dps?=*B}Mhs!+BC__ID0zwn`FQ=hNsCRx_37FrB;3uBsmp zz05FL(}yXu%2q*^QehG`yk>G9b?C3BB)ZccvGClq3;V>z@TAWy91G#wdM9lXnKZRQ z2HpXY{z%itcBg!Dryd;Ugtl&R2w;FaX1LDm$y%n(3Q}YFM%%eizS?Tmu=X=eDY8F> zm82h!p3c4tFH~gyLNwL5v7!yB*w;Eean~7F_k2cEM@)p%PeYy~RM^~pMgGmgDbJ0mn2=(r{IeN8SZotAC6agt$vQRBOYI#H)cDnb&IK3PfnX(LOB1+A(z+qk0Kl!ZZXN=IWJo&ceF4MF?RW%mo0r9u*m z%Lv*U3cV%_$=L^xO`_K$FMvoUj`a)tGs;uGwqS~8xw}86OU3*P19qF>&Pq8q8Yh{Tn*6oRiov-imEhd| zif@m*b7Ovf>kiK|v2DCNY45a)j{ujG>e-DEJIG(;-i@^VxaJ!f=J7-EW&~+ft2yn3 zHgwy0gsW|a71`4h)@R*pZwG;56lG-q_x|3fRFYmDe@-pBb=@c%x>9fN#-iPZK7B-C zvckD6J4L5BR;f*=RqUzjAtNlS*QDNoI?=fjx7mysd(2JX>$P$!U~NJtnm9-$bT)Uu zF-*-9)eM}8T4+dXia*;UhmYCxzNcLJhQdy%wdHJ1mtt+%w9yE3V{k5sjF0N=}WBeDbE+3d}mPz3i4YUoh+=*lVkQGm|%yBrvdO5H-$h zN_Az5Z)~~+WOeCE^>W9zki=vKH@lcOvpfcQaQ4RoygX``x)$vBdl?SDuNZPuFLG5x zv_(D^PwQ6E^9O^oTXnjl)3kC)g%PMky?3e=`E|YEi<)ieLkN|}t$}<*AMzBSj-d_X zMipEYPaUn;EuD}K#Lic*Cl6r{ML6Y z|IxITVfY|7VEW^;R4xq;;#D~86}dOf#~g>%4lHfJ_wrCt*3FIHb6uf|b(3X(2F1PX z?o3ecWL}~}Ps1$^=h=lg+aMV=_S%5t1nx6NZNawWU3~%)8oF4&e;q^-cgk_9E(8JC zAzs7Rfj3Kv=;X(4WmvV9K%cdkjh{^%#j#Bxfp~a=Z${P&nf^lu&csRmo%?1c<_#G) z{nvydiH~e#3>h}zZ9^ z_6lr6s~%K6Mi}^a$Om~(AA9Gmp6BhB6i}`MyiJ>EC?YwU@a8=*N+lRZ^mQJpqnXM! zs&*0kiIZ&|$9wWvC{DVwX`>`}CZ%l=8WBcZsaO?RXaUjM5Y6trXJi)Gc!Pq+fHX$f z+slF<>muA46=Ti%3pd$H4m-}K2f?OyXtoD9-RR7AMe31`j7yZ+#dATR534e>Dzy&5 zaRdWoZG>3R*8zRJab~1%>DxO4C&WDA8>TaMxH=BI60bOwR*!+$;Xz^#SH{t4k1Cd$ zw@Oy=*!7yQ5v)BE1md-KH+zPVkN1pO$5jy*q3qjzy4#axJmjjQjR2Ie&XU&Yz%^EJ z+q+Jups3Q)la*~q+*X$b@%#@%{ok+t5Yk838_)K}{b9B1mWncD2Hfom>Lb_gG;mH7hhG%`I`r<9*!J+5#++vB;LcJ*3260=SS{U!n zD`r5$T5sjMOc`+xV?+ej;GVwa&x}-9m`m;is+Xe_Hy84B~B_`~Ud-4l4N>{QYcP*_ZJ} zQvU8;BjnlJ&+p;O*YKCX|6_cSl;6C47}r04_npJ{Tlk;E@Jac_-ShYG72N;tpZ?TJ z+W-2`MF2@D#PGimmtT*|r0C+}SMZm>e>;Xx%1iwH7x4^v_XPg8aZNrl{>UQ<{13>3 z_$1}u-3{2?*W4fD*`MC@e;+rHl+CcVTrTN<(oRwyaQ}aA;C~RqCuKi|zwdwibMA@@ zGNws=U;m}(PE!6j?r_rn{rmrE3}1@CazFXy=prfYxMBkTTll+&{|o#^nuv!jm-`03 zkWX9^{Jj0#&;Os}<}LgeF|K&D% zJ}G}Sc`z>D*OtqDKZZ}nKWE2J#O2@Y-&gR(`>(k#@8G}u*SP(-{A)7kH=q0V{~q_q z5GCz5{xf*AzVk;>dH;O!Jt_YQu)lr-uf_0M zM))5Amh?Yq-;Uw!7=DU-x1am@`#0PqxR=2HFoyqO4F8RIU=sXH`kmDCAAr4W|3_Bz z-and0hAXh{x|P_Ping@6WqIf^t*BU(?5%TCHv6t12vOz uOXx2lS4rP*;eY(^(fe1vfBS3H*V|vna!-T*=69nR|3=c$80hY6?*9NLkB?RW literal 0 HcmV?d00001 diff --git a/lib/pure/options.nim b/lib/pure/options.nim index ad98e626b..0a20d3d73 100644 --- a/lib/pure/options.nim +++ b/lib/pure/options.nim @@ -77,7 +77,7 @@ type UnpackDefect* = object of Defect UnpackError* {.deprecated: "See corresponding Defect".} = UnpackDefect -proc option*[T](val: T): Option[T] = +proc option*[T](val: T): Option[T] {.inline.} = ## Can be used to convert a pointer type (`ptr` or `ref` or `proc`) to an option type. ## It converts `nil` to `None`. ## @@ -98,7 +98,7 @@ proc option*[T](val: T): Option[T] = when T isnot SomePointer: result.has = true -proc some*[T](val: T): Option[T] = +proc some*[T](val: T): Option[T] {.inline.} = ## Returns an `Option` that has the value `val`. ## ## See also: @@ -121,7 +121,7 @@ proc some*[T](val: T): Option[T] = result.has = true result.val = val -proc none*(T: typedesc): Option[T] = +proc none*(T: typedesc): Option[T] {.inline.} = ## Returns an `Option` for this type that has no value. ## ## See also: @@ -136,7 +136,7 @@ proc none*(T: typedesc): Option[T] = # the default is the none type discard -proc none*[T]: Option[T] = +proc none*[T]: Option[T] {.inline.} = ## Alias for `none(T) proc <#none,typedesc>`_. none(T) @@ -167,7 +167,7 @@ proc isNone*[T](self: Option[T]): bool {.inline.} = else: not self.has -proc get*[T](self: Option[T]): T = +proc get*[T](self: Option[T]): T {.inline.} = ## Returns contents of an `Option`. If it is `None`, then an exception is ## thrown. ## @@ -185,7 +185,7 @@ proc get*[T](self: Option[T]): T = raise newException(UnpackError, "Can't obtain a value from a `none`") self.val -proc get*[T](self: Option[T], otherwise: T): T = +proc get*[T](self: Option[T], otherwise: T): T {.inline.} = ## Returns the contents of the `Option` or an `otherwise` value if ## the `Option` is `None`. runnableExamples: @@ -200,7 +200,7 @@ proc get*[T](self: Option[T], otherwise: T): T = else: otherwise -proc get*[T](self: var Option[T]): var T = +proc get*[T](self: var Option[T]): var T {.inline.} = ## Returns contents of the `var Option`. If it is `None`, then an exception ## is thrown. runnableExamples: @@ -215,7 +215,7 @@ proc get*[T](self: var Option[T]): var T = raise newException(UnpackError, "Can't obtain a value from a `none`") return self.val -proc map*[T](self: Option[T], callback: proc (input: T)) = +proc map*[T](self: Option[T], callback: proc (input: T)) {.inline.} = ## Applies a `callback` function to the value of the `Option`, if it has one. ## ## See also: @@ -239,7 +239,7 @@ proc map*[T](self: Option[T], callback: proc (input: T)) = if self.isSome: callback(self.val) -proc map*[T, R](self: Option[T], callback: proc (input: T): R): Option[R] = +proc map*[T, R](self: Option[T], callback: proc (input: T): R): Option[R] {.inline.} = ## Applies a `callback` function to the value of the `Option` and returns an ## `Option` containing the new value. ## @@ -266,7 +266,7 @@ proc map*[T, R](self: Option[T], callback: proc (input: T): R): Option[R] = else: none(R) -proc flatten*[A](self: Option[Option[A]]): Option[A] = +proc flatten*[A](self: Option[Option[A]]): Option[A] {.inline.} = ## Remove one level of structure in a nested `Option`. runnableExamples: let a = some(some(42)) @@ -278,7 +278,7 @@ proc flatten*[A](self: Option[Option[A]]): Option[A] = none(A) proc flatMap*[A, B](self: Option[A], - callback: proc (input: A): Option[B]): Option[B] = + callback: proc (input: A): Option[B]): Option[B] {.inline.} = ## Applies a `callback` function to the value of the `Option` and returns an ## `Option` containing the new value. ## @@ -308,7 +308,7 @@ proc flatMap*[A, B](self: Option[A], map(self, callback).flatten() -proc filter*[T](self: Option[T], callback: proc (input: T): bool): Option[T] = +proc filter*[T](self: Option[T], callback: proc (input: T): bool): Option[T] {.inline.} = ## Applies a `callback` to the value of the `Option`. ## ## If the `callback` returns `true`, the option is returned as `Some`. @@ -333,7 +333,7 @@ proc filter*[T](self: Option[T], callback: proc (input: T): bool): Option[T] = else: self -proc `==`*(a, b: Option): bool = +proc `==`*(a, b: Option): bool {.inline.} = ## Returns `true` if both `Option`s are `None`, ## or if they are both `Some` and have equal values. runnableExamples: @@ -363,7 +363,7 @@ proc `$`*[T](self: Option[T]): string = else: result = "None[" & name(T) & "]" -proc unsafeGet*[T](self: Option[T]): T = +proc unsafeGet*[T](self: Option[T]): T {.inline.}= ## Returns the value of a `some`. Behavior is undefined for `none`. ## ## **Note:** Use it only when you are **absolutely sure** the value is present @@ -513,4 +513,3 @@ when isMainModule: test "Ref type with overloaded `==`": let p = some(RefPerson.new()) check p.isSome - From 063229a301837484699fb2b8cdd02c16f9c37781 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 21 May 2020 22:18:58 +0200 Subject: [PATCH 0084/3768] fixes a bug reported in https://forum.nim-lang.org/t/6361 (#14422) --- lib/js/dom.nim | 62 +++++++++++++++++++++++++------------------------- 1 file changed, 31 insertions(+), 31 deletions(-) diff --git a/lib/js/dom.nim b/lib/js/dom.nim index 4a33668cd..950a5065b 100644 --- a/lib/js/dom.nim +++ b/lib/js/dom.nim @@ -19,29 +19,29 @@ const type EventTarget* = ref EventTargetObj EventTargetObj {.importc.} = object of RootObj - onabort*: proc (event: Event) {.nimcall.} - onblur*: proc (event: Event) {.nimcall.} - onchange*: proc (event: Event) {.nimcall.} - onclick*: proc (event: Event) {.nimcall.} - ondblclick*: proc (event: Event) {.nimcall.} - onerror*: proc (event: Event) {.nimcall.} - onfocus*: proc (event: Event) {.nimcall.} - onkeydown*: proc (event: Event) {.nimcall.} - onkeypress*: proc (event: Event) {.nimcall.} - onkeyup*: proc (event: Event) {.nimcall.} - onload*: proc (event: Event) {.nimcall.} - onmousedown*: proc (event: Event) {.nimcall.} - onmousemove*: proc (event: Event) {.nimcall.} - onmouseout*: proc (event: Event) {.nimcall.} - onmouseover*: proc (event: Event) {.nimcall.} - onmouseup*: proc (event: Event) {.nimcall.} - onreset*: proc (event: Event) {.nimcall.} - onselect*: proc (event: Event) {.nimcall.} - onsubmit*: proc (event: Event) {.nimcall.} - onunload*: proc (event: Event) {.nimcall.} - onloadstart*: proc (event: Event) {.nimcall.} - onprogress*: proc (event: Event) {.nimcall.} - onloadend*: proc (event: Event) {.nimcall.} + onabort*: proc (event: Event) {.closure.} + onblur*: proc (event: Event) {.closure.} + onchange*: proc (event: Event) {.closure.} + onclick*: proc (event: Event) {.closure.} + ondblclick*: proc (event: Event) {.closure.} + onerror*: proc (event: Event) {.closure.} + onfocus*: proc (event: Event) {.closure.} + onkeydown*: proc (event: Event) {.closure.} + onkeypress*: proc (event: Event) {.closure.} + onkeyup*: proc (event: Event) {.closure.} + onload*: proc (event: Event) {.closure.} + onmousedown*: proc (event: Event) {.closure.} + onmousemove*: proc (event: Event) {.closure.} + onmouseout*: proc (event: Event) {.closure.} + onmouseover*: proc (event: Event) {.closure.} + onmouseup*: proc (event: Event) {.closure.} + onreset*: proc (event: Event) {.closure.} + onselect*: proc (event: Event) {.closure.} + onsubmit*: proc (event: Event) {.closure.} + onunload*: proc (event: Event) {.closure.} + onloadstart*: proc (event: Event) {.closure.} + onprogress*: proc (event: Event) {.closure.} + onloadend*: proc (event: Event) {.closure.} DomEvent* {.pure.} = enum ## see `docs`_ @@ -991,7 +991,7 @@ type passive*: bool since (1, 3): - type + type DomParser* = ref object ## DOM Parser object (defined on browser only, may not be on NodeJS). ## * https://developer.mozilla.org/en-US/docs/Web/API/DOMParser @@ -1001,17 +1001,17 @@ since (1, 3): ## discard prsr.parseFromString("Hello World".cstring, "text/html".cstring) DomException* = ref DOMExceptionObj - ## The DOMException interface represents an abnormal event (called an exception) - ## which occurs as a result of calling a method or accessing a property of a web API. - ## Each exception has a name, which is a short "CamelCase" style string identifying + ## The DOMException interface represents an abnormal event (called an exception) + ## which occurs as a result of calling a method or accessing a property of a web API. + ## Each exception has a name, which is a short "CamelCase" style string identifying ## the error or abnormal condition. ## https://developer.mozilla.org/en-US/docs/Web/API/DOMException - DOMExceptionObj {.importc.} = object + DOMExceptionObj {.importc.} = object FileReader* = ref FileReaderObj - ## The FileReader object lets web applications asynchronously read the contents of files - ## (or raw data buffers) stored on the user's computer, using File or Blob objects to specify + ## The FileReader object lets web applications asynchronously read the contents of files + ## (or raw data buffers) stored on the user's computer, using File or Blob objects to specify ## the file or data to read. ## https://developer.mozilla.org/en-US/docs/Web/API/FileReader @@ -1019,7 +1019,7 @@ since (1, 3): FileReaderState* = distinct range[0'u16..2'u16] - const + const fileReaderEmpty* = 0.FileReaderState fileReaderLoading* = 1.FileReaderState fileReaderDone* = 2.FileReaderState From ddee8a362a6590d8e590e6804e31b79bdb9ab7e6 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 21 May 2020 22:36:34 +0200 Subject: [PATCH 0085/3768] change the [Processing] messages into dots (#14418) * change the [Processing] messages into dots * better implementation * maybe I should work on something else... --- compiler/lineinfos.nim | 2 +- compiler/msgs.nim | 40 +++++++++++++++++++++++++++++----- compiler/options.nim | 1 + compiler/sigmatch.nim | 2 +- tests/pragmas/twarning_off.nim | 3 +-- 5 files changed, 38 insertions(+), 10 deletions(-) diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index 2fd5a5a60..331268d6a 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -112,7 +112,7 @@ const warnUser: "$1", hintSuccess: "operation successful: $#", # keep in sync with `testament.isSuccess` - hintSuccessX: "$loc LOC; $sec sec; $mem; $build build; proj: $project; out: $output", + hintSuccessX: "${loc} lines; ${sec}s; $mem; $build build; proj: $project; out: $output", hintCC: "CC: $1", hintLineTooLong: "line too long", hintXDeclaredButNotUsed: "'$1' is declared but not used", diff --git a/compiler/msgs.nim b/compiler/msgs.nim index a43977606..7523d2d8b 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -277,6 +277,11 @@ type msgSkipHook ## skip message hook even if it is present MsgFlags* = set[MsgFlag] +template flushDot(stdorr) = + if conf.lastMsgWasDot: + write(stdorr, "\n") + conf.lastMsgWasDot = false + proc msgWriteln*(conf: ConfigRef; s: string, flags: MsgFlags = {}) = ## Writes given message string to stderr by default. ## If ``--stdout`` option is given, writes to stdout instead. If message hook @@ -286,15 +291,16 @@ proc msgWriteln*(conf: ConfigRef; s: string, flags: MsgFlags = {}) = ## This is used for 'nim dump' etc. where we don't have nimsuggest ## support. #if conf.cmd == cmdIdeTools and optCDebug notin gGlobalOptions: return - if not isNil(conf.writelnHook) and msgSkipHook notin flags: conf.writelnHook(s) elif optStdout in conf.globalOptions or msgStdout in flags: if eStdOut in conf.m.errorOutputs: + flushDot(stdout) writeLine(stdout, s) flushFile(stdout) else: if eStdErr in conf.m.errorOutputs: + flushDot(stderr) writeLine(stderr, s) # On Windows stderr is fully-buffered when piped, regardless of C std. when defined(windows): @@ -330,15 +336,27 @@ macro callStyledWriteLineStderr(args: varargs[typed]): untyped = template callWritelnHook(args: varargs[string, `$`]) = conf.writelnHook concat(args) +proc msgWrite(conf: ConfigRef; s: string) = + if conf.m.errorOutputs != {}: + let stdOrr = + if optStdout in conf.globalOptions: + stdout + else: + stderr + write(stdOrr, s) + flushFile(stdOrr) + template styledMsgWriteln*(args: varargs[typed]) = if not isNil(conf.writelnHook): callIgnoringStyle(callWritelnHook, nil, args) elif optStdout in conf.globalOptions: if eStdOut in conf.m.errorOutputs: + flushDot(stdout) callIgnoringStyle(writeLine, stdout, args) flushFile(stdout) else: if eStdErr in conf.m.errorOutputs: + flushDot(stderr) if optUseColors in conf.globalOptions: callStyledWriteLineStderr(args) else: @@ -450,11 +468,18 @@ proc rawMessage*(conf: ConfigRef; msg: TMsgKind, args: openArray[string]) = s & (if kind.len > 0: KindFormat % kind else: ""), sev) if not ignoreMsgBecauseOfIdeTools(conf, msg): - if kind.len > 0: - styledMsgWriteln(color, title, resetStyle, s, - KindColor, `%`(KindFormat, kind)) + if msg == hintProcessing: + msgWrite(conf, ".") + conf.lastMsgWasDot = true else: - styledMsgWriteln(color, title, resetStyle, s) + if conf.lastMsgWasDot: + msgWrite(conf, "\n") + conf.lastMsgWasDot = false + if kind.len > 0: + styledMsgWriteln(color, title, resetStyle, s, + KindColor, `%`(KindFormat, kind)) + else: + styledMsgWriteln(color, title, resetStyle, s) handleError(conf, msg, doAbort, s) proc rawMessage*(conf: ConfigRef; msg: TMsgKind, arg: string) = @@ -539,7 +564,10 @@ proc liMessage(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string, if conf.hasHint(hintSource): conf.writeSurroundingSrc(info) if conf.hasHint(hintMsgOrigin): - styledMsgWriteln(styleBright, toFileLineCol(info2), resetStyle, " compiler msg initiated here", KindColor, KindFormat % HintsToStr[ord(hintMsgOrigin) - ord(hintMin)], resetStyle) + styledMsgWriteln(styleBright, toFileLineCol(info2), resetStyle, + " compiler msg initiated here", KindColor, + KindFormat % HintsToStr[ord(hintMsgOrigin) - ord(hintMin)], + resetStyle) handleError(conf, msg, eh, s) diff --git a/compiler/options.nim b/compiler/options.nim index 194d635b1..1418fff63 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -279,6 +279,7 @@ type projectPath*: AbsoluteDir # holds a path like /home/alice/projects/nim/compiler/ projectFull*: AbsoluteFile # projectPath/projectName projectIsStdin*: bool # whether we're compiling from stdin + lastMsgWasDot*: bool # the last compiler message was a single '.' projectMainIdx*: FileIndex # the canonical path id of the main module command*: string # the main command (e.g. cc, check, scan, etc) commandArgs*: seq[string] # any arguments after the main command diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 077b4b269..401d160fa 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -742,7 +742,7 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType = addDecl(c, param) var - oldWriteHook: type(m.c.config.writelnHook) + oldWriteHook: typeof(m.c.config.writelnHook) diagnostics: seq[string] errorPrefix: string flags: TExprFlags = {} diff --git a/tests/pragmas/twarning_off.nim b/tests/pragmas/twarning_off.nim index f1de9076f..d5d31d4c2 100644 --- a/tests/pragmas/twarning_off.nim +++ b/tests/pragmas/twarning_off.nim @@ -1,8 +1,7 @@ discard """ nimout: ''' compile start -Hint: warn_module [Processing] -Hint: hashes [Processing] +.. warn_module.nim(6, 6) Hint: 'test' is declared but not used [XDeclaredButNotUsed] compile end ''' From 3eaa870c0a3ac0a97c1726278412141140d35b9b Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 21 May 2020 22:38:13 +0200 Subject: [PATCH 0086/3768] ARC/ORC: optimize s.setLen(0) to match the old runtime's behaviour (#14423) --- lib/system/strs_v2.nim | 3 +-- tests/destructor/tgcdestructors.nim | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/system/strs_v2.nim b/lib/system/strs_v2.nim index 3beb14382..aa644522f 100644 --- a/lib/system/strs_v2.nim +++ b/lib/system/strs_v2.nim @@ -107,8 +107,7 @@ proc mnewString(len: int): NimStringV2 {.compilerproc.} = proc setLengthStrV2(s: var NimStringV2, newLen: int) {.compilerRtl.} = if newLen == 0: - frees(s) - s.p = nil + discard "do not free the buffer here, pattern 's.setLen 0' is common for avoiding allocations" else: if newLen > s.len or isLiteral(s): prepareAdd(s, newLen - s.len) diff --git a/tests/destructor/tgcdestructors.nim b/tests/destructor/tgcdestructors.nim index e089aedd1..61d296d2e 100644 --- a/tests/destructor/tgcdestructors.nim +++ b/tests/destructor/tgcdestructors.nim @@ -10,7 +10,7 @@ a: @[4, 2, 3] 0 30 true -(allocCount: 40, deallocCount: 40)''' +(allocCount: 36, deallocCount: 36)''' """ include system / ansi_c From 149251cab96741dfd2dd6edd4715589ba92b8480 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 21 May 2020 23:28:47 -0700 Subject: [PATCH 0087/3768] [cleanup] fix UnusedImport sempass2 compiler/semparallel.nim (#14426) --- compiler/semparallel.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/semparallel.nim b/compiler/semparallel.nim index e27e85e04..578687e6c 100644 --- a/compiler/semparallel.nim +++ b/compiler/semparallel.nim @@ -22,7 +22,7 @@ # - output slices need special logic (+) import - ast, astalgo, idents, lowerings, magicsys, guards, sempass2, msgs, + ast, astalgo, idents, lowerings, magicsys, guards, msgs, renderer, types, modulegraphs, options, spawn, lineinfos from trees import getMagic, isTrue, getRoot From f7b73e6bfd45ed7c1f09defc8e896137d22e7f17 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 22 May 2020 09:55:10 +0200 Subject: [PATCH 0088/3768] make malloc.nim consistent in style (#14427) --- lib/system/mm/malloc.nim | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/system/mm/malloc.nim b/lib/system/mm/malloc.nim index 883baaeb4..3dad98e93 100644 --- a/lib/system/mm/malloc.nim +++ b/lib/system/mm/malloc.nim @@ -7,13 +7,13 @@ proc allocImpl(size: Natural): pointer = proc alloc0Impl(size: Natural): pointer = c_calloc(size.csize_t, 1) -proc reallocImpl(p: pointer, newsize: Natural): pointer = +proc reallocImpl(p: pointer, newSize: Natural): pointer = c_realloc(p, newSize.csize_t) -proc realloc0Impl(p: pointer, oldsize, newsize: Natural): pointer = - result = realloc(p, newsize.csize_t) - if newsize > oldsize: - zeroMem(cast[pointer](cast[int](result) + oldsize), newsize - oldsize) +proc realloc0Impl(p: pointer, oldsize, newSize: Natural): pointer = + result = realloc(p, newSize.csize_t) + if newSize > oldSize: + zeroMem(cast[pointer](cast[int](result) + oldSize), newSize - oldSize) proc deallocImpl(p: pointer) = c_free(p) @@ -27,11 +27,11 @@ proc allocSharedImpl(size: Natural): pointer = proc allocShared0Impl(size: Natural): pointer = alloc0Impl(size) -proc reallocSharedImpl(p: pointer, newsize: Natural): pointer = - reallocImpl(p, newsize) +proc reallocSharedImpl(p: pointer, newSize: Natural): pointer = + reallocImpl(p, newSize) -proc reallocShared0Impl(p: pointer, oldsize, newsize: Natural): pointer = - realloc0Impl(p, oldsize, newsize) +proc reallocShared0Impl(p: pointer, oldsize, newSize: Natural): pointer = + realloc0Impl(p, oldSize, newSize) proc deallocSharedImpl(p: pointer) = deallocImpl(p) From 0d0ea3a11ef0bb7d0d2f5dbd84782dee12810c2c Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 22 May 2020 03:02:10 -0700 Subject: [PATCH 0089/3768] no more code duplication bw liMessage and rawMessage + several bug fixes (#14415) * no more code duplication bw rawMessage and liMessage; fixes some bugs in rawMessage * all compiler messages work with hint:msgorigin --- compiler/lineinfos.nim | 6 ++ compiler/main.nim | 6 +- compiler/modules.nim | 2 +- compiler/msgs.nim | 222 +++++++++++++++-------------------------- compiler/semdata.nim | 3 +- compiler/syntaxes.nim | 4 +- 6 files changed, 91 insertions(+), 152 deletions(-) diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index 331268d6a..e1e0a4bc8 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -181,6 +181,12 @@ const hintMin* = hintSuccess hintMax* = high(TMsgKind) +proc msgToStr*(msg: TMsgKind): string = + case msg + of warnMin..warnMax: WarningsToStr[ord(msg) - ord(warnMin)] + of hintMin..hintMax: HintsToStr[ord(msg) - ord(hintMin)] + else: "" # we could at least do $msg - prefix `err` + static: doAssert HintsToStr.len == ord(hintMax) - ord(hintMin) + 1 doAssert WarningsToStr.len == ord(warnMax) - ord(warnMin) + 1 diff --git a/compiler/main.nim b/compiler/main.nim index 21c0695f6..88f1890de 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -345,12 +345,10 @@ proc mainCommand*(graph: ModuleGraph) = var hints = newJObject() # consider factoring with `listHints` for a in hintMin..hintMax: - let key = lineinfos.HintsToStr[ord(a) - ord(hintMin)] - hints[key] = %(a in conf.notes) + hints[a.msgToStr] = %(a in conf.notes) var warnings = newJObject() for a in warnMin..warnMax: - let key = lineinfos.WarningsToStr[ord(a) - ord(warnMin)] - warnings[key] = %(a in conf.notes) + warnings[a.msgToStr] = %(a in conf.notes) var dumpdata = %[ (key: "version", val: %VersionAsString), diff --git a/compiler/modules.nim b/compiler/modules.nim index 47d946a01..d6e82d694 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -125,7 +125,7 @@ proc compileSystemModule*(graph: ModuleGraph) = proc wantMainModule*(conf: ConfigRef) = if conf.projectFull.isEmpty: - fatal(conf, newLineInfo(conf, AbsoluteFile"command line", 1, 1), errGenerated, + fatal(conf, newLineInfo(conf, AbsoluteFile(commandLineDesc), 1, 1), errGenerated, "command expects a filename") conf.projectMainIdx = fileInfoIdx(conf, addFileExt(conf.projectFull, NimExt)) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 7523d2d8b..6bf4d82d2 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -13,6 +13,13 @@ import import std/private/miscdollars type InstantiationInfo = typeof(instantiationInfo()) +template instLoc(): InstantiationInfo = instantiationInfo(-2, fullPaths = true) + +template flushDot(conf, stdorr) = + ## safe to call multiple times + if conf.lastMsgWasDot: + conf.lastMsgWasDot = false + write(stdorr, "\n") proc toCChar*(c: char; result: var string) = case c @@ -37,7 +44,6 @@ proc makeCString*(s: string): Rope = res.add('\"') result.add(rope(res)) - proc newFileInfo(fullPath: AbsoluteFile, projPath: RelativeFile): TFileInfo = result.fullPath = fullPath #shallow(result.fullPath) @@ -111,7 +117,6 @@ proc newLineInfo*(fileInfoIdx: FileIndex, line, col: int): TLineInfo = proc newLineInfo*(conf: ConfigRef; filename: AbsoluteFile, line, col: int): TLineInfo {.inline.} = result = newLineInfo(fileInfoIdx(conf, filename), line, col) - proc concat(strings: openArray[string]): string = var totalLen = 0 for s in strings: totalLen += s.len @@ -147,6 +152,7 @@ const # but column numbers start with 0, however most editors expect # first column to be 1, so we need to +1 here ColOffset* = 1 + commandLineDesc* = "command line" proc getInfoContextLen*(conf: ConfigRef): int = return conf.m.msgContext.len proc setInfoContextLen*(conf: ConfigRef; L: int) = setLen(conf.m.msgContext, L) @@ -162,9 +168,6 @@ proc getInfoContext*(conf: ConfigRef; index: int): TLineInfo = if i >=% conf.m.msgContext.len: result = unknownLineInfo else: result = conf.m.msgContext[i].info -const - commandLineDesc = "command line" - template toFilename*(conf: ConfigRef; fileIdx: FileIndex): string = if fileIdx.int32 < 0 or conf == nil: (if fileIdx == commandLineIdx: commandLineDesc else: "???") @@ -277,11 +280,6 @@ type msgSkipHook ## skip message hook even if it is present MsgFlags* = set[MsgFlag] -template flushDot(stdorr) = - if conf.lastMsgWasDot: - write(stdorr, "\n") - conf.lastMsgWasDot = false - proc msgWriteln*(conf: ConfigRef; s: string, flags: MsgFlags = {}) = ## Writes given message string to stderr by default. ## If ``--stdout`` option is given, writes to stdout instead. If message hook @@ -295,12 +293,12 @@ proc msgWriteln*(conf: ConfigRef; s: string, flags: MsgFlags = {}) = conf.writelnHook(s) elif optStdout in conf.globalOptions or msgStdout in flags: if eStdOut in conf.m.errorOutputs: - flushDot(stdout) + flushDot(conf, stdout) writeLine(stdout, s) flushFile(stdout) else: if eStdErr in conf.m.errorOutputs: - flushDot(stderr) + flushDot(conf, stderr) writeLine(stderr, s) # On Windows stderr is fully-buffered when piped, regardless of C std. when defined(windows): @@ -345,25 +343,25 @@ proc msgWrite(conf: ConfigRef; s: string) = stderr write(stdOrr, s) flushFile(stdOrr) + conf.lastMsgWasDot = true # subsequent writes need `flushDot` template styledMsgWriteln*(args: varargs[typed]) = if not isNil(conf.writelnHook): callIgnoringStyle(callWritelnHook, nil, args) elif optStdout in conf.globalOptions: if eStdOut in conf.m.errorOutputs: - flushDot(stdout) + flushDot(conf, stdout) callIgnoringStyle(writeLine, stdout, args) flushFile(stdout) - else: - if eStdErr in conf.m.errorOutputs: - flushDot(stderr) - if optUseColors in conf.globalOptions: - callStyledWriteLineStderr(args) - else: - callIgnoringStyle(writeLine, stderr, args) - # On Windows stderr is fully-buffered when piped, regardless of C std. - when defined(windows): - flushFile(stderr) + elif eStdErr in conf.m.errorOutputs: + flushDot(conf, stderr) + if optUseColors in conf.globalOptions: + callStyledWriteLineStderr(args) + else: + callIgnoringStyle(writeLine, stderr, args) + # On Windows stderr is fully-buffered when piped, regardless of C std. + when defined(windows): + flushFile(stderr) proc msgKindToString*(kind: TMsgKind): string = # later versions may provide translated error messages @@ -434,57 +432,6 @@ proc writeContext(conf: ConfigRef; lastinfo: TLineInfo) = proc ignoreMsgBecauseOfIdeTools(conf: ConfigRef; msg: TMsgKind): bool = msg >= errGenerated and conf.cmd == cmdIdeTools and optIdeDebug notin conf.globalOptions -proc rawMessage*(conf: ConfigRef; msg: TMsgKind, args: openArray[string]) = - var - title: string - color: ForegroundColor - kind: string - sev: Severity - case msg - of errMin..errMax: - sev = Severity.Error - writeContext(conf, unknownLineInfo) - title = ErrorTitle - color = ErrorColor - of warnMin..warnMax: - sev = Severity.Warning - if not conf.hasWarn(msg): return - writeContext(conf, unknownLineInfo) - title = if msg in conf.warningAsErrors: ErrorTitle else: WarningTitle - color = WarningColor - kind = WarningsToStr[ord(msg) - ord(warnMin)] - inc(conf.warnCounter) - of hintMin..hintMax: - sev = Severity.Hint - if not conf.hasHint(msg): return - title = HintTitle - color = HintColor - if msg != hintUserRaw: kind = HintsToStr[ord(msg) - ord(hintMin)] - inc(conf.hintCounter) - let s = msgKindToString(msg) % args - - if conf.structuredErrorHook != nil: - conf.structuredErrorHook(conf, unknownLineInfo, - s & (if kind.len > 0: KindFormat % kind else: ""), sev) - - if not ignoreMsgBecauseOfIdeTools(conf, msg): - if msg == hintProcessing: - msgWrite(conf, ".") - conf.lastMsgWasDot = true - else: - if conf.lastMsgWasDot: - msgWrite(conf, "\n") - conf.lastMsgWasDot = false - if kind.len > 0: - styledMsgWriteln(color, title, resetStyle, s, - KindColor, `%`(KindFormat, kind)) - else: - styledMsgWriteln(color, title, resetStyle, s) - handleError(conf, msg, doAbort, s) - -proc rawMessage*(conf: ConfigRef; msg: TMsgKind, arg: string) = - rawMessage(conf, msg, [arg]) - proc addSourceLine(conf: ConfigRef; fileIdx: FileIndex, line: string) = conf.m.fileInfos[fileIdx.int32].lines.add line @@ -517,108 +464,111 @@ proc formatMsg*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string): s conf.toFileLineCol(info) & " " & title & getMessageStr(msg, arg) proc liMessage(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string, - eh: TErrorHandling, info2: InstantiationInfo) {.noinline.} = + eh: TErrorHandling, info2: InstantiationInfo, isRaw = false) {.noinline.} = var title: string color: ForegroundColor - kind: string ignoreMsg = false sev: Severity + let kind = if msg != hintUserRaw: msg.msgToStr else: "" # xxx not sure why hintUserRaw is special case msg of errMin..errMax: sev = Severity.Error writeContext(conf, info) title = ErrorTitle color = ErrorColor - # we try to filter error messages so that not two error message - # in the same file and line are produced: - #ignoreMsg = lastError == info and eh != doAbort - conf.m.lastError = info + when false: + # we try to filter error messages so that not two error message + # in the same file and line are produced: + # xxx `lastError` is only used in this disabled code; but could be useful to revive + ignoreMsg = conf.m.lastError == info and info != unknownLineInfo and eh != doAbort + if info != unknownLineInfo: conf.m.lastError = info of warnMin..warnMax: sev = Severity.Warning ignoreMsg = not conf.hasWarn(msg) if not ignoreMsg: writeContext(conf, info) title = if msg in conf.warningAsErrors: ErrorTitle else: WarningTitle color = WarningColor - kind = WarningsToStr[ord(msg) - ord(warnMin)] inc(conf.warnCounter) of hintMin..hintMax: sev = Severity.Hint ignoreMsg = not conf.hasHint(msg) title = HintTitle color = HintColor - if msg != hintUserRaw: kind = HintsToStr[ord(msg) - ord(hintMin)] inc(conf.hintCounter) - let x = conf.toFileLineCol(info) & " " - let s = getMessageStr(msg, arg) + let s = if isRaw: arg else: getMessageStr(msg, arg) if not ignoreMsg: + let loc = if info != unknownLineInfo: conf.toFileLineCol(info) & " " else: "" + var kindmsg = if kind.len > 0: KindFormat % kind else: "" if conf.structuredErrorHook != nil: - conf.structuredErrorHook(conf, info, s & (if kind.len > 0: KindFormat % kind else: ""), sev) + conf.structuredErrorHook(conf, info, s & kindmsg, sev) if not ignoreMsgBecauseOfIdeTools(conf, msg): - if kind.len > 0: - styledMsgWriteln(styleBright, x, resetStyle, color, title, resetStyle, s, - KindColor, `%`(KindFormat, kind)) + if msg == hintProcessing: + msgWrite(conf, ".") else: - styledMsgWriteln(styleBright, x, resetStyle, color, title, resetStyle, s) - if conf.hasHint(hintSource): - conf.writeSurroundingSrc(info) - if conf.hasHint(hintMsgOrigin): - styledMsgWriteln(styleBright, toFileLineCol(info2), resetStyle, - " compiler msg initiated here", KindColor, - KindFormat % HintsToStr[ord(hintMsgOrigin) - ord(hintMin)], - resetStyle) - + styledMsgWriteln(styleBright, loc, resetStyle, color, title, resetStyle, s, KindColor, kindmsg) + if conf.hasHint(hintSource) and info != unknownLineInfo: + conf.writeSurroundingSrc(info) + if conf.hasHint(hintMsgOrigin): + styledMsgWriteln(styleBright, toFileLineCol(info2), resetStyle, + " compiler msg initiated here", KindColor, + KindFormat % hintMsgOrigin.msgToStr, + resetStyle) handleError(conf, msg, eh, s) +template rawMessage*(conf: ConfigRef; msg: TMsgKind, args: openArray[string]) = + let arg = msgKindToString(msg) % args + liMessage(conf, unknownLineInfo, msg, arg, eh = doAbort, instLoc(), isRaw = true) + +template rawMessage*(conf: ConfigRef; msg: TMsgKind, arg: string) = + liMessage(conf, unknownLineInfo, msg, arg, eh = doAbort, instLoc()) + template fatal*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") = - # this fixes bug #7080 so that it is at least obvious 'fatal' - # was executed. + # this fixes bug #7080 so that it is at least obvious 'fatal' was executed. conf.m.errorOutputs = {eStdOut, eStdErr} - const info2 = instantiationInfo(-1, fullPaths = true) - liMessage(conf, info, msg, arg, doAbort, info2) + liMessage(conf, info, msg, arg, doAbort, instLoc()) template globalError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") = - const info2 = instantiationInfo(-1, fullPaths = true) - liMessage(conf, info, msg, arg, doRaise, info2) + liMessage(conf, info, msg, arg, doRaise, instLoc()) template globalError*(conf: ConfigRef; info: TLineInfo, arg: string) = - const info2 = instantiationInfo(-1, fullPaths = true) - liMessage(conf, info, errGenerated, arg, doRaise, info2) + liMessage(conf, info, errGenerated, arg, doRaise, instLoc()) template localError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") = - const info2 = instantiationInfo(-1, fullPaths = true) - liMessage(conf, info, msg, arg, doNothing, info2) + liMessage(conf, info, msg, arg, doNothing, instLoc()) template localError*(conf: ConfigRef; info: TLineInfo, arg: string) = - const info2 = instantiationInfo(-1, fullPaths = true) - liMessage(conf, info, errGenerated, arg, doNothing, info2) + liMessage(conf, info, errGenerated, arg, doNothing, instLoc()) template localError*(conf: ConfigRef; info: TLineInfo, format: string, params: openArray[string]) = - const info2 = instantiationInfo(-1, fullPaths = true) - liMessage(conf, info, errGenerated, format % params, doNothing, info2) + liMessage(conf, info, errGenerated, format % params, doNothing, instLoc()) template message*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") = - const info2 = instantiationInfo(-1, fullPaths = true) - liMessage(conf, info, msg, arg, doNothing, info2) + liMessage(conf, info, msg, arg, doNothing, instLoc()) -proc internalError*(conf: ConfigRef; info: TLineInfo, errMsg: string) = +proc internalErrorImpl(conf: ConfigRef; info: TLineInfo, errMsg: string, info2: InstantiationInfo) = if conf.cmd == cmdIdeTools and conf.structuredErrorHook.isNil: return - const info2 = instantiationInfo(-1, fullPaths = true) writeContext(conf, info) liMessage(conf, info, errInternal, errMsg, doAbort, info2) -proc internalError*(conf: ConfigRef; errMsg: string) = - if conf.cmd == cmdIdeTools and conf.structuredErrorHook.isNil: return - writeContext(conf, unknownLineInfo) - rawMessage(conf, errInternal, errMsg) +template internalError*(conf: ConfigRef; info: TLineInfo, errMsg: string) = + internalErrorImpl(conf, info, errMsg, instLoc()) -template assertNotNil*(conf: ConfigRef; e): untyped = - if e == nil: internalError(conf, $instantiationInfo()) - e +template internalError*(conf: ConfigRef; errMsg: string) = + internalErrorImpl(conf, unknownLineInfo, errMsg, instLoc()) template internalAssert*(conf: ConfigRef, e: bool) = - if not e: internalError(conf, $instantiationInfo()) + # xxx merge with globalAssert from PR #14324 + if not e: + const info2 = instLoc() + let arg = info2.toFileLineCol + internalErrorImpl(conf, unknownLineInfo, arg, info2) + +template lintReport*(conf: ConfigRef; info: TLineInfo, beau, got: string) = + let m = "'$2' should be: '$1'" % [beau, got] + let msg = if optStyleError in conf.globalOptions: errGenerated else: hintName + liMessage(conf, info, msg, m, doNothing, instLoc()) proc quotedFilename*(conf: ConfigRef; i: TLineInfo): Rope = if i.fileIndex.int32 < 0: @@ -628,25 +578,9 @@ proc quotedFilename*(conf: ConfigRef; i: TLineInfo): Rope = else: result = conf.m.fileInfos[i.fileIndex.int32].quotedName -proc listWarnings*(conf: ConfigRef) = - msgWriteln(conf, "Warnings:") - for warn in warnMin..warnMax: - msgWriteln(conf, " [$1] $2" % [ - if warn in conf.notes: "x" else: " ", - lineinfos.WarningsToStr[ord(warn) - ord(warnMin)] - ]) +template listMsg(title, r) = + msgWriteln(conf, title) + for a in r: msgWriteln(conf, " [$1] $2" % [if a in conf.notes: "x" else: " ", a.msgToStr]) -proc listHints*(conf: ConfigRef) = - msgWriteln(conf, "Hints:") - for hint in hintMin..hintMax: - msgWriteln(conf, " [$1] $2" % [ - if hint in conf.notes: "x" else: " ", - lineinfos.HintsToStr[ord(hint) - ord(hintMin)] - ]) - -proc lintReport*(conf: ConfigRef; info: TLineInfo, beau, got: string) = - let m = "'$2' should be: '$1'" % [beau, got] - if optStyleError in conf.globalOptions: - localError(conf, info, m) - else: - message(conf, info, hintName, m) +proc listWarnings*(conf: ConfigRef) = listMsg("Warnings:", warnMin..warnMax) +proc listHints*(conf: ConfigRef) = listMsg("Hints:", hintMin..hintMax) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 278384fc4..5a2c08de2 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -321,7 +321,8 @@ proc makeTypeDesc*(c: PContext, typ: PType): PType = proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode = let typedesc = newTypeS(tyTypeDesc, c) incl typedesc.flags, tfCheckedForDestructor - typedesc.addSonSkipIntLit(assertNotNil(c.config, typ)) + internalAssert(c.config, typ != nil) + typedesc.addSonSkipIntLit(typ) let sym = newSym(skType, c.cache.idAnon, getCurrOwner(c), info, c.config.options).linkTo(typedesc) return newSymNode(sym, info) diff --git a/compiler/syntaxes.nim b/compiler/syntaxes.nim index 29ccf4794..c3f3bc0dc 100644 --- a/compiler/syntaxes.nim +++ b/compiler/syntaxes.nim @@ -121,9 +121,9 @@ proc applyFilter(p: var TParsers, n: PNode, filename: AbsoluteFile, if f != filtNone: assert p.config != nil if p.config.hasHint(hintCodeBegin): - rawMessage(p.config, hintCodeBegin, []) + rawMessage(p.config, hintCodeBegin, "") msgWriteln(p.config, result.s) - rawMessage(p.config, hintCodeEnd, []) + rawMessage(p.config, hintCodeEnd, "") proc evalPipe(p: var TParsers, n: PNode, filename: AbsoluteFile, start: PLLStream): PLLStream = From 7b3dcfde20e2cebdf5902a01e71c695ff7f507e8 Mon Sep 17 00:00:00 2001 From: Mildred Ki'Lya Date: Fri, 22 May 2020 14:34:20 +0200 Subject: [PATCH 0090/3768] Add missing attributes and methods to JavaScript DOM (#14428) - content attribute to access HTML template element: https://developer.mozilla.org/en-US/docs/Web/API/HTMLTemplateElement/content - ownerDocument to access the owning document from a DOM Node: https://developer.mozilla.org/en-US/docs/Web/API/Node/ownerDocument - outerHTML to get/set the outer HTML representation of a Node: https://developer.mozilla.org/en-US/docs/Web/API/Node/ownerDocument - createComment() to create comment Nodes: https://developer.mozilla.org/en-US/docs/Web/API/Document/createComment - querySelector() and querySelectorAll() on Nodes: https://developer.mozilla.org/en-US/docs/Web/API/Element/querySelector https://developer.mozilla.org/en-US/docs/Web/API/Element/querySelectorAll --- lib/js/dom.nim | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/js/dom.nim b/lib/js/dom.nim index 950a5065b..d425b62c5 100644 --- a/lib/js/dom.nim +++ b/lib/js/dom.nim @@ -171,8 +171,11 @@ type nodeType*: NodeType nodeValue*: cstring parentNode*: Node + content*: Node previousSibling*: Node + ownerDocument*: Document innerHTML*: cstring + outerHTML*: cstring innerText*: cstring textContent*: cstring style*: Style @@ -1042,6 +1045,7 @@ when defined(nodejs): result = cast[Element](x.childNodes[idx]) var document* = Document(nodeType: DocumentNode) + document.ownerDocument = document proc getElem(x: Element; id: cstring): Element = if x.id == id: return x @@ -1055,6 +1059,7 @@ when defined(nodejs): proc appendChild*(parent, n: Node) = n.parentNode = parent + n.ownerDocument = parent.ownerDocument parent.childNodes.add n proc replaceChild*(parent, newNode, oldNode: Node) = @@ -1101,6 +1106,12 @@ when defined(nodejs): result.nodeValue = identifier result.nodeType = NodeType.TextNode + proc createComment*(d: Document, data: cstring): Node = + new(result) + result.nodeName = "#comment" + result.nodeValue = data + result.nodeType = NodeType.CommentNode + else: proc len*(x: Node): int {.importcpp: "#.childNodes.length".} proc `[]`*(x: Node; idx: int): Element {.importcpp: "#.childNodes[#]".} @@ -1112,6 +1123,7 @@ else: proc getElementById*(d: Document, id: cstring): Element {.importcpp.} proc createElement*(d: Document, identifier: cstring): Element {.importcpp.} proc createTextNode*(d: Document, identifier: cstring): Node {.importcpp.} + proc createComment*(d: Document, data: cstring): Node {.importcpp.} proc setTimeout*(action: proc(); ms: int): Timeout {.importc, nodecl.} proc clearTimeout*(t: Timeout) {.importc, nodecl.} @@ -1177,6 +1189,8 @@ proc replaceData*(n: Node, start, len: int, text: cstring) proc scrollIntoView*(n: Node) proc setAttribute*(n: Node, name, value: cstring) proc setAttributeNode*(n: Node, attr: Node) +proc querySelector*(n: Node, selectors: cstring): Element +proc querySelectorAll*(n: Node, selectors: cstring): seq[Element] # Document "methods" proc captureEvents*(d: Document, eventMask: int) {.deprecated.} From 6668b4340919fb021b2a607c22ae27f5dcf052f6 Mon Sep 17 00:00:00 2001 From: Bung <535670947@qq.com> Date: Sat, 23 May 2020 01:24:52 +0800 Subject: [PATCH 0091/3768] add insert,tryInsert unify for postgres that need pk name (#14416) * add insert,tryInsert unify for postgres that need pk name * add ReadDbEffect to new procs * add .since and changelog * change since to 1.3 * Update lib/impure/db_postgres.nim Co-authored-by: bung87 Co-authored-by: Andreas Rumpf Co-authored-by: alaviss --- changelog.md | 1 + lib/impure/db_mysql.nim | 15 +++++++++++++++ lib/impure/db_odbc.nim | 15 +++++++++++++++ lib/impure/db_postgres.nim | 22 ++++++++++++++++++++++ lib/impure/db_sqlite.nim | 15 +++++++++++++++ 5 files changed, 68 insertions(+) diff --git a/changelog.md b/changelog.md index 0e1af73c1..0d4fb6af9 100644 --- a/changelog.md +++ b/changelog.md @@ -3,6 +3,7 @@ ## Standard library additions and changes +- Add `tryInsert`,`insert` procs to db_* libs accept primary key column name. - Added `xmltree.newVerbatimText` support create `style`'s,`script`'s text. - `uri` adds Data URI Base64, implements RFC-2397. - Add [DOM Parser](https://developer.mozilla.org/en-US/docs/Web/API/DOMParser) diff --git a/lib/impure/db_mysql.nim b/lib/impure/db_mysql.nim index 510503a63..c96829830 100644 --- a/lib/impure/db_mysql.nim +++ b/lib/impure/db_mysql.nim @@ -88,6 +88,8 @@ import strutils, mysql import db_common export db_common +import std/private/since + type DbConn* = distinct PMySQL ## encapsulates a database connection Row* = seq[string] ## a row of a dataset. NULL database values will be @@ -359,6 +361,19 @@ proc insertId*(db: DbConn, query: SqlQuery, result = tryInsertID(db, query, args) if result < 0: dbError(db) +proc tryInsert*(db: DbConn, query: SqlQuery, pkName: string, + args: varargs[string, `$`]): int64 + {.tags: [WriteDbEffect], raises: [], since: (1, 3).} = + ## same as tryInsertID + tryInsertID(db, query, args) + +proc insert*(db: DbConn, query: SqlQuery, pkName: string, + args: varargs[string, `$`]): int64 + {.tags: [WriteDbEffect], since: (1, 3).} = + ## same as insertId + result = tryInsert(db, query,pkName, args) + if result < 0: dbError(db) + proc execAffectedRows*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): int64 {. tags: [ReadDbEffect, WriteDbEffect].} = diff --git a/lib/impure/db_odbc.nim b/lib/impure/db_odbc.nim index 9ff050bfa..863cf6c8c 100644 --- a/lib/impure/db_odbc.nim +++ b/lib/impure/db_odbc.nim @@ -92,6 +92,8 @@ import strutils, odbcsql import db_common export db_common +import std/private/since + type OdbcConnTyp = tuple[hDb: SqlHDBC, env: SqlHEnv, stmt: SqlHStmt] DbConn* = OdbcConnTyp ## encapsulates a database connection @@ -451,6 +453,19 @@ proc insertId*(db: var DbConn, query: SqlQuery, result = tryInsertID(db, query, args) if result < 0: dbError(db) +proc tryInsert*(db: var DbConn, query: SqlQuery,pkName: string, + args: varargs[string, `$`]): int64 + {.tags: [ReadDbEffect, WriteDbEffect], raises: [], since: (1, 3).} = + ## same as tryInsertID + tryInsertID(db, query, args) + +proc insert*(db: var DbConn, query: SqlQuery, pkName: string, + args: varargs[string, `$`]): int64 + {.tags: [ReadDbEffect, WriteDbEffect], since: (1, 3).} = + ## same as insertId + result = tryInsert(db, query,pkName, args) + if result < 0: dbError(db) + proc execAffectedRows*(db: var DbConn, query: SqlQuery, args: varargs[string, `$`]): int64 {. tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} = diff --git a/lib/impure/db_postgres.nim b/lib/impure/db_postgres.nim index 57c61fa23..9e8075450 100644 --- a/lib/impure/db_postgres.nim +++ b/lib/impure/db_postgres.nim @@ -68,6 +68,8 @@ import strutils, postgres import db_common export db_common +import std/private/since + type DbConn* = PPGconn ## encapsulates a database connection Row* = seq[string] ## a row of a dataset. NULL database values will be @@ -485,6 +487,26 @@ proc insertID*(db: DbConn, query: SqlQuery, result = tryInsertID(db, query, args) if result < 0: dbError(db) +proc tryInsert*(db: DbConn, query: SqlQuery,pkName: string, + args: varargs[string, `$`]): int64 + {.tags: [WriteDbEffect], since: (1, 3).}= + ## executes the query (typically "INSERT") and returns the + ## generated ID for the row or -1 in case of an error. + var x = pqgetvalue(setupQuery(db, SqlQuery(string(query) & " RETURNING " & pkName), + args), 0, 0) + if not isNil(x): + result = parseBiggestInt($x) + else: + result = -1 + +proc insert*(db: DbConn, query: SqlQuery, pkName: string, + args: varargs[string, `$`]): int64 + {.tags: [WriteDbEffect], since: (1, 3).} = + ## executes the query (typically "INSERT") and returns the + ## generated ID + result = tryInsertID(db, query, args) + if result < 0: dbError(db) + proc execAffectedRows*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): int64 {.tags: [ ReadDbEffect, WriteDbEffect].} = diff --git a/lib/impure/db_sqlite.nim b/lib/impure/db_sqlite.nim index c7ace0171..ff00dd86e 100644 --- a/lib/impure/db_sqlite.nim +++ b/lib/impure/db_sqlite.nim @@ -118,6 +118,8 @@ import sqlite3 import db_common export db_common +import std/private/since + type DbConn* = PSqlite3 ## Encapsulates a database connection. Row* = seq[string] ## A row of a dataset. `NULL` database values will be @@ -636,6 +638,19 @@ proc insertID*(db: DbConn, query: SqlQuery, result = tryInsertID(db, query, args) if result < 0: dbError(db) +proc tryInsert*(db: DbConn, query: SqlQuery, pkName: string, + args: varargs[string, `$`]): int64 + {.tags: [WriteDbEffect], raises: [], since: (1, 3).} = + ## same as tryInsertID + tryInsertID(db, query, args) + +proc insert*(db: DbConn, query: SqlQuery, pkName: string, + args: varargs[string, `$`]): int64 + {.tags: [WriteDbEffect], since: (1, 3).} = + ## same as insertId + result = tryInsert(db, query,pkName, args) + if result < 0: dbError(db) + proc execAffectedRows*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): int64 {. tags: [ReadDbEffect, WriteDbEffect].} = From c61f7466292cb19c910f4a2f2976f738796e6bdf Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 22 May 2020 20:24:37 +0200 Subject: [PATCH 0092/3768] avoid unsafe Nim features in preparation for --gc:arc (#14431) --- compiler/seminst.nim | 8 ++++---- compiler/semtypinst.nim | 25 +++++++++++++------------ 2 files changed, 17 insertions(+), 16 deletions(-) diff --git a/compiler/seminst.nim b/compiler/seminst.nim index fa338956d..1a7bd9b6b 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -178,13 +178,13 @@ proc instGenericContainer(c: PContext, info: TLineInfo, header: PType, internalAssert c.config, header.kind == tyGenericInvocation var - typeMap: LayeredIdTable cl: TReplTypeVars initIdTable(cl.symMap) initIdTable(cl.localCache) - initIdTable(typeMap.topLayer) - cl.typeMap = addr(typeMap) + cl.typeMap = LayeredIdTable() + initIdTable(cl.typeMap.topLayer) + cl.info = info cl.c = c cl.allowMetaTypes = allowMetaTypes @@ -244,7 +244,7 @@ proc instantiateProcType(c: PContext, pt: TIdTable, #addDecl(c, prc) pushInfoContext(c.config, info) var typeMap = initLayeredTypeMap(pt) - var cl = initTypeVars(c, addr(typeMap), info, nil) + var cl = initTypeVars(c, typeMap, info, nil) var result = instCopyType(cl, prc.typ) let originalParams = result.n result.n = originalParams.shallowCopy diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 08ea85708..257094289 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -68,13 +68,13 @@ proc cacheTypeInst*(inst: PType) = gt.sym.typeInstCache.add(inst) type - LayeredIdTable* = object + LayeredIdTable* = ref object topLayer*: TIdTable - nextLayer*: ptr LayeredIdTable + nextLayer*: LayeredIdTable TReplTypeVars* = object c*: PContext - typeMap*: ptr LayeredIdTable # map PType to PType + typeMap*: LayeredIdTable # map PType to PType symMap*: TIdTable # map PSym to PSym localCache*: TIdTable # local cache for remembering already replaced # types during instantiation of meta types @@ -92,20 +92,22 @@ proc replaceTypeVarsS(cl: var TReplTypeVars, s: PSym): PSym proc replaceTypeVarsN*(cl: var TReplTypeVars, n: PNode; start=0): PNode proc initLayeredTypeMap*(pt: TIdTable): LayeredIdTable = + result = LayeredIdTable() copyIdTable(result.topLayer, pt) proc newTypeMapLayer*(cl: var TReplTypeVars): LayeredIdTable = + result = LayeredIdTable() result.nextLayer = cl.typeMap initIdTable(result.topLayer) -proc lookup(typeMap: ptr LayeredIdTable, key: PType): PType = +proc lookup(typeMap: LayeredIdTable, key: PType): PType = var tm = typeMap while tm != nil: result = PType(idTableGet(tm.topLayer, key)) if result != nil: return tm = tm.nextLayer -template put(typeMap: ptr LayeredIdTable, key, value: PType) = +template put(typeMap: LayeredIdTable, key, value: PType) = idTablePut(typeMap.topLayer, key, value) template checkMetaInvariants(cl: TReplTypeVars, t: PType) = @@ -369,8 +371,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = let oldSkipTypedesc = cl.skipTypedesc cl.skipTypedesc = true - var typeMapLayer = newTypeMapLayer(cl) - cl.typeMap = addr(typeMapLayer) + cl.typeMap = newTypeMapLayer(cl) for i in 1.. int ----> proc (x: int = 0) var typeMap = initLayeredTypeMap(pt) - var cl = initTypeVars(p, addr(typeMap), info, nil) + var cl = initTypeVars(p, typeMap, info, nil) pushInfoContext(p.config, info) result = replaceTypeVarsT(cl, t) popInfoContext(p.config) @@ -714,7 +715,7 @@ proc generateTypeInstance*(p: PContext, pt: TIdTable, info: TLineInfo, proc prepareMetatypeForSigmatch*(p: PContext, pt: TIdTable, info: TLineInfo, t: PType): PType = var typeMap = initLayeredTypeMap(pt) - var cl = initTypeVars(p, addr(typeMap), info, nil) + var cl = initTypeVars(p, typeMap, info, nil) cl.allowMetaTypes = true pushInfoContext(p.config, info) result = replaceTypeVarsT(cl, t) From b012205b2e1a78b3cf336934f289891477b8c414 Mon Sep 17 00:00:00 2001 From: genotrance Date: Sat, 23 May 2020 11:06:04 -0500 Subject: [PATCH 0093/3768] Fix #14057 - moveFile should overwrite on Windows (#14433) --- lib/pure/os.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 449ae1d84..fbd722863 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -1801,9 +1801,9 @@ proc tryMoveFSObject(source, dest: string): bool {.noNimScript.} = when useWinUnicode: let s = newWideCString(source) let d = newWideCString(dest) - if moveFileExW(s, d, MOVEFILE_COPY_ALLOWED) == 0'i32: raiseOSError(osLastError(), $(source, dest)) + if moveFileExW(s, d, MOVEFILE_COPY_ALLOWED or MOVEFILE_REPLACE_EXISTING) == 0'i32: raiseOSError(osLastError(), $(source, dest)) else: - if moveFileExA(source, dest, MOVEFILE_COPY_ALLOWED) == 0'i32: raiseOSError(osLastError(), $(source, dest)) + if moveFileExA(source, dest, MOVEFILE_COPY_ALLOWED or MOVEFILE_REPLACE_EXISTING) == 0'i32: raiseOSError(osLastError(), $(source, dest)) else: if c_rename(source, dest) != 0'i32: let err = osLastError() From f70d01a7d512ef305cb457db71b35995b3720a1d Mon Sep 17 00:00:00 2001 From: hlaaftana <10591326+hlaaftana@users.noreply.github.com> Date: Sun, 24 May 2020 07:26:49 +0300 Subject: [PATCH 0094/3768] fix repr(char) example assert (#14437) --- lib/system/repr_v2.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/system/repr_v2.nim b/lib/system/repr_v2.nim index be1a4cf3a..fcc187a42 100644 --- a/lib/system/repr_v2.nim +++ b/lib/system/repr_v2.nim @@ -32,7 +32,7 @@ proc repr*(x: char): string {.noSideEffect.} = ## converted to a string. ## ## .. code-block:: Nim - ## assert repr('c') == "c" + ## assert repr('c') == "'c'" '\'' & $x & '\'' proc repr*(x: cstring): string {.noSideEffect.} = From acae3b02c7d3b7d916430223c5a2948e68d83ea4 Mon Sep 17 00:00:00 2001 From: treeform Date: Sat, 23 May 2020 22:32:24 -0700 Subject: [PATCH 0095/3768] Add clipPath to dom.nim. (#14435) Just adds a single new css atribute, see: https://developer.mozilla.org/en-US/docs/Web/CSS/clip-path . --- lib/js/dom.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/js/dom.nim b/lib/js/dom.nim index d425b62c5..65f9a946b 100644 --- a/lib/js/dom.nim +++ b/lib/js/dom.nim @@ -398,6 +398,7 @@ type captionSide*: cstring clear*: cstring clip*: cstring + clipPath*: cstring color*: cstring cursor*: cstring direction*: cstring @@ -1384,4 +1385,4 @@ since (1, 3): proc readAsDataURL*(f: FileReader, b: Blob) {.importcpp: "#.readAsDataURL(#)".} ## https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsDataURL proc readAsText*(f: FileReader, b: Blob, encoding = cstring"UTF-8") {.importcpp: "#.readAsText(#, #)".} - ## https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsText \ No newline at end of file + ## https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsText From a008f591e2b318c2c9e12b8923210435d22e8280 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 25 May 2020 02:17:25 -0700 Subject: [PATCH 0096/3768] add test for `define`, `undef` (#14443) --- tests/pragmas/tpragmas_misc.nim | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 tests/pragmas/tpragmas_misc.nim diff --git a/tests/pragmas/tpragmas_misc.nim b/tests/pragmas/tpragmas_misc.nim new file mode 100644 index 000000000..247fa471e --- /dev/null +++ b/tests/pragmas/tpragmas_misc.nim @@ -0,0 +1,12 @@ +##[ +tests for misc pragmas that don't need a separate file +]## + +block: + static: doAssert not defined(tpragmas_misc_def) + {.undef(tpragmas_misc_def).} # works even if not set + static: doAssert not defined(tpragmas_misc_def) + {.define(tpragmas_misc_def).} + static: doAssert defined(tpragmas_misc_def) + {.undef(tpragmas_misc_def).} + static: doAssert not defined(tpragmas_misc_def) From 79e85cb0b55437d8a5990f66c760fb98998de372 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 25 May 2020 12:16:32 +0200 Subject: [PATCH 0097/3768] manual.rst: updates [backport] (#14445) --- doc/manual.rst | 47 ++++++++++++++++++++++++----------------------- 1 file changed, 24 insertions(+), 23 deletions(-) diff --git a/doc/manual.rst b/doc/manual.rst index 8b27c7819..2c39b3e73 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -1802,7 +1802,7 @@ Mixing GC'ed memory with ``ptr`` Special care has to be taken if an untraced object contains traced objects like traced references, strings or sequences: in order to free everything properly, -the built-in procedure ``GCunref`` has to be called before freeing the untraced +the built-in procedure ``reset`` has to be called before freeing the untraced memory manually: .. code-block:: nim @@ -1816,12 +1816,12 @@ memory manually: d.s = "abc" # tell the GC that the string is not needed anymore: - GCunref(d.s) + reset(d.s) # free the memory: dealloc(d) -Without the ``GCunref`` call the memory allocated for the ``d.s`` string would +Without the ``reset`` call the memory allocated for the ``d.s`` string would never be freed. The example also demonstrates two important features for low level programming: the ``sizeof`` proc returns the size of a type or value in bytes. The ``cast`` operator can circumvent the type system: the compiler @@ -2336,7 +2336,7 @@ The convertible relation can be relaxed by a user-defined type # implicit conversion magic happens here x = chr echo x # => 97 - # you can use the explicit form too + # one can use the explicit form too x = chr.toInt echo x # => 97 @@ -4280,9 +4280,10 @@ caught by reference. Example: fn() -**Note:** `getCurrentException()` and `getCurrentExceptionMsg()` are not available -for imported exceptions. You need to use `except ImportedException as x:` syntax -and rely on functionality of `x` object to get exception details. +**Note:** `getCurrentException()` and `getCurrentExceptionMsg()` are not available +for imported exceptions. One needs to use the `except ImportedException as x:` syntax +and rely on functionality of the `x` object to get exception details. + Effect system ============= @@ -4885,7 +4886,7 @@ compiler. Explicit immediate templates are now deprecated. Passing a code block to a template ---------------------------------- -You can pass a block of statements as the last argument to a template +One can pass a block of statements as the last argument to a template following the special ``:`` syntax: .. code-block:: nim @@ -5425,7 +5426,7 @@ generic type ``static[T]``. The type param can be omitted to obtain the type class of all constant expressions. A more specific type class can be created by instantiating ``static`` with another type class. -You can force an expression to be evaluated at compile time as a constant +One can force an expression to be evaluated at compile time as a constant expression by coercing it to a corresponding ``static`` type: .. code-block:: nim @@ -5439,14 +5440,14 @@ possible type mismatch error. typedesc[T] ----------- -In many contexts, Nim allows you to treat the names of types as regular +In many contexts, Nim allows to treat the names of types as regular values. These values exists only during the compilation phase, but since all values must have a type, ``typedesc`` is considered their special type. ``typedesc`` acts like a generic type. For instance, the type of the symbol ``int`` is ``typedesc[int]``. Just like with regular generic types, when the generic param is omitted, ``typedesc`` denotes the type class of all types. -As a syntactic convenience, you can also use ``typedesc`` as a modifier. +As a syntactic convenience, one can also use ``typedesc`` as a modifier. Procs featuring ``typedesc`` params are considered implicitly generic. They will be instantiated for each unique combination of supplied types @@ -5524,7 +5525,7 @@ typeof operator **Note**: ``typeof(x)`` can for historical reasons also be written as ``type(x)`` but ``type(x)`` is discouraged. -You can obtain the type of a given expression by constructing a ``typeof`` +One can obtain the type of a given expression by constructing a ``typeof`` value from it (in many other languages this is known as the `typeof`:idx: operator): @@ -5751,9 +5752,9 @@ modules don't need to import a module's dependencies: echo $x When the exported symbol is another module, all of its definitions will -be forwarded. You can use an ``except`` list to exclude some of the symbols. +be forwarded. One can use an ``except`` list to exclude some of the symbols. -Notice that when exporting, you need to specify only the module name: +Notice that when exporting, one needs to specify only the module name: .. code-block:: nim import foo/bar/baz @@ -6480,7 +6481,7 @@ with the project: {.compile: "myfile.cpp".} **Note**: Nim computes a SHA1 checksum and only recompiles the file if it -has changed. You can use the ``-f`` command line option to force recompilation +has changed. One can use the ``-f`` command line option to force recompilation of the file. @@ -6495,12 +6496,12 @@ The ``link`` pragma can be used to link an additional file with the project: PassC pragma ------------ The ``passc`` pragma can be used to pass additional parameters to the C -compiler like you would using the commandline switch ``--passc``: +compiler like one would using the commandline switch ``--passc``: .. code-block:: Nim {.passc: "-Wall -Werror".} -Note that you can use ``gorge`` from the `system module `_ to +Note that one can use ``gorge`` from the `system module `_ to embed parameters from an external command that will be executed during semantic analysis: @@ -6523,12 +6524,12 @@ the pragma resides in: PassL pragma ------------ The ``passL`` pragma can be used to pass additional parameters to the linker -like you would using the commandline switch ``--passL``: +like one would using the commandline switch ``--passL``: .. code-block:: Nim {.passL: "-lSDLmain -lSDL".} -Note that you can use ``gorge`` from the `system module `_ to +Note that one can use ``gorge`` from the `system module `_ to embed parameters from an external command that will be executed during semantic analysis: @@ -6783,7 +6784,7 @@ Importcpp for objects ~~~~~~~~~~~~~~~~~~~~~ Generic ``importcpp``'ed objects are mapped to C++ templates. This means that -you can import C++'s templates rather easily without the need for a pattern +one can import C++'s templates rather easily without the need for a pattern language for object types: .. code-block:: nim @@ -7032,9 +7033,9 @@ specified. It is possible to annotate procs, templates, type and variable definitions, statements, etc. Macros module includes helpers which can be used to simplify custom pragma -access `hasCustomPragma`, `getCustomPragmaVal`. Please consult macros module -documentation for details. These macros are no magic, they don't do anything -you cannot do yourself by walking AST object representation. +access `hasCustomPragma`, `getCustomPragmaVal`. Please consult the macros module +documentation for details. These macros are not magic, everything they do can +also be achieved by walking the AST of the object representation. More examples with custom pragmas: From cbfe9325c5c1851630ce10cf780d1af27c57d19a Mon Sep 17 00:00:00 2001 From: Max Grender-Jones Date: Mon, 25 May 2020 11:18:35 +0100 Subject: [PATCH 0098/3768] Add support for mktemps (#14347) --- lib/posix/posix.nim | 13 +++++++++++-- lib/posix/posix_utils.nim | 19 +++++++++++++------ 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index 96203b736..d3d9728ab 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -1036,12 +1036,21 @@ proc mkstemp*(tmpl: cstring): cint {.importc, header: "", sideEffect.} ## Creates a unique temporary file. ## ## **Warning**: The `tmpl` argument is written to by `mkstemp` and thus - ## can't be a string literal. If in doubt copy the string before passing it. + ## can't be a string literal. If in doubt make a copy of the cstring before + ## passing it in. + +proc mkstemps*(tmpl: cstring, suffixlen: int): cint {.importc, header: "", sideEffect.} + ## Creates a unique temporary file. + ## + ## **Warning**: The `tmpl` argument is written to by `mkstemps` and thus + ## can't be a string literal. If in doubt make a copy of the cstring before + ## passing it in. proc mkdtemp*(tmpl: cstring): pointer {.importc, header: "", sideEffect.} -when defined(linux) or defined(bsd): +when defined(linux) or defined(bsd) or defined(osx): proc mkostemp*(tmpl: cstring, oflags: cint): cint {.importc, header: "", sideEffect.} + proc mkostemps*(tmpl: cstring, suffixlen: cint, oflags: cint): cint {.importc, header: "", sideEffect.} proc posix_memalign*(memptr: pointer, alignment: csize_t, size: csize_t): cint {.importc, header: "".} diff --git a/lib/posix/posix_utils.nim b/lib/posix/posix_utils.nim index 2d0288187..d083e20b9 100644 --- a/lib/posix/posix_utils.nim +++ b/lib/posix/posix_utils.nim @@ -78,16 +78,23 @@ proc sendSignal*(pid: Pid, signal: int) = if kill(pid, signal.cint) != 0: raise newException(OSError, $strerror(errno)) -proc mkstemp*(prefix: string): (string, File) = - ## Creates a unique temporary file from a prefix string. Adds a six chars suffix. +proc mkstemp*(prefix: string, suffix=""): (string, File) = + ## Creates a unique temporary file from a prefix string. A six-character string + ## will be added. If suffix is provided it will be added to the string ## The file is created with perms 0600. ## Returns the filename and a file opened in r/w mode. - var tmpl = cstring(prefix & "XXXXXX") + var tmpl = cstring(prefix & "XXXXXX" & suffix) let fd = - when declared(mkostemp): - mkostemp(tmpl, O_CLOEXEC) + if len(suffix)==0: + when declared(mkostemp): + mkostemp(tmpl, O_CLOEXEC) + else: + mkstemp(tmpl) else: - mkstemp(tmpl) + when declared(mkostemps): + mkostemps(tmpl, cint(len(suffix)), O_CLOEXEC) + else: + mkstemps(tmpl, cint(len(suffix))) var f: File if open(f, fd, fmReadWrite): return ($tmpl, f) From 58282547f6d3fe4ce3fa2efe4f6afe07bc5de662 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 25 May 2020 04:25:40 -0700 Subject: [PATCH 0099/3768] fix #6583, fix #14376, index+search now generated for all projects, many bug fixes with nim doc (#14324) * refs #6583 fix nim doc output * changelog * change default for outDir when unspecified * cleanups * --project implies --index --- .gitignore | 5 +- changelog.md | 4 +- compiler/commands.nim | 4 +- compiler/docgen.nim | 64 +++++----- compiler/main.nim | 116 +++++++----------- compiler/msgs.nim | 10 +- compiler/nimpaths.nim | 44 +++++++ compiler/options.nim | 8 +- config/nimdoc.cfg | 9 +- doc/docgen.rst | 12 +- .../expected/index.html | 3 + .../expected/subdir/subdir_b/utils.html | 5 +- nimdoc/testproject/expected/testproject.html | 3 + testament/lib/stdtest/specialpaths.nim | 28 ++--- testament/lib/stdtest/unittest_light.nim | 6 +- tests/misc/trunner.nim | 101 +++++++++++++-- tests/nimdoc/imp.nim | 1 + tests/nimdoc/m13129.nim | 4 +- tests/nimdoc/sub/imp.nim | 1 + tests/nimdoc/sub/imp2.nim | 1 + tests/nimdoc/sub/mmain.nim | 8 ++ tools/kochdocs.nim | 18 ++- 22 files changed, 294 insertions(+), 161 deletions(-) create mode 100644 compiler/nimpaths.nim create mode 100644 tests/nimdoc/imp.nim create mode 100644 tests/nimdoc/sub/imp.nim create mode 100644 tests/nimdoc/sub/imp2.nim create mode 100644 tests/nimdoc/sub/mmain.nim diff --git a/.gitignore b/.gitignore index b967707d8..cf0bcfbd6 100644 --- a/.gitignore +++ b/.gitignore @@ -92,7 +92,8 @@ megatest.nim # ignore debug dirs generated by dsymutil on OSX *.dSYM -nimdoc.out.css - # for `nim c -r nimdoc/tester` etc; this can be in multiple places htmldocs + +## these are not needed anymore unless checkout old older versions +nimdoc.out.css diff --git a/changelog.md b/changelog.md index 0d4fb6af9..8eeebf9ac 100644 --- a/changelog.md +++ b/changelog.md @@ -94,7 +94,6 @@ - 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. - `osproc.execCmdEx` now takes an optional `input` for stdin, `workingDir` and `env` parameters. @@ -188,6 +187,9 @@ proc mydiv(a, b): int {.raises: [].} = and avoids polluting both $pwd and $projectdir. It can be used with any command. - `runnableExamples "-b:cpp -r:off": code` is now supported, allowing to override how an example is compiled and run, for example to change backend or compile only. +- `nim doc` now outputs under `$projectPath/htmldocs` when `--outdir` is unspecified (with or without `--project`); + passing `--project` now automatically generates an index and enables search. + See [docgen](docgen.html#introduction-quick-start) for details. ## Tool changes diff --git a/compiler/commands.nim b/compiler/commands.nim index e2e1a4558..ce4f7cc0f 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -440,7 +440,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; expectArg(conf, switch, arg, pass, info) conf.docSeeSrcUrl = arg of "docroot": - conf.docRoot = if arg.len == 0: "@default" else: arg + conf.docRoot = if arg.len == 0: docRootDefault else: arg of "backend", "b": let backend = parseEnum(arg.normalize, TBackend.default) if backend == TBackend.default: localError(conf, info, "invalid backend: '$1'" % arg) @@ -485,7 +485,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "forcebuild", "f": processOnOffSwitchG(conf, {optForceFullMake}, arg, pass, info) of "project": - processOnOffSwitchG(conf, {optWholeProject}, arg, pass, info) + processOnOffSwitchG(conf, {optWholeProject, optGenIndex}, arg, pass, info) of "gc": expectArg(conf, switch, arg, pass, info) if pass in {passCmd2, passPP}: diff --git a/compiler/docgen.nim b/compiler/docgen.nim index a67dc48ec..bd967d403 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -17,11 +17,10 @@ import packages/docutils/rst, packages/docutils/rstgen, json, xmltree, cgi, trees, types, typesrenderer, astalgo, lineinfos, intsets, - pathutils, trees, tables + pathutils, trees, tables, nimpaths const exportSection = skField - htmldocsDir = RelativeDir"htmldocs" docCmdSkip = "skip" type @@ -51,7 +50,7 @@ type destFile*: AbsoluteFile thisDir*: AbsoluteDir exampleGroups: OrderedTable[string, ExampleGroup] - wroteCss*: bool + wroteSupportFiles*: bool PDoc* = ref TDocumentor ## Alias to type less. @@ -72,7 +71,7 @@ proc presentationPath*(conf: ConfigRef, file: AbsoluteFile, isTitle = false): Re proc nimbleDir(): AbsoluteDir = getNimbleFile(conf, file2).parentDir.AbsoluteDir case conf.docRoot: - of "@default": # using `@` instead of `$` to avoid shell quoting complications + of docRootDefault: result = getRelativePathFromConfigPath(conf, file) let dir = nimbleDir() if not dir.isEmpty: @@ -88,9 +87,12 @@ proc presentationPath*(conf: ConfigRef, file: AbsoluteFile, isTitle = false): Re result = getRelativePathFromConfigPath(conf, file) if result.isEmpty: bail() elif conf.docRoot.len > 0: - doAssert conf.docRoot.isAbsolute, conf.docRoot # or globalError - doAssert conf.docRoot.existsDir, conf.docRoot - result = relativeTo(file, conf.docRoot.AbsoluteDir) + # we're (currently) requiring `isAbsolute` to avoid confusion when passing + # a relative path (would it be relative wrt $PWD or to projectfile) + conf.globalAssert conf.docRoot.isAbsolute, arg=conf.docRoot + conf.globalAssert conf.docRoot.existsDir, arg=conf.docRoot + # needed because `canonicalizePath` called on `file` + result = file.relativeTo conf.docRoot.expandFilename.AbsoluteDir else: bail() if isAbsolute(result.string): @@ -1125,11 +1127,8 @@ proc genSection(d: PDoc, kind: TSymKind) = "sectionid", "sectionTitle", "sectionTitleID", "content"], [ ord(kind).rope, title, rope(ord(kind) + 50), d.toc[kind]]) -const nimdocOutCss = "nimdoc.out.css" - # `out` to make it easier to use with gitignore in user's repos - -proc cssHref(outDir: AbsoluteDir, destFile: AbsoluteFile): Rope = - rope($relativeTo(outDir / nimdocOutCss.RelativeFile, destFile.splitFile().dir, '/')) +proc relLink(outDir: AbsoluteDir, destFile: AbsoluteFile, linkto: RelativeFile): Rope = + rope($relativeTo(outDir / linkto, destFile.splitFile().dir, '/')) proc genOutFile(d: PDoc): Rope = var @@ -1160,15 +1159,17 @@ proc genOutFile(d: PDoc): Rope = elif d.hasToc: "doc.body_toc" else: "doc.body_no_toc" content = ropeFormatNamedVars(d.conf, getConfigVar(d.conf, bodyname), ["title", - "tableofcontents", "moduledesc", "date", "time", "content", "deprecationMsg"], + "tableofcontents", "moduledesc", "date", "time", "content", "deprecationMsg", "theindexhref"], [title.rope, toc, d.modDesc, rope(getDateStr()), - rope(getClockStr()), code, d.modDeprecationMsg]) + rope(getClockStr()), code, d.modDeprecationMsg, relLink(d.conf.outDir, d.destFile, theindexFname.RelativeFile)]) if optCompileOnly notin d.conf.globalOptions: # XXX what is this hack doing here? 'optCompileOnly' means raw output!? code = ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.file"), [ - "nimdoccss", "title", "tableofcontents", "moduledesc", "date", "time", + "nimdoccss", "dochackjs", "title", "tableofcontents", "moduledesc", "date", "time", "content", "author", "version", "analytics", "deprecationMsg"], - [cssHref(d.conf.outDir, d.destFile), title.rope, toc, d.modDesc, rope(getDateStr()), rope(getClockStr()), + [relLink(d.conf.outDir, d.destFile, nimdocOutCss.RelativeFile), + relLink(d.conf.outDir, d.destFile, docHackJsFname.RelativeFile), + title.rope, toc, d.modDesc, rope(getDateStr()), rope(getClockStr()), content, d.meta[metaAuthor].rope, d.meta[metaVersion].rope, d.analytics.rope, d.modDeprecationMsg]) else: code = content @@ -1203,11 +1204,13 @@ proc writeOutput*(d: PDoc, useWarning = false) = if not writeRope(content, outfile): rawMessage(d.conf, if useWarning: warnCannotOpenFile else: errCannotOpenFile, outfile.string) - elif not d.wroteCss: - let cssSource = $d.conf.getPrefixDir() / "doc" / "nimdoc.css" - let cssDest = $dir / nimdocOutCss - copyFile(cssSource, cssDest) - d.wroteCss = true + elif not d.wroteSupportFiles: # nimdoc.css + dochack.js + let nimr = $d.conf.getPrefixDir() + copyFile(docCss.interp(nimr = nimr), $d.conf.outDir / nimdocOutCss) + if optGenIndex in d.conf.globalOptions: + let docHackJs2 = getDocHacksJs(nimr, nim = getAppFilename()) + copyFile(docHackJs2, $d.conf.outDir / docHackJs2.lastPathPart) + d.wroteSupportFiles = true proc writeOutputJson*(d: PDoc, useWarning = false) = runAllExamples(d) @@ -1234,6 +1237,8 @@ proc writeOutputJson*(d: PDoc, useWarning = false) = proc handleDocOutputOptions*(conf: ConfigRef) = if optWholeProject in conf.globalOptions: # Backward compatibility with previous versions + # xxx this is buggy when user provides `nim doc --project -o:sub/bar.html main`, + # it'd write to `sub/bar.html/main.html` conf.outDir = AbsoluteDir(conf.outDir / conf.outFile) proc commandDoc*(cache: IdentCache, conf: ConfigRef) = @@ -1308,20 +1313,23 @@ proc commandTags*(cache: IdentCache, conf: ConfigRef) = if not writeRope(content, filename): rawMessage(conf, errCannotOpenFile, filename.string) -proc commandBuildIndex*(cache: IdentCache, conf: ConfigRef) = - var content = mergeIndexes(conf.projectFull.string).rope +proc commandBuildIndex*(conf: ConfigRef, dir: string, outFile = RelativeFile"") = + var content = mergeIndexes(dir).rope - var outFile = RelativeFile"theindex" - if conf.outFile != RelativeFile"": - outFile = conf.outFile + var outFile = outFile + if outFile.isEmpty: outFile = theindexFname.RelativeFile.changeFileExt("") let filename = getOutFile(conf, outFile, HtmlExt) let code = ropeFormatNamedVars(conf, getConfigVar(conf, "doc.file"), [ - "nimdoccss", "title", "tableofcontents", "moduledesc", "date", "time", + "nimdoccss", "dochackjs", + "title", "tableofcontents", "moduledesc", "date", "time", "content", "author", "version", "analytics"], - [cssHref(conf.outDir, filename), rope"Index", nil, nil, rope(getDateStr()), + [relLink(conf.outDir, filename, nimdocOutCss.RelativeFile), + relLink(conf.outDir, filename, docHackJsFname.RelativeFile), + rope"Index", nil, nil, rope(getDateStr()), rope(getClockStr()), content, nil, nil, nil]) # no analytics because context is not available if not writeRope(code, filename): rawMessage(conf, errCannotOpenFile, filename.string) + diff --git a/compiler/main.nim b/compiler/main.nim index 88f1890de..55b2f2899 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -66,16 +66,8 @@ when not defined(leanCompiler): compileProject(graph) finishDoc2Pass(graph.config.projectName) -proc setOutDir(conf: ConfigRef) = - if conf.outDir.isEmpty: - if optUseNimcache in conf.globalOptions: - conf.outDir = getNimcacheDir(conf) - else: - conf.outDir = conf.projectPath - proc commandCompileToC(graph: ModuleGraph) = let conf = graph.config - setOutDir(conf) if conf.outFile.isEmpty: let base = conf.projectName let targetName = if optGenDynLib in conf.globalOptions: @@ -121,7 +113,6 @@ proc commandCompileToJS(graph: ModuleGraph) = let conf = graph.config conf.exc = excCpp - setOutDir(conf) if conf.outFile.isEmpty: conf.outFile = RelativeFile(conf.projectName & ".js") @@ -191,11 +182,6 @@ proc mainCommand*(graph: ModuleGraph) = conf.searchPaths.add(conf.libpath) setId(100) - ## Calling `setOutDir(conf)` unconditionally would fix regression - ## https://github.com/nim-lang/Nim/issues/6583#issuecomment-625711125 - when false: setOutDir(conf) - if optUseNimcache in conf.globalOptions: setOutDir(conf) - proc customizeForBackend(backend: TBackend) = ## Sets backend specific options but don't compile to backend yet in ## case command doesn't require it. This must be called by all commands. @@ -234,15 +220,40 @@ proc mainCommand*(graph: ModuleGraph) = of backendJs: commandCompileToJS(graph) of backendInvalid: doAssert false + template docLikeCmd(body) = + when defined(leanCompiler): + quit "compiler wasn't built with documentation generator" + else: + wantMainModule(conf) + conf.cmd = cmdDoc + loadConfigs(DocConfig, cache, conf) + defineSymbol(conf.symbols, "nimdoc") + body + + block: ## command prepass + var docLikeCmd2 = false # includes what calls `docLikeCmd` + some more + case conf.command.normalize + of "r": conf.globalOptions.incl {optRun, optUseNimcache} + of "doc0", "doc2", "doc", "rst2html", "rst2tex", "jsondoc0", "jsondoc2", + "jsondoc", "ctags", "buildindex": docLikeCmd2 = true + else: discard + if conf.outDir.isEmpty: + # doc like commands can generate a lot of files (especially with --project) + # so by default should not end up in $PWD nor in $projectPath. + conf.outDir = block: + var ret = if optUseNimcache in conf.globalOptions: getNimcacheDir(conf) + else: conf.projectPath + doAssert ret.string.isAbsolute # `AbsoluteDir` is not a real guarantee + if docLikeCmd2: ret = ret / htmldocsDir + ret + ## process all backend commands case conf.command.normalize of "c", "cc", "compile", "compiletoc": compileToBackend(backendC) # compile means compileToC currently of "cpp", "compiletocpp": compileToBackend(backendCpp) of "objc", "compiletooc": compileToBackend(backendObjc) of "js", "compiletojs": compileToBackend(backendJs) - of "r": # different from `"run"`! - conf.globalOptions.incl {optRun, optUseNimcache} - compileToBackend(backendC) + of "r": compileToBackend(backendC) # different from `"run"`! of "run": when hasTinyCBackend: extccomp.setCC(conf, "tcc", unknownLineInfo) @@ -254,29 +265,19 @@ proc mainCommand*(graph: ModuleGraph) = else: customizeForBackend(backendC) # fallback for other commands ## process all other commands - case conf.command.normalize - of "doc0": - when defined(leanCompiler): - quit "compiler wasn't built with documentation generator" - else: - wantMainModule(conf) - conf.cmd = cmdDoc - loadConfigs(DocConfig, cache, conf) - commandDoc(cache, conf) + case conf.command.normalize # synchronize with `cmdUsingHtmlDocs` + of "doc0": docLikeCmd commandDoc(cache, conf) of "doc2", "doc": - conf.setNoteDefaults(warnLockLevel, false) # issue #13218 - conf.setNoteDefaults(warnRedefinitionOfLabel, false) # issue #13218 - # because currently generates lots of false positives due to conflation - # of labels links in doc comments, eg for random.rand: - # ## * `rand proc<#rand,Rand,Natural>`_ that returns an integer - # ## * `rand proc<#rand,Rand,range[]>`_ that returns a float - when defined(leanCompiler): - quit "compiler wasn't built with documentation generator" - else: - conf.cmd = cmdDoc - loadConfigs(DocConfig, cache, conf) - defineSymbol(conf.symbols, "nimdoc") + docLikeCmd(): + conf.setNoteDefaults(warnLockLevel, false) # issue #13218 + conf.setNoteDefaults(warnRedefinitionOfLabel, false) # issue #13218 + # because currently generates lots of false positives due to conflation + # of labels links in doc comments, eg for random.rand: + # ## * `rand proc<#rand,Rand,Natural>`_ that returns an integer + # ## * `rand proc<#rand,Rand,range[]>`_ that returns a float commandDoc2(graph, false) + if optGenIndex in conf.globalOptions and optWholeProject in conf.globalOptions: + commandBuildIndex(conf, $conf.outDir) of "rst2html": conf.setNoteDefaults(warnRedefinitionOfLabel, false) # similar to issue #13218 when defined(leanCompiler): @@ -292,41 +293,10 @@ proc mainCommand*(graph: ModuleGraph) = conf.cmd = cmdRst2tex loadConfigs(DocTexConfig, cache, conf) commandRst2TeX(cache, conf) - of "jsondoc0": - when defined(leanCompiler): - quit "compiler wasn't built with documentation generator" - else: - wantMainModule(conf) - conf.cmd = cmdDoc - loadConfigs(DocConfig, cache, conf) - wantMainModule(conf) - defineSymbol(conf.symbols, "nimdoc") - commandJson(cache, conf) - of "jsondoc2", "jsondoc": - when defined(leanCompiler): - quit "compiler wasn't built with documentation generator" - else: - conf.cmd = cmdDoc - loadConfigs(DocConfig, cache, conf) - wantMainModule(conf) - defineSymbol(conf.symbols, "nimdoc") - commandDoc2(graph, true) - of "ctags": - when defined(leanCompiler): - quit "compiler wasn't built with documentation generator" - else: - wantMainModule(conf) - conf.cmd = cmdDoc - loadConfigs(DocConfig, cache, conf) - defineSymbol(conf.symbols, "nimdoc") - commandTags(cache, conf) - of "buildindex": - when defined(leanCompiler): - quit "compiler wasn't built with documentation generator" - else: - conf.cmd = cmdDoc - loadConfigs(DocConfig, cache, conf) - commandBuildIndex(cache, conf) + of "jsondoc0": docLikeCmd commandJson(cache, conf) + of "jsondoc2", "jsondoc": docLikeCmd commandDoc2(graph, true) + of "ctags": docLikeCmd commandTags(cache, conf) + of "buildindex": docLikeCmd commandBuildIndex(conf, $conf.projectFull, conf.outFile) of "gendepend": conf.cmd = cmdGenDepend commandGenDepend(graph) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 6bf4d82d2..a26aa0a3c 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -510,7 +510,7 @@ proc liMessage(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string, styledMsgWriteln(styleBright, loc, resetStyle, color, title, resetStyle, s, KindColor, kindmsg) if conf.hasHint(hintSource) and info != unknownLineInfo: conf.writeSurroundingSrc(info) - if conf.hasHint(hintMsgOrigin): + if hintMsgOrigin in conf.mainPackageNotes: styledMsgWriteln(styleBright, toFileLineCol(info2), resetStyle, " compiler msg initiated here", KindColor, KindFormat % hintMsgOrigin.msgToStr, @@ -529,6 +529,14 @@ template fatal*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") = conf.m.errorOutputs = {eStdOut, eStdErr} liMessage(conf, info, msg, arg, doAbort, instLoc()) +template globalAssert*(conf: ConfigRef; cond: untyped, info: TLineInfo = unknownLineInfo, arg = "") = + ## avoids boilerplate + if not cond: + const info2 = instantiationInfo(-1, fullPaths = true) + var arg2 = "'$1' failed" % [astToStr(cond)] + if arg.len > 0: arg2.add "; " & astToStr(arg) & ": " & arg + liMessage(conf, info, errGenerated, arg2, doRaise, info2) + template globalError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") = liMessage(conf, info, msg, arg, doRaise, instLoc()) diff --git a/compiler/nimpaths.nim b/compiler/nimpaths.nim new file mode 100644 index 000000000..2d7fa53cb --- /dev/null +++ b/compiler/nimpaths.nim @@ -0,0 +1,44 @@ +##[ +Represents absolute paths, but using a symbolic variables (eg $nimr) which can be +resolved at runtime; this avoids hardcoding at compile time absolute paths so +that the project root can be relocated. + +xxx consider some refactoring with $nim/testament/lib/stdtest/specialpaths.nim; +specialpaths is simpler because it doesn't need variables to be relocatable at +runtime (eg for use in testament) + +interpolation variables: + $nimr: such that `$nimr/lib/system.nim` exists (avoids confusion with $nim binary) + in compiler, it's obtainable via getPrefixDir(); for other tools (eg koch), + this could be getCurrentDir() or getAppFilename().parentDir.parentDir, + depending on use case + +Unstable API +]## + +import std/[os,strutils] + +const + docCss* = "$nimr/doc/nimdoc.css" + docHackNim* = "$nimr/tools/dochack/dochack.nim" + docHackJs* = docHackNim.changeFileExt("js") + docHackJsFname* = docHackJs.lastPathPart + theindexFname* = "theindex.html" + nimdocOutCss* = "nimdoc.out.css" + # `out` to make it easier to use with gitignore in user's repos + htmldocsDirname* = "htmldocs" + +proc interp*(path: string, nimr: string): string = + result = path % ["nimr", nimr] + doAssert '$' notin result, $(path, nimr, result) # avoids un-interpolated variables in output + +proc getDocHacksJs*(nimr: string, nim = getCurrentCompilerExe(), forceRebuild = false): string = + ## return absolute path to dochhack.js, rebuilding if it doesn't exist or if + ## `forceRebuild`. + let docHackJs2 = docHackJs.interp(nimr = nimr) + if forceRebuild or not docHackJs2.fileExists: + let cmd = "$nim js $file" % ["nim", nim.quoteShell, "file", docHackNim.interp(nimr = nimr).quoteShell] + echo "getDocHacksJs: cmd: " & cmd + doAssert execShellCmd(cmd) == 0, $(cmd) + doAssert docHackJs2.fileExists + result = docHackJs2 diff --git a/compiler/options.nim b/compiler/options.nim index 1418fff63..d990f2fd4 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -9,7 +9,7 @@ import os, strutils, strtabs, sets, lineinfos, platform, - prefixmatches, pathutils + prefixmatches, pathutils, nimpaths from terminal import isatty from times import utc, fromUnix, local, getTime, format, DateTime @@ -518,8 +518,9 @@ const DefaultConfigNims* = RelativeFile"config.nims" DocConfig* = RelativeFile"nimdoc.cfg" DocTexConfig* = RelativeFile"nimdoc.tex.cfg" - -const oKeepVariableNames* = true + htmldocsDir* = htmldocsDirname.RelativeDir + docRootDefault* = "@default" # using `@` instead of `$` to avoid shell quoting complications + oKeepVariableNames* = true proc mainCommandArg*(conf: ConfigRef): string = ## This is intended for commands like check or parse @@ -543,6 +544,7 @@ proc getOutFile*(conf: ConfigRef; filename: RelativeFile, ext: string): Absolute # explains regression https://github.com/nim-lang/Nim/issues/6583#issuecomment-625711125 # Yet another reason why "" should not mean "."; `""/something` should raise # instead of implying "" == "." as it's bug prone. + doAssert conf.outDir.string.len > 0 result = conf.outDir / changeFileExt(filename, ext) proc absOutFile*(conf: ConfigRef): AbsoluteFile = diff --git a/config/nimdoc.cfg b/config/nimdoc.cfg index 4faebdf72..836b19ab6 100644 --- a/config/nimdoc.cfg +++ b/config/nimdoc.cfg @@ -100,7 +100,7 @@ doc.body_toc = """ Standard library

  • - Index + Index
  • @@ -143,7 +143,7 @@ doc.body_toc_group = """ Standard library
  • - Index + Index
  • @@ -183,6 +183,9 @@ doc.body_toc_group = """
    @@ -239,7 +242,7 @@ doc.file = """ $title - + + + 5: Date: Fri, 29 May 2020 15:18:15 +0530 Subject: [PATCH 0117/3768] disallow typedesc in arrays & move existing checks to `types.typeAllowedAux` (#13261) * disallow typedesc in arrays and move previous checks to types.typeAllowedAux --- compiler/sem.nim | 12 ++++---- compiler/semstmts.nim | 45 +++++++++++++--------------- compiler/types.nim | 32 +++++++++++++------- tests/array/t9932.nim | 11 +++++++ tests/errmsgs/t10489_a.nim | 2 +- tests/errmsgs/t10489_b.nim | 2 +- tests/errmsgs/t12844.nim | 6 ++-- tests/errmsgs/t5870.nim | 2 +- tests/errmsgs/t8610.nim | 2 +- tests/errmsgs/ttypeAllowed.nim | 2 +- tests/metatype/typedesc_as_value.nim | 2 +- tests/typerel/typedescs2.nim | 2 +- 12 files changed, 70 insertions(+), 50 deletions(-) create mode 100644 tests/array/t9932.nim diff --git a/compiler/sem.nim b/compiler/sem.nim index 62e3ca8db..683c67254 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -233,13 +233,15 @@ proc typeAllowedCheck(conf: ConfigRef; info: TLineInfo; typ: PType; kind: TSymKi flags: TTypeAllowedFlags = {}) = let t = typeAllowed(typ, kind, flags) if t != nil: + var err: string if t == typ: - localError(conf, info, "invalid type: '" & typeToString(typ) & - "' for " & substr($kind, 2).toLowerAscii) + err = "invalid type: '$1' for $2" % [typeToString(typ), toHumanStr(kind)] + if kind in {skVar, skLet, skConst} and taIsTemplateOrMacro in flags: + err &= ". Did you mean to call the $1 with '()'?" % [toHumanStr(typ.owner.kind)] else: - localError(conf, info, "invalid type: '" & typeToString(t) & - "' in this context: '" & typeToString(typ) & - "' for " & substr($kind, 2).toLowerAscii) + err = "invalid type: '$1' in this context: '$2' for $3" % [typeToString(t), + typeToString(typ), toHumanStr(kind)] + localError(conf, info, err) proc paramsTypeCheck(c: PContext, typ: PType) {.inline.} = typeAllowedCheck(c.config, typ.n.info, typ, skProc) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 3faa32808..a4b4c9362 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -36,8 +36,6 @@ const errRecursiveDependencyX = "recursive dependency: '$1'" errRecursiveDependencyIteratorX = "recursion is not supported in iterators: '$1'" errPragmaOnlyInHeaderOfProcX = "pragmas are only allowed in the header of a proc; redefinition of $1" - errCannotAssignMacroSymbol = "cannot assign $1 '$2' to '$3'. Did you mean to call the $1 with '()'?" - errInvalidTypeDescAssign = "'typedesc' metatype is not valid here; typed '=' instead of ':'?" proc semDiscard(c: PContext, n: PNode): PNode = result = n @@ -491,19 +489,16 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = if a[^2].kind != nkEmpty: typ = semTypeNode(c, a[^2], nil) + var typFlags: TTypeAllowedFlags + var def: PNode = c.graph.emptyNode if a[^1].kind != nkEmpty: def = semExprWithType(c, a[^1], {efAllowDestructor}) - if def.typ.kind == tyProc and def.kind == nkSym: - if def.sym.kind in {skMacro, skTemplate}: - localError(c.config, def.info, errCannotAssignMacroSymbol % [ - if def.sym.kind == skMacro: "macro" else: "template", - def.sym.name.s, a[0].ident.s]) - def.typ = errorType(c) + + if def.kind == nkSym and def.sym.kind in {skTemplate, skMacro}: + typFlags.incl taIsTemplateOrMacro elif def.typ.kind == tyTypeDesc and c.p.owner.kind != skMacro: - # prevent the all too common 'var x = int' bug: - localError(c.config, def.info, errInvalidTypeDescAssign) - def.typ = errorType(c) + typFlags.incl taProcContextIsNotMacro if typ != nil: if typ.isMetaType: @@ -534,7 +529,11 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = # this can only happen for errornous var statements: if typ == nil: continue - typeAllowedCheck(c.config, a.info, typ, symkind, if c.matchedConcept != nil: {taConcept} else: {}) + + if c.matchedConcept != nil: + typFlags.incl taConcept + typeAllowedCheck(c.config, a.info, typ, symkind, typFlags) + when false: liftTypeBoundOps(c, typ, a.info) instAllTypeBoundOp(c, a.info) var tup = skipTypes(typ, {tyGenericInst, tyAlias, tySink}) @@ -642,18 +641,15 @@ proc semConst(c: PContext, n: PNode): PNode = if a[^2].kind != nkEmpty: typ = semTypeNode(c, a[^2], nil) + var typFlags: TTypeAllowedFlags + # don't evaluate here since the type compatibility check below may add a converter var def = semExprWithType(c, a[^1]) - if def.typ.kind == tyProc and def.kind == nkSym: - if def.sym.kind in {skMacro, skTemplate}: - localError(c.config, def.info, errCannotAssignMacroSymbol % [ - if def.sym.kind == skMacro: "macro" else: "template", - def.sym.name.s, a[0].ident.s]) - def.typ = errorType(c) + + if def.kind == nkSym and def.sym.kind in {skTemplate, skMacro}: + typFlags.incl taIsTemplateOrMacro elif def.typ.kind == tyTypeDesc and c.p.owner.kind != skMacro: - # prevent the all too common 'const x = int' bug: - localError(c.config, def.info, errInvalidTypeDescAssign) - def.typ = errorType(c) + typFlags.incl taProcContextIsNotMacro # check type compatibility between def.typ and typ: if typ != nil: @@ -670,9 +666,10 @@ proc semConst(c: PContext, n: PNode): PNode = if def == nil: localError(c.config, a[^1].info, errConstExprExpected) continue - if typeAllowed(typ, skConst) != nil and def.kind != nkNilLit: - localError(c.config, a.info, "invalid type for const: " & typeToString(typ)) - continue + if def.kind != nkNilLit: + if c.matchedConcept != nil: + typFlags.incl taConcept + typeAllowedCheck(c.config, a.info, typ, skConst, typFlags) var b: PNode if a.kind == nkVarTuple: diff --git a/compiler/types.nim b/compiler/types.nim index 709e5bb2e..e1b283f87 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -1246,6 +1246,8 @@ type taConcept, taIsOpenArray, taNoUntyped + taIsTemplateOrMacro + taProcContextIsNotMacro TTypeAllowedFlags* = set[TTypeAllowedFlag] @@ -1307,18 +1309,24 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, if kind notin {skParam, skResult}: result = t else: result = typeAllowedAux(marker, t2, kind, flags) of tyProc: - if isInlineIterator(typ) and kind in {skVar, skLet, skConst, skParam, skResult}: - # only closure iterators my be assigned to anything. + if kind in {skVar, skLet, skConst} and taIsTemplateOrMacro in flags: result = t - let f = if kind in {skProc, skFunc}: flags+{taNoUntyped} else: flags - for i in 1.. Date: Fri, 29 May 2020 12:08:17 +0200 Subject: [PATCH 0118/3768] more checking for --gc:arc, no need for valgrind (#14467) * sigmatch: removed dead code --- compiler/cgen.nim | 3 +- compiler/dfa.nim | 5 ++- compiler/injectdestructors.nim | 2 + compiler/sigmatch.nim | 6 ++- lib/system/bitmasks.nim | 30 ++++++++++++++ lib/system/cellseqs_v1.nim | 43 ++++++++++++++++++++ lib/system/cellsets.nim | 70 ++++++++++++--------------------- lib/system/cyclebreaker.nim | 1 - lib/system/cyclicrefs_bacon.nim | 1 - lib/system/mmdisp.nim | 25 ++---------- lib/system/refs_v2.nim | 45 ++++++++++++++++++++- lib/system/seqs_v2.nim | 1 - 12 files changed, 159 insertions(+), 73 deletions(-) create mode 100644 lib/system/bitmasks.nim create mode 100644 lib/system/cellseqs_v1.nim diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 8fa824fcc..338b337f2 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -2067,7 +2067,8 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = let disp = generateMethodDispatchers(graph) for x in disp: genProcAux(m, x.sym) - m.g.modulesClosed.add m + let mm = m + m.g.modulesClosed.add mm proc genForwardedProcs(g: BModuleList) = # Forward declared proc:s lack bodies when first encountered, so they're given diff --git a/compiler/dfa.nim b/compiler/dfa.nim index 67a9e26d8..a0ec31ac6 100644 --- a/compiler/dfa.nim +++ b/compiler/dfa.nim @@ -809,4 +809,7 @@ proc constructCfg*(s: PSym; body: PNode): ControlFlowGraph = withBlock(s): gen(c, body) genImplicitReturn(c) - shallowCopy(result, c.code) + when defined(gcArc) or defined(gcOrc): + result = c.code # will move + else: + shallowCopy(result, c.code) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 5785df6d6..9cb777f2c 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -354,6 +354,8 @@ proc genWasMoved(n: PNode; c: var Con): PNode = result = newNodeI(nkCall, n.info) result.add(newSymNode(createMagic(c.graph, "wasMoved", mWasMoved))) result.add copyTree(n) #mWasMoved does not take the address + #if n.kind != nkSym: + # message(c.graph.config, n.info, warnUser, "wasMoved(" & $n & ")") proc genDefaultCall(t: PType; c: Con; info: TLineInfo): PNode = result = newNodeI(nkCall, info) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 401d160fa..e99269c41 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -993,6 +993,11 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, result = isNone assert(f != nil) + when declared(deallocatedRefId): + let corrupt = deallocatedRefId(cast[pointer](f)) + if corrupt != 0: + quit "it's corrupt " & $corrupt + if f.kind == tyUntyped: if aOrig != nil: put(c, f, aOrig) return isGeneric @@ -1409,7 +1414,6 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, let roota = a.skipGenericAlias let rootf = f.skipGenericAlias - var m = c if a.kind == tyGenericInst: if roota.base == rootf.base: let nextFlags = flags + {trNoCovariance} diff --git a/lib/system/bitmasks.nim b/lib/system/bitmasks.nim new file mode 100644 index 000000000..922ad5fb7 --- /dev/null +++ b/lib/system/bitmasks.nim @@ -0,0 +1,30 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2015 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +# Page size of the system; in most cases 4096 bytes. For exotic OS or +# CPU this needs to be changed: +const + PageShift = when defined(cpu16): 8 else: 12 # \ + # my tests showed no improvements for using larger page sizes. + PageSize = 1 shl PageShift + PageMask = PageSize-1 + + MemAlign = 16 # also minimal allocatable memory block + + BitsPerPage = PageSize div MemAlign + UnitsPerPage = BitsPerPage div (sizeof(int)*8) + # how many ints do we need to describe a page: + # on 32 bit systems this is only 16 (!) + + TrunkShift = 9 + BitsPerTrunk = 1 shl TrunkShift # needs to be power of 2 and divisible by 64 + TrunkMask = BitsPerTrunk - 1 + IntsPerTrunk = BitsPerTrunk div (sizeof(int)*8) + IntShift = 5 + ord(sizeof(int) == 8) # 5 or 6, depending on int width + IntMask = 1 shl IntShift - 1 diff --git a/lib/system/cellseqs_v1.nim b/lib/system/cellseqs_v1.nim new file mode 100644 index 000000000..1952491b3 --- /dev/null +++ b/lib/system/cellseqs_v1.nim @@ -0,0 +1,43 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2019 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +# ------------------- cell seq handling --------------------------------------- + +type + PCellArray = ptr UncheckedArray[PCell] + CellSeq {.final, pure.} = object + len, cap: int + d: PCellArray + +proc contains(s: CellSeq, c: PCell): bool {.inline.} = + for i in 0 .. s.len-1: + if s.d[i] == c: return true + return false + +proc add(s: var CellSeq, c: PCell) {.inline.} = + if s.len >= s.cap: + s.cap = s.cap * 3 div 2 + var d = cast[PCellArray](alloc(s.cap * sizeof(PCell))) + copyMem(d, s.d, s.len * sizeof(PCell)) + dealloc(s.d) + s.d = d + # XXX: realloc? + s.d[s.len] = c + inc(s.len) + +proc init(s: var CellSeq, cap: int = 1024) = + s.len = 0 + s.cap = cap + s.d = cast[PCellArray](alloc0(cap * sizeof(PCell))) + +proc deinit(s: var CellSeq) = + dealloc(s.d) + s.d = nil + s.len = 0 + s.cap = 0 diff --git a/lib/system/cellsets.nim b/lib/system/cellsets.nim index c73c84f52..ea00176b5 100644 --- a/lib/system/cellsets.nim +++ b/lib/system/cellsets.nim @@ -9,20 +9,28 @@ # Efficient set of pointers for the GC (and repr) +when defined(gcOrc) or defined(gcArc): + type + PCell = Cell + + include bitmasks + +else: + type + RefCount = int + + Cell {.pure.} = object + refcount: RefCount # the refcount and some flags + typ: PNimType + when trackAllocationSource: + filename: cstring + line: int + when useCellIds: + id: int + + PCell = ptr Cell + type - RefCount = int - - Cell {.pure.} = object - refcount: RefCount # the refcount and some flags - typ: PNimType - when trackAllocationSource: - filename: cstring - line: int - when useCellIds: - id: int - - PCell = ptr Cell - PPageDesc = ptr PageDesc BitIndex = range[0..UnitsPerPage-1] PageDesc {.final, pure.} = object @@ -35,39 +43,11 @@ type counter, max: int head: PPageDesc data: PPageDescArray - PCellArray = ptr UncheckedArray[PCell] - CellSeq {.final, pure.} = object - len, cap: int - d: PCellArray -# ------------------- cell seq handling --------------------------------------- - -proc contains(s: CellSeq, c: PCell): bool {.inline.} = - for i in 0 .. s.len-1: - if s.d[i] == c: return true - return false - -proc add(s: var CellSeq, c: PCell) {.inline.} = - if s.len >= s.cap: - s.cap = s.cap * 3 div 2 - var d = cast[PCellArray](alloc(s.cap * sizeof(PCell))) - copyMem(d, s.d, s.len * sizeof(PCell)) - dealloc(s.d) - s.d = d - # XXX: realloc? - s.d[s.len] = c - inc(s.len) - -proc init(s: var CellSeq, cap: int = 1024) = - s.len = 0 - s.cap = cap - s.d = cast[PCellArray](alloc0(cap * sizeof(PCell))) - -proc deinit(s: var CellSeq) = - dealloc(s.d) - s.d = nil - s.len = 0 - s.cap = 0 +when defined(gcOrc) or defined(gcArc): + discard +else: + include cellseqs_v1 # ------------------- cell set handling --------------------------------------- diff --git a/lib/system/cyclebreaker.nim b/lib/system/cyclebreaker.nim index 4b7f8db5a..3d01eeb9d 100644 --- a/lib/system/cyclebreaker.nim +++ b/lib/system/cyclebreaker.nim @@ -60,7 +60,6 @@ const colGreen = 0b000 colYellow = 0b001 colRed = 0b010 - rcShift = 3 # shift by rcShift to get the reference counter colorMask = 0b011 type diff --git a/lib/system/cyclicrefs_bacon.nim b/lib/system/cyclicrefs_bacon.nim index 5f0197384..b9c820e4a 100644 --- a/lib/system/cyclicrefs_bacon.nim +++ b/lib/system/cyclicrefs_bacon.nim @@ -24,7 +24,6 @@ const colPurple = 0b011 isCycleCandidate = 0b100 # cell is marked as a cycle candidate jumpStackFlag = 0b1000 - rcShift = 4 # shift by rcShift to get the reference counter colorMask = 0b011 type diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim index 5cdac2991..5fe1960d1 100644 --- a/lib/system/mmdisp.nim +++ b/lib/system/mmdisp.nim @@ -38,27 +38,10 @@ type PByte = ptr ByteArray PString = ptr string -# Page size of the system; in most cases 4096 bytes. For exotic OS or -# CPU this needs to be changed: -const - PageShift = when defined(cpu16): 8 else: 12 # \ - # my tests showed no improvements for using larger page sizes. - PageSize = 1 shl PageShift - PageMask = PageSize-1 - - MemAlign = 16 # also minimal allocatable memory block - - BitsPerPage = PageSize div MemAlign - UnitsPerPage = BitsPerPage div (sizeof(int)*8) - # how many ints do we need to describe a page: - # on 32 bit systems this is only 16 (!) - - TrunkShift = 9 - BitsPerTrunk = 1 shl TrunkShift # needs to be power of 2 and divisible by 64 - TrunkMask = BitsPerTrunk - 1 - IntsPerTrunk = BitsPerTrunk div (sizeof(int)*8) - IntShift = 5 + ord(sizeof(int) == 8) # 5 or 6, depending on int width - IntMask = 1 shl IntShift - 1 +when declared(IntsPerTrunk): + discard +else: + include bitmasks proc raiseOutOfMem() {.noinline.} = if outOfMemHook != nil: outOfMemHook() diff --git a/lib/system/refs_v2.nim b/lib/system/refs_v2.nim index 8e5e03d3d..df1248586 100644 --- a/lib/system/refs_v2.nim +++ b/lib/system/refs_v2.nim @@ -38,11 +38,13 @@ when defined(gcOrc): const rcIncrement = 0b10000 # so that lowest 4 bits are not touched rcMask = 0b1111 + rcShift = 4 # shift by rcShift to get the reference counter else: const rcIncrement = 0b1000 # so that lowest 3 bits are not touched rcMask = 0b111 + rcShift = 3 # shift by rcShift to get the reference counter type RefHeader = object @@ -52,6 +54,8 @@ type when defined(gcOrc): rootIdx: int # thanks to this we can delete potential cycle roots # in O(1) without doubly linked lists + when defined(nimArcDebug): + refId: int Cell = ptr RefHeader @@ -67,6 +71,14 @@ template head(p: pointer): Cell = const traceCollector = defined(traceArc) +when defined(nimArcDebug): + include cellsets + + const traceId = 7739 # 1037 + + var gRefId: int + var freedCells: CellSet + proc nimNewObj(size: int): pointer {.compilerRtl.} = let s = size + sizeof(RefHeader) when defined(nimscript): @@ -79,6 +91,9 @@ proc nimNewObj(size: int): pointer {.compilerRtl.} = result = allocShared0(s) +! sizeof(RefHeader) else: result = alloc0(s) +! sizeof(RefHeader) + when defined(nimArcDebug): + head(result).refId = gRefId + atomicInc gRefId when traceCollector: cprintf("[Allocated] %p result: %p\n", result -! sizeof(RefHeader), result) @@ -98,6 +113,9 @@ proc nimNewObjUninit(size: int): pointer {.compilerRtl.} = when defined(gcOrc): orig.rootIdx = 0 result = orig +! sizeof(RefHeader) + when defined(nimArcDebug): + head(result).refId = gRefId + atomicInc gRefId when traceCollector: cprintf("[Allocated] %p result: %p\n", result -! sizeof(RefHeader), result) @@ -105,10 +123,25 @@ proc nimDecWeakRef(p: pointer) {.compilerRtl, inl.} = dec head(p).rc, rcIncrement proc nimIncRef(p: pointer) {.compilerRtl, inl.} = + when defined(nimArcDebug): + if head(p).refId == traceId: + writeStackTrace() + cfprintf(cstderr, "[IncRef] %p %ld\n", p, head(p).rc shr rcShift) + inc head(p).rc, rcIncrement when traceCollector: cprintf("[INCREF] %p\n", head(p)) +when not defined(nimscript) and defined(nimArcDebug): + proc deallocatedRefId*(p: pointer): int = + ## Returns the ref's ID if the ref was already deallocated. This + ## is a memory corruption check. Returns 0 if there is no error. + let c = head(p) + if freedCells.data != nil and freedCells.contains(c): + result = c.refId + else: + result = 0 + proc nimRawDispose(p: pointer) {.compilerRtl.} = when not defined(nimscript): when traceCollector: @@ -117,7 +150,11 @@ proc nimRawDispose(p: pointer) {.compilerRtl.} = if head(p).rc >= rcIncrement: cstderr.rawWrite "[FATAL] dangling references exist\n" quit 1 - when defined(useMalloc): + when defined(nimArcDebug): + # we do NOT really free the memory here in order to reliably detect use-after-frees + if freedCells.data == nil: init(freedCells) + freedCells.incl head(p) + elif defined(useMalloc): c_free(p -! sizeof(RefHeader)) elif compileOption("threads"): deallocShared(p -! sizeof(RefHeader)) @@ -150,6 +187,12 @@ when defined(gcOrc): proc nimDecRefIsLast(p: pointer): bool {.compilerRtl, inl.} = if p != nil: var cell = head(p) + + when defined(nimArcDebug): + if cell.refId == traceId: + writeStackTrace() + cfprintf(cstderr, "[DecRef] %p %ld\n", p, cell.rc shr rcShift) + if (cell.rc and not rcMask) == 0: result = true when traceCollector: diff --git a/lib/system/seqs_v2.nim b/lib/system/seqs_v2.nim index 175056f48..1b40c00ab 100644 --- a/lib/system/seqs_v2.nim +++ b/lib/system/seqs_v2.nim @@ -15,7 +15,6 @@ proc supportsCopyMem(t: typedesc): bool {.magic: "TypeTrait".} ## Default seq implementation used by Nim's core. type - NimSeqPayloadBase = object cap: int From e646c16e06e95855a92c1d2453f6f927555aed7e Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 29 May 2020 05:30:36 -0700 Subject: [PATCH 0119/3768] hotfix doc comments for procs without body (#14494) --- compiler/docgen.nim | 1 + lib/system/io.nim | 5 -- nimdoc/testproject/expected/testproject.html | 48 ++++++++++++++++++++ nimdoc/testproject/expected/testproject.idx | 4 ++ nimdoc/testproject/expected/theindex.html | 16 +++++++ nimdoc/testproject/testproject.nim | 30 ++++++++++++ 6 files changed, 99 insertions(+), 5 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index f2dec41b3..ffafb1fcb 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -642,6 +642,7 @@ proc getAllRunnableExamples(d: PDoc, n: PNode, dest: var Rope) = var state = rsStart template fn(n2) = state = getAllRunnableExamplesImpl(d, n2, dest, state) + dest.add genComment(d, n).rope case n.kind of routineDefs: n = n.getRoutineBody diff --git a/lib/system/io.nim b/lib/system/io.nim index 84bebba28..b1f6bb61b 100644 --- a/lib/system/io.nim +++ b/lib/system/io.nim @@ -501,11 +501,6 @@ proc readAll*(file: File): TaintedString {.tags: [ReadIOEffect], benign.} = else: result = readAllBuffer(file).TaintedString -proc writeLn[Ty](f: File, x: varargs[Ty, `$`]) = - for i in items(x): - write(f, i) - write(f, "\n") - proc writeLine*[Ty](f: File, x: varargs[Ty, `$`]) {.inline, tags: [WriteIOEffect], benign.} = ## writes the values `x` to `f` and then writes "\\n". diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html index b8d252cfb..4fd5d8ad3 100644 --- a/nimdoc/testproject/expected/testproject.html +++ b/nimdoc/testproject/expected/testproject.html @@ -190,6 +190,14 @@ function main() { title="p1()">p1
  • addfBug14485
  • +
  • c_printf
  • +
  • c_nonexistant
  • +
  • low
  • +
  • low2
  • @@ -546,6 +554,46 @@ Some proc 7: end of broken html ]#
    + + +
    proc c_printf(frmt: cstring): cint {...}{.importc: "printf", header: "<stdio.h>", varargs,
    +                                 discardable.}
    +
    + +the c printf. etc. + +
    + +
    proc c_nonexistant(frmt: cstring): cint {...}{.importc: "nonexistant", header: "<stdio.h>",
    +                                      varargs, discardable.}
    +
    + + + +
    + +
    proc low[T: Ordinal | enum | range](x: T): T {...}{.magic: "Low", noSideEffect.}
    +
    + +

    Returns the lowest possible value of an ordinal value x. As a special semantic rule, x may also be a type identifier.

    +

    See also:

    + +
    low(2) # => -9223372036854775808
    + +
    + +
    proc low2[T: Ordinal | enum | range](x: T): T {...}{.magic: "Low", noSideEffect.}
    +
    + +

    Returns the lowest possible value of an ordinal value x. As a special semantic rule, x may also be a type identifier.

    +

    See also:

    + +
    low2(2) # => -9223372036854775808
    +

    Example:

    +
    discard "in low2"
    +
    diff --git a/nimdoc/testproject/expected/testproject.idx b/nimdoc/testproject/expected/testproject.idx index 447e9ff59..dd26c03d9 100644 --- a/nimdoc/testproject/expected/testproject.idx +++ b/nimdoc/testproject/expected/testproject.idx @@ -33,6 +33,10 @@ baz testproject.html#baz testproject: baz() z17 testproject.html#z17 testproject: z17() p1 testproject.html#p1 testproject: p1() addfBug14485 testproject.html#addfBug14485 testproject: addfBug14485() +c_printf testproject.html#c_printf,cstring testproject: c_printf(frmt: cstring): cint +c_nonexistant testproject.html#c_nonexistant,cstring testproject: c_nonexistant(frmt: cstring): cint +low testproject.html#low,T testproject: low[T: Ordinal | enum | range](x: T): T +low2 testproject.html#low2,T testproject: low2[T: Ordinal | enum | range](x: T): T bar testproject.html#bar.m testproject: bar(): untyped z16 testproject.html#z16.m testproject: z16() z18 testproject.html#z18.m testproject: z18(): int diff --git a/nimdoc/testproject/expected/theindex.html b/nimdoc/testproject/expected/theindex.html index 3ecf7aab7..1e9e4374f 100644 --- a/nimdoc/testproject/expected/theindex.html +++ b/nimdoc/testproject/expected/theindex.html @@ -129,6 +129,14 @@ function main() {
  • testproject: C_D
  • +
    c_nonexistant:
    +
    c_printf:
    enumValueA:
    +
    low:
    +
    low2:
    myfn:
    • testproject: myfn()
    • diff --git a/nimdoc/testproject/testproject.nim b/nimdoc/testproject/testproject.nim index 90b173403..45bbca223 100644 --- a/nimdoc/testproject/testproject.nim +++ b/nimdoc/testproject/testproject.nim @@ -177,6 +177,36 @@ when true: # issue #14485 7: end of broken html ]# +when true: # procs without `=` (using comment field) + proc c_printf*(frmt: cstring): cint {.importc: "printf", header: "", varargs, discardable.} + ## the c printf. + ## etc. + + proc c_nonexistant*(frmt: cstring): cint {.importc: "nonexistant", header: "", varargs, discardable.} + +when true: # tests RST inside comments + proc low*[T: Ordinal|enum|range](x: T): T {.magic: "Low", noSideEffect.} + ## Returns the lowest possible value of an ordinal value `x`. As a special + ## semantic rule, `x` may also be a type identifier. + ## + ## See also: + ## * `low2(T) <#low2,T>`_ + ## + ## .. code-block:: Nim + ## low(2) # => -9223372036854775808 + + proc low2*[T: Ordinal|enum|range](x: T): T {.magic: "Low", noSideEffect.} = + ## Returns the lowest possible value of an ordinal value `x`. As a special + ## semantic rule, `x` may also be a type identifier. + ## + ## See also: + ## * `low(T) <#low,T>`_ + ## + ## .. code-block:: Nim + ## low2(2) # => -9223372036854775808 + runnableExamples: + discard "in low2" + when true: # (most) macros macro bar*(): untyped = result = newStmtList() From 63d1a0289e872212e836559f92a785df51f4faac Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 29 May 2020 08:10:59 -0700 Subject: [PATCH 0120/3768] fix #14421 items uses lent T (#14447) * fix #14421 items uses lent T for seq + openArray * add -d:nimWorkaround14447 * fix test --- compiler/condsyms.nim | 1 + compiler/lambdalifting.nim | 3 ++- lib/system/iterators.nim | 31 ++++++++++++++++++++++--- testament/important_packages.nim | 4 ++-- tests/destructor/tbintree2.nim | 2 +- tests/destructor/tgcdestructors.nim | 2 +- tests/destructor/tnewruntime_misc.nim | 2 +- tests/destructor/towned_binary_tree.nim | 2 +- tests/destructor/tsimpleclosure.nim | 2 +- tests/destructor/tv2_raise.nim | 2 +- tests/destructor/twidgets_unown.nim | 2 +- 11 files changed, 40 insertions(+), 13 deletions(-) diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 7b05faa87..b7f7100f1 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -118,3 +118,4 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasInvariant") defineSymbol("nimHasStacktraceMsgs") defineSymbol("nimDoesntTrackDefects") + defineSymbol("nimHasLentIterators") diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index fc1ad5e40..c1cd07583 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -291,7 +291,8 @@ proc markAsClosure(g: ModuleGraph; owner: PSym; n: PNode) = if illegalCapture(s): localError(g.config, n.info, ("'$1' is of type <$2> which cannot be captured as it would violate memory" & - " safety, declared here: $3") % [s.name.s, typeToString(s.typ), g.config$s.info]) + " safety, declared here: $3; using '-d:nimWorkaround14447' helps in some cases") % + [s.name.s, typeToString(s.typ), g.config$s.info]) elif owner.typ.callConv notin {ccClosure, ccDefault}: localError(g.config, n.info, "illegal capture '$1' because '$2' has the calling convention: <$3>" % [s.name.s, owner.name.s, CallingConvToStr[owner.typ.callConv]]) diff --git a/lib/system/iterators.nim b/lib/system/iterators.nim index 53b287db0..5d8685e40 100644 --- a/lib/system/iterators.nim +++ b/lib/system/iterators.nim @@ -1,7 +1,32 @@ -iterator items*[T](a: openArray[T]): T {.inline.} = +when defined(nimHasLentIterators) and not defined(nimWorkaround14447): + template lent2(T): untyped = + # xxx this should actually depend on T.sizeof >= thresLentSizeof + # with for example `thresLentSizeof ~= int.sizeof`: + # it may be faster to return by value for small sizes compared to + # forcing a deref; this could be adjusted using profiling. + # However, `simply using `when T.sizeof >= thresLentSizeof: lent T else: T` + # does not work, for a few reasons (eg importc types would cause CT error + # and we can't filter them out without compiles() or some magic. + lent T +else: + template lent2(T): untyped = T + +iterator items*[T: not char](a: openArray[T]): lent2 T {.inline.} = ## Iterates over each item of `a`. var i = 0 - while i < len(a): + let n = len(a) + while i < n: + yield a[i] + inc(i) + +iterator items*[T: char](a: openArray[T]): T {.inline.} = + ## Iterates over each item of `a`. + # a VM bug currently prevents taking address of openArray[char] + # elements converted from a string (would fail in `tests/misc/thallo.nim`) + # in any case there's no performance advantage of returning char by address. + var i = 0 + let n = len(a) + while i < n: yield a[i] inc(i) @@ -179,7 +204,7 @@ iterator mpairs*(a: var cstring): tuple[key: int, val: var char] {.inline.} = yield (i, a[i]) inc(i) -iterator items*[T](a: seq[T]): T {.inline.} = +iterator items*[T](a: seq[T]): lent2 T {.inline.} = ## Iterates over each item of `a`. var i = 0 let L = len(a) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 1d3b1917c..cb436bfb2 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -43,7 +43,7 @@ pkg1 "elvis" pkg1 "fidget", true, "nim c -d:release -r tests/runNative.nim" pkg1 "fragments", false, "nim c -r fragments/dsl.nim" pkg1 "gara" -pkg1 "ggplotnim", true, "nim c -d:noCairo -r tests/tests.nim" +pkg1 "ggplotnim", true, "nim c -d:noCairo -r -d:nimWorkaround14447 tests/tests.nim" # pkg1 "gittyup", true, "nimble test", "https://github.com/disruptek/gittyup" pkg1 "glob" pkg1 "gnuplot" @@ -134,4 +134,4 @@ pkg2 "websocket", false, "nim c websocket.nim" pkg2 "with" pkg2 "ws" pkg2 "yaml" -pkg2 "zero_functional", false, "nim c -r test.nim" +pkg2 "zero_functional", false, "nim c -r -d:nimWorkaround14447 test.nim" diff --git a/tests/destructor/tbintree2.nim b/tests/destructor/tbintree2.nim index e910f430a..6fdda6e54 100644 --- a/tests/destructor/tbintree2.nim +++ b/tests/destructor/tbintree2.nim @@ -1,7 +1,7 @@ discard """ cmd: '''nim c -d:nimAllocStats --newruntime $file''' output: '''0 -(allocCount: 6, deallocCount: 6)''' +(allocCount: 5, deallocCount: 5)''' """ import system / ansi_c diff --git a/tests/destructor/tgcdestructors.nim b/tests/destructor/tgcdestructors.nim index 61d296d2e..4731bf694 100644 --- a/tests/destructor/tgcdestructors.nim +++ b/tests/destructor/tgcdestructors.nim @@ -10,7 +10,7 @@ a: @[4, 2, 3] 0 30 true -(allocCount: 36, deallocCount: 36)''' +(allocCount: 27, deallocCount: 27)''' """ include system / ansi_c diff --git a/tests/destructor/tnewruntime_misc.nim b/tests/destructor/tnewruntime_misc.nim index f6e2a6157..1a5b0e3b0 100644 --- a/tests/destructor/tnewruntime_misc.nim +++ b/tests/destructor/tnewruntime_misc.nim @@ -7,7 +7,7 @@ axc ... destroying GenericObj[T] GenericObj[system.int] test -(allocCount: 17, deallocCount: 15) +(allocCount: 13, deallocCount: 11) 3''' """ diff --git a/tests/destructor/towned_binary_tree.nim b/tests/destructor/towned_binary_tree.nim index 72f7cae0c..320e33759 100644 --- a/tests/destructor/towned_binary_tree.nim +++ b/tests/destructor/towned_binary_tree.nim @@ -1,7 +1,7 @@ discard """ cmd: '''nim c -d:nimAllocStats --newruntime $file''' output: '''31665 -(allocCount: 33335, deallocCount: 33335)''' +(allocCount: 33334, deallocCount: 33334)''' """ # bug #11053 diff --git a/tests/destructor/tsimpleclosure.nim b/tests/destructor/tsimpleclosure.nim index 4916f4bab..35c57a634 100644 --- a/tests/destructor/tsimpleclosure.nim +++ b/tests/destructor/tsimpleclosure.nim @@ -5,7 +5,7 @@ discard """ hello hello hello -(allocCount: 4, deallocCount: 4)''' +(allocCount: 3, deallocCount: 3)''' """ import system / ansi_c diff --git a/tests/destructor/tv2_raise.nim b/tests/destructor/tv2_raise.nim index 4ea94a864..66b0aec30 100644 --- a/tests/destructor/tv2_raise.nim +++ b/tests/destructor/tv2_raise.nim @@ -2,7 +2,7 @@ discard """ valgrind: true cmd: '''nim c -d:nimAllocStats --newruntime $file''' output: '''OK 3 -(allocCount: 8, deallocCount: 5)''' +(allocCount: 7, deallocCount: 4)''' """ import strutils, math diff --git a/tests/destructor/twidgets_unown.nim b/tests/destructor/twidgets_unown.nim index 3a3c4c665..8653d5c28 100644 --- a/tests/destructor/twidgets_unown.nim +++ b/tests/destructor/twidgets_unown.nim @@ -2,7 +2,7 @@ discard """ cmd: '''nim c -d:nimAllocStats --newruntime $file''' output: '''button clicked! -(allocCount: 7, deallocCount: 7)''' +(allocCount: 6, deallocCount: 6)''' """ import system / ansi_c From cb6eb5268fffea891501c0aed7510d7354c16159 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 29 May 2020 13:11:41 -0700 Subject: [PATCH 0121/3768] enable compiler docs with their own index+search (#14493) * enable compiler docs (with its own index) --- tools/kochdocs.nim | 47 ++++++++++++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 20 deletions(-) diff --git a/tools/kochdocs.nim b/tools/kochdocs.nim index bedfcda4a..c39a9c448 100644 --- a/tools/kochdocs.nim +++ b/tools/kochdocs.nim @@ -5,8 +5,7 @@ import "../compiler/nimpaths" const gaCode* = " --doc.googleAnalytics:UA-48159761-1" - # --warning[LockLevel]:off pending #13218 - nimArgs = "--warning[LockLevel]:off --hint[Conf]:off --hint[Path]:off --hint[Processing]:off -d:boot --putenv:nimversion=$#" % system.NimVersion + nimArgs = "--hint:Conf:off --hint:Path:off --hint:Processing:off -d:boot --putenv:nimversion=$#" % system.NimVersion gitUrl = "https://github.com/nim-lang/Nim" docHtmlOutput = "doc/html" webUploadOutput = "web/upload" @@ -231,6 +230,15 @@ proc buildDocSamples(nimArgs, destPath: string) = exec(findNim().quoteShell() & " doc $# -o:$# $#" % [nimArgs, destPath / "docgen_sample.html", "doc" / "docgen_sample.nim"]) +proc buildDocPackages(nimArgs, destPath: string) = + # compiler docs, and later, other packages (perhaps tools, testament etc) + let nim = findNim().quoteShell() + let extra = "-u:boot" + # to avoid broken links to manual from compiler dir, but a multi-package + # structure could be supported later + exec("$1 doc --project --outdir:$2/compiler $3 --git.url:$4 $5 compiler/nim.nim" % + [nim, destPath, nimArgs, gitUrl, extra]) + proc buildDoc(nimArgs, destPath: string) = # call nim for the documentation: var @@ -249,8 +257,7 @@ proc buildDoc(nimArgs, destPath: string) = i.inc for d in items(doc): var nimArgs2 = nimArgs - if d.isRelativeTo("compiler"): - nimArgs2.add " --docroot" + if d.isRelativeTo("compiler"): doAssert false commands[i] = nim & " doc $# --git.url:$# --outdir:$# --index:on $#" % [nimArgs2, gitUrl, destPath, d] i.inc @@ -262,6 +269,11 @@ proc buildDoc(nimArgs, destPath: string) = mexec(commands) exec(nim & " buildIndex -o:$1/theindex.html $1" % [destPath]) + # caveat: this works so long it's called before `buildDocPackages` which + # populates `compiler/` with unrelated idx files that shouldn't be in index, + # so should work in CI but you may need to remove your generated html files + # locally after calling `./koch docs`. The clean fix would be for `idx` files + # to be transient with `--project` (eg all in memory). proc buildPdfDoc*(nimArgs, destPath: string) = createDir(destPath) @@ -296,20 +308,15 @@ proc buildJS(): string = result = getDocHacksJs(nimr = getCurrentDir(), nim) proc buildDocs*(args: string) = - let - a = nimArgs & " " & args - docHackJsSource = buildJS() - docHackJs = docHackJsSource.lastPathPart + let docHackJsSource = buildJS() + template fn(args, dir) = + let dir2 = dir + let args2 = args + createDir(dir2) + buildDocSamples(args2, dir2) + buildDoc(args2, dir2) + buildDocPackages(args2, dir2) + copyFile(docHackJsSource, dir2 / docHackJsSource.lastPathPart) - let docup = webUploadOutput / NimVersion - createDir(docup) - buildDocSamples(a, docup) - buildDoc(a, docup) - - # 'nimArgs' instead of 'a' is correct here because we don't want - # that the offline docs contain the 'gaCode'! - createDir(docHtmlOutput) - buildDocSamples(nimArgs, docHtmlOutput) - buildDoc(nimArgs, docHtmlOutput) - copyFile(docHackJsSource, docHtmlOutput / docHackJs) - copyFile(docHackJsSource, docup / docHackJs) + fn(nimArgs & " " & args, webUploadOutput / NimVersion) + fn(nimArgs, docHtmlOutput) # no `args` to avoid offline docs containing the 'gaCode'! From 3105909f887f2240c2a7a7071b37e1253a70e3e2 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 29 May 2020 23:35:57 +0200 Subject: [PATCH 0122/3768] fixes #14495 [backport:1.2] (#14496) --- compiler/liftdestructors.nim | 8 +++--- tests/arc/tarcmisc.nim | 48 ++++++++++++++++++++++++++++++++---- 2 files changed, 47 insertions(+), 9 deletions(-) diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 5faba79f9..2d8655cae 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -534,8 +534,8 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = body.add genIf(c, cond, actions) body.add newAsgnStmt(x, y) of attachedDestructor: - actions.add newAsgnStmt(x, newNodeIT(nkNilLit, body.info, t)) body.add genIf(c, cond, actions) + body.add newAsgnStmt(x, newNodeIT(nkNilLit, body.info, t)) of attachedDeepCopy: assert(false, "cannot happen") of attachedTrace: if isFinal(elemType): @@ -583,8 +583,8 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = body.add genIf(c, cond, actions) body.add newAsgnStmt(x, y) of attachedDestructor: - actions.add newAsgnStmt(xenv, newNodeIT(nkNilLit, body.info, xenv.typ)) body.add genIf(c, cond, actions) + body.add newAsgnStmt(xenv, newNodeIT(nkNilLit, body.info, xenv.typ)) of attachedDeepCopy: assert(false, "cannot happen") of attachedTrace: body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, genAddrOf(xenv), y) @@ -641,8 +641,8 @@ proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = body.add genIf(c, x, actions) body.add newAsgnStmt(x, y) of attachedDestructor: - actions.add newAsgnStmt(x, newNodeIT(nkNilLit, body.info, t)) body.add genIf(c, x, actions) + body.add newAsgnStmt(x, newNodeIT(nkNilLit, body.info, t)) of attachedDeepCopy: assert(false, "cannot happen") of attachedTrace, attachedDispose: discard @@ -694,8 +694,8 @@ proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = body.add genIf(c, xx, actions) body.add newAsgnStmt(x, y) of attachedDestructor: - actions.add newAsgnStmt(xx, newNodeIT(nkNilLit, body.info, xx.typ)) body.add genIf(c, xx, actions) + body.add newAsgnStmt(xx, newNodeIT(nkNilLit, body.info, xx.typ)) of attachedDeepCopy: assert(false, "cannot happen") of attachedTrace, attachedDispose: discard diff --git a/tests/arc/tarcmisc.nim b/tests/arc/tarcmisc.nim index 24f02959c..898efd138 100644 --- a/tests/arc/tarcmisc.nim +++ b/tests/arc/tarcmisc.nim @@ -4,6 +4,17 @@ discard """ destroyed: false destroyed: false 1 +(x: "0") +(x: "1") +(x: "2") +(x: "3") +(x: "4") +(x: "5") +(x: "6") +(x: "7") +(x: "8") +(x: "9") +(x: "10") closed destroying variable ''' @@ -99,10 +110,10 @@ type MyType = object a: seq[int] -proc re(x: static[string]): static MyType = +proc re(x: static[string]): static MyType = MyType() -proc match(inp: string, rg: static MyType) = +proc match(inp: string, rg: static MyType) = doAssert rg.a.len == 0 match("ac", re"a(b|c)") @@ -125,13 +136,40 @@ var game*: Game #------------------------------------------------------------------------------ # issue #14333 -type +type SimpleLoop = object - + Lsg = object loops: seq[ref SimpleLoop] root: ref SimpleLoop var lsg: Lsg lsg.loops.add lsg.root -echo lsg.loops.len \ No newline at end of file +echo lsg.loops.len + +# bug #14495 +type + Gah = ref object + x: string + +proc bug14495 = + var owners: seq[Gah] + for i in 0..10: + owners.add Gah(x: $i) + + var x: seq[Gah] + for i in 0..10: + x.add owners[i] + + for i in 0..100: + setLen(x, 0) + setLen(x, 10) + + for i in 0..x.len-1: + if x[i] != nil: + echo x[i][] + + for o in owners: + echo o[] + +bug14495() From 7ccc7d7e93e0fdfcf72aed239cf8b5d4c04e7de5 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 30 May 2020 15:32:31 +0200 Subject: [PATCH 0123/3768] fixes #14498 [backport:1.2] (#14503) --- compiler/parampatterns.nim | 12 +++++++++--- compiler/semexprs.nim | 2 +- tests/lent/tnot_allowed_lent.nim | 4 ++-- tests/lent/tnot_allowed_lent2.nim | 14 ++++++++++++++ 4 files changed, 26 insertions(+), 6 deletions(-) create mode 100644 tests/lent/tnot_allowed_lent2.nim diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim index 486f8ff54..33881a7b3 100644 --- a/compiler/parampatterns.nim +++ b/compiler/parampatterns.nim @@ -176,6 +176,7 @@ type arLocalLValue, # is an l-value, but local var; must not escape # its stack frame! arDiscriminant, # is a discriminant + arLentValue, # lent value arStrange # it is a strange beast like 'typedesc[var T]' proc exprRoot*(n: PNode): PSym = @@ -260,9 +261,14 @@ proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult # types that are equal modulo distinction preserve l-value: result = isAssignable(owner, n[1], isUnsafeAddr) of nkHiddenDeref: - if isUnsafeAddr and n[0].typ.kind == tyLent: result = arLValue - elif n[0].typ.kind == tyLent: result = arDiscriminant - else: result = arLValue + let n0 = n[0] + if n0.typ.kind == tyLent: + if isUnsafeAddr or (n0.kind == nkSym and n0.sym.kind == skResult): + result = arLValue + else: + result = arLentValue + else: + result = arLValue of nkDerefExpr, nkHiddenAddr: result = arLValue of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr: diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index e29101385..c5b864d1f 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1712,7 +1712,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = if le == nil: localError(c.config, a.info, "expression has no type") elif (skipTypes(le, {tyGenericInst, tyAlias, tySink}).kind != tyVar and - isAssignable(c, a) == arNone) or + isAssignable(c, a) in {arNone, arLentValue}) or skipTypes(le, abstractVar).kind in {tyOpenArray, tyVarargs}: # Direct assignment to a discriminant is allowed! localError(c.config, a.info, errXCannotBeAssignedTo % diff --git a/tests/lent/tnot_allowed_lent.nim b/tests/lent/tnot_allowed_lent.nim index b144ce36b..2de18d4af 100644 --- a/tests/lent/tnot_allowed_lent.nim +++ b/tests/lent/tnot_allowed_lent.nim @@ -7,10 +7,10 @@ type proc mytest1(s: MyObject, i: int): lent string = ## works fine - if i < s.x.len - 1 and s.x[i] != "": + if i < s.x.len - 1 and s.x[i] != "": result = s.x[i] else: raise newException(KeyError, "err1") - + proc mytest2(s: MyObject, i: int): lent string = ## reject due to if expr if i < s.x.len - 1 and s.x[i] != "": s.x[i] diff --git a/tests/lent/tnot_allowed_lent2.nim b/tests/lent/tnot_allowed_lent2.nim new file mode 100644 index 000000000..e61cae922 --- /dev/null +++ b/tests/lent/tnot_allowed_lent2.nim @@ -0,0 +1,14 @@ +discard """ + errmsg: "'x' cannot be assigned to" + line: 10 +""" + +proc bug14498 = + var a = @['a', 'b', 'c', 'd', 'e', 'f'] + + for x in a: + x = 'c' + + echo a + +bug14498() From 52c3633223005f70bb4ecffa9f86b2e4e845e4a3 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 30 May 2020 19:38:51 +0200 Subject: [PATCH 0124/3768] warn about observerable stores but don't prevent them for 1.2.2 [backport:1.2]; refs https://github.com/nim-lang/RFCs/issues/230 (#14510) --- compiler/ccgcalls.nim | 5 +++-- compiler/lineinfos.nim | 8 ++++++-- tests/ccgbugs/tcodegenbug1.nim | 10 ++++++---- 3 files changed, 15 insertions(+), 8 deletions(-) diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 8f9d879df..e19e96503 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -43,8 +43,9 @@ proc preventNrvo(p: BProc; le, ri: PNode): bool = for i in 1.. 0 or locationEscapes(p, le)) + if canRaiseDisp(p, ri[0]) and + (p.nestedTryStmts.len > 0 or locationEscapes(p, le)): + message(p.config, le.info, warnObservableStores, $le) proc hasNoInit(call: PNode): bool {.inline.} = result = call[0].kind == nkSym and sfNoInit in call[0].sym.flags diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index bb47e947f..bc3c51d53 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -52,7 +52,9 @@ type warnProveInit, warnProveField, warnProveIndex, warnUnreachableElse, warnStaticIndexCheck, warnGcUnsafe, warnGcUnsafe2, warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed, - warnInconsistentSpacing, warnCaseTransition, warnCycleCreated, warnUser, + warnInconsistentSpacing, warnCaseTransition, warnCycleCreated, + warnObservableStores, + warnUser, hintSuccess, hintSuccessX, hintCC, hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded, @@ -119,6 +121,7 @@ const warnInconsistentSpacing: "Number of spaces around '$#' is not consistent", warnCaseTransition: "Potential object case transition, instantiate new object instead", warnCycleCreated: "$1", + warnObservableStores: "observable stores to '$1'", warnUser: "$1", hintSuccess: "operation successful: $#", # keep in sync with `testament.isSuccess` @@ -169,7 +172,8 @@ const "ProveInit", "ProveField", "ProveIndex", "UnreachableElse", "IndexCheck", "GcUnsafe", "GcUnsafe2", "Uninit", "GcMem", "Destructor", "LockLevel", "ResultShadowed", - "Spacing", "CaseTransition", "CycleCreated", "User"] + "Spacing", "CaseTransition", "CycleCreated", + "ObservableStores", "User"] HintsToStr* = [ "Success", "SuccessX", "CC", "LineTooLong", diff --git a/tests/ccgbugs/tcodegenbug1.nim b/tests/ccgbugs/tcodegenbug1.nim index 11846ff95..c62bae1ef 100644 --- a/tests/ccgbugs/tcodegenbug1.nim +++ b/tests/ccgbugs/tcodegenbug1.nim @@ -4,9 +4,9 @@ obj.inner.id = 7 id = 7 obj = (inner: (kind: Just, id: 7)) 2 -(a: "1", b: "2", c: "3") +(a: "a", b: "b", c: "") caught -(a: "1", b: "", c: "3")''' +(a: "a", b: "b", c: "")''' """ # bug #6960 @@ -164,7 +164,8 @@ proc ohmanNoNRVO = discard echo x - doAssert x.c == "3", "shouldn't modify x if f raises" + # once NVRO is sorted out, x.c == "3" + doAssert x.c == "", "shouldn't modify x if f raises" ohmanNoNRVO() @@ -179,4 +180,5 @@ try: except: echo "caught" echo xgg -doAssert xgg.c == "3", "this assert will fail" +# once NVRO is sorted out, xgg.c == "3" +doAssert xgg.c == "", "this assert will fail" From 984af3d64e875016cf00dd2580b0b51f192e1fcd Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 25 May 2020 15:46:55 +0200 Subject: [PATCH 0125/3768] document NVRO and exception handling --- doc/manual.rst | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/doc/manual.rst b/doc/manual.rst index 1472dd4e6..2b9c2b65c 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -3711,6 +3711,71 @@ location is derived from the second parameter (called ``varTy[T, 2]`` which is incompatible with ``varTy[T, 1]``. +NRVO +---- + +The return value is represented inside the body of a routine as the special +`result`:idx: variable. This allows for a mechanism much like C++'s +"named return value optimization" (`NRVO`:idx:). NRVO means that the stores +to ``result`` inside ``p`` directly affect the destination ``dest`` +in ``let/var dest = p(args)`` (definition of ``dest``) and also in ``dest = p(args)`` +(assignment to ``dest``). This is achieved by rewriting ``dest = p(args)`` +to ``p'(args, dest)`` where ``p'`` is a variation of ``p`` that returns ``void`` and +receives a hidden mutable parameter representing ``result``. + +Informally: + +.. code-block:: nim + proc p(): BigT = ... + + var x = p() + x = p() + + # is roughly turned into: + + proc p(result: var BigT) = ... + + var x; p(x) + p(x) + + +Let ``T``'s be ``p``'s return type. NRVO applies for ``T`` +if ``sizeof(T) >= N`` (where ``N`` is implementation dependent), +in other words, it applies for "big" structures. + +If ``p`` can raise an exception, NRVO applies regardless. This can produce +observable differences in behavior: + +.. code-block:: nim + + type + BigT = array[16, int] + + proc p(raiseAt: int): BigT = + for i in 0..high(result): + if i == raiseAt: raise newException(ValueError, "interception") + result[i] = i + + proc main = + var x: BigT + try: + x = p(8) + except ValueError: + doAssert x == [0, 1, 2, 3, 4, 5, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0] + + main() + + +However, the current implementation produces a warning in these cases. +There are different ways to deal with this warning: + +1. Disable the warning via ``{.push warning[ObservableStores]: off.}`` ... ``{.pop.}``. + Then one may need to ensure that ``p`` only raises *before* any stores to ``result`` + happen. + +2. One can use a temporary helper variable, for example instead of ``x = p(8)`` + use ``let tmp = p(8); x = tmp``. + Overloading of the subscript operator ------------------------------------- From 177c7f96e93c6b8d9d206cd13f22c0d0d7d4192e Mon Sep 17 00:00:00 2001 From: Araq Date: Sat, 30 May 2020 19:55:01 +0200 Subject: [PATCH 0126/3768] spec: be explicit that NRVO will evolve further --- doc/manual.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/manual.rst b/doc/manual.rst index 2b9c2b65c..f70c484a2 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -3714,6 +3714,10 @@ location is derived from the second parameter (called NRVO ---- +**Note**: This section describes the current implementation. This part +of the language specification will be changed. +See https://github.com/nim-lang/RFCs/issues/230 for more information. + The return value is represented inside the body of a routine as the special `result`:idx: variable. This allows for a mechanism much like C++'s "named return value optimization" (`NRVO`:idx:). NRVO means that the stores From 5150186c68af1fbcc1185d815f52024c4647fe0b Mon Sep 17 00:00:00 2001 From: Danil Yarantsev Date: Sun, 31 May 2020 13:43:27 +0300 Subject: [PATCH 0127/3768] Disable unused warnings for await in async macro (#14517) * Disable unused warnings for await in async macro * Remove used for the error template --- lib/pure/asyncmacro.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pure/asyncmacro.nim b/lib/pure/asyncmacro.nim index afc28bbdf..77db80bc6 100644 --- a/lib/pure/asyncmacro.nim +++ b/lib/pure/asyncmacro.nim @@ -267,7 +267,7 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} = static: error "await expects Future[T], got " & $typeof(f) - template await[T](f: Future[T]): auto = + template await[T](f: Future[T]): auto {.used.} = var internalTmpFuture: FutureBase = f yield internalTmpFuture (cast[type(f)](internalTmpFuture)).read() @@ -355,4 +355,4 @@ macro multisync*(prc: untyped): untyped = # overload for await as a fallback handler, based on the yglukhov's patch to chronos: https://github.com/status-im/nim-chronos/pull/47 # template await*(f: typed): untyped = # static: - # error "await only available within {.async.}" \ No newline at end of file + # error "await only available within {.async.}" From 004b0759c5743f4b50766a14b0a47477b72bbac9 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 31 May 2020 14:57:16 -0700 Subject: [PATCH 0128/3768] fix CI doc windows: style error in lib/std/time_t.nim (#14523) --- lib/std/time_t.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/std/time_t.nim b/lib/std/time_t.nim index 37918ee6c..5fd2752c7 100644 --- a/lib/std/time_t.nim +++ b/lib/std/time_t.nim @@ -9,8 +9,8 @@ when defined(nimdoc): type - impl = distinct int64 - Time* = impl ## \ + Impl = distinct int64 + Time* = Impl ## \ ## Wrapper for ``time_t``. On posix, this is an alias to ``posix.Time``. elif defined(windows): when defined(i386) and defined(gcc): From 5d0cf883c15b6bd348ecfe7865cb85ca05e341de Mon Sep 17 00:00:00 2001 From: Danil Yarantsev Date: Mon, 1 Jun 2020 12:56:07 +0300 Subject: [PATCH 0129/3768] Disable unused warnings for error await template too (#14531) --- lib/pure/asyncmacro.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/asyncmacro.nim b/lib/pure/asyncmacro.nim index 77db80bc6..cbed6372c 100644 --- a/lib/pure/asyncmacro.nim +++ b/lib/pure/asyncmacro.nim @@ -263,7 +263,7 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} = # based on the yglukhov's patch to chronos: https://github.com/status-im/nim-chronos/pull/47 # however here the overloads are placed inside each expanded async var awaitDefinition = quote: - template await(f: typed): untyped = + template await(f: typed): untyped {.used.} = static: error "await expects Future[T], got " & $typeof(f) From 8710395a0f7b57bab755ea826a82e3ceef85aa52 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 1 Jun 2020 12:33:13 +0200 Subject: [PATCH 0130/3768] fixes #14514 [backport:1.2] (#14533) --- compiler/ccgcalls.nim | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index e19e96503..10f5ef638 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -43,7 +43,9 @@ proc preventNrvo(p: BProc; le, ri: PNode): bool = for i in 1.. 0 or locationEscapes(p, le)): message(p.config, le.info, warnObservableStores, $le) From 0489e76da768502aa7bddea1cbab3bfa5f6b22a2 Mon Sep 17 00:00:00 2001 From: Ico Doornekamp Date: Mon, 1 Jun 2020 14:02:17 +0200 Subject: [PATCH 0131/3768] manual: removed subjective phrase from 'macros' section (#14536) Removed phrase "However, this is no real restriction because Nim's syntax is flexible enough anyway." from the manual - I find it very subjective, and I sometimes *do* find myself restricted by Nim's syntax when writing DSLs --- doc/manual.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/manual.rst b/doc/manual.rst index f70c484a2..451359dca 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -5305,8 +5305,7 @@ twice: this process iterates. While macros enable advanced compile-time code transformations, they -cannot change Nim's syntax. However, this is no real restriction because -Nim's syntax is flexible enough anyway. +cannot change Nim's syntax. Debug Example ------------- From 5900d8442c6bf41bfaab02ae877ebf23680680bb Mon Sep 17 00:00:00 2001 From: Khronos <39792509+Khronos31@users.noreply.github.com> Date: Mon, 1 Jun 2020 21:06:25 +0900 Subject: [PATCH 0132/3768] Fix a problem with extra build commands. (#14528) When --compileOnly is on, extraCmds are not added to JsonBuildInstructions. ``` $ echo 'echo 123'>test.nim $ nim c --debuginfo --nimcache:cache test $ ls cache test test.dSYM test.nim $ tail -n3 cache/test.json "extraCmds": ["dsymutil /Users/khronos31/src/tmp/test"], "stdinInput": false } ``` ``` $ echo 'echo 123'>test.nim $ nim c --debuginfo --nimcache:cache --compileOnly test $ nim jsonscript --nimcache:cache test $ ls cache test test.nim $ tail -n3 cache/test.json "extraCmds": [], "stdinInput": false } ``` --- compiler/extccomp.nim | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 4c5b195ab..7757a9f9d 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -767,19 +767,16 @@ template tryExceptOSErrorMessage(conf: ConfigRef; errorPrefix: string = "", body (ose.msg & " " & $ose.errorCode)) raise +proc getExtraCmds(conf: ConfigRef; output: AbsoluteFile): seq[string] = + when defined(macosx): + if optCDebug in conf.globalOptions and optGenStaticLib notin conf.globalOptions: + # if needed, add an option to skip or override location + result.add "dsymutil " & $(output).quoteShell + proc execLinkCmd(conf: ConfigRef; linkCmd: string) = tryExceptOSErrorMessage(conf, "invocation of external linker program failed."): execExternalProgram(conf, linkCmd, hintLinking) -proc maybeRunDsymutil(conf: ConfigRef; exe: AbsoluteFile) = - when defined(osx): - if optCDebug in conf.globalOptions and optGenStaticLib notin conf.globalOptions: - # if needed, add an option to skip or override location - let cmd = "dsymutil " & $(exe).quoteShell - conf.extraCmds.add cmd - tryExceptOSErrorMessage(conf, "invocation of dsymutil failed."): - execExternalProgram(conf, cmd, hintExecuting) - proc execCmdsInParallel(conf: ConfigRef; cmds: seq[string]; prettyCb: proc (idx: int)) = let runCb = proc (idx: int, p: Process) = let exitCode = p.peekExitCode @@ -850,6 +847,7 @@ proc displayProgressCC(conf: ConfigRef, path, compileCmd: string): string = proc callCCompiler*(conf: ConfigRef) = var linkCmd: string + extraCmds: seq[string] if conf.globalOptions * {optCompileOnly, optGenScript} == {optCompileOnly}: return # speed up that call if only compiling and no script shall be # generated @@ -920,6 +918,7 @@ proc callCCompiler*(conf: ConfigRef) = let mainOutput = if optGenScript notin conf.globalOptions: conf.prepareToWriteOutput else: AbsoluteFile(conf.projectName) linkCmd = getLinkCmd(conf, mainOutput, objfiles) + extraCmds = getExtraCmds(conf, mainOutput) if optCompileOnly notin conf.globalOptions: const MaxCmdLen = when defined(windows): 8_000 else: 32_000 if linkCmd.len > MaxCmdLen: @@ -929,7 +928,8 @@ proc callCCompiler*(conf: ConfigRef) = linkViaResponseFile(conf, linkCmd) else: execLinkCmd(conf, linkCmd) - maybeRunDsymutil(conf, mainOutput) + for cmd in extraCmds: + execExternalProgram(conf, cmd, hintExecuting) else: linkCmd = "" if optGenScript in conf.globalOptions: @@ -1017,7 +1017,7 @@ proc writeJsonBuildInstructions*(conf: ConfigRef) = str getLinkCmd(conf, conf.absOutFile, objfiles) lit ",\L\"extraCmds\": " - lit $(%* conf.extraCmds) + lit $(%* getExtraCmds(conf, conf.absOutFile)) lit ",\L\"stdinInput\": " lit $(%* conf.projectIsStdin) From 00ec43305e51c4b3c0fc4f288a775f90189a4a7c Mon Sep 17 00:00:00 2001 From: Danil Yarantsev Date: Mon, 1 Jun 2020 16:06:45 +0300 Subject: [PATCH 0133/3768] Fix `compiles` for nimsuggest [backport] (#14527) * Handle nimsuggest in tryExpr * Added test --- compiler/semexprs.nim | 7 +++++++ nimsuggest/tests/tchk_compiles.nim | 8 ++++++++ 2 files changed, 15 insertions(+) create mode 100644 nimsuggest/tests/tchk_compiles.nim diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index c5b864d1f..72d407cf7 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2062,6 +2062,10 @@ proc semQuoteAst(c: PContext, n: PNode): PNode = proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = # watch out, hacks ahead: + when defined(nimsuggest): + # Remove the error hook so nimsuggest doesn't report errors there + let tempHook = c.graph.config.structuredErrorHook + c.graph.config.structuredErrorHook = nil let oldErrorCount = c.config.errorCounter let oldErrorMax = c.config.errorMax let oldCompilesId = c.compilesContextId @@ -2111,6 +2115,9 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = c.config.m.errorOutputs = oldErrorOutputs c.config.errorCounter = oldErrorCount c.config.errorMax = oldErrorMax + when defined(nimsuggest): + # Restore the error hook + c.graph.config.structuredErrorHook = tempHook proc semCompiles(c: PContext, n: PNode, flags: TExprFlags): PNode = # we replace this node by a 'true' or 'false' node: diff --git a/nimsuggest/tests/tchk_compiles.nim b/nimsuggest/tests/tchk_compiles.nim new file mode 100644 index 000000000..887a947b5 --- /dev/null +++ b/nimsuggest/tests/tchk_compiles.nim @@ -0,0 +1,8 @@ +discard compiles(2 + "hello") + +#[!]# +discard """ +$nimsuggest --tester $file +>chk $1 +chk;;skUnknown;;;;Hint;;???;;0;;-1;;"tchk_compiles [Processing]";;0 +""" From 0db148163b3c3eef6206ea08a94fea06c4d226a6 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 1 Jun 2020 15:41:14 +0200 Subject: [PATCH 0134/3768] fixes --warningAsError implementation (#14538) --- compiler/msgs.nim | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 0e64d9327..c4658abcf 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -494,8 +494,12 @@ proc liMessage(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string, of warnMin..warnMax: sev = Severity.Warning ignoreMsg = not conf.hasWarn(msg) + if msg in conf.warningAsErrors: + ignoreMsg = false + title = ErrorTitle + else: + title = WarningTitle if not ignoreMsg: writeContext(conf, info) - title = if msg in conf.warningAsErrors: ErrorTitle else: WarningTitle color = WarningColor inc(conf.warnCounter) of hintMin..hintMax: From 0a27cca4b50e73beff5dbef84e6de6ab9a4f9f21 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 1 Jun 2020 06:56:29 -0700 Subject: [PATCH 0135/3768] runnableExamples: correctly handle multiline string litterals (#14492) * runnableExamples: correctly handle multiline string litterals * address comments: improve doc comments + variable namings --- compiler/renderverbatim.nim | 83 ++++++++++++++++++-- nimdoc/testproject/expected/testproject.html | 45 +++++++++++ nimdoc/testproject/expected/testproject.idx | 1 + nimdoc/testproject/expected/theindex.html | 4 + nimdoc/testproject/testproject.nim | 40 ++++++++++ 5 files changed, 168 insertions(+), 5 deletions(-) diff --git a/compiler/renderverbatim.nim b/compiler/renderverbatim.nim index 974454456..d20ee1549 100644 --- a/compiler/renderverbatim.nim +++ b/compiler/renderverbatim.nim @@ -4,8 +4,10 @@ from xmltree import addEscaped import ast, options, msgs import packages/docutils/highlite -# import compiler/renderer -import renderer +const isDebug = false +when isDebug: + import renderer + import astalgo proc lastNodeRec(n: PNode): PNode = result = n @@ -23,6 +25,66 @@ proc isInIndentationBlock(src: string, indent: int): bool = if src[j] != ' ': return false return true +type LineData = object + ## keep track of which lines are starting inside a multiline doc comment. + ## We purposefully avoid re-doing parsing which is already done (we get a PNode) + ## so we don't worry about whether we're inside (nested) doc comments etc. + ## But we sill need some logic to disambiguate different multiline styles. + conf: ConfigRef + lineFirst: int + lines: seq[bool] + ## lines[index] is true if line `lineFirst+index` starts inside a multiline string + ## Using a HashSet (extra dependency) would simplify but not by much. + +proc tripleStrLitStartsAtNextLine(conf: ConfigRef, n: PNode): bool = + # enabling TLineInfo.offsetA,offsetB would probably make this easier + const tripleQuote = "\"\"\"" + let src = sourceLine(conf, n.info) + let col = n.info.col + doAssert src.continuesWith(tripleQuote, col) # sanity check + var i = col + 3 + var onlySpace = true + while true: + if src.len <= i: + doAssert src.len == i + return onlySpace + elif src.continuesWith(tripleQuote, i) and (src.len == i+3 or src[i+3] != '\"'): + return false # triple lit is in 1 line + elif src[i] != ' ': onlySpace = false + i.inc + +proc visitMultilineStrings(ldata: var LineData, n: PNode) = + var cline = ldata.lineFirst + + template setLine() = + let index = cline - ldata.lineFirst + if ldata.lines.len < index+1: ldata.lines.setLen index+1 + ldata.lines[index] = true + + case n.kind + of nkTripleStrLit: + # same logic should be applied for any multiline token + # we could also consider nkCommentStmt but right now we just assume doc comments, + # unlike triple string litterals, don't de-indent from runnableExamples. + cline = n.info.line.int + if tripleStrLitStartsAtNextLine(ldata.conf, n): + cline.inc + setLine() + for ai in n.strVal: + case ai + of '\n': + cline.inc + setLine() + else: discard + else: + for i in 0..= ldata.lines.len: false + else: ldata.lines[index] + proc extractRunnableExamplesSource*(conf: ConfigRef; n: PNode): string = ## TLineInfo.offsetA,offsetB would be cleaner but it's only enabled for nimpretty, ## we'd need to check performance impact to enable it for nimdoc. @@ -39,20 +101,31 @@ proc extractRunnableExamplesSource*(conf: ConfigRef; n: PNode): string = assert true ]# first.line = n[0].info.line + 1 - # first.col = n[0].info.col + 1 # anything with `col > n[0].col` is part of runnableExamples let last = n.lastNodeRec.info var info = first var indent = info.col let numLines = numLines(conf, info.fileIndex).uint16 var lastNonemptyPos = 0 + + var ldata = LineData(lineFirst: first.line.int, conf: conf) + visitMultilineStrings(ldata, n[^1]) + when isDebug: + debug(n) + for i in 0.. last.line and not isInIndentationBlock(src, indent): + let special = startOfLineInsideTriple(ldata, line.int) + if line > last.line and not special and not isInIndentationBlock(src, indent): break if line > first.line: result.add "\n" - if src.len > indent: + if special: + result.add src + lastNonemptyPos = result.len + elif src.len > indent: result.add src[indent..^1] lastNonemptyPos = result.len result.setLen lastNonemptyPos diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html index 4fd5d8ad3..7bd2e64a3 100644 --- a/nimdoc/testproject/expected/testproject.html +++ b/nimdoc/testproject/expected/testproject.html @@ -198,6 +198,8 @@ function main() { title="low[T: Ordinal | enum | range](x: T): T">low
    • low2
    • +
    • tripleStrLitTest
    @@ -594,6 +596,49 @@ the c printf. etc.

    Example:

    discard "in low2"
    +
    + +
    proc tripleStrLitTest() {...}{.raises: [], tags: [].}
    +
    + + +

    Example:

    +
    ## mullitline string litterals are tricky as their indentation can span
    +## below that of the runnableExamples
    +let s1a = """
    +should appear at indent 0
    +  at indent 2
    +at indent 0
    +"""
    +# make sure this works too
    +let s1b = """start at same line
    +  at indent 2
    +at indent 0
    +""" # comment after
    +let s2 = """sandwich """
    +let s3 = """"""
    +when false:
    +  let s5 = """
    +        in s5 """
    +
    +let s3b = ["""
    +%!? #[...] # inside a multiline ...
    +""", "foo"]
    +
    +## make sure handles trailing spaces
    +let s4 = """ 
    +"""
    +
    +let s5 = """ x
    +"""
    +let s6 = """ ""
    +"""
    +let s7 = """"""""""
    +let s8 = ["""""""""", """
    +  """ ]
    +discard
    +# should be in
    +
    diff --git a/nimdoc/testproject/expected/testproject.idx b/nimdoc/testproject/expected/testproject.idx index dd26c03d9..46798f314 100644 --- a/nimdoc/testproject/expected/testproject.idx +++ b/nimdoc/testproject/expected/testproject.idx @@ -37,6 +37,7 @@ c_printf testproject.html#c_printf,cstring testproject: c_printf(frmt: cstring): c_nonexistant testproject.html#c_nonexistant,cstring testproject: c_nonexistant(frmt: cstring): cint low testproject.html#low,T testproject: low[T: Ordinal | enum | range](x: T): T low2 testproject.html#low2,T testproject: low2[T: Ordinal | enum | range](x: T): T +tripleStrLitTest testproject.html#tripleStrLitTest testproject: tripleStrLitTest() bar testproject.html#bar.m testproject: bar(): untyped z16 testproject.html#z16.m testproject: z16() z18 testproject.html#z18.m testproject: z18(): int diff --git a/nimdoc/testproject/expected/theindex.html b/nimdoc/testproject/expected/theindex.html index 1e9e4374f..14d07bfd9 100644 --- a/nimdoc/testproject/expected/theindex.html +++ b/nimdoc/testproject/expected/theindex.html @@ -213,6 +213,10 @@ function main() {
  • testproject: testNimDocTrailingExample()
  • +
    tripleStrLitTest:
    z1:
    • testproject: z1(): Foo
    • diff --git a/nimdoc/testproject/testproject.nim b/nimdoc/testproject/testproject.nim index 45bbca223..b05754d51 100644 --- a/nimdoc/testproject/testproject.nim +++ b/nimdoc/testproject/testproject.nim @@ -207,6 +207,46 @@ when true: # tests RST inside comments runnableExamples: discard "in low2" +when true: # multiline string litterals + proc tripleStrLitTest*() = + runnableExamples: + ## mullitline string litterals are tricky as their indentation can span + ## below that of the runnableExamples + let s1a = """ +should appear at indent 0 + at indent 2 +at indent 0 +""" + # make sure this works too + let s1b = """start at same line + at indent 2 +at indent 0 +""" # comment after + let s2 = """sandwich """ + let s3 = """""" + when false: + let s5 = """ + in s5 """ + + let s3b = [""" +%!? #[...] # inside a multiline ... +""", "foo"] + + ## make sure handles trailing spaces + let s4 = """ +""" + + let s5 = """ x +""" + let s6 = """ "" +""" + let s7 = """""""""" + let s8 = ["""""""""", """ + """ ] + discard + # should be in + # should be out + when true: # (most) macros macro bar*(): untyped = result = newStmtList() From 4bb301dbe2a8fcb9e019e286fdc0cbf622e84434 Mon Sep 17 00:00:00 2001 From: Frank Paulo Filho Date: Mon, 1 Jun 2020 09:57:16 -0400 Subject: [PATCH 0136/3768] Make build_all.sh file executable (#14518) --- build_all.sh | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100644 => 100755 build_all.sh diff --git a/build_all.sh b/build_all.sh old mode 100644 new mode 100755 From 3698f197f10b775cbe0824936637b7e7d294eb19 Mon Sep 17 00:00:00 2001 From: Danil Yarantsev Date: Mon, 1 Jun 2020 18:07:01 +0300 Subject: [PATCH 0137/3768] Change severity of template instantiation message [backport] (#14526) * Change severity of template instatiation msg * Add a test * Trailing newline argh --- compiler/msgs.nim | 2 +- nimsuggest/tests/ttempl_inst.nim | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 nimsuggest/tests/ttempl_inst.nim diff --git a/compiler/msgs.nim b/compiler/msgs.nim index c4658abcf..eca3b0276 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -421,7 +421,7 @@ proc writeContext(conf: ConfigRef; lastinfo: TLineInfo) = if context.info != lastinfo and context.info != info: if conf.structuredErrorHook != nil: conf.structuredErrorHook(conf, context.info, instantiationFrom, - Severity.Error) + Severity.Hint) else: let message = if context.detail == "": instantiationFrom diff --git a/nimsuggest/tests/ttempl_inst.nim b/nimsuggest/tests/ttempl_inst.nim new file mode 100644 index 000000000..ed04a67ce --- /dev/null +++ b/nimsuggest/tests/ttempl_inst.nim @@ -0,0 +1,13 @@ +template foo() = + {.warning: "foo".} + +foo() + +#[!]# +discard """ +$nimsuggest --tester $file +>chk $1 +chk;;skUnknown;;;;Hint;;???;;0;;-1;;"ttempl_inst [Processing]";;0 +chk;;skUnknown;;;;Hint;;$file;;4;;3;;"template/generic instantiation from here";;0 +chk;;skUnknown;;;;Warning;;$file;;2;;11;;"foo [User]";;0 +""" From 75e579ff8ec3b5b3659612ea24f8d2b8c0e20230 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 1 Jun 2020 19:04:28 +0200 Subject: [PATCH 0138/3768] parser.nim: minor refactorings (#14540) --- compiler/parser.nim | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/compiler/parser.nim b/compiler/parser.nim index 9d1f6f97a..8f7eb1226 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -740,10 +740,6 @@ proc commandParam(p: var TParser, isFirstParam: var bool; mode: TPrimaryMode): P result.add(parseExpr(p)) isFirstParam = false -const - tkTypeClasses = {tkRef, tkPtr, tkVar, tkStatic, tkType, - tkEnum, tkTuple, tkObject, tkProc} - proc commandExpr(p: var TParser; r: PNode; mode: TPrimaryMode): PNode = result = newNodeP(nkCommand, p) result.add(r) @@ -797,7 +793,8 @@ proc primarySuffix(p: var TParser, r: PNode, break result = namedParams(p, result, nkCurlyExpr, tkCurlyRi) of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast, - tkOpr, tkDotDot, tkTypeClasses - {tkRef, tkPtr}: + tkOpr, tkDotDot, tkVar, tkStatic, tkType, tkEnum, tkTuple, + tkObject, tkProc: # XXX: In type sections we allow the free application of the # command syntax, with the exception of expressions such as # `foo ref` or `foo ptr`. Unfortunately, these two are also @@ -1270,7 +1267,7 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode = result.add(primary(p, pmNormal)) return - case p.tok.tokType: + case p.tok.tokType of tkTuple: result = parseTuple(p, mode == pmTypeDef) of tkProc: result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef}, nkLambda) of tkFunc: result = parseProcExpr(p, mode notin {pmTypeDesc, pmTypeDef}, nkFuncDef) @@ -1392,7 +1389,7 @@ proc postExprBlocks(p: var TParser, x: PNode): PNode = getTok(p) nextBlock = parseDoBlock(p, info) else: - case nextToken: + case nextToken of tkOf: nextBlock = newNodeP(nkOfBranch, p) exprList(p, tkColon, nextBlock) @@ -1990,8 +1987,8 @@ proc parseObject(p: var TParser): PNode = # an initial IND{>} HAS to follow: if not realInd(p): result.add(p.emptyNode) - return - result.add(parseObjectPart(p)) + else: + result.add(parseObjectPart(p)) proc parseTypeClassParam(p: var TParser): PNode = let modifier = case p.tok.tokType From 3cf88c2b4955f330e147c98064969626719790de Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 1 Jun 2020 10:21:41 -0700 Subject: [PATCH 0139/3768] walkDirRecFilter, update doc CI filter, compiler/index.nim for docs + various other fixes (#14501) * update doc CI filter to include the files mostly likely to require doc rebuild * remove code duplication in ./config/nimdoc.cfg; show link to compiler docs, various fixes * walkDirRecFilter, factor nativeToUnixPath workaround * glob for getRst2html * docslocal: 40s to build all docs * revert code dedup in github actions which did not work alas... * fixups --- .github/workflows/ci_docs.yml | 24 ++++---- compiler/docgen.nim | 21 ++++--- compiler/index.nim | 18 ++++++ config/nimdoc.cfg | 55 +++++------------- doc/idetools.rst | 2 + koch.nim | 7 +++ lib/std/private/globs.nim | 54 +++++++++++++++++ tests/misc/trunner.nim | 7 +-- tools/kochdocs.nim | 105 ++++++++++++++++------------------ tools/nimblepkglist.nim | 3 + 10 files changed, 170 insertions(+), 126 deletions(-) create mode 100644 compiler/index.nim create mode 100644 lib/std/private/globs.nim diff --git a/.github/workflows/ci_docs.yml b/.github/workflows/ci_docs.yml index a1e0920b6..c5e23fbed 100644 --- a/.github/workflows/ci_docs.yml +++ b/.github/workflows/ci_docs.yml @@ -1,18 +1,27 @@ name: Nim Docs CI on: push: - # Run only on changes on these files paths: - - 'lib/**.nim' + - 'compiler/docgen.nim' + - 'compiler/renderverbatim.nim' - 'doc/**.rst' - 'doc/nimdoc.css' + - 'lib/**.nim' + - 'nimdoc/testproject/expected/testproject.html' + - 'tools/dochack/dochack.nim' + - 'tools/kochdocs.nim' - '.github/workflows/ci_docs.yml' - pull_request: # Run only on changes on these files paths: - - 'lib/**.nim' + - 'compiler/docgen.nim' + - 'compiler/renderverbatim.nim' - 'doc/**.rst' + - 'doc/nimdoc.css' + - 'lib/**.nim' + - 'nimdoc/testproject/expected/testproject.html' + - 'tools/dochack/dochack.nim' + - 'tools/kochdocs.nim' - '.github/workflows/ci_docs.yml' jobs: @@ -115,13 +124,6 @@ jobs: shell: bash run: ./koch doc --git.commit:devel - - name: 'Prepare documentation for deployment' - if: | - github.event_name == 'push' && github.ref == 'refs/heads/devel' && - matrix.target == 'linux' - shell: bash - run: cp -f doc/html/{overview,index}.html - - name: 'Publish documentation to Github Pages' if: | github.event_name == 'push' && github.ref == 'refs/heads/devel' && diff --git a/compiler/docgen.nim b/compiler/docgen.nim index ffafb1fcb..535b11ce7 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -19,6 +19,8 @@ import typesrenderer, astalgo, lineinfos, intsets, pathutils, trees, tables, nimpaths, renderverbatim +from std/private/globs import nativeToUnixPath + const exportSection = skField docCmdSkip = "skip" @@ -53,12 +55,6 @@ type PDoc* = ref TDocumentor ## Alias to type less. -proc nativeToUnix(path: string): string = - doAssert not path.isAbsolute # absolute files need more care for the drive - when DirSep == '\\': - result = replace(path, '\\', '/') - else: result = path - proc docOutDir(conf: ConfigRef, subdir: RelativeDir = RelativeDir""): AbsoluteDir = if not conf.outDir.isEmpty: conf.outDir else: conf.projectPath / subdir @@ -97,7 +93,7 @@ proc presentationPath*(conf: ConfigRef, file: AbsoluteFile, isTitle = false): Re if isAbsolute(result.string): result = file.string.splitPath()[1].RelativeFile if isTitle: - result = result.string.nativeToUnix.RelativeFile + result = result.string.nativeToUnixPath.RelativeFile else: result = result.string.replace("..", dotdotMangle).RelativeFile doAssert not result.isEmpty @@ -1244,20 +1240,23 @@ proc genOutFile(d: PDoc): Rope = # Extract the title. Non API modules generate an entry in the index table. if d.meta[metaTitle].len != 0: title = d.meta[metaTitle] - let external = presentationPath(d.conf, AbsoluteFile d.filename).changeFileExt(HtmlExt).string.nativeToUnix + let external = presentationPath(d.conf, AbsoluteFile d.filename).changeFileExt(HtmlExt).string.nativeToUnixPath setIndexTerm(d[], external, "", title) else: # Modules get an automatic title for the HTML, but no entry in the index. # better than `extractFilename(changeFileExt(d.filename, ""))` as it disambiguates dups title = $presentationPath(d.conf, AbsoluteFile d.filename, isTitle = true).changeFileExt("") - let bodyname = if d.hasToc and not d.isPureRst: "doc.body_toc_group" + var groupsection = getConfigVar(d.conf, "doc.body_toc_groupsection") + let bodyname = if d.hasToc and not d.isPureRst: + groupsection.setLen 0 + "doc.body_toc_group" elif d.hasToc: "doc.body_toc" else: "doc.body_no_toc" content = ropeFormatNamedVars(d.conf, getConfigVar(d.conf, bodyname), ["title", - "tableofcontents", "moduledesc", "date", "time", "content", "deprecationMsg", "theindexhref"], + "tableofcontents", "moduledesc", "date", "time", "content", "deprecationMsg", "theindexhref", "body_toc_groupsection"], [title.rope, toc, d.modDesc, rope(getDateStr()), - rope(getClockStr()), code, d.modDeprecationMsg, relLink(d.conf.outDir, d.destFile, theindexFname.RelativeFile)]) + rope(getClockStr()), code, d.modDeprecationMsg, relLink(d.conf.outDir, d.destFile, theindexFname.RelativeFile), groupsection.rope]) if optCompileOnly notin d.conf.globalOptions: # XXX what is this hack doing here? 'optCompileOnly' means raw output!? code = ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.file"), [ diff --git a/compiler/index.nim b/compiler/index.nim new file mode 100644 index 000000000..2c2a34fb5 --- /dev/null +++ b/compiler/index.nim @@ -0,0 +1,18 @@ +##[ +This module only exists to generate docs for the compiler. + +## links +* [main docs](../lib.html) +* [compiler user guide](../nimc.html) +* [Internals of the Nim Compiler](../intern.html) +]## + +#[ +note: this is named `index` so that navigating to https://nim-lang.github.io/Nim/compiler/ +will work. + +xxx this should also import other modules, not transitively imported by `compiler/nim.nim`, +eg `evalffi`, otherwise these aren't shown. A glob could be used at CT. +]# + +import nim diff --git a/config/nimdoc.cfg b/config/nimdoc.cfg index 836b19ab6..945a3dc6c 100644 --- a/config/nimdoc.cfg +++ b/config/nimdoc.cfg @@ -81,42 +81,14 @@ $content
    """ -doc.body_toc = """ -
    -
    -
    - -     Dark Mode +doc.body_toc_groupsection = """ +
    + Group by: +
    - -
    - Search: -
    - $tableofcontents -
    -
    -
    - $deprecationMsg -

    $moduledesc

    - $content -
    -
    """ @if boot: @@ -145,19 +117,16 @@ doc.body_toc_group = """
  • Index
  • +
  • + Compiler docs +
  • Search:
    -
    - Group by: - -
    + $body_toc_groupsection $tableofcontents
    @@ -211,6 +180,8 @@ doc.body_toc_group = """ """ @end +doc.body_toc %= "${doc.body_toc_group}" # should only be used for boot + doc.body_no_toc = """ $moduledesc $content diff --git a/doc/idetools.rst b/doc/idetools.rst index ebb140c5c..e83bfe935 100644 --- a/doc/idetools.rst +++ b/doc/idetools.rst @@ -13,6 +13,8 @@ "yes, I'm the creator" -- Araq, 2013-07-26 19:28:32.

    +Note: this is mostly outdated, see instead `nimsuggest `_ + Nim differs from many other compilers in that it is really fast, and being so fast makes it suited to provide external queries for text editors about the source code being written. Through the diff --git a/koch.nim b/koch.nim index ee7c8006a..c1d8df8da 100644 --- a/koch.nim +++ b/koch.nim @@ -651,6 +651,13 @@ when isMainModule: of "latest": latest = true of "stable": latest = false of "nim": nimExe = op.val.absolutePath # absolute so still works with changeDir + of "docslocal": + # undocumented for now, allows to rebuild local docs in < 40s as follows: + # `./koch --nim:$nimb --docslocal:htmldocs2 --doccmd:skip --warnings:off --hints:off` + # whereas `./koch docs` takes 190s; useful for development. + doAssert op.val.len > 0 + buildDocsDir(op.cmdLineRest, op.val) + break else: showHelp() of cmdArgument: case normalize(op.key) diff --git a/lib/std/private/globs.nim b/lib/std/private/globs.nim new file mode 100644 index 000000000..e697ca91c --- /dev/null +++ b/lib/std/private/globs.nim @@ -0,0 +1,54 @@ +##[ +unstable API, internal use only for now. +this can eventually be moved to std/os and `walkDirRec` can be implemented in terms of this +to avoid duplication +]## + +import std/[os,strutils] + +type + PathEntry* = object + kind*: PathComponent + path*: string + +iterator walkDirRecFilter*(dir: string, follow: proc(entry: PathEntry): bool = nil, + relative = false, checkDir = true): PathEntry {.tags: [ReadDirEffect].} = + ## Improved `os.walkDirRec`. + #[ + note: a yieldFilter isn't needed because caller can filter at call site, without + loss of generality, unlike `follow`. + + Future work: + * need to document + * add a `sort` option, which can be implemented efficiently only here, not at call site. + * provide a way to do error reporting, which is tricky because iteration cannot be resumed + ]# + var stack = @["."] + var checkDir = checkDir + var entry: PathEntry + while stack.len > 0: + let d = stack.pop() + for k, p in walkDir(dir / d, relative = true, checkDir = checkDir): + let rel = d / p + entry.kind = k + if relative: entry.path = rel + else: entry.path = dir / rel + if k in {pcDir, pcLinkToDir}: + if follow == nil or follow(entry): stack.add rel + yield entry + checkDir = false + # We only check top-level dir, otherwise if a subdir is invalid (eg. wrong + # permissions), it'll abort iteration and there would be no way to + # continue iteration. + +proc nativeToUnixPath*(path: string): string = + # pending https://github.com/nim-lang/Nim/pull/13265 + doAssert not path.isAbsolute # not implemented here; absolute files need more care for the drive + when DirSep == '\\': + result = replace(path, '\\', '/') + else: result = path + +when isMainModule: + import sugar + for a in walkDirRecFilter(".", follow = a=>a.path.lastPathPart notin ["nimcache", ".git", ".csources", "bin"]): + echo a diff --git a/tests/misc/trunner.nim b/tests/misc/trunner.nim index 566f9f033..382fd35ae 100644 --- a/tests/misc/trunner.nim +++ b/tests/misc/trunner.nim @@ -11,6 +11,7 @@ import std/[strformat,os,osproc,unittest] from std/sequtils import toSeq,mapIt from std/algorithm import sorted import stdtest/[specialpaths, unittest_light] +from std/private/globs import nativeToUnixPath import "$lib/../compiler/nimpaths" @@ -90,11 +91,7 @@ else: # don't run twice the same test removeDir(htmldocsDir) let (outp, exitCode) = execCmdEx(cmd) check exitCode == 0 - proc nativeToUnixPathWorkaround(a: string): string = - # xxx pending https://github.com/nim-lang/Nim/pull/13265 `nativeToUnixPath` - a.replace(DirSep, '/') - - let ret = toSeq(walkDirRec(htmldocsDir, relative=true)).mapIt(it.nativeToUnixPathWorkaround).sorted.join("\n") + let ret = toSeq(walkDirRec(htmldocsDir, relative=true)).mapIt(it.nativeToUnixPath).sorted.join("\n") let context = $(i, ret, cmd) var expected = "" case i diff --git a/tools/kochdocs.nim b/tools/kochdocs.nim index c39a9c448..80aad4fb6 100644 --- a/tools/kochdocs.nim +++ b/tools/kochdocs.nim @@ -1,6 +1,7 @@ ## Part of 'koch' responsible for the documentation generation. import os, strutils, osproc, sets, pathnorm +from std/private/globs import nativeToUnixPath, walkDirRecFilter, PathEntry import "../compiler/nimpaths" const @@ -85,6 +86,16 @@ proc nimCompileFold*(desc, input: string, outputDir = "bin", mode = "c", options let cmd = findNim().quoteShell() & " " & mode & " -o:" & output & " " & options & " " & input execFold(desc, cmd) +proc getRst2html(): seq[string] = + for a in walkDirRecFilter("doc"): + let path = a.path + if a.kind == pcFile and path.splitFile.ext == ".rst" and path.lastPathPart notin + ["docs.rst", "nimfix.rst"]: + # maybe we should still show nimfix, could help reviving it + # `docs` is redundant with `overview`, might as well remove that file? + result.add path + doAssert "doc/manual/var_t_return.rst".unixToNativePath in result # sanity check + const pdf = """ doc/manual.rst @@ -95,39 +106,6 @@ doc/tut3.rst doc/nimc.rst doc/niminst.rst doc/gc.rst -""".splitWhitespace() - - rst2html = """ -doc/intern.rst -doc/apis.rst -doc/lib.rst -doc/manual.rst -doc/manual_experimental.rst -doc/destructors.rst -doc/tut1.rst -doc/tut2.rst -doc/tut3.rst -doc/nimc.rst -doc/hcr.rst -doc/drnim.rst -doc/overview.rst -doc/filters.rst -doc/tools.rst -doc/niminst.rst -doc/nimgrep.rst -doc/gc.rst -doc/estp.rst -doc/idetools.rst -doc/docgen.rst -doc/koch.rst -doc/backends.rst -doc/nimsuggest.rst -doc/nep1.rst -doc/nims.rst -doc/contributing.rst -doc/codeowners.rst -doc/packaging.rst -doc/manual/var_t_return.rst """.splitWhitespace() doc0 = """ @@ -185,7 +163,7 @@ proc getDocList(): seq[string] = for a in withoutIndex: docIgnore.incl a for a in ignoredModules: docIgnore.incl a - # don't ignore these even though in lib/system + # don't ignore these even though in lib/system (not include files) const goodSystem = """ lib/system/io.nim lib/system/nimscript.nim @@ -194,14 +172,14 @@ lib/system/iterators.nim lib/system/dollars.nim lib/system/widestrs.nim """.splitWhitespace() - - for a in walkDirRec("lib"): - if a.splitFile.ext != ".nim" or - a.isRelativeTo("lib/pure/includes") or - a.isRelativeTo("lib/genode") or - a.isRelativeTo("lib/deprecated") or - (a.isRelativeTo("lib/system") and a.replace('\\', '/') notin goodSystem) or - a.replace('\\', '/') in docIgnore: + + proc follow(a: PathEntry): bool = + a.path.lastPathPart notin ["nimcache", "htmldocs", "includes", "deprecated", "genode"] + for entry in walkDirRecFilter("lib", follow = follow): + let a = entry.path + if entry.kind != pcFile or a.splitFile.ext != ".nim" or + (a.isRelativeTo("lib/system") and a.nativeToUnixPath notin goodSystem) or + a.nativeToUnixPath in docIgnore: continue result.add a result.add normalizePath("nimsuggest/sexp.nim") @@ -231,16 +209,28 @@ proc buildDocSamples(nimArgs, destPath: string) = [nimArgs, destPath / "docgen_sample.html", "doc" / "docgen_sample.nim"]) proc buildDocPackages(nimArgs, destPath: string) = - # compiler docs, and later, other packages (perhaps tools, testament etc) + # compiler docs; later, other packages (perhaps tools, testament etc) let nim = findNim().quoteShell() - let extra = "-u:boot" # to avoid broken links to manual from compiler dir, but a multi-package # structure could be supported later - exec("$1 doc --project --outdir:$2/compiler $3 --git.url:$4 $5 compiler/nim.nim" % - [nim, destPath, nimArgs, gitUrl, extra]) + + proc docProject(outdir, options, mainproj: string) = + exec("$nim doc --project --outdir:$outdir $nimArgs --git.url:$gitUrl $options $mainproj" % [ + "nim", nim, + "outdir", outdir, + "nimArgs", nimArgs, + "gitUrl", gitUrl, + "options", options, + "mainproj", mainproj, + ]) + let extra = "-u:boot" + # xxx keep in sync with what's in $nim_prs_D/config/nimdoc.cfg, or, rather, + # start using nims instead of nimdoc.cfg + docProject(destPath/"compiler", extra, "compiler/index.nim") proc buildDoc(nimArgs, destPath: string) = # call nim for the documentation: + let rst2html = getRst2html() var commands = newSeq[string](rst2html.len + len(doc0) + len(doc) + withoutIndex.len) i = 0 @@ -305,18 +295,19 @@ proc buildJS(): string = let nim = findNim() exec(nim.quoteShell() & " js -d:release --out:$1 tools/nimblepkglist.nim" % [webUploadOutput / "nimblepkglist.js"]) + # xxx deadcode? and why is it only for webUploadOutput, not for local docs? result = getDocHacksJs(nimr = getCurrentDir(), nim) -proc buildDocs*(args: string) = +proc buildDocsDir*(args: string, dir: string) = + let args = nimArgs & " " & args let docHackJsSource = buildJS() - template fn(args, dir) = - let dir2 = dir - let args2 = args - createDir(dir2) - buildDocSamples(args2, dir2) - buildDoc(args2, dir2) - buildDocPackages(args2, dir2) - copyFile(docHackJsSource, dir2 / docHackJsSource.lastPathPart) + createDir(dir) + buildDocSamples(args, dir) + buildDoc(args, dir) # bottleneck + copyFile(dir / "overview.html", dir / "index.html") + buildDocPackages(args, dir) + copyFile(docHackJsSource, dir / docHackJsSource.lastPathPart) - fn(nimArgs & " " & args, webUploadOutput / NimVersion) - fn(nimArgs, docHtmlOutput) # no `args` to avoid offline docs containing the 'gaCode'! +proc buildDocs*(args: string) = + buildDocsDir(args, webUploadOutput / NimVersion) + buildDocsDir("", docHtmlOutput) # no `args` to avoid offline docs containing the 'gaCode'! diff --git a/tools/nimblepkglist.nim b/tools/nimblepkglist.nim index 870944ab0..c4bec4485 100644 --- a/tools/nimblepkglist.nim +++ b/tools/nimblepkglist.nim @@ -1,3 +1,6 @@ +#[ +deadcode? +]# import base64, strutils, json, htmlgen, dom, algorithm type From e5b64af8317eb0f5e8b9912691b69aab4cd26adf Mon Sep 17 00:00:00 2001 From: cooldome Date: Mon, 1 Jun 2020 18:56:17 +0100 Subject: [PATCH 0140/3768] Implement rendering of `[]=`, `{}`, `{}=` braces (#14539) * render curly braces * fix typo * fix test Co-authored-by: cooldome --- compiler/renderer.nim | 70 +++++++++++++++++++++++++++-------- tests/errmsgs/twrongcolon.nim | 6 +-- tests/macros/tmacrostmt.nim | 15 +++++++- 3 files changed, 71 insertions(+), 20 deletions(-) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 29ff80ca9..6c953dc18 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -874,12 +874,23 @@ proc gsub(g: var TSrcGen; n: PNode; i: int) = else: put(g, tkOpr, "<<" & $i & "th child missing for " & $n.kind & " >>") -proc isBracket*(n: PNode): bool = - case n.kind - of nkClosedSymChoice, nkOpenSymChoice: - if n.len > 0: result = isBracket(n[0]) - of nkSym: result = n.sym.name.s == "[]" - else: result = false +type + BracketKind = enum + bkNone, bkBracket, bkBracketAsgn, bkCurly, bkCurlyAsgn + +proc bracketKind*(g: TSrcGen, n: PNode): BracketKind = + if renderIds notin g.flags: + case n.kind + of nkClosedSymChoice, nkOpenSymChoice: + if n.len > 0: result = bracketKind(g, n[0]) + of nkSym: + result = case n.sym.name.s + of "[]": bkBracket + of "[]=": bkBracketAsgn + of "{}": bkCurly + of "{}=": bkCurlyAsgn + else: bkNone + else: result = bkNone proc skipHiddenNodes(n: PNode): PNode = result = n @@ -892,10 +903,11 @@ proc skipHiddenNodes(n: PNode): PNode = else: break proc accentedName(g: var TSrcGen, n: PNode) = + const backticksNeeded = OpChars + {'[', '{'} if n == nil: return let isOperator = - if n.kind == nkIdent and n.ident.s.len > 0 and n.ident.s[0] in OpChars: true - elif n.kind == nkSym and n.sym.name.s.len > 0 and n.sym.name.s[0] in OpChars: true + if n.kind == nkIdent and n.ident.s.len > 0 and n.ident.s[0] in backticksNeeded: true + elif n.kind == nkSym and n.sym.name.s.len > 0 and n.sym.name.s[0] in backticksNeeded: true else: false if isOperator: @@ -954,12 +966,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = of nkCharLit: put(g, tkCharLit, atom(g, n)) of nkNilLit: put(g, tkNil, atom(g, n)) # complex expressions of nkCall, nkConv, nkDotCall, nkPattern, nkObjConstr: - if renderIds notin g.flags and n.len > 0 and isBracket(n[0]): - gsub(g, n, 1) - put(g, tkBracketLe, "[") - gcomma(g, n, 2) - put(g, tkBracketRi, "]") - elif n.len > 1 and n.lastSon.kind == nkStmtList: + if n.len > 1 and n.lastSon.kind in {nkStmtList, nkStmtListExpr}: accentedName(g, n[0]) if n.len > 2: put(g, tkParLe, "(") @@ -967,10 +974,41 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = put(g, tkParRi, ")") put(g, tkColon, ":") gsub(g, n, n.len-1) + elif n.len >= 1: + case bracketKind(g, n[0]) + of bkBracket: + gsub(g, n, 1) + put(g, tkBracketLe, "[") + gcomma(g, n, 2) + put(g, tkBracketRi, "]") + of bkBracketAsgn: + gsub(g, n, 1) + put(g, tkBracketLe, "[") + gcomma(g, n, 2, -2) + put(g, tkBracketRi, "]") + put(g, tkSpaces, Space) + putWithSpace(g, tkEquals, "=") + gsub(g, n, n.len - 1) + of bkCurly: + gsub(g, n, 1) + put(g, tkCurlyLe, "{") + gcomma(g, n, 2) + put(g, tkCurlyRi, "}") + of bkCurlyAsgn: + gsub(g, n, 1) + put(g, tkCurlyLe, "{") + gcomma(g, n, 2, -2) + put(g, tkCurlyRi, "}") + put(g, tkSpaces, Space) + putWithSpace(g, tkEquals, "=") + gsub(g, n, n.len - 1) + of bkNone: + accentedName(g, n[0]) + put(g, tkParLe, "(") + gcomma(g, n, 1) + put(g, tkParRi, ")") else: - if n.len >= 1: accentedName(g, n[0]) put(g, tkParLe, "(") - gcomma(g, n, 1) put(g, tkParRi, ")") of nkCallStrLit: if n.len > 0: accentedName(g, n[0]) diff --git a/tests/errmsgs/twrongcolon.nim b/tests/errmsgs/twrongcolon.nim index 6f5cc3e5d..e59e37660 100644 --- a/tests/errmsgs/twrongcolon.nim +++ b/tests/errmsgs/twrongcolon.nim @@ -1,8 +1,8 @@ discard """ -errormsg: "in expression '(" +errormsg: "in expression ':" nimout: ''' -Error: in expression '( - 890)': identifier expected, but found '' +Error: in expression ': + 890': identifier expected, but found '' ''' line: 11 diff --git a/tests/macros/tmacrostmt.nim b/tests/macros/tmacrostmt.nim index 56a0bf840..79be5f764 100644 --- a/tests/macros/tmacrostmt.nim +++ b/tests/macros/tmacrostmt.nim @@ -100,18 +100,30 @@ proc fn4(x: int): int = proc fn5(a, b: float): float = result = - a * a / (b * b) +proc `{}`(x: seq[float], i: int, j: int): float = + x[i + 0 * j] + +proc `{}=`(x: var seq[float], i: int, val: float) = + x[i] = val + +proc fn6() = + var a = @[1.0, 2.0] + let z = a{0, 1} + a{2} = 5.0 + + #------------------------------------ # bug #10807 proc fn_unsafeaddr(x: int): int = cast[int](unsafeAddr(x)) static: - echo fn_unsafeaddr.repr_to_string let fn1s = "proc fn1(x, y: int): int =\n result = 2 * (x + y)\n" let fn2s = "proc fn2(x, y: float): float =\n result = (y + 2 * x) / (x - y)\n" let fn3s = "proc fn3(x, y: int): bool =\n result = ((x and 3) div 4 or x mod (y xor -1)) == 0 or not contains([1, 2], y)\n" let fn4s = "proc fn4(x: int): int =\n if x mod 2 == 0:\n return x + 2\n else:\n return 0\n" let fn5s = "proc fn5(a, b: float): float =\n result = -a * a / (b * b)\n" + let fn6s = "proc fn6() =\n var a = @[1.0, 2.0]\n let z = a{0, 1}\n a{2} = 5.0\n" let fnAddr = "proc fn_unsafeaddr(x: int): int =\n result = cast[int](unsafeAddr(x))\n" doAssert fn1.repr_to_string == fn1s @@ -119,6 +131,7 @@ static: doAssert fn3.repr_to_string == fn3s doAssert fn4.repr_to_string == fn4s doAssert fn5.repr_to_string == fn5s + doAssert fn6.repr_to_string == fn6s doAssert fn_unsafeaddr.repr_to_string == fnAddr #------------------------------------ From 621384b8efce7def08faeb9bc63289a4a88c3a59 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 2 Jun 2020 01:47:19 -0700 Subject: [PATCH 0141/3768] fix https://github.com/timotheecour/Nim/issues/266 retry on failure to avoid common 503 github errors (#14547) --- lib/std/private/nimbleutils.nim | 32 ++++++++++++++++++++++++++++++++ testament/categories.nim | 7 ++++--- 2 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 lib/std/private/nimbleutils.nim diff --git a/lib/std/private/nimbleutils.nim b/lib/std/private/nimbleutils.nim new file mode 100644 index 000000000..297fb9695 --- /dev/null +++ b/lib/std/private/nimbleutils.nim @@ -0,0 +1,32 @@ +##[ +internal API for now, API subject to change +]## + +import std/[os,osproc,sugar,strutils] + +proc actionRetry*(maxRetry: int, backoffDuration: float, action: proc(): bool): bool = + ## retry `action` up to `maxRetry` times with exponential backoff and initial + ## duraton of `backoffDuration` seconds + var t = backoffDuration + for i in 0.. Date: Tue, 2 Jun 2020 09:54:33 -0700 Subject: [PATCH 0142/3768] * honor --errorMax even for tools (eg drnim, nim doc) (#14546) * fix a bug that prevented nim doc compiler/nim on windows --- compiler/main.nim | 6 +++--- compiler/nimeval.nim | 3 ++- compiler/options.nim | 8 ++++++++ compiler/sem.nim | 2 +- compiler/semexprs.nim | 3 +-- drnim/drnim.nim | 2 +- nimsuggest/nimsuggest.nim | 6 ++---- tools/kochdocs.nim | 4 +++- tools/nimfind.nim | 3 +-- 9 files changed, 22 insertions(+), 15 deletions(-) diff --git a/compiler/main.nim b/compiler/main.nim index 55b2f2899..52fc62650 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -51,7 +51,7 @@ proc commandGenDepend(graph: ModuleGraph) = ' ' & changeFileExt(project, "dot").string) proc commandCheck(graph: ModuleGraph) = - graph.config.errorMax = high(int) # do not stop after first error + graph.config.setErrorMaxHighMaybe defineSymbol(graph.config.symbols, "nimcheck") semanticPasses(graph) # use an empty backend for semantic checking only compileProject(graph) @@ -59,7 +59,7 @@ proc commandCheck(graph: ModuleGraph) = when not defined(leanCompiler): proc commandDoc2(graph: ModuleGraph; json: bool) = handleDocOutputOptions graph.config - graph.config.errorMax = high(int) # do not stop after first error + graph.config.setErrorMaxHighMaybe semanticPasses(graph) if json: registerPass(graph, docgen2JsonPass) else: registerPass(graph, docgen2Pass) @@ -136,7 +136,7 @@ proc interactivePasses(graph: ModuleGraph) = registerPass(graph, evalPass) proc commandInteractive(graph: ModuleGraph) = - graph.config.errorMax = high(int) # do not stop after first error + graph.config.setErrorMaxHighMaybe interactivePasses(graph) compileSystemModule(graph) if graph.config.commandArgs.len > 0: diff --git a/compiler/nimeval.nim b/compiler/nimeval.nim index 11e899cae..df3cb2079 100644 --- a/compiler/nimeval.nim +++ b/compiler/nimeval.nim @@ -137,6 +137,7 @@ proc destroyInterpreter*(i: Interpreter) = proc runRepl*(r: TLLRepl; searchPaths: openArray[string]; supportNimscript: bool) = + ## deadcode but please don't remove... might be revived var conf = newConfigRef() var cache = newIdentCache() var graph = newModuleGraph(cache, conf) @@ -146,7 +147,7 @@ proc runRepl*(r: TLLRepl; if conf.libpath.isEmpty: conf.libpath = AbsoluteDir p conf.cmd = cmdInteractive - conf.errorMax = high(int) + conf.setErrorMaxHighMaybe initDefines(conf.symbols) defineSymbol(conf.symbols, "nimscript") if supportNimscript: defineSymbol(conf.symbols, "nimconfig") diff --git a/compiler/options.nim b/compiler/options.nim index d990f2fd4..31c0765a7 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -315,6 +315,14 @@ type severity: Severity) {.closure, gcsafe.} cppCustomNamespace*: string +proc assignIfDefault*[T](result: var T, val: T, def = default(T)) = + ## if `result` was already assigned to a value (that wasn't `def`), this is a noop. + if result == def: result = val + +template setErrorMaxHighMaybe*(conf: ConfigRef) = + ## do not stop after first error (but honor --errorMax if provided) + assignIfDefault(conf.errorMax, high(int)) + proc setNoteDefaults*(conf: ConfigRef, note: TNoteKind, enabled = true) = template fun(op) = conf.notes.op note diff --git a/compiler/sem.nim b/compiler/sem.nim index 683c67254..ca5d9c448 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -325,7 +325,7 @@ proc tryConstExpr(c: PContext, n: PNode): PNode = let oldErrorOutputs = c.config.m.errorOutputs c.config.m.errorOutputs = {} - c.config.errorMax = high(int) + c.config.errorMax = high(int) # `setErrorMaxHighMaybe` not appropriate here try: result = evalConstExpr(c.module, c.graph, e) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 72d407cf7..200d1d60a 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2074,8 +2074,7 @@ proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = if c.compilesContextId == 0: inc c.compilesContextIdGenerator c.compilesContextId = c.compilesContextIdGenerator - # do not halt after first error: - c.config.errorMax = high(int) + c.config.errorMax = high(int) # `setErrorMaxHighMaybe` not appropriate here # open a scope for temporary symbol inclusions: let oldScope = c.currentScope diff --git a/drnim/drnim.nim b/drnim/drnim.nim index d48c35b3f..e17667ec7 100644 --- a/drnim/drnim.nim +++ b/drnim/drnim.nim @@ -1192,7 +1192,7 @@ proc mainCommand(graph: ModuleGraph) = graph.strongSemCheck = strongSemCheck graph.compatibleProps = compatibleProps - graph.config.errorMax = high(int) # do not stop after first error + graph.config.setErrorMaxHighMaybe defineSymbol(graph.config.symbols, "nimcheck") defineSymbol(graph.config.symbols, "nimDrNim") diff --git a/nimsuggest/nimsuggest.nim b/nimsuggest/nimsuggest.nim index 56646db35..9a0962df3 100644 --- a/nimsuggest/nimsuggest.nim +++ b/nimsuggest/nimsuggest.nim @@ -525,8 +525,7 @@ proc mainCommand(graph: ModuleGraph) = add(conf.searchPaths, conf.libpath) - # do not stop after the first error: - conf.errorMax = high(int) + conf.setErrorMaxHighMaybe # honor --errorMax even if it may not make sense here # do not print errors, but log them conf.writelnHook = myLog conf.structuredErrorHook = nil @@ -674,8 +673,7 @@ else: add(conf.searchPaths, conf.libpath) - # do not stop after the first error: - conf.errorMax = high(int) + conf.setErrorMaxHighMaybe # do not print errors, but log them conf.writelnHook = myLog conf.structuredErrorHook = nil diff --git a/tools/kochdocs.nim b/tools/kochdocs.nim index 80aad4fb6..4f39463e1 100644 --- a/tools/kochdocs.nim +++ b/tools/kochdocs.nim @@ -6,7 +6,9 @@ import "../compiler/nimpaths" const gaCode* = " --doc.googleAnalytics:UA-48159761-1" - nimArgs = "--hint:Conf:off --hint:Path:off --hint:Processing:off -d:boot --putenv:nimversion=$#" % system.NimVersion + # errormax: subsequent errors are probably consequences of 1st one; a simple + # bug could cause unlimited number of errors otherwise, hard to debug in CI. + nimArgs = "--errormax:3 --hint:Conf:off --hint:Path:off --hint:Processing:off -d:boot --putenv:nimversion=$#" % system.NimVersion gitUrl = "https://github.com/nim-lang/Nim" docHtmlOutput = "doc/html" webUploadOutput = "web/upload" diff --git a/tools/nimfind.nim b/tools/nimfind.nim index eca3d8eb2..05980d740 100644 --- a/tools/nimfind.nim +++ b/tools/nimfind.nim @@ -169,8 +169,7 @@ proc mainCommand(graph: ModuleGraph) = if not fileExists(conf.projectFull): quit "cannot find file: " & conf.projectFull.string add(conf.searchPaths, conf.libpath) - # do not stop after the first error: - conf.errorMax = high(int) + conf.setErrorMaxHighMaybe try: compileProject(graph) finally: From 55f0df164befdf2304841c74daeaacc5391e08fd Mon Sep 17 00:00:00 2001 From: alaviss Date: Wed, 3 Jun 2020 11:17:06 -0500 Subject: [PATCH 0143/3768] io: correct signature for some win32 apis (#14551) See https://github.com/nim-lang/Nim/pull/14550#issuecomment-637937649 --- lib/system/io.nim | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/system/io.nim b/lib/system/io.nim index b1f6bb61b..d1a7f1fc7 100644 --- a/lib/system/io.nim +++ b/lib/system/io.nim @@ -279,10 +279,10 @@ elif defined(posix) and not defined(nimscript): importc: "fcntl", header: "", varargs.} elif defined(windows): const HANDLE_FLAG_INHERIT = culong 0x1 - proc getOsfhandle(fd: cint): FileHandle {. + proc getOsfhandle(fd: cint): int {. importc: "_get_osfhandle", header: "".} - proc setHandleInformation(handle: FileHandle, mask, flags: culong): cint {. + proc setHandleInformation(handle: int, mask, flags: culong): cint {. importc: "SetHandleInformation", header: "".} const @@ -318,7 +318,7 @@ proc getOsFileHandle*(f: File): FileHandle = ## returns the OS file handle of the file ``f``. This is only useful for ## platform specific programming. when defined(windows): - result = getOsfhandle(getFileHandle(f)) + result = FileHandle getOsfhandle(cint getFileHandle(f)) else: result = c_fileno(f) @@ -339,7 +339,7 @@ when defined(nimdoc) or (defined(posix) and not defined(nimscript)) or defined(w flags = if inheritable: flags and not FD_CLOEXEC else: flags or FD_CLOEXEC result = c_fcntl(f, F_SETFD, flags) != -1 else: - result = setHandleInformation(f, HANDLE_FLAG_INHERIT, culong inheritable) != 0 + result = setHandleInformation(f.int, HANDLE_FLAG_INHERIT, culong inheritable) != 0 proc readLine*(f: File, line: var TaintedString): bool {.tags: [ReadIOEffect], benign.} = @@ -647,7 +647,7 @@ proc open*(f: var File, filehandle: FileHandle, ## ## The passed file handle will no longer be inheritable. when not defined(nimInheritHandles) and declared(setInheritable): - let oshandle = when defined(windows): getOsfhandle(filehandle) else: filehandle + let oshandle = when defined(windows): FileHandle getOsfhandle(filehandle) else: filehandle if not setInheritable(oshandle, false): return false f = c_fdopen(filehandle, FormatOpen[mode]) From 4ba34522f6f64f66df849368d3aad1c434ae1403 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 3 Jun 2020 19:20:41 -0700 Subject: [PATCH 0144/3768] [cleanup] docgen: remove docOutdir now that outDir is always set (#14554) --- compiler/docgen.nim | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 535b11ce7..0263b36eb 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -55,9 +55,6 @@ type PDoc* = ref TDocumentor ## Alias to type less. -proc docOutDir(conf: ConfigRef, subdir: RelativeDir = RelativeDir""): AbsoluteDir = - if not conf.outDir.isEmpty: conf.outDir else: conf.projectPath / subdir - proc presentationPath*(conf: ConfigRef, file: AbsoluteFile, isTitle = false): RelativeFile = ## returns a relative file that will be appended to outDir let file2 = $file @@ -154,13 +151,9 @@ proc parseRst(text, filename: string, docgenFindFile, compilerMsgHandler) proc getOutFile2(conf: ConfigRef; filename: RelativeFile, - ext: string, dir: RelativeDir; guessTarget: bool): AbsoluteFile = - if optWholeProject in conf.globalOptions: - let d = conf.docOutDir(dir) - createDir(d) - result = d / changeFileExt(filename, ext) - elif guessTarget: - let d = conf.docOutDir + ext: string, guessTarget: bool): AbsoluteFile = + if optWholeProject in conf.globalOptions or guessTarget: + let d = conf.outDir createDir(d) result = d / changeFileExt(filename, ext) elif not conf.outFile.isEmpty: @@ -235,8 +228,7 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef, if execShellCmd(cmd) != status: rawMessage(conf, errGenerated, "executing of external program failed: " & cmd) result.emitted = initIntSet() - result.destFile = getOutFile2(conf, presentationPath(conf, filename), - outExt, htmldocsDir, false) + result.destFile = getOutFile2(conf, presentationPath(conf, filename), outExt, false) result.thisDir = result.destFile.splitFile.dir template dispA(conf: ConfigRef; dest: var Rope, xml, tex: string, args: openArray[Rope]) = @@ -364,8 +356,7 @@ proc belongsToPackage(conf: ConfigRef; module: PSym): bool = proc externalDep(d: PDoc; module: PSym): string = if optWholeProject in d.conf.globalOptions or d.conf.docRoot.len > 0: let full = AbsoluteFile toFullPath(d.conf, FileIndex module.position) - let tmp = getOutFile2(d.conf, presentationPath(d.conf, full), HtmlExt, - htmldocsDir, sfMainModule notin module.flags) + let tmp = getOutFile2(d.conf, presentationPath(d.conf, full), HtmlExt, sfMainModule notin module.flags) result = relativeTo(tmp, d.thisDir, '/').string else: result = extractFilename toFullPath(d.conf, FileIndex module.position) @@ -1272,14 +1263,13 @@ proc genOutFile(d: PDoc): Rope = proc generateIndex*(d: PDoc) = if optGenIndex in d.conf.globalOptions: - let dir = d.conf.docOutDir(htmldocsDir) + let dir = d.conf.outDir createDir(dir) let dest = dir / changeFileExt(presentationPath(d.conf, AbsoluteFile d.filename), IndexExt) writeIndexFile(d[], dest.string) proc updateOutfile(d: PDoc, outfile: AbsoluteFile) = if d.module == nil or sfMainModule in d.module.flags: # nil for eg for commandRst2Html - if d.conf.outDir.isEmpty: d.conf.outDir = d.conf.docOutDir if d.conf.outFile.isEmpty: d.conf.outFile = outfile.relativeTo(d.conf.outDir) if isAbsolute(d.conf.outFile.string): @@ -1292,7 +1282,7 @@ proc writeOutput*(d: PDoc, useWarning = false) = writeRope(stdout, content) else: template outfile: untyped = d.destFile - #let outfile = getOutFile2(d.conf, shortenDir(d.conf, filename), outExt, htmldocsDir) + #let outfile = getOutFile2(d.conf, shortenDir(d.conf, filename), outExt) let dir = outfile.splitFile.dir createDir(dir) updateOutfile(d, outfile) From 01f6e505c8b23fa55506d39864f6353e2a10a276 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 4 Jun 2020 01:40:17 -0700 Subject: [PATCH 0145/3768] bug fixes with sfMainModule, hints, mainPackageNotes, mainPackageId, hintSuccessX (#14555) * SuccessX `out` now works with --compileOnly and jsonscript; fix bugs in jsonscript * several bug fixes; eg: `nim doc lib/system/io` now is sane * dummy edit to force docs CI --- .github/workflows/ci_docs.yml | 2 +- compiler/commands.nim | 2 -- compiler/extccomp.nim | 21 +++++++++++++++++++-- compiler/main.nim | 17 ++++++++++++++--- compiler/modules.nim | 33 +++++++++++++++++++++++++-------- compiler/msgs.nim | 2 ++ compiler/options.nim | 14 ++++++++------ compiler/passes.nim | 4 +--- 8 files changed, 70 insertions(+), 25 deletions(-) diff --git a/.github/workflows/ci_docs.yml b/.github/workflows/ci_docs.yml index c5e23fbed..2fbf7e7dc 100644 --- a/.github/workflows/ci_docs.yml +++ b/.github/workflows/ci_docs.yml @@ -12,7 +12,7 @@ on: - 'tools/kochdocs.nim' - '.github/workflows/ci_docs.yml' pull_request: - # Run only on changes on these files + # Run only on changes on these files. paths: - 'compiler/docgen.nim' - 'compiler/renderverbatim.nim' diff --git a/compiler/commands.nim b/compiler/commands.nim index ce4f7cc0f..2d8418139 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -913,8 +913,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; if strutils.find(switch, '.') >= 0: options.setConfigVar(conf, switch, arg) else: invalidCmdLineOption(conf, pass, switch, info) -template gCmdLineInfo*(): untyped = newLineInfo(commandLineIdx, 1, 1) - proc processCommand*(switch: string, pass: TCmdLinePass; config: ConfigRef) = var cmd, arg: string splitSwitch(config, switch, cmd, arg, pass, gCmdLineInfo) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 7757a9f9d..6feb68f81 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -1003,10 +1003,16 @@ proc writeJsonBuildInstructions*(conf: ConfigRef) = var buf = newStringOfCap(50) let jsonFile = conf.getNimcacheDir / RelativeFile(conf.projectName & ".json") + conf.jsonBuildFile = jsonFile + let output = conf.absOutFile var f: File if open(f, jsonFile.string, fmWrite): - lit "{\"compile\":[\L" + lit "{\L" + lit "\"outputFile\": " + str $output + + lit ",\L\"compile\":[\L" cfiles(conf, f, buf, conf.toCompile, false) lit "],\L\"link\":[\L" var objfiles = "" @@ -1014,7 +1020,7 @@ proc writeJsonBuildInstructions*(conf: ConfigRef) = linkfiles(conf, f, buf, objfiles, conf.toCompile, conf.externalToLink) lit "],\L\"linkcmd\": " - str getLinkCmd(conf, conf.absOutFile, objfiles) + str getLinkCmd(conf, output, objfiles) lit ",\L\"extraCmds\": " lit $(%* getExtraCmds(conf, conf.absOutFile)) @@ -1074,6 +1080,17 @@ proc runJsonBuildInstructions*(conf: ConfigRef; projectfile: AbsoluteFile) = let jsonFile = toGeneratedFile(conf, projectfile, "json") try: let data = json.parseFile(jsonFile.string) + + let output = data["outputFile"].getStr + createDir output.parentDir + let outputCurrent = $conf.absOutFile + if output != outputCurrent: + # previously, any specified output file would be silently ignored; + # simply copying won't work in some cases, for example with `extraCmds`, + # so we just make it an error, user should use same command for jsonscript + # as was used with --compileOnly. + globalError(conf, gCmdLineInfo, "jsonscript command outputFile '$1' must match '$2' which was specified during --compileOnly, see \"outputFile\" entry in '$3' " % [outputCurrent, output, jsonFile.string]) + let toCompile = data["compile"] doAssert toCompile.kind == JArray var cmds: TStringSeq diff --git a/compiler/main.nim b/compiler/main.nim index 52fc62650..e498dc6b9 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -66,8 +66,7 @@ when not defined(leanCompiler): compileProject(graph) finishDoc2Pass(graph.config.projectName) -proc commandCompileToC(graph: ModuleGraph) = - let conf = graph.config +proc setOutFile(conf: ConfigRef) = if conf.outFile.isEmpty: let base = conf.projectName let targetName = if optGenDynLib in conf.globalOptions: @@ -76,6 +75,9 @@ proc commandCompileToC(graph: ModuleGraph) = base & platform.OS[conf.target.targetOS].exeExt conf.outFile = RelativeFile targetName +proc commandCompileToC(graph: ModuleGraph) = + let conf = graph.config + setOutFile(conf) extccomp.initVars(conf) semanticPasses(graph) registerPass(graph, cgenPass) @@ -370,6 +372,7 @@ proc mainCommand*(graph: ModuleGraph) = conf.cmd = cmdDump of "jsonscript": conf.cmd = cmdJsonScript + setOutFile(graph.config) commandJsonScript(graph) elif commandAlreadyProcessed: discard # already handled else: @@ -386,7 +389,15 @@ proc mainCommand*(graph: ModuleGraph) = else: "Debug" let sec = formatFloat(epochTime() - conf.lastCmdTime, ffDecimal, 3) let project = if optListFullPaths in conf.globalOptions: $conf.projectFull else: $conf.projectName - var output = $conf.absOutFile + + var output: string + if optCompileOnly in conf.globalOptions and conf.cmd != cmdJsonScript: + output = $conf.jsonBuildFile + elif conf.outFile.isEmpty and conf.cmd notin {cmdJsonScript, cmdCompileToBackend, cmdDoc}: + # for some cmd we expect a valid absOutFile + output = "unknownOutput" + else: + output = $conf.absOutFile if optListFullPaths notin conf.globalOptions: output = output.AbsoluteFile.extractFilename rawMessage(conf, hintSuccessX, [ "loc", loc, diff --git a/compiler/modules.nim b/compiler/modules.nim index d6e82d694..383858026 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -17,22 +17,29 @@ import proc resetSystemArtifacts*(g: ModuleGraph) = magicsys.resetSysTypes(g) -proc partialInitModule(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; filename: AbsoluteFile) = +template getModuleIdent(graph: ModuleGraph, filename: AbsoluteFile): PIdent = + getIdent(graph.cache, splitFile(filename).name) + +proc getPackage(graph: ModuleGraph; fileIdx: FileIndex): PSym = + ## returns package symbol (skPackage) for yet to be defined module for fileIdx + let filename = AbsoluteFile toFullPath(graph.config, fileIdx) + let name = getModuleIdent(graph, filename) + let info = newLineInfo(fileIdx, 1, 1) let pck = getPackageName(graph.config, filename.string) pck2 = if pck.len > 0: pck else: "unknown" pack = getIdent(graph.cache, pck2) var packSym = graph.packageSyms.strTableGet(pack) if packSym == nil: - packSym = newSym(skPackage, getIdent(graph.cache, pck2), nil, result.info) + packSym = newSym(skPackage, getIdent(graph.cache, pck2), nil, info) initStrTable(packSym.tab) graph.packageSyms.strTableAdd(packSym) else: - let existing = strTableGet(packSym.tab, result.name) - if existing != nil and existing.info.fileIndex != result.info.fileIndex: + let existing = strTableGet(packSym.tab, name) + if existing != nil and existing.info.fileIndex != info.fileIndex: when false: # we used to produce an error: - localError(graph.config, result.info, + localError(graph.config, info, "module names need to be unique per Nimble package; module clashes with " & toFullPath(graph.config, existing.info.fileIndex)) else: @@ -40,10 +47,13 @@ proc partialInitModule(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; fil # to resolve the conflicts: let pck3 = fakePackageName(graph.config, filename) # this makes the new `packSym`'s owner be the original `packSym` - packSym = newSym(skPackage, getIdent(graph.cache, pck3), packSym, result.info) + packSym = newSym(skPackage, getIdent(graph.cache, pck3), packSym, info) initStrTable(packSym.tab) graph.packageSyms.strTableAdd(packSym) + result = packSym +proc partialInitModule(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; filename: AbsoluteFile) = + let packSym = getPackage(graph, fileIdx) result.owner = packSym result.position = int fileIdx @@ -60,13 +70,15 @@ proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym = # We cannot call ``newSym`` here, because we have to circumvent the ID # mechanism, which we do in order to assign each module a persistent ID. result = PSym(kind: skModule, id: -1, # for better error checking - name: getIdent(graph.cache, splitFile(filename).name), + name: getModuleIdent(graph, filename), info: newLineInfo(fileIdx, 1, 1)) if not isNimIdentifier(result.name.s): rawMessage(graph.config, errGenerated, "invalid module name: " & result.name.s) partialInitModule(result, graph, fileIdx, filename) proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags): PSym = + var flags = flags + if fileIdx == graph.config.projectMainIdx2: flags.incl sfMainModule result = graph.getModule(fileIdx) if result == nil: let filename = AbsoluteFile toFullPath(graph.config, fileIdx) @@ -104,7 +116,7 @@ proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PSym = # localError(result.info, errAttemptToRedefine, result.name.s) # restore the notes for outer module: graph.config.notes = - if s.owner.id == graph.config.mainPackageId or isDefined(graph.config, "booting"): graph.config.mainPackageNotes + if s.getnimblePkgId == graph.config.mainPackageId or isDefined(graph.config, "booting"): graph.config.mainPackageNotes else: graph.config.foreignPackageNotes proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PNode = @@ -135,7 +147,12 @@ proc compileProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIdx) = wantMainModule(conf) let systemFileIdx = fileInfoIdx(conf, conf.libpath / RelativeFile"system.nim") let projectFile = if projectFileIdx == InvalidFileIdx: conf.projectMainIdx else: projectFileIdx + conf.projectMainIdx2 = projectFile + + let packSym = getPackage(graph, projectFile) + graph.config.mainPackageId = packSym.getnimblePkgId graph.importStack.add projectFile + if projectFile == systemFileIdx: discard graph.compileModule(projectFile, {sfMainModule, sfSystemModule}) else: diff --git a/compiler/msgs.nim b/compiler/msgs.nim index eca3b0276..a0fc9b08d 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -117,6 +117,8 @@ proc newLineInfo*(fileInfoIdx: FileIndex, line, col: int): TLineInfo = proc newLineInfo*(conf: ConfigRef; filename: AbsoluteFile, line, col: int): TLineInfo {.inline.} = result = newLineInfo(fileInfoIdx(conf, filename), line, col) +const gCmdLineInfo* = newLineInfo(commandLineIdx, 1, 1) + proc concat(strings: openArray[string]): string = var totalLen = 0 for s in strings: totalLen += s.len diff --git a/compiler/options.nim b/compiler/options.nim index 31c0765a7..289509c16 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -273,6 +273,7 @@ type lazyPaths*: seq[AbsoluteDir] outFile*: RelativeFile outDir*: AbsoluteDir + jsonBuildFile*: AbsoluteFile prefixDir*, libpath*, nimcacheDir*: AbsoluteDir dllOverrides, moduleOverrides*, cfileSpecificOptions*: StringTableRef projectName*: string # holds a name like 'nim' @@ -281,6 +282,7 @@ type projectIsStdin*: bool # whether we're compiling from stdin lastMsgWasDot*: bool # the last compiler message was a single '.' projectMainIdx*: FileIndex # the canonical path id of the main module + projectMainIdx2*: FileIndex # consider merging with projectMainIdx command*: string # the main command (e.g. cc, check, scan, etc) commandArgs*: seq[string] # any arguments after the main command commandLine*: string @@ -336,7 +338,10 @@ proc setNote*(conf: ConfigRef, note: TNoteKind, enabled = true) = if enabled: incl(conf.notes, note) else: excl(conf.notes, note) proc hasHint*(conf: ConfigRef, note: TNoteKind): bool = - optHints in conf.options and note in conf.notes + if optHints notin conf.options: false + elif note in {hintConf}: # could add here other special notes like hintSource + note in conf.mainPackageNotes + else: note in conf.notes proc hasWarn*(conf: ConfigRef, note: TNoteKind): bool = optWarns in conf.options and note in conf.notes @@ -556,11 +561,8 @@ proc getOutFile*(conf: ConfigRef; filename: RelativeFile, ext: string): Absolute result = conf.outDir / changeFileExt(filename, ext) proc absOutFile*(conf: ConfigRef): AbsoluteFile = - if false: - doAssert not conf.outDir.isEmpty - doAssert not conf.outFile.isEmpty - # xxx: fix this pre-existing bug causing `SuccessX` error messages to lie - # for `jsonscript` + doAssert not conf.outDir.isEmpty + doAssert not conf.outFile.isEmpty result = conf.outDir / conf.outFile when defined(posix): if dirExists(result.string): result.string.add ".out" diff --git a/compiler/passes.nim b/compiler/passes.nim index 9f562d615..615b9ac88 100644 --- a/compiler/passes.nim +++ b/compiler/passes.nim @@ -113,10 +113,8 @@ const nkExportStmt, nkExportExceptStmt, nkFromStmt, nkImportStmt, nkImportExceptStmt} proc prepareConfigNotes(graph: ModuleGraph; module: PSym) = - if sfMainModule in module.flags: - graph.config.mainPackageId = module.owner.id # don't be verbose unless the module belongs to the main package: - if module.owner.id == graph.config.mainPackageId: + if module.getnimblePkgId == graph.config.mainPackageId: graph.config.notes = graph.config.mainPackageNotes else: if graph.config.mainPackageNotes == {}: graph.config.mainPackageNotes = graph.config.notes From c1ca06b4525b12022a5f8368582549e5bb01a438 Mon Sep 17 00:00:00 2001 From: alaviss Date: Thu, 4 Jun 2020 06:25:38 -0500 Subject: [PATCH 0146/3768] tfdleak: fix flakyness on Windows (#14550) * tfdleak_multiple: introduce stress tester for tfdleak Imported from #14548 and tweaked for consumption by testament. This test seems to be really good at bringing out the flakyness of tfdleadk. Co-authored-by: Timothee Cour * tfdleak: increase accuracy of the test on Windows This commit implements a new testing strategy for Windows: 1. We duplicate the handle that will be tested and enable inheritance. This duplicate will serve as a reference handle. 2. In addition to checking whether the handle is valid, we also verify whether the handle is the same as the reference. This gives us complete certainty on whether the handle in question is inherited from the parent. A side effect is that this uses Windows 10+ APIs. But since this is just for the test, we don't have to be picky about it. Ideally we would want to do something like this for other POSIX-based system, but most of them lack a facility to do this, and as of writing there isn't any false positive for them, so we won't need the additional checks. MemFile.fHandle will also no longer be tested, as this handle defaults to being invalid. Co-authored-by: Timothee Cour --- tests/stdlib/tfdleak.nim | 45 ++++++++++++++++++++++++++++--- tests/stdlib/tfdleak_multiple.nim | 23 ++++++++++++++++ 2 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 tests/stdlib/tfdleak_multiple.nim diff --git a/tests/stdlib/tfdleak.nim b/tests/stdlib/tfdleak.nim index c4f144db5..5931be8c1 100644 --- a/tests/stdlib/tfdleak.nim +++ b/tests/stdlib/tfdleak.nim @@ -8,16 +8,41 @@ import os, osproc, strutils, nativesockets, net, selectors, memfiles, asyncdispatch, asyncnet when defined(windows): import winlean + + # Note: Windows 10-only API + proc compareObjectHandles(first, second: Handle): WINBOOL + {.stdcall, dynlib: "kernelbase", + importc: "CompareObjectHandles".} else: import posix proc leakCheck(f: AsyncFD | int | FileHandle | SocketHandle, msg: string, expectLeak = defined(nimInheritHandles)) = + var args = @[$f.int, msg, $expectLeak] + + when defined(windows): + var refFd: Handle + # NOTE: This function shouldn't be used to duplicate sockets, + # as this function may mess with the socket internal refcounting. + # but due to the lack of type segmentation in the stdlib for + # Windows (AsyncFD can be a file or a socket), we will have to + # settle with this. + # + # Now, as a poor solution for the refcounting problem, we just + # simply let the duplicated handle leak. This should not interfere + # with the test since new handles can't occupy the slot held by + # the leaked ones. + if duplicateHandle(getCurrentProcess(), f.Handle, + getCurrentProcess(), addr refFd, + 0, 1, DUPLICATE_SAME_ACCESS) == 0: + raiseOSError osLastError(), "Couldn't create the reference handle" + args.add $refFd + discard startProcess( getAppFilename(), - args = @[$f.int, msg, $expectLeak], + args = args, options = {poParentStreams} - ).waitForExit -1 + ).waitForExit proc isValidHandle(f: int): bool = ## Check if a handle is valid. Requires OS-native handles. @@ -72,7 +97,6 @@ proc main() = var mf = memfiles.open("__test_fdleak3", fmReadWrite, newFileSize = 1) defer: close mf when defined(windows): - leakCheck(mf.fHandle, "memfiles.open().fHandle", false) leakCheck(mf.mapHandle, "memfiles.open().mapHandle", false) else: leakCheck(mf.handle, "memfiles.open().handle", false) @@ -105,7 +129,20 @@ proc main() = fd = parseInt(paramStr 1) expectLeak = parseBool(paramStr 3) msg = (if expectLeak: "not " else: "") & "leaked " & paramStr 2 - if expectLeak xor fd.isValidHandle: + let validHandle = + when defined(windows): + # On Windows, due to the use of winlean, causes the program to open + # a handle to the various dlls that's loaded. This handle might + # collide with the handle sent for testing. + # + # As a walkaround, we pass an another handle that's purposefully leaked + # as a reference so that we can verify whether the "leaked" handle + # is the right one. + let refFd = parseInt(paramStr 4) + fd.isValidHandle and compareObjectHandles(fd, refFd) != 0 + else: + fd.isValidHandle + if expectLeak xor validHandle: echo msg when isMainModule: main() diff --git a/tests/stdlib/tfdleak_multiple.nim b/tests/stdlib/tfdleak_multiple.nim new file mode 100644 index 000000000..51d291284 --- /dev/null +++ b/tests/stdlib/tfdleak_multiple.nim @@ -0,0 +1,23 @@ +import os, osproc, strutils + +const Iterations = 200 + +proc testFdLeak() = + var count = 0 + let + test = getAppDir() / "tfdleak" + exe = test.addFileExt(ExeExt).quoteShell + options = ["", "-d:nimInheritHandles"] + for opt in options: + let + run = "nim c $1 $2" % [opt, quoteShell test] + (output, status) = execCmdEx run + doAssert status == 0, "Test complination failed:\n$1\n$2" % [run, output] + for i in 1..Iterations: + let (output, status) = execCmdEx exe + doAssert status == 0, "Execution of " & exe & " failed" + if "leaked" in output: + count.inc + doAssert count == 0, "Leaked " & $count & " times" + +when isMainModule: testFdLeak() From 7cb4ef26addb3bb5ce2405d8396df6fd41664dae Mon Sep 17 00:00:00 2001 From: PMunch Date: Fri, 5 Jun 2020 10:54:38 +0200 Subject: [PATCH 0147/3768] Improve JSON serialisation of strtabs (#14549) This creates a more compact serialisation of strtabs that is more in line with the normal tables. --- lib/pure/json.nim | 35 +++++++++++++++++++++++++++++------ lib/pure/strtabs.nim | 2 +- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/lib/pure/json.nim b/lib/pure/json.nim index ab03370b5..04e169d30 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -149,7 +149,7 @@ runnableExamples: doAssert $(%* Foo()) == """{"a1":0,"a2":0,"a0":0,"a3":0,"a4":0}""" import - hashes, tables, strutils, lexbase, streams, macros, parsejson, + hashes, tables, strtabs, strutils, lexbase, streams, macros, parsejson, options export @@ -353,6 +353,14 @@ proc `[]=`*(obj: JsonNode, key: string, val: JsonNode) {.inline.} = assert(obj.kind == JObject) obj.fields[key] = val +proc `%`*(table: StringTableRef): JsonNode = + ## Generic constructor for JSON data. Creates a new ``JObject JsonNode``. + result = newJObject() + result["mode"] = %($table.mode) + var data = newJObject() + for k, v in table: data[k] = %v + result["data"] = data + proc `%`*[T: object](o: T): JsonNode = ## Construct JsonNode from tuples and objects. result = newJObject() @@ -981,11 +989,12 @@ when defined(nimFixedForwardGeneric): proc initFromJson[T: enum](dst: var T; jsonNode: JsonNode; jsonPath: var string) proc initFromJson[T](dst: var seq[T]; jsonNode: JsonNode; jsonPath: var string) proc initFromJson[S,T](dst: var array[S,T]; jsonNode: JsonNode; jsonPath: var string) - proc initFromJson[T](dst: var Table[string,T];jsonNode: JsonNode; jsonPath: var string) - proc initFromJson[T](dst: var OrderedTable[string,T];jsonNode: JsonNode; jsonPath: var string) + proc initFromJson[T](dst: var Table[string,T]; jsonNode: JsonNode; jsonPath: var string) + proc initFromJson[T](dst: var OrderedTable[string,T]; jsonNode: JsonNode; jsonPath: var string) + proc initFromJson(dst: var StringTableRef; jsonNode: JsonNode; jsonPath: var string) proc initFromJson[T](dst: var ref T; jsonNode: JsonNode; jsonPath: var string) proc initFromJson[T](dst: var Option[T]; jsonNode: JsonNode; jsonPath: var string) - proc initFromJson[T: distinct](dst: var T;jsonNode: JsonNode; jsonPath: var string) + proc initFromJson[T: distinct](dst: var T; jsonNode: JsonNode; jsonPath: var string) proc initFromJson[T: object|tuple](dst: var T; jsonNode: JsonNode; jsonPath: var string) # initFromJson definitions @@ -1043,7 +1052,7 @@ when defined(nimFixedForwardGeneric): initFromJson(dst[i], jsonNode[i], jsonPath) jsonPath.setLen originalJsonPathLen - proc initFromJson[T](dst: var Table[string,T];jsonNode: JsonNode; jsonPath: var string) = + proc initFromJson[T](dst: var Table[string,T]; jsonNode: JsonNode; jsonPath: var string) = dst = initTable[string, T]() verifyJsonKind(jsonNode, {JObject}, jsonPath) let originalJsonPathLen = jsonPath.len @@ -1053,7 +1062,7 @@ when defined(nimFixedForwardGeneric): initFromJson(mgetOrPut(dst, key, default(T)), jsonNode[key], jsonPath) jsonPath.setLen originalJsonPathLen - proc initFromJson[T](dst: var OrderedTable[string,T];jsonNode: JsonNode; jsonPath: var string) = + proc initFromJson[T](dst: var OrderedTable[string,T]; jsonNode: JsonNode; jsonPath: var string) = dst = initOrderedTable[string,T]() verifyJsonKind(jsonNode, {JObject}, jsonPath) let originalJsonPathLen = jsonPath.len @@ -1063,6 +1072,20 @@ when defined(nimFixedForwardGeneric): initFromJson(mgetOrPut(dst, key, default(T)), jsonNode[key], jsonPath) jsonPath.setLen originalJsonPathLen + proc mgetOrPut(tab: var StringTableRef, key: string): var string = + if not tab.hasKey(key): tab[key] = "" + result = tab[key] + + proc initFromJson(dst: var StringTableRef; jsonNode: JsonNode; jsonPath: var string) = + dst = newStringTable(parseEnum[StringTableMode](jsonNode["mode"].getStr)) + verifyJsonKind(jsonNode, {JObject}, jsonPath) + let originalJsonPathLen = jsonPath.len + for key in keys(jsonNode["data"].fields): + jsonPath.add '.' + jsonPath.add key + initFromJson(mgetOrPut(dst, key), jsonNode[key], jsonPath) + jsonPath.setLen originalJsonPathLen + proc initFromJson[T](dst: var ref T; jsonNode: JsonNode; jsonPath: var string) = verifyJsonKind(jsonNode, {JObject, JNull}, jsonPath) if jsonNode.kind == JNull: diff --git a/lib/pure/strtabs.nim b/lib/pure/strtabs.nim index 26811cc65..cfafdf018 100644 --- a/lib/pure/strtabs.nim +++ b/lib/pure/strtabs.nim @@ -71,7 +71,7 @@ type StringTableObj* = object of RootObj counter: int data: KeyValuePairSeq - mode: StringTableMode + mode*: StringTableMode StringTableRef* = ref StringTableObj From 0824fdd06100282a853fa50efb66e2161f4235d8 Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 5 Jun 2020 16:15:13 +0200 Subject: [PATCH 0148/3768] fixes #14562 --- compiler/semcall.nim | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index c6e12facf..665eb4ea4 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -527,7 +527,10 @@ proc semResolvedCall(c: PContext, x: TCandidate, for s in instantiateGenericParamList(c, gp, x.bindings): case s.kind of skConst: - x.call.add s.ast + if not s.ast.isNil: + x.call.add s.ast + else: + x.call.add c.graph.emptyNode of skType: x.call.add newSymNode(s, n.info) else: From 7cdf4702699b5de0e31e1139122aa0486fad24a9 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 5 Jun 2020 19:04:39 -0700 Subject: [PATCH 0149/3768] refs #14545 fix snippet errors: avoid showing confusing errors when they are expected (#14569) --- compiler/docgen.nim | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 0263b36eb..0af830a42 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -17,7 +17,7 @@ import packages/docutils/rst, packages/docutils/rstgen, json, xmltree, cgi, trees, types, typesrenderer, astalgo, lineinfos, intsets, - pathutils, trees, tables, nimpaths, renderverbatim + pathutils, trees, tables, nimpaths, renderverbatim, osproc from std/private/globs import nativeToUnixPath @@ -225,8 +225,9 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef, ] let cmd = cmd.interpSnippetCmd rawMessage(conf, hintExecuting, cmd) - if execShellCmd(cmd) != status: - rawMessage(conf, errGenerated, "executing of external program failed: " & cmd) + let (output, gotten) = execCmdEx(cmd) + if gotten != status: + rawMessage(conf, errGenerated, "snippet failed: cmd: '$1' status: $2 expected: $3 output: $4" % [cmd, $gotten, $status, output]) result.emitted = initIntSet() result.destFile = getOutFile2(conf, presentationPath(conf, filename), outExt, false) result.thisDir = result.destFile.splitFile.dir From b19ad22b90b920036fcc7752eca624e55a929cf3 Mon Sep 17 00:00:00 2001 From: Danil Yarantsev Date: Sat, 6 Jun 2020 10:31:19 +0300 Subject: [PATCH 0150/3768] Fix #14570 (#14571) * Fix #14570 --- compiler/jsgen.nim | 2 +- tests/js/t14570.nim | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 tests/js/t14570.nim diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 6aacf818a..0410a7a55 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -1822,7 +1822,7 @@ proc genNewSeq(p: PProc, n: PNode) = x.rdLoc, y.rdLoc, createVar(p, t, false)]) proc genOrd(p: PProc, n: PNode, r: var TCompRes) = - case skipTypes(n[1].typ, abstractVar).kind + case skipTypes(n[1].typ, abstractVar + abstractRange).kind of tyEnum, tyInt..tyUInt64, tyChar: gen(p, n[1], r) of tyBool: unaryExpr(p, n, r, "", "($1 ? 1:0)") else: internalError(p.config, n.info, "genOrd") diff --git a/tests/js/t14570.nim b/tests/js/t14570.nim new file mode 100644 index 000000000..100c2651d --- /dev/null +++ b/tests/js/t14570.nim @@ -0,0 +1,11 @@ +discard """ + output: ''' +18 +''' +""" + +type A = range[15 .. 30] + +let a: A = 18 + +echo ord(a) From d573581eb72997e27d91dac82b860964ed706590 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 6 Jun 2020 02:50:46 -0700 Subject: [PATCH 0151/3768] remove isMainModule from json,os,sequtils (#14572) * move json.isMainModule => tjson * move isMainModule => tos,tsequtils --- lib/pure/collections/sequtils.nim | 461 ------------------------------ lib/pure/json.nim | 235 +-------------- lib/pure/os.nim | 63 ---- tests/stdlib/tjson.nim | 234 +++++++++++++++ tests/stdlib/tos.nim | 63 ++++ tests/stdlib/tsequtils.nim | 460 +++++++++++++++++++++++++++++ 6 files changed, 759 insertions(+), 757 deletions(-) create mode 100644 tests/stdlib/tjson.nim create mode 100644 tests/stdlib/tsequtils.nim diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index a89d2c572..f101d508e 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -1065,464 +1065,3 @@ iterator items*[T](xs: iterator: T): T = ## templates. for x in xs(): yield x - -when isMainModule: - import strutils - from algorithm import sorted - - # helper for testing double substitution side effects which are handled - # by `evalOnceAs` - var counter = 0 - proc identity[T](a: T): auto = - counter.inc - a - - block: # concat test - let - s1 = @[1, 2, 3] - s2 = @[4, 5] - s3 = @[6, 7] - total = concat(s1, s2, s3) - assert total == @[1, 2, 3, 4, 5, 6, 7] - - block: # count test - let - s1 = @[1, 2, 3, 2] - s2 = @['a', 'b', 'x', 'a'] - a1 = [1, 2, 3, 2] - a2 = ['a', 'b', 'x', 'a'] - r0 = count(s1, 0) - r1 = count(s1, 1) - r2 = count(s1, 2) - r3 = count(s2, 'y') - r4 = count(s2, 'x') - r5 = count(s2, 'a') - ar0 = count(a1, 0) - ar1 = count(a1, 1) - ar2 = count(a1, 2) - ar3 = count(a2, 'y') - ar4 = count(a2, 'x') - ar5 = count(a2, 'a') - assert r0 == 0 - assert r1 == 1 - assert r2 == 2 - assert r3 == 0 - assert r4 == 1 - assert r5 == 2 - assert ar0 == 0 - assert ar1 == 1 - assert ar2 == 2 - assert ar3 == 0 - assert ar4 == 1 - assert ar5 == 2 - - block: # cycle tests - let - a = @[1, 2, 3] - b: seq[int] = @[] - c = [1, 2, 3] - - doAssert a.cycle(3) == @[1, 2, 3, 1, 2, 3, 1, 2, 3] - doAssert a.cycle(0) == @[] - #doAssert a.cycle(-1) == @[] # will not compile! - doAssert b.cycle(3) == @[] - doAssert c.cycle(3) == @[1, 2, 3, 1, 2, 3, 1, 2, 3] - doAssert c.cycle(0) == @[] - - block: # repeat tests - assert repeat(10, 5) == @[10, 10, 10, 10, 10] - assert repeat(@[1, 2, 3], 2) == @[@[1, 2, 3], @[1, 2, 3]] - assert repeat([1, 2, 3], 2) == @[[1, 2, 3], [1, 2, 3]] - - block: # deduplicates test - let - dup1 = @[1, 1, 3, 4, 2, 2, 8, 1, 4] - dup2 = @["a", "a", "c", "d", "d"] - dup3 = [1, 1, 3, 4, 2, 2, 8, 1, 4] - dup4 = ["a", "a", "c", "d", "d"] - unique1 = deduplicate(dup1) - unique2 = deduplicate(dup2) - unique3 = deduplicate(dup3) - unique4 = deduplicate(dup4) - unique5 = deduplicate(dup1.sorted, true) - unique6 = deduplicate(dup2, true) - unique7 = deduplicate(dup3.sorted, true) - unique8 = deduplicate(dup4, true) - assert unique1 == @[1, 3, 4, 2, 8] - assert unique2 == @["a", "c", "d"] - assert unique3 == @[1, 3, 4, 2, 8] - assert unique4 == @["a", "c", "d"] - assert unique5 == @[1, 2, 3, 4, 8] - assert unique6 == @["a", "c", "d"] - assert unique7 == @[1, 2, 3, 4, 8] - assert unique8 == @["a", "c", "d"] - - block: # zip test - let - short = @[1, 2, 3] - long = @[6, 5, 4, 3, 2, 1] - words = @["one", "two", "three"] - ashort = [1, 2, 3] - along = [6, 5, 4, 3, 2, 1] - awords = ["one", "two", "three"] - zip1 = zip(short, long) - zip2 = zip(short, words) - zip3 = zip(ashort, along) - assert zip1 == @[(1, 6), (2, 5), (3, 4)] - assert zip2 == @[(1, "one"), (2, "two"), (3, "three")] - assert zip3 == @[(1, 6), (2, 5), (3, 4)] - assert zip1[2][1] == 4 - assert zip2[2][1] == "three" - assert zip3[2][1] == 4 - when (NimMajor, NimMinor) <= (1, 0): - let - # In Nim 1.0.x and older, zip returned a seq of tuple strictly - # with fields named "a" and "b". - zipAb = zip(ashort, awords) - assert zipAb == @[(a: 1, b: "one"), (2, "two"), (3, "three")] - assert zipAb[2].b == "three" - else: - let - # As zip returns seq of anonymous tuples, they can be assigned - # to any variable that's a sequence of named tuples too. - zipXy: seq[tuple[x: int, y: string]] = zip(ashort, awords) - zipMn: seq[tuple[m: int, n: string]] = zip(ashort, words) - assert zipXy == @[(x: 1, y: "one"), (2, "two"), (3, "three")] - assert zipMn == @[(m: 1, n: "one"), (2, "two"), (3, "three")] - assert zipXy[2].y == "three" - assert zipMn[2].n == "three" - - block: # distribute tests - let numbers = @[1, 2, 3, 4, 5, 6, 7] - doAssert numbers.distribute(3) == @[@[1, 2, 3], @[4, 5], @[6, 7]] - doAssert numbers.distribute(6)[0] == @[1, 2] - doAssert numbers.distribute(6)[5] == @[7] - let a = @[1, 2, 3, 4, 5, 6, 7] - doAssert a.distribute(1, true) == @[@[1, 2, 3, 4, 5, 6, 7]] - doAssert a.distribute(1, false) == @[@[1, 2, 3, 4, 5, 6, 7]] - doAssert a.distribute(2, true) == @[@[1, 2, 3, 4], @[5, 6, 7]] - doAssert a.distribute(2, false) == @[@[1, 2, 3, 4], @[5, 6, 7]] - doAssert a.distribute(3, true) == @[@[1, 2, 3], @[4, 5], @[6, 7]] - doAssert a.distribute(3, false) == @[@[1, 2, 3], @[4, 5, 6], @[7]] - doAssert a.distribute(4, true) == @[@[1, 2], @[3, 4], @[5, 6], @[7]] - doAssert a.distribute(4, false) == @[@[1, 2], @[3, 4], @[5, 6], @[7]] - doAssert a.distribute(5, true) == @[@[1, 2], @[3, 4], @[5], @[6], @[7]] - doAssert a.distribute(5, false) == @[@[1, 2], @[3, 4], @[5, 6], @[7], @[]] - doAssert a.distribute(6, true) == @[@[1, 2], @[3], @[4], @[5], @[6], @[7]] - doAssert a.distribute(6, false) == @[ - @[1, 2], @[3, 4], @[5, 6], @[7], @[], @[]] - doAssert a.distribute(8, false) == a.distribute(8, true) - doAssert a.distribute(90, false) == a.distribute(90, true) - var b = @[0] - for f in 1 .. 25: b.add(f) - doAssert b.distribute(5, true)[4].len == 5 - doAssert b.distribute(5, false)[4].len == 2 - - block: # map test - let - numbers = @[1, 4, 5, 8, 9, 7, 4] - anumbers = [1, 4, 5, 8, 9, 7, 4] - m1 = map(numbers, proc(x: int): int = 2*x) - m2 = map(anumbers, proc(x: int): int = 2*x) - assert m1 == @[2, 8, 10, 16, 18, 14, 8] - assert m2 == @[2, 8, 10, 16, 18, 14, 8] - - block: # apply test - var a = @["1", "2", "3", "4"] - apply(a, proc(x: var string) = x &= "42") - assert a == @["142", "242", "342", "442"] - - block: # filter proc test - let - colors = @["red", "yellow", "black"] - acolors = ["red", "yellow", "black"] - f1 = filter(colors, proc(x: string): bool = x.len < 6) - f2 = filter(colors) do (x: string) -> bool: x.len > 5 - f3 = filter(acolors, proc(x: string): bool = x.len < 6) - f4 = filter(acolors) do (x: string) -> bool: x.len > 5 - assert f1 == @["red", "black"] - assert f2 == @["yellow"] - assert f3 == @["red", "black"] - assert f4 == @["yellow"] - - block: # filter iterator test - let numbers = @[1, 4, 5, 8, 9, 7, 4] - let anumbers = [1, 4, 5, 8, 9, 7, 4] - assert toSeq(filter(numbers, proc (x: int): bool = x mod 2 == 0)) == - @[4, 8, 4] - assert toSeq(filter(anumbers, proc (x: int): bool = x mod 2 == 0)) == - @[4, 8, 4] - - block: # keepIf test - var floats = @[13.0, 12.5, 5.8, 2.0, 6.1, 9.9, 10.1] - keepIf(floats, proc(x: float): bool = x > 10) - assert floats == @[13.0, 12.5, 10.1] - - block: # delete tests - let outcome = @[1, 1, 1, 1, 1, 1, 1, 1] - var dest = @[1, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1] - dest.delete(3, 8) - assert outcome == dest, """\ - Deleting range 3-9 from [1,1,1,2,2,2,2,2,2,1,1,1,1,1] - is [1,1,1,1,1,1,1,1]""" - var x = @[1, 2, 3] - x.delete(100, 100) - assert x == @[1, 2, 3] - - block: # insert tests - var dest = @[1, 1, 1, 1, 1, 1, 1, 1] - let - src = @[2, 2, 2, 2, 2, 2] - outcome = @[1, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1] - dest.insert(src, 3) - assert dest == outcome, """\ - Inserting [2,2,2,2,2,2] into [1,1,1,1,1,1,1,1] - at 3 is [1,1,1,2,2,2,2,2,2,1,1,1,1,1]""" - - block: # filterIt test - let - temperatures = @[-272.15, -2.0, 24.5, 44.31, 99.9, -113.44] - acceptable = filterIt(temperatures, it < 50 and it > -10) - notAcceptable = filterIt(temperatures, it > 50 or it < -10) - assert acceptable == @[-2.0, 24.5, 44.31] - assert notAcceptable == @[-272.15, 99.9, -113.44] - - block: # keepItIf test - var candidates = @["foo", "bar", "baz", "foobar"] - keepItIf(candidates, it.len == 3 and it[0] == 'b') - assert candidates == @["bar", "baz"] - - block: # all - let - numbers = @[1, 4, 5, 8, 9, 7, 4] - anumbers = [1, 4, 5, 8, 9, 7, 4] - len0seq: seq[int] = @[] - assert all(numbers, proc (x: int): bool = return x < 10) == true - assert all(numbers, proc (x: int): bool = return x < 9) == false - assert all(len0seq, proc (x: int): bool = return false) == true - assert all(anumbers, proc (x: int): bool = return x < 10) == true - assert all(anumbers, proc (x: int): bool = return x < 9) == false - - block: # allIt - let - numbers = @[1, 4, 5, 8, 9, 7, 4] - anumbers = [1, 4, 5, 8, 9, 7, 4] - len0seq: seq[int] = @[] - assert allIt(numbers, it < 10) == true - assert allIt(numbers, it < 9) == false - assert allIt(len0seq, false) == true - assert allIt(anumbers, it < 10) == true - assert allIt(anumbers, it < 9) == false - - block: # any - let - numbers = @[1, 4, 5, 8, 9, 7, 4] - anumbers = [1, 4, 5, 8, 9, 7, 4] - len0seq: seq[int] = @[] - assert any(numbers, proc (x: int): bool = return x > 8) == true - assert any(numbers, proc (x: int): bool = return x > 9) == false - assert any(len0seq, proc (x: int): bool = return true) == false - assert any(anumbers, proc (x: int): bool = return x > 8) == true - assert any(anumbers, proc (x: int): bool = return x > 9) == false - - block: # anyIt - let - numbers = @[1, 4, 5, 8, 9, 7, 4] - anumbers = [1, 4, 5, 8, 9, 7, 4] - len0seq: seq[int] = @[] - assert anyIt(numbers, it > 8) == true - assert anyIt(numbers, it > 9) == false - assert anyIt(len0seq, true) == false - assert anyIt(anumbers, it > 8) == true - assert anyIt(anumbers, it > 9) == false - - block: # toSeq test - block: - let - numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9] - oddNumbers = toSeq(filter(numeric) do (x: int) -> bool: - if x mod 2 == 1: - result = true) - assert oddNumbers == @[1, 3, 5, 7, 9] - - block: - doAssert [1, 2].toSeq == @[1, 2] - doAssert @[1, 2].toSeq == @[1, 2] - - doAssert @[1, 2].toSeq == @[1, 2] - doAssert toSeq(@[1, 2]) == @[1, 2] - - block: - iterator myIter(seed: int): auto = - for i in 0...nim") - doAssert not isValidFilename(" foo.log") - # Positive Tests. - doAssert isValidFilename("abcd", maxLen = 42.Positive) - doAssert isValidFilename("c0n") - doAssert isValidFilename("foo.aux") - doAssert isValidFilename("bar.prn") - doAssert isValidFilename("OwO_UwU") - doAssert isValidFilename("cron") - doAssert isValidFilename("ux.bat") - doAssert isValidFilename("nim.nim") - doAssert isValidFilename("foo.log") diff --git a/tests/stdlib/tjson.nim b/tests/stdlib/tjson.nim new file mode 100644 index 000000000..bc7ff02b2 --- /dev/null +++ b/tests/stdlib/tjson.nim @@ -0,0 +1,234 @@ +#[ +Note: Macro tests are in tests/stdlib/tjsonmacro.nim +]# + +import std/[json,parsejson,strutils,streams] + +let testJson = parseJson"""{ "a": [1, 2, 3, 4], "b": "asd", "c": "\ud83c\udf83", "d": "\u00E6"}""" +# nil passthrough +doAssert(testJson{"doesnt_exist"}{"anything"}.isNil) +testJson{["e", "f"]} = %true +doAssert(testJson["e"]["f"].bval) + +# make sure UTF-16 decoding works. +doAssert(testJson["c"].str == "🎃") +doAssert(testJson["d"].str == "æ") + +# make sure no memory leek when parsing invalid string +let startMemory = getOccupiedMem() +for i in 0 .. 10000: + try: + discard parseJson"""{ invalid""" + except: + discard +# memory diff should less than 4M +doAssert(abs(getOccupiedMem() - startMemory) < 4 * 1024 * 1024) + + +# test `$` +let stringified = $testJson +let parsedAgain = parseJson(stringified) +doAssert(parsedAgain["b"].str == "asd") + +parsedAgain["abc"] = %5 +doAssert parsedAgain["abc"].num == 5 + +# Bounds checking +when compileOption("boundChecks"): + try: + let a = testJson["a"][9] + doAssert(false, "IndexDefect not thrown") + except IndexDefect: + discard + try: + let a = testJson["a"][-1] + doAssert(false, "IndexDefect not thrown") + except IndexDefect: + discard + try: + doAssert(testJson["a"][0].num == 1, "Index doesn't correspond to its value") + except: + doAssert(false, "IndexDefect thrown for valid index") + +doAssert(testJson{"b"}.getStr() == "asd", "Couldn't fetch a singly nested key with {}") +doAssert(isNil(testJson{"nonexistent"}), "Non-existent keys should return nil") +doAssert(isNil(testJson{"a", "b"}), "Indexing through a list should return nil") +doAssert(isNil(testJson{"a", "b"}), "Indexing through a list should return nil") +doAssert(testJson{"a"} == parseJson"[1, 2, 3, 4]", "Didn't return a non-JObject when there was one to be found") +doAssert(isNil(parseJson("[1, 2, 3]"){"foo"}), "Indexing directly into a list should return nil") + +# Generator: +var j = %* [{"name": "John", "age": 30}, {"name": "Susan", "age": 31}] +doAssert j == %[%{"name": %"John", "age": %30}, %{"name": %"Susan", "age": %31}] + +var j2 = %* + [ + { + "name": "John", + "age": 30 + }, + { + "name": "Susan", + "age": 31 + } + ] +doAssert j2 == %[%{"name": %"John", "age": %30}, %{"name": %"Susan", "age": %31}] + +var name = "John" +let herAge = 30 +const hisAge = 31 + +var j3 = %* + [ {"name": "John" + , "age": herAge + } + , {"name": "Susan" + , "age": hisAge + } + ] +doAssert j3 == %[%{"name": %"John", "age": %30}, %{"name": %"Susan", "age": %31}] + +var j4 = %*{"test": nil} +doAssert j4 == %{"test": newJNull()} + +let seqOfNodes = @[%1, %2] +let jSeqOfNodes = %seqOfNodes +doAssert(jSeqOfNodes[1].num == 2) + +type MyObj = object + a, b: int + s: string + f32: float32 + f64: float64 + next: ref MyObj +var m: MyObj +m.s = "hi" +m.a = 5 +let jMyObj = %m +doAssert(jMyObj["a"].num == 5) +doAssert(jMyObj["s"].str == "hi") + +# Test loading of file. +when not defined(js): + var parsed = parseFile("tests/testdata/jsontest.json") + + try: + discard parsed["key2"][12123] + doAssert(false) + except IndexDefect: doAssert(true) + + var parsed2 = parseFile("tests/testdata/jsontest2.json") + doAssert(parsed2{"repository", "description"}.str == + "IRC Library for Haskell", "Couldn't fetch via multiply nested key using {}") + +doAssert escapeJsonUnquoted("\10Foo🎃barÄ") == "\\nFoo🎃barÄ" +doAssert escapeJsonUnquoted("\0\7\20") == "\\u0000\\u0007\\u0014" # for #7887 +doAssert escapeJson("\10Foo🎃barÄ") == "\"\\nFoo🎃barÄ\"" +doAssert escapeJson("\0\7\20") == "\"\\u0000\\u0007\\u0014\"" # for #7887 + +# Test with extra data +when not defined(js): + try: + discard parseJson("123 456") + doAssert(false) + except JsonParsingError: + doAssert getCurrentExceptionMsg().contains(errorMessages[errEofExpected]) + + try: + discard parseFile("tests/testdata/jsonwithextradata.json") + doAssert(false) + except JsonParsingError: + doAssert getCurrentExceptionMsg().contains(errorMessages[errEofExpected]) + +# bug #6438 +doAssert($ %*[] == "[]") +doAssert($ %*{} == "{}") + +doAssert(not compiles(%{"error": "No messages"})) + +# bug #9111 +block: + type + Bar = string + Foo = object + a: int + b: Bar + + let + js = """{"a": 123, "b": "abc"}""".parseJson + foo = js.to Foo + + doAssert(foo.b == "abc") + +# Generate constructors for range[T] types +block: + type + Q1 = range[0'u8 .. 50'u8] + Q2 = range[0'u16 .. 50'u16] + Q3 = range[0'u32 .. 50'u32] + Q4 = range[0'i8 .. 50'i8] + Q5 = range[0'i16 .. 50'i16] + Q6 = range[0'i32 .. 50'i32] + Q7 = range[0'f32 .. 50'f32] + Q8 = range[0'f64 .. 50'f64] + Q9 = range[0 .. 50] + + X = object + m1: Q1 + m2: Q2 + m3: Q3 + m4: Q4 + m5: Q5 + m6: Q6 + m7: Q7 + m8: Q8 + m9: Q9 + + let obj = X( + m1: Q1(42), + m2: Q2(42), + m3: Q3(42), + m4: Q4(42), + m5: Q5(42), + m6: Q6(42), + m7: Q7(42), + m8: Q8(42), + m9: Q9(42) + ) + + doAssert(obj == to(%obj, type(obj))) + + when not defined(js): + const fragments = """[1,2,3] {"hi":3} 12 [] """ + var res = "" + for x in parseJsonFragments(newStringStream(fragments)): + res.add($x) + res.add " " + doAssert res == fragments + + +# test isRefSkipDistinct +type + MyRef = ref object + MyObject = object + MyDistinct = distinct MyRef + MyOtherDistinct = distinct MyRef + +var x0: ref int +var x1: MyRef +var x2: MyObject +var x3: MyDistinct +var x4: MyOtherDistinct + +doAssert isRefSkipDistinct(x0) +doAssert isRefSkipDistinct(x1) +doAssert not isRefSkipDistinct(x2) +doAssert isRefSkipDistinct(x3) +doAssert isRefSkipDistinct(x4) + + +doAssert isRefSkipDistinct(ref int) +doAssert isRefSkipDistinct(MyRef) +doAssert not isRefSkipDistinct(MyObject) +doAssert isRefSkipDistinct(MyDistinct) +doAssert isRefSkipDistinct(MyOtherDistinct) diff --git a/tests/stdlib/tos.nim b/tests/stdlib/tos.nim index 20e82173d..a440d86d8 100644 --- a/tests/stdlib/tos.nim +++ b/tests/stdlib/tos.nim @@ -447,3 +447,66 @@ block isRelativeTo: doAssert isRelativeTo("foo/bar", ".") doAssert not isRelativeTo("foo/bar.nims", "foo/bar.nim") doAssert not isRelativeTo("/foo2", "/foo") + +block: # quoteShellWindows + assert quoteShellWindows("aaa") == "aaa" + assert quoteShellWindows("aaa\"") == "aaa\\\"" + assert quoteShellWindows("") == "\"\"" + +block: # quoteShellWindows + assert quoteShellPosix("aaa") == "aaa" + assert quoteShellPosix("aaa a") == "'aaa a'" + assert quoteShellPosix("") == "''" + assert quoteShellPosix("a'a") == "'a'\"'\"'a'" + +block: # quoteShell + when defined(posix): + assert quoteShell("") == "''" + +block: # normalizePathEnd + # handle edge cases correctly: shouldn't affect whether path is + # absolute/relative + doAssert "".normalizePathEnd(true) == "" + doAssert "".normalizePathEnd(false) == "" + doAssert "/".normalizePathEnd(true) == $DirSep + doAssert "/".normalizePathEnd(false) == $DirSep + + when defined(posix): + doAssert "//".normalizePathEnd(false) == "/" + doAssert "foo.bar//".normalizePathEnd == "foo.bar" + doAssert "bar//".normalizePathEnd(trailingSep = true) == "bar/" + when defined(Windows): + doAssert r"C:\foo\\".normalizePathEnd == r"C:\foo" + doAssert r"C:\foo".normalizePathEnd(trailingSep = true) == r"C:\foo\" + # this one is controversial: we could argue for returning `D:\` instead, + # but this is simplest. + doAssert r"D:\".normalizePathEnd == r"D:" + doAssert r"E:/".normalizePathEnd(trailingSep = true) == r"E:\" + doAssert "/".normalizePathEnd == r"\" + +block: # isValidFilename + # Negative Tests. + doAssert not isValidFilename("abcd", maxLen = 2) + doAssert not isValidFilename("0123456789", maxLen = 8) + doAssert not isValidFilename("con") + doAssert not isValidFilename("aux") + doAssert not isValidFilename("prn") + doAssert not isValidFilename("OwO|UwU") + doAssert not isValidFilename(" foo") + doAssert not isValidFilename("foo ") + doAssert not isValidFilename("foo.") + doAssert not isValidFilename("con.txt") + doAssert not isValidFilename("aux.bat") + doAssert not isValidFilename("prn.exe") + doAssert not isValidFilename("nim>.nim") + doAssert not isValidFilename(" foo.log") + # Positive Tests. + doAssert isValidFilename("abcd", maxLen = 42.Positive) + doAssert isValidFilename("c0n") + doAssert isValidFilename("foo.aux") + doAssert isValidFilename("bar.prn") + doAssert isValidFilename("OwO_UwU") + doAssert isValidFilename("cron") + doAssert isValidFilename("ux.bat") + doAssert isValidFilename("nim.nim") + doAssert isValidFilename("foo.log") diff --git a/tests/stdlib/tsequtils.nim b/tests/stdlib/tsequtils.nim new file mode 100644 index 000000000..ea3f06982 --- /dev/null +++ b/tests/stdlib/tsequtils.nim @@ -0,0 +1,460 @@ +import std/sequtils +import strutils +from algorithm import sorted + +# helper for testing double substitution side effects which are handled +# by `evalOnceAs` +var counter = 0 +proc identity[T](a: T): auto = + counter.inc + a + +block: # concat test + let + s1 = @[1, 2, 3] + s2 = @[4, 5] + s3 = @[6, 7] + total = concat(s1, s2, s3) + assert total == @[1, 2, 3, 4, 5, 6, 7] + +block: # count test + let + s1 = @[1, 2, 3, 2] + s2 = @['a', 'b', 'x', 'a'] + a1 = [1, 2, 3, 2] + a2 = ['a', 'b', 'x', 'a'] + r0 = count(s1, 0) + r1 = count(s1, 1) + r2 = count(s1, 2) + r3 = count(s2, 'y') + r4 = count(s2, 'x') + r5 = count(s2, 'a') + ar0 = count(a1, 0) + ar1 = count(a1, 1) + ar2 = count(a1, 2) + ar3 = count(a2, 'y') + ar4 = count(a2, 'x') + ar5 = count(a2, 'a') + assert r0 == 0 + assert r1 == 1 + assert r2 == 2 + assert r3 == 0 + assert r4 == 1 + assert r5 == 2 + assert ar0 == 0 + assert ar1 == 1 + assert ar2 == 2 + assert ar3 == 0 + assert ar4 == 1 + assert ar5 == 2 + +block: # cycle tests + let + a = @[1, 2, 3] + b: seq[int] = @[] + c = [1, 2, 3] + + doAssert a.cycle(3) == @[1, 2, 3, 1, 2, 3, 1, 2, 3] + doAssert a.cycle(0) == @[] + #doAssert a.cycle(-1) == @[] # will not compile! + doAssert b.cycle(3) == @[] + doAssert c.cycle(3) == @[1, 2, 3, 1, 2, 3, 1, 2, 3] + doAssert c.cycle(0) == @[] + +block: # repeat tests + assert repeat(10, 5) == @[10, 10, 10, 10, 10] + assert repeat(@[1, 2, 3], 2) == @[@[1, 2, 3], @[1, 2, 3]] + assert repeat([1, 2, 3], 2) == @[[1, 2, 3], [1, 2, 3]] + +block: # deduplicates test + let + dup1 = @[1, 1, 3, 4, 2, 2, 8, 1, 4] + dup2 = @["a", "a", "c", "d", "d"] + dup3 = [1, 1, 3, 4, 2, 2, 8, 1, 4] + dup4 = ["a", "a", "c", "d", "d"] + unique1 = deduplicate(dup1) + unique2 = deduplicate(dup2) + unique3 = deduplicate(dup3) + unique4 = deduplicate(dup4) + unique5 = deduplicate(dup1.sorted, true) + unique6 = deduplicate(dup2, true) + unique7 = deduplicate(dup3.sorted, true) + unique8 = deduplicate(dup4, true) + assert unique1 == @[1, 3, 4, 2, 8] + assert unique2 == @["a", "c", "d"] + assert unique3 == @[1, 3, 4, 2, 8] + assert unique4 == @["a", "c", "d"] + assert unique5 == @[1, 2, 3, 4, 8] + assert unique6 == @["a", "c", "d"] + assert unique7 == @[1, 2, 3, 4, 8] + assert unique8 == @["a", "c", "d"] + +block: # zip test + let + short = @[1, 2, 3] + long = @[6, 5, 4, 3, 2, 1] + words = @["one", "two", "three"] + ashort = [1, 2, 3] + along = [6, 5, 4, 3, 2, 1] + awords = ["one", "two", "three"] + zip1 = zip(short, long) + zip2 = zip(short, words) + zip3 = zip(ashort, along) + assert zip1 == @[(1, 6), (2, 5), (3, 4)] + assert zip2 == @[(1, "one"), (2, "two"), (3, "three")] + assert zip3 == @[(1, 6), (2, 5), (3, 4)] + assert zip1[2][1] == 4 + assert zip2[2][1] == "three" + assert zip3[2][1] == 4 + when (NimMajor, NimMinor) <= (1, 0): + let + # In Nim 1.0.x and older, zip returned a seq of tuple strictly + # with fields named "a" and "b". + zipAb = zip(ashort, awords) + assert zipAb == @[(a: 1, b: "one"), (2, "two"), (3, "three")] + assert zipAb[2].b == "three" + else: + let + # As zip returns seq of anonymous tuples, they can be assigned + # to any variable that's a sequence of named tuples too. + zipXy: seq[tuple[x: int, y: string]] = zip(ashort, awords) + zipMn: seq[tuple[m: int, n: string]] = zip(ashort, words) + assert zipXy == @[(x: 1, y: "one"), (2, "two"), (3, "three")] + assert zipMn == @[(m: 1, n: "one"), (2, "two"), (3, "three")] + assert zipXy[2].y == "three" + assert zipMn[2].n == "three" + +block: # distribute tests + let numbers = @[1, 2, 3, 4, 5, 6, 7] + doAssert numbers.distribute(3) == @[@[1, 2, 3], @[4, 5], @[6, 7]] + doAssert numbers.distribute(6)[0] == @[1, 2] + doAssert numbers.distribute(6)[5] == @[7] + let a = @[1, 2, 3, 4, 5, 6, 7] + doAssert a.distribute(1, true) == @[@[1, 2, 3, 4, 5, 6, 7]] + doAssert a.distribute(1, false) == @[@[1, 2, 3, 4, 5, 6, 7]] + doAssert a.distribute(2, true) == @[@[1, 2, 3, 4], @[5, 6, 7]] + doAssert a.distribute(2, false) == @[@[1, 2, 3, 4], @[5, 6, 7]] + doAssert a.distribute(3, true) == @[@[1, 2, 3], @[4, 5], @[6, 7]] + doAssert a.distribute(3, false) == @[@[1, 2, 3], @[4, 5, 6], @[7]] + doAssert a.distribute(4, true) == @[@[1, 2], @[3, 4], @[5, 6], @[7]] + doAssert a.distribute(4, false) == @[@[1, 2], @[3, 4], @[5, 6], @[7]] + doAssert a.distribute(5, true) == @[@[1, 2], @[3, 4], @[5], @[6], @[7]] + doAssert a.distribute(5, false) == @[@[1, 2], @[3, 4], @[5, 6], @[7], @[]] + doAssert a.distribute(6, true) == @[@[1, 2], @[3], @[4], @[5], @[6], @[7]] + doAssert a.distribute(6, false) == @[ + @[1, 2], @[3, 4], @[5, 6], @[7], @[], @[]] + doAssert a.distribute(8, false) == a.distribute(8, true) + doAssert a.distribute(90, false) == a.distribute(90, true) + var b = @[0] + for f in 1 .. 25: b.add(f) + doAssert b.distribute(5, true)[4].len == 5 + doAssert b.distribute(5, false)[4].len == 2 + +block: # map test + let + numbers = @[1, 4, 5, 8, 9, 7, 4] + anumbers = [1, 4, 5, 8, 9, 7, 4] + m1 = map(numbers, proc(x: int): int = 2*x) + m2 = map(anumbers, proc(x: int): int = 2*x) + assert m1 == @[2, 8, 10, 16, 18, 14, 8] + assert m2 == @[2, 8, 10, 16, 18, 14, 8] + +block: # apply test + var a = @["1", "2", "3", "4"] + apply(a, proc(x: var string) = x &= "42") + assert a == @["142", "242", "342", "442"] + +block: # filter proc test + let + colors = @["red", "yellow", "black"] + acolors = ["red", "yellow", "black"] + f1 = filter(colors, proc(x: string): bool = x.len < 6) + f2 = filter(colors) do (x: string) -> bool: x.len > 5 + f3 = filter(acolors, proc(x: string): bool = x.len < 6) + f4 = filter(acolors) do (x: string) -> bool: x.len > 5 + assert f1 == @["red", "black"] + assert f2 == @["yellow"] + assert f3 == @["red", "black"] + assert f4 == @["yellow"] + +block: # filter iterator test + let numbers = @[1, 4, 5, 8, 9, 7, 4] + let anumbers = [1, 4, 5, 8, 9, 7, 4] + assert toSeq(filter(numbers, proc (x: int): bool = x mod 2 == 0)) == + @[4, 8, 4] + assert toSeq(filter(anumbers, proc (x: int): bool = x mod 2 == 0)) == + @[4, 8, 4] + +block: # keepIf test + var floats = @[13.0, 12.5, 5.8, 2.0, 6.1, 9.9, 10.1] + keepIf(floats, proc(x: float): bool = x > 10) + assert floats == @[13.0, 12.5, 10.1] + +block: # delete tests + let outcome = @[1, 1, 1, 1, 1, 1, 1, 1] + var dest = @[1, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1] + dest.delete(3, 8) + assert outcome == dest, """\ + Deleting range 3-9 from [1,1,1,2,2,2,2,2,2,1,1,1,1,1] + is [1,1,1,1,1,1,1,1]""" + var x = @[1, 2, 3] + x.delete(100, 100) + assert x == @[1, 2, 3] + +block: # insert tests + var dest = @[1, 1, 1, 1, 1, 1, 1, 1] + let + src = @[2, 2, 2, 2, 2, 2] + outcome = @[1, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1] + dest.insert(src, 3) + assert dest == outcome, """\ + Inserting [2,2,2,2,2,2] into [1,1,1,1,1,1,1,1] + at 3 is [1,1,1,2,2,2,2,2,2,1,1,1,1,1]""" + +block: # filterIt test + let + temperatures = @[-272.15, -2.0, 24.5, 44.31, 99.9, -113.44] + acceptable = filterIt(temperatures, it < 50 and it > -10) + notAcceptable = filterIt(temperatures, it > 50 or it < -10) + assert acceptable == @[-2.0, 24.5, 44.31] + assert notAcceptable == @[-272.15, 99.9, -113.44] + +block: # keepItIf test + var candidates = @["foo", "bar", "baz", "foobar"] + keepItIf(candidates, it.len == 3 and it[0] == 'b') + assert candidates == @["bar", "baz"] + +block: # all + let + numbers = @[1, 4, 5, 8, 9, 7, 4] + anumbers = [1, 4, 5, 8, 9, 7, 4] + len0seq: seq[int] = @[] + assert all(numbers, proc (x: int): bool = return x < 10) == true + assert all(numbers, proc (x: int): bool = return x < 9) == false + assert all(len0seq, proc (x: int): bool = return false) == true + assert all(anumbers, proc (x: int): bool = return x < 10) == true + assert all(anumbers, proc (x: int): bool = return x < 9) == false + +block: # allIt + let + numbers = @[1, 4, 5, 8, 9, 7, 4] + anumbers = [1, 4, 5, 8, 9, 7, 4] + len0seq: seq[int] = @[] + assert allIt(numbers, it < 10) == true + assert allIt(numbers, it < 9) == false + assert allIt(len0seq, false) == true + assert allIt(anumbers, it < 10) == true + assert allIt(anumbers, it < 9) == false + +block: # any + let + numbers = @[1, 4, 5, 8, 9, 7, 4] + anumbers = [1, 4, 5, 8, 9, 7, 4] + len0seq: seq[int] = @[] + assert any(numbers, proc (x: int): bool = return x > 8) == true + assert any(numbers, proc (x: int): bool = return x > 9) == false + assert any(len0seq, proc (x: int): bool = return true) == false + assert any(anumbers, proc (x: int): bool = return x > 8) == true + assert any(anumbers, proc (x: int): bool = return x > 9) == false + +block: # anyIt + let + numbers = @[1, 4, 5, 8, 9, 7, 4] + anumbers = [1, 4, 5, 8, 9, 7, 4] + len0seq: seq[int] = @[] + assert anyIt(numbers, it > 8) == true + assert anyIt(numbers, it > 9) == false + assert anyIt(len0seq, true) == false + assert anyIt(anumbers, it > 8) == true + assert anyIt(anumbers, it > 9) == false + +block: # toSeq test + block: + let + numeric = @[1, 2, 3, 4, 5, 6, 7, 8, 9] + oddNumbers = toSeq(filter(numeric) do (x: int) -> bool: + if x mod 2 == 1: + result = true) + assert oddNumbers == @[1, 3, 5, 7, 9] + + block: + doAssert [1, 2].toSeq == @[1, 2] + doAssert @[1, 2].toSeq == @[1, 2] + + doAssert @[1, 2].toSeq == @[1, 2] + doAssert toSeq(@[1, 2]) == @[1, 2] + + block: + iterator myIter(seed: int): auto = + for i in 0.. Date: Sat, 6 Jun 2020 12:20:11 +0200 Subject: [PATCH 0152/3768] Fix asynchttpserver newline breaking content-length (#14565) [backport] --- lib/pure/asynchttpserver.nim | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index 7fbbf38ea..afd452b01 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -105,8 +105,9 @@ proc respond*(req: Request, code: HttpCode, content: string, msg.add("Content-Length: ") # this particular way saves allocations: msg.addInt content.len - - msg.add "\c\L\c\L" + msg.add "\c\L" + + msg.add "\c\L" msg.add(content) result = req.client.send(msg) From 336f1e63d0186ab283fdb9868a8f7d8d05293a31 Mon Sep 17 00:00:00 2001 From: Araq Date: Sat, 6 Jun 2020 20:08:25 +0200 Subject: [PATCH 0153/3768] reorder.nim: fixes the indentation --- compiler/reorder.nim | 79 ++++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 40 deletions(-) diff --git a/compiler/reorder.nim b/compiler/reorder.nim index 631806587..5e380a199 100644 --- a/compiler/reorder.nim +++ b/compiler/reorder.nim @@ -100,10 +100,9 @@ proc computeDeps(cache: IdentCache; n: PNode, declares, uses: var IntSet; topLev for i in 0.. Date: Sat, 6 Jun 2020 11:15:24 -0700 Subject: [PATCH 0154/3768] fix #14576 addr of param (including for lent) now works with nim js (#14577) * fix #14576 addr(param) now works in nim js * workaround https://github.com/nim-lang/Nim/issues/14578 --- compiler/ccgexprs.nim | 1 + compiler/jsgen.nim | 11 ++++++-- tests/js/taddr.nim | 62 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 71 insertions(+), 3 deletions(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index ec54081a2..2850ab750 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -141,6 +141,7 @@ proc genSetNode(p: BProc, n: PNode): Rope = result = genRawSetData(cs, size) proc getStorageLoc(n: PNode): TStorageLoc = + ## deadcode case n.kind of nkSym: case n.sym.kind diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 0410a7a55..9f022882b 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -1065,8 +1065,11 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = lineF(p, "var $1 = $4; $2 = $1[0]; $3 = $1[1];$n", [tmp, a.address, a.res, b.rdLoc]) elif b.typ == etyBaseIndex: lineF(p, "$# = [$#, $#];$n", [a.res, b.address, b.res]) + elif b.typ == etyNone: + internalAssert p.config, b.address == nil + lineF(p, "$# = [$#, 0];$n", [a.address, b.res]) else: - internalError(p.config, x.info, "genAsgn") + internalError(p.config, x.info, $("genAsgn", b.typ, a.typ)) else: lineF(p, "$1 = $2; $3 = $4;$n", [a.address, b.address, a.res, b.res]) else: @@ -1266,6 +1269,10 @@ proc genAddr(p: PProc, n: PNode, r: var TCompRes) = let s = n[0].sym if s.loc.r == nil: internalError(p.config, n.info, "genAddr: 3") case s.kind + of skParam: + r.res = s.loc.r + r.address = nil + r.typ = etyNone of skVar, skLet, skResult: r.kind = resExpr let jsType = mapType(p, n.typ) @@ -1287,7 +1294,7 @@ proc genAddr(p: PProc, n: PNode, r: var TCompRes) = # 'var openArray' for instance produces an 'addr' but this is harmless: gen(p, n[0], r) #internalError(p.config, n.info, "genAddr: 4 " & renderTree(n)) - else: internalError(p.config, n.info, "genAddr: 2") + else: internalError(p.config, n.info, $("genAddr: 2", s.kind)) of nkCheckedFieldExpr: genCheckedFieldOp(p, n[0], n.typ, r) of nkDotExpr: diff --git a/tests/js/taddr.nim b/tests/js/taddr.nim index d8ff4c11b..9c45621a8 100644 --- a/tests/js/taddr.nim +++ b/tests/js/taddr.nim @@ -1,5 +1,5 @@ discard """ - action: run + targets: "c js" """ type T = object @@ -77,3 +77,63 @@ let someGlobalPtr = getSomeGlobalPtr() doAssert(someGlobalPtr[] == 5) someGlobalPtr[] = 10 doAssert(someGlobal == 10) + +block: + # issue #14576 + # lots of these used to give: Error: internal error: genAddr: 2 + proc byLent[T](a: T): lent T = a + proc byPtr[T](a: T): ptr T = a.unsafeAddr + + block: + let a = (10,11) + let (x,y) = byLent(a) + doAssert (x,y) == a + + block: + when defined(c) and defined(release): + # bug; pending https://github.com/nim-lang/Nim/issues/14578 + discard + else: + let a = 10 + doAssert byLent(a) == 10 + let a2 = byLent(a) + doAssert a2 == 10 + + block: + let a = [11,12] + doAssert byLent(a) == [11,12] + let a2 = (11,) + doAssert byLent(a2) == (11,) + + block: + when defined(c) and defined(release): + discard # probably not a bug since optimizer is free to pass by value, and `unsafeAddr` is used + else: + var a = @[12] + doAssert byPtr(a)[] == @[12] + let a2 = [13] + doAssert byPtr(a2)[] == [13] + let a3 = 14 + doAssert byPtr(a3)[] == 14 + + block: + proc byLent2[T](a: seq[T]): lent T = a[1] + var a = @[20,21,22] + doAssert byLent2(a) == 21 + + block: # sanity checks + proc bar[T](a: var T): var T = a + var a = (10, 11) + let (k,v) = bar(a) + doAssert (k, v) == a + doAssert k == 10 + bar(a)[0]+=100 + doAssert a == (110, 11) + var a2 = 12 + doAssert bar(a2) == a2 + bar(a2).inc + doAssert a2 == 13 + + block: # xxx: bug this doesn't work + when false: + proc byLent2[T](a: T): lent type(a[0]) = a[0] From 82092b3bb7ead1414f673ed7f43e9fa1dcf8c5f4 Mon Sep 17 00:00:00 2001 From: Leorize Date: Mon, 1 Jun 2020 17:10:02 -0500 Subject: [PATCH 0155/3768] asyncnet, net: call SSL_shutdown only when connection established This commit prevents "SSL_shutdown while in init" errors from happening. See https://github.com/openssl/openssl/issues/710#issuecomment-253897666 --- lib/pure/asyncnet.nim | 9 ++++++++- lib/pure/net.nim | 22 +++++++++++++--------- lib/wrappers/openssl.nim | 28 ++++++++++++++++++++++++++++ tests/stdlib/thttpclient_ssl.nim | 5 +++-- 4 files changed, 52 insertions(+), 12 deletions(-) diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index 7ccb469cf..8df839c14 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -713,7 +713,14 @@ proc close*(socket: AsyncSocket) = socket.fd.AsyncFD.closeSocket() when defineSsl: if socket.isSsl: - let res = SSL_shutdown(socket.sslHandle) + let res = + # Don't call SSL_shutdown if the connection has not been fully + # established, see: + # https://github.com/openssl/openssl/issues/710#issuecomment-253897666 + if SSL_in_init(socket.sslHandle) == 0: + SSL_shutdown(socket.sslHandle) + else: + 0 SSL_free(socket.sslHandle) if res == 0: discard diff --git a/lib/pure/net.nim b/lib/pure/net.nim index b08629a96..f7aacfb0c 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -1007,15 +1007,19 @@ proc close*(socket: Socket) = when defineSsl: if socket.isSsl and socket.sslHandle != nil: ErrClearError() - # As we are closing the underlying socket immediately afterwards, - # it is valid, under the TLS standard, to perform a unidirectional - # shutdown i.e not wait for the peers "close notify" alert with a second - # call to SSL_shutdown - let res = SSL_shutdown(socket.sslHandle) - if res == 0: - discard - elif res != 1: - socketError(socket, res) + # Don't call SSL_shutdown if the connection has not been fully + # established, see: + # https://github.com/openssl/openssl/issues/710#issuecomment-253897666 + if SSL_in_init(socket.sslHandle) == 0: + # As we are closing the underlying socket immediately afterwards, + # it is valid, under the TLS standard, to perform a unidirectional + # shutdown i.e not wait for the peers "close notify" alert with a second + # call to SSL_shutdown + let res = SSL_shutdown(socket.sslHandle) + if res == 0: + discard + elif res != 1: + socketError(socket, res) finally: when defineSsl: if socket.isSsl and socket.sslHandle != nil: diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim index 5aaa0a0e8..5fd4def0b 100644 --- a/lib/wrappers/openssl.nim +++ b/lib/wrappers/openssl.nim @@ -201,6 +201,9 @@ const SSL_OP_ALL* = 0x000FFFFF SSL_VERIFY_NONE* = 0x00000000 SSL_VERIFY_PEER* = 0x00000001 + SSL_ST_CONNECT* = 0x1000 + SSL_ST_ACCEPT* = 0x2000 + SSL_ST_INIT* = SSL_ST_CONNECT or SSL_ST_ACCEPT OPENSSL_DES_DECRYPT* = 0 OPENSSL_DES_ENCRYPT* = 1 X509_V_OK* = 0 @@ -282,6 +285,13 @@ when compileOption("dynlibOverride", "ssl") or defined(noOpenSSLHacks): # Static linking against OpenSSL < 1.1.0 is not supported discard + when defined(libressl) or defined(openssl10): + proc SSL_state(ssl: SslPtr): cint {.cdecl, dynlib: DLLSSLName, importc.} + proc SSL_in_init*(ssl: SslPtr): cint {.inline.} = + SSl_state(ssl) and SSL_ST_INIT + else: + proc SSL_in_init*(ssl: SslPtr): cint {.cdecl, dynlib: DLLSSLName, importc.} + template OpenSSL_add_all_algorithms*() = discard proc SSLv23_client_method*(): PSSL_METHOD {.cdecl, dynlib: DLLSSLName, importc.} @@ -382,6 +392,24 @@ else: if theProc.isNil: 0.culong else: theProc() + proc SSL_in_init*(ssl: SslPtr): cint = + # A compatibility wrapper for `SSL_in_init()` for OpenSSL 1.0, 1.1 and LibreSSL + const MainProc = "SSL_in_init" + let + theProc {.global.} = cast[proc(ssl: SslPtr): cint {.cdecl.}](sslSymNullable(MainProc)) + # Fallback + sslState {.global.} = cast[proc(ssl: SslPtr): cint {.cdecl.}](sslSymNullable("SSL_state")) + + # FIXME: This shouldn't be needed, but the compiler is complaining about + # `sslState` being GC-ed memory??? + {.gcsafe.}: + if not theProc.isNil: + theProc(ssl) + elif not sslState.isNil: + sslState(ssl) and SSL_ST_INIT + else: + raiseInvalidLibrary MainProc + proc ERR_load_BIO_strings*(){.cdecl, dynlib: DLLUtilName, importc.} proc SSL_new*(context: SslCtx): SslPtr{.cdecl, dynlib: DLLSSLName, importc.} diff --git a/tests/stdlib/thttpclient_ssl.nim b/tests/stdlib/thttpclient_ssl.nim index 2e9d1c5be..f06c1eddd 100644 --- a/tests/stdlib/thttpclient_ssl.nim +++ b/tests/stdlib/thttpclient_ssl.nim @@ -124,7 +124,8 @@ when not defined(windows): let msg = getCurrentExceptionMsg() log "client: exception: " & msg # SSL_shutdown:shutdown while in init - if not (msg.contains("shutdown while in init") or msg.contains("alert number 48") or - msg.contains("routines:CONNECT_CR_CERT:certificate verify failed")): + if not (msg.contains("alert number 48") or + msg.contains("routines:CONNECT_CR_CERT:certificate verify failed") or + msg.contains("routines:tls_process_server_certificate:certificate verify failed")): echo "CVerifyPeer exception: " & msg check(false) From 19ccc5fd8d7e187dde0f9190532d7a168bec9572 Mon Sep 17 00:00:00 2001 From: Leorize Date: Mon, 1 Jun 2020 17:48:12 -0500 Subject: [PATCH 0156/3768] untestable/thttpclient_ssl: catch errors caused by the bad catergory Previously the errors caused by `SSL_shutdown()` masked these, so now we catch them. --- tests/untestable/thttpclient_ssl.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/untestable/thttpclient_ssl.nim b/tests/untestable/thttpclient_ssl.nim index 353567085..a27da6d5c 100644 --- a/tests/untestable/thttpclient_ssl.nim +++ b/tests/untestable/thttpclient_ssl.nim @@ -106,7 +106,7 @@ template evaluate(exception_msg: string, category: Category, desc: string) = exception_msg == "SSL Certificate check failed." or exception_msg.contains("certificate verify failed") or exception_msg.contains("key too small") or - exception_msg.contains "shutdown while in init", exception_msg + exception_msg.contains "alert handshake failure", exception_msg else: # this is unexpected From df91f61e2091164eb5afdd2c88367cf48a042998 Mon Sep 17 00:00:00 2001 From: Leorize Date: Mon, 1 Jun 2020 17:58:55 -0500 Subject: [PATCH 0157/3768] untestable/thttpclient_ssl: fix 10000-sans test --- tests/untestable/thttpclient_ssl.nim | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/untestable/thttpclient_ssl.nim b/tests/untestable/thttpclient_ssl.nim index a27da6d5c..2c3c3b4bb 100644 --- a/tests/untestable/thttpclient_ssl.nim +++ b/tests/untestable/thttpclient_ssl.nim @@ -106,7 +106,9 @@ template evaluate(exception_msg: string, category: Category, desc: string) = exception_msg == "SSL Certificate check failed." or exception_msg.contains("certificate verify failed") or exception_msg.contains("key too small") or - exception_msg.contains "alert handshake failure", exception_msg + exception_msg.contains("alert handshake failure") or + # TODO: This one should only triggers for 10000-sans + exception_msg.contains("excessive message size"), exception_msg else: # this is unexpected From 65b350f11246654f2b04a9a606a731ba0b9a72c5 Mon Sep 17 00:00:00 2001 From: Leorize Date: Mon, 1 Jun 2020 18:15:11 -0500 Subject: [PATCH 0158/3768] untestable/thttpclient_ssl: fix macos --- tests/untestable/thttpclient_ssl.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/untestable/thttpclient_ssl.nim b/tests/untestable/thttpclient_ssl.nim index 2c3c3b4bb..624573a80 100644 --- a/tests/untestable/thttpclient_ssl.nim +++ b/tests/untestable/thttpclient_ssl.nim @@ -107,6 +107,7 @@ template evaluate(exception_msg: string, category: Category, desc: string) = exception_msg.contains("certificate verify failed") or exception_msg.contains("key too small") or exception_msg.contains("alert handshake failure") or + exception_msg.contains("bad dh p length") or # TODO: This one should only triggers for 10000-sans exception_msg.contains("excessive message size"), exception_msg From d06e6eec79c68f8e9bf87c89fc770ca7722c4728 Mon Sep 17 00:00:00 2001 From: Leorize Date: Mon, 1 Jun 2020 20:32:42 -0500 Subject: [PATCH 0159/3768] net: don't clear all errors on close Closing a socket clears the error queue of the entire thread, which seems to be a bit too invasive. Also I don't find any reason in the PR that added this. --- lib/pure/net.nim | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index f7aacfb0c..d807a5112 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -1006,7 +1006,6 @@ proc close*(socket: Socket) = try: when defineSsl: if socket.isSsl and socket.sslHandle != nil: - ErrClearError() # Don't call SSL_shutdown if the connection has not been fully # established, see: # https://github.com/openssl/openssl/issues/710#issuecomment-253897666 From 44d0b4bdfd95a420946d287adacd762bd12ce646 Mon Sep 17 00:00:00 2001 From: Leorize Date: Mon, 1 Jun 2020 21:20:14 -0500 Subject: [PATCH 0160/3768] thttpclient_ssl: be less specific The error we're looking for is "certificate verify failed". The routine that reports this will be different between openssl versions, so it makes no sense to track the routine name as well. --- tests/stdlib/thttpclient_ssl.nim | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/stdlib/thttpclient_ssl.nim b/tests/stdlib/thttpclient_ssl.nim index f06c1eddd..9872985e4 100644 --- a/tests/stdlib/thttpclient_ssl.nim +++ b/tests/stdlib/thttpclient_ssl.nim @@ -125,7 +125,6 @@ when not defined(windows): log "client: exception: " & msg # SSL_shutdown:shutdown while in init if not (msg.contains("alert number 48") or - msg.contains("routines:CONNECT_CR_CERT:certificate verify failed") or - msg.contains("routines:tls_process_server_certificate:certificate verify failed")): + msg.contains("certificate verify failed")): echo "CVerifyPeer exception: " & msg check(false) From 19ccce2617116270c5a83324f5f442fa7d239ee8 Mon Sep 17 00:00:00 2001 From: Leorize Date: Wed, 3 Jun 2020 13:42:12 -0500 Subject: [PATCH 0161/3768] Revert "net: don't clear all errors on close" You're supposed to clear the error queue before any I/O operations. Per SSL_get_error(3) man page. This reverts commit ee3ed37300743addb1344e39bc46d3cd2b89a718. --- lib/pure/net.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index d807a5112..f7aacfb0c 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -1006,6 +1006,7 @@ proc close*(socket: Socket) = try: when defineSsl: if socket.isSsl and socket.sslHandle != nil: + ErrClearError() # Don't call SSL_shutdown if the connection has not been fully # established, see: # https://github.com/openssl/openssl/issues/710#issuecomment-253897666 From 78982b52f035904de0ba2b90f6e4cb43fb76e300 Mon Sep 17 00:00:00 2001 From: Leorize Date: Wed, 3 Jun 2020 13:43:17 -0500 Subject: [PATCH 0162/3768] net: don't clear error queue unless shutdown() will be performed --- lib/pure/net.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index f7aacfb0c..1442e8544 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -1006,7 +1006,6 @@ proc close*(socket: Socket) = try: when defineSsl: if socket.isSsl and socket.sslHandle != nil: - ErrClearError() # Don't call SSL_shutdown if the connection has not been fully # established, see: # https://github.com/openssl/openssl/issues/710#issuecomment-253897666 @@ -1015,6 +1014,7 @@ proc close*(socket: Socket) = # it is valid, under the TLS standard, to perform a unidirectional # shutdown i.e not wait for the peers "close notify" alert with a second # call to SSL_shutdown + ErrClearError() let res = SSL_shutdown(socket.sslHandle) if res == 0: discard From f243f9aeb5c5ef6f59efadbee81feb918a20fae8 Mon Sep 17 00:00:00 2001 From: Leorize Date: Wed, 3 Jun 2020 14:51:05 -0500 Subject: [PATCH 0163/3768] openssl: fix erroneous function signatures Now matches the declaration in openssl/err.h --- lib/pure/net.nim | 2 -- lib/wrappers/openssl.nim | 6 +++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 1442e8544..c60caab08 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -479,8 +479,6 @@ when defineSsl: let err = ERR_peek_last_error() if err == 0: raise newException(SslError, "No error reported.") - if err == -1: - raiseOSError(osLastError()) var errStr = $ERR_error_string(err, nil) case err of 336032814, 336032784: diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim index 5fd4def0b..01c53b755 100644 --- a/lib/wrappers/openssl.nim +++ b/lib/wrappers/openssl.nim @@ -478,10 +478,10 @@ proc BIO_free*(b: BIO): cint{.cdecl, dynlib: DLLUtilName, importc.} proc ERR_print_errors_fp*(fp: File){.cdecl, dynlib: DLLSSLName, importc.} -proc ERR_error_string*(e: cint, buf: cstring): cstring{.cdecl, +proc ERR_error_string*(e: culong, buf: cstring): cstring{.cdecl, dynlib: DLLUtilName, importc.} -proc ERR_get_error*(): cint{.cdecl, dynlib: DLLUtilName, importc.} -proc ERR_peek_last_error*(): cint{.cdecl, dynlib: DLLUtilName, importc.} +proc ERR_get_error*(): culong{.cdecl, dynlib: DLLUtilName, importc.} +proc ERR_peek_last_error*(): culong{.cdecl, dynlib: DLLUtilName, importc.} proc OPENSSL_config*(configName: cstring){.cdecl, dynlib: DLLSSLName, importc.} From 6cb94b5da6ac409915cc0ca3f50c50214edee0ef Mon Sep 17 00:00:00 2001 From: Leorize Date: Wed, 3 Jun 2020 15:11:10 -0500 Subject: [PATCH 0164/3768] asyncnet, net: clear openssl error queue before performing I/O Per SSL_get_error(3): The current thread's error queue must be empty before the TLS/SSL I/O operation is attempted, or SSL_get_error() will not work reliably. There has been records of not clearing the error queue causing weird SSL errors when there shouldn't be any, see: https://github.com/openssl/openssl/issues/11889 --- lib/pure/asyncnet.nim | 1 + lib/pure/net.nim | 7 +++++++ tests/stdlib/thttpclient_ssl.nim | 1 + 3 files changed, 9 insertions(+) diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index 8df839c14..bd9cd4312 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -718,6 +718,7 @@ proc close*(socket: AsyncSocket) = # established, see: # https://github.com/openssl/openssl/issues/710#issuecomment-253897666 if SSL_in_init(socket.sslHandle) == 0: + ErrClearError() SSL_shutdown(socket.sslHandle) else: 0 diff --git a/lib/pure/net.nim b/lib/pure/net.nim index c60caab08..af66c3a15 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -752,12 +752,14 @@ when defineSsl: # Discard result in case OpenSSL version doesn't support SNI, or we're # not using TLSv1+ discard SSL_set_tlsext_host_name(socket.sslHandle, hostname) + ErrClearError() let ret = SSL_connect(socket.sslHandle) socketError(socket, ret) when not defined(nimDisableCertificateValidation) and not defined(windows): if hostname.len > 0 and not isIpAddress(hostname): socket.checkCertName(hostname) of handshakeAsServer: + ErrClearError() let ret = SSL_accept(socket.sslHandle) socketError(socket, ret) @@ -926,6 +928,7 @@ proc acceptAddr*(server: Socket, client: var owned(Socket), address: var string, # We must wrap the client sock in a ssl context. server.sslContext.wrapSocket(client) + ErrClearError() let ret = SSL_accept(client.sslHandle) socketError(client, ret, false) @@ -955,6 +958,7 @@ when false: #defineSsl: if not client.isSsl or client.sslHandle == nil: server.sslContext.wrapSocket(client) + ErrClearError() let ret = SSL_accept(client.sslHandle) while ret <= 0: let err = SSL_get_error(client.sslHandle, ret) @@ -1135,6 +1139,7 @@ proc uniRecv(socket: Socket, buffer: pointer, size, flags: cint): int = assert(not socket.isClosed, "Cannot `recv` on a closed socket") when defineSsl: if socket.isSsl: + ErrClearError() return SSL_read(socket.sslHandle, buffer, size) return recv(socket.fd, buffer, size, flags) @@ -1484,6 +1489,7 @@ proc send*(socket: Socket, data: pointer, size: int): int {. assert(not socket.isClosed, "Cannot `send` on a closed socket") when defineSsl: if socket.isSsl: + ErrClearError() return SSL_write(socket.sslHandle, cast[cstring](data), size) when useWinVersion or defined(macosx): @@ -1768,6 +1774,7 @@ proc connect*(socket: Socket, address: string, # not using TLSv1+ discard SSL_set_tlsext_host_name(socket.sslHandle, address) + ErrClearError() let ret = SSL_connect(socket.sslHandle) socketError(socket, ret) when not defined(nimDisableCertificateValidation) and not defined(windows): diff --git a/tests/stdlib/thttpclient_ssl.nim b/tests/stdlib/thttpclient_ssl.nim index 9872985e4..71745c9df 100644 --- a/tests/stdlib/thttpclient_ssl.nim +++ b/tests/stdlib/thttpclient_ssl.nim @@ -61,6 +61,7 @@ when not defined(windows): var ssl: SslPtr = SSL_new(ctx.context) discard SSL_set_fd(ssl, client.getFd()) log "server: accepting connection" + ErrClearError() if SSL_accept(ssl) <= 0: ERR_print_errors_fp(stderr) else: From 1fb2a58674dd8136b246c7499cd295e85484d8c3 Mon Sep 17 00:00:00 2001 From: Leorize Date: Wed, 3 Jun 2020 17:02:18 -0500 Subject: [PATCH 0165/3768] net: use a secure cipher list by default Previously, the `net` module use the blanket "ALL" as the default cipher list. This list may contain security ciphers that are weak and/or outdated according to the current standard. This commit introduces a new module `ssl_config` that contains the latest OpenSSL configurations as recommended by Mozilla OpSec, and make the `net` module use the cipher list targeting `intermediate` compatibility level as the default. --- changelog.md | 9 ++++++ lib/pure/net.nim | 3 +- lib/pure/ssl_config.nim | 51 +++++++++++++++++++++++++++++ tools/ssl_config_parser.nim | 62 ++++++++++++++++++++++++++++++++++++ tools/ssl_config_parser.nims | 1 + 5 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 lib/pure/ssl_config.nim create mode 100644 tools/ssl_config_parser.nim create mode 100644 tools/ssl_config_parser.nims diff --git a/changelog.md b/changelog.md index 25fc3535c..e20077feb 100644 --- a/changelog.md +++ b/changelog.md @@ -99,6 +99,15 @@ - `osproc.execCmdEx` now takes an optional `input` for stdin, `workingDir` and `env` parameters. +- Add `ssl_config` module containing lists of secure ciphers as recommended by + [Mozilla OpSec](https://wiki.mozilla.org/Security/Server_Side_TLS) + +- `net.newContext` now uses 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 compatiblity with the majority of the Internet. + ## Language changes - In the 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: diff --git a/lib/pure/net.nim b/lib/pure/net.nim index af66c3a15..a5643330e 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -68,6 +68,7 @@ import std/private/since import nativesockets, os, strutils, times, sets, options, std/monotimes from ssl_certs import scanSSLCertificates +import ssl_config export nativesockets.Port, nativesockets.`$`, nativesockets.`==` export Domain, SockType, Protocol @@ -533,7 +534,7 @@ when defineSsl: raiseSSLError("Verification of private key file failed.") proc newContext*(protVersion = protSSLv23, verifyMode = CVerifyPeer, - certFile = "", keyFile = "", cipherList = "ALL", + certFile = "", keyFile = "", cipherList = CiphersIntermediate, caDir = "", caFile = ""): SSLContext = ## Creates an SSL context. ## diff --git a/lib/pure/ssl_config.nim b/lib/pure/ssl_config.nim new file mode 100644 index 000000000..14f66ede4 --- /dev/null +++ b/lib/pure/ssl_config.nim @@ -0,0 +1,51 @@ +# This file was automatically generated by tools/ssl_config_parser on 2020-06-03T22:02:05Z. DO NOT EDIT. + +## This module contains SSL configuration parameters obtained from +## `Mozilla OpSec `_. +## +## The configuration file used to generate this module: https://ssl-config.mozilla.org/guidelines/5.4.json + +const CiphersModern* = "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256" + ## An OpenSSL-compatible list of secure ciphers for ``modern`` compatibility + ## per Mozilla's recommendations. + ## + ## Oldest clients supported by this list: + ## * Firefox 63 + ## * Android 10.0 + ## * Chrome 70 + ## * Edge 75 + ## * Java 11 + ## * OpenSSL 1.1.1 + ## * Opera 57 + ## * Safari 12.1 + +const CiphersIntermediate* = "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384" + ## An OpenSSL-compatible list of secure ciphers for ``intermediate`` compatibility + ## per Mozilla's recommendations. + ## + ## Oldest clients supported by this list: + ## * Firefox 27 + ## * Android 4.4.2 + ## * Chrome 31 + ## * Edge + ## * IE 11 on Windows 7 + ## * Java 8u31 + ## * OpenSSL 1.0.1 + ## * Opera 20 + ## * Safari 9 + +const CiphersOld* = "TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA" + ## An OpenSSL-compatible list of secure ciphers for ``old`` compatibility + ## per Mozilla's recommendations. + ## + ## Oldest clients supported by this list: + ## * Firefox 1 + ## * Android 2.3 + ## * Chrome 1 + ## * Edge 12 + ## * IE8 on Windows XP + ## * Java 6 + ## * OpenSSL 0.9.8 + ## * Opera 5 + ## * Safari 1 + diff --git a/tools/ssl_config_parser.nim b/tools/ssl_config_parser.nim new file mode 100644 index 000000000..d13ea1ee2 --- /dev/null +++ b/tools/ssl_config_parser.nim @@ -0,0 +1,62 @@ +# +# +# SSL configuration generator +# (c) Copyright 2020 Leorize +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +import httpclient, json, net, strformat, strutils, sequtils, times + +const + ConfigSource = "https://ssl-config.mozilla.org/guidelines/latest.json" + OutputFile = "ssl_config.nim" + +func appendToList(list, str: string): string = + if list.len == 0: + result = str + elif str.len == 0: + result = list + else: + result = list & ':' & str + +proc main() = + let + client = newHttpClient(sslContext = newContext(verifyMode = CVerifyPeer)) + resp = client.get(ConfigSource) + defer: client.close() + if not resp.code.is2xx: + quit "Couldn't fetch configuration, server returned: " & $resp.code + + let configs = resp.bodyStream.parseJson("ssl-config.json") + + let generationTime = now().utc() + let output = open(OutputFile, fmWrite) + echo "Generating ", OutputFile + output.writeLine(&""" +# This file was automatically generated by tools/ssl_config_parser on {generationTime}. DO NOT EDIT. + +## This module contains SSL configuration parameters obtained from +## `Mozilla OpSec `_. +## +## The configuration file used to generate this module: {configs["href"].getStr} +""") + + for name, config in configs["configurations"]: + let + constantName = "Ciphers" & name[0].toUpperAscii & name[1..^1] + ciphers = config["ciphersuites"].foldl(a.appendToList b.getStr, "").appendToList( + config["ciphers"]["openssl"].foldl(a.appendToList b.getStr, "") + ) + oldestClients = config["oldest_clients"].foldl(a & "\n ## * " & b.getStr, "") + + output.writeLine(&""" +const {constantName}* = "{ciphers}" + ## An OpenSSL-compatible list of secure ciphers for ``{name}`` compatibility + ## per Mozilla's recommendations. + ## + ## Oldest clients supported by this list:{oldestClients} +""") + +when isMainModule: main() diff --git a/tools/ssl_config_parser.nims b/tools/ssl_config_parser.nims new file mode 100644 index 000000000..796d18894 --- /dev/null +++ b/tools/ssl_config_parser.nims @@ -0,0 +1 @@ +--d:ssl From 23cc4091d93b2aa9f3a342f14da5d94c19b11392 Mon Sep 17 00:00:00 2001 From: Leorize Date: Wed, 3 Jun 2020 17:25:10 -0500 Subject: [PATCH 0166/3768] untestable/thttpclient_ssl: move incomplete-chain to dubious_broken It's unsure why this test fail for macOS, but it doesn't hurt if it's not passing. --- tests/untestable/thttpclient_ssl.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/untestable/thttpclient_ssl.nim b/tests/untestable/thttpclient_ssl.nim index 624573a80..ef9d71078 100644 --- a/tests/untestable/thttpclient_ssl.nim +++ b/tests/untestable/thttpclient_ssl.nim @@ -41,7 +41,7 @@ const certificate_tests: array[0..55, CertTest] = [ ("https://pinning-test.badssl.com/", bad_broken, "pinning-test"), ("https://no-common-name.badssl.com/", dubious_broken, "no-common-name"), ("https://no-subject.badssl.com/", dubious_broken, "no-subject"), - ("https://incomplete-chain.badssl.com/", dubious, "incomplete-chain"), + ("https://incomplete-chain.badssl.com/", dubious_broken, "incomplete-chain"), ("https://sha1-intermediate.badssl.com/", bad_broken, "sha1-intermediate"), ("https://sha256.badssl.com/", good, "sha256"), ("https://sha384.badssl.com/", good, "sha384"), From 3d4d3f4ac2939bc22f830363c46f7a1c0c209c8c Mon Sep 17 00:00:00 2001 From: Leorize Date: Wed, 3 Jun 2020 17:39:17 -0500 Subject: [PATCH 0167/3768] untestable/thttpclient_ssl: some tests are no longer broken With the changes to the default cipher suites, certain tests are no longer broken and are acting as they should. --- tests/untestable/thttpclient_ssl.nim | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/untestable/thttpclient_ssl.nim b/tests/untestable/thttpclient_ssl.nim index ef9d71078..038eba99d 100644 --- a/tests/untestable/thttpclient_ssl.nim +++ b/tests/untestable/thttpclient_ssl.nim @@ -42,7 +42,7 @@ const certificate_tests: array[0..55, CertTest] = [ ("https://no-common-name.badssl.com/", dubious_broken, "no-common-name"), ("https://no-subject.badssl.com/", dubious_broken, "no-subject"), ("https://incomplete-chain.badssl.com/", dubious_broken, "incomplete-chain"), - ("https://sha1-intermediate.badssl.com/", bad_broken, "sha1-intermediate"), + ("https://sha1-intermediate.badssl.com/", bad, "sha1-intermediate"), ("https://sha256.badssl.com/", good, "sha256"), ("https://sha384.badssl.com/", good, "sha384"), ("https://sha512.badssl.com/", good, "sha512"), @@ -54,7 +54,7 @@ const certificate_tests: array[0..55, CertTest] = [ ("https://rsa8192.badssl.com/", dubious_broken, "rsa8192"), ("http://http.badssl.com/", good, "regular http"), ("https://http.badssl.com/", bad_broken, "http on https URL"), # FIXME - ("https://cbc.badssl.com/", dubious_broken, "cbc"), + ("https://cbc.badssl.com/", dubious, "cbc"), ("https://rc4-md5.badssl.com/", bad, "rc4-md5"), ("https://rc4.badssl.com/", bad, "rc4"), ("https://3des.badssl.com/", bad, "3des"), @@ -68,10 +68,10 @@ const certificate_tests: array[0..55, CertTest] = [ ("https://dh2048.badssl.com/", good, "dh2048"), ("https://dh-small-subgroup.badssl.com/", bad_broken, "dh-small-subgroup"), ("https://dh-composite.badssl.com/", bad_broken, "dh-composite"), - ("https://static-rsa.badssl.com/", dubious_broken, "static-rsa"), - ("https://tls-v1-0.badssl.com:1010/", dubious_broken, "tls-v1-0"), - ("https://tls-v1-1.badssl.com:1011/", dubious_broken, "tls-v1-1"), - ("https://invalid-expected-sct.badssl.com/", bad_broken, "invalid-expected-sct"), + ("https://static-rsa.badssl.com/", dubious, "static-rsa"), + ("https://tls-v1-0.badssl.com:1010/", dubious, "tls-v1-0"), + ("https://tls-v1-1.badssl.com:1011/", dubious, "tls-v1-1"), + ("https://invalid-expected-sct.badssl.com/", bad, "invalid-expected-sct"), ("https://hsts.badssl.com/", good, "hsts"), ("https://upgrade.badssl.com/", good, "upgrade"), ("https://preloaded-hsts.badssl.com/", good, "preloaded-hsts"), From 1715935846416cc0e0979f8cc614c0b20cf02841 Mon Sep 17 00:00:00 2001 From: Leorize Date: Wed, 3 Jun 2020 17:46:45 -0500 Subject: [PATCH 0168/3768] ssl_config_parser: refactor for sanity reasons --- tools/ssl_config_parser.nim | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/tools/ssl_config_parser.nim b/tools/ssl_config_parser.nim index d13ea1ee2..6c0cccf1c 100644 --- a/tools/ssl_config_parser.nim +++ b/tools/ssl_config_parser.nim @@ -13,14 +13,6 @@ const ConfigSource = "https://ssl-config.mozilla.org/guidelines/latest.json" OutputFile = "ssl_config.nim" -func appendToList(list, str: string): string = - if list.len == 0: - result = str - elif str.len == 0: - result = list - else: - result = list & ':' & str - proc main() = let client = newHttpClient(sslContext = newContext(verifyMode = CVerifyPeer)) @@ -46,17 +38,27 @@ proc main() = for name, config in configs["configurations"]: let constantName = "Ciphers" & name[0].toUpperAscii & name[1..^1] - ciphers = config["ciphersuites"].foldl(a.appendToList b.getStr, "").appendToList( - config["ciphers"]["openssl"].foldl(a.appendToList b.getStr, "") - ) - oldestClients = config["oldest_clients"].foldl(a & "\n ## * " & b.getStr, "") - output.writeLine(&""" + var ciphers: string + for c in config["ciphersuites"].getElems & config["ciphers"]["openssl"].getElems: + if ciphers.len == 0: + ciphers.add c.getStr + else: + ciphers.add ':' + ciphers.add c.getStr + + var constant = &""" const {constantName}* = "{ciphers}" ## An OpenSSL-compatible list of secure ciphers for ``{name}`` compatibility ## per Mozilla's recommendations. ## - ## Oldest clients supported by this list:{oldestClients} -""") + ## Oldest clients supported by this list: +""" + + for c in config["oldest_clients"]: + constant.add " ## * " & c.getStr + constant.add '\n' + + output.writeLine constant when isMainModule: main() From 256841cf2cfbeb0ccd28de53427583ef348a1ab6 Mon Sep 17 00:00:00 2001 From: Leorize Date: Wed, 3 Jun 2020 17:51:46 -0500 Subject: [PATCH 0169/3768] changelog.md: clarify that only the default has changed [ci-skip] --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index e20077feb..bbcf2ed18 100644 --- a/changelog.md +++ b/changelog.md @@ -102,7 +102,7 @@ - Add `ssl_config` module containing lists of secure ciphers as recommended by [Mozilla OpSec](https://wiki.mozilla.org/Security/Server_Side_TLS) -- `net.newContext` now uses the list of ciphers targeting +- `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 From e4e3d99ee2381e5fc2fe922aa6933df8cebb599b Mon Sep 17 00:00:00 2001 From: Leorize Date: Wed, 3 Jun 2020 23:48:24 -0500 Subject: [PATCH 0170/3768] asyncnet: clear SSL error queue before performing I/O Follow up of 476be6cc278d58232e2c6cd3515eb24eca7e2d38 --- lib/pure/asyncnet.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index bd9cd4312..3fc1a0177 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -253,6 +253,7 @@ when defineSsl: op: untyped) = var opResult {.inject.} = -1.cint while opResult < 0: + ErrClearError() # Call the desired operation. opResult = op # Bit hackish here. From 9278e785bd9490029ea2f9eb353d241fd14b302a Mon Sep 17 00:00:00 2001 From: Leorize Date: Wed, 3 Jun 2020 23:51:59 -0500 Subject: [PATCH 0171/3768] wrappers/openssl: fix SSL_CTX_ctrl signature --- lib/wrappers/openssl.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim index 01c53b755..cf248fc86 100644 --- a/lib/wrappers/openssl.nim +++ b/lib/wrappers/openssl.nim @@ -531,7 +531,7 @@ proc CRYPTO_malloc_init*() = when not useWinVersion and not defined(macosx) and not defined(android) and not defined(nimNoAllocForSSL): CRYPTO_set_mem_functions(allocWrapper, reallocWrapper, deallocWrapper) -proc SSL_CTX_ctrl*(ctx: SslCtx, cmd: cint, larg: int, parg: pointer): int{. +proc SSL_CTX_ctrl*(ctx: SslCtx, cmd: cint, larg: clong, parg: pointer): clong{. cdecl, dynlib: DLLSSLName, importc.} proc SSL_CTX_callback_ctrl(ctx: SslCtx, typ: cint, fp: PFunction): int{. From 6c0f86c486f6491a2602e95228e63f3c44ab14fc Mon Sep 17 00:00:00 2001 From: Leorize Date: Thu, 4 Jun 2020 00:01:13 -0500 Subject: [PATCH 0172/3768] net: enable automatic EC curve selection for OpenSSL 1.0.2 This setting is required for servers running OpenSSL < 1.1.0 to support EC-based secure ciphers that is now part of the default cipher list. --- lib/pure/net.nim | 7 +++++++ lib/wrappers/openssl.nim | 16 ++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index a5643330e..f628ee056 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -580,6 +580,13 @@ when defineSsl: if newCTX.SSL_CTX_set_cipher_list(cipherList) != 1: raiseSSLError() + # Automatically the best ECDH curve for client exchange. Without this, ECDH + # ciphers will be ignored by the server. + # + # From OpenSSL >= 1.1.0, this setting is set by default and can't be + # overriden. + if newCTX.SSL_CTX_set_ecdh_auto(1) != 1: + raiseSSLError() when defined(nimDisableCertificateValidation) or defined(windows): newCTX.SSL_CTX_set_verify(SSL_VERIFY_NONE, nil) diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim index cf248fc86..29cda3efc 100644 --- a/lib/wrappers/openssl.nim +++ b/lib/wrappers/openssl.nim @@ -181,6 +181,7 @@ const SSL_CTRL_SET_TLSEXT_SERVERNAME_CB = 53 SSL_CTRL_SET_TLSEXT_SERVERNAME_ARG = 54 SSL_CTRL_SET_TLSEXT_HOSTNAME = 55 + SSL_CTRL_SET_ECDH_AUTO* = 94 TLSEXT_NAMETYPE_host_name* = 0 SSL_TLSEXT_ERR_OK* = 0 SSL_TLSEXT_ERR_ALERT_WARNING* = 1 @@ -263,6 +264,12 @@ when compileOption("dynlibOverride", "ssl") or defined(noOpenSSLHacks): proc SSL_library_init*(): cint {.cdecl, dynlib: DLLSSLName, importc, discardable.} proc SSL_load_error_strings*() {.cdecl, dynlib: DLLSSLName, importc.} proc SSLv23_method*(): PSSL_METHOD {.cdecl, dynlib: DLLSSLName, importc.} + + proc getOpenSSLVersion*(): culong = + ## This interface is not supported for OpenSSL < 1.1.0 and will + ## always return 0. The interface is provided to aid code + ## supporting multiple OpenSSL versions. + 0 else: proc OPENSSL_init_ssl*(opts: uint64, settings: uint8): cint {.cdecl, dynlib: DLLSSLName, importc, discardable.} proc SSL_library_init*(): cint {.discardable.} = @@ -588,6 +595,15 @@ proc SSL_CTX_use_psk_identity_hint*(ctx: SslCtx; hint: cstring): cint {.cdecl, d proc SSL_get_psk_identity*(ssl: SslPtr): cstring {.cdecl, dynlib: DLLSSLName, importc.} ## Get PSK identity. +proc SSL_CTX_set_ecdh_auto*(ctx: SslCtx, onoff: cint): cint {.inline.} = + ## Set automatic curve selection. + ## + ## On OpenSSL >= 1.1.0 this is on by default and cannot be disabled. + if getOpenSSLVersion() < 0x010100000: + result = cint SSL_CTX_ctrl(ctx, SSL_CTRL_SET_ECDH_AUTO, onoff, nil) + else: + result = 1 + proc bioNew*(b: PBIO_METHOD): BIO{.cdecl, dynlib: DLLUtilName, importc: "BIO_new".} proc bioFreeAll*(b: BIO){.cdecl, dynlib: DLLUtilName, importc: "BIO_free_all".} proc bioSMem*(): PBIO_METHOD{.cdecl, dynlib: DLLUtilName, importc: "BIO_s_mem".} From 61d4fbce4f0ae764092c51591112ce39d78fca73 Mon Sep 17 00:00:00 2001 From: Leorize Date: Thu, 4 Jun 2020 00:12:26 -0500 Subject: [PATCH 0173/3768] wrappers/openssl: getOpenSSLVersion is gcsafe Yet another weird {.gcsafe.} triggering when it shouldn't. --- lib/wrappers/openssl.nim | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim index 29cda3efc..2a03b577f 100644 --- a/lib/wrappers/openssl.nim +++ b/lib/wrappers/openssl.nim @@ -395,9 +395,10 @@ else: proc getOpenSSLVersion*(): culong = ## Return OpenSSL version as unsigned long or 0 if not available let theProc = cast[proc(): culong {.cdecl.}](sslSymNullable("OpenSSL_version_num")) - result = - if theProc.isNil: 0.culong - else: theProc() + {.gcsafe.}: + result = + if theProc.isNil: 0.culong + else: theProc() proc SSL_in_init*(ssl: SslPtr): cint = # A compatibility wrapper for `SSL_in_init()` for OpenSSL 1.0, 1.1 and LibreSSL From b5b191af4bb3dea9c49b2637d203957daf42478a Mon Sep 17 00:00:00 2001 From: Leorize Date: Thu, 4 Jun 2020 00:29:39 -0500 Subject: [PATCH 0174/3768] wrappers/openssl: fix SSL_CTX_set_mode Follow up of 794042080b270d9da8d64bb4285ce83787bdec7e --- lib/wrappers/openssl.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim index 2a03b577f..5af1edea5 100644 --- a/lib/wrappers/openssl.nim +++ b/lib/wrappers/openssl.nim @@ -546,7 +546,7 @@ proc SSL_CTX_callback_ctrl(ctx: SslCtx, typ: cint, fp: PFunction): int{. cdecl, dynlib: DLLSSLName, importc.} proc SSLCTXSetMode*(ctx: SslCtx, mode: int): int = - result = SSL_CTX_ctrl(ctx, SSL_CTRL_MODE, mode, nil) + result = SSL_CTX_ctrl(ctx, SSL_CTRL_MODE, clong mode, nil) proc SSL_ctrl*(ssl: SslPtr, cmd: cint, larg: int, parg: pointer): int{. cdecl, dynlib: DLLSSLName, importc.} From 279438f1df355e2eee09734cc0fc5ab86e0d2271 Mon Sep 17 00:00:00 2001 From: Leorize Date: Thu, 4 Jun 2020 08:53:14 -0500 Subject: [PATCH 0175/3768] net: don't call set_ecdh_auto for super old OpenSSL And the fun thing is that currently we use a super old OpenSSL on Windows. --- lib/pure/net.nim | 2 +- lib/wrappers/openssl.nim | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index f628ee056..5db3edbed 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -585,7 +585,7 @@ when defineSsl: # # From OpenSSL >= 1.1.0, this setting is set by default and can't be # overriden. - if newCTX.SSL_CTX_set_ecdh_auto(1) != 1: + if getOpenSSLVersion() >= 0x10002000 and newCTX.SSL_CTX_set_ecdh_auto(1) != 1: raiseSSLError() when defined(nimDisableCertificateValidation) or defined(windows): diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim index 5af1edea5..c2f0250c0 100644 --- a/lib/wrappers/openssl.nim +++ b/lib/wrappers/openssl.nim @@ -264,12 +264,10 @@ when compileOption("dynlibOverride", "ssl") or defined(noOpenSSLHacks): proc SSL_library_init*(): cint {.cdecl, dynlib: DLLSSLName, importc, discardable.} proc SSL_load_error_strings*() {.cdecl, dynlib: DLLSSLName, importc.} proc SSLv23_method*(): PSSL_METHOD {.cdecl, dynlib: DLLSSLName, importc.} + proc SSLeay(): culong {.cdecl, dynlib: DLLSSLName, importc.} proc getOpenSSLVersion*(): culong = - ## This interface is not supported for OpenSSL < 1.1.0 and will - ## always return 0. The interface is provided to aid code - ## supporting multiple OpenSSL versions. - 0 + SSLeay() else: proc OPENSSL_init_ssl*(opts: uint64, settings: uint8): cint {.cdecl, dynlib: DLLSSLName, importc, discardable.} proc SSL_library_init*(): cint {.discardable.} = @@ -394,7 +392,7 @@ else: proc getOpenSSLVersion*(): culong = ## Return OpenSSL version as unsigned long or 0 if not available - let theProc = cast[proc(): culong {.cdecl.}](sslSymNullable("OpenSSL_version_num")) + let theProc = cast[proc(): culong {.cdecl.}](sslSymNullable("OpenSSL_version_num", "SSLeay")) {.gcsafe.}: result = if theProc.isNil: 0.culong From 0ca069b4f8ff4e4f6d85178247818857970ce591 Mon Sep 17 00:00:00 2001 From: Leorize Date: Thu, 4 Jun 2020 09:00:03 -0500 Subject: [PATCH 0176/3768] net: use CiphersOld list for Windows The default DLLs shipped are too old, switch to CiphersOld list to ensure compatibility. This commit should be reverted before any future release. --- lib/pure/net.nim | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 5db3edbed..8da14f6e3 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -534,7 +534,10 @@ when defineSsl: raiseSSLError("Verification of private key file failed.") proc newContext*(protVersion = protSSLv23, verifyMode = CVerifyPeer, - certFile = "", keyFile = "", cipherList = CiphersIntermediate, + certFile = "", keyFile = "", + # XXX: update the OpenSSL that we ship with Windows for the + # next release. + cipherList = when not defined(windows): CiphersIntermediate else: CiphersOld, caDir = "", caFile = ""): SSLContext = ## Creates an SSL context. ## From d406f588d89fcf4a19c3e556ca57def9644a8a57 Mon Sep 17 00:00:00 2001 From: Leorize Date: Thu, 4 Jun 2020 09:22:41 -0500 Subject: [PATCH 0177/3768] wrappers/openssl: the version number comes from the utility library Previously we loaded the SSL library for this, but that's not where the symbol resides. --- lib/wrappers/openssl.nim | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim index c2f0250c0..8a80a554c 100644 --- a/lib/wrappers/openssl.nim +++ b/lib/wrappers/openssl.nim @@ -264,7 +264,7 @@ when compileOption("dynlibOverride", "ssl") or defined(noOpenSSLHacks): proc SSL_library_init*(): cint {.cdecl, dynlib: DLLSSLName, importc, discardable.} proc SSL_load_error_strings*() {.cdecl, dynlib: DLLSSLName, importc.} proc SSLv23_method*(): PSSL_METHOD {.cdecl, dynlib: DLLSSLName, importc.} - proc SSLeay(): culong {.cdecl, dynlib: DLLSSLName, importc.} + proc SSLeay(): culong {.cdecl, dynlib: DLLUtilName, importc.} proc getOpenSSLVersion*(): culong = SSLeay() @@ -278,7 +278,7 @@ when compileOption("dynlibOverride", "ssl") or defined(noOpenSSLHacks): proc SSLv23_method*(): PSSL_METHOD = TLS_method() - proc OpenSSL_version_num(): culong {.cdecl, dynlib: DLLSSLName, importc.} + proc OpenSSL_version_num(): culong {.cdecl, dynlib: DLLUtilName, importc.} proc getOpenSSLVersion*(): culong = ## Return OpenSSL version as unsigned long @@ -318,13 +318,18 @@ else: result = sslMod - proc sslSymNullable(name: string, alternativeName = ""): pointer = + proc utilModule(): LibHandle {.inline.} = + var utilMod {.global.}: LibHandle + if utilMod.isNil: utilMod = loadLibPattern(DLLUtilName) + + result = utilMod + + proc symNullable(dll: LibHandle, name: string, alternativeName = ""): pointer = # Load from DLL. - var sslDynlib = sslModule() - if not sslDynlib.isNil: - result = symAddr(sslDynlib, name) + if not dll.isNil: + result = symAddr(dll, name) if result.isNil and alternativeName.len > 0: - result = symAddr(sslDynlib, alternativeName) + result = symAddr(dll, alternativeName) # Attempt to load from current exe. if result.isNil: @@ -332,12 +337,18 @@ else: if thisDynlib.isNil: return nil result = symAddr(thisDynlib, name) if result.isNil and alternativeName.len > 0: - result = symAddr(sslDynlib, alternativeName) + result = symAddr(thisDynlib, alternativeName) + + proc sslSymNullable(name: string, alternativeName = ""): pointer = + sslModule().symNullable(name, alternativeName) proc sslSymThrows(name: string, alternativeName = ""): pointer = result = sslSymNullable(name, alternativeName) if result.isNil: raiseInvalidLibrary(name) + proc utilSymNullable(name: string, alternativeName = ""): pointer = + utilModule().symNullable(name, alternativeName) + proc loadPSSLMethod(method1, method2: string): PSSL_METHOD = ## Load from OpenSSL if available, otherwise ## @@ -392,7 +403,7 @@ else: proc getOpenSSLVersion*(): culong = ## Return OpenSSL version as unsigned long or 0 if not available - let theProc = cast[proc(): culong {.cdecl.}](sslSymNullable("OpenSSL_version_num", "SSLeay")) + let theProc = cast[proc(): culong {.cdecl.}](utilSymNullable("OpenSSL_version_num", "SSLeay")) {.gcsafe.}: result = if theProc.isNil: 0.culong From e80a23f4d2bf4f628cb6bf7c235cb63cc58fdcb0 Mon Sep 17 00:00:00 2001 From: Leorize Date: Thu, 4 Jun 2020 09:57:19 -0500 Subject: [PATCH 0178/3768] net: revert compatibility changes for Windows This reverts commit d53a6355f3ad4401731142d8fc66b95ecfdfb321 and 688b1f250ed058ecc49737dc75fd089206fa25b9. --- lib/pure/net.nim | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 8da14f6e3..f628ee056 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -534,10 +534,7 @@ when defineSsl: raiseSSLError("Verification of private key file failed.") proc newContext*(protVersion = protSSLv23, verifyMode = CVerifyPeer, - certFile = "", keyFile = "", - # XXX: update the OpenSSL that we ship with Windows for the - # next release. - cipherList = when not defined(windows): CiphersIntermediate else: CiphersOld, + certFile = "", keyFile = "", cipherList = CiphersIntermediate, caDir = "", caFile = ""): SSLContext = ## Creates an SSL context. ## @@ -588,7 +585,7 @@ when defineSsl: # # From OpenSSL >= 1.1.0, this setting is set by default and can't be # overriden. - if getOpenSSLVersion() >= 0x10002000 and newCTX.SSL_CTX_set_ecdh_auto(1) != 1: + if newCTX.SSL_CTX_set_ecdh_auto(1) != 1: raiseSSLError() when defined(nimDisableCertificateValidation) or defined(windows): From b323bccd8125e9fa4ff28330da1a3adf206b8d32 Mon Sep 17 00:00:00 2001 From: Leorize Date: Thu, 4 Jun 2020 10:50:28 -0500 Subject: [PATCH 0179/3768] wrappers/openssl: enable SSL_CTX_set_ecdh_auto for LibreSSL This procedure is not no-op for older LibreSSL, and the ABI is kept for newer versions, so there's no harm in enabling it unconditionally for all LibreSSL versions. --- lib/wrappers/openssl.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim index 8a80a554c..82fcb2194 100644 --- a/lib/wrappers/openssl.nim +++ b/lib/wrappers/openssl.nim @@ -609,7 +609,7 @@ proc SSL_CTX_set_ecdh_auto*(ctx: SslCtx, onoff: cint): cint {.inline.} = ## Set automatic curve selection. ## ## On OpenSSL >= 1.1.0 this is on by default and cannot be disabled. - if getOpenSSLVersion() < 0x010100000: + if getOpenSSLVersion() < 0x010100000 or getOpenSSLVersion() == 0x020000000: result = cint SSL_CTX_ctrl(ctx, SSL_CTRL_SET_ECDH_AUTO, onoff, nil) else: result = 1 From 3cd74c6408353aec97f04672140d5de226f3f3af Mon Sep 17 00:00:00 2001 From: Leorize Date: Thu, 4 Jun 2020 17:41:53 -0500 Subject: [PATCH 0180/3768] wrappers/openssl: mark casts as gcsafe Nim will pretend that these proc are not gcsafe if they are not marked. --- lib/wrappers/openssl.nim | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim index 82fcb2194..a3825b801 100644 --- a/lib/wrappers/openssl.nim +++ b/lib/wrappers/openssl.nim @@ -403,29 +403,25 @@ else: proc getOpenSSLVersion*(): culong = ## Return OpenSSL version as unsigned long or 0 if not available - let theProc = cast[proc(): culong {.cdecl.}](utilSymNullable("OpenSSL_version_num", "SSLeay")) - {.gcsafe.}: - result = - if theProc.isNil: 0.culong - else: theProc() + let theProc = cast[proc(): culong {.cdecl, gcsafe.}](utilSymNullable("OpenSSL_version_num", "SSLeay")) + result = + if theProc.isNil: 0.culong + else: theProc() proc SSL_in_init*(ssl: SslPtr): cint = # A compatibility wrapper for `SSL_in_init()` for OpenSSL 1.0, 1.1 and LibreSSL const MainProc = "SSL_in_init" let - theProc {.global.} = cast[proc(ssl: SslPtr): cint {.cdecl.}](sslSymNullable(MainProc)) + theProc {.global.} = cast[proc(ssl: SslPtr): cint {.cdecl, gcsafe.}](sslSymNullable(MainProc)) # Fallback - sslState {.global.} = cast[proc(ssl: SslPtr): cint {.cdecl.}](sslSymNullable("SSL_state")) + sslState {.global.} = cast[proc(ssl: SslPtr): cint {.cdecl, gcsafe.}](sslSymNullable("SSL_state")) - # FIXME: This shouldn't be needed, but the compiler is complaining about - # `sslState` being GC-ed memory??? - {.gcsafe.}: - if not theProc.isNil: - theProc(ssl) - elif not sslState.isNil: - sslState(ssl) and SSL_ST_INIT - else: - raiseInvalidLibrary MainProc + if not theProc.isNil: + theProc(ssl) + elif not sslState.isNil: + sslState(ssl) and SSL_ST_INIT + else: + raiseInvalidLibrary MainProc proc ERR_load_BIO_strings*(){.cdecl, dynlib: DLLUtilName, importc.} From 6b2d8bfc8b9e46ae92e490111cb6a2abc49a32b6 Mon Sep 17 00:00:00 2001 From: Leorize Date: Thu, 4 Jun 2020 18:13:06 -0500 Subject: [PATCH 0181/3768] net: also set TLSv1.3 cipher suites --- lib/pure/net.nim | 7 +++++++ lib/wrappers/openssl.nim | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index f628ee056..c1896dc1c 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -580,6 +580,13 @@ when defineSsl: if newCTX.SSL_CTX_set_cipher_list(cipherList) != 1: raiseSSLError() + when not defined(openssl10) and not defined(libressl): + let sslVersion = getOpenSSLVersion() + if sslVersion >= 0x010101000 and not sslVersion == 0x020000000: + # In OpenSSL >= 1.1.1, TLSv1.3 cipher suites can only be configured via + # this API. + if newCTX.SSL_CTX_set_ciphersuites(cipherList) != 1: + raiseSSLError() # Automatically the best ECDH curve for client exchange. Without this, ECDH # ciphers will be ignored by the server. # diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim index a3825b801..c81176a9b 100644 --- a/lib/wrappers/openssl.nim +++ b/lib/wrappers/openssl.nim @@ -296,6 +296,7 @@ when compileOption("dynlibOverride", "ssl") or defined(noOpenSSLHacks): SSl_state(ssl) and SSL_ST_INIT else: proc SSL_in_init*(ssl: SslPtr): cint {.cdecl, dynlib: DLLSSLName, importc.} + proc SSL_CTX_set_ciphersuites*(ctx: SslCtx, str: cstring): cint {.cdecl, dynlib: DLLSSLName, importc.} template OpenSSL_add_all_algorithms*() = discard @@ -423,6 +424,10 @@ else: else: raiseInvalidLibrary MainProc + proc SSL_CTX_set_ciphersuites*(ctx: SslCtx, str: cstring): cint = + let theProc {.global.} = cast[proc(ctx: SslCtx, str: cstring) {.cdecl, gcsafe.}](sslSymThrows("SSL_CTX_set_ciphersuites")) + theProc(ctx, str) + proc ERR_load_BIO_strings*(){.cdecl, dynlib: DLLUtilName, importc.} proc SSL_new*(context: SslCtx): SslPtr{.cdecl, dynlib: DLLSSLName, importc.} From 4fdf9cb808db9251feb1c23652da6141ce111437 Mon Sep 17 00:00:00 2001 From: Leorize Date: Thu, 4 Jun 2020 18:30:20 -0500 Subject: [PATCH 0182/3768] wrappers/openssl: defer loading SSL_CTX_set_ciphersuites --- lib/wrappers/openssl.nim | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim index c81176a9b..b7f18ad79 100644 --- a/lib/wrappers/openssl.nim +++ b/lib/wrappers/openssl.nim @@ -425,7 +425,9 @@ else: raiseInvalidLibrary MainProc proc SSL_CTX_set_ciphersuites*(ctx: SslCtx, str: cstring): cint = - let theProc {.global.} = cast[proc(ctx: SslCtx, str: cstring) {.cdecl, gcsafe.}](sslSymThrows("SSL_CTX_set_ciphersuites")) + var theProc {.global.}: proc(ctx: SslCtx, str: cstring) {.cdecl, gcsafe.} + if theProc.isNil: + theProc = cast[typeof(theProc)](sslSymThrows("SSL_CTX_set_ciphersuites")) theProc(ctx, str) proc ERR_load_BIO_strings*(){.cdecl, dynlib: DLLUtilName, importc.} From 32083c7ff88c46b81c30df95ff5c81c57935bc23 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 6 Jun 2020 21:28:46 +0200 Subject: [PATCH 0183/3768] more precise analysis about 'observable stores' [backport:1.2] (#14582) --- compiler/ast.nim | 1 + compiler/ccgcalls.nim | 12 +++++++++--- compiler/sempass2.nim | 6 +++++- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index d13487e9c..76b1d8c91 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -293,6 +293,7 @@ type sfInjectDestructors # whether the proc needs the 'injectdestructors' transformation sfNeverRaises # proc can never raise an exception, not even OverflowDefect # or out-of-memory + sfUsedInFinallyOrExcept # symbol is used inside an 'except' or 'finally' TSymFlags* = set[TSymFlag] diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 10f5ef638..3dd2b54d4 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -22,14 +22,20 @@ proc canRaiseDisp(p: BProc; n: PNode): bool = result = canRaiseConservative(n) proc preventNrvo(p: BProc; le, ri: PNode): bool = - proc locationEscapes(p: BProc; le: PNode): bool = + proc locationEscapes(p: BProc; le: PNode; inTryStmt: bool): bool = var n = le while true: # do NOT follow nkHiddenDeref here! case n.kind of nkSym: # we don't own the location so it escapes: - return n.sym.owner != p.prc + if n.sym.owner != p.prc: + return true + elif inTryStmt and sfUsedInFinallyOrExcept in n.sym.flags: + # it is also an observable store if the location is used + # in 'except' or 'finally' + return true + return false of nkDotExpr, nkBracketExpr, nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr: n = n[0] @@ -46,7 +52,7 @@ proc preventNrvo(p: BProc; le, ri: PNode): bool = # we use the weaker 'canRaise' here in order to prevent too many # annoying warnings, see #14514 if canRaise(ri[0]) and - (p.nestedTryStmts.len > 0 or locationEscapes(p, le)): + locationEscapes(p, le, p.nestedTryStmts.len > 0): message(p.config, le.info, warnObservableStores, $le) proc hasNoInit(call: PNode): bool {.inline.} = diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 862123eab..6808b1b9b 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -66,7 +66,7 @@ type TEffects = object exc: PNode # stack of exceptions tags: PNode # list of tags - bottom, inTryStmt: int + bottom, inTryStmt, inExceptOrFinallyStmt: int owner: PSym ownerModule: PSym init: seq[int] # list of initialized variables @@ -248,6 +248,8 @@ proc listGcUnsafety(s: PSym; onlyWarning: bool; conf: ConfigRef) = proc useVar(a: PEffects, n: PNode) = let s = n.sym + if a.inExceptOrFinallyStmt > 0: + incl s.flags, sfUsedInFinallyOrExcept if isLocalVar(a, s): if sfNoInit in s.flags: # If the variable is explicitly marked as .noinit. do not emit any error @@ -382,6 +384,7 @@ proc trackTryStmt(tracked: PEffects, n: PNode) = var branches = 1 var hasFinally = false + inc tracked.inExceptOrFinallyStmt # Collect the exceptions caught by the except branches for i in 1.. Date: Sat, 6 Jun 2020 21:53:18 +0200 Subject: [PATCH 0184/3768] Fix link to "rebuilding the compiler" (#14567) --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index eb6b68d0d..f529c0df0 100644 --- a/readme.md +++ b/readme.md @@ -79,7 +79,7 @@ Next run the appropriate build shell script for your platform: Finally, once you have finished the build steps (on Windows, Mac or Linux) you should add the ``bin`` directory to your PATH. -See also [rebuilding the compiler](intern.html#rebuilding-the-compiler). +See also [rebuilding the compiler](doc/intern.rst#rebuilding-the-compiler). ## Koch From 66c50c2ffcfdd31f9f1eecfaf23b1b58b22d3a36 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 7 Jun 2020 09:55:56 +0200 Subject: [PATCH 0185/3768] implement the 'bind' statement for generics, it was an oversight that this was never implemented (#14584) --- compiler/semgnrc.nim | 15 +++++++++++---- tests/lookups/tbind_for_generics.nim | 17 +++++++++++++++++ 2 files changed, 28 insertions(+), 4 deletions(-) create mode 100644 tests/lookups/tbind_for_generics.nim diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index 6fefc2233..23b6e9b9d 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -28,7 +28,7 @@ proc getIdentNode(c: PContext; n: PNode): PNode = type GenericCtx = object - toMixin: IntSet + toMixin, toBind: IntSet cursorInBody: bool # only for nimsuggest bracketExpr: PNode @@ -124,7 +124,7 @@ proc lookup(c: PContext, n: PNode, flags: TSemGenericFlags, if ident.id notin ctx.toMixin and withinMixin notin flags: errorUndeclaredIdentifier(c, n.info, ident.s) else: - if withinBind in flags: + if withinBind in flags or s.id in ctx.toBind: result = symChoice(c, n, s, scClosed) elif s.isMixedIn: result = symChoice(c, n, s, scForceOpen) @@ -155,7 +155,7 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags, var s = searchInScopes(c, ident, routineKinds).skipAlias(n, c.config) if s != nil: isMacro = s.kind in {skTemplate, skMacro} - if withinBind in flags: + if withinBind in flags or s.id in ctx.toBind: result = newDot(result, symChoice(c, n, s, scClosed)) elif s.isMixedIn: result = newDot(result, symChoice(c, n, s, scForceOpen)) @@ -211,6 +211,8 @@ proc semGenericStmt(c: PContext, n: PNode, result = semGenericStmt(c, n[0], flags+{withinBind}, ctx) of nkMixinStmt: result = semMixinStmt(c, n, ctx.toMixin) + of nkBindStmt: + result = semBindStmt(c, n, ctx.toBind) of nkCall, nkHiddenCallConv, nkInfix, nkPrefix, nkCommand, nkCallStrLit: # check if it is an expression macro: checkMinSonsLen(n, 1, c.config) @@ -227,7 +229,10 @@ proc semGenericStmt(c: PContext, n: PNode, if s != nil: incl(s.flags, sfUsed) mixinContext = s.magic in {mDefined, mDefinedInScope, mCompiles, mAstToStr} - let sc = symChoice(c, fn, s, if s.isMixedIn: scForceOpen else: scOpen) + let whichChoice = if s.id in ctx.toBind: scClosed + elif s.isMixedIn: scForceOpen + else: scOpen + let sc = symChoice(c, fn, s, whichChoice) case s.kind of skMacro: if macroToExpand(s) and sc.safeLen <= 1: @@ -492,12 +497,14 @@ proc semGenericStmt(c: PContext, n: PNode, proc semGenericStmt(c: PContext, n: PNode): PNode = var ctx: GenericCtx ctx.toMixin = initIntSet() + ctx.toBind = initIntSet() result = semGenericStmt(c, n, {}, ctx) semIdeForTemplateOrGeneric(c, result, ctx.cursorInBody) proc semConceptBody(c: PContext, n: PNode): PNode = var ctx: GenericCtx ctx.toMixin = initIntSet() + ctx.toBind = initIntSet() result = semGenericStmt(c, n, {withinConcept}, ctx) semIdeForTemplateOrGeneric(c, result, ctx.cursorInBody) diff --git a/tests/lookups/tbind_for_generics.nim b/tests/lookups/tbind_for_generics.nim new file mode 100644 index 000000000..db5fbebbc --- /dev/null +++ b/tests/lookups/tbind_for_generics.nim @@ -0,0 +1,17 @@ +discard """ + errormsg: "type mismatch: got " + line: 8 +""" +proc g[T](x: T) = + bind `+` + # because we bind `+` here, we must not find the `+` for 'Foo' below: + echo x + x + +type + Foo = object + a: int + +proc `+`(a, b: Foo): Foo = Foo(a: a.a+b.a) + +g(3) +g(Foo(a: 8)) From 419dbd573d91c493f80c75fa23fae1dd6fece18e Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 7 Jun 2020 20:11:42 +0200 Subject: [PATCH 0186/3768] fixes #14118 (#14595) --- compiler/injectdestructors.nim | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 9cb777f2c..c13c93b00 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -277,8 +277,11 @@ proc canBeMoved(c: Con; t: PType): bool {.inline.} = else: result = t.attachedOps[attachedSink] != nil +proc isNoInit(dest: PNode): bool {.inline.} = + result = dest.kind == nkSym and sfNoInit in dest.sym.flags + proc genSink(c: var Con; dest, ri: PNode): PNode = - if isUnpackedTuple(dest) or isFirstWrite(dest, c): + if isUnpackedTuple(dest) or isFirstWrite(dest, c) or isNoInit(dest): # optimize sink call into a bitwise memcopy result = newTree(nkFastAsgn, dest, ri) else: From 754a196d4739b17fc0783d8e4b025d38bbcc4e32 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 7 Jun 2020 23:29:26 +0200 Subject: [PATCH 0187/3768] fixes #14315 (#14594) --- compiler/semtypinst.nim | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 257094289..15a63e75e 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -390,7 +390,6 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = let bbody = lastSon body var newbody = replaceTypeVarsT(cl, bbody) - let bodyIsNew = newbody != bbody cl.skipTypedesc = oldSkipTypedesc newbody.flags = newbody.flags + (t.flags + body.flags - tfInstClearedFlags) result.flags = result.flags + newbody.flags - tfInstClearedFlags @@ -412,7 +411,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = # generics *when the type is constructed*: newbody.attachedOps[attachedDeepCopy] = cl.c.instTypeBoundOp(cl.c, dc, result, cl.info, attachedDeepCopy, 1) - if bodyIsNew and newbody.typeInst == nil: + if newbody.typeInst == nil: #doassert newbody.typeInst == nil newbody.typeInst = result if tfRefsAnonObj in newbody.flags and newbody.kind != tyGenericInst: From 52841dba0936d3679d2c7115fed620c35627155d Mon Sep 17 00:00:00 2001 From: Clyybber Date: Sun, 7 Jun 2020 23:32:41 +0200 Subject: [PATCH 0188/3768] Fix #14568 (#14583) * Fix #14568 * Add testcase * Fix bogus test * Adapt other failing tests * Declarations are always first writes --- compiler/injectdestructors.nim | 46 ++++++++++++++-------------- tests/arc/tmovebug.nim | 27 ++++++++++++++-- tests/destructor/tmove_objconstr.nim | 1 + 3 files changed, 49 insertions(+), 25 deletions(-) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index c13c93b00..d5eca7475 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -63,7 +63,7 @@ template dbg(body) = body proc p(n: PNode; c: var Con; mode: ProcessMode): PNode -proc moveOrCopy(dest, ri: PNode; c: var Con): PNode +proc moveOrCopy(dest, ri: PNode; c: var Con, isDecl = false): PNode proc isLastRead(location: PNode; c: var Con; pc, until: int): int = var pc = pc @@ -280,8 +280,8 @@ proc canBeMoved(c: Con; t: PType): bool {.inline.} = proc isNoInit(dest: PNode): bool {.inline.} = result = dest.kind == nkSym and sfNoInit in dest.sym.flags -proc genSink(c: var Con; dest, ri: PNode): PNode = - if isUnpackedTuple(dest) or isFirstWrite(dest, c) or isNoInit(dest): +proc genSink(c: var Con; dest, ri: PNode, isDecl = false): PNode = + if isUnpackedTuple(dest) or (isDecl and c.inLoop <= 0) or (isAnalysableFieldAccess(dest, c.owner) and isFirstWrite(dest, c)) or isNoInit(dest): # optimize sink call into a bitwise memcopy result = newTree(nkFastAsgn, dest, ri) else: @@ -578,7 +578,7 @@ proc ensureDestruction(arg: PNode; c: var Con): PNode = let tmp = getTemp(c, arg.typ, arg.info) when not scopeBasedDestruction: c.addTopVar(tmp) - result.add genSink(c, tmp, arg) + result.add genSink(c, tmp, arg, isDecl = true) result.add tmp c.destroys.add genDestroy(c, tmp) else: @@ -652,9 +652,9 @@ proc pVarTopLevel(v: PNode; c: var Con; ri, res: PNode) = else: c.destroys.add genDestroy(c, v) if ri.kind == nkEmpty and c.inLoop > 0: - res.add moveOrCopy(v, genDefaultCall(v.typ, c, v.info), c) + res.add moveOrCopy(v, genDefaultCall(v.typ, c, v.info), c, isDecl = true) elif ri.kind != nkEmpty: - res.add moveOrCopy(v, ri, c) + res.add moveOrCopy(v, ri, c, isDecl = true) proc pVarScoped(v: PNode; c: var Con; ri, res: PNode) = if not containsOrIncl(c.declaredVars, v.sym.id): @@ -675,9 +675,9 @@ proc pVarScoped(v: PNode; c: var Con; ri, res: PNode) = #if c.inDangerousBranch == 0: v.sym.flags.incl sfNoInit c.scopeDestroys.add genDestroy(c, v) if ri.kind == nkEmpty and c.inLoop > 0: - res.add moveOrCopy(v, genDefaultCall(v.typ, c, v.info), c) + res.add moveOrCopy(v, genDefaultCall(v.typ, c, v.info), c, isDecl = true) elif ri.kind != nkEmpty: - res.add moveOrCopy(v, ri, c) + res.add moveOrCopy(v, ri, c, isDecl = true) template handleNestedTempl(n: untyped, processCall: untyped) = case n.kind @@ -901,7 +901,7 @@ proc p(n: PNode; c: var Con; mode: ProcessMode): PNode = if ri.kind == nkEmpty and c.inLoop > 0: ri = genDefaultCall(v.typ, c, v.info) if ri.kind != nkEmpty: - result.add moveOrCopy(v, ri, c) + result.add moveOrCopy(v, ri, c, isDecl = true) else: # keep the var but transform 'ri': var v = copyNode(n) var itCopy = copyNode(it) @@ -975,18 +975,18 @@ proc p(n: PNode; c: var Con; mode: ProcessMode): PNode = for i in 0.. 0: echo "assign ",x.who + if y.who.len > 0: echo "assign ",y.who proc `=sink`(x: var ME, y: ME) = - if x.who.len > 0: echo "sink ",x.who + if y.who.len > 0: echo "sink ",y.who var dump: ME template use(x) = dump = x @@ -302,3 +306,22 @@ proc shouldNotSink() = use(x) # Not ok without the '[else]' shouldNotSink() + +# bug #14568 +import os + +type O2 = object + s: seq[int] + +proc `=sink`(dest: var O2, src: O2) = + echo "sinked and not optimized to a bitcopy" + +var testSeq: O2 + +proc Update(): void = + # testSeq.add(0) # uncommenting this line fixes the leak + testSeq = O2(s: @[]) + testSeq.s.add(0) + +for i in 1..3: + Update() diff --git a/tests/destructor/tmove_objconstr.nim b/tests/destructor/tmove_objconstr.nim index 5b2198e51..5366f4957 100644 --- a/tests/destructor/tmove_objconstr.nim +++ b/tests/destructor/tmove_objconstr.nim @@ -2,6 +2,7 @@ discard """ output: '''test created test destroyed 0 +Pony is dying! 1 2 3 From 8edac9cf1d8a052b306e43ba92b3a91f51a221be Mon Sep 17 00:00:00 2001 From: alaviss Date: Mon, 8 Jun 2020 01:15:10 -0500 Subject: [PATCH 0189/3768] openssl: use explicit result for SSL_in_init (#14597) Sometimes the analysis might get funky and decide that the if-expression below is not an if-expression. Details of how this can happen is largely unknown, but it's easy to prevent so we will just go forward with that. Fix #14591 --- lib/wrappers/openssl.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim index b7f18ad79..bea96127d 100644 --- a/lib/wrappers/openssl.nim +++ b/lib/wrappers/openssl.nim @@ -418,9 +418,9 @@ else: sslState {.global.} = cast[proc(ssl: SslPtr): cint {.cdecl, gcsafe.}](sslSymNullable("SSL_state")) if not theProc.isNil: - theProc(ssl) + result = theProc(ssl) elif not sslState.isNil: - sslState(ssl) and SSL_ST_INIT + result = sslState(ssl) and SSL_ST_INIT else: raiseInvalidLibrary MainProc From 0d814f2d4464b6426df8f65d845299b969a86d71 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 7 Jun 2020 23:15:34 -0700 Subject: [PATCH 0190/3768] hotfix disable nitter refs https://github.com/timotheecour/Nim/issues/167 (#14603) --- testament/important_packages.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index cb436bfb2..19e11e322 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -90,7 +90,8 @@ pkg2 "nimsvg" # pkg2 "nimterop", true pkg2 "nimwc", true, "nim c nimwc.nim" # pkg2 "nimx", true, "nim c --threads:on test/main.nim" -pkg2 "nitter", true, "nim c src/nitter.nim", "https://github.com/zedeus/nitter" +# pkg2 "nitter", true, "nim c src/nitter.nim", "https://github.com/zedeus/nitter" + # pending https://github.com/timotheecour/Nim/issues/167 or new git tag for nitter pkg2 "norm", true, "nim c -r tests/tsqlite.nim" pkg2 "npeg", false, "nimble testarc" pkg2 "numericalnim", true, "nim c -r tests/test_integrate.nim" From 733bd76f6bc8253f255df4cec26099502090264b Mon Sep 17 00:00:00 2001 From: Tomohiro Date: Mon, 8 Jun 2020 17:28:42 +0900 Subject: [PATCH 0191/3768] Fix sugar.dump: It doesn't work correctly with compile time expression (#14580) --- lib/pure/sugar.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/sugar.nim b/lib/pure/sugar.nim index dbc60b8a9..d2bdb0fdc 100644 --- a/lib/pure/sugar.nim +++ b/lib/pure/sugar.nim @@ -150,7 +150,7 @@ macro `->`*(p, b: untyped): untyped = result = createProcType(p, b) -macro dump*(x: typed): untyped = +macro dump*(x: untyped): untyped = ## Dumps the content of an expression, useful for debugging. ## It accepts any expression and prints a textual representation ## of the tree representing the expression - as it would appear in From c7a1a7b8bf38ab5c15decdf913dfc272ad922c21 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 8 Jun 2020 01:35:23 -0700 Subject: [PATCH 0192/3768] `toJson`, `jsonTo`, json (de)serialization for custom types; remove dependency on strtabs thanks to a hooking mechanism (#14563) * json custom serialization; application for strtabs * serialize using nesting * make toJson more feature complete * add since * Revert "Improve JSON serialisation of strtabs (#14549)" This reverts commit 7cb4ef26addb3bb5ce2405d8396df6fd41664dae. * better approach via mixin * toJson, jsonTo * fix test * address comments * move to jsonutils * doc * cleanups * also test for js * also test for vm --- changelog.md | 2 + lib/pure/json.nim | 42 ++++-------- lib/pure/strtabs.nim | 20 +++++- lib/std/jsonutils.nim | 126 ++++++++++++++++++++++++++++++++++++ tests/stdlib/tjsonutils.nim | 44 +++++++++++++ 5 files changed, 203 insertions(+), 31 deletions(-) create mode 100644 lib/std/jsonutils.nim create mode 100644 tests/stdlib/tjsonutils.nim diff --git a/changelog.md b/changelog.md index bbcf2ed18..8e06d30f1 100644 --- a/changelog.md +++ b/changelog.md @@ -108,6 +108,8 @@ users from the use of weak and insecure ciphers while still provides adequate compatiblity with the majority of the Internet. +- new module `std/jsonutils` with hookable `jsonTo,toJson,fromJson` for json serialization/deserialization of custom types. + ## Language changes - In the 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: diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 58a8c1dce..041816c7d 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -140,6 +140,9 @@ ## var j2 = %* {"name": "Isaac", "books": ["Robot Dreams"]} ## j2["details"] = %* {"age":35, "pi":3.1415} ## echo j2 +## +## See also: std/jsonutils for hookable json serialization/deserialization +## of arbitrary types. runnableExamples: ## Note: for JObject, key ordering is preserved, unlike in some languages, @@ -149,8 +152,10 @@ runnableExamples: doAssert $(%* Foo()) == """{"a1":0,"a2":0,"a0":0,"a3":0,"a4":0}""" import - hashes, tables, strtabs, strutils, lexbase, streams, macros, parsejson, - options + hashes, tables, strutils, lexbase, streams, macros, parsejson + +import options # xxx remove this dependency using same approach as https://github.com/nim-lang/Nim/pull/14563 +import std/private/since export tables.`$` @@ -353,14 +358,6 @@ proc `[]=`*(obj: JsonNode, key: string, val: JsonNode) {.inline.} = assert(obj.kind == JObject) obj.fields[key] = val -proc `%`*(table: StringTableRef): JsonNode = - ## Generic constructor for JSON data. Creates a new ``JObject JsonNode``. - result = newJObject() - result["mode"] = %($table.mode) - var data = newJObject() - for k, v in table: data[k] = %v - result["data"] = data - proc `%`*[T: object](o: T): JsonNode = ## Construct JsonNode from tuples and objects. result = newJObject() @@ -378,20 +375,20 @@ proc `%`*(o: enum): JsonNode = ## string. Creates a new ``JString JsonNode``. result = %($o) -proc toJson(x: NimNode): NimNode {.compileTime.} = +proc toJsonImpl(x: NimNode): NimNode {.compileTime.} = case x.kind of nnkBracket: # array if x.len == 0: return newCall(bindSym"newJArray") result = newNimNode(nnkBracket) for i in 0 ..< x.len: - result.add(toJson(x[i])) + result.add(toJsonImpl(x[i])) result = newCall(bindSym("%", brOpen), result) of nnkTableConstr: # object if x.len == 0: return newCall(bindSym"newJObject") result = newNimNode(nnkTableConstr) for i in 0 ..< x.len: x[i].expectKind nnkExprColonExpr - result.add newTree(nnkExprColonExpr, x[i][0], toJson(x[i][1])) + result.add newTree(nnkExprColonExpr, x[i][0], toJsonImpl(x[i][1])) result = newCall(bindSym("%", brOpen), result) of nnkCurly: # empty object x.expectLen(0) @@ -399,7 +396,7 @@ proc toJson(x: NimNode): NimNode {.compileTime.} = of nnkNilLit: result = newCall(bindSym"newJNull") of nnkPar: - if x.len == 1: result = toJson(x[0]) + if x.len == 1: result = toJsonImpl(x[0]) else: result = newCall(bindSym("%", brOpen), x) else: result = newCall(bindSym("%", brOpen), x) @@ -407,7 +404,7 @@ proc toJson(x: NimNode): NimNode {.compileTime.} = macro `%*`*(x: untyped): untyped = ## Convert an expression to a JsonNode directly, without having to specify ## `%` for every element. - result = toJson(x) + result = toJsonImpl(x) proc `==`*(a, b: JsonNode): bool = ## Check two nodes for equality @@ -992,7 +989,6 @@ when defined(nimFixedForwardGeneric): proc initFromJson[S,T](dst: var array[S,T]; jsonNode: JsonNode; jsonPath: var string) proc initFromJson[T](dst: var Table[string,T]; jsonNode: JsonNode; jsonPath: var string) proc initFromJson[T](dst: var OrderedTable[string,T]; jsonNode: JsonNode; jsonPath: var string) - proc initFromJson(dst: var StringTableRef; jsonNode: JsonNode; jsonPath: var string) proc initFromJson[T](dst: var ref T; jsonNode: JsonNode; jsonPath: var string) proc initFromJson[T](dst: var Option[T]; jsonNode: JsonNode; jsonPath: var string) proc initFromJson[T: distinct](dst: var T; jsonNode: JsonNode; jsonPath: var string) @@ -1073,20 +1069,6 @@ when defined(nimFixedForwardGeneric): initFromJson(mgetOrPut(dst, key, default(T)), jsonNode[key], jsonPath) jsonPath.setLen originalJsonPathLen - proc mgetOrPut(tab: var StringTableRef, key: string): var string = - if not tab.hasKey(key): tab[key] = "" - result = tab[key] - - proc initFromJson(dst: var StringTableRef; jsonNode: JsonNode; jsonPath: var string) = - dst = newStringTable(parseEnum[StringTableMode](jsonNode["mode"].getStr)) - verifyJsonKind(jsonNode, {JObject}, jsonPath) - let originalJsonPathLen = jsonPath.len - for key in keys(jsonNode["data"].fields): - jsonPath.add '.' - jsonPath.add key - initFromJson(mgetOrPut(dst, key), jsonNode[key], jsonPath) - jsonPath.setLen originalJsonPathLen - proc initFromJson[T](dst: var ref T; jsonNode: JsonNode; jsonPath: var string) = verifyJsonKind(jsonNode, {JObject, JNull}, jsonPath) if jsonNode.kind == JNull: diff --git a/lib/pure/strtabs.nim b/lib/pure/strtabs.nim index cfafdf018..81ff7fbde 100644 --- a/lib/pure/strtabs.nim +++ b/lib/pure/strtabs.nim @@ -71,7 +71,7 @@ type StringTableObj* = object of RootObj counter: int data: KeyValuePairSeq - mode*: StringTableMode + mode: StringTableMode StringTableRef* = ref StringTableObj @@ -419,6 +419,24 @@ proc `%`*(f: string, t: StringTableRef, flags: set[FormatFlag] = {}): string {. add(result, f[i]) inc(i) +since (1,3,5): + proc fromJsonHook*[T](a: var StringTableRef, b: T) = + ## for json.fromJson + mixin jsonTo + var mode = jsonTo(b["mode"], StringTableMode) + a = newStringTable(mode) + let b2 = b["table"] + for k,v in b2: a[k] = jsonTo(v, string) + + proc toJsonHook*[](a: StringTableRef): auto = + ## for json.toJson + mixin newJObject + mixin toJson + result = newJObject() + result["mode"] = toJson($a.mode) + let t = newJObject() + for k,v in a: t[k] = toJson(v) + result["table"] = t when isMainModule: var x = {"k": "v", "11": "22", "565": "67"}.newStringTable diff --git a/lib/std/jsonutils.nim b/lib/std/jsonutils.nim new file mode 100644 index 000000000..bfa600fa9 --- /dev/null +++ b/lib/std/jsonutils.nim @@ -0,0 +1,126 @@ +##[ +This module implements a hookable (de)serialization for arbitrary types. +Design goal: avoid importing modules where a custom serialization is needed; +see strtabs.fromJsonHook,toJsonHook for an example. + +]## + +import std/[json,tables,strutils] + +#[ +xxx +use toJsonHook,fromJsonHook for Table|OrderedTable +add Options support also using toJsonHook,fromJsonHook and remove `json=>options` dependency + +future direction: +add a way to customize serialization, for eg allowing missing +or extra fields in JsonNode, field renaming, and a way to handle cyclic references +using a cache of already visited addresses. +]# + +proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".} +proc distinctBase(T: typedesc): typedesc {.magic: "TypeTrait".} +template distinctBase[T](a: T): untyped = distinctBase(type(a))(a) + +proc checkJsonImpl(cond: bool, condStr: string, msg = "") = + if not cond: + # just pick 1 exception type for simplicity; other choices would be: + # JsonError, JsonParser, JsonKindError + raise newException(ValueError, msg) + +template checkJson(cond: untyped, msg = "") = + checkJsonImpl(cond, astToStr(cond), msg) + +proc fromJson*[T](a: var T, b: JsonNode) = + ## inplace version of `jsonTo` + #[ + adding "json path" leading to `b` can be added in future work. + ]# + checkJson b != nil, $($T, b) + when compiles(fromJsonHook(a, b)): fromJsonHook(a, b) + elif T is bool: a = to(b,T) + elif T is Table | OrderedTable: + a.clear + for k,v in b: + a[k] = jsonTo(v, typeof(a[k])) + elif T is enum: + case b.kind + of JInt: a = T(b.getBiggestInt()) + of JString: a = parseEnum[T](b.getStr()) + else: checkJson false, $($T, " ", b) + elif T is Ordinal: a = T(to(b, int)) + elif T is pointer: a = cast[pointer](to(b, int)) + elif T is distinct: + when nimvm: + # bug, potentially related to https://github.com/nim-lang/Nim/issues/12282 + a = T(jsonTo(b, distinctBase(T))) + else: + a.distinctBase.fromJson(b) + elif T is string|SomeNumber: a = to(b,T) + elif T is JsonNode: a = b + elif T is ref | ptr: + if b.kind == JNull: a = nil + else: + a = T() + fromJson(a[], b) + elif T is array: + checkJson a.len == b.len, $(a.len, b.len, $T) + for i, val in b.getElems: + fromJson(a[i], val) + elif T is seq: + a.setLen b.len + for i, val in b.getElems: + fromJson(a[i], val) + elif T is object | tuple: + const isNamed = T is object or isNamedTuple(T) + when isNamed: + checkJson b.kind == JObject, $(b.kind) # we could customize whether to allow JNull + var num = 0 + for key, val in fieldPairs(a): + num.inc + if b.hasKey key: + fromJson(val, b[key]) + else: + # we could customize to allow this + checkJson false, $($T, key, b) + checkJson b.len == num, $(b.len, num, $T, b) # could customize + else: + checkJson b.kind == JArray, $(b.kind) # we could customize whether to allow JNull + var i = 0 + for val in fields(a): + fromJson(val, b[i]) + i.inc + else: + # checkJson not appropriate here + static: doAssert false, "not yet implemented: " & $T + +proc jsonTo*(b: JsonNode, T: typedesc): T = + ## reverse of `toJson` + fromJson(result, b) + +proc toJson*[T](a: T): JsonNode = + ## serializes `a` to json; uses `toJsonHook(a: T)` if it's in scope to + ## customize serialization, see strtabs.toJsonHook for an example. + when compiles(toJsonHook(a)): result = toJsonHook(a) + elif T is Table | OrderedTable: + result = newJObject() + for k, v in pairs(a): result[k] = toJson(v) + elif T is object | tuple: + const isNamed = T is object or isNamedTuple(T) + when isNamed: + result = newJObject() + for k, v in a.fieldPairs: result[k] = toJson(v) + else: + result = newJArray() + for v in a.fields: result.add toJson(v) + elif T is ref | ptr: + if a == nil: result = newJNull() + else: result = toJson(a[]) + elif T is array | seq: + result = newJArray() + for ai in a: result.add toJson(ai) + elif T is pointer: result = toJson(cast[int](a)) + elif T is distinct: result = toJson(a.distinctBase) + elif T is bool: result = %(a) + elif T is Ordinal: result = %(a.ord) + else: result = %a diff --git a/tests/stdlib/tjsonutils.nim b/tests/stdlib/tjsonutils.nim new file mode 100644 index 000000000..01c6aa05a --- /dev/null +++ b/tests/stdlib/tjsonutils.nim @@ -0,0 +1,44 @@ +discard """ + targets: "c cpp js" +""" + +import std/jsonutils +import std/json + +proc testRoundtrip[T](t: T, expected: string) = + let j = t.toJson + doAssert $j == expected, $j + doAssert j.jsonTo(T).toJson == j + +import tables +import strtabs + +template fn() = + block: # toJson, jsonTo + type Foo = distinct float + testRoundtrip('x', """120""") + when not defined(js): + testRoundtrip(cast[pointer](12345)): """12345""" + + # causes workaround in `fromJson` potentially related to + # https://github.com/nim-lang/Nim/issues/12282 + testRoundtrip(Foo(1.5)): """1.5""" + + block: + testRoundtrip({"z": "Z", "y": "Y"}.toOrderedTable): """{"z":"Z","y":"Y"}""" + when not defined(js): # pending https://github.com/nim-lang/Nim/issues/14574 + testRoundtrip({"z": (f1: 'f'), }.toTable): """{"z":{"f1":102}}""" + + block: + testRoundtrip({"name": "John", "city": "Monaco"}.newStringTable): """{"mode":"modeCaseSensitive","table":{"city":"Monaco","name":"John"}}""" + + block: # complex example + let t = {"z": "Z", "y": "Y"}.newStringTable + type A = ref object + a1: string + let a = (1.1, "fo", 'x', @[10,11], [true, false], [t,newStringTable()], [0'i8,3'i8], -4'i16, (foo: 0.5'f32, bar: A(a1: "abc"), bar2: A.default)) + testRoundtrip(a): + """[1.1,"fo",120,[10,11],[true,false],[{"mode":"modeCaseSensitive","table":{"y":"Y","z":"Z"}},{"mode":"modeCaseSensitive","table":{}}],[0,3],-4,{"foo":0.5,"bar":{"a1":"abc"},"bar2":null}]""" + +static: fn() +fn() From e3f80e434aa8eb1d81a3d7a83d0beb02c0158846 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 8 Jun 2020 14:32:45 +0200 Subject: [PATCH 0193/3768] fixes #14557 (#14607) --- compiler/parampatterns.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim index 33881a7b3..e97631b77 100644 --- a/compiler/parampatterns.nim +++ b/compiler/parampatterns.nim @@ -200,7 +200,7 @@ proc exprRoot*(n: PNode): PSym = if it.len > 0 and it.typ != nil: it = it.lastSon else: break of nkCallKinds: - if it.typ != nil and it.typ.kind == tyVar and it.len > 1: + if it.typ != nil and it.typ.kind in {tyVar, tyLent} and it.len > 1: # See RFC #7373, calls returning 'var T' are assumed to # return a view into the first argument (if there is one): it = it[1] From c308c2e60b472c6ceade2cd319e2fe32fcd1d9a7 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 8 Jun 2020 14:34:26 +0200 Subject: [PATCH 0194/3768] optimized wrapWords; fixes #14579 (#14606) [backport:1.2] --- lib/std/wordwrap.nim | 73 +++++++++++++++++++++++++++++--------------- 1 file changed, 48 insertions(+), 25 deletions(-) diff --git a/lib/std/wordwrap.nim b/lib/std/wordwrap.nim index 4b0dc4417..bb6f2dc6f 100644 --- a/lib/std/wordwrap.nim +++ b/lib/std/wordwrap.nim @@ -11,10 +11,10 @@ import strutils, unicode -proc olen(s: string): int = - var i = 0 +proc olen(s: string; start, lastExclusive: int): int = + var i = start result = 0 - while i < s.len: + while i < lastExclusive: inc result let L = graphemeLen(s, i) inc i, L @@ -32,31 +32,46 @@ proc wrapWords*(s: string, maxLineWidth = 80, result = newStringOfCap(s.len + s.len shr 6) var spaceLeft = maxLineWidth var lastSep = "" - for word, isSep in tokenize(s, seps): - let wlen = olen(word) + + var i = 0 + while true: + var j = i + let isSep = j < s.len and s[j] in seps + while j < s.len and (s[j] in seps) == isSep: inc(j) + if j <= i: break + #yield (substr(s, i, j-1), isSep) if isSep: - lastSep = word - spaceLeft = spaceLeft - wlen - elif wlen > spaceLeft: - if splitLongWords and wlen > maxLineWidth: - var i = 0 - while i < word.len: - if spaceLeft <= 0: - spaceLeft = maxLineWidth - result.add newLine - dec spaceLeft - let L = graphemeLen(word, i) - for j in 0 ..< L: result.add word[i+j] - inc i, L + lastSep.setLen 0 + for k in i.. spaceLeft: + if splitLongWords and wlen > maxLineWidth: + var k = 0 + while k < j - i: + if spaceLeft <= 0: + spaceLeft = maxLineWidth + result.add newLine + dec spaceLeft + let L = graphemeLen(s, k+i) + for m in 0 ..< L: result.add s[i+k+m] + inc k, L + else: + spaceLeft = maxLineWidth - wlen + result.add(newLine) + for k in i.. Date: Mon, 8 Jun 2020 20:06:04 +0200 Subject: [PATCH 0195/3768] [OpenBSD] Add arm support (#14608) * add arm support to openbsd * use clang on openbsd --- compiler/installer.ini | 2 +- tools/niminst/buildsh.nimf | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/installer.ini b/compiler/installer.ini index ed815d2eb..9718dbb94 100644 --- a/compiler/installer.ini +++ b/compiler/installer.ini @@ -11,7 +11,7 @@ Platforms: """ solaris: i386;amd64;sparc;sparc64 freebsd: i386;amd64;powerpc64;arm;arm64 netbsd: i386;amd64 - openbsd: i386;amd64 + openbsd: i386;amd64;arm;arm64 dragonfly: i386;amd64 haiku: i386;amd64 android: i386;arm;arm64 diff --git a/tools/niminst/buildsh.nimf b/tools/niminst/buildsh.nimf index 83c4aca6d..537a8a01d 100644 --- a/tools/niminst/buildsh.nimf +++ b/tools/niminst/buildsh.nimf @@ -90,6 +90,7 @@ case $uos in ;; *openbsd* ) myos="openbsd" + CC="clang" LINK_FLAGS="$LINK_FLAGS -lm" ;; *netbsd* ) From cc3409e26437e7e1d89c8d4b7baacdee695a7b44 Mon Sep 17 00:00:00 2001 From: Max Grender-Jones Date: Tue, 9 Jun 2020 02:49:07 +0100 Subject: [PATCH 0196/3768] Make the example better describe the desired outcome (#14611) --- lib/std/wordwrap.nim | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/std/wordwrap.nim b/lib/std/wordwrap.nim index bb6f2dc6f..27df229bf 100644 --- a/lib/std/wordwrap.nim +++ b/lib/std/wordwrap.nim @@ -110,9 +110,9 @@ tnaetdriaoenvlcyfglwckßqfgvwkßqgfvlwkßqfgvlwckßqvlwkgfUIαοιαοιαχολ # bug #14579 const input60 = """ -This is a long string. It is manually wrapped to 60 -characters. I would not expect it to be changed by -wordwrap if wordwrap is set to wrap at 80 characters""" - const input60Res = """This is a long string. It is manually wrapped to 60 characters. I would not -expect it to be changed by wordwrap if wordwrap is set to wrap at 80 characters""" +This string is wrapped to 60 characters. If we call +wrapwords on it it will be re-wrapped to 80 characters. +""" + const input60Res = """This string is wrapped to 60 characters. If we call wrapwords on it it will be +re-wrapped to 80 characters.""" doAssert wrapWords(input60) == input60Res From aa70cfdea282e717a41f7dbfec32d739f7b2e08a Mon Sep 17 00:00:00 2001 From: ynfle <23086821+ynfle@users.noreply.github.com> Date: Mon, 8 Jun 2020 23:14:26 -0400 Subject: [PATCH 0197/3768] Fix(astspec.txt): type of repeated word (#14612) --- doc/astspec.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/astspec.txt b/doc/astspec.txt index 5d50d82d9..ab2fbcacc 100644 --- a/doc/astspec.txt +++ b/doc/astspec.txt @@ -1014,7 +1014,7 @@ AST: ) Note that not all ``nnkTypeDef`` utilize ``nnkIdent`` as their -their parameter. One of the most common uses of type declarations +parameter. One of the most common uses of type declarations is to work with objects. Concrete syntax: From 881fa94b57d6d0ebd661ad21340c8e75f173809d Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Tue, 9 Jun 2020 10:43:42 -0300 Subject: [PATCH 0198/3768] Documentation Fix Typo, Add Table (#14609) --- doc/gc.rst | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/doc/gc.rst b/doc/gc.rst index 01e6b347f..39d6d0484 100644 --- a/doc/gc.rst +++ b/doc/gc.rst @@ -36,13 +36,25 @@ To choose the memory management strategy use the ``--gc:`` switch. cause memory leaks, beware. - ``--gc:orc``. Same as ``-gc:arc`` but adds a cycle collector based on "trial deletion". - Unforunately that makes its performance profile hard to reason about so it is less + Unfortunately that makes its performance profile hard to reason about so it is less useful for hard realtime systems. - ``--gc:none``. No memory management strategy nor garbage collector. Allocated memory is simply never freed. You should use ``--gc:arc`` instead. +================== ======== ================= ====================== +Memory Management Heap Reference Cycles Command line switch +================== ======== ================= ====================== +RefC Local Cycle Collector ``--gc:refc`` +Mark & Sweep Local Cycle Collector ``--gc:markAndSweep`` +ARC Shared Leak ``--gc:arc`` +ORC Shared Cycle Collector ``--gc:orc`` +Boehm Shared Cycle Collector ``--gc:boehm`` +Go Shared Cycle Collector ``--gc:go`` +None Manual Manual ``--gc:none`` +================== ======== ================= ====================== + JavaScript's garbage collector is used for the `JavaScript and NodeJS `_ compilation targets. The `NimScript `_ target uses the memory management strategy built into From b3029ba2139a018946218e1917918d1a1ef019e2 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 9 Jun 2020 17:05:01 +0200 Subject: [PATCH 0199/3768] fixes #14578 (#14615) --- compiler/ccgtypes.nim | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 96b59cd60..f7eb1bc34 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -268,19 +268,18 @@ proc ccgIntroducedPtr(conf: ConfigRef; s: PSym, retType: PType): bool = result = true elif (optByRef in s.options) or (getSize(conf, pt) > conf.target.floatSize * 3): result = true # requested anyway - elif retType != nil and retType.kind == tyLent: - result = true elif (tfFinal in pt.flags) and (pt[0] == nil): result = false # no need, because no subtyping possible else: result = true # ordinary objects are always passed by reference, # otherwise casting doesn't work of tyTuple: - if retType != nil and retType.kind == tyLent: - result = true - else: - result = (getSize(conf, pt) > conf.target.floatSize*3) or (optByRef in s.options) - else: result = false + result = (getSize(conf, pt) > conf.target.floatSize*3) or (optByRef in s.options) + else: + result = false + # first parameter and return type is 'lent T'? --> use pass by pointer + if s.position == 0 and retType != nil and retType.kind == tyLent: + result = true proc fillResult(conf: ConfigRef; param: PNode) = fillLoc(param.sym.loc, locParam, param, ~"Result", From 6085ad95ff4f712d14de2786a8bd5e2cea4397df Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 9 Jun 2020 20:39:26 +0200 Subject: [PATCH 0200/3768] fixes #14279 (#14618) --- compiler/closureiters.nim | 8 ++++++-- tests/async/tasynctry.nim | 15 +++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/compiler/closureiters.nim b/compiler/closureiters.nim index 3422a8a46..675f1ae41 100644 --- a/compiler/closureiters.nim +++ b/compiler/closureiters.nim @@ -129,7 +129,7 @@ # break :stateLoop import - ast, msgs, idents, + ast, astalgo, msgs, idents, renderer, magicsys, lowerings, lambdalifting, modulegraphs, lineinfos type @@ -388,6 +388,10 @@ proc hasYieldsInExpressions(n: PNode): bool = return true else: result = n.hasYields + of nkCast: + for i in 1.. Date: Tue, 9 Jun 2020 18:17:32 -0300 Subject: [PATCH 0201/3768] Documentation update a description (#14619) * Documentation update a description in a page listing links * Update doc/docs.rst Co-authored-by: Danil Yarantsev Co-authored-by: Danil Yarantsev --- doc/docs.rst | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/doc/docs.rst b/doc/docs.rst index 4a69bd69a..b34383253 100644 --- a/doc/docs.rst +++ b/doc/docs.rst @@ -23,8 +23,8 @@ The documentation consists of several documents: | Description of some tools that come with the standard distribution. - | `GC `_ - | Additional documentation about Nim's GC and how to operate it in a - | realtime setting. + | Additional documentation about Nim's multi-paradigm memory management strategies + | and how to operate them in a realtime setting. - | `Source code filters `_ | The Nim compiler supports source code filters as a simple yet powerful @@ -36,4 +36,3 @@ The documentation consists of several documents: - | `Index `_ | The generated index. **Index + (Ctrl+F) == Joy** - From d81bc69587efb9249ad2e022d1bc3f8e4865506f Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 9 Jun 2020 19:15:18 -0700 Subject: [PATCH 0202/3768] enable tioselectors on osx; more diagnostic for #13166 (#14625) --- tests/async/tioselectors.nim | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/tests/async/tioselectors.nim b/tests/async/tioselectors.nim index 48aa64838..c20df29e5 100644 --- a/tests/async/tioselectors.nim +++ b/tests/async/tioselectors.nim @@ -10,9 +10,7 @@ template processTest(t, x: untyped) = #stdout.flushFile() if not x: echo(t & " FAILED\r\n") -when defined(macosx): - echo "All tests passed!" -elif not defined(windows): +when not defined(windows): import os, posix, nativesockets when ioselSupportedPlatform: @@ -149,13 +147,13 @@ elif not defined(windows): var timer = selector.registerTimer(100, false, 0) var rc1 = selector.select(140) var rc2 = selector.select(140) - assert(len(rc1) == 1 and len(rc2) == 1) + assert len(rc1) == 1 and len(rc2) == 1, $(len(rc1), len(rc2)) selector.unregister(timer) discard selector.select(0) selector.registerTimer(100, true, 0) var rc4 = selector.select(120) var rc5 = selector.select(120) - assert(len(rc4) == 1 and len(rc5) == 0) + assert len(rc4) == 1 and len(rc5) == 0, $(len(rc4), len(rc5)) assert(selector.isEmpty()) selector.close() result = true From b7b84fa1652e6ef4ea203f553a6c1f0aed51bcf3 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 9 Jun 2020 23:57:27 -0700 Subject: [PATCH 0203/3768] parseutils: integerOutOfRangeDefect => integerOutOfRangeError (#14627) --- lib/pure/parseutils.nim | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim index c185eca37..c358005f1 100644 --- a/lib/pure/parseutils.nim +++ b/lib/pure/parseutils.nim @@ -393,7 +393,7 @@ proc captureBetween*(s: string, first: char, second = '\0', start = 0): string = result = "" discard s.parseUntil(result, if second == '\0': first else: second, i) -proc integerOutOfRangeDefect() {.noinline.} = +proc integerOutOfRangeError() {.noinline.} = raise newException(ValueError, "Parsed integer outside of valid range") # See #6752 @@ -416,11 +416,11 @@ proc rawParseInt(s: string, b: var BiggestInt, start = 0): int = if b >= (low(BiggestInt) + c) div 10: b = b * 10 - c else: - integerOutOfRangeDefect() + integerOutOfRangeError() inc(i) while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored if sign == -1 and b == low(BiggestInt): - integerOutOfRangeDefect() + integerOutOfRangeError() else: b = b * sign result = i - start @@ -459,7 +459,7 @@ proc parseInt*(s: string, number: var int, start = 0): int {. result = parseBiggestInt(s, res, start) when sizeof(int) <= 4: if res < low(int) or res > high(int): - integerOutOfRangeDefect() + integerOutOfRangeError() if result != 0: number = int(res) @@ -493,7 +493,7 @@ proc rawParseUInt(s: string, b: var BiggestUInt, start = 0): int = prev = 0.BiggestUInt i = start if i < s.len - 1 and s[i] == '-' and s[i + 1] in {'0'..'9'}: - integerOutOfRangeDefect() + integerOutOfRangeError() if i < s.len and s[i] == '+': inc(i) # Allow if i < s.len and s[i] in {'0'..'9'}: b = 0 @@ -501,7 +501,7 @@ proc rawParseUInt(s: string, b: var BiggestUInt, start = 0): int = prev = res res = res * 10 + (ord(s[i]) - ord('0')).BiggestUInt if prev > res: - integerOutOfRangeDefect() + integerOutOfRangeError() inc(i) while i < s.len and s[i] == '_': inc(i) # underscores are allowed and ignored b = res @@ -540,7 +540,7 @@ proc parseUInt*(s: string, number: var uint, start = 0): int {. result = parseBiggestUInt(s, res, start) when sizeof(BiggestUInt) > sizeof(uint) and sizeof(uint) <= 4: if res > 0xFFFF_FFFF'u64: - integerOutOfRangeDefect() + integerOutOfRangeError() if result != 0: number = uint(res) From 5131af1a446140d275510d67bff281f2e42cc19b Mon Sep 17 00:00:00 2001 From: ee7 <45465154+ee7@users.noreply.github.com> Date: Wed, 10 Jun 2020 09:34:20 +0200 Subject: [PATCH 0204/3768] exceptions.nim: Fix a bad `Error` -> `Defect` renaming (#14621) This commit fixes a mistake from 7d6cbf290a. --- lib/system/exceptions.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/system/exceptions.nim b/lib/system/exceptions.nim index ab092647c..3006cff19 100644 --- a/lib/system/exceptions.nim +++ b/lib/system/exceptions.nim @@ -140,7 +140,7 @@ type ## This is only raised if the `segfaults module `_ was imported! ArithmeticError* {.deprecated: "See corresponding Defect".} = ArithmeticDefect - DivByZeroError* {.deprecated: "See corresponding Defect".} = ArithmeticDefect + DivByZeroError* {.deprecated: "See corresponding Defect".} = DivByZeroDefect OverflowError* {.deprecated: "See corresponding Defect".} = OverflowDefect AccessViolationError* {.deprecated: "See corresponding Defect".} = AccessViolationDefect AssertionError* {.deprecated: "See corresponding Defect".} = AssertionDefect From 8bbdb8f43fb0da04d4b75bca011662f690972bfe Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 10 Jun 2020 05:10:57 -0700 Subject: [PATCH 0205/3768] fix #14545 windows CI docs (#14590) * fix #14545 windows paths * add lib/std/private/strutils2.nim * remove dependency strutils2 => strutils * address comment: lib/std/private/strutils2.nim => compiler/strutils2.nim --- .github/workflows/ci_docs.yml | 1 + compiler/msgs.nim | 18 +++++++++-- compiler/strutils2.nim | 57 +++++++++++++++++++++++++++++++++++ tests/stdlib/tstrutils2.nim | 32 ++++++++++++++++++++ 4 files changed, 105 insertions(+), 3 deletions(-) create mode 100644 compiler/strutils2.nim create mode 100644 tests/stdlib/tstrutils2.nim diff --git a/.github/workflows/ci_docs.yml b/.github/workflows/ci_docs.yml index 2fbf7e7dc..816f560c9 100644 --- a/.github/workflows/ci_docs.yml +++ b/.github/workflows/ci_docs.yml @@ -24,6 +24,7 @@ on: - 'tools/kochdocs.nim' - '.github/workflows/ci_docs.yml' + jobs: build: strategy: diff --git a/compiler/msgs.nim b/compiler/msgs.nim index a0fc9b08d..e27eedd1c 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -11,6 +11,7 @@ import options, strutils, os, tables, ropes, terminal, macros, lineinfos, pathutils import std/private/miscdollars +import strutils2 type InstantiationInfo = typeof(instantiationInfo()) template instLoc(): InstantiationInfo = instantiationInfo(-2, fullPaths = true) @@ -66,6 +67,12 @@ when defined(nimpretty): proc fileSection*(conf: ConfigRef; fid: FileIndex; a, b: int): string = substr(conf.m.fileInfos[fid.int].fullContent, a, b) +proc canonicalCase(path: var string) = + ## the idea is to only use this for checking whether a path is already in + ## the table but otherwise keep the original case + when FileSystemCaseSensitive: discard + else: toLowerAscii(path) + proc fileInfoKnown*(conf: ConfigRef; filename: AbsoluteFile): bool = var canon: AbsoluteFile @@ -73,6 +80,7 @@ proc fileInfoKnown*(conf: ConfigRef; filename: AbsoluteFile): bool = canon = canonicalizePath(conf, filename) except OSError: canon = filename + canon.string.canonicalCase result = conf.m.filenameToIndexTbl.hasKey(canon.string) proc fileInfoIdx*(conf: ConfigRef; filename: AbsoluteFile; isKnownFile: var bool): FileIndex = @@ -89,15 +97,19 @@ proc fileInfoIdx*(conf: ConfigRef; filename: AbsoluteFile; isKnownFile: var bool # This flag indicates that we are working with such a path here pseudoPath = true - if conf.m.filenameToIndexTbl.hasKey(canon.string): + var canon2: string + forceCopy(canon2, canon.string) # because `canon` may be shallow + canon2.canonicalCase + + if conf.m.filenameToIndexTbl.hasKey(canon2): isKnownFile = true - result = conf.m.filenameToIndexTbl[canon.string] + result = conf.m.filenameToIndexTbl[canon2] else: isKnownFile = false result = conf.m.fileInfos.len.FileIndex conf.m.fileInfos.add(newFileInfo(canon, if pseudoPath: RelativeFile filename else: relativeTo(canon, conf.projectPath))) - conf.m.filenameToIndexTbl[canon.string] = result + conf.m.filenameToIndexTbl[canon2] = result proc fileInfoIdx*(conf: ConfigRef; filename: AbsoluteFile): FileIndex = var dummy: bool diff --git a/compiler/strutils2.nim b/compiler/strutils2.nim new file mode 100644 index 000000000..6cb50347f --- /dev/null +++ b/compiler/strutils2.nim @@ -0,0 +1,57 @@ +##[ +internal API for now, subject to modifications and moving around + +string API's focusing on performance, that can be used as building blocks +for other routines. + +Un-necessary allocations are avoided and appropriate algorithms are used at the +expense of code clarity when justified. +]## + +proc dataPointer*[T](a: T): pointer = + ## same as C++ `data` that works with std::string, std::vector etc. + ## Note: safe to use when a.len == 0 but whether the result is nil or not + ## is implementation defined for performance reasons. + # this could be improved with ocmpiler support to avoid the `if`, eg in C++ + # `&a[0]` is well defined even if a.size() == 0 + when T is string | seq: + if a.len == 0: nil else: cast[pointer](a[0].unsafeAddr) + elif T is array: + when a.len > 0: a.unsafeAddr + else: nil + elif T is cstring: + cast[pointer](a) + else: static: doAssert false, $T + +proc setLen*(result: var string, n: int, isInit: bool) = + ## when isInit = false, elements are left uninitialized, analog to `{.noinit.}` + ## else, there are 0-initialized. + # xxx placeholder until system.setLen supports this + # to distinguish between algorithms that need 0-initialization vs not; note + # that `setLen` for string is inconsistent with `setLen` for seq. + # likwise with `newString` vs `newSeq`. This should be fixed in `system`. + let n0 = result.len + result.setLen(n) + if isInit and n > n0: + zeroMem(result[n0].addr, n - n0) + +proc forceCopy*(result: var string, a: string) = + ## also forces a copy if `a` is shallow + # the naitve `result = a` would not work if `a` is shallow + let n = a.len + result.setLen n, isInit = false + copyMem(result.dataPointer, a.dataPointer, n) + +proc isUpperAscii(c: char): bool {.inline.} = + # avoids import strutils.isUpperAscii + c in {'A'..'Z'} + +proc toLowerAscii*(a: var string) = + ## optimized and inplace overload of strutils.toLowerAscii + # refs https://github.com/timotheecour/Nim/pull/54 + # this is 10X faster than a naive implementation using a an optimization trick + # that can be adapted in similar contexts. Predictable writes avoid write + # hazards and lead to better machine code, compared to random writes arising + # from: `if c.isUpperAscii: c = ...` + for c in mitems(a): + c = chr(c.ord + (if c.isUpperAscii: (ord('a') - ord('A')) else: 0)) diff --git a/tests/stdlib/tstrutils2.nim b/tests/stdlib/tstrutils2.nim new file mode 100644 index 000000000..881817f90 --- /dev/null +++ b/tests/stdlib/tstrutils2.nim @@ -0,0 +1,32 @@ +import "$lib/.." / compiler/strutils2 + +block: # setLen + var a = "abc" + a.setLen 0 + a.setLen 3, isInit = false + doAssert a[1] == 'b' + a.setLen 0 + a.setLen 3, isInit = true + doAssert a[1] == '\0' + +block: # forceCopy + var a: string + a = "foo" + shallow(a) + var b: string + b = a + doAssert b[0].addr == a[0].addr + var c: string + c.forceCopy a + doAssert c == a + doAssert c[0].addr != a[0].addr + +block: # toLowerAscii + var a = "fooBAr" + a.toLowerAscii + doAssert a == "foobar" + +block: # dataPointer + var a: string + discard a.dataPointer + # doAssert a.dataPointer == nil # not guaranteed From 6aa971d39f1339a27a953972cd329f9cbe791f98 Mon Sep 17 00:00:00 2001 From: c-blake Date: Wed, 10 Jun 2020 14:53:18 -0400 Subject: [PATCH 0206/3768] Add `proc find` to `heapqueue` (#14628) * Unwind just the "pseudorandom probing" (whole hash-code-keyed variable stride double hashing) part of recent sets & tables changes (which has still been causing bugs over a month later (e.g., two days ago https://github.com/nim-lang/Nim/issues/13794) as well as still having several "figure this out" implementation question comments in them (see just diffs of this PR). This topic has been discussed in many places: https://github.com/nim-lang/Nim/issues/13393 https://github.com/nim-lang/Nim/pull/13418 https://github.com/nim-lang/Nim/pull/13440 https://github.com/nim-lang/Nim/issues/13794 Alternative/non-mandatory stronger integer hashes (or vice-versa opt-in identity hashes) are a better solution that is more general (no illusion of one hard-coded sequence solving all problems) while retaining the virtues of linear probing such as cache obliviousness and age-less tables under delete-heavy workloads (still untested after a month of this change). The only real solution for truly adversarial keys is a hash keyed off of data unobservable to attackers. That all fits better with a few families of user-pluggable/define-switchable hashes which can be provided in a separate PR more about `hashes.nim`. This PR carefully preserves the better (but still hard coded!) probing of the `intsets` and other recent fixes like `move` annotations, hash order invariant tests, `intsets.missingOrExcl` fixing, and the move of `rightSize` into `hashcommon.nim`. * Fix `data.len` -> `dataLen` problem. * Add neglected API call `find` to heapqueue. * Add a changelog.md entry, `since` annotation and rename parameter to be `heap` like all the other procs for consistency. * Add missing import. --- changelog.md | 2 ++ lib/pure/collections/heapqueue.nim | 17 +++++++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/changelog.md b/changelog.md index 8e06d30f1..e86e42b33 100644 --- a/changelog.md +++ b/changelog.md @@ -110,6 +110,8 @@ - new module `std/jsonutils` with hookable `jsonTo,toJson,fromJson` for json serialization/deserialization of custom types. +- new proc `heapqueue.find[T](heap: HeapQueue[T], x: T): int` to get index of element ``x``. + ## Language changes - In the 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: diff --git a/lib/pure/collections/heapqueue.nim b/lib/pure/collections/heapqueue.nim index f958a5b0a..b0789593a 100644 --- a/lib/pure/collections/heapqueue.nim +++ b/lib/pure/collections/heapqueue.nim @@ -50,6 +50,7 @@ assert jobs[0].priority == 1 ]## +import std/private/since type HeapQueue*[T] = object ## A heap queue, commonly known as a priority queue. @@ -125,6 +126,12 @@ proc pop*[T](heap: var HeapQueue[T]): T = else: result = lastelt +proc find*[T](heap: HeapQueue[T], x: T): int {.since: (1, 3).} = + ## Linear scan to find index of item ``x`` or -1 if not found. + result = -1 + for i in 0 ..< heap.len: + if heap[i] == x: return i + proc del*[T](heap: var HeapQueue[T], index: Natural) = ## Removes the element at `index` from `heap`, maintaining the heap invariant. swap(heap.data[^1], heap.data[index]) @@ -207,18 +214,20 @@ when isMainModule: heap.del(0) doAssert(heap[0] == 1) - heap.del(heap.data.find(7)) + heap.del(heap.find(7)) doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 5, 6, 8, 9]) - heap.del(heap.data.find(5)) + heap.del(heap.find(5)) doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 6, 8, 9]) - heap.del(heap.data.find(6)) + heap.del(heap.find(6)) doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 8, 9]) - heap.del(heap.data.find(2)) + heap.del(heap.find(2)) doAssert(heap.toSortedSeq == @[1, 3, 4, 8, 9]) + doAssert(heap.find(2) == -1) + block: # Test del last var heap = initHeapQueue[int]() let data = [1, 2, 3] From c83a22b76e22d3cae0e0e6e4b9358ed9bd31ecb5 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 11 Jun 2020 12:54:32 -0700 Subject: [PATCH 0207/3768] Disable tfdleak_multiple on platforms other than Windows (#14624) --- tests/stdlib/tfdleak_multiple.nim | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/stdlib/tfdleak_multiple.nim b/tests/stdlib/tfdleak_multiple.nim index 51d291284..75c4c0503 100644 --- a/tests/stdlib/tfdleak_multiple.nim +++ b/tests/stdlib/tfdleak_multiple.nim @@ -20,4 +20,7 @@ proc testFdLeak() = count.inc doAssert count == 0, "Leaked " & $count & " times" -when isMainModule: testFdLeak() +when defined(windows): + # tfdleak was only flaky for windows (and for netbsd, there is still a bug) + # note that this test is quite slow, 87 sec on windows. + testFdLeak() From 67d34366dc442cd655c78ce41a3fa50491736eb6 Mon Sep 17 00:00:00 2001 From: alaviss Date: Fri, 12 Jun 2020 03:06:29 -0500 Subject: [PATCH 0208/3768] tools/kochdocs: add log folding supports for more CI services (#14643) Added log folding for: - Github Actions - Azure Pipelines --- tools/kochdocs.nim | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tools/kochdocs.nim b/tools/kochdocs.nim index 4f39463e1..eacfb45ca 100644 --- a/tools/kochdocs.nim +++ b/tools/kochdocs.nim @@ -49,14 +49,22 @@ proc exec*(cmd: string, errorcode: int = QuitFailure, additionalPath = "") = template inFold*(desc, body) = if existsEnv("TRAVIS"): echo "travis_fold:start:" & desc.replace(" ", "_") + elif existsEnv("GITHUB_ACTIONS"): + echo "::group::" & desc + elif existsEnv("TF_BUILD"): + echo "##[group]" & desc body if existsEnv("TRAVIS"): echo "travis_fold:end:" & desc.replace(" ", "_") + elif existsEnv("GITHUB_ACTIONS"): + echo "::endgroup::" + elif existsEnv("TF_BUILD"): + echo "##[endgroup]" proc execFold*(desc, cmd: string, errorcode: int = QuitFailure, additionalPath = "") = - ## Execute shell command. Add log folding on Travis CI. + ## Execute shell command. Add log folding for various CI services. # https://github.com/travis-ci/travis-ci/issues/2285#issuecomment-42724719 inFold(desc): exec(cmd, errorcode, additionalPath) From 1168c75381446f8c81ce3f5f329ce2dab238415c Mon Sep 17 00:00:00 2001 From: djazz Date: Fri, 12 Jun 2020 10:16:38 +0200 Subject: [PATCH 0209/3768] httpcore: Add http code 308 Permanent Redirect (#14639) * httpcore: Add http code 308 * httpclient: Add 308 to redirection proc * fix typo --- lib/pure/httpclient.nim | 2 +- lib/pure/httpcore.nim | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 42735ad4e..1ac3ae185 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -457,7 +457,7 @@ proc sendFile(socket: Socket | AsyncSocket, file.close() proc redirection(status: string): bool = - const redirectionNRs = ["301", "302", "303", "307"] + const redirectionNRs = ["301", "302", "303", "307", "308"] for i in items(redirectionNRs): if status.startsWith(i): return true diff --git a/lib/pure/httpcore.nim b/lib/pure/httpcore.nim index 78cb66ded..9c51887cb 100644 --- a/lib/pure/httpcore.nim +++ b/lib/pure/httpcore.nim @@ -66,6 +66,7 @@ const Http304* = HttpCode(304) Http305* = HttpCode(305) Http307* = HttpCode(307) + Http308* = HttpCode(308) Http400* = HttpCode(400) Http401* = HttpCode(401) Http403* = HttpCode(403) @@ -272,6 +273,7 @@ proc `$`*(code: HttpCode): string = of 304: "304 Not Modified" of 305: "305 Use Proxy" of 307: "307 Temporary Redirect" + of 308: "308 Permanent Redirect" of 400: "400 Bad Request" of 401: "401 Unauthorized" of 403: "403 Forbidden" From 82e424189ee4bf0f05fcf5695fe1c3e2d2dfc51f Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Fri, 12 Jun 2020 05:51:55 -0300 Subject: [PATCH 0210/3768] Add rstgen.rstToLatex convenience proc for renderRstToOut and initRstGenerator with outLatex output, see https://github.com/nim-lang/fusion/pull/11#issuecomment-641804899 (#14629) --- changelog.md | 2 ++ lib/packages/docutils/rstgen.nim | 11 +++++++++++ 2 files changed, 13 insertions(+) diff --git a/changelog.md b/changelog.md index e86e42b33..623b3f6bc 100644 --- a/changelog.md +++ b/changelog.md @@ -111,6 +111,8 @@ - new module `std/jsonutils` with hookable `jsonTo,toJson,fromJson` for json serialization/deserialization of custom types. - new proc `heapqueue.find[T](heap: HeapQueue[T], x: T): int` to get index of element ``x``. +- Add `rstgen.rstToLatex` convenience proc for `renderRstToOut` and `initRstGenerator` with `outLatex` output. + ## Language changes - In the 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: diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 6a7b33cc0..2ec25cc6e 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -29,6 +29,7 @@ import strutils, os, hashes, strtabs, rstast, rst, highlite, tables, sequtils, algorithm, parseutils import "$lib/../compiler/nimpaths" import "$lib/../compiler/pathutils" +import ../../std/private/since const HtmlExt = "html" @@ -1344,6 +1345,16 @@ proc rstToHtml*(s: string, options: RstParseOptions, renderRstToOut(d, rst, result) +proc rstToLatex*(rstSource: string; options: RstParseOptions): string {.inline, since: (1, 3).} = + ## Convenience proc for `renderRstToOut` and `initRstGenerator`. + runnableExamples: doAssert rstToLatex("*Hello* **world**", {}) == """\emph{Hello} \textbf{world}""" + if rstSource.len == 0: return + var option: bool + var rstGenera: RstGenerator + rstGenera.initRstGenerator(outLatex, defaultConfig(), "input", options) + rstGenera.renderRstToOut(rstParse(rstSource, "", 1, 1, option, options), result) + + when isMainModule: assert rstToHtml("*Hello* **world**!", {}, newStringTable(modeStyleInsensitive)) == From e30a08103da14d5b1e02d6c04dc2d5afb5b9368d Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 12 Jun 2020 02:03:52 -0700 Subject: [PATCH 0211/3768] remove tyOpt, mOpt (#14636) * remove tyOpt, mOpt * fixup --- compiler/ast.nim | 10 +++++----- compiler/ccgtypes.nim | 2 +- compiler/jsgen.nim | 9 +++++---- compiler/liftdestructors.nim | 3 ++- compiler/semtypes.nim | 1 - compiler/types.nim | 10 ++++------ compiler/vmdeps.nim | 2 +- compiler/vmgen.nim | 2 -- lib/core/macros.nim | 2 +- lib/system/hti.nim | 2 +- 10 files changed, 20 insertions(+), 23 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 76b1d8c91..bc26defd4 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -434,8 +434,8 @@ type # instantiation and prior to this it has the potential to # be any type. - tyOpt - # Builtin optional type + tyOptDeprecated + # deadcode: was `tyOpt`, Builtin optional type tyVoid # now different from tyEmpty, hurray! @@ -658,7 +658,7 @@ type mNewString, mNewStringOfCap, mParseBiggestFloat, mMove, mWasMoved, mDestroy, mDefault, mUnown, mAccessEnv, mReset, - mArray, mOpenArray, mRange, mSet, mSeq, mOpt, mVarargs, + mArray, mOpenArray, mRange, mSet, mSeq, mVarargs, mRef, mPtr, mVar, mDistinct, mVoid, mTuple, mOrdinal, mInt, mInt8, mInt16, mInt32, mInt64, @@ -1492,7 +1492,7 @@ proc isGCedMem*(t: PType): bool {.inline.} = t.kind == tyProc and t.callConv == ccClosure proc propagateToOwner*(owner, elem: PType; propagateHasAsgn = true) = - const HaveTheirOwnEmpty = {tySequence, tyOpt, tySet, tyPtr, tyRef, tyProc} + const HaveTheirOwnEmpty = {tySequence, tySet, tyPtr, tyRef, tyProc} owner.flags = owner.flags + (elem.flags * {tfHasMeta, tfTriggersCompileTime}) if tfNotNil in elem.flags: if owner.kind in {tyGenericInst, tyGenericBody, tyGenericInvocation}: @@ -1505,7 +1505,7 @@ proc propagateToOwner*(owner, elem: PType; propagateHasAsgn = true) = if mask != {} and propagateHasAsgn: let o2 = owner.skipTypes({tyGenericInst, tyAlias, tySink}) if o2.kind in {tyTuple, tyObject, tyArray, - tySequence, tyOpt, tySet, tyDistinct, tyOpenArray, tyVarargs}: + tySequence, tySet, tyDistinct, tyOpenArray, tyVarargs}: o2.flags.incl mask owner.flags.incl mask diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index f7eb1bc34..199d5c918 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -367,7 +367,7 @@ proc getTypeForward(m: BModule, typ: PType; sig: SigHash): Rope = if result != nil: return result = getTypePre(m, typ, sig) if result != nil: return - let concrete = typ.skipTypes(abstractInst + {tyOpt}) + let concrete = typ.skipTypes(abstractInst) case concrete.kind of tySequence, tyTuple, tyObject: result = getTypeName(m, typ, sig) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 9f022882b..99d2bc4f2 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -193,7 +193,7 @@ proc mapType(typ: PType): TJSTypeKind = of tyBool: result = etyBool of tyFloat..tyFloat128: result = etyFloat of tySet: result = etyObject # map a set to a table - of tyString, tySequence, tyOpt: result = etySeq + of tyString, tySequence: result = etySeq of tyObject, tyArray, tyTuple, tyOpenArray, tyVarargs, tyUncheckedArray: result = etyObject of tyNil: result = etyNull @@ -210,6 +210,7 @@ proc mapType(typ: PType): TJSTypeKind = else: result = etyNone of tyProc: result = etyProc of tyCString: result = etyString + of tyOptDeprecated: doAssert false proc mapType(p: PProc; typ: PType): TJSTypeKind = result = mapType(typ) @@ -1041,7 +1042,7 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = # we don't care if it's an etyBaseIndex (global) of a string, it's # still a string that needs to be copied properly: - if x.typ.skipTypes(abstractInst).kind in {tySequence, tyOpt, tyString}: + if x.typ.skipTypes(abstractInst).kind in {tySequence, tyString}: xtyp = etySeq case xtyp of etySeq: @@ -1086,7 +1087,7 @@ proc genFastAsgn(p: PProc, n: PNode) = # See bug #5933. So we try to be more compatible with the C backend semantics # here for 'shallowCopy'. This is an educated guess and might require further # changes later: - let noCopy = n[0].typ.skipTypes(abstractInst).kind in {tySequence, tyOpt, tyString} + let noCopy = n[0].typ.skipTypes(abstractInst).kind in {tySequence, tyString} genAsgnAux(p, n[0], n[1], noCopyNeeded=noCopy) proc genSwap(p: PProc, n: PNode) = @@ -1697,7 +1698,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope = result = putToSeq("null", indirect) of tySequence, tyString: result = putToSeq("[]", indirect) - of tyCString, tyOpt, tyProc: + of tyCString, tyProc: result = putToSeq("null", indirect) of tyStatic: if t.n != nil: diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 2d8655cae..0346f6c67 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -703,7 +703,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) = case t.kind of tyNone, tyEmpty, tyVoid: discard of tyPointer, tySet, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString, - tyPtr, tyOpt, tyUncheckedArray, tyVar, tyLent: + tyPtr, tyUncheckedArray, tyVar, tyLent: defaultOp(c, t, body, x, y) of tyRef: if c.g.config.selectedGC in {gcArc, gcOrc}: @@ -785,6 +785,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) = of tyOrdinal, tyRange, tyInferred, tyGenericInst, tyAlias, tySink: fillBody(c, lastSon(t), body, x, y) + of tyOptDeprecated: doAssert false proc produceSymDistinctType(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp; info: TLineInfo): PSym = diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 77da403fa..313328385 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1814,7 +1814,6 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = result = semContainer(c, n, tySequence, "seq", prev) if optSeqDestructors in c.config.globalOptions: incl result.flags, tfHasAsgn - of mOpt: result = semContainer(c, n, tyOpt, "opt", prev) of mVarargs: result = semVarargs(c, n, prev) of mTypeDesc, mType, mTypeOf: result = makeTypeDesc(c, semTypeNode(c, n[1], nil)) diff --git a/compiler/types.nim b/compiler/types.nim index e1b283f87..aaaa8e99f 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -608,10 +608,6 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = result = "seq" if t.len > 0: result &= "[" & typeToString(t[0]) & ']' - of tyOpt: - result = "opt" - if t.len > 0: - result &= "opt[" & typeToString(t[0]) & ']' of tyOrdinal: result = "ordinal" if t.len > 0: @@ -1150,7 +1146,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = of tyGenericInvocation, tyGenericBody, tySequence, tyOpenArray, tySet, tyRef, tyPtr, tyVar, tyLent, tySink, tyUncheckedArray, tyArray, tyProc, tyVarargs, tyOrdinal, tyCompositeTypeClass, tyUserTypeClass, tyUserTypeClassInst, - tyAnd, tyOr, tyNot, tyAnything, tyOpt, tyOwned: + tyAnd, tyOr, tyNot, tyAnything, tyOwned: cycleCheck() if a.kind == tyUserTypeClass and a.n != nil: return a.n == b.n result = sameChildrenAux(a, b, c) @@ -1173,6 +1169,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = cycleCheck() result = sameTypeAux(a.lastSon, b.lastSon, c) of tyNone: result = false + of tyOptDeprecated: doAssert false proc sameBackendType*(x, y: PType): bool = var c = initSameTypeClosure() @@ -1365,7 +1362,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, result = t else: result = typeAllowedAux(marker, lastSon(t), kind, flags-{taHeap}) - of tySequence, tyOpt: + of tySequence: if t[0].kind != tyEmpty: result = typeAllowedAux(marker, t[0], kind, flags+{taHeap}) elif kind in {skVar, skLet}: @@ -1408,6 +1405,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, result = typeAllowedAux(marker, t.lastSon, kind, flags+{taHeap}) else: result = t + of tyOptDeprecated: doAssert false proc typeAllowed*(t: PType, kind: TSymKind; flags: TTypeAllowedFlags = {}): PType = # returns 'nil' on success and otherwise the part of the type that is diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim index 7cfba046a..6f90b7918 100644 --- a/compiler/vmdeps.nim +++ b/compiler/vmdeps.nim @@ -224,7 +224,6 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo; of tyLent: result = mapTypeToBracket("lent", mBuiltinType, t, info) of tySink: result = mapTypeToBracket("sink", mBuiltinType, t, info) of tySequence: result = mapTypeToBracket("seq", mSeq, t, info) - of tyOpt: result = mapTypeToBracket("opt", mOpt, t, info) of tyProc: if inst: result = newNodeX(nkProcTy) @@ -298,6 +297,7 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo; if t.n != nil: result.add t.n.copyTree of tyOwned: result = mapTypeToBracket("owned", mBuiltinType, t, info) + of tyOptDeprecated: doAssert false proc opMapTypeToAst*(cache: IdentCache; t: PType; info: TLineInfo): PNode = result = mapTypeToAstX(cache, t, info, inst=false, allowRecursionX=true) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index b408db08b..52b7b6eb3 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1820,8 +1820,6 @@ proc getNullValue(typ: PType, info: TLineInfo; conf: ConfigRef): PNode = result.add getNullValue(t[i], info, conf) of tySet: result = newNodeIT(nkCurly, info, t) - of tyOpt: - result = newNodeIT(nkNilLit, info, t) of tySequence: result = newNodeIT(nkBracket, info, t) else: diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 9e17b665a..3590a162b 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -106,7 +106,7 @@ type ntyError, ntyBuiltinTypeClass, ntyUserTypeClass, ntyUserTypeClassInst, ntyCompositeTypeClass, ntyInferred, ntyAnd, ntyOr, ntyNot, - ntyAnything, ntyStatic, ntyFromExpr, ntyOpt, ntyVoid + ntyAnything, ntyStatic, ntyFromExpr, ntyOptDeprecated, ntyVoid TNimTypeKinds* {.deprecated.} = set[NimTypeKind] NimSymKind* = enum diff --git a/lib/system/hti.nim b/lib/system/hti.nim index 63c1a5448..b77f7ccde 100644 --- a/lib/system/hti.nim +++ b/lib/system/hti.nim @@ -69,7 +69,7 @@ type tyAnythingHidden, tyStaticHidden, tyFromExprHidden, - tyOpt, + tyOptDeprecated, tyVoidHidden TNimNodeKind = enum nkNone, nkSlot, nkList, nkCase From 5a26c3799b77e68c6e9d1c90e0dcb614de25206f Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 12 Jun 2020 11:57:10 +0200 Subject: [PATCH 0212/3768] reorder.nim: fixed typos --- compiler/reorder.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/reorder.nim b/compiler/reorder.nim index 5e380a199..28e12ca2a 100644 --- a/compiler/reorder.nim +++ b/compiler/reorder.nim @@ -290,13 +290,13 @@ proc hasAccQuoted(n: PNode): bool = if hasAccQuoted(a): return true -const extandedProcDefs = procDefs + {nkMacroDef, nkTemplateDef} +const extendedProcDefs = procDefs + {nkMacroDef, nkTemplateDef} proc hasAccQuotedDef(n: PNode): bool = # Checks if the node is a function, macro, template ... # with a quoted name or if it contains one case n.kind - of extandedProcDefs: + of extendedProcDefs: result = n[0].hasAccQuoted of nkStmtList, nkStmtListExpr, nkWhenStmt, nkElifBranch, nkElse, nkStaticStmt: for a in n: @@ -316,7 +316,7 @@ proc hasBody(n: PNode): bool = case n.kind of nkCommand, nkCall: result = true - of extandedProcDefs: + of extendedProcDefs: result = n[^1].kind == nkStmtList of nkStmtList, nkStmtListExpr, nkWhenStmt, nkElifBranch, nkElse, nkStaticStmt: for a in n: From d1498230198888c1b7699ed0b1de12796fd30a7d Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 12 Jun 2020 21:19:23 -0700 Subject: [PATCH 0213/3768] fix #13166 tioselectors flaky test on freebsd+OSX (#14634) --- testament/important_packages.nim | 3 +- tests/async/tioselectors.nim | 9 +++--- tests/m14634.nim | 48 ++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 5 deletions(-) create mode 100644 tests/m14634.nim diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 19e11e322..5a28fa3dd 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -40,7 +40,8 @@ pkg1 "delaunay" pkg1 "docopt" pkg1 "easygl", true, "nim c -o:egl -r src/easygl.nim", "https://github.com/jackmott/easygl" pkg1 "elvis" -pkg1 "fidget", true, "nim c -d:release -r tests/runNative.nim" +# pkg1 "fidget", true, "nim c -d:release -r tests/runNative.nim" + # Error: cannot open 'tests/runNative.nim' pkg1 "fragments", false, "nim c -r fragments/dsl.nim" pkg1 "gara" pkg1 "ggplotnim", true, "nim c -d:noCairo -r -d:nimWorkaround14447 tests/tests.nim" diff --git a/tests/async/tioselectors.nim b/tests/async/tioselectors.nim index c20df29e5..f7487525a 100644 --- a/tests/async/tioselectors.nim +++ b/tests/async/tioselectors.nim @@ -145,14 +145,15 @@ when not defined(windows): proc timer_notification_test(): bool = var selector = newSelector[int]() var timer = selector.registerTimer(100, false, 0) - var rc1 = selector.select(140) - var rc2 = selector.select(140) + var rc1 = selector.select(10000) + var rc2 = selector.select(10000) + # if this flakes, see tests/m14634.nim assert len(rc1) == 1 and len(rc2) == 1, $(len(rc1), len(rc2)) selector.unregister(timer) discard selector.select(0) selector.registerTimer(100, true, 0) - var rc4 = selector.select(120) - var rc5 = selector.select(120) + var rc4 = selector.select(10000) + var rc5 = selector.select(1000) # this will be an actual wait, keep it small assert len(rc4) == 1 and len(rc5) == 0, $(len(rc4), len(rc5)) assert(selector.isEmpty()) selector.close() diff --git a/tests/m14634.nim b/tests/m14634.nim new file mode 100644 index 000000000..56a3d9034 --- /dev/null +++ b/tests/m14634.nim @@ -0,0 +1,48 @@ +#[ +Tool to investigate underlying reasons for https://github.com/nim-lang/Nim/pull/14634 +nim r --threads:on -d:threadsafe tests/m14634.nim +]# + +when not defined(windows): + import std/selectors + + type TestData = object + s1, s2, s3: int + + proc timerNotificationTestImpl(data: var TestData) = + var selector = newSelector[int]() + let t0 = 5 + var timer = selector.registerTimer(t0, false, 0) + let t = 2000 + # values too close to `t0` cause the test to be flaky in CI on OSX+freebsd + # When running locally, t0=100, t=98 will succeed some of the time which indicates + # there is some lag involved. Note that the higher `t-t0` is, the less times + # the test fails. + var rc1 = selector.select(t) + var rc2 = selector.select(t) + assert len(rc1) <= 1 and len(rc2) <= 1 + data.s1 += ord(len(rc1) == 1) + data.s2 += ord(len(rc2) == 1) + selector.unregister(timer) + discard selector.select(0) + selector.registerTimer(t0, true, 0) + # same comment as above + var rc4 = selector.select(t) + let t2 = 100 + # this can't be too large as it'll actually wait that long: + # timer_notification_test.n * t2 + var rc5 = selector.select(t2) + assert len(rc4) + len(rc5) <= 1 + data.s3 += ord(len(rc4) + len(rc5) == 1) + assert(selector.isEmpty()) + selector.close() + + proc timerNotificationTest() = + var data: TestData + let n = 10 + for i in 0.. Date: Sat, 13 Jun 2020 11:44:01 +0100 Subject: [PATCH 0214/3768] Clarify imported exceptions note in manual --- doc/manual.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual.rst b/doc/manual.rst index 451359dca..6eec8bef9 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -4354,7 +4354,7 @@ caught by reference. Example: fn() **Note:** `getCurrentException()` and `getCurrentExceptionMsg()` are not available -for imported exceptions. One needs to use the `except ImportedException as x:` syntax +for imported exceptions from C++. One needs to use the `except ImportedException as x:` syntax and rely on functionality of the `x` object to get exception details. From 0fc5d3f13beb13b4bd5a6174a2f2b00d14d862fb Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 14 Jun 2020 02:11:26 -0700 Subject: [PATCH 0215/3768] fix #14655 setLen(seq) now zeros memory (#14656) * simplify sysstr.nim * fix #14655 --- lib/system/sysstr.nim | 29 +++++++++++++++-------------- tests/collections/tseq.nim | 9 ++++++++- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim index dbf9bcf06..06e605a7b 100644 --- a/lib/system/sysstr.nim +++ b/lib/system/sysstr.nim @@ -15,6 +15,13 @@ # we don't use refcounts because that's a behaviour # the programmer may not want + +proc dataPointer(a: PGenericSeq, elemAlign: int): pointer = + cast[pointer](cast[ByteAddress](a) +% align(GenericSeqSize, elemAlign)) + +proc dataPointer(a: PGenericSeq, elemAlign, elemSize, index: int): pointer = + cast[pointer](cast[ByteAddress](a) +% align(GenericSeqSize, elemAlign) +% (index*%elemSize)) + proc resize(old: int): int {.inline.} = if old <= 0: result = 4 elif old < 65536: result = old * 2 @@ -267,9 +274,6 @@ proc incrSeqV2(seq: PGenericSeq, elemSize, elemAlign: int): PGenericSeq {.compil result = cast[PGenericSeq](growObj(result, align(GenericSeqSize, elemAlign) + elemSize * r)) result.reserved = r -template `+!`(p: pointer, s: int): pointer = - cast[pointer](cast[int](p) +% s) - proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerproc.} = if s == nil: result = cast[PGenericSeq](newSeq(typ, 1)) @@ -281,7 +285,7 @@ proc incrSeqV3(s: PGenericSeq, typ: PNimType): PGenericSeq {.compilerproc.} = when defined(nimIncrSeqV3): result = cast[PGenericSeq](newSeq(typ, r)) result.len = s.len - copyMem(result +! align(GenericSeqSize, typ.base.align), s +! align(GenericSeqSize, typ.base.align), s.len * typ.base.size) + copyMem(dataPointer(result, typ.base.align), dataPointer(s, typ.base.align), s.len * typ.base.size) # since we steal the content from 's', it's crucial to set s's len to 0. s.len = 0 else: @@ -303,7 +307,7 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, elemAlign, newLen: int): PGenericS when false: # compileOption("gc", "v2"): for i in newLen..result.len-1: let len0 = gch.tempStack.len - forAllChildrenAux(cast[pointer](cast[ByteAddress](result) +% align(GenericSeqSize, elemAlign) +% (i*%elemSize)), + forAllChildrenAux(dataPointer(result, elemAlign, elemSize, i), extGetCellType(result).base, waPush) let len1 = gch.tempStack.len for i in len0 ..< len1: @@ -312,8 +316,7 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, elemAlign, newLen: int): PGenericS else: if ntfNoRefs notin extGetCellType(result).base.flags: for i in newLen..result.len-1: - forAllChildrenAux(cast[pointer](cast[ByteAddress](result) +% - align(GenericSeqSize, elemAlign) +% (i*%elemSize)), + forAllChildrenAux(dataPointer(result, elemAlign, elemSize, i), extGetCellType(result).base, waZctDecRef) # XXX: zeroing out the memory can still result in crashes if a wiped-out @@ -322,8 +325,7 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, elemAlign, newLen: int): PGenericS # presence of user defined destructors, the user will expect the cell to be # "destroyed" thus creating the same problem. We can destroy the cell in the # finalizer of the sequence, but this makes destruction non-deterministic. - zeroMem(cast[pointer](cast[ByteAddress](result) +% align(GenericSeqSize, elemAlign) +% - (newLen*%elemSize)), (result.len-%newLen) *% elemSize) + zeroMem(dataPointer(result, elemAlign, elemSize, newLen), (result.len-%newLen) *% elemSize) result.len = newLen proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {. @@ -338,7 +340,7 @@ proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {. if s.space < newLen: let r = max(resize(s.space), newLen) result = cast[PGenericSeq](newSeq(typ, r)) - copyMem(result +! align(GenericSeqSize, elemAlign), s +! align(GenericSeqSize, elemAlign), s.len * elemSize) + copyMem(dataPointer(result, elemAlign), dataPointer(s, elemAlign), s.len * elemSize) # since we steal the content from 's', it's crucial to set s's len to 0. s.len = 0 elif newLen < s.len: @@ -349,8 +351,7 @@ proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {. not defined(gcRegions): if ntfNoRefs notin typ.base.flags: for i in newLen..result.len-1: - forAllChildrenAux(cast[pointer](cast[ByteAddress](result) +% - align(GenericSeqSize, elemAlign) +% (i*%elemSize)), + forAllChildrenAux(dataPointer(result, elemAlign, elemSize, i), extGetCellType(result).base, waZctDecRef) # XXX: zeroing out the memory can still result in crashes if a wiped-out @@ -359,10 +360,10 @@ proc setLengthSeqV2(s: PGenericSeq, typ: PNimType, newLen: int): PGenericSeq {. # presence of user defined destructors, the user will expect the cell to be # "destroyed" thus creating the same problem. We can destroy the cell in the # finalizer of the sequence, but this makes destruction non-deterministic. - zeroMem(cast[pointer](cast[ByteAddress](result) +% align(GenericSeqSize, elemAlign) +% - (newLen*%elemSize)), (result.len-%newLen) *% elemSize) + zeroMem(dataPointer(result, elemAlign, elemSize, newLen), (result.len-%newLen) *% elemSize) else: result = s + zeroMem(dataPointer(result, elemAlign, elemSize, result.len), (newLen-%result.len) *% elemSize) result.len = newLen else: result = setLengthSeq(s, typ.base.size, newLen) diff --git a/tests/collections/tseq.nim b/tests/collections/tseq.nim index ac68e5636..263a571bf 100644 --- a/tests/collections/tseq.nim +++ b/tests/collections/tseq.nim @@ -206,4 +206,11 @@ block ttoseq: stdout.write("\n") block tseqmapitchain: - doAssert @[101, 102] == [1, 2].mapIt(func (x: int): int = it + x).mapIt(it(100)) \ No newline at end of file + doAssert @[101, 102] == [1, 2].mapIt(func (x: int): int = it + x).mapIt(it(100)) + + +for i in 0..100: + # fix #14655 + var test = newSeqOfCap[uint32](1) + test.setLen(1) + doAssert test[0] == 0, $(test[0], i) From 4e3edf58360d1c4da79d50acdf5cf98b97ce585d Mon Sep 17 00:00:00 2001 From: Andy Davidoff Date: Sun, 14 Jun 2020 05:12:08 -0400 Subject: [PATCH 0216/3768] add arc and orc to gc list (#14653) --- doc/advopt.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/advopt.txt b/doc/advopt.txt index a668be5c0..e522973cd 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -113,7 +113,7 @@ Advanced options: --skipUserCfg:on|off do not read the user's configuration file --skipParentCfg:on|off do not read the parent dirs' configuration files --skipProjCfg:on|off do not read the project's configuration file - --gc:refc|markAndSweep|boehm|go|none|regions + --gc:refc|arc|orc|markAndSweep|boehm|go|none|regions select the GC to use; default is 'refc' --exceptions:setjmp|cpp|goto|quirky select the exception handling implementation From d749c8cd87508871dc28b528f57d26b1004007b3 Mon Sep 17 00:00:00 2001 From: alaviss Date: Sun, 14 Jun 2020 04:22:55 -0500 Subject: [PATCH 0217/3768] compiler/commands: make gitHash settable at compile-time. (#14654) This is useful for building nightlies, since we will be building from a generated source archive and git metadata is lost there. --- compiler/commands.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/commands.nim b/compiler/commands.nim index 2d8418139..c945a633a 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -98,7 +98,7 @@ proc writeVersionInfo(conf: ConfigRef; pass: TCmdLinePass) = CPU[conf.target.hostCPU].name, CompileDate]), {msgStdout}) - const gitHash = gorge("git log -n 1 --format=%H").strip + const gitHash {.strdefine.} = gorge("git log -n 1 --format=%H").strip when gitHash.len == 40: msgWriteln(conf, "git hash: " & gitHash, {msgStdout}) From 5a22d6b57bec6eca3d123a7d911f32b58b410cf8 Mon Sep 17 00:00:00 2001 From: jcosborn Date: Mon, 15 Jun 2020 01:40:12 -0500 Subject: [PATCH 0218/3768] fix codegen bug due to changing existing symbol declaration in template (#14666) --- compiler/semtempl.nim | 2 +- tests/template/template_various.nim | 106 ++++++++++++++++++++++++++++ 2 files changed, 107 insertions(+), 1 deletion(-) diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index dedea2e99..c9cd82833 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -229,7 +229,7 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) = if s != nil and s.owner == c.owner and sfGenSym in s.flags: onUse(n.info, s) replaceIdentBySym(c.c, n, newSymNode(s, n.info)) - elif not (n.kind == nkSym and sfGenSym in n.sym.flags): + elif n.kind != nkSym: let local = newGenSym(k, ident, c) addPrelimDecl(c.c, local) styleCheckDef(c.c.config, n.info, local) diff --git a/tests/template/template_various.nim b/tests/template/template_various.nim index ac7e91fa2..e8202a2e0 100644 --- a/tests/template/template_various.nim +++ b/tests/template/template_various.nim @@ -10,6 +10,28 @@ bar7 4true 132 20 +1 +-1 +4 +11 +26 +57 +-1-1-1 + 4 +4 + 4 + 11 +11 + 4 + 11 + 26 +26 + 4 + 11 + 26 + 57 +57 +-1-1-1 ''' """ @@ -245,3 +267,87 @@ template parse9(body: untyped): untyped = parse9: echo val9("1") + + +block gensym1: + template x: untyped = -1 + template t1() = + template x: untyped {.gensym.} = 1 + echo x() # 1 + template t2() = + template x: untyped = 1 # defaults to {.inject.} + echo x() # -1 injected x not available during template definition + t1() + t2() + +block gensym2: + let x,y,z = -1 + template `!`(xx,yy: typed): untyped = + template x: untyped {.gensym.} = xx + template y: untyped {.gensym.} = yy + let z = x + x + y + z + var + a = 1 + b = 2 + c = 3 + d = 4 + e = 5 + echo a ! b + echo a ! b ! c + echo a ! b ! c ! d + echo a ! b ! c ! d ! e + echo x,y,z + +block gensym3: + macro liftStmts(body: untyped): auto = + # convert + # template x: untyped {.gensym.} = + # let z = a + a + b + # echo z + # z + # to + # let z = a + a + b + # echo z + # template x: untyped {.gensym.} = + # z + #echo body.repr + body.expectKind nnkStmtList + result = newNimNode nnkStmtList + for s in body: + s.expectKind nnkTemplateDef + var sle = s[6] + while sle.kind == nnkStmtList: + doAssert(sle.len==1) + sle = sle[0] + if sle.kind == nnkStmtListExpr: + let n = sle.len + for i in 0..(n-2): + result.add sle[i] + var td = newNimNode nnkTemplateDef + for i in 0..5: + td.add s[i] + td.add sle[n-1] + result.add td + else: + result.add s + #echo result.repr + let x,y,z = -1 + template `!`(xx,yy: typed): untyped = + liftStmts: + template x: untyped {.gensym.} = xx + template y: untyped {.gensym.} = yy + let z = x + x + y + echo " ", z + z + var + a = 1 + b = 2 + c = 3 + d = 4 + e = 5 + echo a ! b + echo a ! b ! c + echo a ! b ! c ! d + echo a ! b ! c ! d ! e + echo x,y,z From a056b4c3f135120bdd948c32273fd902273c2773 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Mon, 15 Jun 2020 03:55:24 -0300 Subject: [PATCH 0219/3768] Change 'Future Directions' to link memory management documentation (#14664) --- doc/manual.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/manual.rst b/doc/manual.rst index 6eec8bef9..75cc507a4 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -7449,9 +7449,9 @@ be used: deepCopy(perThread, someGlobal) -Future directions: +See also: -- A shared GC'ed heap might be provided. +- `Shared heap memory management. `_. Threadvar pragma From 571bc011eaa9eb9214291d34ca008ab41ce7ebc1 Mon Sep 17 00:00:00 2001 From: Ico Doornekamp Date: Mon, 15 Jun 2020 08:58:11 +0200 Subject: [PATCH 0220/3768] Added --benchmarkVM to times.cpuTime() documentation (#14663) --- lib/pure/times.nim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index 1fff3d99b..01eb16c23 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -2619,6 +2619,8 @@ when not defined(js): fib.add(fib[^1] + fib[^2]) echo "CPU time [s] ", cpuTime() - t0 echo "Fib is [s] ", fib + ## When the flag `--benchmarkVM` is passed to the compiler, this proc is + ## also available at compile time when defined(posix) and not defined(osx) and declared(CLOCK_THREAD_CPUTIME_ID): # 'clocksPerSec' is a compile-time constant, possibly a # rather awful one, so use clock_gettime instead From 3cbf59336d6b9838dbf534f8e55fdfc8e7ab6954 Mon Sep 17 00:00:00 2001 From: solo989 Date: Mon, 15 Jun 2020 00:15:17 -0700 Subject: [PATCH 0221/3768] Update newLit set[T] to work when set is empty. (#14662) * Update macros.nim * Update lib/core/macros.nim Co-authored-by: Clyybber Co-authored-by: Clyybber --- lib/core/macros.nim | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 3590a162b..27ff063a7 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -793,6 +793,10 @@ proc newLit*[T](s: set[T]): NimNode {.compileTime.} = result = nnkCurly.newTree for x in s: result.add newLit(x) + if result.len == 0: + # add type cast for empty set + var typ = getTypeInst(typeof(s))[1] + result = newCall(typ,result) proc newLit*(arg: tuple): NimNode {.compileTime.} = result = nnkPar.newTree From bf604c6829f87a551dc3b5ad836679be4ab53789 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 15 Jun 2020 01:57:34 -0700 Subject: [PATCH 0222/3768] normalizeExe (#14668) --- changelog.md | 1 + lib/pure/os.nim | 16 ++++++++++++++-- testament/testament.nim | 11 +++-------- tests/stdlib/tos.nim | 10 ++++++++++ 4 files changed, 28 insertions(+), 10 deletions(-) diff --git a/changelog.md b/changelog.md index 623b3f6bc..b9fb48117 100644 --- a/changelog.md +++ b/changelog.md @@ -112,6 +112,7 @@ - new proc `heapqueue.find[T](heap: HeapQueue[T], x: T): int` to get index of element ``x``. - Add `rstgen.rstToLatex` convenience proc for `renderRstToOut` and `initRstGenerator` with `outLatex` output. +- Add `os.normalizeExe`, eg: `koch` => `./koch`. ## Language changes diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 97e414d44..79cd27e10 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -1407,6 +1407,18 @@ proc absolutePath*(path: string, root = getCurrentDir()): string = proc absolutePathInternal(path: string): string = absolutePath(path, getCurrentDir()) +proc normalizeExe*(file: var string) {.since: (1, 3, 5).} = + ## on posix, prepends `./` if `file` doesn't contain `/` and is not `"", ".", ".."`. + runnableExamples: + import sugar + when defined(posix): + doAssert "foo".dup(normalizeExe) == "./foo" + doAssert "foo/../bar".dup(normalizeExe) == "foo/../bar" + doAssert "".dup(normalizeExe) == "" + when defined(posix): + if file.len > 0 and DirSep notin file and file != "." and file != "..": + file = "./" & file + proc normalizePath*(path: var string) {.rtl, extern: "nos$1", tags: [].} = ## Normalize a path. ## @@ -1420,8 +1432,8 @@ proc normalizePath*(path: var string) {.rtl, extern: "nos$1", tags: [].} = ## ## See also: ## * `absolutePath proc <#absolutePath,string>`_ - ## * `normalizedPath proc <#normalizedPath,string>`_ for a version which returns - ## a new string + ## * `normalizedPath proc <#normalizedPath,string>`_ for outplace version + ## * `normalizeExe proc <#normalizeExe,string>`_ runnableExamples: when defined(posix): var a = "a///b//..//c///d" diff --git a/testament/testament.nim b/testament/testament.nim index f83b244c2..69b7e322e 100644 --- a/testament/testament.nim +++ b/testament/testament.nim @@ -13,8 +13,8 @@ import strutils, pegs, os, osproc, streams, json, backend, parseopt, specs, htmlgen, browsers, terminal, algorithm, times, md5, sequtils, azure - -include compiler/nodejs +from std/sugar import dup +import compiler/nodejs var useColors = true var backendLogging = true @@ -450,12 +450,7 @@ proc testSpecHelper(r: var TResults, test: TTest, expected: TSpec, exeCmd = nodejs args = concat(@[exeFile], args) else: - if defined(posix) and not exeFile.contains('/'): - # "security" in Posix is actually just a euphemism - # for "unproductive arbitrary shit" - exeCmd = "./" & exeFile - else: - exeCmd = exeFile + exeCmd = exeFile.dup(normalizeExe) if expected.useValgrind: args = @["--error-exitcode=1"] & exeCmd & args exeCmd = "valgrind" diff --git a/tests/stdlib/tos.nim b/tests/stdlib/tos.nim index a440d86d8..814ff103d 100644 --- a/tests/stdlib/tos.nim +++ b/tests/stdlib/tos.nim @@ -510,3 +510,13 @@ block: # isValidFilename doAssert isValidFilename("ux.bat") doAssert isValidFilename("nim.nim") doAssert isValidFilename("foo.log") + +import sugar + +block: # normalizeExe + doAssert "".dup(normalizeExe) == "" + when defined(posix): + doAssert "foo".dup(normalizeExe) == "./foo" + doAssert "foo/../bar".dup(normalizeExe) == "foo/../bar" + when defined(windows): + doAssert "foo".dup(normalizeExe) == "foo" From d51beb7b20c7670a17769b30e721fe67761f98e6 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 15 Jun 2020 04:22:43 -0700 Subject: [PATCH 0223/3768] make `fromJson/toJson` work with `array[range, typ]`, + 1 bugfix (#14669) * make toJson more robust * properly handle array --- lib/std/jsonutils.nim | 32 ++++++++++++++++++++++++-------- tests/stdlib/tjsonutils.nim | 26 ++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 8 deletions(-) diff --git a/lib/std/jsonutils.nim b/lib/std/jsonutils.nim index bfa600fa9..be3d7e7c8 100644 --- a/lib/std/jsonutils.nim +++ b/lib/std/jsonutils.nim @@ -2,9 +2,17 @@ This module implements a hookable (de)serialization for arbitrary types. Design goal: avoid importing modules where a custom serialization is needed; see strtabs.fromJsonHook,toJsonHook for an example. - ]## +runnableExamples: + import std/[strtabs,json] + type Foo = ref object + t: bool + z1: int8 + let a = (1.5'f32, (b: "b2", a: "a2"), 'x', @[Foo(t: true, z1: -3), nil], [{"name": "John"}.newStringTable]) + let j = a.toJson + doAssert j.jsonTo(type(a)).toJson == j + import std/[json,tables,strutils] #[ @@ -12,10 +20,14 @@ xxx use toJsonHook,fromJsonHook for Table|OrderedTable add Options support also using toJsonHook,fromJsonHook and remove `json=>options` dependency -future direction: -add a way to customize serialization, for eg allowing missing -or extra fields in JsonNode, field renaming, and a way to handle cyclic references -using a cache of already visited addresses. +Future directions: +add a way to customize serialization, for eg: +* allowing missing or extra fields in JsonNode +* field renaming +* allow serializing `enum` and `char` as `string` instead of `int` + (enum is more compact/efficient, and robust to enum renamings, but string + is more human readable) +* handle cyclic references, using a cache of already visited addresses ]# proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".} @@ -65,8 +77,10 @@ proc fromJson*[T](a: var T, b: JsonNode) = fromJson(a[], b) elif T is array: checkJson a.len == b.len, $(a.len, b.len, $T) - for i, val in b.getElems: - fromJson(a[i], val) + var i = 0 + for ai in mitems(a): + fromJson(ai, b[i]) + i.inc elif T is seq: a.setLen b.len for i, val in b.getElems: @@ -114,12 +128,14 @@ proc toJson*[T](a: T): JsonNode = result = newJArray() for v in a.fields: result.add toJson(v) elif T is ref | ptr: - if a == nil: result = newJNull() + if system.`==`(a, nil): result = newJNull() else: result = toJson(a[]) elif T is array | seq: result = newJArray() for ai in a: result.add toJson(ai) elif T is pointer: result = toJson(cast[int](a)) + # edge case: `a == nil` could've also led to `newJNull()`, but this results + # in simpler code for `toJson` and `fromJson`. elif T is distinct: result = toJson(a.distinctBase) elif T is bool: result = %(a) elif T is Ordinal: result = %(a.ord) diff --git a/tests/stdlib/tjsonutils.nim b/tests/stdlib/tjsonutils.nim index 01c6aa05a..fca980dc9 100644 --- a/tests/stdlib/tjsonutils.nim +++ b/tests/stdlib/tjsonutils.nim @@ -13,12 +13,26 @@ proc testRoundtrip[T](t: T, expected: string) = import tables import strtabs +type Foo = ref object + id: int + +proc `==`(a, b: Foo): bool = + a.id == b.id + template fn() = block: # toJson, jsonTo type Foo = distinct float testRoundtrip('x', """120""") when not defined(js): testRoundtrip(cast[pointer](12345)): """12345""" + when nimvm: + discard + # bugs: + # Error: unhandled exception: 'intVal' is not accessible using discriminant 'kind' of type 'TNode' [ + # Error: VM does not support 'cast' from tyNil to tyPointer + else: + testRoundtrip(pointer(nil)): """0""" + testRoundtrip(cast[pointer](nil)): """0""" # causes workaround in `fromJson` potentially related to # https://github.com/nim-lang/Nim/issues/12282 @@ -40,5 +54,17 @@ template fn() = testRoundtrip(a): """[1.1,"fo",120,[10,11],[true,false],[{"mode":"modeCaseSensitive","table":{"y":"Y","z":"Z"}},{"mode":"modeCaseSensitive","table":{}}],[0,3],-4,{"foo":0.5,"bar":{"a1":"abc"},"bar2":null}]""" + block: + # edge case when user defined `==` doesn't handle `nil` well, eg: + # https://github.com/nim-lang/nimble/blob/63695f490728e3935692c29f3d71944d83bb1e83/src/nimblepkg/version.nim#L105 + testRoundtrip(@[Foo(id: 10), nil]): """[{"id":10},null]""" + + block: + type Foo = enum f1, f2, f3, f4, f5 + type Bar = enum b1, b2, b3, b4 + let a = [f2: b2, f3: b3, f4: b4] + doAssert b2.ord == 1 # explains the `1` + testRoundtrip(a): """[1,2,3]""" + static: fn() fn() From 49033eb5314d7f07e5ecc088dd0933010bb581d8 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 15 Jun 2020 04:27:33 -0700 Subject: [PATCH 0224/3768] make tests/stdlib tests joinable (#14626) * make tests/stdlib tests joinable * fixup --- testament/categories.nim | 33 +++++++++++++++++++------------ tests/stdlib/tcgi.nim | 6 ++++++ tests/stdlib/tfdleak.nim | 1 + tests/stdlib/tfdleak_multiple.nim | 4 ++++ tests/stdlib/tmarshal.nim | 5 +++++ tests/stdlib/tnetbind.nim | 10 ++++++++++ tests/stdlib/tosproc.nim | 9 ++++++++- tests/stdlib/trst.nim | 6 ++++++ tests/stdlib/tstrutil.nim | 10 ++++------ 9 files changed, 64 insertions(+), 20 deletions(-) diff --git a/testament/categories.nim b/testament/categories.nim index 88f168608..4e83fa1a9 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -35,7 +35,6 @@ const "niminaction", "threads", "untestable", - "stdlib", "testdata", "nimcache", "coroutines", @@ -573,6 +572,9 @@ proc isJoinableSpec(spec: TSpec): bool = spec.outputCheck != ocSubstr and spec.ccodeCheck.len == 0 and (spec.targets == {} or spec.targets == {targetC}) + if result: + if spec.file.readFile.contains "when isMainModule": + result = false proc norm(s: var string) = while true: @@ -585,6 +587,13 @@ proc quoted(a: string): string = # todo: consider moving to system.nim result.addQuoted(a) +proc normalizeExe(file: string): string = + # xxx common pattern, should be exposed in std/os, even if simple (error prone) + when defined(posix): + if file.len == 0: "" + elif DirSep in file: file else: "./" & file + else: file + proc runJoinedTest(r: var TResults, cat: Category, testsDir: string) = ## returns a list of tests that have problems var specs: seq[TSpec] = @[] @@ -620,26 +629,24 @@ proc runJoinedTest(r: var TResults, cat: Category, testsDir: string) = for i, runSpec in specs: let file = runSpec.file - let file2 = outDir / ("megatest_" & $i & ".nim") + let file2 = outDir / ("megatest_$1.nim" % $i) # `include` didn't work with `trecmod2.nim`, so using `import` - let code = "echo \"" & marker & "\", " & quoted(file) & "\n" + let code = "echo \"$1\", $2\n" % [marker, quoted(file)] createDir(file2.parentDir) writeFile(file2, code) - megatest.add "import " & quoted(file2) & "\n" - megatest.add "import " & quoted(file) & "\n" + megatest.add "import $1\nimport $2\n" % [quoted(file2), quoted(file)] - writeFile("megatest.nim", megatest) - - let args = ["c", "--nimCache:" & outDir, "-d:testing", "--listCmd", - "--listFullPaths:off", "--excessiveStackTrace:off", "megatest.nim"] + let megatestFile = testsDir / "megatest.nim" # so it uses testsDir / "config.nims" + writeFile(megatestFile, megatest) + let root = getCurrentDir() + let args = ["c", "--nimCache:" & outDir, "-d:testing", "--listCmd", "--path:" & root, megatestFile] var (cmdLine, buf, exitCode) = execCmdEx2(command = compilerPrefix, args = args, input = "") if exitCode != 0: - echo "$ ", cmdLine - echo buf.string + echo "$ " & cmdLine & "\n" & buf.string quit("megatest compilation failed") - (buf, exitCode) = execCmdEx("./megatest") + (buf, exitCode) = execCmdEx(megatestFile.changeFileExt(ExeExt).normalizeExe) if exitCode != 0: echo buf.string quit("megatest execution failed") @@ -662,7 +669,7 @@ proc runJoinedTest(r: var TResults, cat: Category, testsDir: string) = else: echo "output OK" removeFile("outputGotten.txt") - removeFile("megatest.nim") + removeFile(megatestFile) #testSpec r, makeTest("megatest", options, cat) # --------------------------------------------------------------------------- diff --git a/tests/stdlib/tcgi.nim b/tests/stdlib/tcgi.nim index bc177125e..7df1b077d 100644 --- a/tests/stdlib/tcgi.nim +++ b/tests/stdlib/tcgi.nim @@ -1,3 +1,9 @@ +discard """ + output: ''' +[Suite] Test cgi module +''' +""" + import unittest import cgi, strtabs diff --git a/tests/stdlib/tfdleak.nim b/tests/stdlib/tfdleak.nim index 5931be8c1..79d7ee0d0 100644 --- a/tests/stdlib/tfdleak.nim +++ b/tests/stdlib/tfdleak.nim @@ -2,6 +2,7 @@ discard """ exitcode: 0 output: "" matrix: "; -d:nimInheritHandles" + joinable: false """ import os, osproc, strutils, nativesockets, net, selectors, memfiles, diff --git a/tests/stdlib/tfdleak_multiple.nim b/tests/stdlib/tfdleak_multiple.nim index 75c4c0503..22387607f 100644 --- a/tests/stdlib/tfdleak_multiple.nim +++ b/tests/stdlib/tfdleak_multiple.nim @@ -1,3 +1,7 @@ +discard """ +joinable: false +""" + import os, osproc, strutils const Iterations = 200 diff --git a/tests/stdlib/tmarshal.nim b/tests/stdlib/tmarshal.nim index a6fc6c1a5..118d0ae02 100644 --- a/tests/stdlib/tmarshal.nim +++ b/tests/stdlib/tmarshal.nim @@ -5,8 +5,13 @@ true alpha 100 omega 200 ''' +joinable: false """ +#[ +joinable: false pending https://github.com/nim-lang/Nim/issues/9754 +]# + import marshal template testit(x) = discard $$to[type(x)]($$x) diff --git a/tests/stdlib/tnetbind.nim b/tests/stdlib/tnetbind.nim index b2bcf4b05..734b6c5e7 100644 --- a/tests/stdlib/tnetbind.nim +++ b/tests/stdlib/tnetbind.nim @@ -1,3 +1,13 @@ +discard """ +joinable: false +""" + +#[ +joinable: false +otherwise: +Error: unhandled exception: Address already in use [OSError] +]# + import net ## Test for net.bindAddr diff --git a/tests/stdlib/tosproc.nim b/tests/stdlib/tosproc.nim index b5328c9eb..cb6e260c5 100644 --- a/tests/stdlib/tosproc.nim +++ b/tests/stdlib/tosproc.nim @@ -1,4 +1,11 @@ -# test the osproc module +discard """ +joinable: false +""" + +#[ +joinable: false +because it'd need cleanup up stdout +]# import stdtest/[specialpaths, unittest_light] diff --git a/tests/stdlib/trst.nim b/tests/stdlib/trst.nim index 6b1bd6897..797010a22 100644 --- a/tests/stdlib/trst.nim +++ b/tests/stdlib/trst.nim @@ -1,3 +1,9 @@ +discard """ + output: ''' +[Suite] RST include directive +''' +""" + # tests for rst module import ../../lib/packages/docutils/rstgen diff --git a/tests/stdlib/tstrutil.nim b/tests/stdlib/tstrutil.nim index cb6985a6e..5702b7341 100644 --- a/tests/stdlib/tstrutil.nim +++ b/tests/stdlib/tstrutil.nim @@ -1,6 +1,3 @@ -discard """ - output: "ha/home/a1xyz/usr/bin" -""" # test the new strutils module import @@ -15,7 +12,7 @@ template rejectParse(e) = except ValueError: discard proc testStrip() = - write(stdout, strip(" ha ")) + doAssert strip(" ha ") == "ha" proc testRemoveSuffix = var s = "hello\n\r" @@ -143,8 +140,9 @@ proc main() = testStrip() testRemoveSuffix() testRemovePrefix() - for p in split("/home/a1:xyz:/usr/bin", {':'}): - write(stdout, p) + var ret: seq[string] # or use `toSeq` + for p in split("/home/a1:xyz:/usr/bin", {':'}): ret.add p + doAssert ret == @["/home/a1", "xyz", "/usr/bin"] proc testDelete = var s = "0123456789ABCDEFGH" From faedb14a167497f96b1499305deb5538e579eeb0 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 15 Jun 2020 21:17:24 -0700 Subject: [PATCH 0225/3768] misc cleanups in compiler msgs: use toHumanStr, etc (#14677) --- compiler/msgs.nim | 7 ++++--- compiler/sem.nim | 6 ++---- compiler/semstmts.nim | 3 +-- compiler/semtypes.nim | 5 ++--- 4 files changed, 9 insertions(+), 12 deletions(-) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index e27eedd1c..aecaaf405 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -558,12 +558,13 @@ template fatal*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") = template globalAssert*(conf: ConfigRef; cond: untyped, info: TLineInfo = unknownLineInfo, arg = "") = ## avoids boilerplate if not cond: - const info2 = instantiationInfo(-1, fullPaths = true) var arg2 = "'$1' failed" % [astToStr(cond)] if arg.len > 0: arg2.add "; " & astToStr(arg) & ": " & arg - liMessage(conf, info, errGenerated, arg2, doRaise, info2) + liMessage(conf, info, errGenerated, arg2, doRaise, instLoc()) template globalError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") = + ## `local` means compilation keeps going until errorMax is reached (via `doNothing`), + ## `global` means it stops. liMessage(conf, info, msg, arg, doRaise, instLoc()) template globalError*(conf: ConfigRef; info: TLineInfo, arg: string) = @@ -593,7 +594,7 @@ template internalError*(conf: ConfigRef; errMsg: string) = internalErrorImpl(conf, unknownLineInfo, errMsg, instLoc()) template internalAssert*(conf: ConfigRef, e: bool) = - # xxx merge with globalAssert from PR #14324 + # xxx merge with `globalAssert` if not e: const info2 = instLoc() let arg = info2.toFileLineCol diff --git a/compiler/sem.nim b/compiler/sem.nim index ca5d9c448..a657939a5 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -197,15 +197,13 @@ proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym = suggestDecl(c, n, result) proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym = - proc `$`(kind: TSymKind): string = substr(system.`$`(kind), 2).toLowerAscii - # like newSymS, but considers gensym'ed symbols if n.kind == nkSym: # and sfGenSym in n.sym.flags: result = n.sym if result.kind notin {kind, skTemp}: - localError(c.config, n.info, "cannot use symbol of kind '" & - $result.kind & "' as a '" & $kind & "'") + localError(c.config, n.info, "cannot use symbol of kind '$1' as a '$2'" % + [result.kind.toHumanStr, kind.toHumanStr]) when false: if sfGenSym in result.flags and result.kind notin {skTemplate, skMacro, skParam}: # declarative context, so produce a fresh gensym: diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index a4b4c9362..6ffaa9dda 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -514,8 +514,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = if typ.kind in tyUserTypeClasses and typ.isResolvedUserTypeClass: typ = typ.lastSon if hasEmpty(typ): - localError(c.config, def.info, errCannotInferTypeOfTheLiteral % - ($typ.kind).substr(2).toLowerAscii) + localError(c.config, def.info, errCannotInferTypeOfTheLiteral % typ.kind.toHumanStr) elif typ.kind == tyProc and tfUnresolved in typ.flags: localError(c.config, def.info, errProcHasNoConcreteType % def.renderTree) when false: diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 313328385..eba8dd397 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -905,8 +905,7 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType = if t.kind == tyTypeDesc and tfUnresolved notin t.flags: t = t.base if t.kind == tyVoid: - const kindToStr: array[tyPtr..tyRef, string] = ["ptr", "ref"] - localError(c.config, n.info, "type '$1 void' is not allowed" % kindToStr[kind]) + localError(c.config, n.info, "type '$1 void' is not allowed" % kind.toHumanStr) result = newOrPrevType(kind, prev, c) var isNilable = false var wrapperKind = tyNone @@ -1400,7 +1399,7 @@ proc semObjectTypeForInheritedGenericInst(c: PContext, n: PNode, t: PType) = proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = if s.typ == nil: localError(c.config, n.info, "cannot instantiate the '$1' $2" % - [s.name.s, ($s.kind).substr(2).toLowerAscii]) + [s.name.s, s.kind.toHumanStr]) return newOrPrevType(tyError, prev, c) var t = s.typ From de5cde473a2809ac4a2940ec2704d575307521b4 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 15 Jun 2020 23:49:57 -0700 Subject: [PATCH 0226/3768] `hintMsgOrigin` now works in VM code (#14678) * `hintMsgOrigin` now works in VM code * remove a simplification for now --- compiler/msgs.nim | 6 +++--- compiler/vm.nim | 18 ++++++++---------- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index aecaaf405..a5953237e 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -13,7 +13,7 @@ import import std/private/miscdollars import strutils2 -type InstantiationInfo = typeof(instantiationInfo()) +type InstantiationInfo* = typeof(instantiationInfo()) template instLoc(): InstantiationInfo = instantiationInfo(-2, fullPaths = true) template flushDot(conf, stdorr) = @@ -385,7 +385,7 @@ proc getMessageStr(msg: TMsgKind, arg: string): string = result = msgKindToString(msg) % [arg] type - TErrorHandling = enum doNothing, doAbort, doRaise + TErrorHandling* = enum doNothing, doAbort, doRaise proc log*(s: string) = var f: File @@ -485,7 +485,7 @@ proc formatMsg*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string): s else: ErrorTitle conf.toFileLineCol(info) & " " & title & getMessageStr(msg, arg) -proc liMessage(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string, +proc liMessage*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string, eh: TErrorHandling, info2: InstantiationInfo, isRaw = false) {.noinline.} = var title: string diff --git a/compiler/vm.nim b/compiler/vm.nim index c15f9e268..9b76d7986 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -72,20 +72,18 @@ proc stackTraceAux(c: PCtx; x: PStackFrame; pc: int; recursionLimit=100) = msgWriteln(c.config, s) proc stackTraceImpl(c: PCtx, tos: PStackFrame, pc: int, - msg: string, lineInfo: TLineInfo) = + msg: string, lineInfo: TLineInfo, infoOrigin: InstantiationInfo) {.noinline.} = + # noinline to avoid code bloat msgWriteln(c.config, "stack trace: (most recent call last)") stackTraceAux(c, tos, pc) - # XXX test if we want 'globalError' for every mode - if c.mode == emRepl: globalError(c.config, lineInfo, msg) - else: localError(c.config, lineInfo, msg) + let action = if c.mode == emRepl: doRaise else: doNothing + # XXX test if we want 'globalError' for every mode + let lineInfo = if lineInfo == TLineInfo.default: c.debug[pc] else: lineInfo + liMessage(c.config, lineInfo, errGenerated, msg, action, infoOrigin) template stackTrace(c: PCtx, tos: PStackFrame, pc: int, - msg: string, lineInfo: TLineInfo) = - stackTraceImpl(c, tos, pc, msg, lineInfo) - return - -template stackTrace(c: PCtx, tos: PStackFrame, pc: int, msg: string) = - stackTraceImpl(c, tos, pc, msg, c.debug[pc]) + msg: string, lineInfo: TLineInfo = TLineInfo.default) = + stackTraceImpl(c, tos, pc, msg, lineInfo, instantiationInfo(-2, fullPaths = true)) return proc bailOut(c: PCtx; tos: PStackFrame) = From 7b12f13946e833d85ddfe23fafee0c808b27bafb Mon Sep 17 00:00:00 2001 From: Euan Date: Tue, 16 Jun 2020 07:50:57 +0100 Subject: [PATCH 0227/3768] Use cc on OpenBSD and link to libm when building result (#14672) --- tools/detect/detect.nim | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/tools/detect/detect.nim b/tools/detect/detect.nim index 720e24975..a10794555 100644 --- a/tools/detect/detect.nim +++ b/tools/detect/detect.nim @@ -14,10 +14,18 @@ # compilation. import os, strutils -const - cc = "gcc -o $# $#.c" - cpp = "gcc -E -o $#.i $#.c" +when defined(openbsd): + const + cc = "cc -o $# $#.c" + cpp = "cc -E -o $#.i $#.c" + ccLinkMath = "cc -lm -o $# $#.c" + cppLinkMath = "cc -lm -E -o $#.i $#.c" +else: + const + cc = "gcc -o $# $#.c" + cpp = "gcc -E -o $#.i $#.c" +const cfile = """ /* Generated by detect.nim */ #define _GNU_SOURCE @@ -89,8 +97,11 @@ proc main = if open(f, "other_consts.nim", fmWrite): f.write(nimfile % [other]) close(f) - if not myExec(cc % [gen.addFileExt(ExeExt), gen]): quit(1) - if not myExec(cpp % [pre.addFileExt(ExeExt), pre]): quit(1) + + let cCompile = when defined(openbsd): ccLinkMath else: cc + let cppCompile = when defined(openbsd): cppLinkMath else: cpp + if not myExec(cCompile % [gen.addFileExt(ExeExt), gen]): quit(1) + if not myExec(cppCompile % [pre.addFileExt(ExeExt), pre]): quit(1) when defined(windows): if not myExec(gen.addFileExt(ExeExt)): quit(1) else: From 45cac4afda2272182ea1eb7572493d6c71e2da4e Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 15 Jun 2020 23:56:12 -0700 Subject: [PATCH 0228/3768] fix #14179, fix #14142, make CI 1.4x faster (2x faster locally) (#14658) * fix #14142: no more clash with: import os + use of existsDir/dirExists/existsFile/fileExists/findExe in config.nims * remove a comment * Revert "fixes the regression #12860 caused; hotfix" This reverts commit 3d2459bdc0b6d6236a2cd9209ed81c965ee411a5. * Revert "Undefine `paramCount` & `paramStr` in nimscript.nim for *.nims (#12860)" This reverts commit d38853c504d89d6e361f064a17391afaf42d74b8. * noNimScript => noWeirdTarget + noNimJs --- changelog.md | 1 - compiler/scriptconfig.nim | 10 ++- lib/pure/os.nim | 131 ++++++++++++++++++-------------------- lib/system/nimscript.nim | 11 +++- tests/test_nimscript.nims | 7 ++ 5 files changed, 81 insertions(+), 79 deletions(-) diff --git a/changelog.md b/changelog.md index b9fb48117..588af989b 100644 --- a/changelog.md +++ b/changelog.md @@ -62,7 +62,6 @@ - Added high-level `asyncnet.sendTo` and `asyncnet.recvFrom`. UDP functionality. -- `paramCount` & `paramStr` are now defined in os.nim instead of nimscript.nim for nimscript/nimble. - `dollars.$` now works for unsigned ints with `nim js` - Improvements to the `bitops` module, including bitslices, non-mutating versions diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim index 994a2d342..13ab03426 100644 --- a/compiler/scriptconfig.nim +++ b/compiler/scriptconfig.nim @@ -141,12 +141,10 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string; setResult(a, options.existsConfigVar(conf, a.getString 0)) cbconf nimcacheDir: setResult(a, options.getNimcacheDir(conf).string) - result.registerCallback "stdlib.os." & astToStr(paramStr), - proc (a: VmArgs) = - setResult(a, os.paramStr(int a.getInt 0)) - result.registerCallback "stdlib.os." & astToStr(paramCount), - proc (a: VmArgs) = - setResult(a, os.paramCount()) + cbconf paramStr: + setResult(a, os.paramStr(int a.getInt 0)) + cbconf paramCount: + setResult(a, os.paramCount()) cbconf cmpIgnoreStyle: setResult(a, strutils.cmpIgnoreStyle(a.getString 0, a.getString 1)) cbconf cmpIgnoreCase: diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 79cd27e10..bb09c966f 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -76,9 +76,17 @@ else: {.error: "OS module not ported to your operating system!".} when weirdTarget and defined(nimErrorProcCanHaveBody): - {.pragma: noNimScript, error: "this proc is not available on the NimScript target".} + {.pragma: noWeirdTarget, error: "this proc is not available on the NimScript/js target".} else: - {.pragma: noNimScript.} + {.pragma: noWeirdTarget.} + +when defined(nimscript): + # for procs already defined in scriptconfig.nim + template noNimJs(body): untyped = discard +elif defined(js): + {.pragma: noNimJs, error: "this proc is not available on the js target".} +else: + {.pragma: noNimJs.} proc normalizePathAux(path: var string){.inline, raises: [], noSideEffect.} @@ -1088,7 +1096,7 @@ when defined(windows) and not weirdTarget: f.cFileName[1].int == dot and f.cFileName[2].int == 0) proc existsFile*(filename: string): bool {.rtl, extern: "nos$1", - tags: [ReadDirEffect], noNimScript.} = + tags: [ReadDirEffect], noNimJs.} = ## Returns true if `filename` exists and is a regular file or symlink. ## ## Directories, device files, named pipes and sockets return false. @@ -1108,7 +1116,7 @@ proc existsFile*(filename: string): bool {.rtl, extern: "nos$1", return stat(filename, res) >= 0'i32 and S_ISREG(res.st_mode) proc existsDir*(dir: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect], - noNimScript.} = + noNimJs.} = ## Returns true if the directory `dir` exists. If `dir` is a file, false ## is returned. Follows symlinks. ## @@ -1128,7 +1136,7 @@ proc existsDir*(dir: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect] proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect], - noNimScript.} = + noWeirdTarget.} = ## Returns true if the symlink `link` exists. Will return true ## regardless of whether the link points to a directory or file. ## @@ -1146,7 +1154,7 @@ proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1", var res: Stat return lstat(link, res) >= 0'i32 and S_ISLNK(res.st_mode) -proc fileExists*(filename: string): bool {.inline, noNimScript.} = +proc fileExists*(filename: string): bool {.inline, noNimJs.} = ## Alias for `existsFile proc <#existsFile,string>`_. ## ## See also: @@ -1154,7 +1162,7 @@ proc fileExists*(filename: string): bool {.inline, noNimScript.} = ## * `symlinkExists proc <#symlinkExists,string>`_ existsFile(filename) -proc dirExists*(dir: string): bool {.inline, noNimScript.} = +proc dirExists*(dir: string): bool {.inline, noNimJs.} = ## Alias for `existsDir proc <#existsDir,string>`_. ## ## See also: @@ -1175,7 +1183,7 @@ const proc findExe*(exe: string, followSymlinks: bool = true; extensions: openArray[string]=ExeExts): string {. - tags: [ReadDirEffect, ReadEnvEffect, ReadIOEffect], noNimScript.} = + tags: [ReadDirEffect, ReadEnvEffect, ReadIOEffect], noNimJs.} = ## Searches for `exe` in the current working directory and then ## in directories listed in the ``PATH`` environment variable. ## @@ -1231,7 +1239,7 @@ when weirdTarget: const times = "fake const" template Time(x: untyped): untyped = string -proc getLastModificationTime*(file: string): times.Time {.rtl, extern: "nos$1", noNimScript.} = +proc getLastModificationTime*(file: string): times.Time {.rtl, extern: "nos$1", noWeirdTarget.} = ## Returns the `file`'s last modification time. ## ## See also: @@ -1249,7 +1257,7 @@ proc getLastModificationTime*(file: string): times.Time {.rtl, extern: "nos$1", result = fromWinTime(rdFileTime(f.ftLastWriteTime)) findClose(h) -proc getLastAccessTime*(file: string): times.Time {.rtl, extern: "nos$1", noNimScript.} = +proc getLastAccessTime*(file: string): times.Time {.rtl, extern: "nos$1", noWeirdTarget.} = ## Returns the `file`'s last read or write access time. ## ## See also: @@ -1267,7 +1275,7 @@ proc getLastAccessTime*(file: string): times.Time {.rtl, extern: "nos$1", noNimS result = fromWinTime(rdFileTime(f.ftLastAccessTime)) findClose(h) -proc getCreationTime*(file: string): times.Time {.rtl, extern: "nos$1", noNimScript.} = +proc getCreationTime*(file: string): times.Time {.rtl, extern: "nos$1", noWeirdTarget.} = ## Returns the `file`'s creation time. ## ## **Note:** Under POSIX OS's, the returned time may actually be the time at @@ -1289,7 +1297,7 @@ proc getCreationTime*(file: string): times.Time {.rtl, extern: "nos$1", noNimScr result = fromWinTime(rdFileTime(f.ftCreationTime)) findClose(h) -proc fileNewer*(a, b: string): bool {.rtl, extern: "nos$1", noNimScript.} = +proc fileNewer*(a, b: string): bool {.rtl, extern: "nos$1", noWeirdTarget.} = ## Returns true if the file `a` is newer than file `b`, i.e. if `a`'s ## modification time is later than `b`'s. ## @@ -1368,7 +1376,7 @@ when not defined(nimscript): else: raiseOSError(osLastError()) -proc setCurrentDir*(newDir: string) {.inline, tags: [], noNimScript.} = +proc setCurrentDir*(newDir: string) {.inline, tags: [], noWeirdTarget.} = ## Sets the `current working directory`:idx:; `OSError` ## is raised if `newDir` cannot been set. ## @@ -1502,7 +1510,7 @@ when defined(Windows) and not weirdTarget: ) proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1", - tags: [ReadDirEffect], noNimScript.} = + tags: [ReadDirEffect], noWeirdTarget.} = ## Returns true if both pathname arguments refer to the same physical ## file or directory. ## @@ -1547,7 +1555,7 @@ proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1", result = a.st_dev == b.st_dev and a.st_ino == b.st_ino proc sameFileContent*(path1, path2: string): bool {.rtl, extern: "nos$1", - tags: [ReadIOEffect], noNimScript.} = + tags: [ReadIOEffect], noWeirdTarget.} = ## Returns true if both pathname arguments refer to files with identical ## binary content. ## @@ -1598,7 +1606,7 @@ type fpOthersRead ## read access for others proc getFilePermissions*(filename: string): set[FilePermission] {. - rtl, extern: "nos$1", tags: [ReadDirEffect], noNimScript.} = + rtl, extern: "nos$1", tags: [ReadDirEffect], noWeirdTarget.} = ## Retrieves file permissions for `filename`. ## ## `OSError` is raised in case of an error. @@ -1636,7 +1644,7 @@ proc getFilePermissions*(filename: string): set[FilePermission] {. result = {fpUserExec..fpOthersRead} proc setFilePermissions*(filename: string, permissions: set[FilePermission]) {. - rtl, extern: "nos$1", tags: [WriteDirEffect], noNimScript.} = + rtl, extern: "nos$1", tags: [WriteDirEffect], noWeirdTarget.} = ## Sets the file permissions for `filename`. ## ## `OSError` is raised in case of an error. @@ -1678,7 +1686,7 @@ proc setFilePermissions*(filename: string, permissions: set[FilePermission]) {. if res2 == - 1'i32: raiseOSError(osLastError(), $(filename, permissions)) proc copyFile*(source, dest: string) {.rtl, extern: "nos$1", - tags: [ReadIOEffect, WriteIOEffect], noNimScript.} = + tags: [ReadIOEffect, WriteIOEffect], noWeirdTarget.} = ## Copies a file from `source` to `dest`. ## ## If this fails, `OSError` is raised. @@ -1754,7 +1762,7 @@ when defined(Windows) and not weirdTarget: template setFileAttributes(file, attrs: untyped): untyped = setFileAttributesA(file, attrs) -proc tryRemoveFile*(file: string): bool {.rtl, extern: "nos$1", tags: [WriteDirEffect], noNimScript.} = +proc tryRemoveFile*(file: string): bool {.rtl, extern: "nos$1", tags: [WriteDirEffect], noWeirdTarget.} = ## Removes the `file`. ## ## If this fails, returns `false`. This does not fail @@ -1786,7 +1794,7 @@ proc tryRemoveFile*(file: string): bool {.rtl, extern: "nos$1", tags: [WriteDirE if unlink(file) != 0'i32 and errno != ENOENT: result = false -proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect], noNimScript.} = +proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect], noWeirdTarget.} = ## Removes the `file`. ## ## If this fails, `OSError` is raised. This does not fail @@ -1803,7 +1811,7 @@ proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect], n if not tryRemoveFile(file): raiseOSError(osLastError(), file) -proc tryMoveFSObject(source, dest: string): bool {.noNimScript.} = +proc tryMoveFSObject(source, dest: string): bool {.noWeirdTarget.} = ## Moves a file or directory from `source` to `dest`. ## ## Returns false in case of `EXDEV` error. @@ -1827,7 +1835,7 @@ proc tryMoveFSObject(source, dest: string): bool {.noNimScript.} = return true proc moveFile*(source, dest: string) {.rtl, extern: "nos$1", - tags: [ReadIOEffect, WriteIOEffect], noNimScript.} = + tags: [ReadIOEffect, WriteIOEffect], noWeirdTarget.} = ## Moves a file from `source` to `dest`. ## ## If this fails, `OSError` is raised. @@ -1864,7 +1872,7 @@ proc exitStatusLikeShell*(status: cint): cint = status proc execShellCmd*(command: string): int {.rtl, extern: "nos$1", - tags: [ExecIOEffect], noNimScript.} = + tags: [ExecIOEffect], noWeirdTarget.} = ## Executes a `shell command`:idx:. ## ## Command has the form 'program args' where args are the command @@ -1938,7 +1946,7 @@ template walkCommon(pattern: string, filter) = if filter(path): yield path -iterator walkPattern*(pattern: string): string {.tags: [ReadDirEffect], noNimScript.} = +iterator walkPattern*(pattern: string): string {.tags: [ReadDirEffect], noWeirdTarget.} = ## Iterate over all the files and directories that match the `pattern`. ## ## On POSIX this uses the `glob`:idx: call. @@ -1952,7 +1960,7 @@ iterator walkPattern*(pattern: string): string {.tags: [ReadDirEffect], noNimScr ## * `walkDirRec iterator <#walkDirRec.i,string>`_ walkCommon(pattern, defaultWalkFilter) -iterator walkFiles*(pattern: string): string {.tags: [ReadDirEffect], noNimScript.} = +iterator walkFiles*(pattern: string): string {.tags: [ReadDirEffect], noWeirdTarget.} = ## Iterate over all the files that match the `pattern`. ## ## On POSIX this uses the `glob`:idx: call. @@ -1966,7 +1974,7 @@ iterator walkFiles*(pattern: string): string {.tags: [ReadDirEffect], noNimScrip ## * `walkDirRec iterator <#walkDirRec.i,string>`_ walkCommon(pattern, isFile) -iterator walkDirs*(pattern: string): string {.tags: [ReadDirEffect], noNimScript.} = +iterator walkDirs*(pattern: string): string {.tags: [ReadDirEffect], noWeirdTarget.} = ## Iterate over all the directories that match the `pattern`. ## ## On POSIX this uses the `glob`:idx: call. @@ -1981,7 +1989,7 @@ iterator walkDirs*(pattern: string): string {.tags: [ReadDirEffect], noNimScript walkCommon(pattern, isDir) proc expandFilename*(filename: string): string {.rtl, extern: "nos$1", - tags: [ReadDirEffect], noNimScript.} = + tags: [ReadDirEffect], noWeirdTarget.} = ## Returns the full (`absolute`:idx:) path of an existing file `filename`. ## ## Raises `OSError` in case of an error. Follows symlinks. @@ -2215,7 +2223,7 @@ iterator walkDirRec*(dir: string, # continue iteration. # Future work can provide a way to customize this and do error reporting. -proc rawRemoveDir(dir: string) {.noNimScript.} = +proc rawRemoveDir(dir: string) {.noWeirdTarget.} = when defined(windows): when useWinUnicode: wrapUnary(res, removeDirectoryW, dir) @@ -2229,7 +2237,7 @@ proc rawRemoveDir(dir: string) {.noNimScript.} = if rmdir(dir) != 0'i32 and errno != ENOENT: raiseOSError(osLastError(), dir) proc removeDir*(dir: string, checkDir = false) {.rtl, extern: "nos$1", tags: [ - WriteDirEffect, ReadDirEffect], benign, noNimScript.} = + WriteDirEffect, ReadDirEffect], benign, noWeirdTarget.} = ## Removes the directory `dir` including all subdirectories and files ## in `dir` (recursively). ## @@ -2253,7 +2261,7 @@ proc removeDir*(dir: string, checkDir = false) {.rtl, extern: "nos$1", tags: [ # the same files) rawRemoveDir(dir) -proc rawCreateDir(dir: string): bool {.noNimScript.} = +proc rawCreateDir(dir: string): bool {.noWeirdTarget.} = # Try to create one directory (not the whole path). # returns `true` for success, `false` if the path has previously existed # @@ -2298,7 +2306,7 @@ proc rawCreateDir(dir: string): bool {.noNimScript.} = raiseOSError(osLastError(), dir) proc existsOrCreateDir*(dir: string): bool {.rtl, extern: "nos$1", - tags: [WriteDirEffect, ReadDirEffect], noNimScript.} = + tags: [WriteDirEffect, ReadDirEffect], noWeirdTarget.} = ## Check if a `directory`:idx: `dir` exists, and create it otherwise. ## ## Does not create parent directories (fails if parent does not exist). @@ -2318,7 +2326,7 @@ proc existsOrCreateDir*(dir: string): bool {.rtl, extern: "nos$1", raise newException(IOError, "Failed to create '" & dir & "'") proc createDir*(dir: string) {.rtl, extern: "nos$1", - tags: [WriteDirEffect, ReadDirEffect], noNimScript.} = + tags: [WriteDirEffect, ReadDirEffect], noWeirdTarget.} = ## Creates the `directory`:idx: `dir`. ## ## The directory may contain several subdirectories that do not exist yet. @@ -2349,7 +2357,7 @@ proc createDir*(dir: string) {.rtl, extern: "nos$1", discard existsOrCreateDir(dir) proc copyDir*(source, dest: string) {.rtl, extern: "nos$1", - tags: [WriteIOEffect, ReadIOEffect], benign, noNimScript.} = + tags: [WriteIOEffect, ReadIOEffect], benign, noWeirdTarget.} = ## Copies a directory from `source` to `dest`. ## ## If this fails, `OSError` is raised. @@ -2380,7 +2388,7 @@ proc copyDir*(source, dest: string) {.rtl, extern: "nos$1", copyDir(path, dest / noSource) else: discard -proc moveDir*(source, dest: string) {.tags: [ReadIOEffect, WriteIOEffect], noNimScript.} = +proc moveDir*(source, dest: string) {.tags: [ReadIOEffect, WriteIOEffect], noWeirdTarget.} = ## Moves a directory from `source` to `dest`. ## ## If this fails, `OSError` is raised. @@ -2398,7 +2406,7 @@ proc moveDir*(source, dest: string) {.tags: [ReadIOEffect, WriteIOEffect], noNim copyDir(source, dest) removeDir(source) -proc createSymlink*(src, dest: string) {.noNimScript.} = +proc createSymlink*(src, dest: string) {.noWeirdTarget.} = ## Create a symbolic link at `dest` which points to the item specified ## by `src`. On most operating systems, will fail if a link already exists. ## @@ -2426,7 +2434,7 @@ proc createSymlink*(src, dest: string) {.noNimScript.} = if symlink(src, dest) != 0: raiseOSError(osLastError(), $(src, dest)) -proc createHardlink*(src, dest: string) {.noNimScript.} = +proc createHardlink*(src, dest: string) {.noWeirdTarget.} = ## Create a hard link at `dest` which points to the item specified ## by `src`. ## @@ -2449,7 +2457,7 @@ proc createHardlink*(src, dest: string) {.noNimScript.} = raiseOSError(osLastError(), $(src, dest)) proc copyFileWithPermissions*(source, dest: string, - ignorePermissionErrors = true) {.noNimScript.} = + ignorePermissionErrors = true) {.noWeirdTarget.} = ## Copies a file from `source` to `dest` preserving file permissions. ## ## This is a wrapper proc around `copyFile <#copyFile,string,string>`_, @@ -2483,7 +2491,7 @@ proc copyFileWithPermissions*(source, dest: string, proc copyDirWithPermissions*(source, dest: string, ignorePermissionErrors = true) {.rtl, extern: "nos$1", - tags: [WriteIOEffect, ReadIOEffect], benign, noNimScript.} = + tags: [WriteIOEffect, ReadIOEffect], benign, noWeirdTarget.} = ## Copies a directory from `source` to `dest` preserving file permissions. ## ## If this fails, `OSError` is raised. This is a wrapper proc around `copyDir @@ -2526,7 +2534,7 @@ proc copyDirWithPermissions*(source, dest: string, proc inclFilePermissions*(filename: string, permissions: set[FilePermission]) {. - rtl, extern: "nos$1", tags: [ReadDirEffect, WriteDirEffect], noNimScript.} = + rtl, extern: "nos$1", tags: [ReadDirEffect, WriteDirEffect], noWeirdTarget.} = ## A convenience proc for: ## ## .. code-block:: nim @@ -2535,14 +2543,14 @@ proc inclFilePermissions*(filename: string, proc exclFilePermissions*(filename: string, permissions: set[FilePermission]) {. - rtl, extern: "nos$1", tags: [ReadDirEffect, WriteDirEffect], noNimScript.} = + rtl, extern: "nos$1", tags: [ReadDirEffect, WriteDirEffect], noWeirdTarget.} = ## A convenience proc for: ## ## .. code-block:: nim ## setFilePermissions(filename, getFilePermissions(filename)-permissions) setFilePermissions(filename, getFilePermissions(filename)-permissions) -proc expandSymlink*(symlinkPath: string): string {.noNimScript.} = +proc expandSymlink*(symlinkPath: string): string {.noWeirdTarget.} = ## Returns a string representing the path to which the symbolic link points. ## ## On Windows this is a noop, ``symlinkPath`` is simply returned. @@ -2720,23 +2728,8 @@ when defined(nimdoc): ## else: ## # Do something else! -elif defined(nimscript): - proc paramStr*(i: int): string = - ## Retrieves the ``i``'th command line parameter. - discard - - proc paramCount*(): int = - ## Retrieves the number of command line parameters. - discard - -elif defined(js): - proc paramStr*(i: int): TaintedString {.tags: [ReadIOEffect].} = - raise newException(OSError, "paramStr is not implemented on JavaScript") - - proc paramCount*(): int {.tags: [ReadIOEffect].} = - raise newException(OSError, "paramCount is not implemented on JavaScript") - -elif defined(nintendoswitch): +elif defined(nimscript): discard +elif defined(nintendoswitch) or weirdTarget: proc paramStr*(i: int): TaintedString {.tags: [ReadIOEffect].} = raise newException(OSError, "paramStr is not implemented on Nintendo Switch") @@ -2947,7 +2940,7 @@ when defined(haiku): else: result = "" -proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noNimScript.} = +proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noWeirdTarget.} = ## Returns the filename of the application's executable. ## This proc will resolve symlinks. ## @@ -3013,14 +3006,14 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noN if result.len == 0: result = getApplHeuristic() -proc getAppDir*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noNimScript.} = +proc getAppDir*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noWeirdTarget.} = ## Returns the directory of the application's executable. ## ## See also: ## * `getAppFilename proc <#getAppFilename>`_ result = splitFile(getAppFilename()).dir -proc sleep*(milsecs: int) {.rtl, extern: "nos$1", tags: [TimeEffect], noNimScript.} = +proc sleep*(milsecs: int) {.rtl, extern: "nos$1", tags: [TimeEffect], noWeirdTarget.} = ## Sleeps `milsecs` milliseconds. when defined(windows): winlean.sleep(int32(milsecs)) @@ -3031,7 +3024,7 @@ proc sleep*(milsecs: int) {.rtl, extern: "nos$1", tags: [TimeEffect], noNimScrip discard posix.nanosleep(a, b) proc getFileSize*(file: string): BiggestInt {.rtl, extern: "nos$1", - tags: [ReadIOEffect], noNimScript.} = + tags: [ReadIOEffect], noWeirdTarget.} = ## Returns the file size of `file` (in bytes). ``OSError`` is ## raised in case of an error. when defined(windows): @@ -3132,13 +3125,13 @@ template rawToFormalFileInfo(rawInfo, path, formalInfo): untyped = assert(path != "") # symlinks can't occur for file handles formalInfo.kind = getSymlinkFileKind(path) -when defined(js) or defined(nimscript): +when defined(js): when not declared(FileHandle): type FileHandle = distinct int32 when not declared(File): type File = object -proc getFileInfo*(handle: FileHandle): FileInfo {.noNimScript.} = +proc getFileInfo*(handle: FileHandle): FileInfo {.noWeirdTarget.} = ## Retrieves file information for the file object represented by the given ## handle. ## @@ -3164,7 +3157,7 @@ proc getFileInfo*(handle: FileHandle): FileInfo {.noNimScript.} = raiseOSError(osLastError(), $handle) rawToFormalFileInfo(rawInfo, "", result) -proc getFileInfo*(file: File): FileInfo {.noNimScript.} = +proc getFileInfo*(file: File): FileInfo {.noWeirdTarget.} = ## Retrieves file information for the file object. ## ## See also: @@ -3174,7 +3167,7 @@ proc getFileInfo*(file: File): FileInfo {.noNimScript.} = raise newException(IOError, "File is nil") result = getFileInfo(file.getFileHandle()) -proc getFileInfo*(path: string, followSymlink = true): FileInfo {.noNimScript.} = +proc getFileInfo*(path: string, followSymlink = true): FileInfo {.noWeirdTarget.} = ## Retrieves file information for the file object pointed to by `path`. ## ## Due to intrinsic differences between operating systems, the information @@ -3212,7 +3205,7 @@ proc getFileInfo*(path: string, followSymlink = true): FileInfo {.noNimScript.} raiseOSError(osLastError(), path) rawToFormalFileInfo(rawInfo, path, result) -proc isHidden*(path: string): bool {.noNimScript.} = +proc isHidden*(path: string): bool {.noWeirdTarget.} = ## Determines whether ``path`` is hidden or not, using `this ## reference `_. ## @@ -3242,7 +3235,7 @@ proc isHidden*(path: string): bool {.noNimScript.} = let fileName = lastPathPart(path) result = len(fileName) >= 2 and fileName[0] == '.' and fileName != ".." -proc getCurrentProcessId*(): int {.noNimScript.} = +proc getCurrentProcessId*(): int {.noWeirdTarget.} = ## Return current process ID. ## ## See also: @@ -3254,7 +3247,7 @@ proc getCurrentProcessId*(): int {.noNimScript.} = else: result = getpid() -proc setLastModificationTime*(file: string, t: times.Time) {.noNimScript.} = +proc setLastModificationTime*(file: string, t: times.Time) {.noWeirdTarget.} = ## Sets the `file`'s last modification time. `OSError` is raised in case of ## an error. when defined(posix): diff --git a/lib/system/nimscript.nim b/lib/system/nimscript.nim index da5c291fa..ceaaef013 100644 --- a/lib/system/nimscript.nim +++ b/lib/system/nimscript.nim @@ -56,6 +56,14 @@ proc rawExec(cmd: string): int {.tags: [ExecIOEffect], raises: [OSError].} = proc warningImpl(arg, orig: string) = discard proc hintImpl(arg, orig: string) = discard +proc paramStr*(i: int): string = + ## Retrieves the ``i``'th command line parameter. + builtin + +proc paramCount*(): int = + ## Retrieves the number of command line parameters. + builtin + proc switch*(key: string, val="") = ## Sets a Nim compiler command line switch, for ## example ``switch("checks", "on")``. @@ -272,9 +280,6 @@ proc selfExec*(command: string) {. raise newException(OSError, "FAILED: " & c) checkOsError() -from os import paramCount, paramStr -export paramCount, paramStr - proc put*(key, value: string) = ## Sets a configuration 'key' like 'gcc.options.always' to its value. builtin diff --git a/tests/test_nimscript.nims b/tests/test_nimscript.nims index a2e8ac945..de4b34728 100644 --- a/tests/test_nimscript.nims +++ b/tests/test_nimscript.nims @@ -81,3 +81,10 @@ when false: # #14142 discard findExe("nim") echo "Nimscript imports are successful." + +block: # #14142 + discard existsDir("/usr") + discard dirExists("/usr") + discard existsFile("/usr/foo") + discard fileExists("/usr/foo") + discard findExe("nim") From dfe51d10a1f204d0c3ab1a8be1e109029cc54f9b Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 16 Jun 2020 02:43:48 -0700 Subject: [PATCH 0229/3768] `addQuitProc` now works with closures, and c, js(node/browser) backend; fix some bugs in testament (#14342) * make addQuitProc great again * fix bugs in testament * fix test * change 2016 => 2020 * addQuitProc => addExitProc + locks * move to std/exitprocs --- lib/core/locks.nim | 13 ++++-- lib/pure/quitprocs.nim | 28 ------------ lib/std/exitprocs.nim | 65 ++++++++++++++++++++++++++++ lib/system.nim | 9 ++-- testament/categories.nim | 4 +- testament/specs.nim | 7 +++ testament/testament.nim | 12 ++--- tests/converter/tor_in_converter.nim | 2 +- tests/stdlib/texitprocs.nim | 21 +++++++++ 9 files changed, 117 insertions(+), 44 deletions(-) delete mode 100644 lib/pure/quitprocs.nim create mode 100644 lib/std/exitprocs.nim create mode 100644 tests/stdlib/texitprocs.nim diff --git a/lib/core/locks.nim b/lib/core/locks.nim index 7e2f4ad34..bddd6d864 100644 --- a/lib/core/locks.nim +++ b/lib/core/locks.nim @@ -9,6 +9,10 @@ ## This module contains Nim's support for locks and condition vars. +#[ +for js, for now we treat locks as noop's to avoid pushing `when defined(js)` +in client code that uses locks. +]# when not compileOption("threads") and not defined(nimdoc): when false: # fix #12330 @@ -26,7 +30,8 @@ type proc initLock*(lock: var Lock) {.inline.} = ## Initializes the given lock. - initSysLock(lock) + when not defined(js): + initSysLock(lock) proc deinitLock*(lock: var Lock) {.inline.} = ## Frees the resources associated with the lock. @@ -38,11 +43,13 @@ proc tryAcquire*(lock: var Lock): bool = proc acquire*(lock: var Lock) = ## Acquires the given lock. - acquireSys(lock) + when not defined(js): + acquireSys(lock) proc release*(lock: var Lock) = ## Releases the given lock. - releaseSys(lock) + when not defined(js): + releaseSys(lock) proc initCond*(cond: var Cond) {.inline.} = diff --git a/lib/pure/quitprocs.nim b/lib/pure/quitprocs.nim deleted file mode 100644 index 76bbcdb2b..000000000 --- a/lib/pure/quitprocs.nim +++ /dev/null @@ -1,28 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2016 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## ``system.addQuitProc`` is nice and very useful but due to its C-based -## implementation it doesn't support closures which limits its usefulness. -## This module fixes this. Later versions of this module will also -## support the JavaScript backend. - -var - gClosures: seq[proc () {.closure.}] - -proc callClosures() {.noconv.} = - for i in countdown(gClosures.len-1, 0): - gClosures[i]() - -proc addQuitClosure*(cl: proc () {.closure.}) = - ## Like ``system.addQuitProc`` but it supports closures. - if gClosures.len == 0: - addQuitProc(callClosures) - gClosures = @[cl] - else: - gClosures.add(cl) diff --git a/lib/std/exitprocs.nim b/lib/std/exitprocs.nim new file mode 100644 index 000000000..b2811735c --- /dev/null +++ b/lib/std/exitprocs.nim @@ -0,0 +1,65 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2020 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +import locks + +type + FunKind = enum kClosure, kNoconv # extend as needed + Fun = object + case kind: FunKind + of kClosure: fun1: proc () {.closure.} + of kNoconv: fun2: proc () {.noconv.} + +var + gFunsLock: Lock + gFuns: seq[Fun] + +initLock(gFunsLock) + +when defined(js): + proc addAtExit(quitProc: proc() {.noconv.}) = + when defined(nodejs): + asm """ + process.on('exit', `quitProc`); + """ + elif defined(js): + asm """ + window.onbeforeunload = `quitProc`; + """ +else: + proc addAtExit(quitProc: proc() {.noconv.}) {. + importc: "atexit", header: "".} + +proc callClosures() {.noconv.} = + withLock gFunsLock: + for i in countdown(gFuns.len-1, 0): + let fun = gFuns[i] + case fun.kind + of kClosure: fun.fun1() + of kNoconv: fun.fun2() + +template fun() = + if gFuns.len == 0: + addAtExit(callClosures) + +proc addExitProc*(cl: proc () {.closure.}) = + ## Adds/registers a quit procedure. Each call to `addExitProc` registers + ## another quit procedure. They are executed on a last-in, first-out basis. + # Support for `addExitProc` is done by Ansi C's facilities here. + # In case of an unhandled exception the exit handlers should + # not be called explicitly! The user may decide to do this manually though. + withLock gFunsLock: + fun() + gFuns.add Fun(kind: kClosure, fun1: cl) + +proc addExitProc*(cl: proc() {.noconv.}) = + ## overload for `noconv` procs. + withLock gFunsLock: + fun() + gFuns.add Fun(kind: kNoconv, fun2: cl) diff --git a/lib/system.nim b/lib/system.nim index 85be43cba..2235b8b70 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1147,8 +1147,8 @@ when defined(nimdoc): proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit", noreturn.} ## Stops the program immediately with an exit code. ## - ## Before stopping the program the "quit procedures" are called in the - ## opposite order they were added with `addQuitProc <#addQuitProc,proc>`_. + ## Before stopping the program the "exit procedures" are called in the + ## opposite order they were added with `addExitProc `_. ## ``quit`` never returns and ignores any exception that may have been raised ## by the quit procedures. It does *not* call the garbage collector to free ## all the memory, unless a quit procedure calls `GC_fullCollect @@ -1426,8 +1426,8 @@ proc toBiggestInt*(f: BiggestFloat): BiggestInt {.noSideEffect.} = ## Same as `toInt <#toInt,float>`_ but for ``BiggestFloat`` to ``BiggestInt``. if f >= 0: BiggestInt(f+0.5) else: BiggestInt(f-0.5) -proc addQuitProc*(quitProc: proc() {.noconv.}) {. - importc: "atexit", header: "".} +proc addQuitProc*(quitProc: proc() {.noconv.}) {. + importc: "atexit", header: "", deprecated: "use exitprocs.addExitProc".} ## Adds/registers a quit procedure. ## ## Each call to ``addQuitProc`` registers another quit procedure. Up to 30 @@ -1440,7 +1440,6 @@ proc addQuitProc*(quitProc: proc() {.noconv.}) {. # In case of an unhandled exception the exit handlers should # not be called explicitly! The user may decide to do this manually though. - proc swap*[T](a, b: var T) {.magic: "Swap", noSideEffect.} ## Swaps the values `a` and `b`. ## diff --git a/testament/categories.nim b/testament/categories.nim index 4e83fa1a9..45a2113a6 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -269,8 +269,8 @@ proc debuggerTests(r: var TResults, cat: Category, options: string) = proc jsTests(r: var TResults, cat: Category, options: string) = template test(filename: untyped) = - testSpec r, makeTest(filename, options & " -d:nodejs", cat), {targetJS} - testSpec r, makeTest(filename, options & " -d:nodejs -d:release", cat), {targetJS} + testSpec r, makeTest(filename, options, cat), {targetJS} + testSpec r, makeTest(filename, options & " -d:release", cat), {targetJS} for t in os.walkFiles("tests/js/t*.nim"): test(t) diff --git a/testament/specs.nim b/testament/specs.nim index 964d53d24..7405c5055 100644 --- a/testament/specs.nim +++ b/testament/specs.nim @@ -87,6 +87,13 @@ const targetToExt*: array[TTarget, string] = ["nim.c", "nim.cpp", "nim.m", "js"] targetToCmd*: array[TTarget, string] = ["c", "cpp", "objc", "js"] +proc defaultOptions*(a: TTarget): string = + case a + of targetJS: "-d:nodejs" + # once we start testing for `nim js -d:nimbrowser` (eg selenium or similar), + # we can adapt this logic; or a given js test can override with `-u:nodejs`. + else: "" + when not declared(parseCfgBool): # candidate for the stdlib: proc parseCfgBool(s: string): bool = diff --git a/testament/testament.nim b/testament/testament.nim index 69b7e322e..098c07ac5 100644 --- a/testament/testament.nim +++ b/testament/testament.nim @@ -135,7 +135,10 @@ proc nimcacheDir(filename, options: string, target: TTarget): string = proc prepareTestArgs(cmdTemplate, filename, options, nimcache: string, target: TTarget, extraOptions = ""): seq[string] = - let options = options & " " & quoteShell("--nimCache:" & nimcache) & " " & extraOptions + var options = target.defaultOptions & " " & options + # improve pending https://github.com/nim-lang/Nim/issues/14343 + if nimcache.len > 0: options.add " " & ("--nimCache:" & nimcache).quoteShell + options.add " " & extraOptions result = parseCmdLine(cmdTemplate % ["target", targetToCmd[target], "options", options, "file", filename.quoteShell, "filedir", filename.getFileDir()]) @@ -192,9 +195,7 @@ proc callCompiler(cmdTemplate, filename, options, nimcache: string, proc callCCompiler(cmdTemplate, filename, options: string, target: TTarget): TSpec = - let c = parseCmdLine(cmdTemplate % ["target", targetToCmd[target], - "options", options, "file", filename.quoteShell, - "filedir", filename.getFileDir()]) + let c = prepareTestArgs(cmdTemplate, filename, options, nimcache = "", target) var p = startProcess(command="gcc", args=c[5 .. ^1], options={poStdErrToStdOut, poUsePath}) let outp = p.outputStream @@ -254,7 +255,8 @@ proc addResult(r: var TResults, test: TTest, target: TTarget, # test.name is easier to find than test.name.extractFilename # A bit hacky but simple and works with tests/testament/tshould_not_work.nim var name = test.name.replace(DirSep, '/') - name.add " " & $target & test.options + name.add " " & $target + if test.options.len > 0: name.add " " & test.options let duration = epochTime() - test.startTime let success = if test.spec.timeout > 0.0 and duration > test.spec.timeout: reTimeout diff --git a/tests/converter/tor_in_converter.nim b/tests/converter/tor_in_converter.nim index 5674526a1..df2334647 100644 --- a/tests/converter/tor_in_converter.nim +++ b/tests/converter/tor_in_converter.nim @@ -4,7 +4,7 @@ test''' """ # bug #4537 -# nim js --d:nodejs +# nim js -d:nodejs type Str = distinct string diff --git a/tests/stdlib/texitprocs.nim b/tests/stdlib/texitprocs.nim new file mode 100644 index 000000000..9d5378fe8 --- /dev/null +++ b/tests/stdlib/texitprocs.nim @@ -0,0 +1,21 @@ +discard """ +targets: "c cpp js" +output: ''' +ok4 +ok3 +ok2 +ok1 +''' +""" + +import std/exitprocs + +proc fun1() {.noconv.} = echo "ok1" +proc fun2() = echo "ok2" +proc fun3() {.noconv.} = echo "ok3" +proc fun4() = echo "ok4" + +addExitProc(fun1) +addExitProc(fun2) +addExitProc(fun3) +addExitProc(fun4) From 1bc2e19498a718276496263b574a66bb2ee84f56 Mon Sep 17 00:00:00 2001 From: Euan Date: Tue, 16 Jun 2020 10:44:28 +0100 Subject: [PATCH 0230/3768] Set cincludes and clibdir for FreeBSD, OpenBSD and NetBSD. (#14680) --- config/nim.cfg | 3 +++ tests/niminaction/Chapter8/sfml/sfml_test.nim | 3 --- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/config/nim.cfg b/config/nim.cfg index b43dfd80b..206f80413 100644 --- a/config/nim.cfg +++ b/config/nim.cfg @@ -205,6 +205,9 @@ clang.objc.options.linker = "-lobjc -lgnustep-base" llvm_gcc.cpp.options.linker = "-Wl,-rpath=.:/usr/local/lib:/usr/pkg/lib:/usr/X11R6/lib" clang.options.linker = "-Wl,-rpath=.:/usr/local/lib:/usr/pkg/lib:/usr/X11R6/lib" clang.cpp.options.linker = "-Wl,-rpath=.:/usr/local/lib:/usr/pkg/lib:/usr/X11R6/lib" + + cincludes: "/usr/local/include" + clibdir: "/usr/local/lib" @end # Configuration for the VxWorks diff --git a/tests/niminaction/Chapter8/sfml/sfml_test.nim b/tests/niminaction/Chapter8/sfml/sfml_test.nim index cdd5be776..e71060cb4 100644 --- a/tests/niminaction/Chapter8/sfml/sfml_test.nim +++ b/tests/niminaction/Chapter8/sfml/sfml_test.nim @@ -1,9 +1,6 @@ discard """ action: compile disabled: "windows" -disabled: "freebsd" -disabled: "openbsd" -disabled: "netbsd" """ import sfml, os From e35b21e55310e93ca04980febe1a48c9436bded9 Mon Sep 17 00:00:00 2001 From: Dylan Modesitt Date: Tue, 16 Jun 2020 10:24:26 -0400 Subject: [PATCH 0231/3768] Close#5586 (#14682) * re-enable macos and bsd on some coro tests * re-enable macos and bsd on some coro tests --- tests/coroutines/tgc.nim | 1 - tests/coroutines/twait.nim | 2 -- 2 files changed, 3 deletions(-) diff --git a/tests/coroutines/tgc.nim b/tests/coroutines/tgc.nim index 86b625370..46f4f8e83 100644 --- a/tests/coroutines/tgc.nim +++ b/tests/coroutines/tgc.nim @@ -1,6 +1,5 @@ discard """ target: "c" - disabled: "openbsd" """ import coro diff --git a/tests/coroutines/twait.nim b/tests/coroutines/twait.nim index 2563ad7d3..b88801869 100644 --- a/tests/coroutines/twait.nim +++ b/tests/coroutines/twait.nim @@ -1,7 +1,5 @@ discard """ output: "Exit 1\nExit 2" - disabled: "macosx" - disabled: "openbsd" target: "c" """ import coro From 7ded490ec06286bb016bcb525e8d816f952e7bfc Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 16 Jun 2020 14:38:30 -0700 Subject: [PATCH 0232/3768] cleanup tests/test_nimscript.nims (#14686) --- tests/test_nimscript.nims | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/tests/test_nimscript.nims b/tests/test_nimscript.nims index de4b34728..7c64ea762 100644 --- a/tests/test_nimscript.nims +++ b/tests/test_nimscript.nims @@ -69,19 +69,11 @@ import std/[ decls, compilesettings, with, wrapnils ] +echo "Nimscript imports are successful." block: doAssert "./foo//./bar/".normalizedPath == "foo/bar".unixToNativePath -when false: # #14142 - discard existsDir("/usr") - discard dirExists("/usr") - discard existsFile("/usr/foo") - discard fileExists("/usr/foo") - discard findExe("nim") - -echo "Nimscript imports are successful." - block: # #14142 discard existsDir("/usr") discard dirExists("/usr") From ff93302f69c724bc03d7e0529fb0b9b6bfa0b36e Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 16 Jun 2020 15:57:57 -0700 Subject: [PATCH 0233/3768] use check to investigate #14685 flaky tests/async/t7758.nim (#14689) --- tests/async/t7758.nim | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/async/t7758.nim b/tests/async/t7758.nim index 15aec1c04..3cbe16852 100644 --- a/tests/async/t7758.nim +++ b/tests/async/t7758.nim @@ -1,4 +1,5 @@ import asyncdispatch +import std/unittest proc task() {.async.} = await sleepAsync(40) @@ -10,6 +11,6 @@ proc main() = inc(counter) poll(10) - doAssert counter <= 4 + check counter <= 4 -for i in 0 .. 4: main() +for i in 0 .. 10: main() From bdd2c25e9f97e8b92d6445417dd70450123a79fe Mon Sep 17 00:00:00 2001 From: solo989 Date: Tue, 16 Jun 2020 22:57:04 -0700 Subject: [PATCH 0234/3768] Update macros.nim (#14697) --- lib/core/macros.nim | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 27ff063a7..aa4269101 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -466,6 +466,8 @@ proc newIdentNode*(i: string): NimNode {.magic: "StrToIdent", noSideEffect, comp ## Creates an identifier node from `i`. It is simply an alias for ## ``ident(string)``. Use that, it's shorter. +proc ident*(name: string): NimNode {.magic: "StrToIdent", noSideEffect.} + ## Create a new ident node from a string. type BindSymRule* = enum ## specifies how ``bindSym`` behaves @@ -1283,9 +1285,6 @@ proc `$`*(node: NimNode): string {.compileTime.} = else: badNodeKind node, "$" -proc ident*(name: string): NimNode {.magic: "StrToIdent", noSideEffect.} - ## Create a new ident node from a string. - iterator items*(n: NimNode): NimNode {.inline.} = ## Iterates over the children of the NimNode ``n``. for i in 0 ..< n.len: From 15a45e8e73a4e8eb645e4c3c3a5080465347cc60 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Wed, 17 Jun 2020 02:58:03 -0300 Subject: [PATCH 0235/3768] Documentation update nims.rst (#14683) * Update nims.rst to add modules that work on NimScript as mentioned in the changelog.md * https://github.com/nim-lang/Nim/pull/14683#issuecomment-644976921 --- doc/nims.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/doc/nims.rst b/doc/nims.rst index c1d434476..03afa258a 100644 --- a/doc/nims.rst +++ b/doc/nims.rst @@ -87,11 +87,24 @@ At least the following standard library modules are available: * `std/editdistance `_ * `std/wordwrap `_ * `std/sums `_ +* `parsecsv `_ +* `parsecfg `_ +* `parsesql `_ +* `xmlparser `_ +* `htmlparser `_ +* `ropes `_ +* `json `_ +* `parsejson `_ +* `strtabs `_ +* `unidecode `_ In addition to the standard Nim syntax (`system `_ module), NimScripts support the procs and templates defined in the `nimscript `_ module too. +See also: +* `Check the tests for more information about modules compatible with NimScript. `_ + NimScript as a configuration file ================================= From 861953429362d72a76fe3768a25db3f8a1cf70ee Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 16 Jun 2020 23:10:34 -0700 Subject: [PATCH 0236/3768] remove compilerproc from `newIdentNode` (#14692) --- lib/core/macros.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index aa4269101..f3bca3af8 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -462,7 +462,7 @@ proc newIdentNode*(i: NimIdent): NimNode {.compileTime, deprecated.} = {.pop.} -proc newIdentNode*(i: string): NimNode {.magic: "StrToIdent", noSideEffect, compilerproc.} +proc newIdentNode*(i: string): NimNode {.magic: "StrToIdent", noSideEffect.} ## Creates an identifier node from `i`. It is simply an alias for ## ``ident(string)``. Use that, it's shorter. From e7f280bd26a54b4f187b087c8d35f43f0e6328c7 Mon Sep 17 00:00:00 2001 From: Miran Date: Wed, 17 Jun 2020 15:25:02 +0200 Subject: [PATCH 0237/3768] Remove deprecated stuff from stdlib (#14699) * update to the latest Jester * remove deprecated procs from some stdlib modules * 'criterion' is not maintained anymore and relies on obsolete stuff --- doc/backends.rst | 2 +- lib/core/macros.nim | 6 - lib/impure/re.nim | 7 - lib/pure/algorithm.nim | 4 - lib/pure/asyncdispatch.nim | 2 +- lib/pure/collections/heapqueue.nim | 8 - lib/pure/collections/sets.nim | 39 +--- lib/pure/concurrency/cpuload.nim | 2 +- lib/pure/httpclient.nim | 2 +- lib/pure/math.nim | 39 ---- lib/pure/nativesockets.nim | 16 -- lib/pure/random.nim | 34 +--- lib/pure/selectors.nim | 19 -- lib/pure/times.nim | 175 +----------------- lib/pure/unicode.nim | 82 -------- lib/pure/unittest.nim | 5 +- lib/system/arithmetics.nim | 8 - testament/important_packages.nim | 1 - tests/ccgbugs2/tinefficient_const_table.nim | 27 +++ tests/collections/thashsets.nim | 5 +- tests/deps/jester-#head/jester.nim | 121 +++++++----- tests/deps/jester-#head/jester.nimble | 8 +- tests/deps/jester-#head/jester/patterns.nim | 4 +- .../jester-#head/jester/private/utils.nim | 3 +- tests/deps/jester-#head/jester/request.nim | 1 + tests/gc/gcleak5.nim | 2 +- .../argument_parser/argument_parser.nim | 2 +- tests/manyloc/keineschweine/keineschweine.nim | 2 +- tests/manyloc/nake/nakefile.nim | 4 +- .../Chapter7/Tweeter/src/database.nim | 4 +- .../Chapter7/Tweeter/src/tweeter.nim | 2 +- .../Chapter7/Tweeter/src/views/user.nim | 2 +- tests/stdlib/tstdlib_various.nim | 18 +- tests/stdlib/tunittest.nim | 4 +- tests/template/template_various.nim | 2 +- tests/template/tgensymregression.nim | 2 +- tests/vm/textensionmap.nim | 2 +- tests/vm/tgloballetfrommacro.nim | 2 +- tools/niminst/debcreation.nim | 2 +- tools/nimweb.nim | 2 +- tools/website.nimf | 2 +- 41 files changed, 155 insertions(+), 519 deletions(-) create mode 100644 tests/ccgbugs2/tinefficient_const_table.nim diff --git a/doc/backends.rst b/doc/backends.rst index d34a06238..e46941a12 100644 --- a/doc/backends.rst +++ b/doc/backends.rst @@ -367,7 +367,7 @@ A similar thing happens with C code invoking Nim code which returns a .. code-block:: nim proc gimme(): cstring {.exportc.} = - result = "Hey there C code! " & $random(100) + result = "Hey there C code! " & $rand(100) Since Nim's garbage collector is not aware of the C code, once the ``gimme`` proc has finished it can reclaim the memory of the ``cstring``. diff --git a/lib/core/macros.nim b/lib/core/macros.nim index f3bca3af8..856c4f2c1 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -996,12 +996,6 @@ macro dumpAstGen*(s: untyped): untyped = echo s.astGenRepr ## ## Also see ``dumpTree`` and ``dumpLisp``. -macro dumpTreeImm*(s: untyped): untyped {.deprecated.} = echo s.treeRepr - ## Deprecated. Use `dumpTree` instead. - -macro dumpLispImm*(s: untyped): untyped {.deprecated.} = echo s.lispRepr - ## Deprecated. Use `dumpLisp` instead. - proc newEmptyNode*(): NimNode {.compileTime, noSideEffect.} = ## Create a new empty node. result = newNimNode(nnkEmpty) diff --git a/lib/impure/re.nim b/lib/impure/re.nim index c520fa833..1a6622677 100644 --- a/lib/impure/re.nim +++ b/lib/impure/re.nim @@ -483,13 +483,6 @@ proc multiReplace*(s: string, subs: openArray[ # copy the rest: add(result, substr(s, i)) -proc parallelReplace*(s: string, subs: openArray[ - tuple[pattern: Regex, repl: string]]): string {.deprecated: - "Deprecated since v0.18.0: Use ``multiReplace`` instead.".} = - ## Returns a modified copy of ``s`` with the substitutions in ``subs`` - ## applied in parallel. - result = multiReplace(s, subs) - proc transformFile*(infile, outfile: string, subs: openArray[tuple[pattern: Regex, repl: string]]) = ## reads in the file ``infile``, performs a parallel replacement (calls diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim index 91f2f41b8..4a6c2510a 100644 --- a/lib/pure/algorithm.nim +++ b/lib/pure/algorithm.nim @@ -223,10 +223,6 @@ proc binarySearch*[T](a: openArray[T], key: T): int = assert binarySearch([0, 1, 4, 2, 3], 4) == 2 binarySearch(a, key, cmp[T]) -proc smartBinarySearch*[T](a: openArray[T], key: T): int {.deprecated: - "Deprecated since v0.18.1; Use 'binarySearch'".} = - binarySearch(a, key, cmp[T]) - const onlySafeCode = true diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 0800cb638..a15442109 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -285,7 +285,7 @@ when defined(windows) or defined(nimdoc): new result result.ioPort = createIoCompletionPort(INVALID_HANDLE_VALUE, 0, 0, 1) result.handles = initHashSet[AsyncFD]() - result.timers.newHeapQueue() + result.timers.clear() result.callbacks = initDeque[proc () {.closure, gcsafe.}](64) var gDisp{.threadvar.}: owned PDispatcher ## Global dispatcher diff --git a/lib/pure/collections/heapqueue.nim b/lib/pure/collections/heapqueue.nim index b0789593a..608fd2920 100644 --- a/lib/pure/collections/heapqueue.nim +++ b/lib/pure/collections/heapqueue.nim @@ -183,14 +183,6 @@ proc `$`*[T](heap: HeapQueue[T]): string = result.addQuoted(x) result.add("]") -proc newHeapQueue*[T](): HeapQueue[T] {.deprecated: - "Deprecated since v0.20.0: use 'initHeapQueue' instead.".} = - initHeapQueue[T]() - -proc newHeapQueue*[T](heap: var HeapQueue[T]) {.deprecated: - "Deprecated since v0.20.0: use 'clear' instead.".} = - heap.clear() - when isMainModule: proc toSortedSeq[T](h: HeapQueue[T]): seq[T] = var tmp = h diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim index 11639df3b..2b270c2cb 100644 --- a/lib/pure/collections/sets.nim +++ b/lib/pure/collections/sets.nim @@ -111,9 +111,7 @@ proc init*[A](s: var HashSet[A], initialSize = defaultInitialSize) = ## * `toHashSet proc <#toHashSet,openArray[A]>`_ runnableExamples: var a: HashSet[int] - assert(not a.isValid) init(a) - assert a.isValid initImpl(s, initialSize) @@ -647,9 +645,7 @@ proc init*[A](s: var OrderedSet[A], initialSize = defaultInitialSize) = ## * `toOrderedSet proc <#toOrderedSet,openArray[A]>`_ runnableExamples: var a: OrderedSet[int] - assert(not a.isValid) init(a) - assert a.isValid initImpl(s, initialSize) @@ -923,21 +919,6 @@ iterator pairs*[A](s: OrderedSet[A]): tuple[a: int, b: A] = -proc isValid*[A](s: OrderedSet[A]): bool {.deprecated: - "Deprecated since v0.20; sets are initialized by default".} = - ## - ## Returns `true` if the set has been initialized (with `initHashSet proc - ## <#initOrderedSet,int>`_ or `init proc <#init,OrderedSet[A],int>`_). - ## - ## **Examples:** - ## - ## .. code-block :: - ## proc savePreferences(options: OrderedSet[string]) = - ## assert options.isValid, "Pass an initialized set!" - ## # Do stuff here, may crash in release builds! - result = s.data.len > 0 - - # ----------------------------------------------------------------------- @@ -945,16 +926,8 @@ proc isValid*[A](s: OrderedSet[A]): bool {.deprecated: when isMainModule and not defined(release): proc testModule() = ## Internal micro test to validate docstrings and such. - block isValidTest: # isValid is deprecated - var options: HashSet[string] - proc savePreferences(options: HashSet[string]) = - assert options.isValid, "Pass an initialized set!" - options = initHashSet[string]() - options.savePreferences - block lenTest: var values: HashSet[int] - assert(not values.isValid) assert values.len == 0 assert values.card == 0 @@ -1046,16 +1019,8 @@ when isMainModule and not defined(release): var b = a.map(proc (x: int): string = $x) assert b == toHashSet(["1", "2", "3"]) - block isValidTest: # isValid is deprecated - var cards: OrderedSet[string] - proc saveTarotCards(cards: OrderedSet[string]) = - assert cards.isValid, "Pass an initialized set!" - cards = initOrderedSet[string]() - cards.saveTarotCards - block lenTest: var values: OrderedSet[int] - assert(not values.isValid) assert values.len == 0 assert values.card == 0 @@ -1119,7 +1084,7 @@ when isMainModule and not defined(release): a.init(4) a.incl(2) a.init - assert a.len == 0 and a.isValid + assert a.len == 0 a = initOrderedSet[int](4) a.incl(2) assert a.len == 1 @@ -1128,7 +1093,7 @@ when isMainModule and not defined(release): b.init(4) b.incl(2) b.init - assert b.len == 0 and b.isValid + assert b.len == 0 b = initHashSet[int](4) b.incl(2) assert b.len == 1 diff --git a/lib/pure/concurrency/cpuload.nim b/lib/pure/concurrency/cpuload.nim index 3ee7336f0..841d58d86 100644 --- a/lib/pure/concurrency/cpuload.nim +++ b/lib/pure/concurrency/cpuload.nim @@ -88,7 +88,7 @@ when not defined(testing) and isMainModule and not defined(nimdoc): proc busyLoop() = while true: - discard random(80) + discard rand(80) os.sleep(100) spawn busyLoop() diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 1ac3ae185..dfd4abea5 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -435,7 +435,7 @@ proc `[]=`*(p: MultipartData, name: string, proc getBoundary(p: MultipartData): string = if p == nil or p.content.len == 0: return while true: - result = $random(int.high) + result = $rand(int.high) for i, entry in p.content: if result in entry.content: break elif i == p.content.high: return diff --git a/lib/pure/math.nim b/lib/pure/math.nim index 6291cddf1..ea44de6d3 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -196,17 +196,6 @@ proc nextPowerOfTwo*(x: int): int {.noSideEffect.} = result = result or (result shr 1) result += 1 + ord(x <= 0) -proc countBits32*(n: int32): int {.noSideEffect, deprecated: - "Deprecated since v0.20.0; use 'bitops.countSetBits' instead".} = - runnableExamples: - doAssert countBits32(7) == 3 - doAssert countBits32(8) == 1 - doAssert countBits32(15) == 4 - doAssert countBits32(16) == 1 - doAssert countBits32(17) == 2 - - bitops.countSetBits(n) - proc sum*[T](x: openArray[T]): T {.noSideEffect.} = ## Computes the sum of the elements in ``x``. ## @@ -611,13 +600,6 @@ when not defined(js): # C ## echo gamma(4.0) # 6.0 ## echo gamma(11.0) # 3628800.0 ## echo gamma(-1.0) # nan - proc tgamma*(x: float32): float32 - {.deprecated: "Deprecated since v0.19.0; use 'gamma' instead", - importc: "tgammaf", header: "".} - proc tgamma*(x: float64): float64 - {.deprecated: "Deprecated since v0.19.0; use 'gamma' instead", - importc: "tgamma", header: "".} - ## The gamma function proc lgamma*(x: float32): float32 {.importc: "lgammaf", header: "".} proc lgamma*(x: float64): float64 {.importc: "lgamma", header: "".} ## Computes the natural log of the gamma function for ``x``. @@ -750,12 +732,6 @@ when not defined(js): # C ## echo trunc(PI) # 3.0 ## echo trunc(-1.85) # -1.0 - proc fmod*(x, y: float32): float32 {.deprecated: "Deprecated since v0.19.0; use 'mod' instead", - importc: "fmodf", header: "".} - proc fmod*(x, y: float64): float64 {.deprecated: "Deprecated since v0.19.0; use 'mod' instead", - importc: "fmod", header: "".} - ## Computes the remainder of ``x`` divided by ``y``. - proc `mod`*(x, y: float32): float32 {.importc: "fmodf", header: "".} proc `mod`*(x, y: float64): float64 {.importc: "fmod", header: "".} ## Computes the modulo operation for float values (the remainder of ``x`` divided by ``y``). @@ -1111,7 +1087,6 @@ when isMainModule and not defined(js) and not windowsCC89: # check gamma function assert(gamma(5.0) == 24.0) # 4! - assert($tgamma(5.0) == $24.0) # 4! assert(lgamma(1.0) == 0.0) # ln(1.0) == 0.0 assert(erf(6.0) > erf(5.0)) assert(erfc(6.0) < erfc(5.0)) @@ -1133,20 +1108,6 @@ when isMainModule: doAssert round(-54.652) ==~ -55.0 doAssert round(-54.352) ==~ -54.0 doAssert round(0.0) ==~ 0.0 - # Round to positive decimal places - doAssert round(-547.652, 1) ==~ -547.7 - doAssert round(547.652, 1) ==~ 547.7 - doAssert round(-547.652, 2) ==~ -547.65 - doAssert round(547.652, 2) ==~ 547.65 - # Round to negative decimal places - doAssert round(547.652, -1) ==~ 550.0 - doAssert round(547.652, -2) ==~ 500.0 - doAssert round(547.652, -3) ==~ 1000.0 - doAssert round(547.652, -4) ==~ 0.0 - doAssert round(-547.652, -1) ==~ -550.0 - doAssert round(-547.652, -2) ==~ -500.0 - doAssert round(-547.652, -3) ==~ -1000.0 - doAssert round(-547.652, -4) ==~ 0.0 block: # splitDecimal() tests doAssert splitDecimal(54.674).intpart ==~ 54.0 diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim index 177476f41..67e24eedc 100644 --- a/lib/pure/nativesockets.nim +++ b/lib/pure/nativesockets.nim @@ -233,22 +233,6 @@ proc createNativeSocket*(domain: Domain = AF_INET, ## by child processes. createNativeSocket(toInt(domain), toInt(sockType), toInt(protocol), inheritable) -proc newNativeSocket*(domain: Domain = AF_INET, - sockType: SockType = SOCK_STREAM, - protocol: Protocol = IPPROTO_TCP): SocketHandle - {.deprecated: "deprecated since v0.18.0; use 'createNativeSocket' instead".} = - ## Creates a new socket; returns `osInvalidSocket` if an error occurs. - createNativeSocket(domain, sockType, protocol) - -proc newNativeSocket*(domain: cint, sockType: cint, - protocol: cint): SocketHandle - {.deprecated: "deprecated since v0.18.0; use 'createNativeSocket' instead".} = - ## Creates a new socket; returns `osInvalidSocket` if an error occurs. - ## - ## Use this overload if one of the enums specified above does - ## not contain what you need. - createNativeSocket(domain, sockType, protocol) - proc bindAddr*(socket: SocketHandle, name: ptr SockAddr, namelen: SockLen): cint = result = bindSocket(socket, name, namelen) diff --git a/lib/pure/random.nim b/lib/pure/random.nim index e36f2be47..e794a4dde 100644 --- a/lib/pure/random.nim +++ b/lib/pure/random.nim @@ -206,30 +206,6 @@ proc skipRandomNumbers*(s: var Rand) = s.a0 = s0 s.a1 = s1 -proc random*(max: int): int {.benign, deprecated: - "Deprecated since v0.18.0; use 'rand' instead".} = - while true: - let x = next(state) - if x < randMax - (randMax mod Ui(max)): - return int(x mod uint64(max)) - -proc random*(max: float): float {.benign, deprecated: - "Deprecated since v0.18.0; use 'rand' instead".} = - let x = next(state) - when defined(js): - result = (float(x) / float(high(uint32))) * max - else: - let u = (0x3FFu64 shl 52u64) or (x shr 12u64) - result = (cast[float](u) - 1.0) * max - -proc random*[T](x: HSlice[T, T]): T {.deprecated: - "Deprecated since v0.18.0; use 'rand' instead".} = - result = T(random(x.b - x.a)) + x.a - -proc random*[T](a: openArray[T]): T {.deprecated: - "Deprecated since v0.18.0; use 'sample' instead".} = - result = a[random(a.low..a.len)] - proc rand*(r: var Rand; max: Natural): int {.benign.} = ## Returns a random integer in the range `0..max` using the given state. ## @@ -362,10 +338,6 @@ proc rand*[T: Ordinal or SomeFloat](x: HSlice[T, T]): T = doAssert rand(1..6) == 6 result = rand(state, x) -proc rand*[T](r: var Rand; a: openArray[T]): T {.deprecated: - "Deprecated since v0.20.0; use 'sample' instead".} = - result = a[rand(r, a.low..a.high)] - proc rand*[T: SomeInteger](t: typedesc[T]): T = ## Returns a random integer in the range `low(T)..high(T)`. ## @@ -395,10 +367,6 @@ proc rand*[T: SomeInteger](t: typedesc[T]): T = else: result = cast[T](state.next) -proc rand*[T](a: openArray[T]): T {.deprecated: - "Deprecated since v0.20.0; use 'sample' instead".} = - result = a[rand(a.low..a.high)] - proc sample*[T](r: var Rand; s: set[T]): T = ## Returns a random element from the set ``s`` using the given state. ## @@ -687,6 +655,6 @@ when isMainModule: # don't use causes integer overflow - doAssert compiles(random[int](low(int) .. high(int))) + doAssert compiles(rand[int](low(int) .. high(int))) main() diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim index c475c1b4f..12a134ef8 100644 --- a/lib/pure/selectors.nim +++ b/lib/pure/selectors.nim @@ -334,22 +334,3 @@ else: include ioselects/ioselectors_select else: include ioselects/ioselectors_poll - -proc register*[T](s: Selector[T], fd: int | SocketHandle, - events: set[Event], data: T) {.deprecated: "use registerHandle instead".} = - ## **Deprecated since v0.18.0:** Use ``registerHandle`` instead. - s.registerHandle(fd, events, data) - -proc setEvent*(ev: SelectEvent) {.deprecated: "use trigger instead".} = - ## Trigger event ``ev``. - ## - ## **Deprecated since v0.18.0:** Use ``trigger`` instead. - ev.trigger() - -proc update*[T](s: Selector[T], fd: int | SocketHandle, - events: set[Event]) {.deprecated: "use updateHandle instead".} = - ## Update file/socket descriptor ``fd``, registered in selector - ## ``s`` with new events set ``event``. - ## - ## **Deprecated since v0.18.0:** Use ``updateHandle`` instead. - s.updateHandle() diff --git a/lib/pure/times.nim b/lib/pure/times.nim index 01eb16c23..7d82733e1 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -581,8 +581,8 @@ proc initDuration*(nanoseconds, microseconds, milliseconds, ## Create a new `Duration <#Duration>`_. runnableExamples: let dur = initDuration(seconds = 1, milliseconds = 1) - doAssert dur.milliseconds == 1 - doAssert dur.seconds == 1 + doAssert dur.inMilliseconds == 1001 + doAssert dur.inSeconds == 1 let seconds = convert(Weeks, Seconds, weeks) + convert(Days, Seconds, days) + @@ -1464,7 +1464,7 @@ type s, ss fff, ffffff, fffffffff t, tt - y, yy, yyy, yyyy, yyyyy + yy, yyyy YYYY uuuu UUUU @@ -1612,11 +1612,8 @@ proc stringToPattern(str: string): FormatPattern = of "fffffffff": result = fffffffff of "t": result = t of "tt": result = tt - of "y": result = y of "yy": result = yy - of "yyy": result = yyy of "yyyy": result = yyyy - of "yyyyy": result = yyyyy of "YYYY": result = YYYY of "uuuu": result = uuuu of "UUUU": result = UUUU @@ -1709,20 +1706,14 @@ proc formatPattern(dt: DateTime, pattern: FormatPattern, result: var string, result.add if dt.hour >= 12: "P" else: "A" of tt: result.add if dt.hour >= 12: "PM" else: "AM" - of y: # Deprecated - result.add $(dt.yearOfEra mod 10) of yy: result.add (dt.yearOfEra mod 100).intToStr(2) - of yyy: # Deprecated - result.add (dt.yearOfEra mod 1000).intToStr(3) of yyyy: let year = dt.yearOfEra if year < 10000: result.add year.intToStr(4) else: result.add '+' & $year - of yyyyy: # Deprecated - result.add (dt.yearOfEra mod 100_000).intToStr(5) of YYYY: if dt.year < 1: result.add $(abs(dt.year) + 1) @@ -1933,8 +1924,6 @@ proc parsePattern(input: string, pattern: FormatPattern, i: var int, i.inc 2 else: result = false - of y, yyy, yyyyy: - raiseAssert "Pattern is invalid for parsing: " & $pattern of Lit: doAssert false, "Can't happen" proc toDateTime(p: ParsedTime, zone: Timezone, f: TimeFormat, @@ -2631,167 +2620,11 @@ when not defined(js): else: result = toFloat(int(getClock())) / toFloat(clocksPerSec) + # # Deprecations # -proc countLeapYears*(yearSpan: int): int - {.deprecated.} = - ## Returns the number of leap years spanned by a given number of years. - ## - ## **Note:** For leap years, start date is assumed to be 1 AD. - ## counts the number of leap years up to January 1st of a given year. - ## Keep in mind that if specified year is a leap year, the leap day - ## has not happened before January 1st of that year. - # Deprecated since v0.20.0 - (yearSpan - 1) div 4 - (yearSpan - 1) div 100 + (yearSpan - 1) div 400 - -proc countDays*(yearSpan: int): int - {.deprecated.} = - ## Returns the number of days spanned by a given number of years. - # Deprecated since v0.20.0 - (yearSpan - 1) * 365 + countLeapYears(yearSpan) - -proc countYears*(daySpan: int): int - {.deprecated.} = - ## Returns the number of years spanned by a given number of days. - # Deprecated since v0.20.0 - ((daySpan - countLeapYears(daySpan div 365)) div 365) - -proc countYearsAndDays*(daySpan: int): tuple[years: int, days: int] - {.deprecated.} = - ## Returns the number of years spanned by a given number of days and the - ## remainder as days. - # Deprecated since v0.20.0 - let days = daySpan - countLeapYears(daySpan div 365) - result.years = days div 365 - result.days = days mod 365 - -proc toTimeInterval*(time: Time): TimeInterval - {.deprecated: "Use 'between' instead".} = - ## Converts a Time to a TimeInterval. To be used when diffing times. - # Deprecated since version 0.20.0 - runnableExamples: - let a = fromUnix(10) - let b = fromUnix(1_500_000_000) - let ti = b.toTimeInterval() - a.toTimeInterval() - doAssert a + ti == b - var dt = time.local - initTimeInterval(dt.nanosecond, 0, 0, dt.second, dt.minute, dt.hour, - dt.monthday, 0, dt.month.ord - 1, dt.year) - -proc weeks*(dur: Duration): int64 - {.inline, deprecated: "Use 'inWeeks' instead".} = - ## Number of whole weeks represented by the duration. - # Deprecated since version v0.20.0 - runnableExamples: - let dur = initDuration(weeks = 1, days = 2, hours = 3, minutes = 4) - doAssert dur.weeks == 1 - dur.inWeeks - -proc days*(dur: Duration): int64 - {.inline, deprecated: "Use 'inDays' instead".} = - ## Number of whole days represented by the duration. - # Deprecated since version v0.20.0 - runnableExamples: - let dur = initDuration(weeks = 1, days = 2, hours = 3, minutes = 4) - doAssert dur.days == 9 - dur.inDays - -proc hours*(dur: Duration): int64 - {.inline, deprecated: "Use 'inHours' instead".} = - ## Number of whole hours represented by the duration. - # Deprecated since version v0.20.0 - runnableExamples: - let dur = initDuration(days = 1, hours = 2, minutes = 3) - doAssert dur.hours == 26 - dur.inHours - -proc minutes*(dur: Duration): int64 - {.inline, deprecated: "Use 'inMinutes' instead".} = - ## Number of whole minutes represented by the duration. - # Deprecated since version v0.20.0 - runnableExamples: - let dur = initDuration(days = 1, hours = 2, minutes = 3) - doAssert dur.minutes == 1563 - dur.inMinutes - -proc seconds*(dur: Duration): int64 - {.inline, deprecated: "Use 'inSeconds' instead".} = - ## Number of whole seconds represented by the duration. - # Deprecated since version v0.20.0 - runnableExamples: - let dur = initDuration(minutes = 10, seconds = 30) - doAssert dur.seconds == 630 - dur.inSeconds - -proc milliseconds*(dur: Duration): int {.inline, deprecated.} = - ## Number of whole milliseconds represented by the **fractional** - ## part of the duration. - # Deprecated since version v0.20.0 - runnableExamples: - let dur = initDuration(minutes = 5, seconds = 6, milliseconds = 7, - microseconds = 8, nanoseconds = 9) - doAssert dur.milliseconds == 7 - result = convert(Nanoseconds, Milliseconds, dur.nanosecond) - -proc microseconds*(dur: Duration): int {.inline, deprecated.} = - ## Number of whole microseconds represented by the **fractional** - ## part of the duration. - # Deprecated since version v0.20.0 - runnableExamples: - let dur = initDuration(minutes = 5, seconds = 6, milliseconds = 7, - microseconds = 8, nanoseconds = 9) - doAssert dur.microseconds == 7008 - result = convert(Nanoseconds, Microseconds, dur.nanosecond) - -proc nanoseconds*(dur: Duration): NanosecondRange {.inline, deprecated.} = - ## Number of whole microseconds represented by the **fractional** - ## part of the duration. - # Deprecated since version v0.20.0 - runnableExamples: - let dur = initDuration(minutes = 5, seconds = 6, milliseconds = 7, - microseconds = 8, nanoseconds = 9) - doAssert dur.nanoseconds == 7008009 - dur.nanosecond - -proc fractional*(dur: Duration): Duration {.inline, deprecated.} = - ## The fractional part of `dur`, as a duration. - # Deprecated since version v0.20.0 - runnableExamples: - let dur = initDuration(minutes = 5, seconds = 6, milliseconds = 7, - microseconds = 8, nanoseconds = 9) - doAssert dur.fractional == initDuration(milliseconds = 7, microseconds = 8, - nanoseconds = 9) - initDuration(nanoseconds = dur.nanosecond) - -proc fromSeconds*(since1970: float): Time - {.tags: [], raises: [], benign, deprecated: "Use `fromUnixFloat or 'fromUnix' instead".} = - ## Takes a float which contains the number of seconds since the unix epoch and - ## returns a time object. - # Deprecated since v0.18.0 - fromUnixFloat(since1970) - -proc fromSeconds*(since1970: int64): Time - {.tags: [], raises: [], benign, deprecated: "Use 'fromUnix' instead".} = - ## Takes an int which contains the number of seconds since the unix epoch and - ## returns a time object. - # Deprecated since v0.18.0 - fromUnix(since1970) - -proc toSeconds*(time: Time): float - {.tags: [], raises: [], benign, deprecated: "Use 'toUnixFloat' or 'toUnix' instead".} = - ## Returns the time in seconds since the unix epoch, with subsecond resolution. - # Deprecated since v0.18.0 - toUnixFloat(time) - -proc getGMTime*(time: Time): DateTime - {.tags: [], raises: [], benign, deprecated: "Use 'utc' instead".} = - ## Converts the calendar time `time` to broken-down time representation, - ## expressed in Coordinated Universal Time (UTC). - # Deprecated since v0.18.0 - time.utc - proc `nanosecond=`*(dt: var DateTime, value: NanosecondRange) {.deprecated: "Deprecated since v1.3.1".} = dt.nanosecond = value diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim index 497d5e465..b550b0eac 100644 --- a/lib/pure/unicode.nim +++ b/lib/pure/unicode.nim @@ -1169,88 +1169,6 @@ proc alignLeft*(s: string, count: Natural, padding = ' '.Rune): string {. else: result = s -# ----------------------------------------------------------------------------- -# deprecated - -template runeCaseCheck(s, runeProc, skipNonAlpha) = - ## Common code for rune.isLower and rune.isUpper. - if len(s) == 0: return false - var - i = 0 - rune: Rune - hasAtleastOneAlphaRune = false - while i < len(s): - fastRuneAt(s, i, rune, doInc = true) - if skipNonAlpha: - var runeIsAlpha = isAlpha(rune) - if not hasAtleastOneAlphaRune: - hasAtleastOneAlphaRune = runeIsAlpha - if runeIsAlpha and (not runeProc(rune)): - return false - else: - if not runeProc(rune): - return false - return if skipNonAlpha: hasAtleastOneAlphaRune else: true - -proc isLower*(s: string, skipNonAlpha: bool): bool {. - deprecated: "Deprecated since version 0.20 since its semantics are unclear".} = - ## **Deprecated since version 0.20 since its semantics are unclear** - ## - ## Checks whether ``s`` is lower case. - ## - ## If ``skipNonAlpha`` is true, returns true if all alphabetical - ## runes in ``s`` are lower case. Returns false if none of the - ## runes in ``s`` are alphabetical. - ## - ## If ``skipNonAlpha`` is false, returns true only if all runes in - ## ``s`` are alphabetical and lower case. - ## - ## For either value of ``skipNonAlpha``, returns false if ``s`` is - ## an empty string. - runeCaseCheck(s, isLower, skipNonAlpha) - -proc isUpper*(s: string, skipNonAlpha: bool): bool {. - deprecated: "Deprecated since version 0.20 since its semantics are unclear".} = - ## **Deprecated since version 0.20 since its semantics are unclear** - ## - ## Checks whether ``s`` is upper case. - ## - ## If ``skipNonAlpha`` is true, returns true if all alphabetical - ## runes in ``s`` are upper case. Returns false if none of the - ## runes in ``s`` are alphabetical. - ## - ## If ``skipNonAlpha`` is false, returns true only if all runes in - ## ``s`` are alphabetical and upper case. - ## - ## For either value of ``skipNonAlpha``, returns false if ``s`` is - ## an empty string. - runeCaseCheck(s, isUpper, skipNonAlpha) - -proc isTitle*(s: string): bool {.noSideEffect, rtl, extern: "nuc$1Str", - deprecated: "Deprecated since version 0.20 since its semantics are unclear".} = - ## **Deprecated since version 0.20 since its semantics are unclear** - ## - ## Checks whether or not ``s`` is a unicode title. - ## - ## Returns true if the first character in each word inside ``s`` - ## are upper case and there is at least one character in ``s``. - if s.len == 0: - return false - result = true - var - i = 0 - rune: Rune - var firstRune = true - - while i < len(s) and result: - fastRuneAt(s, i, rune, doInc = true) - if not rune.isWhiteSpace() and firstRune: - result = rune.isUpper() and result - firstRune = false - elif rune.isWhiteSpace(): - firstRune = true - - when isMainModule: diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim index bb4e2867c..bea7d9c44 100644 --- a/lib/pure/unittest.nim +++ b/lib/pure/unittest.nim @@ -416,8 +416,7 @@ proc ensureInitialized() = if formatters.len == 0: formatters = @[OutputFormatter(defaultConsoleFormatter())] - if not disabledParamFiltering and not testsFilters.isValid: - testsFilters.init() + if not disabledParamFiltering: when declared(paramCount): # Read tests to run from the command line. for i in 1 .. paramCount(): @@ -712,7 +711,7 @@ macro expect*(exceptions: varargs[typed], body: untyped): untyped = ## import math, random ## proc defectiveRobot() = ## randomize() - ## case random(1..4) + ## case rand(1..4) ## of 1: raise newException(OSError, "CANNOT COMPUTE!") ## of 2: discard parseInt("Hello World!") ## of 3: raise newException(IOError, "I can't do that Dave.") diff --git a/lib/system/arithmetics.nim b/lib/system/arithmetics.nim index 8d176985d..f6c1b69ff 100644 --- a/lib/system/arithmetics.nim +++ b/lib/system/arithmetics.nim @@ -481,18 +481,10 @@ proc `+=`*[T: SomeInteger](x: var T, y: T) {. magic: "Inc", noSideEffect.} ## Increments an integer. -proc `+=`*[T: enum|bool](x: var T, y: T) {. - magic: "Inc", noSideEffect, deprecated: "use `inc` instead".} - ## **Deprecated since v0.20**: use `inc` instead. - proc `-=`*[T: SomeInteger](x: var T, y: T) {. magic: "Dec", noSideEffect.} ## Decrements an integer. -proc `-=`*[T: enum|bool](x: var T, y: T) {. - magic: "Dec", noSideEffect, deprecated: "0.20.0, use `dec` instead".} - ## **Deprecated since v0.20**: use `dec` instead. - proc `*=`*[T: SomeInteger](x: var T, y: T) {. inline, noSideEffect.} = ## Binary `*=` operator for integers. diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 5a28fa3dd..6da644ce3 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -34,7 +34,6 @@ pkg1 "coco", true pkg1 "combparser" pkg1 "compactdict" pkg1 "comprehension", false, "nimble test", "https://github.com/alehander42/comprehension" -pkg1 "criterion" pkg1 "dashing", false, "nim c tests/functional.nim" pkg1 "delaunay" pkg1 "docopt" diff --git a/tests/ccgbugs2/tinefficient_const_table.nim b/tests/ccgbugs2/tinefficient_const_table.nim new file mode 100644 index 000000000..a1b89e220 --- /dev/null +++ b/tests/ccgbugs2/tinefficient_const_table.nim @@ -0,0 +1,27 @@ +discard """ + output: '''a +long +list +of +words''' + cmd: r"nim c --hints:on $options -d:release $file" + ccodecheck: "! @'genericSeqAssign'" + target: "c" +""" + +# bug #4354 +import tables +import sets +import strutils + +#const FRUITS = ["banana", "apple", "grapes"] +#let FRUITS = ["banana", "apple", "grapes"].toHashSet +const FRUITS = {"banana":0, "apple":0, "grapes":0}.toTable + +proc main() = + let L = "a long list of words".split() + for word in L: + if word notin FRUITS: + echo(word) + +main() diff --git a/tests/collections/thashsets.nim b/tests/collections/thashsets.nim index bc387d292..4ab3a66b7 100644 --- a/tests/collections/thashsets.nim +++ b/tests/collections/thashsets.nim @@ -79,9 +79,8 @@ block hashForHashedSet: let seq1 = "This is the test." seq2 = "the test is This." - s1 = seq1.toSet() - s2 = seq2.toSet() - var hashSeq: seq[Hash] = @[] + s1 = seq1.toHashSet() + s2 = seq2.toHashSet() doAssert s1 == s2 doAssert hash(s1) == hash(s2) diff --git a/tests/deps/jester-#head/jester.nim b/tests/deps/jester-#head/jester.nim index d9db50856..1118214c2 100644 --- a/tests/deps/jester-#head/jester.nim +++ b/tests/deps/jester-#head/jester.nim @@ -1,6 +1,6 @@ # Copyright (C) 2015 Dominik Picheta # MIT License - Look at license.txt for details. -import net, strtabs, re, tables, parseutils, os, strutils, uri, +import net, strtabs, re, tables, os, strutils, uri, times, mimetypes, asyncnet, asyncdispatch, macros, md5, logging, httpcore, asyncfile, macrocache, json, options, strformat @@ -23,6 +23,7 @@ export SameSite when useHttpBeast: import httpbeast except Settings, Request import options + from nativesockets import close else: import asynchttpserver except Request @@ -72,14 +73,14 @@ type of RouteCode: data: ResponseData -const jesterVer = "0.4.0" +const jesterVer = "0.4.3" proc toStr(headers: Option[RawHeaders]): string = return $newHttpHeaders(headers.get(@({:}))) proc createHeaders(headers: RawHeaders): string = result = "" - if headers != nil: + if headers.len > 0: for header in headers: let (key, value) = header result.add(key & ": " & value & "\c\L") @@ -98,22 +99,24 @@ proc unsafeSend(request: Request, content: string) = proc send( request: Request, code: HttpCode, headers: Option[RawHeaders], body: string -) = +): Future[void] = when useHttpBeast: let h = if headers.isNone: "" else: headers.get().createHeaders request.getNativeReq.send(code, body, h) + var fut = newFuture[void]() + complete(fut) + return fut else: - # TODO: This may cause issues if we send too fast. - asyncCheck request.getNativeReq.respond( + return request.getNativeReq.respond( code, body, newHttpHeaders(headers.get(@({:}))) ) proc statusContent(request: Request, status: HttpCode, content: string, - headers: Option[RawHeaders]) = + headers: Option[RawHeaders]): Future[void] = try: - send(request, status, headers, content) + result = send(request, status, headers, content) when not defined(release): logging.debug(" $1 $2" % [$status, toStr(headers)]) except: @@ -174,7 +177,11 @@ proc sendStaticIfExists( return Http403 let fileSize = getFileSize(p) - let mimetype = req.settings.mimes.getMimetype(p.splitFile.ext[1 .. ^1]) + let ext = p.splitFile.ext + let mimetype = req.settings.mimes.getMimetype( + if ext.len > 0: ext[1 .. ^1] + else: "" + ) if fileSize < 10_000_000: # 10 mb var file = readFile(p) @@ -183,18 +190,18 @@ proc sendStaticIfExists( # If the user has a cached version of this file and it matches our # version, let them use it if req.headers.hasKey("If-None-Match") and req.headers["If-None-Match"] == hashed: - req.statusContent(Http304, "", none[RawHeaders]()) + await req.statusContent(Http304, "", none[RawHeaders]()) else: - req.statusContent(Http200, file, some(@({ - "Content-Type": mimetype, - "ETag": hashed - }))) + await req.statusContent(Http200, file, some(@({ + "Content-Type": mimetype, + "ETag": hashed + }))) else: let headers = @({ "Content-Type": mimetype, "Content-Length": $fileSize }) - req.statusContent(Http200, "", some(headers)) + await req.statusContent(Http200, "", some(headers)) var fileStream = newFutureStream[string]("sendStaticIfExists") var file = openAsync(p, fmRead) @@ -216,13 +223,22 @@ proc sendStaticIfExists( # If we get to here then no match could be found. return Http404 +proc close*(request: Request) = + ## Closes client socket connection. + ## + ## Routes using this procedure must enable raw mode. + let nativeReq = request.getNativeReq() + when useHttpBeast: + nativeReq.forget() + nativeReq.client.close() + proc defaultErrorFilter(error: RouteError): ResponseData = case error.kind of RouteException: let e = error.exc let traceback = getStackTrace(e) var errorMsg = e.msg - if errorMsg.isNil: errorMsg = "(nil)" + if errorMsg.len == 0: errorMsg = "(empty)" let error = traceback & errorMsg logging.error(error) @@ -295,9 +311,11 @@ proc handleFileRequest( jes.settings.staticDir / cgi.decodeUrl(req.pathInfo) ) - # Verify that this isn't outside our static` dir. + # Verify that this isn't outside our static dir. var status = Http400 - if path.splitFile.dir.startsWith(jes.settings.staticDir): + let pathDir = path.splitFile.dir / "" + let staticDir = jes.settings.staticDir / "" + if pathDir.startsWith(staticDir): if existsDir(path): status = await sendStaticIfExists( req, @@ -347,7 +365,7 @@ proc handleRequestSlow( not dispatchedError and respData.content.len == 0: respData = await dispatchError(jes, req, initRouteError(respData)) - statusContent( + await statusContent( req, respData.code, respData.content, @@ -368,7 +386,7 @@ proc handleRequest(jes: Jester, httpReq: NativeRequest): Future[void] = if likely(jes.matchers.len == 1 and not jes.matchers[0].async): let respData = jes.matchers[0].syncProc(req) if likely(respData.matched): - statusContent( + return statusContent( req, respData.code, respData.content, @@ -383,6 +401,8 @@ proc handleRequest(jes: Jester, httpReq: NativeRequest): Future[void] = let respDataFut = dispatchError(jes, req, initRouteError(exc)) return handleRequestSlow(jes, req, respDataFut, true) + assert(not result.isNil, "Expected handleRequest to return a valid future.") + proc newSettings*( port = Port(5000), staticDir = getCurrentDir() / "public", appName = "", bindAddr = "", reusePort = false, @@ -461,14 +481,19 @@ proc serve*( ] ) else: - logging.info("Jester is making jokes at http://0.0.0.0:$1$2" % - [$self.settings.port, self.settings.appName]) + when defined(windows): + logging.info("Jester is making jokes at http://127.0.0.1:$1$2 (all interfaces)" % + [$self.settings.port, self.settings.appName]) + else: + logging.info("Jester is making jokes at http://0.0.0.0:$1$2" % + [$self.settings.port, self.settings.appName]) var jes = self when useHttpBeast: run( proc (req: httpbeast.Request): Future[void] = - result = handleRequest(jes, req), + {.gcsafe.}: + result = handleRequest(jes, req), httpbeast.initSettings(self.settings.port, self.settings.bindAddr) ) else: @@ -484,14 +509,6 @@ proc serve*( asyncCheck serveFut runForever() -template resp*(code: HttpCode, - headers: openarray[tuple[key, value: string]], - content: string): typed = - ## Sets ``(code, headers, content)`` as the response. - bind TCActionSend, newHttpHeaders - result = (TCActionSend, code, headers.newHttpHeaders.some(), content, true) - break route - template setHeader(headers: var Option[RawHeaders], key, value: string): typed = bind isNone if isNone(headers): @@ -509,6 +526,17 @@ template setHeader(headers: var Option[RawHeaders], key, value: string): typed = # Add key if it doesn't exist. headers = some(h & @({key: value})) +template resp*(code: HttpCode, + headers: openarray[tuple[key, val: string]], + content: string): typed = + ## Sets ``(code, headers, content)`` as the response. + bind TCActionSend + result = (TCActionSend, code, none[RawHeaders](), content, true) + for header in headers: + setHeader(result[2], header[0], header[1]) + break route + + template resp*(content: string, contentType = "text/html;charset=utf-8"): typed = ## Sets ``content`` as the response; ``Http200`` as the status code ## and ``contentType`` as the Content-Type. @@ -687,7 +715,8 @@ proc daysForward*(days: int): DateTime = return getTime().utc + initTimeInterval(days = days) template setCookie*(name, value: string, expires="", - sameSite: SameSite=Lax): typed = + sameSite: SameSite=Lax, secure = false, + httpOnly = false, domain = "", path = "") = ## Creates a cookie which stores ``value`` under ``name``. ## ## The SameSite argument determines the level of CSRF protection that @@ -695,7 +724,7 @@ template setCookie*(name, value: string, expires="", ## should protect you from most vulnerabilities. Note that this is only ## supported by some browsers: ## https://caniuse.com/#feat=same-site-cookie-attribute - let newCookie = makeCookie(name, value, expires) + let newCookie = makeCookie(name, value, expires, domain, path, secure, httpOnly, sameSite) if isSome(result[2]) and (let headers = result[2].get(); headers.toTable.hasKey("Set-Cookie")): result[2] = some(headers & @({"Set-Cookie": newCookie})) @@ -703,9 +732,12 @@ template setCookie*(name, value: string, expires="", setHeader(result[2], "Set-Cookie", newCookie) template setCookie*(name, value: string, expires: DateTime, - sameSite: SameSite=Lax): typed = + sameSite: SameSite=Lax, secure = false, + httpOnly = false, domain = "", path = "") = ## Creates a cookie which stores ``value`` under ``name``. - setCookie(name, value, format(expires, "ddd',' dd MMM yyyy HH:mm:ss 'GMT'")) + setCookie(name, value, + format(expires.utc, "ddd',' dd MMM yyyy HH:mm:ss 'GMT'"), + sameSite, secure, httpOnly, domain, path) proc normalizeUri*(uri: string): string = ## Remove any trailing ``/``. @@ -721,7 +753,10 @@ proc checkAction*(respData: var ResponseData): bool = of TCActionPass: result = false of TCActionNothing: - assert(false) + raise newException( + ValueError, + "Missing route action, did you forget to use `resp` in your route?" + ) proc skipDo(node: NimNode): NimNode {.compiletime.} = if node.kind == nnkDo: @@ -764,14 +799,14 @@ proc ctParsePattern(pattern, pathPrefix: string): NimNode {.compiletime.} = newStrLitNode(node.text), newIdentNode(if node.optional: "true" else: "false")) -template setDefaultResp*(): typed = +template setDefaultResp*() = # TODO: bindSym this in the 'routes' macro and put it in each route bind TCActionNothing, newHttpHeaders result.action = TCActionNothing result.code = Http200 result.content = "" -template declareSettings(): typed {.dirty.} = +template declareSettings() {.dirty.} = when not declaredInScope(settings): var settings = newSettings() @@ -1299,7 +1334,7 @@ proc routesEx(name: string, body: NimNode): NimNode = for branch in exceptionBranches: stmts.add(newIfStmt(branch)) errorHandlerProc[6][0][1][^1][1][1][0] = stmts - if httpCodeBranches.len > 1: + if httpCodeBranches.len != 0: var stmts = newStmtList() for branch in httpCodeBranches: stmts.add(newIfStmt(branch)) @@ -1313,7 +1348,7 @@ proc routesEx(name: string, body: NimNode): NimNode = # echo toStrLit(result) # echo treeRepr(result) -macro routes*(body: untyped): typed = +macro routes*(body: untyped) = result = routesEx("match", body) let jesIdent = genSym(nskVar, "jes") let matchIdent = newIdentNode("match") @@ -1329,13 +1364,13 @@ macro routes*(body: untyped): typed = serve(`jesIdent`) ) -macro router*(name: untyped, body: untyped): typed = +macro router*(name: untyped, body: untyped) = if name.kind != nnkIdent: error("Need an ident.", name) - routesEx($name.ident, body) + routesEx(strVal(name), body) -macro settings*(body: untyped): typed = +macro settings*(body: untyped) = #echo(treeRepr(body)) expectKind(body, nnkStmtList) diff --git a/tests/deps/jester-#head/jester.nimble b/tests/deps/jester-#head/jester.nimble index 08e990bd7..a1306c525 100644 --- a/tests/deps/jester-#head/jester.nimble +++ b/tests/deps/jester-#head/jester.nimble @@ -1,6 +1,6 @@ # Package -version = "0.4.0" # Be sure to update jester.jesterVer too! +version = "0.4.3" # Be sure to update jester.jesterVer too! author = "Dominik Picheta" description = "A sinatra-like web framework for Nim." license = "MIT" @@ -13,10 +13,10 @@ skipDirs = @["tests"] requires "nim >= 0.18.1" when not defined(windows): - requires "httpbeast >= 0.2.0" + requires "httpbeast >= 0.2.2" # For tests -requires "asynctools" +requires "https://github.com/timotheecour/asynctools#pr_fix_compilation" task test, "Runs the test suite.": - exec "nimble c -y -r tests/tester" \ No newline at end of file + exec "nimble c -y -r tests/tester" diff --git a/tests/deps/jester-#head/jester/patterns.nim b/tests/deps/jester-#head/jester/patterns.nim index c827fbc7f..03d41796e 100644 --- a/tests/deps/jester-#head/jester/patterns.nim +++ b/tests/deps/jester-#head/jester/patterns.nim @@ -15,7 +15,7 @@ type proc parsePattern*(pattern: string): Pattern = result = @[] template addNode(result: var Pattern, theT: NodeType, theText: string, - isOptional: bool): typed = + isOptional: bool) = block: var newNode: Node newNode.typ = theT @@ -120,7 +120,7 @@ proc match*(pattern: Pattern, s: string): if s.len != i: result.matched = false -when true: +when isMainModule: let f = parsePattern("/show/@id/test/@show?/?") doAssert match(f, "/show/12/test/hallo/").matched doAssert match(f, "/show/2131726/test/jjjuuwąąss").matched diff --git a/tests/deps/jester-#head/jester/private/utils.nim b/tests/deps/jester-#head/jester/private/utils.nim index 16dce61fe..2d1390955 100644 --- a/tests/deps/jester-#head/jester/private/utils.nim +++ b/tests/deps/jester-#head/jester/private/utils.nim @@ -21,7 +21,7 @@ type JesterError* = object of Exception proc parseUrlQuery*(query: string, result: var Table[string, string]) - {.deprecated: "use stdlib".} = + {.deprecated: "use stdlib cgi/decodeData".} = var i = 0 i = query.skip("?") while i < query.len()-1: @@ -192,4 +192,3 @@ when false: var r = {:}.newStringTable parseUrlQuery("FirstName=Mickey", r) echo r - diff --git a/tests/deps/jester-#head/jester/request.nim b/tests/deps/jester-#head/jester/request.nim index 1b837d728..7c6a1a961 100644 --- a/tests/deps/jester-#head/jester/request.nim +++ b/tests/deps/jester-#head/jester/request.nim @@ -61,6 +61,7 @@ proc reqMethod*(req: Request): HttpMethod = req.req.httpMethod.get() else: req.req.reqMethod + proc reqMeth*(req: Request): HttpMethod {.deprecated.} = req.reqMethod diff --git a/tests/gc/gcleak5.nim b/tests/gc/gcleak5.nim index 6ab50e19e..f1913831b 100644 --- a/tests/gc/gcleak5.nim +++ b/tests/gc/gcleak5.nim @@ -9,7 +9,7 @@ proc main = for ii in 0..50_000: #while true: var t = getTime() - var g = t.getGMTime() + var g = t.utc() #echo isOnStack(addr g) if i mod 100 == 0: diff --git a/tests/manyloc/argument_parser/argument_parser.nim b/tests/manyloc/argument_parser/argument_parser.nim index 459e38674..136d3e06b 100644 --- a/tests/manyloc/argument_parser/argument_parser.nim +++ b/tests/manyloc/argument_parser/argument_parser.nim @@ -447,7 +447,7 @@ proc build_help*(expected: seq[Tparameter_specification] = @[], # First generate the joined version of input parameters in a list. var - seen = initSet[string]() + seen = initHashSet[string]() prefixes: seq[string] = @[] helps: seq[string] = @[] for key in keys: diff --git a/tests/manyloc/keineschweine/keineschweine.nim b/tests/manyloc/keineschweine/keineschweine.nim index b6fd3cc19..6d8e48f24 100644 --- a/tests/manyloc/keineschweine/keineschweine.nim +++ b/tests/manyloc/keineschweine/keineschweine.nim @@ -486,7 +486,7 @@ when defined(DebugKeys): activeVehicle.body.setPos mouseToSpace()) ingameClient.registerHandler(KeyY, down, proc() = const looloo = ["Asteroid1", "Asteroid2"] - addObject(looloo[random(looloo.len)])) + addObject(looloo[rand(looloo.len)])) ingameClient.registerHandler(KeyO, down, proc() = if objects.len == 0: echo "Objects is empty" diff --git a/tests/manyloc/nake/nakefile.nim b/tests/manyloc/nake/nakefile.nim index 0b5055512..91dd61726 100644 --- a/tests/manyloc/nake/nakefile.nim +++ b/tests/manyloc/nake/nakefile.nim @@ -63,7 +63,7 @@ task "release", "release build": ## zip up all the files and such or something useful here task "testskel", "create skeleton test dir for testing": - let dirname = "test-" & $random(5000) + let dirname = "test-" & $rand(5000) removeDir dirName createDir dirName/"data/fnt" copyFile "data/fnt/LiberationMono-Regular", dirName/"data/fnt/LiberationMono-Regular.ttf" @@ -133,7 +133,7 @@ task "download", "download game assets": else: ## this crashes, dunno why var z: TZipArchive - destDir = getCurrentDir()/("unzip" & $random(5000)) + destDir = getCurrentDir()/("unzip" & $rand(5000)) if not z.open(path, fmRead): echo "Could not open zip, bad download?" return diff --git a/tests/niminaction/Chapter7/Tweeter/src/database.nim b/tests/niminaction/Chapter7/Tweeter/src/database.nim index 3785ea307..bd6667f70 100644 --- a/tests/niminaction/Chapter7/Tweeter/src/database.nim +++ b/tests/niminaction/Chapter7/Tweeter/src/database.nim @@ -50,7 +50,7 @@ proc post*(database: Database, message: Message) = raise newException(ValueError, "Message has to be less than 140 characters.") database.db.exec(sql"INSERT INTO Message VALUES (?, ?, ?);", #<2> - message.username, $message.time.toSeconds().int, message.msg) #<3> + message.username, $message.time.toUnix().int, message.msg) #<3> proc follow*(database: Database, follower: User, user: User) = database.db.exec(sql"INSERT INTO Following VALUES (?, ?);",#<2> @@ -90,4 +90,4 @@ proc findMessages*(database: Database, usernames: seq[string], "ORDER BY time DESC LIMIT " & $limit), usernames) for row in messages: - result.add(Message(username: row[0], time: fromSeconds(row[1].parseInt), msg: row[2])) + result.add(Message(username: row[0], time: fromUnix(row[1].parseInt), msg: row[2])) diff --git a/tests/niminaction/Chapter7/Tweeter/src/tweeter.nim b/tests/niminaction/Chapter7/Tweeter/src/tweeter.nim index 12aaf49b8..fe39278fb 100644 --- a/tests/niminaction/Chapter7/Tweeter/src/tweeter.nim +++ b/tests/niminaction/Chapter7/Tweeter/src/tweeter.nim @@ -51,7 +51,7 @@ routes: redirect(uri("/" & @"target")) post "/login": - setCookie("username", @"username", getTime().getGMTime() + 2.hours) + setCookie("username", @"username", getTime().utc() + 2.hours) redirect("/") post "/createMessage": diff --git a/tests/niminaction/Chapter7/Tweeter/src/views/user.nim b/tests/niminaction/Chapter7/Tweeter/src/views/user.nim index 18f3713b3..4abcf440d 100644 --- a/tests/niminaction/Chapter7/Tweeter/src/views/user.nim +++ b/tests/niminaction/Chapter7/Tweeter/src/views/user.nim @@ -33,7 +33,7 @@ #for message in messages:
    ${message.username} - ${message.time.getGMTime().format("HH:mm MMMM d',' yyyy")} + ${message.time.utc().format("HH:mm MMMM d',' yyyy")}

    ${message.msg}

    #end for diff --git a/tests/stdlib/tstdlib_various.nim b/tests/stdlib/tstdlib_various.nim index 42ed4fda2..cddd43f6e 100644 --- a/tests/stdlib/tstdlib_various.nim +++ b/tests/stdlib/tstdlib_various.nim @@ -60,15 +60,15 @@ block tcritbits: block testequivalence: - doAssert(toSet(@[1,2,3]) <= toSet(@[1,2,3,4]), "equivalent or subset") - doAssert(toSet(@[1,2,3]) <= toSet(@[1,2,3]), "equivalent or subset") - doAssert((not(toSet(@[1,2,3]) <= toSet(@[1,2]))), "equivalent or subset") - doAssert(toSet(@[1,2,3]) <= toSet(@[1,2,3,4]), "strict subset") - doAssert((not(toSet(@[1,2,3]) < toSet(@[1,2,3]))), "strict subset") - doAssert((not(toSet(@[1,2,3]) < toSet(@[1,2]))), "strict subset") - doAssert((not(toSet(@[1,2,3]) == toSet(@[1,2,3,4]))), "==") - doAssert(toSet(@[1,2,3]) == toSet(@[1,2,3]), "==") - doAssert((not(toSet(@[1,2,3]) == toSet(@[1,2]))), "==") + doAssert(toHashSet(@[1,2,3]) <= toHashSet(@[1,2,3,4]), "equivalent or subset") + doAssert(toHashSet(@[1,2,3]) <= toHashSet(@[1,2,3]), "equivalent or subset") + doAssert((not(toHashSet(@[1,2,3]) <= toHashSet(@[1,2]))), "equivalent or subset") + doAssert(toHashSet(@[1,2,3]) <= toHashSet(@[1,2,3,4]), "strict subset") + doAssert((not(toHashSet(@[1,2,3]) < toHashSet(@[1,2,3]))), "strict subset") + doAssert((not(toHashSet(@[1,2,3]) < toHashSet(@[1,2]))), "strict subset") + doAssert((not(toHashSet(@[1,2,3]) == toHashSet(@[1,2,3,4]))), "==") + doAssert(toHashSet(@[1,2,3]) == toHashSet(@[1,2,3]), "==") + doAssert((not(toHashSet(@[1,2,3]) == toHashSet(@[1,2]))), "==") diff --git a/tests/stdlib/tunittest.nim b/tests/stdlib/tunittest.nim index b0efdb617..9ef689e32 100644 --- a/tests/stdlib/tunittest.nim +++ b/tests/stdlib/tunittest.nim @@ -47,10 +47,10 @@ test "unittest multiple requires": require(true) -import math, random +import random from strutils import parseInt proc defectiveRobot() = - case random(1..4) + case rand(1..4) of 1: raise newException(OSError, "CANNOT COMPUTE!") of 2: discard parseInt("Hello World!") of 3: raise newException(IOError, "I can't do that Dave.") diff --git a/tests/template/template_various.nim b/tests/template/template_various.nim index e8202a2e0..75226ea2d 100644 --- a/tests/template/template_various.nim +++ b/tests/template/template_various.nim @@ -107,7 +107,7 @@ block tgetast_typeliar: error("Assertion failed: " & $(`message`) & "\n" & `line`) return - macro assertOrReturn(condition: bool): typed = + macro assertOrReturn(condition: bool) = var message : NimNode = newLit(condition.repr) # echo message result = getAst assertOrReturn2(condition, message) diff --git a/tests/template/tgensymregression.nim b/tests/template/tgensymregression.nim index a743d3a08..f84c96403 100644 --- a/tests/template/tgensymregression.nim +++ b/tests/template/tgensymregression.nim @@ -57,7 +57,7 @@ converter int2string(x: int): string = $x template wrap(body: typed): untyped = body -macro makeProc(): typed = +macro makeProc() = # Make a template tree result = quote do: proc someProc* = diff --git a/tests/vm/textensionmap.nim b/tests/vm/textensionmap.nim index 5d4b25d01..7ada1880d 100644 --- a/tests/vm/textensionmap.nim +++ b/tests/vm/textensionmap.nim @@ -10,4 +10,4 @@ const EXTENSIONMAP = { "c": @["*.c", "*.h"], }.toTable() -const EXTENSIONS = toSet(concat(toSeq(EXTENSIONMAP.values()))) +const EXTENSIONS = toHashSet(concat(toSeq(EXTENSIONMAP.values()))) diff --git a/tests/vm/tgloballetfrommacro.nim b/tests/vm/tgloballetfrommacro.nim index 14dbff1e8..4be09b56e 100644 --- a/tests/vm/tgloballetfrommacro.nim +++ b/tests/vm/tgloballetfrommacro.nim @@ -5,7 +5,7 @@ line: 11 import sets -let BUILTIN_NAMES = toSet(["int8", "int16", "int32", "int64"]) +let BUILTIN_NAMES = toHashSet(["int8", "int16", "int32", "int64"]) macro test*(): bool = echo "int64" notin BUILTIN_NAMES diff --git a/tools/niminst/debcreation.nim b/tools/niminst/debcreation.nim index 60aa48639..d0f46fa52 100644 --- a/tools/niminst/debcreation.nim +++ b/tools/niminst/debcreation.nim @@ -101,7 +101,7 @@ proc createChangelog(pkgName, version, maintainer: string): string = addN(" * Initial release.") addN("") addN(" -- " & maintainer & " " & - formatDateTime(getGMTime(getTime()), "+0000")) + formatDateTime(utc(getTime()), "+0000")) proc createRules(): string = ## Creates a nim application-agnostic rules file for building deb packages. diff --git a/tools/nimweb.nim b/tools/nimweb.nim index 460135f49..a654c2b0a 100644 --- a/tools/nimweb.nim +++ b/tools/nimweb.nim @@ -421,7 +421,7 @@ proc generateRss(outputFilename: string, news: seq[TRssItem]) = output.write(link(href = rssNewsUrl)) output.write(id(rssNewsUrl)) - let now = getGMTime(getTime()) + let now = utc(getTime()) output.write(updatedDate($now.year, $(int(now.month) + 1), $now.monthday)) for rss in news: diff --git a/tools/website.nimf b/tools/website.nimf index 9e5eb2460..bde84b32f 100644 --- a/tools/website.nimf +++ b/tools/website.nimf @@ -250,7 +250,7 @@ runForever()

    This section lists the companies and individuals that are, very kindly, contributing a monthly amount to help sustain Nim's development. For more details take a look at the Bountysource campaign.

    -

    Last updated: ${getTime().getGMTime().format("dd/MM/yyyy")}

    +

    Last updated: ${getTime().utc().format("dd/MM/yyyy")}

    ${generateSponsors(activeSponsors)}
    From 7df27b5035ad128812fbd070e3fa80ef6819e4df Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 17 Jun 2020 20:25:25 -0700 Subject: [PATCH 0238/3768] [cleanups] doassert => doAssert; mark deadcode (#14711) --- compiler/semtypinst.nim | 22 ++++++++++++---------- tests/casestmt/tcasestmt.nim | 12 ++++++------ tests/stdlib/thtmlparser.nim | 12 ++++++------ tests/stdlib/tstrformat.nim | 4 ++-- 4 files changed, 26 insertions(+), 24 deletions(-) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 15a63e75e..22f0d1db6 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -110,7 +110,7 @@ proc lookup(typeMap: LayeredIdTable, key: PType): PType = template put(typeMap: LayeredIdTable, key, value: PType) = idTablePut(typeMap.topLayer, key, value) -template checkMetaInvariants(cl: TReplTypeVars, t: PType) = +template checkMetaInvariants(cl: TReplTypeVars, t: PType) = # noop code when false: if t != nil and tfHasMeta in t.flags and cl.allowMetaTypes == false: @@ -412,7 +412,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = newbody.attachedOps[attachedDeepCopy] = cl.c.instTypeBoundOp(cl.c, dc, result, cl.info, attachedDeepCopy, 1) if newbody.typeInst == nil: - #doassert newbody.typeInst == nil + # doAssert newbody.typeInst == nil newbody.typeInst = result if tfRefsAnonObj in newbody.flags and newbody.kind != tyGenericInst: # can come here for tyGenericInst too, see tests/metatype/ttypeor.nim @@ -670,14 +670,16 @@ proc replaceTypesInBody*(p: PContext, pt: TIdTable, n: PNode; result = replaceTypeVarsN(cl, n) popInfoContext(p.config) -proc replaceTypesForLambda*(p: PContext, pt: TIdTable, n: PNode; - original, new: PSym): PNode = - var typeMap = initLayeredTypeMap(pt) - var cl = initTypeVars(p, typeMap, n.info, original) - idTablePut(cl.symMap, original, new) - pushInfoContext(p.config, n.info) - result = replaceTypeVarsN(cl, n) - popInfoContext(p.config) +when false: + # deadcode + proc replaceTypesForLambda*(p: PContext, pt: TIdTable, n: PNode; + original, new: PSym): PNode = + var typeMap = initLayeredTypeMap(pt) + var cl = initTypeVars(p, typeMap, n.info, original) + idTablePut(cl.symMap, original, new) + pushInfoContext(p.config, n.info) + result = replaceTypeVarsN(cl, n) + popInfoContext(p.config) proc recomputeFieldPositions*(t: PType; obj: PNode; currPosition: var int) = if t != nil and t.len > 0 and t[0] != nil: diff --git a/tests/casestmt/tcasestmt.nim b/tests/casestmt/tcasestmt.nim index 6abea53bc..53cccdb64 100644 --- a/tests/casestmt/tcasestmt.nim +++ b/tests/casestmt/tcasestmt.nim @@ -184,33 +184,33 @@ block tcasestm: doAssert(bstatic == false) var bb: bool - doassert(not compiles( + doAssert(not compiles( bb = case str2: of "": raise newException(ValueError, "Invalid boolean") elif str.startsWith("Y"): true elif str.startsWith("N"): false )) - doassert(not compiles( + doAssert(not compiles( bb = case str2: of "Y": true of "N": false )) - doassert(not compiles( + doAssert(not compiles( bb = case str2: of "Y": true of "N": raise newException(ValueError, "N not allowed") )) - doassert(not compiles( + doAssert(not compiles( bb = case str2: of "Y": raise newException(ValueError, "Invalid Y") else: raise newException(ValueError, "Invalid N") )) - doassert(not compiles( + doAssert(not compiles( bb = case str2: of "Y": raise newException(ValueError, "Invalid Y") @@ -219,7 +219,7 @@ block tcasestm: )) - doassert(not compiles( + doAssert(not compiles( bb = case str2: of "Y": "invalid Y".quit(3) diff --git a/tests/stdlib/thtmlparser.nim b/tests/stdlib/thtmlparser.nim index e7dcd50d5..2ac7b4004 100644 --- a/tests/stdlib/thtmlparser.nim +++ b/tests/stdlib/thtmlparser.nim @@ -110,21 +110,21 @@ block t6154: doAssert ps[0].attrsLen == 2 doAssert ps[0].attr("class") == "foo" doAssert ps[0].attr("id") == "bar" - doassert ps[0].len == 0 + doAssert ps[0].len == 0 doAssert ps[1].attrsLen == 1 doAssert ps[1].attr("something") == "\tfoo\tbar²" - doassert ps[1].len == 0 + doAssert ps[1].len == 0 doAssert ps[2].attrsLen == 2 doAssert ps[2].attr("something") == "\tfoo\tbar²" doAssert ps[2].attr("foo") == "bloo" - doassert ps[2].len == 0 + doAssert ps[2].len == 0 doAssert ps[3].attrsLen == 2 doAssert ps[3].attr("class") == "foo2" doAssert ps[3].attr("id") == "bar2" - doassert ps[3].len == 0 + doAssert ps[3].len == 0 doAssert ps[4].attrsLen == 1 doAssert ps[4].attr("wrong") == "" @@ -133,12 +133,12 @@ block t6154: doAssert ps[5].attr("data-foo") == "" doAssert ps[5].attr("data-bar") == "correct!" doAssert ps[5].attr("enabled") == "" - doassert ps[5].len == 0 + doAssert ps[5].len == 0 doAssert ps[6].attrsLen == 2 doAssert ps[6].attr("quux") == "" doAssert ps[6].attr("whatever") == "" - doassert ps[6].len == 0 + doAssert ps[6].len == 0 # bug #11713, #1034 var content = """ diff --git a/tests/stdlib/tstrformat.nim b/tests/stdlib/tstrformat.nim index 7db001979..22c0cf95d 100644 --- a/tests/stdlib/tstrformat.nim +++ b/tests/stdlib/tstrformat.nim @@ -65,8 +65,8 @@ doAssert fmt"{1.5: 08}" == " 00001.5" # works # only add explicitly requested sign if value != -0.0 (neg zero) doAssert fmt"{-0.0:g}" == "-0" -doassert fmt"{-0.0:+g}" == "-0" -doassert fmt"{-0.0: g}" == "-0" +doAssert fmt"{-0.0:+g}" == "-0" +doAssert fmt"{-0.0: g}" == "-0" doAssert fmt"{0.0:g}" == "0" doAssert fmt"{0.0:+g}" == "+0" doAssert fmt"{0.0: g}" == " 0" From bb62feff72a911ca9a314eba6409a6f4f786b568 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 17 Jun 2020 23:47:16 -0700 Subject: [PATCH 0239/3768] fix #14691 docgen works again for methods (#14701) * fix #14691 docgen methods * fixup --- compiler/docgen.nim | 19 ++----- nimdoc/testproject/expected/testproject.html | 58 ++++++++++++++++++++ nimdoc/testproject/expected/testproject.idx | 5 ++ nimdoc/testproject/expected/theindex.html | 20 +++++++ nimdoc/testproject/testproject.nim | 22 ++++++++ 5 files changed, 110 insertions(+), 14 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 0af830a42..a973bdfc1 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -609,21 +609,12 @@ proc getRoutineBody(n: PNode): PNode = so we normalize the results to get to the statement list containing the (0 or more) doc comments and runnableExamples. - (even if using `result = n[bodyPos]`, you'd still to apply similar logic). ]## - result = n[^1] - case result.kind - of nkSym: - result = n[^2] - case result.kind - of nkAsgn: - doAssert result[0].kind == nkSym - doAssert result.len == 2 - result = result[1] - else: # eg: nkStmtList - discard - else: - discard + result = n[bodyPos] + if result.kind == nkAsgn and n.len > bodyPos+1 and n[bodyPos+1].kind == nkSym: + doAssert result[0].kind == nkSym + doAssert result.len == 2 + result = result[1] proc getAllRunnableExamples(d: PDoc, n: PNode, dest: var Rope) = var n = n diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html index 7bd2e64a3..a4f8876fd 100644 --- a/nimdoc/testproject/expected/testproject.html +++ b/nimdoc/testproject/expected/testproject.html @@ -211,11 +211,27 @@ function main() { +
  • + Methods + +
  • Iterators
  • @@ -653,6 +669,32 @@ My someFunc. Stuff in quotes +
    +
    +

    Methods

    +
    + +
    method method1(self: Moo) {...}{.raises: [], tags: [].}
    +
    + +foo1 + +
    + +
    method method2(self: Moo): int {...}{.raises: [], tags: [].}
    +
    + +foo2 + +
    + +
    method method3(self: Moo): int {...}{.raises: [], tags: [].}
    +
    + +foo3 + +
    +

    Iterators

    @@ -667,6 +709,22 @@ My someFunc. Stuff in quotesassert 1 == 1 # ok2
    + + +
    iterator iter1(n: int): int {...}{.raises: [], tags: [].}
    +
    + +foo1 + +
    + +
    iterator iter2(n: int): int {...}{.raises: [], tags: [].}
    +
    + +foo2 +

    Example:

    +
    discard # bar
    +
    diff --git a/nimdoc/testproject/expected/testproject.idx b/nimdoc/testproject/expected/testproject.idx index 46798f314..6d0bc6c5e 100644 --- a/nimdoc/testproject/expected/testproject.idx +++ b/nimdoc/testproject/expected/testproject.idx @@ -38,6 +38,11 @@ c_nonexistant testproject.html#c_nonexistant,cstring testproject: c_nonexistant( low testproject.html#low,T testproject: low[T: Ordinal | enum | range](x: T): T low2 testproject.html#low2,T testproject: low2[T: Ordinal | enum | range](x: T): T tripleStrLitTest testproject.html#tripleStrLitTest testproject: tripleStrLitTest() +method1 testproject.html#method1.e,Moo testproject: method1(self: Moo) +method2 testproject.html#method2.e,Moo testproject: method2(self: Moo): int +method3 testproject.html#method3.e,Moo testproject: method3(self: Moo): int +iter1 testproject.html#iter1.i,int testproject: iter1(n: int): int +iter2 testproject.html#iter2.i,int testproject: iter2(n: int): int bar testproject.html#bar.m testproject: bar(): untyped z16 testproject.html#z16.m testproject: z16() z18 testproject.html#z18.m testproject: z18(): int diff --git a/nimdoc/testproject/expected/theindex.html b/nimdoc/testproject/expected/theindex.html index 14d07bfd9..8d34506a6 100644 --- a/nimdoc/testproject/expected/theindex.html +++ b/nimdoc/testproject/expected/theindex.html @@ -181,6 +181,14 @@ function main() {
  • testproject: isValid[T](x: T): bool
  • +
    iter1:
    +
    iter2:
    low:
    +
    method1:
    +
    method2:
    +
    method3:
    myfn:
    • testproject: myfn()
    • diff --git a/nimdoc/testproject/testproject.nim b/nimdoc/testproject/testproject.nim index b05754d51..5282c6f77 100644 --- a/nimdoc/testproject/testproject.nim +++ b/nimdoc/testproject/testproject.nim @@ -247,6 +247,28 @@ at indent 0 # should be in # should be out +when true: # methods; issue #14691 + type Moo = object + method method1*(self: Moo) = + ## foo1 + method method2*(self: Moo): int = + ## foo2 + result = 1 + method method3*(self: Moo): int = + ## foo3 + 1 + +when true: # iterators + iterator iter1*(n: int): int = + ## foo1 + for i in 0.. Date: Thu, 18 Jun 2020 20:48:55 +0200 Subject: [PATCH 0240/3768] make nim-gdb compatible with BSD systems (#14700) --- bin/nim-gdb | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/bin/nim-gdb b/bin/nim-gdb index b9956073b..1486b6dce 100755 --- a/bin/nim-gdb +++ b/bin/nim-gdb @@ -5,17 +5,15 @@ set -e which nim > /dev/null || (echo "nim not in PATH"; exit 1) which gdb > /dev/null || (echo "gdb not in PATH"; exit 1) +which readlink > /dev/null || (echo "readlink not in PATH."; exit 1) -if [[ "$(uname -s)" == "Darwin" ]]; then - which greadlink > /dev/null || (echo "readlink not in PATH. Please install coreutils from homebrew."; exit 1) - READLINK=greadlink +if [[ "$(uname -s)" == "Darwin" || "(uname -s)" == *"BSD" ]]; then + NIM_SYSROOT=$(dirname $(dirname $($READLINK -f $(which nim)))) else - which readlink > /dev/null || (echo "readlink not in PATH."; exit 1) - READLINK=readlink + NIM_SYSROOT=$(dirname $(dirname $($READLINK -e $(which nim)))) fi # Find out where the pretty printer Python module is -NIM_SYSROOT=$(dirname $(dirname $($READLINK -e $(which nim)))) GDB_PYTHON_MODULE_PATH="$NIM_SYSROOT/tools/nim-gdb.py" # Run GDB with the additional arguments that load the pretty printers From 408518c9fed9757fd1cc246e3ced05ecbb3bcaab Mon Sep 17 00:00:00 2001 From: solo989 Date: Tue, 16 Jun 2020 13:35:41 -0700 Subject: [PATCH 0241/3768] Update tuple newLit --- lib/core/macros.nim | 17 ++++++++++++----- tests/macros/tmacros_various.nim | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 856c4f2c1..04a85d355 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -760,7 +760,7 @@ proc newLit*(arg: enum): NimNode {.compileTime.} = proc newLit*[N,T](arg: array[N,T]): NimNode {.compileTime.} proc newLit*[T](arg: seq[T]): NimNode {.compileTime.} proc newLit*[T](s: set[T]): NimNode {.compileTime.} -proc newLit*(arg: tuple): NimNode {.compileTime.} +proc newLit*[T: tuple](arg: T): NimNode {.compileTime.} proc newLit*(arg: object): NimNode {.compileTime.} = result = nnkObjConstr.newTree(arg.type.getTypeInst[1]) @@ -800,10 +800,17 @@ proc newLit*[T](s: set[T]): NimNode {.compileTime.} = var typ = getTypeInst(typeof(s))[1] result = newCall(typ,result) -proc newLit*(arg: tuple): NimNode {.compileTime.} = - result = nnkPar.newTree - for a,b in arg.fieldPairs: - result.add nnkExprColonExpr.newTree(newIdentNode(a), newLit(b)) +proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".} + ## See typetraits.isNamedTuple + +proc newLit*[T: tuple](arg: T): NimNode {.compileTime.} = + result = nnkTupleConstr.newTree + when isNamedTuple(T): + for a,b in arg.fieldPairs: + result.add nnkExprColonExpr.newTree(newIdentNode(a), newLit(b)) + else: + for b in arg.fields: + result.add newLit(b) proc nestList*(op: NimNode; pack: NimNode): NimNode {.compileTime.} = ## Nests the list `pack` into a tree of call expressions: diff --git a/tests/macros/tmacros_various.nim b/tests/macros/tmacros_various.nim index 3991a5e30..695353b0c 100644 --- a/tests/macros/tmacros_various.nim +++ b/tests/macros/tmacros_various.nim @@ -194,3 +194,35 @@ static: doAssert(v == a) echo "macrocache ok" + +block tupleNewLitTests: + macro t0(): untyped = + result = newLit(()) + doAssert t0 == () + macro t1(): untyped = + result = newLit((5,)) + doAssert t1 == (5,) + macro t2(): untyped = + result = newLit((a: 5)) + doAssert t2 == (a: 5) + macro t3(): untyped = + result = newLit((5, "5")) + doAssert t3 == (5, "5") + macro t4(): untyped = + result = newLit((a: 5, b: "5")) + doAssert t4 == (a: 5, b: "5") + macro t5(): untyped = + result = newLit(@[(5,)]) + doAssert t5 == @[(5,)] + macro t6(): untyped = + result = newLit(@[(a: 5)]) + doAssert t6 == @[(a: 5)] + macro t7(): untyped = + result = newLit(@[(5, "5")]) + doAssert t7 == @[(5, "5")] + macro t8(): untyped = + result = newLit(@[(a: 5, b: "5")]) + doAssert t8 == @[(a: 5, b: "5")] + macro t9(): untyped = + result = newLit(@[(a: (5, 6), b: ())]) + doAssert t9 == @[(a: (5, 6), b: ())] From 9c42ae91b735bda29de9afbc1333da52a85b3297 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 18 Jun 2020 13:36:45 -0700 Subject: [PATCH 0242/3768] add legacy workaround; improve test so that it actually tests for the bugfix --- changelog.md | 1 + lib/core/macros.nim | 8 ++++--- testament/important_packages.nim | 2 +- tests/macros/tmacros_various.nim | 38 ++++++-------------------------- 4 files changed, 14 insertions(+), 35 deletions(-) diff --git a/changelog.md b/changelog.md index 588af989b..3f3a2f9b2 100644 --- a/changelog.md +++ b/changelog.md @@ -112,6 +112,7 @@ - new proc `heapqueue.find[T](heap: HeapQueue[T], x: T): int` to get index of element ``x``. - Add `rstgen.rstToLatex` convenience proc for `renderRstToOut` and `initRstGenerator` with `outLatex` output. - Add `os.normalizeExe`, eg: `koch` => `./koch`. +- `macros.newLit` now preserves named vs unnamed tuples; use `-d:nimHasWorkaround14720` to keep old behavior ## Language changes diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 04a85d355..1b0986d33 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -801,12 +801,14 @@ proc newLit*[T](s: set[T]): NimNode {.compileTime.} = result = newCall(typ,result) proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".} - ## See typetraits.isNamedTuple + ## See `typetraits.isNamedTuple` proc newLit*[T: tuple](arg: T): NimNode {.compileTime.} = + ## use -d:nimHasWorkaround14720 to restore behavior prior to PR, forcing + ## a named tuple even when `arg` is unnamed. result = nnkTupleConstr.newTree - when isNamedTuple(T): - for a,b in arg.fieldPairs: + when defined(nimHasWorkaround14720) or isNamedTuple(T): + for a, b in arg.fieldPairs: result.add nnkExprColonExpr.newTree(newIdentNode(a), newLit(b)) else: for b in arg.fields: diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 6da644ce3..0a64f6c33 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -43,7 +43,7 @@ pkg1 "elvis" # Error: cannot open 'tests/runNative.nim' pkg1 "fragments", false, "nim c -r fragments/dsl.nim" pkg1 "gara" -pkg1 "ggplotnim", true, "nim c -d:noCairo -r -d:nimWorkaround14447 tests/tests.nim" +pkg1 "ggplotnim", true, "nim c -d:noCairo -r -d:nimWorkaround14447 -d:nimHasWorkaround14720 tests/tests.nim" # pkg1 "gittyup", true, "nimble test", "https://github.com/disruptek/gittyup" pkg1 "glob" pkg1 "gnuplot" diff --git a/tests/macros/tmacros_various.nim b/tests/macros/tmacros_various.nim index 695353b0c..533db25e1 100644 --- a/tests/macros/tmacros_various.nim +++ b/tests/macros/tmacros_various.nim @@ -194,35 +194,11 @@ static: doAssert(v == a) echo "macrocache ok" - + block tupleNewLitTests: - macro t0(): untyped = - result = newLit(()) - doAssert t0 == () - macro t1(): untyped = - result = newLit((5,)) - doAssert t1 == (5,) - macro t2(): untyped = - result = newLit((a: 5)) - doAssert t2 == (a: 5) - macro t3(): untyped = - result = newLit((5, "5")) - doAssert t3 == (5, "5") - macro t4(): untyped = - result = newLit((a: 5, b: "5")) - doAssert t4 == (a: 5, b: "5") - macro t5(): untyped = - result = newLit(@[(5,)]) - doAssert t5 == @[(5,)] - macro t6(): untyped = - result = newLit(@[(a: 5)]) - doAssert t6 == @[(a: 5)] - macro t7(): untyped = - result = newLit(@[(5, "5")]) - doAssert t7 == @[(5, "5")] - macro t8(): untyped = - result = newLit(@[(a: 5, b: "5")]) - doAssert t8 == @[(a: 5, b: "5")] - macro t9(): untyped = - result = newLit(@[(a: (5, 6), b: ())]) - doAssert t9 == @[(a: (5, 6), b: ())] + macro t(): untyped = + result = newLit (1, "foo", (), (1,), (a1: 'x', a2: @["ba"])) + doAssert $t() == """(1, "foo", (), (1,), (a1: 'x', a2: @["ba"]))""" + # this `$` test is needed because tuple equality doesn't distinguish + # between named vs unnamed tuples + doAssert t() == (1, "foo", (), (1, ), (a1: 'x', a2: @["ba"])) From 5ad8b9e8ebb2e21e20484afbaffa9450715079cd Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 19 Jun 2020 00:53:45 -0700 Subject: [PATCH 0243/3768] fix #14685 tests/async/t7758.nim flaky (#14721) * fix #14685 tests/async/t7758.nim flaky * address comment * address comment --- tests/async/t7758.nim | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/async/t7758.nim b/tests/async/t7758.nim index 3cbe16852..fe6d32ad3 100644 --- a/tests/async/t7758.nim +++ b/tests/async/t7758.nim @@ -2,7 +2,8 @@ import asyncdispatch import std/unittest proc task() {.async.} = - await sleepAsync(40) + const tSleep = 40 + await sleepAsync(tSleep) proc main() = var counter = 0 @@ -11,6 +12,10 @@ proc main() = inc(counter) poll(10) - check counter <= 4 + const slack = 1 + # because there is overhead in `async` + `sleepAsync` + # as can be seen by increasing `tSleep` from 40 to 49, which increases the number + # of failures. + check counter <= 4 + slack for i in 0 .. 10: main() From 522bc8dda1f36ffe5f14eb6b1bfdba215b45b139 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 16 Jun 2020 11:39:58 +0200 Subject: [PATCH 0244/3768] added a space --- compiler/ast.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index bc26defd4..9639f52d3 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1294,7 +1294,7 @@ proc newIntTypeNode*(intVal: BiggestInt, typ: PType): PNode = of tyUInt16: result = newNode(nkUInt16Lit) of tyUInt32: result = newNode(nkUInt32Lit) of tyUInt64: result = newNode(nkUInt64Lit) - of tyBool,tyEnum: + of tyBool, tyEnum: # XXX: does this really need to be the kind nkIntLit? result = newNode(nkIntLit) of tyStatic: # that's a pre-existing bug, will fix in another PR From 45bc6954c510fa9ca742cb333ce88d9625ad1e29 Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 19 Jun 2020 10:50:39 +0200 Subject: [PATCH 0245/3768] improve the parser's error message --- compiler/parser.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/parser.nim b/compiler/parser.nim index 8f7eb1226..868054f36 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -2285,7 +2285,7 @@ proc parseStmt(p: var TParser): PNode = case p.tok.tokType of tkIf, tkWhile, tkCase, tkTry, tkFor, tkBlock, tkAsm, tkProc, tkFunc, tkIterator, tkMacro, tkType, tkConst, tkWhen, tkVar: - parMessage(p, "complex statement requires indentation") + parMessage(p, "nestable statement requires indentation") result = p.emptyNode else: if p.inSemiStmtList > 0: From 45d1e55e72aea9acfcf1a6fb7061779a243d7921 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Fri, 19 Jun 2020 14:56:38 +0200 Subject: [PATCH 0246/3768] Add testcases for #11811 and #14315 (#14726) * Add testcase for #11811 * Add testcase for #14315 --- tests/bind/tbind.nim | 11 +++++++++++ tests/destructor/tdestructor.nim | 12 +++++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/tests/bind/tbind.nim b/tests/bind/tbind.nim index 49c37ae2e..f3fb952e3 100644 --- a/tests/bind/tbind.nim +++ b/tests/bind/tbind.nim @@ -4,6 +4,7 @@ output: ''' 1 1 1 +5 ''' """ @@ -65,3 +66,13 @@ block tmixin: Foo(a) Foo(b) + +# issue #11811 +proc p(a : int) = + echo a + +proc printVar*[T:int|float|string](a : T) = + bind p + p(a) + +printVar(5) diff --git a/tests/destructor/tdestructor.nim b/tests/destructor/tdestructor.nim index b6d60323c..9fd47fe00 100644 --- a/tests/destructor/tdestructor.nim +++ b/tests/destructor/tdestructor.nim @@ -154,4 +154,14 @@ proc caseobj_test_sink: TCaseObj = echo "---" -discard caseobj_test_sink() \ No newline at end of file +discard caseobj_test_sink() + +# issue #14315 + +type Vector*[T] = object + x1: int + # x2: T # uncomment will remove error + +# proc `=destroy`*(x: var Vector[int]) = discard # this will remove error +proc `=destroy`*[T](x: var Vector[T]) = discard +var a: Vector[int] # Error: unresolved generic parameter From 65c7884a3c84d7e1750cf630940b089ed204bc1a Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 19 Jun 2020 09:51:25 +0200 Subject: [PATCH 0247/3768] fixes #14718 [backport] --- lib/pure/pegs.nim | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim index 4f723b4af..946891420 100644 --- a/lib/pure/pegs.nim +++ b/lib/pure/pegs.nim @@ -907,16 +907,16 @@ macro mkHandlerTplts(handlers: untyped): untyped = result = newStmtList() for topCall in handlers[0]: - if nnkCall != topCall.kind: + if topCall.kind notin nnkCallKinds: error("Call syntax expected.", topCall) let pegKind = topCall[0] - if nnkIdent != pegKind.kind: + if pegKind.kind notin {nnkIdent, nnkSym}: error("PegKind expected.", pegKind) if 2 == topCall.len: for hdDef in topCall[1]: - if nnkCall != hdDef.kind: + if hdDef.kind notin nnkCallKinds: error("Call syntax expected.", hdDef) - if nnkIdent != hdDef[0].kind: + if hdDef[0].kind notin {nnkIdent, nnkSym}: error("Handler identifier expected.", hdDef[0]) if 2 == hdDef.len: let hdPostf = substr(pegKind.strVal, 2) From 2039e3e883ac42cd9b6012335a92564ca2b56387 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 19 Jun 2020 06:08:00 -0700 Subject: [PATCH 0248/3768] fix #13899 defer now works with async (#14723) --- lib/pure/asyncmacro.nim | 3 +++ tests/async/tasync_misc.nim | 25 ++++++++++++++++++------- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/lib/pure/asyncmacro.nim b/lib/pure/asyncmacro.nim index cbed6372c..621a4b00c 100644 --- a/lib/pure/asyncmacro.nim +++ b/lib/pure/asyncmacro.nim @@ -207,6 +207,9 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} = futureVarIdents) # don't do anything with forward bodies (empty) if procBody.kind != nnkEmpty: + # fix #13899, defer should not escape its original scope + procBody = newStmtList(newTree(nnkBlockStmt, newEmptyNode(), procBody)) + procBody.add(createFutureVarCompletions(futureVarIdents, nil)) if not subtypeIsVoid: diff --git a/tests/async/tasync_misc.nim b/tests/async/tasync_misc.nim index c5ee22a42..29e6faa72 100644 --- a/tests/async/tasync_misc.nim +++ b/tests/async/tasync_misc.nim @@ -1,8 +1,3 @@ -discard """ - exitcode: 0 - output: "ok" -""" - import json, asyncdispatch block: #6100 let done = newFuture[int]() @@ -61,5 +56,21 @@ block: # 12743 waitFor prc() - -echo "ok" +block: # issue #13899 + proc someConnect() {.async.} = + await sleepAsync(1) + proc someClose() {.async.} = + await sleepAsync(2) + proc testFooFails(): Future[bool] {.async.} = + await someConnect() + defer: + await someClose() + result = true + proc testFooSucceed(): Future[bool] {.async.} = + try: + await someConnect() + finally: + await someClose() + result = true + doAssert waitFor testFooSucceed() + doAssert waitFor testFooFails() From 99c198625c602984578f9c53b05a28c54de4f4cb Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Fri, 19 Jun 2020 10:08:45 -0300 Subject: [PATCH 0249/3768] Deprecate unroll pragma, remove from documentation (#14705) --- changelog.md | 2 ++ doc/manual.rst | 19 ------------------- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/changelog.md b/changelog.md index 3f3a2f9b2..9ab6ebfda 100644 --- a/changelog.md +++ b/changelog.md @@ -176,6 +176,8 @@ proc mydiv(a, b): int {.raises: [].} = with `--panics:on` `Defects` become unrecoverable errors. - Added `thiscall` calling convention as specified by Microsoft, mostly for hooking purpose +- Deprecated `{.unroll.}` pragma, was ignored by the compiler anyways, was a nop. + ## Compiler changes diff --git a/doc/manual.rst b/doc/manual.rst index 75cc507a4..a7bca7616 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -6210,25 +6210,6 @@ the underlying backend (C compiler) does not support the computed goto extension the pragma is simply ignored. -unroll pragma -------------- -The ``unroll`` pragma can be used to tell the compiler that it should unroll -a `for`:idx: or `while`:idx: loop for execution efficiency: - -.. code-block:: nim - proc searchChar(s: string, c: char): int = - for i in 0 .. s.high: - {.unroll: 4.} - if s[i] == c: return i - result = -1 - -In the above example, the search loop is unrolled by a factor 4. The unroll -factor can be left out too; the compiler then chooses an appropriate unroll -factor. - -**Note**: Currently the compiler recognizes but ignores this pragma. - - immediate pragma ---------------- From ac8ab4c54967a83d800655d9de42ece7d5e21c93 Mon Sep 17 00:00:00 2001 From: Kaushal Modi Date: Fri, 19 Jun 2020 10:22:48 -0400 Subject: [PATCH 0250/3768] Clarify the use of the backwards index operator (^N) in tut1 (#14681) * Clarify the use of the backwards index operator (^N) in tut1 For consistency: - Do `[a .. ^b]` (notice spaces on both sides of `..`) - Do `[c ..< d]` (notice spaces on both sides of `..<`) Fixes https://github.com/nim-lang/Nim/issues/14671. * tut1: Add a note that ^ template calls can be saved to consts --- doc/tut1.rst | 38 ++++++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/doc/tut1.rst b/doc/tut1.rst index 6d54d88a3..24874096c 100644 --- a/doc/tut1.rst +++ b/doc/tut1.rst @@ -395,20 +395,29 @@ Since counting up occurs so often in programs, Nim also has a `.. `_ iterator that does the same: .. code-block:: nim - for i in 1..10: + for i in 1 .. 10: ... -Zero-indexed counting have two shortcuts ``..<`` and ``..^`` to simplify counting to one less than the higher index: +Zero-indexed counting has two shortcuts ``..<`` and ``.. ^1`` +(`backwards index operator `_) to simplify +counting to one less than the higher index: .. code-block:: nim - for i in 0..<10: - ... # 0..9 + for i in 0 ..< 10: + ... # 0 .. 9 or .. code-block:: nim var s = "some string" - for i in 0.. 'a prog' - b[11..^2] = "useful" + echo a[7 .. 12] # --> 'a prog' + b[11 .. ^2] = "useful" echo b # --> 'Slices are useful.' In the previous example slices are used to modify a part of a string. The @@ -1425,17 +1434,22 @@ indices are 0 11 17 using indices ^19 ^8 ^2 using ^ syntax -where ``b[0..^1]`` is equivalent to ``b[0..b.len-1]`` and ``b[0..`_. In the above example, because the string ends in a period, to get the portion of the string that is "useless" and replace it with "useful". -``b[11..^2]`` is the portion "useless", and ``b[11..^2] = "useful"`` replaces the +``b[11 .. ^2]`` is the portion "useless", and ``b[11 .. ^2] = "useful"`` replaces the "useless" portion with "useful", giving the result "Slices are useful." -Note: alternate ways of writing this are ``b[^8..^2] = "useful"`` or -as ``b[11..b.len-2] = "useful"`` or as ``b[11..`_ +of type ``BackwardsIndex``, we can have a ``lastIndex`` constant defined as ``const lastIndex = ^1``, +and later used as ``b[0 .. lastIndex]``. Objects ------- From 79c90b30ee5590b02f8bcfac68ed0c9ef7b38e7d Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 19 Jun 2020 14:38:43 -0700 Subject: [PATCH 0251/3768] nep1: use subjectVerb, not verbSuject (#14732) --- doc/nep1.rst | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/doc/nep1.rst b/doc/nep1.rst index 1ef8c3c24..35b1a1f62 100644 --- a/doc/nep1.rst +++ b/doc/nep1.rst @@ -101,11 +101,13 @@ changed in the future. fd: int64 HandleRef = ref Handle # Will be used less often -- Exception and Error types should have the "Error" suffix. +- Exception and Error types should have the "Error" or "Defect" suffix. .. code-block:: nim type - UnluckyError = object of Exception + ValueError = object of CatchableError + AssertionDefect = object of Defect + Foo = object of Exception # bad style, try to inherit CatchableError or Defect - Unless marked with the `{.pure.}` pragma, members of enums should have an identifying prefix, such as an abbreviation of the enum's name. @@ -147,6 +149,8 @@ changed in the future. an in-place version should get an ``-In`` suffix (``replaceIn`` for this example). +- Use `subjectVerb`, not `verbSubject`, eg: `fileExists`, not `existsFile`. + The stdlib API is designed to be **easy to use** and consistent. Ease of use is measured by the number of calls to achieve a concrete high level action. The ultimate goal is that the programmer can *guess* a name. From ca71a60ff6a86244eddee28d175e9c546fe2bfc7 Mon Sep 17 00:00:00 2001 From: treeform Date: Sun, 21 Jun 2020 10:25:29 -0700 Subject: [PATCH 0252/3768] Add css white-space property to dom.nim. (#14743) https://developer.mozilla.org/en-US/docs/Web/CSS/white-space --- lib/js/dom.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/js/dom.nim b/lib/js/dom.nim index 65f9a946b..38ec18a61 100644 --- a/lib/js/dom.nim +++ b/lib/js/dom.nim @@ -463,6 +463,7 @@ type verticalAlign*: cstring visibility*: cstring width*: cstring + whiteSpace*: cstring wordSpacing*: cstring zIndex*: int From c7dee55b87fe0f35a411b4ec48f833be5537a231 Mon Sep 17 00:00:00 2001 From: alaviss Date: Sun, 21 Jun 2020 17:28:03 +0000 Subject: [PATCH 0253/3768] encodings: use only one iconv definition [backport:1.2] (#14741) Fix an issue reported on IRC: using encodings with --dynlibOverrideAll result in duplicated iconv definitions, causing compile errors. This commit remove the `var` wrapper of iconv and go all out on pointers, as it should due to how the API accepts nil. Also corrected the API to resemble iconv(3p). --- lib/pure/encodings.nim | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/lib/pure/encodings.nim b/lib/pure/encodings.nim index ff9c3889a..1d8512018 100644 --- a/lib/pure/encodings.nim +++ b/lib/pure/encodings.nim @@ -290,11 +290,8 @@ else: importc: "iconv_open", importIconv.} proc iconvClose(c: EncodingConverter) {. importc: "iconv_close", importIconv.} - proc iconv(c: EncodingConverter, inbuf: var cstring, inbytesLeft: var int, - outbuf: var cstring, outbytesLeft: var int): int {. - importc: "iconv", importIconv.} - proc iconv(c: EncodingConverter, inbuf: pointer, inbytesLeft: pointer, - outbuf: var cstring, outbytesLeft: var int): int {. + proc iconv(c: EncodingConverter, inbuf: ptr cstring, inbytesLeft: ptr csize_t, + outbuf: ptr cstring, outbytesLeft: ptr csize_t): csize_t {. importc: "iconv", importIconv.} proc getCurrentEncoding*(uiApp = false): string = @@ -428,14 +425,14 @@ when defined(windows): else: proc convert*(c: EncodingConverter, s: string): string = result = newString(s.len) - var inLen = len(s) - var outLen = len(result) + var inLen = csize_t len(s) + var outLen = csize_t len(result) var src = cstring(s) var dst = cstring(result) - var iconvres: int + var iconvres: csize_t while inLen > 0: - iconvres = iconv(c, src, inLen, dst, outLen) - if iconvres == -1: + iconvres = iconv(c, addr src, addr inLen, addr dst, addr outLen) + if iconvres == high(csize_t): var lerr = errno if lerr == EILSEQ or lerr == EINVAL: # unknown char, skip @@ -446,24 +443,24 @@ else: dec(outLen) elif lerr == E2BIG: var offset = cast[int](dst) - cast[int](cstring(result)) - setLen(result, len(result)+inLen*2+5) + setLen(result, len(result) + inLen.int * 2 + 5) # 5 is minimally one utf-8 char dst = cast[cstring](cast[int](cstring(result)) + offset) - outLen = len(result) - offset + outLen = csize_t(len(result) - offset) else: raiseOSError(lerr.OSErrorCode) # iconv has a buffer that needs flushing, specially if the last char is # not '\0' - discard iconv(c, nil, nil, dst, outLen) - if iconvres == cint(-1) and errno == E2BIG: + discard iconv(c, nil, nil, addr dst, addr outLen) + if iconvres == high(csize_t) and errno == E2BIG: var offset = cast[int](dst) - cast[int](cstring(result)) - setLen(result, len(result)+inLen*2+5) + setLen(result, len(result) + inLen.int * 2 + 5) # 5 is minimally one utf-8 char dst = cast[cstring](cast[int](cstring(result)) + offset) - outLen = len(result) - offset - discard iconv(c, nil, nil, dst, outLen) + outLen = csize_t(len(result) - offset) + discard iconv(c, nil, nil, addr dst, addr outLen) # trim output buffer - setLen(result, len(result) - outLen) + setLen(result, len(result) - outLen.int) proc convert*(s: string, destEncoding = "UTF-8", srcEncoding = "CP1252"): string = From 3ba0c30758e1044aba11bb908a7d83de7ea215bc Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 21 Jun 2020 19:58:37 +0200 Subject: [PATCH 0254/3768] sizeof for empty objects/tuples should be 1; fixes #14690 (#14751) --- compiler/sizealignoffsetimpl.nim | 4 ++-- tests/misc/tsizeof.nim | 10 +++++++++- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/compiler/sizealignoffsetimpl.nim b/compiler/sizealignoffsetimpl.nim index 9d12297ea..4d73923db 100644 --- a/compiler/sizealignoffsetimpl.nim +++ b/compiler/sizealignoffsetimpl.nim @@ -330,7 +330,7 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) = sym.offset = accum.offset accum.inc(int(child.size)) typ.paddingAtEnd = int16(accum.finish()) - typ.size = accum.offset + typ.size = if accum.offset == 0: 1 else: accum.offset typ.align = int16(accum.maxAlign) except IllegalTypeRecursionError: typ.paddingAtEnd = szIllegalRecursion @@ -388,7 +388,7 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) = typ.align = szUnknownSize typ.paddingAtEnd = szUnknownSize else: - typ.size = accum.offset + typ.size = if accum.offset == 0: 1 else: accum.offset typ.align = int16(accum.maxAlign) typ.paddingAtEnd = paddingAtEnd except IllegalTypeRecursionError: diff --git a/tests/misc/tsizeof.nim b/tests/misc/tsizeof.nim index 7b92d3639..579532fcc 100644 --- a/tests/misc/tsizeof.nim +++ b/tests/misc/tsizeof.nim @@ -78,7 +78,7 @@ macro c_offsetof(fieldAccess: typed): int32 = ## Bullet proof implementation that works on actual offsetof operator ## in the c backend. Assuming of course this implementation is ## correct. - let s = if fieldAccess.kind == nnkCheckedFieldExpr: fieldAccess[0] + let s = if fieldAccess.kind == nnkCheckedFieldExpr: fieldAccess[0] else: fieldAccess let a = s[0].getTypeInst let b = s[1] @@ -686,3 +686,11 @@ reject: reject: const off8 = offsetof(MyPackedCaseObject, val5) + + +type + O0 = object + T0 = tuple[] + +doAssert sizeof(O0) == 1 +doAssert sizeof(T0) == 1 From 1ffe1134501210e23e71e0f23099c2005edd98c9 Mon Sep 17 00:00:00 2001 From: treeform Date: Sun, 21 Jun 2020 11:41:24 -0700 Subject: [PATCH 0255/3768] Export fields from the Selection object. (#14752) See: https://developer.mozilla.org/en-US/docs/Web/API/Selection --- lib/js/dom.nim | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/js/dom.nim b/lib/js/dom.nim index 38ec18a61..48d32fc18 100644 --- a/lib/js/dom.nim +++ b/lib/js/dom.nim @@ -107,6 +107,13 @@ type timing*: PerformanceTiming Selection* {.importc.} = ref object ## see `docs`_ + anchorNode*: Node + anchorOffset*: int + focusNode*: Node + focusOffset*: int + isCollapsed*: bool + rangeCount*: int + `type`*: cstring LocalStorage* {.importc.} = ref object From 1e484ed62b499432883575e6b29c930874377d7a Mon Sep 17 00:00:00 2001 From: ee7 <45465154+ee7@users.noreply.github.com> Date: Mon, 22 Jun 2020 08:12:50 +0200 Subject: [PATCH 0256/3768] [backport] Docs: Fix broken `code-block` (#14749) This commit indents the contents of a `code-block` in `httpclient.nim` so that it displays correctly. The bug was introduced by 42a64245f8. I did a quick search for other `code-block`s that are broken in the same way, but the only other one I found (in `pegs.nim`) is not included in the generated documentation. --- lib/pure/httpclient.nim | 10 +++++----- lib/pure/pegs.nim | 40 ++++++++++++++++++++-------------------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index dfd4abea5..92e4cd2d6 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -25,13 +25,13 @@ ## ``AsyncHttpClient``: ## ## .. code-block:: Nim -## import asyncdispatch, httpclient +## import asyncdispatch, httpclient ## -## proc asyncProc(): Future[string] {.async.} = -## var client = newAsyncHttpClient() -## return await client.getContent("http://example.com") +## proc asyncProc(): Future[string] {.async.} = +## var client = newAsyncHttpClient() +## return await client.getContent("http://example.com") ## -## echo waitFor asyncProc() +## echo waitFor asyncProc() ## ## The functionality implemented by ``HttpClient`` and ``AsyncHttpClient`` ## is the same, so you can use whichever one suits you best in the examples diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim index 946891420..8abd3deb5 100644 --- a/lib/pure/pegs.nim +++ b/lib/pure/pegs.nim @@ -870,26 +870,26 @@ macro mkHandlerTplts(handlers: untyped): untyped = # The AST structure of *handlers[0]*: # # .. code-block:: - # StmtList - # Call - # Ident "pkNonTerminal" - # StmtList - # Call - # Ident "enter" - # StmtList - # - # Call - # Ident "leave" - # StmtList - # - # Call - # Ident "pkChar" - # StmtList - # Call - # Ident "leave" - # StmtList - # - # ... + # StmtList + # Call + # Ident "pkNonTerminal" + # StmtList + # Call + # Ident "enter" + # StmtList + # + # Call + # Ident "leave" + # StmtList + # + # Call + # Ident "pkChar" + # StmtList + # Call + # Ident "leave" + # StmtList + # + # ... proc mkEnter(hdName, body: NimNode): NimNode = template helper(hdName, body) {.dirty.} = template hdName(s, p, start) = From c8554ebc0c0c67b4f691a760b5e844f974e92ddc Mon Sep 17 00:00:00 2001 From: alaviss Date: Mon, 22 Jun 2020 07:23:24 +0000 Subject: [PATCH 0257/3768] posix_other: add define to force time_t to 64 bit [backport] (#14753) This is a temporary remedy for time_t transition to 64 bit on newer Linux libc (musl >= 1.2.0, glibc >= 2.32). In the future we might want to move away from libc, or figure out a way to reliably detect the real size of C types at compile time, both of which are difficult. --- lib/posix/posix_other.nim | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/lib/posix/posix_other.nim b/lib/posix/posix_other.nim index 919d564d3..59ae9c8f7 100644 --- a/lib/posix/posix_other.nim +++ b/lib/posix/posix_other.nim @@ -32,7 +32,12 @@ type SocketHandle* = distinct cint # The type used to represent socket descriptors type - Time* {.importc: "time_t", header: "".} = distinct clong + Time* {.importc: "time_t", header: "".} = distinct ( + when defined(nimUse64BitCTime): + int64 + else: + clong + ) Timespec* {.importc: "struct timespec", header: "", final, pure.} = object ## struct timespec From 8df20276c9e7f714aacfad7b20f9308233f6bfdd Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 22 Jun 2020 13:06:22 +0200 Subject: [PATCH 0258/3768] fixes #14458 [backport:1.2] (#14756) --- compiler/cgen.nim | 9 ++++++--- compiler/pragmas.nim | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 338b337f2..417acf178 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -975,6 +975,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) @@ -1029,7 +1032,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: @@ -1092,13 +1095,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])) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 021a8b339..c9fe3a5e1 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -904,7 +904,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, From 451129de4213805b74b1635565d998904d705458 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 22 Jun 2020 14:11:13 +0200 Subject: [PATCH 0259/3768] fixes #14240 [backport:1.2] (#14757) --- compiler/ccgstmts.nim | 17 +++++++++-------- compiler/cgen.nim | 1 + compiler/cgendata.nim | 3 ++- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index ca6e51f29..82f1caadc 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -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: diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 417acf178..cd02ba9b1 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -682,6 +682,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) diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index 96e382873..ce74963e2 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -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 From 59ba4d8c031ecd145c027c632e443c92b9c3b29d Mon Sep 17 00:00:00 2001 From: b3liever <43617260+b3liever@users.noreply.github.com> Date: Mon, 22 Jun 2020 21:48:33 +0300 Subject: [PATCH 0260/3768] added normal variate function (#14725) * added normal variate function * change method only slightly faster * changelog + since Co-authored-by: b3liever --- changelog.md | 2 +- lib/pure/random.nim | 43 ++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index 9ab6ebfda..151eafcd5 100644 --- a/changelog.md +++ b/changelog.md @@ -113,7 +113,7 @@ - Add `rstgen.rstToLatex` convenience proc for `renderRstToOut` and `initRstGenerator` with `outLatex` output. - Add `os.normalizeExe`, eg: `koch` => `./koch`. - `macros.newLit` now preserves named vs unnamed tuples; use `-d:nimHasWorkaround14720` to keep old behavior - +- Add `random.gauss`, that uses the ratio of uniforms method of sampling from a Gaussian distribution. ## Language changes - In the 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: diff --git a/lib/pure/random.nim b/lib/pure/random.nim index e794a4dde..7c5d51ceb 100644 --- a/lib/pure/random.nim +++ b/lib/pure/random.nim @@ -78,7 +78,8 @@ ## `_ ## in the standard library -import algorithm #For upperBound +import algorithm, math +import std/private/since include "system/inclrtl" {.push debugger: off.} @@ -516,6 +517,33 @@ proc sample*[T, U](a: openArray[T]; cdf: openArray[U]): T = doAssert sample(marbles, cdf) == "blue" state.sample(a, cdf) +proc gauss*(r: var Rand; mu = 0.0; sigma = 1.0): float {.since: (1, 3).} = + ## Returns a Gaussian random variate, + ## with mean ``mu`` and standard deviation ``sigma`` + ## using the given state. + # Ratio of uniforms method for normal + # http://www2.econ.osaka-u.ac.jp/~tanizaki/class/2013/econome3/13.pdf + const K = sqrt(2 / E) + var + a = 0.0 + b = 0.0 + while true: + a = rand(r, 1.0) + b = (2.0 * rand(r, 1.0) - 1.0) * K + if b * b <= -4.0 * a * a * ln(a): break + result = mu + sigma * (b / a) + +proc gauss*(mu = 0.0, sigma = 1.0): float {.since: (1, 3).} = + ## Returns a Gaussian random variate, + ## with mean ``mu`` and standard deviation ``sigma``. + ## + ## If `randomize<#randomize>`_ has not been called, the order of outcomes + ## from this proc will always be the same. + ## + ## This proc uses the default random number generator. Thus, it is **not** + ## thread-safe. + result = gauss(state, mu, sigma) + proc initRand*(seed: int64): Rand = ## Initializes a new `Rand<#Rand>`_ state using the given seed. ## @@ -619,6 +647,8 @@ when not defined(nimscript) and not defined(standalone): {.pop.} when isMainModule: + import stats + proc main = var occur: array[1000, int] @@ -632,6 +662,17 @@ when isMainModule: elif oc > 150: doAssert false, "too many occurrences of " & $i + when false: + var rs: RunningStat + for j in 1..5: + for i in 1 .. 1_000: + rs.push(gauss()) + echo("mean: ", rs.mean, + " stdDev: ", rs.standardDeviation(), + " min: ", rs.min, + " max: ", rs.max) + rs.clear() + var a = [0, 1] shuffle(a) doAssert a[0] == 1 From 03c8bbcc6ed499c9333e1c1e946c403c1f135831 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Mon, 22 Jun 2020 22:05:18 +0200 Subject: [PATCH 0261/3768] Remove outdated comment and copy of length (#14759) --- lib/system/iterators.nim | 16 +++------------- 1 file changed, 3 insertions(+), 13 deletions(-) diff --git a/lib/system/iterators.nim b/lib/system/iterators.nim index 5d8685e40..99ad6eef6 100644 --- a/lib/system/iterators.nim +++ b/lib/system/iterators.nim @@ -1,21 +1,12 @@ when defined(nimHasLentIterators) and not defined(nimWorkaround14447): - template lent2(T): untyped = - # xxx this should actually depend on T.sizeof >= thresLentSizeof - # with for example `thresLentSizeof ~= int.sizeof`: - # it may be faster to return by value for small sizes compared to - # forcing a deref; this could be adjusted using profiling. - # However, `simply using `when T.sizeof >= thresLentSizeof: lent T else: T` - # does not work, for a few reasons (eg importc types would cause CT error - # and we can't filter them out without compiles() or some magic. - lent T + template lent2(T): untyped = lent T else: template lent2(T): untyped = T iterator items*[T: not char](a: openArray[T]): lent2 T {.inline.} = ## Iterates over each item of `a`. var i = 0 - let n = len(a) - while i < n: + while i < len(a): yield a[i] inc(i) @@ -25,8 +16,7 @@ iterator items*[T: char](a: openArray[T]): T {.inline.} = # elements converted from a string (would fail in `tests/misc/thallo.nim`) # in any case there's no performance advantage of returning char by address. var i = 0 - let n = len(a) - while i < n: + while i < len(a): yield a[i] inc(i) From 235d8ba069b122f34dee3ab12a34d2fba73326ce Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 22 Jun 2020 18:38:23 -0700 Subject: [PATCH 0262/3768] unbreak CI, refs https://github.com/timotheecour/Nim/issues/167 (#14765) --- testament/important_packages.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 0a64f6c33..8974b78ec 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -92,7 +92,8 @@ pkg2 "nimwc", true, "nim c nimwc.nim" # pkg2 "nimx", true, "nim c --threads:on test/main.nim" # pkg2 "nitter", true, "nim c src/nitter.nim", "https://github.com/zedeus/nitter" # pending https://github.com/timotheecour/Nim/issues/167 or new git tag for nitter -pkg2 "norm", true, "nim c -r tests/tsqlite.nim" +# pkg2 "norm", true, "nim c -r tests/tsqlite.nim" + # pending https://github.com/timotheecour/Nim/issues/167 pkg2 "npeg", false, "nimble testarc" pkg2 "numericalnim", true, "nim c -r tests/test_integrate.nim" pkg2 "optionsutils" From 511bd947d3a90a5ffedc994a45a36c4eeca63856 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 23 Jun 2020 01:06:36 -0700 Subject: [PATCH 0263/3768] fix bug in semgnrc: runnableExamples should not semcheck, even with > 1 arg (#14768) --- compiler/semgnrc.nim | 2 +- tests/nimdoc/trunnableexamples.nim | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index 23b6e9b9d..3366732ca 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -265,7 +265,7 @@ proc semGenericStmt(c: PContext, n: PNode, # We're not interested in the example code during this pass so let's # skip it if s.magic == mRunnableExamples: - inc first + first = result.safeLen # see trunnableexamples.fun3 of skGenericParam: result[0] = newSymNodeTypeDesc(s, fn.info) onUse(fn.info, s) diff --git a/tests/nimdoc/trunnableexamples.nim b/tests/nimdoc/trunnableexamples.nim index 73cfacd43..6232011cb 100644 --- a/tests/nimdoc/trunnableexamples.nim +++ b/tests/nimdoc/trunnableexamples.nim @@ -91,6 +91,16 @@ when true: # runnableExamples with rdoccmd proc fun2*() = runnableExamples "-d:foo": discard # checks that it also works inside procs + template fun3Impl(): untyped = + runnableExamples(rdoccmd="-d:foo"): + nonexistant + # bugfix: this shouldn't be semchecked when `runnableExamples` + # has more than 1 argument + discard + + proc fun3*[T]() = + fun3Impl() + when false: # future work # passing non-string-litterals (for reuse) const a = "-b:cpp" From 8e54bc5b0c06dcf2ed0de6b8a10733155e15dc0a Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Tue, 23 Jun 2020 05:51:33 -0300 Subject: [PATCH 0264/3768] Deprecate and/or remove ospaths (#14767) --- lib/pure/reservedmem.nim | 2 +- lib/pure/ssl_certs.nim | 2 +- nimpretty/tests/expected/wrong_ind.nim | 2 +- nimpretty/tests/wrong_ind.nim | 2 +- tests/untestable/thttpclient_ssl_disabled.nim | 2 +- tests/untestable/thttpclient_ssl_env_var.nim | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/pure/reservedmem.nim b/lib/pure/reservedmem.nim index 85a6e324f..469ca7efa 100644 --- a/lib/pure/reservedmem.nim +++ b/lib/pure/reservedmem.nim @@ -18,7 +18,7 @@ ## ## Unstable API. -from ospaths import raiseOSError, osLastError +from os import raiseOSError, osLastError template distance*(lhs, rhs: pointer): int = cast[int](rhs) - cast[int](lhs) diff --git a/lib/pure/ssl_certs.nim b/lib/pure/ssl_certs.nim index 9845de9b6..806316b02 100644 --- a/lib/pure/ssl_certs.nim +++ b/lib/pure/ssl_certs.nim @@ -11,7 +11,7 @@ ## SSL_CERT_DIR environment variables. import os, strutils -from ospaths import existsEnv, getEnv +from os import existsEnv, getEnv import strutils # SECURITY: this unnecessarily scans through dirs/files regardless of the diff --git a/nimpretty/tests/expected/wrong_ind.nim b/nimpretty/tests/expected/wrong_ind.nim index 95a35a8fd..3ff9de91c 100644 --- a/nimpretty/tests/expected/wrong_ind.nim +++ b/nimpretty/tests/expected/wrong_ind.nim @@ -2,7 +2,7 @@ # bug #9505 import std/[ - strutils, ospaths, os + strutils, os ] import pkg/[ regex diff --git a/nimpretty/tests/wrong_ind.nim b/nimpretty/tests/wrong_ind.nim index 3c7544cdb..a042e8822 100644 --- a/nimpretty/tests/wrong_ind.nim +++ b/nimpretty/tests/wrong_ind.nim @@ -2,7 +2,7 @@ # bug #9505 import std/[ - strutils, ospaths, os + strutils, os ] import pkg/[ regex diff --git a/tests/untestable/thttpclient_ssl_disabled.nim b/tests/untestable/thttpclient_ssl_disabled.nim index 4d4ede4de..0f7e95831 100644 --- a/tests/untestable/thttpclient_ssl_disabled.nim +++ b/tests/untestable/thttpclient_ssl_disabled.nim @@ -12,7 +12,7 @@ import httpclient, net, unittest, - ospaths + os from strutils import contains diff --git a/tests/untestable/thttpclient_ssl_env_var.nim b/tests/untestable/thttpclient_ssl_env_var.nim index 32af43579..3f25a6ff4 100644 --- a/tests/untestable/thttpclient_ssl_env_var.nim +++ b/tests/untestable/thttpclient_ssl_env_var.nim @@ -13,7 +13,7 @@ ## SSL_CERT_FILE=BogusInexistentFileName tests/untestable/thttpclient_ssl_env_var ## SSL_CERT_DIR=BogusInexistentDirName tests/untestable/thttpclient_ssl_env_var -import httpclient, unittest, ospaths +import httpclient, unittest, os from net import newSocket, newContext, wrapSocket, connect, close, Port, CVerifyPeerUseEnvVars from strutils import contains From a9eee6db65e72e0e11cbf5faf0794b1b6ac8bd0c Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 23 Jun 2020 01:53:03 -0700 Subject: [PATCH 0265/3768] misc testament cleanups (#14764) * misc testament cleanups extracted cleanups from #14530 * make sure all CI failures can be searched with `FAIL: ` including megatest failures --- testament/categories.nim | 36 ++++++++++------------------ testament/testament.nim | 5 ++-- tests/misc/tunsignedconv.nim | 14 ++++------- tests/testament/tshould_not_work.nim | 2 ++ 4 files changed, 22 insertions(+), 35 deletions(-) diff --git a/testament/categories.nim b/testament/categories.nim index 45a2113a6..3bd31ddc4 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -587,13 +587,6 @@ proc quoted(a: string): string = # todo: consider moving to system.nim result.addQuoted(a) -proc normalizeExe(file: string): string = - # xxx common pattern, should be exposed in std/os, even if simple (error prone) - when defined(posix): - if file.len == 0: "" - elif DirSep in file: file else: "./" & file - else: file - proc runJoinedTest(r: var TResults, cat: Category, testsDir: string) = ## returns a list of tests that have problems var specs: seq[TSpec] = @[] @@ -618,12 +611,7 @@ proc runJoinedTest(r: var TResults, cat: Category, testsDir: string) = return var megatest: string - #[ - TODO(minor): - get from Nim cmd - put outputGotten.txt, outputGotten.txt, megatest.nim there too - delete upon completion, maybe - ]# + # xxx (minor) put outputExceptedFile, outputGottenFile, megatestFile under here or `buildDir` var outDir = nimcacheDir(testsDir / "megatest", "", targetC) const marker = "megatest:processing: " @@ -644,15 +632,17 @@ proc runJoinedTest(r: var TResults, cat: Category, testsDir: string) = var (cmdLine, buf, exitCode) = execCmdEx2(command = compilerPrefix, args = args, input = "") if exitCode != 0: echo "$ " & cmdLine & "\n" & buf.string - quit("megatest compilation failed") + quit(failString & "megatest compilation failed") - (buf, exitCode) = execCmdEx(megatestFile.changeFileExt(ExeExt).normalizeExe) + (buf, exitCode) = execCmdEx(megatestFile.changeFileExt(ExeExt).dup normalizeExe) if exitCode != 0: echo buf.string - quit("megatest execution failed") + quit(failString & "megatest execution failed") norm buf.string - writeFile("outputGotten.txt", buf.string) + const outputExceptedFile = "outputExpected.txt" + const outputGottenFile = "outputGotten.txt" + writeFile(outputGottenFile, buf.string) var outputExpected = "" for i, runSpec in specs: outputExpected.add marker & runSpec.file & "\n" @@ -661,14 +651,14 @@ proc runJoinedTest(r: var TResults, cat: Category, testsDir: string) = norm outputExpected if buf.string != outputExpected: - writeFile("outputExpected.txt", outputExpected) - discard execShellCmd("diff -uNdr outputExpected.txt outputGotten.txt") - echo "output different!" - # outputGotten.txt, outputExpected.txt not removed on purpose for debugging. + writeFile(outputExceptedFile, outputExpected) + discard execShellCmd("diff -uNdr $1 $2" % [outputExceptedFile, outputGottenFile]) + echo failString & "megatest output different!" + # outputGottenFile, outputExceptedFile not removed on purpose for debugging. quit 1 else: - echo "output OK" - removeFile("outputGotten.txt") + echo "megatest output OK" + removeFile(outputGottenFile) removeFile(megatestFile) #testSpec r, makeTest("megatest", options, cat) diff --git a/testament/testament.nim b/testament/testament.nim index 098c07ac5..158a68822 100644 --- a/testament/testament.nim +++ b/testament/testament.nim @@ -21,6 +21,7 @@ var backendLogging = true var simulate = false const + failString* = "FAIL: " # ensures all failures can be searched with 1 keyword in CI logs testsDir = "tests" & DirSep resultsFile = "testresults.html" #jsonFile = "testresults.json" # not used @@ -279,7 +280,7 @@ proc addResult(r: var TResults, test: TTest, target: TTarget, elif success == reJoined: maybeStyledEcho styleDim, fgYellow, "JOINED: ", styleBright, fgCyan, name else: - maybeStyledEcho styleBright, fgRed, "FAIL: ", fgCyan, name + maybeStyledEcho styleBright, fgRed, failString, fgCyan, name maybeStyledEcho styleBright, fgCyan, "Test \"", test.name, "\"", " in category \"", test.cat.string, "\"" maybeStyledEcho styleBright, fgRed, "Failure: ", $success if success in {reBuildFailed, reNimcCrash, reInstallFailed}: @@ -701,7 +702,7 @@ proc main() = cmds.add(myself & runtype & quoteShell(cat) & rest) proc progressStatus(idx: int) = - echo "progress[all]: i: " & $idx & " / " & $cats.len & " cat: " & cats[idx] + echo "progress[all]: $1/$2 starting: cat: $3" % [$idx, $cats.len, cats[idx]] if simulate: skips = loadSkipFrom(skipFrom) diff --git a/tests/misc/tunsignedconv.nim b/tests/misc/tunsignedconv.nim index bc7aff30a..0e5fc43db 100644 --- a/tests/misc/tunsignedconv.nim +++ b/tests/misc/tunsignedconv.nim @@ -1,10 +1,4 @@ -discard """ - output: '''uint -1''' -""" - # Tests unsigned literals and implicit conversion between uints and ints -# Passes if it compiles var h8:uint8 = 128 var h16:uint16 = 32768 @@ -53,7 +47,7 @@ block t4176: proc fun(): uint = cast[uint](-1) const x0 = fun() -echo typeof(x0) +doAssert typeof(x0) is uint discard $x0 @@ -62,6 +56,6 @@ discard $x0 const x1 = cast[uint](-1) discard $(x1,) -# bug 13698 -let n: csize = 1 -echo n.int32 +# bug #13698 +let n: csize = 1 # xxx should that be csize_t or is that essential here? +doAssert $n.int32 == "1" diff --git a/tests/testament/tshould_not_work.nim b/tests/testament/tshould_not_work.nim index aca73c308..e7790a4df 100644 --- a/tests/testament/tshould_not_work.nim +++ b/tests/testament/tshould_not_work.nim @@ -31,3 +31,5 @@ FAIL: tests/shouldfail/ttimeout.nim C Failure: reTimeout ''' """ + +# xxx `--nim:../compiler/nim`, doesn't seem correct (and should also honor `testament --nim`) From da29222f86f7689227ffe12605842d18c9bf0fc1 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 23 Jun 2020 10:53:57 +0200 Subject: [PATCH 0266/3768] init checks and 'out' parameters (#14521) * I don't care about observable stores * enforce explicit initializations * cleaner code for the stdlib * stdlib: use explicit initializations * make tests green * algorithm.nim: set result explicitly * remove out parameters and bring the PR into a mergable state * updated the changelog --- changelog.md | 10 +- compiler/ast.nim | 12 +- compiler/ccgcalls.nim | 14 +-- compiler/ccgexprs.nim | 12 +- compiler/ccgtypes.nim | 2 +- compiler/dfa.nim | 6 +- compiler/guards.nim | 2 +- compiler/jsgen.nim | 2 +- compiler/lineinfos.nim | 2 +- compiler/lowerings.nim | 2 +- compiler/nim.cfg | 4 + compiler/nimsets.nim | 32 +++--- compiler/parampatterns.nim | 6 +- compiler/parser.nim | 6 +- compiler/renderer.nim | 8 +- compiler/renderverbatim.nim | 1 + compiler/semcall.nim | 2 +- compiler/semexprs.nim | 6 +- compiler/semmagic.nim | 3 - compiler/semobjconstr.nim | 2 +- compiler/sempass2.nim | 219 ++++++++++++++++++++---------------- compiler/semstmts.nim | 18 +-- compiler/semtypes.nim | 8 +- compiler/sigmatch.nim | 12 +- compiler/spawn.nim | 4 +- compiler/transf.nim | 10 +- compiler/types.nim | 2 +- compiler/writetracking.nim | 6 +- doc/manual.rst | 32 ++++-- doc/manual_experimental.rst | 6 +- lib/core/macros.nim | 7 +- lib/pure/algorithm.nim | 1 + lib/pure/bitops.nim | 2 + lib/pure/os.nim | 6 +- lib/pure/parseutils.nim | 16 ++- lib/pure/pathnorm.nim | 2 +- lib/pure/strutils.nim | 22 +++- lib/pure/times.nim | 18 +-- lib/pure/typetraits.nim | 2 +- lib/pure/unicode.nim | 1 + lib/std/stackframes.nim | 6 +- lib/system.nim | 7 +- lib/system/alloc.nim | 3 +- lib/system/assertions.nim | 1 + lib/system/gc_common.nim | 5 +- lib/system/io.nim | 16 +-- lib/system/repr.nim | 1 + lib/system/sets.nim | 1 + lib/system/strmantle.nim | 6 +- lib/system/widestrs.nim | 1 + tests/assert/tassert_c.nim | 7 +- tests/effects/toutparam.nim | 28 +++++ tests/init/tuninit1.nim | 2 +- 53 files changed, 355 insertions(+), 257 deletions(-) create mode 100644 tests/effects/toutparam.nim diff --git a/changelog.md b/changelog.md index 151eafcd5..9d1454dcb 100644 --- a/changelog.md +++ b/changelog.md @@ -60,7 +60,7 @@ accept an existing string to modify, which avoids memory allocations, similar to `streams.readLine` (#13857). -- Added high-level `asyncnet.sendTo` and `asyncnet.recvFrom`. UDP functionality. +- Added high-level `asyncnet.sendTo` and `asyncnet.recvFrom` UDP functionality. - `dollars.$` now works for unsigned ints with `nim js` @@ -116,7 +116,11 @@ - Add `random.gauss`, that uses the ratio of uniforms method of sampling from a Gaussian distribution. ## Language changes -- In the 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: +- In the newruntime it is now allowed to assign to the discriminator field + without restrictions as long as case object doesn't have custom destructor. + The discriminator value doesn't have to be a constant either. If you have a + custom destructor for a case object and you do want to freely assign discriminator + fields, it is recommended to refactor object into 2 objects like this: ```nim type @@ -175,6 +179,8 @@ proc mydiv(a, b): int {.raises: [].} = The reason for this is that `DivByZeroDefect` inherits from `Defect` and with `--panics:on` `Defects` become unrecoverable errors. +- Added the `thiscall` calling convention as specified by Microsoft. + - Added `thiscall` calling convention as specified by Microsoft, mostly for hooking purpose - Deprecated `{.unroll.}` pragma, was ignored by the compiler anyways, was a nop. diff --git a/compiler/ast.nim b/compiler/ast.nim index 9639f52d3..66a1a253b 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -435,7 +435,8 @@ type # be any type. tyOptDeprecated - # deadcode: was `tyOpt`, Builtin optional type + # 'out' parameter. Comparable to a 'var' parameter but every + # path must assign a value to it before it can be read from. tyVoid # now different from tyEmpty, hurray! @@ -1802,12 +1803,12 @@ proc skipStmtList*(n: PNode): PNode = else: result = n -proc toVar*(typ: PType): PType = +proc toVar*(typ: PType; kind: TTypeKind): PType = ## If ``typ`` is not a tyVar then it is converted into a `var ` and ## returned. Otherwise ``typ`` is simply returned as-is. result = typ - if typ.kind != tyVar: - result = newType(tyVar, typ.owner) + if typ.kind != kind: + result = newType(kind, typ.owner) rawAddSon(result, typ) proc toRef*(typ: PType): PType = @@ -1934,3 +1935,6 @@ proc toHumanStr*(kind: TSymKind): string = proc toHumanStr*(kind: TTypeKind): string = ## strips leading `tk` result = toHumanStrImpl(kind, 2) + +proc skipAddr*(n: PNode): PNode {.inline.} = + (if n.kind == nkHiddenAddr: n[0] else: n) diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 3dd2b54d4..80f02b6c3 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -178,10 +178,10 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode): Rope = result = "($4*)($1)+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c), dest] of tyString, tySequence: let atyp = skipTypes(a.t, abstractInst) - if formalType.skipTypes(abstractInst).kind == tyVar and atyp.kind == tyString and + if formalType.skipTypes(abstractInst).kind in {tyVar} and atyp.kind == tyString and optSeqDestructors in p.config.globalOptions: linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)]) - if atyp.kind == tyVar and not compileToCpp(p.module): + if atyp.kind in {tyVar} and not compileToCpp(p.module): result = "($5*)(*$1)$4+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c), dataField(p), dest] else: result = "($5*)$1$4+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c), dataField(p), dest] @@ -194,10 +194,10 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode): Rope = result = "$1, $1Len_0" % [rdLoc(a)] of tyString, tySequence: let ntyp = skipTypes(n.typ, abstractInst) - if formalType.skipTypes(abstractInst).kind == tyVar and ntyp.kind == tyString and + if formalType.skipTypes(abstractInst).kind in {tyVar} and ntyp.kind == tyString and optSeqDestructors in p.config.globalOptions: linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)]) - if ntyp.kind == tyVar and not compileToCpp(p.module): + if ntyp.kind in {tyVar} and not compileToCpp(p.module): var t: TLoc t.r = "(*$1)" % [a.rdLoc] result = "(*$1)$3, $2" % [a.rdLoc, lenExpr(p, t), dataField(p)] @@ -232,7 +232,7 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode): Rope = elif ccgIntroducedPtr(p.config, param, call[0].typ[0]): initLocExpr(p, n, a) result = addrLoc(p.config, a) - elif p.module.compileToCpp and param.typ.kind == tyVar and + elif p.module.compileToCpp and param.typ.kind in {tyVar} and n.kind == nkHiddenAddr: initLocExprSingleUse(p, n[0], a) # if the proc is 'importc'ed but not 'importcpp'ed then 'var T' still @@ -372,7 +372,7 @@ proc genOtherArg(p: BProc; ri: PNode; i: int; typ: PType): Rope = assert(paramType.kind == nkSym) if paramType.typ.isCompileTimeOnly: result = nil - elif typ[i].kind == tyVar and ri[i].kind == nkHiddenAddr: + elif typ[i].kind in {tyVar} and ri[i].kind == nkHiddenAddr: result = genArgNoParam(p, ri[i][0]) else: result = genArgNoParam(p, ri[i]) #, typ.n[i].sym) @@ -449,7 +449,7 @@ proc genThisArg(p: BProc; ri: PNode; i: int; typ: PType): Rope = var ri = ri[i] while ri.kind == nkObjDownConv: ri = ri[0] let t = typ[i].skipTypes({tyGenericInst, tyAlias, tySink}) - if t.kind == tyVar: + if t.kind in {tyVar}: let x = if ri.kind == nkHiddenAddr: ri[0] else: ri if x.typ.kind == tyPtr: result = genArgNoParam(p, x) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 2850ab750..a59529021 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -126,9 +126,8 @@ proc genRawSetData(cs: TBitSet, size: int): Rope = result = intLiteral(cast[BiggestInt](bitSetToWord(cs, size))) proc genSetNode(p: BProc, n: PNode): Rope = - var cs: TBitSet var size = int(getSize(p.config, n.typ)) - toBitSet(p.config, n, cs) + let cs = toBitSet(p.config, n) if size > 8: let id = nodeTableTestOrSet(p.module.dataCache, n, p.module.labels) result = p.module.tmpBase & rope(id) @@ -676,7 +675,7 @@ proc unaryArith(p: BProc, e: PNode, d: var TLoc, op: TMagic) = proc isCppRef(p: BProc; typ: PType): bool {.inline.} = result = p.module.compileToCpp and - skipTypes(typ, abstractInstOwned).kind == tyVar and + skipTypes(typ, abstractInstOwned).kind in {tyVar} and tfVarIsPtr notin skipTypes(typ, abstractInstOwned).flags proc genDeref(p: BProc, e: PNode, d: var TLoc) = @@ -693,7 +692,7 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc) = if typ.kind in {tyUserTypeClass, tyUserTypeClassInst} and typ.isResolvedUserTypeClass: typ = typ.lastSon typ = typ.skipTypes(abstractInstOwned) - if typ.kind == tyVar and tfVarIsPtr notin typ.flags and p.module.compileToCpp and e[0].kind == nkHiddenAddr: + if typ.kind in {tyVar} and tfVarIsPtr notin typ.flags and p.module.compileToCpp and e[0].kind == nkHiddenAddr: initLocExprSingleUse(p, e[0][0], d) return else: @@ -716,7 +715,7 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc) = else: internalError(p.config, e.info, "genDeref " & $typ.kind) elif p.module.compileToCpp: - if typ.kind == tyVar and tfVarIsPtr notin typ.flags and + if typ.kind in {tyVar} and tfVarIsPtr notin typ.flags and e.kind == nkHiddenDeref: putIntoDest(p, d, e, rdLoc(a), a.storage) return @@ -2960,8 +2959,7 @@ proc genBracedInit(p: BProc, n: PNode; isConst: bool): Rope = ty = skipTypes(n.typ, abstractInstOwned + {tyStatic}).kind case ty of tySet: - var cs: TBitSet - toBitSet(p.config, n, cs) + let cs = toBitSet(p.config, n) result = genRawSetData(cs, int(getSize(p.config, n.typ))) of tySequence: if optSeqDestructors in p.config.globalOptions: diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 199d5c918..11743499d 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -701,7 +701,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope = return case t.kind of tyRef, tyPtr, tyVar, tyLent: - var star = if t.kind == tyVar and tfVarIsPtr notin origTyp.flags and + var star = if t.kind in {tyVar} and tfVarIsPtr notin origTyp.flags and compileToCpp(m): "&" else: "*" var et = origTyp.skipTypes(abstractInst).lastSon var etB = et.skipTypes(abstractInst) diff --git a/compiler/dfa.nim b/compiler/dfa.nim index a0ec31ac6..c393b2c81 100644 --- a/compiler/dfa.nim +++ b/compiler/dfa.nim @@ -715,10 +715,8 @@ proc genCall(c: var Con; n: PNode) = for i in 1.. 0 and canRaiseConservative(n[0]): diff --git a/compiler/guards.nim b/compiler/guards.nim index e4f87bae4..a6ca44978 100644 --- a/compiler/guards.nim +++ b/compiler/guards.nim @@ -46,7 +46,7 @@ proc isLet(n: PNode): bool = if n.sym.kind in {skLet, skTemp, skForVar}: result = true elif n.sym.kind == skParam and skipTypes(n.sym.typ, - abstractInst).kind != tyVar: + abstractInst).kind notin {tyVar}: result = true proc isVar(n: PNode): bool = diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 99d2bc4f2..57a6b8094 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -1053,7 +1053,7 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = lineF(p, "$1 = nimCopy(null, $2, $3);$n", [a.rdLoc, b.res, genTypeInfo(p, y.typ)]) of etyObject: - if x.typ.kind == tyVar or (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded: + if x.typ.kind in {tyVar} or (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded: lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc]) else: useMagic(p, "nimCopy") diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index bc3c51d53..6287e21aa 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -113,7 +113,7 @@ const warnStaticIndexCheck: "$1", warnGcUnsafe: "not GC-safe: '$1'", warnGcUnsafe2: "$1", - warnUninit: "'$1' might not have been initialized", + warnUninit: "use explicit initialization of '$1' for clarity", warnGcMem: "'$1' uses GC'ed memory", warnDestructor: "usage of a type with a destructor in a non destructible context. This will become a compile time error in the future.", warnLockLevel: "$1", diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index 575bfe8aa..5e75c36de 100644 --- a/compiler/lowerings.nim +++ b/compiler/lowerings.nim @@ -59,7 +59,7 @@ proc newFastAsgnStmt*(le, ri: PNode): PNode = result[0] = le result[1] = ri -proc newFastMoveStmt*(g: ModuleGraph, le, ri: PNode): PNode = +proc newFastMoveStmt*(g: ModuleGraph, le, ri: PNode): PNode = result = newNodeI(nkFastAsgn, le.info, 2) result[0] = le result[1] = newNodeIT(nkCall, ri.info, ri.typ) diff --git a/compiler/nim.cfg b/compiler/nim.cfg index a77fa84d3..8e733bdf8 100644 --- a/compiler/nim.cfg +++ b/compiler/nim.cfg @@ -21,3 +21,7 @@ define:useStdoutAsStdmsg #define:useNodeIds #gc:markAndSweep + +@if nimHasWarningObservableStores: + warning[ObservableStores]: off +@end diff --git a/compiler/nimsets.nim b/compiler/nimsets.nim index 3876a114e..8683604af 100644 --- a/compiler/nimsets.nim +++ b/compiler/nimsets.nim @@ -58,18 +58,18 @@ proc someInSet*(s: PNode, a, b: PNode): bool = return true result = false -proc toBitSet*(conf: ConfigRef; s: PNode, b: var TBitSet) = +proc toBitSet*(conf: ConfigRef; s: PNode): TBitSet = var first, j: Int128 first = firstOrd(conf, s.typ[0]) - bitSetInit(b, int(getSize(conf, s.typ))) + bitSetInit(result, int(getSize(conf, s.typ))) for i in 0.. 0: result = bracketKind(g, n[0]) - of nkSym: + of nkSym: result = case n.sym.name.s of "[]": bkBracket of "[]=": bkBracketAsgn @@ -974,7 +974,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = put(g, tkParRi, ")") put(g, tkColon, ":") gsub(g, n, n.len-1) - elif n.len >= 1: + elif n.len >= 1: case bracketKind(g, n[0]) of bkBracket: gsub(g, n, 1) diff --git a/compiler/renderverbatim.nim b/compiler/renderverbatim.nim index d20ee1549..2dce6824c 100644 --- a/compiler/renderverbatim.nim +++ b/compiler/renderverbatim.nim @@ -107,6 +107,7 @@ proc extractRunnableExamplesSource*(conf: ConfigRef; n: PNode): string = var indent = info.col let numLines = numLines(conf, info.fileIndex).uint16 var lastNonemptyPos = 0 + result = "" var ldata = LineData(lineFirst: first.line.int, conf: conf) visitMultilineStrings(ldata, n[^1]) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 665eb4ea4..405de52ee 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -676,7 +676,7 @@ proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): PSym = if t.kind == tyDistinct or param.typ.kind == tyDistinct: hasDistinct = true var x: PType if param.typ.kind == tyVar: - x = newTypeS(tyVar, c) + x = newTypeS(param.typ.kind, c) x.addSonSkipIntLit t.baseOfDistinct else: x = t.baseOfDistinct diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 200d1d60a..6f267b4eb 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -712,7 +712,7 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) = # note sometimes this is eval'ed twice so we check for nkHiddenAddr here: for i in 1.. 0: @@ -257,20 +270,11 @@ proc useVar(a: PEffects, n: PNode) = elif s.id notin a.init: if s.typ.requiresInit: message(a.config, n.info, warnProveInit, s.name.s) - else: + elif a.leftPartOfAsgn <= 0: message(a.config, n.info, warnUninit, s.name.s) # prevent superfluous warnings about the same variable: a.init.add s.id - if {sfGlobal, sfThread} * s.flags != {} and s.kind in {skVar, skLet} and - s.magic != mNimvm: - if s.guard != nil: guardGlobal(a, n, s.guard) - if {sfGlobal, sfThread} * s.flags == {sfGlobal} and - (tfHasGCedMem in s.typ.flags or s.typ.isGCedMem): - #if a.config.hasWarn(warnGcUnsafe): warnAboutGcUnsafe(n) - markGcUnsafe(a, s) - markSideEffect(a, s) - else: - markSideEffect(a, s) + useVarNoInitCheck(a, n, s) type @@ -538,7 +542,7 @@ proc isNoEffectList(n: PNode): bool {.inline.} = proc isTrival(caller: PNode): bool {.inline.} = result = caller.kind == nkSym and caller.sym.magic in {mEqProc, mIsNil, mMove, mWasMoved} -proc trackOperand(tracked: PEffects, n: PNode, paramType: PType; caller: PNode) = +proc trackOperandForIndirectCall(tracked: PEffects, n: PNode, paramType: PType; caller: PNode) = let a = skipConvAndClosure(n) let op = a.typ # assume indirect calls are taken here: @@ -572,7 +576,7 @@ proc trackOperand(tracked: PEffects, n: PNode, paramType: PType; caller: PNode) markGcUnsafe(tracked, a) elif tfNoSideEffect notin op.flags: markSideEffect(tracked, a) - if paramType != nil and paramType.kind == tyVar: + if paramType != nil and paramType.kind in {tyVar}: invalidateFacts(tracked.guards, n) if n.kind == nkSym and isLocalVar(tracked, n.sym): makeVolatile(tracked, n.sym) @@ -732,7 +736,7 @@ proc createTypeBoundOps(tracked: PEffects, typ: PType; info: TLineInfo) = optSeqDestructors in tracked.config.globalOptions: tracked.owner.flags.incl sfInjectDestructors -proc track(tracked: PEffects, n: PNode) = +proc trackCall(tracked: PEffects; n: PNode) = template gcsafeAndSideeffectCheck() = if notGcSafe(op) and not importedFromC(a): # and it's not a recursive call: @@ -744,11 +748,105 @@ proc track(tracked: PEffects, n: PNode) = if not (a.kind == nkSym and a.sym == tracked.owner): markSideEffect(tracked, a) + # p's effects are ours too: + var a = n[0] + #if canRaise(a): + # echo "this can raise ", tracked.config $ n.info + let op = a.typ + if n.typ != nil: + if tracked.owner.kind != skMacro and n.typ.skipTypes(abstractVar).kind != tyOpenArray: + createTypeBoundOps(tracked, n.typ, n.info) + if getConstExpr(tracked.ownerModule, n, tracked.graph) != nil: + return + if a.kind == nkCast and a[1].typ.kind == tyProc: + a = a[1] + # XXX: in rare situations, templates and macros will reach here after + # calling getAst(templateOrMacro()). Currently, templates and macros + # are indistinguishable from normal procs (both have tyProc type) and + # we can detect them only by checking for attached nkEffectList. + if op != nil and op.kind == tyProc and op.n[0].kind == nkEffectList: + if a.kind == nkSym: + if a.sym == tracked.owner: tracked.isRecursive = true + # even for recursive calls we need to check the lock levels (!): + mergeLockLevels(tracked, n, a.sym.getLockLevel) + if sfSideEffect in a.sym.flags: markSideEffect(tracked, a) + else: + mergeLockLevels(tracked, n, op.lockLevel) + var effectList = op.n[0] + if a.kind == nkSym and a.sym.kind == skMethod: + propagateEffects(tracked, n, a.sym) + elif isNoEffectList(effectList): + if isForwardedProc(a): + propagateEffects(tracked, n, a.sym) + elif isIndirectCall(a, tracked.owner): + assumeTheWorst(tracked, n, op) + gcsafeAndSideeffectCheck() + else: + mergeEffects(tracked, effectList[exceptionEffects], n) + mergeTags(tracked, effectList[tagEffects], n) + gcsafeAndSideeffectCheck() + if a.kind != nkSym or a.sym.magic != mNBindSym: + for i in 1.. 0: + createTypeBoundOps(tracked, n[1].typ.lastSon, n.info) + createTypeBoundOps(tracked, n[1].typ, n.info) + # new(x, finalizer): Problem: how to move finalizer into 'createTypeBoundOps'? + + elif a.kind == nkSym and a.sym.magic in {mArrGet, mArrPut} and + optStaticBoundsCheck in tracked.currOptions: + checkBounds(tracked, n[1], n[2]) + + if a.kind == nkSym and a.sym.name.s.len > 0 and a.sym.name.s[0] == '=' and + tracked.owner.kind != skMacro: + let opKind = find(AttachedOpToStr, a.sym.name.s.normalize) + if opKind != -1: + # rebind type bounds operations after createTypeBoundOps call + let t = n[1].typ.skipTypes({tyAlias, tyVar}) + if a.sym != t.attachedOps[TTypeAttachedOp(opKind)]: + createTypeBoundOps(tracked, t, n.info) + let op = t.attachedOps[TTypeAttachedOp(opKind)] + if op != nil: + n[0].sym = op + + if a.kind != nkSym or a.sym.magic != mRunnableExamples: + for i in 0.. 0: - createTypeBoundOps(tracked, n[1].typ.lastSon, n.info) - createTypeBoundOps(tracked, n[1].typ, n.info) - # new(x, finalizer): Problem: how to move finalizer into 'createTypeBoundOps'? - - elif a.kind == nkSym and a.sym.magic in {mArrGet, mArrPut} and - optStaticBoundsCheck in tracked.currOptions: - checkBounds(tracked, n[1], n[2]) - - if a.kind == nkSym and a.sym.name.s.len > 0 and a.sym.name.s[0] == '=' and - tracked.owner.kind != skMacro: - let opKind = find(AttachedOpToStr, a.sym.name.s.normalize) - if opKind != -1: - # rebind type bounds operations after createTypeBoundOps call - let t = n[1].typ.skipTypes({tyAlias, tyVar}) - if a.sym != t.attachedOps[TTypeAttachedOp(opKind)]: - createTypeBoundOps(tracked, t, n.info) - let op = t.attachedOps[TTypeAttachedOp(opKind)] - if op != nil: - n[0].sym = op - - if a.kind != nkSym or a.sym.magic != mRunnableExamples: - for i in 0.. x @@ -569,14 +569,14 @@ proc putArgInto(arg: PNode, formal: PType): TPutArgInto = result = putArgInto(arg[0], formal) of nkCurly, nkBracket: for i in 0.. 0 and p[0].kind == nnkSym and p[0] == cp: diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim index 4a6c2510a..a277d1429 100644 --- a/lib/pure/algorithm.nim +++ b/lib/pure/algorithm.nim @@ -187,6 +187,7 @@ proc binarySearch*[T, K](a: openArray[T], key: K, else: return -1 + result = 0 if (len and (len - 1)) == 0: # when `len` is a power of 2, a faster shr can be used. var step = len shr 1 diff --git a/lib/pure/bitops.nim b/lib/pure/bitops.nim index 7dd171a38..4f22388f3 100644 --- a/lib/pure/bitops.nim +++ b/lib/pure/bitops.nim @@ -395,6 +395,8 @@ proc firstSetBitNim(x: uint64): int {.inline, noSideEffect.} = if k == 0: k = uint32(v shr 32'u32) and 0xFFFFFFFF'u32 result = 32 + else: + result = 0 result += firstSetBitNim(k) proc fastlog2Nim(x: uint32): int {.inline, noSideEffect.} = diff --git a/lib/pure/os.nim b/lib/pure/os.nim index bb09c966f..dcb63458b 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -417,7 +417,8 @@ proc relativePath*(path, base: string, sep = DirSep): string {. if not sameRoot(path, base): return path - var f, b: PathIter + var f = default PathIter + var b = default PathIter var ff = (0, -1) var bb = (0, -1) # (int, int) result = newStringOfCap(path.len) @@ -1053,6 +1054,7 @@ when defined(windows) or defined(posix) or defined(nintendoswitch): assert quoteShellCommand(["aaa", "", "c d"]) == "aaa \"\" \"c d\"" # can't use `map` pending https://github.com/nim-lang/Nim/issues/8303 + result = "" for i in 0.. 0: result.add " " result.add quoteShell(args[i]) @@ -2981,7 +2983,7 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noW setLen(result, L) break elif defined(macosx): - var size: cuint32 + var size = cuint32(0) getExecPath1(nil, size) result = newString(int(size)) if getExecPath2(result, size): diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim index c358005f1..baf238843 100644 --- a/lib/pure/parseutils.nim +++ b/lib/pure/parseutils.nim @@ -249,6 +249,7 @@ proc skipWhitespace*(s: string, start = 0): int {.inline.} = doAssert skipWhitespace(" Hello World", 0) == 1 doAssert skipWhitespace("Hello World", 5) == 1 doAssert skipWhitespace("Hello World", 5) == 2 + result = 0 while start+result < s.len and s[start+result] in Whitespace: inc(result) proc skip*(s, token: string, start = 0): int {.inline.} = @@ -260,6 +261,7 @@ proc skip*(s, token: string, start = 0): int {.inline.} = doAssert skip("2019-01-22", "19", 2) == 2 doAssert skip("CAPlow", "CAP", 0) == 3 doAssert skip("CAPlow", "cap", 0) == 0 + result = 0 while start+result < s.len and result < token.len and s[result+start] == token[result]: inc(result) @@ -270,6 +272,7 @@ proc skipIgnoreCase*(s, token: string, start = 0): int = runnableExamples: doAssert skipIgnoreCase("CAPlow", "CAP", 0) == 3 doAssert skipIgnoreCase("CAPlow", "cap", 0) == 3 + result = 0 while start+result < s.len and result < token.len and toLower(s[result+start]) == toLower(token[result]): inc(result) if result != token.len: result = 0 @@ -282,6 +285,7 @@ proc skipUntil*(s: string, until: set[char], start = 0): int {.inline.} = doAssert skipUntil("Hello World", {'W', 'e'}, 0) == 1 doAssert skipUntil("Hello World", {'W'}, 0) == 6 doAssert skipUntil("Hello World", {'W', 'd'}, 0) == 6 + result = 0 while start+result < s.len and s[result+start] notin until: inc(result) proc skipUntil*(s: string, until: char, start = 0): int {.inline.} = @@ -293,6 +297,7 @@ proc skipUntil*(s: string, until: char, start = 0): int {.inline.} = doAssert skipUntil("Hello World", 'o', 4) == 0 doAssert skipUntil("Hello World", 'W', 0) == 6 doAssert skipUntil("Hello World", 'w', 0) == 11 + result = 0 while start+result < s.len and s[result+start] != until: inc(result) proc skipWhile*(s: string, toSkip: set[char], start = 0): int {.inline.} = @@ -302,6 +307,7 @@ proc skipWhile*(s: string, toSkip: set[char], start = 0): int {.inline.} = doAssert skipWhile("Hello World", {'H', 'e'}) == 2 doAssert skipWhile("Hello World", {'e'}) == 0 doAssert skipWhile("Hello World", {'W', 'o', 'r'}, 6) == 3 + result = 0 while start+result < s.len and s[result+start] in toSkip: inc(result) proc parseUntil*(s: string, token: var string, until: set[char], @@ -437,7 +443,7 @@ proc parseBiggestInt*(s: string, number: var BiggestInt, start = 0): int {. var res: BiggestInt doAssert parseBiggestInt("9223372036854775807", res, 0) == 19 doAssert res == 9223372036854775807 - var res: BiggestInt + var res = BiggestInt(0) # use 'res' for exception safety (don't write to 'number' in case of an # overflow exception): result = rawParseInt(s, res, start) @@ -455,7 +461,7 @@ proc parseInt*(s: string, number: var int, start = 0): int {. doAssert res == 2019 doAssert parseInt("2019", res, 2) == 2 doAssert res == 19 - var res: BiggestInt + var res = BiggestInt(0) result = parseBiggestInt(s, res, start) when sizeof(int) <= 4: if res < low(int) or res > high(int): @@ -518,7 +524,7 @@ proc parseBiggestUInt*(s: string, number: var BiggestUInt, start = 0): int {. doAssert res == 12 doAssert parseBiggestUInt("1111111111111111111", res, 0) == 19 doAssert res == 1111111111111111111'u64 - var res: BiggestUInt + var res = BiggestUInt(0) # use 'res' for exception safety (don't write to 'number' in case of an # overflow exception): result = rawParseUInt(s, res, start) @@ -536,7 +542,7 @@ proc parseUInt*(s: string, number: var uint, start = 0): int {. doAssert res == 3450 doAssert parseUInt("3450", res, 2) == 2 doAssert res == 50 - var res: BiggestUInt + var res = BiggestUInt(0) result = parseBiggestUInt(s, res, start) when sizeof(BiggestUInt) > sizeof(uint) and sizeof(uint) <= 4: if res > 0xFFFF_FFFF'u64: @@ -563,7 +569,7 @@ proc parseFloat*(s: string, number: var float, start = 0): int {. doAssert res == 32.57 doAssert parseFloat("32.57", res, 3) == 2 doAssert res == 57.00 - var bf: BiggestFloat + var bf = BiggestFloat(0.0) result = parseBiggestFloat(s, bf, start) if result != 0: number = bf diff --git a/lib/pure/pathnorm.nim b/lib/pure/pathnorm.nim index 8763ba878..03a65bd60 100644 --- a/lib/pure/pathnorm.nim +++ b/lib/pure/pathnorm.nim @@ -44,7 +44,7 @@ proc next*(it: var PathIter; x: string): (int, int) = it.notFirst = true iterator dirs(x: string): (int, int) = - var it: PathIter + var it = default PathIter while hasNext(it, x): yield next(it, x) proc isDot(x: string; bounds: (int, int)): bool = diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 7c00de2d2..1ffc8bf22 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -1103,6 +1103,7 @@ proc parseInt*(s: string): int {.noSideEffect, ## If `s` is not a valid integer, `ValueError` is raised. runnableExamples: doAssert parseInt("-0042") == -42 + result = 0 let L = parseutils.parseInt(s, result, 0) if L != s.len or L == 0: raise newException(ValueError, "invalid integer: " & s) @@ -1112,6 +1113,7 @@ proc parseBiggestInt*(s: string): BiggestInt {.noSideEffect, ## Parses a decimal integer value contained in `s`. ## ## If `s` is not a valid integer, `ValueError` is raised. + result = BiggestInt(0) let L = parseutils.parseBiggestInt(s, result, 0) if L != s.len or L == 0: raise newException(ValueError, "invalid integer: " & s) @@ -1121,6 +1123,7 @@ proc parseUInt*(s: string): uint {.noSideEffect, ## Parses a decimal unsigned integer value contained in `s`. ## ## If `s` is not a valid integer, `ValueError` is raised. + result = uint(0) let L = parseutils.parseUInt(s, result, 0) if L != s.len or L == 0: raise newException(ValueError, "invalid unsigned integer: " & s) @@ -1130,6 +1133,7 @@ proc parseBiggestUInt*(s: string): BiggestUInt {.noSideEffect, ## Parses a decimal unsigned integer value contained in `s`. ## ## If `s` is not a valid integer, `ValueError` is raised. + result = BiggestUInt(0) let L = parseutils.parseBiggestUInt(s, result, 0) if L != s.len or L == 0: raise newException(ValueError, "invalid unsigned integer: " & s) @@ -1143,6 +1147,7 @@ proc parseFloat*(s: string): float {.noSideEffect, runnableExamples: doAssert parseFloat("3.14") == 3.14 doAssert parseFloat("inf") == 1.0/0 + result = 0.0 let L = parseutils.parseFloat(s, result, 0) if L != s.len or L == 0: raise newException(ValueError, "invalid float: " & s) @@ -1161,6 +1166,7 @@ proc parseBinInt*(s: string): int {.noSideEffect, doAssert a.parseBinInt() == 53 doAssert b.parseBinInt() == 7 + result = 0 let L = parseutils.parseBin(s, result, 0) if L != s.len or L == 0: raise newException(ValueError, "invalid binary integer: " & s) @@ -1172,6 +1178,7 @@ proc parseOctInt*(s: string): int {.noSideEffect, ## If `s` is not a valid oct integer, `ValueError` is raised. `s` can have one ## of the following optional prefixes: ``0o``, ``0O``. Underscores within ## `s` are ignored. + result = 0 let L = parseutils.parseOct(s, result, 0) if L != s.len or L == 0: raise newException(ValueError, "invalid oct integer: " & s) @@ -1183,6 +1190,7 @@ proc parseHexInt*(s: string): int {.noSideEffect, ## If `s` is not a valid hex integer, `ValueError` is raised. `s` can have one ## of the following optional prefixes: ``0x``, ``0X``, ``#``. Underscores ## within `s` are ignored. + result = 0 let L = parseutils.parseHex(s, result, 0) if L != s.len or L == 0: raise newException(ValueError, "invalid hex integer: " & s) @@ -1270,9 +1278,9 @@ macro genEnumStmt(typ: typedesc, argSym: typed, default: typed): untyped = result = nnkCaseStmt.newTree(nnkDotExpr.newTree(argSym, bindSym"nimIdentNormalize")) # stores all processed field strings to give error msg for ambiguous enums - var foundFields: seq[string] - var fStr: string # string of current field - var fNum: BiggestInt # int value of current field + var foundFields: seq[string] = @[] + var fStr = "" # string of current field + var fNum = BiggestInt(0) # int value of current field for f in impl: case f.kind of nnkEmpty: continue # skip first node of `enumTy` @@ -2028,6 +2036,7 @@ proc rfind*(s, sub: string, start: Natural = 0, last = -1): int {.noSideEffect, if sub.len == 0: return -1 let last = if last == -1: s.high else: last + result = 0 for i in countdown(last - sub.len + 1, start): for j in 0..sub.len-1: result = i @@ -2044,6 +2053,7 @@ proc count*(s: string, sub: char): int {.noSideEffect, ## ## See also: ## * `countLines proc<#countLines,string>`_ + result = 0 for c in s: if c == sub: inc result @@ -2054,6 +2064,7 @@ proc count*(s: string, subs: set[char]): int {.noSideEffect, ## See also: ## * `countLines proc<#countLines,string>`_ doAssert card(subs) > 0 + result = 0 for c in s: if c in subs: inc result @@ -2066,6 +2077,7 @@ proc count*(s: string, sub: string, overlapping: bool = false): int {. ## See also: ## * `countLines proc<#countLines,string>`_ doAssert sub.len > 0 + result = 0 var i = 0 while true: i = s.find(sub, i) @@ -2313,7 +2325,7 @@ proc unescape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect, case s[i+1]: of 'x': inc i, 2 - var c: int + var c = 0 i += parseutils.parseHex(s, c, i, maxLen = 2) result.add(chr(c)) dec i, 2 @@ -2514,7 +2526,7 @@ proc formatSize*(bytes: int64, xb: int64 = bytes fbytes: float lastXb: int64 = bytes - matchedIndex: int + matchedIndex = 0 prefixes: array[9, string] if prefix == bpColloquial: prefixes = collPrefixes diff --git a/lib/pure/times.nim b/lib/pure/times.nim index 7d82733e1..fb2f5e430 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -895,16 +895,16 @@ proc getTime*(): Time {.tags: [TimeEffect], benign.} = millis mod convert(Seconds, Milliseconds, 1).int) result = initTime(seconds, nanos) elif defined(macosx): - var a: Timeval + var a {.noinit.}: Timeval gettimeofday(a) result = initTime(a.tv_sec.int64, convert(Microseconds, Nanoseconds, a.tv_usec.int)) elif defined(posix): - var ts: Timespec + var ts {.noinit.}: Timespec discard clock_gettime(CLOCK_REALTIME, ts) result = initTime(ts.tv_sec.int64, ts.tv_nsec.int) elif defined(windows): - var f: FILETIME + var f {.noinit.}: FILETIME getSystemTimeAsFileTime(f) result = fromWinTime(rdFileTime(f)) @@ -1756,7 +1756,7 @@ proc formatPattern(dt: DateTime, pattern: FormatPattern, result: var string, proc parsePattern(input: string, pattern: FormatPattern, i: var int, parsed: var ParsedTime, loc: DateTimeLocale): bool = template takeInt(allowedWidth: Slice[int], allowSign = false): int = - var sv: int + var sv = 0 var pd = parseInt(input, sv, i, allowedWidth.b, allowSign) if pd < allowedWidth.a: return false @@ -1987,6 +1987,7 @@ proc format*(dt: DateTime, f: TimeFormat, let dt = initDateTime(01, mJan, 2000, 00, 00, 00, utc()) doAssert "2000-01-01" == dt.format(f) assertDateTimeInitialized dt + result = "" var idx = 0 while idx <= f.patterns.high: case f.patterns[idx].FormatPattern @@ -2284,7 +2285,7 @@ proc between*(startDt, endDt: DateTime): TimeInterval = endDate.monthday.dec # Years - result.years.inc endDate.year - startDate.year - 1 + result.years = endDate.year - startDate.year - 1 if (startDate.month, startDate.monthday) <= (endDate.month, endDate.monthday): result.years.inc startDate.year.inc result.years @@ -2441,6 +2442,7 @@ proc evaluateInterval(dt: DateTime, interval: TimeInterval): var months = interval.years * 12 + interval.months var curYear = dt.year var curMonth = dt.month + result = default(tuple[adjDur, absDur: Duration]) # Subtracting if months < 0: for mth in countdown(-1 * months, 1): @@ -2562,17 +2564,17 @@ proc epochTime*(): float {.tags: [TimeEffect].} = ## ## ``getTime`` should generally be preferred over this proc. when defined(macosx): - var a: Timeval + var a {.noinit.}: Timeval gettimeofday(a) result = toBiggestFloat(a.tv_sec.int64) + toBiggestFloat( a.tv_usec)*0.00_0001 elif defined(posix): - var ts: Timespec + var ts {.noinit.}: Timespec discard clock_gettime(CLOCK_REALTIME, ts) result = toBiggestFloat(ts.tv_sec.int64) + toBiggestFloat(ts.tv_nsec.int64) / 1_000_000_000 elif defined(windows): - var f: winlean.FILETIME + var f {.noinit.}: winlean.FILETIME getSystemTimeAsFileTime(f) var i64 = rdFileTime(f) - epochDiff var secs = i64 div rateDiff diff --git a/lib/pure/typetraits.nim b/lib/pure/typetraits.nim index 9a215a353..317376405 100644 --- a/lib/pure/typetraits.nim +++ b/lib/pure/typetraits.nim @@ -112,7 +112,7 @@ macro genericParamsImpl(T: typedesc): untyped = of nnkBracketExpr: for i in 1.. 0, "getBigChunk 2") var size = size # roundup(size, PageSize) - var fl, sl: int + var fl = 0 + var sl = 0 mappingSearch(size, fl, sl) sysAssert((size and PageMask) == 0, "getBigChunk: unaligned chunk") result = findSuitableBlock(a, fl, sl) diff --git a/lib/system/assertions.nim b/lib/system/assertions.nim index ca3bd7bc1..c6283c89c 100644 --- a/lib/system/assertions.nim +++ b/lib/system/assertions.nim @@ -11,6 +11,7 @@ proc `$`(x: int): string {.magic: "IntToStr", noSideEffect.} proc `$`(info: InstantiationInfo): string = # The +1 is needed here # instead of overriding `$` (and changing its meaning), consider explicit name. + result = "" result.toLocation(info.filename, info.line, info.column+1) # --------------------------------------------------------------------------- diff --git a/lib/system/gc_common.nim b/lib/system/gc_common.nim index d75ada791..7f6c41e1b 100644 --- a/lib/system/gc_common.nim +++ b/lib/system/gc_common.nim @@ -217,7 +217,7 @@ proc stackSize(stack: ptr GcStack): int {.noinline.} = when nimCoroutines: var pos = stack.pos else: - var pos {.volatile.}: pointer + var pos {.volatile, noinit.}: pointer pos = addr(pos) if pos != nil: @@ -229,6 +229,7 @@ proc stackSize(stack: ptr GcStack): int {.noinline.} = result = 0 proc stackSize(): int {.noinline.} = + result = 0 for stack in gch.stack.items(): result = result + stack.stackSize() @@ -303,7 +304,7 @@ when not defined(useNimRtl): {.pop.} proc isOnStack(p: pointer): bool = - var stackTop {.volatile.}: pointer + var stackTop {.volatile, noinit.}: pointer stackTop = addr(stackTop) var a = cast[ByteAddress](gch.getActiveStack().bottom) var b = cast[ByteAddress](stackTop) diff --git a/lib/system/io.nim b/lib/system/io.nim index d1a7f1fc7..482057214 100644 --- a/lib/system/io.nim +++ b/lib/system/io.nim @@ -424,12 +424,12 @@ proc write*(f: File, b: bool) {.tags: [WriteIOEffect], benign.} = else: write(f, "false") proc write*(f: File, r: float32) {.tags: [WriteIOEffect], benign.} = - var buffer: array[65, char] + var buffer {.noinit.}: array[65, char] discard writeFloatToBuffer(buffer, r) if c_fprintf(f, "%s", buffer[0].addr) < 0: checkErr(f) proc write*(f: File, r: BiggestFloat) {.tags: [WriteIOEffect], benign.} = - var buffer: array[65, char] + var buffer {.noinit.}: array[65, char] discard writeFloatToBuffer(buffer, r) if c_fprintf(f, "%s", buffer[0].addr) < 0: checkErr(f) @@ -591,7 +591,7 @@ when defined(posix) and not defined(nimscript): proc open*(f: var File, filename: string, mode: FileMode = fmRead, - bufSize: int = -1): bool {.tags: [], raises: [], benign.} = + bufSize: int = -1): bool {.tags: [], raises: [], benign.} = ## Opens a file named `filename` with given `mode`. ## ## Default mode is readonly. Returns true if the file could be opened. @@ -605,7 +605,7 @@ proc open*(f: var File, filename: string, # How `fopen` handles opening a directory is not specified in ISO C and # POSIX. We do not want to handle directories as regular files that can # be opened. - var res: Stat + var res {.noinit.}: Stat if c_fstat(getFileHandle(f2), res) >= 0'i32 and modeIsDir(res.st_mode): close(f2) return false @@ -759,7 +759,7 @@ proc readFile*(filename: string): TaintedString {.tags: [ReadIOEffect], benign.} ## Raises an IO exception in case of an error. If you need to call ## this inside a compile time macro you can use `staticRead ## `_. - var f: File + var f: File = nil if open(f, filename): try: result = readAll(f) @@ -772,7 +772,7 @@ proc writeFile*(filename, content: string) {.tags: [WriteIOEffect], benign.} = ## Opens a file named `filename` for writing. Then writes the ## `content` completely to the file and closes the file afterwards. ## Raises an IO exception in case of an error. - var f: File + var f: File = nil if open(f, filename, fmWrite): try: f.write(content) @@ -785,7 +785,7 @@ proc writeFile*(filename: string, content: openArray[byte]) {.since: (1, 1).} = ## Opens a file named `filename` for writing. Then writes the ## `content` completely to the file and closes the file afterwards. ## Raises an IO exception in case of an error. - var f: File + var f: File = nil if open(f, filename, fmWrite): try: f.writeBuffer(unsafeAddr content[0], content.len) @@ -799,7 +799,7 @@ proc readLines*(filename: string, n: Natural): seq[TaintedString] = ## in case of an error. Raises EOF if file does not contain at least `n` lines. ## Available at compile time. A line of text may be delimited by ``LF`` or ``CRLF``. ## The newline character(s) are not part of the returned strings. - var f: File + var f: File = nil if open(f, filename): try: result = newSeq[TaintedString](n) diff --git a/lib/system/repr.nim b/lib/system/repr.nim index 526839aa2..318e95ebb 100644 --- a/lib/system/repr.nim +++ b/lib/system/repr.nim @@ -102,6 +102,7 @@ proc reprSetAux(result: var string, p: pointer, typ: PNimType) = of 4: u = cast[ptr uint32](p)[] of 8: u = cast[ptr uint64](p)[] else: + u = uint64(0) var a = cast[PByteArray](p) for i in 0 .. typ.size*8-1: if (uint(a[i shr 3]) and (1'u shl (i and 7))) != 0: diff --git a/lib/system/sets.nim b/lib/system/sets.nim index 0df50957d..42c448848 100644 --- a/lib/system/sets.nim +++ b/lib/system/sets.nim @@ -31,6 +31,7 @@ proc countBits64(n: uint64): int {.compilerproc, inline.} = proc cardSet(s: NimSet, len: int): int {.compilerproc, inline.} = var i = 0 + result = 0 when defined(x86) or defined(amd64): while i < len - 8: inc(result, countBits64((cast[ptr uint64](s[i].unsafeAddr))[])) diff --git a/lib/system/strmantle.nim b/lib/system/strmantle.nim index c3ac5fc1b..cb26833ac 100644 --- a/lib/system/strmantle.nim +++ b/lib/system/strmantle.nim @@ -94,7 +94,7 @@ proc addFloat*(result: var string; x: float) = when nimvm: result.add $x else: - var buffer: array[65, char] + var buffer {.noinit.}: array[65, char] let n = writeFloatToBuffer(buffer, x) result.addCstringN(cstring(buffer[0].addr), n) @@ -131,8 +131,8 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat, i = start sign = 1.0 kdigits, fdigits = 0 - exponent: int - integer: uint64 + exponent = 0 + integer = uint64(0) fracExponent = 0 expSign = 1 firstDigit = -1 diff --git a/lib/system/widestrs.nim b/lib/system/widestrs.nim index 3048de82a..a7a514446 100644 --- a/lib/system/widestrs.nim +++ b/lib/system/widestrs.nim @@ -63,6 +63,7 @@ proc ord(arg: Utf16Char): int = int(cast[uint16](arg)) proc len*(w: WideCString): int = ## returns the length of a widestring. This traverses the whole string to ## find the binary zero end marker! + result = 0 while int16(w[result]) != 0'i16: inc result const diff --git a/tests/assert/tassert_c.nim b/tests/assert/tassert_c.nim index cbb565f84..c6a2eadb1 100644 --- a/tests/assert/tassert_c.nim +++ b/tests/assert/tassert_c.nim @@ -6,8 +6,8 @@ discard """ const expected = """ tassert_c.nim(35) tassert_c tassert_c.nim(34) foo -assertions.nim(29) failedAssertImpl -assertions.nim(22) raiseAssert +assertions.nim(30) failedAssertImpl +assertions.nim(23) raiseAssert fatal.nim(49) sysFatal""" proc tmatch(x, p: string): bool = @@ -36,4 +36,5 @@ try: except AssertionDefect: let e = getCurrentException() let trace = e.getStackTrace - if tmatch(trace, expected): echo true else: echo "wrong trace:\n" & trace + if tmatch(trace, expected): echo true + else: echo "wrong trace:\n" & trace diff --git a/tests/effects/toutparam.nim b/tests/effects/toutparam.nim new file mode 100644 index 000000000..00572aa6b --- /dev/null +++ b/tests/effects/toutparam.nim @@ -0,0 +1,28 @@ +discard """ + cmd: '''nim c --warningAsError[Uninit]:on --skipCfg --skipParentCfg $file''' + errormsg: "use explicit initialization of 'x' for clarity [Uninit]" + line: 24 + disabled: "true" +""" + +proc gah[T](x: out T) = + x = 3 + +proc main = + var a: array[2, int] + var x: int + gah(x) + a[0] = 3 + a[x] = 3 + echo x + +main() + +proc mainB = + var a: array[2, int] + var x: int + a[0] = 3 + a[x] = 3 + echo x + +mainB() diff --git a/tests/init/tuninit1.nim b/tests/init/tuninit1.nim index 67c0c4d8b..fe91733ff 100644 --- a/tests/init/tuninit1.nim +++ b/tests/init/tuninit1.nim @@ -1,5 +1,5 @@ discard """ - nimout: "Warning: 'y' might not have been initialized [Uninit]" + nimout: "Warning: use explicit initialization of 'y' for clarity [Uninit]" line:34 action: compile """ From ebd1cc0265bd2b9fa071b0649183f14def0f307b Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 23 Jun 2020 10:58:55 +0200 Subject: [PATCH 0267/3768] minor bugfixes for 'func' and .borrow --- compiler/ast.nim | 2 +- compiler/semcall.nim | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 66a1a253b..40fd40209 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1756,7 +1756,7 @@ proc isRoutine*(s: PSym): bool {.inline.} = proc isCompileTimeProc*(s: PSym): bool {.inline.} = result = s.kind == skMacro or - s.kind == skProc and sfCompileTime in s.flags + s.kind in {skProc, skFunc} and sfCompileTime in s.flags proc isRunnableExamples*(n: PNode): bool = # Templates and generics don't perform symbol lookups. diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 405de52ee..0d8f0ae8b 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -682,7 +682,8 @@ proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): PSym = x = t.baseOfDistinct call.add(newNodeIT(nkEmpty, fn.info, x)) if hasDistinct: - var resolved = semOverloadedCall(c, call, call, {fn.kind}, {}) + let filter = if fn.kind in {skProc, skFunc}: {skProc, skFunc} else: {fn.kind} + var resolved = semOverloadedCall(c, call, call, filter, {}) if resolved != nil: result = resolved[0].sym if not compareTypes(result.typ[0], fn.typ[0], dcEqIgnoreDistinct): From 79ed8e79efa49f49e1c522ea803d0317e05f0462 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Tue, 23 Jun 2020 06:21:33 -0300 Subject: [PATCH 0268/3768] Documentation GC (#14739) --- doc/gc.rst | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/doc/gc.rst b/doc/gc.rst index 39d6d0484..b118b1f8a 100644 --- a/doc/gc.rst +++ b/doc/gc.rst @@ -43,17 +43,17 @@ To choose the memory management strategy use the ``--gc:`` switch. simply never freed. You should use ``--gc:arc`` instead. -================== ======== ================= ====================== -Memory Management Heap Reference Cycles Command line switch -================== ======== ================= ====================== -RefC Local Cycle Collector ``--gc:refc`` -Mark & Sweep Local Cycle Collector ``--gc:markAndSweep`` -ARC Shared Leak ``--gc:arc`` -ORC Shared Cycle Collector ``--gc:orc`` -Boehm Shared Cycle Collector ``--gc:boehm`` -Go Shared Cycle Collector ``--gc:go`` -None Manual Manual ``--gc:none`` -================== ======== ================= ====================== +================== ======== ================= ============== =================== +Memory Management Heap Reference Cycles Stop-The-World Command line switch +================== ======== ================= ============== =================== +RefC Local Cycle Collector No ``--gc:refc`` +Mark & Sweep Local Cycle Collector No ``--gc:markAndSweep`` +ARC Shared Leak No ``--gc:arc`` +ORC Shared Cycle Collector No ``--gc:orc`` +Boehm Shared Cycle Collector Yes ``--gc:boehm`` +Go Shared Cycle Collector Yes ``--gc:go`` +None Manual Manual Manual ``--gc:none`` +================== ======== ================= ============== =================== JavaScript's garbage collector is used for the `JavaScript and NodeJS `_ compilation targets. From c3459c7b148cd00d85b01ddae74782648a5d4e37 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Tue, 23 Jun 2020 06:22:07 -0300 Subject: [PATCH 0269/3768] Deprecate oldNewlines, clean out deprecated code from oldNewlines (#14763) --- changelog.md | 1 + compiler/commands.nim | 10 ---------- compiler/lexer.nim | 7 +------ doc/advopt.txt | 1 - 4 files changed, 2 insertions(+), 17 deletions(-) diff --git a/changelog.md b/changelog.md index 9d1454dcb..d2cf0b811 100644 --- a/changelog.md +++ b/changelog.md @@ -218,5 +218,6 @@ proc mydiv(a, b): int {.raises: [].} = - `nim doc` now outputs under `$projectPath/htmldocs` when `--outdir` is unspecified (with or without `--project`); passing `--project` now automatically generates an index and enables search. See [docgen](docgen.html#introduction-quick-start) for details. +- Deprecated `--oldNewlines` and its Deprecated code cleaned out. ## Tool changes diff --git a/compiler/commands.nim b/compiler/commands.nim index c945a633a..6dccbcd00 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -582,16 +582,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; else: undefSymbol(conf.symbols, "hotcodereloading") undefSymbol(conf.symbols, "useNimRtl") - of "oldnewlines": - case arg.normalize - of "", "on": - conf.oldNewlines = true - defineSymbol(conf.symbols, "nimOldNewlines") - of "off": - conf.oldNewlines = false - undefSymbol(conf.symbols, "nimOldNewlines") - else: - localError(conf, info, errOnOrOffExpectedButXFound % arg) of "laxstrings": processOnOffSwitch(conf, {optLaxStrings}, arg, pass, info) of "nilseqs": processOnOffSwitch(conf, {optNilSeqs}, arg, pass, info) of "oldast": processOnOffSwitch(conf, {optOldAst}, arg, pass, info) diff --git a/compiler/lexer.nim b/compiler/lexer.nim index bd71a6ad5..f9679adee 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -684,12 +684,7 @@ proc getEscapedChar(L: var TLexer, tok: var TToken) = inc(L.bufpos) # skip '\' case L.buf[L.bufpos] of 'n', 'N': - if L.config.oldNewlines: - if tok.tokType == tkCharLit: - lexMessage(L, errGenerated, "\\n not allowed in character literal") - tok.literal.add(L.config.target.tnl) - else: - tok.literal.add('\L') + tok.literal.add('\L') inc(L.bufpos) of 'p', 'P': if tok.tokType == tkCharLit: diff --git a/doc/advopt.txt b/doc/advopt.txt index e522973cd..b2aa82d2b 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -101,7 +101,6 @@ Advanced options: --excessiveStackTrace:on|off stack traces use full file paths --stackTraceMsgs:on|off enable user defined stack frame msgs via `setFrameMsg` - --oldNewlines:on|off turn on|off the old behaviour of "\n" --laxStrings:on|off when turned on, accessing the zero terminator in strings is allowed; only for backwards compatibility --nilseqs:on|off allow 'nil' for strings/seqs for From 28510a9da9bf2a6b02590ba27b64e951a208b23d Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Tue, 23 Jun 2020 08:20:53 -0300 Subject: [PATCH 0270/3768] Deprecated laxStrings for mutating the internal zero terminator on strings and its Deprecated code cleaned out (#14766) Co-authored-by: Andreas Rumpf --- changelog.md | 3 +++ compiler/ccgexprs.nim | 2 +- compiler/commands.nim | 1 - compiler/options.nim | 1 - compiler/semfold.nim | 2 -- compiler/vm.nim | 2 -- doc/advopt.txt | 2 -- 7 files changed, 4 insertions(+), 9 deletions(-) diff --git a/changelog.md b/changelog.md index d2cf0b811..96841fc50 100644 --- a/changelog.md +++ b/changelog.md @@ -220,4 +220,7 @@ proc mydiv(a, b): int {.raises: [].} = See [docgen](docgen.html#introduction-quick-start) for details. - Deprecated `--oldNewlines` and its Deprecated code cleaned out. +- Deprecated `--laxStrings` for mutating the internal zero terminator on strings and its Deprecated code cleaned out. + + ## Tool changes diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index a59529021..08b9e1ff6 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -930,7 +930,7 @@ proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) = if ty.kind in {tyRef, tyPtr}: ty = skipTypes(ty.lastSon, abstractVarRange) # emit range check: if optBoundsCheck in p.options: - if ty.kind == tyString and (not defined(nimNoZeroTerminator) or optLaxStrings in p.options): + if ty.kind == tyString and not defined(nimNoZeroTerminator): linefmt(p, cpsStmts, "if ((NU)($1) > (NU)$2){ #raiseIndexError2($1,$2); $3}$n", [rdLoc(b), lenExpr(p, a), raiseInstr(p)]) diff --git a/compiler/commands.nim b/compiler/commands.nim index 6dccbcd00..fc559d68c 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -582,7 +582,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; else: undefSymbol(conf.symbols, "hotcodereloading") undefSymbol(conf.symbols, "useNimRtl") - of "laxstrings": processOnOffSwitch(conf, {optLaxStrings}, arg, pass, info) of "nilseqs": processOnOffSwitch(conf, {optNilSeqs}, arg, pass, info) of "oldast": processOnOffSwitch(conf, {optOldAst}, arg, pass, info) of "checks", "x": processOnOffSwitch(conf, ChecksOptions, arg, pass, info) diff --git a/compiler/options.nim b/compiler/options.nim index 289509c16..92c664587 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -39,7 +39,6 @@ type # please make sure we have under 32 options # evaluation optTrMacros, # en/disable pattern matching optMemTracker, - optLaxStrings, optNilSeqs, optOldAst, optSinkInference # 'sink T' inference diff --git a/compiler/semfold.nim b/compiler/semfold.nim index d24adb979..604aa9fea 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -482,8 +482,6 @@ proc foldArrayAccess(m: PSym, n: PNode; g: ModuleGraph): PNode = result = newNodeIT(nkCharLit, x.info, n.typ) if idx >= 0 and idx < x.strVal.len: result.intVal = ord(x.strVal[int(idx)]) - elif idx == x.strVal.len and optLaxStrings in g.config.options: - discard else: localError(g.config, n.info, formatErrorIndexBound(idx, x.strVal.len-1) & $n) else: discard diff --git a/compiler/vm.nim b/compiler/vm.nim index 9b76d7986..5d8d21bb9 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -684,8 +684,6 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let s = regs[rb].node.strVal if idx <% s.len: regs[ra].intVal = s[idx].ord - elif idx == s.len and optLaxStrings in c.config.options: - regs[ra].intVal = 0 else: stackTrace(c, tos, pc, formatErrorIndexBound(idx, s.len-1)) of opcWrArr: diff --git a/doc/advopt.txt b/doc/advopt.txt index b2aa82d2b..18bfde11b 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -101,8 +101,6 @@ Advanced options: --excessiveStackTrace:on|off stack traces use full file paths --stackTraceMsgs:on|off enable user defined stack frame msgs via `setFrameMsg` - --laxStrings:on|off when turned on, accessing the zero terminator in - strings is allowed; only for backwards compatibility --nilseqs:on|off allow 'nil' for strings/seqs for backwards compatibility --seqsv2:on|off use the new string/seq implementation based on From 4cb41f53f06995709d8f00587fbcaa34eb555b7b Mon Sep 17 00:00:00 2001 From: Clyybber Date: Tue, 23 Jun 2020 14:17:12 +0200 Subject: [PATCH 0271/3768] Add testcase for #14440 (#14771) --- tests/borrow/tborrow.nim | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/tests/borrow/tborrow.nim b/tests/borrow/tborrow.nim index ee0d0194d..9c403021e 100644 --- a/tests/borrow/tborrow.nim +++ b/tests/borrow/tborrow.nim @@ -1,5 +1,6 @@ discard """ - output: "4887 true" + output: '''4887 true +0.5''' """ # test the new borrow feature that works with generics: @@ -19,3 +20,16 @@ proc `$`(x: DI): string {.borrow.} proc `$`(x: DF): string {.borrow.} echo 4544.DI ++ 343.DI, " ", (4.5.DF ++ 0.5.DF).float == 5.0 + +# issue #14440 + +type Radians = distinct float64 + +func `-=`(a: var Radians, b: Radians) {.borrow.} + +var a = Radians(1.5) +let b = Radians(1.0) + +a -= b + +echo a.float64 From 16bdc547b7726c3abfcc763d2410d5cd39481a54 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 23 Jun 2020 15:17:17 +0200 Subject: [PATCH 0272/3768] fixes #14760 (#14769) --- lib/pure/memfiles.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim index dab5483e4..09db80936 100644 --- a/lib/pure/memfiles.nim +++ b/lib/pure/memfiles.nim @@ -445,7 +445,7 @@ iterator lines*(mfile: MemFile, buf: var TaintedString, delim = '\l', for ms in memSlices(mfile, delim, eat): setLen(buf.string, ms.size) if ms.size > 0: - copyMem(addr buf[0], ms.data, ms.size) + copyMem(addr string(buf)[0], ms.data, ms.size) yield buf iterator lines*(mfile: MemFile, delim = '\l', eat = '\r'): TaintedString {.inline.} = From 685d03e2e0af156021b63b33575ad6d26dc6a228 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Tue, 23 Jun 2020 15:20:31 +0200 Subject: [PATCH 0273/3768] Correct changelog (#14775) These switches were removed, not deprecated :) --- changelog.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index 96841fc50..abb761b4f 100644 --- a/changelog.md +++ b/changelog.md @@ -218,9 +218,8 @@ proc mydiv(a, b): int {.raises: [].} = - `nim doc` now outputs under `$projectPath/htmldocs` when `--outdir` is unspecified (with or without `--project`); passing `--project` now automatically generates an index and enables search. See [docgen](docgen.html#introduction-quick-start) for details. -- Deprecated `--oldNewlines` and its Deprecated code cleaned out. - -- Deprecated `--laxStrings` for mutating the internal zero terminator on strings and its Deprecated code cleaned out. +- Removed the `--oldNewlines` switch. +- Removed the `--laxStrings` switch for mutating the internal zero terminator on strings. ## Tool changes From 881d1ee57b99c42e016fe0f8d2ca64a253f75960 Mon Sep 17 00:00:00 2001 From: Miran Date: Tue, 23 Jun 2020 16:15:37 +0200 Subject: [PATCH 0274/3768] fix #14750, don't allocate too much in newWideCString (#14773) --- lib/system/widestrs.nim | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/system/widestrs.nim b/lib/system/widestrs.nim index a7a514446..aabcbdc90 100644 --- a/lib/system/widestrs.nim +++ b/lib/system/widestrs.nim @@ -56,7 +56,7 @@ else: WideCStringObj* = WideCString template createWide(a; L) = - unsafeNew(a, L * 4 + 2) + unsafeNew(a, L) proc ord(arg: Utf16Char): int = int(cast[uint16](arg)) @@ -138,8 +138,7 @@ iterator runes(s: cstring, L: int): int = yield result proc newWideCString*(source: cstring, L: int): WideCStringObj = - createWide(result, L * 4 + 2) - #result = cast[wideCString](alloc(L * 4 + 2)) + createWide(result, L * 2 + 2) var d = 0 for ch in runes(source, L): From 6bf2990f6639726afa96cabfe1a5338f491743e0 Mon Sep 17 00:00:00 2001 From: Miran Date: Tue, 23 Jun 2020 20:36:59 +0200 Subject: [PATCH 0275/3768] [backport] fix #14748, move gdb files to other section of installer.ini (#14772) --- compiler/installer.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/installer.ini b/compiler/installer.ini index 9718dbb94..c59b9a2ea 100644 --- a/compiler/installer.ini +++ b/compiler/installer.ini @@ -112,13 +112,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] From 9ebbe000a81b735f0bb8350b0897c9d1b0d1f43f Mon Sep 17 00:00:00 2001 From: treeform Date: Tue, 23 Jun 2020 11:37:29 -0700 Subject: [PATCH 0276/3768] Enable fidget in important_packages. (#14742) * Enable fidget in important_packages. Fidget now works with `nimble test` and there is a CI: https://github.com/treeform/fidget/commits/master * Add libx11-dev to make fidget compile. * try libxcursor-dev * try xorg-dev --- .github/workflows/ci_packages.yml | 2 +- testament/important_packages.nim | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci_packages.yml b/.github/workflows/ci_packages.yml index 6cf41aa8e..8a1a8a81f 100644 --- a/.github/workflows/ci_packages.yml +++ b/.github/workflows/ci_packages.yml @@ -33,7 +33,7 @@ jobs: DEBIAN_FRONTEND='noninteractive' \ sudo apt-fast install --no-install-recommends -yq \ libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev \ - valgrind libc6-dbg libblas-dev + valgrind libc6-dbg libblas-dev xorg-dev - name: 'Install dependencies (macOS)' if: runner.os == 'macOS' run: brew install boehmgc make sfml gtk+3 diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 8974b78ec..8cb277d5f 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -39,8 +39,7 @@ pkg1 "delaunay" pkg1 "docopt" pkg1 "easygl", true, "nim c -o:egl -r src/easygl.nim", "https://github.com/jackmott/easygl" pkg1 "elvis" -# pkg1 "fidget", true, "nim c -d:release -r tests/runNative.nim" - # Error: cannot open 'tests/runNative.nim' +pkg1 "fidget", true pkg1 "fragments", false, "nim c -r fragments/dsl.nim" pkg1 "gara" pkg1 "ggplotnim", true, "nim c -d:noCairo -r -d:nimWorkaround14447 -d:nimHasWorkaround14720 tests/tests.nim" From a3b95162499833c17699e0d22c44d3dd09a1ef54 Mon Sep 17 00:00:00 2001 From: ktamp Date: Tue, 23 Jun 2020 23:31:41 +0300 Subject: [PATCH 0277/3768] readLine: Unicode support for Windows console When input is read from the Windows console, input encoding is UTF16. This is translated internally to UTF8. --- lib/system/io.nim | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/lib/system/io.nim b/lib/system/io.nim index 482057214..330b67dff 100644 --- a/lib/system/io.nim +++ b/lib/system/io.nim @@ -14,6 +14,11 @@ include inclrtl import std/private/since import formatfloat +when defined(windows) and not defined(useWinAnsi): + import os + import system/widestrs + import terminal + # ----------------- IO Part ------------------------------------------------ type CFile {.importc: "FILE", header: "", @@ -352,6 +357,37 @@ proc readLine*(f: File, line: var TaintedString): bool {.tags: [ReadIOEffect], proc c_memchr(s: pointer, c: cint, n: csize_t): pointer {. importc: "memchr", header: "".} + when defined(windows) and not defined(useWinAnsi): + proc readConsole(hConsoleInput: FileHandle, lpBuffer: pointer, + nNumberOfCharsToRead: uint32, + lpNumberOfCharsRead: ptr uint32, + pInputControl: pointer): int32 {. + stdcall, dynlib: "kernel32", importc: "ReadConsoleW".} + + if f.isatty: + if c_feof(f) < 0: + return false + const numberOfCharsToRead = 2048 + var numberOfCharsRead = 0'u32 + var buffer = newWideCString("", numberOfCharsToRead) + if readConsole(getOsFileHandle(f), addr(buffer[0]), + numberOfCharsToRead, addr(numberOfCharsRead), nil) == 0: + var error = osLastError() + raiseEIO("error: " & $error & " `" & osErrorMsg(error) & "`") + # input always ends with "\r\n" + numberOfCharsRead -= 2 + # handle Ctrl+Z as EOF + for i in 0'u32.. Date: Wed, 24 Jun 2020 02:36:41 +0400 Subject: [PATCH 0278/3768] Fix Norm test path. (#14779) --- testament/important_packages.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 8cb277d5f..501d9c22d 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -91,7 +91,7 @@ pkg2 "nimwc", true, "nim c nimwc.nim" # pkg2 "nimx", true, "nim c --threads:on test/main.nim" # pkg2 "nitter", true, "nim c src/nitter.nim", "https://github.com/zedeus/nitter" # pending https://github.com/timotheecour/Nim/issues/167 or new git tag for nitter -# pkg2 "norm", true, "nim c -r tests/tsqlite.nim" +pkg2 "norm", true, "nim c -r tests/tsqliterows.nim" # pending https://github.com/timotheecour/Nim/issues/167 pkg2 "npeg", false, "nimble testarc" pkg2 "numericalnim", true, "nim c -r tests/test_integrate.nim" From 2220ff70beea0a72c61f9363cb8f0102ea317e6e Mon Sep 17 00:00:00 2001 From: narimiran Date: Wed, 24 Jun 2020 13:16:40 +0200 Subject: [PATCH 0279/3768] cleanup [ci skip] --- testament/important_packages.nim | 1 - 1 file changed, 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 501d9c22d..3d30b40e2 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -92,7 +92,6 @@ pkg2 "nimwc", true, "nim c nimwc.nim" # pkg2 "nitter", true, "nim c src/nitter.nim", "https://github.com/zedeus/nitter" # pending https://github.com/timotheecour/Nim/issues/167 or new git tag for nitter pkg2 "norm", true, "nim c -r tests/tsqliterows.nim" - # pending https://github.com/timotheecour/Nim/issues/167 pkg2 "npeg", false, "nimble testarc" pkg2 "numericalnim", true, "nim c -r tests/test_integrate.nim" pkg2 "optionsutils" From 302e3b7f447d2ab776030a720441694a374f59c6 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Wed, 24 Jun 2020 17:27:46 +0200 Subject: [PATCH 0280/3768] CI: Install the pkg we cloned (#14770) --- lib/std/private/nimbleutils.nim | 32 ------------------- testament/categories.nim | 53 ++++++++++++++++++++++---------- testament/important_packages.nim | 17 +++++----- 3 files changed, 45 insertions(+), 57 deletions(-) delete mode 100644 lib/std/private/nimbleutils.nim diff --git a/lib/std/private/nimbleutils.nim b/lib/std/private/nimbleutils.nim deleted file mode 100644 index 297fb9695..000000000 --- a/lib/std/private/nimbleutils.nim +++ /dev/null @@ -1,32 +0,0 @@ -##[ -internal API for now, API subject to change -]## - -import std/[os,osproc,sugar,strutils] - -proc actionRetry*(maxRetry: int, backoffDuration: float, action: proc(): bool): bool = - ## retry `action` up to `maxRetry` times with exponential backoff and initial - ## duraton of `backoffDuration` seconds - var t = backoffDuration - for i in 0.. Date: Wed, 24 Jun 2020 09:00:35 -0700 Subject: [PATCH 0281/3768] fix #10343 (#14789) --- tests/exception/t9657.nim | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/exception/t9657.nim b/tests/exception/t9657.nim index ea93aff59..514d29353 100644 --- a/tests/exception/t9657.nim +++ b/tests/exception/t9657.nim @@ -1,11 +1,12 @@ discard """ action: run exitcode: 1 - target: "c" + target: "c cpp" disabled: "openbsd" disabled: "netbsd" """ -# todo: remove `target: "c"` workaround once #10343 is properly fixed + close stdmsg const m = "exception!" +# see #10343 for details on this test discard writeBuffer(stdmsg, cstring(m), m.len) From a3ae52de1a1a1e0556f235eadf3279db44ca3113 Mon Sep 17 00:00:00 2001 From: Euan Date: Wed, 24 Jun 2020 17:02:18 +0100 Subject: [PATCH 0282/3768] Fix #14715 - detect tool fails on FreeBSD (#14716) --- tools/detect/detect.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/detect/detect.nim b/tools/detect/detect.nim index a10794555..fda122002 100644 --- a/tools/detect/detect.nim +++ b/tools/detect/detect.nim @@ -14,7 +14,7 @@ # compilation. import os, strutils -when defined(openbsd): +when defined(openbsd) or defined(freebsd) or defined(netbsd): const cc = "cc -o $# $#.c" cpp = "cc -E -o $#.i $#.c" @@ -98,8 +98,8 @@ proc main = f.write(nimfile % [other]) close(f) - let cCompile = when defined(openbsd): ccLinkMath else: cc - let cppCompile = when defined(openbsd): cppLinkMath else: cpp + let cCompile = defined(openbsd) or defined(freebsd) or defined(netbsd): ccLinkMath else: cc + let cppCompile = defined(openbsd) or defined(freebsd) or defined(netbsd): cppLinkMath else: cpp if not myExec(cCompile % [gen.addFileExt(ExeExt), gen]): quit(1) if not myExec(cppCompile % [pre.addFileExt(ExeExt), pre]): quit(1) when defined(windows): From 37081e29458d9fba906e162c690d9c2d56cffeca Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 24 Jun 2020 09:03:01 -0700 Subject: [PATCH 0283/3768] fromJson: support object variants (#14694) --- lib/std/jsonutils.nim | 88 ++++++++++++++++++++++++++++++------- tests/stdlib/tjsonutils.nim | 53 ++++++++++++++++++++++ 2 files changed, 126 insertions(+), 15 deletions(-) diff --git a/lib/std/jsonutils.nim b/lib/std/jsonutils.nim index be3d7e7c8..22f2a7a89 100644 --- a/lib/std/jsonutils.nim +++ b/lib/std/jsonutils.nim @@ -30,10 +30,59 @@ add a way to customize serialization, for eg: * handle cyclic references, using a cache of already visited addresses ]# +import std/macros + proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".} proc distinctBase(T: typedesc): typedesc {.magic: "TypeTrait".} template distinctBase[T](a: T): untyped = distinctBase(type(a))(a) +macro getDiscriminants(a: typedesc): seq[string] = + ## return the discriminant keys + # candidate for std/typetraits + var a = a.getTypeImpl + doAssert a.kind == nnkBracketExpr + let sym = a[1] + let t = sym.getTypeImpl + let t2 = t[2] + doAssert t2.kind == nnkRecList + result = newTree(nnkBracket) + for ti in t2: + if ti.kind == nnkRecCase: + let key = ti[0][0] + let typ = ti[0][1] + result.add newLit key.strVal + if result.len > 0: + result = quote do: + @`result` + else: + result = quote do: + seq[string].default + +macro initCaseObject(a: typedesc, fun: untyped): untyped = + ## does the minimum to construct a valid case object, only initializing + ## the discriminant fields; see also `getDiscriminants` + # maybe candidate for std/typetraits + var a = a.getTypeImpl + doAssert a.kind == nnkBracketExpr + let sym = a[1] + let t = sym.getTypeImpl + var t2: NimNode + case t.kind + of nnkObjectTy: t2 = t[2] + of nnkRefTy: t2 = t[0].getTypeImpl[2] + else: doAssert false, $t.kind # xxx `nnkPtrTy` could be handled too + doAssert t2.kind == nnkRecList + result = newTree(nnkObjConstr) + result.add sym + for ti in t2: + if ti.kind == nnkRecCase: + let key = ti[0][0] + let typ = ti[0][1] + let key2 = key.strVal + let val = quote do: + `fun`(`key2`, typedesc[`typ`]) + result.add newTree(nnkExprColonExpr, key, val) + proc checkJsonImpl(cond: bool, condStr: string, msg = "") = if not cond: # just pick 1 exception type for simplicity; other choices would be: @@ -43,6 +92,19 @@ proc checkJsonImpl(cond: bool, condStr: string, msg = "") = template checkJson(cond: untyped, msg = "") = checkJsonImpl(cond, astToStr(cond), msg) +template fromJsonFields(a, b, T, keys) = + checkJson b.kind == JObject, $(b.kind) # we could customize whether to allow JNull + var num = 0 + for key, val in fieldPairs(a): + num.inc + when key notin keys: + if b.hasKey key: + fromJson(val, b[key]) + else: + # we could customize to allow this + checkJson false, $($T, key, b) + checkJson b.len == num, $(b.len, num, $T, b) # could customize + proc fromJson*[T](a: var T, b: JsonNode) = ## inplace version of `jsonTo` #[ @@ -85,25 +147,22 @@ proc fromJson*[T](a: var T, b: JsonNode) = a.setLen b.len for i, val in b.getElems: fromJson(a[i], val) - elif T is object | tuple: - const isNamed = T is object or isNamedTuple(T) - when isNamed: - checkJson b.kind == JObject, $(b.kind) # we could customize whether to allow JNull - var num = 0 - for key, val in fieldPairs(a): - num.inc - if b.hasKey key: - fromJson(val, b[key]) - else: - # we could customize to allow this - checkJson false, $($T, key, b) - checkJson b.len == num, $(b.len, num, $T, b) # could customize + elif T is object: + template fun(key, typ): untyped = + jsonTo(b[key], typ) + a = initCaseObject(T, fun) + const keys = getDiscriminants(T) + fromJsonFields(a, b, T, keys) + elif T is tuple: + when isNamedTuple(T): + fromJsonFields(a, b, T, seq[string].default) else: checkJson b.kind == JArray, $(b.kind) # we could customize whether to allow JNull var i = 0 for val in fields(a): fromJson(val, b[i]) i.inc + checkJson b.len == i, $(b.len, i, $T, b) # could customize else: # checkJson not appropriate here static: doAssert false, "not yet implemented: " & $T @@ -120,8 +179,7 @@ proc toJson*[T](a: T): JsonNode = result = newJObject() for k, v in pairs(a): result[k] = toJson(v) elif T is object | tuple: - const isNamed = T is object or isNamedTuple(T) - when isNamed: + when T is object or isNamedTuple(T): result = newJObject() for k, v in a.fieldPairs: result[k] = toJson(v) else: diff --git a/tests/stdlib/tjsonutils.nim b/tests/stdlib/tjsonutils.nim index fca980dc9..0b2ec7179 100644 --- a/tests/stdlib/tjsonutils.nim +++ b/tests/stdlib/tjsonutils.nim @@ -9,6 +9,9 @@ proc testRoundtrip[T](t: T, expected: string) = let j = t.toJson doAssert $j == expected, $j doAssert j.jsonTo(T).toJson == j + var t2: T + t2.fromJson(j) + doAssert t2.toJson == j import tables import strtabs @@ -66,5 +69,55 @@ template fn() = doAssert b2.ord == 1 # explains the `1` testRoundtrip(a): """[1,2,3]""" + block: # case object + type Foo = object + x0: float + case t1: bool + of true: z1: int8 + of false: z2: uint16 + x1: string + testRoundtrip(Foo(t1: true, z1: 5, x1: "bar")): """{"x0":0.0,"t1":true,"z1":5,"x1":"bar"}""" + testRoundtrip(Foo(x0: 1.5, t1: false, z2: 6)): """{"x0":1.5,"t1":false,"z2":6,"x1":""}""" + type PFoo = ref Foo + testRoundtrip(PFoo(x0: 1.5, t1: false, z2: 6)): """{"x0":1.5,"t1":false,"z2":6,"x1":""}""" + + block: # ref case object + type Foo = ref object + x0: float + case t1: bool + of true: z1: int8 + of false: z2: uint16 + x1: string + testRoundtrip(Foo(t1: true, z1: 5, x1: "bar")): """{"x0":0.0,"t1":true,"z1":5,"x1":"bar"}""" + testRoundtrip(Foo(x0: 1.5, t1: false, z2: 6)): """{"x0":1.5,"t1":false,"z2":6,"x1":""}""" + + block: # generic case object + type Foo[T] = ref object + x0: float + case t1: bool + of true: z1: int8 + of false: z2: uint16 + x1: string + testRoundtrip(Foo[float](t1: true, z1: 5, x1: "bar")): """{"x0":0.0,"t1":true,"z1":5,"x1":"bar"}""" + testRoundtrip(Foo[int](x0: 1.5, t1: false, z2: 6)): """{"x0":1.5,"t1":false,"z2":6,"x1":""}""" + # sanity check: nesting inside a tuple + testRoundtrip((Foo[int](x0: 1.5, t1: false, z2: 6), "foo")): """[{"x0":1.5,"t1":false,"z2":6,"x1":""},"foo"]""" + + block: # case object: 2 discriminants, `when` branch, range discriminant + type Foo[T] = object + case t1: bool + of true: + z1: int8 + of false: + z2: uint16 + when T is float: + case t2: range[0..3] + of 0: z3: int8 + of 2,3: z4: uint16 + else: discard + testRoundtrip(Foo[float](t1: true, z1: 5, t2: 3, z4: 12)): """{"t1":true,"z1":5,"t2":3,"z4":12}""" + testRoundtrip(Foo[int](t1: false, z2: 7)): """{"t1":false,"z2":7}""" + # pending https://github.com/nim-lang/Nim/issues/14698, test with `type Foo[T] = ref object` + static: fn() fn() From 0434aef4cdc1dd018dff702a9edeb19b5073bc05 Mon Sep 17 00:00:00 2001 From: Euan Date: Wed, 24 Jun 2020 17:44:10 +0100 Subject: [PATCH 0284/3768] Patch #14716 - add missing `when` (#14792) --- tools/detect/detect.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/detect/detect.nim b/tools/detect/detect.nim index fda122002..841b3a675 100644 --- a/tools/detect/detect.nim +++ b/tools/detect/detect.nim @@ -98,8 +98,8 @@ proc main = f.write(nimfile % [other]) close(f) - let cCompile = defined(openbsd) or defined(freebsd) or defined(netbsd): ccLinkMath else: cc - let cppCompile = defined(openbsd) or defined(freebsd) or defined(netbsd): cppLinkMath else: cpp + let cCompile = when defined(openbsd) or defined(freebsd) or defined(netbsd): ccLinkMath else: cc + let cppCompile = when defined(openbsd) or defined(freebsd) or defined(netbsd): cppLinkMath else: cpp if not myExec(cCompile % [gen.addFileExt(ExeExt), gen]): quit(1) if not myExec(cppCompile % [pre.addFileExt(ExeExt), pre]): quit(1) when defined(windows): From 3e5743d6f72d9953eb97e54c11149c8b77c1062d Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 24 Jun 2020 13:31:07 -0700 Subject: [PATCH 0285/3768] add typetraits.elementType (#14780) * add typetraits.elementType * add a test for 0-sized seq --- changelog.md | 1 + lib/pure/typetraits.nim | 12 ++++++++++++ tests/metatype/ttypetraits.nim | 20 ++++++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/changelog.md b/changelog.md index abb761b4f..2c7cdeed0 100644 --- a/changelog.md +++ b/changelog.md @@ -114,6 +114,7 @@ - Add `os.normalizeExe`, eg: `koch` => `./koch`. - `macros.newLit` now preserves named vs unnamed tuples; use `-d:nimHasWorkaround14720` to keep old behavior - Add `random.gauss`, that uses the ratio of uniforms method of sampling from a Gaussian distribution. +- Add `typetraits.elementType` to get element type of an iterable. ## Language changes - In the newruntime it is now allowed to assign to the discriminator field diff --git a/lib/pure/typetraits.nim b/lib/pure/typetraits.nim index 317376405..346ac06d4 100644 --- a/lib/pure/typetraits.nim +++ b/lib/pure/typetraits.nim @@ -93,6 +93,18 @@ since (1, 1): type StaticParam*[value: static type] = object ## used to wrap a static value in `genericParams` +since (1, 3, 5): + template elementType*(a: untyped): typedesc = + ## return element type of `a`, which can be any iterable (over which you + ## can iterate) + runnableExamples: + iterator myiter(n: int): auto = + for i in 0.. Date: Wed, 24 Jun 2020 23:32:01 +0300 Subject: [PATCH 0286/3768] Reject casts to builtin typeclasses (#14788) * Closes #14231, closes #14452 * Merge test to tcast.nim --- compiler/semexprs.nim | 2 ++ tests/misc/tcast.nim | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 6f267b4eb..a9d45615e 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -191,6 +191,8 @@ proc isCastable(conf: ConfigRef; dst, src: PType): bool = return false if skipTypes(src, abstractInst-{tyTypeDesc}).kind == tyTypeDesc: return false + if skipTypes(dst, abstractInst).kind == tyBuiltInTypeClass: + return false if conf.selectedGC in {gcArc, gcOrc}: let d = skipTypes(dst, abstractInst) let s = skipTypes(src, abstractInst) diff --git a/tests/misc/tcast.nim b/tests/misc/tcast.nim index a8d1cf306..745b9e221 100644 --- a/tests/misc/tcast.nim +++ b/tests/misc/tcast.nim @@ -10,15 +10,22 @@ type MyProc3 = proc() #{.closure.} is implicit proc testProc() = echo "Hello World" +template reject(x) = doAssert(not compiles(x)) + proc callPointer(p: pointer) = # can cast to proc(){.cdecl.} let ffunc0 = cast[MyProc](p) # can cast to proc(){.nimcall.} let ffunc1 = cast[MyProc2](p) # cannot cast to proc(){.closure.} - doAssert(not compiles(cast[MyProc3](p))) + reject: cast[MyProc3](p) ffunc0() ffunc1() callPointer(cast[pointer](testProc)) + +reject: discard cast[enum](0) +proc a = echo "hi" + +reject: discard cast[ptr](a) From c6e09e335a63f2a0e3108d40e56b72a3214a045f Mon Sep 17 00:00:00 2001 From: ktamp Date: Thu, 25 Jun 2020 00:22:24 +0300 Subject: [PATCH 0287/3768] readLine: Remove recursive imports --- lib/system/io.nim | 41 +++++++++++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/lib/system/io.nim b/lib/system/io.nim index 330b67dff..b550b6b7b 100644 --- a/lib/system/io.nim +++ b/lib/system/io.nim @@ -14,11 +14,6 @@ include inclrtl import std/private/since import formatfloat -when defined(windows) and not defined(useWinAnsi): - import os - import system/widestrs - import terminal - # ----------------- IO Part ------------------------------------------------ type CFile {.importc: "FILE", header: "", @@ -362,18 +357,44 @@ proc readLine*(f: File, line: var TaintedString): bool {.tags: [ReadIOEffect], nNumberOfCharsToRead: uint32, lpNumberOfCharsRead: ptr uint32, pInputControl: pointer): int32 {. - stdcall, dynlib: "kernel32", importc: "ReadConsoleW".} + importc: "ReadConsoleW", stdcall, dynlib: "kernel32".} + proc getLastError(): int32 {. + importc: "GetLastError", stdcall, dynlib: "kernel32", sideEffect.} + + proc formatMessageW(dwFlags: int32, lpSource: pointer, + dwMessageId, dwLanguageId: int32, + lpBuffer: pointer, nSize: int32, + arguments: pointer): int32 {. + importc: "FormatMessageW", stdcall, dynlib: "kernel32".} + + proc localFree(p: pointer) {. + importc: "LocalFree", stdcall, dynlib: "kernel32".} + + proc isatty(f: File): bool = + when defined(posix): + proc isatty(fildes: FileHandle): cint {. + importc: "isatty", header: "".} + else: + proc isatty(fildes: FileHandle): cint {. + importc: "_isatty", header: "".} + result = isatty(getFileHandle(f)) != 0'i32 + + # this implies the file is open if f.isatty: - if c_feof(f) < 0: - return false const numberOfCharsToRead = 2048 var numberOfCharsRead = 0'u32 var buffer = newWideCString("", numberOfCharsToRead) if readConsole(getOsFileHandle(f), addr(buffer[0]), numberOfCharsToRead, addr(numberOfCharsRead), nil) == 0: - var error = osLastError() - raiseEIO("error: " & $error & " `" & osErrorMsg(error) & "`") + var error = getLastError() + var errorMsg: string + var msgbuf: WideCString + if formatMessageW(0x00000100 or 0x00001000 or 0x00000200, + nil, error, 0, addr(msgbuf), 0, nil) != 0'i32: + errorMsg = $msgbuf + if msgbuf != nil: localFree(cast[pointer](msgbuf)) + raiseEIO("error: " & $error & " `" & errorMsg & "`") # input always ends with "\r\n" numberOfCharsRead -= 2 # handle Ctrl+Z as EOF From 03271593446f72cb99ebe8b68121999ba7ea8021 Mon Sep 17 00:00:00 2001 From: Danil Yarantsev Date: Thu, 25 Jun 2020 06:38:08 +0300 Subject: [PATCH 0288/3768] Add test-cases to some fixed issues to close them (#14795) --- tests/async/tasync_misc.nim | 7 +++++++ tests/generics/tgenerics_issues.nim | 18 ++++++++++++++++++ tests/metatype/ttypedesc1.nim | 6 ++++++ tests/misc/tints.nim | 4 ++++ 4 files changed, 35 insertions(+) diff --git a/tests/async/tasync_misc.nim b/tests/async/tasync_misc.nim index 29e6faa72..ec1418e8c 100644 --- a/tests/async/tasync_misc.nim +++ b/tests/async/tasync_misc.nim @@ -74,3 +74,10 @@ block: # issue #13899 result = true doAssert waitFor testFooSucceed() doAssert waitFor testFooFails() + +block: # issue #9313 + doAssert compiles(block: + proc a() {.async.} = + echo "Hi" + quit(0) + ) diff --git a/tests/generics/tgenerics_issues.nim b/tests/generics/tgenerics_issues.nim index af71a8938..54360f178 100644 --- a/tests/generics/tgenerics_issues.nim +++ b/tests/generics/tgenerics_issues.nim @@ -762,3 +762,21 @@ block t3717: var f: Foo[Foo[int]] discard foo(f) + + + +block: # issue #9458 + type + Option[T] = object + val: T + has: bool + + Bar = object + + proc none(T: typedesc): Option[T] = + discard + + proc foo[T](self: T; x: Option[Bar] = Bar.none) = + discard + + foo(1) diff --git a/tests/metatype/ttypedesc1.nim b/tests/metatype/ttypedesc1.nim index 837c8eccc..549dbc62a 100644 --- a/tests/metatype/ttypedesc1.nim +++ b/tests/metatype/ttypedesc1.nim @@ -60,3 +60,9 @@ reject: reject: foo seq[int], ss +# issue #12398 +reject: + let xs = [int, float, string] + +reject: + let data = @[int, typedesc] diff --git a/tests/misc/tints.nim b/tests/misc/tints.nim index fa3eb42cb..d24cbd4ac 100644 --- a/tests/misc/tints.nim +++ b/tests/misc/tints.nim @@ -77,5 +77,9 @@ block: # Casts to uint testCast(-5'i32, uint8, 251'u8) +# issue #7174 +let c = 1'u +let val = c > 0 +doAssert val echo("Success") #OUT Success From 32f335f594d7007fabe4232a63f51137d7fb6cc1 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 24 Jun 2020 23:00:19 -0700 Subject: [PATCH 0289/3768] fix #14802 (#14803) --- compiler/ast.nim | 3 ++- tests/statictypes/tstatictypes.nim | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 40fd40209..b57ee542d 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1841,7 +1841,8 @@ proc isInfixAs*(n: PNode): bool = return n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.s == "as" proc findUnresolvedStatic*(n: PNode): PNode = - if n.kind == nkSym and n.typ.kind == tyStatic and n.typ.n == nil: + # n.typ == nil: see issue #14802 + if n.kind == nkSym and n.typ != nil and n.typ.kind == tyStatic and n.typ.n == nil: return n for son in n: diff --git a/tests/statictypes/tstatictypes.nim b/tests/statictypes/tstatictypes.nim index 1aab6e124..2a8d09dbf 100644 --- a/tests/statictypes/tstatictypes.nim +++ b/tests/statictypes/tstatictypes.nim @@ -216,3 +216,10 @@ block: # #12713 proc test(c: static string) = discard #Remove this and it compiles proc test(c: Cell) = discard test Cell() + +block: # issue #14802 + template fn(s: typed): untyped = + proc bar() = discard + 12 + const myConst = static(fn(1)) + doAssert myConst == 12 From 0c56eeda0e3a9a2fd5bb3d9b53f593d6d41fb5d9 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 25 Jun 2020 01:22:08 -0700 Subject: [PATCH 0290/3768] expr => untyped; stmt => typed (#14804) * expr => untyped; stmt => typed * changelog + comment --- changelog.md | 1 + compiler/vmdeps.nim | 4 ++-- tests/metatype/ttypetraits.nim | 6 ++++++ 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index 2c7cdeed0..07d9b6112 100644 --- a/changelog.md +++ b/changelog.md @@ -221,6 +221,7 @@ proc mydiv(a, b): int {.raises: [].} = See [docgen](docgen.html#introduction-quick-start) for details. - Removed the `--oldNewlines` switch. - Removed the `--laxStrings` switch for mutating the internal zero terminator on strings. +- `$getType(untyped)` is now "untyped" instead of "expr", `$getType(typed)` is now "typed" instead of "stmt" ## Tool changes diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim index 6f90b7918..4ba7b23bf 100644 --- a/compiler/vmdeps.nim +++ b/compiler/vmdeps.nim @@ -93,8 +93,8 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo; of tyBool: result = atomicType("bool", mBool) of tyChar: result = atomicType("char", mChar) of tyNil: result = atomicType("nil", mNil) - of tyUntyped: result = atomicType("expr", mExpr) - of tyTyped: result = atomicType("stmt", mStmt) + of tyUntyped: result = atomicType("untyped", mExpr) + of tyTyped: result = atomicType("typed", mStmt) of tyVoid: result = atomicType("void", mVoid) of tyEmpty: result = atomicType("empty", mNone) of tyUncheckedArray: diff --git a/tests/metatype/ttypetraits.nim b/tests/metatype/ttypetraits.nim index 10aa7783d..d0480415e 100644 --- a/tests/metatype/ttypetraits.nim +++ b/tests/metatype/ttypetraits.nim @@ -49,6 +49,12 @@ block: # typeToString doAssert (tuple[a: C2b[MyInt, C4[cstring]], b: cint, c: float]).name3 == "tuple[a: C[MyInt{int}, C4[cstring]], b: cint{int32}, c: float]" + macro fn(): string = + # not 100% sure whether this should even compile; if some PR breaks this test, + # this could be revisited, maybe. + newLit $($getType(untyped), $getType(typed)) + doAssert fn() == """("untyped", "typed")""" + block distinctBase: block: type From 1a0725f022299ab345a34250d37d25498664184a Mon Sep 17 00:00:00 2001 From: alaviss Date: Thu, 25 Jun 2020 08:28:57 +0000 Subject: [PATCH 0291/3768] koch: add --localdocs to allow building only local docs (#14783) * koch: add --localdocs to allow building only local docs This flag also make koch doc use the passed arguments when building the offline docs. This is useful when generating nightlies as we would want to use --doccmd:skip and also skipping a pass of docgen speed things up drastically (for non-native targets). This flag superseded the undocumented --docslocal. * kochdocs: filter google analytics code from the arg list instead This commit introduce a small PEG expression to filter out the google analytics code before building local docs when --localdocs is not specified. This lets us keep any arguments unrelated to google analytics when building local docs, useful for use with --doccmd:skip --- koch.nim | 20 +++++++++++--------- tools/kochdocs.nim | 23 ++++++++++++++++++----- 2 files changed, 29 insertions(+), 14 deletions(-) diff --git a/koch.nim b/koch.nim index c1d8df8da..bbcb0e349 100644 --- a/koch.nim +++ b/koch.nim @@ -53,6 +53,8 @@ Options: --latest bundle the installers with a bleeding edge Nimble --stable bundle the installers with a stable Nimble (default) --nim:path use specified path for nim binary + --localdocs[:path] only build local documentations. If a path is not + specified (or empty), the default is used. Possible Commands: boot [options] bootstraps with given command line options distrohelper [bindir] helper for distro packagers @@ -642,7 +644,10 @@ proc showHelp() = when isMainModule: var op = initOptParser() - var latest = false + var + latest = false + localDocsOnly = false + localDocsOut = "" while true: op.next() case op.kind @@ -651,19 +656,16 @@ when isMainModule: of "latest": latest = true of "stable": latest = false of "nim": nimExe = op.val.absolutePath # absolute so still works with changeDir - of "docslocal": - # undocumented for now, allows to rebuild local docs in < 40s as follows: - # `./koch --nim:$nimb --docslocal:htmldocs2 --doccmd:skip --warnings:off --hints:off` - # whereas `./koch docs` takes 190s; useful for development. - doAssert op.val.len > 0 - buildDocsDir(op.cmdLineRest, op.val) - break + of "localdocs": + localDocsOnly = true + if op.val.len > 0: + localDocsOut = op.val.absolutePath else: showHelp() of cmdArgument: case normalize(op.key) of "boot": boot(op.cmdLineRest) of "clean": clean(op.cmdLineRest) - of "doc", "docs": buildDocs(op.cmdLineRest) + of "doc", "docs": buildDocs(op.cmdLineRest, localDocsOnly, localDocsOut) of "doc0", "docs0": # undocumented command for Araq-the-merciful: buildDocs(op.cmdLineRest & gaCode) diff --git a/tools/kochdocs.nim b/tools/kochdocs.nim index eacfb45ca..d0f8077ed 100644 --- a/tools/kochdocs.nim +++ b/tools/kochdocs.nim @@ -1,6 +1,6 @@ ## Part of 'koch' responsible for the documentation generation. -import os, strutils, osproc, sets, pathnorm +import os, strutils, osproc, sets, pathnorm, pegs from std/private/globs import nativeToUnixPath, walkDirRecFilter, PathEntry import "../compiler/nimpaths" @@ -182,7 +182,7 @@ lib/system/iterators.nim lib/system/dollars.nim lib/system/widestrs.nim """.splitWhitespace() - + proc follow(a: PathEntry): bool = a.path.lastPathPart notin ["nimcache", "htmldocs", "includes", "deprecated", "genode"] for entry in walkDirRecFilter("lib", follow = follow): @@ -318,6 +318,19 @@ proc buildDocsDir*(args: string, dir: string) = buildDocPackages(args, dir) copyFile(docHackJsSource, dir / docHackJsSource.lastPathPart) -proc buildDocs*(args: string) = - buildDocsDir(args, webUploadOutput / NimVersion) - buildDocsDir("", docHtmlOutput) # no `args` to avoid offline docs containing the 'gaCode'! +proc buildDocs*(args: string, localOnly = false, localOutDir = "") = + let localOutDir = + if localOutDir.len == 0: + docHtmlOutput + else: + localOutDir + + var args = args + + if not localOnly: + buildDocsDir(args, webUploadOutput / NimVersion) + + let gaFilter = peg"@( y'--doc.googleAnalytics:' @(\s / $) )" + args = args.replace(gaFilter) + + buildDocsDir(args, localOutDir) From 0a9a63923060cab29f0d0d9f8dec7fd9a37222e6 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Thu, 25 Jun 2020 15:12:30 +0200 Subject: [PATCH 0292/3768] Add testcase for #4796 (#14784) * Add testcase for #4796 * Fix test --- tests/modules/seq.nim | 5 +++++ tests/modules/tseq.nim | 8 ++++++++ 2 files changed, 13 insertions(+) create mode 100644 tests/modules/seq.nim create mode 100644 tests/modules/tseq.nim diff --git a/tests/modules/seq.nim b/tests/modules/seq.nim new file mode 100644 index 000000000..176e44025 --- /dev/null +++ b/tests/modules/seq.nim @@ -0,0 +1,5 @@ +var seq: seq[int] +var b: seq[float] + +echo seq +echo b diff --git a/tests/modules/tseq.nim b/tests/modules/tseq.nim new file mode 100644 index 000000000..22ee48f42 --- /dev/null +++ b/tests/modules/tseq.nim @@ -0,0 +1,8 @@ +discard """ + joinable: false + output: '''@[] +@[] +''' +""" + +import seq From 13193b2964d76e4656a088782b10365c0ffeceed Mon Sep 17 00:00:00 2001 From: narimiran Date: Thu, 25 Jun 2020 16:55:11 +0200 Subject: [PATCH 0293/3768] turn 'runnableExample' into 'code-block' to make nightlies green This example doesn't work on our nightlies CI. Other procs in this module already use 'code-block'. --- lib/pure/osproc.nim | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 3e3391d52..8be914992 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -1446,6 +1446,7 @@ proc execCmdEx*(command: string, options: set[ProcessOption] = { ## A convenience proc that runs the `command`, and returns its `output` and ## `exitCode`. `env` and `workingDir` params behave as for `startProcess`. ## If `input.len > 0`, it is passed as stdin. + ## ## Note: this could block if `input.len` is greater than your OS's maximum ## pipe buffer size. ## @@ -1456,15 +1457,17 @@ proc execCmdEx*(command: string, options: set[ProcessOption] = { ## * `execProcess proc ## <#execProcess,string,string,openArray[string],StringTableRef,set[ProcessOption]>`_ ## - runnableExamples: - var result = execCmdEx("nim r --hints:off -", options = {}, input = "echo 3*4") - import strutils, strtabs - stripLineEnd(result[0]) ## portable way to remove trailing newline, if any - doAssert result == ("12", 0) - doAssert execCmdEx("ls --nonexistant").exitCode != 0 - when defined(posix): - assert execCmdEx("echo $FO", env = newStringTable({"FO": "B"})) == ("B\n", 0) - assert execCmdEx("echo $PWD", workingDir = "/") == ("/\n", 0) + ## Example: + ## + ## .. code-block:: Nim + ## var result = execCmdEx("nim r --hints:off -", options = {}, input = "echo 3*4") + ## import strutils, strtabs + ## stripLineEnd(result[0]) ## portable way to remove trailing newline, if any + ## doAssert result == ("12", 0) + ## doAssert execCmdEx("ls --nonexistant").exitCode != 0 + ## when defined(posix): + ## assert execCmdEx("echo $FO", env = newStringTable({"FO": "B"})) == ("B\n", 0) + ## assert execCmdEx("echo $PWD", workingDir = "/") == ("/\n", 0) when (NimMajor, NimMinor, NimPatch) < (1, 3, 5): doAssert input.len == 0 From 41c97e4b70d8d3b67f9080939790c6b9a64a7e74 Mon Sep 17 00:00:00 2001 From: ktamp Date: Fri, 26 Jun 2020 00:31:06 +0300 Subject: [PATCH 0294/3768] readLine: Fix issues with --gc:arc **--gc:arc** defines **nimv2**. This changes the definition of **WideCStringObj**. Also an empty string should be returned in case of EOF. --- lib/system/io.nim | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/system/io.nim b/lib/system/io.nim index b550b6b7b..9f12a1767 100644 --- a/lib/system/io.nim +++ b/lib/system/io.nim @@ -354,8 +354,8 @@ proc readLine*(f: File, line: var TaintedString): bool {.tags: [ReadIOEffect], when defined(windows) and not defined(useWinAnsi): proc readConsole(hConsoleInput: FileHandle, lpBuffer: pointer, - nNumberOfCharsToRead: uint32, - lpNumberOfCharsRead: ptr uint32, + nNumberOfCharsToRead: int32, + lpNumberOfCharsRead: ptr int32, pInputControl: pointer): int32 {. importc: "ReadConsoleW", stdcall, dynlib: "kernel32".} @@ -383,7 +383,7 @@ proc readLine*(f: File, line: var TaintedString): bool {.tags: [ReadIOEffect], # this implies the file is open if f.isatty: const numberOfCharsToRead = 2048 - var numberOfCharsRead = 0'u32 + var numberOfCharsRead = 0'i32 var buffer = newWideCString("", numberOfCharsToRead) if readConsole(getOsFileHandle(f), addr(buffer[0]), numberOfCharsToRead, addr(numberOfCharsRead), nil) == 0: @@ -393,20 +393,25 @@ proc readLine*(f: File, line: var TaintedString): bool {.tags: [ReadIOEffect], if formatMessageW(0x00000100 or 0x00001000 or 0x00000200, nil, error, 0, addr(msgbuf), 0, nil) != 0'i32: errorMsg = $msgbuf - if msgbuf != nil: localFree(cast[pointer](msgbuf)) + if msgbuf != nil: + localFree(cast[pointer](msgbuf)) raiseEIO("error: " & $error & " `" & errorMsg & "`") # input always ends with "\r\n" numberOfCharsRead -= 2 # handle Ctrl+Z as EOF - for i in 0'u32.. Date: Thu, 25 Jun 2020 14:45:37 -0700 Subject: [PATCH 0295/3768] Add hasAttribute method to dom.nim. (#14814) See: https://developer.mozilla.org/en-US/docs/Web/API/Element/hasAttribute --- lib/js/dom.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/js/dom.nim b/lib/js/dom.nim index 48d32fc18..c8e10d0f8 100644 --- a/lib/js/dom.nim +++ b/lib/js/dom.nim @@ -1190,6 +1190,7 @@ proc deleteData*(n: Node, start, len: int) proc focus*(e: Node) proc getAttribute*(n: Node, attr: cstring): cstring proc getAttributeNode*(n: Node, attr: cstring): Node +proc hasAttribute*(n: Node; attr: cstring): bool proc hasChildNodes*(n: Node): bool proc insertData*(n: Node, position: int, data: cstring) proc removeAttribute*(n: Node, attr: cstring) From 49fcda33306227466161b31e0f8e0a21cbecdfaf Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 25 Jun 2020 19:10:22 -0700 Subject: [PATCH 0296/3768] followup after https://github.com/Vindaar/ggplotnim/pull/74 wrt #14447 lent iterators (#14817) * followup after https://github.com/Vindaar/ggplotnim/pull/74 wrt #14447 lent iterators * ggplotnim: remove -d:nimHasWorkaround14720 --- testament/important_packages.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index bc7f77cc4..352ce2cd7 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -42,7 +42,7 @@ pkg1 "elvis" pkg1 "fidget", true pkg1 "fragments", false, "nim c -r fragments/dsl.nim" pkg1 "gara" -pkg1 "ggplotnim", true, "nim c -d:noCairo -r -d:nimWorkaround14447 -d:nimHasWorkaround14720 tests/tests.nim" +pkg1 "ggplotnim", true, "nim c -d:noCairo -r tests/tests.nim" # pkg1 "gittyup", true, "nimble test", "https://github.com/disruptek/gittyup" pkg1 "glob" pkg1 "gnuplot" From 4dfb062f4dda5e8491867dc817c4b499f91ee5f7 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 26 Jun 2020 01:40:34 -0700 Subject: [PATCH 0297/3768] update contributing.rst and docstyle.rst: refer to a bug via `bug #1234` + other guidelines (#14796) * add a testing convention in nep1 * misc updates to contributing.rst and docstyle.rst * address comments --- doc/contributing.rst | 64 +++++++++++++++++++++++++++++--------------- doc/docstyle.rst | 9 ++++++- 2 files changed, 50 insertions(+), 23 deletions(-) diff --git a/doc/contributing.rst b/doc/contributing.rst index 39a44fb2b..a4baaef05 100644 --- a/doc/contributing.rst +++ b/doc/contributing.rst @@ -18,15 +18,23 @@ See `codeowners `_ for more details. Writing tests ============= -There are 3 types of tests: +There are 4 types of tests: 1. ``runnableExamples`` documentation comment tests, ran by ``nim doc mymod.nim`` These end up in documentation and ensure documentation stays in sync with code. -2. tests in ``when isMainModule:`` block, ran by ``nim c mymod.nim`` - ``nimble test`` also typially runs these in external nimble packages. +2. separate test files, e.g.: ``tests/stdlib/tos.nim``. + In nim repo, `testament` (see below) runs all `$nim/tests/*/t*.nim` test files; + for nimble packages, see https://github.com/nim-lang/nimble#tests. -3. testament tests, e.g.: ``tests/stdlib/tos.nim`` (only used for Nim repo). +3. (deprecated) tests in ``when isMainModule:`` block, ran by ``nim r mymod.nim``. + ``nimble test`` can run those in nimble packages when specified in a + `task "test"`. + +4. (not preferred) `.. code-block:: nim` RST snippets; these should only be used in rst sources, + in nim sources `runnableExamples` should now always be preferred to those for + several reasons (cleaner syntax, syntax highlights, batched testing, and + `rdoccmd` allows customization). Not all the tests follow the convention here, feel free to change the ones that don't. Always leave the code cleaner than you found it. @@ -34,31 +42,43 @@ that don't. Always leave the code cleaner than you found it. Stdlib ------ -If you change the stdlib (anything under ``lib/``, e.g. ``lib/pure/os.nim``), -put a test in the file you changed. Add the tests under a ``when isMainModule:`` -condition so they only get executed when the tester is building the -file. Each test should be in a separate ``block:`` statement, such that +Each stdlib module (anything under ``lib/``, e.g. ``lib/pure/os.nim``) should +preferably have a corresponding separate test file, eg `tests/stdlib/tos.nim`. +The old convention was to add a ``when isMainModule:`` block in the source file, +which only gets executed when the tester is building the file. + +Each test should be in a separate ``block:`` statement, such that each has its own scope. Use boolean conditions and ``doAssert`` for the -testing by itself, don't rely on echo statements or similar. +testing by itself, don't rely on echo statements or similar; in particular avoid +things like `echo "done"`. Sample test: .. code-block:: nim - when isMainModule: - block: # newSeqWith tests - var seq2D = newSeqWith(4, newSeq[bool](2)) - seq2D[0][0] = true - seq2D[1][0] = true - seq2D[0][1] = true - doAssert seq2D == @[@[true, true], @[true, false], - @[false, false], @[false, false]] - # doAssert with `not` can now be done as follows: - doAssert not (1 == 2) + block: # bug #1234 + static: doAssert 1+1 == 2 -Newer tests tend to be run via ``testament`` rather than via ``when isMainModule:``, -e.g. ``tests/stdlib/tos.nim``; this allows additional features such as custom -compiler flags; for more details see below. + block: # bug #1235 + var seq2D = newSeqWith(4, newSeq[bool](2)) + seq2D[0][0] = true + seq2D[1][0] = true + seq2D[0][1] = true + doAssert seq2D == @[@[true, true], @[true, false], + @[false, false], @[false, false]] + # doAssert with `not` can now be done as follows: + doAssert not (1 == 2) + +Always refer to a github issue using the following exact syntax: `bug #1234` as shown +above, so that it's consistent and easier to search or for tooling. Some browser +extensions (eg https://github.com/sindresorhus/refined-github) will even turn those +in clickable links when it works. + +Rationale for using a separate test file instead of `when isMainModule:` block: +* allows custom compiler flags or testing options (see details below) +* faster CI since they can be joined in `megatest` (combined into a single test) +* avoids making the parser do un-necessary work when a source file is merely imported +* avoids mixing source and test code when reporting line of code statistics or code coverage Compiler -------- diff --git a/doc/docstyle.rst b/doc/docstyle.rst index f0785bc0a..70ded0689 100644 --- a/doc/docstyle.rst +++ b/doc/docstyle.rst @@ -4,10 +4,17 @@ Documentation Style General Guidelines ------------------ +* See also `nep1`_ which should probably be merged here. * Authors should document anything that is exported; documentation for private procs can be useful too (visible via ``nim doc --docInternal foo.nim``). -* Within documentation, a period (`.`) should follow each sentence (or sentence fragment) in a comment block. The documentation may be limited to one sentence fragment, but if multiple sentences are within the documentation, each sentence after the first should be complete and in present tense. +* Within documentation, a period (`.`) should follow each sentence (or sentence fragment) in a comment block. + The documentation may be limited to one sentence fragment, but if multiple sentences are within the documentation, + each sentence after the first should be complete and in present tense. * Documentation is parsed as a custom ReStructuredText (RST) with partial markdown support. +* In nim sources, prefer single backticks to double backticks since it's simpler + and `nim doc` supports it (even in rst files with `nim rst2html`). +* In nim sources, for links, prefer `[link text](link.html)` to ``` `link text`_ ``` + since the syntax is simpler and markdown is more common (likewise, `nim rst2html` also supports it in rst files). .. code-block:: nim From f926dc53f127cb7fc26daf35e0de38612a512552 Mon Sep 17 00:00:00 2001 From: treeform Date: Fri, 26 Jun 2020 11:15:21 -0700 Subject: [PATCH 0298/3768] Add all missing css properties to dom.nim. (#14825) * Add all missing css properties * remove extra space --- lib/js/dom.nim | 274 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 270 insertions(+), 4 deletions(-) diff --git a/lib/js/dom.nim b/lib/js/dom.nim index c8e10d0f8..262c0daad 100644 --- a/lib/js/dom.nim +++ b/lib/js/dom.nim @@ -371,19 +371,73 @@ type Style* = ref StyleObj StyleObj {.importc.} = object of RootObj + alignContent*: cstring + alignItems*: cstring + alignSelf*: cstring + all*: cstring + animation*: cstring + animationDelay*: cstring + animationDirection*: cstring + animationDuration*: cstring + animationFillMode*: cstring + animationIterationCount*: cstring + animationName*: cstring + animationPlayState*: cstring + animationTimingFunction*: cstring + backdropFilter*: cstring + backfaceVisibility*: cstring background*: cstring backgroundAttachment*: cstring + backgroundBlendMode*: cstring + backgroundClip*: cstring backgroundColor*: cstring backgroundImage*: cstring + backgroundOrigin*: cstring backgroundPosition*: cstring backgroundRepeat*: cstring backgroundSize*: cstring + blockSize*: cstring border*: cstring + borderBlock*: cstring + borderBlockColor*: cstring + borderBlockEnd*: cstring + borderBlockEndColor*: cstring + borderBlockEndStyle*: cstring + borderBlockEndWidth*: cstring + borderBlockStart*: cstring + borderBlockStartColor*: cstring + borderBlockStartStyle*: cstring + borderBlockStartWidth*: cstring + borderBlockStyle*: cstring + borderBlockWidth*: cstring borderBottom*: cstring borderBottomColor*: cstring + borderBottomLeftRadius*: cstring + borderBottomRightRadius*: cstring borderBottomStyle*: cstring borderBottomWidth*: cstring + borderCollapse*: cstring borderColor*: cstring + borderEndEndRadius*: cstring + borderEndStartRadius*: cstring + borderImage*: cstring + borderImageOutset*: cstring + borderImageRepeat*: cstring + borderImageSlice*: cstring + borderImageSource*: cstring + borderImageWidth*: cstring + borderInline*: cstring + borderInlineColor*: cstring + borderInlineEnd*: cstring + borderInlineEndColor*: cstring + borderInlineEndStyle*: cstring + borderInlineEndWidth*: cstring + borderInlineStart*: cstring + borderInlineStartColor*: cstring + borderInlineStartStyle*: cstring + borderInlineStartWidth*: cstring + borderInlineStyle*: cstring + borderInlineWidth*: cstring borderLeft*: cstring borderLeftColor*: cstring borderLeftStyle*: cstring @@ -393,86 +447,298 @@ type borderRightColor*: cstring borderRightStyle*: cstring borderRightWidth*: cstring + borderSpacing*: cstring + borderStartEndRadius*: cstring + borderStartStartRadius*: cstring borderStyle*: cstring borderTop*: cstring borderTopColor*: cstring + borderTopLeftRadius*: cstring + borderTopRightRadius*: cstring borderTopStyle*: cstring borderTopWidth*: cstring borderWidth*: cstring bottom*: cstring - boxSizing*: cstring + boxDecorationBreak*: cstring boxShadow*: cstring + boxSizing*: cstring + breakAfter*: cstring + breakBefore*: cstring + breakInside*: cstring captionSide*: cstring + caretColor*: cstring clear*: cstring clip*: cstring clipPath*: cstring color*: cstring + colorAdjust*: cstring + columnCount*: cstring + columnFill*: cstring + columnGap*: cstring + columnRule*: cstring + columnRuleColor*: cstring + columnRuleStyle*: cstring + columnRuleWidth*: cstring + columnSpan*: cstring + columnWidth*: cstring + columns*: cstring + contain*: cstring + content*: cstring + counterIncrement*: cstring + counterReset*: cstring + counterSet*: cstring cursor*: cstring direction*: cstring display*: cstring emptyCells*: cstring + filter*: cstring + flex*: cstring + flexBasis*: cstring + flexDirection*: cstring + flexFlow*: cstring + flexGrow*: cstring + flexShrink*: cstring + flexWrap*: cstring cssFloat*: cstring font*: cstring fontFamily*: cstring + fontFeatureSettings*: cstring + fontKerning*: cstring + fontLanguageOverride*: cstring + fontOpticalSizing*: cstring fontSize*: cstring + fontSizeAdjust*: cstring fontStretch*: cstring fontStyle*: cstring + fontSynthesis*: cstring fontVariant*: cstring + fontVariantAlternates*: cstring + fontVariantCaps*: cstring + fontVariantEastAsian*: cstring + fontVariantLigatures*: cstring + fontVariantNumeric*: cstring + fontVariantPosition*: cstring + fontVariationSettings*: cstring fontWeight*: cstring + gap*: cstring + grid*: cstring + gridArea*: cstring + gridAutoColumns*: cstring + gridAutoFlow*: cstring + gridAutoRows*: cstring + gridColumn*: cstring + gridColumnEnd*: cstring + gridColumnStart*: cstring + gridRow*: cstring + gridRowEnd*: cstring + gridRowStart*: cstring + gridTemplate*: cstring + gridTemplateAreas*: cstring + gridTemplateColumns*: cstring + gridTemplateRows*: cstring + hangingPunctuation*: cstring height*: cstring + hyphens*: cstring + imageOrientation*: cstring + imageRendering*: cstring + inlineSize*: cstring + inset*: cstring + insetBlock*: cstring + insetBlockEnd*: cstring + insetBlockStart*: cstring + insetInline*: cstring + insetInlineEnd*: cstring + insetInlineStart*: cstring + isolation*: cstring + justifyContent*: cstring + justifyItems*: cstring + justifySelf*: cstring left*: cstring letterSpacing*: cstring + lineBreak*: cstring lineHeight*: cstring listStyle*: cstring listStyleImage*: cstring listStylePosition*: cstring listStyleType*: cstring margin*: cstring + marginBlock*: cstring + marginBlockEnd*: cstring + marginBlockStart*: cstring marginBottom*: cstring + marginInline*: cstring + marginInlineEnd*: cstring + marginInlineStart*: cstring marginLeft*: cstring marginRight*: cstring marginTop*: cstring + mask*: cstring + maskBorder*: cstring + maskBorderMode*: cstring + maskBorderOutset*: cstring + maskBorderRepeat*: cstring + maskBorderSlice*: cstring + maskBorderSource*: cstring + maskBorderWidth*: cstring + maskClip*: cstring + maskComposite*: cstring + maskImage*: cstring + maskMode*: cstring + maskOrigin*: cstring + maskPosition*: cstring + maskRepeat*: cstring + maskSize*: cstring + maskType*: cstring + maxBlockSize*: cstring maxHeight*: cstring + maxInlineSize*: cstring maxWidth*: cstring + minBlockSize*: cstring minHeight*: cstring + minInlineSize*: cstring minWidth*: cstring + mixBlendMode*: cstring + objectFit*: cstring + objectPosition*: cstring + offset*: cstring + offsetAnchor*: cstring + offsetDistance*: cstring + offsetPath*: cstring + offsetRotate*: cstring opacity*: cstring + order*: cstring + orphans*: cstring outline*: cstring + outlineColor*: cstring + outlineOffset*: cstring + outlineStyle*: cstring + outlineWidth*: cstring overflow*: cstring + overflowAnchor*: cstring + overflowBlock*: cstring + overflowInline*: cstring + overflowWrap*: cstring overflowX*: cstring overflowY*: cstring + overscrollBehavior*: cstring + overscrollBehaviorBlock*: cstring + overscrollBehaviorInline*: cstring + overscrollBehaviorX*: cstring + overscrollBehaviorY*: cstring padding*: cstring + paddingBlock*: cstring + paddingBlockEnd*: cstring + paddingBlockStart*: cstring paddingBottom*: cstring + paddingInline*: cstring + paddingInlineEnd*: cstring + paddingInlineStart*: cstring paddingLeft*: cstring paddingRight*: cstring paddingTop*: cstring pageBreakAfter*: cstring pageBreakBefore*: cstring + pageBreakInside*: cstring + paintOrder*: cstring + perspective*: cstring + perspectiveOrigin*: cstring + placeContent*: cstring + placeItems*: cstring + placeSelf*: cstring pointerEvents*: cstring position*: cstring + quotes*: cstring resize*: cstring right*: cstring + rotate*: cstring + rowGap*: cstring + scale*: cstring + scrollBehavior*: cstring + scrollMargin*: cstring + scrollMarginBlock*: cstring + scrollMarginBlockEnd*: cstring + scrollMarginBlockStart*: cstring + scrollMarginBottom*: cstring + scrollMarginInline*: cstring + scrollMarginInlineEnd*: cstring + scrollMarginInlineStart*: cstring + scrollMarginLeft*: cstring + scrollMarginRight*: cstring + scrollMarginTop*: cstring + scrollPadding*: cstring + scrollPaddingBlock*: cstring + scrollPaddingBlockEnd*: cstring + scrollPaddingBlockStart*: cstring + scrollPaddingBottom*: cstring + scrollPaddingInline*: cstring + scrollPaddingInlineEnd*: cstring + scrollPaddingInlineStart*: cstring + scrollPaddingLeft*: cstring + scrollPaddingRight*: cstring + scrollPaddingTop*: cstring + scrollSnapAlign*: cstring + scrollSnapStop*: cstring + scrollSnapType*: cstring scrollbar3dLightColor*: cstring scrollbarArrowColor*: cstring scrollbarBaseColor*: cstring + scrollbarColor*: cstring scrollbarDarkshadowColor*: cstring scrollbarFaceColor*: cstring scrollbarHighlightColor*: cstring scrollbarShadowColor*: cstring scrollbarTrackColor*: cstring + scrollbarWidth*: cstring + shapeImageThreshold*: cstring + shapeMargin*: cstring + shapeOutside*: cstring + tabSize*: cstring tableLayout*: cstring textAlign*: cstring + textAlignLast*: cstring + textCombineUpright*: cstring textDecoration*: cstring + textDecorationColor*: cstring + textDecorationLine*: cstring + textDecorationSkipInk*: cstring + textDecorationStyle*: cstring + textDecorationThickness*: cstring + textEmphasis*: cstring + textEmphasisColor*: cstring + textEmphasisPosition*: cstring + textEmphasisStyle*: cstring textIndent*: cstring + textJustify*: cstring + textOrientation*: cstring + textOverflow*: cstring + textRendering*: cstring + textShadow*: cstring textTransform*: cstring - transform*: cstring + textUnderlineOffset*: cstring + textUnderlinePosition*: cstring top*: cstring + touchAction*: cstring + transform*: cstring + transformBox*: cstring + transformOrigin*: cstring + transformStyle*: cstring + transition*: cstring + transitionDelay*: cstring + transitionDuration*: cstring + transitionProperty*: cstring + transitionTimingFunction*: cstring + translate*: cstring + unicodeBidi*: cstring verticalAlign*: cstring visibility*: cstring - width*: cstring whiteSpace*: cstring + widows*: cstring + width*: cstring + willChange*: cstring + wordBreak*: cstring wordSpacing*: cstring - zIndex*: int + writingMode*: cstring + zIndex*: cstring EventPhase* = enum None = 0, From 70b07edf42d5bc5296bfc02769846d9d10069213 Mon Sep 17 00:00:00 2001 From: Sizhe Zhao Date: Sat, 27 Jun 2020 18:11:29 +0800 Subject: [PATCH 0299/3768] Fix missing comma (#14829) --- doc/nep1.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/nep1.rst b/doc/nep1.rst index 35b1a1f62..75343c85b 100644 --- a/doc/nep1.rst +++ b/doc/nep1.rst @@ -267,7 +267,7 @@ Conventions for multi-line statements and expressions EventCallback = proc (timeReceived: Time, errorCode: int, event: Event, output: var string) - proc lotsOfArguments(argOne: string, argTwo: int, argThree: float + proc lotsOfArguments(argOne: string, argTwo: int, argThree: float, argFour: proc(), argFive: bool): int {.heyLookALongPragma.} = From fdb37400cba234938a27cf158b6f4cf3ac57d689 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Sat, 27 Jun 2020 15:58:19 +0200 Subject: [PATCH 0300/3768] Testament: Reenable arraymancer (#14831) --- testament/important_packages.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 352ce2cd7..972bb3faa 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -11,7 +11,7 @@ var packages2*: seq[tuple[name, cmd: string; hasDeps: bool; url: string, useHead # packages A-M pkg1 "alea", true pkg1 "argparse" -# pkg1 "arraymancer", true, "nim c tests/tests_cpu.nim" +pkg1 "arraymancer", true, "nim c tests/tests_cpu.nim" pkg1 "ast_pattern_matching", false, "nim c -r --oldgensym:on tests/test1.nim" pkg1 "asyncmysql", true pkg1 "awk", true From 90808877c54a3822a978124f39f470ea95e37d4d Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 27 Jun 2020 07:51:17 -0700 Subject: [PATCH 0301/3768] testament: generic N-fold batching: windows CI 37mn=>16m (#14823) * testament: run CI faster thanks to batching * move ta_in, tstdin into existing tosproc * move ta_out,tafalse,texitcode,tstderr into existing tosproc * joinable osproc * move tstdout into existing tosproc * spec: batchable; fix tests * fixup --- azure-pipelines.yml | 12 +++- koch.nim | 7 ++- lib/nimhcr.nim | 4 ++ lib/nimrtl.nim | 4 ++ testament/categories.nim | 5 +- testament/specs.nim | 23 ++++++++ testament/testament.nim | 18 +++++- tests/osproc/ta_in.nim | 9 --- tests/osproc/ta_out.nim | 33 ----------- tests/osproc/tafalse.nim | 7 --- tests/osproc/texecps.nim | 4 ++ tests/osproc/texitcode.nim | 26 --------- tests/osproc/tstderr.nim | 37 ------------ tests/osproc/tstdin.nim | 17 ------ tests/osproc/tstdout.nim | 31 ---------- tests/stdlib/tosproc.nim | 116 ++++++++++++++++++++++++++++++++----- 16 files changed, 172 insertions(+), 181 deletions(-) delete mode 100644 tests/osproc/ta_in.nim delete mode 100644 tests/osproc/ta_out.nim delete mode 100644 tests/osproc/tafalse.nim delete mode 100644 tests/osproc/texitcode.nim delete mode 100644 tests/osproc/tstderr.nim delete mode 100644 tests/osproc/tstdin.nim delete mode 100644 tests/osproc/tstdout.nim diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 6ec6d0b95..fd8b2eea8 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -27,9 +27,19 @@ jobs: vmImage: 'macOS-10.15' CPU: amd64 NIM_COMPILE_TO_CPP: true - Windows_amd64: + Windows_amd64_batch0_3: vmImage: 'windows-2019' CPU: amd64 + # see also: `NIM_TEST_PACKAGES` + NIM_TESTAMENT_BATCH: "0_3" + Windows_amd64_batch1_3: + vmImage: 'windows-2019' + CPU: amd64 + NIM_TESTAMENT_BATCH: "1_3" + Windows_amd64_batch2_3: + vmImage: 'windows-2019' + CPU: amd64 + NIM_TESTAMENT_BATCH: "2_3" pool: vmImage: $(vmImage) diff --git a/koch.nim b/koch.nim index bbcb0e349..e279d76de 100644 --- a/koch.nim +++ b/koch.nim @@ -548,7 +548,12 @@ proc runCI(cmd: string) = execFold("Compile tester", "nim c -d:nimCoroutines --os:genode -d:posix --compileOnly testament/testament") # main bottleneck here - execFold("Run tester", "nim c -r -d:nimCoroutines testament/testament --pedantic all -d:nimCoroutines") + # xxx: even though this is the main bottlneck, we could use same code to batch the other tests + #[ + BUG: with initOptParser, `--batch:'' all` interprets `all` as the argument of --batch + ]# + execFold("Run tester", "nim c -r -d:nimCoroutines testament/testament --pedantic --batch:$1 all -d:nimCoroutines" % ["NIM_TESTAMENT_BATCH".getEnv("_")]) + block CT_FFI: when defined(posix): # windows can be handled in future PR's execFold("nimble install -y libffi", "nimble install -y libffi") diff --git a/lib/nimhcr.nim b/lib/nimhcr.nim index 372ee1973..89bece6dc 100644 --- a/lib/nimhcr.nim +++ b/lib/nimhcr.nim @@ -1,3 +1,7 @@ +discard """ +batchable: false +""" + # # # Nim's Runtime Library diff --git a/lib/nimrtl.nim b/lib/nimrtl.nim index 7ec5eb981..a2a10c6bd 100644 --- a/lib/nimrtl.nim +++ b/lib/nimrtl.nim @@ -1,3 +1,7 @@ +discard """ +batchable: false +""" + # # # Nim's Runtime Library diff --git a/testament/categories.nim b/testament/categories.nim index d4e6a0803..4dfd42fd2 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -679,8 +679,9 @@ proc runJoinedTest(r: var TResults, cat: Category, testsDir: string) = quit 1 else: echo "megatest output OK" - removeFile(outputGottenFile) - removeFile(megatestFile) + when false: # no point removing those, always good for debugging + removeFile(outputGottenFile) + removeFile(megatestFile) # keep it around #testSpec r, makeTest("megatest", options, cat) # --------------------------------------------------------------------------- diff --git a/testament/specs.nim b/testament/specs.nim index 7405c5055..2f2c006d8 100644 --- a/testament/specs.nim +++ b/testament/specs.nim @@ -8,6 +8,15 @@ # import sequtils, parseutils, strutils, os, streams, parsecfg +from hashes import hash + +type TestamentData* = ref object + # better to group globals under 1 object; could group the other ones here too + batchArg*: string + testamentNumBatch*: int + testamentBatch*: int + +let testamentData0* = TestamentData() var compilerPrefix* = findExe("nim") @@ -68,11 +77,13 @@ type ccodeCheck*: string maxCodeSize*: int err*: TResultEnum + inCurrentBatch*: bool targets*: set[TTarget] matrix*: seq[string] nimout*: string parseErrors*: string # when the spec definition is invalid, this is not empty. unjoinable*: bool + unbatchable*: bool useValgrind*: bool timeout*: float # in seconds, fractions possible, # but don't rely on much precision @@ -138,6 +149,12 @@ proc addLine*(self: var string; a,b: string) = proc initSpec*(filename: string): TSpec = result.file = filename +proc isCurrentBatch(testamentData: TestamentData, filename: string): bool = + if testamentData.testamentNumBatch != 0: + hash(filename) mod testamentData.testamentNumBatch == testamentData.testamentBatch + else: + true + proc parseSpec*(filename: string): TSpec = result.file = filename let specStr = extractSpec(filename) @@ -203,6 +220,8 @@ proc parseSpec*(filename: string): TSpec = result.action = actionReject of "nimout": result.nimout = e.value + of "batchable": + result.unbatchable = not parseCfgBool(e.value) of "joinable": result.unjoinable = not parseCfgBool(e.value) of "valgrind": @@ -297,3 +316,7 @@ proc parseSpec*(filename: string): TSpec = if skips.anyIt(it in result.file): result.err = reDisabled + + result.inCurrentBatch = isCurrentBatch(testamentData0, filename) or result.unbatchable + if not result.inCurrentBatch: + result.err = reDisabled diff --git a/testament/testament.nim b/testament/testament.nim index 158a68822..58c4e5196 100644 --- a/testament/testament.nim +++ b/testament/testament.nim @@ -273,12 +273,14 @@ proc addResult(r: var TResults, test: TTest, target: TTarget, expected = expected, given = given) r.data.addf("$#\t$#\t$#\t$#", name, expected, given, $success) + template disp(msg) = + maybeStyledEcho styleDim, fgYellow, msg & " ", styleBright, fgCyan, name if success == reSuccess: maybeStyledEcho fgGreen, "PASS: ", fgCyan, alignLeft(name, 60), fgBlue, " (", durationStr, " sec)" elif success == reDisabled: - maybeStyledEcho styleDim, fgYellow, "SKIP: ", styleBright, fgCyan, name - elif success == reJoined: - maybeStyledEcho styleDim, fgYellow, "JOINED: ", styleBright, fgCyan, name + if test.spec.inCurrentBatch: disp("SKIP:") + else: disp("NOTINBATCH:") + elif success == reJoined: disp("JOINED:") else: maybeStyledEcho styleBright, fgRed, failString, fgCyan, name maybeStyledEcho styleBright, fgCyan, "Test \"", test.name, "\"", " in category \"", test.cat.string, "\"" @@ -645,6 +647,15 @@ proc main() = useColors = false else: quit Usage + of "batch": + testamentData0.batchArg = p.val + if p.val != "_": + let s = p.val.split("_") + doAssert s.len == 2, $(p.val, s) + testamentData0.testamentBatch = s[0].parseInt + testamentData0.testamentNumBatch = s[1].parseInt + doAssert testamentData0.testamentNumBatch > 0 + doAssert testamentData0.testamentBatch >= 0 and testamentData0.testamentBatch < testamentData0.testamentNumBatch of "simulate": simulate = true of "megatest": @@ -682,6 +693,7 @@ proc main() = myself &= " " & quoteShell("--targets:" & targetsStr) myself &= " " & quoteShell("--nim:" & compilerPrefix) + myself &= " --batch:" & testamentData0.batchArg if skipFrom.len > 0: myself &= " " & quoteShell("--skipFrom:" & skipFrom) diff --git a/tests/osproc/ta_in.nim b/tests/osproc/ta_in.nim deleted file mode 100644 index fb294ec14..000000000 --- a/tests/osproc/ta_in.nim +++ /dev/null @@ -1,9 +0,0 @@ -discard """ -action: compile -""" - -# This file is prefixed with an "a", because other tests -# depend on it and it must be compiled first. -import strutils -let x = stdin.readLine() -echo x.parseInt + 5 diff --git a/tests/osproc/ta_out.nim b/tests/osproc/ta_out.nim deleted file mode 100644 index 01b78eb11..000000000 --- a/tests/osproc/ta_out.nim +++ /dev/null @@ -1,33 +0,0 @@ -discard """ -output: ''' -start ta_out -to stdout -to stdout -to stderr -to stderr -to stdout -to stdout -end ta_out -''' -""" - -echo "start ta_out" - -# This file is prefixed with an "a", because other tests -# depend on it and it must be compiled first. -stdout.writeLine("to stdout") -stdout.flushFile() -stdout.writeLine("to stdout") -stdout.flushFile() - -stderr.writeLine("to stderr") -stderr.flushFile() -stderr.writeLine("to stderr") -stderr.flushFile() - -stdout.writeLine("to stdout") -stdout.flushFile() -stdout.writeLine("to stdout") -stdout.flushFile() - -echo "end ta_out" diff --git a/tests/osproc/tafalse.nim b/tests/osproc/tafalse.nim deleted file mode 100644 index 05a0bfce9..000000000 --- a/tests/osproc/tafalse.nim +++ /dev/null @@ -1,7 +0,0 @@ -discard """ -exitcode: 1 -""" - -# 'tafalse.nim' to ensure it is compiled before texitcode.nim -import system -quit(QuitFailure) diff --git a/tests/osproc/texecps.nim b/tests/osproc/texecps.nim index 10715fa0f..b818fe8eb 100644 --- a/tests/osproc/texecps.nim +++ b/tests/osproc/texecps.nim @@ -1,3 +1,7 @@ +discard """ +joinable: false +""" + import osproc, streams, strutils, os const NumberOfProcesses = 13 diff --git a/tests/osproc/texitcode.nim b/tests/osproc/texitcode.nim deleted file mode 100644 index 0b05ca3cb..000000000 --- a/tests/osproc/texitcode.nim +++ /dev/null @@ -1,26 +0,0 @@ -discard """ - output: "" -""" - -import osproc, os - -const filename = when defined(Windows): "tafalse.exe" else: "tafalse" -let dir = getCurrentDir() / "tests" / "osproc" -doAssert fileExists(dir / filename) - -var p = startProcess(filename, dir) -doAssert(waitForExit(p) == QuitFailure) - -p = startProcess(filename, dir) -var running = true -while running: - running = running(p) -doAssert(waitForExit(p) == QuitFailure) - -# make sure that first call to running() after process exit returns false -p = startProcess(filename, dir) -for j in 0..<30: # refs #13449 - os.sleep(50) - if not running(p): break -doAssert(not running(p)) -doAssert(waitForExit(p) == QuitFailure) # avoid zombies diff --git a/tests/osproc/tstderr.nim b/tests/osproc/tstderr.nim deleted file mode 100644 index 55b11eba5..000000000 --- a/tests/osproc/tstderr.nim +++ /dev/null @@ -1,37 +0,0 @@ -discard """ - output: ''' -start tstderr --------------------------------------- -to stderr -to stderr --------------------------------------- -''' -""" - -echo "start tstderr" - -import osproc, os, streams - -const filename = "ta_out".addFileExt(ExeExt) - -doAssert fileExists(getCurrentDir() / "tests" / "osproc" / filename) - -var p = startProcess(filename, getCurrentDir() / "tests" / "osproc", - options={}) - -try: - let stdoutStream = p.outputStream - let stderrStream = p.errorStream - var x = newStringOfCap(120) - var output = "" - while stderrStream.readLine(x.TaintedString): - output.add(x & "\n") - - echo "--------------------------------------" - stdout.flushFile() - stderr.write output - stderr.flushFile() - echo "--------------------------------------" - stdout.flushFile() -finally: - p.close() diff --git a/tests/osproc/tstdin.nim b/tests/osproc/tstdin.nim deleted file mode 100644 index 8579680ab..000000000 --- a/tests/osproc/tstdin.nim +++ /dev/null @@ -1,17 +0,0 @@ -discard """ - output: "10" -""" -import osproc, os, streams - -const filename = when defined(Windows): "ta_in.exe" else: "ta_in" - -doAssert fileExists(getCurrentDir() / "tests" / "osproc" / filename) - -var p = startProcess(filename, getCurrentDir() / "tests" / "osproc") -p.inputStream.write("5\n") -p.inputStream.flush() - -var line = "" - -while p.outputStream.readLine(line.TaintedString): - echo line diff --git a/tests/osproc/tstdout.nim b/tests/osproc/tstdout.nim deleted file mode 100644 index e3ed3986a..000000000 --- a/tests/osproc/tstdout.nim +++ /dev/null @@ -1,31 +0,0 @@ -discard """ - output: '''-------------------------------------- -start ta_out -to stdout -to stdout -to stderr -to stderr -to stdout -to stdout -end ta_out --------------------------------------- -''' -""" -import osproc, os, streams - -const filename = when defined(Windows): "ta_out.exe" else: "ta_out" - -doAssert fileExists(getCurrentDir() / "tests" / "osproc" / filename) - -var p = startProcess(filename, getCurrentDir() / "tests" / "osproc", - options={poStdErrToStdOut}) - -let outputStream = p.outputStream -var x = newStringOfCap(120) -var output = "" -while outputStream.readLine(x.TaintedString): - output.add(x & "\n") - -echo "--------------------------------------" -stdout.write output -echo "--------------------------------------" diff --git a/tests/stdlib/tosproc.nim b/tests/stdlib/tosproc.nim index cb6e260c5..363bd5d74 100644 --- a/tests/stdlib/tosproc.nim +++ b/tests/stdlib/tosproc.nim @@ -5,9 +5,10 @@ joinable: false #[ joinable: false because it'd need cleanup up stdout -]# -import stdtest/[specialpaths, unittest_light] +see also: tests/osproc/*.nim; consider merging those into a single test here +(easier to factor and test more things as a single self contained test) +]# when defined(case_testfile): # compiled test file for child process from posix import exitnow @@ -51,22 +52,58 @@ when defined(case_testfile): # compiled test file for child process echo args[1] main() -else: +elif defined(case_testfile2): + import strutils + let x = stdin.readLine() + echo x.parseInt + 5 +elif defined(case_testfile3): + echo "start ta_out" + stdout.writeLine("to stdout") + stdout.flushFile() + stdout.writeLine("to stdout") + stdout.flushFile() + + stderr.writeLine("to stderr") + stderr.flushFile() + stderr.writeLine("to stderr") + stderr.flushFile() + + stdout.writeLine("to stdout") + stdout.flushFile() + stdout.writeLine("to stdout") + stdout.flushFile() + echo "end ta_out" + +elif defined(case_testfile4): + import system # we could remove that + quit(QuitFailure) + +else: # main driver + import stdtest/[specialpaths, unittest_light] import os, osproc, strutils const nim = getCurrentCompilerExe() + const sourcePath = currentSourcePath() + let dir = getCurrentDir() / "tests" / "osproc" + + template deferScoped(cleanup, body) = + # pending https://github.com/nim-lang/RFCs/issues/236#issuecomment-646855314 + # xxx move to std/sugar or (preferably) some low level module + try: body + finally: cleanup + + # we're testing `execShellCmd` so don't rely on it to compile test file + # note: this should be exported in posix.nim + proc c_system(cmd: cstring): cint {.importc: "system", header: "".} + + proc compileNimProg(opt: string, name: string): string = + result = buildDir / name.addFileExt(ExeExt) + let cmd = "$# c -o:$# --hints:off $# $#" % [nim.quoteShell, result.quoteShell, opt, sourcePath.quoteShell] + doAssert c_system(cmd) == 0, $cmd + doAssert result.fileExists block execShellCmdTest: - ## first, compile child program - const sourcePath = currentSourcePath() - let output = buildDir / "D20190111T024543".addFileExt(ExeExt) - let cmd = "$# c -o:$# -d:release -d:case_testfile $#" % [nim, output, - sourcePath] - # we're testing `execShellCmd` so don't rely on it to compile test file - # note: this should be exported in posix.nim - proc c_system(cmd: cstring): cint {.importc: "system", - header: "".} - assertEquals c_system(cmd), 0 + let output = compileNimProg("-d:release -d:case_testfile", "D20190111T024543") ## use it template runTest(arg: string, expected: int) = @@ -79,7 +116,7 @@ else: runTest("quit_139", 139) block execProcessTest: - let dir = parentDir(currentSourcePath()) + let dir = sourcePath.parentDir let (_, err) = execCmdEx(nim & " c " & quoteShell(dir / "osproctest.nim")) doAssert err == 0 let exePath = dir / addFileExt("osproctest", ExeExt) @@ -101,6 +138,7 @@ else: discard import std/streams + block: # test for startProcess (more tests needed) # bugfix: windows stdin.close was a noop and led to blocking reads proc startProcessTest(command: string, options: set[ProcessOption] = { @@ -126,6 +164,56 @@ else: var result = startProcessTest("nim r --hints:off -", options = {}, input = "echo 3*4") doAssert result == ("12\n", 0) + block: # startProcess stdin (replaces old test `tstdin` + `ta_in`) + let output = compileNimProg("-d:case_testfile2", "D20200626T215919") + var p = startProcess(output, dir) # dir not needed though + p.inputStream.write("5\n") + p.inputStream.flush() + var line = "" + var s: seq[string] + while p.outputStream.readLine(line.TaintedString): + s.add line + doAssert s == @["10"] + + block: + let output = compileNimProg("-d:case_testfile3", "D20200626T221233") + var x = newStringOfCap(120) + block: # startProcess stdout poStdErrToStdOut (replaces old test `tstdout` + `ta_out`) + var p = startProcess(output, dir, options={poStdErrToStdOut}) + deferScoped: p.close() + do: + var sout: seq[string] + while p.outputStream.readLine(x.TaintedString): sout.add x + doAssert sout == @["start ta_out", "to stdout", "to stdout", "to stderr", "to stderr", "to stdout", "to stdout", "end ta_out"] + block: # startProcess stderr (replaces old test `tstderr` + `ta_out`) + var p = startProcess(output, dir, options={}) + deferScoped: p.close() + do: + var serr, sout: seq[string] + while p.errorStream.readLine(x.TaintedString): serr.add x + while p.outputStream.readLine(x.TaintedString): sout.add x + doAssert serr == @["to stderr", "to stderr"] + doAssert sout == @["start ta_out", "to stdout", "to stdout", "to stdout", "to stdout", "end ta_out"] + + block: # startProcess exit code (replaces old test `texitcode` + `tafalse`) + let output = compileNimProg("-d:case_testfile4", "D20200626T224758") + var p = startProcess(output, dir) + doAssert waitForExit(p) == QuitFailure + p = startProcess(output, dir) + var running = true + while running: + # xxx: avoid busyloop? + running = running(p) + doAssert waitForExit(p) == QuitFailure + + # make sure that first call to running() after process exit returns false + p = startProcess(output, dir) + for j in 0..<30: # refs #13449 + os.sleep(50) + if not running(p): break + doAssert not running(p) + doAssert waitForExit(p) == QuitFailure # avoid zombies + import std/strtabs block execProcessTest: var result = execCmdEx("nim r --hints:off -", options = {}, input = "echo 3*4") From ad00d616e8ed3dc3e7b1e8fb4a8681e5b4eca0e8 Mon Sep 17 00:00:00 2001 From: Sizhe Zhao Date: Sat, 27 Jun 2020 22:51:48 +0800 Subject: [PATCH 0302/3768] Warn about calling wrappers at compile time until #14049 is fixed. (#14828) --- doc/manual.rst | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/manual.rst b/doc/manual.rst index da50e2c56..9f5252ae0 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -843,7 +843,10 @@ language features: * closure iterators * the ``cast`` operator * reference (pointer) types -* the FFI +* FFI + +The use of wrappers that use FFI and/or ``cast`` is also disallowed. Note that +these wrappers include the ones in the standard libraries. Some or all of these restrictions are likely to be lifted over time. From 22f1b5120e6ea1b182185e31b2744fc3cd39cd6e Mon Sep 17 00:00:00 2001 From: Manuel Bojato <30560560+KingDarBoja@users.noreply.github.com> Date: Sat, 27 Jun 2020 16:36:31 -0500 Subject: [PATCH 0303/3768] Fix nimdoc invalid css on theme switch class (#14834) --- doc/nimdoc.css | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/doc/nimdoc.css b/doc/nimdoc.css index 72b6734ab..6025d6601 100644 --- a/doc/nimdoc.css +++ b/doc/nimdoc.css @@ -63,12 +63,13 @@ Modified by Boyd Greenfield and narimiran .theme-switch-wrapper { display: flex; align-items: center; - - em { - margin-left: 10px; - font-size: 1rem; - } } + +.theme-switch-wrapper em { + margin-left: 10px; + font-size: 1rem; +} + .theme-switch { display: inline-block; height: 22px; From bc66788b3fbe0a312ddd4e1c2d58e902787ca512 Mon Sep 17 00:00:00 2001 From: Manuel Bojato <30560560+KingDarBoja@users.noreply.github.com> Date: Sat, 27 Jun 2020 16:42:58 -0500 Subject: [PATCH 0304/3768] docs: Make `..<`, `.. ^` more discoverable (#14835) --- lib/system_overview.rst | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/lib/system_overview.rst b/lib/system_overview.rst index 23284dabf..f0945873c 100644 --- a/lib/system_overview.rst +++ b/lib/system_overview.rst @@ -45,24 +45,27 @@ Proc Usage Seqs ---- -======================================== ========================================== -Proc Usage -======================================== ========================================== -`newSeq<#newSeq>`_ Create a new sequence of a given length -`newSeqOfCap<#newSeqOfCap,Natural>`_ Create a new sequence with zero length - and a given capacity -`setLen<#setLen,seq[T][T],Natural>`_ Set the length of a sequence -`len<#len,seq[T][T]>`_ Return the length of a sequence -`@<#@,array[IDX,T]>`_ Turn an array into a sequence -`add<#add,seq[T][T],T>`_ Add an item to the sequence -`insert<#insert,seq[T][T],T>`_ Insert an item at a specific position -`delete<#delete,seq[T][T],Natural>`_ Delete an item while preserving the - order of elements (`O(n)` operation) -`del<#del,seq[T][T],Natural>`_ `O(1)` removal, doesn't preserve the order -`pop<#pop,seq[T][T]>`_ Remove and return last item of a sequence -`x & y<#&,seq[T][T],seq[T][T]>`_ Concatenate two sequences -`x[a..b]<#[],openArray[T],HSlice[U,V]>`_ Slice of a sequence (both ends included) -======================================== ========================================== +=========================================== ========================================== +Proc Usage +=========================================== ========================================== +`newSeq<#newSeq>`_ Create a new sequence of a given length +`newSeqOfCap<#newSeqOfCap,Natural>`_ Create a new sequence with zero length + and a given capacity +`setLen<#setLen,seq[T][T],Natural>`_ Set the length of a sequence +`len<#len,seq[T][T]>`_ Return the length of a sequence +`@<#@,array[IDX,T]>`_ Turn an array into a sequence +`add<#add,seq[T][T],T>`_ Add an item to the sequence +`insert<#insert,seq[T][T],T>`_ Insert an item at a specific position +`delete<#delete,seq[T][T],Natural>`_ Delete an item while preserving the + order of elements (`O(n)` operation) +`del<#del,seq[T][T],Natural>`_ `O(1)` removal, doesn't preserve the order +`pop<#pop,seq[T][T]>`_ Remove and return last item of a sequence +`x & y<#&,seq[T][T],seq[T][T]>`_ Concatenate two sequences +`x[a..b]<#[],openArray[T],HSlice[U,V]>`_ Slice of a sequence (both ends included) +`x[a..^b]<#[],openArray[T],HSlice[U,V]>`_ Slice of a sequence but `b` is a + reversed index (both ends included) +`x[a..\`_ Slice of a sequence (excluded upper bound) +=========================================== ========================================== **See also:** * `sequtils module `_ for operations on container @@ -160,8 +163,10 @@ Proc Usage `T or F<#or,bool,bool>`_ Boolean `or` `T xor F<#xor,bool,bool>`_ Boolean `xor` (exclusive or) `not T<#not,bool>`_ Boolean `not` +`a[^x]<#^.t,int>`_ Take the element at the reversed index `x` `a .. b<#..,T,U>`_ Binary slice that constructs an interval `[a, b]` +`a ..^ b<#..^.t,untyped,untyped>`_ Interval `[a, b]` but `b` as reversed index [a ..< b](#..<.t,untyped,untyped) Interval `[a, b)` (excluded upper bound) [runnableExamples](#runnableExamples,untyped) Create testable documentation ============================================= ============================================ From 252f668829ee15f91c9c7febd6cec8e9a85d6fc7 Mon Sep 17 00:00:00 2001 From: rockcavera Date: Sat, 27 Jun 2020 18:59:08 -0300 Subject: [PATCH 0305/3768] add a second asyncnet.recvFrom (#14237) * add a second asyncnet.recvFrom * fixes * pre-allocating address * add a new nativesockets.getAddrString() --- lib/pure/asyncnet.nim | 95 +++++++++++++++++++++++++------------- lib/pure/nativesockets.nim | 34 ++++++++++++++ 2 files changed, 98 insertions(+), 31 deletions(-) diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index 3fc1a0177..410310e29 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -853,6 +853,60 @@ proc sendTo*(socket: AsyncSocket, address: string, port: Port, data: string, else: raise newException(IOError, "Couldn't resolve address: " & address) +proc recvFrom*(socket: AsyncSocket, data: FutureVar[string], size: int, + address: FutureVar[string], port: FutureVar[Port], + flags = {SocketFlag.SafeDisconn}): owned(Future[int]) + {.async, since: (1, 3).} = + ## Receives a datagram data from ``socket`` into ``data``, which must be at + ## least of size ``size``. The address and port of datagram's sender will be + ## stored into ``address`` and ``port``, respectively. Returned future will + ## complete once one datagram has been received, and will return size of + ## packet received. + ## + ## If an error occurs an OSError exception will be raised. + ## + ## This proc is normally used with connectionless sockets (UDP sockets). + ## + ## **Notes** + ## * ``data`` must be initialized to the length of ``size``. + ## * ``address`` must be initialized to 46 in length. + template adaptRecvFromToDomain(domain: Domain) = + var lAddr = sizeof(sAddr).SockLen + + result = await recvFromInto(AsyncFD(getFd(socket)), cstring(data.mget()), size, + cast[ptr SockAddr](addr sAddr), addr lAddr, + flags) + + data.mget().setLen(result) + data.complete() + + getAddrString(cast[ptr SockAddr](addr sAddr), address.mget()) + + address.complete() + + when domain == AF_INET6: + port.complete(ntohs(sAddr.sin6_port).Port) + else: + port.complete(ntohs(sAddr.sin_port).Port) + + assert(socket.protocol != IPPROTO_TCP, + "Cannot `recvFrom` on a TCP socket. Use `recv` or `recvInto` instead") + assert(not socket.closed, "Cannot `recvFrom` on a closed socket") + assert(size == len(data.mget()), + "`date` was not initialized correctly. `size` != `len(data.mget())`") + assert(46 == len(address.mget()), + "`address` was not initialized correctly. 46 != `len(address.mget())`") + + case socket.domain + of AF_INET6: + var sAddr: Sockaddr_in6 + adaptRecvFromToDomain(AF_INET6) + of AF_INET: + var sAddr: Sockaddr_in + adaptRecvFromToDomain(AF_INET) + else: + raise newException(ValueError, "Unknown socket address family") + proc recvFrom*(socket: AsyncSocket, size: int, flags = {SocketFlag.SafeDisconn}): owned(Future[tuple[data: string, address: string, port: Port]]) @@ -865,38 +919,17 @@ proc recvFrom*(socket: AsyncSocket, size: int, ## If an error occurs an OSError exception will be raised. ## ## This proc is normally used with connectionless sockets (UDP sockets). - template adaptRecvFromToDomain(domain: Domain) = - var lAddr = sizeof(sAddr).SockLen - - let fut = await recvFromInto(AsyncFD(getFd(socket)), cstring(data), size, - cast[ptr SockAddr](addr sAddr), addr lAddr, - flags) - - data.setLen(fut) - - result.data = data - result.address = getAddrString(cast[ptr SockAddr](addr sAddr)) - - when domain == AF_INET6: - result.port = ntohs(sAddr.sin6_port).Port - else: - result.port = ntohs(sAddr.sin_port).Port - - assert(socket.protocol != IPPROTO_TCP, - "Cannot `recvFrom` on a TCP socket. Use `recv` or `recvInto` instead") - assert(not socket.closed, "Cannot `recvFrom` on a closed socket") - - var data = newString(size) + var + data = newFutureVar[string]() + address = newFutureVar[string]() + port = newFutureVar[Port]() - case socket.domain - of AF_INET6: - var sAddr: Sockaddr_in6 - adaptRecvFromToDomain(AF_INET6) - of AF_INET: - var sAddr: Sockaddr_in - adaptRecvFromToDomain(AF_INET) - else: - raise newException(ValueError, "Unknown socket address family") + data.mget().setLen(size) + address.mget().setLen(46) + + let read = await recvFrom(socket, data, size, address, port, flags) + + result = (data.mget(), address.mget(), port.mget()) when not defined(testing) and isMainModule: type diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim index 67e24eedc..35a7396b9 100644 --- a/lib/pure/nativesockets.nim +++ b/lib/pure/nativesockets.nim @@ -461,6 +461,40 @@ proc getAddrString*(sockAddr: ptr SockAddr): string = return "unix" raise newException(IOError, "Unknown socket family in getAddrString") +proc getAddrString*(sockAddr: ptr SockAddr, strAddress: var string) = + ## Stores in ``strAddress`` the string representation of the address inside + ## ``sockAddr`` + ## + ## **Note** + ## * ``strAddress`` must be initialized to 46 in length. + assert(46 == len(strAddress), + "`strAddress` was not initialized correctly. 46 != `len(strAddress)`") + 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], + strAddress.len.int32) == nil: + raiseOSError(osLastError()) + else: + if winlean.inet_ntop(winlean.AF_INET, addr4, addr strAddress[0], + 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], + 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], + strAddress.len.int32) == nil: + raiseOSError(osLastError()) + else: + raise newException(IOError, "Unknown socket family in getAddrString") + setLen(strAddress, len(cstring(strAddress))) + when defined(posix) and not defined(nimdoc): proc makeUnixAddr*(path: string): Sockaddr_un = result.sun_family = AF_UNIX.TSa_Family From 54a52e7d088a388f8c52007fbb7e6db1bf2044c6 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Sun, 28 Jun 2020 00:13:54 +0200 Subject: [PATCH 0306/3768] Update link to parseSpec proc --- tests/readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/readme.md b/tests/readme.md index 234ee4efe..69be92051 100644 --- a/tests/readme.md +++ b/tests/readme.md @@ -9,7 +9,7 @@ top-level tests like `tests/tbar.nim`. Each test can contain a spec in a ``discard """ ... """`` block. -**Check out the [``parseSpec`` procedure](https://github.com/nim-lang/Nim/blob/devel/testament/specs.nim#L132) in the ``specs`` module for a full and reliable reference** +**Check out the [``parseSpec`` procedure](https://github.com/nim-lang/Nim/blob/devel/testament/specs.nim#L158) in the ``specs`` module for a full and reliable reference** ## action From 5d5271b7896eef3eafbb2beaf3f1fd4159b797f6 Mon Sep 17 00:00:00 2001 From: lqdev Date: Sun, 28 Jun 2020 13:38:57 +0200 Subject: [PATCH 0307/3768] fixed #14839 (#14840) --- lib/pure/colors.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/pure/colors.nim b/lib/pure/colors.nim index d57e309e9..dbcb1b4a2 100644 --- a/lib/pure/colors.nim +++ b/lib/pure/colors.nim @@ -452,7 +452,7 @@ proc parseColor*(name: string): Color = assert parseColor(b) == Color(0x01_79_fc) doAssertRaises(ValueError): discard parseColor(c) - if name[0] == '#': + if name.len > 0 and name[0] == '#': result = Color(parseHexInt(name)) else: var idx = binarySearch(colorNames, name, colorNameCmp) @@ -472,6 +472,7 @@ proc isColor*(name: string): bool = assert b.isColor assert not c.isColor + if name.len == 0: return false if name[0] == '#': for i in 1 .. name.len-1: if name[i] notin {'0'..'9', 'a'..'f', 'A'..'F'}: return false From 037990bc78914a793a4195c326282f8353cda319 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Sun, 28 Jun 2020 15:38:24 +0200 Subject: [PATCH 0308/3768] Changelog: Tiny style improvement --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 07d9b6112..de2e443fc 100644 --- a/changelog.md +++ b/changelog.md @@ -150,7 +150,7 @@ deallocShared(x.val) x.val = nil ``` -- getImpl() on enum type symbols now returns field syms instead of idents. This helps +- `getImpl` on enum type symbols now returns field syms instead of idents. This helps with writing typed macros. Old behavior for backwards compatiblity can be restored with command line switch `--useVersion:1.0`. - ``let`` statements can now be used without a value if declared with From 394a56650ae6139786d8cefb838b0e3e282c24a1 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Sun, 28 Jun 2020 17:47:47 +0200 Subject: [PATCH 0309/3768] Fix #14647 (#14776) * Fix #14647 * Correct fix * Typo and add test * For real now :p --- compiler/cgen.nim | 9 +++------ compiler/cgendata.nim | 4 ++-- tests/destructor/tgotoexceptions5.nim | 8 +++++++- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index cd02ba9b1..99bfe9119 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -1777,11 +1777,6 @@ proc genModule(m: BModule, cfile: Cfile): Rope = if moduleIsEmpty: result = nil -proc newPreInitProc(m: BModule): BProc = - result = newProc(nil, m) - # little hack so that unique temporaries are generated: - result.labels = 100_000 - proc initProcOptions(m: BModule): TOptions = let opts = m.config.options if sfSystemModule in m.module.flags: opts-{optStackTrace} else: opts @@ -1802,7 +1797,9 @@ proc rawNewModule(g: BModuleList; module: PSym, filename: AbsoluteFile): BModule result.sigConflicts = initCountTable[SigHash]() result.initProc = newProc(nil, result) result.initProc.options = initProcOptions(result) - result.preInitProc = newPreInitProc(result) + result.preInitProc = newProc(nil, result) + result.preInitProc.flags.incl nimErrorFlagDisabled + result.preInitProc.labels = 100_000 # little hack so that unique temporaries are generated initNodeTable(result.dataCache) result.typeStack = @[] result.typeNodesName = getTempName(result) diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index ce74963e2..a20931be4 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -189,8 +189,8 @@ proc newProc*(prc: PSym, module: BModule): BProc = new(result) result.prc = prc result.module = module - if prc != nil: result.options = prc.options - else: result.options = module.config.options + result.options = if prc != nil: prc.options + else: module.config.options newSeq(result.blocks, 1) result.nestedTryStmts = @[] result.finallySafePoints = @[] diff --git a/tests/destructor/tgotoexceptions5.nim b/tests/destructor/tgotoexceptions5.nim index 0e538d89e..695aab0a4 100644 --- a/tests/destructor/tgotoexceptions5.nim +++ b/tests/destructor/tgotoexceptions5.nim @@ -5,7 +5,7 @@ swallowed before swallowed B ''' - cmd: "nim c --gc:arc --exceptions:goto $file" + cmd: "nim c --gc:arc --exceptions:goto -d:ssl $file" """ # bug #13599 @@ -37,3 +37,9 @@ try: mainB() except: echo "swallowed B" + +# bug #14647 +import httpclient + +newAsyncHttpClient().close() + From d9c15017572afbbdfb8007bbaf33733b9ac69613 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Sun, 28 Jun 2020 18:59:57 +0300 Subject: [PATCH 0310/3768] get rid of $READLINK variable (#14841) --- bin/nim-gdb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/nim-gdb b/bin/nim-gdb index 1486b6dce..f7f9a79c6 100755 --- a/bin/nim-gdb +++ b/bin/nim-gdb @@ -8,9 +8,9 @@ which gdb > /dev/null || (echo "gdb not in PATH"; exit 1) which readlink > /dev/null || (echo "readlink not in PATH."; exit 1) if [[ "$(uname -s)" == "Darwin" || "(uname -s)" == *"BSD" ]]; then - NIM_SYSROOT=$(dirname $(dirname $($READLINK -f $(which nim)))) + NIM_SYSROOT=$(dirname $(dirname $(readlink -f $(which nim)))) else - NIM_SYSROOT=$(dirname $(dirname $($READLINK -e $(which nim)))) + NIM_SYSROOT=$(dirname $(dirname $(readlink -e $(which nim)))) fi # Find out where the pretty printer Python module is From c285739d55408bb5f75d3190a286d18ac883bd41 Mon Sep 17 00:00:00 2001 From: Danil Yarantsev Date: Sun, 28 Jun 2020 19:00:43 +0300 Subject: [PATCH 0311/3768] Remove double entry for thiscall (#14842) --- changelog.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index de2e443fc..54996ea34 100644 --- a/changelog.md +++ b/changelog.md @@ -180,9 +180,7 @@ proc mydiv(a, b): int {.raises: [].} = The reason for this is that `DivByZeroDefect` inherits from `Defect` and with `--panics:on` `Defects` become unrecoverable errors. -- Added the `thiscall` calling convention as specified by Microsoft. - -- Added `thiscall` calling convention as specified by Microsoft, mostly for hooking purpose +- Added the `thiscall` calling convention as specified by Microsoft, mostly for hooking purpose - Deprecated `{.unroll.}` pragma, was ignored by the compiler anyways, was a nop. From 4f70180a58ae46fcf313aed001242f8aa1252b1a Mon Sep 17 00:00:00 2001 From: Clyybber Date: Sun, 28 Jun 2020 18:03:39 +0200 Subject: [PATCH 0312/3768] Fix typo --- doc/hcr.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/hcr.rst b/doc/hcr.rst index f6db0ddfd..b2441dfbc 100644 --- a/doc/hcr.rst +++ b/doc/hcr.rst @@ -221,7 +221,7 @@ is a suitable place for implementing a program loop capable of calling Please note that reloading won't be possible when any of the type definitions in the program has been changed. When closure iterators are used (directly or -through async code), the reloaded refinitions will affect only newly created +through async code), the reloaded definitions will affect only newly created instances. Existing iterator instancess will execute their original code to completion. From 79367685601f4c90f6c478ce45651bffd9222c39 Mon Sep 17 00:00:00 2001 From: Danil Yarantsev Date: Sun, 28 Jun 2020 20:17:18 +0300 Subject: [PATCH 0313/3768] Fix some typos (#14843) --- doc/advopt.txt | 2 +- doc/codeowners.rst | 50 ++++++++++++++++++------------------- doc/destructors.rst | 2 +- doc/docgen.rst | 6 ++--- doc/hcr.rst | 2 +- doc/idetools.rst | 4 +-- doc/intern.rst | 6 ++--- doc/koch.rst | 2 +- doc/lib.rst | 4 +-- doc/manual.rst | 2 +- doc/manual_experimental.rst | 2 +- doc/nimc.rst | 2 +- 12 files changed, 42 insertions(+), 42 deletions(-) diff --git a/doc/advopt.txt b/doc/advopt.txt index 18bfde11b..588d4eece 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -78,7 +78,7 @@ Advanced options: if path == @path, will use first matching dir in `--path` if path == @default (the default and most useful), will use best match among @pkg,@path. - if these are nonexistant, will use project path + if these are nonexistent, will use project path -b, --backend:c|cpp|js|objc sets backend to use with commands like `nim doc` or `nim r` --docCmd:cmd if `cmd == skip`, skips runnableExamples else, runs runnableExamples with given options, eg: diff --git a/doc/codeowners.rst b/doc/codeowners.rst index 129c47cfc..4b4a5810e 100644 --- a/doc/codeowners.rst +++ b/doc/codeowners.rst @@ -13,31 +13,31 @@ library, araq is responsible for the compiler. Compiler ~~~~~~~~ -====================== ====================================================== -subsystem owner(s) -====================== ====================================================== -Parsing, Lexing araq -Renderer cooldome, araq -Order of passes cooldome -Semantic Checking araq -Virtual machine jangko, GULPF, araq -Sempass2: effects tracking cooldome, araq -type system, concepts zahary -transf cooldome, araq -semfold constant folding araq -template instantiation zahary, araq -term rewriting macros cooldome, araq -closure interators yglukhov, araq -lambda lifting yglukhov, araq -c, cpp codegen lemonboy, araq -js codegen yglukhov, lemonboy -alias analysis araq -dfa, writetracking araq -parallel, multithreading araq -incremental araq -sizeof computations krux02 -Exception handling cooldome, araq -====================== ====================================================== +=========================== ====================================================== +subsystem owner(s) +=========================== ====================================================== +Parsing, Lexing araq +Renderer cooldome, araq +Order of passes cooldome +Semantic Checking araq +Virtual machine jangko, GULPF, araq +Sempass2: effects tracking cooldome, araq +type system, concepts zahary +transf cooldome, araq +semfold constant folding araq +template instantiation zahary, araq +term rewriting macros cooldome, araq +closure iterators yglukhov, araq +lambda lifting yglukhov, araq +c, cpp codegen lemonboy, araq +js codegen yglukhov, lemonboy +alias analysis araq +dfa, writetracking araq +parallel, multithreading araq +incremental araq +sizeof computations krux02 +Exception handling cooldome, araq +=========================== ====================================================== diff --git a/doc/destructors.rst b/doc/destructors.rst index e7ebd93ea..b06d78bbe 100644 --- a/doc/destructors.rst +++ b/doc/destructors.rst @@ -225,7 +225,7 @@ A sink parameter *may* be consumed once in the proc's body but doesn't have to be consumed at all. The reason for this is that signatures like ``proc put(t: var Table; k: sink Key, v: sink Value)`` should be possible -without any further overloads and ``put`` might not take owership of ``k`` if +without any further overloads and ``put`` might not take ownership of ``k`` if ``k`` already exists in the table. Sink parameters enable an affine type system, not a linear type system. diff --git a/doc/docgen.rst b/doc/docgen.rst index 24743ba13..dac8808ad 100644 --- a/doc/docgen.rst +++ b/doc/docgen.rst @@ -244,7 +244,7 @@ Other Input Formats The *Nim compiler* also has support for RST (reStructuredText) files with the ``rst2html`` and ``rst2tex`` commands. Documents like this one are -initially written in a dialect of RST which adds support for nim sourcecode +initially written in a dialect of RST which adds support for nim source code highlighting with the ``.. code-block:: nim`` prefix. ``code-block`` also supports highlighting of C++ and some other c-like languages. @@ -330,7 +330,7 @@ Index (idx) file format Files with the ``.idx`` extension are generated when you use the `Index switch <#related-options-index-switch>`_ along with commands to generate -documentation from source or text files. You can programatically generate +documentation from source or text files. You can programmatically generate indices with the `setIndexTerm() `_ and `writeIndexFile() `_ procs. @@ -375,7 +375,7 @@ exception to this are table of content (TOC) entries. TOC entries are added to the index file with their third column having as much prefix spaces as their level is in the TOC (at least 1 character). The prefix whitespace helps to filter TOC entries from API or text symbols. This is important because the -amount of spaces is used to replicate the hiearchy for document TOCs in the +amount of spaces is used to replicate the hierarchy for document TOCs in the final index, and TOC entries found in ``.nim`` files are discarded. diff --git a/doc/hcr.rst b/doc/hcr.rst index b2441dfbc..567d77178 100644 --- a/doc/hcr.rst +++ b/doc/hcr.rst @@ -222,7 +222,7 @@ is a suitable place for implementing a program loop capable of calling Please note that reloading won't be possible when any of the type definitions in the program has been changed. When closure iterators are used (directly or through async code), the reloaded definitions will affect only newly created -instances. Existing iterator instancess will execute their original code to +instances. Existing iterator instances will execute their original code to completion. JavaScript target diff --git a/doc/idetools.rst b/doc/idetools.rst index e83bfe935..ac3b92947 100644 --- a/doc/idetools.rst +++ b/doc/idetools.rst @@ -69,7 +69,7 @@ Or:: pass in the same value as **proj.nim**. ``DIRTY_FILE`` - The **FILE** paramater is enough for static analysis, but IDEs + The **FILE** parameter is enough for static analysis, but IDEs tend to have *unsaved buffers* where the user may still be in the middle of typing a line. In such situations the IDE can save the current contents to a temporary file and then use the @@ -210,7 +210,7 @@ tab characters (``\t``). The values of each column are: def for definition, ``sug`` for suggestion, etc). 2. Type of the symbol. This can be ``skProc``, ``skLet``, and just about any of the enums defined in the module ``compiler/ast.nim``. -3. Full qualitifed path of the symbol. If you are querying a symbol +3. Full qualified path of the symbol. If you are querying a symbol defined in the ``proj.nim`` file, this would have the form ``proj.symbolName``. 4. Type/signature. For variables and enums this will contain the diff --git a/doc/intern.rst b/doc/intern.rst index b68067d1b..be183fd8e 100644 --- a/doc/intern.rst +++ b/doc/intern.rst @@ -468,7 +468,7 @@ The CellSet data structure The GC depends on an extremely efficient datastructure for storing a set of pointers - this is called a ``TCellSet`` in the source code. Inserting, deleting and searching are done in constant time. However, -modifying a ``TCellSet`` during traversation leads to undefined behaviour. +modifying a ``TCellSet`` during traversal leads to undefined behaviour. .. code-block:: Nim type @@ -558,7 +558,7 @@ This means that a call through a closure generates an ``if`` but the interoperability is worth the cost of the ``if``. Thunk generation would be possible too, but it's slightly more effort to implement. -Tests with GCC on Amd64 showed that it's really beneficical if the +Tests with GCC on Amd64 showed that it's really beneficial if the 'environment' pointer is passed as the last argument, not as the first argument. Proper thunk generation is harder because the proc that is to wrap @@ -609,7 +609,7 @@ Beware of nesting: .. code-block:: nim proc add(x: int): proc (y: int): proc (z: int): int {.closure.} {.closure.} = - return lamba (y: int): proc (z: int): int {.closure.} = + return lambda (y: int): proc (z: int): int {.closure.} = return lambda (z: int): int = return x + y + z diff --git a/doc/koch.rst b/doc/koch.rst index 436e0e406..e984a71ea 100644 --- a/doc/koch.rst +++ b/doc/koch.rst @@ -77,7 +77,7 @@ https://nim-lang.org. By default the documentation will be built in parallel using the number of available CPU cores. If any documentation build sub commands fail, they will -be rerun in serial fashion so that meaninful error output can be gathered for +be rerun in serial fashion so that meaningful error output can be gathered for inspection. The ``--parallelBuild:n`` switch or configuration option can be used to force a specific number of parallel jobs or run everything serially from the start (``n == 1``). diff --git a/doc/lib.rst b/doc/lib.rst index 97f6c1db1..e05657b42 100644 --- a/doc/lib.rst +++ b/doc/lib.rst @@ -214,7 +214,7 @@ Generic Operating System Services This module implements the ability to access symbols from shared libraries. * `marshal `_ - Contains procs for serialization and deseralization of arbitrary Nim + Contains procs for serialization and deserialization of arbitrary Nim data structures. * `memfiles `_ @@ -267,7 +267,7 @@ Math libraries Statistical analysis * `std/sums `_ - Fast sumation functions. + Fast summation functions. Internet Protocols and Support diff --git a/doc/manual.rst b/doc/manual.rst index 9f5252ae0..d2fcdc080 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -6455,7 +6455,7 @@ Align pragma The `align`:idx: pragma is for variables and object field members. It modifies the alignment requirement of the entity being declared. The argument must be a constant power of 2. Valid non-zero -alignments that are weaker than nother align pragmas on the same +alignments that are weaker than other align pragmas on the same declaration are ignored. Alignments that are weaker that the alignment requirement of the type are ignored. diff --git a/doc/manual_experimental.rst b/doc/manual_experimental.rst index d8efddf94..b0614885a 100644 --- a/doc/manual_experimental.rst +++ b/doc/manual_experimental.rst @@ -811,7 +811,7 @@ object inheritance syntax involving the ``of`` keyword: .. code-block:: nim type - Graph = concept g, type G of EqualyComparable, Copyable + Graph = concept g, type G of EquallyComparable, Copyable type VertexType = G.VertexType EdgeType = G.EdgeType diff --git a/doc/nimc.rst b/doc/nimc.rst index 59289a7c8..bd5e897c3 100644 --- a/doc/nimc.rst +++ b/doc/nimc.rst @@ -348,7 +348,7 @@ of your program. proc glfmMain*(display: ptr GLFMDisplay) {.exportc.} = NimMain() # initialize garbage collector memory, types and stack -Note: XCodes "make clean" gets confused about the genreated nim.c files, +Note: XCode's "make clean" gets confused about the generated nim.c files, so you need to clean those files manually to do a clean build. From 62394616e848e7a94bc0208d6535df6f582e74ce Mon Sep 17 00:00:00 2001 From: Clyybber Date: Sun, 28 Jun 2020 19:36:30 +0200 Subject: [PATCH 0314/3768] DFA and injectdestructors cleanup (#14824) * DFA and injectdestructors cleanup * More precise write analysis * Cleanup obsoleted path * Unify defInstrTargets and useInstrTargets * Misc cleanups * Nicer CFG printing * Misc cleanups 2 --- compiler/dfa.nim | 225 +++++++++++++-------------------- compiler/injectdestructors.nim | 48 +++---- 2 files changed, 117 insertions(+), 156 deletions(-) diff --git a/compiler/dfa.nim b/compiler/dfa.nim index c393b2c81..2254cd0ff 100644 --- a/compiler/dfa.nim +++ b/compiler/dfa.nim @@ -29,7 +29,7 @@ ## "A Graph–Free Approach to Data–Flow Analysis" by Markus Mohnen. ## https://link.springer.com/content/pdf/10.1007/3-540-45937-5_6.pdf -import ast, types, intsets, lineinfos, renderer +import ast, types, intsets, lineinfos, renderer, asciitables from patterns import sameTrees @@ -57,14 +57,11 @@ type Con = object code: ControlFlowGraph - inCall, inTryStmt: int + inTryStmt: int blocks: seq[TBlock] owner: PSym -proc debugInfo(info: TLineInfo): string = - result = $info.line #info.toFilename & ":" & $info.line - -proc codeListing(c: ControlFlowGraph, result: var string, start=0; last = -1) = +proc codeListing(c: ControlFlowGraph, start = 0; last = -1): string = # for debugging purposes # first iteration: compute all necessary labels: var jumpTargets = initIntSet() @@ -85,17 +82,14 @@ proc codeListing(c: ControlFlowGraph, result: var string, start=0; last = -1) = result.add "L" result.addInt c[i].dest+i result.add("\t#") - result.add(debugInfo(c[i].n.info)) + result.add($c[i].n.info.line) result.add("\n") inc i if i in jumpTargets: result.add("L" & $i & ": End\n") - # consider calling `asciitables.alignTable` -proc echoCfg*(c: ControlFlowGraph; start=0; last = -1) {.deprecated.} = +proc echoCfg*(c: ControlFlowGraph; start = 0; last = -1) {.deprecated.} = ## echos the ControlFlowGraph for debugging purposes. - var buf = "" - codeListing(c, buf, start, last) - echo buf + echo codeListing(c, start, last).alignTable proc forkI(c: var Con; n: PNode): TPosition = result = TPosition(c.code.len) @@ -273,22 +267,20 @@ duplicate the 'join' instructions on breaks and return exits! ]# -proc genLabel(c: Con): TPosition = - result = TPosition(c.code.len) +proc genLabel(c: Con): TPosition = TPosition(c.code.len) + +template checkedDistance(dist): int = + doAssert low(int) div 2 + 1 < dist and dist < high(int) div 2 + dist proc jmpBack(c: var Con, n: PNode, p = TPosition(0)) = - let dist = p.int - c.code.len - doAssert(low(int) div 2 + 1 < dist and dist < high(int) div 2) - c.code.add Instr(n: n, kind: goto, dest: dist) + c.code.add Instr(n: n, kind: goto, dest: checkedDistance(p.int - c.code.len)) proc patch(c: var Con, p: TPosition) = # patch with current index - let p = p.int - let diff = c.code.len - p - doAssert(low(int) div 2 + 1 < diff and diff < high(int) div 2) - c.code[p].dest = diff + c.code[p.int].dest = checkedDistance(c.code.len - p.int) -proc gen(c: var Con; n: PNode) # {.noSideEffect.} +func gen(c: var Con; n: PNode) proc popBlock(c: var Con; oldLen: int) = var exits: seq[TPosition] @@ -302,8 +294,8 @@ proc popBlock(c: var Con; oldLen: int) = c.patch e c.blocks.setLen(oldLen) -template withBlock(labl: PSym; body: untyped) {.dirty.} = - var oldLen {.gensym.} = c.blocks.len +template withBlock(labl: PSym; body: untyped) = + let oldLen = c.blocks.len c.blocks.add TBlock(isTryBlock: false, label: labl) body popBlock(c, oldLen) @@ -366,11 +358,9 @@ when true: endings[i] = c.forkI(n) c.gen(n[1]) for i in countdown(endings.high, 0): - let endPos = endings[i] - c.patch(endPos) + c.patch(endings[i]) else: - proc genWhile(c: var Con; n: PNode) = # lab1: # cond, tmp @@ -385,10 +375,9 @@ else: c.jmpBack(n, lab1) else: c.gen(n[0]) - let lab2 = c.forkI(n) - c.gen(n[1]) - c.jmpBack(n, lab1) - c.patch(lab2) + forkT(n): + c.gen(n[1]) + c.jmpBack(n, lab1) template forkT(n, body) = let lab1 = c.forkI(n) @@ -434,16 +423,14 @@ proc genIf(c: var Con, n: PNode) = ]# var endings: seq[TPosition] = @[] for i in 0.. x; x -> x.f + elif loc.aliases(insloc): + Partial # x.f -> x + else: None proc isAnalysableFieldAccess*(orig: PNode; owner: PSym): bool = var n = orig while true: case n.kind - of nkDotExpr, nkCheckedFieldExpr, nkHiddenSubConv, nkHiddenStdConv, - nkObjDownConv, nkObjUpConv, nkHiddenAddr, nkAddr: + of PathKinds0 - {nkBracketExpr, nkHiddenDeref, nkDerefExpr}: n = n[0] + of PathKinds1: + n = n[1] of nkBracketExpr: # in a[i] the 'i' must be known if n.len > 1 and n[1].kind in {nkCharLit..nkUInt64Lit}: @@ -662,10 +622,9 @@ proc isAnalysableFieldAccess*(orig: PNode; owner: PSym): bool = # bug #14159, we cannot reason about sinkParam[].location as it can # still be shared for tyRef. n = n[0] - return n.kind == nkSym and n.sym.owner == owner and ( - n.sym.typ.skipTypes(abstractInst-{tyOwned}).kind in {tyOwned}) - else: - break + return n.kind == nkSym and n.sym.owner == owner and + (n.sym.typ.skipTypes(abstractInst-{tyOwned}).kind in {tyOwned}) + else: break # XXX Allow closure deref operations here if we know # the owner controlled the closure allocation? result = n.kind == nkSym and n.sym.owner == owner and @@ -682,36 +641,37 @@ proc isAnalysableFieldAccess*(orig: PNode; owner: PSym): bool = # free the old data and all is lost! Lesson: Don't be too smart, trust the # lower level C++ optimizer to specialize this code. -proc genDef(c: var Con; n: PNode) = - var m = n - # XXX do something about this duplicated logic here. +proc skipTrivials(c: var Con, n: PNode): PNode = + result = n while true: - case m.kind - of nkDotExpr, nkCheckedFieldExpr, nkHiddenSubConv, nkHiddenStdConv, - nkObjDownConv, nkObjUpConv, nkHiddenAddr, nkAddr: - m = m[0] + case result.kind + of PathKinds0 - {nkBracketExpr}: + result = result[0] of nkBracketExpr: - gen(c, m[1]) - m = m[0] - of nkHiddenDeref, nkDerefExpr: - m = m[0] - else: - break + gen(c, result[1]) + result = result[0] + of PathKinds1: + result = result[1] + else: break + +proc genUse(c: var Con; orig: PNode) = + let n = c.skipTrivials(orig) if n.kind == nkSym and n.sym.kind in InterestingSyms: - c.code.add Instr(n: n, kind: def) - elif isAnalysableFieldAccess(n, c.owner): - c.code.add Instr(n: n, kind: def) - else: - # bug #13314: An assignment to t5.w = -5 is a usage of 't5' - # we still need to gather the use information: + c.code.add Instr(n: orig, kind: use) + elif n.kind in nkCallKinds: gen(c, n) +proc genDef(c: var Con; orig: PNode) = + let n = c.skipTrivials(orig) + + if n.kind == nkSym and n.sym.kind in InterestingSyms: + c.code.add Instr(n: orig, kind: def) + proc genCall(c: var Con; n: PNode) = gen(c, n[0]) var t = n[0].typ if t != nil: t = t.skipTypes(abstractInst) - inc c.inCall for i in 1.. abandon it. return high(int) + elif instrTargets(cfg[pc].n, location) == Partial: + # only partially writes to 's' --> can't sink 's', so this def reads 's' + otherRead = cfg[pc].n + return -1 inc pc of use: - if useInstrTargets(c.g[pc], location): - c.otherRead = c.g[pc].n + if instrTargets(cfg[pc].n, location) != None: + otherRead = cfg[pc].n return -1 inc pc of goto: - pc = pc + c.g[pc].dest + pc = pc + cfg[pc].dest of fork: # every branch must lead to the last read of the location: var variantA = pc + 1 - var variantB = pc + c.g[pc].dest + var variantB = pc + cfg[pc].dest while variantA != variantB: if min(variantA, variantB) < 0: return -1 - if max(variantA, variantB) >= c.g.len or min(variantA, variantB) >= until: + if max(variantA, variantB) >= cfg.len or min(variantA, variantB) >= until: break if variantA < variantB: - variantA = isLastRead(location, c, variantA, min(variantB, until)) + variantA = isLastRead(location, cfg, otherRead, variantA, min(variantB, until)) else: - variantB = isLastRead(location, c, variantB, min(variantA, until)) + variantB = isLastRead(location, cfg, otherRead, variantB, min(variantA, until)) pc = min(variantA, variantB) return pc proc isLastRead(n: PNode; c: var Con): bool = # first we need to search for the instruction that belongs to 'n': - c.otherRead = nil var instr = -1 let m = dfa.skipConvDfa(n) @@ -116,36 +119,37 @@ proc isLastRead(n: PNode; c: var Con): bool = # ensure that we don't find another 'use X' instruction. if instr+1 >= c.g.len: return true - result = isLastRead(n, c, instr+1, int.high) >= 0 + c.otherRead = nil + result = isLastRead(n, c.g, c.otherRead, instr+1, int.high) >= 0 dbg: echo "ugh ", c.otherRead.isNil, " ", result -proc isFirstWrite(location: PNode; c: var Con; pc, until: int): int = +proc isFirstWrite(location: PNode; cfg: ControlFlowGraph; pc, until: int): int = var pc = pc while pc < until: - case c.g[pc].kind + case cfg[pc].kind of def: - if defInstrTargets(c.g[pc], location): + if instrTargets(cfg[pc].n, location) != None: # a definition of 's' before ours makes ours not the first write return -1 inc pc of use: - if useInstrTargets(c.g[pc], location): + if instrTargets(cfg[pc].n, location) != None: return -1 inc pc of goto: - pc = pc + c.g[pc].dest + pc = pc + cfg[pc].dest of fork: # every branch must not contain a def/use of our location: var variantA = pc + 1 - var variantB = pc + c.g[pc].dest + var variantB = pc + cfg[pc].dest while variantA != variantB: if min(variantA, variantB) < 0: return -1 if max(variantA, variantB) > until: break if variantA < variantB: - variantA = isFirstWrite(location, c, variantA, min(variantB, until)) + variantA = isFirstWrite(location, cfg, variantA, min(variantB, until)) else: - variantB = isFirstWrite(location, c, variantB, min(variantA, until)) + variantB = isFirstWrite(location, cfg, variantB, min(variantA, until)) pc = min(variantA, variantB) return pc @@ -165,7 +169,7 @@ proc isFirstWrite(n: PNode; c: var Con): bool = # ensure that we don't find another 'def/use X' instruction. if instr == 0: return true - result = isFirstWrite(n, c, 0, instr) >= 0 + result = isFirstWrite(n, c.g, 0, instr) >= 0 proc initialized(code: ControlFlowGraph; pc: int, init, uninit: var IntSet; until: int): int = From 37325564689c638ef409c79b8380d7564f173c72 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 28 Jun 2020 14:34:34 -0700 Subject: [PATCH 0315/3768] fix `./koch tests` following #14823 (#14845) --- testament/testament.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/testament/testament.nim b/testament/testament.nim index 58c4e5196..83446336c 100644 --- a/testament/testament.nim +++ b/testament/testament.nim @@ -693,7 +693,8 @@ proc main() = myself &= " " & quoteShell("--targets:" & targetsStr) myself &= " " & quoteShell("--nim:" & compilerPrefix) - myself &= " --batch:" & testamentData0.batchArg + if testamentData0.batchArg.len > 0: + myself &= " --batch:" & testamentData0.batchArg if skipFrom.len > 0: myself &= " " & quoteShell("--skipFrom:" & skipFrom) From 299ddda5f6eaceab0bde443159bd17bab1cdf2f1 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Mon, 29 Jun 2020 08:49:53 +0200 Subject: [PATCH 0316/3768] Make unreachable code a warning instead of an error (#14816) * Make unreachable code a warning * Adapt test * Trigger CI --- compiler/lineinfos.nim | 6 ++++-- compiler/semstmts.nim | 6 ++---- tests/controlflow/tstatret.nim | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index 6287e21aa..378e06c72 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -49,7 +49,8 @@ type warnEachIdentIsTuple, warnUnsafeSetLen, warnUnsafeDefault, - warnProveInit, warnProveField, warnProveIndex, warnUnreachableElse, + warnProveInit, warnProveField, warnProveIndex, + warnUnreachableElse, warnUnreachableCode, warnStaticIndexCheck, warnGcUnsafe, warnGcUnsafe2, warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed, warnInconsistentSpacing, warnCaseTransition, warnCycleCreated, @@ -110,6 +111,7 @@ const warnProveField: "cannot prove that field '$1' is accessible", warnProveIndex: "cannot prove index '$1' is valid", warnUnreachableElse: "unreachable else, all cases are already covered", + warnUnreachableCode: "unreachable code after 'return' statement or '{.noReturn.}' proc", warnStaticIndexCheck: "$1", warnGcUnsafe: "not GC-safe: '$1'", warnGcUnsafe2: "$1", @@ -169,7 +171,7 @@ const "UnsafeCode", "UnusedImport", "InheritFromException", "EachIdentIsTuple", "UnsafeSetLen", "UnsafeDefault", - "ProveInit", "ProveField", "ProveIndex", "UnreachableElse", + "ProveInit", "ProveField", "ProveIndex", "UnreachableElse", "UnreachableCode", "IndexCheck", "GcUnsafe", "GcUnsafe2", "Uninit", "GcMem", "Destructor", "LockLevel", "ResultShadowed", "Spacing", "CaseTransition", "CycleCreated", diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 5875a3b68..47a5130af 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -2269,10 +2269,8 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode = sfNoReturn in n[i][0].sym.flags: for j in i + 1.. Date: Mon, 29 Jun 2020 08:50:37 +0200 Subject: [PATCH 0317/3768] SSL_CTX_load_verify_locations parameters are reversed (#14815) [backport] --- lib/pure/net.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index c1896dc1c..c47d9e8a6 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -614,7 +614,7 @@ when defineSsl: if verifyMode != CVerifyNone: # Use the caDir and caFile parameters if set if caDir != "" or caFile != "": - if newCTX.SSL_CTX_load_verify_locations(caDir, caFile) != 0: + if newCTX.SSL_CTX_load_verify_locations(caFile, caDir) != 0: raise newException(IOError, "Failed to load SSL/TLS CA certificate(s).") else: From 5d5df4a39413b76d6131cb3d85e606e255d54b22 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Mon, 29 Jun 2020 04:33:07 -0300 Subject: [PATCH 0318/3768] Clean out Deprecated proc (#14797) * Remove and/or clean out Deprecated 'add' proc for floats * Update a test --- lib/system/strmantle.nim | 8 -------- tests/concepts/t3330.nim | 21 ++++++--------------- tests/system/tostring.nim | 4 ++-- 3 files changed, 8 insertions(+), 25 deletions(-) diff --git a/lib/system/strmantle.nim b/lib/system/strmantle.nim index cb26833ac..43a769b5f 100644 --- a/lib/system/strmantle.nim +++ b/lib/system/strmantle.nim @@ -66,10 +66,6 @@ proc addInt*(result: var string; x: int64) = for j in 0..i div 2 - 1: swap(result[base+j], result[base+i-j-1]) -proc add*(result: var string; x: int64) {.deprecated: - "Deprecated since v0.20, use 'addInt'".} = - addInt(result, x) - proc nimIntToStr(x: int): string {.compilerRtl.} = result = newStringOfCap(sizeof(x)*4) result.addInt x @@ -98,10 +94,6 @@ proc addFloat*(result: var string; x: float) = let n = writeFloatToBuffer(buffer, x) result.addCstringN(cstring(buffer[0].addr), n) -proc add*(result: var string; x: float) {.deprecated: - "Deprecated since v0.20, use 'addFloat'".} = - addFloat(result, x) - proc nimFloatToStr(f: float): string {.compilerproc.} = result = newStringOfCap(8) result.addFloat f diff --git a/tests/concepts/t3330.nim b/tests/concepts/t3330.nim index 6faa79c9d..b92af5485 100644 --- a/tests/concepts/t3330.nim +++ b/tests/concepts/t3330.nim @@ -1,21 +1,13 @@ discard """ errormsg: "type mismatch: got " nimout: ''' -t3330.nim(78, 4) Error: type mismatch: got +t3330.nim(70, 4) Error: type mismatch: got but expected one of: proc test(foo: Foo[int]) first type mismatch at position: 1 required type for foo: Foo[int] but expression 'bar' is of type: Bar[system.int] -t3330.nim(63, 8) Hint: Non-matching candidates for add(k, string, T) -proc add(result: var string; x: float) - first type mismatch at position: 1 - required type for result: var string - but expression 'k' is of type: Alias -proc add(result: var string; x: int64) - first type mismatch at position: 1 - required type for result: var string - but expression 'k' is of type: Alias +t3330.nim(55, 8) Hint: Non-matching candidates for add(k, string, T) proc add(x: var string; y: char) first type mismatch at position: 1 required type for x: var string @@ -37,10 +29,10 @@ proc add[T](x: var seq[T]; y: sink T) required type for x: var seq[T] but expression 'k' is of type: Alias -t3330.nim(63, 8) template/generic instantiation of `add` from here -t3330.nim(70, 6) Foo: 'bar.value' cannot be assigned to -t3330.nim(63, 8) template/generic instantiation of `add` from here -t3330.nim(71, 6) Foo: 'bar.x' cannot be assigned to +t3330.nim(55, 8) template/generic instantiation of `add` from here +t3330.nim(62, 6) Foo: 'bar.value' cannot be assigned to +t3330.nim(55, 8) template/generic instantiation of `add` from here +t3330.nim(63, 6) Foo: 'bar.x' cannot be assigned to expression: test(bar)''' """ @@ -76,4 +68,3 @@ proc test(foo: Foo[int]) = var bar = Bar[int]() bar.test() - diff --git a/tests/system/tostring.nim b/tests/system/tostring.nim index ea4a44417..4ff363075 100644 --- a/tests/system/tostring.nim +++ b/tests/system/tostring.nim @@ -83,9 +83,9 @@ proc stringCompare() = doAssert b == "bee" b.add g doAssert b == "bee" - c.add 123.456 + c.addFloat 123.456 doAssert c == "123.456" - d.add 123456 + d.addInt 123456 doAssert d == "123456" doAssert e == "" From 1b41c3122be1fa2628d814893a4f3a1196b762fc Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 29 Jun 2020 00:34:05 -0700 Subject: [PATCH 0319/3768] fix #13432 typetraits.$: $(int,) is now (int,); $tuple[] is now tuple[] (#14799) * typetraits.$: $(int,) is now (int,); $tuple[] is now tuple[] * changelog --- changelog.md | 1 + compiler/types.nim | 3 ++- lib/pure/typetraits.nim | 21 --------------------- tests/metatype/ttypetraits.nim | 31 +++++++++++++++++++++++++++++++ 4 files changed, 34 insertions(+), 22 deletions(-) diff --git a/changelog.md b/changelog.md index 54996ea34..d5f3c8aa9 100644 --- a/changelog.md +++ b/changelog.md @@ -115,6 +115,7 @@ - `macros.newLit` now preserves named vs unnamed tuples; use `-d:nimHasWorkaround14720` to keep old behavior - Add `random.gauss`, that uses the ratio of uniforms method of sampling from a Gaussian distribution. - Add `typetraits.elementType` to get element type of an iterable. +- `typetraits.$`: `$(int,)` is now `"(int,)"`; `$tuple[]` is now `"tuple[]"` ## Language changes - In the newruntime it is now allowed to assign to the discriminator field diff --git a/compiler/types.nim b/compiler/types.nim index b8f58c950..0be0b3eed 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -634,13 +634,14 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = if i < t.n.len - 1: result.add(", ") result.add(']') elif t.len == 0: - result = "tuple" + result = "tuple[]" else: if prefer == preferTypeName: result = "(" else: result = "tuple of (" for i in 0.. Date: Mon, 29 Jun 2020 10:31:25 +0200 Subject: [PATCH 0320/3768] New freebsd platforms (#14801) * add riscv as riscv64 * add all platforms supported by FreeBSD --- compiler/installer.ini | 2 +- tools/niminst/buildsh.nimf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/installer.ini b/compiler/installer.ini index c59b9a2ea..543a3befb 100644 --- a/compiler/installer.ini +++ b/compiler/installer.ini @@ -9,7 +9,7 @@ Platforms: """ linux: i386;hppa;ia64;alpha;amd64;powerpc64;arm;sparc;sparc64;m68k;mips;mipsel;mips64;mips64el;powerpc;powerpc64el;arm64;riscv64 macosx: i386;amd64;powerpc64 solaris: i386;amd64;sparc;sparc64 - freebsd: i386;amd64;powerpc64;arm;arm64 + freebsd: i386;amd64;powerpc64;arm;arm64;riscv64;sparc64;mips;mipsel;mips64;mips64el;powerpc netbsd: i386;amd64 openbsd: i386;amd64;arm;arm64 dragonfly: i386;amd64 diff --git a/tools/niminst/buildsh.nimf b/tools/niminst/buildsh.nimf index 537a8a01d..5d532a0fd 100644 --- a/tools/niminst/buildsh.nimf +++ b/tools/niminst/buildsh.nimf @@ -185,7 +185,7 @@ case $ucpu in mycpu="arm64" ;; *arm*|*armv6l*|*armv71* ) mycpu="arm" ;; - *riscv64* ) + *riscv64|riscv* ) mycpu="riscv64" ;; *) echo 2>&1 "Error: unknown processor: $ucpu" From 74d1f2501023805dadca75e402e70ef73a1d5bf7 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Tue, 30 Jun 2020 05:01:25 -0300 Subject: [PATCH 0321/3768] Clean out oldast (#14837) * Clean out old Deprecated CLI switch * Update to remove --oldast CLI option --- changelog.md | 1 + compiler/commands.nim | 2 -- compiler/options.nim | 1 - compiler/semstmts.nim | 28 ++++++++++++---------------- compiler/suggest.nim | 5 +---- config/nim.cfg | 1 - doc/advopt.txt | 1 - 7 files changed, 14 insertions(+), 25 deletions(-) diff --git a/changelog.md b/changelog.md index d5f3c8aa9..2d9a78d3b 100644 --- a/changelog.md +++ b/changelog.md @@ -220,6 +220,7 @@ proc mydiv(a, b): int {.raises: [].} = See [docgen](docgen.html#introduction-quick-start) for details. - Removed the `--oldNewlines` switch. - Removed the `--laxStrings` switch for mutating the internal zero terminator on strings. +- Removed the `--oldast` switch. - `$getType(untyped)` is now "untyped" instead of "expr", `$getType(typed)` is now "typed" instead of "stmt" diff --git a/compiler/commands.nim b/compiler/commands.nim index fc559d68c..95df45ebe 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -320,7 +320,6 @@ proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool of "patterns", "trmacros": result = contains(conf.options, optTrMacros) of "excessivestacktrace": result = contains(conf.globalOptions, optExcessiveStackTrace) of "nilseqs": result = contains(conf.options, optNilSeqs) - of "oldast": result = contains(conf.options, optOldAst) else: invalidCmdLineOption(conf, passCmd1, switch, info) proc processPath(conf: ConfigRef; path: string, info: TLineInfo, @@ -583,7 +582,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; undefSymbol(conf.symbols, "hotcodereloading") undefSymbol(conf.symbols, "useNimRtl") of "nilseqs": processOnOffSwitch(conf, {optNilSeqs}, arg, pass, info) - of "oldast": processOnOffSwitch(conf, {optOldAst}, arg, pass, info) of "checks", "x": processOnOffSwitch(conf, ChecksOptions, arg, pass, info) of "floatchecks": processOnOffSwitch(conf, {optNaNCheck, optInfCheck}, arg, pass, info) diff --git a/compiler/options.nim b/compiler/options.nim index 92c664587..abec038ed 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -40,7 +40,6 @@ type # please make sure we have under 32 options optTrMacros, # en/disable pattern matching optMemTracker, optNilSeqs, - optOldAst, optSinkInference # 'sink T' inference diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 47a5130af..a2515357b 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -586,23 +586,19 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = b.add a[^2] b.add copyTree(def) addToVarSection(c, result, n, b) - if optOldAst in c.config.options: - if def.kind != nkEmpty: - v.ast = def + # this is needed for the evaluation pass, guard checking + # and custom pragmas: + var ast = newNodeI(nkIdentDefs, a.info) + if a[j].kind == nkPragmaExpr: + var p = newNodeI(nkPragmaExpr, a.info) + p.add newSymNode(v) + p.add a[j][1].copyTree + ast.add p else: - # this is needed for the evaluation pass, guard checking - # and custom pragmas: - var ast = newNodeI(nkIdentDefs, a.info) - if a[j].kind == nkPragmaExpr: - var p = newNodeI(nkPragmaExpr, a.info) - p.add newSymNode(v) - p.add a[j][1].copyTree - ast.add p - else: - ast.add newSymNode(v) - ast.add a[^2].copyTree - ast.add def - v.ast = ast + ast.add newSymNode(v) + ast.add a[^2].copyTree + ast.add def + v.ast = ast else: if def.kind in {nkPar, nkTupleConstr}: v.ast = def[j] # bug #7663, for 'nim check' this can be a non-tuple: diff --git a/compiler/suggest.nim b/compiler/suggest.nim index 58b54cc03..a9f248fb9 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -510,10 +510,7 @@ proc extractPragma(s: PSym): PNode = proc warnAboutDeprecated(conf: ConfigRef; info: TLineInfo; s: PSym) = var pragmaNode: PNode - if optOldAst in conf.options and s.kind in {skVar, skLet}: - pragmaNode = nil - else: - pragmaNode = if s.kind == skEnumField: extractPragma(s.owner) else: extractPragma(s) + pragmaNode = if s.kind == skEnumField: extractPragma(s.owner) else: extractPragma(s) let name = if s.kind == skEnumField and sfDeprecated notin s.flags: "enum '" & s.owner.name.s & "' which contains field '" & s.name.s & "'" else: s.name.s diff --git a/config/nim.cfg b/config/nim.cfg index 206f80413..c888fcf89 100644 --- a/config/nim.cfg +++ b/config/nim.cfg @@ -314,7 +314,6 @@ tcc.options.always = "-w" @if nimv019: --multimethods:on - --oldAst:on --define:nimOldCaseObjects --define:nimOldShiftRight @end diff --git a/doc/advopt.txt b/doc/advopt.txt index 588d4eece..f5b8c9ec9 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -105,7 +105,6 @@ Advanced options: backwards compatibility --seqsv2:on|off use the new string/seq implementation based on destructors - --oldast:on|off use old AST for backwards compatibility --skipCfg:on|off do not read the nim installation's configuration file --skipUserCfg:on|off do not read the user's configuration file --skipParentCfg:on|off do not read the parent dirs' configuration files From 04deb6c9421d0a11cedfc7e8c52ae3b30e7bf1db Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Tue, 30 Jun 2020 05:08:16 -0300 Subject: [PATCH 0322/3768] Clean out dom (#14855) * Clean out dom * Clean out dom --- changelog.md | 2 +- lib/js/dom.nim | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/changelog.md b/changelog.md index 2d9a78d3b..b983478d2 100644 --- a/changelog.md +++ b/changelog.md @@ -183,7 +183,7 @@ proc mydiv(a, b): int {.raises: [].} = - Added the `thiscall` calling convention as specified by Microsoft, mostly for hooking purpose - Deprecated `{.unroll.}` pragma, was ignored by the compiler anyways, was a nop. - +- Remove `dom.releaseEvents` and `dom.captureEvents`, was deprecated. ## Compiler changes diff --git a/lib/js/dom.nim b/lib/js/dom.nim index 262c0daad..09e45c04d 100644 --- a/lib/js/dom.nim +++ b/lib/js/dom.nim @@ -1415,7 +1415,6 @@ proc removeEventListener*(et: EventTarget; ev: cstring; cb: proc(ev: Event)) proc alert*(w: Window, msg: cstring) proc back*(w: Window) proc blur*(w: Window) -proc captureEvents*(w: Window, eventMask: int) {.deprecated.} proc clearInterval*(w: Window, interval: ref Interval) proc clearTimeout*(w: Window, timeout: ref TimeOut) proc close*(w: Window) @@ -1435,7 +1434,6 @@ proc open*(w: Window, uri, windowname: cstring, properties: cstring = nil): Window proc print*(w: Window) proc prompt*(w: Window, text, default: cstring): cstring -proc releaseEvents*(w: Window, eventMask: int) {.deprecated.} proc resizeBy*(w: Window, x, y: int) proc resizeTo*(w: Window, x, y: int) proc routeEvent*(w: Window, event: Event) @@ -1469,7 +1467,6 @@ proc querySelector*(n: Node, selectors: cstring): Element proc querySelectorAll*(n: Node, selectors: cstring): seq[Element] # Document "methods" -proc captureEvents*(d: Document, eventMask: int) {.deprecated.} proc createAttribute*(d: Document, identifier: cstring): Node proc getElementsByName*(d: Document, name: cstring): seq[Element] proc getElementsByTagName*(d: Document, name: cstring): seq[Element] @@ -1477,7 +1474,6 @@ proc getElementsByClassName*(d: Document, name: cstring): seq[Element] proc getSelection*(d: Document): Selection proc handleEvent*(d: Document, event: Event) proc open*(d: Document) -proc releaseEvents*(d: Document, eventMask: int) {.deprecated.} proc routeEvent*(d: Document, event: Event) proc write*(d: Document, text: cstring) proc writeln*(d: Document, text: cstring) From 89a15e417df52454aa5c40ba058c9b19425dd184 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 30 Jun 2020 05:21:30 -0700 Subject: [PATCH 0323/3768] CI openbsd: 3x batching via NIM_TESTAMENT_BATCH ; overall CI finishes in 21m instead of 34m (#14851) * CI openbsd: 2x batching via NIM_TESTAMENT_BATCH * auto-generate .builds/openbsd_x.yml to avoid code duplication * 3x batching --- .builds/{openbsd.yml => openbsd_0.yml} | 3 ++ .builds/openbsd_1.yml | 34 ++++++++++++++ .builds/openbsd_2.yml | 34 ++++++++++++++ testament/specs.nim | 3 ++ tests/dll/server.nim | 1 + tools/ci_generate.nim | 63 ++++++++++++++++++++++++++ 6 files changed, 138 insertions(+) rename .builds/{openbsd.yml => openbsd_0.yml} (85%) create mode 100644 .builds/openbsd_1.yml create mode 100644 .builds/openbsd_2.yml create mode 100644 tools/ci_generate.nim diff --git a/.builds/openbsd.yml b/.builds/openbsd_0.yml similarity index 85% rename from .builds/openbsd.yml rename to .builds/openbsd_0.yml index c4db2f621..278cb3de9 100644 --- a/.builds/openbsd.yml +++ b/.builds/openbsd_0.yml @@ -1,3 +1,5 @@ +## do not edit directly; auto-generated by `nim r tools/ci_generate.nim` + image: openbsd/latest packages: - gmake @@ -11,6 +13,7 @@ packages: sources: - https://github.com/nim-lang/Nim environment: + NIM_TESTAMENT_BATCH: "0_3" CC: /usr/bin/clang tasks: - setup: | diff --git a/.builds/openbsd_1.yml b/.builds/openbsd_1.yml new file mode 100644 index 000000000..d3f0c8795 --- /dev/null +++ b/.builds/openbsd_1.yml @@ -0,0 +1,34 @@ +## do not edit directly; auto-generated by `nim r tools/ci_generate.nim` + +image: openbsd/latest +packages: +- gmake +- sqlite3 +- node +- boehm-gc +- pcre +- sfml +- sdl2 +- libffi +sources: +- https://github.com/nim-lang/Nim +environment: + NIM_TESTAMENT_BATCH: "1_3" + CC: /usr/bin/clang +tasks: +- setup: | + cd Nim + git clone --depth 1 -q https://github.com/nim-lang/csources.git + gmake -C csources -j $(sysctl -n hw.ncpuonline) + bin/nim c koch + echo 'export PATH=$HOME/Nim/bin:$PATH' >> $HOME/.buildenv +- test: | + cd Nim + if ! ./koch runCI; then + nim c -r tools/ci_testresults.nim + exit 1 + fi +triggers: +- action: email + condition: failure + to: Andreas Rumpf diff --git a/.builds/openbsd_2.yml b/.builds/openbsd_2.yml new file mode 100644 index 000000000..4b63e914d --- /dev/null +++ b/.builds/openbsd_2.yml @@ -0,0 +1,34 @@ +## do not edit directly; auto-generated by `nim r tools/ci_generate.nim` + +image: openbsd/latest +packages: +- gmake +- sqlite3 +- node +- boehm-gc +- pcre +- sfml +- sdl2 +- libffi +sources: +- https://github.com/nim-lang/Nim +environment: + NIM_TESTAMENT_BATCH: "2_3" + CC: /usr/bin/clang +tasks: +- setup: | + cd Nim + git clone --depth 1 -q https://github.com/nim-lang/csources.git + gmake -C csources -j $(sysctl -n hw.ncpuonline) + bin/nim c koch + echo 'export PATH=$HOME/Nim/bin:$PATH' >> $HOME/.buildenv +- test: | + cd Nim + if ! ./koch runCI; then + nim c -r tools/ci_testresults.nim + exit 1 + fi +triggers: +- action: email + condition: failure + to: Andreas Rumpf diff --git a/testament/specs.nim b/testament/specs.nim index 2f2c006d8..acffbf8be 100644 --- a/testament/specs.nim +++ b/testament/specs.nim @@ -84,6 +84,9 @@ type parseErrors*: string # when the spec definition is invalid, this is not empty. unjoinable*: bool unbatchable*: bool + # whether this test can be batchable via `NIM_TESTAMENT_BATCH`; only very + # few tests are not batchable; the ones that are not could be turned batchable + # by making the dependencies explicit useValgrind*: bool timeout*: float # in seconds, fractions possible, # but don't rely on much precision diff --git a/tests/dll/server.nim b/tests/dll/server.nim index 2b7791d4b..dd4606298 100644 --- a/tests/dll/server.nim +++ b/tests/dll/server.nim @@ -1,6 +1,7 @@ discard """ action: compile cmd: "nim $target --debuginfo --hints:on --define:useNimRtl --app:lib $options $file" +batchable: false """ type diff --git a/tools/ci_generate.nim b/tools/ci_generate.nim new file mode 100644 index 000000000..bfed8d88f --- /dev/null +++ b/tools/ci_generate.nim @@ -0,0 +1,63 @@ +##[ +avoid code duplication in CI pipelines. +For now, this is only used for openbsd, but there is a lot of other code +duplication that could be removed. + +## usage +edit this file as needed and then re-generate via: +``` +nim r tools/ci_generate.nim +``` +]## + +import std/strformat + +proc genCIopenbsd(batch: int, num: int): string = + result = fmt""" +## do not edit directly; auto-generated by `nim r tools/ci_generate.nim` + +image: openbsd/latest +packages: +- gmake +- sqlite3 +- node +- boehm-gc +- pcre +- sfml +- sdl2 +- libffi +sources: +- https://github.com/nim-lang/Nim +environment: + NIM_TESTAMENT_BATCH: "{batch}_{num}" + CC: /usr/bin/clang +tasks: +- setup: | + cd Nim + git clone --depth 1 -q https://github.com/nim-lang/csources.git + gmake -C csources -j $(sysctl -n hw.ncpuonline) + bin/nim c koch + echo 'export PATH=$HOME/Nim/bin:$PATH' >> $HOME/.buildenv +- test: | + cd Nim + if ! ./koch runCI; then + nim c -r tools/ci_testresults.nim + exit 1 + fi +triggers: +- action: email + condition: failure + to: Andreas Rumpf +""" + +proc main()= + let num = 3 + # if you reduce this, make sure to remove files that shouldn't be generated, + # or better, do the cleanup logic here eg: `rm .builds/openbsd_*` + for i in 0.. Date: Tue, 30 Jun 2020 21:21:37 +0800 Subject: [PATCH 0324/3768] add debug fmt string like python's (#14808) * add debug format string * remove try except * add changelog --- changelog.md | 2 + lib/pure/strformat.nim | 14 ++++- tests/stdlib/tstrformat.nim | 105 ++++++++++++++++++++++++++++++++++++ 3 files changed, 119 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index b983478d2..5f10c5c19 100644 --- a/changelog.md +++ b/changelog.md @@ -117,6 +117,8 @@ - Add `typetraits.elementType` to get element type of an iterable. - `typetraits.$`: `$(int,)` is now `"(int,)"`; `$tuple[]` is now `"tuple[]"` +- `strformat.fmt` and `strformat.&` support `= specifier`. `fmt"{expr=}"` now expands to `fmt"expr={expr}"`. + ## Language changes - In the newruntime it is now allowed to assign to the discriminator field without restrictions as long as case object doesn't have custom destructor. diff --git a/lib/pure/strformat.nim b/lib/pure/strformat.nim index 5f7ef380d..0ea784a43 100644 --- a/lib/pure/strformat.nim +++ b/lib/pure/strformat.nim @@ -552,8 +552,18 @@ proc strformatImpl(pattern: NimNode; openChar, closeChar: char): NimNode = var subexpr = "" while i < f.len and f[i] != closeChar and f[i] != ':': - subexpr.add f[i] - inc i + if f[i] == '=': + let start = i + inc i + i += f.skipWhitespace(i) + if f[i] == closeChar or f[i] == ':': + result.add newCall(bindSym"add", res, newLit(subexpr & f[start ..< i])) + else: + subexpr.add f[start ..< i] + else: + subexpr.add f[i] + inc i + var x: NimNode try: x = parseExpr(subexpr) diff --git a/tests/stdlib/tstrformat.nim b/tests/stdlib/tstrformat.nim index 22c0cf95d..c65165946 100644 --- a/tests/stdlib/tstrformat.nim +++ b/tests/stdlib/tstrformat.nim @@ -6,6 +6,8 @@ output: '''Received (name: "Foo", species: "Bar")''' # issue #7632 import genericstrformat +import strutils + doAssert works(5) == "formatted 5" doAssert fails0(6) == "formatted 6" @@ -86,6 +88,8 @@ proc formatValue(result: var string; value: (array|seq|openArray); specifier: st doAssert fmt"data1: {data1:8} #" == "data1: [ 1, 10000, 10000000] #" doAssert fmt"data2: {data2:8} =" == "data2: [10000000, 100, 1] =" +doAssert fmt"data1: {data1=:8} #" == "data1: data1=[ 1, 10000, 10000000] #" +doAssert fmt"data2: {data2=:8} =" == "data2: data2=[10000000, 100, 1] =" # custom format Value @@ -103,6 +107,7 @@ proc formatValue[T](result: var string; value: Vec2[T]; specifier: string) = let v1 = Vec2[float32](x:1.0, y: 2.0) let v2 = Vec2[int32](x:1, y: 1337) doAssert fmt"v1: {v1:+08} v2: {v2:>4}" == "v1: [+0000001, +0000002] v2: [ 1, 1337]" +doAssert fmt"v1: {v1=:+08} v2: {v2=:>4}" == "v1: v1=[+0000001, +0000002] v2: v2=[ 1, 1337]" # bug #11012 @@ -126,6 +131,14 @@ doAssert fmt"{pos:3o}" == "100" doAssert fmt"{pos:3x}" == " 40" doAssert fmt"{pos:3X}" == " 40" +doAssert fmt"{pos=:3}" == "pos= 64" +doAssert fmt"{pos=:3b}" == "pos=1000000" +doAssert fmt"{pos=:3d}" == "pos= 64" +doAssert fmt"{pos=:3o}" == "pos=100" +doAssert fmt"{pos=:3x}" == "pos= 40" +doAssert fmt"{pos=:3X}" == "pos= 40" + + let nat: Natural = 64 doAssert fmt"{nat:3}" == " 64" doAssert fmt"{nat:3b}" == "1000000" @@ -134,6 +147,13 @@ doAssert fmt"{nat:3o}" == "100" doAssert fmt"{nat:3x}" == " 40" doAssert fmt"{nat:3X}" == " 40" +doAssert fmt"{nat=:3}" == "nat= 64" +doAssert fmt"{nat=:3b}" == "nat=1000000" +doAssert fmt"{nat=:3d}" == "nat= 64" +doAssert fmt"{nat=:3o}" == "nat=100" +doAssert fmt"{nat=:3x}" == "nat= 40" +doAssert fmt"{nat=:3X}" == "nat= 40" + # bug #12612 proc my_proc = const value = "value" @@ -186,6 +206,14 @@ doAssert fmt"{msg}{'\n'}" == "hello\n" doAssert fmt("{msg}\n") == "hello\n" doAssert "{msg}\n".fmt == "hello\n" +doAssert fmt"{msg=}\n" == "msg=hello\\n" + +doAssert &"{msg=}\n" == "msg=hello\n" + +doAssert fmt"{msg=}{'\n'}" == "msg=hello\n" +doAssert fmt("{msg=}\n") == "msg=hello\n" +doAssert "{msg=}\n".fmt == "msg=hello\n" + doAssert &"""{"abc":>4}""" == " abc" doAssert &"""{"abc":<4}""" == "abc " @@ -204,3 +232,80 @@ doAssert fmt"{123.456:<9.4f}" == "123.4560 " doAssert fmt"{123.456:e}" == "1.234560e+02" doAssert fmt"{123.456:>13e}" == " 1.234560e+02" doAssert fmt"{123.456:13e}" == " 1.234560e+02" + + +doAssert &"""{"abc"=:>4}""" == "\"abc\"= abc" +doAssert &"""{"abc"=:<4}""" == "\"abc\"=abc " + +doAssert fmt"{-12345=:08}" == "-12345=-0012345" +doAssert fmt"{-1=:3}" == "-1= -1" +doAssert fmt"{-1=:03}" == "-1=-01" +doAssert fmt"{16=:#X}" == "16=0x10" + +doAssert fmt"{123.456=}" == "123.456=123.456" +doAssert fmt"{123.456=:>9.3f}" == "123.456= 123.456" +doAssert fmt"{123.456=:9.3f}" == "123.456= 123.456" +doAssert fmt"{123.456=:9.4f}" == "123.456= 123.4560" +doAssert fmt"{123.456=:>9.0f}" == "123.456= 123." +doAssert fmt"{123.456=:<9.4f}" == "123.456=123.4560 " + +doAssert fmt"{123.456=:e}" == "123.456=1.234560e+02" +doAssert fmt"{123.456=:>13e}" == "123.456= 1.234560e+02" +doAssert fmt"{123.456=:13e}" == "123.456= 1.234560e+02" + +## tests for debug format string +block: + var name = "hello" + let age = 21 + const hobby = "swim" + doAssert fmt"{age*9 + 16=}" == "age*9 + 16=205" + doAssert &"name: {name =}\nage: { age =: >7}\nhobby: { hobby= : 8}" == + "name: name =hello\nage: age = 21\nhobby: hobby= swim " + doAssert fmt"{age == 12}" == "false" + doAssert fmt"{name.toUpperAscii( ) = }" == "name.toUpperAscii( ) = HELLO" + doAssert fmt"{ toUpperAscii( s = name ) = }" == " toUpperAscii( s = name ) = HELLO" + doAssert fmt"{ strutils.toUpperAscii( s = name ) = }" == " strutils.toUpperAscii( s = name ) = HELLO" + doAssert fmt"{age==12}" == "false" + doAssert fmt"{age!= 12}" == "true" + doAssert fmt"{age <= 12}" == "false" + for i in 1 .. 10: + doAssert fmt"{age.float =: .2f}" == "age.float = 21.00" + doAssert fmt"{age.float() =:.3f}" == "age.float() =21.000" + doAssert fmt"{float age= :.3f}" == "float age= 21.000" + doAssert fmt"{12 == int(`!=`(age, 12))}" == "false" + doAssert fmt"{0==1}" == "false" + + +block: + let x = "hello" + doAssert fmt"{x=}" == "x=" & $x + doAssert fmt"{x =}" == "x =" & $x + + + let y = 3.1415926 + doAssert fmt"{y=:.2f}" == fmt"y={y:.2f}" + doAssert fmt"{y=}" == fmt"y={y}" + doAssert fmt"{y =: <16}" == fmt"y ={y: <16}" + + proc hello(a: string, b: float): int = 12 + template foo(a: string, b: float): int = 18 + + doAssert fmt"{hello(x, y)=}" == "hello(x, y)=12" + doAssert fmt"{hello(x, y) =}" == "hello(x, y) =12" + doAssert fmt"{hello(x, y)= }" == "hello(x, y)= 12" + doAssert fmt"{hello(x, y) = }" == "hello(x, y) = 12" + + doAssert fmt"{x.hello(y)=}" == "x.hello(y)=12" + doAssert fmt"{x.hello(y) =}" == "x.hello(y) =12" + doAssert fmt"{x.hello(y)= }" == "x.hello(y)= 12" + doAssert fmt"{x.hello(y) = }" == "x.hello(y) = 12" + + doAssert fmt"{foo(x, y)=}" == "foo(x, y)=18" + doAssert fmt"{foo(x, y) =}" == "foo(x, y) =18" + doAssert fmt"{foo(x, y)= }" == "foo(x, y)= 18" + doAssert fmt"{foo(x, y) = }" == "foo(x, y) = 18" + + doAssert fmt"{x.foo(y)=}" == "x.foo(y)=18" + doAssert fmt"{x.foo(y) =}" == "x.foo(y) =18" + doAssert fmt"{x.foo(y)= }" == "x.foo(y)= 18" + doAssert fmt"{x.foo(y) = }" == "x.foo(y) = 18" From 36fa79a5242d2e39a83083cd66cf3af9f8931f3b Mon Sep 17 00:00:00 2001 From: Ray Imber Date: Tue, 30 Jun 2020 06:23:53 -0700 Subject: [PATCH 0325/3768] Fix asyncdispatch drain behavior (#14820) (#14838) * Fix asyncdispatch drain behavior (#14820) * Changed test to use asyncCheck instead of discard after code review (#14820) * Added some debug statements to help understand what is happening in Azure. * Removed debug statements and increased timeouts by 1 order of magnitude to account for slow Azure VMs Co-authored-by: Ray Imber --- lib/pure/asyncdispatch.nim | 13 +++++++++---- tests/async/t14820.nim | 25 +++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 tests/async/t14820.nim diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index a15442109..a0bc96e72 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -1601,11 +1601,16 @@ else: p.selector.registerEvent(SelectEvent(ev), data) proc drain*(timeout = 500) = - ## Waits for completion events and processes them. Raises ``ValueError`` + ## Waits for completion of **all** events and processes them. Raises ``ValueError`` ## if there are no pending operations. In contrast to ``poll`` this - ## processes as many events as are available. - if runOnce(timeout) or hasPendingOperations(): - while hasPendingOperations() and runOnce(timeout): discard + ## processes as many events as are available until the timeout has elapsed. + var curTimeout = timeout + let start = now() + while hasPendingOperations(): + discard runOnce(curTimeout) + curTimeout -= (now() - start).inMilliseconds.int + if curTimeout < 0: + break proc poll*(timeout = 500) = ## Waits for completion events and processes them. Raises ``ValueError`` diff --git a/tests/async/t14820.nim b/tests/async/t14820.nim new file mode 100644 index 000000000..359884468 --- /dev/null +++ b/tests/async/t14820.nim @@ -0,0 +1,25 @@ +discard """ +output: ''' +iteration: 1 +iteration: 2 +iteration: 3 +iteration: 4 +async done +iteration: 5 +''' +""" + +import asyncdispatch, times + +var done = false +proc somethingAsync() {.async.} = + yield sleepAsync 5000 + echo "async done" + done = true + +asyncCheck somethingAsync() +var count = 0 +while not done: + count += 1 + drain 1000 + echo "iteration: ", count From 56b3d422b014b42c6367fa56916cfd9851884506 Mon Sep 17 00:00:00 2001 From: alaviss Date: Tue, 30 Jun 2020 13:25:53 +0000 Subject: [PATCH 0326/3768] typetraits: features and fixes (#14791) * typetraits: add support for nnkTypeOfExpr * typetraits: don't wrap typedesc symbols in StaticParam * typetraits: add nested generics support to genericParams * typetraits: make genericParams understand array[I, T] whackiness Also moved tests to ttypetraits * typetraits: clarify comment on genericParams --- lib/pure/typetraits.nim | 42 +++++++++++++++++++++++++++++++--- tests/metatype/ttypetraits.nim | 17 ++++++++++++++ 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/lib/pure/typetraits.nim b/lib/pure/typetraits.nim index c289f6c84..0e23077ac 100644 --- a/lib/pure/typetraits.nim +++ b/lib/pure/typetraits.nim @@ -121,6 +121,9 @@ macro genericParamsImpl(T: typedesc): untyped = of nnkTypeDef: impl = impl[2] continue + of nnkTypeOfExpr: + impl = getTypeInst(impl[0]) + continue of nnkBracketExpr: for i in 1.. Date: Tue, 30 Jun 2020 11:29:53 -0300 Subject: [PATCH 0327/3768] Removed asyncdispatch.newAsyncNativeSocket, was deprecated since 0.18 (#14854) Co-authored-by: Andreas Rumpf --- changelog.md | 1 + lib/pure/asyncdispatch.nim | 10 ---------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/changelog.md b/changelog.md index 5f10c5c19..35c701a8a 100644 --- a/changelog.md +++ b/changelog.md @@ -185,6 +185,7 @@ proc mydiv(a, b): int {.raises: [].} = - Added the `thiscall` calling convention as specified by Microsoft, mostly for hooking purpose - Deprecated `{.unroll.}` pragma, was ignored by the compiler anyways, was a nop. +- Removed `asyncdispatch.newAsyncNativeSocket`, was deprecated since `0.18`. - Remove `dom.releaseEvents` and `dom.captureEvents`, was deprecated. ## Compiler changes diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index a0bc96e72..2ade9065f 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -1640,16 +1640,6 @@ proc createAsyncNativeSocket*(domain: Domain = Domain.AF_INET, inheritable = defined(nimInheritHandles)): AsyncFD = createAsyncNativeSocketImpl(domain, sockType, protocol, inheritable) -proc newAsyncNativeSocket*(domain: cint, sockType: cint, - protocol: cint): AsyncFD {.deprecated: "use createAsyncNativeSocket instead".} = - createAsyncNativeSocketImpl(domain, sockType, protocol) - -proc newAsyncNativeSocket*(domain: Domain = Domain.AF_INET, - sockType: SockType = SOCK_STREAM, - protocol: Protocol = IPPROTO_TCP): AsyncFD - {.deprecated: "use createAsyncNativeSocket instead".} = - createAsyncNativeSocketImpl(domain, sockType, protocol) - when defined(windows) or defined(nimdoc): proc bindToDomain(handle: SocketHandle, domain: Domain) = # Extracted into a separate proc, because connect() on Windows requires From 05384efec53256d8917a80d1117d8f822a8a2f55 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Tue, 30 Jun 2020 14:09:13 -0300 Subject: [PATCH 0328/3768] Clean out sharedtables (#14858) Co-authored-by: Andreas Rumpf --- changelog.md | 1 + lib/pure/collections/sharedtables.nim | 10 ---------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/changelog.md b/changelog.md index 35c701a8a..f32d6f19f 100644 --- a/changelog.md +++ b/changelog.md @@ -185,6 +185,7 @@ proc mydiv(a, b): int {.raises: [].} = - Added the `thiscall` calling convention as specified by Microsoft, mostly for hooking purpose - Deprecated `{.unroll.}` pragma, was ignored by the compiler anyways, was a nop. +- Remove `sharedtables.initSharedTable`, was deprecated and produces undefined behavior. - Removed `asyncdispatch.newAsyncNativeSocket`, was deprecated since `0.18`. - Remove `dom.releaseEvents` and `dom.captureEvents`, was deprecated. diff --git a/lib/pure/collections/sharedtables.nim b/lib/pure/collections/sharedtables.nim index 23b653c82..abef89507 100644 --- a/lib/pure/collections/sharedtables.nim +++ b/lib/pure/collections/sharedtables.nim @@ -225,13 +225,3 @@ proc init*[A, B](t: var SharedTable[A, B], initialSize = 64) = proc deinitSharedTable*[A, B](t: var SharedTable[A, B]) = deallocShared(t.data) deinitLock t.lock - -proc initSharedTable*[A, B](initialSize = 64): SharedTable[A, B] {.deprecated: - "use 'init' instead".} = - ## This is not posix compliant, may introduce undefined behavior. - assert isPowerOfTwo(initialSize) - result.counter = 0 - result.dataLen = initialSize - result.data = cast[KeyValuePairSeq[A, B]](allocShared0( - sizeof(KeyValuePair[A, B]) * initialSize)) - initLock result.lock From 2867a33ebcaddccdb52b101d0ea5f75f1d4bd66f Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 1 Jul 2020 00:26:23 -0700 Subject: [PATCH 0329/3768] fix #14846; add macros.extractDocCommentsAndRunnables (#14849) * fix #14846; add macros.extractDocCommentsAndRunnables * fixup * update tests * address comment --- changelog.md | 1 + lib/core/macros.nim | 36 ++++++++++++++++++++ lib/pure/asyncmacro.nim | 6 ++-- nimdoc/testproject/expected/testproject.html | 29 ++++++++++++++++ nimdoc/testproject/expected/testproject.idx | 3 ++ nimdoc/testproject/expected/theindex.html | 12 +++++++ nimdoc/testproject/testproject.nim | 14 ++++++++ 7 files changed, 98 insertions(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index f32d6f19f..287bce3da 100644 --- a/changelog.md +++ b/changelog.md @@ -116,6 +116,7 @@ - Add `random.gauss`, that uses the ratio of uniforms method of sampling from a Gaussian distribution. - Add `typetraits.elementType` to get element type of an iterable. - `typetraits.$`: `$(int,)` is now `"(int,)"`; `$tuple[]` is now `"tuple[]"` +- add `macros.extractDocCommentsAndRunnables` helper - `strformat.fmt` and `strformat.&` support `= specifier`. `fmt"{expr=}"` now expands to `fmt"expr={expr}"`. diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 5be3a2cce..838707280 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -1692,3 +1692,39 @@ when defined(nimMacrosSizealignof): proc isExported*(n: NimNode): bool {.noSideEffect.} = ## Returns whether the symbol is exported or not. + +proc extractDocCommentsAndRunnables*(n: NimNode): NimNode = + ## returns a `nnkStmtList` containing the top-level doc comments and + ## runnableExamples in `a`, stopping at the first child that is neither. + ## Example: + ## + ## .. code-block:: nim + ## import macros + ## macro transf(a): untyped = + ## result = quote do: + ## proc fun2*() = discard + ## let header = extractDocCommentsAndRunnables(a.body) + ## # correct usage: rest is appended + ## result.body = header + ## result.body.add quote do: discard # just an example + ## # incorrect usage: nesting inside a nnkStmtList: + ## # result.body = quote do: (`header`; discard) + ## + ## proc fun*() {.transf.} = + ## ## first comment + ## runnableExamples: discard + ## runnableExamples: discard + ## ## last comment + ## discard # first statement after doc comments + runnableExamples + ## ## not docgen'd + + result = newStmtList() + for ni in n: + case ni.kind + of nnkCommentStmt: + result.add ni + of nnkCall: + if ni[0].kind == nnkIdent and ni[0].strVal == "runnableExamples": + result.add ni + else: break + else: break diff --git a/lib/pure/asyncmacro.nim b/lib/pure/asyncmacro.nim index 621a4b00c..219ef6c67 100644 --- a/lib/pure/asyncmacro.nim +++ b/lib/pure/asyncmacro.nim @@ -180,8 +180,7 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} = # Extract the documentation comment from the original procedure declaration. # Note that we're not removing it from the body in order not to make this # transformation even more complex. - if prc.body.len > 1 and prc.body[0].kind == nnkCommentStmt: - outerProcBody.add(prc.body[0]) + let body2 = extractDocCommentsAndRunnables(prc.body) # -> var retFuture = newFuture[T]() var retFutureSym = genSym(nskVar, "retFuture") @@ -276,9 +275,10 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} = (cast[type(f)](internalTmpFuture)).read() if procBody.kind != nnkEmpty: - result.body = quote: + body2.add quote do: `awaitDefinition` `outerProcBody` + result.body = body2 #echo(treeRepr(result)) #if prcName == "recvLineInto": diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html index a4f8876fd..648ecb629 100644 --- a/nimdoc/testproject/expected/testproject.html +++ b/nimdoc/testproject/expected/testproject.html @@ -200,6 +200,12 @@ function main() { title="low2[T: Ordinal | enum | range](x: T): T">low2
    • tripleStrLitTest
    • +
    • asyncFun1
    • +
    • asyncFun2
    • +
    • asyncFun3
    @@ -655,6 +661,29 @@ at indent 0 discard # should be in
    + + +
    proc asyncFun1(): Future[int] {...}{.raises: [Exception, ValueError], tags: [RootEffect].}
    +
    + +ok1 + +
    + +
    proc asyncFun2(): owned(Future[void]) {...}{.raises: [Exception], tags: [RootEffect].}
    +
    + + + +
    + +
    proc asyncFun3(): owned(Future[void]) {...}{.raises: [Exception], tags: [RootEffect].}
    +
    + + +

    Example:

    +
    discard
    ok1 +
    diff --git a/nimdoc/testproject/expected/testproject.idx b/nimdoc/testproject/expected/testproject.idx index 6d0bc6c5e..0b75b29d2 100644 --- a/nimdoc/testproject/expected/testproject.idx +++ b/nimdoc/testproject/expected/testproject.idx @@ -50,4 +50,7 @@ foo testproject.html#foo.t,SomeType,SomeType testproject: foo(a, b: SomeType) myfn testproject.html#myfn.t testproject: myfn() z14 testproject.html#z14.t testproject: z14() z15 testproject.html#z15.t testproject: z15() +asyncFun1 testproject.html#asyncFun1 testproject: asyncFun1(): Future[int] +asyncFun2 testproject.html#asyncFun2 testproject: asyncFun2(): owned(Future[void]) +asyncFun3 testproject.html#asyncFun3 testproject: asyncFun3(): owned(Future[void]) testNimDocTrailingExample testproject.html#testNimDocTrailingExample.t testproject: testNimDocTrailingExample() diff --git a/nimdoc/testproject/expected/theindex.html b/nimdoc/testproject/expected/theindex.html index 8d34506a6..0b835c409 100644 --- a/nimdoc/testproject/expected/theindex.html +++ b/nimdoc/testproject/expected/theindex.html @@ -85,6 +85,18 @@ function main() {
  • utils: aEnum(): untyped
  • +
    asyncFun1:
    +
    asyncFun2:
    +
    asyncFun3:
    aVariable:
    • testproject: aVariable
    • diff --git a/nimdoc/testproject/testproject.nim b/nimdoc/testproject/testproject.nim index 5282c6f77..25cdf39a4 100644 --- a/nimdoc/testproject/testproject.nim +++ b/nimdoc/testproject/testproject.nim @@ -343,6 +343,20 @@ when true: # issue #14473 toSeq([1,2]) echo doit() # using doAssert or similar to avoid echo would "hide" the original bug +when true: # issue #14846 + import asyncdispatch + proc asyncFun1*(): Future[int] {.async.} = + ## ok1 + result = 1 + proc asyncFun2*() {.async.} = discard + proc asyncFun3*() {.async.} = + runnableExamples: + discard + ## ok1 + discard + ## should be out + discard + when true: template testNimDocTrailingExample*() = # this must be last entry in this file, it checks against a bug (that got fixed) From 1edb9a6178d868e34e76faf9e2a05afbc1431cb0 Mon Sep 17 00:00:00 2001 From: Miran Date: Wed, 1 Jul 2020 11:40:13 +0200 Subject: [PATCH 0330/3768] fix #14401, trailing comma confuses nimpretty (#14867) --- compiler/layouter.nim | 3 +++ nimpretty/tests/exhaustive.nim | 21 +++++++++++++++++++++ nimpretty/tests/expected/exhaustive.nim | 23 ++++++++++++++++++++++- 3 files changed, 46 insertions(+), 1 deletion(-) diff --git a/compiler/layouter.nim b/compiler/layouter.nim index 2477458f1..f5d3084dc 100644 --- a/compiler/layouter.nim +++ b/compiler/layouter.nim @@ -452,6 +452,9 @@ proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = if tok.tokType in openPars and tok.indent > em.indentStack[^1]: while em.indentStack[^1] < tok.indent: em.indentStack.add(em.indentStack[^1] + em.indWidth) + while em.indentStack[^1] > tok.indent: + discard em.indentStack.pop() + # aka: we are in an expression context: let alignment = max(tok.indent - em.indentStack[^1], 0) em.indentLevel = alignment + em.indentStack.high * em.indWidth diff --git a/nimpretty/tests/exhaustive.nim b/nimpretty/tests/exhaustive.nim index d61820f7a..2ba885d9a 100644 --- a/nimpretty/tests/exhaustive.nim +++ b/nimpretty/tests/exhaustive.nim @@ -822,3 +822,24 @@ proc f = # doesn't break the code, but leaving indentation as is would be nice. let x = if true: callingProcWhatever() else: callingADifferentProc() + + +type + EventKind = enum + Stop, StopSuccess, StopError, + SymbolChange, TextChange, + + SpinnyEvent = tuple + kind: EventKind + payload: string + + +type + EventKind2 = enum + Stop2, StopSuccess2, StopError2, + SymbolChange2, TextChange2, + +type + SpinnyEvent2 = tuple + kind: EventKind + payload: string diff --git a/nimpretty/tests/expected/exhaustive.nim b/nimpretty/tests/expected/exhaustive.nim index e120cf019..cfe9a43fa 100644 --- a/nimpretty/tests/expected/exhaustive.nim +++ b/nimpretty/tests/expected/exhaustive.nim @@ -812,7 +812,7 @@ let foo = if cond: let a = [[aaadsfas, bbb], - [ccc, ddd]] + [ccc, ddd]] let b = [ [aaa, bbb], @@ -835,3 +835,24 @@ proc f = # doesn't break the code, but leaving indentation as is would be nice. let x = if true: callingProcWhatever() else: callingADifferentProc() + + +type + EventKind = enum + Stop, StopSuccess, StopError, + SymbolChange, TextChange, + + SpinnyEvent = tuple + kind: EventKind + payload: string + + +type + EventKind2 = enum + Stop2, StopSuccess2, StopError2, + SymbolChange2, TextChange2, + +type + SpinnyEvent2 = tuple + kind: EventKind + payload: string From 1440e70c62e7dc2e2a2062c1291f65c12055818a Mon Sep 17 00:00:00 2001 From: Clyybber Date: Wed, 1 Jul 2020 11:41:03 +0200 Subject: [PATCH 0331/3768] allow packed union (#14868) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Arne Döring --- compiler/sizealignoffsetimpl.nim | 16 +++++++--------- tests/misc/tsizeof.nim | 10 ++++++++++ 2 files changed, 17 insertions(+), 9 deletions(-) diff --git a/compiler/sizealignoffsetimpl.nim b/compiler/sizealignoffsetimpl.nim index 4d73923db..baa2c40f9 100644 --- a/compiler/sizealignoffsetimpl.nim +++ b/compiler/sizealignoffsetimpl.nim @@ -164,7 +164,7 @@ proc computeObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, packed: bool, a accum.maxAlign = szUnknownSize accum.offset = szUnknownSize -proc computeUnionObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode; accum: var OffsetAccum) = +proc computeUnionObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode; packed: bool; accum: var OffsetAccum) = ## ``accum.offset`` will the offset from the larget member of the union. case n.kind of nkRecCase: @@ -175,7 +175,7 @@ proc computeUnionObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode; accum: var let accumRoot = accum # copy, because each branch should start af the same offset for i, child in n.sons: var branchAccum = accumRoot - computeUnionObjectOffsetsFoldFunction(conf, child, branchAccum) + computeUnionObjectOffsetsFoldFunction(conf, child, packed, branchAccum) accum.mergeBranch(branchAccum) of nkSym: var size = szUnknownSize @@ -183,7 +183,7 @@ proc computeUnionObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode; accum: var if n.sym.bitsize == 0: # 0 represents bitsize not set computeSizeAlign(conf, n.sym.typ) size = n.sym.typ.size.int - align = n.sym.typ.align.int + align = if packed: 1 else: n.sym.typ.align.int accum.align(align) if n.sym.alignment > 0: accum.align(n.sym.alignment) @@ -365,16 +365,14 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) = else: OffsetAccum(maxAlign: 1) if tfUnion in typ.flags: - if tfPacked in typ.flags: - let info = if typ.sym != nil: typ.sym.info else: unknownLineInfo - localError(conf, info, "union type may not be packed.") - accum = OffsetAccum(offset: szUnknownSize, maxAlign: szUnknownSize) - elif accum.offset != 0: + if accum.offset != 0: let info = if typ.sym != nil: typ.sym.info else: unknownLineInfo localError(conf, info, "union type may not have an object header") accum = OffsetAccum(offset: szUnknownSize, maxAlign: szUnknownSize) + elif tfPacked in typ.flags: + computeUnionObjectOffsetsFoldFunction(conf, typ.n, true, accum) else: - computeUnionObjectOffsetsFoldFunction(conf, typ.n, accum) + computeUnionObjectOffsetsFoldFunction(conf, typ.n, false, accum) elif tfPacked in typ.flags: accum.maxAlign = 1 computeObjectOffsetsFoldFunction(conf, typ.n, true, accum) diff --git a/tests/misc/tsizeof.nim b/tests/misc/tsizeof.nim index 579532fcc..c1d9df611 100644 --- a/tests/misc/tsizeof.nim +++ b/tests/misc/tsizeof.nim @@ -694,3 +694,13 @@ type doAssert sizeof(O0) == 1 doAssert sizeof(T0) == 1 + + +type + # this thing may not have padding bytes at the end + PackedUnion* {.union, packed.} = object + a*: array[11, byte] + b*: int64 + +doAssert sizeof(PackedUnion) == 11 +doAssert alignof(PackedUnion) == 1 From 930a0c999b823ae33cecc765110f10b4eaabfcfb Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 1 Jul 2020 14:16:22 -0700 Subject: [PATCH 0332/3768] cleanup comment now that #14434 was fixed (#14874) --- nimdoc/testproject/subdir/subdir_b/utils.nim | 2 -- 1 file changed, 2 deletions(-) diff --git a/nimdoc/testproject/subdir/subdir_b/utils.nim b/nimdoc/testproject/subdir/subdir_b/utils.nim index ad8f3d162..128e8e481 100644 --- a/nimdoc/testproject/subdir/subdir_b/utils.nim +++ b/nimdoc/testproject/subdir/subdir_b/utils.nim @@ -21,8 +21,6 @@ More text. ]## -# BUG: module link in theindex.html is wrong, see #14434 - type SomeType* = enum enumValueA, From cbcaf2bbc4c7a818747a27f365c4cd1bf54feedf Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Wed, 1 Jul 2020 18:16:50 -0300 Subject: [PATCH 0333/3768] Clean out strutils (#14859) Co-authored-by: Andreas Rumpf --- changelog.md | 1 + lib/pure/strutils.nim | 5 ----- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/changelog.md b/changelog.md index 287bce3da..49e6c23b6 100644 --- a/changelog.md +++ b/changelog.md @@ -186,6 +186,7 @@ proc mydiv(a, b): int {.raises: [].} = - Added the `thiscall` calling convention as specified by Microsoft, mostly for hooking purpose - Deprecated `{.unroll.}` pragma, was ignored by the compiler anyways, was a nop. +- Remove `strutils.isNilOrWhitespace`, was deprecated. - Remove `sharedtables.initSharedTable`, was deprecated and produces undefined behavior. - Removed `asyncdispatch.newAsyncNativeSocket`, was deprecated since `0.18`. - Remove `dom.releaseEvents` and `dom.captureEvents`, was deprecated. diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 1ffc8bf22..32d6ede1e 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -2935,11 +2935,6 @@ proc isEmptyOrWhitespace*(s: string): bool {.noSideEffect, rtl, ## Checks if `s` is empty or consists entirely of whitespace characters. result = s.allCharsInSet(Whitespace) -proc isNilOrWhitespace*(s: string): bool {.noSideEffect, rtl, - extern: "nsuIsNilOrWhitespace", - deprecated: "use isEmptyOrWhitespace instead".} = - ## Alias for isEmptyOrWhitespace - result = isEmptyOrWhitespace(s) when isMainModule: proc nonStaticTests = From d590611fc5de48a281db946d223def3d94b0260f Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Thu, 2 Jul 2020 03:40:58 -0300 Subject: [PATCH 0334/3768] Clean out sharedlists (#14857) --- changelog.md | 3 +++ lib/pure/collections/sharedlist.nim | 6 ------ 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/changelog.md b/changelog.md index 49e6c23b6..fded20ca6 100644 --- a/changelog.md +++ b/changelog.md @@ -191,6 +191,9 @@ proc mydiv(a, b): int {.raises: [].} = - Removed `asyncdispatch.newAsyncNativeSocket`, was deprecated since `0.18`. - Remove `dom.releaseEvents` and `dom.captureEvents`, was deprecated. +- Remove `sharedlists.initSharedList`, was deprecated and produces undefined behaviour. + + ## Compiler changes - Specific warnings can now be turned into errors via `--warningAsError[X]:on|off`. diff --git a/lib/pure/collections/sharedlist.nim b/lib/pure/collections/sharedlist.nim index f9182acce..790529b79 100644 --- a/lib/pure/collections/sharedlist.nim +++ b/lib/pure/collections/sharedlist.nim @@ -94,10 +94,4 @@ proc deinitSharedList*[A](t: var SharedList[A]) = clear(t) deinitLock t.lock -proc initSharedList*[A](): SharedList[A] {.deprecated: "use 'init' instead".} = - ## This is not posix compliant, may introduce undefined behavior. - initLock result.lock - result.head = nil - result.tail = nil - {.pop.} From 366b9a7e4a067cece3b1215de7fb4dffc703e88a Mon Sep 17 00:00:00 2001 From: Tomohiro Date: Thu, 2 Jul 2020 20:20:34 +0900 Subject: [PATCH 0335/3768] Fix #12745 (#14879) --- compiler/extccomp.nim | 12 +----------- compiler/main.nim | 5 +++++ 2 files changed, 6 insertions(+), 11 deletions(-) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 6feb68f81..7d5df0906 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -281,9 +281,6 @@ const hExt* = ".h" -proc libNameTmpl(conf: ConfigRef): string {.inline.} = - result = if conf.target.targetOS == osWindows: "$1.lib" else: "lib$1.a" - proc nameToCC*(name: string): TSystemCC = ## Returns the kind of compiler referred to by `name`, or ccNone ## if the name doesn't refer to any known compiler. @@ -663,14 +660,7 @@ proc addExternalFileToCompile*(conf: ConfigRef; filename: AbsoluteFile) = proc getLinkCmd(conf: ConfigRef; output: AbsoluteFile, objfiles: string, isDllBuild: bool): string = if optGenStaticLib in conf.globalOptions: - var libname: string - if not conf.outFile.isEmpty: - libname = conf.outFile.string.expandTilde - if not libname.isAbsolute(): - libname = getCurrentDir() / libname - else: - libname = (libNameTmpl(conf) % splitFile(conf.projectName).name) - result = CC[conf.cCompiler].buildLib % ["libfile", quoteShell(libname), + result = CC[conf.cCompiler].buildLib % ["libfile", quoteShell(output), "objfiles", objfiles] else: var linkerExe = getConfigVar(conf, conf.cCompiler, ".linkerexe") diff --git a/compiler/main.nim b/compiler/main.nim index e498dc6b9..25a42300f 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -67,10 +67,15 @@ when not defined(leanCompiler): finishDoc2Pass(graph.config.projectName) proc setOutFile(conf: ConfigRef) = + proc libNameTmpl(conf: ConfigRef): string {.inline.} = + result = if conf.target.targetOS == osWindows: "$1.lib" else: "lib$1.a" + if conf.outFile.isEmpty: let base = conf.projectName let targetName = if optGenDynLib in conf.globalOptions: platform.OS[conf.target.targetOS].dllFrmt % base + elif optGenStaticLib in conf.globalOptions: + libNameTmpl(conf) % base else: base & platform.OS[conf.target.targetOS].exeExt conf.outFile = RelativeFile targetName From dc5a40f3f39c6ea672e6dc6aca7f8118a69dda99 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 2 Jul 2020 07:19:13 -0700 Subject: [PATCH 0336/3768] {.deprecated: [existsFile: fileExists].} (#14735) * {.deprecated: [existsFile: fileExists].} * s/existsFile/fileExists/ except under deps * workaround pending #14819 * fix test --- compiler/docgen.nim | 2 +- compiler/options.nim | 2 +- doc/manual.rst | 4 +-- koch.nim | 6 ++-- lib/packages/docutils/rst.nim | 4 +-- lib/pure/asyncftpclient.nim | 2 +- lib/pure/net.nim | 4 +-- lib/pure/os.nim | 33 +++++++++---------- lib/pure/ssl_certs.nim | 2 +- lib/system/nimscript.nim | 9 ++--- nimpretty/nimpretty.nim | 2 +- testament/categories.nim | 2 +- testament/testament.nim | 2 +- .../keineschweine/dependencies/nake/nake.nim | 2 +- .../keineschweine/enet_server/enet_server.nim | 4 +-- .../keineschweine/lib/client_helpers.nim | 2 +- tests/manyloc/keineschweine/lib/sg_assets.nim | 4 +-- .../keineschweine/server/old_dirserver.nim | 2 +- .../keineschweine/server/old_sg_server.nim | 4 +-- tests/manyloc/nake/nake.nim | 2 +- tests/manyloc/nake/nakefile.nim | 2 +- tests/misc/trunner.nim | 2 +- tests/newconfig/tfoo.nims | 2 +- tests/stdlib/tgetfileinfo.nim | 2 +- tests/test_nimscript.nims | 1 - tools/cmerge.nim | 2 +- tools/kochdocs.nim | 6 ++-- tools/nimgrep.nim | 2 +- tools/niminst/niminst.nim | 8 ++--- tools/nimweb.nim | 8 ++--- tools/vccexe/vccvswhere.nim | 4 +-- 31 files changed, 66 insertions(+), 67 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index a973bdfc1..f501dcc8d 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -140,7 +140,7 @@ template declareClosures = result = options.findFile(conf, s).string if result.len == 0: result = getCurrentDir() / s - if not existsFile(result): result = "" + if not fileExists(result): result = "" proc parseRst(text, filename: string, line, column: int, hasToc: var bool, diff --git a/compiler/options.nim b/compiler/options.nim index abec038ed..247f35419 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -739,7 +739,7 @@ proc getRelativePathFromConfigPath*(conf: ConfigRef; f: AbsoluteFile): RelativeF proc findFile*(conf: ConfigRef; f: string; suppressStdlib = false): AbsoluteFile = if f.isAbsolute: - result = if f.existsFile: AbsoluteFile(f) else: AbsoluteFile"" + result = if f.fileExists: AbsoluteFile(f) else: AbsoluteFile"" else: result = rawFindFile(conf, RelativeFile f, suppressStdlib) if result.isEmpty: diff --git a/doc/manual.rst b/doc/manual.rst index d2fcdc080..99888ad53 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -7347,9 +7347,9 @@ string expressions in general: proc getDllName: string = result = "mylib.dll" - if existsFile(result): return + if fileExists(result): return result = "mylib2.dll" - if existsFile(result): return + if fileExists(result): return quit("could not load dynamic library") proc myImport(s: cstring) {.cdecl, importc, dynlib: getDllName().} diff --git a/koch.nim b/koch.nim index e279d76de..95c54892f 100644 --- a/koch.nim +++ b/koch.nim @@ -113,7 +113,7 @@ proc tryExec(cmd: string): bool = result = execShellCmd(cmd) == 0 proc safeRemove(filename: string) = - if existsFile(filename): removeFile(filename) + if fileExists(filename): removeFile(filename) proc overwriteFile(source, dest: string) = safeRemove(dest) @@ -280,11 +280,11 @@ proc findStartNim: string = if ok: return nim when defined(Posix): const buildScript = "build.sh" - if existsFile(buildScript): + if fileExists(buildScript): if tryExec("./" & buildScript): return "bin" / nim else: const buildScript = "build.bat" - if existsFile(buildScript): + if fileExists(buildScript): if tryExec(buildScript): return "bin" / nim echo("Found no nim compiler and every attempt to build one failed!") diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 07cb3eccc..05b58c56b 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -313,7 +313,7 @@ proc defaultMsgHandler*(filename: string, line, col: int, msgkind: MsgKind, else: writeLine(stdout, message) proc defaultFindFile*(filename: string): string = - if existsFile(filename): result = filename + if fileExists(filename): result = filename else: result = "" proc newSharedState(options: RstParseOptions, @@ -328,7 +328,7 @@ proc newSharedState(options: RstParseOptions, proc findRelativeFile(p: RstParser; filename: string): string = result = p.filename.splitFile.dir / filename - if not existsFile(result): + if not fileExists(result): result = p.s.findFile(filename) proc rstMessage(p: RstParser, msgKind: MsgKind, arg: string) = diff --git a/lib/pure/asyncftpclient.nim b/lib/pure/asyncftpclient.nim index 132682b23..2d3556a89 100644 --- a/lib/pure/asyncftpclient.nim +++ b/lib/pure/asyncftpclient.nim @@ -231,7 +231,7 @@ proc listDirs*(ftp: AsyncFtpClient, dir = ""): Future[seq[string]] {.async.} = result = splitLines(await ftp.getLines()) -proc existsFile*(ftp: AsyncFtpClient, file: string): Future[bool] {.async.} = +proc fileExists*(ftp: AsyncFtpClient, file: string): Future[bool] {.async.} = ## Determines whether ``file`` exists. var files = await ftp.listDirs() for f in items(files): diff --git a/lib/pure/net.nim b/lib/pure/net.nim index c47d9e8a6..7aeffbc35 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -513,10 +513,10 @@ when defineSsl: # http://simplestcodings.blogspot.co.uk/2010/08/secure-server-client-using-openssl-in-c.html proc loadCertificates(ctx: SslCtx, certFile, keyFile: string) = - if certFile != "" and not existsFile(certFile): + if certFile != "" and not fileExists(certFile): raise newException(system.IOError, "Certificate file could not be found: " & certFile) - if keyFile != "" and not existsFile(keyFile): + if keyFile != "" and not fileExists(keyFile): raise newException(system.IOError, "Key file could not be found: " & keyFile) if certFile != "": diff --git a/lib/pure/os.nim b/lib/pure/os.nim index dcb63458b..5d73544ce 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -1097,14 +1097,14 @@ when defined(windows) and not weirdTarget: result = f.cFileName[0].int == dot and (f.cFileName[1].int == 0 or f.cFileName[1].int == dot and f.cFileName[2].int == 0) -proc existsFile*(filename: string): bool {.rtl, extern: "nos$1", +proc fileExists*(filename: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect], noNimJs.} = ## Returns true if `filename` exists and is a regular file or symlink. ## ## Directories, device files, named pipes and sockets return false. ## ## See also: - ## * `existsDir proc <#existsDir,string>`_ + ## * `dirExists proc <#dirExists,string>`_ ## * `symlinkExists proc <#symlinkExists,string>`_ when defined(windows): when useWinUnicode: @@ -1117,13 +1117,19 @@ proc existsFile*(filename: string): bool {.rtl, extern: "nos$1", var res: Stat return stat(filename, res) >= 0'i32 and S_ISREG(res.st_mode) +when not defined(nimscript): + when not defined(js): # `noNimJs` doesn't work with templates, this should improve. + template existsFile*(args: varargs[untyped]): untyped {.deprecated: "use fileExists".} = + fileExists(args) + # {.deprecated: [existsFile: fileExists].} # pending bug #14819; this would avoid above mentioned issue + proc existsDir*(dir: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect], noNimJs.} = ## Returns true if the directory `dir` exists. If `dir` is a file, false ## is returned. Follows symlinks. ## ## See also: - ## * `existsFile proc <#existsFile,string>`_ + ## * `fileExists proc <#fileExists,string>`_ ## * `symlinkExists proc <#symlinkExists,string>`_ when defined(windows): when useWinUnicode: @@ -1143,7 +1149,7 @@ proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1", ## regardless of whether the link points to a directory or file. ## ## See also: - ## * `existsFile proc <#existsFile,string>`_ + ## * `fileExists proc <#fileExists,string>`_ ## * `existsDir proc <#existsDir,string>`_ when defined(windows): when useWinUnicode: @@ -1156,19 +1162,12 @@ proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1", var res: Stat return lstat(link, res) >= 0'i32 and S_ISLNK(res.st_mode) -proc fileExists*(filename: string): bool {.inline, noNimJs.} = - ## Alias for `existsFile proc <#existsFile,string>`_. - ## - ## See also: - ## * `existsDir proc <#existsDir,string>`_ - ## * `symlinkExists proc <#symlinkExists,string>`_ - existsFile(filename) proc dirExists*(dir: string): bool {.inline, noNimJs.} = ## Alias for `existsDir proc <#existsDir,string>`_. ## ## See also: - ## * `existsFile proc <#existsFile,string>`_ + ## * `fileExists proc <#fileExists,string>`_ ## * `symlinkExists proc <#symlinkExists,string>`_ existsDir(dir) @@ -1200,7 +1199,7 @@ proc findExe*(exe: string, followSymlinks: bool = true; template checkCurrentDir() = for ext in extensions: result = addFileExt(exe, ext) - if existsFile(result): return + if fileExists(result): return when defined(posix): if '/' in exe: checkCurrentDir() else: @@ -1216,7 +1215,7 @@ proc findExe*(exe: string, followSymlinks: bool = true; var x = expandTilde(candidate) / exe for ext in extensions: var x = addFileExt(x, ext) - if existsFile(x): + if fileExists(x): when not defined(windows): while followSymlinks: # doubles as if here if x.checkSymlink: @@ -2027,7 +2026,7 @@ proc expandFilename*(filename: string): string {.rtl, extern: "nos$1", # way of retrieving the true filename for x in walkFiles(result): result = x - if not existsFile(result) and not existsDir(result): + if not fileExists(result) and not existsDir(result): # consider using: `raiseOSError(osLastError(), result)` raise newException(OSError, "file '" & result & "' does not exist") else: @@ -2893,7 +2892,7 @@ when not weirdTarget and defined(openbsd): # search in path for p in split(string(getEnv("PATH")), {PathSep}): var x = joinPath(p, exePath) - if existsFile(x): + if fileExists(x): return expandFilename(x) else: result = "" @@ -2908,7 +2907,7 @@ when not (defined(windows) or defined(macosx) or weirdTarget): # iterate over any path in the $PATH environment variable for p in split(string(getEnv("PATH")), {PathSep}): var x = joinPath(p, result) - if existsFile(x): return x + if fileExists(x): return x else: result = "" diff --git a/lib/pure/ssl_certs.nim b/lib/pure/ssl_certs.nim index 806316b02..df845decc 100644 --- a/lib/pure/ssl_certs.nim +++ b/lib/pure/ssl_certs.nim @@ -79,7 +79,7 @@ iterator scanSSLCertificates*(useEnvVars = false): string = when not defined(haiku): for p in certificate_paths: if p.endsWith(".pem") or p.endsWith(".crt"): - if existsFile(p): + if fileExists(p): yield p elif existsDir(p): for fn in joinPath(p, "*").walkFiles(): diff --git a/lib/system/nimscript.nim b/lib/system/nimscript.nim index ceaaef013..75a4b7a6e 100644 --- a/lib/system/nimscript.nim +++ b/lib/system/nimscript.nim @@ -131,15 +131,16 @@ proc fileExists*(filename: string): bool {.tags: [ReadIOEffect].} = ## Checks if the file exists. builtin +template existsFile*(args: varargs[untyped]): untyped {.deprecated: "use fileExists".} = + # xxx: warning won't be shown for nimsscript because of current logic handling + # `foreignPackageNotes` + fileExists(args) + proc dirExists*(dir: string): bool {. tags: [ReadIOEffect].} = ## Checks if the directory `dir` exists. builtin -proc existsFile*(filename: string): bool = - ## An alias for ``fileExists``. - fileExists(filename) - proc existsDir*(dir: string): bool = ## An alias for ``dirExists``. dirExists(dir) diff --git a/nimpretty/nimpretty.nim b/nimpretty/nimpretty.nim index cca4e7aa8..9b5cf556b 100644 --- a/nimpretty/nimpretty.nim +++ b/nimpretty/nimpretty.nim @@ -86,7 +86,7 @@ proc main = quit "[Error] no input file." if outfile.len == 0: outfile = infile - if not existsFile(outfile) or not sameFile(infile, outfile): + if not fileExists(outfile) or not sameFile(infile, outfile): backup = false # no backup needed since won't be over-written if backup: let infileBackup = infile & ".backup" # works with .nim or .nims diff --git a/testament/categories.nim b/testament/categories.nim index 4dfd42fd2..6abb3faa9 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -572,7 +572,7 @@ proc `&.?`(a, b: string): string = proc processSingleTest(r: var TResults, cat: Category, options, test: string) = let test = testsDir &.? cat.string / test let target = if cat.string.normalize == "js": targetJS else: targetC - if existsFile(test): + if fileExists(test): testSpec r, makeTest(test, options, cat), {target} else: doAssert false, test & " test does not exist" diff --git a/testament/testament.nim b/testament/testament.nim index 83446336c..109ce4ed9 100644 --- a/testament/testament.nim +++ b/testament/testament.nim @@ -440,7 +440,7 @@ proc testSpecHelper(r: var TResults, test: TTest, expected: TSpec, else: let isJsTarget = target == targetJS var exeFile = changeFileExt(test.name, if isJsTarget: "js" else: ExeExt) - if not existsFile(exeFile): + if not fileExists(exeFile): r.addResult(test, target, expected.output, "executable not found: " & exeFile, reExeNotFound) else: diff --git a/tests/manyloc/keineschweine/dependencies/nake/nake.nim b/tests/manyloc/keineschweine/dependencies/nake/nake.nim index 83569e30e..bc380986f 100644 --- a/tests/manyloc/keineschweine/dependencies/nake/nake.nim +++ b/tests/manyloc/keineschweine/dependencies/nake/nake.nim @@ -51,7 +51,7 @@ template withDir*(dir: string; body: stmt): stmt = cd(curDir) when true: - if not existsFile("nakefile.nim"): + if not fileExists("nakefile.nim"): echo "No nakefile.nim found. Current working dir is ", getCurrentDir() quit 1 var args = "" diff --git a/tests/manyloc/keineschweine/enet_server/enet_server.nim b/tests/manyloc/keineschweine/enet_server/enet_server.nim index 336e57755..794e1c843 100644 --- a/tests/manyloc/keineschweine/enet_server/enet_server.nim +++ b/tests/manyloc/keineschweine/enet_server/enet_server.nim @@ -118,7 +118,7 @@ when true: of cmdShortOption, cmdLongOption: case key of "f", "file": - if existsFile(val): + if fileExists(val): zoneCfgFile = val else: echo("File does not exist: ", val) @@ -136,7 +136,7 @@ when true: address.port = port var path = getAppDir()/../"data"/zoneFile - if not existsFile(path): + if not fileExists(path): echo("Zone settings file does not exist: ../data/", zoneFile) echo(path) quit(1) diff --git a/tests/manyloc/keineschweine/lib/client_helpers.nim b/tests/manyloc/keineschweine/lib/client_helpers.nim index 5f74aef85..8c45feb62 100644 --- a/tests/manyloc/keineschweine/lib/client_helpers.nim +++ b/tests/manyloc/keineschweine/lib/client_helpers.nim @@ -127,7 +127,7 @@ proc handleFileChallenge*(serv: PServer; buffer: PBuffer) = challenge = readScFileChallenge(buffer) path = expandPath(challenge) resp: CsFileChallenge - if not existsFile(path): + if not fileExists(path): resp.needFile = true echo "Got file challenge, need file." else: diff --git a/tests/manyloc/keineschweine/lib/sg_assets.nim b/tests/manyloc/keineschweine/lib/sg_assets.nim index 7caa9c3fb..5929add43 100644 --- a/tests/manyloc/keineschweine/lib/sg_assets.nim +++ b/tests/manyloc/keineschweine/lib/sg_assets.nim @@ -221,7 +221,7 @@ template cacheImpl(procName, cacheName, resultType, body: untyped) {.dirty.} = cacheName[filename] = result template checkFile(path: untyped) {.dirty.} = - if not existsFile(path): + if not fileExists(path): errors.add("File missing: " & path) cacheImpl newSprite, SpriteSheets, PSpriteSheet: @@ -322,7 +322,7 @@ proc validateSettings*(settings: JsonNode, errors: var seq[string]): bool = inc id proc loadSettingsFromFile*(filename: string, errors: var seq[string]): bool = - if not existsFile(filename): + if not fileExists(filename): errors.add("File does not exist: "&filename) else: result = loadSettings(readFile(filename), errors) diff --git a/tests/manyloc/keineschweine/server/old_dirserver.nim b/tests/manyloc/keineschweine/server/old_dirserver.nim index 390b738aa..cd2b60b26 100644 --- a/tests/manyloc/keineschweine/server/old_dirserver.nim +++ b/tests/manyloc/keineschweine/server/old_dirserver.nim @@ -164,7 +164,7 @@ when true: of cmdShortOption, cmdLongOption: case key of "f", "file": - if existsFile(val): + if fileExists(val): cfgFile = val else: echo("File does not exist: ", val) diff --git a/tests/manyloc/keineschweine/server/old_sg_server.nim b/tests/manyloc/keineschweine/server/old_sg_server.nim index 473880e2b..8bd44017e 100644 --- a/tests/manyloc/keineschweine/server/old_sg_server.nim +++ b/tests/manyloc/keineschweine/server/old_sg_server.nim @@ -149,7 +149,7 @@ when true: of cmdShortOption, cmdLongOption: case key of "f", "file": - if existsFile(val): + if fileExists(val): zoneCfgFile = val else: echo("File does not exist: ", val) @@ -165,7 +165,7 @@ when true: dirServerInfo = jsonSettings["dirserver"] var path = getAppDir()/../"data"/zoneFile - if not existsFile(path): + if not fileExists(path): echo("Zone settings file does not exist: ../data/", zoneFile) echo(path) quit(1) diff --git a/tests/manyloc/nake/nake.nim b/tests/manyloc/nake/nake.nim index ebcf21d19..87db5b54d 100644 --- a/tests/manyloc/nake/nake.nim +++ b/tests/manyloc/nake/nake.nim @@ -51,7 +51,7 @@ template withDir*(dir: string; body: untyped) = cd(curDir) when true: - if not existsFile("nakefile.nim"): + if not fileExists("nakefile.nim"): echo "No nakefile.nim found. Current working dir is ", getCurrentDir() quit 1 var args = "" diff --git a/tests/manyloc/nake/nakefile.nim b/tests/manyloc/nake/nakefile.nim index 91dd61726..1da92b76b 100644 --- a/tests/manyloc/nake/nakefile.nim +++ b/tests/manyloc/nake/nakefile.nim @@ -86,7 +86,7 @@ task "download", "download game assets": client = newHttpClient() path.add DirSep path.add(extractFilename(GameAssets)) - if existsFile(path): + if fileExists(path): echo "The file already exists\n", "[R]emove [M]ove [Q]uit [S]kip Source: ", GameAssets case stdin.readLine.toLowerAscii diff --git a/tests/misc/trunner.nim b/tests/misc/trunner.nim index 382fd35ae..d67547d62 100644 --- a/tests/misc/trunner.nim +++ b/tests/misc/trunner.nim @@ -26,7 +26,7 @@ const proc runCmd(file, options = ""): auto = let fileabs = testsDir / file.unixToNativePath - doAssert fileabs.existsFile, fileabs + doAssert fileabs.fileExists, fileabs let cmd = fmt"{nim} {mode} {options} --hints:off {fileabs}" result = execCmdEx(cmd) when false: echo result[0] & "\n" & result[1] # for debugging diff --git a/tests/newconfig/tfoo.nims b/tests/newconfig/tfoo.nims index 4186ee047..fbff38f01 100644 --- a/tests/newconfig/tfoo.nims +++ b/tests/newconfig/tfoo.nims @@ -72,7 +72,7 @@ assert cmpic("HeLLO", "hello") == 0 assert fileExists("tests/newconfig/tfoo.nims") == true assert dirExists("tests") == true -assert existsFile("tests/newconfig/tfoo.nims") == true +assert fileExists("tests/newconfig/tfoo.nims") == true assert existsDir("tests") == true discard selfExe() diff --git a/tests/stdlib/tgetfileinfo.nim b/tests/stdlib/tgetfileinfo.nim index d5ccdea56..19de193e4 100644 --- a/tests/stdlib/tgetfileinfo.nim +++ b/tests/stdlib/tgetfileinfo.nim @@ -20,7 +20,7 @@ proc genBadFileName(limit = 100): string = var hitLimit = true for i in 0..100: - if existsFile(result): + if fileExists(result): result.add("a") else: hitLimit = false diff --git a/tests/test_nimscript.nims b/tests/test_nimscript.nims index 7c64ea762..1a6f26137 100644 --- a/tests/test_nimscript.nims +++ b/tests/test_nimscript.nims @@ -77,6 +77,5 @@ block: block: # #14142 discard existsDir("/usr") discard dirExists("/usr") - discard existsFile("/usr/foo") discard fileExists("/usr/foo") discard findExe("nim") diff --git a/tools/cmerge.nim b/tools/cmerge.nim index ae81699a6..35f2f5555 100644 --- a/tools/cmerge.nim +++ b/tools/cmerge.nim @@ -9,7 +9,7 @@ proc process(dir, infile: string, outfile: File, processed: var HashSet[string]): ProcessResult = if processed.containsOrIncl(infile): return prSkipIncludeDir let toProcess = dir / infile - if not existsFile(toProcess): + if not fileExists(toProcess): echo "Warning: could not process: ", toProcess return prAddIncludeDir echo "adding: ", toProcess diff --git a/tools/kochdocs.nim b/tools/kochdocs.nim index d0f8077ed..12d3c31a3 100644 --- a/tools/kochdocs.nim +++ b/tools/kochdocs.nim @@ -25,10 +25,10 @@ proc findNimImpl*(): tuple[path: string, ok: bool] = let nim = "nim".exe result.path = "bin" / nim result.ok = true - if existsFile(result.path): return + if fileExists(result.path): return for dir in split(getEnv("PATH"), PathSep): result.path = dir / nim - if existsFile(result.path): return + if fileExists(result.path): return # assume there is a symlink to the exe or something: return (nim, false) @@ -295,7 +295,7 @@ proc buildPdfDoc*(nimArgs, destPath: string) = removeFile(dest) moveFile(dest=dest, source=pdf) removeFile(changeFileExt(pdf, "aux")) - if existsFile(changeFileExt(pdf, "toc")): + if fileExists(changeFileExt(pdf, "toc")): removeFile(changeFileExt(pdf, "toc")) removeFile(changeFileExt(pdf, "log")) removeFile(changeFileExt(pdf, "out")) diff --git a/tools/nimgrep.nim b/tools/nimgrep.nim index 42b192f24..e4f0e91aa 100644 --- a/tools/nimgrep.nim +++ b/tools/nimgrep.nim @@ -511,7 +511,7 @@ proc walker(pattern; dir: string; counter: var int, errors: var int) = if optFollow in options and optRecursive in options and path.hasRightDirectory: walker(pattern, path, counter, errors) - elif existsFile(dir): + elif fileExists(dir): processFile(pattern, dir, counter, errors) else: printError "Error: no such file or directory: " & dir diff --git a/tools/niminst/niminst.nim b/tools/niminst/niminst.nim index 4746f328a..88d16f48a 100644 --- a/tools/niminst/niminst.nim +++ b/tools/niminst/niminst.nim @@ -639,13 +639,13 @@ proc xzDist(c: var ConfigData; windowsZip=false) = proc processFile(destFile, src: string) = let dest = tmpDir / destFile when false: echo "Copying ", src, " to ", dest - if not existsFile(src): + if not fileExists(src): echo "[Warning] Source file doesn't exist: ", src let destDir = dest.splitFile.dir if not dirExists(destDir): createDir(destDir) copyFileWithPermissions(src, dest) - if not windowsZip and not existsFile("build" / buildBatFile): + if not windowsZip and not fileExists("build" / buildBatFile): quit("No C sources found in ./build/, please build by running " & "./koch csource -d:release.") @@ -708,8 +708,8 @@ RunProgram="tools\downloader.exe" # -- prepare build files for .deb creation proc debDist(c: var ConfigData) = - if not existsFile(getOutputDir(c) / "build.sh"): quit("No build.sh found.") - if not existsFile(getOutputDir(c) / "install.sh"): quit("No install.sh found.") + if not fileExists(getOutputDir(c) / "build.sh"): quit("No build.sh found.") + if not fileExists(getOutputDir(c) / "install.sh"): quit("No install.sh found.") if c.debOpts.shortDesc == "": quit("shortDesc must be set in the .ini file.") if c.debOpts.licenses.len == 0: diff --git a/tools/nimweb.nim b/tools/nimweb.nim index a654c2b0a..0a853a54d 100644 --- a/tools/nimweb.nim +++ b/tools/nimweb.nim @@ -199,7 +199,7 @@ proc walkDirRecursively(s: var seq[string], root, ext: string) = proc addFiles(s: var seq[string], dir, ext: string, patterns: seq[string]) = for p in items(patterns): - if existsFile(dir / addFileExt(p, ext)): + if fileExists(dir / addFileExt(p, ext)): s.add(dir / addFileExt(p, ext)) if existsDir(dir / p): walkDirRecursively(s, dir / p, ext) @@ -273,9 +273,9 @@ proc findNim(c: TConfigData): string = if c.nimCompiler.len > 0: return c.nimCompiler var nim = "nim".exe result = "bin" / nim - if existsFile(result): return + if fileExists(result): return for dir in split(getEnv("PATH"), PathSep): - if existsFile(dir / nim): return dir / nim + if fileExists(dir / nim): return dir / nim # assume there is a symlink to the exe or something: return nim @@ -351,7 +351,7 @@ proc buildPdfDoc(c: var TConfigData, destPath: string) = removeFile(dest) moveFile(dest=dest, source=pdf) removeFile(changeFileExt(pdf, "aux")) - if existsFile(changeFileExt(pdf, "toc")): + if fileExists(changeFileExt(pdf, "toc")): removeFile(changeFileExt(pdf, "toc")) removeFile(changeFileExt(pdf, "log")) removeFile(changeFileExt(pdf, "out")) diff --git a/tools/vccexe/vccvswhere.nim b/tools/vccexe/vccvswhere.nim index 11ad39ce9..8f62f06ca 100644 --- a/tools/vccexe/vccvswhere.nim +++ b/tools/vccexe/vccvswhere.nim @@ -18,7 +18,7 @@ proc vccVswhereExtractVcVarsAllPath(vswherePath: string): string = let vsPath = execProcess(&"\"{vswherePath}\" {vswhereArgs}").strip() if vsPath.len > 0: let vcvarsallPath = joinPath(vsPath, vcvarsRelativePath) - if existsFile(vcvarsallPath): + if fileExists(vcvarsallPath): return vcvarsallPath proc vccVswhereGeneratePath(envName: string): string = @@ -41,7 +41,7 @@ proc vccVswhereVcVarsAllPath*(): string = for tryEnv in ["ProgramFiles(x86)", "ProgramFiles"]: let vswherePath = vccVswhereGeneratePath(tryEnv) - if vswherePath.len > 0 and existsFile(vswherePath): + if vswherePath.len > 0 and fileExists(vswherePath): let vcVarsAllPath = vccVswhereExtractVcVarsAllPath(vswherePath) if vcVarsAllPath.len > 0: return vcVarsAllPath From a6cbe58e8c5a9b89d749d9935ee39192de3246d9 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 2 Jul 2020 07:19:43 -0700 Subject: [PATCH 0337/3768] typetraits.$: $((int, float), int)` is now `"((int, float), int)"` instead of `"(tuple of (int, float), int)" (#14812) Co-authored-by: Andreas Rumpf --- changelog.md | 4 +++- compiler/types.nim | 3 +-- tests/errmsgs/tnested_empty_seq.nim | 2 +- tests/errmsgs/tsigmatch.nim | 6 +++--- tests/metatype/ttypetraits.nim | 6 ++---- tests/template/tgensym_instantiationinfo.nim | 2 +- 6 files changed, 11 insertions(+), 12 deletions(-) diff --git a/changelog.md b/changelog.md index fded20ca6..030644951 100644 --- a/changelog.md +++ b/changelog.md @@ -115,7 +115,9 @@ - `macros.newLit` now preserves named vs unnamed tuples; use `-d:nimHasWorkaround14720` to keep old behavior - Add `random.gauss`, that uses the ratio of uniforms method of sampling from a Gaussian distribution. - Add `typetraits.elementType` to get element type of an iterable. -- `typetraits.$`: `$(int,)` is now `"(int,)"`; `$tuple[]` is now `"tuple[]"` +- `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)"` - add `macros.extractDocCommentsAndRunnables` helper - `strformat.fmt` and `strformat.&` support `= specifier`. `fmt"{expr=}"` now expands to `fmt"expr={expr}"`. diff --git a/compiler/types.nim b/compiler/types.nim index 0be0b3eed..1f787a4e5 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -636,8 +636,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = elif t.len == 0: result = "tuple[]" else: - if prefer == preferTypeName: result = "(" - else: result = "tuple of (" + result = "(" for i in 0.. +tsigmatch.nim(125, 6) Error: type mismatch: got <(string, proc (){.gcsafe, locks: 0.})> but expected one of: proc foo(x: (string, proc ())) first type mismatch at position: 1 - required type for x: tuple of (string, proc (){.closure.}) - but expression '("foobar", proc () = echo(["Hello!"]))' is of type: tuple of (string, proc (){.gcsafe, locks: 0.}) + required type for x: (string, proc (){.closure.}) + but expression '("foobar", proc () = echo(["Hello!"]))' is of type: (string, proc (){.gcsafe, locks: 0.}) expression: foo(("foobar", proc () = echo(["Hello!"]))) tsigmatch.nim(132, 11) Error: type mismatch: got diff --git a/tests/metatype/ttypetraits.nim b/tests/metatype/ttypetraits.nim index ca918266f..50c474fe9 100644 --- a/tests/metatype/ttypetraits.nim +++ b/tests/metatype/ttypetraits.nim @@ -52,10 +52,8 @@ block: # name, `$` doAssert $tuple[] == "tuple[]" doAssert $(int,) == "(int,)" doAssert $(int, float) == "(int, float)" - doAssert $((int), tuple[], tuple[a: uint], tuple[a: uint, b: float], (int,), (int, float)) == "(int, tuple[], tuple[a: uint], tuple[a: uint, b: float], tuple of (int,), tuple of (int, float))" - # xxx this is inconsistent, it should be: - # "(int, tuple[], tuple[a: uint], tuple[a: uint, b: float], (int,), (int, float))" - # which matches how you write it, is consistent with `$(int,)`, and is un-ambiguous. + doAssert $((int), tuple[], tuple[a: uint], tuple[a: uint, b: float], (int,), (int, float)) == + "(int, tuple[], tuple[a: uint], tuple[a: uint, b: float], (int,), (int, float))" block: # typeToString type MyInt = int diff --git a/tests/template/tgensym_instantiationinfo.nim b/tests/template/tgensym_instantiationinfo.nim index 305e7e1c3..4b997ed6a 100644 --- a/tests/template/tgensym_instantiationinfo.nim +++ b/tests/template/tgensym_instantiationinfo.nim @@ -5,7 +5,7 @@ discard """ # bug #7937 template printError(error: typed) = - # Error: inconsistent typing for reintroduced symbol 'instInfo': previous type was: tuple[filename: string, line: int, column: int]; new type is: tuple of (string, int, int) + # Error: inconsistent typing for reintroduced symbol 'instInfo': previous type was: tuple[filename: string, line: int, column: int]; new type is: (string, int, int) let instInfo {.gensym.} = instantiationInfo() echo "Error at ", instInfo.filename, ':', instInfo.line, ": ", error From 4f6acf24ffde4c43ea9f8c954265d23312453b31 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Fri, 3 Jul 2020 15:13:10 -0300 Subject: [PATCH 0338/3768] Add jsre (#14870) --- changelog.md | 3 +++ lib/impure/nre.nim | 2 +- lib/impure/re.nim | 2 +- lib/js/jsre.nim | 43 +++++++++++++++++++++++++++++++++++++++++++ tools/kochdocs.nim | 7 +++++-- 5 files changed, 53 insertions(+), 4 deletions(-) create mode 100644 lib/js/jsre.nim diff --git a/changelog.md b/changelog.md index 030644951..8446f9467 100644 --- a/changelog.md +++ b/changelog.md @@ -122,6 +122,9 @@ - `strformat.fmt` and `strformat.&` support `= specifier`. `fmt"{expr=}"` now expands to `fmt"expr={expr}"`. +- Add `jsre` module, [Regular Expressions for the JavaScript target.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions) + + ## Language changes - In the newruntime it is now allowed to assign to the discriminator field without restrictions as long as case object doesn't have custom destructor. diff --git a/lib/impure/nre.nim b/lib/impure/nre.nim index fe71f89fe..653d4d1c5 100644 --- a/lib/impure/nre.nim +++ b/lib/impure/nre.nim @@ -7,7 +7,7 @@ # when defined(js): - {.error: "This library needs to be compiled with a c-like backend, and depends on PCRE.".} + {.error: "This library needs to be compiled with a c-like backend, and depends on PCRE; See jsre for JS backend.".} ## What is NRE? ## ============ diff --git a/lib/impure/re.nim b/lib/impure/re.nim index 1a6622677..fe3ea96ff 100644 --- a/lib/impure/re.nim +++ b/lib/impure/re.nim @@ -8,7 +8,7 @@ # when defined(js): - {.error: "This library needs to be compiled with a c-like backend, and depends on PCRE.".} + {.error: "This library needs to be compiled with a c-like backend, and depends on PCRE; See jsre for JS backend.".} ## Regular expression support for Nim. ## diff --git a/lib/js/jsre.nim b/lib/js/jsre.nim new file mode 100644 index 000000000..31cfa3d0b --- /dev/null +++ b/lib/js/jsre.nim @@ -0,0 +1,43 @@ +## Regular Expressions for the JavaScript target. +## * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions + +type RegExp* {.importjs.} = object ## Regular Expressions for JavaScript target. + flags* {.importjs.}: cstring ## cstring that contains the flags of the RegExp object. + dotAll* {.importjs.}: bool ## Whether `.` matches newlines or not. + global* {.importjs.}: bool ## Whether to test against all possible matches in a string, or only against the first. + ignoreCase* {.importjs.}: bool ## Whether to ignore case while attempting a match in a string. + multiline* {.importjs.}: bool ## Whether to search in strings across multiple lines. + source* {.importjs.}: cstring ## The text of the pattern. + sticky* {.importjs.}: bool ## Whether the search is sticky. + unicode* {.importjs.}: bool ## Whether Unicode features are enabled. + lastIndex* {.importjs.}: cint ## Index at which to start the next match (read/write property). + input* {.importjs.}: cstring ## Read-only and modified on successful match. + lastMatch* {.importjs.}: cstring ## Ditto. + lastParen* {.importjs.}: cstring ## Ditto. + leftContext* {.importjs.}: cstring ## Ditto. + rightContext* {.importjs.}: cstring ## Ditto. + +func newRegExp*(pattern: cstring; flags: cstring): RegExp {.importjs: "new RegExp(@)".} + ## Creates a new RegExp object. + +func compile*(self: RegExp; pattern: cstring; flags: cstring) {.importjs: "#.compile(@)".} + ## Recompiles a regular expression during execution of a script. + +func exec*(self: RegExp; pattern: cstring): seq[cstring] {.importjs: "#.exec(#)".} + ## Executes a search for a match in its string parameter. + +func test*(self: RegExp; pattern: cstring): bool {.importjs: "#.test(#)".} + ## Tests for a match in its string parameter. + +func toString*(self: RegExp): cstring {.importjs: "#.toString()".} + ## Returns a string representing the RegExp object. + + +runnableExamples: + let jsregex: RegExp = newRegExp(r"\s+", r"i") + jsregex.compile(r"\w+", r"i") + doAssert jsregex.test(r"nim javascript") + doAssert jsregex.exec(r"nim javascript") == @["nim".cstring] + doAssert jsregex.toString() == r"/\w+/i" + jsregex.compile(r"[0-9]", r"i") + doAssert jsregex.test(r"0123456789abcd") diff --git a/tools/kochdocs.nim b/tools/kochdocs.nim index 12d3c31a3..43b802ee2 100644 --- a/tools/kochdocs.nim +++ b/tools/kochdocs.nim @@ -15,6 +15,8 @@ const var nimExe*: string +template isJsOnly(file: string): bool = file.isRelativeTo("lib/js") + proc exe*(f: string): string = result = addFileExt(f, ExeExt) when defined(windows): @@ -256,10 +258,11 @@ proc buildDoc(nimArgs, destPath: string) = destPath / changeFileExt(splitFile(d).name, "html"), d] i.inc for d in items(doc): + let extra = if isJsOnly(d): " --backend:js " else: "" var nimArgs2 = nimArgs if d.isRelativeTo("compiler"): doAssert false - commands[i] = nim & " doc $# --git.url:$# --outdir:$# --index:on $#" % - [nimArgs2, gitUrl, destPath, d] + commands[i] = nim & " doc $# $# --git.url:$# --outdir:$# --index:on $#" % + [extra, nimArgs2, gitUrl, destPath, d] i.inc for d in items(withoutIndex): commands[i] = nim & " doc2 $# --git.url:$# -o:$# $#" % From 695154970d839add2fbbe9754e9e638511120729 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 3 Jul 2020 14:04:23 -0700 Subject: [PATCH 0339/3768] deprecate existsDir; use dirExists instead (#14884) --- changelog.md | 1 + compiler/docgen.nim | 2 +- lib/pure/os.nim | 28 ++++++++----------- lib/pure/ssl_certs.nim | 2 +- lib/system/nimscript.nim | 13 ++++----- testament/categories.nim | 2 +- tests/manyloc/keineschweine/keineschweine.nim | 2 +- .../keineschweine/lib/client_helpers.nim | 2 +- tests/manyloc/nake/nakefile.nim | 2 +- tests/newconfig/tfoo.nims | 2 +- tests/test_nimscript.nims | 1 - tools/nimgrep.nim | 2 +- tools/niminst/niminst.nim | 8 +++--- tools/nimweb.nim | 4 +-- 14 files changed, 32 insertions(+), 39 deletions(-) diff --git a/changelog.md b/changelog.md index 8446f9467..abe69a086 100644 --- a/changelog.md +++ b/changelog.md @@ -121,6 +121,7 @@ - add `macros.extractDocCommentsAndRunnables` helper - `strformat.fmt` and `strformat.&` support `= specifier`. `fmt"{expr=}"` now expands to `fmt"expr={expr}"`. +- deprecations: `os.existsDir` => `dirExists`, `os.existsFile` => `fileExists` - Add `jsre` module, [Regular Expressions for the JavaScript target.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index f501dcc8d..cb8c77e87 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -82,7 +82,7 @@ proc presentationPath*(conf: ConfigRef, file: AbsoluteFile, isTitle = false): Re # we're (currently) requiring `isAbsolute` to avoid confusion when passing # a relative path (would it be relative wrt $PWD or to projectfile) conf.globalAssert conf.docRoot.isAbsolute, arg=conf.docRoot - conf.globalAssert conf.docRoot.existsDir, arg=conf.docRoot + conf.globalAssert conf.docRoot.dirExists, arg=conf.docRoot # needed because `canonicalizePath` called on `file` result = file.relativeTo conf.docRoot.expandFilename.AbsoluteDir else: diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 5d73544ce..8bbceddeb 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -1117,13 +1117,7 @@ proc fileExists*(filename: string): bool {.rtl, extern: "nos$1", var res: Stat return stat(filename, res) >= 0'i32 and S_ISREG(res.st_mode) -when not defined(nimscript): - when not defined(js): # `noNimJs` doesn't work with templates, this should improve. - template existsFile*(args: varargs[untyped]): untyped {.deprecated: "use fileExists".} = - fileExists(args) - # {.deprecated: [existsFile: fileExists].} # pending bug #14819; this would avoid above mentioned issue - -proc existsDir*(dir: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect], +proc dirExists*(dir: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect], noNimJs.} = ## Returns true if the directory `dir` exists. If `dir` is a file, false ## is returned. Follows symlinks. @@ -1150,7 +1144,7 @@ proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1", ## ## See also: ## * `fileExists proc <#fileExists,string>`_ - ## * `existsDir proc <#existsDir,string>`_ + ## * `dirExists proc <#dirExists,string>`_ when defined(windows): when useWinUnicode: wrapUnary(a, getFileAttributesW, link) @@ -1163,13 +1157,13 @@ proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1", return lstat(link, res) >= 0'i32 and S_ISLNK(res.st_mode) -proc dirExists*(dir: string): bool {.inline, noNimJs.} = - ## Alias for `existsDir proc <#existsDir,string>`_. - ## - ## See also: - ## * `fileExists proc <#fileExists,string>`_ - ## * `symlinkExists proc <#symlinkExists,string>`_ - existsDir(dir) +when not defined(nimscript): + when not defined(js): # `noNimJs` doesn't work with templates, this should improve. + template existsFile*(args: varargs[untyped]): untyped {.deprecated: "use fileExists".} = + fileExists(args) + template existsDir*(args: varargs[untyped]): untyped {.deprecated: "use dirExists".} = + dirExists(args) + # {.deprecated: [existsFile: fileExists].} # pending bug #14819; this would avoid above mentioned issue when not defined(windows) and not weirdTarget: proc checkSymlink(path: string): bool = @@ -2026,7 +2020,7 @@ proc expandFilename*(filename: string): string {.rtl, extern: "nos$1", # way of retrieving the true filename for x in walkFiles(result): result = x - if not fileExists(result) and not existsDir(result): + if not fileExists(result) and not dirExists(result): # consider using: `raiseOSError(osLastError(), result)` raise newException(OSError, "file '" & result & "' does not exist") else: @@ -2323,7 +2317,7 @@ proc existsOrCreateDir*(dir: string): bool {.rtl, extern: "nos$1", result = not rawCreateDir(dir) if result: # path already exists - need to check that it is indeed a directory - if not existsDir(dir): + if not dirExists(dir): raise newException(IOError, "Failed to create '" & dir & "'") proc createDir*(dir: string) {.rtl, extern: "nos$1", diff --git a/lib/pure/ssl_certs.nim b/lib/pure/ssl_certs.nim index df845decc..d04244307 100644 --- a/lib/pure/ssl_certs.nim +++ b/lib/pure/ssl_certs.nim @@ -81,7 +81,7 @@ iterator scanSSLCertificates*(useEnvVars = false): string = if p.endsWith(".pem") or p.endsWith(".crt"): if fileExists(p): yield p - elif existsDir(p): + elif dirExists(p): for fn in joinPath(p, "*").walkFiles(): yield fn else: diff --git a/lib/system/nimscript.nim b/lib/system/nimscript.nim index 75a4b7a6e..bf298e0d4 100644 --- a/lib/system/nimscript.nim +++ b/lib/system/nimscript.nim @@ -131,18 +131,17 @@ proc fileExists*(filename: string): bool {.tags: [ReadIOEffect].} = ## Checks if the file exists. builtin -template existsFile*(args: varargs[untyped]): untyped {.deprecated: "use fileExists".} = - # xxx: warning won't be shown for nimsscript because of current logic handling - # `foreignPackageNotes` - fileExists(args) - proc dirExists*(dir: string): bool {. tags: [ReadIOEffect].} = ## Checks if the directory `dir` exists. builtin -proc existsDir*(dir: string): bool = - ## An alias for ``dirExists``. +template existsFile*(args: varargs[untyped]): untyped {.deprecated: "use fileExists".} = + # xxx: warning won't be shown for nimsscript because of current logic handling + # `foreignPackageNotes` + fileExists(args) + +template existsDir*(args: varargs[untyped]): untyped {.deprecated: "use dirExists".} = dirExists(dir) proc selfExe*(): string = diff --git a/testament/categories.nim b/testament/categories.nim index 6abb3faa9..760c3c5be 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -507,7 +507,7 @@ proc testNimblePackages(r: var TResults; cat: Category; packageFilter: string, p inc r.total var test = makeSupTest(url, "", cat) let buildPath = packagesDir / name - if not existsDir(buildPath): + if not dirExists(buildPath): if useHead: let (installCmdLine, installOutput, installStatus) = execCmdEx2("git", ["clone", url, buildPath]) if installStatus != QuitSuccess: diff --git a/tests/manyloc/keineschweine/keineschweine.nim b/tests/manyloc/keineschweine/keineschweine.nim index 6d8e48f24..cde31bd18 100644 --- a/tests/manyloc/keineschweine/keineschweine.nim +++ b/tests/manyloc/keineschweine/keineschweine.nim @@ -446,7 +446,7 @@ ingameClient.registerHandler(KeyF12, down, proc() = toggleSpec()) ingameClient.registerHandler(KeyF11, down, toggleShipSelect) ingameClient.registerHandler(MouseLeft, down, handleLClick) when defined(recordMode): - if not existsDir("data/snapshots"): + if not dirExists("data/snapshots"): createDir("data/snapshots") ingameClient.registerHandler(keynum9, down, proc() = if not isRecording: startRecording() diff --git a/tests/manyloc/keineschweine/lib/client_helpers.nim b/tests/manyloc/keineschweine/lib/client_helpers.nim index 8c45feb62..b535225dc 100644 --- a/tests/manyloc/keineschweine/lib/client_helpers.nim +++ b/tests/manyloc/keineschweine/lib/client_helpers.nim @@ -103,7 +103,7 @@ proc saveCurrentFile() = let path = expandPath(currentFileTransfer.assetType, currentFileTransfer.fileName) parent = parentDir(path) - if not existsDir(parent): + if not dirExists(parent): createDir(parent) echo("Created dir") writeFile path, currentFIleTransfer.data diff --git a/tests/manyloc/nake/nakefile.nim b/tests/manyloc/nake/nakefile.nim index 1da92b76b..95058c277 100644 --- a/tests/manyloc/nake/nakefile.nim +++ b/tests/manyloc/nake/nakefile.nim @@ -77,7 +77,7 @@ task "testskel", "create skeleton test dir for testing": task "clean", "cleanup generated files": var dirs = @["nimcache", "server"/"nimcache"] dirs.apply(proc(x: var string) = - if existsDir(x): removeDir(x)) + if dirExists(x): removeDir(x)) task "download", "download game assets": var diff --git a/tests/newconfig/tfoo.nims b/tests/newconfig/tfoo.nims index fbff38f01..a53e777d4 100644 --- a/tests/newconfig/tfoo.nims +++ b/tests/newconfig/tfoo.nims @@ -73,7 +73,7 @@ assert fileExists("tests/newconfig/tfoo.nims") == true assert dirExists("tests") == true assert fileExists("tests/newconfig/tfoo.nims") == true -assert existsDir("tests") == true +assert dirExists("tests") == true discard selfExe() diff --git a/tests/test_nimscript.nims b/tests/test_nimscript.nims index 1a6f26137..d9f477366 100644 --- a/tests/test_nimscript.nims +++ b/tests/test_nimscript.nims @@ -75,7 +75,6 @@ block: doAssert "./foo//./bar/".normalizedPath == "foo/bar".unixToNativePath block: # #14142 - discard existsDir("/usr") discard dirExists("/usr") discard fileExists("/usr/foo") discard findExe("nim") diff --git a/tools/nimgrep.nim b/tools/nimgrep.nim index e4f0e91aa..138f1680c 100644 --- a/tools/nimgrep.nim +++ b/tools/nimgrep.nim @@ -495,7 +495,7 @@ proc styleInsensitive(s: string): string = else: addx() proc walker(pattern; dir: string; counter: var int, errors: var int) = - if existsDir(dir): + if dirExists(dir): for kind, path in walkDir(dir): case kind of pcFile: diff --git a/tools/niminst/niminst.nim b/tools/niminst/niminst.nim index 88d16f48a..a5fc388cd 100644 --- a/tools/niminst/niminst.nim +++ b/tools/niminst/niminst.nim @@ -251,12 +251,12 @@ proc walkDirRecursively(s: var seq[string], root, explicit: string, proc addFiles(s: var seq[string], patterns: seq[string]) = for p in items(patterns): - if existsDir(p): + if dirExists(p): walkDirRecursively(s, p, p, false) else: var i = 0 for f in walkPattern(p): - if existsDir(f): + if dirExists(f): walkDirRecursively(s, f, p, false) elif not ignoreFile(f, p, false): add(s, unixToNativePath(f)) @@ -518,7 +518,7 @@ template gatherFiles(fun, libpath, outDir) = proc srcdist(c: var ConfigData) = let cCodeDir = getOutputDir(c) / "c_code" - if not existsDir(cCodeDir): createDir(cCodeDir) + if not dirExists(cCodeDir): createDir(cCodeDir) gatherFiles(copyFile, c.libpath, cCodeDir) var winIndex = -1 var intel32Index = -1 @@ -532,7 +532,7 @@ proc srcdist(c: var ConfigData) = if cpuname.cmpIgnoreStyle("i386") == 0: intel32Index = cpuA elif cpuname.cmpIgnoreStyle("amd64") == 0: intel64Index = cpuA var dir = getOutputDir(c) / buildDir(osA, cpuA) - if existsDir(dir): removeDir(dir) + if dirExists(dir): removeDir(dir) createDir(dir) var cmd = ("nim compile -f --symbolfiles:off --compileonly " & "--gen_mapping --cc:gcc --skipUserCfg" & diff --git a/tools/nimweb.nim b/tools/nimweb.nim index 0a853a54d..10319be87 100644 --- a/tools/nimweb.nim +++ b/tools/nimweb.nim @@ -201,7 +201,7 @@ proc addFiles(s: var seq[string], dir, ext: string, patterns: seq[string]) = for p in items(patterns): if fileExists(dir / addFileExt(p, ext)): s.add(dir / addFileExt(p, ext)) - if existsDir(dir / p): + if dirExists(dir / p): walkDirRecursively(s, dir / p, ext) proc parseIniFile(c: var TConfigData) = @@ -483,7 +483,7 @@ proc buildPage(c: var TConfigData, file, title, rss: string, assetDir = "") = quit("[Error] cannot open: " & temp) var f: File var outfile = c.webUploadOutput / "$#.html" % file - if not existsDir(outfile.splitFile.dir): + if not dirExists(outfile.splitFile.dir): createDir(outfile.splitFile.dir) if open(f, outfile, fmWrite): writeLine(f, generateHTMLPage(c, file, title, content, rss, assetDir)) From 1854d29781aff913ca6892cbf73df91b0399397e Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 4 Jul 2020 07:37:24 +0200 Subject: [PATCH 0340/3768] scoped memory management (#14790) * fixes the regressions * closes #13936 * scope based memory management implemented * enabled tcontrolflow.nim test case * final cleanups --- compiler/ccgcalls.nim | 26 + compiler/ccgexprs.nim | 22 +- compiler/ccgstmts.nim | 24 +- compiler/cgen.nim | 6 +- compiler/cgendata.nim | 1 + compiler/injectdestructors.nim | 721 ++++++++++++---------------- compiler/semstmts.nim | 4 +- compiler/spawn.nim | 4 +- doc/destructors.rst | 4 +- tests/arc/tarcmisc.nim | 36 ++ tests/arc/tcontrolflow.nim | 21 +- tests/arc/tmovebug.nim | 6 +- tests/arc/tthread.nim | 10 +- tests/arc/tweave.nim | 154 ++++++ tests/async/testmanyasyncevents.nim | 3 +- tests/casestmt/t12785.nim | 8 +- tests/destructor/tdestructor.nim | 38 +- 17 files changed, 611 insertions(+), 477 deletions(-) create mode 100644 tests/arc/tweave.nim diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 80f02b6c3..2c5d306a3 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -644,7 +644,33 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) = pl.add(~"];$n") line(p, cpsStmts, pl) +proc notYetAlive(n: PNode): bool {.inline.} = + let r = getRoot(n) + result = r != nil and r.loc.lode == nil + +proc isInactiveDestructorCall(p: BProc, e: PNode): bool = + #[ Consider this example. + + var :tmpD_3281815 + try: + if true: + return + let args_3280013 = + wasMoved_3281816(:tmpD_3281815) + `=_3280036`(:tmpD_3281815, [1]) + :tmpD_3281815 + finally: + `=destroy_3280027`(args_3280013) + + We want to return early but the 'finally' section is traversed before + the 'let args = ...' statement. We exploit this to generate better + code for 'return'. ]# + result = e.len == 2 and e[0].kind == nkSym and + e[0].sym.name.s == "=destroy" and notYetAlive(e[1].skipAddr) + proc genCall(p: BProc, e: PNode, d: var TLoc) = + if p.withinBlockLeaveActions > 0 and isInactiveDestructorCall(p, e): + return if e[0].typ.skipTypes({tyGenericInst, tyAlias, tySink, tyOwned}).callConv == ccClosure: genClosureCall(p, nil, e, d) elif e[0].kind == nkSym and sfInfixCall in e[0].sym.flags: diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 08b9e1ff6..c194ce326 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2066,10 +2066,14 @@ proc skipAddr(n: PNode): PNode = proc genWasMoved(p: BProc; n: PNode) = var a: TLoc - initLocExpr(p, n[1].skipAddr, a) - resetLoc(p, a) - #linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n", - # [addrLoc(p.config, a), getTypeDesc(p.module, a.t)]) + let n1 = n[1].skipAddr + if p.withinBlockLeaveActions > 0 and notYetAlive(n1): + discard + else: + initLocExpr(p, n1, a) + resetLoc(p, a) + #linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n", + # [addrLoc(p.config, a), getTypeDesc(p.module, a.t)]) proc genMove(p: BProc; n: PNode; d: var TLoc) = var a: TLoc @@ -2593,10 +2597,12 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = else: putLocIntoDest(p, d, sym.loc) of skTemp: - if sym.loc.r == nil: - # we now support undeclared 'skTemp' variables for easier - # transformations in other parts of the compiler: - assignLocalVar(p, n) + when false: + # this is more harmful than helpful. + if sym.loc.r == nil: + # we now support undeclared 'skTemp' variables for easier + # transformations in other parts of the compiler: + assignLocalVar(p, n) if sym.loc.r == nil or sym.loc.t == nil: #echo "FAILED FOR PRCO ", p.prc.name.s #echo renderTree(p.prc.ast, {renderIds}) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 82f1caadc..532272374 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -200,6 +200,7 @@ proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) = var stack = newSeq[tuple[fin: PNode, inExcept: bool, label: Natural]](0) + inc p.withinBlockLeaveActions for i in 1..howManyTrys: let tryStmt = p.nestedTryStmts.pop if p.config.exc == excSetjmp: @@ -217,6 +218,8 @@ proc blockLeaveActions(p: BProc, howManyTrys, howManyExcepts: int) = if finallyStmt != nil: genStmts(p, finallyStmt[0]) + dec p.withinBlockLeaveActions + # push old elements again: for i in countdown(howManyTrys-1, 0): p.nestedTryStmts.add(stack[i]) @@ -861,10 +864,10 @@ proc genStringCase(p: BProc, t: PNode, d: var TLoc) = genCaseGeneric(p, t, d, "", "if (#eqStrings($1, $2)) goto $3;$n") proc branchHasTooBigRange(b: PNode): bool = - for i in 0.. RangeExpandLimit: + if (it.kind == nkRange) and + it[1].intVal - it[0].intVal > RangeExpandLimit: return true proc ifSwitchSplitPoint(p: BProc, n: PNode): int = @@ -988,9 +991,14 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) = let fin = if t[^1].kind == nkFinally: t[^1] else: nil p.nestedTryStmts.add((fin, false, 0.Natural)) - startBlock(p, "try {$n") - expr(p, t[0], d) - endBlock(p) + if t.kind == nkHiddenTryStmt: + lineCg(p, cpsStmts, "try {$n", []) + expr(p, t[0], d) + lineCg(p, cpsStmts, "}$n", []) + else: + startBlock(p, "try {$n") + expr(p, t[0], d) + endBlock(p) # First pass: handle Nim based exceptions: lineCg(p, cpsStmts, "catch (#Exception* T$1_) {$n", [etmp+1]) @@ -1335,13 +1343,13 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) = linefmt(p, cpsStmts, "$1.status = _setjmp($1.context);$n", [safePoint]) else: linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", [safePoint]) - startBlock(p, "if ($1.status == 0) {$n", [safePoint]) + lineCg(p, cpsStmts, "if ($1.status == 0) {$n", [safePoint]) let fin = if t[^1].kind == nkFinally: t[^1] else: nil p.nestedTryStmts.add((fin, quirkyExceptions, 0.Natural)) expr(p, t[0], d) if not quirkyExceptions: linefmt(p, cpsStmts, "#popSafePoint();$n", []) - endBlock(p) + lineCg(p, cpsStmts, "}$n", []) startBlock(p, "else {$n") linefmt(p, cpsStmts, "#popSafePoint();$n", []) genRestoreFrameAfterException(p) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 99bfe9119..86860fbf4 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -1561,7 +1561,7 @@ proc registerModuleToMain(g: BModuleList; m: BModule) = if sfSystemModule in m.module.flags: if emulatedThreadVars(m.config) and m.config.target.targetOS != osStandalone: g.mainDatInit.add(ropecg(m, "\t#initThreadVarsEmulation();$N", [])) - if m.config.target.targetOS != osStandalone and m.config.selectedGC != gcNone: + if m.config.target.targetOS != osStandalone and m.config.selectedGC notin {gcNone, gcArc, gcOrc}: g.mainDatInit.add(ropecg(m, "\t#initStackBottomWith((void *)&inner);$N", [])) if m.s[cfsInitProc].len > 0: @@ -1666,6 +1666,10 @@ proc genInitCode(m: BModule) = writeSection(preInitProc, cpsInit, m.hcrOn) writeSection(preInitProc, cpsStmts) prc.addf("}$N", []) + when false: + m.initProc.blocks[0].sections[cpsLocals].add m.preInitProc.s(cpsLocals) + m.initProc.blocks[0].sections[cpsInit].prepend m.preInitProc.s(cpsInit) + m.initProc.blocks[0].sections[cpsStmts].prepend m.preInitProc.s(cpsStmts) # add new scope for following code, because old vcc compiler need variable # be defined at the top of the block diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index a20931be4..3384558f8 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -97,6 +97,7 @@ type # requires 'T x = T()' to become 'T x; x = T()' # (yes, C++ is weird like that) withinTryWithExcept*: int # required for goto based exception handling + withinBlockLeaveActions*: int # complex to explain sigConflicts*: CountTable[string] TTypeSeq* = seq[PType] diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index a2669be99..e10686917 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -27,10 +27,101 @@ import lineinfos, parampatterns, sighashes, liftdestructors from trees import exprStructuralEquivalent, getRoot -from algorithm import reverse -const - scopeBasedDestruction = false +type + Scope = object # well we do scope-based memory management. \ + # a scope is comparable to an nkStmtListExpr like + # (try: statements; dest = y(); finally: destructors(); dest) + vars: seq[PSym] + wasMoved: seq[PNode] + final: seq[PNode] # finally section + needsTry: bool + parent: ptr Scope + +proc nestedScope(parent: var Scope): Scope = + Scope(vars: @[], wasMoved: @[], final: @[], needsTry: false, parent: addr(parent)) + +proc rememberParent(parent: var Scope; inner: Scope) {.inline.} = + parent.needsTry = parent.needsTry or inner.needsTry + +proc optimize(s: var Scope) = + # optimize away simple 'wasMoved(x); destroy(x)' pairs. + #[ Unfortunately this optimization is only really safe when no exceptions + are possible, see for example: + + proc main(inp: string; cond: bool) = + if cond: + try: + var s = ["hi", inp & "more"] + for i in 0..4: + echo s + consume(s) + wasMoved(s) + finally: + destroy(x) + + Now assume 'echo' raises, then we shouldn't do the 'wasMoved(s)' + ]# + # XXX: Investigate how to really insert 'wasMoved()' calls! + proc findCorrespondingDestroy(final: seq[PNode]; moved: PNode): int = + # remember that it's destroy(addr(x)) + for i in 0 ..< final.len: + if final[i] != nil and exprStructuralEquivalent(final[i][1].skipAddr, moved, strictSymEquality = true): + return i + return -1 + + var removed = 0 + for i in 0 ..< s.wasMoved.len: + let j = findCorrespondingDestroy(s.final, s.wasMoved[i][1]) + if j >= 0: + s.wasMoved[i] = nil + s.final[j] = nil + inc removed + if removed > 0: + template filterNil(field) = + var m = newSeq[PNode](s.field.len - removed) + var mi = 0 + for i in 0 ..< s.field.len: + if s.field[i] != nil: + m[mi] = s.field[i] + inc mi + assert mi == m.len + s.field = m + + filterNil(wasMoved) + filterNil(final) + +proc toTree(s: var Scope; ret: PNode; onlyCareAboutVars = false): PNode = + if not s.needsTry: optimize(s) + assert ret != nil + if s.vars.len == 0 and s.final.len == 0 and s.wasMoved.len == 0: + # trivial, nothing was done: + result = ret + else: + if isEmptyType(ret.typ): + result = newNodeI(nkStmtList, ret.info) + else: + result = newNodeIT(nkStmtListExpr, ret.info, ret.typ) + + if s.vars.len > 0: + let varSection = newNodeI(nkVarSection, ret.info) + for tmp in s.vars: + varSection.add newTree(nkIdentDefs, newSymNode(tmp), newNodeI(nkEmpty, ret.info), + newNodeI(nkEmpty, ret.info)) + result.add varSection + if onlyCareAboutVars: + result.add ret + s.vars.setLen 0 + elif s.needsTry: + var finSection = newNodeI(nkStmtList, ret.info) + for m in s.wasMoved: finSection.add m + for i in countdown(s.final.high, 0): finSection.add s.final[i] + result.add newTryFinally(ret, finSection) + else: + #assert isEmptyType(ret.typ) + result.add ret + for m in s.wasMoved: result.add m + for i in countdown(s.final.high, 0): result.add s.final[i] type Con = object @@ -38,14 +129,10 @@ type g: ControlFlowGraph jumpTargets: IntSet destroys, topLevelVars: PNode - scopeDestroys: seq[PNode] # used as a stack that pop from - # at strategic places which try to - # mimic the natural scope. graph: ModuleGraph emptyNode: PNode otherRead: PNode - inLoop, inSpawn, hasUnstructuredCf, inDangerousBranch: int - declaredVars: IntSet # variables we already moved to the top level + inLoop, inSpawn: int uninit: IntSet # set of uninit'ed vars uninitComputed: bool @@ -62,8 +149,8 @@ template dbg(body) = if c.owner.name.s == toDebug or toDebug == "always": body -proc p(n: PNode; c: var Con; mode: ProcessMode): PNode -proc moveOrCopy(dest, ri: PNode; c: var Con, isDecl = false): PNode +proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode +proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope; isDecl = false): PNode proc isLastRead(location: PNode; cfg: ControlFlowGraph; otherRead: var PNode; pc, until: int): int = var pc = pc @@ -265,15 +352,6 @@ proc genDestroy(c: Con; dest: PNode): PNode = let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink}) result = genOp(c, t, attachedDestructor, dest, nil) -when false: - proc preventMoveRef(dest, ri: PNode): bool = - let lhs = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink}) - var ri = ri - if ri.kind in nkCallKinds and ri[0].kind == nkSym and ri[0].sym.magic == mUnown: - ri = ri[1] - let rhs = ri.typ.skipTypes({tyGenericInst, tyAlias, tySink}) - result = lhs.kind == tyRef and rhs.kind == tyOwned - proc canBeMoved(c: Con; t: PType): bool {.inline.} = let t = t.skipTypes({tyGenericInst, tyAlias, tySink}) if optOwnedRefs in c.graph.config.globalOptions: @@ -284,8 +362,10 @@ proc canBeMoved(c: Con; t: PType): bool {.inline.} = proc isNoInit(dest: PNode): bool {.inline.} = result = dest.kind == nkSym and sfNoInit in dest.sym.flags -proc genSink(c: var Con; dest, ri: PNode, isDecl = false): PNode = - if isUnpackedTuple(dest) or (isDecl and c.inLoop <= 0) or (isAnalysableFieldAccess(dest, c.owner) and isFirstWrite(dest, c)) or isNoInit(dest): +proc genSink(c: var Con; s: var Scope; dest, ri: PNode, isDecl = false): PNode = + if isUnpackedTuple(dest) or (isDecl and c.inLoop <= 0) or + (isAnalysableFieldAccess(dest, c.owner) and isFirstWrite(dest, c)) or + isNoInit(dest): # optimize sink call into a bitwise memcopy result = newTree(nkFastAsgn, dest, ri) else: @@ -311,34 +391,34 @@ proc genCopy(c: var Con; dest, ri: PNode): PNode = checkForErrorPragma(c, t, ri, "=") result = genCopyNoCheck(c, dest, ri) -proc addTopVar(c: var Con; v: PNode) = - c.topLevelVars.add newTree(nkIdentDefs, v, c.emptyNode, c.emptyNode) +proc addTopVar(c: var Con; s: var Scope; v: PNode) = + s.vars.add v.sym -proc getTemp(c: var Con; typ: PType; info: TLineInfo): PNode = +proc getTemp(c: var Con; s: var Scope; typ: PType; info: TLineInfo): PNode = let sym = newSym(skTemp, getIdent(c.graph.cache, ":tmpD"), c.owner, info) sym.typ = typ + s.vars.add(sym) result = newSymNode(sym) -proc genDiscriminantAsgn(c: var Con; n: PNode): PNode = +proc genDiscriminantAsgn(c: var Con; s: var Scope; n: PNode): PNode = # discriminator is ordinal value that doesn't need sink destroy # but fields within active case branch might need destruction # tmp to support self assignments - let tmp = getTemp(c, n[1].typ, n.info) - c.addTopVar(tmp) + let tmp = getTemp(c, s, n[1].typ, n.info) result = newTree(nkStmtList) - result.add newTree(nkFastAsgn, tmp, p(n[1], c, consumed)) - result.add p(n[0], c, normal) + result.add newTree(nkFastAsgn, tmp, p(n[1], c, s, consumed)) + result.add p(n[0], c, s, normal) - let le = p(n[0], c, normal) + let le = p(n[0], c, s, normal) let leDotExpr = if le.kind == nkCheckedFieldExpr: le[0] else: le let objType = leDotExpr[0].typ if hasDestructor(objType): if objType.attachedOps[attachedDestructor] != nil and sfOverriden in objType.attachedOps[attachedDestructor].flags: - localError(c.graph.config, n.info, errGenerated, """Assignment to discriminant for object's with user defined destructor is not supported, object must have default destructor. + localError(c.graph.config, n.info, errGenerated, """Assignment to discriminant for objects with user defined destructor is not supported, object must have default destructor. It is best to factor out piece of object that needs custom destructor into separate object or not use discriminator assignment""") result.add newTree(nkFastAsgn, le, tmp) return @@ -353,9 +433,7 @@ It is best to factor out piece of object that needs custom destructor into separ notExpr.add newSymNode(createMagic(c.graph, "not", mNot)) notExpr.add cond result.add newTree(nkIfStmt, newTree(nkElifBranch, notExpr, genOp(c, branchDestructor, le))) - result.add newTree(nkFastAsgn, le, tmp) - else: - result.add newTree(nkFastAsgn, le, tmp) + result.add newTree(nkFastAsgn, le, tmp) proc genWasMoved(n: PNode; c: var Con): PNode = result = newNodeI(nkCall, n.info) @@ -369,7 +447,7 @@ proc genDefaultCall(t: PType; c: Con; info: TLineInfo): PNode = result.add(newSymNode(createMagic(c.graph, "default", mDefault))) result.typ = t -proc destructiveMoveVar(n: PNode; c: var Con): PNode = +proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): PNode = # generate: (let tmp = v; reset(v); tmp) if not hasDestructor(n.typ): result = copyTree(n) @@ -388,28 +466,21 @@ proc destructiveMoveVar(n: PNode; c: var Con): PNode = v.add(vpart) result.add v - result.add genWasMoved(skipConv(n), c) + let wasMovedCall = genWasMoved(skipConv(n), c) + result.add wasMovedCall result.add tempAsNode -proc sinkParamIsLastReadCheck(c: var Con, s: PNode) = - assert s.kind == nkSym and s.sym.kind == skParam - if not isLastRead(s, c): - localError(c.graph.config, c.otherRead.info, "sink parameter `" & $s.sym.name.s & - "` is already consumed at " & toFileLineCol(c. graph.config, s.info)) - proc isCapturedVar(n: PNode): bool = let root = getRoot(n) if root != nil: result = root.name.s[0] == ':' -proc passCopyToSink(n: PNode; c: var Con): PNode = +proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode = result = newNodeIT(nkStmtListExpr, n.info, n.typ) - let tmp = getTemp(c, n.typ, n.info) - when not scopeBasedDestruction: - c.addTopVar(tmp) + let tmp = getTemp(c, s, n.typ, n.info) if hasDestructor(n.typ): result.add genWasMoved(tmp, c) var m = genCopy(c, tmp, n) - m.add p(n, c, normal) + m.add p(n, c, s, normal) result.add m if isLValue(n) and not isCapturedVar(n) and n.typ.skipTypes(abstractInst).kind != tyRef and c.inSpawn == 0: message(c.graph.config, n.info, hintPerformance, @@ -418,7 +489,7 @@ proc passCopyToSink(n: PNode; c: var Con): PNode = else: if c.graph.config.selectedGC in {gcArc, gcOrc}: assert(not containsGarbageCollectedRef(n.typ)) - result.add newTree(nkAsgn, tmp, p(n, c, normal)) + result.add newTree(nkAsgn, tmp, p(n, c, s, normal)) # Since we know somebody will take over the produced copy, there is # no need to destroy it. result.add tmp @@ -442,161 +513,24 @@ proc containsConstSeq(n: PNode): bool = if containsConstSeq(son): return true else: discard -proc handleTmpDestroys(c: var Con; body: PNode; t: PType; - oldHasUnstructuredCf, oldTmpDestroysLen: int) = - if c.hasUnstructuredCf == oldHasUnstructuredCf: - # no need for a try-finally statement: - if body.kind == nkStmtList: - for i in countdown(c.scopeDestroys.high, oldTmpDestroysLen): - body.add c.scopeDestroys[i] - elif isEmptyType(t): - var n = newNodeI(nkStmtList, body.info) - n.add body[^1] - for i in countdown(c.scopeDestroys.high, oldTmpDestroysLen): - n.add c.scopeDestroys[i] - body[^1] = n - elif body.kind == nkStmtListExpr and body.len > 0 and body[^1].kind == nkSym: - # special case: Do not translate (x; y; sym) into - # (x; y; tmp = sym; destroy(x); destroy(y); tmp ) - # but into - # (x; y; destroy(x); destroy(y); sym ) - let sym = body[^1] - body[^1] = c.scopeDestroys[^1] - for i in countdown(c.scopeDestroys.high - 1, oldTmpDestroysLen): - body.add c.scopeDestroys[i] - body.add sym - else: - # fun ahead: We have to transform (x; y; E()) into - # (x; y; tmp = E(); destroy(x); destroy(y); tmp ) - let t2 = body[^1].typ - let tmp = getTemp(c, t2, body.info) - when not scopeBasedDestruction: - c.addTopVar(tmp) - # the tmp does not have to be initialized - var n = newNodeIT(nkStmtListExpr, body.info, t2) - n.add newTree(nkFastAsgn, tmp, body[^1]) - for i in countdown(c.scopeDestroys.high, oldTmpDestroysLen): - n.add c.scopeDestroys[i] - n.add tmp - body[^1] = n - #c.scopeDestroys.add genDestroy(c, tmp) - else: - # unstructured control flow was used, use a 'try finally' to ensure - # destruction: - if isEmptyType(t): - var n = newNodeI(nkStmtList, body.info) - for i in countdown(c.scopeDestroys.high, oldTmpDestroysLen): - n.add c.scopeDestroys[i] - body[^1] = newTryFinally(body[^1], n) - else: - # fun ahead: We have to transform (x; y; E()) into - # ((try: tmp = (x; y; E()); finally: destroy(x); destroy(y)); tmp ) - let t2 = body[^1].typ - let tmp = getTemp(c, t2, body.info) - when not scopeBasedDestruction: - c.addTopVar(tmp) - # the tmp does not have to be initialized - var fin = newNodeI(nkStmtList, body.info) - for i in countdown(c.scopeDestroys.high, oldTmpDestroysLen): - fin.add c.scopeDestroys[i] - var n = newNodeIT(nkStmtListExpr, body.info, t2) - n.add newTryFinally(newTree(nkFastAsgn, tmp, body[^1]), fin) - n.add tmp - body[^1] = n - #c.scopeDestroys.add genDestroy(c, tmp) - - c.scopeDestroys.setLen oldTmpDestroysLen - -proc handleNested(n, dest: PNode; c: var Con; mode: ProcessMode): PNode = - template processCall(node: PNode): PNode = - if node.typ == nil or dest == nil: - p(node, c, mode) - else: - moveOrCopy(dest, node, c) - - proc handleScope(n, dest: PNode; t: PType; - takeOver: Natural; c: var Con; mode: ProcessMode): PNode = - let oldHasUnstructuredCf = c.hasUnstructuredCf - let oldTmpDestroysLen = c.scopeDestroys.len - result = shallowCopy(n) - for i in 0.. oldTmpDestroysLen: - handleTmpDestroys(c, result, t, oldHasUnstructuredCf, oldTmpDestroysLen) - else: - setLen(result.sons, last) - if c.scopeDestroys.len > oldTmpDestroysLen: - handleTmpDestroys(c, result, t, oldHasUnstructuredCf, oldTmpDestroysLen) - if result.kind != nkFinally: - result.add processCall(n[last]) - else: - result = newTree(nkStmtListExpr, result, processCall(n[last])) - result.typ = t - - case n.kind - of nkStmtList, nkStmtListExpr: - if n.len == 0: return n - result = shallowCopy(n) - let last = n.len - 1 - for i in 0.. 0: # unpacked tuple needs reset at every loop iteration res.add newTree(nkFastAsgn, v, genDefaultCall(v.typ, c, v.info)) elif sfThread notin v.sym.flags: # do not destroy thread vars for now at all for consistency. - if sfGlobal in v.sym.flags: + if sfGlobal in v.sym.flags and s.parent == nil: c.graph.globalDestructors.add genDestroy(c, v) else: - c.destroys.add genDestroy(c, v) + s.final.add genDestroy(c, v) if ri.kind == nkEmpty and c.inLoop > 0: - res.add moveOrCopy(v, genDefaultCall(v.typ, c, v.info), c, isDecl = true) + res.add moveOrCopy(v, genDefaultCall(v.typ, c, v.info), c, s, isDecl = true) elif ri.kind != nkEmpty: - res.add moveOrCopy(v, ri, c, isDecl = true) - -proc pVarScoped(v: PNode; c: var Con; ri, res: PNode) = - if not containsOrIncl(c.declaredVars, v.sym.id): - c.addTopVar(v) - if isUnpackedTuple(v): - if c.inLoop > 0: - # unpacked tuple needs reset at every loop iteration - res.add newTree(nkFastAsgn, v, genDefaultCall(v.typ, c, v.info)) - elif {sfGlobal, sfThread} * v.sym.flags == {sfGlobal}: - c.graph.globalDestructors.add genDestroy(c, v) - else: - # We always translate 'var v = f()' into bitcopies. If 'v' is in a loop, - # the destruction at the loop end will free the resources. Other assignments - # will destroy the old value inside 'v'. If we have 'var v' without an initial - # default value we translate it into 'var v = default()'. We translate - # 'var x = someGlobal' into 'var v = default(); `=`(v, someGlobal). The - # lack of copy constructors is really beginning to hurt us. :-( - #if c.inDangerousBranch == 0: v.sym.flags.incl sfNoInit - c.scopeDestroys.add genDestroy(c, v) - if ri.kind == nkEmpty and c.inLoop > 0: - res.add moveOrCopy(v, genDefaultCall(v.typ, c, v.info), c, isDecl = true) - elif ri.kind != nkEmpty: - res.add moveOrCopy(v, ri, c, isDecl = true) + res.add moveOrCopy(v, ri, c, s, isDecl = true) template handleNestedTempl(n: untyped, processCall: untyped) = + template maybeVoid(child, s): untyped = + if isEmptyType(child.typ): p(child, c, s, normal) + else: processCall(child, s) + case n.kind of nkStmtList, nkStmtListExpr: + # a statement list does not open a new scope if n.len == 0: return n result = copyNode(n) for i in 0.. 0): - result[i] = p(n[i], c, sinkArg) + result[i] = p(n[i], c, s, sinkArg) else: - result[i] = p(n[i], c, normal) + result[i] = p(n[i], c, s, normal) - if isDangerous: - dec c.inDangerousBranch + when false: + if isDangerous: + dec c.inDangerousBranch if n[0].kind == nkSym and n[0].sym.magic in {mNew, mNewFinalize}: result[0] = copyTree(n[0]) @@ -873,15 +805,14 @@ proc p(n: PNode; c: var Con; mode: ProcessMode): PNode = let destroyOld = genDestroy(c, result[1]) result = newTree(nkStmtList, destroyOld, result) else: - result[0] = p(n[0], c, normal) - when scopeBasedDestruction: - if canRaise(n[0]): inc c.hasUnstructuredCf + result[0] = p(n[0], c, s, normal) + if canRaise(n[0]): s.needsTry = true if mode == normal: - result = ensureDestruction(result, c) + result = ensureDestruction(result, c, s) of nkDiscardStmt: # Small optimization result = shallowCopy(n) if n[0].kind != nkEmpty: - result[0] = p(n[0], c, normal) + result[0] = p(n[0], c, s, normal) else: result[0] = copyNode(n[0]) of nkVarSection, nkLetSection: @@ -891,27 +822,24 @@ proc p(n: PNode; c: var Con; mode: ProcessMode): PNode = var ri = it[^1] if it.kind == nkVarTuple and hasDestructor(ri.typ): let x = lowerTupleUnpacking(c.graph, it, c.owner) - result.add p(x, c, consumed) + result.add p(x, c, s, consumed) elif it.kind == nkIdentDefs and hasDestructor(it[0].typ) and not isCursor(it[0]): for j in 0.. 0: ri = genDefaultCall(v.typ, c, v.info) if ri.kind != nkEmpty: - result.add moveOrCopy(v, ri, c, isDecl = true) + result.add moveOrCopy(v, ri, c, s, isDecl = true) else: # keep the var but transform 'ri': var v = copyNode(n) var itCopy = copyNode(it) for j in 0.. 0 and isDangerousSeq(ri.typ): result = genCopy(c, dest, ri) - result.add p(ri, c, consumed) + result.add p(ri, c, s, consumed) else: - result = genSink(c, dest, p(ri, c, consumed), isDecl) + result = genSink(c, s, dest, p(ri, c, s, consumed), isDecl) of nkObjConstr, nkTupleConstr, nkClosure, nkCharLit..nkNilLit: - result = genSink(c, dest, p(ri, c, consumed), isDecl) + result = genSink(c, s, dest, p(ri, c, s, consumed), isDecl) of nkSym: if isSinkParam(ri.sym) and isLastRead(ri, c): # Rule 3: `=sink`(x, z); wasMoved(z) - #sinkParamIsLastReadCheck(c, ri) - let snk = genSink(c, dest, ri, isDecl) + let snk = genSink(c, s, dest, ri, isDecl) result = newTree(nkStmtList, snk, genWasMoved(ri, c)) elif ri.sym.kind != skParam and ri.sym.owner == c.owner and isLastRead(ri, c) and canBeMoved(c, dest.typ): # Rule 3: `=sink`(x, z); wasMoved(z) - let snk = genSink(c, dest, ri, isDecl) + let snk = genSink(c, s, dest, ri, isDecl) result = newTree(nkStmtList, snk, genWasMoved(ri, c)) else: result = genCopy(c, dest, ri) - result.add p(ri, c, consumed) - of nkHiddenSubConv, nkHiddenStdConv, nkConv: - when false: - result = moveOrCopy(dest, ri[1], c, isDecl) - if not sameType(ri.typ, ri[1].typ): - let copyRi = copyTree(ri) - copyRi[1] = result[^1] - result[^1] = copyRi - else: - result = genSink(c, dest, p(ri, c, sinkArg), isDecl) - of nkObjDownConv, nkObjUpConv: - when false: - result = moveOrCopy(dest, ri[0], c, isDecl) - let copyRi = copyTree(ri) - copyRi[0] = result[^1] - result[^1] = copyRi - else: - result = genSink(c, dest, p(ri, c, sinkArg), isDecl) + result.add p(ri, c, s, consumed) + of nkHiddenSubConv, nkHiddenStdConv, nkConv, nkObjDownConv, nkObjUpConv: + result = genSink(c, s, dest, p(ri, c, s, sinkArg), isDecl) of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt: - when scopeBasedDestruction: - result = handleNested(ri, dest, c, normal) - else: - handleNestedTempl(ri): moveOrCopy(dest, node, c, isDecl) + template process(child, s): untyped = moveOrCopy(dest, child, c, s, isDecl) + handleNestedTempl(ri, process) + of nkRaiseStmt: + result = pRaiseStmt(ri, c, s) else: if isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c) and canBeMoved(c, dest.typ): # Rule 3: `=sink`(x, z); wasMoved(z) - let snk = genSink(c, dest, ri, isDecl) + let snk = genSink(c, s, dest, ri, isDecl) result = newTree(nkStmtList, snk, genWasMoved(ri, c)) else: result = genCopy(c, dest, ri) - result.add p(ri, c, consumed) + result.add p(ri, c, s, consumed) proc computeUninit(c: var Con) = if not c.uninitComputed: @@ -1102,30 +998,19 @@ proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode = echo "\n### ", owner.name.s, ":\nCFG:" echoCfg(c.g) echo n + + var scope: Scope + let body = p(n, c, scope, normal) + if owner.kind in {skProc, skFunc, skMethod, skIterator, skConverter}: let params = owner.typ.n for i in 1.. 0: - result.add c.topLevelVars - if c.destroys.len > 0 or c.scopeDestroys.len > 0: - reverse c.destroys.sons - var fin: PNode - if owner.kind == skModule: - fin = newTryFinally(body, extractDestroysForTemporaries(c, c.destroys)) - g.globalDestructors.add c.destroys - else: - fin = newTryFinally(body, c.destroys) - for i in countdown(c.scopeDestroys.high, 0): fin[1][0].add c.scopeDestroys[i] - result.add fin - else: - result.add body + result = toTree(scope, body) dbg: echo ">---------transformed-to--------->" echo renderTree(result, {renderIds}) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index a2515357b..c88ff3609 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -229,8 +229,8 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags): PNode = a[0][2] = newSymNode(symbol, a[0][2].info) elif a.len == 1: - # count number of ``except: body`` blocks - inc catchAllExcepts + # count number of ``except: body`` blocks + inc catchAllExcepts else: # support ``except KeyError, ValueError, ... : body`` diff --git a/compiler/spawn.nim b/compiler/spawn.nim index 9d837b993..15bed77b0 100644 --- a/compiler/spawn.nim +++ b/compiler/spawn.nim @@ -10,7 +10,7 @@ ## This module implements threadpool's ``spawn``. import ast, types, idents, magicsys, msgs, options, modulegraphs, - lowerings, liftdestructors + lowerings, liftdestructors, renderer from trees import getMagic, getRoot proc callProc(a: PNode): PNode = @@ -321,7 +321,7 @@ proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: P result = newNodeI(nkStmtList, n.info) if n.kind notin nkCallKinds: - localError(g.config, n.info, "'spawn' takes a call expression") + localError(g.config, n.info, "'spawn' takes a call expression; got " & $n) return if optThreadAnalysis in g.config.globalOptions: if {tfThread, tfNoSideEffect} * n[0].typ.flags == {}: diff --git a/doc/destructors.rst b/doc/destructors.rst index b06d78bbe..4285bad8b 100644 --- a/doc/destructors.rst +++ b/doc/destructors.rst @@ -283,8 +283,8 @@ Rewrite rules around the complete routine body. 2. The produced ``finally`` section is wrapped around the enclosing scope. -The current implementation follows strategy (1). This means that resources are -not destroyed at the scope exit, but at the proc exit. +The current implementation follows strategy (2). This means that resources are +destroyed at the scope exit. :: diff --git a/tests/arc/tarcmisc.nim b/tests/arc/tarcmisc.nim index 898efd138..e66d9a75f 100644 --- a/tests/arc/tarcmisc.nim +++ b/tests/arc/tarcmisc.nim @@ -173,3 +173,39 @@ proc bug14495 = echo o[] bug14495() + +when false: + # bug #14396 + type + Spinny = ref object + t: ref int + text: string + + proc newSpinny*(): Spinny = + Spinny(t: new(int), text: "hello") + + proc spinnyLoop(x: ref int, spinny: sink Spinny) = + echo x[] + + proc start*(spinny: sink Spinny) = + spinnyLoop(spinny.t, spinny) + + var spinner1 = newSpinny() + spinner1.start() + + # bug #14345 + + type + SimpleLoopB = ref object + children: seq[SimpleLoopB] + parent: SimpleLoopB + + proc addChildLoop(self: SimpleLoopB, loop: SimpleLoopB) = + self.children.add loop + + proc setParent(self: SimpleLoopB, parent: SimpleLoopB) = + self.parent = parent + self.parent.addChildLoop(self) + + var l = SimpleLoopB() + l.setParent(l) diff --git a/tests/arc/tcontrolflow.nim b/tests/arc/tcontrolflow.nim index efe51cc32..478806567 100644 --- a/tests/arc/tcontrolflow.nim +++ b/tests/arc/tcontrolflow.nim @@ -11,15 +11,13 @@ begin true if end true 7 +##index 2 not in 0 .. 1## ''' cmd: "nim c --gc:arc -d:danger $file" - disabled: "true" """ # we use the -d:danger switch to detect uninitialized stack # slots more reliably (there shouldn't be any, of course). -# XXX Enable once scope based destruction works! - type Foo = object id: int @@ -68,12 +66,25 @@ proc run(data: Control) = evt.control = data if evt.button == 1: discard - else: + else: return - + echo data.x var c = Control(x: 7) run(c) +proc sysFatal(exceptn: typedesc, message: string) {.inline, noreturn.} = + var buf = newStringOfCap(200) + add(buf, "##") + add(buf, message) + add(buf, "##") + echo buf + +proc ifexpr(i, a, b: int) {.compilerproc, noinline.} = + sysFatal(IndexDefect, + if b < a: "index out of bounds, the container is empty" + else: "index " & $i & " not in " & $a & " .. " & $b) + +ifexpr(2, 0, 1) diff --git a/tests/arc/tmovebug.nim b/tests/arc/tmovebug.nim index 951979a46..ec0fce9a8 100644 --- a/tests/arc/tmovebug.nim +++ b/tests/arc/tmovebug.nim @@ -247,7 +247,7 @@ iterator combinations[T](s: openarray[T], k: int): seq[T] = break type - UndefEx = object of Exception + UndefEx = object of ValueError proc main2 = var delayedSyms = @[1, 2, 3] @@ -318,10 +318,10 @@ proc `=sink`(dest: var O2, src: O2) = var testSeq: O2 -proc Update(): void = +proc update() = # testSeq.add(0) # uncommenting this line fixes the leak testSeq = O2(s: @[]) testSeq.s.add(0) for i in 1..3: - Update() + update() diff --git a/tests/arc/tthread.nim b/tests/arc/tthread.nim index c653c753f..8a55a666e 100644 --- a/tests/arc/tthread.nim +++ b/tests/arc/tthread.nim @@ -14,7 +14,7 @@ type p: int MyObjRef = ref MyObj -proc `=destroy`(x: var MyObj) = +proc `=destroy`(x: var MyObj) = if x.p != 0: echo "destroyed" @@ -48,16 +48,16 @@ proc thread5(x: sink MyObjRef): MyObjRef = os.sleep(1000) result = x -proc ref_forwarding_test = +proc ref_forwarding_test = var x = new(MyObj) x[].p = 2 var y = spawn thread4(x) - -proc ref_sink_forwarding_test = + +proc ref_sink_forwarding_test = var x = new(MyObj) x[].p = 2 var y = spawn thread5(x) ref_forwarding_test() -ref_sink_forwarding_test() +ref_sink_forwarding_test() sync() diff --git a/tests/arc/tweave.nim b/tests/arc/tweave.nim new file mode 100644 index 000000000..220d65f97 --- /dev/null +++ b/tests/arc/tweave.nim @@ -0,0 +1,154 @@ +discard """ + outputsub: '''Success''' + cmd: '''nim c --gc:arc --threads:on $file''' + disabled: "bsd" +""" + +# bug #13936 + +import std/atomics + +const MemBlockSize = 256 + +type + ChannelSPSCSingle* = object + full{.align: 128.}: Atomic[bool] + itemSize*: uint8 + buffer*{.align: 8.}: UncheckedArray[byte] + +proc `=`( + dest: var ChannelSPSCSingle, + source: ChannelSPSCSingle + ) {.error: "A channel cannot be copied".} + +proc initialize*(chan: var ChannelSPSCSingle, itemsize: SomeInteger) {.inline.} = + ## If ChannelSPSCSingle is used intrusive another data structure + ## be aware that it should be the last part due to ending by UncheckedArray + ## Also due to 128 bytes padding, it automatically takes half + ## of the default MemBlockSize + assert itemsize.int in 0 .. int high(uint8) + assert itemSize.int + + sizeof(chan.itemsize) + + sizeof(chan.full) < MemBlockSize + + chan.itemSize = uint8 itemsize + chan.full.store(false, moRelaxed) + +func isEmpty*(chan: var ChannelSPSCSingle): bool {.inline.} = + not chan.full.load(moAcquire) + +func tryRecv*[T](chan: var ChannelSPSCSingle, dst: var T): bool {.inline.} = + ## Try receiving the item buffered in the channel + ## Returns true if successful (channel was not empty) + ## + ## ⚠ Use only in the consumer thread that reads from the channel. + assert (sizeof(T) == chan.itemsize.int) or + # Support dummy object + (sizeof(T) == 0 and chan.itemsize == 1) + + let full = chan.full.load(moAcquire) + if not full: + return false + dst = cast[ptr T](chan.buffer.addr)[] + chan.full.store(false, moRelease) + return true + +func trySend*[T](chan: var ChannelSPSCSingle, src: sink T): bool {.inline.} = + ## Try sending an item into the channel + ## Reurns true if successful (channel was empty) + ## + ## ⚠ Use only in the producer thread that writes from the channel. + assert (sizeof(T) == chan.itemsize.int) or + # Support dummy object + (sizeof(T) == 0 and chan.itemsize == 1) + + let full = chan.full.load(moAcquire) + if full: + return false + cast[ptr T](chan.buffer.addr)[] = src + chan.full.store(true, moRelease) + return true + +# Sanity checks +# ------------------------------------------------------------------------------ +when isMainModule: + + when not compileOption("threads"): + {.error: "This requires --threads:on compilation flag".} + + template sendLoop[T](chan: var ChannelSPSCSingle, + data: sink T, + body: untyped): untyped = + while not chan.trySend(data): + body + + template recvLoop[T](chan: var ChannelSPSCSingle, + data: var T, + body: untyped): untyped = + while not chan.tryRecv(data): + body + + type + ThreadArgs = object + ID: WorkerKind + chan: ptr ChannelSPSCSingle + + WorkerKind = enum + Sender + Receiver + + template Worker(id: WorkerKind, body: untyped): untyped {.dirty.} = + if args.ID == id: + body + + proc thread_func(args: ThreadArgs) = + + # Worker RECEIVER: + # --------- + # <- chan + # <- chan + # <- chan + # + # Worker SENDER: + # --------- + # chan <- 42 + # chan <- 53 + # chan <- 64 + Worker(Receiver): + var val: int + for j in 0 ..< 10: + args.chan[].recvLoop(val): + # Busy loop, in prod we might want to yield the core/thread timeslice + discard + echo " Receiver got: ", val + doAssert val == 42 + j*11 + + Worker(Sender): + doAssert args.chan.full.load(moRelaxed) == false + for j in 0 ..< 10: + let val = 42 + j*11 + args.chan[].sendLoop(val): + # Busy loop, in prod we might want to yield the core/thread timeslice + discard + echo "Sender sent: ", val + + proc main() = + echo "Testing if 2 threads can send data" + echo "-----------------------------------" + var threads: array[2, Thread[ThreadArgs]] + + var chan = cast[ptr ChannelSPSCSingle](allocShared(MemBlockSize)) + chan[].initialize(itemSize = sizeof(int)) + + createThread(threads[0], thread_func, ThreadArgs(ID: Receiver, chan: chan)) + createThread(threads[1], thread_func, ThreadArgs(ID: Sender, chan: chan)) + + joinThread(threads[0]) + joinThread(threads[1]) + + freeShared(chan) + + echo "-----------------------------------" + echo "Success" + + main() diff --git a/tests/async/testmanyasyncevents.nim b/tests/async/testmanyasyncevents.nim index 5a103736f..9fdd01b4f 100644 --- a/tests/async/testmanyasyncevents.nim +++ b/tests/async/testmanyasyncevents.nim @@ -1,8 +1,9 @@ discard """ output: ''' hasPendingOperations: false -triggerCount: 100 +triggerCount: 100 ''' +disabled: "windows" """ import asyncDispatch diff --git a/tests/casestmt/t12785.nim b/tests/casestmt/t12785.nim index bf0f30d42..7177fb9c2 100644 --- a/tests/casestmt/t12785.nim +++ b/tests/casestmt/t12785.nim @@ -3,6 +3,8 @@ discard """ output: '''copied copied 2 +destroyed +destroyed copied copied 2 @@ -37,9 +39,9 @@ proc test(a: range[0..1], arg: ObjWithDestructor) = inc iteration case a - of 0: - assert false - of 1: + of 0: + assert false + of 1: echo b if iteration == 2: break diff --git a/tests/destructor/tdestructor.nim b/tests/destructor/tdestructor.nim index 9fd47fe00..e081eb251 100644 --- a/tests/destructor/tdestructor.nim +++ b/tests/destructor/tdestructor.nim @@ -1,27 +1,27 @@ discard """ - output: '''---- + output: '''----1 myobj constructed myobj destroyed ----- +----2 mygeneric1 constructed mygeneric1 destroyed ----- +----3 mygeneric2 constructed mygeneric2 destroyed myobj destroyed ----- +----4 mygeneric3 constructed mygeneric1 destroyed ----- +----5 mydistinctObj constructed myobj destroyed mygeneric2 destroyed ------------------- ----- ----- -myobj destroyed +------------------8 mygeneric1 destroyed ---- +----6 +myobj destroyed +----7 +---9 myobj destroyed myobj destroyed ''' @@ -114,19 +114,19 @@ proc mydistinctObj = echo "mydistinctObj constructed" -echo "----" +echo "----1" myobj() -echo "----" +echo "----2" mygeneric1() -echo "----" +echo "----3" mygeneric2[int](10) -echo "----" +echo "----4" mygeneric3() -echo "----" +echo "----5" mydistinctObj() proc caseobj = @@ -134,16 +134,16 @@ proc caseobj = var o1 = TCaseObj(kind: A, x: TMyGeneric1[int](x: 10)) block: - echo "----" + echo "----6" var o2 = TCaseObj(kind: B, y: open()) block: - echo "----" + echo "----7" var o3 = TCaseObj(kind: D, innerKind: B, r: "test", p: TMyGeneric3[int, float, string](x: 10, y: 1.0, z: "test")) -echo "------------------" +echo "------------------8" caseobj() proc caseobj_test_sink: TCaseObj = @@ -153,7 +153,7 @@ proc caseobj_test_sink: TCaseObj = result = TCaseObj(kind: B, y: open()) -echo "---" +echo "---9" discard caseobj_test_sink() # issue #14315 From af27e6bdea63bbf66718193ec44bc61e745ded38 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Sat, 4 Jul 2020 17:45:07 +0200 Subject: [PATCH 0341/3768] Fix #14396 (#14793) * Correct Left-To-Right evaluation of proc args * Fix CPP backend * Add testcase * closes #14396 * closes #14345 * Improve test and optimize * Improve testcase and optimize literals * Fix bug * Expand testcase and use DFA to optimize * Turn genParams into proc * Turn withTmpIfNeeded into a proc * Cleanup * Fix crash * Better analysis * Cleanup * Trailing newline.. * Fix build * Tiny cleanup Co-authored-by: Andreas Rumpf --- compiler/ccgcalls.nim | 145 ++++++++++++++++++++--------- compiler/closureiters.nim | 2 +- tests/arc/tarcmisc.nim | 52 +++++------ tests/ccgbugs/targ_lefttoright.nim | 70 ++++++++++++++ 4 files changed, 198 insertions(+), 71 deletions(-) create mode 100644 tests/ccgbugs/targ_lefttoright.nim diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 2c5d306a3..5f99f357d 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -217,21 +217,30 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode): Rope = internalError(p.config, "openArrayLoc: " & typeToString(a.t)) else: internalError(p.config, "openArrayLoc: " & typeToString(a.t)) -proc genArgStringToCString(p: BProc, n: PNode): Rope {.inline.} = +proc withTmpIfNeeded(p: BProc, a: TLoc, needsTmp: bool): TLoc = + if needsTmp: + var tmp: TLoc + getTemp(p, a.lode.typ, tmp, needsInit=false) + genAssignment(p, tmp, a, {}) + tmp + else: + a + +proc genArgStringToCString(p: BProc, n: PNode, needsTmp: bool): Rope {.inline.} = var a: TLoc initLocExpr(p, n[0], a) - result = ropecg(p.module, "#nimToCStringConv($1)", [a.rdLoc]) + ropecg(p.module, "#nimToCStringConv($1)", [withTmpIfNeeded(p, a, needsTmp).rdLoc]) -proc genArg(p: BProc, n: PNode, param: PSym; call: PNode): Rope = +proc genArg(p: BProc, n: PNode, param: PSym; call: PNode, needsTmp = false): Rope = var a: TLoc if n.kind == nkStringToCString: - result = genArgStringToCString(p, n) + result = genArgStringToCString(p, n, needsTmp) elif skipTypes(param.typ, abstractVar).kind in {tyOpenArray, tyVarargs}: var n = if n.kind != nkHiddenAddr: n else: n[0] result = openArrayLoc(p, param.typ, n) elif ccgIntroducedPtr(p.config, param, call[0].typ[0]): initLocExpr(p, n, a) - result = addrLoc(p.config, a) + result = addrLoc(p.config, withTmpIfNeeded(p, a, needsTmp)) elif p.module.compileToCpp and param.typ.kind in {tyVar} and n.kind == nkHiddenAddr: initLocExprSingleUse(p, n[0], a) @@ -246,26 +255,85 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode): Rope = result = rdLoc(a) else: initLocExprSingleUse(p, n, a) - result = rdLoc(a) + result = rdLoc(withTmpIfNeeded(p, a, needsTmp)) -proc genArgNoParam(p: BProc, n: PNode): Rope = +proc genArgNoParam(p: BProc, n: PNode, needsTmp = false): Rope = var a: TLoc if n.kind == nkStringToCString: - result = genArgStringToCString(p, n) + result = genArgStringToCString(p, n, needsTmp) else: initLocExprSingleUse(p, n, a) - result = rdLoc(a) + result = rdLoc(withTmpIfNeeded(p, a, needsTmp)) -template genParamLoop(params) {.dirty.} = - if i < typ.len: - assert(typ.n[i].kind == nkSym) - let paramType = typ.n[i] - if not paramType.typ.isCompileTimeOnly: - if params != nil: params.add(~", ") - params.add(genArg(p, ri[i], paramType.sym, ri)) +from dfa import instrTargets, InstrTargetKind + +proc potentialAlias(n: PNode, potentialWrites: seq[PNode]): bool = + for p in potentialWrites: + if instrTargets(p, n) != None: + return true + +proc skipTrivialIndirections(n: PNode): PNode = + result = n + while true: + case result.kind + of {nkDerefExpr, nkHiddenDeref, nkAddr, nkHiddenAddr, nkObjDownConv, nkObjUpConv}: + result = result[0] + of {nkHiddenStdConv, nkHiddenSubConv}: + result = result[1] + else: break + +proc getPotentialWrites(n: PNode, mutate = false): seq[PNode] = + case n.kind: + of nkLiterals, nkIdent: discard + of nkSym: + if mutate: result.add n + of nkAsgn, nkFastAsgn: + result.add getPotentialWrites(n[0], true) + result.add getPotentialWrites(n[1], mutate) + of nkAddr, nkHiddenAddr: + result.add getPotentialWrites(n[0], true) + of nkCallKinds: #TODO: Find out why in f += 1, f is a nkSym and not a nkHiddenAddr + for s in n.sons: + result.add getPotentialWrites(s, true) else: - if params != nil: params.add(~", ") - params.add(genArgNoParam(p, ri[i])) + for s in n.sons: + result.add getPotentialWrites(s, mutate) + +proc getPotentialReads(n: PNode): seq[PNode] = + case n.kind: + of nkLiterals, nkIdent: discard + of nkSym: result.add n + else: + for s in n.sons: + result.add getPotentialReads(s) + +proc genParams(p: BProc, ri: PNode, typ: PType): Rope = + # We must generate temporaries in cases like #14396 + # to keep the strict Left-To-Right evaluation + var needTmp = newSeq[bool](ri.len - 1) + var potentialWrites: seq[PNode] + for i in countdown(ri.len - 1, 1): + if ri[i].skipTrivialIndirections.kind == nkSym: + needTmp[i - 1] = potentialAlias(ri[i], potentialWrites) + else: + for n in getPotentialReads(ri[i]): + if not needTmp[i - 1]: + needTmp[i - 1] = potentialAlias(n, potentialWrites) + potentialWrites.add getPotentialWrites(ri[i]) + if ri[i].kind == nkHiddenAddr: + # Optimization: don't use a temp, if we would only take the adress anyway + needTmp[i - 1] = false + + for i in 1.. 0 and isInactiveDestructorCall(p, e): - return - if e[0].typ.skipTypes({tyGenericInst, tyAlias, tySink, tyOwned}).callConv == ccClosure: - genClosureCall(p, nil, e, d) - elif e[0].kind == nkSym and sfInfixCall in e[0].sym.flags: - genInfixCall(p, nil, e, d) - elif e[0].kind == nkSym and sfNamedParamCall in e[0].sym.flags: - genNamedParamCall(p, e, d) - else: - genPrefixCall(p, nil, e, d) - postStmtActions(p) - proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) = + if p.withinBlockLeaveActions > 0 and isInactiveDestructorCall(p, ri): + return if ri[0].typ.skipTypes({tyGenericInst, tyAlias, tySink, tyOwned}).callConv == ccClosure: genClosureCall(p, le, ri, d) elif ri[0].kind == nkSym and sfInfixCall in ri[0].sym.flags: @@ -691,3 +746,5 @@ proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) = else: genPrefixCall(p, le, ri, d) postStmtActions(p) + +proc genCall(p: BProc, e: PNode, d: var TLoc) = genAsgnCall(p, nil, e, d) diff --git a/compiler/closureiters.nim b/compiler/closureiters.nim index 675f1ae41..2248f956e 100644 --- a/compiler/closureiters.nim +++ b/compiler/closureiters.nim @@ -129,7 +129,7 @@ # break :stateLoop import - ast, astalgo, msgs, idents, + ast, msgs, idents, renderer, magicsys, lowerings, lambdalifting, modulegraphs, lineinfos type diff --git a/tests/arc/tarcmisc.nim b/tests/arc/tarcmisc.nim index e66d9a75f..b6d9d781d 100644 --- a/tests/arc/tarcmisc.nim +++ b/tests/arc/tarcmisc.nim @@ -15,6 +15,7 @@ destroyed: false (x: "8") (x: "9") (x: "10") +0 closed destroying variable ''' @@ -174,38 +175,37 @@ proc bug14495 = bug14495() -when false: - # bug #14396 - type - Spinny = ref object - t: ref int - text: string +# bug #14396 +type + Spinny = ref object + t: ref int + text: string - proc newSpinny*(): Spinny = - Spinny(t: new(int), text: "hello") +proc newSpinny*(): Spinny = + Spinny(t: new(int), text: "hello") - proc spinnyLoop(x: ref int, spinny: sink Spinny) = - echo x[] +proc spinnyLoop(x: ref int, spinny: sink Spinny) = + echo x[] - proc start*(spinny: sink Spinny) = - spinnyLoop(spinny.t, spinny) +proc start*(spinny: sink Spinny) = + spinnyLoop(spinny.t, spinny) - var spinner1 = newSpinny() - spinner1.start() +var spinner1 = newSpinny() +spinner1.start() - # bug #14345 +# bug #14345 - type - SimpleLoopB = ref object - children: seq[SimpleLoopB] - parent: SimpleLoopB +type + SimpleLoopB = ref object + children: seq[SimpleLoopB] + parent: SimpleLoopB - proc addChildLoop(self: SimpleLoopB, loop: SimpleLoopB) = - self.children.add loop +proc addChildLoop(self: SimpleLoopB, loop: SimpleLoopB) = + self.children.add loop - proc setParent(self: SimpleLoopB, parent: SimpleLoopB) = - self.parent = parent - self.parent.addChildLoop(self) +proc setParent(self: SimpleLoopB, parent: SimpleLoopB) = + self.parent = parent + self.parent.addChildLoop(self) - var l = SimpleLoopB() - l.setParent(l) +var l = SimpleLoopB() +l.setParent(l) diff --git a/tests/ccgbugs/targ_lefttoright.nim b/tests/ccgbugs/targ_lefttoright.nim new file mode 100644 index 000000000..b107b2327 --- /dev/null +++ b/tests/ccgbugs/targ_lefttoright.nim @@ -0,0 +1,70 @@ +discard """ + nimout: '''1,2 +2,3 +2,2 +1,2 +1,2 +2,2 +1,2 +''' + output: '''1,2 +2,3 +1,2 +2,2 +1,2 +1,2 +2,2 +1,2 +''' +""" + +template test = + proc say(a, b: int) = + echo a,",",b + + var a = 1 + say a, (a += 1; a) #1,2 + + var b = 1 + say (b += 1; b), (b += 1; b) #2,3 + + type C = object {.byRef.} + i: int + + proc say(a, b: C) = + echo a.i,",",b.i + + proc `+=`(x: var C, y: C) = x.i += y.i + + var c = C(i: 1) + when nimvm: #XXX: This would output 2,2 in the VM, which is wrong + discard + else: + say c, (c += C(i: 1); c) #1,2 + + proc sayVar(a: var int, b: int) = + echo a,",",b + + var d = 1 + sayVar d, (d += 1; d) #2,2 + + var e = 1 + say (addr e)[], (e += 1; e) #1,2 + + var f = 1 + say f, if false: f + else: f += 1; f #1,2 + + var g = 1 + say g + 1, if false: g + else: g += 1; g #2,2 + + proc `+=+`(x: var int, y: int): int = (inc(x, y); x) + + var h = 1 + say h, h +=+ 1 # 1,2 + +test + +static: + test From 21b3ca636fa7eee53911ab134afe2805fd0bf47e Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Mon, 6 Jul 2020 05:00:25 -0300 Subject: [PATCH 0342/3768] Fix logging tiny bug (#14910) * Fix logging tiny bug * Update changelog.md Co-authored-by: Danil Yarantsev * Update changelog.md Co-authored-by: Danil Yarantsev * Update changelog.md Co-authored-by: Danil Yarantsev * https://github.com/nim-lang/Nim/pull/14910#discussion_r450012032 Co-authored-by: Danil Yarantsev --- changelog.md | 4 ++++ lib/pure/logging.nim | 10 +++++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index abe69a086..efad01515 100644 --- a/changelog.md +++ b/changelog.md @@ -124,6 +124,10 @@ - deprecations: `os.existsDir` => `dirExists`, `os.existsFile` => `fileExists` - Add `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, + eg. `debug` uses `console.debug`, `info` uses `console.info`, `warn` uses `console.warn`, etc. ## Language changes diff --git a/lib/pure/logging.nim b/lib/pure/logging.nim index 2637fdf9d..2e79cd3ca 100644 --- a/lib/pure/logging.nim +++ b/lib/pure/logging.nim @@ -364,7 +364,12 @@ method log*(logger: ConsoleLogger, level: Level, args: varargs[string, `$`]) = let ln = substituteLog(logger.fmtStr, level, args) when defined(js): let cln: cstring = ln - {.emit: "console.log(`cln`);".} + case level + of lvlDebug: {.emit: "console.debug(`cln`);".} + of lvlInfo: {.emit: "console.info(`cln`);".} + of lvlWarn: {.emit: "console.warn(`cln`);".} + of lvlError: {.emit: "console.error(`cln`);".} + else: {.emit: "console.log(`cln`);".} else: try: var handle = stdout @@ -504,7 +509,6 @@ when not defined(js): # ------ proc countLogLines(logger: RollingFileLogger): int = - result = 0 let fp = open(logger.baseName, fmRead) for line in fp.lines(): result.inc() @@ -531,7 +535,7 @@ when not defined(js): mode: FileMode = fmReadWrite, levelThreshold = lvlAll, fmtStr = defaultFmtStr, - maxLines = 1000, + maxLines: Positive = 1000, bufSize: int = -1): RollingFileLogger = ## Creates a new `RollingFileLogger<#RollingFileLogger>`_. ## From 842efab8ae867f001d5043a773ee96a7087c2334 Mon Sep 17 00:00:00 2001 From: Dean Eigenmann <7621705+decanus@users.noreply.github.com> Date: Mon, 6 Jul 2020 10:23:42 +0200 Subject: [PATCH 0343/3768] Update btrees.nim (#14916) --- compiler/btrees.nim | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/btrees.nim b/compiler/btrees.nim index 65edc54f3..18854d474 100644 --- a/compiler/btrees.nim +++ b/compiler/btrees.nim @@ -79,6 +79,9 @@ proc insert[Key, Val](h: Node[Key, Val], key: Key, val: Val): Node[Key, Val] = var j = 0 if not h.isInternal: while j < h.entries: + if eq(key, h.keys[j]): + h.vals[j] = val + return if less(key, h.keys[j]): break inc j for i in countdown(h.entries, j+1): From ebaf603c84a569b8de447fdb66bb05124d3a0772 Mon Sep 17 00:00:00 2001 From: narimiran Date: Mon, 6 Jul 2020 10:55:51 +0200 Subject: [PATCH 0344/3768] revert 0944b0f4 --- lib/impure/re.nim | 2 +- lib/pure/asyncnet.nim | 16 +++--- lib/pure/base64.nim | 2 +- lib/pure/httpclient.nim | 6 +-- lib/pure/net.nim | 110 +++++++++++++++++++-------------------- testament/backend.nim | 4 +- testament/categories.nim | 6 +-- testament/htmlgen.nim | 2 +- testament/testament.nim | 14 ++--- tools/nimfind.nim | 8 +-- 10 files changed, 85 insertions(+), 85 deletions(-) diff --git a/lib/impure/re.nim b/lib/impure/re.nim index fe3ea96ff..24841b40e 100644 --- a/lib/impure/re.nim +++ b/lib/impure/re.nim @@ -122,7 +122,7 @@ proc bufSubstr(b: cstring, sPos, ePos: int): string {.inline.} = ## Don't assume cstring is '\0' terminated let sz = ePos - sPos result = newString(sz+1) - copyMem(addr(result[0]), unsafeAddr(b[sPos]), sz) + copyMem(addr(result[0]), unsafeaddr(b[sPos]), sz) result.setLen(sz) proc matchOrFind(buf: cstring, pattern: Regex, matches: var openArray[string], diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index 410310e29..0bdbeb29b 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -202,7 +202,7 @@ proc newAsyncSocket*(domain, sockType, protocol: cint, when defineSsl: proc getSslError(handle: SslPtr, err: cint): cint = assert err < 0 - var ret = SSL_get_error(handle, err.cint) + var ret = SSLGetError(handle, err.cint) case ret of SSL_ERROR_ZERO_RETURN: raiseSSLError("TLS/SSL connection failed to initiate, socket closed prematurely.") @@ -224,9 +224,9 @@ when defineSsl: let read = bioRead(socket.bioOut, addr data[0], len) assert read != 0 if read < 0: - raiseSSLError() + raiseSslError() data.setLen(read) - await socket.fd.AsyncFD.send(data, flags) + await socket.fd.AsyncFd.send(data, flags) proc appeaseSsl(socket: AsyncSocket, flags: set[SocketFlag], sslError: cint): owned(Future[bool]) {.async.} = @@ -727,7 +727,7 @@ proc close*(socket: AsyncSocket) = if res == 0: discard elif res != 1: - raiseSSLError() + raiseSslError() socket.closed = true # TODO: Add extra debugging checks for this. when defineSsl: @@ -739,12 +739,12 @@ when defineSsl: ## prone to security vulnerabilities. socket.isSsl = true socket.sslContext = ctx - socket.sslHandle = SSL_new(socket.sslContext.context) + socket.sslHandle = SSLNew(socket.sslContext.context) if socket.sslHandle == nil: - raiseSSLError() + raiseSslError() - socket.bioIn = bioNew(bioSMem()) - socket.bioOut = bioNew(bioSMem()) + socket.bioIn = bioNew(bio_s_mem()) + socket.bioOut = bioNew(bio_s_mem()) sslSetBio(socket.sslHandle, socket.bioIn, socket.bioOut) proc wrapConnectedSocket*(ctx: SslContext, socket: AsyncSocket, diff --git a/lib/pure/base64.nim b/lib/pure/base64.nim index 9495c4d56..c140b9714 100644 --- a/lib/pure/base64.nim +++ b/lib/pure/base64.nim @@ -229,7 +229,7 @@ proc decode*(s: string): string = return (size * 3 div 4) + 6 template inputChar(x: untyped) = - let x = int decodeTable[ord(s[inputIndex])] + let x = int decode_table[ord(s[inputIndex])] inc inputIndex if x == invalidChar: raise newException(ValueError, diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 92e4cd2d6..e8936f87b 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -313,10 +313,10 @@ proc fileError(msg: string) = raise e when not defined(ssl): - type SslContext = ref object -var defaultSslContext {.threadvar.}: SslContext + type SSLContext = ref object +var defaultSslContext {.threadvar.}: SSLContext -proc getDefaultSSL(): SslContext = +proc getDefaultSSL(): SSLContext = result = defaultSslContext when defined(ssl): if result == nil: diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 7aeffbc35..d18f18c92 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -128,8 +128,8 @@ type bufLen: int # current length of buffer when defineSsl: isSsl: bool - sslHandle: SslPtr - sslContext: SslContext + sslHandle: SSLPtr + sslContext: SSLContext sslNoHandshake: bool # True if needs handshake. sslHasPeekChar: bool sslPeekChar: char @@ -469,15 +469,15 @@ proc fromSockAddr*(sa: Sockaddr_storage | SockAddr | Sockaddr_in | Sockaddr_in6, when defineSsl: CRYPTO_malloc_init() doAssert SslLibraryInit() == 1 - SSL_load_error_strings() - ERR_load_BIO_strings() + SslLoadErrorStrings() + ErrLoadBioStrings() OpenSSL_add_all_algorithms() proc raiseSSLError*(s = "") = ## Raises a new SSL error. if s != "": - raise newException(SslError, s) - let err = ERR_peek_last_error() + raise newException(SSLError, s) + let err = ErrPeekLastError() if err == 0: raise newException(SslError, "No error reported.") var errStr = $ERR_error_string(err, nil) @@ -487,10 +487,10 @@ when defineSsl: "necessary protocols. OpenSSL error is: " & errStr else: discard - raise newException(SslError, errStr) + raise newException(SSLError, errStr) - proc getExtraData*(ctx: SslContext, index: int): RootRef = - ## Retrieves arbitrary data stored inside SslContext. + proc getExtraData*(ctx: SSLContext, index: int): RootRef = + ## Retrieves arbitrary data stored inside SSLContext. if index notin ctx.referencedData: raise newException(IndexDefect, "No data with that index.") let res = ctx.context.SSL_CTX_get_ex_data(index.cint) @@ -498,8 +498,8 @@ when defineSsl: raiseSSLError() return cast[RootRef](res) - proc setExtraData*(ctx: SslContext, index: int, data: RootRef) = - ## Stores arbitrary data inside SslContext. The unique `index` + proc setExtraData*(ctx: SSLContext, index: int, data: RootRef) = + ## Stores arbitrary data inside SSLContext. The unique `index` ## should be retrieved using getSslContextExtraDataIndex. if index in ctx.referencedData: GC_unref(getExtraData(ctx, index)) @@ -520,7 +520,7 @@ when defineSsl: raise newException(system.IOError, "Key file could not be found: " & keyFile) if certFile != "": - var ret = SSL_CTX_use_certificate_chain_file(ctx, certFile) + var ret = SSLCTXUseCertificateChainFile(ctx, certFile) if ret != 1: raiseSSLError() @@ -567,18 +567,18 @@ when defineSsl: ## or using ECDSA: ## - ``openssl ecparam -out mykey.pem -name secp256k1 -genkey`` ## - ``openssl req -new -key mykey.pem -x509 -nodes -days 365 -out mycert.pem`` - var newCTX: SslCtx + var newCTX: SSL_CTX case protVersion of protSSLv23: newCTX = SSL_CTX_new(SSLv23_method()) # SSlv2,3 and TLS1 support. of protSSLv2: - raiseSSLError("SSLv2 is no longer secure and has been deprecated, use protSSLv23") + raiseSslError("SSLv2 is no longer secure and has been deprecated, use protSSLv23") of protSSLv3: - raiseSSLError("SSLv3 is no longer secure and has been deprecated, use protSSLv23") + raiseSslError("SSLv3 is no longer secure and has been deprecated, use protSSLv23") of protTLSv1: newCTX = SSL_CTX_new(TLSv1_method()) - if newCTX.SSL_CTX_set_cipher_list(cipherList) != 1: + if newCTX.SSLCTXSetCipherList(cipherList) != 1: raiseSSLError() when not defined(openssl10) and not defined(libressl): let sslVersion = getOpenSSLVersion() @@ -631,11 +631,11 @@ when defineSsl: result = SSLContext(context: newCTX, referencedData: initHashSet[int](), extraInternal: new(SslContextExtraInternal)) - proc getExtraInternal(ctx: SslContext): SslContextExtraInternal = + proc getExtraInternal(ctx: SSLContext): SslContextExtraInternal = return ctx.extraInternal - proc destroyContext*(ctx: SslContext) = - ## Free memory referenced by SslContext. + proc destroyContext*(ctx: SSLContext) = + ## Free memory referenced by SSLContext. # We assume here that OpenSSL's internal indexes increase by 1 each time. # That means we can assume that the next internal index is the length of @@ -644,20 +644,20 @@ when defineSsl: GC_unref(getExtraData(ctx, i).RootRef) ctx.context.SSL_CTX_free() - proc `pskIdentityHint=`*(ctx: SslContext, hint: string) = + proc `pskIdentityHint=`*(ctx: SSLContext, hint: string) = ## Sets the identity hint passed to server. ## ## Only used in PSK ciphersuites. if ctx.context.SSL_CTX_use_psk_identity_hint(hint) <= 0: raiseSSLError() - proc clientGetPskFunc*(ctx: SslContext): SslClientGetPskFunc = + proc clientGetPskFunc*(ctx: SSLContext): SslClientGetPskFunc = return ctx.getExtraInternal().clientGetPskFunc proc pskClientCallback(ssl: SslPtr; hint: cstring; identity: cstring; max_identity_len: cuint; psk: ptr cuchar; max_psk_len: cuint): cuint {.cdecl.} = - let ctx = SslContext(context: ssl.SSL_get_SSL_CTX) + let ctx = SSLContext(context: ssl.SSL_get_SSL_CTX) let hintString = if hint == nil: "" else: $hint let (identityString, pskString) = (ctx.clientGetPskFunc)(hintString) if psk.len.cuint > max_psk_len: @@ -670,7 +670,7 @@ when defineSsl: return pskString.len.cuint - proc `clientGetPskFunc=`*(ctx: SslContext, fun: SslClientGetPskFunc) = + proc `clientGetPskFunc=`*(ctx: SSLContext, fun: SslClientGetPskFunc) = ## Sets function that returns the client identity and the PSK based on identity ## hint from the server. ## @@ -679,12 +679,12 @@ when defineSsl: ctx.context.SSL_CTX_set_psk_client_callback( if fun == nil: nil else: pskClientCallback) - proc serverGetPskFunc*(ctx: SslContext): SslServerGetPskFunc = + proc serverGetPskFunc*(ctx: SSLContext): SslServerGetPskFunc = return ctx.getExtraInternal().serverGetPskFunc proc pskServerCallback(ssl: SslCtx; identity: cstring; psk: ptr cuchar; max_psk_len: cint): cuint {.cdecl.} = - let ctx = SslContext(context: ssl.SSL_get_SSL_CTX) + let ctx = SSLContext(context: ssl.SSL_get_SSL_CTX) let pskString = (ctx.serverGetPskFunc)($identity) if psk.len.cint > max_psk_len: return 0 @@ -692,7 +692,7 @@ when defineSsl: return pskString.len.cuint - proc `serverGetPskFunc=`*(ctx: SslContext, fun: SslServerGetPskFunc) = + proc `serverGetPskFunc=`*(ctx: SSLContext, fun: SslServerGetPskFunc) = ## Sets function that returns PSK based on the client identity. ## ## Only used in PSK ciphersuites. @@ -702,10 +702,10 @@ when defineSsl: proc getPskIdentity*(socket: Socket): string = ## Gets the PSK identity provided by the client. - assert socket.isSsl + assert socket.isSSL return $(socket.sslHandle.SSL_get_psk_identity) - proc wrapSocket*(ctx: SslContext, socket: Socket) = + proc wrapSocket*(ctx: SSLContext, socket: Socket) = ## Wraps a socket in an SSL context. This function effectively turns ## ``socket`` into an SSL socket. ## @@ -716,16 +716,16 @@ when defineSsl: ## **Disclaimer**: This code is not well tested, may be very unsafe and ## prone to security vulnerabilities. - assert(not socket.isSsl) - socket.isSsl = true + assert(not socket.isSSL) + socket.isSSL = true socket.sslContext = ctx - socket.sslHandle = SSL_new(socket.sslContext.context) + socket.sslHandle = SSLNew(socket.sslContext.context) socket.sslNoHandshake = false socket.sslHasPeekChar = false if socket.sslHandle == nil: raiseSSLError() - if SSL_set_fd(socket.sslHandle, socket.fd) != 1: + if SSLSetFd(socket.sslHandle, socket.fd) != 1: raiseSSLError() proc checkCertName(socket: Socket, hostname: string) = @@ -819,7 +819,7 @@ proc getSocketError*(socket: Socket): OSErrorCode = proc socketError*(socket: Socket, err: int = -1, async = false, lastError = (-1).OSErrorCode) = - ## Raises an OSError based on the error code returned by ``SSL_get_error`` + ## Raises an OSError based on the error code returned by ``SSLGetError`` ## (for SSL sockets) and ``osLastError`` otherwise. ## ## If ``async`` is ``true`` no error will be thrown in the case when the @@ -827,9 +827,9 @@ proc socketError*(socket: Socket, err: int = -1, async = false, ## ## If ``err`` is not lower than 0 no exception will be raised. when defineSsl: - if socket.isSsl: + if socket.isSSL: if err <= 0: - var ret = SSL_get_error(socket.sslHandle, err.cint) + var ret = SSLGetError(socket.sslHandle, err.cint) case ret of SSL_ERROR_ZERO_RETURN: raiseSSLError("TLS/SSL connection failed to initiate, socket closed prematurely.") @@ -845,13 +845,13 @@ proc socketError*(socket: Socket, err: int = -1, async = false, raiseSSLError("Function for x509 lookup has been called.") of SSL_ERROR_SYSCALL: var errStr = "IO error has occurred " - let sslErr = ERR_peek_last_error() + let sslErr = ErrPeekLastError() if sslErr == 0 and err == 0: errStr.add "because an EOF was observed that violates the protocol" elif sslErr == 0 and err == -1: errStr.add "in the BIO layer" else: - let errStr = $ERR_error_string(sslErr, nil) + let errStr = $ErrErrorString(sslErr, nil) raiseSSLError(errStr & ": " & errStr) let osErr = osLastError() raiseOSError(osErr, errStr) @@ -859,7 +859,7 @@ proc socketError*(socket: Socket, err: int = -1, async = false, raiseSSLError() else: raiseSSLError("Unknown Error") - if err == -1 and not (when defineSsl: socket.isSsl else: false): + if err == -1 and not (when defineSsl: socket.isSSL else: false): var lastE = if lastError.int == -1: getSocketError(socket) else: lastError if async: when useWinVersion: @@ -939,7 +939,7 @@ proc acceptAddr*(server: Socket, client: var owned(Socket), address: var string, # Handle SSL. when defineSsl: - if server.isSsl: + if server.isSSL: # We must wrap the client sock in a ssl context. server.sslContext.wrapSocket(client) @@ -949,7 +949,7 @@ proc acceptAddr*(server: Socket, client: var owned(Socket), address: var string, when false: #defineSsl: proc acceptAddrSSL*(server: Socket, client: var Socket, - address: var string): SSL_acceptResult {. + address: var string): SSLAcceptResult {. tags: [ReadIOEffect].} = ## This procedure should only be used for non-blocking **SSL** sockets. ## It will immediately return with one of the following values: @@ -967,16 +967,16 @@ when false: #defineSsl: ## to connect. template doHandshake(): untyped = when defineSsl: - if server.isSsl: + if server.isSSL: client.setBlocking(false) # We must wrap the client sock in a ssl context. - if not client.isSsl or client.sslHandle == nil: + if not client.isSSL or client.sslHandle == nil: server.sslContext.wrapSocket(client) ErrClearError() let ret = SSL_accept(client.sslHandle) while ret <= 0: - let err = SSL_get_error(client.sslHandle, ret) + let err = SSLGetError(client.sslHandle, ret) if err != SSL_ERROR_WANT_ACCEPT: case err of SSL_ERROR_ZERO_RETURN: @@ -993,7 +993,7 @@ when false: #defineSsl: raiseSSLError("Unknown error") client.sslNoHandshake = false - if client.isSsl and client.sslNoHandshake: + if client.isSSL and client.sslNoHandshake: doHandshake() return AcceptSuccess else: @@ -1039,8 +1039,8 @@ proc close*(socket: Socket) = socketError(socket, res) finally: when defineSsl: - if socket.isSsl and socket.sslHandle != nil: - SSL_free(socket.sslHandle) + if socket.isSSL and socket.sslHandle != nil: + SSLFree(socket.sslHandle) socket.sslHandle = nil socket.fd.close() @@ -1119,7 +1119,7 @@ when defined(ssl): ## and the server that ``socket`` is connected to. ## ## Throws SslError if ``socket`` is not an SSL socket. - if socket.isSsl: + if socket.isSSL: return not socket.sslNoHandshake else: raiseSSLError("Socket is not an SSL socket.") @@ -1131,7 +1131,7 @@ proc hasDataBuffered*(s: Socket): bool = result = s.bufLen > 0 and s.currPos != s.bufLen when defineSsl: - if s.isSsl and not result: + if s.isSSL and not result: result = s.sslHasPeekChar proc select(readfd: Socket, timeout = 500): int = @@ -1207,7 +1207,7 @@ proc recv*(socket: Socket, data: pointer, size: int): int {.tags: [ result = read else: when defineSsl: - if socket.isSsl: + if socket.isSSL: if socket.sslHasPeekChar: # TODO: Merge this peek char mess into uniRecv copyMem(data, addr(socket.sslPeekChar), 1) socket.sslHasPeekChar = false @@ -1246,11 +1246,11 @@ proc waitFor(socket: Socket, waited: var Duration, timeout, size: int, raise newException(TimeoutError, "Call to '" & funcName & "' timed out.") when defineSsl: - if socket.isSsl: + if socket.isSSL: if socket.hasDataBuffered: # sslPeekChar is present. return 1 - let sslPending = SSL_pending(socket.sslHandle) + let sslPending = SSLPending(socket.sslHandle) if sslPending != 0: return min(sslPending, size) @@ -1352,7 +1352,7 @@ proc peekChar(socket: Socket, c: var char): int {.tags: [ReadIOEffect].} = c = socket.buffer[socket.currPos] else: when defineSsl: - if socket.isSsl: + if socket.isSSL: if not socket.sslHasPeekChar: result = uniRecv(socket, addr(socket.sslPeekChar), 1, 0'i32) socket.sslHasPeekChar = true @@ -1584,7 +1584,7 @@ proc sendTo*(socket: Socket, address: string, port: Port, proc isSsl*(socket: Socket): bool = ## Determines whether ``socket`` is a SSL socket. when defineSsl: - result = socket.isSsl + result = socket.isSSL else: result = false @@ -1782,7 +1782,7 @@ proc connect*(socket: Socket, address: string, if not success: raiseOSError(lastError) when defineSsl: - if socket.isSsl: + if socket.isSSL: # RFC3546 for SNI specifies that IP addresses are not allowed. if not isIpAddress(address): # Discard result in case OpenSSL version doesn't support SNI, or we're @@ -1850,7 +1850,7 @@ proc connect*(socket: Socket, address: string, port = Port(0), if res != 0: raiseOSError(OSErrorCode(res)) when defineSsl and not defined(nimdoc): - if socket.isSsl: + if socket.isSSL: socket.fd.setBlocking(true) doAssert socket.gotHandshake() socket.fd.setBlocking(true) diff --git a/testament/backend.nim b/testament/backend.nim index 0a6d7d8b9..c2658d6a0 100644 --- a/testament/backend.nim +++ b/testament/backend.nim @@ -23,8 +23,8 @@ var proc getMachine*(): MachineId = var name = execProcess("hostname").string.strip if name.len == 0: - name = when defined(posix): getEnv("HOSTNAME").string - else: getEnv("COMPUTERNAME").string + name = when defined(posix): getenv("HOSTNAME").string + else: getenv("COMPUTERNAME").string if name.len == 0: quit "cannot determine the machine name" diff --git a/testament/categories.nim b/testament/categories.nim index 760c3c5be..e44c3729f 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -297,7 +297,7 @@ proc testNimInAction(r: var TResults, cat: Category, options: string) = testSpec r, makeTest(filename, options, cat), {targetJS} template testCPP(filename: untyped) = - testSpec r, makeTest(filename, options, cat), {targetCpp} + testSpec r, makeTest(filename, options, cat), {targetCPP} let tests = [ "niminaction/Chapter1/various1", @@ -384,7 +384,7 @@ proc findMainFile(dir: string): string = elif file.endsWith(".nim"): if result.len == 0: result = file inc nimFiles - if nimFiles != 1: result.setLen(0) + if nimFiles != 1: result.setlen(0) proc manyLoc(r: var TResults, cat: Category, options: string) = for kind, dir in os.walkDir("tests/manyloc"): @@ -567,7 +567,7 @@ const MegaTestCat = "megatest" proc `&.?`(a, b: string): string = # candidate for the stdlib? - result = if b.startsWith(a): b else: a & b + result = if b.startswith(a): b else: a & b proc processSingleTest(r: var TResults, cat: Category, options, test: string) = let test = testsDir &.? cat.string / test diff --git a/testament/htmlgen.nim b/testament/htmlgen.nim index 8e58dd95e..002bd83bc 100644 --- a/testament/htmlgen.nim +++ b/testament/htmlgen.nim @@ -63,7 +63,7 @@ proc generateTestResultPanelPartial(outfile: File, testResultRow: JsonNode) = type AllTests = object - data: JsonNode + data: JSonNode totalCount, successCount, ignoredCount, failedCount: int successPercentage, ignoredPercentage, failedPercentage: BiggestFloat diff --git a/testament/testament.nim b/testament/testament.nim index 109ce4ed9..a798544ec 100644 --- a/testament/testament.nim +++ b/testament/testament.nim @@ -97,7 +97,7 @@ proc getFileDir(filename: string): string = if not result.isAbsolute(): result = getCurrentDir() / result -proc execCmdEx2(command: string, args: openArray[string]; workingDir, input: string = ""): tuple[ +proc execCmdEx2(command: string, args: openarray[string]; workingDir, input: string = ""): tuple[ cmdLine: string, output: TaintedString, exitCode: int] {.tags: @@ -473,7 +473,7 @@ proc testSpecHelper(r: var TResults, test: TTest, expected: TSpec, if exitCode != expected.exitCode: r.addResult(test, target, "exitcode: " & $expected.exitCode, "exitcode: " & $exitCode & "\n\nOutput:\n" & - bufB, reExitcodesDiffer) + bufB, reExitCodesDiffer) elif (expected.outputCheck == ocEqual and expected.output != bufB) or (expected.outputCheck == ocSubstr and expected.output notin bufB): given.err = reOutputsDiffer @@ -536,7 +536,7 @@ proc testC(r: var TResults, test: TTest, action: TTestAction) = elif action == actionRun: let exeFile = changeFileExt(test.name, ExeExt) var (_, exitCode) = execCmdEx(exeFile, options = {poStdErrToStdOut, poUsePath}) - if exitCode != 0: given.err = reExitcodesDiffer + if exitCode != 0: given.err = reExitCodesDiffer if given.err == reSuccess: inc(r.passed) proc testExec(r: var TResults, test: TTest) = @@ -549,7 +549,7 @@ proc testExec(r: var TResults, test: TTest) = if errC == 0: given.err = reSuccess else: - given.err = reExitcodesDiffer + given.err = reExitCodesDiffer given.msg = outp.string if given.err == reSuccess: inc(r.passed) @@ -613,8 +613,8 @@ proc loadSkipFrom(name: string): seq[string] = result.add sline proc main() = - os.putEnv "NIMTEST_COLOR", "never" - os.putEnv "NIMTEST_OUTPUT_LVL", "PRINT_FAILURES" + os.putenv "NIMTEST_COLOR", "never" + os.putenv "NIMTEST_OUTPUT_LVL", "PRINT_FAILURES" azure.init() backend.open() @@ -627,7 +627,7 @@ proc main() = var p = initOptParser() p.next() - while p.kind in {cmdLongOption, cmdShortOption}: + while p.kind in {cmdLongoption, cmdShortOption}: case p.key.string.normalize of "print", "verbose": optPrintResults = true of "failing": optFailing = true diff --git a/tools/nimfind.nim b/tools/nimfind.nim index 05980d740..6a4585c7e 100644 --- a/tools/nimfind.nim +++ b/tools/nimfind.nim @@ -182,10 +182,10 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string; conf: ConfigRef) = parseopt.next(p) case p.kind of cmdEnd: break - of cmdLongOption, cmdShortOption: + of cmdLongoption, cmdShortOption: case p.key.normalize of "help", "h": - stdout.writeLine(Usage) + stdout.writeline(Usage) quit() of "project": conf.projectName = p.val @@ -215,7 +215,7 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = self.initDefinesProg(conf, "nimfind") if paramCount() == 0: - stdout.writeLine(Usage) + stdout.writeline(Usage) return self.processCmdLineAndProjectPath(conf) @@ -231,4 +231,4 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = discard self.loadConfigsAndRunMainCommand(cache, conf) -handleCmdLine(newIdentCache(), newConfigRef()) +handleCmdline(newIdentCache(), newConfigRef()) From 46aacf917f39d02c96175d358e7a76fc875ea070 Mon Sep 17 00:00:00 2001 From: narimiran Date: Mon, 6 Jul 2020 11:24:43 +0200 Subject: [PATCH 0345/3768] Fix style inconsistencies due to the previous commit --- lib/impure/re.nim | 2 +- lib/pure/asyncnet.nim | 16 +++--- lib/pure/base64.nim | 2 +- lib/pure/httpclient.nim | 6 +-- lib/pure/net.nim | 110 +++++++++++++++++++-------------------- testament/backend.nim | 4 +- testament/categories.nim | 6 +-- testament/htmlgen.nim | 2 +- testament/testament.nim | 14 ++--- tools/nimfind.nim | 8 +-- 10 files changed, 85 insertions(+), 85 deletions(-) diff --git a/lib/impure/re.nim b/lib/impure/re.nim index 24841b40e..fe3ea96ff 100644 --- a/lib/impure/re.nim +++ b/lib/impure/re.nim @@ -122,7 +122,7 @@ proc bufSubstr(b: cstring, sPos, ePos: int): string {.inline.} = ## Don't assume cstring is '\0' terminated let sz = ePos - sPos result = newString(sz+1) - copyMem(addr(result[0]), unsafeaddr(b[sPos]), sz) + copyMem(addr(result[0]), unsafeAddr(b[sPos]), sz) result.setLen(sz) proc matchOrFind(buf: cstring, pattern: Regex, matches: var openArray[string], diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index 0bdbeb29b..410310e29 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -202,7 +202,7 @@ proc newAsyncSocket*(domain, sockType, protocol: cint, when defineSsl: proc getSslError(handle: SslPtr, err: cint): cint = assert err < 0 - var ret = SSLGetError(handle, err.cint) + var ret = SSL_get_error(handle, err.cint) case ret of SSL_ERROR_ZERO_RETURN: raiseSSLError("TLS/SSL connection failed to initiate, socket closed prematurely.") @@ -224,9 +224,9 @@ when defineSsl: let read = bioRead(socket.bioOut, addr data[0], len) assert read != 0 if read < 0: - raiseSslError() + raiseSSLError() data.setLen(read) - await socket.fd.AsyncFd.send(data, flags) + await socket.fd.AsyncFD.send(data, flags) proc appeaseSsl(socket: AsyncSocket, flags: set[SocketFlag], sslError: cint): owned(Future[bool]) {.async.} = @@ -727,7 +727,7 @@ proc close*(socket: AsyncSocket) = if res == 0: discard elif res != 1: - raiseSslError() + raiseSSLError() socket.closed = true # TODO: Add extra debugging checks for this. when defineSsl: @@ -739,12 +739,12 @@ when defineSsl: ## prone to security vulnerabilities. socket.isSsl = true socket.sslContext = ctx - socket.sslHandle = SSLNew(socket.sslContext.context) + socket.sslHandle = SSL_new(socket.sslContext.context) if socket.sslHandle == nil: - raiseSslError() + raiseSSLError() - socket.bioIn = bioNew(bio_s_mem()) - socket.bioOut = bioNew(bio_s_mem()) + socket.bioIn = bioNew(bioSMem()) + socket.bioOut = bioNew(bioSMem()) sslSetBio(socket.sslHandle, socket.bioIn, socket.bioOut) proc wrapConnectedSocket*(ctx: SslContext, socket: AsyncSocket, diff --git a/lib/pure/base64.nim b/lib/pure/base64.nim index c140b9714..9495c4d56 100644 --- a/lib/pure/base64.nim +++ b/lib/pure/base64.nim @@ -229,7 +229,7 @@ proc decode*(s: string): string = return (size * 3 div 4) + 6 template inputChar(x: untyped) = - let x = int decode_table[ord(s[inputIndex])] + let x = int decodeTable[ord(s[inputIndex])] inc inputIndex if x == invalidChar: raise newException(ValueError, diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index e8936f87b..92e4cd2d6 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -313,10 +313,10 @@ proc fileError(msg: string) = raise e when not defined(ssl): - type SSLContext = ref object -var defaultSslContext {.threadvar.}: SSLContext + type SslContext = ref object +var defaultSslContext {.threadvar.}: SslContext -proc getDefaultSSL(): SSLContext = +proc getDefaultSSL(): SslContext = result = defaultSslContext when defined(ssl): if result == nil: diff --git a/lib/pure/net.nim b/lib/pure/net.nim index d18f18c92..7aeffbc35 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -128,8 +128,8 @@ type bufLen: int # current length of buffer when defineSsl: isSsl: bool - sslHandle: SSLPtr - sslContext: SSLContext + sslHandle: SslPtr + sslContext: SslContext sslNoHandshake: bool # True if needs handshake. sslHasPeekChar: bool sslPeekChar: char @@ -469,15 +469,15 @@ proc fromSockAddr*(sa: Sockaddr_storage | SockAddr | Sockaddr_in | Sockaddr_in6, when defineSsl: CRYPTO_malloc_init() doAssert SslLibraryInit() == 1 - SslLoadErrorStrings() - ErrLoadBioStrings() + SSL_load_error_strings() + ERR_load_BIO_strings() OpenSSL_add_all_algorithms() proc raiseSSLError*(s = "") = ## Raises a new SSL error. if s != "": - raise newException(SSLError, s) - let err = ErrPeekLastError() + raise newException(SslError, s) + let err = ERR_peek_last_error() if err == 0: raise newException(SslError, "No error reported.") var errStr = $ERR_error_string(err, nil) @@ -487,10 +487,10 @@ when defineSsl: "necessary protocols. OpenSSL error is: " & errStr else: discard - raise newException(SSLError, errStr) + raise newException(SslError, errStr) - proc getExtraData*(ctx: SSLContext, index: int): RootRef = - ## Retrieves arbitrary data stored inside SSLContext. + proc getExtraData*(ctx: SslContext, index: int): RootRef = + ## Retrieves arbitrary data stored inside SslContext. if index notin ctx.referencedData: raise newException(IndexDefect, "No data with that index.") let res = ctx.context.SSL_CTX_get_ex_data(index.cint) @@ -498,8 +498,8 @@ when defineSsl: raiseSSLError() return cast[RootRef](res) - proc setExtraData*(ctx: SSLContext, index: int, data: RootRef) = - ## Stores arbitrary data inside SSLContext. The unique `index` + proc setExtraData*(ctx: SslContext, index: int, data: RootRef) = + ## Stores arbitrary data inside SslContext. The unique `index` ## should be retrieved using getSslContextExtraDataIndex. if index in ctx.referencedData: GC_unref(getExtraData(ctx, index)) @@ -520,7 +520,7 @@ when defineSsl: raise newException(system.IOError, "Key file could not be found: " & keyFile) if certFile != "": - var ret = SSLCTXUseCertificateChainFile(ctx, certFile) + var ret = SSL_CTX_use_certificate_chain_file(ctx, certFile) if ret != 1: raiseSSLError() @@ -567,18 +567,18 @@ when defineSsl: ## or using ECDSA: ## - ``openssl ecparam -out mykey.pem -name secp256k1 -genkey`` ## - ``openssl req -new -key mykey.pem -x509 -nodes -days 365 -out mycert.pem`` - var newCTX: SSL_CTX + var newCTX: SslCtx case protVersion of protSSLv23: newCTX = SSL_CTX_new(SSLv23_method()) # SSlv2,3 and TLS1 support. of protSSLv2: - raiseSslError("SSLv2 is no longer secure and has been deprecated, use protSSLv23") + raiseSSLError("SSLv2 is no longer secure and has been deprecated, use protSSLv23") of protSSLv3: - raiseSslError("SSLv3 is no longer secure and has been deprecated, use protSSLv23") + raiseSSLError("SSLv3 is no longer secure and has been deprecated, use protSSLv23") of protTLSv1: newCTX = SSL_CTX_new(TLSv1_method()) - if newCTX.SSLCTXSetCipherList(cipherList) != 1: + if newCTX.SSL_CTX_set_cipher_list(cipherList) != 1: raiseSSLError() when not defined(openssl10) and not defined(libressl): let sslVersion = getOpenSSLVersion() @@ -631,11 +631,11 @@ when defineSsl: result = SSLContext(context: newCTX, referencedData: initHashSet[int](), extraInternal: new(SslContextExtraInternal)) - proc getExtraInternal(ctx: SSLContext): SslContextExtraInternal = + proc getExtraInternal(ctx: SslContext): SslContextExtraInternal = return ctx.extraInternal - proc destroyContext*(ctx: SSLContext) = - ## Free memory referenced by SSLContext. + proc destroyContext*(ctx: SslContext) = + ## Free memory referenced by SslContext. # We assume here that OpenSSL's internal indexes increase by 1 each time. # That means we can assume that the next internal index is the length of @@ -644,20 +644,20 @@ when defineSsl: GC_unref(getExtraData(ctx, i).RootRef) ctx.context.SSL_CTX_free() - proc `pskIdentityHint=`*(ctx: SSLContext, hint: string) = + proc `pskIdentityHint=`*(ctx: SslContext, hint: string) = ## Sets the identity hint passed to server. ## ## Only used in PSK ciphersuites. if ctx.context.SSL_CTX_use_psk_identity_hint(hint) <= 0: raiseSSLError() - proc clientGetPskFunc*(ctx: SSLContext): SslClientGetPskFunc = + proc clientGetPskFunc*(ctx: SslContext): SslClientGetPskFunc = return ctx.getExtraInternal().clientGetPskFunc proc pskClientCallback(ssl: SslPtr; hint: cstring; identity: cstring; max_identity_len: cuint; psk: ptr cuchar; max_psk_len: cuint): cuint {.cdecl.} = - let ctx = SSLContext(context: ssl.SSL_get_SSL_CTX) + let ctx = SslContext(context: ssl.SSL_get_SSL_CTX) let hintString = if hint == nil: "" else: $hint let (identityString, pskString) = (ctx.clientGetPskFunc)(hintString) if psk.len.cuint > max_psk_len: @@ -670,7 +670,7 @@ when defineSsl: return pskString.len.cuint - proc `clientGetPskFunc=`*(ctx: SSLContext, fun: SslClientGetPskFunc) = + proc `clientGetPskFunc=`*(ctx: SslContext, fun: SslClientGetPskFunc) = ## Sets function that returns the client identity and the PSK based on identity ## hint from the server. ## @@ -679,12 +679,12 @@ when defineSsl: ctx.context.SSL_CTX_set_psk_client_callback( if fun == nil: nil else: pskClientCallback) - proc serverGetPskFunc*(ctx: SSLContext): SslServerGetPskFunc = + proc serverGetPskFunc*(ctx: SslContext): SslServerGetPskFunc = return ctx.getExtraInternal().serverGetPskFunc proc pskServerCallback(ssl: SslCtx; identity: cstring; psk: ptr cuchar; max_psk_len: cint): cuint {.cdecl.} = - let ctx = SSLContext(context: ssl.SSL_get_SSL_CTX) + let ctx = SslContext(context: ssl.SSL_get_SSL_CTX) let pskString = (ctx.serverGetPskFunc)($identity) if psk.len.cint > max_psk_len: return 0 @@ -692,7 +692,7 @@ when defineSsl: return pskString.len.cuint - proc `serverGetPskFunc=`*(ctx: SSLContext, fun: SslServerGetPskFunc) = + proc `serverGetPskFunc=`*(ctx: SslContext, fun: SslServerGetPskFunc) = ## Sets function that returns PSK based on the client identity. ## ## Only used in PSK ciphersuites. @@ -702,10 +702,10 @@ when defineSsl: proc getPskIdentity*(socket: Socket): string = ## Gets the PSK identity provided by the client. - assert socket.isSSL + assert socket.isSsl return $(socket.sslHandle.SSL_get_psk_identity) - proc wrapSocket*(ctx: SSLContext, socket: Socket) = + proc wrapSocket*(ctx: SslContext, socket: Socket) = ## Wraps a socket in an SSL context. This function effectively turns ## ``socket`` into an SSL socket. ## @@ -716,16 +716,16 @@ when defineSsl: ## **Disclaimer**: This code is not well tested, may be very unsafe and ## prone to security vulnerabilities. - assert(not socket.isSSL) - socket.isSSL = true + assert(not socket.isSsl) + socket.isSsl = true socket.sslContext = ctx - socket.sslHandle = SSLNew(socket.sslContext.context) + socket.sslHandle = SSL_new(socket.sslContext.context) socket.sslNoHandshake = false socket.sslHasPeekChar = false if socket.sslHandle == nil: raiseSSLError() - if SSLSetFd(socket.sslHandle, socket.fd) != 1: + if SSL_set_fd(socket.sslHandle, socket.fd) != 1: raiseSSLError() proc checkCertName(socket: Socket, hostname: string) = @@ -819,7 +819,7 @@ proc getSocketError*(socket: Socket): OSErrorCode = proc socketError*(socket: Socket, err: int = -1, async = false, lastError = (-1).OSErrorCode) = - ## Raises an OSError based on the error code returned by ``SSLGetError`` + ## Raises an OSError based on the error code returned by ``SSL_get_error`` ## (for SSL sockets) and ``osLastError`` otherwise. ## ## If ``async`` is ``true`` no error will be thrown in the case when the @@ -827,9 +827,9 @@ proc socketError*(socket: Socket, err: int = -1, async = false, ## ## If ``err`` is not lower than 0 no exception will be raised. when defineSsl: - if socket.isSSL: + if socket.isSsl: if err <= 0: - var ret = SSLGetError(socket.sslHandle, err.cint) + var ret = SSL_get_error(socket.sslHandle, err.cint) case ret of SSL_ERROR_ZERO_RETURN: raiseSSLError("TLS/SSL connection failed to initiate, socket closed prematurely.") @@ -845,13 +845,13 @@ proc socketError*(socket: Socket, err: int = -1, async = false, raiseSSLError("Function for x509 lookup has been called.") of SSL_ERROR_SYSCALL: var errStr = "IO error has occurred " - let sslErr = ErrPeekLastError() + let sslErr = ERR_peek_last_error() if sslErr == 0 and err == 0: errStr.add "because an EOF was observed that violates the protocol" elif sslErr == 0 and err == -1: errStr.add "in the BIO layer" else: - let errStr = $ErrErrorString(sslErr, nil) + let errStr = $ERR_error_string(sslErr, nil) raiseSSLError(errStr & ": " & errStr) let osErr = osLastError() raiseOSError(osErr, errStr) @@ -859,7 +859,7 @@ proc socketError*(socket: Socket, err: int = -1, async = false, raiseSSLError() else: raiseSSLError("Unknown Error") - if err == -1 and not (when defineSsl: socket.isSSL else: false): + if err == -1 and not (when defineSsl: socket.isSsl else: false): var lastE = if lastError.int == -1: getSocketError(socket) else: lastError if async: when useWinVersion: @@ -939,7 +939,7 @@ proc acceptAddr*(server: Socket, client: var owned(Socket), address: var string, # Handle SSL. when defineSsl: - if server.isSSL: + if server.isSsl: # We must wrap the client sock in a ssl context. server.sslContext.wrapSocket(client) @@ -949,7 +949,7 @@ proc acceptAddr*(server: Socket, client: var owned(Socket), address: var string, when false: #defineSsl: proc acceptAddrSSL*(server: Socket, client: var Socket, - address: var string): SSLAcceptResult {. + address: var string): SSL_acceptResult {. tags: [ReadIOEffect].} = ## This procedure should only be used for non-blocking **SSL** sockets. ## It will immediately return with one of the following values: @@ -967,16 +967,16 @@ when false: #defineSsl: ## to connect. template doHandshake(): untyped = when defineSsl: - if server.isSSL: + if server.isSsl: client.setBlocking(false) # We must wrap the client sock in a ssl context. - if not client.isSSL or client.sslHandle == nil: + if not client.isSsl or client.sslHandle == nil: server.sslContext.wrapSocket(client) ErrClearError() let ret = SSL_accept(client.sslHandle) while ret <= 0: - let err = SSLGetError(client.sslHandle, ret) + let err = SSL_get_error(client.sslHandle, ret) if err != SSL_ERROR_WANT_ACCEPT: case err of SSL_ERROR_ZERO_RETURN: @@ -993,7 +993,7 @@ when false: #defineSsl: raiseSSLError("Unknown error") client.sslNoHandshake = false - if client.isSSL and client.sslNoHandshake: + if client.isSsl and client.sslNoHandshake: doHandshake() return AcceptSuccess else: @@ -1039,8 +1039,8 @@ proc close*(socket: Socket) = socketError(socket, res) finally: when defineSsl: - if socket.isSSL and socket.sslHandle != nil: - SSLFree(socket.sslHandle) + if socket.isSsl and socket.sslHandle != nil: + SSL_free(socket.sslHandle) socket.sslHandle = nil socket.fd.close() @@ -1119,7 +1119,7 @@ when defined(ssl): ## and the server that ``socket`` is connected to. ## ## Throws SslError if ``socket`` is not an SSL socket. - if socket.isSSL: + if socket.isSsl: return not socket.sslNoHandshake else: raiseSSLError("Socket is not an SSL socket.") @@ -1131,7 +1131,7 @@ proc hasDataBuffered*(s: Socket): bool = result = s.bufLen > 0 and s.currPos != s.bufLen when defineSsl: - if s.isSSL and not result: + if s.isSsl and not result: result = s.sslHasPeekChar proc select(readfd: Socket, timeout = 500): int = @@ -1207,7 +1207,7 @@ proc recv*(socket: Socket, data: pointer, size: int): int {.tags: [ result = read else: when defineSsl: - if socket.isSSL: + if socket.isSsl: if socket.sslHasPeekChar: # TODO: Merge this peek char mess into uniRecv copyMem(data, addr(socket.sslPeekChar), 1) socket.sslHasPeekChar = false @@ -1246,11 +1246,11 @@ proc waitFor(socket: Socket, waited: var Duration, timeout, size: int, raise newException(TimeoutError, "Call to '" & funcName & "' timed out.") when defineSsl: - if socket.isSSL: + if socket.isSsl: if socket.hasDataBuffered: # sslPeekChar is present. return 1 - let sslPending = SSLPending(socket.sslHandle) + let sslPending = SSL_pending(socket.sslHandle) if sslPending != 0: return min(sslPending, size) @@ -1352,7 +1352,7 @@ proc peekChar(socket: Socket, c: var char): int {.tags: [ReadIOEffect].} = c = socket.buffer[socket.currPos] else: when defineSsl: - if socket.isSSL: + if socket.isSsl: if not socket.sslHasPeekChar: result = uniRecv(socket, addr(socket.sslPeekChar), 1, 0'i32) socket.sslHasPeekChar = true @@ -1584,7 +1584,7 @@ proc sendTo*(socket: Socket, address: string, port: Port, proc isSsl*(socket: Socket): bool = ## Determines whether ``socket`` is a SSL socket. when defineSsl: - result = socket.isSSL + result = socket.isSsl else: result = false @@ -1782,7 +1782,7 @@ proc connect*(socket: Socket, address: string, if not success: raiseOSError(lastError) when defineSsl: - if socket.isSSL: + if socket.isSsl: # RFC3546 for SNI specifies that IP addresses are not allowed. if not isIpAddress(address): # Discard result in case OpenSSL version doesn't support SNI, or we're @@ -1850,7 +1850,7 @@ proc connect*(socket: Socket, address: string, port = Port(0), if res != 0: raiseOSError(OSErrorCode(res)) when defineSsl and not defined(nimdoc): - if socket.isSSL: + if socket.isSsl: socket.fd.setBlocking(true) doAssert socket.gotHandshake() socket.fd.setBlocking(true) diff --git a/testament/backend.nim b/testament/backend.nim index c2658d6a0..0a6d7d8b9 100644 --- a/testament/backend.nim +++ b/testament/backend.nim @@ -23,8 +23,8 @@ var proc getMachine*(): MachineId = var name = execProcess("hostname").string.strip if name.len == 0: - name = when defined(posix): getenv("HOSTNAME").string - else: getenv("COMPUTERNAME").string + name = when defined(posix): getEnv("HOSTNAME").string + else: getEnv("COMPUTERNAME").string if name.len == 0: quit "cannot determine the machine name" diff --git a/testament/categories.nim b/testament/categories.nim index e44c3729f..760c3c5be 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -297,7 +297,7 @@ proc testNimInAction(r: var TResults, cat: Category, options: string) = testSpec r, makeTest(filename, options, cat), {targetJS} template testCPP(filename: untyped) = - testSpec r, makeTest(filename, options, cat), {targetCPP} + testSpec r, makeTest(filename, options, cat), {targetCpp} let tests = [ "niminaction/Chapter1/various1", @@ -384,7 +384,7 @@ proc findMainFile(dir: string): string = elif file.endsWith(".nim"): if result.len == 0: result = file inc nimFiles - if nimFiles != 1: result.setlen(0) + if nimFiles != 1: result.setLen(0) proc manyLoc(r: var TResults, cat: Category, options: string) = for kind, dir in os.walkDir("tests/manyloc"): @@ -567,7 +567,7 @@ const MegaTestCat = "megatest" proc `&.?`(a, b: string): string = # candidate for the stdlib? - result = if b.startswith(a): b else: a & b + result = if b.startsWith(a): b else: a & b proc processSingleTest(r: var TResults, cat: Category, options, test: string) = let test = testsDir &.? cat.string / test diff --git a/testament/htmlgen.nim b/testament/htmlgen.nim index 002bd83bc..8e58dd95e 100644 --- a/testament/htmlgen.nim +++ b/testament/htmlgen.nim @@ -63,7 +63,7 @@ proc generateTestResultPanelPartial(outfile: File, testResultRow: JsonNode) = type AllTests = object - data: JSonNode + data: JsonNode totalCount, successCount, ignoredCount, failedCount: int successPercentage, ignoredPercentage, failedPercentage: BiggestFloat diff --git a/testament/testament.nim b/testament/testament.nim index a798544ec..109ce4ed9 100644 --- a/testament/testament.nim +++ b/testament/testament.nim @@ -97,7 +97,7 @@ proc getFileDir(filename: string): string = if not result.isAbsolute(): result = getCurrentDir() / result -proc execCmdEx2(command: string, args: openarray[string]; workingDir, input: string = ""): tuple[ +proc execCmdEx2(command: string, args: openArray[string]; workingDir, input: string = ""): tuple[ cmdLine: string, output: TaintedString, exitCode: int] {.tags: @@ -473,7 +473,7 @@ proc testSpecHelper(r: var TResults, test: TTest, expected: TSpec, if exitCode != expected.exitCode: r.addResult(test, target, "exitcode: " & $expected.exitCode, "exitcode: " & $exitCode & "\n\nOutput:\n" & - bufB, reExitCodesDiffer) + bufB, reExitcodesDiffer) elif (expected.outputCheck == ocEqual and expected.output != bufB) or (expected.outputCheck == ocSubstr and expected.output notin bufB): given.err = reOutputsDiffer @@ -536,7 +536,7 @@ proc testC(r: var TResults, test: TTest, action: TTestAction) = elif action == actionRun: let exeFile = changeFileExt(test.name, ExeExt) var (_, exitCode) = execCmdEx(exeFile, options = {poStdErrToStdOut, poUsePath}) - if exitCode != 0: given.err = reExitCodesDiffer + if exitCode != 0: given.err = reExitcodesDiffer if given.err == reSuccess: inc(r.passed) proc testExec(r: var TResults, test: TTest) = @@ -549,7 +549,7 @@ proc testExec(r: var TResults, test: TTest) = if errC == 0: given.err = reSuccess else: - given.err = reExitCodesDiffer + given.err = reExitcodesDiffer given.msg = outp.string if given.err == reSuccess: inc(r.passed) @@ -613,8 +613,8 @@ proc loadSkipFrom(name: string): seq[string] = result.add sline proc main() = - os.putenv "NIMTEST_COLOR", "never" - os.putenv "NIMTEST_OUTPUT_LVL", "PRINT_FAILURES" + os.putEnv "NIMTEST_COLOR", "never" + os.putEnv "NIMTEST_OUTPUT_LVL", "PRINT_FAILURES" azure.init() backend.open() @@ -627,7 +627,7 @@ proc main() = var p = initOptParser() p.next() - while p.kind in {cmdLongoption, cmdShortOption}: + while p.kind in {cmdLongOption, cmdShortOption}: case p.key.string.normalize of "print", "verbose": optPrintResults = true of "failing": optFailing = true diff --git a/tools/nimfind.nim b/tools/nimfind.nim index 6a4585c7e..05980d740 100644 --- a/tools/nimfind.nim +++ b/tools/nimfind.nim @@ -182,10 +182,10 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string; conf: ConfigRef) = parseopt.next(p) case p.kind of cmdEnd: break - of cmdLongoption, cmdShortOption: + of cmdLongOption, cmdShortOption: case p.key.normalize of "help", "h": - stdout.writeline(Usage) + stdout.writeLine(Usage) quit() of "project": conf.projectName = p.val @@ -215,7 +215,7 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = self.initDefinesProg(conf, "nimfind") if paramCount() == 0: - stdout.writeline(Usage) + stdout.writeLine(Usage) return self.processCmdLineAndProjectPath(conf) @@ -231,4 +231,4 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = discard self.loadConfigsAndRunMainCommand(cache, conf) -handleCmdline(newIdentCache(), newConfigRef()) +handleCmdLine(newIdentCache(), newConfigRef()) From 6f962a4b3628fe746dba66520d725f4d2d1b5c1a Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 6 Jul 2020 13:41:42 +0200 Subject: [PATCH 0346/3768] fixes #14830 --- lib/packages/docutils/highlite.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/packages/docutils/highlite.nim b/lib/packages/docutils/highlite.nim index ca6a3e05e..796c17d7d 100644 --- a/lib/packages/docutils/highlite.nim +++ b/lib/packages/docutils/highlite.nim @@ -342,7 +342,7 @@ proc nimNextToken(g: var GeneralTokenizer) = inc(pos) g.kind = gtNone g.length = pos - g.pos - if g.kind != gtEof and g.length <= 0: + if g.kind != gtEof and g.state != gtNone and g.length <= 0: assert false, "nimNextToken: produced an empty token" g.pos = pos From 65af99a203387882d9fba7d94ae0cd46f479dd1f Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 6 Jul 2020 14:01:25 +0200 Subject: [PATCH 0347/3768] added security.md; refs #14882 --- security.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 security.md diff --git a/security.md b/security.md new file mode 100644 index 000000000..f1eaf3290 --- /dev/null +++ b/security.md @@ -0,0 +1,17 @@ +# Security Policy + +## Supported Versions + +Security fixes are provided in new releases and bugfix releases. + +We do not backport security fixes to older releases. + +(Yet, Linux distributions might backport security fixes for their packages.) + +## Reporting a Vulnerability + +Please do not report vulnerabilities via GitHub issues. + +If you have discovered a vulnerability, it is the best to notify us about it via +security@nim-lang.org in order to setup a meeting where we can discuss the next +steps. From a754160d654f03584088412f51b153ae6a170adb Mon Sep 17 00:00:00 2001 From: ee7 <45465154+ee7@users.noreply.github.com> Date: Mon, 6 Jul 2020 14:02:03 +0200 Subject: [PATCH 0348/3768] tables.nim: Add named fields in `smallest` and `largest` (#14919) The `smallest` and `largest` procs for `CountTable` returned a tuple with named fields, but the same procs for `CountTableRef` returned an anonymous tuple. This commit makes those `CountTableRef` procs more consistent, and adds a test. Fixes: #14918 --- lib/pure/collections/tables.nim | 4 ++-- tests/collections/ttables.nim | 14 ++++++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index 2ea58ce1f..a969a4c5d 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -2670,14 +2670,14 @@ proc inc*[A](t: CountTableRef[A], key: A, val = 1) = doAssert a == newCountTable("aaabbbbbbbbbbb") t[].inc(key, val) -proc smallest*[A](t: CountTableRef[A]): (A, int) = +proc smallest*[A](t: CountTableRef[A]): tuple[key: A, val: int] = ## Returns the ``(key, value)`` pair with the smallest ``val``. Efficiency: O(n) ## ## See also: ## * `largest proc<#largest,CountTableRef[A]>`_ t[].smallest -proc largest*[A](t: CountTableRef[A]): (A, int) = +proc largest*[A](t: CountTableRef[A]): tuple[key: A, val: int] = ## Returns the ``(key, value)`` pair with the largest ``val``. Efficiency: O(n) ## ## See also: diff --git a/tests/collections/ttables.nim b/tests/collections/ttables.nim index 2a590dd26..392b5e93e 100644 --- a/tests/collections/ttables.nim +++ b/tests/collections/ttables.nim @@ -325,6 +325,20 @@ block tablesref: else: break inc i + block smallestLargestNamedFieldsTest: # bug #14918 + const a = [7, 8, 8] + + proc testNamedFields(t: CountTable | CountTableRef) = + doAssert t.smallest.key == 7 + doAssert t.smallest.val == 1 + doAssert t.largest.key == 8 + doAssert t.largest.val == 2 + + let t1 = toCountTable(a) + testNamedFields(t1) + let t2 = newCountTable(a) + testNamedFields(t2) + block SyntaxTest: var x = newTable[int, string]({:}) discard x From ebd97884a0134ba2f88e19f647585aa6133536aa Mon Sep 17 00:00:00 2001 From: Clyybber Date: Mon, 6 Jul 2020 20:53:38 +0200 Subject: [PATCH 0349/3768] Add testcase for #14472 (#14921) --- tests/arc/t14472.nim | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 tests/arc/t14472.nim diff --git a/tests/arc/t14472.nim b/tests/arc/t14472.nim new file mode 100644 index 000000000..47ed77f4c --- /dev/null +++ b/tests/arc/t14472.nim @@ -0,0 +1,23 @@ +discard """ + valgrind: true + cmd: "nim c --gc:arc -d:useMalloc $file" +""" + +type + ImportMaterial* = object + # Adding a field here makes the problem go away. + + Mesh* = object + vertices: seq[float32] + material: ImportMaterial + + ImportedScene* = object + meshes*: seq[Mesh] + +proc bork() : ImportedScene = + var mats: seq[ImportMaterial] + + setLen(mats, 1) + add(result.meshes, Mesh(material: mats[0])) + +var s = bork() From 5bd2da3f6461ef4dbcfd1f0c35178fa8fa9b5368 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Tue, 7 Jul 2020 10:21:18 +0200 Subject: [PATCH 0350/3768] Fix #14911 (#14922) [backport] * Fix #14911 * Add testcase * Fix test --- compiler/semexprs.nim | 2 +- tests/tuples/ttuples_issues.nim | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index a9d45615e..de9f2198a 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2432,7 +2432,7 @@ proc checkPar(c: PContext; n: PNode): TParKind = for i in 0.. Date: Tue, 7 Jul 2020 17:22:10 +0900 Subject: [PATCH 0351/3768] Limit number of error messages from gcc/clang backend (#14852) * Limit number of error messages from backend * Avoid duplication of -fmax-errors * make tests/misc/trunner.nim success --- config/nim.cfg | 16 +++++++++------- tests/misc/msizeof5.nim.cfg | 5 +++++ 2 files changed, 14 insertions(+), 7 deletions(-) create mode 100644 tests/misc/msizeof5.nim.cfg diff --git a/config/nim.cfg b/config/nim.cfg index c888fcf89..d8d3d4f8b 100644 --- a/config/nim.cfg +++ b/config/nim.cfg @@ -167,17 +167,19 @@ path="$lib/pure" @end @end +gcc.maxerrorsimpl = "-fmax-errors=3" + @if macosx or freebsd or openbsd: cc = clang tlsEmulation:on - gcc.options.always = "-w" - gcc.cpp.options.always = "-w -fpermissive" + gcc.options.always %= "-w ${gcc.maxerrorsimpl}" + gcc.cpp.options.always %= "-w ${gcc.maxerrorsimpl} -fpermissive" @elif windows: - gcc.options.always = "-w -mno-ms-bitfields" - gcc.cpp.options.always = "-w -fpermissive -mno-ms-bitfields" + gcc.options.always %= "-w ${gcc.maxerrorsimpl} -mno-ms-bitfields" + gcc.cpp.options.always %= "-w ${gcc.maxerrorsimpl} -fpermissive -mno-ms-bitfields" @else: - gcc.options.always = "-w" - gcc.cpp.options.always = "-w -fpermissive" + gcc.options.always %= "-w ${gcc.maxerrorsimpl}" + gcc.cpp.options.always %= "-w ${gcc.maxerrorsimpl} -fpermissive" @end # Configuration for Objective-C compiler: @@ -245,7 +247,7 @@ llvm_gcc.options.size = "-Os" # Configuration for the LLVM CLang compiler: clang.options.debug = "-g" clang.cpp.options.debug = "-g" -clang.options.always = "-w" +clang.options.always = "-w -ferror-limit=3" clang.options.speed = "-O3" clang.options.size = "-Os" diff --git a/tests/misc/msizeof5.nim.cfg b/tests/misc/msizeof5.nim.cfg new file mode 100644 index 000000000..dc0712a8c --- /dev/null +++ b/tests/misc/msizeof5.nim.cfg @@ -0,0 +1,5 @@ +# Do not limit number of error messages from backend compiler. +gcc.options.always %= "${gcc.options.always} -fmax-errors=100" +clang.options.always %= "${clang.options.always} -ferror-limit=100" +gcc.cpp.options.always %= "${gcc.cpp.options.always} -fmax-errors=100" +clang.cpp.options.always %= "${clang.cpp.options.always} -ferror-limit=100" From 37253d660f93ac86942579defbde790ec557dcf1 Mon Sep 17 00:00:00 2001 From: awr1 <41453959+awr1@users.noreply.github.com> Date: Tue, 7 Jul 2020 03:22:55 -0500 Subject: [PATCH 0352/3768] Minor improvements to typecast section of manual (#14896) * Minor improvements to typecast section of manual * Clarification to casting w/ concrete types * Added less ambiguous language --- doc/manual.rst | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/doc/manual.rst b/doc/manual.rst index 99888ad53..c5608da84 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -3230,7 +3230,7 @@ has lots of advantages: Type conversions ---------------- -Syntactically a `type conversion` is like a procedure call, but a +Syntactically a *type conversion* is like a procedure call, but a type name replaces the procedure name. A type conversion is always safe in the sense that a failure to convert a type to another results in an exception (if it cannot be determined statically). @@ -3265,15 +3265,26 @@ conversions too are now *always unchecked*. Type casts ---------- -Example: + +*Type casts* are a crude mechanism to interpret the bit pattern of an expression +as if it would be of another type. Type casts are only needed for low-level +programming and are inherently unsafe. .. code-block:: nim cast[int](x) + +The target type of a cast must be a concrete type, for instance, a target type +that is a type class (which is non-concrete) would be invalid: -Type casts are a crude mechanism to interpret the bit pattern of -an expression as if it would be of another type. Type casts are -only needed for low-level programming and are inherently unsafe. - +.. code-block:: nim + type Foo = int or float + var x = cast[Foo](1) # Error: cannot cast to a non concrete type: 'Foo' + +Type casts should not be confused with *type conversions,* as mentioned in the +prior section. Unlike type conversions, a type cast cannot change the underlying +bit pattern of the data being casted (aside from that the size of the target type +may differ from the source type). Casting resembles *type punning* in other +languages or C++'s ``reinterpret_cast`` and ``bit_cast`` features. The addr operator ----------------- From 49cea86d8bb836188bf1e3b173715d406af48153 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 7 Jul 2020 12:14:43 +0200 Subject: [PATCH 0353/3768] weaken tosproc test for my Windows machine which doesn't have 'ls' --- tests/stdlib/tosproc.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/stdlib/tosproc.nim b/tests/stdlib/tosproc.nim index 363bd5d74..78b3ea807 100644 --- a/tests/stdlib/tosproc.nim +++ b/tests/stdlib/tosproc.nim @@ -219,7 +219,8 @@ else: # main driver var result = execCmdEx("nim r --hints:off -", options = {}, input = "echo 3*4") stripLineEnd(result[0]) doAssert result == ("12", 0) - doAssert execCmdEx("ls --nonexistant").exitCode != 0 + when not defined(windows): + doAssert execCmdEx("ls --nonexistant").exitCode != 0 when false: # bug: on windows, this raises; on posix, passes doAssert execCmdEx("nonexistant").exitCode != 0 From 95808c2806ef2dbbbc10296601ba3885efe8bc78 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 7 Jul 2020 12:15:20 +0200 Subject: [PATCH 0354/3768] speed up Nim's lexer by using cstring instead of string. C optimizers are fragile. --- compiler/nimlexbase.nim | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/compiler/nimlexbase.nim b/compiler/nimlexbase.nim index e3e8f1dbb..af2ae64a3 100644 --- a/compiler/nimlexbase.nim +++ b/compiler/nimlexbase.nim @@ -39,7 +39,9 @@ const type TBaseLexer* = object of RootObj bufpos*: int - buf*: string + buf*: cstring + bufStorage: string + bufLen: int stream*: PLLStream # we read from this stream lineNumber*: int # the current line number # private data: @@ -75,8 +77,8 @@ proc fillBuffer(L: var TBaseLexer) = oldBufLen: int # we know here that pos == L.sentinel, but not if this proc # is called the first time by initBaseLexer() - assert(L.sentinel < L.buf.len) - toCopy = L.buf.len - L.sentinel - 1 + assert(L.sentinel < L.bufLen) + toCopy = L.bufLen - L.sentinel - 1 assert(toCopy >= 0) if toCopy > 0: moveMem(addr L.buf[0], addr L.buf[L.sentinel + 1], toCopy) @@ -90,7 +92,7 @@ proc fillBuffer(L: var TBaseLexer) = # compute sentinel: dec(s) # BUGFIX (valgrind) while true: - assert(s < L.buf.len) + assert(s < L.bufLen) while (s >= 0) and not (L.buf[s] in NewLines): dec(s) if s >= 0: # we found an appropriate character for a sentinel: @@ -99,16 +101,18 @@ proc fillBuffer(L: var TBaseLexer) = else: # rather than to give up here because the line is too long, # double the buffer's size and try again: - oldBufLen = L.buf.len - L.buf.setLen(L.buf.len * 2) - assert(L.buf.len - oldBufLen == oldBufLen) + oldBufLen = L.bufLen + L.bufLen = L.bufLen * 2 + L.bufStorage.setLen(L.bufLen) + L.buf = L.bufStorage + assert(L.bufLen - oldBufLen == oldBufLen) charsRead = llStreamRead(L.stream, addr(L.buf[oldBufLen]), oldBufLen) if charsRead < oldBufLen: L.buf[oldBufLen + charsRead] = EndOfFile L.sentinel = oldBufLen + charsRead break - s = L.buf.len - 1 + s = L.bufLen - 1 proc fillBaseLexer(L: var TBaseLexer, pos: int): int = assert(pos <= L.sentinel) @@ -142,7 +146,9 @@ proc openBaseLexer(L: var TBaseLexer, inputstream: PLLStream, bufLen = 8192) = assert(bufLen > 0) L.bufpos = 0 L.offsetBase = 0 - L.buf = newString(bufLen) + L.bufStorage = newString(bufLen) + L.buf = L.bufStorage + L.bufLen = bufLen L.sentinel = bufLen - 1 L.lineStart = 0 L.lineNumber = 1 # lines start at 1 From a61a7889b8ffe3b5a31b025272b5fd27d69484f5 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 7 Jul 2020 17:47:57 +0200 Subject: [PATCH 0355/3768] injectdestructors: refactoring, added more cases explicitly (#14929) --- compiler/injectdestructors.nim | 75 ++++++++++++++++++++++++++++++---- 1 file changed, 68 insertions(+), 7 deletions(-) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index e10686917..bf0206055 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -706,7 +706,7 @@ proc pRaiseStmt(n: PNode, c: var Con; s: var Scope): PNode = proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode = if n.kind in {nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr, nkIfStmt, - nkIfExpr, nkCaseStmt, nkWhen, nkWhileStmt}: + nkIfExpr, nkCaseStmt, nkWhen, nkWhileStmt, nkTryStmt}: template process(child, s): untyped = p(child, c, s, mode) handleNestedTempl(n, process) elif mode == sinkArg: @@ -748,7 +748,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode = result = passCopyToSink(n, c, s) else: case n.kind - of nkBracket, nkObjConstr, nkTupleConstr, nkClosure: + of nkBracket, nkObjConstr, nkTupleConstr, nkClosure, nkCurly: # Let C(x) be the construction, 'x' the vector of arguments. # C(x) either owns 'x' or it doesn't. # If C(x) owns its data, we must consume C(x). @@ -790,7 +790,9 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode = result = shallowCopy(n) for i in 1.. 0): + if i < L and isCompileTimeOnly(parameters[i]): + result[i] = n[i] + elif i < L and (isSinkTypeForParam(parameters[i]) or inSpawn > 0): result[i] = p(n[i], c, s, sinkArg) else: result[i] = p(n[i], c, s, normal) @@ -867,8 +869,67 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode = of nkNone..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt, - nkExportStmt, nkPragma, nkCommentStmt, nkBreakState: + nkExportStmt, nkPragma, nkCommentStmt, nkBreakState, nkTypeOfExpr: result = n + + of nkStringToCString, nkCStringToString, nkChckRangeF, nkChckRange64, nkChckRange, nkPragmaBlock: + result = shallowCopy(n) + for i in 0 ..< n.len: + result[i] = p(n[i], c, s, normal) + if n.typ != nil and hasDestructor(n.typ): + if mode == normal: + result = ensureDestruction(result, c, s) + + of nkHiddenSubConv, nkHiddenStdConv, nkConv: + # we have an "ownership invariance" for all constructors C(x). + # See the comment for nkBracket construction. If the caller wants + # to own 'C(x)', it really wants to own 'x' too. If it doesn't, + # we need to destroy 'x' but the function call handling ensures that + # already. + result = copyTree(n) + if n.typ.skipTypes(abstractInst-{tyOwned}).kind != tyOwned and + n[1].typ.skipTypes(abstractInst-{tyOwned}).kind == tyOwned: + # allow conversions from owned to unowned via this little hack: + let nTyp = n[1].typ + n[1].typ = n.typ + result[1] = p(n[1], c, s, mode) + result[1].typ = nTyp + else: + result[1] = p(n[1], c, s, mode) + + of nkObjDownConv, nkObjUpConv: + result = copyTree(n) + result[0] = p(n[0], c, s, mode) + + of nkDotExpr: + result = shallowCopy(n) + result[0] = p(n[0], c, s, normal) + for i in 1 ..< n.len: + result[i] = n[i] + if mode == sinkArg and hasDestructor(n.typ): + if isAnalysableFieldAccess(n, c.owner) and isLastRead(n, c): + s.wasMoved.add genWasMoved(n, c) + else: + result = passCopyToSink(result, c, s) + + of nkBracketExpr, nkAddr, nkHiddenAddr, nkDerefExpr, nkHiddenDeref: + result = shallowCopy(n) + for i in 0 ..< n.len: + result[i] = p(n[i], c, s, normal) + if mode == sinkArg and hasDestructor(n.typ): + if isAnalysableFieldAccess(n, c.owner) and isLastRead(n, c): + # consider 'a[(g; destroy(g); 3)]', we want to say 'wasMoved(a[3])' + # without the junk, hence 'genWasMoved(n, c)' + # and not 'genWasMoved(result, c)': + s.wasMoved.add genWasMoved(n, c) + else: + result = passCopyToSink(result, c, s) + + of nkDefer, nkRange: + result = shallowCopy(n) + for i in 0 ..< n.len: + result[i] = p(n[i], c, s, normal) + of nkBreakStmt: s.needsTry = true result = n @@ -886,10 +947,10 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode = result[0] = p(n[0], c, s, mode) for i in 1.. Date: Tue, 7 Jul 2020 19:18:02 +0100 Subject: [PATCH 0356/3768] Revert commit 3e843ab3358. Closes #14930. --- lib/pure/times.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index fb2f5e430..16d7ff31d 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -1408,7 +1408,7 @@ proc getDateStr*(dt = now()): string {.rtl, extern: "nt$1", tags: [TimeEffect].} '-' & intToStr(dt.monthday, 2) proc getClockStr*(dt = now()): string {.rtl, extern: "nt$1", tags: [TimeEffect].} = - ## Gets the current local clock time as a string of the format ``HH:mm:ss``. + ## Gets the current local clock time as a string of the format ``HH:MM:SS``. runnableExamples: echo getClockStr(now() - 1.hours) assertDateTimeInitialized dt From 8480bef1c4f289228808a9b7525f6052c0ca7152 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Tue, 7 Jul 2020 22:43:36 +0200 Subject: [PATCH 0357/3768] Add testcase for #14864 (#14928) * Add testcase for #14864 * :D --- tests/arc/bmodule.nim | 4 ++++ tests/arc/cmodule.nim | 4 ++++ tests/arc/t14864.nim | 5 +++++ 3 files changed, 13 insertions(+) create mode 100644 tests/arc/bmodule.nim create mode 100644 tests/arc/cmodule.nim create mode 100644 tests/arc/t14864.nim diff --git a/tests/arc/bmodule.nim b/tests/arc/bmodule.nim new file mode 100644 index 000000000..70c2cc645 --- /dev/null +++ b/tests/arc/bmodule.nim @@ -0,0 +1,4 @@ +import cmodule + +for i in @[1, 2, 3].cycle(): + echo i diff --git a/tests/arc/cmodule.nim b/tests/arc/cmodule.nim new file mode 100644 index 000000000..6c6e6c0fa --- /dev/null +++ b/tests/arc/cmodule.nim @@ -0,0 +1,4 @@ +iterator cycle*[T](s: openArray[T]): T = + let s = @s + for x in s: + yield x diff --git a/tests/arc/t14864.nim b/tests/arc/t14864.nim new file mode 100644 index 000000000..f59b14d2c --- /dev/null +++ b/tests/arc/t14864.nim @@ -0,0 +1,5 @@ +discard """ + cmd: "nim c --gc:arc $file" +""" + +import bmodule From e82a14b2735e58a4230d79820465753e2592cd2b Mon Sep 17 00:00:00 2001 From: narimiran Date: Tue, 7 Jul 2020 22:57:06 +0200 Subject: [PATCH 0358/3768] fix mistake in times.nim docs --- lib/pure/times.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index 16d7ff31d..fb2f5e430 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -1408,7 +1408,7 @@ proc getDateStr*(dt = now()): string {.rtl, extern: "nt$1", tags: [TimeEffect].} '-' & intToStr(dt.monthday, 2) proc getClockStr*(dt = now()): string {.rtl, extern: "nt$1", tags: [TimeEffect].} = - ## Gets the current local clock time as a string of the format ``HH:MM:SS``. + ## Gets the current local clock time as a string of the format ``HH:mm:ss``. runnableExamples: echo getClockStr(now() - 1.hours) assertDateTimeInitialized dt From 87f6a9592cbbace5e8f0d647841b4490bb7c0f1f Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 8 Jul 2020 08:21:55 +0200 Subject: [PATCH 0359/3768] fixes #14402 (#14908) * fixes #14402 * added a test case --- lib/pure/asyncstreams.nim | 2 +- tests/arc/tasyncorc.nim | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 tests/arc/tasyncorc.nim diff --git a/lib/pure/asyncstreams.nim b/lib/pure/asyncstreams.nim index 44e73003e..393262c4f 100644 --- a/lib/pure/asyncstreams.nim +++ b/lib/pure/asyncstreams.nim @@ -96,7 +96,7 @@ proc read*[T](future: FutureStream[T]): owned(Future[(bool, T)]) = if resFut.finished: return # We don't want this callback called again. - future.cb = nil + #future.cb = nil # The return value depends on whether the FutureStream has finished. var res: (bool, T) diff --git a/tests/arc/tasyncorc.nim b/tests/arc/tasyncorc.nim new file mode 100644 index 000000000..56b8909b1 --- /dev/null +++ b/tests/arc/tasyncorc.nim @@ -0,0 +1,26 @@ +discard """ + output: '''230000''' + cmd: '''nim c --gc:orc -d:useMalloc $file''' + valgrind: "true" +""" + +# bug #14402 + +import asynchttpserver, asyncdispatch, httpclient, strutils + +proc cb(req: Request) {.async, gcsafe.} = + const html = " ".repeat(230000) + await req.respond(Http200, html) + +var server = newAsyncHttpServer() +asyncCheck server.serve(Port(8080), cb) + +proc test {.async.} = + var + client = newAsyncHttpClient() + resp = await client.get("http://localhost:8080") + + let x = (await resp.body).len + echo x # crash + +waitFor test() From b17e1efc67a64ce6acd2ddf4f432879f3bf739e2 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 7 Jul 2020 23:10:00 +0200 Subject: [PATCH 0360/3768] progress --- compiler/injectdestructors.nim | 50 ++++++++++++++++++---------- tests/destructor/tmove_objconstr.nim | 2 +- 2 files changed, 34 insertions(+), 18 deletions(-) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index bf0206055..e57898ae8 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -91,17 +91,23 @@ proc optimize(s: var Scope) = filterNil(wasMoved) filterNil(final) -proc toTree(s: var Scope; ret: PNode; onlyCareAboutVars = false): PNode = +type + ToTreeFlag = enum + onlyCareAboutVars, + producesValue + +proc toTree(s: var Scope; ret: PNode; flags: set[ToTreeFlag]): PNode = if not s.needsTry: optimize(s) assert ret != nil if s.vars.len == 0 and s.final.len == 0 and s.wasMoved.len == 0: # trivial, nothing was done: result = ret else: - if isEmptyType(ret.typ): - result = newNodeI(nkStmtList, ret.info) - else: + let isExpr = producesValue in flags and not isEmptyType(ret.typ) + if isExpr: result = newNodeIT(nkStmtListExpr, ret.info, ret.typ) + else: + result = newNodeI(nkStmtList, ret.info) if s.vars.len > 0: let varSection = newNodeI(nkVarSection, ret.info) @@ -109,7 +115,7 @@ proc toTree(s: var Scope; ret: PNode; onlyCareAboutVars = false): PNode = varSection.add newTree(nkIdentDefs, newSymNode(tmp), newNodeI(nkEmpty, ret.info), newNodeI(nkEmpty, ret.info)) result.add varSection - if onlyCareAboutVars: + if onlyCareAboutVars in flags: result.add ret s.vars.setLen 0 elif s.needsTry: @@ -117,8 +123,11 @@ proc toTree(s: var Scope; ret: PNode; onlyCareAboutVars = false): PNode = for m in s.wasMoved: finSection.add m for i in countdown(s.final.high, 0): finSection.add s.final[i] result.add newTryFinally(ret, finSection) + elif isExpr: + for m in s.wasMoved: result.add m + for i in countdown(s.final.high, 0): result.add s.final[i] + result.add ret else: - #assert isEmptyType(ret.typ) result.add ret for m in s.wasMoved: result.add m for i in countdown(s.final.high, 0): result.add s.final[i] @@ -593,22 +602,25 @@ proc pVarTopLevel(v: PNode; c: var Con; s: var Scope; ri, res: PNode) = elif ri.kind != nkEmpty: res.add moveOrCopy(v, ri, c, s, isDecl = true) -template handleNestedTempl(n: untyped, processCall: untyped) = +template handleNestedTempl(n, processCall: untyped; alwaysStmt: bool) = template maybeVoid(child, s): untyped = if isEmptyType(child.typ): p(child, c, s, normal) else: processCall(child, s) + let treeFlags = if not isEmptyType(n.typ) and not alwaysStmt: {producesValue} else: {} case n.kind of nkStmtList, nkStmtListExpr: # a statement list does not open a new scope if n.len == 0: return n result = copyNode(n) + if alwaysStmt: result.typ = nil for i in 0..---------transformed-to--------->" echo renderTree(result, {renderIds}) diff --git a/tests/destructor/tmove_objconstr.nim b/tests/destructor/tmove_objconstr.nim index 5366f4957..0740bd46b 100644 --- a/tests/destructor/tmove_objconstr.nim +++ b/tests/destructor/tmove_objconstr.nim @@ -113,7 +113,7 @@ proc myfunc2(x, y: int): tuple[a: MySeqNonCopyable, b:int, c:MySeqNonCopyable] = var cc = newMySeq(y, 5.0) (a: case x: of 1: - let (z1, z2) = myfunc(x,y) + let (z1, z2) = myfunc(x, y) z2 elif x > 5: raise newException(ValueError, "new error") else: newMySeq(x, 1.0), From cdce245a235f3a8c255d0f782bc088546343b6ed Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 7 Jul 2020 23:59:13 +0200 Subject: [PATCH 0361/3768] fixes #14899 --- compiler/injectdestructors.nim | 41 ++++++++++++++++++++++++++++++---- tests/arc/tcontrolflow.nim | 14 ++++++++++++ 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index e57898ae8..291cd36aa 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -37,6 +37,9 @@ type final: seq[PNode] # finally section needsTry: bool parent: ptr Scope + escapingSyms: IntSet # a construct like (block: let x = f(); x) + # means that 'x' escapes. We then destroy it + # in the parent's scope (and also allocate it there). proc nestedScope(parent: var Scope): Scope = Scope(vars: @[], wasMoved: @[], final: @[], needsTry: false, parent: addr(parent)) @@ -400,8 +403,11 @@ proc genCopy(c: var Con; dest, ri: PNode): PNode = checkForErrorPragma(c, t, ri, "=") result = genCopyNoCheck(c, dest, ri) -proc addTopVar(c: var Con; s: var Scope; v: PNode) = - s.vars.add v.sym +proc addTopVar(c: var Con; s: var Scope; v: PNode): ptr Scope = + result = addr(s) + while v.sym.id in result.escapingSyms and result.parent != nil: + result = result.parent + result[].vars.add v.sym proc getTemp(c: var Con; s: var Scope; typ: PType; info: TLineInfo): PNode = let sym = newSym(skTemp, getIdent(c.graph.cache, ":tmpD"), c.owner, info) @@ -584,9 +590,31 @@ proc cycleCheck(n: PNode; c: var Con) = message(c.graph.config, n.info, warnCycleCreated, msg) break +proc markEscapingVars(n: PNode; s: var Scope) = + case n.kind + of nkSym: + s.escapingSyms.incl n.sym.id + of nkDotExpr, nkBracketExpr, nkCheckedFieldExpr, nkDerefExpr, nkHiddenDeref, + nkAddr, nkHiddenAddr, nkStringToCString, nkCStringToString, nkObjDownConv, + nkObjUpConv: + markEscapingVars(n[0], s) + of nkCast, nkHiddenSubConv, nkHiddenStdConv, nkConv: + markEscapingVars(n[1], s) + of nkTupleConstr, nkBracket, nkPar, nkClosure: + for i in 0 ..< n.len: + markEscapingVars(n[i], s) + of nkObjConstr: + for i in 1 ..< n.len: + markEscapingVars(n[i], s) + of nkStmtList, nkStmtListExpr: + if n.len > 0: + markEscapingVars(n[^1], s) + else: + discard "no arbitrary tree traversal here" + proc pVarTopLevel(v: PNode; c: var Con; s: var Scope; ri, res: PNode) = # move the variable declaration to the top of the frame: - c.addTopVar s, v + let owningScope = c.addTopVar(s, v) if isUnpackedTuple(v): if c.inLoop > 0: # unpacked tuple needs reset at every loop iteration @@ -596,7 +624,7 @@ proc pVarTopLevel(v: PNode; c: var Con; s: var Scope; ri, res: PNode) = if sfGlobal in v.sym.flags and s.parent == nil: c.graph.globalDestructors.add genDestroy(c, v) else: - s.final.add genDestroy(c, v) + owningScope[].final.add genDestroy(c, v) if ri.kind == nkEmpty and c.inLoop > 0: res.add moveOrCopy(v, genDefaultCall(v.typ, c, v.info), c, s, isDecl = true) elif ri.kind != nkEmpty: @@ -617,6 +645,7 @@ template handleNestedTempl(n, processCall: untyped; alwaysStmt: bool) = for i in 0.. Date: Wed, 8 Jul 2020 10:03:09 +0200 Subject: [PATCH 0362/3768] fixes #14900 --- compiler/injectdestructors.nim | 80 ++++++++++++++++++---------------- tests/arc/tcontrolflow.nim | 20 +++++++++ 2 files changed, 62 insertions(+), 38 deletions(-) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 291cd36aa..67d61525e 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -41,6 +41,30 @@ type # means that 'x' escapes. We then destroy it # in the parent's scope (and also allocate it there). +type + Con = object + owner: PSym + g: ControlFlowGraph + jumpTargets: IntSet + destroys, topLevelVars: PNode + graph: ModuleGraph + emptyNode: PNode + otherRead: PNode + inLoop, inSpawn: int + uninit: IntSet # set of uninit'ed vars + uninitComputed: bool + + ProcessMode = enum + normal + consumed + sinkArg + +proc getTemp(c: var Con; s: var Scope; typ: PType; info: TLineInfo): PNode = + let sym = newSym(skTemp, getIdent(c.graph.cache, ":tmpD"), c.owner, info) + sym.typ = typ + s.vars.add(sym) + result = newSymNode(sym) + proc nestedScope(parent: var Scope): Scope = Scope(vars: @[], wasMoved: @[], final: @[], needsTry: false, parent: addr(parent)) @@ -99,7 +123,7 @@ type onlyCareAboutVars, producesValue -proc toTree(s: var Scope; ret: PNode; flags: set[ToTreeFlag]): PNode = +proc toTree(c: var Con; s: var Scope; ret: PNode; flags: set[ToTreeFlag]): PNode = if not s.needsTry: optimize(s) assert ret != nil if s.vars.len == 0 and s.final.len == 0 and s.wasMoved.len == 0: @@ -107,8 +131,11 @@ proc toTree(s: var Scope; ret: PNode; flags: set[ToTreeFlag]): PNode = result = ret else: let isExpr = producesValue in flags and not isEmptyType(ret.typ) + var r = PNode(nil) if isExpr: result = newNodeIT(nkStmtListExpr, ret.info, ret.typ) + if ret.kind == nkStmtListExpr: + r = getTemp(c, s, ret.typ, ret.info) else: result = newNodeI(nkStmtList, ret.info) @@ -126,32 +153,15 @@ proc toTree(s: var Scope; ret: PNode; flags: set[ToTreeFlag]): PNode = for m in s.wasMoved: finSection.add m for i in countdown(s.final.high, 0): finSection.add s.final[i] result.add newTryFinally(ret, finSection) - elif isExpr: - for m in s.wasMoved: result.add m - for i in countdown(s.final.high, 0): result.add s.final[i] - result.add ret else: - result.add ret + if r != nil: + result.add newTree(nkFastAsgn, r, ret) + else: + result.add ret for m in s.wasMoved: result.add m for i in countdown(s.final.high, 0): result.add s.final[i] - -type - Con = object - owner: PSym - g: ControlFlowGraph - jumpTargets: IntSet - destroys, topLevelVars: PNode - graph: ModuleGraph - emptyNode: PNode - otherRead: PNode - inLoop, inSpawn: int - uninit: IntSet # set of uninit'ed vars - uninitComputed: bool - - ProcessMode = enum - normal - consumed - sinkArg + if r != nil: + result.add r const toDebug {.strdefine.} = "" @@ -409,12 +419,6 @@ proc addTopVar(c: var Con; s: var Scope; v: PNode): ptr Scope = result = result.parent result[].vars.add v.sym -proc getTemp(c: var Con; s: var Scope; typ: PType; info: TLineInfo): PNode = - let sym = newSym(skTemp, getIdent(c.graph.cache, ":tmpD"), c.owner, info) - sym.typ = typ - s.vars.add(sym) - result = newSymNode(sym) - proc genDiscriminantAsgn(c: var Con; s: var Scope; n: PNode): PNode = # discriminator is ordinal value that doesn't need sink destroy # but fields within active case branch might need destruction @@ -661,7 +665,7 @@ template handleNestedTempl(n, processCall: untyped; alwaysStmt: bool) = var ofScope = nestedScope(s) markEscapingVars(it[^1], ofScope) let ofResult = maybeVoid(it[^1], ofScope) - branch[^1] = toTree(ofScope, ofResult, treeFlags) + branch[^1] = toTree(c, ofScope, ofResult, treeFlags) result.add branch rememberParent(s, ofScope) @@ -671,7 +675,7 @@ template handleNestedTempl(n, processCall: untyped; alwaysStmt: bool) = result.add p(n[0], c, s, normal) var bodyScope = nestedScope(s) let bodyResult = p(n[1], c, bodyScope, normal) - result.add toTree(bodyScope, bodyResult, treeFlags) + result.add toTree(c, bodyScope, bodyResult, treeFlags) rememberParent(s, bodyScope) dec c.inLoop @@ -682,7 +686,7 @@ template handleNestedTempl(n, processCall: untyped; alwaysStmt: bool) = var bodyScope = nestedScope(s) markEscapingVars(n[1], bodyScope) let bodyResult = processCall(n[1], bodyScope) - result.add toTree(bodyScope, bodyResult, treeFlags) + result.add toTree(c, bodyScope, bodyResult, treeFlags) rememberParent(s, bodyScope) of nkIfStmt, nkIfExpr: @@ -695,12 +699,12 @@ template handleNestedTempl(n, processCall: untyped; alwaysStmt: bool) = branchScope.parent = nil if it.kind in {nkElifBranch, nkElifExpr}: let cond = p(it[0], c, branchScope, normal) - branch[0] = toTree(branchScope, cond, {producesValue, onlyCareAboutVars}) + branch[0] = toTree(c, branchScope, cond, {producesValue, onlyCareAboutVars}) branchScope.parent = addr(s) markEscapingVars(it[^1], branchScope) var branchResult = processCall(it[^1], branchScope) - branch[^1] = toTree(branchScope, branchResult, treeFlags) + branch[^1] = toTree(c, branchScope, branchResult, treeFlags) result.add branch rememberParent(s, branchScope) @@ -710,7 +714,7 @@ template handleNestedTempl(n, processCall: untyped; alwaysStmt: bool) = var tryScope = nestedScope(s) markEscapingVars(n[0], tryScope) var tryResult = maybeVoid(n[0], tryScope) - result.add toTree(tryScope, tryResult, treeFlags) + result.add toTree(c, tryScope, tryResult, treeFlags) rememberParent(s, tryScope) for i in 1..---------transformed-to--------->" echo renderTree(result, {renderIds}) diff --git a/tests/arc/tcontrolflow.nim b/tests/arc/tcontrolflow.nim index b53344139..27ae45257 100644 --- a/tests/arc/tcontrolflow.nim +++ b/tests/arc/tcontrolflow.nim @@ -12,6 +12,7 @@ if end true 7 ##index 2 not in 0 .. 1## +true ''' cmd: "nim c --gc:arc -d:danger $file" """ @@ -102,3 +103,22 @@ proc escapeCheck = clItems(toSeq()) escapeCheck() + +# bug #14900 +template sortedByIt(seq1): untyped = + var result = seq1 + result + +proc seqsEqual(seq1, seq2: seq[string]): bool = + # works as a normal proc (if we would assign result inside) + # doesn't work as an expression + result = if false: # needed + false + else: + let a1 = seq1.sortedByIt() + let a2 = seq2.sortedByIt() + # echo a1 - works if you uncomment any of these + # echo a2 + a1 == a2 + +echo seqsEqual(@["a", "b", "c"], @["a", "b", "c"]) From e42ce877a6469ccc6eb99e1e0f6162aeb6839193 Mon Sep 17 00:00:00 2001 From: Araq Date: Wed, 8 Jul 2020 10:45:51 +0200 Subject: [PATCH 0363/3768] fixes #14805 --- compiler/ccgtypes.nim | 2 +- tests/lent/tlent_from_var.nim | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 tests/lent/tlent_from_var.nim diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 11743499d..fb205110e 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -279,7 +279,7 @@ proc ccgIntroducedPtr(conf: ConfigRef; s: PSym, retType: PType): bool = result = false # first parameter and return type is 'lent T'? --> use pass by pointer if s.position == 0 and retType != nil and retType.kind == tyLent: - result = true + result = pt.kind != tyVar proc fillResult(conf: ConfigRef; param: PNode) = fillLoc(param.sym.loc, locParam, param, ~"Result", diff --git a/tests/lent/tlent_from_var.nim b/tests/lent/tlent_from_var.nim new file mode 100644 index 000000000..c8a15b1a1 --- /dev/null +++ b/tests/lent/tlent_from_var.nim @@ -0,0 +1,14 @@ +discard """ + output: "x" +""" + +# bug #14805 + +type Foo = object + a: string + +proc bar(f: var Foo): lent string = + result = f.a + +var foo = Foo(a: "x") +echo bar(foo) From cb1ecbf95620bffd559610b70f99c6f16963ac5c Mon Sep 17 00:00:00 2001 From: Araq Date: Wed, 8 Jul 2020 10:51:26 +0200 Subject: [PATCH 0364/3768] closes #14878 --- tests/lent/tlent_from_var.nim | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/tests/lent/tlent_from_var.nim b/tests/lent/tlent_from_var.nim index c8a15b1a1..912390dc1 100644 --- a/tests/lent/tlent_from_var.nim +++ b/tests/lent/tlent_from_var.nim @@ -1,5 +1,6 @@ discard """ - output: "x" + output: '''x +[10, 11, 12, 13]''' """ # bug #14805 @@ -12,3 +13,20 @@ proc bar(f: var Foo): lent string = var foo = Foo(a: "x") echo bar(foo) + + +# bug #14878 + +# proc byLentImpl[T](a: T): lent T = a +proc byLentVar[T](a: var T): lent T = + result = a + # result = a.byLentImpl # this doesn't help + +var x = 3 # error: cannot take the address of an rvalue of type 'NI *' + +var xs = [10,11,12,13] # SIGSEGV + +let x2 = x.byLentVar + +let xs2 = xs.byLentVar +echo xs2 From 06d776a5828d429f62c3189a99161604d50f9b96 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 8 Jul 2020 14:47:14 +0200 Subject: [PATCH 0365/3768] fixes #14900, this time for real, maybe (#14934) --- compiler/injectdestructors.nim | 10 ++++++++-- tests/arc/tcontrolflow.nim | 24 +++++++++--------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 67d61525e..b5e1db169 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -134,7 +134,7 @@ proc toTree(c: var Con; s: var Scope; ret: PNode; flags: set[ToTreeFlag]): PNode var r = PNode(nil) if isExpr: result = newNodeIT(nkStmtListExpr, ret.info, ret.typ) - if ret.kind == nkStmtListExpr: + if ret.kind in nkCallKinds + {nkStmtListExpr}: r = getTemp(c, s, ret.typ, ret.info) else: result = newNodeI(nkStmtList, ret.info) @@ -155,7 +155,13 @@ proc toTree(c: var Con; s: var Scope; ret: PNode; flags: set[ToTreeFlag]): PNode result.add newTryFinally(ret, finSection) else: if r != nil: - result.add newTree(nkFastAsgn, r, ret) + if ret.kind == nkStmtListExpr: + # simplify it a bit further by merging the nkStmtListExprs + let last = ret.len - 1 + for i in 0 ..< last: result.add ret[i] + result.add newTree(nkFastAsgn, r, ret[last]) + else: + result.add newTree(nkFastAsgn, r, ret) else: result.add ret for m in s.wasMoved: result.add m diff --git a/tests/arc/tcontrolflow.nim b/tests/arc/tcontrolflow.nim index 27ae45257..41f614cb2 100644 --- a/tests/arc/tcontrolflow.nim +++ b/tests/arc/tcontrolflow.nim @@ -105,20 +105,14 @@ proc escapeCheck = escapeCheck() # bug #14900 -template sortedByIt(seq1): untyped = - var result = seq1 - result -proc seqsEqual(seq1, seq2: seq[string]): bool = - # works as a normal proc (if we would assign result inside) - # doesn't work as an expression - result = if false: # needed - false - else: - let a1 = seq1.sortedByIt() - let a2 = seq2.sortedByIt() - # echo a1 - works if you uncomment any of these - # echo a2 - a1 == a2 +proc seqsEqual(a, b: string): bool = + if false: + false + else: + (var result1 = a; result1) == (var result2 = b; result2) -echo seqsEqual(@["a", "b", "c"], @["a", "b", "c"]) +# can be const or var too +let expected = "hello" + +echo seqsEqual(expected, expected) From 3de5296337a0711b4d907728ac24fa07791d3224 Mon Sep 17 00:00:00 2001 From: Miran Date: Wed, 8 Jul 2020 15:01:47 +0200 Subject: [PATCH 0366/3768] remove a condition that table size must be passed as power of 2 (#14926) * remove a condition that table size must be passed as power of 2 * remove power-of-2 condition from sets and sharedtables * remove power-of-2 condition from deques * use 'correctSize' for both branches * prettify changelog.md and fix typos * add a changelog entry * fix double-call of 'right-size' * fix the same thing in sets.nim * introduce a new internal proc `slotsNeeded` Deprecate the public proc `rightSize`, which is not needed anymore. Now it is an identity function, allowing the old code to work correctly and without extra allocations. --- changelog.md | 69 ++++++++++++------- lib/pure/collections/deques.nim | 11 +-- lib/pure/collections/hashcommon.nim | 14 ++-- lib/pure/collections/setimpl.nim | 4 +- lib/pure/collections/sets.nim | 30 +------- lib/pure/collections/sharedtables.nim | 8 +-- lib/pure/collections/tableimpl.nim | 6 +- lib/pure/collections/tables.nim | 48 ++----------- tests/collections/ttables.nim | 10 +++ .../argument_parser/argument_parser.nim | 3 +- 10 files changed, 83 insertions(+), 120 deletions(-) diff --git a/changelog.md b/changelog.md index efad01515..1eda82297 100644 --- a/changelog.md +++ b/changelog.md @@ -5,7 +5,7 @@ ## Standard library additions and changes - Added `bindParams`, `bindParam` to `db_sqlite` for binding parameters into a `SqlPrepared` statement. -- Add `tryInsert`,`insert` procs to db_* libs accept primary key column name. +- Add `tryInsert`,`insert` procs to `db_*` libs accept primary key column name. - Added `xmltree.newVerbatimText` support create `style`'s,`script`'s text. - `uri` adds Data URI Base64, implements RFC-2397. - Add [DOM Parser](https://developer.mozilla.org/en-US/docs/Web/API/DOMParser) @@ -24,7 +24,7 @@ ease the writing of multi-process servers, where sockets inheritance is desired. - For a transistion period, define `nimInheritHandles` to enable file handle + For a transition period, define `nimInheritHandles` to enable file handle inheritance by default. This flag does **not** affect the `selectors` module due to the differing semantics between operating systems. @@ -83,11 +83,14 @@ proc foo(x: int, y: int): auto {.noSideEffect.} = x + y ``` + - The fields of `times.DateTime` are now private, and are accessed with getters and deprecated setters. -- The `times` module now handles the default value for `DateTime` more consistently. Most procs raise an assertion error when given - an uninitialized `DateTime`, the exceptions are `==` and `$` (which returns `"Uninitialized DateTime"`). The proc `times.isInitialized` - has been added which can be used to check if a `DateTime` has been initialized. +- The `times` module now handles the default value for `DateTime` more consistently. + Most procs raise an assertion error when given + an uninitialized `DateTime`, the exceptions are `==` and `$` (which returns `"Uninitialized DateTime"`). + The proc `times.isInitialized` has been added which can be used to check if + a `DateTime` has been initialized. - 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). @@ -105,14 +108,17 @@ ["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 compatiblity with the majority of the Internet. + adequate compatibility with the majority of the Internet. -- new module `std/jsonutils` with hookable `jsonTo,toJson,fromJson` for json serialization/deserialization of custom types. +- new module `std/jsonutils` with hookable `jsonTo,toJson,fromJson` for json + serialization/deserialization of custom types. - new proc `heapqueue.find[T](heap: HeapQueue[T], x: T): int` to get index of element ``x``. -- Add `rstgen.rstToLatex` convenience proc for `renderRstToOut` and `initRstGenerator` with `outLatex` output. -- Add `os.normalizeExe`, eg: `koch` => `./koch`. -- `macros.newLit` now preserves named vs unnamed tuples; use `-d:nimHasWorkaround14720` to keep old behavior +- Add `rstgen.rstToLatex` convenience proc for `renderRstToOut` and `initRstGenerator` + with `outLatex` output. +- Add `os.normalizeExe`, e.g.: `koch` => `./koch`. +- `macros.newLit` now preserves named vs unnamed tuples; use `-d:nimHasWorkaround14720` + to keep old behavior. - Add `random.gauss`, that uses the ratio of uniforms method of sampling from a Gaussian distribution. - Add `typetraits.elementType` to get element type of an iterable. - `typetraits.$` changes: `$(int,)` is now `"(int,)"` instead of `"(int)"`; @@ -120,14 +126,20 @@ `$((int, float), int)` is now `"((int, float), int)"` instead of `"(tuple of (int, float), int)"` - add `macros.extractDocCommentsAndRunnables` helper -- `strformat.fmt` and `strformat.&` support `= specifier`. `fmt"{expr=}"` now expands to `fmt"expr={expr}"`. +- `strformat.fmt` and `strformat.&` support `= specifier`. `fmt"{expr=}"` now + expands to `fmt"expr={expr}"`. - deprecations: `os.existsDir` => `dirExists`, `os.existsFile` => `fileExists` - Add `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. + 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, - eg. `debug` uses `console.debug`, `info` uses `console.info`, `warn` uses `console.warn`, etc. + 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. + ## Language changes @@ -165,7 +177,7 @@ x.val = nil ``` - `getImpl` on enum type symbols now returns field syms instead of idents. This helps - with writing typed macros. Old behavior for backwards compatiblity can be restored + with writing typed macros. Old behavior for backwards compatibility can be restored with command line switch `--useVersion:1.0`. - ``let`` statements can now be used without a value if declared with ``importc``/``importcpp``/``importjs``/``importobjc``. @@ -223,24 +235,29 @@ proc mydiv(a, b): int {.raises: [].} = (likewise with other hints and warnings), which is consistent with all other bool flags. (since 1.3.3). - `nim doc -r main` and `nim rst2html -r main` now call openDefaultBrowser -- new hint: `--hint:msgOrigin` will show where a compiler msg (hint|warning|error) was generated; this - helps in particular when it's non obvious where it came from either because multiple locations generate - the same message, or because the message involves runtime formatting. -- new flag `--backend:js|c|cpp|objc (or -b:js etc), to change backend; can be used with any command - (eg nim r, doc, check etc); safe to re-assign. -- new flag `--doccmd:cmd` to pass additional flags for runnableExamples, eg: `--doccmd:-d:foo --threads` +- new hint: `--hint:msgOrigin` will show where a compiler msg (hint|warning|error) + was generated; this helps in particular when it's non obvious where it came from + either because multiple locations generate the same message, or because the + message involves runtime formatting. +- new flag `--backend:js|c|cpp|objc` (or -b:js etc), to change backend; can be + used with any command (eg nim r, doc, check etc); safe to re-assign. +- new flag `--doccmd:cmd` to pass additional flags for runnableExamples, + e.g.: `--doccmd:-d:foo --threads` use `--doccmd:skip` to skip runnableExamples and rst test snippets. -- new flag `--usenimcache` to output to nimcache (whatever it resolves to after all commands are processed) +- new flag `--usenimcache` to output to nimcache (whatever it resolves to after + all commands are processed) and avoids polluting both $pwd and $projectdir. It can be used with any command. -- `runnableExamples "-b:cpp -r:off": code` is now supported, allowing to override how an example is compiled and run, - for example to change backend or compile only. -- `nim doc` now outputs under `$projectPath/htmldocs` when `--outdir` is unspecified (with or without `--project`); - passing `--project` now automatically generates an index and enables search. +- `runnableExamples "-b:cpp -r:off": code` is now supported, allowing to override + how an example is compiled and run, for example to change backend or compile only. +- `nim doc` now outputs under `$projectPath/htmldocs` when `--outdir` is unspecified + (with or without `--project`); passing `--project` now automatically generates + an index and enables search. See [docgen](docgen.html#introduction-quick-start) for details. - Removed the `--oldNewlines` switch. - Removed the `--laxStrings` switch for mutating the internal zero terminator on strings. - Removed the `--oldast` switch. -- `$getType(untyped)` is now "untyped" instead of "expr", `$getType(typed)` is now "typed" instead of "stmt" +- `$getType(untyped)` is now "untyped" instead of "expr", `$getType(typed)` is + now "typed" instead of "stmt". ## Tool changes diff --git a/lib/pure/collections/deques.nim b/lib/pure/collections/deques.nim index d096874a3..8150563cc 100644 --- a/lib/pure/collections/deques.nim +++ b/lib/pure/collections/deques.nim @@ -67,9 +67,9 @@ const defaultInitialSize* = 4 template initImpl(result: typed, initialSize: int) = - assert isPowerOfTwo(initialSize) - result.mask = initialSize-1 - newSeq(result.data, initialSize) + let correctSize = nextPowerOfTwo(initialSize) + result.mask = correctSize-1 + newSeq(result.data, correctSize) template checkIfInitialized(deq: typed) = when compiles(defaultInitialSize): @@ -82,11 +82,6 @@ proc initDeque*[T](initialSize: int = 4): Deque[T] = ## Optionally, the initial capacity can be reserved via `initialSize` ## as a performance optimization. ## The length of a newly created deque will still be 0. - ## - ## ``initialSize`` must be a power of two (default: 4). - ## If you need to accept runtime values for this you could use the - ## `nextPowerOfTwo proc`_ from the - ## `math module`_. result.initImpl(initialSize) proc len*[T](deq: Deque[T]): int {.inline.} = diff --git a/lib/pure/collections/hashcommon.nim b/lib/pure/collections/hashcommon.nim index e998145e7..336dbc07d 100644 --- a/lib/pure/collections/hashcommon.nim +++ b/lib/pure/collections/hashcommon.nim @@ -30,17 +30,23 @@ proc nextTry(h, maxHash: Hash): Hash {.inline.} = result = (h + 1) and maxHash proc mustRehash[T](t: T): bool {.inline.} = + # If this is changed, make sure to synchronize it with `slotsNeeded` below assert(t.dataLen > t.counter) result = (t.dataLen * 2 < t.counter * 3) or (t.dataLen - t.counter < 4) -proc rightSize*(count: Natural): int {.inline.} = +proc slotsNeeded(count: Natural): int {.inline.} = + # Make sure to synchronize with `mustRehash` above + result = nextPowerOfTwo(count * 3 div 2 + 4) + +proc rightSize*(count: Natural): int {.inline, deprecated: "Deprecated since 1.4.0".} = + ## **Deprecated since Nim v1.4.0**, it is not needed anymore + ## because picking the correct size is done internally. + ## ## Return the value of ``initialSize`` to support ``count`` items. ## ## If more items are expected to be added, simply add that ## expected extra amount to the parameter before calling this. - # - # Make sure to synchronize with `mustRehash` - result = nextPowerOfTwo(count * 3 div 2 + 4) + result = count template rawGetKnownHCImpl() {.dirty.} = if t.dataLen == 0: diff --git a/lib/pure/collections/setimpl.nim b/lib/pure/collections/setimpl.nim index d798cbcb3..20da6b6c2 100644 --- a/lib/pure/collections/setimpl.nim +++ b/lib/pure/collections/setimpl.nim @@ -16,12 +16,12 @@ template dataLen(t): untyped = len(t.data) include hashcommon template initImpl(s: typed, size: int) = - assert isPowerOfTwo(size) + let correctSize = slotsNeeded(size) when s is OrderedSet: s.first = -1 s.last = -1 s.counter = 0 - newSeq(s.data, size) + newSeq(s.data, correctSize) template rawInsertImpl() {.dirty.} = if data.len == 0: diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim index 2b270c2cb..b019da2a7 100644 --- a/lib/pure/collections/sets.nim +++ b/lib/pure/collections/sets.nim @@ -94,11 +94,6 @@ include setimpl proc init*[A](s: var HashSet[A], initialSize = defaultInitialSize) = ## Initializes a hash set. ## - ## The `initialSize` parameter needs to be a power of two (default: 64). - ## If you need to accept runtime values for this, you can use - ## `math.nextPowerOfTwo proc `_ or - ## `rightSize proc <#rightSize,Natural>`_ from this module. - ## ## Starting from Nim v0.20, sets are initialized by default and it is ## not necessary to call this function explicitly. ## @@ -222,7 +217,7 @@ proc toHashSet*[A](keys: openArray[A]): HashSet[A] = assert len(b) == 5 ## b == {'a', 'b', 'c', 'd', 'r'} - result = initHashSet[A](rightSize(keys.len)) + result = initHashSet[A](keys.len) for key in items(keys): result.incl(key) iterator items*[A](s: HashSet[A]): A = @@ -628,11 +623,6 @@ template forAllOrderedPairs(yieldStmt: untyped) {.dirty.} = proc init*[A](s: var OrderedSet[A], initialSize = defaultInitialSize) = ## Initializes an ordered hash set. ## - ## The `initialSize` parameter needs to be a power of two (default: 64). - ## If you need to accept runtime values for this, you can use - ## `math.nextPowerOfTwo proc `_ or - ## `rightSize proc <#rightSize,Natural>`_ from this module. - ## ## Starting from Nim v0.20, sets are initialized by default and it is ## not necessary to call this function explicitly. ## @@ -685,7 +675,7 @@ proc toOrderedSet*[A](keys: openArray[A]): OrderedSet[A] = assert len(b) == 5 ## b == {'a', 'b', 'r', 'c', 'd'} # different than in HashSet - result = initOrderedSet[A](rightSize(keys.len)) + result = initOrderedSet[A](keys.len) for key in items(keys): result.incl(key) proc contains*[A](s: OrderedSet[A], key: A): bool = @@ -980,7 +970,7 @@ when isMainModule and not defined(release): block toSeqAndString: var a = toHashSet([2, 7, 5]) - var b = initHashSet[int](rightSize(a.len)) + var b = initHashSet[int](a.len) for x in [2, 7, 5]: b.incl(x) assert($a == $b) #echo a @@ -1098,20 +1088,6 @@ when isMainModule and not defined(release): b.incl(2) assert b.len == 1 - block: - type FakeTable = object - dataLen: int - counter: int - countDeleted: int - - var t: FakeTable - for i in 0 .. 32: - var s = rightSize(i) - t.dataLen = s - t.counter = i - doAssert s > i and not mustRehash(t), - "performance issue: rightSize() will not elide enlarge() at: " & $i - block missingOrExcl: var s = toOrderedSet([2, 3, 6, 7]) assert s.missingOrExcl(4) == true diff --git a/lib/pure/collections/sharedtables.nim b/lib/pure/collections/sharedtables.nim index abef89507..cbd922db7 100644 --- a/lib/pure/collections/sharedtables.nim +++ b/lib/pure/collections/sharedtables.nim @@ -207,15 +207,11 @@ proc len*[A, B](t: var SharedTable[A, B]): int = withLock t: result = t.counter -proc init*[A, B](t: var SharedTable[A, B], initialSize = 64) = +proc init*[A, B](t: var SharedTable[A, B], initialSize = 32) = ## creates a new hash table that is empty. ## ## This proc must be called before any other usage of `t`. - ## - ## `initialSize` needs to be a power of two. If you need to accept runtime - ## values for this you could use the ``nextPowerOfTwo`` proc from the - ## `math `_ module or the ``rightSize`` proc from this module. - assert isPowerOfTwo(initialSize) + let initialSize = slotsNeeded(initialSize) t.counter = 0 t.dataLen = initialSize t.data = cast[KeyValuePairSeq[A, B]](allocShared0( diff --git a/lib/pure/collections/tableimpl.nim b/lib/pure/collections/tableimpl.nim index 47c14af93..b9d7c70d9 100644 --- a/lib/pure/collections/tableimpl.nim +++ b/lib/pure/collections/tableimpl.nim @@ -121,12 +121,12 @@ template ctAnd(a, b): bool = else: false template initImpl(result: typed, size: int) = + let correctSize = slotsNeeded(size) when ctAnd(declared(SharedTable), type(result) is SharedTable): - init(result, size) + init(result, correctSize) else: - assert isPowerOfTwo(size) result.counter = 0 - newSeq(result.data, size) + newSeq(result.data, correctSize) when compiles(result.first): result.first = -1 result.last = -1 diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index a969a4c5d..cf864c640 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -239,7 +239,7 @@ type ## <#newTable,int>`_. const - defaultInitialSize* = 64 + defaultInitialSize* = 32 # ------------------------------ helpers --------------------------------- @@ -288,12 +288,6 @@ proc enlarge[A, B](t: var Table[A, B]) = proc initTable*[A, B](initialSize = defaultInitialSize): Table[A, B] = ## Creates a new hash table that is empty. ## - ## ``initialSize`` must be a power of two (default: 64). - ## If you need to accept runtime values for this you could use the - ## `nextPowerOfTwo proc`_ from the - ## `math module`_ or the `rightSize proc<#rightSize,Natural>`_ - ## from this module. - ## ## Starting from Nim v0.20, tables are initialized by default and it is ## not necessary to call this function explicitly. ## @@ -335,7 +329,7 @@ proc toTable*[A, B](pairs: openArray[(A, B)]): Table[A, B] = let b = toTable(a) assert b == {'a': 5, 'b': 9}.toTable - result = initTable[A, B](rightSize(pairs.len)) + result = initTable[A, B](pairs.len) for key, val in items(pairs): result[key] = val proc `[]`*[A, B](t: Table[A, B], key: A): B = @@ -780,12 +774,6 @@ iterator allValues*[A, B](t: Table[A, B]; key: A): B = proc newTable*[A, B](initialSize = defaultInitialSize): TableRef[A, B] = ## Creates a new ref hash table that is empty. ## - ## ``initialSize`` must be a power of two (default: 64). - ## If you need to accept runtime values for this you could use the - ## `nextPowerOfTwo proc`_ from the - ## `math module`_ or the `rightSize proc<#rightSize,Natural>`_ - ## from this module. - ## ## See also: ## * `newTable proc<#newTable,openArray[]>`_ for creating a `TableRef` ## from a collection of `(key, value)` pairs @@ -1260,12 +1248,6 @@ template forAllOrderedPairs(yieldStmt: untyped) {.dirty.} = proc initOrderedTable*[A, B](initialSize = defaultInitialSize): OrderedTable[A, B] = ## Creates a new ordered hash table that is empty. ## - ## ``initialSize`` must be a power of two (default: 64). - ## If you need to accept runtime values for this you could use the - ## `nextPowerOfTwo proc`_ from the - ## `math module`_ or the `rightSize proc<#rightSize,Natural>`_ - ## from this module. - ## ## Starting from Nim v0.20, tables are initialized by default and it is ## not necessary to call this function explicitly. ## @@ -1309,7 +1291,7 @@ proc toOrderedTable*[A, B](pairs: openArray[(A, B)]): OrderedTable[A, B] = let b = toOrderedTable(a) assert b == {'a': 5, 'b': 9}.toOrderedTable - result = initOrderedTable[A, B](rightSize(pairs.len)) + result = initOrderedTable[A, B](pairs.len) for key, val in items(pairs): result[key] = val proc `[]`*[A, B](t: OrderedTable[A, B], key: A): B = @@ -1771,12 +1753,6 @@ iterator mvalues*[A, B](t: var OrderedTable[A, B]): var B = proc newOrderedTable*[A, B](initialSize = defaultInitialSize): OrderedTableRef[A, B] = ## Creates a new ordered ref hash table that is empty. ## - ## ``initialSize`` must be a power of two (default: 64). - ## If you need to accept runtime values for this you could use the - ## `nextPowerOfTwo proc`_ from the - ## `math module`_ or the `rightSize proc<#rightSize,Natural>`_ - ## from this module. - ## ## See also: ## * `newOrderedTable proc<#newOrderedTable,openArray[]>`_ for creating ## an `OrderedTableRef` from a collection of `(key, value)` pairs @@ -1803,7 +1779,7 @@ proc newOrderedTable*[A, B](pairs: openArray[(A, B)]): OrderedTableRef[A, B] let b = newOrderedTable(a) assert b == {'a': 5, 'b': 9}.newOrderedTable - result = newOrderedTable[A, B](rightSize(pairs.len)) + result = newOrderedTable[A, B](pairs.len) for key, val in items(pairs): result.add(key, val) @@ -2251,12 +2227,6 @@ proc inc*[A](t: var CountTable[A], key: A, val: Positive = 1) proc initCountTable*[A](initialSize = defaultInitialSize): CountTable[A] = ## Creates a new count table that is empty. ## - ## ``initialSize`` must be a power of two (default: 64). - ## If you need to accept runtime values for this you could use the - ## `nextPowerOfTwo proc`_ from the - ## `math module`_ or the `rightSize proc<#rightSize,Natural>`_ - ## from this module. - ## ## Starting from Nim v0.20, tables are initialized by default and it is ## not necessary to call this function explicitly. ## @@ -2269,7 +2239,7 @@ proc initCountTable*[A](initialSize = defaultInitialSize): CountTable[A] = proc toCountTable*[A](keys: openArray[A]): CountTable[A] = ## Creates a new count table with every member of a container ``keys`` ## having a count of how many times it occurs in that container. - result = initCountTable[A](rightSize(keys.len)) + result = initCountTable[A](keys.len) for key in items(keys): result.inc(key) proc `[]`*[A](t: CountTable[A], key: A): int = @@ -2617,12 +2587,6 @@ proc inc*[A](t: CountTableRef[A], key: A, val = 1) proc newCountTable*[A](initialSize = defaultInitialSize): CountTableRef[A] = ## Creates a new ref count table that is empty. ## - ## ``initialSize`` must be a power of two (default: 64). - ## If you need to accept runtime values for this you could use the - ## `nextPowerOfTwo proc`_ from the - ## `math module`_ or the `rightSize proc<#rightSize,Natural>`_ - ## from this module. - ## ## See also: ## * `newCountTable proc<#newCountTable,openArray[A]>`_ for creating ## a `CountTableRef` from a collection @@ -2634,7 +2598,7 @@ proc newCountTable*[A](initialSize = defaultInitialSize): CountTableRef[A] = proc newCountTable*[A](keys: openArray[A]): CountTableRef[A] = ## Creates a new ref count table with every member of a container ``keys`` ## having a count of how many times it occurs in that container. - result = newCountTable[A](rightSize(keys.len)) + result = newCountTable[A](keys.len) for key in items(keys): result.inc(key) proc `[]`*[A](t: CountTableRef[A], key: A): int = diff --git a/tests/collections/ttables.nim b/tests/collections/ttables.nim index 392b5e93e..af8364488 100644 --- a/tests/collections/ttables.nim +++ b/tests/collections/ttables.nim @@ -446,3 +446,13 @@ block: # https://github.com/nim-lang/Nim/issues/13496 testDel(): (let t = newOrderedTable[int, int]()) testDel(): (var t: CountTable[int]) testDel(): (let t = newCountTable[int]()) + + +block testNonPowerOf2: + var a = initTable[int, int](7) + a[1] = 10 + assert a[1] == 10 + + var b = initTable[int, int](9) + b[1] = 10 + assert b[1] == 10 diff --git a/tests/manyloc/argument_parser/argument_parser.nim b/tests/manyloc/argument_parser/argument_parser.nim index 136d3e06b..b9788a81d 100644 --- a/tests/manyloc/argument_parser/argument_parser.nim +++ b/tests/manyloc/argument_parser/argument_parser.nim @@ -301,8 +301,7 @@ template build_specification_lookup(): OrderedTable[string, ptr Tparameter_specification] = ## Returns the table used to keep pointers to all of the specifications. var result {.gensym.}: OrderedTable[string, ptr Tparameter_specification] - result = initOrderedTable[string, ptr Tparameter_specification]( - tables.rightSize(expected.len)) + result = initOrderedTable[string, ptr Tparameter_specification](expected.len) for i in 0..expected.len-1: for param_to_detect in expected[i].names: if result.hasKey(param_to_detect): From 27579cfd3a473c1b449c6c62997107005f8d4c90 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Wed, 8 Jul 2020 16:20:16 +0200 Subject: [PATCH 0367/3768] Make arc compile laser again --- compiler/injectdestructors.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index b5e1db169..0a14a1daf 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -1006,7 +1006,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode = result[0] = p(n[0], c, s, mode) for i in 1.. Date: Wed, 8 Jul 2020 18:59:34 +0200 Subject: [PATCH 0368/3768] fixes #14865 (#14937) --- compiler/ccgthreadvars.nim | 1 + compiler/ccgtypes.nim | 1 + 2 files changed, 2 insertions(+) diff --git a/compiler/ccgthreadvars.nim b/compiler/ccgthreadvars.nim index 8bf5e573f..eba46f829 100644 --- a/compiler/ccgthreadvars.nim +++ b/compiler/ccgthreadvars.nim @@ -42,6 +42,7 @@ proc declareThreadVar(m: BModule, s: PSym, isExtern: bool) = proc generateThreadLocalStorage(m: BModule) = if m.g.nimtv != nil and (usesThreadVars in m.flags or sfMainModule in m.module.flags): for t in items(m.g.nimtvDeps): discard getTypeDesc(m, t) + finishTypeDescriptions(m) m.s[cfsSeqTypes].addf("typedef struct {$1} NimThreadVars;$n", [m.g.nimtv]) proc generateThreadVarsSize(m: BModule) = diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index fb205110e..f1dc85818 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -944,6 +944,7 @@ proc finishTypeDescriptions(m: BModule) = else: discard getTypeDescAux(m, t, check) inc(i) + m.typeStack.setLen 0 template cgDeclFrmt*(s: PSym): string = s.constraint.strVal From 9016dd96ee1ab6d3a60925cee4ee815dd8627f1e Mon Sep 17 00:00:00 2001 From: Clyybber Date: Wed, 8 Jul 2020 22:04:21 +0200 Subject: [PATCH 0369/3768] Add testcase for #12129 (#14940) * Add testcase for #12129 * Fix test --- tests/macros/tmacros_issues.nim | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/macros/tmacros_issues.nim b/tests/macros/tmacros_issues.nim index df385c445..23c9c66c0 100644 --- a/tests/macros/tmacros_issues.nim +++ b/tests/macros/tmacros_issues.nim @@ -19,6 +19,8 @@ nil 42 false true +@[i0, i1, i2, i3, i4] +@[tmp, tmp, tmp, tmp, tmp] ''' output: ''' @@ -244,3 +246,15 @@ macro toRendererBug(n): untyped = result = newLit repr(n) echo toRendererBug(0o377'i8) + +# bug #12129 +macro foobar() = + var loopVars = newSeq[NimNode](5) + for i, sym in loopVars.mpairs(): + sym = ident("i" & $i) + echo loopVars + for sym in loopVars.mitems(): + sym = ident("tmp") + echo loopVars + +foobar() From 3db6d9ea0c96782955923be4a24f49c1b2cc06fc Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Thu, 9 Jul 2020 04:21:30 +0800 Subject: [PATCH 0370/3768] add docs and more tests for debug format strings (#14861) * add debug format string * remove try except * add changelog * add docs and more tests * Update lib/pure/strformat.nim Co-authored-by: Juan Carlos * minor Co-authored-by: Juan Carlos --- lib/pure/strformat.nim | 47 +++++++++++++++++++++++- tests/stdlib/tstrformat.nim | 73 +++++++++++++++++++++++++++++++++++-- 2 files changed, 116 insertions(+), 4 deletions(-) diff --git a/lib/pure/strformat.nim b/lib/pure/strformat.nim index 0ea784a43..c37b6b1c0 100644 --- a/lib/pure/strformat.nim +++ b/lib/pure/strformat.nim @@ -63,7 +63,6 @@ Formatting floats .. code-block:: nim import strformat - doAssert fmt"{-12345:08}" == "-0012345" doAssert fmt"{-1:3}" == " -1" doAssert fmt"{-1:03}" == "-01" @@ -81,6 +80,52 @@ Formatting floats doAssert fmt"{123.456:13e}" == " 1.234560e+02" +Debugging strings +================= + +``fmt"{expr=}"`` expands to ``fmt"expr={expr}"`` namely the text of the expression, +an equal sign and the results of evaluated expression. + +.. code-block:: nim + + import strformat + doAssert fmt"{123.456=}" == "123.456=123.456" + doAssert fmt"{123.456=:>9.3f}" == "123.456= 123.456" + + let x = "hello" + doAssert fmt"{x=}" == "x=hello" + doAssert fmt"{x =}" == "x =hello" + + let y = 3.1415926 + doAssert fmt"{y=:.2f}" == fmt"y={y:.2f}" + doAssert fmt"{y=}" == fmt"y={y}" + doAssert fmt"{y = : <8}" == fmt"y = 3.14159 " + + proc hello(a: string, b: float): int = 12 + let a = "hello" + let b = 3.1415926 + doAssert fmt"{hello(x, y) = }" == "hello(x, y) = 12" + doAssert fmt"{x.hello(y) = }" == "x.hello(y) = 12" + doAssert fmt"{hello x, y = }" == "hello x, y = 12" + + +Note that it is space sensitive: + +.. code-block:: nim + + import strformat + let x = "12" + doAssert fmt"{x=}" == "x=12" + doAssert fmt"{x =:}" == "x =12" + doAssert fmt"{x =}" == "x =12" + doAssert fmt"{x= :}" == "x= 12" + doAssert fmt"{x= }" == "x= 12" + doAssert fmt"{x = :}" == "x = 12" + doAssert fmt"{x = }" == "x = 12" + doAssert fmt"{x = :}" == "x = 12" + doAssert fmt"{x = }" == "x = 12" + + Implementation details ====================== diff --git a/tests/stdlib/tstrformat.nim b/tests/stdlib/tstrformat.nim index c65165946..b1f3340a4 100644 --- a/tests/stdlib/tstrformat.nim +++ b/tests/stdlib/tstrformat.nim @@ -30,6 +30,10 @@ var o: Obj doAssert fmt"{o}" == "foobar" doAssert fmt"{o:10}" == "foobar " +doAssert fmt"{o=}" == "o=foobar" +doAssert fmt"{o=:10}" == "o=foobar " + + # see issue #7933 var str = "abc" doAssert fmt">7.1 :: {str:>7.1}" == ">7.1 :: a" @@ -47,15 +51,39 @@ doAssert fmt"^7.2 :: {str:^7.2}" == "^7.2 :: ab " doAssert fmt"^7.3 :: {str:^7.3}" == "^7.3 :: abc " doAssert fmt"^7.9 :: {str:^7.9}" == "^7.9 :: abc " doAssert fmt"^7.0 :: {str:^7.0}" == "^7.0 :: " + +doAssert fmt">7.1 :: {str=:>7.1}" == ">7.1 :: str= a" +doAssert fmt">7.2 :: {str=:>7.2}" == ">7.2 :: str= ab" +doAssert fmt">7.3 :: {str=:>7.3}" == ">7.3 :: str= abc" +doAssert fmt">7.9 :: {str=:>7.9}" == ">7.9 :: str= abc" +doAssert fmt">7.0 :: {str=:>7.0}" == ">7.0 :: str= " +doAssert fmt" 7.1 :: {str=:7.1}" == " 7.1 :: str=a " +doAssert fmt" 7.2 :: {str=:7.2}" == " 7.2 :: str=ab " +doAssert fmt" 7.3 :: {str=:7.3}" == " 7.3 :: str=abc " +doAssert fmt" 7.9 :: {str=:7.9}" == " 7.9 :: str=abc " +doAssert fmt" 7.0 :: {str=:7.0}" == " 7.0 :: str= " +doAssert fmt"^7.1 :: {str=:^7.1}" == "^7.1 :: str= a " +doAssert fmt"^7.2 :: {str=:^7.2}" == "^7.2 :: str= ab " +doAssert fmt"^7.3 :: {str=:^7.3}" == "^7.3 :: str= abc " +doAssert fmt"^7.9 :: {str=:^7.9}" == "^7.9 :: str= abc " +doAssert fmt"^7.0 :: {str=:^7.0}" == "^7.0 :: str= " str = "äöüe\u0309\u0319o\u0307\u0359" doAssert fmt"^7.1 :: {str:^7.1}" == "^7.1 :: ä " doAssert fmt"^7.2 :: {str:^7.2}" == "^7.2 :: äö " doAssert fmt"^7.3 :: {str:^7.3}" == "^7.3 :: äöü " doAssert fmt"^7.0 :: {str:^7.0}" == "^7.0 :: " + +doAssert fmt"^7.1 :: {str=:^7.1}" == "^7.1 :: str= ä " +doAssert fmt"^7.2 :: {str=:^7.2}" == "^7.2 :: str= äö " +doAssert fmt"^7.3 :: {str=:^7.3}" == "^7.3 :: str= äöü " +doAssert fmt"^7.0 :: {str=:^7.0}" == "^7.0 :: str= " # this is actually wrong, but the unicode module has no support for graphemes doAssert fmt"^7.4 :: {str:^7.4}" == "^7.4 :: äöüe " doAssert fmt"^7.9 :: {str:^7.9}" == "^7.9 :: äöüe\u0309\u0319o\u0307\u0359" +doAssert fmt"^7.4 :: {str=:^7.4}" == "^7.4 :: str= äöüe " +doAssert fmt"^7.9 :: {str=:^7.9}" == "^7.9 :: str=äöüe\u0309\u0319o\u0307\u0359" + # see issue #7932 doAssert fmt"{15:08}" == "00000015" # int, works doAssert fmt"{1.5:08}" == "000001.5" # float, works @@ -65,6 +93,14 @@ doAssert fmt"{-1.5:08}" == "-00001.5" # works doAssert fmt"{1.5:+08}" == "+00001.5" # works doAssert fmt"{1.5: 08}" == " 00001.5" # works +doAssert fmt"{15=:08}" == "15=00000015" # int, works +doAssert fmt"{1.5=:08}" == "1.5=000001.5" # float, works +doAssert fmt"{1.5=:0>8}" == "1.5=000001.5" # workaround using fill char works for positive floats +doAssert fmt"{-1.5=:0>8}" == "-1.5=0000-1.5" # even that does not work for negative floats +doAssert fmt"{-1.5=:08}" == "-1.5=-00001.5" # works +doAssert fmt"{1.5=:+08}" == "1.5=+00001.5" # works +doAssert fmt"{1.5=: 08}" == "1.5= 00001.5" # works + # only add explicitly requested sign if value != -0.0 (neg zero) doAssert fmt"{-0.0:g}" == "-0" doAssert fmt"{-0.0:+g}" == "-0" @@ -73,6 +109,13 @@ doAssert fmt"{0.0:g}" == "0" doAssert fmt"{0.0:+g}" == "+0" doAssert fmt"{0.0: g}" == " 0" +doAssert fmt"{-0.0=:g}" == "-0.0=-0" +doAssert fmt"{-0.0=:+g}" == "-0.0=-0" +doAssert fmt"{-0.0=: g}" == "-0.0=-0" +doAssert fmt"{0.0=:g}" == "0.0=0" +doAssert fmt"{0.0=:+g}" == "0.0=+0" +doAssert fmt"{0.0=: g}" == "0.0= 0" + # seq format let data1 = [1'i64, 10000'i64, 10000000'i64] @@ -88,6 +131,7 @@ proc formatValue(result: var string; value: (array|seq|openArray); specifier: st doAssert fmt"data1: {data1:8} #" == "data1: [ 1, 10000, 10000000] #" doAssert fmt"data2: {data2:8} =" == "data2: [10000000, 100, 1] =" + doAssert fmt"data1: {data1=:8} #" == "data1: data1=[ 1, 10000, 10000000] #" doAssert fmt"data2: {data2=:8} =" == "data2: data2=[10000000, 100, 1] =" @@ -160,6 +204,9 @@ proc my_proc = const a = &"{value}" assert a == value + const b = &"{value=}" + assert b == "value=" & value + my_proc() block: @@ -262,6 +309,7 @@ block: doAssert &"name: {name =}\nage: { age =: >7}\nhobby: { hobby= : 8}" == "name: name =hello\nage: age = 21\nhobby: hobby= swim " doAssert fmt"{age == 12}" == "false" + doAssert fmt"{name.toUpperAscii() = }" == "name.toUpperAscii() = HELLO" doAssert fmt"{name.toUpperAscii( ) = }" == "name.toUpperAscii( ) = HELLO" doAssert fmt"{ toUpperAscii( s = name ) = }" == " toUpperAscii( s = name ) = HELLO" doAssert fmt"{ strutils.toUpperAscii( s = name ) = }" == " strutils.toUpperAscii( s = name ) = HELLO" @@ -276,16 +324,30 @@ block: doAssert fmt"{0==1}" == "false" +# It is space sensitive. +block: + let x = "12" + doAssert fmt"{x=:}" == "x=12" + doAssert fmt"{x=}" == "x=12" + doAssert fmt"{x =:}" == "x =12" + doAssert fmt"{x =}" == "x =12" + doAssert fmt"{x= :}" == "x= 12" + doAssert fmt"{x= }" == "x= 12" + doAssert fmt"{x = :}" == "x = 12" + doAssert fmt"{x = }" == "x = 12" + doAssert fmt"{x = :}" == "x = 12" + doAssert fmt"{x = }" == "x = 12" + block: let x = "hello" - doAssert fmt"{x=}" == "x=" & $x - doAssert fmt"{x =}" == "x =" & $x + doAssert fmt"{x=}" == "x=hello" + doAssert fmt"{x =}" == "x =hello" let y = 3.1415926 doAssert fmt"{y=:.2f}" == fmt"y={y:.2f}" doAssert fmt"{y=}" == fmt"y={y}" - doAssert fmt"{y =: <16}" == fmt"y ={y: <16}" + doAssert fmt"{y = : <8}" == fmt"y = 3.14159 " proc hello(a: string, b: float): int = 12 template foo(a: string, b: float): int = 18 @@ -295,6 +357,11 @@ block: doAssert fmt"{hello(x, y)= }" == "hello(x, y)= 12" doAssert fmt"{hello(x, y) = }" == "hello(x, y) = 12" + doAssert fmt"{hello x, y=}" == "hello x, y=12" + doAssert fmt"{hello x, y =}" == "hello x, y =12" + doAssert fmt"{hello x, y= }" == "hello x, y= 12" + doAssert fmt"{hello x, y = }" == "hello x, y = 12" + doAssert fmt"{x.hello(y)=}" == "x.hello(y)=12" doAssert fmt"{x.hello(y) =}" == "x.hello(y) =12" doAssert fmt"{x.hello(y)= }" == "x.hello(y)= 12" From 00528cbc3c82ba33e8b9b551d5d86b1b6382c34a Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Thu, 9 Jul 2020 07:51:18 +0800 Subject: [PATCH 0371/3768] Add testcase for #10465 (#14943) * add debug format string * remove try except * add changelog * add docs and more tests * Update lib/pure/strformat.nim Co-authored-by: Juan Carlos * minor * add testcase Co-authored-by: Juan Carlos --- tests/constr/tconexpr.nim | 43 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 tests/constr/tconexpr.nim diff --git a/tests/constr/tconexpr.nim b/tests/constr/tconexpr.nim new file mode 100644 index 000000000..cca6dd84f --- /dev/null +++ b/tests/constr/tconexpr.nim @@ -0,0 +1,43 @@ +discard """ + nimout: ''' +Fibonacci sequence: 0, 1, 1, 2, 3 +Sequence continues: 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610 +''' +""" + + +import strformat + +var fib_n {.compileTime.}: int +var fib_prev {.compileTime.}: int +var fib_prev_prev {.compileTime.}: int + +proc next_fib(): int {.compileTime.} = + let fib = if fib_n < 2: + fib_n + else: + fib_prev_prev + fib_prev + inc(fib_n) + fib_prev_prev = fib_prev + fib_prev = fib + fib + +const f0 = next_fib() +const f1 = next_fib() +const f2 = next_fib() +const f3 = next_fib() +const f4 = next_fib() + +static: + echo fmt"Fibonacci sequence: {f0}, {f1}, {f2}, {f3}, {f4}" + +const fib_continues = block: + var result = fmt"Sequence continues: " + for i in 0..10: + if i > 0: + add(result, ", ") + add(result, $next_fib()) + result + +static: + echo fib_continues \ No newline at end of file From 399b2e3134751432f66a2414fbaf9c2a168fa953 Mon Sep 17 00:00:00 2001 From: treeform Date: Thu, 9 Jul 2020 00:18:27 -0700 Subject: [PATCH 0372/3768] Add missing dom.nim things (#14944) --- lib/js/dom.nim | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/lib/js/dom.nim b/lib/js/dom.nim index 09e45c04d..575ec3deb 100644 --- a/lib/js/dom.nim +++ b/lib/js/dom.nim @@ -106,7 +106,17 @@ type memory*: PerformanceMemory timing*: PerformanceTiming - Selection* {.importc.} = ref object ## see `docs`_ + Range* {.importc.} = ref object + ## see `docs{https://developer.mozilla.org/en-US/docs/Web/API/Range}`_ + collapsed*: bool + commonAncestorContainer*: Node + endContainer*: Node + endOffset*: int + startContainer*: Node + startOffset*: int + + Selection* {.importc.} = ref object + ## see `docs`_ anchorNode*: Node anchorOffset*: int focusNode*: Node @@ -210,6 +220,7 @@ type applets*: seq[Element] embeds*: seq[EmbedElement] links*: seq[LinkElement] + fonts*: FontFaceSet Element* = ref ElementObj ElementObj {.importc.} = object of NodeObj @@ -1189,6 +1200,10 @@ type ## see `docs`_ dataTransfer*: DataTransfer + ClipboardEvent* {.importc.} = object of Event + ## see `docs`_ + clipboardData*: DataTransfer + TouchList* {.importc.} = ref object of RootObj length*: int @@ -1268,6 +1283,15 @@ type once*: bool passive*: bool + FontFaceSetReady* {.importc.} = ref object + ## see: `docs`_ + then*: proc(cb: proc()) + + FontFaceSet* {.importc.} = ref object + ## see: `docs`_ + ready*: FontFaceSetReady + onloadingdone*: proc(event: Event) + since (1, 3): type DomParser* = ref object @@ -1393,6 +1417,7 @@ else: proc getElementById*(id: cstring): Element {.importc: "document.getElementById", nodecl.} proc appendChild*(n, child: Node) {.importcpp.} proc removeChild*(n, child: Node) {.importcpp.} + proc remove*(child: Node) {.importcpp.} proc replaceChild*(n, newNode, oldNode: Node) {.importcpp.} proc insertBefore*(n, newNode, before: Node) {.importcpp.} proc getElementById*(d: Document, id: cstring): Element {.importcpp.} @@ -1454,7 +1479,7 @@ proc deleteData*(n: Node, start, len: int) proc focus*(e: Node) proc getAttribute*(n: Node, attr: cstring): cstring proc getAttributeNode*(n: Node, attr: cstring): Node -proc hasAttribute*(n: Node; attr: cstring): bool +proc hasAttribute*(n: Node, attr: cstring): bool proc hasChildNodes*(n: Node): bool proc insertData*(n: Node, position: int, data: cstring) proc removeAttribute*(n: Node, attr: cstring) @@ -1471,6 +1496,7 @@ proc createAttribute*(d: Document, identifier: cstring): Node proc getElementsByName*(d: Document, name: cstring): seq[Element] proc getElementsByTagName*(d: Document, name: cstring): seq[Element] proc getElementsByClassName*(d: Document, name: cstring): seq[Element] +proc insertNode*(range: Range, node: Node) proc getSelection*(d: Document): Selection proc handleEvent*(d: Document, event: Event) proc open*(d: Document) @@ -1562,6 +1588,8 @@ proc now*(p: Performance): float # Selection "methods" proc removeAllRanges*(s: Selection) +proc deleteFromDocument*(s: Selection) +proc getRangeAt*(s: Selection, index: int): Range converter toString*(s: Selection): cstring proc `$`*(s: Selection): string = $(s.toString()) From 40b58a0a1864e0cc06ecd0ceb8aadf9e933eeb30 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Thu, 9 Jul 2020 15:57:35 +0800 Subject: [PATCH 0373/3768] fix #11009 (#14935) --- compiler/semdata.nim | 1 + compiler/suggest.nim | 7 ++++++- tests/deprecated/tmessages.nim | 10 ++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 tests/deprecated/tmessages.nim diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 5a2c08de2..8680fcac3 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -143,6 +143,7 @@ type # would otherwise fail. unusedImports*: seq[(PSym, TLineInfo)] exportIndirections*: HashSet[(int, int)] + lastTLineInfo*: TLineInfo template config*(c: PContext): ConfigRef = c.graph.config diff --git a/compiler/suggest.nim b/compiler/suggest.nim index a9f248fb9..6e9844759 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -557,7 +557,12 @@ proc markUsed(c: PContext; info: TLineInfo; s: PSym) = if sfDeprecated in s.owner.flags: warnAboutDeprecated(conf, info, s) if {sfDeprecated, sfError} * s.flags != {}: - if sfDeprecated in s.flags: warnAboutDeprecated(conf, info, s) + if sfDeprecated in s.flags: + if not (c.lastTLineInfo.line == info.line and + c.lastTLineInfo.col == info.col): + warnAboutDeprecated(conf, info, s) + c.lastTLineInfo = info + if sfError in s.flags: userError(conf, info, s) when defined(nimsuggest): suggestSym(conf, info, s, c.graph.usageSym, false) diff --git a/tests/deprecated/tmessages.nim b/tests/deprecated/tmessages.nim new file mode 100644 index 000000000..5884e396d --- /dev/null +++ b/tests/deprecated/tmessages.nim @@ -0,0 +1,10 @@ +discard """ + nimout:'''tmessages.nim(10, 1) Warning: Deprecated since v1.2.0, use 'HelloZ'; hello is deprecated [Deprecated] +''' +""" + +proc hello[T](a: T) {.deprecated: "Deprecated since v1.2.0, use 'HelloZ'".} = + discard + + +hello[int](12) From 64815f59b277ce03e7dd74d9d42a9058379852ec Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Thu, 9 Jul 2020 18:53:12 +0800 Subject: [PATCH 0374/3768] add testcase for #4668 (#14946) closes #4668 closes #9463 --- tests/generics/t4668.nim | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 tests/generics/t4668.nim diff --git a/tests/generics/t4668.nim b/tests/generics/t4668.nim new file mode 100644 index 000000000..0d1b18111 --- /dev/null +++ b/tests/generics/t4668.nim @@ -0,0 +1,16 @@ +discard """ +action: compile +""" + +type + FooObj*[T] = object + v*: T + Foo1*[T] = FooObj[T] + Foo2* = FooObj + +proc foo1(x: Foo1) = echo "foo1" +proc foo2(x: Foo2) = echo "foo2" + +var x: FooObj[float] +foo1(x) # works +foo2(x) # works From c38a459582fb4744ce4abcba2d278b48857c0a31 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 9 Jul 2020 17:24:23 +0200 Subject: [PATCH 0375/3768] fixes #14925 (#14947) --- compiler/ccgstmts.nim | 4 ++++ tests/destructor/tgotoexceptions8.nim | 9 ++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 532272374..c56340e00 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -1218,6 +1218,10 @@ proc genTryGoto(p: BProc; t: PNode; d: var TLoc) = p.nestedTryStmts.add((fin, false, Natural lab)) p.flags.incl nimErrorFlagAccessed + + if not isEmptyType(t.typ) and d.k == locNone: + getTemp(p, t.typ, d) + expr(p, t[0], d) if 1 < t.len and t[1].kind == nkExceptBranch: diff --git a/tests/destructor/tgotoexceptions8.nim b/tests/destructor/tgotoexceptions8.nim index e968cdce2..8ed2ed0ba 100644 --- a/tests/destructor/tgotoexceptions8.nim +++ b/tests/destructor/tgotoexceptions8.nim @@ -8,7 +8,8 @@ outer finally msg1 msg2 finally2 -finally1''' +finally1 +true''' cmd: "nim c --gc:arc $file" """ @@ -67,3 +68,9 @@ when true: echo "finally1" nested_finally() + +# bug #14925 +proc test(b: bool) = + echo b + +test(try: true except: false) From d7ccd82eacd7a03c7cf31ea56315c3a35218e122 Mon Sep 17 00:00:00 2001 From: Ico Doornekamp Date: Thu, 9 Jul 2020 19:18:35 +0200 Subject: [PATCH 0376/3768] VM profiler (#14833) * Crude VM profiler * Added --profileVM flag, refactoring * fixed FileLine hash * Use TLineInfo instead of own FileLine, updated formatting * Refactoring, moved PStackFrame to vmdefs for cleaner data structure * Moved vmprofiler to separate file * Simplified TLineInfo hash, use toFileLineCol for output * Hash * Moved profile data into Config, dump only once at end of run * Changed profile output header to show '#instr' instead of 'count' * Do not accumulate instrcount for non-top frames Co-authored-by: Ico Doornekamp --- compiler/commands.nim | 2 ++ compiler/lineinfos.nim | 5 ++++- compiler/main.nim | 4 +++- compiler/options.nim | 15 ++++++++++++++- compiler/vm.nim | 17 +++++------------ compiler/vmdef.nim | 15 +++++++++++++++ compiler/vmprofiler.nim | 42 +++++++++++++++++++++++++++++++++++++++++ doc/advopt.txt | 1 + 8 files changed, 86 insertions(+), 15 deletions(-) create mode 100644 compiler/vmprofiler.nim diff --git a/compiler/commands.nim b/compiler/commands.nim index 95df45ebe..cbf93238f 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -884,6 +884,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; localError(conf, info, "unknown Nim version; currently supported values are: {1.0}") of "benchmarkvm": processOnOffSwitchG(conf, {optBenchmarkVM}, arg, pass, info) + of "profilevm": + processOnOffSwitchG(conf, {optProfileVM}, arg, pass, info) of "sinkinference": processOnOffSwitch(conf, {optSinkInference}, arg, pass, info) of "panics": diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index 378e06c72..c9821ac9f 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -10,7 +10,7 @@ ## This module contains the ``TMsgKind`` enum as well as the ## ``TLineInfo`` object. -import ropes, tables, pathutils +import ropes, tables, pathutils, hashes const explanationsBaseUrl* = "https://nim-lang.github.io/Nim" @@ -271,6 +271,9 @@ type proc `==`*(a, b: FileIndex): bool {.borrow.} +proc hash*(i: TLineInfo): Hash = + hash (i.line.int, i.col.int, i.fileIndex.int) + proc raiseRecoverableError*(msg: string) {.noinline.} = raise newException(ERecoverableError, msg) diff --git a/compiler/main.nim b/compiler/main.nim index 25a42300f..256ec14cd 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -19,7 +19,7 @@ import cgen, json, nversion, platform, nimconf, passaux, depends, vm, idgen, modules, - modulegraphs, tables, rod, lineinfos, pathutils + modulegraphs, tables, rod, lineinfos, pathutils, vmprofiler when not defined(leanCompiler): import jsgen, docgen, docgen2 @@ -404,6 +404,8 @@ proc mainCommand*(graph: ModuleGraph) = else: output = $conf.absOutFile if optListFullPaths notin conf.globalOptions: output = output.AbsoluteFile.extractFilename + if optProfileVM in conf.globalOptions: + echo conf.dump(conf.vmProfileData) rawMessage(conf, hintSuccessX, [ "loc", loc, "sec", sec, diff --git a/compiler/options.nim b/compiler/options.nim index 247f35419..80881d903 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -9,7 +9,7 @@ import os, strutils, strtabs, sets, lineinfos, platform, - prefixmatches, pathutils, nimpaths + prefixmatches, pathutils, nimpaths, tables from terminal import isatty from times import utc, fromUnix, local, getTime, format, DateTime @@ -95,6 +95,7 @@ type # please make sure we have under 32 options optPanics # turn panics (sysFatal) into a process termination optNimV1Emulation # emulate Nim v1.0 optSourcemap + optProfileVM # enable VM profiler TGlobalOptions* = set[TGlobalOption] @@ -217,6 +218,13 @@ type version*: int Suggestions* = seq[Suggest] + ProfileInfo* = object + time*: float + count*: int + + ProfileData* = ref object + data*: TableRef[TLineInfo, ProfileInfo] + ConfigRef* = ref object ## every global configuration ## fields marked with '*' are subject to ## the incremental compilation mechanisms @@ -314,6 +322,7 @@ type structuredErrorHook*: proc (config: ConfigRef; info: TLineInfo; msg: string; severity: Severity) {.closure, gcsafe.} cppCustomNamespace*: string + vmProfileData*: ProfileData proc assignIfDefault*[T](result: var T, val: T, def = default(T)) = ## if `result` was already assigned to a value (that wasn't `def`), this is a noop. @@ -390,6 +399,9 @@ template newPackageCache*(): untyped = else: modeCaseSensitive) +proc newProfileData(): ProfileData = + ProfileData(data: newTable[TLineInfo, ProfileInfo]()) + proc newConfigRef*(): ConfigRef = result = ConfigRef( selectedGC: gcRefc, @@ -442,6 +454,7 @@ proc newConfigRef*(): ConfigRef = arguments: "", suggestMaxResults: 10_000, maxLoopIterationsVM: 10_000_000, + vmProfileData: newProfileData(), ) setTargetFromSystem(result.target) # enable colors by default on terminals diff --git a/compiler/vm.nim b/compiler/vm.nim index 5d8d21bb9..1edcace82 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -16,7 +16,7 @@ import strutils, msgs, vmdef, vmgen, nimsets, types, passes, parser, vmdeps, idents, trees, renderer, options, transf, parseutils, vmmarshal, gorgeimpl, lineinfos, tables, btrees, macrocacheimpl, - modulegraphs, sighashes, int128 + modulegraphs, sighashes, int128, vmprofiler from semfold import leValueConv, ordinalValToString from evaltempl import evalTemplate @@ -27,17 +27,6 @@ const when hasFFI: import evalffi -type - PStackFrame* = ref TStackFrame - TStackFrame* = object - prc: PSym # current prc; proc that is evaluated - slots: seq[TFullReg] # parameters passed to the proc + locals; - # parameters come first - next: PStackFrame # for stacking - comesFrom: int - safePoints: seq[int] # used for exception handling - # XXX 'break' should perform cleanup actions - # What does the C backend do for it? proc stackTraceAux(c: PCtx; x: PStackFrame; pc: int; recursionLimit=100) = if x != nil: @@ -552,6 +541,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = "ra", regDescr("ra", ra), "rb", regDescr("rb", instr.regB), "rc", regDescr("rc", instr.regC)] + c.profiler.enter(c, tos) + case instr.opcode of opcEof: return regs[ra] of opcRet: @@ -2077,6 +2068,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = if regs[ra].node.strVal.isNil: regs[ra].node.strVal = newStringOfCap(1000) storeAny(regs[ra].node.strVal, typ, regs[rb].regToNode, c.config) + c.profiler.leave(c) + inc pc proc execute(c: PCtx, start: int): PNode = diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index 7b51c626d..9228ce5eb 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -261,6 +261,21 @@ type config*: ConfigRef graph*: ModuleGraph oldErrorCount*: int + profiler*: Profiler + + PStackFrame* = ref TStackFrame + TStackFrame* = object + prc*: PSym # current prc; proc that is evaluated + slots*: seq[TFullReg] # parameters passed to the proc + locals; + # parameters come first + next*: PStackFrame # for stacking + comesFrom*: int + safePoints*: seq[int] # used for exception handling + # XXX 'break' should perform cleanup actions + # What does the C backend do for it? + Profiler* = object + tEnter*: float + tos*: PStackFrame TPosition* = distinct int diff --git a/compiler/vmprofiler.nim b/compiler/vmprofiler.nim new file mode 100644 index 000000000..57a0430b4 --- /dev/null +++ b/compiler/vmprofiler.nim @@ -0,0 +1,42 @@ + +import + options, vmdef, times, lineinfos, strutils, tables, + msgs + +proc enter*(prof: var Profiler, c: PCtx, tos: PStackFrame) {.inline.} = + if optProfileVM in c.config.globalOptions: + prof.tEnter = cpuTime() + prof.tos = tos + +proc leave*(prof: var Profiler, c: PCtx) {.inline.} = + if optProfileVM in c.config.globalOptions: + let tLeave = cpuTime() + var tos = prof.tos + var data = c.config.vmProfileData.data + while tos != nil: + if tos.prc != nil: + let li = tos.prc.info + if li notin data: + data[li] = ProfileInfo() + data[li].time += tLeave - prof.tEnter + if tos == prof.tos: + inc data[li].count + tos = tos.next + +proc dump*(conf: ConfigRef, pd: ProfileData): string = + var data = pd.data + echo "\nprof: µs #instr location" + for i in 0..<32: + var tMax: float + var infoMax: ProfileInfo + var flMax: TLineInfo + for fl, info in data: + if info.time > infoMax.time: + infoMax = info + flMax = fl + if infoMax.count == 0: + break + result.add " " & align($int(infoMax.time * 1e6), 10) & + align($int(infoMax.count), 10) & " " & + conf.toFileLineCol(flMax) & "\n" + data.del flMax diff --git a/doc/advopt.txt b/doc/advopt.txt index f5b8c9ec9..547005485 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -150,5 +150,6 @@ Advanced options: works better with `--stackTrace:on` see also https://nim-lang.github.io/Nim/estp.html --benchmarkVM:on|off enable benchmarking of VM code with cpuTime() + --profileVM:on|off enable compile time VM profiler --sinkInference:on|off en-/disable sink parameter inference (default: on) --panics:on|off turn panics into process terminations (default: off) From f92d2eaa3516b4c11e4d499ef2ec0d6047862f80 Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 9 Jul 2020 19:21:20 +0200 Subject: [PATCH 0377/3768] cleanup of PR #14833 (VM profiler) --- compiler/vmprofiler.nim | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/compiler/vmprofiler.nim b/compiler/vmprofiler.nim index 57a0430b4..f586c8ffe 100644 --- a/compiler/vmprofiler.nim +++ b/compiler/vmprofiler.nim @@ -2,26 +2,29 @@ import options, vmdef, times, lineinfos, strutils, tables, msgs - + proc enter*(prof: var Profiler, c: PCtx, tos: PStackFrame) {.inline.} = if optProfileVM in c.config.globalOptions: prof.tEnter = cpuTime() prof.tos = tos +proc leaveImpl(prof: var Profiler, c: PCtx) {.noinline.} = + let tLeave = cpuTime() + var tos = prof.tos + var data = c.config.vmProfileData.data + while tos != nil: + if tos.prc != nil: + let li = tos.prc.info + if li notin data: + data[li] = ProfileInfo() + data[li].time += tLeave - prof.tEnter + if tos == prof.tos: + inc data[li].count + tos = tos.next + proc leave*(prof: var Profiler, c: PCtx) {.inline.} = if optProfileVM in c.config.globalOptions: - let tLeave = cpuTime() - var tos = prof.tos - var data = c.config.vmProfileData.data - while tos != nil: - if tos.prc != nil: - let li = tos.prc.info - if li notin data: - data[li] = ProfileInfo() - data[li].time += tLeave - prof.tEnter - if tos == prof.tos: - inc data[li].count - tos = tos.next + leaveImpl(prof, c) proc dump*(conf: ConfigRef, pd: ProfileData): string = var data = pd.data From 3e52136f262b9700713eb10594f18e42a2f2af65 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Wed, 8 Jul 2020 18:04:14 +0200 Subject: [PATCH 0378/3768] Cosmetics --- compiler/injectdestructors.nim | 103 ++++++++++++++++----------------- 1 file changed, 51 insertions(+), 52 deletions(-) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 0a14a1daf..41f195d43 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -135,7 +135,7 @@ proc toTree(c: var Con; s: var Scope; ret: PNode; flags: set[ToTreeFlag]): PNode if isExpr: result = newNodeIT(nkStmtListExpr, ret.info, ret.typ) if ret.kind in nkCallKinds + {nkStmtListExpr}: - r = getTemp(c, s, ret.typ, ret.info) + r = c.getTemp(s, ret.typ, ret.info) else: result = newNodeI(nkStmtList, ret.info) @@ -374,11 +374,11 @@ proc genOp(c: Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode = if kind == attachedDestructor: echo "destructor is ", op.id, " ", op.ast if sfError in op.flags: checkForErrorPragma(c, t, ri, AttachedOpToStr[kind]) - genOp(c, op, dest) + c.genOp(op, dest) proc genDestroy(c: Con; dest: PNode): PNode = let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink}) - result = genOp(c, t, attachedDestructor, dest, nil) + result = c.genOp(t, attachedDestructor, dest, nil) proc canBeMoved(c: Con; t: PType): bool {.inline.} = let t = t.skipTypes({tyGenericInst, tyAlias, tySink}) @@ -399,25 +399,24 @@ proc genSink(c: var Con; s: var Scope; dest, ri: PNode, isDecl = false): PNode = else: let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink}) if t.attachedOps[attachedSink] != nil: - result = genOp(c, t, attachedSink, dest, ri) + result = c.genOp(t, attachedSink, dest, ri) result.add ri else: # the default is to use combination of `=destroy(dest)` and # and copyMem(dest, source). This is efficient. - let snk = newTree(nkFastAsgn, dest, ri) - result = newTree(nkStmtList, genDestroy(c, dest), snk) + result = newTree(nkStmtList, c.genDestroy(dest), newTree(nkFastAsgn, dest, ri)) proc genCopyNoCheck(c: Con; dest, ri: PNode): PNode = let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink}) - result = genOp(c, t, attachedAsgn, dest, ri) + result = c.genOp(t, attachedAsgn, dest, ri) proc genCopy(c: var Con; dest, ri: PNode): PNode = let t = dest.typ if tfHasOwned in t.flags and ri.kind != nkNilLit: # try to improve the error message here: if c.otherRead == nil: discard isLastRead(ri, c) - checkForErrorPragma(c, t, ri, "=") - result = genCopyNoCheck(c, dest, ri) + c.checkForErrorPragma(t, ri, "=") + result = c.genCopyNoCheck(dest, ri) proc addTopVar(c: var Con; s: var Scope; v: PNode): ptr Scope = result = addr(s) @@ -430,7 +429,7 @@ proc genDiscriminantAsgn(c: var Con; s: var Scope; n: PNode): PNode = # but fields within active case branch might need destruction # tmp to support self assignments - let tmp = getTemp(c, s, n[1].typ, n.info) + let tmp = c.getTemp(s, n[1].typ, n.info) result = newTree(nkStmtList) result.add newTree(nkFastAsgn, tmp, p(n[1], c, s, consumed)) @@ -457,10 +456,10 @@ It is best to factor out piece of object that needs custom destructor into separ let notExpr = newNodeIT(nkPrefix, n.info, getSysType(c.graph, unknownLineInfo, tyBool)) notExpr.add newSymNode(createMagic(c.graph, "not", mNot)) notExpr.add cond - result.add newTree(nkIfStmt, newTree(nkElifBranch, notExpr, genOp(c, branchDestructor, le))) + result.add newTree(nkIfStmt, newTree(nkElifBranch, notExpr, c.genOp(branchDestructor, le))) result.add newTree(nkFastAsgn, le, tmp) -proc genWasMoved(n: PNode; c: var Con): PNode = +proc genWasMoved(c: var Con, n: PNode): PNode = result = newNodeI(nkCall, n.info) result.add(newSymNode(createMagic(c.graph, "wasMoved", mWasMoved))) result.add copyTree(n) #mWasMoved does not take the address @@ -491,7 +490,7 @@ proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): PNode = v.add(vpart) result.add v - let wasMovedCall = genWasMoved(skipConv(n), c) + let wasMovedCall = c.genWasMoved(skipConv(n)) result.add wasMovedCall result.add tempAsNode @@ -501,10 +500,10 @@ proc isCapturedVar(n: PNode): bool = proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode = result = newNodeIT(nkStmtListExpr, n.info, n.typ) - let tmp = getTemp(c, s, n.typ, n.info) + let tmp = c.getTemp(s, n.typ, n.info) if hasDestructor(n.typ): - result.add genWasMoved(tmp, c) - var m = genCopy(c, tmp, n) + result.add c.genWasMoved(tmp) + var m = c.genCopy(tmp, n) m.add p(n, c, s, normal) result.add m if isLValue(n) and not isCapturedVar(n) and n.typ.skipTypes(abstractInst).kind != tyRef and c.inSpawn == 0: @@ -547,15 +546,15 @@ proc ensureDestruction(arg: PNode; c: var Con; s: var Scope): PNode = result = newNodeIT(nkStmtListExpr, arg.info, arg.typ) if s.parent != nil: - let tmp = getTemp(c, s.parent[], arg.typ, arg.info) - result.add genSink(c, s, tmp, arg, isDecl = true) + let tmp = c.getTemp(s.parent[], arg.typ, arg.info) + result.add c.genSink(s, tmp, arg, isDecl = true) result.add tmp - s.parent[].final.add genDestroy(c, tmp) + s.parent[].final.add c.genDestroy(tmp) else: - let tmp = getTemp(c, s, arg.typ, arg.info) - result.add genSink(c, s, tmp, arg, isDecl = true) + let tmp = c.getTemp(s, arg.typ, arg.info) + result.add c.genSink(s, tmp, arg, isDecl = true) result.add tmp - s.final.add genDestroy(c, tmp) + s.final.add c.genDestroy(tmp) else: result = arg @@ -632,9 +631,9 @@ proc pVarTopLevel(v: PNode; c: var Con; s: var Scope; ri, res: PNode) = elif sfThread notin v.sym.flags: # do not destroy thread vars for now at all for consistency. if sfGlobal in v.sym.flags and s.parent == nil: - c.graph.globalDestructors.add genDestroy(c, v) + c.graph.globalDestructors.add c.genDestroy(v) else: - owningScope[].final.add genDestroy(c, v) + owningScope[].final.add c.genDestroy(v) if ri.kind == nkEmpty and c.inLoop > 0: res.add moveOrCopy(v, genDefaultCall(v.typ, c, v.info), c, s, isDecl = true) elif ri.kind != nkEmpty: @@ -746,14 +745,14 @@ proc pRaiseStmt(n: PNode, c: var Con; s: var Scope): PNode = result = copyNode(n) result.add call else: - let tmp = getTemp(c, s, n[0].typ, n.info) - var m = genCopyNoCheck(c, tmp, n[0]) + let tmp = c.getTemp(s, n[0].typ, n.info) + var m = c.genCopyNoCheck(tmp, n[0]) m.add p(n[0], c, s, normal) - result = newTree(nkStmtList, genWasMoved(tmp, c), m) + result = newTree(nkStmtList, c.genWasMoved(tmp), m) var toDisarm = n[0] if toDisarm.kind == nkStmtListExpr: toDisarm = toDisarm.lastSon if toDisarm.kind == nkSym and toDisarm.sym.owner == c.owner: - result.add genWasMoved(toDisarm, c) + result.add c.genWasMoved(toDisarm) result.add newTree(nkRaiseStmt, tmp) else: result = copyNode(n) @@ -863,7 +862,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode = if n[0].kind == nkSym and n[0].sym.magic in {mNew, mNewFinalize}: result[0] = copyTree(n[0]) if c.graph.config.selectedGC in {gcHooks, gcArc, gcOrc}: - let destroyOld = genDestroy(c, result[1]) + let destroyOld = c.genDestroy(result[1]) result = newTree(nkStmtList, destroyOld, result) else: result[0] = p(n[0], c, s, normal) @@ -915,7 +914,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode = assert n[1].kind notin {nkAsgn, nkFastAsgn} result = moveOrCopy(p(n[0], c, s, mode), n[1], c, s) elif isDiscriminantField(n[0]): - result = genDiscriminantAsgn(c, s, n) + result = c.genDiscriminantAsgn(s, n) else: result = copyNode(n) result.add p(n[0], c, s, mode) @@ -967,7 +966,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode = result[i] = n[i] if mode == sinkArg and hasDestructor(n.typ): if isAnalysableFieldAccess(n, c.owner) and isLastRead(n, c): - s.wasMoved.add genWasMoved(n, c) + s.wasMoved.add c.genWasMoved(n) else: result = passCopyToSink(result, c, s) @@ -978,9 +977,9 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode = if mode == sinkArg and hasDestructor(n.typ): if isAnalysableFieldAccess(n, c.owner) and isLastRead(n, c): # consider 'a[(g; destroy(g); 3)]', we want to say 'wasMoved(a[3])' - # without the junk, hence 'genWasMoved(n, c)' - # and not 'genWasMoved(result, c)': - s.wasMoved.add genWasMoved(n, c) + # without the junk, hence 'c.genWasMoved(n)' + # and not 'c.genWasMoved(result)': + s.wasMoved.add c.genWasMoved(n) else: result = passCopyToSink(result, c, s) @@ -1014,43 +1013,43 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode = proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, isDecl = false): PNode = case ri.kind of nkCallKinds: - result = genSink(c, s, dest, p(ri, c, s, consumed), isDecl) + result = c.genSink(s, dest, p(ri, c, s, consumed), isDecl) of nkBracketExpr: if isUnpackedTuple(ri[0]): # unpacking of tuple: take over the elements - result = genSink(c, s, dest, p(ri, c, s, consumed), isDecl) + result = c.genSink(s, dest, p(ri, c, s, consumed), isDecl) elif isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c) and not aliases(dest, ri): # Rule 3: `=sink`(x, z); wasMoved(z) - var snk = genSink(c, s, dest, ri, isDecl) - result = newTree(nkStmtList, snk, genWasMoved(ri, c)) + var snk = c.genSink(s, dest, ri, isDecl) + result = newTree(nkStmtList, snk, c.genWasMoved(ri)) else: - result = genCopy(c, dest, ri) + result = c.genCopy(dest, ri) result.add p(ri, c, s, consumed) of nkBracket: # array constructor if ri.len > 0 and isDangerousSeq(ri.typ): - result = genCopy(c, dest, ri) + result = c.genCopy(dest, ri) result.add p(ri, c, s, consumed) else: - result = genSink(c, s, dest, p(ri, c, s, consumed), isDecl) + result = c.genSink(s, dest, p(ri, c, s, consumed), isDecl) of nkObjConstr, nkTupleConstr, nkClosure, nkCharLit..nkNilLit: - result = genSink(c, s, dest, p(ri, c, s, consumed), isDecl) + result = c.genSink(s, dest, p(ri, c, s, consumed), isDecl) of nkSym: if isSinkParam(ri.sym) and isLastRead(ri, c): # Rule 3: `=sink`(x, z); wasMoved(z) - let snk = genSink(c, s, dest, ri, isDecl) - result = newTree(nkStmtList, snk, genWasMoved(ri, c)) + let snk = c.genSink(s, dest, ri, isDecl) + result = newTree(nkStmtList, snk, c.genWasMoved(ri)) elif ri.sym.kind != skParam and ri.sym.owner == c.owner and isLastRead(ri, c) and canBeMoved(c, dest.typ): # Rule 3: `=sink`(x, z); wasMoved(z) - let snk = genSink(c, s, dest, ri, isDecl) - result = newTree(nkStmtList, snk, genWasMoved(ri, c)) + let snk = c.genSink(s, dest, ri, isDecl) + result = newTree(nkStmtList, snk, c.genWasMoved(ri)) else: - result = genCopy(c, dest, ri) + result = c.genCopy(dest, ri) result.add p(ri, c, s, consumed) of nkHiddenSubConv, nkHiddenStdConv, nkConv, nkObjDownConv, nkObjUpConv: - result = genSink(c, s, dest, p(ri, c, s, sinkArg), isDecl) + result = c.genSink(s, dest, p(ri, c, s, sinkArg), isDecl) of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt: template process(child, s): untyped = moveOrCopy(dest, child, c, s, isDecl) handleNestedTempl(ri, process, true) @@ -1060,10 +1059,10 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, isDecl = false): PNod if isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c) and canBeMoved(c, dest.typ): # Rule 3: `=sink`(x, z); wasMoved(z) - let snk = genSink(c, s, dest, ri, isDecl) - result = newTree(nkStmtList, snk, genWasMoved(ri, c)) + let snk = c.genSink(s, dest, ri, isDecl) + result = newTree(nkStmtList, snk, c.genWasMoved(ri)) else: - result = genCopy(c, dest, ri) + result = c.genCopy(dest, ri) result.add p(ri, c, s, consumed) proc computeUninit(c: var Con) = @@ -1127,7 +1126,7 @@ proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode = for i in 1.. Date: Wed, 8 Jul 2020 20:48:17 +0200 Subject: [PATCH 0379/3768] Move `wasMoved` out of `=destroy` --- compiler/ccgexprs.nim | 4 ++-- compiler/injectdestructors.nim | 10 +++++----- compiler/liftdestructors.nim | 10 +--------- lib/system/seqs_v2.nim | 3 +-- lib/system/widestrs.nim | 1 - 5 files changed, 9 insertions(+), 19 deletions(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index c194ce326..dc9617fe2 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2100,14 +2100,14 @@ proc genDestroy(p: BProc; n: PNode) = initLocExpr(p, arg, a) linefmt(p, cpsStmts, "if ($1.p && !($1.p->cap & NIM_STRLIT_FLAG)) {$n" & " #deallocShared($1.p);$n" & - " $1.p = NIM_NIL; $1.len = 0; }$n", + "}$n", [rdLoc(a)]) of tySequence: var a: TLoc initLocExpr(p, arg, a) linefmt(p, cpsStmts, "if ($1.p && !($1.p->cap & NIM_STRLIT_FLAG)) {$n" & " #deallocShared($1.p);$n" & - " $1.p = NIM_NIL; $1.len = 0; }$n", + "}$n", [rdLoc(a), getTypeDesc(p.module, t.lastSon)]) else: discard "nothing to do" else: diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 41f195d43..f1284f523 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -93,7 +93,7 @@ proc optimize(s: var Scope) = proc findCorrespondingDestroy(final: seq[PNode]; moved: PNode): int = # remember that it's destroy(addr(x)) for i in 0 ..< final.len: - if final[i] != nil and exprStructuralEquivalent(final[i][1].skipAddr, moved, strictSymEquality = true): + if final[i] != nil and exprStructuralEquivalent(final[i][0][1].skipAddr, moved, strictSymEquality = true): return i return -1 @@ -549,12 +549,12 @@ proc ensureDestruction(arg: PNode; c: var Con; s: var Scope): PNode = let tmp = c.getTemp(s.parent[], arg.typ, arg.info) result.add c.genSink(s, tmp, arg, isDecl = true) result.add tmp - s.parent[].final.add c.genDestroy(tmp) + s.parent[].final.add newTree(nkStmtList, c.genDestroy(tmp), c.genWasMoved(tmp)) else: let tmp = c.getTemp(s, arg.typ, arg.info) result.add c.genSink(s, tmp, arg, isDecl = true) result.add tmp - s.final.add c.genDestroy(tmp) + s.final.add newTree(nkStmtList, c.genDestroy(tmp), c.genWasMoved(tmp)) else: result = arg @@ -631,9 +631,9 @@ proc pVarTopLevel(v: PNode; c: var Con; s: var Scope; ri, res: PNode) = elif sfThread notin v.sym.flags: # do not destroy thread vars for now at all for consistency. if sfGlobal in v.sym.flags and s.parent == nil: - c.graph.globalDestructors.add c.genDestroy(v) + c.graph.globalDestructors.add c.genDestroy(v) #No need to genWasMoved here else: - owningScope[].final.add c.genDestroy(v) + owningScope[].final.add newTree(nkStmtList, c.genDestroy(v), c.genWasMoved(v)) if ri.kind == nkEmpty and c.inLoop > 0: res.add moveOrCopy(v, genDefaultCall(v.typ, c, v.info), c, s, isDecl = true) elif ri.kind != nkEmpty: diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 0346f6c67..21fef1d61 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -535,7 +535,6 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = body.add newAsgnStmt(x, y) of attachedDestructor: body.add genIf(c, cond, actions) - body.add newAsgnStmt(x, newNodeIT(nkNilLit, body.info, t)) of attachedDeepCopy: assert(false, "cannot happen") of attachedTrace: if isFinal(elemType): @@ -584,7 +583,6 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = body.add newAsgnStmt(x, y) of attachedDestructor: body.add genIf(c, cond, actions) - body.add newAsgnStmt(xenv, newNodeIT(nkNilLit, body.info, xenv.typ)) of attachedDeepCopy: assert(false, "cannot happen") of attachedTrace: body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, genAddrOf(xenv), y) @@ -613,7 +611,6 @@ proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = # prevent wrong "dangling refs exist" problems: var actions = newNodeI(nkStmtList, c.info) actions.add callCodegenProc(c.g, "nimDecWeakRef", c.info, x) - actions.add newAsgnStmt(x, newNodeIT(nkNilLit, body.info, t)) let des = genIf(c, x, actions) if body.len == 0: body.add des @@ -642,7 +639,6 @@ proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = body.add newAsgnStmt(x, y) of attachedDestructor: body.add genIf(c, x, actions) - body.add newAsgnStmt(x, newNodeIT(nkNilLit, body.info, t)) of attachedDeepCopy: assert(false, "cannot happen") of attachedTrace, attachedDispose: discard @@ -672,10 +668,7 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = body.add genIf(c, xx, callCodegenProc(c.g, "nimDecWeakRef", c.info, xx)) body.add newAsgnStmt(x, y) of attachedDestructor: - var actions = newNodeI(nkStmtList, c.info) - actions.add callCodegenProc(c.g, "nimDecWeakRef", c.info, xx) - actions.add newAsgnStmt(xx, newNodeIT(nkNilLit, body.info, xx.typ)) - let des = genIf(c, xx, actions) + let des = genIf(c, xx, callCodegenProc(c.g, "nimDecWeakRef", c.info, xx)) if body.len == 0: body.add des else: @@ -695,7 +688,6 @@ proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = body.add newAsgnStmt(x, y) of attachedDestructor: body.add genIf(c, xx, actions) - body.add newAsgnStmt(xx, newNodeIT(nkNilLit, body.info, xx.typ)) of attachedDeepCopy: assert(false, "cannot happen") of attachedTrace, attachedDispose: discard diff --git a/lib/system/seqs_v2.nim b/lib/system/seqs_v2.nim index 1b40c00ab..4001e97c3 100644 --- a/lib/system/seqs_v2.nim +++ b/lib/system/seqs_v2.nim @@ -73,11 +73,10 @@ proc shrink*[T](x: var seq[T]; newLen: Natural) = when nimvm: setLen(x, newLen) else: - mixin `=destroy` #sysAssert newLen <= x.len, "invalid newLen parameter for 'shrink'" when not supportsCopyMem(T): for i in countdown(x.len - 1, newLen): - `=destroy`(x[i]) + reset x[i] # XXX This is wrong for const seqs that were moved into 'x'! cast[ptr NimSeqV2[T]](addr x).len = newLen diff --git a/lib/system/widestrs.nim b/lib/system/widestrs.nim index aabcbdc90..83c11eb79 100644 --- a/lib/system/widestrs.nim +++ b/lib/system/widestrs.nim @@ -28,7 +28,6 @@ when defined(nimv2): proc `=destroy`(a: var WideCStringObj) = if a.data != nil: deallocShared(a.data) - a.data = nil proc `=`(a: var WideCStringObj; b: WideCStringObj) {.error.} From bf51cee4311f5715c2eac675e95fb3d4f2909404 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Thu, 9 Jul 2020 17:59:40 +0200 Subject: [PATCH 0380/3768] Update docs and changelog --- changelog.md | 2 ++ doc/destructors.rst | 7 ++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index 1eda82297..36582eee2 100644 --- a/changelog.md +++ b/changelog.md @@ -143,6 +143,8 @@ ## Language changes +- The `=destroy` hook no longer has to reset its target, as the compiler now automatically inserts + wasMoved calls where needed. - In the newruntime it is now allowed to assign to the discriminator field without restrictions as long as case object doesn't have custom destructor. The discriminator value doesn't have to be a constant either. If you have a diff --git a/doc/destructors.rst b/doc/destructors.rst index 4285bad8b..fefc646f5 100644 --- a/doc/destructors.rst +++ b/doc/destructors.rst @@ -39,12 +39,12 @@ written as: if x.data != nil: for i in 0.. Date: Fri, 10 Jul 2020 09:45:41 +0200 Subject: [PATCH 0381/3768] renderer.nim: more obvious debug output --- compiler/renderer.nim | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 4b8c78cc7..449ee4504 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -1016,11 +1016,21 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = put(g, tkRStrLit, '\"' & replace(n[1].strVal, "\"", "\"\"") & '\"') else: gsub(g, n, 1) - of nkHiddenStdConv, nkHiddenSubConv, nkHiddenCallConv: + of nkHiddenStdConv, nkHiddenSubConv: if n.len >= 2: gsub(g, n[1]) else: put(g, tkSymbol, "(wrong conv)") + of nkHiddenCallConv: + if renderIds in g.flags: + accentedName(g, n[0]) + put(g, tkParLe, "(") + gcomma(g, n, 1) + put(g, tkParRi, ")") + elif n.len >= 2: + gsub(g, n[1]) + else: + put(g, tkSymbol, "(wrong conv)") of nkCast: put(g, tkCast, "cast") put(g, tkBracketLe, "[") From 37a98c77f32a5a7f2397ea9faabea6e04e5123ed Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 10 Jul 2020 09:51:43 +0200 Subject: [PATCH 0382/3768] optimize sinks even when in a loop --- compiler/injectdestructors.nim | 35 ++++++++++++---------------------- 1 file changed, 12 insertions(+), 23 deletions(-) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index f1284f523..d20b4de15 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -46,7 +46,6 @@ type owner: PSym g: ControlFlowGraph jumpTargets: IntSet - destroys, topLevelVars: PNode graph: ModuleGraph emptyNode: PNode otherRead: PNode @@ -59,6 +58,13 @@ type consumed sinkArg +const toDebug {.strdefine.} = "" + +template dbg(body) = + when toDebug.len > 0: + if c.owner.name.s == toDebug or toDebug == "always": + body + proc getTemp(c: var Con; s: var Scope; typ: PType; info: TLineInfo): PNode = let sym = newSym(skTemp, getIdent(c.graph.cache, ":tmpD"), c.owner, info) sym.typ = typ @@ -89,7 +95,6 @@ proc optimize(s: var Scope) = Now assume 'echo' raises, then we shouldn't do the 'wasMoved(s)' ]# - # XXX: Investigate how to really insert 'wasMoved()' calls! proc findCorrespondingDestroy(final: seq[PNode]; moved: PNode): int = # remember that it's destroy(addr(x)) for i in 0 ..< final.len: @@ -169,14 +174,6 @@ proc toTree(c: var Con; s: var Scope; ret: PNode; flags: set[ToTreeFlag]): PNode if r != nil: result.add r - -const toDebug {.strdefine.} = "" - -template dbg(body) = - when toDebug.len > 0: - if c.owner.name.s == toDebug or toDebug == "always": - body - proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope; isDecl = false): PNode @@ -391,7 +388,7 @@ proc isNoInit(dest: PNode): bool {.inline.} = result = dest.kind == nkSym and sfNoInit in dest.sym.flags proc genSink(c: var Con; s: var Scope; dest, ri: PNode, isDecl = false): PNode = - if isUnpackedTuple(dest) or (isDecl and c.inLoop <= 0) or + if isUnpackedTuple(dest) or isDecl or (isAnalysableFieldAccess(dest, c.owner) and isFirstWrite(dest, c)) or isNoInit(dest): # optimize sink call into a bitwise memcopy @@ -545,16 +542,10 @@ proc ensureDestruction(arg: PNode; c: var Con; s: var Scope): PNode = # This was already done in the sink parameter handling logic. result = newNodeIT(nkStmtListExpr, arg.info, arg.typ) - if s.parent != nil: - let tmp = c.getTemp(s.parent[], arg.typ, arg.info) - result.add c.genSink(s, tmp, arg, isDecl = true) - result.add tmp - s.parent[].final.add newTree(nkStmtList, c.genDestroy(tmp), c.genWasMoved(tmp)) - else: - let tmp = c.getTemp(s, arg.typ, arg.info) - result.add c.genSink(s, tmp, arg, isDecl = true) - result.add tmp - s.final.add newTree(nkStmtList, c.genDestroy(tmp), c.genWasMoved(tmp)) + let tmp = c.getTemp(s, arg.typ, arg.info) + result.add c.genSink(s, tmp, arg, isDecl = true) + result.add tmp + s.final.add newTree(nkStmtList, c.genDestroy(tmp), c.genWasMoved(tmp)) else: result = arg @@ -1103,8 +1094,6 @@ proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode = return n var c: Con c.owner = owner - c.destroys = newNodeI(nkStmtList, n.info) - c.topLevelVars = newNodeI(nkVarSection, n.info) c.graph = g c.emptyNode = newNodeI(nkEmpty, n.info) let cfg = constructCfg(owner, n) From b59385f22b0c9d7826721079cef879a354c9529a Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 10 Jul 2020 10:14:14 +0200 Subject: [PATCH 0383/3768] no wasMoved() calls after destructors necessary --- compiler/injectdestructors.nim | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index d20b4de15..d5de70e29 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -87,18 +87,18 @@ proc optimize(s: var Scope) = try: var s = ["hi", inp & "more"] for i in 0..4: - echo s + use s consume(s) wasMoved(s) finally: destroy(x) - Now assume 'echo' raises, then we shouldn't do the 'wasMoved(s)' + Now assume 'use' raises, then we shouldn't do the 'wasMoved(s)' ]# proc findCorrespondingDestroy(final: seq[PNode]; moved: PNode): int = # remember that it's destroy(addr(x)) for i in 0 ..< final.len: - if final[i] != nil and exprStructuralEquivalent(final[i][0][1].skipAddr, moved, strictSymEquality = true): + if final[i] != nil and exprStructuralEquivalent(final[i][1].skipAddr, moved, strictSymEquality = true): return i return -1 @@ -129,7 +129,7 @@ type producesValue proc toTree(c: var Con; s: var Scope; ret: PNode; flags: set[ToTreeFlag]): PNode = - if not s.needsTry: optimize(s) + #if not s.needsTry: optimize(s) assert ret != nil if s.vars.len == 0 and s.final.len == 0 and s.wasMoved.len == 0: # trivial, nothing was done: @@ -545,7 +545,7 @@ proc ensureDestruction(arg: PNode; c: var Con; s: var Scope): PNode = let tmp = c.getTemp(s, arg.typ, arg.info) result.add c.genSink(s, tmp, arg, isDecl = true) result.add tmp - s.final.add newTree(nkStmtList, c.genDestroy(tmp), c.genWasMoved(tmp)) + s.final.add c.genDestroy(tmp) else: result = arg @@ -624,7 +624,7 @@ proc pVarTopLevel(v: PNode; c: var Con; s: var Scope; ri, res: PNode) = if sfGlobal in v.sym.flags and s.parent == nil: c.graph.globalDestructors.add c.genDestroy(v) #No need to genWasMoved here else: - owningScope[].final.add newTree(nkStmtList, c.genDestroy(v), c.genWasMoved(v)) + owningScope[].final.add c.genDestroy(v) if ri.kind == nkEmpty and c.inLoop > 0: res.add moveOrCopy(v, genDefaultCall(v.typ, c, v.info), c, s, isDecl = true) elif ri.kind != nkEmpty: From 112511084d61edc0bb748ff8c267fb54bc7e9716 Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 10 Jul 2020 10:58:16 +0200 Subject: [PATCH 0384/3768] fixes the tcontrolflow regression, clen idea of an escaping expression --- compiler/injectdestructors.nim | 64 +++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 20 deletions(-) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index d5de70e29..d091e8e29 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -40,6 +40,7 @@ type escapingSyms: IntSet # a construct like (block: let x = f(); x) # means that 'x' escapes. We then destroy it # in the parent's scope (and also allocate it there). + escapingExpr: PNode type Con = object @@ -129,6 +130,9 @@ type producesValue proc toTree(c: var Con; s: var Scope; ret: PNode; flags: set[ToTreeFlag]): PNode = + proc isComplexStmtListExpr(n: PNode): bool = + n.kind == nkStmtListExpr and (n.len == 0 or n[^1].kind != nkSym) + #if not s.needsTry: optimize(s) assert ret != nil if s.vars.len == 0 and s.final.len == 0 and s.wasMoved.len == 0: @@ -139,7 +143,7 @@ proc toTree(c: var Con; s: var Scope; ret: PNode; flags: set[ToTreeFlag]): PNode var r = PNode(nil) if isExpr: result = newNodeIT(nkStmtListExpr, ret.info, ret.typ) - if ret.kind in nkCallKinds + {nkStmtListExpr}: + if ret.kind in nkCallKinds or isComplexStmtListExpr(ret): r = c.getTemp(s, ret.typ, ret.info) else: result = newNodeI(nkStmtList, ret.info) @@ -167,12 +171,20 @@ proc toTree(c: var Con; s: var Scope; ret: PNode; flags: set[ToTreeFlag]): PNode result.add newTree(nkFastAsgn, r, ret[last]) else: result.add newTree(nkFastAsgn, r, ret) + for m in s.wasMoved: result.add m + for i in countdown(s.final.high, 0): result.add s.final[i] + result.add r + elif ret.kind == nkStmtListExpr: + # simplify it a bit further by merging the nkStmtListExprs + let last = ret.len - 1 + for i in 0 ..< last: result.add ret[i] + for m in s.wasMoved: result.add m + for i in countdown(s.final.high, 0): result.add s.final[i] + result.add ret[last] else: result.add ret - for m in s.wasMoved: result.add m - for i in countdown(s.final.high, 0): result.add s.final[i] - if r != nil: - result.add r + for m in s.wasMoved: result.add m + for i in countdown(s.final.high, 0): result.add s.final[i] proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope; isDecl = false): PNode @@ -534,18 +546,23 @@ proc containsConstSeq(n: PNode): bool = if containsConstSeq(son): return true else: discard -proc ensureDestruction(arg: PNode; c: var Con; s: var Scope): PNode = +proc ensureDestruction(arg, orig: PNode; c: var Con; s: var Scope): PNode = # it can happen that we need to destroy expression contructors # like [], (), closures explicitly in order to not leak them. if arg.typ != nil and hasDestructor(arg.typ): # produce temp creation for (fn, env). But we need to move 'env'? # This was already done in the sink parameter handling logic. result = newNodeIT(nkStmtListExpr, arg.info, arg.typ) - - let tmp = c.getTemp(s, arg.typ, arg.info) - result.add c.genSink(s, tmp, arg, isDecl = true) - result.add tmp - s.final.add c.genDestroy(tmp) + if orig == s.escapingExpr and s.parent != nil: + let tmp = c.getTemp(s.parent[], arg.typ, arg.info) + result.add c.genSink(s.parent[], tmp, arg, isDecl = true) + result.add tmp + s.parent[].final.add c.genDestroy(tmp) + else: + let tmp = c.getTemp(s, arg.typ, arg.info) + result.add c.genSink(s, tmp, arg, isDecl = true) + result.add tmp + s.final.add c.genDestroy(tmp) else: result = arg @@ -590,28 +607,35 @@ proc cycleCheck(n: PNode; c: var Con) = message(c.graph.config, n.info, warnCycleCreated, msg) break -proc markEscapingVars(n: PNode; s: var Scope) = +proc markEscapingVarsRec(n: PNode; s: var Scope) = case n.kind of nkSym: s.escapingSyms.incl n.sym.id of nkDotExpr, nkBracketExpr, nkCheckedFieldExpr, nkDerefExpr, nkHiddenDeref, nkAddr, nkHiddenAddr, nkStringToCString, nkCStringToString, nkObjDownConv, nkObjUpConv: - markEscapingVars(n[0], s) + markEscapingVarsRec(n[0], s) of nkCast, nkHiddenSubConv, nkHiddenStdConv, nkConv: - markEscapingVars(n[1], s) + markEscapingVarsRec(n[1], s) of nkTupleConstr, nkBracket, nkPar, nkClosure: for i in 0 ..< n.len: - markEscapingVars(n[i], s) + markEscapingVarsRec(n[i], s) of nkObjConstr: for i in 1 ..< n.len: - markEscapingVars(n[i], s) + markEscapingVarsRec(n[i], s) of nkStmtList, nkStmtListExpr: if n.len > 0: - markEscapingVars(n[^1], s) + markEscapingVarsRec(n[^1], s) else: discard "no arbitrary tree traversal here" +proc markEscapingVars(n: PNode; s: var Scope) = + markEscapingVarsRec(n, s) + var it = n + while it.kind in {nkStmtList, nkStmtListExpr} and it.len > 0: + it = it[^1] + s.escapingExpr = it + proc pVarTopLevel(v: PNode; c: var Con; s: var Scope; ri, res: PNode) = # move the variable declaration to the top of the frame: let owningScope = c.addTopVar(s, v) @@ -820,7 +844,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode = else: result[i] = p(n[i], c, s, m) if mode == normal and isRefConstr: - result = ensureDestruction(result, c, s) + result = ensureDestruction(result, n, c, s) of nkCallKinds: let inSpawn = c.inSpawn if n[0].kind == nkSym and n[0].sym.magic == mSpawn: @@ -859,7 +883,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode = result[0] = p(n[0], c, s, normal) if canRaise(n[0]): s.needsTry = true if mode == normal: - result = ensureDestruction(result, c, s) + result = ensureDestruction(result, n, c, s) of nkDiscardStmt: # Small optimization result = shallowCopy(n) if n[0].kind != nkEmpty: @@ -927,7 +951,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode = result[i] = p(n[i], c, s, normal) if n.typ != nil and hasDestructor(n.typ): if mode == normal: - result = ensureDestruction(result, c, s) + result = ensureDestruction(result, n, c, s) of nkHiddenSubConv, nkHiddenStdConv, nkConv: # we have an "ownership invariance" for all constructors C(x). From 4603197fe8f2c6fb1fd74fa7ad5b4ef510e8bfb7 Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 10 Jul 2020 12:27:32 +0200 Subject: [PATCH 0385/3768] fixes a minor regression --- compiler/injectdestructors.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index d091e8e29..da41b2d5c 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -555,7 +555,7 @@ proc ensureDestruction(arg, orig: PNode; c: var Con; s: var Scope): PNode = result = newNodeIT(nkStmtListExpr, arg.info, arg.typ) if orig == s.escapingExpr and s.parent != nil: let tmp = c.getTemp(s.parent[], arg.typ, arg.info) - result.add c.genSink(s.parent[], tmp, arg, isDecl = true) + result.add c.genSink(s, tmp, arg, isDecl = true) result.add tmp s.parent[].final.add c.genDestroy(tmp) else: From 04232d92cd401bf301a9e4cac582273356c86cfa Mon Sep 17 00:00:00 2001 From: Manuel Bojato <30560560+KingDarBoja@users.noreply.github.com> Date: Fri, 10 Jul 2020 08:06:07 -0500 Subject: [PATCH 0386/3768] Fix theme switch load from local storage (#14897) * fix theme switch load from local storage * Fix tests Co-authored-by: Clyybber --- config/nimdoc.cfg | 19 +++++-------------- .../expected/index.html | 19 +++++-------------- .../expected/theindex.html | 19 +++++-------------- .../expected/subdir/subdir_b/utils.html | 19 +++++-------------- nimdoc/testproject/expected/testproject.html | 19 +++++-------------- nimdoc/testproject/expected/theindex.html | 19 +++++-------------- 6 files changed, 30 insertions(+), 84 deletions(-) diff --git a/config/nimdoc.cfg b/config/nimdoc.cfg index 945a3dc6c..3560c0d65 100644 --- a/config/nimdoc.cfg +++ b/config/nimdoc.cfg @@ -240,21 +240,12 @@ function main() { toggleSwitch.addEventListener('change', switchTheme, false); + const currentTheme = localStorage.getItem('theme') ? localStorage.getItem('theme') : null; + if (currentTheme) { + document.documentElement.setAttribute('data-theme', currentTheme); - if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { - document.documentElement.setAttribute('data-theme', "dark"); - toggleSwitch.checked = true; - } else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches) { - document.documentElement.setAttribute('data-theme', "light"); - toggleSwitch.checked = false; - } else { - const currentTheme = localStorage.getItem('theme') ? localStorage.getItem('theme') : null; - if (currentTheme) { - document.documentElement.setAttribute('data-theme', currentTheme); - - if (currentTheme === 'dark') { - toggleSwitch.checked = true; - } + if (currentTheme === 'dark') { + toggleSwitch.checked = true; } } } diff --git a/nimdoc/test_out_index_dot_html/expected/index.html b/nimdoc/test_out_index_dot_html/expected/index.html index a8b65b2c9..f35334503 100644 --- a/nimdoc/test_out_index_dot_html/expected/index.html +++ b/nimdoc/test_out_index_dot_html/expected/index.html @@ -47,21 +47,12 @@ function main() { toggleSwitch.addEventListener('change', switchTheme, false); + const currentTheme = localStorage.getItem('theme') ? localStorage.getItem('theme') : null; + if (currentTheme) { + document.documentElement.setAttribute('data-theme', currentTheme); - if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { - document.documentElement.setAttribute('data-theme', "dark"); - toggleSwitch.checked = true; - } else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches) { - document.documentElement.setAttribute('data-theme', "light"); - toggleSwitch.checked = false; - } else { - const currentTheme = localStorage.getItem('theme') ? localStorage.getItem('theme') : null; - if (currentTheme) { - document.documentElement.setAttribute('data-theme', currentTheme); - - if (currentTheme === 'dark') { - toggleSwitch.checked = true; - } + if (currentTheme === 'dark') { + toggleSwitch.checked = true; } } } diff --git a/nimdoc/test_out_index_dot_html/expected/theindex.html b/nimdoc/test_out_index_dot_html/expected/theindex.html index 095dd7471..b2863f99a 100644 --- a/nimdoc/test_out_index_dot_html/expected/theindex.html +++ b/nimdoc/test_out_index_dot_html/expected/theindex.html @@ -47,21 +47,12 @@ function main() { toggleSwitch.addEventListener('change', switchTheme, false); + const currentTheme = localStorage.getItem('theme') ? localStorage.getItem('theme') : null; + if (currentTheme) { + document.documentElement.setAttribute('data-theme', currentTheme); - if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { - document.documentElement.setAttribute('data-theme', "dark"); - toggleSwitch.checked = true; - } else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches) { - document.documentElement.setAttribute('data-theme', "light"); - toggleSwitch.checked = false; - } else { - const currentTheme = localStorage.getItem('theme') ? localStorage.getItem('theme') : null; - if (currentTheme) { - document.documentElement.setAttribute('data-theme', currentTheme); - - if (currentTheme === 'dark') { - toggleSwitch.checked = true; - } + if (currentTheme === 'dark') { + toggleSwitch.checked = true; } } } diff --git a/nimdoc/testproject/expected/subdir/subdir_b/utils.html b/nimdoc/testproject/expected/subdir/subdir_b/utils.html index 6e83f718f..eefbcfdf9 100644 --- a/nimdoc/testproject/expected/subdir/subdir_b/utils.html +++ b/nimdoc/testproject/expected/subdir/subdir_b/utils.html @@ -47,21 +47,12 @@ function main() { toggleSwitch.addEventListener('change', switchTheme, false); + const currentTheme = localStorage.getItem('theme') ? localStorage.getItem('theme') : null; + if (currentTheme) { + document.documentElement.setAttribute('data-theme', currentTheme); - if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { - document.documentElement.setAttribute('data-theme', "dark"); - toggleSwitch.checked = true; - } else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches) { - document.documentElement.setAttribute('data-theme', "light"); - toggleSwitch.checked = false; - } else { - const currentTheme = localStorage.getItem('theme') ? localStorage.getItem('theme') : null; - if (currentTheme) { - document.documentElement.setAttribute('data-theme', currentTheme); - - if (currentTheme === 'dark') { - toggleSwitch.checked = true; - } + if (currentTheme === 'dark') { + toggleSwitch.checked = true; } } } diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html index 648ecb629..479a2e89d 100644 --- a/nimdoc/testproject/expected/testproject.html +++ b/nimdoc/testproject/expected/testproject.html @@ -47,21 +47,12 @@ function main() { toggleSwitch.addEventListener('change', switchTheme, false); + const currentTheme = localStorage.getItem('theme') ? localStorage.getItem('theme') : null; + if (currentTheme) { + document.documentElement.setAttribute('data-theme', currentTheme); - if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { - document.documentElement.setAttribute('data-theme', "dark"); - toggleSwitch.checked = true; - } else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches) { - document.documentElement.setAttribute('data-theme', "light"); - toggleSwitch.checked = false; - } else { - const currentTheme = localStorage.getItem('theme') ? localStorage.getItem('theme') : null; - if (currentTheme) { - document.documentElement.setAttribute('data-theme', currentTheme); - - if (currentTheme === 'dark') { - toggleSwitch.checked = true; - } + if (currentTheme === 'dark') { + toggleSwitch.checked = true; } } } diff --git a/nimdoc/testproject/expected/theindex.html b/nimdoc/testproject/expected/theindex.html index 0b835c409..66dea92fc 100644 --- a/nimdoc/testproject/expected/theindex.html +++ b/nimdoc/testproject/expected/theindex.html @@ -47,21 +47,12 @@ function main() { toggleSwitch.addEventListener('change', switchTheme, false); + const currentTheme = localStorage.getItem('theme') ? localStorage.getItem('theme') : null; + if (currentTheme) { + document.documentElement.setAttribute('data-theme', currentTheme); - if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) { - document.documentElement.setAttribute('data-theme', "dark"); - toggleSwitch.checked = true; - } else if (window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches) { - document.documentElement.setAttribute('data-theme', "light"); - toggleSwitch.checked = false; - } else { - const currentTheme = localStorage.getItem('theme') ? localStorage.getItem('theme') : null; - if (currentTheme) { - document.documentElement.setAttribute('data-theme', currentTheme); - - if (currentTheme === 'dark') { - toggleSwitch.checked = true; - } + if (currentTheme === 'dark') { + toggleSwitch.checked = true; } } } From 371b416ac818412909243d10a66c20db3eaa23f3 Mon Sep 17 00:00:00 2001 From: Miran Date: Fri, 10 Jul 2020 16:44:12 +0200 Subject: [PATCH 0387/3768] fix #14912, make `--useVersion:1.0` work again (#14945) * fix #14912, make `--useVersion:1.0` work again * a better fix * add test --- compiler/commands.nim | 1 + tests/system/tuse_version.nim | 50 +++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 tests/system/tuse_version.nim diff --git a/compiler/commands.nim b/compiler/commands.nim index cbf93238f..1984c894a 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -878,6 +878,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; defineSymbol(conf.symbols, "NimPatch", "100") # old behaviors go here: defineSymbol(conf.symbols, "nimOldRelativePathBehavior") + undefSymbol(conf.symbols, "nimDoesntTrackDefects") ast.eqTypeFlags.excl {tfGcSafe, tfNoSideEffect} conf.globalOptions.incl optNimV1Emulation else: diff --git a/tests/system/tuse_version.nim b/tests/system/tuse_version.nim new file mode 100644 index 000000000..36dac46ec --- /dev/null +++ b/tests/system/tuse_version.nim @@ -0,0 +1,50 @@ +discard """ + cmd: "nim c --useVersion:1.0 -r $file" + output: "1.0.100" +""" + +{.warning[UnusedImport]: off.} + +import std/[ + # Core: + bitops, typetraits, lenientops, macros, volatile, + + # Algorithms: + algorithm, sequtils, + + # Collections: + critbits, deques, heapqueue, intsets, lists, options, sets, + sharedlist, tables, + + # Strings: + editdistance, wordwrap, parseutils, ropes, + pegs, punycode, strformat, strmisc, strscans, strtabs, + strutils, unicode, unidecode, + + # Generic operator system services: + os, streams, + + # Math libraries: + complex, math, mersenne, random, rationals, stats, sums, + + # Internet protocols: + httpcore, mimetypes, uri, + + # Parsers: + htmlparser, json, lexbase, parsecfg, parsecsv, parsesql, parsexml, + + # XML processing: + xmltree, xmlparser, + + # Generators: + htmlgen, + + # Hashing: + base64, hashes, + + # Miscellaneous: + colors, sugar, varints, +] + + +echo NimVersion From 229a623849da617abd9afc3f30ba6c612b4c04c2 Mon Sep 17 00:00:00 2001 From: kwgchi Date: Fri, 10 Jul 2020 23:45:26 +0900 Subject: [PATCH 0388/3768] Update readme.md (#14953) --- readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index f529c0df0..94241822f 100644 --- a/readme.md +++ b/readme.md @@ -138,7 +138,7 @@ you should familiarize yourself with the following repository structure: If you are not familiar with making a pull request using GitHub and/or git, please read [this guide][pull-request-instructions]. -Ideally you should make sure that all tests pass before submitting a pull request. +Ideally, you should make sure that all tests pass before submitting a pull request. However, if you are short on time, you can just run the tests specific to your changes by only running the corresponding categories of tests. Travis CI verifies that all tests pass before allowing the pull request to be accepted, so only @@ -146,7 +146,7 @@ running specific tests should be harmless. Integration tests should go in ``tests/untestable``. If you're looking for ways to contribute, please look at our [issue tracker][nim-issues]. -There are always plenty of issues labelled [``Easy``][nim-issues-easy]; these should +There are always plenty of issues labeled [``Easy``][nim-issues-easy]; these should be a good starting point for an initial contribution to Nim. You can also help with the development of Nim by making donations. Donations can be From b21782a667f7a819124e95da14053387bd0079a0 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Fri, 10 Jul 2020 17:02:49 +0200 Subject: [PATCH 0389/3768] Add testcase for #4722 (#14954) --- tests/template/template_issues.nim | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/template/template_issues.nim b/tests/template/template_issues.nim index e56d44480..420373e61 100644 --- a/tests/template/template_issues.nim +++ b/tests/template/template_issues.nim @@ -8,6 +8,9 @@ hi Hello, World! (e: 42) hey +foo +foo +foo ''' """ @@ -245,3 +248,21 @@ proc foo(): auto = echo "hey" discard foo() + + +# bug #4722 +type + IteratorF*[In] = iterator() : In {.closure.} + +template foof(In: untyped) : untyped = + proc ggg*(arg: IteratorF[In]) = + for i in arg(): + echo "foo" + + +iterator hello() : int {.closure.} = + for i in 1 .. 3: + yield i + +foof(int) +ggg(hello) From 0db32e98854dd3fc5a6e1d27a1d4980fd61053da Mon Sep 17 00:00:00 2001 From: Clyybber Date: Fri, 10 Jul 2020 17:13:37 +0200 Subject: [PATCH 0390/3768] Add testcase for #12571 (#14955) --- tests/statictypes/tstatictypes.nim | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/statictypes/tstatictypes.nim b/tests/statictypes/tstatictypes.nim index 2a8d09dbf..50dc60e09 100644 --- a/tests/statictypes/tstatictypes.nim +++ b/tests/statictypes/tstatictypes.nim @@ -9,6 +9,7 @@ output: ''' b is 2 times a 17 ['\x00', '\x00', '\x00', '\x00'] +heyho ''' """ @@ -223,3 +224,18 @@ block: # issue #14802 12 const myConst = static(fn(1)) doAssert myConst == 12 + + +# bug #12571 +type + T[K: static bool] = object of RootObj + when K == true: + foo: string + else: + bar: string + U[K: static bool] = object of T[K] + +let t = T[true](foo: "hey") +let u = U[false](bar: "ho") +echo t.foo, u.bar + From db6ffeba8e8be9d27a8c4610952b0424e9c46d4a Mon Sep 17 00:00:00 2001 From: Clyybber Date: Fri, 10 Jul 2020 17:31:15 +0200 Subject: [PATCH 0391/3768] Add testcase for #13815 (#14956) --- tests/iter/tclosureiters.nim | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/tests/iter/tclosureiters.nim b/tests/iter/tclosureiters.nim index 345a4867a..0ee8e81cc 100644 --- a/tests/iter/tclosureiters.nim +++ b/tests/iter/tclosureiters.nim @@ -19,7 +19,9 @@ discard """ 0 1 2 -70''' +70 +0 +''' """ when true: @@ -139,3 +141,14 @@ iterator filesIt(path: string): auto {.closure.} = let prefix = path.splitPath[1] for f in files: yield prefix / f + +# bug #13815 +var love = iterator: int {.closure.} = + yield cast[type( + block: + var a = 0 + yield a + a)](0) + +for i in love(): + echo i From 1850efbb6c618946da7c943b153f7caa9a66ba33 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Fri, 10 Jul 2020 18:10:32 +0200 Subject: [PATCH 0392/3768] Add testcase for #14383 (#14957) --- tests/arc/dmodule.nim | 23 +++++++++++++++++++++++ tests/arc/t14383.nim | 13 +++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 tests/arc/dmodule.nim create mode 100644 tests/arc/t14383.nim diff --git a/tests/arc/dmodule.nim b/tests/arc/dmodule.nim new file mode 100644 index 000000000..455ec7084 --- /dev/null +++ b/tests/arc/dmodule.nim @@ -0,0 +1,23 @@ +type + MinKind* = enum + minDictionary + minBool + MinValue* = object + case kind*: MinKind + of minDictionary: + symbols: seq[MinOperator] + else: discard + MinOperator = object + +# remove this inline pragma to make it compile +proc `$`*(a: MinValue): string {.inline.} = + case a.kind + of minDictionary: + result = "hello" + for i in a.symbols: + result = "hello" + else: discard + +proc parseMinValue*(): MinValue = + # or this echo + echo result diff --git a/tests/arc/t14383.nim b/tests/arc/t14383.nim new file mode 100644 index 000000000..115cbf426 --- /dev/null +++ b/tests/arc/t14383.nim @@ -0,0 +1,13 @@ +discard """ + cmd: "nim c --gc:arc $file" + output: ''' +hello +hello +''' +""" + +import dmodule + +var val = parseMinValue() +if val.kind == minDictionary: + echo val From 425972c4e8bde84ff8b84a64f1e68356dc0a1a3a Mon Sep 17 00:00:00 2001 From: Clyybber Date: Fri, 10 Jul 2020 19:21:50 +0200 Subject: [PATCH 0393/3768] Add testcase for some old fixed issues (#14960) * Add testcase for #2703 * Add testcase for #2586 * Small fix --- tests/template/template_issues.nim | 8 ++++++++ tests/ccgbugs/trecursive_closure.nim => tissues.nim | 10 ++++++++++ 2 files changed, 18 insertions(+) rename tests/ccgbugs/trecursive_closure.nim => tissues.nim (67%) diff --git a/tests/template/template_issues.nim b/tests/template/template_issues.nim index 420373e61..d12b3c3ef 100644 --- a/tests/template/template_issues.nim +++ b/tests/template/template_issues.nim @@ -11,6 +11,8 @@ hey foo foo foo +false +true ''' """ @@ -266,3 +268,9 @@ iterator hello() : int {.closure.} = foof(int) ggg(hello) + + +# bug #2586 +var z = 10'u8 +echo z < 9 # Works +echo z > 9 # Error: type mismatch diff --git a/tests/ccgbugs/trecursive_closure.nim b/tissues.nim similarity index 67% rename from tests/ccgbugs/trecursive_closure.nim rename to tissues.nim index 4b6514b90..7a8ede958 100644 --- a/tests/ccgbugs/trecursive_closure.nim +++ b/tissues.nim @@ -16,3 +16,13 @@ f(nil) type A = object #of RootObj <-- Uncomment this to get no errors test: proc(i: A): bool var a: proc(i: A): bool # Or comment this line to get no errors + + +# bug #2703 +type + fooObj[T] = object of RootObj + bazObj[T] = object of fooObj[T] + x: T + +var troz: fooObj[string] +echo bazObj[string](troz).x From 6d10baa3391b5dd25e035c83a41ead9bf456be69 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Sat, 11 Jul 2020 07:21:59 -0300 Subject: [PATCH 0394/3768] https://github.com/nim-lang/Nim/pull/14948#issuecomment-656498426 (#14958) --- doc/advopt.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/advopt.txt b/doc/advopt.txt index 547005485..135f297c4 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -111,7 +111,7 @@ Advanced options: --skipProjCfg:on|off do not read the project's configuration file --gc:refc|arc|orc|markAndSweep|boehm|go|none|regions select the GC to use; default is 'refc' - --exceptions:setjmp|cpp|goto|quirky + --exceptions:setjmp|cpp|goto select the exception handling implementation --index:on|off turn index file generation on|off --putenv:key=value set an environment variable From 21772b6419b6cb3b59b436fbdcd8975a9da62349 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sat, 11 Jul 2020 23:09:41 +0800 Subject: [PATCH 0395/3768] add testcase for #5926 (#14965) --- tests/generics/t5926.nim | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 tests/generics/t5926.nim diff --git a/tests/generics/t5926.nim b/tests/generics/t5926.nim new file mode 100644 index 000000000..bb14c3af5 --- /dev/null +++ b/tests/generics/t5926.nim @@ -0,0 +1,22 @@ +discard """ +action: compile +""" + +type + SomeObj[T] = object + +template useSomeObj[T]() = + var retObj: SomeObj[T] + +useSomeObj[void]() +useSomeObj[int]() + + +type + Data*[T] = object + x: T + +template test*[T](xxx: T) = + let data = Data[T](x: xxx) + +test(1) From d409694b02ae403501b23a94c21f60ad8edb7365 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Sat, 11 Jul 2020 19:17:40 +0200 Subject: [PATCH 0396/3768] :D --- tissues.nim => tests/ccgbugs/tissues.nim | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tissues.nim => tests/ccgbugs/tissues.nim (100%) diff --git a/tissues.nim b/tests/ccgbugs/tissues.nim similarity index 100% rename from tissues.nim rename to tests/ccgbugs/tissues.nim From 9ec9a31f46697a615630fce3614e68e57d390a74 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sun, 12 Jul 2020 15:31:03 +0800 Subject: [PATCH 0397/3768] Fix #12759 (#14967) * add testcase for #5926 * fix #12759 --- lib/pure/terminal.nim | 2 +- tests/stdlib/tterminal_12759.nim | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 tests/stdlib/tterminal_12759.nim diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index e798bbaf1..99a333cc6 100644 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -37,7 +37,7 @@ type var gTerm {.threadvar.}: owned(PTerminal) -proc newTerminal(): owned(PTerminal) {.gcsafe.} +proc newTerminal(): owned(PTerminal) {.gcsafe, raises: [].} proc getTerminal(): PTerminal {.inline.} = if isNil(gTerm): diff --git a/tests/stdlib/tterminal_12759.nim b/tests/stdlib/tterminal_12759.nim new file mode 100644 index 000000000..d6034ab57 --- /dev/null +++ b/tests/stdlib/tterminal_12759.nim @@ -0,0 +1,10 @@ +discard """ + action: "compile" +""" + +import terminal + +proc test() {.raises:[IOError, ValueError].} = + setBackgroundColor(stdout, bgRed) + +test() From 124feb2af87dcbb44e25e7dd4303098ae5e9225d Mon Sep 17 00:00:00 2001 From: Alexander Wolfe <61166+memoryhole@users.noreply.github.com> Date: Sun, 12 Jul 2020 07:07:31 -0700 Subject: [PATCH 0398/3768] nimpretty support for multiple files (#14890) --- nimpretty/nimpretty.nim | 53 ++++++++++++++++++++++++++++++----------- nimpretty/tester.nim | 28 ++++++++++++++++++++-- 2 files changed, 65 insertions(+), 16 deletions(-) diff --git a/nimpretty/nimpretty.nim b/nimpretty/nimpretty.nim index 9b5cf556b..bedf9f248 100644 --- a/nimpretty/nimpretty.nim +++ b/nimpretty/nimpretty.nim @@ -14,7 +14,7 @@ when not defined(nimpretty): import ../compiler / [idents, msgs, syntaxes, options, pathutils, layouter] -import parseopt, strutils, os +import parseopt, strutils, os, sequtils const Version = "0.2" @@ -22,9 +22,10 @@ const (c) 2017 Andreas Rumpf Usage: - nimpretty [options] file.nim + nimpretty [options] nimfiles... Options: --out:file set the output file (default: overwrite the input file) + --outDir:dir set the output dir (default: overwrite the input files) --indent:N[=0] set the number of spaces that is used for indentation --indent:0 means autodetection (default behaviour) --maxLineLen:N set the desired maximum line length (default: 80) @@ -61,37 +62,61 @@ proc prettyPrint(infile, outfile: string, opt: PrettyOptions) = closeParsers(p) proc main = - var infile, outfile: string + var outfile, outdir: string + + var infiles = newSeq[string]() + var outfiles = newSeq[string]() + var backup = false # when `on`, create a backup file of input in case # `prettyPrint` could over-write it (note that the backup may happen even # if input is not actually over-written, when nimpretty is a noop). # --backup was un-documented (rely on git instead). var opt = PrettyOptions(indWidth: 0, maxLineLen: 80) + + for kind, key, val in getopt(): case kind of cmdArgument: - infile = key.addFileExt(".nim") + infiles.add(key.addFileExt(".nim")) of cmdLongOption, cmdShortOption: case normalize(key) of "help", "h": writeHelp() of "version", "v": writeVersion() of "backup": backup = parseBool(val) of "output", "o", "out": outfile = val + of "outDir", "outdir": outdir = val of "indent": opt.indWidth = parseInt(val) of "maxlinelen": opt.maxLineLen = parseInt(val) else: writeHelp() of cmdEnd: assert(false) # cannot happen - if infile.len == 0: + if infiles.len == 0: quit "[Error] no input file." - if outfile.len == 0: - outfile = infile - if not fileExists(outfile) or not sameFile(infile, outfile): - backup = false # no backup needed since won't be over-written - if backup: - let infileBackup = infile & ".backup" # works with .nim or .nims - echo "writing backup " & infile & " > " & infileBackup - os.copyFile(source = infile, dest = infileBackup) - prettyPrint(infile, outfile, opt) + + if outfile.len != 0 and outdir.len != 0: + quit "[Error] out and outDir cannot both be specified" + + if outfile.len == 0 and outdir.len == 0: + outfiles = infiles + elif outfile.len != 0 and infiles.len > 1: + # Take the last file to maintain backwards compatibility + let infile = infiles[^1] + infiles = @[infile] + outfiles = @[outfile] + elif outfile.len != 0: + outfiles = @[outfile] + elif outdir.len != 0: + outfiles = infiles.mapIt($(joinPath(outdir, it))) + + for (infile, outfile) in zip(infiles, outfiles): + let (dir, _, _) = splitFile(outfile) + createDir(dir) + if not fileExists(outfile) or not sameFile(infile, outfile): + backup = false # no backup needed since won't be over-written + if backup: + let infileBackup = infile & ".backup" # works with .nim or .nims + echo "writing backup " & infile & " > " & infileBackup + os.copyFile(source = infile, dest = infileBackup) + prettyPrint(infile, outfile, opt) main() diff --git a/nimpretty/tester.nim b/nimpretty/tester.nim index 041e7edd8..6124eb66a 100644 --- a/nimpretty/tester.nim +++ b/nimpretty/tester.nim @@ -1,9 +1,10 @@ # Small program that runs the test cases -import strutils, os +import strutils, os, sequtils const dir = "nimpretty/tests/" + outputdir = dir / "outputdir" var failures = 0 @@ -30,7 +31,25 @@ proc test(infile, ext: string) = else: echo "SUCCESS: files identical: ", nimFile -for t in walkFiles(dir / "*.nim"): +proc testTogether(infiles: seq[string]) = + if execShellCmd("$# --outDir:$# --backup:off $#" % [nimp, outputdir, infiles.join(" ")]) != 0: + echo "FAILURE: nimpretty cannot prettify files: ", $infiles + failures += 1 + return + + for infile in infiles: + let nimFile = splitFile(infile).name + let expected = dir / "expected" / nimFile & ".nim" + let produced = dir / "outputdir" / infile + if readFile(expected) != readFile(produced): + echo "FAILURE: files differ: ", nimFile + discard execShellCmd("diff -uNdr " & expected & " " & produced) + failures += 1 + else: + echo "SUCCESS: files identical: ", nimFile + +let allFiles = toSeq(walkFiles(dir / "*.nim")) +for t in allFiles: test(t, "pretty") # also test that pretty(pretty(x)) == pretty(x) test(t.changeFileExt("pretty"), "pretty2") @@ -38,5 +57,10 @@ for t in walkFiles(dir / "*.nim"): removeFile(t.changeFileExt("pretty")) removeFile(t.changeFileExt("pretty2")) +testTogether(allFiles) +removeDir(outputdir) + + + if failures > 0: quit($failures & " failures occurred.") From 6cc0061a72826022991262eb8e31b97f6fdf37e9 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 12 Jul 2020 16:50:56 +0200 Subject: [PATCH 0399/3768] optimize the new nimPrepareStrMutationV2 with inlining (#14969) --- lib/system/strs_v2.nim | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/system/strs_v2.nim b/lib/system/strs_v2.nim index aa644522f..85c0af190 100644 --- a/lib/system/strs_v2.nim +++ b/lib/system/strs_v2.nim @@ -132,10 +132,13 @@ proc nimAsgnStrV2(a: var NimStringV2, b: NimStringV2) {.compilerRtl.} = a.len = b.len copyMem(unsafeAddr a.p.data[0], unsafeAddr b.p.data[0], b.len+1) -proc nimPrepareStrMutationV2(s: var NimStringV2) {.compilerRtl.} = +proc nimPrepareStrMutationImpl(s: var NimStringV2) = + let oldP = s.p + # can't mutate a literal, so we need a fresh copy here: + s.p = cast[ptr NimStrPayload](allocShared0(contentSize(s.len))) + s.p.cap = s.len + copyMem(unsafeAddr s.p.data[0], unsafeAddr oldP.data[0], s.len+1) + +proc nimPrepareStrMutationV2(s: var NimStringV2) {.compilerRtl, inline.} = if s.p != nil and (s.p.cap and strlitFlag) == strlitFlag: - let oldP = s.p - # can't mutate a literal, so we need a fresh copy here: - s.p = cast[ptr NimStrPayload](allocShared0(contentSize(s.len))) - s.p.cap = s.len - copyMem(unsafeAddr s.p.data[0], unsafeAddr oldP.data[0], s.len+1) + nimPrepareStrMutationImpl(s) From f4f21c89e57115a57ed97e30a9b9fe6c901ae453 Mon Sep 17 00:00:00 2001 From: Miran Date: Tue, 14 Jul 2020 08:18:02 +0200 Subject: [PATCH 0400/3768] asyncftpclient.nim - don't assume a sufficiend line length (#14973) --- lib/pure/asyncftpclient.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/asyncftpclient.nim b/lib/pure/asyncftpclient.nim index 2d3556a89..4e62ecd97 100644 --- a/lib/pure/asyncftpclient.nim +++ b/lib/pure/asyncftpclient.nim @@ -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) From 086efac49b746214c8d39347fa2d96f0f85413b2 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Tue, 14 Jul 2020 15:22:48 +0800 Subject: [PATCH 0401/3768] fix #6608 (#14963) * fix #6608 --- compiler/semexprs.nim | 11 ++++++++++- tests/errmsgs/t6608.nim | 16 ++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 tests/errmsgs/t6608.nim diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index de9f2198a..fd9203f34 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -955,8 +955,17 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = hasErrorType = true break if not hasErrorType: + let typ = n[0].typ msg.add(">\nbut expected one of: \n" & - typeToString(n[0].typ)) + typeToString(typ)) + # prefer notin preferToResolveSymbols + # t.sym != nil + # sfAnon notin t.sym.flags + # t.kind != tySequence(It is tyProc) + if typ.sym != nil and sfAnon notin typ.sym.flags and + typ.kind == tyProc: + msg.add(" = " & + typeToString(typ, preferDesc)) localError(c.config, n.info, msg) return errorNode(c, n) result = nil diff --git a/tests/errmsgs/t6608.nim b/tests/errmsgs/t6608.nim new file mode 100644 index 000000000..3d82f3cd8 --- /dev/null +++ b/tests/errmsgs/t6608.nim @@ -0,0 +1,16 @@ +discard """ + cmd: "nim c --hints:off $file" + errormsg: "type mismatch: got <>" + nimout: '''t6608.nim(14, 4) Error: type mismatch: got <> +but expected one of: +AcceptCB = proc (s: string){.closure.}''' + line: 14 +""" + +type + AcceptCB = proc (s: string) + +proc x(x: AcceptCB) = + x() + +x() From ffaf4797be4a8e8aec5ada9863ccdec66a066930 Mon Sep 17 00:00:00 2001 From: jcosborn Date: Tue, 14 Jul 2020 04:24:17 -0500 Subject: [PATCH 0402/3768] add full tests from #9463 (#14975) --- tests/generics/t4668.nim | 172 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 161 insertions(+), 11 deletions(-) diff --git a/tests/generics/t4668.nim b/tests/generics/t4668.nim index 0d1b18111..bd44cc975 100644 --- a/tests/generics/t4668.nim +++ b/tests/generics/t4668.nim @@ -1,16 +1,166 @@ discard """ -action: compile + output: ''' +foo1 +foo2 +''' """ -type - FooObj*[T] = object - v*: T - Foo1*[T] = FooObj[T] - Foo2* = FooObj +block: + type + FooObj[T] = object + v: T + Foo1[T] = FooObj[T] + Foo2 = FooObj -proc foo1(x: Foo1) = echo "foo1" -proc foo2(x: Foo2) = echo "foo2" + proc foo1(x: Foo1) = echo "foo1" + proc foo2(x: Foo2) = echo "foo2" -var x: FooObj[float] -foo1(x) # works -foo2(x) # works + var x: FooObj[float] + foo1(x) # works + foo2(x) # works + +block: + type + FooObj[T] = T + Foo1[T] = FooObj[T] + Foo2 = FooObj + Foo3 = Foo1 + Foo4x = FooObj[SomeInteger] + Foo4 = FooObj[SomeFloat] + Foo5x = Foo1[SomeInteger] + Foo5 = Foo1[SomeFloat] + + proc foo0(x: FooObj): int = 0 + proc foo1(x: Foo1): int = 1 + proc foo2(x: Foo2): int = 2 + proc foo3(x: Foo3): int = 3 + proc foo4(x: Foo4x): int = 40 + proc foo4(x: Foo4): int = 4 + proc foo5(x: Foo5x): int = 50 + proc foo5(x: Foo5): int = 5 + + block: + var x: FooObj[float] + doAssert(foo0(x) == 0) + doAssert(foo1(x) == 1) + doAssert(foo2(x) == 2) + doAssert(foo3(x) == 3) + doAssert(foo4(x) == 4) + doAssert(foo5(x) == 5) + + block: + var x: Foo1[float] + doAssert(foo0(x) == 0) + doAssert(foo1(x) == 1) + doAssert(foo2(x) == 2) + doAssert(foo3(x) == 3) + doAssert(foo4(x) == 4) + doAssert(foo5(x) == 5) + + block: + var x: Foo2[float] + doAssert(foo0(x) == 0) + doAssert(foo1(x) == 1) + doAssert(foo2(x) == 2) + doAssert(foo3(x) == 3) + doAssert(foo4(x) == 4) + doAssert(foo5(x) == 5) + +block: + type + FooObj[T,U] = object + x: T + y: U + Foo1[U,T] = FooObj[T,U] + Foo2 = FooObj + Foo3 = Foo1 + Foo4x = FooObj[SomeInteger,SomeInteger] + Foo4y = FooObj[SomeInteger,SomeFloat] + Foo4z = FooObj[SomeFloat,SomeFloat] + Foo4 = FooObj[SomeFloat,SomeInteger] + Foo5x = Foo1[SomeInteger,SomeInteger] + Foo5y = Foo1[SomeFloat,SomeInteger] + Foo5z = Foo1[SomeFloat,SomeFloat] + Foo5 = Foo1[SomeInteger,SomeFloat] + + proc foo0(x: FooObj): int = 0 + proc foo1(x: Foo1): int = 1 + proc foo2(x: Foo2): int = 2 + proc foo3(x: Foo3): int = 3 + proc foo4(x: Foo4x): int = 40 + proc foo4(x: Foo4y): int = 41 + proc foo4(x: Foo4z): int = 42 + proc foo4(x: Foo4): int = 4 + proc foo5(x: Foo5x): int = 50 + proc foo5(x: Foo5y): int = 51 + proc foo5(x: Foo5z): int = 52 + proc foo5(x: Foo5): int = 5 + + block: + var x: FooObj[float,int] + doAssert(foo0(x) == 0) + doAssert(foo1(x) == 1) + doAssert(foo2(x) == 2) + doAssert(foo3(x) == 3) + doAssert(foo4(x) == 4) + doAssert(foo5(x) == 5) + + block: + var x: Foo1[int,float] + doAssert(foo0(x) == 0) + doAssert(foo1(x) == 1) + doAssert(foo2(x) == 2) + doAssert(foo3(x) == 3) + doAssert(foo4(x) == 4) + doAssert(foo5(x) == 5) + +block: + type + FooObj[T] = object of RootObj + v: T + FooObj2[T] = object of FooObj[T] + Foo1[T] = FooObj[T] + Foo2 = FooObj + Foo3 = Foo1 + Foo4x = FooObj[SomeInteger] + Foo4 = FooObj[SomeFloat] + Foo5x = Foo1[SomeInteger] + Foo5 = Foo1[SomeFloat] + + proc foo0(x: FooObj): int = 0 + proc foo1(x: Foo1): int = 1 + proc foo2(x: Foo2): int = 2 + proc foo3(x: Foo3): int = 3 + proc foo4(x: Foo4x): int = 40 + proc foo4(x: Foo4): int = 4 + proc foo5(x: Foo5x): int = 50 + proc foo5(x: Foo5): int = 5 + + block: + var x: FooObj[float] + doAssert(foo0(x) == 0) + doAssert(foo1(x) == 1) + doAssert(foo2(x) == 2) + doAssert(foo3(x) == 3) + doAssert(foo4(x) == 4) + doAssert(foo5(x) == 5) + + block: + var x: Foo1[float] + doAssert(foo0(x) == 0) + doAssert(foo1(x) == 1) + doAssert(foo2(x) == 2) + doAssert(foo3(x) == 3) + doAssert(foo4(x) == 4) + doAssert(foo5(x) == 5) + + #[ XXX These still fail + block: + var x: FooObj2[float] + doAssert(foo0(x) == 0) + doAssert(foo1(x) == 1) + doAssert(foo2(x) == 2) + doAssert(foo3(x) == 3) + doAssert(foo4(x) == 4) + doAssert(foo5(x) == 5) + ]# From e07d661d1600989c281a270d90563f1c60392b9d Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 14 Jul 2020 04:14:32 -0700 Subject: [PATCH 0403/3768] fix #14475; unittest.require now works with `nim c`; require and check now works with -d:nodejs (#14676) * fix #14475; make unittest work with -d:nodejs * fixup * fixup * disable inim, delaunay which failed after unittest.require got fixed * re-enable tests that have been fixed --- lib/pure/unittest.nim | 13 ++++++------- lib/std/exitprocs.nim | 22 ++++++++++++++++++++++ lib/system.nim | 7 ++----- tests/js/tunittest_error.nim | 2 ++ tests/stdlib/tquit.nim | 2 +- tests/stdlib/tunittest_error.nim | 22 ++++++++++++++++++++++ 6 files changed, 55 insertions(+), 13 deletions(-) create mode 100644 tests/stdlib/tunittest_error.nim diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim index bea7d9c44..f3d920d0e 100644 --- a/lib/pure/unittest.nim +++ b/lib/pure/unittest.nim @@ -94,6 +94,7 @@ ## echo "suite teardown: run once after the tests" import std/private/since +import std/exitprocs import macros, strutils, streams, times, sets, sequtils @@ -498,7 +499,7 @@ template test*(name, body) {.dirty.} = ## .. code-block:: ## ## [OK] roses are red - bind shouldRun, checkpoints, formatters, ensureInitialized, testEnded, exceptionTypeName + bind shouldRun, checkpoints, formatters, ensureInitialized, testEnded, exceptionTypeName, setProgramResult ensureInitialized() @@ -524,7 +525,7 @@ template test*(name, body) {.dirty.} = finally: if testStatusIMPL == TestStatus.FAILED: - programResult = 1 + setProgramResult 1 let testResult = TestResult( suiteName: when declared(testSuiteName): testSuiteName else: "", testName: name, @@ -560,12 +561,11 @@ template fail* = ## fail() ## ## outputs "Checkpoint A" before quitting. - bind ensureInitialized - + bind ensureInitialized, setProgramResult when declared(testStatusIMPL): testStatusIMPL = TestStatus.FAILED else: - programResult = 1 + setProgramResult 1 ensureInitialized() @@ -576,8 +576,7 @@ template fail* = else: formatter.failureOccurred(checkpoints, "") - when declared(programResult): - if abortOnError: quit(programResult) + if abortOnError: quit(1) checkpoints = @[] diff --git a/lib/std/exitprocs.nim b/lib/std/exitprocs.nim index b2811735c..c6537f7f8 100644 --- a/lib/std/exitprocs.nim +++ b/lib/std/exitprocs.nim @@ -63,3 +63,25 @@ proc addExitProc*(cl: proc() {.noconv.}) = withLock gFunsLock: fun() gFuns.add Fun(kind: kNoconv, fun2: cl) + +when not defined(nimscript): + proc getProgramResult*(): int = + when defined(js) and defined(nodejs): + asm """ +`result` = process.exitCode; +""" + elif not defined(js): + result = programResult + else: + doAssert false + + proc setProgramResult*(a: int) = + # pending https://github.com/nim-lang/Nim/issues/14674 + when defined(js) and defined(nodejs): + asm """ +process.exitCode = `a`; +""" + elif not defined(js): + programResult = a + else: + doAssert false diff --git a/lib/system.nim b/lib/system.nim index 822454626..71eed5bee 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1127,12 +1127,9 @@ const ## is the value that should be passed to `quit <#quit,int>`_ to indicate ## failure. -when defined(js) and defined(nodejs) and not defined(nimscript): - var programResult* {.importc: "process.exitCode".}: int - programResult = 0 -elif hostOS != "standalone": +when not defined(js) and hostOS != "standalone": var programResult* {.compilerproc, exportc: "nim_program_result".}: int - ## deprecated, prefer ``quit`` + ## deprecated, prefer `quit` or `exitprocs.getProgramResult`, `exitprocs.setProgramResult`. import std/private/since diff --git a/tests/js/tunittest_error.nim b/tests/js/tunittest_error.nim index ab736bf59..781e34338 100644 --- a/tests/js/tunittest_error.nim +++ b/tests/js/tunittest_error.nim @@ -3,6 +3,8 @@ discard """ outputsub: "[FAILED] with exception" """ +# see also: `tests/stdlib/tunittest_error.nim` + import unittest proc ddd() = diff --git a/tests/stdlib/tquit.nim b/tests/stdlib/tquit.nim index 1f9283ec4..0b4a479dc 100644 --- a/tests/stdlib/tquit.nim +++ b/tests/stdlib/tquit.nim @@ -5,7 +5,7 @@ just exiting... joinable: false """ -# Test the new beforeQuit variable: +# Test `addQuitProc` proc myExit() {.noconv.} = write(stdout, "just exiting...\n") diff --git a/tests/stdlib/tunittest_error.nim b/tests/stdlib/tunittest_error.nim new file mode 100644 index 000000000..7f05ec2a9 --- /dev/null +++ b/tests/stdlib/tunittest_error.nim @@ -0,0 +1,22 @@ +discard """ + exitcode: 1 + outputsub: "failed: 1 == 3" + matrix: "-d:case1; -d:case2" + targets: "c js" + joinable: false +""" + +when defined case1: + import unittest + suite "Test": + test "test require": + check 1==2 + check 1==3 + +when defined case2: + import unittest + suite "Test": + test "test require": + require 1 == 3 + if true: + quit 0 # intentional From 9ed0dbfa6e92f4aa1ebb8ac2ac48af97ae72dbbb Mon Sep 17 00:00:00 2001 From: Hendrik Date: Tue, 14 Jul 2020 13:15:22 +0200 Subject: [PATCH 0404/3768] fix index error (#14974) Co-authored-by: Hendrik Albers --- lib/pure/logging.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/pure/logging.nim b/lib/pure/logging.nim index 2e79cd3ca..fa6285a04 100644 --- a/lib/pure/logging.nim +++ b/lib/pure/logging.nim @@ -287,6 +287,7 @@ proc substituteLog*(frmt: string, level: Level, runnableExamples: doAssert substituteLog(defaultFmtStr, lvlInfo, "a message") == "INFO a message" doAssert substituteLog("$levelid - ", lvlError, "an error") == "E - an error" + doAssert substituteLog("$levelid", lvlDebug, "error") == "Derror" var msgLen = 0 for arg in args: msgLen += arg.len @@ -300,7 +301,7 @@ proc substituteLog*(frmt: string, level: Level, inc(i) var v = "" let app = when defined(js): "" else: getAppFilename() - while frmt[i] in IdentChars: + while i < frmt.len and frmt[i] in IdentChars: v.add(toLowerAscii(frmt[i])) inc(i) case v From f999f916f31946be3cf8abc485fc07e0d89de9ae Mon Sep 17 00:00:00 2001 From: Bung Date: Tue, 14 Jul 2020 19:53:41 +0800 Subject: [PATCH 0405/3768] add missing props,procs (#14978) --- lib/js/dom.nim | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/js/dom.nim b/lib/js/dom.nim index 575ec3deb..be676a813 100644 --- a/lib/js/dom.nim +++ b/lib/js/dom.nim @@ -196,6 +196,9 @@ type innerText*: cstring textContent*: cstring style*: Style + baseURI*: cstring + parentElement*: Element + isConnected*: bool Document* = ref DocumentObj DocumentObj {.importc.} = object of NodeObj @@ -224,6 +227,7 @@ type Element* = ref ElementObj ElementObj {.importc.} = object of NodeObj + className*: cstring classList*: ClassList checked*: bool defaultChecked*: bool @@ -1481,6 +1485,7 @@ proc getAttribute*(n: Node, attr: cstring): cstring proc getAttributeNode*(n: Node, attr: cstring): Node proc hasAttribute*(n: Node, attr: cstring): bool proc hasChildNodes*(n: Node): bool +proc normalize*(n: Node) proc insertData*(n: Node, position: int, data: cstring) proc removeAttribute*(n: Node, attr: cstring) proc removeAttributeNode*(n, attr: Node) @@ -1490,6 +1495,13 @@ proc setAttribute*(n: Node, name, value: cstring) proc setAttributeNode*(n: Node, attr: Node) proc querySelector*(n: Node, selectors: cstring): Element proc querySelectorAll*(n: Node, selectors: cstring): seq[Element] +proc compareDocumentPosition*(n: Node, otherNode:Node): int +proc lookupPrefix*(n: Node): cstring +proc lookupNamespaceURI*(n: Node): cstring +proc isDefaultNamespace*(n: Node): bool +proc contains*(n: Node): bool +proc isEqualNode*(n: Node): bool +proc isSameNode*(n: Node): bool # Document "methods" proc createAttribute*(d: Document, identifier: cstring): Node From 2b0e336c971401d06797240686722ce669d902b9 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Tue, 14 Jul 2020 14:15:39 +0200 Subject: [PATCH 0406/3768] injectdestructors fixes and refactor (#14964) * injectdestructors fixes and refactor * Tiny cleanup * Refactor and expand testcase * Closes #14902 by adding testcase * Better naming * Fix test failures * Misc cleanup * Add testcase for #14968 * Better approach; expand testcases * Optimizations and fixes * Add testcase * typo * Tiny cleanup --- compiler/dfa.nim | 2 +- compiler/injectdestructors.nim | 301 ++++++++++----------------- tests/arc/tarcmisc.nim | 38 +++- tests/arc/tcontrolflow.nim | 4 +- tests/arc/tmovebug.nim | 169 +++++++++++++++ tests/destructor/tmove_objconstr.nim | 1 - 6 files changed, 317 insertions(+), 198 deletions(-) diff --git a/compiler/dfa.nim b/compiler/dfa.nim index 2254cd0ff..f3b358d72 100644 --- a/compiler/dfa.nim +++ b/compiler/dfa.nim @@ -628,7 +628,7 @@ proc isAnalysableFieldAccess*(orig: PNode; owner: PSym): bool = # XXX Allow closure deref operations here if we know # the owner controlled the closure allocation? result = n.kind == nkSym and n.sym.owner == owner and - owner.kind != skModule and + sfGlobal notin n.sym.flags and (n.sym.kind != skParam or isSinkParam(n.sym)) # or n.sym.typ.kind == tyVar) # Note: There is a different move analyzer possible that checks for # consume(param.key); param.key = newValue for all paths. Then code like diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index da41b2d5c..ff311f079 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -37,18 +37,12 @@ type final: seq[PNode] # finally section needsTry: bool parent: ptr Scope - escapingSyms: IntSet # a construct like (block: let x = f(); x) - # means that 'x' escapes. We then destroy it - # in the parent's scope (and also allocate it there). - escapingExpr: PNode type Con = object owner: PSym g: ControlFlowGraph - jumpTargets: IntSet graph: ModuleGraph - emptyNode: PNode otherRead: PNode inLoop, inSpawn: int uninit: IntSet # set of uninit'ed vars @@ -72,12 +66,11 @@ proc getTemp(c: var Con; s: var Scope; typ: PType; info: TLineInfo): PNode = s.vars.add(sym) result = newSymNode(sym) +const sfSingleUsedTemp = sfExported # For temporaries that we know will only be used once + proc nestedScope(parent: var Scope): Scope = Scope(vars: @[], wasMoved: @[], final: @[], needsTry: false, parent: addr(parent)) -proc rememberParent(parent: var Scope; inner: Scope) {.inline.} = - parent.needsTry = parent.needsTry or inner.needsTry - proc optimize(s: var Scope) = # optimize away simple 'wasMoved(x); destroy(x)' pairs. #[ Unfortunately this optimization is only really safe when no exceptions @@ -124,68 +117,6 @@ proc optimize(s: var Scope) = filterNil(wasMoved) filterNil(final) -type - ToTreeFlag = enum - onlyCareAboutVars, - producesValue - -proc toTree(c: var Con; s: var Scope; ret: PNode; flags: set[ToTreeFlag]): PNode = - proc isComplexStmtListExpr(n: PNode): bool = - n.kind == nkStmtListExpr and (n.len == 0 or n[^1].kind != nkSym) - - #if not s.needsTry: optimize(s) - assert ret != nil - if s.vars.len == 0 and s.final.len == 0 and s.wasMoved.len == 0: - # trivial, nothing was done: - result = ret - else: - let isExpr = producesValue in flags and not isEmptyType(ret.typ) - var r = PNode(nil) - if isExpr: - result = newNodeIT(nkStmtListExpr, ret.info, ret.typ) - if ret.kind in nkCallKinds or isComplexStmtListExpr(ret): - r = c.getTemp(s, ret.typ, ret.info) - else: - result = newNodeI(nkStmtList, ret.info) - - if s.vars.len > 0: - let varSection = newNodeI(nkVarSection, ret.info) - for tmp in s.vars: - varSection.add newTree(nkIdentDefs, newSymNode(tmp), newNodeI(nkEmpty, ret.info), - newNodeI(nkEmpty, ret.info)) - result.add varSection - if onlyCareAboutVars in flags: - result.add ret - s.vars.setLen 0 - elif s.needsTry: - var finSection = newNodeI(nkStmtList, ret.info) - for m in s.wasMoved: finSection.add m - for i in countdown(s.final.high, 0): finSection.add s.final[i] - result.add newTryFinally(ret, finSection) - else: - if r != nil: - if ret.kind == nkStmtListExpr: - # simplify it a bit further by merging the nkStmtListExprs - let last = ret.len - 1 - for i in 0 ..< last: result.add ret[i] - result.add newTree(nkFastAsgn, r, ret[last]) - else: - result.add newTree(nkFastAsgn, r, ret) - for m in s.wasMoved: result.add m - for i in countdown(s.final.high, 0): result.add s.final[i] - result.add r - elif ret.kind == nkStmtListExpr: - # simplify it a bit further by merging the nkStmtListExprs - let last = ret.len - 1 - for i in 0 ..< last: result.add ret[i] - for m in s.wasMoved: result.add m - for i in countdown(s.final.high, 0): result.add s.final[i] - result.add ret[last] - else: - result.add ret - for m in s.wasMoved: result.add m - for i in countdown(s.final.high, 0): result.add s.final[i] - proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope; isDecl = false): PNode @@ -228,6 +159,7 @@ proc isLastRead(n: PNode; c: var Con): bool = # first we need to search for the instruction that belongs to 'n': var instr = -1 let m = dfa.skipConvDfa(n) + if m.kind == nkSym and sfSingleUsedTemp in m.sym.flags: return true for i in 0.. 0: - it = it[^1] - s.escapingExpr = it - proc pVarTopLevel(v: PNode; c: var Con; s: var Scope; ri, res: PNode) = # move the variable declaration to the top of the frame: - let owningScope = c.addTopVar(s, v) + s.vars.add v.sym if isUnpackedTuple(v): if c.inLoop > 0: # unpacked tuple needs reset at every loop iteration res.add newTree(nkFastAsgn, v, genDefaultCall(v.typ, c, v.info)) elif sfThread notin v.sym.flags: # do not destroy thread vars for now at all for consistency. - if sfGlobal in v.sym.flags and s.parent == nil: - c.graph.globalDestructors.add c.genDestroy(v) #No need to genWasMoved here + if sfGlobal in v.sym.flags and s.parent == nil: #XXX: Rethink this logic (see tarcmisc.test2) + c.graph.globalDestructors.add c.genDestroy(v) else: - owningScope[].final.add c.genDestroy(v) + s.final.add c.genDestroy(v) if ri.kind == nkEmpty and c.inLoop > 0: res.add moveOrCopy(v, genDefaultCall(v.typ, c, v.info), c, s, isDecl = true) elif ri.kind != nkEmpty: res.add moveOrCopy(v, ri, c, s, isDecl = true) -template handleNestedTempl(n, processCall: untyped; alwaysStmt: bool) = +proc processScope(c: var Con; s: var Scope; ret: PNode): PNode = + result = newNodeI(nkStmtList, ret.info) + if s.vars.len > 0: + let varSection = newNodeI(nkVarSection, ret.info) + for tmp in s.vars: + varSection.add newTree(nkIdentDefs, newSymNode(tmp), newNodeI(nkEmpty, ret.info), + newNodeI(nkEmpty, ret.info)) + result.add varSection + if s.wasMoved.len > 0 or s.final.len > 0: + let finSection = newNodeI(nkStmtList, ret.info) + for m in s.wasMoved: finSection.add m + for i in countdown(s.final.high, 0): finSection.add s.final[i] + if s.needsTry: + result.add newTryFinally(ret, finSection) + else: + result.add ret + result.add finSection + else: + result.add ret + + if s.parent != nil: s.parent[].needsTry = s.parent[].needsTry or s.needsTry + +template processScopeExpr(c: var Con; s: var Scope; ret: PNode, processCall: untyped): PNode = + assert not ret.typ.isEmptyType + var result = newNodeI(nkStmtListExpr, ret.info) + # There is a possibility to do this check: s.wasMoved.len > 0 or s.final.len > 0 + # later and use it to eliminate the temporary when theres no need for it, but its + # tricky because you would have to intercept moveOrCopy at a certain point + let tmp = c.getTemp(s.parent[], ret.typ, ret.info) + tmp.sym.flags.incl sfSingleUsedTemp + let cpy = if ret.typ.hasDestructor: + moveOrCopy(tmp, ret, c, s, isDecl = true) + else: + newTree(nkFastAsgn, tmp, p(ret, c, s, normal)) + + if s.vars.len > 0: + let varSection = newNodeI(nkVarSection, ret.info) + for tmp in s.vars: + varSection.add newTree(nkIdentDefs, newSymNode(tmp), newNodeI(nkEmpty, ret.info), + newNodeI(nkEmpty, ret.info)) + result.add varSection + let finSection = newNodeI(nkStmtList, ret.info) + for m in s.wasMoved: finSection.add m + for i in countdown(s.final.high, 0): finSection.add s.final[i] + if s.needsTry: + result.add newTryFinally(newTree(nkStmtListExpr, cpy, processCall(tmp, s.parent[])), finSection) + else: + result.add cpy + result.add finSection + result.add processCall(tmp, s.parent[]) + + if s.parent != nil: s.parent[].needsTry = s.parent[].needsTry or s.needsTry + + result + +template handleNestedTempl(n, processCall: untyped, willProduceStmt = false) = template maybeVoid(child, s): untyped = if isEmptyType(child.typ): p(child, c, s, normal) else: processCall(child, s) - let treeFlags = if not isEmptyType(n.typ) and not alwaysStmt: {producesValue} else: {} case n.kind of nkStmtList, nkStmtListExpr: # a statement list does not open a new scope if n.len == 0: return n result = copyNode(n) - if alwaysStmt: result.typ = nil for i in 0..---------transformed-to--------->" echo renderTree(result, {renderIds}) diff --git a/tests/arc/tarcmisc.nim b/tests/arc/tarcmisc.nim index b6d9d781d..2d7e6b455 100644 --- a/tests/arc/tarcmisc.nim +++ b/tests/arc/tarcmisc.nim @@ -3,6 +3,11 @@ discard """ 123xyzabc destroyed: false destroyed: false +destroyed2: false +destroyed2: false +destroying variable: 2 +destroying variable: 1 +whiley ends :( 1 (x: "0") (x: "1") @@ -17,7 +22,8 @@ destroyed: false (x: "10") 0 closed -destroying variable +destroying variable: 20 +destroying variable: 10 ''' cmd: "nim c --gc:arc $file" """ @@ -40,11 +46,12 @@ type Variable = ref object value: int proc `=destroy`(self: var typeof(Variable()[])) = - echo "destroying variable" + echo "destroying variable: ",self.value proc newVariable(value: int): Variable = result = Variable() result.value = value + #echo "creating variable: ",result.value proc test(count: int) = var v {.global.} = newVariable(10) @@ -57,6 +64,28 @@ proc test(count: int) = test(3) +proc test2(count: int) = + #block: #XXX: Fails with block currently + var v {.global.} = newVariable(20) + + var count = count - 1 + if count == 0: return + + test2(count) + echo "destroyed2: ", v.isNil + +test2(3) + +proc whiley = + var a = newVariable(1) + while true: + var b = newVariable(2) + if true: raise newException(CatchableError, "test") + +try: + whiley() +except CatchableError: + echo "whiley ends :(" #------------------------------------------------------------------------------ # issue #13810 @@ -209,3 +238,8 @@ proc setParent(self: SimpleLoopB, parent: SimpleLoopB) = var l = SimpleLoopB() l.setParent(l) + + +# bug #14968 +import times +let currentTime = now().utc diff --git a/tests/arc/tcontrolflow.nim b/tests/arc/tcontrolflow.nim index 41f614cb2..80cc2b187 100644 --- a/tests/arc/tcontrolflow.nim +++ b/tests/arc/tcontrolflow.nim @@ -1,12 +1,12 @@ discard """ output: '''begin A elif -destroyed end A +destroyed begin false if -destroyed end false +destroyed begin true if end true diff --git a/tests/arc/tmovebug.nim b/tests/arc/tmovebug.nim index ec0fce9a8..424785ed7 100644 --- a/tests/arc/tmovebug.nim +++ b/tests/arc/tmovebug.nim @@ -40,6 +40,33 @@ sink me (not sink) sinked and not optimized to a bitcopy sinked and not optimized to a bitcopy sinked and not optimized to a bitcopy +(data: @[0, 0]) +(data: @[0, 0]) +(data: @[0, 0]) +(data: @[0, 0]) +(data: @[0, 0]) +(data: @[0, 0]) +(data: @[0, 0]) +100 +hey +hey +(a: "a", b: 2) +ho +(a: "b", b: 3) +(b: "b", a: 2) +ho +(b: "a", a: 3) +hey +break +break +hey +ho +hey +ho +ho +king +live long; long live +king ''' """ @@ -325,3 +352,145 @@ proc update() = for i in 1..3: update() + + +# bug #14961 +type + Foo = object + data: seq[int] + +proc initFoo(len: int): Foo = + result = (let s = newSeq[int](len); Foo(data: s) ) + +var f = initFoo(2) +echo initFoo(2) + +proc initFoo2(len: int) = + echo if true: + let s = newSeq[int](len); Foo(data: s) + else: + let s = newSeq[int](len); Foo(data: s) + +initFoo2(2) + +proc initFoo3(len: int) = + echo (block: + let s = newSeq[int](len); Foo(data: s)) + +initFoo3(2) + +proc initFoo4(len: int) = + echo (let s = newSeq[int](len); Foo(data: s)) + +initFoo4(2) + +proc initFoo5(len: int) = + echo (case true + of true: + let s = newSeq[int](len); Foo(data: s) + of false: + let s = newSeq[int](len); Foo(data: s)) + +initFoo5(2) + +proc initFoo6(len: int) = + echo (block: + try: + let s = newSeq[int](len); Foo(data: s) + finally: discard) + +initFoo6(2) + +proc initFoo7(len: int) = + echo (block: + try: + raise newException(CatchableError, "sup") + let s = newSeq[int](len); Foo(data: s) + except CatchableError: + let s = newSeq[int](len); Foo(data: s) ) + +initFoo7(2) + + +# bug #14902 +iterator zip[T](s: openarray[T]): (T, T) = + var i = 0 + while i < 10: + yield (s[i mod 2], s[i mod 2 + 1]) + inc i + +var lastMem = int.high + +proc leak = + const len = 10 + var x = @[newString(len), newString(len), newString(len)] + + var c = 0 + for (a, b) in zip(x): + let newMem = getOccupiedMem() + assert newMem <= lastMem + lastMem = newMem + c += a.len + echo c + +leak() + + +proc consume(a: sink string) = echo a + +proc weirdScopes = + if (let a = "hey"; a.len > 0): + echo a + + while (let a = "hey"; a.len > 0): + echo a + break + + var a = block: (a: "a", b: 2) + echo a + (discard; a) = (echo "ho"; (a: "b", b: 3)) + echo a + + var b = try: (b: "b", a: 2) + except: raise + echo b + (discard; b) = (echo "ho"; (b: "a", a: 3)) + echo b + + var s = "break" + consume((echo "hey"; s)) + echo s + + echo (block: + var a = "hey" + (echo "hey"; "ho")) + + var b2 = "ho" + echo (block: + var a = "hey" + (echo "hey"; b2)) + echo b2 + + type status = enum + alive + + var king = "king" + echo (block: + var a = "a" + when true: + var b = "b" + case alive + of alive: + try: + var c = "c" + if true: + king + else: + "the abyss" + except: + echo "he ded" + "dead king") + echo "live long; long live" + echo king + +weirdScopes() diff --git a/tests/destructor/tmove_objconstr.nim b/tests/destructor/tmove_objconstr.nim index 0740bd46b..16d0daa22 100644 --- a/tests/destructor/tmove_objconstr.nim +++ b/tests/destructor/tmove_objconstr.nim @@ -2,7 +2,6 @@ discard """ output: '''test created test destroyed 0 -Pony is dying! 1 2 3 From 9465b5db443b98d7ac2c1683e986716fb9a9290f Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Tue, 14 Jul 2020 11:44:56 -0300 Subject: [PATCH 0407/3768] Clean up macros (#14959) --- changelog.md | 2 +- examples/talk/formatoptimizer.nim | 2 +- lib/core/macros.nim | 34 ----------------------------- lib/pure/unittest.nim | 2 +- testament/important_packages.nim | 2 +- tests/macros/tmemit.nim | 3 --- tests/showoff/tformatopt.nim | 2 +- tests/usingstmt/tusingstatement.nim | 2 +- 8 files changed, 6 insertions(+), 43 deletions(-) diff --git a/changelog.md b/changelog.md index 36582eee2..e30e9038b 100644 --- a/changelog.md +++ b/changelog.md @@ -139,7 +139,7 @@ - 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. - +- Removed deprecated symbols from `macros` module, deprecated as far back as `0.15`. ## Language changes diff --git a/examples/talk/formatoptimizer.nim b/examples/talk/formatoptimizer.nim index 104214e19..6e3d0c2c3 100644 --- a/examples/talk/formatoptimizer.nim +++ b/examples/talk/formatoptimizer.nim @@ -33,7 +33,7 @@ macro optFormat{`%`(f, a)}(f: string{lit}, a: openArray[string]): untyped = #newCall("&") let f = f.strVal formatImpl(newLit) - result = nestList(!"&", result) + result = nestList(newIdentNode("&"), result) template optAdd1{x = y; add(x, z)}(x, y, z: string) = x = y & z diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 838707280..7b03d4e4d 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -140,10 +140,6 @@ const {.push warnings: off.} -proc `!`*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect, deprecated: - "Deprecated since version 0.18.0: Use 'ident' or 'newIdentNode' instead.".} - ## Constructs an identifier from the string `s`. - proc toNimIdent*(s: string): NimIdent {.magic: "StrToIdent", noSideEffect, deprecated: "Deprecated since version 0.18.0: Use 'ident' or 'newIdentNode' instead.".} ## Constructs an identifier from the string `s`. @@ -393,13 +389,6 @@ proc `ident=`*(n: NimNode, val: NimIdent) {.magic: "NSetIdent", noSideEffect, de {.pop.} -#proc `typ=`*(n: NimNode, typ: typedesc) {.magic: "NSetType".} -# this is not sound! Unfortunately forbidding 'typ=' is not enough, as you -# can easily do: -# let bracket = semCheck([1, 2]) -# let fake = semCheck(2.0) -# bracket[0] = fake # constructs a mixed array with ints and floats! - proc `strVal=`*(n: NimNode, val: string) {.magic: "NSetStrVal", noSideEffect.} ## Sets the string value of a string literal or comment. ## Setting `strVal` is disallowed for `nnkIdent` and `nnkSym` nodes; a new node @@ -830,14 +819,6 @@ proc nestList*(op: NimNode; pack: NimNode; init: NimNode): NimNode {.compileTime for i in countdown(pack.len - 1, 0): result = newCall(op, pack[i], result) -{.push warnings: off.} - -proc nestList*(theProc: NimIdent, x: NimNode): NimNode {.compileTime, deprecated: - "Deprecated since v0.18.1; use one of 'nestList(NimNode, ...)' instead.".} = - nestList(newIdentNode(theProc), x) - -{.pop.} - proc treeTraverse(n: NimNode; res: var string; level = 0; isLisp = false, indented = false) {.benign.} = if level > 0: if indented: @@ -1628,21 +1609,6 @@ macro getCustomPragmaVal*(n: typed, cp: typed{nkSym}): untyped = if result.kind == nnkEmpty: error(n.repr & " doesn't have a pragma named " & cp.repr()) # returning an empty node results in most cases in a cryptic error, - -when not defined(booting): - template emit*(e: static[string]): untyped {.deprecated.} = - ## Accepts a single string argument and treats it as nim code - ## that should be inserted verbatim in the program - ## Example: - ## - ## .. code-block:: nim - ## emit("echo " & '"' & "hello world".toUpper & '"') - ## - ## Deprecated since version 0.15 since it's so rarely useful. - macro payload: untyped {.gensym.} = - result = parseStmt(e) - payload() - macro unpackVarargs*(callee: untyped; args: varargs[untyped]): untyped = result = newCall(callee) for i in 0 ..< args.len: diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim index f3d920d0e..98be959e3 100644 --- a/lib/pure/unittest.nim +++ b/lib/pure/unittest.nim @@ -678,7 +678,7 @@ macro check*(conditions: untyped): untyped = result = newNimNode(nnkStmtList) for node in checked: if node.kind != nnkCommentStmt: - result.add(newCall(!"check", node)) + result.add(newCall(newIdentNode("check"), node)) else: let lineinfo = newStrLitNode(checked.lineInfo) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 972bb3faa..58f07b017 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -9,7 +9,7 @@ var packages2*: seq[tuple[name, cmd: string; hasDeps: bool; url: string, useHead # packages A-M -pkg1 "alea", true +# pkg1 "alea", true pkg1 "argparse" pkg1 "arraymancer", true, "nim c tests/tests_cpu.nim" pkg1 "ast_pattern_matching", false, "nim c -r --oldgensym:on tests/test1.nim" diff --git a/tests/macros/tmemit.nim b/tests/macros/tmemit.nim index 06ab8a1e2..6c9f9f935 100644 --- a/tests/macros/tmemit.nim +++ b/tests/macros/tmemit.nim @@ -1,6 +1,5 @@ discard """ output: ''' -HELLO WORLD c_func 12 ''' @@ -8,8 +7,6 @@ c_func import macros, strutils -emit("echo " & '"' & "hello world".toUpperAscii & '"') - # bug #1025 macro foo(icname): untyped = diff --git a/tests/showoff/tformatopt.nim b/tests/showoff/tformatopt.nim index 6e790c38e..420dd026b 100644 --- a/tests/showoff/tformatopt.nim +++ b/tests/showoff/tformatopt.nim @@ -37,7 +37,7 @@ macro optFormat{`%`(f, a)}(f: string{lit}, a: openArray[string]): untyped = result = newNimNode(nnkBracket) let f = f.strVal formatImpl(newLit) - result = nestList(!"&", result) + result = nestList(newIdentNode("&"), result) template optAdd1{x = y; add(x, z)}(x, y, z: string) = x = y & z diff --git a/tests/usingstmt/tusingstatement.nim b/tests/usingstmt/tusingstatement.nim index 6e4998892..dd4cf589d 100644 --- a/tests/usingstmt/tusingstatement.nim +++ b/tests/usingstmt/tusingstatement.nim @@ -38,7 +38,7 @@ macro autoClose(args: varargs[untyped]): untyped = varAssignment.add(varValue) variables.add(varAssignment) - closingCalls.add(newCall(!"close", varName)) + closingCalls.add(newCall(newIdentNode("close"), varName)) else: error "Using statement: Unexpected expression. Got " & $args[i].kind & " instead of assignment." From 03b0374e5cec82146ead8468db0826c287378a13 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Tue, 14 Jul 2020 18:36:06 +0200 Subject: [PATCH 0408/3768] Closes #8426 --- tests/generics/tgenerics_issues.nim | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/generics/tgenerics_issues.nim b/tests/generics/tgenerics_issues.nim index 54360f178..986066a77 100644 --- a/tests/generics/tgenerics_issues.nim +++ b/tests/generics/tgenerics_issues.nim @@ -23,6 +23,7 @@ concrete 88 G:0,1:0.1 G:0,1:0.1 H:1:0.1 +0 ''' joinable: false """ @@ -780,3 +781,16 @@ block: # issue #9458 discard foo(1) + + +# bug #8426 +type + MyBool[T: uint] = range[T(0)..T(1)] # Works + +var x: MyBool[uint] +echo x + +# x = 2 # correctly prevented + +type + MyBool2 = range[uint(0)..uint(1)] # Error ordinal or float type expected From 695a537c054c5b4e2d8d50e349bef3dfdf15800e Mon Sep 17 00:00:00 2001 From: Clyybber Date: Tue, 14 Jul 2020 18:38:45 +0200 Subject: [PATCH 0409/3768] Closes #13253 --- tests/macros/tmacros_issues.nim | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/macros/tmacros_issues.nim b/tests/macros/tmacros_issues.nim index 23c9c66c0..255a98ba9 100644 --- a/tests/macros/tmacros_issues.nim +++ b/tests/macros/tmacros_issues.nim @@ -29,6 +29,12 @@ array[0 .. 100, int] 10 test 0o377'i8 +1 +2 +3 +foo1 +foo2 +foo3 ''' """ @@ -258,3 +264,28 @@ macro foobar() = echo loopVars foobar() + + +# bug #13253 +import macros + +type + FooBar = object + a: seq[int] + +macro genFoobar(a: static FooBar): untyped = + result = newStmtList() + for b in a.a: + result.add(newCall(bindSym"echo", newLit b)) + +proc foobar(a: static FooBar) = + genFoobar(a) # removing this make it work + for b in a.a: + echo "foo" & $b + +proc main() = + const a: seq[int] = @[1, 2,3] + # Error: type mismatch: got but expected 'seq[int]' + const fb = Foobar(a: a) + foobar(fb) +main() From ddfb7d9877920564ceec7863930380d21aa23f47 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Tue, 14 Jul 2020 18:44:16 +0200 Subject: [PATCH 0410/3768] Closes #10396 --- tests/generics/tgenerics_issues.nim | 55 +++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/tests/generics/tgenerics_issues.nim b/tests/generics/tgenerics_issues.nim index 986066a77..cdbaf885e 100644 --- a/tests/generics/tgenerics_issues.nim +++ b/tests/generics/tgenerics_issues.nim @@ -24,6 +24,9 @@ G:0,1:0.1 G:0,1:0.1 H:1:0.1 0 +(foo: None[seq[Foo]], s: "") +(foo: Some(@[(a: "world", bar: None[Bar])]), s: "hello,") +@[(a: "hey", bar: None[Bar])] ''' joinable: false """ @@ -794,3 +797,55 @@ echo x type MyBool2 = range[uint(0)..uint(1)] # Error ordinal or float type expected + + +# bug #10396 +import options, strutils + +type + Foo {.acyclic.} = object + a: string + bar: Option[Bar] + + Bar {.acyclic.} = object + foo: Option[seq[Foo]] # if this was just Option[Foo], everything works fine + s: string + +proc getBar(x: string): Bar + +proc intoFoos(ss: seq[string]): seq[Foo] = + result = @[] + for s in ss: + let spl = s.split(',') + if spl.len > 1: + result.add Foo(a: spl[0], + bar: some(getBar(spl[1]))) + else: + result.add Foo(a: s, + bar: none[Bar]()) + +proc getBar(x: string): Bar = + let spl = x.split(' ') + result = + if spl.len > 1: + Bar(foo: some(spl[1..high(spl)].intoFoos), + s: spl[0]) + else: + Bar(foo: none[seq[Foo]](), + s: "") + +proc fakeReadLine(): TaintedString = "hey" + +echo getBar(fakeReadLine()) # causes error + +echo getBar("hello, world") # causes error + +discard $getBar(fakeReadLine()) # causes error + +discard $getBar("hello, world") # causes error + +discard getBar(fakeReadLine()) # no error + +discard getBar("hello, world") # no error + +echo intoFoos(fakeReadLine().split(' ')) # no error, works as expected From f2b041f1684430d0118864b3900c54a1bfc15fa3 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Tue, 14 Jul 2020 19:33:10 +0200 Subject: [PATCH 0411/3768] Reenable a few tests --- tests/generics/texplicitgeneric2.nim | 7 +++---- tests/misc/tnoforward.nim | 2 +- tests/trmacros/tdisallowif.nim | 5 ++--- tests/typerel/ttypenovalue.nim | 3 +-- 4 files changed, 7 insertions(+), 10 deletions(-) diff --git a/tests/generics/texplicitgeneric2.nim b/tests/generics/texplicitgeneric2.nim index 573b10ae8..37b133130 100644 --- a/tests/generics/texplicitgeneric2.nim +++ b/tests/generics/texplicitgeneric2.nim @@ -1,6 +1,5 @@ discard """ output: "Key: 12 value: 12Key: 13 value: 13 Key: A value: 12 Key: B value: 13" - disabled: true """ # test explicit type instantiation @@ -14,12 +13,12 @@ proc newDict*[TKey, TValue](): PDict[TKey, TValue] = new(result) result.data = @[] -proc add*(d: PDict, k: TKey, v: TValue) = +proc add*(d: PDict, k: d.TKey, v: d.TValue) = d.data.add((k, v)) -#iterator items*(d: PDict): tuple[k: TKey, v: TValue] = -# for k, v in items(d.data): yield (k, v) +iterator items*(d: PDict): tuple[k: d.TKey, v: d.TValue] = + for k, v in items(d.data): yield (k, v) var d = newDict[int, string]() d.add(12, "12") diff --git a/tests/misc/tnoforward.nim b/tests/misc/tnoforward.nim index 3e96e3489..b6a71897a 100644 --- a/tests/misc/tnoforward.nim +++ b/tests/misc/tnoforward.nim @@ -1,5 +1,5 @@ discard """ - disabled: true + output: "10" """ # {. noforward: on .} diff --git a/tests/trmacros/tdisallowif.nim b/tests/trmacros/tdisallowif.nim index 36f60800e..8d83f6ddf 100644 --- a/tests/trmacros/tdisallowif.nim +++ b/tests/trmacros/tdisallowif.nim @@ -1,7 +1,6 @@ discard """ - errormsg: "usage of 'disallowIf' is a user-defined error" + errormsg: "usage of 'disallowIf' is an {.error.} defined at tdisallowif.nim(10, 1)" line: 24 - disabled: true """ template optZero{x+x}(x: int): int = x*3 @@ -11,7 +10,7 @@ template optSubstr1{x = substr(x, 0, b)}(x: string, b: int) = setlen(x, b+1) template disallowIf{ if cond: action else: action2 -}(cond: bool, action, action2: stmt) {.error.} = action +}(cond: bool, action, action2: typed) {.error.} = action var y = 12 echo y+y diff --git a/tests/typerel/ttypenovalue.nim b/tests/typerel/ttypenovalue.nim index b86baf8b4..9af978466 100644 --- a/tests/typerel/ttypenovalue.nim +++ b/tests/typerel/ttypenovalue.nim @@ -1,7 +1,6 @@ discard """ - errormsg: "value expected, but got a type" + errormsg: "invalid type: 'type seq[tuple[title: string, body: string]]' for var" line: 7 - disabled: true """ proc crashAndBurn() = From c5f64f101b772a1df3ed61c1a2f26a21767b580c Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Wed, 15 Jul 2020 15:46:17 +0800 Subject: [PATCH 0412/3768] fix #13086 (#14987) * fix #6608 * minor * fix * clean tests * make testamnet happy * again * minor * fix #13086 --- lib/pure/parseopt.nim | 4 ++-- tests/misc/tparseopt.nim | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/lib/pure/parseopt.nim b/lib/pure/parseopt.nim index a95a5b48d..94df5ea40 100644 --- a/lib/pure/parseopt.nim +++ b/lib/pure/parseopt.nim @@ -280,8 +280,8 @@ proc handleShortOption(p: var OptParser; cmd: string) = while i < cmd.len and cmd[i] in {'\t', ' '}: inc(i) p.inShortState = false - if i < cmd.len and cmd[i] in {':', '='} or - card(p.shortNoVal) > 0 and p.key.string[0] notin p.shortNoVal: + if i < cmd.len and (cmd[i] in {':', '='} or + card(p.shortNoVal) > 0 and p.key.string[0] notin p.shortNoVal): if i < cmd.len and cmd[i] in {':', '='}: inc(i) p.inShortState = false diff --git a/tests/misc/tparseopt.nim b/tests/misc/tparseopt.nim index a3dfcbb26..47be05bac 100644 --- a/tests/misc/tparseopt.nim +++ b/tests/misc/tparseopt.nim @@ -22,6 +22,15 @@ kind: cmdShortOption key:val -- r:0 kind: cmdShortOption key:val -- l: kind: cmdShortOption key:val -- r:4 kind: cmdLongOption key:val -- debug: +cmdShortOption key: v value: '' +cmdArgument key: ABC value: '' +cmdShortOption key: v value: 'ABC' +cmdShortOption key: v value: '' +cmdArgument key: ABC value: '' +cmdShortOption key: v value: '' +cmdArgument key: ABC value: '' +cmdShortOption key: j value: '4' +cmdArgument key: ok value: '' ''' joinable: false """ @@ -121,3 +130,27 @@ arg 3 ai.len:4 :{a4"b} arg 4 ai.len:4 :{a5'b} arg 5 ai.len:4 :{a6\b} arg 6 ai.len:4 :{a7'b}""" + + + + block: + let args = @["-v", "ABC"] + var p = parseopt.initOptParser(args, shortnoVal = {'n'}, longnoVal = @["novalue"]) + for kind, key, val in parseopt.getopt(p): + echo kind," key: ", key, " value: '", val, "'" + + var r = parseopt.initOptParser(@["-v ABC"], shortnoVal = {'n'}, longnoVal = @["novalue"]) + for kind, key, val in parseopt.getopt(r): + echo kind," key: ", key, " value: '", val, "'" + + var s = parseopt.initOptParser("-v ABC", shortnoVal = {'v'}, longnoVal = @["novalue"]) + for kind, key, val in parseopt.getopt(s): + echo kind," key: ", key, " value: '", val, "'" + + var m = parseopt.initOptParser("-v ABC", shortnoVal = {'n'}, longnoVal = @["novalue"]) + for kind, key, val in parseopt.getopt(m): + echo kind," key: ", key, " value: '", val, "'" + + var n = parseopt.initOptParser("-j4 ok", shortnoVal = {'n'}, longnoVal = @["novalue"]) + for kind, key, val in parseopt.getopt(n): + echo kind," key: ", key, " value: '", val, "'" From 08159733cd1ab508a611cf60380572e879ae7f0f Mon Sep 17 00:00:00 2001 From: Clyybber Date: Wed, 15 Jul 2020 11:34:10 +0200 Subject: [PATCH 0413/3768] Fix #14985 (#14988) --- compiler/injectdestructors.nim | 4 ++-- tests/arc/tmovebug.nim | 30 ++++++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 2 deletions(-) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index ff311f079..98cfb4743 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -660,7 +660,7 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false) = branch[0] = p(it[0], c, s, normal) branch[^1] = if it[^1].typ.isEmptyType or willProduceStmt: - processScope(c, branchScope, processCall(it[^1], branchScope)) + processScope(c, branchScope, maybeVoid(it[^1], branchScope)) else: processScopeExpr(c, branchScope, it[^1], processCall) result.add branch @@ -679,7 +679,7 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false) = var branchScope = nestedScope(s) branch[^1] = if it[^1].typ.isEmptyType or willProduceStmt or it.kind == nkFinally: processScope(c, branchScope, if it.kind == nkFinally: p(it[^1], c, branchScope, normal) - else: processCall(it[^1], branchScope)) + else: maybeVoid(it[^1], branchScope)) else: processScopeExpr(c, branchScope, it[^1], processCall) result.add branch diff --git a/tests/arc/tmovebug.nim b/tests/arc/tmovebug.nim index 424785ed7..3b7a7c5df 100644 --- a/tests/arc/tmovebug.nim +++ b/tests/arc/tmovebug.nim @@ -67,6 +67,9 @@ ho king live long; long live king +hi +try +bye ''' """ @@ -494,3 +497,30 @@ proc weirdScopes = echo king weirdScopes() + + +# bug #14985 +proc getScope(): string = + if true: + return "hi" + else: + "else" + +echo getScope() + +proc getScope3(): string = + try: + "try" + except: + return "except" + +echo getScope3() + +proc getScope2(): string = + case true + of true: + return "bye" + else: + "else" + +echo getScope2() From e057b1d8395f4d639e195bc8f6a7e6922c149201 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Wed, 15 Jul 2020 19:00:52 +0200 Subject: [PATCH 0414/3768] Fix #14990 (#14991) * Fix #14990 * Add testcase --- compiler/semtypes.nim | 4 ++-- tests/generics/tgenerics_issues.nim | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 5407b9435..4001953bc 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1402,8 +1402,8 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = [s.name.s, s.kind.toHumanStr]) return newOrPrevType(tyError, prev, c) - var t = s.typ - if t.kind in {tyCompositeTypeClass, tyAlias} and t.base.kind == tyGenericBody: + var t = s.typ.skipTypes({tyAlias}) + if t.kind == tyCompositeTypeClass and t.base.kind == tyGenericBody: t = t.base result = newOrPrevType(tyGenericInvocation, prev, c) diff --git a/tests/generics/tgenerics_issues.nim b/tests/generics/tgenerics_issues.nim index cdbaf885e..812f339b9 100644 --- a/tests/generics/tgenerics_issues.nim +++ b/tests/generics/tgenerics_issues.nim @@ -849,3 +849,14 @@ discard getBar(fakeReadLine()) # no error discard getBar("hello, world") # no error echo intoFoos(fakeReadLine().split(' ')) # no error, works as expected + + +# bug #14990 +type + Tile3 = Tile2 + Tile2 = Tile + Tile[n] = object + a: n + +var a: Tile3[int] + From 813dd1b670b953b0ac8348b04079faadace46c29 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Wed, 15 Jul 2020 22:04:15 +0200 Subject: [PATCH 0415/3768] repr_v2 improvements (#14992) * Support proc in arc repr * Typo * Improve repr for strings and chars --- lib/system/repr_v2.nim | 38 ++++++++++++++++++++++++++++---------- tests/arc/trepr.nim | 6 ++++++ 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/lib/system/repr_v2.nim b/lib/system/repr_v2.nim index fcc187a42..d456f4454 100644 --- a/lib/system/repr_v2.nim +++ b/lib/system/repr_v2.nim @@ -29,21 +29,35 @@ proc repr*(x: bool): string {.magic: "BoolToStr", noSideEffect.} proc repr*(x: char): string {.noSideEffect.} = ## repr for a character argument. Returns `x` - ## converted to a string. + ## converted to an escaped string. ## ## .. code-block:: Nim ## assert repr('c') == "'c'" - '\'' & $x & '\'' + result.add '\'' + # Elides string creations if not needed + if x in {'\\', '\0'..'\31', '\127'..'\255'}: + result.add '\\' + if x in {'\0'..'\31', '\127'..'\255'}: + result.add $x.uint8 + else: + result.add x + result.add '\'' -proc repr*(x: cstring): string {.noSideEffect.} = - ## repr for a CString argument. Returns `x` - ## converted to a quoted string. - '"' & $x & '"' - -proc repr*(x: string): string {.noSideEffect.} = +proc repr*(x: string | cstring): string {.noSideEffect.} = ## repr for a string argument. Returns `x` - ## but quoted. - '"' & x & '"' + ## converted to a quoted and escaped string. + result.add '\"' + for i in 0.. Date: Wed, 15 Jul 2020 23:00:06 +0200 Subject: [PATCH 0416/3768] An optimizer for ARC (#14962) * WIP: an optimizer for ARC * do not optimize away destructors in 'finally' if unstructured control flow is involved * optimized the optimizer * minor code cleanup * first steps to .cursor inference * cursor inference: big steps to a working solution * baby steps * better .cursor inference * new feature: expandArc for easy inspection of the AST after ARC transformations * added topt_cursor test * adapt tests * cleanups, make tests green * optimize common traversal patterns * moved test case * fixes .cursor inference so that npeg compiles once again * cursor inference: more bugfixes Co-authored-by: Clyybber --- compiler/commands.nim | 3 + compiler/cursor_inference.nim | 295 ++++++++++++++++++ compiler/injectdestructors.nim | 54 ++-- compiler/optimizer.nim | 285 +++++++++++++++++ compiler/options.nim | 2 + compiler/renderer.nim | 21 +- compiler/transf.nim | 2 +- doc/advopt.txt | 2 + doc/destructors.rst | 28 +- lib/impure/nre.nim | 10 +- .../t12785.nim => arc/tcomputedgoto.nim} | 12 +- tests/arc/topt_cursor.nim | 35 +++ tests/arc/topt_no_cursor.nim | 37 +++ tests/arc/topt_refcursors.nim | 39 +++ tests/arc/topt_wasmoved_destroy_pairs.nim | 92 ++++++ tests/{generics => arc}/trtree.nim | 18 +- tests/destructor/tdestructor3.nim | 11 +- tests/destructor/tmisc_destructors.nim | 6 +- .../destructor/tuse_result_prevents_sinks.nim | 5 + 19 files changed, 895 insertions(+), 62 deletions(-) create mode 100644 compiler/cursor_inference.nim create mode 100644 compiler/optimizer.nim rename tests/{casestmt/t12785.nim => arc/tcomputedgoto.nim} (87%) create mode 100644 tests/arc/topt_cursor.nim create mode 100644 tests/arc/topt_no_cursor.nim create mode 100644 tests/arc/topt_refcursors.nim create mode 100644 tests/arc/topt_wasmoved_destroy_pairs.nim rename tests/{generics => arc}/trtree.nim (98%) diff --git a/compiler/commands.nim b/compiler/commands.nim index 1984c894a..d0936a0c6 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -866,6 +866,9 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "expandmacro": expectArg(conf, switch, arg, pass, info) conf.macrosToExpand[arg] = "T" + of "expandarc": + expectArg(conf, switch, arg, pass, info) + conf.arcToExpand[arg] = "T" of "oldgensym": processOnOffSwitchG(conf, {optNimV019}, arg, pass, info) of "useversion": diff --git a/compiler/cursor_inference.nim b/compiler/cursor_inference.nim new file mode 100644 index 000000000..7806a535f --- /dev/null +++ b/compiler/cursor_inference.nim @@ -0,0 +1,295 @@ +# +# +# The Nim Compiler +# (c) Copyright 2020 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Cursor inference: +## The basic idea was like this: Elide 'destroy(x)' calls if only +## special literals are assigned to 'x' and 'x' is not mutated or +## passed by 'var T' to something else. Special literals are string literals or +## arrays / tuples of string literals etc. +## +## However, there is a much more general rule here: Compute which variables +## can be annotated with `.cursor`. To see how and when we can do that, +## think about this question: In `dest = src` when do we really have to +## *materialize* the full copy? - Only if `dest` or `src` are mutated +## afterwards. `dest` is the potential cursor variable, so that is +## simple to analyse. And if `src` is a location derived from a +## formal parameter, we also know it is not mutated! In other words, we +## do a compile-time copy-on-write analysis. + +import + ast, types, renderer, idents, intsets, options, msgs + +type + Cursor = object + s: PSym + deps: IntSet + Con = object + cursors: seq[Cursor] + mayOwnData: IntSet + mutations: IntSet + reassigns: IntSet + config: ConfigRef + +proc locationRoot(e: PNode; followDotExpr = true): PSym = + var n = e + while true: + case n.kind + of nkSym: + if n.sym.kind in {skVar, skResult, skTemp, skLet, skForVar, skParam}: + return n.sym + else: + return nil + of nkDotExpr, nkDerefExpr, nkBracketExpr, nkHiddenDeref, + nkCheckedFieldExpr, nkAddr, nkHiddenAddr: + if followDotExpr: + n = n[0] + else: + return nil + of nkObjUpConv, nkObjDownConv: + n = n[0] + of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkCast: + n = n[1] + of nkStmtList, nkStmtListExpr: + if n.len > 0: + n = n[^1] + else: + return nil + else: + return nil + +proc addDep(c: var Con; dest: var Cursor; dependsOn: PSym) = + if dest.s != dependsOn: + dest.deps.incl dependsOn.id + +proc cursorId(c: Con; x: PSym): int = + for i in 0.. 0: + analyseAsgn(c, dest, n[^1]) + + of nkClosure: + for i in 1.. treat it like a sink parameter + c.mayOwnData.incl r.id + c.mutations.incl r.id + +proc analyse(c: var Con; n: PNode) = + case n.kind + of nkCallKinds: + let parameters = n[0].typ + let L = if parameters != nil: parameters.len else: 0 + + analyse(c, n[0]) + for i in 1..= 0: + analyseAsgn(c, c.cursors[idx], n[1]) + + c.reassigns.incl n[0].sym.id + else: + # assignments like 'x.field = value' mean that 'x' itself cannot + # be a cursor: + let r = locationRoot(n[0]) + if r != nil and r.typ.skipTypes(abstractInst).kind notin {tyPtr, tyRef}: + # however, an assignment like 'it.field = x' does not influence r's + # cursorness property: + c.mayOwnData.incl r.id + c.mutations.incl r.id + + if hasDestructor(n[1].typ): + rhsIsSink(c, n[1]) + + of nkAddr, nkHiddenAddr: + analyse(c, n[0]) + let r = locationRoot(n[0]) + if r != nil: + c.mayOwnData.incl r.id + c.mutations.incl r.id + + of nkTupleConstr, nkBracket, nkObjConstr: + for i in ord(n.kind == nkObjConstr)..---------transformed-to--------->" echo renderTree(result, {renderIds}) + + if g.config.arcToExpand.hasKey(owner.name.s): + echo "--expandArc: ", owner.name.s + echo renderTree(result, {renderIr}) + echo "-- end of expandArc ------------------------" diff --git a/compiler/optimizer.nim b/compiler/optimizer.nim new file mode 100644 index 000000000..5d1139bfd --- /dev/null +++ b/compiler/optimizer.nim @@ -0,0 +1,285 @@ +# +# +# The Nim Compiler +# (c) Copyright 2020 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Optimizer: +## - elide 'wasMoved(x); destroy(x)' pairs +## - recognize "all paths lead to 'wasMoved(x)'" + +import + ast, renderer, idents, intsets + +from trees import exprStructuralEquivalent + +const + nfMarkForDeletion = nfNone # faster than a lookup table + +type + BasicBlock = object + wasMovedLocs: seq[PNode] + kind: TNodeKind + hasReturn, hasBreak: bool + label: PSym # can be nil + parent: ptr BasicBlock + + Con = object + somethingTodo: bool + inFinally: int + +proc nestedBlock(parent: var BasicBlock; kind: TNodeKind): BasicBlock = + BasicBlock(wasMovedLocs: @[], kind: kind, hasReturn: false, hasBreak: false, + label: nil, parent: addr(parent)) + +proc breakStmt(b: var BasicBlock; n: PNode) = + var it = addr(b) + while it != nil: + it.wasMovedLocs.setLen 0 + it.hasBreak = true + + if n.kind == nkSym: + if it.label == n.sym: break + else: + # unnamed break leaves the block is nkWhileStmt or the like: + if it.kind in {nkWhileStmt, nkBlockStmt, nkBlockExpr}: break + + it = it.parent + +proc returnStmt(b: var BasicBlock) = + b.hasReturn = true + var it = addr(b) + while it != nil: + it.wasMovedLocs.setLen 0 + it = it.parent + +proc mergeBasicBlockInfo(parent: var BasicBlock; this: BasicBlock) {.inline.} = + if this.hasReturn: + parent.wasMovedLocs.setLen 0 + parent.hasReturn = true + +proc wasMovedTarget(matches: var IntSet; branch: seq[PNode]; moveTarget: PNode): bool = + result = false + for i in 0.. 0 and (b.hasReturn or b.hasBreak): + discard "cannot optimize away the destructor" + else: + c.wasMovedDestroyPair b, n + special = true + elif s.name.s == "=sink": + reverse = true + + if not special: + if not reverse: + for i in 0 ..< n.len: + analyse(c, b, n[i]) + else: + #[ Test tmatrix.test3: + Prevent this from being elided. We should probably + find a better solution... + + `=sink`(b, - ( + let blitTmp = b; + wasMoved(b); + blitTmp + a) + `=destroy`(b) + + ]# + for i in countdown(n.len-1, 0): + analyse(c, b, n[i]) + if canRaise(n[0]): returnStmt(b) + + of nkSym: + # any usage of the location before destruction implies we + # cannot elide the 'wasMoved(x)': + b.invalidateWasMoved n + + of nkNone..pred(nkSym), succ(nkSym)..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef, + nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo, + nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt, + nkExportStmt, nkPragma, nkCommentStmt, nkBreakState, nkTypeOfExpr: + discard "do not follow the construct" + + of nkAsgn, nkFastAsgn: + # reverse order, see remark for `=sink`: + analyse(c, b, n[1]) + analyse(c, b, n[0]) + + of nkIfStmt, nkIfExpr: + let isExhaustive = n[^1].kind in {nkElse, nkElseExpr} + var wasMovedSet: seq[PNode] = @[] + + for i in 0 ..< n.len: + var branch = nestedBlock(b, n[i].kind) + + analyse(c, branch, n[i]) + mergeBasicBlockInfo(b, branch) + if isExhaustive: + if i == 0: + wasMovedSet = move(branch.wasMovedLocs) + else: + wasMovedSet.intersect(branch.wasMovedLocs) + for i in 0..= ord(tokKeywordLow) - ord(tkSymbol)) and (i.id <= ord(tokKeywordHigh) - ord(tkSymbol)): @@ -850,7 +860,12 @@ proc gident(g: var TSrcGen, n: PNode) = t = tkSymbol else: t = tkOpr - if n.kind == nkSym and (renderIds in g.flags or sfGenSym in n.sym.flags or n.sym.kind == skTemp): + if renderIr in g.flags and n.kind == nkSym: + let localId = disamb(g, n.sym) + if localId != 0 and n.sym.magic == mNone: + s.add '_' + s.addInt localId + elif n.kind == nkSym and (renderIds in g.flags or sfGenSym in n.sym.flags or n.sym.kind == skTemp): s.add '_' s.addInt n.sym.id when defined(debugMagics): @@ -1022,7 +1037,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = else: put(g, tkSymbol, "(wrong conv)") of nkHiddenCallConv: - if renderIds in g.flags: + if {renderIds, renderIr} * g.flags != {}: accentedName(g, n[0]) put(g, tkParLe, "(") gcomma(g, n, 1) diff --git a/compiler/transf.nim b/compiler/transf.nim index 10a2680ae..be559abb8 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -233,7 +233,7 @@ proc hasContinue(n: PNode): bool = proc newLabel(c: PTransf, n: PNode): PSym = result = newSym(skLabel, nil, getCurrOwner(c), n.info) - result.name = getIdent(c.graph.cache, genPrefix & $result.id) + result.name = getIdent(c.graph.cache, genPrefix) proc transformBlock(c: PTransf, n: PNode): PNode = var labl: PSym diff --git a/doc/advopt.txt b/doc/advopt.txt index 135f297c4..6665ce537 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -122,6 +122,8 @@ Advanced options: use the provided namespace for the generated C++ code, if no namespace is provided "Nim" will be used --expandMacro:MACRO dump every generated AST from MACRO + --expandArc:PROCNAME show how PROCNAME looks like after diverse optimizations + before the final backend phase (mostly ARC/ORC specific) --excludePath:PATH exclude a path from the list of search paths --dynlibOverride:SYMBOL marks SYMBOL so that dynlib:SYMBOL has no effect and can be statically linked instead; diff --git a/doc/destructors.rst b/doc/destructors.rst index fefc646f5..6cab6e1d2 100644 --- a/doc/destructors.rst +++ b/doc/destructors.rst @@ -354,7 +354,8 @@ Destructor removal ``wasMoved(x);`` followed by a `=destroy(x)` operation cancel each other out. An implementation is encouraged to exploit this in order to improve -efficiency and code sizes. +efficiency and code sizes. The current implementation does perform this +optimization. Self assignments @@ -509,6 +510,31 @@ to be safe, but for ``ptr`` the compiler has to remain silent about possible problems. +Cursor inference / copy elision +=============================== + +The current implementation also performs `.cursor` inference. Cursor inference is +a form of copy elision. + +To see how and when we can do that, think about this question: In `dest = src` when +do we really have to *materialize* the full copy? - Only if `dest` or `src` are mutated +afterwards. If `dest` is a local variable that is simple to analyse. And if `src` is a +location derived from a formal parameter, we also know it is not mutated! In other +words, we do a compile-time copy-on-write analysis. + +This means that "borrowed" views can be written naturally and without explicit pointer +indirections: + +.. code-block:: nim + + proc main(tab: Table[string, string]) = + let v = tab["key"] # inferred as .cursor because 'tab' is not mutated. + # no copy into 'v', no destruction of 'v'. + use(v) + useItAgain(v) + + + Owned refs ========== diff --git a/lib/impure/nre.nim b/lib/impure/nre.nim index 653d4d1c5..0817ca4ec 100644 --- a/lib/impure/nre.nim +++ b/lib/impure/nre.nim @@ -498,7 +498,7 @@ proc re*(pattern: string): Regex = initRegex(pattern, flags, study) proc matchImpl(str: string, pattern: Regex, start, endpos: int, flags: int): Option[RegexMatch] = - var myResult = RegexMatch(pattern : pattern, str : str) + var myResult = RegexMatch(pattern: pattern, str: str) # See PCRE man pages. # 2x capture count to make room for start-end pairs # 1x capture count as slack space for PCRE @@ -528,13 +528,13 @@ proc matchImpl(str: string, pattern: Regex, start, endpos: int, flags: int): Opt of pcre.ERROR_NULL: raise newException(AccessViolationDefect, "Expected non-null parameters") of pcre.ERROR_BADOPTION: - raise RegexInternalError(msg : "Unknown pattern flag. Either a bug or " & + raise RegexInternalError(msg: "Unknown pattern flag. Either a bug or " & "outdated PCRE.") of pcre.ERROR_BADUTF8, pcre.ERROR_SHORTUTF8, pcre.ERROR_BADUTF8_OFFSET: - raise InvalidUnicodeError(msg : "Invalid unicode byte sequence", - pos : myResult.pcreMatchBounds[0].a) + raise InvalidUnicodeError(msg: "Invalid unicode byte sequence", + pos: myResult.pcreMatchBounds[0].a) else: - raise RegexInternalError(msg : "Unknown internal error: " & $execRet) + raise RegexInternalError(msg: "Unknown internal error: " & $execRet) proc match*(str: string, pattern: Regex, start = 0, endpos = int.high): Option[RegexMatch] = ## Like ` ``find(...)`` <#proc-find>`_, but anchored to the start of the diff --git a/tests/casestmt/t12785.nim b/tests/arc/tcomputedgoto.nim similarity index 87% rename from tests/casestmt/t12785.nim rename to tests/arc/tcomputedgoto.nim index 7177fb9c2..8af17b56e 100644 --- a/tests/casestmt/t12785.nim +++ b/tests/arc/tcomputedgoto.nim @@ -1,15 +1,7 @@ discard """ cmd: '''nim c --newruntime $file''' - output: '''copied -copied -2 -destroyed -destroyed -copied -copied -2 -destroyed -destroyed''' + output: '''2 +2''' """ type diff --git a/tests/arc/topt_cursor.nim b/tests/arc/topt_cursor.nim new file mode 100644 index 000000000..6b923cf76 --- /dev/null +++ b/tests/arc/topt_cursor.nim @@ -0,0 +1,35 @@ +discard """ + output: '''("string here", 80)''' + cmd: '''nim c --gc:arc --expandArc:main --hint:Performance:off $file''' + nimout: '''--expandArc: main + +var + :tmpD + :tmpD_1 + :tmpD_2 +try: + var x = ("hi", 5) + x = if cond: + :tmpD = ("different", 54) + :tmpD else: + :tmpD_1 = ("string here", 80) + :tmpD_1 + echo [ + :tmpD_2 = `$`(x) + :tmpD_2] +finally: + `=destroy`(:tmpD_2) +-- end of expandArc ------------------------''' +""" + +proc main(cond: bool) = + var x = ("hi", 5) # goal: computed as cursor + + x = if cond: + ("different", 54) + else: + ("string here", 80) + + echo x + +main(false) diff --git a/tests/arc/topt_no_cursor.nim b/tests/arc/topt_no_cursor.nim new file mode 100644 index 000000000..3d3262491 --- /dev/null +++ b/tests/arc/topt_no_cursor.nim @@ -0,0 +1,37 @@ +discard """ + output: '''(repo: "", package: "meo", ext: "")''' + cmd: '''nim c --gc:arc --expandArc:newTarget --hint:Performance:off $file''' + nimout: '''--expandArc: newTarget + +var + splat + :tmp + :tmp_1 + :tmp_2 +splat = splitFile(path) +:tmp = splat.dir +wasMoved(splat.dir) +:tmp_1 = splat.name +wasMoved(splat.name) +:tmp_2 = splat.ext +wasMoved(splat.ext) +result = ( + let blitTmp = :tmp + blitTmp, + let blitTmp_1 = :tmp_1 + blitTmp_1, + let blitTmp_2 = :tmp_2 + blitTmp_2) +`=destroy`(splat) +-- end of expandArc ------------------------''' +""" + +import os + +type Target = tuple[repo, package, ext: string] + +proc newTarget*(path: string): Target = + let splat = path.splitFile + result = (repo: splat.dir, package: splat.name, ext: splat.ext) + +echo newTarget("meo") diff --git a/tests/arc/topt_refcursors.nim b/tests/arc/topt_refcursors.nim new file mode 100644 index 000000000..7316e2beb --- /dev/null +++ b/tests/arc/topt_refcursors.nim @@ -0,0 +1,39 @@ +discard """ + output: '''''' + cmd: '''nim c --gc:arc --expandArc:traverse --hint:Performance:off $file''' + nimout: '''--expandArc: traverse + +var it = root +block :tmp: + while ( + not (it == nil)): + echo [it.s] + it = it.ri +var jt = root +block :tmp_1: + while ( + not (jt == nil)): + let ri_1 = jt.ri + echo [jt.s] + jt = ri_1 +-- end of expandArc ------------------------''' +""" + +type + Node = ref object + le, ri: Node + s: string + +proc traverse(root: Node) = + var it = root + while it != nil: + echo it.s + it = it.ri + + var jt = root + while jt != nil: + let ri = jt.ri + echo jt.s + jt = ri + +traverse(nil) diff --git a/tests/arc/topt_wasmoved_destroy_pairs.nim b/tests/arc/topt_wasmoved_destroy_pairs.nim new file mode 100644 index 000000000..4d248492b --- /dev/null +++ b/tests/arc/topt_wasmoved_destroy_pairs.nim @@ -0,0 +1,92 @@ +discard """ + output: '''''' + cmd: '''nim c --gc:arc --expandArc:main --expandArc:tfor --hint:Performance:off $file''' + nimout: '''--expandArc: main + +var + a + b + x +x = f() +if cond: + add(a): + let blitTmp = x + blitTmp +else: + add(b): + let blitTmp_1 = x + blitTmp_1 +`=destroy`(b) +`=destroy`(a) +-- end of expandArc ------------------------ +--expandArc: tfor + +var + a + b + x +try: + x = f() + block :tmp: + var i + var i_1 = 0 + block :tmp_1: + while i_1 < 4: + var :tmpD + i = i_1 + if i == 2: + return + add(a): + wasMoved(:tmpD) + `=`(:tmpD, x) + :tmpD + inc i_1, 1 + if cond: + add(a): + let blitTmp = x + wasMoved(x) + blitTmp + else: + add(b): + let blitTmp_1 = x + wasMoved(x) + blitTmp_1 +finally: + `=destroy`(x) + `=destroy_1`(b) + `=destroy_1`(a) +-- end of expandArc ------------------------''' +""" + +proc f(): seq[int] = + @[1, 2, 3] + +proc main(cond: bool) = + var a, b: seq[seq[int]] + var x = f() + if cond: + a.add x + else: + b.add x + +# all paths move 'x' so no wasMoved(x); destroy(x) pair should be left in the +# AST. + +main(false) + + +proc tfor(cond: bool) = + var a, b: seq[seq[int]] + + var x = f() + + for i in 0 ..< 4: + if i == 2: return + a.add x + + if cond: + a.add x + else: + b.add x + +tfor(false) diff --git a/tests/generics/trtree.nim b/tests/arc/trtree.nim similarity index 98% rename from tests/generics/trtree.nim rename to tests/arc/trtree.nim index b45ac8c83..986268f51 100644 --- a/tests/generics/trtree.nim +++ b/tests/arc/trtree.nim @@ -138,16 +138,16 @@ proc search*[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; b: Box[D, RT]): seq[LT] # a R*TREE proc proc chooseSubtree[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; b: Box[D, RT]; level: int): H[M, D, RT, LT] = assert level >= 0 - var n = t.root - while n.level > level: - let nn = Node[M, D, RT, LT](n) + var it = t.root + while it.level > level: + let nn = Node[M, D, RT, LT](it) var i0 = 0 # selected index var minLoss = type(b[0].a).high - if n.level == 1: # childreen are leaves -- determine the minimum overlap costs - for i in 0 ..< n.numEntries: + if it.level == 1: # childreen are leaves -- determine the minimum overlap costs + for i in 0 ..< it.numEntries: let nx = union(nn.a[i].b, b) var loss = 0 - for j in 0 ..< n.numEntries: + for j in 0 ..< it.numEntries: if i == j: continue loss += (overlap(nx, nn.a[j].b) - overlap(nn.a[i].b, nn.a[j].b)) # overlap (i, j) == (j, i), so maybe cache that? var rep = loss < minLoss @@ -163,7 +163,7 @@ proc chooseSubtree[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; b: Box[D, RT]; lev i0 = i minLoss = loss else: - for i in 0 ..< n.numEntries: + for i in 0 ..< it.numEntries: let loss = enlargement(nn.a[i].b, b) var rep = loss < minLoss if loss == minLoss: @@ -174,8 +174,8 @@ proc chooseSubtree[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; b: Box[D, RT]; lev if rep: i0 = i minLoss = loss - n = nn.a[i0].n - return n + it = nn.a[i0].n + return it proc pickSeeds[M, D: Dim; RT, LT](t: RTree[M, D, RT, LT]; n: Node[M, D, RT, LT] | Leaf[M, D, RT, LT]; bx: Box[D, RT]): (int, int) = var i0, j0: int diff --git a/tests/destructor/tdestructor3.nim b/tests/destructor/tdestructor3.nim index b68aedce9..f967bbf95 100644 --- a/tests/destructor/tdestructor3.nim +++ b/tests/destructor/tdestructor3.nim @@ -1,5 +1,6 @@ discard """ - output: '''assign + output: ''' +assign destroy destroy 5 @@ -104,12 +105,12 @@ test() #------------------------------------------------------------ # Issue #12883 -type +type TopObject = object internal: UniquePtr[int] proc deleteTop(p: ptr TopObject) = - if p != nil: + if p != nil: `=destroy`(p[]) # !!! this operation used to leak the integer deallocshared(p) @@ -117,12 +118,12 @@ proc createTop(): ptr TopObject = result = cast[ptr TopObject](allocShared0(sizeof(TopObject))) result.internal = newUniquePtr(1) -proc test2() = +proc test2() = let x = createTop() echo $x.internal deleteTop(x) -echo "---------------" +echo "---------------" echo "app begin" test2() echo "app end" \ No newline at end of file diff --git a/tests/destructor/tmisc_destructors.nim b/tests/destructor/tmisc_destructors.nim index 73c54eab3..082cb0f78 100644 --- a/tests/destructor/tmisc_destructors.nim +++ b/tests/destructor/tmisc_destructors.nim @@ -21,11 +21,13 @@ proc `=sink`(dest: var Foo, src: Foo) = proc `=`(dest: var Foo, src: Foo) = assign_counter.inc +proc createFoo(): Foo = Foo(boo: 0) + proc test(): auto = - var a, b: Foo + var a, b = createFoo() return (a, b, Foo(boo: 5)) -var (a, b, _) = test() +var (ag, bg, _) = test() doAssert assign_counter == 0 doAssert sink_counter == 0 diff --git a/tests/destructor/tuse_result_prevents_sinks.nim b/tests/destructor/tuse_result_prevents_sinks.nim index 37b5af9b2..6eac5c902 100644 --- a/tests/destructor/tuse_result_prevents_sinks.nim +++ b/tests/destructor/tuse_result_prevents_sinks.nim @@ -17,15 +17,20 @@ proc `=sink`(self: var Foo; other: Foo) = proc `=destroy`(self: var Foo) = discard +template preventCursorInference(x) = + let p = unsafeAddr(x) + proc test(): Foo = result = Foo() let temp = result + preventCursorInference temp doAssert temp.i > 0 return result proc testB(): Foo = result = Foo() let temp = result + preventCursorInference temp doAssert temp.i > 0 discard test() From 617c1f16d70657fbe4a69d76a2f00b84560af15a Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 15 Jul 2020 23:04:18 +0200 Subject: [PATCH 0417/3768] disable debug output --- compiler/cursor_inference.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/cursor_inference.nim b/compiler/cursor_inference.nim index 7806a535f..e0999b5be 100644 --- a/compiler/cursor_inference.nim +++ b/compiler/cursor_inference.nim @@ -97,7 +97,7 @@ proc getCursors(c: Con): IntSet = break doAdd when true: result.incl cur.s.id - when true: + when false: echo "computed as a cursor ", cur.s, " ", cur.deps, " ", c.config $ cur.s.info proc analyseAsgn(c: var Con; dest: var Cursor; n: PNode) = From 282128ab66b6391c4ab9bc76cd19a63a5bce16f8 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Wed, 15 Jul 2020 23:33:58 +0200 Subject: [PATCH 0418/3768] Fix #14994 (#14996) * Fix #14994 * Revert misplaced "optimization" * Typo --- lib/pure/collections/sequtils.nim | 3 ++- lib/system/seqs_v2.nim | 1 + tests/arc/tarcmisc.nim | 14 ++++++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index f101d508e..51d8ade85 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -527,7 +527,8 @@ proc insert*[T](dest: var seq[T], src: openArray[T], pos = 0) = assert dest == outcome var j = len(dest) - 1 - var i = len(dest) + len(src) - 1 + var i = j + len(src) + if i == j: return dest.setLen(i + 1) # Move items after `pos` to the end of the sequence. diff --git a/lib/system/seqs_v2.nim b/lib/system/seqs_v2.nim index 4001e97c3..3c94a03f9 100644 --- a/lib/system/seqs_v2.nim +++ b/lib/system/seqs_v2.nim @@ -82,6 +82,7 @@ proc shrink*[T](x: var seq[T]; newLen: Natural) = proc grow*[T](x: var seq[T]; newLen: Natural; value: T) = let oldLen = x.len + #sysAssert newLen >= x.len, "invalid newLen parameter for 'grow'" if newLen <= oldLen: return var xu = cast[ptr NimSeqV2[T]](addr x) if xu.p == nil or xu.p.cap < newLen: diff --git a/tests/arc/tarcmisc.nim b/tests/arc/tarcmisc.nim index 2d7e6b455..56aa3ec3b 100644 --- a/tests/arc/tarcmisc.nim +++ b/tests/arc/tarcmisc.nim @@ -21,6 +21,8 @@ whiley ends :( (x: "9") (x: "10") 0 +new line before - @['a'] +new line after - @['a'] closed destroying variable: 20 destroying variable: 10 @@ -243,3 +245,15 @@ l.setParent(l) # bug #14968 import times let currentTime = now().utc + + +# bug #14994 +import sequtils +var newLine = @['a'] +let indent = newSeq[char]() + +echo "new line before - ", newline + +newline.insert(indent, 0) + +echo "new line after - ", newline From ba5604b96d7d2604a02b4f8ac8b67c54ea92ecfa Mon Sep 17 00:00:00 2001 From: Bung Date: Thu, 16 Jul 2020 17:18:59 +0800 Subject: [PATCH 0419/3768] fix #13621, the nim-livereload is mentioned as proposal in #8927 (#14998) --- doc/hcr.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/hcr.rst b/doc/hcr.rst index 567d77178..55ccc06dc 100644 --- a/doc/hcr.rst +++ b/doc/hcr.rst @@ -228,7 +228,6 @@ completion. JavaScript target ================= -Once your code is compiled for hot reloading, the ``nim-livereload`` NPM -package provides a convenient solution for implementing the actual reloading +Once your code is compiled for hot reloading, a convenient solution for implementing the actual reloading in the browser using a framework such as [LiveReload](http://livereload.com/) or [BrowserSync](https://browsersync.io/). From 6b607413e90111f74dd77c1fa31ba5f183f72bdf Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 16 Jul 2020 12:07:06 +0200 Subject: [PATCH 0420/3768] cursor inference: hotfix (#14999) --- compiler/cursor_inference.nim | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/compiler/cursor_inference.nim b/compiler/cursor_inference.nim index e0999b5be..224131270 100644 --- a/compiler/cursor_inference.nim +++ b/compiler/cursor_inference.nim @@ -35,6 +35,7 @@ type mutations: IntSet reassigns: IntSet config: ConfigRef + inAsgnSource: int proc locationRoot(e: PNode; followDotExpr = true): PSym = var n = e @@ -221,7 +222,9 @@ proc analyse(c: var Con; n: PNode) = of nkAsgn, nkFastAsgn: analyse(c, n[0]) + inc c.inAsgnSource analyse(c, n[1]) + dec c.inAsgnSource if n[0].kind == nkSym: if hasDestructor(n[0].typ): @@ -252,14 +255,16 @@ proc analyse(c: var Con; n: PNode) = c.mutations.incl r.id of nkTupleConstr, nkBracket, nkObjConstr: - for i in ord(n.kind == nkObjConstr).. 0: + for i in ord(n.kind == nkObjConstr).. Date: Fri, 17 Jul 2020 16:53:26 +0800 Subject: [PATCH 0421/3768] fix #15006 (#15007) --- lib/system/nimscript.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/system/nimscript.nim b/lib/system/nimscript.nim index bf298e0d4..9e19fcfc4 100644 --- a/lib/system/nimscript.nim +++ b/lib/system/nimscript.nim @@ -142,7 +142,7 @@ template existsFile*(args: varargs[untyped]): untyped {.deprecated: "use fileExi fileExists(args) template existsDir*(args: varargs[untyped]): untyped {.deprecated: "use dirExists".} = - dirExists(dir) + dirExists(args) proc selfExe*(): string = ## Returns the currently running nim or nimble executable. From 1355b461aa70cd1e17b2f07085aa6c97cb54283f Mon Sep 17 00:00:00 2001 From: Clyybber Date: Fri, 17 Jul 2020 10:56:17 +0200 Subject: [PATCH 0422/3768] Show that a variable is cursor in --expandArc (#15002) --- compiler/cursor_inference.nim | 9 ++++----- compiler/injectdestructors.nim | 11 +++++------ compiler/renderer.nim | 2 ++ tests/arc/topt_cursor.nim | 6 +++--- tests/arc/topt_refcursors.nim | 18 +++++++++--------- 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/compiler/cursor_inference.nim b/compiler/cursor_inference.nim index 224131270..94b06ac79 100644 --- a/compiler/cursor_inference.nim +++ b/compiler/cursor_inference.nim @@ -73,7 +73,7 @@ proc cursorId(c: Con; x: PSym): int = if c.cursors[i].s == x: return i return -1 -proc getCursors(c: Con): IntSet = +proc getCursors(c: Con) = #[ Question: if x depends on y and y depends on z then also y depends on z. @@ -86,7 +86,6 @@ proc getCursors(c: Con): IntSet = y.s = "mutate" ]# - result = initIntSet() for cur in c.cursors: if not c.mayOwnData.contains(cur.s.id) and cur.s.typ.skipTypes({tyGenericInst, tyAlias}).kind != tyOwned: @@ -97,7 +96,7 @@ proc getCursors(c: Con): IntSet = #echo "bah, not a cursor ", cur.s, " bad dependency ", d break doAdd when true: - result.incl cur.s.id + cur.s.flags.incl sfCursor when false: echo "computed as a cursor ", cur.s, " ", cur.deps, " ", c.config $ cur.s.info @@ -294,7 +293,7 @@ proc analyse(c: var Con; n: PNode) = else: for child in n: analyse(c, child) -proc computeCursors*(n: PNode; config: ConfigRef): IntSet = +proc computeCursors*(n: PNode; config: ConfigRef) = var c = Con(config: config) analyse(c, n) - result = getCursors c + getCursors c diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 556d098f4..99eefca57 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -35,7 +35,6 @@ type Con = object owner: PSym g: ControlFlowGraph - cursors: IntSet graph: ModuleGraph otherRead: PNode inLoop, inSpawn: int @@ -152,13 +151,13 @@ proc isLastRead(location: PNode; cfg: ControlFlowGraph; otherRead: var PNode; pc proc isCursor(n: PNode; c: Con): bool = case n.kind of nkSym: - result = sfCursor in n.sym.flags or c.cursors.contains(n.sym.id) + sfCursor in n.sym.flags of nkDotExpr: - result = sfCursor in n[1].sym.flags + isCursor(n[1], c) of nkCheckedFieldExpr: - result = isCursor(n[0], c) + isCursor(n[0], c) else: - result = false + false proc isLastRead(n: PNode; c: var Con): bool = # first we need to search for the instruction that belongs to 'n': @@ -1043,7 +1042,7 @@ proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode = echoCfg(c.g) echo n - c.cursors = computeCursors(n, g.config) + computeCursors(n, g.config) var scope: Scope let body = p(n, c, scope, normal) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 3cd532e15..f600d58bf 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -865,6 +865,8 @@ proc gident(g: var TSrcGen, n: PNode) = if localId != 0 and n.sym.magic == mNone: s.add '_' s.addInt localId + if sfCursor in n.sym.flags: + s.add "_cursor" elif n.kind == nkSym and (renderIds in g.flags or sfGenSym in n.sym.flags or n.sym.kind == skTemp): s.add '_' s.addInt n.sym.id diff --git a/tests/arc/topt_cursor.nim b/tests/arc/topt_cursor.nim index 6b923cf76..63f261877 100644 --- a/tests/arc/topt_cursor.nim +++ b/tests/arc/topt_cursor.nim @@ -8,14 +8,14 @@ var :tmpD_1 :tmpD_2 try: - var x = ("hi", 5) - x = if cond: + var x_cursor = ("hi", 5) + x_cursor = if cond: :tmpD = ("different", 54) :tmpD else: :tmpD_1 = ("string here", 80) :tmpD_1 echo [ - :tmpD_2 = `$`(x) + :tmpD_2 = `$`(x_cursor) :tmpD_2] finally: `=destroy`(:tmpD_2) diff --git a/tests/arc/topt_refcursors.nim b/tests/arc/topt_refcursors.nim index 7316e2beb..c8759f121 100644 --- a/tests/arc/topt_refcursors.nim +++ b/tests/arc/topt_refcursors.nim @@ -3,19 +3,19 @@ discard """ cmd: '''nim c --gc:arc --expandArc:traverse --hint:Performance:off $file''' nimout: '''--expandArc: traverse -var it = root +var it_cursor = root block :tmp: while ( - not (it == nil)): - echo [it.s] - it = it.ri -var jt = root + not (it_cursor == nil)): + echo [it_cursor.s] + it_cursor = it_cursor.ri +var jt_cursor = root block :tmp_1: while ( - not (jt == nil)): - let ri_1 = jt.ri - echo [jt.s] - jt = ri_1 + not (jt_cursor == nil)): + let ri_1_cursor = jt_cursor.ri + echo [jt_cursor.s] + jt_cursor = ri_1_cursor -- end of expandArc ------------------------''' """ From c62513049cd7dcbd5b339c2078856a31c498b4aa Mon Sep 17 00:00:00 2001 From: Miran Date: Fri, 17 Jul 2020 10:59:53 +0200 Subject: [PATCH 0423/3768] fix #14082, don't crash on incorrectly formatted input (#14977) [backport] * fix #14082, don't crash on incorrectly formatted input * address code review * remove duplication --- lib/pure/cgi.nim | 44 ++++++++-------------------- lib/pure/includes/decode_helpers.nim | 24 +++++++++++++++ lib/pure/uri.nim | 16 ++++------ 3 files changed, 42 insertions(+), 42 deletions(-) create mode 100644 lib/pure/includes/decode_helpers.nim diff --git a/lib/pure/cgi.nim b/lib/pure/cgi.nim index dd7c4d477..734ab9171 100644 --- a/lib/pure/cgi.nim +++ b/lib/pure/cgi.nim @@ -32,12 +32,7 @@ import strutils, os, strtabs, cookies, uri export uri.encodeUrl, uri.decodeUrl -proc handleHexChar(c: char, x: var int) {.inline.} = - case c - of '0'..'9': x = (x shl 4) or (ord(c) - ord('0')) - of 'a'..'f': x = (x shl 4) or (ord(c) - ord('a') + 10) - of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10) - else: assert(false) +include includes/decode_helpers proc addXmlChar(dest: var string, c: char) {.inline.} = case c @@ -93,40 +88,27 @@ proc getEncodedData(allowedMethods: set[RequestMethod]): string = iterator decodeData*(data: string): tuple[key, value: TaintedString] = ## Reads and decodes CGI data and yields the (name, value) pairs the ## data consists of. + proc parseData(data: string, i: int, field: var string): int = + result = i + while result < data.len: + case data[result] + of '%': add(field, decodePercent(data, result)) + of '+': add(field, ' ') + of '=', '&': break + else: add(field, data[result]) + inc(result) + var i = 0 var name = "" var value = "" # decode everything in one pass: while i < data.len: setLen(name, 0) # reuse memory - while i < data.len: - case data[i] - of '%': - var x = 0 - handleHexChar(data[i+1], x) - handleHexChar(data[i+2], x) - inc(i, 2) - add(name, chr(x)) - of '+': add(name, ' ') - of '=', '&': break - else: add(name, data[i]) - inc(i) + i = parseData(data, i, name) if i >= data.len or data[i] != '=': cgiError("'=' expected") inc(i) # skip '=' setLen(value, 0) # reuse memory - while i < data.len: - case data[i] - of '%': - var x = 0 - if i+2 < data.len: - handleHexChar(data[i+1], x) - handleHexChar(data[i+2], x) - inc(i, 2) - add(value, chr(x)) - of '+': add(value, ' ') - of '&', '\0': break - else: add(value, data[i]) - inc(i) + i = parseData(data, i, value) yield (name.TaintedString, value.TaintedString) if i < data.len: if data[i] == '&': inc(i) diff --git a/lib/pure/includes/decode_helpers.nim b/lib/pure/includes/decode_helpers.nim new file mode 100644 index 000000000..74fe37d07 --- /dev/null +++ b/lib/pure/includes/decode_helpers.nim @@ -0,0 +1,24 @@ +# Include file that implements 'decodePercent' and friends. Do not import it! + +proc handleHexChar(c: char, x: var int, f: var bool) {.inline.} = + case c + of '0'..'9': x = (x shl 4) or (ord(c) - ord('0')) + of 'a'..'f': x = (x shl 4) or (ord(c) - ord('a') + 10) + of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10) + else: f = true + +proc decodePercent(s: string, i: var int): char = + ## Converts `%xx` hexadecimal to the charracter with ordinal number `xx`. + ## + ## If `xx` is not a valid hexadecimal value, it is left intact: only the + ## leading `%` is returned as-is, and `xx` characters will be processed in the + ## next step (e.g. in `uri.decodeUrl`) as regular characters. + result = '%' + if i+2 < s.len: + var x = 0 + var failed = false + handleHexChar(s[i+1], x, failed) + handleHexChar(s[i+2], x, failed) + if not failed: + result = chr(x) + inc(i, 2) diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim index b163a2ab4..04a9d97bd 100644 --- a/lib/pure/uri.nim +++ b/lib/pure/uri.nim @@ -47,6 +47,8 @@ import std/private/since import strutils, parseutils, base64 +include includes/decode_helpers + type Url* = distinct string @@ -90,6 +92,7 @@ proc decodeUrl*(s: string, decodePlus = true): string = ## This means that any ``%xx`` (where ``xx`` denotes a hexadecimal ## value) are converted to the character with ordinal number ``xx``, ## and every other character is carried over. + ## If ``xx`` is not a valid hexadecimal value, it is left intact. ## ## As a special rule, when the value of ``decodePlus`` is true, ``+`` ## characters are converted to a space. @@ -101,12 +104,7 @@ proc decodeUrl*(s: string, decodePlus = true): string = assert decodeUrl("https%3A%2F%2Fnim-lang.org%2Fthis+is+a+test") == "https://nim-lang.org/this is a test" assert decodeUrl("https%3A%2F%2Fnim-lang.org%2Fthis%20is%20a%20test", false) == "https://nim-lang.org/this is a test" - proc handleHexChar(c: char, x: var int) {.inline.} = - case c - of '0'..'9': x = (x shl 4) or (ord(c) - ord('0')) - of 'a'..'f': x = (x shl 4) or (ord(c) - ord('a') + 10) - of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10) - else: assert(false) + assert decodeUrl("abc%xyz") == "abc%xyz" result = newString(s.len) var i = 0 @@ -114,11 +112,7 @@ proc decodeUrl*(s: string, decodePlus = true): string = while i < s.len: case s[i] of '%': - var x = 0 - handleHexChar(s[i+1], x) - handleHexChar(s[i+2], x) - inc(i, 2) - result[j] = chr(x) + result[j] = decodePercent(s, i) of '+': if decodePlus: result[j] = ' ' From c2f80de1c74c673ab9d3459d1f86fb9fe4366f61 Mon Sep 17 00:00:00 2001 From: Bung Date: Fri, 17 Jul 2020 17:01:47 +0800 Subject: [PATCH 0424/3768] fix #14822 copy test into var in matrix process, so can reset startTime before actully run (#15000) * TTest pass by ref , so can reset startTime before actully run * change TTest to ref type * clone test in matrix process --- testament/testament.nim | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/testament/testament.nim b/testament/testament.nim index 109ce4ed9..baa340139 100644 --- a/testament/testament.nim +++ b/testament/testament.nim @@ -58,7 +58,6 @@ type TResults = object total, passed, skipped: int data: string - TTest = object name: string cat: Category @@ -425,8 +424,9 @@ proc checkDisabled(r: var TResults, test: TTest): bool = var count = 0 -proc testSpecHelper(r: var TResults, test: TTest, expected: TSpec, +proc testSpecHelper(r: var TResults, test: var TTest, expected: TSpec, target: TTarget, nimcache: string, extraOptions = "") = + test.startTime = epochTime() case expected.action of actionCompile: var given = callCompiler(expected.getCmd, test.name, test.options, nimcache, target, @@ -496,7 +496,8 @@ proc targetHelper(r: var TResults, test: TTest, expected: TSpec, extraOptions = echo "testSpec count: ", count, " expected: ", expected else: let nimcache = nimcacheDir(test.name, test.options, target) - testSpecHelper(r, test, expected, target, nimcache, extraOptions) + var testClone = test + testSpecHelper(r, testClone, expected, target, nimcache, extraOptions) proc testSpec(r: var TResults, test: TTest, targets: set[TTarget] = {}) = var expected = test.spec @@ -521,7 +522,8 @@ proc testSpecWithNimcache(r: var TResults, test: TTest; nimcache: string) = if not checkDisabled(r, test): return for target in test.spec.targets: inc(r.total) - testSpecHelper(r, test, test.spec, target, nimcache) + var testClone = test + testSpecHelper(r, testClone, test.spec, target, nimcache) proc testC(r: var TResults, test: TTest, action: TTestAction) = # runs C code. Doesn't support any specs, just goes by exit code. From d4984e069adc1984c2f67d073ebb7c42873c4d08 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 17 Jul 2020 15:24:36 +0200 Subject: [PATCH 0425/3768] arc: cursors for simple for loop variables (#15008) * arc: cursors for simple for loop variables * merged devel --- compiler/injectdestructors.nim | 2 +- compiler/transf.nim | 24 ++++++++++++++++++-- tests/arc/topt_cursor.nim | 27 ++++++++++++++++++++++- tests/arc/topt_wasmoved_destroy_pairs.nim | 6 ++--- 4 files changed, 52 insertions(+), 7 deletions(-) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 99eefca57..752438180 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -1062,5 +1062,5 @@ proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode = if g.config.arcToExpand.hasKey(owner.name.s): echo "--expandArc: ", owner.name.s - echo renderTree(result, {renderIr}) + echo renderTree(result, {renderIr, renderNoComments}) echo "-- end of expandArc ------------------------" diff --git a/compiler/transf.nim b/compiler/transf.nim index be559abb8..7aa18bf52 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -350,7 +350,7 @@ proc transformYield(c: PTransf, n: PNode): PNode = # Choose the right assignment instruction according to the given ``lhs`` # node since it may not be a nkSym (a stack-allocated skForVar) but a # nkDotExpr (a heap-allocated slot into the envP block) - case lhs.kind: + case lhs.kind of nkSym: internalAssert c.graph.config, lhs.sym.kind == skForVar result = newAsgnStmt(c, nkFastAsgn, lhs, rhs) @@ -592,6 +592,22 @@ proc findWrongOwners(c: PTransf, n: PNode) = else: for i in 0.. Date: Sat, 18 Jul 2020 09:21:09 +0100 Subject: [PATCH 0426/3768] fix odbc regressions (#15009) [backport] * fix odbc regressions * make only necessary changes --- lib/wrappers/odbcsql.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/wrappers/odbcsql.nim b/lib/wrappers/odbcsql.nim index 393f29ad0..7836f087d 100644 --- a/lib/wrappers/odbcsql.nim +++ b/lib/wrappers/odbcsql.nim @@ -47,8 +47,8 @@ type SqlHDesc* = SqlHandle TSqlInteger* = int32 SqlUInteger* = int32 - TSqlLen* = int64 - TSqlULen* = uint64 + TSqlLen* = int + TSqlULen* = uint SqlPointer* = pointer TSqlReal* = cfloat TSqlDouble* = cdouble From c983466c15f1807c66f4c2f07ba348b0f95584b3 Mon Sep 17 00:00:00 2001 From: Tomohiro Date: Sat, 18 Jul 2020 17:41:33 +0900 Subject: [PATCH 0427/3768] Fix #14906 (#14949) * Fix #14906 by wrapping outputStream with PipeOutStream * Fix compile error when ./build_all.sh * Use PipeOutStream on posix * Fix compile error when build_all.sh * Use ptr UncheckedArray * Replace copyRefObj * Remove tmp buffer from posPeekData * Add more tests for outputStream * Add comments about PipeOutStream.buffer * Fix bug in posReadLine * Move implementation of newPipeOutStream to streamwrapper module --- lib/pure/osproc.nim | 71 +++++++++++++++++++--- lib/pure/streamwrapper.nim | 117 +++++++++++++++++++++++++++++++++++++ tests/stdlib/tosproc.nim | 56 +++++++++++++++++- 3 files changed, 235 insertions(+), 9 deletions(-) create mode 100644 lib/pure/streamwrapper.nim diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 8be914992..5113695d8 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -18,7 +18,8 @@ include "system/inclrtl" import - strutils, os, strtabs, streams, cpuinfo + strutils, os, strtabs, streams, cpuinfo, streamwrapper, + std/private/since export quoteShell, quoteShellWindows, quoteShellPosix @@ -237,6 +238,10 @@ proc inputStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [].} proc outputStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [].} ## Returns ``p``'s output stream for reading from. ## + ## You cannot perform peek/write/setOption operations to this stream. + ## Use `peekableOutputStream proc <#peekableOutputStream,Process>`_ + ## if you need to peek stream. + ## ## **WARNING**: The returned `Stream` should not be closed manually as it ## is closed when closing the Process ``p``. ## @@ -247,6 +252,10 @@ proc outputStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [].} proc errorStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [].} ## Returns ``p``'s error stream for reading from. ## + ## You cannot perform peek/write/setOption operations to this stream. + ## Use `peekableErrorStream proc <#peekableErrorStream,Process>`_ + ## if you need to peek stream. + ## ## **WARNING**: The returned `Stream` should not be closed manually as it ## is closed when closing the Process ``p``. ## @@ -254,6 +263,30 @@ proc errorStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [].} ## * `inputStream proc <#inputStream,Process>`_ ## * `outputStream proc <#outputStream,Process>`_ +proc peekableOutputStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [], since: (1, 3).} + ## Returns ``p``'s output stream for reading from. + ## + ## You can peek returned stream. + ## + ## **WARNING**: The returned `Stream` should not be closed manually as it + ## is closed when closing the Process ``p``. + ## + ## See also: + ## * `outputStream proc <#outputStream,Process>`_ + ## * `peekableErrorStream proc <#peekableErrorStream,Process>`_ + +proc peekableErrorStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [], since: (1, 3).} + ## Returns ``p``'s error stream for reading from. + ## + ## You can run peek operation to returned stream. + ## + ## **WARNING**: The returned `Stream` should not be closed manually as it + ## is closed when closing the Process ``p``. + ## + ## See also: + ## * `errorStream proc <#errorStream,Process>`_ + ## * `peekableOutputStream proc <#peekableOutputStream,Process>`_ + proc inputHandle*(p: Process): FileHandle {.rtl, extern: "nosp$1", tags: [].} = ## Returns ``p``'s input file handle for writing to. @@ -737,6 +770,18 @@ when defined(Windows) and not defined(useNimRtl): p.errStream = newFileHandleStream(p.errHandle) result = p.errStream + proc peekableOutputStream(p: Process): Stream = + streamAccess(p) + if p.outStream == nil: + p.outStream = newFileHandleStream(p.outHandle).newPipeOutStream + result = p.outStream + + proc peekableErrorStream(p: Process): Stream = + streamAccess(p) + if p.errStream == nil: + p.errStream = newFileHandleStream(p.errHandle).newPipeOutStream + result = p.errStream + proc execCmd(command: string): int = var si: STARTUPINFO @@ -1360,28 +1405,40 @@ elif not defined(useNimRtl): p.exitStatus = status result = exitStatusLikeShell(status) - proc createStream(stream: var owned(Stream), handle: var FileHandle, - fileMode: FileMode) = + proc createStream(handle: var FileHandle, + fileMode: FileMode): owned FileStream = var f: File if not open(f, handle, fileMode): raiseOSError(osLastError()) - stream = newFileStream(f) + return newFileStream(f) proc inputStream(p: Process): Stream = streamAccess(p) if p.inStream == nil: - createStream(p.inStream, p.inHandle, fmWrite) + p.inStream = createStream(p.inHandle, fmWrite) return p.inStream proc outputStream(p: Process): Stream = streamAccess(p) if p.outStream == nil: - createStream(p.outStream, p.outHandle, fmRead) + p.outStream = createStream(p.outHandle, fmRead) return p.outStream proc errorStream(p: Process): Stream = streamAccess(p) if p.errStream == nil: - createStream(p.errStream, p.errHandle, fmRead) + p.errStream = createStream(p.errHandle, fmRead) + return p.errStream + + proc peekableOutputStream(p: Process): Stream = + streamAccess(p) + if p.outStream == nil: + p.outStream = createStream(p.outHandle, fmRead).newPipeOutStream + return p.outStream + + proc peekableErrorStream(p: Process): Stream = + streamAccess(p) + if p.errStream == nil: + p.errStream = createStream(p.errHandle, fmRead).newPipeOutStream return p.errStream proc csystem(cmd: cstring): cint {.nodecl, importc: "system", diff --git a/lib/pure/streamwrapper.nim b/lib/pure/streamwrapper.nim new file mode 100644 index 000000000..b99982f1b --- /dev/null +++ b/lib/pure/streamwrapper.nim @@ -0,0 +1,117 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2020 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module implements stream wrapper. +## +## **Since** version 1.2. + +import deques, streams + +type + PipeOutStream*[T] = ref object of T + # When stream peek operation is called, it reads from base stream + # type using `baseReadDataImpl` and stores the content to this buffer. + # Next stream read operation returns data in the buffer so that previus peek + # operation looks like didn't changed read positon. + # When stream read operation that returns N byte data is called and the size is smaller than buffer size, + # first N elements are removed from buffer. + # Deque type can do such operation more efficiently than seq type. + buffer: Deque[char] + baseReadLineImpl: typeof(StreamObj.readLineImpl) + baseReadDataImpl: typeof(StreamObj.readDataImpl) + +proc posReadLine[T](s: Stream, line: var TaintedString): bool = + var s = PipeOutStream[T](s) + assert s.baseReadLineImpl != nil + + let n = s.buffer.len + line.string.setLen(0) + for i in 0.. 0 + line.string.add(c) + + var line2: string + result = s.baseReadLineImpl(s, line2) + line.add line2 + +proc posReadData[T](s: Stream, buffer: pointer, bufLen: int): int = + var s = PipeOutStream[T](s) + assert s.baseReadDataImpl != nil + + let + dest = cast[ptr UncheckedArray[char]](buffer) + n = min(s.buffer.len, bufLen) + result = n + for i in 0.. n: + result += s.baseReadDataImpl(s, addr dest[n], bufLen - n) + +proc posReadDataStr[T](s: Stream, buffer: var string, slice: Slice[int]): int = + posReadData[T](s, addr buffer[slice.a], slice.len) + +proc posPeekData[T](s: Stream, buffer: pointer, bufLen: int): int = + var s = PipeOutStream[T](s) + assert s.baseReadDataImpl != nil + + let + dest = cast[ptr UncheckedArray[char]](buffer) + n = min(s.buffer.len, bufLen) + + result = n + for i in 0.. n: + let + newDataNeeded = bufLen - n + numRead = s.baseReadDataImpl(s, addr dest[n], newDataNeeded) + result += numRead + for i in 0.. Date: Sat, 18 Jul 2020 01:49:36 -0700 Subject: [PATCH 0428/3768] enable,document,test getImplTransformed, very useful for understanding how nim transforms code (#14924) * enable,document,test getImplTransformed, very useful for understanding how nim transforms code --- lib/core/macros.nim | 7 +++++-- tests/macros/tmacros_various.nim | 20 ++++++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 7b03d4e4d..e4a56d6f9 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -277,9 +277,12 @@ else: # bootstrapping substitute {.pop.} -when defined(nimSymImplTransform): +when (NimMajor, NimMinor, NimPatch) >= (1, 3, 5) or defined(nimSymImplTransform): proc getImplTransformed*(symbol: NimNode): NimNode {.magic: "GetImplTransf", noSideEffect.} - ## For a typed proc returns the AST after transformation pass. + ## For a typed proc returns the AST after transformation pass; this is useful + ## for debugging how the compiler transforms code (eg: `defer`, `for`) but + ## note that code transformations are implementation dependent and subject to change. + ## See an example in `tests/macros/tmacros_various.nim`. when defined(nimHasSymOwnerInMacro): proc owner*(sym: NimNode): NimNode {.magic: "SymOwner", noSideEffect.} diff --git a/tests/macros/tmacros_various.nim b/tests/macros/tmacros_various.nim index 533db25e1..3cb5aaa8f 100644 --- a/tests/macros/tmacros_various.nim +++ b/tests/macros/tmacros_various.nim @@ -202,3 +202,23 @@ block tupleNewLitTests: # this `$` test is needed because tuple equality doesn't distinguish # between named vs unnamed tuples doAssert t() == (1, "foo", (), (1, ), (a1: 'x', a2: @["ba"])) + +from strutils import contains +block getImplTransformed: + macro bar(a: typed): string = + # newLit a.getImpl.repr # this would be before code transformation + let b = a.getImplTransformed + newLit b.repr + template toExpand() = + for ai in 0..2: echo ai + proc baz(a=1): int = + defer: discard + toExpand() + 12 + const code = bar(baz) + # sanity check: + doAssert "finally" in code # `defer` is lowered to try/finally + doAssert "while" in code # `for` is lowered to `while` + doAssert "toExpand" notin code + # template is expanded (but that would already be the case with + # `a.getImpl.repr`, unlike the other transformations mentioned above From ae358a0c7a5f7b14e5dde259cd6ce02b235985a3 Mon Sep 17 00:00:00 2001 From: Andy Davidoff Date: Sat, 18 Jul 2020 10:39:41 -0400 Subject: [PATCH 0429/3768] fix docs for nativesocket read/write selects (#15010) The sockets which *cannot* be _[read from or written to]_ will also be removed from ``readfds``. --- lib/pure/nativesockets.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim index 35a7396b9..49a1192df 100644 --- a/lib/pure/nativesockets.nim +++ b/lib/pure/nativesockets.nim @@ -656,7 +656,7 @@ proc pruneSocketSet(s: var seq[SocketHandle], fd: var TFdSet) = proc selectRead*(readfds: var seq[SocketHandle], timeout = 500): int = ## When a socket in ``readfds`` is ready to be read from then a non-zero ## value will be returned specifying the count of the sockets which can be - ## read from. The sockets which can be read from will also be removed + ## read from. The sockets which cannot be read from will also be removed ## from ``readfds``. ## ## ``timeout`` is specified in milliseconds and ``-1`` can be specified for @@ -678,7 +678,7 @@ proc selectWrite*(writefds: var seq[SocketHandle], timeout = 500): int {.tags: [ReadIOEffect].} = ## When a socket in ``writefds`` is ready to be written to then a non-zero ## value will be returned specifying the count of the sockets which can be - ## written to. The sockets which can be written to will also be removed + ## written to. The sockets which cannot be written to will also be removed ## from ``writefds``. ## ## ``timeout`` is specified in milliseconds and ``-1`` can be specified for From ffe7b3a9bf1596e5016c8fcaf775f9261188d349 Mon Sep 17 00:00:00 2001 From: Araq Date: Sat, 18 Jul 2020 21:40:44 +0200 Subject: [PATCH 0430/3768] threadpool.nim: minor code style changes --- lib/pure/concurrency/threadpool.nim | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/pure/concurrency/threadpool.nim b/lib/pure/concurrency/threadpool.nim index 2abcafb80..f0269f526 100644 --- a/lib/pure/concurrency/threadpool.nim +++ b/lib/pure/concurrency/threadpool.nim @@ -208,7 +208,7 @@ proc finished(fv: var FlowVarBaseObj) = # the worker thread waits for "data" to be set to nil before shutting down owner.data = nil -proc `=destroy`[T](fv: var FlowVarObj[T]) = +proc `=destroy`[T](fv: var FlowVarObj[T]) = finished(fv) `=destroy`(fv.blob) @@ -321,15 +321,15 @@ var currentPoolSize: int maxPoolSize = MaxThreadPoolSize minPoolSize = 4 - gSomeReady : Semaphore + gSomeReady: Semaphore readyWorker: ptr Worker # A workaround for recursion deadlock issue # https://github.com/nim-lang/Nim/issues/4597 var numSlavesLock: Lock - numSlavesRunning {.guard: numSlavesLock}: int - numSlavesWaiting {.guard: numSlavesLock}: int + numSlavesRunning {.guard: numSlavesLock.}: int + numSlavesWaiting {.guard: numSlavesLock.}: int isSlave {.threadvar.}: bool numSlavesLock.initLock @@ -464,7 +464,7 @@ proc pinnedSpawn*(id: ThreadId; call: sink typed): void {.magic: "Spawn".} ## ``call`` has to be proc call ``p(...)`` where ``p`` is gcsafe and has a ## return type that is either ``void`` or compatible with ``FlowVar[T]``. -template spawnX*(call): void = +template spawnX*(call) = ## Spawns a new task if a CPU core is ready, otherwise executes the ## call in the calling thread. ## From 1e3a0ef1e1983f2e493f8f42afbb0ae3fbc4fd11 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sun, 19 Jul 2020 07:41:36 +0100 Subject: [PATCH 0431/3768] [Backport] Fixes callbacks being dropped on Linux/macOS/BSD. (#15012) Fixes #15003. This is a serious bug which occurs when data cannot be read/sent immediately and there are a bunch of other read/write events pending. What happens is that the new events are dropped which results in the case of the reported bug resulted in some data not being sent (!). --- lib/pure/asyncdispatch.nim | 7 +++- tests/async/tasynceagain.nim | 67 ++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 tests/async/tasynceagain.nim diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 2ade9065f..6330a6ba9 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -1228,14 +1228,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. diff --git a/tests/async/tasynceagain.nim b/tests/async/tasynceagain.nim new file mode 100644 index 000000000..aebd4ef16 --- /dev/null +++ b/tests/async/tasynceagain.nim @@ -0,0 +1,67 @@ +discard """ + disabled: "windows" + exitcode: 0 +""" +# AsyncSocketBug.nim +# Jens Alfke (@snej) -- 16 July 2020 +# Demonstrates data loss by Nim's AsyncSocket. +# Just run it, and it will raise an assertion failure within a minute. + +import asyncdispatch, asyncnet, strformat, strutils, sugar + +const FrameSize = 9999 # Exact size not important, but larger sizes fail quicker + +proc runServer() {.async.} = + # Server side: + var server = newAsyncSocket() + server.bindAddr(Port(9001)) + server.listen() + let client = await server.accept() + echo "Server got client connection" + var lastN = 0 + while true: + let frame = await client.recv(FrameSize) + assert frame.len == FrameSize + let n = frame[0..<6].parseInt() + echo "RCVD #", n, ": ", frame[0..80], "..." + if n != lastN + 1: + echo &"******** ERROR: Server received #{n}, but last was #{lastN}!" + assert n == lastN + 1 + lastN = n + await sleepAsync 100 + + +proc main() {.async.} = + asyncCheck runServer() + + # Client side: + let socket = newAsyncSocket(buffered = false) + await socket.connect("localhost", Port(9001)) + echo "Client socket connected" + + var sentCount = 0 + var completedCount = 0 + + while sentCount < 2000: + sentCount += 1 + let n = sentCount + + var message = &"{n:06} This is message #{n} of ∞. Please stay tuned for more. " + #echo ">>> ", message + while message.len < FrameSize: + message = message & message + let frame = message[0.. Date: Mon, 20 Jul 2020 05:44:06 +0200 Subject: [PATCH 0432/3768] enable 'nimterop' test --- testament/important_packages.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 58f07b017..3d5436c84 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -86,7 +86,7 @@ pkg2 "nimpy", false, "nim c -r tests/nimfrompy.nim" pkg2 "nimquery" pkg2 "nimsl", true pkg2 "nimsvg" -# pkg2 "nimterop", true +pkg2 "nimterop", true, "nimble minitest" pkg2 "nimwc", true, "nim c nimwc.nim" # pkg2 "nimx", true, "nim c --threads:on test/main.nim" # pkg2 "nitter", true, "nim c src/nitter.nim", "https://github.com/zedeus/nitter" From bb1adf6a706190883fa57a0208ba8e3118235256 Mon Sep 17 00:00:00 2001 From: alaviss Date: Mon, 20 Jul 2020 04:17:33 +0000 Subject: [PATCH 0433/3768] io: fix SetHandleInformation signature to match Windows' (#15017) * io: fix SetHandleInformation signature to match Windows' Fixes #14980 * rename Handle -> IoHandle because system.nim is a mess --- lib/system/io.nim | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/system/io.nim b/lib/system/io.nim index 482057214..0b624de8f 100644 --- a/lib/system/io.nim +++ b/lib/system/io.nim @@ -282,7 +282,11 @@ elif defined(windows): proc getOsfhandle(fd: cint): int {. importc: "_get_osfhandle", header: "".} - proc setHandleInformation(handle: int, mask, flags: culong): cint {. + type + IoHandle = distinct pointer + ## Windows' HANDLE type. Defined as an untyped pointer but is **not** + ## one. Named like this to avoid collision with other `system` modules. + proc setHandleInformation(handle: IoHandle, mask, flags: culong): cint {. importc: "SetHandleInformation", header: "".} const @@ -339,7 +343,8 @@ when defined(nimdoc) or (defined(posix) and not defined(nimscript)) or defined(w flags = if inheritable: flags and not FD_CLOEXEC else: flags or FD_CLOEXEC result = c_fcntl(f, F_SETFD, flags) != -1 else: - result = setHandleInformation(f.int, HANDLE_FLAG_INHERIT, culong inheritable) != 0 + result = setHandleInformation(cast[IoHandle](f), HANDLE_FLAG_INHERIT, + culong inheritable) != 0 proc readLine*(f: File, line: var TaintedString): bool {.tags: [ReadIOEffect], benign.} = From 71dd5f85b9a13d97ea0c74338722bf08a9ae6286 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 20 Jul 2020 07:50:19 +0200 Subject: [PATCH 0434/3768] 'isolate' builtin; refs https://github.com/nim-lang/RFCs/issues/244 (#15011) --- compiler/ast.nim | 2 +- compiler/ccgexprs.nim | 2 +- compiler/isolation_check.nim | 117 +++++++++++++++++++++++++++++++++++ compiler/jsgen.nim | 2 +- compiler/sem.nim | 3 +- compiler/semmagic.nim | 5 +- compiler/vmgen.nim | 2 +- lib/std/isolation.nim | 31 ++++++++++ tests/isolate/tisolate.nim | 36 +++++++++++ 9 files changed, 194 insertions(+), 6 deletions(-) create mode 100644 compiler/isolation_check.nim create mode 100644 lib/std/isolation.nim create mode 100644 tests/isolate/tisolate.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index b57ee542d..d8fe6b1c0 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -658,7 +658,7 @@ type mSwap, mIsNil, mArrToSeq, mNewString, mNewStringOfCap, mParseBiggestFloat, mMove, mWasMoved, mDestroy, - mDefault, mUnown, mAccessEnv, mReset, + mDefault, mUnown, mIsolate, mAccessEnv, mReset, mArray, mOpenArray, mRange, mSet, mSeq, mVarargs, mRef, mPtr, mVar, mDistinct, mVoid, mTuple, mOrdinal, diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index dc9617fe2..003b3e240 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2209,7 +2209,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mCharToStr: genDollar(p, e, d, "#nimCharToStr($1)") of mFloatToStr: genDollar(p, e, d, "#nimFloatToStr($1)") of mCStrToStr: genDollar(p, e, d, "#cstrToNimstr($1)") - of mStrToStr, mUnown: expr(p, e[1], d) + of mStrToStr, mUnown, mIsolate: expr(p, e[1], d) of mEnumToStr: if optTinyRtti in p.config.globalOptions: genEnumToStr(p, e, d) diff --git a/compiler/isolation_check.nim b/compiler/isolation_check.nim new file mode 100644 index 000000000..a77e8c97a --- /dev/null +++ b/compiler/isolation_check.nim @@ -0,0 +1,117 @@ +# +# +# The Nim Compiler +# (c) Copyright 2020 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Implementation of the check that `recover` needs, see +## https://github.com/nim-lang/RFCs/issues/244 for more details. + +import + ast, types, renderer, idents, intsets, options, msgs + +proc canAlias(arg, ret: PType; marker: var IntSet): bool + +proc canAliasN(arg: PType; n: PNode; marker: var IntSet): bool = + case n.kind + of nkRecList: + for i in 0.. 0 and ret[0] != nil: + result = canAlias(arg, ret[0], marker) + else: + result = true + of tyTuple: + for i in 0.. 0: + result = checkIsolate(n[^1]) + else: + result = false + else: + # unanalysable expression: + result = false + else: + # no ref, no cry: + result = true + diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 57a6b8094..d876531e9 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -653,7 +653,7 @@ proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = useMagic(p, "nimFloatToString") applyFormat "cstrToNimstr(nimFloatToString($1))" of mCStrToStr: applyFormat("cstrToNimstr($1)", "cstrToNimstr($1)") - of mStrToStr, mUnown: applyFormat("$1", "$1") + of mStrToStr, mUnown, mIsolate: applyFormat("$1", "$1") else: assert false, $op diff --git a/compiler/sem.nim b/compiler/sem.nim index a657939a5..1f98b4f87 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -16,7 +16,8 @@ import procfind, lookups, pragmas, passes, semdata, semtypinst, sigmatch, intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting, evaltempl, patterns, parampatterns, sempass2, linter, semmacrosanity, - lowerings, plugins/active, rod, lineinfos, strtabs, int128 + lowerings, plugins/active, rod, lineinfos, strtabs, int128, + isolation_check from modulegraphs import ModuleGraph, PPassContext, onUse, onDef, onDefResolveForward diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index e7a964d81..6aa440aa9 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -558,6 +558,9 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, let constructed = result[1].typ.base if constructed.requiresInit: message(c.config, n.info, warnUnsafeDefault, typeToString(constructed)) + of mIsolate: + if not checkIsolate(n[1]): + localError(c.config, n.info, "expression cannot be isolated: " & $n[1]) + result = n else: result = n - diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 52b7b6eb3..4ef510d20 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -997,7 +997,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = c.genNarrow(n[1], d) c.genAsgnPatch(n[1], d) c.freeTemp(d) - of mOrd, mChr, mArrToSeq, mUnown: c.gen(n[1], dest) + of mOrd, mChr, mArrToSeq, mUnown, mIsolate: c.gen(n[1], dest) of mNew, mNewFinalize: unused(c, n, dest) c.genNew(n) diff --git a/lib/std/isolation.nim b/lib/std/isolation.nim new file mode 100644 index 000000000..7ca5f0080 --- /dev/null +++ b/lib/std/isolation.nim @@ -0,0 +1,31 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2020 Nim contributors +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module implements the `Isolated[T]` type for +## safe construction of isolated subgraphs that can be +## passed efficiently to different channels and threads. + +type + Isolated*[T] = object ## Isolated data can only be moved, not copied. + value: T + +proc `=`*[T](dest: var Isolated[T]; src: Isolated[T]) {.error.} + +proc `=sink`*[T](dest: var Isolated[T]; src: Isolated[T]) {.inline.} = + # delegate to value's sink operation + `=sink`(dest.value, src.value) + +proc `=destroy`*[T](dest: var Isolated[T]) {.inline.} = + # delegate to value's destroy operation + `=destroy`(dest.value) + +func isolate*[T](value: sink T): Isolated[T] {.magic: "Isolate".} + ## Create an isolated subgraph from the expression `value`. + ## Please read https://github.com/nim-lang/RFCs/issues/244 + ## for more details. diff --git a/tests/isolate/tisolate.nim b/tests/isolate/tisolate.nim new file mode 100644 index 000000000..9f3b15441 --- /dev/null +++ b/tests/isolate/tisolate.nim @@ -0,0 +1,36 @@ +discard """ + errormsg: "expression cannot be isolated: select(a, b)" + line: 34 +""" + +import std / isolation + +import json, streams + +proc f(): seq[int] = + @[1, 2, 3] + +type + Node = ref object + x: string + +proc g(): Node = nil + +proc select(a, b: Node): Node = + a + +proc main = + discard isolate f() + + + discard isolate g() + + discard isolate select(Node(x: "a"), nil) + discard isolate select(Node(x: "a"), Node(x: "b")) + + discard isolate parseJson(newFileStream("my.json"), "my.json") + + var a, b: Node + discard isolate select(a, b) + +main() From 9a76398ec6405dab88512fbc891b809ec2dd0325 Mon Sep 17 00:00:00 2001 From: alaviss Date: Mon, 20 Jul 2020 06:02:48 +0000 Subject: [PATCH 0435/3768] koch: use in-tree Nim to run test if possible (#15018) Fixes #15013 --- koch.nim | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/koch.nim b/koch.nim index 95c54892f..7dedb039a 100644 --- a/koch.nim +++ b/koch.nim @@ -440,8 +440,10 @@ template `|`(a, b): string = (if a.len > 0: a else: b) proc tests(args: string) = nimexec "cc --opt:speed testament/testament" - let tester = quoteShell(getCurrentDir() / "testament/testament".exe) - let success = tryExec tester & " " & (args|"all") + var testCmd = quoteShell(getCurrentDir() / "testament/testament".exe) + testCmd.add " " & quoteShell("--nim:" & findNim()) + testCmd.add " " & (args|"all") + let success = tryExec testCmd if not success: quit("tests failed", QuitFailure) From 49d4b50fe52f0e961ef367a44be8b1bd79994a03 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 20 Jul 2020 14:28:00 +0200 Subject: [PATCH 0436/3768] fixes #14194 (#15023) --- compiler/extccomp.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 7d5df0906..1c6b1a8d0 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -429,7 +429,6 @@ proc noAbsolutePaths(conf: ConfigRef): bool {.inline.} = proc cFileSpecificOptions(conf: ConfigRef; nimname, fullNimFile: string): string = result = conf.compileOptions - addOpt(result, conf.cfileSpecificOptions.getOrDefault(fullNimFile)) for option in conf.compileOptionsCmd: if strutils.find(result, option, 0) < 0: @@ -450,6 +449,8 @@ proc cFileSpecificOptions(conf: ConfigRef; nimname, fullNimFile: string): string let key = nimname & ".always" if existsConfigVar(conf, key): addOpt(result, getConfigVar(conf, key)) + addOpt(result, conf.cfileSpecificOptions.getOrDefault(fullNimFile)) + proc getCompileOptions(conf: ConfigRef): string = result = cFileSpecificOptions(conf, "__dummy__", "__dummy__") From 80d3ef995282ec2e37968feedf7914ade6f2e62f Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 20 Jul 2020 17:44:35 +0200 Subject: [PATCH 0437/3768] hotfix: firstOrd/lastOrd for 'tyLent' as it shows up in strange places, as usual --- compiler/types.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/types.nim b/compiler/types.nim index 1f787a4e5..506e63478 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -729,7 +729,7 @@ proc firstOrd*(conf: ConfigRef; t: PType): Int128 = assert(t.n[0].kind == nkSym) result = toInt128(t.n[0].sym.position) of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink, - tyStatic, tyInferred, tyUserTypeClasses: + tyStatic, tyInferred, tyUserTypeClasses, tyLent: result = firstOrd(conf, lastSon(t)) of tyOrdinal: if t.len > 0: result = firstOrd(conf, lastSon(t)) @@ -786,7 +786,7 @@ proc lastOrd*(conf: ConfigRef; t: PType): Int128 = assert(t.n[^1].kind == nkSym) result = toInt128(t.n[^1].sym.position) of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink, - tyStatic, tyInferred, tyUserTypeClasses: + tyStatic, tyInferred, tyUserTypeClasses, tyLent: result = lastOrd(conf, lastSon(t)) of tyProxy: result = Zero of tyOrdinal: From 3a4e4cec4d9017705bea1b496e8a2b81776ca34f Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 20 Jul 2020 18:37:14 +0200 Subject: [PATCH 0438/3768] cursor inference bugfix --- compiler/cursor_inference.nim | 5 +-- tests/arc/topt_no_cursor.nim | 64 +++++++++++++++++++++++++++++++++-- 2 files changed, 65 insertions(+), 4 deletions(-) diff --git a/compiler/cursor_inference.nim b/compiler/cursor_inference.nim index 94b06ac79..b26a29d4c 100644 --- a/compiler/cursor_inference.nim +++ b/compiler/cursor_inference.nim @@ -237,10 +237,11 @@ proc analyse(c: var Con; n: PNode) = # assignments like 'x.field = value' mean that 'x' itself cannot # be a cursor: let r = locationRoot(n[0]) - if r != nil and r.typ.skipTypes(abstractInst).kind notin {tyPtr, tyRef}: + if r != nil: # however, an assignment like 'it.field = x' does not influence r's # cursorness property: - c.mayOwnData.incl r.id + if r.typ.skipTypes(abstractInst).kind notin {tyPtr, tyRef}: + c.mayOwnData.incl r.id c.mutations.incl r.id if hasDestructor(n[1].typ): diff --git a/tests/arc/topt_no_cursor.nim b/tests/arc/topt_no_cursor.nim index 3d3262491..e50418671 100644 --- a/tests/arc/topt_no_cursor.nim +++ b/tests/arc/topt_no_cursor.nim @@ -1,6 +1,9 @@ discard """ - output: '''(repo: "", package: "meo", ext: "")''' - cmd: '''nim c --gc:arc --expandArc:newTarget --hint:Performance:off $file''' + output: '''(repo: "", package: "meo", ext: "") +doing shady stuff... +3 +6''' + cmd: '''nim c --gc:arc --expandArc:newTarget --expandArc:delete --hint:Performance:off $file''' nimout: '''--expandArc: newTarget var @@ -23,6 +26,14 @@ result = ( let blitTmp_2 = :tmp_2 blitTmp_2) `=destroy`(splat) +-- end of expandArc ------------------------ +--expandArc: delete + +var saved +var sibling_cursor = target.parent.left +`=`(saved, sibling_cursor.right) +`=`(sibling_cursor.right, saved.left) +`=sink`(sibling_cursor.parent, saved) -- end of expandArc ------------------------''' """ @@ -35,3 +46,52 @@ proc newTarget*(path: string): Target = result = (repo: splat.dir, package: splat.name, ext: splat.ext) echo newTarget("meo") + +type + Node = ref object + left, right, parent: Node + value: int + +proc delete(target: var Node) = + var sibling = target.parent.left # b3 + var saved = sibling.right # b3.right -> r4 + + sibling.right = saved.left # b3.right -> r4.left = nil + sibling.parent = saved # b3.parent -> r5 = r4 + + #[after this proc: + b 5 + / \ + b 3 b 6 + ]# + + +#[before: + r 5 + / \ + b 3 b 6 - to delete + / \ +empty r 4 +]# +proc main = + var five = Node(value: 5) + + var six = Node(value: 6) + six.parent = five + five.right = six + + var three = Node(value: 3) + three.parent = five + five.left = three + + var four = Node(value: 4) + four.parent = three + three.right = four + + echo "doing shady stuff..." + delete(six) + # need both of these echos + echo five.left.value + echo five.right.value + +main() From 64d629c617b5bbc700349afbc16607e2ed9f8eeb Mon Sep 17 00:00:00 2001 From: Bung Date: Wed, 22 Jul 2020 01:11:12 +0800 Subject: [PATCH 0439/3768] Shadow Dom apis (#14979) * shadow dom api * fix typos * host to Element type * fix code style * move elementsFromPoint to dom_extensions.nim --- lib/js/dom.nim | 32 ++++++++++++++++++++++++++++++++ lib/js/dom_extensions.nim | 5 +++++ 2 files changed, 37 insertions(+) create mode 100644 lib/js/dom_extensions.nim diff --git a/lib/js/dom.nim b/lib/js/dom.nim index be676a813..84ceb73f3 100644 --- a/lib/js/dom.nim +++ b/lib/js/dom.nim @@ -1324,6 +1324,26 @@ since (1, 3): FileReaderObj {.importc.} = object of EventTargetObj FileReaderState* = distinct range[0'u16..2'u16] + RootNodeOptions* = object of RootObj + composed*: bool + DocumentOrShadowRoot* {.importc.} = object of RootObj + activeElement*: Element + # styleSheets*: StyleSheetList + ShadowRoot* = ref ShadowRootObj + ShadowRootObj {.importc.} = object of DocumentOrShadowRoot + delegatesFocus*: bool + host*: Element + innerHTML*: cstring + mode*: cstring # "open" or "closed" + ShadowRootInit* = object of RootObj + mode*: cstring + delegatesFocus*: bool + + HTMLSlotElement* = ref HTMLSlotElementObj + HTMLSlotElementObj {.importc.} = object of RootObj + name*: cstring + SlotOptions* = object of RootObj + flatten*: bool const fileReaderEmpty* = 0.FileReaderState @@ -1503,6 +1523,18 @@ proc contains*(n: Node): bool proc isEqualNode*(n: Node): bool proc isSameNode*(n: Node): bool +since (1, 3): + proc getRootNode*(n: Node,options: RootNodeOptions): Node + + # DocumentOrShadowRoot + proc getSelection*(n: DocumentOrShadowRoot): Selection + proc elementFromPoint*(n: DocumentOrShadowRoot; x, y: float): Element + + # shadow dom + proc attachShadow*(n: Element): ShadowRoot + proc assignedNodes*(n: HTMLSlotElement; options: SlotOptions): seq[Node] + proc assignedElements*(n: HTMLSlotElement; options: SlotOptions): seq[Element] + # Document "methods" proc createAttribute*(d: Document, identifier: cstring): Node proc getElementsByName*(d: Document, name: cstring): seq[Element] diff --git a/lib/js/dom_extensions.nim b/lib/js/dom_extensions.nim new file mode 100644 index 000000000..851ec0c5f --- /dev/null +++ b/lib/js/dom_extensions.nim @@ -0,0 +1,5 @@ +import dom + +{.push importcpp.} +proc elementsFromPoint*(n: DocumentOrShadowRoot; x, y: float): seq[Element] +{.pop.} \ No newline at end of file From 450a3e3179096c09cb473956386940a212f9a1d2 Mon Sep 17 00:00:00 2001 From: genotrance Date: Tue, 21 Jul 2020 12:14:36 -0500 Subject: [PATCH 0440/3768] Fix #2408 - add -d:globalSymbols (#14904) --- doc/nimc.rst | 3 +++ lib/system/dyncalls.nim | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/doc/nimc.rst b/doc/nimc.rst index bd5e897c3..379d8b559 100644 --- a/doc/nimc.rst +++ b/doc/nimc.rst @@ -454,6 +454,9 @@ Define Effect path for the ``sh`` binary, in cases where it is not located in the default location ``/bin/sh``. ``noSignalHandler`` Disable the crash handler from ``system.nim``. +``globalSymbols`` Load all ``{.dynlib.}`` libraries with the ``RTLD_GLOBAL`` + flag on Posix systems to resolve symbols in subsequently + loaded libraries. ====================== ========================================================= diff --git a/lib/system/dyncalls.nim b/lib/system/dyncalls.nim index fe4797c5f..a15d9267f 100644 --- a/lib/system/dyncalls.nim +++ b/lib/system/dyncalls.nim @@ -91,7 +91,10 @@ when defined(posix): dlclose(lib) proc nimLoadLibrary(path: string): LibHandle = - result = dlopen(path, RTLD_NOW) + let flags = + when defined(globalSymbols): RTLD_NOW or RTLD_GLOBAL + else: RTLD_NOW + result = dlopen(path, flags) when defined(nimDebugDlOpen): let error = dlerror() if error != nil: From 5fafa2fd5c78232e4fe2d3d13bc67ab6c0cda0bb Mon Sep 17 00:00:00 2001 From: Miran Date: Tue, 21 Jul 2020 22:49:08 +0200 Subject: [PATCH 0441/3768] fix several newline problems (#15028) [backend] * prevent newlines where they shouldn't be * 'contentLength' shouldn't be negative --- lib/pure/asyncftpclient.nim | 4 ++++ lib/pure/httpclient.nim | 8 +++++++- lib/pure/smtp.nim | 27 ++++++++++++++++++++++++++- 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/lib/pure/asyncftpclient.nim b/lib/pure/asyncftpclient.nim index 4e62ecd97..208782c05 100644 --- a/lib/pure/asyncftpclient.nim +++ b/lib/pure/asyncftpclient.nim @@ -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() diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 92e4cd2d6..a590bd83a 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -245,7 +245,8 @@ proc contentLength*(response: Response | AsyncResponse): int = ## ## A ``ValueError`` exception will be raised if the value is not an integer. var contentLengthHeader = response.headers.getOrDefault("Content-Length") - return contentLengthHeader.parseInt() + result = contentLengthHeader.parseInt() + doAssert(result >= 0 and result <= high(int32)) proc lastModified*(response: Response | AsyncResponse): DateTime = ## Retrieves the specified response's last modified time. @@ -1033,6 +1034,11 @@ proc request*(client: HttpClient | AsyncHttpClient, url: string, ## ## This procedure will follow redirects up to a maximum number of redirects ## specified in ``client.maxRedirects``. + ## + ## You need to make sure that the ``url`` doesn't contain any newline + ## characters. Failing to do so will raise ``AssertionDefect``. + doAssert(not url.contains({'\c', '\L'}), "url shouldn't contain any newline characters") + result = await client.requestAux(url, httpMethod, body, headers, multipart) var lastURL = url diff --git a/lib/pure/smtp.nim b/lib/pure/smtp.nim index f24815a1d..7d426041d 100644 --- a/lib/pure/smtp.nim +++ b/lib/pure/smtp.nim @@ -43,7 +43,7 @@ ## For SSL support this module relies on OpenSSL. If you want to ## enable SSL, compile with ``-d:ssl``. -import net, strutils, strtabs, base64, os +import net, strutils, strtabs, base64, os, strutils import asyncnet, asyncdispatch export Port @@ -65,6 +65,11 @@ type Smtp* = SmtpBase[Socket] AsyncSmtp* = SmtpBase[AsyncSocket] +proc containsNewline(xs: seq[string]): bool = + for x in xs: + if x.contains({'\c', '\L'}): + return true + proc debugSend*(smtp: Smtp | AsyncSmtp, cmd: string) {.multisync.} = ## Sends ``cmd`` on the socket connected to the SMTP server. ## @@ -119,6 +124,14 @@ else: proc createMessage*(mSubject, mBody: string, mTo, mCc: seq[string], otherHeaders: openarray[tuple[name, value: string]]): Message = ## Creates a new MIME compliant message. + ## + ## You need to make sure that ``mSubject``, ``mTo`` and ``mCc`` don't contain + ## any newline characters. Failing to do so will raise ``AssertionDefect``. + doAssert(not mSubject.contains({'\c', '\L'}), + "'mSubject' shouldn't contain any newline characters") + doAssert(not (mTo.containsNewline() or mCc.containsNewline()), + "'mTo' and 'mCc' shouldn't contain any newline characters") + result.msgTo = mTo result.msgCc = mCc result.msgSubject = mSubject @@ -130,6 +143,13 @@ proc createMessage*(mSubject, mBody: string, mTo, mCc: seq[string], proc createMessage*(mSubject, mBody: string, mTo, mCc: seq[string] = @[]): Message = ## Alternate version of the above. + ## + ## You need to make sure that ``mSubject``, ``mTo`` and ``mCc`` don't contain + ## any newline characters. Failing to do so will raise ``AssertionDefect``. + doAssert(not mSubject.contains({'\c', '\L'}), + "'mSubject' shouldn't contain any newline characters") + doAssert(not (mTo.containsNewline() or mCc.containsNewline()), + "'mTo' and 'mCc' shouldn't contain any newline characters") result.msgTo = mTo result.msgCc = mCc result.msgSubject = mSubject @@ -247,6 +267,11 @@ proc sendMail*(smtp: Smtp | AsyncSmtp, fromAddr: string, ## Sends ``msg`` from ``fromAddr`` to the addresses specified in ``toAddrs``. ## Messages may be formed using ``createMessage`` by converting the ## Message into a string. + ## + ## You need to make sure that ``fromAddr`` and ``toAddrs`` don't contain + ## any newline characters. Failing to do so will raise ``AssertionDefect``. + doAssert(not (toAddrs.containsNewline() or fromAddr.contains({'\c', '\L'})), + "'toAddrs' and 'fromAddr' shouldn't contain any newline characters") await smtp.debugSend("MAIL FROM:<" & fromAddr & ">\c\L") await smtp.checkReply("250") From 911e39351e34c1022b586fe9bfc3a67b2ce3bf44 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 21 Jul 2020 23:23:33 +0200 Subject: [PATCH 0442/3768] ARC: optimize the code better when --panics:off (#15031) --- compiler/liftdestructors.nim | 42 ++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 21fef1d61..034e62494 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -10,9 +10,6 @@ ## This module implements lifting for type-bound operations ## (``=sink``, ``=``, ``=destroy``, ``=deepCopy``). -# Todo: -# - use openArray instead of array to avoid over-specializations - import modulegraphs, lineinfos, idents, ast, renderer, semdata, sighashes, lowerings, options, types, msgs, magicsys, tables @@ -26,8 +23,9 @@ type fn: PSym asgnForType: PType recurse: bool - filterDiscriminator: PSym # we generating destructor for case branch addMemReset: bool # add wasMoved() call after destructor call + canRaise: bool + filterDiscriminator: PSym # we generating destructor for case branch c: PContext # c can be nil, then we are called from lambdalifting! proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) @@ -120,10 +118,12 @@ proc genContainerOf(c: TLiftCtx; objType: PType, field, x: PSym): PNode = result.add newNodeIT(nkType, c.info, objPtr) result.add minusExpr -proc destructorCall(c: TLiftCtx; op: PSym; x: PNode): PNode = +proc destructorCall(c: var TLiftCtx; op: PSym; x: PNode): PNode = var destroy = newNodeIT(nkCall, x.info, op.typ[0]) destroy.add(newSymNode(op)) destroy.add genAddr(c.g, x) + if sfNeverRaises notin op.flags: + c.canRaise = true if c.addMemReset: result = newTree(nkStmtList, destroy, genBuiltin(c.g, mWasMoved, "wasMoved", x)) else: @@ -243,25 +243,29 @@ proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) = else: fillBodyObjTImpl(c, t, body, x, y) -proc newHookCall(g: ModuleGraph; op: PSym; x, y: PNode): PNode = +proc newHookCall(c: var TLiftCtx; op: PSym; x, y: PNode): PNode = #if sfError in op.flags: # localError(c.config, x.info, "usage of '$1' is a user-defined error" % op.name.s) result = newNodeI(nkCall, x.info) result.add newSymNode(op) + if sfNeverRaises notin op.flags: + c.canRaise = true if op.typ.sons[1].kind == tyVar: - result.add genAddr(g, x) + result.add genAddr(c.g, x) else: result.add x if y != nil: result.add y -proc newOpCall(op: PSym; x: PNode): PNode = +proc newOpCall(c: var TLiftCtx; op: PSym; x: PNode): PNode = result = newNodeIT(nkCall, x.info, op.typ[0]) result.add(newSymNode(op)) result.add x + if sfNeverRaises notin op.flags: + c.canRaise = true -proc newDeepCopyCall(op: PSym; x, y: PNode): PNode = - result = newAsgnStmt(x, newOpCall(op, y)) +proc newDeepCopyCall(c: var TLiftCtx; op: PSym; x, y: PNode): PNode = + result = newAsgnStmt(x, newOpCall(c, op, y)) proc usesBuiltinArc(t: PType): bool = proc wrap(t: PType): bool {.nimcall.} = ast.isGCedMem(t) @@ -293,7 +297,7 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode; #else: # markUsed(c.g.config, c.info, op, c.g.usageSym) onUse(c.info, op) - body.add newHookCall(c.g, op, x, y) + body.add newHookCall(c, op, x, y) result = true elif tfHasAsgn in t.flags: var op: PSym @@ -322,7 +326,7 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode; #debug(t) #return false assert op.ast[genericParamsPos].kind == nkEmpty - body.add newHookCall(c.g, op, x, y) + body.add newHookCall(c, op, x, y) result = true proc addDestructorCall(c: var TLiftCtx; orig: PType; body, x: PNode) = @@ -373,7 +377,7 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool = if op != nil: #markUsed(c.g.config, c.info, op, c.g.usageSym) onUse(c.info, op) - body.add newDeepCopyCall(op, x, y) + body.add newDeepCopyCall(c, op, x, y) result = true proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode = @@ -460,7 +464,7 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = of attachedAsgn, attachedDeepCopy: if t.assignment == nil: return # protect from recursion - body.add newHookCall(c.g, t.assignment, x, y) + body.add newHookCall(c, t.assignment, x, y) of attachedSink: # we always inline the move for better performance: let moveCall = genBuiltin(c.g, mMove, "move", x) @@ -471,18 +475,18 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = # alternatively we could do this: when false: doAssert t.asink != nil - body.add newHookCall(c.g, t.asink, x, y) + body.add newHookCall(c, t.asink, x, y) of attachedDestructor: doAssert t.destructor != nil body.add destructorCall(c, t.destructor, x) of attachedTrace: if t.attachedOps[c.kind] == nil: return # protect from recursion - body.add newHookCall(c.g, t.attachedOps[c.kind], x, y) + body.add newHookCall(c, t.attachedOps[c.kind], x, y) of attachedDispose: if t.attachedOps[c.kind] == nil: return # protect from recursion - body.add newHookCall(c.g, t.attachedOps[c.kind], x, nil) + body.add newHookCall(c, t.attachedOps[c.kind], x, nil) proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = case c.kind @@ -837,7 +841,7 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp; sfOverriden in typ.attachedOps[attachedDestructor].flags: ## compiler can use a combination of `=destroy` and memCopy for sink op dest.flags.incl sfCursor - result.ast[bodyPos].add newOpCall(typ.attachedOps[attachedDestructor], d[0]) + result.ast[bodyPos].add newOpCall(a, typ.attachedOps[attachedDestructor], d[0]) result.ast[bodyPos].add newAsgnStmt(d, src) else: var tk: TTypeKind @@ -852,6 +856,7 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp; fillStrOp(a, typ, result.ast[bodyPos], d, src) else: fillBody(a, typ, result.ast[bodyPos], d, src) + if not a.canRaise: incl result.flags, sfNeverRaises proc produceDestructorForDiscriminator*(g: ModuleGraph; typ: PType; field: PSym, info: TLineInfo): PSym = @@ -873,6 +878,7 @@ proc produceDestructorForDiscriminator*(g: ModuleGraph; typ: PType; field: PSym, result.ast[bodyPos].add v let placeHolder = newNodeIT(nkSym, info, getSysType(g, info, tyPointer)) fillBody(a, typ, result.ast[bodyPos], d, placeHolder) + if not a.canRaise: incl result.flags, sfNeverRaises template liftTypeBoundOps*(c: PContext; typ: PType; info: TLineInfo) = From 503c5e0520779af87dc6c42da7c9d1dbd2e8f8e1 Mon Sep 17 00:00:00 2001 From: Mildred Ki'Lya Date: Wed, 22 Jul 2020 06:30:33 +0200 Subject: [PATCH 0443/3768] smtp: Fix STARTTLS, request HELO once TLS is established (#15032) --- lib/pure/smtp.nim | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/pure/smtp.nim b/lib/pure/smtp.nim index 7d426041d..b42664029 100644 --- a/lib/pure/smtp.nim +++ b/lib/pure/smtp.nim @@ -60,6 +60,7 @@ type SmtpBase[SocketType] = ref object sock: SocketType + address: string debug: bool Smtp* = SmtpBase[Socket] @@ -225,15 +226,19 @@ proc checkReply*(smtp: Smtp | AsyncSmtp, reply: string) {.multisync.} = if not line.startswith(reply): await quitExcpt(smtp, "Expected " & reply & " reply, got: " & line) +proc helo*(smtp: Smtp | AsyncSmtp) {.multisync.} = + # Sends the HELO request + await smtp.debugSend("HELO " & smtp.address & "\c\L") + await smtp.checkReply("250") + proc connect*(smtp: Smtp | AsyncSmtp, address: string, port: Port) {.multisync.} = ## Establishes a connection with a SMTP server. ## May fail with ReplyError or with a socket error. + smtp.address = address await smtp.sock.connect(address, port) - await smtp.checkReply("220") - await smtp.debugSend("HELO " & address & "\c\L") - await smtp.checkReply("250") + await smtp.helo() proc startTls*(smtp: Smtp | AsyncSmtp, sslContext: SSLContext = nil) {.multisync.} = ## Put the SMTP connection in TLS (Transport Layer Security) mode. @@ -245,6 +250,7 @@ proc startTls*(smtp: Smtp | AsyncSmtp, sslContext: SSLContext = nil) {.multisync getSSLContext().wrapConnectedSocket(smtp.sock, handshakeAsClient) else: sslContext.wrapConnectedSocket(smtp.sock, handshakeAsClient) + await smtp.helo() else: {.error: "SMTP module compiled without SSL support".} From f25454d5c6b455d95aa526b57678ec8494ac0e36 Mon Sep 17 00:00:00 2001 From: Danil Yarantsev Date: Wed, 22 Jul 2020 10:52:38 +0300 Subject: [PATCH 0444/3768] Add a testcase for #14480. Fixes #14480 (#15037) --- tests/arc/tarcmisc.nim | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/arc/tarcmisc.nim b/tests/arc/tarcmisc.nim index 56aa3ec3b..51c80c348 100644 --- a/tests/arc/tarcmisc.nim +++ b/tests/arc/tarcmisc.nim @@ -257,3 +257,10 @@ echo "new line before - ", newline newline.insert(indent, 0) echo "new line after - ", newline + +# bug #14480 +proc hello(): int = + result = 42 + +var leaves {.global.} = hello() +doAssert leaves == 42 From 32ac1b7c9df8614510c08c0a4649131cf33f369c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Granstr=C3=B6m?= <5092565+HugoGranstrom@users.noreply.github.com> Date: Wed, 22 Jul 2020 10:30:46 +0200 Subject: [PATCH 0445/3768] fix #15033 (#15034) --- lib/pure/math.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/math.nim b/lib/pure/math.nim index ea44de6d3..c02bef2f7 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -958,7 +958,7 @@ proc sgn*[T: SomeNumber](x: T): int {.inline.} = {.pop.} {.pop.} -proc `^`*[T](x: T, y: Natural): T = +proc `^`*[T: SomeNumber](x: T, y: Natural): T = ## Computes ``x`` to the power ``y``. ## ## Exponent ``y`` must be non-negative, use From d84894de2e04b22111a607eb72d301a1535a65e9 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 22 Jul 2020 13:02:03 +0200 Subject: [PATCH 0446/3768] fixes #15026 [backport] (#15040) no test case since only a special case was affected and the special case got removed --- compiler/ccgstmts.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index c56340e00..0f58df3a4 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -1292,7 +1292,7 @@ proc genTryGoto(p: BProc; t: PNode; d: var TLoc) = # handler present or only handlers that did not match. linefmt(p, cpsStmts, "*nimErr_ = oldNimErrFin$1_;$n", [lab]) endBlock(p) - if p.prc != nil: raiseExit(p) + raiseExit(p) if hasExcept: inc p.withinTryWithExcept proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) = From ec65bfae3ac20a6c3d13249bc1fc8413db2b3abb Mon Sep 17 00:00:00 2001 From: Miran Date: Wed, 22 Jul 2020 14:57:40 +0200 Subject: [PATCH 0447/3768] Change testing commands for some packages (#15041) --- testament/important_packages.nim | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 3d5436c84..8fa7c99a9 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -13,7 +13,6 @@ var packages2*: seq[tuple[name, cmd: string; hasDeps: bool; url: string, useHead pkg1 "argparse" pkg1 "arraymancer", true, "nim c tests/tests_cpu.nim" pkg1 "ast_pattern_matching", false, "nim c -r --oldgensym:on tests/test1.nim" -pkg1 "asyncmysql", true pkg1 "awk", true pkg1 "bigints" pkg1 "binaryheap", false, "nim c -r binaryheap.nim" @@ -30,7 +29,6 @@ pkg1 "chronicles", true, "nim c -o:chr -r chronicles.nim" when not defined(osx): # testdatagram.nim(560, 54): Check failed pkg1 "chronos", true, "nim c -r -d:release tests/testall" pkg1 "cligen", false, "nim c -o:cligenn -r cligen.nim" -pkg1 "coco", true pkg1 "combparser" pkg1 "compactdict" pkg1 "comprehension", false, "nimble test", "https://github.com/alehander42/comprehension" @@ -45,7 +43,7 @@ pkg1 "gara" pkg1 "ggplotnim", true, "nim c -d:noCairo -r tests/tests.nim" # pkg1 "gittyup", true, "nimble test", "https://github.com/disruptek/gittyup" pkg1 "glob" -pkg1 "gnuplot" +pkg1 "gnuplot", false, "nim c gnuplot.nim" pkg1 "hts", false, "nim c -o:htss src/hts.nim" # pkg1 "httpauth", true pkg1 "illwill", false, "nimble examples" @@ -69,7 +67,7 @@ pkg1 "stint", false, "nim c -o:stintt -r stint.nim" # packages N-Z pkg2 "nake", false, "nim c nakefile.nim" pkg2 "neo", true, "nim c -d:blas=openblas tests/all.nim" -pkg2 "nesm" +# pkg2 "nesm", false, "nimble tests" # notice plural 'tests' # pkg2 "nico", true pkg2 "nicy", false, "nim c -r src/nicy.nim" pkg2 "nigui", false, "nim c -o:niguii -r src/nigui.nim" @@ -121,7 +119,7 @@ pkg2 "tempdir" pkg2 "templates" pkg2 "tensordsl", false, "nim c -r tests/tests.nim", "https://krux02@bitbucket.org/krux02/tensordslnim.git" pkg2 "terminaltables", false, "nim c src/terminaltables.nim" -pkg2 "termstyle" +pkg2 "termstyle", false, "nim c -r termstyle.nim" pkg2 "timeit" pkg2 "timezones" pkg2 "tiny_sqlite" @@ -132,5 +130,5 @@ pkg2 "websocket", false, "nim c websocket.nim" # pkg2 "winim", true pkg2 "with" pkg2 "ws" -pkg2 "yaml" +pkg2 "yaml", false, "nim build" pkg2 "zero_functional", false, "nim c -r -d:nimWorkaround14447 test.nim" From 925dd92e07bd8b031c9f237ceb9e352c34a97d64 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 23 Jul 2020 11:03:39 +0200 Subject: [PATCH 0448/3768] enforce browsers.nim only handles URLs [backport] (#15045) --- lib/pure/browsers.nim | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/lib/pure/browsers.nim b/lib/pure/browsers.nim index 2dc14b895..9b3b67104 100644 --- a/lib/pure/browsers.nim +++ b/lib/pure/browsers.nim @@ -18,6 +18,7 @@ import strutils when defined(windows): import winlean + from os import absolutePath else: import os, osproc @@ -26,16 +27,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: From 14d16c2174a4fcefd405f8c4eeaf57a6dcd2731d Mon Sep 17 00:00:00 2001 From: Miran Date: Thu, 23 Jul 2020 15:42:12 +0200 Subject: [PATCH 0449/3768] json.nim: smaller init size (#15048) There was a recent `rightSize` change in tables.nim, so the existing value (4) was creating too large tables. --- lib/pure/json.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 041816c7d..3f2e369f6 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -219,7 +219,7 @@ proc newJNull*(): JsonNode = proc newJObject*(): JsonNode = ## Creates a new `JObject JsonNode` - result = JsonNode(kind: JObject, fields: initOrderedTable[string, JsonNode](4)) + result = JsonNode(kind: JObject, fields: initOrderedTable[string, JsonNode](2)) proc newJArray*(): JsonNode = ## Creates a new `JArray JsonNode` @@ -264,7 +264,7 @@ proc getBool*(n: JsonNode, default: bool = false): bool = else: return n.bval proc getFields*(n: JsonNode, - default = initOrderedTable[string, JsonNode](4)): + default = initOrderedTable[string, JsonNode](2)): OrderedTable[string, JsonNode] = ## Retrieves the key, value pairs of a `JObject JsonNode`. ## From c90c53e8bb93c99a323684d73df7e7ec4791107d Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 23 Jul 2020 16:53:07 +0200 Subject: [PATCH 0450/3768] fixes #15044 [backport:1.2] --- compiler/semmagic.nim | 8 +++++--- tests/arc/tarcmisc.nim | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 6aa440aa9..f991b4ac6 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -527,12 +527,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 diff --git a/tests/arc/tarcmisc.nim b/tests/arc/tarcmisc.nim index 51c80c348..cb889db15 100644 --- a/tests/arc/tarcmisc.nim +++ b/tests/arc/tarcmisc.nim @@ -23,6 +23,7 @@ whiley ends :( 0 new line before - @['a'] new line after - @['a'] +finalizer closed destroying variable: 20 destroying variable: 10 @@ -258,6 +259,23 @@ newline.insert(indent, 0) echo "new line after - ", newline +# bug #15044 + +type + Test = ref object + +proc test: Test = + # broken + new(result, proc(x: Test) = + echo "finalizer" + ) + +proc tdirectFinalizer = + discard test() + +tdirectFinalizer() + + # bug #14480 proc hello(): int = result = 42 From 18a70a75638ded6ff23b2a9a57edfc7dd09bb5b1 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 23 Jul 2020 18:21:44 +0200 Subject: [PATCH 0451/3768] fixes #15036 --- compiler/ast.nim | 3 ++- compiler/injectdestructors.nim | 2 -- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index d8fe6b1c0..6ca79aeee 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -230,7 +230,7 @@ type TNodeKinds* = set[TNodeKind] type - TSymFlag* = enum # 42 flags! + TSymFlag* = enum # 43 flags! sfUsed, # read access of sym (for warnings) or simply used sfExported, # symbol is exported from module sfFromGeneric, # symbol is instantiation of a generic; this is needed @@ -294,6 +294,7 @@ type sfNeverRaises # proc can never raise an exception, not even OverflowDefect # or out-of-memory sfUsedInFinallyOrExcept # symbol is used inside an 'except' or 'finally' + sfSingleUsedTemp # For temporaries that we know will only be used once TSymFlags* = set[TSymFlag] diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 752438180..01f72744a 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -59,8 +59,6 @@ proc getTemp(c: var Con; s: var Scope; typ: PType; info: TLineInfo): PNode = s.vars.add(sym) result = newSymNode(sym) -const sfSingleUsedTemp = sfExported # For temporaries that we know will only be used once - proc nestedScope(parent: var Scope): Scope = Scope(vars: @[], wasMoved: @[], final: @[], needsTry: false, parent: addr(parent)) From 12a9fe017e4a49a161f5cabbbbcae0f5668b0ae3 Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 24 Jul 2020 17:49:25 +0200 Subject: [PATCH 0452/3768] fixes #15056 [backport] --- compiler/semcall.nim | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 0d8f0ae8b..0c30ce45e 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -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") @@ -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: From 4b93c61f0d783cbc1ce35d4494b533186595cdbd Mon Sep 17 00:00:00 2001 From: Miran Date: Fri, 24 Jul 2020 21:15:20 +0200 Subject: [PATCH 0453/3768] jsre: try to fix nightlies (#15057) --- lib/js/jsre.nim | 3 +++ tools/kochdocs.nim | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/js/jsre.nim b/lib/js/jsre.nim index 31cfa3d0b..b4f273d0b 100644 --- a/lib/js/jsre.nim +++ b/lib/js/jsre.nim @@ -1,6 +1,9 @@ ## Regular Expressions for the JavaScript target. ## * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions +when not defined(js) and not defined(Nimdoc): + {.error: "This module only works on the JavaScript platform".} + type RegExp* {.importjs.} = object ## Regular Expressions for JavaScript target. flags* {.importjs.}: cstring ## cstring that contains the flags of the RegExp object. dotAll* {.importjs.}: bool ## Whether `.` matches newlines or not. diff --git a/tools/kochdocs.nim b/tools/kochdocs.nim index 43b802ee2..8a8406a69 100644 --- a/tools/kochdocs.nim +++ b/tools/kochdocs.nim @@ -160,6 +160,7 @@ lib/posix/posix_linux_amd64_consts.nim lib/posix/posix_other_consts.nim lib/posix/posix_openbsd_amd64.nim lib/posix/posix_haiku.nim +lib/js/jsre.nim """.splitWhitespace() when (NimMajor, NimMinor) < (1, 1) or not declared(isRelativeTo): @@ -258,7 +259,7 @@ proc buildDoc(nimArgs, destPath: string) = destPath / changeFileExt(splitFile(d).name, "html"), d] i.inc for d in items(doc): - let extra = if isJsOnly(d): " --backend:js " else: "" + let extra = if isJsOnly(d): "--backend:js" else: "" var nimArgs2 = nimArgs if d.isRelativeTo("compiler"): doAssert false commands[i] = nim & " doc $# $# --git.url:$# --outdir:$# --index:on $#" % From add003a07435a862191334a7f105cb27ecb73d1d Mon Sep 17 00:00:00 2001 From: jcosborn Date: Fri, 24 Jul 2020 14:19:11 -0500 Subject: [PATCH 0454/3768] fix assignment to converted concept type (#15051) * fix assignment to converted concept type * check for resolved concepts * add extra test --- compiler/types.nim | 4 +++ tests/concepts/tusertypeclasses2.nim | 54 +++++++++++++++++++--------- 2 files changed, 41 insertions(+), 17 deletions(-) diff --git a/compiler/types.nim b/compiler/types.nim index 506e63478..b0581e96a 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -1070,7 +1070,11 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = if x == y: return true var a = skipTypes(x, {tyGenericInst, tyAlias}) + while a.kind == tyUserTypeClass and tfResolved in a.flags: + a = skipTypes(a[^1], {tyGenericInst, tyAlias}) var b = skipTypes(y, {tyGenericInst, tyAlias}) + while b.kind == tyUserTypeClass and tfResolved in b.flags: + b = skipTypes(b[^1], {tyGenericInst, tyAlias}) assert(a != nil) assert(b != nil) if a.kind != b.kind: diff --git a/tests/concepts/tusertypeclasses2.nim b/tests/concepts/tusertypeclasses2.nim index ae05540cd..c9978f6ef 100644 --- a/tests/concepts/tusertypeclasses2.nim +++ b/tests/concepts/tusertypeclasses2.nim @@ -1,24 +1,44 @@ -type - hasFieldX = concept z - z.x is int +block: + type + hasFieldX = concept z + z.x is int - obj_x = object - x: int + obj_x = object + x: int - ref_obj_x = ref object - x: int + ref_obj_x = ref object + x: int - ref_to_obj_x = ref obj_x + ref_to_obj_x = ref obj_x - p_o_x = ptr obj_x - v_o_x = var obj_x + p_o_x = ptr obj_x + v_o_x = var obj_x -template check(x) = - static: assert(x) + template check(x) = + static: assert(x) -check obj_x is hasFieldX -check ref_obj_x is hasFieldX -check ref_to_obj_x is hasFieldX -check p_o_x is hasFieldX -check v_o_x is hasFieldX + check obj_x is hasFieldX + check ref_obj_x is hasFieldX + check ref_to_obj_x is hasFieldX + check p_o_x is hasFieldX + check v_o_x is hasFieldX +block: + type + Foo = concept x + x.isFoo + Bar = distinct float + template isFoo(x: Bar): untyped = true + proc foo(x: var Foo) = + float(x) = 1.0 + proc foo2(x: var Bar) = + float(x) = 1.0 + proc foo3(x: var (Bar|SomeNumber)) = + float(x) = 1.0 + proc foo4(x: var any) = + float(x) = 1.0 + var x: Bar + foo(x) + foo2(x) + foo3(x) + foo4(x) From 8211d385697407c47a17681e37a2de1368e45a9e Mon Sep 17 00:00:00 2001 From: genotrance Date: Sat, 25 Jul 2020 01:16:25 -0500 Subject: [PATCH 0455/3768] Bump nimble commit (#15053) --- koch.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/koch.nim b/koch.nim index 7dedb039a..1d1fdf997 100644 --- a/koch.nim +++ b/koch.nim @@ -10,11 +10,11 @@ # const - NimbleStableCommit = "63695f490728e3935692c29f3d71944d83bb1e83" # master + NimbleStableCommit = "e5d340df8cf26f89ad2c9f0bc081ecd7e0323b0a" # master when not defined(windows): const - Z3StableCommit = "65de3f748a6812eecd7db7c478d5fc54424d368b" # the version of Z3 that DrNim uses + Z3StableCommit = "65de3f748a6812eecd7db7c478dcd5fc54424d368b" # the version of Z3 that DrNim uses when defined(gcc) and defined(windows): when defined(x86): From 9746868832df3ef4b40597fb84f58900b2336496 Mon Sep 17 00:00:00 2001 From: Bung Date: Sat, 25 Jul 2020 15:51:59 +0800 Subject: [PATCH 0456/3768] fix #14534 (#15060) [backport] --- compiler/jsgen.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index d876531e9..3351d64ae 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -1689,7 +1689,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: From 2dddf32726a1472d322644ec1a66caf8c547360e Mon Sep 17 00:00:00 2001 From: Bung Date: Sat, 25 Jul 2020 15:52:29 +0800 Subject: [PATCH 0457/3768] fix #14684 (#15059) --- compiler/commands.nim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/commands.nim b/compiler/commands.nim index d0936a0c6..504550045 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -486,6 +486,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "project": processOnOffSwitchG(conf, {optWholeProject, optGenIndex}, arg, pass, info) of "gc": + if conf.backend == backendJs: + return expectArg(conf, switch, arg, pass, info) if pass in {passCmd2, passPP}: case arg.normalize From 1330597f6df1d2f7d23ec25a7705e21be2e78d29 Mon Sep 17 00:00:00 2001 From: Miran Date: Sat, 25 Jul 2020 09:53:47 +0200 Subject: [PATCH 0458/3768] deprecate tables.add (#15047) --- lib/pure/collections/tables.nim | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index cf864c640..a8742bdbe 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -484,7 +484,8 @@ proc len*[A, B](t: Table[A, B]): int = result = t.counter -proc add*[A, B](t: var Table[A, B], key: A, val: B) = +proc add*[A, B](t: var Table[A, B], key: A, val: B) {.deprecated: + "Deprecated since v1.4; it was more confusing than useful, use `[]=`".} = ## Puts a new ``(key, value)`` pair into ``t`` even if ``t[key]`` already exists. ## ## **This can introduce duplicate keys into the table!** @@ -962,7 +963,8 @@ proc len*[A, B](t: TableRef[A, B]): int = result = t.counter -proc add*[A, B](t: TableRef[A, B], key: A, val: B) = +proc add*[A, B](t: TableRef[A, B], key: A, val: B) {.deprecated: + "Deprecated since v1.4; it was more confusing than useful, use `[]=`".} = ## Puts a new ``(key, value)`` pair into ``t`` even if ``t[key]`` already exists. ## ## **This can introduce duplicate keys into the table!** @@ -1448,7 +1450,8 @@ proc len*[A, B](t: OrderedTable[A, B]): int {.inline.} = result = t.counter -proc add*[A, B](t: var OrderedTable[A, B], key: A, val: B) = +proc add*[A, B](t: var OrderedTable[A, B], key: A, val: B) {.deprecated: + "Deprecated since v1.4; it was more confusing than useful, use `[]=`".} = ## Puts a new ``(key, value)`` pair into ``t`` even if ``t[key]`` already exists. ## ## **This can introduce duplicate keys into the table!** @@ -1935,7 +1938,8 @@ proc len*[A, B](t: OrderedTableRef[A, B]): int {.inline.} = result = t.counter -proc add*[A, B](t: OrderedTableRef[A, B], key: A, val: B) = +proc add*[A, B](t: OrderedTableRef[A, B], key: A, val: B) {.deprecated: + "Deprecated since v1.4; it was more confusing than useful, use `[]=`".} = ## Puts a new ``(key, value)`` pair into ``t`` even if ``t[key]`` already exists. ## ## **This can introduce duplicate keys into the table!** From 7ca32c86bbb84ac7bcadd0a2bc2cd5fc277f1bff Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 25 Jul 2020 15:14:28 +0200 Subject: [PATCH 0459/3768] writing to a location counts as "side effect"; implements https://github.com/nim-lang/RFCs/issues/234 (#15030) --- compiler/isolation_check.nim | 2 +- compiler/options.nim | 1 + compiler/sempass2.nim | 11 +- compiler/varpartitions.nim | 243 +++++++++++++++++++++++++ tests/effects/tfuncs_cannot_mutate.nim | 31 ++++ 5 files changed, 283 insertions(+), 5 deletions(-) create mode 100644 compiler/varpartitions.nim create mode 100644 tests/effects/tfuncs_cannot_mutate.nim diff --git a/compiler/isolation_check.nim b/compiler/isolation_check.nim index a77e8c97a..2b53bce92 100644 --- a/compiler/isolation_check.nim +++ b/compiler/isolation_check.nim @@ -64,7 +64,7 @@ proc canAlias(arg, ret: PType; marker: var IntSet): bool = else: result = false -proc canAlias(arg, ret: PType): bool = +proc canAlias*(arg, ret: PType): bool = var marker = initIntSet() result = canAlias(arg, ret, marker) diff --git a/compiler/options.nim b/compiler/options.nim index 8b73c07fc..5d9bc245b 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -163,6 +163,7 @@ type ## which itself requires `nimble install libffi`, see #10150 ## Note: this feature can't be localized with {.push.} vmopsDanger, + strictFuncs LegacyFeature* = enum allowSemcheckedAstModification, diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 49fa44ca2..c6d6d9241 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -10,7 +10,7 @@ import intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees, wordrecg, strutils, options, guards, lineinfos, semfold, semdata, - modulegraphs + modulegraphs, varpartitions when defined(useDfa): import dfa @@ -36,9 +36,6 @@ For every sink parameter of type T T is marked. For every call f() the return type of f() is marked. - - - ]# # ------------------------ exception and tag tracking ------------------------- @@ -73,6 +70,7 @@ type guards: TModel # nested guards locked: seq[PNode] # locked locations gcUnsafe, isRecursive, isTopLevel, hasSideEffect, inEnforcedGcSafe: bool + hasDangerousAssign: bool inEnforcedNoSideEffects: bool maxLockLevel, currLockLevel: TLockLevel currOptions: TOptions @@ -885,6 +883,8 @@ proc track(tracked: PEffects, n: PNode) = createTypeBoundOps(tracked, n[0].typ, n.info) if n[0].kind != nkSym or not isLocalVar(tracked, n[0].sym): checkForSink(tracked.config, tracked.owner, n[1]) + if not tracked.hasDangerousAssign and n[0].kind != nkSym: + tracked.hasDangerousAssign = true of nkVarSection, nkLetSection: for child in n: let last = lastSon(child) @@ -1237,6 +1237,9 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) = patchResult(t, ensuresSpec) effects[ensuresEffects] = ensuresSpec + if strictFuncs in c.features and not t.hasSideEffect and t.hasDangerousAssign: + t.hasSideEffect = mutatesNonVarParameters(s, body) + if sfThread in s.flags and t.gcUnsafe: if optThreads in g.config.globalOptions and optThreadAnalysis in g.config.globalOptions: #localError(s.info, "'$1' is not GC-safe" % s.name.s) diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim new file mode 100644 index 000000000..b89b7d1da --- /dev/null +++ b/compiler/varpartitions.nim @@ -0,0 +1,243 @@ +# +# +# The Nim Compiler +# (c) Copyright 2020 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Partition variables into different graphs. Used for +## Nim's write tracking. The used algorithm is "union find" +## with path compression. + +import ast, types +from trees import getMagic +from isolation_check import canAlias + +type + SubgraphFlag = enum + isMutated, # graph might be mutated + connectsConstParam, # graph is connected to a non-var parameter. + + VarIndexKind = enum + isEmptyRoot, + dependsOn, + isRootOf + + VarIndex = object + case kind: VarIndexKind + of isEmptyRoot: discard + of dependsOn: parent: int + of isRootOf: graphIndex: int + + Partitions = object + symToId: seq[PSym] + s: seq[VarIndex] + graphs: seq[set[SubgraphFlag]] + +proc hasSideEffect(p: Partitions): bool = + for g in p.graphs: + if g == {isMutated, connectsConstParam}: return true + return false + +template isConstParam(a): bool = a.kind == skParam and a.typ.kind != tyVar + +proc registerVariable(p: var Partitions; n: PNode) = + if n.kind == nkSym: + p.symToId.add n.sym + if isConstParam(n.sym): + p.s.add VarIndex(kind: isRootOf, graphIndex: p.graphs.len) + p.graphs.add({connectsConstParam}) + else: + p.s.add VarIndex(kind: isEmptyRoot) + +proc variableId(p: Partitions; x: PSym): int {.inline.} = system.find(p.symToId, x) + +proc root(v: var Partitions; start: int): int = + result = start + while v.s[result].kind == dependsOn: + result = v.s[result].parent + # path compression: + var it = start + while v.s[it].kind == dependsOn: + let next = v.s[it].parent + v.s[it] = VarIndex(kind: dependsOn, parent: result) + it = next + +proc potentialMutation(v: var Partitions; s: PSym) = + let id = variableId(v, s) + if id >= 0: + let r = root(v, id) + case v.s[r].kind + of isEmptyRoot: + v.s[r] = VarIndex(kind: isRootOf, graphIndex: v.graphs.len) + v.graphs.add({isMutated}) + of isRootOf: + v.graphs[v.s[r].graphIndex].incl isMutated + else: + assert false, "cannot happen" + else: + discard "we are not interested in the mutation" + +proc connect(v: var Partitions; a, b: PSym) = + let aid = variableId(v, a) + if aid < 0: + return + let bid = variableId(v, b) + if bid < 0: + return + + let ra = root(v, aid) + let rb = root(v, bid) + if ra != rb: + let paramFlags = + if isConstParam(a) or isConstParam(b): + {connectsConstParam} + else: + {} + + # for now we always make 'rb' the slave and 'ra' the master: + let rbFlags = + if v.s[rb].kind == isRootOf: + v.graphs[v.s[rb].graphIndex] + else: + {} + + v.s[rb] = VarIndex(kind: dependsOn, parent: ra) + case v.s[ra].kind + of isEmptyRoot: + v.s[ra] = VarIndex(kind: isRootOf, graphIndex: v.graphs.len) + v.graphs.add(paramFlags + rbFlags) + of isRootOf: + v.graphs[v.s[ra].graphIndex].incl paramFlags + rbFlags + else: + assert false, "cannot happen" + +proc allRoots(n: PNode; result: var seq[PSym]) = + case n.kind + of nkSym: + if n.sym.kind in {skParam, skVar, skTemp, skLet, skResult, skForVar}: + result.add(n.sym) + of nkHiddenDeref, nkDerefExpr, nkAddr, nkDotExpr, nkBracketExpr, + nkCheckedFieldExpr, nkHiddenAddr, nkObjUpConv, nkObjDownConv: + allRoots(n[0], result) + of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv, nkConv, + nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr, nkCast: + if n.len > 0: + allRoots(n.lastSon, result) + of nkCaseStmt, nkObjConstr: + for i in 1.. 1: + allRoots(n[1], result) + else: + let m = getMagic(n) + case m + of mNone: + # we do significantly better here by using the available escape + # information: + if n[0].typ.isNil: return + var typ = n[0].typ + if typ != nil: + typ = skipTypes(typ, abstractInst) + if typ.kind != tyProc: typ = nil + else: assert(typ.len == typ.n.len) + + for i in 1 ..< n.len: + let it = n[i] + if typ != nil and i < typ.len: + assert(typ.n[i].kind == nkSym) + let paramType = typ.n[i].typ + if not paramType.isCompileTimeOnly and not typ.sons[0].isEmptyType and + canAlias(paramType, typ.sons[0]): + allRoots(it, result) + else: + allRoots(it, result) + + of mSlice: + allRoots(n[1], result) + else: + discard "harmless operation" + else: + discard "nothing to do" + +proc deps(p: var Partitions; dest, src: PNode) = + var targets, sources: seq[PSym] + allRoots(dest, targets) + allRoots(src, sources) + for t in targets: + if dest.kind != nkSym: + potentialMutation(p, t) + + proc wrap(t: PType): bool {.nimcall.} = t.kind in {tyRef, tyPtr} + if types.searchTypeFor(t.typ, wrap): + for s in sources: + connect(p, t, s) + +proc traverse(p: var Partitions; n: PNode) = + case n.kind + of nkLetSection, nkVarSection: + for child in n: + let last = lastSon(child) + traverse(p, last) + if child.kind == nkVarTuple and last.kind in {nkPar, nkTupleConstr}: + if child.len-2 != last.len: return + for i in 0.. Date: Sat, 25 Jul 2020 19:36:53 +0200 Subject: [PATCH 0460/3768] strict func: much better error messages (#15068) * strict func: much better error messages * documented the 'strict funcs' mode --- changelog.md | 27 ++++--- compiler/sempass2.nim | 5 +- compiler/varpartitions.nim | 104 +++++++++++++++++-------- doc/manual_experimental.rst | 38 +++++++++ tests/effects/tfuncs_cannot_mutate.nim | 5 +- 5 files changed, 133 insertions(+), 46 deletions(-) diff --git a/changelog.md b/changelog.md index e30e9038b..d4161373c 100644 --- a/changelog.md +++ b/changelog.md @@ -101,7 +101,7 @@ - `osproc.execCmdEx` now takes an optional `input` for stdin, `workingDir` and `env` parameters. -- Add `ssl_config` module containing lists of secure ciphers as recommended by +- 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 @@ -110,27 +110,28 @@ users from the use of weak and insecure ciphers while still provides adequate compatibility with the majority of the Internet. -- new module `std/jsonutils` with hookable `jsonTo,toJson,fromJson` for json - serialization/deserialization of custom types. +- A new module `std/jsonutils` with hookable `jsonTo,toJson,fromJson` operations for json + serialization/deserialization of custom types was added. -- new proc `heapqueue.find[T](heap: HeapQueue[T], x: T): int` to get index of element ``x``. -- Add `rstgen.rstToLatex` convenience proc for `renderRstToOut` and `initRstGenerator` +- 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. -- Add `os.normalizeExe`, e.g.: `koch` => `./koch`. +- Added `os.normalizeExe`, e.g.: `koch` => `./koch`. - `macros.newLit` now preserves named vs unnamed tuples; use `-d:nimHasWorkaround14720` to keep old behavior. -- Add `random.gauss`, that uses the ratio of uniforms method of sampling from a Gaussian distribution. -- Add `typetraits.elementType` to get element type of an iterable. +- 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)"` -- add `macros.extractDocCommentsAndRunnables` helper +- 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` -- Add `jsre` module, [Regular Expressions for the JavaScript target.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions) +- 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. @@ -144,7 +145,7 @@ ## Language changes - The `=destroy` hook no longer has to reset its target, as the compiler now automatically inserts - wasMoved calls where needed. + `wasMoved` calls where needed. - In the newruntime it is now allowed to assign to the discriminator field without restrictions as long as case object doesn't have custom destructor. The discriminator value doesn't have to be a constant either. If you have a @@ -217,6 +218,10 @@ proc mydiv(a, b): int {.raises: [].} = - Remove `sharedlists.initSharedList`, was deprecated and produces undefined behaviour. +- There is a new experimental feature called "strictFuncs" which makes the definition of + `.noSideEffect` stricter. [See](manual_experimental.html#stricts-funcs) + for more information. + ## Compiler changes diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index c6d6d9241..f6fce712a 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -1237,8 +1237,9 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) = patchResult(t, ensuresSpec) effects[ensuresEffects] = ensuresSpec + var mutationInfo = MutationInfo() if strictFuncs in c.features and not t.hasSideEffect and t.hasDangerousAssign: - t.hasSideEffect = mutatesNonVarParameters(s, body) + t.hasSideEffect = mutatesNonVarParameters(s, body, mutationInfo) if sfThread in s.flags and t.gcUnsafe: if optThreads in g.config.globalOptions and optThreadAnalysis in g.config.globalOptions: @@ -1251,7 +1252,7 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) = when false: listGcUnsafety(s, onlyWarning=false, g.config) else: - localError(g.config, s.info, "'$1' can have side effects" % s.name.s) + localError(g.config, s.info, ("'$1' can have side effects" % s.name.s) & (g.config $ mutationInfo)) if not t.gcUnsafe: s.typ.flags.incl tfGcSafe if not t.hasSideEffect and sfSideEffect notin s.flags: diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim index b89b7d1da..1dee5607d 100644 --- a/compiler/varpartitions.nim +++ b/compiler/varpartitions.nim @@ -11,7 +11,7 @@ ## Nim's write tracking. The used algorithm is "union find" ## with path compression. -import ast, types +import ast, types, lineinfos, options, msgs from trees import getMagic from isolation_check import canAlias @@ -31,14 +31,36 @@ type of dependsOn: parent: int of isRootOf: graphIndex: int + MutationInfo* = object + param: PSym + mutatedHere, connectedVia: TLineInfo + flags: set[SubgraphFlag] + Partitions = object symToId: seq[PSym] s: seq[VarIndex] - graphs: seq[set[SubgraphFlag]] + graphs: seq[MutationInfo] -proc hasSideEffect(p: Partitions): bool = - for g in p.graphs: - if g == {isMutated, connectsConstParam}: return true +proc `$`*(config: ConfigRef; g: MutationInfo): string = + result = "" + if g.flags == {isMutated, connectsConstParam}: + result.add "\nan object reachable from '" + result.add g.param.name.s + result.add "' is potentially mutated" + if g.mutatedHere != unknownLineInfo: + result.add "\n" + result.add config $ g.mutatedHere + result.add " the mutation is here" + if g.connectedVia != unknownLineInfo: + result.add "\n" + result.add config $ g.connectedVia + result.add " is the statement that connected the mutation to the parameter" + +proc hasSideEffect(p: var Partitions; info: var MutationInfo): bool = + for g in mitems p.graphs: + if g.flags == {isMutated, connectsConstParam}: + info = g + return true return false template isConstParam(a): bool = a.kind == skParam and a.typ.kind != tyVar @@ -48,7 +70,8 @@ proc registerVariable(p: var Partitions; n: PNode) = p.symToId.add n.sym if isConstParam(n.sym): p.s.add VarIndex(kind: isRootOf, graphIndex: p.graphs.len) - p.graphs.add({connectsConstParam}) + p.graphs.add MutationInfo(param: n.sym, mutatedHere: unknownLineInfo, + connectedVia: unknownLineInfo, flags: {connectsConstParam}) else: p.s.add VarIndex(kind: isEmptyRoot) @@ -56,31 +79,40 @@ proc variableId(p: Partitions; x: PSym): int {.inline.} = system.find(p.symToId, proc root(v: var Partitions; start: int): int = result = start + var depth = 0 while v.s[result].kind == dependsOn: result = v.s[result].parent - # path compression: - var it = start - while v.s[it].kind == dependsOn: - let next = v.s[it].parent - v.s[it] = VarIndex(kind: dependsOn, parent: result) - it = next + inc depth + if depth > 0: + # path compression: + var it = start + while v.s[it].kind == dependsOn: + let next = v.s[it].parent + v.s[it] = VarIndex(kind: dependsOn, parent: result) + it = next -proc potentialMutation(v: var Partitions; s: PSym) = +proc potentialMutation(v: var Partitions; s: PSym; info: TLineInfo) = let id = variableId(v, s) if id >= 0: let r = root(v, id) case v.s[r].kind of isEmptyRoot: v.s[r] = VarIndex(kind: isRootOf, graphIndex: v.graphs.len) - v.graphs.add({isMutated}) + v.graphs.add MutationInfo(param: if isConstParam(s): s else: nil, mutatedHere: info, + connectedVia: unknownLineInfo, flags: {isMutated}) of isRootOf: - v.graphs[v.s[r].graphIndex].incl isMutated + let g = addr v.graphs[v.s[r].graphIndex] + if g.param == nil and isConstParam(s): + g.param = s + if g.mutatedHere == unknownLineInfo: + g.mutatedHere = info + g.flags.incl isMutated else: assert false, "cannot happen" else: discard "we are not interested in the mutation" -proc connect(v: var Partitions; a, b: PSym) = +proc connect(v: var Partitions; a, b: PSym; info: TLineInfo) = let aid = variableId(v, a) if aid < 0: return @@ -91,26 +123,37 @@ proc connect(v: var Partitions; a, b: PSym) = let ra = root(v, aid) let rb = root(v, bid) if ra != rb: + var param = PSym(nil) + if isConstParam(a): param = a + elif isConstParam(b): param = b + let paramFlags = - if isConstParam(a) or isConstParam(b): + if param != nil: {connectsConstParam} else: {} # for now we always make 'rb' the slave and 'ra' the master: - let rbFlags = - if v.s[rb].kind == isRootOf: - v.graphs[v.s[rb].graphIndex] - else: - {} + var rbFlags: set[SubgraphFlag] = {} + var mutatedHere = unknownLineInfo + if v.s[rb].kind == isRootOf: + var gb = addr v.graphs[v.s[rb].graphIndex] + if param == nil: param = gb.param + mutatedHere = gb.mutatedHere + rbFlags = gb.flags v.s[rb] = VarIndex(kind: dependsOn, parent: ra) case v.s[ra].kind of isEmptyRoot: v.s[ra] = VarIndex(kind: isRootOf, graphIndex: v.graphs.len) - v.graphs.add(paramFlags + rbFlags) + v.graphs.add MutationInfo(param: param, mutatedHere: mutatedHere, + connectedVia: info, flags: paramFlags + rbFlags) of isRootOf: - v.graphs[v.s[ra].graphIndex].incl paramFlags + rbFlags + var g = addr v.graphs[v.s[ra].graphIndex] + if g.param == nil: g.param = param + if g.mutatedHere == unknownLineInfo: g.mutatedHere = mutatedHere + g.connectedVia = info + g.flags.incl paramFlags + rbFlags else: assert false, "cannot happen" @@ -144,8 +187,6 @@ proc allRoots(n: PNode; result: var seq[PSym]) = let m = getMagic(n) case m of mNone: - # we do significantly better here by using the available escape - # information: if n[0].typ.isNil: return var typ = n[0].typ if typ != nil: @@ -177,12 +218,12 @@ proc deps(p: var Partitions; dest, src: PNode) = allRoots(src, sources) for t in targets: if dest.kind != nkSym: - potentialMutation(p, t) + potentialMutation(p, t, dest.info) proc wrap(t: PType): bool {.nimcall.} = t.kind in {tyRef, tyPtr} if types.searchTypeFor(t.typ, wrap): for s in sources: - connect(p, t, s) + connect(p, t, s, dest.info) proc traverse(p: var Partitions; n: PNode) = case n.kind @@ -224,13 +265,12 @@ proc traverse(p: var Partitions; n: PNode) = if paramType.typ.kind == tyVar: var roots: seq[PSym] allRoots(n, roots) - for r in roots: potentialMutation(p, r) + for r in roots: potentialMutation(p, r, n.info) else: for child in n: traverse(p, child) - -proc mutatesNonVarParameters*(s: PSym; n: PNode): bool = +proc mutatesNonVarParameters*(s: PSym; n: PNode; info: var MutationInfo): bool = var par = Partitions() if s.kind != skMacro: let params = s.typ.n @@ -240,4 +280,4 @@ proc mutatesNonVarParameters*(s: PSym; n: PNode): bool = registerVariable(par, s.ast[resultPos]) traverse(par, n) - result = hasSideEffect(par) + result = hasSideEffect(par, info) diff --git a/doc/manual_experimental.rst b/doc/manual_experimental.rst index b0614885a..43aa84f20 100644 --- a/doc/manual_experimental.rst +++ b/doc/manual_experimental.rst @@ -1844,3 +1844,41 @@ via ``.noSideEffect``. The rules 3 and 4 can also be approximated by a different 5. A global or thread local variable (or a location derived from such a location) can only passed to a parameter of a ``.noSideEffect`` proc. + + +Strict funcs +============ + +Since version 1.4 a stricter definition of "side effect" is available. In addition +to the existing rule that a side effect is calling a function with side effects +the following rule is also enforced: + +Any mutation to an object does count as a side effect if that object is reachable +via a parameter that is not declared as a ``var`` parameter. + +For example: + +.. code-block:: nim + + {.experimental: "strictFuncs".} + + type + Node = ref object + le, ri: Node + data: string + + func len(n: Node): int = + # valid: len does not have side effects + var it = n + while it != nil: + inc result + it = it.ri + + func mut(n: Node) = + let m = n # is the statement that connected the mutation to the parameter + m.data = "yeah" # the mutation is here + # Error: 'mut' can have side effects + # an object reachable from 'n' is potentially mutated + + +The algorithm behind this analysis is currently not documented. diff --git a/tests/effects/tfuncs_cannot_mutate.nim b/tests/effects/tfuncs_cannot_mutate.nim index ec3ad43f7..2990894ed 100644 --- a/tests/effects/tfuncs_cannot_mutate.nim +++ b/tests/effects/tfuncs_cannot_mutate.nim @@ -1,6 +1,9 @@ discard """ errormsg: "'mutate' can have side effects" - line: 25 + nimout: '''an object reachable from 'n' is potentially mutated +tfuncs_cannot_mutate.nim(34, 15) the mutation is here +tfuncs_cannot_mutate.nim(32, 7) is the statement that connected the mutation to the parameter''' + line: 28 """ {.experimental: "strictFuncs".} From 080bed8b6b478699f740b61915f9e06ab30554d6 Mon Sep 17 00:00:00 2001 From: Danil Yarantsev Date: Sat, 25 Jul 2020 21:35:20 +0300 Subject: [PATCH 0461/3768] Add a test-case for #12990 (#15072) * closes #12990 * Add a test-case for #12990 --- tests/arc/amodule.nim | 13 ++++++++++++- tests/arc/tamodule.nim | 5 ++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/tests/arc/amodule.nim b/tests/arc/amodule.nim index e853d0a77..2b4204a66 100644 --- a/tests/arc/amodule.nim +++ b/tests/arc/amodule.nim @@ -1,3 +1,4 @@ +# bug #14219 var vectors = @["a", "b", "c", "d", "e"] iterator testVectors(): string = @@ -7,4 +8,14 @@ iterator testVectors(): string = var r = "" for item in testVectors(): r.add item -echo r \ No newline at end of file +echo r + +# bug #12990 +iterator test(): int {.closure.} = + yield 0 + +let x = test +while true: + let val = x() + if finished(x): break + echo val diff --git a/tests/arc/tamodule.nim b/tests/arc/tamodule.nim index ac9757bd7..1412e0a7c 100644 --- a/tests/arc/tamodule.nim +++ b/tests/arc/tamodule.nim @@ -1,5 +1,8 @@ discard """ - output: "abcde" + output: ''' +abcde +0 +''' cmd: "nim c --gc:arc $file" """ From 33c2d7bfc4fa5ab34fad35c11a384e6945aa6281 Mon Sep 17 00:00:00 2001 From: alaviss Date: Sat, 25 Jul 2020 20:52:18 +0000 Subject: [PATCH 0462/3768] koch: bundle nim-lang/fusion with Nim (#15061) Initial work on bundling nim-lang/fusion as part of the Nim distribution. --- koch.nim | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/koch.nim b/koch.nim index 1d1fdf997..3afc20189 100644 --- a/koch.nim +++ b/koch.nim @@ -11,6 +11,7 @@ const NimbleStableCommit = "e5d340df8cf26f89ad2c9f0bc081ecd7e0323b0a" # master + FusionStableCommit = "88100a2f45860e98850f7a62fe0b11c37c7a86a1" when not defined(windows): const @@ -50,8 +51,10 @@ Usage: koch [options] command [options for command] Options: --help, -h shows this help and quits - --latest bundle the installers with a bleeding edge Nimble - --stable bundle the installers with a stable Nimble (default) + --latest bundle the installers with bleeding edge versions of + external components. + --stable bundle the installers with stable versions of + external components (default). --nim:path use specified path for nim binary --localdocs[:path] only build local documentations. If a path is not specified (or empty), the default is used. @@ -59,9 +62,11 @@ Possible Commands: boot [options] bootstraps with given command line options distrohelper [bindir] helper for distro packagers tools builds Nim related tools - toolsNoNimble builds Nim related tools (except nimble) + toolsNoExternal builds Nim related tools (except external tools, + ie. nimble) doesn't require network connectivity nimble builds the Nimble tool + fusion clone fusion into the working tree Boot options: -d:release produce a release version of the compiler -d:nimUseLinenoise use the linenoise library for interactive mode @@ -194,7 +199,13 @@ proc bundleWinTools(args: string) = nimCompile(r"tools\downloader.nim", options = r"--cc:vcc --app:gui -d:ssl --noNimblePath --path:..\ui " & args) +proc bundleFusion(latest: bool) = + let commit = if latest: "HEAD" else: FusionStableCommit + cloneDependency(distDir, "https://github.com/nim-lang/fusion.git", commit) + copyDir(distDir / "fusion" / "src" / "fusion", "lib" / "fusion") + proc zip(latest: bool; args: string) = + bundleFusion(latest) bundleNimbleExe(latest, args) bundleNimsuggest(args) bundleNimpretty(args) @@ -234,6 +245,7 @@ proc buildTools(args: string = "") = options = "-d:release " & args) proc nsis(latest: bool; args: string) = + bundleFusion(latest) bundleNimbleExe(latest, args) bundleNimsuggest(args) bundleWinTools(args) @@ -693,15 +705,18 @@ when isMainModule: of "wintools": bundleWinTools(op.cmdLineRest) of "nimble": buildNimble(latest, op.cmdLineRest) of "nimsuggest": bundleNimsuggest(op.cmdLineRest) - of "toolsnonimble": + # toolsNoNimble is kept for backward compatibility with build scripts + of "toolsnonimble", "toolsnoexternal": buildTools(op.cmdLineRest) of "tools": buildTools(op.cmdLineRest) buildNimble(latest, op.cmdLineRest) + bundleFusion(latest) of "pushcsource", "pushcsources": pushCsources() of "valgrind": valgrind(op.cmdLineRest) of "c2nim": bundleC2nim(op.cmdLineRest) of "drnim": buildDrNim(op.cmdLineRest) + of "fusion": bundleFusion(latest) else: showHelp() break of cmdEnd: break From 5c4e2bf68e067507ce0f26c93737b302bbeb5103 Mon Sep 17 00:00:00 2001 From: Miran Date: Sat, 25 Jul 2020 22:56:37 +0200 Subject: [PATCH 0463/3768] fix nightlies: smaller log files (#15074) Hopefully this will fix "The job exceeded the maximum log length, and has been terminated." --- tools/kochdocs.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/kochdocs.nim b/tools/kochdocs.nim index 8a8406a69..05ddb4f1e 100644 --- a/tools/kochdocs.nim +++ b/tools/kochdocs.nim @@ -8,7 +8,7 @@ const gaCode* = " --doc.googleAnalytics:UA-48159761-1" # errormax: subsequent errors are probably consequences of 1st one; a simple # bug could cause unlimited number of errors otherwise, hard to debug in CI. - nimArgs = "--errormax:3 --hint:Conf:off --hint:Path:off --hint:Processing:off -d:boot --putenv:nimversion=$#" % system.NimVersion + nimArgs = "--errormax:3 --hint:Conf:off --hint:Path:off --hint:Processing:off --hint:XDeclaredButNotUsed:off --warning:UnusedImport:off -d:boot --putenv:nimversion=$#" % system.NimVersion gitUrl = "https://github.com/nim-lang/Nim" docHtmlOutput = "doc/html" webUploadOutput = "web/upload" From 624762cfb7ab17606616f75ca280b83c55c3ec3b Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 25 Jul 2020 20:26:30 +0200 Subject: [PATCH 0464/3768] fixes #15052 --- compiler/ccgexprs.nim | 11 +++++++++++ tests/arc/tarcmisc.nim | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 003b3e240..73cc2c8a1 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -729,6 +729,15 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc) = else: putIntoDest(p, d, e, "(*$1)" % [rdLoc(a)], a.storage) +proc cow(p: BProc; n: PNode) = + if n.kind == nkHiddenAddr and optSeqDestructors in p.config.globalOptions: + if n[0].kind == nkBracketExpr: + let strCandidate = n[0][0] + if strCandidate.typ.skipTypes(abstractInst).kind == tyString: + var a: TLoc + initLocExpr(p, strCandidate, a) + linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)]) + proc genAddr(p: BProc, e: PNode, d: var TLoc) = # careful 'addr(myptrToArray)' needs to get the ampersand: if e[0].typ.skipTypes(abstractInstOwned).kind in {tyRef, tyPtr}: @@ -2699,9 +2708,11 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = of nkReturnStmt: genReturnStmt(p, n) of nkBreakStmt: genBreakStmt(p, n) of nkAsgn: + cow(p, n[1]) if nfPreventCg notin n.flags: genAsgn(p, n, fastAsgn=false) of nkFastAsgn: + cow(p, n[1]) if nfPreventCg notin n.flags: # transf is overly aggressive with 'nkFastAsgn', so we work around here. # See tests/run/tcnstseq3 for an example that would fail otherwise. diff --git a/tests/arc/tarcmisc.nim b/tests/arc/tarcmisc.nim index cb889db15..d60e932a8 100644 --- a/tests/arc/tarcmisc.nim +++ b/tests/arc/tarcmisc.nim @@ -24,6 +24,7 @@ whiley ends :( new line before - @['a'] new line after - @['a'] finalizer +aaaaa closed destroying variable: 20 destroying variable: 10 @@ -282,3 +283,13 @@ proc hello(): int = var leaves {.global.} = hello() doAssert leaves == 42 + +# bug #15052 + +proc mutstrings = + var data = "hello" + for c in data.mitems(): + c = 'a' + echo data + +mutstrings() From aae998feff6d0234ee02311d59b299adfee0b01c Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 25 Jul 2020 22:29:45 +0200 Subject: [PATCH 0465/3768] fixes #15038 [backport:1.2] --- compiler/injectdestructors.nim | 47 +--------------------------------- compiler/sempass2.nim | 2 ++ tests/arc/tarcmisc.nim | 26 +++++++++++++++++++ 3 files changed, 29 insertions(+), 46 deletions(-) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 01f72744a..aaa3db7f9 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -62,52 +62,6 @@ proc getTemp(c: var Con; s: var Scope; typ: PType; info: TLineInfo): PNode = proc nestedScope(parent: var Scope): Scope = Scope(vars: @[], wasMoved: @[], final: @[], needsTry: false, parent: addr(parent)) -proc optimize(s: var Scope) = - # optimize away simple 'wasMoved(x); destroy(x)' pairs. - #[ Unfortunately this optimization is only really safe when no exceptions - are possible, see for example: - - proc main(inp: string; cond: bool) = - if cond: - try: - var s = ["hi", inp & "more"] - for i in 0..4: - use s - consume(s) - wasMoved(s) - finally: - destroy(x) - - Now assume 'use' raises, then we shouldn't do the 'wasMoved(s)' - ]# - proc findCorrespondingDestroy(final: seq[PNode]; moved: PNode): int = - # remember that it's destroy(addr(x)) - for i in 0 ..< final.len: - if final[i] != nil and exprStructuralEquivalent(final[i][1].skipAddr, moved, strictSymEquality = true): - return i - return -1 - - var removed = 0 - for i in 0 ..< s.wasMoved.len: - let j = findCorrespondingDestroy(s.final, s.wasMoved[i][1]) - if j >= 0: - s.wasMoved[i] = nil - s.final[j] = nil - inc removed - if removed > 0: - template filterNil(field) = - var m = newSeq[PNode](s.field.len - removed) - var mi = 0 - for i in 0 ..< s.field.len: - if s.field[i] != nil: - m[mi] = s.field[i] - inc mi - assert mi == m.len - s.field = m - - filterNil(wasMoved) - filterNil(final) - proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope; isDecl = false): PNode @@ -411,6 +365,7 @@ proc genDefaultCall(t: PType; c: Con; info: TLineInfo): PNode = proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): 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) diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index f6fce712a..e9dcc7d8a 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -840,6 +840,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 nkHiddenAddr, nkAddr: if n[0].kind == nkSym and isLocalVar(tracked, n[0].sym): useVarNoInitCheck(tracked, n[0], n[0].sym) diff --git a/tests/arc/tarcmisc.nim b/tests/arc/tarcmisc.nim index d60e932a8..a7f2dd98b 100644 --- a/tests/arc/tarcmisc.nim +++ b/tests/arc/tarcmisc.nim @@ -25,6 +25,7 @@ new line before - @['a'] new line after - @['a'] finalizer aaaaa +hello closed destroying variable: 20 destroying variable: 10 @@ -293,3 +294,28 @@ proc mutstrings = echo data mutstrings() + +# bug #15038 + +type + Machine = ref object + hello: string + +var machineTypes: seq[tuple[factory: proc(): Machine]] + +proc registerMachine(factory: proc(): Machine) = + var mCreator = proc(): Machine = + result = factory() + + machineTypes.add((factory: mCreator)) + +proc facproc(): Machine = + result = Machine(hello: "hello") + +registerMachine(facproc) + +proc createMachine = + for machine in machineTypes: + echo machine.factory().hello + +createMachine() From c229df95a4831d083c0465f745fd2ad0f64b9863 Mon Sep 17 00:00:00 2001 From: genotrance Date: Sun, 26 Jul 2020 01:44:14 -0500 Subject: [PATCH 0466/3768] Bump nimble (#15077) --- koch.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/koch.nim b/koch.nim index 3afc20189..a54de1860 100644 --- a/koch.nim +++ b/koch.nim @@ -10,7 +10,7 @@ # const - NimbleStableCommit = "e5d340df8cf26f89ad2c9f0bc081ecd7e0323b0a" # master + NimbleStableCommit = "c4b9c6e459e72c5b04e0beb993b2a912a4078cb5" # master FusionStableCommit = "88100a2f45860e98850f7a62fe0b11c37c7a86a1" when not defined(windows): From a0049d1fb0c997053ffafe8e53e8be6f611c4695 Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 27 Jul 2020 08:05:52 +0200 Subject: [PATCH 0467/3768] compiler: minor code cleanups --- compiler/cursor_inference.nim | 2 +- compiler/importer.nim | 3 +-- compiler/isolation_check.nim | 2 +- compiler/suggest.nim | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/compiler/cursor_inference.nim b/compiler/cursor_inference.nim index b26a29d4c..0402febec 100644 --- a/compiler/cursor_inference.nim +++ b/compiler/cursor_inference.nim @@ -23,7 +23,7 @@ ## do a compile-time copy-on-write analysis. import - ast, types, renderer, idents, intsets, options, msgs + ast, types, renderer, idents, intsets, options type Cursor = object diff --git a/compiler/importer.nim b/compiler/importer.nim index 844cb1ad0..34e76d7d9 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -187,10 +187,9 @@ proc impMod(c: PContext; it: PNode; importStmtResult: PNode) = let it = transformImportAs(c, it) let m = myImportModule(c, it, importStmtResult) if m != nil: - var emptySet: IntSet # ``addDecl`` needs to be done before ``importAllSymbols``! addDecl(c, m, it.info) # add symbol to symbol table of module - importAllSymbolsExcept(c, m, emptySet) + importAllSymbols(c, m) #importForwarded(c, m.ast, emptySet, m) proc evalImport*(c: PContext, n: PNode): PNode = diff --git a/compiler/isolation_check.nim b/compiler/isolation_check.nim index 2b53bce92..f3b0c4919 100644 --- a/compiler/isolation_check.nim +++ b/compiler/isolation_check.nim @@ -11,7 +11,7 @@ ## https://github.com/nim-lang/RFCs/issues/244 for more details. import - ast, types, renderer, idents, intsets, options, msgs + ast, types, renderer, idents, intsets proc canAlias(arg, ret: PType; marker: var IntSet): bool diff --git a/compiler/suggest.nim b/compiler/suggest.nim index 6e9844759..03c65cd42 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -558,7 +558,7 @@ proc markUsed(c: PContext; info: TLineInfo; s: PSym) = warnAboutDeprecated(conf, info, s) if {sfDeprecated, sfError} * s.flags != {}: if sfDeprecated in s.flags: - if not (c.lastTLineInfo.line == info.line and + if not (c.lastTLineInfo.line == info.line and c.lastTLineInfo.col == info.col): warnAboutDeprecated(conf, info, s) c.lastTLineInfo = info From a62bc5595598d6cc0f1e4edf1458fdbbda025572 Mon Sep 17 00:00:00 2001 From: Jjp137 Date: Sun, 26 Jul 2020 23:17:23 -0700 Subject: [PATCH 0468/3768] parsecsv: fix '\0' being displayed as '0' in docs (#15086) [backport] --- lib/pure/parsecsv.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pure/parsecsv.nim b/lib/pure/parsecsv.nim index 44703e3f8..607702fc0 100644 --- a/lib/pure/parsecsv.nim +++ b/lib/pure/parsecsv.nim @@ -108,10 +108,10 @@ proc open*(my: var CsvParser, input: Stream, filename: string, ## the diverse optional parameters: ## - `separator`: character used to separate fields ## - `quote`: Used to quote fields containing special characters like - ## `separator`, `quote` or new-line characters. '\0' disables the parsing + ## `separator`, `quote` or new-line characters. '\\0' disables the parsing ## of quotes. ## - `escape`: removes any special meaning from the following character; - ## '\0' disables escaping; if escaping is disabled and `quote` is not '\0', + ## '\\0' disables escaping; if escaping is disabled and `quote` is not '\\0', ## two `quote` characters are parsed one literal `quote` character. ## - `skipInitialSpace`: If true, whitespace immediately following the ## `separator` is ignored. From c292c57e48c719875a37fa50d2ca6a3d3639ae2e Mon Sep 17 00:00:00 2001 From: alaviss Date: Mon, 27 Jul 2020 08:38:20 +0000 Subject: [PATCH 0469/3768] Small optimization for the CI pipeline. (#15088) * azure-pipelines: cache csources to speed up build Building csources takes about 2-4 mins, and since it hasn't changed for a long time, employ some caching to cut the time spent. * azure-pipelines: remove ppa avoidance configs Testing shows that there aren't any difference between having this and not having it, so removing this to simplify the code. --- azure-pipelines.yml | 40 +++++++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 15 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index fd8b2eea8..109efc6e1 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -53,9 +53,10 @@ jobs: condition: eq(variables['Agent.OS'], 'Windows_NT') - checkout: self + fetchDepth: 1 - - bash: git clone --depth 1 https://github.com/nim-lang/csources.git - displayName: 'Checkout csources' + - bash: git clone --depth 1 https://github.com/nim-lang/csources + displayName: 'Checkout Nim csources' - task: NodeTool@0 inputs: @@ -73,19 +74,12 @@ jobs: - bash: | sudo dpkg --add-architecture i386 - # Downgrade llvm, libgcc and libstdc++: + # Downgrade llvm: # - llvm has to be downgraded to have 32bit version installed for sfml. - # - libgcc and libstdc++ have to be downgraded as an optimization to - # prevent the use of the toolchain ppa, which has a terrible download - # speed. cat << EOF | sudo tee /etc/apt/preferences.d/pin-to-rel - Package: libllvm6.0 libgcc1 libstdc++6 + Package: libllvm6.0 Pin: origin "azure.archive.ubuntu.com" Pin-Priority: 1001 - - Package: * - Pin: release o=LP-PPA-ubuntu-toolchain-r-test - Pin-Priority: 100 EOF sudo apt-fast update -qq @@ -141,8 +135,18 @@ jobs: make -v displayName: 'System information' + - bash: echo '##vso[task.setvariable variable=csources_version]'"$(git -C csources rev-parse HEAD)" + displayName: 'Get csources version' + + - task: Cache@2 + inputs: + key: 'csources | "$(Agent.OS)" | $(CPU) | $(csources_version)' + path: csources/bin + displayName: 'Restore built csources' + - bash: | ncpu= + ext= case '$(Agent.OS)' in 'Linux') ncpu=$(nproc) @@ -152,19 +156,25 @@ jobs: ;; 'Windows_NT') ncpu=$NUMBER_OF_PROCESSORS + ext=.exe ;; esac [[ -z "$ncpu" || $ncpu -le 0 ]] && ncpu=1 - make -C csources -j $ncpu CC=gcc ucpu=$(CPU) - displayName: 'Build csources' + if [[ -x csources/bin/nim$ext ]]; then + echo "Found cached compiler, skipping build" + else + make -C csources -j $ncpu CC=gcc ucpu=$(CPU) koch=no + fi + + cp csources/bin/nim$ext bin + displayName: 'Build 1-stage compiler from csources' - bash: nim c koch displayName: 'Build koch' # set result to omit the "bash exited with error code '1'" message - - bash: | - ./koch runCI || echo '##vso[task.complete result=Failed]' + - bash: ./koch runCI || echo '##vso[task.complete result=Failed]' displayName: 'Run CI' env: SYSTEM_ACCESSTOKEN: $(System.AccessToken) From 4c43915b5911b4b1d7cf35e3ba156fabde58ed84 Mon Sep 17 00:00:00 2001 From: Danil Yarantsev Date: Mon, 27 Jul 2020 14:01:50 +0300 Subject: [PATCH 0470/3768] Add test-cases for #12576 and #12523 (#15085) * Add a test-case for #12576 * Add a test-case for #12523 --- tests/iter/titer_issues.nim | 15 +++++++++++++-- tests/pragmas/tcustom_pragma.nim | 25 +++++++++++++++++++++++-- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/tests/iter/titer_issues.nim b/tests/iter/titer_issues.nim index 872ebe2b7..4a76b2fb0 100644 --- a/tests/iter/titer_issues.nim +++ b/tests/iter/titer_issues.nim @@ -27,6 +27,8 @@ end 9014 9016 9018 +@[1, 2] +@[1, 2, 3] ''' """ @@ -191,7 +193,7 @@ block t3499_keepstate: break # bug #3499 last snippet fixed - # bug 705 last snippet fixed + # bug #705 last snippet fixed @@ -225,7 +227,7 @@ block t2023_objiter: block: - # issue #13739 + # bug #13739 iterator myIter(arg: openarray[int]): int = var tmp = 0 let len = arg.len @@ -240,3 +242,12 @@ block: echo x someProc() + +block: + # bug #12576 + iterator ff(sq: varargs[seq[int]]): int = + for x in sq: + echo x + + for x in ff(@[1, 2], @[1, 2, 3]): + echo x diff --git a/tests/pragmas/tcustom_pragma.nim b/tests/pragmas/tcustom_pragma.nim index b306045e0..756396529 100644 --- a/tests/pragmas/tcustom_pragma.nim +++ b/tests/pragmas/tcustom_pragma.nim @@ -336,7 +336,7 @@ ProcDef static: assert bar("x") == "x" #------------------------------------------------------ -# issue #13909 +# bug #13909 template dependency*(id: string, weight = 0.0) {.pragma.} @@ -345,4 +345,25 @@ type provider*: proc(obj: string): pointer {.dependency("Data/" & obj, 16.1), noSideEffect.} proc myproc(obj: string): string {.dependency("Data/" & obj, 16.1).} = - result = obj \ No newline at end of file + result = obj + +# bug 12523 +template myCustomPragma {.pragma.} + +type + RefType = ref object + field {.myCustomPragma.}: int + + ObjType = object + field {.myCustomPragma.}: int + RefType2 = ref ObjType + +block: + let x = RefType() + for fieldName, fieldSym in fieldPairs(x[]): + doAssert hasCustomPragma(fieldSym, myCustomPragma) + +block: + let x = RefType2() + for fieldName, fieldSym in fieldPairs(x[]): + doAssert hasCustomPragma(fieldSym, myCustomPragma) From 318f0992eaa6ddce1c306e695f95f49b319c53c7 Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 27 Jul 2020 11:44:12 +0200 Subject: [PATCH 0471/3768] fixes a closure iterator memory leaks, progress on #15076 --- compiler/injectdestructors.nim | 2 +- tests/arc/tclosureiter.nim | 36 ++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 tests/arc/tclosureiter.nim diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index aaa3db7f9..231ece0ce 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -787,7 +787,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode = if ri.kind == nkEmpty and c.inLoop > 0: ri = genDefaultCall(v.typ, c, v.info) if ri.kind != nkEmpty: - result.add moveOrCopy(v, ri, c, s, isDecl = true) + result.add moveOrCopy(v, ri, c, s, isDecl = false) else: # keep the var but transform 'ri': var v = copyNode(n) var itCopy = copyNode(it) diff --git a/tests/arc/tclosureiter.nim b/tests/arc/tclosureiter.nim new file mode 100644 index 000000000..e4a2ceac6 --- /dev/null +++ b/tests/arc/tclosureiter.nim @@ -0,0 +1,36 @@ +discard """ + cmd: '''nim c -d:nimAllocStats --gc:arc $file''' + output: '''(allocCount: 102, deallocCount: 102)''' +""" + +type + FutureBase = ref object + someData: string + +const + # Just to occupy some RAM + BigData = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + +iterator mainIter(): FutureBase {.closure.} = + for x in 0 .. 100: + var internalTmpFuture = FutureBase(someData: BigData) + yield internalTmpFuture + +proc main() = + var nameIterVar = mainIter + var next = nameIterVar() + while not isNil(next): + next = nameIterVar() + if not isNil(next): + doAssert next.someData.len == 97 + # GC_unref(next) + # If you uncomment the GC_ref above, + # the program basically uses no memory after the run. + # but crashes with refc, which might indicate + # that arc/orc simply never frees the result of "next"? + if finished(nameIterVar): + break + +main() +GC_fullCollect() +echo getAllocStats() From 46bd6787b8d81c8c8abd742c92380989938a33a3 Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 27 Jul 2020 11:56:53 +0200 Subject: [PATCH 0472/3768] code cleanup --- lib/system/cyclicrefs_v2.nim | 207 ------------------- lib/system/{cyclicrefs_bacon.nim => orc.nim} | 0 lib/system/refs_v2.nim | 3 +- 3 files changed, 1 insertion(+), 209 deletions(-) delete mode 100644 lib/system/cyclicrefs_v2.nim rename lib/system/{cyclicrefs_bacon.nim => orc.nim} (100%) diff --git a/lib/system/cyclicrefs_v2.nim b/lib/system/cyclicrefs_v2.nim deleted file mode 100644 index 564562c99..000000000 --- a/lib/system/cyclicrefs_v2.nim +++ /dev/null @@ -1,207 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2019 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -# Cycle collector based on Lins' Jump Stack and other ideas, -# see for example: -# https://pdfs.semanticscholar.org/f2b2/0d168acf38ff86305809a55ef2c5d6ebc787.pdf -# Further refinement in 2008 by the notion of "critical links", see -# "Cyclic reference counting" by Rafael Dueire Lins -# R.D. Lins / Information Processing Letters 109 (2008) 71–78 - -type PT = ptr pointer -include cellseqs_v2 - -const - colGreen = 0b000 - colYellow = 0b001 - colRed = 0b010 - jumpStackFlag = 0b100 # stored in jumpstack - rcShift = 3 # shift by rcShift to get the reference counter - colorMask = 0b011 - -type - TraceProc = proc (p, env: pointer) {.nimcall, benign.} - DisposeProc = proc (p: pointer) {.nimcall, benign.} - -template color(c): untyped = c.rc and colorMask -template setColor(c, col) = - when col == colGreen: - c.rc = c.rc and not colorMask - else: - c.rc = c.rc and not colorMask or col - -proc nimIncRefCyclic(p: pointer) {.compilerRtl, inl.} = - let h = head(p) - inc h.rc, rcIncrement - h.setColor colYellow # mark as potential cycle! - -proc markCyclic*[T](x: ref T) {.inline.} = - ## Mark the underlying object as a candidate for cycle collections. - ## Experimental API. Do not use! - let h = head(cast[pointer](x)) - h.setColor colYellow - -type - GcEnv = object - traceStack: CellSeq - jumpStack: CellSeq - -proc trace(s: Cell; desc: PNimType; j: var GcEnv) {.inline.} = - if desc.traceImpl != nil: - var p = s +! sizeof(RefHeader) - cast[TraceProc](desc.traceImpl)(p, addr(j)) - -proc free(s: Cell; desc: PNimType) {.inline.} = - when traceCollector: - cprintf("[From ] %p rc %ld color %ld in jumpstack %ld\n", s, s.rc shr rcShift, - s.color, s.rc and jumpStackFlag) - let p = s +! sizeof(RefHeader) - if desc.disposeImpl != nil: - cast[DisposeProc](desc.disposeImpl)(p) - nimRawDispose(p) - -proc collect(s: Cell; desc: PNimType; j: var GcEnv) = - #[ Algorithm from the paper: - - Collect(S) = - If (Color(S) == red) then - RC(S) = 1; - Color(S) = green; - for T in Sons(S) do - Remove(); - if (Color(T) == red) then Collect(T); - free_list = S; - - (Whatever that really means...) - ]# - if s.color == colRed: - s.setColor colGreen - trace(s, desc, j) - while j.traceStack.len > 0: - let (p, desc) = j.traceStack.pop() - let t = head(p[]) - #Remove(): - p[] = nil - if t.color == colRed: - t.setColor colGreen - trace(t, desc, j) - free(t, desc) - free(s, desc) - #cprintf("[Cycle free] %p %ld\n", s, s.rc shr rcShift) - -proc markRed(s: Cell; desc: PNimType; j: var GcEnv) = - if s.color != colRed: - s.setColor colRed - trace(s, desc, j) - while j.traceStack.len > 0: - let (p, desc) = j.traceStack.pop() - let t = head(p[]) - when traceCollector: - cprintf("[Cycle dec] %p %ld color %ld in jumpstack %ld\n", t, t.rc shr rcShift, t.color, t.rc and jumpStackFlag) - dec t.rc, rcIncrement - if (t.rc and not rcMask) >= 0 and (t.rc and jumpStackFlag) == 0: - t.rc = t.rc or jumpStackFlag - when traceCollector: - cprintf("[Now in jumpstack] %p %ld color %ld in jumpstack %ld\n", t, t.rc shr rcShift, t.color, t.rc and jumpStackFlag) - j.jumpStack.add(p, desc) - if t.color != colRed: - t.setColor colRed - trace(t, desc, j) - -proc scanGreen(s: Cell; desc: PNimType; j: var GcEnv) = - s.setColor colGreen - trace(s, desc, j) - while j.traceStack.len > 0: - let (p, desc) = j.traceStack.pop() - let t = head(p[]) - if t.color != colGreen: - t.setColor colGreen - trace(t, desc, j) - inc t.rc, rcIncrement - when traceCollector: - cprintf("[Cycle inc] %p %ld color %ld\n", t, t.rc shr rcShift, t.color) - -proc nimTraceRef(q: pointer; desc: PNimType; env: pointer) {.compilerRtl.} = - let p = cast[ptr pointer](q) - if p[] != nil: - var j = cast[ptr GcEnv](env) - j.traceStack.add(p, desc) - -proc nimTraceRefDyn(q: pointer; env: pointer) {.compilerRtl.} = - let p = cast[ptr pointer](q) - if p[] != nil: - var j = cast[ptr GcEnv](env) - j.traceStack.add(p, cast[ptr PNimType](p[])[]) - -proc scan(s: Cell; desc: PNimType; j: var GcEnv) = - when traceCollector: - cprintf("[doScanGreen] %p %ld\n", s, s.rc shr rcShift) - # even after trial deletion, `s` is still alive, so undo - # the decrefs by calling `scanGreen`: - if (s.rc and not rcMask) >= 0: - scanGreen(s, desc, j) - s.setColor colYellow - else: - # first we have to repair all the nodes we have seen - # that are still alive; we also need to mark what they - # refer to as alive: - while j.jumpStack.len > 0: - let (p, desc) = j.jumpStack.pop - let t = head(p[]) - # not in jump stack anymore! - t.rc = t.rc and not jumpStackFlag - if t.color == colRed and (t.rc and not rcMask) >= 0: - scanGreen(t, desc, j) - t.setColor colYellow - when traceCollector: - cprintf("[jump stack] %p %ld\n", t, t.rc shr rcShift) - # we have proven that `s` and its subgraph are dead, so we can - # collect these nodes: - collect(s, desc, j) - -proc traceCycle(s: Cell; desc: PNimType) {.noinline.} = - when traceCollector: - cprintf("[traceCycle] %p %ld\n", s, s.rc shr rcShift) - var j: GcEnv - init j.jumpStack - init j.traceStack - markRed(s, desc, j) - scan(s, desc, j) - while j.jumpStack.len > 0: - let (p, desc) = j.jumpStack.pop - let t = head(p[]) - # not in jump stack anymore! - t.rc = t.rc and not jumpStackFlag - deinit j.jumpStack - deinit j.traceStack - -proc nimDecRefIsLastCyclicDyn(p: pointer): bool {.compilerRtl, inl.} = - if p != nil: - var cell = head(p) - if (cell.rc and not rcMask) == 0: - result = true - #cprintf("[DESTROY] %p\n", p) - else: - dec cell.rc, rcIncrement - if cell.color == colYellow: - let desc = cast[ptr PNimType](p)[] - traceCycle(cell, desc) - # According to Lins it's correct to do nothing else here. - #cprintf("[DeCREF] %p\n", p) - -proc nimDecRefIsLastCyclicStatic(p: pointer; desc: PNimType): bool {.compilerRtl, inl.} = - if p != nil: - var cell = head(p) - if (cell.rc and not rcMask) == 0: - result = true - #cprintf("[DESTROY] %p %s\n", p, desc.name) - else: - dec cell.rc, rcIncrement - if cell.color == colYellow: traceCycle(cell, desc) - #cprintf("[DeCREF] %p %s %ld\n", p, desc.name, cell.rc) diff --git a/lib/system/cyclicrefs_bacon.nim b/lib/system/orc.nim similarity index 100% rename from lib/system/cyclicrefs_bacon.nim rename to lib/system/orc.nim diff --git a/lib/system/refs_v2.nim b/lib/system/refs_v2.nim index df1248586..7fe17d3ea 100644 --- a/lib/system/refs_v2.nim +++ b/lib/system/refs_v2.nim @@ -180,9 +180,8 @@ when defined(gcOrc): when defined(nimThinout): include cyclebreaker else: - include cyclicrefs_bacon + include orc #include cyclecollector - #include cyclicrefs_v2 proc nimDecRefIsLast(p: pointer): bool {.compilerRtl, inl.} = if p != nil: From 947ecd1257f78e8ee723bf3a6806b8f24642a064 Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 27 Jul 2020 11:58:31 +0200 Subject: [PATCH 0473/3768] more renamings --- lib/system.nim | 6 +++--- lib/system/{refs_v2.nim => arc.nim} | 0 2 files changed, 3 insertions(+), 3 deletions(-) rename lib/system/{refs_v2.nim => arc.nim} (100%) diff --git a/lib/system.nim b/lib/system.nim index 71eed5bee..93386c4ae 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1423,8 +1423,8 @@ proc toBiggestInt*(f: BiggestFloat): BiggestInt {.noSideEffect.} = ## Same as `toInt <#toInt,float>`_ but for ``BiggestFloat`` to ``BiggestInt``. if f >= 0: BiggestInt(f+0.5) else: BiggestInt(f-0.5) -proc addQuitProc*(quitProc: proc() {.noconv.}) {. - importc: "atexit", header: "", deprecated: "use exitprocs.addExitProc".} +proc addQuitProc*(quitProc: proc() {.noconv.}) {. + importc: "atexit", header: "", deprecated: "use exitprocs.addExitProc".} ## Adds/registers a quit procedure. ## ## Each call to ``addQuitProc`` registers another quit procedure. Up to 30 @@ -1752,7 +1752,7 @@ when notJSnotNims: when defined(nimV2): - include system/refs_v2 + include system/arc import system/assertions export assertions diff --git a/lib/system/refs_v2.nim b/lib/system/arc.nim similarity index 100% rename from lib/system/refs_v2.nim rename to lib/system/arc.nim From 191c388792c1b57c3855e9c3b9c83293be8a6207 Mon Sep 17 00:00:00 2001 From: Bung Date: Mon, 27 Jul 2020 20:06:54 +0800 Subject: [PATCH 0474/3768] fixes #14189 (#15080) [backport] --- compiler/semtypes.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 4001953bc..588a0f4f6 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -670,7 +670,7 @@ 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: @@ -678,7 +678,7 @@ proc semRecordCase(c: PContext, n: PNode, check: var IntSet, pos: var int, 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") From cac09a43aea0e8bcc6a7f2634cfcd4274a260b49 Mon Sep 17 00:00:00 2001 From: Bung Date: Tue, 28 Jul 2020 03:13:49 +0800 Subject: [PATCH 0475/3768] =?UTF-8?q?fix=20#11354=20jsgen=20not=20carefull?= =?UTF-8?q?y=20handle=20genAddr=20with=20nkHiddenAddr,nkStm=E2=80=A6=20(#1?= =?UTF-8?q?5078)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix #11354 jsgen not carefully handle genAddr with nkHiddenAddr,nkStmtListExpr; genAsgn with lvalue tyVar and rvalue tyPtr * correct logic * add test for #11354 * handle nkHiddenAddr when n.len == 1 * Update compiler/jsgen.nim * Update compiler/jsgen.nim * Apply suggestions from code review Co-authored-by: Andreas Rumpf --- compiler/jsgen.nim | 11 ++++++++++- tests/js/t11354.nim | 20 ++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 tests/js/t11354.nim diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 3351d64ae..ede4d14e0 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -1069,6 +1069,10 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = elif b.typ == etyNone: internalAssert p.config, b.address == nil lineF(p, "$# = [$#, 0];$n", [a.address, b.res]) + elif x.typ.kind == tyVar and y.typ.kind == tyPtr: + lineF(p, "$# = [$#, $#];$n", [a.res, b.address, b.res]) + lineF(p, "$1 = $2;$n", [a.address, b.res]) + lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc]) else: internalError(p.config, x.info, $("genAsgn", b.typ, a.typ)) else: @@ -1318,7 +1322,12 @@ proc genAddr(p: PProc, n: PNode, r: var TCompRes) = of nkObjDownConv: gen(p, n[0], r) of nkHiddenDeref: - gen(p, n[0][0], r) + gen(p, n[0], r) + of nkHiddenAddr: + gen(p, n[0], r) + of nkStmtListExpr: + if n.len == 1: gen(p, n[0], r) + else: internalError(p.config, n[0].info, "genAddr for complex nkStmtListExpr") else: internalError(p.config, n[0].info, "genAddr: " & $n[0].kind) proc attachProc(p: PProc; content: Rope; s: PSym) = diff --git a/tests/js/t11354.nim b/tests/js/t11354.nim new file mode 100644 index 000000000..8dee90de0 --- /dev/null +++ b/tests/js/t11354.nim @@ -0,0 +1,20 @@ +discard """ + output: ''' +0 +@[@[0, 1]] +''' +""" + +type + TrackySeq[T] = object + s: seq[T] + pos: int + +proc foobar(ls: var TrackySeq[seq[int]], i: int): var seq[int] = + echo ls.pos # removing this, or making the return explicit works + ls.s[i] + +var foo: TrackySeq[seq[int]] +foo.s.add(@[0]) +foo.foobar(0).add(1) +echo foo.s \ No newline at end of file From 377f71676629d5621d052e662996d30323ea3bee Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 27 Jul 2020 13:32:21 -0700 Subject: [PATCH 0476/3768] fix #14698 nkRecWhen caused internalAssert in semConstructFields when generic type not mentioned in fields (#14709) * fix #14698 nkRecWhen caused internalAssert in semConstructFields when generic type not mentioned in fields * address comment * Update compiler/semtypinst.nim Co-authored-by: Andreas Rumpf --- compiler/semtypinst.nim | 5 ++++- tests/objects/tobject.nim | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 22f0d1db6..e7b289d77 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -630,7 +630,10 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = result = t # Slow path, we have some work to do - if result.n != nil and t.kind == tyObject: + if t.kind == tyRef and t.len > 0 and t[0].kind == tyObject and t[0].n != nil: + discard replaceObjBranches(cl, t[0].n) + + elif result.n != nil and t.kind == tyObject: # Invalidate the type size as we may alter its structure result.size = -1 result.n = replaceObjBranches(cl, result.n) diff --git a/tests/objects/tobject.nim b/tests/objects/tobject.nim index 34856eaef..a79c2bdfd 100644 --- a/tests/objects/tobject.nim +++ b/tests/objects/tobject.nim @@ -42,3 +42,23 @@ var b: TMyObj = a type InheritableFoo {.inheritable.} = ref object InheritableBar = ref object of InheritableFoo # ERROR. + +block: # bug #14698 + const N = 3 + type Foo[T] = ref object + x1: int + when N == 2: + x2: float + when N == 3: + x3: seq[int] + else: + x4: char + x4b: array[9, char] + + let t = Foo[float](x1: 1) + doAssert $(t[]) == "(x1: 1, x3: @[])" + doAssert t.sizeof == int.sizeof + type Foo1 = object + x1: int + x3: seq[int] + doAssert t[].sizeof == Foo1.sizeof From e6f0d4a5a59f8230133481d25ce1c3f6e8daf0a5 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 27 Jul 2020 23:27:20 +0200 Subject: [PATCH 0477/3768] fixes #15076 (#15095) --- compiler/injectdestructors.nim | 33 ++++++++++++++++++++------------- compiler/liftdestructors.nim | 2 ++ compiler/sempass2.nim | 29 ++++++++++++++--------------- tests/arc/tasyncleak.nim | 21 +++++++++++++++++++++ 4 files changed, 57 insertions(+), 28 deletions(-) create mode 100644 tests/arc/tasyncleak.nim diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 231ece0ce..fa960b5cf 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -48,6 +48,13 @@ type const toDebug {.strdefine.} = "" +proc hasDestructor(c: Con; t: PType): bool {.inline.} = + result = ast.hasDestructor(t) + when toDebug.len > 0: + # for more effective debugging + if not result and c.graph.config.selectedGC in {gcArc, gcOrc}: + assert(not containsGarbageCollectedRef(t)) + template dbg(body) = when toDebug.len > 0: if c.owner.name.s == toDebug or toDebug == "always": @@ -330,7 +337,7 @@ proc genDiscriminantAsgn(c: var Con; s: var Scope; n: PNode): PNode = let leDotExpr = if le.kind == nkCheckedFieldExpr: le[0] else: le let objType = leDotExpr[0].typ - if hasDestructor(objType): + if hasDestructor(c, objType): if objType.attachedOps[attachedDestructor] != nil and sfOverriden in objType.attachedOps[attachedDestructor].flags: localError(c.graph.config, n.info, errGenerated, """Assignment to discriminant for objects with user defined destructor is not supported, object must have default destructor. @@ -364,8 +371,8 @@ proc genDefaultCall(t: PType; c: Con; info: TLineInfo): PNode = proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): PNode = # generate: (let tmp = v; reset(v); tmp) - if not hasDestructor(n.typ): - assert n.kind != nkSym or not hasDestructor(n.sym.typ) + if not hasDestructor(c, n.typ): + assert n.kind != nkSym or not hasDestructor(c, n.sym.typ) result = copyTree(n) else: result = newNodeIT(nkStmtListExpr, n.info, n.typ) @@ -393,7 +400,7 @@ proc isCapturedVar(n: PNode): bool = proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode = result = newNodeIT(nkStmtListExpr, n.info, n.typ) let tmp = c.getTemp(s, n.typ, n.info) - if hasDestructor(n.typ): + if hasDestructor(c, n.typ): result.add c.genWasMoved(tmp) var m = c.genCopy(tmp, n) m.add p(n, c, s, normal) @@ -432,7 +439,7 @@ proc containsConstSeq(n: PNode): bool = proc ensureDestruction(arg, orig: PNode; c: var Con; s: var Scope): PNode = # it can happen that we need to destroy expression contructors # like [], (), closures explicitly in order to not leak them. - if arg.typ != nil and hasDestructor(arg.typ): + if arg.typ != nil and hasDestructor(c, arg.typ): # produce temp creation for (fn, env). But we need to move 'env'? # This was already done in the sink parameter handling logic. result = newNodeIT(nkStmtListExpr, arg.info, arg.typ) @@ -521,7 +528,7 @@ template processScopeExpr(c: var Con; s: var Scope; ret: PNode, processCall: unt # tricky because you would have to intercept moveOrCopy at a certain point let tmp = c.getTemp(s.parent[], ret.typ, ret.info) tmp.sym.flags.incl sfSingleUsedTemp - let cpy = if ret.typ.hasDestructor: + let cpy = if hasDestructor(c, ret.typ): moveOrCopy(tmp, ret, c, s, isDecl = true) else: newTree(nkFastAsgn, tmp, p(ret, c, s, normal)) @@ -774,10 +781,10 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode = result = newNodeI(nkStmtList, n.info) for it in n: var ri = it[^1] - if it.kind == nkVarTuple and hasDestructor(ri.typ): + if it.kind == nkVarTuple and hasDestructor(c, ri.typ): let x = lowerTupleUnpacking(c.graph, it, c.owner) result.add p(x, c, s, consumed) - elif it.kind == nkIdentDefs and hasDestructor(it[0].typ) and not isCursor(it[0], c): + elif it.kind == nkIdentDefs and hasDestructor(c, it[0].typ) and not isCursor(it[0], c): for j in 0.. 0: - createTypeBoundOps(tracked, n[1].typ.lastSon, n.info) createTypeBoundOps(tracked, n[1].typ, n.info) # new(x, finalizer): Problem: how to move finalizer into 'createTypeBoundOps'? diff --git a/tests/arc/tasyncleak.nim b/tests/arc/tasyncleak.nim new file mode 100644 index 000000000..16e7446a7 --- /dev/null +++ b/tests/arc/tasyncleak.nim @@ -0,0 +1,21 @@ +discard """ + outputsub: "(allocCount: 6013, deallocCount: 6007)" + cmd: "nim c --gc:orc -d:nimAllocStats $file" +""" + +import asyncdispatch +# bug #15076 +const + # Just to occupy some RAM + BigData = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + +proc doNothing(): Future[void] {.async.} = + discard + +proc main(): Future[void] {.async.} = + for x in 0 .. 1_000: + await doNothing() + +waitFor main() +GC_fullCollect() +echo getAllocStats() From d60fae576f3228c1cee360129b687a1e8c6a8ed8 Mon Sep 17 00:00:00 2001 From: Ray Imber Date: Mon, 27 Jul 2020 23:04:04 -0700 Subject: [PATCH 0478/3768] Improvements to Windows install instructions (#15099) --- readme.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/readme.md b/readme.md index 94241822f..4ef10f231 100644 --- a/readme.md +++ b/readme.md @@ -52,6 +52,12 @@ Next, to build from source you will need: * Either ``git`` or ``wget`` to download the needed source repositories. * The ``build-essential`` package when using ``gcc`` on Ubuntu (and likely other distros as well). + * On Windows MinGW 4.3.0 (GCC 8.10) is the minimum recommended compiler. + * Nim hosts a known working MinGW distribution: + * [MinGW32.7z](https://nim-lang.org/download/mingw32.7z) + * [MinGW64.7z](https://nim-lang.org/download/mingw64.7z) + +**Windows Note: Cygwin and similar posix runtime environments are not supported.** Then, if you are on a \*nix system or Windows, the following steps should compile Nim from source using ``gcc``, ``git`` and the ``koch`` build tool. @@ -75,6 +81,9 @@ Next run the appropriate build shell script for your platform: * `build_all.sh` (Linux, Mac) * `build_all.bat` (Windows) +Windows requires a number of other dependencies that you may need to install including +PCRE and OpenSSL. Nim hosts a zip package containing known working versions of the +required DLLs [here](https://nim-lang.org/download/dlls.zip). Finally, once you have finished the build steps (on Windows, Mac or Linux) you should add the ``bin`` directory to your PATH. From d6a0c35330260acf2ebebfe884290ba88971a8a4 Mon Sep 17 00:00:00 2001 From: narimiran Date: Tue, 28 Jul 2020 08:51:10 +0200 Subject: [PATCH 0479/3768] bump FusionStableCommit to the latest commit --- koch.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/koch.nim b/koch.nim index a54de1860..421e5bb0d 100644 --- a/koch.nim +++ b/koch.nim @@ -11,7 +11,7 @@ const NimbleStableCommit = "c4b9c6e459e72c5b04e0beb993b2a912a4078cb5" # master - FusionStableCommit = "88100a2f45860e98850f7a62fe0b11c37c7a86a1" + FusionStableCommit = "166900c74d74f005980baf337f17cb956aeed6d2" when not defined(windows): const From 75083979f90c554e695211f129938046f6e9fe52 Mon Sep 17 00:00:00 2001 From: narimiran Date: Tue, 28 Jul 2020 09:27:00 +0200 Subject: [PATCH 0480/3768] another bump --- koch.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/koch.nim b/koch.nim index 421e5bb0d..f087e305f 100644 --- a/koch.nim +++ b/koch.nim @@ -11,7 +11,7 @@ const NimbleStableCommit = "c4b9c6e459e72c5b04e0beb993b2a912a4078cb5" # master - FusionStableCommit = "166900c74d74f005980baf337f17cb956aeed6d2" + FusionStableCommit = "0ab9466e297393829900c4297193fe66f9d61d2b" when not defined(windows): const From a2f330569c3d7f59371854b76d7f87de95d1a72b Mon Sep 17 00:00:00 2001 From: narimiran Date: Tue, 28 Jul 2020 10:57:24 +0200 Subject: [PATCH 0481/3768] yet another fusion fix --- koch.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/koch.nim b/koch.nim index f087e305f..be658a53e 100644 --- a/koch.nim +++ b/koch.nim @@ -11,7 +11,7 @@ const NimbleStableCommit = "c4b9c6e459e72c5b04e0beb993b2a912a4078cb5" # master - FusionStableCommit = "0ab9466e297393829900c4297193fe66f9d61d2b" + FusionStableCommit = "319aac4d43b04113831b529f8003e82f4af6a4a5" when not defined(windows): const From be78b35bb8ec45e995e2e27058fc925da11db550 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 28 Jul 2020 11:34:44 +0200 Subject: [PATCH 0482/3768] cleanup ARC documentation (#15100) --- doc/advopt.txt | 2 -- doc/destructors.rst | 42 ------------------------------------------ 2 files changed, 44 deletions(-) diff --git a/doc/advopt.txt b/doc/advopt.txt index 6665ce537..9b7f29a5e 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -146,8 +146,6 @@ Advanced options: --legacy:$2 enable obsolete/legacy language feature --useVersion:1.0 emulate Nim version X of the Nim compiler - --newruntime use an alternative runtime that uses destructors - and that uses a shared heap via -d:useMalloc --profiler:on|off enable profiling; requires `import nimprof`, and works better with `--stackTrace:on` see also https://nim-lang.github.io/Nim/estp.html diff --git a/doc/destructors.rst b/doc/destructors.rst index 6cab6e1d2..c97b17d6f 100644 --- a/doc/destructors.rst +++ b/doc/destructors.rst @@ -534,48 +534,6 @@ indirections: useItAgain(v) - -Owned refs -========== - -**Note**: The ``owned`` type constructor is only available with -the ``--newruntime`` compiler switch and is experimental. - - -Let ``W`` be an ``owned ref`` type. Conceptually its hooks look like: - -.. code-block:: nim - - proc `=destroy`(x: var W) = - if x != nil: - assert x.refcount == 0, "dangling unowned pointers exist!" - `=destroy`(x[]) - - proc `=`(x: var W; y: W) {.error: "owned refs can only be moved".} - - proc `=sink`(x: var W; y: W) = - `=destroy`(x) - bitwiseCopy x, y # raw pointer copy - - -Let ``U`` be an unowned ``ref`` type. Conceptually its hooks look like: - -.. code-block:: nim - - proc `=destroy`(x: var U) = - if x != nil: - dec x.refcount - - proc `=`(x: var U; y: U) = - # Note: No need to check for self-assignments here. - if y != nil: inc y.refcount - if x != nil: dec x.refcount - bitwiseCopy x, y # raw pointer copy - - proc `=sink`(x: var U, y: U) {.error.} - # Note: Moves are not available. - - Hook lifting ============ From 39487f9728c03e98109b70c2d169e6366b9a8337 Mon Sep 17 00:00:00 2001 From: Miran Date: Tue, 28 Jul 2020 11:40:56 +0200 Subject: [PATCH 0483/3768] deprecate tables.allValues; continuation of #15047 (#15092) --- lib/pure/collections/tables.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index a8742bdbe..b1354eec3 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -744,7 +744,8 @@ iterator mvalues*[A, B](t: var Table[A, B]): var B = yield t.data[h].val assert(len(t) == L, "the length of the table changed while iterating over it") -iterator allValues*[A, B](t: Table[A, B]; key: A): B = +iterator allValues*[A, B](t: Table[A, B]; key: A): B {.deprecated: + "Deprecated since v1.4; tables with duplicated keys are deprecated".} = ## Iterates over any value in the table ``t`` that belongs to the given ``key``. ## ## Used if you have a table with duplicate keys (as a result of using From e2b149190584e53c662d09aeee9390908bbb250f Mon Sep 17 00:00:00 2001 From: Bung Date: Tue, 28 Jul 2020 17:47:22 +0800 Subject: [PATCH 0484/3768] Fix #11352 strutil.insertSep() fails on negative numbers (#15087) * fix #11352 strutil.insertSep handle negtive number * test for #11352 * optimize * not parts string var * Update lib/pure/strutils.nim Thanks! Co-authored-by: alaviss * need to be countdown Co-authored-by: alaviss --- lib/pure/strutils.nim | 23 +++++++++++++++++------ tests/modules/tstrutils_insert_sep.nim | 13 +++++++++++++ 2 files changed, 30 insertions(+), 6 deletions(-) create mode 100644 tests/modules/tstrutils_insert_sep.nim diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 32d6ede1e..a0a172430 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -2264,17 +2264,28 @@ proc insertSep*(s: string, sep = '_', digits = 3): string {.noSideEffect, ## if `s` contains a number. runnableExamples: doAssert insertSep("1000000") == "1_000_000" - - var L = (s.len-1) div digits + s.len - result = newString(L) + result = newStringOfCap(s.len) + let hasPrefix = isDigit(s[s.low]) == false + var idx:int + if hasPrefix: + result.add s[s.low] + for i in (s.low + 1)..s.high: + idx = i + if not isDigit(s[i]): + result.add s[i] + else: + break + let partsLen = s.len - idx + var L = (partsLen-1) div digits + partsLen + result.setLen(L + idx) var j = 0 dec(L) - for i in countdown(len(s)-1, 0): + for i in countdown(partsLen-1,0): if j == digits: - result[L] = sep + result[L + idx] = sep dec(L) j = 0 - result[L] = s[i] + result[L + idx] = s[i + idx] inc(j) dec(L) diff --git a/tests/modules/tstrutils_insert_sep.nim b/tests/modules/tstrutils_insert_sep.nim new file mode 100644 index 000000000..775fe7da1 --- /dev/null +++ b/tests/modules/tstrutils_insert_sep.nim @@ -0,0 +1,13 @@ +discard """ + output: ''' +-100 +-100,000 +100,000 +''' +""" +# test https://github.com/nim-lang/Nim/issues/11352 + +import strutils +echo insertSep($(-100), ',') +echo insertSep($(-100_000), ',') +echo insertSep($(100_000), ',') \ No newline at end of file From e4e4931432fbc81b1ced68b351691228fae31d36 Mon Sep 17 00:00:00 2001 From: Euan Date: Tue, 28 Jul 2020 17:01:25 +0100 Subject: [PATCH 0485/3768] Change clibdir and cincludes for NetBSD (#15102) --- config/nim.cfg | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/config/nim.cfg b/config/nim.cfg index d8d3d4f8b..a33a2f0a9 100644 --- a/config/nim.cfg +++ b/config/nim.cfg @@ -212,6 +212,14 @@ clang.objc.options.linker = "-lobjc -lgnustep-base" clibdir: "/usr/local/lib" @end +@if freebsd or openbsd: + cincludes: "/usr/local/include" + clibdir: "/usr/local/lib" +@elif netbsd: + cincludes: "/usr/pkg/include" + clibdir: "/usr/pkg/lib" +@end + # Configuration for the VxWorks # This has been tested with VxWorks 6.9 only @if vxworks: @@ -350,4 +358,4 @@ tcc.options.always = "-w" gcc.cpp.options.linker %= "${gcc.cpp.options.linker} -s" clang.options.linker %= "${clang.options.linker} -s" clang.cpp.options.linker %= "${clang.cpp.options.linker} -s" -@end \ No newline at end of file +@end From 86c9b7833917cd3deac912488e4dc18a1ca1223a Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 28 Jul 2020 19:18:46 +0200 Subject: [PATCH 0486/3768] disable sink inference, only enable it for the stdlib. Reason: better source code compatibility (#15105) --- changelog.md | 4 ++++ compiler/options.nim | 2 +- doc/destructors.rst | 12 ++++++++---- lib/system/inclrtl.nim | 3 +++ tests/arc/nim.cfg | 1 + tests/destructor/nim.cfg | 1 + 6 files changed, 18 insertions(+), 5 deletions(-) create mode 100644 tests/arc/nim.cfg create mode 100644 tests/destructor/nim.cfg diff --git a/changelog.md b/changelog.md index d4161373c..93c0797d3 100644 --- a/changelog.md +++ b/changelog.md @@ -265,6 +265,10 @@ proc mydiv(a, b): int {.raises: [].} = - Removed the `--oldast` switch. - `$getType(untyped)` is now "untyped" instead of "expr", `$getType(typed)` is now "typed" instead of "stmt". +- Sink inference is now disabled per default and has to enabled explicitly via + `--sinkInference:on`. *Note*: For the standard library sink inference remains + enabled. This change is most relevant for the `--gc:arc`, `--gc:orc` memory + management modes. ## Tool changes diff --git a/compiler/options.nim b/compiler/options.nim index 5d9bc245b..7beabe5e8 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -373,7 +373,7 @@ const DefaultOptions* = {optObjCheck, optFieldCheck, optRangeCheck, optBoundsCheck, optOverflowCheck, optAssert, optWarns, optRefCheck, optHints, optStackTrace, optLineTrace, # consider adding `optStackTraceMsgs` - optTrMacros, optStyleCheck, optSinkInference} + optTrMacros, optStyleCheck} DefaultGlobalOptions* = {optThreadAnalysis, optExcessiveStackTrace, optListFullPaths} diff --git a/doc/destructors.rst b/doc/destructors.rst index c97b17d6f..b433c38d7 100644 --- a/doc/destructors.rst +++ b/doc/destructors.rst @@ -261,8 +261,14 @@ optimizations (and the current implementation does not). Sink parameter inference ======================== -The current implementation does a limited form of sink parameter -inference. The `.nosinks`:idx: pragma can be used to disable this inference +The current implementation can do a limited form of sink parameter +inference. But it has to be enabled via `--sinkInference:on`, either +on the command line or via a `push` pragma. + +To enable it for a section of code, one can +use `{.push sinkInference: on.}`...`{.pop.}`. + +The `.nosinks`:idx: pragma can be used to disable this inference for a single routine: .. code-block:: nim @@ -270,8 +276,6 @@ for a single routine: proc addX(x: T; child: T) {.nosinks.} = x.s.add child -To disable it for a section of code, one can -use `{.push sinkInference: off.}`...`{.pop.}`. The details of the inference algorithm are currently undocumented. diff --git a/lib/system/inclrtl.nim b/lib/system/inclrtl.nim index 4193f2bdd..d80980c72 100644 --- a/lib/system/inclrtl.nim +++ b/lib/system/inclrtl.nim @@ -48,3 +48,6 @@ when defined(nimlocks): {.pragma: benign, gcsafe, locks: 0.} else: {.pragma: benign, gcsafe.} + +when defined(nimHasSinkInference): + {.push sinkInference: on.} diff --git a/tests/arc/nim.cfg b/tests/arc/nim.cfg new file mode 100644 index 000000000..7c148b797 --- /dev/null +++ b/tests/arc/nim.cfg @@ -0,0 +1 @@ +--sinkInference:on diff --git a/tests/destructor/nim.cfg b/tests/destructor/nim.cfg new file mode 100644 index 000000000..7c148b797 --- /dev/null +++ b/tests/destructor/nim.cfg @@ -0,0 +1 @@ +--sinkInference:on From b2a1944587bd23f87d05ed20cf33dd11c4c86e26 Mon Sep 17 00:00:00 2001 From: c-blake Date: Tue, 28 Jul 2020 17:48:50 -0400 Subject: [PATCH 0487/3768] Fulfill https://github.com/nim-lang/Nim/pull/14995#issuecomment-664914391 (#15104) request. This can be conceived as an alternate, more capable resolution of https://github.com/nim-lang/Nim/issues/12200 than https://github.com/nim-lang/Nim/pull/12208 The code re-org idea here is to upgrade tablimpl.nim:`delImpl`/`delImplIdx` to abstract client code conventions for cell emptiness & cell hashing via three new template arguments - `makeEmpty`, `cellEmpty`, `cellHash` which all take a single integer argument and clear a cell, test if clear or produce the hash of the key stored at that index in `.data[]`. Then we update the 3 call sites (`Table`, `CountTable`, `SharedTable`) of `delImpl`/`delImplIdx` by defining define those arguments just before the first invocation as non-exported templates. Because `CountTable` does not save hash() outputs as `.hcode`, it needs a new tableimpl.nim:`delImplNoHCode` which simply in-lines the hash search when no `.hcode` field is available for "prefix compare" acceleration. It is conceivable this new template could be used by future variants, such as one optimized for integer keys where `hash()` and `==` are fast and `.hcode` is both wasted space & time (though a small change to interfaces there for a sentinel key meaning "empty" is needed for maximum efficiency). We also eliminate the old O(n) `proc remove(CountTable...)` in favor of simply invoking the new `delImpl*` templates and take care to correctly handle the case where `val` is either zero for non-existent keys in `inc` or evolves to zero over time in `[]=` or `inc`. The only user-visible changes from the +-42 delta here are speed, iteration order post deletes, and relaxing the `Positive` constraint on `val` in `proc inc` again, as indicated in the `changelog.md` entry. --- changelog.md | 1 + lib/pure/collections/sharedtables.nim | 8 +++-- lib/pure/collections/tableimpl.nim | 23 ++++++++---- lib/pure/collections/tables.nim | 52 ++++++++++----------------- 4 files changed, 42 insertions(+), 42 deletions(-) diff --git a/changelog.md b/changelog.md index 93c0797d3..d9eaa5823 100644 --- a/changelog.md +++ b/changelog.md @@ -140,6 +140,7 @@ - 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`. diff --git a/lib/pure/collections/sharedtables.nim b/lib/pure/collections/sharedtables.nim index cbd922db7..af498d70d 100644 --- a/lib/pure/collections/sharedtables.nim +++ b/lib/pure/collections/sharedtables.nim @@ -138,6 +138,10 @@ proc hasKeyOrPut*[A, B](t: var SharedTable[A, B], key: A, val: B): bool = withLock t: hasKeyOrPutImpl(enlarge) +template tabMakeEmpty(i) = t.data[i].hcode = 0 +template tabCellEmpty(i) = isEmpty(t.data[i].hcode) +template tabCellHash(i) = t.data[i].hcode + proc withKey*[A, B](t: var SharedTable[A, B], key: A, mapper: proc(key: A, val: var B, pairExists: var bool)) = ## Computes a new mapping for the ``key`` with the specified ``mapper`` @@ -179,7 +183,7 @@ proc withKey*[A, B](t: var SharedTable[A, B], key: A, if pairExists: mapper(t.data[index].key, t.data[index].val, pairExists) if not pairExists: - delImplIdx(t, index) + delImplIdx(t, index, tabMakeEmpty, tabCellEmpty, tabCellHash) else: var val: B mapper(key, val, pairExists) @@ -200,7 +204,7 @@ proc add*[A, B](t: var SharedTable[A, B], key: A, val: B) = proc del*[A, B](t: var SharedTable[A, B], key: A) = ## deletes `key` from hash table `t`. withLock t: - delImpl() + delImpl(tabMakeEmpty, tabCellEmpty, tabCellHash) proc len*[A, B](t: var SharedTable[A, B]): int = ## number of elements in `t` diff --git a/lib/pure/collections/tableimpl.nim b/lib/pure/collections/tableimpl.nim index b9d7c70d9..ec2806200 100644 --- a/lib/pure/collections/tableimpl.nim +++ b/lib/pure/collections/tableimpl.nim @@ -78,22 +78,22 @@ template hasKeyOrPutImpl(enlarge) {.dirty.} = maybeRehashPutImpl(enlarge) else: result = true -template delImplIdx(t, i) = +template delImplIdx(t, i, makeEmpty, cellEmpty, cellHash) = let msk = maxHash(t) if i >= 0: dec(t.counter) block outer: while true: # KnuthV3 Algo6.4R adapted for i=i+1 instead of i=i-1 - var j = i # The correctness of this depends on (h+1) in nextTry, + var j = i # The correctness of this depends on (h+1) in nextTry var r = j # though may be adaptable to other simple sequences. - t.data[i].hcode = 0 # mark current EMPTY + makeEmpty(i) # mark current EMPTY t.data[i].key = default(type(t.data[i].key)) t.data[i].val = default(type(t.data[i].val)) while true: i = (i + 1) and msk # increment mod table size - if isEmpty(t.data[i].hcode): # end of collision cluster; So all done + if cellEmpty(i): # end of collision cluster; So all done break outer - r = t.data[i].hcode and msk # "home" location of key@i + r = cellHash(i) and msk # initial probe index for key@slot i if not ((i >= r and r > j) or (r > j and j > i) or (j > i and i >= r)): break when defined(js): @@ -101,10 +101,19 @@ template delImplIdx(t, i) = else: t.data[j] = move(t.data[i]) # data[j] will be marked EMPTY next loop -template delImpl() {.dirty.} = +template delImpl(makeEmpty, cellEmpty, cellHash) {.dirty.} = var hc: Hash var i = rawGet(t, key, hc) - delImplIdx(t, i) + delImplIdx(t, i, makeEmpty, cellEmpty, cellHash) + +template delImplNoHCode(makeEmpty, cellEmpty, cellHash) {.dirty.} = + if t.dataLen > 0: + var i: Hash = hash(key) and maxHash(t) + while not cellEmpty(i): + if t.data[i].key == key: + delImplIdx(t, i, makeEmpty, cellEmpty, cellHash) + break + i = nextTry(i, maxHash(t)) template clearImpl() {.dirty.} = for i in 0 ..< t.dataLen: diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index b1354eec3..3aa3af4e9 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -494,6 +494,10 @@ proc add*[A, B](t: var Table[A, B], key: A, val: B) {.deprecated: ## (key, value) pair in the table without introducing duplicates. addImpl(enlarge) +template tabMakeEmpty(i) = t.data[i].hcode = 0 +template tabCellEmpty(i) = isEmpty(t.data[i].hcode) +template tabCellHash(i) = t.data[i].hcode + proc del*[A, B](t: var Table[A, B], key: A) = ## Deletes ``key`` from hash table ``t``. Does nothing if the key does not exist. ## @@ -507,7 +511,7 @@ proc del*[A, B](t: var Table[A, B], key: A) = a.del('z') doAssert a == {'b': 9, 'c': 13}.toTable - delImpl() + delImpl(tabMakeEmpty, tabCellEmpty, tabCellHash) proc pop*[A, B](t: var Table[A, B], key: A, val: var B): bool = ## Deletes the ``key`` from the table. @@ -535,7 +539,7 @@ proc pop*[A, B](t: var Table[A, B], key: A, val: var B): bool = result = index >= 0 if result: val = move(t.data[index].val) - delImplIdx(t, index) + delImplIdx(t, index, tabMakeEmpty, tabCellEmpty, tabCellHash) proc take*[A, B](t: var Table[A, B], key: A, val: var B): bool {.inline.} = ## Alias for: @@ -2199,19 +2203,6 @@ proc enlarge[A](t: var CountTable[A]) = if t.data[i].val != 0: ctRawInsert(t, n, move t.data[i].key, move t.data[i].val) swap(t.data, n) -proc remove[A](t: var CountTable[A], key: A) = - var n: seq[tuple[key: A, val: int]] - newSeq(n, len(t.data)) - var removed: bool - for i in countup(0, high(t.data)): - if t.data[i].val != 0: - if t.data[i].key != key: - ctRawInsert(t, n, move t.data[i].key, move t.data[i].val) - else: - removed = true - swap(t.data, n) - if removed: dec(t.counter) - proc rawGet[A](t: CountTable[A], key: A): int = if t.data.len == 0: return -1 @@ -2225,7 +2216,7 @@ template ctget(t, key, default: untyped): untyped = var index = rawGet(t, key) result = if index >= 0: t.data[index].val else: default -proc inc*[A](t: var CountTable[A], key: A, val: Positive = 1) +proc inc*[A](t: var CountTable[A], key: A, val = 1) # ---------------------------------------------------------------------- @@ -2262,6 +2253,10 @@ proc `[]`*[A](t: CountTable[A], key: A): int = assert(not t.isSorted, "CountTable must not be used after sorting") ctget(t, key, 0) +template cntMakeEmpty(i) = t.data[i].val = 0 +template cntCellEmpty(i) = t.data[i].val == 0 +template cntCellHash(i) = hash(t.data[i].key) + proc `[]=`*[A](t: var CountTable[A], key: A, val: int) = ## Inserts a ``(key, value)`` pair into ``t``. ## @@ -2272,7 +2267,7 @@ proc `[]=`*[A](t: var CountTable[A], key: A, val: int) = assert(not t.isSorted, "CountTable must not be used after sorting") assert val >= 0 if val == 0: - t.remove(key) + delImplNoHCode(cntMakeEmpty, cntCellEmpty, cntCellHash) else: let h = rawGet(t, key) if h >= 0: @@ -2280,11 +2275,8 @@ proc `[]=`*[A](t: var CountTable[A], key: A, val: int) = else: insertImpl() -proc inc*[A](t: var CountTable[A], key: A, val: Positive = 1) = +proc inc*[A](t: var CountTable[A], key: A, val = 1) = ## Increments ``t[key]`` by ``val`` (default: 1). - ## - ## ``val`` must be a positive number. If you need to decrement a value, - ## use a regular ``Table`` instead. runnableExamples: var a = toCountTable("aab") a.inc('a') @@ -2295,9 +2287,11 @@ proc inc*[A](t: var CountTable[A], key: A, val: Positive = 1) = var index = rawGet(t, key) if index >= 0: inc(t.data[index].val, val) - if t.data[index].val == 0: dec(t.counter) + if t.data[index].val == 0: + delImplIdx(t, index, cntMakeEmpty, cntCellEmpty, cntCellHash) else: - insertImpl() + if val != 0: + insertImpl() proc smallest*[A](t: CountTable[A]): tuple[key: A, val: int] = ## Returns the ``(key, value)`` pair with the smallest ``val``. Efficiency: O(n) @@ -2358,8 +2352,6 @@ proc len*[A](t: CountTable[A]): int = proc del*[A](t: var CountTable[A], key: A) {.since: (1, 1).} = ## Deletes ``key`` from table ``t``. Does nothing if the key does not exist. ## - ## O(n) complexity. - ## ## See also: ## * `pop proc<#pop,CountTable[A],A,int>`_ ## * `clear proc<#clear,CountTable[A]>`_ to empty the whole table @@ -2372,7 +2364,7 @@ proc del*[A](t: var CountTable[A], key: A) {.since: (1, 1).} = a.del('c') assert a == toCountTable("aa") - remove(t, key) + delImplNoHCode(cntMakeEmpty, cntCellEmpty, cntCellHash) proc pop*[A](t: var CountTable[A], key: A, val: var int): bool {.since: (1, 1).} = ## Deletes the ``key`` from the table. @@ -2380,8 +2372,6 @@ proc pop*[A](t: var CountTable[A], key: A, val: var int): bool {.since: (1, 1).} ## mapping of the key. Otherwise, returns ``false``, and the ``val`` is ## unchanged. ## - ## O(n) complexity. - ## ## See also: ## * `del proc<#del,CountTable[A],A>`_ ## * `clear proc<#clear,CountTable[A]>`_ to empty the whole table @@ -2398,7 +2388,7 @@ proc pop*[A](t: var CountTable[A], key: A, val: var int): bool {.since: (1, 1).} result = index >= 0 if result: val = move(t.data[index].val) - remove(t, key) + delImplIdx(t, index, cntMakeEmpty, cntCellEmpty, cntCellHash) proc clear*[A](t: var CountTable[A]) = ## Resets the table so that it is empty. @@ -2686,8 +2676,6 @@ proc len*[A](t: CountTableRef[A]): int = proc del*[A](t: CountTableRef[A], key: A) {.since: (1, 1).} = ## Deletes ``key`` from table ``t``. Does nothing if the key does not exist. ## - ## O(n) complexity. - ## ## See also: ## * `pop proc<#pop,CountTableRef[A],A,int>`_ ## * `clear proc<#clear,CountTableRef[A]>`_ to empty the whole table @@ -2699,8 +2687,6 @@ proc pop*[A](t: CountTableRef[A], key: A, val: var int): bool {.since: (1, 1).} ## mapping of the key. Otherwise, returns ``false``, and the ``val`` is ## unchanged. ## - ## O(n) complexity. - ## ## See also: ## * `del proc<#del,CountTableRef[A],A>`_ ## * `clear proc<#clear,CountTableRef[A]>`_ to empty the whole table From a3a87cdb298d7166846133bca5d56f055717474c Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 29 Jul 2020 11:11:58 +0200 Subject: [PATCH 0488/3768] fixes #14616 [backport:1.2] (#15109) --- compiler/semmagic.nim | 4 ++++ tests/misc/tunsignedconv.nim | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index f991b4ac6..5cd7b28b0 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -564,5 +564,9 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, if not checkIsolate(n[1]): localError(c.config, n.info, "expression cannot be isolated: " & $n[1]) result = n + of mPred: + if n[1].typ.skipTypes(abstractInst).kind in {tyUInt..tyUInt64}: + n[0].sym.magic = mSubU + result = n else: result = n diff --git a/tests/misc/tunsignedconv.nim b/tests/misc/tunsignedconv.nim index 0e5fc43db..07fc03e59 100644 --- a/tests/misc/tunsignedconv.nim +++ b/tests/misc/tunsignedconv.nim @@ -59,3 +59,13 @@ discard $(x1,) # bug #13698 let n: csize = 1 # xxx should that be csize_t or is that essential here? doAssert $n.int32 == "1" + +# bug #14616 + +let limit = 1'u64 + +let rangeVar = 0'u64 ..< limit + +doAssert repr(rangeVar) == """[a = 0, +b = 0] +""" From 196e747df150ace81e7f4c01253128d4a89f03c7 Mon Sep 17 00:00:00 2001 From: c-blake Date: Wed, 29 Jul 2020 05:19:02 -0400 Subject: [PATCH 0489/3768] Attempt to explain better why delImplIdx is the way it is. Maybe this can (#15108) avoid future implementation mischief. (Maybe not. Sometimes, general distrust of theory leads people to distrust simple reasoning over times from CPUs trying as hard as possible to mask DRAM latency via pre-fetch.) --- lib/pure/collections/tableimpl.nim | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/lib/pure/collections/tableimpl.nim b/lib/pure/collections/tableimpl.nim index ec2806200..df9cb7517 100644 --- a/lib/pure/collections/tableimpl.nim +++ b/lib/pure/collections/tableimpl.nim @@ -78,6 +78,36 @@ template hasKeyOrPutImpl(enlarge) {.dirty.} = maybeRehashPutImpl(enlarge) else: result = true +# delImplIdx is KnuthV3 Algo6.4R adapted to i=i+1 (from i=i-1) which has come to +# be called "back shift delete". It shifts elements in the collision cluster of +# a victim backward to make things as-if the victim were never inserted in the +# first place. This is desirable to keep things "ageless" after many deletes. +# It is trickier than you might guess since initial probe (aka "home") locations +# of keys in a cluster may collide and since table addresses wrap around. +# +# A before-after diagram might look like ('.' means empty): +# slot: 0 1 2 3 4 5 6 7 +# before(1) +# hash1: 6 7 . 3 . 5 5 6 ; Really hash() and msk +# data1: E F . A . B C D ; About to delete C @index 6 +# after(2) +# hash2: 7 . . 3 . 5 6 6 ; Really hash() and msk +# data2: F . . A . B D E ; After deletion of C +# +# This lowers total search depth over the whole table from 1+1+2+2+2+2=10 to 7. +# Had the victim been B@5, C would need back shifting to slot 5. Total depth is +# always lowered by at least 1, e.g. victim A@3. This is all quite fast when +# empty slots are frequent (also needed to keep insert/miss searches fast) and +# hash() is either fast or avoided (via `.hcode`). It need not compare keys. +# +# delImplIdx realizes the above transformation, but only works for dense Linear +# Probing, nextTry(h)=h+1. This is not an important limitation since that's the +# fastest sequence on any CPU made since the 1980s. { Performance analysis often +# overweights "key cmp" neglecting cache behavior, giving bad ideas how big/slow +# tables behave (when perf matters most!). Comparing hcode first means usually +# only 1 key cmp is needed for *any* seq. Timing only predictable activity, +# small tables, and/or integer keys often perpetuates such bad ideas. } + template delImplIdx(t, i, makeEmpty, cellEmpty, cellHash) = let msk = maxHash(t) if i >= 0: From 2629d619a114f9c27b753a32da717a25906c99e6 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Wed, 29 Jul 2020 16:17:20 +0200 Subject: [PATCH 0490/3768] Fix forward declaration issues in template/macro context (#15091) * Fix forward declaration issues in template/macro context * Correct forward declaration resolving for overloads * Remove old dead code * WIP consistent gensym ids * Minimize diff * Remove obsoleted hack * Add templInstCounter to give unique IDs to template instantiations * Remove obsoleted code * Eh, init in myOpen, not myProcess... * Remove optNimV019 * Add testcase for #13484 --- changelog.md | 1 + compiler/commands.nim | 2 - compiler/evaltempl.nim | 10 ++- compiler/lookups.nim | 3 +- compiler/options.nim | 1 - compiler/procfind.nim | 45 +--------- compiler/sem.nim | 3 +- compiler/semdata.nim | 2 +- compiler/semexprs.nim | 13 +-- compiler/semstmts.nim | 6 +- compiler/semtempl.nim | 27 ++---- compiler/types.nim | 2 +- compiler/vm.nim | 5 +- compiler/vmdef.nim | 1 + testament/important_packages.nim | 4 +- tests/macros/tmacros_issues.nim | 114 +++++++++++++++++++++++++ tests/template/tparams_gensymed.nim | 126 ++++++++++++++++++++++++++++ 17 files changed, 270 insertions(+), 95 deletions(-) diff --git a/changelog.md b/changelog.md index d9eaa5823..517919de9 100644 --- a/changelog.md +++ b/changelog.md @@ -264,6 +264,7 @@ proc mydiv(a, b): int {.raises: [].} = - Removed the `--oldNewlines` switch. - Removed the `--laxStrings` switch for mutating the internal zero terminator on strings. - Removed the `--oldast` switch. +- Removed the `--oldgensym` switch - `$getType(untyped)` is now "untyped" instead of "expr", `$getType(typed)` is now "typed" instead of "stmt". - Sink inference is now disabled per default and has to enabled explicitly via diff --git a/compiler/commands.nim b/compiler/commands.nim index 504550045..b67cd461f 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -871,8 +871,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "expandarc": expectArg(conf, switch, arg, pass, info) conf.arcToExpand[arg] = "T" - of "oldgensym": - processOnOffSwitchG(conf, {optNimV019}, arg, pass, info) of "useversion": expectArg(conf, switch, arg, pass, info) case arg diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim index 2a01a7911..c1024e2fe 100644 --- a/compiler/evaltempl.nim +++ b/compiler/evaltempl.nim @@ -21,6 +21,7 @@ type # new symbol config: ConfigRef ic: IdentCache + instID: int proc copyNode(ctx: TemplCtx, a, b: PNode): PNode = result = copyNode(a) @@ -53,8 +54,8 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) = #if x.kind == skParam and x.owner.kind == skModule: # internalAssert c.config, false idTablePut(c.mapping, s, x) - if sfGenSym in s.flags and optNimV019 notin c.config.globalOptions: - result.add newIdentNode(getIdent(c.ic, x.name.s & "`gensym" & $x.id), + if sfGenSym in s.flags: + result.add newIdentNode(getIdent(c.ic, x.name.s & "`gensym" & $c.instID), if c.instLines: actual.info else: templ.info) else: result.add newSymNode(x, if c.instLines: actual.info else: templ.info) @@ -166,7 +167,7 @@ proc wrapInComesFrom*(info: TLineInfo; sym: PSym; res: PNode): PNode = proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym; conf: ConfigRef; - ic: IdentCache; + ic: IdentCache; instID: ref int; fromHlo=false): PNode = inc(conf.evalTemplateCounter) if conf.evalTemplateCounter > evalTemplateLimit: @@ -181,6 +182,7 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym; ctx.config = conf ctx.ic = ic initIdTable(ctx.mapping) + ctx.instID = instID[] let body = tmpl.getBody #echo "instantion of ", renderTree(body, {renderIds}) @@ -203,3 +205,5 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym; #if ctx.debugActive: # echo "instantion of ", renderTree(result, {renderIds}) dec(conf.evalTemplateCounter) + # The instID must be unique for every template instantiation, so we increment it here + inc instID[] diff --git a/compiler/lookups.nim b/compiler/lookups.nim index fe0e6fe7c..a3a2bf49c 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -316,8 +316,7 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym = fixSpelling(n, ident, searchInScopes) errorUndeclaredIdentifier(c, n.info, ident.s) result = errorSym(c, n) - elif checkAmbiguity in flags and result != nil and - contains(c.ambiguousSymbols, result.id): + elif checkAmbiguity in flags and result != nil and result.id in c.ambiguousSymbols: errorUseQualifier(c, n.info, result) of nkSym: result = n.sym diff --git a/compiler/options.nim b/compiler/options.nim index 7beabe5e8..b5fb546ef 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -89,7 +89,6 @@ type # please make sure we have under 32 options # implementation optOwnedRefs # active if the Nim compiler knows about 'owned'. optMultiMethods - optNimV019 optBenchmarkVM # Enables cpuTime() in the VM optProduceAsm # produce assembler code optPanics # turn panics (sysFatal) into a process termination diff --git a/compiler/procfind.nim b/compiler/procfind.nim index f7dc6c379..1d897758a 100644 --- a/compiler/procfind.nim +++ b/compiler/procfind.nim @@ -28,40 +28,7 @@ proc equalGenericParams(procA, procB: PNode): bool = if not exprStructuralEquivalent(a.ast, b.ast): return result = true -proc searchForProcOld*(c: PContext, scope: PScope, fn: PSym): PSym = - # Searches for a forward declaration or a "twin" symbol of fn - # in the symbol table. If the parameter lists are exactly - # the same the sym in the symbol table is returned, else nil. - var it: TIdentIter - result = initIdentIter(it, scope.symbols, fn.name) - if isGenericRoutine(fn): - # we simply check the AST; this is imprecise but nearly the best what - # can be done; this doesn't work either though as type constraints are - # not kept in the AST .. - while result != nil: - if result.kind == fn.kind and isGenericRoutine(result): - let genR = result.ast[genericParamsPos] - let genF = fn.ast[genericParamsPos] - if exprStructuralEquivalent(genR, genF) and - exprStructuralEquivalent(result.ast[paramsPos], - fn.ast[paramsPos]) and - equalGenericParams(genR, genF): - return - result = nextIdentIter(it, scope.symbols) - else: - while result != nil: - if result.kind == fn.kind and not isGenericRoutine(result): - case equalParams(result.typ.n, fn.typ.n) - of paramsEqual: - return - of paramsIncompatible: - localError(c.config, fn.info, "overloaded '$1' leads to ambiguous calls" % fn.name.s) - return - of paramsNotEqual: - discard - result = nextIdentIter(it, scope.symbols) - -proc searchForProcNew(c: PContext, scope: PScope, fn: PSym): PSym = +proc searchForProc*(c: PContext, scope: PScope, fn: PSym): PSym = const flags = {ExactGenericParams, ExactTypeDescValues, ExactConstraints, IgnoreCC} var it: TIdentIter @@ -83,16 +50,6 @@ proc searchForProcNew(c: PContext, scope: PScope, fn: PSym): PSym = discard result = nextIdentIter(it, scope.symbols) -proc searchForProc*(c: PContext, scope: PScope, fn: PSym): PSym = - result = searchForProcNew(c, scope, fn) - when false: - let old = searchForProcOld(c, scope, fn) - if old != result: - echo "Mismatch in searchForProc: ", fn.info - debug fn.typ - debug if result != nil: result.typ else: nil - debug if old != nil: old.typ else: nil - when false: proc paramsFitBorrow(child, parent: PNode): bool = result = false diff --git a/compiler/sem.nim b/compiler/sem.nim index 1f98b4f87..a07f62ca0 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -472,7 +472,7 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, #if c.evalContext == nil: # c.evalContext = c.createEvalContext(emStatic) - result = evalMacroCall(c.module, c.graph, n, nOrig, sym) + result = evalMacroCall(c.module, c.graph, c.templInstCounter, n, nOrig, sym) if efNoSemCheck notin flags: result = semAfterMacroCall(c, n, result, sym, flags) if c.config.macrosToExpand.hasKey(sym.name.s): @@ -521,6 +521,7 @@ proc myOpen(graph: ModuleGraph; module: PSym): PPassContext {.nosinks.} = c.semTypeNode = semTypeNode c.instTypeBoundOp = sigmatch.instTypeBoundOp c.hasUnresolvedArgs = hasUnresolvedArgs + c.templInstCounter = new int pushProcCon(c, module) pushOwner(c, c.module) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 8680fcac3..5bd6c4cf2 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -35,7 +35,6 @@ type inTryStmt*: int # whether we are in a try statement; works also # in standalone ``except`` and ``finally`` next*: PProcCon # used for stacking procedure contexts - wasForwarded*: bool # whether the current proc has a separate header mappingExists*: bool mapping*: TIdTable caseContext*: seq[tuple[n: PNode, idx: int]] @@ -85,6 +84,7 @@ type # this is used so that generic instantiations # can access private object fields instCounter*: int # to prevent endless instantiations + templInstCounter*: ref int # gives every template instantiation a unique id ambiguousSymbols*: IntSet # ids of all ambiguous symbols (cannot # store this info in the syms themselves!) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index fd9203f34..cdd0f61dd 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -33,7 +33,7 @@ proc semTemplateExpr(c: PContext, n: PNode, s: PSym, # Note: This is n.info on purpose. It prevents template from creating an info # context when called from an another template pushInfoContext(c.config, n.info, s.detailedInfo) - result = evalTemplate(n, s, getCurrOwner(c), c.config, c.cache, efFromHlo in flags) + result = evalTemplate(n, s, getCurrOwner(c), c.config, c.cache, c.templInstCounter, efFromHlo in flags) if efNoSemCheck notin flags: result = semAfterMacroCall(c, n, result, s, flags) popInfoContext(c.config) @@ -1203,17 +1203,6 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = elif sfGenSym in s.flags: # the owner should have been set by now by addParamOrResult internalAssert c.config, s.owner != nil - if c.p.wasForwarded: - # gensym'ed parameters that nevertheless have been forward declared - # need a special fixup: - let realParam = c.p.owner.typ.n[s.position+1] - internalAssert c.config, realParam.kind == nkSym and realParam.sym.kind == skParam - return newSymNode(c.p.owner.typ.n[s.position+1].sym, n.info) - elif c.p.owner.kind == skMacro: - # gensym'ed macro parameters need a similar hack (see bug #1944): - var u = searchInScopes(c, s.name) - internalAssert c.config, u != nil and u.kind == skParam and u.owner == s.owner - return newSymNode(u, n.info) result = newSymNode(s, n.info) of skVar, skLet, skResult, skForVar: if s.magic == mNimvm: diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index c88ff3609..68f0cee64 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1898,6 +1898,10 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, incl(s.typ.flags, tfNoSideEffect) var proto: PSym = if isAnon: nil else: searchForProc(c, oldScope, s) + if proto == nil and sfForward in s.flags: + #This is a definition that shares its sym with its forward declaration (generated by a macro), + #if the symbol is also gensymmed we won't find it with searchForProc, so we check here + proto = s if proto == nil: if s.kind == skIterator: if s.typ.callConv != ccClosure: @@ -1943,7 +1947,6 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, addGenericParamListToScope(c, proto.ast[genericParamsPos]) addParams(c, proto.typ.n, proto.kind) proto.info = s.info # more accurate line information - s.typ = proto.typ proto.options = s.options s = proto n[genericParamsPos] = proto.ast[genericParamsPos] @@ -1983,7 +1986,6 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, if n[genericParamsPos].kind == nkEmpty or usePseudoGenerics: if not usePseudoGenerics and s.magic == mNone: paramsTypeCheck(c, s.typ) - c.p.wasForwarded = proto != nil maybeAddResult(c, s, n) # semantic checking also needed with importc in case used in VM s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos])) diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index c9cd82833..e771e17c4 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -145,9 +145,6 @@ proc getIdentNode(c: var TemplCtx, n: PNode): PNode = illFormedAst(n, c.c.config) result = n -template oldCheck(cx: TemplCtx; cond: bool): bool = - (optNimV019 notin cx.c.config.globalOptions or cond) - proc isTemplParam(c: TemplCtx, n: PNode): bool {.inline.} = result = n.kind == nkSym and n.sym.kind == skParam and n.sym.owner == c.owner and sfTemplateParam in n.sym.flags @@ -215,21 +212,7 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) = closeScope(c) let ident = getIdentNode(c, n) if not isTemplParam(c, ident): - # fix #2670, consider: - # - # when b: - # var a = "hi" - # else: - # var a = 5 - # echo a - # - # We need to ensure that both 'a' produce the same gensym'ed symbol. - # So we need only check the *current* scope. - let s = localSearchInScope(c.c, considerQuotedIdent(c.c, ident)) - if s != nil and s.owner == c.owner and sfGenSym in s.flags: - onUse(n.info, s) - replaceIdentBySym(c.c, n, newSymNode(s, n.info)) - elif n.kind != nkSym: + if n.kind != nkSym: let local = newGenSym(k, ident, c) addPrelimDecl(c.c, local) styleCheckDef(c.c.config, n.info, local) @@ -560,16 +543,16 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = if n.kind == nkDotExpr: result = n result[0] = semTemplBody(c, n[0]) - if optNimV019 notin c.c.config.globalOptions: inc c.noGenSym + inc c.noGenSym result[1] = semTemplBody(c, n[1]) - if optNimV019 notin c.c.config.globalOptions: dec c.noGenSym + dec c.noGenSym else: result = semTemplBodySons(c, n) of nkExprColonExpr, nkExprEqExpr: if n.len == 2: - if optNimV019 notin c.c.config.globalOptions: inc c.noGenSym + inc c.noGenSym result[0] = semTemplBody(c, n[0]) - if optNimV019 notin c.c.config.globalOptions: dec c.noGenSym + dec c.noGenSym result[1] = semTemplBody(c, n[1]) else: result = semTemplBodySons(c, n) diff --git a/compiler/types.nim b/compiler/types.nim index b0581e96a..94fe8fd54 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -936,7 +936,7 @@ proc equalParams(a, b: PNode): TParamsEquality = discard of paramsIncompatible: result = paramsIncompatible - if (m.name.id != n.name.id): + if m.name.id != n.name.id: # BUGFIX return paramsNotEqual # paramsIncompatible; # continue traversal! If not equal, we can return immediately; else diff --git a/compiler/vm.nim b/compiler/vm.nim index 1edcace82..bb217c4db 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1233,7 +1233,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let node = regs[rb+i].regToNode node.info = c.debug[pc] macroCall.add(node) - var a = evalTemplate(macroCall, prc, genSymOwner, c.config, c.cache) + var a = evalTemplate(macroCall, prc, genSymOwner, c.config, c.cache, c.templInstCounter) if a.kind == nkStmtList and a.len == 1: a = a[0] a.recSetFlagIsRef ensureKind(rkNode) @@ -2232,7 +2232,7 @@ proc errorNode(owner: PSym, n: PNode): PNode = result.typ = newType(tyError, owner) result.typ.flags.incl tfCheckedForDestructor -proc evalMacroCall*(module: PSym; g: ModuleGraph; +proc evalMacroCall*(module: PSym; g: ModuleGraph; templInstCounter: ref int; n, nOrig: PNode, sym: PSym): PNode = if g.config.errorCounter > 0: return errorNode(module, n) @@ -2253,6 +2253,7 @@ proc evalMacroCall*(module: PSym; g: ModuleGraph; c.mode = emStaticStmt c.comesFromHeuristic.line = 0'u16 c.callsite = nOrig + c.templInstCounter = templInstCounter let start = genProc(c, sym) var tos = PStackFrame(prc: sym, comesFrom: 0, next: nil) diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index 9228ce5eb..59ffe9581 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -262,6 +262,7 @@ type graph*: ModuleGraph oldErrorCount*: int profiler*: Profiler + templInstCounter*: ref int # gives every template instantiation a unique ID, needed here for getAst PStackFrame* = ref TStackFrame TStackFrame* = object diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 8fa7c99a9..a4187f64c 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -12,7 +12,7 @@ var packages2*: seq[tuple[name, cmd: string; hasDeps: bool; url: string, useHead # pkg1 "alea", true pkg1 "argparse" pkg1 "arraymancer", true, "nim c tests/tests_cpu.nim" -pkg1 "ast_pattern_matching", false, "nim c -r --oldgensym:on tests/test1.nim" +#pkg1 "ast_pattern_matching", false, "nim c -r --oldgensym:on tests/test1.nim" pkg1 "awk", true pkg1 "bigints" pkg1 "binaryheap", false, "nim c -r binaryheap.nim" @@ -95,7 +95,7 @@ pkg2 "optionsutils" pkg2 "ormin", true, "nim c -o:orminn ormin.nim" pkg2 "parsetoml" pkg2 "patty" -pkg2 "plotly", true, "nim c --oldgensym:on examples/all.nim" +pkg2 "plotly", true, "nim c examples/all.nim" pkg2 "pnm" pkg2 "polypbren" pkg2 "prologue", true, "nim c -d:release -r tests/test_compile/test_compile.nim" diff --git a/tests/macros/tmacros_issues.nim b/tests/macros/tmacros_issues.nim index 255a98ba9..882c0735d 100644 --- a/tests/macros/tmacros_issues.nim +++ b/tests/macros/tmacros_issues.nim @@ -35,6 +35,10 @@ test foo1 foo2 foo3 +true +false +true +false ''' """ @@ -289,3 +293,113 @@ proc main() = const fb = Foobar(a: a) foobar(fb) main() + +# bug #13484 + +proc defForward(id, nid: NimNode): NimNode = + result = newProc(id, @[newIdentNode("bool"), newIdentDefs(nid, newIdentNode("int"))], body=newEmptyNode()) + +proc defEven(evenid, oddid, nid: NimNode): NimNode = + result = quote do: + proc `evenid`(`nid`: int): bool = + if `nid` == 0: + return true + else: + return `oddid`(`nid` - 1) + +proc defOdd(evenid, oddid, nid: NimNode): NimNode = + result = quote do: + proc `oddid`(`nid`: int): bool = + if `nid` == 0: + return false + else: + return `evenid`(`nid` - 1) + +proc callNode(funid, param: NimNode): NimNode = + result = quote do: + `funid`(`param`) + +macro testEvenOdd3(): untyped = + let + evenid = newIdentNode("even3") + oddid = newIdentNode("odd3") + nid = newIdentNode("n") + oddForward = defForward(oddid, nid) + even = defEven(evenid, oddid, nid) + odd = defOdd(evenid, oddid, nid) + callEven = callNode(evenid, newLit(42)) + callOdd = callNode(oddid, newLit(42)) + result = quote do: + `oddForward` + `even` + `odd` + echo `callEven` + echo `callOdd` + +macro testEvenOdd4(): untyped = + let + evenid = newIdentNode("even4") + oddid = newIdentNode("odd4") + nid = newIdentNode("n") + oddForward = defForward(oddid, nid) + even = defEven(evenid, oddid, nid) + odd = defOdd(evenid, oddid, nid) + callEven = callNode(evenid, newLit(42)) + callOdd = callNode(oddid, newLit(42)) + # rewrite the body of proc node. + oddForward[6] = newStmtList() + result = quote do: + `oddForward` + `even` + `odd` + echo `callEven` + echo `callOdd` + +macro testEvenOdd5(): untyped = + let + evenid = genSym(nskProc, "even5") + oddid = genSym(nskProc, "odd5") + nid = newIdentNode("n") + oddForward = defForward(oddid, nid) + even = defEven(evenid, oddid, nid) + odd = defOdd(evenid, oddid, nid) + callEven = callNode(evenid, newLit(42)) + callOdd = callNode(oddid, newLit(42)) + result = quote do: + `oddForward` + `even` + `odd` + echo `callEven` + echo `callOdd` + +macro testEvenOdd6(): untyped = + let + evenid = genSym(nskProc, "even6") + oddid = genSym(nskProc, "odd6") + nid = newIdentNode("n") + oddForward = defForward(oddid, nid) + even = defEven(evenid, oddid, nid) + odd = defOdd(evenid, oddid, nid) + callEven = callNode(evenid, newLit(42)) + callOdd = callNode(oddid, newLit(42)) + # rewrite the body of proc node. + oddForward[6] = newStmtList() + result = quote do: + `oddForward` + `even` + `odd` + echo `callEven` + echo `callOdd` + +# it works +testEvenOdd3() + +# it causes an error (redefinition of odd4), which is correct +assert not compiles testEvenOdd4() + +# it caused an error (still forwarded: odd5) +testEvenOdd5() + +# it works, because the forward decl and definition share the symbol and the compiler is forgiving here +#testEvenOdd6() #Don't test it though, the compiler may become more strict in the future + diff --git a/tests/template/tparams_gensymed.nim b/tests/template/tparams_gensymed.nim index a35d878d5..cfd354a74 100644 --- a/tests/template/tparams_gensymed.nim +++ b/tests/template/tparams_gensymed.nim @@ -195,3 +195,129 @@ mystate_machine: state_push(S1) echo state_current() state_pop() + +# bug #15075 +block: #Doesn't work + template genGenTempl: untyped = + proc loop(locals: int) + proc loop(locals: int) = discard + genGenTempl() + let pool = loop + +block: #Doesn't work + macro genGenMacro: untyped = + quote do: + proc loop(locals: int) + proc loop(locals: int) = discard + genGenMacro() + let pool = loop + +block: #This works + proc loop(locals: int) + proc loop(locals: int) = discard + let pool = loop + +#Now somewhat recursive: +type Cont = ref object of RootObj + fn*: proc(c: Cont): Cont {.nimcall.} + +block: #Doesn't work + template genGenTempl(): untyped = + proc loop(locals: Cont): Cont + proc loop(locals: Cont): Cont = + return Cont(fn: loop) + proc doServer(): Cont = + return Cont(fn: loop) + genGenTempl() + discard doServer() + +block: #Doesn't work + macro genGenMacro(): untyped = + quote: + proc loop(locals: Cont): Cont + proc loop(locals: Cont): Cont = + return Cont(fn: loop) + proc doServer(): Cont = + return Cont(fn: loop) + genGenMacro() + discard doServer() + +block: #This works + proc loop(locals: Cont): Cont + proc loop(locals: Cont): Cont = + return Cont(fn: loop) + proc doServer(): Cont = + return Cont(fn: loop) + discard doServer() + +#And fully recursive: +block: #Doesn't work + template genGenTempl: untyped = + proc loop(locals: int) + proc loop(locals: int) = loop(locals) + genGenTempl() + let pool = loop + +block: #Doesn't work + macro genGenMacro: untyped = + quote do: + proc loop(locals: int) + proc loop(locals: int) = loop(locals) + genGenMacro() + let pool = loop + +block: #This works + proc loop(locals: int) + proc loop(locals: int) = loop(locals) + let pool = loop + +block: + template genAndCallLoop: untyped = + proc loop() {.gensym.} + proc loop() {.gensym.} = + discard + loop() + genAndCallLoop + +block: #Fully recursive and gensymmed: + template genGenTempl: untyped = + proc loop(locals: int) {.gensym.} + proc loop(locals: int) {.gensym.} = loop(locals) + let pool = loop + genGenTempl() + +block: #Make sure gensymmed symbol doesn't overwrite the forward decl + proc loop() + proc loop() = discard + template genAndCallLoop: untyped = + proc loop() {.gensym.} = + discard + loop() + genAndCallLoop() + +template genLoopDecl: untyped = + proc loop() +template genLoopDef: untyped = + proc loop() = discard +block: + genLoopDecl + genLoopDef + loop() +block: + proc loop() + genLoopDef + loop() +block: + genLoopDecl + proc loop() = discard + loop() + +block: #Gensymmed sym sharing forward decl + macro genGenMacro: untyped = + let sym = genSym(nskProc, "loop") + nnkStmtList.newTree( + newProc(sym, body = newEmptyNode()), + newCall(sym), + newProc(sym, body = newStmtList()), + ) + genGenMacro From c645dba038f3d801afe1b9c842bd49bd9455b410 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Wed, 29 Jul 2020 22:18:27 +0800 Subject: [PATCH 0491/3768] fixes #14139 (#15107) * fix #14139 * Update lib/pure/collections/heapqueue.nim Co-authored-by: Andreas Rumpf Co-authored-by: Clyybber --- lib/pure/collections/heapqueue.nim | 6 +++--- tests/stdlib/t14139.nim | 9 +++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 tests/stdlib/t14139.nim diff --git a/lib/pure/collections/heapqueue.nim b/lib/pure/collections/heapqueue.nim index 608fd2920..c2952cf1b 100644 --- a/lib/pure/collections/heapqueue.nim +++ b/lib/pure/collections/heapqueue.nim @@ -156,10 +156,10 @@ proc replace*[T](heap: var HeapQueue[T], item: T): T = proc pushpop*[T](heap: var HeapQueue[T], item: T): T = ## Fast version of a push followed by a pop. - if heap.len > 0 and heapCmp(heap[0], item): - swap(item, heap[0]) + result = item + if heap.len > 0 and heapCmp(heap.data[0], item): + swap(result, heap.data[0]) siftup(heap, 0) - return item proc clear*[T](heap: var HeapQueue[T]) = ## Remove all elements from `heap`, making it empty. diff --git a/tests/stdlib/t14139.nim b/tests/stdlib/t14139.nim new file mode 100644 index 000000000..78e0f6645 --- /dev/null +++ b/tests/stdlib/t14139.nim @@ -0,0 +1,9 @@ +import heapqueue + +var test_queue : HeapQueue[int] + +test_queue.push(7) +test_queue.push(3) +test_queue.push(9) +let i = test_queue.pushpop(10) +assert i == 3 From 0788a7f97085ceed618285d9b8a869022d17c373 Mon Sep 17 00:00:00 2001 From: genotrance Date: Wed, 29 Jul 2020 15:42:08 -0500 Subject: [PATCH 0492/3768] Bump nimble (#15114) --- koch.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/koch.nim b/koch.nim index be658a53e..1364b6b04 100644 --- a/koch.nim +++ b/koch.nim @@ -10,7 +10,7 @@ # const - NimbleStableCommit = "c4b9c6e459e72c5b04e0beb993b2a912a4078cb5" # master + NimbleStableCommit = "d01d8f2df0ab321d414156be373704416da67f71" # master FusionStableCommit = "319aac4d43b04113831b529f8003e82f4af6a4a5" when not defined(windows): From 20315637aa5c6f018d868b7c3c26ca1b52d4cc65 Mon Sep 17 00:00:00 2001 From: alaviss Date: Wed, 29 Jul 2020 22:35:02 +0000 Subject: [PATCH 0493/3768] asyncnet, net: don't attempt SSL_shutdown if a fatal error occurred (#15066) * asyncnet, net: don't attempt SSL_shutdown if a fatal error occurred Per TLS standard and SSL_shutdown(3ssl). This should prevent errors coming from a close() after a bad event (ie. the other end of the pipe is closed before shutdown can be negotiated). Ref #9867 * tssl: try sending until an error occur * tssl: cleanup * tssl: actually run the test I forgot to make the test run :P * tssl: run the test on ARC, maybe then it'll be happy * tssl: turns off ARC, switch tlsEmulation on for freebsd * tssl: document why tlsEmulation is employed * net: move SafeDisconn handling logic to socketError --- lib/pure/asyncnet.nim | 13 +++++--- lib/pure/net.nim | 73 +++++++++++++++++++++++++----------------- tests/stdlib/tssl.nim | 63 ++++++++++++++++++++++++++++++++++++ tests/stdlib/tssl.nims | 5 +++ 4 files changed, 120 insertions(+), 34 deletions(-) create mode 100644 tests/stdlib/tssl.nim create mode 100644 tests/stdlib/tssl.nims diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index 410310e29..9846f8999 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -123,6 +123,7 @@ type sslContext: SslContext bioIn: BIO bioOut: BIO + sslNoShutdown: bool domain: Domain sockType: SockType protocol: Protocol @@ -200,9 +201,10 @@ proc newAsyncSocket*(domain, sockType, protocol: cint, Protocol(protocol), buffered, inheritable) when defineSsl: - proc getSslError(handle: SslPtr, err: cint): cint = + proc getSslError(socket: AsyncSocket, err: cint): cint = + assert socket.isSsl assert err < 0 - var ret = SSL_get_error(handle, err.cint) + var ret = SSL_get_error(socket.sslHandle, err.cint) case ret of SSL_ERROR_ZERO_RETURN: raiseSSLError("TLS/SSL connection failed to initiate, socket closed prematurely.") @@ -213,6 +215,7 @@ when defineSsl: of SSL_ERROR_WANT_X509_LOOKUP: raiseSSLError("Function for x509 lookup has been called.") of SSL_ERROR_SYSCALL, SSL_ERROR_SSL: + socket.sslNoShutdown = true raiseSSLError() else: raiseSSLError("Unknown Error") @@ -265,7 +268,7 @@ when defineSsl: # If the operation failed, try to see if SSL has some data to read # or write. if opResult < 0: - let err = getSslError(socket.sslHandle, opResult.cint) + let err = getSslError(socket, opResult.cint) let fut = appeaseSsl(socket, flags, err.cint) yield fut if not fut.read(): @@ -718,7 +721,7 @@ proc close*(socket: AsyncSocket) = # Don't call SSL_shutdown if the connection has not been fully # established, see: # https://github.com/openssl/openssl/issues/710#issuecomment-253897666 - if SSL_in_init(socket.sslHandle) == 0: + if not socket.sslNoShutdown and SSL_in_init(socket.sslHandle) == 0: ErrClearError() SSL_shutdown(socket.sslHandle) else: @@ -747,6 +750,8 @@ when defineSsl: socket.bioOut = bioNew(bioSMem()) sslSetBio(socket.sslHandle, socket.bioIn, socket.bioOut) + socket.sslNoShutdown = true + proc wrapConnectedSocket*(ctx: SslContext, socket: AsyncSocket, handshake: SslHandshakeType, hostname: string = "") = diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 7aeffbc35..f53095854 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -133,6 +133,7 @@ type sslNoHandshake: bool # True if needs handshake. sslHasPeekChar: bool sslPeekChar: char + sslNoShutdown: bool # True if shutdown shouldn't be done. lastError: OSErrorCode ## stores the last error on this socket domain: Domain sockType: SockType @@ -173,7 +174,8 @@ when defined(nimHasStyleChecks): {.pop.} proc socketError*(socket: Socket, err: int = -1, async = false, - lastError = (-1).OSErrorCode): void {.gcsafe.} + lastError = (-1).OSErrorCode, + flags: set[SocketFlag] = {}): void {.gcsafe.} proc isDisconnectionError*(flags: set[SocketFlag], lastError: OSErrorCode): bool = @@ -722,6 +724,7 @@ when defineSsl: socket.sslHandle = SSL_new(socket.sslContext.context) socket.sslNoHandshake = false socket.sslHasPeekChar = false + socket.sslNoShutdown = false if socket.sslHandle == nil: raiseSSLError() @@ -818,7 +821,8 @@ proc getSocketError*(socket: Socket): OSErrorCode = raiseOSError(result, "No valid socket error code available") proc socketError*(socket: Socket, err: int = -1, async = false, - lastError = (-1).OSErrorCode) = + lastError = (-1).OSErrorCode, + flags: set[SocketFlag] = {}) = ## Raises an OSError based on the error code returned by ``SSL_get_error`` ## (for SSL sockets) and ``osLastError`` otherwise. ## @@ -826,6 +830,9 @@ proc socketError*(socket: Socket, err: int = -1, async = false, ## error was caused by no data being available to be read. ## ## If ``err`` is not lower than 0 no exception will be raised. + ## + ## If ``flags`` contains ``SafeDisconn``, no exception will be raised + ## when the error was caused by a peer disconnection. when defineSsl: if socket.isSsl: if err <= 0: @@ -844,33 +851,39 @@ proc socketError*(socket: Socket, err: int = -1, async = false, of SSL_ERROR_WANT_X509_LOOKUP: raiseSSLError("Function for x509 lookup has been called.") of SSL_ERROR_SYSCALL: - var errStr = "IO error has occurred " - let sslErr = ERR_peek_last_error() - if sslErr == 0 and err == 0: - errStr.add "because an EOF was observed that violates the protocol" - elif sslErr == 0 and err == -1: - errStr.add "in the BIO layer" - else: - let errStr = $ERR_error_string(sslErr, nil) - raiseSSLError(errStr & ": " & errStr) + # SSL shutdown must not be done if a fatal error occurred. + socket.sslNoShutdown = true let osErr = osLastError() - raiseOSError(osErr, errStr) + if not flags.isDisconnectionError(osErr): + var errStr = "IO error has occurred " + let sslErr = ERR_peek_last_error() + if sslErr == 0 and err == 0: + errStr.add "because an EOF was observed that violates the protocol" + elif sslErr == 0 and err == -1: + errStr.add "in the BIO layer" + else: + let errStr = $ERR_error_string(sslErr, nil) + raiseSSLError(errStr & ": " & errStr) + raiseOSError(osErr, errStr) of SSL_ERROR_SSL: + # SSL shutdown must not be done if a fatal error occurred. + socket.sslNoShutdown = true raiseSSLError() else: raiseSSLError("Unknown Error") if err == -1 and not (when defineSsl: socket.isSsl else: false): var lastE = if lastError.int == -1: getSocketError(socket) else: lastError - if async: - when useWinVersion: - if lastE.int32 == WSAEWOULDBLOCK: - return - else: raiseOSError(lastE) - else: - if lastE.int32 == EAGAIN or lastE.int32 == EWOULDBLOCK: - return - else: raiseOSError(lastE) - else: raiseOSError(lastE) + if not flags.isDisconnectionError(lastE): + if async: + when useWinVersion: + if lastE.int32 == WSAEWOULDBLOCK: + return + else: raiseOSError(lastE) + else: + if lastE.int32 == EAGAIN or lastE.int32 == EWOULDBLOCK: + return + else: raiseOSError(lastE) + else: raiseOSError(lastE) proc listen*(socket: Socket, backlog = SOMAXCONN) {.tags: [ReadIOEffect].} = ## Marks ``socket`` as accepting connections. @@ -1026,7 +1039,7 @@ proc close*(socket: Socket) = # Don't call SSL_shutdown if the connection has not been fully # established, see: # https://github.com/openssl/openssl/issues/710#issuecomment-253897666 - if SSL_in_init(socket.sslHandle) == 0: + if not socket.sslNoShutdown and SSL_in_init(socket.sslHandle) == 0: # As we are closing the underlying socket immediately afterwards, # it is valid, under the TLS standard, to perform a unidirectional # shutdown i.e not wait for the peers "close notify" alert with a second @@ -1312,9 +1325,9 @@ proc recv*(socket: Socket, data: var string, size: int, timeout = -1, if result < 0: data.setLen(0) let lastError = getSocketError(socket) - if flags.isDisconnectionError(lastError): return - socket.socketError(result, lastError = lastError) - data.setLen(result) + socket.socketError(result, lastError = lastError, flags = flags) + else: + data.setLen(result) proc recv*(socket: Socket, size: int, timeout = -1, flags = {SocketFlag.SafeDisconn}): string {.inline.} = @@ -1388,8 +1401,9 @@ proc readLine*(socket: Socket, line: var TaintedString, timeout = -1, template raiseSockError() {.dirty.} = let lastError = getSocketError(socket) - if flags.isDisconnectionError(lastError): setLen(line.string, 0); return - socket.socketError(n, lastError = lastError) + if flags.isDisconnectionError(lastError): + setLen(line.string, 0) + socket.socketError(n, lastError = lastError, flags = flags) var waited: Duration @@ -1520,8 +1534,7 @@ proc send*(socket: Socket, data: string, let sent = send(socket, cstring(data), data.len) if sent < 0: let lastError = osLastError() - if flags.isDisconnectionError(lastError): return - socketError(socket, lastError = lastError) + socketError(socket, lastError = lastError, flags = flags) if sent != data.len: raiseOSError(osLastError(), "Could not send all data.") diff --git a/tests/stdlib/tssl.nim b/tests/stdlib/tssl.nim new file mode 100644 index 000000000..cda3cb590 --- /dev/null +++ b/tests/stdlib/tssl.nim @@ -0,0 +1,63 @@ +discard """ + joinable: false +""" + +import net, nativesockets + +when defined(posix): import os, posix + +when not defined(ssl): + {.error: "This test must be compiled with -d:ssl".} + +const DummyData = "dummy data\n" + +proc connector(port: Port) {.thread.} = + let clientContext = newContext(verifyMode = CVerifyNone) + var client = newSocket(buffered = false) + clientContext.wrapSocket(client) + client.connect("localhost", port) + + discard client.recvLine() + client.getFd.close() + +proc main() = + let serverContext = newContext(verifyMode = CVerifyNone, + certFile = "tests/testdata/mycert.pem", + keyFile = "tests/testdata/mycert.pem") + + when defined(posix): + var + ignoreAction = SigAction(sa_handler: SIG_IGN) + oldSigPipeHandler: SigAction + if sigemptyset(ignoreAction.sa_mask) == -1: + raiseOSError(osLastError(), "Couldn't create an empty signal set") + if sigaction(SIGPIPE, ignoreAction, oldSigPipeHandler) == -1: + raiseOSError(osLastError(), "Couldn't ignore SIGPIPE") + + block peer_close_without_shutdown: + var server = newSocket(buffered = false) + defer: server.close() + serverContext.wrapSocket(server) + server.bindAddr(address = "localhost") + let (_, port) = server.getLocalAddr() + server.listen() + + var clientThread: Thread[Port] + createThread(clientThread, connector, port) + + var peer: Socket + try: + server.accept(peer) + peer.send(DummyData) + + joinThread clientThread + + while true: + # Send data until we get EPIPE. + peer.send(DummyData, {}) + except OSError: + discard + finally: + peer.close() + +when isMainModule: main() diff --git a/tests/stdlib/tssl.nims b/tests/stdlib/tssl.nims new file mode 100644 index 000000000..3f24dc325 --- /dev/null +++ b/tests/stdlib/tssl.nims @@ -0,0 +1,5 @@ +--threads:on +--d:ssl +when defined(freebsd): + # See https://github.com/nim-lang/Nim/pull/15066#issuecomment-665541265 + --tlsEmulation:off From 604f7461d786e31e7ae9df5a101770c329e00fe5 Mon Sep 17 00:00:00 2001 From: treeform Date: Wed, 29 Jul 2020 20:49:51 -0700 Subject: [PATCH 0494/3768] Fix tables.CountTable largest and smallest (#15115) It needs to have len defined first because of the assert .len > 0. I just moved it up a bit to make them work. --- lib/pure/collections/tables.nim | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index 3aa3af4e9..017a2f337 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -2293,6 +2293,10 @@ proc inc*[A](t: var CountTable[A], key: A, val = 1) = if val != 0: insertImpl() +proc len*[A](t: CountTable[A]): int = + ## Returns the number of keys in ``t``. + result = t.counter + proc smallest*[A](t: CountTable[A]): tuple[key: A, val: int] = ## Returns the ``(key, value)`` pair with the smallest ``val``. Efficiency: O(n) ## @@ -2345,10 +2349,6 @@ proc getOrDefault*[A](t: CountTable[A], key: A; default: int = 0): int = ## is in the table ctget(t, key, default) -proc len*[A](t: CountTable[A]): int = - ## Returns the number of keys in ``t``. - result = t.counter - proc del*[A](t: var CountTable[A], key: A) {.since: (1, 1).} = ## Deletes ``key`` from table ``t``. Does nothing if the key does not exist. ## From 32c6146200196398986bee092b4ef4a0f82d90b2 Mon Sep 17 00:00:00 2001 From: jcosborn Date: Thu, 30 Jul 2020 08:34:11 -0500 Subject: [PATCH 0495/3768] fix overloading case with generic alias (#15116) * fix overloading case with generic alias * revert reorganization of tests --- compiler/sigmatch.nim | 2 +- tests/overload/toverload_various.nim | 105 +++++++++++++++++++++++++++ 2 files changed, 106 insertions(+), 1 deletion(-) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index e663f96b6..7d97944ce 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1476,7 +1476,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, of tyGenericBody: considerPreviousT: - if a == f or a.kind == tyGenericInst and a[0] == f: + if a == f or a.kind == tyGenericInst and a.skipGenericAlias[0] == f: bindingRet isGeneric let ff = lastSon(f) if ff != nil: diff --git a/tests/overload/toverload_various.nim b/tests/overload/toverload_various.nim index 78497e607..96cb9a736 100644 --- a/tests/overload/toverload_various.nim +++ b/tests/overload/toverload_various.nim @@ -270,3 +270,108 @@ proc hmac*[A, B](HashType: typedesc, key: openarray[A], ctx.init(nil, 0) sha256.hmac("", "") + + + +# nested generic types +block: + type + Foo[T] = object + f: T + Bar[T] = object + b: T + Baz[T] = object + z: T + FooBar[T] = Foo[Bar[T]] + FooBarBaz[T] = FooBar[Baz[T]] + #Int = int + Int = SomeInteger + FooBarBazInt = FooBarBaz[Int] + FooBarBazX = FooBarBaz[int] + + proc p00(x: Foo): auto = x.f + proc p01[T](x: Foo[T]): auto = x.f + proc p02[T:Foo](x: T): auto = x.f + + proc p10(x: FooBar): auto = x.f + proc p11[T](x: FooBar[T]): auto = x.f + proc p12[T:FooBar](x: T): auto = x.f + proc p13(x: Foo[Bar]): auto = x.f + proc p14[T](x: Foo[Bar[T]]): auto = x.f + proc p15[T:Bar](x: Foo[T]): auto = x.f + proc p16[T:Foo[Bar]](x: T): auto = x.f + + proc p20(x: FooBarBaz): auto = x.f + proc p21[T](x: FooBarBaz[T]): auto = x.f + proc p22[T:FooBarBaz](x: T): auto = x.f + proc p23(x: FooBar[Baz]): auto = x.f + proc p24[T](x: FooBar[Baz[T]]): auto = x.f + proc p25[T:Baz](x: FooBar[T]): auto = x.f + proc p26[T:FooBar[Baz]](x: T): auto = x.f + proc p27(x: Foo[Bar[Baz]]): auto = x.f + proc p28[T](x: Foo[Bar[Baz[T]]]): auto = x.f + proc p29[T:Baz](x: Foo[Bar[T]]): auto = x.f + proc p2A[T:Bar[Baz]](x: Foo[T]): auto = x.f + proc p2B[T:Foo[Bar[Baz]]](x: T): auto = x.f + + proc p30(x: FooBarBazInt): auto = x.f + proc p31[T:FooBarBazInt](x: T): auto = x.f + proc p32(x: FooBarBaz[Int]): auto = x.f + proc p33[T:Int](x: FooBarBaz[T]): auto = x.f + proc p34[T:FooBarBaz[Int]](x: T): auto = x.f + proc p35(x: FooBar[Baz[Int]]): auto = x.f + proc p36[T:Int](x: FooBar[Baz[T]]): auto = x.f + proc p37[T:Baz[Int]](x: FooBar[T]): auto = x.f + proc p38[T:FooBar[Baz[Int]]](x: T): auto = x.f + proc p39(x: Foo[Bar[Baz[Int]]]): auto = x.f + proc p3A[T:Int](x: Foo[Bar[Baz[T]]]): auto = x.f + proc p3B[T:Baz[Int]](x: Foo[Bar[T]]): auto = x.f + proc p3C[T:Bar[Baz[Int]]](x: Foo[T]): auto = x.f + proc p3D[T:Foo[Bar[Baz[Int]]]](x: T): auto = x.f + + template test(x: typed) = + let t00 = p00(x) + let t01 = p01(x) + let t02 = p02(x) + let t10 = p10(x) + let t11 = p11(x) + let t12 = p12(x) + #let t13 = p13(x) + let t14 = p14(x) + #let t15 = p15(x) + #let t16 = p16(x) + let t20 = p20(x) + let t21 = p21(x) + let t22 = p22(x) + #let t23 = p23(x) + let t24 = p24(x) + #let t25 = p25(x) + #let t26 = p26(x) + #let t27 = p27(x) + let t28 = p28(x) + #let t29 = p29(x) + #let t2A = p2A(x) + #let t2B = p2B(x) + let t30 = p30(x) + let t31 = p31(x) + let t32 = p32(x) + let t33 = p33(x) + let t34 = p34(x) + let t35 = p35(x) + let t36 = p36(x) + let t37 = p37(x) + let t38 = p38(x) + let t39 = p39(x) + let t3A = p3A(x) + let t3B = p3B(x) + let t3C = p3C(x) + let t3D = p3D(x) + + var a: Foo[Bar[Baz[int]]] + test(a) + var b: FooBar[Baz[int]] + test(b) + var c: FooBarBaz[int] + test(c) + var d: FooBarBazX + test(d) From d130175342f2b7c1bf87dd05c86509f04cf78154 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 30 Jul 2020 17:32:48 +0200 Subject: [PATCH 0496/3768] cursor and mutation tracking fixes (#15113) * fixes #15110 * fixes #15096 * prepare varpartitions for cursor inference * new cursor inference begins to work * make tests green --- compiler/ast.nim | 9 + compiler/cursor_inference.nim | 300 --------------------------------- compiler/injectdestructors.nim | 4 +- compiler/sempass2.nim | 2 + compiler/varpartitions.nim | 295 ++++++++++++++++++++++++++------ compiler/vmmarshal.nim | 5 - tests/arc/topt_cursor2.nim | 51 ++++++ tests/arc/topt_no_cursor.nim | 13 +- 8 files changed, 311 insertions(+), 368 deletions(-) delete mode 100644 compiler/cursor_inference.nim create mode 100644 tests/arc/topt_cursor2.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index 6ca79aeee..1e619e51b 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1841,6 +1841,11 @@ proc isImportedException*(t: PType; conf: ConfigRef): bool = proc isInfixAs*(n: PNode): bool = return n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.s == "as" +proc skipColon*(n: PNode): PNode = + result = n + if n.kind == nkExprColonExpr: + result = n[1] + proc findUnresolvedStatic*(n: PNode): PNode = # n.typ == nil: see issue #14802 if n.kind == nkSym and n.typ != nil and n.typ.kind == tyStatic and n.typ.n == nil: @@ -1860,6 +1865,10 @@ when false: if n[i].containsNil: return true template hasDestructor*(t: PType): bool = {tfHasAsgn, tfHasOwned} * t.flags != {} + +proc hasDisabledAsgn*(t: PType): bool = + t.attachedOps[attachedAsgn] != nil and sfError in t.attachedOps[attachedAsgn].flags + template incompleteType*(t: PType): bool = t.sym != nil and {sfForward, sfNoForward} * t.sym.flags == {sfForward} diff --git a/compiler/cursor_inference.nim b/compiler/cursor_inference.nim deleted file mode 100644 index 0402febec..000000000 --- a/compiler/cursor_inference.nim +++ /dev/null @@ -1,300 +0,0 @@ -# -# -# The Nim Compiler -# (c) Copyright 2020 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## Cursor inference: -## The basic idea was like this: Elide 'destroy(x)' calls if only -## special literals are assigned to 'x' and 'x' is not mutated or -## passed by 'var T' to something else. Special literals are string literals or -## arrays / tuples of string literals etc. -## -## However, there is a much more general rule here: Compute which variables -## can be annotated with `.cursor`. To see how and when we can do that, -## think about this question: In `dest = src` when do we really have to -## *materialize* the full copy? - Only if `dest` or `src` are mutated -## afterwards. `dest` is the potential cursor variable, so that is -## simple to analyse. And if `src` is a location derived from a -## formal parameter, we also know it is not mutated! In other words, we -## do a compile-time copy-on-write analysis. - -import - ast, types, renderer, idents, intsets, options - -type - Cursor = object - s: PSym - deps: IntSet - Con = object - cursors: seq[Cursor] - mayOwnData: IntSet - mutations: IntSet - reassigns: IntSet - config: ConfigRef - inAsgnSource: int - -proc locationRoot(e: PNode; followDotExpr = true): PSym = - var n = e - while true: - case n.kind - of nkSym: - if n.sym.kind in {skVar, skResult, skTemp, skLet, skForVar, skParam}: - return n.sym - else: - return nil - of nkDotExpr, nkDerefExpr, nkBracketExpr, nkHiddenDeref, - nkCheckedFieldExpr, nkAddr, nkHiddenAddr: - if followDotExpr: - n = n[0] - else: - return nil - of nkObjUpConv, nkObjDownConv: - n = n[0] - of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkCast: - n = n[1] - of nkStmtList, nkStmtListExpr: - if n.len > 0: - n = n[^1] - else: - return nil - else: - return nil - -proc addDep(c: var Con; dest: var Cursor; dependsOn: PSym) = - if dest.s != dependsOn: - dest.deps.incl dependsOn.id - -proc cursorId(c: Con; x: PSym): int = - for i in 0.. 0: - analyseAsgn(c, dest, n[^1]) - - of nkClosure: - for i in 1.. treat it like a sink parameter - c.mayOwnData.incl r.id - c.mutations.incl r.id - -proc analyse(c: var Con; n: PNode) = - case n.kind - of nkCallKinds: - let parameters = n[0].typ - let L = if parameters != nil: parameters.len else: 0 - - analyse(c, n[0]) - for i in 1..= 0: - analyseAsgn(c, c.cursors[idx], n[1]) - - c.reassigns.incl n[0].sym.id - else: - # assignments like 'x.field = value' mean that 'x' itself cannot - # be a cursor: - let r = locationRoot(n[0]) - if r != nil: - # however, an assignment like 'it.field = x' does not influence r's - # cursorness property: - if r.typ.skipTypes(abstractInst).kind notin {tyPtr, tyRef}: - c.mayOwnData.incl r.id - c.mutations.incl r.id - - if hasDestructor(n[1].typ): - rhsIsSink(c, n[1]) - - of nkAddr, nkHiddenAddr: - analyse(c, n[0]) - let r = locationRoot(n[0]) - if r != nil: - c.mayOwnData.incl r.id - c.mutations.incl r.id - - of nkTupleConstr, nkBracket, nkObjConstr: - for child in n: analyse(c, child) - if c.inAsgnSource > 0: - for i in ord(n.kind == nkObjConstr).. 0: - allRoots(n.lastSon, result) + allRoots(n.lastSon, result, followDotExpr) of nkCaseStmt, nkObjConstr: for i in 1.. 1: - allRoots(n[1], result) + allRoots(n[1], result, followDotExpr) else: let m = getMagic(n) case m @@ -201,77 +216,223 @@ proc allRoots(n: PNode; result: var seq[PSym]) = let paramType = typ.n[i].typ if not paramType.isCompileTimeOnly and not typ.sons[0].isEmptyType and canAlias(paramType, typ.sons[0]): - allRoots(it, result) + allRoots(it, result, followDotExpr) else: - allRoots(it, result) + allRoots(it, result, followDotExpr) of mSlice: - allRoots(n[1], result) + allRoots(n[1], result, followDotExpr) else: discard "harmless operation" else: discard "nothing to do" -proc deps(p: var Partitions; dest, src: PNode) = +proc analyseAsgn(c: var Partitions; dest: var VarIndex; n: PNode) = + case n.kind + of nkEmpty, nkCharLit..nkNilLit: + # primitive literals including the empty are harmless: + discard + + of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv, nkCast, nkConv: + analyseAsgn(c, dest, n[1]) + + of nkIfStmt, nkIfExpr: + for i in 0.. 0: + analyseAsgn(c, dest, n[^1]) + + of nkClosure: + for i in 1..= 0: + c.s[vid].flags.incl preventCursor + +proc rhsIsSink(c: var Partitions, n: PNode) = + if n.kind == nkSym and n.typ.skipTypes(abstractInst-{tyOwned}).kind == tyRef: + discard "do no pessimize simple refs further, injectdestructors.nim will prevent moving from it" + else: + var roots: seq[PSym] + allRoots(n, roots, followDotExpr = false) + # let x = cursor? --> treat it like a sink parameter + for r in roots: + noCursor(c, r) + +proc deps(c: var Partitions; dest, src: PNode) = var targets, sources: seq[PSym] allRoots(dest, targets) allRoots(src, sources) for t in targets: if dest.kind != nkSym: - potentialMutation(p, t, dest.info) + potentialMutation(c, t, dest.info) proc wrap(t: PType): bool {.nimcall.} = t.kind in {tyRef, tyPtr} if types.searchTypeFor(t.typ, wrap): for s in sources: - connect(p, t, s, dest.info) + connect(c, t, s, dest.info) -proc traverse(p: var Partitions; n: PNode) = + if c.performCursorInference and src.kind != nkEmpty: + if dest.kind == nkSym: + let vid = variableId(c, dest.sym) + if vid >= 0: + analyseAsgn(c, c.s[vid], src) + # do not borrow from a different local variable, this is easier + # than tracking reassignments, consider 'var cursor = local; local = newNode()' + if src.kind == nkSym and (src.sym.kind in {skVar, skResult, skTemp} or + (src.sym.kind in {skLet, skParam, skForVar} and hasDisabledAsgn(src.sym.typ))): + c.s[vid].flags.incl preventCursor + + if hasDestructor(src.typ): + rhsIsSink(c, src) + +proc traverse(c: var Partitions; n: PNode) = case n.kind of nkLetSection, nkVarSection: for child in n: let last = lastSon(child) - traverse(p, last) + traverse(c, last) if child.kind == nkVarTuple and last.kind in {nkPar, nkTupleConstr}: if child.len-2 != last.len: return for i in 0.. 0: + for i in 0.. 0: + for i in 1.. Date: Thu, 30 Jul 2020 19:19:59 +0200 Subject: [PATCH 0497/3768] fixes #15112 (#15124) --- compiler/ccgexprs.nim | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 73cc2c8a1..33231b1bc 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -729,14 +729,16 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc) = else: putIntoDest(p, d, e, "(*$1)" % [rdLoc(a)], a.storage) -proc cow(p: BProc; n: PNode) = - if n.kind == nkHiddenAddr and optSeqDestructors in p.config.globalOptions: - if n[0].kind == nkBracketExpr: - let strCandidate = n[0][0] - if strCandidate.typ.skipTypes(abstractInst).kind == tyString: - var a: TLoc - initLocExpr(p, strCandidate, a) - linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)]) +proc cowBracket(p: BProc; n: PNode) = + if n.kind == nkBracketExpr and optSeqDestructors in p.config.globalOptions: + let strCandidate = n[0] + if strCandidate.typ.skipTypes(abstractInst).kind == tyString: + var a: TLoc + initLocExpr(p, strCandidate, a) + linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)]) + +proc cow(p: BProc; n: PNode) {.inline.} = + if n.kind == nkHiddenAddr: cowBracket(p, n[0]) proc genAddr(p: BProc, e: PNode, d: var TLoc) = # careful 'addr(myptrToArray)' needs to get the ampersand: @@ -1759,6 +1761,8 @@ proc genSwap(p: BProc, e: PNode, d: var TLoc) = # temp = a # a = b # b = temp + cowBracket(p, e[1]) + cowBracket(p, e[2]) var a, b, tmp: TLoc getTemp(p, skipTypes(e[1].typ, abstractVar), tmp) initLocExpr(p, e[1], a) # eval a From f1971243d3e162889b10b46caab8ba6e98afa92d Mon Sep 17 00:00:00 2001 From: genotrance Date: Thu, 30 Jul 2020 13:13:34 -0500 Subject: [PATCH 0498/3768] Bump nimble (#15126) --- koch.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/koch.nim b/koch.nim index 1364b6b04..68a880d0d 100644 --- a/koch.nim +++ b/koch.nim @@ -10,7 +10,7 @@ # const - NimbleStableCommit = "d01d8f2df0ab321d414156be373704416da67f71" # master + NimbleStableCommit = "a3e1267265965fc79a50b66999b660e0920db8cd" # master FusionStableCommit = "319aac4d43b04113831b529f8003e82f4af6a4a5" when not defined(windows): From d23ae4953510287c64e5bbbfb8c0e1eea23822f6 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 31 Jul 2020 12:13:33 +0200 Subject: [PATCH 0499/3768] fixes #15071 [backport] (#15131) --- compiler/transf.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/transf.nim b/compiler/transf.nim index 7aa18bf52..29344e7a1 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -1109,15 +1109,15 @@ proc liftDeferAux(n: PNode) = if n[i].kind == nkDefer: let deferPart = newNodeI(nkFinally, n[i].info) deferPart.add n[i][0] - var tryStmt = newNodeI(nkTryStmt, n[i].info) - var body = newNodeI(n.kind, n[i].info) + var tryStmt = newNodeIT(nkTryStmt, n[i].info, n.typ) + var body = newNodeIT(n.kind, n[i].info, n.typ) if i < last: body.sons = n.sons[(i+1)..last] tryStmt.add body tryStmt.add deferPart n[i] = tryStmt n.sons.setLen(i+1) - n.typ = n[i].typ + n.typ = tryStmt.typ goOn = true break for i in 0..n.safeLen-1: From 4bf8d38248f40cc0bf5323843a9ea38c5bcc20a8 Mon Sep 17 00:00:00 2001 From: Oliver Daniel Date: Fri, 31 Jul 2020 10:35:50 -0400 Subject: [PATCH 0500/3768] Small typo (#15132) The expected output necessarily cannot match the formatted string. --- lib/pure/sugar.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/sugar.nim b/lib/pure/sugar.nim index d2bdb0fdc..3d9a9d42e 100644 --- a/lib/pure/sugar.nim +++ b/lib/pure/sugar.nim @@ -201,7 +201,7 @@ macro capture*(locals: varargs[typed], body: untyped): untyped {.since: (1, 1).} ## for j in 7..9: ## if i * j == 42: ## capture i, j: - ## myClosure = proc () = echo fmt"{i} * {j} = 42" + ## myClosure = proc () = echo fmt"{i} * {j} == 42" ## myClosure() # output: 6 * 7 == 42 ## let m = @[proc (s: string): string = "to " & s, proc (s: string): string = "not to " & s] ## var l = m.mapIt(capture(it, proc (s: string): string = it(s))) From 6b88cd0d42cac8059cb4ead691d20c21ed445a12 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 31 Jul 2020 20:27:58 +0200 Subject: [PATCH 0501/3768] Revert "Small typo (#15132)" (#15134) This reverts commit 4bf8d38248f40cc0bf5323843a9ea38c5bcc20a8. --- lib/pure/sugar.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/sugar.nim b/lib/pure/sugar.nim index 3d9a9d42e..d2bdb0fdc 100644 --- a/lib/pure/sugar.nim +++ b/lib/pure/sugar.nim @@ -201,7 +201,7 @@ macro capture*(locals: varargs[typed], body: untyped): untyped {.since: (1, 1).} ## for j in 7..9: ## if i * j == 42: ## capture i, j: - ## myClosure = proc () = echo fmt"{i} * {j} == 42" + ## myClosure = proc () = echo fmt"{i} * {j} = 42" ## myClosure() # output: 6 * 7 == 42 ## let m = @[proc (s: string): string = "to " & s, proc (s: string): string = "not to " & s] ## var l = m.mapIt(capture(it, proc (s: string): string = it(s))) From 4ab6887a67dcc87f961937447df0907b9dc7f26d Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 1 Aug 2020 07:39:00 +0200 Subject: [PATCH 0502/3768] fixes #15111 (#15136) --- compiler/dfa.nim | 4 ++-- compiler/parser.nim | 4 +++- compiler/semobjconstr.nim | 38 +++++++++++++++++++------------------- compiler/semtypes.nim | 8 ++++++-- compiler/varpartitions.nim | 2 ++ 5 files changed, 32 insertions(+), 24 deletions(-) diff --git a/compiler/dfa.nim b/compiler/dfa.nim index f3b358d72..85a9ccf42 100644 --- a/compiler/dfa.nim +++ b/compiler/dfa.nim @@ -280,7 +280,7 @@ proc patch(c: var Con, p: TPosition) = # patch with current index c.code[p.int].dest = checkedDistance(c.code.len - p.int) -func gen(c: var Con; n: PNode) +proc gen(c: var Con; n: PNode) proc popBlock(c: var Con; oldLen: int) = var exits: seq[TPosition] @@ -712,7 +712,7 @@ proc genVarSection(c: var Con; n: PNode) = if a.lastSon.kind != nkEmpty: genDef(c, a[0]) -func gen(c: var Con; n: PNode) = +proc gen(c: var Con; n: PNode) = case n.kind of nkSym: genUse(c, n) of nkCallKinds: diff --git a/compiler/parser.nim b/compiler/parser.nim index ccb7ca3b1..8cfeff6b0 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -1953,7 +1953,7 @@ proc parseObjectPart(p: var TParser): PNode = else: parMessage(p, errIdentifierExpected, p.tok) break - else: + elif sameOrNoInd(p): case p.tok.tokType of tkWhen: result = parseObjectWhen(p) @@ -1968,6 +1968,8 @@ proc parseObjectPart(p: var TParser): PNode = getTok(p) else: result = p.emptyNode + else: + result = p.emptyNode proc parseObject(p: var TParser): PNode = #| object = 'object' pragma? ('of' typeDesc)? COMMENT? objectPart diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim index f8639b000..cd5a52fe9 100644 --- a/compiler/semobjconstr.nim +++ b/compiler/semobjconstr.nim @@ -154,20 +154,20 @@ proc collectMissingFields(c: PContext, fieldsRecList: PNode, if assignment == nil: constrCtx.missingFields.add r.sym -proc semConstructFields(c: PContext, recNode: PNode, +proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext, flags: TExprFlags): InitStatus = result = initUnknown - case recNode.kind + case n.kind of nkRecList: - for field in recNode: + for field in n: let status = semConstructFields(c, field, constrCtx, flags) mergeInitStatus(result, status) of nkRecCase: template fieldsPresentInBranch(branchIdx: int): string = - let branch = recNode[branchIdx] + let branch = n[branchIdx] let fields = branch[^1] fieldsPresentInInitExpr(c, fields, constrCtx.initExpr) @@ -176,12 +176,12 @@ proc semConstructFields(c: PContext, recNode: PNode, let fields = branchNode[^1] collectMissingFields(c, fields, constrCtx) - let discriminator = recNode[0] + let discriminator = n[0] internalAssert c.config, discriminator.kind == nkSym var selectedBranch = -1 - for i in 1.. MaxSetElements or - lengthOrd(c.config, recNode[0].typ) > MaxSetElements): + lengthOrd(c.config, n[0].typ) > MaxSetElements): localError(c.config, discriminatorVal.info, "branch initialization with a runtime discriminator only " & "supports ordinal types with 2^16 elements or less.") @@ -242,7 +242,7 @@ proc semConstructFields(c: PContext, recNode: PNode, if ctorCase == nil: if discriminatorVal.typ.kind == tyRange: let rangeVals = c.getIntSetOfType(discriminatorVal.typ) - let recBranchVals = branchVals(c, recNode, selectedBranch, false) + let recBranchVals = branchVals(c, n, selectedBranch, false) let diff = rangeVals - recBranchVals if diff.len != 0: valuesInConflictError(diff) @@ -260,7 +260,7 @@ proc semConstructFields(c: PContext, recNode: PNode, else: var ctorBranchVals = branchVals(c, ctorCase, ctorIdx, true) - recBranchVals = branchVals(c, recNode, selectedBranch, false) + recBranchVals = branchVals(c, n, selectedBranch, false) branchValsDiff = ctorBranchVals - recBranchVals if branchValsDiff.len != 0: valuesInConflictError(branchValsDiff) @@ -271,14 +271,14 @@ proc semConstructFields(c: PContext, recNode: PNode, failedBranch = selectedBranch else: # With an else clause, check that all other branches don't match: - for i in 1.. Date: Sat, 1 Aug 2020 17:30:51 +0800 Subject: [PATCH 0503/3768] improve epoll docs (#15137) --- lib/posix/epoll.nim | 44 +++++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/lib/posix/epoll.nim b/lib/posix/epoll.nim index 74433631d..9f0cf75ed 100644 --- a/lib/posix/epoll.nim +++ b/lib/posix/epoll.nim @@ -45,44 +45,46 @@ type proc epoll_create*(size: cint): cint {.importc: "epoll_create", header: "".} ## Creates an epoll instance. Returns an fd for the new instance. - ## The "size" parameter is a hint specifying the number of file - ## descriptors to be associated with the new instance. The fd - ## returned by epoll_create() should be closed with close(). + ## + ## The "size" parameter is a hint specifying the number of file + ## descriptors to be associated with the new instance. The fd + ## returned by epoll_create() should be closed with close(). proc epoll_create1*(flags: cint): cint {.importc: "epoll_create1", header: "".} ## Same as epoll_create but with an FLAGS parameter. The unused SIZE - ## parameter has been dropped. + ## parameter has been dropped. proc epoll_ctl*(epfd: cint; op: cint; fd: cint | SocketHandle; event: ptr EpollEvent): cint {. importc: "epoll_ctl", header: "".} - ## Manipulate an epoll instance "epfd". Returns 0 in case of success, - ## -1 in case of error ( the "errno" variable will contain the - ## specific error code ) The "op" parameter is one of the EPOLL_CTL_* - ## constants defined above. The "fd" parameter is the target of the - ## operation. The "event" parameter describes which events the caller - ## is interested in and any associated user data. + ## Manipulate an epoll instance "epfd". Returns ``0`` in case of success, + ## ``-1`` in case of error (the "errno" variable will contain the specific error code). + ## + ## The "op" parameter is one of the ``EPOLL_CTL_*`` + ## constants defined above. The "fd" parameter is the target of the + ## operation. The "event" parameter describes which events the caller + ## is interested in and any associated user data. proc epoll_wait*(epfd: cint; events: ptr EpollEvent; maxevents: cint; timeout: cint): cint {.importc: "epoll_wait", header: "".} ## Wait for events on an epoll instance "epfd". Returns the number of - ## triggered events returned in "events" buffer. Or -1 in case of - ## error with the "errno" variable set to the specific error code. The - ## "events" parameter is a buffer that will contain triggered - ## events. The "maxevents" is the maximum number of events to be - ## returned ( usually size of "events" ). The "timeout" parameter - ## specifies the maximum wait time in milliseconds (-1 == infinite). + ## triggered events returned in "events" buffer. Or -1 in case of + ## error with the "errno" variable set to the specific error code. The + ## "events" parameter is a buffer that will contain triggered + ## events. The "maxevents" is the maximum number of events to be + ## returned ( usually size of "events" ). The "timeout" parameter + ## specifies the maximum wait time in milliseconds (-1 == infinite). ## - ## This function is a cancellation point and therefore not marked with - ## __THROW. + ## This function is a cancellation point and therefore not marked with + ## __THROW. #proc epoll_pwait*(epfd: cint; events: ptr EpollEvent; maxevents: cint; # timeout: cint; ss: ptr sigset_t): cint {. # importc: "epoll_pwait", header: "".} # Same as epoll_wait, but the thread's signal mask is temporarily -# and atomically replaced with the one provided as parameter. +# and atomically replaced with the one provided as parameter. # -# This function is a cancellation point and therefore not marked with -# __THROW. +# This function is a cancellation point and therefore not marked with +# __THROW. From e192e07bf0ac790bcb0b95ee63e62fce688ab40c Mon Sep 17 00:00:00 2001 From: alaviss Date: Sat, 1 Aug 2020 09:33:19 +0000 Subject: [PATCH 0504/3768] ci_docs: build fusion docs (#15127) --- .github/workflows/ci_docs.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/ci_docs.yml b/.github/workflows/ci_docs.yml index 816f560c9..d3d0ba612 100644 --- a/.github/workflows/ci_docs.yml +++ b/.github/workflows/ci_docs.yml @@ -11,6 +11,7 @@ on: - 'tools/dochack/dochack.nim' - 'tools/kochdocs.nim' - '.github/workflows/ci_docs.yml' + - 'koch.nim' pull_request: # Run only on changes on these files. paths: @@ -121,6 +122,10 @@ jobs: shell: bash run: ./koch boot -d:release + - name: 'Clone fusion' + shell: bash + run: ./koch fusion + - name: 'Build documentation' shell: bash run: ./koch doc --git.commit:devel From 9ff2c50155ca172e2decb0b66ee78b50af3ff51f Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 1 Aug 2020 13:16:50 +0200 Subject: [PATCH 0505/3768] fixes #15122 [backport:1.2] (#15139) --- compiler/sempass2.nim | 1 + tests/arc/tarcmisc.nim | 28 ++++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index a2da2f2e4..6db6448df 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -799,6 +799,7 @@ proc trackCall(tracked: PEffects; n: PNode) = # check required for 'nim check': if n[1].typ.len > 0: + createTypeBoundOps(tracked, n[1].typ.lastSon, n.info) createTypeBoundOps(tracked, n[1].typ, n.info) # new(x, finalizer): Problem: how to move finalizer into 'createTypeBoundOps'? diff --git a/tests/arc/tarcmisc.nim b/tests/arc/tarcmisc.nim index a7f2dd98b..542e4f316 100644 --- a/tests/arc/tarcmisc.nim +++ b/tests/arc/tarcmisc.nim @@ -319,3 +319,31 @@ proc createMachine = echo machine.factory().hello createMachine() + +# bug #15122 + +import tables + +type + BENodeKind = enum + tkBytes, + tkList, + tkDict + + BENode = object + case kind: BENodeKind + of tkBytes: strVal: string + of tkList: listVal: seq[BENode] + of tkDict: dictVal: Table[string, BENode] + +var data = { + "examples": { + "values": BENode( + kind: tkList, + listVal: @[BENode(kind: tkBytes, strVal: "test")] + ) + }.toTable() +}.toTable() + +# For ARC listVal is empty for some reason +doAssert data["examples"]["values"].listVal[0].strVal == "test" From 8e3f51f06c7008d9db4499864cced392ec02d423 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 1 Aug 2020 18:04:04 +0200 Subject: [PATCH 0506/3768] fixes #15130 (#15141) * fixes #15130 * you really have to copy from cursors --- compiler/dfa.nim | 2 +- compiler/varpartitions.nim | 6 ++- tests/arc/topt_no_cursor.nim | 77 +++++++++++++++++++++++++++++++++++- 3 files changed, 81 insertions(+), 4 deletions(-) diff --git a/compiler/dfa.nim b/compiler/dfa.nim index 85a9ccf42..d07122552 100644 --- a/compiler/dfa.nim +++ b/compiler/dfa.nim @@ -628,7 +628,7 @@ proc isAnalysableFieldAccess*(orig: PNode; owner: PSym): bool = # XXX Allow closure deref operations here if we know # the owner controlled the closure allocation? result = n.kind == nkSym and n.sym.owner == owner and - sfGlobal notin n.sym.flags and + {sfGlobal, sfThread, sfCursor} * n.sym.flags == {} and (n.sym.kind != skParam or isSinkParam(n.sym)) # or n.sym.typ.kind == tyVar) # Note: There is a different move analyzer possible that checks for # consume(param.key); param.key = newValue for all paths. Then code like diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim index 7a6cc4f63..4535c9aac 100644 --- a/compiler/varpartitions.nim +++ b/compiler/varpartitions.nim @@ -48,7 +48,7 @@ type s: seq[VarIndex] graphs: seq[MutationInfo] unanalysableMutation, performCursorInference: bool - inAsgnSource: int + inAsgnSource, inConstructor: int proc `$`*(config: ConfigRef; g: MutationInfo): string = result = "" @@ -264,8 +264,10 @@ proc analyseAsgn(c: var Partitions; dest: var VarIndex; n: PNode) = dest.flags.incl ownsData of nkCurly, nkBracket, nkPar, nkTupleConstr: + inc c.inConstructor for son in n: analyseAsgn(c, dest, son) + dec c.inConstructor if n.typ.skipTypes(abstractInst).kind == tySequence: # you must destroy a sequence: dest.flags.incl ownsData @@ -278,6 +280,8 @@ proc analyseAsgn(c: var Partitions; dest: var VarIndex; n: PNode) = else: # otherwise it's just a dependency, nothing to worry about: connect(c, dest.sym, n.sym, n.info) + # but a construct like ``[symbol]`` is dangerous: + if c.inConstructor > 0: dest.flags.incl ownsData of nkDotExpr, nkBracketExpr, nkHiddenDeref, nkDerefExpr, nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr, nkAddr, nkHiddenAddr: diff --git a/tests/arc/topt_no_cursor.nim b/tests/arc/topt_no_cursor.nim index 3ea4deaf1..fe63e7585 100644 --- a/tests/arc/topt_no_cursor.nim +++ b/tests/arc/topt_no_cursor.nim @@ -2,8 +2,9 @@ discard """ output: '''(repo: "", package: "meo", ext: "") doing shady stuff... 3 -6''' - cmd: '''nim c --gc:arc --expandArc:newTarget --expandArc:delete --hint:Performance:off $file''' +6 +(@[1], @[2])''' + cmd: '''nim c --gc:arc --expandArc:newTarget --expandArc:delete --expandArc:p1 --expandArc:tt --hint:Performance:off $file''' nimout: '''--expandArc: newTarget var @@ -37,6 +38,44 @@ var `=`(sibling.right, saved.left) `=sink`(sibling.parent, saved) `=destroy`(sibling) +-- end of expandArc ------------------------ +--expandArc: p1 + +var + lresult + lvalue + _ +`=`(lresult, [123]) +var lnext_cursor: string +_ = ( + let blitTmp = lresult + blitTmp, ";") +lvalue = _[0] +lnext_cursor = _[1] +`=sink`(result.value, lvalue) +-- end of expandArc ------------------------ +--expandArc: tt + +var + a + :tmpD + :tmpD_1 + :tmpD_2 +try: + var it_cursor = x + a = ( + wasMoved(:tmpD) + `=`(:tmpD, it_cursor.key) + :tmpD, + wasMoved(:tmpD_1) + `=`(:tmpD_1, it_cursor.val) + :tmpD_1) + echo [ + :tmpD_2 = `$`(a) + :tmpD_2] +finally: + `=destroy`(:tmpD_2) + `=destroy_1`(a) -- end of expandArc ------------------------''' """ @@ -98,3 +137,37 @@ proc main = echo five.right.value main() + +type + Maybe = object + value: seq[int] + +proc p1(): Maybe = + let lresult = @[123] + var lvalue: seq[int] + var lnext: string + (lvalue, lnext) = (lresult, ";") + + result.value = lvalue + +proc tissue15130 = + doAssert p1().value == @[123] + +tissue15130() + +type + KeyValue = tuple[key, val: seq[int]] + +proc tt(x: KeyValue) = + var it = x + let a = (it.key, it.val) + echo a + +proc encodedQuery = + var query: seq[KeyValue] + query.add (key: @[1], val: @[2]) + + for elem in query: + elem.tt() + +encodedQuery() From 3ce32a7e40c92f305a6f48017fe9323d35720f13 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sun, 2 Aug 2020 03:27:21 +0800 Subject: [PATCH 0507/3768] export asyncdispatch handles (#15140) * improve epoll docs * export handles * add comments and changelog --- changelog.md | 2 ++ lib/pure/asyncdispatch.nim | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 517919de9..281995b0e 100644 --- a/changelog.md +++ b/changelog.md @@ -143,6 +143,8 @@ `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`. +- Export `asyncdispatch.PDispatcher.handles` so that an external library can register them. + ## Language changes - The `=destroy` hook no longer has to reset its target, as the compiler now automatically inserts diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 6330a6ba9..41d67d172 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -253,7 +253,7 @@ when defined(windows) or defined(nimdoc): PDispatcher* = ref object of PDispatcherBase ioPort: Handle - handles: HashSet[AsyncFD] + handles*: HashSet[AsyncFD] # Export handles so that an external library can register them. CustomObj = object of OVERLAPPED data*: CompletionData From c619cedd7cba60d588707fbbb2905c8ac85fd12a Mon Sep 17 00:00:00 2001 From: alaviss Date: Sat, 1 Aug 2020 19:27:55 +0000 Subject: [PATCH 0508/3768] net: allow close() to ignore SSL failures due to disconnections (#15120) * net: allow close() to ignore SSL failures due to disconnections Comes with this PR is also a SIGPIPE handling contraption. * net: don't do selectSigpipe() on macOS macOS sockets have SO_NOSIGPIPE set, so an EPIPE doesn't necessary mean that a SIGPIPE happened. * net: fix alreadyBlocked logic * net: WSAESHUTDOWN is also a disconnection error --- lib/pure/net.nim | 106 +++++++++++++++++++++++++++++++++++++--- lib/windows/winlean.nim | 1 + tests/stdlib/tssl.nim | 85 +++++++++++++++++++++++++++++--- 3 files changed, 177 insertions(+), 15 deletions(-) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index f53095854..c0bfc39cc 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -75,6 +75,9 @@ export Domain, SockType, Protocol const useWinVersion = defined(Windows) or defined(nimdoc) const defineSsl = defined(ssl) or defined(nimdoc) +when useWinVersion: + from winlean import WSAESHUTDOWN + when defineSsl: import openssl @@ -187,6 +190,7 @@ proc isDisconnectionError*(flags: set[SocketFlag], lastError.int32 == WSAECONNABORTED or lastError.int32 == WSAENETRESET or lastError.int32 == WSAEDISCON or + lastError.int32 == WSAESHUTDOWN or lastError.int32 == ERROR_NETNAME_DELETED) else: SocketFlag.SafeDisconn in flags and @@ -1031,8 +1035,72 @@ proc accept*(server: Socket, client: var owned(Socket), var addrDummy = "" acceptAddr(server, client, addrDummy, flags) -proc close*(socket: Socket) = +when defined(posix): + from posix import Sigset, sigwait, sigismember, sigemptyset, sigaddset, + sigprocmask, pthread_sigmask, SIGPIPE, SIG_BLOCK, SIG_UNBLOCK + +template blockSigpipe(body: untyped): untyped = + ## Temporary block SIGPIPE within the provided code block. If SIGPIPE is + ## raised for the duration of the code block, it will be queued and will be + ## raised once the block ends. + ## + ## Within the block a `selectSigpipe()` template is provided which can be + ## used to remove SIGPIPE from the queue. Note that if SIGPIPE is **not** + ## raised at the time of call, it will block until SIGPIPE is raised. + ## + ## If SIGPIPE has already been blocked at the time of execution, the + ## signal mask is left as-is and `selectSigpipe()` will become a no-op. + ## + ## For convenience, this template is also available for non-POSIX system, + ## where `body` will be executed as-is. + when not defined(posix): + body + else: + template sigmask(how: cint, set, oset: var Sigset): untyped {.gensym.} = + ## Alias for pthread_sigmask or sigprocmask depending on the status + ## of --threads + when compileOption("threads"): + pthread_sigmask(how, set, oset) + else: + sigprocmask(how, set, oset) + + var oldSet, watchSet: Sigset + if sigemptyset(oldSet) == -1: + raiseOSError(osLastError()) + if sigemptyset(watchSet) == -1: + raiseOSError(osLastError()) + + if sigaddset(watchSet, SIGPIPE) == -1: + raiseOSError(osLastError(), "Couldn't add SIGPIPE to Sigset") + + if sigmask(SIG_BLOCK, watchSet, oldSet) == -1: + raiseOSError(osLastError(), "Couldn't block SIGPIPE") + + let alreadyBlocked = sigismember(oldSet, SIGPIPE) == 1 + + template selectSigpipe(): untyped {.used.} = + if not alreadyBlocked: + var signal: cint + let err = sigwait(watchSet, signal) + if err != 0: + raiseOSError(err.OSErrorCode, "Couldn't select SIGPIPE") + assert signal == SIGPIPE + + try: + body + finally: + if not alreadyBlocked: + if sigmask(SIG_UNBLOCK, watchSet, oldSet) == -1: + raiseOSError(osLastError(), "Couldn't unblock SIGPIPE") + +proc close*(socket: Socket, flags = {SocketFlag.SafeDisconn}) = ## Closes a socket. + ## + ## If `socket` is an SSL/TLS socket, this proc will also send a closure + ## notification to the peer. If `SafeDisconn` is in `flags`, failure to do so + ## due to disconnections will be ignored. This is generally safe in + ## practice. See + ## `here `_ for more details. try: when defineSsl: if socket.isSsl and socket.sslHandle != nil: @@ -1044,12 +1112,34 @@ proc close*(socket: Socket) = # it is valid, under the TLS standard, to perform a unidirectional # shutdown i.e not wait for the peers "close notify" alert with a second # call to SSL_shutdown - ErrClearError() - let res = SSL_shutdown(socket.sslHandle) - if res == 0: - discard - elif res != 1: - socketError(socket, res) + blockSigpipe: + ErrClearError() + let res = SSL_shutdown(socket.sslHandle) + if res == 0: + discard + elif res != 1: + let + err = osLastError() + sslError = SSL_get_error(socket.sslHandle, res) + + # If a close notification is received, failures outside of the + # protocol will be returned as SSL_ERROR_ZERO_RETURN instead + # of SSL_ERROR_SYSCALL. This fact is deduced by digging into + # SSL_get_error() source code. + if sslError == SSL_ERROR_ZERO_RETURN or + sslError == SSL_ERROR_SYSCALL: + when defined(posix) and not defined(macosx) and + not defined(nimdoc): + if err == EPIPE.OSErrorCode: + # Clear the SIGPIPE that's been raised due to + # the disconnection. + selectSigpipe() + else: + discard + if not flags.isDisconnectionError(err): + socketError(socket, res, lastError = err, flags = flags) + else: + socketError(socket, res, lastError = err, flags = flags) finally: when defineSsl: if socket.isSsl and socket.sslHandle != nil: @@ -1470,7 +1560,7 @@ proc recvFrom*(socket: Socket, data: var string, length: int, var addrLen = sizeof(sockAddress).SockLen result = recvfrom(socket.fd, cstring(data), length.cint, flags.cint, cast[ptr SockAddr](addr(sockAddress)), addr(addrLen)) - + if result != -1: data.setLen(result) address = getAddrString(cast[ptr SockAddr](addr(sockAddress))) diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index bd4cfdca7..c28700374 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -813,6 +813,7 @@ const WSAEINPROGRESS* = 10036 WSAEINTR* = 10004 WSAEWOULDBLOCK* = 10035 + WSAESHUTDOWN* = 10058 ERROR_NETNAME_DELETED* = 64 STATUS_PENDING* = 0x103 diff --git a/tests/stdlib/tssl.nim b/tests/stdlib/tssl.nim index cda3cb590..3c2b6be2b 100644 --- a/tests/stdlib/tssl.nim +++ b/tests/stdlib/tssl.nim @@ -5,13 +5,16 @@ discard """ import net, nativesockets when defined(posix): import os, posix +else: + import winlean + const SD_SEND = 1 when not defined(ssl): {.error: "This test must be compiled with -d:ssl".} const DummyData = "dummy data\n" -proc connector(port: Port) {.thread.} = +proc abruptShutdown(port: Port) {.thread.} = let clientContext = newContext(verifyMode = CVerifyNone) var client = newSocket(buffered = false) clientContext.wrapSocket(client) @@ -20,11 +23,16 @@ proc connector(port: Port) {.thread.} = discard client.recvLine() client.getFd.close() -proc main() = - let serverContext = newContext(verifyMode = CVerifyNone, - certFile = "tests/testdata/mycert.pem", - keyFile = "tests/testdata/mycert.pem") +proc notifiedShutdown(port: Port) {.thread.} = + let clientContext = newContext(verifyMode = CVerifyNone) + var client = newSocket(buffered = false) + clientContext.wrapSocket(client) + client.connect("localhost", port) + discard client.recvLine() + client.close() + +proc main() = when defined(posix): var ignoreAction = SigAction(sa_handler: SIG_IGN) @@ -34,7 +42,11 @@ proc main() = if sigaction(SIGPIPE, ignoreAction, oldSigPipeHandler) == -1: raiseOSError(osLastError(), "Couldn't ignore SIGPIPE") - block peer_close_without_shutdown: + let serverContext = newContext(verifyMode = CVerifyNone, + certFile = "tests/testdata/mycert.pem", + keyFile = "tests/testdata/mycert.pem") + + block peer_close_during_write_without_shutdown: var server = newSocket(buffered = false) defer: server.close() serverContext.wrapSocket(server) @@ -43,7 +55,7 @@ proc main() = server.listen() var clientThread: Thread[Port] - createThread(clientThread, connector, port) + createThread(clientThread, abruptShutdown, port) var peer: Socket try: @@ -60,4 +72,63 @@ proc main() = finally: peer.close() + when defined(posix): + if sigaction(SIGPIPE, oldSigPipeHandler, nil) == -1: + raiseOSError(osLastError(), "Couldn't restore SIGPIPE handler") + + block peer_close_before_received_shutdown: + var server = newSocket(buffered = false) + defer: server.close() + serverContext.wrapSocket(server) + server.bindAddr(address = "localhost") + let (_, port) = server.getLocalAddr() + server.listen() + + var clientThread: Thread[Port] + createThread(clientThread, abruptShutdown, port) + + var peer: Socket + try: + server.accept(peer) + peer.send(DummyData) + + joinThread clientThread + + # Tell the OS to close off the write side so shutdown attempts will + # be met with SIGPIPE. + when defined(posix): + discard peer.getFd.shutdown(SHUT_WR) + else: + discard peer.getFd.shutdown(SD_SEND) + finally: + peer.close() + + block peer_close_after_received_shutdown: + var server = newSocket(buffered = false) + defer: server.close() + serverContext.wrapSocket(server) + server.bindAddr(address = "localhost") + let (_, port) = server.getLocalAddr() + server.listen() + + var clientThread: Thread[Port] + createThread(clientThread, notifiedShutdown, port) + + var peer: Socket + try: + server.accept(peer) + peer.send(DummyData) + + doAssert peer.recv(1024) == "" # Get the shutdown notification + joinThread clientThread + + # Tell the OS to close off the write side so shutdown attempts will + # be met with SIGPIPE. + when defined(posix): + discard peer.getFd.shutdown(SHUT_WR) + else: + discard peer.getFd.shutdown(SD_SEND) + finally: + peer.close() + when isMainModule: main() From 98eb8410f276edcb2767517ebcb391b8369bc911 Mon Sep 17 00:00:00 2001 From: sschwarzer Date: Sat, 1 Aug 2020 22:00:25 +0200 Subject: [PATCH 0509/3768] Remove `noSideEffect` pragmas (#15143) These don't seem to make sense for the purpose of the procs and lead to errors when the `--experimental:strictFuncs` feature is enabled. See also https://github.com/nim-lang/Nim/issues/15142 --- lib/pure/strtabs.nim | 2 +- lib/pure/xmltree.nim | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pure/strtabs.nim b/lib/pure/strtabs.nim index 81ff7fbde..76f90938c 100644 --- a/lib/pure/strtabs.nim +++ b/lib/pure/strtabs.nim @@ -232,7 +232,7 @@ proc enlarge(t: StringTableRef) = swap(t.data, n) proc `[]=`*(t: StringTableRef, key, val: string) {. - rtlFunc, extern: "nstPut", noSideEffect.} = + rtlFunc, extern: "nstPut".} = ## Inserts a `(key, value)` pair into `t`. ## ## See also: diff --git a/lib/pure/xmltree.nim b/lib/pure/xmltree.nim index 9d2e4317e..00e9a031b 100644 --- a/lib/pure/xmltree.nim +++ b/lib/pure/xmltree.nim @@ -334,7 +334,7 @@ proc insert*(father, son: XmlNode, index: int) {.inline.} = else: insert(father.s, son, len(father.s)) -proc delete*(n: XmlNode, i: Natural) {.noSideEffect.} = +proc delete*(n: XmlNode, i: Natural) = ## Delete the `i`'th child of `n`. ## ## See also: From d6161e7e9ea1f40ad734c6e811e8169fe24b5e9f Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 1 Aug 2020 22:03:15 +0200 Subject: [PATCH 0510/3768] fixes #15129 [backport:1.2] (#15144) --- compiler/sempass2.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 6db6448df..41d83ed55 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -843,7 +843,7 @@ proc track(tracked: PEffects, n: PNode) = 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 + if not hasDestructor(n.typ) and sameType(n.typ, n.sym.typ): n.typ = n.sym.typ of nkHiddenAddr, nkAddr: if n[0].kind == nkSym and isLocalVar(tracked, n[0].sym): useVarNoInitCheck(tracked, n[0], n[0].sym) From 9b08c7b3754cf839492a0183125c70f1dedf6990 Mon Sep 17 00:00:00 2001 From: narimiran Date: Tue, 4 Aug 2020 09:03:20 +0200 Subject: [PATCH 0511/3768] add stale bot --- .github/stale.yml | 65 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 .github/stale.yml diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 000000000..821efc268 --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,65 @@ +# Configuration for probot-stale - https://github.com/probot/stale + +# Number of days of inactivity before an Issue or Pull Request becomes stale +daysUntilStale: 550 + +# Number of days of inactivity before an Issue or Pull Request with the stale label is closed. +# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. +daysUntilClose: 20 + +# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled) +onlyLabels: [] + +# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable +exemptLabels: + - ARC + - bounty + - Crash + - High Priority + - Next release + - Showstopper + +# Set to true to ignore issues in a project (defaults to false) +exemptProjects: false + +# Set to true to ignore issues in a milestone (defaults to false) +exemptMilestones: false + +# Set to true to ignore issues with an assignee (defaults to false) +exemptAssignees: false + +# Label to use when marking as stale +staleLabel: stale + +# Comment to post when marking as stale. Set to `false` to disable +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. + If you think it is still a valid issue, write a comment below; + otherwise it will be closed. Thank you for your contributions. + +# Comment to post when removing the stale label. +# unmarkComment: > +# Your comment here. + +# Comment to post when closing a stale Issue or Pull Request. +# closeComment: > +# Your comment here. + +# Limit the number of actions per hour, from 1-30. Default is 30 +limitPerRun: 30 + +# Limit to only `issues` or `pulls` +# only: issues + +# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls': +# pulls: +# daysUntilStale: 30 +# markComment: > +# This pull request has been automatically marked as stale because it has not had +# recent activity. It will be closed if no further activity occurs. Thank you +# for your contributions. + +# issues: +# exemptLabels: +# - confirmed From e4710f893e2314a5479839d042a517b9b27f2535 Mon Sep 17 00:00:00 2001 From: narimiran Date: Tue, 4 Aug 2020 10:47:16 +0200 Subject: [PATCH 0512/3768] stalebot: don't send messages to keep spam down --- .github/stale.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.github/stale.yml b/.github/stale.yml index 821efc268..811a43ff1 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -5,7 +5,7 @@ daysUntilStale: 550 # Number of days of inactivity before an Issue or Pull Request with the stale label is closed. # Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. -daysUntilClose: 20 +daysUntilClose: 30 # Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled) onlyLabels: [] @@ -32,11 +32,12 @@ exemptAssignees: false staleLabel: stale # Comment to post when marking as stale. Set to `false` to disable -markComment: > - This issue has been automatically marked as stale because it has not had - recent activity. - If you think it is still a valid issue, write a comment below; - otherwise it will be closed. Thank you for your contributions. +markComment: false +# markComment: > +# This issue has been automatically marked as stale because it has not had +# recent activity. +# If you think it is still a valid issue, write a comment below; +# otherwise it will be closed. Thank you for your contributions. # Comment to post when removing the stale label. # unmarkComment: > From 836660a2975ae783009501653f69f0c97cdd8b85 Mon Sep 17 00:00:00 2001 From: narimiran Date: Tue, 4 Aug 2020 11:00:04 +0200 Subject: [PATCH 0513/3768] put stale limit at 3 years It needs to be slowly brought down to limit the notifications and prevent excessive spamming. --- .github/stale.yml | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/.github/stale.yml b/.github/stale.yml index 811a43ff1..31f1f7798 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -1,7 +1,7 @@ # Configuration for probot-stale - https://github.com/probot/stale # Number of days of inactivity before an Issue or Pull Request becomes stale -daysUntilStale: 550 +daysUntilStale: 1095 # Number of days of inactivity before an Issue or Pull Request with the stale label is closed. # Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. @@ -32,12 +32,11 @@ exemptAssignees: false staleLabel: stale # Comment to post when marking as stale. Set to `false` to disable -markComment: false -# markComment: > -# This issue has been automatically marked as stale because it has not had -# recent activity. -# If you think it is still a valid issue, write a comment below; -# otherwise it will be closed. Thank you for your contributions. +markComment: > + This issue has been automatically marked as stale because it has not had + recent activity. + If you think it is still a valid issue, write a comment below; + otherwise it will be closed. Thank you for your contributions. # Comment to post when removing the stale label. # unmarkComment: > From 2d494f42f305bbac1f74ebf14c47d20c87acacde Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Tue, 4 Aug 2020 17:29:57 +0800 Subject: [PATCH 0514/3768] minor improvement (#15155) --- lib/pure/selectors.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim index 12a134ef8..6007a8250 100644 --- a/lib/pure/selectors.nim +++ b/lib/pure/selectors.nim @@ -290,7 +290,7 @@ else: skey.ident = pident skey.events = pevents skey.param = pparam - skey.data = data + skey.data = pdata when ioselSupportedPlatform: template blockSignals(newmask: var Sigset, oldmask: var Sigset) = From 64aec6eb85b79f21c9da1d9d0c475942514180c5 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 4 Aug 2020 12:45:15 +0200 Subject: [PATCH 0515/3768] fixes a collect() bug reported on the forum (#15156) [backport:1.2] --- compiler/semstmts.nim | 2 +- compiler/varpartitions.nim | 2 +- lib/pure/sugar.nim | 10 ++++++++++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 68f0cee64..1315e8960 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -159,7 +159,7 @@ proc semIf(c: PContext, n: PNode; flags: TExprFlags): PNode = if it.len == 2: openScope(c) it[0] = forceBool(c, semExprWithType(c, it[0])) - it[1] = semExprBranch(c, it[1]) + it[1] = semExprBranch(c, it[1], flags) typ = commonType(typ, it[1]) closeScope(c) elif it.len == 1: diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim index 4535c9aac..6857e1b57 100644 --- a/compiler/varpartitions.nim +++ b/compiler/varpartitions.nim @@ -11,7 +11,7 @@ ## Nim's write tracking and also for the cursor inference. ## The algorithm is a reinvention / variation of Steensgaard's ## algorithm. -## The used algorithm is "union find" with path compression. +## The used data structure is "union find" with path compression. import ast, types, lineinfos, options, msgs, renderer from trees import getMagic diff --git a/lib/pure/sugar.nim b/lib/pure/sugar.nim index d2bdb0fdc..00f4de5d8 100644 --- a/lib/pure/sugar.nim +++ b/lib/pure/sugar.nim @@ -394,3 +394,13 @@ when isMainModule: of "bird": "word" else: d assert z == @["word", "word"] + + + proc tforum = + let ans = collect(newSeq): + for y in 0..10: + if y mod 5 == 2: + for x in 0..y: + x + + tforum() From eb3ba0ae5f9804645343d9b3ae1c41199c5016e8 Mon Sep 17 00:00:00 2001 From: narimiran Date: Tue, 4 Aug 2020 13:00:09 +0200 Subject: [PATCH 0516/3768] limit stalebot a bit more --- .github/stale.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/stale.yml b/.github/stale.yml index 31f1f7798..e314fa55e 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -14,10 +14,14 @@ onlyLabels: [] exemptLabels: - ARC - bounty + - Codegen - Crash + - Generics - High Priority + - Macros - Next release - Showstopper + - Static[T] # Set to true to ignore issues in a project (defaults to false) exemptProjects: false @@ -47,7 +51,7 @@ markComment: > # Your comment here. # Limit the number of actions per hour, from 1-30. Default is 30 -limitPerRun: 30 +limitPerRun: 20 # Limit to only `issues` or `pulls` # only: issues From ef6358f0b0be71266d9fa7ae19a3b6f193b305cf Mon Sep 17 00:00:00 2001 From: cooldome Date: Tue, 4 Aug 2020 18:22:35 +0100 Subject: [PATCH 0517/3768] implement (#15153) --- compiler/vmops.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/vmops.nim b/compiler/vmops.nim index 27d1ef479..e49d1e38c 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -86,7 +86,7 @@ template wrap2svoid(op, modop) {.dirty.} = template wrapDangerous(op, modop) {.dirty.} = proc `op Wrapper`(a: VmArgs) {.nimcall.} = - if defined(nimsuggest) or c.config.cmd == cmdCheck: + if vmopsDanger notin c.config.features and (defined(nimsuggest) or c.config.cmd == cmdCheck): discard else: op(getString(a, 0), getString(a, 1)) From d9ed816b10a67e802817daa07b5b823c79bcd13e Mon Sep 17 00:00:00 2001 From: archnim <68890552+archnim@users.noreply.github.com> Date: Wed, 5 Aug 2020 16:41:42 +0000 Subject: [PATCH 0518/3768] Added the ability to initialize a deque with an openArray (#15138) * Add ability to initialize a deque with a sequence Example: var dq = initDeque[char](@['a', 'b', 'c']) * Update deques.nim * Optimized deque initialization * Sequence replaced by open array in deque initialization --- lib/pure/collections/deques.nim | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/pure/collections/deques.nim b/lib/pure/collections/deques.nim index 8150563cc..43ca536b6 100644 --- a/lib/pure/collections/deques.nim +++ b/lib/pure/collections/deques.nim @@ -84,6 +84,12 @@ proc initDeque*[T](initialSize: int = 4): Deque[T] = ## The length of a newly created deque will still be 0. result.initImpl(initialSize) +proc initDeque*[T](arr: openArray[T]): Deque[T] = + ## Create a new deque initialized with an open array + result.initImpl(nextPowerOfTwo(arr.len)) + for i in 0 ..< arr.len: + result.addLast(arr[i]) + proc len*[T](deq: Deque[T]): int {.inline.} = ## Return the number of elements of `deq`. result = deq.count From a528382e057780845585f0befa6da505462a622e Mon Sep 17 00:00:00 2001 From: cooldome Date: Fri, 7 Aug 2020 21:37:54 +0100 Subject: [PATCH 0519/3768] fix sqlgetdata regression in odbc (#15161) * fix sqlgetdata import * fix db_odbc * more fixes * fix style --- lib/impure/db_odbc.nim | 20 ++++++++------------ lib/wrappers/odbcsql.nim | 8 ++++---- 2 files changed, 12 insertions(+), 16 deletions(-) diff --git a/lib/impure/db_odbc.nim b/lib/impure/db_odbc.nim index 863cf6c8c..8aadfae73 100644 --- a/lib/impure/db_odbc.nim +++ b/lib/impure/db_odbc.nim @@ -279,7 +279,7 @@ iterator fastRows*(db: var DbConn, query: SqlQuery, ## Rows are retrieved from the server at each iteration. var rowRes: Row - sz: TSqlInteger = 0 + sz: TSqlLen = 0 cCnt: TSqlSmallInt = 0 res: TSqlSmallInt = 0 res = db.prepareFetch(query, args) @@ -293,8 +293,7 @@ iterator fastRows*(db: var DbConn, query: SqlQuery, for colId in 1..cCnt: buf[0] = '\0' db.sqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR, - cast[cstring](buf.addr), 4095.TSqlSmallInt, - sz.addr)) + cast[cstring](buf.addr), 4095, sz.addr)) rowRes[colId-1] = $(addr buf) yield rowRes res = SQLFetch(db.stmt) @@ -308,7 +307,7 @@ iterator instantRows*(db: var DbConn, query: SqlQuery, ## on demand using []. Returned handle is valid only within the iterator body. var rowRes: Row = @[] - sz: TSqlInteger = 0 + sz: TSqlLen = 0 cCnt: TSqlSmallInt = 0 res: TSqlSmallInt = 0 res = db.prepareFetch(query, args) @@ -322,8 +321,7 @@ iterator instantRows*(db: var DbConn, query: SqlQuery, for colId in 1..cCnt: buf[0] = '\0' db.sqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR, - cast[cstring](buf.addr), 4095.TSqlSmallInt, - sz.addr)) + cast[cstring](buf.addr), 4095, sz.addr)) rowRes[colId-1] = $(addr buf) yield (row: rowRes, len: cCnt.int) res = SQLFetch(db.stmt) @@ -349,7 +347,7 @@ proc getRow*(db: var DbConn, query: SqlQuery, ## will return a Row with empty strings for each column. var rowRes: Row - sz: TSqlInteger = 0 + sz: TSqlLen = 0 cCnt: TSqlSmallInt = 0 res: TSqlSmallInt = 0 res = db.prepareFetch(query, args) @@ -362,8 +360,7 @@ proc getRow*(db: var DbConn, query: SqlQuery, for colId in 1..cCnt: buf[0] = '\0' db.sqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR, - cast[cstring](buf.addr), 4095.TSqlSmallInt, - sz.addr)) + cast[cstring](buf.addr), 4095, sz.addr)) rowRes[colId-1] = $(addr buf) res = SQLFetch(db.stmt) result = rowRes @@ -377,7 +374,7 @@ proc getAllRows*(db: var DbConn, query: SqlQuery, var rows: seq[Row] = @[] rowRes: Row - sz: TSqlInteger = 0 + sz: TSqlLen = 0 cCnt: TSqlSmallInt = 0 res: TSqlSmallInt = 0 res = db.prepareFetch(query, args) @@ -391,8 +388,7 @@ proc getAllRows*(db: var DbConn, query: SqlQuery, for colId in 1..cCnt: buf[0] = '\0' db.sqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR, - cast[cstring](buf.addr), 4095.TSqlSmallInt, - sz.addr)) + cast[cstring](buf.addr), 4095, sz.addr)) rowRes[colId-1] = $(addr buf) rows.add(rowRes) res = SQLFetch(db.stmt) diff --git a/lib/wrappers/odbcsql.nim b/lib/wrappers/odbcsql.nim index 7836f087d..4e37e1a17 100644 --- a/lib/wrappers/odbcsql.nim +++ b/lib/wrappers/odbcsql.nim @@ -718,19 +718,19 @@ proc SQLNumResultCols*(StatementHandle: SqlHStmt, ColumnCount: var TSqlSmallInt) proc SQLDescribeCol*(StatementHandle: SqlHStmt, ColumnNumber: SqlUSmallInt, ColumnName: PSQLCHAR, BufferLength: TSqlSmallInt, NameLength: var TSqlSmallInt, DataType: var TSqlSmallInt, - ColumnSize: var TSQLULEN, + ColumnSize: var TSqlULen, DecimalDigits: var TSqlSmallInt, Nullable: var TSqlSmallInt): TSqlSmallInt{. dynlib: odbclib, importc.} proc SQLFetchScroll*(StatementHandle: SqlHStmt, FetchOrientation: TSqlSmallInt, - FetchOffset: TSqlInteger): TSqlSmallInt{.dynlib: odbclib, + FetchOffset: TSqlLen): TSqlSmallInt{.dynlib: odbclib, importc.} proc SQLExtendedFetch*(hstmt: SqlHStmt, fFetchType: SqlUSmallInt, - irow: TSqlInteger, pcrow: var TSQLULEN, + irow: TSqlLen, pcrow: var TSqlULen, rgfRowStatus: PSQLUSMALLINT): TSqlSmallInt{.dynlib: odbclib, importc.} proc SQLGetData*(StatementHandle: SqlHStmt, ColumnNumber: SqlUSmallInt, TargetType: TSqlSmallInt, TargetValue: SqlPointer, - BufferLength: TSqlInteger, StrLen_or_Ind: PSQLINTEGER): TSqlSmallInt{. + BufferLength: TSqlLen, StrLen_or_Ind: ptr TSqlLen): TSqlSmallInt{. dynlib: odbclib, importc.} proc SQLSetStmtAttr*(StatementHandle: SqlHStmt, Attribute: TSqlInteger, Value: SqlPointer, StringLength: TSqlInteger): TSqlSmallInt{. From 6f482ab9346230f8e9c24f71091bcd5ca2cb41f8 Mon Sep 17 00:00:00 2001 From: ee7 <45465154+ee7@users.noreply.github.com> Date: Fri, 7 Aug 2020 22:54:34 +0200 Subject: [PATCH 0520/3768] deques.nim: Refactor the `toDeque` functionality (#15166) This commit polishes the new proc introduced by d9ed816b10a6. Changes: - Rename to `toDeque` for more consistency with well-known procs like `toHashSet` and `toTable`. - Rename the `openArray` parameter. The name `arr` was potentially less clear given that the proc can be used with a seq (or string). - Add a `since` annotation. - Reword the doc comment, and clarify that ordering is preserved. - Add runnableExamples. - Add "see also" cross linking between `initDeque` and `toDeque`. - Remove duplicate `nextPowerOfTwo`. The `initImpl` template already includes it. - Implement the proc using the `items` iterator, rather than indexing. This matches the implementation of `sets.toHashSet` and `tables.toTable`. - Add a test within `when isMainModule`. - Add a changelog entry. --- changelog.md | 3 +++ lib/pure/collections/deques.nim | 23 ++++++++++++++++++----- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/changelog.md b/changelog.md index 281995b0e..f6f1f94cd 100644 --- a/changelog.md +++ b/changelog.md @@ -145,6 +145,9 @@ - Export `asyncdispatch.PDispatcher.handles` so that an external library can register them. +- Added `deques.toDeque`, which creates a deque from an openArray. The usage is + similar to procs such as `sets.toHashSet` and `tables.toTable`. Previously, + it was necessary to create an empty deque and add items manually. ## Language changes - The `=destroy` hook no longer has to reset its target, as the compiler now automatically inserts diff --git a/lib/pure/collections/deques.nim b/lib/pure/collections/deques.nim index 43ca536b6..54756707d 100644 --- a/lib/pure/collections/deques.nim +++ b/lib/pure/collections/deques.nim @@ -82,13 +82,25 @@ proc initDeque*[T](initialSize: int = 4): Deque[T] = ## Optionally, the initial capacity can be reserved via `initialSize` ## as a performance optimization. ## The length of a newly created deque will still be 0. + ## + ## See also: + ## * `toDeque proc <#toDeque,openArray[T]>`_ result.initImpl(initialSize) -proc initDeque*[T](arr: openArray[T]): Deque[T] = - ## Create a new deque initialized with an open array - result.initImpl(nextPowerOfTwo(arr.len)) - for i in 0 ..< arr.len: - result.addLast(arr[i]) +proc toDeque*[T](x: openArray[T]): Deque[T] {.since: (1, 3).} = + ## Creates a new deque that contains the elements of `x` (in the same order). + ## + ## See also: + ## * `initDeque proc <#initDeque,int>`_ + runnableExamples: + var a = toDeque([7, 8, 9]) + assert len(a) == 3 + assert a.popFirst == 7 + assert len(a) == 2 + + result.initImpl(x.len) + for item in items(x): + result.addLast(item) proc len*[T](deq: Deque[T]): int {.inline.} = ## Return the number of elements of `deq`. @@ -534,6 +546,7 @@ when isMainModule: assert first == 123 assert second == 9 assert($deq == "[4, 56, 6, 789]") + assert deq == [4, 56, 6, 789].toDeque assert deq[0] == deq.peekFirst and deq.peekFirst == 4 #assert deq[^1] == deq.peekLast and deq.peekLast == 789 From eee3b189ff867c0149434e198e89802eca4920ce Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sat, 8 Aug 2020 13:24:54 +0800 Subject: [PATCH 0521/3768] fix #15148 (#15149) * fix #15148 Co-authored-by: alaviss --- lib/pure/asyncfile.nim | 15 +++++++++++---- tests/async/t15148.nim | 12 ++++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) create mode 100644 tests/async/t15148.nim diff --git a/lib/pure/asyncfile.nim b/lib/pure/asyncfile.nim index ad111ec50..71b42c763 100644 --- a/lib/pure/asyncfile.nim +++ b/lib/pure/asyncfile.nim @@ -379,7 +379,7 @@ proc writeBuffer*(f: AsyncFile, buf: pointer, size: int): Future[void] = proc cb(fd: AsyncFD): bool = result = true - let remainderSize = size-written + let remainderSize = size - written var cbuf = cast[cstring](buf) let res = write(fd.cint, addr cbuf[written], remainderSize.cint) if res < 0: @@ -409,7 +409,7 @@ proc write*(f: AsyncFile, data: string): Future[void] = var copy = data when defined(windows) or defined(nimdoc): var buffer = alloc0(data.len) - copyMem(buffer, addr copy[0], data.len) + copyMem(buffer, copy.cstring, data.len) var ol = newCustom() ol.data = CompletionData(fd: f.fd, cb: @@ -454,8 +454,15 @@ proc write*(f: AsyncFile, data: string): Future[void] = proc cb(fd: AsyncFD): bool = result = true - let remainderSize = data.len-written - let res = write(fd.cint, addr copy[written], remainderSize.cint) + + let remainderSize = data.len - written + + let res = + if data.len == 0: + write(fd.cint, copy.cstring, 0) + else: + write(fd.cint, addr copy[written], remainderSize.cint) + if res < 0: let lastError = osLastError() if lastError.int32 != EAGAIN: diff --git a/tests/async/t15148.nim b/tests/async/t15148.nim new file mode 100644 index 000000000..ba14c1157 --- /dev/null +++ b/tests/async/t15148.nim @@ -0,0 +1,12 @@ +import asyncdispatch, asyncfile, os + +const Filename = "t15148.txt" + +proc saveEmpty() {.async.} = + let + text = "" + file = openAsync(Filename, fmWrite) + await file.write(text) + file.close() + +waitFor saveEmpty() From 0bc8dd0b0065534b2dfab53b1844a9f6cc36844e Mon Sep 17 00:00:00 2001 From: c-blake Date: Sat, 8 Aug 2020 01:26:20 -0400 Subject: [PATCH 0522/3768] Add `iterator inotify_events` which is *almost always* needed logic for (#15152) client code since Linux `inotify` is much like Linux `getdents64`. Expanding on "almost always"..The only time that this `iterator` logic is ***not*** needed on the output of a `read` from inotify fd's is when one passes a length to `read` *guaranteed* to only pass one event struct in the buffer. That unusual circumstance requires (at least!) knowing the length of the delivered filename before an event occurs, and the filename itself is optional for some event types. It is *far* more common to not know lengths in advance which means one passes a buffer big enough for at least one maximum length directory entry (256 bytes) which is then also big enough for *many* "typical" length entries and therefore many events. In such more common scenarios this iterator logic is definitely needed. Further, not using this logic, yet treating the return from read as "the whole answer" can test ok on "thin" event streams (e.g. 1 event per ms), hiding a latent bug of processing only the first event. --- lib/posix/inotify.nim | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/posix/inotify.nim b/lib/posix/inotify.nim index 79b408425..db698c59c 100644 --- a/lib/posix/inotify.nim +++ b/lib/posix/inotify.nim @@ -73,6 +73,20 @@ proc inotify_rm_watch*(fd: cint; wd: cint): cint {.cdecl, importc: "inotify_rm_watch", header: "".} ## Remove the watch specified by WD from the inotify instance FD. +iterator inotify_events*(evs: pointer, n: int): ptr InotifyEvent = + ## Abstract the packed buffer interface to yield event object pointers. + ## + ## .. code-block:: Nim + ## var evs = newSeq[byte](8192) # Already did inotify_init+add_watch + ## while (let n = read(fd, evs[0].addr, 8192); n) > 0: # read forever + ## for e in inotify_events(evs[0].addr, n): echo e[].len # echo name lens + var ev: ptr InotifyEvent = cast[ptr InotifyEvent](evs) + var n = n + while n > 0: + yield ev + let sz = InotifyEvent.sizeof + int(ev[].len) + n -= sz + ev = cast[ptr InotifyEvent](cast[uint](ev) + uint(sz)) runnableExamples: when defined(linux): From 38068f977843c5070667f40c199f037f9a838d7c Mon Sep 17 00:00:00 2001 From: Clyybber Date: Sat, 8 Aug 2020 12:36:20 +0200 Subject: [PATCH 0523/3768] Make explicit {.nimcall.} a seperate calling convention --- compiler/ast.nim | 7 +++++-- compiler/ccgexprs.nim | 2 +- compiler/ccgtypes.nim | 4 ++-- compiler/cgmeth.nim | 2 +- compiler/lambdalifting.nim | 4 ++-- compiler/pragmas.nim | 6 +++--- compiler/semdata.nim | 2 +- compiler/sigmatch.nim | 4 ++-- compiler/transf.nim | 2 +- compiler/types.nim | 2 +- tests/closure/tinvalidclosure.nim | 2 +- tests/closure/tinvalidclosure4.nim | 9 +++++++++ 12 files changed, 29 insertions(+), 17 deletions(-) create mode 100644 tests/closure/tinvalidclosure4.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index 1e619e51b..9f9ead23d 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -17,7 +17,8 @@ export int128 type TCallingConvention* = enum - ccDefault, # proc has no explicit calling convention + ccImplicit, # proc has no explicit calling convention + ccNimCall, # proc was explicitly marked as nimcall ccStdCall, # procedure is stdcall ccCDecl, # cdecl ccSafeCall, # safecall @@ -30,10 +31,12 @@ type ccNoConvention # needed for generating proper C procs sometimes const - CallingConvToStr*: array[TCallingConvention, string] = ["", "stdcall", + ccDefault* = {ccImplicit, ccNimCall} + CallingConvToStr*: array[TCallingConvention, string] = ["", "nimcall", "stdcall", "cdecl", "safecall", "syscall", "inline", "noinline", "fastcall", "thiscall", "closure", "noconv"] + type TNodeKind* = enum # order is extremely important, because ranges are used # to check whether a node belongs to a certain class diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 33231b1bc..feaabc59e 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1240,7 +1240,7 @@ proc rawGenNew(p: BProc, a: var TLoc, sizeExpr: Rope; needsInit: bool) = # the prototype of a destructor is ``=destroy(x: var T)`` and that of a # finalizer is: ``proc (x: ref T) {.nimcall.}``. We need to check the calling # convention at least: - if bt.destructor.typ == nil or bt.destructor.typ.callConv != ccDefault: + if bt.destructor.typ == nil or bt.destructor.typ.callConv notin ccDefault: localError(p.module.config, a.lode.info, "the destructor that is turned into a finalizer needs " & "to have the 'nimcall' calling convention") diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index f1dc85818..b64d0664b 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -234,7 +234,7 @@ proc isInvalidReturnType(conf: ConfigRef; rettype: PType): bool = else: result = false const - CallingConvToStr: array[TCallingConvention, string] = ["N_NIMCALL", + CallingConvToStr: array[TCallingConvention, string] = ["N_NIMCALL", "N_NIMCALL", "N_STDCALL", "N_CDECL", "N_SAFECALL", "N_SYSCALL", # this is probably not correct for all platforms, # but one can #define it to what one wants @@ -1297,7 +1297,7 @@ proc genHook(m: BModule; t: PType; info: TLineInfo; op: TTypeAttachedOp): Rope = # the prototype of a destructor is ``=destroy(x: var T)`` and that of a # finalizer is: ``proc (x: ref T) {.nimcall.}``. We need to check the calling # convention at least: - if theProc.typ == nil or theProc.typ.callConv != ccDefault: + if theProc.typ == nil or theProc.typ.callConv notin ccDefault: localError(m.config, info, theProc.name.s & " needs to have the 'nimcall' calling convention") diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim index edf9db383..9b871a898 100644 --- a/compiler/cgmeth.nim +++ b/compiler/cgmeth.nim @@ -113,7 +113,7 @@ proc createDispatcher(s: PSym): PSym = excl(disp.flags, sfExported) disp.typ = copyType(disp.typ, disp.typ.owner, false) # we can't inline the dispatcher itself (for now): - if disp.typ.callConv == ccInline: disp.typ.callConv = ccDefault + if disp.typ.callConv == ccInline: disp.typ.callConv = ccNimCall disp.ast = copyTree(s.ast) disp.ast[bodyPos] = newNodeI(nkEmpty, s.info) disp.loc.r = nil diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index c1cd07583..e113b81e4 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -293,7 +293,7 @@ proc markAsClosure(g: ModuleGraph; owner: PSym; n: PNode) = ("'$1' is of type <$2> which cannot be captured as it would violate memory" & " safety, declared here: $3; using '-d:nimWorkaround14447' helps in some cases") % [s.name.s, typeToString(s.typ), g.config$s.info]) - elif owner.typ.callConv notin {ccClosure, ccDefault}: + elif owner.typ.callConv notin {ccClosure, ccImplicit}: localError(g.config, n.info, "illegal capture '$1' because '$2' has the calling convention: <$3>" % [s.name.s, owner.name.s, CallingConvToStr[owner.typ.callConv]]) incl(owner.typ.flags, tfCapturesEnv) @@ -822,7 +822,7 @@ proc semCaptureSym*(s, owner: PSym) = var o = owner.skipGenericOwner while o != nil and o.kind != skModule: if s.owner == o: - if owner.typ.callConv in {ccClosure, ccDefault} or owner.kind == skIterator: + if owner.typ.callConv in {ccClosure, ccImplicit} or owner.kind == skIterator: owner.typ.callConv = ccClosure propagateClosure(owner.skipGenericOwner, s.owner) else: diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index c9fe3a5e1..d19584d48 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -247,7 +247,7 @@ proc processMagic(c: PContext, n: PNode, s: PSym) = proc wordToCallConv(sw: TSpecialWord): TCallingConvention = # this assumes that the order of special words and calling conventions is # the same - result = TCallingConvention(ord(ccDefault) + ord(sw) - ord(wNimcall)) + TCallingConvention(ord(ccNimCall) + ord(sw) - ord(wNimcall)) proc isTurnedOn(c: PContext, n: PNode): bool = if n.kind in nkPragmaCallKinds and n.len == 2: @@ -326,7 +326,7 @@ proc processDynLib(c: PContext, n: PNode, sym: PSym) = # a calling convention that doesn't introduce custom name mangling # cdecl is the default - the user can override this explicitly if sym.kind in routineKinds and sym.typ != nil and - sym.typ.callConv == ccDefault: + sym.typ.callConv == ccImplicit: sym.typ.callConv = ccCDecl proc processNote(c: PContext, n: PNode) = @@ -978,7 +978,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, incl(sym.flags, sfProcvar) if sym.typ != nil: incl(sym.typ.flags, tfThread) - if sym.typ.callConv == ccClosure: sym.typ.callConv = ccDefault + if sym.typ.callConv == ccClosure: sym.typ.callConv = ccNimCall of wGcSafe: noVal(c, it) if sym != nil: diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 5bd6c4cf2..5b1e425d7 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -212,7 +212,7 @@ proc considerGenSyms*(c: PContext; n: PNode) = proc newOptionEntry*(conf: ConfigRef): POptionEntry = new(result) result.options = conf.options - result.defaultCC = ccDefault + result.defaultCC = ccImplicit result.dynlib = nil result.notes = conf.notes result.warningAsErrors = conf.warningAsErrors diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 7d97944ce..34b75861a 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -639,11 +639,11 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = return isNone elif f.callConv != a.callConv: # valid to pass a 'nimcall' thingie to 'closure': - if f.callConv == ccClosure and a.callConv == ccDefault: + if f.callConv == ccClosure and a.callConv in ccDefault: result = if result == isInferred: isInferredConvertible elif result == isBothMetaConvertible: isBothMetaConvertible else: isConvertible - else: + elif not(f.callConv in ccDefault and a.callConv in ccDefault): return isNone when useEffectSystem: if compatibleEffects(f, a) != efCompat: return isNone diff --git a/compiler/transf.nim b/compiler/transf.nim index 29344e7a1..6d497c546 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -539,7 +539,7 @@ proc transformConv(c: PTransf, n: PNode): PNode = # happens sometimes for generated assignments, etc. of tyProc: result = transformSons(c, n) - if dest.callConv == ccClosure and source.callConv == ccDefault: + if dest.callConv == ccClosure and source.callConv in ccDefault: result = generateThunk(c, result[1], dest) else: result = transformSons(c, n) diff --git a/compiler/types.nim b/compiler/types.nim index 94fe8fd54..8d94423a8 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -679,7 +679,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = if i < t.len - 1: result.add(", ") result.add(')') if t.len > 0 and t[0] != nil: result.add(": " & typeToString(t[0])) - var prag = if t.callConv == ccDefault: "" else: CallingConvToStr[t.callConv] + var prag = if t.callConv == ccImplicit: "" else: CallingConvToStr[t.callConv] if tfNoSideEffect in t.flags: addSep(prag) prag.add("noSideEffect") diff --git a/tests/closure/tinvalidclosure.nim b/tests/closure/tinvalidclosure.nim index b2d8bd28d..47f3f105f 100644 --- a/tests/closure/tinvalidclosure.nim +++ b/tests/closure/tinvalidclosure.nim @@ -1,5 +1,5 @@ discard """ - errormsg: "type mismatch: got " + errormsg: "type mismatch: got " line: 12 """ diff --git a/tests/closure/tinvalidclosure4.nim b/tests/closure/tinvalidclosure4.nim new file mode 100644 index 000000000..7985a2488 --- /dev/null +++ b/tests/closure/tinvalidclosure4.nim @@ -0,0 +1,9 @@ +discard """ + errormsg: "illegal capture 'v'" + line: 7 +""" + +proc outer(v: int) = + proc b {.nimcall.} = echo v + b() +outer(5) From 78b2012a9264448fdde8d1018f6623339c113f04 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Sat, 8 Aug 2020 12:44:45 +0200 Subject: [PATCH 0524/3768] Add testcase for #5688 --- tests/closure/tnested.nim | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/closure/tnested.nim b/tests/closure/tnested.nim index dbbe9ba58..7a1881a60 100644 --- a/tests/closure/tnested.nim +++ b/tests/closure/tnested.nim @@ -33,6 +33,7 @@ py py px 6 +proc (){.closure, gcsafe, locks: 0.} ''' """ @@ -177,3 +178,19 @@ block tclosure2: outer2() + +# bug #5688 + +import typetraits + +proc myDiscard[T](a: T) = discard + +proc foo() = + let a = 5 + let f = (proc() = + myDiscard (proc() = echo a) + ) + echo name(type(f)) + +foo() + From 2a8d010742f2429bc31e319554302ffc2a8503a2 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Sat, 8 Aug 2020 12:50:06 +0200 Subject: [PATCH 0525/3768] Fix bootstrapping --- compiler/vmops.nim | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/compiler/vmops.nim b/compiler/vmops.nim index e49d1e38c..7d623ad86 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -85,10 +85,11 @@ template wrap2svoid(op, modop) {.dirty.} = modop op template wrapDangerous(op, modop) {.dirty.} = - proc `op Wrapper`(a: VmArgs) {.nimcall.} = - if vmopsDanger notin c.config.features and (defined(nimsuggest) or c.config.cmd == cmdCheck): + if vmopsDanger notin c.config.features and (defined(nimsuggest) or c.config.cmd == cmdCheck): + proc `op Wrapper`(a: VmArgs) {.nimcall.} = discard - else: + else: + proc `op Wrapper`(a: VmArgs) {.nimcall.} = op(getString(a, 0), getString(a, 1)) modop op From 26a867c83ca162373b8a6f780443d253f70c8912 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Sat, 8 Aug 2020 13:22:53 +0200 Subject: [PATCH 0526/3768] Remove little lies :) --- compiler/vmops.nim | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/compiler/vmops.nim b/compiler/vmops.nim index 7d623ad86..298a02bb6 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -88,10 +88,11 @@ template wrapDangerous(op, modop) {.dirty.} = if vmopsDanger notin c.config.features and (defined(nimsuggest) or c.config.cmd == cmdCheck): proc `op Wrapper`(a: VmArgs) {.nimcall.} = discard + modop op else: proc `op Wrapper`(a: VmArgs) {.nimcall.} = op(getString(a, 0), getString(a, 1)) - modop op + modop op proc getCurrentExceptionMsgWrapper(a: VmArgs) {.nimcall.} = setResult(a, if a.currentException.isNil: "" @@ -187,9 +188,9 @@ proc registerAdditionalOps*(c: PCtx) = registerCallback c, "stdlib.*.staticWalkDir", proc (a: VmArgs) {.nimcall.} = setResult(a, staticWalkDirImpl(getString(a, 0), getBool(a, 1))) when defined(nimHasInvariant): - registerCallback c, "stdlib.compilesettings.querySetting", proc (a: VmArgs) {.nimcall.} = + registerCallback c, "stdlib.compilesettings.querySetting", proc (a: VmArgs) = setResult(a, querySettingImpl(c.config, getInt(a, 0))) - registerCallback c, "stdlib.compilesettings.querySettingSeq", proc (a: VmArgs) {.nimcall.} = + registerCallback c, "stdlib.compilesettings.querySettingSeq", proc (a: VmArgs) = setResult(a, querySettingSeqImpl(c.config, getInt(a, 0))) if defined(nimsuggest) or c.config.cmd == cmdCheck: @@ -201,14 +202,14 @@ proc registerAdditionalOps*(c: PCtx) = registerCallback c, "stdlib.os.getCurrentCompilerExe", proc (a: VmArgs) {.nimcall.} = setResult(a, getAppFilename()) - registerCallback c, "stdlib.macros.symBodyHash", proc (a: VmArgs) {.nimcall.} = + registerCallback c, "stdlib.macros.symBodyHash", proc (a: VmArgs) = let n = getNode(a, 0) if n.kind != nkSym: stackTrace(c, PStackFrame(prc: c.prc.sym, comesFrom: 0, next: nil), c.exceptionInstr, "symBodyHash() requires a symbol. '" & $n & "' is of kind '" & $n.kind & "'", n.info) setResult(a, $symBodyDigest(c.graph, n.sym)) - registerCallback c, "stdlib.macros.isExported", proc(a: VmArgs) {.nimcall.} = + registerCallback c, "stdlib.macros.isExported", proc(a: VmArgs) = let n = getNode(a, 0) if n.kind != nkSym: stackTrace(c, PStackFrame(prc: c.prc.sym, comesFrom: 0, next: nil), c.exceptionInstr, From 2a8bf470af995f4e7d5bb8a0f49bb2226f95c311 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 8 Aug 2020 23:08:34 +0200 Subject: [PATCH 0527/3768] fixes #15101 [backport] (#15171) --- compiler/semfields.nim | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/compiler/semfields.nim b/compiler/semfields.nim index a911ae823..cea084d3a 100644 --- a/compiler/semfields.nim +++ b/compiler/semfields.nim @@ -23,7 +23,7 @@ proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode = result = newNode(nkEmpty) return case n.kind - of nkEmpty..pred(nkIdent), succ(nkSym)..nkNilLit: result = n + of nkEmpty..pred(nkIdent), succ(nkSym)..nkNilLit: result = copyNode(n) of nkIdent, nkSym: result = n let ident = considerQuotedIdent(c.c, n) @@ -52,8 +52,7 @@ proc instFieldLoopBody(c: TFieldInstCtx, n: PNode, forLoop: PNode): PNode = if n.kind == nkContinueStmt: localError(c.c.config, n.info, "'continue' not supported in a 'fields' loop") - result = copyNode(n) - newSons(result, n.len) + result = shallowCopy(n) for i in 0.. Date: Sun, 9 Aug 2020 15:12:53 +0300 Subject: [PATCH 0528/3768] [FIX] strtabs interpolation in nimscript (#15172) * [FIX] strtabs interpolation in nimscript * [TEST] Use `static` in strtabs test --- lib/pure/strtabs.nim | 2 +- tests/stdlib/tstrtabs.nims | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 tests/stdlib/tstrtabs.nims diff --git a/lib/pure/strtabs.nim b/lib/pure/strtabs.nim index 76f90938c..676e8eb1d 100644 --- a/lib/pure/strtabs.nim +++ b/lib/pure/strtabs.nim @@ -302,7 +302,7 @@ proc getValue(t: StringTableRef, flags: set[FormatFlag], key: string): string = when defined(js) or defined(nimscript): result = "" else: - if useEnvironment in flags: result = os.getEnv(key).string + if useEnvironment in flags: result = getEnv(key).string else: result = "" if result.len == 0: if useKey in flags: result = '$' & key diff --git a/tests/stdlib/tstrtabs.nims b/tests/stdlib/tstrtabs.nims new file mode 100644 index 000000000..c8ed4ac40 --- /dev/null +++ b/tests/stdlib/tstrtabs.nims @@ -0,0 +1,5 @@ +import strtabs + +static: + let t = {"name": "John", "city": "Monaco"}.newStringTable + doAssert "${name} lives in ${city}" % t == "John lives in Monaco" From b022576ce96f5c166c7f5684bef9de682704cb1b Mon Sep 17 00:00:00 2001 From: Clyybber Date: Mon, 10 Aug 2020 12:57:32 +0200 Subject: [PATCH 0529/3768] Use typeflag instead --- compiler/ast.nim | 8 +++----- compiler/ccgexprs.nim | 2 +- compiler/ccgtypes.nim | 4 ++-- compiler/evalffi.nim | 2 +- compiler/lambdalifting.nim | 5 +++-- compiler/pragmas.nim | 6 ++++-- compiler/semdata.nim | 2 +- compiler/sigmatch.nim | 4 ++-- compiler/transf.nim | 2 +- compiler/types.nim | 2 +- tests/closure/tinvalidclosure5.nim | 10 ++++++++++ 11 files changed, 29 insertions(+), 18 deletions(-) create mode 100644 tests/closure/tinvalidclosure5.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index 9f9ead23d..62c1e3894 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -17,8 +17,7 @@ export int128 type TCallingConvention* = enum - ccImplicit, # proc has no explicit calling convention - ccNimCall, # proc was explicitly marked as nimcall + ccNimCall, # nimcall, also the default ccStdCall, # procedure is stdcall ccCDecl, # cdecl ccSafeCall, # safecall @@ -31,12 +30,10 @@ type ccNoConvention # needed for generating proper C procs sometimes const - ccDefault* = {ccImplicit, ccNimCall} - CallingConvToStr*: array[TCallingConvention, string] = ["", "nimcall", "stdcall", + CallingConvToStr*: array[TCallingConvention, string] = ["nimcall", "stdcall", "cdecl", "safecall", "syscall", "inline", "noinline", "fastcall", "thiscall", "closure", "noconv"] - type TNodeKind* = enum # order is extremely important, because ranges are used # to check whether a node belongs to a certain class @@ -563,6 +560,7 @@ type tfCompleteStruct # (for importc types); type is fully specified, allowing to compute # sizeof, alignof, offsetof at CT + tfExplicitCallConv TTypeFlags* = set[TTypeFlag] diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index feaabc59e..a97d15d70 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1240,7 +1240,7 @@ proc rawGenNew(p: BProc, a: var TLoc, sizeExpr: Rope; needsInit: bool) = # the prototype of a destructor is ``=destroy(x: var T)`` and that of a # finalizer is: ``proc (x: ref T) {.nimcall.}``. We need to check the calling # convention at least: - if bt.destructor.typ == nil or bt.destructor.typ.callConv notin ccDefault: + if bt.destructor.typ == nil or bt.destructor.typ.callConv != ccNimCall: localError(p.module.config, a.lode.info, "the destructor that is turned into a finalizer needs " & "to have the 'nimcall' calling convention") diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index b64d0664b..fad6093ed 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -234,7 +234,7 @@ proc isInvalidReturnType(conf: ConfigRef; rettype: PType): bool = else: result = false const - CallingConvToStr: array[TCallingConvention, string] = ["N_NIMCALL", "N_NIMCALL", + CallingConvToStr: array[TCallingConvention, string] = ["N_NIMCALL", "N_STDCALL", "N_CDECL", "N_SAFECALL", "N_SYSCALL", # this is probably not correct for all platforms, # but one can #define it to what one wants @@ -1297,7 +1297,7 @@ proc genHook(m: BModule; t: PType; info: TLineInfo; op: TTypeAttachedOp): Rope = # the prototype of a destructor is ``=destroy(x: var T)`` and that of a # finalizer is: ``proc (x: ref T) {.nimcall.}``. We need to check the calling # convention at least: - if theProc.typ == nil or theProc.typ.callConv notin ccDefault: + if theProc.typ == nil or theProc.typ.callConv != ccNimCall: localError(m.config, info, theProc.name.s & " needs to have the 'nimcall' calling convention") diff --git a/compiler/evalffi.nim b/compiler/evalffi.nim index f89451f51..0d471cf98 100644 --- a/compiler/evalffi.nim +++ b/compiler/evalffi.nim @@ -104,7 +104,7 @@ proc mapType(conf: ConfigRef, t: ast.PType): ptr libffi.Type = proc mapCallConv(conf: ConfigRef, cc: TCallingConvention, info: TLineInfo): TABI = case cc - of ccDefault: result = DEFAULT_ABI + of ccNimCall: result = DEFAULT_ABI of ccStdCall: result = when defined(windows) and defined(x86): STDCALL else: DEFAULT_ABI of ccCDecl: result = DEFAULT_ABI else: diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index e113b81e4..c23ca52cb 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -293,7 +293,7 @@ proc markAsClosure(g: ModuleGraph; owner: PSym; n: PNode) = ("'$1' is of type <$2> which cannot be captured as it would violate memory" & " safety, declared here: $3; using '-d:nimWorkaround14447' helps in some cases") % [s.name.s, typeToString(s.typ), g.config$s.info]) - elif owner.typ.callConv notin {ccClosure, ccImplicit}: + elif not (owner.typ.callConv == ccClosure or owner.typ.callConv == ccNimCall and tfExplicitCallConv notin owner.typ.flags): localError(g.config, n.info, "illegal capture '$1' because '$2' has the calling convention: <$3>" % [s.name.s, owner.name.s, CallingConvToStr[owner.typ.callConv]]) incl(owner.typ.flags, tfCapturesEnv) @@ -822,7 +822,8 @@ proc semCaptureSym*(s, owner: PSym) = var o = owner.skipGenericOwner while o != nil and o.kind != skModule: if s.owner == o: - if owner.typ.callConv in {ccClosure, ccImplicit} or owner.kind == skIterator: + if owner.typ.callConv == ccClosure or owner.kind == skIterator or + owner.typ.callConv == ccNimCall and tfExplicitCallConv notin owner.typ.flags: owner.typ.callConv = ccClosure propagateClosure(owner.skipGenericOwner, s.owner) else: diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index d19584d48..44ff90010 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -326,7 +326,7 @@ proc processDynLib(c: PContext, n: PNode, sym: PSym) = # a calling convention that doesn't introduce custom name mangling # cdecl is the default - the user can override this explicitly if sym.kind in routineKinds and sym.typ != nil and - sym.typ.callConv == ccImplicit: + tfExplicitCallConv notin sym.typ.flags: sym.typ.callConv = ccCDecl proc processNote(c: PContext, n: PNode) = @@ -1061,7 +1061,9 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of FirstCallConv..LastCallConv: assert(sym != nil) if sym.typ == nil: invalidPragma(c, it) - else: sym.typ.callConv = wordToCallConv(k) + else: + sym.typ.callConv = wordToCallConv(k) + sym.typ.flags.incl tfExplicitCallConv of wEmit: pragmaEmit(c, it) of wUnroll: pragmaUnroll(c, it) of wLinearScanEnd, wComputedGoto: noVal(c, it) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 5b1e425d7..b99ddcba3 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -212,7 +212,7 @@ proc considerGenSyms*(c: PContext; n: PNode) = proc newOptionEntry*(conf: ConfigRef): POptionEntry = new(result) result.options = conf.options - result.defaultCC = ccImplicit + result.defaultCC = ccNimCall result.dynlib = nil result.notes = conf.notes result.warningAsErrors = conf.warningAsErrors diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 34b75861a..786cca01f 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -639,11 +639,11 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = return isNone elif f.callConv != a.callConv: # valid to pass a 'nimcall' thingie to 'closure': - if f.callConv == ccClosure and a.callConv in ccDefault: + if f.callConv == ccClosure and a.callConv == ccNimCall: result = if result == isInferred: isInferredConvertible elif result == isBothMetaConvertible: isBothMetaConvertible else: isConvertible - elif not(f.callConv in ccDefault and a.callConv in ccDefault): + else: return isNone when useEffectSystem: if compatibleEffects(f, a) != efCompat: return isNone diff --git a/compiler/transf.nim b/compiler/transf.nim index 6d497c546..3333acbf1 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -539,7 +539,7 @@ proc transformConv(c: PTransf, n: PNode): PNode = # happens sometimes for generated assignments, etc. of tyProc: result = transformSons(c, n) - if dest.callConv == ccClosure and source.callConv in ccDefault: + if dest.callConv == ccClosure and source.callConv == ccNimCall: result = generateThunk(c, result[1], dest) else: result = transformSons(c, n) diff --git a/compiler/types.nim b/compiler/types.nim index 8d94423a8..3545b0703 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -679,7 +679,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = if i < t.len - 1: result.add(", ") result.add(')') if t.len > 0 and t[0] != nil: result.add(": " & typeToString(t[0])) - var prag = if t.callConv == ccImplicit: "" else: CallingConvToStr[t.callConv] + var prag = if t.callConv == ccNimCall and tfExplicitCallConv notin t.flags: "" else: CallingConvToStr[t.callConv] if tfNoSideEffect in t.flags: addSep(prag) prag.add("noSideEffect") diff --git a/tests/closure/tinvalidclosure5.nim b/tests/closure/tinvalidclosure5.nim new file mode 100644 index 000000000..d03d93867 --- /dev/null +++ b/tests/closure/tinvalidclosure5.nim @@ -0,0 +1,10 @@ +discard """ + errormsg: "type mismatch: got but expected 'A = proc (){.nimcall.}'" + line: 9 +""" + +type A = proc() {.nimcall.} +proc main = + let b = 1 + let a: A = proc() = echo b + From 2260ede247c50b88677e9f80530469d4d7d0c43a Mon Sep 17 00:00:00 2001 From: Ico Doornekamp Date: Mon, 10 Aug 2020 14:01:47 +0200 Subject: [PATCH 0530/3768] Added array type definition to manual (#15173) Co-authored-by: Ico Doornekamp --- doc/manual.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/manual.rst b/doc/manual.rst index c5608da84..4d26421fe 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -1301,6 +1301,9 @@ array constructor ``[]``. The element type of this array expression is inferred from the type of the first element. All other elements need to be implicitly convertible to this type. +An array type can be defined using the `array[size, T]` syntax, or using +`array[lo..hi, T]` for arrays that start at an index other than zero. + Sequences are similar to arrays but of dynamic length which may change during runtime (like strings). Sequences are implemented as growable arrays, allocating pieces of memory as items are added. A sequence ``S`` is always From 963db9e7dac39f4238f013addfae36439318b67f Mon Sep 17 00:00:00 2001 From: Silvio Date: Tue, 11 Aug 2020 14:49:57 +0200 Subject: [PATCH 0531/3768] docs: dlimport -> dynlib (#15175) --- lib/pure/dynlib.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/dynlib.nim b/lib/pure/dynlib.nim index f31ae94dd..42d13535f 100644 --- a/lib/pure/dynlib.nim +++ b/lib/pure/dynlib.nim @@ -97,7 +97,7 @@ proc libCandidates*(s: string, dest: var seq[string]) = add(dest, s) proc loadLibPattern*(pattern: string, globalSymbols = false): LibHandle = - ## loads a library with name matching `pattern`, similar to what `dlimport` + ## loads a library with name matching `pattern`, similar to what `dynlib` ## pragma does. Returns nil if the library could not be loaded. ## Warning: this proc uses the GC and so cannot be used to load the GC. var candidates = newSeq[string]() From 2cea52ee7e7d0f118976df5311a84540cbea85cd Mon Sep 17 00:00:00 2001 From: Clyybber Date: Tue, 11 Aug 2020 22:28:49 +0200 Subject: [PATCH 0532/3768] Allow pragmas on parameters (#15178) --- compiler/semtypes.nim | 4 ++-- tests/template/tparams_gensymed.nim | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index c2aeadc21..6a1c1afc9 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1234,7 +1234,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, if typ == nil: typ = def.typ if isEmptyContainer(typ): - localError(c.config, a.info, "cannot infer the type of parameter '" & a[0].ident.s & "'") + localError(c.config, a.info, "cannot infer the type of parameter '" & $a[0] & "'") if typ.kind == tyTypeDesc: # consider a proc such as: @@ -1265,7 +1265,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, continue for j in 0.. Date: Tue, 11 Aug 2020 23:46:16 -0300 Subject: [PATCH 0533/3768] Fix typo on CoroutineRef* doc (#15179) Just a small fix for a typo ("for for"). --- lib/pure/coro.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/coro.nim b/lib/pure/coro.nim index ead3849c6..6e49b6323 100644 --- a/lib/pure/coro.nim +++ b/lib/pure/coro.nim @@ -158,7 +158,7 @@ type ## CoroutineRef holds a pointer to actual coroutine object. Public API always returns ## CoroutineRef instead of CoroutinePtr in order to allow holding a reference to coroutine ## object while it can be safely deallocated by coroutine scheduler loop. In this case - ## Coroutine.reference.coro is set to nil. Public API checks for for it being nil and + ## Coroutine.reference.coro is set to nil. Public API checks for it being nil and ## gracefully fails if it is nil. coro: CoroutinePtr From 957bf15a08d5443a50452909743747d19b5f29f8 Mon Sep 17 00:00:00 2001 From: alaviss Date: Wed, 12 Aug 2020 06:04:54 +0000 Subject: [PATCH 0534/3768] asyncnet: don't try to close the socket again [backport] (#15174) The closed flag isn't a good design by any means, but let's have this working first before I get rid of the flag and potentially create a non-backportable commit. --- lib/pure/asyncnet.nim | 37 ++++++++++++++++++++----------------- 1 file changed, 20 insertions(+), 17 deletions(-) diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index 9846f8999..455efe1c1 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -713,8 +713,12 @@ elif defined(nimdoc): proc close*(socket: AsyncSocket) = ## Closes the socket. + if socket.closed: return + defer: socket.fd.AsyncFD.closeSocket() + socket.closed = true # TODO: Add extra debugging checks for this. + when defineSsl: if socket.isSsl: let res = @@ -731,7 +735,6 @@ proc close*(socket: AsyncSocket) = discard elif res != 1: raiseSSLError() - socket.closed = true # TODO: Add extra debugging checks for this. when defineSsl: proc wrapSocket*(ctx: SslContext, socket: AsyncSocket) = @@ -819,9 +822,9 @@ proc sendTo*(socket: AsyncSocket, address: string, port: Port, data: string, ## address or a hostname. If a hostname is specified this function will try ## each IP of that hostname. The returned future will complete once all data ## has been sent. - ## + ## ## If an error occurs an OSError exception will be raised. - ## + ## ## This proc is normally used with connectionless sockets (UDP sockets). assert(socket.protocol != IPPROTO_TCP, "Cannot `sendTo` on a TCP socket. Use `send` instead") @@ -834,22 +837,22 @@ proc sendTo*(socket: AsyncSocket, address: string, port: Port, data: string, it = aiList success = false lastException: ref Exception - + while it != nil: let fut = sendTo(socket.fd.AsyncFD, cstring(data), len(data), it.ai_addr, it.ai_addrlen.SockLen, flags) - + yield fut if not fut.failed: success = true break - + lastException = fut.readError() it = it.ai_next - + freeaddrinfo(aiList) if not success: @@ -869,24 +872,24 @@ proc recvFrom*(socket: AsyncSocket, data: FutureVar[string], size: int, ## packet received. ## ## If an error occurs an OSError exception will be raised. - ## + ## ## This proc is normally used with connectionless sockets (UDP sockets). - ## + ## ## **Notes** ## * ``data`` must be initialized to the length of ``size``. ## * ``address`` must be initialized to 46 in length. template adaptRecvFromToDomain(domain: Domain) = var lAddr = sizeof(sAddr).SockLen - + result = await recvFromInto(AsyncFD(getFd(socket)), cstring(data.mget()), size, cast[ptr SockAddr](addr sAddr), addr lAddr, flags) - + data.mget().setLen(result) data.complete() getAddrString(cast[ptr SockAddr](addr sAddr), address.mget()) - + address.complete() when domain == AF_INET6: @@ -901,7 +904,7 @@ proc recvFrom*(socket: AsyncSocket, data: FutureVar[string], size: int, "`date` was not initialized correctly. `size` != `len(data.mget())`") assert(46 == len(address.mget()), "`address` was not initialized correctly. 46 != `len(address.mget())`") - + case socket.domain of AF_INET6: var sAddr: Sockaddr_in6 @@ -920,18 +923,18 @@ proc recvFrom*(socket: AsyncSocket, size: int, ## ``size``. Returned future will complete once one datagram has been received ## and will return tuple with: data of packet received; and address and port ## of datagram's sender. - ## + ## ## If an error occurs an OSError exception will be raised. - ## + ## ## This proc is normally used with connectionless sockets (UDP sockets). var data = newFutureVar[string]() address = newFutureVar[string]() port = newFutureVar[Port]() - + data.mget().setLen(size) address.mget().setLen(46) - + let read = await recvFrom(socket, data, size, address, port, flags) result = (data.mget(), address.mget(), port.mget()) From ba042af9cc6194cd3c3e67e5daf0165b4b3630c0 Mon Sep 17 00:00:00 2001 From: slangmgh <37659406+slangmgh@users.noreply.github.com> Date: Sat, 15 Aug 2020 07:33:21 +0800 Subject: [PATCH 0535/3768] std/with support field assign (#14484) * std/with support filed assign * add changelog * add support x.dup.with * add example * revert support x.dup.with; add example * update changelog; fix assignment in parameter * Update changelog.md * add example for assignment in parameter * Remove colon style assign Co-authored-by: Clyybber --- changelog.md | 15 +++++++++++++++ lib/pure/sugar.nim | 26 ++++++++++++++++++++++++++ lib/std/private/underscored_calls.nim | 23 +++++++++++------------ lib/std/with.nim | 7 +++++++ 4 files changed, 59 insertions(+), 12 deletions(-) diff --git a/changelog.md b/changelog.md index f6f1f94cd..eebccb62f 100644 --- a/changelog.md +++ b/changelog.md @@ -148,6 +148,21 @@ - Added `deques.toDeque`, which creates a deque from an openArray. The usage is similar to procs such as `sets.toHashSet` and `tables.toTable`. Previously, it was necessary to create an empty deque and add items manually. + +- `std/with`, `sugar.dup` now support object field assignment expression: + ```nim + import std/with + + type Foo = object + x, y: int + + var foo = Foo() + with foo: + x = 10 + y = 20 + + echo foo + ``` ## Language changes - The `=destroy` hook no longer has to reset its target, as the compiler now automatically inserts diff --git a/lib/pure/sugar.nim b/lib/pure/sugar.nim index 00f4de5d8..0edcc5697 100644 --- a/lib/pure/sugar.nim +++ b/lib/pure/sugar.nim @@ -337,6 +337,32 @@ macro collect*(init, body: untyped): untyped {.since: (1, 1).} = when isMainModule: since (1, 1): + block dup_with_field: + type + Foo = object + col, pos: int + name: string + + proc inc_col(foo: var Foo) = inc(foo.col) + proc inc_pos(foo: var Foo) = inc(foo.pos) + proc name_append(foo: var Foo, s: string) = foo.name &= s + + let a = Foo(col: 1, pos: 2, name: "foo") + block: + let b = a.dup(inc_col, inc_pos): + _.pos = 3 + name_append("bar") + inc_pos + + doAssert(b == Foo(col: 2, pos: 4, name: "foobar")) + + block: + let b = a.dup(inc_col, pos = 3, name = "bar"): + name_append("bar") + inc_pos + + doAssert(b == Foo(col: 2, pos: 4, name: "barbar")) + import algorithm var a = @[1, 2, 3, 4, 5, 6, 7, 8, 9] diff --git a/lib/std/private/underscored_calls.nim b/lib/std/private/underscored_calls.nim index c7aeb6ae2..f0bcbcc74 100644 --- a/lib/std/private/underscored_calls.nim +++ b/lib/std/private/underscored_calls.nim @@ -26,6 +26,12 @@ proc underscoredCall(n, arg0: NimNode): NimNode = for i in 1..u-1: result.add n[i] result.add arg0 for i in u+1..n.len-1: result.add n[i] + elif n.kind in {nnkAsgn, nnkExprEqExpr}: + var field = n[0] + if n[0].kind == nnkDotExpr and n[0][0].eqIdent("_"): + # handle _.field = ... + field = n[0][1] + result = newDotExpr(arg0, field).newAssignment n[1] else: # handle e.g. 'x.dup(sort)' result = newNimNode(nnkCall, n) @@ -33,17 +39,10 @@ proc underscoredCall(n, arg0: NimNode): NimNode = result.add arg0 proc underscoredCalls*(result, calls, arg0: NimNode) = - proc handleStmtList(result, n, arg0: NimNode) = - for a in n: - if a.kind in {nnkStmtList, nnkStmtListExpr}: - handleStmtList(result, a, arg0) - else: - result.add underscoredCall(a, arg0) + expectKind calls, {nnkArgList, nnkStmtList, nnkStmtListExpr} - expectKind calls, nnkArgList - if calls.len == 1 and calls[0].kind in {nnkStmtList, nnkStmtListExpr}: - # the 'macro: body' syntax is used: - handleStmtList(result, calls[0], arg0) - else: - for call in calls: + for call in calls: + if call.kind in {nnkStmtList, nnkStmtListExpr}: + underscoredCalls(result, call, arg0) + else: result.add underscoredCall(call, arg0) diff --git a/lib/std/with.nim b/lib/std/with.nim index c1ac96fcb..ea26065a1 100644 --- a/lib/std/with.nim +++ b/lib/std/with.nim @@ -41,6 +41,7 @@ when isMainModule: type Foo = object col, pos: string + name: string proc setColor(f: var Foo; r, g, b: int) = f.col = $(r, g, b) proc setPosition(f: var Foo; x, y: float) = f.pos = $(x, y) @@ -49,3 +50,9 @@ when isMainModule: with(f, setColor(2, 3, 4), setPosition(0.0, 1.0)) echo f + f = Foo() + with f: + col = $(2, 3, 4) + pos = $(0.0, 1.0) + _.name = "bar" + echo f From c9fdad2c21a6394daec70e9d0e215176b69783ac Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Mon, 17 Aug 2020 03:39:58 -0300 Subject: [PATCH 0536/3768] db_postgres document how to use it with unix socket (#15187) --- lib/impure/db_postgres.nim | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/lib/impure/db_postgres.nim b/lib/impure/db_postgres.nim index 9e8075450..f9f584663 100644 --- a/lib/impure/db_postgres.nim +++ b/lib/impure/db_postgres.nim @@ -37,6 +37,26 @@ ## VALUES ($1, $2, $3)""", ## 3) ## +## +## Unix Socket +## =========== +## +## Using Unix sockets instead of TCP connection can +## `improve performance up to 30% ~ 175% for some operations `_. +## +## To use Unix sockets with `db_postgres`, change the server address to the socket file path: +## +## .. code-block:: Nim +## import db_postgres ## Change "localhost" or "127.0.0.1" to the socket file path +## let db = db_postgres.open("/run/postgresql", "user", "password", "database") +## echo db.getAllRows(sql"SELECT version();") +## db.close() +## +## The socket file path is operating system specific and distribution specific, +## additional configuration may or may not be needed on your `postgresql.conf`. +## The Postgres server must be on the same computer and only works for Unix-like operating systems. +## +## ## Examples ## ======== ## @@ -491,7 +511,7 @@ proc tryInsert*(db: DbConn, query: SqlQuery,pkName: string, args: varargs[string, `$`]): int64 {.tags: [WriteDbEffect], since: (1, 3).}= ## executes the query (typically "INSERT") and returns the - ## generated ID for the row or -1 in case of an error. + ## generated ID for the row or -1 in case of an error. var x = pqgetvalue(setupQuery(db, SqlQuery(string(query) & " RETURNING " & pkName), args), 0, 0) if not isNil(x): @@ -503,7 +523,7 @@ proc insert*(db: DbConn, query: SqlQuery, pkName: string, args: varargs[string, `$`]): int64 {.tags: [WriteDbEffect], since: (1, 3).} = ## executes the query (typically "INSERT") and returns the - ## generated ID + ## generated ID result = tryInsertID(db, query, args) if result < 0: dbError(db) From 39906c52c4fd075b813bc1e0f65a9e25a8e9e95a Mon Sep 17 00:00:00 2001 From: narimiran Date: Mon, 17 Aug 2020 09:11:33 +0200 Subject: [PATCH 0537/3768] stale bot is now active only for pull requests --- .github/stale.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/stale.yml b/.github/stale.yml index e314fa55e..7825ad58d 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -1,7 +1,7 @@ # Configuration for probot-stale - https://github.com/probot/stale # Number of days of inactivity before an Issue or Pull Request becomes stale -daysUntilStale: 1095 +daysUntilStale: 730 # Number of days of inactivity before an Issue or Pull Request with the stale label is closed. # Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. @@ -37,9 +37,9 @@ staleLabel: stale # Comment to post when marking as stale. Set to `false` to disable markComment: > - This issue has been automatically marked as stale because it has not had + This pull request has been automatically marked as stale because it has not had recent activity. - If you think it is still a valid issue, write a comment below; + If you think it is still a valid PR, please rebase it on the latest devel; otherwise it will be closed. Thank you for your contributions. # Comment to post when removing the stale label. @@ -54,7 +54,7 @@ markComment: > limitPerRun: 20 # Limit to only `issues` or `pulls` -# only: issues +only: pulls # Optionally, specify configuration settings that are specific to just 'issues' or 'pulls': # pulls: From cd28fe2ef7a204721efa72082c9f65f8d13bcfb3 Mon Sep 17 00:00:00 2001 From: Bung Date: Mon, 17 Aug 2020 16:54:34 +0800 Subject: [PATCH 0538/3768] add openssl missing procs (#15180) --- lib/wrappers/openssl.nim | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim index bea96127d..a37c5b1be 100644 --- a/lib/wrappers/openssl.nim +++ b/lib/wrappers/openssl.nim @@ -691,6 +691,7 @@ proc EVP_sha512*(): EVP_MD {.cdecl, importc.} proc EVP_mdc2*(): EVP_MD {.cdecl, importc.} proc EVP_ripemd160*(): EVP_MD {.cdecl, importc.} proc EVP_whirlpool*(): EVP_MD {.cdecl, importc.} +proc EVP_MD_size*(md: EVP_MD): cint {.cdecl, importc.} # hmac functions proc HMAC*(evp_md: EVP_MD; key: pointer; key_len: cint; d: cstring; n: csize_t; md: cstring; md_len: ptr cuint): cstring {.cdecl, importc.} @@ -699,7 +700,9 @@ proc HMAC*(evp_md: EVP_MD; key: pointer; key_len: cint; d: cstring; n: csize_t; proc PEM_read_bio_PrivateKey*(bp: BIO, x: ptr EVP_PKEY, cb: pointer, u: pointer): EVP_PKEY {.cdecl, importc.} proc EVP_PKEY_free*(p: EVP_PKEY) {.cdecl, importc.} proc EVP_DigestSignInit*(ctx: EVP_MD_CTX, pctx: ptr EVP_PKEY_CTX, typ: EVP_MD, e: ENGINE, pkey: EVP_PKEY): cint {.cdecl, importc.} +proc EVP_DigestInit_ex*(ctx: EVP_MD_CTX, typ: PEVP_MD, engine: SslPtr = nil): cint {.cdecl, importc.} proc EVP_DigestUpdate*(ctx: EVP_MD_CTX, data: pointer, len: cuint): cint {.cdecl, importc.} +proc EVP_DigestFinal_ex*(ctx: EVP_MD_CTX, buffer: pointer, size: ptr cuint): cint {.cdecl, importc.} proc EVP_DigestSignFinal*(ctx: EVP_MD_CTX, data: pointer, len: ptr csize_t): cint {.cdecl, importc.} proc EVP_PKEY_CTX_new*(pkey: EVP_PKEY, e: ENGINE): EVP_PKEY_CTX {.cdecl, importc.} proc EVP_PKEY_CTX_free*(pkeyCtx: EVP_PKEY_CTX) {.cdecl, importc.} @@ -708,10 +711,12 @@ proc EVP_PKEY_sign_init*(c: EVP_PKEY_CTX): cint {.cdecl, importc.} when defined(macosx) or defined(windows): proc EVP_MD_CTX_create*(): EVP_MD_CTX {.cdecl, importc.} proc EVP_MD_CTX_destroy*(ctx: EVP_MD_CTX) {.cdecl, importc.} + proc EVP_MD_CTX_cleanup*(ctx: EVP_MD_CTX): cint {.cdecl, importc.} else: # some times you will need this instead: proc EVP_MD_CTX_create*(): EVP_MD_CTX {.cdecl, importc: "EVP_MD_CTX_new".} proc EVP_MD_CTX_destroy*(ctx: EVP_MD_CTX) {.cdecl, importc: "EVP_MD_CTX_free".} + proc EVP_MD_CTX_cleanup*(ctx: EVP_MD_CTX): cint {.cdecl, importc: "EVP_MD_CTX_cleanup".} # type From 6feb3900b121a356a1c091855008d080223ea802 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 17 Aug 2020 14:32:52 +0200 Subject: [PATCH 0539/3768] fixes #15177, the error message is now what it should have been (#15195) --- lib/pure/net.nim | 2 +- lib/pure/smtp.nim | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index c0bfc39cc..ea1c86c31 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -116,7 +116,7 @@ when defineSsl: else: type - SslContext* = void # TODO: Workaround #4797. + SslContext* = ref object # TODO: Workaround #4797. const BufferSize*: int = 4000 ## size of a buffered socket's buffer diff --git a/lib/pure/smtp.nim b/lib/pure/smtp.nim index b42664029..5eafc6680 100644 --- a/lib/pure/smtp.nim +++ b/lib/pure/smtp.nim @@ -87,7 +87,7 @@ proc debugSend*(smtp: Smtp | AsyncSmtp, cmd: string) {.multisync.} = await smtp.sock.send(cmd) proc debugRecv*(smtp: Smtp | AsyncSmtp): Future[TaintedString] {.multisync.} = - ## Receives a line of data from the socket connected to the + ## Receives a line of data from the socket connected to the ## SMTP server. ## ## If the ``smtp`` object was created with ``debug`` enabled, @@ -112,8 +112,7 @@ proc quitExcpt(smtp: Smtp, msg: string) = const compiledWithSsl = defined(ssl) when not defined(ssl): - type PSSLContext = ref object - let defaultSSLContext: PSSLContext = nil + let defaultSSLContext: SSLContext = nil else: var defaultSSLContext {.threadvar.}: SSLContext From e9df8ebcfd475a715756d24e1718a5c1455689d2 Mon Sep 17 00:00:00 2001 From: alaviss Date: Mon, 17 Aug 2020 18:20:48 +0000 Subject: [PATCH 0540/3768] gc_regions: cleanup & fixes for deallocation (#11920) * gc_regions: withRegion nows return the modified MemRegion * gc_regions: make withScratchRegion dealloc correctly * tests/gc: add tregionleak test This test checks if memory within regions are freed properly. --- lib/system/gc_regions.nim | 9 ++++----- tests/gc/tregionleak.nim | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+), 5 deletions(-) create mode 100644 tests/gc/tregionleak.nim diff --git a/lib/system/gc_regions.nim b/lib/system/gc_regions.nim index 4f802c812..9425e040a 100644 --- a/lib/system/gc_regions.nim +++ b/lib/system/gc_regions.nim @@ -87,13 +87,13 @@ var tlRegion {.threadVar.}: MemRegion # tempStrRegion {.threadVar.}: MemRegion # not yet used -template withRegion*(r: MemRegion; body: untyped) = +template withRegion*(r: var MemRegion; body: untyped) = let oldRegion = tlRegion tlRegion = r try: body finally: - #r = tlRegion + r = tlRegion tlRegion = oldRegion template inc(p: pointer, s: int) = @@ -262,14 +262,13 @@ when false: setObstackPtr(obs) template withScratchRegion*(body: untyped) = - var scratch: MemRegion let oldRegion = tlRegion - tlRegion = scratch + tlRegion = MemRegion() try: body finally: + deallocAll() tlRegion = oldRegion - deallocAll(scratch) when false: proc joinRegion*(dest: var MemRegion; src: MemRegion) = diff --git a/tests/gc/tregionleak.nim b/tests/gc/tregionleak.nim new file mode 100644 index 000000000..277cfc987 --- /dev/null +++ b/tests/gc/tregionleak.nim @@ -0,0 +1,23 @@ +discard """ + cmd: '''nim c --gc:regions $file''' + output: ''' +finalized +finalized +''' +""" + +proc finish(o: RootRef) = + echo "finalized" + +withScratchRegion: + var test: RootRef + new(test, finish) + +var + mr: MemRegion + test: RootRef + +withRegion(mr): + new(test, finish) + +deallocAll(mr) From e194cb41a4502d3d8b5e4426fbd72e8d80513c3b Mon Sep 17 00:00:00 2001 From: jcosborn Date: Tue, 18 Aug 2020 01:23:19 -0500 Subject: [PATCH 0541/3768] fix overloading issue with generic invocation (#15135) * fix overloading issue with generic alias * add test for inheritance depth --- compiler/sigmatch.nim | 15 +------ tests/overload/toverload_various.nim | 60 ++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 14 deletions(-) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 786cca01f..1d648c32a 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -579,7 +579,7 @@ proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = if f.isMetaType: # We are matching a generic proc (as proc param) # to another generic type appearing in the proc - # signature. There is a change that the target + # signature. There is a chance that the target # type is already fully-determined, so we are # going to try resolve it if c.call != nil: @@ -1496,7 +1496,6 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, #echo "inferred ", typeToString(inst), " for ", f return typeRel(c, inst, a) - var depth = 0 if x.kind == tyGenericInvocation or f[0].kind != tyGenericBody: #InternalError("typeRel: tyGenericInvocation -> tyGenericInvocation") # simply no match for now: @@ -1510,18 +1509,6 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, # Workaround for regression #4589 if f[i].kind != tyTypeDesc: return result = isGeneric - elif x.kind == tyGenericInst and isGenericSubtype(c, x, f, depth, f) and - (x.len - 1 == f.len): - # do not recurse here in order to not K bind twice for this code: - # - # type - # BaseFruit[T] = object of RootObj - # Banana[T] = object of BaseFruit[uint32] # Concrete type here, not T! - # proc setColor[K](self: var BaseFruit[K]) - # var x: Banana[float64] - # x.setColor() - c.inheritancePenalty += depth - result = isGeneric else: let genericBody = f[0] var askip = skippedNone diff --git a/tests/overload/toverload_various.nim b/tests/overload/toverload_various.nim index 96cb9a736..e8a0108e3 100644 --- a/tests/overload/toverload_various.nim +++ b/tests/overload/toverload_various.nim @@ -375,3 +375,63 @@ block: test(c) var d: FooBarBazX test(d) + + +# overloading on tuples with generic alias +block: + type + Foo[F,T] = object + exArgs: T + FooUn[F,T] = Foo[F,tuple[a:T]] + FooBi[F,T1,T2] = Foo[F,tuple[a:T1,b:T2]] + + proc foo1[F,T](x: Foo[F,tuple[a:T]]): int = 1 + proc foo1[F,T1,T2](x: Foo[F,tuple[a:T1,b:T2]]): int = 2 + proc foo2[F,T](x: FooUn[F,T]): int = 1 + proc foo2[F,T1,T2](x: FooBi[F,T1,T2]):int = 2 + + template bar1[F,T](x: Foo[F,tuple[a:T]]): int = 1 + template bar1[F,T1,T2](x: Foo[F,tuple[a:T1,b:T2]]): int = 2 + template bar2[F,T](x: FooUn[F,T]): int = 1 + template bar2[F,T1,T2](x: FooBi[F,T1,T2]): int = 2 + + proc test(x: any, n: int) = + doAssert(foo1(x) == n) + doAssert(foo2(x) == n) + doAssert(bar1(x) == n) + doAssert(bar2(x) == n) + + var a: Foo[int, tuple[a:int]] + test(a, 1) + var b: FooUn[int, int] + test(b, 1) + var c: Foo[int, tuple[a:int,b:int]] + test(c, 2) + var d: FooBi[int, int, int] + test(d, 2) + + +# inheritance depth +block: + type + Foo[T] = object of RootObj + x: T + Bar[T] = object of Foo[T] + y: T + Baz[T] = object of Bar[T] + z: T + + template t0[T](x: Foo[T]): int = 0 + template t0[T](x: Bar[T]): int = 1 + proc p0[T](x: Foo[T]): int = 0 + proc p0[T](x: Bar[T]): int = 1 + + var a: Foo[int] + var b: Bar[int] + var c: Baz[int] + doAssert(t0(a) == 0) + doAssert(t0(b) == 1) + doAssert(t0(c) == 1) + doAssert(p0(a) == 0) + doAssert(p0(b) == 1) + doAssert(p0(c) == 1) From ddff13f01b4704dbe22d8b6c86a606675a1700b0 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 18 Aug 2020 12:45:22 +0200 Subject: [PATCH 0542/3768] better strict funcs, WIP (#15199) * better strict funcs, WIP * progress --- compiler/types.nim | 2 +- compiler/varpartitions.nim | 26 +++++++++++++++++++++----- lib/impure/nre.nim | 6 ++++-- lib/pure/mimetypes.nim | 3 ++- lib/pure/strtabs.nim | 13 ++++++++----- 5 files changed, 36 insertions(+), 14 deletions(-) diff --git a/compiler/types.nim b/compiler/types.nim index 3545b0703..b8452c3ea 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -543,7 +543,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = else: result = "" of tyBuiltInTypeClass: - result = case t.base.kind: + result = case t.base.kind of tyVar: "var" of tyRef: "ref" of tyPtr: "ptr" diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim index 6857e1b57..fd6751cdf 100644 --- a/compiler/varpartitions.nim +++ b/compiler/varpartitions.nim @@ -14,7 +14,8 @@ ## The used data structure is "union find" with path compression. import ast, types, lineinfos, options, msgs, renderer -from trees import getMagic +from trees import getMagic, whichPragma +from wordrecg import wNoSideEffect from isolation_check import canAlias type @@ -48,7 +49,7 @@ type s: seq[VarIndex] graphs: seq[MutationInfo] unanalysableMutation, performCursorInference: bool - inAsgnSource, inConstructor: int + inAsgnSource, inConstructor, inNoSideEffectSection: int proc `$`*(config: ConfigRef; g: MutationInfo): string = result = "" @@ -336,12 +337,15 @@ proc deps(c: var Partitions; dest, src: PNode) = var targets, sources: seq[PSym] allRoots(dest, targets) allRoots(src, sources) + + proc wrap(t: PType): bool {.nimcall.} = t.kind in {tyRef, tyPtr} + let destIsComplex = types.searchTypeFor(dest.typ, wrap) + for t in targets: if dest.kind != nkSym: potentialMutation(c, t, dest.info) - proc wrap(t: PType): bool {.nimcall.} = t.kind in {tyRef, tyPtr} - if types.searchTypeFor(t.typ, wrap): + if destIsComplex: for s in sources: connect(c, t, s, dest.info) @@ -399,7 +403,8 @@ proc traverse(c: var Partitions; n: PNode) = var roots: seq[PSym] allRoots(it, roots) if paramType.kind == tyVar: - for r in roots: potentialMutation(c, r, it.info) + if c.inNoSideEffectSection == 0: + for r in roots: potentialMutation(c, r, it.info) else: for r in roots: noCursor(c, r) @@ -434,6 +439,17 @@ proc traverse(c: var Partitions; n: PNode) = # test arc/topt_no_cursor.nim noCursor(c, it.sym) + of nkPragmaBlock: + let pragmaList = n[0] + var enforceNoSideEffects = 0 + for i in 0.. 0, "ext argument can not be empty string" assert mimetype.strip.len > 0, "mimetype argument can not be empty string" - mimedb.mimes[ext.toLowerAscii()] = mimetype.toLowerAscii() + {.noSideEffect.}: + mimedb.mimes[ext.toLowerAscii()] = mimetype.toLowerAscii() runnableExamples: static: diff --git a/lib/pure/strtabs.nim b/lib/pure/strtabs.nim index 676e8eb1d..4c2dd1451 100644 --- a/lib/pure/strtabs.nim +++ b/lib/pure/strtabs.nim @@ -252,7 +252,7 @@ proc `[]=`*(t: StringTableRef, key, val: string) {. inc(t.counter) proc newStringTable*(mode: StringTableMode): owned(StringTableRef) {. - rtlFunc, extern: "nst$1".} = + rtlFunc, extern: "nst$1", noSideEffect.} = ## Creates a new empty string table. ## ## See also: @@ -265,7 +265,7 @@ proc newStringTable*(mode: StringTableMode): owned(StringTableRef) {. proc newStringTable*(keyValuePairs: varargs[string], mode: StringTableMode): owned(StringTableRef) {. - rtlFunc, extern: "nst$1WithPairs".} = + rtlFunc, extern: "nst$1WithPairs", noSideEffect.} = ## Creates a new string table with given `key, value` string pairs. ## ## `StringTableMode` must be specified. @@ -276,12 +276,13 @@ proc newStringTable*(keyValuePairs: varargs[string], result = newStringTable(mode) var i = 0 while i < high(keyValuePairs): - result[keyValuePairs[i]] = keyValuePairs[i + 1] + {.noSideEffect.}: + result[keyValuePairs[i]] = keyValuePairs[i + 1] inc(i, 2) proc newStringTable*(keyValuePairs: varargs[tuple[key, val: string]], mode: StringTableMode = modeCaseSensitive): owned(StringTableRef) {. - rtlFunc, extern: "nst$1WithTableConstr".} = + rtlFunc, extern: "nst$1WithTableConstr", noSideEffect.} = ## Creates a new string table with given `(key, value)` tuple pairs. ## ## The default mode is case sensitive. @@ -291,7 +292,9 @@ proc newStringTable*(keyValuePairs: varargs[tuple[key, val: string]], mytab2 = newStringTable([("key3", "val3"), ("key4", "val4")]) result = newStringTable(mode) - for key, val in items(keyValuePairs): result[key] = val + for key, val in items(keyValuePairs): + {.noSideEffect.}: + result[key] = val proc raiseFormatException(s: string) = raise newException(ValueError, "format string: key not found: " & s) From 8a004e2fc07c87b8308c3c03e4448372a2094383 Mon Sep 17 00:00:00 2001 From: Elijah Shaw-Rutschman Date: Tue, 18 Aug 2020 11:02:10 -0500 Subject: [PATCH 0543/3768] Add test coverage for atomics (#15193) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add test coverage for atomics Signed-off-by: Elijah Shaw-Rutschman * Fix compareExchange bugs for non-trivial objects Bugs fixed: 1. compareExchange would not set the desired value in the success case. 2. compareExchange would not set var expected to the found value in the failure case. 3. withLock would spin forever running the unit tests. try..body..finally prevents this. Not sure why this makes a difference, since an exception wasn’t being raised, but clearing the guard in a finally block seems correct anyways. Signed-off-by: Elijah Shaw-Rutschman --- lib/pure/concurrency/atomics.nim | 14 +- tests/stdlib/concurrency/tatomics.nim | 609 ++++++++++++++++++++++++++ 2 files changed, 616 insertions(+), 7 deletions(-) create mode 100644 tests/stdlib/concurrency/tatomics.nim diff --git a/lib/pure/concurrency/atomics.nim b/lib/pure/concurrency/atomics.nim index 29491f68c..eaa811aa6 100644 --- a/lib/pure/concurrency/atomics.nim +++ b/lib/pure/concurrency/atomics.nim @@ -326,8 +326,10 @@ else: template withLock[T: not Trivial](location: var Atomic[T]; order: MemoryOrder; body: untyped): untyped = while location.guard.testAndSet(moAcquire): discard - body - location.guard.clear(moRelease) + try: + body + finally: + location.guard.clear(moRelease) proc load*[T: not Trivial](location: var Atomic[T]; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} = withLock(location, order): @@ -345,16 +347,14 @@ else: proc compareExchange*[T: not Trivial](location: var Atomic[T]; expected: var T; desired: T; success, failure: MemoryOrder): bool {.inline.} = withLock(location, success): if location.nonAtomicValue != expected: + expected = location.nonAtomicValue return false + expected = desired swap(location.nonAtomicValue, expected) return true proc compareExchangeWeak*[T: not Trivial](location: var Atomic[T]; expected: var T; desired: T; success, failure: MemoryOrder): bool {.inline.} = - withLock(location, success): - if location.nonAtomicValue != expected: - return false - swap(location.nonAtomicValue, expected) - return true + compareExchange(location, expected, desired, success, failure) proc compareExchange*[T: not Trivial](location: var Atomic[T]; expected: var T; desired: T; order: MemoryOrder = moSequentiallyConsistent): bool {.inline.} = compareExchange(location, expected, desired, order, order) diff --git a/tests/stdlib/concurrency/tatomics.nim b/tests/stdlib/concurrency/tatomics.nim new file mode 100644 index 000000000..260d00990 --- /dev/null +++ b/tests/stdlib/concurrency/tatomics.nim @@ -0,0 +1,609 @@ +# test atomic operations + +import atomics, bitops + +type + Object = object + val: int + + +# Atomic operations for trivial objects + + +block trivialLoad: + var location: Atomic[int] + location.store(1) + doAssert location.load == 1 + location.store(2) + doAssert location.load(moRelaxed) == 2 + location.store(3) + doAssert location.load(moAcquire) == 3 + + +block trivialStore: + var location: Atomic[int] + location.store(1) + doAssert location.load == 1 + location.store(2, moRelaxed) + doAssert location.load == 2 + location.store(3, moRelease) + doAssert location.load == 3 + + +block trivialExchange: + var location: Atomic[int] + location.store(1) + doAssert location.exchange(2) == 1 + doAssert location.exchange(3, moRelaxed) == 2 + doAssert location.exchange(4, moAcquire) == 3 + doAssert location.exchange(5, moRelease) == 4 + doAssert location.exchange(6, moAcquireRelease) == 5 + doAssert location.load == 6 + + +block trivialCompareExchangeDoesExchange: + var location: Atomic[int] + var expected = 1 + location.store(1) + doAssert location.compareExchange(expected, 2) + doAssert expected == 1 + doAssert location.load == 2 + expected = 2 + doAssert location.compareExchange(expected, 3, moRelaxed) + doAssert expected == 2 + doAssert location.load == 3 + expected = 3 + doAssert location.compareExchange(expected, 4, moAcquire) + doAssert expected == 3 + doAssert location.load == 4 + expected = 4 + doAssert location.compareExchange(expected, 5, moRelease) + doAssert expected == 4 + doAssert location.load == 5 + expected = 5 + doAssert location.compareExchange(expected, 6, moAcquireRelease) + doAssert expected == 5 + doAssert location.load == 6 + + +block trivialCompareExchangeDoesNotExchange: + var location: Atomic[int] + var expected = 10 + location.store(1) + doAssert not location.compareExchange(expected, 2) + doAssert expected == 1 + doAssert location.load == 1 + expected = 10 + doAssert not location.compareExchange(expected, 3, moRelaxed) + doAssert expected == 1 + doAssert location.load == 1 + expected = 10 + doAssert not location.compareExchange(expected, 4, moAcquire) + doAssert expected == 1 + doAssert location.load == 1 + expected = 10 + doAssert not location.compareExchange(expected, 5, moRelease) + doAssert expected == 1 + doAssert location.load == 1 + expected = 10 + doAssert not location.compareExchange(expected, 6, moAcquireRelease) + doAssert expected == 1 + doAssert location.load == 1 + + +block trivialCompareExchangeSuccessFailureDoesExchange: + var location: Atomic[int] + var expected = 1 + location.store(1) + doAssert location.compareExchange(expected, 2, moSequentiallyConsistent, moSequentiallyConsistent) + doAssert expected == 1 + doAssert location.load == 2 + expected = 2 + doAssert location.compareExchange(expected, 3, moRelaxed, moRelaxed) + doAssert expected == 2 + doAssert location.load == 3 + expected = 3 + doAssert location.compareExchange(expected, 4, moAcquire, moAcquire) + doAssert expected == 3 + doAssert location.load == 4 + expected = 4 + doAssert location.compareExchange(expected, 5, moRelease, moRelease) + doAssert expected == 4 + doAssert location.load == 5 + expected = 5 + doAssert location.compareExchange(expected, 6, moAcquireRelease, moAcquireRelease) + doAssert expected == 5 + doAssert location.load == 6 + + +block trivialCompareExchangeSuccessFailureDoesNotExchange: + var location: Atomic[int] + var expected = 10 + location.store(1) + doAssert not location.compareExchange(expected, 2, moSequentiallyConsistent, moSequentiallyConsistent) + doAssert expected == 1 + doAssert location.load == 1 + expected = 10 + doAssert not location.compareExchange(expected, 3, moRelaxed, moRelaxed) + doAssert expected == 1 + doAssert location.load == 1 + expected = 10 + doAssert not location.compareExchange(expected, 4, moAcquire, moAcquire) + doAssert expected == 1 + doAssert location.load == 1 + expected = 10 + doAssert not location.compareExchange(expected, 5, moRelease, moRelease) + doAssert expected == 1 + doAssert location.load == 1 + expected = 10 + doAssert not location.compareExchange(expected, 6, moAcquireRelease, moAcquireRelease) + doAssert expected == 1 + doAssert location.load == 1 + + +block trivialCompareExchangeWeakDoesExchange: + var location: Atomic[int] + var expected = 1 + location.store(1) + doAssert location.compareExchangeWeak(expected, 2) + doAssert expected == 1 + doAssert location.load == 2 + expected = 2 + doAssert location.compareExchangeWeak(expected, 3, moRelaxed) + doAssert expected == 2 + doAssert location.load == 3 + expected = 3 + doAssert location.compareExchangeWeak(expected, 4, moAcquire) + doAssert expected == 3 + doAssert location.load == 4 + expected = 4 + doAssert location.compareExchangeWeak(expected, 5, moRelease) + doAssert expected == 4 + doAssert location.load == 5 + expected = 5 + doAssert location.compareExchangeWeak(expected, 6, moAcquireRelease) + doAssert expected == 5 + doAssert location.load == 6 + + +block trivialCompareExchangeWeakDoesNotExchange: + var location: Atomic[int] + var expected = 10 + location.store(1) + doAssert not location.compareExchangeWeak(expected, 2) + doAssert expected == 1 + doAssert location.load == 1 + expected = 10 + doAssert not location.compareExchangeWeak(expected, 3, moRelaxed) + doAssert expected == 1 + doAssert location.load == 1 + expected = 10 + doAssert not location.compareExchangeWeak(expected, 4, moAcquire) + doAssert expected == 1 + doAssert location.load == 1 + expected = 10 + doAssert not location.compareExchangeWeak(expected, 5, moRelease) + doAssert expected == 1 + doAssert location.load == 1 + expected = 10 + doAssert not location.compareExchangeWeak(expected, 6, moAcquireRelease) + doAssert expected == 1 + doAssert location.load == 1 + + +block trivialCompareExchangeWeakSuccessFailureDoesExchange: + var location: Atomic[int] + var expected = 1 + location.store(1) + doAssert location.compareExchangeWeak(expected, 2, moSequentiallyConsistent, moSequentiallyConsistent) + doAssert expected == 1 + doAssert location.load == 2 + expected = 2 + doAssert location.compareExchangeWeak(expected, 3, moRelaxed, moRelaxed) + doAssert expected == 2 + doAssert location.load == 3 + expected = 3 + doAssert location.compareExchangeWeak(expected, 4, moAcquire, moAcquire) + doAssert expected == 3 + doAssert location.load == 4 + expected = 4 + doAssert location.compareExchangeWeak(expected, 5, moRelease, moRelease) + doAssert expected == 4 + doAssert location.load == 5 + expected = 5 + doAssert location.compareExchangeWeak(expected, 6, moAcquireRelease, moAcquireRelease) + doAssert expected == 5 + doAssert location.load == 6 + + +block trivialCompareExchangeWeakSuccessFailureDoesNotExchange: + var location: Atomic[int] + var expected = 10 + location.store(1) + doAssert not location.compareExchangeWeak(expected, 2, moSequentiallyConsistent, moSequentiallyConsistent) + doAssert expected == 1 + doAssert location.load == 1 + expected = 10 + doAssert not location.compareExchangeWeak(expected, 3, moRelaxed, moRelaxed) + doAssert expected == 1 + doAssert location.load == 1 + expected = 10 + doAssert not location.compareExchangeWeak(expected, 4, moAcquire, moAcquire) + doAssert expected == 1 + doAssert location.load == 1 + expected = 10 + doAssert not location.compareExchangeWeak(expected, 5, moRelease, moRelease) + doAssert expected == 1 + doAssert location.load == 1 + expected = 10 + doAssert not location.compareExchangeWeak(expected, 6, moAcquireRelease, moAcquireRelease) + doAssert expected == 1 + doAssert location.load == 1 + + +# Atomic operations for non-trivial objects + + +block objectLoad: + var location: Atomic[Object] + location.store(Object(val: 1)) + doAssert location.load == Object(val: 1) + location.store(Object(val: 2)) + doAssert location.load(moRelaxed) == Object(val: 2) + location.store(Object(val: 3)) + doAssert location.load(moAcquire) == Object(val: 3) + + +block objectStore: + var location: Atomic[Object] + location.store(Object(val: 1)) + doAssert location.load == Object(val: 1) + location.store(Object(val: 2), moRelaxed) + doAssert location.load == Object(val: 2) + location.store(Object(val: 3), moRelease) + doAssert location.load == Object(val: 3) + + +block objectExchange: + var location: Atomic[Object] + location.store(Object(val: 1)) + doAssert location.exchange(Object(val: 2)) == Object(val: 1) + doAssert location.exchange(Object(val: 3), moRelaxed) == Object(val: 2) + doAssert location.exchange(Object(val: 4), moAcquire) == Object(val: 3) + doAssert location.exchange(Object(val: 5), moRelease) == Object(val: 4) + doAssert location.exchange(Object(val: 6), moAcquireRelease) == Object(val: 5) + doAssert location.load == Object(val: 6) + + +block objectCompareExchangeDoesExchange: + var location: Atomic[Object] + var expected = Object(val: 1) + location.store(Object(val: 1)) + doAssert location.compareExchange(expected, Object(val: 2)) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 2) + expected = Object(val: 2) + doAssert location.compareExchange(expected, Object(val: 3), moRelaxed) + doAssert expected == Object(val: 2) + doAssert location.load == Object(val: 3) + expected = Object(val: 3) + doAssert location.compareExchange(expected, Object(val: 4), moAcquire) + doAssert expected == Object(val: 3) + doAssert location.load == Object(val: 4) + expected = Object(val: 4) + doAssert location.compareExchange(expected, Object(val: 5), moRelease) + doAssert expected == Object(val: 4) + doAssert location.load == Object(val: 5) + expected = Object(val: 5) + doAssert location.compareExchange(expected, Object(val: 6), moAcquireRelease) + doAssert expected == Object(val: 5) + doAssert location.load == Object(val: 6) + + +block objectCompareExchangeDoesNotExchange: + var location: Atomic[Object] + var expected = Object(val: 10) + location.store(Object(val: 1)) + doAssert not location.compareExchange(expected, Object(val: 2)) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 1) + expected = Object(val: 10) + doAssert not location.compareExchange(expected, Object(val: 3), moRelaxed) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 1) + expected = Object(val: 10) + doAssert not location.compareExchange(expected, Object(val: 4), moAcquire) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 1) + expected = Object(val: 10) + doAssert not location.compareExchange(expected, Object(val: 5), moRelease) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 1) + expected = Object(val: 10) + doAssert not location.compareExchange(expected, Object(val: 6), moAcquireRelease) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 1) + + +block objectCompareExchangeSuccessFailureDoesExchange: + var location: Atomic[Object] + var expected = Object(val: 1) + location.store(Object(val: 1)) + doAssert location.compareExchange(expected, Object(val: 2), moSequentiallyConsistent, moSequentiallyConsistent) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 2) + expected = Object(val: 2) + doAssert location.compareExchange(expected, Object(val: 3), moRelaxed, moRelaxed) + doAssert expected == Object(val: 2) + doAssert location.load == Object(val: 3) + expected = Object(val: 3) + doAssert location.compareExchange(expected, Object(val: 4), moAcquire, moAcquire) + doAssert expected == Object(val: 3) + doAssert location.load == Object(val: 4) + expected = Object(val: 4) + doAssert location.compareExchange(expected, Object(val: 5), moRelease, moRelease) + doAssert expected == Object(val: 4) + doAssert location.load == Object(val: 5) + expected = Object(val: 5) + doAssert location.compareExchange(expected, Object(val: 6), moAcquireRelease, moAcquireRelease) + doAssert expected == Object(val: 5) + doAssert location.load == Object(val: 6) + + +block objectCompareExchangeSuccessFailureDoesNotExchange: + var location: Atomic[Object] + var expected = Object(val: 10) + location.store(Object(val: 1)) + doAssert not location.compareExchange(expected, Object(val: 2), moSequentiallyConsistent, moSequentiallyConsistent) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 1) + expected = Object(val: 10) + doAssert not location.compareExchange(expected, Object(val: 3), moRelaxed, moRelaxed) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 1) + expected = Object(val: 10) + doAssert not location.compareExchange(expected, Object(val: 4), moAcquire, moAcquire) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 1) + expected = Object(val: 10) + doAssert not location.compareExchange(expected, Object(val: 5), moRelease, moRelease) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 1) + expected = Object(val: 10) + doAssert not location.compareExchange(expected, Object(val: 6), moAcquireRelease, moAcquireRelease) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 1) + + +block objectCompareExchangeWeakDoesExchange: + var location: Atomic[Object] + var expected = Object(val: 1) + location.store(Object(val: 1)) + doAssert location.compareExchangeWeak(expected, Object(val: 2)) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 2) + expected = Object(val: 2) + doAssert location.compareExchangeWeak(expected, Object(val: 3), moRelaxed) + doAssert expected == Object(val: 2) + doAssert location.load == Object(val: 3) + expected = Object(val: 3) + doAssert location.compareExchangeWeak(expected, Object(val: 4), moAcquire) + doAssert expected == Object(val: 3) + doAssert location.load == Object(val: 4) + expected = Object(val: 4) + doAssert location.compareExchangeWeak(expected, Object(val: 5), moRelease) + doAssert expected == Object(val: 4) + doAssert location.load == Object(val: 5) + expected = Object(val: 5) + doAssert location.compareExchangeWeak(expected, Object(val: 6), moAcquireRelease) + doAssert expected == Object(val: 5) + doAssert location.load == Object(val: 6) + + +block objectCompareExchangeWeakDoesNotExchange: + var location: Atomic[Object] + var expected = Object(val: 10) + location.store(Object(val: 1)) + doAssert not location.compareExchangeWeak(expected, Object(val: 2)) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 1) + expected = Object(val: 10) + doAssert not location.compareExchangeWeak(expected, Object(val: 3), moRelaxed) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 1) + expected = Object(val: 10) + doAssert not location.compareExchangeWeak(expected, Object(val: 4), moAcquire) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 1) + expected = Object(val: 10) + doAssert not location.compareExchangeWeak(expected, Object(val: 5), moRelease) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 1) + expected = Object(val: 10) + doAssert not location.compareExchangeWeak(expected, Object(val: 6), moAcquireRelease) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 1) + + +block objectCompareExchangeWeakSuccessFailureDoesExchange: + var location: Atomic[Object] + var expected = Object(val: 1) + location.store(Object(val: 1)) + doAssert location.compareExchangeWeak(expected, Object(val: 2), moSequentiallyConsistent, moSequentiallyConsistent) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 2) + expected = Object(val: 2) + doAssert location.compareExchangeWeak(expected, Object(val: 3), moRelaxed, moRelaxed) + doAssert expected == Object(val: 2) + doAssert location.load == Object(val: 3) + expected = Object(val: 3) + doAssert location.compareExchangeWeak(expected, Object(val: 4), moAcquire, moAcquire) + doAssert expected == Object(val: 3) + doAssert location.load == Object(val: 4) + expected = Object(val: 4) + doAssert location.compareExchangeWeak(expected, Object(val: 5), moRelease, moRelease) + doAssert expected == Object(val: 4) + doAssert location.load == Object(val: 5) + expected = Object(val: 5) + doAssert location.compareExchangeWeak(expected, Object(val: 6), moAcquireRelease, moAcquireRelease) + doAssert expected == Object(val: 5) + doAssert location.load == Object(val: 6) + + +block objectCompareExchangeWeakSuccessFailureDoesNotExchange: + var location: Atomic[Object] + var expected = Object(val: 10) + location.store(Object(val: 1)) + doAssert not location.compareExchangeWeak(expected, Object(val: 2), moSequentiallyConsistent, moSequentiallyConsistent) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 1) + expected = Object(val: 10) + doAssert not location.compareExchangeWeak(expected, Object(val: 3), moRelaxed, moRelaxed) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 1) + expected = Object(val: 10) + doAssert not location.compareExchangeWeak(expected, Object(val: 4), moAcquire, moAcquire) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 1) + expected = Object(val: 10) + doAssert not location.compareExchangeWeak(expected, Object(val: 5), moRelease, moRelease) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 1) + expected = Object(val: 10) + doAssert not location.compareExchangeWeak(expected, Object(val: 6), moAcquireRelease, moAcquireRelease) + doAssert expected == Object(val: 1) + doAssert location.load == Object(val: 1) + + +# Numerical operations + + +block fetchAdd: + var location: Atomic[int] + doAssert location.fetchAdd(1) == 0 + doAssert location.fetchAdd(1, moRelaxed) == 1 + doAssert location.fetchAdd(1, moRelease) == 2 + doAssert location.load == 3 + + +block fetchSub: + var location: Atomic[int] + doAssert location.fetchSub(1) == 0 + doAssert location.fetchSub(1, moRelaxed) == -1 + doAssert location.fetchSub(1, moRelease) == -2 + doAssert location.load == -3 + + +block fetchAnd: + var location: Atomic[int] + + for i in 0..16: + for j in 0..16: + location.store(i) + doAssert(location.fetchAnd(j) == i) + doAssert(location.load == i.bitand(j)) + + for i in 0..16: + for j in 0..16: + location.store(i) + doAssert(location.fetchAnd(j, moRelaxed) == i) + doAssert(location.load == i.bitand(j)) + + for i in 0..16: + for j in 0..16: + location.store(i) + doAssert(location.fetchAnd(j, moRelease) == i) + doAssert(location.load == i.bitand(j)) + + +block fetchOr: + var location: Atomic[int] + + for i in 0..16: + for j in 0..16: + location.store(i) + doAssert(location.fetchOr(j) == i) + doAssert(location.load == i.bitor(j)) + + for i in 0..16: + for j in 0..16: + location.store(i) + doAssert(location.fetchOr(j, moRelaxed) == i) + doAssert(location.load == i.bitor(j)) + + for i in 0..16: + for j in 0..16: + location.store(i) + doAssert(location.fetchOr(j, moRelease) == i) + doAssert(location.load == i.bitor(j)) + + +block fetchXor: + var location: Atomic[int] + + for i in 0..16: + for j in 0..16: + location.store(i) + doAssert(location.fetchXor(j) == i) + doAssert(location.load == i.bitxor(j)) + + for i in 0..16: + for j in 0..16: + location.store(i) + doAssert(location.fetchXor(j, moRelaxed) == i) + doAssert(location.load == i.bitxor(j)) + + for i in 0..16: + for j in 0..16: + location.store(i) + doAssert(location.fetchXor(j, moRelease) == i) + doAssert(location.load == i.bitxor(j)) + + +block atomicInc: + var location: Atomic[int] + location.atomicInc + doAssert location.load == 1 + location.atomicInc(1) + doAssert location.load == 2 + location += 1 + doAssert location.load == 3 + + +block atomicDec: + var location: Atomic[int] + location.atomicDec + doAssert location.load == -1 + location.atomicDec(1) + doAssert location.load == -2 + location -= 1 + doAssert location.load == -3 + + +# Flag operations + + +block testAndSet: + var location: AtomicFlag + doAssert not location.testAndSet + doAssert location.testAndSet + doAssert location.testAndSet + location.clear() + doAssert not location.testAndSet(moRelaxed) + doAssert location.testAndSet(moRelaxed) + doAssert location.testAndSet(moRelaxed) + location.clear() + doAssert not location.testAndSet(moRelease) + doAssert location.testAndSet(moRelease) + doAssert location.testAndSet(moRelease) + + +block clear: + var location: AtomicFlag + discard location.testAndSet + location.clear + doAssert not location.testAndSet + location.clear(moRelaxed) + doAssert not location.testAndSet + location.clear(moRelease) + doAssert not location.testAndSet From 66eba3388aacba20c49b925c9c700f988fb9e5d0 Mon Sep 17 00:00:00 2001 From: John Dupuy Date: Sat, 22 Aug 2020 05:00:38 -0500 Subject: [PATCH 0544/3768] Added more SSL documentation to `net` module. (#15206) * Added more SSL documentation to `net` module. * Changed two of the net.nim doc refs to links. * Update lib/pure/net.nim doc wording. Co-authored-by: Dominik Picheta * Update lib/pure/net.nim - added space to doc URI Co-authored-by: alaviss * Fixed another doc URI in net module. * For net module doc added warning to connect procedure. * Update net.nim Co-authored-by: Dominik Picheta Co-authored-by: alaviss --- lib/pure/net.nim | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index ea1c86c31..239e3e9f6 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -20,7 +20,9 @@ ## ==== ## ## In order to use the SSL procedures defined in this module, you will need to -## compile your application with the ``-d:ssl`` flag. +## compile your application with the ``-d:ssl`` flag. See the +## `newContext`_ +## procedure for additional details. ## ## Examples ## ======== @@ -36,9 +38,17 @@ ## var socket = newSocket() ## socket.connect("google.com", Port(80)) ## +## For SSL, use the following example (and make sure to compile with ``-d:ssl``): +## +## .. code-block:: Nim +## var socket = newSocket() +## var ctx = newContext() +## wrapSocket(ctx, socket) +## socket.connect("google.com", Port(443)) +## ## UDP is a connectionless protocol, so UDP sockets don't have to explicitly -## call the ``connect`` procedure. They can simply start sending data -## immediately. +## call the `connect `_ procedure. They can +## simply start sending data immediately. ## ## .. code-block:: Nim ## var socket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) @@ -1942,6 +1952,10 @@ proc connect*(socket: Socket, address: string, port = Port(0), ## ## The ``timeout`` parameter specifies the time in milliseconds to allow for ## the connection to the server to be made. + ## + ## **Warning:** This procedure appears to be broken for SSL connections as of + ## Nim v1.0.2. Consider using the other `connect` procedure. See + ## https://github.com/nim-lang/Nim/issues/15215 for more info. socket.fd.setBlocking(false) socket.connectAsync(address, port, socket.domain) From 9bae1ed205875dee7fd58cf1d740db0d70130228 Mon Sep 17 00:00:00 2001 From: wltsmrz Date: Sat, 22 Aug 2020 17:14:50 -0400 Subject: [PATCH 0545/3768] Unsigned iovlen (#15216) --- lib/posix/posix_macos_amd64.nim | 2 +- lib/posix/posix_nintendoswitch.nim | 2 +- lib/posix/posix_openbsd_amd64.nim | 2 +- lib/posix/posix_other.nim | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/posix/posix_macos_amd64.nim b/lib/posix/posix_macos_amd64.nim index 33f2c5c41..94c08acad 100644 --- a/lib/posix/posix_macos_amd64.nim +++ b/lib/posix/posix_macos_amd64.nim @@ -411,7 +411,7 @@ type IOVec* {.importc: "struct iovec", pure, final, header: "".} = object ## struct iovec iov_base*: pointer ## Base address of a memory region for input or output. - iov_len*: int ## The size of the memory pointed to by iov_base. + iov_len*: csize_t ## The size of the memory pointed to by iov_base. Tmsghdr* {.importc: "struct msghdr", pure, final, header: "".} = object ## struct msghdr diff --git a/lib/posix/posix_nintendoswitch.nim b/lib/posix/posix_nintendoswitch.nim index d1cc042ea..44e431437 100644 --- a/lib/posix/posix_nintendoswitch.nim +++ b/lib/posix/posix_nintendoswitch.nim @@ -371,7 +371,7 @@ type IOVec* {.importc: "struct iovec", pure, final, header: "".} = object ## struct iovec iov_base*: pointer ## Base address of a memory region for input or output. - iov_len*: csize ## The size of the memory pointed to by iov_base. + iov_len*: csize_t ## The size of the memory pointed to by iov_base. Tmsghdr* {.importc: "struct msghdr", pure, final, header: "".} = object ## struct msghdr diff --git a/lib/posix/posix_openbsd_amd64.nim b/lib/posix/posix_openbsd_amd64.nim index 4f34791fe..584d06501 100644 --- a/lib/posix/posix_openbsd_amd64.nim +++ b/lib/posix/posix_openbsd_amd64.nim @@ -398,7 +398,7 @@ type IOVec* {.importc: "struct iovec", pure, final, header: "".} = object ## struct iovec iov_base*: pointer ## Base address of a memory region for input or output. - iov_len*: int ## The size of the memory pointed to by iov_base. + iov_len*: csize_t ## The size of the memory pointed to by iov_base. Tmsghdr* {.importc: "struct msghdr", pure, final, header: "".} = object ## struct msghdr diff --git a/lib/posix/posix_other.nim b/lib/posix/posix_other.nim index 59ae9c8f7..ed058d5cf 100644 --- a/lib/posix/posix_other.nim +++ b/lib/posix/posix_other.nim @@ -421,7 +421,7 @@ type IOVec* {.importc: "struct iovec", pure, final, header: "".} = object ## struct iovec iov_base*: pointer ## Base address of a memory region for input or output. - iov_len*: int ## The size of the memory pointed to by iov_base. + iov_len*: csize_t ## The size of the memory pointed to by iov_base. Tmsghdr* {.importc: "struct msghdr", pure, final, header: "".} = object ## struct msghdr From 575697e1f4d94cfe080edb28f88ce8982b8add54 Mon Sep 17 00:00:00 2001 From: alaviss Date: Sun, 23 Aug 2020 02:48:37 -0500 Subject: [PATCH 0546/3768] doc/nimdoc.css: align field names to the right (#15217) --- doc/nimdoc.css | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/nimdoc.css b/doc/nimdoc.css index 6025d6601..fb9efd858 100644 --- a/doc/nimdoc.css +++ b/doc/nimdoc.css @@ -582,6 +582,7 @@ table th { table th.docinfo-name { background-color: transparent; + text-align: right; } table tr:hover { From 07d82b5cc4b7ba177de43a3eb46c7c645930421a Mon Sep 17 00:00:00 2001 From: Mamy Ratsimbazafy Date: Mon, 24 Aug 2020 08:05:17 +0200 Subject: [PATCH 0547/3768] Use more `lent` in options (#15208) --- lib/pure/options.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pure/options.nim b/lib/pure/options.nim index d56cf4767..4efe04d67 100644 --- a/lib/pure/options.nim +++ b/lib/pure/options.nim @@ -363,14 +363,14 @@ proc `$`*[T](self: Option[T]): string = else: result = "None[" & name(T) & "]" -proc unsafeGet*[T](self: Option[T]): T {.inline.}= +proc unsafeGet*[T](self: Option[T]): lent T {.inline.}= ## Returns the value of a `some`. Behavior is undefined for `none`. ## ## **Note:** Use it only when you are **absolutely sure** the value is present ## (e.g. after checking `isSome <#isSome,Option[T]>`_). ## Generally, using `get proc <#get,Option[T]>`_ is preferred. assert self.isSome - self.val + result = self.val when isMainModule: From 8321617a7845f01f1e60e0b00f275345b15845e9 Mon Sep 17 00:00:00 2001 From: Jon Date: Mon, 24 Aug 2020 14:33:51 +0800 Subject: [PATCH 0548/3768] fix in doc: incomplete output (#15222) [ci skip] --- lib/pure/collections/tables.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index 017a2f337..6d79638c2 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -131,7 +131,7 @@ ## let myString = "abracadabra" ## let letterFrequencies = toCountTable(myString) ## echo letterFrequencies -## # 'a': 5, 'b': 2, 'c': 1, 'd': 1, 'r': 2} +## # output: {'a': 5, 'b': 2, 'c': 1, 'd': 1, 'r': 2} ## ## The same could have been achieved by manually iterating over a container ## and increasing each key's value with `inc proc From 5651a2f711ae0ce3eb86400646ca2b7e9ab2988a Mon Sep 17 00:00:00 2001 From: narimiran Date: Mon, 24 Aug 2020 13:44:19 +0200 Subject: [PATCH 0549/3768] PRs with no activity in last year are marked as stale --- .github/stale.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/stale.yml b/.github/stale.yml index 7825ad58d..c175f64a0 100644 --- a/.github/stale.yml +++ b/.github/stale.yml @@ -1,7 +1,7 @@ # Configuration for probot-stale - https://github.com/probot/stale # Number of days of inactivity before an Issue or Pull Request becomes stale -daysUntilStale: 730 +daysUntilStale: 365 # Number of days of inactivity before an Issue or Pull Request with the stale label is closed. # Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. From 15ff89cec14ea6d1bc05a5975dedfcb6e712720b Mon Sep 17 00:00:00 2001 From: Miran Date: Tue, 25 Aug 2020 09:57:15 +0200 Subject: [PATCH 0550/3768] [backport] fix #15064, strscans.scanf edge case for '$+' (#15223) --- lib/pure/strscans.nim | 2 +- tests/stdlib/tstrscans.nim | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/pure/strscans.nim b/lib/pure/strscans.nim index 0125a1926..f078b0d20 100644 --- a/lib/pure/strscans.nim +++ b/lib/pure/strscans.nim @@ -396,7 +396,7 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b var resLen = genSym(nskLet, "resLen") conds.add newLetStmt(resLen, newCall(bindSym"parseUntil", inp, results[i], newLit(token), idx)) - conds.add newCall(bindSym"!=", resLen, newLit min) + conds.add newCall(bindSym">=", resLen, newLit min) conds.add resLen else: matchError diff --git a/tests/stdlib/tstrscans.nim b/tests/stdlib/tstrscans.nim index 08fc14e45..6f952368c 100644 --- a/tests/stdlib/tstrscans.nim +++ b/tests/stdlib/tstrscans.nim @@ -79,8 +79,22 @@ block EmptyTuple: block Arrow: let text = "foo;bar;baz;" var idx = 0 - var res = "" doAssert scanp(text, idx, +(~{';','\0'} -> (discard $_)), ';') doAssert scanp(text, idx, +(~{';','\0'} -> (discard $_)), ';') doAssert scanp(text, idx, +(~{';','\0'} -> (discard $_)), ';') doAssert scanp(text, idx, +(~{';','\0'} -> (discard $_)), ';') == false + + +block issue15064: + var nick1, msg1: string + doAssert scanf(" a", "<$+> $+", nick1, msg1) + doAssert nick1 == "abcd" + doAssert msg1 == "a" + + var nick2, msg2: string + doAssert(not scanf(" ", "<$+> $+", nick2, msg2)) + + var nick3, msg3: string + doAssert scanf(" ", "<$+> $*", nick3, msg3) + doAssert nick3 == "abcd" + doAssert msg3 == "" From 7cee63bba3805bd49f0c03b94d65f02dea04f946 Mon Sep 17 00:00:00 2001 From: Bung Date: Tue, 25 Aug 2020 15:58:32 +0800 Subject: [PATCH 0551/3768] avoid #8231, bitwise move to mul,div (#15070) * avoid #8231, bitwise move to mul,div * add test for #8231 * fix bitwise move when div result is float * bitwise move depends on typ.size --- compiler/jsgen.nim | 12 ++++++++++-- tests/js/t8231.nim | 3 +++ 2 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 tests/js/t8231.nim diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index ede4d14e0..bea5225e6 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -602,8 +602,16 @@ proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = of mMulF64: applyFormat("($1 * $2)", "($1 * $2)") of mDivF64: applyFormat("($1 / $2)", "($1 / $2)") of mShrI: applyFormat("", "") - of mShlI: applyFormat("($1 << $2)", "($1 << $2)") - of mAshrI: applyFormat("($1 >> $2)", "($1 >> $2)") + of mShlI: + if n[1].typ.size <= 4: + applyFormat("($1 << $2)", "($1 << $2)") + else: + applyFormat("($1 * Math.pow(2,$2))", "($1 * Math.pow(2,$2))") + of mAshrI: + if n[1].typ.size <= 4: + applyFormat("($1 >> $2)", "($1 >> $2)") + else: + applyFormat("Math.floor($1 / Math.pow(2,$2))", "Math.floor($1 / Math.pow(2,$2))") of mBitandI: applyFormat("($1 & $2)", "($1 & $2)") of mBitorI: applyFormat("($1 | $2)", "($1 | $2)") of mBitxorI: applyFormat("($1 ^ $2)", "($1 ^ $2)") diff --git a/tests/js/t8231.nim b/tests/js/t8231.nim new file mode 100644 index 000000000..b0625a621 --- /dev/null +++ b/tests/js/t8231.nim @@ -0,0 +1,3 @@ +import strutils + +doAssert formatSize(2462056448, '.', bpIEC, false) == "2.293GiB" \ No newline at end of file From 5163fe7d878f48870c6d0235d70d45e1e414dca8 Mon Sep 17 00:00:00 2001 From: Miran Date: Tue, 25 Aug 2020 15:59:42 +0200 Subject: [PATCH 0552/3768] remove deprecation from `math.round` (#15224) --- changelog.md | 7 +++++++ lib/pure/math.nim | 3 +-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index eebccb62f..3865f1efb 100644 --- a/changelog.md +++ b/changelog.md @@ -164,7 +164,14 @@ echo foo ``` +- Proc `math.round` is no longer deprecated. The advice to use `strformat` instead + cannot be applied to every use case. The limitations and the (lack of) reliability + of `round` are well documented. + + + ## Language changes + - The `=destroy` hook no longer has to reset its target, as the compiler now automatically inserts `wasMoved` calls where needed. - In the newruntime it is now allowed to assign to the discriminator field diff --git a/lib/pure/math.nim b/lib/pure/math.nim index c02bef2f7..21d614270 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -768,8 +768,7 @@ else: # JS ## ( 6.5 mod -2.5) == 1.5 ## (-6.5 mod -2.5) == -1.5 -proc round*[T: float32|float64](x: T, places: int): T {. - deprecated: "use strformat module instead".} = +proc round*[T: float32|float64](x: T, places: int): T = ## Decimal rounding on a binary floating point number. ## ## This function is NOT reliable. Floating point numbers cannot hold From 71025b8a8f4ebbb58555de74565ce006a9c17444 Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 20 Aug 2020 12:47:07 +0200 Subject: [PATCH 0553/3768] deleted dead code, writetracking.nim was replaced by varpartitions.nim --- compiler/writetracking.nim | 275 ------------------------------------- 1 file changed, 275 deletions(-) delete mode 100644 compiler/writetracking.nim diff --git a/compiler/writetracking.nim b/compiler/writetracking.nim deleted file mode 100644 index 44ef3a937..000000000 --- a/compiler/writetracking.nim +++ /dev/null @@ -1,275 +0,0 @@ -# -# -# The Nim Compiler -# (c) Copyright 2015 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## This module implements the write tracking analysis. Read my block post for -## a basic description of the algorithm and ideas. -## The algorithm operates in 2 phases: -## -## * Collecting information about assignments (and pass-by-var calls). -## * Computing an aliasing relation based on the assignments. This relation -## is then used to compute the 'writes' and 'escapes' effects. - -import intsets, idents, ast, trees, msgs, types, options, lineinfos - -const - debug = false - -type - AssignToResult = enum - asgnNil, # 'nil' is fine - asgnNew, # 'new(result)' - asgnOther # result = fooBar # not a 'new' --> 'result' might not 'new' - NewLocation = enum - newNone, - newLit, - newCall - RootInfo = enum - rootIsResultOrParam, - rootIsHeapAccess, - rootIsSym, - markAsWrittenTo, - markAsEscaping - - Assignment = object # \ - # Note that the transitive closures MUST be computed in - # phase 2 of the algorithm. - dest, src: seq[ptr TSym] # we use 'ptr' here to save RC ops and GC cycles - destNoTc, srcNoTc: int # length of 'dest', 'src' without the - # transitive closure - destInfo: set[RootInfo] - info: TLineInfo - - W = object # WriteTrackContext - owner: PSym - returnsNew: AssignToResult # assignments to 'result' - assignments: seq[Assignment] # list of all assignments in this proc - -proc allRoots(n: PNode; result: var seq[ptr TSym]; info: var set[RootInfo]) = - case n.kind - of nkSym: - if n.sym.kind in {skParam, skVar, skTemp, skLet, skResult, skForVar}: - if n.sym.kind in {skResult, skParam}: incl(info, rootIsResultOrParam) - result.add(cast[ptr TSym](n.sym)) - of nkHiddenDeref, nkDerefExpr: - incl(info, rootIsHeapAccess) - allRoots(n[0], result, info) - of nkDotExpr, nkBracketExpr, nkCheckedFieldExpr, - nkHiddenAddr, nkObjUpConv, nkObjDownConv: - allRoots(n[0], result, info) - of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv, nkConv, - nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr, nkOfBranch, - nkElifBranch, nkElse, nkExceptBranch, nkFinally, nkCast: - allRoots(n.lastSon, result, info) - of nkCallKinds: - if getMagic(n) == mSlice: - allRoots(n[1], result, info) - else: - # we do significantly better here by using the available escape - # information: - if n[0].typ.isNil: return - var typ = n[0].typ - if typ != nil: - typ = skipTypes(typ, abstractInst) - if typ.kind != tyProc: typ = nil - else: assert(typ.len == typ.n.len) - - for i in 1.. 0 and a.dest[0] == x and rootIsSym in a.destInfo: - # x = f(..., y, ....) - for i in 0.. Date: Wed, 26 Aug 2020 14:31:00 +0200 Subject: [PATCH 0554/3768] fixes #15207 [backport:1.2] --- lib/pure/terminal.nim | 33 ++++++++++++++------------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index 99a333cc6..968d6bd9a 100644 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -776,25 +776,20 @@ when defined(windows): ## ``true`` otherwise. password.string.setLen(0) stdout.write(prompt) - while true: - let c = getch() - case c.char - of '\r', chr(0xA): - break - of '\b': - # ensure we delete the whole UTF-8 character: - var i = 0 - var x = 1 - while i < password.len: - x = runeLenAt(password.string, i) - inc i, x - password.string.setLen(max(password.len - x, 0)) - of chr(0x0): - # modifier key - ignore - for details see - # https://github.com/nim-lang/Nim/issues/7764 - continue - else: - password.string.add(toUTF8(c.Rune)) + let hi = createFileA("CONIN$", + GENERIC_READ or GENERIC_WRITE, 0, nil, OPEN_EXISTING, 0, 0) + var mode = DWORD 0 + discard getConsoleMode(hi, addr mode) + let origMode = mode + const + ENABLE_PROCESSED_INPUT = 1 + ENABLE_ECHO_INPUT = 4 + mode = (mode or ENABLE_PROCESSED_INPUT) and not ENABLE_ECHO_INPUT + + discard setConsoleMode(hi, mode) + result = readLine(stdin, password) + discard setConsoleMode(hi, origMode) + discard closeHandle(hi) stdout.write "\n" else: From d85015e9225a0747453a1bd9a8b6f292f5bfe3af Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Wed, 26 Aug 2020 11:51:41 -0300 Subject: [PATCH 0555/3768] Remove unroll pragma from stdlib (#14706) --- lib/packages/docutils/highlite.nim | 4 ---- 1 file changed, 4 deletions(-) diff --git a/lib/packages/docutils/highlite.nim b/lib/packages/docutils/highlite.nim index 796c17d7d..df74dd980 100644 --- a/lib/packages/docutils/highlite.nim +++ b/lib/packages/docutils/highlite.nim @@ -651,19 +651,16 @@ proc yamlNextToken(g: var GeneralTokenizer) = of 'x': inc(pos) for i in 1..2: - {.unroll.} if g.buf[pos] in hexChars: inc(pos) break of 'u': inc(pos) for i in 1..4: - {.unroll.} if g.buf[pos] in hexChars: inc(pos) break of 'U': inc(pos) for i in 1..8: - {.unroll.} if g.buf[pos] in hexChars: inc(pos) break else: inc(pos) @@ -802,7 +799,6 @@ proc yamlNextToken(g: var GeneralTokenizer) = if pos == 0 or g.buf[pos - 1] in {'\x0A', '\x0D'}: inc(pos) for i in 1..2: - {.unroll.} if g.buf[pos] != '.': break inc(pos) if pos == g.start + 3: From a9a9860b0b5171487fae100010a18ad71f5d5f6d Mon Sep 17 00:00:00 2001 From: Scott Wadden Date: Wed, 26 Aug 2020 15:00:54 -0300 Subject: [PATCH 0556/3768] Raise KeyError if passed an invalid row entry (#15227) --- lib/pure/parsecsv.nim | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/pure/parsecsv.nim b/lib/pure/parsecsv.nim index 607702fc0..2c0dbb802 100644 --- a/lib/pure/parsecsv.nim +++ b/lib/pure/parsecsv.nim @@ -322,24 +322,29 @@ proc rowEntry*(my: var CsvParser, entry: string): var string = ## ## Assumes that `readHeaderRow <#readHeaderRow,CsvParser>`_ has already been ## called. + ## + ## If specified `entry` does not exist, raises KeyError. runnableExamples: import streams var strm = newStringStream("One,Two,Three\n1,2,3\n\n10,20,30") var parser: CsvParser parser.open(strm, "tmp.csv") - ## Need calling `readHeaderRow`. + ## Requires calling `readHeaderRow`. parser.readHeaderRow() doAssert parser.readRow() doAssert parser.rowEntry("One") == "1" doAssert parser.rowEntry("Two") == "2" doAssert parser.rowEntry("Three") == "3" - ## `parser.rowEntry("NotExistEntry")` causes SIGSEGV fault. + doAssertRaises(KeyError): + discard parser.rowEntry("NonexistentEntry") parser.close() strm.close() let index = my.headers.find(entry) if index >= 0: result = my.row[index] + else: + raise newException(KeyError, "Entry `" & entry & "` doesn't exist") when not defined(testing) and isMainModule: import os From ccccd30cf6b8704c224e42001d0def2b566d5388 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 27 Aug 2020 11:25:20 +0200 Subject: [PATCH 0557/3768] fixes #15221 (#15230) --- lib/impure/nre.nim | 2 +- lib/pure/typetraits.nim | 90 ++++++++++++++++++++--------------------- 2 files changed, 45 insertions(+), 47 deletions(-) diff --git a/lib/impure/nre.nim b/lib/impure/nre.nim index 88fa0b323..71936bd5f 100644 --- a/lib/impure/nre.nim +++ b/lib/impure/nre.nim @@ -367,7 +367,7 @@ func contains*(pattern: CaptureBounds, name: string): bool = func contains*(pattern: Captures, name: string): bool = name in CaptureBounds(pattern) -func checkNamedCaptured(pattern: RegexMatch, name: string): void = +func checkNamedCaptured(pattern: RegexMatch, name: string) = if not (name in pattern.captureBounds): raise newException(KeyError, "Group '" & name & "' was not captured") diff --git a/lib/pure/typetraits.nim b/lib/pure/typetraits.nim index 0e23077ac..0a05b5ded 100644 --- a/lib/pure/typetraits.nim +++ b/lib/pure/typetraits.nim @@ -115,54 +115,52 @@ macro genericParamsImpl(T: typedesc): untyped = impl = impl[1] while true: case impl.kind - of nnkSym: - impl = impl.getImpl - continue - of nnkTypeDef: - impl = impl[2] - continue - of nnkTypeOfExpr: - impl = getTypeInst(impl[0]) - continue - of nnkBracketExpr: - for i in 1.. Date: Thu, 27 Aug 2020 05:56:38 -0500 Subject: [PATCH 0558/3768] fix some issues overloading with generics and inheritance (#15211) * fix some issues overloading with generics and inheritance * fix passing procs with subtype matches --- compiler/sigmatch.nim | 45 +++++++++++++++++++++------- tests/overload/toverload_various.nim | 36 ++++++++++++++++++---- 2 files changed, 65 insertions(+), 16 deletions(-) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 1d648c32a..8700d9a47 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -199,6 +199,27 @@ proc copyCandidate(a: var TCandidate, b: TCandidate) = a.baseTypeMatch = b.baseTypeMatch copyIdTable(a.bindings, b.bindings) +proc typeRel*(c: var TCandidate, f, aOrig: PType, + flags: TTypeRelFlags = {}): TTypeRelation + +proc checkGeneric(a, b: TCandidate): int = + let c = a.c + let aa = a.callee + let bb = b.callee + var winner = 0 + for i in 1.. tyGenericInvocation") - # simply no match for now: - discard + if x.kind == tyGenericInvocation: + if f[0] == x[0]: + for i in 1.. Date: Thu, 27 Aug 2020 15:50:59 +0200 Subject: [PATCH 0559/3768] Fix #5691 (#15158) * Fix #5691 * Cleanup and thoughts * Use scope approach * Seperate defined/declared/declaredInScope magics * Fix declaredInScope * Update spec accordingly --- compiler/ast.nim | 2 +- compiler/condsyms.nim | 1 + compiler/lookups.nim | 22 +++++++++++ compiler/sem.nim | 13 +++---- compiler/semcall.nim | 4 -- compiler/semexprs.nim | 42 +++++++++++---------- compiler/semgnrc.nim | 2 +- compiler/semstmts.nim | 2 +- compiler/sigmatch.nim | 26 ++++++------- doc/manual.rst | 25 ++++--------- lib/system.nim | 41 +++++++++++--------- tests/macros/tmacros_various.nim | 15 ++++++++ tests/macros/tmacrotypes.nim | 2 + tests/template/tparams_gensymed.nim | 58 +++++++++++++++++++++++++++++ tests/template/tparamscope.nim | 10 +++++ 15 files changed, 180 insertions(+), 85 deletions(-) create mode 100644 tests/template/tparamscope.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index 62c1e3894..06ff92e9f 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -617,7 +617,7 @@ var type TMagic* = enum # symbols that require compiler magic: mNone, - mDefined, mDefinedInScope, mCompiles, mArrGet, mArrPut, mAsgn, + mDefined, mDeclared, mDeclaredInScope, mCompiles, mArrGet, mArrPut, mAsgn, mLow, mHigh, mSizeOf, mAlignOf, mOffsetOf, mTypeTrait, mIs, mOf, mAddr, mType, mTypeOf, mPlugin, mEcho, mShallowCopy, mSlurp, mStaticExec, mStatic, diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index b7f7100f1..ad13f3e1e 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -119,3 +119,4 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasStacktraceMsgs") defineSymbol("nimDoesntTrackDefects") defineSymbol("nimHasLentIterators") + defineSymbol("nimHasDeclaredMagic") diff --git a/compiler/lookups.nim b/compiler/lookups.nim index a3a2bf49c..c0db25950 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -93,6 +93,11 @@ proc skipAlias*(s: PSym; n: PNode; conf: ConfigRef): PSym = proc localSearchInScope*(c: PContext, s: PIdent): PSym = result = strTableGet(c.currentScope.symbols, s) + var shadow = c.currentScope + while result == nil and shadow.parent != nil and shadow.depthLevel == shadow.parent.depthLevel: + # We are in a shadow scope, check in the parent too + result = strTableGet(shadow.parent.symbols, s) + shadow = shadow.parent proc searchInScopes*(c: PContext, s: PIdent): PSym = for scope in walkScopes(c.currentScope): @@ -231,6 +236,23 @@ proc addInterfaceOverloadableSymAt*(c: PContext, scope: PScope, sym: PSym) = addOverloadableSymAt(c, scope, sym) addInterfaceDeclAux(c, sym) +proc openShadowScope*(c: PContext) = + c.currentScope = PScope(parent: c.currentScope, + symbols: newStrTable(), + depthLevel: c.scopeDepth) + +proc closeShadowScope*(c: PContext) = + c.closeScope + +proc mergeShadowScope*(c: PContext) = + let shadowScope = c.currentScope + c.rawCloseScope + for sym in shadowScope.symbols: + if sym.kind in OverloadableSyms: + c.addInterfaceOverloadableSymAt(c.currentScope, sym) + else: + c.addInterfaceDecl(sym) + when defined(nimfix): # when we cannot find the identifier, retry with a changed identifier: proc altSpelling(x: PIdent): PIdent = diff --git a/compiler/sem.nim b/compiler/sem.nim index a07f62ca0..07c76eed4 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -383,12 +383,10 @@ when not defined(nimHasSinkInference): include hlo, seminst, semcall -when false: - # hopefully not required: - proc resetSemFlag(n: PNode) = - excl n.flags, nfSem - for i in 0.. 1: m.callee.n[1].sym else: nil while a < n.len: + + c.openShadowScope + if a >= formalLen-1 and f < formalLen and m.callee.n[f].typ.isVarargsUntyped: formal = m.callee.n[f].sym incl(marker, formal.position) @@ -2383,12 +2385,10 @@ proc matchesAux(c: PContext, n, nOrig: PNode, if n[a][0].kind != nkIdent: localError(c.config, n[a].info, "named parameter has to be an identifier") noMatch() - return formal = getNamedParamFromList(m.callee.n, n[a][0].ident) if formal == nil: # no error message! noMatch() - return if containsOrIncl(marker, formal.position): m.firstMismatch.kind = kAlreadyGiven # already in namedParams, so no match @@ -2397,7 +2397,6 @@ proc matchesAux(c: PContext, n, nOrig: PNode, # different parameter names could match later on): when false: localError(n[a].info, errCannotBindXTwice, formal.name.s) noMatch() - return m.baseTypeMatch = false m.typedescMatched = false n[a][1] = prepareOperand(c, formal.typ, n[a][1]) @@ -2407,7 +2406,6 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m.firstMismatch.kind = kTypeMismatch if arg == nil: noMatch() - return checkConstraint(n[a][1]) if m.baseTypeMatch: #assert(container == nil) @@ -2448,16 +2446,13 @@ proc matchesAux(c: PContext, n, nOrig: PNode, checkConstraint(n[a]) else: noMatch() - return else: m.firstMismatch.kind = kExtraArg noMatch() - return else: if m.callee.n[f].kind != nkSym: internalError(c.config, n[a].info, "matches") noMatch() - return formal = m.callee.n[f].sym m.firstMismatch.kind = kTypeMismatch if containsOrIncl(marker, formal.position) and container.isNil: @@ -2465,7 +2460,6 @@ proc matchesAux(c: PContext, n, nOrig: PNode, # positional param already in namedParams: (see above remark) when false: localError(n[a].info, errCannotBindXTwice, formal.name.s) noMatch() - return if formal.typ.isVarargsUntyped: if container.isNil: @@ -2482,7 +2476,6 @@ proc matchesAux(c: PContext, n, nOrig: PNode, n[a], nOrig[a]) if arg == nil: noMatch() - return if m.baseTypeMatch: assert formal.typ.kind == tyVarargs #assert(container == nil) @@ -2511,8 +2504,13 @@ proc matchesAux(c: PContext, n, nOrig: PNode, localError(c.config, n[a].info, "cannot convert $1 to $2" % [ typeToString(n[a].typ), typeToString(formal.typ) ]) noMatch() - return checkConstraint(n[a]) + + if m.state == csMatch and not(m.calleeSym != nil and m.calleeSym.kind in {skTemplate, skMacro}): + c.mergeShadowScope + else: + c.closeShadowScope + inc(a) # for some edge cases (see tdont_return_unowned_from_owned test case) m.firstMismatch.arg = a @@ -2544,7 +2542,7 @@ proc matches*(c: PContext, n, nOrig: PNode, m: var TCandidate) = if m.state == csNoMatch: return # check that every formal parameter got a value: for f in 1..`_ - ## - ## This can be used to check whether a library provides a certain - ## feature or not: - ## - ## .. code-block:: Nim - ## when not declared(strutils.toUpper): - ## # provide our own toUpper proc here, because strutils is - ## # missing it. +when defined(nimHasDeclaredMagic): + proc declared*(x: untyped): bool {.magic: "Declared", noSideEffect, compileTime.} + ## Special compile-time procedure that checks whether `x` is + ## declared. `x` has to be an identifier or a qualified identifier. + ## + ## See also: + ## * `declaredInScope <#declaredInScope,untyped>`_ + ## + ## This can be used to check whether a library provides a certain + ## feature or not: + ## + ## .. code-block:: Nim + ## when not declared(strutils.toUpper): + ## # provide our own toUpper proc here, because strutils is + ## # missing it. +else: + proc declared*(x: untyped): bool {.magic: "Defined", noSideEffect, compileTime.} -proc declaredInScope*(x: untyped): bool {. - magic: "DefinedInScope", noSideEffect, compileTime.} - ## Special compile-time procedure that checks whether `x` is - ## declared in the current scope. `x` has to be an identifier. +when defined(nimHasDeclaredMagic): + proc declaredInScope*(x: untyped): bool {.magic: "DeclaredInScope", noSideEffect, compileTime.} + ## Special compile-time procedure that checks whether `x` is + ## declared in the current scope. `x` has to be an identifier. +else: + proc declaredInScope*(x: untyped): bool {.magic: "DefinedInScope", noSideEffect, compileTime.} proc `addr`*[T](x: var T): ptr T {.magic: "Addr", noSideEffect.} = ## Builtin `addr` operator for taking the address of a memory location. diff --git a/tests/macros/tmacros_various.nim b/tests/macros/tmacros_various.nim index 3cb5aaa8f..64bb7345a 100644 --- a/tests/macros/tmacros_various.nim +++ b/tests/macros/tmacros_various.nim @@ -22,6 +22,7 @@ a[1]: 45 x: some string ([("key", "val"), ("keyB", "2")], [("val", "key"), ("2", "keyB")]) ([("key", "val"), ("keyB", "2")], [("val", "key"), ("2", "keyB")]) +0 ''' """ @@ -222,3 +223,17 @@ block getImplTransformed: doAssert "toExpand" notin code # template is expanded (but that would already be the case with # `a.getImpl.repr`, unlike the other transformations mentioned above + + +# test macro resemming +macro makeVar(): untyped = + quote: + var tensorY {.inject.}: int + +macro noop(a: typed): untyped = + a + +noop: + makeVar +echo tensorY + diff --git a/tests/macros/tmacrotypes.nim b/tests/macros/tmacrotypes.nim index cfc4ef7fb..43819c81d 100644 --- a/tests/macros/tmacrotypes.nim +++ b/tests/macros/tmacrotypes.nim @@ -43,6 +43,8 @@ macro checkType(ex: typed): untyped = echo ex.getTypeInst.repr, "; ", ex.typeKind, "; ", ex.getType.repr, "; ", ex.getTypeImpl.repr macro checkProcType(fn: typed): untyped = + if fn.kind == nnkProcDef: + result = fn let fn_sym = if fn.kind == nnkProcDef: fn[0] else: fn echo fn_sym, "; ", fn_sym.typeKind, "; ", fn_sym.getType.repr, "; ", fn_sym.getTypeImpl.repr diff --git a/tests/template/tparams_gensymed.nim b/tests/template/tparams_gensymed.nim index 2a1fda41a..1444667f4 100644 --- a/tests/template/tparams_gensymed.nim +++ b/tests/template/tparams_gensymed.nim @@ -336,3 +336,61 @@ test(hello): var data = 5 hello(data) + +# bug #5691 + +template bar(x: typed) = discard +macro barry(x: typed) = discard + +var a = 0 + +bar: + var a = 10 + +barry: + var a = 20 + +bar: + var b = 10 + +barry: + var b = 20 + +var b = 30 + +# template bar(x: static int) = discard +#You may think that this should work: +# bar((var c = 1; echo "hey"; c)) +# echo c +#But it must not! Since this would be incorrect: +# bar((var b = 3; const c = 1; echo "hey"; c)) +# echo b # <- b wouldn't exist + +discard not (let xx = 1; true) +discard xx + +template barrel(a: typed): untyped = a + +barrel: + var aa* = 1 + var bb = 3 + export bb + +# Test declaredInScope within params +template test1: untyped = + when not declaredInScope(thing): + var thing {.inject.}: int + +proc chunkedReadLoop = + test1 + test1 + +template test2: untyped = + when not not not declaredInScope(thing): + var thing {.inject.}: int + +proc chunkedReadLoop2 = + test2 + test2 + +test1(); test2() diff --git a/tests/template/tparamscope.nim b/tests/template/tparamscope.nim new file mode 100644 index 000000000..177c682cf --- /dev/null +++ b/tests/template/tparamscope.nim @@ -0,0 +1,10 @@ +discard """ + errormsg: "undeclared identifier: 'a'" + line: 10 +""" + + +template secondArg(a, b: typed): untyped = + b + +echo secondArg((var a = 1; 1), a) From f8c48fc1863a243718acf86b699baed1a5c1512e Mon Sep 17 00:00:00 2001 From: cooldome Date: Fri, 28 Aug 2020 16:57:52 +0100 Subject: [PATCH 0560/3768] fix #15035 (#15236) --- compiler/injectdestructors.nim | 5 +++-- tests/arc/t14383.nim | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index bac47b3d6..fcaa3c76a 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -699,8 +699,9 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode = elif n.kind in {nkObjDownConv, nkObjUpConv}: result = copyTree(n) result[0] = p(n[0], c, s, sinkArg) - elif n.typ == nil: - # 'raise X' can be part of a 'case' expression. Deal with it here: + elif n.typ == nil or n.typ.skipTypes({tyGenericInst, tyAlias, tySink, tyVar}).kind == tyOpenArray: + # 'raise X' can be part of a 'case' expression. Deal with it here. + # openarrays require perfect forwarding result = p(n, c, s, normal) else: # copy objects that are not temporary but passed to a 'sink' parameter diff --git a/tests/arc/t14383.nim b/tests/arc/t14383.nim index 115cbf426..742088937 100644 --- a/tests/arc/t14383.nim +++ b/tests/arc/t14383.nim @@ -3,6 +3,7 @@ discard """ output: ''' hello hello +@[4, 3, 2, 1] ''' """ @@ -11,3 +12,18 @@ import dmodule var val = parseMinValue() if val.kind == minDictionary: echo val + + +#------------------------------------------------------------------------------ +# Issue #15035 +#------------------------------------------------------------------------------ + +proc countRun(lst: sink openArray[int]): int = + discard + +proc timSort(lst: var openArray[int]) = + let run = countRun(lst) + +var a = @[4, 3, 2, 1] +timSort(a) +echo a \ No newline at end of file From 13e659cfec83eb3c2c3c2bbbf10d01ba59bc0d5b Mon Sep 17 00:00:00 2001 From: Clyybber Date: Fri, 28 Aug 2020 22:18:09 +0200 Subject: [PATCH 0561/3768] Big compiler Cleanup (#14777) --- compiler/ast.nim | 34 ++- compiler/btrees.nim | 3 +- compiler/ccgexprs.nim | 2 +- compiler/ccgmerge.nim | 6 +- compiler/ccgstmts.nim | 2 +- compiler/cgen.nim | 2 +- compiler/closureiters.nim | 3 - compiler/commands.nim | 21 +- compiler/condsyms.nim | 2 +- compiler/docgen.nim | 45 ++-- compiler/extccomp.nim | 4 +- compiler/filter_tmpl.nim | 8 +- compiler/forloops.nim | 89 -------- compiler/injectdestructors.nim | 6 +- compiler/int128.nim | 6 +- compiler/layouter.nim | 6 +- compiler/lexer.nim | 157 ++++++-------- compiler/liftdestructors.nim | 5 - compiler/linter.nim | 16 +- compiler/main.nim | 4 +- compiler/modulepaths.nim | 6 +- compiler/modules.nim | 2 +- compiler/msgs.nim | 9 +- compiler/nimconf.nim | 34 +-- compiler/nimlexbase.nim | 10 +- compiler/options.nim | 6 +- compiler/packagehandling.nim | 2 - compiler/parampatterns.nim | 4 +- compiler/parser.nim | 369 ++++++++++++++++----------------- compiler/passes.nim | 6 +- compiler/pragmas.nim | 6 +- compiler/renderer.nim | 28 +-- compiler/renderverbatim.nim | 2 +- compiler/reorder.nim | 27 +-- compiler/rodutils.nim | 6 +- compiler/semexprs.nim | 2 +- compiler/semfold.nim | 2 +- compiler/seminst.nim | 2 +- compiler/semobjconstr.nim | 3 +- compiler/semtempl.nim | 1 - compiler/semtypes.nim | 6 +- compiler/semtypinst.nim | 3 +- compiler/sighashes.nim | 2 +- compiler/sigmatch.nim | 51 ++--- compiler/sourcemap.nim | 8 +- compiler/suggest.nim | 2 +- compiler/syntaxes.nim | 128 ++++-------- compiler/types.nim | 2 +- compiler/vmgen.nim | 21 +- compiler/wordrecg.nim | 74 ++----- nimpretty/nimpretty.nim | 12 +- tools/grammar_nanny.nim | 4 +- tools/nimfind.nim | 3 +- 53 files changed, 481 insertions(+), 783 deletions(-) delete mode 100644 compiler/forloops.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index 06ff92e9f..23b564af3 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -17,22 +17,21 @@ export int128 type TCallingConvention* = enum - ccNimCall, # nimcall, also the default - ccStdCall, # procedure is stdcall - ccCDecl, # cdecl - ccSafeCall, # safecall - ccSysCall, # system call - ccInline, # proc should be inlined - ccNoInline, # proc should not be inlined - ccFastCall, # fastcall (pass parameters in registers) - ccThisCall, # thiscall (parameters are pushed right-to-left) - ccClosure, # proc has a closure + ccNimCall # nimcall, also the default + ccStdCall # procedure is stdcall + ccCDecl # cdecl + ccSafeCall # safecall + ccSysCall # system call + ccInline # proc should be inlined + ccNoInline # proc should not be inlined + ccFastCall # fastcall (pass parameters in registers) + ccThisCall # thiscall (parameters are pushed right-to-left) + ccClosure # proc has a closure ccNoConvention # needed for generating proper C procs sometimes -const - CallingConvToStr*: array[TCallingConvention, string] = ["nimcall", "stdcall", - "cdecl", "safecall", "syscall", "inline", "noinline", "fastcall", "thiscall", - "closure", "noconv"] +const CallingConvToStr*: array[TCallingConvention, string] = ["nimcall", "stdcall", + "cdecl", "safecall", "syscall", "inline", "noinline", "fastcall", "thiscall", + "closure", "noconv"] type TNodeKind* = enum # order is extremely important, because ranges are used @@ -1363,7 +1362,7 @@ proc newType*(kind: TTypeKind, owner: PSym): PType = proc mergeLoc(a: var TLoc, b: TLoc) = if a.k == low(a.k): a.k = b.k if a.storage == low(a.storage): a.storage = b.storage - a.flags = a.flags + b.flags + a.flags.incl b.flags if a.lode == nil: a.lode = b.lode if a.r == nil: a.r = b.r @@ -1388,7 +1387,7 @@ proc assignType*(dest, src: PType) = # this fixes 'type TLock = TSysLock': if src.sym != nil: if dest.sym != nil: - dest.sym.flags = dest.sym.flags + (src.sym.flags-{sfExported}) + dest.sym.flags.incl src.sym.flags-{sfExported} if dest.sym.annex == nil: dest.sym.annex = src.sym.annex mergeLoc(dest.sym.loc, src.sym.loc) else: @@ -1495,8 +1494,7 @@ proc isGCedMem*(t: PType): bool {.inline.} = t.kind == tyProc and t.callConv == ccClosure proc propagateToOwner*(owner, elem: PType; propagateHasAsgn = true) = - const HaveTheirOwnEmpty = {tySequence, tySet, tyPtr, tyRef, tyProc} - owner.flags = owner.flags + (elem.flags * {tfHasMeta, tfTriggersCompileTime}) + owner.flags.incl elem.flags * {tfHasMeta, tfTriggersCompileTime} if tfNotNil in elem.flags: if owner.kind in {tyGenericInst, tyGenericBody, tyGenericInvocation}: owner.flags.incl tfNotNil diff --git a/compiler/btrees.nim b/compiler/btrees.nim index 18854d474..473cbd1b0 100644 --- a/compiler/btrees.nim +++ b/compiler/btrees.nim @@ -135,8 +135,7 @@ proc `$`[Key, Val](b: BTree[Key, Val]): string = result = "" toString(b.root, "", result) -proc hasNext*[Key, Val](b: BTree[Key, Val]; index: int): bool = - result = index < b.entries +proc hasNext*[Key, Val](b: BTree[Key, Val]; index: int): bool = index < b.entries proc countSubTree[Key, Val](it: Node[Key, Val]): int = if it.isInternal: diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index a97d15d70..995b75d69 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1653,7 +1653,7 @@ template genDollar(p: BProc, n: PNode, d: var TLoc, frmt: string) = var a: TLoc initLocExpr(p, n[1], a) a.r = ropecg(p.module, frmt, [rdLoc(a)]) - a.flags = a.flags - {lfIndirect} # this flag should not be propagated here (not just for HCR) + a.flags.excl lfIndirect # this flag should not be propagated here (not just for HCR) if d.k == locNone: getTemp(p, n.typ, d) genAssignment(p, d, a, {}) gcUsage(p.config, n) diff --git a/compiler/ccgmerge.nim b/compiler/ccgmerge.nim index c12d6c0b2..a87d5b609 100644 --- a/compiler/ccgmerge.nim +++ b/compiler/ccgmerge.nim @@ -282,7 +282,7 @@ proc mergeRequired*(m: BModule): bool = if m.s[i] != nil: #echo "not empty: ", i, " ", m.s[i] return true - for i in low(TCProcSection)..high(TCProcSection): + for i in TCProcSection: if m.initProc.s(i) != nil: #echo "not empty: ", i, " ", m.initProc.s[i] return true @@ -292,7 +292,7 @@ proc mergeFiles*(cfilename: AbsoluteFile, m: BModule) = var old: TMergeSections readMergeSections(cfilename, old) # do the merge; old section before new section: - for i in low(TCFileSection)..high(TCFileSection): + for i in TCFileSection: m.s[i] = old.f[i] & m.s[i] - for i in low(TCProcSection)..high(TCProcSection): + for i in TCProcSection: m.initProc.s(i) = old.p[i] & m.initProc.s(i) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 0f58df3a4..e40e94d01 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -1498,7 +1498,7 @@ proc genPragma(p: BProc, n: PNode) = of wEmit: genEmit(p, it) of wInjectStmt: var p = newProc(nil, p.module) - p.options = p.options - {optLineTrace, optStackTrace} + p.options.excl {optLineTrace, optStackTrace} genStmts(p, it[1]) p.module.injectStmt = p.s(cpsStmts) else: discard diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 86860fbf4..d40aa32d7 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -681,7 +681,7 @@ proc loadDynamicLib(m: BModule, lib: PLib) = [loadlib, genStringLiteral(m, lib.path)]) else: var p = newProc(nil, m) - p.options = p.options - {optStackTrace} + p.options.excl optStackTrace p.flags.incl nimErrorFlagDisabled var dest: TLoc initLoc(dest, locTemp, lib.path, OnStack) diff --git a/compiler/closureiters.nim b/compiler/closureiters.nim index 2248f956e..912172e62 100644 --- a/compiler/closureiters.nim +++ b/compiler/closureiters.nim @@ -485,7 +485,6 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode = if ns: needsSplit = true var tmp: PSym - var s: PNode let isExpr = not isEmptyType(n.typ) if isExpr: tmp = ctx.newTempVar(n.typ) @@ -742,8 +741,6 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode = result.add(n) of nkWhileStmt: - var ns = false - var condNeedsSplit = false n[0] = ctx.lowerStmtListExprs(n[0], condNeedsSplit) var bodyNeedsSplit = false diff --git a/compiler/commands.nim b/compiler/commands.nim index b67cd461f..0af31b2c7 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -53,11 +53,10 @@ const "Copyright (c) 2006-" & copyrightYear & " by Andreas Rumpf\n" proc genFeatureDesc[T: enum](t: typedesc[T]): string {.compileTime.} = - var x = "" - for f in low(T)..high(T): - if x.len > 0: x.add "|" - x.add $f - x + result = "" + for f in T: + if result.len > 0: result.add "|" + result.add $f const Usage = slurp"../doc/basicopt.txt".replace(" //", " ") @@ -146,24 +145,24 @@ proc splitSwitch(conf: ConfigRef; switch: string, cmd, arg: var string, pass: TC proc processOnOffSwitch(conf: ConfigRef; op: TOptions, arg: string, pass: TCmdLinePass, info: TLineInfo) = case arg.normalize - of "","on": conf.options = conf.options + op - of "off": conf.options = conf.options - op + of "","on": conf.options.incl op + of "off": conf.options.excl op else: localError(conf, info, errOnOrOffExpectedButXFound % arg) proc processOnOffSwitchOrList(conf: ConfigRef; op: TOptions, arg: string, pass: TCmdLinePass, info: TLineInfo): bool = result = false case arg.normalize - of "on": conf.options = conf.options + op - of "off": conf.options = conf.options - op + of "on": conf.options.incl op + of "off": conf.options.excl op of "list": result = true else: localError(conf, info, errOnOffOrListExpectedButXFound % arg) proc processOnOffSwitchG(conf: ConfigRef; op: TGlobalOptions, arg: string, pass: TCmdLinePass, info: TLineInfo) = case arg.normalize - of "", "on": conf.globalOptions = conf.globalOptions + op - of "off": conf.globalOptions = conf.globalOptions - op + of "", "on": conf.globalOptions.incl op + of "off": conf.globalOptions.excl op else: localError(conf, info, errOnOrOffExpectedButXFound % arg) proc expectArg(conf: ConfigRef; switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index ad13f3e1e..d99b8b7e6 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -86,7 +86,7 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimMacrosSizealignof") defineSymbol("nimNoZeroExtendMagic") defineSymbol("nimMacrosGetNodeId") - for f in low(Feature)..high(Feature): + for f in Feature: defineSymbol("nimHas" & $f) for s in WarningsToStr: diff --git a/compiler/docgen.nim b/compiler/docgen.nim index cb8c77e87..1e0756d64 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -302,15 +302,13 @@ proc ropeFormatNamedVars(conf: ConfigRef; frmt: FormatStr, proc genComment(d: PDoc, n: PNode): string = result = "" - var dummyHasToc: bool if n.comment.len > 0: - var comment2 = n.comment + let comment = n.comment when false: # RFC: to preseve newlines in comments, this would work: - comment2 = comment2.replace("\n", "\n\n") - renderRstToOut(d[], parseRst(comment2, toFullPath(d.conf, n.info), - toLinenumber(n.info), toColumn(n.info), - dummyHasToc, d.options, d.conf), result) + comment = comment.replace("\n", "\n\n") + renderRstToOut(d[], parseRst(comment, toFullPath(d.conf, n.info), toLinenumber(n.info), + toColumn(n.info), (var dummy: bool; dummy), d.options, d.conf), result) proc genRecCommentAux(d: PDoc, n: PNode): Rope = if n == nil: return nil @@ -342,11 +340,10 @@ proc getPlainDocstring(n: PNode): string = ## You need to call this before genRecComment, whose side effects are removal ## of comments from the tree. The proc will recursively scan and return all ## the concatenated ``##`` comments of the node. - result = "" - if n == nil: return - if startsWith(n.comment, "##"): + if n == nil: result = "" + elif startsWith(n.comment, "##"): result = n.comment - if result.len < 1: + else: for i in 0.. 0: return @@ -458,7 +455,6 @@ proc writeExample(d: PDoc; ex: PNode, rdoccmd: string) = d.exampleGroups[rdoccmd].code.add "import r\"$1\"\n" % outp.string proc runAllExamples(d: PDoc) = - let backend = d.conf.backend # This used to be: `let backend = if isDefined(d.conf, "js"): "js"` (etc), however # using `-d:js` (etc) cannot work properly, eg would fail with `importjs` # since semantics are affected by `config.backend`, not by isDefined(d.conf, "js") @@ -522,20 +518,6 @@ proc prepareExample(d: PDoc; n: PNode): tuple[rdoccmd: string, code: string] = for imp in imports: runnableExamples.add imp runnableExamples.add newTree(nkBlockStmt, newNode(nkEmpty), copyTree savedLastSon) -proc renderNimCodeOld(d: PDoc, n: PNode, dest: var Rope) = - ## this is a rather hacky way to get rid of the initial indentation - ## that the renderer currently produces: - # deadcode - var i = 0 - var body = n.lastSon - if body.len == 1 and body.kind == nkStmtList and - body.lastSon.kind == nkStmtList: - body = body.lastSon - for b in body: - if i > 0: dest.add "\n" - inc i - nodeToHighlightedHtml(d, b, dest, {renderRunnableExamples}, nil) - type RunnableState = enum rsStart rsComment @@ -575,12 +557,9 @@ proc getAllRunnableExamplesImpl(d: PDoc; n: PNode, dest: var Rope, state: Runnab inc d.listingCounter let id = $d.listingCounter dest.add(d.config.getOrDefault"doc.listing_start" % [id, "langNim"]) - when true: - var dest2 = "" - renderNimCode(dest2, code, isLatex = d.conf.cmd == cmdRst2tex) - dest.add dest2 - else: - renderNimCodeOld(d, n, dest) + var dest2 = "" + renderNimCode(dest2, code, isLatex = d.conf.cmd == cmdRst2tex) + dest.add dest2 dest.add(d.config.getOrDefault"doc.listing_end" % id) return rsRunnable else: discard @@ -1213,12 +1192,12 @@ proc genOutFile(d: PDoc): Rope = var tmp = "" renderTocEntries(d[], j, 1, tmp) var toc = tmp.rope - for i in low(TSymKind)..high(TSymKind): + for i in TSymKind: genSection(d, i) toc.add(d.toc[i]) if toc != nil: toc = ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.toc"), ["content"], [toc]) - for i in low(TSymKind)..high(TSymKind): code.add(d.section[i]) + for i in TSymKind: code.add(d.section[i]) # Extract the title. Non API modules generate an entry in the index table. if d.meta[metaTitle].len != 0: diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 1c6b1a8d0..79b4ba637 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -331,7 +331,7 @@ proc setCC*(conf: ConfigRef; ccname: string; info: TLineInfo) = conf.compileOptions = getConfigVar(conf, conf.cCompiler, ".options.always") conf.linkOptions = "" conf.cCompilerPath = getConfigVar(conf, conf.cCompiler, ".path") - for i in low(CC)..high(CC): undefSymbol(conf.symbols, CC[i].name) + for c in CC: undefSymbol(conf.symbols, c.name) defineSymbol(conf.symbols, CC[conf.cCompiler].name) proc addOpt(dest: var string, src: string) = @@ -353,7 +353,7 @@ proc addCompileOptionCmd*(conf: ConfigRef; option: string) = proc initVars*(conf: ConfigRef) = # we need to define the symbol here, because ``CC`` may have never been set! - for i in low(CC)..high(CC): undefSymbol(conf.symbols, CC[i].name) + for c in CC: undefSymbol(conf.symbols, c.name) defineSymbol(conf.symbols, CC[conf.cCompiler].name) addCompileOption(conf, getConfigVar(conf, conf.cCompiler, ".options.always")) #addLinkOption(getConfigVar(cCompiler, ".options.linker")) diff --git a/compiler/filter_tmpl.nim b/compiler/filter_tmpl.nim index 1d95369a8..6165ff2f3 100644 --- a/compiler/filter_tmpl.nim +++ b/compiler/filter_tmpl.nim @@ -199,8 +199,8 @@ proc parseLine(p: var TTmplParser) = inc(j) llStreamWrite(p.outp, "\\n\"") -proc filterTmpl*(stdin: PLLStream, filename: AbsoluteFile, - call: PNode; conf: ConfigRef): PLLStream = +proc filterTmpl*(conf: ConfigRef, stdin: PLLStream, filename: AbsoluteFile, + call: PNode): PLLStream = var p: TTmplParser p.config = conf p.info = newLineInfo(conf, filename, 0, 0) @@ -214,9 +214,9 @@ proc filterTmpl*(stdin: PLLStream, filename: AbsoluteFile, p.x = newStringOfCap(120) # do not process the first line which contains the directive: if llStreamReadLine(p.inp, p.x): - p.info.line = p.info.line + 1'u16 + inc p.info.line while llStreamReadLine(p.inp, p.x): - p.info.line = p.info.line + 1'u16 + inc p.info.line parseLine(p) newLine(p) result = p.outp diff --git a/compiler/forloops.nim b/compiler/forloops.nim deleted file mode 100644 index ee91000d4..000000000 --- a/compiler/forloops.nim +++ /dev/null @@ -1,89 +0,0 @@ -# -# -# The Nim Compiler -# (c) Copyright 2015 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## This module implements for loop detection for better C code generation. - -import ast, astalgo - -const - someCmp = {mEqI, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc, - mLeI, mLeF64, mLeU, mLeEnum, - mLeCh, mLeB, mLePtr, mLtI, mLtF64, mLtU, mLtEnum, - mLtCh, mLtB, mLtPtr} - -proc isCounter(s: PSym): bool {.inline.} = - s.kind in {skResult, skVar, skLet, skTemp} and - {sfGlobal, sfAddrTaken} * s.flags == {} - -proc isCall(n: PNode): bool {.inline.} = - n.kind in nkCallKinds and n[0].kind == nkSym - -proc fromSystem(op: PSym): bool = sfSystemModule in getModule(op).flags - -proc getCounter(lastStmt: PNode): PSym = - if lastStmt.isCall: - let op = lastStmt.sym - if op.magic in {mDec, mInc} or - ((op.name.s == "+=" or op.name.s == "-=") and op.fromSystem): - if op[1].kind == nkSym and isCounter(op[1].sym): - result = op[1].sym - -proc counterInTree(n, loop: PNode; counter: PSym): bool = - # prune the search tree: within the loop the counter may be used: - if n == loop: return - case n.kind - of nkSym: - if n.sym == counter: return true - of nkVarSection, nkLetSection: - # definitions are fine! - for it in n: - if counterInTree(it.lastSon): return true - else: - for i in 0..= denominator: - dividend = dividend - denominator + dividend -= denominator quotient = bitor(quotient, One) denominator = denominator shr 1 diff --git a/compiler/layouter.nim b/compiler/layouter.nim index f5d3084dc..ee45d357f 100644 --- a/compiler/layouter.nim +++ b/compiler/layouter.nim @@ -35,7 +35,7 @@ type Emitter* = object config: ConfigRef fid: FileIndex - lastTok: TTokType + lastTok: TokType inquote, lastTokWasTerse: bool semicolons: SemicolonKind col, lastLineNumber, lineSpan, indentLevel, indWidth*, inSection: int @@ -402,7 +402,7 @@ proc endsInAlpha(em: Emitter): bool = while i >= 0 and em.kinds[i] in {ltBeginSection, ltEndSection}: dec(i) result = if i >= 0: em.tokens[i].lastChar in SymChars+{'_'} else: false -proc emitComment(em: var Emitter; tok: TToken; dontIndent: bool) = +proc emitComment(em: var Emitter; tok: Token; dontIndent: bool) = var col = em.col let lit = strip fileSection(em.config, em.fid, tok.commentOffsetA, tok.commentOffsetB) em.lineSpan = countNewlines(lit) @@ -417,7 +417,7 @@ proc emitComment(em: var Emitter; tok: TToken; dontIndent: bool) = inc col emitMultilineComment(em, lit, col, dontIndent) -proc emitTok*(em: var Emitter; L: TLexer; tok: TToken) = +proc emitTok*(em: var Emitter; L: Lexer; tok: Token) = template wasExportMarker(em): bool = em.kinds.len > 0 and em.kinds[^1] == ltExportMarker diff --git a/compiler/lexer.nim b/compiler/lexer.nim index f9679adee..ff433928c 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -30,7 +30,7 @@ const # don't forget to update the 'highlite' module if these charsets should change type - TTokType* = enum + TokType* = enum tkInvalid, tkEof, # order is important here! tkSymbol, # keywords: tkAddr, tkAnd, tkAs, tkAsm, @@ -64,7 +64,7 @@ type tkOpr, tkComment, tkAccent, tkSpaces, tkInfixOpr, tkPrefixOpr, tkPostfixOpr - TTokTypes* = set[TTokType] + TokTypes* = set[TokType] const weakTokens = {tkComma, tkSemiColon, tkColon, @@ -73,7 +73,7 @@ const # tokens that should not be considered for previousToken tokKeywordLow* = succ(tkSymbol) tokKeywordHigh* = pred(tkIntLit) - TokTypeToStr*: array[TTokType, string] = ["tkInvalid", "[EOF]", + TokTypeToStr*: array[TokType, string] = ["tkInvalid", "[EOF]", "tkSymbol", "addr", "and", "as", "asm", "bind", "block", "break", "case", "cast", @@ -104,22 +104,19 @@ const "tkPrefixOpr", "tkPostfixOpr"] type - TNumericalBase* = enum + NumericalBase* = enum base10, # base10 is listed as the first element, # so that it is the correct default value base2, base8, base16 - CursorPosition* {.pure.} = enum ## XXX remove this again - None, InToken, BeforeToken, AfterToken - - TToken* = object # a Nim token - tokType*: TTokType # the type of the token + Token* = object # a Nim token + tokType*: TokType # the type of the token indent*: int # the indentation; != -1 if the token has been # preceded with indentation ident*: PIdent # the parsed identifier iNumber*: BiggestInt # the parsed integer literal fNumber*: BiggestFloat # the parsed floating point literal - base*: TNumericalBase # the numerical base; only valid for int + base*: NumericalBase # the numerical base; only valid for int # or float literals strongSpaceA*: int8 # leading spaces of an operator strongSpaceB*: int8 # trailing spaces of an operator @@ -127,26 +124,25 @@ type # documentation comments are here too line*, col*: int when defined(nimpretty): - offsetA*, offsetB*: int # used for pretty printing so that literals - # like 0b01 or r"\L" are unaffected + offsetA*, offsetB*: int # used for pretty printing so that literals + # like 0b01 or r"\L" are unaffected commentOffsetA*, commentOffsetB*: int - TErrorHandler* = proc (conf: ConfigRef; info: TLineInfo; msg: TMsgKind; arg: string) - TLexer* = object of TBaseLexer + ErrorHandler* = proc (conf: ConfigRef; info: TLineInfo; msg: TMsgKind; arg: string) + Lexer* = object of TBaseLexer fileIdx*: FileIndex indentAhead*: int # if > 0 an indentation has already been read # this is needed because scanning comments # needs so much look-ahead currLineIndent*: int strongSpaces*, allowTabs*: bool - cursor*: CursorPosition - errorHandler*: TErrorHandler + errorHandler*: ErrorHandler cache*: IdentCache when defined(nimsuggest): previousToken: TLineInfo config*: ConfigRef -proc getLineInfo*(L: TLexer, tok: TToken): TLineInfo {.inline.} = +proc getLineInfo*(L: Lexer, tok: Token): TLineInfo {.inline.} = result = newLineInfo(L.fileIdx, tok.line, tok.col) when defined(nimpretty): result.offsetA = tok.offsetA @@ -154,8 +150,8 @@ proc getLineInfo*(L: TLexer, tok: TToken): TLineInfo {.inline.} = result.commentOffsetA = tok.commentOffsetA result.commentOffsetB = tok.commentOffsetB -proc isKeyword*(kind: TTokType): bool = - result = (kind >= tokKeywordLow) and (kind <= tokKeywordHigh) +proc isKeyword*(kind: TokType): bool = + (kind >= tokKeywordLow) and (kind <= tokKeywordHigh) template ones(n): untyped = ((1 shl n)-1) # for utf-8 conversion @@ -169,28 +165,27 @@ proc isNimIdentifier*(s: string): bool = inc(i) result = true -proc `$`*(tok: TToken): string = +proc `$`*(tok: Token): string = case tok.tokType - of tkIntLit..tkInt64Lit: result = $tok.iNumber - of tkFloatLit..tkFloat64Lit: result = $tok.fNumber - of tkInvalid, tkStrLit..tkCharLit, tkComment: result = tok.literal - of tkParLe..tkColon, tkEof, tkAccent: - result = TokTypeToStr[tok.tokType] + of tkIntLit..tkInt64Lit: $tok.iNumber + of tkFloatLit..tkFloat64Lit: $tok.fNumber + of tkInvalid, tkStrLit..tkCharLit, tkComment: tok.literal + of tkParLe..tkColon, tkEof, tkAccent: TokTypeToStr[tok.tokType] else: if tok.ident != nil: - result = tok.ident.s + tok.ident.s else: - result = "" + "" -proc prettyTok*(tok: TToken): string = - if isKeyword(tok.tokType): result = "keyword " & tok.ident.s - else: result = $tok +proc prettyTok*(tok: Token): string = + if isKeyword(tok.tokType): "keyword " & tok.ident.s + else: $tok -proc printTok*(conf: ConfigRef; tok: TToken) = +proc printTok*(conf: ConfigRef; tok: Token) = msgWriteln(conf, $tok.line & ":" & $tok.col & "\t" & TokTypeToStr[tok.tokType] & " " & $tok) -proc initToken*(L: var TToken) = +proc initToken*(L: var Token) = L.tokType = tkInvalid L.iNumber = 0 L.indent = 0 @@ -203,7 +198,7 @@ proc initToken*(L: var TToken) = L.commentOffsetA = 0 L.commentOffsetB = 0 -proc fillToken(L: var TToken) = +proc fillToken(L: var Token) = L.tokType = tkInvalid L.iNumber = 0 L.indent = 0 @@ -216,7 +211,7 @@ proc fillToken(L: var TToken) = L.commentOffsetA = 0 L.commentOffsetB = 0 -proc openLexer*(lex: var TLexer, fileIdx: FileIndex, inputstream: PLLStream; +proc openLexer*(lex: var Lexer, fileIdx: FileIndex, inputstream: PLLStream; cache: IdentCache; config: ConfigRef) = openBaseLexer(lex, inputstream) lex.fileIdx = fileIdx @@ -228,36 +223,36 @@ proc openLexer*(lex: var TLexer, fileIdx: FileIndex, inputstream: PLLStream; lex.previousToken.fileIndex = fileIdx lex.config = config -proc openLexer*(lex: var TLexer, filename: AbsoluteFile, inputstream: PLLStream; +proc openLexer*(lex: var Lexer, filename: AbsoluteFile, inputstream: PLLStream; cache: IdentCache; config: ConfigRef) = openLexer(lex, fileInfoIdx(config, filename), inputstream, cache, config) -proc closeLexer*(lex: var TLexer) = +proc closeLexer*(lex: var Lexer) = if lex.config != nil: inc(lex.config.linesCompiled, lex.lineNumber) closeBaseLexer(lex) -proc getLineInfo(L: TLexer): TLineInfo = +proc getLineInfo(L: Lexer): TLineInfo = result = newLineInfo(L.fileIdx, L.lineNumber, getColNumber(L, L.bufpos)) -proc dispMessage(L: TLexer; info: TLineInfo; msg: TMsgKind; arg: string) = +proc dispMessage(L: Lexer; info: TLineInfo; msg: TMsgKind; arg: string) = if L.errorHandler.isNil: msgs.message(L.config, info, msg, arg) else: L.errorHandler(L.config, info, msg, arg) -proc lexMessage*(L: TLexer, msg: TMsgKind, arg = "") = +proc lexMessage*(L: Lexer, msg: TMsgKind, arg = "") = L.dispMessage(getLineInfo(L), msg, arg) -proc lexMessageTok*(L: TLexer, msg: TMsgKind, tok: TToken, arg = "") = +proc lexMessageTok*(L: Lexer, msg: TMsgKind, tok: Token, arg = "") = var info = newLineInfo(L.fileIdx, tok.line, tok.col) L.dispMessage(info, msg, arg) -proc lexMessagePos(L: var TLexer, msg: TMsgKind, pos: int, arg = "") = +proc lexMessagePos(L: var Lexer, msg: TMsgKind, pos: int, arg = "") = var info = newLineInfo(L.fileIdx, L.lineNumber, pos - L.lineStart) L.dispMessage(info, msg, arg) -proc matchTwoChars(L: TLexer, first: char, second: set[char]): bool = +proc matchTwoChars(L: Lexer, first: char, second: set[char]): bool = result = (L.buf[L.bufpos] == first) and (L.buf[L.bufpos + 1] in second) template tokenBegin(tok, pos) {.dirty.} = @@ -271,7 +266,6 @@ template tokenEnd(tok, pos) {.dirty.} = let colB = getColNumber(L, pos)+1 if L.fileIdx == L.config.m.trackPos.fileIndex and L.config.m.trackPos.col in colA..colB and L.lineNumber == L.config.m.trackPos.line.int and L.config.ideCmd in {ideSug, ideCon}: - L.cursor = CursorPosition.InToken L.config.m.trackPos.col = colA.int16 colA = 0 when defined(nimpretty): @@ -296,23 +290,22 @@ template tokenEndPrevious(tok, pos) = let colB = getColNumber(L, pos) if L.fileIdx == L.config.m.trackPos.fileIndex and L.config.m.trackPos.col in colA..colB and L.lineNumber == L.config.m.trackPos.line.int and L.config.ideCmd in {ideSug, ideCon}: - L.cursor = CursorPosition.BeforeToken L.config.m.trackPos = L.previousToken L.config.m.trackPosAttached = true colA = 0 when defined(nimpretty): tok.offsetB = L.offsetBase + pos -template eatChar(L: var TLexer, t: var TToken, replacementChar: char) = +template eatChar(L: var Lexer, t: var Token, replacementChar: char) = t.literal.add(replacementChar) inc(L.bufpos) -template eatChar(L: var TLexer, t: var TToken) = +template eatChar(L: var Lexer, t: var Token) = t.literal.add(L.buf[L.bufpos]) inc(L.bufpos) -proc getNumber(L: var TLexer, result: var TToken) = - proc matchUnderscoreChars(L: var TLexer, tok: var TToken, chars: set[char]): Natural = +proc getNumber(L: var Lexer, result: var Token) = + proc matchUnderscoreChars(L: var Lexer, tok: var Token, chars: set[char]): Natural = var pos = L.bufpos # use registers for pos, buf result = 0 while true: @@ -332,19 +325,19 @@ proc getNumber(L: var TLexer, result: var TToken) = inc(pos) L.bufpos = pos - proc matchChars(L: var TLexer, tok: var TToken, chars: set[char]) = + proc matchChars(L: var Lexer, tok: var Token, chars: set[char]) = var pos = L.bufpos # use registers for pos, buf while L.buf[pos] in chars: tok.literal.add(L.buf[pos]) inc(pos) L.bufpos = pos - proc lexMessageLitNum(L: var TLexer, msg: string, startpos: int, msgKind = errGenerated) = + proc lexMessageLitNum(L: var Lexer, msg: string, startpos: int, msgKind = errGenerated) = # Used to get slightly human friendlier err messages. const literalishChars = {'A'..'F', 'a'..'f', '0'..'9', 'X', 'x', 'o', 'O', 'c', 'C', 'b', 'B', '_', '.', '\'', 'd', 'i', 'u'} var msgPos = L.bufpos - var t: TToken + var t: Token t.literal = "" L.bufpos = startpos # Use L.bufpos as pos because of matchChars matchChars(L, t, literalishChars) @@ -612,7 +605,7 @@ proc getNumber(L: var TLexer, result: var TToken) = tokenEnd(result, postPos-1) L.bufpos = postPos -proc handleHexChar(L: var TLexer, xi: var int; position: range[0..4]) = +proc handleHexChar(L: var Lexer, xi: var int; position: range[0..4]) = template invalid() = lexMessage(L, errGenerated, "expected a hex digit, but found: " & L.buf[L.bufpos] & @@ -637,7 +630,7 @@ proc handleHexChar(L: var TLexer, xi: var int; position: range[0..4]) = # Need to progress for `nim check` inc(L.bufpos) -proc handleDecChars(L: var TLexer, xi: var int) = +proc handleDecChars(L: var Lexer, xi: var int) = while L.buf[L.bufpos] in {'0'..'9'}: xi = (xi * 10) + (ord(L.buf[L.bufpos]) - ord('0')) inc(L.bufpos) @@ -680,7 +673,7 @@ proc addUnicodeCodePoint(s: var string, i: int) = s[pos+4] = chr(i shr 6 and ones(6) or 0b10_0000_00) s[pos+5] = chr(i and ones(6) or 0b10_0000_00) -proc getEscapedChar(L: var TLexer, tok: var TToken) = +proc getEscapedChar(L: var Lexer, tok: var Token) = inc(L.bufpos) # skip '\' case L.buf[L.bufpos] of 'n', 'N': @@ -760,13 +753,7 @@ proc getEscapedChar(L: var TLexer, tok: var TToken) = else: lexMessage(L, errGenerated, "invalid character constant") else: lexMessage(L, errGenerated, "invalid character constant") -proc newString(s: cstring, len: int): string = - ## XXX, how come there is no support for this? - result = newString(len) - for i in 0.. ord(tokKeywordHigh) - ord(tkSymbol)): tok.tokType = tkSymbol else: - tok.tokType = TTokType(tok.ident.id + ord(tkSymbol)) + tok.tokType = TokType(tok.ident.id + ord(tkSymbol)) if suspicious and {optStyleHint, optStyleError} * L.config.globalOptions != {}: lintReport(L.config, getLineInfo(L), tok.ident.s.normalize, tok.ident.s) L.bufpos = pos -proc endOperator(L: var TLexer, tok: var TToken, pos: int, +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) if (tok.ident.id < oprLow) or (tok.ident.id > oprHigh): tok.tokType = tkOpr - else: tok.tokType = TTokType(tok.ident.id - oprLow + ord(tkColon)) + else: tok.tokType = TokType(tok.ident.id - oprLow + ord(tkColon)) L.bufpos = pos -proc getOperator(L: var TLexer, tok: var TToken) = +proc getOperator(L: var Lexer, tok: var Token) = var pos = L.bufpos tokenBegin(tok, pos) var h: Hash = 0 @@ -935,18 +922,15 @@ proc getOperator(L: var TLexer, tok: var TToken) = if L.buf[pos] in {CR, LF, nimlexbase.EndOfFile}: tok.strongSpaceB = -1 -proc getPrecedence*(tok: TToken, strongSpaces: bool): int = +proc getPrecedence*(tok: Token): int = ## Calculates the precedence of the given token. - template considerStrongSpaces(x): untyped = - x + (if strongSpaces: 100 - tok.strongSpaceA.int*10 else: 0) - case tok.tokType of tkOpr: let relevantChar = tok.ident.s[0] # arrow like? if tok.ident.s.len > 1 and tok.ident.s[^1] == '>' and - tok.ident.s[^2] in {'-', '~', '='}: return considerStrongSpaces(1) + tok.ident.s[^2] in {'-', '~', '='}: return 1 template considerAsgn(value: untyped) = result = if tok.ident.s[^1] == '=': 1 else: value @@ -962,15 +946,13 @@ proc getPrecedence*(tok: TToken, strongSpaces: bool): int = of '?': result = 2 else: considerAsgn(2) of tkDiv, tkMod, tkShl, tkShr: result = 9 - of tkIn, tkNotin, tkIs, tkIsnot, tkOf, tkAs, tkFrom: result = 5 of tkDotDot: result = 6 + of tkIn, tkNotin, tkIs, tkIsnot, tkOf, tkAs, tkFrom: result = 5 of tkAnd: result = 4 of tkOr, tkXor, tkPtr, tkRef: result = 3 else: return -10 - result = considerStrongSpaces(result) - -proc newlineFollows*(L: TLexer): bool = +proc newlineFollows*(L: Lexer): bool = var pos = L.bufpos while true: case L.buf[pos] @@ -986,7 +968,7 @@ proc newlineFollows*(L: TLexer): bool = else: break -proc skipMultiLineComment(L: var TLexer; tok: var TToken; start: int; +proc skipMultiLineComment(L: var Lexer; tok: var Token; start: int; isDoc: bool) = var pos = start var toStrip = 0 @@ -1051,7 +1033,7 @@ proc skipMultiLineComment(L: var TLexer; tok: var TToken; start: int; when defined(nimpretty): tok.commentOffsetB = L.offsetBase + pos - 1 -proc scanComment(L: var TLexer, tok: var TToken) = +proc scanComment(L: var Lexer, tok: var Token) = var pos = L.bufpos tok.tokType = tkComment # iNumber contains the number of '\n' in the token @@ -1101,7 +1083,7 @@ proc scanComment(L: var TLexer, tok: var TToken) = when defined(nimpretty): tok.commentOffsetB = L.offsetBase + pos - 1 -proc skip(L: var TLexer, tok: var TToken) = +proc skip(L: var Lexer, tok: var Token) = var pos = L.bufpos tokenBegin(tok, pos) tok.strongSpaceA = 0 @@ -1173,7 +1155,7 @@ proc skip(L: var TLexer, tok: var TToken) = tok.tokType = tkComment tok.indent = commentIndent -proc rawGetTok*(L: var TLexer, tok: var TToken) = +proc rawGetTok*(L: var Lexer, tok: var Token) = template atTokenEnd() {.dirty.} = when defined(nimsuggest): # we attach the cursor to the last *strong* token @@ -1181,8 +1163,6 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) = L.previousToken.line = tok.line.uint16 L.previousToken.col = tok.col.int16 - when defined(nimsuggest): - L.cursor = CursorPosition.None fillToken(tok) if L.indentAhead >= 0: tok.indent = L.indentAhead @@ -1253,7 +1233,6 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) = if L.fileIdx == L.config.m.trackPos.fileIndex and tok.col+1 == L.config.m.trackPos.col and tok.line == L.config.m.trackPos.line.int and L.config.ideCmd == ideSug: tok.tokType = tkDot - L.cursor = CursorPosition.InToken L.config.m.trackPos.col = tok.col.int16 inc(L.bufpos) atTokenEnd() @@ -1326,8 +1305,8 @@ proc rawGetTok*(L: var TLexer, tok: var TToken) = proc getIndentWidth*(fileIdx: FileIndex, inputstream: PLLStream; cache: IdentCache; config: ConfigRef): int = - var lex: TLexer - var tok: TToken + var lex: Lexer + var tok: Token initToken(tok) openLexer(lex, fileIdx, inputstream, cache, config) var prevToken = tkEof @@ -1341,11 +1320,11 @@ proc getIndentWidth*(fileIdx: FileIndex, inputstream: PLLStream; proc getPrecedence*(ident: PIdent): int = ## assumes ident is binary operator already - var tok: TToken + var tok: Token initToken(tok) tok.ident = ident tok.tokType = if tok.ident.id in ord(tokKeywordLow) - ord(tkSymbol)..ord(tokKeywordHigh) - ord(tkSymbol): - TTokType(tok.ident.id + ord(tkSymbol)) + TokType(tok.ident.id + ord(tkSymbol)) else: tkOpr - getPrecedence(tok, false) + getPrecedence(tok) diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 1e6b552e2..c1fd7981e 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -919,11 +919,6 @@ template inst(field, t) = proc isTrival(s: PSym): bool {.inline.} = s == nil or (s.ast != nil and s.ast[bodyPos].len == 0) - -proc isEmptyContainer(g: ModuleGraph, t: PType): bool = - (t.kind == tyArray and lengthOrd(g.config, t[0]) == 0) or - (t.kind == tySequence and t[0].kind == tyError) - proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInfo) = ## In the semantic pass this is called in strategic places ## to ensure we lift assignment, destructors and moves properly. diff --git a/compiler/linter.nim b/compiler/linter.nim index 07563ddbb..a229958e6 100644 --- a/compiler/linter.nim +++ b/compiler/linter.nim @@ -71,12 +71,9 @@ proc beautifyName(s: string, k: TSymKind): string = proc differ*(line: string, a, b: int, x: string): string = proc substrEq(s: string, pos, last: int, substr: string): bool = - var i = 0 - while i < substr.len and pos+i <= last and s[pos+i] == substr[i]: - inc i - return i == substr.len - - let last = min(b, line.len) + result = true + for i in 0.. last or s[pos+i] != substr[i]: return false result = "" if not substrEq(line, a, b, x): @@ -84,11 +81,6 @@ proc differ*(line: string, a, b: int, x: string): string = if cmpIgnoreStyle(y, x) == 0: result = y -proc checkStyle(conf: ConfigRef; cache: IdentCache; info: TLineInfo, s: string, k: TSymKind; sym: PSym) = - let beau = beautifyName(s, k) - if s != beau: - lintReport(conf, info, beau, s) - proc nep1CheckDefImpl(conf: ConfigRef; info: TLineInfo; s: PSym; k: TSymKind) = # operators stay as they are: if k in {skResult, skTemp} or s.name.s[0] notin Letters: return @@ -136,6 +128,6 @@ proc styleCheckUse*(conf: ConfigRef; info: TLineInfo; s: PSym) = lintReport(conf, info, newName, oldName) proc checkPragmaUse*(conf: ConfigRef; info: TLineInfo; w: TSpecialWord; pragmaName: string) = - let wanted = canonPragmaSpelling(w) + let wanted = specialWords[w] if pragmaName != wanted: lintReport(conf, info, wanted, pragmaName) diff --git a/compiler/main.nim b/compiler/main.nim index 256ec14cd..774dd7625 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -163,8 +163,8 @@ proc commandScan(cache: IdentCache, config: ConfigRef) = var stream = llStreamOpen(f, fmRead) if stream != nil: var - L: TLexer - tok: TToken + L: Lexer + tok: Token initToken(tok) openLexer(L, f, stream, cache, config) while true: diff --git a/compiler/modulepaths.nim b/compiler/modulepaths.nim index 0c8a4628e..8511b1592 100644 --- a/compiler/modulepaths.nim +++ b/compiler/modulepaths.nim @@ -91,8 +91,7 @@ when false: if result.len > 0: return result proc scriptableImport(pkg, sub: string; info: TLineInfo): string = - result = resolveDollar(gProjectFull, info.toFullPath(), pkg, sub, info) - if result.isNil: result = "" + resolveDollar(gProjectFull, info.toFullPath(), pkg, sub, info) proc lookupPackage(pkg, subdir: PNode): string = let sub = if subdir != nil: renderTree(subdir, {renderNoComments}).replace(" ") else: "" @@ -112,8 +111,7 @@ proc getModuleName*(conf: ConfigRef; n: PNode): string = case n.kind of nkStrLit, nkRStrLit, nkTripleStrLit: try: - result = - pathSubs(conf, n.strVal, toFullPath(conf, n.info).splitFile().dir) + result = pathSubs(conf, n.strVal, toFullPath(conf, n.info).splitFile().dir) except ValueError: localError(conf, n.info, "invalid path: " & n.strVal) result = n.strVal diff --git a/compiler/modules.nim b/compiler/modules.nim index 383858026..8c5b86ea4 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -86,7 +86,7 @@ proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags): P result = r if result == nil: result = newModule(graph, fileIdx) - result.flags = result.flags + flags + result.flags.incl flags result.id = id registerModule(graph, result) else: diff --git a/compiler/msgs.nim b/compiler/msgs.nim index a5953237e..7e2b8e8a6 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -377,15 +377,12 @@ template styledMsgWriteln*(args: varargs[typed]) = when defined(windows): flushFile(stderr) -proc msgKindToString*(kind: TMsgKind): string = +proc msgKindToString*(kind: TMsgKind): string = MsgKindToStr[kind] # later versions may provide translated error messages - result = MsgKindToStr[kind] -proc getMessageStr(msg: TMsgKind, arg: string): string = - result = msgKindToString(msg) % [arg] +proc getMessageStr(msg: TMsgKind, arg: string): string = msgKindToString(msg) % [arg] -type - TErrorHandling* = enum doNothing, doAbort, doRaise +type TErrorHandling* = enum doNothing, doAbort, doRaise proc log*(s: string) = var f: File diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim index bd00832be..c3591a945 100644 --- a/compiler/nimconf.nim +++ b/compiler/nimconf.nim @@ -16,13 +16,13 @@ import # ---------------- configuration file parser ----------------------------- # we use Nim's scanner here to save space and work -proc ppGetTok(L: var TLexer, tok: var TToken) = +proc ppGetTok(L: var Lexer, tok: var Token) = # simple filter rawGetTok(L, tok) while tok.tokType in {tkComment}: rawGetTok(L, tok) -proc parseExpr(L: var TLexer, tok: var TToken; config: ConfigRef): bool -proc parseAtom(L: var TLexer, tok: var TToken; config: ConfigRef): bool = +proc parseExpr(L: var Lexer, tok: var Token; config: ConfigRef): bool +proc parseAtom(L: var Lexer, tok: var Token; config: ConfigRef): bool = if tok.tokType == tkParLe: ppGetTok(L, tok) result = parseExpr(L, tok, config) @@ -35,21 +35,21 @@ proc parseAtom(L: var TLexer, tok: var TToken; config: ConfigRef): bool = result = isDefined(config, tok.ident.s) ppGetTok(L, tok) -proc parseAndExpr(L: var TLexer, tok: var TToken; config: ConfigRef): bool = +proc parseAndExpr(L: var Lexer, tok: var Token; config: ConfigRef): bool = result = parseAtom(L, tok, config) while tok.tokType == tkAnd: ppGetTok(L, tok) # skip "and" var b = parseAtom(L, tok, config) result = result and b -proc parseExpr(L: var TLexer, tok: var TToken; config: ConfigRef): bool = +proc parseExpr(L: var Lexer, tok: var Token; config: ConfigRef): bool = result = parseAndExpr(L, tok, config) while tok.tokType == tkOr: ppGetTok(L, tok) # skip "or" var b = parseAndExpr(L, tok, config) result = result or b -proc evalppIf(L: var TLexer, tok: var TToken; config: ConfigRef): bool = +proc evalppIf(L: var Lexer, tok: var Token; config: ConfigRef): bool = ppGetTok(L, tok) # skip 'if' or 'elif' result = parseExpr(L, tok, config) if tok.tokType == tkColon: ppGetTok(L, tok) @@ -57,7 +57,7 @@ proc evalppIf(L: var TLexer, tok: var TToken; config: ConfigRef): bool = #var condStack: seq[bool] = @[] -proc doEnd(L: var TLexer, tok: var TToken; condStack: var seq[bool]) = +proc doEnd(L: var Lexer, tok: var Token; condStack: var seq[bool]) = if high(condStack) < 0: lexMessage(L, errGenerated, "expected @if") ppGetTok(L, tok) # skip 'end' setLen(condStack, high(condStack)) @@ -66,21 +66,21 @@ type TJumpDest = enum jdEndif, jdElseEndif -proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest; config: ConfigRef; +proc jumpToDirective(L: var Lexer, tok: var Token, dest: TJumpDest; config: ConfigRef; condStack: var seq[bool]) -proc doElse(L: var TLexer, tok: var TToken; config: ConfigRef; condStack: var seq[bool]) = +proc doElse(L: var Lexer, tok: var Token; config: ConfigRef; condStack: var seq[bool]) = if high(condStack) < 0: lexMessage(L, errGenerated, "expected @if") ppGetTok(L, tok) if tok.tokType == tkColon: ppGetTok(L, tok) if condStack[high(condStack)]: jumpToDirective(L, tok, jdEndif, config, condStack) -proc doElif(L: var TLexer, tok: var TToken; config: ConfigRef; condStack: var seq[bool]) = +proc doElif(L: var Lexer, tok: var Token; config: ConfigRef; condStack: var seq[bool]) = if high(condStack) < 0: lexMessage(L, errGenerated, "expected @if") var res = evalppIf(L, tok, config) if condStack[high(condStack)] or not res: jumpToDirective(L, tok, jdElseEndif, config, condStack) else: condStack[high(condStack)] = true -proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest; config: ConfigRef; +proc jumpToDirective(L: var Lexer, tok: var Token, dest: TJumpDest; config: ConfigRef; condStack: var seq[bool]) = var nestedIfs = 0 while true: @@ -110,7 +110,7 @@ proc jumpToDirective(L: var TLexer, tok: var TToken, dest: TJumpDest; config: Co else: ppGetTok(L, tok) -proc parseDirective(L: var TLexer, tok: var TToken; config: ConfigRef; condStack: var seq[bool]) = +proc parseDirective(L: var Lexer, tok: var Token; config: ConfigRef; condStack: var seq[bool]) = ppGetTok(L, tok) # skip @ case whichKeyword(tok.ident) of wIf: @@ -149,16 +149,16 @@ proc parseDirective(L: var TLexer, tok: var TToken; config: ConfigRef; condStack else: lexMessage(L, errGenerated, "invalid directive: '$1'" % $tok) -proc confTok(L: var TLexer, tok: var TToken; config: ConfigRef; condStack: var seq[bool]) = +proc confTok(L: var Lexer, tok: var Token; config: ConfigRef; condStack: var seq[bool]) = ppGetTok(L, tok) while tok.ident != nil and tok.ident.s == "@": parseDirective(L, tok, config, condStack) # else: give the token to the parser -proc checkSymbol(L: TLexer, tok: TToken) = +proc checkSymbol(L: Lexer, tok: Token) = if tok.tokType notin {tkSymbol..tkInt64Lit, tkStrLit..tkTripleStrLit}: lexMessage(L, errGenerated, "expected identifier, but got: " & $tok) -proc parseAssignment(L: var TLexer, tok: var TToken; +proc parseAssignment(L: var Lexer, tok: var Token; config: ConfigRef; condStack: var seq[bool]) = if tok.ident != nil: if tok.ident.s == "-" or tok.ident.s == "--": @@ -211,8 +211,8 @@ proc parseAssignment(L: var TLexer, tok: var TToken; proc readConfigFile*(filename: AbsoluteFile; cache: IdentCache; config: ConfigRef): bool = var - L: TLexer - tok: TToken + L: Lexer + tok: Token stream: PLLStream stream = llStreamOpen(filename, fmRead) if stream != nil: diff --git a/compiler/nimlexbase.nim b/compiler/nimlexbase.nim index af2ae64a3..6132e6f66 100644 --- a/compiler/nimlexbase.nim +++ b/compiler/nimlexbase.nim @@ -162,9 +162,9 @@ proc getColNumber(L: TBaseLexer, pos: int): int = proc getCurrentLine(L: TBaseLexer, marker: bool = true): string = result = "" var i = L.lineStart - while not (L.buf[i] in {CR, LF, EndOfFile}): - result.add(L.buf[i]) - inc(i) - result.add("\n") + while L.buf[i] notin {CR, LF, EndOfFile}: + result.add L.buf[i] + inc i + result.add "\n" if marker: - result.add(spaces(getColNumber(L, L.bufpos)) & '^' & "\n") + result.add spaces(getColNumber(L, L.bufpos)) & '^' & "\n" diff --git a/compiler/options.nim b/compiler/options.nim index b5fb546ef..8ff8cb753 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -691,12 +691,10 @@ iterator nimbleSubs*(conf: ConfigRef; p: string): string = proc toGeneratedFile*(conf: ConfigRef; path: AbsoluteFile, ext: string): AbsoluteFile = ## converts "/home/a/mymodule.nim", "rod" to "/home/a/nimcache/mymodule.rod" - let (head, tail) = splitPath(path.string) - result = getNimcacheDir(conf) / RelativeFile changeFileExt(tail, ext) + result = getNimcacheDir(conf) / RelativeFile path.string.splitPath.tail.changeFileExt(ext) proc completeGeneratedFilePath*(conf: ConfigRef; f: AbsoluteFile, createSubDir: bool = true): AbsoluteFile = - let (head, tail) = splitPath(f.string) let subdir = getNimcacheDir(conf) if createSubDir: try: @@ -704,7 +702,7 @@ proc completeGeneratedFilePath*(conf: ConfigRef; f: AbsoluteFile, except OSError: writeLine(stdout, "cannot create directory: " & subdir.string) quit(1) - result = subdir / RelativeFile tail + result = subdir / RelativeFile f.string.splitPath.tail #echo "completeGeneratedFilePath(", f, ") = ", result proc rawFindFile(conf: ConfigRef; f: RelativeFile; suppressStdlib: bool): AbsoluteFile = diff --git a/compiler/packagehandling.nim b/compiler/packagehandling.nim index 2243e7063..9a3224de2 100644 --- a/compiler/packagehandling.nim +++ b/compiler/packagehandling.nim @@ -28,8 +28,6 @@ proc getNimbleFile*(conf: ConfigRef; path: string): string = result = file break packageSearch # we also store if we didn't find anything: - when not defined(nimNoNilSeqs): - if result.isNil: result = "" for d in myParentDirs(path): #echo "set cache ", d, " |", result, "|", parents conf.packageCache[d] = result diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim index ce9a855d8..ed8f2c16a 100644 --- a/compiler/parampatterns.nim +++ b/compiler/parampatterns.nim @@ -99,14 +99,14 @@ proc compileConstraints(p: PNode, result: var TPatternCode; conf: ConfigRef) = else: # check all symkinds: internalAssert conf, int(high(TSymKind)) < 255 - for i in low(TSymKind)..high(TSymKind): + for i in TSymKind: if cmpIgnoreStyle(($i).substr(2), spec) == 0: result.add(ppSymKind) result.add(chr(i.ord)) return # check all nodekinds: internalAssert conf, int(high(TNodeKind)) < 255 - for i in low(TNodeKind)..high(TNodeKind): + for i in TNodeKind: if cmpIgnoreStyle($i, spec) == 0: result.add(ppNodeKind) result.add(chr(i.ord)) diff --git a/compiler/parser.nim b/compiler/parser.nim index 8cfeff6b0..e7db8d8b1 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -37,14 +37,14 @@ when defined(nimpretty): import layouter type - TParser* = object # A TParser object represents a file that - # is being parsed - currInd: int # current indentation level - firstTok: bool # Has the first token been read? - hasProgress: bool # some while loop requires progress ensurance - lex*: TLexer # The lexer that is used for parsing - tok*: TToken # The current token - inPragma*: int # Pragma level + Parser* = object # A Parser object represents a file that + # is being parsed + currInd: int # current indentation level + firstTok: bool # Has the first token been read? + hasProgress: bool # some while loop requires progress ensurance + lex*: Lexer # The lexer that is used for parsing + tok*: Token # The current token + inPragma*: int # Pragma level inSemiStmtList*: int emptyNode: PNode when defined(nimpretty): @@ -53,42 +53,42 @@ type SymbolMode = enum smNormal, smAllowNil, smAfterDot - TPrimaryMode = enum + PrimaryMode = enum pmNormal, pmTypeDesc, pmTypeDef, pmSkipSuffix -proc parseAll*(p: var TParser): PNode -proc closeParser*(p: var TParser) -proc parseTopLevelStmt*(p: var TParser): PNode +proc parseAll*(p: var Parser): PNode +proc closeParser*(p: var Parser) +proc parseTopLevelStmt*(p: var Parser): PNode # helpers for the other parsers -proc isOperator*(tok: TToken): bool -proc getTok*(p: var TParser) -proc parMessage*(p: TParser, msg: TMsgKind, arg: string = "") -proc skipComment*(p: var TParser, node: PNode) -proc newNodeP*(kind: TNodeKind, p: TParser): PNode -proc newIntNodeP*(kind: TNodeKind, intVal: BiggestInt, p: TParser): PNode -proc newFloatNodeP*(kind: TNodeKind, floatVal: BiggestFloat, p: TParser): PNode -proc newStrNodeP*(kind: TNodeKind, strVal: string, p: TParser): PNode -proc newIdentNodeP*(ident: PIdent, p: TParser): PNode -proc expectIdentOrKeyw*(p: TParser) -proc expectIdent*(p: TParser) -proc parLineInfo*(p: TParser): TLineInfo -proc eat*(p: var TParser, tokType: TTokType) -proc skipInd*(p: var TParser) -proc optPar*(p: var TParser) -proc optInd*(p: var TParser, n: PNode) -proc indAndComment*(p: var TParser, n: PNode) -proc setBaseFlags*(n: PNode, base: TNumericalBase) -proc parseSymbol*(p: var TParser, mode = smNormal): PNode -proc parseTry(p: var TParser; isExpr: bool): PNode -proc parseCase(p: var TParser): PNode -proc parseStmtPragma(p: var TParser): PNode -proc parsePragma(p: var TParser): PNode -proc postExprBlocks(p: var TParser, x: PNode): PNode -proc parseExprStmt(p: var TParser): PNode -proc parseBlock(p: var TParser): PNode -proc primary(p: var TParser, mode: TPrimaryMode): PNode -proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode +proc isOperator*(tok: Token): bool +proc getTok*(p: var Parser) +proc parMessage*(p: Parser, msg: TMsgKind, arg: string = "") +proc skipComment*(p: var Parser, node: PNode) +proc newNodeP*(kind: TNodeKind, p: Parser): PNode +proc newIntNodeP*(kind: TNodeKind, intVal: BiggestInt, p: Parser): PNode +proc newFloatNodeP*(kind: TNodeKind, floatVal: BiggestFloat, p: Parser): PNode +proc newStrNodeP*(kind: TNodeKind, strVal: string, p: Parser): PNode +proc newIdentNodeP*(ident: PIdent, p: Parser): PNode +proc expectIdentOrKeyw*(p: Parser) +proc expectIdent*(p: Parser) +proc parLineInfo*(p: Parser): TLineInfo +proc eat*(p: var Parser, tokType: TokType) +proc skipInd*(p: var Parser) +proc optPar*(p: var Parser) +proc optInd*(p: var Parser, n: PNode) +proc indAndComment*(p: var Parser, n: PNode) +proc setBaseFlags*(n: PNode, base: NumericalBase) +proc parseSymbol*(p: var Parser, mode = smNormal): PNode +proc parseTry(p: var Parser; isExpr: bool): PNode +proc parseCase(p: var Parser): PNode +proc parseStmtPragma(p: var Parser): PNode +proc parsePragma(p: var Parser): PNode +proc postExprBlocks(p: var Parser, x: PNode): PNode +proc parseExprStmt(p: var Parser): PNode +proc parseBlock(p: var Parser): PNode +proc primary(p: var Parser, mode: PrimaryMode): PNode +proc simpleExprAux(p: var Parser, limit: int, mode: PrimaryMode): PNode # implementation @@ -97,7 +97,7 @@ template prettySection(body) = body when defined(nimpretty): endSection(p.em) -proc getTok(p: var TParser) = +proc getTok(p: var Parser) = ## Get the next token from the parser's lexer, and store it in the parser's ## `tok` member. rawGetTok(p.lex, p.tok) @@ -110,7 +110,7 @@ proc getTok(p: var TParser) = rawGetTok(p.lex, p.tok) emitTok(p.em, p.lex, p.tok) -proc openParser*(p: var TParser, fileIdx: FileIndex, inputStream: PLLStream, +proc openParser*(p: var Parser, fileIdx: FileIndex, inputStream: PLLStream, cache: IdentCache; config: ConfigRef) = ## Open a parser, using the given arguments to set up its internal state. ## @@ -122,25 +122,25 @@ proc openParser*(p: var TParser, fileIdx: FileIndex, inputStream: PLLStream, p.firstTok = true p.emptyNode = newNode(nkEmpty) -proc openParser*(p: var TParser, filename: AbsoluteFile, inputStream: PLLStream, +proc openParser*(p: var Parser, filename: AbsoluteFile, inputStream: PLLStream, cache: IdentCache; config: ConfigRef) = openParser(p, fileInfoIdx(config, filename), inputStream, cache, config) -proc closeParser(p: var TParser) = +proc closeParser(p: var Parser) = ## Close a parser, freeing up its resources. closeLexer(p.lex) when defined(nimpretty): closeEmitter(p.em) -proc parMessage(p: TParser, msg: TMsgKind, arg = "") = +proc parMessage(p: Parser, msg: TMsgKind, arg = "") = ## Produce and emit the parser message `arg` to output. lexMessageTok(p.lex, msg, p.tok, arg) -proc parMessage(p: TParser, msg: string, tok: TToken) = +proc parMessage(p: Parser, msg: string, tok: Token) = ## Produce and emit a parser message to output about the token `tok` parMessage(p, errGenerated, msg % prettyTok(tok)) -proc parMessage(p: TParser, arg: string) = +proc parMessage(p: Parser, arg: string) = ## Produce and emit the parser message `arg` to output. lexMessageTok(p.lex, errGenerated, p.tok, arg) @@ -150,7 +150,7 @@ template withInd(p, body: untyped) = body p.currInd = oldInd -template newlineWasSplitting(p: var TParser) = +template newlineWasSplitting(p: var Parser) = when defined(nimpretty): layouter.newlineWasSplitting(p.em) @@ -158,10 +158,10 @@ template realInd(p): bool = p.tok.indent > p.currInd template sameInd(p): bool = p.tok.indent == p.currInd template sameOrNoInd(p): bool = p.tok.indent == p.currInd or p.tok.indent < 0 -proc validInd(p: var TParser): bool {.inline.} = +proc validInd(p: var Parser): bool {.inline.} = result = p.tok.indent < 0 or p.tok.indent > p.currInd -proc rawSkipComment(p: var TParser, node: PNode) = +proc rawSkipComment(p: var Parser, node: PNode) = if p.tok.tokType == tkComment: if node != nil: when not defined(nimNoNilSeqs): @@ -177,43 +177,42 @@ proc rawSkipComment(p: var TParser, node: PNode) = parMessage(p, errInternal, "skipComment") getTok(p) -proc skipComment(p: var TParser, node: PNode) = +proc skipComment(p: var Parser, node: PNode) = if p.tok.indent < 0: rawSkipComment(p, node) -proc flexComment(p: var TParser, node: PNode) = +proc flexComment(p: var Parser, node: PNode) = if p.tok.indent < 0 or realInd(p): rawSkipComment(p, node) const errInvalidIndentation = "invalid indentation" errIdentifierExpected = "identifier expected, but got '$1'" errExprExpected = "expression expected, but found '$1'" - errTokenExpected = "'$1' expected" -proc skipInd(p: var TParser) = +proc skipInd(p: var Parser) = if p.tok.indent >= 0: if not realInd(p): parMessage(p, errInvalidIndentation) -proc optPar(p: var TParser) = +proc optPar(p: var Parser) = if p.tok.indent >= 0: if p.tok.indent < p.currInd: parMessage(p, errInvalidIndentation) -proc optInd(p: var TParser, n: PNode) = +proc optInd(p: var Parser, n: PNode) = skipComment(p, n) skipInd(p) -proc getTokNoInd(p: var TParser) = +proc getTokNoInd(p: var Parser) = getTok(p) if p.tok.indent >= 0: parMessage(p, errInvalidIndentation) -proc expectIdentOrKeyw(p: TParser) = +proc expectIdentOrKeyw(p: Parser) = if p.tok.tokType != tkSymbol and not isKeyword(p.tok.tokType): lexMessage(p.lex, errGenerated, errIdentifierExpected % prettyTok(p.tok)) -proc expectIdent(p: TParser) = +proc expectIdent(p: Parser) = if p.tok.tokType != tkSymbol: lexMessage(p.lex, errGenerated, errIdentifierExpected % prettyTok(p.tok)) -proc eat(p: var TParser, tokType: TTokType) = +proc eat(p: var Parser, tokType: TokType) = ## Move the parser to the next token if the current token is of type ## `tokType`, otherwise error. if p.tok.tokType == tokType: @@ -222,64 +221,63 @@ proc eat(p: var TParser, tokType: TTokType) = lexMessage(p.lex, errGenerated, "expected: '" & TokTypeToStr[tokType] & "', but got: '" & prettyTok(p.tok) & "'") -proc parLineInfo(p: TParser): TLineInfo = +proc parLineInfo(p: Parser): TLineInfo = ## Retrieve the line information associated with the parser's current state. result = getLineInfo(p.lex, p.tok) -proc indAndComment(p: var TParser, n: PNode) = +proc indAndComment(p: var Parser, n: PNode) = if p.tok.indent > p.currInd: if p.tok.tokType == tkComment: rawSkipComment(p, n) else: parMessage(p, errInvalidIndentation) else: skipComment(p, n) -proc newNodeP(kind: TNodeKind, p: TParser): PNode = +proc newNodeP(kind: TNodeKind, p: Parser): PNode = result = newNodeI(kind, parLineInfo(p)) -proc newIntNodeP(kind: TNodeKind, intVal: BiggestInt, p: TParser): PNode = +proc newIntNodeP(kind: TNodeKind, intVal: BiggestInt, p: Parser): PNode = result = newNodeP(kind, p) result.intVal = intVal proc newFloatNodeP(kind: TNodeKind, floatVal: BiggestFloat, - p: TParser): PNode = + p: Parser): PNode = result = newNodeP(kind, p) result.floatVal = floatVal -proc newStrNodeP(kind: TNodeKind, strVal: string, p: TParser): PNode = +proc newStrNodeP(kind: TNodeKind, strVal: string, p: Parser): PNode = result = newNodeP(kind, p) result.strVal = strVal -proc newIdentNodeP(ident: PIdent, p: TParser): PNode = +proc newIdentNodeP(ident: PIdent, p: Parser): PNode = result = newNodeP(nkIdent, p) result.ident = ident -proc parseExpr(p: var TParser): PNode -proc parseStmt(p: var TParser): PNode -proc parseTypeDesc(p: var TParser): PNode -proc parseParamList(p: var TParser, retColon = true): PNode +proc parseExpr(p: var Parser): PNode +proc parseStmt(p: var Parser): PNode +proc parseTypeDesc(p: var Parser): PNode +proc parseParamList(p: var Parser, retColon = true): PNode -proc isSigilLike(tok: TToken): bool {.inline.} = +proc isSigilLike(tok: Token): bool {.inline.} = result = tok.tokType == tkOpr and tok.ident.s[0] == '@' -proc isRightAssociative(tok: TToken): bool {.inline.} = +proc isRightAssociative(tok: Token): bool {.inline.} = ## Determines whether the token is right assocative. result = tok.tokType == tkOpr and tok.ident.s[0] == '^' # or (tok.ident.s.len > 1 and tok.ident.s[^1] == '>') -proc isOperator(tok: TToken): bool = +proc isOperator(tok: Token): bool = ## Determines if the given token is an operator type token. tok.tokType in {tkOpr, tkDiv, tkMod, tkShl, tkShr, tkIn, tkNotin, tkIs, tkIsnot, tkNot, tkOf, tkAs, tkFrom, tkDotDot, tkAnd, tkOr, tkXor} -proc isUnary(p: TParser): bool = - ## Check if the current parser token is a unary operator - if p.tok.tokType in {tkOpr, tkDotDot} and - p.tok.strongSpaceB == 0 and - p.tok.strongSpaceA > 0: - result = true +proc isUnary(tok: Token): bool = + ## Check if the given token is a unary operator + tok.tokType in {tkOpr, tkDotDot} and + tok.strongSpaceB == 0 and + tok.strongSpaceA > 0 -proc checkBinary(p: TParser) {.inline.} = +proc checkBinary(p: Parser) {.inline.} = ## Check if the current parser token is a binary operator. # we don't check '..' here as that's too annoying if p.tok.tokType == tkOpr: @@ -315,13 +313,13 @@ proc checkBinary(p: TParser) {.inline.} = #| mulExpr = dollarExpr (OP9 optInd dollarExpr)* #| dollarExpr = primary (OP10 optInd primary)* -proc colcom(p: var TParser, n: PNode) = +proc colcom(p: var Parser, n: PNode) = eat(p, tkColon) skipComment(p, n) const tkBuiltInMagics = {tkType, tkStatic, tkAddr} -proc parseSymbol(p: var TParser, mode = smNormal): PNode = +proc parseSymbol(p: var Parser, mode = smNormal): PNode = #| symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`' #| | IDENT | KEYW case p.tok.tokType @@ -374,7 +372,7 @@ proc parseSymbol(p: var TParser, mode = smNormal): PNode = #if not isKeyword(p.tok.tokType): getTok(p) result = p.emptyNode -proc colonOrEquals(p: var TParser, a: PNode): PNode = +proc colonOrEquals(p: var Parser, a: PNode): PNode = if p.tok.tokType == tkColon: result = newNodeP(nkExprColonExpr, p) getTok(p) @@ -391,7 +389,7 @@ proc colonOrEquals(p: var TParser, a: PNode): PNode = else: result = a -proc exprColonEqExpr(p: var TParser): PNode = +proc exprColonEqExpr(p: var Parser): PNode = #| exprColonEqExpr = expr (':'|'=' expr)? var a = parseExpr(p) if p.tok.tokType == tkDo: @@ -399,7 +397,7 @@ proc exprColonEqExpr(p: var TParser): PNode = else: result = colonOrEquals(p, a) -proc exprList(p: var TParser, endTok: TTokType, result: PNode) = +proc exprList(p: var Parser, endTok: TokType, result: PNode) = #| exprList = expr ^+ comma when defined(nimpretty): inc p.em.doIndentMore @@ -415,7 +413,7 @@ proc exprList(p: var TParser, endTok: TTokType, result: PNode) = when defined(nimpretty): dec p.em.doIndentMore -proc exprColonEqExprListAux(p: var TParser, endTok: TTokType, result: PNode) = +proc exprColonEqExprListAux(p: var Parser, endTok: TokType, result: PNode) = assert(endTok in {tkCurlyRi, tkCurlyDotRi, tkBracketRi, tkParRi}) getTok(p) flexComment(p, result) @@ -433,13 +431,13 @@ proc exprColonEqExprListAux(p: var TParser, endTok: TTokType, result: PNode) = optPar(p) eat(p, endTok) -proc exprColonEqExprList(p: var TParser, kind: TNodeKind, - endTok: TTokType): PNode = +proc exprColonEqExprList(p: var Parser, kind: TNodeKind, + endTok: TokType): PNode = #| exprColonEqExprList = exprColonEqExpr (comma exprColonEqExpr)* (comma)? result = newNodeP(kind, p) exprColonEqExprListAux(p, endTok, result) -proc dotExpr(p: var TParser, a: PNode): PNode = +proc dotExpr(p: var Parser, a: PNode): PNode = #| dotExpr = expr '.' optInd (symbol | '[:' exprList ']') #| explicitGenericInstantiation = '[:' exprList ']' ( '(' exprColonEqExpr ')' )? var info = p.parLineInfo @@ -461,12 +459,12 @@ proc dotExpr(p: var TParser, a: PNode): PNode = exprColonEqExprListAux(p, tkParRi, y) result = y -proc qualifiedIdent(p: var TParser): PNode = +proc qualifiedIdent(p: var Parser): PNode = #| qualifiedIdent = symbol ('.' optInd symbol)? result = parseSymbol(p) if p.tok.tokType == tkDot: result = dotExpr(p, result) -proc setOrTableConstr(p: var TParser): PNode = +proc setOrTableConstr(p: var Parser): PNode = #| setOrTableConstr = '{' ((exprColonEqExpr comma)* | ':' ) '}' result = newNodeP(nkCurly, p) getTok(p) # skip '{' @@ -486,7 +484,7 @@ proc setOrTableConstr(p: var TParser): PNode = optPar(p) eat(p, tkCurlyRi) # skip '}' -proc parseCast(p: var TParser): PNode = +proc parseCast(p: var Parser): PNode = #| castExpr = 'cast' '[' optInd typeDesc optPar ']' '(' optInd expr optPar ')' result = newNodeP(nkCast, p) getTok(p) @@ -501,14 +499,14 @@ proc parseCast(p: var TParser): PNode = optPar(p) eat(p, tkParRi) -proc setBaseFlags(n: PNode, base: TNumericalBase) = +proc setBaseFlags(n: PNode, base: NumericalBase) = case base of base10: discard of base2: incl(n.flags, nfBase2) of base8: incl(n.flags, nfBase8) of base16: incl(n.flags, nfBase16) -proc parseGStrLit(p: var TParser, a: PNode): PNode = +proc parseGStrLit(p: var Parser, a: PNode): PNode = case p.tok.tokType of tkGStrLit: result = newNodeP(nkCallStrLit, p) @@ -523,10 +521,10 @@ proc parseGStrLit(p: var TParser, a: PNode): PNode = else: result = a -proc complexOrSimpleStmt(p: var TParser): PNode -proc simpleExpr(p: var TParser, mode = pmNormal): PNode +proc complexOrSimpleStmt(p: var Parser): PNode +proc simpleExpr(p: var Parser, mode = pmNormal): PNode -proc semiStmtList(p: var TParser, result: PNode) = +proc semiStmtList(p: var Parser, result: PNode) = inc p.inSemiStmtList result.add(complexOrSimpleStmt(p)) # progress guaranteed @@ -537,7 +535,7 @@ proc semiStmtList(p: var TParser, result: PNode) = dec p.inSemiStmtList result.transitionSonsKind(nkStmtListExpr) -proc parsePar(p: var TParser): PNode = +proc parsePar(p: var Parser): PNode = #| parKeyw = 'discard' | 'include' | 'if' | 'while' | 'case' | 'try' #| | 'finally' | 'except' | 'for' | 'block' | 'const' | 'let' #| | 'when' | 'var' | 'mixin' @@ -607,7 +605,7 @@ proc parsePar(p: var TParser): PNode = optPar(p) eat(p, tkParRi) -proc identOrLiteral(p: var TParser, mode: TPrimaryMode): PNode = +proc identOrLiteral(p: var Parser, mode: PrimaryMode): PNode = #| literal = | INT_LIT | INT8_LIT | INT16_LIT | INT32_LIT | INT64_LIT #| | UINT_LIT | UINT8_LIT | UINT16_LIT | UINT32_LIT | UINT64_LIT #| | FLOAT_LIT | FLOAT32_LIT | FLOAT64_LIT @@ -717,15 +715,15 @@ proc identOrLiteral(p: var TParser, mode: TPrimaryMode): PNode = getTok(p) # we must consume a token here to prevent endless loops! result = p.emptyNode -proc namedParams(p: var TParser, callee: PNode, - kind: TNodeKind, endTok: TTokType): PNode = +proc namedParams(p: var Parser, callee: PNode, + kind: TNodeKind, endTok: TokType): PNode = let a = callee result = newNodeP(kind, p) result.add(a) # progress guaranteed exprColonEqExprListAux(p, endTok, result) -proc commandParam(p: var TParser, isFirstParam: var bool; mode: TPrimaryMode): PNode = +proc commandParam(p: var Parser, isFirstParam: var bool; mode: PrimaryMode): PNode = if mode == pmTypeDesc: result = simpleExpr(p, mode) else: @@ -740,7 +738,7 @@ proc commandParam(p: var TParser, isFirstParam: var bool; mode: TPrimaryMode): P result.add(parseExpr(p)) isFirstParam = false -proc commandExpr(p: var TParser; r: PNode; mode: TPrimaryMode): PNode = +proc commandExpr(p: var Parser; r: PNode; mode: PrimaryMode): PNode = result = newNodeP(nkCommand, p) result.add(r) var isFirstParam = true @@ -748,8 +746,8 @@ proc commandExpr(p: var TParser; r: PNode; mode: TPrimaryMode): PNode = p.hasProgress = false result.add commandParam(p, isFirstParam, mode) -proc primarySuffix(p: var TParser, r: PNode, - baseIndent: int, mode: TPrimaryMode): PNode = +proc primarySuffix(p: var Parser, r: PNode, + baseIndent: int, mode: PrimaryMode): PNode = #| primarySuffix = '(' (exprColonEqExpr comma?)* ')' #| | '.' optInd symbol generalizedLit? #| | '[' optInd exprColonEqExprList optPar ']' @@ -800,7 +798,7 @@ proc primarySuffix(p: var TParser, r: PNode, # `foo ref` or `foo ptr`. Unfortunately, these two are also # used as infix operators for the memory regions feature and # the current parsing rules don't play well here. - if p.inPragma == 0 and (isUnary(p) or p.tok.tokType notin {tkOpr, tkDotDot}): + if p.inPragma == 0 and (isUnary(p.tok) or p.tok.tokType notin {tkOpr, tkDotDot}): # actually parsing {.push hints:off.} as {.push(hints:off).} is a sweet # solution, but pragmas.nim can't handle that result = commandExpr(p, result, mode) @@ -808,17 +806,17 @@ proc primarySuffix(p: var TParser, r: PNode, else: break -proc parseOperators(p: var TParser, headNode: PNode, - limit: int, mode: TPrimaryMode): PNode = +proc parseOperators(p: var Parser, headNode: PNode, + limit: int, mode: PrimaryMode): PNode = result = headNode # expand while operators have priorities higher than 'limit' - var opPrec = getPrecedence(p.tok, false) + var opPrec = getPrecedence(p.tok) let modeB = if mode == pmTypeDef: pmTypeDesc else: mode # the operator itself must not start on a new line: # progress guaranteed - while opPrec >= limit and p.tok.indent < 0 and not isUnary(p): + while opPrec >= limit and p.tok.indent < 0 and not isUnary(p.tok): checkBinary(p) - var leftAssoc = 1-ord(isRightAssociative(p.tok)) + let leftAssoc = ord(not isRightAssociative(p.tok)) var a = newNodeP(nkInfix, p) var opNode = newIdentNodeP(p.tok.ident, p) # skip operator: getTok(p) @@ -830,9 +828,9 @@ proc parseOperators(p: var TParser, headNode: PNode, a.add(result) a.add(b) result = a - opPrec = getPrecedence(p.tok, false) + opPrec = getPrecedence(p.tok) -proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode = +proc simpleExprAux(p: var Parser, limit: int, mode: PrimaryMode): PNode = result = primary(p, mode) if p.tok.tokType == tkCurlyDotLe and (p.tok.indent < 0 or realInd(p)) and mode == pmNormal: @@ -842,14 +840,14 @@ proc simpleExprAux(p: var TParser, limit: int, mode: TPrimaryMode): PNode = result = pragmaExp result = parseOperators(p, result, limit, mode) -proc simpleExpr(p: var TParser, mode = pmNormal): PNode = +proc simpleExpr(p: var Parser, mode = pmNormal): PNode = when defined(nimpretty): inc p.em.doIndentMore result = simpleExprAux(p, -1, mode) when defined(nimpretty): dec p.em.doIndentMore -proc parseIfExpr(p: var TParser, kind: TNodeKind): PNode = +proc parseIfExpr(p: var Parser, kind: TNodeKind): PNode = #| condExpr = expr colcom expr optInd #| ('elif' expr colcom expr optInd)* #| 'else' colcom expr @@ -907,7 +905,7 @@ proc parseIfExpr(p: var TParser, kind: TNodeKind): PNode = if wasIndented: p.currInd = oldInd -proc parsePragma(p: var TParser): PNode = +proc parsePragma(p: var Parser): PNode = #| pragma = '{.' optInd (exprColonEqExpr comma?)* optPar ('.}' | '}') result = newNodeP(nkPragma, p) inc p.inPragma @@ -936,7 +934,7 @@ proc parsePragma(p: var TParser): PNode = dec p.em.doIndentMore dec p.em.keepIndents -proc identVis(p: var TParser; allowDot=false): PNode = +proc identVis(p: var Parser; allowDot=false): PNode = #| identVis = symbol OPR? # postfix position #| identVisDot = symbol '.' optInd symbol OPR? var a = parseSymbol(p) @@ -952,7 +950,7 @@ proc identVis(p: var TParser; allowDot=false): PNode = else: result = a -proc identWithPragma(p: var TParser; allowDot=false): PNode = +proc identWithPragma(p: var Parser; allowDot=false): PNode = #| identWithPragma = identVis pragma? #| identWithPragmaDot = identVisDot pragma? var a = identVis(p, allowDot) @@ -964,13 +962,13 @@ proc identWithPragma(p: var TParser; allowDot=false): PNode = result = a type - TDeclaredIdentFlag = enum + DeclaredIdentFlag = enum withPragma, # identifier may have pragma withBothOptional # both ':' and '=' parts are optional withDot # allow 'var ident.ident = value' - TDeclaredIdentFlags = set[TDeclaredIdentFlag] + DeclaredIdentFlags = set[DeclaredIdentFlag] -proc parseIdentColonEquals(p: var TParser, flags: TDeclaredIdentFlags): PNode = +proc parseIdentColonEquals(p: var Parser, flags: DeclaredIdentFlags): PNode = #| declColonEquals = identWithPragma (comma identWithPragma)* comma? #| (':' optInd typeDesc)? ('=' optInd expr)? #| identColonEquals = IDENT (comma IDENT)* comma? @@ -1004,7 +1002,7 @@ proc parseIdentColonEquals(p: var TParser, flags: TDeclaredIdentFlags): PNode = else: result.add(newNodeP(nkEmpty, p)) -proc parseTuple(p: var TParser, indentAllowed = false): PNode = +proc parseTuple(p: var Parser, indentAllowed = false): PNode = #| inlTupleDecl = 'tuple' #| '[' optInd (identColonEquals (comma/semicolon)?)* optPar ']' #| extTupleDecl = 'tuple' @@ -1049,7 +1047,7 @@ proc parseTuple(p: var TParser, indentAllowed = false): PNode = else: result = newNodeP(nkTupleClassTy, p) -proc parseParamList(p: var TParser, retColon = true): PNode = +proc parseParamList(p: var Parser, retColon = true): PNode = #| paramList = '(' declColonEquals ^* (comma/semicolon) ')' #| paramListArrow = paramList? ('->' optInd typeDesc)? #| paramListColon = paramList? (':' optInd typeDesc)? @@ -1097,13 +1095,13 @@ proc parseParamList(p: var TParser, retColon = true): PNode = dec p.em.doIndentMore dec p.em.keepIndents -proc optPragmas(p: var TParser): PNode = +proc optPragmas(p: var Parser): PNode = if p.tok.tokType == tkCurlyDotLe and (p.tok.indent < 0 or realInd(p)): result = parsePragma(p) else: result = p.emptyNode -proc parseDoBlock(p: var TParser; info: TLineInfo): PNode = +proc parseDoBlock(p: var Parser; info: TLineInfo): PNode = #| doBlock = 'do' paramListArrow pragma? colcom stmt let params = parseParamList(p, retColon=false) let pragmas = optPragmas(p) @@ -1114,7 +1112,7 @@ proc parseDoBlock(p: var TParser; info: TLineInfo): PNode = body = result, params = params, name = p.emptyNode, pattern = p.emptyNode, genericParams = p.emptyNode, pragmas = pragmas, exceptions = p.emptyNode) -proc parseProcExpr(p: var TParser; isExpr: bool; kind: TNodeKind): PNode = +proc parseProcExpr(p: var Parser; isExpr: bool; kind: TNodeKind): PNode = #| procExpr = 'proc' paramListColon pragma? ('=' COMMENT? stmt)? # either a proc type or a anonymous proc let info = parLineInfo(p) @@ -1136,7 +1134,7 @@ proc parseProcExpr(p: var TParser; isExpr: bool; kind: TNodeKind): PNode = parMessage(p, "func keyword is not allowed in type descriptions, use proc with {.noSideEffect.} pragma instead") result.add(pragmas) -proc isExprStart(p: TParser): bool = +proc isExprStart(p: Parser): bool = case p.tok.tokType of tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf, tkFor, tkProc, tkFunc, tkIterator, tkBind, tkBuiltInMagics, @@ -1145,7 +1143,7 @@ proc isExprStart(p: TParser): bool = result = true else: result = false -proc parseSymbolList(p: var TParser, result: PNode) = +proc parseSymbolList(p: var Parser, result: PNode) = # progress guaranteed while true: var s = parseSymbol(p, smAllowNil) @@ -1155,8 +1153,8 @@ proc parseSymbolList(p: var TParser, result: PNode) = getTok(p) optInd(p, s) -proc parseTypeDescKAux(p: var TParser, kind: TNodeKind, - mode: TPrimaryMode): PNode = +proc parseTypeDescKAux(p: var Parser, kind: TNodeKind, + mode: PrimaryMode): PNode = #| distinct = 'distinct' optInd typeDesc result = newNodeP(kind, p) getTok(p) @@ -1178,9 +1176,9 @@ proc parseTypeDescKAux(p: var TParser, kind: TNodeKind, result.add list parseSymbolList(p, list) -proc parseVarTuple(p: var TParser): PNode +proc parseVarTuple(p: var Parser): PNode -proc parseFor(p: var TParser): PNode = +proc parseFor(p: var Parser): PNode = #| forStmt = 'for' (identWithPragma ^+ comma) 'in' expr colcom stmt #| forExpr = forStmt getTokNoInd(p) @@ -1210,7 +1208,7 @@ template nimprettyDontTouch(body) = when defined(nimpretty): dec p.em.keepIndents -proc parseExpr(p: var TParser): PNode = +proc parseExpr(p: var Parser): PNode = #| expr = (blockExpr #| | ifExpr #| | whenExpr @@ -1241,11 +1239,11 @@ proc parseExpr(p: var TParser): PNode = result = parseTry(p, isExpr=true) else: result = simpleExpr(p) -proc parseEnum(p: var TParser): PNode -proc parseObject(p: var TParser): PNode -proc parseTypeClass(p: var TParser): PNode +proc parseEnum(p: var Parser): PNode +proc parseObject(p: var Parser): PNode +proc parseTypeClass(p: var Parser): PNode -proc primary(p: var TParser, mode: TPrimaryMode): PNode = +proc primary(p: var Parser, mode: PrimaryMode): PNode = #| typeKeyw = 'var' | 'out' | 'ref' | 'ptr' | 'shared' | 'tuple' #| | 'proc' | 'iterator' | 'distinct' | 'object' | 'enum' #| primary = typeKeyw optInd typeDesc @@ -1313,7 +1311,7 @@ proc primary(p: var TParser, mode: TPrimaryMode): PNode = if mode != pmSkipSuffix: result = primarySuffix(p, result, baseInd, mode) -proc binaryNot(p: var TParser; a: PNode): PNode = +proc binaryNot(p: var Parser; a: PNode): PNode = if p.tok.tokType == tkNot: let notOpr = newIdentNodeP(p.tok.ident, p) getTok(p) @@ -1326,13 +1324,13 @@ proc binaryNot(p: var TParser; a: PNode): PNode = else: result = a -proc parseTypeDesc(p: var TParser): PNode = +proc parseTypeDesc(p: var Parser): PNode = #| typeDesc = simpleExpr ('not' expr)? newlineWasSplitting(p) result = simpleExpr(p, pmTypeDesc) result = binaryNot(p, result) -proc parseTypeDefAux(p: var TParser): PNode = +proc parseTypeDefAux(p: var Parser): PNode = #| typeDefAux = simpleExpr ('not' expr)? #| | 'concept' typeClass result = simpleExpr(p, pmTypeDef) @@ -1346,7 +1344,7 @@ proc makeCall(n: PNode): PNode = result = newNodeI(nkCall, n.info) result.add n -proc postExprBlocks(p: var TParser, x: PNode): PNode = +proc postExprBlocks(p: var Parser, x: PNode): PNode = #| postExprBlocks = ':' stmt? ( IND{=} doBlock #| | IND{=} 'of' exprList ':' stmt #| | IND{=} 'elif' expr ':' stmt @@ -1420,7 +1418,7 @@ proc postExprBlocks(p: var TParser, x: PNode): PNode = if openingParams.kind != nkEmpty: parMessage(p, "expected ':'") -proc parseExprStmt(p: var TParser): PNode = +proc parseExprStmt(p: var Parser): PNode = #| exprStmt = simpleExpr #| (( '=' optInd expr colonBody? ) #| / ( expr ^+ comma @@ -1456,7 +1454,7 @@ proc parseExprStmt(p: var TParser): PNode = result = a result = postExprBlocks(p, result) -proc parseModuleName(p: var TParser, kind: TNodeKind): PNode = +proc parseModuleName(p: var Parser, kind: TNodeKind): PNode = result = parseExpr(p) when false: # parseExpr already handles 'as' syntax ... @@ -1467,7 +1465,7 @@ proc parseModuleName(p: var TParser, kind: TNodeKind): PNode = result.add(a) result.add(parseExpr(p)) -proc parseImport(p: var TParser, kind: TNodeKind): PNode = +proc parseImport(p: var Parser, kind: TNodeKind): PNode = #| importStmt = 'import' optInd expr #| ((comma expr)* #| / 'except' optInd (expr ^+ comma)) @@ -1495,7 +1493,7 @@ proc parseImport(p: var TParser, kind: TNodeKind): PNode = optInd(p, a) #expectNl(p) -proc parseIncludeStmt(p: var TParser): PNode = +proc parseIncludeStmt(p: var Parser): PNode = #| includeStmt = 'include' optInd expr ^+ comma result = newNodeP(nkIncludeStmt, p) getTok(p) # skip `import` or `include` @@ -1511,7 +1509,7 @@ proc parseIncludeStmt(p: var TParser): PNode = optInd(p, a) #expectNl(p) -proc parseFromStmt(p: var TParser): PNode = +proc parseFromStmt(p: var Parser): PNode = #| fromStmt = 'from' expr 'import' optInd expr (comma expr)* result = newNodeP(nkFromStmt, p) getTok(p) # skip `from` @@ -1531,7 +1529,7 @@ proc parseFromStmt(p: var TParser): PNode = optInd(p, a) #expectNl(p) -proc parseReturnOrRaise(p: var TParser, kind: TNodeKind): PNode = +proc parseReturnOrRaise(p: var Parser, kind: TNodeKind): PNode = #| returnStmt = 'return' optInd expr? #| raiseStmt = 'raise' optInd expr? #| yieldStmt = 'yield' optInd expr? @@ -1552,7 +1550,7 @@ proc parseReturnOrRaise(p: var TParser, kind: TNodeKind): PNode = e = postExprBlocks(p, e) result.add(e) -proc parseIfOrWhen(p: var TParser, kind: TNodeKind): PNode = +proc parseIfOrWhen(p: var Parser, kind: TNodeKind): PNode = #| condStmt = expr colcom stmt COMMENT? #| (IND{=} 'elif' expr colcom stmt)* #| (IND{=} 'else' colcom stmt)? @@ -1576,7 +1574,7 @@ proc parseIfOrWhen(p: var TParser, kind: TNodeKind): PNode = branch.add(parseStmt(p)) result.add(branch) -proc parseWhile(p: var TParser): PNode = +proc parseWhile(p: var Parser): PNode = #| whileStmt = 'while' expr colcom stmt result = newNodeP(nkWhileStmt, p) getTok(p) @@ -1585,7 +1583,7 @@ proc parseWhile(p: var TParser): PNode = colcom(p, result) result.add(parseStmt(p)) -proc parseCase(p: var TParser): PNode = +proc parseCase(p: var Parser): PNode = #| ofBranch = 'of' exprList colcom stmt #| ofBranches = ofBranch (IND{=} ofBranch)* #| (IND{=} 'elif' expr colcom stmt)* @@ -1632,7 +1630,7 @@ proc parseCase(p: var TParser): PNode = if wasIndented: p.currInd = oldInd -proc parseTry(p: var TParser; isExpr: bool): PNode = +proc parseTry(p: var Parser; isExpr: bool): PNode = #| tryStmt = 'try' colcom stmt &(IND{=}? 'except'|'finally') #| (IND{=}? 'except' exprList colcom stmt)* #| (IND{=}? 'finally' colcom stmt)? @@ -1658,14 +1656,14 @@ proc parseTry(p: var TParser; isExpr: bool): PNode = result.add(b) if b == nil: parMessage(p, "expected 'except'") -proc parseExceptBlock(p: var TParser, kind: TNodeKind): PNode = +proc parseExceptBlock(p: var Parser, kind: TNodeKind): PNode = #| exceptBlock = 'except' colcom stmt result = newNodeP(kind, p) getTok(p) colcom(p, result) result.add(parseStmt(p)) -proc parseBlock(p: var TParser): PNode = +proc parseBlock(p: var Parser): PNode = #| blockStmt = 'block' symbol? colcom stmt #| blockExpr = 'block' symbol? colcom stmt result = newNodeP(nkBlockStmt, p) @@ -1675,7 +1673,7 @@ proc parseBlock(p: var TParser): PNode = colcom(p, result) result.add(parseStmt(p)) -proc parseStaticOrDefer(p: var TParser; k: TNodeKind): PNode = +proc parseStaticOrDefer(p: var Parser; k: TNodeKind): PNode = #| staticStmt = 'static' colcom stmt #| deferStmt = 'defer' colcom stmt result = newNodeP(k, p) @@ -1683,7 +1681,7 @@ proc parseStaticOrDefer(p: var TParser; k: TNodeKind): PNode = colcom(p, result) result.add(parseStmt(p)) -proc parseAsm(p: var TParser): PNode = +proc parseAsm(p: var Parser): PNode = #| asmStmt = 'asm' pragma? (STR_LIT | RSTR_LIT | TRIPLESTR_LIT) result = newNodeP(nkAsmStmt, p) getTokNoInd(p) @@ -1699,7 +1697,7 @@ proc parseAsm(p: var TParser): PNode = return getTok(p) -proc parseGenericParam(p: var TParser): PNode = +proc parseGenericParam(p: var Parser): PNode = #| genericParam = symbol (comma symbol)* (colon expr)? ('=' optInd expr)? var a: PNode result = newNodeP(nkIdentDefs, p) @@ -1734,7 +1732,7 @@ proc parseGenericParam(p: var TParser): PNode = else: result.add(p.emptyNode) -proc parseGenericParamList(p: var TParser): PNode = +proc parseGenericParamList(p: var Parser): PNode = #| genericParamList = '[' optInd #| genericParam ^* (comma/semicolon) optPar ']' result = newNodeP(nkGenericParams, p) @@ -1752,13 +1750,13 @@ proc parseGenericParamList(p: var TParser): PNode = optPar(p) eat(p, tkBracketRi) -proc parsePattern(p: var TParser): PNode = +proc parsePattern(p: var Parser): PNode = #| pattern = '{' stmt '}' eat(p, tkCurlyLe) result = parseStmt(p) eat(p, tkCurlyRi) -proc parseRoutine(p: var TParser, kind: TNodeKind): PNode = +proc parseRoutine(p: var Parser, kind: TNodeKind): PNode = #| indAndComment = (IND{>} COMMENT)? | COMMENT? #| routine = optInd identVis pattern? genericParamList? #| paramListColon pragma? ('=' COMMENT? stmt)? indAndComment @@ -1785,17 +1783,14 @@ proc parseRoutine(p: var TParser, kind: TNodeKind): PNode = result.add(p.emptyNode) indAndComment(p, result) -proc newCommentStmt(p: var TParser): PNode = +proc newCommentStmt(p: var Parser): PNode = #| commentStmt = COMMENT result = newNodeP(nkCommentStmt, p) result.comment = p.tok.literal getTok(p) -type - TDefParser = proc (p: var TParser): PNode {.nimcall.} - -proc parseSection(p: var TParser, kind: TNodeKind, - defparser: TDefParser): PNode = +proc parseSection(p: var Parser, kind: TNodeKind, + defparser: proc (p: var Parser): PNode {.nimcall.}): PNode = #| section(RULE) = COMMENT? RULE / (IND{>} (RULE / COMMENT)^+IND{=} DED) result = newNodeP(kind, p) if kind != nkTypeSection: getTok(p) @@ -1823,7 +1818,7 @@ proc parseSection(p: var TParser, kind: TNodeKind, else: parMessage(p, errIdentifierExpected, p.tok) -proc parseEnum(p: var TParser): PNode = +proc parseEnum(p: var Parser): PNode = #| enum = 'enum' optInd (symbol pragma? optInd ('=' optInd expr COMMENT?)? comma?)+ result = newNodeP(nkEnumTy, p) getTok(p) @@ -1869,8 +1864,8 @@ proc parseEnum(p: var TParser): PNode = if result.len <= 1: parMessage(p, errIdentifierExpected, p.tok) -proc parseObjectPart(p: var TParser): PNode -proc parseObjectWhen(p: var TParser): PNode = +proc parseObjectPart(p: var Parser): PNode +proc parseObjectWhen(p: var Parser): PNode = #| objectWhen = 'when' expr colcom objectPart COMMENT? #| ('elif' expr colcom objectPart COMMENT?)* #| ('else' colcom objectPart COMMENT?)? @@ -1894,7 +1889,7 @@ proc parseObjectWhen(p: var TParser): PNode = flexComment(p, branch) result.add(branch) -proc parseObjectCase(p: var TParser): PNode = +proc parseObjectCase(p: var Parser): PNode = #| objectBranch = 'of' exprList colcom objectPart #| objectBranches = objectBranch (IND{=} objectBranch)* #| (IND{=} 'elif' expr colcom objectPart)* @@ -1939,7 +1934,7 @@ proc parseObjectCase(p: var TParser): PNode = if wasIndented: p.currInd = oldInd -proc parseObjectPart(p: var TParser): PNode = +proc parseObjectPart(p: var Parser): PNode = #| objectPart = IND{>} objectPart^+IND{=} DED #| / objectWhen / objectCase / 'nil' / 'discard' / declColonEquals if realInd(p): @@ -1971,7 +1966,7 @@ proc parseObjectPart(p: var TParser): PNode = else: result = p.emptyNode -proc parseObject(p: var TParser): PNode = +proc parseObject(p: var Parser): PNode = #| object = 'object' pragma? ('of' typeDesc)? COMMENT? objectPart result = newNodeP(nkObjectTy, p) getTok(p) @@ -1996,7 +1991,7 @@ proc parseObject(p: var TParser): PNode = else: result.add(parseObjectPart(p)) -proc parseTypeClassParam(p: var TParser): PNode = +proc parseTypeClassParam(p: var Parser): PNode = let modifier = case p.tok.tokType of tkOut, tkVar: nkVarTy of tkPtr: nkPtrTy @@ -2012,7 +2007,7 @@ proc parseTypeClassParam(p: var TParser): PNode = else: result = p.parseSymbol -proc parseTypeClass(p: var TParser): PNode = +proc parseTypeClass(p: var Parser): PNode = #| typeClassParam = ('var' | 'out')? symbol #| typeClass = typeClassParam ^* ',' (pragma)? ('of' typeDesc ^* ',')? #| &IND{>} stmt @@ -2047,7 +2042,7 @@ proc parseTypeClass(p: var TParser): PNode = else: result.add(parseStmt(p)) -proc parseTypeDef(p: var TParser): PNode = +proc parseTypeDef(p: var Parser): PNode = #| #| typeDef = identWithPragmaDot genericParamList? '=' optInd typeDefAux #| indAndComment? / identVisDot genericParamList? pragma '=' optInd typeDefAux @@ -2095,7 +2090,7 @@ proc parseTypeDef(p: var TParser): PNode = result.add(p.emptyNode) indAndComment(p, result) # special extension! -proc parseVarTuple(p: var TParser): PNode = +proc parseVarTuple(p: var Parser): PNode = #| varTuple = '(' optInd identWithPragma ^+ comma optPar ')' '=' optInd expr result = newNodeP(nkVarTuple, p) getTok(p) # skip '(' @@ -2111,7 +2106,7 @@ proc parseVarTuple(p: var TParser): PNode = optPar(p) eat(p, tkParRi) -proc parseVariable(p: var TParser): PNode = +proc parseVariable(p: var Parser): PNode = #| colonBody = colcom stmt postExprBlocks? #| variable = (varTuple / identColonEquals) colonBody? indAndComment if p.tok.tokType == tkParLe: @@ -2123,7 +2118,7 @@ proc parseVariable(p: var TParser): PNode = result[^1] = postExprBlocks(p, result[^1]) indAndComment(p, result) -proc parseConstant(p: var TParser): PNode = +proc parseConstant(p: var Parser): PNode = #| constant = (varTuple / identWithPragma) (colon typeDesc)? '=' optInd expr indAndComment if p.tok.tokType == tkParLe: result = parseVarTuple(p) else: @@ -2142,7 +2137,7 @@ proc parseConstant(p: var TParser): PNode = result[^1] = postExprBlocks(p, result[^1]) indAndComment(p, result) -proc parseBind(p: var TParser, k: TNodeKind): PNode = +proc parseBind(p: var Parser, k: TNodeKind): PNode = #| bindStmt = 'bind' optInd qualifiedIdent ^+ comma #| mixinStmt = 'mixin' optInd qualifiedIdent ^+ comma result = newNodeP(k, p) @@ -2157,7 +2152,7 @@ proc parseBind(p: var TParser, k: TNodeKind): PNode = optInd(p, a) #expectNl(p) -proc parseStmtPragma(p: var TParser): PNode = +proc parseStmtPragma(p: var Parser): PNode = #| pragmaStmt = pragma (':' COMMENT? stmt)? result = parsePragma(p) if p.tok.tokType == tkColon and p.tok.indent < 0: @@ -2168,7 +2163,7 @@ proc parseStmtPragma(p: var TParser): PNode = result.add a result.add parseStmt(p) -proc simpleStmt(p: var TParser): PNode = +proc simpleStmt(p: var Parser): PNode = #| simpleStmt = ((returnStmt | raiseStmt | yieldStmt | discardStmt | breakStmt #| | continueStmt | pragmaStmt | importStmt | exportStmt | fromStmt #| | includeStmt | commentStmt) / exprStmt) COMMENT? @@ -2191,7 +2186,7 @@ proc simpleStmt(p: var TParser): PNode = else: result = p.emptyNode if result.kind notin {nkEmpty, nkCommentStmt}: skipComment(p, result) -proc complexOrSimpleStmt(p: var TParser): PNode = +proc complexOrSimpleStmt(p: var Parser): PNode = #| complexOrSimpleStmt = (ifStmt | whenStmt | whileStmt #| | tryStmt | forStmt #| | blockStmt | staticStmt | deferStmt | asmStmt @@ -2251,7 +2246,7 @@ proc complexOrSimpleStmt(p: var TParser): PNode = of tkUsing: result = parseSection(p, nkUsingStmt, parseVariable) else: result = simpleStmt(p) -proc parseStmt(p: var TParser): PNode = +proc parseStmt(p: var Parser): PNode = #| stmt = (IND{>} complexOrSimpleStmt^+(IND{=} / ';') DED) #| / simpleStmt ^+ ';' if p.tok.indent > p.currInd: @@ -2311,7 +2306,7 @@ proc parseStmt(p: var TParser): PNode = getTok(p) if err and p.tok.tokType == tkEof: break -proc parseAll(p: var TParser): PNode = +proc parseAll(p: var Parser): PNode = ## Parses the rest of the input stream held by the parser into a PNode. result = newNodeP(nkStmtList, p) while p.tok.tokType != tkEof: @@ -2326,7 +2321,7 @@ proc parseAll(p: var TParser): PNode = if p.tok.indent != 0: parMessage(p, errInvalidIndentation) -proc parseTopLevelStmt(p: var TParser): PNode = +proc parseTopLevelStmt(p: var Parser): PNode = ## Implements an iterator which, when called repeatedly, returns the next ## top-level statement or emptyNode if end of stream. result = p.emptyNode @@ -2357,7 +2352,7 @@ proc parseTopLevelStmt(p: var TParser): PNode = proc parseString*(s: string; cache: IdentCache; config: ConfigRef; filename: string = ""; line: int = 0; - errorHandler: TErrorHandler = nil): PNode = + errorHandler: ErrorHandler = nil): PNode = ## Parses a string into an AST, returning the top node. ## `filename` and `line`, although optional, provide info so that the ## compiler can generate correct error messages referring to the original @@ -2365,7 +2360,7 @@ proc parseString*(s: string; cache: IdentCache; config: ConfigRef; var stream = llStreamOpen(s) stream.lineOffset = line - var parser: TParser + var parser: Parser parser.lex.errorHandler = errorHandler openParser(parser, AbsoluteFile filename, stream, cache, config) diff --git a/compiler/passes.nim b/compiler/passes.nim index 615b9ac88..2d30ebbb5 100644 --- a/compiler/passes.nim +++ b/compiler/passes.nim @@ -126,7 +126,7 @@ proc moduleHasChanged*(graph: ModuleGraph; module: PSym): bool {.inline.} = proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream): bool {.discardable.} = if graph.stopCompile(): return true var - p: TParsers + p: Parser a: TPassContextArray s: PLLStream fileIdx = module.fileIdx @@ -164,7 +164,7 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream): bool { else: s = stream while true: - openParsers(p, fileIdx, s, graph.cache, graph.config) + openParser(p, fileIdx, s, graph.cache, graph.config) if module.owner == nil or module.owner.name.s != "stdlib" or module.name.s == "distros": # XXX what about caching? no processing then? what if I change the @@ -212,7 +212,7 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream): bool { else: #echo "----- single\n", n if not processTopLevelStmt(graph, n, a): break - closeParsers(p) + closeParser(p) if s.kind != llsStdIn: break closePasses(graph, a) # id synchronization point for more consistent code generation: diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 44ff90010..b2529a5b8 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -238,7 +238,7 @@ proc processMagic(c: PContext, n: PNode, s: PSym) = var v: string if n[1].kind == nkIdent: v = n[1].ident.s else: v = expectStrLit(c, n) - for m in low(TMagic)..high(TMagic): + for m in TMagic: if substr($m, 1) == v: s.magic = m break @@ -257,8 +257,8 @@ proc isTurnedOn(c: PContext, n: PNode): bool = localError(c.config, n.info, "'on' or 'off' expected") proc onOff(c: PContext, n: PNode, op: TOptions, resOptions: var TOptions) = - if isTurnedOn(c, n): resOptions = resOptions + op - else: resOptions = resOptions - op + if isTurnedOn(c, n): resOptions.incl op + else: resOptions.excl op proc pragmaNoForward(c: PContext, n: PNode; flag=sfNoForward) = if isTurnedOn(c, n): diff --git a/compiler/renderer.nim b/compiler/renderer.nim index f600d58bf..9601bf082 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -24,7 +24,7 @@ type renderIr TRenderFlags* = set[TRenderFlag] TRenderTok* = object - kind*: TTokType + kind*: TokType length*: int16 sym*: PSym @@ -118,7 +118,7 @@ proc initSrcGen(g: var TSrcGen, renderFlags: TRenderFlags; config: ConfigRef) = g.inGenericParams = false g.config = config -proc addTok(g: var TSrcGen, kind: TTokType, s: string; sym: PSym = nil) = +proc addTok(g: var TSrcGen, kind: TokType, s: string; sym: PSym = nil) = setLen(g.tokens, g.tokens.len + 1) g.tokens[^1].kind = kind g.tokens[^1].length = int16(s.len) @@ -178,7 +178,7 @@ proc dedent(g: var TSrcGen) = dec(g.pendingNL, IndentWidth) dec(g.lineLen, IndentWidth) -proc put(g: var TSrcGen, kind: TTokType, s: string; sym: PSym = nil) = +proc put(g: var TSrcGen, kind: TokType, s: string; sym: PSym = nil) = if kind != tkSpaces: addPendingNL(g) if s.len > 0: @@ -250,7 +250,7 @@ proc maxLineLength(s: string): int = inc(lineLen) inc(i) -proc putRawStr(g: var TSrcGen, kind: TTokType, s: string) = +proc putRawStr(g: var TSrcGen, kind: TokType, s: string) = var i = 0 let hi = s.len - 1 var str = "" @@ -459,8 +459,8 @@ proc lsub(g: TSrcGen; n: PNode): int = of nkDo: result = lsons(g, n) + len("do__:_") of nkConstDef, nkIdentDefs: result = lcomma(g, n, 0, - 3) - if n[^2].kind != nkEmpty: result = result + lsub(g, n[^2]) + 2 - if n[^1].kind != nkEmpty: result = result + lsub(g, n[^1]) + 3 + if n[^2].kind != nkEmpty: result += lsub(g, n[^2]) + 2 + if n[^1].kind != nkEmpty: result += lsub(g, n[^1]) + 3 of nkVarTuple: if n[^1].kind == nkEmpty: result = lcomma(g, n, 0, - 2) + len("()") @@ -471,8 +471,8 @@ proc lsub(g: TSrcGen; n: PNode): int = of nkChckRange: result = len("chckRange") + 2 + lcomma(g, n) of nkObjDownConv, nkObjUpConv: result = 2 - if n.len >= 1: result = result + lsub(g, n[0]) - result = result + lcomma(g, n, 1) + if n.len >= 1: result += lsub(g, n[0]) + result += lcomma(g, n, 1) of nkExprColonExpr: result = lsons(g, n) + 2 of nkInfix: result = lsons(g, n) + 2 of nkPrefix: @@ -536,7 +536,7 @@ proc lsub(g: TSrcGen; n: PNode): int = of nkGenericParams: result = lcomma(g, n) + 2 of nkFormalParams: result = lcomma(g, n, 1) + 2 - if n[0].kind != nkEmpty: result = result + lsub(g, n[0]) + 2 + if n[0].kind != nkEmpty: result += lsub(g, n[0]) + 2 of nkExceptBranch: result = lcomma(g, n, 0, -2) + lsub(g, lastSon(n)) + len("except_:_") of nkObjectTy: @@ -575,7 +575,7 @@ proc hasCom(n: PNode): bool = for i in 0.. 0 and s[0] in lexer.SymChars: if n.kind == nkIdent: @@ -855,7 +855,7 @@ proc gident(g: var TSrcGen, n: PNode) = (n.ident.id > ord(tokKeywordHigh) - ord(tkSymbol)): t = tkSymbol else: - t = TTokType(n.ident.id + ord(tkSymbol)) + t = TokType(n.ident.id + ord(tkSymbol)) else: t = tkSymbol else: @@ -1644,7 +1644,7 @@ proc initTokRender*(r: var TSrcGen, n: PNode, renderFlags: TRenderFlags = {}) = initSrcGen(r, renderFlags, newPartialConfigRef()) gsub(r, n) -proc getNextTok*(r: var TSrcGen, kind: var TTokType, literal: var string) = +proc getNextTok*(r: var TSrcGen, kind: var TokType, literal: var string) = if r.idx < r.tokens.len: kind = r.tokens[r.idx].kind let length = r.tokens[r.idx].length.int diff --git a/compiler/renderverbatim.nim b/compiler/renderverbatim.nim index 2dce6824c..02d405844 100644 --- a/compiler/renderverbatim.nim +++ b/compiler/renderverbatim.nim @@ -107,7 +107,6 @@ proc extractRunnableExamplesSource*(conf: ConfigRef; n: PNode): string = var indent = info.col let numLines = numLines(conf, info.fileIndex).uint16 var lastNonemptyPos = 0 - result = "" var ldata = LineData(lineFirst: first.line.int, conf: conf) visitMultilineStrings(ldata, n[^1]) @@ -116,6 +115,7 @@ proc extractRunnableExamplesSource*(conf: ConfigRef; n: PNode): string = for i in 0.. 0 - if nb == 1: - return parts[0] - result = parts[0] / parts[1] - for i in 2..= 0 and idx < x.len: result = x[int(idx)] else: localError(g.config, n.info, formatErrorIndexBound(idx, x.len-1) & $n) of nkStrLit..nkTripleStrLit: diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 1a7bd9b6b..ffeed00d6 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -66,7 +66,7 @@ iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable): PSym continue let symKind = if q.typ.kind == tyStatic: skConst else: skType var s = newSym(symKind, q.name, getCurrOwner(c), q.info) - s.flags = s.flags + {sfUsed, sfFromGeneric} + s.flags.incl {sfUsed, sfFromGeneric} var t = PType(idTableGet(pt, q.typ)) if t == nil: if tfRetType in q.typ.flags: diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim index cd5a52fe9..64a27d1db 100644 --- a/compiler/semobjconstr.nim +++ b/compiler/semobjconstr.nim @@ -139,8 +139,7 @@ template quoteStr(s: string): string = "'" & s & "'" proc fieldsPresentInInitExpr(c: PContext, fieldsRecList, initExpr: PNode): string = result = "" for field in directFieldsInRecList(fieldsRecList): - let assignment = locateFieldInInitExpr(c, field.sym, initExpr) - if assignment != nil: + if locateFieldInInitExpr(c, field.sym, initExpr) != nil: if result.len != 0: result.add ", " result.add field.sym.name.s.quoteStr diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index e771e17c4..a1afd2c6d 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -336,7 +336,6 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = let s = qualifiedLookUp(c.c, n, {}) if s != nil: if s.owner == c.owner and s.kind == skParam and sfTemplateParam in s.flags: - # oldCheck(c, sfGenSym notin s.flags or c.noGenSym == 0): incl(s.flags, sfUsed) result = newSymNode(s, n.info) onUse(n.info, s) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 6a1c1afc9..953801431 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -786,7 +786,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, {sfImportc, sfExportc} * fieldOwner.flags != {} and not hasCaseFields and f.loc.r == nil: f.loc.r = rope(f.name.s) - f.flags = f.flags + ({sfImportc, sfExportc} * fieldOwner.flags) + f.flags.incl {sfImportc, sfExportc} * fieldOwner.flags inc(pos) if containsOrIncl(check, f.name.id): localError(c.config, info, "attempt to redefine: '" & f.name.s & "'") @@ -1211,11 +1211,9 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, if hasType: typ = semParamType(c, a[^2], constraint) - let sym = getCurrOwner(c) - var owner = sym.owner # TODO: Disallow typed/untyped in procs in the compiler/stdlib if kind == skProc and (typ.kind == tyTyped or typ.kind == tyUntyped): - if not isMagic(sym): + if not isMagic(getCurrOwner(c)): localError(c.config, a[^2].info, "'" & typ.sym.name.s & "' is only allowed in templates and macros or magic procs") if hasDefault: diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index e7b289d77..5fa3880af 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -12,8 +12,7 @@ import ast, astalgo, msgs, types, magicsys, semdata, renderer, options, lineinfos -const - tfInstClearedFlags = {tfHasMeta, tfUnresolved} +const tfInstClearedFlags = {tfHasMeta, tfUnresolved} proc checkPartialConstructedType(conf: ConfigRef; info: TLineInfo, t: PType) = if t.kind in {tyVar, tyLent} and t[0].kind in {tyVar, tyLent}: diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim index 0902a839b..9a16cc3e0 100644 --- a/compiler/sighashes.nim +++ b/compiler/sighashes.nim @@ -157,7 +157,7 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) = if t.n.len > 0: let oldFlags = t.sym.flags # Mild hack to prevent endless recursion. - t.sym.flags = t.sym.flags - {sfAnon, sfGenSym} + t.sym.flags.excl {sfAnon, sfGenSym} hashTree(c, t.n, flags + {CoHashTypeInsideNode}) t.sym.flags = oldFlags else: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 4be84d659..95231744c 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -265,17 +265,12 @@ proc sumGeneric(t: PType): int = else: return 0 -#var ggDebug: bool - proc complexDisambiguation(a, b: PType): int = # 'a' matches better if *every* argument matches better or equal than 'b'. var winner = 0 for i in 1.. y: winner = 1 @@ -337,14 +332,13 @@ proc argTypeToString(arg: PNode; prefer: TPreferedDesc): string = else: result = arg.typ.typeToString(prefer) -proc describeArgs*(c: PContext, n: PNode, startIdx = 1; - prefer: TPreferedDesc = preferName): string = +proc describeArgs*(c: PContext, n: PNode, startIdx = 1; prefer = preferName): string = result = "" for i in startIdx.. 1: m.callee.n[1].sym else: nil + + var + a = 1 # iterates over the actual given arguments + f = if m.callee.kind != tyGenericBody: 1 + else: 0 # iterates over formal parameters + arg: PNode # current prepared argument + formalLen = m.callee.n.len + formal = if formalLen > 1: m.callee.n[1].sym else: nil # current routine parameter + container: PNode = nil # constructed container while a < n.len: - c.openShadowScope if a >= formalLen-1 and f < formalLen and m.callee.n[f].typ.isVarargsUntyped: @@ -2493,7 +2478,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, f = max(f, formalLen - n.len + a + 1) elif formal.typ.kind != tyVarargs or container == nil: setSon(m.call, formal.position + 1, arg) - inc(f) + inc f container = nil else: # we end up here if the argument can be converted into the varargs @@ -2511,7 +2496,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, else: c.closeShadowScope - inc(a) + inc a # for some edge cases (see tdont_return_unowned_from_owned test case) m.firstMismatch.arg = a m.firstMismatch.formal = formal diff --git a/compiler/sourcemap.nim b/compiler/sourcemap.nim index 90e34eb73..2ec40227b 100644 --- a/compiler/sourcemap.nim +++ b/compiler/sourcemap.nim @@ -78,8 +78,7 @@ proc text*(sourceNode: SourceNode, depth: int): string = result.add(child.node.text(depth + 1)) -proc `$`*(sourceNode: SourceNode): string = - text(sourceNode, 0) +proc `$`*(sourceNode: SourceNode): string = text(sourceNode, 0) # base64_VLQ @@ -107,8 +106,7 @@ proc encode*(i: int): string = z += 1 -type - TokenState = enum Normal, String, Ident, Mangled +type TokenState = enum Normal, String, Ident, Mangled iterator tokenize*(line: string): (bool, string) = # result = @[] @@ -244,10 +242,10 @@ proc serializeMappings(map: SourceMapGenerator, mappings: seq[Mapping]): string var previous = Mapping(generated: (line: 1, column: 0), original: (line: 0, column: 0), name: "", source: "") var previousSourceId = 0 var previousNameId = 0 - result = "" var next = "" var nameId = 0 var sourceId = 0 + result = "" for z, mapping in mappings: next = "" diff --git a/compiler/suggest.nim b/compiler/suggest.nim index 03c65cd42..4943603ce 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -33,7 +33,7 @@ # included from sigmatch.nim import algorithm, sets, prefixmatches, lineinfos, parseutils, linter -from wordrecg import wDeprecated, wError, wAddr, wYield, specialWords +from wordrecg import wDeprecated, wError, wAddr, wYield when defined(nimsuggest): import passes, tables, pathutils # importer diff --git a/compiler/syntaxes.nim b/compiler/syntaxes.nim index c3f3bc0dc..e745e28ba 100644 --- a/compiler/syntaxes.nim +++ b/compiler/syntaxes.nim @@ -13,44 +13,20 @@ import strutils, llstream, ast, idents, lexer, options, msgs, parser, filters, filter_tmpl, renderer, lineinfos, pathutils -type - TFilterKind* = enum - filtNone, filtTemplate, filtReplace, filtStrip - TParserKind* = enum - skinStandard, skinEndX - -const - parserNames*: array[TParserKind, string] = ["standard", - "endx"] - filterNames*: array[TFilterKind, string] = ["none", "stdtmpl", "replace", - "strip"] +export Parser, parseAll, parseTopLevelStmt, closeParser type - TParsers* = object - skin*: TParserKind - parser*: TParser - -template config(p: TParsers): ConfigRef = p.parser.lex.config - -proc parseAll*(p: var TParsers): PNode = - case p.skin - of skinStandard: - result = parser.parseAll(p.parser) - of skinEndX: - internalError(p.config, "parser to implement") - -proc parseTopLevelStmt*(p: var TParsers): PNode = - case p.skin - of skinStandard: - result = parser.parseTopLevelStmt(p.parser) - of skinEndX: - internalError(p.config, "parser to implement") + FilterKind = enum + filtNone = "none" + filtTemplate = "stdtmpl" + filtReplace = "replace" + filtStrip = "strip" proc utf8Bom(s: string): int = if s.len >= 3 and s[0] == '\xEF' and s[1] == '\xBB' and s[2] == '\xBF': - result = 3 + 3 else: - result = 0 + 0 proc containsShebang(s: string, i: int): bool = if i+1 < s.len and s[i] == '#' and s[i+1] == '!': @@ -78,23 +54,16 @@ proc parsePipe(filename: AbsoluteFile, inputStream: PLLStream; cache: IdentCache else: inc(i, 2) while i < line.len and line[i] in Whitespace: inc(i) - var q: TParser - parser.openParser(q, filename, llStreamOpen(substr(line, i)), cache, config) - result = parser.parseAll(q) - parser.closeParser(q) + var p: Parser + openParser(p, filename, llStreamOpen(substr(line, i)), cache, config) + result = parseAll(p) + closeParser(p) llStreamClose(s) -proc getFilter(ident: PIdent): TFilterKind = - for i in low(TFilterKind)..high(TFilterKind): - if cmpIgnoreStyle(ident.s, filterNames[i]) == 0: +proc getFilter(ident: PIdent): FilterKind = + for i in FilterKind: + if cmpIgnoreStyle(ident.s, $i) == 0: return i - result = filtNone - -proc getParser(conf: ConfigRef; n: PNode; ident: PIdent): TParserKind = - for i in low(TParserKind)..high(TParserKind): - if cmpIgnoreStyle(ident.s, parserNames[i]) == 0: - return i - localError(conf, n.info, "unknown parser: " & ident.s) proc getCallee(conf: ConfigRef; n: PNode): PIdent = if n.kind in nkCallKinds and n[0].kind == nkIdent: @@ -104,30 +73,28 @@ proc getCallee(conf: ConfigRef; n: PNode): PIdent = else: localError(conf, n.info, "invalid filter: " & renderTree(n)) -proc applyFilter(p: var TParsers, n: PNode, filename: AbsoluteFile, +proc applyFilter(p: var Parser, n: PNode, filename: AbsoluteFile, stdin: PLLStream): PLLStream = - var ident = getCallee(p.config, n) - var f = getFilter(ident) - case f - of filtNone: - p.skin = getParser(p.config, n, ident) - result = stdin - of filtTemplate: - result = filterTmpl(stdin, filename, n, p.config) - of filtStrip: - result = filterStrip(p.config, stdin, filename, n) - of filtReplace: - result = filterReplace(p.config, stdin, filename, n) + var f = getFilter(getCallee(p.lex.config, n)) + result = case f + of filtNone: + stdin + of filtTemplate: + filterTmpl(p.lex.config, stdin, filename, n) + of filtStrip: + filterStrip(p.lex.config, stdin, filename, n) + of filtReplace: + filterReplace(p.lex.config, stdin, filename, n) if f != filtNone: - assert p.config != nil - if p.config.hasHint(hintCodeBegin): - rawMessage(p.config, hintCodeBegin, "") - msgWriteln(p.config, result.s) - rawMessage(p.config, hintCodeEnd, "") + assert p.lex.config != nil + if p.lex.config.hasHint(hintCodeBegin): + rawMessage(p.lex.config, hintCodeBegin, "") + msgWriteln(p.lex.config, result.s) + rawMessage(p.lex.config, hintCodeEnd, "") -proc evalPipe(p: var TParsers, n: PNode, filename: AbsoluteFile, +proc evalPipe(p: var Parser, n: PNode, filename: AbsoluteFile, start: PLLStream): PLLStream = - assert p.config != nil + assert p.lex.config != nil result = start if n.kind == nkEmpty: return if n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.s == "|": @@ -141,35 +108,28 @@ proc evalPipe(p: var TParsers, n: PNode, filename: AbsoluteFile, else: result = applyFilter(p, n, filename, result) -proc openParsers*(p: var TParsers, fileIdx: FileIndex, inputstream: PLLStream; +proc openParser*(p: var Parser, fileIdx: FileIndex, inputstream: PLLStream; cache: IdentCache; config: ConfigRef) = assert config != nil - var s: PLLStream - p.skin = skinStandard let filename = toFullPathConsiderDirty(config, fileIdx) var pipe = parsePipe(filename, inputstream, cache, config) - p.config() = config - if pipe != nil: s = evalPipe(p, pipe, filename, inputstream) - else: s = inputstream - case p.skin - of skinStandard, skinEndX: - parser.openParser(p.parser, fileIdx, s, cache, config) + p.lex.config = config + let s = if pipe != nil: evalPipe(p, pipe, filename, inputstream) + else: inputstream + parser.openParser(p, fileIdx, s, cache, config) -proc closeParsers*(p: var TParsers) = - parser.closeParser(p.parser) - -proc setupParsers*(p: var TParsers; fileIdx: FileIndex; cache: IdentCache; +proc setupParser*(p: var Parser; fileIdx: FileIndex; cache: IdentCache; config: ConfigRef): bool = - var f: File let filename = toFullPathConsiderDirty(config, fileIdx) + var f: File if not open(f, filename.string): rawMessage(config, errGenerated, "cannot open file: " & filename.string) return false - openParsers(p, fileIdx, llStreamOpen(f), cache, config) + openParser(p, fileIdx, llStreamOpen(f), cache, config) result = true proc parseFile*(fileIdx: FileIndex; cache: IdentCache; config: ConfigRef): PNode = - var p: TParsers - if setupParsers(p, fileIdx, cache, config): + var p: Parser + if setupParser(p, fileIdx, cache, config): result = parseAll(p) - closeParsers(p) + closeParser(p) diff --git a/compiler/types.nim b/compiler/types.nim index b8452c3ea..44827aa33 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -469,9 +469,9 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = prefer proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = + result = "" let prefer = getPrefer(prefer) let t = typ - result = "" if t == nil: return if prefer in preferToResolveSymbols and t.sym != nil and sfAnon notin t.sym.flags and t.kind != tySequence: diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 4ef510d20..c2a2f6d42 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1360,19 +1360,6 @@ proc genMarshalStore(c: PCtx, n: PNode, dest: var TDest) = c.gABx(n, opcMarshalStore, 0, c.genType(n[1].typ)) c.freeTemp(tmp) -const - atomicTypes = {tyBool, tyChar, - tyUntyped, tyTyped, tyTypeDesc, tyStatic, - tyEnum, - tyOrdinal, - tyRange, - tyProc, - tyPointer, tyOpenArray, - tyString, tyCString, - tyInt, tyInt8, tyInt16, tyInt32, tyInt64, - tyFloat, tyFloat32, tyFloat64, tyFloat128, - tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64} - proc unneededIndirection(n: PNode): bool = n.typ.skipTypes(abstractInstOwned-{tyTypeDesc}).kind == tyRef @@ -2200,7 +2187,7 @@ proc optimizeJumps(c: PCtx; start: int) = for iters in countdown(maxIterations, 0): case c.code[d].opcode of opcJmp: - d = d + c.code[d].jmpDiff + d += c.code[d].jmpDiff of opcTJmp, opcFJmp: if c.code[d].regA != reg: break # tjmp x, 23 @@ -2208,12 +2195,12 @@ proc optimizeJumps(c: PCtx; start: int) = # tjmp x, 12 # -- we know 'x' is true, and so can jump to 12+13: if c.code[d].opcode == opc: - d = d + c.code[d].jmpDiff + d += c.code[d].jmpDiff else: # tjmp x, 23 # fjmp x, 22 # We know 'x' is true so skip to the next instruction: - d = d + 1 + d += 1 else: break if d != i + c.code[i].jmpDiff: c.finalJumpTarget(i, d - i) @@ -2221,7 +2208,7 @@ proc optimizeJumps(c: PCtx; start: int) = var d = i + c.code[i].jmpDiff var iters = maxIterations while c.code[d].opcode == opcJmp and iters > 0: - d = d + c.code[d].jmpDiff + d += c.code[d].jmpDiff dec iters if c.code[d].opcode == opcRet: # optimize 'jmp to ret' to 'ret' here diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index 454224ae4..e8d03fe4b 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -104,8 +104,7 @@ const wAsm, wBreak, wCase, wConst, wContinue, wDo, wElse, wEnum, wExport, wFor, wIf, wReturn, wStatic, wTemplate, wTry, wWhile, wUsing} - specialWords*: array[low(TSpecialWord)..high(TSpecialWord), string] = ["", - + specialWords*: array[TSpecialWord, string] = ["", "addr", "and", "as", "asm", "bind", "block", "break", "case", "cast", "concept", "const", "continue", "converter", @@ -129,35 +128,35 @@ const "immediate", "constructor", "destructor", "delegator", "override", "importcpp", "importobjc", - "importcompilerproc", "importc", "importjs", "exportc", "exportcpp", "exportnims", - "incompletestruct", - "completestruct", - "requiresinit", "align", "nodecl", "pure", "sideeffect", - "header", "nosideeffect", "gcsafe", "noreturn", "nosinks", "merge", "lib", "dynlib", + "importCompilerProc", "importc", "importjs", "exportc", "exportcpp", "exportnims", + "incompleteStruct", + "completeStruct", + "requiresInit", "align", "nodecl", "pure", "sideEffect", + "header", "noSideEffect", "gcsafe", "noreturn", "nosinks", "merge", "lib", "dynlib", "compilerproc", "core", "procvar", "base", "used", - "fatal", "error", "warning", "hint", "warningaserror", "line", - "push", "pop", "define", "undef", "linedir", "stacktrace", "linetrace", + "fatal", "error", "warning", "hint", "warningAsError", "line", + "push", "pop", "define", "undef", "lineDir", "stackTrace", "lineTrace", "link", "compile", "linksys", "deprecated", "varargs", "callconv", "debugger", "nimcall", "stdcall", "cdecl", "safecall", "syscall", "inline", "noinline", "fastcall", "thiscall", "closure", - "noconv", "on", "off", "checks", "rangechecks", "boundchecks", - "overflowchecks", "nilchecks", - "floatchecks", "nanchecks", "infchecks", "stylechecks", "staticboundchecks", - "nonreloadable", "executeonreload", + "noconv", "on", "off", "checks", "rangeChecks", "boundChecks", + "overflowChecks", "nilChecks", + "floatChecks", "nanChecks", "infChecks", "styleChecks", "staticBoundChecks", + "nonReloadable", "executeOnReload", "assertions", "patterns", "trmacros", "sinkinference", "warnings", "hints", "optimization", "raises", "writes", "reads", "size", "effects", "tags", "requires", "ensures", "invariant", "assume", "assert", - "deadcodeelim", # deprecated, dead code elim always happens + "deadCodeElim", # deprecated, dead code elim always happens "safecode", "package", "noforward", "reorder", "norewrite", "nodestroy", "pragma", - "compiletime", "noinit", - "passc", "passl", "localpassc", "borrow", "discardable", "fieldchecks", - "subschar", "acyclic", "shallow", "unroll", "linearscanend", - "computedgoto", "injectstmt", "experimental", + "compileTime", "noinit", + "passc", "passl", "localPassC", "borrow", "discardable", "fieldChecks", + "subschar", "acyclic", "shallow", "unroll", "linearScanEnd", + "computedGoto", "injectStmt", "experimental", "write", "gensym", "inject", "dirty", "inheritable", "threadvar", "emit", - "asmnostackframe", "implicitstatic", "global", "codegendecl", "unchecked", - "guard", "locks", "partial", "explain", "liftlocals", + "asmNoStackFrame", "implicitStatic", "global", "codegenDecl", "unchecked", + "guard", "locks", "partial", "explain", "liftLocals", "auto", "bool", "catch", "char", "class", "compl", "const_cast", "default", "delete", "double", @@ -184,38 +183,3 @@ proc findStr*(a: openArray[string], s: string): int = if cmpIgnoreStyle(a[i], s) == 0: return i result = - 1 - -proc canonPragmaSpelling*(w: TSpecialWord): string = - case w - of wNoSideEffect: "noSideEffect" - of wImportCompilerProc: "importCompilerProc" - of wIncompleteStruct: "incompleteStruct" - of wCompleteStruct: "completeStruct" - of wRequiresInit: "requiresInit" - of wSideEffect: "sideEffect" - of wLineDir: "lineDir" - of wStackTrace: "stackTrace" - of wLineTrace: "lineTrace" - of wRangeChecks: "rangeChecks" - of wBoundChecks: "boundChecks" - of wOverflowChecks: "overflowChecks" - of wNilChecks: "nilChecks" - of wFloatChecks: "floatChecks" - of wNanChecks: "nanChecks" - of wInfChecks: "infChecks" - of wStyleChecks: "styleChecks" - of wNonReloadable: "nonReloadable" - of wExecuteOnReload: "executeOnReload" - of wDeadCodeElimUnused: "deadCodeElim" - of wCompileTime: "compileTime" - of wFieldChecks: "fieldChecks" - of wLinearScanEnd: "linearScanEnd" - of wComputedGoto: "computedGoto" - of wInjectStmt: "injectStmt" - of wAsmNoStackFrame: "asmNoStackFrame" - of wImplicitStatic: "implicitStatic" - of wCodegenDecl: "codegenDecl" - of wLiftLocals: "liftLocals" - of wLocalPassc: "localPassc" - of wWarningAsError: "warningAsError" - else: specialWords[w] diff --git a/nimpretty/nimpretty.nim b/nimpretty/nimpretty.nim index bedf9f248..0fb2c6a9e 100644 --- a/nimpretty/nimpretty.nim +++ b/nimpretty/nimpretty.nim @@ -54,12 +54,12 @@ proc prettyPrint(infile, outfile: string, opt: PrettyOptions) = let f = splitFile(outfile.expandTilde) conf.outFile = RelativeFile f.name & f.ext conf.outDir = toAbsoluteDir f.dir - var p: TParsers - p.parser.em.indWidth = opt.indWidth - if setupParsers(p, fileIdx, newIdentCache(), conf): - p.parser.em.maxLineLen = opt.maxLineLen - discard parseAll(p) - closeParsers(p) + var parser: Parser + parser.em.indWidth = opt.indWidth + if setupParser(parser, fileIdx, newIdentCache(), conf): + parser.em.maxLineLen = opt.maxLineLen + discard parseAll(parser) + closeParser(parser) proc main = var outfile, outdir: string diff --git a/tools/grammar_nanny.nim b/tools/grammar_nanny.nim index d07c2bf8c..397041559 100644 --- a/tools/grammar_nanny.nim +++ b/tools/grammar_nanny.nim @@ -16,8 +16,8 @@ proc checkGrammarFileImpl(cache: IdentCache, config: ConfigRef) = if stream != nil: declaredSyms.incl "section" # special case for 'section(RULE)' in the grammar var - L: TLexer - tok: TToken + L: Lexer + tok: Token initToken(tok) openLexer(L, f, stream, cache, config) # load the first token: diff --git a/tools/nimfind.nim b/tools/nimfind.nim index 05980d740..4ad5fbb4f 100644 --- a/tools/nimfind.nim +++ b/tools/nimfind.nim @@ -195,8 +195,7 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string; conf: ConfigRef) = of cmdArgument: let info = p.key.split(':') if info.len == 3: - let (dir, file, ext) = info[0].splitFile() - conf.projectName = findProjectNimFile(conf, dir) + conf.projectName = findProjectNimFile(conf, info[0].splitFile.dir) if conf.projectName.len == 0: conf.projectName = info[0] try: conf.m.trackPos = newLineInfo(conf, AbsoluteFile info[0], From b5424b826e57c791a751bf31b69549c7d8daab0b Mon Sep 17 00:00:00 2001 From: Andy Davidoff Date: Sat, 29 Aug 2020 08:06:11 -0400 Subject: [PATCH 0562/3768] template hygiene (#15240) --- lib/pure/os.nim | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 8bbceddeb..e7087acf7 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -3080,14 +3080,15 @@ template rawToFormalFileInfo(rawInfo, path, formalInfo): untyped = formalInfo.permissions = {fpUserExec, fpUserRead, fpGroupExec, fpGroupRead, fpOthersExec, fpOthersRead} else: - result.permissions = {fpUserExec..fpOthersRead} + formalInfo.permissions = {fpUserExec..fpOthersRead} # Retrieve basic file kind - result.kind = pcFile if (rawInfo.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) != 0'i32: formalInfo.kind = pcDir + else: + formalInfo.kind = pcFile if (rawInfo.dwFileAttributes and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32: - formalInfo.kind = succ(result.kind) + formalInfo.kind = succ(formalInfo.kind) else: template checkAndIncludeMode(rawMode, formalMode: untyped) = @@ -3100,7 +3101,7 @@ template rawToFormalFileInfo(rawInfo, path, formalInfo): untyped = formalInfo.lastWriteTime = rawInfo.st_mtim.toTime formalInfo.creationTime = rawInfo.st_ctim.toTime - result.permissions = {} + formalInfo.permissions = {} checkAndIncludeMode(S_IRUSR, fpUserRead) checkAndIncludeMode(S_IWUSR, fpUserWrite) checkAndIncludeMode(S_IXUSR, fpUserExec) @@ -3113,12 +3114,14 @@ template rawToFormalFileInfo(rawInfo, path, formalInfo): untyped = checkAndIncludeMode(S_IWOTH, fpOthersWrite) checkAndIncludeMode(S_IXOTH, fpOthersExec) - formalInfo.kind = pcFile - if S_ISDIR(rawInfo.st_mode): - formalInfo.kind = pcDir - elif S_ISLNK(rawInfo.st_mode): - assert(path != "") # symlinks can't occur for file handles - formalInfo.kind = getSymlinkFileKind(path) + formalInfo.kind = + if S_ISDIR(rawInfo.st_mode): + pcDir + elif S_ISLNK(rawInfo.st_mode): + assert(path != "") # symlinks can't occur for file handles + getSymlinkFileKind(path) + else: + pcFile when defined(js): when not declared(FileHandle): From a76ae8f79584c5a491861daf2efda9908b21e488 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Mon, 31 Aug 2020 03:48:54 -0600 Subject: [PATCH 0563/3768] Changes for FreeRTOS/LwIP Port for the ESP32 (ESP-IDF) (#15250) * Changes for FreeRTOS/LwIP Port for the ESP32 (ESP-IDF) Adding FreeRTOS/LwIP to compiler: * adding freertos option * dyncalls for freertos * add freertos to posix os list * adding lwip option Setting up networking FreeRTOS/LwIP Port: * setting up lwip network for freertos * fixing posix / networking for freertos * disable setInheritable for freerots * using lwip for net control items * Fix builds by ignoring lib/posix/posix_freertos_consts.nim similar to lib/posix/posix_other_consts.nim --- compiler/options.nim | 6 +- compiler/platform.nim | 9 +- lib/posix/posix.nim | 17 +- lib/posix/posix_freertos_consts.nim | 488 ++++++++++++++++++++++ lib/posix/posix_other.nim | 80 +++- lib/pure/ioselects/ioselectors_select.nim | 2 +- lib/pure/nativesockets.nim | 2 +- lib/pure/selectors.nim | 2 + lib/system/dyncalls.nim | 2 +- lib/system/io.nim | 12 +- tools/kochdocs.nim | 1 + 11 files changed, 589 insertions(+), 32 deletions(-) create mode 100644 lib/posix/posix_freertos_consts.nim diff --git a/compiler/options.nim b/compiler/options.nim index 8ff8cb753..56b061358 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -494,7 +494,7 @@ proc isDefined*(conf: ConfigRef; symbol: string): bool = osQnx, osAtari, osAix, osHaiku, osVxWorks, osSolaris, osNetbsd, osFreebsd, osOpenbsd, osDragonfly, osMacosx, osIos, - osAndroid, osNintendoSwitch} + osAndroid, osNintendoSwitch, osFreeRTOS} of "linux": result = conf.target.targetOS in {osLinux, osAndroid} of "bsd": @@ -510,6 +510,10 @@ proc isDefined*(conf: ConfigRef; symbol: string): bool = of "sunos": result = conf.target.targetOS == osSolaris of "nintendoswitch": result = conf.target.targetOS == osNintendoSwitch + of "freertos": + result = conf.target.targetOS == osFreeRTOS + of "lwip": + result = conf.target.targetOS in {osFreeRTOS} of "littleendian": result = CPU[conf.target.targetCPU].endian == platform.littleEndian of "bigendian": result = CPU[conf.target.targetCPU].endian == platform.bigEndian of "cpu8": result = CPU[conf.target.targetCPU].bit == 8 diff --git a/compiler/platform.nim b/compiler/platform.nim index 4bac7c5a1..df79b2f8d 100644 --- a/compiler/platform.nim +++ b/compiler/platform.nim @@ -22,7 +22,7 @@ type osNone, osDos, osWindows, osOs2, osLinux, osMorphos, osSkyos, osSolaris, osIrix, osNetbsd, osFreebsd, osOpenbsd, osDragonfly, osAix, osPalmos, osQnx, osAmiga, osAtari, osNetware, osMacos, osMacosx, osIos, osHaiku, osAndroid, osVxWorks - osGenode, osJS, osNimVM, osStandalone, osNintendoSwitch, osAny + osGenode, osJS, osNimVM, osStandalone, osNintendoSwitch, osFreeRTOS, osAny type TInfoOSProp* = enum @@ -177,6 +177,10 @@ const objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/", scriptExt: ".sh", curDir: ".", exeExt: ".elf", extSep: ".", props: {ospNeedsPIC, ospPosix}), + (name: "FreeRTOS", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/", + objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/", + scriptExt: ".sh", curDir: ".", exeExt: ".elf", extSep: ".", + props: {ospPosix}), (name: "Any", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/", objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/", scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".", @@ -189,7 +193,7 @@ type cpuNone, cpuI386, cpuM68k, cpuAlpha, cpuPowerpc, cpuPowerpc64, cpuPowerpc64el, cpuSparc, cpuVm, cpuHppa, cpuIa64, cpuAmd64, cpuMips, cpuMipsel, cpuArm, cpuArm64, cpuJS, cpuNimVM, cpuAVR, cpuMSP430, - cpuSparc64, cpuMips64, cpuMips64el, cpuRiscV64, cpuWasm32 + cpuSparc64, cpuMips64, cpuMips64el, cpuRiscV64, cpuEsp, cpuWasm32 type TEndian* = enum @@ -223,6 +227,7 @@ const (name: "mips64", intSize: 64, endian: bigEndian, floatSize: 64, bit: 64), (name: "mips64el", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64), (name: "riscv64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64), + (name: "esp", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32), (name: "wasm32", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32)] type diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index d3d9728ab..c7a324cde 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -962,9 +962,15 @@ proc IN6_IS_ADDR_LINKLOCAL* (a1: ptr In6Addr): cint {. proc IN6_IS_ADDR_SITELOCAL* (a1: ptr In6Addr): cint {. importc, header: "".} ## Unicast site-local address. -proc IN6_IS_ADDR_V4MAPPED* (a1: ptr In6Addr): cint {. - importc, header: "".} - ## IPv4 mapped address. +when defined(lwip): + proc IN6_IS_ADDR_V4MAPPED*(ipv6_address: ptr In6Addr): cint = + var bits32: ptr array[4, uint32] = cast[ptr array[4, uint32]](ipv6_address) + return (bits32[1] == 0'u32 and bits32[2] == htonl(0x0000FFFF)).cint +else: + proc IN6_IS_ADDR_V4MAPPED* (a1: ptr In6Addr): cint {. + importc, header: "".} + ## IPv4 mapped address. + proc IN6_IS_ADDR_V4COMPAT* (a1: ptr In6Addr): cint {. importc, header: "".} ## IPv4-compatible address. @@ -1026,8 +1032,9 @@ proc setnetent*(a1: cint) {.importc, header: "".} proc setprotoent*(a1: cint) {.importc, header: "".} proc setservent*(a1: cint) {.importc, header: "".} -proc poll*(a1: ptr TPollfd, a2: Tnfds, a3: int): cint {. - importc, header: "", sideEffect.} +when not defined(lwip): + proc poll*(a1: ptr TPollfd, a2: Tnfds, a3: int): cint {. + importc, header: "", sideEffect.} proc realpath*(name, resolved: cstring): cstring {. importc: "realpath", header: "".} diff --git a/lib/posix/posix_freertos_consts.nim b/lib/posix/posix_freertos_consts.nim new file mode 100644 index 000000000..5e83fa0f5 --- /dev/null +++ b/lib/posix/posix_freertos_consts.nim @@ -0,0 +1,488 @@ +# Generated by detect.nim + + + +# +var E2BIG* {.importc: "E2BIG", header: "".}: cint +var EACCES* {.importc: "EACCES", header: "".}: cint +var EADDRINUSE* {.importc: "EADDRINUSE", header: "".}: cint +var EADDRNOTAVAIL* {.importc: "EADDRNOTAVAIL", header: "".}: cint +var EAFNOSUPPORT* {.importc: "EAFNOSUPPORT", header: "".}: cint +var EAGAIN* {.importc: "EAGAIN", header: "".}: cint +var EALREADY* {.importc: "EALREADY", header: "".}: cint +var EBADF* {.importc: "EBADF", header: "".}: cint +var EBADMSG* {.importc: "EBADMSG", header: "".}: cint +var EBUSY* {.importc: "EBUSY", header: "".}: cint +var ECANCELED* {.importc: "ECANCELED", header: "".}: cint +var ECHILD* {.importc: "ECHILD", header: "".}: cint +var ECONNABORTED* {.importc: "ECONNABORTED", header: "".}: cint +var ECONNREFUSED* {.importc: "ECONNREFUSED", header: "".}: cint +var ECONNRESET* {.importc: "ECONNRESET", header: "".}: cint +var EDEADLK* {.importc: "EDEADLK", header: "".}: cint +var EDESTADDRREQ* {.importc: "EDESTADDRREQ", header: "".}: cint +var EDOM* {.importc: "EDOM", header: "".}: cint +var EDQUOT* {.importc: "EDQUOT", header: "".}: cint +var EEXIST* {.importc: "EEXIST", header: "".}: cint +var EFAULT* {.importc: "EFAULT", header: "".}: cint +var EFBIG* {.importc: "EFBIG", header: "".}: cint +var EHOSTUNREACH* {.importc: "EHOSTUNREACH", header: "".}: cint +var EIDRM* {.importc: "EIDRM", header: "".}: cint +var EILSEQ* {.importc: "EILSEQ", header: "".}: cint +var EINPROGRESS* {.importc: "EINPROGRESS", header: "".}: cint +var EINTR* {.importc: "EINTR", header: "".}: cint +var EINVAL* {.importc: "EINVAL", header: "".}: cint +var EIO* {.importc: "EIO", header: "".}: cint +var EISCONN* {.importc: "EISCONN", header: "".}: cint +var EISDIR* {.importc: "EISDIR", header: "".}: cint +var ELOOP* {.importc: "ELOOP", header: "".}: cint +var EMFILE* {.importc: "EMFILE", header: "".}: cint +var EMLINK* {.importc: "EMLINK", header: "".}: cint +var EMSGSIZE* {.importc: "EMSGSIZE", header: "".}: cint +var EMULTIHOP* {.importc: "EMULTIHOP", header: "".}: cint +var ENAMETOOLONG* {.importc: "ENAMETOOLONG", header: "".}: cint +var ENETDOWN* {.importc: "ENETDOWN", header: "".}: cint +var ENETRESET* {.importc: "ENETRESET", header: "".}: cint +var ENETUNREACH* {.importc: "ENETUNREACH", header: "".}: cint +var ENFILE* {.importc: "ENFILE", header: "".}: cint +var ENOBUFS* {.importc: "ENOBUFS", header: "".}: cint +var ENODATA* {.importc: "ENODATA", header: "".}: cint +var ENODEV* {.importc: "ENODEV", header: "".}: cint +var ENOENT* {.importc: "ENOENT", header: "".}: cint +var ENOEXEC* {.importc: "ENOEXEC", header: "".}: cint +var ENOLCK* {.importc: "ENOLCK", header: "".}: cint +var ENOLINK* {.importc: "ENOLINK", header: "".}: cint +var ENOMEM* {.importc: "ENOMEM", header: "".}: cint +var ENOMSG* {.importc: "ENOMSG", header: "".}: cint +var ENOPROTOOPT* {.importc: "ENOPROTOOPT", header: "".}: cint +var ENOSPC* {.importc: "ENOSPC", header: "".}: cint +var ENOSR* {.importc: "ENOSR", header: "".}: cint +var ENOSTR* {.importc: "ENOSTR", header: "".}: cint +var ENOSYS* {.importc: "ENOSYS", header: "".}: cint +var ENOTCONN* {.importc: "ENOTCONN", header: "".}: cint +var ENOTDIR* {.importc: "ENOTDIR", header: "".}: cint +var ENOTEMPTY* {.importc: "ENOTEMPTY", header: "".}: cint +var ENOTSOCK* {.importc: "ENOTSOCK", header: "".}: cint +var ENOTSUP* {.importc: "ENOTSUP", header: "".}: cint +var ENOTTY* {.importc: "ENOTTY", header: "".}: cint +var ENXIO* {.importc: "ENXIO", header: "".}: cint +var EOPNOTSUPP* {.importc: "EOPNOTSUPP", header: "".}: cint +var EOVERFLOW* {.importc: "EOVERFLOW", header: "".}: cint +var EPERM* {.importc: "EPERM", header: "".}: cint +var EPIPE* {.importc: "EPIPE", header: "".}: cint +var EPROTO* {.importc: "EPROTO", header: "".}: cint +var EPROTONOSUPPORT* {.importc: "EPROTONOSUPPORT", header: "".}: cint +var EPROTOTYPE* {.importc: "EPROTOTYPE", header: "".}: cint +var ERANGE* {.importc: "ERANGE", header: "".}: cint +var EROFS* {.importc: "EROFS", header: "".}: cint +var ESPIPE* {.importc: "ESPIPE", header: "".}: cint +var ESRCH* {.importc: "ESRCH", header: "".}: cint +var ESTALE* {.importc: "ESTALE", header: "".}: cint +var ETIME* {.importc: "ETIME", header: "".}: cint +var ETIMEDOUT* {.importc: "ETIMEDOUT", header: "".}: cint +var ETXTBSY* {.importc: "ETXTBSY", header: "".}: cint +var EWOULDBLOCK* {.importc: "EWOULDBLOCK", header: "".}: cint +var EXDEV* {.importc: "EXDEV", header: "".}: cint + +# +var F_GETFD* {.importc: "F_GETFD", header: "".}: cint +# var F_SETFD* {.importc: "F_SETFD", header: "".}: cint +var F_GETFL* {.importc: "F_GETFL", header: "".}: cint +var F_SETFL* {.importc: "F_SETFL", header: "".}: cint +# var F_GETLK* {.importc: "F_GETLK", header: "".}: cint +# var F_SETLK* {.importc: "F_SETLK", header: "".}: cint +# var F_SETLKW* {.importc: "F_SETLKW", header: "".}: cint +# var F_GETOWN* {.importc: "F_GETOWN", header: "".}: cint +# var F_SETOWN* {.importc: "F_SETOWN", header: "".}: cint +# var FD_CLOEXEC* {.importc: "FD_CLOEXEC", header: "".}: cint +# var F_RDLCK* {.importc: "F_RDLCK", header: "".}: cint +# var F_UNLCK* {.importc: "F_UNLCK", header: "".}: cint +# var F_WRLCK* {.importc: "F_WRLCK", header: "".}: cint +var O_CREAT* {.importc: "O_CREAT", header: "".}: cint +var O_EXCL* {.importc: "O_EXCL", header: "".}: cint +# var O_NOCTTY* {.importc: "O_NOCTTY", header: "".}: cint +var O_TRUNC* {.importc: "O_TRUNC", header: "".}: cint +var O_APPEND* {.importc: "O_APPEND", header: "".}: cint +# var O_DSYNC* {.importc: "O_DSYNC", header: "".}: cint +var O_NONBLOCK* {.importc: "O_NONBLOCK", header: "".}: cint +# var O_RSYNC* {.importc: "O_RSYNC", header: "".}: cint +# var O_SYNC* {.importc: "O_SYNC", header: "".}: cint +# var O_ACCMODE* {.importc: "O_ACCMODE", header: "".}: cint +var O_RDONLY* {.importc: "O_RDONLY", header: "".}: cint +var O_RDWR* {.importc: "O_RDWR", header: "".}: cint +var O_WRONLY* {.importc: "O_WRONLY", header: "".}: cint +# var O_CLOEXEC* {.importc: "O_CLOEXEC", header: "".}: cint + +# # +# var LC_ALL* {.importc: "LC_ALL", header: "".}: cint +# var LC_COLLATE* {.importc: "LC_COLLATE", header: "".}: cint +# var LC_CTYPE* {.importc: "LC_CTYPE", header: "".}: cint +# var LC_MESSAGES* {.importc: "LC_MESSAGES", header: "".}: cint +# var LC_MONETARY* {.importc: "LC_MONETARY", header: "".}: cint +# var LC_NUMERIC* {.importc: "LC_NUMERIC", header: "".}: cint +# var LC_TIME* {.importc: "LC_TIME", header: "".}: cint + +# +var HOST_NOT_FOUND* {.importc: "HOST_NOT_FOUND", header: "".}: cint +var NO_DATA* {.importc: "NO_DATA", header: "".}: cint +var NO_RECOVERY* {.importc: "NO_RECOVERY", header: "".}: cint +var TRY_AGAIN* {.importc: "TRY_AGAIN", header: "".}: cint +var AI_PASSIVE* {.importc: "AI_PASSIVE", header: "".}: cint +var AI_CANONNAME* {.importc: "AI_CANONNAME", header: "".}: cint +var AI_NUMERICHOST* {.importc: "AI_NUMERICHOST", header: "".}: cint +var AI_NUMERICSERV* {.importc: "AI_NUMERICSERV", header: "".}: cint +var AI_V4MAPPED* {.importc: "AI_V4MAPPED", header: "".}: cint +var AI_ALL* {.importc: "AI_ALL", header: "".}: cint +var AI_ADDRCONFIG* {.importc: "AI_ADDRCONFIG", header: "".}: cint +var NI_NOFQDN* {.importc: "NI_NOFQDN", header: "".}: cint +var NI_NUMERICHOST* {.importc: "NI_NUMERICHOST", header: "".}: cint +var NI_NAMEREQD* {.importc: "NI_NAMEREQD", header: "".}: cint +var NI_NUMERICSERV* {.importc: "NI_NUMERICSERV", header: "".}: cint +var NI_DGRAM* {.importc: "NI_DGRAM", header: "".}: cint +var EAI_AGAIN* {.importc: "EAI_AGAIN", header: "".}: cint +var EAI_BADFLAGS* {.importc: "EAI_BADFLAGS", header: "".}: cint +var EAI_FAIL* {.importc: "EAI_FAIL", header: "".}: cint +var EAI_FAMILY* {.importc: "EAI_FAMILY", header: "".}: cint +var EAI_MEMORY* {.importc: "EAI_MEMORY", header: "".}: cint +var EAI_NONAME* {.importc: "EAI_NONAME", header: "".}: cint +var EAI_SERVICE* {.importc: "EAI_SERVICE", header: "".}: cint +var EAI_SOCKTYPE* {.importc: "EAI_SOCKTYPE", header: "".}: cint + +var LWIP_DNS_API_DECLARE_H_ERRNO* {.importc: "LWIP_DNS_API_DECLARE_H_ERRNO", header: "".}: cint +var LWIP_DNS_API_DEFINE_ERRORS* {.importc: "LWIP_DNS_API_DEFINE_ERRORS", header: "".}: cint +var LWIP_DNS_API_DEFINE_FLAGS* {.importc: "LWIP_DNS_API_DEFINE_FLAGS", header: "".}: cint +var LWIP_DNS_API_DECLARE_STRUCTS* {.importc: "LWIP_DNS_API_DECLARE_STRUCTS", header: "".}: cint +var NETDB_ELEM_SIZE* {.importc: "NETDB_ELEM_SIZE", header: "".}: cint + + +# +var ENABLE_LOOPBACK* {.importc: "ENABLE_LOOPBACK", header: "".}: cint +var NETIF_MAX_HWADDR_LEN* {.importc: "NETIF_MAX_HWADDR_LEN", header: "".}: cint +var NETIF_NAMESIZE* {.importc: "NETIF_NAMESIZE", header: "".}: cint +var NETIF_FLAG_UP* {.importc: "NETIF_FLAG_UP", header: "".}: cint +var NETIF_FLAG_BROADCAST* {.importc: "NETIF_FLAG_BROADCAST", header: "".}: cint +var NETIF_FLAG_LINK_UP* {.importc: "NETIF_FLAG_LINK_UP", header: "".}: cint +var NETIF_FLAG_ETHARP* {.importc: "NETIF_FLAG_ETHARP", header: "".}: cint +var NETIF_FLAG_ETHERNET* {.importc: "NETIF_FLAG_ETHERNET", header: "".}: cint +var NETIF_FLAG_IGMP* {.importc: "NETIF_FLAG_IGMP", header: "".}: cint +var NETIF_FLAG_MLD6* {.importc: "NETIF_FLAG_MLD6", header: "".}: cint +var NETIF_FLAG_GARP* {.importc: "NETIF_FLAG_GARP", header: "".}: cint +var NETIF_CHECKSUM_GEN_IP* {.importc: "NETIF_CHECKSUM_GEN_IP", header: "".}: cint +var NETIF_CHECKSUM_GEN_UDP* {.importc: "NETIF_CHECKSUM_GEN_UDP", header: "".}: cint +var NETIF_CHECKSUM_GEN_TCP* {.importc: "NETIF_CHECKSUM_GEN_TCP", header: "".}: cint +var NETIF_CHECKSUM_GEN_ICMP* {.importc: "NETIF_CHECKSUM_GEN_ICMP", header: "".}: cint +var NETIF_CHECKSUM_GEN_ICMP6* {.importc: "NETIF_CHECKSUM_GEN_ICMP6", header: "".}: cint +var NETIF_CHECKSUM_CHECK_IP* {.importc: "NETIF_CHECKSUM_CHECK_IP", header: "".}: cint +var NETIF_CHECKSUM_CHECK_UDP* {.importc: "NETIF_CHECKSUM_CHECK_UDP", header: "".}: cint +var NETIF_CHECKSUM_CHECK_TCP* {.importc: "NETIF_CHECKSUM_CHECK_TCP", header: "".}: cint +var NETIF_CHECKSUM_CHECK_ICMP* {.importc: "NETIF_CHECKSUM_CHECK_ICMP", header: "".}: cint +var NETIF_CHECKSUM_CHECK_ICMP6* {.importc: "NETIF_CHECKSUM_CHECK_ICMP6", header: "".}: cint +var NETIF_CHECKSUM_ENABLE_ALL* {.importc: "NETIF_CHECKSUM_ENABLE_ALL", header: "".}: cint +var NETIF_CHECKSUM_DISABLE_ALL* {.importc: "NETIF_CHECKSUM_DISABLE_ALL", header: "".}: cint +var NETIF_ADDR_IDX_MAX* {.importc: "NETIF_ADDR_IDX_MAX", header: "".}: cint +var LWIP_NETIF_USE_HINTS* {.importc: "LWIP_NETIF_USE_HINTS", header: "".}: cint +var NETIF_NO_INDEX* {.importc: "NETIF_NO_INDEX", header: "".}: cint +var LWIP_NSC_NONE* {.importc: "LWIP_NSC_NONE", header: "".}: cint +var LWIP_NSC_NETIF_ADDED* {.importc: "LWIP_NSC_NETIF_ADDED", header: "".}: cint +var LWIP_NSC_NETIF_REMOVED* {.importc: "LWIP_NSC_NETIF_REMOVED", header: "".}: cint +var LWIP_NSC_LINK_CHANGED* {.importc: "LWIP_NSC_LINK_CHANGED", header: "".}: cint +var LWIP_NSC_STATUS_CHANGED* {.importc: "LWIP_NSC_STATUS_CHANGED", header: "".}: cint +var LWIP_NSC_IPV4_ADDRESS_CHANGED* {.importc: "LWIP_NSC_IPV4_ADDRESS_CHANGED", header: "".}: cint +var LWIP_NSC_IPV4_GATEWAY_CHANGED* {.importc: "LWIP_NSC_IPV4_GATEWAY_CHANGED", header: "".}: cint +var LWIP_NSC_IPV4_NETMASK_CHANGED* {.importc: "LWIP_NSC_IPV4_NETMASK_CHANGED", header: "".}: cint +var LWIP_NSC_IPV4_SETTINGS_CHANGED* {.importc: "LWIP_NSC_IPV4_SETTINGS_CHANGED", header: "".}: cint +var LWIP_NSC_IPV6_SET* {.importc: "LWIP_NSC_IPV6_SET", header: "".}: cint +var LWIP_NSC_IPV6_ADDR_STATE_CHANGED* {.importc: "LWIP_NSC_IPV6_ADDR_STATE_CHANGED", header: "".}: cint +#define NETIF_DECLARE_EXT_CALLBACK(name) + + +# +var IF_NAMESIZE* {.importc: "IF_NAMESIZE", header: "".}: cint + +# +var IPPROTO_IP* {.importc: "IPPROTO_IP", header: "".}: cint +var IPPROTO_IPV6* {.importc: "IPPROTO_IPV6", header: "".}: cint +var IPPROTO_ICMP* {.importc: "IPPROTO_ICMP", header: "".}: cint +var IPPROTO_ICMPV6* {.importc: "IPPROTO_ICMPV6", header: "".}: cint +var IPPROTO_RAW* {.importc: "IPPROTO_RAW", header: "".}: cint +var IPPROTO_TCP* {.importc: "IPPROTO_TCP", header: "".}: cint +var IPPROTO_UDP* {.importc: "IPPROTO_UDP", header: "".}: cint +var INADDR_ANY* {.importc: "INADDR_ANY", header: "".}: InAddrScalar +var INADDR_LOOPBACK* {.importc: "INADDR_LOOPBACK", header: "".}: InAddrScalar +var INADDR_BROADCAST* {.importc: "INADDR_BROADCAST", header: "".}: InAddrScalar +var INET_ADDRSTRLEN* {.importc: "INET_ADDRSTRLEN", header: "".}: cint +var INET6_ADDRSTRLEN* {.importc: "INET6_ADDRSTRLEN", header: "".}: cint +var IPV6_JOIN_GROUP* {.importc: "IPV6_JOIN_GROUP", header: "".}: cint +var IPV6_LEAVE_GROUP* {.importc: "IPV6_LEAVE_GROUP", header: "".}: cint +var IPV6_MULTICAST_HOPS* {.importc: "IPV6_MULTICAST_HOPS", header: "".}: cint +var IPV6_MULTICAST_IF* {.importc: "IPV6_MULTICAST_IF", header: "".}: cint +var IPV6_MULTICAST_LOOP* {.importc: "IPV6_MULTICAST_LOOP", header: "".}: cint +var IPV6_UNICAST_HOPS* {.importc: "IPV6_UNICAST_HOPS", header: "".}: cint +var IPV6_V6ONLY* {.importc: "IPV6_V6ONLY", header: "".}: cint + +# +var TCP_NODELAY* {.importc: "TCP_NODELAY", header: "".}: cint +var TCP_KEEPALIVE* {.importc: "TCP_KEEPALIVE", header: "".}: cint +var TCP_KEEPIDLE* {.importc: "TCP_KEEPIDLE", header: "".}: cint +var TCP_KEEPINTVL* {.importc: "TCP_KEEPINTVL", header: "".}: cint +var TCP_KEEPCNT* {.importc: "TCP_KEEPCNT", header: "".}: cint + +# +# var NL_SETD* {.importc: "NL_SETD", header: "".}: cint +# var NL_CAT_LOCALE* {.importc: "NL_CAT_LOCALE", header: "".}: cint + +# +# var POLLIN* {.importc: "POLLIN", header: "".}: cshort +# var POLLRDNORM* {.importc: "POLLRDNORM", header: "".}: cshort +# var POLLRDBAND* {.importc: "POLLRDBAND", header: "".}: cshort +# var POLLPRI* {.importc: "POLLPRI", header: "".}: cshort +# var POLLOUT* {.importc: "POLLOUT", header: "".}: cshort +# var POLLWRNORM* {.importc: "POLLWRNORM", header: "".}: cshort +# var POLLWRBAND* {.importc: "POLLWRBAND", header: "".}: cshort +# var POLLERR* {.importc: "POLLERR", header: "".}: cshort +# var POLLHUP* {.importc: "POLLHUP", header: "".}: cshort +# var POLLNVAL* {.importc: "POLLNVAL", header: "".}: cshort + +# +var PTHREAD_STACK_MIN* {.importc: "PTHREAD_STACK_MIN", header: "".}: cint +# var PTHREAD_BARRIER_SERIAL_THREAD* {.importc: "PTHREAD_BARRIER_SERIAL_THREAD", header: "".}: cint +# var PTHREAD_CANCEL_ASYNCHRONOUS* {.importc: "PTHREAD_CANCEL_ASYNCHRONOUS", header: "".}: cint +# var PTHREAD_CANCEL_ENABLE* {.importc: "PTHREAD_CANCEL_ENABLE", header: "".}: cint +# var PTHREAD_CANCEL_DEFERRED* {.importc: "PTHREAD_CANCEL_DEFERRED", header: "".}: cint +# var PTHREAD_CANCEL_DISABLE* {.importc: "PTHREAD_CANCEL_DISABLE", header: "".}: cint +# var PTHREAD_CREATE_DETACHED* {.importc: "PTHREAD_CREATE_DETACHED", header: "".}: cint +# var PTHREAD_CREATE_JOINABLE* {.importc: "PTHREAD_CREATE_JOINABLE", header: "".}: cint +# var PTHREAD_EXPLICIT_SCHED* {.importc: "PTHREAD_EXPLICIT_SCHED", header: "".}: cint +# var PTHREAD_INHERIT_SCHED* {.importc: "PTHREAD_INHERIT_SCHED", header: "".}: cint +# var PTHREAD_MUTEX_DEFAULT* {.importc: "PTHREAD_MUTEX_DEFAULT", header: "".}: cint +# var PTHREAD_MUTEX_ERRORCHECK* {.importc: "PTHREAD_MUTEX_ERRORCHECK", header: "".}: cint +# var PTHREAD_MUTEX_NORMAL* {.importc: "PTHREAD_MUTEX_NORMAL", header: "".}: cint +# var PTHREAD_MUTEX_RECURSIVE* {.importc: "PTHREAD_MUTEX_RECURSIVE", header: "".}: cint +# var PTHREAD_PRIO_INHERIT* {.importc: "PTHREAD_PRIO_INHERIT", header: "".}: cint +# var PTHREAD_PRIO_NONE* {.importc: "PTHREAD_PRIO_NONE", header: "".}: cint +# var PTHREAD_PRIO_PROTECT* {.importc: "PTHREAD_PRIO_PROTECT", header: "".}: cint +# var PTHREAD_PROCESS_SHARED* {.importc: "PTHREAD_PROCESS_SHARED", header: "".}: cint +# var PTHREAD_PROCESS_PRIVATE* {.importc: "PTHREAD_PROCESS_PRIVATE", header: "".}: cint +# var PTHREAD_SCOPE_PROCESS* {.importc: "PTHREAD_SCOPE_PROCESS", header: "".}: cint +# var PTHREAD_SCOPE_SYSTEM* {.importc: "PTHREAD_SCOPE_SYSTEM", header: "".}: cint + +# # +# var SCHED_FIFO* {.importc: "SCHED_FIFO", header: "".}: cint +# var SCHED_RR* {.importc: "SCHED_RR", header: "".}: cint +# var SCHED_SPORADIC* {.importc: "SCHED_SPORADIC", header: "".}: cint +# var SCHED_OTHER* {.importc: "SCHED_OTHER", header: "".}: cint + +# +var SEM_FAILED* {.importc: "SEM_FAILED", header: "".}: pointer + +# # +# var SIGEV_NONE* {.importc: "SIGEV_NONE", header: "".}: cint +# var SIGEV_SIGNAL* {.importc: "SIGEV_SIGNAL", header: "".}: cint +# var SIGEV_THREAD* {.importc: "SIGEV_THREAD", header: "".}: cint +# var SIGABRT* {.importc: "SIGABRT", header: "".}: cint +# var SIGALRM* {.importc: "SIGALRM", header: "".}: cint +# var SIGBUS* {.importc: "SIGBUS", header: "".}: cint +# var SIGCHLD* {.importc: "SIGCHLD", header: "".}: cint +# var SIGCONT* {.importc: "SIGCONT", header: "".}: cint +# var SIGFPE* {.importc: "SIGFPE", header: "".}: cint +# var SIGHUP* {.importc: "SIGHUP", header: "".}: cint +# var SIGILL* {.importc: "SIGILL", header: "".}: cint +# var SIGINT* {.importc: "SIGINT", header: "".}: cint +# var SIGKILL* {.importc: "SIGKILL", header: "".}: cint +# var SIGPIPE* {.importc: "SIGPIPE", header: "".}: cint +# var SIGQUIT* {.importc: "SIGQUIT", header: "".}: cint +# var SIGSEGV* {.importc: "SIGSEGV", header: "".}: cint +# var SIGSTOP* {.importc: "SIGSTOP", header: "".}: cint +# var SIGTERM* {.importc: "SIGTERM", header: "".}: cint +# var SIGTSTP* {.importc: "SIGTSTP", header: "".}: cint +# var SIGTTIN* {.importc: "SIGTTIN", header: "".}: cint +# var SIGTTOU* {.importc: "SIGTTOU", header: "".}: cint +# var SIGUSR1* {.importc: "SIGUSR1", header: "".}: cint +# var SIGUSR2* {.importc: "SIGUSR2", header: "".}: cint +# var SIGPOLL* {.importc: "SIGPOLL", header: "".}: cint +# var SIGPROF* {.importc: "SIGPROF", header: "".}: cint +# var SIGSYS* {.importc: "SIGSYS", header: "".}: cint +# var SIGTRAP* {.importc: "SIGTRAP", header: "".}: cint +# var SIGURG* {.importc: "SIGURG", header: "".}: cint +# var SIGVTALRM* {.importc: "SIGVTALRM", header: "".}: cint +# var SIGXCPU* {.importc: "SIGXCPU", header: "".}: cint +# var SIGXFSZ* {.importc: "SIGXFSZ", header: "".}: cint +# var SA_NOCLDSTOP* {.importc: "SA_NOCLDSTOP", header: "".}: cint +# var SIG_BLOCK* {.importc: "SIG_BLOCK", header: "".}: cint +# var SIG_UNBLOCK* {.importc: "SIG_UNBLOCK", header: "".}: cint +# var SIG_SETMASK* {.importc: "SIG_SETMASK", header: "".}: cint +# var SA_ONSTACK* {.importc: "SA_ONSTACK", header: "".}: cint +# var SA_RESETHAND* {.importc: "SA_RESETHAND", header: "".}: cint +# var SA_RESTART* {.importc: "SA_RESTART", header: "".}: cint +# var SA_SIGINFO* {.importc: "SA_SIGINFO", header: "".}: cint +# var SA_NOCLDWAIT* {.importc: "SA_NOCLDWAIT", header: "".}: cint +# var SA_NODEFER* {.importc: "SA_NODEFER", header: "".}: cint +# var SS_ONSTACK* {.importc: "SS_ONSTACK", header: "".}: cint +# var SS_DISABLE* {.importc: "SS_DISABLE", header: "".}: cint +# var MINSIGSTKSZ* {.importc: "MINSIGSTKSZ", header: "".}: cint +# var SIGSTKSZ* {.importc: "SIGSTKSZ", header: "".}: cint +# var SIG_HOLD* {.importc: "SIG_HOLD", header: "".}: Sighandler +# var SIG_DFL* {.importc: "SIG_DFL", header: "".}: Sighandler +# var SIG_ERR* {.importc: "SIG_ERR", header: "".}: Sighandler +# var SIG_IGN* {.importc: "SIG_IGN", header: "".}: Sighandler + +# # +# var IPC_CREAT* {.importc: "IPC_CREAT", header: "".}: cint +# var IPC_EXCL* {.importc: "IPC_EXCL", header: "".}: cint +# var IPC_NOWAIT* {.importc: "IPC_NOWAIT", header: "".}: cint +# var IPC_PRIVATE* {.importc: "IPC_PRIVATE", header: "".}: cint +# var IPC_RMID* {.importc: "IPC_RMID", header: "".}: cint +# var IPC_SET* {.importc: "IPC_SET", header: "".}: cint +# var IPC_STAT* {.importc: "IPC_STAT", header: "".}: cint + +# # +# var PROT_READ* {.importc: "PROT_READ", header: "".}: cint +# var PROT_WRITE* {.importc: "PROT_WRITE", header: "".}: cint +# var PROT_EXEC* {.importc: "PROT_EXEC", header: "".}: cint +# var PROT_NONE* {.importc: "PROT_NONE", header: "".}: cint +# var MAP_ANONYMOUS* {.importc: "MAP_ANONYMOUS", header: "".}: cint +# var MAP_FIXED_NOREPLACE* {.importc: "MAP_FIXED_NOREPLACE", header: "".}: cint +# var MAP_NORESERVE* {.importc: "MAP_NORESERVE", header: "".}: cint +# var MAP_SHARED* {.importc: "MAP_SHARED", header: "".}: cint +# var MAP_PRIVATE* {.importc: "MAP_PRIVATE", header: "".}: cint +# var MAP_FIXED* {.importc: "MAP_FIXED", header: "".}: cint +# var MS_ASYNC* {.importc: "MS_ASYNC", header: "".}: cint +# var MS_SYNC* {.importc: "MS_SYNC", header: "".}: cint +# var MS_INVALIDATE* {.importc: "MS_INVALIDATE", header: "".}: cint +# var MCL_CURRENT* {.importc: "MCL_CURRENT", header: "".}: cint +# var MCL_FUTURE* {.importc: "MCL_FUTURE", header: "".}: cint +# var MAP_FAILED* {.importc: "MAP_FAILED", header: "".}: pointer +# var POSIX_MADV_NORMAL* {.importc: "POSIX_MADV_NORMAL", header: "".}: cint +# var POSIX_MADV_SEQUENTIAL* {.importc: "POSIX_MADV_SEQUENTIAL", header: "".}: cint +# var POSIX_MADV_RANDOM* {.importc: "POSIX_MADV_RANDOM", header: "".}: cint +# var POSIX_MADV_WILLNEED* {.importc: "POSIX_MADV_WILLNEED", header: "".}: cint +# var POSIX_MADV_DONTNEED* {.importc: "POSIX_MADV_DONTNEED", header: "".}: cint +# var POSIX_TYPED_MEM_ALLOCATE* {.importc: "POSIX_TYPED_MEM_ALLOCATE", header: "".}: cint +# var POSIX_TYPED_MEM_ALLOCATE_CONTIG* {.importc: "POSIX_TYPED_MEM_ALLOCATE_CONTIG", header: "".}: cint +# var POSIX_TYPED_MEM_MAP_ALLOCATABLE* {.importc: "POSIX_TYPED_MEM_MAP_ALLOCATABLE", header: "".}: cint + +# +# var RLIMIT_NOFILE* {.importc: "RLIMIT_NOFILE", header: "".}: cint + +# +var FD_SETSIZE* {.importc: "FD_SETSIZE", header: "".}: cint + +# +var MSG_CTRUNC* {.importc: "MSG_CTRUNC", header: "".}: cint +var MSG_DONTROUTE* {.importc: "MSG_DONTROUTE", header: "".}: cint +var MSG_EOR* {.importc: "MSG_EOR", header: "".}: cint +var MSG_OOB* {.importc: "MSG_OOB", header: "".}: cint + +# var SCM_RIGHTS* {.importc: "SCM_RIGHTS", header: "".}: cint +var SO_ACCEPTCONN* {.importc: "SO_ACCEPTCONN", header: "".}: cint +var SO_BROADCAST* {.importc: "SO_BROADCAST", header: "".}: cint +var SO_DEBUG* {.importc: "SO_DEBUG", header: "".}: cint +var SO_DONTROUTE* {.importc: "SO_DONTROUTE", header: "".}: cint +var SO_ERROR* {.importc: "SO_ERROR", header: "".}: cint +var SO_KEEPALIVE* {.importc: "SO_KEEPALIVE", header: "".}: cint +var SO_LINGER* {.importc: "SO_LINGER", header: "".}: cint +var SO_OOBINLINE* {.importc: "SO_OOBINLINE", header: "".}: cint +var SO_RCVBUF* {.importc: "SO_RCVBUF", header: "".}: cint +var SO_RCVLOWAT* {.importc: "SO_RCVLOWAT", header: "".}: cint +var SO_RCVTIMEO* {.importc: "SO_RCVTIMEO", header: "".}: cint +var SO_REUSEADDR* {.importc: "SO_REUSEADDR", header: "".}: cint +var SO_SNDBUF* {.importc: "SO_SNDBUF", header: "".}: cint +var SO_SNDLOWAT* {.importc: "SO_SNDLOWAT", header: "".}: cint +var SO_SNDTIMEO* {.importc: "SO_SNDTIMEO", header: "".}: cint +var SO_TYPE* {.importc: "SO_TYPE", header: "".}: cint +var SOCK_DGRAM* {.importc: "SOCK_DGRAM", header: "".}: cint +var SOCK_RAW* {.importc: "SOCK_RAW", header: "".}: cint + +# var SOCK_SEQPACKET* {.importc: "SOCK_SEQPACKET", header: "".}: cint +const SOCK_SEQPACKET* = cint(5) + +var SOCK_STREAM* {.importc: "SOCK_STREAM", header: "".}: cint +var SOL_SOCKET* {.importc: "SOL_SOCKET", header: "".}: cint + +const SocketMaxConnections {.intdefine.}: int = 32 +var SOMAXCONN*: cint = SocketMaxConnections.cint + +var MSG_PEEK* {.importc: "MSG_PEEK", header: "".}: cint +var MSG_TRUNC* {.importc: "MSG_TRUNC", header: "".}: cint +var MSG_WAITALL* {.importc: "MSG_WAITALL", header: "".}: cint +var AF_INET* {.importc: "AF_INET", header: "".}: cint +var AF_INET6* {.importc: "AF_INET6", header: "".}: cint +# var AF_UNIX* {.importc: "AF_UNIX", header: "".}: cint +const AF_UNIX*: cint = 1 # for compat with Nim libraries, doesn't exist on freertos +var AF_UNSPEC* {.importc: "AF_UNSPEC", header: "".}: cint +var SHUT_RD* {.importc: "SHUT_RD", header: "".}: cint +var SHUT_RDWR* {.importc: "SHUT_RDWR", header: "".}: cint +var SHUT_WR* {.importc: "SHUT_WR", header: "".}: cint + +# # +# + +# var S_IFBLK* {.importc: "S_IFBLK", header: "".}: cint +# var S_IFCHR* {.importc: "S_IFCHR", header: "".}: cint +# var S_IFDIR* {.importc: "S_IFDIR", header: "".}: cint +# var S_IFIFO* {.importc: "S_IFIFO", header: "".}: cint +# var S_IFLNK* {.importc: "S_IFLNK", header: "".}: cint +# var S_IFMT* {.importc: "S_IFMT", header: "".}: cint +# var S_IFREG* {.importc: "S_IFREG", header: "".}: cint +# var S_IFSOCK* {.importc: "S_IFSOCK", header: "".}: cint +var S_IRGRP* {.importc: "S_IRGRP", header: "".}: cint +var S_IROTH* {.importc: "S_IROTH", header: "".}: cint +var S_IRUSR* {.importc: "S_IRUSR", header: "".}: cint +# var S_IRWXG* {.importc: "S_IRWXG", header: "".}: cint +# var S_IRWXO* {.importc: "S_IRWXO", header: "".}: cint +# var S_IRWXU* {.importc: "S_IRWXU", header: "".}: cint +# var S_ISGID* {.importc: "S_ISGID", header: "".}: cint +# var S_ISUID* {.importc: "S_ISUID", header: "".}: cint +# var S_ISVTX* {.importc: "S_ISVTX", header: "".}: cint +var S_IWGRP* {.importc: "S_IWGRP", header: "".}: cint +var S_IWOTH* {.importc: "S_IWOTH", header: "".}: cint +var S_IWUSR* {.importc: "S_IWUSR", header: "".}: cint +var S_IXGRP* {.importc: "S_IXGRP", header: "".}: cint +var S_IXOTH* {.importc: "S_IXOTH", header: "".}: cint +var S_IXUSR* {.importc: "S_IXUSR", header: "".}: cint + +# # +# var ST_RDONLY* {.importc: "ST_RDONLY", header: "".}: cint +# var ST_NOSUID* {.importc: "ST_NOSUID", header: "".}: cint + +# # +# var WNOHANG* {.importc: "WNOHANG", header: "".}: cint +# var WUNTRACED* {.importc: "WUNTRACED", header: "".}: cint +# var WEXITED* {.importc: "WEXITED", header: "".}: cint +# var WSTOPPED* {.importc: "WSTOPPED", header: "".}: cint +# var WCONTINUED* {.importc: "WCONTINUED", header: "".}: cint +# var WNOWAIT* {.importc: "WNOWAIT", header: "".}: cint +# var P_ALL* {.importc: "P_ALL", header: "".}: cint +# var P_PID* {.importc: "P_PID", header: "".}: cint +# var P_PGID* {.importc: "P_PGID", header: "".}: cint + +# # +# var POSIX_SPAWN_RESETIDS* {.importc: "POSIX_SPAWN_RESETIDS", header: "".}: cint +# var POSIX_SPAWN_SETPGROUP* {.importc: "POSIX_SPAWN_SETPGROUP", header: "".}: cint +# var POSIX_SPAWN_SETSCHEDPARAM* {.importc: "POSIX_SPAWN_SETSCHEDPARAM", header: "".}: cint +# var POSIX_SPAWN_SETSCHEDULER* {.importc: "POSIX_SPAWN_SETSCHEDULER", header: "".}: cint +# var POSIX_SPAWN_SETSIGDEF* {.importc: "POSIX_SPAWN_SETSIGDEF", header: "".}: cint +# var POSIX_SPAWN_SETSIGMASK* {.importc: "POSIX_SPAWN_SETSIGMASK", header: "".}: cint + +## +# var IOFBF* {.importc: "_IOFBF", header: "".}: cint +# var IONBF* {.importc: "_IONBF", header: "".}: cint + +# +var CLOCKS_PER_SEC* {.importc: "CLOCKS_PER_SEC", header: "".}: clong +var CLOCK_PROCESS_CPUTIME_ID* {.importc: "CLOCK_PROCESS_CPUTIME_ID", header: "".}: cint +var CLOCK_THREAD_CPUTIME_ID* {.importc: "CLOCK_THREAD_CPUTIME_ID", header: "".}: cint +var CLOCK_REALTIME* {.importc: "CLOCK_REALTIME", header: "".}: cint +var TIMER_ABSTIME* {.importc: "TIMER_ABSTIME", header: "".}: cint +var CLOCK_MONOTONIC* {.importc: "CLOCK_MONOTONIC", header: "".}: cint + +# + +const F_OK* = cint(0) +const R_OK* = cint(4) +const W_OK* = cint(2) +const X_OK* = cint(1) +const F_LOCK* = cint(1) +const F_TEST* = cint(3) +const F_TLOCK* = cint(2) +const F_ULOCK* = cint(0) + diff --git a/lib/posix/posix_other.nim b/lib/posix/posix_other.nim index ed058d5cf..1ad05060d 100644 --- a/lib/posix/posix_other.nim +++ b/lib/posix/posix_other.nim @@ -10,9 +10,14 @@ when defined(nimHasStyleChecks): {.push styleChecks: off.} -const - hasSpawnH = true # should exist for every Posix system nowadays - hasAioH = defined(linux) +when defined(freertos): + const + hasSpawnH = false # should exist for every Posix system nowadays + hasAioH = false +else: + const + hasSpawnH = true # should exist for every Posix system nowadays + hasAioH = defined(linux) when defined(linux) and not defined(android): # On Linux: @@ -397,21 +402,55 @@ type SockLen* {.importc: "socklen_t", header: "".} = cuint TSa_Family* {.importc: "sa_family_t", header: "".} = cushort - SockAddr* {.importc: "struct sockaddr", header: "", - pure, final.} = object ## struct sockaddr - sa_family*: TSa_Family ## Address family. - sa_data*: array[0..255, char] ## Socket address (variable-length data). +when defined(lwip): + type + SockAddr* {.importc: "struct sockaddr", header: "", + pure, final.} = object ## struct sockaddr + sa_len*: uint8 ## Address family. + sa_family*: TSa_Family ## Address family. + sa_data*: array[0..255, char] ## Socket address (variable-length data). +else: + type + SockAddr* {.importc: "struct sockaddr", header: "", + pure, final.} = object ## struct sockaddr + sa_family*: TSa_Family ## Address family. + sa_data*: array[0..255, char] ## Socket address (variable-length data). +type Sockaddr_un* {.importc: "struct sockaddr_un", header: "", pure, final.} = object ## struct sockaddr_un sun_family*: TSa_Family ## Address family. sun_path*: array[0..Sockaddr_un_path_length-1, char] ## Socket path - Sockaddr_storage* {.importc: "struct sockaddr_storage", - header: "", - pure, final.} = object ## struct sockaddr_storage - ss_family*: TSa_Family ## Address family. +when defined(lwip): + when not defined(lwip6): + type + Sockaddr_storage* {.importc: "struct sockaddr_storage", + header: "", + pure, final.} = object ## struct sockaddr_storage + s2_len*: uint8 ## Address family. + ss_family*: TSa_Family ## Address family. + s2_data1*: array[2, char] ## Address family. + s2_data2*: array[3, uint32] ## Address family. + else: + type + Sockaddr_storage* {.importc: "struct sockaddr_storage", + header: "", + pure, final.} = object ## struct sockaddr_storage + s2_len*: uint8 ## Address family. + ss_family*: TSa_Family ## Address family. + s2_data1*: array[2, char] ## Address family. + s2_data2*: array[3, uint32] ## Address family. + s2_data3*: array[3, uint32] ## Address family. +else: + type + Sockaddr_storage* {.importc: "struct sockaddr_storage", + header: "", + pure, final.} = object ## struct sockaddr_storage + ss_family*: TSa_Family ## Address family. + +type Tif_nameindex* {.importc: "struct if_nameindex", final, pure, header: "".} = object ## struct if_nameindex if_index*: cint ## Numeric index of the interface. @@ -530,13 +569,15 @@ type ai_canonname*: cstring ## Canonical name of service location. ai_next*: ptr AddrInfo ## Pointer to next in list. - TPollfd* {.importc: "struct pollfd", pure, final, - header: "".} = object ## struct pollfd - fd*: cint ## The following descriptor being polled. - events*: cshort ## The input event flags (see below). - revents*: cshort ## The output event flags (see below). +when not defined(lwip): + type + TPollfd* {.importc: "struct pollfd", pure, final, + header: "".} = object ## struct pollfd + fd*: cint ## The following descriptor being polled. + events*: cshort ## The input event flags (see below). + revents*: cshort ## The output event flags (see below). - Tnfds* {.importc: "nfds_t", header: "".} = cint + Tnfds* {.importc: "nfds_t", header: "".} = cint var errno* {.importc, header: "".}: cint ## error variable @@ -545,7 +586,10 @@ var timezone* {.importc, header: "".}: int # Regenerate using detect.nim! -include posix_other_consts +when defined(lwip): + include posix_freertos_consts +else: + include posix_other_consts when defined(linux): var diff --git a/lib/pure/ioselects/ioselectors_select.nim b/lib/pure/ioselects/ioselectors_select.nim index 02a853b42..2f22f1b30 100644 --- a/lib/pure/ioselects/ioselectors_select.nim +++ b/lib/pure/ioselects/ioselectors_select.nim @@ -312,7 +312,7 @@ proc selectInto*[T](s: Selector[T], timeout: int, verifySelectParams(timeout) if timeout != -1: - when defined(genode): + when defined(genode) or defined(freertos): tv.tv_sec = Time(timeout div 1_000) else: tv.tv_sec = timeout.int32 div 1_000 diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim index 49a1192df..0b0c4b398 100644 --- a/lib/pure/nativesockets.nim +++ b/lib/pure/nativesockets.nim @@ -269,7 +269,7 @@ proc getAddrInfo*(address: string, port: Port, domain: Domain = AF_INET, let socketPort = if sockType == SOCK_RAW: "" else: $port var gaiResult = getaddrinfo(address, socketPort, addr(hints), result) if gaiResult != 0'i32: - when useWinVersion: + when useWinVersion or defined(freertos): raiseOSError(osLastError()) else: raiseOSError(osLastError(), $gai_strerror(gaiResult)) diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim index 6007a8250..f3726efaa 100644 --- a/lib/pure/selectors.nim +++ b/lib/pure/selectors.nim @@ -332,5 +332,7 @@ else: include ioselects/ioselectors_select # TODO: use the native VFS layer elif defined(nintendoswitch): include ioselects/ioselectors_select + elif defined(freertos) or defined(lwip): + include ioselects/ioselectors_select else: include ioselects/ioselectors_poll diff --git a/lib/system/dyncalls.nim b/lib/system/dyncalls.nim index a15d9267f..1b0a3e64c 100644 --- a/lib/system/dyncalls.nim +++ b/lib/system/dyncalls.nim @@ -179,7 +179,7 @@ elif defined(genode): proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr {. error: "nimGetProcAddr not implemented".} -elif defined(nintendoswitch): +elif defined(nintendoswitch) or defined(freertos): proc nimUnloadLibrary(lib: LibHandle) = cstderr.rawWrite("nimUnLoadLibrary not implemented") cstderr.rawWrite("\n") diff --git a/lib/system/io.nim b/lib/system/io.nim index 3d5cf981a..c3b2976fb 100644 --- a/lib/system/io.nim +++ b/lib/system/io.nim @@ -269,7 +269,7 @@ when SupportIoctlInheritCtl: proc c_ioctl(fd: cint, request: cint): cint {. importc: "ioctl", header: "", varargs.} -elif defined(posix) and not defined(nimscript): +elif defined(posix) and not defined(lwip) and not defined(nimscript): var F_GETFD {.importc, header: "".}: cint F_SETFD {.importc, header: "".}: cint @@ -336,6 +336,8 @@ when defined(nimdoc) or (defined(posix) and not defined(nimscript)) or defined(w ## availability with `declared() `. when SupportIoctlInheritCtl: result = c_ioctl(f, if inheritable: FIONCLEX else: FIOCLEX) != -1 + elif defined(freertos): + result = true elif defined(posix): var flags = c_fcntl(f, F_GETFD) if flags == -1: @@ -767,6 +769,10 @@ when declared(stdout): var echoLock: SysLock initSysLock echoLock + const stdOutLock = not defined(windows) and not defined(android) and + not defined(nintendoswitch) and not defined(freertos) and + hostOS != "any" + proc echoBinSafe(args: openArray[string]) {.compilerproc.} = when defined(androidNDK): var s = "" @@ -775,7 +781,7 @@ when declared(stdout): android_log_print(ANDROID_LOG_VERBOSE, "nim", s) else: # flockfile deadlocks some versions of Android 5.x.x - when not defined(windows) and not defined(android) and not defined(nintendoswitch) and hostOS != "any": + when stdOutLock: proc flockfile(f: File) {.importc, nodecl.} proc funlockfile(f: File) {.importc, nodecl.} flockfile(stdout) @@ -789,7 +795,7 @@ when declared(stdout): const linefeed = "\n" discard c_fwrite(linefeed.cstring, linefeed.len, 1, stdout) discard c_fflush(stdout) - when not defined(windows) and not defined(android) and not defined(nintendoswitch) and hostOS != "any": + when stdOutLock: funlockfile(stdout) when defined(windows) and compileOption("threads"): releaseSys echoLock diff --git a/tools/kochdocs.nim b/tools/kochdocs.nim index 05ddb4f1e..453b1037b 100644 --- a/tools/kochdocs.nim +++ b/tools/kochdocs.nim @@ -158,6 +158,7 @@ lib/posix/posix_nintendoswitch_consts.nim lib/posix/posix_linux_amd64.nim lib/posix/posix_linux_amd64_consts.nim lib/posix/posix_other_consts.nim +lib/posix/posix_freertos_consts.nim lib/posix/posix_openbsd_amd64.nim lib/posix/posix_haiku.nim lib/js/jsre.nim From ff13f8cc3c74d4048b6c2604fe4b8e5365f5c13f Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 31 Aug 2020 14:46:23 +0200 Subject: [PATCH 0564/3768] fixes #15210 [backport:1.2] (#15237) * fixes #15210 [backport:1.2] * use patched version of bigints library --- compiler/ccgexprs.nim | 43 +++++++++++++++++--------------- lib/system/chcks.nim | 3 +++ testament/important_packages.nim | 2 +- tests/misc/tunsignedconv.nim | 11 ++++++++ 4 files changed, 38 insertions(+), 21 deletions(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 995b75d69..6da44a33a 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1996,25 +1996,30 @@ proc genRangeChck(p: BProc, n: PNode, d: var TLoc) = checkUnsignedConversions notin p.config.legacyFeatures): discard "no need to generate a check because it was disabled" else: - let raiser = - case skipTypes(n.typ, abstractVarRange).kind - of tyUInt..tyUInt64, tyChar: "raiseRangeErrorU" - of tyFloat..tyFloat128: "raiseRangeErrorF" - else: "raiseRangeErrorI" - discard cgsym(p.module, raiser) - # This seems to be bug-compatible with Nim version 1 but what we - # should really do here is to check if uint64Value < high(int) let n0t = n[0].typ - let boundaryCast = - if n0t.skipTypes(abstractVarRange).kind in {tyUInt, tyUInt32, tyUInt64} or - (n0t.sym != nil and sfSystemModule in n0t.sym.owner.flags and n0t.sym.name.s == "csize"): - "(NI64)" - else: - "" + # emit range check: - linefmt(p, cpsStmts, "if ($6($1) < $2 || $6($1) > $3){ $4($1, $2, $3); $5}$n", - [rdCharLoc(a), genLiteral(p, n[1], dest), genLiteral(p, n[2], dest), - raiser, raiseInstr(p), boundaryCast]) + if n0t.kind in {tyUInt, tyUInt64}: + linefmt(p, cpsStmts, "if ($1 > ($6)($3)){ #raiseRangeErrorNoArgs(); $5}$n", + [rdCharLoc(a), genLiteral(p, n[1], dest), genLiteral(p, n[2], dest), + raiser, raiseInstr(p), getTypeDesc(p.module, n0t)]) + else: + let raiser = + case skipTypes(n.typ, abstractVarRange).kind + of tyUInt..tyUInt64, tyChar: "raiseRangeErrorU" + of tyFloat..tyFloat128: "raiseRangeErrorF" + else: "raiseRangeErrorI" + discard cgsym(p.module, raiser) + + let boundaryCast = + if n0t.skipTypes(abstractVarRange).kind in {tyUInt, tyUInt32, tyUInt64} or + (n0t.sym != nil and sfSystemModule in n0t.sym.owner.flags and n0t.sym.name.s == "csize"): + "(NI64)" + else: + "" + linefmt(p, cpsStmts, "if ($6($1) < $2 || $6($1) > $3){ $4($1, $2, $3); $5}$n", + [rdCharLoc(a), genLiteral(p, n[1], dest), genLiteral(p, n[2], dest), + raiser, raiseInstr(p), boundaryCast]) putIntoDest(p, d, n, "(($1) ($2))" % [getTypeDesc(p.module, dest), rdCharLoc(a)], a.storage) @@ -2690,9 +2695,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = expr(p, n[1][0], d) of nkObjDownConv: downConv(p, n, d) of nkObjUpConv: upConv(p, n, d) - of nkChckRangeF: genRangeChck(p, n, d) - of nkChckRange64: genRangeChck(p, n, d) - of nkChckRange: genRangeChck(p, n, d) + of nkChckRangeF, nkChckRange64, nkChckRange: genRangeChck(p, n, d) of nkStringToCString: convStrToCStr(p, n, d) of nkCStringToString: convCStrToStr(p, n, d) of nkLambdaKinds: diff --git a/lib/system/chcks.nim b/lib/system/chcks.nim index 0b6a75b1f..e0a8bd78e 100644 --- a/lib/system/chcks.nim +++ b/lib/system/chcks.nim @@ -38,6 +38,9 @@ proc raiseRangeErrorU(i, a, b: uint64) {.compilerproc, noinline.} = # todo: better error reporting sysFatal(RangeDefect, "value out of range") +proc raiseRangeErrorNoArgs() {.compilerproc, noinline.} = + sysFatal(RangeDefect, "value out of range") + proc raiseObjectConversionError() {.compilerproc, noinline.} = sysFatal(ObjectConversionDefect, "invalid object conversion") diff --git a/testament/important_packages.nim b/testament/important_packages.nim index a4187f64c..dc2f8f0d1 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -14,7 +14,7 @@ pkg1 "argparse" pkg1 "arraymancer", true, "nim c tests/tests_cpu.nim" #pkg1 "ast_pattern_matching", false, "nim c -r --oldgensym:on tests/test1.nim" pkg1 "awk", true -pkg1 "bigints" +pkg1 "bigints", url = "https://github.com/Araq/nim-bigints" pkg1 "binaryheap", false, "nim c -r binaryheap.nim" pkg1 "BipBuffer" # pkg1 "blscurve", true # pending https://github.com/status-im/nim-blscurve/issues/39 diff --git a/tests/misc/tunsignedconv.nim b/tests/misc/tunsignedconv.nim index 07fc03e59..e9a2a2189 100644 --- a/tests/misc/tunsignedconv.nim +++ b/tests/misc/tunsignedconv.nim @@ -69,3 +69,14 @@ let rangeVar = 0'u64 ..< limit doAssert repr(rangeVar) == """[a = 0, b = 0] """ + +# bug #15210 + +let a3 = not 0'u64 +var success = false +try: + discard a3.int64 +except RangeDefect: + success = true + +doAssert success, "conversion should fail at runtime" From 244760197cf1d24829ae45de5f25c10577152cdd Mon Sep 17 00:00:00 2001 From: PMunch Date: Wed, 2 Sep 2020 08:07:08 +0000 Subject: [PATCH 0565/3768] Fix sets for architectures with default integers smaller than 32 bits (#15258) [backport] --- compiler/ccgexprs.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 6da44a33a..dd4774b2a 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1796,9 +1796,9 @@ template binaryExprIn(p: BProc, e: PNode, a, b, d: var TLoc, frmt: string) = proc genInExprAux(p: BProc, e: PNode, a, b, d: var TLoc) = case int(getSize(p.config, skipTypes(e[1].typ, abstractVar))) - of 1: binaryExprIn(p, e, a, b, d, "(($1 &(1U<<((NU)($2)&7U)))!=0)") - of 2: binaryExprIn(p, e, a, b, d, "(($1 &(1U<<((NU)($2)&15U)))!=0)") - of 4: binaryExprIn(p, e, a, b, d, "(($1 &(1U<<((NU)($2)&31U)))!=0)") + of 1: binaryExprIn(p, e, a, b, d, "(($1 &((NU8)1<<((NU)($2)&7U)))!=0)") + of 2: binaryExprIn(p, e, a, b, d, "(($1 &((NU16)1<<((NU)($2)&15U)))!=0)") + of 4: binaryExprIn(p, e, a, b, d, "(($1 &((NU32)1<<((NU)($2)&31U)))!=0)") of 8: binaryExprIn(p, e, a, b, d, "(($1 &((NU64)1<<((NU)($2)&63U)))!=0)") else: binaryExprIn(p, e, a, b, d, "(($1[(NU)($2)>>3] &(1U<<((NU)($2)&7U)))!=0)") From 3172ca54758e0a7e68d140a79ed98d0981b05e3f Mon Sep 17 00:00:00 2001 From: Silvio Date: Wed, 2 Sep 2020 20:53:02 +0200 Subject: [PATCH 0566/3768] replace / with _ in trId (#15256) Fixes jquery error when clicking to collapse/expand a panel --- testament/htmlgen.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testament/htmlgen.nim b/testament/htmlgen.nim index 8e58dd95e..174f36d0b 100644 --- a/testament/htmlgen.nim +++ b/testament/htmlgen.nim @@ -16,7 +16,7 @@ import "testamenthtml.nimf" proc generateTestResultPanelPartial(outfile: File, testResultRow: JsonNode) = let trId = htmlQuote(testResultRow["category"].str & "_" & testResultRow["name"].str). - multiReplace({".": "_", " ": "_", ":": "_"}) + multiReplace({".": "_", " ": "_", ":": "_", "/": "_"}) name = testResultRow["name"].str.htmlQuote() category = testResultRow["category"].str.htmlQuote() target = testResultRow["target"].str.htmlQuote() From 48f29972210612b41cb6d98122672b1713edc907 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 3 Sep 2020 12:59:34 +0200 Subject: [PATCH 0567/3768] fixes system.add for strict funcs (#15259) * fixes system.add for strict funcs * fixes #15248 --- compiler/varpartitions.nim | 2 +- lib/system.nim | 38 ++++++++++++++++++++------------------ 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim index fd6751cdf..23ad8fe09 100644 --- a/compiler/varpartitions.nim +++ b/compiler/varpartitions.nim @@ -342,7 +342,7 @@ proc deps(c: var Partitions; dest, src: PNode) = let destIsComplex = types.searchTypeFor(dest.typ, wrap) for t in targets: - if dest.kind != nkSym: + if dest.kind != nkSym and c.inNoSideEffectSection == 0: potentialMutation(c, t, dest.info) if destIsComplex: diff --git a/lib/system.nim b/lib/system.nim index 204a1194e..dc603b56d 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1227,9 +1227,10 @@ proc add*[T](x: var seq[T], y: openArray[T]) {.noSideEffect.} = ## .. code-block:: Nim ## var s: seq[string] = @["test2","test2"] ## s.add("test") # s <- @[test2, test2, test] - let xl = x.len - setLen(x, xl + y.len) - for i in 0..high(y): x[xl+i] = y[i] + {.noSideEffect.}: + let xl = x.len + setLen(x, xl + y.len) + for i in 0..high(y): x[xl+i] = y[i] when defined(nimSeqsV2): template movingCopy(a, b) = @@ -1283,22 +1284,23 @@ proc insert*[T](x: var seq[T], item: sink T, i = 0.Natural) {.noSideEffect.} = ## .. code-block:: Nim ## var i = @[1, 3, 5] ## i.insert(99, 0) # i <- @[99, 1, 3, 5] - template defaultImpl = - let xl = x.len - setLen(x, xl+1) - var j = xl-1 - while j >= i: - movingCopy(x[j+1], x[j]) - dec(j) - when nimvm: - defaultImpl() - else: - when defined(js): - var it : T - {.emit: "`x` = `x` || []; `x`.splice(`i`, 0, `it`);".} - else: + {.noSideEffect.}: + template defaultImpl = + let xl = x.len + setLen(x, xl+1) + var j = xl-1 + while j >= i: + movingCopy(x[j+1], x[j]) + dec(j) + when nimvm: defaultImpl() - x[i] = item + else: + when defined(js): + var it : T + {.emit: "`x` = `x` || []; `x`.splice(`i`, 0, `it`);".} + else: + defaultImpl() + x[i] = item when not defined(nimV2): proc repr*[T](x: T): string {.magic: "Repr", noSideEffect.} From 4fb17bc03be3046d8ff619a74eec7cb7ce02b4d2 Mon Sep 17 00:00:00 2001 From: Miran Date: Fri, 4 Sep 2020 09:23:27 +0200 Subject: [PATCH 0568/3768] fix #15257, `toHex` couldn't handle large uint64 (#15261) [backport:1.2] --- lib/pure/strutils.nim | 43 ++++++++++++++++++++++++++++----------- tests/stdlib/tstrutil.nim | 6 ++++-- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index a0a172430..20010ff81 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -938,6 +938,31 @@ proc toOct*(x: BiggestInt, len: Positive): string {.noSideEffect, inc shift, 3 mask = mask shl BiggestUInt(3) +proc toHexImpl(x: BiggestUInt, len: Positive, handleNegative: bool): string {.noSideEffect.} = + const + HexChars = "0123456789ABCDEF" + var n = x + result = newString(len) + for j in countdown(len-1, 0): + result[j] = HexChars[int(n and 0xF)] + n = n shr 4 + # handle negative overflow + if n == 0 and handleNegative: n = not(BiggestUInt 0) + +proc toHex*(x: BiggestUInt, len: Positive): string {.noSideEffect.} = + ## Converts `x` to its hexadecimal representation. + ## + ## The resulting string will be exactly `len` characters long. No prefix like + ## ``0x`` is generated. + runnableExamples: + let + a = 62'u64 + b = 4097'u64 + doAssert a.toHex(3) == "03E" + doAssert b.toHex(3) == "001" + doAssert b.toHex(4) == "1001" + toHexImpl(x, len, false) + proc toHex*(x: BiggestInt, len: Positive): string {.noSideEffect, rtl, extern: "nsuToHex".} = ## Converts `x` to its hexadecimal representation. @@ -948,25 +973,19 @@ proc toHex*(x: BiggestInt, len: Positive): string {.noSideEffect, let a = 62 b = 4097 + c = -8 doAssert a.toHex(3) == "03E" doAssert b.toHex(3) == "001" doAssert b.toHex(4) == "1001" - const - HexChars = "0123456789ABCDEF" - var - n = x - result = newString(len) - for j in countdown(len-1, 0): - result[j] = HexChars[int(n and 0xF)] - n = n shr 4 - # handle negative overflow - if n == 0 and x < 0: n = -1 + doAssert c.toHex(6) == "FFFFF8" + toHexImpl(cast[BiggestUInt](x), len, x < 0) -proc toHex*[T: SomeInteger](x: T): string = +proc toHex*[T: SomeInteger](x: T): string {.noSideEffect.} = ## Shortcut for ``toHex(x, T.sizeof * 2)`` runnableExamples: doAssert toHex(1984'i64) == "00000000000007C0" - toHex(BiggestInt(x), T.sizeof * 2) + doAssert toHex(1984'i16) == "07C0" + toHexImpl(cast[BiggestUInt](x), 2*sizeof(T), x < 0) proc toHex*(s: string): string {.noSideEffect, rtl.} = ## Converts a bytes string to its hexadecimal representation. diff --git a/tests/stdlib/tstrutil.nim b/tests/stdlib/tstrutil.nim index 5702b7341..ef5b041be 100644 --- a/tests/stdlib/tstrutil.nim +++ b/tests/stdlib/tstrutil.nim @@ -3,8 +3,6 @@ import strutils -import macros - template rejectParse(e) = try: discard e @@ -296,6 +294,10 @@ assert "/1/2/3".rfind('0') == -1 assert(toHex(100i16, 32) == "00000000000000000000000000000064") assert(toHex(-100i16, 32) == "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9C") +assert(toHex(high(uint64)) == "FFFFFFFFFFFFFFFF") +assert(toHex(high(uint64), 16) == "FFFFFFFFFFFFFFFF") +assert(toHex(high(uint64), 32) == "0000000000000000FFFFFFFFFFFFFFFF") + assert "".parseHexStr == "" assert "00Ff80".parseHexStr == "\0\xFF\x80" try: From 77df02313d92859cd0d8faa87a139b4b6ea0f7d9 Mon Sep 17 00:00:00 2001 From: cooldome Date: Fri, 4 Sep 2020 09:26:14 +0100 Subject: [PATCH 0569/3768] fix #15238 (#15262) * fix_15238 * fix test --- compiler/sempass2.nim | 1 + tests/arc/t14383.nim | 18 +++++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 41d83ed55..dc833ce34 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -826,6 +826,7 @@ proc trackCall(tracked: PEffects; n: PNode) = for i in 1.. Date: Fri, 4 Sep 2020 17:04:27 +0800 Subject: [PATCH 0570/3768] Fix #15219 SQL escape in db_mysql is not enough (#15234) --- lib/impure/db_mysql.nim | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/impure/db_mysql.nim b/lib/impure/db_mysql.nim index c96829830..c3bcee677 100644 --- a/lib/impure/db_mysql.nim +++ b/lib/impure/db_mysql.nim @@ -118,10 +118,24 @@ when false: proc dbQuote*(s: string): string = ## DB quotes the string. - result = "'" + result = newStringOfCap(s.len + 2) + result.add "'" for c in items(s): - if c == '\'': add(result, "''") - else: add(result, c) + # see https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html#mysql-escaping + case c + of '\0': result.add "\\0" + of '\b': result.add "\\b" + of '\t': result.add "\\t" + of '\l': result.add "\\n" + of '\r': result.add "\\r" + of '\x1a': result.add "\\Z" + of '"': result.add "\\\"" + of '%': result.add "\\%" + of '\'': result.add "\\'" + of '\\': result.add "\\\\" + of '_': result.add "\\_" + of Letters+Digits: result.add c + else: result.add "\\" & $ord(c) add(result, '\'') proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string = From 70d62387568d55cd276472f28ce22ab8bafadf1c Mon Sep 17 00:00:00 2001 From: IDF Date: Sat, 5 Sep 2020 00:27:51 +0300 Subject: [PATCH 0571/3768] Add SSL_CTX_set_session_id_context (#15233) * Added SSL_CTX_set_session_id_context() * Added basic nimdoc * Raise an error if sessionIdContext is longer than the maximum length * Update nimdocs --- lib/pure/net.nim | 16 ++++++++++++++++ lib/wrappers/openssl.nim | 1 + 2 files changed, 17 insertions(+) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 239e3e9f6..5d2ec8e30 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -825,6 +825,22 @@ when defineSsl: else: result = getPeerCertificates(socket.sslHandle) + proc `sessionIdContext=`*(ctx: SslContext, sidCtx: string) = + ## Sets the session id context in which a session can be reused. + ## Used for permitting clients to reuse a session id instead of + ## doing a new handshake. + ## + ## TLS clients might attempt to resume a session using the session id context, + ## thus it must be set if verifyMode is set to CVerifyPeer or CVerifyPeerUseEnvVars, + ## otherwise the connection will fail and SslError will be raised if resumption occurs. + ## + ## - Only useful if set server-side. + ## - Should be unique per-application to prevent clients from malfunctioning. + ## - sidCtx must be at most 32 characters in length. + if sidCtx.len > 32: + raiseSSLError("sessionIdContext must be shorter than 32 characters") + SSL_CTX_set_session_id_context(ctx.context, sidCtx, sidCtx.len) + proc getSocketError*(socket: Socket): OSErrorCode = ## Checks ``osLastError`` for a valid error. If it has been reset it uses ## the last error stored in the socket object. diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim index a37c5b1be..becabad99 100644 --- a/lib/wrappers/openssl.nim +++ b/lib/wrappers/openssl.nim @@ -436,6 +436,7 @@ proc SSL_new*(context: SslCtx): SslPtr{.cdecl, dynlib: DLLSSLName, importc.} proc SSL_free*(ssl: SslPtr){.cdecl, dynlib: DLLSSLName, importc.} proc SSL_get_SSL_CTX*(ssl: SslPtr): SslCtx {.cdecl, dynlib: DLLSSLName, importc.} proc SSL_set_SSL_CTX*(ssl: SslPtr, ctx: SslCtx): SslCtx {.cdecl, dynlib: DLLSSLName, importc.} +proc SSL_CTX_set_session_id_context*(context: SslCtx, sid_ctx: string, sid_ctx_len: int){.cdecl, dynlib: DLLSSLName, importc.} proc SSL_get0_verified_chain*(ssl: SslPtr): PSTACK {.cdecl, dynlib: DLLSSLName, importc.} proc SSL_CTX_new*(meth: PSSL_METHOD): SslCtx{.cdecl, From 35ff17410f303ce0434ef149f3e372d7243f41ab Mon Sep 17 00:00:00 2001 From: Clyybber Date: Sat, 5 Sep 2020 22:01:47 +0200 Subject: [PATCH 0572/3768] Expand hoisted default params in sem (#15270) * Expand hoisted default params in sem Introduce ast.newTree{I,IT} Add test for default params in procs * Cleanup * Simplify hoist transformation and expand test --- compiler/ast.nim | 78 ++++++++++---------- compiler/docgen.nim | 8 +- compiler/lowerings.nim | 11 --- compiler/parser.nim | 2 +- compiler/pragmas.nim | 6 +- compiler/semdata.nim | 8 +- compiler/semexprs.nim | 59 +++++++++++++-- compiler/sempass2.nim | 2 +- compiler/semtypinst.nim | 2 +- compiler/transf.nim | 47 ------------ compiler/vmdeps.nim | 3 +- tests/defaultprocparam/tdefaultprocparam.nim | 75 +++++++++++++++++++ 12 files changed, 178 insertions(+), 123 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 23b564af3..b1a3a4a7b 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -312,10 +312,6 @@ const # the compiler will avoid printing such names # in user messages. - sfHoisted* = sfForward - # an expression was hoised to an anonymous variable. - # the flag is applied to the var/let symbol - sfNoForward* = sfRegister # forward declarations are not required (per module) sfReorder* = sfForward @@ -1121,12 +1117,49 @@ proc newNode*(kind: TNodeKind): PNode = writeStackTrace() inc gNodeId +proc newNodeI*(kind: TNodeKind, info: TLineInfo): PNode = + result = PNode(kind: kind, info: info) + when defined(useNodeIds): + result.id = gNodeId + if result.id == nodeIdToDebug: + echo "KIND ", result.kind + writeStackTrace() + inc gNodeId + +proc newNodeI*(kind: TNodeKind, info: TLineInfo, children: int): PNode = + result = PNode(kind: kind, info: info) + if children > 0: + newSeq(result.sons, children) + when defined(useNodeIds): + result.id = gNodeId + if result.id == nodeIdToDebug: + echo "KIND ", result.kind + writeStackTrace() + inc gNodeId + +proc newNodeIT*(kind: TNodeKind, info: TLineInfo, typ: PType): PNode = + result = newNode(kind) + result.info = info + result.typ = typ + proc newTree*(kind: TNodeKind; children: varargs[PNode]): PNode = result = newNode(kind) if children.len > 0: result.info = children[0].info result.sons = @children +proc newTreeI*(kind: TNodeKind; info: TLineInfo; children: varargs[PNode]): PNode = + result = newNodeI(kind, info) + if children.len > 0: + result.info = children[0].info + result.sons = @children + +proc newTreeIT*(kind: TNodeKind; info: TLineInfo; typ: PType; children: varargs[PNode]): PNode = + result = newNodeIT(kind, info, typ) + if children.len > 0: + result.info = children[0].info + result.sons = @children + template previouslyInferred*(t: PType): PType = if t.sons.len > 1: t.lastSon else: nil @@ -1227,43 +1260,6 @@ proc newSymNode*(sym: PSym, info: TLineInfo): PNode = result.typ = sym.typ result.info = info -proc newNodeI*(kind: TNodeKind, info: TLineInfo): PNode = - result = PNode(kind: kind, info: info) - when defined(useNodeIds): - result.id = gNodeId - if result.id == nodeIdToDebug: - echo "KIND ", result.kind - writeStackTrace() - inc gNodeId - -proc newNodeI*(kind: TNodeKind, info: TLineInfo, children: int): PNode = - result = PNode(kind: kind, info: info) - if children > 0: - newSeq(result.sons, children) - when defined(useNodeIds): - result.id = gNodeId - if result.id == nodeIdToDebug: - echo "KIND ", result.kind - writeStackTrace() - inc gNodeId - -proc newNode*(kind: TNodeKind, info: TLineInfo, sons: TNodeSeq = @[], - typ: PType = nil): PNode = - # XXX use shallowCopy here for ownership transfer: - result = PNode(kind: kind, info: info, typ: typ) - result.sons = sons - when defined(useNodeIds): - result.id = gNodeId - if result.id == nodeIdToDebug: - echo "KIND ", result.kind - writeStackTrace() - inc gNodeId - -proc newNodeIT*(kind: TNodeKind, info: TLineInfo, typ: PType): PNode = - result = newNode(kind) - result.info = info - result.typ = typ - proc newIntNode*(kind: TNodeKind, intVal: BiggestInt): PNode = result = newNode(kind) result.intVal = intVal diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 1e0756d64..ff3a540be 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -992,8 +992,8 @@ proc documentEffect(cache: IdentCache; n, x: PNode, effectType: TSpecialWord, id # set the type so that the following analysis doesn't screw up: effects[i].typ = real[i].typ - result = newNode(nkExprColonExpr, n.info, @[ - newIdentNode(getIdent(cache, specialWords[effectType]), n.info), effects]) + result = newTreeI(nkExprColonExpr, n.info, + newIdentNode(getIdent(cache, specialWords[effectType]), n.info), effects) proc documentWriteEffect(cache: IdentCache; n: PNode; flag: TSymFlag; pragmaName: string): PNode = let s = n[namePos].sym @@ -1005,8 +1005,8 @@ proc documentWriteEffect(cache: IdentCache; n: PNode; flag: TSymFlag; pragmaName effects.add params[i] if effects.len > 0: - result = newNode(nkExprColonExpr, n.info, @[ - newIdentNode(getIdent(cache, pragmaName), n.info), effects]) + result = newTreeI(nkExprColonExpr, n.info, + newIdentNode(getIdent(cache, pragmaName), n.info), effects) proc documentRaises*(cache: IdentCache; n: PNode) = if n[namePos].kind != nkSym: return diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index 5e75c36de..f6ca98196 100644 --- a/compiler/lowerings.nim +++ b/compiler/lowerings.nim @@ -369,14 +369,3 @@ proc genLen*(g: ModuleGraph; n: PNode): PNode = result[0] = newSymNode(getSysMagic(g, n.info, "len", mLengthSeq)) result[1] = n -proc hoistExpr*(varSection, expr: PNode, name: PIdent, owner: PSym): PSym = - result = newSym(skLet, name, owner, varSection.info, owner.options) - result.flags.incl sfHoisted - result.typ = expr.typ - - var varDef = newNodeI(nkIdentDefs, varSection.info, 3) - varDef[0] = newSymNode(result) - varDef[1] = newNodeI(nkEmpty, varSection.info) - varDef[2] = expr - - varSection.add varDef diff --git a/compiler/parser.nim b/compiler/parser.nim index e7db8d8b1..0fa860c99 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -1444,7 +1444,7 @@ proc parseExprStmt(p: var Parser): PNode = result.add(commandParam(p, isFirstParam, pmNormal)) if p.tok.tokType != tkComma: break elif p.tok.indent < 0 and isExprStart(p): - result = newNode(nkCommand, a.info, @[a]) + result = newTreeI(nkCommand, a.info, a) while true: result.add(commandParam(p, isFirstParam, pmNormal)) if p.tok.tokType != tkComma: break diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index b2529a5b8..71bd6f81f 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -532,7 +532,7 @@ proc processLink(c: PContext, n: PNode) = proc semAsmOrEmit*(con: PContext, n: PNode, marker: char): PNode = case n[1].kind of nkStrLit, nkRStrLit, nkTripleStrLit: - result = newNode(if n.kind == nkAsmStmt: nkAsmStmt else: nkArgList, n.info) + result = newNodeI(if n.kind == nkAsmStmt: nkAsmStmt else: nkArgList, n.info) var str = n[1].strVal if str == "": localError(con.config, n.info, "empty 'asm' statement") @@ -563,7 +563,7 @@ proc semAsmOrEmit*(con: PContext, n: PNode, marker: char): PNode = a = c + 1 else: illFormedAstLocal(n, con.config) - result = newNode(nkAsmStmt, n.info) + result = newNodeI(nkAsmStmt, n.info) proc pragmaEmit(c: PContext, n: PNode) = if n.kind notin nkPragmaCallKinds or n.len != 2: @@ -626,7 +626,7 @@ proc processPragma(c: PContext, n: PNode, i: int) = invalidPragma(c, n) var userPragma = newSym(skTemplate, it[1].ident, nil, it.info, c.config.options) - userPragma.ast = newNode(nkPragma, n.info, n.sons[i+1..^1]) + userPragma.ast = newTreeI(nkPragma, n.info, n.sons[i+1..^1]) strTableAdd(c.userPragmas, userPragma) proc pragmaRaisesOrTags(c: PContext, n: PNode) = diff --git a/compiler/semdata.nim b/compiler/semdata.nim index b99ddcba3..b15f65967 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -381,8 +381,7 @@ proc makeNotType*(c: PContext, t1: PType): PType = result.flags.incl tfHasMeta proc nMinusOne(c: PContext; n: PNode): PNode = - result = newNode(nkCall, n.info, @[ - newSymNode(getSysMagic(c.graph, n.info, "pred", mPred)), n]) + result = newTreeI(nkCall, n.info, newSymNode(getSysMagic(c.graph, n.info, "pred", mPred)), n) # Remember to fix the procs below this one when you make changes! proc makeRangeWithStaticExpr*(c: PContext, n: PNode): PType = @@ -391,9 +390,8 @@ proc makeRangeWithStaticExpr*(c: PContext, n: PNode): PType = result.sons = @[intType] if n.typ != nil and n.typ.n == nil: result.flags.incl tfUnresolved - result.n = newNode(nkRange, n.info, @[ - newIntTypeNode(0, intType), - makeStaticExpr(c, nMinusOne(c, n))]) + result.n = newTreeI(nkRange, n.info, newIntTypeNode(0, intType), + makeStaticExpr(c, nMinusOne(c, n))) template rangeHasUnresolvedStatic*(t: PType): bool = tfUnresolved in t.flags diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 575d0b774..0e1c5e9d3 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1556,10 +1556,9 @@ proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode = # this is ugly. XXX Semantic checking should use the ``nfSem`` flag for # nodes? let aOrig = nOrig[0] - result = newNode(nkCall, n.info, sons = @[setterId, a[0], - semExprWithType(c, n[1])]) + result = newTreeI(nkCall, n.info, setterId, a[0], semExprWithType(c, n[1])) result.flags.incl nfDotSetter - let orig = newNode(nkCall, n.info, sons = @[setterId, aOrig[0], nOrig[1]]) + let orig = newTreeI(nkCall, n.info, setterId, aOrig[0], nOrig[1]) result = semOverloadedCallAnalyseEffects(c, result, orig, {}) if result != nil: @@ -2041,7 +2040,7 @@ proc semQuoteAst(c: PContext, n: PNode): PNode = dummyTemplate[paramsPos].add getSysSym(c.graph, n.info, "untyped").newSymNode # return type ids.add getSysSym(c.graph, n.info, "untyped").newSymNode # params type ids.add c.graph.emptyNode # no default value - dummyTemplate[paramsPos].add newNode(nkIdentDefs, n.info, ids) + dummyTemplate[paramsPos].add newTreeI(nkIdentDefs, n.info, ids) var tmpl = semTemplateDef(c, dummyTemplate) quotes[0] = tmpl[namePos] @@ -2053,10 +2052,10 @@ proc semQuoteAst(c: PContext, n: PNode): PNode = newIdentNode(getIdent(c.cache, "newIdentNode"), n.info) else: identNodeSym.newSymNode - quotes[1] = newNode(nkCall, n.info, @[identNode, newStrNode(nkStrLit, "result")]) - result = newNode(nkCall, n.info, @[ + quotes[1] = newTreeI(nkCall, n.info, identNode, newStrNode(nkStrLit, "result")) + result = newTreeI(nkCall, n.info, createMagic(c.graph, "getAst", mExpandToAst).newSymNode, - newNode(nkCall, n.info, quotes)]) + newTreeI(nkCall, n.info, quotes)) result = semExpandToAst(c, result) proc tryExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = @@ -2579,6 +2578,43 @@ proc shouldBeBracketExpr(n: PNode): bool = n[0] = be return true +proc hoistParamsUsedInDefault(c: PContext, call, letSection, defExpr: var PNode) = + # This takes care of complicated signatures such as: + # proc foo(a: int, b = a) + # proc bar(a: int, b: int, c = a + b) + # + # The recursion may confuse you. It performs two duties: + # + # 1) extracting all referenced params from default expressions + # into a let section preceding the call + # + # 2) replacing the "references" within the default expression + # with these extracted skLet symbols. + # + # The first duty is carried out directly in the code here, while the second + # duty is activated by returning a non-nil value. The caller is responsible + # for replacing the input to the function with the returned non-nil value. + # (which is the hoisted symbol) + if defExpr.kind == nkSym and defExpr.sym.kind == skParam and defExpr.sym.owner == call[0].sym: + let paramPos = defExpr.sym.position + 1 + + if call[paramPos].kind != nkSym: + let hoistedVarSym = newSym(skLet, getIdent(c.graph.cache, genPrefix), c.p.owner, letSection.info, c.p.owner.options) + hoistedVarSym.typ = call[paramPos].typ + + letSection.add newTreeI(nkIdentDefs, letSection.info, + newSymNode(hoistedVarSym), + newNodeI(nkEmpty, letSection.info), + call[paramPos]) + + call[paramPos] = newSymNode(hoistedVarSym) # Refer the original arg to its hoisted sym + + # arg we refer to is a sym, wether introduced by hoisting or not doesn't matter, we simply reuse it + defExpr = call[paramPos] + else: + for i in 0.. Date: Sat, 5 Sep 2020 22:00:59 +0100 Subject: [PATCH 0573/3768] Fix c_malloc inside syslocks.nim to take size_t (#15268) [backport] * update c_malloc's to csize_t --- lib/system/syslocks.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/system/syslocks.nim b/lib/system/syslocks.nim index fa4164b95..b7f7aa6bf 100644 --- a/lib/system/syslocks.nim +++ b/lib/system/syslocks.nim @@ -145,13 +145,13 @@ else: SysCond = ptr SysCondObj when not declared(c_malloc): - proc c_malloc(size: csize): pointer {. + proc c_malloc(size: csize_t): pointer {. importc: "malloc", header: "".} proc c_free(p: pointer) {. importc: "free", header: "".} proc initSysLock(L: var SysLock, attr: ptr SysLockAttr = nil) = - L = cast[SysLock](c_malloc(sizeof(SysLockObj))) + L = cast[SysLock](c_malloc(csize_t(sizeof(SysLockObj)))) initSysLockAux(L[], attr) proc deinitSys(L: var SysLock) = @@ -201,7 +201,7 @@ else: when defined(ios): proc initSysCond(cond: var SysCond, cond_attr: ptr SysCondAttr = nil) = - cond = cast[SysCond](c_malloc(sizeof(SysCondObj))) + cond = cast[SysCond](c_malloc(csize_t(sizeof(SysCondObj)))) initSysCondAux(cond[], cond_attr) proc deinitSysCond(cond: var SysCond) = From 072c6556539706f8301731ebb695ffed848cfe71 Mon Sep 17 00:00:00 2001 From: genotrance Date: Sun, 6 Sep 2020 00:05:35 -0500 Subject: [PATCH 0574/3768] Bump nimble (#15272) --- koch.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/koch.nim b/koch.nim index 68a880d0d..d9bbb4176 100644 --- a/koch.nim +++ b/koch.nim @@ -10,7 +10,7 @@ # const - NimbleStableCommit = "a3e1267265965fc79a50b66999b660e0920db8cd" # master + NimbleStableCommit = "311e5a8cd2c53056bdec24c81393a46b41f96241" # master FusionStableCommit = "319aac4d43b04113831b529f8003e82f4af6a4a5" when not defined(windows): From 2f6d04fd5d302646d40abc60b611653ec58463d0 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 6 Sep 2020 22:01:39 +0200 Subject: [PATCH 0575/3768] strict funcs: use control flow information for a more precise analysis (#15271) * strict funcs: use control flow information for a more precise analysis * cursor inference uses control flow information --- compiler/varpartitions.nim | 99 ++++++++++++++++++++------ tests/effects/tfuncs_cannot_mutate.nim | 11 ++- 2 files changed, 87 insertions(+), 23 deletions(-) diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim index 23ad8fe09..6cf8e1932 100644 --- a/compiler/varpartitions.nim +++ b/compiler/varpartitions.nim @@ -39,18 +39,26 @@ type of dependsOn: parent: int of isRootOf: graphIndex: int sym: PSym + aliveStart, aliveEnd: int # the range for which the variable is alive. MutationInfo* = object param: PSym mutatedHere, connectedVia: TLineInfo flags: set[SubgraphFlag] + maxMutation, minConnection: int + mutations: seq[int] Partitions = object + abstractTime: int s: seq[VarIndex] graphs: seq[MutationInfo] unanalysableMutation, performCursorInference: bool inAsgnSource, inConstructor, inNoSideEffectSection: int +proc mutationAfterConnection(g: MutationInfo): bool {.inline.} = + #echo g.maxMutation, " ", g.minConnection, " ", g.param + g.maxMutation > g.minConnection + proc `$`*(config: ConfigRef; g: MutationInfo): string = result = "" if g.flags == {isMutated, connectsConstParam}: @@ -68,27 +76,31 @@ proc `$`*(config: ConfigRef; g: MutationInfo): string = proc hasSideEffect(c: var Partitions; info: var MutationInfo): bool = for g in mitems c.graphs: - if g.flags == {isMutated, connectsConstParam}: + if g.flags == {isMutated, connectsConstParam} and mutationAfterConnection(g): info = g return true return false template isConstParam(a): bool = a.kind == skParam and a.typ.kind != tyVar -proc registerVariable(c: var Partitions; n: PNode) = - if n.kind == nkSym: - if isConstParam(n.sym): - c.s.add VarIndex(kind: isRootOf, graphIndex: c.graphs.len, sym: n.sym) - c.graphs.add MutationInfo(param: n.sym, mutatedHere: unknownLineInfo, - connectedVia: unknownLineInfo, flags: {connectsConstParam}) - else: - c.s.add VarIndex(kind: isEmptyRoot, sym: n.sym) - proc variableId(c: Partitions; x: PSym): int {.inline.} = for i in 0 ..< c.s.len: if c.s[i].sym == x: return i return -1 +proc registerVariable(c: var Partitions; n: PNode) = + if n.kind == nkSym and variableId(c, n.sym) < 0: + if isConstParam(n.sym): + c.s.add VarIndex(kind: isRootOf, graphIndex: c.graphs.len, sym: n.sym, + aliveStart: c.abstractTime, aliveEnd: c.abstractTime) + c.graphs.add MutationInfo(param: n.sym, mutatedHere: unknownLineInfo, + connectedVia: unknownLineInfo, flags: {connectsConstParam}, + maxMutation: -1, minConnection: high(int), + mutations: @[]) + else: + c.s.add VarIndex(kind: isEmptyRoot, sym: n.sym, + aliveStart: c.abstractTime, aliveEnd: c.abstractTime) + proc root(v: var Partitions; start: int): int = result = start var depth = 0 @@ -101,7 +113,9 @@ proc root(v: var Partitions; start: int): int = while v.s[it].kind == dependsOn: let next = v.s[it].parent v.s[it] = VarIndex(kind: dependsOn, parent: result, - sym: v.s[it].sym, flags: v.s[it].flags) + sym: v.s[it].sym, flags: v.s[it].flags, + aliveStart: v.s[it].aliveStart, + aliveEnd: v.s[it].aliveEnd) it = next proc potentialMutation(v: var Partitions; s: PSym; info: TLineInfo) = @@ -111,16 +125,22 @@ proc potentialMutation(v: var Partitions; s: PSym; info: TLineInfo) = case v.s[r].kind of isEmptyRoot: v.s[r] = VarIndex(kind: isRootOf, graphIndex: v.graphs.len, - sym: v.s[r].sym, flags: v.s[r].flags) + sym: v.s[r].sym, flags: v.s[r].flags, + aliveStart: v.s[r].aliveStart, + aliveEnd: v.s[r].aliveEnd) v.graphs.add MutationInfo(param: if isConstParam(s): s else: nil, mutatedHere: info, - connectedVia: unknownLineInfo, flags: {isMutated}) + connectedVia: unknownLineInfo, flags: {isMutated}, + maxMutation: v.abstractTime, minConnection: high(int), + mutations: @[v.abstractTime]) of isRootOf: let g = addr v.graphs[v.s[r].graphIndex] if g.param == nil and isConstParam(s): g.param = s - if g.mutatedHere == unknownLineInfo: + if v.abstractTime > g.maxMutation: g.mutatedHere = info + g.maxMutation = v.abstractTime g.flags.incl isMutated + g.mutations.add v.abstractTime else: assert false, "cannot happen" else: @@ -150,24 +170,39 @@ proc connect(v: var Partitions; a, b: PSym; info: TLineInfo) = # for now we always make 'rb' the slave and 'ra' the master: var rbFlags: set[SubgraphFlag] = {} var mutatedHere = unknownLineInfo + var mut = 0 + var con = v.abstractTime + var gb: ptr MutationInfo = nil if v.s[rb].kind == isRootOf: - var gb = addr v.graphs[v.s[rb].graphIndex] + gb = addr v.graphs[v.s[rb].graphIndex] if param == nil: param = gb.param mutatedHere = gb.mutatedHere rbFlags = gb.flags + mut = gb.maxMutation + con = min(con, gb.minConnection) - v.s[rb] = VarIndex(kind: dependsOn, parent: ra, sym: v.s[rb].sym, flags: v.s[rb].flags) + v.s[rb] = VarIndex(kind: dependsOn, parent: ra, sym: v.s[rb].sym, flags: v.s[rb].flags, + aliveStart: v.s[rb].aliveStart, + aliveEnd: v.s[rb].aliveEnd) case v.s[ra].kind of isEmptyRoot: - v.s[ra] = VarIndex(kind: isRootOf, graphIndex: v.graphs.len, sym: v.s[ra].sym, flags: v.s[ra].flags) + v.s[ra] = VarIndex(kind: isRootOf, graphIndex: v.graphs.len, sym: v.s[ra].sym, + flags: v.s[ra].flags, + aliveStart: v.s[ra].aliveStart, + aliveEnd: v.s[ra].aliveEnd) v.graphs.add MutationInfo(param: param, mutatedHere: mutatedHere, - connectedVia: info, flags: paramFlags + rbFlags) + connectedVia: info, flags: paramFlags + rbFlags, + maxMutation: mut, minConnection: con, + mutations: if gb != nil: gb.mutations else: @[]) of isRootOf: var g = addr v.graphs[v.s[ra].graphIndex] if g.param == nil: g.param = param if g.mutatedHere == unknownLineInfo: g.mutatedHere = mutatedHere + g.minConnection = min(g.minConnection, con) g.connectedVia = info g.flags.incl paramFlags + rbFlags + if gb != nil: + g.mutations.add gb.mutations else: assert false, "cannot happen" @@ -364,6 +399,7 @@ proc deps(c: var Partitions; dest, src: PNode) = rhsIsSink(c, src) proc traverse(c: var Partitions; n: PNode) = + inc c.abstractTime case n.kind of nkLetSection, nkVarSection: for child in n: @@ -384,10 +420,18 @@ proc traverse(c: var Partitions; n: PNode) = traverse(c, n[1]) dec c.inAsgnSource deps(c, n[0], n[1]) - of nkNone..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef, + of nkSym: + dec c.abstractTime + if n.sym.kind in {skVar, skResult, skTemp, skLet, skForVar, skParam}: + let id = variableId(c, n.sym) + if id >= 0: + c.s[id].aliveEnd = max(c.s[id].aliveEnd, c.abstractTime) + + of nkNone..pred(nkSym), succ(nkSym)..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt, nkExportStmt, nkPragma, nkCommentStmt, nkBreakState, nkTypeOfExpr: + dec c.abstractTime discard "do not follow the construct" of nkCallKinds: for child in n: traverse(c, child) @@ -450,6 +494,14 @@ proc traverse(c: var Partitions; n: PNode) = inc c.inNoSideEffectSection, enforceNoSideEffects traverse(c, n.lastSon) dec c.inNoSideEffectSection, enforceNoSideEffects + of nkWhileStmt, nkForStmt, nkParForStmt: + for child in n: traverse(c, child) + # analyse loops twice so that 'abstractTime' suffices to detect cases + # like: + # while cond: + # mutate(graph) + # connect(graph, cursorVar) + for child in n: traverse(c, child) else: for child in n: traverse(c, child) @@ -465,6 +517,13 @@ proc mutatesNonVarParameters*(s: PSym; n: PNode; info: var MutationInfo): bool = traverse(par, n) result = hasSideEffect(par, info) +proc dangerousMutation(g: MutationInfo; v: VarIndex): bool = + if isMutated in g.flags: + for m in g.mutations: + if m in v.aliveStart..v.aliveEnd: + return true + return false + proc computeCursors*(s: PSym; n: PNode; config: ConfigRef) = var par = Partitions(performCursorInference: true) if s.kind notin {skMacro, skModule}: @@ -481,7 +540,7 @@ proc computeCursors*(s: PSym; n: PNode; config: ConfigRef) = v.sym.flags * {sfThread, sfGlobal} == {} and hasDestructor(v.sym.typ) and v.sym.typ.skipTypes({tyGenericInst, tyAlias}).kind != tyOwned: let rid = root(par, i) - if par.s[rid].kind == isRootOf and isMutated in par.graphs[par.s[rid].graphIndex].flags: + if par.s[rid].kind == isRootOf and dangerousMutation(par.graphs[par.s[rid].graphIndex], par.s[i]): discard "cannot cursor into a graph that is mutated" else: v.sym.flags.incl sfCursor diff --git a/tests/effects/tfuncs_cannot_mutate.nim b/tests/effects/tfuncs_cannot_mutate.nim index 2990894ed..4768af2b3 100644 --- a/tests/effects/tfuncs_cannot_mutate.nim +++ b/tests/effects/tfuncs_cannot_mutate.nim @@ -1,9 +1,9 @@ discard """ errormsg: "'mutate' can have side effects" nimout: '''an object reachable from 'n' is potentially mutated -tfuncs_cannot_mutate.nim(34, 15) the mutation is here -tfuncs_cannot_mutate.nim(32, 7) is the statement that connected the mutation to the parameter''' - line: 28 +tfuncs_cannot_mutate.nim(39, 15) the mutation is here +tfuncs_cannot_mutate.nim(37, 7) is the statement that connected the mutation to the parameter''' + line: 33 """ {.experimental: "strictFuncs".} @@ -13,6 +13,11 @@ type le, ri: Node data: string +func insert(x: var seq[Node]; yyy: Node) = + let L = x.len + x.setLen L + 1 + x[L] = yyy + func len(n: Node): int = var it = n while it != nil: From e08b802d79e173d230b893ed96fd82c3c5228161 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 7 Sep 2020 18:10:20 +0800 Subject: [PATCH 0576/3768] more Protocol supports in windows (#15274) [backport:1.2] --- lib/pure/nativesockets.nim | 24 +++++++++++++++++++----- tests/stdlib/tnativesockets.nim | 29 +++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 5 deletions(-) create mode 100644 tests/stdlib/tnativesockets.nim diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim index 0b0c4b398..9303dff1d 100644 --- a/lib/pure/nativesockets.nim +++ b/lib/pure/nativesockets.nim @@ -68,11 +68,11 @@ type Protocol* = enum ## third argument to `socket` proc IPPROTO_TCP = 6, ## Transmission control protocol. IPPROTO_UDP = 17, ## User datagram protocol. - IPPROTO_IP, ## Internet protocol. Unsupported on Windows. - IPPROTO_IPV6, ## Internet Protocol Version 6. Unsupported on Windows. + IPPROTO_IP, ## Internet protocol. + IPPROTO_IPV6, ## Internet Protocol Version 6. IPPROTO_RAW, ## Raw IP Packets Protocol. Unsupported on Windows. - IPPROTO_ICMP ## Control message protocol. Unsupported on Windows. - IPPROTO_ICMPV6 ## Control message protocol for IPv6. Unsupported on Windows. + IPPROTO_ICMP ## Control message protocol. + IPPROTO_ICMPV6 ## Control message protocol for IPv6. Servent* = object ## information about a service name*: string @@ -174,7 +174,21 @@ else: result = cint(ord(typ)) proc toInt(p: Protocol): cint = - result = cint(ord(p)) + case p + of IPPROTO_IP: + result = 0.cint + of IPPROTO_ICMP: + result = 1.cint + of IPPROTO_TCP: + result = 6.cint + of IPPROTO_UDP: + result = 17.cint + of IPPROTO_IPV6: + result = 41.cint + of IPPROTO_ICMPV6: + result = 58.cint + else: + result = cint(ord(p)) proc toSockType*(protocol: Protocol): SockType = result = case protocol diff --git a/tests/stdlib/tnativesockets.nim b/tests/stdlib/tnativesockets.nim new file mode 100644 index 000000000..b0cfd09cf --- /dev/null +++ b/tests/stdlib/tnativesockets.nim @@ -0,0 +1,29 @@ +discard """ + cmd: "nim c -r --styleCheck:hint --panics:on $options $file" + targets: "c" + nimout: "" + action: "run" + exitcode: 0 + timeout: 60.0 +""" + +import nativesockets + + +when defined(windows): + doAssert toInt(IPPROTO_IP) == 0.cint + doAssert toInt(IPPROTO_ICMP) == 1.cint + doAssert toInt(IPPROTO_TCP) == 6.cint + doAssert toInt(IPPROTO_UDP) == 17.cint + doAssert toInt(IPPROTO_IPV6) == 41.cint + doAssert toInt(IPPROTO_ICMPV6) == 58.cint + doAssert toInt(IPPROTO_RAW) == 20.cint + + # no changes to enum value + doAssert ord(IPPROTO_TCP) == 6 + doAssert ord(IPPROTO_UDP) == 17 + doAssert ord(IPPROTO_IP) == 18 + doAssert ord(IPPROTO_IPV6) == 19 + doAssert ord(IPPROTO_RAW) == 20 + doAssert ord(IPPROTO_ICMP) == 21 + doAssert ord(IPPROTO_ICMPV6) == 22 From 0b74d26d0c45e0fe16126bd264072783afacd12a Mon Sep 17 00:00:00 2001 From: Andy Davidoff Date: Mon, 7 Sep 2020 14:04:07 -0400 Subject: [PATCH 0577/3768] don't raise index defects on malformed ast (#15278) * don't raise index defects on malformed ast * style --- compiler/semcall.nim | 11 ++++++++--- compiler/semstmts.nim | 9 ++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 1a6f754ee..3e4ed096b 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -495,9 +495,14 @@ proc updateDefaultParams(call: PNode) = proc getCallLineInfo(n: PNode): TLineInfo = case n.kind of nkAccQuoted, nkBracketExpr, nkCall, nkCallStrLit, nkCommand: - getCallLineInfo(n[0]) - of nkDotExpr: getCallLineInfo(n[1]) - else: n.info + if len(n) > 0: + return getCallLineInfo(n[0]) + of nkDotExpr: + if len(n) > 1: + return getCallLineInfo(n[1]) + else: + discard + result = n.info proc semResolvedCall(c: PContext, x: TCandidate, n: PNode, flags: TExprFlags): PNode = diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index fd4635ea5..5599eb3ef 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -324,11 +324,14 @@ proc semIdentDef(c: PContext, n: PNode, kind: TSymKind): PSym = proc getLineInfo(n: PNode): TLineInfo = case n.kind of nkPostfix: - getLineInfo(n[1]) + if len(n) > 1: + return getLineInfo(n[1]) of nkAccQuoted, nkPragmaExpr: - getLineInfo(n[0]) + if len(n) > 0: + return getLineInfo(n[0]) else: - n.info + discard + result = n.info let info = getLineInfo(n) suggestSym(c.config, info, result, c.graph.usageSym) From 098a8a7c522986ce250d4e52dc42182e5d9d7352 Mon Sep 17 00:00:00 2001 From: Scott Wadden Date: Mon, 7 Sep 2020 15:05:07 -0300 Subject: [PATCH 0578/3768] nimeval errorHook support (#15255) --- compiler/nimeval.nim | 11 +++++--- tests/compilerapi/invalid.nim | 1 + tests/compilerapi/tcompilerapi.nim | 41 ++++++++++++++++++++---------- 3 files changed, 37 insertions(+), 16 deletions(-) create mode 100644 tests/compilerapi/invalid.nim diff --git a/compiler/nimeval.nim b/compiler/nimeval.nim index df3cb2079..5d4fea9f3 100644 --- a/compiler/nimeval.nim +++ b/compiler/nimeval.nim @@ -10,9 +10,9 @@ ## exposes the Nim VM to clients. import ast, astalgo, modules, passes, condsyms, - options, sem, semdata, llstream, vm, vmdef, - modulegraphs, idents, os, pathutils, passaux, - scriptconfig + options, sem, semdata, llstream, lineinfos, vm, + vmdef, modulegraphs, idents, os, pathutils, + passaux, scriptconfig type Interpreter* = ref object ## Use Nim as an interpreter with this object @@ -134,6 +134,11 @@ proc destroyInterpreter*(i: Interpreter) = ## destructor. discard "currently nothing to do." +proc registerErrorHook*(i: Interpreter, hook: + proc (config: ConfigRef; info: TLineInfo; msg: string; + severity: Severity) {.gcsafe.}) = + i.graph.config.structuredErrorHook = hook + proc runRepl*(r: TLLRepl; searchPaths: openArray[string]; supportNimscript: bool) = diff --git a/tests/compilerapi/invalid.nim b/tests/compilerapi/invalid.nim new file mode 100644 index 000000000..3c9364402 --- /dev/null +++ b/tests/compilerapi/invalid.nim @@ -0,0 +1 @@ +noSuchProc() diff --git a/tests/compilerapi/tcompilerapi.nim b/tests/compilerapi/tcompilerapi.nim index 9d5e0e3f2..d8489c763 100644 --- a/tests/compilerapi/tcompilerapi.nim +++ b/tests/compilerapi/tcompilerapi.nim @@ -5,6 +5,7 @@ discard """ my secret 11 12 +raising VMQuit ''' joinable: "false" """ @@ -12,34 +13,35 @@ my secret ## Example program that demonstrates how to use the ## compiler as an API to embed into your own projects. -import "../../compiler" / [ast, vmdef, vm, nimeval, llstream] +import "../../compiler" / [ast, vmdef, vm, nimeval, llstream, lineinfos, options] import std / [os] -proc main() = +proc initInterpreter(script: string): Interpreter = let std = findNimStdLibCompileTime() - var intr = createInterpreter("myscript.nim", [std, parentDir(currentSourcePath), + result = createInterpreter(script , [std, parentDir(currentSourcePath), std / "pure", std / "core"]) - intr.implementRoutine("*", "exposed", "addFloats", proc (a: VmArgs) = + +proc main() = + let i = initInterpreter("myscript.nim") + i.implementRoutine("*", "exposed", "addFloats", proc (a: VmArgs) = setResult(a, getFloat(a, 0) + getFloat(a, 1) + getFloat(a, 2)) ) - - intr.evalScript() - - let foreignProc = selectRoutine(intr, "hostProgramRunsThis") + i.evalScript() + let foreignProc = i.selectRoutine("hostProgramRunsThis") if foreignProc == nil: quit "script does not export a proc of the name: 'hostProgramRunsThis'" - let res = intr.callRoutine(foreignProc, [newFloatNode(nkFloatLit, 0.9), - newFloatNode(nkFloatLit, 0.1)]) + let res = i.callRoutine(foreignProc, [newFloatNode(nkFloatLit, 0.9), + newFloatNode(nkFloatLit, 0.1)]) doAssert res.kind == nkFloatLit echo res.floatVal - let foreignValue = selectUniqueSymbol(intr, "hostProgramWantsThis") + let foreignValue = i.selectUniqueSymbol("hostProgramWantsThis") if foreignValue == nil: quit "script does not export a global of the name: hostProgramWantsThis" - let val = intr.getGlobalValue(foreignValue) + let val = i.getGlobalValue(foreignValue) doAssert val.kind in {nkStrLit..nkTripleStrLit} echo val.strVal - destroyInterpreter(intr) + i.destroyInterpreter() main() @@ -54,3 +56,16 @@ block issue9180: evalString("echo 10+1") evalString("echo 10+2") + +block error_hook: + type VMQuit = object of CatchableError + + let i = initInterpreter("invalid.nim") + i.registerErrorHook proc(config: ConfigRef; info: TLineInfo; msg: string; + severity: Severity) {.gcsafe.} = + if severity == Error and config.errorCounter >= config.errorMax: + echo "raising VMQuit" + raise newException(VMQuit, "Script error") + + doAssertRaises(VMQuit): + i.evalScript() From a81610230ddacf33f11e8cc57a379f12a985f8b6 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Tue, 8 Sep 2020 17:27:43 +0800 Subject: [PATCH 0579/3768] nativesockets docs minor [backport: 1.2] (#15285) --- lib/pure/nativesockets.nim | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim index 9303dff1d..e38dc2ecd 100644 --- a/lib/pure/nativesockets.nim +++ b/lib/pure/nativesockets.nim @@ -71,8 +71,8 @@ type IPPROTO_IP, ## Internet protocol. IPPROTO_IPV6, ## Internet Protocol Version 6. IPPROTO_RAW, ## Raw IP Packets Protocol. Unsupported on Windows. - IPPROTO_ICMP ## Control message protocol. - IPPROTO_ICMPV6 ## Control message protocol for IPv6. + IPPROTO_ICMP ## Internet Control message protocol. + IPPROTO_ICMPV6 ## Internet Control message protocol for IPv6. Servent* = object ## information about a service name*: string @@ -113,7 +113,7 @@ proc `==`*(a, b: Port): bool {.borrow.} ## ``==`` for ports. proc `$`*(p: Port): string {.borrow.} - ## returns the port number as a string + ## Returns the port number as a string proc toInt*(domain: Domain): cint ## Converts the Domain enum to a platform-dependent ``cint``. @@ -200,7 +200,7 @@ proc toSockType*(protocol: Protocol): SockType = SOCK_RAW proc close*(socket: SocketHandle) = - ## closes a socket. + ## Closes a socket. when useWinVersion: discard winlean.closesocket(socket) else: @@ -437,7 +437,7 @@ proc getHostname*(): string {.tags: [ReadIOEffect].} = result.setLen(x) proc getSockDomain*(socket: SocketHandle): Domain = - ## returns the socket's domain (AF_INET or AF_INET6). + ## Returns the socket's domain (AF_INET or AF_INET6). var name: Sockaddr_in6 var namelen = sizeof(name).SockLen if getsockname(socket, cast[ptr SockAddr](addr(name)), @@ -450,7 +450,7 @@ proc getSockDomain*(socket: SocketHandle): Domain = raise newException(IOError, "Unknown socket family in getSockDomain") proc getAddrString*(sockAddr: ptr SockAddr): string = - ## return the string representation of address within sockAddr + ## Returns the string representation of address within sockAddr if sockAddr.sa_family.cint == nativeAfInet: result = $inet_ntoa(cast[ptr Sockaddr_in](sockAddr).sin_addr) elif sockAddr.sa_family.cint == nativeAfInet6: @@ -517,7 +517,7 @@ when defined(posix) and not defined(nimdoc): copyMem(addr result.sun_path, path.cstring, path.len + 1) proc getSockName*(socket: SocketHandle): Port = - ## returns the socket's associated port number. + ## Returns the socket's associated port number. var name: Sockaddr_in when useWinVersion: name.sin_family = uint16(ord(AF_INET)) @@ -532,7 +532,7 @@ proc getSockName*(socket: SocketHandle): Port = result = Port(nativesockets.ntohs(name.sin_port)) proc getLocalAddr*(socket: SocketHandle, domain: Domain): (string, Port) = - ## returns the socket's local address and port number. + ## Returns the socket's local address and port number. ## ## Similar to POSIX's `getsockname`:idx:. case domain @@ -569,7 +569,7 @@ proc getLocalAddr*(socket: SocketHandle, domain: Domain): (string, Port) = raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr") proc getPeerAddr*(socket: SocketHandle, domain: Domain): (string, Port) = - ## returns the socket's peer address and port number. + ## Returns the socket's peer address and port number. ## ## Similar to POSIX's `getpeername`:idx: case domain From c49b88163c12d18506ef43e4d26abd5d76f68359 Mon Sep 17 00:00:00 2001 From: Miran Date: Tue, 8 Sep 2020 14:25:25 +0200 Subject: [PATCH 0580/3768] "for-loop macros" are no longer an experimental feature (#15288) --- changelog.md | 4 +++ compiler/options.nim | 2 +- compiler/semstmts.nim | 5 ++-- doc/manual.rst | 42 +++++++++++++++++++++++++++++ doc/manual_experimental.rst | 46 -------------------------------- tests/macros/tforloop_macro1.nim | 2 +- 6 files changed, 50 insertions(+), 51 deletions(-) diff --git a/changelog.md b/changelog.md index 3865f1efb..58efc42e6 100644 --- a/changelog.md +++ b/changelog.md @@ -250,6 +250,10 @@ proc mydiv(a, b): int {.raises: [].} = `.noSideEffect` stricter. [See](manual_experimental.html#stricts-funcs) for more information. +- "for-loop macros" (see [the manual](manual.html#macros-for-loop-macros)) are no longer + an experimental feature. In other words, you don't have to write pragma + `{.experimental: "forLoopMacros".}` if you want to use them. + ## Compiler changes diff --git a/compiler/options.nim b/compiler/options.nim index 56b061358..14016495f 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -154,7 +154,7 @@ type destructor, notnil, dynamicBindSym, - forLoopMacros, + forLoopMacros, # not experimental anymore; remains here for backwards compatibility caseStmtMacros, codeReordering, compiletimeFFI, diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 5599eb3ef..2c3ff2bea 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -890,9 +890,8 @@ proc handleCaseStmtMacro(c: PContext; n: PNode; flags: TExprFlags): PNode = proc semFor(c: PContext, n: PNode; flags: TExprFlags): PNode = checkMinSonsLen(n, 3, c.config) - if forLoopMacros in c.features: - result = handleForLoopMacro(c, n, flags) - if result != nil: return result + result = handleForLoopMacro(c, n, flags) + if result != nil: return result openScope(c) result = n n[^2] = semExprNoDeref(c, n[^2], {efWantIterator}) diff --git a/doc/manual.rst b/doc/manual.rst index b78e11b32..fc4c11f50 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -5469,6 +5469,48 @@ powerful programming construct that still suffices. So the "check list" is: (4) Else: Use a macro. +For loop macros +--------------- + +A macro that takes as its only input parameter an expression of the special +type ``system.ForLoopStmt`` can rewrite the entirety of a ``for`` loop: + +.. code-block:: nim + :test: "nim c $1" + + import macros + + macro enumerate(x: ForLoopStmt): untyped = + expectKind x, nnkForStmt + # we strip off the first for loop variable and use + # it as an integer counter: + result = newStmtList() + result.add newVarStmt(x[0], newLit(0)) + var body = x[^1] + if body.kind != nnkStmtList: + body = newTree(nnkStmtList, body) + body.add newCall(bindSym"inc", x[0]) + var newFor = newTree(nnkForStmt) + for i in 1..x.len-3: + newFor.add x[i] + # transform enumerate(X) to 'X' + newFor.add x[^2][1] + newFor.add body + result.add newFor + # now wrap the whole macro in a block to create a new scope + result = quote do: + block: `result` + + for a, b in enumerate(items([1, 2, 3])): + echo a, " ", b + + # without wrapping the macro in a block, we'd need to choose different + # names for `a` and `b` here to avoid redefinition errors + for a, b in enumerate([1, 2, 3, 5]): + echo a, " ", b + + + Special Types ============= diff --git a/doc/manual_experimental.rst b/doc/manual_experimental.rst index 43aa84f20..913b771c2 100644 --- a/doc/manual_experimental.rst +++ b/doc/manual_experimental.rst @@ -1030,52 +1030,6 @@ but only the statement's selector expression is used to determine which macro to call. -For loop macros ---------------- - -A macro that takes as its only input parameter an expression of the special -type ``system.ForLoopStmt`` can rewrite the entirety of a ``for`` loop: - -.. code-block:: nim - :test: "nim c $1" - - import macros - {.experimental: "forLoopMacros".} - - macro enumerate(x: ForLoopStmt): untyped = - expectKind x, nnkForStmt - # we strip off the first for loop variable and use - # it as an integer counter: - result = newStmtList() - result.add newVarStmt(x[0], newLit(0)) - var body = x[^1] - if body.kind != nnkStmtList: - body = newTree(nnkStmtList, body) - body.add newCall(bindSym"inc", x[0]) - var newFor = newTree(nnkForStmt) - for i in 1..x.len-3: - newFor.add x[i] - # transform enumerate(X) to 'X' - newFor.add x[^2][1] - newFor.add body - result.add newFor - # now wrap the whole macro in a block to create a new scope - result = quote do: - block: `result` - - for a, b in enumerate(items([1, 2, 3])): - echo a, " ", b - - # without wrapping the macro in a block, we'd need to choose different - # names for `a` and `b` here to avoid redefinition errors - for a, b in enumerate([1, 2, 3, 5]): - echo a, " ", b - - -Currently for loop macros must be enabled explicitly -via ``{.experimental: "forLoopMacros".}``. - - Term rewriting macros ===================== diff --git a/tests/macros/tforloop_macro1.nim b/tests/macros/tforloop_macro1.nim index 49918563d..a8f45c7ac 100644 --- a/tests/macros/tforloop_macro1.nim +++ b/tests/macros/tforloop_macro1.nim @@ -12,7 +12,7 @@ discard """ """ import macros -{.experimental: "forLoopMacros".} + macro mymacro(): untyped = result = newLit([1, 2, 3]) From 10988d48407e96b707b28c5900fcb0e59354e00a Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 9 Sep 2020 07:32:03 +0200 Subject: [PATCH 0581/3768] borrow checking (#15282) * refactoring: move procs to typeallowed.nim * frontend preparations for first class openArray support * prepare the code generator for first class openArray * code generation for first class openArray; WIP * code generation for open arrays, progress * added isViewType proc * preparations for borrow checking * added borrow checking to the front end --- compiler/ccgcalls.nim | 21 +++- compiler/ccgexprs.nim | 102 ++++++++++++---- compiler/ccgstmts.nim | 2 +- compiler/ccgtypes.nim | 111 ++++++++++-------- compiler/cgen.nim | 56 +++++---- compiler/options.nim | 3 +- compiler/renderer.nim | 9 +- compiler/sem.nim | 10 +- compiler/semexprs.nim | 21 ++-- compiler/sempass2.nim | 7 +- compiler/semstmts.nim | 4 +- compiler/transf.nim | 4 + compiler/trees.nim | 2 +- compiler/typeallowed.nim | 228 ++++++++++++++++++++++++++++++++++++ compiler/types.nim | 170 +-------------------------- compiler/varpartitions.nim | 36 +++--- doc/manual_experimental.rst | 52 ++++++++ tests/ccgbugs/tviews1.nim | 27 +++++ 18 files changed, 554 insertions(+), 311 deletions(-) create mode 100644 compiler/typeallowed.nim create mode 100644 tests/ccgbugs/tviews1.nim diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 5f99f357d..6d254ca6f 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -141,6 +141,13 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc, proc genBoundsCheck(p: BProc; arr, a, b: TLoc) +proc reifiedOpenArray(n: PNode): bool {.inline.} = + let x = trees.getRoot(n) + if x != nil and x.kind == skParam: + result = false + else: + result = true + proc openArrayLoc(p: BProc, formalType: PType, n: PNode): Rope = var a: TLoc @@ -174,7 +181,12 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode): Rope = else: result = "($5*)($1)+(($2)-($4)), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c), intLiteral(first), dest] - of tyOpenArray, tyVarargs, tyUncheckedArray, tyCString: + of tyOpenArray, tyVarargs: + if reifiedOpenArray(q[1]): + result = "($4*)($1.d)+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c), dest] + else: + result = "($4*)($1)+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c), dest] + of tyUncheckedArray, tyCString: result = "($4*)($1)+($2), ($3)-($2)+1" % [rdLoc(a), rdLoc(b), rdLoc(c), dest] of tyString, tySequence: let atyp = skipTypes(a.t, abstractInst) @@ -188,10 +200,13 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode): Rope = else: internalError(p.config, "openArrayLoc: " & typeToString(a.t)) else: - initLocExpr(p, n, a) + initLocExpr(p, if n.kind == nkHiddenStdConv: n[1] else: n, a) case skipTypes(a.t, abstractVar).kind of tyOpenArray, tyVarargs: - result = "$1, $1Len_0" % [rdLoc(a)] + if reifiedOpenArray(n): + result = "$1.d, $1.l" % [rdLoc(a)] + else: + result = "$1, $1Len_0" % [rdLoc(a)] of tyString, tySequence: let ntyp = skipTypes(n.typ, abstractInst) if formalType.skipTypes(abstractInst).kind in {tyVar} and ntyp.kind == tyString and diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index dd4774b2a..8f45452bf 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -271,6 +271,34 @@ proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = linefmt(p, cpsStmts, "#genericAssign((void*)$1, (void*)$2, $3);$n", [addrLoc(p.config, dest), addrLoc(p.config, src), genTypeInfo(p.module, dest.t, dest.lode.info)]) +proc genOpenArrayConv(p: BProc; d: TLoc; a: TLoc) = + assert d.k != locNone + # getTemp(p, d.t, d) + + case a.t.skipTypes(abstractVar).kind + of tyOpenArray, tyVarargs: + if reifiedOpenArray(a.lode): + linefmt(p, cpsStmts, "$1.d = $2.d; $1.l = $2.l;$n", + [rdLoc(d), a.rdLoc]) + else: + linefmt(p, cpsStmts, "$1.d = $2; $1.l = $2Len_0;$n", + [rdLoc(d), a.rdLoc]) + of tySequence: + linefmt(p, cpsStmts, "$1.d = $2$3; $1.l = $4;$n", + [rdLoc(d), a.rdLoc, dataField(p), lenExpr(p, a)]) + of tyArray: + linefmt(p, cpsStmts, "$1.d = $2; $1.l = $3;$n", + [rdLoc(d), rdLoc(a), rope(lengthOrd(p.config, a.t))]) + of tyString: + let etyp = skipTypes(a.t, abstractInst) + if etyp.kind in {tyVar} and optSeqDestructors in p.config.globalOptions: + linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)]) + + linefmt(p, cpsStmts, "$1.d = $2$3; $1.l = $4;$n", + [rdLoc(d), a.rdLoc, dataField(p), lenExpr(p, a)]) + else: + internalError(p.config, a.lode.info, "cannot handle " & $a.t.kind) + proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = # This function replaces all other methods for generating # the assignment operation in C. @@ -349,7 +377,9 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = of tyOpenArray, tyVarargs: # open arrays are always on the stack - really? What if a sequence is # passed to an open array? - if containsGarbageCollectedRef(dest.t): + if reifiedOpenArray(dest.lode): + genOpenArrayConv(p, dest, src) + elif containsGarbageCollectedRef(dest.t): linefmt(p, cpsStmts, # XXX: is this correct for arrays? "#genericAssignOpenArray((void*)$1, (void*)$2, $1Len_0, $3);$n", [addrLoc(p.config, dest), addrLoc(p.config, src), @@ -361,7 +391,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = "$1 = $2;$n", [rdLoc(dest), rdLoc(src)]) of tySet: - if mapType(p.config, ty) == ctArray: + if mapSetType(p.config, ty) == ctArray: linefmt(p, cpsStmts, "#nimCopyMem((void*)$1, (NIM_CONST void*)$2, $3);$n", [rdLoc(dest), rdLoc(src), getSize(p.config, dest.t)]) else: @@ -406,7 +436,7 @@ proc genDeepCopy(p: BProc; dest, src: TLoc) = [addrLoc(p.config, dest), addrLocOrTemp(src), genTypeInfo(p.module, dest.t, dest.lode.info)]) of tySet: - if mapType(p.config, ty) == ctArray: + if mapSetType(p.config, ty) == ctArray: linefmt(p, cpsStmts, "#nimCopyMem((void*)$1, (NIM_CONST void*)$2, $3);$n", [rdLoc(dest), rdLoc(src), getSize(p.config, dest.t)]) else: @@ -679,7 +709,7 @@ proc isCppRef(p: BProc; typ: PType): bool {.inline.} = tfVarIsPtr notin skipTypes(typ, abstractInstOwned).flags proc genDeref(p: BProc, e: PNode, d: var TLoc) = - let mt = mapType(p.config, e[0].typ) + let mt = mapType(p.config, e[0].typ, mapTypeChooser(e[0])) if mt in {ctArray, ctPtrToArray} and lfEnforceDeref notin d.flags: # XXX the amount of hacks for C's arrays is incredible, maybe we should # simply wrap them in a struct? --> Losing auto vectorization then? @@ -747,7 +777,7 @@ proc genAddr(p: BProc, e: PNode, d: var TLoc) = initLocExpr(p, e[0], a) putIntoDest(p, d, e, "&" & a.r, a.storage) #Message(e.info, warnUser, "HERE NEW &") - elif mapType(p.config, e[0].typ) == ctArray or isCppRef(p, e.typ): + elif mapType(p.config, e[0].typ, mapTypeChooser(e[0])) == ctArray or isCppRef(p, e.typ): expr(p, e[0], d) else: var a: TLoc @@ -905,10 +935,16 @@ proc genBoundsCheck(p: BProc; arr, a, b: TLoc) = let ty = skipTypes(arr.t, abstractVarRange) case ty.kind of tyOpenArray, tyVarargs: - linefmt(p, cpsStmts, - "if ($2-$1 != -1 && " & - "((NU)($1) >= (NU)($3Len_0) || (NU)($2) >= (NU)($3Len_0))){ #raiseIndexError(); $4}$n", - [rdLoc(a), rdLoc(b), rdLoc(arr), raiseInstr(p)]) + if reifiedOpenArray(arr.lode): + linefmt(p, cpsStmts, + "if ($2-$1 != -1 && " & + "((NU)($1) >= (NU)($3.l) || (NU)($2) >= (NU)($3.l))){ #raiseIndexError(); $4}$n", + [rdLoc(a), rdLoc(b), rdLoc(arr), raiseInstr(p)]) + else: + linefmt(p, cpsStmts, + "if ($2-$1 != -1 && " & + "((NU)($1) >= (NU)($3Len_0) || (NU)($2) >= (NU)($3Len_0))){ #raiseIndexError(); $4}$n", + [rdLoc(a), rdLoc(b), rdLoc(arr), raiseInstr(p)]) of tyArray: let first = intLiteral(firstOrd(p.config, ty)) linefmt(p, cpsStmts, @@ -925,13 +961,22 @@ proc genBoundsCheck(p: BProc; arr, a, b: TLoc) = proc genOpenArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) = var a, b: TLoc initLocExpr(p, x, a) - initLocExpr(p, y, b) # emit range check: - if optBoundsCheck in p.options: - linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2Len_0)){ #raiseIndexError2($1,$2Len_0-1); $3}$n", - [rdLoc(b), rdLoc(a), raiseInstr(p)]) # BUGFIX: ``>=`` and not ``>``! - inheritLocation(d, a) - putIntoDest(p, d, n, - ropecg(p.module, "$1[$2]", [rdLoc(a), rdCharLoc(b)]), a.storage) + initLocExpr(p, y, b) + if not reifiedOpenArray(x): + # emit range check: + if optBoundsCheck in p.options: + linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2Len_0)){ #raiseIndexError2($1,$2Len_0-1); $3}$n", + [rdLoc(b), rdLoc(a), raiseInstr(p)]) # BUGFIX: ``>=`` and not ``>``! + inheritLocation(d, a) + putIntoDest(p, d, n, + ropecg(p.module, "$1[$2]", [rdLoc(a), rdCharLoc(b)]), a.storage) + else: + if optBoundsCheck in p.options: + linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2.l)){ #raiseIndexError2($1,$2.l-1); $3}$n", + [rdLoc(b), rdLoc(a), raiseInstr(p)]) # BUGFIX: ``>=`` and not ``>``! + inheritLocation(d, a) + putIntoDest(p, d, n, + ropecg(p.module, "$1.d[$2]", [rdLoc(a), rdCharLoc(b)]), a.storage) proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) = var a, b: TLoc @@ -1675,8 +1720,12 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) = else: putIntoDest(p, d, e, ropecg(p.module, "($2)-($1)+1", [rdLoc(b), rdLoc(c)])) else: - if op == mHigh: unaryExpr(p, e, d, "($1Len_0-1)") - else: unaryExpr(p, e, d, "$1Len_0") + if not reifiedOpenArray(a): + if op == mHigh: unaryExpr(p, e, d, "($1Len_0-1)") + else: unaryExpr(p, e, d, "$1Len_0") + else: + if op == mHigh: unaryExpr(p, e, d, "($1.l-1)") + else: unaryExpr(p, e, d, "$1.l") of tyCString: if op == mHigh: unaryExpr(p, e, d, "($1 ? (#nimCStrLen($1)-1) : -1)") else: unaryExpr(p, e, d, "($1 ? #nimCStrLen($1) : 0)") @@ -2253,13 +2302,12 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = putIntoDest(p, d, e, "((NI)NIM_ALIGNOF($1))" % [getTypeDesc(p.module, t)]) of mOffsetOf: var dotExpr: PNode - block findDotExpr: - if e[1].kind == nkDotExpr: - dotExpr = e[1] - elif e[1].kind == nkCheckedFieldExpr: - dotExpr = e[1][0] - else: - internalError(p.config, e.info, "unknown ast") + if e[1].kind == nkDotExpr: + dotExpr = e[1] + elif e[1].kind == nkCheckedFieldExpr: + dotExpr = e[1][0] + else: + internalError(p.config, e.info, "unknown ast") let t = dotExpr[0].typ.skipTypes({tyTypeDesc}) let tname = getTypeDesc(p.module, t) let member = @@ -2813,8 +2861,10 @@ proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo): Rope = result.add getDefaultValue(p, t.sons[1], info) result.add "}" #result = rope"{}" + of tyOpenArray, tyVarargs: + result = rope"{NIM_NIL, 0}" of tySet: - if mapType(p.config, t) == ctArray: result = rope"{}" + if mapSetType(p.config, t) == ctArray: result = rope"{}" else: result = rope"0" else: globalError(p.config, info, "cannot create null element for: " & $t.kind) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index e40e94d01..48c024b1f 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -1556,7 +1556,7 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) = let le = e[0] let ri = e[1] var a: TLoc - discard getTypeDesc(p.module, le.typ.skipTypes(skipPtrs)) + discard getTypeDesc(p.module, le.typ.skipTypes(skipPtrs), skVar) initLoc(a, locNone, le, OnUnknown) a.flags.incl(lfEnforceDeref) a.flags.incl(lfPrepareForMutation) diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index fad6093ed..5b5720934 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -149,7 +149,7 @@ proc mapSetType(conf: ConfigRef; typ: PType): TCTypeKind = of 8: result = ctInt64 else: result = ctArray -proc mapType(conf: ConfigRef; typ: PType): TCTypeKind = +proc mapType(conf: ConfigRef; typ: PType; kind: TSymKind): TCTypeKind = ## Maps a Nim type to a C type case typ.kind of tyNone, tyTyped: result = ctVoid @@ -157,14 +157,17 @@ proc mapType(conf: ConfigRef; typ: PType): TCTypeKind = of tyChar: result = ctChar of tyNil: result = ctPtr of tySet: result = mapSetType(conf, typ) - of tyOpenArray, tyArray, tyVarargs, tyUncheckedArray: result = ctArray + of tyOpenArray, tyVarargs: + if kind == skParam: result = ctArray + else: result = ctStruct + of tyArray, tyUncheckedArray: result = ctArray of tyObject, tyTuple: result = ctStruct of tyUserTypeClasses: doAssert typ.isResolvedUserTypeClass - return mapType(conf, typ.lastSon) + return mapType(conf, typ.lastSon, kind) of tyGenericBody, tyGenericInst, tyGenericParam, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias, tySink, tyInferred, tyOwned: - result = mapType(conf, lastSon(typ)) + result = mapType(conf, lastSon(typ), kind) of tyEnum: if firstOrd(conf, typ) < 0: result = ctInt32 @@ -175,7 +178,7 @@ proc mapType(conf: ConfigRef; typ: PType): TCTypeKind = of 4: result = ctInt32 of 8: result = ctInt64 else: result = ctInt32 - of tyRange: result = mapType(conf, typ[0]) + of tyRange: result = mapType(conf, typ[0], kind) of tyPtr, tyVar, tyLent, tyRef: var base = skipTypes(typ.lastSon, typedescInst) case base.kind @@ -192,14 +195,14 @@ proc mapType(conf: ConfigRef; typ: PType): TCTypeKind = of tyInt..tyUInt64: result = TCTypeKind(ord(typ.kind) - ord(tyInt) + ord(ctInt)) of tyStatic: - if typ.n != nil: result = mapType(conf, lastSon typ) + if typ.n != nil: result = mapType(conf, lastSon typ, kind) else: doAssert(false, "mapType") else: doAssert(false, "mapType") proc mapReturnType(conf: ConfigRef; typ: PType): TCTypeKind = #if skipTypes(typ, typedescInst).kind == tyArray: result = ctPtr #else: - result = mapType(conf, typ) + result = mapType(conf, typ, skResult) proc isImportedType(t: PType): bool = result = t.sym != nil and sfImportc in t.sym.flags @@ -209,7 +212,7 @@ proc isImportedCppType(t: PType): bool = result = (t.sym != nil and sfInfixCall in t.sym.flags) or (x.sym != nil and sfInfixCall in x.sym.flags) -proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet): Rope +proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKind): Rope proc isObjLackingTypeField(typ: PType): bool {.inline.} = result = (typ.kind == tyObject) and ((tfFinal in typ.flags) and @@ -222,7 +225,7 @@ proc isInvalidReturnType(conf: ConfigRef; rettype: PType): bool = # is necessary for proper code generation of assignments. if rettype == nil: result = true else: - case mapType(conf, rettype) + case mapType(conf, rettype, skResult) of ctArray: result = not (skipTypes(rettype, typedescInst).kind in {tyVar, tyLent, tyRef, tyPtr}) @@ -346,8 +349,8 @@ proc getTypePre(m: BModule, typ: PType; sig: SigHash): Rope = if result == nil: result = cacheGetType(m.typeCache, sig) proc structOrUnion(t: PType): Rope = - let cachedUnion {.global.} = rope("union") - let cachedStruct {.global.} = rope("struct") + let cachedUnion = rope("union") + let cachedStruct = rope("struct") let t = t.skipTypes({tyAlias, tySink}) if tfUnion in t.flags: cachedUnion else: cachedStruct @@ -379,7 +382,7 @@ proc getTypeForward(m: BModule, typ: PType; sig: SigHash): Rope = doAssert m.forwTypeCache[sig] == result else: internalError(m.config, "getTypeForward(" & $typ.kind & ')') -proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet): Rope = +proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet; kind: TSymKind): Rope = ## like getTypeDescAux but creates only a *weak* dependency. In other words ## we know we only need a pointer to it so we only generate a struct forward ## declaration: @@ -387,7 +390,7 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet): Rope = case etB.kind of tyObject, tyTuple: if isImportedCppType(etB) and t.kind == tyGenericInst: - result = getTypeDescAux(m, t, check) + result = getTypeDescAux(m, t, check, kind) else: result = getTypeForward(m, t, hashType(t)) pushType(m, t) @@ -417,18 +420,18 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet): Rope = result = getTypeForward(m, t, sig) & seqStar(m) pushType(m, t) else: - result = getTypeDescAux(m, t, check) + result = getTypeDescAux(m, t, check, kind) proc getSeqPayloadType(m: BModule; t: PType): Rope = var check = initIntSet() - result = getTypeDescWeak(m, t, check) & "_Content" + result = getTypeDescWeak(m, t, check, skParam) & "_Content" #result = getTypeForward(m, t, hashType(t)) & "_Content" proc seqV2ContentType(m: BModule; t: PType; check: var IntSet) = let sig = hashType(t) let result = cacheGetType(m.typeCache, sig) if result == nil: - discard getTypeDescAux(m, t, check) + discard getTypeDescAux(m, t, check, skVar) else: # little hack for now to prevent multiple definitions of the same # Seq_Content: @@ -437,7 +440,7 @@ $3ifndef $2_Content_PP $3define $2_Content_PP struct $2_Content { NI cap; $1 data[SEQ_DECL_SIZE];}; $3endif$N - """, [getTypeDescAux(m, t.skipTypes(abstractInst)[0], check), result, rope"#"]) + """, [getTypeDescAux(m, t.skipTypes(abstractInst)[0], check, skVar), result, rope"#"]) proc paramStorageLoc(param: PSym): TStorageLoc = if param.typ.skipTypes({tyVar, tyLent, tyTypeDesc}).kind notin { @@ -453,7 +456,7 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope, if t[0] == nil or isInvalidReturnType(m.config, t[0]): rettype = ~"void" else: - rettype = getTypeDescAux(m, t[0], check) + rettype = getTypeDescAux(m, t[0], check, skResult) for i in 1.. 0: result.addf("NIM_ALIGN($1) ", [rope(s.alignment)]) - result.add getTypeDesc(p.module, s.typ) + result.add getTypeDesc(p.module, s.typ, skVar) if s.constraint.isNil: if sfRegister in s.flags: result.add(" register") #elif skipTypes(s.typ, abstractInst).kind in GcTypeKinds: @@ -541,7 +547,7 @@ proc assignGlobalVar(p: BProc, n: PNode; value: Rope) = internalError(p.config, n.info, ".threadvar variables cannot have a value") else: var decl: Rope = nil - var td = getTypeDesc(p.module, s.loc.t) + var td = getTypeDesc(p.module, s.loc.t, skVar) if s.constraint.isNil: if s.kind in {skLet, skVar, skField, skForVar} and s.alignment > 0: decl.addf "NIM_ALIGN($1) ", [rope(s.alignment)] @@ -687,7 +693,7 @@ proc loadDynamicLib(m: BModule, lib: PLib) = initLoc(dest, locTemp, lib.path, OnStack) dest.r = getTempName(m) appcg(m, m.s[cfsDynLibInit],"$1 $2;$n", - [getTypeDesc(m, lib.path.typ), rdLoc(dest)]) + [getTypeDesc(m, lib.path.typ, skVar), rdLoc(dest)]) expr(p, lib.path, dest) m.s[cfsVars].add(p.s(cpsLocals)) @@ -727,7 +733,7 @@ proc symInDynamicLib(m: BModule, sym: PSym) = params.add(rdLoc(a)) params.add(", ") let load = "\t$1 = ($2) ($3$4));$n" % - [tmp, getTypeDesc(m, sym.typ), params, makeCString($extname)] + [tmp, getTypeDesc(m, sym.typ, skVar), params, makeCString($extname)] var last = lastSon(n) if last.kind == nkHiddenStdConv: last = last[1] internalAssert(m.config, last.kind == nkStrLit) @@ -741,8 +747,8 @@ proc symInDynamicLib(m: BModule, sym: PSym) = else: appcg(m, m.s[cfsDynLibInit], "\t$1 = ($2) #nimGetProcAddr($3, $4);$n", - [tmp, getTypeDesc(m, sym.typ), lib.name, makeCString($extname)]) - m.s[cfsVars].addf("$2 $1;$n", [sym.loc.r, getTypeDesc(m, sym.loc.t)]) + [tmp, getTypeDesc(m, sym.typ, skVar), lib.name, makeCString($extname)]) + m.s[cfsVars].addf("$2 $1;$n", [sym.loc.r, getTypeDesc(m, sym.loc.t, skVar)]) proc varInDynamicLib(m: BModule, sym: PSym) = var lib = sym.annex @@ -754,9 +760,9 @@ proc varInDynamicLib(m: BModule, sym: PSym) = inc(m.labels, 2) appcg(m, m.s[cfsDynLibInit], "$1 = ($2*) #nimGetProcAddr($3, $4);$n", - [tmp, getTypeDesc(m, sym.typ), lib.name, makeCString($extname)]) + [tmp, getTypeDesc(m, sym.typ, skVar), lib.name, makeCString($extname)]) m.s[cfsVars].addf("$2* $1;$n", - [sym.loc.r, getTypeDesc(m, sym.loc.t)]) + [sym.loc.r, getTypeDesc(m, sym.loc.t, skVar)]) proc symInDynamicLibPartial(m: BModule, sym: PSym) = sym.loc.r = mangleDynLibProc(sym) @@ -1187,7 +1193,7 @@ proc requestConstImpl(p: BProc, sym: PSym) = [getTypeDesc(q, sym.typ), actualConstName, genBracedInit(q.initProc, sym.ast, isConst = true)]) if m.hcrOn: # generate the global pointer with the real name - q.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(m, sym.loc.t), sym.loc.r]) + q.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(m, sym.loc.t, skVar), sym.loc.r]) # register it (but ignore the boolean result of hcrRegisterGlobal) q.initProc.procSec(cpsLocals).addf( "\thcrRegisterGlobal($1, \"$2\", sizeof($3), NULL, (void**)&$2);$n", @@ -1201,13 +1207,13 @@ proc requestConstImpl(p: BProc, sym: PSym) = if q != m and not containsOrIncl(m.declaredThings, sym.id): assert(sym.loc.r != nil) if m.hcrOn: - m.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(m, sym.loc.t), sym.loc.r]); + m.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(m, sym.loc.t, skVar), sym.loc.r]); m.initProc.procSec(cpsLocals).addf( "\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.r, - getTypeDesc(m, sym.loc.t), getModuleDllPath(q, sym)]) + getTypeDesc(m, sym.loc.t, skVar), getModuleDllPath(q, sym)]) else: let headerDecl = "extern NIM_CONST $1 $2;$n" % - [getTypeDesc(m, sym.loc.t), sym.loc.r] + [getTypeDesc(m, sym.loc.t, skVar), sym.loc.r] m.s[cfsData].add(headerDecl) if sfExportc in sym.flags and p.module.g.generatedHeader != nil: p.module.g.generatedHeader.s[cfsData].add(headerDecl) @@ -1247,7 +1253,7 @@ proc genVarPrototype(m: BModule, n: PNode) = if sym.kind in {skLet, skVar, skField, skForVar} and sym.alignment > 0: m.s[cfsVars].addf "NIM_ALIGN($1) ", [rope(sym.alignment)] m.s[cfsVars].add(if m.hcrOn: "static " else: "extern ") - m.s[cfsVars].add(getTypeDesc(m, sym.loc.t)) + m.s[cfsVars].add(getTypeDesc(m, sym.loc.t, skVar)) if m.hcrOn: m.s[cfsVars].add("*") if lfDynamicLib in sym.loc.flags: m.s[cfsVars].add("*") if sfRegister in sym.flags: m.s[cfsVars].add(" register") @@ -1255,7 +1261,7 @@ proc genVarPrototype(m: BModule, n: PNode) = m.s[cfsVars].addf(" $1;$n", [sym.loc.r]) if m.hcrOn: m.initProc.procSec(cpsLocals).addf( "\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.r, - getTypeDesc(m, sym.loc.t), getModuleDllPath(m, sym)]) + getTypeDesc(m, sym.loc.t, skVar), getModuleDllPath(m, sym)]) proc addNimDefines(result: var Rope; conf: ConfigRef) {.inline.} = result.addf("#define NIM_INTBITS $1\L", [ @@ -1613,10 +1619,10 @@ proc hcrGetProcLoadCode(m: BModule, sym, prefix, handle, getProcFunc: string): R prc.typ.sym = nil if not containsOrIncl(m.declaredThings, prc.id): - m.s[cfsVars].addf("static $2 $1;$n", [prc.loc.r, getTypeDesc(m, prc.loc.t)]) + m.s[cfsVars].addf("static $2 $1;$n", [prc.loc.r, getTypeDesc(m, prc.loc.t, skVar)]) result = "\t$1 = ($2) $3($4, $5);$n" % - [tmp, getTypeDesc(m, prc.typ), getProcFunc.rope, handle.rope, makeCString(prefix & sym)] + [tmp, getTypeDesc(m, prc.typ, skVar), getProcFunc.rope, handle.rope, makeCString(prefix & sym)] proc genInitCode(m: BModule) = ## this function is called in cgenWriteModules after all modules are closed, diff --git a/compiler/options.nim b/compiler/options.nim index 14016495f..5b23ac9af 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -162,7 +162,8 @@ type ## which itself requires `nimble install libffi`, see #10150 ## Note: this feature can't be localized with {.push.} vmopsDanger, - strictFuncs + strictFuncs, + views LegacyFeature* = enum allowSemcheckedAstModification, diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 9601bf082..9c7609f9a 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -1035,7 +1035,14 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = gsub(g, n, 1) of nkHiddenStdConv, nkHiddenSubConv: if n.len >= 2: - gsub(g, n[1]) + when false: + # if {renderIds, renderIr} * g.flags != {}: + put(g, tkSymbol, "(conv)") + put(g, tkParLe, "(") + gsub(g, n[1]) + put(g, tkParRi, ")") + else: + gsub(g, n[1]) else: put(g, tkSymbol, "(wrong conv)") of nkHiddenCallConv: diff --git a/compiler/sem.nim b/compiler/sem.nim index 07c76eed4..705e1b72c 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -17,7 +17,7 @@ import intsets, transf, vmdef, vm, idgen, aliases, cgmeth, lambdalifting, evaltempl, patterns, parampatterns, sempass2, linter, semmacrosanity, lowerings, plugins/active, rod, lineinfos, strtabs, int128, - isolation_check + isolation_check, typeallowed from modulegraphs import ModuleGraph, PPassContext, onUse, onDef, onDefResolveForward @@ -228,9 +228,9 @@ proc semIdentVis(c: PContext, kind: TSymKind, n: PNode, proc semIdentWithPragma(c: PContext, kind: TSymKind, n: PNode, allowed: TSymFlags): PSym -proc typeAllowedCheck(conf: ConfigRef; info: TLineInfo; typ: PType; kind: TSymKind; +proc typeAllowedCheck(c: PContext; info: TLineInfo; typ: PType; kind: TSymKind; flags: TTypeAllowedFlags = {}) = - let t = typeAllowed(typ, kind, flags) + let t = typeAllowed(typ, kind, c, flags) if t != nil: var err: string if t == typ: @@ -240,10 +240,10 @@ proc typeAllowedCheck(conf: ConfigRef; info: TLineInfo; typ: PType; kind: TSymKi else: err = "invalid type: '$1' in this context: '$2' for $3" % [typeToString(t), typeToString(typ), toHumanStr(kind)] - localError(conf, info, err) + localError(c.config, info, err) proc paramsTypeCheck(c: PContext, typ: PType) {.inline.} = - typeAllowedCheck(c.config, typ.n.info, typ, skProc) + typeAllowedCheck(c, typ.n.info, typ, skProc) proc expectMacroOrTemplateCall(c: PContext, n: PNode): PSym proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 0e1c5e9d3..80e04ead4 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -178,7 +178,7 @@ proc checkConvertible(c: PContext, targetTyp: PType, src: PNode): TConvStatus = else: discard -proc isCastable(conf: ConfigRef; dst, src: PType): bool = +proc isCastable(c: PContext; dst, src: PType): bool = ## Checks whether the source type can be cast to the destination type. ## Casting is very unrestrictive; casts are allowed as long as ## castDest.size >= src.size, and typeAllowed(dst, skParam) @@ -193,6 +193,7 @@ proc isCastable(conf: ConfigRef; dst, src: PType): bool = return false if skipTypes(dst, abstractInst).kind == tyBuiltInTypeClass: return false + let conf = c.config if conf.selectedGC in {gcArc, gcOrc}: let d = skipTypes(dst, abstractInst) let s = skipTypes(src, abstractInst) @@ -210,7 +211,7 @@ proc isCastable(conf: ConfigRef; dst, src: PType): bool = result = false elif srcSize < 0: result = false - elif typeAllowed(dst, skParam) != nil: + elif typeAllowed(dst, skParam, c) != nil: result = false elif dst.kind == tyProc and dst.callConv == ccClosure: result = src.kind == tyProc and src.callConv == ccClosure @@ -338,7 +339,7 @@ proc semCast(c: PContext, n: PNode): PNode = let castedExpr = semExprWithType(c, n[1]) if tfHasMeta in targetType.flags: localError(c.config, n[0].info, "cannot cast to a non concrete type: '$1'" % $targetType) - if not isCastable(c.config, targetType, castedExpr.typ): + if not isCastable(c, targetType, castedExpr.typ): let tar = $targetType let alt = typeToString(targetType, preferDesc) let msg = if tar != alt: tar & "=" & alt else: tar @@ -794,7 +795,7 @@ proc evalAtCompileTime(c: PContext, n: PNode): PNode = if callee.kind notin {skProc, skFunc, skConverter, skConst} or callee.isGenericRoutine: return - if n.typ != nil and typeAllowed(n.typ, skConst) != nil: return + if n.typ != nil and typeAllowed(n.typ, skConst, c) != nil: return var call = newNodeIT(nkCall, n.info, n.typ) call.add(n[0]) @@ -962,9 +963,9 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = # t.sym != nil # sfAnon notin t.sym.flags # t.kind != tySequence(It is tyProc) - if typ.sym != nil and sfAnon notin typ.sym.flags and + if typ.sym != nil and sfAnon notin typ.sym.flags and typ.kind == tyProc: - msg.add(" = " & + msg.add(" = " & typeToString(typ, preferDesc)) localError(c.config, n.info, msg) return errorNode(c, n) @@ -1600,7 +1601,7 @@ proc takeImplicitAddr(c: PContext, n: PNode; isLent: bool): PNode = proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} = if le.kind == nkHiddenDeref: var x = le[0] - if x.typ.kind in {tyVar, tyLent} and x.kind == nkSym and x.sym.kind == skResult: + if (x.typ.kind in {tyVar, tyLent} or isViewType(x.typ)) and x.kind == nkSym and x.sym.kind == skResult: n[0] = x # 'result[]' --> 'result' n[1] = takeImplicitAddr(c, ri, x.typ.kind == tyLent) x.typ.flags.incl tfVarIsPtr @@ -1732,7 +1733,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = if cmpTypes(c, lhs.typ, rhsTyp) in {isGeneric, isEqual}: internalAssert c.config, c.p.resultSym != nil # Make sure the type is valid for the result variable - typeAllowedCheck(c.config, n.info, rhsTyp, skResult) + typeAllowedCheck(c, n.info, rhsTyp, skResult) lhs.typ = rhsTyp c.p.resultSym.typ = rhsTyp c.p.owner.typ[0] = rhsTyp @@ -1825,7 +1826,9 @@ proc semYieldVarResult(c: PContext, n: PNode, restype: PType) = tupleConstr[i] = takeImplicitAddr(c, tupleConstr[i], e.kind == tyLent) else: localError(c.config, n[0].info, errXExpected, "tuple constructor") - else: discard + else: + if isViewType(t): + n[0] = takeImplicitAddr(c, n[0], false) proc semYield(c: PContext, n: PNode): PNode = result = n diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index f745f9075..b48d7e29e 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -1243,8 +1243,11 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) = effects[ensuresEffects] = ensuresSpec var mutationInfo = MutationInfo() - if strictFuncs in c.features and not t.hasSideEffect and t.hasDangerousAssign: - t.hasSideEffect = mutatesNonVarParameters(s, body, mutationInfo) + if {strictFuncs, views} * c.features != {}: + var partitions = computeGraphPartitions(s, body) + if not t.hasSideEffect and t.hasDangerousAssign: + t.hasSideEffect = varpartitions.hasSideEffect(partitions, mutationInfo) + checkBorrowedLocations(partitions, g.config) if sfThread in s.flags and t.gcUnsafe: if optThreads in g.config.globalOptions and optThreadAnalysis in g.config.globalOptions: diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 2c3ff2bea..14f08b4b3 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -534,7 +534,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = if c.matchedConcept != nil: typFlags.incl taConcept - typeAllowedCheck(c.config, a.info, typ, symkind, typFlags) + typeAllowedCheck(c, a.info, typ, symkind, typFlags) when false: liftTypeBoundOps(c, typ, a.info) instAllTypeBoundOp(c, a.info) @@ -667,7 +667,7 @@ proc semConst(c: PContext, n: PNode): PNode = if def.kind != nkNilLit: if c.matchedConcept != nil: typFlags.incl taConcept - typeAllowedCheck(c.config, a.info, typ, skConst, typFlags) + typeAllowedCheck(c, a.info, typ, skConst, typFlags) var b: PNode if a.kind == nkVarTuple: diff --git a/compiler/transf.nim b/compiler/transf.nim index 8ff0664da..5dd275414 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -494,6 +494,7 @@ proc transformConv(c: PTransf, n: PNode): PNode = result = transformSons(c, n) of tyOpenArray, tyVarargs: result = transform(c, n[1]) + #result = transformSons(c, n) result.typ = takeType(n.typ, n[1].typ) #echo n.info, " came here and produced ", typeToString(result.typ), # " from ", typeToString(n.typ), " and ", typeToString(n[1].typ) @@ -1107,6 +1108,9 @@ proc transformBody*(g: ModuleGraph, prc: PSym, cache: bool): PNode = else: prc.transformedBody = nil + #if prc.name.s == "main": + # echo "transformed into ", renderTree(result, {renderIds}) + proc transformStmt*(g: ModuleGraph; module: PSym, n: PNode): PNode = if nfTransf in n.flags: result = n diff --git a/compiler/trees.nim b/compiler/trees.nim index bfb429f13..c5c1a0d75 100644 --- a/compiler/trees.nim +++ b/compiler/trees.nim @@ -183,7 +183,7 @@ proc getRoot*(n: PNode): PSym = if n.sym.kind in {skVar, skResult, skTemp, skLet, skForVar, skParam}: result = n.sym of nkDotExpr, nkBracketExpr, nkHiddenDeref, nkDerefExpr, - nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr: + nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr, nkHiddenAddr, nkAddr: result = getRoot(n[0]) of nkHiddenStdConv, nkHiddenSubConv, nkConv: result = getRoot(n[1]) diff --git a/compiler/typeallowed.nim b/compiler/typeallowed.nim new file mode 100644 index 000000000..5a192783e --- /dev/null +++ b/compiler/typeallowed.nim @@ -0,0 +1,228 @@ +# +# +# The Nim Compiler +# (c) Copyright 2020 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module contains 'typeAllowed' and friends which check +## for invalid types like 'openArray[var int]'. + +import + intsets, ast, renderer, options, semdata, types + +type + TTypeAllowedFlag* = enum + taField, + taHeap, + taConcept, + taIsOpenArray, + taNoUntyped + taIsTemplateOrMacro + taProcContextIsNotMacro + + TTypeAllowedFlags* = set[TTypeAllowedFlag] + +proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind; + c: PContext; flags: TTypeAllowedFlags = {}): PType + +proc typeAllowedNode(marker: var IntSet, n: PNode, kind: TSymKind, + c: PContext; flags: TTypeAllowedFlags = {}): PType = + if n != nil: + result = typeAllowedAux(marker, n.typ, kind, c, flags) + if result == nil: + case n.kind + of nkNone..nkNilLit: + discard + else: + #if n.kind == nkRecCase and kind in {skProc, skFunc, skConst}: + # return n[0].typ + for i in 0.. Date: Wed, 9 Sep 2020 07:36:31 +0200 Subject: [PATCH 0582/3768] Better semiStmtList parsing (#15123) * Better semiStmtList parsing * Add examples from forums and wiki * Move parseIfExpr near parseIfOrWhen * Update grammar --- compiler/parser.nim | 122 ++++++++++---------------- tests/parser/tstmtlists.nim | 165 ++++++++++++++++++++++++++++++++++++ 2 files changed, 209 insertions(+), 78 deletions(-) create mode 100644 tests/parser/tstmtlists.nim diff --git a/compiler/parser.nim b/compiler/parser.nim index 0fa860c99..f256cee66 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -523,15 +523,21 @@ proc parseGStrLit(p: var Parser, a: PNode): PNode = proc complexOrSimpleStmt(p: var Parser): PNode proc simpleExpr(p: var Parser, mode = pmNormal): PNode +proc parseIfExpr(p: var Parser, kind: TNodeKind): PNode proc semiStmtList(p: var Parser, result: PNode) = inc p.inSemiStmtList - result.add(complexOrSimpleStmt(p)) - # progress guaranteed - while p.tok.tokType == tkSemiColon: - getTok(p) - optInd(p, result) - result.add(complexOrSimpleStmt(p)) + withInd(p): + # Be lenient with the first stmt/expr + result.add if p.tok.tokType == tkIf: parseIfExpr(p, nkIfStmt) else: complexOrSimpleStmt(p) + while true: + if p.tok.tokType == tkSemiColon: + getTok(p) + if p.tok.tokType == tkParRi: + break + elif not (sameInd(p) or realInd(p)): + parMessage(p, errInvalidIndentation) + result.add complexOrSimpleStmt(p) dec p.inSemiStmtList result.transitionSonsKind(nkStmtListExpr) @@ -540,10 +546,10 @@ proc parsePar(p: var Parser): PNode = #| | 'finally' | 'except' | 'for' | 'block' | 'const' | 'let' #| | 'when' | 'var' | 'mixin' #| par = '(' optInd - #| ( &parKeyw complexOrSimpleStmt ^+ ';' - #| | ';' complexOrSimpleStmt ^+ ';' + #| ( &parKeyw (ifExpr \ complexOrSimpleStmt) ^+ ';' + #| | ';' (ifExpr \ complexOrSimpleStmt) ^+ ';' #| | pragmaStmt - #| | simpleExpr ( ('=' expr (';' complexOrSimpleStmt ^+ ';' )? ) + #| | simpleExpr ( ('=' expr (';' (ifExpr \ complexOrSimpleStmt) ^+ ';' )? ) #| | (':' expr (',' exprColonEqExpr ^+ ',' )? ) ) ) #| optPar ')' # @@ -847,64 +853,6 @@ proc simpleExpr(p: var Parser, mode = pmNormal): PNode = when defined(nimpretty): dec p.em.doIndentMore -proc parseIfExpr(p: var Parser, kind: TNodeKind): PNode = - #| condExpr = expr colcom expr optInd - #| ('elif' expr colcom expr optInd)* - #| 'else' colcom expr - #| ifExpr = 'if' condExpr - #| whenExpr = 'when' condExpr - when true: - result = newNodeP(kind, p) - while true: - getTok(p) # skip `if`, `when`, `elif` - var branch = newNodeP(nkElifExpr, p) - optInd(p, branch) - branch.add(parseExpr(p)) - colcom(p, branch) - branch.add(parseStmt(p)) - skipComment(p, branch) - result.add(branch) - if p.tok.tokType != tkElif: break # or not sameOrNoInd(p): break - if p.tok.tokType == tkElse: # and sameOrNoInd(p): - var branch = newNodeP(nkElseExpr, p) - eat(p, tkElse) - colcom(p, branch) - branch.add(parseStmt(p)) - result.add(branch) - else: - var - b: PNode - wasIndented = false - result = newNodeP(kind, p) - - getTok(p) - let branch = newNodeP(nkElifExpr, p) - branch.add(parseExpr(p)) - colcom(p, branch) - let oldInd = p.currInd - if realInd(p): - p.currInd = p.tok.indent - wasIndented = true - branch.add(parseExpr(p)) - result.add branch - while sameInd(p) or not wasIndented: - case p.tok.tokType - of tkElif: - b = newNodeP(nkElifExpr, p) - getTok(p) - optInd(p, b) - b.add(parseExpr(p)) - of tkElse: - b = newNodeP(nkElseExpr, p) - getTok(p) - else: break - colcom(p, b) - b.add(parseStmt(p)) - result.add(b) - if b.kind == nkElseExpr: break - if wasIndented: - p.currInd = oldInd - proc parsePragma(p: var Parser): PNode = #| pragma = '{.' optInd (exprColonEqExpr comma?)* optPar ('.}' | '}') result = newNodeP(nkPragma, p) @@ -1574,6 +1522,30 @@ proc parseIfOrWhen(p: var Parser, kind: TNodeKind): PNode = branch.add(parseStmt(p)) result.add(branch) +proc parseIfExpr(p: var Parser, kind: TNodeKind): PNode = + #| condExpr = expr colcom expr optInd + #| ('elif' expr colcom expr optInd)* + #| 'else' colcom expr + #| ifExpr = 'if' condExpr + #| whenExpr = 'when' condExpr + result = newNodeP(kind, p) + while true: + getTok(p) # skip `if`, `when`, `elif` + var branch = newNodeP(nkElifExpr, p) + optInd(p, branch) + branch.add(parseExpr(p)) + colcom(p, branch) + branch.add(parseStmt(p)) + skipComment(p, branch) + result.add(branch) + if p.tok.tokType != tkElif: break + if p.tok.tokType == tkElse: + var branch = newNodeP(nkElseExpr, p) + eat(p, tkElse) + colcom(p, branch) + branch.add(parseStmt(p)) + result.add(branch) + proc parseWhile(p: var Parser): PNode = #| whileStmt = 'while' expr colcom stmt result = newNodeP(nkWhileStmt, p) @@ -2269,17 +2241,11 @@ proc parseStmt(p: var Parser): PNode = # deprecate this syntax later break p.hasProgress = false - var a = complexOrSimpleStmt(p) - if a.kind != nkEmpty: - result.add(a) - else: - # This is done to make the new 'if' expressions work better. - # XXX Eventually we need to be more strict here. - if p.tok.tokType notin {tkElse, tkElif}: - parMessage(p, errExprExpected, p.tok) - getTok(p) - else: - break + if p.tok.tokType in {tkElse, tkElif}: + parMessage(p, errInvalidIndentation) + getTok(p) + + result.add complexOrSimpleStmt(p) if not p.hasProgress and p.tok.tokType == tkEof: break else: # the case statement is only needed for better error messages: diff --git a/tests/parser/tstmtlists.nim b/tests/parser/tstmtlists.nim new file mode 100644 index 000000000..b6c7e4a94 --- /dev/null +++ b/tests/parser/tstmtlists.nim @@ -0,0 +1,165 @@ +discard """ + output: ''' +2 +2 +2 +2 +2 +2 +2 +2 +2 +2 +hello +1 +hello +2 +hello +3 +hello +4 +hello +5 +hello +6 +hello +7 +hello +8 +hello +9 +hello +10 +hello +1 +hello +2 +hello +3 +hello +4 +hello +5 +hello +6 +hello +7 +hello +8 +hello +9 +hello +10 +''' +""" + +block: ( + discard; + echo 1 + 1; + ) + +block: ( + discard; #Haha + #haha + echo 1 + 1; +) + +block: ( + discard; + #Hmm + echo 1 + + 1; +) + +block: ( + discard + echo "2" +) + +block: ( + discard; + echo 1 + + 1 +) + +block: ( + discard + echo 1 + + 1 +) + +block: ( + discard; + discard +) + +block: ( + discard + echo 1 + 1; + ) + +block: ( + discard + echo 1 + 1; +) + +block: ( + discard + echo 1 + + 1; +) + +block: ( + discard; + ) + +block: ( discard; echo 1 + #heh + 1; +) + +for i in 1..10: + echo "hello" + echo i + +for i in 1..10: ( + echo "hello"; + echo i; +) + +proc square(inSeq: seq[float]): seq[float] = ( + result = newSeq[float](len(inSeq)); + for i, v in inSeq: ( + result[i] = v * v; + ) +) + +proc square2(inSeq: seq[float]): seq[float] = + result = newSeq[float](len(inSeq)); + for i, v in inSeq: ( + result[i] = v * v; + ) + +proc cstringCheck(tracked: int; n: int) = + if true == false and (let a = high(int); let b = high(int); + a.int8 == 8 and a.int8 notin {3..9}): + echo(tracked, n) + +template dim: int = + (if int.high == 0: + int.high + else: + int.high) + +template dim2: int = + (if int.high == 0: + int.high + else: + int.high) + +template dim: int = + ( + if int.high == 0: + int.high + else: + int.high) + From 7d4f0df107fdce5d1da93e26e54dd87526a0a74f Mon Sep 17 00:00:00 2001 From: Miran Date: Wed, 9 Sep 2020 12:43:04 +0200 Subject: [PATCH 0583/3768] deprecate `high(value)` and `low(value)` (#15283) --- lib/system.nim | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/system.nim b/lib/system.nim index dc603b56d..0a8c52908 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -288,13 +288,14 @@ else: type sink*[T]{.magic: "BuiltinType".} type lent*[T]{.magic: "BuiltinType".} -proc high*[T: Ordinal|enum|range](x: T): T {.magic: "High", noSideEffect.} +proc high*[T: Ordinal|enum|range](x: T): T {.magic: "High", noSideEffect, + deprecated: "Deprecated since v1.4; there should not be `high(value)`. Use `high(type)`.".} ## Returns the highest possible value of an ordinal value `x`. ## ## As a special semantic rule, `x` may also be a type identifier. ## - ## See also: - ## * `low(T) <#low,T>`_ + ## **This proc is deprecated**, use this one instead: + ## * `high(typedesc) <#high,typedesc[T]>`_ ## ## .. code-block:: Nim ## high(2) # => 9223372036854775807 @@ -360,12 +361,13 @@ proc high*(x: string): int {.magic: "High", noSideEffect.} ## var str = "Hello world!" ## high(str) # => 11 -proc low*[T: Ordinal|enum|range](x: T): T {.magic: "Low", noSideEffect.} +proc low*[T: Ordinal|enum|range](x: T): T {.magic: "Low", noSideEffect, + deprecated: "Deprecated since v1.4; there should not be `low(value)`. Use `low(type)`.".} ## Returns the lowest possible value of an ordinal value `x`. As a special ## semantic rule, `x` may also be a type identifier. ## - ## See also: - ## * `high(T) <#high,T>`_ + ## **This proc is deprecated**, use this one instead: + ## * `low(typedesc) <#low,typedesc[T]>`_ ## ## .. code-block:: Nim ## low(2) # => -9223372036854775808 From 8c82144ba507244084836b38689e4925de982626 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Wed, 9 Sep 2020 18:47:22 +0800 Subject: [PATCH 0584/3768] add getprotobyname (#15273) * add getprotobyname * tests, docs, changelog * add since --- changelog.md | 3 +++ lib/pure/nativesockets.nim | 14 ++++++++++++++ lib/windows/winlean.nim | 14 ++++++++++++++ tests/stdlib/tgetprotobyname.nim | 25 +++++++++++++++++++++++++ 4 files changed, 56 insertions(+) create mode 100644 tests/stdlib/tgetprotobyname.nim diff --git a/changelog.md b/changelog.md index 58efc42e6..2f7b584a3 100644 --- a/changelog.md +++ b/changelog.md @@ -168,6 +168,9 @@ cannot be applied to every use case. The limitations and the (lack of) reliability of `round` are well documented. +- Add `getprotobyname` to `winlean`. Add `getProtoByname` to `nativesockets` which returns a protocol code + from the database that matches the protocol `name`. + ## Language changes diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim index e38dc2ecd..1f597f8eb 100644 --- a/lib/pure/nativesockets.nim +++ b/lib/pure/nativesockets.nim @@ -13,6 +13,8 @@ # TODO: Clean up the exports a bit and everything else in general. import os, options +import std/private/since + when hostOS == "solaris": {.passl: "-lsocket -lnsl".} @@ -199,6 +201,18 @@ proc toSockType*(protocol: Protocol): SockType = of IPPROTO_IP, IPPROTO_IPV6, IPPROTO_RAW, IPPROTO_ICMP, IPPROTO_ICMPV6: SOCK_RAW +proc getProtoByName*(name: string): int {.since: (1, 3, 5).} = + ## Returns a protocol code from the database that matches the protocol ``name``. + when useWinVersion: + let protoent = winlean.getprotobyname(name.cstring) + else: + let protoent = posix.getprotobyname(name.cstring) + + if protoent == nil: + raise newException(OsError, "protocol not found") + + result = protoent.p_proto.int + proc close*(socket: SocketHandle) = ## Closes a socket. when useWinVersion: diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index c28700374..1334a85d5 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -96,6 +96,12 @@ type dwPlatformId*: DWORD szCSDVersion*: array[0..127, WinChar] + Protoent* = object + p_name*: cstring + p_aliases*: cstringArray + p_proto*: cshort + + const STARTF_USESHOWWINDOW* = 1'i32 STARTF_USESTDHANDLES* = 256'i32 @@ -573,6 +579,14 @@ proc gethostbyname*(name: cstring): ptr Hostent {. proc gethostname*(hostname: cstring, len: cint): cint {. stdcall, importc: "gethostname", dynlib: ws2dll, sideEffect.} +proc getprotobyname*( + name: cstring +): ptr Protoent {.stdcall, importc: "getprotobyname", dynlib: ws2dll, sideEffect.} + +proc getprotobynumber*( + proto: cint +): ptr Protoent {.stdcall, importc: "getprotobynumber", dynlib: ws2dll, sideEffect.} + proc socket*(af, typ, protocol: cint): SocketHandle {. stdcall, importc: "socket", dynlib: ws2dll.} diff --git a/tests/stdlib/tgetprotobyname.nim b/tests/stdlib/tgetprotobyname.nim new file mode 100644 index 000000000..c3d8da0d8 --- /dev/null +++ b/tests/stdlib/tgetprotobyname.nim @@ -0,0 +1,25 @@ +discard """ + cmd: "nim c -r --styleCheck:hint --panics:on $options $file" + targets: "c" + nimout: "" + action: "run" + exitcode: 0 + timeout: 60.0 +""" + +import nativesockets + + +doAssert getProtoByName("ip") == 0 +doAssert getProtoByName("ipv6") == 41 +doAssert getProtoByName("tcp") == 6 +doAssert getProtoByName("udp") == 17 +doAssert getProtoByName("icmp") == 1 +doAssert getProtoByName("ipv6-icmp") == 58 + +when defined(windows): + doAssertRaises(OSError): + discard getProtoByName("raw") + +doAssertRaises(OSError): + discard getProtoByName("Error") From 8a2cd7b24bbf493ffb90204ec2892ddf7e2e8316 Mon Sep 17 00:00:00 2001 From: narimiran Date: Wed, 9 Sep 2020 12:59:14 +0200 Subject: [PATCH 0585/3768] [ci skip] fix typo in the manual --- doc/manual.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/manual.rst b/doc/manual.rst index fc4c11f50..e72554603 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -5469,8 +5469,8 @@ powerful programming construct that still suffices. So the "check list" is: (4) Else: Use a macro. -For loop macros ---------------- +For loop macro +-------------- A macro that takes as its only input parameter an expression of the special type ``system.ForLoopStmt`` can rewrite the entirety of a ``for`` loop: From ccd77b42af11e53631bee7bd74e16d39538541c9 Mon Sep 17 00:00:00 2001 From: Ivan Bobev Date: Wed, 9 Sep 2020 15:18:59 +0300 Subject: [PATCH 0586/3768] Add some enhancements to `jsonutils.nim` (#15133) * Add some enhancements to `jsonutils.nim` * Use `jsonutils.nim` hookable API to add a possibility to deserialize JSON arrays directly to `HashSet` and `OrderedSet` types and respectively to serialize those types to JSON arrays. * Also add a possibility to deserialize JSON `null` objects to Nim option objects and respectively to serialize Nim option object to JSON object if some or to JSON `null` object if none. * Move serialization/deserialization functionality for `Table` and `OrderedTable` types from `jsonutils.nim` to `tables.nim` via the hookable API. * Add object `jsonutils.Joptions` and parameter from its type to `jsonutils.fromJson` procedure to control whether to allow deserializing JSON objects to Nim objects when the JSON has some extra or missing keys. * Add unit tests for the added functionalities to `tjsonutils.nim`. * improve fromJsonFields * Add changelog entry for the jsonutils enhancements * Add TODO in `jsonutils.nim` * Added an entry to "Future directions" section in `jsonutils.nim` as suggestion for future support of serialization and de-serialization of nested variant objects. * Added currently disabled test case in `tjsonutils.nim` for testing serialization and de-serialization of nested variant objects. * Move JSON hooks to `jsonutils.nim` Move `fromJsonHook` and `toJsonHook` procedures for different types to `jsonutils.nim` module to avoid a dependency of collections modules to the `json.nim` module. The hooks are removed from the following modules: * `tables.nim` * `sets.nim` * `options.nim` * `strtabs.nim` * Add some tests about `StringTableRef` Add tests for `StringTableRef`'s `fromJsonHook` and `toJsonHook` to `tjsonutils.nim`. * Disable a warning in `jsonutils.nim` Mark `fun` template in `jsonutils` module with `{.used.}` pragma in order to disable `[XDeclaredButNotUsed]` hint. The template is actually used by the `initCaseObject` macro in the same module. Co-authored-by: Timothee Cour --- changelog.md | 14 ++ lib/pure/collections/sets.nim | 4 +- lib/pure/collections/tables.nim | 4 - lib/pure/options.nim | 1 - lib/pure/strtabs.nim | 20 +-- lib/std/jsonutils.nim | 282 ++++++++++++++++++++++++++++---- tests/stdlib/tjsonutils.nim | 185 ++++++++++++++++++++- 7 files changed, 449 insertions(+), 61 deletions(-) diff --git a/changelog.md b/changelog.md index 2f7b584a3..3333a837b 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,20 @@ ## Standard library additions and changes +- Added some enhancements to `std/jsonutils` module. + * Added a possibility to deserialize JSON arrays directly to `HashSet` and + `OrderedSet` types and respectively to serialize those types to JSON arrays + via `jsonutils.fromJson` and `jsonutils.toJson` procedures. + * Added a possibility to deserialize JSON `null` objects to Nim option objects + and respectively to serialize Nim option object to JSON object if `isSome` + or to JSON null object if `isNone` via `jsonutils.fromJson` and + `jsonutils.toJson` procedures. + * Added `Joptions` parameter to `jsonutils.fromJson` procedure currently + containing two boolean options `allowExtraKeys` and `allowMissingKeys`. + - If `allowExtraKeys` is `true` Nim's object to which the JSON is parsed is + not required to have a field for every JSON key. + - If `allowMissingKeys` is `true` Nim's object to which JSON is parsed is + allowed to have fields without corresponding JSON keys. - Added `bindParams`, `bindParam` to `db_sqlite` for binding parameters into a `SqlPrepared` statement. - Add `tryInsert`,`insert` procs to `db_*` libs accept primary key column name. - Added `xmltree.newVerbatimText` support create `style`'s,`script`'s text. diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim index b019da2a7..67e407576 100644 --- a/lib/pure/collections/sets.nim +++ b/lib/pure/collections/sets.nim @@ -80,6 +80,8 @@ type ## <#initOrderedSet,int>`_ before calling other procs on it. data: OrderedKeyValuePairSeq[A] counter, first, last: int + SomeSet*[A] = HashSet[A] | OrderedSet[A] + ## Type union representing `HashSet` or `OrderedSet`. const defaultInitialSize* = 64 @@ -907,8 +909,6 @@ iterator pairs*[A](s: OrderedSet[A]): tuple[a: int, b: A] = forAllOrderedPairs: yield (idx, s.data[h].key) - - # ----------------------------------------------------------------------- diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index 6d79638c2..dc21c0539 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -1750,10 +1750,6 @@ iterator mvalues*[A, B](t: var OrderedTable[A, B]): var B = yield t.data[h].val assert(len(t) == L, "the length of the table changed while iterating over it") - - - - # --------------------------------------------------------------------------- # --------------------------- OrderedTableRef ------------------------------- # --------------------------------------------------------------------------- diff --git a/lib/pure/options.nim b/lib/pure/options.nim index 4efe04d67..076cf3707 100644 --- a/lib/pure/options.nim +++ b/lib/pure/options.nim @@ -372,7 +372,6 @@ proc unsafeGet*[T](self: Option[T]): lent T {.inline.}= assert self.isSome result = self.val - when isMainModule: import unittest, sequtils diff --git a/lib/pure/strtabs.nim b/lib/pure/strtabs.nim index 4c2dd1451..864db148f 100644 --- a/lib/pure/strtabs.nim +++ b/lib/pure/strtabs.nim @@ -89,6 +89,7 @@ const growthFactor = 2 startSize = 64 +proc mode*(t: StringTableRef): StringTableMode {.inline.} = t.mode iterator pairs*(t: StringTableRef): tuple[key, value: string] = ## Iterates over every `(key, value)` pair in the table `t`. @@ -422,25 +423,6 @@ proc `%`*(f: string, t: StringTableRef, flags: set[FormatFlag] = {}): string {. add(result, f[i]) inc(i) -since (1,3,5): - proc fromJsonHook*[T](a: var StringTableRef, b: T) = - ## for json.fromJson - mixin jsonTo - var mode = jsonTo(b["mode"], StringTableMode) - a = newStringTable(mode) - let b2 = b["table"] - for k,v in b2: a[k] = jsonTo(v, string) - - proc toJsonHook*[](a: StringTableRef): auto = - ## for json.toJson - mixin newJObject - mixin toJson - result = newJObject() - result["mode"] = toJson($a.mode) - let t = newJObject() - for k,v in a: t[k] = toJson(v) - result["table"] = t - when isMainModule: var x = {"k": "v", "11": "22", "565": "67"}.newStringTable assert x["k"] == "v" diff --git a/lib/std/jsonutils.nim b/lib/std/jsonutils.nim index 22f2a7a89..dd174303a 100644 --- a/lib/std/jsonutils.nim +++ b/lib/std/jsonutils.nim @@ -13,25 +13,33 @@ runnableExamples: let j = a.toJson doAssert j.jsonTo(type(a)).toJson == j -import std/[json,tables,strutils] +import std/[json,strutils,tables,sets,strtabs,options] #[ -xxx -use toJsonHook,fromJsonHook for Table|OrderedTable -add Options support also using toJsonHook,fromJsonHook and remove `json=>options` dependency - Future directions: add a way to customize serialization, for eg: -* allowing missing or extra fields in JsonNode * field renaming * allow serializing `enum` and `char` as `string` instead of `int` (enum is more compact/efficient, and robust to enum renamings, but string is more human readable) * handle cyclic references, using a cache of already visited addresses +* implement support for serialization and de-serialization of nested variant + objects. ]# import std/macros +type + Joptions* = object + ## Options controlling the behavior of `fromJson`. + allowExtraKeys*: bool + ## If `true` Nim's object to which the JSON is parsed is not required to + ## have a field for every JSON key. + allowMissingKeys*: bool + ## If `true` Nim's object to which JSON is parsed is allowed to have + ## fields without corresponding JSON keys. + # in future work: a key rename could be added + proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".} proc distinctBase(T: typedesc): typedesc {.magic: "TypeTrait".} template distinctBase[T](a: T): untyped = distinctBase(type(a))(a) @@ -58,11 +66,11 @@ macro getDiscriminants(a: typedesc): seq[string] = result = quote do: seq[string].default -macro initCaseObject(a: typedesc, fun: untyped): untyped = +macro initCaseObject(T: typedesc, fun: untyped): untyped = ## does the minimum to construct a valid case object, only initializing ## the discriminant fields; see also `getDiscriminants` # maybe candidate for std/typetraits - var a = a.getTypeImpl + var a = T.getTypeImpl doAssert a.kind == nnkBracketExpr let sym = a[1] let t = sym.getTypeImpl @@ -92,20 +100,81 @@ proc checkJsonImpl(cond: bool, condStr: string, msg = "") = template checkJson(cond: untyped, msg = "") = checkJsonImpl(cond, astToStr(cond), msg) -template fromJsonFields(a, b, T, keys) = - checkJson b.kind == JObject, $(b.kind) # we could customize whether to allow JNull - var num = 0 - for key, val in fieldPairs(a): - num.inc - when key notin keys: - if b.hasKey key: - fromJson(val, b[key]) - else: - # we could customize to allow this - checkJson false, $($T, key, b) - checkJson b.len == num, $(b.len, num, $T, b) # could customize +proc hasField[T](obj: T, field: string): bool = + for k, _ in fieldPairs(obj): + if k == field: + return true + return false -proc fromJson*[T](a: var T, b: JsonNode) = +macro accessField(obj: typed, name: static string): untyped = + newDotExpr(obj, ident(name)) + +template fromJsonFields(newObj, oldObj, json, discKeys, opt) = + type T = typeof(newObj) + # we could customize whether to allow JNull + checkJson json.kind == JObject, $json.kind + var num, numMatched = 0 + for key, val in fieldPairs(newObj): + num.inc + when key notin discKeys: + if json.hasKey key: + numMatched.inc + fromJson(val, json[key]) + elif opt.allowMissingKeys: + # if there are no discriminant keys the `oldObj` must always have the + # same keys as the new one. Otherwise we must check, because they could + # be set to different branches. + when typeof(oldObj) isnot typeof(nil): + if discKeys.len == 0 or hasField(oldObj, key): + val = accessField(oldObj, key) + else: + checkJson false, $($T, key, json) + else: + if json.hasKey key: + numMatched.inc + + let ok = + if opt.allowExtraKeys and opt.allowMissingKeys: + true + elif opt.allowExtraKeys: + # This check is redundant because if here missing keys are not allowed, + # and if `num != numMatched` it will fail in the loop above but it is left + # for clarity. + assert num == numMatched + num == numMatched + elif opt.allowMissingKeys: + json.len == numMatched + else: + json.len == num and num == numMatched + + checkJson ok, $(json.len, num, numMatched, $T, json) + +proc fromJson*[T](a: var T, b: JsonNode, opt = Joptions()) + +proc discKeyMatch[T](obj: T, json: JsonNode, key: static string): bool = + if not json.hasKey key: + return true + let field = accessField(obj, key) + var jsonVal: typeof(field) + fromJson(jsonVal, json[key]) + if jsonVal != field: + return false + return true + +macro discKeysMatchBodyGen(obj: typed, json: JsonNode, + keys: static seq[string]): untyped = + result = newStmtList() + let r = ident("result") + for key in keys: + let keyLit = newLit key + result.add quote do: + `r` = `r` and discKeyMatch(`obj`, `json`, `keyLit`) + +proc discKeysMatch[T](obj: T, json: JsonNode, keys: static seq[string]): bool = + result = true + discKeysMatchBodyGen(obj, json, keys) + +proc fromJson*[T](a: var T, b: JsonNode, opt = Joptions()) = ## inplace version of `jsonTo` #[ adding "json path" leading to `b` can be added in future work. @@ -113,10 +182,6 @@ proc fromJson*[T](a: var T, b: JsonNode) = checkJson b != nil, $($T, b) when compiles(fromJsonHook(a, b)): fromJsonHook(a, b) elif T is bool: a = to(b,T) - elif T is Table | OrderedTable: - a.clear - for k,v in b: - a[k] = jsonTo(v, typeof(a[k])) elif T is enum: case b.kind of JInt: a = T(b.getBiggestInt()) @@ -148,14 +213,26 @@ proc fromJson*[T](a: var T, b: JsonNode) = for i, val in b.getElems: fromJson(a[i], val) elif T is object: - template fun(key, typ): untyped = - jsonTo(b[key], typ) - a = initCaseObject(T, fun) + template fun(key, typ): untyped {.used.} = + if b.hasKey key: + jsonTo(b[key], typ) + elif hasField(a, key): + accessField(a, key) + else: + default(typ) const keys = getDiscriminants(T) - fromJsonFields(a, b, T, keys) + when keys.len == 0: + fromJsonFields(a, nil, b, keys, opt) + else: + if discKeysMatch(a, b, keys): + fromJsonFields(a, nil, b, keys, opt) + else: + var newObj = initCaseObject(T, fun) + fromJsonFields(newObj, a, b, keys, opt) + a = newObj elif T is tuple: when isNamedTuple(T): - fromJsonFields(a, b, T, seq[string].default) + fromJsonFields(a, nil, b, seq[string].default, opt) else: checkJson b.kind == JArray, $(b.kind) # we could customize whether to allow JNull var i = 0 @@ -175,9 +252,6 @@ proc toJson*[T](a: T): JsonNode = ## serializes `a` to json; uses `toJsonHook(a: T)` if it's in scope to ## customize serialization, see strtabs.toJsonHook for an example. when compiles(toJsonHook(a)): result = toJsonHook(a) - elif T is Table | OrderedTable: - result = newJObject() - for k, v in pairs(a): result[k] = toJson(v) elif T is object | tuple: when T is object or isNamedTuple(T): result = newJObject() @@ -198,3 +272,145 @@ proc toJson*[T](a: T): JsonNode = elif T is bool: result = %(a) elif T is Ordinal: result = %(a.ord) else: result = %a + +proc fromJsonHook*[K, V](t: var (Table[K, V] | OrderedTable[K, V]), + jsonNode: JsonNode) = + ## Enables `fromJson` for `Table` and `OrderedTable` types. + ## + ## See also: + ## * `toJsonHook proc<#toJsonHook,(Table[K,V]|OrderedTable[K,V])>`_ + runnableExamples: + import tables, json + var foo: tuple[t: Table[string, int], ot: OrderedTable[string, int]] + fromJson(foo, parseJson(""" + {"t":{"two":2,"one":1},"ot":{"one":1,"three":3}}""")) + assert foo.t == [("one", 1), ("two", 2)].toTable + assert foo.ot == [("one", 1), ("three", 3)].toOrderedTable + + assert jsonNode.kind == JObject, + "The kind of the `jsonNode` must be `JObject`, but its actual " & + "type is `" & $jsonNode.kind & "`." + clear(t) + for k, v in jsonNode: + t[k] = jsonTo(v, V) + +proc toJsonHook*[K, V](t: (Table[K, V] | OrderedTable[K, V])): JsonNode = + ## Enables `toJson` for `Table` and `OrderedTable` types. + ## + ## See also: + ## * `fromJsonHook proc<#fromJsonHook,(Table[K,V]|OrderedTable[K,V]),JsonNode>`_ + runnableExamples: + import tables, json + let foo = ( + t: [("two", 2)].toTable, + ot: [("one", 1), ("three", 3)].toOrderedTable) + assert $toJson(foo) == """{"t":{"two":2},"ot":{"one":1,"three":3}}""" + + result = newJObject() + for k, v in pairs(t): + result[k] = toJson(v) + +proc fromJsonHook*[A](s: var SomeSet[A], jsonNode: JsonNode) = + ## Enables `fromJson` for `HashSet` and `OrderedSet` types. + ## + ## See also: + ## * `toJsonHook proc<#toJsonHook,SomeSet[A]>`_ + runnableExamples: + import sets, json + var foo: tuple[hs: HashSet[string], os: OrderedSet[string]] + fromJson(foo, parseJson(""" + {"hs": ["hash", "set"], "os": ["ordered", "set"]}""")) + assert foo.hs == ["hash", "set"].toHashSet + assert foo.os == ["ordered", "set"].toOrderedSet + + assert jsonNode.kind == JArray, + "The kind of the `jsonNode` must be `JArray`, but its actual " & + "type is `" & $jsonNode.kind & "`." + clear(s) + for v in jsonNode: + incl(s, jsonTo(v, A)) + +proc toJsonHook*[A](s: SomeSet[A]): JsonNode = + ## Enables `toJson` for `HashSet` and `OrderedSet` types. + ## + ## See also: + ## * `fromJsonHook proc<#fromJsonHook,SomeSet[A],JsonNode>`_ + runnableExamples: + import sets, json + let foo = (hs: ["hash"].toHashSet, os: ["ordered", "set"].toOrderedSet) + assert $toJson(foo) == """{"hs":["hash"],"os":["ordered","set"]}""" + + result = newJArray() + for k in s: + add(result, toJson(k)) + +proc fromJsonHook*[T](self: var Option[T], jsonNode: JsonNode) = + ## Enables `fromJson` for `Option` types. + ## + ## See also: + ## * `toJsonHook proc<#toJsonHook,Option[T]>`_ + runnableExamples: + import options, json + var opt: Option[string] + fromJsonHook(opt, parseJson("\"test\"")) + assert get(opt) == "test" + fromJson(opt, parseJson("null")) + assert isNone(opt) + + if jsonNode.kind != JNull: + self = some(jsonTo(jsonNode, T)) + else: + self = none[T]() + +proc toJsonHook*[T](self: Option[T]): JsonNode = + ## Enables `toJson` for `Option` types. + ## + ## See also: + ## * `fromJsonHook proc<#fromJsonHook,Option[T],JsonNode>`_ + runnableExamples: + import options, json + let optSome = some("test") + assert $toJson(optSome) == "\"test\"" + let optNone = none[string]() + assert $toJson(optNone) == "null" + + if isSome(self): + toJson(get(self)) + else: + newJNull() + +proc fromJsonHook*(a: var StringTableRef, b: JsonNode) = + ## Enables `fromJson` for `StringTableRef` type. + ## + ## See also: + ## * `toJsonHook` proc<#toJsonHook,StringTableRef>`_ + runnableExamples: + import strtabs, json + var t = newStringTable(modeCaseSensitive) + let jsonStr = """{"mode": 0, "table": {"name": "John", "surname": "Doe"}}""" + fromJsonHook(t, parseJson(jsonStr)) + assert t[] == newStringTable("name", "John", "surname", "Doe", + modeCaseSensitive)[] + + var mode = jsonTo(b["mode"], StringTableMode) + a = newStringTable(mode) + let b2 = b["table"] + for k,v in b2: a[k] = jsonTo(v, string) + +proc toJsonHook*(a: StringTableRef): JsonNode = + ## Enables `toJson` for `StringTableRef` type. + ## + ## See also: + ## * `fromJsonHook` proc<#fromJsonHook,StringTableRef,JsonNode>`_ + runnableExamples: + import strtabs, json + let t = newStringTable("name", "John", "surname", "Doe", modeCaseSensitive) + let jsonStr = """{"mode": "modeCaseSensitive", + "table": {"name": "John", "surname": "Doe"}}""" + assert toJson(t) == parseJson(jsonStr) + + result = newJObject() + result["mode"] = toJson($a.mode) + let t = newJObject() + for k,v in a: t[k] = toJson(v) + result["table"] = t diff --git a/tests/stdlib/tjsonutils.nim b/tests/stdlib/tjsonutils.nim index 0b2ec7179..fefd412e7 100644 --- a/tests/stdlib/tjsonutils.nim +++ b/tests/stdlib/tjsonutils.nim @@ -13,8 +13,7 @@ proc testRoundtrip[T](t: T, expected: string) = t2.fromJson(j) doAssert t2.toJson == j -import tables -import strtabs +import tables, sets, algorithm, sequtils, options, strtabs type Foo = ref object id: int @@ -119,5 +118,187 @@ template fn() = testRoundtrip(Foo[int](t1: false, z2: 7)): """{"t1":false,"z2":7}""" # pending https://github.com/nim-lang/Nim/issues/14698, test with `type Foo[T] = ref object` + block testHashSet: + testRoundtrip(HashSet[string]()): "[]" + testRoundtrip([""].toHashSet): """[""]""" + testRoundtrip(["one"].toHashSet): """["one"]""" + + var s: HashSet[string] + fromJson(s, parseJson("""["one","two"]""")) + doAssert s == ["one", "two"].toHashSet + + let jsonNode = toJson(s) + doAssert jsonNode.elems.mapIt(it.str).sorted == @["one", "two"] + + block testOrderedSet: + testRoundtrip(["one", "two", "three"].toOrderedSet): + """["one","two","three"]""" + + block testOption: + testRoundtrip(some("test")): "\"test\"" + testRoundtrip(none[string]()): "null" + testRoundtrip(some(42)): "42" + testRoundtrip(none[int]()): "null" + + block testStrtabs: + testRoundtrip(newStringTable(modeStyleInsensitive)): + """{"mode":"modeStyleInsensitive","table":{}}""" + + testRoundtrip( + newStringTable("name", "John", "surname", "Doe", modeCaseSensitive)): + """{"mode":"modeCaseSensitive","table":{"name":"John","surname":"Doe"}}""" + + block testJoptions: + type + AboutLifeUniverseAndEverythingElse = object + question: string + answer: int + + block testExceptionOnExtraKeys: + var guide: AboutLifeUniverseAndEverythingElse + let json = parseJson( + """{"question":"6*9=?","answer":42,"author":"Douglas Adams"}""") + doAssertRaises ValueError, fromJson(guide, json) + doAssertRaises ValueError, + fromJson(guide, json, Joptions(allowMissingKeys: true)) + + type + A = object + a1,a2,a3: int + var a: A + let j = parseJson("""{"a3": 1, "a4": 2}""") + doAssertRaises ValueError, + fromJson(a, j, Joptions(allowMissingKeys: true)) + + block testExceptionOnMissingKeys: + var guide: AboutLifeUniverseAndEverythingElse + let json = parseJson("""{"answer":42}""") + doAssertRaises ValueError, fromJson(guide, json) + doAssertRaises ValueError, + fromJson(guide, json, Joptions(allowExtraKeys: true)) + + block testAllowExtraKeys: + var guide: AboutLifeUniverseAndEverythingElse + let json = parseJson( + """{"question":"6*9=?","answer":42,"author":"Douglas Adams"}""") + fromJson(guide, json, Joptions(allowExtraKeys: true)) + doAssert guide == AboutLifeUniverseAndEverythingElse( + question: "6*9=?", answer: 42) + + block testAllowMissingKeys: + var guide = AboutLifeUniverseAndEverythingElse( + question: "6*9=?", answer: 54) + let json = parseJson("""{"answer":42}""") + fromJson(guide, json, Joptions(allowMissingKeys: true)) + doAssert guide == AboutLifeUniverseAndEverythingElse( + question: "6*9=?", answer: 42) + + block testAllowExtraAndMissingKeys: + var guide = AboutLifeUniverseAndEverythingElse( + question: "6*9=?", answer: 54) + let json = parseJson( + """{"answer":42,"author":"Douglas Adams"}""") + fromJson(guide, json, Joptions( + allowExtraKeys: true, allowMissingKeys: true)) + doAssert guide == AboutLifeUniverseAndEverythingElse( + question: "6*9=?", answer: 42) + + type + Foo = object + a: array[2, string] + case b: bool + of false: f: float + of true: t: tuple[i: int, s: string] + case c: range[0 .. 2] + of 0: c0: int + of 1: c1: float + of 2: c2: string + + block testExceptionOnMissingDiscriminantKey: + var foo: Foo + let json = parseJson("""{"a":["one","two"]}""") + doAssertRaises ValueError, fromJson(foo, json) + + block testDoNotResetMissingFieldsWhenHaveDiscriminantKey: + var foo = Foo(a: ["one", "two"], b: true, t: (i: 42, s: "s"), + c: 0, c0: 1) + let json = parseJson("""{"b":true,"c":2}""") + fromJson(foo, json, Joptions(allowMissingKeys: true)) + doAssert foo.a == ["one", "two"] + doAssert foo.b + doAssert foo.t == (i: 42, s: "s") + doAssert foo.c == 2 + doAssert foo.c2 == "" + + block testAllowMissingDiscriminantKeys: + var foo: Foo + let json = parseJson("""{"a":["one","two"],"c":1,"c1":3.14159}""") + fromJson(foo, json, Joptions(allowMissingKeys: true)) + doAssert foo.a == ["one", "two"] + doAssert not foo.b + doAssert foo.f == 0.0 + doAssert foo.c == 1 + doAssert foo.c1 == 3.14159 + + block testExceptionOnWrongDiscirminatBranchInJson: + var foo = Foo(b: false, f: 3.14159, c: 0, c0: 42) + let json = parseJson("""{"c2": "hello"}""") + doAssertRaises ValueError, + fromJson(foo, json, Joptions(allowMissingKeys: true)) + # Test that the original fields are not reset. + doAssert not foo.b + doAssert foo.f == 3.14159 + doAssert foo.c == 0 + doAssert foo.c0 == 42 + + block testNoExceptionOnRightDiscriminantBranchInJson: + var foo = Foo(b: false, f: 0, c:1, c1: 0) + let json = parseJson("""{"f":2.71828,"c1": 3.14159}""") + fromJson(foo, json, Joptions(allowMissingKeys: true)) + doAssert not foo.b + doAssert foo.f == 2.71828 + doAssert foo.c == 1 + doAssert foo.c1 == 3.14159 + + block testAllowExtraKeysInJsonOnWrongDisciriminatBranch: + var foo = Foo(b: false, f: 3.14159, c: 0, c0: 42) + let json = parseJson("""{"c2": "hello"}""") + fromJson(foo, json, Joptions(allowMissingKeys: true, + allowExtraKeys: true)) + # Test that the original fields are not reset. + doAssert not foo.b + doAssert foo.f == 3.14159 + doAssert foo.c == 0 + doAssert foo.c0 == 42 + + when false: + ## TODO: Implement support for nested variant objects allowing the tests + ## bellow to pass. + block testNestedVariantObjects: + type + Variant = object + case b: bool + of false: + case bf: bool + of false: bff: int + of true: bft: float + of true: + case bt: bool + of false: btf: string + of true: btt: char + + testRoundtrip(Variant(b: false, bf: false, bff: 42)): + """{"b": false, "bf": false, "bff": 42}""" + testRoundtrip(Variant(b: false, bf: true, bft: 3.14159)): + """{"b": false, "bf": true, "bft": 3.14159}""" + testRoundtrip(Variant(b: true, bt: false, btf: "test")): + """{"b": true, "bt": false, "btf": "test"}""" + testRoundtrip(Variant(b: true, bt: true, btt: 'c')): + """{"b": true, "bt": true, "btt": "c"}""" + + # TODO: Add additional tests with missing and extra JSON keys, both when + # allowed and forbidden analogous to the tests for the not nested + # variant objects. + static: fn() fn() From 217675cf84dc47bb68f03b341aff14315804efec Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 9 Sep 2020 14:19:22 +0200 Subject: [PATCH 0587/3768] borrow checking refinements (#15290) * added basic borrowing test --- compiler/typeallowed.nim | 6 +++--- compiler/varpartitions.nim | 20 ++++++++++++++++++-- tests/effects/tcannot_borrow.nim | 18 ++++++++++++++++++ 3 files changed, 39 insertions(+), 5 deletions(-) create mode 100644 tests/effects/tcannot_borrow.nim diff --git a/compiler/typeallowed.nim b/compiler/typeallowed.nim index 5a192783e..651a21ade 100644 --- a/compiler/typeallowed.nim +++ b/compiler/typeallowed.nim @@ -57,7 +57,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, of tyVar, tyLent: if kind in {skProc, skFunc, skConst} and (views notin c.features): result = t - elif t.kind == tyLent and kind != skResult: + elif t.kind == tyLent and kind != skResult and (views notin c.features): result = t else: var t2 = skipTypes(t[0], abstractInst-{tyTypeDesc}) @@ -68,10 +68,10 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, if (kind != skParam and views notin c.features) or taIsOpenArray in flags: result = t else: result = typeAllowedAux(marker, t2[0], kind, c, flags+{taIsOpenArray}) of tyUncheckedArray: - if kind != skParam: result = t + if kind != skParam and views notin c.features: result = t else: result = typeAllowedAux(marker, t2[0], kind, c, flags) else: - if kind notin {skParam, skResult}: result = t + if kind notin {skParam, skResult} and views notin c.features: result = t else: result = typeAllowedAux(marker, t2, kind, c, flags) of tyProc: if kind in {skVar, skLet, skConst} and taIsTemplateOrMacro in flags: diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim index 756249c8b..bbde68cbe 100644 --- a/compiler/varpartitions.nim +++ b/compiler/varpartitions.nim @@ -375,7 +375,7 @@ proc deps(c: var Partitions; dest, src: PNode) = allRoots(src, sources) proc wrap(t: PType): bool {.nimcall.} = t.kind in {tyRef, tyPtr} - let destIsComplex = types.searchTypeFor(dest.typ, wrap) + let destIsComplex = types.searchTypeFor(dest.typ, wrap) or isViewType(dest.typ) for t in targets: if dest.kind != nkSym and c.inNoSideEffectSection == 0: @@ -518,19 +518,35 @@ proc computeGraphPartitions*(s: PSym; n: PNode; cursorInference = false): Partit traverse(result, n) proc dangerousMutation(g: MutationInfo; v: VarIndex): bool = + #echo "range ", v.aliveStart, " .. ", v.aliveEnd if isMutated in g.flags: for m in g.mutations: + #echo "mutation ", m if m in v.aliveStart..v.aliveEnd: return true return false +proc cannotBorrow(config: ConfigRef; s: PSym; g: MutationInfo) = + var m = "cannot borrow " & s.name.s & + "; what it borrows from is potentially mutated" + + if g.mutatedHere != unknownLineInfo: + m.add "\n" + m.add config $ g.mutatedHere + m.add " the mutation is here" + if g.connectedVia != unknownLineInfo: + m.add "\n" + m.add config $ g.connectedVia + m.add " is the statement that connected the mutation to the parameter" + localError(config, s.info, m) + proc checkBorrowedLocations*(par: var Partitions; config: ConfigRef) = for i in 0 ..< par.s.len: let s = par.s[i].sym if s.kind != skParam and isViewType(s.typ): let rid = root(par, i) if par.s[rid].kind == isRootOf and dangerousMutation(par.graphs[par.s[rid].graphIndex], par.s[i]): - localError(config, s.info, config $ par.graphs[par.s[rid].graphIndex]) + cannotBorrow(config, s, par.graphs[par.s[rid].graphIndex]) proc computeCursors*(s: PSym; n: PNode; config: ConfigRef) = var par = computeGraphPartitions(s, n, true) diff --git a/tests/effects/tcannot_borrow.nim b/tests/effects/tcannot_borrow.nim new file mode 100644 index 000000000..699176b04 --- /dev/null +++ b/tests/effects/tcannot_borrow.nim @@ -0,0 +1,18 @@ +discard """ + errormsg: "cannot borrow" + nimout: '''tcannot_borrow.nim(16, 7) Error: cannot borrow meh; what it borrows from is potentially mutated +tcannot_borrow.nim(17, 3) the mutation is here +tcannot_borrow.nim(16, 7) is the statement that connected the mutation to the parameter''' + line: 16 +""" + +{.experimental: "views".} + +type + Foo = object + field: string + +proc dangerous(s: var seq[Foo]) = + let meh: lent Foo = s[0] + s.setLen 0 + echo meh.field From 3f00a738dbc8319b4dd2b86bf5529c096f2dd243 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 9 Sep 2020 14:20:10 +0200 Subject: [PATCH 0588/3768] fixes #15280 [backport:1.2] (#15281) * fixes #15280 [backport:1.2] * make tests green again * adapt tests --- compiler/sem.nim | 2 +- tests/arc/tmovebug.nim | 6 +-- tests/casestmt/tcasestmt.nim | 2 +- tests/semreject/tmixing_return_and_exprs.nim | 44 ++++++++++++++++++++ 4 files changed, 49 insertions(+), 5 deletions(-) create mode 100644 tests/semreject/tmixing_return_and_exprs.nim diff --git a/compiler/sem.nim b/compiler/sem.nim index 705e1b72c..bc6119460 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -184,7 +184,7 @@ proc endsInNoReturn(n: PNode): bool = var it = n while it.kind in {nkStmtList, nkStmtListExpr} and it.len > 0: it = it.lastSon - result = it.kind in nkLastBlockStmts or + result = it.kind in (nkLastBlockStmts-{nkReturnStmt}) or it.kind in nkCallKinds and it[0].kind == nkSym and sfNoReturn in it[0].sym.flags proc commonType*(x: PType, y: PNode): PType = diff --git a/tests/arc/tmovebug.nim b/tests/arc/tmovebug.nim index 3b7a7c5df..c735ea1eb 100644 --- a/tests/arc/tmovebug.nim +++ b/tests/arc/tmovebug.nim @@ -502,7 +502,7 @@ weirdScopes() # bug #14985 proc getScope(): string = if true: - return "hi" + "hi" else: "else" @@ -512,14 +512,14 @@ proc getScope3(): string = try: "try" except: - return "except" + "except" echo getScope3() proc getScope2(): string = case true of true: - return "bye" + "bye" else: "else" diff --git a/tests/casestmt/tcasestmt.nim b/tests/casestmt/tcasestmt.nim index 53cccdb64..2151892aa 100644 --- a/tests/casestmt/tcasestmt.nim +++ b/tests/casestmt/tcasestmt.nim @@ -258,7 +258,7 @@ func foo(input: string): int = try: parseInt(input) except: - return + 0 func foo2(b, input: string): int = case b: diff --git a/tests/semreject/tmixing_return_and_exprs.nim b/tests/semreject/tmixing_return_and_exprs.nim new file mode 100644 index 000000000..2848c47e4 --- /dev/null +++ b/tests/semreject/tmixing_return_and_exprs.nim @@ -0,0 +1,44 @@ +discard """ + errormsg: "expression 'len(src) shl 1' is of type 'int' and has to be used (or discarded)" + line: 19 +""" + +# bug #15280 + +type + HexFlags* {.pure.} = enum + LowerCase, ## Produce lowercase hexadecimal characters + PadOdd, ## Pads odd strings + SkipSpaces, ## Skips all the whitespace characters inside of string + SkipPrefix ## Skips `0x` and `x` prefixes at the begining of string + + +proc bytesToHex*(src: openarray[byte], dst: var openarray[char], + flags: set[HexFlags]): int = + if len(dst) == 0: + (len(src) shl 1) + else: + var halflast = false + let dstlen = len(dst) + var srclen = len(src) + + if dstlen < (srclen shl 1): + if (dstlen and 1) == 1: + srclen = (dstlen - 1) shr 1 + halflast = true + else: + srclen = (dstlen shr 1) + + let lowercase = (HexFlags.LowerCase in flags) + + var k = 0 + for i in 0 ..< srclen: + let x = int(src[i]) + inc(k, 2) + + if halflast: + let x = int(src[srclen]) + inc(k) + + return k + From 61c85e034de571cb25020b6dddf662c82896907a Mon Sep 17 00:00:00 2001 From: cooldome Date: Thu, 10 Sep 2020 11:40:39 +0100 Subject: [PATCH 0589/3768] Fix #15286 (#15292) * fix #15286 * fix spacing --- compiler/injectdestructors.nim | 3 +++ tests/arc/t14383.nim | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 4fe4cb1da..b8b8e776e 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -728,6 +728,9 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode = for i in ord(n.kind in {nkObjConstr, nkClosure}).. Date: Thu, 10 Sep 2020 16:30:20 +0200 Subject: [PATCH 0590/3768] testament improvement: allow inline error messages inside test cases (#15294) * testament support for inline error messages * adapt teffects1.nim test to show the potential --- testament/specs.nim | 95 +++++++++++++++++++++++++++++++++---- testament/testament.nim | 49 ++++++++++++++++++- tests/effects/teffects1.nim | 19 ++++---- 3 files changed, 142 insertions(+), 21 deletions(-) diff --git a/testament/specs.nim b/testament/specs.nim index acffbf8be..e0b9cbeec 100644 --- a/testament/specs.nim +++ b/testament/specs.nim @@ -7,8 +7,8 @@ # distribution, for details about the copyright. # -import sequtils, parseutils, strutils, os, streams, parsecfg -from hashes import hash +import sequtils, parseutils, strutils, os, streams, parsecfg, + tables, hashes type TestamentData* = ref object # better to group globals under 1 object; could group the other ones here too @@ -62,6 +62,11 @@ type targetObjC = "ObjC" targetJS = "JS" + InlineError* = object + kind*: string + msg*: string + line*, col*: int + TSpec* = object action*: TTestAction file*, cmd*: string @@ -90,6 +95,7 @@ type useValgrind*: bool timeout*: float # in seconds, fractions possible, # but don't rely on much precision + inlineErrors*: seq[InlineError] # line information to error message proc getCmd*(s: TSpec): string = if s.cmd.len == 0: @@ -116,14 +122,85 @@ when not declared(parseCfgBool): of "n", "no", "false", "0", "off": result = false else: raise newException(ValueError, "cannot interpret as a bool: " & s) -proc extractSpec(filename: string): string = - const tripleQuote = "\"\"\"" - var x = readFile(filename).string - var a = x.find(tripleQuote) - var b = x.find(tripleQuote, a+3) +const + inlineErrorMarker = "#[tt." + +proc extractErrorMsg(s: string; i: int; line: var int; col: var int; spec: var TSpec): int = + result = i + len(inlineErrorMarker) + inc col, len(inlineErrorMarker) + var kind = "" + while result < s.len and s[result] in IdentChars: + kind.add s[result] + inc result + inc col + + var caret = (line, -1) + + template skipWhitespace = + while result < s.len and s[result] in Whitespace: + if s[result] == '\n': + col = 1 + inc line + else: + inc col + inc result + + skipWhitespace() + if result < s.len and s[result] == '^': + caret = (line-1, col) + inc result + inc col + skipWhitespace() + + var msg = "" + while result < s.len-1: + if s[result] == '\n': + inc result + inc line + col = 1 + elif s[result] == ']' and s[result+1] == '#': + while msg.len > 0 and msg[^1] in Whitespace: + setLen msg, msg.len - 1 + + inc result + inc col, 2 + if kind == "Error": spec.action = actionReject + spec.unjoinable = true + spec.inlineErrors.add InlineError(kind: kind, msg: msg, line: caret[0], col: caret[1]) + break + else: + msg.add s[result] + inc result + inc col + +proc extractSpec(filename: string; spec: var TSpec): string = + const + tripleQuote = "\"\"\"" + var s = readFile(filename).string + + var i = 0 + var a = -1 + var b = -1 + var line = 1 + var col = 1 + while i < s.len: + if s.continuesWith(tripleQuote, i): + if a < 0: a = i + elif b < 0: b = i + inc i, 2 + inc col + elif s[i] == '\n': + inc line + col = 1 + elif s.continuesWith(inlineErrorMarker, i): + i = extractErrorMsg(s, i, line, col, spec) + else: + inc col + inc i + # look for """ only in the first section if a >= 0 and b > a and a < 40: - result = x.substr(a+3, b-1).replace("'''", tripleQuote) + result = s.substr(a+3, b-1).replace("'''", tripleQuote) else: #echo "warning: file does not contain spec: " & filename result = "" @@ -160,7 +237,7 @@ proc isCurrentBatch(testamentData: TestamentData, filename: string): bool = proc parseSpec*(filename: string): TSpec = result.file = filename - let specStr = extractSpec(filename) + let specStr = extractSpec(filename, result) var ss = newStringStream(specStr) var p: CfgParser open(p, ss, filename, 1) diff --git a/testament/testament.nim b/testament/testament.nim index baa340139..4afafb645 100644 --- a/testament/testament.nim +++ b/testament/testament.nim @@ -12,7 +12,7 @@ import strutils, pegs, os, osproc, streams, json, backend, parseopt, specs, htmlgen, browsers, terminal, - algorithm, times, md5, sequtils, azure + algorithm, times, md5, sequtils, azure, intsets from std/sugar import dup import compiler/nodejs @@ -71,6 +71,7 @@ type let pegLineError = peg"{[^(]*} '(' {\d+} ', ' {\d+} ') ' ('Error') ':' \s* {.*}" + pegLineTemplate = peg""" {[^(]*} '(' {\d+} ', ' {\d+} ') ' @@ -317,8 +318,50 @@ proc addResult(r: var TResults, test: TTest, target: TTarget, discard waitForExit(p) close(p) +proc checkForInlineErrors(r: var TResults, expected, given: TSpec, test: TTest, target: TTarget) = + let pegLine = peg"{[^(]*} '(' {\d+} ', ' {\d+} ') ' {[^:]*} ':' \s* {.*}" + var covered = initIntSet() + for line in splitLines(given.nimout): + + if line =~ pegLine: + let file = extractFilename(matches[0]) + let line = try: parseInt(matches[1]) except: -1 + let col = try: parseInt(matches[2]) except: -1 + let kind = matches[3] + let msg = matches[4] + + if file == extractFilename test.name: + var i = 0 + for x in expected.inlineErrors: + if x.line == line and (x.col == col or x.col < 0) and + x.kind == kind and x.msg in msg: + covered.incl i + inc i + + block coverCheck: + for j in 0..high(expected.inlineErrors): + if j notin covered: + var e = test.name + e.add "(" + e.addInt expected.inlineErrors[j].line + if expected.inlineErrors[j].col > 0: + e.add ", " + e.addInt expected.inlineErrors[j].col + e.add ") " + e.add expected.inlineErrors[j].kind + e.add ": " + e.add expected.inlineErrors[j].msg + + r.addResult(test, target, e, given.nimout, reMsgsDiffer) + break coverCheck + + r.addResult(test, target, "", given.msg, reSuccess) + inc(r.passed) + proc cmpMsgs(r: var TResults, expected, given: TSpec, test: TTest, target: TTarget) = - if strip(expected.msg) notin strip(given.msg): + if expected.inlineErrors.len > 0: + checkForInlineErrors(r, expected, given, test, target) + elif strip(expected.msg) notin strip(given.msg): r.addResult(test, target, expected.msg, given.msg, reMsgsDiffer) elif expected.nimout.len > 0 and expected.nimout.normalizeMsg notin given.nimout.normalizeMsg: r.addResult(test, target, expected.nimout, given.nimout, reMsgsDiffer) @@ -389,6 +432,8 @@ proc nimoutCheck(test: TTest; expectedNimout: string; given: var TSpec) = given.err = reMsgsDiffer break + + proc compilerOutputTests(test: TTest, target: TTarget, given: var TSpec, expected: TSpec; r: var TResults) = var expectedmsg: string = "" diff --git a/tests/effects/teffects1.nim b/tests/effects/teffects1.nim index 3bd19f4ab..3551ff1a1 100644 --- a/tests/effects/teffects1.nim +++ b/tests/effects/teffects1.nim @@ -1,13 +1,5 @@ discard """ - errormsg: "type mismatch: got but expected 'MyProcType = proc (x: int): string{.closure.}'" - file: "teffects1.nim" - line: 38 cmd: "nim check $file" - nimout: '''teffects1.nim(22, 28) template/generic instantiation from here -teffects1.nim(23, 13) Error: can raise an unlisted exception: ref IOError -teffects1.nim(22, 29) Hint: 'IO2Error' is declared but not used [XDeclaredButNotUsed] -teffects1.nim(38, 21) Error: type mismatch: got but expected 'MyProcType = proc (x: int): string{.closure.}' -.raise effects differ''' """ type @@ -20,7 +12,10 @@ type proc forw: int {. .} proc lier(): int {.raises: [IO2Error].} = - writeLine stdout, "arg" + #[tt.Hint ^ 'IO2Error' is declared but not used [XDeclaredButNotUsed] ]# + writeLine stdout, "arg" #[tt.Error + ^ can raise an unlisted exception: ref IOError + ]# proc forw: int = raise newException(IOError, "arg") @@ -35,5 +30,9 @@ proc foo(x: int): string {.raises: [ValueError].} = raise newException(ValueError, "Use single digit") $x -var p: MyProcType = foo +var p: MyProcType = foo #[tt.Error + ^ +type mismatch: got but expected 'MyProcType = proc (x: int): string{.closure.}' + +]# {.pop.} From b03d221d0b98cc338cb0809e9906e840551fa765 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Thu, 10 Sep 2020 19:25:09 -0300 Subject: [PATCH 0591/3768] Improve prelude so it does not hijacks documentation when used (#15299) --- lib/prelude.nim | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/lib/prelude.nim b/lib/prelude.nim index 940864207..5ad561158 100644 --- a/lib/prelude.nim +++ b/lib/prelude.nim @@ -7,17 +7,15 @@ # distribution, for details about the copyright. # -## This is an include file that simply imports common modules for your -## convenience: -## -## .. code-block:: nim -## include prelude -## -## Same as: -## -## .. code-block:: nim -## import os, strutils, times, parseutils, parseopt, hashes, tables, sets +# This is an include file that simply imports common modules for your +# convenience: +# +# .. code-block:: nim +# include prelude +# +# Same as: +# +# .. code-block:: nim +# import os, strutils, times, parseutils, parseopt, hashes, tables, sets import os, strutils, times, parseutils, parseopt, hashes, tables, sets - - From 625edf6a98142a4a31c52d694623aa9b7188708d Mon Sep 17 00:00:00 2001 From: hlaaftana <10591326+hlaaftana@users.noreply.github.com> Date: Fri, 11 Sep 2020 01:43:10 +0300 Subject: [PATCH 0592/3768] Add /lib/fusion to gitignore (#15295) --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index cf0bcfbd6..a4577c0d0 100644 --- a/.gitignore +++ b/.gitignore @@ -66,6 +66,7 @@ testament.db /tests/**/*.js /csources /dist/ +/lib/fusion # Private directories and files (IDEs) .*/ From 6a621b35e7c935655d3cafe840eb69d662e333b7 Mon Sep 17 00:00:00 2001 From: Miran Date: Fri, 11 Sep 2020 01:04:07 +0200 Subject: [PATCH 0593/3768] fix warnings for deprecated `low` and `high` (#15291) --- compiler/ast.nim | 4 ++-- compiler/saturate.nim | 20 ++++++++++---------- compiler/vmgen.nim | 2 +- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index b1a3a4a7b..8baf49265 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1356,8 +1356,8 @@ proc newType*(kind: TTypeKind, owner: PSym): PType = writeStackTrace() proc mergeLoc(a: var TLoc, b: TLoc) = - if a.k == low(a.k): a.k = b.k - if a.storage == low(a.storage): a.storage = b.storage + if a.k == low(typeof(a.k)): a.k = b.k + if a.storage == low(typeof(a.storage)): a.storage = b.storage a.flags.incl b.flags if a.lode == nil: a.lode = b.lode if a.r == nil: a.r = b.r diff --git a/compiler/saturate.nim b/compiler/saturate.nim index 9e8919514..fe6e03c8b 100644 --- a/compiler/saturate.nim +++ b/compiler/saturate.nim @@ -15,33 +15,33 @@ proc `|+|`*(a, b: BiggestInt): BiggestInt = if (result xor a) >= 0'i64 or (result xor b) >= 0'i64: return result if a < 0 or b < 0: - result = low(result) + result = low(typeof(result)) else: - result = high(result) + result = high(typeof(result)) proc `|-|`*(a, b: BiggestInt): BiggestInt = result = a -% b if (result xor a) >= 0'i64 or (result xor not b) >= 0'i64: return result if b > 0: - result = low(result) + result = low(typeof(result)) else: - result = high(result) + result = high(typeof(result)) proc `|abs|`*(a: BiggestInt): BiggestInt = - if a != low(a): + if a != low(typeof(a)): if a >= 0: result = a else: result = -a else: - result = low(a) + result = low(typeof(a)) proc `|div|`*(a, b: BiggestInt): BiggestInt = # (0..5) div (0..4) == (0..5) div (1..4) == (0 div 4)..(5 div 1) if b == 0'i64: # make the same as ``div 1``: result = a - elif a == low(a) and b == -1'i64: - result = high(result) + elif a == low(typeof(a)) and b == -1'i64: + result = high(typeof(result)) else: result = a div b @@ -74,6 +74,6 @@ proc `|*|`*(a, b: BiggestInt): BiggestInt = return result if floatProd >= 0.0: - result = high(result) + result = high(typeof(result)) else: - result = low(result) + result = low(typeof(result)) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index c2a2f6d42..ad5023aeb 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -611,7 +611,7 @@ proc genField(c: PCtx; n: PNode): TRegister = if n.kind != nkSym or n.sym.kind != skField: globalError(c.config, n.info, "no field symbol") let s = n.sym - if s.position > high(result): + if s.position > high(typeof(result)): globalError(c.config, n.info, "too large offset! cannot generate code for: " & s.name.s) result = s.position From 0c41ac792b64b710b4702cd4ae88d4392744a1ea Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 11 Sep 2020 01:50:42 +0200 Subject: [PATCH 0594/3768] fixes #15122 (#15301) --- compiler/liftdestructors.nim | 17 ++++++++++++-- tests/arc/theavy_recursion.nim | 43 ++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 tests/arc/theavy_recursion.nim diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index c1fd7981e..b3300850e 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -462,6 +462,7 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = case c.kind of attachedAsgn, attachedDeepCopy: + # XXX: replace these with assertions. if t.assignment == nil: return # protect from recursion body.add newHookCall(c, t.assignment, x, y) @@ -827,7 +828,10 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp; if typ.kind == tyDistinct: return produceSymDistinctType(g, c, typ, kind, info) - result = symPrototype(g, typ, typ.owner, kind, info) + result = typ.attachedOps[kind] + if result == nil: + result = symPrototype(g, typ, typ.owner, kind, info) + var a = TLiftCtx(info: info, g: g, kind: kind, c: c, asgnForType:typ) a.fn = result @@ -947,9 +951,18 @@ proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInf let lastAttached = if g.config.selectedGC == gcOrc: attachedDispose else: attachedSink + # bug #15122: We need to produce all prototypes before entering the + # mind boggling recursion. Hacks like these imply we shoule rewrite + # this module. + var generics: array[attachedDestructor..attachedDispose, bool] + for k in attachedDestructor..lastAttached: + generics[k] = canon.attachedOps[k] != nil + if not generics[k]: + canon.attachedOps[k] = symPrototype(g, canon, canon.owner, k, info) + # we generate the destructor first so that other operators can depend on it: for k in attachedDestructor..lastAttached: - if canon.attachedOps[k] == nil: + if not generics[k]: discard produceSym(g, c, canon, k, info) else: inst(canon.attachedOps[k], canon) diff --git a/tests/arc/theavy_recursion.nim b/tests/arc/theavy_recursion.nim new file mode 100644 index 000000000..9813331f4 --- /dev/null +++ b/tests/arc/theavy_recursion.nim @@ -0,0 +1,43 @@ +discard """ + output: "yay" + cmd: "nim c --gc:arc $file" +""" + +# bug #15122 + +import tables + +type + BENodeKind* = enum + tkEof, + tkBytes, + tkList, + tkDict + + BENode* = object + case kind: BENodeKind + of tkBytes: strVal: string + of tkList: listVal: seq[BENode] + of tkDict: dictVal*: Table[string, BENode] + else: + discard + +proc unused(s: string): BENode = + # bad: + result = BENode(kind: tkBytes, strVal: "abc") + +proc main = + var data = { + "examples": { + "values": BENode( + kind: tkList, + listVal: @[BENode(kind: tkBytes, strVal: "test")] + ) + }.toTable() + }.toTable() + + # For ARC listVal is empty for some reason + doAssert data["examples"]["values"].listVal[0].strVal == "test" + +main() +echo "yay" From c4e03b540e34a37c24794ade073bf708ab17b35d Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Fri, 11 Sep 2020 03:57:57 -0300 Subject: [PATCH 0595/3768] Fix #15183 (#15300) --- lib/js/jsre.nim | 23 +++++++++++++---------- tools/kochdocs.nim | 1 - 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/lib/js/jsre.nim b/lib/js/jsre.nim index b4f273d0b..ff80056ea 100644 --- a/lib/js/jsre.nim +++ b/lib/js/jsre.nim @@ -1,5 +1,18 @@ ## Regular Expressions for the JavaScript target. ## * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions +## +## Examples +## ======== +## +## .. code-block::nim +## let jsregex: RegExp = newRegExp(r"\s+", r"i") +## jsregex.compile(r"\w+", r"i") +## doAssert jsregex.test(r"nim javascript") +## doAssert jsregex.exec(r"nim javascript") == @["nim".cstring] +## doAssert jsregex.toString() == r"/\w+/i" +## jsregex.compile(r"[0-9]", r"i") +## doAssert jsregex.test(r"0123456789abcd") + when not defined(js) and not defined(Nimdoc): {.error: "This module only works on the JavaScript platform".} @@ -34,13 +47,3 @@ func test*(self: RegExp; pattern: cstring): bool {.importjs: "#.test(#)".} func toString*(self: RegExp): cstring {.importjs: "#.toString()".} ## Returns a string representing the RegExp object. - - -runnableExamples: - let jsregex: RegExp = newRegExp(r"\s+", r"i") - jsregex.compile(r"\w+", r"i") - doAssert jsregex.test(r"nim javascript") - doAssert jsregex.exec(r"nim javascript") == @["nim".cstring] - doAssert jsregex.toString() == r"/\w+/i" - jsregex.compile(r"[0-9]", r"i") - doAssert jsregex.test(r"0123456789abcd") diff --git a/tools/kochdocs.nim b/tools/kochdocs.nim index 453b1037b..ba2963cfd 100644 --- a/tools/kochdocs.nim +++ b/tools/kochdocs.nim @@ -161,7 +161,6 @@ lib/posix/posix_other_consts.nim lib/posix/posix_freertos_consts.nim lib/posix/posix_openbsd_amd64.nim lib/posix/posix_haiku.nim -lib/js/jsre.nim """.splitWhitespace() when (NimMajor, NimMinor) < (1, 1) or not declared(isRelativeTo): From 9e4920c06851307187e9897be91424dc6640f8d3 Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 11 Sep 2020 14:21:09 +0200 Subject: [PATCH 0596/3768] fixes #15021 --- lib/pure/collections/tables.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index dc21c0539..8d2680815 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -2298,7 +2298,7 @@ proc smallest*[A](t: CountTable[A]): tuple[key: A, val: int] = ## ## See also: ## * `largest proc<#largest,CountTable[A]>`_ - assert t.len > 0 + assert t.len > 0, "counttable is empty" var minIdx = -1 for h in 0 .. high(t.data): if t.data[h].val > 0 and (minIdx == -1 or t.data[minIdx].val > t.data[h].val): @@ -2311,7 +2311,7 @@ proc largest*[A](t: CountTable[A]): tuple[key: A, val: int] = ## ## See also: ## * `smallest proc<#smallest,CountTable[A]>`_ - assert t.len > 0 + assert t.len > 0, "counttable is empty" var maxIdx = 0 for h in 1 .. high(t.data): if t.data[maxIdx].val < t.data[h].val: maxIdx = h From f1f694dccb7ea0c7a6f10793dc05849da01970b7 Mon Sep 17 00:00:00 2001 From: genotrance Date: Fri, 11 Sep 2020 11:57:54 -0500 Subject: [PATCH 0597/3768] Bump nimble (#15304) --- koch.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/koch.nim b/koch.nim index d9bbb4176..90c1296fa 100644 --- a/koch.nim +++ b/koch.nim @@ -10,7 +10,7 @@ # const - NimbleStableCommit = "311e5a8cd2c53056bdec24c81393a46b41f96241" # master + NimbleStableCommit = "27d1063febe3055252103cacb11787ac83893416" # master FusionStableCommit = "319aac4d43b04113831b529f8003e82f4af6a4a5" when not defined(windows): From 5f08d39398d134607605cb910590aac8babc02ef Mon Sep 17 00:00:00 2001 From: narimiran Date: Sat, 12 Sep 2020 09:22:14 +0200 Subject: [PATCH 0598/3768] fix broken links in the documentation --- lib/system_overview.rst | 42 ++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/lib/system_overview.rst b/lib/system_overview.rst index f0945873c..9125c29ba 100644 --- a/lib/system_overview.rst +++ b/lib/system_overview.rst @@ -45,27 +45,27 @@ Proc Usage Seqs ---- -=========================================== ========================================== -Proc Usage -=========================================== ========================================== -`newSeq<#newSeq>`_ Create a new sequence of a given length -`newSeqOfCap<#newSeqOfCap,Natural>`_ Create a new sequence with zero length - and a given capacity -`setLen<#setLen,seq[T][T],Natural>`_ Set the length of a sequence -`len<#len,seq[T][T]>`_ Return the length of a sequence -`@<#@,array[IDX,T]>`_ Turn an array into a sequence -`add<#add,seq[T][T],T>`_ Add an item to the sequence -`insert<#insert,seq[T][T],T>`_ Insert an item at a specific position -`delete<#delete,seq[T][T],Natural>`_ Delete an item while preserving the - order of elements (`O(n)` operation) -`del<#del,seq[T][T],Natural>`_ `O(1)` removal, doesn't preserve the order -`pop<#pop,seq[T][T]>`_ Remove and return last item of a sequence -`x & y<#&,seq[T][T],seq[T][T]>`_ Concatenate two sequences -`x[a..b]<#[],openArray[T],HSlice[U,V]>`_ Slice of a sequence (both ends included) -`x[a..^b]<#[],openArray[T],HSlice[U,V]>`_ Slice of a sequence but `b` is a - reversed index (both ends included) -`x[a..\`_ Slice of a sequence (excluded upper bound) -=========================================== ========================================== +============================================== ========================================== +Proc Usage +============================================== ========================================== +`newSeq<#newSeq>`_ Create a new sequence of a given length +`newSeqOfCap<#newSeqOfCap,Natural>`_ Create a new sequence with zero length + and a given capacity +`setLen<#setLen,seq[T],Natural>`_ Set the length of a sequence +`len<#len,seq[T]>`_ Return the length of a sequence +`@<#@>`_ Turn an array into a sequence +`add<#add,seq[T],>`_ Add an item to the sequence +`insert<#insert,seq[T],>`_ Insert an item at a specific position +`delete<#delete,seq[T],Natural>`_ Delete an item while preserving the + order of elements (`O(n)` operation) +`del<#del,seq[T],Natural>`_ `O(1)` removal, doesn't preserve the order +`pop<#pop,seq[T]>`_ Remove and return last item of a sequence +`x & y<#&,seq[T],seq[T]>`_ Concatenate two sequences +`x[a .. b]<#[],openArray[T],HSlice[U,V]>`_ Slice of a sequence (both ends included) +`x[a .. ^b]<#[],openArray[T],HSlice[U,V]>`_ Slice of a sequence but `b` is a + reversed index (both ends included) +`x[a ..\< b]<#[],openArray[T],HSlice[U,V]>`_ Slice of a sequence (excluded upper bound) +============================================== ========================================== **See also:** * `sequtils module `_ for operations on container From a41b243fea007b9aec8da0e950aa3368777aab37 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sat, 12 Sep 2020 16:34:11 +0800 Subject: [PATCH 0599/3768] remove annoying messages when creating orderedTables (#15309) * nativesockets docs minor [backport: 1.2] * remove annoying messages --- lib/pure/collections/tables.nim | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index 8d2680815..c5a7fca56 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -1784,7 +1784,7 @@ proc newOrderedTable*[A, B](pairs: openArray[(A, B)]): OrderedTableRef[A, B] assert b == {'a': 5, 'b': 9}.newOrderedTable result = newOrderedTable[A, B](pairs.len) - for key, val in items(pairs): result.add(key, val) + for key, val in items(pairs): result[key] = val proc `[]`*[A, B](t: OrderedTableRef[A, B], key: A): var B = @@ -2967,37 +2967,37 @@ when isMainModule: block: #5482 var a = [("wrong?", "foo"), ("wrong?", "foo2")].newOrderedTable() var b = newOrderedTable[string, string](initialSize = 2) - b.add("wrong?", "foo") - b.add("wrong?", "foo2") + b["wrong?"] = "foo" + b["wrong?"] = "foo2" assert a == b block: #5482 var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable() var b = newOrderedTable[string, string](initialSize = 2) - b.add("wrong?", "foo") - b.add("wrong?", "foo2") + b["wrong?"] = "foo" + b["wrong?"] = "foo2" assert a == b block: #5487 var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable() var b = newOrderedTable[string, string]() # notice, default size! - b.add("wrong?", "foo") - b.add("wrong?", "foo2") + b["wrong?"] = "foo" + b["wrong?"] = "foo2" assert a == b block: #5487 var a = [("wrong?", "foo"), ("wrong?", "foo2")].newOrderedTable() var b = newOrderedTable[string, string]() # notice, default size! - b.add("wrong?", "foo") - b.add("wrong?", "foo2") + b["wrong?"] = "foo" + b["wrong?"] = "foo2" assert a == b block: var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable() var b = [("wrong?", "foo"), ("wrong?", "foo2")].newOrderedTable() var c = newOrderedTable[string, string]() # notice, default size! - c.add("wrong?", "foo") - c.add("wrong?", "foo2") + c["wrong?"] = "foo" + c["wrong?"] = "foo2" assert a == b assert a == c From 1881fc5812aeada551e32c0d81a3aa4f6fceb0bb Mon Sep 17 00:00:00 2001 From: Clyybber Date: Sat, 12 Sep 2020 13:39:46 +0200 Subject: [PATCH 0600/3768] Fix #15305 (#15311) * Fix #15305 * Fix test --- compiler/parser.nim | 3 +-- tests/parser/tifexprs.nim | 12 ++++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 tests/parser/tifexprs.nim diff --git a/compiler/parser.nim b/compiler/parser.nim index f256cee66..17dd092a4 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -2242,8 +2242,7 @@ proc parseStmt(p: var Parser): PNode = break p.hasProgress = false if p.tok.tokType in {tkElse, tkElif}: - parMessage(p, errInvalidIndentation) - getTok(p) + break # Allow this too, see tests/parser/tifexprs result.add complexOrSimpleStmt(p) if not p.hasProgress and p.tok.tokType == tkEof: break diff --git a/tests/parser/tifexprs.nim b/tests/parser/tifexprs.nim new file mode 100644 index 000000000..3a7e2bddc --- /dev/null +++ b/tests/parser/tifexprs.nim @@ -0,0 +1,12 @@ +discard """ + output: ''' +1 +''' +""" + +var a, b: int +let x = if a > b: + 0 + else: 1 + +echo x From 60ad95f79954fa870a124b5afc4ad5239fbebab1 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Sat, 12 Sep 2020 14:28:19 +0200 Subject: [PATCH 0601/3768] Add testcase for invalid if statement (#15313) --- tests/parser/tinvifstmt.nim | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 tests/parser/tinvifstmt.nim diff --git a/tests/parser/tinvifstmt.nim b/tests/parser/tinvifstmt.nim new file mode 100644 index 000000000..0455bce60 --- /dev/null +++ b/tests/parser/tinvifstmt.nim @@ -0,0 +1,12 @@ +discard """ + errormsg: "invalid indentation" + line: 9 + column: 3 +""" + +if true: + echo "a" + elif: + echo "b" + else: + echo "c" From 1df5cfba52ccc71e3c69b6cd7d08cebd70bbb187 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 12 Sep 2020 23:11:38 +0200 Subject: [PATCH 0602/3768] fixes #15147 (#15315) --- compiler/varpartitions.nim | 2 +- tests/arc/topt_no_cursor.nim | 21 +++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim index bbde68cbe..87424c4be 100644 --- a/compiler/varpartitions.nim +++ b/compiler/varpartitions.nim @@ -396,7 +396,7 @@ proc deps(c: var Partitions; dest, src: PNode) = (src.sym.kind in {skLet, skParam, skForVar} and hasDisabledAsgn(src.sym.typ))): c.s[vid].flags.incl preventCursor - if hasDestructor(src.typ): + if src.kind == nkSym and hasDestructor(src.typ): rhsIsSink(c, src) proc traverse(c: var Partitions; n: PNode) = diff --git a/tests/arc/topt_no_cursor.nim b/tests/arc/topt_no_cursor.nim index fe63e7585..8a33cb2ee 100644 --- a/tests/arc/topt_no_cursor.nim +++ b/tests/arc/topt_no_cursor.nim @@ -171,3 +171,24 @@ proc encodedQuery = elem.tt() encodedQuery() + +# bug #15147 + +proc s(input: string): (string, string) = + result = (";", "") + +proc charmatch(input: string): (string, string) = + result = ("123", input[0 .. input.high]) + +proc plus(input: string) = + var + lvalue, rvalue: string # cursors + lnext: string # must be cursor!!! + rnext: string # cursor + let lresult = charmatch(input) + (lvalue, lnext) = lresult + + let rresult = s(lnext) + (rvalue, rnext) = rresult + +plus("123;") From 6c49767a6c656fb097df71c41e4f123380bc1ca9 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Mon, 14 Sep 2020 00:57:49 -0600 Subject: [PATCH 0603/3768] Fixing issue #15302 -- lwip doesn't support signals (#15303) * Fixing issue #15302 -- lwip doesn't support signals * Adding test to catch issue #15302 -- lwip/freertos net library don't try to build / run on windows, it'll compile only but not run Fixing issue #15302 -- reworking test to compile on other platforms --- compiler/platform.nim | 2 +- lib/posix/posix_freertos_consts.nim | 36 +++++++++++++++++++---------- lib/posix/posix_other.nim | 4 ++++ lib/pure/net.nim | 4 ++-- tests/stdlib/tlwip.nim | 30 ++++++++++++++++++++++++ 5 files changed, 61 insertions(+), 15 deletions(-) create mode 100644 tests/stdlib/tlwip.nim diff --git a/compiler/platform.nim b/compiler/platform.nim index df79b2f8d..78c523c1a 100644 --- a/compiler/platform.nim +++ b/compiler/platform.nim @@ -179,7 +179,7 @@ const props: {ospNeedsPIC, ospPosix}), (name: "FreeRTOS", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/", objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/", - scriptExt: ".sh", curDir: ".", exeExt: ".elf", extSep: ".", + scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".", props: {ospPosix}), (name: "Any", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/", objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/", diff --git a/lib/posix/posix_freertos_consts.nim b/lib/posix/posix_freertos_consts.nim index 5e83fa0f5..efe87aacf 100644 --- a/lib/posix/posix_freertos_consts.nim +++ b/lib/posix/posix_freertos_consts.nim @@ -220,11 +220,11 @@ var IPV6_UNICAST_HOPS* {.importc: "IPV6_UNICAST_HOPS", header: "". var IPV6_V6ONLY* {.importc: "IPV6_V6ONLY", header: "".}: cint # -var TCP_NODELAY* {.importc: "TCP_NODELAY", header: "".}: cint -var TCP_KEEPALIVE* {.importc: "TCP_KEEPALIVE", header: "".}: cint -var TCP_KEEPIDLE* {.importc: "TCP_KEEPIDLE", header: "".}: cint -var TCP_KEEPINTVL* {.importc: "TCP_KEEPINTVL", header: "".}: cint -var TCP_KEEPCNT* {.importc: "TCP_KEEPCNT", header: "".}: cint +const TCP_NODELAY* = 0x01 # don't delay send to coalesce packets +const TCP_KEEPALIVE* = 0x02 # send KEEPALIVE probes when idle for pcb->keep_idle milliseconds +const TCP_KEEPIDLE* = 0x03 # set pcb->keep_idle - Same as TCP_KEEPALIVE, but use seconds for get/setsockopt +const TCP_KEEPINTVL* = 0x04 # set pcb->keep_intvl - Use seconds for get/setsockopt +const TCP_KEEPCNT* = 0x05 # set pcb->keep_cnt - Use number of probes sent for get/setsockopt # # var NL_SETD* {.importc: "NL_SETD", header: "".}: cint @@ -367,10 +367,25 @@ var SEM_FAILED* {.importc: "SEM_FAILED", header: "".}: pointer var FD_SETSIZE* {.importc: "FD_SETSIZE", header: "".}: cint # -var MSG_CTRUNC* {.importc: "MSG_CTRUNC", header: "".}: cint -var MSG_DONTROUTE* {.importc: "MSG_DONTROUTE", header: "".}: cint -var MSG_EOR* {.importc: "MSG_EOR", header: "".}: cint -var MSG_OOB* {.importc: "MSG_OOB", header: "".}: cint +# struct msghdr->msg_flags bit field values +const MSG_TRUNC* = 0x04 +const MSG_CTRUNC* = 0x08 + +# Flags we can use with send and recv. +const MSG_PEEK* = 0x01 # Peeks at an incoming message +const MSG_WAITALL* = 0x02 # Unimplemented: Requests that the function block until the full amount of data requested can be returned +const MSG_OOB* = 0x04 # Unimplemented: Requests out-of-band data. The significance and semantics of out-of-band data are protocol-specific +const MSG_DONTWAIT* = 0x08 # Nonblocking i/o for this operation only +const MSG_MORE* = 0x10 # Sender will send more +# const MSG_NOSIGNAL* = 0x20 # Uninmplemented: Requests not to send the SIGPIPE signal if an attempt to send is made on a stream-oriented socket that is no longer connected. + +# Alternately, they can defined like this, but the above seems better when they're known/stable values: +# var MSG_TRUNC* {.importc: "MSG_TRUNC", header: "".}: cint +# var MSG_CTRUNC* {.importc: "MSG_CTRUNC", header: "".}: cint +# var MSG_DONTROUTE* {.importc: "MSG_DONTROUTE", header: "".}: cint # not defined in lwip +# var MSG_EOR* {.importc: "MSG_EOR", header: "".}: cint # not defined in lwip +# var MSG_OOB* {.importc: "MSG_OOB", header: "".}: cint + # var SCM_RIGHTS* {.importc: "SCM_RIGHTS", header: "".}: cint var SO_ACCEPTCONN* {.importc: "SO_ACCEPTCONN", header: "".}: cint @@ -401,9 +416,6 @@ var SOL_SOCKET* {.importc: "SOL_SOCKET", header: "".}: cint const SocketMaxConnections {.intdefine.}: int = 32 var SOMAXCONN*: cint = SocketMaxConnections.cint -var MSG_PEEK* {.importc: "MSG_PEEK", header: "".}: cint -var MSG_TRUNC* {.importc: "MSG_TRUNC", header: "".}: cint -var MSG_WAITALL* {.importc: "MSG_WAITALL", header: "".}: cint var AF_INET* {.importc: "AF_INET", header: "".}: cint var AF_INET6* {.importc: "AF_INET6", header: "".}: cint # var AF_UNIX* {.importc: "AF_UNIX", header: "".}: cint diff --git a/lib/posix/posix_other.nim b/lib/posix/posix_other.nim index 1ad05060d..a5eb32d22 100644 --- a/lib/posix/posix_other.nim +++ b/lib/posix/posix_other.nim @@ -625,6 +625,10 @@ elif defined(solaris): # Solaris doesn't have MSG_NOSIGNAL const MSG_NOSIGNAL* = 0'i32 +elif defined(freertos) or defined(lwip): + # LwIP/FreeRTOS doesn't have MSG_NOSIGNAL + const + MSG_NOSIGNAL* = 0x20'i32 else: var MSG_NOSIGNAL* {.importc, header: "".}: cint diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 5d2ec8e30..f30b893a7 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -1061,7 +1061,7 @@ proc accept*(server: Socket, client: var owned(Socket), var addrDummy = "" acceptAddr(server, client, addrDummy, flags) -when defined(posix): +when defined(posix) and not defined(lwip): from posix import Sigset, sigwait, sigismember, sigemptyset, sigaddset, sigprocmask, pthread_sigmask, SIGPIPE, SIG_BLOCK, SIG_UNBLOCK @@ -1079,7 +1079,7 @@ template blockSigpipe(body: untyped): untyped = ## ## For convenience, this template is also available for non-POSIX system, ## where `body` will be executed as-is. - when not defined(posix): + when not defined(posix) or defined(lwip): body else: template sigmask(how: cint, set, oset: var Sigset): untyped {.gensym.} = diff --git a/tests/stdlib/tlwip.nim b/tests/stdlib/tlwip.nim new file mode 100644 index 000000000..7f7822236 --- /dev/null +++ b/tests/stdlib/tlwip.nim @@ -0,0 +1,30 @@ +discard """ + targets: "c" + cmd: "nim $target --os:freertos --gc:arc $options $file" + disabled: "bsd" + disabled: "windows" + action: compile +""" + +# Note: +# This file tests FreeRTOS/LwIP cross-compilation on UNIX platforms +# Windows should run when compiled with esp-idf, however I'm not +# sure how to test for only compilation on Windows without running +# a test exe +# +# Note: +# disabling *BSDs since they're not playing well with `gcc` + +import net +import asynchttpserver, asyncdispatch + +proc cb*(req: Request) {.async.} = + await req.respond(Http200, "Hello World") + +proc run_http_server*() {.exportc.} = + echo "starting http server" + var server = newAsyncHttpServer() + + waitFor server.serve(Port(8181), cb) + +echo("ok") From a667475aaf83f3228fe76ffd25c296ca1847146b Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Mon, 14 Sep 2020 03:58:21 -0300 Subject: [PATCH 0604/3768] dom.Navigator add missing attributes (#15310) * Add missing attributes and methods to dom.Navigator * Add missing attributes and methods to dom.Navigator * Add missing attributes and methods to dom.Navigator --- changelog.md | 3 ++- lib/js/dom.nim | 17 ++++++++++++++++- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index 3333a837b..e7afaa229 100644 --- a/changelog.md +++ b/changelog.md @@ -162,7 +162,7 @@ - Added `deques.toDeque`, which creates a deque from an openArray. The usage is similar to procs such as `sets.toHashSet` and `tables.toTable`. Previously, it was necessary to create an empty deque and add items manually. - + - `std/with`, `sugar.dup` now support object field assignment expression: ```nim import std/with @@ -185,6 +185,7 @@ - Add `getprotobyname` to `winlean`. Add `getProtoByname` to `nativesockets` which returns a protocol code from the database that matches the protocol `name`. +- Add missing attributes and methods to `dom.Navigator` like `deviceMemory`, `onLine`, `vibrate()`, etc. ## Language changes diff --git a/lib/js/dom.nim b/lib/js/dom.nim index 84ceb73f3..a74c63231 100644 --- a/lib/js/dom.nim +++ b/lib/js/dom.nim @@ -1245,10 +1245,19 @@ type appCodeName*: cstring appName*: cstring appVersion*: cstring + buildID*: cstring ## https://developer.mozilla.org/en-US/docs/Web/API/Navigator/buildID cookieEnabled*: bool + deviceMemory*: float ## https://developer.mozilla.org/en-US/docs/Web/API/Navigator/deviceMemory + doNotTrack*: cstring ## https://developer.mozilla.org/en-US/docs/Web/API/Navigator/doNotTrack language*: cstring + languages*: seq[cstring] ## https://developer.mozilla.org/en-US/docs/Web/API/NavigatorLanguage/languages + maxTouchPoints*: cint ## https://developer.mozilla.org/en-US/docs/Web/API/Navigator/maxTouchPoints + onLine*: bool ## https://developer.mozilla.org/en-US/docs/Web/API/NavigatorOnLine/onLine + oscpu*: cstring ## https://developer.mozilla.org/en-US/docs/Web/API/Navigator/oscpu platform*: cstring userAgent*: cstring + vendor*: cstring ## https://developer.mozilla.org/en-US/docs/Web/API/Navigator/vendor + webdriver*: bool ## https://developer.mozilla.org/en-US/docs/Web/API/Navigator/webdriver mimeTypes*: seq[ref MimeType] Plugin* {.importc.} = object of RootObj @@ -1328,7 +1337,7 @@ since (1, 3): composed*: bool DocumentOrShadowRoot* {.importc.} = object of RootObj activeElement*: Element - # styleSheets*: StyleSheetList + # styleSheets*: StyleSheetList ShadowRoot* = ref ShadowRootObj ShadowRootObj {.importc.} = object of DocumentOrShadowRoot delegatesFocus*: bool @@ -1581,6 +1590,12 @@ proc pushState*[T](h: History, stateObject: T, title, url: cstring) # Navigator "methods" proc javaEnabled*(h: Navigator): bool +since (1, 3): + proc canShare*(self: Navigator; data: cstring): bool ## https://developer.mozilla.org/en-US/docs/Web/API/Navigator/canShare + proc sendBeacon*(self: Navigator; url, data: cstring): bool ## https://developer.mozilla.org/en-US/docs/Web/API/Navigator/sendBeacon + proc vibrate*(self: Navigator; pattern: cint): bool ## https://developer.mozilla.org/en-US/docs/Web/API/Navigator/vibrate + proc vibrate*(self: Navigator; pattern: openArray[cint]): bool ## https://developer.mozilla.org/en-US/docs/Web/API/Navigator/vibrate + proc registerProtocolHandler*(self: Navigator; scheme, url, title: cstring) ## https://developer.mozilla.org/en-US/docs/Web/API/Navigator/registerProtocolHandler # ClassList "methods" proc add*(c: ClassList, class: cstring) From 366216da6455fc882da44bac84b1887e6e1a267e Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 14 Sep 2020 15:00:29 +0800 Subject: [PATCH 0605/3768] fix cookie with comma (#15319) --- lib/pure/httpcore.nim | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/pure/httpcore.nim b/lib/pure/httpcore.nim index 9c51887cb..81e02a1f8 100644 --- a/lib/pure/httpcore.nim +++ b/lib/pure/httpcore.nim @@ -229,7 +229,11 @@ proc parseHeader*(line: string): tuple[key: string, value: seq[string]] = i = line.parseUntil(result.key, ':') inc(i) # skip : if i < len(line): - i += parseList(line, result.value, i) + if cmpIgnoreCase(result.key, "cookie") == 0: + i += line.skipWhitespace(i) + result.value.add line.substr(i) + else: + i += parseList(line, result.value, i) elif result.key.len > 0: result.value = @[""] else: From e111b610caa26b6577f99423d360ff805e0b0ecf Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 14 Sep 2020 18:20:42 +0800 Subject: [PATCH 0606/3768] test cookies with comma for #15319 (#15322) * test cookies with comma for #15319 * space --- tests/stdlib/thttpcore.nim | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/stdlib/thttpcore.nim b/tests/stdlib/thttpcore.nim index f96fcc3b8..33c24453e 100644 --- a/tests/stdlib/thttpcore.nim +++ b/tests/stdlib/thttpcore.nim @@ -34,3 +34,20 @@ suite "httpcore": let h1 = newHttpHeaders({"a": "1", "a": "2", "A": "3"}) assert seq[string](h1["a"]).join(",") == "1,2,3" + + test "test cookies with comma": + doAssert parseHeader("cookie: foo, bar") == ("cookie", @["foo, bar"]) + doAssert parseHeader("cookie: foo, bar, prologue") == ("cookie", @["foo, bar, prologue"]) + doAssert parseHeader("cookie: foo, bar, prologue, starlight") == ("cookie", @["foo, bar, prologue, starlight"]) + + doAssert parseHeader("cookie: foo, bar") == ("cookie", @["foo, bar"]) + doAssert parseHeader("cookie: foo, bar, prologue") == ("cookie", @["foo, bar, prologue"]) + doAssert parseHeader("cookie: foo, bar, prologue, starlight") == ("cookie", @["foo, bar, prologue, starlight"]) + + doAssert parseHeader("Cookie: foo, bar") == (key: "Cookie", value: @["foo, bar"]) + doAssert parseHeader("Cookie: foo, bar, prologue") == (key: "Cookie", value: @["foo, bar, prologue"]) + doAssert parseHeader("Cookie: foo, bar, prologue, starlight") == (key: "Cookie", value: @["foo, bar, prologue, starlight"]) + + doAssert parseHeader("Accept: foo, bar") == (key: "Accept", value: @["foo", "bar"]) + doAssert parseHeader("Accept: foo, bar, prologue") == (key: "Accept", value: @["foo", "bar", "prologue"]) + doAssert parseHeader("Accept: foo, bar, prologue, starlight") == (key: "Accept", value: @["foo", "bar", "prologue", "starlight"]) From c38487aa225674f67659c28ce171fd93e2231ad1 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 14 Sep 2020 12:50:23 +0200 Subject: [PATCH 0607/3768] fixes a critical ORC bug, refs #15076 (#15323) --- lib/system/orc.nim | 64 ++++++++++++++++++---------------------- tests/arc/tasyncleak.nim | 2 +- 2 files changed, 30 insertions(+), 36 deletions(-) diff --git a/lib/system/orc.nim b/lib/system/orc.nim index b9c820e4a..7c4cf9d98 100644 --- a/lib/system/orc.nim +++ b/lib/system/orc.nim @@ -50,6 +50,7 @@ type traceStack: CellSeq when useJumpStack: jumpStack: CellSeq # Lins' jump stack in order to speed up traversals + toFree: CellSeq freed, touched: int proc trace(s: Cell; desc: PNimType; j: var GcEnv) {.inline.} = @@ -207,6 +208,11 @@ proc scan(s: Cell; desc: PNimType; j: var GcEnv) = t.setColor(colWhite) trace(t, desc, j) +when false: + proc writeCell(msg: cstring; s: Cell) = + cfprintf(cstderr, "%s %p root index: %ld; RC: %ld; color: %ld\n", + msg, s, s.rootIdx, s.rc shr rcShift, s.color) + proc collectWhite(s: Cell; desc: PNimType; j: var GcEnv) = #[ proc collectWhite(s: Cell) = @@ -218,37 +224,14 @@ proc collectWhite(s: Cell; desc: PNimType; j: var GcEnv) = ]# if s.color == colWhite and (s.rc and isCycleCandidate) == 0: s.setColor(colBlack) - when false: - # optimized version (does not work) - j.traceStack.add(s, desc) - # this way of writing the loop means we can free all the nodes - # afterwards avoiding the "use after free" bug in the paper. - var i = 0 - while i < j.traceStack.len: - let (t, desc) = j.traceStack.d[j.traceStack.len-1] - inc i - if t.color == colWhite and (t.rc and isCycleCandidate) == 0: - t.setColor(colBlack) - trace(t, desc, j) - - for i in 0 ..< j.traceStack.len: - free(j.traceStack.d[i][0], j.traceStack.d[i][1]) - j.traceStack.len = 0 - else: - var subgraph: CellSeq - init subgraph - subgraph.add(s, desc) - trace(s, desc, j) - while j.traceStack.len > 0: - let (t, desc) = j.traceStack.pop() - if t.color == colWhite and (t.rc and isCycleCandidate) == 0: - subgraph.add(t, desc) - t.setColor(colBlack) - trace(t, desc, j) - for i in 0 ..< subgraph.len: - free(subgraph.d[i][0], subgraph.d[i][1]) - inc j.freed, subgraph.len - deinit subgraph + j.toFree.add(s, desc) + trace(s, desc, j) + while j.traceStack.len > 0: + let (t, desc) = j.traceStack.pop() + if t.color == colWhite and (t.rc and isCycleCandidate) == 0: + j.toFree.add(t, desc) + t.setColor(colBlack) + trace(t, desc, j) proc collectCyclesBacon(j: var GcEnv) = # pretty direct translation from @@ -269,10 +252,17 @@ proc collectCyclesBacon(j: var GcEnv) = for i in 0 ..< roots.len: scan(roots.d[i][0], roots.d[i][1], j) + init j.toFree for i in 0 ..< roots.len: let s = roots.d[i][0] s.rc = s.rc and not isCycleCandidate collectWhite(s, roots.d[i][1], j) + + for i in 0 ..< j.toFree.len: + free(j.toFree.d[i][0], j.toFree.d[i][1]) + + inc j.freed, j.toFree.len + deinit j.toFree #roots.len = 0 const @@ -283,6 +273,9 @@ var proc collectCycles() = ## Collect cycles. + when false: + cfprintf(cstderr, "[collectCycles] begin\n") + var j: GcEnv init j.traceStack when useJumpStack: @@ -304,17 +297,18 @@ proc collectCycles() = # we touched. If we're effective, we can reset the threshold: if j.freed * 2 >= j.touched: rootsThreshold = defaultThreshold - else: + elif rootsThreshold < high(int) div 4: rootsThreshold = rootsThreshold * 3 div 2 when false: - cprintf("[collectCycles] freed %ld new threshold %ld\n", j.freed, rootsThreshold) + cfprintf(cstderr, "[collectCycles] freed %ld new threshold %ld\n", j.freed, rootsThreshold) proc registerCycle(s: Cell; desc: PNimType) = + if roots.len >= rootsThreshold: + collectCycles() if roots.d == nil: init(roots) s.rootIdx = roots.len add(roots, s, desc) - if roots.len >= rootsThreshold: - collectCycles() + #writeCell("[added root]", s) proc GC_fullCollect* = ## Forces a full garbage collection pass. With ``--gc:orc`` triggers the cycle diff --git a/tests/arc/tasyncleak.nim b/tests/arc/tasyncleak.nim index 16e7446a7..71d62222d 100644 --- a/tests/arc/tasyncleak.nim +++ b/tests/arc/tasyncleak.nim @@ -1,5 +1,5 @@ discard """ - outputsub: "(allocCount: 6013, deallocCount: 6007)" + outputsub: "(allocCount: 4016, deallocCount: 4010)" cmd: "nim c --gc:orc -d:nimAllocStats $file" """ From 073bea66064afd72cd5e0ffc65569a1833dce385 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Tue, 15 Sep 2020 06:59:33 -0300 Subject: [PATCH 0608/3768] Remove Deprecated {.this:self.} from Documentation so people dont use it anymore (#15328) --- doc/manual_experimental.rst | 36 ------------------------------------ 1 file changed, 36 deletions(-) diff --git a/doc/manual_experimental.rst b/doc/manual_experimental.rst index ac8dedf11..a96c121a2 100644 --- a/doc/manual_experimental.rst +++ b/doc/manual_experimental.rst @@ -321,42 +321,6 @@ scope. Therefore, the following will *fail to compile:* a() -Automatic self insertions -========================= - -**Note**: The ``.this`` pragma is deprecated and should not be used anymore. - -Starting with version 0.14 of the language, Nim supports ``field`` as a -shortcut for ``self.field`` comparable to the `this`:idx: keyword in Java -or C++. This feature has to be explicitly enabled via a ``{.this: self.}`` -statement pragma (instead of ``self`` any other identifier can be used too). -This pragma is active for the rest of the module: - -.. code-block:: nim - type - Parent = object of RootObj - parentField: int - Child = object of Parent - childField: int - - {.this: self.} - proc sumFields(self: Child): int = - result = parentField + childField - # is rewritten to: - # result = self.parentField + self.childField - -In addition to fields, routine applications are also rewritten, but only -if no other interpretation of the call is possible: - -.. code-block:: nim - proc test(self: Child) = - echo childField, " ", sumFields() - # is rewritten to: - echo self.childField, " ", sumFields(self) - # but NOT rewritten to: - echo self, self.childField, " ", sumFields(self) - - Named argument overloading ========================== From bc179ccc3f3c0df8b0c725b477c7cfeefaef4594 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 15 Sep 2020 12:40:23 +0200 Subject: [PATCH 0609/3768] fixes #15076 (#15329) --- compiler/ccgtypes.nim | 6 +-- compiler/liftdestructors.nim | 1 + compiler/lowerings.nim | 2 +- lib/system/orc.nim | 23 ++++++++-- tests/arc/tasyncleak2.nim | 88 ++++++++++++++++++++++++++++++++++++ 5 files changed, 111 insertions(+), 9 deletions(-) create mode 100644 tests/arc/tasyncleak2.nim diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 5b5720934..aa92b8534 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -1319,9 +1319,9 @@ proc genHook(m: BModule; t: PType; info: TLineInfo; op: TTypeAttachedOp): Rope = genProc(m, theProc) result = theProc.loc.r else: - if op == attachedTrace and m.config.selectedGC == gcOrc and - containsGarbageCollectedRef(t): - when false: + when false: + if op == attachedTrace and m.config.selectedGC == gcOrc and + containsGarbageCollectedRef(t): # unfortunately this check is wrong for an object type that only contains # .cursor fields like 'Node' inside 'cycleleak'. internalError(m.config, info, "no attached trace proc found") diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index b3300850e..3d0635761 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -448,6 +448,7 @@ proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = # follow all elements: forallElements(c, t, body, x, y) of attachedDispose: + forallElements(c, t, body, x, y) body.add genBuiltin(c.g, mDestroy, "destroy", x) proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index f6ca98196..2d96f8a60 100644 --- a/compiler/lowerings.nim +++ b/compiler/lowerings.nim @@ -158,7 +158,7 @@ proc createObj*(g: ModuleGraph; owner: PSym, info: TLineInfo; final=true): PType else: rawAddSon(result, getCompilerProc(g, "RootObj").typ) result.n = newNodeI(nkRecList, info) - let s = newSym(skType, getIdent(g.cache, "Env_" & toFilename(g.config, info)), + let s = newSym(skType, getIdent(g.cache, "Env_" & toFilename(g.config, info) & "_" & $owner.name.s), owner, info, owner.options) incl s.flags, sfAnon s.typ = result diff --git a/lib/system/orc.nim b/lib/system/orc.nim index 7c4cf9d98..7500ba374 100644 --- a/lib/system/orc.nim +++ b/lib/system/orc.nim @@ -40,7 +40,7 @@ template setColor(c, col) = proc nimIncRefCyclic(p: pointer) {.compilerRtl, inl.} = let h = head(p) inc h.rc, rcIncrement - h.setColor colPurple # mark as potential cycle! + #h.setColor colPurple # mark as potential cycle! const useJumpStack = false # for thavlak the jump stack doesn't improve the performance at all @@ -74,6 +74,19 @@ proc free(s: Cell; desc: PNimType) {.inline.} = if desc.disposeImpl != nil: cast[DisposeProc](desc.disposeImpl)(p) + + when false: + cstderr.rawWrite desc.name + cstderr.rawWrite " " + if desc.disposeImpl == nil: + cstderr.rawWrite "lacks dispose" + if desc.traceImpl != nil: + cstderr.rawWrite ", but has trace\n" + else: + cstderr.rawWrite ", and lacks trace\n" + else: + cstderr.rawWrite "has dispose!\n" + nimRawDispose(p) proc nimTraceRef(q: pointer; desc: PNimType; env: pointer) {.compilerRtl.} = @@ -342,8 +355,8 @@ proc nimDecRefIsLastCyclicDyn(p: pointer): bool {.compilerRtl, inl.} = #cprintf("[DESTROY] %p\n", p) else: dec cell.rc, rcIncrement - if cell.color == colPurple: - rememberCycle(result, cell, cast[ptr PNimType](p)[]) + #if cell.color == colPurple: + rememberCycle(result, cell, cast[ptr PNimType](p)[]) proc nimDecRefIsLastCyclicStatic(p: pointer; desc: PNimType): bool {.compilerRtl, inl.} = if p != nil: @@ -353,5 +366,5 @@ proc nimDecRefIsLastCyclicStatic(p: pointer; desc: PNimType): bool {.compilerRtl #cprintf("[DESTROY] %p %s\n", p, desc.name) else: dec cell.rc, rcIncrement - if cell.color == colPurple: - rememberCycle(result, cell, desc) + #if cell.color == colPurple: + rememberCycle(result, cell, desc) diff --git a/tests/arc/tasyncleak2.nim b/tests/arc/tasyncleak2.nim new file mode 100644 index 000000000..4d8486b3b --- /dev/null +++ b/tests/arc/tasyncleak2.nim @@ -0,0 +1,88 @@ +discard """ + output: "success" + cmd: "nim c --gc:orc $file" +""" + +# issue #15076 +import deques, strutils, asyncdispatch + +proc doNothing(): Future[void] = + #[ + var + :env + :env_1 + try: + `=destroy`(:env) + internalNew(:env) + `=sink`(:env.retFuture1, newFuture("doNothing")) + + `=destroy_1`(:env_1) + internalNew(:env_1) + `=`(:env_1.:up, :env) + `=sink_1`(:env.nameIterVar2, (doNothingIter, :env_1)) + + (doNothingNimAsyncContinue, :env)() + return `=_1`(result, :env.retFuture1) + finally: + `=destroy`(:env) + ]# + + var retFuture = newFuture[void]("doNothing") + iterator doNothingIter(): FutureBase {.closure.} = + # inspected ARC code: looks correct! + block: + var qqq = initDeque[string]() + for i in 0 .. 1000: + qqq.addLast($i) + complete(retFuture) # env.up.retFuture1 + + var nameIterVar = doNothingIter # iter_Env -> retFuture -> + + proc doNothingNimAsyncContinue() {.closure.} = + # inspected ARC code: looks correct + if not nameIterVar.finished: + var next_gensym0 = nameIterVar() + while (not next_gensym0.isNil) and next_gensym0.finished: + next_gensym0 = nameIterVar() + if nameIterVar.finished: + break + if next_gensym0 != nil: + {.gcsafe.}: + next_gensym0.addCallback cast[proc () {.closure, gcsafe.}](doNothingNimAsyncContinue) + + doNothingNimAsyncContinue() + return retFuture + +proc main(): Future[void] = + template await[T](f_gensym12: Future[T]): auto {.used.} = + var internalTmpFuture_gensym12: FutureBase = f_gensym12 + yield internalTmpFuture_gensym12 + (cast[type(f_gensym12)](internalTmpFuture_gensym12)).read() + + var retFuture = newFuture[void]("main") + iterator mainIter(): FutureBase {.closure.} = + block: + for x in 0 .. 1000: + await doNothing() + complete(retFuture) + + var nameIterVar_gensym11 = mainIter + proc mainNimAsyncContinue() {.closure.} = + if not nameIterVar_gensym11.finished: + var next_gensym11 = unown nameIterVar_gensym11() + while (not next_gensym11.isNil) and next_gensym11.finished: + next_gensym11 = unown nameIterVar_gensym11() + if nameIterVar_gensym11.finished: + break + if next_gensym11 != nil: + {.gcsafe.}: + next_gensym11.addCallback cast[proc () {.closure, gcsafe.}](mainNimAsyncContinue) + + mainNimAsyncContinue() + return retFuture + +for i in 0..9: + waitFor main() + GC_fullCollect() + assert getOccupiedMem() < 1024 +echo "success" From 0aaf4e2dfa2d06a71202bd283e99bb017c781f2c Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Wed, 16 Sep 2020 20:51:01 +0800 Subject: [PATCH 0610/3768] Methods docs improvement (#15338) * docs improvement * minor --- doc/manual.rst | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/doc/manual.rst b/doc/manual.rst index e72554603..ada6261b9 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -3816,13 +3816,10 @@ Overloading of the subscript operator The ``[]`` subscript operator for arrays/openarrays/sequences can be overloaded. -Multi-methods +Methods ============= -**Note:** Starting from Nim 0.20, to use multi-methods one must explicitly pass -``--multimethods:on`` when compiling. - -Procedures always use static dispatch. Multi-methods use dynamic +Procedures always use static dispatch. Methods use dynamic dispatch. For dynamic dispatch to work on an object it should be a reference type. @@ -3869,6 +3866,35 @@ the effects that a call to ``m`` might cause. **Note**: Starting from Nim 0.20, generic methods are deprecated. +Multi-methods +-------------- + +**Note:** Starting from Nim 0.20, to use multi-methods one must explicitly pass +``--multimethods:on`` when compiling. + +In a multi-method all parameters that have an object type are used for the dispatching: + +.. code-block:: nim + :test: "nim c --multiMethods:on $1" + + type + Thing = ref object of RootObj + Unit = ref object of Thing + x: int + + method collide(a, b: Thing) {.inline.} = + quit "to override!" + + method collide(a: Thing, b: Unit) {.inline.} = + echo "1" + + method collide(a: Unit, b: Thing) {.inline.} = + echo "2" + + var a, b: Unit + new a + new b + collide(a, b) # output: 2 Inhibit dynamic method resolution via procCall ----------------------------------------------- From fd31e8ff6f96527cc52125d01311d022ff82fead Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 16 Sep 2020 14:57:01 +0200 Subject: [PATCH 0611/3768] allow old styled RTTI for arc/orc (#15331) --- compiler/ast.nim | 2 +- compiler/ccgexprs.nim | 83 ++++++++++------- compiler/ccgstmts.nim | 8 +- compiler/ccgtrav.nim | 2 +- compiler/ccgtypes.nim | 164 ++++++++++++++++++++------------- compiler/cgen.nim | 9 +- compiler/cgendata.nim | 2 + compiler/liftdestructors.nim | 4 +- compiler/options.nim | 5 +- lib/core/typeinfo.nim | 167 +++++++++++++++++++++++----------- lib/pure/marshal.nim | 11 +-- lib/system.nim | 57 ++++++------ lib/system/arc.nim | 22 +++-- lib/system/assign.nim | 136 ++++++++++++++++++--------- lib/system/cellseqs_v2.nim | 6 +- lib/system/cyclebreaker.nim | 14 +-- lib/system/deepcopy.nim | 52 ++++++----- lib/system/hti.nim | 2 + lib/system/mm/malloc.nim | 6 +- lib/system/orc.nim | 24 ++--- lib/system/seqs_v2.nim | 16 ++-- lib/system/seqs_v2_reimpl.nim | 17 ++++ tests/stdlib/tmarshal.nim | 6 +- 23 files changed, 512 insertions(+), 303 deletions(-) create mode 100644 lib/system/seqs_v2_reimpl.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index 8baf49265..4d37b284c 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -680,7 +680,7 @@ type mNBindSym, mNCallSite, mEqIdent, mEqNimrodNode, mSameNodeType, mGetImpl, mNGenSym, mNHint, mNWarning, mNError, - mInstantiationInfo, mGetTypeInfo, + mInstantiationInfo, mGetTypeInfo, mGetTypeInfoV2, mNimvm, mIntDefine, mStrDefine, mBoolDefine, mRunnableExamples, mException, mBuiltinType, mSymOwner, mUncheckedArray, mGetImplTransf, mSymIsInstantiationOf, mNodeId diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 8f45452bf..0d235f62e 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -266,10 +266,10 @@ proc genGenericAsgn(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = [addrLoc(p.config, dest), addrLoc(p.config, src), rdLoc(dest)]) else: linefmt(p, cpsStmts, "#genericShallowAssign((void*)$1, (void*)$2, $3);$n", - [addrLoc(p.config, dest), addrLoc(p.config, src), genTypeInfo(p.module, dest.t, dest.lode.info)]) + [addrLoc(p.config, dest), addrLoc(p.config, src), genTypeInfoV1(p.module, dest.t, dest.lode.info)]) else: linefmt(p, cpsStmts, "#genericAssign((void*)$1, (void*)$2, $3);$n", - [addrLoc(p.config, dest), addrLoc(p.config, src), genTypeInfo(p.module, dest.t, dest.lode.info)]) + [addrLoc(p.config, dest), addrLoc(p.config, src), genTypeInfoV1(p.module, dest.t, dest.lode.info)]) proc genOpenArrayConv(p: BProc; d: TLoc; a: TLoc) = assert d.k != locNone @@ -318,7 +318,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = else: linefmt(p, cpsStmts, "#genericSeqAssign($1, $2, $3);$n", [addrLoc(p.config, dest), rdLoc(src), - genTypeInfo(p.module, dest.t, dest.lode.info)]) + genTypeInfoV1(p.module, dest.t, dest.lode.info)]) of tyString: if optSeqDestructors in p.config.globalOptions: genGenericAsgn(p, dest, src, flags) @@ -383,7 +383,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = linefmt(p, cpsStmts, # XXX: is this correct for arrays? "#genericAssignOpenArray((void*)$1, (void*)$2, $1Len_0, $3);$n", [addrLoc(p.config, dest), addrLoc(p.config, src), - genTypeInfo(p.module, dest.t, dest.lode.info)]) + genTypeInfoV1(p.module, dest.t, dest.lode.info)]) else: linefmt(p, cpsStmts, # bug #4799, keep the nimCopyMem for a while @@ -425,16 +425,21 @@ proc genDeepCopy(p: BProc; dest, src: TLoc) = # XXX optimize this linefmt(p, cpsStmts, "#genericDeepCopy((void*)$1, (void*)$2, $3);$n", [addrLoc(p.config, dest), addrLocOrTemp(src), - genTypeInfo(p.module, dest.t, dest.lode.info)]) + genTypeInfoV1(p.module, dest.t, dest.lode.info)]) of tySequence, tyString: - linefmt(p, cpsStmts, "#genericSeqDeepCopy($1, $2, $3);$n", - [addrLoc(p.config, dest), rdLoc(src), - genTypeInfo(p.module, dest.t, dest.lode.info)]) + if optTinyRtti in p.config.globalOptions: + linefmt(p, cpsStmts, "#genericDeepCopy((void*)$1, (void*)$2, $3);$n", + [addrLoc(p.config, dest), addrLocOrTemp(src), + genTypeInfoV1(p.module, dest.t, dest.lode.info)]) + else: + linefmt(p, cpsStmts, "#genericSeqDeepCopy($1, $2, $3);$n", + [addrLoc(p.config, dest), rdLoc(src), + genTypeInfoV1(p.module, dest.t, dest.lode.info)]) of tyOpenArray, tyVarargs: linefmt(p, cpsStmts, "#genericDeepCopyOpenArray((void*)$1, (void*)$2, $1Len_0, $3);$n", [addrLoc(p.config, dest), addrLocOrTemp(src), - genTypeInfo(p.module, dest.t, dest.lode.info)]) + genTypeInfoV1(p.module, dest.t, dest.lode.info)]) of tySet: if mapSetType(p.config, ty) == ctArray: linefmt(p, cpsStmts, "#nimCopyMem((void*)$1, (NIM_CONST void*)$2, $3);$n", @@ -1229,12 +1234,12 @@ proc genSeqElemAppend(p: BProc, e: PNode, d: var TLoc) = const seqAppendPattern = "($2) #incrSeqV3((TGenericSeq*)($1), $3)" call.r = ropecg(p.module, seqAppendPattern, [rdLoc(a), getTypeDesc(p.module, e[1].typ), - genTypeInfo(p.module, seqType, e.info)]) + genTypeInfoV1(p.module, seqType, e.info)]) else: const seqAppendPattern = "($2) #incrSeqV3($1, $3)" call.r = ropecg(p.module, seqAppendPattern, [rdLoc(a), getTypeDesc(p.module, e[1].typ), - genTypeInfo(p.module, seqType, e.info)]) + genTypeInfoV1(p.module, seqType, e.info)]) # emit the write barrier if required, but we can always move here, so # use 'genRefAssign' for the seq. genRefAssign(p, a, call) @@ -1254,7 +1259,7 @@ proc genReset(p: BProc, n: PNode) = when false: linefmt(p, cpsStmts, "#genericReset((void*)$1, $2);$n", [addrLoc(p.config, a), - genTypeInfo(p.module, skipTypes(a.t, {tyVar}), n.info)]) + genTypeInfoV1(p.module, skipTypes(a.t, {tyVar}), n.info)]) proc genDefault(p: BProc; n: PNode; d: var TLoc) = if d.k == locNone: getTemp(p, n.typ, d, needsInit=true) @@ -1280,7 +1285,7 @@ proc rawGenNew(p: BProc, a: var TLoc, sizeExpr: Rope; needsInit: bool) = [getTypeDesc(p.module, typ), sizeExpr]) genAssignment(p, a, b, {}) else: - let ti = genTypeInfo(p.module, typ, a.lode.info) + let ti = genTypeInfoV1(p.module, typ, a.lode.info) if bt.destructor != nil and not isTrivialProc(bt.destructor): # the prototype of a destructor is ``=destroy(x: var T)`` and that of a # finalizer is: ``proc (x: ref T) {.nimcall.}``. We need to check the calling @@ -1339,18 +1344,18 @@ proc genNewSeqAux(p: BProc, dest: TLoc, length: Rope; lenIsZero: bool) = if p.config.selectedGC == gcGo: # we need the write barrier call.r = ropecg(p.module, "($1) #newSeq($2, $3)", [getTypeDesc(p.module, seqtype), - genTypeInfo(p.module, seqtype, dest.lode.info), length]) + genTypeInfoV1(p.module, seqtype, dest.lode.info), length]) linefmt(p, cpsStmts, "#unsureAsgnRef((void**) $1, $2);$n", [addrLoc(p.config, dest), call.rdLoc]) else: call.r = ropecg(p.module, "($1) #newSeqRC1($2, $3)", [getTypeDesc(p.module, seqtype), - genTypeInfo(p.module, seqtype, dest.lode.info), length]) + genTypeInfoV1(p.module, seqtype, dest.lode.info), length]) linefmt(p, cpsStmts, "$1 = $2;$n", [dest.rdLoc, call.rdLoc]) else: if lenIsZero: call.r = rope"NIM_NIL" else: call.r = ropecg(p.module, "($1) #newSeq($2, $3)", [getTypeDesc(p.module, seqtype), - genTypeInfo(p.module, seqtype, dest.lode.info), length]) + genTypeInfoV1(p.module, seqtype, dest.lode.info), length]) genAssignment(p, dest, call, {}) proc genNewSeq(p: BProc, e: PNode) = @@ -1383,7 +1388,7 @@ proc genNewSeqOfCap(p: BProc; e: PNode; d: var TLoc) = putIntoDest(p, d, e, ropecg(p.module, "($1)#nimNewSeqOfCap($2, $3)", [ getTypeDesc(p.module, seqtype), - genTypeInfo(p.module, seqtype, e.info), a.rdLoc])) + genTypeInfoV1(p.module, seqtype, e.info), a.rdLoc])) gcUsage(p.config, e) proc rawConstExpr(p: BProc, n: PNode; d: var TLoc) = @@ -1553,7 +1558,10 @@ proc genNewFinalize(p: BProc, e: PNode) = initLocExpr(p, e[1], a) initLocExpr(p, e[2], f) initLoc(b, locExpr, a.lode, OnHeap) - ti = genTypeInfo(p.module, refType, e.info) + if optTinyRtti in p.config.globalOptions: + ti = genTypeInfoV2(p.module, refType, e.info) + else: + ti = genTypeInfoV1(p.module, refType, e.info) p.module.s[cfsTypeInit3].addf("$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)]) b.r = ropecg(p.module, "($1) #newObj($2, sizeof($3))", [ getTypeDesc(p.module, refType), @@ -1568,9 +1576,9 @@ proc genOfHelper(p: BProc; dest: PType; a: Rope; info: TLineInfo): Rope = result = ropecg(p.module, "#isObj($1.m_type, $2)", [a, genTypeInfo2Name(p.module, dest)]) else: - # unfortunately 'genTypeInfo' sets tfObjHasKids as a side effect, so we + # unfortunately 'genTypeInfoV1' sets tfObjHasKids as a side effect, so we # have to call it here first: - let ti = genTypeInfo(p.module, dest, info) + let ti = genTypeInfoV1(p.module, dest, info) if tfFinal in dest.flags or (objHasKidsValid in p.module.flags and tfObjHasKids notin dest.flags): result = "$1.m_type == $2" % [a, ti] @@ -1583,7 +1591,7 @@ proc genOfHelper(p: BProc; dest: PType; a: Rope; info: TLineInfo): Rope = when false: # former version: result = ropecg(p.module, "#isObj($1.m_type, $2)", - [a, genTypeInfo(p.module, dest, info)]) + [a, genTypeInfoV1(p.module, dest, info)]) proc genOf(p: BProc, x: PNode, typ: PType, d: var TLoc) = var a: TLoc @@ -1633,12 +1641,12 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) = of tyEnum, tyOrdinal: putIntoDest(p, d, e, ropecg(p.module, "#reprEnum((NI)$1, $2)", [ - rdLoc(a), genTypeInfo(p.module, t, e.info)]), a.storage) + rdLoc(a), genTypeInfoV1(p.module, t, e.info)]), a.storage) of tyString: putIntoDest(p, d, e, ropecg(p.module, "#reprStr($1)", [rdLoc(a)]), a.storage) of tySet: putIntoDest(p, d, e, ropecg(p.module, "#reprSet($1, $2)", [ - addrLoc(p.config, a), genTypeInfo(p.module, t, e.info)]), a.storage) + addrLoc(p.config, a), genTypeInfoV1(p.module, t, e.info)]), a.storage) of tyOpenArray, tyVarargs: var b: TLoc case skipTypes(a.t, abstractVarRange).kind @@ -1653,20 +1661,20 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) = else: internalError(p.config, e[0].info, "genRepr()") putIntoDest(p, d, e, ropecg(p.module, "#reprOpenArray($1, $2)", [rdLoc(b), - genTypeInfo(p.module, elemType(t), e.info)]), a.storage) + genTypeInfoV1(p.module, elemType(t), e.info)]), a.storage) of tyCString, tyArray, tyRef, tyPtr, tyPointer, tyNil, tySequence: putIntoDest(p, d, e, ropecg(p.module, "#reprAny($1, $2)", [ - rdLoc(a), genTypeInfo(p.module, t, e.info)]), a.storage) + rdLoc(a), genTypeInfoV1(p.module, t, e.info)]), a.storage) of tyEmpty, tyVoid: localError(p.config, e.info, "'repr' doesn't support 'void' type") else: putIntoDest(p, d, e, ropecg(p.module, "#reprAny($1, $2)", - [addrLoc(p.config, a), genTypeInfo(p.module, t, e.info)]), + [addrLoc(p.config, a), genTypeInfoV1(p.module, t, e.info)]), a.storage) gcUsage(p.config, e) -proc rdMType(p: BProc; a: TLoc; nilCheck: var Rope): Rope = +proc rdMType(p: BProc; a: TLoc; nilCheck: var Rope; enforceV1 = false): Rope = result = rdLoc(a) var t = skipTypes(a.t, abstractInst) while t.kind in {tyVar, tyLent, tyPtr, tyRef}: @@ -1680,13 +1688,20 @@ proc rdMType(p: BProc; a: TLoc; nilCheck: var Rope): Rope = result.add(".Sup") t = skipTypes(t[0], skipPtrs) result.add ".m_type" + if optTinyRtti in p.config.globalOptions and enforceV1: + result.add "->typeInfoV1" proc genGetTypeInfo(p: BProc, e: PNode, d: var TLoc) = discard cgsym(p.module, "TNimType") + let t = e[1].typ + # ordinary static type information + putIntoDest(p, d, e, genTypeInfoV1(p.module, t, e.info)) + +proc genGetTypeInfoV2(p: BProc, e: PNode, d: var TLoc) = let t = e[1].typ if isFinal(t) or e[0].sym.name.s != "getDynamicTypeInfo": # ordinary static type information - putIntoDest(p, d, e, genTypeInfo(p.module, t, e.info)) + putIntoDest(p, d, e, genTypeInfoV2(p.module, t, e.info)) else: var a: TLoc initLocExpr(p, e[1], a) @@ -1779,13 +1794,13 @@ proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) = const setLenPattern = "($3) #setLengthSeqV2(&($1)->Sup, $4, $2)" call.r = ropecg(p.module, setLenPattern, [ rdLoc(a), rdLoc(b), getTypeDesc(p.module, t), - genTypeInfo(p.module, t.skipTypes(abstractInst), e.info)]) + genTypeInfoV1(p.module, t.skipTypes(abstractInst), e.info)]) else: const setLenPattern = "($3) #setLengthSeqV2($1, $4, $2)" call.r = ropecg(p.module, setLenPattern, [ rdLoc(a), rdLoc(b), getTypeDesc(p.module, t), - genTypeInfo(p.module, t.skipTypes(abstractInst), e.info)]) + genTypeInfoV1(p.module, t.skipTypes(abstractInst), e.info)]) genAssignment(p, a, call, {}) gcUsage(p.config, e) @@ -2226,6 +2241,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mAddI..mPred: binaryArithOverflow(p, e, d, op) of mRepr: genRepr(p, e, d) of mGetTypeInfo: genGetTypeInfo(p, e, d) + of mGetTypeInfoV2: genGetTypeInfoV2(p, e, d) of mSwap: genSwap(p, e, d) of mInc, mDec: const opr: array[mInc..mDec, string] = ["+=", "-="] @@ -2534,7 +2550,7 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) = let checkFor = if optTinyRtti in p.config.globalOptions: genTypeInfo2Name(p.module, dest) else: - genTypeInfo(p.module, dest, n.info) + genTypeInfoV1(p.module, dest, n.info) if nilCheck != nil: linefmt(p, cpsStmts, "if ($1 && !#isObj($2, $3)){ #raiseObjectConversionError(); $4}$n", [nilCheck, r, checkFor, raiseInstr(p)]) @@ -2951,7 +2967,10 @@ proc getNullValueAuxT(p: BProc; orig, t: PType; obj, constOrNil: PNode, getNullValueAuxT(p, orig, base, base.n, constOrNil, result, count, isConst, info) result.add "}" elif not isObjLackingTypeField(t): - result.add genTypeInfo(p.module, orig, obj.info) + if optTinyRtti in p.config.globalOptions: + result.add genTypeInfoV2(p.module, orig, obj.info) + else: + result.add genTypeInfoV1(p.module, orig, obj.info) inc count getNullValueAux(p, t, obj, constOrNil, result, count, isConst, info) # do not emit '{}' as that is not valid C: diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 48c024b1f..5bed7cf32 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -1040,7 +1040,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) = let checkFor = if optTinyRtti in p.config.globalOptions: genTypeInfo2Name(p.module, typeNode.typ) else: - genTypeInfo(p.module, typeNode.typ, typeNode.info) + genTypeInfoV1(p.module, typeNode.typ, typeNode.info) let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type" appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor]) @@ -1255,7 +1255,7 @@ proc genTryGoto(p: BProc; t: PNode; d: var TLoc) = let checkFor = if optTinyRtti in p.config.globalOptions: genTypeInfo2Name(p.module, t[i][j].typ) else: - genTypeInfo(p.module, t[i][j].typ, t[i][j].info) + genTypeInfoV1(p.module, t[i][j].typ, t[i][j].info) let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type" appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor]) @@ -1383,7 +1383,7 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) = let checkFor = if optTinyRtti in p.config.globalOptions: genTypeInfo2Name(p.module, t[i][j].typ) else: - genTypeInfo(p.module, t[i][j].typ, t[i][j].info) + genTypeInfoV1(p.module, t[i][j].typ, t[i][j].info) let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type" appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor]) @@ -1508,7 +1508,7 @@ proc genDiscriminantCheck(p: BProc, a, tmp: TLoc, objtype: PType, field: PSym) = var t = skipTypes(objtype, abstractVar) assert t.kind == tyObject - discard genTypeInfo(p.module, t, a.lode.info) + discard genTypeInfoV1(p.module, t, a.lode.info) if not containsOrIncl(p.module.declaredThings, field.id): appcg(p.module, cfsVars, "extern $1", [discriminatorTableDecl(p.module, t, field)]) diff --git a/compiler/ccgtrav.nim b/compiler/ccgtrav.nim index 63b1cb88a..29b93e530 100644 --- a/compiler/ccgtrav.nim +++ b/compiler/ccgtrav.nim @@ -173,7 +173,7 @@ proc genTraverseProc(m: BModule, origTyp: PType; sig: SigHash): Rope = [result, markerName, getModuleDllPath(m)]) proc genTraverseProcForGlobal(m: BModule, s: PSym; info: TLineInfo): Rope = - discard genTypeInfo(m, s.loc.t, info) + discard genTypeInfoV1(m, s.loc.t, info) var c: TTraversalClosure var p = newProc(nil, m) diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index aa92b8534..771e1ede9 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -609,7 +609,10 @@ proc getRecordDesc(m: BModule, typ: PType, name: Rope, if (typ.sym != nil and sfPure in typ.sym.flags) or tfFinal in typ.flags: appcg(m, result, " {$n", []) else: - appcg(m, result, " {$n#TNimType* m_type;$n", []) + if optTinyRtti in m.config.globalOptions: + appcg(m, result, " {$n#TNimTypeV2* m_type;$n", []) + else: + appcg(m, result, " {$n#TNimType* m_type;$n", []) hasField = true elif m.compileToCpp: appcg(m, result, " : public $1 {$n", @@ -1004,7 +1007,7 @@ proc genProcHeader(m: BModule, prc: PSym, asPtr: bool = false): Rope = # ------------------ type info generation ------------------------------------- -proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope +proc genTypeInfoV1(m: BModule, t: PType; info: TLineInfo): Rope proc getNimNode(m: BModule): Rope = result = "$1[$2]" % [m.typeNodesName, rope(m.typeNodes)] inc(m.typeNodes) @@ -1067,7 +1070,7 @@ proc genTypeInfoAux(m: BModule, typ, origType: PType, name: Rope; if typ.kind == tyPtr and x.kind == tyObject and incompleteType(x): base = rope("0") else: - base = genTypeInfo(m, x, info) + base = genTypeInfoV1(m, x, info) else: base = rope("0") genTypeInfoAuxBase(m, typ, origType, name, base, info) @@ -1126,7 +1129,7 @@ proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope; "$1.offset = offsetof($2, $3);$n" & "$1.typ = $4;$n" & "$1.name = $5;$n" & "$1.sons = &$6[0];$n" & "$1.len = $7;$n", [expr, getTypeDesc(m, origType), field.loc.r, - genTypeInfo(m, field.typ, info), + genTypeInfoV1(m, field.typ, info), makeCString(field.name.s), tmp, rope(L)]) m.s[cfsData].addf("TNimNode* $1[$2];$n", [tmp, rope(L+1)]) @@ -1163,7 +1166,7 @@ proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope; m.s[cfsTypeInit3].addf("$1.kind = 1;$n" & "$1.offset = offsetof($2, $3);$n" & "$1.typ = $4;$n" & "$1.name = $5;$n", [expr, getTypeDesc(m, origType), - field.loc.r, genTypeInfo(m, field.typ, info), makeCString(field.name.s)]) + field.loc.r, genTypeInfoV1(m, field.typ, info), makeCString(field.name.s)]) else: internalError(m.config, n.info, "genObjectFields") proc genObjectInfo(m: BModule, typ, origType: PType, name: Rope; info: TLineInfo) = @@ -1198,7 +1201,7 @@ proc genTupleInfo(m: BModule, typ, origType: PType, name: Rope; info: TLineInfo) "$1.offset = offsetof($2, Field$3);$n" & "$1.typ = $4;$n" & "$1.name = \"Field$3\";$n", - [tmp2, getTypeDesc(m, origType), rope(i), genTypeInfo(m, a, info)]) + [tmp2, getTypeDesc(m, origType), rope(i), genTypeInfoV1(m, a, info)]) m.s[cfsTypeInit3].addf("$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n", [expr, rope(typ.len), tmp]) else: @@ -1255,7 +1258,7 @@ proc genSetInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) = [tmp, rope(firstOrd(m.config, typ)), tiNameForHcr(m, name)]) proc genArrayInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) = - genTypeInfoAuxBase(m, typ, typ, name, genTypeInfo(m, typ[1], info), info) + genTypeInfoAuxBase(m, typ, typ, name, genTypeInfoV1(m, typ[1], info), info) proc fakeClosureType(m: BModule; owner: PSym): PType = # we generate the same RTTI as for a tuple[pointer, ref tuple[]] @@ -1273,13 +1276,14 @@ proc genDeepCopyProc(m: BModule; s: PSym; result: Rope) = m.s[cfsTypeInit3].addf("$1.deepcopy =(void* (N_RAW_NIMCALL*)(void*))$2;$n", [result, s.loc.r]) -proc declareNimType(m: BModule, str: Rope, ownerModule: PSym) = +proc declareNimType(m: BModule, name: string; str: Rope, ownerModule: PSym) = + let nr = rope(name) if m.hcrOn: - m.s[cfsData].addf("static TNimType* $1;$n", [str]) - m.s[cfsTypeInit1].addf("\t$1 = (TNimType*)hcrGetGlobal($2, \"$1\");$n", - [str, getModuleDllPath(m, ownerModule)]) + m.s[cfsData].addf("static $2* $1;$n", [str, nr]) + m.s[cfsTypeInit1].addf("\t$1 = ($3*)hcrGetGlobal($2, \"$1\");$n", + [str, getModuleDllPath(m, ownerModule), nr]) else: - m.s[cfsData].addf("extern TNimType $1;$n", [str]) + m.s[cfsData].addf("extern $2 $1;$n", [str, nr]) proc genTypeInfo2Name(m: BModule; t: PType): Rope = var res = "|" @@ -1327,7 +1331,7 @@ proc genHook(m: BModule; t: PType; info: TLineInfo; op: TTypeAttachedOp): Rope = internalError(m.config, info, "no attached trace proc found") result = rope("NIM_NIL") -proc genTypeInfoV2(m: BModule, t, origType: PType, name: Rope; info: TLineInfo) = +proc genTypeInfoV2Impl(m: BModule, t, origType: PType, name: Rope; info: TLineInfo) = var typeName: Rope if t.kind == tyObject: if incompleteType(t): @@ -1337,8 +1341,8 @@ proc genTypeInfoV2(m: BModule, t, origType: PType, name: Rope; info: TLineInfo) else: typeName = rope("NIM_NIL") - discard cgsym(m, "TNimType") - m.s[cfsData].addf("N_LIB_PRIVATE TNimType $1;$n", [name]) + discard cgsym(m, "TNimTypeV2") + m.s[cfsData].addf("N_LIB_PRIVATE TNimTypeV2 $1;$n", [name]) let destroyImpl = genHook(m, t, info, attachedDestructor) let traceImpl = genHook(m, t, info, attachedTrace) let disposeImpl = genHook(m, t, info, attachedDispose) @@ -1347,7 +1351,43 @@ proc genTypeInfoV2(m: BModule, t, origType: PType, name: Rope; info: TLineInfo) name, destroyImpl, getTypeDesc(m, t), typeName, traceImpl, disposeImpl]) -proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope = +proc genTypeInfoV2(m: BModule, t: PType; info: TLineInfo): Rope = + let origType = t + var t = skipTypes(origType, irrelevantForBackend + tyUserTypeClasses) + + let prefixTI = if m.hcrOn: "(" else: "(&" + + let sig = hashType(origType) + result = m.typeInfoMarkerV2.getOrDefault(sig) + if result != nil: + return prefixTI.rope & result & ")".rope + + let marker = m.g.typeInfoMarkerV2.getOrDefault(sig) + if marker.str != nil: + discard cgsym(m, "TNimTypeV2") + declareNimType(m, "TNimTypeV2", marker.str, marker.owner) + # also store in local type section: + m.typeInfoMarkerV2[sig] = marker.str + return prefixTI.rope & marker.str & ")".rope + + result = "NTIv2$1_" % [rope($sig)] + m.typeInfoMarkerV2[sig] = result + + let owner = t.skipTypes(typedescPtrs).owner.getModule + if owner != m.module: + # make sure the type info is created in the owner module + assert m.g.modules[owner.position] != nil + discard genTypeInfoV2(m.g.modules[owner.position], origType, info) + # reference the type info as extern here + discard cgsym(m, "TNimTypeV2") + declareNimType(m, "TNimTypeV2", result, owner) + return prefixTI.rope & result & ")".rope + + m.g.typeInfoMarkerV2[sig] = (str: result, owner: owner) + genTypeInfoV2Impl(m, t, origType, result, info) + result = prefixTI.rope & result & ")".rope + +proc genTypeInfoV1(m: BModule, t: PType; info: TLineInfo): Rope = let origType = t var t = skipTypes(origType, irrelevantForBackend + tyUserTypeClasses) @@ -1362,7 +1402,7 @@ proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope = if marker.str != nil: discard cgsym(m, "TNimType") discard cgsym(m, "TNimNode") - declareNimType(m, marker.str, marker.owner) + declareNimType(m, "TNimType", marker.str, marker.owner) # also store in local type section: m.typeInfoMarker[sig] = marker.str return prefixTI.rope & marker.str & ")".rope @@ -1374,63 +1414,65 @@ proc genTypeInfo(m: BModule, t: PType; info: TLineInfo): Rope = if owner != m.module: # make sure the type info is created in the owner module assert m.g.modules[owner.position] != nil - discard genTypeInfo(m.g.modules[owner.position], origType, info) + discard genTypeInfoV1(m.g.modules[owner.position], origType, info) # reference the type info as extern here discard cgsym(m, "TNimType") discard cgsym(m, "TNimNode") - declareNimType(m, result, owner) + declareNimType(m, "TNimType", result, owner) return prefixTI.rope & result & ")".rope m.g.typeInfoMarker[sig] = (str: result, owner: owner) - if optTinyRtti in m.config.globalOptions: - genTypeInfoV2(m, t, origType, result, info) - else: - case t.kind - of tyEmpty, tyVoid: result = rope"0" - of tyPointer, tyBool, tyChar, tyCString, tyString, tyInt..tyUInt64, tyVar, tyLent: + case t.kind + of tyEmpty, tyVoid: result = rope"0" + of tyPointer, tyBool, tyChar, tyCString, tyString, tyInt..tyUInt64, tyVar, tyLent: + genTypeInfoAuxBase(m, t, t, result, rope"0", info) + of tyStatic: + if t.n != nil: result = genTypeInfoV1(m, lastSon t, info) + else: internalError(m.config, "genTypeInfoV1(" & $t.kind & ')') + of tyUserTypeClasses: + internalAssert m.config, t.isResolvedUserTypeClass + return genTypeInfoV1(m, t.lastSon, info) + of tyProc: + if t.callConv != ccClosure: genTypeInfoAuxBase(m, t, t, result, rope"0", info) - of tyStatic: - if t.n != nil: result = genTypeInfo(m, lastSon t, info) - else: internalError(m.config, "genTypeInfo(" & $t.kind & ')') - of tyUserTypeClasses: - internalAssert m.config, t.isResolvedUserTypeClass - return genTypeInfo(m, t.lastSon, info) - of tyProc: - if t.callConv != ccClosure: - genTypeInfoAuxBase(m, t, t, result, rope"0", info) - else: - let x = fakeClosureType(m, t.owner) - genTupleInfo(m, x, x, result, info) - of tySequence: - genTypeInfoAux(m, t, t, result, info) - if optSeqDestructors notin m.config.globalOptions: - if m.config.selectedGC >= gcMarkAndSweep: - let markerProc = genTraverseProc(m, origType, sig) - m.s[cfsTypeInit3].addf("$1.marker = $2;$n", [tiNameForHcr(m, result), markerProc]) - of tyRef: - genTypeInfoAux(m, t, t, result, info) - if m.config.selectedGC >= gcMarkAndSweep: - let markerProc = genTraverseProc(m, origType, sig) - m.s[cfsTypeInit3].addf("$1.marker = $2;$n", [tiNameForHcr(m, result), markerProc]) - of tyPtr, tyRange, tyUncheckedArray: genTypeInfoAux(m, t, t, result, info) - of tyArray: genArrayInfo(m, t, result, info) - of tySet: genSetInfo(m, t, result, info) - of tyEnum: genEnumInfo(m, t, result, info) - of tyObject: - genObjectInfo(m, t, origType, result, info) - of tyTuple: - # if t.n != nil: genObjectInfo(m, t, result) - # else: - # BUGFIX: use consistently RTTI without proper field names; otherwise - # results are not deterministic! - genTupleInfo(m, t, origType, result, info) - else: internalError(m.config, "genTypeInfo(" & $t.kind & ')') + else: + let x = fakeClosureType(m, t.owner) + genTupleInfo(m, x, x, result, info) + of tySequence: + genTypeInfoAux(m, t, t, result, info) + if m.config.selectedGC >= gcMarkAndSweep: + let markerProc = genTraverseProc(m, origType, sig) + m.s[cfsTypeInit3].addf("$1.marker = $2;$n", [tiNameForHcr(m, result), markerProc]) + of tyRef: + genTypeInfoAux(m, t, t, result, info) + if m.config.selectedGC >= gcMarkAndSweep: + let markerProc = genTraverseProc(m, origType, sig) + m.s[cfsTypeInit3].addf("$1.marker = $2;$n", [tiNameForHcr(m, result), markerProc]) + of tyPtr, tyRange, tyUncheckedArray: genTypeInfoAux(m, t, t, result, info) + of tyArray: genArrayInfo(m, t, result, info) + of tySet: genSetInfo(m, t, result, info) + of tyEnum: genEnumInfo(m, t, result, info) + of tyObject: + genObjectInfo(m, t, origType, result, info) + of tyTuple: + # if t.n != nil: genObjectInfo(m, t, result) + # else: + # BUGFIX: use consistently RTTI without proper field names; otherwise + # results are not deterministic! + genTupleInfo(m, t, origType, result, info) + else: internalError(m.config, "genTypeInfoV1(" & $t.kind & ')') if t.attachedOps[attachedDeepCopy] != nil: genDeepCopyProc(m, t.attachedOps[attachedDeepCopy], result) elif origType.attachedOps[attachedDeepCopy] != nil: genDeepCopyProc(m, origType.attachedOps[attachedDeepCopy], result) + + if optTinyRtti in m.config.globalOptions and t.kind == tyObject: + let v2info = genTypeInfoV2(m, origType, info) + addf(m.s[cfsTypeInit3], "$1->typeInfoV1 = (void*)&$2; $2.typeInfoV2 = (void*)$1;$n", [ + v2info, result]) + result = prefixTI.rope & result & ")".rope proc genTypeSection(m: BModule, n: PNode) = diff --git a/compiler/cgen.nim b/compiler/cgen.nim index d5e1e020c..242d1f89b 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -360,7 +360,10 @@ proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: var TLoc, while s.kind == tyObject and s[0] != nil: r.add(".Sup") s = skipTypes(s[0], skipPtrs) - linefmt(p, section, "$1.m_type = $2;$n", [r, genTypeInfo(p.module, t, a.lode.info)]) + if optTinyRtti in p.config.globalOptions: + linefmt(p, section, "$1.m_type = $2;$n", [r, genTypeInfoV2(p.module, t, a.lode.info)]) + else: + linefmt(p, section, "$1.m_type = $2;$n", [r, genTypeInfoV1(p.module, t, a.lode.info)]) of frEmbedded: if optTinyRtti in p.config.globalOptions: var tmp: TLoc @@ -376,7 +379,7 @@ proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: var TLoc, else: # worst case for performance: var r = if mode == constructObj: addrLoc(p.config, a) else: rdLoc(a) - linefmt(p, section, "#objectInit($1, $2);$n", [r, genTypeInfo(p.module, t, a.lode.info)]) + linefmt(p, section, "#objectInit($1, $2);$n", [r, genTypeInfoV1(p.module, t, a.lode.info)]) if isException(t): var r = rdLoc(a) @@ -417,7 +420,7 @@ proc resetLoc(p: BProc, loc: var TLoc) = specializeReset(p, loc) when false: linefmt(p, cpsStmts, "#genericReset((void*)$1, $2);$n", - [addrLoc(p.config, loc), genTypeInfo(p.module, loc.t, loc.lode.info)]) + [addrLoc(p.config, loc), genTypeInfoV1(p.module, loc.t, loc.lode.info)]) # XXX: generated reset procs should not touch the m_type # field, so disabling this should be safe: genObjectInit(p, cpsStmts, loc.t, loc, constructObj) diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index 3384558f8..8e5094336 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -122,6 +122,7 @@ type forwardedProcs*: seq[PSym] # proc:s that did not yet have a body generatedHeader*: BModule typeInfoMarker*: TypeCacheWithOwner + typeInfoMarkerV2*: TypeCacheWithOwner config*: ConfigRef graph*: ModuleGraph strVersion*, seqVersion*: int # version of the string/seq implementation to use @@ -155,6 +156,7 @@ type declaredProtos*: IntSet # prototypes we have declared in this .c file headerFiles*: seq[string] # needed headers to include typeInfoMarker*: TypeCache # needed for generating type information + typeInfoMarkerV2*: TypeCache initProc*: BProc # code for init procedure preInitProc*: BProc # code executed before the init proc hcrCreateTypeInfosProc*: Rope # type info globals are in here when HCR=on diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 3d0635761..dd5332b38 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -523,7 +523,7 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = var cond: PNode if isCyclic: if isFinal(elemType): - let typInfo = genBuiltin(c.g, mGetTypeInfo, "getTypeInfo", newNodeIT(nkType, x.info, elemType)) + let typInfo = genBuiltin(c.g, mGetTypeInfoV2, "getTypeInfoV2", newNodeIT(nkType, x.info, elemType)) typInfo.typ = getSysType(c.g, c.info, tyPointer) cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicStatic", c.info, x, typInfo) else: @@ -546,7 +546,7 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = of attachedDeepCopy: assert(false, "cannot happen") of attachedTrace: if isFinal(elemType): - let typInfo = genBuiltin(c.g, mGetTypeInfo, "getTypeInfo", newNodeIT(nkType, x.info, elemType)) + let typInfo = genBuiltin(c.g, mGetTypeInfoV2, "getTypeInfoV2", newNodeIT(nkType, x.info, elemType)) typInfo.typ = getSysType(c.g, c.info, tyPointer) body.add callCodegenProc(c.g, "nimTraceRef", c.info, genAddrOf(x), typInfo, y) else: diff --git a/compiler/options.nim b/compiler/options.nim index 5b23ac9af..03418d953 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -136,9 +136,8 @@ type cmdCompileToBackend, # compile to backend in TBackend TStringSeq* = seq[string] TGCMode* = enum # the selected GC - gcUnselected, gcNone, gcBoehm, gcRegions, gcMarkAndSweep, gcArc, gcOrc, - gcHooks, - gcRefc, gcV2, gcGo + gcUnselected, gcNone, gcBoehm, gcRegions, gcArc, gcOrc, + gcMarkAndSweep, gcHooks, gcRefc, gcV2, gcGo # gcRefc and the GCs that follow it use a write barrier, # as far as usesWriteBarrier() is concerned diff --git a/lib/core/typeinfo.nim b/lib/core/typeinfo.nim index 01588e9eb..b1b9bcb27 100644 --- a/lib/core/typeinfo.nim +++ b/lib/core/typeinfo.nim @@ -19,8 +19,6 @@ ## work with the types' AST representation at compile time. See, for example, ## the `getTypeImpl proc`_. As an alternative ## approach to storing arbitrary types at runtime, consider using generics. -## -## {.push hints: off.} @@ -77,11 +75,21 @@ type ppointer = ptr pointer pbyteArray = ptr array[0xffff, int8] - TGenericSeq {.importc.} = object - len, space: int - when defined(gogc): - elemSize: int - PGenSeq = ptr TGenericSeq +when not defined(gcDestructors): + type + TGenericSeq {.importc.} = object + len, space: int + when defined(gogc): + elemSize: int + PGenSeq = ptr TGenericSeq + + when defined(gogc): + const GenericSeqSize = (3 * sizeof(int)) + else: + const GenericSeqSize = (2 * sizeof(int)) + +else: + include system/seqs_v2_reimpl when not defined(js): template rawType(x: Any): PNimType = @@ -90,18 +98,20 @@ when not defined(js): template `rawType=`(x: var Any, p: PNimType) = x.rawTypePtr = cast[pointer](p) -when defined(gogc): - const GenericSeqSize = (3 * sizeof(int)) -else: - const GenericSeqSize = (2 * sizeof(int)) - proc genericAssign(dest, src: pointer, mt: PNimType) {.importCompilerProc.} -proc genericShallowAssign(dest, src: pointer, mt: PNimType) {. - importCompilerProc.} -proc incrSeq(seq: PGenSeq, elemSize, elemAlign: int): PGenSeq {.importCompilerProc.} -proc newObj(typ: PNimType, size: int): pointer {.importCompilerProc.} -proc newSeq(typ: PNimType, len: int): pointer {.importCompilerProc.} -proc objectInit(dest: pointer, typ: PNimType) {.importCompilerProc.} + +when not defined(gcDestructors): + proc genericShallowAssign(dest, src: pointer, mt: PNimType) {. + importCompilerProc.} + proc incrSeq(seq: PGenSeq, elemSize, elemAlign: int): PGenSeq {.importCompilerProc.} + proc newObj(typ: PNimType, size: int): pointer {.importCompilerProc.} + proc newSeq(typ: PNimType, len: int): pointer {.importCompilerProc.} + proc objectInit(dest: pointer, typ: PNimType) {.importCompilerProc.} +else: + proc nimNewObj(size: int): pointer {.importCompilerProc.} + proc newSeqPayload(cap, elemSize, elemAlign: int): pointer {.importCompilerProc.} + proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize, elemAlign: int): pointer {. + importCompilerProc.} template `+!!`(a, b): untyped = cast[pointer](cast[ByteAddress](a) + b) @@ -167,29 +177,47 @@ proc baseTypeSize*(x: Any): int {.inline.} = proc invokeNew*(x: Any) = ## performs ``new(x)``. `x` needs to represent a ``ref``. assert x.rawType.kind == tyRef - var z = newObj(x.rawType, x.rawType.base.size) - genericAssign(x.value, addr(z), x.rawType) + when defined(gcDestructors): + cast[ppointer](x.value)[] = nimNewObj(x.rawType.base.size) + else: + var z = newObj(x.rawType, x.rawType.base.size) + genericAssign(x.value, addr(z), x.rawType) proc invokeNewSeq*(x: Any, len: int) = ## performs ``newSeq(x, len)``. `x` needs to represent a ``seq``. assert x.rawType.kind == tySequence - var z = newSeq(x.rawType, len) - genericShallowAssign(x.value, addr(z), x.rawType) + when defined(gcDestructors): + var s = cast[ptr NimSeqV2Reimpl](x.value) + s.len = len + let elem = x.rawType.base + s.p = cast[ptr NimSeqPayloadReimpl](newSeqPayload(len, elem.size, elem.align)) + else: + var z = newSeq(x.rawType, len) + genericShallowAssign(x.value, addr(z), x.rawType) proc extendSeq*(x: Any) = ## performs ``setLen(x, x.len+1)``. `x` needs to represent a ``seq``. assert x.rawType.kind == tySequence - var y = cast[ptr PGenSeq](x.value)[] - var z = incrSeq(y, x.rawType.base.size, x.rawType.base.align) - # 'incrSeq' already freed the memory for us and copied over the RC! - # So we simply copy the raw pointer into 'x.value': - cast[ppointer](x.value)[] = z - #genericShallowAssign(x.value, addr(z), x.rawType) + when defined(gcDestructors): + var s = cast[ptr NimSeqV2Reimpl](x.value) + let elem = x.rawType.base + s.p = cast[ptr NimSeqPayloadReimpl](prepareSeqAdd(s.len, s.p, 1, elem.size, elem.align)) + inc s.len + else: + var y = cast[ptr PGenSeq](x.value)[] + var z = incrSeq(y, x.rawType.base.size, x.rawType.base.align) + # 'incrSeq' already freed the memory for us and copied over the RC! + # So we simply copy the raw pointer into 'x.value': + cast[ppointer](x.value)[] = z + #genericShallowAssign(x.value, addr(z), x.rawType) proc setObjectRuntimeType*(x: Any) = ## this needs to be called to set `x`'s runtime object type field. assert x.rawType.kind == tyObject - objectInit(x.value, x.rawType) + when defined(gcDestructors): + cast[ppointer](x.value)[] = x.rawType.typeInfoV2 + else: + objectInit(x.value, x.rawType) proc skipRange(x: PNimType): PNimType {.inline.} = result = x @@ -202,17 +230,26 @@ proc `[]`*(x: Any, i: int): Any = ## accessor for an any `x` that represents an array or a sequence. case x.rawType.kind of tyArray: - var bs = x.rawType.base.size + let bs = x.rawType.base.size if i >=% x.rawType.size div bs: raise newException(IndexDefect, formatErrorIndexBound(i, x.rawType.size div bs)) return newAny(x.value +!! i*bs, x.rawType.base) of tySequence: - var s = cast[ppointer](x.value)[] - if s == nil: raise newException(ValueError, "sequence is nil") - var bs = x.rawType.base.size - if i >=% cast[PGenSeq](s).len: - raise newException(IndexDefect, formatErrorIndexBound(i, cast[PGenSeq](s).len-1)) - return newAny(s +!! (align(GenericSeqSize, x.rawType.base.align)+i*bs), x.rawType.base) + when defined(gcDestructors): + var s = cast[ptr NimSeqV2Reimpl](x.value) + if i >=% s.len: + raise newException(IndexDefect, formatErrorIndexBound(i, s.len-1)) + let bs = x.rawType.base.size + let ba = x.rawType.base.align + let headerSize = align(sizeof(int), ba) + return newAny(s.p +!! (headerSize+i*bs), x.rawType.base) + else: + var s = cast[ppointer](x.value)[] + if s == nil: raise newException(ValueError, "sequence is nil") + let bs = x.rawType.base.size + if i >=% cast[PGenSeq](s).len: + raise newException(IndexDefect, formatErrorIndexBound(i, cast[PGenSeq](s).len-1)) + return newAny(s +!! (align(GenericSeqSize, x.rawType.base.align)+i*bs), x.rawType.base) else: assert false proc `[]=`*(x: Any, i: int, y: Any) = @@ -225,13 +262,23 @@ proc `[]=`*(x: Any, i: int, y: Any) = assert y.rawType == x.rawType.base genericAssign(x.value +!! i*bs, y.value, y.rawType) of tySequence: - var s = cast[ppointer](x.value)[] - if s == nil: raise newException(ValueError, "sequence is nil") - var bs = x.rawType.base.size - if i >=% cast[PGenSeq](s).len: - raise newException(IndexDefect, formatErrorIndexBound(i, cast[PGenSeq](s).len-1)) - assert y.rawType == x.rawType.base - genericAssign(s +!! (align(GenericSeqSize, x.rawType.base.align)+i*bs), y.value, y.rawType) + when defined(gcDestructors): + var s = cast[ptr NimSeqV2Reimpl](x.value) + if i >=% s.len: + raise newException(IndexDefect, formatErrorIndexBound(i, s.len-1)) + let bs = x.rawType.base.size + let ba = x.rawType.base.align + let headerSize = align(sizeof(int), ba) + assert y.rawType == x.rawType.base + genericAssign(s.p +!! (headerSize+i*bs), y.value, y.rawType) + else: + var s = cast[ppointer](x.value)[] + if s == nil: raise newException(ValueError, "sequence is nil") + var bs = x.rawType.base.size + if i >=% cast[PGenSeq](s).len: + raise newException(IndexDefect, formatErrorIndexBound(i, cast[PGenSeq](s).len-1)) + assert y.rawType == x.rawType.base + genericAssign(s +!! (align(GenericSeqSize, x.rawType.base.align)+i*bs), y.value, y.rawType) else: assert false proc len*(x: Any): int = @@ -240,11 +287,14 @@ proc len*(x: Any): int = of tyArray: result = x.rawType.size div x.rawType.base.size of tySequence: - let pgenSeq = cast[PGenSeq](cast[ppointer](x.value)[]) - if isNil(pgenSeq): - result = 0 + when defined(gcDestructors): + result = cast[ptr NimSeqV2Reimpl](x.value).len else: - result = pgenSeq.len + let pgenSeq = cast[PGenSeq](cast[ppointer](x.value)[]) + if isNil(pgenSeq): + result = 0 + else: + result = pgenSeq.len else: assert false @@ -260,21 +310,27 @@ proc isNil*(x: Any): bool = assert x.rawType.kind in {tyCString, tyRef, tyPtr, tyPointer, tyProc} result = isNil(cast[ppointer](x.value)[]) +const + pointerLike = when defined(gcDestructors): {tyCString, tyRef, tyPtr, tyPointer, tyProc} + else: {tyString, tyCString, tyRef, tyPtr, tyPointer, + tySequence, tyProc} + proc getPointer*(x: Any): pointer = ## retrieve the pointer value out of `x`. ``x`` needs to be of kind ## ``akString``, ``akCString``, ``akProc``, ``akRef``, ``akPtr``, ## ``akPointer``, ``akSequence``. - assert x.rawType.kind in {tyString, tyCString, tyRef, tyPtr, tyPointer, - tySequence, tyProc} + assert x.rawType.kind in pointerLike result = cast[ppointer](x.value)[] proc setPointer*(x: Any, y: pointer) = ## sets the pointer value of `x`. ``x`` needs to be of kind ## ``akString``, ``akCString``, ``akProc``, ``akRef``, ``akPtr``, ## ``akPointer``, ``akSequence``. - assert x.rawType.kind in {tyString, tyCString, tyRef, tyPtr, tyPointer, - tySequence, tyProc} - cast[ppointer](x.value)[] = y + assert x.rawType.kind in pointerLike + when defined(gcDestructors): + genericAssign(x.value, y, x.rawType) + else: + cast[ppointer](x.value)[] = y proc fieldsAux(p: pointer, n: ptr TNimNode, ret: var seq[tuple[name: cstring, any: Any]]) = @@ -607,13 +663,16 @@ proc setBiggestFloat*(x: Any, y: BiggestFloat) = proc getString*(x: Any): string = ## retrieve the string value out of `x`. `x` needs to represent a string. assert x.rawType.kind == tyString - if not isNil(cast[ptr pointer](x.value)[]): + when defined(gcDestructors): result = cast[ptr string](x.value)[] + else: + if not isNil(cast[ptr pointer](x.value)[]): + result = cast[ptr string](x.value)[] proc setString*(x: Any, y: string) = ## sets the string value of `x`. `x` needs to represent a string. assert x.rawType.kind == tyString - cast[ptr string](x.value)[] = y + cast[ptr string](x.value)[] = y # also correct for gcDestructors proc getCString*(x: Any): cstring = ## retrieve the cstring value out of `x`. `x` needs to represent a cstring. diff --git a/lib/pure/marshal.nim b/lib/pure/marshal.nim index b6ad2e20f..e74e68b05 100644 --- a/lib/pure/marshal.nim +++ b/lib/pure/marshal.nim @@ -51,16 +51,15 @@ ## * `json module `_ const unsupportedPlatform = - when defined(nimV2): "new runtime" - elif defined(js): "javascript" + when defined(js): "javascript" elif defined(nimscript): "nimscript" else: "" when unsupportedPlatform != "": {.error: "marshal module is not supported in " & unsupportedPlatform & """. -Please use alternative packages for serialization. -It is possible to reimplement this module using generics and type traits. -Please contribute new implementation.""".} +Please use alternative packages for serialization. +It is possible to reimplement this module using generics and type traits. +Please contribute a new implementation.""".} import streams, typeinfo, json, intsets, tables, unicode @@ -345,7 +344,7 @@ proc to*[T](data: string): T = when not defined(testing) and isMainModule: - template testit(x: untyped) = echo($$to[type(x)]($$x)) + template testit(x: untyped) = echo($$to[typeof(x)]($$x)) var x: array[0..4, array[0..4, string]] = [ ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"], diff --git a/lib/system.nim b/lib/system.nim index 0a8c52908..c05ca24dc 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -537,7 +537,7 @@ when notJSnotNims and not defined(nimSeqsV2): template space(s: PGenericSeq): int {.dirty.} = s.reserved and not (seqShallowFlag or strlitFlag) -when notJSnotNims and not defined(nimV2): +when notJSnotNims: include "system/hti" type @@ -1718,16 +1718,16 @@ when not defined(js) and hasThreadSupport and hostOS != "standalone": when not defined(js) and defined(nimV2): type - TNimNode {.compilerproc.} = object # to keep the code generator simple DestructorProc = proc (p: pointer) {.nimcall, benign, raises: [].} - TNimType {.compilerproc.} = object + TNimTypeV2 {.compilerproc.} = object destructor: pointer size: int align: int name: cstring traceImpl: pointer disposeImpl: pointer - PNimType = ptr TNimType + typeInfoV1: pointer # for backwards compat, usually nil + PNimTypeV2 = ptr TNimTypeV2 when notJSnotNims and defined(nimSeqsV2): include "system/strs_v2" @@ -2271,29 +2271,28 @@ when notJSnotNims: else: const GenericSeqSize = (2 * sizeof(int)) - when not defined(nimV2): - proc getDiscriminant(aa: pointer, n: ptr TNimNode): uint = - sysAssert(n.kind == nkCase, "getDiscriminant: node != nkCase") - var d: uint - var a = cast[uint](aa) - case n.typ.size - of 1: d = uint(cast[ptr uint8](a + uint(n.offset))[]) - of 2: d = uint(cast[ptr uint16](a + uint(n.offset))[]) - of 4: d = uint(cast[ptr uint32](a + uint(n.offset))[]) - of 8: d = uint(cast[ptr uint64](a + uint(n.offset))[]) - else: - d = 0'u - sysAssert(false, "getDiscriminant: invalid n.typ.size") - return d + proc getDiscriminant(aa: pointer, n: ptr TNimNode): uint = + sysAssert(n.kind == nkCase, "getDiscriminant: node != nkCase") + var d: uint + var a = cast[uint](aa) + case n.typ.size + of 1: d = uint(cast[ptr uint8](a + uint(n.offset))[]) + of 2: d = uint(cast[ptr uint16](a + uint(n.offset))[]) + of 4: d = uint(cast[ptr uint32](a + uint(n.offset))[]) + of 8: d = uint(cast[ptr uint64](a + uint(n.offset))[]) + else: + d = 0'u + sysAssert(false, "getDiscriminant: invalid n.typ.size") + return d - proc selectBranch(aa: pointer, n: ptr TNimNode): ptr TNimNode = - var discr = getDiscriminant(aa, n) - if discr < cast[uint](n.len): - result = n.sons[discr] - if result == nil: result = n.sons[n.len] - # n.sons[n.len] contains the ``else`` part (but may be nil) - else: - result = n.sons[n.len] + proc selectBranch(aa: pointer, n: ptr TNimNode): ptr TNimNode = + var discr = getDiscriminant(aa, n) + if discr < cast[uint](n.len): + result = n.sons[discr] + if result == nil: result = n.sons[n.len] + # n.sons[n.len] contains the ``else`` part (but may be nil) + else: + result = n.sons[n.len] when notJSnotNims and hasAlloc: {.push profiler: off.} @@ -2305,8 +2304,8 @@ when notJSnotNims and hasAlloc: {.pop.} include "system/strmantle" - when not usesDestructors: - include "system/assign" + include "system/assign" + when not defined(nimV2): include "system/repr" @@ -2862,7 +2861,7 @@ proc locals*(): RootObj {.magic: "Plugin", noSideEffect.} = ## # -> B is 1 discard -when hasAlloc and notJSnotNims and not usesDestructors: +when hasAlloc and notJSnotNims: # XXX how to implement 'deepCopy' is an open problem. proc deepCopy*[T](x: var T, y: T) {.noSideEffect, magic: "DeepCopy".} = ## Performs a deep copy of `y` and copies it into `x`. diff --git a/lib/system/arc.nim b/lib/system/arc.nim index 7fe17d3ea..68376cdab 100644 --- a/lib/system/arc.nim +++ b/lib/system/arc.nim @@ -59,12 +59,6 @@ type Cell = ptr RefHeader -template `+!`(p: pointer, s: int): pointer = - cast[pointer](cast[int](p) +% s) - -template `-!`(p: pointer, s: int): pointer = - cast[pointer](cast[int](p) -% s) - template head(p: pointer): Cell = cast[Cell](cast[int](p) -% sizeof(RefHeader)) @@ -132,6 +126,14 @@ proc nimIncRef(p: pointer) {.compilerRtl, inl.} = when traceCollector: cprintf("[INCREF] %p\n", head(p)) +proc unsureAsgnRef(dest: ptr pointer, src: pointer) {.inline.} = + # This is only used by the old RTTI mechanism and we know + # that 'dest[]' is nil and needs no destruction. Which is really handy + # as we cannot destroy the object reliably if it's an object of unknown + # compile-time type. + dest[] = src + if src != nil: nimIncRef src + when not defined(nimscript) and defined(nimArcDebug): proc deallocatedRefId*(p: pointer): int = ## Returns the ref's ID if the ref was already deallocated. This @@ -165,10 +167,10 @@ template dispose*[T](x: owned(ref T)) = nimRawDispose(cast[pointer](x)) #proc dispose*(x: pointer) = nimRawDispose(x) proc nimDestroyAndDispose(p: pointer) {.compilerRtl, raises: [].} = - let d = cast[ptr PNimType](p)[].destructor + let d = cast[ptr PNimTypeV2](p)[].destructor if d != nil: cast[DestructorProc](d)(p) when false: - cstderr.rawWrite cast[ptr PNimType](p)[].name + cstderr.rawWrite cast[ptr PNimTypeV2](p)[].name cstderr.rawWrite "\n" if d == nil: cstderr.rawWrite "bah, nil\n" @@ -226,11 +228,11 @@ template tearDownForeignThreadGc* = ## With ``--gc:arc`` a nop. discard -proc isObj(obj: PNimType, subclass: cstring): bool {.compilerRtl, inl.} = +proc isObj(obj: PNimTypeV2, subclass: cstring): bool {.compilerRtl, inl.} = proc strstr(s, sub: cstring): cstring {.header: "", importc.} result = strstr(obj.name, subclass) != nil -proc chckObj(obj: PNimType, subclass: cstring) {.compilerRtl.} = +proc chckObj(obj: PNimTypeV2, subclass: cstring) {.compilerRtl.} = # checks if obj is of type subclass: if not isObj(obj, subclass): sysFatal(ObjectConversionDefect, "invalid object conversion") diff --git a/lib/system/assign.nim b/lib/system/assign.nim index ff4ac021e..d332124cd 100644 --- a/lib/system/assign.nim +++ b/lib/system/assign.nim @@ -7,6 +7,8 @@ # distribution, for details about the copyright. # +include seqs_v2_reimpl + proc genericResetAux(dest: pointer, n: ptr TNimNode) {.benign.} proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) {.benign.} @@ -38,6 +40,20 @@ proc genericAssignAux(dest, src: pointer, n: ptr TNimNode, # echo "ugh memory corruption! ", n.kind # quit 1 +template deepSeqAssignImpl(operation, additionalArg) {.dirty.} = + var d = cast[ptr NimSeqV2Reimpl](dest) + var s = cast[ptr NimSeqV2Reimpl](src) + d.len = s.len + let elem = mt.base + d.p = cast[ptr NimSeqPayloadReimpl](newSeqPayload(s.len, elem.size, elem.align)) + + let bs = elem.size + let ba = elem.align + let headerSize = align(sizeof(NimSeqPayloadBase), ba) + + for i in 0..d.len-1: + operation(d.p +! (headerSize+i*bs), s.p +! (headerSize+i*bs), mt.base, additionalArg) + proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) = var d = cast[ByteAddress](dest) @@ -45,38 +61,46 @@ proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) = sysAssert(mt != nil, "genericAssignAux 2") case mt.kind of tyString: - var x = cast[PPointer](dest) - var s2 = cast[PPointer](s)[] - if s2 == nil or shallow or ( - cast[PGenericSeq](s2).reserved and seqShallowFlag) != 0: - unsureAsgnRef(x, s2) + when defined(nimSeqsV2): + var x = cast[ptr NimStringV2](dest) + var s2 = cast[ptr NimStringV2](s)[] + nimAsgnStrV2(x[], s2) else: - unsureAsgnRef(x, copyString(cast[NimString](s2))) + var x = cast[PPointer](dest) + var s2 = cast[PPointer](s)[] + if s2 == nil or shallow or ( + cast[PGenericSeq](s2).reserved and seqShallowFlag) != 0: + unsureAsgnRef(x, s2) + else: + unsureAsgnRef(x, copyString(cast[NimString](s2))) of tySequence: - var s2 = cast[PPointer](src)[] - var seq = cast[PGenericSeq](s2) - var x = cast[PPointer](dest) - if s2 == nil or shallow or (seq.reserved and seqShallowFlag) != 0: - # this can happen! nil sequences are allowed - unsureAsgnRef(x, s2) - return - sysAssert(dest != nil, "genericAssignAux 3") - if ntfNoRefs in mt.base.flags: - var ss = nimNewSeqOfCap(mt, seq.len) - cast[PGenericSeq](ss).len = seq.len - unsureAsgnRef(x, ss) - var dst = cast[ByteAddress](cast[PPointer](dest)[]) - copyMem(cast[pointer](dst +% align(GenericSeqSize, mt.base.align)), - cast[pointer](cast[ByteAddress](s2) +% align(GenericSeqSize, mt.base.align)), - seq.len *% mt.base.size) + when defined(nimSeqsV2): + deepSeqAssignImpl(genericAssignAux, shallow) else: - unsureAsgnRef(x, newSeq(mt, seq.len)) - var dst = cast[ByteAddress](cast[PPointer](dest)[]) - for i in 0..seq.len-1: - genericAssignAux( - cast[pointer](dst +% align(GenericSeqSize, mt.base.align) +% i *% mt.base.size ), - cast[pointer](cast[ByteAddress](s2) +% align(GenericSeqSize, mt.base.align) +% i *% mt.base.size ), - mt.base, shallow) + var s2 = cast[PPointer](src)[] + var seq = cast[PGenericSeq](s2) + var x = cast[PPointer](dest) + if s2 == nil or shallow or (seq.reserved and seqShallowFlag) != 0: + # this can happen! nil sequences are allowed + unsureAsgnRef(x, s2) + return + sysAssert(dest != nil, "genericAssignAux 3") + if ntfNoRefs in mt.base.flags: + var ss = nimNewSeqOfCap(mt, seq.len) + cast[PGenericSeq](ss).len = seq.len + unsureAsgnRef(x, ss) + var dst = cast[ByteAddress](cast[PPointer](dest)[]) + copyMem(cast[pointer](dst +% align(GenericSeqSize, mt.base.align)), + cast[pointer](cast[ByteAddress](s2) +% align(GenericSeqSize, mt.base.align)), + seq.len *% mt.base.size) + else: + unsureAsgnRef(x, newSeq(mt, seq.len)) + var dst = cast[ByteAddress](cast[PPointer](dest)[]) + for i in 0..seq.len-1: + genericAssignAux( + cast[pointer](dst +% align(GenericSeqSize, mt.base.align) +% i *% mt.base.size ), + cast[pointer](cast[ByteAddress](s2) +% align(GenericSeqSize, mt.base.align) +% i *% mt.base.size ), + mt.base, shallow) of tyObject: var it = mt.base # don't use recursion here on the PNimType because the subtype @@ -87,14 +111,19 @@ proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) = genericAssignAux(dest, src, mt.node, shallow) # we need to copy m_type field for tyObject, as it could be empty for # sequence reallocations: - var pint = cast[ptr PNimType](dest) - # We need to copy the *static* type not the dynamic type: - # if p of TB: - # var tbObj = TB(p) - # tbObj of TC # needs to be false! - #c_fprintf(stdout, "%s %s\n", pint[].name, mt.name) - chckObjAsgn(cast[ptr PNimType](src)[], mt) - pint[] = mt # cast[ptr PNimType](src)[] + when defined(nimSeqsV2): + var pint = cast[ptr PNimTypeV2](dest) + #chckObjAsgn(cast[ptr PNimTypeV2](src)[].typeInfoV2, mt) + pint[] = cast[PNimTypeV2](mt.typeInfoV2) + else: + var pint = cast[ptr PNimType](dest) + # We need to copy the *static* type not the dynamic type: + # if p of TB: + # var tbObj = TB(p) + # tbObj of TC # needs to be false! + #c_fprintf(stdout, "%s %s\n", pint[].name, mt.name) + chckObjAsgn(cast[ptr PNimType](src)[], mt) + pint[] = mt # cast[ptr PNimType](src)[] of tyTuple: genericAssignAux(dest, src, mt.node, shallow) of tyArray, tyArrayConstr: @@ -175,8 +204,12 @@ proc objectInit(dest: pointer, typ: PNimType) = of tyObject: # iterate over any structural type # here we have to init the type field: - var pint = cast[ptr PNimType](dest) - pint[] = typ + when defined(nimSeqsV2): + var pint = cast[ptr PNimTypeV2](dest) + pint[] = cast[PNimTypeV2](typ.typeInfoV2) + else: + var pint = cast[ptr PNimType](dest) + pint[] = typ objectInitAux(dest, typ.node) of tyTuple: objectInitAux(dest, typ.node) @@ -204,15 +237,34 @@ proc genericReset(dest: pointer, mt: PNimType) = var d = cast[ByteAddress](dest) sysAssert(mt != nil, "genericReset 2") case mt.kind - of tyString, tyRef, tySequence: + of tyRef: unsureAsgnRef(cast[PPointer](dest), nil) + of tyString: + when defined(nimSeqsV2): + var s = cast[ptr NimStringV2](dest) + frees(s[]) + zeroMem(dest, mt.size) + else: + unsureAsgnRef(cast[PPointer](dest), nil) + of tySequence: + when defined(nimSeqsV2): + var s = cast[ptr NimSeqV2Reimpl](dest) + if s.p != nil: + deallocShared(s.p) + zeroMem(dest, mt.size) + else: + unsureAsgnRef(cast[PPointer](dest), nil) of tyTuple: genericResetAux(dest, mt.node) of tyObject: genericResetAux(dest, mt.node) # also reset the type field for tyObject, for correct branch switching! - var pint = cast[ptr PNimType](dest) - pint[] = nil + when defined(nimSeqsV2): + var pint = cast[ptr PNimTypeV2](dest) + pint[] = nil + else: + var pint = cast[ptr PNimType](dest) + pint[] = nil of tyArray, tyArrayConstr: for i in 0..(mt.size div mt.base.size)-1: genericReset(cast[pointer](d +% i *% mt.base.size), mt.base) diff --git a/lib/system/cellseqs_v2.nim b/lib/system/cellseqs_v2.nim index b2ae41d73..fdd9e3099 100644 --- a/lib/system/cellseqs_v2.nim +++ b/lib/system/cellseqs_v2.nim @@ -10,13 +10,13 @@ # Cell seqs for cyclebreaker and cyclicrefs_v2. type - CellTuple = (PT, PNimType) + CellTuple = (PT, PNimTypeV2) CellArray = ptr UncheckedArray[CellTuple] CellSeq = object len, cap: int d: CellArray -proc add(s: var CellSeq, c: PT; t: PNimType) {.inline.} = +proc add(s: var CellSeq, c: PT; t: PNimTypeV2) {.inline.} = if s.len >= s.cap: s.cap = s.cap * 3 div 2 when defined(useMalloc): @@ -51,6 +51,6 @@ proc deinit(s: var CellSeq) = s.len = 0 s.cap = 0 -proc pop(s: var CellSeq): (PT, PNimType) = +proc pop(s: var CellSeq): (PT, PNimTypeV2) = result = s.d[s.len-1] dec s.len diff --git a/lib/system/cyclebreaker.nim b/lib/system/cyclebreaker.nim index 3d01eeb9d..c4d802a92 100644 --- a/lib/system/cyclebreaker.nim +++ b/lib/system/cyclebreaker.nim @@ -78,14 +78,14 @@ type GcEnv = object traceStack: CellSeq -proc trace(p: pointer; desc: PNimType; j: var GcEnv) {.inline.} = +proc trace(p: pointer; desc: PNimTypeV2; j: var GcEnv) {.inline.} = when false: cprintf("[Trace] desc: %p %p\n", desc, p) cprintf("[Trace] trace: %p\n", desc.traceImpl) if desc.traceImpl != nil: cast[TraceProc](desc.traceImpl)(p, addr(j)) -proc nimTraceRef(q: pointer; desc: PNimType; env: pointer) {.compilerRtl.} = +proc nimTraceRef(q: pointer; desc: PNimTypeV2; env: pointer) {.compilerRtl.} = let p = cast[ptr pointer](q) when traceCollector: cprintf("[Trace] raw: %p\n", p) @@ -101,11 +101,11 @@ proc nimTraceRefDyn(q: pointer; env: pointer) {.compilerRtl.} = cprintf("[TraceDyn] deref: %p\n", p[]) if p[] != nil: var j = cast[ptr GcEnv](env) - j.traceStack.add(p, cast[ptr PNimType](p[])[]) + j.traceStack.add(p, cast[ptr PNimTypeV2](p[])[]) var markerGeneration: int -proc breakCycles(s: Cell; desc: PNimType) = +proc breakCycles(s: Cell; desc: PNimTypeV2) = let markerColor = if (markerGeneration and 1) == 0: colRed else: colYellow atomicInc markerGeneration @@ -147,7 +147,7 @@ proc thinout*[T](x: ref T) {.inline.} = ## and thus would keep the graph from being freed are `nil`'ed. ## This is a form of cycle collection that works well with Nim's ARC ## and its associated cost model. - proc getDynamicTypeInfo[T](x: T): PNimType {.magic: "GetTypeInfo", noSideEffect, locks: 0.} + proc getDynamicTypeInfo[T](x: T): PNimTypeV2 {.magic: "GetTypeInfoV2", noSideEffect, locks: 0.} breakCycles(head(cast[pointer](x)), getDynamicTypeInfo(x[])) @@ -158,7 +158,7 @@ proc thinout*[T: proc](x: T) {.inline.} = """.} let p = rawEnv(x) - breakCycles(head(p), cast[ptr PNimType](p)[]) + breakCycles(head(p), cast[ptr PNimTypeV2](p)[]) proc nimDecRefIsLastCyclicDyn(p: pointer): bool {.compilerRtl, inl.} = if p != nil: @@ -171,7 +171,7 @@ proc nimDecRefIsLastCyclicDyn(p: pointer): bool {.compilerRtl, inl.} = # According to Lins it's correct to do nothing else here. #cprintf("[DeCREF] %p\n", p) -proc nimDecRefIsLastCyclicStatic(p: pointer; desc: PNimType): bool {.compilerRtl, inl.} = +proc nimDecRefIsLastCyclicStatic(p: pointer; desc: PNimTypeV2): bool {.compilerRtl, inl.} = if p != nil: var cell = head(p) if (cell.rc and not rcMask) == 0: diff --git a/lib/system/deepcopy.nim b/lib/system/deepcopy.nim index 801821d26..5905b5785 100644 --- a/lib/system/deepcopy.nim +++ b/lib/system/deepcopy.nim @@ -90,27 +90,35 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType; tab: var PtrTable) = sysAssert(mt != nil, "genericDeepCopyAux 2") case mt.kind of tyString: - var x = cast[PPointer](dest) - var s2 = cast[PPointer](s)[] - if s2 == nil: - unsureAsgnRef(x, s2) + when defined(nimSeqsV2): + var x = cast[ptr NimStringV2](dest) + var s2 = cast[ptr NimStringV2](s)[] + nimAsgnStrV2(x[], s2) else: - unsureAsgnRef(x, copyDeepString(cast[NimString](s2))) + var x = cast[PPointer](dest) + var s2 = cast[PPointer](s)[] + if s2 == nil: + unsureAsgnRef(x, s2) + else: + unsureAsgnRef(x, copyDeepString(cast[NimString](s2))) of tySequence: - var s2 = cast[PPointer](src)[] - var seq = cast[PGenericSeq](s2) - var x = cast[PPointer](dest) - if s2 == nil: - unsureAsgnRef(x, s2) - return - sysAssert(dest != nil, "genericDeepCopyAux 3") - unsureAsgnRef(x, newSeq(mt, seq.len)) - var dst = cast[ByteAddress](cast[PPointer](dest)[]) - for i in 0..seq.len-1: - genericDeepCopyAux( - cast[pointer](dst +% align(GenericSeqSize, mt.base.align) +% i *% mt.base.size), - cast[pointer](cast[ByteAddress](s2) +% align(GenericSeqSize, mt.base.align) +% i *% mt.base.size), - mt.base, tab) + when defined(nimSeqsV2): + deepSeqAssignImpl(genericDeepCopyAux, tab) + else: + var s2 = cast[PPointer](src)[] + var seq = cast[PGenericSeq](s2) + var x = cast[PPointer](dest) + if s2 == nil: + unsureAsgnRef(x, s2) + return + sysAssert(dest != nil, "genericDeepCopyAux 3") + unsureAsgnRef(x, newSeq(mt, seq.len)) + var dst = cast[ByteAddress](cast[PPointer](dest)[]) + for i in 0..seq.len-1: + genericDeepCopyAux( + cast[pointer](dst +% align(GenericSeqSize, mt.base.align) +% i *% mt.base.size), + cast[pointer](cast[ByteAddress](s2) +% align(GenericSeqSize, mt.base.align) +% i *% mt.base.size), + mt.base, tab) of tyObject: # we need to copy m_type field for tyObject, as it could be empty for # sequence reallocations: @@ -151,7 +159,7 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType; tab: var PtrTable) = sysAssert realType == mt, " types do differ" # this version should work for any possible GC: let typ = if mt.base.kind == tyObject: cast[ptr PNimType](s2)[] else: mt.base - let z = newObj(mt, typ.size) + let z = when defined(nimSeqsV2): nimNewObj(typ.size) else: newObj(mt, typ.size) unsureAsgnRef(cast[PPointer](dest), z) tab.put(s2, z) genericDeepCopyAux(z, s2, typ, tab) @@ -168,11 +176,11 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType; tab: var PtrTable) = copyMem(dest, src, mt.size) proc genericDeepCopy(dest, src: pointer, mt: PNimType) {.compilerproc.} = - GC_disable() + when not defined(nimSeqsV2): GC_disable() var tab = initPtrTable() genericDeepCopyAux(dest, src, mt, tab) deinit tab - GC_enable() + when not defined(nimSeqsV2): GC_enable() proc genericSeqDeepCopy(dest, src: pointer, mt: PNimType) {.compilerproc.} = # also invoked for 'string' diff --git a/lib/system/hti.nim b/lib/system/hti.nim index b77f7ccde..3dbcd7615 100644 --- a/lib/system/hti.nim +++ b/lib/system/hti.nim @@ -98,6 +98,8 @@ type finalizer*: pointer # the finalizer for the type marker*: proc (p: pointer, op: int) {.nimcall, benign, tags: [], raises: [].} # marker proc for GC deepcopy: proc (p: pointer): pointer {.nimcall, benign, tags: [], raises: [].} + when defined(nimSeqsV2): + typeInfoV2*: pointer when defined(nimTypeNames): name: cstring nextType: ptr TNimType diff --git a/lib/system/mm/malloc.nim b/lib/system/mm/malloc.nim index 3dad98e93..2ba5c0fec 100644 --- a/lib/system/mm/malloc.nim +++ b/lib/system/mm/malloc.nim @@ -65,8 +65,10 @@ proc growObj(old: pointer, newsize: int): pointer = proc nimGCref(p: pointer) {.compilerproc, inline.} = discard proc nimGCunref(p: pointer) {.compilerproc, inline.} = discard -proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} = - dest[] = src +when not defined(gcDestructors): + proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} = + dest[] = src + proc asgnRef(dest: PPointer, src: pointer) {.compilerproc, inline.} = dest[] = src proc asgnRefNoCycle(dest: PPointer, src: pointer) {.compilerproc, inline, diff --git a/lib/system/orc.nim b/lib/system/orc.nim index 7500ba374..4ba462c1f 100644 --- a/lib/system/orc.nim +++ b/lib/system/orc.nim @@ -53,7 +53,7 @@ type toFree: CellSeq freed, touched: int -proc trace(s: Cell; desc: PNimType; j: var GcEnv) {.inline.} = +proc trace(s: Cell; desc: PNimTypeV2; j: var GcEnv) {.inline.} = if desc.traceImpl != nil: var p = s +! sizeof(RefHeader) cast[TraceProc](desc.traceImpl)(p, addr(j)) @@ -65,7 +65,7 @@ else: let p = s +! sizeof(RefHeader) cprintf("[%s] name %s RC %ld\n", str, p, s.rc shr rcShift) -proc free(s: Cell; desc: PNimType) {.inline.} = +proc free(s: Cell; desc: PNimTypeV2) {.inline.} = when traceCollector: cprintf("[From ] %p rc %ld color %ld\n", s, s.rc shr rcShift, s.color) let p = s +! sizeof(RefHeader) @@ -89,7 +89,7 @@ proc free(s: Cell; desc: PNimType) {.inline.} = nimRawDispose(p) -proc nimTraceRef(q: pointer; desc: PNimType; env: pointer) {.compilerRtl.} = +proc nimTraceRef(q: pointer; desc: PNimTypeV2; env: pointer) {.compilerRtl.} = let p = cast[ptr pointer](q) if p[] != nil: var j = cast[ptr GcEnv](env) @@ -99,7 +99,7 @@ proc nimTraceRefDyn(q: pointer; env: pointer) {.compilerRtl.} = let p = cast[ptr pointer](q) if p[] != nil: var j = cast[ptr GcEnv](env) - j.traceStack.add(head p[], cast[ptr PNimType](p[])[]) + j.traceStack.add(head p[], cast[ptr PNimTypeV2](p[])[]) var roots {.threadvar.}: CellSeq @@ -115,7 +115,7 @@ proc unregisterCycle(s: Cell) = roots.d[idx][0].rootIdx = idx dec roots.len -proc scanBlack(s: Cell; desc: PNimType; j: var GcEnv) = +proc scanBlack(s: Cell; desc: PNimTypeV2; j: var GcEnv) = #[ proc scanBlack(s: Cell) = setColor(s, colBlack) @@ -135,7 +135,7 @@ proc scanBlack(s: Cell; desc: PNimType; j: var GcEnv) = t.setColor colBlack trace(t, desc, j) -proc markGray(s: Cell; desc: PNimType; j: var GcEnv) = +proc markGray(s: Cell; desc: PNimTypeV2; j: var GcEnv) = #[ proc markGray(s: Cell) = if s.color != colGray: @@ -163,7 +163,7 @@ proc markGray(s: Cell; desc: PNimType; j: var GcEnv) = inc j.touched trace(t, desc, j) -proc scan(s: Cell; desc: PNimType; j: var GcEnv) = +proc scan(s: Cell; desc: PNimTypeV2; j: var GcEnv) = #[ proc scan(s: Cell) = if s.color == colGray: @@ -226,7 +226,7 @@ when false: cfprintf(cstderr, "%s %p root index: %ld; RC: %ld; color: %ld\n", msg, s, s.rootIdx, s.rc shr rcShift, s.color) -proc collectWhite(s: Cell; desc: PNimType; j: var GcEnv) = +proc collectWhite(s: Cell; desc: PNimTypeV2; j: var GcEnv) = #[ proc collectWhite(s: Cell) = if s.color == colWhite and not buffered(s): @@ -315,7 +315,7 @@ proc collectCycles() = when false: cfprintf(cstderr, "[collectCycles] freed %ld new threshold %ld\n", j.freed, rootsThreshold) -proc registerCycle(s: Cell; desc: PNimType) = +proc registerCycle(s: Cell; desc: PNimTypeV2) = if roots.len >= rootsThreshold: collectCycles() if roots.d == nil: init(roots) @@ -334,7 +334,7 @@ proc GC_enableMarkAndSweep() = proc GC_disableMarkAndSweep() = rootsThreshold = high(int) -proc rememberCycle(isDestroyAction: bool; s: Cell; desc: PNimType) {.noinline.} = +proc rememberCycle(isDestroyAction: bool; s: Cell; desc: PNimTypeV2) {.noinline.} = if isDestroyAction: if (s.rc and isCycleCandidate) != 0: s.rc = s.rc and not isCycleCandidate @@ -356,9 +356,9 @@ proc nimDecRefIsLastCyclicDyn(p: pointer): bool {.compilerRtl, inl.} = else: dec cell.rc, rcIncrement #if cell.color == colPurple: - rememberCycle(result, cell, cast[ptr PNimType](p)[]) + rememberCycle(result, cell, cast[ptr PNimTypeV2](p)[]) -proc nimDecRefIsLastCyclicStatic(p: pointer; desc: PNimType): bool {.compilerRtl, inl.} = +proc nimDecRefIsLastCyclicStatic(p: pointer; desc: PNimTypeV2): bool {.compilerRtl, inl.} = if p != nil: var cell = head(p) if (cell.rc and not rcMask) == 0: diff --git a/lib/system/seqs_v2.nim b/lib/system/seqs_v2.nim index 3c94a03f9..111299fff 100644 --- a/lib/system/seqs_v2.nim +++ b/lib/system/seqs_v2.nim @@ -22,7 +22,8 @@ type cap: int data: UncheckedArray[T] - NimSeqV2*[T] = object + NimSeqV2*[T] = object # \ + # if you change this implementation, also change seqs_v2_reimpl.nim! len: int p: ptr NimSeqPayload[T] @@ -40,12 +41,15 @@ proc newSeqPayload(cap, elemSize, elemAlign: int): pointer {.compilerRtl, raises else: result = nil -proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize, elemAlign: int): pointer {. - noSideEffect, raises: [].} = - {.noSideEffect.}: - template `+!`(p: pointer, s: int): pointer = - cast[pointer](cast[int](p) +% s) +template `+!`(p: pointer, s: int): pointer = + cast[pointer](cast[int](p) +% s) +template `-!`(p: pointer, s: int): pointer = + cast[pointer](cast[int](p) -% s) + +proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize, elemAlign: int): pointer {. + noSideEffect, raises: [], compilerRtl.} = + {.noSideEffect.}: let headerSize = align(sizeof(NimSeqPayloadBase), elemAlign) if addlen <= 0: result = p diff --git a/lib/system/seqs_v2_reimpl.nim b/lib/system/seqs_v2_reimpl.nim new file mode 100644 index 000000000..750f78c0d --- /dev/null +++ b/lib/system/seqs_v2_reimpl.nim @@ -0,0 +1,17 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2020 Nim contributors +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +type + NimSeqPayloadReimpl = object + cap: int + data: pointer + + NimSeqV2Reimpl = object + len: int + p: ptr NimSeqPayloadReimpl diff --git a/tests/stdlib/tmarshal.nim b/tests/stdlib/tmarshal.nim index 118d0ae02..fc5703a03 100644 --- a/tests/stdlib/tmarshal.nim +++ b/tests/stdlib/tmarshal.nim @@ -14,7 +14,7 @@ joinable: false pending https://github.com/nim-lang/Nim/issues/9754 import marshal -template testit(x) = discard $$to[type(x)]($$x) +template testit(x) = discard $$to[typeof(x)]($$x) var x: array[0..4, array[0..4, string]] = [ ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"], @@ -89,8 +89,8 @@ var instance1 = Person(name: "Cletus", age: 12, bio: "Я Cletus", blob: "ABC\x80") echo($$instance1) -echo(to[Person]($$instance1).bio == instance1.bio) -echo(to[Person]($$instance1).blob == instance1.blob) +echo(to[Person]($$instance1).bio == instance1.bio) # true +echo(to[Person]($$instance1).blob == instance1.blob) # true # bug 5757 From de7f2372be770ca97f3f63dcc9447bdd2827f9d0 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Wed, 16 Sep 2020 20:58:50 +0800 Subject: [PATCH 0612/3768] docs minor and #15335 (#15337) --- lib/pure/collections/sequtils.nim | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index 51d8ade85..d8ea3ad1f 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -809,10 +809,17 @@ template foldl*(sequence, operation: untyped): untyped = multiplication = foldl(numbers, a * b) words = @["nim", "is", "cool"] concatenation = foldl(words, a & b) + procs = @["proc", "Is", "Also", "Fine"] + + + proc foo(acc, cur: string): string = + result = acc & cur + assert addition == 25, "Addition is (((5)+9)+11)" assert subtraction == -15, "Subtraction is (((5)-9)-11)" assert multiplication == 495, "Multiplication is (((5)*9)*11)" assert concatenation == "nimiscool" + assert foldl(procs, foo(a, b)) == "procIsAlsoFine" let s = sequence assert s.len > 0, "Can't fold empty sequences" From ae4ede6b009632bd0d809c61066b240e8ee7719a Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 16 Sep 2020 17:26:13 +0200 Subject: [PATCH 0613/3768] fixes #15325 (#15340) --- compiler/commands.nim | 3 + compiler/injectdestructors.nim | 3 +- compiler/options.nim | 3 +- compiler/varpartitions.nim | 13 ++- tests/arc/tcursor_on_localvar.nim | 126 ++++++++++++++++++++++++++++++ 5 files changed, 144 insertions(+), 4 deletions(-) create mode 100644 tests/arc/tcursor_on_localvar.nim diff --git a/compiler/commands.nim b/compiler/commands.nim index 0af31b2c7..a76a5be73 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -891,6 +891,9 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; processOnOffSwitchG(conf, {optProfileVM}, arg, pass, info) of "sinkinference": processOnOffSwitch(conf, {optSinkInference}, arg, pass, info) + of "cursorinference": + # undocumented, for debugging purposes only: + processOnOffSwitch(conf, {optCursorInference}, arg, pass, info) of "panics": processOnOffSwitchG(conf, {optPanics}, arg, pass, info) if optPanics in conf.globalOptions: diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index b8b8e776e..9d1f43595 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -1006,7 +1006,8 @@ proc injectDestructorCalls*(g: ModuleGraph; owner: PSym; n: PNode): PNode = echoCfg(c.g) echo n - computeCursors(owner, n, g.config) + if optCursorInference in g.config.options: + computeCursors(owner, n, g.config) var scope: Scope let body = p(n, c, scope, normal) diff --git a/compiler/options.nim b/compiler/options.nim index 03418d953..226f13c73 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -41,6 +41,7 @@ type # please make sure we have under 32 options optMemTracker, optNilSeqs, optSinkInference # 'sink T' inference + optCursorInference TOptions* = set[TOption] @@ -372,7 +373,7 @@ const DefaultOptions* = {optObjCheck, optFieldCheck, optRangeCheck, optBoundsCheck, optOverflowCheck, optAssert, optWarns, optRefCheck, optHints, optStackTrace, optLineTrace, # consider adding `optStackTraceMsgs` - optTrMacros, optStyleCheck} + optTrMacros, optStyleCheck, optCursorInference} DefaultGlobalOptions* = {optThreadAnalysis, optExcessiveStackTrace, optListFullPaths} diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim index 87424c4be..743c4d55d 100644 --- a/compiler/varpartitions.nim +++ b/compiler/varpartitions.nim @@ -392,9 +392,18 @@ proc deps(c: var Partitions; dest, src: PNode) = analyseAsgn(c, c.s[vid], src) # do not borrow from a different local variable, this is easier # than tracking reassignments, consider 'var cursor = local; local = newNode()' - if src.kind == nkSym and (src.sym.kind in {skVar, skResult, skTemp} or + if src.kind == nkSym: + if (src.sym.kind in {skVar, skResult, skTemp} or (src.sym.kind in {skLet, skParam, skForVar} and hasDisabledAsgn(src.sym.typ))): - c.s[vid].flags.incl preventCursor + c.s[vid].flags.incl preventCursor + elif src.sym.kind in {skVar, skResult, skTemp, skLet, skForVar}: + # XXX: we need to compute variable alive ranges before doing anything else: + let srcid = variableId(c, src.sym) + if srcid >= 0 and preventCursor in c.s[srcid].flags: + # you cannot borrow from a local that lives shorter than 'vid': + if c.s[srcid].aliveStart > c.s[vid].aliveStart or + c.s[srcid].aliveEnd < c.s[vid].aliveEnd: + c.s[vid].flags.incl preventCursor if src.kind == nkSym and hasDestructor(src.typ): rhsIsSink(c, src) diff --git a/tests/arc/tcursor_on_localvar.nim b/tests/arc/tcursor_on_localvar.nim new file mode 100644 index 000000000..81b48271f --- /dev/null +++ b/tests/arc/tcursor_on_localvar.nim @@ -0,0 +1,126 @@ +discard """ + output: '''Section: common + Param: Floats1 +Section: local + Param: Str + Param: Bool + Param: Floats2''' + cmd: '''nim c --gc:arc $file''' +""" + +# bug #15325 + +import tables +import strutils + +const defaultSection = "***" + +type + Config* = ref object + table: OrderedTableRef[string, OrderedTable[string, string]] + +# ---------------------------------------------------------------------------------------------------------------------- +proc newConfig*(): Config = + result = new(Config) + result.table = newOrderedTable[string, OrderedTable[string, string]]() + +# ---------------------------------------------------------------------------------------------------------------------- +proc add*(self: Config, param, value, section: string) {.nosinks.} = + let s = if section == "": defaultSection else: section + + if not self.table.contains(s): + self.table[s] = initOrderedTable[string, string]() + + self.table[s][param] = value + +# ---------------------------------------------------------------------------------------------------------------------- +proc sections*(self: Config): seq[string] = + for i in self.table.keys: + let s = if i == defaultSection: "" else: i + result.add(s) + +# ---------------------------------------------------------------------------------------------------------------------- +proc params*(self: Config, section: string): seq[string] = + let s = if section == "": defaultSection else: section + + if self.table.contains(s): + for i in self.table[s].keys: + result.add(i) + +# ---------------------------------------------------------------------------------------------------------------------- +proc extract*(str, start, finish: string): string = + let startPos = str.find(start) + + if startPos < 0: + return "" + + let endPos = str.find(finish, startPos) + + if endPos < 0: + return "" + + return str[startPos + start.len() ..< endPos] + +# ---------------------------------------------------------------------------------------------------------------------- +proc loadString*(self: Config, text: string): tuple[valid: bool, errorInLine: int] {.discardable.} = + self.table.clear() + + var data = "" + + data = text + + var + actualSection = "" + lineCount = 0 + + for i in splitLines(data): + lineCount += 1 + + var line = strip(i) + + if line.len() == 0: + continue + + if line[0] == '#' or line[0] == ';': + continue + + if line[0] == '[': + let section = strip(extract(line, "[", "]")) + + if section.len() != 0: + actualSection = section + else: + self.table.clear() + return (false, lineCount) + else: + let equal = find(line, '=') + + if equal <= 0: + self.table.clear() + return (false, lineCount) + else: + let + param = strip(line[0 .. equal - 1]) + value = strip(line[equal + 1 .. ^1]) + + if param.len() == 0: + self.table.clear() + return (false, lineCount) + else: + self.add(param, value, actualSection) + + return (true, 0) + +# ---------------------------------------------------------------------------------------------------------------------- +when isMainModule: + var cfg = newConfig() + + cfg.loadString("[common]\nFloats1 = 1,2,3\n[local]\nStr = \"String...\"\nBool = true\nFloats2 = 4, 5, 6\n") + + for s in cfg.sections(): + echo "Section: " & s + + for p in cfg.params(s): + echo " Param: " & p + + From a3e9cc52343a54cadc7b77b783e1c8b6ba2b327f Mon Sep 17 00:00:00 2001 From: cooldome Date: Wed, 16 Sep 2020 16:26:45 +0100 Subject: [PATCH 0614/3768] Introduce explicit copy (#15330) --- compiler/injectdestructors.nim | 2 +- lib/system.nim | 5 +++++ tests/arc/tcopytosink_warning.nim | 22 ++++++++++++++++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 tests/arc/tcopytosink_warning.nim diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 9d1f43595..de28c9f70 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -408,7 +408,7 @@ proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode = if isLValue(n) and not isCapturedVar(n) and n.typ.skipTypes(abstractInst).kind != tyRef and c.inSpawn == 0: message(c.graph.config, n.info, hintPerformance, ("passing '$1' to a sink parameter introduces an implicit copy; " & - "if possible, rearrange your program's control flow to prevent it") % $n) + "if possible, rearrange your program's control flow to prevent it or use 'copy($1)' to hint the compiler it is intentional") % $n) else: if c.graph.config.selectedGC in {gcArc, gcOrc}: assert(not containsGarbageCollectedRef(n.typ)) diff --git a/lib/system.nim b/lib/system.nim index c05ca24dc..dbe0f11f7 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -264,6 +264,11 @@ proc move*[T](x: var T): T {.magic: "Move", noSideEffect.} = result = x wasMoved(x) +func copy*[T](x: T): T {.inline.} = + ## make explicit copy of the argument `x`, used to signal to the compiler + ## the copy is intentional + result = x + type range*[T]{.magic: "Range".} ## Generic type to construct range types. array*[I, T]{.magic: "Array".} ## Generic type to construct diff --git a/tests/arc/tcopytosink_warning.nim b/tests/arc/tcopytosink_warning.nim new file mode 100644 index 000000000..8ae36386a --- /dev/null +++ b/tests/arc/tcopytosink_warning.nim @@ -0,0 +1,22 @@ +discard """ + cmd: "nim c --gc:arc $file" + nimout: '''tcopytosink_warning.nim(17, 7) Hint: myhint [User] +tcopytosink_warning.nim(19, 9) Hint: passing 'x' to a sink parameter introduces an implicit copy; if possible, rearrange your program's control flow to prevent it or use 'copy(x)' to hint the compiler it is intentional [Performance] +''' + output: "x" +""" +import macros + +proc test(v: var seq[string], x: sink string) = + v.add x + +var v = @["a", "b", "c"] +var x = "x" + +static: + hint("myhint") +test(v, copy(x)) # no warning +test(v, x) # produces warning + +echo x # use after sink + From 341be0b61cc0603dc921c99b891208dcda274c98 Mon Sep 17 00:00:00 2001 From: cooldome Date: Wed, 16 Sep 2020 17:25:24 +0100 Subject: [PATCH 0615/3768] proc params as syms (#15332) * proc params are now syms * Fix typesrenderer * Add testcase for disrupteks issue * fix test * Trigger build * Trigger build * Trigger build Co-authored-by: Clyybber --- compiler/semtypes.nim | 1 + compiler/typesrenderer.nim | 2 +- lib/js/jsffi.nim | 10 +++++++++- tests/macros/tmacros_various.nim | 8 ++++++++ 4 files changed, 19 insertions(+), 2 deletions(-) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 953801431..d85008342 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1286,6 +1286,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, addParamOrResult(c, arg, kind) styleCheckDef(c.config, a[j].info, arg) onDef(a[j].info, arg) + a[j] = newSymNode(arg) var r: PType if n[0].kind != nkEmpty: diff --git a/compiler/typesrenderer.nim b/compiler/typesrenderer.nim index 60f9f6603..050a057f4 100644 --- a/compiler/typesrenderer.nim +++ b/compiler/typesrenderer.nim @@ -67,7 +67,7 @@ proc renderType(n: PNode): string = let typeStr = renderType(n[typePos]) result = typeStr for i in 1.. Date: Wed, 16 Sep 2020 19:41:14 +0200 Subject: [PATCH 0616/3768] fixes #9754 [backport] (#15342) --- lib/core/typeinfo.nim | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/lib/core/typeinfo.nim b/lib/core/typeinfo.nim index b1b9bcb27..9fd8ebf8c 100644 --- a/lib/core/typeinfo.nim +++ b/lib/core/typeinfo.nim @@ -327,10 +327,7 @@ proc setPointer*(x: Any, y: pointer) = ## ``akString``, ``akCString``, ``akProc``, ``akRef``, ``akPtr``, ## ``akPointer``, ``akSequence``. assert x.rawType.kind in pointerLike - when defined(gcDestructors): - genericAssign(x.value, y, x.rawType) - else: - cast[ppointer](x.value)[] = y + genericAssign(x.value, y, x.rawType) proc fieldsAux(p: pointer, n: ptr TNimNode, ret: var seq[tuple[name: cstring, any: Any]]) = From 28a64869f0a5b96ee491a2a4210461709d4800fa Mon Sep 17 00:00:00 2001 From: Yuriy Glukhov Date: Wed, 16 Sep 2020 20:41:57 +0300 Subject: [PATCH 0617/3768] Fixed undeclared nimIdentNormalize compilation error in parseEnum (#15343) --- lib/pure/strutils.nim | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 20010ff81..156929dee 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -1294,8 +1294,7 @@ macro genEnumStmt(typ: typedesc, argSym: typed, default: typed): untyped = let typ = typ.getTypeInst[1] let impl = typ.getImpl[2] expectKind impl, nnkEnumTy - result = nnkCaseStmt.newTree(nnkDotExpr.newTree(argSym, - bindSym"nimIdentNormalize")) + result = nnkCaseStmt.newTree(newCall(bindSym"nimIdentNormalize", argSym)) # stores all processed field strings to give error msg for ambiguous enums var foundFields: seq[string] = @[] var fStr = "" # string of current field From 913ffbb92d0f0b9c5e7cf3fcbb446c38e890680e Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Wed, 16 Sep 2020 16:31:35 -0300 Subject: [PATCH 0618/3768] Add documentation for Testament (#15344) --- doc/testament.rst | 187 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 doc/testament.rst diff --git a/doc/testament.rst b/doc/testament.rst new file mode 100644 index 000000000..82dfb75bd --- /dev/null +++ b/doc/testament.rst @@ -0,0 +1,187 @@ +Testament is an advanced automatic unittests runner for Nim tests, is used for the development of Nim itself, +offers process isolation for your tests, it can generate statistics about test cases, +supports multiple targets (C, C++, ObjectiveC, JavaScript, etc), +simulated `Dry-Runs `_, +has logging, can generate HTML reports, skip tests from a file and more, +so can be useful to run your tests, even the most complex ones. + + +Test files location +=================== + +By default Testament looks for test files on ``"./tests/*.nim"``, +you can overwrite this pattern glob using ``pattern ``, +the default working directory path can be changed using ``--directory:"folder/subfolder/"``. + +Testament uses the nim compiler on ``PATH`` you can change that using ``--nim:"folder/subfolder/nim"``, +running JavaScript tests with ``--targets:"js"`` requires a working NodeJS on ``PATH``. + + +Options +======= + +* ``--print`` Also print results to the console +* ``--simulate`` See what tests would be run but don't run them (for debugging) +* ``--failing`` Only show failing/ignored tests +* ``--targets:"c c++ js objc"`` Run tests for specified targets (default: all) +* ``--nim:path`` Use a particular nim executable (default: ``$PATH/nim``) +* ``--directory:dir`` Change to directory dir before reading the tests or doing anything else. +* ``--colors:on|off`` Turn messages coloring on|off. +* ``--backendLogging:on|off`` Disable or enable backend logging. By default turned on. +* ``--skipFrom:file`` Read tests to skip from ``file`` - one test per line, # comments ignored + + +Running a single test +===================== + +This is a minimal example to understand the basics, +not very useful for production, but easy to understand: + +.. code:: + + $ mkdir tests + $ echo "assert 42 == 42" > tests/test0.nim + $ testament run test0.nim + PASS: tests/test0.nim C ( 0.2 sec) + + $ testament r test0 + PASS: tests/test0.nim C ( 0.2 sec) + + $ + + +Running all tests from a directory +================================== + +.. code:: + + $ testament pattern "tests/*.nim" + + +HTML Reports +============ + +Generate HTML Reports ``testresults.html`` from unittests, +you have to run at least 1 test *before* generating a report: + +.. code:: + + $ testament html + + +Writing Unitests +================ + +Example "template" **to edit** and write a Testament unittest: + +.. code-block:: nim + + discard """ + + action: "run" # What to do, one of "compile" OR "run". + + exitcode: 0 # This is the Exit Code the test should return, zero typically. + + output: "" # This is the Standard Output the test should print, if any. + + input: "" # This is the Standard Input the test should take, if any. + + errormsg: "" # Error message the test should print, if any. + + batchable: true # Can be run in batch mode, or not. + + joinable: true # Can be run Joined with other tests to run all togheter, or not. + + valgrind: false # Can use Valgrind to check for memory leaks, or not (Linux 64Bit only). + + cmd: "c -r $file" # Command the test should use to run. + + maxcodesize: 666 # Maximum generated temporary intermediate code file size for the test. + + timeout: 666 # Timeout microseconds to run the test. + + target: "c js" # Targets to run the test into (C, C++, JavaScript, etc). + + disabled: "bsd" # Disable the test by condition, here BSD is disabled just as an example. + + """ + assert true + assert 42 == 42, "Assert error message" + + +* As you can see the "Spec" is just a ``discard """ """``. +* Spec has sane defaults, so you dont need to provide them all, any simple assert will work Ok. +* `This is not the full spec of Testament, check the Testament Spec on GitHub, see parseSpec(). `_ +* `Nim itself uses Testament, so theres plenty of test examples. `_ +* Has some built-in CI compatibility, like Azure Pipelines, etc. +* `Testament supports inlined error messages on Unittests, basically comments with the expected error directly on the code. `_ + + +Unitests Examples +================= + +Expected to fail: + +.. code-block:: nim + + discard """ + errormsg: "undeclared identifier: 'not_defined'" + """ + assert not_defined == "not_defined", "not_defined is not defined" + +Non-Zero exit code: + +.. code-block:: nim + + discard """ + exitcode: 1 + """ + quit "Non-Zero exit code", 1 + +Standard output checking: + +.. code-block:: nim + + discard """ + + output: ''' + 0 + 1 + 2 + 3 + 4 + 5 + ''' + + """ + for i in 0..5: echo i + +JavaScript tests: + +.. code-block:: nim + + discard """ + target: "js" + """ + when defined(js): + import jsconsole + console.log("My Frontend Project") + +Compile time tests: + +.. code-block:: nim + + discard """ + action: "compile" + """ + static: assert 9 == 9, "Compile time assert" + +Tests without Spec: + +.. code-block:: nim + + assert 1 == 1 + + +See also: +* `Unittest `_ From d938c6411e399ee1c936fa7b9ee3a1b53ab65791 Mon Sep 17 00:00:00 2001 From: cooldome Date: Wed, 16 Sep 2020 20:37:23 +0100 Subject: [PATCH 0619/3768] fix #15326 (#15341) --- compiler/semtypes.nim | 118 +++++++++++++++++++++------------------ tests/macros/tmacro2.nim | 8 +++ 2 files changed, 71 insertions(+), 55 deletions(-) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index d85008342..4d912a46f 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -2055,72 +2055,80 @@ proc semGenericConstraints(c: PContext, x: PType): PType = result = newTypeWithSons(c, tyGenericParam, @[x]) proc semGenericParamList(c: PContext, n: PNode, father: PType = nil): PNode = + + template addSym(result: PNode, s: PSym): untyped = + if father != nil: addSonSkipIntLit(father, s.typ) + if sfGenSym notin s.flags: addDecl(c, s) + result.add newSymNode(s) + result = copyNode(n) if n.kind != nkGenericParams: illFormedAst(n, c.config) return for i in 0.. Date: Wed, 16 Sep 2020 23:24:17 +0200 Subject: [PATCH 0620/3768] Revert "Introduce explicit copy (#15330)" (#15346) This reverts commit a3e9cc52343a54cadc7b77b783e1c8b6ba2b327f. --- compiler/injectdestructors.nim | 2 +- lib/system.nim | 5 ----- tests/arc/tcopytosink_warning.nim | 22 ---------------------- 3 files changed, 1 insertion(+), 28 deletions(-) delete mode 100644 tests/arc/tcopytosink_warning.nim diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index de28c9f70..9d1f43595 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -408,7 +408,7 @@ proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode = if isLValue(n) and not isCapturedVar(n) and n.typ.skipTypes(abstractInst).kind != tyRef and c.inSpawn == 0: message(c.graph.config, n.info, hintPerformance, ("passing '$1' to a sink parameter introduces an implicit copy; " & - "if possible, rearrange your program's control flow to prevent it or use 'copy($1)' to hint the compiler it is intentional") % $n) + "if possible, rearrange your program's control flow to prevent it") % $n) else: if c.graph.config.selectedGC in {gcArc, gcOrc}: assert(not containsGarbageCollectedRef(n.typ)) diff --git a/lib/system.nim b/lib/system.nim index dbe0f11f7..c05ca24dc 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -264,11 +264,6 @@ proc move*[T](x: var T): T {.magic: "Move", noSideEffect.} = result = x wasMoved(x) -func copy*[T](x: T): T {.inline.} = - ## make explicit copy of the argument `x`, used to signal to the compiler - ## the copy is intentional - result = x - type range*[T]{.magic: "Range".} ## Generic type to construct range types. array*[I, T]{.magic: "Array".} ## Generic type to construct diff --git a/tests/arc/tcopytosink_warning.nim b/tests/arc/tcopytosink_warning.nim deleted file mode 100644 index 8ae36386a..000000000 --- a/tests/arc/tcopytosink_warning.nim +++ /dev/null @@ -1,22 +0,0 @@ -discard """ - cmd: "nim c --gc:arc $file" - nimout: '''tcopytosink_warning.nim(17, 7) Hint: myhint [User] -tcopytosink_warning.nim(19, 9) Hint: passing 'x' to a sink parameter introduces an implicit copy; if possible, rearrange your program's control flow to prevent it or use 'copy(x)' to hint the compiler it is intentional [Performance] -''' - output: "x" -""" -import macros - -proc test(v: var seq[string], x: sink string) = - v.add x - -var v = @["a", "b", "c"] -var x = "x" - -static: - hint("myhint") -test(v, copy(x)) # no warning -test(v, x) # produces warning - -echo x # use after sink - From cbd78ec1f68e78a88e4a83a5009bc4c8c28c79b3 Mon Sep 17 00:00:00 2001 From: Miran Date: Thu, 17 Sep 2020 19:55:18 +0200 Subject: [PATCH 0621/3768] close #6071, remove the mentions of deprecated `docSeeSrcUrl` (#15350) --- config/nimdoc.cfg | 4 ++-- doc/docgen.rst | 7 ++----- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/config/nimdoc.cfg b/config/nimdoc.cfg index 3560c0d65..9a56fdaae 100644 --- a/config/nimdoc.cfg +++ b/config/nimdoc.cfg @@ -55,13 +55,13 @@ doc.item.toc = """ """ # HTML rendered for doc.item's seeSrc variable. Note that this will render to -# the empty string if you don't pass anything through --docSeeSrcURL. Available +# the empty string if you don't pass anything through --git.url. Available # substitutaion variables here are: # * $commit: branch/commit to use in source link. # * $devel: branch to use in edit link. # * $path: relative path to the file being processed. # * $line: line of the item in the original source file. -# * $url: whatever you did pass through the --docSeeSrcUrl switch (which also +# * $url: whatever you did pass through the --git.url switch (which also # gets variables path/line replaced!) doc.item.seesrc = """   filename.nim @@ -345,9 +343,8 @@ but can have up to four (additional columns are ignored). The content of these columns is: 1. Mandatory term being indexed. Terms can include quoting according to - Nim's rules (eg. \`^\`). -2. Base filename plus anchor hyperlink (eg. - ``algorithm.html#*,int,SortOrder``). + Nim's rules (e.g. \`^\`). +2. Base filename plus anchor hyperlink (e.g. ``algorithm.html#*,int,SortOrder``). 3. Optional human readable string to display as hyperlink. If the value is not present or is the empty string, the hyperlink will be rendered using the term. Prefix whitespace indicates that this entry is From c57023f3241e99910d44e91330a381e41193047f Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Fri, 18 Sep 2020 16:02:31 +0800 Subject: [PATCH 0622/3768] string is not nil anymore (#15352) --- lib/pure/collections/critbits.nim | 5 +---- lib/pure/strtabs.nim | 6 ++---- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/lib/pure/collections/critbits.nim b/lib/pure/collections/critbits.nim index 91c113988..d25e31d66 100644 --- a/lib/pure/collections/critbits.nim +++ b/lib/pure/collections/critbits.nim @@ -281,10 +281,7 @@ proc `[]=`*[T](c: var CritBitTree[T], key: string, val: T) = template get[T](c: CritBitTree[T], key: string): T = let n = rawGet(c, key) if n == nil: - when compiles($key): - raise newException(KeyError, "key not found: " & $key) - else: - raise newException(KeyError, "key not found") + raise newException(KeyError, "key not found: " & key) n.val diff --git a/lib/pure/strtabs.nim b/lib/pure/strtabs.nim index 864db148f..d9f6bbc54 100644 --- a/lib/pure/strtabs.nim +++ b/lib/pure/strtabs.nim @@ -141,10 +141,8 @@ template get(t: StringTableRef, key: string) = var index = rawGet(t, key) if index >= 0: result = t.data[index].val else: - when compiles($key): - raise newException(KeyError, "key not found: " & $key) - else: - raise newException(KeyError, "key not found") + raise newException(KeyError, "key not found: " & key) + proc len*(t: StringTableRef): int {.rtlFunc, extern: "nst$1".} = ## Returns the number of keys in `t`. From e56d50d747451ea222617902ff70f31b4bb3123c Mon Sep 17 00:00:00 2001 From: jcosborn Date: Fri, 18 Sep 2020 04:21:37 -0500 Subject: [PATCH 0623/3768] fix infinite recursion in typeRel (#15241) * fix generic constraints Co-authored-by: James Osborn --- compiler/sigmatch.nim | 115 ++++++++++++++------------- tests/overload/toverload_various.nim | 45 +++++++++++ 2 files changed, 104 insertions(+), 56 deletions(-) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 95231744c..1441203e8 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -82,6 +82,7 @@ type TTypeRelFlag* = enum trDontBind trNoCovariance + trBindGenericParam # bind tyGenericParam even with trDontBind TTypeRelFlags* = set[TTypeRelFlag] @@ -1034,7 +1035,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, if aOrig.kind == tyInferred: let prev = aOrig.previouslyInferred if prev != nil: - return typeRel(c, f, prev) + return typeRel(c, f, prev, flags) else: var candidate = f @@ -1055,7 +1056,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, else: discard - result = typeRel(c, aOrig.base, candidate) + result = typeRel(c, aOrig.base, candidate, flags) if result != isNone: c.inferredTypes.add aOrig aOrig.add candidate @@ -1072,16 +1073,16 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, # situation when nkDotExpr are rotated to nkDotCalls if aOrig.kind in {tyAlias, tySink}: - return typeRel(c, f, lastSon(aOrig)) + return typeRel(c, f, lastSon(aOrig), flags) if a.kind == tyGenericInst and skipTypes(f, {tyVar, tyLent, tySink}).kind notin { tyGenericBody, tyGenericInvocation, tyGenericInst, tyGenericParam} + tyTypeClasses: - return typeRel(c, f, lastSon(a)) + return typeRel(c, f, lastSon(a), flags) if a.isResolvedUserTypeClass: - return typeRel(c, f, a.lastSon) + return typeRel(c, f, a.lastSon, flags) template bindingRet(res) = if doBind: @@ -1092,7 +1093,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, template considerPreviousT(body: untyped) = var prev = PType(idTableGet(c.bindings, f)) if prev == nil: body - else: return typeRel(c, prev, a) + else: return typeRel(c, prev, a, flags) case a.kind of tyOr: @@ -1126,7 +1127,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, # seq[float] matches the first, but not the second # we must turn the problem around: # is number a subset of int? - return typeRel(c, a.lastSon, f.lastSon) + return typeRel(c, a.lastSon, f.lastSon, flags) else: # negative type classes are essentially infinite, @@ -1158,7 +1159,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, of tyRange: if a.kind == f.kind: if f.base.kind == tyNone: return isGeneric - result = typeRel(c, base(f), base(a)) + result = typeRel(c, base(f), base(a), flags) # bugfix: accept integer conversions here #if result < isGeneric: result = isNone if result notin {isNone, isGeneric}: @@ -1189,7 +1190,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, of tyFloat64: result = handleFloatRange(f, a) of tyFloat128: result = handleFloatRange(f, a) of tyVar, tyLent: - if aOrig.kind == f.kind: result = typeRel(c, f.base, aOrig.base) + if aOrig.kind == f.kind: result = typeRel(c, f.base, aOrig.base, flags) else: result = typeRel(c, f.base, aOrig, flags + {trNoCovariance}) subtypeCheck() of tyArray: @@ -1211,7 +1212,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, if f[0].kind != tyGenericParam and aa.kind == tyEmpty: result = isGeneric else: - result = typeRel(c, ff, aa) + result = typeRel(c, ff, aa, flags) if result < isGeneric: if nimEnableCovariance and @@ -1232,7 +1233,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, else: discard of tyUncheckedArray: if a.kind == tyUncheckedArray: - result = typeRel(c, base(f), base(a)) + result = typeRel(c, base(f), base(a), flags) if result < isGeneric: result = isNone else: discard of tyOpenArray, tyVarargs: @@ -1240,13 +1241,13 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, # handle varargs[stmt] which is the same as varargs[typed]: if f.kind == tyVarargs: if tfVarargs in a.flags: - return typeRel(c, f.base, a.lastSon) + return typeRel(c, f.base, a.lastSon, flags) if f[0].kind == tyTyped: return template matchArrayOrSeq(aBase: PType) = let ff = f.base let aa = aBase - let baseRel = typeRel(c, ff, aa) + let baseRel = typeRel(c, ff, aa, flags) if baseRel >= isGeneric: result = isConvertible elif nimEnableCovariance and @@ -1257,7 +1258,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, case a.kind of tyOpenArray, tyVarargs: - result = typeRel(c, base(f), base(a)) + result = typeRel(c, base(f), base(a), flags) if result < isGeneric: result = isNone of tyArray: if (f[0].kind != tyGenericParam) and (a[1].kind == tyEmpty): @@ -1272,7 +1273,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, if f[0].kind == tyChar: result = isConvertible elif f[0].kind == tyGenericParam and a.len > 0 and - typeRel(c, base(f), base(a)) >= isGeneric: + typeRel(c, base(f), base(a), flags) >= isGeneric: result = isConvertible else: discard of tySequence: @@ -1283,7 +1284,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, else: let ff = f[0] let aa = a[0] - result = typeRel(c, ff, aa) + result = typeRel(c, ff, aa, flags) if result < isGeneric: if nimEnableCovariance and trNoCovariance notin flags and @@ -1302,7 +1303,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, if f[0].kind == tyNone: result = isGeneric else: - result = typeRel(c, f[0], x) + result = typeRel(c, f[0], x, flags) if result < isGeneric: result = isNone elif a.kind == tyGenericParam: result = isGeneric @@ -1329,16 +1330,16 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, if a.kind == tyDistinct: if sameDistinctTypes(f, a): result = isEqual #elif f.base.kind == tyAnything: result = isGeneric # issue 4435 - elif c.coerceDistincts: result = typeRel(c, f.base, a) + elif c.coerceDistincts: result = typeRel(c, f.base, a, flags) elif a.kind == tyNil and f.base.kind in NilableTypes: result = f.allowsNil # XXX remove this typing rule, it is not in the spec - elif c.coerceDistincts: result = typeRel(c, f.base, a) + elif c.coerceDistincts: result = typeRel(c, f.base, a, flags) of tySet: if a.kind == tySet: if f[0].kind != tyGenericParam and a[0].kind == tyEmpty: result = isSubtype else: - result = typeRel(c, f[0], a[0]) + result = typeRel(c, f[0], a[0], flags) if result <= isConvertible: result = isNone # BUGFIX! of tyPtr, tyRef: @@ -1347,7 +1348,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, # ptr[R, T] can be passed to ptr[T], but not the other way round: if a.len < f.len: return isNone for i in 0.. isGeneric: result = isGeneric elif c.isNoCall: - if doBind: + if doBindGP: let concrete = concreteType(c, a, f) if concrete == nil: return isNone put(c, f, concrete) @@ -1700,8 +1702,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, # check if 'T' has a constraint as in 'proc p[T: Constraint](x: T)' if f.len > 0 and f[0].kind != tyNone: let oldInheritancePenalty = c.inheritancePenalty - result = typeRel(c, f[0], a, flags + {trDontBind}) - if doBind and result notin {isNone, isGeneric}: + result = typeRel(c, f[0], a, flags + {trDontBind,trBindGenericParam}) + if doBindGP and result notin {isNone, isGeneric}: let concrete = concreteType(c, a, f) if concrete == nil: return isNone put(c, f, concrete) @@ -1729,7 +1731,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, concrete = concreteType(c, a, f) if concrete == nil: return isNone - if doBind: + if doBindGP: put(c, f, concrete) elif result > isGeneric: result = isGeneric @@ -1738,14 +1740,14 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, elif x.kind == tyGenericParam: result = isGeneric else: - result = typeRel(c, x, a) # check if it fits + result = typeRel(c, x, a, flags) # check if it fits if result > isGeneric: result = isGeneric of tyStatic: let prev = PType(idTableGet(c.bindings, f)) if prev == nil: if aOrig.kind == tyStatic: if f.base.kind != tyNone: - result = typeRel(c, f.base, a) + result = typeRel(c, f.base, a, flags) if result != isNone and f.n != nil: if not exprStructuralEquivalent(f.n, aOrig.n): result = isNone @@ -1753,7 +1755,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, result = isGeneric if result != isNone: put(c, f, aOrig) elif aOrig.n != nil and aOrig.n.typ != nil: - result = if f.base.kind != tyNone: typeRel(c, f.lastSon, aOrig.n.typ) + result = if f.base.kind != tyNone: + typeRel(c, f.lastSon, aOrig.n.typ, flags) else: isGeneric if result != isNone: var boundType = newTypeWithSons(c.c, tyStatic, @[aOrig.n.typ]) @@ -1763,22 +1766,22 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, result = isNone elif prev.kind == tyStatic: if aOrig.kind == tyStatic: - result = typeRel(c, prev.lastSon, a) + result = typeRel(c, prev.lastSon, a, flags) if result != isNone and prev.n != nil: if not exprStructuralEquivalent(prev.n, aOrig.n): result = isNone else: result = isNone else: # XXX endless recursion? - #result = typeRel(c, prev, aOrig) + #result = typeRel(c, prev, aOrig, flags) result = isNone of tyInferred: let prev = f.previouslyInferred if prev != nil: - result = typeRel(c, prev, a) + result = typeRel(c, prev, a, flags) else: - result = typeRel(c, f.base, a) + result = typeRel(c, f.base, a, flags) if result != isNone: c.inferredTypes.add f f.add a @@ -1798,15 +1801,15 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, elif f.base.kind == tyNone: result = isGeneric else: - result = typeRel(c, f.base, a.base) + result = typeRel(c, f.base, a.base, flags) if result != isNone: put(c, f, a) else: if tfUnresolved in f.flags: - result = typeRel(c, prev.base, a) + result = typeRel(c, prev.base, a, flags) elif a.kind == tyTypeDesc: - result = typeRel(c, prev.base, a.base) + result = typeRel(c, prev.base, a.base, flags) else: result = isNone @@ -1824,15 +1827,15 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, let reevaluated = tryResolvingStaticExpr(c, f.n) case reevaluated.typ.kind of tyTypeDesc: - result = typeRel(c, a, reevaluated.typ.base) + result = typeRel(c, a, reevaluated.typ.base, flags) of tyStatic: - result = typeRel(c, a, reevaluated.typ.base) + result = typeRel(c, a, reevaluated.typ.base, flags) if result != isNone and reevaluated.typ.n != nil: if not exprStructuralEquivalent(aOrig.n, reevaluated.typ.n): result = isNone else: # bug #14136: other types are just like 'tyStatic' here: - result = typeRel(c, a, reevaluated.typ) + result = typeRel(c, a, reevaluated.typ, flags) if result != isNone and reevaluated.typ.n != nil: if not exprStructuralEquivalent(aOrig.n, reevaluated.typ.n): result = isNone diff --git a/tests/overload/toverload_various.nim b/tests/overload/toverload_various.nim index 132c3be4b..f913ce3ee 100644 --- a/tests/overload/toverload_various.nim +++ b/tests/overload/toverload_various.nim @@ -425,6 +425,7 @@ block: template t0(x: Bar[int]): int = 1 template t0(x: Foo[bool or int]): int = 10 template t0(x: Bar[bool or int]): int = 11 + #template t0[T:bool or int](x: Bar[T]): int = 11 template t0[T](x: Foo[T]): int = 20 template t0[T](x: Bar[T]): int = 21 proc p0(x: Foo[int]): int = 0 @@ -461,3 +462,47 @@ block: doAssert(p0(g) == 20) doAssert(p0(h) == 21) doAssert(p0(i) == 21) + + #type + # f0 = proc(x:Foo) + + +block: + type + TilesetCT[n: static[int]] = distinct int + TilesetRT = int + Tileset = TilesetCT | TilesetRT + + func prepareTileset(tileset: var Tileset) = discard + + func prepareTileset(tileset: Tileset): Tileset = + result = tileset + result.prepareTileset + + var parsedTileset: TilesetRT + prepareTileset(parsedTileset) + + +block: + proc p1[T,U: SomeInteger|SomeFloat](x: T, y: U): int|float = + when T is SomeInteger and U is SomeInteger: + result = int(x) + int(y) + else: + result = float(x) + float(y) + doAssert(p1(1,2) == 3) + doAssert(p1(1.0,2) == 3.0) + doAssert(p1(1,2.0) == 3.0) + doAssert(p1(1.0,2.0) == 3.0) + + type Foo[T,U] = U + template F[T,U](t: typedesc[T], x: U): untyped = Foo[T,U](x) + proc p2[T; U,V:Foo[T,SomeNumber]](x: U, y: V): T = + T(x) + T(y) + #proc p2[T; U:Foo[T,SomeNumber], V:Foo[not T,SomeNumber]](x: U, y: V): T = + # T(x) + T(y) + doAssert(p2(F(int,1),F(int,2)) == 3) + doAssert(p2(F(float,1),F(float,2)) == 3.0) + doAssert(p2(F(float,1),F(float,2.0)) == 3.0) + doAssert(p2(F(float,1.0),F(float,2)) == 3.0) + doAssert(p2(F(float,1.0),F(float,2.0)) == 3.0) + #doAssert(p2(F(float,1),F(int,2.0)) == 3.0) From 2671efab78e0c1a56d5f527b6a25f658575ca9a0 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 18 Sep 2020 11:22:34 +0200 Subject: [PATCH 0624/3768] async: minor refactorings (#15354) --- lib/pure/asyncdispatch.nim | 29 +++++++++---------- lib/pure/asyncfutures.nim | 34 ++++++---------------- lib/pure/asyncstreams.nim | 36 ++++++++++++------------ lib/pure/ioselects/ioselectors_epoll.nim | 4 +-- lib/pure/selectors.nim | 2 +- 5 files changed, 43 insertions(+), 62 deletions(-) diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 41d67d172..6f38583d8 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -290,7 +290,7 @@ when defined(windows) or defined(nimdoc): var gDisp{.threadvar.}: owned PDispatcher ## Global dispatcher - proc setGlobalDispatcher*(disp: owned PDispatcher) = + proc setGlobalDispatcher*(disp: sink PDispatcher) = if not gDisp.isNil: assert gDisp.callbacks.len == 0 gDisp = disp @@ -1217,10 +1217,12 @@ else: withData(selector, fd.int, fdData): case event of Event.Read: - shallowCopy(curList, fdData.readList) + #shallowCopy(curList, fdData.readList) + curList = move fdData.readList fdData.readList = newSeqOfCap[Callback](InitCallbackListSize) of Event.Write: - shallowCopy(curList, fdData.writeList) + #shallowCopy(curList, fdData.writeList) + curList = move fdData.writeList fdData.writeList = newSeqOfCap[Callback](InitCallbackListSize) else: assert false, "Cannot process callbacks for " & $event @@ -1232,8 +1234,7 @@ else: for cb in curList: if eventsExtinguished: newList.add(cb) - continue - if not cb(fd): + elif not cb(fd): # Callback wants to be called again. newList.add(cb) # This callback has returned with EAGAIN, so we don't need to @@ -1259,15 +1260,15 @@ else: result.readCbListCount = -1 result.writeCbListCount = -1 - template processCustomCallbacks(ident: untyped) = + proc processCustomCallbacks(p: PDispatcher; fd: AsyncFD) = # Process pending custom event callbacks. Custom events are # {Event.Timer, Event.Signal, Event.Process, Event.Vnode}. # There can be only one callback registered with one descriptor, # so there is no need to iterate over list. var curList: seq[Callback] - withData(p.selector, ident.int, adata) do: - shallowCopy(curList, adata.readList) + withData(p.selector, fd.int, adata) do: + curList = move adata.readList adata.readList = newSeqOfCap[Callback](InitCallbackListSize) let newLength = len(curList) @@ -1277,7 +1278,7 @@ else: if not cb(fd.AsyncFD): newList.add(cb) - withData(p.selector, ident.int, adata) do: + withData(p.selector, fd.int, adata) do: # descriptor still present in queue. adata.readList = newList & adata.readList if len(adata.readList) == 0: @@ -1308,10 +1309,6 @@ else: proc runOnce(timeout = 500): bool = let p = getGlobalDispatcher() - when ioselSupportedPlatform: - let customSet = {Event.Timer, Event.Signal, Event.Process, - Event.Vnode} - if p.selector.isEmpty() and p.timers.len == 0 and p.callbacks.len == 0: raise newException(ValueError, "No handles or timers registered in dispatcher.") @@ -1346,9 +1343,11 @@ else: result = true when ioselSupportedPlatform: + const customSet = {Event.Timer, Event.Signal, Event.Process, + Event.Vnode} if (customSet * events) != {}: isCustomEvent = true - processCustomCallbacks(fd) + processCustomCallbacks(p, fd) result = true # because state `data` can be modified in callback we need to update @@ -1612,7 +1611,7 @@ proc drain*(timeout = 500) = var curTimeout = timeout let start = now() while hasPendingOperations(): - discard runOnce(curTimeout) + discard runOnce(curTimeout) curTimeout -= (now() - start).inMilliseconds.int if curTimeout < 0: break diff --git a/lib/pure/asyncfutures.nim b/lib/pure/asyncfutures.nim index c1e2770bb..97abf72c9 100644 --- a/lib/pure/asyncfutures.nim +++ b/lib/pure/asyncfutures.nim @@ -157,33 +157,15 @@ proc checkFinished[T](future: Future[T]) = raise err proc call(callbacks: var CallbackList) = - when not defined(nimV2): - # strictly speaking a little code duplication here, but we strive - # to minimize regressions and I'm not sure I got the 'nimV2' logic - # right: - var current = callbacks - while true: - if not current.function.isNil: - callSoon(current.function) - - if current.next.isNil: - break - else: - current = current.next[] - else: - var currentFunc = unown callbacks.function - var currentNext = unown callbacks.next - - while true: - if not currentFunc.isNil: - callSoon(currentFunc) - - if currentNext.isNil: - break - else: - currentFunc = currentNext.function - currentNext = unown currentNext.next + var current = callbacks + while true: + if not current.function.isNil: + callSoon(current.function) + if current.next.isNil: + break + else: + current = current.next[] # callback will be called only once, let GC collect them now callbacks.next = nil callbacks.function = nil diff --git a/lib/pure/asyncstreams.nim b/lib/pure/asyncstreams.nim index 393262c4f..7ffde9c10 100644 --- a/lib/pure/asyncstreams.nim +++ b/lib/pure/asyncstreams.nim @@ -54,7 +54,8 @@ proc `callback=`*[T](future: FutureStream[T], ## ## If the future stream already has data or is finished then ``cb`` will be ## called immediately. - future.cb = proc () = cb(future) + proc named() = cb(future) + future.cb = named if future.queue.len > 0 or future.finished: callSoon(future.cb) @@ -90,27 +91,26 @@ proc read*[T](future: FutureStream[T]): owned(Future[(bool, T)]) = ## ``FutureStream``. var resFut = newFuture[(bool, T)]("FutureStream.take") let savedCb = future.cb - var newCb = - proc (fs: FutureStream[T]) = - # Exit early if `resFut` is already complete. (See #8994). - if resFut.finished: return + proc newCb(fs: FutureStream[T]) = + # Exit early if `resFut` is already complete. (See #8994). + if resFut.finished: return - # We don't want this callback called again. - #future.cb = nil + # We don't want this callback called again. + #future.cb = nil - # The return value depends on whether the FutureStream has finished. - var res: (bool, T) - if finished(fs): - # Remember, this callback is called when the FutureStream is completed. - res[0] = false - else: - res[0] = true - res[1] = fs.queue.popFirst() + # The return value depends on whether the FutureStream has finished. + var res: (bool, T) + if finished(fs): + # Remember, this callback is called when the FutureStream is completed. + res[0] = false + else: + res[0] = true + res[1] = fs.queue.popFirst() - resFut.complete(res) + resFut.complete(res) - # If the saved callback isn't nil then let's call it. - if not savedCb.isNil: savedCb() + # If the saved callback isn't nil then let's call it. + if not savedCb.isNil: savedCb() if future.queue.len > 0 or future.finished: newCb(future) diff --git a/lib/pure/ioselects/ioselectors_epoll.nim b/lib/pure/ioselects/ioselectors_epoll.nim index 1b317f8dc..3dcf547bd 100644 --- a/lib/pure/ioselects/ioselectors_epoll.nim +++ b/lib/pure/ioselects/ioselectors_epoll.nim @@ -514,7 +514,7 @@ template withData*[T](s: Selector[T], fd: SocketHandle|int, value, let fdi = int(fd) s.checkFd(fdi) if fdi in s: - var value = addr(s.getData(fdi)) + var value = addr(s.fds[fdi].data) body template withData*[T](s: Selector[T], fd: SocketHandle|int, value, body1, @@ -523,7 +523,7 @@ template withData*[T](s: Selector[T], fd: SocketHandle|int, value, body1, let fdi = int(fd) s.checkFd(fdi) if fdi in s: - var value = addr(s.getData(fdi)) + var value = addr(s.fds[fdi].data) body1 else: body2 diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim index f3726efaa..e89cc7447 100644 --- a/lib/pure/selectors.nim +++ b/lib/pure/selectors.nim @@ -255,7 +255,7 @@ else: IOSelectorsException* = object of CatchableError ReadyKey* = object - fd* : int + fd*: int events*: set[Event] errorCode*: OSErrorCode From 59b7857167ad458290dc57c4e39ca564b3080e52 Mon Sep 17 00:00:00 2001 From: narimiran Date: Fri, 18 Sep 2020 11:50:15 +0200 Subject: [PATCH 0625/3768] bump NimVersion to 1.3.7 --- lib/system.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/system.nim b/lib/system.nim index c05ca24dc..362b5b2b9 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2079,7 +2079,7 @@ const ## is the minor number of Nim's version. ## Odd for devel, even for releases. - NimPatch* {.intdefine.}: int = 5 + NimPatch* {.intdefine.}: int = 7 ## is the patch number of Nim's version. ## Odd for devel, even for releases. From d19316bbb986a7dd1d6d091173963f6e74c65991 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 18 Sep 2020 11:55:58 +0200 Subject: [PATCH 0626/3768] more ORC bugfixes (#15355) * introduced --define:nimArcIds * ORC: bugfixes --- lib/system/arc.nim | 6 +++-- lib/system/orc.nim | 37 ++++++++++++++++++++++++++----- tests/arc/tasyncleak.nim | 2 +- tests/arc/tasyncleak3.nim | 46 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+), 9 deletions(-) create mode 100644 tests/arc/tasyncleak3.nim diff --git a/lib/system/arc.nim b/lib/system/arc.nim index 68376cdab..3cbd02806 100644 --- a/lib/system/arc.nim +++ b/lib/system/arc.nim @@ -54,7 +54,7 @@ type when defined(gcOrc): rootIdx: int # thanks to this we can delete potential cycle roots # in O(1) without doubly linked lists - when defined(nimArcDebug): + when defined(nimArcDebug) or defined(nimArcIds): refId: int Cell = ptr RefHeader @@ -72,6 +72,8 @@ when defined(nimArcDebug): var gRefId: int var freedCells: CellSet +elif defined(nimArcIds): + var gRefId: int proc nimNewObj(size: int): pointer {.compilerRtl.} = let s = size + sizeof(RefHeader) @@ -85,7 +87,7 @@ proc nimNewObj(size: int): pointer {.compilerRtl.} = result = allocShared0(s) +! sizeof(RefHeader) else: result = alloc0(s) +! sizeof(RefHeader) - when defined(nimArcDebug): + when defined(nimArcDebug) or defined(nimArcIds): head(result).refId = gRefId atomicInc gRefId when traceCollector: diff --git a/lib/system/orc.nim b/lib/system/orc.nim index 4ba462c1f..5deed1d14 100644 --- a/lib/system/orc.nim +++ b/lib/system/orc.nim @@ -41,6 +41,7 @@ proc nimIncRefCyclic(p: pointer) {.compilerRtl, inl.} = let h = head(p) inc h.rc, rcIncrement #h.setColor colPurple # mark as potential cycle! + h.setColor colBlack const useJumpStack = false # for thavlak the jump stack doesn't improve the performance at all @@ -101,6 +102,11 @@ proc nimTraceRefDyn(q: pointer; env: pointer) {.compilerRtl.} = var j = cast[ptr GcEnv](env) j.traceStack.add(head p[], cast[ptr PNimTypeV2](p[])[]) +template orcAssert(cond, msg) = + if not cond: + cfprintf(cstderr, "[Bug!] %s\n", msg) + quit 1 + var roots {.threadvar.}: CellSeq @@ -115,6 +121,11 @@ proc unregisterCycle(s: Cell) = roots.d[idx][0].rootIdx = idx dec roots.len +when false: + proc writeCell(msg: cstring; s: Cell; desc: PNimTypeV2) = + cfprintf(cstderr, "%s %s %ld root index: %ld; RC: %ld; color: %ld\n", + msg, desc.name, s.refId, s.rootIdx, s.rc shr rcShift, s.color) + proc scanBlack(s: Cell; desc: PNimTypeV2; j: var GcEnv) = #[ proc scanBlack(s: Cell) = @@ -126,14 +137,17 @@ proc scanBlack(s: Cell; desc: PNimTypeV2; j: var GcEnv) = ]# debug "scanBlack", s s.setColor colBlack + let until = j.traceStack.len trace(s, desc, j) - while j.traceStack.len > 0: + #writeCell("root still alive", s, desc) + while j.traceStack.len > until: let (t, desc) = j.traceStack.pop() inc t.rc, rcIncrement debug "incRef", t if t.color != colBlack: t.setColor colBlack trace(t, desc, j) + #writeCell("child still alive", t, desc) proc markGray(s: Cell; desc: PNimTypeV2; j: var GcEnv) = #[ @@ -148,6 +162,7 @@ proc markGray(s: Cell; desc: PNimTypeV2; j: var GcEnv) = if s.color != colGray: s.setColor colGray inc j.touched + orcAssert(j.traceStack.len == 0, "markGray: trace stack not empty") trace(s, desc, j) while j.traceStack.len > 0: let (t, desc) = j.traceStack.pop() @@ -195,6 +210,7 @@ proc scan(s: Cell; desc: PNimTypeV2; j: var GcEnv) = when traceCollector: cprintf("[jump stack] %p %ld\n", t, t.rc shr rcShift) + orcAssert(j.traceStack.len == 0, "scan: trace stack not empty") s.setColor(colWhite) trace(s, desc, j) while j.traceStack.len > 0: @@ -236,6 +252,8 @@ proc collectWhite(s: Cell; desc: PNimTypeV2; j: var GcEnv) = free(s) # watch out, a bug here! ]# if s.color == colWhite and (s.rc and isCycleCandidate) == 0: + orcAssert(j.traceStack.len == 0, "collectWhite: trace stack not empty") + s.setColor(colBlack) j.toFree.add(s, desc) trace(s, desc, j) @@ -261,6 +279,7 @@ proc collectCyclesBacon(j: var GcEnv) = collectWhite(s) ]# for i in 0 ..< roots.len: + #writeCell("root", roots.d[i][0], roots.d[i][1]) markGray(roots.d[i][0], roots.d[i][1], j) for i in 0 ..< roots.len: scan(roots.d[i][0], roots.d[i][1], j) @@ -279,7 +298,7 @@ proc collectCyclesBacon(j: var GcEnv) = #roots.len = 0 const - defaultThreshold = 10_000 + defaultThreshold = when defined(nimAdaptiveOrc): 128 else: 10_000 var rootsThreshold = defaultThreshold @@ -309,11 +328,16 @@ proc collectCycles() = # we're effective when we collected 50% or more of the nodes # we touched. If we're effective, we can reset the threshold: if j.freed * 2 >= j.touched: - rootsThreshold = defaultThreshold + when defined(nimAdaptiveOrc): + rootsThreshold = max(rootsThreshold div 2, 16) + else: + rootsThreshold = defaultThreshold + #cfprintf(cstderr, "[collectCycles] freed %ld, touched %ld new threshold %ld\n", j.freed, j.touched, rootsThreshold) elif rootsThreshold < high(int) div 4: rootsThreshold = rootsThreshold * 3 div 2 when false: - cfprintf(cstderr, "[collectCycles] freed %ld new threshold %ld\n", j.freed, rootsThreshold) + cfprintf(cstderr, "[collectCycles] end; freed %ld new threshold %ld touched: %ld mem: %ld\n", j.freed, rootsThreshold, j.touched, + getOccupiedMem()) proc registerCycle(s: Cell; desc: PNimTypeV2) = if roots.len >= rootsThreshold: @@ -328,10 +352,10 @@ proc GC_fullCollect* = ## collector. collectCycles() -proc GC_enableMarkAndSweep() = +proc GC_enableMarkAndSweep*() = rootsThreshold = defaultThreshold -proc GC_disableMarkAndSweep() = +proc GC_disableMarkAndSweep*() = rootsThreshold = high(int) proc rememberCycle(isDestroyAction: bool; s: Cell; desc: PNimTypeV2) {.noinline.} = @@ -345,6 +369,7 @@ proc rememberCycle(isDestroyAction: bool; s: Cell; desc: PNimTypeV2) {.noinline. #s.setColor colGreen # XXX This is wrong! if (s.rc and isCycleCandidate) == 0: s.rc = s.rc or isCycleCandidate + s.setColor colBlack registerCycle(s, desc) proc nimDecRefIsLastCyclicDyn(p: pointer): bool {.compilerRtl, inl.} = diff --git a/tests/arc/tasyncleak.nim b/tests/arc/tasyncleak.nim index 71d62222d..72aa3d4c2 100644 --- a/tests/arc/tasyncleak.nim +++ b/tests/arc/tasyncleak.nim @@ -1,5 +1,5 @@ discard """ - outputsub: "(allocCount: 4016, deallocCount: 4010)" + outputsub: "(allocCount: 4016, deallocCount: 4014)" cmd: "nim c --gc:orc -d:nimAllocStats $file" """ diff --git a/tests/arc/tasyncleak3.nim b/tests/arc/tasyncleak3.nim new file mode 100644 index 000000000..c1510895a --- /dev/null +++ b/tests/arc/tasyncleak3.nim @@ -0,0 +1,46 @@ +discard """ + cmd: "nim c --gc:orc $file" + output: "true" +""" + +import strutils + +type + FutureBase* = ref object of RootObj ## Untyped future. + finished: bool + stackTrace: seq[StackTraceEntry] ## For debugging purposes only. + +proc newFuture*(): FutureBase = + new(result) + result.finished = false + result.stackTrace = getStackTraceEntries() + +type + PDispatcher {.acyclic.} = ref object + +var gDisp: PDispatcher +new gDisp + +proc testCompletion(): FutureBase = + var retFuture = newFuture() + + iterator testCompletionIter(): FutureBase {.closure.} = + retFuture.finished = true + when not defined(nobug): + let disp = gDisp # even worse memory consumption this way... + + var nameIterVar = testCompletionIter + proc testCompletionNimAsyncContinue() {.closure.} = + if not nameIterVar.finished: + discard nameIterVar() + testCompletionNimAsyncContinue() + return retFuture + +proc main = + for i in 0..10_000: + discard testCompletion() + +main() + +GC_fullCollect() +echo getOccupiedMem() < 1024 From 341cd844b282cc6ed3b1fc4c1ef10c38fc06d5e7 Mon Sep 17 00:00:00 2001 From: yatsen1 Date: Fri, 18 Sep 2020 23:17:09 +0800 Subject: [PATCH 0627/3768] fix coro proc crash for stack problem when run long enough than a GC cycle (#7612) (#11410) Co-authored-by: drswinghead Co-authored-by: Clyybber --- lib/pure/coro.nim | 8 ++++++++ lib/system/gc_common.nim | 6 ++++++ 2 files changed, 14 insertions(+) diff --git a/lib/pure/coro.nim b/lib/pure/coro.nim index 6e49b6323..2a702b65b 100644 --- a/lib/pure/coro.nim +++ b/lib/pure/coro.nim @@ -36,6 +36,7 @@ const defaultStackSize = 512 * 1024 proc GC_addStack(bottom: pointer) {.cdecl, importc.} proc GC_removeStack(bottom: pointer) {.cdecl, importc.} proc GC_setActiveStack(bottom: pointer) {.cdecl, importc.} +proc GC_getActiveStack() : pointer {.cdecl, importc.} const CORO_BACKEND_UCONTEXT = 0 @@ -166,6 +167,7 @@ type coroutines: DoublyLinkedList[CoroutinePtr] current: DoublyLinkedNode[CoroutinePtr] loop: Coroutine + ncbottom: pointer # non coroutine stack botttom var ctx {.threadvar.}: CoroutineLoopContext @@ -183,6 +185,7 @@ proc initialize() = ctx.coroutines = initDoublyLinkedList[CoroutinePtr]() ctx.loop = Coroutine() ctx.loop.state = CORO_EXECUTING + ctx.ncbottom = GC_getActiveStack() when coroBackend == CORO_BACKEND_FIBERS: ctx.loop.execContext = ConvertThreadToFiberEx(nil, FIBER_FLAG_FLOAT_SWITCH) @@ -193,6 +196,7 @@ proc switchTo(current, to: CoroutinePtr) = to.lastRun = getTicks() # Update position of current stack so gc invoked from another stack knows how much to scan. GC_setActiveStack(current.stack.bottom) + nimGC_setStackBottom(current.stack.bottom) var frame = getFrameState() block: # Execution will switch to another fiber now. We do not need to update current stack @@ -215,12 +219,14 @@ proc switchTo(current, to: CoroutinePtr) = # Execution was just resumed. Restore frame information and set active stack. setFrameState(frame) GC_setActiveStack(current.stack.bottom) + nimGC_setStackBottom(ctx.ncbottom) proc suspend*(sleepTime: float = 0) = ## Stops coroutine execution and resumes no sooner than after ``sleeptime`` seconds. ## Until then other coroutines are executed. var current = getCurrent() current.sleepTime = sleepTime + nimGC_setStackBottom(ctx.ncbottom) switchTo(current, addr(ctx.loop)) proc runCurrentTask() = @@ -230,6 +236,7 @@ proc runCurrentTask() = block: var current = getCurrent() current.stack.bottom = sp + nimGC_setStackBottom(current.stack.bottom) # Execution of new fiber just started. Since it was entered not through `switchTo` we # have to set active stack here as well. GC_removeStack() has to be called in main loop # because we still need stack available in final suspend(0) call from which we will not @@ -244,6 +251,7 @@ proc runCurrentTask() = echo "Unhandled exception in coroutine." writeStackTrace() current.state = CORO_FINISHED + nimGC_setStackBottom(ctx.ncbottom) suspend(0) # Exit coroutine without returning from coroExecWithStack() doAssert false diff --git a/lib/system/gc_common.nim b/lib/system/gc_common.nim index 7f6c41e1b..658c5d025 100644 --- a/lib/system/gc_common.nim +++ b/lib/system/gc_common.nim @@ -273,6 +273,9 @@ when nimCoroutines: gch.activeStack = gch.stack.find(bottom) gch.activeStack.setPosition(addr(sp)) + proc GC_getActiveStack() : pointer {.cdecl, exportc.} = + return gch.activeStack.bottom + when not defined(useNimRtl): proc nimGC_setStackBottom(theStackBottom: pointer) = # Initializes main stack of the thread. @@ -300,6 +303,9 @@ when not defined(useNimRtl): else: gch.stack.bottom = cast[pointer](max(a, b)) + when nimCoroutines: + if theStackBottom != nil: gch.stack.bottom = theStackBottom + gch.stack.setPosition(theStackBottom) {.pop.} From ae1f5c15593be8137812137d315bb8d367dfac9f Mon Sep 17 00:00:00 2001 From: Euan Date: Fri, 18 Sep 2020 21:26:53 +0100 Subject: [PATCH 0628/3768] Use sysctl on NetBSD to get exe name (#15359) --- lib/pure/os.nim | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index e7087acf7..45f022379 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -2826,12 +2826,19 @@ when not weirdTarget and (defined(freebsd) or defined(dragonfly)): when defined(freebsd): const KERN_PROC_PATHNAME = 12 + elif defined(netbsd): + const KERN_PROC_ARGS = 48 + const KERN_PROC_PATHNAME = 5 else: const KERN_PROC_PATHNAME = 9 proc getApplFreebsd(): string = var pathLength = csize_t(0) - var req = [CTL_KERN.cint, KERN_PROC.cint, KERN_PROC_PATHNAME.cint, -1.cint] + + when defined(netbsd): + var req = [CTL_KERN.cint, KERN_PROC_ARGS.cint, -1.cint, KERN_PROC_PATHNAME.cint] + else: + var req = [CTL_KERN.cint, KERN_PROC.cint, KERN_PROC_PATHNAME.cint, -1.cint] # first call to get the required length var res = sysctl(addr req[0], 4, nil, addr pathLength, nil, 0) @@ -2984,13 +2991,13 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noW if result.len > 0: result = result.expandFilename else: - when defined(linux) or defined(aix) or defined(netbsd): + when defined(linux) or defined(aix): result = getApplAux("/proc/self/exe") elif defined(solaris): result = getApplAux("/proc/" & $getpid() & "/path/a.out") elif defined(genode) or defined(nintendoswitch): raiseOSError(OSErrorCode(-1), "POSIX command line not supported") - elif defined(freebsd) or defined(dragonfly): + elif defined(freebsd) or defined(dragonfly) or defined(netbsd): result = getApplFreebsd() elif defined(haiku): result = getApplHaiku() From 8ae4a774b77d492f997f72d36cc3b8705bc080af Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 18 Sep 2020 15:20:41 -0700 Subject: [PATCH 0629/3768] doc fix typo in lib/pure/httpclient.nim (#15364) --- lib/pure/httpclient.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index a590bd83a..533bf3c15 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -17,7 +17,7 @@ ## ``http://google.com``: ## ## .. code-block:: Nim -## import httpClient +## import httpclient ## var client = newHttpClient() ## echo client.getContent("http://google.com") ## From 9a34009f00749bb29f3c58256648994940533e06 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 19 Sep 2020 00:27:43 +0200 Subject: [PATCH 0630/3768] ORC and stdlib optimizations (#15362) --- build.out | Bin 430264 -> 0 bytes lib/pure/asyncdispatch.nim | 8 ++++++-- lib/pure/parseutils.nim | 16 ++++++++++++---- lib/system/arc.nim | 9 ++++++++- lib/system/orc.nim | 38 +++++++++++++++++-------------------- lib/system/strs_v2.nim | 3 ++- 6 files changed, 45 insertions(+), 29 deletions(-) delete mode 100755 build.out diff --git a/build.out b/build.out deleted file mode 100755 index 9edb996b0c494271d8d4f910ce04ee6ae72f2b6d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 430264 zcmeFad3;n=(l_2g8qoneE@;$I<2HgTDyXPPTv{6yjSB7xiXe=rh>4~ z;+}CM;~Ez<`Ur$&(otu$-Nv{_1idk+(LsqTzwfu|oV#>)BJcCp@BMsUXGq_BYB^PP z>eQ*T+UxT*+eo0qv;JLqlHhA{eO2}W*b@$)t{S{(=L&bVex-c)zeeUmq)0C_I zwQ#q-sixMP-_|xV4TbElpN_T}caGE=8h3Hv%4ujt()?$Zp!yWc`5$o{QLg0aBn@8uO2Tv}NnjHg}|x^P0pgiEeqA#pFRyo5DSyh|<(O}uj27J%NI;X8eHHrPCl90_k{Tnv2hyd`W1c z1)$E!(;zGmE@ogofLGHhc)2exFPNg?mJAer%<&_SICAWMd+$3WWG}s|eGhaC_uG3Y zU8k4)Pk%SSb#GIm)&v zz&8lpW$r7tnYvd$Do>N};4=s=9lsB33 zbWZt-rxbr)PWdWRUYAq8+LSlsl&>-6jXCANoARcd^8O84Z{M7LX75`s)&9+~Q8C4A zp*COnCR!wa9bft8zVc3AIb*KC%=D(7u zD*uMKH%%^(QeU~v1qc}EE8jpc_0M2mxpLI?nZ5{sG^#k5Bq_$_}{w>}!l(sj?{j0oZ2yJhW`=z|+5Yk>J z_b>6Dp|m|M_s{U2A+$X%_mA?Pp|gFe+&{p3hRpVHa(@@^87kY$<^C4lGeowR$^8wy zXJ~96Ece&)o*}WlRPL|lJwsu8vD{DNJ%_UPklg>3_Y8^coxh`C*YkPLP}trk_h<5+ zA+S9w_b2n7p|8D3?vLd?hrsp*xj%yU41w)+a(^)I8T#7Oa=#z%8S>iWa=$z88S2`n z%Kc8fXNYSbC->X%o}sP19QSO`eZL-=c=Oy?VnwWaWoP;5<5S`1$Mg=xQvFBB&+w;L z_X@2WumUR#YtpgQ@X25hONFUdY*<&t0Jy>;z~~Aat!sfSz z9!qq_mV7fJwxnx=SW$EA&EG4w0E4-PLEm+46C*nu<wDZ|{QW9u{kcLNZ4p=_t>rlQ1B0Vc|KiX{hBlK{A}M6v#a zFEb^?o;Wr_yqxG@mn7Y`YENW$-JDg&svcz!P%M>>i-oQt|$t;RG&l1gBAtJ<#*2RV`joi{y8(r)?3Hr$M(Z#uf z{{D+k&~n(cG2fn_DOcL_OItxfAwT#{?3rDz(!6AmX`nmd+6VV3edKM(34X_AXW((`j z@BQ@0l5XQk+H{8T!-A4Ni)Nmq_xnF zw|Es62nqvKIsSD5720|4gRGD%w_>dRZlaSXN>b5MQWz z2n>>A8e)kfN@CSjjiCz0PIL`*6PjdSHA&~`wjW0u4sX=L{vWb1HQ=A}GjtQFFyky~ zL2t^!k@BpvbC<8py6M`jS`n@Ck*%{}o2`S1DIkYUAU|c1jr<%)&G^twP0PHRrdi^G zHO)0Oaqfzmq88cIw5K-`4MwtvFJb9aO4U zm&zEHsO$u#0}A{b)d>oWO+s-uD)F$3UF_p-LnQt~ zu^+-JFtMg7);Jdn6(bW7)kd`iD*lxzCrD3Zc;v83DRYPUt=9Nr!8rJHeSRBv2qS4@ zM=!f+<6~7tiI5c5wDGf+2;=-{wDEpxjGRCjr~0HVN`4zxT2O&DKI_wf#*czKHRgzi zK12~r8xOWj*if9|5N*34^=d=CgN~QBjen5hoHkzZNuc7(4CzWUfSERa$&%)@@dLIz z*v3s;1={!;7t7bilYR*7#++x_y6A4#)H>0Hr@v!bj1HVV&mawbrTy0 z0#GP67LyLW5! zjf6nf<5-W#=EDDr zDaQq0|8dLg<4O8%xv@e)Npq$g^&YY-A%}|fpK>&>&XulkZ@XV>qAimr_9Wq^I;N;N z(BV-G_aHjeNA#o?9chbfk1NV@mf;*m4)pt+wTNx~qIT2Yeo>wj^G3_QpdtT?nFa@3 z$_eDL7Fp``X4Rid9x?`Vn_kW%g(fH0b*!KLCVPVY_k^D8_okUPe5-;rTjc7hx=_Vm zAhM<_Toge=|H3iU&gF3biI*;RDuWu!Ksztj>IQ*}=Ppd1GtM*02u9b5+%sqdlapX2 zizYfWzzA_XQS96bVbdT9tbSbnRjx_$L(%!ljO}!RISWdrW&S~-3%djaC!(M-l+oi zGsv0fZK6UlTFZu>>#SRsh~8FRbz3Mh`*`+5kldvt?;r1a<~*UvMCbi8b`V_314}_Dp zVu(QdRAoaI2LUt7`FBmzReNd?+m}VQDPO`&n*Ysod(v}XLDNjs5sih7p(*JI|Bsph z9AMI2Ko(0Z0Yg;zfmw-IzNc!KV;Q~W1*w5fU)R1G&CV%uQ>#k4mn&&nB?*6c6m7ml z_-W@!4S$?&@cl}PBE;`2apy5sO%of#_X%;vxk7MnV?`e-?s9`0ONOs9Nc(<{>FXVs zal*Hvj(_F{b|yC6&=`m*yi7$l=FBe(?C!e3kQM^VXyqc&=3dfx$5@c-ZQ>}2?}yCA&wPd-hk%McRA~RN!Dhf-v}!KJABPyOoWtA2+!E4tU? zS)hfEds|w_jsvdh90MWHI7^%gSB_#7Q|;RodW3~q*P|5>Vka2oq~VI9_0Dgaw?hA* z!+rLNUky53zA8I;s+h}e!cqJ!QN>k(2Oglp*WL^Y?yQMav4Y<6K(H{54n!L(Mz94p zRroUjcp0;f27-As0-SFRfTe47JePKgZIYg&?3$1NSlMc2$((wq<&FwbU(VXmt#1aE z<>?C$g*>7LbaF`x&5qJ4JDrgrtajj;3{bu5fm}P3QPC2GKN^6Sl~_CQ$Og!hK`=(j zXk*8-X=g*7FE`}2ddEjTLzJV{3#?CFuRLjpLwlTD+yLWH)N3^4MJ%$!jClP(K;e;h zNm_y_v`Z`Axnp_Xc(*S|8A>VgDS81rlmR7KW&AI#yOlG(T z%z-;FXA1vv(S z_^o1AhWh!4%*xPDnt->$qY=xZbuqItv>ilMdj-RE(RzydrB+2zzwp%xqvUk^d;aa& z%$!nL;3F_g#t+ycE2eu&r2$xd$b#*DYj(A4DdES?5)q4(A1rCtGtRE!_G;JZXQ?gtnVuV`PrBM{3U6Y>Cj! zk>|6J3oYIovp6>^=0NH;3$i@~L?a$~Dr$8Z_;k5=3>1GqYn zJRu0jD8ev{U{=|j$8}`@)(JXX`aPd@rXpHkj#8PQei1v_jnZ1z7F6j%CKY zV-$Y4rKAy;boJ4yuwJvYS1h>&8>O+jZ3L!hZ-1rJGcF>`#nQr49VKGF)0EFoN9&3S zHF2h5a*RIkRIcqt|Ho&$ZX)e>+7Ptc!-gl-$?vHDeMS0!8)@%TL8e+Fy3>-be$?0b z^uIQG1Owf&cXI_Q?CKLep^7d4f+n6kblXv)8<h6?M&)M zs^jydD(rUKdy;*2WKY)mvN_m*|5524_Yr-dMR(dF5qMOcuE?zY4xL*XcL7^j8n=>3 z(hz%k?#{Pz4Ov*^OVnp*%o)};1&uibBHIRjR)?=qK7s{Wbg(V*jkCAQY0!QUw9q)) z`KC|M9^&&W%9(=hWo=SW&?HvQ(0ARjb|;mr>j={zzSZNM74)$OvdMmAHscfn*)EFn zqQPm7KB?V4iOElLOxncwTM-;;qfbJ?Cv}zf&)O*ECHS>SeK4W}Nmp|Xo;Us-C*gTl zt?pR2It(<@yn*UoMk;`i9(jHYVm1bX@o(9YxcxxPanjCx*mDIWZ#29%>sou(YIb8R zupY!&QDVVh{Nx9KGu75KdvjS&F1^Ka+N&l8E?_IBi|d@?CPUX;a|D60hdKQ`8DP^1S?r6(cp5lLcB~ma;n1ijb@k9tg|-0A(yuQ%;sZyjBsnaqB!%>r@2b z_rnydwuYtgg#wGz^fHxHEA1yERL|y`qjG335L%W(O8yuryVzGz`D|ryuVDZcMXW^4 z>A)pIjUrs*AyAE7*C7TiDwEd8ICHQG4TZ)TlIxXZq$^Yu02onuCd&uoQgAidcmP9r zZ4GfCB7cmCBaMhRD7&8z^V%}0MPK@g4AwhtQOzXQN(oKH)lFrRdYHOp4E!5M?S!CG zS;KdZ$7E^_lVo|{p!9cYV-gR(G~Gp?D<7Bea%4tU5y5cnR=UR zjwT=UhB!{gVk+Le7`DKU1Ej4L7%W8;?YP`=&8C#aJHy?W5l3D0?e>pAn{NM){ACA{ zUYl0@hg^Kq@Yu%#x0@AuoeM>AbyF$gw-Dc=;0q17xu(p>o5rRB{Ud@1Duob~Fcow= zXp|0U^auueMU)WZ!xg1&)q2()>MDi6q)KrWGTr`^{QU?2QtaCm{|y)4^f|2k1A2!- zpEl6un!{CU?bTR1N{+D!@D-Rl*9sg85KR@p0~!XIZs6|v#awLF!X=2*YzWa<_gz}| zP*1Xhz%`cC#SO3GM>uKM{m<;(N>gNMxWVZ~`Fj-l`5~@t>R{Sp5Oa$(d#{3*Td*Bo zI4C2g?8=#$-QYI)`u$3Bvmt4&IY#XR3cnYA-$~mL2HWsQZNt+8**ax$f~{3*lLqV9 zFx}BR4f{9?u;U0j(lv8a&f8ygV}14N^1_QsX>~e=_xG%2P&52GXPP|}6KTpM!(uA@ zs702s$vcEG9S5z@@|dvpa<+kJaM0l5O_5)yaa_D9oR|Er6;Lp9+0A72o`oWqJFWN= zL-*UfTRRWfC1-wNCoNkC_RXxtgdZnzB`&WH{W&=OTj31MGNpZ3 zr9Iw5u15)D`8=#nqvy%OWX?H;YDYIJISF2YJb6(Fc3?NXj!T}ln^rqTbOF}#h}QDO zfv!!!y;%H0JO**Bn0WIQZ&3lfwb|H?+|RJ66cbwB8bA#(c^?rQE`K8@(7YD^ABsOd zh!2^k>oP4OwLB~IsNx+Gz@w;X*aM1F=bt$@NR_vtwAM)en3DDO$cPW5pM@01gD%Eq zD$0VLb#$}WwyO(wgGTi`Ht8b=^wbU3;A9Pq=yn5h^yM-`1Q%S}x-Gqy{eR`B{bdG#%D=78mP}eCiBGA(Hu~|!dW4}2$ zI^7$$;)qKc5r5ak-fr75n}|L~U*X{hkl$j-eVUt9$}0`1y6U-5#RSqIl9A?I14u@n zgEeO8(mNbQ9Kft+9&Gt?aYDp`92`ZlhO#0YD|!_|Zo<(rc~llDPqeZ912C6>(WJF> z4RIB#Pc{@IqJSE_LfGkliQ3rivtuWu=u$VVGg2&39?y9^NQvqFj7lo9RKart;DGIp zO7{lW2b8PTZJCmtn@9Esx^WzQ0vokZpfQ#VhNqjC`Fy1mzQU6?ov4V;*d@Q_jLvDm zgr&N`r3=5E`0aF`r&LNj_hX5wtJ*>pn*yhek>wt?exkV}%;te1`E(BbDkHyvSok(`6%o0$?$o5 zVy_b0v44ss!((v0E(@!X$>EB+wTC)mXP~Brj+By&^D`X~h({=5r|~>ZqW6RJ<{_+c z?1=fYZ|ccD)H-cJ+26(z0_t%CM4Q$ZvPAIh!_h}Wp~I!Ky~ug}OURHy%;;I}ELb8f zC^3Bn0jcm(<4dx}#}#QgQ1wE*>V;5cf1H(RT&D$+YDQns_XaB$!|7h}T;VAXW*wq`U+EB5&!99%v-(cy8*2S?hHqYf2Sqz$vm!ed1 z4vwXbv4oU3Z+={0Y-(KW6Kya%cA|1+?o)ZmQ7gg7(dXhG0R=m_*VT$ahQ5wx@{(iN z-@CN?qC-D~4&4KuS@l^Ez~V?W58MiDE(}rA(?0Qida}>!&xn0A%YmJKQac@Ih?=AG zX&}VI_;lLJ91xrzubLmK#F;V=f@nM}nvUF$wYT}|-1Eh`BQqSB znf%GD6Xy#HklB$}TVoF{2NtuYzFO05g-BvL*|k-H2BUQXQ((|-q;zMybbxY9*Cmfj zU^{2|2hvqBiwsU#>R)#I!>rW}Y`Mh3N$^js?2VJ3xu@eNKR*9myC7Y2)t)`M=Kf$x z8FyD18!fU~l?jIiu})or*~92>aGGmoqHdl|jEA@xCAXOGvQ$IvmQXX!6r#FoJ36g^ z#BY5_9BzRWcgP(zb#S>W;fdlXWzZ{Uy?yC$Abs11ldG%HM-=UP4V}Hdd@o-=>+Dn{yh(dqf z-POs6(BO&Ch$=zDT(Yek%X<+h~2Pp<3I=>{RLzVfxE^}>DeP}t3zTxy~EfC7T ze$jPZ^X7_tH@!jIk8XOHWe$gfiNjt2Je}j7JMBhA4>11_~qSHa2RsknjI$actJRg5$U??=ZDN?f|5rFuNB8~_m zqCdgC4vzi`tbp=X@!(w;Dl6*a|%X?f%X{bj(_)xSiV^-?e%2S)pz zYikSe$eTO+sZD@?Ss85SG2rqkL}KV;4GR_F*WH4uCsoQ}f;ZzBVlr!oEN@v%|7x`9 zsu`yj&m82eK}*}VGkX*ZHYuMQTt3mbF`pRS)QpT{sqkWl!9Q<5V~NrqYw4>OmP40N z<=GT+sRH-1Krgn5!r(fN$KDeNX%g0SRT_lLlyJ>o0(NK?mn-TRB$EGAak4=ixSKSW zXVZ-0)w_5y(73HD+dD5&In>*txYxM2+K9Q_B#O9idZCoh_;VVjbq)TWj!O9*u zTXcE-!B1UU*V{@`?2;Jm%ZjA|!1Nu(_-0qv3c@W@#>_%I@?FLFR}Mx}Yd3@Tdx|u_ z8zeEnIF8z8Sb1OZuXOP_kYEUaL}t_XYzxXn>qHbLc4*D_e-|39R1kVT3xq z@&<xWriK>I_U{>*w*8bBjR%s4CA#Tq9$@sOVLh`N%5`3ZrEiQp+vv^%8 zjD#uWR#uLr7U|wqM?ZLx><-ED8yJ16jAnX_wDIQZ2qbp81>4qksQOItPjvB_2x(@z zLP0|esJZ3@v4;)&oI=1{kLvP2hBFn}wG+lGvC|hgyjLr`5zf1%Wy(T6yOw5 zz?zVFhzHOtVf-*% zho*2I&BAu3nw;b?`HVNhE=PFjX_5AE?Jyk6-r33M1QF>gUi%|WvSi1eOcKF@ zHH1_BAtYbu4Wb(@5x1te%BJQR*=o!I%;OaEe2dA0VUe18+7VEB_z&%Gp`N4HFDfSv zy!{FsUV(9GIVi6EWR!C4XX#*6J-JtB4vV3m6tjX86!G&?>%>09Q9$aKCDw_Gwa~*t zH_=R$b{_OP9gtloI5p|tu>Ph#M!PgkeX6YnHPfMj-uKXhYML`ura=~&eq)c|je?pC z8Zs_y%5^Mbm$Q^;%?_Trfx~uZEBqY~o`2YGNS2nyVLPTRA+uWgrdZ#c%iHKXu?COk z!GO-j|1RK$-#zKsX89lV%)#3IX`6$sWCd+g`+(S{j#gv4ZLWPK>=DtiOb;D{-Xi_% zWt?(?fiSa_KwC%3EtK-L?Oj8mPa`nyVfz?-S*NZgiurdB6AZkuc54M+8w9%p?Y0UY z?*}I-nRw?83XKM!oRf@#G#nbGfw#ly49Z-p*jstnz#=adbVmhuZRd7m8?K;>bQ@iQ zG=s=X+8W z@cD*1RE|e(+%v*Mcvb`|n*&+9SZcH&BEq4cOM^BL0}G0bM7=zW25c8WlX`c{sM;xo z0c7>moaTDx&u+8hgsIbq_HmBH4zBpMSRuIDzBBeR*Jr&juDz8SPqlSGNXVGe%NN}b zu>rY9#Xr&_qj7YHuDWdpSzxCYU*NTs8!_-VV5WHwws7ekfky|bAM?q_pby_Ev|jh6 z_7uvO-PZM!0`hHdpAU>33atsgPBijdEZputyawQp_XdO%Orl2Q)C zm6w>Q*ea#JrDyuo4Z2CiRg%#yvIJ(9y3gtbQVG@f>*6s zSNu}z-NqwFuT8t#TN>%kVvqe>rhtQ-|BoC80sro@g15CV(McZLx+f?33EoSa?xK-r zhDDZaATUO)u^B6{cUA0i51T`~)%PH((1`unJRiTm-lsx2d>o!uXw7~HPyE#-J&Av? z5g)G35VF`JciRWoW@Rs&y>QDy68<*dC*e3NAyO|WH>$wPRcC0gr=gFqmBT=EhG0&R zzFQ6bo=Q5vA~TdH(IeRFlA}<|>#-$Ypzpx;Mc>6{EfFWQ4%n*1ty{UC4X=PzY{b?yRqjLLR}}NpEq#i+UB~*}L-0G(#XnN~r#yU&G~V3#V+E&! z;9``9oOReX%yZl?9`@W~c^qz03&)M;50}?z-=;h4Z<#}UG7h@KtUJXWG}i&f@!F$) zAK)s8S`aKV(Rn1sZ=Lzgo}j32ThtUHZfX=l31h2_`Wt_&CNEdaf8=7yf{S3D@~I5N z66{Y>?7vxTKCK_AS*x{YPgdwT7RpFIeBHVYLo-LSflg8IVIH^^8&-Wo9A!?#%Wdq4 zGFtJrbn&>00Jrx<+7_u&$4qLTJynt0x3ET$EeDuh9IFd2h_P%>SIosOCW4Wj-wj(j zLwx8A#kt$X$*Z~!YZ&b}K;d-zZ2qMTXDQVLmx@7=Q>98HbjEqzBzI>k>cK85Oq5FV z0IdW^=BC4zR_#fy=O}8#Ma`3C3&>)ed8{I@*xZ@{%IV5FS)H%Y*L+9WZ9w2vDc*e^9+R8~ zXHyj#F*taR=ZC-hgzjOaG7(1NzvW#}@H7s?QD8ZboeB@I$P)7y{u^v!^pj5T{=2E$ z+-NLW&DwjDnmUlsQ|9+dQ zi!a_V#Q()2i@MzZZKxbWZHg4$$AhCO%y6g^tPK=nWpSQ4xaTIOF_FFF{B*}y>9l>6 zrop2D*Rm2cGJa1kwwB&d(Go$lVkVm0^dn8Np(z24HEv%eJK0a>n@(<|;QfPOnNB(n zEG%IB#Qi>Bo7#Qmf2u|c8vj3$o;t}cII9Wc*4a)nQ6>7jN94`15ue|~B9;VF4HXL0=s^E@I z+=d5p*-#|$RE7TALTyZtC9M3gcQDQnLPv(ZZl=mzn6Em$UX~b!5FeZ>e{rv`iN>Lc z3Y;cdu3ehVbD$?xff!bEvouemZJvJGS$kO`Hbb~|Adt+Ix4$BM*59>+i3pW4ACeRi z=ZOMfo_Z*zp(0BBP#(0rFBis&|3@?i!=T1%?(sBUj$EXtW!ZPM_fqrWwi?t-{esau znHJ66tYJ(w%MTWTZo{Fw@y>6g)f##^6|9Lh5Z5yuAmW z$JFT2-;7%V<dpwhVY#>@9moF{W}U>yz;+7j;HukqREdzo1FW*><7c|yjrCgRnX=25`3Vk>Ss>!gm z)_#f<&qcy$M`Ni=?XO5@M9;|8a!ZuM&a3;LA=lY^D)Ktt9XT?FQ)4#{HB%VorT1OtCjWYcb^vAhSaxJkKXz9!iSDSBDn}^Iazi7}z1k`zU9nZ}FI6t)gRNTS5LS7l zlGo%^nVJfpLsqZ5KvE~)3uI;>M=Mdep9oSdF%zF-6ufT^_|;!AqBcBNpxy7d-EH?- zt1%`Z=yvxmJ$=jBP1>i}@E`W+so@7<`UW*?!BaX*r=|&ySR#GRox4aK`Ip4sI$Gwx ziuyN;I;*Nq9>AV6dIRP4gCd-15oq;Pc?3gMED~Faot@rxtoTte2fLW&S->&%`Z5H0 zGljI~TE*;RF{`W6m~_V&%^^~kLY=Vn5A!e~zSF`Mvh++>g=ppq3o^D~I8h0|iZ%gA%5aCv|A5!5v47 zI1xoI4j@Auo`wOh%@q9I20oQ2t%NP|DJ9nCiuGC$YhZ!K*-A10o{KpwS~ue`vf5e^ z;vT{u+y4#h>6-Acy^J`SusBE zWvqdIiDn%!1COy|<;y-_D^a#ld&N9+j$s$2{D-B?!{0+5)J4(>8|IB#=fE;)BPHA!5{*IF4%?V((@vRpFenF3i`fi^l=ypFLF z+9M~&)}T=^)gW)jN{&LQ8wJd3jk9|y+omGxFgXo?aSSKw_xQQ4B8^&%%28W2EXlwV%=>S}avE&hKnQL9oXT5li8PWHLl%rY(voeu+t5Jl z1&X~QWVPe;uBcVPO3okliOkBNg zgAnRzMLh^8Xhn>>Bk{^jYJqJDcmsc<=m?@&Jq`A6C4D+b>aJ^$4{Xg0=j0MZ6T7Z~ z7i+P`tAk`E2$HfKLb4J=Rtz$Gbo~t_JJKVYCq2{Ml)%HS!A%LC{;y#UMPX?I)Y{u4 zM@1%g;x350Ap$me8zOHiaprf|NPko}9!scsl2oYL>}VuB>h;>sJ9HJy5AwIP@?$MI zip-A2fwo8{wCLA?9gU$RenpA<`!Q3wn}Kg()xv$AnpAHq)qB6W`eHos_BGy7aAOc$ zjM9*^8}^rB+2E%0CMlo0kFyDQ{=UZk8nEcSXIbVDhm3%1@U^Pw&e*4vECWnk>VO6&hYN22kI23cf!WUaO7>?JX$88b&NA63h z3Z9e6#@1%}B4HoFj)BuMoX)(D&DcC^HYHtay3aYBE@Qxga{T0*^)rnGG;41wXF;=` zJX7s3Q1htwezEOuPVVV}XOQl$2+w;6B7l2}RUW%=2LlyOPq>134<)|JBUW@g`p|~& zZt~!tKDJRzU))QvkITn4=d6LVx8m$(ajF+`bjQmL_`xbN*+&ujScJe?X02Ucu%W}k zCNO&}=TvhvPDcAGqlG`KWtwZwXGI7^aY(It%D{3K<|DNN#JNi1=-de35CtE^)>@1d z3P&^hQ6e>Rm=IO%uT`DwRyCegHP=kk8kNsG!iPm*!XJ6p87hlBj}x1uh(FA~xR*N> zV`~wE?K2#(!}#(ifvPOT7raQ#YNTh#cvD*aa`0$&E7_wBUGG_ z5hq{ed-xU;$bFOZq4mo1RL`rzHVhwe%y7S40{v#eBE1btDQOG4?UgwWmtKh%^e~T%_ z>pxgS6!78(5IKV%EHF@QRpSm+=JhLu&*7Y)A&%qI1-2&W$W9*|_Me%qekDmA_CG8# z$Izs#zN39$P#HLO(~)_z01|fQbeZ0nf&JpVp>@J+pTtvbqEh%s^n8sgl=?nfM?tB7 zy;7w8>|*i1M7j}kJ-Wg1inP5!3aruv76wxo z(E^{(m6xrIQu0sN*ydGPxa zpInGV({?_FBaM2RlDzl5>K0gEMkB~dyVg`zic_#f+*P>;rdkxQS`?~0luDegEN(L_ zwBWxUQMlQ)!usK8Oke`%gf_a{gtfvv*I%qdE4%hMFt3tQ!#Qv=PadY|1P&B zjf#-? z8>^Vd`Z2-ibHiwyg7@%)z3LVMt3%(5b??0e0^IDGz5rK_0M}SJB)_gbk-_avd^iHR zLn}>zx^qJE4wq{r`dY<$$RcY}WE491Q;N!mO$>2|3rlMW0L-YO?*- zm<>Q_gl=pcSUBU%S_bEH4=&`s+V_Y~bEf?#&p~wZj%z&fn_^w5Sa$}oP_0<& zRD^t|2z$b5kOq#XJJ)!vxI7M1)xuN-PCuThU7e;HYU?Q2iq%uqREOx&U0;iA=3tlM z5H569afgf?ks3L*f*KD~q6R~x1kpO0e}sbO zdLRTB^mUVimnrxP4_s@XV%~#(MVE*6y0Q&+QN=&n!-svO6|*B1w1)>madLp4S0gdtpA^+;MW4+vVj;b>t|d^`co9)Q4gU$cfTsI z$pXb>3Rq_n-``u+ZAL4f^F2P4bRaB&B{>=d1my$US3xrg%cm;-em&z;!qXJ9ZvYc2 z=$nsgZec$F+Ubh+*;lS1{?8QIh0{OqIYar}>G6R}phb$cnllxAwF~C<0$W?U$FARC zV8*wKaX55SJ3ODK6fu{Aj){~Pb)B!Ey*v;c$!>@sDTd1p%5|m*O7l~@t0yYQOUFKh z`0|vHn{P~1%r`vDM%}DXmUtG337-frdEzoYYYnL`RJuF-bQDD<)-wj67KrAm{3*|% z?j~(INeRz&2?K~5@$6JHHg)qt#}m`N(4l8c5IPE+qu*V_Aw$Q{j!_@-vadavaf*Zo+xNP9e09bp4sK~>MWNLAgRdZogfS!DJ*Sy13FhwjLn zHADdub0p(SFQ_Og-nxhP9Ps-W*r)z#1h3wN@bng_uJh!fTFqmY)*G0>13t2(Z3B8^ z+ZXvpJwQpPT2he^GTSf3QP3Du@&*(U%fEgpsv2`YA@MPj#Mi8XBwa#d;HgX3&>CprjBjYN0uSd!uSm zDvW+oMss-we&lYHDZYy=2?tC57?!i7RTPsGS7RLs5x+<>RWlG=jOoa55mpDJ=FPNb zf__~BPq2hf+~!QTzQOY`6h0&qBX4*$6U_~1Nv zWdMFq9(;@kUsPsneB~XUjq72ibaTxwYS_7A*nn%Cb3a{!a2S<8+G!89rq304B}R_i zM(yKO*l{acG|kTH|Ip)2m-%AKIF1>)ofPk!`BKJ1ju&ry-qX;y*93MqZ;FJp2TV#pY7-~!gU8mrL2ZmTk53vqCJ4xj*;yt1`6I`6|2cO`a0NxZDON8Ix zC6MMTQkg~a=2#g0GR`$CedVs`hkk+eH>xBo9^Nome=EQcj_- zQjB|o7_sUWoKmBD4Q7^cwc=H{c$i^`@)GU#T8kkC+O^+UK)dWEx!UD>Z_`RwvrTOk z1vR_$eAR5Z%CoY~IFQJ~V;3)dge>c6G?jC9Az_a63)78*4)=taZTS}zX2p3b%quF) zX%<;iibXbR&x9c8C~zW4EM8UYK?d6_`Z5V|DegmN5m@fsk!|&w5_W#->MVZw0F&$Uju*k^pvw{T`%uHB#EBfG-zFR1f3<3wkT5x_9ZU3Xg|(KCy5su$BocCekJ zb?i3IziyD2f&LRbN7OK7{X|@#RZzs=KI%!t<8nk?sL6bbB?>P89Kv~%nT{<|%=?0v z=#Ap#4UfaC`gEhkR~PxTC`BK3&}3a`F^tIeNYEu8z&AI?V2%2^P#ioGgvP9dr3$^i zWr_9)s=i;3-J=}ML07@_haod;k>!L5L#F2PNnGxjwOU`S$gQ6HHu{os98AnI{lfX4 z!DM}@Vn6C(!*yi^+uG?SW(BZl#{!uYV!?~DT;&nuu%}G(&^eq(YYTYr&42X_yF3oV z7I-cDGs>Ix+00f`(0d1C9hXW}s7$N8u2gH*cj^ePRB&@HnC^@jlD>_{qWFabco z78Pwgz>i>jdb)ys{=|(MkgB`{65yl@CTni%${9~H9{j5(>|*p_Mt>MUmJRqMKLQZt zDA&q{S~;qt^NQL>F;gYf3?i2Xn3t7saDBxrQT|OC93McD)vOXHd0hebm^LZbJo7n*W%%*Bre+=)I|a=Ds-Qt`Q7dB6~>p$E)rSRqiV8z_6n8 zV^oP?ljY;T)m)kP^|Ai%m?;742$^EOH>Pgw*#d zXsicnh%IsO0Yx39)LVDt9L>bCIwjcKMN+K%n#+Nd*GX~tO^qjuVPATBD-MU+6^Ci#d_GYsc{e_C0j5qYl}_&gE6P%< ztbA@St1r(d>x({=^6dbNn2=&X&d#44_Xv#-nuXdK9paSlN0 z18?kt1y*7}tr&V_Xl}hx2B??ZopYsLCFBCK7Vn*xGsyO2lZM^)i zG~JhqFeQK>yUG4piy87AMIK?1nLJmPq9MUlXq36X-!^m%O?T`kF{wkTi!61IWKN%} z3mf;VvvQ2v2;(*+vbE92#iJ6~y+Jrtw_;8_``e;Wh!=DCK1X`zIrtNqvpMd#bh)vV z7UAQbxPVEzXn(RW<9~|@ni&q35*o&^CV*uR4SiWAXFO-p&QINdMXRvLMB6-8=0F&? zq%zSaQ{IHhWm{y9ywO!QuH?ZYRy+_jw0g4_XmcneloLQISq=ibYUFwTYht#Am$Prm z_Z)g(_LkQ-IG#MYHRLG!!Aw!s!FOY+tb-n*L&x*jgicFOWt(=cH^YQ7>@*B%J)a6K z>~U((PGirVVakr()7|3tg;;2MvHdsn`08j=QTu#^SbX{pDLZN)T*kZ0G_}PtSyeVv zv9V}0WfMEX%b>>%9H_dg1tZ*W)R0D5 z6Fg*7@sJ`oe$60Otfk^NPVefjlF0P~P>g74-=UZa-}A->p(H-@hc~qhNaGqodtDoB z$ZA?$$G8_WH~Dm!fi$5#OH~kQqhrwq?Ql$*qs^GZ`Xg{5$r$|L3Ap6Ou{&dHf0FZ) z33#4~h+vgPcX4JicgR9Jb|mY6pezGrF+gpm%@2ep0IL&NrbmzI1i00Jf4WA%80bY0 zFo=NxM`n@sVNzG)g-A>v>?brm*C>X$fm%t9aOo^5)zxmt3cZI{8pxB(VkBgR^Nz8C z;>0m0ie0LLTYQUGTmKGC2){NPfeFrpFk-X6WoO@ZD^G9Mmna%Y3hn zDohP0|Fb6_%HU-nY@t;&5!Np}WbGsD3^2sGSBV;EaEwB+M-DNy1%KLf6YVJAca~|6j0Ci*o z485qxSh2$Zd0F^Ha03dyN6XnQIzbCw>_5AyBJ7MMEj-*1peGb%1Ne18{CxyJic-3M zG%LAWD*P3WwxI}3i&IXNQRDnTPI1jCRWqnJxtOrIEGZU~kNGak19{N$zRr%=kOotr z2j7kzKFMnYT_nuk%DquT7+vH^+o@3<%@d574;=+V_;Qfh$W%del_e4vVeFXmEF8X< zI((y3gB@JR2Bm{X4=>-X!qjM-_TACMnn2%F!YYF9j zs)%#|nJ-g?EXcln=!*0oFqD+^M&uv0(VMW&aFZ_7mjD5N z!0sss7bybb1#xPlbDTBUzs2>O-0<0Yg4-ZHzVl#;hGsU$0MB#<9Om*P;2^M=tfQsE zj;qC!?4{^t8Rs|T%eB!b<5f@M{Wy=X3j+Ta{-(l>)+T&=P6fYD-XM+2aSEhwYxB^m z4ZN?xLb;h~uTutBx)na96|xb{xxLXyfC8WR;0Nn%sMF@oI8(I>+FPPs<)=2gryUo>DJ(5wRt2e~%I4~WXRpsy$+DvX1Io!X* zxY4l#_;I)>eB_g9U$o23%*GWS4VpAovgOWN+*3z-u}oS*?G070Q-l7+BeJHdr|DzN zbQtcWn=|g>$WaRWz$ifb1pEDE$Gc{jwYrmx6AR@)U1C8glhTA9+seonbO6R`k-$Th z*zlCk&P!p^p6*g2@%5WzC&zr6UVjq8rX5e@bL_4W<%B+*;pc>eAfqPawbi`@)L`U(o*{+HQ%?+?1P^^ z?HXiH%YN((V=io^pBV}~UXSS<0A}EOW_#LK-y4&O#?^94WM5<>mfH{P&}wn`b*(`P z@cFCo!Kh^;tJQZ*t%)<89D?*QacNETwa8QEJqB2#8^!JrH;MG8c*P*0DO;;v>8u^jU`FE8- z3iLS@O*ZT^yT+#HZbzF&8NN+GGIb#CYRp~9A&&vy4JmUm*s(Vt=!v~Q`&KZRAT+6* z64epN1pJ^fXacxJ!CD2pX&cOc!Mg>_@J}Y2O)tSn5J{55{W}4#7Ojffw?nOR<`jL{ z@Ch{Qt$!9(vZ$xkUr%yO)=09oO5$bGNu!FfqKLb>YSRWbjbwu9)IXE#h*-bsyJHwCOcaFtHXR22*Av zrFqXSNFsx1X4{6lK^2?H!VoG(lw>fZZdww_JD86v73JOIvMH+YoJVEsey|| zvdY=Sn0eukRE4Y8bw-P8yP(BIWZEO6Rw#~ts2L|g)Tz}Ocx**ha5t0BfFqS18h(_M z*EgJ-u-X=^Yyc_)6L>5XqJov%eZ-;k7x7Nyi5GhN`718GO1h==Ln@RQ}y(s&= zfZ4XKrZO0l7HQ%P!%k&oNQH^T{3ay5hQuCB%RCxW4tQry02RKgH2A^NsAm`~9vOu{?t)pyY39)Y9d1x1^=MPG52 z%u0n%d%+EEr|4xj!EG2G4ur|F<{81iSY)$00EKaYAcBNuI-3Kx{!F8el;)Aa)m|{F zgSVFYNYtUe#{^t517)R!UCige1g9-v+VFhK#7+(XGS3FyXhHf0UNjJ!I%^&Pyu?CL zgm6t>XW1go2wu8yqW*{Fw%A&3GU8osy<$s#8WBsp8XE-1Kr9CBFs0qWrA6^Y*&ktl ze9E4uXJ+igYiU<}T`tjr)9ghpMNPQO4r-}x8k|_t0oCz|x}}LMVOhY~t@x{fI088hT{@w*xW$Y#` zpUwr(kuHM1MeQp28uVcb+|vSmO)a0QfpD)f6v>xKM=0{T=X}zc(XC8DD=kR3_(2Wz zUGLFW|5BDB-EuAb`fG882C$(=YJh<7M=0MLUA|n=^go5FnJF@WXcl{yR5bgHJD$%o z?Ce451s22^(jn2t$ee#^eYT!myn5!rzzt-3q_W+}fSRM%qaV0B$qo*7Z;M`yJP$k7 zMz42)=*qIa11q%Py}Avt)KqjLkFpA_KMIsTMV=qMUvcf|z98_RuK8nofPLy%tfh^) zUXUPDxyrQaX}2YCp87B7q-m$>i~>2p1&8=@fN>a63#{Q^{H!ozO0=C`b`v!=*eBRV z<^@QOG4A402|meZQz7`g z+;v-Xs(x{HL)3b**2>=8gi7g3nub?b%?nj*gc{}rVptX>-t5MmXrrwTiMB-M$a@AQ zm5Vl^tw4)SUw3KDOfssQN*EfeyMCH?cI0nbLaC^+o#3q7g_^Ts{7VhJ?|#*|1_#Q~5^X>Sez#HxUl?eNj1?GnC7Yop zZ9Yx7?5qjv&HmcFS;$tX4W{*pHOY^d8hW7->lO%ea$N3(FL#t?B+GTQHN805LXVPeq)XV=$nZm z!S6AF12#crm3L`J_$5L2l2WuQLyAaY5>lC=513KM$qc6y6bnD$DJZdeCBx?m9L5Td z_LW;h6sL6Lboh=Ke!3yNT)h3_{D8__t^< zn<+K?sXL+Fx2#<^l`skoI16QJX5QT{JjkRztMrh7=+Ndmsu*4B1XlX2Q zaoq~RKjYMY1`tNGeWkwehE_swIlNH|`+xs9G)N8jNPdRyDkwO_8b+|c6LIRgC&L%t2Wjz0Yd>gn_&pnh968|;ONK`i0139|dcC`yQ4Ax(yTYf=1=L&dc?(&# zwe+mj#pvbOO5iI5a)u)3VF_IFVuI$H2)q>wW#KpP6&WJ4ncKutx;22R4<)j>MV>Fx zKz1BkN|@DY{ek%S2B$28YJZTo7ZCkES)$bi(&T{2tj8`o|YeclXBhv3Kt(o(4|x>oDzk2ECun0RkjM}JnsqYf~W_9(Z_%Ft#lISrw?z|p!| z+zQeb^t4jcQ^Dz?-uQ<`S;PocZW2o|#AlokM%j*YS*PNn=xPp$IxTD?qx?|`l(LNS zi!R%vTA9b8SbFiS6TtxMo8*5jmq{^iU4&(Eal?#D0|8P)x4ctB`6$w;5b_>%D}6~| zIJ6aC>oI}Y^2~;F+KXcjp}-vEs;fFRjp{5Y z!a7~&r^%^*r_sHJY^krU{yjflYUmZF`f~&?{GRq&EQKcryvA#C zwEqsx(ceKf;6vU^qz83v^!Iqx@1cq-giZJb50~%X7%)GGyDna}4vRI4`#%PkS%Zv% z$8)JH8GexE@OwAKx*GaWNkQ5bT~n9As#y$Hp2X$o7%&wUT@qXwAJufc24yr18WmAx z6L4WG0YTr!FSlMlA!4bonVHJS*v&;6oYib9rJ3Gvkb2HQOoUM3ujh$%BD1G3NLqfW zp{s62Igm3zmQ?^tFJ0q=uhc!XJ|v>YVyV6_#O_oUTuvATUuxkVHKtPLyf&kOT`w#M zE&&)x6U_R*N)zF7MMxL~n*}3Hc8V?w>%0Sp zgUYl7%aIPJNXsCJ#fI0sOhnA+&-R2JZQh9lRV zvXMFeRQ_-~cH;St{ZB_5rj z&^J9OmeyU7}lSiZ#ag|K8A4BQ87qiUQ02u##*x7$pOL|z;!g&4!J?*xl9!ZP(3wO}2j z*I;*|5NH}{HcbcgzwYaPq$^izjJWEDvCZo9)(rGVatVI7MyZqI<13O{R+&D4Asb_9TTQ1ERz;F%Z> zoXraf`SUJ5A*EZW(c&%KSVP)*2H5JFEZB=JlHqq3A%E~rwV=j-~ zB?;8^CK4aD4Cb%K6Ji}peJOo6QD&4nIHSz3xAxTL$8N=Q%ZsJm@6d*Q%@Sy&>5ECM zPAK-ru#Q(vIeQ;2r+62`?dgo}8{IbB+1Jif;f(VNXHXB!6^SU^9GT|9%%QPUktu$d*0C8(*CVV**|Jm1cL-u_O- zdB=zIzj)rhqeLR<-OB8G!%UuEe|>~~em%HG`^=6$`xe8#U9hh@4kGI_4N#E7oQ_kS zX6@mQhY&VQh?upyj@2a=PQr)m9Yu-;L%TQzCl?&7b4`VvBtMAM{L0S3sI#OEZykxYG}l}PAd8SmWt<0& zBN>b03k49V;lobNHPif9#m5tiMPSY!!(6_{E}XFS#OGjMf)RF13OaUnrQB_~r(VE*K+gPI@H9j}Cc26)*-@EAU{9 z=+v+sGcu%8_dw1xz%3~2#4nbZxUN8&D)^INg30G{PJV8}jXg1p~~xeqn)(^XwX^8U3{56SYZLOpo#nyQIH^ zunY_^VAurS8+`~H>>GO8U|G6An>+Nhxi&5|^q_v3O}z3ls;Z8*ap(~_QT<-FdR*TR zOImf@+QUaa&J}=Y7fHP50|rA`9a+1V8}J_F=8kIja-+(ObIg-g;8$2XhL^Iz)ZGeM zy;{XEhlZVJ4iyiC!!p&4PvwY>qeMIj1Q+LVjaIy(Uy6BI-86}{3>zUW08qyH^bvEN zOiw4ZFoF{9XE4aXgTzlKlJ1oe!rSKPmr4hAi@s;0e~Agjm!|jP&<`-aVC3thDpG?H zWWg{Izthyp7x8pP?!{MIzKBFmU* zzMYvBHDQUMEW}s5<8NeCR+-6hJyxy{GxN<)#UhsUG&Fz)sLadQQShnKhC?%7QtYcr^}d!n*-8Lw;$w6 z4{nI8!|-+#nrE#VWi1%$)sMB&AL3O%h{=91Ci|f_x&{@jQOoh|EMzi{LlM6rv_>fn zc(8z>*jzO%QA@B!`O+E=AH(IZS`2XKZY)e)dXdbM5;lzv)W<@Al*glC}W z1)iFm3a84uK-MW)qx8DwXdBIkQrtE#?hB(h9~@1u!h`S8Hr0mNr(jg)Otl?ZQFWP` z9?+}(YSf(uk%f2WR{B|9r4yvm&rs=S>shH`>Q)-gsT4*HYPuq?(oT+n(G{q4#d=n1 zn7Wmwdj&OZ%dPbDyh{Hjm41#&KVQ#E4O6$$ZF4F`cM58{GOyA;Qt3)mx^g`$HB8+~ z`NCs=n`Uz>U6ohq%^0z3qpMKqs`aeYFm)^4H>c8;+)5GYa=iD?Qt20{)I_mA^j^c% zt@LGd0>7qBxs^%)4s@CMn9%wIaU~zz?y<`lrnb_KV_9Ps9kc|$2zrux>~0Wt>Z`xNTqp018Mv3B>l0D1FnF{Y}iM;nCthkm5hMcFCw>MxO;@q;Ih}g(X&ObN3 zVhi8g$qxSBK+60H(RF>Eseilzn;rd-HM(}QX(}_`9!{-$@!hsp@O<-F<>|D)X56$j zCJO*r>lGZjum&#w;fcT&9#!Z5FIp|?v~z;YDW#_r@v%x}@!>-mr|X|0I2+z$U-;Pb z8#?H-g$*jyqa)$Y%U$$&8jiKarY)|)GBB_}r)S%FU2p=LSTGKR-cN3k?6XwkPBCD$W96&1ER?a>2i^~L*Tz!)xU>$D2c)mz`VcBg^xjnR zcKk{6BOP&F8I5<`m{vHpN?5Brdlkg^l18o*hO?^Daa_!}3^ZvR=fo`5*#V7F2aG1c zz#JRy%R1|kbJxiUFm#Y;6S(8_AzN>rRy_~0Lz;R$0Y<6ag{Kex2B|Z>F*bk`XB$9m zu9;~}GcUw1AIdFE!&0AVW{PQ8sHXA2%%c6KsbnRx@nlWo6YXZ|oU=kF+G1DM%viY= zBcIr=eUte~;q|#ZR7mvN6ja+5wNX6tqBeky}|0^^UL-{dC0y0H5vm8!x&F;|D(9r4Lv zg?sobgl+D8n~XKUYI7hoblV(naPZ0%W5wct7!qiuISzvu=&^aqBnol4C&(o=^fIGN z*Z;@do4`j|B=6(lcm)qkJW#{~6b}SZJXk?M5gg5`xVVbP5R2wo5`j3N+}ok3BF;(>R(-xyrcRf!k;o~Nq&z2=>OyX^P>=c9Sw>8`G>uCA`G zuCDI4r;HD@N0p~%TwA>PJEtd?qWK~~F^z^IK%2+oTCYScm;wE3aeHx~U6#eP++B!7 z2@IW2%@pj|7q0N6puI(>^Ym(B@+%MxgpL^fQ%nUDS%C+O9=>L@_VB0Jti?G<1dZ+v z;SSgy(DM=!w+-uu_Z}yS#3EXV03sO-TiAL$!(lh6MD3<@pO-Gv8m>bPO((Kh%@Bx% zyP4vz(6c}Z@QQ!BfHd0To10!6Oad#Mo5qk0-htdHs60q)bU(!Qahiq?bOlr+P|N#i z@37*myXQ&~s_;%rA58f=i@7scCOxoC5-|fmziq>9w(a0W4_FR;TcR9dbYt`R*+FPw z(++5_vl1DFPY>D+=VrB`=ck74hH#)d7Laxq>BE_0QV4zi!1{J2?cA}b9@^YrI1WRH zK&awA19HwdvYKM!Ou0s0^H^Pjfz%zocTB?vY;GG~?8iJ^z%h=#=L80;z&RPeoBFb8 zLzx5L$>_Z`i=yuIWuo;T_(!aqt}G_;RY1ZchJ{FQ_W&(^>xx?JL83Rk$5Qwa+8-Og zS%CNv`K{B<%RD~)I=&@7bzY0UhgE70$t<_PoZwC55poZsD5ssrg1LGHcY78dq}jLF zY-{b(CCbG-WbnhgD2vb)M6BD3Meb93#WeBGO#@BrjV8hzEUC}OG_~cP0Ra>=n3myq z5V7$5c#sd@2NmMT#cb-^exAWwLS`RiW5~A-QZg(`LE5?YdXLE`U)>UuYaA?vMkz-TyePLT#%pp?F>J6 zOD+4jpZi`Qw@`DZ___ZK zY<`y#DCR~`bRo>621RFusjw)F7^_7A{dkR2;GRd4RE}%a6Ycz)5%OQ{9wW-HZQ_Y? zg|lQ-+k%DCUlgo)j?$6!=_PJ>%z{9RstD3qy z;??J1SLg|h^^ykAjwh#9{*MKteVVO-L z^_&+jv%-}5sw>LWnKJE78HB`0J-x$aJ~CzIv5eeOhULhWfX zWv)k=rqe4)SqD=B`~6blrErN;FIEl=V~M>vc2+MeG@$o{K`$|&djOPhWZd=3S%20& z?p2JB%b}2TxSZR@GuV1_OaUQ#`v7(q*V^v$t%dG-WUe%s9htc~GiPchHB5uaY$obW zm|3ow4an3LQo7H3%!F&Jz`aN_{SCWxpM}ii{No;{nX6G!9sKD&GnfgDgiA3sv)Pxo z5j1Hc0tfYTx6X+)`@a4Md6!QU9`gg5=x_Pgr%(+F)%u0Zq8U~QIQT9M5Fo1sXYoJ2 zgxU;a1s87Z)CK7hUl#Vd6}0k{k+mIL);VWCD2SS6MEk0!*iR1Xoc;aepw8)xa{ACX4Raf3+;KYRdg9)RfEG>Goj5(;cTEP1O{C^_2Np)Rg5Q07Xrad&13E z9%f^Q*Tfuyn(>HKYL7t`SPD^-GcCA)y5B@2rBzaUWl zfT2~3^lq9CA_C`AitGtOnG|?n2QBej5pui2HY~i$d_jsvp<(Gh?_8+*r+!By^9&1< zb*a5Tk_2jnfqF=xlt&6R-9o7^r-5~xiDCUaZZl3iz`98+&utY zfaLX#j0v*7mXT4euX#qsmfwOTqde@$IPP4Gj8|E^KQey)OK@cDET&P*BjbtL!sxh> zF_EJV+S(it{6f4H5QX5#c#Vnaj`QWw=MgcIvhJFfss201jEqxpCvejYSGiii_*31nSI_d!-AI(iW9~hOYN8uOL3WsXhlAYCLe9r1zo@Vxs!gqQOR6n-^ z`9YlgG9>$huj>X_ z;K%!jIt51YA%1eO+HQVwu-Yv`)sBca7%Igjr+y-R%`@gZe+^DV#{BIgFy`N7?f#fQ z5^4TyI7DQ-yok|r?RGI^ z{;xQ#YlcpGVC{L~<3AhHIZsxT-W-K|m!BN$oE!Y)VCP(lWM3vmq?+*!kX+rft+YmZ zXByTw()L*0b4`;l%F{jk0tFP-Ii`@>y@`1Mq7erayDPvTixoNsCp{i1A6DcRca-+! z=$w7lc#I3jlj?R}F7@a7vNf_r*;+{BA`ov1`Ua~#+S``1j_N_^A#6YaVu(U)8HVt# z=WNTJrq2lj*drA76JLJHF{05Q1;rH%U()DCx%`LrMtwrzA2;w`Kp{5!aOP>Tdsw-I z7-HLv@q@bUwx{Pz2aOdxrwC|OX8(p2pe1xO&M$O0YdcbFJ0e7#9C)b|uOI`$K`1m3 z6xdS>*@+Te*!VivgpGsvmw5iDcz*T!R9mU{idG|+%$RMS_2#OUSHj?3P1xXUVl=nt z+<1{Ed!@FtIU{kY&Ei&Sf1J^pT%*-k^q2VPOVM@JM0*TD(#{EfR<*ZprDxT&!3fxr zo+%MF9jm2wUSBC;)1(DHn;u3%VbCzeFds&y45hL}X{*M;=t0RfEn0Ai*H|`%EHj3@ zN(uU)u9nC#kR*Pbu5b(%`dctR))ui0fL72KA$k#^(OfC0EF`x=vMe0>Ts?v{W!=kd zl6I?`mEY#}pYXgt``>{vSSK|tI7Mdmr-sR6Dx#R9cM*Wq-L+9PVg}mQqa%PIas=kU zFF~F?p$3Rbta(wWy(y%eC|khF5CZr|bJ=b}y7gv-eP7IBXz4N`tza~k3Fd-h@tria zE2ck$Y!GUT`_&g0{^@x=e%Lcn9X!`)Sl+%kRd0_r4#}{jWM~n%*g{1{5jD>Ydqs-0 z2s5-u5?8ep^Qsn+@>`Vd-95U;%`UhSam>hlmUQCZc+X{24%dxjq2s=Mg8-8&dMJPwsep5J?78R34iR9L=k5qtWi3;* zy&oAe%tiy&xZK-_Y&MkAK=3ykE?WW=R#+XD;ARD?2M7ZY+4S1A(V zF_j2$C0A>dk{gF=a77mm9u$?!$JU3DlWFIEu^8%sU~mdWI`a22Zwv;4rG`vHy8aJ@1(Q){RClp_|s)kjj)-!b7{pJ1|jDLoHsfR|=Pc z-*rI1p^G70oThTgx??{@-N?zx=7BTZ0{i?lkp`B%{sNd&s%Z!@EL9BK7!0;AE73Jn zFOlvvV>H;K_drSjJd(ihDePk3bB9yEfBTRX(oe+|{$0ZT{s9*{?h+m>HYP(9gm&%U z%snXTtEy^pgw(%i+W%MK+fqxk9>k)esx#Bt6Ute#v(y$glR%Zt(rFD}(SBG*lf}L! z2mloj;1R}CSl;#e^pMD3yI7iSdjcJU3obml4~xDQhx8+czym=EL?nAZQ6fAFOr0_i zlJu1FdpV~2`aOSnXZxUCz~BvxBfSItX=I42{G6 zF`J-0V<~MsfpL@!#p|#G42ey+*s0Wmk@&F6$W?3C6<@c6!|(LN&+sme8HUACZLR_P zlN~~7=TB!E^(sRyI&1#+ku;)yIfW$c>}G*0oOU;0ITYAes#k~tAL8>QApCu$FZh!N z1tZl^nfdn7@-s~R70#1{(0!!`@mlFl-&9)~*Z{ZtN-MOwoBh%^9LwtZ94t-4H3{97 z@;4N4qyf|g!y(|7+o#E@uHv#K(5?ACs745EQtUcte;H)eQvhuY ziq91CA`cSNzW-63&oy^wPA)#G^Kv_>6X4=TkSWRFsV7aMtTMel(bKBegXeviJA=J!m&)F=F zq5g@Q@grEyU?1;c7rO?%Z|UqV!eH*(;SPXNxI%a@s9a((TKrU7g#@imt88QdKJytV zgRa@Zt3_v)5U%klVG*tB`lSlrc#dha*sM?qGMTBum--)tPo42ZDDt8fnP-b+6V*t8 zIM<;XkBkk2LHlh}{F*=IB_*?fiu&+8ZXy=PFTLhHdB5 zS5NxFzT`(3k%q>|m9%N6(GE+?C^P`Mud-cfXPKX?qO9LiHQ&VS4k=F6VX{uL8i20? zxcw$(1B) zKst;d{u#m*`iN)H4|4r!h7dno-7dc_uvt2&;0JkB>$Y|7>D4I%tC~!FSIODjX4z$= z6;)1tL1FB2qev^qxp7Iz`pOFP$L%E>8rX_D%F~jNAtE=aqEa(N=bdVY6i=}x(9xAm zy-C#jO4OLMJf>^UMn-(L8%nr4VV}1Wl>|%=$8GbeH!R9liD?xdqnOnyI2vYKYCr($ zkBo-9-%@#Kt&`N%7MNS8egnV6BEhXFh#~0mPXYvW#q8;#q0OuZ#wd0-_+4`sb_TL= z*V`=NVsq1`9AgTF1{#-fg|jmaD45^it+N&1;r8)jsa@0remDip0J6%*u!Rp{EO7sY z9NBm<@i*Q#;cW~KZlK_S%{8r>dI?9Nnj1#%1Ol7oRN|Tfz45MD9f8=NeHT~HtdYZ= zYdk|XpCkEvRmf!ZP|LkoRv*X^dMjRl=*p3lb~>3tID_?wx;wYVZf%d_Fq_~1u}WRj zR+UIPm`OLAq)PYKwXnC@MUhiE4Gn=F89{r77ed{iYl1(kRuT!HaI!c0Vr1kh#OP3b zGn)y)CmUc8d_O-M?6xawzM^pAZwT`VDMQ+5RvDhnx~wwHDxN}Hsaitb@)EmfTJPqITxtSb-aE6budKdlVn>~C-m zCeEk1$_36B73T$ca9){%b8-l0JA?CI*a>aK7OvoYNpWtI2j?C+IQxfit~gfPemij% z8l1}%=OP$@m~dAbPI+yA?WJhjznmOw`#lEdk;FM4y~wt|tT->qgL7OC&Rat`FETj4 z!)9tD{8obV6~(!I9-MpU;5;mZ^I(JXZsOd=;CxkazHnlE+uy`qRalmqCI#Ex#^5}f zI2obDwl7zlm*&BFT@KFaA)L#P(LVa|XW+cZ;H+1iJLbXJD+lM05Y9Uc&U=Y-dxP^e z#hE=JzU}WUjkf)pbg=Cg7@Wru=L^P;cwKS+!{97jIQ)JiPdXG|rB5Nng9LVP-VUUV z6HM_pwD>`JNIEiyq%%S!eSfsFZvIcuZRm-&6y4{4))pF#10kF-sLBO$Wq-oZ{7A!}`hIWtz3d&KvRx4u(&l!Ct_Tkqu+r zFSD@R7NYAueYKI)@0Ii$hnac=8C~{72BK{^1`8UOJw98?WxD``U4nqo_LaQL_6=tb zz!6?g0|v|A^299CzecOv-c(sSZdSqAy_nIg8EgNfv|!N90)i?1w zT>w7gn)&p=Y3N?}c9pOzTk>0mpF z44)X+$S1KS>uwv@$Y0(JG&1H`OedXZ*|^y<#pi3>-JE#s;xJ&G!zia3Qac7$YPT1` zAK2}DoWB?{p5&ts!4#}b2{*)6B5S5ZO{IG#Xh%oaM9uTVgOqob1yjr6yVc`_HO=a< z^}b?|@{{2g@RBe0le-6!Pe*bfCWfnm+y%i$+6Xf3Q`??cvA>YhxM7+NQrvqW0+#l^ zRyc!3qu~gn9l)hpk@;0F0@rfD(w3!cEY_ZS${_d^gHMp>tUS3QAVx*t3S;uHDCCC>s)j&&Eu+fv8Mh(*wfbr{At#3E z9X~nPU(fr=!Tx#}$$qsX;`?hqG2kSG=|Fv5Ne1Prsb@G3NBXND`o_KT8f5qZW}o*D z_LnR3RLlK!7CSt?zYgFi@%!s$8Z^-Wvul``?s&dD`dlFu;@&rU8+KkD(_d|0_xcO# z;lb!2ST>=xKjJiTHSQXyN^v`MLX{Xf_@7Juy|j1v36zzf+J$TqwrO`!qTcF%Ie)Ii zu3vk>l4>1w2BZf*0-fkS&p!`=x9%Aj5N=x>iLld4)|q-h+X7JnsIK$8@8UD zN<8^5<6Mt7ToCA-e#k!tRtSP z{G1~dx{&zOPJ>K#&ZhdSGao5ubFK6gK$^x6H?JAzB)shmj3?m9e;Fr%w>BB)gM?P~ zE}lim0P-D?19Wd71;4J*s$SJ!AMufLX8#e7thC)!)dPi;F+K6N6EeHwsa18xTkDK- zFUqo2_u$z{t6IIkRCSvo-%hK#Re#;jN6JZPRjq+T3T2&jD5O;tAaQG8{0%8u)i-#< zywCX&hzvlCia#j^f8(%cFp?t=?W}GbE zx*%l!P*ujc96wl=d8R8W zdrR316}r8aJ(s_-PW=&10VY5cd=PJx+5<>&%6S1l@oNenDd%gV$U1{Th3Trp%xlIO zgttxreNd}81V1y*KKM&)LlDk~@&hGtJ0K%%+u+$1l_LLpE!074E0HO|X{Wzd@{w|G zK8o#n2|rjii)VK&J9a;5*J6b}Ov}#audLI4h*Q9R$~Yen)^_y(8esbcKT+??e59O? zM3HqK0u^R#&cdt8#caHBY`(UyRC6yW%{r5TpZ_w>NW8I$=di| zs77mk3J;bC=ndvI<4nXGbx3c*q@3&cz-_@L(qk3;1wDs{p&^Y|rC}1@NW&G{0p$wn zcr<(n=94pT;|av0G(Ty|1MBtXHRF`wjWp~-n3OY)4}|qh6&gq&pxH)4gKbAf*i9P# zt-UirK_5en+B-KI8p@Q0ziU3*X!vlgdCfRvH)-fjn3QuRA8E&Bpjk=?1PyE>)}17W z?ci%hSV@^=Bed2MytP5fZFm}zr{PuE#%?FcgOp_UDM0u2NN#6HE>Mz%j;upw84 zii#{D$$f|;C4}JDgW`jdJ_F*Z$TKuhXtdz1cq0wZ^^$_@Q-HpU(Vz`@!i<(jbbPQq z$fs-@8rUa_iYy@w`x7Q5eTrX)iSO6SCEI9du{HZngGa;JhK5s= zhRaO8kBa*I%*4f^pVfEoU8XEBgs=sLd6{b8uIYt!~*-aXD zAxz4-ln>0)N`sLHw$adF+mUguK_Y3`Lr3Ho7R)*?phoT1I}Hs}m4>r4pKUaJ;22dJ z$ZpcmnJ_8mB0lg@512;yW#oZvGzD!tG7eeEf*<#gf|uiG#yJmvWmZ`oEpOToF3&dX zs$#+NXv2P98`_&Tw9+=5rupQNA%$aH+i)@7NXmAENjVqrfe*Vf8WZ*ye`Ou=$P~1E z$VgZ0j!eQR=g!3&M>OT$kW8V|dZ-FW@+I12qqiR!WZ9m?8(V)Zo|cZRbe5@^eX6L) zQj**aPk@Tv#)QELK$UB2m|Ay;n8|B#(285eJ%Z__pFT;~I2HzY;T%lbaKA~;%`KGIxSLxxVFB9i zvV_FL`JMubl)%j`3!jlF0=rnMaOwqyT*%+Chunt z*v4)=p3I~xvLcQr`;EaeJ}#r*7475;rA>~Et8$^TbS7!#C39i2=R#3Xa6VF2ym7d) z0)09H@PXTJ8Wi9O1K{1_w|?2d3Z1;-bfPx1#CEz-HqfGy#~u9;USFXsJc> z_-{O}H>_())EL%{7+$u(D=(aBbcjf%p_HyB-H zVk)mQRa=p&C{Q9Us@p77byaw{_7&vAZSXamo#DXiM0L2~?G?V-R+B1f>Mp*16O#G! zp<&5hs4M^vQoT6jR+sYp390~@Ry8{kV@?94Gc;m z-LyR=(mf;#5w!dGXlH{s_g3skLENxwF^;<~1MFQ@=P&fUbN)9V5AU95*6)stxLLo5 z1wym_+P!jS{iO*00V)2h|E|Ck6sMeeyxCd5Y}F{ZPOEZf`1=M38#u#P>ua7FeqV4A z?5n|`5=GsEPwWf$g+$Af!#y=?n9Yj)7CQGJCp~Ml{yth_mr#k=V?(ue4>EYcX=EO+ zHG7cXE@1aCj8jIf*@K)=H`Ws*Y(%xZ?RSU`I)NH%Tm-!%K1+@{T^}$ z$C6v;Is-N-2~pfSB%|0l3W|QY4HY599W2HBD#Z&9*{Bp(J{QN}I=AhBEyEPIX^G;u zCYe!0icd#KhwykmrFip=OYw*E<0)=4V3#n(ZCj#v_aMa&LeUDvvaCrvr}o>ZJl-b{ z#jOV%8m8E3iQ>y`bP0L92CNl|%?{G6z8jU|t8fAA^hcrg0F{WJ4pwUIWU$^bJSKpO>ghnm^H>DWdP4yA_3_j+?#1cEEr$sLDr=%g zDgIg;m;gp_(<**{{T!u%+IKfY4k-kIk^S|WpHNX?SJvDC`Sl;Xp7YK-MV3F+R$&4l z&shtaFtV2}XO3kT9!cJ+2MV(T>xP^cG(Kj>=OFx|{@=J1914>VvV$2lNP+4=Ap+GG zH11=GI9!SN0v0hBPFSID3^qLTbUYDMYeED){f)EixF4)UoU?I>I4BPh)Ko$QJu8j7 zSt5og5$_+gQ8_VpUOXqLe1r&kXd3Ur9~q%XC=n-bTq1hJ5+V9zGs-0|MQhqg2uLUa zZyjj3V5HV<_>t{^6%e`NX2Fx*IJJi>^ypb}YU)@RJh}GLGm4rcc9dUB%!pn41FFm> zszH(1OM}o;DAeCCr5ATt0kL;*fNz;Cov zs*&sEW^7*yg(ZtIhTh1@^V*n`=UZa@*<+^AYPqYO%J6EZvxciw!ku-c<8EPpnKj4} zO4(7CGF+#x*ZhWrMZi&t?NNhGba*OJUFpvIUcj+@3m=ZV_3?nDuv3fmLwC6na68rk%;Yc!&Mof+p_F38s&@x+~w*D4)j_ z+q?Tkc(n0+U^B4Rifxe0PlZW)Zb|MQO*_N>ca&_OkCFk4uizwKBho$@E^!$JCla{~#B17`?>w#~5@>WH= zWrN(dz%JHH>lz^w%RP}fi59MNHyO}c910{>9SR=VCH;dWhgVe*kvo7S8>ek$6t(Fkm4Qe&ISXwRLJdPV>vJ5Lso(x-wgY9XhWd1%@ z2Ck+I7}tA#K4AI?ib9X;gb(GOC`29d>VOX73tw*$5v_L_%eFqI(4Dtoq5$l|lr@hE znFwEs69v+&qQJNEBsl?b<|B_dhh-xo&f9HB8-BkyAE<4}BhDmN+u)I1avb}aUKr^0 z6+N_{gFO*mg-0ZO*!ZA?Gh9ei2aN%sn_$p+y5&F%zzP6xt_4_MXa0T(@9_vJi7!3Gs@Ya+AO}6OV^`oxcy{Cxoy@06`bn5a+?+I z{m8)Ic|)4@`v?9Z4@z(48Th1Mo8`5nR)n2dk%50Q6o@yqH4Tta zzesUSGPvxhZyF@Ii#2ytGNwNoYW|_VSyjEuMeE%+1I|y ze9Nu!xXgbjig%fRfOHVs>~EAWGbw?M@~O8A2Yr|MEP*K~PC0ksE#fkdKOi(g8%85P z<+m|r8Q24@&LrlD&KZK0Dhiz97w}x>M`+eSKdU~WNj+>*z7r9rCUC!ec5GwDu1(yT--s#-ZadQg_lS|lT8^@D9_5qG}d|!KX>^UOO zDc-_4qFST6dJ$FBhhKzsJVrfNr%PeH@?=rp=lP+s#9SE~iMhIQg~*IK1x?2ZTaHq; zytjLJc;tEyVORw%p1$w{pem%ob9&xdgg0k?qmEC^iHh*3`+X5!QksGgmXww!ZpvLD z-lQ~8c#bvXZ~%k<5wG9ZzoPEI3h@}YTTp0wTS)d-v+iqu0hnG3V^@rxU>&PPKI;~4 zc4_pAIP95*nteC}PPvn6){{eZK)-MBfU0Vwh*%%TXWCh8vuGw$wk9t%0EUJUK>&IP z@_XyVNZ{pgEjTUFBQm*djzbN`WhkK<_#P#C=cdrLYkNeRU!Ak$iKzC$W2E^=O;k5> z3|G3#f03jd&2{Cyez&)D%*fqMx`4g@g}&z5X4I?$}lUiY`h!xOdL24$kNW{R{TBijHm?`Nfn`9lk*qy%x zgLRR6HOiy27DWl&BS`2l_|I!|M^Rs&$U|)fu|&>N-+8?#xU{pC1*ZPc??M&g1jr`) zV`w)4l!_&EhOVkQ%C%f#e%{sV+{g4aPv>?v#Js43W`YHl9xTw)RfuiiT{9xoDY-rH zC51l8Knu$wCH91B0!ztFRerltr!3QgI|r))dWbBI2=|xfnD)-TXZ`KnN)b|+7k5?F z!s<|4M1}Q04al*2ss4H;=qA6Asw%W*VG;8{T@BS9^^Wuqz5sC|kx&OmZg^OG_xdTl zOlu;g+xwNqP+Gc1D6RioDg3I7*DQ}h689Nc=M#tD=qdLJl+BTw5joVwZvuU+HIc$s zcJV5Wq3}a`u>*?XA=Ls^fkJaMDQT1Jh{I+M80v1$Hj=9mKloF31sIuUybbipeAS|l z?ec?_OuG`DVS%MyU&L{N3q>4bu(kUS6v^RV*C0_N@^7L&tQC-deQaH^gx#$!`TB3P zhud4=vT@Y~qxbZrRNOpS_ZO8y2puPXd^AnR48?r4BFOG+dP|2QeBk&lo$Vi!hJufW z#2LS#oiX@`=vzf}Z3q$cc&f-9A*OUh6?M2X*iX4?Xtp9EE56&YB~iMSZf-Ly?ZL&G zHc*43a7zzFMO&Tjcm~qcnC3CYGX;H~gK+7A%RA#7+VG>KTfwryoiKq14=$cSMv$9j zfzKoDL@@v>lJrYsf5egvxWj8TvBREBOm}=~CqDYT%?J28;UbK@erQOJ`SQ=A{@i3P5e{J!=b*SlD-hfp5EML9rzV5k8LCX_QH|;1R z!d%~VP@qC&x_<`QpzG@KTDa>rRFR94^ zY4_(RO%OzAQ3S<;V5T6LSpedBOA@{SFtc#p>#$VORoKd;jbE6gS&*N`@xAjhW<0lm zrMH>}Q-H+H&~z|yVRae369dZ}j?pNQ4K*Gom--$kDnlEQd zO0AqhFO=uX+F2@Kl(mbuYf09&WMzTn-8fZ&ef>gK{OYgOtetHZV_|tAV?V^jr{%&! z4A$`q)ZEcjYBzv!?aTI%m2zI+D-x*-r`M-R_13z9f;ZoQ=CTiBCD+2ClVp{3o1Mg{ z(o6UOry6#j*_JB!sezm_XL|;wtyt+p$8#0;Ghw-g_CycEIO!uk{0b` zie{4Au_PR;ih&!Q1_Si6cWsN_-wc=WfgWU005TjqsleLhk53`Drz%R#@RiN(vZ+P_ z;8W#8_0qYf>lHk*ubAu#xUc}cdp=TZ129?G1~loJ0aPz#`qj(6BkK|M=GR!5{Be8b z&Sd4z&s%E`wn#|#972u{5|WRTQfxyxsddQU87Z;ZNl8(L>QzSb>SSgtoV+dCFt8LN zQkcGYYhDO@b>W2h*uCY;O_U8x6}>i5emC)0*Nbo1~0Q>KnQ85Ytf$0RG^p znA)D5=-n41dkio~Dw2|}6}B5pki7Uf4ipQF1L>*WKcWB{d8;<^(V|#k3h6Yxu0G^E zYkQtlTP#whLjgFaD9%$OI7bjFI#Pz^2x!f@f>Sf3MU88=)y|uyhzks2zflW!he**> z-gPJnN15~0AS8mW=~{eo;Rdudm#$7Bx=sPzjb|IWZd1e~4PtzKXC$(%BD`I5kB+Z4 zv}>H~ox_e9^m$g843EWkD9#_ZG-7NuOTGb3a=DxKdsX9f39`_XJw?wv}~ zxrQWYOXjGy9MYna%H{PV=SZWC43Ky;ChyWp)*{{;Qz;JAZI3*EW{6R&{e@?uRS65S z_zb*s{Aug^ynv%hsbUmxG$ktSK$3ZvPeJ$U#GKnV~d3wV54A zok%14h4tu1xtLPAK+;QgK#rLx3Bc{NBy-eA;hPThc5X3DS2I}e)jEzhbwC6Jxxth> zfl97UPhI5JKqaQ72~>*20_2}SpD3YBMJCY9A;Ro@_;M%EZ_v)hv$oX1cfYpuf=y#( zDLjkz1+&6~Z{ZnIw`S-?@aUnLigO!-6P;FSXOssttGT^ax88v}mQ`-3_7`C7h1*gj z%xW;n@C{?t{<{!0sGX%G{nd~JHOric_7@E6A6s)y7k&&AMhQQRK+V=l3T(RrH-|r{ zS)aGF^1K5|`?@`LVjwv(F%T9M^YMEo25HWJz}CU#`Pqtdo`H!W!t!yWBL|I1ap_F{ zH4{Ui30IhSxQCQ8%QpER3(ManCOVI>>{och<(#PU2~)qqiGMZ|*~3cjj)q>N@DO?w zoyBx+2yS7QoC?OS&lLJkd+nwhq}QZYZx+)IvU~ISW-*<4g0tANQ$)m>5kHHm@CO@L zrA0IY?7*OVL|L`Et?gQbox^2;VF6wiGIH+)@h%08}jFcCKbUYO?I~BJEoL zNNpRWNk(ep_3)p_ELfwMPq39DCLZ7UkT39n^v5)}kI5}vKy^`2GSKht^-qvDoGeT< z9I0$NEHXPLO4l%R*edak43~Ws?BN$0_ES z{%>Xbe@D>I{-5oAtduF3E%?wb_WDg7m9q=)gmuJhl?4y5Fxd{{(n>fy!r=oQ$=or2 z|B0Z?d=X6gN*a)BIcZ6emd_<{S#iaR1UB#JdXOTq0QoJaEB{VLMJ%VA5KqtozFe_+ z>T_^r2a%blmD~FjD7W3sKLaj|!dgw=-lSVu+CZ&z$q7D1DOvtCK7}Y=B7Rm8FI?AJ z){hpk5z_Elyf1n%Y^Zfq#G1hmq$KL+E5eHn!c1}}lxo@TNK}c@K|qKRAI9i7lr!LZ zrZ3bAiv!yP#urNV7LZWcfD3(8(t5O4jAyo)m{Hz0;YN&s3o9<;s=^zlVOYgy4jO~i zKwPQQ#$b;iE@QuZCHjpyAhrY!&g7}%?2?EHME;e|X~c?>NXWQSVS1`M@+#|MNa zQPp_BS~HJ5r#)Qp+XjeCu3q~D<19c%{f_U z>-U8K)KW+nXcLzHs!hlwcLuwt)M`DY#uP{+AF)cUR+JjERjGM^VWpN6Wf-H>mj5Zh zX)2TTE489=evw$6^xKmEJ`V@Kz(-~|5wow;`QQ$0Qpb4R7yYX!$ zE4{t3vQ@pYbLpl^K8uDzR1EzLJr@y&XSHS9QxhANzXELn+E9AD+fnB$Jb{&J0hNoC!0`{14G1*Dt1`)_PEtw|?V zd*ORape2PGlyNaVqYMH-$h(r+#rTvi<2NzWX5HdX5yMi#CUZkIvgzDqtk-qdtr`ZI z`L1xQ7EWkrM zAXy}@#p&c~0)nv1>+#GcUHl|SHu;8mLn6yvxT%chn|y?sWDlh8Ve}t;=)VtJp;}?a)E+-2tNLh5^maa^TN*(#X7*p8YJ|tA1t9{jv;i67TMG%K_F5!a0xQTbgWR=s)hW?0dao`n?s1=s=|2_9Cq%2 zeMfJbleSOEH74!cddR$tAfAJemiy`nU!=d{ATPhj&u5f|_Yri9-K=@LfCz{gaoU{1 zd1;RP@?%>U$rmsy|7VroJK7VyQf1zxe+M|+67w!VQJ>(Ogn5y6umM)EwW~)3u~Qm~ z%_VGDAks2PCmWa;+6KAtNHRz>D3aqTlVJ6=xawX~IbYzoUi~$WdzKHI5>aY==r>NI zUn~cV+L?*~x~ESsHLPUTYPzV{P~F}k;x5ROSKavj;*mj$V|b2My# znESw{%XkoCYrbuX*yEL0Z=N<^K)Tu%3eMAZN|s2o#hUw#jB?@upEdCa}xkfQrW zc|2qIs0kaD4=R-5bB^5NO{^&$x2B*3A64fU78tyssj?DCtkLyC2dK64M3C_{#SWDM zixW%euV%bAo4aBC%STeH&DA4_RvHD_El*6yoz%Bggfyl$w+)OnrXE>})>&T=Aj)wr z9fjWd#&D%<0qZCz*%je(xSE4k2b8lZ@YqlV)ye2#nAaAdT(_O{oKgKG#dMRwWY==D zaf=@8opqsZtqgbT7ZQ|V_v|9x9vc5a8F)s4k*ZeLWUY5wuLa|33JNEEK#hU+Rch{! z|FZ34tDt+eRl%Sr5=oaquSm;-$`?%*FtzCKyOfeLyrytrVnT66T5{5tL2(Z)FK+cM zKU%chee@FZ=^xN?v(oZ6pBBVIA3`c`(VSs6hvY#XCqw(lii_hwoK~|qpy8rx>RJh* zV$o?@w4*JGzGWX(y8CBIU4jD6!z%~bs|eq_*pp*l$e^)GjhN*M*K;aS6s|d6x1<67 zlwm0GbYcAUH0jdLIKP0W7|Jy3FE*=e0a>R3Z^A^+Vy6;gBjp=nI2dKZH+-&PsgPQ&G7QVc_(55HgZj5UJwF~R#MwdRi-F6O5_5Uq;~DZ zCKVH_1ZtvwC9Ks#gZi^WrCy{W>~m-9QMy5~^)uMK?q=oFE8RaWp(`daJu2Zp{VgEj zbyNhY1*bs5utd_e^HjQ%a^?I36)(zp=Bt+E{DK#NEU>4FooCAW&9+e40@hP7n*Aa* z%3LshOME)%x5m~5?hF_WX86w4CZ1%A`dF($xQwJ*734r4q|#_7uJ?fi@?!Pk|dEwsRwHmOV)qeD+v> zY$z(e(wRhOJe)pe5zgC`(y!dGa3bbAyuW}w!;o+uH9&+Dsfo%)dQC;~-mcj0G}tg~ zt7O{D&ruXNy^eqv_RI)d7M*p6q8pk^T*MVR3Iv4do7G>$vEE?a%wn}+BtzjC33Bm3 zn(f@@LA5nn?OR_O8KDMD_c?eK5BX%>+jbR5C`Fww)5SQsG8}W$W++dq%7%&q0$(O8 zd%ksXMcaDh!QupdU^rzPU|^Aop~zp{x;WMLC9o=Z22Ee^eadH8Sos`a%jGJc?M;@|AM3$P`IG{BT+UFy)aT^{(v`fRKnRhERNj=9pt|b++3!?WRdh7VMF5bx zuk2iE)YaFYn!f%$>I&SYx_Zo(_0`qgCdrOQ=+ek&%vD#-e-J6GH&{=%So5i?3FxYb zy4uStBkHRBOI252K{*5Rzfe~XZb{y?sIKnO$=|B0J3k39v~a>b7&t+7HO^#P25pGC zdbbgl#84WbnYM|}rvw~a=RCi8519635wW}2^6<)23 z#)UYO3L0;;V{@eP=;IHyx9!-3ff6wU(S-KJsd{^k_0R)Mm0YE)q-&5IzfiH=YOtaE zv<(zm(+0bues>#zl~C$$N3iq#aDWq~ooj;$C}4AnbZEJY4E*&;c7UA>Er&fUe__DqjU|Ft{r zL?t7?JMM(yvQ?*8H)&7}y8G9m%^kITEneUJNMvd$8d+zW4>lBIhmY3qUYL!UbD=hW zwgr~m%9GJg)#^69miKqnU7}FehoH=!W~U~Hx7)Fcp+d4QKB$b<)YkfeR-DeSFrV3MkE<^!MFXqQ$CdGiSpQ(jNIR>osK@s4V0x|W(f|;! z;%Ggz(ehk|dioy0M$B?8KK1967pCPRSd^WpEEw+rV3om%3z1!M2Uxc{1631=7yF<_ z7nKdyB)D5bf(b+~b<@tM$M7W)=bAb;SR3@-S1JR)p$!ff83o0RCr3~U zCO@<8T=2%Naau^810TM&qcH32^hVo)Q%G&r*#WOO4u}uIb;cV%l{REP zW5b+n@iXIWg1@bhxb`htM$QF%Xn7XODUYxUXL}Ukzl^gf-te*1#VEv&FfPPXN~N8T zRtUOUqDwntq*2b}`s)QgQchbwvd*`FG*$h}yk?wl@W!fE;i*-9h&NPq0}9Fa9`P(f z4v;^I9H1LVbQob;)fM`yoR5?<<4vh514vWV3+6TBEWsPAT8yVw^#tBf)iEf`s*3UK zq*bMm19XEG`IGOn=bQohYbYNn=TNQc4nUf!s?2M~xeafuY8sweRXN^JRT0Xvst$N| z)vC@y4$!qz>92G6NI8$AaKMFC&|ky(!1n-A7KNSw zq#?7$yk?xo@J2G{;K_d(ry6e{vlGg)s*c2=RgFOo&=o53+INNdo9eI5e59Nmh0HDR zgJqdFOv_$XFZpd0`bI7Lo6kRleW31tqlVV#*qgMb4kSG7e5k*+CyK1I4}O_u>}_5% zP7l2Cdx$HROEtT*MA|8XXAMN{i(gjX*h^BlnZ@8A@2iDfF;O51_otXFJh68*s67;H zgR#z>wVYifxlV?xRhovw&D&))4!nNeLKY+rYmQGQ{=}7?BgaSq7Bpb#g%q zq**MGOIFHsLj&v|`YP^TNJ_*2!<@D5{K9YQxKmw=skr`1N7U^=Q%drR3wtDH3xtbp zhIQW+7A0!2vwUqGx{Q<7w$_H2e8O6ZXQ{JB#HvxbwC(KE)}=+-Zo!0XX~U$MsPXRq z8fK>I?I8PT;>x$BwJWoUDQuxySD8h8ci;arVpS4T*tG2x`XK{tQCFik#2R~$^+~gg zGqjTyxH3`zCMnI)tc&O?uK@T{L?#t=cnRR&6`r*i??bD2?{RKWF6VYrLfEUHzokuPyV%aKS$zskuJpj?U|6_E9V73{_}(AyuEa1<0)y7y zo5y?R2ELOHbSa^v zMZeUuTIxPktt$;E5WMM?dJAuzk+Q;+$~gbT8&vB!l%;yS3Qt4C9moL@qlq=`IKmg_ z68$xvkCe<=DD)^Gjrx1Yyk?w-@kVoL+sDORnu(toXFC3}nhH}5KCg>6s@F3W;|%~n zKI=9ZtIR9rf4otl4kk>>xs{K!^UTkpQ18O8jPu27ivLqQrF_~sQK3IT3ZN5j$_()~ zf8o2uc);a`f96Os z?8W$%apvF$%RY>!bYc3GOsHt26WwH!qeLn zdX|>GmA_y|pb%FA8D|H)u}z;Sl?UP{ezoNz<($dtv(DwnGmO5}yk?wHcq5~CP!j*f z@@eM+33hQt01ZDHCcI^+ZtNxMIQEgWy_DocV zf;ylg+XtK_TVe00BJl{OjOx^D#VZ;R4*@lrdLC)1*H*Zju1+PMbP=BepeLj8Ne2)0 zw3GLBVsW+W4sNcdoTlWlb;lS=<25A^b8%XY20+dd_HM`RY#|#(jb$`)zaUiFX?{%` zkE9w7GG=|LSs&P}GK8I!IBsWb(G;hRM-p3JvL2Vc%fCQJ%g0HZy8y(CovF-$N8{uBaQScp_i+YOis zebAp3boUVGg>|>~nYg15^s54`TdwqLrTa@yk*41?yTQw57cT6e)mOv5rGIj zWkIJhBUQh+p>?bJUUyD;c!!i=ZR`cXtjvb)Lusw9gsV(ZY@3RRJuL@tAfWuRI1HKk z;*@cYhqesbXz2ldX>6xX60rqYZ8dinlWT=3XL};L7v6to=^aRz?xQ-2=dD+BJDLZm zLc61pj$XZ!?P#7U$s=wkBjR@O(To=z3EnMkXrDp>3JBa?x&zm0DfZM51F&g*N?;nX zjib)lO_aVr82S{2l^&vFAMvyy`e>^Vkd>Z$F_9})f=Z-1?rVO<3I=gl>9P2D6b1Me zhk$(P*+R4LHQ8v(Y(KMuW+uI|l$XVBkq@z@LYy0dxUlY`mzt6)OQf?ZsLCt1iFhxw?Ynr7GYnelc{gj@BEhJH=XOPG0S{*$M;|Q0C9}!6Et4Um+ z2^5c~V&fO_j?aVnj1EHqbHc=n4ufiR+&j+9mrYN0+4DubPdfFsyT6Ud9YAuqoQRz4 zNF3?$1lCI?fiI6F)*|eZk)^!paV<8K*6M~59iJ}uS&O^ji3k{mL#>5*K#=mpIzHF6 zuvpI~RvTu?vuR(h>VR`Kw?B#(H?-A*k66vaF!2`j2mK2x=d#3VT#h)dS-F8i9yj_Y zR*!$$+eq;DEzX>q#~wNY8FBW|J}eNjhdP^-fIakC--tc*;~bWvJ+umM)(#ZuYA#NF zTbz1{A{oDVaeyys`&p@p6*U+%-c6~0(Jk?{%XDCX;%an-2uXE-E%Z7w+C}>CY49n`^?sh^$))dj!{m zmyTOhP{PQxb6-ScTDxPt$TVC)$D)6?^>A!t2hk{(aW+P?e|yO=fLj=HVyZgk&7daM0V$xPyxg8Zc`Av7pmHttsqGXDi+{o)}SWp{0IS#qj2i_&tIZDFjm z6vJYXuYw@Ald`0jEf9N4tOkG8@1U@)eAt*9R?QB3XN7$eQih=j4#5z_<*Oy9P-&Zf zsI{45>0qODZmXt#Vg&A_rKei}+M#-9G8{$4RNOTbCInpLK5U3Jzxv@ez^4gb&5d^9 zJA(pUI0F2G1ja3yF45Y^zlBQ;*|Gdc1$7>g*Hf0J~<0|JO0}|anuw?pBg5{9f zErFqe^BcPmvCJ0X8FYKU1=p>rW^Jj}e@rzWo5~!UT8_buNY_!ExNP`_ekv=K=1yv^ zoJ>+W+0M7JEw!^NeHz(B6)F@Ra2TIyXNE1L))kdY7n$bt9>|n@u;7mhTVi3s$U&O) z7n@|RWrP~XMmNs;CF&1Xh(QsEY{870XSPNc6UKbVd?z19nhs?gvjNYTRnF!mU0Kw8 zQT*eK*XSxRXkFuPQ0{`ex7J^00nDlmxFK?Ob3b}Eq6S%HH0Psk>|%5#D%)Rijn2iz zPz#}%6IdX8TjIfS?q4$_l}4f>ATMGk7aqo;JQ-TZtU*zEU|MGlxNjc)SRRN`DNHV+ z)dV8{phPbJr*cF>cKrG`CZXpaN)E*nN##ekZzf?NPZIm5Fgck-{%@$NADEY_ zvO2Yh%vcg`j}=BvQrk<(Q^`75`8aWjkt~`ASTQx;hjfvwz6x-@2N2uIF!F+@Zfc>) z@IP@$_5Ebi2H&IMwz8n@r)UceTGc$})K1Tczos&*e9kFk)49Hf^Li!^mCDDV!na=E z-QQE4_V9>jUug^SeI~iLl0+qQ*$duyi!tcu%r)X-4x&I=VQe?7H5ss-Zi*aD|9&zSrydYU9sqdSdPWe*D@E?wfM2P z)g1^=4+@sj5OW@4nN&J%Qb7q+v?mU+L_r87=*jq1U}Q$Z-5?=FfpE8^R0X*yYi=W7 z__87kE#N-$y-bsnqj5-&_Y3rOH97a+OfvnI=hFY^6-Zeg`)vNd!Ir=zWAys~TWCG3 ziP7(^Y*AoE=d5eQiN=tAZ>Y1w)MNXG==WUZK0O!F#?+Yy^xFXC{wBIJtlZ_>P`+~i zZc(iCZ%jf{eJR@U%DtetpD6eLOSS%!gha>E6Q|Z^2n+2P(N(t?3`VWjEVS~HN3Box z05(LRqH6sw(15fW8lQ@&^}Yt}?@{YFZpyAOYW>yK@}ny1eaQClhI)(Z&k0HZ2k|ww zi4ZL4hKN_@$8S(kz#=wZEITStNTf3D9AuNk_u_xCtecVw-h^kMoi>ovZa%5I+A3m6 zJqD!O^`QA;+3E$pf?^;7n)`uGvfoegzF2k@%IiWgOz1xj3h;CoRvERqmiMcQmHd3e zRuD_%1OF0!)-O?NW?5j_nC6=L%zN>rofC1(i2D^lIj4kH&|hFT+FLS?;F2eom`b0R zqb&YdVs3 z>*v@FptPM&>7(;~(PX9JIDql1R7^fs3QK)pk3!o2v0p5E9A$IlX3qYWk0%sgw~7lt z$WA$y%cbE%>Bp*vJSbEe8he(Kz|h#;o)OaXgvKsu64L98E8vG(1I&XgvLcjI+w$&v z9l5lkdm(XR*j@4jTv@H~Aux4ifiq&5&-hsJ`ov>))rN%Ec(Pl;i26P@ zUzd3vYY)I*0OSzZkSD2e#|F8^~P`Eklxxt<4*ZxUQi@*TgF`^ zSw`dYai2G;FozrfzjlKW}r zKwHQ^C5FDGm30biP|9>rj?3*bqddCIFnx{=s4r`1B|FITS`mR};GQa6YpU=t;5@Ma8`$nJ_HqjH0c!VBYLib1bJ;`=B@pM4$$eP+YdFTeNcbYkyqXn6 zL(RbPS8bm4-T6E1>}%1MjVsmLBp{U7)(Ed_DTolML^TQOt0f1hJcA%vQCYlZfxC;% zH4DD}iR!}1M zOz#CNB8eOFczPLJ;+ifn<~ZuJ?jewrask5eWx^VV#z9z*knkQ#+D}gy!Z!|SzwQHR z**K^kBPu%+#x)ZzlHA6N!r^#T!S5}hqNz353~=xM76{=@j##vza0WA|g9G3xi7qu6 zSp9-`J(YJ09^a_Et3x!*M&exwQN?hlqD7uPr98vo8)YB7yp*KBHH-1XhWFQoZ(-!w z=n)MCfCL5uLt;32kp$$L3@Ow&8@mCO?tP!dm;?9hZOwt=vL#RjY6|4)&WuVhO{@27 zJjD{TPIwi!-LVm7k-yFY%i%=&fn~ZWk7`S^sb**WdNb?Snc}<~L}&enKjExDt#sTp zo%MH_i&=j+7&XQrWM=(|!Fsd)CYNy#k2KkK)*t&Yl<4MfQHh#J1ks5Pzhm)Qb!eTw zds+Nxxpau?0c{Engx;2kxk+TNnz?pTJ5ldV})_o81#q2(seSw#qkN!7l-eMpd zz8XLBex_VY!y8&Z}2sNa3yV#HE^ zvq73s%N#c6N_Hjg*Rh3kLacRn0$Csv!#})Z+|j2j>zp^+i^M$#uiT@}IDf$#<9E-- zlm9Z#@pxn4Ycw^zeYvCX8$T(-=FJnMY z$CLk%KcE~t@hN9G!Eld?2!Qh^{WXG*l(QI*jPqywWm)F2Qb3nZl(Iq}u2a#nLs%&5 zG_ZbvWt?XWC6_ZJ<*dL@l)ayil(S%#K);TMAk8|<&1=S~$6F_WUP+jg^Aa8z=Q;dk zyI%Q_0_?mZiF*PWY3qh3{~>>Z7TQ^B`&Gow* z%Osvfmn7;3Z!TT_j4i;y2n(|W$B3V8)mVnIve`03!EP~NnZ$fpw)}W0H>~EDj+aNWD|5lfjzvOj;c#0mnpk3hTyrfanC8b zXWcL100F{hqaL!KclDGOrR2Z5sP5ZGM2Y1;8HG$e2l?67RuD_%8L;6f$xA3mY3FS%w-WMHt1&pe zCc$60dky@B)in1w7K0Ht^KfK1IPt~#3+sGhuCVxHiCM1U5%tDjc(etU9`wC`@&3Y{ z473SdLZAC3TJ#rw`jBTCU<~>G!WvMM)85Q3>u>Mdijd5oXmQ83_ahCHsG*p%6O6y` z&we4D_R;?Q{=!W)^rI#^@$a+&lpgF;`oV)OSs{ItQX2(d;{$sXl6A5E!jUMO!^dlP zj`A^HEzi=L$je)2w?yHs44e!ohKJNnIxA3UjwT&rlkAAYW$aW8bsB}5qZn4;JM*51 z=NWHpd@`q4^s!yO%Gk?f8`^Y%1s2QA7uZjsveBU!gRR}}peu)ehX#piL9t$@6_9_w z%xZ~$Cm6!MQNmuez?_yw?+MPTlvX6F+0nmHZ7{e?;;FJOoIG}=v|16QEdm|9T%qml z^^ZvdqQ?~xm`;U*-;idrH278#9T7qVJ)SCZJA&98mh{}w_7iVm7#mz{s*Cwz#Y7kL zrTUu3#eCEnk)^uRm4d+rxNx2`@t(E?6Y2_+%XC3icZNcAwdg9GivhvL;xvSF4&TwJ zD@4gU$Kfacfem$MYr!uckUk+Dl{RaHX1#2(GKq3kq(!3yTk zCwMbb+;W*eog{PNHclUn5a{+;#v$YCmq+}w=+LYo$-uZY*rYkJ#j{GNAZw4gQ zc7+&3zR&mwoeJvyqRm(|Q~6|^7H~91S8nCumD_Wxh+YqeG(H3AqybfTu422)V9Pl3 z?t%@@Rox3%EmERFe&BaH5sBvqHb;lWoJJTY5H@RjbK*D$?Ld|Vzk|?K4B%ABYH-R$ zyYc_f)u^x8M1I}@tI{59wn5Q^E*rPHVD!-d1TZ>f7_QaBt41<5OVX$!LDLGD<@itq zFiDDhgk6fYqVoYY6``fJ64>LZ)#Zur6_iF~Gj53fUhdK+uvc4i=7^#q}1e=T-U0}k*nz`at$KL6mbpBqU%*{=A@|GMAgE9L=I3mXvjq5L*H4swmJ~R zjZ`AKTOul)k~=USFz(SC&V_tu^QhqW^H>r3d!CLkQyeCVQPGjIa966H-7$--us zQoXC8I{zpy4Uh7-Tp8s^#SM0Em{DGTv0|&3;fY&0YKE)wLpqVg{}VR-zunf(9GEyp zVWd{&%JIvJjT7ksKH_>*6O*hM&c)EZmrF&aGWY%tbdx`f>^)LOfxX+4PYLz{zby7org>bj+nb!LVaBT zbxF&ptBKkwkP7E0f%b>YGwePQdrt-Ja<4Ea5LOtS@}QmC2R8d&{G5XUS5a3u?ldw4 z*SYc{)rz)D&1gKmyP&LYq{fticZ2eE#NFYJaT%3^4jQ?Y2&U|tOjOW52GsbDYdu-L z4Z$8Ew=r+GhcMw2@>^@QO}k5-)bQx(Rd;!HLMdk5tKXHns@=Yf8ljyTx%TuJ5aZk7 zxg)f{Qby?s=B7}E)9)d$&wepNj(8Qzyw!$eim+h8YYEZb!@ z$V7Eane4q`ly+iy1LfJJ?94eUr86rVF^7qy$sE0~H;G9PJiG)xg~MT$fhiDI{Gaal z^=P_uY0L1}(|1xhh82mXw}XIm$K&PE=L9}L&J`cPx%C2LGiXOeyn=6S;f>6)RWUcO z{8f<1M^BbP1|SZ39GCxgQ9*j(Yk0V?IQVnCi9F#jc3WcL~Rf^D2I3oI3pFj=(0YB;~w@pVmCz!26E2M8zQfKe+FxH|zy>1J*?Qub?MWMja#LJsQv>X1c1>XGl@7xs-sJ{S_T9%?g%bA zet<^+BDQU<(Vbm3UAImG2=FWlUUp$jpL*uw>^EFi?jb$(2s zka3;&wMh}zc`qdwb9I>1Dks39WuF~r1lexD=A4}$`hM62}Xw$D#O;@P(;e9|e z=sKT@BAC*{TUKp?M77ZE?`Q?&-ymC8EMc;75a6FV!?1avklB-Lp5oCAqX=FSvnTm= zo~?&d+6MS1q~`Lkk56W!MITG%4!T29Ljgc{+uf!o{X(k7AdB8^6GE407SITTB1zP^ zLzGeqX2^vwr%ja7__1`e)<&jQ_!Y+pVbAPP?_G0iV&00=s*9p#>Ih(PRG2D6Dk8xCI90W}qiW6vwuXaxJ%ipHigc z*rpbLZ2$H%#Js4idH`Mwg$3Xw?-P4j_HL#}dg~Y(daXW*W zoc5NtincdiN|q{8wz;FlAKT^xkDnsP>{_`un>NSqT0NnDF$ga-D`-6zYKsd8AXu$z zm}L=AA;VFcXV>a)K?fTU9iojUBC~6CRtOROp+#;x5Szo2k^;*T9-NR{gHj7HNn+SN zRUnV4&A^^oTAutK&!BwhuGalpQ!RM)jK1dC)!Kn;taPGuVRyC_RByxO6m~_f*4gQ5 zQ#z9}Qz@63wCrBRqb~ec5;&s9gRDE za+A+0X(yW;O^#^ra%pktOt#y6;EJz?1@d_$^hc@oM{=?ZnWY}#`I;y_w4qHDU~S?9 zSoFZ!Q?4n~y|15_!wd&0*C{DKPf}7^?4Z7AN!a3htv(?kcDAN6Bt z%2Chl(qAy}{vd&c+_Q-3bbcJmE1ZuDQ{km zOtUqmGx^uZG+#~gK4$aK9}`rN9=qv(EYq+5lE1}9G|UGM_cWxmDAQc-hGqJtlzaUDls4zgoRx2@qZx~kNljUO9OhfAQwGbkc+NSUB}jx&g5Ss z7lGzkxiH^axb4ROv0Oa+Q~nm0C$6W6rc^|?G^G4ax%hlp9=WJcv`?q3T;>x(I{4DycqZj6ByQXug`qDn3%`)<;9E-yFjOq3ynI#HL_}o&5LvugllL*nEs9C5=5Mo9h}I05(v2*d z4=lc!G&-zes!YK!=n)Ro9t|o0p(X}s=)Qc_Z@gl3n6JOyZi*T=pq4iuV+=b3jfq+m z*zdfE-cDnC`bPa!ZR;R|Ig{K2L~N+qAuJ?_kN*o9-x=$tylQ7(k#UluG9DnR$ru;r ztCS~B&<<#^UvnkBXT}#spMU0axdjF%$P09;w}jBZuCY8*nbI8qbgKEB?mx?voJgk* zLrWsp>c+U+r~NZW*mmM_xXye8a->0yq)H%^b)5P7>QIxFN%B)&8`|ZBMVIqoy4?L= zTBbd|YoW^rjmd9)o;D`G)jDEKx4dF>RaA$trKL0Z*XWQy(?2$XWj|k4H*83Vv_}XJ(hqR?x$1 zHNC^uOiy=gx|WYVhX_>GUA;gyN8OLL=JrDG&{D+TV($n(pi?l%4TbycOzeu`BG1$* zAc}sqVO`g- zu%n`4iGmPBbTt+f3yKBBf{OSmDkv5-8Wysw3sDJTkD`DbMIj<3Sh9#MHbk*tzai+0 zohJN0pP6&+J$H8#ec$i*`Tu^;kLMwG_RgGhX6DSy>F3&XrSn;0-pKtBA!@f*in(bdP0r#+>??R$di}20 z`YnpkNf03k=l{a{*LT!{r`;raSY{&`B`MxV+$^%LT+H{8g~S^cNH9}`ZiIsPvIRHg zon*2FFQcfNTSPaVw9Er7GL&Ga`cOA(aQlSJId@j_%?x=>{1|3cGN1L@OZp>xQ8Hg% zkK9HPa;Q%zwWC6$X-dDOR>9-Wn(ve1e3 zgrLMy)bW`ystKEevg^Rv6jY`=h03Cvf{^^&X;cGxtjP+kIDi^&+JAwJ<$KD(tETdb zKPC3mRhzl7+Few>H*e6YRlS^B5PyyDA^)yMeuFPgRrIH{x}$0Va>--p>xMaexg_O| zc~a3rV?HIZo8~ao=3tlcOGlUM-hlEPx@-BP=&q8w4s?fG8%1%Qj}paIT00c|?-MJJ z7~w#&88O1&Ul!g3VJ>hHcrGaUFTB8IBE8RUBGWw=mI-0tp(BL>-g=1L0_NkOVVyPX zQaC)oy|`J|+G7$ITD<*H6f3*<5Q}BUf=rthe@2DdN=C$%w+G5wIZkh1b&^-y>7bRo z9Lf{t*h8z}`%zlS5KR{@|A7&Yp|hFwSgQlqh$rV&JQjk~HY*s5 zvO|3Cd+Gc6U(P zKjwR?LZv~*Yn1RnLx?792EMg{j@TH6y3#e*6pU$4lCYa&J6l)wLuYIMiIUr;UXz-VX7yl5`D>(THvBx9ih|@1cVA%F+IoBPBheO5e9^&-baegXB?o zxKO42-(Rrzfz)o)^#2}dB;tmU(lwh3#(YbIKmnVS;2lfg+@%s_BYGd3tj&HZ%(?kpK6_?l@Qg0)t z1bLxB2aL>oIcBI$guDH> zyP4m9*z+pvb8fNnFS~4Bpw`&OA>6WHqMRx~Hs;GQyBMt4%S+v8gKR1C7rTugL!`UX zyBu_t4QQk(F<+j!?`k!r zn%MNblF^e!@kGt^^Xt8GJdS`tO)r>TYi3c%UG$SgGZ=#si%ZSLYJ-%!8R`P1e)Gme z>Gsb>dBdC+P*FzH?;wi*_D856+2<79!Pb36V0yQ%>d(4J%*Xs861`Y0kBTZhvJK;B zR3kE`VZ;?|V07@^hcpbCT7)(w0QJ@??`P8`<65GQw@~YcD?!(gpc&)uBLDERIJS36 znCzo8%?u5O?8N8*UalMJs{}s`*E&-@n=#$6>P@b?%|-*)cosZQi?BNoSiFfUM`^a@ zCR>Qs3P>wjOtDv6Y%)&u70m9lndxD3`v8Nm+^2;5ak2EmnxL5 zxw2qPxdA3VFTlVN%}du@Fk(R6RFmH^TJYK{O|VN9?e{M@8jn>YFp|akn^Kk&Vs9THPc+JdGr_ej1WYZ2g}~Cd4Y)M%g%R z_vwhp;XWPFs`Vge9}K+17@sDv8@P?HS;`bEMUIxE#>~#{`-f_k+R)yCHIlgl3@gPM zYWwCXQJW&fX6mZ3qfsJ@oEA-a&tDc8i;3S&P5d5WtZJYdO~Me?#BnBI6Z)3Y3YE#P zlKJvZ=Ale3O?vIiZ$1fHCtpZX7XOk=La;M6*Mp+P%guVW(%{fW!`qlMRNCW+ew87v z*5Y;qmYLSEo7qE}84(4uE69FME>y>E!izPbQGPMYhTLbV z+&xW&fSYOOTdDEQth$R_3H6zwDWf7wK~0{5-5s2op66*_HHb zcyq0pqA!&JvpVKL(Tp?0(U8ybHVyO}U%3m#nB_G)R4~VFB$keRlbW{v(Vw}iN!`5)?Z@G)|riUs#>}Mk$H;71cD<`s2}eXp$3aC zbj+11dn__$7wE3b8nU0#Ju5w(Iaq}mgJ|6Qb9 z-YfOalSQ@@lHaRWL0r1p&NPA@=p*3}O?bXd=!7xN+?jdD*dHlOqrdP%qYya)E;|v4 zHI4qm0ah512-!o3ZrZ%Ot_3=ZBsPVAdy&mbvoX^eAh=k$do?7`O2_OgXLM-3s+B?I zqE4}Aq%#fB4u%Wc0ClFe|GojbfOFWv*n)}b4#pobhf9^C1EL)L@3a6bACq#mX}}CT z1R1Kuy%JR?#w+=~Fde>xSWsQ2RQFq|RE4w%IN}@htz}UsW@p49QRZ~GHZyJq=zGpK8{M1fa3RWW zG)g`a24;vWCQVP?n7!~JJdDRu3%ZpJPKwiFa&_~H8=n-vE``FgC&dqX-SJvZc}u&p zqr8nwiZ_>6GAT|D>e!A-UHnVy;=S{YOO#@st6;(>3^%B$D-=7+VuLAjP|1{>o^wZz z&bq-FABv)M>L;V7%Cx2~;)T?hhG=NMykRX#!#ebK(MINc`R=c|+L&pyQ6O(+zMR>j zo8{NaD-&-r=0Dzb=09X`{?De&KOZa#l=%+@8=C)k6-+|4kE?12T4K{}8NoXeiovIz z6)A{6XsY4~rSD?t&5|0Z`;!{xzk_r)xRUly<|CEnuHjM+Sf zI)vh$){;8elC-^VZq#Is73LeP%*N}2!D6Jt!a>>Npp?0kN&yBI+G{+xE;gzjo6K^6{+lare2#bCi!(NL44@FOWQ^BqO3E z7%}A4^&|=;&nwBvQIcjP=^!M*`h#8An0zo6Hcr2mwWqDJ*=Cw*+Y1<7*pT)+)%F0% zRTnm9YeD{?fZb8hG0MqXI0%3Iwh76RFQIUGqzcEMMe4NwX0^MpL8L~S-@g!QElw6T zB%w|vT%uh&LAnbYC-FT*e?d7q-EyRI98sn3TlVJrRNFD~D7;vx(tgMLgN2Pbn!cS) zZ+jUjv7FXk-HE|%p2z}gyVF(kG+mA>764qFktT#Kvv!6D&FiV z?6&8KYcPawuV4cq91Bx4;9zZIl^WuAXIs~b)wb2OrshsQhH9aqTDTXz?x8rX41o>j zx3$-pDl8O=w11RrTf&t~0Zyk1&oc*R7ob@2&aqq#U?yDCNENm<>388If~+QEE7W2k zsq%3p3twWHIeOZ*&*H#LgB75Q6S{7lre7sIG_(Y?1&ZRz2z`BbvYPS^Gs5H5 zOyM4=Jrvc+qHtRR7JOvK39x%AHs8VK-Mj@{c;=R7;bu!v1gcp6=EhA0a|TSJ+?+V~ z^GjD0Bo=+JQodk=vzIrM8?zF4uw|bYG&GXwSY*l!%VN`+x{229A7=*DEtbIb9>&VM zc^Z~|8(qw|ow6b9(`I)59PsLBXA zBWj$w9L!q?5HTmVN{9M8kjGH{P?q$G7ku&8CjVQU&dmzYN5Af9tAFkr1wPgCsG&_~1l68H zL))SR>R`Esg!e5mJuX#vpZrYw2Y?w;eWp}TS*oGYJBn(>~Mw6c{%2ZLW@opm_Jnt9++7 z#`Bx@bNr#m4|XyqGwv(~WpD*spKOM&(?Y5F`00&l{vJlfRM8V_&=4=2hvV^o;zd?v zpBWaH_PP`|%|rhcGJ3lMWI(F%_5j4sPwa{(|J8VHNrv)qyy8IKz8OZ?@h7Cyt1 z|9~&*!tR&1lw>I{E!=vw`s*t`65a)P)Oa7@FVhmoLP4FcX_t^Jojc@f2mv)vBukYj9%#UF`me+gHqS&ufBYwy)*D& z*Aw7r2CVT00BVPfHy$ksPr}a{?+EcUjn-X`lo2 zKmDjg8x-*$d{n}7*;_U=WH0HhJJ#WRzJm(yaoS>^2Kp{P?VY>tvJ~%&BYl_G%p0fg zG679ELR{E)sT3O4)MP+bx23BjK;epmnc1%Ln6@W$l_zY=hA@Kq%n0LqqyOSh!k;Yn zI1CFPfj2f%)Gc9Ahc{Z(Li7Rry>b=x?n0-i!3?{UisQX($vTF`)i&Bd^=EMy*k(&M z*e$MsDF1rc4i0U!m^!q&{;9c&S-ZfIs)1rI4U1{gXfe03m{qx2_&eQVx%aZ-Rh28nuX)HksG>iYmZEUO?1o~Xp7#*ao2``H1ZW|UH`5*)M#s(~p zU01ddRoZc={+_I*ycU)icVb{}v6)#*%V&d!^h>q9=@(FWx9zl9*vC6VZuax8z&q(l zEZBv)qLk(gMo9mImhzZwI+9*r(4zf)`DC^rY5%#U(*9P@(t^GX3AZC5D1J0q8CG5i zDgNA%?#yEX(yzGKWc96Pbx%n64hfOfBPOdlr3f0dr1umlQiUhh1Nr3W%fiZ@~m73`Un<EiG#S-i`FMs>cjs&WI)vVT^ChRUoN={qAC7NrGc^g#p z(I=szyJ;yyBQ!Pt&auD2d9hUxSQKj>#Ih}$Q9l8rHn1SOQ zajcSZ(z%dwW567y7JK4jm#OQFpZ=x6(fdq5 zsoph@law|yY@7@){}(s;T^X2ns!^ZoChgc0m$jm_|BGG>OE)Q}n|!Umqi!mb9>3MXocrKoGkTYsuO)`=y*hH zRQ$jCq2hl=i(Gdro0_^C`Pcsp;UufwzO5?olhW5p5U|U#8_^$55iQMtY)J*Hz~D^q z$6#Gep<@z6T4=zU$%X{P=UQSc@4!=OcQe;1>PUmCiB%vb#>!y0TlQ+g9SyMxboMLM zh+|B#UQ@IVQcMm~;OiIsNR|0gBGPs4m9KBmBf`(g$W0;5ups^iLn$)dpqb8-0Ll?} ztQGH9!dYwpa@W$$^q>zhYV?1S`MlT^^`I$8DOs=s4k51(v!E2VMe^0sXG+Ti|1wnM zQMTnZ8PTzHO_N(XqR=f$*W{0AR^J-8Sa+^(fxn&VJ^bC7hB|Yp#{L_FQoNb%ofa*Q zZBO`A!rjG5ZwlUv$!W8F=xSQAcN^ZK(Xn$0KVy-C*?P+SJTwJxTFCF6Ny1cN6G%)$ zUu+0X`RgrVn6|`uWvD(fuZ)z)?#Lo`{c&&b0){iJ8F)XpW8)?*;6+hp_O}@(y|+k| zDy+bS08{rggr?QKA|#wDx#8YiN*k){gn(i|2b4KFEOYfcssPjK?ivt!15H+4O;)DW z%?~LKQLf%SR=Ii%S=CL0HAqD@Q;lARm@67h;ggs$T?77K<6dxykBIXxN?@;rbxp=> zh+nS1+4jx+ORaJblSin=4LwzjPuNyFRdhWQpQ?#{WZr>_I#Pe*qZ}yRV{Ul40DQs= z{Lk=Fi){aYA@qM8B4qpj&nJpZ=uCJ?1siCoTj*yqYFnx<&b7qSQa8`@&&tI?p38ys zNYP(dk=Z51c}oUi-&`s%42hs-r&HP)v1<)du0~4ex>iFHYDMbM&Xoj`%8@$ed;xafEG%Bo1DGAevn^SlaMQ(-k)SbAjN$@a)%J zvti$jq<}$wdxf~bl$zK?M$+0PYWDA1M$)lLyb;u!e5Sk?EUK}L(EuVdxm_yd-DXp; zf7=s-h149(W{{w@8s{R3EwaZ{rDNa2NEhjQ;KYjdJ(P@n4@;KO_oy^+t&i;Z)>G6h zKHRz(n@A4K8GyqL2XOO=GUL2U>6(rOzT%`BW|mb{E0{a16alg zvw(9-bo!^T+J%=>BgHG3I`e7#hXr`A79gx1Ara#n8zq#Rm)qN5Ek2mRym|phyA-$a6N_l@uJT~&% z^iM_8?_=dh->zdorCDo&!2=%-;qG!s~^wY(J58W|?N&B*D!f!YC`xj?>>&C28p zE;Y!(N8}Lhz-G%XCx~9=6u<8CU=g;#-wH{~>-T08Rcp>ar;F8I3Kk5QNJ?#tUsv{~ z2&L?h!@Q&s-S%WFGkdb^6uaHn_tD=(mUtVhWQp%m_LTR#DY3D*03VgU>w8C`T`Qc8 zru7Kab$MM+{`EWmXj;&WbBeFx`-{P})8K~Z8(uv|WM+_DtY8C8Sg~FkP6sxU(#3a*jG~N znD8+-E1%6Aa_Dd46Pc`L_rY3(Y%a9f=VWtSo#@Yos@mJs1;>YpR08l8OblUzoMXJW zS@}6;!Z?+&)_YnOr@5#1F45Ac)bqoFa~S0(e%8&(-JAsGX5}62D{GL!&C1QdC<5`q z%*(!?eB4oK117n3$mHb`jI|Q8J?CIB$r^u6Re882W>t!-S77Yq-`)dDN25TRWr{nw z%t(;RuHU3)N{xMq2nc^`AV5v$o?$TahBEWKB^F!zyPK8A80wnXLJp!4lEM^wzzE7> z3#m;VeecX-Ob6_Ew-|&wd$V#GxXF@tzZs75Mc&+2@}Oo)nd=cO8(7(S~`QS)KoZ4;Wj|8(>D%oMWqY|-4s3gZq%-|z} zcB-?24VXbCcFD4S4$+YFYagRO2`_zGX^WjI+;txvICedYy#tKD*-5ExWjd;7((jSU zp_<7v(M&oAD{+?(aZ`6Tsdq4`yKCy5fDx7@LV zDl-RL*Y`H{j#JTjBhEqnqQ}o2QaAS}RUa+rBTHqQf3f>Ms7Q8H*;h>a7hwfV%1ZR@ zjJ_r=qi8MG0@OfMv>qezr$B-%UG;wSpX=&1wG#ULLThedG&|@n zlPK(x?h~8R*lv)+ucKUCOrrI3Fn2ZXtpo4%L1D7iaCdgg6jCo@3Jki$6W_e_=T zR8&=T$M4Zo1G#Tq)tRbJ%;%Q)Yzuj;NUEs}QXi|}6v$R?q^?9tUY!V4zacju#mbM= zitA{IFE03Z^)Cgj<+P}O%t_Y2%<<8roYDHFpjB}P{1KvG3eH)?1l=$;XG!L)@msxw z57>xc=I#d$q)W9B<>kR?uc-FF+aF1SGmYgj<>XGAG8L-|I$^75&zx#%h16t!xf*!6 zIg>odCNk4g`^u;MaCA7ljOY%zZ$TP6ThyL56>^@7p(=7_e|l68^rtrpbF2zImzI<+ zb(Pzt{wAg(n{&*c^i+}2uGyK_9We7+FtI5Nir0ax#|jK*V0+d50*J~wUPXS$q;$&C za#DpIH!@R9JEE+*mrXKrDEU&8tR{|oOmX@%;IuQ_8GMS_a^Bf^YQ{~UOjYt2|K{U} z7N6Q2pF-Bm+2*EMtjY8>f`!c-U=T^nG1$`ieP9iFa-}P?m6`FdfP(lq>KcWdWaK~0 z7)x5Vl>_Mr(^z@Q3ON{(Jf#%_m#G^JQs4ELaUg5%0u48-Ly_BZd}j_ctwuo{g_fJc z+3hVKj&RUqgTnTw&6m#S0|yc&7@^Er?j^7yggttIGTBEY;okY+w*=6U-Q8CxuZ{bg zqx;AQb!!$f4mVm|GkQW3G4z#OA~_#5E_iyI2m>a(bNE zQW@s(adV4x3TaFk163J(rgLEB=AiQY?wQIE8--z739OB_bZ#f8-wL%OoA4Pl z$lDoeFjwTvu3*glB+JaQU_#uaHV0t4Wu)r|`#MsP%*(XpjIqaiBW8>@JueC&f!Q4Rz#is_{2CMtm;p#vt`>P@m`&hzFyl-&biNI( zCl)Xji@l)MI>kUT{W3Z!tI2eVYX;{eox_DrKFJDJK)6bb?;(R{s9dIc8<}-ZR*1+t z2Y2W=M+s*BS9PHb+@fQ*^8Q+X8;z}5`;pqoTr1AD;(u}aJbLJF9k==*o>RkvRl{E$ z9MrneFnzmV%vD4Ok{kanm~a+fBlY>3da6yGid6^A{a?$W5_hG16<^rPp40y8It96c z`Iy6~XWM+75@8U#Q4tBO=7c34U@1|6)9UYXAOdBxE<#=%^4$y*Y1MA4|6>tIklTgI z|2GF2>8(j$hqO{tV$!$ZXs6jTRBjHWK4(*;Vv7TbTd07xic>`&8~}Dc>Llzejb*Gz><6(0CIQE5MpWd5BW=NJWapAz(j z&1S>we(-cHU@@ZG5ehaiQd*>!K-FHZ)!D`p>+m+<>e1hntAF9aG^Bw`zqu?>M-k(4#D^?41aE=QO{>eA60RgGGGHr+8p;o$;aa{XzQSg8fg_Jqf`!b(f<&sf#Sfkf`0$UKYW!U^x!dN#kl2OY7tqY``>8Wp-^ z!_mZptrV{FHKRrJSJ7bboA+Eje-O7%L08YBMrdGdcIV>$2+u#FA}}ypeHo{;aBL{C z4!5EWc&aRxV~gqop-=3)zxZDRihcS632`$~E<<%~jf$~mzaWuVp$^7UMP(hE=J}WQ zmbg&`nXsj;?ZJ2_=lo(-1+&3Vf7S)T8D;&O2@MX2__J6Lm5{O6a{8n|RGjq5Hjy6@ zdJsOuk3yzu$uIT^tpL|(P3REcyf2c>Acm5dWupB|BAs_i745MvLfJ33fR;y;K*y|L zf1BIPlipMl0Q$Vkin@zL?0?PgMi8;lC#)EE=JuZf;Ai7W@- zc15b;%7GZ(2TC`_(5YGrKL;7*nvLls9jtA~Og~iOGXhTT%BwFH3;akiMbT_#n{2L( zX0ud@TZP&1-thVpB&UxR`Ku(QwAdY$D!QF~SB<8$lVP-z9j2CPf>(kBU`jd;wVxSWq*4F+^{<$W9+)0j$42k(dF%u1@{uI%?EY7C99>y3=CoYz~ zMv8#yu8}@-k#LQ-vC6vj2r%}R$uG5-E&&4r?t%=HUOT1el;Z=|{9jheX zAEPR$WQ`Xm+g?u`zR+tba{_vl#*U|I@hkSS0bnfF>b;?$$8tSvHMogCHnwEQSWL3S z#;SjGuZfL=RBiWT)=6)FJ`x|J0RO_{=t|>2Ixaf{6dUAG5I>NmLw}Dd7iSqR7W9D( z@XYtyL%hYv4m39Y1rdF^rrE=!@$ZKRqt+$%nL*(aV;x9d4r(dN3`s==Iy&o=G{wR{@)BUTAH zjr+2JmJTU{jRU1b(kZS&7<>-Je~xx2sBM zb-&qa$XT_&y%C;>{8O2FHB2B?ioH1ZBcwC9p-9;pf_^2PUtRCdTDh|{HmBTg3*J!+ zI1d?Z0jkDl@ZLa2{tMLy*`B{!U|2aUvPjr|Oe}G@hKbv40$x5Z-+_m}GO~~CNR7+f zLdv_?P}Rg9%ddMDGx$;+*Ju{U21uOmmTcGuWj6JmS~qXy;k@zQv<6PByzAqb9^&ux zlF`%6mmfxGW*c_35sf!Da3M>Kc|YtkEsH%?hY^65eXWwe9gxS$WnjBN%fJrfT}9av zX|Ge_N=uw`;}t6gt7Q4|^{v|LHOapsNzhKhtW`Z-K#c8M+!`jYa!wvM8!zqv0$jWi zN>ljtMv-O+3K31-sHJV%#R#8Ei|RWWlaZNzv+Fr1Fz1+zi;g{NM@y~Vq{Wq+;!vx3 zb?hq&FE=alngBVCwF*)HW9ggo8WB-XMEXN8Z3UAw$cMLRkEsb=GW=IKK|1X@!hY}=3E@-FT7+d z5HF*5L*%3;J1~-rBBP5dk%e`OPd?Cf}7YD{3slV;24!u7IkE#ihC@tGGvo38sL3_PVRKl3w>qd}yz`#~`sTGqezcy<3wv zHOY}{e%+JC^eZK!9Np@*ECA=SY`jc*C$JD*HuL&Oxrkbk^*qz=MmyjAtdXMgZ3?S3 zo$tVYse9>suP3XhU{iX+`m|IIOc|(k%GebF%A3_f8N+6DZ#rT4fU^jKWLN)WJ|ed?lF! zllQ2~@91dV$lKbKO(vU|vU4)|UGq@QpAbe_4KTSCm;{5fgeuA)Zh#UdW0hp*US+3= zVMm8-*m;#S-zd`+8|^}IQ?41dsmF;8-KRNJ?_k4cJiq=jI|%QuRdS&*Fxv(F+Y!yO z)5QPEiaDbB6J0k6p$}+L1Hz*6%Kg)-#R21)+e!W`u?~1@ap|f{*}S-&PXB;v81d~I zCwM+})Al03VrZorTys2uUL5AwX0k$WSqSF=2HhY?$lg9&8}(e#{W)#wA%ONu(qqLN;Fieu(GT8L;t7nRpEN>3fSMp{}4vfVmJ3mq^9 zq~v~h5Im=L(9bfP^yY4%nbU`!A)MjpQ>>%kVF>ByaYLBlL$=$y_!jEiJ5Q)^Fd(cQ z@f>qu+~N+_kz=U9Rn0by?2Ip*Z5oMoGwok|DI_Up0Ab@S?ZZH+WHT~$}v5?w=%%?>b3TbSn+6IV1n-c&%Wn65(X&6lja34A2HJ*mmG*9#A$ zv>pbm@eTt-r5%bV|J8VV0ix2@@TXPU8Ys=np|s1PVWYHMBvjhTAc070idAVKEB54f zDB|fKHK;V&mQh-c0`R?6aBGwXe4wea4 z@R!CD+D?sx0~+C<8Xf4!__dZbl<;;T>##FCOsqKAfHhtrAjXO>RG<5kH0AxgLSjV^ z{z`kj@#sKq(q2!1S`obf@uoD=@E;Toh&)UcyYX%ZD>YsPeo&%2@#H_?w_PT+aJ%xj zppE<*tH18%BjF8{v zI6|q<)U;RdSK3=n&l4$L07NO?)_QqIjNE%jH93%4N_+ppqYH&edzS%Jg$x5k%RFtp z6f=aR2%gM8;XQ=hYP@3nAX70sjX<0KEYydSEaknqt%%n{e;vn1!W$-Od*KJu5@!Tz zzE;w*0YTcynwISce)Z-f;T^$<!SRsmMn)w-_~u^LczEypvED_FA)~_b`k< zSP?#|V|XW9c=6f<@|)|@*pWcwx-@nW6G-f1R7VK(jyG?di`Uv-l5z3co6eka>JCnJN%vBaW0G2^Dr0qMZ)rJVRm70O96tQ5JGB`(eE5}pC6hrI zfSJRGXD|oZJ{4Uyxb^CwlhG)vwVE$kea9A&lT{2tFx`OjWx$%=;R8+r))n#?Z(n5{ z>BFap%;H;=(PdXTjP}RqxGGzpH-=2!Y75B8D!5&Nt%H>NzVIka`A= zypr`LN_~%^{*RCJ4R(t`kKP{X+YiNL$$R#2NBIrNdzxlQ3u$We&Z+!C*xv#PW>OQB z7;hdKO)3jOb~Um;76+lJinx{_qub;~kCjgL1rcqF|g=$-`OZAg1J zyh%0Wjw|04iYh7Kdj|(v%^~3X>!d1zxMB{4-|r+c1HgoRWgXTW3g5&Wa>SLru5jw1 z0e3wjWb=KSeNKznN7=OVH;-9jDK|XA>K+Qe1WB^gKj~i%Q;q2FSuKG2>tV~v$=IEk zU#AJ1lc`%-;?mLhuyxkS)Ol*eGsSLUH~bPArskQ!YW0CP4>Gl-zR!t-TqjeniFiZW zzXrBuGBWqk-4@9R%>YiOo(5*KRC4R(j!L>AlKFpkA>@@v0atYH-f4woXeDv?PI3VL z$JPWE`zTr+do?L`1!o7ze#$2`eT-!}r>47byAb9!gNRUqto>X0R-*u>1fmVb^kIQy zN$*~C8Un6%Y~%V-1I9Tk+?Q$gMK{nkPvEU*w8T_l8|hW0{We&Q@xR0SEOl?=GPR@2 zRN1yJQ!q|w^ylKl5~v!rm+NxV<3bKwDA)jptNsX^`&Yn@FwNr3KI<&-w44w-JHu` zqofopAci}#KnQiQ5lj^wwgRALWrZy%XT-V|yHFzRzoV^KCD_DX*@@EkDN~-OhB-|k zn&KoBRc29*oI>Jy1X7IG6sOx1I-`ucuVf1c2+WfB=i$ENDH2|QN^08eZCYCzrkaEM zuG~dFnQHD9+~@-r2*?_kXxaxGSh)7~ksk6{iTH1RO0 zgOT!mV6#5IbI3cwV%o>PS9PMqfU3F*2qDes?JCU)hWH;(OSi@)9x#=1Xk`jDn$mol z+Cr^X;Ad9cdQW>#_5!2T9z1gdIvRC1apX{k@9kj4Vi_-A3v4jD>ulpifen_f>c%D* zZ7D)!^ipQH+6v?VoNNypIwZ3d?i{qj|Cii=hjsSfomO}HR{v=O_kOr}a&Nm$6P1EG z$rwSkz_u1U<}BALTWfY7vo*uDCjJ>9$<=nB) z3z$T|9PP^?A&cYee&?JlUW&8tHL>~P+2$@ z7JI2=^h)*UL!k>Y3q3~iT!5SwL^eRrD;YA6JE-Sr{s-zPG{O#Q9qcRw(ZcMc zTKqf|uh2_nvHAW}a;a%-erDsc^n9nP2LFQ&(*5BM&6VLU&>Z&jCXb)nh8ER#iH4#V zO(OZ6MYI+ULD<39DInDx5xpQjmUf41l({v+v^C(j%9=U z<-Lt>$RKA8Y<7+Ll9ddGWv1;bAZ?PQcaU4k#ZDu1(!Fw?-G&xZgf#sL{P(B)Tt5F zAQ#o$LCMVKn!g)QT^MfJeA(<$rXOaT&;7V{ETg#@ZxSFNNOR+Zgds1}Y_=H`=-N!T zX7@1rhkP8=C?9Q5h^=^XdZmV*jdmL{UYU*3_2!jEGPEU;G0QRu(Q`^h&&eCPH3ka&vgD)3{ez0`|WD_B3t+V!^$h&2+C>X9iW6^iHpL zLfKwT+)#F;i`qJZ>gJ+;#R@52$FKaMwSr+@RMlbUv>3=eM`3YCJLB_1nONRl0vH!E~$QQcBnKjha>~_xG z9cihT#s!{eMlXK`yN>ZOzUb)X2TTYzI(m88q(x_-Z1@RoGRe^ynRRNQ zWW@jPo!GF~2G^B`xu~rpsKGAk*Y$3y0WNA;1l89?&5xk=cTtZ-P#s;=xCm-X7j=0A z^~ZOPTqj3R-?*qlfI=Ml*nPK4g#1GnwM7KA$VIKf1w*D8D1g2Y&%wKPyKV*35b<2P8wo~xg92f;2GHbos5|lcGG+H zt?-8af;Z_+@1v>pw?*-M6B#x2IWY*i=umR}mp!8WQyHaz=i9P#4p;hqssdK&NcLb$ z%ut{3T7HdHvk4%{V&Y}&p)t1@CB2`%a*XYOKf-D&eXRuiMFN%X!#X46Z&91ZD&Ub% zls_JXgfNpF;#qpoMt6onx46G%6Pm29xJ!Vl$Br1LSZ5@{zGrYLBiEjaSz$2#q4Oj~ zCF)8h6xpsLWl31p6pPX|J8y_ZCA|wi!id=%KRBJRyK5dnP6qQc$q?5>knLVUTG)ad zKaguTLC{;givK4Kj^DW zyc%%Xa+FZUDs@dq7elr>SXE{An+@1j>+l~h`)uEdOoYom=EzD*lMnc7ZY09OkGJuI z483A<5cX+LBfPs3)qV{+B6Tw`uToyeja4!w8&g^SG`ZqZl1Gt~y64u&clHZS^3XK> z`*>I^Yv>rIPlsIG#FC-cj-Vb+804)bCU}GJlmFX8B2$iYT;zi<92eR8*U&|h zUbTz*^%ocQw2N96LEY=3=0{KyUDP8$f%#JR-Gm5viHo|%L5cTfF3+HS=Z$3p;Mo0g zIQ9)VK2)q%Qu(bKbL=;8YZ>O?!|f$If3ALOKD79RliGah>u&QZM^~i7cZ|MHSLA%w z=aw2Y&I_~R&I`CGx+EqFmmubrsU>o<3Q-v6o*|reWH!AIS&S2>{mHV?8gngJ`K;er zET)B)@K=F~>W}#hLU2Y0B>n)37%V|XC-H794veG0X~_dy2fDo96hNscLUcdl=nqA8 z!FXi8a&?JGWW91m@v!R1jjiOf8BQydnuBCxaKpSX%<0FwnW^;nql>4DjtMwVdLuq_ z%=*yPq3t#()F7=4woK`*8#9m@diz=+6qjO<=uVU99}D&D_Kr}m=uK;gA5o!J9qkA; zeYqpl1^*6(N_vxA)G-m%tuCr-1a*arY8^qH?V|qp$t|ncMJ)#kZrsa#myVG4a8XY= zsQ<=|*Q%CTOFOr0td^SaW;xf5&oT|R`AESxTZO63HQ%-7RZZ*}xME~PaWV$hWwB>4 zntw*4^w~0%YWx)+i|m)>uXf{9ecOoN+Sj;Dkkgith0te8hCIU?-NIsap=CuYFG(WF}t1o}%u3|51TGHRuVas$!|*`7J^%WxMx-wc+Z= zB4zS+n*g^L+$amn3;pjw29XwmANCi6&0?)WUXtFCpE$NXc2#I^4YK)^>Vs^yv!&*Y zLVw~SGrLIhin94#b0e3-=HU&nIrPXtWmpHr#o56u~X@ZZV7r-neFZ*n58{|pk7i%&DWxnQM(9Lj{HpD@|7Rl8io3uD<2lTN-`qMkE}F&gfgN3NwG48KMS&(6^k^SNfJG3RQ!B%S!?#T6Pr5Xy0h%%AZl68cx4dQrs=fWO4fZYXGa5(VvKTpqeao|+kiO+z zN5FPj@uqJ%ET|!`v*Ch$%Ql43w|o)KfrB~u_{o3lTNdDwHn{b~%qFWxAID#A!ayPL zmuLOZUy|NX7d0t@;&>%Ub$tYNq>DN?g6ig?j*OsocTrt{f@s^h@7hGj+qtNkc~0NH0yn$aKvYh@OL^y*1W;sWZcdsDl3cO!+ungv5fHQEiy!i^ zWurW-%$ukF22z%_{R8le#80(_LY%w3+fYJ@(ek{%rqu z=9la@1p-w9fmL&yS3bF!$d@<*x{1!4G=qG2Hzj)ca&{f>qvcVa-EGl1?QHW*dCrCR z`se*;TwUEO!qu&-*uO8iDUPe?41I*Ip}-xULM?wtKG zl;rHq8mpOJi!*9w5}HTWj2VpW=%hA-vF7HL?5j;y43a8t5wPLLlm4Y zv&iC^S;FZwn89#iS<1*Z;N!JwXuucc zH&*R2T)S4Bij@aPJRU$>!y4LNW5^y(>Oy_Bt$mfEWabJWy|yX|?%Tc`h_f>18tcIjdq~V?hytfF1F@6YA{S|h;rVP*O;7xaN`@~ z4CfkNG;@S$6{@#QEgLjWrv?@)W;cUrxbuOi)65w{AA!v-kO^;bw@WRGjRxl>n(f<| zs03!2e;OIF!FhMG83gC(0QLCpfj*Mn*6%p&_M#PGyP5QMby3qIsO~N*8A0vqqJ}%D zoDK0)%02bo)CfYA!Mig8{&%?fETbX*0IQ0W+&07qyq3EmKKsx>-Ude|XKUq1{hMra z^Dzi~5M$cjlN@5x_VzNbgxg5l`+Ly#I&=H`pqg5-ijO=L)t%sOC)Bv?c6tll7~JpP zfv<4Kdwpk#DW*~W=r5IC>C=8anqz-Ia0efVHdbe2zpKUqb2nAE{#S|hKa>{zFrKde zEz5VYj7>&z73)@R&#}d4Q!ma zmZLMySfvW*y{bZOI1zi#-p2bZ1+P2UD!90zV2j2koa#J5KTVSzZOg1SQr;i9f{wKhKRX;WvLS32ntnHf10u_V&{8^uCQOM0`-%8haFNi(D~ojaUh`%VnE zseUu{Ov4eIYU}YeA5N<9HP1EaBxrDI1eCH8guVGS{uK!6^}D)lq-P5@hcjRd-QX|& z*$_2L<7Ef^FKK*OXb@x8HKy?)3svK$IXlB4HqF^l_7$45pj&#kG>dXKu(W8!$1uElv+Ok|@i814nH=+-X_6TCju z3_92hbcNF24{GwSLh)HbK6pSNB<|-*JB?Kv+yfA<=`M9qYd>*OZ@Q>29Mu2JS(mE} zRO_*88dK}u^CQmM(IGa@x}|xQ!&%p2tk5RmM-$qiQTMowZ<$d=8Rc ziK84C4BbfQ{ZuLMx0Kd-#l6ETdaLv6y(*9*P~aN^D+7;a3v4}lgbYljdHlSNT&Df= zt?V|D{sLfo| z_aC{b)}T!fGJG?F`q4$rjG#VsQ56wX+C|+ELA~Ij&WoTPc2P$~P?KHMUO*M&-dOit z+X(qM7q#v~SJn|O>PrXJU}yN5U&Zj~XICoNKsTg+RoE$)5jpN3auQ6_KHfg|73~qL zCzGlIR}oyp-`&ufVtY0J0K?g)7tZu z!SOb|9WZInv)^ZbZSp@u>MY~jGUPGob$rFKrCUA-Ro|d~=V*D20g21 zhx#?Mk6-*8%6gBo|E7Iq^jjhN-4%@I(r>@L1N|nwOBXr3zxsa2d(s==qGm);eO*+_ zK{Y7xtWe?|Ew?!(-uR+KwP2+d+c7vy@K>wUcI+&dAiJOH%v^r<{-Ve5Eta19Dj(D+MKLE#k}%AA*r2udV(3*NSd*hUAw!Z{$cYS^&kC? ztNu|gYM%&dh>L0!L7n2Fet+A|@NgIPX#~~PMJ%J?GkZ2i?dp;lXi0|;I?^A1-$-N zs4wwdOuaUKg^j~uaibjW1r9SpV|$YTJ}O5pM}j1mzdp2U!0$g}YDlQIS!g_9l?|wE z(z9aTK}%x}UfgUFtsoy2QPj< zPCF!*Db-r(Q-gCv-vxLj9qh?lo7@PwU`ad1G-sG9;dP{L5zds%C zvs~}P&W@I^bD26W!qnh@a5;j*;oCnnX64U3mF0T(n*`SN)`29K>+Kfu`_Ei&rDn{g zrhB7IZHE%!6Pd2JnMw5DxL*2uqXm3F6B~y7Ke#D4>Uxh_lBnyQR~&J@n*%Dx^>%0x zalPYE0$uM43|ynG*T<$#6~(>*8&?`O{E8=YyWYZ`9HY2wj$^5XY1fF)a8cVwP)E6_ zO*L++16Wv7hg^PLyD7fCH*$!*>M#wj~s9PLVgV%}WVSaFhnHxNj34-1K zRc>AfMe^#88FWlPr-1qtQ9q~Tv zd*44HxRk{HrrPCyY)}E}^xY`o4b%*S;k5;RGO3_DH zwA?dyn)U%Ou5MfT2)v+2?Z_ka2D)7WOyO8!kW)8Y9w&P)@VV?(43)gHPD+v0x!#v= z+EjNRO2J2Vc!%A|K@M%lJgj;5&dU2TVvzTT@I9ON3%I2yBk!xvMtJROGVT#(y#9G4 zPZfUqkmiDo=H|oA+_vj7w{Rdbz_zyzP!?+~a`Y*-o#T&<1ec}oe-m8Rebtyx9{E_L zN(N~<>BCXjUCo@+Gg>9j*t{~Uq@__?c9r~EsRBQMHBzg#(X8mG)Tdt6V3qt#m7%(` ztK?&&%p;tjtW1)N@iLCqJkhmf0%fMq6f6-Xgv_=VR zQcV!sN7Dr!^Yal}H7{z_++_2TsyR25ZutYMy5qA{w}=?{-mrs0)g8|YXXU$FGf1== zmBaO$x@W@*Iy)@oMJf*!RGd}HeMYUGQ_6R;dpSobC!V9F@DuPN$dO?Yr#yoqQiUz< zQ?C1B*-gEuQ%;6=A7HXVh%09u7iPL0HHNx5 zG%M3h_o^<3I+-5pWV-NN7<*5XBfiat8bfQJYWwk1QWYilX!1FK+N!`=KW$>SK26=^ z<%e=;-rvS*-k(^F0b$e4vCh@d@pH^|M!Y3Q(Idk?l+aOASu??X0I_%01>vm?s) zTjGU(VHN(&W}a8A!k_J#@vhAH4rJ^vxBzO$w`?}mX0>+%LYD5L5*y^fA50E#`M6=k z@-PMWPBf7B+SXQFcva`b=nd#-f$st6PmzDG$CI1oC1V>BUKap(hmM%HFF^l#mXY+X zzp+VPuv@Wum%O}ewUjKULB-ds7T%UElV8n##;G{ zijsKvf9yt$7M+W*oF z*_V?4me1k+WTknO@OXtU#AK*{v%*&szFOf&2w$x5>x9oz_(Q@cD*O%Mz6yVjB@F)% zg?}f!x57>50NzRAZ3%Cq@GgWmSNH(J>o^ycD(XY{CxuTU{I$YY6JDzDt%R2-+!3pE z{sM*fA^g0;hY_Bp@UevNRd^ub2?}3A_%?;FC48;Ial)4=JcIDL3eO>Yio!L7k5+gk z)+zkM6kbbsKZToNvw^>h!tDvSQ+N-;%@r;rypa=NsUm(F&i_T>Qwe{s@CAfFRro5x z?kh5HizTH%ukFI9Lj;Ux-RNqB+6cM^VH;RPQ8o~H2Igzr`OGr|)Tt|xq(!abJ* zzE#X@Job0Rd_MscNFgU72sDCev9yIg_je4LgAH!A2jecfbUfJUfdey zk5%|_!Z#>fMfeJZ(}XWjcq!r275OR)h~xn75kxdn>#j z;hhvNBD{^lg9&f0a3$e&JBsW(;(A{HCxyEb{#xOlgqJFO9N{GjpGkOu!j}?$p0GB# zW>3$z@4Ibr=dZObMyvj+^u6MI1Fa8u9P?-|zlbTEg!ecxrSpZBhX$1ft#$srpw-;b zFGWYp8yn{Ce>y6_VyHmur6Cq=erGiPZ1c}#F6Jk0Dd(#>TcY48Xs^4FCaDkXMTvJj z6WnWwoR3DHAJ)3^tbjwXZ((R&Qkt0#4er0V1L9zp70*^aNZ2_881F*yQ6L|QcsD+@ zIPPUYaYNj!;w-MXr;rt-X;#;}ycquvX7OL_;Ia(V1>4=vX{cBz@+o708BZ*FK? z0bW-wJmUL;o9eeHPQS7V|G=V@t<#u?ZHZb=dhZEgg&=|t($=6dS5HCb?W(Y{Fc>F$ zpSZGkZ-NNJnRBL-=d`~OW@tn=SAAUeZXk`>*r7aRe4{o#>T{%w9F5pxIi~^*LKCT; z%;g`g!ki&SbBbZ=XXC_-`suFqBM())e7m-08qD>w0vy)7cq4l3b%XiWus=?vJ@uV{ zeg9+XBH^vL8@VUie#M6}RHn3hhO|)M8^HUA-52Nsd;X}tdqb}MpGF4Q@rwQS?Vvc5 zH#QtI*@a=Un)r#(6%5cjnk=LWpM@Ay{#UnR?NX2hv*jTJ%Yyj(?U03Xie19Yt!a$R5T1m)5d$cz7^Pm z^&TxM`i19sNtXBcAB*=WLgkEj&--r%IYZvJZdX0AeyRz(_0wPLM;@wp@t(4&@Sd() z<@6q$`l7T?hwLL(=luflU|SDP!qfaTp?kw-CUiF=O)@SIhVFw~2F6#Wd7kOysS=f| zYG*77BwBEx#NDD7p;*6~KA4F2LCc1daK(J2<1>FWAwOjhY5%l)gpL?+pbuD!*Lf23 zp(Q<{EG%_&r-+9XG2cNXy&vvE!cu%9b<8|?k;!7JQr_iIrizNl=Tx|_A-Y5p zV}>_Az_%5Wl|ib_Umyr2M<&0N1Pq%C4(5kdP~&}kr`&NMBX@I!)&n8l|9N>V4u!-2ycS^mV{liU97^%BN4CVFRd)oDD%u7G$!*4NhuCtg=7V( zNwg>>HkWLD6DjwBgqF*&atGmzutMJXeRwv?R~cG{l_7>UBdiFys`#;d2`jyXutE@# zuyP-oc>l9I16lg2r5xq5*Yb}tvgBO}T8$cdsC69rwMHrvUfxazDzM>2i_8cwWm(}R zfjzLW97U+|R9~iXz9!5WocxOnnR+^3n9GAPO2dNL6|EGCL?!H1qJ`W`)!GNCS+FKG(mm*NAhrbrWNx`a7{ z%jYY>W%tp6C74jNnM0J0uRxVha%ALNFxhrbDc|cN<;$7v*7s9dp+V3QQVBYQ=o6*Y zEl|%bm=n~RnkzwvnZtDCkt%!y{1CZ0qi?c8Qz}wD;>Rf=Ip{{;grA5GmaTMrjRli! zW74(oj^;zJS-et%KWV_BkP3(LLO9$C9BySetba*3Wai*d9;w0-!{J53NQI_Uq;hz? zawz2BaQThGAtx+NSez!?xFpilc-81J;B=%L7RQEo|I=YXY7-W_ycU=mlC8-Z7MnUW zxc_bp$2DK%TlVs z8xC4GEDpz%hN%w|7H7W_sKACrEixl4mS=^dg3Jr_9EebSeRQkMz>Zy+4s*&duF+N1p#IMu1*LJ?N<=z55 z)Q`tbV5B3yWWG=p#9M@&Ds%l(?=UY!I{$uX>cnt5!8qe~AyJKY5K72!#Uv)&l6+xV&2bqb3>l_-i|F?#fJcO|8j2*O+<&lVY<}cNbamKE0R`EO(2WKQ% zE@wPE z?qVzw<%}~{y0Wx9MN)h9=nc5e_zgpZs=zqoUJC<_TW8cFGn{csmNR}QJ4)k9X)CwXWEh%?3~!^HO5w4sqc$`c{`aK_V+u`c)WKGBN@ zi!(m-XO=UL2vdP>55pRraeKmW#+S!ZQQj8#89JlC1BUH&hj@8;erpckle%;T@h8C!u^{dmy?${#9i`ec&c45`W@oEgc_a8{c%i#DYn0M|Px= zePmXyc7e31#G(ZjB58r;g|xs;&;lcG5o_r4Ld|T!y*yHd6YgO}rTwqV`L;q+DpD=- z4MAA)TMYRu=dDOIVJswiXj~u>?iHg%TSO!(vtp#<dcP1Pfk$g?D9mR-0Co-}i(EK89}KHDKjnMYrP{M7n!)f}a6J>RwdVaxz@Q(I&FA<7zxH~!UPW#Yvqe11AF zNLJ(hhW0oke9jH={?-=;g_`hr&dWh+aMLVj`0Vb`VE*%_P@{Vg4mFyHPf;z&BN4CU zFO|`R&mZT8#bHuG!zalKQt({s1!jns7jh0in|q^{%kcRO;f(MpnVIl8!O${%-ezbs z!l#gH&Afu!d_j1o5LO5x5>+5`1Ep6YF?n2A0Bops-{?b9-BcBEd zO#t;@3Y1_2s1}+LKr6EX=%cu|09K_4RbM24P6%^`KxH?m#yE#`6JfWeW@t^xqo>rA z1kj`IKmhgEB7ovEP?&)|J$sNhG^|4e&~1@%7(jPFTLS30zhni_gTho`=ez5bHO_E; zLKv2QgG{%0HTcPY{s-Zp8u#GRL;f9#hmyBMx2ISBNPII~+yU3plIG2j2GRJAvzp|= zu6PfhA;XotBzx2$OBKE|3M{0G+9OAQd~-NlI^GQp4A}sKdD7jIH~4h{y9{7=$o&i& z-Z%VNI$y|TRfQ1oPnJl%cMtQamXvY6L1E)DOXhdhOr~ijLMxdF4KleHnLyN?fG_jP zNTtH!_H|0}MO9!)va_m?{h3XLXUN}U@z<}r5{2>3CQa8~(@k^ICB1=^9$iaT#tDQ` z@Q=5Ud#?w6sut`FAasWP^1y{~{H4X2 z==ko74!3a@ht4I*a?PuM9Vz$zYgIHx$8y3M(NQup(Q&w;WpupA&}KwOA=l{m=uZ+I zdkSHNAR^JR3F^jQE?N{n+@$q*WQ4uXZw5=L!aopQsPy%6HMEeMnE2rff#^0SYEc<6 zaYj~5EU%WBs0dY>D%Er)JZ5_aq7Qmr3(sNy9icEggO|KV?liP@`C zW?p@OQ*){K3Nx>M^k~$e`Dy0WA6>OUmqdM?SCxCgi(a&PvcFt_C()!17LLdik* z@X&x^)7!c{)P0S&5siIDZ|j5*?^m7~q&D&7r{{v2gC08Mj3-MRnxyyI)ezpyNK_Ga z?RB;CFAwa}<1ei&6Hjh&vy$QvPe_()uzKh7A#Z30Ou39FeYD)j%n^CFGe?~bE#pZW zLz@v#gj`j8+DeHh^;c1t3PD8T$t&nk_{ng}&J0$6b!BP!n56b5gI2?Z_O%XqFHnJr zD>uyyq_S~Ei_C~CGqd7K_jwXm6rsvf<(jy%OPDj{?ns7AJ+%_%oSCCM?Q6+HD?sAP zvT^WlZ((uayo{fwk+AimY`4{4c^V?5$-M#ybl? z`7bhaWWvvFP9`e&JsN)AdnQmRg4>!ffsmw7|_$XpM^LY|Hk3m?*UUnNA&FuYUPO?DE)*E}MDi)kRbJW_>E8@8@uA351}nxs^udeuNdm^W$PzbN3y1g=?O zt<`ujq|XRkcZGQWhtq=8CU6x#?JBWz&cM~$p-Flzu28FEL%Zo;fZf2=S%s4ab_elS zByiO{6&8nmO4<=3S+2nKNu=DnSQf0Efoq(W8wp&Jndt~!VrW?zXB*m#z$N6W;`_dn zz;&PyRtO>zxVA$N$$$UaKo%3Y4s+RSx$1JN!ut_j4>xd)ek(|30@tfg1}dy|kZxD=tviv+HrVb0*>LNa6m*BQc`6S%r*J;|e|)RP3RPNgvVSL+)KTt|g9 zh`{yJFgI}J^^?HWV{lc7T;v-NsY~zF) zb7zuBmg)AsnFAQ&OXdqzuF3QKFfR-j>#?WFZ+(7XCdREl3x%!muDB-4t>=Y!ztzb> zYU9@DhVnsGU2?khVGd2wJNUBDT)Gf;-MYJ)i#)KuBg?J-JtI&jLK}-ie}rVY+`37~ zIqHAjP%W2kJ(F<8_(n1_Ze4C@>DFTmZH8M5xw`dhUx{0vErb<pR1oA@}W< zhV@iJ*sZ5xttWYC1&CW0-vSd`^+RKB{jabF;nwYuaX9z0*RkT(7p%x~>qEm-V6E;F zWo<{mpA&{#-^^)>w6_F5#jS(sn)}qJ2g!~)-5R1&8=wBnkvihjuVx`kR@*pPrQ#)S z_vJj+vcA*q%j7Z8$f+DPMe@ER%)2H&(s(&@f>oD=UOuaYUQT?;e4)za1@9*6&NNv1))MJ6oL5^-Yz5FzX2J62Uht|`Vuxma2 z)q3QSh!5m1)ea51IOT2WW+lbJ%So2Y%R7dg!yJ|l(Q@hKO9*FpIeE9Ej{lFZD-X14 z`u>VT$s=?tV^N5tzPMy~NrpnmP%=biN=O+K@l;pux$aNC+$7%yAt4H-!mA9umpK^< zMTm~E$Pkr&pU+zBoW0MzSN+l6=d88YUVE**KYQ)rp7*5ja+HrZ-hxps>^jOnUoNBk z24Tz#Au-CYLbwv9q!-GzgpKl{ep|}gO6i~@c>Pg6|Ml1eHp;htG`4s&%C*XZQNFNf zl)uVFZZ@L?HJ%#RRvl9xi6x`mlP__88q2)zr{3x(Nz?%{%KP4eF19u1-haQwGt#M# zQT`tYcBA|_T=l_#y>(g9C_lrMf@)iuY8>T%UJTVxXpCH?2v*|JMtQg%6RMt%{lcl$ zosTuq6(ILV$ZK89&d0uWRtTGsQ4UC^BYX+ZxH@)*osZ3N%#gx+`6h5ml*PRsiG1{Y zZ2BW`K~;uSEHQ*h_8H^_I^^*A{`uHR4+uBKLVHPa=>fOVOSR#vyic6XYe^+)cYHz! z5&K2PexLVNTJ$Z#obdLm;uhKY*s4jc6X51#)C}B6v~2jS#wmP88|1Z|kI4)Fd~Afp zSPat#Gw*tkJ0H7rw6u{Gp^cK{(w`31HhzA+v{5B1QQNpc2od{d#y(BnrnZy1eD={E zJ50B4&$!igKK2W)b8)eVXbZ}|#^3{d!~dXeydM7w-h^km+d77wFS=qiL09k;EPTc~ zdiZnaxKhK;qbIsbYcuUjVrNs2CmZb42Z~uhy*2aBqE(ssYC=h>G7I^Xz|MCjxazV} z9k7#ZrOwAzB>Oac#Lm9PTY#Oyu8y3v6ei9FM+jqH2np=$ zhTtds#BG(?`Pi|(dZ`u?A(j1j5j6|;;FIpz44W4l!O(Nz_}B;$dTONw=((r}Jx4rA zO*2YR^Ci%8fRhX*`cW72)CHoKxAa-0o{~geAn4ihMxf`y&w-x*dOmiN^C8f4?S(#i z2Cxk}ZHFaA=()2i1=SXy6jkGq%pB%X=)B8ls9-W4`7eClZFSiB*n6&dsJRb%$LYcu^Yy9kcr_X85d$s4FE@dDI6;SLVW z;5ZV8R|~uB)G){9WOdVIpp(;F=zQ!>uUs~0&(@nW4rCq`zatwg!7g}I zD`adZALnE3ewAJry{H#1nNZ?u4=VMIleC)to;)!&S~_Y8e*ba4R>AouWZp-|fqMQT3I19GpR_uQET{RRGOP~h zNVZbvW3PI;;r!d`B{~k&dK241BFxs0-Hey$c)alzprf!WI*yQw*THVWm={6<9s9yJ z;Y)5mZj)NRMMAxDb>JW9^ECQrv*V^4CeFa-t64F^M6Id-6PFfY;=sqGH!4Amrbe}h z@;oOQ?LGTE=btXj`~Im=|45>S7fft82qSgIC&0viJs(SZp37zCuSN6F%zgVJwMRQ2 z8}YuKk1Zq*0%0aRAN#)@;=T)L;t)9r$6-;&%4H`0rZN|Ei^LvHX1hq{6!K&Lp78Wz;?gz%$2ol=woAzYc(cco4X*3y9DF(R zzJuHAq$deBa9oRQ03PIvlIpmD1GZ92yJJ27WP|nRXuTYOtC=qdPpEqop1fzg9DuJG zZ@~Z*b{&9Q;`_&794m}@AtVOicW`rfq}w^OrQIaIEoEm*>7W|CI=(bG`R0Y#;x-Ul zkB)mtG!V7Qf`PcAXdte7SO%gJ)Od-3_&%=eTHix+-s<7}G>>`TPhHeclBfe@Adb8m zU99!Tn_JpVb?Rf@>;b`UY1a>35~K3rcZ-6P8(k@=*2+|4aI!n|C{*2xh6?KAk^jOL zS7Tkr$uZmoISjg`UmyYFF?bAh5x^=&Hwq7*kd-r<~XLB;4MT?1q*-&9X=fN zcs$u)$L`K^0rS4+nyTj{!TsW*5%#uRaj`{?REH5pwo)VPi$uK-o~`wAgyono7-7^s z8evx(FGpA(<1H9r!mcCiVSJ?+jO~RnFNDMhy9bUAmr4)tH%~hH>XrR^7F7x^2CqND zhCd%$+(y_N_(hM6gJ^_ll?5X#EE-{%@iM}cpvFs#uxp%TXmSM=vhcr`DCdR$t<+DF zr~_n#?b9D!?2W~n8(}9q4`PJRarjtEGXbC&N$Q2Px)Ur{rsIWIZ zjoY0%F;3Bm(K`g`;)K*>u7j80_q=dJT`44oAjjiUSf$V_s)ei6_UHz$3AysEuZ8)y z>k{`E8{tbFQ%&$4qLzXYKFiU=2JPe0Hp0(!O@ozsmK@<}j|bn0Kg0E$7R>u2yqoqM zNw7UyG{V2XC$=Qq$LcV`$yREFZ%ouXtDDx#5&jhO1tVO9SrD5wUXJkFjkjQg3%ic+ zc?)HP_YlUs5E3JN=D8T*f4KWBHius5t5?=gN(WWo^+))_(_)L;2w(Mou?eCPu2mL{ z@HIsv{E@LT!j+)LON{WGlZZ5Kj%S= z@SPyo&7m#Z$q4UNTQtHCcBP=&%F|ReZjUZy9wYqb9yC<20FV6VZdBThj5S{4wAI+U z;y6Dh7uyq|?{o~Y_(sOv^qIb66-Nn(2uPdIhdiO#Jj)`fI#_RIUlmMMzYDNBcQ?c*`LWrUo=TdL!$+ zS)1uy5|1A9c(TESuG*QnsJ@SRzn>3gt8n_vq$*RvM62V_L+3fq7bVpJkH}UEkFJ(` zDA6A5epWB>Xg94lv8bl*QN*$S6xESN1qX3VDT}|9pzdBqajCmm>@FWeK6rgR+VQElnBmbmcf}Tu@JOpHz@v3Vc(gNj#_^*}A*k`xxVE_7 zSQSe~yZ=1d`RNDdeLq#IpCnNS2p&D(2czPgH#dhzOP%@{3&SAT;nDrA1&`i&wFr+U zxl&N=Y*Vcn@?Dt60QgSs3j}FA@}J(d2v5RFN>6N#;hM@t_Lu4siz{}rTQ9Q{n?qe~ z;nif6E&Cf0c7+qpiv3|HHrqI6NLleiEHI3++2AON*!0BapDG#jq+-wulim#F3mNqO ziOmB;g_~lby(IX1qnGHQZ+HRU=0%hdwMYJWzR&)&v482kl@?7!cox2MCU~_U*ojTr z>j(I)bkq#|df2kzCZY{4aZ)S%E}F09#718DCpITrY{$82kRlzL9fAHnms+9iM%r5bRBDczp-(X{+tT<`~>rL7VKv zriud4t=?Zo-FRX%5^utZ?pBJyXFXS}CfF4`1@IY;jKzjW9~4(=@OjsrF%{bMD+!;M zc|6#^?hM6jA#gVH&Z1SBEp%8)f~FL~=Wf0zsSfZ-wo)fH2RY5*z3@b>m+-lad306( zTR7A`g3p(Ym+(2=cnjcD*wvA1_!B?GKf;(7LIOS;BT@_Z>lT~NPHgV++miZDBBrt< zMM&^9pLA1X(PMEz1JK5q*ya&{YNZ7Ly2a-xnfRvE;!)HzqXacy0zh9L5lcpcUN}K5 z#=id~^S-AVs;4AT7YKk}djSBn@d5zozn<7U;M50z9t*(^K+oD&0QBzaA^`2nsI2|Pu=HsvnvuS#e+up~Nm z@py2aXH{^_9mc#r<|_3>KoV?~^GU0-;BsSyr)`GS0UgO!>cpmDqG@j)t4$+1&Sky; z9YvTecE=kp(eb~=TY!$juIRXUj-cZe!k8CA0v(&fH(`D4>~yf5*bGXTWj%wWTzW=d zb>RLJ;-(uW)^$yfFj1>2z{Gk*m^e2pn5YCbni|!D$|<*dMhzx)R6Ef>qnY>pGe-R* zi5gxo@xpU3QtQqKCjRS*&2Xna){hmxqj~6C*K_a19_@5x@hm&BsU#0Bz%k+J%JGfj zzH4jEw!X~t8z<3%3r=MqP#$y{S2YzSp# zQ@2P`et{(EUQF~;J-=ZUS1H^T)l#O^r+S7u$xwAwZ|If7P_<5OE7YDI6N0<%<8XCd zl+C7=b)h!Fawhr#jg}GxXUM~VkE{~3$_oivx5r9#dkkS3{;O+=%_5uu9Sh~sOP~*l z=QD)L=Cz~}wWoIyg1xB?7ayvs{}5|fFUn?9C##01I1PvO20LON@IXUh6%FM@k%k>r z!!5{-37ttUoi+{UFxJTCwWJbN!-+z$H~7ORrXfb11%vB{#+|(;7>ekuAQ;qpMS4Eg zwk2vwz}?VW9^o-Ar)_Mtd?=fEhcho%!2B#S>;qDRREr2^=R}+6dL*Q0y3b$_|y%K^nY@W*Q1EcqlGru-D+0*y0iFX_W=A*RTloYBA@c=O{sqmw>%z zhs2W6?x&7&ewx6%@2BSKCrQ)+0(+OAiSgUurOmCS?{?}VN;wRIT`1UPcL@dWc)lnU z?BPm5wR)ynGvqfM3Dqz}$91Big75Iie~I5M7&`9ggv{FpJmIR7P^Kz)AK@cDUA7pB z{s`ecegB5Of0KzC%5ZX_XDB5YPEK=#n#@4M$zd#?O%0c%{1QphmoibD-1#)(B)Jiq zFtO02_`7D^oOpockPBe!-tFT4VPM(XW5Q1k;3(Py4QLl-v#Bo_vf=kp61mA)!4W(+ z#~C~QiXYpPRiH>-2o(8W4V!tjfF*^aLrIcL_lG|8#6ey8Hm@a>sN*Ch1bb5(KGqg| zxbhdNRh#J;WwWVMRm0Pqh7Z6@(2zXPP*_Dnc~PX{&Z;4WLqkb&>F|8j@a|LiHm@a> zs2W};1bc&@^QwjnQ4B2K#T^48Hr~_(-4Lu5faNoej&F3M7VRDu8+N-XE)7CONw7T7 zmb)_V55g|mI7#s1WIkzi78`zvt8}I;Y{2ROmSighmcJ$HedchjmtZ-G z`2w&MVHO(>GhTw_jmBF5mcp(kenIYE2d4^SUI+=WyaYjG_*2KYEe4j|ef7$=kfrU)$eccq}(XK7Wf8S?Kj zk0Cm+oQ4Ws#v}ig0?Udqt8f^OONfrzE>OW$ve;4 zyp~j=4uwDn_NF$>9{@g_x!Ll6bhg$CC{jAAadJb zJS1BwJY1Qmcl^OxFY)ld%opIH2s1pq)_94BmlfpIT6LQUpc!?4%vp#x=t!Y^88lR7;ssYvVpnGT`lpvtXV<`^FwMcstr-s?CgnY1smFmWfd| zn`$og2YX5V0&gdu06Dl}LQf2@7FGcdc_HB8hkG>_3%pS{x_~6P^!X>VE8)Hw18-bE z@rc^9_Z5P&gYSiXz=z9cky`j&O_a^1Zqo_ z4h<#ArRSWa8jd@bZ}VDGiK^iUA=sPRu#sx$g?nGfNwtN0ePF%9YT{bC935OJjCmm>;B6c{9{zJ! z+!lkk%Y3txHI>rAAn^L|_Qc(BF@v|&SH>oY;7zM6fVWmf@b=VA0&hxC<0arN?%bPi+p~209M{-gbpx7w#QcCh&IYqebv`q$>s0 zer&C(HA8*{^B8(}$Q6j-9X#@%!dv(y#;2Y$FVT@2-5}k_N6g3AXC%AzGCOB}+ZiGZ zcril`8b3!Q86NI7?(Cd-f@6l1+1!Ftf%Bs*_E#8_+Ltp&UnxLCDh9nU$)J}P0!#im zbK8rAn_@BOCBc2y6Lru}Dd*d~mQh^%6d>*LoA@%+x*FT0Gl$ z37=hzw*WqcT^(6Dk+2jr6UMv{67YE&!p3mkf8w^-IrDzLdZ{xcwk%t`A1xA`!YAEa z>^CwlXaM@mWwAXY0M$wh0JL2ZfcEF^bT*>|HD3Zi&vufb#A(#U;=@j&mlq#~>M2Ro z1p=Vkb^w4rGZ_H-ujkBdoeu$^pX}=c=nvZnfVP}a1fYKMY7dmRrJ-{(Ld9Bg{G5O>szfBikST>AcfphB*E z93+O__~98q4JH_>0R``r09M}MsTD&8sN4D^f<|x!iNmXfU5<#EBYjXCQGcnEbE3-O z8&uY@Gc5gW7ZGxx3K3orB0LasAcUZ)UEA`Ud3$oHzwMm)(M#i=q`wUO0gRi{%jN&~ z@P9aMcT`q%&U}?u4(I4>y*Xni=27wU2U7cBQ%iLQI-Q+};hecK|KNgqQ7^2$y6@3mS+m81y}()H5%3nroB(R>J#omaNfW=1hz9XJD4bIm|m)yimE*n=r5^ z5t|oGZ1dtqd^XPvv|gnRchcaxG+Glibbsa{>&@2E(8f_iRT)FatrA*JEQyXQ-xI6UX+6`$L3@T2%oiwlBiO9^8b+?z66$rH~rc_7FQd$!NxrdprLe z#JumH>FOUz)bN6df3(L)9s4ja@n6rGck?`lFUS2s2V;&u0I5CNIdlI9?VPzEd2s!f z3D23A){FbDout%yiffCesc9R_aisRRTxR~y-bLw)=?8tMG2@#EOTu#`^vIDDRW4+b zODzJWOVxW|3wP%Zlmuk2A$kozq}p1et;>%tQpNTjTX=+QkYOmCOZO1TwP72Cd!@fV z_oLr}@*An+hQ@KFw&9#2Gqu(pjK^$h=>aUMMD6LH3c=pghW(vJ7Ha7^u&q=_qI^N9m2w(~S8N@($G~LP zg>e^$b*GdBllwd#{Qjw#Hk4p;2lIYs9mrN;myrp6X3Qi3CS80{QXRmAY^7GJ=U?P> z!{5KMdI=`=wcZ4nQ1>X*`fg9vk^J-h;Wdo#i&(ZY^6;VP^e2ql+)A~^sgD?7AOyRW>ac(6@K=s6 z<%X)KqTGSNGaTl2)C^I+_Y9~N7^1xI5wgK?3|n*Qtwb_@QI0Ld(ugwF zE0!zYsuPU+2g#<;=_zwv#HTiQi-;*5Q4p_4VyZR3{k#z=Bx?QC|`gmuQ__yq;A|ELzGL;i~Bdm z{`8WFa;(RL`{ui$akeCA%e>!HM``0E!F5PJB@pG0b6s`#fDS?6C}AswDD|D@FzRc& zYQ031Sh!bysQ(QTqF4 zDcfC22g?xz`iS!M&9Mm#QP$%4s{m27$^t~`QiLeeE)hggf*LP@C=WWxX!m{i!EofK zk<9ykny7w~L>(ZAa(X*-`n6*=hbY%O4+2s4gkXm#?GS*YFJEzQ5u$W(rJ&lHrm7kd zWhL_{lxa>w1@GgL|7`E;8=UOx-q&Y5<9ENYsH68O82gajdYSEgEpmnk>yuGnk5pLzh_=Cz~}wMX73goynlV;}0hl@?VX`V6nwD{hhPeZ4Qog$l5^tbwYn zMO`c#K15^U58C1xZIIWp_a!gc+jV z^YJD;T6&o*G7LT+b;W9eKbjZ8=Lkm+mu`^1h~b@#?%O>c%+O|bN%*Ym@!%d*O8Y(o zvNg>6!?K&2P!jxPmQM-zoOQOV4m+s|pJXew_w{C?-Vs(W;d8Lon}AOdX7Jh5cnP0f zjkf?kg2V=B)jVR zxS#>(b2wlt08p*806@DH0q6j3L}xQfQ1c}Kw5O8{CC;QSHqo6ZdU<{Y}LSlIKqp&g7 zCa?(AfP#NZ%qVa0bRa_psN4214$NRViTJo6*=5^`Inr;~Rs25P$$3%b@C_>4>Tp>4 z`?Ls|s6vESga{9Ww1W^dwK?dO!7y^Ezisbp%9(Lb(!H;Xz_KZQhWvjV{tut8kIIVn zz6N^bvccYLy*Z;X^Qd^1+@T1z#G_gvTBT#}Yghh3hIrY2eZBA%EH#VvzP599guU0r zxbvLG&^@|2&9zCGdf|iMqcdVcP3A1@YtaLDwlx_-k#iKM-m9gNmUP89NA7;`4@D1<`Ra^FBJx z*1Z);sxtHWq}4ItLfaqoMP*nWf=jZM+WVU7>4y6sRxi=Fpp8#=H;`=(quKWB8c6a$|d6WeN3`Fu2U6J07MET!2rk!!$ z_xWu8fb>P!%8EY{a5?>;?=)z5_h9ILK0^-`VQbrKJlWYiML?IOO!QL38FA3L4~AY? z!-l8;Te}|Q2~dKqT~i(bf2WFz`w0sKlj^I-VglY&s)Zo+S+xF$An)K ze-|Tkg~j9S(bqFCVQdquU7K)Z!4+2>OQ4l9XeoZH>NhNCEe_IIl zrZ&95X=K6IeoiBNucS#)@U@Mjhv%=2LD1mkiBp^-amHH`UNRmJzW=eE>%P}9?|0vj zt;%L3!4FLMq^>b|Inoy;)d60}R%-2fYFDQleg|pw5?xBa7hzWu?=V8(WwJ2lg^+-kryHaHo#?K-*xL0Ozb$3GrF3uyc>T5OFa2W^7|a|l zcZnp%j$lTsEP$EbMKH6ryTFVR)Oc!KgPG-W2Plf(mu&0&^cM5JpVq0LBvA(l%-pvZ zI{o2yZEo#4+o_MJpf3cwwd?giNa*$Su%ggwm@5UB9BQgHL%tRB7yxhVNJ9lFJn~+F~nlW0Fb;_2Aqr>}mj!kcfvB5J?0x`b!2>4zq1Jzvm+txZX zf5KI%(z{(#y<)lY#{YrwUfvXsFhh)y9uq2cvDk$c8?%#2oGggp|JkV{%LOr527^kH zT)Hi)p~0SKteQY+MUM=!QSA{Q#p+cF@BfxJLxAi!7RkO1-o4zIeNJN>bO0I z7&pn`k@hm2&z3}tvpk+`aB4l+(-r~8GVk}qdfGTisxl4vlt7Gnz9^{}ulWw+muiFN}F1BoHHuP8l|m9_@d_ z>{h=mWd}*=;3Dw)h%xW7*aU_c!HF>rMu?$R79d8yBE)#*3_%PfsPPhr@sym!M2PX& z*3M54GVlATsrpG0b$}p7pFPm&14I=6{)MAEo%%qGb`b0kqthxuj9YFiLX2*%6kJl* zR3l>i5kNHzfCpszBKR7Q4l%+%ani24T|e>6-R*YmyocD8(GJ~_hEqTQ|&ju38& zh4zx*_eRZi(9dNQgzKFiQG4WLLWtPUH}-qHx6-1zY|$;-Lx}bR+wJ=L__&9GFgt2S zZDzh@!>4IXEErfZZmaRN>~_fuf46I*7v0{UjBed-SD*IMMplG2N|H-|+)Uf}DG&T` zo6RF?8($GZ#Jf5%8>yInsW=USZ&zSv&7)$L=b8+W^w z<4t&)+axpie8Cl~2?m3w06s@Mdiec!ais>I?d7l{g3qNT;j@v)gTITor5l!YnfHg~ zLbZ}4xQNH61bn_BM>aOXGpr8qNw!kEUCR>nj;^cq5bIW72EC7a~^9;>KHI}$(Dt6m;^`gNq4($>k}Kn z0QB{av7;jZ)k+HhbYKyHR&eVXr@KN>^CbYZzmp6lE}M2Ro1p=VW zn*u;zuh`sf*9p#t0MON2_yAhxD*@0WZ!QAR9b74>_6kZ-HSTuJWF8};M?D%Un1o0E z3#YjiwC#4i<(h|@a|m&Zce@^UJP>yEm;bWcRpGJa(ob%xix8{)K1d8NyB9XD%{0dZ zBRGohYcrO{2Tf(l2|6%jfV#)F;J^$nB5`=Nu**i3uz@~u8}WN9C+BFD!#Akx#C>7u z4SS1_y;X?tiV)#}kWLVSrcOc_S{ZCbF7>zVcD;B^+>>;->q{_hN`D~#FTnrdf~@ie-7Y4!+cgef z6fgs=*J>Vg(jexPe_TV4VIHzp1k&pc#G^D+l`(YOHKFB-lIZxy(Xqm)V$DXag1cQS znfK9gk?y8Qf?KhC(&`8UU2?%wzNieV13Hqe)Na>YPdDuUTfIcbtF+$4ZkGr%bnI%p zM8}TCTY!$juIN~C1HBzI5yrd_66kmv#zQ#E-D}yTmT#6&?_3U=T)GqXjcD{G?)w*p ziPMjYtr%gVR#kwBgNrcns?O3Im7qpbquMgGhm(wEblc$kb3F6De^#h}BvHc)CT_Pg zM(XriHn-dLAJ22SO!IZ{9GbZ|c4e{mB#*Y+RdJo|cC{i;HX{jlyEbhV_ubiQ!4r;* zO)~Embdd1*Z7DXhLN=xHA(LEY4U%}XYY5$t3x@Jw8~YvPz-QQyNY}=}uxd;dLwG(grNA$i96P zc7-K@Wt_*84IcQ*b=)lTe#fm~tFjqM@bgPPC4l8jUsQ(G0a(aZYSnt_5plo6fN5s+ z5?FTDdK17R!VFl}u2UTeEUTD@GYWu3*ww`Q4H96PD~x#|B!DHo6@cZ!x^Y`#OXI}Zq>TTsgHPI2n4%T>)lHw=vr`1QP4Hkm4ZvU znrh9E@5np`z?c89YX)ub$bX4_t``-Ds?)LMfHzyL_V8v~dM6h=-5d!%CkPVyT|Fc+ zc(F&sH~2J;B;}15TV&IWDeSkHOZUVF^FPFrMwiHMa&hC9$ZK@1Lx>c4YIR#8&vRA} z*O5`yNc9oJhX-sSdJx5IiTq#33k>`6(sm*kW znt`wQTQCLAER!{atNp-+#vXxpQ&+&A_-#@W>iPe{Dy$P(Q?$HioXX7PS zA7#7+SS{@8$Xod9K-{Yq#=H;`Sp5XT=kPgqm&TUJyZY*-Ze_@wEnDy_li)@Pz~7|< z&p$IZf}!=pva6gxYpt{Ztt*Srx)(R8aTikvYQ6+ocXpDY#Ie-HJe3x`yd`o!^^_#) z0zvDI^?=q758B)kd2i=Kp!M6o_-Ot4VnORB{fp50$AoI*Q7T%M{g-(d>X5$#t#8L8 z|AlumBGl&D5_yV~fto)>gj>8s9^rTZ{U<{o0DbX=kX{~JE1?l%_@g)P}$DA zz|x=WEJ7|)A;K#{ga<;JK?s_+4u{!^Us>uOs$#=$(6$n`VQ3oW|G5_$T;C6>tR+s3+u zr+*yd&dV169~4uqNq;Nhbyut=)7_THk8?uLrQ5+|RIbxkTq2W*B{CCRA`ja+US_OU zX*W7)aAYgACTi&J%tKZ+_Ym;+Z=!~(eGMJ|Y~yRWt|U5s;PGUGcYf9?IN!X&ypN8d zF2N+h3WiTwoh^|oeNh=!2XrJ`sU`A5o&~c(cdM7^c#_teKt~Z~k?j7)OLW}Bcni=` z*cBanT}E#Qzx~9<4u5917k}^ zOJuF8024L-^OEP>us5~gJY9B&ky;4eqHH$RSvBn9G<=ppHZVsu z6jsqtUWkVNccw=r9UesH__;O*)3n2l<7ybq?-sacXbhfQ5@lG<@J!X~j)7{MbeyT^$04SNL0|b=M{s$=X3~s<276f&nn^VB7%w9%@dW&wDVEDA9osaLBPXTd}C8ly}d(q zd4u0;2&$J~D^q>?IwlI6qj+lvpS~o$D&IxRb2OPw?VV0FnE{H$dEv$?SqjNrphMB$ zDurHAEnKDk9@vk5Gs<7UYB}8SS=>(zAZi^G{{9XY0tIW?#~nRf|5jYu0OF*Uad*W~ zu2&Ku_VIYI|M!i0m(kkJ%=;a$p>{k;@XcU8B>>`6*=CIZg4LmalC2a#{FJEo@s(OH z0pcO%3jjidS+sVG@e&}eG2Q}z5OxKKX%`4UoG6TWAtV6e1jP5@w?DO!ysh(@mqK%9Dh5kTziNww+|Td5xo`RAbHKc^%N9lIV*%~$S4~@ zRT#nH-ap1Zw0*$495bX$jG_VDnu)UbK8j#~?gQQ-$Fo93&>~C{5-`UOIefnV!?0Z& z!(sTco6ee&H6Qjs8+90oGACS4kK432|?mr_L_q%<-!&1^lRs;`8l1o3eLEAWu z_j$8{E@sLf)pn`H{WVXTRKIM_>8VEsbp-@YUw7)!E>G6ac=nc~U9H z8jCk!3%7k}*qL?3YJxq$Q-GaUI(j&-CN{NUXIV2>X>F!qN$mV?PsfuDzWzcxKCyEN z^ZuBeuO^hFDzlJJ3G5v2i<0Vqon$Mu5BRL7TQ<1T>Lqqwr1d6(E9xEvSH~DHvGWk) zEx=A;S4UoV4lya%Rv7a_NMPswKfv%$hOy~vA8;pMz0`*9X^OJy&zS^&@JUZ?&gu{w z!O-);Jz{%C=&6+!py!k#^z6ug2|YQ6+|9^)iKi6f|sg~4q^FK^AeNIfNqx&t!g{20LtI_(ZUiqLaqLbcH-7468}#XJhNmirXJO?c$L zaD?p8$lk&Z3X2n+4AguuBKzWfz*`*;Vzo}t2a#FvK45o`EtkH18C1xXcY(z4r~$As z7Ri`hs0Qr#A1)!PTG;iL3#TQ}FIXuZrk#`X3*&FZYh=Lh zJhw(D^!_Gn3seP5NWo{)$tyyH2SS=a2%6d%^vYlkxzyi{)gbrm9`_`@Dl-}^o6?o? z{}B8ij{N{y6zv0^b*v4?e@*R!@A0Tsh;}Tn4_HUeIO7-f!YNob z7G0HDyIZVV*!k6X@XXghxqR1{YE80zz?WUInoJMd2bjRQKbNkz4GYzVN50FzlSJ$T zGO>L?{tU#V**@R^Ck?J^&c<0o8(BkVVownbtrIm=?Q7_`S~eb|aK33tbbQO>!M*lR zwF;u+9Oiv=+~R9^9tVg_u)JcDK*tJSREE_79m!T|AMifUg1G-}^%5PA)p`@?D8ekV z+{<{0jw$0UKu2L$bnJenpyQ99urYZdB+&5?EQi9orMvk1fWP@|3AX1F3g?G`I&kXY zanlVGGfm_E5MiQLRe*`pi!kxnmV${&P@^RHN0Tr zm#cwBnVy^52i)3gZ7#FI_5uIoPKZ6)K4AB5whtKUKAm?x38@dqKH%XHMfj@SmNSP4 z2O6*r42=!Yhs2KGK@|Sl1>YJ`$KifLPFlAmI6_)k{RP+o|J{yyM7sv(6#2}yG47fr+QjD&Fb?-5nQj1ZI^8m3f3?kiZR zx~+VBAIZ zmjq*nPudfRKNu@(aX}6i<0cqv!0G_TWGl7I-`Z(TyML(l5{zdsU$D#SUH!Y7%*GUJ}<-S?Uc9GJ-li*dX)j;)0_q|mE z?f4`YQhZk1*zikN}Ryum%Wg zK8a0dz;TeTUfGKGXo}!w@cO_psBPRq3^-=t?w?_21RPpr0dUMO0*>o;72r^U8ZQAH zy_{rdat;+TfImf)^P;(d>L*E#5)6Q(yuzHCOy|ms0I#Bm$2dvz9i4OlU-U4(Gc14Fl#|t{t6UMv{66kQx zQlP_>k7CmqIyCgvD|_`Vnj*LuygoXdC0`wurD3%HwQX$82pzP_0(4kdgbv-g8I8}n zYFHtK66kP@lMGFcphAWYZA3W_9V*pNlBff8|LaS1u?J7y96IdkJP34n{Y@VoK7LB~ zzm6+Hhm{G{Mx#`O4tFsRbZEVp_6=^rBmV&%!vDimucrqiIO)67gD3e|o8jt_yoMI_ zG{(5uRTVx*M%h0803$eD$?YIDS$$>h>X<=Tvnm$2F3RGZRxm(M502BN0jXF>2$L)% z|xqG4^y`*$&P}aN>KA9(6f${j0UY=;5@aOdEZmb z)l-tF3j{r9EdzRM@cLeI&r z6jbYJs_}=mU71Hu|4DWmf+O(Af8mK(d|1tPdeGO&K+V^`U+VOrgW~}dZv}l2nR$WQ zj@pPO9$PMb?CVe=S3VXJ!;RfxWBhR@*oSIB!Qps~F~T=^l6V2?E?p%X|Cfwj8TUW&qF&g4>k_92Gq;R&3+q1_yK;dB%A4xMRBMu*9^B@N)nv}G-NQbd z`*Z0}f6;kl!%POAB;xddiJczo+Xi$skewd1bJEbbuU>Tx{g`<)^rknZp|9al8mh$& z9iL5T*|8)#<~$z!{VS`2k>xGS`(v)D?oLaByPSN|>I@w_`Jytc4(Ld>Ql|&qWiu~A z#~rL*qT`lYZvq`fn5{d%enrhdbX>wbCjElQQrHz8n;a?V_>3^-g^)nU4Aw&7Y0_Q% z(}NfMwgkPoe#)hHS)&ddz|VDQ(+v|lW0=}J6k(!PRe*_0i!gD^?W8vA^zsAP{E4(}T(uao@Ea`RhHI+M>60?7W687HiQwf?N2X_fQW$JEH5k zg-q~iIVAClaUFYkE?B{vsQ3T|IdDf7i4AUflg;xD_LD?VcBTl@72`u2STJ~k%ZsYi zlT1`4({-UU@;nZta6At9P@qx^RJj6^6uBO71x%8i9^B>#HJM9n{2$Bm*;FS<%3Da1 zZplQ|`wD?~rEphNOPNxq2Q8gs!0$6ZLCZq{Bv$>ks@EoGTM$Nl8;71_qHH$xh^$0{ zN15m?;<69LuH?a7AgmHn%L@sq_rdAGJ}?ZsMb)noZc;dUfF!x}TGR5fEYXYZk{2kPmI*U20)a}rgl^fcXAqD$sikl#zi$0R?$#ih=%@;lUMyB8d5l_ zlq8qFz%=YFisZGV5>>;_s-dt$!zFH^ZlUTVxkV(9RTCVKRZ>ByI?U0-j~|itd8Y?0 zH^ki@uIW+|jJNQ3@IBa<;UQZ-{>;2T*r#jbB&o{G=96}51LK+4s({7p^nld?jLBAN znZGbm?=Y*EV0@$2n*d`GW?5u0sWk_Y%gu5E5YA>J5PLD+^-N zb<$qFzpq}|XD`td!G?v&W&Tz2Szmvd|J>iP2_jI|Dhoh)MG+_uKtzC^qXhAl8P`C$ zr;`j#&ZI)-rxQgvZ<#+|{UnJxKtOr>#TdWO9l5z>{vpnTh@qCh=m)B+ACf?|)geWJ z>c)g>(@=`4HG`UuFprLXyxfHdM&XhF5~l}-hqFB)+N<7C?B3?D*rfAVIK1Hv8JH`G zyjA&-3BI0yB*yNe>CRlxlD|sBLYy4ys$1ivbrz+Tk zYoe;uo=g(hy`9rFT=%;0%KE7)RoC$vY_D+Yn-GKTT1TkKTxziWqAZ3|FG*7VKS|Q# zn5bs0ad=xP+!fVQrW9;n?IZ)X{|sTXq0^wHw!vV#i^qfybfU|Maq#86D4R{~F7*d{ zNd2;b*+V8N;Q?$5tAHYN42p)&_hEbSj{=Gm4y#L&OKF>!ht0;vuCW;UYW%7w z!cqJVyd-R&G0sqGzBFtcWg7Ffz?=#*4*cNs*@dt8eI_NKqc_AcV`(Z4z!fofrrZd>S z);CL8ODP@P0Nxj+ckBMbO!vvvmk9{DU_EXCn#GmT>@d6lw!HKsL^S+O2V8-WP)1{zCiF0^5Vy z6v6h1t`t;TH$zoxhWwArqhse@rhS7?@W_9qV7rbdL>vCD!gjZ{u}L-LmNN|e_-GCK zUsb*ylU$}Dk{Gr#^H5zbxSBud!7u#CQJZOsgq6d{@&?zjNXS-;pzKT$q~O@<7b)1B z^s0O-CioEvlDLKzT0;Y^fh5$>9*NZ;Z}1}$60+4IC_7UGX$>`NSVM3qS9VpYG!t#K zO=4@kM)Tum;TxRzs)C)hKqFVcW`|c@0h462!xN5Blex@hhqq<W;dCb%v%@D}L(7YvfR@@2n;j1Dm_UfO#c_8(%4SmsN&P`vsb8?S zJy&lC0Ck^SSOt6Kh0G2e5Y%^|H_^Lhd@Hj9g~Pd$DqtTRU1=LPkr%Z`9X*|nLT zC1;1$9uMySJ&X3+?63>-KI$}A{UyQuEIw&ZAP{0SuE3@neX3W2SRI0TvXz=0)+XwG zdb-xj+2LX43uXrqX0yW`#>?5^2II|j+w{XWU20$5gnzKVu&aq@wvwQ}t1#w;keD5w zug2`~$QlnzFM*Pk68*daE7%?`_dj!h8F4q9cw?69V2c6jh_baBL+ zIy*=qHLlGL!<}Tbd+0Rhr$Nm7erl(Fl0+RKv%?WJ7{AL8+T84Lq4OYShk6j~W`|uz z$?R~}enqoGD_07tefo^5)(rV~nMcRI<^|d}cnOdES88_n+H0RS{9~OR4wjX%^eUYl zCRRfj?&=YF@$)<;xlA`CF?L^0cjkiq)(Rs@=%_uBuyPn#-emK91MC(-*_jm7H5WY3 zC6Z#~SDYkN7X$4Mo|5=`DP+x+*I5C({zE)dUc>91JYFRjUjOA9RFk>f@Vcw4%u=UH zQhtym>9$N%m&S;uD}}qFTFR8d>j_RW^s4#-dQEdHeZ%V^9usCX^cArRE=We%Z0Z>< z#)5A0V8+RFt<|p9N zUId`EncgMw`WBA|`1ceXWq5rB^F9vrRQ)BvSBLnd9ntW*oi9qN174G@6kZ?aG)K?a z_@vfLy#9sx0=%Z~(Rtzf#!I|jV7vu*E$nLIfS*{!@4JLCFN6eM9|EI>N9+C^4R3h; zsNa^d9#T5k2E0CAui`arHG$!E8(CorxI}oZRTkj&x+1*(?l-|}C8+UKvf=dyvNE*E zFnH$)=ciYg_x;pQ{UnJxK=8V1KE`jG{Wpi#&p7pg*F7QF;dTGJ1g{_2s|c?LyHc?9 z0j63rM8Bi{DWM{IDld2XPbnf;e zrDsO@LQc>cpOYY6NT_iPIHX2w1dF^p-$1O^HM0^=m?2)_YD`i2YWJrJY=+p~b9*j5 znXI|;W}m^6%Vc9qb^vXLSmP{Zz0zigC5}*&>1Q*LoKO9 z)o`K^><#{MnQG|G5bNc3kKjU0Fz&gc8R9cX58K`yyT)dSp|a*tRKYJ)O3o1HdOX?S ztjFLen;|+g?<3DBZJZ?dk{6$}ClCxVBDeQNNp+YZ$X05G*vn~-p7GhES}$ja_n0r3 zA*g#aLrgPX&Jd3nZ@~;9>}uk#cb6Grh%n}bkeDHkg;B#@-A0?u5X1bolpQLigA{n# zR&+c0D&2xDV-wg6(LmO4i5WtxESMp-_yQ%X>UF7FG<-#!5j@GS{aI!RC8+TdGsLrB zvD#>ccK`*)@aj2xa(JYKGX+6QT|OLT8BHU&ale$HL*e&q7#LW+KtH zDnEruE;AiTJVWH421hRFzDgKLLPwpAgyq3J-)HV8%sIZp$Q5S%KwX}yrH<@O*5PJ| zzrLaL%pzY%3$1a~_fjKCsBtM0sWB_ez$m`K454++T!<&k5btqCrl@?%cPOu|w;AGZ z*Lpzk46uebur6h?so^7!NeMPJyzB@ynJa8+xJFihsezJ|pCL*5OeU(tC$DhvK<t$0zxyOV z-w}zj+0-$rVMnLo<+AxEJkU^BMMLIP!yPd-?8wdr;rpnD6b=m~$)(4dhU1?T4Yi~a zRl|%B>1|MG{>Km09&c4VQZ&3ddB?mS}&)D8O#?< z4I<1od+s-0P7RgDTQD^UyP9~RT!0QP62`m`5>rDD7&YAVcx*bG8ZP(EQnrhf4h{gX zKQ%nb>m6zWn;OhTS08O$}{sld0jdRMFIMlq&_*e)^xP)(rVCnaAiD z_9zV%yo*Qv)2Si+3gc7nYA@B1x>T%E8~(#b94q3k4%yAb^-vgq|Bf?6xHT7ja#{BQ zEbqehlVg9>SLTV18B%)URKx`Kj!3DA&=K`%@)=;ejLlK-Wbc8i3Y zVln6?$)&r`Vi&;qAH$7oUP~%bd*mn~MC>OT`w;J~w214&@Uq9^7Hz97dKU`^0H^Gz zBHW9bf#Gf0a15=7HIueMUdvr=dEwvHzLHBjivurW-gP2(SG((b(neNSBHWIMK_y0#m7;l0;n~ z0NP+00CZXt0O$q({L5YKcFu7~A_BkXl+JO;jmJ21nY^`X;Tn`C#j7fU=T*k6|fVWtv zHtbf(z@9|h)n+0yIHLPKL04&ZS9^q$21g#2cMUy&dB}Q8wkm=qc$9`}aYM%-p=Cu$ zbo}xI*AlG%@7F4L7x+EqeROQ6cY!6T%CzT`R!10sQAYcsGOP~hNVZaUwV&{G!|&^? zUZUeUT5kd!MVJMbhZ`@^v9<9Qprf!WI$kMvp@X`@m={6<9Z$mAEu7?b(>AGtw@s+m z5=?O5({HE)Uv)=JFKg2u!qq#B4NmzHCTdj$nAo%k6VLfvFi{C=G&QOPmB%{CXzx+? zIsdd}-uF*O^^YWKc)`R!pTJ0cXeVIezuwi}-Sb>7GjTmUhrU(z5W2lR+Fk7{w}rN2 z(9$X7$!fy8+HW?30aWp?)q?Hbk4<6(?AoxKO!Hc#C2(Xd{ildvqP|(=MB!V|u;JW# zv^#V>n4ZJ^u!(d~o}@17_ZODOW7fnU?!?z*dh5^>VU+XjGLA{Ut*91SsUPnC?lmb_ zKCc#b4KS9id53E&7CEMx;47eQ!PT(ojvj6`I4*5N_Drv-_`E^MA$yR=gYUrK>$=45 z%==wp`ZwYxNpPc=Pl+M>^?S~;_)`ozWXV=)J@`kW-sz*XUJltunJ-umiZC0pBaD|r zcChgl3|V1U*UhdcTZN|!V_pb}Av+mMy>Rt|vFU6*c#f}LS$!!T3<0l>NDXJky1joJ zx+~s|O%M%Tt+HU~wkjIB|KnbBHlqYJUSjCp=_I4w756wlUC+Glr}^q9NpPbVPcn3m zo{TQGV*Aal2m3e=Vy0~X!EQac$3O{ZdTdh^&a`%=pxW~PQ`Pvh{`Z;3fVfsRI)b@) zT`LL3^gh~iF=iyMja=LR!*{QXOB-tJ z?ll!Z(I|-;>m>S!Hm-lYTV2kH{#)k#-ndYEqa@fx;!^@Op7upab=*Y)TPf6-@9CBe zhFHBsjR9J3Vxp(+(K6^X<0WczGTs8z5Ozh4VOt4m>>`YLAtX>^@K~V6oP2CLLyf(B z^~yfT(iA~g@cO86$q#WcLyafjjQeSX8d_xmYP2sxjmth3)KG#NFM%4}oMdQnG8M8# zPzO=YTLdjtKS`nv5Y*Uu61v!vn5qB$Piwb!9t3K9beE4BtF9E(Xuf3;YW$v1?QxW% zYDA5Pm|rN<1b}W0el?-6VpFI^joL)#cLPGC|=jKaQ=8c#9Fi2(Lml66RA9 zuJj1mRJt#IDTnx&$q}#aFyid3z@+?Gd654ok3Ux)yaEHA;x-d(o4m2dgcTHw%Qiqz z7-jMIP5baid9~rh1q3jmaOFb=*@a?`Ga=$7Y&|z*d2l~jE!-4|2}6=xdeeL*wqOXK z&1*>|YLDJl2==Bn9DBQ%XHBFGe(?}xv%!(3S3T8>6zC-kqL;i7y&6F;1Z$$#PO2A0 zLN7^j>Bmj4EW4>YWG;BYnB$&1KcyRx*QhOr7Y!~KzFxx_R3M7F}MSD6caKSFB4qtlG5QuE0z-(=z z-ls=uy#%v|nJ)-|L|AnH(Rc}FHyCdLm!smZ zFGu_|<_ktVb&tZAdyJPOez@@#jCf(!5&znH8S&={V_pb}5&sF6cH!UdYKe{bzP?$? z8cFHkY4G|Z{;{uOi`$6*0W)C1h}S9$MtrxT5&!r*GUAn>##7_ke#N~`GTNOT=KOR! z^S+;2sh=cK2gr!;{2;p64|O&-;;(WZ#E5SU!EVI2x>!bhpTCPn{9&#XR15D=)fm1k zV;%!yXcY|=yn#m<@pYo_eEg{6_=5Sd*}!%rmYDo!z3V6CY47vM_OFfgS)OGQV|08G z!9@Ku&sB&0I4&BYWT=;VdlrrL9!^e8=3*P`!iG{8FXC9|+lp$Tl^W}foCNMC&w(Ra z-W2y88|$lG18aghP^w^df6vjw7MI7RZLAOS+Jw6ZCCB=y9uM}fE7VaO>qjx~_n~&$ zha|zCWYJi!>x+`=xF`89V?E_GM_upTuJv-Pzs7vQSf}pMSbxNLIo2OA-h#0%>^jz$ z{3&C7fH3BTkQnQ~=P=f{m%b<+$j17Oep|{~N$KDX@cLtY&gZekZLDv?$SfG^T4ljl z?_D(3UwT8vx)RiQiLw5elZ^RA{py9{82R4Pj;oC+J>R38prw?<}o1d&B$0^jz|9Em&ty+KRWIH9yvtB zOT{Eu=j*bOo1Wyg0bdU#F`azjtPnOOqa1f0fputj^2pd3cG~@%V}_K$Q2<|?sfe=K z;2&A%>1p>98ljVlcwU$!o|hNskjcMm(&a_clM7{U1F6<2uJ4b50iESPcW-Fu1jhEPYzVQ}dr?9JM9^#imvcbW^ zm={6Q*X*?WNMF6wR9PgJeRDG{5=`ckZV%t$_Wuk$UxDq-2oQn1S)%Z| zP`@Jd9QwMTrxMhB3G}?&Nrn=AsEcj%bQirmPfbxzNun+g^xR_<(DRjbK+g;Q?Xo&*!{$baEfcOPU29U%){ z^HB4rSd|r@c0cNP5Kr}oK6N0>?KSVHqK0~G*q^uwD&)$y!0I-- z@3^kSN=W7#jTfM9hldD^!P_JbuNHPWs1`QRN9Z0#Hz(%_mBTlv?D?nB#=pjjkb6{! z@QM)OfskV$1WjFy^s$4AV{OH(S#eL&)9%wCXj8hQ{C_b14__UC5npuLeY;l< z-#umP%^CYJkBVzZLQC=Gr7X?OrLjhIFGRbFN05K`rP0TzV9)VoV$xR&4^+zna<(eX>> zeRS-ir>Tu>WKA5*@G5dK2gjSIiSkRDv2!B_k}N56^Ov(cV+9cm6q+dEY-h)jyJ`;RO@xXE9Qr zUJFe8*VFEUJkQ~0N*mxgG_%>QXr?{dY4^|{*6V3^C3z4aFyU$UpE!OtldRDp(&@R_ zBu21R-z#2=w5hhkGo^@NqW*j~78N$*Pt9?6hxgU-DI6vVAs0I#HJJ-_0Oo}opRABV za+UicAXBB#E2@R7)Z(v+lLf@9GZl8+4`bIVJX}HSSJ%#(;C`%=3l@Ki9X;Il+_>bTJZwo)5CO`YcGdW#2X zy&R%7%oi;FsC%^dn{2!sqGOG>V2BF44$)7)lOZ}t81q6%4AE{_;)XlA9X4D1-R!re zthJO5T7p+cpoT9mW07zEvY}dcdfb_!p{i9D4AsF!Lv`*;GE|kI#!C#bd^UBHB;`FNNuSF^e+be9(QKt~S5!-xQa=Pa*Gb0c zeQgR%d&Q-3Z@|c5vkvu`@L71cg{_C~?W1fqwUq1QZ19OJlLb9GjbLkq2f>rD${Lh8 zj*H>*{oiQMorHdaKO3Yzmn4@yWCFWkZFu+{5QXorctjl)dkR5If$y11MD>o5S_tu? zY&LbGYS`InIFa!_fs2biX z1bb5(Zl@Z0r(R2BzO+-Xn{O?G##xRYHaJ__=lu}mT;MI5V3)=hmW0N%$CC}(UJXat zilGJb{*ddYjgzD*)00oy5s3m_a>4gcx$1BiQll5Lm4e2NiF#*UrS%dTpJKiM8bz2z zFInRyG~RB!1<)w$YT|h-1sZz@V_paeXzYd6Z1~3wvFQvNFZ9(bYbd3I|A5zr#)tXQ zF*Sifj)O$ zk^k(6AfI4->JLHQ(~%l&js4C??E4@zBDSQ!fpqb%-;%F0-`#{Ta~0UA;<=!HoJy}Xb?@Ba{FzwyFNu^9A{;gE= zXY7X~c#o((a-DeQ+jW08#z;V7u)HXgMgou5P zvFGG%>TfPFO_s7>@3F&lTU`|U(YD6k6C{lSgJ*4x+GitmtHInf4R6ABZb!``%Q3E4 zO>iW53L?wFjvl^!T3o3`mb*Ugn|e`6`1}L6=^}{5KJew*_i53U%sY$XBW5}*B>^ZG z!RJ%Hs0^z^WJ$JCTVr!Q-EjZW>Lq+$rS&Eje+l){na#7FanSK7vAfZ+dC*OJndwl z=F_pVE#4Zday$@rI`k>IHP+W-%cbwW1S;gpFNMUg%O9{Yetrh~Pz~7eG%lg>$Gya2 zL34%-P`3r38?o$vNE}`*?6RvRY@lCpmH2(2le4|b;Tu#oWgOc0`A8A++jSyDctwct zKuAjnK~paQy)yWiTS3ig8o#rHJ0;5 zNp(O+vX$Bzo9J0E8=PnL5*<&|dK2hK-J^B)LB>mT+}n5y&{5bG9s9~{?clFIY)oDV z33Tj+C3X0qyPINLV|5bh$+|n2ZuviT;GB7Je=tn^@5I>B5hiL?1(-Oh2oq16D!ow& zYBcqs1(ofcWVH8SHp4J+Uulwui38O?lBnSY6Mw!5BlW*4Hn%mlv*$VdwcU*{Hu~09 z+y=5o+ZyY;)V9X@kq5sNWx}noK3m0o_s=@W;)8Co_S1MESN<24+~LOOV!PRbd#vBO z)Q4a~BR^uIUo!h$elg4NVIc+l;x?uxVPvD0b3FlSH(PRd^a$DDE{e>h9~nzG)rKb{ zk{bc7$88yF{fNq|?-k3H-v|D17YtV$mbRW==`{^sQ?RHGnzvvza1Xm?6Xqoz^}i?o zz-STPWruWSiaOZQ z!#huiOB+snD8LjgLRXZ;iRm5>e&>6kHk3Fqo_QZ9W^1e|3H}xXpVT!R8t~brzNieV z15S{w)FO27c)BbKD-X7Mi4*&3y$PHUVHQPgZM<~G-@Vis1vnw>YT_g1es=J_Fy@7j z#0kus;UIUfrIYrTPyDu&WuJ+qzsBxIm2J z3)Ld@z!kC^3zpfFj3gJKGN8#K^xhj~Y>_P5OW=F6(?$E(+;FUT`p5YU7pYogiX^P) z>vi-!Vw$NFktN#9ioIcnvn{0;p;>o0{f2eARSeE&=*x!I(kSwtGRRsq`|DhefI~9MKA&;8BUT^i zL!3DG`R#3Xvi$iQJ5%jGmo2whv}s7_KBu|jEDTJ&$g$`|aqU=?FMBB~NmwvqNmJydp!s9-BmSXr`lWo)KMdf=CjN!W3qm-d$q^wz&MaiHan6zUwWRTJkk4ine3^d~MA77myf zC}T~{Ri@7o;HRQ8(tKD%#A-e zfqkSOO}bYf#*dmRo{9MdX%da@-N5TMHI8o68Y~n z{M*p!JdE085&GUTcRRUh9O*KeiOQHOuF3cmsePcDTV(}ny38VU!M|lI)bnS~XY^Q} zU=ezSlSjkf&a^v^UTePedq)FRBu(TvV)b(;yn|KIs z)N$jyql~V+<2!#9wH=7ah|475@s4M*d~ay48oPKnq!*#H++_(IDVF?;``mqls=(fl z+3i95#Dj0PE6VbTDyul3xVni?d|B)uFPy6gWi*dZeBvfMWs33eSY;$UUi!qcGJ5L4uP0lC-r%Uus*_O`*T5KmF~!ukdJ+20%X$$yLd4;N&;sHXp+Bib z=)JPFTZ_=$te6|V{iAI+y$C(RmGQzO{vwyvfsF#xBJ{J>NXY5K6C#0fu_vX>qZgsu zUFK1QUW7jDA~^pel3d*9_{FLMUt*S%co90sCAN4_TjDn?rMv1(^&)hlD-zF19T(YF zWTO|MDdJkrN8QhA51T98u9OkxlfJZ1XRX#kF*{m##Fj}yQjw|=S4%>N@cy5pQ9m`b zyy_U6zrTxo+$`#0hi_=n&n}Pl6Qgf*+8B>Y68eeLTyc2+Z;UFaXop9OYj-;wBTB{B z;|1^}RDh})(KT>OyJR=g>q?m&enLb`v+L0OLu;KZ~8n zq|y$<=ma}hmyCyUhe0hu=PZ`pSg=fdWF)x=l>xHTyPt#}ceRWyl0|z7i~9^NRlevJ z@i>n^dg3Bgi@Z`2R`fG;^eL`TS)#{Tu{Y#78)bSC`s^ZipAK5Z*gQ8wUpCB@Mv?cF zK?ci3sJQYx2_2)i3q)tn5!X&5au%TjIMqjQ7a37T35)wYGDVf~aWOYuxJ;$2NL9w$ zBwAq{W4c{n#+&bc0KAv=u4bML<{w&RRTAP1c-N`{38ZpD|0a+ z4W~5O!@I!|s}BT;6X!lF9lqg(o_40%eYU&a$&|kMf!%n-e1db`!sqQ-;Kbqk59gqm z-W&RtxOObcIUZ#~37i2EcOLh7k2@)u9qv;^OSA}G=hBVuKj?gg`<$%u_3YsZ-7WVy zSjQFav#*XD=RRe0Rgu$Q5N;`Ou#C7&5+3*2@nQ`BaGq^Cy$C(Rt=HgrPLC$?x?>Ak9UggAL5jqron9{k9>>a9-8t;m> zxX(4iQDO0*GfA=GthcZ+PeQSePy+?i#l`|puB62RM~W!}>K;8+IE_FnLGmP2L|0Ej zMKt({qow*)PCC2gYJ08-A+xsEQpW9<$wXce%(c(Sxavu$PUIL8p{VD;&kw8-u~dEQ zMd;1f*)2&u30*)Gv(FXs-wOV1xbrNuXtD^s*d@0x&_+y~%{f!KfFIAL zZRuOR2p#{dUW8_dI1A@W5R1@%IL{PQR2Ml;wstii*gSU^x(R>O@SHgIxIo?`FM=_w z+*hDA|FFXDXsIfdH>Js~RK%$MxFRla*+m)bF+~h=i2Avg=pHyi>_L!0=uXk~MCk12 zB$w#jfT&CJ#e-hD3at;K%j&M#Pi6Cl6RAG1gZ+TGdCOA{-_W{?EvMYKfAzL+Q9r?OSP zqGvuRuH%+{GBMpVuh(%!&-|T^8`m>sbk#He{3qEn$H|DxB;o0qE6%59e&-xQ(aXL| z-0B^Ch?EXg5m$9QHEUV9(zdwnp&wI?aXnOJ8P`L%H0h!DE|fh~5z2U;9=ga$nR3r# zdS%tq<>K01Py19oNl+Cad+6W`X<{G$CE2oXic^COsE3lUv+O(RL^*3ISl?vU($kSb zwGYo$s-3{~o8p=dQ788^0-LxMKfUbR!J0-b`(6{P8)wE|T71VcyVfq+K6+*>{T*nN(I|7@go+>vCb>Ub&`M%f7`|NxvajrW!JmT=vO; zCduqy7;YC z@i+a>ZebAXtzz)%kG^cUK^jHgQwA9Ov+R4!wRdozckjSi_6@FWI|`ZX-^d`<&tM4Szk!&Q!b4qh~pp@;6cwy3g;faN;oj zXFA>TLviiymRpnwCGfOH+?IhSq=12c5K!hKFv`FbWcLU(J~ zcdm{r+~?UkZk+p+(N#rWwGI<=4p2s1CJB%GyfPQVKjoZy(aXN$-Rd1NhDbbneECcP zfpf&2a-b9Mux+j#=zW*l_EesBp|V8$;f-xg9BAP@q3K*jC?SsnJ=aMYC58%J^b}`+ z)XO!+*`aDmf~o@PKwFH(f!_B_vSr^XP95Svw+(bV&<~E44)oY3nmEv}JgVJ`lu%79 z`)(1}^oV|gg`oo1aw~ofOPq!FA<8eTanc~?M_8seUG~j);*i)V^l3q2&YN0#*;nX_ zwz$v20jRKe&?-`F7%rZ*U{z3aJXA6#rSTxm@=Slr%Ob~3{(m7+UI0+xnh&i z;7irMPMVX>c`6+dLS{#CSfSya0-4AeDiIl1CL-gI$VntZQD0=yK0k1Th^6XVFZ=G8 zX}2V`?E8j@W}n^i-#7TT;r<>NwaK!t$R)QhaD*tmu8af4H5orBPbmbx@2@I@MyDsJ zEc*_AO}0WSAgy8D6>{0v@EMEL&)PTa77Jcyq-!l4pHf?Y1a^0wt#-qiy=3GJ=A%M~ zK*OWzOHoe11*G0^UFJ`GiEZJ~eZ?bAYk6`8RZ7%#k4DXxUmQ1L!AGLq3`#ND5zggo zF4MTI4_wXFVTAk+6uIU_EI*aVz*SX6UE);Knx*~Nqyj-;)>XhfTZ@N zfW?80>h3M!j5Y_p0D6c-HwQlBQWa_sSBDbyJEh{sD(V@eFMjobHe88>10u%-f!7o- ze6`eXb6}UgtPi};)saNKUc^}tcv}F6BXX#d1%Z#m-R8i*feuQCPqFwVQ6_`$y?Jkf%{4prO_XArm`@VdSeupp2wWrcl&tl(tD zE)Jv~E)Ba?#ePW1){DE%fg(T|Hd9=Y(?4Z@3tXULcNVZ9kcFDW9tUV^jMbO*fjq86 zp~isXS05P6RSN9`S=l~7@gwoNBUJlPiFZ`0OjUQe0u}_0K$z4{1$4vg%%!ql23I1h zlR@#T4;;mn&^oMyGh}BEoUSEwI!gF7Ei4jI!p8{V`oLslg`N{Z@guoyZKa+UiBuN{ zX5nBU_+C#oiZ@vQyOEY5@xZqF(9)w{s(8 zZw0meI<%Ad-lSr0RI&@j-R8hP_NYWA^?|>OEA(nDO1mKNnb^x(9M~XWL13K7eskbK zKqk65@JD@FA6U(mu-|>4_|*q)zKq zFKr_=@250xtu*hc?hX^MAaDi3q_zamnl09s^?@6>5-zEqh_fItAD{#BuPEmm3#DgT zC8tpLhgcZau88R z2^49HnBxAo-)|Q9%>ut!;5Q5WW`W-<@S6piYk?MWc4!!yYa*eF?uFH5k z8!?Pbot)9k^oqh2Ris%Hu2kt&N6NE{i!UCMZJ^8I#)e9a+M4S8@Q_e> zxq;5ZhtyV9he}3;hs-Q04;d6}SYf2FTxZzK{7OU;GRy3mnoxD5EL@orDhU-ujOxO& znve<@za=
        $plainName + $content +
      +""" + # Chunk of HTML emitted for each entry in the HTML table of contents. # Available variables are: # * $desc: the actual docstring of the item. @@ -54,6 +60,14 @@ doc.item.toc = """ title="$header_plain">$name$attype """ +# This is used for TOC items which are grouped by the same name (e.g. procs). +doc.item.tocTable = """ +
    • $itemSymOrID$attype
    • +""" + + + # HTML rendered for doc.item's seeSrc variable. Note that this will render to # the empty string if you don't pass anything through --git.url. Available # substitutaion variables here are: diff --git a/doc/nimdoc.css b/doc/nimdoc.css index fb9efd858..e0c5a1de4 100644 --- a/doc/nimdoc.css +++ b/doc/nimdoc.css @@ -154,10 +154,12 @@ body { margin-left: 0; } .three.columns { - width: 19%; } + width: 22%; + line-break: anywhere; +} .nine.columns { - width: 80.0%; } + width: 77.0%; } .twelve.columns { width: 100%; @@ -420,7 +422,7 @@ ul.simple-boot li { } ol.simple > li, ul.simple > li { - margin-bottom: 0.25em; + margin-bottom: 0.2em; margin-left: 0.4em } ul.simple.simple-toc > li { @@ -439,9 +441,19 @@ ul.simple-toc > li { ul.simple-toc-section { list-style-type: circle; - margin-left: 1em; + margin-left: 0.8em; color: #6c9aae; } +ul.nested-toc-section { + list-style-type: circle; + margin-left: -0.75em; + color: var(--text); +} + +ul.nested-toc-section > li { + margin-left: 1.25em; +} + ol.arabic { list-style: decimal; } diff --git a/nimdoc/test_out_index_dot_html/expected/index.html b/nimdoc/test_out_index_dot_html/expected/index.html index f35334503..d6e318f7d 100644 --- a/nimdoc/test_out_index_dot_html/expected/index.html +++ b/nimdoc/test_out_index_dot_html/expected/index.html @@ -94,8 +94,11 @@ function main() {
    • Procs
        +
          foo
        • foo
        • + title="foo()">foo + +
    • diff --git a/nimdoc/testproject/expected/subdir/subdir_b/utils.html b/nimdoc/testproject/expected/subdir/subdir_b/utils.html index eefbcfdf9..ebbedc648 100644 --- a/nimdoc/testproject/expected/subdir/subdir_b/utils.html +++ b/nimdoc/testproject/expected/subdir/subdir_b/utils.html @@ -108,8 +108,11 @@ function main() {
    • Procs
        +
          someType
        • someTypeSomeType
        • + title="someType(): SomeType">someType_2SomeType + +
    • diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html index 479a2e89d..9c06211c9 100644 --- a/nimdoc/testproject/expected/testproject.html +++ b/nimdoc/testproject/expected/testproject.html @@ -137,74 +137,164 @@ function main() {
    • Procs
    • Funcs
        +
          someFunc
        • someFunc
        • + title="someFunc()">someFunc + +
    • From af9cc2882f2863712ddc29b2f0f36df1f26db551 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 5 Oct 2020 23:04:08 +0800 Subject: [PATCH 0717/3768] use func in uri module (#15486) --- lib/pure/uri.nim | 52 ++++++++++++++++----------------- tests/effects/tstrict_funcs.nim | 2 +- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim index 4b84a8902..3e107a6b8 100644 --- a/lib/pure/uri.nim +++ b/lib/pure/uri.nim @@ -59,7 +59,7 @@ type opaque*: bool isIpv6: bool # not expose it for compatibility. -proc encodeUrl*(s: string, usePlus = true): string = +func encodeUrl*(s: string, usePlus = true): string = ## Encodes a URL according to RFC3986. ## ## This means that characters in the set @@ -72,7 +72,7 @@ proc encodeUrl*(s: string, usePlus = true): string = ## spaces are encoded as ``+`` instead of ``%20``. ## ## **See also:** - ## * `decodeUrl proc<#decodeUrl,string>`_ + ## * `decodeUrl func<#decodeUrl,string>`_ runnableExamples: assert encodeUrl("https://nim-lang.org") == "https%3A%2F%2Fnim-lang.org" assert encodeUrl("https://nim-lang.org/this is a test") == "https%3A%2F%2Fnim-lang.org%2Fthis+is+a+test" @@ -88,7 +88,7 @@ proc encodeUrl*(s: string, usePlus = true): string = add(result, '%') add(result, toHex(ord(c), 2)) -proc decodeUrl*(s: string, decodePlus = true): string = +func decodeUrl*(s: string, decodePlus = true): string = ## Decodes a URL according to RFC3986. ## ## This means that any ``%xx`` (where ``xx`` denotes a hexadecimal @@ -100,7 +100,7 @@ proc decodeUrl*(s: string, decodePlus = true): string = ## characters are converted to a space. ## ## **See also:** - ## * `encodeUrl proc<#encodeUrl,string>`_ + ## * `encodeUrl func<#encodeUrl,string>`_ runnableExamples: assert decodeUrl("https%3A%2F%2Fnim-lang.org") == "https://nim-lang.org" assert decodeUrl("https%3A%2F%2Fnim-lang.org%2Fthis+is+a+test") == "https://nim-lang.org/this is a test" @@ -125,7 +125,7 @@ proc decodeUrl*(s: string, decodePlus = true): string = inc(j) setLen(result, j) -proc encodeQuery*(query: openArray[(string, string)], usePlus = true, +func encodeQuery*(query: openArray[(string, string)], usePlus = true, omitEq = true): string = ## Encodes a set of (key, value) parameters into a URL query string. ## @@ -138,7 +138,7 @@ proc encodeQuery*(query: openArray[(string, string)], usePlus = true, ## is used for the URL encoding of the string values. ## ## **See also:** - ## * `encodeUrl proc<#encodeUrl,string>`_ + ## * `encodeUrl func<#encodeUrl,string>`_ runnableExamples: assert encodeQuery({: }) == "" assert encodeQuery({"a": "1", "b": "2"}) == "a=1&b=2" @@ -153,7 +153,7 @@ proc encodeQuery*(query: openArray[(string, string)], usePlus = true, result.add('=') result.add(encodeUrl(val, usePlus)) -proc parseAuthority(authority: string, result: var Uri) = +func parseAuthority(authority: string, result: var Uri) = var i = 0 var inPort = false var inIPv6 = false @@ -182,7 +182,7 @@ proc parseAuthority(authority: string, result: var Uri) = result.hostname.add(authority[i]) i.inc -proc parsePath(uri: string, i: var int, result: var Uri) = +func parsePath(uri: string, i: var int, result: var Uri) = i.inc parseUntil(uri, result.path, {'?', '#'}, i) @@ -199,7 +199,7 @@ proc parsePath(uri: string, i: var int, result: var Uri) = i.inc # Skip '#' i.inc parseUntil(uri, result.anchor, {}, i) -proc initUri*(): Uri = +func initUri*(): Uri = ## Initializes a URI with ``scheme``, ``username``, ``password``, ## ``hostname``, ``port``, ``path``, ``query`` and ``anchor``. ## @@ -211,7 +211,7 @@ proc initUri*(): Uri = result = Uri(scheme: "", username: "", password: "", hostname: "", port: "", path: "", query: "", anchor: "") -proc initUri*(isIpv6: bool): Uri {.since: (1, 3, 5).} = +func initUri*(isIpv6: bool): Uri {.since: (1, 3, 5).} = ## Initializes a URI with ``scheme``, ``username``, ``password``, ## ``hostname``, ``port``, ``path``, ``query``, ``anchor`` and ``isIpv6``. ## @@ -226,19 +226,19 @@ proc initUri*(isIpv6: bool): Uri {.since: (1, 3, 5).} = result = Uri(scheme: "", username: "", password: "", hostname: "", port: "", path: "", query: "", anchor: "", isIpv6: isIpv6) -proc resetUri(uri: var Uri) = +func resetUri(uri: var Uri) = for f in uri.fields: when f is string: f.setLen(0) else: f = false -proc parseUri*(uri: string, result: var Uri) = +func parseUri*(uri: string, result: var Uri) = ## Parses a URI. The `result` variable will be cleared before. ## ## **See also:** ## * `Uri type <#Uri>`_ for available fields in the URI type - ## * `initUri proc <#initUri>`_ for initializing a URI + ## * `initUri func <#initUri>`_ for initializing a URI runnableExamples: var res = initUri() parseUri("https://nim-lang.org/docs/manual.html", res) @@ -281,7 +281,7 @@ proc parseUri*(uri: string, result: var Uri) = # Path parsePath(uri, i, result) -proc parseUri*(uri: string): Uri = +func parseUri*(uri: string): Uri = ## Parses a URI and returns it. ## ## **See also:** @@ -294,7 +294,7 @@ proc parseUri*(uri: string): Uri = result = initUri() parseUri(uri, result) -proc removeDotSegments(path: string): string = +func removeDotSegments(path: string): string = if path.len == 0: return "" var collection: seq[string] = @[] let endsWithSlash = path[path.len-1] == '/' @@ -324,7 +324,7 @@ proc removeDotSegments(path: string): string = result = collection.join("/") if endsWithSlash: result.add '/' -proc merge(base, reference: Uri): string = +func merge(base, reference: Uri): string = # http://tools.ietf.org/html/rfc3986#section-5.2.3 if base.hostname != "" and base.path == "": '/' & reference.path @@ -335,7 +335,7 @@ proc merge(base, reference: Uri): string = else: base.path[0 .. lastSegment] & reference.path -proc combine*(base: Uri, reference: Uri): Uri = +func combine*(base: Uri, reference: Uri): Uri = ## Combines a base URI with a reference URI. ## ## This uses the algorithm specified in @@ -345,7 +345,7 @@ proc combine*(base: Uri, reference: Uri): Uri = ## URIs path affect the resulting URI. ## ## **See also:** - ## * `/ proc <#/,Uri,string>`_ for building URIs + ## * `/ func <#/,Uri,string>`_ for building URIs runnableExamples: let foo = combine(parseUri("https://nim-lang.org/foo/bar"), parseUri("/baz")) assert foo.path == "/baz" @@ -386,11 +386,11 @@ proc combine*(base: Uri, reference: Uri): Uri = result.scheme = base.scheme result.anchor = reference.anchor -proc combine*(uris: varargs[Uri]): Uri = +func combine*(uris: varargs[Uri]): Uri = ## Combines multiple URIs together. ## ## **See also:** - ## * `/ proc <#/,Uri,string>`_ for building URIs + ## * `/ func <#/,Uri,string>`_ for building URIs runnableExamples: let foo = combine(parseUri("https://nim-lang.org/"), parseUri("docs/"), parseUri("manual.html")) @@ -400,7 +400,7 @@ proc combine*(uris: varargs[Uri]): Uri = for i in 1 ..< uris.len: result = combine(result, uris[i]) -proc isAbsolute*(uri: Uri): bool = +func isAbsolute*(uri: Uri): bool = ## Returns true if URI is absolute, false otherwise. runnableExamples: let foo = parseUri("https://nim-lang.org") @@ -409,15 +409,15 @@ proc isAbsolute*(uri: Uri): bool = assert isAbsolute(bar) == false return uri.scheme != "" and (uri.hostname != "" or uri.path != "") -proc `/`*(x: Uri, path: string): Uri = +func `/`*(x: Uri, path: string): Uri = ## Concatenates the path specified to the specified URIs path. ## - ## Contrary to the `combine proc <#combine,Uri,Uri>`_ you do not have to worry about + ## Contrary to the `combine func <#combine,Uri,Uri>`_ you do not have to worry about ## the slashes at the beginning and end of the path and URIs path ## respectively. ## ## **See also:** - ## * `combine proc <#combine,Uri,Uri>`_ + ## * `combine func <#combine,Uri,Uri>`_ runnableExamples: let foo = parseUri("https://nim-lang.org/foo/bar") / "/baz" assert foo.path == "/foo/bar/baz" @@ -443,7 +443,7 @@ proc `/`*(x: Uri, path: string): Uri = result.path.add '/' result.path.add(path) -proc `?`*(u: Uri, query: openArray[(string, string)]): Uri = +func `?`*(u: Uri, query: openArray[(string, string)]): Uri = ## Concatenates the query parameters to the specified URI object. runnableExamples: let foo = parseUri("https://example.com") / "foo" ? {"bar": "qux"} @@ -451,7 +451,7 @@ proc `?`*(u: Uri, query: openArray[(string, string)]): Uri = result = u result.query = encodeQuery(query) -proc `$`*(u: Uri): string = +func `$`*(u: Uri): string = ## Returns the string representation of the specified URI object. runnableExamples: let foo = parseUri("https://nim-lang.org") diff --git a/tests/effects/tstrict_funcs.nim b/tests/effects/tstrict_funcs.nim index 38f5b7601..166ab77c1 100644 --- a/tests/effects/tstrict_funcs.nim +++ b/tests/effects/tstrict_funcs.nim @@ -2,7 +2,7 @@ discard """ cmd: "nim c --experimental:strictFuncs --experimental:views $file" """ -import tables, streams, nre, parsecsv +import tables, streams, nre, parsecsv, uri type Contig2Reads = TableRef[string, seq[string]] From f25ca0d3043c67a73b9eb78c36f910daee631a18 Mon Sep 17 00:00:00 2001 From: David Krause Date: Mon, 5 Oct 2020 17:04:47 +0200 Subject: [PATCH 0718/3768] added testament documentation link to tools.rst (#15481) --- doc/tools.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/tools.rst b/doc/tools.rst index 66cbdca26..df4d2e99f 100644 --- a/doc/tools.rst +++ b/doc/tools.rst @@ -30,7 +30,7 @@ The standard distribution ships with the following tools: | ``nimpretty`` is a Nim source code beautifier, to format code according to the official style guide. -- | testament +- | `testament `_ | ``testament`` is an advanced automatic *unittests runner* for Nim tests, is used for the development of Nim itself, offers process isolation for your tests, it can generate statistics about test cases, supports multiple targets (C, JS, etc), From 1e28cea0d102afd43a62f6f7fcabd65cd8996762 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Mon, 5 Oct 2020 12:08:50 -0300 Subject: [PATCH 0719/3768] inline tiny func on httpcore (#15480) --- lib/pure/httpcore.nim | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) diff --git a/lib/pure/httpcore.nim b/lib/pure/httpcore.nim index e7da77ae8..af1bfc088 100644 --- a/lib/pure/httpcore.nim +++ b/lib/pure/httpcore.nim @@ -113,7 +113,7 @@ func toCaseInsensitive(headers: HttpHeaders, s: string): string {.inline.} = return if headers.isTitleCase: toTitleCase(s) else: toLowerAscii(s) func newHttpHeaders*(titleCase=false): HttpHeaders = - ## Returns a new ``HttpHeaders`` object. if ``titleCase`` is set to true, + ## Returns a new ``HttpHeaders`` object. if ``titleCase`` is set to true, ## headers are passed to the server in title case (e.g. "Content-Length") new result result.table = newTable[string, seq[string]]() @@ -121,7 +121,7 @@ func newHttpHeaders*(titleCase=false): HttpHeaders = func newHttpHeaders*(keyValuePairs: openArray[tuple[key: string, val: string]], titleCase=false): HttpHeaders = - ## Returns a new ``HttpHeaders`` object from an array. if ``titleCase`` is set to true, + ## Returns a new ``HttpHeaders`` object from an array. if ``titleCase`` is set to true, ## headers are passed to the server in title case (e.g. "Content-Length") new result result.table = newTable[string, seq[string]]() @@ -134,10 +134,10 @@ func newHttpHeaders*(keyValuePairs: result.table[key] = @[pair.val] -func `$`*(headers: HttpHeaders): string = - return $headers.table +func `$`*(headers: HttpHeaders): string {.inline.} = + $headers.table -proc clear*(headers: HttpHeaders) = +proc clear*(headers: HttpHeaders) {.inline.} = headers.table.clear() func `[]`*(headers: HttpHeaders, key: string): HttpHeaderValues = @@ -206,7 +206,7 @@ func getOrDefault*(headers: HttpHeaders, key: string, else: return default -func len*(headers: HttpHeaders): int = return headers.table.len +func len*(headers: HttpHeaders): int {.inline.} = headers.table.len func parseList(line: string, list: var seq[string], start: int): int = var i = 0 @@ -323,21 +323,21 @@ proc `==`*(rawCode: string, code: HttpCode): bool ## string form of itself. return cmpIgnoreCase(rawCode, $code) == 0 -func is2xx*(code: HttpCode): bool = +func is2xx*(code: HttpCode): bool {.inline.} = ## Determines whether ``code`` is a 2xx HTTP status code. - return code.int in {200 .. 299} + code.int in {200 .. 299} -func is3xx*(code: HttpCode): bool = +func is3xx*(code: HttpCode): bool {.inline.} = ## Determines whether ``code`` is a 3xx HTTP status code. - return code.int in {300 .. 399} + code.int in {300 .. 399} -func is4xx*(code: HttpCode): bool = +func is4xx*(code: HttpCode): bool {.inline.} = ## Determines whether ``code`` is a 4xx HTTP status code. - return code.int in {400 .. 499} + code.int in {400 .. 499} -func is5xx*(code: HttpCode): bool = +func is5xx*(code: HttpCode): bool {.inline.} = ## Determines whether ``code`` is a 5xx HTTP status code. - return code.int in {500 .. 599} + code.int in {500 .. 599} func `$`*(httpMethod: HttpMethod): string = return (system.`$`(httpMethod))[4 .. ^1].toUpperAscii() @@ -366,5 +366,3 @@ when isMainModule: doAssert testTitleCase.hasKey("Content-Length") for key, val in testTitleCase: doAssert key == "Content-Length" - - From 4e438f9096fc413824bdbe8377a4ca376d63be35 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 5 Oct 2020 18:31:46 +0200 Subject: [PATCH 0720/3768] const view types; fixes some cases from https://github.com/nim-lang/Nim/issues/15428 (#15488) --- compiler/astalgo.nim | 9 +++- compiler/ccgexprs.nim | 95 ++++++++++++++++++++---------------- compiler/ccgstmts.nim | 4 +- compiler/cgen.nim | 3 +- compiler/typeallowed.nim | 2 +- compiler/vmgen.nim | 2 +- tests/views/tconst_views.nim | 26 ++++++++++ 7 files changed, 94 insertions(+), 47 deletions(-) create mode 100644 tests/views/tconst_views.nim diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 5e23d2284..6aecb1dcb 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -591,10 +591,17 @@ proc value(this: var DebugPrinter; value: PNode) = if this.conf != nil: this.key "info" this.value $lineInfoToStr(this.conf, value.info) - if card(value.flags) > 0: + if value.flags != {}: this.key "flags" this.value value.flags + if value.typ != nil: + this.key "typ" + this.value value.typ.kind + else: + this.key "typ" + this.value "nil" + case value.kind of nkCharLit..nkUInt64Lit: this.key "intVal" diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index b58519614..364bcc143 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1400,7 +1400,7 @@ proc rawConstExpr(p: BProc, n: PNode; d: var TLoc) = # expression not found in the cache: inc(p.module.labels) p.module.s[cfsData].addf("static NIM_CONST $1 $2 = $3;$n", - [getTypeDesc(p.module, t), d.r, genBracedInit(p, n, isConst = true)]) + [getTypeDesc(p.module, t), d.r, genBracedInit(p, n, isConst = true, t)]) proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool = if d.k == locNone and n.len > ord(n.kind == nkObjConstr) and n.isDeepConstExpr: @@ -2485,7 +2485,7 @@ proc genClosure(p: BProc, n: PNode, d: var TLoc) = inc(p.module.labels) var tmp = "CNSTCLOSURE" & rope(p.module.labels) p.module.s[cfsData].addf("static NIM_CONST $1 $2 = $3;$n", - [getTypeDesc(p.module, n.typ), tmp, genBracedInit(p, n, isConst = true)]) + [getTypeDesc(p.module, n.typ), tmp, genBracedInit(p, n, isConst = true, n.typ)]) putIntoDest(p, d, n, tmp, OnStatic) else: var tmp, a, b: TLoc @@ -2624,7 +2624,7 @@ proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) = # expression not found in the cache: inc(p.module.labels) p.module.s[cfsData].addf("static NIM_CONST $1 $2 = $3;$n", - [getTypeDesc(p.module, t), tmp, genBracedInit(p, n, isConst = true)]) + [getTypeDesc(p.module, t, skConst), tmp, genBracedInit(p, n, isConst = true, t)]) if d.k == locNone: fillLoc(d, locData, n, tmp, OnStatic) @@ -2847,8 +2847,8 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = else: internalError(p.config, n.info, "expr(" & $n.kind & "); unknown node kind") proc genNamedConstExpr(p: BProc, n: PNode; isConst: bool): Rope = - if n.kind == nkExprColonExpr: result = genBracedInit(p, n[1], isConst) - else: result = genBracedInit(p, n, isConst) + if n.kind == nkExprColonExpr: result = genBracedInit(p, n[1], isConst, n[0].typ) + else: result = genBracedInit(p, n, isConst, n.typ) proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo): Rope = var t = skipTypes(typ, abstractRange+{tyOwned}-{tyTypeDesc}) @@ -2955,10 +2955,10 @@ proc getNullValueAux(p: BProc; t: PType; obj, constOrNil: PNode, for i in 1.. 0: - result.add(genNamedConstExpr(p, n[^1], isConst)) - result.addf("}$n", []) + for i in 0.. 0: result.add ",\n" + if it.kind == nkExprColonExpr: result.add genBracedInit(p, it[1], isConst, it[0].typ) + else: result.add genBracedInit(p, it, isConst, it.typ) + result.add("}\n") + +proc genConstTuple(p: BProc, n: PNode; isConst: bool; tup: PType): Rope = + result = rope("{") + for i in 0.. 0: result.add ",\n" + if it.kind == nkExprColonExpr: result.add genBracedInit(p, it[1], isConst, tup[i]) + else: result.add genBracedInit(p, it, isConst, tup[i]) + result.add("}\n") proc genConstSeq(p: BProc, n: PNode, t: PType; isConst: bool): Rope = var data = "{{$1, $1 | NIM_STRLIT_FLAG}" % [n.len.rope] + let base = t.skipTypes(abstractInst)[0] if n.len > 0: # array part needs extra curlies: data.add(", {") for i in 0.. 0: data.addf(",$n", []) - data.add genBracedInit(p, n[i], isConst) + data.add genBracedInit(p, n[i], isConst, base) data.add("}") data.add("}") result = getTempName(p.module) - let base = t.skipTypes(abstractInst)[0] appcg(p.module, cfsData, "static $5 struct {$n" & @@ -3030,14 +3040,14 @@ proc genConstSeq(p: BProc, n: PNode, t: PType; isConst: bool): Rope = result = "(($1)&$2)" % [getTypeDesc(p.module, t), result] proc genConstSeqV2(p: BProc, n: PNode, t: PType; isConst: bool): Rope = + let base = t.skipTypes(abstractInst)[0] var data = rope"{" for i in 0.. 0: data.addf(",$n", []) - data.add genBracedInit(p, n[i], isConst) + data.add genBracedInit(p, n[i], isConst, base) data.add("}") let payload = getTempName(p.module) - let base = t.skipTypes(abstractInst)[0] appcg(p.module, cfsData, "static $5 struct {$n" & @@ -3047,46 +3057,32 @@ proc genConstSeqV2(p: BProc, n: PNode, t: PType; isConst: bool): Rope = if isConst: "const" else: ""]) result = "{$1, ($2*)&$3}" % [rope(n.len), getSeqPayloadType(p.module, t), payload] -proc genBracedInit(p: BProc, n: PNode; isConst: bool): Rope = +proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType): Rope = case n.kind of nkHiddenStdConv, nkHiddenSubConv: - when false: - # XXX The frontend doesn't keep conversions to openArray for us. :-( - # We need to change 'transformConv' first, but that is hard. - if n.typ.kind == tyOpenArray: - assert n[1].kind == nkBracket - let data = genBracedInit(p, n[1], isConst) - - let payload = getTempName(p.module) - let ctype = getTypeDesc(p.module, n.typ.skipTypes(abstractInst)[0]) - let arrLen = n[1].len - appcg(p.module, cfsData, - "static $5 $1 $3[$2] = $4;$n", [ - ctype, arrLen, payload, data, - if isConst: "const" else: ""]) - result = "{($1*)&$2, $3}" % [ctype, payload, rope arrLen] - else: - result = genBracedInit(p, n[1], isConst) + result = genBracedInit(p, n[1], isConst, n.typ) else: var ty = tyNone - if n.typ == nil: + var typ: PType = nil + if optionalType == nil: if n.kind in nkStrKinds: ty = tyString else: internalError(p.config, n.info, "node has no type") else: - ty = skipTypes(n.typ, abstractInstOwned + {tyStatic}).kind + typ = skipTypes(optionalType, abstractInstOwned + {tyStatic}) + ty = typ.kind case ty of tySet: let cs = toBitSet(p.config, n) result = genRawSetData(cs, int(getSize(p.config, n.typ))) of tySequence: if optSeqDestructors in p.config.globalOptions: - result = genConstSeqV2(p, n, n.typ, isConst) + result = genConstSeqV2(p, n, typ, isConst) else: - result = genConstSeq(p, n, n.typ, isConst) + result = genConstSeq(p, n, typ, isConst) of tyProc: - if n.typ.callConv == ccClosure and n.len > 1 and n[1].kind == nkNilLit: + if typ.callConv == ccClosure and n.len > 1 and n[1].kind == nkNilLit: # Conversion: nimcall -> closure. # this hack fixes issue that nkNilLit is expanded to {NIM_NIL,NIM_NIL} # this behaviour is needed since closure_var = nil must be @@ -3099,13 +3095,30 @@ proc genBracedInit(p: BProc, n: PNode; isConst: bool): Rope = else: var d: TLoc initLocExpr(p, n[0], d) - result = "{(($1) $2),NIM_NIL}" % [getClosureType(p.module, n.typ, clHalfWithEnv), rdLoc(d)] + result = "{(($1) $2),NIM_NIL}" % [getClosureType(p.module, typ, clHalfWithEnv), rdLoc(d)] else: var d: TLoc initLocExpr(p, n, d) result = rdLoc(d) - of tyArray, tyTuple, tyOpenArray, tyVarargs: + of tyArray, tyVarargs: result = genConstSimpleList(p, n, isConst) + of tyTuple: + result = genConstTuple(p, n, isConst, typ) + of tyOpenArray: + if n.kind != nkBracket: + internalError(p.config, n.info, "const openArray expression is not an array construction") + + let data = genConstSimpleList(p, n, isConst) + + let payload = getTempName(p.module) + let ctype = getTypeDesc(p.module, typ[0]) + let arrLen = n.len + appcg(p.module, cfsData, + "static $5 $1 $3[$2] = $4;$n", [ + ctype, arrLen, payload, data, + if isConst: "const" else: ""]) + result = "{($1*)&$2, $3}" % [ctype, payload, rope arrLen] + of tyObject: result = genConstObjConstr(p, n, isConst) of tyString, tyCString: diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 5bed7cf32..1282cf11f 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -272,7 +272,7 @@ proc genGotoVar(p: BProc; value: PNode) = else: lineF(p, cpsStmts, "goto NIMSTATE_$#;$n", [value.intVal.rope]) -proc genBracedInit(p: BProc, n: PNode; isConst: bool): Rope +proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType): Rope proc potentialValueInit(p: BProc; v: PSym; value: PNode): Rope = if lfDynamicLib in v.loc.flags or sfThread in v.flags or p.hcrOn: @@ -280,7 +280,7 @@ proc potentialValueInit(p: BProc; v: PSym; value: PNode): Rope = elif sfGlobal in v.flags and value != nil and isDeepConstExpr(value, p.module.compileToCpp) and p.withinLoop == 0 and not containsGarbageCollectedRef(v.typ): #echo "New code produced for ", v.name.s, " ", p.config $ value.info - result = genBracedInit(p, value, isConst = false) + result = genBracedInit(p, value, isConst = false, v.typ) else: result = nil diff --git a/compiler/cgen.nim b/compiler/cgen.nim index f50676647..9a50b718c 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -1195,7 +1195,8 @@ proc requestConstImpl(p: BProc, sym: PSym) = # add a suffix for hcr - will later init the global pointer with this data let actualConstName = if m.hcrOn: sym.loc.r & "_const" else: sym.loc.r q.s[cfsData].addf("N_LIB_PRIVATE NIM_CONST $1 $2 = $3;$n", - [getTypeDesc(q, sym.typ), actualConstName, genBracedInit(q.initProc, sym.ast, isConst = true)]) + [getTypeDesc(q, sym.typ), actualConstName, + genBracedInit(q.initProc, sym.ast, isConst = true, sym.typ)]) if m.hcrOn: # generate the global pointer with the real name q.s[cfsVars].addf("static $1* $2;$n", [getTypeDesc(m, sym.loc.t, skVar), sym.loc.r]) diff --git a/compiler/typeallowed.nim b/compiler/typeallowed.nim index 13eff35be..a8210508c 100644 --- a/compiler/typeallowed.nim +++ b/compiler/typeallowed.nim @@ -217,7 +217,7 @@ proc classifyViewTypeAux(marker: var IntSet, t: PType): ViewTypeKind = case t.kind of tyVar: result = mutableView - of tyLent, tyVarargs, tyOpenArray: + of tyLent, tyOpenArray: result = immutableView of tyGenericInst, tyDistinct, tyAlias, tyInferred, tySink, tyOwned, tyUncheckedArray, tySequence, tyArray, tyRef, tyStatic: diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index ad5023aeb..6f81c6a9c 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1807,7 +1807,7 @@ proc getNullValue(typ: PType, info: TLineInfo; conf: ConfigRef): PNode = result.add getNullValue(t[i], info, conf) of tySet: result = newNodeIT(nkCurly, info, t) - of tySequence: + of tySequence, tyOpenArray: result = newNodeIT(nkBracket, info, t) else: globalError(conf, info, "cannot create null element for: " & $t.kind) diff --git a/tests/views/tconst_views.nim b/tests/views/tconst_views.nim new file mode 100644 index 000000000..d7f1fc481 --- /dev/null +++ b/tests/views/tconst_views.nim @@ -0,0 +1,26 @@ +discard """ + cmd: "nim c --experimental:views $file" + output: '''(data: [1, 2, 3], other: 4) +[1, 20, 3]''' +""" + +type + Foo = object + data: openArray[int] + other: int + +const + c = Foo(data: [1, 2, 3], other: 4) + + c2 = Foo(data: [1, 20, 3], other: 4) + +proc `$`(x: openArray[int]): string = + result = "[" + for i in x: + if result.len > 1: result.add ", " + result.add $i + result.add "]" + +echo c +echo c2.data + From e9d7e21dbef65bb8bab88b7cd326e15a2d407fca Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 6 Oct 2020 06:10:47 +0200 Subject: [PATCH 0721/3768] minor reformating --- compiler/main.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/main.nim b/compiler/main.nim index 774dd7625..ab074aa1f 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -249,7 +249,7 @@ proc mainCommand*(graph: ModuleGraph) = # so by default should not end up in $PWD nor in $projectPath. conf.outDir = block: var ret = if optUseNimcache in conf.globalOptions: getNimcacheDir(conf) - else: conf.projectPath + else: conf.projectPath doAssert ret.string.isAbsolute # `AbsoluteDir` is not a real guarantee if docLikeCmd2: ret = ret / htmldocsDir ret From 37758826ff64fb93324675d81ef724e04c474b39 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 6 Oct 2020 06:33:43 +0200 Subject: [PATCH 0722/3768] typo --- lib/pure/osproc.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 2d48597dc..a7c6c3754 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -80,7 +80,7 @@ proc execProcess*(command: string, workingDir: string = "", ## A convenience procedure that executes ``command`` with ``startProcess`` ## and returns its output as a string. ## - ## **WARNING:** This function uses `poEvalCommand` by default for backward + ## **WARNING:** This function uses `poEvalCommand` by default for backwards ## compatibility. ## Make sure to pass options explicitly. ## From aca1fae55a8009627888944afeb69f0a0141c160 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Tue, 6 Oct 2020 01:52:49 -0300 Subject: [PATCH 0723/3768] GitHub Actions Skip CI (#15289) --- .github/workflows/ci_docs.yml | 2 ++ .github/workflows/ci_packages.yml | 2 ++ .github/workflows/ci_ssl.yml | 2 ++ 3 files changed, 6 insertions(+) diff --git a/.github/workflows/ci_docs.yml b/.github/workflows/ci_docs.yml index d3d0ba612..6b21475f6 100644 --- a/.github/workflows/ci_docs.yml +++ b/.github/workflows/ci_docs.yml @@ -28,6 +28,8 @@ on: jobs: build: + if: | + !contains(format('{0} {1} {2}', github.event.head_commit.message, github.event.pull_request.title), '[skip ci]') strategy: fail-fast: false matrix: diff --git a/.github/workflows/ci_packages.yml b/.github/workflows/ci_packages.yml index 8a1a8a81f..a59bcee9d 100644 --- a/.github/workflows/ci_packages.yml +++ b/.github/workflows/ci_packages.yml @@ -3,6 +3,8 @@ on: [push, pull_request] jobs: build: + if: | + !contains(format('{0} {1} {2}', github.event.head_commit.message, github.event.pull_request.title), '[skip ci]') strategy: fail-fast: false matrix: diff --git a/.github/workflows/ci_ssl.yml b/.github/workflows/ci_ssl.yml index fe5faded6..070e4aa1e 100644 --- a/.github/workflows/ci_ssl.yml +++ b/.github/workflows/ci_ssl.yml @@ -12,6 +12,8 @@ on: jobs: build: + if: | + !contains(format('{0} {1} {2}', github.event.head_commit.message, github.event.pull_request.title), '[skip ci]') strategy: fail-fast: false matrix: From acd71dd6bb745eb08f81ab489d635951f8edfcfa Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Tue, 6 Oct 2020 04:29:45 -0400 Subject: [PATCH 0724/3768] Iterate over smaller set when computing intersection (#15497) Closes #15496 --- lib/pure/collections/sets.nim | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim index 67e407576..98dedc5ff 100644 --- a/lib/pure/collections/sets.nim +++ b/lib/pure/collections/sets.nim @@ -422,8 +422,15 @@ proc intersection*[A](s1, s2: HashSet[A]): HashSet[A] = assert c == toHashSet(["b"]) result = initHashSet[A](max(min(s1.data.len, s2.data.len), 2)) - for item in s1: - if item in s2: incl(result, item) + + # iterate over the elements of the smaller set + if s1.data.len < s2.data.len: + for item in s1: + if item in s2: incl(result, item) + else: + for item in s2: + if item in s1: incl(result, item) + proc difference*[A](s1, s2: HashSet[A]): HashSet[A] = ## Returns the difference of the sets `s1` and `s2`. From 92163fa3304e5b6768a50d36a5243639ce4a2f69 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 6 Oct 2020 16:47:15 +0200 Subject: [PATCH 0725/3768] implements https://github.com/nim-lang/RFCs/issues/258 (#15503) * implements https://github.com/nim-lang/RFCs/issues/258 * don't be too strict with custom pragma blocks * cast pragmas: documentation * added most missing inference query procs to effecttraits.nim --- changelog.md | 5 +- compiler/condsyms.nim | 2 + compiler/parser.nim | 25 ++++--- compiler/pragmas.nim | 9 +++ compiler/renderer.nim | 7 +- compiler/sempass2.nim | 108 +++++++++++++++++++++++------- compiler/semstmts.nim | 2 +- compiler/transf.nim | 1 + compiler/trees.nim | 14 +++- compiler/varpartitions.nim | 5 +- compiler/vmops.nim | 20 +++++- doc/grammar.txt | 22 +++--- doc/manual.rst | 8 +-- lib/std/effecttraits.nim | 43 ++++++++++-- tests/effects/tcast_as_pragma.nim | 18 +++++ tests/macros/tgetraiseslist.nim | 11 ++- 16 files changed, 232 insertions(+), 68 deletions(-) create mode 100644 tests/effects/tcast_as_pragma.nim diff --git a/changelog.md b/changelog.md index e905dd941..3144c7cf5 100644 --- a/changelog.md +++ b/changelog.md @@ -304,10 +304,13 @@ proc mydiv(a, b): int {.raises: [].} = - `system.deepcopy` has to be enabled explicitly for `--gc:arc` and `--gc:orc` via `--deepcopy:on`. -- Added a `std/effecttraits` module for introspection of the inferred `.raise` effects. +- Added a `std/effecttraits` module for introspection of the inferred effects. We hope this enables `async` macros that are precise about the possible exceptions that can be raised. - Added `critbits.toCritBitTree`, similar to `tables.toTable`, creates a new `CritBitTree` with given arguments. +- The pragma blocks `{.gcsafe.}: ...` and `{.noSideEffect.}: ...` can now also be + written as `{.cast(gcsafe).}: ...` and `{.cast(noSideEffect).}: ...`. This is the new + preferred way of writing these, emphasizing their unsafe nature. ## Compiler changes diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 7dc1d183e..9771a48d3 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -121,3 +121,5 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasLentIterators") defineSymbol("nimHasDeclaredMagic") defineSymbol("nimHasStacktracesModule") + defineSymbol("nimHasEffectTraitsModule") + defineSymbol("nimHasCastPragmaBlocks") diff --git a/compiler/parser.nim b/compiler/parser.nim index 1ec708057..e3cf54b38 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -485,17 +485,24 @@ proc setOrTableConstr(p: var Parser): PNode = eat(p, tkCurlyRi) # skip '}' proc parseCast(p: var Parser): PNode = - #| castExpr = 'cast' '[' optInd typeDesc optPar ']' '(' optInd expr optPar ')' + #| castExpr = 'cast' ('[' optInd typeDesc optPar ']' '(' optInd expr optPar ')') / + # ('(' optInd exprColonEqExpr optPar ')') result = newNodeP(nkCast, p) getTok(p) - eat(p, tkBracketLe) - optInd(p, result) - result.add(parseTypeDesc(p)) - optPar(p) - eat(p, tkBracketRi) - eat(p, tkParLe) - optInd(p, result) - result.add(parseExpr(p)) + if p.tok.tokType == tkBracketLe: + getTok(p) + optInd(p, result) + result.add(parseTypeDesc(p)) + optPar(p) + eat(p, tkBracketRi) + eat(p, tkParLe) + optInd(p, result) + result.add(parseExpr(p)) + else: + result.add p.emptyNode + eat(p, tkParLe) + optInd(p, result) + result.add(exprColonEqExpr(p)) optPar(p) eat(p, tkParRi) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 67e574d52..fe8cd3cfe 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -771,6 +771,15 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, if key.kind == nkBracketExpr: processNote(c, it) return + elif key.kind == nkCast: + if comesFromPush: + localError(c.config, n.info, "a 'cast' pragma cannot be pushed") + elif not isStatement: + localError(c.config, n.info, "'cast' pragma only allowed in a statement context") + case whichPragma(key[1]) + of wRaises, wTags: pragmaRaisesOrTags(c, key[1]) + else: discard + return elif key.kind notin nkIdentKinds: n[i] = semCustomPragma(c, it) return diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 88b9adf27..08012bb55 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -1057,9 +1057,10 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = put(g, tkSymbol, "(wrong conv)") of nkCast: put(g, tkCast, "cast") - put(g, tkBracketLe, "[") - gsub(g, n, 0) - put(g, tkBracketRi, "]") + if n.len > 0 and n[0].kind != nkEmpty: + put(g, tkBracketLe, "[") + gsub(g, n, 0) + put(g, tkBracketRi, "]") put(g, tkParLe, "(") gsub(g, n, 1) put(g, tkParRi, ")") diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index cda156224..19223b88f 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -325,7 +325,7 @@ proc createTag(g: ModuleGraph; n: PNode): PNode = result.typ = g.sysTypeFromName(n.info, "RootEffect") if not n.isNil: result.info = n.info -proc addEffect(a: PEffects, e, comesFrom: PNode) = +proc addRaiseEffect(a: PEffects, e, comesFrom: PNode) = assert e.kind != nkRaiseStmt var aa = a.exc for i in a.bottom.. 0: n[0] else: n - if key.kind == nkIdent: result = whichKeyword(key.ident) + case key.kind + of nkIdent: result = whichKeyword(key.ident) + of nkSym: result = whichKeyword(key.sym.name) + of nkCast: result = wCast + of nkClosedSymChoice, nkOpenSymChoice: + result = whichPragma(key[0]) + else: result = wInvalid + +proc isNoSideEffectPragma*(n: PNode): bool = + var k = whichPragma(n) + if k == wCast: + k = whichPragma(n[1]) + result = k == wNoSideEffect proc findPragma*(n: PNode, which: TSpecialWord): PNode = if n.kind == nkPragma: diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim index e59c5cb75..a172a906e 100644 --- a/compiler/varpartitions.nim +++ b/compiler/varpartitions.nim @@ -29,8 +29,7 @@ ## for a high-level description of how borrow checking works. import ast, types, lineinfos, options, msgs, renderer, typeallowed -from trees import getMagic, whichPragma, stupidStmtListExpr -from wordrecg import wNoSideEffect +from trees import getMagic, isNoSideEffectPragma, stupidStmtListExpr from isolation_check import canAlias type @@ -713,7 +712,7 @@ proc traverse(c: var Partitions; n: PNode) = let pragmaList = n[0] var enforceNoSideEffects = 0 for i in 0..= effectListLen and - fn.typ.n[0][exceptionEffects] != nil: + fn.typ.n[0][effectIndex] != nil: var list = newNodeI(nkBracket, fn.info) - for e in fn.typ.n[0][exceptionEffects]: + for e in fn.typ.n[0][effectIndex]: list.add opMapTypeInstToAst(c.cache, e.typ.skipTypes({tyRef}), e.info) setResult(a, list) + + registerCallback c, "stdlib.effecttraits.getRaisesListImpl", proc (a: VmArgs) = + getEffectList(c, a, exceptionEffects) + registerCallback c, "stdlib.effecttraits.getTagsListImpl", proc (a: VmArgs) = + getEffectList(c, a, tagEffects) + + registerCallback c, "stdlib.effecttraits.isGcSafeImpl", proc (a: VmArgs) = + let fn = getNode(a, 0) + setResult(a, fn.typ != nil and tfGcSafe in fn.typ.flags) + + registerCallback c, "stdlib.effecttraits.hasNoSideEffectsImpl", proc (a: VmArgs) = + let fn = getNode(a, 0) + setResult(a, (fn.typ != nil and tfNoSideEffect in fn.typ.flags) or + (fn.kind == nkSym and fn.sym.kind == skFunc)) diff --git a/doc/grammar.txt b/doc/grammar.txt index 9d952d372..f5e619ebc 100644 --- a/doc/grammar.txt +++ b/doc/grammar.txt @@ -6,7 +6,7 @@ colon = ':' COMMENT? colcom = ':' COMMENT? operator = OP0 | OP1 | OP2 | OP3 | OP4 | OP5 | OP6 | OP7 | OP8 | OP9 | 'or' | 'xor' | 'and' - | 'is' | 'isnot' | 'in' | 'notin' | 'of' | 'as' | 'from' | + | 'is' | 'isnot' | 'in' | 'notin' | 'of' | 'as' | 'from' | 'div' | 'mod' | 'shl' | 'shr' | 'not' | 'static' | '..' prefixOperator = operator optInd = COMMENT? IND? @@ -31,15 +31,15 @@ dotExpr = expr '.' optInd (symbol | '[:' exprList ']') explicitGenericInstantiation = '[:' exprList ']' ( '(' exprColonEqExpr ')' )? qualifiedIdent = symbol ('.' optInd symbol)? setOrTableConstr = '{' ((exprColonEqExpr comma)* | ':' ) '}' -castExpr = 'cast' '[' optInd typeDesc optPar ']' '(' optInd expr optPar ')' +castExpr = 'cast' ('[' optInd typeDesc optPar ']' '(' optInd expr optPar ')') / parKeyw = 'discard' | 'include' | 'if' | 'while' | 'case' | 'try' | 'finally' | 'except' | 'for' | 'block' | 'const' | 'let' | 'when' | 'var' | 'mixin' par = '(' optInd - ( &parKeyw complexOrSimpleStmt ^+ ';' - | ';' complexOrSimpleStmt ^+ ';' + ( &parKeyw (ifExpr \ complexOrSimpleStmt) ^+ ';' + | ';' (ifExpr \ complexOrSimpleStmt) ^+ ';' | pragmaStmt - | simpleExpr ( ('=' expr (';' complexOrSimpleStmt ^+ ';' )? ) + | simpleExpr ( ('=' expr (';' (ifExpr \ complexOrSimpleStmt) ^+ ';' )? ) | (':' expr (',' exprColonEqExpr ^+ ',' )? ) ) ) optPar ')' literal = | INT_LIT | INT8_LIT | INT16_LIT | INT32_LIT | INT64_LIT @@ -59,11 +59,6 @@ primarySuffix = '(' (exprColonEqExpr comma?)* ')' | '[' optInd exprColonEqExprList optPar ']' | '{' optInd exprColonEqExprList optPar '}' | &( '`'|IDENT|literal|'cast'|'addr'|'type') expr # command syntax -condExpr = expr colcom expr optInd - ('elif' expr colcom expr optInd)* - 'else' colcom expr -ifExpr = 'if' condExpr -whenExpr = 'when' condExpr pragma = '{.' optInd (exprColonEqExpr comma?)* optPar ('.}' | '}') identVis = symbol OPR? # postfix position identVisDot = symbol '.' optInd symbol OPR? @@ -130,6 +125,11 @@ condStmt = expr colcom stmt COMMENT? (IND{=} 'else' colcom stmt)? ifStmt = 'if' condStmt whenStmt = 'when' condStmt +condExpr = expr colcom expr optInd + ('elif' expr colcom expr optInd)* + 'else' colcom expr +ifExpr = 'if' condExpr +whenExpr = 'when' condExpr whileStmt = 'while' expr colcom stmt ofBranch = 'of' exprList colcom stmt ofBranches = ofBranch (IND{=} ofBranch)* @@ -193,8 +193,8 @@ complexOrSimpleStmt = (ifStmt | whenStmt | whileStmt | tryStmt | forStmt | blockStmt | staticStmt | deferStmt | asmStmt | 'proc' routine - | 'func' routine | 'method' routine + | 'func' routine | 'iterator' routine | 'macro' routine | 'template' routine diff --git a/doc/manual.rst b/doc/manual.rst index 11ea6b99c..494a009cb 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -6031,12 +6031,12 @@ so that it can be used for debugging routines marked as ``noSideEffect``. To override the compiler's side effect analysis a ``{.noSideEffect.}`` -pragma block can be used: +``cast`` pragma block can be used: .. code-block:: nim func f() = - {.noSideEffect.}: + {.cast(noSideEffect).}: echo "test" @@ -7501,7 +7501,7 @@ To disable the GC-safety checking the ``--threadAnalysis:off`` command line switch can be used. This is a temporary workaround to ease the porting effort from old code to the new threading model. -To override the compiler's gcsafety analysis a ``{.gcsafe.}`` pragma block can +To override the compiler's gcsafety analysis a ``{.cast(gcsafe).}`` pragma block can be used: .. code-block:: nim @@ -7511,7 +7511,7 @@ be used: perThread {.threadvar.}: string proc setPerThread() = - {.gcsafe.}: + {.cast(gcsafe).}: deepCopy(perThread, someGlobal) diff --git a/lib/std/effecttraits.nim b/lib/std/effecttraits.nim index a5d799786..0f0a24492 100644 --- a/lib/std/effecttraits.nim +++ b/lib/std/effecttraits.nim @@ -1,7 +1,7 @@ # # # Nim's Runtime Library -# (c) Copyright 2018 Nim contributors +# (c) Copyright 2020 Nim contributors # # See the file "copying.txt", included in this # distribution, for details about the copyright. @@ -9,11 +9,46 @@ ## This module provides access to the inferred .raises effects ## for Nim's macro system. +## **Since**: Version 1.4. +## +## One can test for the existance of this standard module +## via ``defined(nimHasEffectTraitsModule)``. import macros proc getRaisesListImpl(n: NimNode): NimNode = discard "see compiler/vmops.nim" +proc getTagsListImpl(n: NimNode): NimNode = discard "see compiler/vmops.nim" +proc isGcSafeImpl(n: NimNode): bool = discard "see compiler/vmops.nim" +proc hasNoSideEffectsImpl(n: NimNode): bool = discard "see compiler/vmops.nim" -proc getRaisesList*(call: NimNode): NimNode = - expectKind call, nnkCallKinds - result = getRaisesListImpl(call[0]) +proc getRaisesList*(fn: NimNode): NimNode = + ## Extracts the ``.raises`` list of the func/proc/etc ``fn``. + ## ``fn`` has to be a resolved symbol of kind ``nnkSym``. This + ## implies that the macro that calls this proc should accept ``typed`` + ## arguments and not ``untyped`` arguments. + expectKind fn, nnkSym + result = getRaisesListImpl(fn) + +proc getTagsList*(fn: NimNode): NimNode = + ## Extracts the ``.tags`` list of the func/proc/etc ``fn``. + ## ``fn`` has to be a resolved symbol of kind ``nnkSym``. This + ## implies that the macro that calls this proc should accept ``typed`` + ## arguments and not ``untyped`` arguments. + expectKind fn, nnkSym + result = getTagsListImpl(fn) + +proc isGcSafe*(fn: NimNode): bool = + ## Return true if the func/proc/etc ``fn`` is `gcsafe`. + ## ``fn`` has to be a resolved symbol of kind ``nnkSym``. This + ## implies that the macro that calls this proc should accept ``typed`` + ## arguments and not ``untyped`` arguments. + expectKind fn, nnkSym + result = isGcSafeImpl(fn) + +proc hasNoSideEffects*(fn: NimNode): bool = + ## Return true if the func/proc/etc ``fn`` has `noSideEffect`. + ## ``fn`` has to be a resolved symbol of kind ``nnkSym``. This + ## implies that the macro that calls this proc should accept ``typed`` + ## arguments and not ``untyped`` arguments. + expectKind fn, nnkSym + result = hasNoSideEffectsImpl(fn) diff --git a/tests/effects/tcast_as_pragma.nim b/tests/effects/tcast_as_pragma.nim new file mode 100644 index 000000000..1de61333e --- /dev/null +++ b/tests/effects/tcast_as_pragma.nim @@ -0,0 +1,18 @@ +discard """ + cmd: "nim c $file" + action: "compile" +""" + +proc taggy() {.tags: RootEffect.} = discard + +proc m {.raises: [], tags: [].} = + {.cast(noSideEffect).}: + echo "hi" + + {.cast(raises: []).}: + raise newException(ValueError, "bah") + + {.cast(tags: []).}: + taggy() + +m() diff --git a/tests/macros/tgetraiseslist.nim b/tests/macros/tgetraiseslist.nim index de7ba06c8..79694a66f 100644 --- a/tests/macros/tgetraiseslist.nim +++ b/tests/macros/tgetraiseslist.nim @@ -1,5 +1,7 @@ discard """ - nimout: '''##[ValueError, Gen[string]]##''' + nimout: '''##[ValueError, Gen[string]]## +%%[RootEffect]%% +true true''' """ import macros @@ -10,13 +12,18 @@ type x: T macro m(call: typed): untyped = - echo "##", repr getRaisesList(call), "##" + echo "##", repr getRaisesList(call[0]), "##" + echo "%%", repr getTagsList(call[0]), "%%" + echo isGcSafe(call[0]), " ", hasNoSideEffects(call[0]) result = call +proc gutenTag() {.tags: RootEffect.} = discard + proc r(inp: int) = if inp == 0: raise newException(ValueError, "bah") elif inp == 1: raise newException(Gen[string], "bahB") + gutenTag() m r(2) From 9560e49e8fa412839743aa86400bd73e2c57ab3d Mon Sep 17 00:00:00 2001 From: IDF Date: Tue, 6 Oct 2020 17:49:30 +0300 Subject: [PATCH 0726/3768] New hint for unused exceptions in .raises (#15492) * New hint for unused exceptions in .raises * Fix effects test * Further adapt teffects1.nim --- compiler/lineinfos.nim | 4 +++- compiler/sempass2.nim | 8 +++++--- tests/effects/teffects1.nim | 7 ++++++- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index c9821ac9f..db0569fda 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -58,6 +58,7 @@ type warnUser, hintSuccess, hintSuccessX, hintCC, hintLineTooLong, hintXDeclaredButNotUsed, + hintXCannotRaiseY, hintConvToBaseNotNeeded, hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled, hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath, @@ -131,6 +132,7 @@ const hintCC: "CC: $1", hintLineTooLong: "line too long", hintXDeclaredButNotUsed: "'$1' is declared but not used", + hintXCannotRaiseY: "$1", hintConvToBaseNotNeeded: "conversion to base object is not needed", hintConvFromXtoItselfNotNeeded: "conversion from $1 to itself is pointless", hintExprAlwaysX: "expression evaluates always to '$1'", @@ -179,7 +181,7 @@ const HintsToStr* = [ "Success", "SuccessX", "CC", "LineTooLong", - "XDeclaredButNotUsed", + "XDeclaredButNotUsed", "XCannotRaiseY", "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded", "ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf", "Path", "CondTrue", "CondFalse", "Name", "Pattern", "Exec", "Link", "Dependency", diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 19223b88f..4eac6ef13 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -1140,7 +1140,8 @@ proc subtypeRelation(g: ModuleGraph; spec, real: PNode): bool = return safeInheritanceDiff(g.excType(real), spec.typ) <= 0 proc checkRaisesSpec(g: ModuleGraph; spec, real: PNode, msg: string, hints: bool; - effectPredicate: proc (g: ModuleGraph; a, b: PNode): bool {.nimcall.}) = + effectPredicate: proc (g: ModuleGraph; a, b: PNode): bool {.nimcall.}; + hintsArg: PNode = nil) = # check that any real exception is listed in 'spec'; mark those as used; # report any unused exception var used = initIntSet() @@ -1158,7 +1159,8 @@ proc checkRaisesSpec(g: ModuleGraph; spec, real: PNode, msg: string, hints: bool if hints: for s in 0.. but expected 'MyProcType = proc (x: int): string{.closure.}' +.raise effects differ''' """ type @@ -12,7 +17,7 @@ type proc forw: int {. .} proc lier(): int {.raises: [IO2Error].} = - #[tt.Hint ^ 'IO2Error' is declared but not used [XDeclaredButNotUsed] ]# + #[tt.Hint ^ 'lier' cannot raise 'IO2Error' [XCannotRaiseY] ]# writeLine stdout, "arg" #[tt.Error ^ can raise an unlisted exception: ref IOError ]# From 695f955f70bfcbad12e64592583daf28e781c047 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Tue, 6 Oct 2020 11:52:12 -0300 Subject: [PATCH 0727/3768] inline tiny proc (#15498) --- lib/pure/httpclient.nim | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 533bf3c15..0cf19f031 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -232,7 +232,7 @@ proc code*(response: Response | AsyncResponse): HttpCode ## corresponding ``HttpCode``. return response.status[0 .. 2].parseInt.HttpCode -proc contentType*(response: Response | AsyncResponse): string = +proc contentType*(response: Response | AsyncResponse): string {.inline.} = ## Retrieves the specified response's content type. ## ## This is effectively the value of the "Content-Type" header. @@ -329,7 +329,7 @@ proc newProxy*(url: string, auth = ""): Proxy = ## Constructs a new ``TProxy`` object. result = Proxy(url: parseUri(url), auth: auth) -proc newMultipartData*: MultipartData = +proc newMultipartData*: MultipartData {.inline.} = ## Constructs a new ``MultipartData`` object. MultipartData() @@ -415,7 +415,7 @@ proc addFiles*(p: MultipartData, xs: openArray[tuple[name, file: string]], p.add(name, content, fName & ext, contentType, useStream = useStream) result = p -proc `[]=`*(p: MultipartData, name, content: string) = +proc `[]=`*(p: MultipartData, name, content: string) {.inline.} = ## Add a multipart entry to the multipart data ``p``. The value is added ## without a filename and without a content type. ## @@ -424,7 +424,7 @@ proc `[]=`*(p: MultipartData, name, content: string) = p.add(name, content) proc `[]=`*(p: MultipartData, name: string, - file: tuple[name, contentType, content: string]) = + file: tuple[name, contentType, content: string]) {.inline.} = ## Add a file to the multipart data ``p``, specifying filename, contentType ## and content manually. ## @@ -635,7 +635,7 @@ proc close*(client: HttpClient | AsyncHttpClient) = client.socket.close() client.connected = false -proc getSocket*(client: HttpClient): Socket = +proc getSocket*(client: HttpClient): Socket {.inline.} = ## Get network socket, useful if you want to find out more details about the connection ## ## this example shows info about local and remote endpoints @@ -647,7 +647,7 @@ proc getSocket*(client: HttpClient): Socket = ## return client.socket -proc getSocket*(client: AsyncHttpClient): AsyncSocket = +proc getSocket*(client: AsyncHttpClient): AsyncSocket {.inline.} = return client.socket proc reportProgress(client: HttpClient | AsyncHttpClient, From fa841214212cba21ebbf9a0a18a9ce367fab15d2 Mon Sep 17 00:00:00 2001 From: Paul Tan Date: Wed, 7 Oct 2020 00:05:31 +0800 Subject: [PATCH 0728/3768] effects: exclude swap() from "indirect calls" assumption (#15504) swap() will never call any procs passed to it, and so it can be safely excluded from the "assume indirect calls are taken" effects tracking rule. --- compiler/sempass2.nim | 2 +- tests/effects/teffects10.nim | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 tests/effects/teffects10.nim diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 4eac6ef13..f0d6c6c7a 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -552,7 +552,7 @@ proc isNoEffectList(n: PNode): bool {.inline.} = n.len == 0 or (n[tagEffects] == nil and n[exceptionEffects] == nil) proc isTrival(caller: PNode): bool {.inline.} = - result = caller.kind == nkSym and caller.sym.magic in {mEqProc, mIsNil, mMove, mWasMoved} + result = caller.kind == nkSym and caller.sym.magic in {mEqProc, mIsNil, mMove, mWasMoved, mSwap} proc trackOperandForIndirectCall(tracked: PEffects, n: PNode, paramType: PType; caller: PNode) = let a = skipConvAndClosure(n) diff --git a/tests/effects/teffects10.nim b/tests/effects/teffects10.nim new file mode 100644 index 000000000..8193b394a --- /dev/null +++ b/tests/effects/teffects10.nim @@ -0,0 +1,12 @@ +discard """ +action: compile +""" + +# https://github.com/nim-lang/Nim/issues/15495 + +proc f() {.raises: [].} = + var a: proc () + var b: proc () + swap(a, b) + +f() From dd862285483b402424607d5a1365d99cbd8566c7 Mon Sep 17 00:00:00 2001 From: Serban Constantin Date: Tue, 6 Oct 2020 19:05:57 +0300 Subject: [PATCH 0729/3768] update unittest docs with correct exit code info (#15502) --- lib/pure/unittest.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim index 98be959e3..31eaa657b 100644 --- a/lib/pure/unittest.nim +++ b/lib/pure/unittest.nim @@ -22,8 +22,8 @@ ## parent test as failed. Setup and teardown are inherited. Setup can be ## overridden locally. ## -## Compiled test files return the number of failed test as exit code, while -## ``nim c -r `` exits with 0 or 1 +## Compiled test files as well as ``nim c -r `` +## exit with 0 for success (no failed tests) or 1 for failure. ## ## Running a single test ## ===================== From 51e3e0c7c43e82932d9bbae2e6ccc876d29b9fab Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 7 Oct 2020 00:09:28 +0200 Subject: [PATCH 0730/3768] implements https://github.com/nim-lang/RFCs/issues/260 (#15505) * implements https://github.com/nim-lang/RFCs/issues/260 * added a test case --- compiler/extccomp.nim | 4 ++++ compiler/options.nim | 1 + compiler/pragmas.nim | 30 ++++++++++++++++++++---------- compiler/rodimpl.nim | 5 +++-- doc/manual.rst | 8 ++++++++ tests/pragmas/cfunction.c | 4 ++++ tests/pragmas/tcompile_pragma.nim | 9 +++++++++ 7 files changed, 49 insertions(+), 12 deletions(-) create mode 100644 tests/pragmas/cfunction.c create mode 100644 tests/pragmas/tcompile_pragma.nim diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 79b4ba637..11b001f9d 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -554,6 +554,10 @@ proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile, ospNeedsPIC in platform.OS[conf.target.targetOS].props: options.add(' ' & CC[c].pic) + if cfile.customArgs != "": + options.add ' ' + options.add cfile.customArgs + var compilePattern: string # compute include paths: var includeCmd = CC[c].includeCmd & quoteShell(conf.libpath) diff --git a/compiler/options.nim b/compiler/options.nim index 40d86a01d..6054eb3a5 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -198,6 +198,7 @@ type nimname*: string cname*, obj*: AbsoluteFile flags*: set[CfileFlag] + customArgs*: string CfileList* = seq[Cfile] Suggest* = ref object diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index fe8cd3cfe..4d5240e3c 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -93,11 +93,10 @@ proc getPragmaVal*(procAst: PNode; name: TSpecialWord): PNode = proc pragma*(c: PContext, sym: PSym, n: PNode, validPragmas: TSpecialWords; isStatement: bool = false) -proc recordPragma(c: PContext; n: PNode; key, val: string; val2 = "") = +proc recordPragma(c: PContext; n: PNode; args: varargs[string]) = var recorded = newNodeI(nkCommentStmt, n.info) - recorded.add newStrNode(key, n.info) - recorded.add newStrNode(val, n.info) - if val2.len > 0: recorded.add newStrNode(val2, n.info) + for i in 0..args.high: + recorded.add newStrNode(args[i], n.info) c.graph.recordStmt(c.graph, c.module, recorded) const @@ -491,11 +490,12 @@ proc relativeFile(c: PContext; n: PNode; ext=""): AbsoluteFile = if result.isEmpty: result = AbsoluteFile s proc processCompile(c: PContext, n: PNode) = - proc docompile(c: PContext; it: PNode; src, dest: AbsoluteFile) = + proc docompile(c: PContext; it: PNode; src, dest: AbsoluteFile; customArgs: string) = var cf = Cfile(nimname: splitFile(src).name, - cname: src, obj: dest, flags: {CfileFlag.External}) + cname: src, obj: dest, flags: {CfileFlag.External}, + customArgs: customArgs) extccomp.addExternalFileToCompile(c.config, cf) - recordPragma(c, it, "compile", src.string, dest.string) + recordPragma(c, it, "compile", src.string, dest.string, customArgs) proc getStrLit(c: PContext, n: PNode; i: int): string = n[i] = c.semConstExpr(c, n[i]) @@ -513,9 +513,19 @@ proc processCompile(c: PContext, n: PNode) = var found = parentDir(toFullPath(c.config, n.info)) / s for f in os.walkFiles(found): let obj = completeCfilePath(c.config, AbsoluteFile(dest % extractFilename(f))) - docompile(c, it, AbsoluteFile f, obj) + docompile(c, it, AbsoluteFile f, obj, "") else: - let s = expectStrLit(c, n) + var s = "" + var customArgs = "" + if n.kind in nkCallKinds: + s = getStrLit(c, n, 1) + if n.len <= 3: + customArgs = getStrLit(c, n, 2) + else: + localError(c.config, n.info, "'.compile' pragma takes up 2 arguments") + else: + s = expectStrLit(c, n) + var found = AbsoluteFile(parentDir(toFullPath(c.config, n.info)) / s) if not fileExists(found): if isAbsolute(s): found = AbsoluteFile s @@ -523,7 +533,7 @@ proc processCompile(c: PContext, n: PNode) = found = findFile(c.config, s) if found.isEmpty: found = AbsoluteFile s let obj = toObjFile(c.config, completeCfilePath(c.config, found, false)) - docompile(c, it, found, obj) + docompile(c, it, found, obj, customArgs) proc processLink(c: PContext, n: PNode) = let found = relativeFile(c, n, CC[c.config.cCompiler].objExt) diff --git a/compiler/rodimpl.nim b/compiler/rodimpl.nim index 52b4d1f85..e50ab66de 100644 --- a/compiler/rodimpl.nim +++ b/compiler/rodimpl.nim @@ -838,11 +838,12 @@ proc replay(g: ModuleGraph; module: PSym; n: PNode) = of "warning": message(g.config, n.info, warnUser, n[1].strVal) of "error": localError(g.config, n.info, errUser, n[1].strVal) of "compile": - internalAssert g.config, n.len == 3 and n[2].kind == nkStrLit + internalAssert g.config, n.len == 4 and n[2].kind == nkStrLit let cname = AbsoluteFile n[1].strVal var cf = Cfile(nimname: splitFile(cname).name, cname: cname, obj: AbsoluteFile n[2].strVal, - flags: {CfileFlag.External}) + flags: {CfileFlag.External}, + customArgs: n[3].strVal) extccomp.addExternalFileToCompile(g.config, cf) of "link": extccomp.addExternalFileToLink(g.config, AbsoluteFile n[1].strVal) diff --git a/doc/manual.rst b/doc/manual.rst index 494a009cb..7258e7775 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -6622,6 +6622,14 @@ with the project: has changed. One can use the ``-f`` command line option to force recompilation of the file. +Since 1.4 the `compile` pragma is also available with this syntax: + +.. code-block:: Nim + {.compile("myfile.cpp", "--custom flags here").} + +As can be seen in the example, this new variant allows for custom flags +that are passed to the C compiler when the file is recompiled. + Link pragma ----------- diff --git a/tests/pragmas/cfunction.c b/tests/pragmas/cfunction.c new file mode 100644 index 000000000..bf49d6a29 --- /dev/null +++ b/tests/pragmas/cfunction.c @@ -0,0 +1,4 @@ + +int cfunction(void) { + return NUMBER_HERE; +} diff --git a/tests/pragmas/tcompile_pragma.nim b/tests/pragmas/tcompile_pragma.nim new file mode 100644 index 000000000..4e09a7501 --- /dev/null +++ b/tests/pragmas/tcompile_pragma.nim @@ -0,0 +1,9 @@ +discard """ + output: '''34''' +""" + +{.compile("cfunction.c", "-DNUMBER_HERE=34").} + +proc cfunction(): cint {.importc.} + +echo cfunction() From acbe27b082c202895df1b78a82951389b4a232a0 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 7 Oct 2020 13:05:19 +0200 Subject: [PATCH 0731/3768] disable 'observable stores' warning message for 1.4 (#15507) --- compiler/lineinfos.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index db0569fda..a0b1d1965 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -214,7 +214,7 @@ type TNoteKinds* = set[TNoteKind] proc computeNotesVerbosity(): array[0..3, TNoteKinds] = - result[3] = {low(TNoteKind)..high(TNoteKind)} - {} + result[3] = {low(TNoteKind)..high(TNoteKind)} - {warnObservableStores} result[2] = result[3] - {hintStackTrace, warnUninit, hintExtendedContext} result[1] = result[2] - {warnProveField, warnProveIndex, warnGcUnsafe, hintPath, hintDependency, hintCodeBegin, hintCodeEnd, From 0426a4d85ab8f9558dee35c657aad0d299748c15 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 7 Oct 2020 14:38:25 +0200 Subject: [PATCH 0732/3768] fixes #15508 (#15509) --- compiler/varpartitions.nim | 19 +++++++++++------ tests/effects/tfuncs_cannot_mutate_simple.nim | 21 +++++++++++++++++++ 2 files changed, 34 insertions(+), 6 deletions(-) create mode 100644 tests/effects/tfuncs_cannot_mutate_simple.nim diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim index a172a906e..f831d33cc 100644 --- a/compiler/varpartitions.nim +++ b/compiler/varpartitions.nim @@ -48,7 +48,8 @@ proc dec(x: var AbstractTime; diff = 1) {.borrow.} type SubgraphFlag = enum isMutated, # graph might be mutated - connectsConstParam, # graph is connected to a non-var parameter. + isMutatedDirectly, # graph is mutated directly by a non-var parameter. + connectsConstParam # graph is connected to a non-var parameter. VarFlag = enum ownsData, @@ -99,12 +100,12 @@ type config: ConfigRef proc mutationAfterConnection(g: MutationInfo): bool {.inline.} = - #echo g.maxMutation, " ", g.minConnection, " ", g.param + #echo g.maxMutation.int, " ", g.minConnection.int, " ", g.param g.maxMutation > g.minConnection proc `$`*(config: ConfigRef; g: MutationInfo): string = result = "" - if g.flags == {isMutated, connectsConstParam}: + if g.flags * {isMutated, connectsConstParam} == {isMutated, connectsConstParam}: result.add "\nan object reachable from '" result.add g.param.name.s result.add "' is potentially mutated" @@ -119,7 +120,8 @@ proc `$`*(config: ConfigRef; g: MutationInfo): string = proc hasSideEffect*(c: var Partitions; info: var MutationInfo): bool = for g in mitems c.graphs: - if g.flags == {isMutated, connectsConstParam} and mutationAfterConnection(g): + if g.flags * {isMutated, connectsConstParam} == {isMutated, connectsConstParam} and + (mutationAfterConnection(g) or isMutatedDirectly in g.flags): info = g return true return false @@ -173,11 +175,16 @@ proc potentialMutation(v: var Partitions; s: PSym; info: TLineInfo) = let id = variableId(v, s) if id >= 0: let r = root(v, id) + let flags = if s.kind == skParam and isConstParam(s): + {isMutated, isMutatedDirectly} + else: + {isMutated} + case v.s[r].con.kind of isEmptyRoot: v.s[r].con = Connection(kind: isRootOf, graphIndex: v.graphs.len) v.graphs.add MutationInfo(param: if isConstParam(s): s else: nil, mutatedHere: info, - connectedVia: unknownLineInfo, flags: {isMutated}, + connectedVia: unknownLineInfo, flags: flags, maxMutation: v.abstractTime, minConnection: MaxTime, mutations: @[v.abstractTime]) of isRootOf: @@ -187,7 +194,7 @@ proc potentialMutation(v: var Partitions; s: PSym; info: TLineInfo) = if v.abstractTime > g.maxMutation: g.mutatedHere = info g.maxMutation = v.abstractTime - g.flags.incl isMutated + g.flags.incl flags g.mutations.add v.abstractTime else: assert false, "cannot happen" diff --git a/tests/effects/tfuncs_cannot_mutate_simple.nim b/tests/effects/tfuncs_cannot_mutate_simple.nim new file mode 100644 index 000000000..9de20d1ec --- /dev/null +++ b/tests/effects/tfuncs_cannot_mutate_simple.nim @@ -0,0 +1,21 @@ +discard """ + errormsg: "'edit' can have side effects" + nimout: '''an object reachable from 'x' is potentially mutated +tfuncs_cannot_mutate_simple.nim(17, 4) the mutation is here''' + line: 16 +""" + +{.experimental: "strictFuncs".} + +# bug #15508 + +type + MyType = ref object + data: string + +func edit(x: MyType) = + x.data = "hello" + +let x = MyType() +x.edit() +echo x.data From 3b901d1e361f49d48fb64d115e42c04a4a37100c Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 7 Oct 2020 14:39:13 +0200 Subject: [PATCH 0733/3768] docgen: improve alignment of comments (still not perfect) (#15506) * docgen: improve alignment of comments (still not perfect) * fix error message text in `tunknown_named_parameter` Co-authored-by: narimiran --- compiler/renderer.nim | 2 +- nimdoc/testproject/expected/testproject.html | 11 ++++++----- tests/errmsgs/tunknown_named_parameter.nim | 4 ++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 08012bb55..1de56a296 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -183,9 +183,9 @@ proc put(g: var TSrcGen, kind: TokType, s: string; sym: PSym = nil) = addPendingNL(g) if s.len > 0: addTok(g, kind, s, sym) - inc(g.lineLen, s.len) else: g.pendingWhitespace = s.len + inc(g.lineLen, s.len) proc putComment(g: var TSrcGen, s: string) = if s.len == 0: return diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html index 9c06211c9..a5522a2e2 100644 --- a/nimdoc/testproject/expected/testproject.html +++ b/nimdoc/testproject/expected/testproject.html @@ -661,16 +661,16 @@ Some proc

      FMe)pXdJNe8EXx`FQ)r(Z;bly2h-c!ph2UBshcq5(?Hts>>>;1S8>K3H2sp{?-rU z`r&CO8)cQUw-(k+sT93d_MD+3M@xG(vwf6>Wv3iDdT4cZxY{Tx4HZr0(lA0ey}@Ob zd4(maKAM|;Bvf4(3F{Wv+L#iG45Nxi6jm1Niq`GoI4@?63ezeIi>3x6)rCc&$>OST zI=#AZa(Sq=j_U@~Xwf2tU#kAKjQa)l^E=?b{I&XD{#my8o`<92XWhr+3Pm^J57iMh zW=)t-P0it&i-RTA;fjIrHc!>@HD+CMi8n^PKlr8oHeO!r=(>OLe7rx6=e^pwQ&QI* zb&$p^*&hZ5Yb#4-Q!NgL>WV^DV%$8Ew3?4#;gg3E6O5~P;r-Fw{G3yB)r<3pl>y1<&7LZa#a4n;K)imNOf%$ zi3BIl3=WA@m!Cc)K~jUXyhb6PH+1*~Bv4)+no?MfHwjl4P6-7Ii;6-uHQj<4*(g>~ z6;Z}5RLwkCSyp~_e9>ge3G~YzJQfABCs&56E64ynE6OShMH5M&-!WQ0jY7X+V}>Gb zPZ)!gGhCfOR7%E=pTa)5xlZxIRl?CG&RZmqsq@z;@*g}d&yoKx&0`#?)7L1{A2U)E za7ugOwDV|${sT^SX(11RGMvI zaQKjkp-M42$d)HsOKpU}U|CJDu)I1{SUfYR_+p)s7f#c+8nepiHg&-;X@pCHlf(3) zny$e}DXE7mBP;+y)goolfOu5JFMj@}ky5J?C+6@FJGj()-xXu=-3*wN(#$s+!5l(2h0Ci znZ*0=fa2k4n!`1;Exi~nZlsRyam5*@QEPTXZ&26$WSmx7c zt)Gg^YN`q&MWts3tt;i27{|P+9j$LQ(!W&i3{Sp{Rk-kK=pZ`0j>klObWu@lRavO` zyii5+*X8BqVYStu7j(_Ed!ao{s-Nxq8O$D>*wry8fG4e!Z_m$?`&x;h~73hszqH zim`g67*u6x#2l=Q!CD5hqa~J}4Ef_KYndkyBaazX`4ACgoJ?^CiN)_&(Ro0OhWWBZ z%PM4AI3`j}pj;VOi*dTPN)(})`iE=CSGU-Tn#uIZio&WYswXd0SY1?Vl*_OZM)$nh zvWS>&3?}+iwH6;;8yQ_9drPzPuc#3HST-L$_oxU}gsW$^Ho`Ts#A7gH4LAAKGhO$T ze@Jb0waB}?T!tHk#l<5+g;lv_n6NrDtu|B>8C@eST4U5!F(`+|REMU^E~IK}Ol?Hi zvD~PsDld!3n7yphkE|4%cA|+6#v~*y>HP3{h3wqaxg-p>3=6A8q-yk@9+L5fv4B)D zdx@4kxNM3H7T2zNE-$Nz&=L%RW2LZ&FoN!A#~vJxM8Xw@tfK7c<)gyI>`zV&%}@#m zy{L#8y8NOZ6Ki3xmMb?(=%=`?XV+&{K`qKeJE7=p2 zUar~gRE=Hi>rCt^8>>!Pv>|=z?);;1dRUz5%qgoWsxA}jq)Ij1HLst_JZnPb3|)hn z9MdsRJ=riG?GZCed;a|YMf@A}|8BWjosNZq6LH0TWoX9e$(I=l#t!M3V8U1PFX`Vg0Gr_39q~&VlVJsu9{BRzw z@z>tJAFH;7^zOYenrVaBUC{T;_@`9T56UWW0rDTw9B9#FL;QnHM7g!~MukU;Sn8+u zTjc&iF&izV!E&03*mo#oQ?fY7+NLmA9GYA^g@s*Nd2x`*SC1aSlG;kfNtP)>u%=75 z;6>qDuDO>}EhbgL89b2*o?bid2~VL|#ENnaA0ldgY-kGJDpZ|cDpr`e3`phY6;@9TRT~qA54l7r zB9?j8Iu=tw5iN*gV!vQIa~d&#oJp49Ahk!{iTD~L{*j5j)I}3A7n{LFjC>J8j<32V z-TDsv&r2O;KT0h|#x@Eqder zKg7%*`G3@^n0YV%f6xEt+!r(N;{U7v_{;zQCV|{w(h>iE!3TS-YYT5r5N2`zr{b{x z50y#uz-G2zdwiBnm`-AhDl5;HTc5huGm0^K4zbsLI#?`H@Feo#V@1srW**@pc~j1w zmmwa{$vgQ}hIu6O_v)HZUSXt`rEI8pl)Mm2PnX_fs>4&N3o97(r*t31Lv-=Kcw}D1 zpx9l?*G9_9^)t%q+R9LFsPbpa*H)GlF%6W()Uw%3K1pSCNr@P8iJyiJL%)7eV?Ml!4tjDzO`uhL(d5NaaX?0U#=}${yj1`Oh z3&i%WobwowaBg@;s5-l*h!1uZRaA|f!UF{s{xew}i-qPW`FMe?j$*OE5!-zI_r%Qa z`15oBNNG&^|6;zu>5e1r_xHa$e*AU6pJL|!#Q!H7s@JhPW3d|>EMKo2pP~s(oYI zK~g2Yr3qDqi%Rp$)HxZKeUjQaLVRUqd1VYM3zZl5P|(x3 zJX{$n7D<$JKt=)_> z&gdE(fCXDey2-T?+vJ(SNVj4-2h$D5d}TGb=Ca~n-0o%jh^s%@ekY6@~1KJ`m(FFV<=a}o&z6Q zT3(|qA7KwU$mXbLoht71lC>+x$f}rAu!Y*WrgK-h+c||jNu8Vx*5iGRKO=oM2`7iD zUFkD73yKHco$JV_^LX*kWbse2_$S=i%32jG{(M0XYl5ORYQj&pRh1V___z})@$>&4 zUhyV(!*z(pELS^7+MXEblf*+s9u&a}ch_ibU;={mm&m#7O@e!rmI zDZeICXr6qN^?bK(29BZm>BW!3xCe1mx!L;Z@sf#w^MqQC?aeH&|G2Inp;?b(WDn%= zO=WRmb@4E@bf+Jcm6WNG(Ut$tZy)YuzkE`n=307fEB6G9im5qeQkq`U3eUo4D&&iSj*H2p;I=N`U_Z|~mHG<}~Zt~)e2KW7`# zZ4}paeEl7mNo$GJh&l+0ZHz&k(tCC~%MpmT_bKI(Gac4wQXi*G@$vy3S!32Ig%wrq z1P;)=#;jAO@kGiMp$T7D6|Qmd@%B6AB)4`=ub1i|jlWL&_#Zr<7u5P|%sNn2U{PU> zU4JKa3bLgmYVss^O)9~Iw7kZw%$liXRXpkoc2|`izy8YSRH0}gLG`Mee9A3P4An-e zY9sPgAy2{Oj8vw4qE25U`W=`l*&zFE;vBV7EB&O*?lRcb)THF`T3(}+S99OfiGP-m zbKUC5E|8N7?lnqzp*qiinrfg4w+;vEv@{C)b{FLr1{U#}@QRilNbrYgd5u|?e;hq7 zf6Tc2iMbb84{&t6c!*Emk{>cUSGv{s{#41Ep>9_l-(L#LXB5tKEfU)43^m&NjT$}C z%EF^keE%9aP|>o$%vx^{WsUGt$aW+V{T?S{;!!Y zvrQfM!lGK)G|w(p>u9!J6R+WoO=vnk?Rh;$$ADEO=c>yeZ1fq)N7eCTRf;|F6vNdiL)5Gw1*61@m9< z{za-l!$-7FtN;x!kVCxiC;f(w6VfTt?8BCrZO}(vwXd&t^rNT z#~PXEgyQ8HoWv;FPSfm-`1!Gxu?rMG|H|X@9qxCITj(@3X2rjd7Qg-#YD_>KtK4n0 zyhip%rKUkkfr~V+v6=SigMIq@*r!`rjmtLi<(Uv?vv}SrM_e@fHvat6IKLO4wM2aS zK|3XN5#PRQsE@cPp4&_HoyKO`uYXr|U7OfX%5&riK^cyOl2?dAA&*XIF-C@_q21Ffk5S=IT z-CQbIgM-FjH-AoRS`Vr0&n!sK>ge(7)KA}p^&@Q0X_`nEuZwk+-Y~4uUt=@NFOGNi zYSKQ$I|5P%X0#j*6l#c`@5#S|zSEeMAig~oc?9)MnisQpBa9~y`sH=F$(q;Lyz=#; z|MhBGKHC?FA>O{x8ERlSXUSL|FCpL8D&4QtzIri!B(=XNfox;Ke4Yw8MBi!5a-yjU_;u{dA*=-U zwdPXECV=tnpEmqy3%H zbpGnJB~>(AtDjpq^gLecs?pUx$S7bSUW5#hz>>C_w!8gA$~fpw^v)rs_+L&8&YhzBZkIql@iyRMFht zTa_w){_T}Xs=CxX{`ncNZCn09zRjoBi%xmtuYQ&Jr`V22;NPlvhlbbjL~G}(74bQ% z`}py}lCesDz82Hiy!z=)|Lom#e2f1!ipWaudsvu-J+%gjVZIL5*i8NCAAOqcf5_f} z<+^l3iwt3zV8--;VzYpunty|XS_gyEgQo_2iPpt+P|Y%W^>#*T>->*K8eR4Kv(rpk%?ayQ}vGCxk_KXkS>%+psIn`s~NPtbm>_P-zN z$OjQ@s`5H|)0JNQe6ggwFmj&D=UJc6)7U)y`%=HXo7zvce`;6dnZavbqj`acPw+;> zYua+ilyQ0`Sa@i=p6o^4{*$B`*V^O9ADy3W-G(t!r?0V@2wpeAKN=NJulTWPTyape~Ht#y~X~P_xVYD|EegQIXNWXsTaE~Vp=9+bn4m3-;5_| zeKj_*UtF3^j1=F#>>PFbieIl->p9CzDf&WVR?oNyH)vjC^V(-W_RstJ^iNm&^p@3b zDKdB{pVlcXt8iQOL}AteT0f2Zv7b+xO{{8Jr|I`2wSCE38TeP}zx~Ag#m7D_U-NVA zBYlio8u>RouiY#=D*OG?Jd@b(P@RuP&;Fu42sq|$a&?dn(U_IT`dNSBOfrR<*Vw%F z*nhPbZGw-^JjhXbMhC3*X6a~GJT=3ndKMHw-T(6I6x9E$%s@` zscxrkt0H_;&>k?`>HIZj)wpDmGz&}pCf_I3@>VAMZ6cTIlr%Q4`~$@Lx~YHH^P0x#nzCIz}|0(RRqo3G+X{w*ho2GC{hEM0fLdCj%G&Zw- zc)pQf|5^Gu(GrzH%DIvanx}xp3ZHWZppI@g3`6!flIf?_{;sa3K;@$;!=Pj<| z{nsc_U*fb;v8uCQ!#;g_@cb>o{!n^lcygHU6JwoDu6#Y)eev@vOUCNEzU5cN7p2cg zHps-MFZCG1CqcuN(g4=pxW;C+k3RDGXw&CIl!dQXlapfsowH+t`1z9~qbzWou279x zymch2Ulm_`V3n^(aB4L;Y0#iaI<6be(>EHMRlc4*@z3&g1o1?VBT-@*URFLSNZR>2 zeT~gZzfVtbKBQ^?u5vvvv_8nTf2Axu*Ig$uB>*X}v$nwvO|6GW7Fb-g!mlYO?seLS|A`NW@(6`wKVh~|tYB@x@o0}H8RR`yJH1>n)D%J;HnmQ^%O2Z2J9jTCEt;y1zy9L0 z^!h}g_;8i@w21x`m;Q4=MvG%p&s=eoVSK|vu&z(k+{SfAzo@w$Jh6Y&+y^cJJMh4C zHP|1_8xS?egE6oUTredKLV@3 z!_VeVm4kKQTJRHa8)$-Gfgff^&2}6J+B_s`_5fStAP;s2tH7n;5-@cr^5C)HHn1N2 z3JecJ{#f({dw_d}BM;s(0(r3aNaVryz_noH9OS|4&P5)4JQw+nhH={{(AVD~)A50-)Zz+1r%3?wnIKiEDW`+=*$Ixue>_5;UWfIN8gg~)>k zj7J_k%dVHkOzMN*Ms*KAP?qVf;@QHrO5LKC+?q!JlJm% z^5EBC9XO{Dd9cG|$gOIr88u70844hmi*d zRUr?SRU^;-!XLo?VA~qx!Fn(Pz8=AT;2&$F<~nfj^r*QVoC)p)?*ZF0bZ-WGgJWk# z&3y3SS;&KTflI;gwaA0l%t0Rfd@l0ff!86=M$4!3kq6&gfIK+kdgQ_7i;xE=EJhw| z1h<3jmmm+0`91PH@C__Q9^4D&gVSz79&EoHdGLVSkOx1#9eME37082A??j#r#iF~B z2UG4t9&7|7V7L2`2lw!2ZP$SVA4DGfa4quSvd54=#W1Eng*=%1H1gmlU<5quPsoGk zJ%>DagXNo$2akLSc`&ygdGOTD$b(OV+rd9>K^{EkW#lvX zTMn-v4`ze;VA)@h2j{u8W;g{ zzC|9KxEFcwb#Ob_>O17Yyzh}e9e?v9^5Ezw^5CkUkOv=rR|9r!4?9o!D?1&!30*}gmJg1x~dftZ;Oz5+(T zSqC5w&Q3!fyyZaT!TZ6lzylA8neBQQ#+PkkW)JYwgJb3xu;`GOSp`mL7c-ZD#-TBD zEjaqHn7IvX0KWo9v`3x|+TVdaz{5Kr4}JhvfyU9ugV%v;!H&lw4<6GId2m<|`Cf+c z4%h>nb0YHKk6;yOc0wL}Ap?0Z+y!~?CGab7H`s>f{!CbKCX)&`LyaHSV zUfMNgt^pgstzeh#F>?=?1Gec+`{aAgUBP)^F4zc`gYWV^=SASMGh*gya8&P@xdr?Y zYyhYAiJ57AC|}>0nE|%$7c)nI?fb{fVsIfi2fSxM%v=RtJTPW%0&hAqX6^#hGGnH} z8RM(KAh>K$%*+Ds0}H^8XT{7};A`LtaO~iixdCiFEN1QmSBylyANDv0dGMTE?}l1UL)aH5z&FKVy*x=jS00ZUJLp>-?D6p+Da}1N(!=jEkA$!P6(i%sTM+OJnA8 zun)Li@DpR^4$uVmfq$BW`~bt~QHVS^Y%=m-8CVDI0+)l|7au*9Hy!!XZgI$**pJ^EX1p9-Nmmv?1y#;wN1}+D0zZH3K=yK%2e}VhJ&u>G1 z5cPfs^57_NJU9!i1K$UigFmf69xUP8y*t2`_aF}jHz0qOVO$9I2d@IhgKs~FJXrA} z^599EkOwb(33>1ta36TW7Ua)n{`6Pm!S#Pb9vrwGd2r*K$b;Q?AP)`)cYq&*`@oTJ zA)jR!r~NBtW`a+>P5T311!sYScE!w@eOH}ZpNmmeb!mVSaf_$D|B{1TiEzSw{~cn{yD+z3ATCGud4 zuaF2^|FZ~vIa5=aOd>=G&(EB^&!4JVKu;54J!4Wa! z!843Tb0zpLxDmXrMWeY3Y|nQ!jiJjBj99a50z*egl?+XCB{Z zE&>lcvC&*3uv4SC6?`=50DE79JUAKba6a;2 zf3V%P$bvx)XVD{9VX{--C5vkGqivXM*d&KkptYcqgNvj-Urr!^BzPV9Q+XSBK#5L!D}8v9!z}_`EiU>Pc@pE;E?r=W&v3GOrtpq zZ2xSdxdQC^0)7J=4(C0mdOXTFR)xE5Rq?f^G}d9NT3R)WTO=0ji*O#LhJ;B(+4u=iHv z!OgEC57xbgJQ&-CJlOZ|$X~>I5e$MagIVBia1uE4b>zYH?Z|@*{)s%e3fu+$1vD%WN_&&G` zjDf~P_9xy$9_$5XfgkKb9vuDw^5A4}C3q#c5nK-L0-pelN$3v-!B4>~u;quygC~Hq z!GYjPungP?&H;CUcY{VD^RC^EW;(bQ%mM!j7K0yybHElKG46pK!A)R4a5p#>Y&jV} z38sT9z#Q;tuo!$DoCAIit^(6OMjku~+zp-uwk$G?i@_Hy9>T~450bd{=Vm<_>gKfV= z9_#`ZgE`ey!312!GD9|!`*>zkOyx9SAlE5P2lt2 zArI~ZTTWp-1k=I8zDFKB9V`Y%fOEhZ;41KDa1(gse~gG z;N3Cg!ABdB2VVgEPX9jzAN6aGvp#IUh7y{A8{Mt5be5w}3NJ ze=-}uf|fs-X_s*iW`H+?Bfy8jQt*TWkO$LSArI!KArFSZ2Jm_?Z7Tj4%m6onBfz)8 zQt*3lK6vPX$b((ME#Ppl0h|t|m1AEp16%`+0MBcUJoq&@A8d0F^5DbZ7H|vL0DcIj zRbU1%16f;`v@ECma}`QR*Y zHF#<}@MQuv}z|Ho&S6D|H|Xo%O;-Dw%xF{9nLxApc!eijkAwA^VBoaI}vIK zS%xU%#9-8-=8`~%?CaFeKwy7KjfsSd6$R3&E^Z> zpNBsZw9B7*XbTZRlzA56Pp3ysT#3VTDY)fufs>nZ9pTo&<-rZ)+D^AtW-INs9lip- zo0~6l_`UE~z%v9m@+p&Sz1yeI2jTtdtvCE3r})=fK71?q-Q;8Im-?P8x6nTV|JTm` z{8IQEyZD!H9sC3Ee&yQ^|1`W``S!xc;Qh+iK9x1{ss82b4L=0luU+QDzXtEuE+g<4 zp61^!m%?v?_iLBy;FoswZ`O`E{;6tADoqo6iegnLpf7}4S4t}Xe{zhBBo$%sW zt6x3G;GcjuJ@P-=@*NLgPQ)|W%^v;*o6m$FFd%Av?%^A3z5xD!fl>27Pd&G^>v(h`;@2 z!T%e+(o_C2r~L5!hepjkv{T1Ep9Dk(qJM6HZ+Wi2pV|pO6aIdWe)rh=#o$eNKR?y+ zK<3WpMa`Wa{ob_o%Y?7zS^ce^^l!G)FMuC3I%;-x`hi z)c%D}*Z}_s{Cao(TnpRmfIDYw)NJF?y_K!|KKQ!hUoB~w|9JQ-;WONPk;B))&xJq5%@;cSa`=Vt3q7`*XWMQ){P!0`&1XIQ zQ#QW?{-%rk`_4Z2l#8S0-|+!X+uC}7&_LL)!$J7QN&aJ8fB5?gsT+@dOKkg&hyQwV z)a>ZtkFxnX_|J=?<}MHaw#_exe>fC1zi`_pt8edxxXulb-a~+UYNce;NKu5C3nQUk|^$Dr#Qov0t5Sza8-3O^cd;@bGup z{66@)nyC4WC;iXu^gA3(|A|D+4?Xhl+VcJ351vl@_V6d!{CM~yW-y2G@Z)U04t{hU z=T|)Zw>G~VzVqc#^KOstyWRGE>*4ReDr!bO`hRchzXSg2*-^8nn=hBNwEsT%o8kZL zslPwj_1B>-`_|V+%~2lxM%w!Ihrg6_Fb{b8&nmnBjEDc^`l$K1JO7l2ZT;%tXD*DI z?=uf`+Q-h8B7$fi%i#wuiJEVCvH(HocDRi z(|_)_`_Fp#c6a*EJ$As~4&T>Pe?9H`+Xuh)uBiF0C;zwX@^xUfUwOCx+^RqP+wgvC zl=1Knt)%~X(!a}2zYf0JJyEmX!#{8H%i*`f`?cTo@E^hZ^}ikPpWhobPw=$wW9;_5 z4}Q<8s5!!8-yybrI~>X$$ZCK4^@l(80spznc=);pIalM!{}4O>I{5N6j9;GeKjoAk zzSYA~^Bxbs!sge*&xik;r+#0t>vsoyzel6yB_4jf&F_PctcCaJH{RB-!(r?vJsvgJ zc=G?FoqvD$K~J!T_N0HAo&I?EMeCA`|L{xUXL!XuL0V(Y&T{tNhvJo@L``gb@S|NFH6_|qT$dw9RSpYiaK^{hQS>Cd*) zuY-S!b9E~``rT^lw;X=lGt6H+@{4Tw_3-u2M$JhceuB;KfIr~5sL5j*$G@grWT(Fm zJ_p{<|8!tu@KyL(9{)JQ_K*GHyS%{M)06)bcK+kxpMNoGp6t$FEp27})xp=p-|3NG zZp$x+AM%p_{A)dY-+Ja3?(}Ck?PCZ0Sa`Y3Y41HwbIK3D8va;M{zuyRcQ}IQIGj_g zai^bBY0LMA{~o@No3C>69}j=fmZ+KQNq@MVejR+jm!swokA2Uw?YkWQ;#Z^QpWNxE zth4i95C1LvIFJ7tWBaci@EL!PnoRSY`XA=#w-5es`0;K&MQy%{{@3A1_Q2o^Jp6@r z{{7*vgHLz!6P@(O!!LyQ+q(_yb+q5IG zeMFu1=bvZZ;@rABeXfP>`EZZG6?w{jshxHN{_B55&5u3&`!>H6{%OwY{^(9SvnH@y=zFZamT+Vb1sKYN$CwMYJ2TYfM6i|_gG>$N|cXD_>==4em)=h*3scMN8{ zA2o0Dq`%ZoKOg=pct779f$#Ala|^e;a!t~HOW}vXFZ1;6C3fFl2mkEHjQwu;lvnKh zx5Hn?8RX;LymI|A{k`y|@YlKdlxuAH_Q!BG3x1iW{7anj!{>Y&HGlHR|6t4K!*}{O zZQtYDI@`W20^f}@(6@Tzoqdy~@b|(0(ZkzDq%eLMU$@BxqhhOPfz_$gnp z_Vui-{%o(U+8@il&-YRDVNd?6?fiSg-wJ<$hgZ&tre);AzY6b}yAUqgZWY`%xUfgo zc_y|5{)GP|-t!UiYvC`0_ZyeC!9NFojwk)0w(eiSe**uZhkw`R#d|0Z?u(k&d-%CF zFWygS|3lQw_V|rV+b@atR{C)MoNOGweSs8L<%jP9-^Eim>2}>Lfgccynlw>IKHrgF z3*QTVhMP}u=8fCn7r?*o;djaeMBRJ^e@qvRto)xmvd);>75;ao z|C~4%zO@na)vX-95Z-UhSpC;#8sy03wM2EKIy-M7H4PVvvX0p5i7%R3Ec z@m*>xVcCV96&6 ze>EsXW`~%0lw1GQ!<_QNpK)x=_ZdVM{3`e?gP%1|xnd_`^MWP;Ko!sGaZ+cZ!)`d&<7YE_)3AcPCSJgP*1Q zSf#tjzvGFV35Ng3&8O^=5d@zJKkyWPz5w2Yf5s#4JfE2be_Ln&wz~rUEBHkHLgc>z z{$t)rImw;=rH=kP;Xj4<>l-onEZ$v7l)k9Dj-6Oz@h(fEcGVy5RJd|?-YHXT+l+^w z4i7lC)%#;Xg6bgZ;BR9e-nM<}($-3Q)jn1b=kwk%a~e_XI4NoqQe-iJ=_@!R}e=Pjvo_R*CJ`Vw-2BOQ``rNFY6R~Hh((^&ep4qLWa+;Xet4e0{xSGl z;QjRP$l~q>_)Jg!{p|cR;U63qGyl`Vo&PuXSY7~s^@TCt=c}{e+l-I-?m?`8Uk1OJ z z&98ueeqqdfx}{tH$L)302KezyV&*j-{z{wQDe~uCvU#a)`KxXJ5`zye_xBGSF?<-_ z&p%|sPl3PNW53&N`xU@{3}58o)c_SFY!-YM-dX#=&5Lt7J7f~F4Xh;2C&XFkse^fT z9c+Xjdq?6iT;#C}ek6RA$3|17jYPJho?3Rn&+sms-#j24ei8g6Az-!RlnHh@bKtka z`>lD3;WsDY=fJ-J@7E7j!M_DhQJnmX92;$de;?j&O}QJs$4dWtY^^i!N? z$?5Q`;Rkx^ppRV#Iq)ys6Em;y$WOQJQw)FHy)pB3Pdk6rZs&90|F+71oL>bWywAUU zo8ZIne(iKO{1o`@9{v7i>(}x$#_9WG=DQw0@s{l;(&68Fh<8Ih z{%||}V)!56{pMkF;Kw}dU%plFcf?2*6DmQRPj1%9BLua&gyKRNJg;YYgplpI^W82&BzEKmMt+WF6c|6y&+_Z-+N z_&XkpnKM0oIAZ&`P4Ii4bKrNvKj^WaGf!CsU-IX~^OPVmo8WiD|J~icMqHbkx=D61 zSw{`Tx#{_sd6uV*53t*K8k2}e>(SAjM(SsFAI*Tj>gAYuyGO@mvW|rgBjA64|JqXr zdt@C55#eXciBr$J!4)FF_M@eCJ6QyO_*Q>Ex(2>2yq}-j3V#W_Uq9IcKTf3Y(b0M4 z)22K9@YTfQmC(N{{O{o(^62l(2Xo<%f6c$0l*9iKez8a1na?hQZ~r&{cCrS3H@sgv z*$Q8|Epa;$`tO0?1MjDQn;znsiT`}IEBqe#MDy7o>F2_~_j=5{z}THDchZkMzG#{4ieRAW_fTi8Glve)EaF@H654<`eCEv2KI+^Y^{s?|}EKqkQ-W z;Qi_-0{;bk*i!~)4}2;7p!faz@jCcE@HHMAR@ye)4nLoF`k(fs|F~TTd*M&`$iE-7 zKZA8G{4$Tc>-;mwzkV8l@BN9tpI8e29K4^OSOQL>giZ;9){-9QkzUi-rFMx0D>4yQkAMSu3b|CME9_lJ%YP6jwV344F@TVNp zXrAwxQ{;Nq1s(hFp3U)%zUzWa_;cX>=KTfmL*f1U(Jc6h@P7Sh1$+g(Uq9La|2Vu~ zKiUc3;so9er@VGOyZ67~e?b03x35d>XwREE@__P3n!l!7qgO z%fDlP_OIZlc+#J2r=JObVrPH80KOESY@GJz+TWQ4e-*snIJ5%(I`~viAOFd&-wp7W zb!jx`xzkU%+SYF;{5JRukB>RY9w%b(JG$Z{JU$?5^Bo6JzkKWC8c+IH+UaM)AJfyn ze;2^_hWG2=v)~8A`}OY?@Ylin&1E*guZ8dI@v)t3AG;I&>AsETzdZW?)7C!*KdoP* z?|iT$jw=Sg%p>oNgPHKt`uocl2zhwFac~y=yaA2=`@isq3~clr2RFdag8#~+pKJdY zKKIPVgyWzXV`A`UXZrIU&*WM2phn;Q;Y|3o@P21}3g9=K#Wy-U`Z?!NX2CB#yU}cW z5D~0#IQ2k#|9%DhHRm+?zJId;ejof@p7d|C>vt#o^mF~&XAFJ`ykGn3n92LE@P6y| zO!&8Q{rzVF{5JUQ9{rqsms#+0&g0u8p8k2O-Tzj=8|OEgZ+ZIvc6*H90RJbxm9xl` zzO!$!6aM7$%!S^D&S78aw@T__gqJ zJbmmcyN~6-x3BZ}v&Hb;;Qi(SbKo=K{pJCy;ID@Fv+pMOS@3@CV>kTQ@DH`1fOa3h z-zooKzDGXZJ%QJ z$KhY`@Gsf?9QcCY`S+hy@YCV_<|&)t?}qoAr|gFR%T@mM+j1!5M-o0A{>dbK4*c3A zd@=k(N%%SN_b1_3!QYdF-vqw`-miV^-cSB5hb7NH9bV+`*MD;051#FB-(vW*B>Wus z)Fk{W_@9!MAO43V{BHQYN%)p*rhb`(Plx|Eyq|q@;FINF44*9jIs3_fmCj#&Z^!oE z583{C6a3|T6VcCq?}q=&HU9O}as=%^37-!CWD-6Her*!I82+Io{2ciElkltH?@7XM zf?tt@-wl6j622vla|37-K!CJ8?R{@f&dDf}>aKO4{APyVa-lmC|e0fVty~I!!FB%f^Wo1=!mozUO~P-1AD)D7 zfFBI+XTP-blINcRFY=$_nIliOeeekQ-_Q47qm;s*z932Y;WOa<);O!-Uq~Xq1^&4t zd;|RYBz)Q^_NtTc8SsxJ;YYwf0PkntQut)~&)4~1<+1POwtZK_AGXkc4zdOQckq7g zvjKi)68W^zy!(+vJ_CLi{1Kk^-Og^`BjEpegMa>|@Y|B`^Wp!RgkKF`pM>86|9ld@ z0e%B~cX$7Hts&CJB+owsUgST=EuV6g?LS7qpL%1X>E~Zc;SX5CcRM}u@7eP6;a`Ej z#lzoZ^Q+;rZfZ1Jr4YgPFDdpKd<%T%-}4=CPy1S0&-M!cR@Y?}0B#!neui{j?-}SNMV?d@lTj@P0m~96nk8i}sWM8lC?=?lzv{ zd=q0U{5#~o%ERAn`;a{%{k#0@ugy5h2k)1DSNM05$mhcENWz!H|04;%2>vyAzd67f z_|m)m_1g;nDZHN#-2*>yWs>q=z#0MGxBT#LCy~#EeR-M0}{rAA%l0?5Y z7js4!-p~GB;SX5lZ~t8QbMN!Fe>wcHB>W=y>?Hge_{=2yR``BN_&xBw;Qj31W!k1Z9B(W9(AEC@9{BYS zH2QuMu+1fW8~&k2^WhdKV6QP&JM%yIEo<;^9^U!xNG^P*NBrxj9R4`?I#2m(?D8#w zuZ3^p=7&pKwy!nth43t89Q{(xwdJ?M-vpoK=C4rlLBjUHKL#I|OaZL&wKTYM;@dQ$4R7*0$Y^DI?o?}eX9wv7xc=?;n+1Ne zz;71#%>ut!;5Q5WW`W-<@S6pGv%qf__}{X?#=}(3RT^inZYcv+KA>=;MoYgB9#!EV zY22gH;`i$NpEMr4R>c=fN%5PYFRXicmWOcbzABEtLf^}^qDVk2sQ4YMz*s#+rGK=( zpH!h%S;y-8G>yk-EY*C+IR3kz)Uj^)Tp!L_tF0I7Lw*{JEb&~FUuy-%V_Lt1^nC}7 zM`*M_i}7GXr7RxriC>kzXs>RKRvM4e*q}4A!qW~^gjKFxI)5E3|E89R!C$)x>m+2< zpY;8DEoka{EpB|J?=RE*5BlD=yH4zX8n)>AiqE&NZU@6OUZAl=<8+PlG~S}|evMCQ ztk?L4#*a0AuknCGRnA9hJVj$)jl(ovps_^bbdB>g-lFk-jZbN;*BD>E`1`bJR*Up9 zRy(jlJ6a9P3fK=?@)iysGGt&dWB90XU4lJ(bnDrz$A07m&3hbtld);UR3pXMlrTKi zXh;~|(y;oUJGE4Lqt)-c;RhI2|MZ5pGOTv&4No(y_U{cp(6IWIH@vlB^*?Voyz)rAvgx**Sz8FjMe{26n>~N;+sU_hZ&YX^rmyTVfjIC zczdHNL4Qj%jxZAS!&Ku)V@J4YyG%9kUfR!j)5kj|Xve9>(Z=kA;g;8SQW5^DrSY}t zgs3ageiMZsPI!tKE%f+i$qD}~!h6JpXXtQiJhbBTY7MRYXk&}k)AE}fw1KC$Pyh4Ycc%%w=P>0{5!~d$o@6zF`b$FQ`(BwB5;r>B* zqVhHn-oiM<*fmYHXRBOk_zMwkRC~iSba*f>e1s0qiVH8*;gjOR=j-sM^LBbbLMDsKc%DeoQ)v?DIY0(hfCVJBagI zB0Lxu-dl%f#f9hT@JTw{^7B@C!^bPdswcyqPDcbmGhVkPf%XYqi@K zb$EQctqiJk;@j;}C#vxHcDqW4$G6+1omBkzc6-)ID%@(f)!0YWqxiO@_*wegt;4TN zSK;y*BA1(Vc*m1fxO_*A%dI;6jxH*^NP+PV;fd<^Yr;i6&aPGUYt>6TZbkUU>E3$p zuETf5g^$wV{LyEbqws6uSE<8$=y1#aH|X#&ap8~X@T$1**L3)jxbQD@_}aMewhR+O z|7~&M-E_E7haf+zyrXn@4;^mlS*gRv#D(9W!)NPotGtis@Qrcdujz0@er${H{L9?= zUW*RT(&1J*ZCL=wdePyQp51i#Mjbvzr!z{22QT-Qt5SzgiVMF%hp&tae?*7x(&1M6 zuj%lt|A(^k0F$h!`hKg3D2j`(A}GS3AczW`gCb7n?&%z7x>b5Q=bT4{HKQOdBFO-& z0-`9chzN+fhyhV_6+}c{T?4NefE5)nf9Kx%*X(vN-Pdm(9%lA;PoFw<>eQ)l@2!3# z)6eaE7<@bU66`sQ77&vCJHff0Q@~a77oezA%iMnOW$;b^gRg^g{pW#i2j})&3%*Ox z^F8oA0)H4>_2$`n(XBMu{$+um0=`|~LGWGRoA!h65%_uF`viV1__i~dJzUTCz|{hO z7(6WS!)Spf`M)FZQ^0o!JP5u^;4Sby0zVIYpTMsL-*%Q@Ke$@p4}*sVei*GoW%~tw z3iwX&lX(1q?*+dayv60WGkbWyJCDnQ^Zv!hqaVMLDGHc9_`y%6?hWrKKYd0}42EQ5n`{0<)svZD; zSdi!IrVS}U(GQ=0KI3p!uaj?M9L>Br&ph$FjPrIs74omTlW}xol>_|H`xrl-{Zd82 zPX*`sFaz!ee-Y#>;05p_z`NiZ;KzZ#1N=ka+&`ZJf6h;tAYTvidGgi&%Q(VU^#jPC z^s8CReyNV7b+XjozxEgd@^|ylpF!}SJe36$(uC>^@PnVm_+U_(PP-j+`xb75rj&+=lbuO zqo3gSG>M2q7EzZW$<;_$;)k5w=VZ*;Jn;#qFyhP zzs!99y7*D>yTN&W=6U$dZKhw$!_UACpM9{0&wu_7&hz<2^l{0bS9F;ko+tVI$|k=I zv)>**4|xduLg?q~^tbkz9-cq>JoA%YY0c^-~}j|I;C|9)^2^l<-w8=U)}`|Xe5qTkkUVdZkabx`gEd?)l@MqJ82 z>*pi?u#c&}0N({G=I{)0IiFm_;_z(fc@y|v@Ezcnf^WZgR{jR!l0BcigV{fZJa3n!?`&PK z_kq*L<)6LKvvDVrKNtL98lWZp2Y-)o-mkZTFN1p_ZvlTd_%i#L$`1bR?=!&HcNOq+ z_AriZAk_}=gMP&LAD~Ct7nbbVcMk*dS?yFd&#OPZkMV0@56`Rb{3+vNUOn}G#>KpP z3%Hn9KmETpE1t!>H}n_6wiAvVDY)cE~^fD0pq*Dx53U6z*QH{#dUeLwhi@FmE9j=1E{GvVMS`2FC&2j_W%$JN0Pu?mU(WeCp8 z{X6u$7yO0r1J9pVKgeA?qoUK}3{;Cy^M zmKs8`^Ml~L-US6nS2NQGVl&KZ{IJ2?-2O? z;JXC=5AZz#f5FkrPSs9UF1N=5z7EdI<>S;_scb+N z{qF_m{^#+3Ew~u}FFKg%;dZW({Ze}wz~!^Z$){(6U;T6@|3&zj$6H1|u=i~A9)s&7yHr4hcV9k5uXRX;Mt7xIOp+s z&Py2Q@yW;EyTSSRdnNqvi2Mb{bbA|q_#F89DNGOCp}L>=R&o9)aVgH1KF;EY`+plP zz~ymP;HQAE3takcPRVaya32-s4ZUz_Q z>Sb=Ghu7!C#ctU1S@0!rF8>4KTlwubvOL`vyMp1;M@;)fiL5^K{NdGD3=H4{y&`lc#>-Wb0&vC zUM+LFKLz<^@DTW3L4ON;3C}00A^%?Rb#QLar@{Gijr33X=R5R=WX}Z8y>NSeCv%D` z=%IhgKTki7$!{0rPm(!3w*YzGj&AU!PtC?*349rx$KgA`*99)!&z9PKJ2=;W8~83k z{+Gn1Jn+#V>vz1rt6t3bE#N#qycGN{a6XR;gD;0>^|ZiWCvZN#jKIY_@BlckFCPcD zMVWpR{LI&DZg5^-p07UkdL}RCtNX#leD#bsFnOM@c%E;A^E}VTubc8ro{vX-zEmhM zE{@FJ|pt zXfXab`1Rnb>8w0oH(dtK{mj=*_kwf3@paR28%)pglUM;?gZ}Ir8RzlF*K4=Fm2tja z`yJ#1Z)03O)0$Kr_7Z0QDeqwX7T9kjF0E6xU&_XjFzaTjq|BeI+JSz`<82K2Js)B6 zoPQAVy9D_!L4Kbg|1tUl>w>(S_*UcE>t%VG59~Tf*39*6 zfG>kzjdCvm-zUg_k?Z*!(_@DGJ>cu$T+d&?w_V5NdAZL$f#siN@Co$jz;}c5JRAkz zC+O*dFMXcr??BHE@SWgG;GgCC!MQ!(1K%s~hrm_WGyPo8GhfQ=UlRDs!I#0gJuBe5 zz` zoR^yg-wnPU^4qzdFEahS9WMvp4$kfQ3b^VWaRxgQ<_-viFu<%pA+ z{rd!N1Yi0JEB9>pEeXCG+zdVfSABI>{vz<5;M|@Y!1sW2d+yXFGTjd^r5d;`}5z6AaEfbRsafIkMl?KY-| z*Y}85F#DImcS7C>z8BmBp5*dhpY_iOd>woT2hPjA0etBjOb;*j9`N1ZyxhmQ z{5NOIJ>r$HAN&$-Klom7?uR7!`nQ-KErL*{}LO<0r{iRVpL+GWZgB5_}ze0zLxYEAWfB{2r!%JLGTR z^5DGOd%%}|Fl*0aTn~5$dXCUA`}Ydm2p;|+)3fP+@a^DS&j?&~7nA4px`@kzb31PU z-!1TaxSqS2o+a4%82A!6*MEeT*}qeeH-hg5=jA5BRX>{5KLTF@=lU<=`UUwLxPEYQ zul#cl*MASwzYhKw_&Rt6{0N<7|52)KpE_73+rVEAz9jHvaJ9e_;L8H9frkZt7IA5P zefNjidW5g9`TF+}@HXuEGX0k1&-e(F=kf4O@Dcbmkbe~XxXYOQC&7=Vb(N&&4)FJa z^XHmYFK6=m!1+4Vy_4}zf%A3ZAHn%Lkv}i=zK=5b<01bM)c5n?yuN(B{5^1PKVL6T zKgRU%^)lagJ?!I*^K~oV2RQBu#>IVrHEUxGc`!QTiwe*yjvaBk;+z>m0+>ED3- z@p|UZ9pGHv4t@>z&5*Bw7q4P^#^5`^?+3pC>)-3a+tS7b{d^Acd>`YAYZ&M2YkoiF zXW*3f<)5FBnAGmi{4|pUuiEC2B4$36^Bj{(R(Z-@Tt1pOZezZ3ji$bS($^|@I)?*Six z?}GfZjI6%LUpFiN3h>tooIlT&20spZe2{-5I1Oj=PYwK2f}VGSe?#C`gYOmix4{1a zej)Tf1inKG4Ep&Y_zS3mNbR1xp7A@t`M%97zQ8z-E52`20{23m@7sJ9d>QfZTIhe| zhFQOz0sf3H&hj^dpCEAQyB#Gvmj(VM@Yf0aKJbyi{|0`(z>hRD{htQs?P3D|sUV*O ze@x&*@WXFp_2TjNKJbr#^L}wH_>BU;6Z|fL{{j4AfgfsN_CNimS%01ke!RfF;I9>U z1w0MT{qQdEcMI}YfnOo;Z-9SQ;17V`2fhn&awr9u)V`10GHd_K!JqNvS?&WrLEv@p zWr3dq{yKqQ13nV?cfijV`0v3#E$~BM&Fue{z+VpjOK|Q_ANb*4nXOkH{1pN}2iy(* z82a5V@Yj5GR?i;rHG%H~?+bj}Y0Uof1g-}Eq`<@AUl(`>{O1DS0sePz?uT9AFWW8H z5AG27KJbjdx4nkhzaeln_=N%wgYOb}2mCt%-vR!A0^bGxq+131!H)*#_1y>V6Xds@ z&g?IN{}b)22mj62X7$IxpYhFEJ^(*K;2!{A7WnnxuM_y);3IHu|DVCH6y%@#T4w)l zaQS;5s17FZU)(-x=PLLO-=5`T@E-{LV(>o*{EOgEyJJ?*kHKFo@V|n;2AtRH`OD1y zh9GYOe;2s?eG62tGr-^f-Pv-_1h?Ed%P#}3?wRFZ0e`!|e+GWJ!2b{8iv@SFxWf!M_6jPw+dzZ~h_E^K0-wfnRnP;}?M+X=8Tk z?`B;7egeAo8t_-&!#ICmrUbrvFXJzSo_B)x1b!9xdjf8e))^ZGu^&g^vm zwT@OKOR0q`pX{!|aM^Q!_s0sKC2-i}W2r~hu&o+9}1 z;MbyE&H?|SAb$<`LjwN}_#wZa)&G0&mk9h&S`bO|*wY36a`2SEec%IdUava%#|8Ow zz`r8!Yrua3&hyVb;ITi<+W#1M3!JYLk6vN+S00{~KMnkC0)IXDWdeT__)P-;F!@Zw9p-5q|aEPvqtwvxYu>Zq6jSha_$>hbnsZrszc`5fu7ZE|`|RI-lsf|N#aV&xWS3PR z0^fEa8NtHt6{!_~ox;{Cx1IhFN|8{W1n_ z1wS5K^K}N^gmQJ@$Nz%ygHWym{3Qo7{l9{q9Qb>_!~~1%vg)nif4P_mejD;12fr)K z#?Sa4cHzkpW`8@%_*2*~)rsJ@)R^39XfGdl^efDeH$r~~{G3}Dcm%u& zzVBfMjNli8zX9*pP+E|Gz6}2R_cKFIfIYtif68xIx%7RC!VeSh&)m-BZ-o9U zz_qVr{7UvqbqDxOBL>ot|2_Dba6FI0L*vZOb*C}~$ z|C^Y@J`DRW0RO}bn1Wx!o-cscka!~S&wb!O`WMqPLAk#K|Fxch_kuq?!R){K_Y8!f zUjzP$e=&X={QL&+3;xdZe+Kf~!B77+livpY=Yd<@O#V1-Klp2oWPr>=p+ z4fsFyFknZ04^A@sf1YFd?}Pj+z`yk|rXR~NRT$j+T*hyL{2Acixr}i=`=#oFfA?CZ zpSSz@;CJFZ;`Y;+qTS%;z3ktwg8qHrqdOTGf&UBKvYUbPQSPy;tX?<1ft7nA{BHyQ z^c$Jr(O6)tfai8HUSuCrCBYwf9pfj1Z-C$DXMpF)^TA69%s%YA3jEQJGw@CD+rh7_ zFz|fnf1LB}jPv+Eni7#T{=R07*>g4G{B-bd;K9<LqPwbY0RO<-m|T#3Om#Q-*DMS?z%Hx)3jUSL8Q}ATV^geN zpS+UEeZo=x>2P$HKsAu;+a6mttVK5PCij{*;GUy?A`y z4SwN(DL^`=FA!sPo{fg8bp2HxAtz;)nHrw>Z;<~V*< z|8d}V+{y&kQSPbW51z#g=i5~&aPPO7Akv3w3|_pM=|MQE&IDijB-8VD_~FCgU%7%cpw;+B3;6F03 zdSMx+YJ*?6&J4k@s(LT@wOYo}Y^u+LKi*{g3+$KbKJY*Nmg)Z>_+#LQU&X+&sKzmG zVD|s7naT5hw+wz;l<^JZ2Osz+@ZRzD(BA+LVm!*TkEzZDKk_R~@Ezb6fM1J*_bc$5 zz?(R4>R9OiEx4AVOZxdB{CVgZ%>F6H**fG;1wU+qDaJ6PN`k-c^Gxs!C|CM!A}Ma~ z!1{|nx9~CWV~b4wIQZ>5;NAcONUy2~z@HEMX}FPp4$Cn8XWz#D{Ymf_f$w;T0p2f8 z2S0R!@yp@=47mCx#!rHt8u(v-#6SZ6IS2e&EKpjg?=J9vz)tSBd%!>Y6Q+m4M*jH~ z_!oAt`WoSfr>?VlxoG^6etvK;`+e#<L&2J zu4nul_Dgj?_&G;21-}OWC-|RtGX68@e_@{4{}jxJuLQS&|Lz^Ez&!l94*s<7GeJII zdNcUNzhNBBrur!OA3x6w`6cv!75shgXMnG#egppA-!b_=!v24OzX|dA3hH+e z7a{$_E~JH{-Z{rD}s;^FGF(0sZd>e>FBv+|Yj&`0JZY{;)&Yh1g?_&Qnx{vOop80dK&xVg^C<#G5<@aJKmy8-eSf}c$JTl%S^ zUS9-nM;JecsZ-qx{_r|$aQS-yN#u9nCt&@;^VOlW4w3Bt3?{tWm^#%9!B4!H75I9T zdn$2hekGpAZi9U7DNOJ)(9;C3-^TdQp2{w~AN(H}DEZnEL^V`@CNAyfis$4%jRq{Ky`KC$rpE+3 zj|9J^#|nHD<-QvH1z6v`7~D(zNn4z6pMm^)?qGr+;eG)Bp9$j_##HYFe;pilEaX29 z{<#O3{QrU90e&MUo>;D{{sjK_7?a2LrRqp(IH|tss~Pto51$79cQxbRV!u>pfPVrT z&o_f_fIs^TX2|-X?7~j)%T8yU$N!hW-*yS(??>GJ0Q@6gWuN~r^#2L`&aW`}A0i%} z(O~u$?_h!_K;8iUn!}hppC^>SZ~Y<@Y_rR%*g2A(L-%j+L(ln;zh^rGcsxsWE4cM# zjHe<0Ao#VYFa1;gIgHjJlApB=R^VSz?$O}C73NDW@a2E9it=%x27V|e2(N@6J_7y@ zOmIGdaz6#W?{Ox`=aY94Km5sbZqGRf%aY5Gf0Xm980YQvf|it(o~7DZW_b_Ir8=3o zr042SFnLr-$6S_1^;RpZH@YF8OmO7L;6meUAK@kl**Q+4^1qz76MNa6Pwz?}Z;Ye~k0@F+IyL?YUi* zYUzn)s?@{}m)@tw_grv24vCZf%O9Wh^GDw%>7gJOzQ3a=%RgIn(he3^NI$Ccq305z z9lr>^e(tQD_Yjxj;WI)!s3>1c_Uu9Ya61j)dxiK+aK2+!e;@o_!JZ4iw+sHfiR&59 z#>0=n*9WuwG4R{bkGP$$pb5NW|2CoAb>h$2qFn}%-`k(n|9(lH{1d=D_L=zX<#Y6W z9(o=}zrgfXbqo0N&RPH52VTLsUfljC4Ve8M^dsKCwBV}CXZ3`^w+s54;ExObc@J@^ zUdR57<$(uKug^h#8TM}jzgv>u!fy{l{t(D>`(H9-_U{nlc?G;9@Fw^^2jg5Fy@v1io8n zmq)?(3HkYXV`k6UA7$;!%QX|w`YLw5cIzVeECSGf1SoH>E{FB z6Cr=z1imEn>wCbr3vqQQ&BNq=C$!^hIfp!tt1|fGLVeExe^AH|yTL0$JN^b-E%^V1 zQ)bV0!Jgygaqx-X4`n0yt>QchJsx3PJ5%OIs+Q0$%d$6Bs-2KugFU?eeiwYNApd*t zlTcqS|NISR|4t$A>A`ovZ`_}8@I6Aibck4PmsSA{6T^L1$?j2UN3(mvvXaD zpVNtNRc@HePiEt`34Rj#FZaWx;2j|zzQDO)=l$Tjgns%s_}N07Join^{)&({)Zj-# z54Y#_;7iEcyxljzw+s3CQt+L^yzvI`icqiLfv*ew?vOVlo-dxw|0jT}gnFgH)q?)B zh)a3(Dm*87GvfAY$S(kkDzDwxWmxDhjlzSI= zM=1AcXEA$Jm(2R-rQkbx%^YNqkZ9rBhfCe0N)AyoX5cT3jL@Dz61KX{H0uvFn_xldow#ot##&o<;q zuID@8D#356cQAW)3+?z)@LhtupK~D(_qZNG{*%O)wy^U?$nO>M-aha(!EYzLli9gP z@Z0H^s#M3(I&>%IAw1q*2l?%S{`blJNR{|KsUIf3l|Qe8o*jaod%-7yAO1Z@&*ASP z`|11;{(YSX!6!=+r+D5j^j{ZosohV!owYk3*E*0lBTk+KJs$xN3-jb}fG@*No}V8A z-z((vL(gXRm=OMrozIG=49{{J5M?LxUveh;%}9rG)0 z=ZWAdLEZuO3i2HxpPVPlL+F5CAA|huLj3<2{3Ibx{ssQHz+XZefKq>n-^u!m z^gWl-Pk^{&|88L(RRcdn@XzJoR||ROCN7WoKZL1Q{Ty6{bIzB+{|UZVm{%P2US`jd zFz+&g?-b_Aaqw-hljrjZ_&y>3e1_{0_VM<;P8!!}9|_N~!%Wqc^7ys?dxuZOh3}9a zseMz}FA9U-1^qjOarQ5gJo!I|_gE>7%Rk4Q$Ld=V`rWH#PUk5NkqOZ;vXI{`tVhlS ze_V*qE5XA;e!Crfr_f&i2A>G=`NH=xJNLlPyr0^@*M)e z&)dPxLL5HA^}ugj|8sV*b`4yykO@yiEouB?~^%w zF9YIa8SVaa=m`t@pLag9b6Mz*TF!;~rond#`F{+q7V3Kq_~SzR{t$e-&`%#BzE!;r zdOx#sx6tk{2frQphsVh(_?jSp9{AOWPcDBm_+B9o+zYM}?Eeq=y@Edt7ce`|7V(ps3;Fq8@V$aPe+GY0$j{q8!0bFy z;I9C`8hW^$Yv7Ly^%@YD?Ej}QFWL$DC1D=&Rq#Eqe;ew%7yL${UQfM{**Ou~@zvn> z3h~wk-y_uP1K{g|{oe-PCHU=O@O?sl`=5)LJxjv4uL0jH?3V_?w+r)=2KchDpYv|c zg?@1r_}N09{{i^AFs?lezEkk?|6I)MU&s7```-fI5%O~r{A!`x^TAbuKfeLKL+CGm zllhUVim)zt(Iw2Dj-cNKz8CGz$Jr+MHi3TtdZ_vli#-1q z#=B#{mxT5`P2yXucVfh!gV;XsR|R_Zz)tS}cX9o~{?!%W4+`;l#~l6lLw*&-n)UHlg4B16(EC&pPTdmFmS?#Djsj)K7N`{Uru|yAbDRgD(s5 za4q;Q*w4qid%3(|=f61@{CxD~%${ZBRj$Vleu%J+&VZ|gy!2-9C1D@+L&T+V_Pf7e z^8_^txRuKb?f4t;eFA^hPWVm8V=tFD<+0sDe|#S0zBRh!lZJ=deQ75gTT3jWh7_(!YYAFV?Dv<7j9Rj zzwDJ_zD_d(Qb7Y>G3W6)TybqEoO32qdXvkN<8#1Hwp{eK3dKn#lMjSUJ?m;j?b}GL zg_C8QIvv&VlB#`cK40wjtGPxolX0f|o$`iRuj{N>vW1pIo$joJjC^({>7|-9uyfxwX)Z@?6)R?Y z`AQ};QMWBwe_!33=BkF+v>EDK^3}MXw^(D5#!UlhT~de*tAi}Hm#xvxlgqOGWKb6f z1>D-6&DxEbVojfVb1fjvKWS#=FSb;@?0B_T9TZjVzO_Lg4O4UWGZ{}HR9Wq%G?74- z=Gj_Xeq|hUq`6h?e!MsRFT^(uq?w<6*z3t*z7kF}%pJW>Yfk$uxwV#akjtj?+=XOX ztT>PbHzp;YE!7$fEqPnEQ?K;wp}MA2ZWfzuT6&RSy4Y)zq-ulx&SYHuQKb~<=K5o2 zyg3c54zf*0k%!a)!S67qSJlmFx?pl;jjg<6v`P`15Af;@JB93^815BEvFZkO087qb zqnNIbP0?(_Vb@ptHhmL-llsCG)uB~jz z=CeLtAxSPuZW~c$=80}9EflHawe!|dBiJs`2Rh|)CD?QXR>NAIzdH$bE!jcc#4AsW zMWvSA)ZFenm~I4FAsTQF2jQ5to{L&?mRuv)jBkwE-0G}UGisS=snRleMy*IZY|WXo zwYFx++q9eyWP23t6GJTL59)?Vo4xPM8cjj1x)tU^d0L~!+lfAuy3;t z_10Fq7`E;gl{NOvZkuy;Iu5E$HeED3&}BDb`iQDm?8~8>57`~7J&iHt(ARVYS}Nx} zx+!;XXV|YujYseRp+gq(!J`gYSdDR6BRP{HCjl&i(t@;=C< z-ZiIlD?LljoA7ypmb_0th#P`jT-w5k4YK*VtvwjDo2o4RubTI^@|n!Y*bPPdwAS8e zcs!MmU$c_3+N1n~(t>>M!b)pax$T)kwJ#;>OeWe-X^Yl6)y_F;I4U_)dKz_^dGDI_ zWz6hI8Y`uI*>1_Jz12kD+Ky^1`G};j*jlJ>vk%T{jIOjip&Y45>RQDhsddz&<-l}& zK>OH|+0}AAk~HP|X;ZgcPfcjsMa`=^tFEx(FGr@SrZt(0ta_@Un9UZSS`Sd>o0u)x zMxgBqcUF5&O8*mWe!7z6J!@8*C*VyMQnf-lwc4-8oAF4pZLmdz_;2S1U014K9vEDu z6>n_ZX*Fx(1BNS1|aShBWOEk3C_Om)g+gLJZB$p)&m11sXTWGjQ=25s4m?Q6!-&>600ht;hk zCF`?J+J1F|B1s)i2TL@Bhi#n$yPb7l?M*1+yNO}eSx+P^`SH-(tMisv7{cmP6eh>| zjNuf8`L!XVqit5~?$NZiUmOmq4ZguggS(?TqG>Ld7`Cc;8jY~+gOBwGy#qfa*}USW zS(znQ(K>Xkcp(ur#og1cIcv?Z<;2{}v|QY;w3uyjLnNJvqG`~w<@RL6DE1G;X!dM*}S%Wp59m~5Yb2O;Q0}Be!XEbVZBkUSfn?-KbY>0XK zE7e3IPveTmR!C6l^Nj1UxHwQ)vWAg96bU!n-M&?8sJ4UViYsN64KN0Cj`6r=aHZ4+ zOHNbX&|3S}bR@&pIP&siKRnYbjh<(9P4J%V_rpR3+Q5YkEOptVW9REo&GW1c20wREvd_lmOJYR8gqM!dsfDeNm0 zw1)A}>EubB-9?bbFdpudK}+o(1^og0>0QU#M!#B$M+z4aw;D3<;vY$EnJ$~ zHCkOFrr|?yp*^7gNNPeG0Jfwpu(}bW1@2lfP^~woelE5-w$T<_v+k&trS)JST#Xow zbxS_4t2Pb6ysNGWlxa-fY#`oZwB1AZ2eXX>8q6Nl^|Cfvl?`ikXEofJq^yNTnP=~L zS6Nb;clCg*Bv0c@jo^yEXAPzYRpTm6(Eag-+sy`#c`Dcj&AC3Nl z0Sz+;R65br*&|^}N?yO!+is*o{YlVEH+@*O=3Qk$IPWUklAL#yMd-Y%xF0n4G7G`E zmswhxdwH?ln#G1|6f9?bYhKw}%u(j}8~1VMetL1mv@A&GetL0h;vUc3PcN>wcCpux zQ|2kh{h7I!7h8_IGjl(^*mB&vnfGbjdYN|_cW&lg#*LY|mlroEZp+O5G)uK}FE93* zVR6~o{llr8vqFWQ+tbS#2ccRvNITLiy(CS}511DpFz8lBt-ODAC9NNIriI8_-d;7? zS$dmi>*C1O?;kU|)-{^(Omqfc!j&>vvTNq>if%$PB=!Uc4F`BSB5KzNHEwHpXf1Ib9u&hJ%Po+VQ2QCp6=LAyK6*tXiuVF+?2tc6buv3(N`cEs6RLT&RG+fAmuU2-DC8y`CGy41syCgY5Ee$#vDn`R zbw-c5-Mp$8O!APjsJ2E)%jT-IEpLb&I1AJ}f_;51kf`^^mDRkytuF*DIqAt6d3HAM zD)ZpHs~9-uUS_VDdzpEE?q!9tS)Q5uX*M#~q{$pZn>>KF4kCd7suVu9$wYa>L>lGc0Z~1MB7UOv4TJxtAB4 zhTGZmK8?G%b1yHB7SrOg&1~42r`)`_-sZ(+TNZy>qh=Gwd4+4#i;dH$7q_WKz1TdB zdT}&s)QcldqfxLA&pFMlA6A`nuPfvU4W@B(Kd;c&HF%C^{^u3^i@DnT&nx&(ui$^o ziRS88=+hd7POZUnEc5j*?mHSh$1(Twc#dNJb%p*xU(G9x=yN}BR`8!?aZ=ML^-s&< z{-agszgmUiU#k%RTCDx%)nALn-`wj;gSbNf($ej$MgG?+#J^TyK-1zmt-1E=75r~t z%h$P|H!RMtT7~|l#dBcu^(*)X&wJ+A>Iy~n%w|;m|aQ^kh^8=m2_@^@~eBQjc z|LdrK1gp(Vs3bomn6X>!9;eQ-1=LfdKS5GY!`Bv2yA@n)=;CRhLDixcbLh#r72Vnj z-N5M4?Z=B=<6&x0@uk*U_D&>Q{%=%hG<1tC zC|hFcjDosmz0fSvO;c^zVW-<%(fF8a*}QQh?=-De41+gxhDN1ykYmL>3$tO zFq6sD1~sE;)mky=%gJynYfafSbxUs3o_?F&Aj&i~p_#am zF|?>add_`|j|BSkuxrgf&`~(&%c%kF!X))cdd$JlEc3fvi?m{kbY8E;9_ZZ5i$h&u z%AznMp$P|X&zP-~wj2D~R=J(1PSurGsqIZ|Hq)@xr^mG9`>=s_qwJhUsyf$IT4;1F`Eo-2Uxk^%JO_`f&+{0bHlKTW@gRz4 z$>)B0@erf10#TR%DdcyP{4$rV-Rrf-|6g&fFfUR_`wFWXg-MaZoJe6xWLccb6&6$q z>m?eQ7WG>V4N^#A)5b93HwH&0pSRnJw952krG7+D#Ia3V1ucswO&Ya=6*NvPv{GSG zrcqe5Y4GyYynxr>p4|NF3ic^1`ZNlAvl`rZn_s?#4So5rHB1jZ(n2RH%q%qu+prqk=bCGzLXy|u4W#*>UpxwE6jrtxg_WyDVJ}ysuyWNX%zQNp z8C|20(lrWOvKob)ufZEebN#C@^VTS=j5W0KrFShdnYDVG9*!`!t=>{8YEEc1O{*^7 z{O=M4y`{4-Mio}C8im!XMj_2>%nFH0VRKnSs{`2wdS||zP3NXDXMdz0nXNj1GvwNw zf-bD0W%1~$RoH;msux#5tFVC9Dr`_|6*j1~3b|ZMxx7@R_krlS-eP}B&pwsa*~rSI zS4i3{dAG?N+nn<+a+6jeWoz-I!@OwM;(gS)*A@1uwF)zDE#5bsuV0~=6tb2oT$5@TD(F}s#V&xl+U%&w%PrT&2dKV z1NO0h5Hk&G{!zV8kGwbO4UnKw-QE9=6 zc^K)8P5RBKW^QhG3a*gb)b4Z#p`6t;Y^g2T6}#Z&{=m?#^6j4iowaYF5@ z+bmM{v$Yl7nB*ws#%wEv9zC28@daud_RRt}{oKs1_G9j0(3dQ@eWjwF9(k`6r~jRs z6_x~Ag&Trela3`ub9~@cH)@fAwK3^W=|R@2$(q$Y(bifo;IOuvBifuWYKqQeVxy*} zr(yq_*``+E7KK(}5YQ^zVALvX0cjN`>{^8xyH?>Ah*n`ruT>gB6z2RogTupgts$KH9n$A$XS=DTIN?vPi!>|&W zwxg+(ezHMNum_?;dS&3ht2K>o+{JTB`!B1lu=3C;tUPoIx1)3lD-)fTi|MM2zPg`B;RIS@Cgle?q+Dhj_HLKaSvYBTWt699) zr=uOU#h+KW;h}ItQm3%-rc+q5>1atdyZKKOB~m&yg{u1Qz*}|1niG4}l4Vbi&TVnp z!DBP8N-gTFyWB>*J?EUX>E)QU@oGD^8MfTz4x1(%(L~*$gm2BJ9fxCUp@iwb%Qgzz zeL98tfzGmcqeG`Kf6yt+A9OT75MH%itqv*_Z1kqC^p`!GuM`&sg&I$X&hxgJUi?v5 z-x#HLe-`g->I@o^S-iETGg?SSIzP@8wFaCSXOLbSQT6F;hN6=W-k@i8YkHF}SF8?o zqY}Lg)yoYdx(&ClOfJaNd1%=U51n6?GIaZvoYqO{G1f2YOU<-ZRqV9$l{mcwD7{M6 zF4%jkYyEu4T?{5G+I(l2uG8yb^qK^{3Y^I{lMUJqt##9CjnGA@eXW@(%qTCs|m~=;(!FcKjWG zlfz4Av^BcJ zSinrBVkR@<_E`+1 z$&7sgohjY+8&?P6P=a24GbYL!vsJ~;p`&+vijlS8Bv&wX%+vem}0SCkHZp%?GTVN*-CJWWNbuAx8LubK0c zoGGBC_eSM+V#%$oLbg|+GhgT>tyrg+ua>IPRz)&t_j&atpV6HPPa^c*cBkfzNv~Q< zk+#_w(WW~dYib&1ZMrllmUI;<;mfxr>Ag*yt)dYM#A&9}BHGEx9PY7nwI#v$7H*k0BX)Tp_Kqm=D9eQsf5u!s26Ln`{u+kr`Ib~IH zFNp_&vFS+PHBY0yhR0CUBhAbp8ZavI*=<%?|_zL!Hv2; z8*eA=vA9#y?v$J(3i@KVKtbGV)6ox^LQG?G>5`KgouRQ3Xf`X!6<@3qp(vzRFY^QW z1VehJe$7*D)0rWimcQ*Wxu^D;hF(T0(tSxCV#*uJWYVd+Mqk&_SuE*f!43C1*I;7KhEIHR&+`3}UXG4Cyw>{Oibv7LZ0WUK?zdTl&e5#L=?n3qC zPL6XnG8vy~7%^t!zR{{N)Cwid&OkdDj8KrX1BB!=T&?s@@;w$UAfn&y==UiaqtT8H{=wp-|ZzHhG6u6`h_bA0i{4mJsh{rQ<8| zPFp1$)94Cx{*ZUA9Wqoq32R3VK6;6@=%K)t&OfMx#{OKuR9wrZ%g&XaXVqV<2I-7L zsnMk80ovxyBwbi@TD@7*xbA7t3lSmdXur*lSj$#}$&K3TaA>6rsUKx04qcW^-fZDoN#~68l5{s*I&Z1ci2J+s>RQV08zTJboddSe-o@yMv!$RvyCCS!)mAZ>@;0tXi^$g4Z14Lj*C8HL`8?u1n6B3 z>9&eBKd6qRJ}w>e(;S9Ot8TYRrw+$gRy*xPt1X%fcgyFSE{atBmJMg0I8C8w zI9Ok^(7OrKCg}$DUFR;D27JGUa8ThV-s5a zYHToV6z%dkGM0SOTS_>c#&pSO(-mR~_t>2G(@`Z-2Bx?DZBywy9~FJlogJ3R7bDv5 z)2Q9uQLwmXrDG#v-GW5@y^9lf6!jEga!fnvt=Sq`rY#Pfy<{NP3`gjuxM?gEP5GqSq=Jn4cw0&& zlwDYRhv+a)t8@zN{?-lDM$t8Iz*lUg1G=?Z#9gk};$pb+VxR2L)Tjz3K}4 zQqjI6pK{r2X@7u@>q(RUXprv>60M5dISO)rrjt&yX1lqQpvizgvYH&%b%usvRFXOY z4d-;A2_lo`OD#l2rx9wn`Vp&jlus3OEtkPxTHP3{Cd~#r$Aa>pHkD7&2HtesDi+=8 zWHr$=X{p~)9j%1|oxVwRrB{sPcZGYyRxwm6QLdt`lU9#5bpz^LyBp~it==fD-wI9g zIfe_ooytcHav)7e=##Qx{2UeOcp){tb2J&`)4E7#&DXb?nt^;}k|@yw2~xe}vx?-L z?dr9gfh-OF6UyFeuF}A2UFlh)+2+I@q8F+na@@%sd2v5hNwuIkiFKFxS-qNV|;*rFq5rNE2n%}I5!>Ksn(*+#`QZnre4RZ17qc|=}!GSHt+D(+IK zP;>MPd8a$np$u4HzG5$R9WakMa8&b7VlHpdn$-7w$)VpkEanDPZ&_8GNHaegzgD}Q zvR4z#RVry+J{k5@yarh_pYfDkW!EqtX{^x6DS2Zm78~~pJwrr=gW)!lerZ%`k>3ns zXK7<Gaz{un8}z&p}=?)w3%}aekdtDJ zT6gPt%FO&@!xr!Rw1Mz2n9loB#)PqHDy&GUYJYzVwxuAKeJag;*`$Nw-CxX(iqv-~ zCgnrQqBwp!Ug;IH1%EqFCo;%qLa_x7OMm3^P)R#Syzj`Cn2ss#1tZv(c?5UMhBkgE4GR(Ybc^ zV1;sIE9!4`SAF#{#g%-h65dH16*TlznXzuO76x5?+*(K{YKh6_%$W7kMsr;D^b3`E zzZaof39j1EJeCJbO0ASC=^Uy|rY{{-QSrC(u417)-WVCC;lOdp z8n3ov$(5L=jN_*HSM2A$4YgIj@Go#YsM4_mOX;=Ll zu|kX{X$LGd^oCM+;HsqbuIwn9O^qFuXo;phY{@o=wMy}|k;y$-YdfYcN7IrUZFI}3 zO0`^37|AHr(SUy7Nr&p{#Aa*wY=%gX)@GAlb7K%~+Q+Jfbi=Hl476h-b8WJgUYXF4 z=E~O!dg_OCzFCaMFP^?=VjZT*yes8x72BRhZDJ0sjk+_LB=57@z0tvN%@m9UyndSU z>XKF(?}mA#NeWJykyI%n+zx-RJ56ki=>)D^Z{%v^UC*5Yk8 zsAV##*h+e(Tuc}e32ioOb_Yj&-Rctt)B{Eod3`I7r39q2 z6u1K$Dc#Z0bFrREiRR&S2X{5P5*+)6K3W9z2XxYWu-c^G2ln=M&A;l&c6@$Kt>c=k zBx_AyIZ>v`1D&APAB6g{qiO!T^$M5z-ps~DsS+|4X}cik&K6QbdhR%Ept+)fwl1iP zdT2r_tDCEhR(+JY4ouMU0(t*B+Z!JBd(&>cPoY1VWXn2lq^Fg(eFoHbWjBR%u4r%7 z=Svi-=3zAMo>apHnjXND(fwODSxrCbiAS?=-qxlaN7ErX|njg3{J5&|-J)z|7~Cv;{|d9hJH@>yL(d1+_;?sL{~s zKts(Z*Vih4p#jY(JMI;?!QPH+IF0&#-9_Vg*ln`1QCC{5SX%`?CQ+j#jB(S->cHXe ztyTuj%37>VGdZK=4{6<(qWc2*NX4-ljg?b%Psz8M@46cO2raHD2yN{)9q35i9}{%; zx73@d?n&u{Hhn#>&rde`c2hQ`@rHbZkUP6U8IXndoQdabJoG~LH99*f-*H=W!+Ld9 zH*vOX)>#?p34+$N8uA6p3A1LyS#p)5x)B9`C0`F#8;)wfBSjChs!uy}2D4gkG;O3> zI<3`7XWP-njo$CKvOOz*ReA(o>uWpGYb!RrzeyVlVUOS4q?vzte~+_j$K6^rT6K2D z*;QN7<}`bQRe89Q4>e2~>A-`Ot5zPbsA;FezoF0N)ohTU?j~kD-3cHx22qs{`^P z4e4Wf#T1~Ei|G_wc`q_bPeJzzzDb{E%${B(U+~1}xqO=U#M^#pPs-ZQSF7?W=Ls8g zZR?sXZAyimXtuy|AdPzzd-8e9ol>=5AJpmK#9nJ9ykW@ez5YS8ERX6^ z%p*f5;?qgMtf!+_y}q$6Y|^J)^feb~cB(>_TwbB6iFz2D>Y|-+qDuDxY`PVivr9RO zue#~<|o?pKW^-7rzsm&?^?jAl2oSEWuMtr%(hS6g?Kx`kL?Q%sfAY5Qu(U8Ua2 z_8G8nrW1_m%QNZ`bGK%m7!AF`P?|qej+J&90`0<(zNLdN7i9NP7*jasdo(_`veM$) zy`5m!+i~h0(_+6w$69XKYaL^Qx&uv8 z^7W2%1~*NU@>5T=77nI$&0ZnvX;!^G=i25lPAA{)-`YN)NjLn1{7Sc3t5A>frs>S$ zbihrM7kPSPr&Fg%u|HLhw#I=vO>*LP+Wj)n@us76p9W8C%2nNEb=2$27o($olQwW& z2~Wr=Y%A$DYBXi;~2N-sjw<{%wxscA(ubU)kEP341m zhxLj3)<(Kd5o!BN`9?kGNcVer51j^Qqz4M5xM8s-p9+W(Et2NrTZ8R|NXphFaU|@Bvm{vz<`_Y~1YMffyM!`+>1AIuwrw#@*fKm5ejnX-y zqfj+l^cFV;MyqpUt&6Rw{kx))QEW*YqmJ7sIvaF6-po>>FRkLFInWb!GX1G=N;@jJ z%;99Oq>09SPC5pa2DV|3{`1I(+u5u3YMS;A>9F-|qvz60lDddPlci~>lm*x+(#cRl zN6$d#2A)95R<$Q>hCyIWLs3pUIK{3M({en?UAUAmrW-5P0o@Jn71etBejb0p-KP}? z9n5~f?qA**^m|h)5rf0)8$`O%6^}V)5~rdqAMH!ip`z7%sWtGro5{3s)wzF$$?wP< z;9Tk_a(APf6XwRMmWF6;%aHbDCmu`ATGG>JtF36ZC8b!Kx2tRD+O|>8R!qfKU2#Ko zjrKJ8Tr)&R)k~Wj-OAML&aP#{bX&{c?HQA1J9*0`P1dL|>wwZ}Zb zDQeEs;c5Pjc+5;s{~5?lbY?l7T*t=xrsOm^2U8=ugNcmOgBxxhN0iM=WCP{#z$=A0 z+k2uTbDG^&(&l#zQk8Udqe~C5jXZP?qI5ey|vg>)8goFCmShb8e#QgeH`mM_2K6=m=etw$V*= zbB#f(KXujUPTQ6nne*0sG_#RMavObfnRJhj7PEuoY9gPU`aHBIvW4{yb=Z=pd8~YE zoPuKPR<+*zSX|9GQQwr1C;&(^$gjaNEf{E0i&i*}@> ziMzZ@gl)4Y%(i+lqa|NoacH~AVBcrfYr5XPKSEQIxR>rF(Mh2tdeXG6Pq`z;R?$h{ zfaVYT#u;fn({AN8blWa%HJQ_E(px4pL&~StNSsFWqQg!nSDGkG(3h{}Gx`2d{t`Dn zjUNpA-ay!&$%JB^GHru|I=xl1wDySozt*lD)_o?chvzOJV4(pCicexCX!b8V@#{(y zn|GY|!*SwBkj=x1llT=sk~kFwLQ8|Tph6T|p|6AnZAn3~1qBV#ifS9gRumvnA(r3x zac^!uC)a<8NcWBt`^=1I&YU^#q0G7r{o8g9N4&5Pk1{mRb6c_ick4Pa>LEusW=>nP zj8Pp+^_A?;+GS1IdPr~|vW2R7`u4Z$ZLw4){szd5mF*4U=N9nd2@J~zp`5eqMb*5e z&U$@u?KR|0rxO&wlw=waY&MymD}2sKu`!Pco(Uaf*)hLmuG+pKzbwllryA0>vU`=F zvJ8D88u~|B^XECsd^46L8SLltC6d#h2fEanY3;LGsT8<9@~3Tc8{dR_;{bPm^==Me zi+}Y2Al6v9uU4A6B7xoxiuTOjG=%eh3oKF{$F%0CB~((ruO+0QF~?8q&Z?|1CB6Y6 zZ_%p_uA3FpKVuneOLcOlpP?R1yD@RR^Rd&cqY*52a9N2xamrCp&%1$<=XuDC>IR*zbzLsOXth;+D9G0ti_EYgdM;ghy;Y@4L)`c468w)YheHFY$#QcM?o@B5AWEGw zIRjB5+2RTDw0!UVTSRz~M|&gd%#Veui34gb7t_5?0;VdXk0jS~#7p6TwlE?3L>5PV z-!06JWqITyKK%NB6I{E74u~Er)JNd z!}emWz-j>LJb^w}_g3YISPP#T_?599?oS_&fDpA|7K+SvKXhudlHWBYx@RhL+1)b<68)eB7hs%w*1CIOPRST1RFho5dn>)ucjqpfJ zL)S2YOHS!*L8`aT3RVfwdJw<(ScP?;=!YZeEtJ!t*AoN9F&ebuKnT|_O7*PFAX$XH ze-OSO4}7tHG-!2vz%iMZtEuQbUvg%oOW-_@3sa-ctzE6=4@*1Ec1m4lK(-8H5iHe^ zR}I^zS`M~lea{?lDwAAgDXb5#0+z~xy~8Cc>N?nmu~RmX(_5-@vy;EHwcH)}Dh?b;CbG}Rk3FUWSZq(; zKV*=5rYhpZPmfsju^U+MrYV+=<2Br5pa@y{aLui?u_=B;aFAO)Q>P2hdX#aWz1hKT zC@}jHlaTyup<2`Y9+}G>=e#>C*&;Q7WhG@Mc$R0mkZiv!^5vI>ou~T~ryta>GrvDck%MOf89fSjduB^46_BO)AG^J{9QJ^6;yz)O!=1F+#9E{gIym3|#x;1X=Un{=j znk^Pv@?2d*2^F>U=tz)+*LuqeNcoE&%j3(?%wd$uc z&i#s7J~?c~lIXqImg?)FuAJzeV&fgtM)w7g;31wHhdtc$#^Yj;<9 zt)X~4_Kg(`J!zfhUnq(v<2UW^3L_nRP?y`nt!X&icHtrp4}^~^o+GwAt!|Es2c#qy z*KK#C9j3fHI7w~4U>;zN%v3c|tvWC@zLA-bLw!|qfvZ~{S*<-(dcjUwDFLK9aOD-^ z#ZN>rCmNIrHNF;^t6T9|Z)-OPcxa_h``gdpC1a)_@Pb{rd2BIoodV{H&=L$~y9l-> zw%uglBIDgP{h%Zw)O+19AaX0!t3s%OA~;WRb`sqf0AJ;$~g zoY`mS%3uI~Fl2D%EA22R63yGVrBBiehg})+@tmGfd8pKVZMqx`h4Rs#nau(Y<4Bv? z7nWs0px}C(wvLoD=B`Um<8~ut{B5UW8iP2(koYJU$S^h|FKZeEhiPTZffp|pLf57_ zc$Y2q$vKoTaB0gK2;X~q12Xd==?Sc^UaDWHO5PjpuHsr7_|-0!W%e0(wpV0YO*jbU z32|qh+~c0ydo=)NKq4j@iwB1@#GC@UKFzrQua{MyQWCyg~EFwWaUGFQA|tk@n$#vRof%b#qd99BZ;44ZYJvNQ@kz z(*D6_H@{(=9ycFLp3aahT^+i@9-ND1(cM)yPzhU`GGziK#4NYnbDfPw+l~CO4>lHV z6VhPD!AUF?Do@59Z`8V!Il3J*nVLPYx5FZwx5m@-)y?s2#HM%6WogtLjnA6v!H%_C zI(-V~RfWjGk2jJd_FXvR*DL*V1DeBg6Q}&d$oS|iv3br=4d`Uw69OXe>WCbGJ9_Bb z+xDP%hhDj?a$;X=7RI%1yNlH7Vap?soHA70=0cg7^~q{4t>NJYwEU3MLo&Ru2C1QJ z{UU_#(RR09U1x>hTopSIbv~|v3h5HeoSrW&$@W;> zpGSvyMMUt!#*kQz@Nm#I>n{u1*TMS_o0DA!7hO~AoRSs$;M6XlTe{jJtTp|BJq(qx zCI;n98-G3<>w_EynOLMeO*HPvjJ6^stV~J(Oh^_Egi~V9Ug$&Oml5%w z&1H}FcFeeaan8`uG1qQ?iIo|v2Z7%>!P9^V^ga5Ms*St7F^g&g`21Cl7*43!h9pLt zU3=#AO~lz*PzS8(j?trrt7{DlnK;(&@B5WPWC-6GsgMfh?5Z-y7PmFpQiGWthm;5< zD?CtN_1@00tt;#jtHrCF^S!BZY*tO!U&wa<5vIpfGyUK`^mk6g48k+P(w3z@mhhBc z8Tqs~WD|M7E>=`Z4H#WfF7C|fTq^YobY!nEk4ZiTk3Yo z^GIz@k*yx`l&~xx+oe2&jQ(MGY>O{2N0E4g*IB)3odL4v`H)304#;jN2HLi^P|wbJ zRpvCrhot}s)JrTGcEloFtIN*bZ*Brfhm+zsFAkoE72+ZN&`d?;wh{s;z$qMTsP5dBr{*16XV~E6 z9z-}wgzcxFc=}myd_x0n(3#D%6i-&%_}sXPCX%5PkF|2MqguOSH4k1CqOzlIU@U7p zJ$`x|`C?s#J<|1Nb%ioOeqdNJt@j8XcDYKmsdHVyQEB~(K+A2{>-O>q*8`1$QWI;w zSHZcImX8BcXa1!4K(EarO)W#@BD!Sl*V^QAG)~N=v{=@?w&=hG(ma(d1Mv&QeYo8Y zYM?#t+NWpJPUTcog$g`evh2QTRDx%?m?V{}k|5v^TyNjy_W{)(?!bUR_$S{7q|;e z+z(<*x=8s=VPiu+;i1=mSx!!tR39`5XYpn(CEg5aO$g98MNRCW$)Mr5zGT+p4c}`r z_S>fK;&_JJEb#SR<^$$3qt@ zwr`1Bx)~LQx)5{g)ze5GUDo}{WZS8C7y*)q@&Gp(Cf-E!qhKAqGNNHi{z*9)r@kUN z@E3z3$!XLC&I*(XHR>&?Sx;Q$14IR9>3ggRE?0z#eM0oMQ5>ndyy%WB?wYJ6c>&L= z7PYh$<0uX&qqS;m2Iy}1F4JnTa>}lONE^7{Oo~c%wJs$$9~>f^Ny14(4_?60A~;V8 z#4Ip-6Xr4=6p)lw6E~G<%`5;t!amgm#2o85ow<(i=v@nbXPa_GCPHdtID$_owyH2X zwK3etokn4=sHNtW8*oS##`V2eYkNDR57@CMnjol`Hf|dVYTphGqcF#qY+jt5jtb-I zK?xBL=X!S-n_AP)LQI5Hg2+#N5rlYud!j}UJxrpo(i`e5u<>#`E?30W#F>GbA~5#aMXb}zup+xMS38atz~0xiU;UUqFSE3e`0<5 zU?h`wdQ#I12-(tgV~N9dps?=*B}Mhs!+BC__ID0zwn`FQ=hNsCRx_37FrB;3uBsmp zz05FL(}yXu%2q*^QehG`yk>G9b?C3BB)ZccvGClq3;V>z@TAWy91G#wdM9lXnKZRQ z2HpXY{z%itcBg!Dryd;Ugtl&R2w;FaX1LDm$y%n(3Q}YFM%%eizS?Tmu=X=eDY8F> zm82h!p3c4tFH~gyLNwL5v7!yB*w;Eean~7F_k2cEM@)p%PeYy~RM^~pMgGmgDbJ0mn2=(r{IeN8SZotAC6agt$vQRBOYI#H)cDnb&IK3PfnX(LOB1+A(z+qk0Kl!ZZXN=IWJo&ceF4MF?RW%mo0r9u*m z%Lv*U3cV%_$=L^xO`_K$FMvoUj`a)tGs;uGwqS~8xw}86OU3*P19qF>&Pq8q8Yh{Tn*6oRiov-imEhd| zif@m*b7Ovf>kiK|v2DCNY45a)j{ujG>e-DEJIG(;-i@^VxaJ!f=J7-EW&~+ft2yn3 zHgwy0gsW|a71`4h)@R*pZwG;56lG-q_x|3fRFYmDe@-pBb=@c%x>9fN#-iPZK7B-C zvckD6J4L5BR;f*=RqUzjAtNlS*QDNoI?=fjx7mysd(2JX>$P$!U~NJtnm9-$bT)Uu zF-*-9)eM}8T4+dXia*;UhmYCxzNcLJhQdy%wdHJ1mtt+%w9yE3V{k5sjF0N=}WBeDbE+3d}mPz3i4YUoh+=*lVkQGm|%yBrvdO5H-$h zN_Az5Z)~~+WOeCE^>W9zki=vKH@lcOvpfcQaQ4RoygX``x)$vBdl?SDuNZPuFLG5x zv_(D^PwQ6E^9O^oTXnjl)3kC)g%PMky?3e=`E|YEi<)ieLkN|}t$}<*AMzBSj-d_X zMipEYPaUn;EuD}K#Lic*Cl6r{ML6Y z|IxITVfY|7VEW^;R4xq;;#D~86}dOf#~g>%4lHfJ_wrCt*3FIHb6uf|b(3X(2F1PX z?o3ecWL}~}Ps1$^=h=lg+aMV=_S%5t1nx6NZNawWU3~%)8oF4&e;q^-cgk_9E(8JC zAzs7Rfj3Kv=;X(4WmvV9K%cdkjh{^%#j#Bxfp~a=Z${P&nf^lu&csRmo%?1c<_#G) z{nvydiH~e#3>h}zZ9^ z_6lr6s~%K6Mi}^a$Om~(AA9Gmp6BhB6i}`MyiJ>EC?YwU@a8=*N+lRZ^mQJpqnXM! zs&*0kiIZ&|$9wWvC{DVwX`>`}CZ%l=8WBcZsaO?RXaUjM5Y6trXJi)Gc!Pq+fHX$f z+slF<>muA46=Ti%3pd$H4m-}K2f?OyXtoD9-RR7AMe31`j7yZ+#dATR534e>Dzy&5 zaRdWoZG>3R*8zRJab~1%>DxO4C&WDA8>TaMxH=BI60bOwR*!+$;Xz^#SH{t4k1Cd$ zw@Oy=*!7yQ5v)BE1md-KH+zPVkN1pO$5jy*q3qjzy4#axJmjjQjR2Ie&XU&Yz%^EJ z+q+Jups3Q)la*~q+*X$b@%#@%{ok+t5Yk838_)K}{b9B1mWncD2Hfom>Lb_gG;mH7hhG%`I`r<9*!J+5#++vB;LcJ*3260=SS{U!n zD`r5$T5sjMOc`+xV?+ej;GVwa&x}-9m`m;is+Xe_Hy84B~B_`~Ud-4l4N>{QYcP*_ZJ} zQvU8;BjnlJ&+p;O*YKCX|6_cSl;6C47}r04_npJ{Tlk;E@Jac_-ShYG72N;tpZ?TJ z+W-2`MF2@D#PGimmtT*|r0C+}SMZm>e>;Xx%1iwH7x4^v_XPg8aZNrl{>UQ<{13>3 z_$1}u-3{2?*W4fD*`MC@e;+rHl+CcVTrTN<(oRwyaQ}aA;C~RqCuKi|zwdwibMA@@ zGNws=U;m}(PE!6j?r_rn{rmrE3}1@CazFXy=prfYxMBkTTll+&{|o#^nuv!jm-`03 zkWX9^{Jj0#&;Os}<}LgeF|K&D% zJ}G}Sc`z>D*OtqDKZZ}nKWE2J#O2@Y-&gR(`>(k#@8G}u*SP(-{A)7kH=q0V{~q_q z5GCz5{xf*AzVk;>dH;O!Jt_YQu)lr-uf_0M zM))5Amh?Yq-;Uw!7=DU-x1am@`#0PqxR=2HFoyqO4F8RIU=sXH`kmDCAAr4W|3_Bz z-and0hAXh{x|P_Ping@6WqIf^t*BU(?5%TCHv6t12vOz uOXx2lS4rP*;eY(^(fe1vfBS3H*V|vna!-T*=69nR|3=c$80hY6?*9NLkB?RW diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 6f38583d8..5cb56f4e4 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -1247,9 +1247,13 @@ else: # Descriptor is still present in the queue. case event of Event.Read: - fdData.readList = newList & fdData.readList + let oldReadList = move fdData.readList + fdData.readList = move newList + fdData.readList.add oldReadList of Event.Write: - fdData.writeList = newList & fdData.writeList + let oldWriteList = move fdData.writeList + fdData.writeList = move newList + fdData.writeList.add oldWriteList else: assert false, "Cannot process callbacks for " & $event diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim index baf238843..4b123117a 100644 --- a/lib/pure/parseutils.nim +++ b/lib/pure/parseutils.nim @@ -310,6 +310,10 @@ proc skipWhile*(s: string, toSkip: set[char], start = 0): int {.inline.} = result = 0 while start+result < s.len and s[result+start] in toSkip: inc(result) +proc fastSubstr(s: string; token: var string; start, length: int) = + token.setLen length + for i in 0 ..< length: token[i] = s[i+start] + proc parseUntil*(s: string, token: var string, until: set[char], start = 0): int {.inline.} = ## Parses a token and stores it in ``token``. Returns @@ -326,7 +330,8 @@ proc parseUntil*(s: string, token: var string, until: set[char], var i = start while i < s.len and s[i] notin until: inc(i) result = i-start - token = substr(s, start, i-1) + fastSubstr(s, token, start, result) + #token = substr(s, start, i-1) proc parseUntil*(s: string, token: var string, until: char, start = 0): int {.inline.} = @@ -344,7 +349,8 @@ proc parseUntil*(s: string, token: var string, until: char, var i = start while i < s.len and s[i] != until: inc(i) result = i-start - token = substr(s, start, i-1) + fastSubstr(s, token, start, result) + #token = substr(s, start, i-1) proc parseUntil*(s: string, token: var string, until: string, start = 0): int {.inline.} = @@ -370,7 +376,8 @@ proc parseUntil*(s: string, token: var string, until: string, if u >= until.len: break inc(i) result = i-start - token = substr(s, start, i-1) + fastSubstr(s, token, start, result) + #token = substr(s, start, i-1) proc parseWhile*(s: string, token: var string, validChars: set[char], start = 0): int {.inline.} = @@ -386,7 +393,8 @@ proc parseWhile*(s: string, token: var string, validChars: set[char], var i = start while i < s.len and s[i] in validChars: inc(i) result = i-start - token = substr(s, start, i-1) + fastSubstr(s, token, start, result) + #token = substr(s, start, i-1) proc captureBetween*(s: string, first: char, second = '\0', start = 0): string = ## Finds the first occurrence of ``first``, then returns everything from there diff --git a/lib/system/arc.nim b/lib/system/arc.nim index 3cbd02806..973b951d4 100644 --- a/lib/system/arc.nim +++ b/lib/system/arc.nim @@ -68,7 +68,7 @@ const when defined(nimArcDebug): include cellsets - const traceId = 7739 # 1037 + const traceId = 20 # 1037 var gRefId: int var freedCells: CellSet @@ -90,6 +90,9 @@ proc nimNewObj(size: int): pointer {.compilerRtl.} = when defined(nimArcDebug) or defined(nimArcIds): head(result).refId = gRefId atomicInc gRefId + if head(result).refId == traceId: + writeStackTrace() + cfprintf(cstderr, "[nimNewObj] %p %ld\n", result, head(result).rc shr rcShift) when traceCollector: cprintf("[Allocated] %p result: %p\n", result -! sizeof(RefHeader), result) @@ -112,6 +115,10 @@ proc nimNewObjUninit(size: int): pointer {.compilerRtl.} = when defined(nimArcDebug): head(result).refId = gRefId atomicInc gRefId + if head(result).refId == traceId: + writeStackTrace() + cfprintf(cstderr, "[nimNewObjUninit] %p %ld\n", result, head(result).rc shr rcShift) + when traceCollector: cprintf("[Allocated] %p result: %p\n", result -! sizeof(RefHeader), result) diff --git a/lib/system/orc.nim b/lib/system/orc.nim index 5deed1d14..ac6211ebb 100644 --- a/lib/system/orc.nim +++ b/lib/system/orc.nim @@ -26,6 +26,8 @@ const jumpStackFlag = 0b1000 colorMask = 0b011 + logOrc = defined(nimArcIds) + type TraceProc = proc (p, env: pointer) {.nimcall, benign.} DisposeProc = proc (p: pointer) {.nimcall, benign.} @@ -59,19 +61,17 @@ proc trace(s: Cell; desc: PNimTypeV2; j: var GcEnv) {.inline.} = var p = s +! sizeof(RefHeader) cast[TraceProc](desc.traceImpl)(p, addr(j)) -when true: - template debug(str: cstring; s: Cell) = discard -else: - proc debug(str: cstring; s: Cell) = - let p = s +! sizeof(RefHeader) - cprintf("[%s] name %s RC %ld\n", str, p, s.rc shr rcShift) +when logOrc: + proc writeCell(msg: cstring; s: Cell; desc: PNimTypeV2) = + cfprintf(cstderr, "%s %s %ld root index: %ld; RC: %ld; color: %ld\n", + msg, desc.name, s.refId, s.rootIdx, s.rc shr rcShift, s.color) proc free(s: Cell; desc: PNimTypeV2) {.inline.} = when traceCollector: cprintf("[From ] %p rc %ld color %ld\n", s, s.rc shr rcShift, s.color) let p = s +! sizeof(RefHeader) - debug("free", s) + when logOrc: writeCell("free", s, desc) if desc.disposeImpl != nil: cast[DisposeProc](desc.disposeImpl)(p) @@ -90,13 +90,13 @@ proc free(s: Cell; desc: PNimTypeV2) {.inline.} = nimRawDispose(p) -proc nimTraceRef(q: pointer; desc: PNimTypeV2; env: pointer) {.compilerRtl.} = +proc nimTraceRef(q: pointer; desc: PNimTypeV2; env: pointer) {.compilerRtl, inline.} = let p = cast[ptr pointer](q) if p[] != nil: var j = cast[ptr GcEnv](env) j.traceStack.add(head p[], desc) -proc nimTraceRefDyn(q: pointer; env: pointer) {.compilerRtl.} = +proc nimTraceRefDyn(q: pointer; env: pointer) {.compilerRtl, inline.} = let p = cast[ptr pointer](q) if p[] != nil: var j = cast[ptr GcEnv](env) @@ -121,11 +121,6 @@ proc unregisterCycle(s: Cell) = roots.d[idx][0].rootIdx = idx dec roots.len -when false: - proc writeCell(msg: cstring; s: Cell; desc: PNimTypeV2) = - cfprintf(cstderr, "%s %s %ld root index: %ld; RC: %ld; color: %ld\n", - msg, desc.name, s.refId, s.rootIdx, s.rc shr rcShift, s.color) - proc scanBlack(s: Cell; desc: PNimTypeV2; j: var GcEnv) = #[ proc scanBlack(s: Cell) = @@ -135,19 +130,17 @@ proc scanBlack(s: Cell; desc: PNimTypeV2; j: var GcEnv) = if t.color != colBlack: scanBlack(t) ]# - debug "scanBlack", s s.setColor colBlack let until = j.traceStack.len trace(s, desc, j) - #writeCell("root still alive", s, desc) + when logOrc: writeCell("root still alive", s, desc) while j.traceStack.len > until: let (t, desc) = j.traceStack.pop() inc t.rc, rcIncrement - debug "incRef", t if t.color != colBlack: t.setColor colBlack trace(t, desc, j) - #writeCell("child still alive", t, desc) + when logOrc: writeCell("child still alive", t, desc) proc markGray(s: Cell; desc: PNimTypeV2; j: var GcEnv) = #[ @@ -278,8 +271,11 @@ proc collectCyclesBacon(j: var GcEnv) = s.buffered = false collectWhite(s) ]# + when logOrc: + for i in 0 ..< roots.len: + writeCell("root", roots.d[i][0], roots.d[i][1]) + for i in 0 ..< roots.len: - #writeCell("root", roots.d[i][0], roots.d[i][1]) markGray(roots.d[i][0], roots.d[i][1], j) for i in 0 ..< roots.len: scan(roots.d[i][0], roots.d[i][1], j) @@ -305,7 +301,7 @@ var proc collectCycles() = ## Collect cycles. - when false: + when logOrc: cfprintf(cstderr, "[collectCycles] begin\n") var j: GcEnv @@ -335,7 +331,7 @@ proc collectCycles() = #cfprintf(cstderr, "[collectCycles] freed %ld, touched %ld new threshold %ld\n", j.freed, j.touched, rootsThreshold) elif rootsThreshold < high(int) div 4: rootsThreshold = rootsThreshold * 3 div 2 - when false: + when logOrc: cfprintf(cstderr, "[collectCycles] end; freed %ld new threshold %ld touched: %ld mem: %ld\n", j.freed, rootsThreshold, j.touched, getOccupiedMem()) diff --git a/lib/system/strs_v2.nim b/lib/system/strs_v2.nim index 85c0af190..57fdd5ee1 100644 --- a/lib/system/strs_v2.nim +++ b/lib/system/strs_v2.nim @@ -52,7 +52,8 @@ proc prepareAdd(s: var NimStringV2; addlen: int) {.compilerRtl.} = s.p = cast[ptr NimStrPayload](reallocShared0(s.p, contentSize(oldCap), contentSize(newCap))) s.p.cap = newCap -proc nimAddCharV1(s: var NimStringV2; c: char) {.compilerRtl.} = +proc nimAddCharV1(s: var NimStringV2; c: char) {.compilerRtl, inline.} = + #if (s.p == nil) or (s.len+1 > s.p.cap and not strlitFlag): prepareAdd(s, 1) s.p.data[s.len] = c s.p.data[s.len+1] = '\0' From cbdda631dbd68765203d1843f217f47c6e0c5c48 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 20 Sep 2020 00:05:41 +0200 Subject: [PATCH 0631/3768] base64: fixes the error message for an invalid base64 input character [backport:1.2] --- lib/pure/base64.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/base64.nim b/lib/pure/base64.nim index 9495c4d56..40c9af905 100644 --- a/lib/pure/base64.nim +++ b/lib/pure/base64.nim @@ -230,11 +230,11 @@ proc decode*(s: string): string = template inputChar(x: untyped) = let x = int decodeTable[ord(s[inputIndex])] - inc inputIndex if x == invalidChar: raise newException(ValueError, "Invalid base64 format character `" & s[inputIndex] & "` (ord " & $s[inputIndex].ord & ") at location " & $inputIndex & ".") + inc inputIndex template outputChar(x: untyped) = result[outputIndex] = char(x and 255) From a15d77df7e324f6cda831bf44ced7a5a8bd93ef4 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 20 Sep 2020 00:27:32 +0200 Subject: [PATCH 0632/3768] ORC/ARC async progress (#15370) * ARC/ORC: fixes memory leak for empty seqs that have a non-zero capacity * async: minor refactorings * it helps to finish a refactoring --- compiler/ccgexprs.nim | 2 +- lib/pure/asyncdispatch.nim | 15 +++++++-------- lib/pure/ioselects/ioselectors_kqueue.nim | 4 ++-- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 0d235f62e..83f7f7278 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2164,7 +2164,7 @@ proc genMove(p: BProc; n: PNode; d: var TLoc) = # generated by liftdestructors: var src: TLoc initLocExpr(p, n[2], src) - linefmt(p, cpsStmts, "if ($1.len && $1.p != $2.p) {", [rdLoc(a), rdLoc(src)]) + linefmt(p, cpsStmts, "if ($1.p != $2.p) {", [rdLoc(a), rdLoc(src)]) genStmts(p, n[3]) linefmt(p, cpsStmts, "}$n$1.len = $2.len; $1.p = $2.p;$n", [rdLoc(a), rdLoc(src)]) else: diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 5cb56f4e4..d8b274c34 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -1198,6 +1198,11 @@ else: let p = getGlobalDispatcher() not p.selector.isEmpty() or p.timers.len != 0 or p.callbacks.len != 0 + proc prependSeq(dest: var seq[Callback]; src: sink seq[Callback]) = + let old = move dest + dest = src + dest.add old + proc processBasicCallbacks( fd: AsyncFD, event: Event ): tuple[readCbListCount, writeCbListCount: int] = @@ -1246,14 +1251,8 @@ else: withData(selector, fd.int, fdData) do: # Descriptor is still present in the queue. case event - of Event.Read: - let oldReadList = move fdData.readList - fdData.readList = move newList - fdData.readList.add oldReadList - of Event.Write: - let oldWriteList = move fdData.writeList - fdData.writeList = move newList - fdData.writeList.add oldWriteList + of Event.Read: prependSeq(fdData.readList, newList) + of Event.Write: prependSeq(fdData.writeList, newList) else: assert false, "Cannot process callbacks for " & $event diff --git a/lib/pure/ioselects/ioselectors_kqueue.nim b/lib/pure/ioselects/ioselectors_kqueue.nim index 7635a04d5..a65be9842 100644 --- a/lib/pure/ioselects/ioselectors_kqueue.nim +++ b/lib/pure/ioselects/ioselectors_kqueue.nim @@ -616,7 +616,7 @@ template withData*[T](s: Selector[T], fd: SocketHandle|int, value, let fdi = int(fd) s.checkFd(fdi) if fdi in s: - var value = addr(s.getData(fdi)) + var value = addr(s.fds[fdi].data) body template withData*[T](s: Selector[T], fd: SocketHandle|int, value, body1, @@ -625,7 +625,7 @@ template withData*[T](s: Selector[T], fd: SocketHandle|int, value, body1, let fdi = int(fd) s.checkFd(fdi) if fdi in s: - var value = addr(s.getData(fdi)) + var value = addr(s.fds[fdi].data) body1 else: body2 From ebb632ccba0d786b90684e4da2b393cfae04a24c Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 20 Sep 2020 01:07:13 +0200 Subject: [PATCH 0633/3768] fixes #15369 (#15371) --- lib/pure/cgi.nim | 6 +++--- tests/stdlib/tcgi.nim | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/pure/cgi.nim b/lib/pure/cgi.nim index 734ab9171..1f0e0736a 100644 --- a/lib/pure/cgi.nim +++ b/lib/pure/cgi.nim @@ -105,10 +105,10 @@ iterator decodeData*(data: string): tuple[key, value: TaintedString] = while i < data.len: setLen(name, 0) # reuse memory i = parseData(data, i, name) - if i >= data.len or data[i] != '=': cgiError("'=' expected") - inc(i) # skip '=' setLen(value, 0) # reuse memory - i = parseData(data, i, value) + if i < data.len and data[i] == '=': + inc(i) # skip '=' + i = parseData(data, i, value) yield (name.TaintedString, value.TaintedString) if i < data.len: if data[i] == '&': inc(i) diff --git a/tests/stdlib/tcgi.nim b/tests/stdlib/tcgi.nim index 7df1b077d..222a3bf6d 100644 --- a/tests/stdlib/tcgi.nim +++ b/tests/stdlib/tcgi.nim @@ -1,6 +1,14 @@ discard """ output: ''' [Suite] Test cgi module +(key: "a", value: "1") +(key: "b", value: "0") +(key: "c", value: "3") +(key: "d", value: "") +(key: "e", value: "") +(key: "a", value: "5") +(key: "a", value: "t e x t") +(key: "e", value: "http://w3schools.com/my test.asp?name=ståle&car=saab") ''' """ @@ -21,3 +29,9 @@ suite "Test cgi module": expect KeyError: discard parsedQuery["not_existing_key"] + +# bug #15369 +let queryString = "a=1&b=0&c=3&d&e&a=5&a=t%20e%20x%20t&e=http%3A%2F%2Fw3schools.com%2Fmy%20test.asp%3Fname%3Dst%C3%A5le%26car%3Dsaab" + +for pair in decodeData(queryString): + echo pair From 33be7c6f5ef1a9e54d037682984f563c8ee11fcb Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 20 Sep 2020 09:01:42 +0200 Subject: [PATCH 0634/3768] arc: =deepcopy fixes --- compiler/varpartitions.nim | 1 - lib/system/deepcopy.nim | 19 ++++++++--- tests/arc/tdeepcopy.nim | 67 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 81 insertions(+), 6 deletions(-) create mode 100644 tests/arc/tdeepcopy.nim diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim index 743c4d55d..f983f9416 100644 --- a/compiler/varpartitions.nim +++ b/compiler/varpartitions.nim @@ -459,7 +459,6 @@ proc traverse(c: var Partitions; n: PNode) = if paramType.kind == tyVar: if c.inNoSideEffectSection == 0: for r in roots: potentialMutation(c, r, it.info) - else: for r in roots: noCursor(c, r) of nkAddr, nkHiddenAddr: diff --git a/lib/system/deepcopy.nim b/lib/system/deepcopy.nim index 5905b5785..b9dc594fa 100644 --- a/lib/system/deepcopy.nim +++ b/lib/system/deepcopy.nim @@ -140,7 +140,10 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType; tab: var PtrTable) = unsureAsgnRef(cast[PPointer](dest), s2) elif mt.base.deepcopy != nil: let z = mt.base.deepcopy(s2) - unsureAsgnRef(cast[PPointer](dest), z) + when defined(nimSeqsV2): + cast[PPointer](dest)[] = z + else: + unsureAsgnRef(cast[PPointer](dest), z) else: let z = tab.get(s2) if z == nil: @@ -157,10 +160,16 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType; tab: var PtrTable) = let x = usrToCell(s2) let realType = x.typ sysAssert realType == mt, " types do differ" - # this version should work for any possible GC: - let typ = if mt.base.kind == tyObject: cast[ptr PNimType](s2)[] else: mt.base - let z = when defined(nimSeqsV2): nimNewObj(typ.size) else: newObj(mt, typ.size) - unsureAsgnRef(cast[PPointer](dest), z) + when defined(nimSeqsV2): + let typ = if mt.base.kind == tyObject: cast[PNimType](cast[ptr PNimTypeV2](s2)[].typeInfoV1) + else: mt.base + let z = nimNewObj(typ.size) + cast[PPointer](dest)[] = z + else: + # this version should work for any other GC: + let typ = if mt.base.kind == tyObject: cast[ptr PNimType](s2)[] else: mt.base + let z = newObj(mt, typ.size) + unsureAsgnRef(cast[PPointer](dest), z) tab.put(s2, z) genericDeepCopyAux(z, s2, typ, tab) else: diff --git a/tests/arc/tdeepcopy.nim b/tests/arc/tdeepcopy.nim new file mode 100644 index 000000000..0eaf7ea40 --- /dev/null +++ b/tests/arc/tdeepcopy.nim @@ -0,0 +1,67 @@ +discard """ + cmd: "nim c --gc:arc $file" + output: '''13 abc +13 abc +13 abc +13 abc +13 abc +13 abc +13 abc +13 abc +13 abc +13 abc +13 abc +called deepCopy for int +called deepCopy for int +called deepCopy for int +called deepCopy for int +called deepCopy for int +called deepCopy for int +called deepCopy for int +called deepCopy for int +called deepCopy for int +called deepCopy for int +called deepCopy for int +0''' +""" + +type + PBinaryTree = ref object of RootObj + le, ri: PBinaryTree + value: int + +proc mainB = + var x: PBinaryTree + deepCopy(x, PBinaryTree(ri: PBinaryTree(le: PBinaryTree(value: 13)))) + + var y: string + deepCopy y, "abc" + echo x.ri.le.value, " ", y + +for i in 0..10: + mainB() + + +type + Bar[T] = object + x: T + +proc `=deepCopy`[T](b: ref Bar[T]): ref Bar[T] = + result.new + result.x = b.x + when T is int: + echo "called deepCopy for int" + else: + echo "called deepCopy for something else" + +proc main = + var dummy, c: ref Bar[int] + new(dummy) + dummy.x = 44 + + deepCopy c, dummy + +for i in 0..10: + main() + +echo getOccupiedMem() From cdac67376c599c39e748401e8c0a82ab51a87326 Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 20 Sep 2020 09:10:40 +0200 Subject: [PATCH 0635/3768] arc: added tmarshal.nim test case --- tests/arc/tmarshal.nim | 140 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 tests/arc/tmarshal.nim diff --git a/tests/arc/tmarshal.nim b/tests/arc/tmarshal.nim new file mode 100644 index 000000000..f7ec6e2c5 --- /dev/null +++ b/tests/arc/tmarshal.nim @@ -0,0 +1,140 @@ +discard """ + cmd: "nim c --gc:orc $file" + output: ''' +{"age": 12, "bio": "Я Cletus", "blob": [65, 66, 67, 128], "name": "Cletus"} +true +true +alpha 100 +omega 200 +0''' +""" + +import marshal + +template testit(x) = discard $$to[typeof(x)]($$x) + +var x: array[0..4, array[0..4, string]] = [ + ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"], + ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"], + ["test", "1", "2", "3", "4"]] + +proc blockA = + testit(x) + var test2: tuple[name: string, s: int] = ("tuple test", 56) + testit(test2) + +blockA() + +type + TE = enum + blah, blah2 + + TestObj = object + test, asd: int + case test2: TE + of blah: + help: string + else: + discard + + PNode = ref TNode + TNode = object + next, prev: PNode + data: string + +proc buildList(): PNode = + new(result) + new(result.next) + new(result.prev) + result.data = "middle" + result.next.data = "next" + result.prev.data = "prev" + result.next.next = result.prev + result.next.prev = result + result.prev.next = result + result.prev.prev = result.next + +proc blockB = + var test3: TestObj + test3.test = 42 + test3.test2 = blah + testit(test3) + + var test4: ref tuple[a, b: string] + new(test4) + test4.a = "ref string test: A" + test4.b = "ref string test: B" + testit(test4) + + var test5 = @[(0,1),(2,3),(4,5)] + testit(test5) + + var test7 = buildList() + testit(test7) + + var test6: set[char] = {'A'..'Z', '_'} + testit(test6) + +blockB() + +# bug #1352 + +type + Entity = object of RootObj + name: string + + Person = object of Entity + age: int + bio: string + blob: string + +proc blockC = + var instance1 = Person(name: "Cletus", age: 12, + bio: "Я Cletus", + blob: "ABC\x80") + echo($$instance1) + echo(to[Person]($$instance1).bio == instance1.bio) # true + echo(to[Person]($$instance1).blob == instance1.blob) # true + +blockC() + +# bug 5757 + +type + Something = object + x: string + y: int + +proc blockD = + var data1 = """{"x": "alpha", "y": 100}""" + var data2 = """{"x": "omega", "y": 200}""" + + var r = to[Something](data1) + + echo r.x, " ", r.y + + r = to[Something](data2) + + echo r.x, " ", r.y + +blockD() + +type + Foo = object + a1: string + a2: string + a3: seq[string] + a4: seq[int] + a5: seq[int] + a6: seq[int] + +proc blockE = + var foo = Foo(a2: "", a4: @[], a6: @[1]) + foo.a6.setLen 0 + doAssert $$foo == """{"a1": "", "a2": "", "a3": [], "a4": [], "a5": [], "a6": []}""" + testit(foo) + +blockE() + +GC_fullCollect() +echo getOccupiedMem() From 04cecdf9c28c1f1d526d209c582afe7daa7fe654 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 20 Sep 2020 18:01:07 +0200 Subject: [PATCH 0636/3768] async: removed the 'unown' references, async never worked with --newruntime anyway and --newruntime is dead (#15374) --- lib/pure/asyncmacro.nim | 11 +++++------ lib/system/orc.nim | 7 ++++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/pure/asyncmacro.nim b/lib/pure/asyncmacro.nim index 219ef6c67..65cf2f3b9 100644 --- a/lib/pure/asyncmacro.nim +++ b/lib/pure/asyncmacro.nim @@ -14,21 +14,20 @@ import macros, strutils, asyncfutures template createCb(retFutureSym, iteratorNameSym, strName, identName, futureVarCompletions: untyped) = bind finished - let retFutUnown = unown retFutureSym var nameIterVar = iteratorNameSym proc identName {.closure.} = try: if not nameIterVar.finished: - var next = unown nameIterVar() + var next = nameIterVar() # Continue while the yielded future is already finished. while (not next.isNil) and next.finished: - next = unown nameIterVar() + next = nameIterVar() if nameIterVar.finished: break if next == nil: - if not retFutUnown.finished: + if not retFutureSym.finished: let msg = "Async procedure ($1) yielded `nil`, are you await'ing a " & "`nil` Future?" raise newException(AssertionDefect, msg % strName) @@ -39,12 +38,12 @@ template createCb(retFutureSym, iteratorNameSym, {.pop.} except: futureVarCompletions - if retFutUnown.finished: + if retFutureSym.finished: # Take a look at tasyncexceptions for the bug which this fixes. # That test explains it better than I can here. raise else: - retFutUnown.fail(getCurrentException()) + retFutureSym.fail(getCurrentException()) identName() proc createFutureVarCompletions(futureVarIdents: seq[NimNode], diff --git a/lib/system/orc.nim b/lib/system/orc.nim index ac6211ebb..cf8d6379a 100644 --- a/lib/system/orc.nim +++ b/lib/system/orc.nim @@ -103,9 +103,10 @@ proc nimTraceRefDyn(q: pointer; env: pointer) {.compilerRtl, inline.} = j.traceStack.add(head p[], cast[ptr PNimTypeV2](p[])[]) template orcAssert(cond, msg) = - if not cond: - cfprintf(cstderr, "[Bug!] %s\n", msg) - quit 1 + when logOrc: + if not cond: + cfprintf(cstderr, "[Bug!] %s\n", msg) + quit 1 var roots {.threadvar.}: CellSeq From 4d75059e8b16c64a9765da6489af7c3d697c81b5 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 20 Sep 2020 22:47:48 +0200 Subject: [PATCH 0637/3768] added a basic ORC test I still had lying around (#15376) --- tests/arc/torc_basic_test.nim | 138 ++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 tests/arc/torc_basic_test.nim diff --git a/tests/arc/torc_basic_test.nim b/tests/arc/torc_basic_test.nim new file mode 100644 index 000000000..73039fad7 --- /dev/null +++ b/tests/arc/torc_basic_test.nim @@ -0,0 +1,138 @@ +discard """ + output: "MEM 0" + cmd: "nim c --gc:orc $file" +""" + +type + Node = ref object + name: char + sccId: int + #a: array[3, Node] + a0, a1, a2: Node + rc: int + +proc edge(a, b: Node) = + inc b.rc + if a.a0 == nil: a.a0 = b + elif a.a1 == nil: a.a1 = b + else: a.a2 = b + when false: + var i = 0 + while a.a[i] != nil: inc i + a.a[i] = b + +proc createNode(name: char): Node = + new result + result.name = name + +#[ + + +--------------------------------+ + v | ++---------+ +------+ | +| | | | | +| A +----->+ | +------+------+ ++--+------+ | | | | + | | | | C ------------> G <--| + | | R | | | ++--v------+ | | +-------------+ +| | | | ^ +| B <------+ | | +| | | +--------+ ++---------+ | | + +------+ + +]# +proc use(x: Node) = discard + +proc main = + let a = createNode('A') + let b = createNode('B') + let r = createNode('R') + let c = createNode('C') + + a.edge b + a.edge r + + r.edge b + r.edge c + + let g = createNode('G') + g.edge g + g.edge g + + c.edge g + c.edge a + + use g + use b + +proc buildComplexGraph: Node = + # see https://en.wikipedia.org/wiki/Strongly_connected_component for the + # graph: + let a = createNode('a') + let b = createNode('b') + let c = createNode('c') + let d = createNode('d') + let e = createNode('e') + + a.edge c + c.edge b + c.edge e + b.edge a + d.edge c + e.edge d + + + let f = createNode('f') + b.edge f + e.edge f + + let g = createNode('g') + let h = createNode('h') + let i = createNode('i') + + f.edge g + f.edge i + g.edge h + h.edge i + i.edge g + + let j = createNode('j') + + h.edge j + i.edge j + + let k = createNode('k') + let l = createNode('l') + + f.edge k + k.edge l + l.edge k + k.edge j + + let m = createNode('m') + let n = createNode('n') + let p = createNode('p') + let q = createNode('q') + + m.edge n + n.edge p + n.edge q + q.edge p + p.edge m + + q.edge k + + d.edge m + e.edge n + + result = a + +proc main2 = + let g = buildComplexGraph() + +main() +main2() +GC_fullCollect() +echo "MEM ", getOccupiedMem() From 0a1e4cad7fc6149d168ecaa06bd613b64fc704fb Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 21 Sep 2020 18:21:05 +0800 Subject: [PATCH 0638/3768] add testcase for #9710 (#15365) * string is not nil anymore * tatus --- tests/stdlib/t9710.nim | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 tests/stdlib/t9710.nim diff --git a/tests/stdlib/t9710.nim b/tests/stdlib/t9710.nim new file mode 100644 index 000000000..f3ed860df --- /dev/null +++ b/tests/stdlib/t9710.nim @@ -0,0 +1,11 @@ +discard """ + cmd: "nim c -r --debugger:native --panics:on $options $file" + targets: "c" + nimout: "" + action: "run" + exitcode: 0 + timeout: 60.0 +""" + +for i in 1 || 200: + discard i From c2434d939e193c066fad1eaefcba486dc0af6087 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 21 Sep 2020 12:35:23 +0200 Subject: [PATCH 0639/3768] fixes #15360 [backport:1.2] (#15378) --- compiler/semstmts.nim | 2 +- tests/iter/titer.nim | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 14f08b4b3..e99ce8937 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -718,7 +718,7 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode = result = n let iterBase = n[^2].typ var iter = skipTypes(iterBase, {tyGenericInst, tyAlias, tySink, tyOwned}) - var iterAfterVarLent = iter.skipTypes({tyLent, tyVar}) + var iterAfterVarLent = iter.skipTypes({tyGenericInst, tyAlias, tyLent, tyVar}) # n.len == 3 means that there is one for loop variable # and thus no tuple unpacking: if iterAfterVarLent.kind != tyTuple or n.len == 3: diff --git a/tests/iter/titer.nim b/tests/iter/titer.nim index 22be1bad5..8e1c13e82 100644 --- a/tests/iter/titer.nim +++ b/tests/iter/titer.nim @@ -55,3 +55,10 @@ const for i in 0..len(stringArray)-1: echo(stringArray[i]) +# bug #15360 + +type Rule[T] = (int, T) + +var t: seq[Rule[int]] +for (c, t) in t: + discard From db9ab2a4c00bc650a8526115d659b57dcd38c39b Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Mon, 21 Sep 2020 07:37:14 -0300 Subject: [PATCH 0640/3768] Documentation prelude (#15377) --- doc/prelude.rst | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 doc/prelude.rst diff --git a/doc/prelude.rst b/doc/prelude.rst new file mode 100644 index 000000000..47fa70b70 --- /dev/null +++ b/doc/prelude.rst @@ -0,0 +1,29 @@ +Prelude +======= + +This is an include file that simply imports common modules for your convenience: + +.. code-block:: nim + include prelude + +Same as: + +.. code-block:: nim + import os, strutils, times, parseutils, parseopt, hashes, tables, sets + + +Examples +======== + +Get the basic most common imports ready to start coding using ``prelude``: + +.. code-block:: nim + include prelude + + echo now() + echo getCurrentDir() + echo "Hello $1".format("World") + + +See also: +- `Sugar `_ From 6c83eb48c2686818a0527a64576e27f0dbb87560 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 21 Sep 2020 19:32:05 +0800 Subject: [PATCH 0641/3768] add testcase for #7165 (#15368) * add testcase for #7165 * Remove overspecific testament spec Co-authored-by: Clyybber --- tests/distinct/t7165.nim | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 tests/distinct/t7165.nim diff --git a/tests/distinct/t7165.nim b/tests/distinct/t7165.nim new file mode 100644 index 000000000..b16c29c0e --- /dev/null +++ b/tests/distinct/t7165.nim @@ -0,0 +1,15 @@ + +type + Table[K, V] = object + key: K + val: V + + MyTable = distinct Table[string, int] + MyTableRef = ref MyTable + +proc newTable[K, V](): ref Table[K, V] = discard + +proc newMyTable: MyTableRef = + MyTableRef(newTable[string, int]()) # <--- error here + +discard newMyTable() From 40a868bccc6934aba99a9e490dad04296533b00f Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 21 Sep 2020 19:37:14 +0800 Subject: [PATCH 0642/3768] add testcase for #6060 (#15366) * add testcase for #6060 * Remove overspecific testament spec Co-authored-by: Clyybber --- tests/generics/t6060.nim | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 tests/generics/t6060.nim diff --git a/tests/generics/t6060.nim b/tests/generics/t6060.nim new file mode 100644 index 000000000..6b1856f1c --- /dev/null +++ b/tests/generics/t6060.nim @@ -0,0 +1,11 @@ +import tables + +type MyTab[A,B] = distinct TableRef[A,B] + +proc `$`[A,B](t: MyTab[A,B]): string = + "My special table " & $TableRef[A,B](t) + +proc create[A,B](): MyTab[A,B] = MyTab(newTable[A,B]()) + +var a = create[int,int]() +doAssert $a == "My special table {:}" From 464518327211ea3a693e9ae5e9b03c739d410ac4 Mon Sep 17 00:00:00 2001 From: Miran Date: Mon, 21 Sep 2020 17:01:12 +0200 Subject: [PATCH 0643/3768] fix #6430, support `:target:` for images (#15379) --- lib/packages/docutils/rstgen.nim | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 2ec25cc6e..9f707f4e8 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -848,6 +848,18 @@ proc renderImage(d: PDoc, n: PRstNode, result: var string) = """ else: htmlOut = "" + + # support for `:target:` links for images: + var target = esc(d.target, getFieldValue(n, "target").strip()) + if target.len > 0: + # `htmlOut` needs to be of the following format for link to work for images: + # + var htmlOutWithLink = "" + dispA(d.target, htmlOutWithLink, + "$1", + "\\href{$2}{$1}", [htmlOut, target]) + htmlOut = htmlOutWithLink + dispA(d.target, result, htmlOut, "\\includegraphics$2{$1}", [esc(d.target, arg), options]) if len(n) >= 3: renderRstToOut(d, n.sons[2], result) From 8f511fd0e68d1f75f418a5ed69663b8a41e29ae2 Mon Sep 17 00:00:00 2001 From: genotrance Date: Mon, 21 Sep 2020 11:48:00 -0500 Subject: [PATCH 0644/3768] Bump nimble (#15380) --- koch.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/koch.nim b/koch.nim index 90c1296fa..966ff932e 100644 --- a/koch.nim +++ b/koch.nim @@ -10,7 +10,7 @@ # const - NimbleStableCommit = "27d1063febe3055252103cacb11787ac83893416" # master + NimbleStableCommit = "f4c818a046c2c3ceb1523c4c974e4b6e625d8ca3" # master FusionStableCommit = "319aac4d43b04113831b529f8003e82f4af6a4a5" when not defined(windows): From aca9c5fb30fe2de12671e9103e93b81201bb8e71 Mon Sep 17 00:00:00 2001 From: alaviss Date: Mon, 21 Sep 2020 17:52:44 +0000 Subject: [PATCH 0645/3768] os: make getApplFreebsd available for NetBSD (#15381) NetBSD was switched to getApplFreebsd() in #15359, however the proc was not made available for the said target. This commit exposes `getApplFreebsd()` to NetBSD. --- lib/pure/os.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 45f022379..5efbde268 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -2814,7 +2814,7 @@ else: "commandLineParams() unsupported by dynamic libraries".} = discard -when not weirdTarget and (defined(freebsd) or defined(dragonfly)): +when not weirdTarget and (defined(freebsd) or defined(dragonfly) or defined(netbsd)): proc sysctl(name: ptr cint, namelen: cuint, oldp: pointer, oldplen: ptr csize_t, newp: pointer, newplen: csize_t): cint {.importc: "sysctl",header: """#include From d67c5cb75171fcbf1ca9078452e8fcbe36fc79bc Mon Sep 17 00:00:00 2001 From: Clyybber Date: Tue, 22 Sep 2020 10:43:12 +0200 Subject: [PATCH 0646/3768] Add strutils.indentation and make unindent use it (#15264) * Add strutils.indentation and make unindent use it * Code style * Fix bootstrapping * Improve wording * Fix test * Introduce without breaking change * Fix * Reduce diff * Fix docs link * Add since annotation * Update changelog --- changelog.md | 9 ++++++++ lib/pure/strutils.nim | 47 ++++++++++++++++++++++++++++++--------- tests/stdlib/tstrutil.nim | 22 ++++++++++++++++++ 3 files changed, 67 insertions(+), 11 deletions(-) diff --git a/changelog.md b/changelog.md index e7afaa229..1bc1d7303 100644 --- a/changelog.md +++ b/changelog.md @@ -187,6 +187,15 @@ - Add missing attributes and methods to `dom.Navigator` like `deviceMemory`, `onLine`, `vibrate()`, etc. +- Added `strutils.indentation` and `strutils.dedent` which enable indented string literals: + ```nim + import strutils + echo dedent """ + This + is + cool! + """ + ``` ## Language changes diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 156929dee..d97ed9a07 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -82,6 +82,7 @@ when defined(nimVmExportFixed): export toLower, toUpper include "system/inclrtl" +import std/private/since const Whitespace* = {' ', '\t', '\v', '\r', '\l', '\f'} @@ -1511,6 +1512,7 @@ proc indent*(s: string, count: Natural, padding: string = " "): string ## * `alignLeft proc<#alignLeft,string,Natural,char>`_ ## * `spaces proc<#spaces,Natural>`_ ## * `unindent proc<#unindent,string,Natural,string>`_ + ## * `dedent proc<#dedent,string,Natural,string>`_ runnableExamples: doAssert indent("First line\c\l and second line.", 2) == " First line\l and second line." @@ -1524,21 +1526,25 @@ proc indent*(s: string, count: Natural, padding: string = " "): string result.add(line) i.inc -proc unindent*(s: string, count: Natural, padding: string = " "): string +proc unindent*(s: string, count: Natural = int.high, padding: string = " "): string {.noSideEffect, rtl, extern: "nsuUnindent".} = ## Unindents each line in ``s`` by ``count`` amount of ``padding``. - ## Sometimes called `dedent`:idx: ## ## **Note:** This does not preserve the new line characters used in ``s``. ## ## See also: + ## * `dedent proc<#dedent,string,Natural,string>` ## * `align proc<#align,string,Natural,char>`_ ## * `alignLeft proc<#alignLeft,string,Natural,char>`_ ## * `spaces proc<#spaces,Natural>`_ ## * `indent proc<#indent,string,Natural,string>`_ runnableExamples: - doAssert unindent(" First line\l and second line", 3) == - "First line\land second line" + let x = """ + Hello + There + """.unindent() + + doAssert x == "Hello\nThere\n" result = "" var i = 0 for line in s.splitLines(): @@ -1553,11 +1559,30 @@ proc unindent*(s: string, count: Natural, padding: string = " "): string result.add(line[indentCount*padding.len .. ^1]) i.inc -proc unindent*(s: string): string - {.noSideEffect, rtl, extern: "nsuUnindentAll".} = - ## Removes all indentation composed of whitespace from each line in ``s``. +proc indentation*(s: string): Natural {.since: (1, 3).} = + ## Returns the amount of indentation all lines of ``s`` have in common, + ## ignoring lines that consist only of whitespace. + result = int.high + for line in s.splitLines: + for i, c in line: + if i >= result: break + elif c != ' ': + result = i + break + if result == int.high: + result = 0 + +proc dedent*(s: string, count: Natural = indentation(s)): string + {.noSideEffect, rtl, extern: "nsuDedent", since: (1, 3).} = + ## Unindents each line in ``s`` by ``count`` amount of ``padding``. + ## The only difference between this and `unindent proc<#unindent,string,Natural,string>` + ## is that this by default only cuts off the amount of indentation that all + ## lines of ``s`` share as opposed to all indentation. It only supports spcaes as padding. + ## + ## **Note:** This does not preserve the new line characters used in ``s``. ## ## See also: + ## * `unindent proc<#unindent,string,Natural,string>` ## * `align proc<#align,string,Natural,char>`_ ## * `alignLeft proc<#alignLeft,string,Natural,char>`_ ## * `spaces proc<#spaces,Natural>`_ @@ -1565,11 +1590,11 @@ proc unindent*(s: string): string runnableExamples: let x = """ Hello - There - """.unindent() + There + """.dedent() - doAssert x == "Hello\nThere\n" - unindent(s, 1000) # TODO: Passing a 1000 is a bit hackish. + doAssert x == "Hello\n There\n" + unindent(s, count, " ") proc delete*(s: var string, first, last: int) {.noSideEffect, rtl, extern: "nsuDelete".} = diff --git a/tests/stdlib/tstrutil.nim b/tests/stdlib/tstrutil.nim index ef5b041be..162b8ea1b 100644 --- a/tests/stdlib/tstrutil.nim +++ b/tests/stdlib/tstrutil.nim @@ -435,3 +435,25 @@ block: doAssert a == f1 doAssert b == f2 doAssert c == f3 + +block: + assert 0 == indentation """ +hey + low + there +""" + assert 2 == indentation """ + hey + low + there +""" + assert 2 == indentation """ hey + low + there +""" + assert 2 == indentation """ hey + low + there""" + assert 0 == indentation "" + assert 0 == indentation " \n \n" + assert 0 == indentation " " From 1fae66e4df8cc43b4ec8ab97fff96282ef234f2e Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 22 Sep 2020 13:03:24 +0200 Subject: [PATCH 0647/3768] better nativestacktrace support; refs #15284; backport [1.2] (#15384) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * nimStackTraceOverride: enable stack traces in exceptions This is a two-step stack trace collection scheme, because re-raised exceptions will collect multiple stack traces but use them rarely, when printing info about an uncaught exception, so it makes sense to only do the cheap stack unwinding all the time and the relatively expensive debugging information collection on-demand. `asyncfutures` implements its own `$` proc for printing `seq[StackTraceEntry]`, so we have to add the debugging info there, just like we do for the private `$` proc in `system/excpt`. * cleaned up PR #15284 Co-authored-by: Ștefan Talpalaru --- lib/pure/asyncfutures.nim | 13 +++- lib/system.nim | 6 ++ lib/system/exceptions.nim | 5 ++ lib/system/excpt.nim | 45 ++++++------ lib/system/stacktraces.nim | 83 +++++++++++++++++++++++ tests/system/tnim_stacktrace_override.nim | 18 +++++ 6 files changed, 142 insertions(+), 28 deletions(-) create mode 100644 lib/system/stacktraces.nim create mode 100644 tests/system/tnim_stacktrace_override.nim diff --git a/lib/pure/asyncfutures.nim b/lib/pure/asyncfutures.nim index 97abf72c9..2333c5ef1 100644 --- a/lib/pure/asyncfutures.nim +++ b/lib/pure/asyncfutures.nim @@ -9,6 +9,8 @@ import os, tables, strutils, times, heapqueue, options, deques, cstrutils +import "system/stacktraces" + # TODO: This shouldn't need to be included, but should ideally be exported. type CallbackFunc = proc () {.closure, gcsafe.} @@ -293,7 +295,12 @@ proc getHint(entry: StackTraceEntry): string = if cmpIgnoreStyle(entry.filename, "asyncmacro.nim") == 0: return "Resumes an async procedure" -proc `$`*(entries: seq[StackTraceEntry]): string = +proc `$`*(stackTraceEntries: seq[StackTraceEntry]): string = + when defined(nimStackTraceOverride): + let entries = addDebuggingInfo(stackTraceEntries) + else: + let entries = stackTraceEntries + result = "" # Find longest filename & line number combo for alignment purposes. var longestLeft = 0 @@ -308,10 +315,10 @@ proc `$`*(entries: seq[StackTraceEntry]): string = # Format the entries. for entry in entries: if entry.procname.isNil: - if entry.line == -10: + if entry.line == reraisedFromBegin: result.add(spaces(indent) & "#[\n") indent.inc(2) - else: + elif entry.line == reraisedFromEnd: indent.dec(2) result.add(spaces(indent) & "]#\n") continue diff --git a/lib/system.nim b/lib/system.nim index 362b5b2b9..eefa84565 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1383,6 +1383,12 @@ type # these work for most platforms: culonglong* {.importc: "unsigned long long", nodecl.} = uint64 ## This is the same as the type ``unsigned long long`` in *C*. + # There is a disparity on macOS where Nim's `uint` is `unsigned long long` and + # `uintptr_t` is `unsigned long`. Even though both data types are the same + # size (64 bits), clang++ refuses to do automatic conversion between them. + cuintptr_t* {.importc: "uintptr_t", nodecl.} = uint + ## This is the same as the type ``uintptr_t`` in *C*. + cstringArray* {.importc: "char**", nodecl.} = ptr UncheckedArray[cstring] ## This is binary compatible to the type ``char**`` in *C*. The array's ## high value is large enough to disable bounds checking in practice. diff --git a/lib/system/exceptions.nim b/lib/system/exceptions.nim index 3006cff19..fc8bd89f7 100644 --- a/lib/system/exceptions.nim +++ b/lib/system/exceptions.nim @@ -25,6 +25,11 @@ type ## rendered at a later time, we should ensure the stacktrace ## data isn't invalidated; any pointer into PFrame is ## subject to being invalidated so shouldn't be stored. + when defined(nimStackTraceOverride): + programCounter*: uint ## Program counter - will be used to get the rest of the info, + ## when `$` is called on this type. We can't use + ## "cuintptr_t" in here. + procnameStr*, filenameStr*: string ## GC-ed objects holding the cstrings in "procname" and "filename" Exception* {.compilerproc, magic: "Exception".} = object of RootObj ## \ ## Base exception class. diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index 089048163..1a7473a76 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -11,6 +11,7 @@ # use the heap (and nor exceptions) do not include the GC or memory allocator. import std/private/miscdollars +import stacktraces var errorMessageWriter*: (proc(msg: string) {.tags: [WriteIOEffect], benign, @@ -137,20 +138,6 @@ const hasSomeStackTrace = NimStackTrace or defined(nimStackTraceOverride) or (defined(nativeStackTrace) and nativeStackTraceSupported) -when defined(nimStackTraceOverride): - type StackTraceOverrideProc* = proc (): string {.nimcall, noinline, benign, raises: [], tags: [].} - ## Procedure type for overriding the default stack trace. - - var stackTraceOverrideGetTraceback: StackTraceOverrideProc = proc(): string {.noinline.} = - result = "Stack trace override procedure not registered.\n" - - proc registerStackTraceOverride*(overrideProc: StackTraceOverrideProc) = - ## Override the default stack trace inside rawWriteStackTrace() with your - ## own procedure. - stackTraceOverrideGetTraceback = overrideProc - - proc auxWriteStackTraceWithOverride(s: var string) = - add(s, stackTraceOverrideGetTraceback()) when defined(nativeStacktrace) and nativeStackTraceSupported: type @@ -168,13 +155,13 @@ when defined(nativeStacktrace) and nativeStackTraceSupported: when not hasThreadSupport: var - tempAddresses: array[0..127, pointer] # should not be alloc'd on stack + tempAddresses: array[maxStackTraceLines, pointer] # should not be alloc'd on stack tempDlInfo: TDl_info proc auxWriteStackTraceWithBacktrace(s: var string) = when hasThreadSupport: var - tempAddresses: array[0..127, pointer] # but better than a threadvar + tempAddresses: array[maxStackTraceLines, pointer] # but better than a threadvar tempDlInfo: TDl_info # This is allowed to be expensive since it only happens during crashes # (but this way you don't need manual stack tracing) @@ -202,11 +189,7 @@ when defined(nativeStacktrace) and nativeStackTraceSupported: when hasSomeStackTrace and not hasThreadSupport: var - tempFrames: array[0..127, PFrame] # should not be alloc'd on stack - -const - reraisedFromBegin = -10 - reraisedFromEnd = -100 + tempFrames: array[maxStackTraceLines, PFrame] # should not be alloc'd on stack template reraisedFrom(z): untyped = StackTraceEntry(procname: nil, line: z, filename: nil) @@ -253,7 +236,12 @@ template addFrameEntry(s: var string, f: StackTraceEntry|PFrame) = for i in first.. 0: + result.add(stackTraceOverrideGetDebuggingInfo(programCounters, maxStackTraceLines)) diff --git a/tests/system/tnim_stacktrace_override.nim b/tests/system/tnim_stacktrace_override.nim new file mode 100644 index 000000000..c75da9d9b --- /dev/null +++ b/tests/system/tnim_stacktrace_override.nim @@ -0,0 +1,18 @@ +discard """ + cmd: "nim c -d:nimStacktraceOverride $file" + output: '''begin +Traceback (most recent call last, using override) +Error: unhandled exception: stack trace produced [ValueError] +''' + exitcode: 1 +""" + +import asyncfutures + +proc main = + echo "begin" + if true: + raise newException(ValueError, "stack trace produced") + echo "unreachable" + +main() From ab05e141c0ee298d42344e8a15101097e73ff2f9 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Tue, 22 Sep 2020 19:07:31 +0800 Subject: [PATCH 0648/3768] deinitLock (#15383) * deinitLock * minor --- lib/pure/ioselects/ioselectors_select.nim | 1 + lib/system/threads.nim | 2 ++ 2 files changed, 3 insertions(+) diff --git a/lib/pure/ioselects/ioselectors_select.nim b/lib/pure/ioselects/ioselectors_select.nim index 2f22f1b30..eed64a34d 100644 --- a/lib/pure/ioselects/ioselectors_select.nim +++ b/lib/pure/ioselects/ioselectors_select.nim @@ -110,6 +110,7 @@ proc close*[T](s: Selector[T]) = when hasThreadSupport: deallocSharedArray(s.fds) deallocShared(cast[pointer](s)) + deinitLock(s.lock) when defined(windows): proc newSelectEvent*(): SelectEvent = diff --git a/lib/system/threads.nim b/lib/system/threads.nim index 6b858c4bb..bf93b5e01 100644 --- a/lib/system/threads.nim +++ b/lib/system/threads.nim @@ -41,6 +41,8 @@ ## for i in 0..high(thr): ## createThread(thr[i], threadFunc, (i*10, i*10+5)) ## joinThreads(thr) +## +## deinitLock(L) when not declared(ThisIsSystem): {.error: "You must not import this module explicitly".} From 11c377c1149a23657a7f0dd897866cb550ade8d1 Mon Sep 17 00:00:00 2001 From: Miran Date: Tue, 22 Sep 2020 13:08:36 +0200 Subject: [PATCH 0649/3768] add `enumerate` macro (#15297) * add `enumerate` macro * address the comments * put `enumerate` in its own module --- doc/manual.rst | 11 +++++---- lib/pure/sugar.nim | 1 + lib/std/enumerate.nim | 56 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 5 deletions(-) create mode 100644 lib/std/enumerate.nim diff --git a/doc/manual.rst b/doc/manual.rst index ada6261b9..8c167bf62 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -5508,10 +5508,11 @@ type ``system.ForLoopStmt`` can rewrite the entirety of a ``for`` loop: macro enumerate(x: ForLoopStmt): untyped = expectKind x, nnkForStmt - # we strip off the first for loop variable and use - # it as an integer counter: + # check if the starting count is specified: + var countStart = if x[^2].len == 2: newLit(0) else: x[^2][1] result = newStmtList() - result.add newVarStmt(x[0], newLit(0)) + # we strip off the first for loop variable and use it as an integer counter: + result.add newVarStmt(x[0], countStart) var body = x[^1] if body.kind != nnkStmtList: body = newTree(nnkStmtList, body) @@ -5520,7 +5521,7 @@ type ``system.ForLoopStmt`` can rewrite the entirety of a ``for`` loop: for i in 1..x.len-3: newFor.add x[i] # transform enumerate(X) to 'X' - newFor.add x[^2][1] + newFor.add x[^2][^1] newFor.add body result.add newFor # now wrap the whole macro in a block to create a new scope @@ -5532,7 +5533,7 @@ type ``system.ForLoopStmt`` can rewrite the entirety of a ``for`` loop: # without wrapping the macro in a block, we'd need to choose different # names for `a` and `b` here to avoid redefinition errors - for a, b in enumerate([1, 2, 3, 5]): + for a, b in enumerate(10, [1, 2, 3, 5]): echo a, " ", b diff --git a/lib/pure/sugar.nim b/lib/pure/sugar.nim index 0edcc5697..8bfc7b7ff 100644 --- a/lib/pure/sugar.nim +++ b/lib/pure/sugar.nim @@ -335,6 +335,7 @@ macro collect*(init, body: untyped): untyped {.since: (1, 1).} = call.add init[i] result = newTree(nnkStmtListExpr, newVarStmt(res, call), resBody, res) + when isMainModule: since (1, 1): block dup_with_field: diff --git a/lib/std/enumerate.nim b/lib/std/enumerate.nim new file mode 100644 index 000000000..4620af94d --- /dev/null +++ b/lib/std/enumerate.nim @@ -0,0 +1,56 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2020 Nim contributors +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module implements `enumerate` syntactic sugar based on Nim's +## macro system. + +import std/private/since +import macros + + +macro enumerate*(x: ForLoopStmt): untyped {.since: (1, 3).} = + ## Enumerating iterator for collections. + ## + ## It yields `(count, value)` tuples (which must be immediately unpacked). + ## The default starting count `0` can be manually overridden if needed. + runnableExamples: + let a = [10, 20, 30] + var b: seq[(int, int)] + for i, x in enumerate(a): + b.add((i, x)) + assert b == @[(0, 10), (1, 20), (2, 30)] + + let c = "abcd" + var d: seq[(int, char)] + for i, x in enumerate(97, c): + d.add((i, x)) + assert d == @[(97, 'a'), (98, 'b'), (99, 'c'), (100, 'd')] + + expectKind x, nnkForStmt + # check if the starting count is specified: + var countStart = if x[^2].len == 2: newLit(0) else: x[^2][1] + result = newStmtList() + # We strip off the first for loop variable and use it as an integer counter. + # We must immediately decrement it by one, because it gets incremented before + # the loop body - to be able to use the final expression in other macros. + result.add newVarStmt(x[0], infix(countStart, "-", newLit(1))) + var body = x[^1] + if body.kind != nnkStmtList: + body = newTree(nnkStmtList, body) + body.insert(0, newCall(bindSym"inc", x[0])) + var newFor = newTree(nnkForStmt) + for i in 1..x.len-3: + newFor.add x[i] + # transform enumerate(X) to 'X' + newFor.add x[^2][^1] + newFor.add body + result.add newFor + # now wrap the whole macro in a block to create a new scope + result = quote do: + block: `result` From 4b9eea2fcc5d030393ca9020fbbee33fcb48d41a Mon Sep 17 00:00:00 2001 From: Clyybber Date: Tue, 22 Sep 2020 18:24:13 +0200 Subject: [PATCH 0650/3768] Fix forward declarations in shadow scope contexts (#15386) * Fix forward declarations in shadow scope contexts * Add testcase for #15385 * Less empty lines * Fix tests * Inline isShadowScope * Add original testcase (with reduced amount of iterations) * Add testcase without forward decl --- compiler/lookups.nim | 12 ++--- compiler/procfind.nim | 12 ++++- compiler/semstmts.nim | 9 ++-- compiler/semtempl.nim | 4 +- tests/macros/tmacros_issues.nim | 77 +++++++++++++++++++++++++++++++++ 5 files changed, 101 insertions(+), 13 deletions(-) diff --git a/compiler/lookups.nim b/compiler/lookups.nim index c0db25950..744f77cf8 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -91,13 +91,15 @@ proc skipAlias*(s: PSym; n: PNode; conf: ConfigRef): PSym = message(conf, n.info, warnDeprecated, "use " & result.name.s & " instead; " & s.name.s & " is deprecated") +proc isShadowScope*(s: PScope): bool {.inline.} = s.parent != nil and s.parent.depthLevel == s.depthLevel + proc localSearchInScope*(c: PContext, s: PIdent): PSym = - result = strTableGet(c.currentScope.symbols, s) - var shadow = c.currentScope - while result == nil and shadow.parent != nil and shadow.depthLevel == shadow.parent.depthLevel: + var scope = c.currentScope + result = strTableGet(scope.symbols, s) + while result == nil and scope.isShadowScope: # We are in a shadow scope, check in the parent too - result = strTableGet(shadow.parent.symbols, s) - shadow = shadow.parent + scope = scope.parent + result = strTableGet(scope.symbols, s) proc searchInScopes*(c: PContext, s: PIdent): PSym = for scope in walkScopes(c.currentScope): diff --git a/compiler/procfind.nim b/compiler/procfind.nim index 1d897758a..0bdb3dae6 100644 --- a/compiler/procfind.nim +++ b/compiler/procfind.nim @@ -11,7 +11,7 @@ # This is needed for proper handling of forward declarations. import - ast, astalgo, msgs, semdata, types, trees, strutils + ast, astalgo, msgs, semdata, types, trees, strutils, lookups proc equalGenericParams(procA, procB: PNode): bool = if procA.len != procB.len: return false @@ -28,7 +28,7 @@ proc equalGenericParams(procA, procB: PNode): bool = if not exprStructuralEquivalent(a.ast, b.ast): return result = true -proc searchForProc*(c: PContext, scope: PScope, fn: PSym): PSym = +proc searchForProcAux(c: PContext, scope: PScope, fn: PSym): PSym = const flags = {ExactGenericParams, ExactTypeDescValues, ExactConstraints, IgnoreCC} var it: TIdentIter @@ -50,6 +50,14 @@ proc searchForProc*(c: PContext, scope: PScope, fn: PSym): PSym = discard result = nextIdentIter(it, scope.symbols) +proc searchForProc*(c: PContext, scope: PScope, fn: PSym): tuple[proto: PSym, comesFromShadowScope: bool] = + var scope = scope + result.proto = searchForProcAux(c, scope, fn) + while result.proto == nil and scope.isShadowScope: + scope = scope.parent + result.proto = searchForProcAux(c, scope, fn) + result.comesFromShadowScope = true + when false: proc paramsFitBorrow(child, parent: PNode): bool = result = false diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index e99ce8937..53d381f5d 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1898,8 +1898,8 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, elif s.kind == skFunc: incl(s.flags, sfNoSideEffect) incl(s.typ.flags, tfNoSideEffect) - var proto: PSym = if isAnon: nil - else: searchForProc(c, oldScope, s) + var (proto, comesFromShadowScope) = if isAnon: (nil, false) + else: searchForProc(c, oldScope, s) if proto == nil and sfForward in s.flags: #This is a definition that shares its sym with its forward declaration (generated by a macro), #if the symbol is also gensymmed we won't find it with searchForProc, so we check here @@ -1941,8 +1941,9 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, onDefResolveForward(n[namePos].info, proto) if sfForward notin proto.flags and proto.magic == mNone: wrongRedefinition(c, n.info, proto.name.s, proto.info) - excl(proto.flags, sfForward) - incl(proto.flags, sfWasForwarded) + if not comesFromShadowScope: + excl(proto.flags, sfForward) + incl(proto.flags, sfWasForwarded) closeScope(c) # close scope with wrong parameter symbols openScope(c) # open scope for old (correct) parameter symbols if proto.ast[genericParamsPos].kind != nkEmpty: diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index a1afd2c6d..1ec8c07b0 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -662,10 +662,10 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = localError(c.config, n[bodyPos].info, errImplOfXNotAllowed % s.name.s) elif n[bodyPos].kind == nkEmpty: localError(c.config, n.info, "implementation of '$1' expected" % s.name.s) - var proto = searchForProc(c, c.currentScope, s) + var (proto, comesFromShadowscope) = searchForProc(c, c.currentScope, s) if proto == nil: addInterfaceOverloadableSymAt(c, c.currentScope, s) - else: + elif not comesFromShadowscope: symTabReplace(c.currentScope.symbols, proto, s) if n[patternPos].kind != nkEmpty: c.patterns.add(s) diff --git a/tests/macros/tmacros_issues.nim b/tests/macros/tmacros_issues.nim index 882c0735d..f53429888 100644 --- a/tests/macros/tmacros_issues.nim +++ b/tests/macros/tmacros_issues.nim @@ -39,6 +39,7 @@ true false true false +1.0 ''' """ @@ -403,3 +404,79 @@ testEvenOdd5() # it works, because the forward decl and definition share the symbol and the compiler is forgiving here #testEvenOdd6() #Don't test it though, the compiler may become more strict in the future +# bug #15385 +var captured_funcs {.compileTime.}: seq[NimNode] = @[] + +macro aad*(fns: varargs[typed]): typed = + result = newStmtList() + for fn in fns: + captured_funcs.add fn[0] + result.add fn + +func exp*(x: float): float ## get different error if you remove forward declaration + +func exp*(x: float): float {.aad.} = + var x1 = min(max(x, -708.4), 709.8) + var result: float ## looks weird because it is taken from template expansion + result = x1 + 1.0 + result + +template check_accuracy(f: untyped, rng: Slice[float], n: int, verbose = false): auto = + + proc check_accuracy: tuple[avg_ulp: float, max_ulp: int] {.gensym.} = + let k = (rng.b - rng.a) / (float) n + var + res, x: float + i, max_ulp = 0 + avg_ulp = 0.0 + + x = rng.a + while (i < n): + res = f(x) + i.inc + x = x + 0.001 + (avg_ulp, max_ulp) + check_accuracy() + +discard check_accuracy(exp, -730.0..709.4, 4) + +# And without forward decl +macro aad2*(fns: varargs[typed]): typed = + result = newStmtList() + for fn in fns: + captured_funcs.add fn[0] + result.add fn + +func exp2*(x: float): float {.aad2.} = + var x1 = min(max(x, -708.4), 709.8) + var result: float ## looks weird because it is taken from template expansion + result = x1 + 1.0 + result + +template check_accuracy2(f: untyped, rng: Slice[float], n: int, verbose = false): auto = + + proc check_accuracy2: tuple[avg_ulp: float, max_ulp: int] {.gensym.} = + let k = (rng.b - rng.a) / (float) n + var + res, x: float + i, max_ulp = 0 + avg_ulp = 0.0 + + x = rng.a + while (i < n): + res = f(x) + i.inc + x = x + 0.001 + (avg_ulp, max_ulp) + check_accuracy2() + +discard check_accuracy2(exp2, -730.0..709.4, 4) + +# And minimized: +macro aadMin(fn: typed): typed = fn + +func expMin: float + +func expMin: float {.aadMin.} = 1 + +echo expMin() From e2d80b15a6963f4a1ea64c32d7aeec9fcb862260 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 23 Sep 2020 10:01:52 +0200 Subject: [PATCH 0651/3768] finish the stacktraces.nim implementation [backport:1.2] (#15393) --- compiler/condsyms.nim | 1 + lib/system/stacktraces.nim | 12 ++++++------ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index d99b8b7e6..7dc1d183e 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -120,3 +120,4 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimDoesntTrackDefects") defineSymbol("nimHasLentIterators") defineSymbol("nimHasDeclaredMagic") + defineSymbol("nimHasStacktracesModule") diff --git a/lib/system/stacktraces.nim b/lib/system/stacktraces.nim index dce25fdc0..62de27260 100644 --- a/lib/system/stacktraces.nim +++ b/lib/system/stacktraces.nim @@ -22,26 +22,26 @@ when defined(nimStackTraceOverride): ## This is the same as the type ``uintptr_t`` in C. StackTraceOverrideGetTracebackProc* = proc (): string {. - nimcall, gcsafe, locks: 0, raises: [], tags: [].} + nimcall, gcsafe, locks: 0, raises: [], tags: [], noinline.} StackTraceOverrideGetProgramCountersProc* = proc (maxLength: cint): seq[cuintptr_t] {. - nimcall, gcsafe, locks: 0, raises: [], tags: [].} + nimcall, gcsafe, locks: 0, raises: [], tags: [], noinline.} StackTraceOverrideGetDebuggingInfoProc* = proc (programCounters: seq[cuintptr_t], maxLength: cint): seq[StackTraceEntry] {. - nimcall, gcsafe, locks: 0, raises: [], tags: [].} + nimcall, gcsafe, locks: 0, raises: [], tags: [], noinline.} # Default procedures (not normally used, because people opting in on this # override are supposed to register their own versions). var stackTraceOverrideGetTraceback: StackTraceOverrideGetTracebackProc = - proc (): string {.nimcall, gcsafe, locks: 0, raises: [], tags: [].} = + proc (): string {.nimcall, gcsafe, locks: 0, raises: [], tags: [], noinline.} = discard #result = "Stack trace override procedure not registered.\n" stackTraceOverrideGetProgramCounters: StackTraceOverrideGetProgramCountersProc = - proc (maxLength: cint): seq[cuintptr_t] {.nimcall, gcsafe, locks: 0, raises: [], tags: [].} = + proc (maxLength: cint): seq[cuintptr_t] {.nimcall, gcsafe, locks: 0, raises: [], tags: [], noinline.} = discard stackTraceOverrideGetDebuggingInfo: StackTraceOverrideGetDebuggingInfoProc = proc (programCounters: seq[cuintptr_t], maxLength: cint): seq[StackTraceEntry] {. - nimcall, gcsafe, locks: 0, raises: [], tags: [].} = + nimcall, gcsafe, locks: 0, raises: [], tags: [], noinline.} = discard # Custom procedure registration. From 94c8d53a03c437d1601fa75aecb5b84bb694711f Mon Sep 17 00:00:00 2001 From: Araq Date: Wed, 23 Sep 2020 10:41:23 +0200 Subject: [PATCH 0652/3768] 'koch temp' bugfix --- compiler/docgen.nim | 4 ++-- koch.nim | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index ff3a540be..c2ce12e50 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -992,7 +992,7 @@ proc documentEffect(cache: IdentCache; n, x: PNode, effectType: TSpecialWord, id # set the type so that the following analysis doesn't screw up: effects[i].typ = real[i].typ - result = newTreeI(nkExprColonExpr, n.info, + result = newTreeI(nkExprColonExpr, n.info, newIdentNode(getIdent(cache, specialWords[effectType]), n.info), effects) proc documentWriteEffect(cache: IdentCache; n: PNode; flag: TSymFlag; pragmaName: string): PNode = @@ -1005,7 +1005,7 @@ proc documentWriteEffect(cache: IdentCache; n: PNode; flag: TSymFlag; pragmaName effects.add params[i] if effects.len > 0: - result = newTreeI(nkExprColonExpr, n.info, + result = newTreeI(nkExprColonExpr, n.info, newIdentNode(getIdent(cache, pragmaName), n.info), effects) proc documentRaises*(cache: IdentCache; n: PNode) = diff --git a/koch.nim b/koch.nim index 966ff932e..188004571 100644 --- a/koch.nim +++ b/koch.nim @@ -481,7 +481,7 @@ proc temp(args: string) = var (bootArgs, programArgs) = splitArgs(args) if "doc" notin programArgs and "threads" notin programArgs and - "js" notin programArgs: + "js" notin programArgs and "rst2html" notin programArgs: bootArgs.add " -d:leanCompiler" let nimexec = findNim().quoteShell() exec(nimexec & " c -d:debug --debugger:native -d:nimBetterRun " & bootArgs & " " & (d / "compiler" / "nim"), 125) From fe3211fbcfa6b14a2e2fcd4dfb8664dcf8a1bd21 Mon Sep 17 00:00:00 2001 From: Miran Date: Wed, 23 Sep 2020 12:44:41 +0200 Subject: [PATCH 0653/3768] fix the indentation in `--help` and `--fullhelp` (#15387) * fix the indentation in `--help` and `--fullhelp` * a better way to fix it --- compiler/commands.nim | 4 ++-- doc/advopt.txt | 4 ++-- doc/basicopt.txt | 10 +++++----- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/compiler/commands.nim b/compiler/commands.nim index a76a5be73..17124a759 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -59,8 +59,8 @@ proc genFeatureDesc[T: enum](t: typedesc[T]): string {.compileTime.} = result.add $f const - Usage = slurp"../doc/basicopt.txt".replace(" //", " ") - AdvancedUsage = slurp"../doc/advopt.txt".replace(" //", " ") % [genFeatureDesc(Feature), genFeatureDesc(LegacyFeature)] + Usage = slurp"../doc/basicopt.txt".replace(" //", " ") + AdvancedUsage = slurp"../doc/advopt.txt".replace(" //", " ") % [genFeatureDesc(Feature), genFeatureDesc(LegacyFeature)] proc getCommandLineDesc(conf: ConfigRef): string = result = (HelpMessage % [VersionAsString, platform.OS[conf.target.hostOS].name, diff --git a/doc/advopt.txt b/doc/advopt.txt index 9b7f29a5e..8b906298d 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -11,9 +11,9 @@ Advanced commands: //ctags create a tags file //buildIndex build an index for the whole documentation //genDepend generate a DOT file containing the - module dependency graph + module dependency graph //dump dump all defined conditionals and search paths - see also: --dump.format:json (useful with: ` | jq`) + see also: --dump.format:json (useful with: ` | jq`) //check checks the project for syntax and semantic Runtime checks (see -x): diff --git a/doc/basicopt.txt b/doc/basicopt.txt index cdd8ab9be..7ea55cf6c 100644 --- a/doc/basicopt.txt +++ b/doc/basicopt.txt @@ -3,11 +3,11 @@ nim command [options] [projectfile] [arguments] Command: - //compile, c compile project with default code generator (C) - //r compile to $nimcache/projname, run with [arguments] - using backend specified by `--backend` (default: c) - //doc generate the documentation for inputfile for - backend specified by `--backend` (default: c) + //compile, c compile project with default code generator (C) + //r compile to $nimcache/projname, run with [arguments] + using backend specified by `--backend` (default: c) + //doc generate the documentation for inputfile for + backend specified by `--backend` (default: c) Arguments: arguments are passed to the program being run (if --run option is selected) From f21a49b2c4b20402198546aae9d9884bb9fa2a9f Mon Sep 17 00:00:00 2001 From: cooldome Date: Thu, 24 Sep 2020 00:07:30 +0100 Subject: [PATCH 0654/3768] Fix #15389 (#15390) * allow result symbol reuse * try different approach * Revert "try different approach" This reverts commit abcfb6b75983c2f798cc887342ff1a9ff10c0324. --- compiler/semexprs.nim | 18 +++++++++++------- compiler/semstmts.nim | 33 ++++++++++++++++----------------- tests/macros/tmacros_issues.nim | 17 +++++++++++++++++ 3 files changed, 44 insertions(+), 24 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 80e04ead4..d6b146800 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1754,17 +1754,21 @@ proc semReturn(c: PContext, n: PNode): PNode = if c.p.owner.kind in {skConverter, skMethod, skProc, skFunc, skMacro} or (not c.p.owner.typ.isNil and isClosureIterator(c.p.owner.typ)): if n[0].kind != nkEmpty: - # transform ``return expr`` to ``result = expr; return`` - if c.p.resultSym != nil: + if n[0].kind == nkAsgn and n[0][0].kind == nkSym and c.p.resultSym == n[0][0].sym: + discard "return is already transformed" + elif c.p.resultSym != nil: + # transform ``return expr`` to ``result = expr; return`` var a = newNodeI(nkAsgn, n[0].info) a.add newSymNode(c.p.resultSym) a.add n[0] - n[0] = semAsgn(c, a) - # optimize away ``result = result``: - if n[0][1].kind == nkSym and n[0][1].sym == c.p.resultSym: - n[0] = c.graph.emptyNode - else: + n[0] = a + else: localError(c.config, n.info, errNoReturnTypeDeclared) + return + result[0] = semAsgn(c, n[0]) + # optimize away ``result = result``: + if result[0][1].kind == nkSym and result[0][1].sym == c.p.resultSym: + result[0] = c.graph.emptyNode else: localError(c.config, n.info, "'return' not allowed here") diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 53d381f5d..abf2cc0b6 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1430,16 +1430,19 @@ proc semBorrow(c: PContext, n: PNode, s: PSym) = else: localError(c.config, n.info, errNoSymbolToBorrowFromFound) -proc addResult(c: PContext, t: PType, info: TLineInfo, owner: TSymKind) = +proc addResult(c: PContext, n: PNode, t: PType, owner: TSymKind) = if owner == skMacro or t != nil: - var s = newSym(skResult, getIdent(c.cache, "result"), getCurrOwner(c), info) - s.typ = t - incl(s.flags, sfUsed) - addParamOrResult(c, s, owner) - c.p.resultSym = s - -proc addResultNode(c: PContext, n: PNode) = - if c.p.resultSym != nil: n.add newSymNode(c.p.resultSym) + if n.len > resultPos and n[resultPos] != nil: + if n[resultPos].sym.kind != skResult or n[resultPos].sym.owner != getCurrOwner(c): + localError(c.config, n.info, "incorrect result proc symbol") + c.p.resultSym = n[resultPos].sym + else: + var s = newSym(skResult, getIdent(c.cache, "result"), getCurrOwner(c), n.info) + s.typ = t + incl(s.flags, sfUsed) + c.p.resultSym = s + n.add newSymNode(c.p.resultSym) + addParamOrResult(c, c.p.resultSym, owner) proc copyExcept(n: PNode, i: int): PNode = result = copyNode(n) @@ -1563,8 +1566,7 @@ proc semLambda(c: PContext, n: PNode, flags: TExprFlags): PNode = # XXX not good enough; see tnamedparamanonproc.nim if gp.len == 0 or (gp.len == 1 and tfRetType in gp[0].typ.flags): pushProcCon(c, s) - addResult(c, s.typ[0], n.info, skProc) - addResultNode(c, n) + addResult(c, n, s.typ[0], skProc) s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos])) trackProc(c, s, s.ast[bodyPos]) popProcCon(c) @@ -1606,8 +1608,7 @@ proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode {.nosinks.} = pushOwner(c, s) addParams(c, params, skProc) pushProcCon(c, s) - addResult(c, n.typ[0], n.info, skProc) - addResultNode(c, n) + addResult(c, n, n.typ[0], skProc) s.ast[bodyPos] = hloBody(c, semProcBody(c, n[bodyPos])) trackProc(c, s, s.ast[bodyPos]) popProcCon(c) @@ -1637,11 +1638,9 @@ proc activate(c: PContext, n: PNode) = proc maybeAddResult(c: PContext, s: PSym, n: PNode) = if s.kind == skMacro: let resultType = sysTypeFromName(c.graph, n.info, "NimNode") - addResult(c, resultType, n.info, s.kind) - addResultNode(c, n) + addResult(c, n, resultType, s.kind) elif s.typ[0] != nil and not isInlineIterator(s.typ): - addResult(c, s.typ[0], n.info, s.kind) - addResultNode(c, n) + addResult(c, n, s.typ[0], s.kind) proc canonType(c: PContext, t: PType): PType = if t.kind == tySequence: diff --git a/tests/macros/tmacros_issues.nim b/tests/macros/tmacros_issues.nim index f53429888..3d992a27f 100644 --- a/tests/macros/tmacros_issues.nim +++ b/tests/macros/tmacros_issues.nim @@ -480,3 +480,20 @@ func expMin: float func expMin: float {.aadMin.} = 1 echo expMin() + + +# issue #15389 +block double_sem_for_procs: + + macro aad(fns: varargs[typed]): typed = + result = newStmtList() + for fn in fns: + result.add fn + + func exp(x: float): float {.aad.} = + var x1 = min(max(x, -708.4), 709.8) + if x1 > 0.0: + return x1 + 1.0 + result = 10.0 + + discard exp(5.0) \ No newline at end of file From 7a6f4eca1987f41fccf2eea0896f4a433208a355 Mon Sep 17 00:00:00 2001 From: genotrance Date: Thu, 24 Sep 2020 01:07:32 -0500 Subject: [PATCH 0655/3768] Bump nimble (#15398) --- koch.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/koch.nim b/koch.nim index 188004571..cce689135 100644 --- a/koch.nim +++ b/koch.nim @@ -10,7 +10,7 @@ # const - NimbleStableCommit = "f4c818a046c2c3ceb1523c4c974e4b6e625d8ca3" # master + NimbleStableCommit = "26167cd3b7671006c8bd38cbef8708431ddf8687" # master FusionStableCommit = "319aac4d43b04113831b529f8003e82f4af6a4a5" when not defined(windows): From e85c5ad3797b378cf6b93f35cc1d63e2d066b57a Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Thu, 24 Sep 2020 14:54:13 +0800 Subject: [PATCH 0656/3768] fix #15333 (#15336) --- changelog.md | 2 ++ lib/pure/uri.nim | 28 ++++++++++++++++++++++++++-- tests/stdlib/turi.nim | 17 +++++++++++++++++ 3 files changed, 45 insertions(+), 2 deletions(-) create mode 100644 tests/stdlib/turi.nim diff --git a/changelog.md b/changelog.md index 1bc1d7303..bd5674610 100644 --- a/changelog.md +++ b/changelog.md @@ -197,6 +197,8 @@ """ ``` +- Add `initUri(isIpv6: bool)` to `uri` module, now `uri` supports parsing ipv6 hostname. + ## Language changes - The `=destroy` hook no longer has to reset its target, as the compiler now automatically inserts diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim index 04a9d97bd..4b84a8902 100644 --- a/lib/pure/uri.nim +++ b/lib/pure/uri.nim @@ -49,6 +49,7 @@ import std/private/since import strutils, parseutils, base64 include includes/decode_helpers + type Url* = distinct string @@ -56,6 +57,7 @@ type scheme*, username*, password*: string hostname*, port*, path*, query*, anchor*: string opaque*: bool + isIpv6: bool # not expose it for compatibility. proc encodeUrl*(s: string, usePlus = true): string = ## Encodes a URL according to RFC3986. @@ -170,6 +172,7 @@ proc parseAuthority(authority: string, result: var Uri) = inPort = true of '[': inIPv6 = true + result.isIpv6 = true of ']': inIPv6 = false else: @@ -208,6 +211,21 @@ proc initUri*(): Uri = result = Uri(scheme: "", username: "", password: "", hostname: "", port: "", path: "", query: "", anchor: "") +proc initUri*(isIpv6: bool): Uri {.since: (1, 3, 5).} = + ## Initializes a URI with ``scheme``, ``username``, ``password``, + ## ``hostname``, ``port``, ``path``, ``query``, ``anchor`` and ``isIpv6``. + ## + ## **See also:** + ## * `Uri type <#Uri>`_ for available fields in the URI type + runnableExamples: + var uri2 = initUri(isIpv6 = true) + uri2.scheme = "tcp" + uri2.hostname = "2001:0db8:85a3:0000:0000:8a2e:0370:7334" + uri2.port = "8080" + assert $uri2 == "tcp://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:8080" + result = Uri(scheme: "", username: "", password: "", hostname: "", port: "", + path: "", query: "", anchor: "", isIpv6: isIpv6) + proc resetUri(uri: var Uri) = for f in uri.fields: when f is string: @@ -452,9 +470,15 @@ proc `$`*(u: Uri): string = result.add(u.password) result.add("@") if u.hostname.endsWith('/'): - result.add(u.hostname[0..^2]) + if u.isIpv6: + result.add("[" & u.hostname[0 .. ^2] & "]") + else: + result.add(u.hostname[0 .. ^2]) else: - result.add(u.hostname) + if u.isIpv6: + result.add("[" & u.hostname & "]") + else: + result.add(u.hostname) if u.port.len > 0: result.add(":") result.add(u.port) diff --git a/tests/stdlib/turi.nim b/tests/stdlib/turi.nim new file mode 100644 index 000000000..9fb349e88 --- /dev/null +++ b/tests/stdlib/turi.nim @@ -0,0 +1,17 @@ +discard """ + cmd: "nim c -r --styleCheck:hint --panics:on $options $file" + targets: "c" + nimout: "" + action: "run" + exitcode: 0 + timeout: 60.0 +""" +import uri + + +block: + let org = "udp://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:8080" + let url = parseUri(org) + doAssert url.hostname == "2001:0db8:85a3:0000:0000:8a2e:0370:7334" # true + let newUrl = parseUri($url) + doAssert newUrl.hostname == "2001:0db8:85a3:0000:0000:8a2e:0370:7334" # true From 8a6c5edc43bb4ef90feb4eabbc2520004244a011 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Thu, 24 Sep 2020 20:14:23 +0800 Subject: [PATCH 0657/3768] use release version (#15400) --- compiler/nimpaths.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/nimpaths.nim b/compiler/nimpaths.nim index 9caaf4f05..7b2216080 100644 --- a/compiler/nimpaths.nim +++ b/compiler/nimpaths.nim @@ -36,11 +36,11 @@ proc interp*(path: string, nimr: string): string = doAssert '$' notin result, $(path, nimr, result) # avoids un-interpolated variables in output proc getDocHacksJs*(nimr: string, nim = getCurrentCompilerExe(), forceRebuild = false): string = - ## return absolute path to dochhack.js, rebuilding if it doesn't exist or if + ## return absolute path to dochack.js, rebuilding if it doesn't exist or if ## `forceRebuild`. let docHackJs2 = docHackJs.interp(nimr = nimr) if forceRebuild or not docHackJs2.fileExists: - let cmd = "$nim js $file" % ["nim", nim.quoteShell, "file", docHackNim.interp(nimr = nimr).quoteShell] + let cmd = "$nim js -d:release $file" % ["nim", nim.quoteShell, "file", docHackNim.interp(nimr = nimr).quoteShell] echo "getDocHacksJs: cmd: " & cmd doAssert execShellCmd(cmd) == 0, $(cmd) doAssert docHackJs2.fileExists From 4ec5cbac0df2914b71e9813e6df81c248510ae57 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 24 Sep 2020 15:41:45 +0200 Subject: [PATCH 0658/3768] fixes #15361 (#15401) * fixes #15361; better cursor inference --- compiler/varpartitions.nim | 167 +++++++++++++++++++++++------- tests/arc/top_no_cursor2.nim | 53 ++++++++++ tests/arc/topt_no_cursor.nim | 4 +- tests/destructor/tdestructor3.nim | 11 +- tests/destructor/tmatrix.nim | 6 +- 5 files changed, 196 insertions(+), 45 deletions(-) create mode 100644 tests/arc/top_no_cursor2.nim diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim index f983f9416..9297c19e1 100644 --- a/compiler/varpartitions.nim +++ b/compiler/varpartitions.nim @@ -13,6 +13,17 @@ ## algorithm. ## The used data structure is "union find" with path compression. +## We perform two passes over the AST: +## - Pass one (``computeLiveRanges``): collect livetimes of local +## variables and whether they are potentially re-assigned. +## - Pass two (``traverse``): combine local variables to abstract "graphs". +## Strict func checking: Ensure that graphs that are connected to +## const parameters are not mutated. +## Cursor inference: Ensure that potential cursors are not +## borrowed from locations that are connected to a graph +## that is mutated during the liveness of the cursor. +## (We track all possible mutations of a graph.) + import ast, types, lineinfos, options, msgs, renderer from trees import getMagic, whichPragma from wordrecg import wNoSideEffect @@ -26,7 +37,8 @@ type VarFlag = enum ownsData, - preventCursor + preventCursor, + isReassigned VarIndexKind = enum isEmptyRoot, @@ -266,36 +278,38 @@ proc allRoots(n: PNode; result: var seq[PSym]; followDotExpr = true) = else: discard "nothing to do" -proc analyseAsgn(c: var Partitions; dest: var VarIndex; n: PNode) = +proc destMightOwn(c: var Partitions; dest: var VarIndex; n: PNode) = + ## Analyse if 'n' is an expression that owns the data, if so mark 'dest' + ## with 'ownsData'. case n.kind of nkEmpty, nkCharLit..nkNilLit: # primitive literals including the empty are harmless: discard of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv, nkCast, nkConv: - analyseAsgn(c, dest, n[1]) + destMightOwn(c, dest, n[1]) of nkIfStmt, nkIfExpr: for i in 0.. 0: - analyseAsgn(c, dest, n[^1]) + destMightOwn(c, dest, n[^1]) of nkClosure: for i in 1..= 0: - analyseAsgn(c, c.s[vid], src) - # do not borrow from a different local variable, this is easier - # than tracking reassignments, consider 'var cursor = local; local = newNode()' + destMightOwn(c, c.s[vid], src) if src.kind == nkSym: - if (src.sym.kind in {skVar, skResult, skTemp} or - (src.sym.kind in {skLet, skParam, skForVar} and hasDisabledAsgn(src.sym.typ))): + let s = src.sym + if {sfGlobal, sfThread} * s.flags != {} or hasDisabledAsgn(s.typ): + # do not borrow from a global variable or from something with a + # disabled assignment operator. c.s[vid].flags.incl preventCursor - elif src.sym.kind in {skVar, skResult, skTemp, skLet, skForVar}: - # XXX: we need to compute variable alive ranges before doing anything else: - let srcid = variableId(c, src.sym) - if srcid >= 0 and preventCursor in c.s[srcid].flags: - # you cannot borrow from a local that lives shorter than 'vid': - if c.s[srcid].aliveStart > c.s[vid].aliveStart or - c.s[srcid].aliveEnd < c.s[vid].aliveEnd: + when false: echo "A not a cursor: ", dest.sym, " ", s + else: + let srcid = variableId(c, s) + if srcid >= 0: + if s.kind notin {skResult, skParam} and ( + c.s[srcid].aliveEnd < c.s[vid].aliveEnd): + # you cannot borrow from a local that lives shorter than 'vid': + when false: echo "B not a cursor ", dest.sym, " ", c.s[srcid].aliveEnd, " ", c.s[vid].aliveEnd + c.s[vid].flags.incl preventCursor + elif {isReassigned, preventCursor} * c.s[srcid].flags != {}: + # you cannot borrow from something that is re-assigned: + when false: echo "C not a cursor ", dest.sym, " ", c.s[srcid].flags c.s[vid].flags.incl preventCursor - if src.kind == nkSym and hasDestructor(src.typ): - rhsIsSink(c, src) + #if src.kind == nkSym and hasDestructor(src.typ): + # rhsIsSink(c, src) + +const + nodesToIgnoreSet = {nkNone..pred(nkSym), succ(nkSym)..nkNilLit, + nkTypeSection, nkProcDef, nkConverterDef, + nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo, + nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt, + nkExportStmt, nkPragma, nkCommentStmt, nkBreakState, nkTypeOfExpr} proc traverse(c: var Partitions; n: PNode) = inc c.abstractTime @@ -418,11 +444,11 @@ proc traverse(c: var Partitions; n: PNode) = if child.kind == nkVarTuple and last.kind in {nkPar, nkTupleConstr}: if child.len-2 != last.len: return for i in 0..= 0: - c.s[id].aliveEnd = max(c.s[id].aliveEnd, c.abstractTime) - of nkNone..pred(nkSym), succ(nkSym)..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef, - nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo, - nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt, - nkExportStmt, nkPragma, nkCommentStmt, nkBreakState, nkTypeOfExpr: + of nodesToIgnoreSet: dec c.abstractTime discard "do not follow the construct" of nkCallKinds: @@ -514,6 +533,79 @@ proc traverse(c: var Partitions; n: PNode) = else: for child in n: traverse(c, child) +proc computeLiveRanges(c: var Partitions; n: PNode) = + # first pass: Compute live ranges for locals. + # **Watch out!** We must traverse the tree like 'traverse' does + # so that the 'c.abstractTime' is consistent. + inc c.abstractTime + case n.kind + of nkLetSection, nkVarSection: + for child in n: + let last = lastSon(child) + computeLiveRanges(c, last) + if child.kind == nkVarTuple and last.kind in {nkPar, nkTupleConstr}: + if child.len-2 != last.len: return + for i in 0..= 0: + c.s[vid].flags.incl isReassigned + + of nkSym: + dec c.abstractTime + if n.sym.kind in {skVar, skResult, skTemp, skLet, skForVar, skParam}: + let id = variableId(c, n.sym) + if id >= 0: + c.s[id].aliveEnd = max(c.s[id].aliveEnd, c.abstractTime) + + of nodesToIgnoreSet: + dec c.abstractTime + discard "do not follow the construct" + of nkCallKinds: + for child in n: computeLiveRanges(c, child) + + let parameters = n[0].typ + let L = if parameters != nil: parameters.len else: 0 + + for i in 1..= 0: + c.s[vid].flags.incl isReassigned + + of nkAddr, nkHiddenAddr: + computeLiveRanges(c, n[0]) + if n[0].kind == nkSym: + let vid = variableId(c, n[0].sym) + if vid >= 0: + c.s[vid].flags.incl preventCursor + + of nkPragmaBlock: + computeLiveRanges(c, n.lastSon) + of nkWhileStmt, nkForStmt, nkParForStmt: + for child in n: computeLiveRanges(c, child) + # analyse loops twice so that 'abstractTime' suffices to detect cases + # like: + # while cond: + # mutate(graph) + # connect(graph, cursorVar) + for child in n: computeLiveRanges(c, child) + else: + for child in n: computeLiveRanges(c, child) + proc computeGraphPartitions*(s: PSym; n: PNode; cursorInference = false): Partitions = result = Partitions(performCursorInference: cursorInference) if s.kind notin {skModule, skMacro}: @@ -523,6 +615,9 @@ proc computeGraphPartitions*(s: PSym; n: PNode; cursorInference = false): Partit if resultPos < s.ast.safeLen: registerVariable(result, s.ast[resultPos]) + computeLiveRanges(result, n) + # resart the timer for the second pass: + result.abstractTime = 0 traverse(result, n) proc dangerousMutation(g: MutationInfo; v: VarIndex): bool = @@ -560,7 +655,7 @@ proc computeCursors*(s: PSym; n: PNode; config: ConfigRef) = var par = computeGraphPartitions(s, n, true) for i in 0 ..< par.s.len: let v = addr(par.s[i]) - if v.flags == {} and v.sym.kind notin {skParam, skResult} and + if v.flags * {ownsData, preventCursor} == {} and v.sym.kind notin {skParam, skResult} and v.sym.flags * {sfThread, sfGlobal} == {} and hasDestructor(v.sym.typ) and v.sym.typ.skipTypes({tyGenericInst, tyAlias}).kind != tyOwned: let rid = root(par, i) diff --git a/tests/arc/top_no_cursor2.nim b/tests/arc/top_no_cursor2.nim new file mode 100644 index 000000000..5dfde57aa --- /dev/null +++ b/tests/arc/top_no_cursor2.nim @@ -0,0 +1,53 @@ +discard """ + output: '''true +true +true +true +true''' + cmd: "nim c --gc:arc $file" +""" +# bug #15361 + +type + ErrorNodeKind = enum Branch, Leaf + Error = ref object + case kind: ErrorNodeKind + of Branch: + left: Error + right: Error + of Leaf: + leafError: string + input: string + +proc ret(input: string, lefterr, righterr: Error): Error = + result = Error(kind: Branch, left: lefterr, right: righterr, input: input) + +proc parser() = + var rerrors: Error + let lerrors = Error( + kind: Leaf, + leafError: "first error", + input: "123 ;" + ) + # If you remove "block" - everything works + block: + let rresult = Error( + kind: Leaf, + leafError: "second error", + input: ";" + ) + # this assignment is needed too + rerrors = rresult + + # Returns Error(kind: Branch, left: lerrors, right: rerrors, input: "some val") + # needs to be a proc call for some reason, can't inline the result + var data = ret(input = "some val", lefterr = lerrors, righterr = rerrors) + + echo data.left.leafError == "first error" + echo data.left.input == "123 ;" + # stacktrace shows this line + echo data.right.leafError == "second error" + echo data.right.input == ";" + echo data.input == "some val" + +parser() diff --git a/tests/arc/topt_no_cursor.nim b/tests/arc/topt_no_cursor.nim index 8a33cb2ee..1e5cf7142 100644 --- a/tests/arc/topt_no_cursor.nim +++ b/tests/arc/topt_no_cursor.nim @@ -52,7 +52,7 @@ _ = ( blitTmp, ";") lvalue = _[0] lnext_cursor = _[1] -`=sink`(result.value, lvalue) +`=sink`(result.value, move lvalue) -- end of expandArc ------------------------ --expandArc: tt @@ -148,7 +148,7 @@ proc p1(): Maybe = var lnext: string (lvalue, lnext) = (lresult, ";") - result.value = lvalue + result.value = move lvalue proc tissue15130 = doAssert p1().value == @[123] diff --git a/tests/destructor/tdestructor3.nim b/tests/destructor/tdestructor3.nim index f967bbf95..ca9a891e1 100644 --- a/tests/destructor/tdestructor3.nim +++ b/tests/destructor/tdestructor3.nim @@ -23,17 +23,18 @@ joinable: false type T = object proc `=`(lhs: var T, rhs: T) = - echo "assign" + echo "assign" proc `=destroy`(v: var T) = - echo "destroy" + echo "destroy" proc use(x: T) = discard proc usedToBeBlock = - var v1 : T - var v2 : T = v1 - use v1 + var v1 = T() + var v2: T = v1 + discard addr(v2) # prevent cursorfication + use v1 usedToBeBlock() diff --git a/tests/destructor/tmatrix.nim b/tests/destructor/tmatrix.nim index a3bd59df3..98ca95c94 100644 --- a/tests/destructor/tmatrix.nim +++ b/tests/destructor/tmatrix.nim @@ -96,14 +96,16 @@ proc info = allocCount = 0 deallocCount = 0 +proc copy(a: Matrix): Matrix = a + proc test1 = var a = matrix(5, 5, 1.0) - var b = a + var b = copy a var c = a + b proc test2 = var a = matrix(5, 5, 1.0) - var b = a + var b = copy a var c = -a proc test3 = From 496e9f74e12b1ad73b621872771e9634404f95ff Mon Sep 17 00:00:00 2001 From: Miran Date: Thu, 24 Sep 2020 16:41:08 +0200 Subject: [PATCH 0659/3768] fix #14474, crash inside of a sole code-block (#15402) --- lib/packages/docutils/rst.nim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 05b58c56b..123922c99 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -1102,6 +1102,8 @@ proc whichSection(p: RstParser): RstNodeKind = of tkPunct: if isMarkdownHeadline(p): result = rnHeadline + elif p.tok[p.idx].symbol == "```": + result = rnCodeBlock elif match(p, tokenAfterNewline(p), "ai"): result = rnHeadline elif p.tok[p.idx].symbol == "::": From ce72d7ffc512c99e8eb8825b08c9985763f878b6 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 24 Sep 2020 19:34:59 +0200 Subject: [PATCH 0660/3768] fixes #15403 (#15404) --- compiler/sempass2.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index b48d7e29e..d6db0885e 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -1247,7 +1247,8 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) = var partitions = computeGraphPartitions(s, body) if not t.hasSideEffect and t.hasDangerousAssign: t.hasSideEffect = varpartitions.hasSideEffect(partitions, mutationInfo) - checkBorrowedLocations(partitions, g.config) + if views in c.features: + checkBorrowedLocations(partitions, g.config) if sfThread in s.flags and t.gcUnsafe: if optThreads in g.config.globalOptions and optThreadAnalysis in g.config.globalOptions: From e3b5f80a890428a0c9b5c2bdd7a3205d79946957 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 24 Sep 2020 22:07:23 +0200 Subject: [PATCH 0661/3768] more precise borrow checking of 'result' (#15406) --- compiler/sempass2.nim | 2 +- compiler/varpartitions.nim | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index d6db0885e..797aff7f7 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -1248,7 +1248,7 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) = if not t.hasSideEffect and t.hasDangerousAssign: t.hasSideEffect = varpartitions.hasSideEffect(partitions, mutationInfo) if views in c.features: - checkBorrowedLocations(partitions, g.config) + checkBorrowedLocations(partitions, body, g.config) if sfThread in s.flags and t.gcUnsafe: if optThreads in g.config.globalOptions and optThreadAnalysis in g.config.globalOptions: diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim index 9297c19e1..474a362cd 100644 --- a/compiler/varpartitions.nim +++ b/compiler/varpartitions.nim @@ -110,6 +110,9 @@ proc registerVariable(c: var Partitions; n: PNode) = connectedVia: unknownLineInfo, flags: {connectsConstParam}, maxMutation: -1, minConnection: high(int), mutations: @[]) + elif n.sym.kind == skResult: + c.s.add VarIndex(kind: isEmptyRoot, sym: n.sym, + aliveStart: high(int), aliveEnd: c.abstractTime) else: c.s.add VarIndex(kind: isEmptyRoot, sym: n.sym, aliveStart: c.abstractTime, aliveEnd: c.abstractTime) @@ -567,6 +570,8 @@ proc computeLiveRanges(c: var Partitions; n: PNode) = let id = variableId(c, n.sym) if id >= 0: c.s[id].aliveEnd = max(c.s[id].aliveEnd, c.abstractTime) + if n.sym.kind == skResult: + c.s[id].aliveStart = min(c.s[id].aliveStart, c.abstractTime) of nodesToIgnoreSet: dec c.abstractTime @@ -621,7 +626,7 @@ proc computeGraphPartitions*(s: PSym; n: PNode; cursorInference = false): Partit traverse(result, n) proc dangerousMutation(g: MutationInfo; v: VarIndex): bool = - #echo "range ", v.aliveStart, " .. ", v.aliveEnd + #echo "range ", v.aliveStart, " .. ", v.aliveEnd, " ", v.sym if isMutated in g.flags: for m in g.mutations: #echo "mutation ", m @@ -643,7 +648,7 @@ proc cannotBorrow(config: ConfigRef; s: PSym; g: MutationInfo) = m.add " is the statement that connected the mutation to the parameter" localError(config, s.info, m) -proc checkBorrowedLocations*(par: var Partitions; config: ConfigRef) = +proc checkBorrowedLocations*(par: var Partitions; body: PNode; config: ConfigRef) = for i in 0 ..< par.s.len: let s = par.s[i].sym if s.kind != skParam and isViewType(s.typ): From 68ec7a018b85e978c437e776dfcc183cf7b79057 Mon Sep 17 00:00:00 2001 From: cooldome Date: Thu, 24 Sep 2020 21:21:14 +0100 Subject: [PATCH 0662/3768] Revert "fix #15035 (#15236)" (#15408) This reverts commit f8c48fc1863a243718acf86b699baed1a5c1512e. --- compiler/injectdestructors.nim | 5 ++--- tests/arc/t14383.nim | 19 +------------------ 2 files changed, 3 insertions(+), 21 deletions(-) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 9d1f43595..28f2c93f1 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -699,9 +699,8 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode = elif n.kind in {nkObjDownConv, nkObjUpConv}: result = copyTree(n) result[0] = p(n[0], c, s, sinkArg) - elif n.typ == nil or n.typ.skipTypes({tyGenericInst, tyAlias, tySink, tyVar}).kind == tyOpenArray: - # 'raise X' can be part of a 'case' expression. Deal with it here. - # openarrays require perfect forwarding + elif n.typ == nil: + # 'raise X' can be part of a 'case' expression. Deal with it here: result = p(n, c, s, normal) else: # copy objects that are not temporary but passed to a 'sink' parameter diff --git a/tests/arc/t14383.nim b/tests/arc/t14383.nim index 78afc58e2..f112a1a6c 100644 --- a/tests/arc/t14383.nim +++ b/tests/arc/t14383.nim @@ -3,7 +3,6 @@ discard """ output: ''' hello hello -@[4, 3, 2, 1] @["a", "b"] ''' """ @@ -14,22 +13,6 @@ var val = parseMinValue() if val.kind == minDictionary: echo val - -#------------------------------------------------------------------------------ -# Issue #15035 -#------------------------------------------------------------------------------ - -proc countRun(lst: sink openArray[int]): int = - discard - -proc timSort(lst: var openArray[int]) = - let run = countRun(lst) - -var a = @[4, 3, 2, 1] -timSort(a) -echo a - - #------------------------------------------------------------------------------ # Issue #15238 #------------------------------------------------------------------------------ @@ -50,4 +33,4 @@ echo x #------------------------------------------------------------------------------ import std/os -discard getFileInfo(".") \ No newline at end of file +discard getFileInfo(".") From e9fa486493b5fa796cfd7d312b0004d5ee6ea6f5 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 25 Sep 2020 08:49:21 +0200 Subject: [PATCH 0663/3768] fixes #14983 (#15320) * fixes #14983 * allow bootstrapping with 0.20 * added a test case for the new system.add with a sink parameter * make npeg green again --- lib/system.nim | 63 ++++++++++++++++++++++++++++----------- tests/destructor/tarc.nim | 15 +++++++++- 2 files changed, 59 insertions(+), 19 deletions(-) diff --git a/lib/system.nim b/lib/system.nim index eefa84565..a9c45f8fa 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1215,24 +1215,51 @@ when defined(nimscript) or not defined(nimSeqsV2): ## Generic code becomes much easier to write if the Nim naming scheme is ## respected. -proc add*[T](x: var seq[T], y: openArray[T]) {.noSideEffect.} = - ## Generic proc for adding a container `y` to a container `x`. - ## - ## For containers that have an order, `add` means *append*. New generic - ## containers should also call their adding proc `add` for consistency. - ## Generic code becomes much easier to write if the Nim naming scheme is - ## respected. - ## - ## See also: - ## * `& proc <#&,seq[T][T],seq[T][T]>`_ - ## - ## .. code-block:: Nim - ## var s: seq[string] = @["test2","test2"] - ## s.add("test") # s <- @[test2, test2, test] - {.noSideEffect.}: - let xl = x.len - setLen(x, xl + y.len) - for i in 0..high(y): x[xl+i] = y[i] +when defined(gcDestructors): + proc add*[T](x: var seq[T], y: sink openArray[T]) {.noSideEffect.} = + ## Generic proc for adding a container `y` to a container `x`. + ## + ## For containers that have an order, `add` means *append*. New generic + ## containers should also call their adding proc `add` for consistency. + ## Generic code becomes much easier to write if the Nim naming scheme is + ## respected. + ## + ## See also: + ## * `& proc <#&,seq[T][T],seq[T][T]>`_ + ## + ## .. code-block:: Nim + ## var s: seq[string] = @["test2","test2"] + ## s.add("test") # s <- @[test2, test2, test] + {.noSideEffect.}: + let xl = x.len + setLen(x, xl + y.len) + for i in 0..high(y): + when nimvm: + # workaround the fact that the VM does not yet + # handle sink parameters properly: + x[xl+i] = y[i] + else: + x[xl+i] = move y[i] +else: + proc add*[T](x: var seq[T], y: openArray[T]) {.noSideEffect.} = + ## Generic proc for adding a container `y` to a container `x`. + ## + ## For containers that have an order, `add` means *append*. New generic + ## containers should also call their adding proc `add` for consistency. + ## Generic code becomes much easier to write if the Nim naming scheme is + ## respected. + ## + ## See also: + ## * `& proc <#&,seq[T][T],seq[T][T]>`_ + ## + ## .. code-block:: Nim + ## var s: seq[string] = @["test2","test2"] + ## s.add("test") # s <- @[test2, test2, test] + {.noSideEffect.}: + let xl = x.len + setLen(x, xl + y.len) + for i in 0..high(y): x[xl+i] = y[i] + when defined(nimSeqsV2): template movingCopy(a, b) = diff --git a/tests/destructor/tarc.nim b/tests/destructor/tarc.nim index 12ea46b2c..54d75a410 100644 --- a/tests/destructor/tarc.nim +++ b/tests/destructor/tarc.nim @@ -8,6 +8,8 @@ Hello 2 0 List +@["4", "5", "6", "", "", "a", ""] +@["", "", "a", ""] ''' cmd: '''nim c --gc:arc $file''' """ @@ -162,10 +164,21 @@ type case kind: TagKind of List: values: seq[Tag] - of Compound: + of Compound: compound: Table[string, Tag] var a = Tag(kind: List) var b = a echo a.kind var c = a + +proc testAdd(i: int; yyy: openArray[string]) = + var x: seq[string] + x.add [$i, $(i+1), $(i+2)] + x.add yyy + echo x + +var y = newSeq[string](4) +y[2] = "a" +testAdd(4, y) +echo y From 2de6e18774ec9f0f15c88ab916d7f501ba13b467 Mon Sep 17 00:00:00 2001 From: Miran Date: Fri, 25 Sep 2020 09:25:47 +0200 Subject: [PATCH 0664/3768] fix #11537, correct parse inline code without surrounding spaces (#15399) --- doc/advopt.txt | 2 +- lib/packages/docutils/rst.nim | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/advopt.txt b/doc/advopt.txt index 8b906298d..830b8bfc6 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -13,7 +13,7 @@ Advanced commands: //genDepend generate a DOT file containing the module dependency graph //dump dump all defined conditionals and search paths - see also: --dump.format:json (useful with: ` | jq`) + see also: --dump.format:json (useful with: `| jq`) //check checks the project for syntax and semantic Runtime checks (see -x): diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 123922c99..56bfe580b 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -507,6 +507,7 @@ proc isInlineMarkupEnd(p: RstParser, markup: string): bool = if not result: return # Rule 4: result = (p.tok[p.idx + 1].kind in {tkIndent, tkWhite, tkEof}) or + (markup in ["``", "`"] and p.tok[p.idx + 1].kind in {tkIndent, tkWhite, tkWord, tkEof}) or (p.tok[p.idx + 1].symbol[0] in {'\'', '\"', ')', ']', '}', '>', '-', '/', '\\', ':', '.', ',', ';', '!', '?', '_'}) @@ -522,6 +523,7 @@ proc isInlineMarkupStart(p: RstParser, markup: string): bool = if not result: return # Rule 1: result = (p.idx == 0) or (p.tok[p.idx - 1].kind in {tkIndent, tkWhite}) or + (markup in ["``", "`"] and p.tok[p.idx - 1].kind in {tkIndent, tkWhite, tkWord}) or (p.tok[p.idx - 1].symbol[0] in {'\'', '\"', '(', '[', '{', '<', '-', '/', ':', '_'}) if not result: From d4892e9388084e71f9389efce7bba42d8c069d09 Mon Sep 17 00:00:00 2001 From: alaviss Date: Sat, 26 Sep 2020 07:31:55 +0000 Subject: [PATCH 0665/3768] koch, compiler: bundle fusion as part of the source archive (#15409) This allows distributions to build Nim from the downloaded source archive without an Internet connection. --- compiler/installer.ini | 1 + koch.nim | 3 ++- tools/deps.nim | 25 +++++++++++++++++++++---- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/compiler/installer.ini b/compiler/installer.ini index 543a3befb..01b7b8c6b 100644 --- a/compiler/installer.ini +++ b/compiler/installer.ini @@ -76,6 +76,7 @@ Files: "lib" [Other] Files: "examples" Files: "dist/nimble" +Files: "dist/fusion" Files: "tests" diff --git a/koch.nim b/koch.nim index cce689135..68c5764be 100644 --- a/koch.nim +++ b/koch.nim @@ -201,7 +201,8 @@ proc bundleWinTools(args: string) = proc bundleFusion(latest: bool) = let commit = if latest: "HEAD" else: FusionStableCommit - cloneDependency(distDir, "https://github.com/nim-lang/fusion.git", commit) + cloneDependency(distDir, "https://github.com/nim-lang/fusion.git", commit, + allowBundled = true) copyDir(distDir / "fusion" / "src" / "fusion", "lib" / "fusion") proc zip(latest: bool; args: string) = diff --git a/tools/deps.nim b/tools/deps.nim index 888237761..394dc0d25 100644 --- a/tools/deps.nim +++ b/tools/deps.nim @@ -1,13 +1,25 @@ -import os, uri, strformat +import os, uri, strformat, osproc proc exec(cmd: string) = echo "deps.cmd: " & cmd let status = execShellCmd(cmd) doAssert status == 0, cmd +proc execEx(cmd: string): tuple[output: TaintedString, exitCode: int] = + echo "deps.cmd: " & cmd + execCmdEx(cmd, {poStdErrToStdOut, poUsePath, poEvalCommand}) + +proc isGitRepo(dir: string): bool = + # This command is used to get the relative path to the root of the repository. + # Using this, we can verify whether a folder is a git repository by checking + # whether the command success and if the output is empty. + let (output, status) = execEx fmt"git -C {quoteShell(dir)} rev-parse --show-cdup" + result = status == 0 and output == "" + const commitHead* = "HEAD" -proc cloneDependency*(destDirBase: string, url: string, commit = commitHead, appendRepoName = true) = +proc cloneDependency*(destDirBase: string, url: string, commit = commitHead, + appendRepoName = true, allowBundled = false) = let destDirBase = destDirBase.absolutePath let p = url.parseUri.path let name = p.splitFile.name @@ -18,5 +30,10 @@ proc cloneDependency*(destDirBase: string, url: string, commit = commitHead, app # note: old code used `destDir / .git` but that wouldn't prevent git clone # from failing exec fmt"git clone -q {url} {destDir2}" - exec fmt"git -C {destDir2} fetch -q" - exec fmt"git -C {destDir2} checkout -q {commit}" + if isGitRepo(destDir): + exec fmt"git -C {destDir2} fetch -q" + exec fmt"git -C {destDir2} checkout -q {commit}" + elif allowBundled: + discard "this dependency was bundled with Nim, don't do anything" + else: + quit "FAILURE: " & destdir & " already exists but is not a git repo" From e6616115e65381aaab427c557431d7992bb25063 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 26 Sep 2020 13:11:35 +0200 Subject: [PATCH 0666/3768] cursor inference: makes combparser work; refactorings (#15411) * cursor inference: makes combparser work; refactorings --- compiler/varpartitions.nim | 139 ++++++++++++++++--------------- testament/important_packages.nim | 2 +- tests/arc/topt_no_cursor.nim | 6 +- tests/arc/topt_refcursors.nim | 2 + 4 files changed, 78 insertions(+), 71 deletions(-) diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim index 474a362cd..5af6e56b5 100644 --- a/compiler/varpartitions.nim +++ b/compiler/varpartitions.nim @@ -45,13 +45,17 @@ type dependsOn, isRootOf - VarIndex = object - flags: set[VarFlag] + Connection = object case kind: VarIndexKind of isEmptyRoot: discard of dependsOn: parent: int of isRootOf: graphIndex: int + + VarIndex = object + con: Connection + flags: set[VarFlag] sym: PSym + reassignedTo: int aliveStart, aliveEnd: int # the range for which the variable is alive. MutationInfo* = object @@ -96,60 +100,62 @@ proc hasSideEffect*(c: var Partitions; info: var MutationInfo): bool = template isConstParam(a): bool = a.kind == skParam and a.typ.kind != tyVar -proc variableId(c: Partitions; x: PSym): int {.inline.} = +proc variableId(c: Partitions; x: PSym): int = for i in 0 ..< c.s.len: if c.s[i].sym == x: return i return -1 +proc registerResult(c: var Partitions; n: PNode) = + if n.kind == nkSym: + c.s.add VarIndex(con: Connection(kind: isEmptyRoot), sym: n.sym, reassignedTo: 0, + aliveStart: high(int), aliveEnd: c.abstractTime) + +proc registerParam(c: var Partitions; n: PNode) = + assert n.kind == nkSym + if isConstParam(n.sym): + c.s.add VarIndex(con: Connection(kind: isRootOf, graphIndex: c.graphs.len), + sym: n.sym, reassignedTo: 0, + aliveStart: c.abstractTime, aliveEnd: c.abstractTime) + c.graphs.add MutationInfo(param: n.sym, mutatedHere: unknownLineInfo, + connectedVia: unknownLineInfo, flags: {connectsConstParam}, + maxMutation: -1, minConnection: high(int), + mutations: @[]) + else: + c.s.add VarIndex(con: Connection(kind: isEmptyRoot), sym: n.sym, reassignedTo: 0, + aliveStart: c.abstractTime, aliveEnd: c.abstractTime) + proc registerVariable(c: var Partitions; n: PNode) = if n.kind == nkSym and variableId(c, n.sym) < 0: - if isConstParam(n.sym): - c.s.add VarIndex(kind: isRootOf, graphIndex: c.graphs.len, sym: n.sym, - aliveStart: c.abstractTime, aliveEnd: c.abstractTime) - c.graphs.add MutationInfo(param: n.sym, mutatedHere: unknownLineInfo, - connectedVia: unknownLineInfo, flags: {connectsConstParam}, - maxMutation: -1, minConnection: high(int), - mutations: @[]) - elif n.sym.kind == skResult: - c.s.add VarIndex(kind: isEmptyRoot, sym: n.sym, - aliveStart: high(int), aliveEnd: c.abstractTime) - else: - c.s.add VarIndex(kind: isEmptyRoot, sym: n.sym, - aliveStart: c.abstractTime, aliveEnd: c.abstractTime) + c.s.add VarIndex(con: Connection(kind: isEmptyRoot), sym: n.sym, reassignedTo: 0, + aliveStart: c.abstractTime, aliveEnd: c.abstractTime) proc root(v: var Partitions; start: int): int = result = start var depth = 0 - while v.s[result].kind == dependsOn: - result = v.s[result].parent + while v.s[result].con.kind == dependsOn: + result = v.s[result].con.parent inc depth if depth > 0: # path compression: var it = start - while v.s[it].kind == dependsOn: - let next = v.s[it].parent - v.s[it] = VarIndex(kind: dependsOn, parent: result, - sym: v.s[it].sym, flags: v.s[it].flags, - aliveStart: v.s[it].aliveStart, - aliveEnd: v.s[it].aliveEnd) + while v.s[it].con.kind == dependsOn: + let next = v.s[it].con.parent + v.s[it].con = Connection(kind: dependsOn, parent: result) it = next proc potentialMutation(v: var Partitions; s: PSym; info: TLineInfo) = let id = variableId(v, s) if id >= 0: let r = root(v, id) - case v.s[r].kind + case v.s[r].con.kind of isEmptyRoot: - v.s[r] = VarIndex(kind: isRootOf, graphIndex: v.graphs.len, - sym: v.s[r].sym, flags: v.s[r].flags, - aliveStart: v.s[r].aliveStart, - aliveEnd: v.s[r].aliveEnd) + v.s[r].con = Connection(kind: isRootOf, graphIndex: v.graphs.len) v.graphs.add MutationInfo(param: if isConstParam(s): s else: nil, mutatedHere: info, connectedVia: unknownLineInfo, flags: {isMutated}, maxMutation: v.abstractTime, minConnection: high(int), mutations: @[v.abstractTime]) of isRootOf: - let g = addr v.graphs[v.s[r].graphIndex] + let g = addr v.graphs[v.s[r].con.graphIndex] if g.param == nil and isConstParam(s): g.param = s if v.abstractTime > g.maxMutation: @@ -189,29 +195,24 @@ proc connect(v: var Partitions; a, b: PSym; info: TLineInfo) = var mut = 0 var con = v.abstractTime var gb: ptr MutationInfo = nil - if v.s[rb].kind == isRootOf: - gb = addr v.graphs[v.s[rb].graphIndex] + if v.s[rb].con.kind == isRootOf: + gb = addr v.graphs[v.s[rb].con.graphIndex] if param == nil: param = gb.param mutatedHere = gb.mutatedHere rbFlags = gb.flags mut = gb.maxMutation con = min(con, gb.minConnection) - v.s[rb] = VarIndex(kind: dependsOn, parent: ra, sym: v.s[rb].sym, flags: v.s[rb].flags, - aliveStart: v.s[rb].aliveStart, - aliveEnd: v.s[rb].aliveEnd) - case v.s[ra].kind + v.s[rb].con = Connection(kind: dependsOn, parent: ra) + case v.s[ra].con.kind of isEmptyRoot: - v.s[ra] = VarIndex(kind: isRootOf, graphIndex: v.graphs.len, sym: v.s[ra].sym, - flags: v.s[ra].flags, - aliveStart: v.s[ra].aliveStart, - aliveEnd: v.s[ra].aliveEnd) + v.s[ra].con = Connection(kind: isRootOf, graphIndex: v.graphs.len) v.graphs.add MutationInfo(param: param, mutatedHere: mutatedHere, connectedVia: info, flags: paramFlags + rbFlags, maxMutation: mut, minConnection: con, mutations: if gb != nil: gb.mutations else: @[]) of isRootOf: - var g = addr v.graphs[v.s[ra].graphIndex] + var g = addr v.graphs[v.s[ra].con.graphIndex] if g.param == nil: g.param = param if g.mutatedHere == unknownLineInfo: g.mutatedHere = mutatedHere g.minConnection = min(g.minConnection, con) @@ -376,15 +377,13 @@ proc noCursor(c: var Partitions, s: PSym) = if vid >= 0: c.s[vid].flags.incl preventCursor -proc rhsIsSink(c: var Partitions, n: PNode) = - if n.kind == nkSym and n.typ.skipTypes(abstractInst-{tyOwned}).kind == tyRef: - discard "do no pessimize simple refs further, injectdestructors.nim will prevent moving from it" - else: - var roots: seq[PSym] - allRoots(n, roots, followDotExpr = false) - # let x = cursor? --> treat it like a sink parameter - for r in roots: - noCursor(c, r) +proc pretendOwnsData(c: var Partitions, s: PSym) = + let vid = variableId(c, s) + if vid >= 0: + c.s[vid].flags.incl ownsData + +const + explainCursors = false proc deps(c: var Partitions; dest, src: PNode) = var targets, sources: seq[PSym] @@ -407,28 +406,29 @@ proc deps(c: var Partitions; dest, src: PNode) = let vid = variableId(c, dest.sym) if vid >= 0: destMightOwn(c, c.s[vid], src) - if src.kind == nkSym: - let s = src.sym - if {sfGlobal, sfThread} * s.flags != {} or hasDisabledAsgn(s.typ): + for s in sources: + if s == dest.sym: + discard "assignments like: it = it.next are fine" + elif {sfGlobal, sfThread} * s.flags != {} or hasDisabledAsgn(s.typ): # do not borrow from a global variable or from something with a # disabled assignment operator. c.s[vid].flags.incl preventCursor - when false: echo "A not a cursor: ", dest.sym, " ", s + when explainCursors: echo "A not a cursor: ", dest.sym, " ", s else: let srcid = variableId(c, s) if srcid >= 0: if s.kind notin {skResult, skParam} and ( c.s[srcid].aliveEnd < c.s[vid].aliveEnd): # you cannot borrow from a local that lives shorter than 'vid': - when false: echo "B not a cursor ", dest.sym, " ", c.s[srcid].aliveEnd, " ", c.s[vid].aliveEnd + when explainCursors: echo "B not a cursor ", dest.sym, " ", c.s[srcid].aliveEnd, " ", c.s[vid].aliveEnd c.s[vid].flags.incl preventCursor elif {isReassigned, preventCursor} * c.s[srcid].flags != {}: # you cannot borrow from something that is re-assigned: - when false: echo "C not a cursor ", dest.sym, " ", c.s[srcid].flags + when explainCursors: echo "C not a cursor ", dest.sym, " ", c.s[srcid].flags, " reassignedTo ", c.s[srcid].reassignedTo + c.s[vid].flags.incl preventCursor + elif c.s[srcid].reassignedTo != 0 and c.s[srcid].reassignedTo != dest.sym.id: + when explainCursors: echo "D not a cursor ", dest.sym, " reassignedTo ", c.s[srcid].reassignedTo c.s[vid].flags.incl preventCursor - - #if src.kind == nkSym and hasDestructor(src.typ): - # rhsIsSink(c, src) const nodesToIgnoreSet = {nkNone..pred(nkSym), succ(nkSym)..nkNilLit, @@ -501,7 +501,7 @@ proc traverse(c: var Partitions; n: PNode) = # we assume constructions with cursors are better without # the cursors because it's likely we can move then, see # test arc/topt_no_cursor.nim - noCursor(c, n[i].sym) + pretendOwnsData(c, n[i].sym) of nkObjConstr: for child in n: traverse(c, child) @@ -512,7 +512,7 @@ proc traverse(c: var Partitions; n: PNode) = # we assume constructions with cursors are better without # the cursors because it's likely we can move then, see # test arc/topt_no_cursor.nim - noCursor(c, it.sym) + pretendOwnsData(c, it.sym) of nkPragmaBlock: let pragmaList = n[0] @@ -562,7 +562,10 @@ proc computeLiveRanges(c: var Partitions; n: PNode) = if n[0].kind == nkSym: let vid = variableId(c, n[0].sym) if vid >= 0: - c.s[vid].flags.incl isReassigned + if n[1].kind == nkSym and (c.s[vid].reassignedTo == 0 or c.s[vid].reassignedTo == n[1].sym.id): + c.s[vid].reassignedTo = n[1].sym.id + else: + c.s[vid].flags.incl isReassigned of nkSym: dec c.abstractTime @@ -616,9 +619,9 @@ proc computeGraphPartitions*(s: PSym; n: PNode; cursorInference = false): Partit if s.kind notin {skModule, skMacro}: let params = s.typ.n for i in 1.. Date: Sun, 27 Sep 2020 09:20:10 +0200 Subject: [PATCH 0667/3768] better support for slices as views (#15414) * moved view tests to tests/views * refactoring * more refactorings * better support for system.toOpenArray for first class view types --- compiler/ccgcalls.nim | 85 ++++++++++++--------- compiler/ccgexprs.nim | 12 ++- compiler/semexprs.nim | 10 ++- tests/{effects => views}/tcannot_borrow.nim | 0 tests/views/tsplit_into_openarray.nim | 37 +++++++++ tests/{ccgbugs => views}/tviews1.nim | 0 6 files changed, 100 insertions(+), 44 deletions(-) rename tests/{effects => views}/tcannot_borrow.nim (100%) create mode 100644 tests/views/tsplit_into_openarray.nim rename tests/{ccgbugs => views}/tviews1.nim (100%) diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 6d254ca6f..31e5e5924 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -148,9 +148,52 @@ proc reifiedOpenArray(n: PNode): bool {.inline.} = else: result = true -proc openArrayLoc(p: BProc, formalType: PType, n: PNode): Rope = - var a: TLoc +proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType): (Rope, Rope) = + var a, b, c: TLoc + initLocExpr(p, q[1], a) + initLocExpr(p, q[2], b) + initLocExpr(p, q[3], c) + # but first produce the required index checks: + if optBoundsCheck in p.options: + genBoundsCheck(p, a, b, c) + let ty = skipTypes(a.t, abstractVar+{tyPtr}) + let dest = getTypeDesc(p.module, destType) + let lengthExpr = "($1)-($2)+1" % [rdLoc(c), rdLoc(b)] + case ty.kind + of tyArray: + let first = toInt64(firstOrd(p.config, ty)) + if first == 0: + result = ("($3*)(($1)+($2))" % [rdLoc(a), rdLoc(b), dest], + lengthExpr) + else: + result = ("($4*)($1)+(($2)-($3))" % + [rdLoc(a), rdLoc(b), intLiteral(first), dest], + lengthExpr) + of tyOpenArray, tyVarargs: + if reifiedOpenArray(q[1]): + result = ("($3*)($1.d)+($2)" % [rdLoc(a), rdLoc(b), dest], + lengthExpr) + else: + result = ("($3*)($1)+($2)" % [rdLoc(a), rdLoc(b), dest], + lengthExpr) + of tyUncheckedArray, tyCString: + result = ("($3*)($1)+($2)" % [rdLoc(a), rdLoc(b), dest], + lengthExpr) + of tyString, tySequence: + let atyp = skipTypes(a.t, abstractInst) + if formalType.skipTypes(abstractInst).kind in {tyVar} and atyp.kind == tyString and + optSeqDestructors in p.config.globalOptions: + linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)]) + if atyp.kind in {tyVar} and not compileToCpp(p.module): + result = ("($4*)(*$1)$3+($2)" % [rdLoc(a), rdLoc(b), dataField(p), dest], + lengthExpr) + else: + result = ("($4*)$1$3+($2)" % [rdLoc(a), rdLoc(b), dataField(p), dest], + lengthExpr) + else: + internalError(p.config, "openArrayLoc: " & typeToString(a.t)) +proc openArrayLoc(p: BProc, formalType: PType, n: PNode): Rope = var q = skipConv(n) var skipped = false while q.kind == nkStmtListExpr and q.len > 0: @@ -164,42 +207,10 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode): Rope = for i in 0.. Date: Sun, 27 Sep 2020 20:47:58 +0300 Subject: [PATCH 0668/3768] Fix proc generic params ident defs, missing empty (#15412) --- doc/astspec.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/astspec.txt b/doc/astspec.txt index ab2fbcacc..c41aee96f 100644 --- a/doc/astspec.txt +++ b/doc/astspec.txt @@ -1255,7 +1255,9 @@ AST: nnkEmpty(), # patterns for term rewriting in templates and macros (not procs) nnkGenericParams( # generic type parameters, like with type declaration nnkIdentDefs( - nnkIdent("T"), nnkIdent("SomeInteger") + nnkIdent("T"), + nnkIdent("SomeInteger"), + nnkEmpty() ) ), nnkFormalParams( From f1ac01459cfecdaa26fd26128bb586236d4ba24e Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 27 Sep 2020 20:44:22 +0200 Subject: [PATCH 0669/3768] produce runtime type information for reified openArrays (#15415) * produce runtime type information for reified openArrays * added a test case --- compiler/ccgcalls.nim | 7 ++++-- compiler/ccgexprs.nim | 22 ++++++++--------- compiler/ccgtypes.nim | 24 ++++++++++++++----- tests/views/tsplit_into_seq.nim | 42 +++++++++++++++++++++++++++++++++ 4 files changed, 76 insertions(+), 19 deletions(-) create mode 100644 tests/views/tsplit_into_seq.nim diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 31e5e5924..6f0d327d9 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -171,7 +171,7 @@ proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType): (Rope, lengthExpr) of tyOpenArray, tyVarargs: if reifiedOpenArray(q[1]): - result = ("($3*)($1.d)+($2)" % [rdLoc(a), rdLoc(b), dest], + result = ("($3*)($1.Field0)+($2)" % [rdLoc(a), rdLoc(b), dest], lengthExpr) else: result = ("($3*)($1)+($2)" % [rdLoc(a), rdLoc(b), dest], @@ -215,7 +215,10 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode): Rope = case skipTypes(a.t, abstractVar).kind of tyOpenArray, tyVarargs: if reifiedOpenArray(n): - result = "$1.d, $1.l" % [rdLoc(a)] + if a.t.kind in {tyVar, tyLent}: + result = "$1->Field0, $1->Field1" % [rdLoc(a)] + else: + result = "$1.Field0, $1.Field1" % [rdLoc(a)] else: result = "$1, $1Len_0" % [rdLoc(a)] of tyString, tySequence: diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index f348a7e26..b6808a55e 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -278,23 +278,23 @@ proc genOpenArrayConv(p: BProc; d: TLoc; a: TLoc) = case a.t.skipTypes(abstractVar).kind of tyOpenArray, tyVarargs: if reifiedOpenArray(a.lode): - linefmt(p, cpsStmts, "$1.d = $2.d; $1.l = $2.l;$n", + linefmt(p, cpsStmts, "$1.Field0 = $2.Field0; $1.Field1 = $2.Field1;$n", [rdLoc(d), a.rdLoc]) else: - linefmt(p, cpsStmts, "$1.d = $2; $1.l = $2Len_0;$n", + linefmt(p, cpsStmts, "$1.Field0 = $2; $1.Field1 = $2Len_0;$n", [rdLoc(d), a.rdLoc]) of tySequence: - linefmt(p, cpsStmts, "$1.d = $2$3; $1.l = $4;$n", + linefmt(p, cpsStmts, "$1.Field0 = $2$3; $1.Field1 = $4;$n", [rdLoc(d), a.rdLoc, dataField(p), lenExpr(p, a)]) of tyArray: - linefmt(p, cpsStmts, "$1.d = $2; $1.l = $3;$n", + linefmt(p, cpsStmts, "$1.Field0 = $2; $1.Field1 = $3;$n", [rdLoc(d), rdLoc(a), rope(lengthOrd(p.config, a.t))]) of tyString: let etyp = skipTypes(a.t, abstractInst) if etyp.kind in {tyVar} and optSeqDestructors in p.config.globalOptions: linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)]) - linefmt(p, cpsStmts, "$1.d = $2$3; $1.l = $4;$n", + linefmt(p, cpsStmts, "$1.Field0 = $2$3; $1.Field1 = $4;$n", [rdLoc(d), a.rdLoc, dataField(p), lenExpr(p, a)]) else: internalError(p.config, a.lode.info, "cannot handle " & $a.t.kind) @@ -943,7 +943,7 @@ proc genBoundsCheck(p: BProc; arr, a, b: TLoc) = if reifiedOpenArray(arr.lode): linefmt(p, cpsStmts, "if ($2-$1 != -1 && " & - "((NU)($1) >= (NU)($3.l) || (NU)($2) >= (NU)($3.l))){ #raiseIndexError(); $4}$n", + "((NU)($1) >= (NU)($3.Field1) || (NU)($2) >= (NU)($3.Field1))){ #raiseIndexError(); $4}$n", [rdLoc(a), rdLoc(b), rdLoc(arr), raiseInstr(p)]) else: linefmt(p, cpsStmts, @@ -977,11 +977,11 @@ proc genOpenArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) = ropecg(p.module, "$1[$2]", [rdLoc(a), rdCharLoc(b)]), a.storage) else: if optBoundsCheck in p.options: - linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2.l)){ #raiseIndexError2($1,$2.l-1); $3}$n", + linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2.Field1)){ #raiseIndexError2($1,$2.Field1-1); $3}$n", [rdLoc(b), rdLoc(a), raiseInstr(p)]) # BUGFIX: ``>=`` and not ``>``! inheritLocation(d, a) putIntoDest(p, d, n, - ropecg(p.module, "$1.d[$2]", [rdLoc(a), rdCharLoc(b)]), a.storage) + ropecg(p.module, "$1.Field0[$2]", [rdLoc(a), rdCharLoc(b)]), a.storage) proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) = var a, b: TLoc @@ -1739,8 +1739,8 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) = if op == mHigh: unaryExpr(p, e, d, "($1Len_0-1)") else: unaryExpr(p, e, d, "$1Len_0") else: - if op == mHigh: unaryExpr(p, e, d, "($1.l-1)") - else: unaryExpr(p, e, d, "$1.l") + if op == mHigh: unaryExpr(p, e, d, "($1.Field1-1)") + else: unaryExpr(p, e, d, "$1.Field1") of tyCString: if op == mHigh: unaryExpr(p, e, d, "($1 ? (#nimCStrLen($1)-1) : -1)") else: unaryExpr(p, e, d, "($1 ? #nimCStrLen($1) : 0)") @@ -2218,7 +2218,7 @@ proc genDispose(p: BProc; n: PNode) = proc genSlice(p: BProc; e: PNode; d: var TLoc) = let (x, y) = genOpenArraySlice(p, e, e.typ, e.typ.lastSon) if d.k == locNone: getTemp(p, e.typ, d) - linefmt(p, cpsStmts, "$1.d = $2; $1.l = $3;$n", [rdLoc(d), x, y]) + linefmt(p, cpsStmts, "$1.Field0 = $2; $1.Field1 = $3;$n", [rdLoc(d), x, y]) when false: localError(p.config, e.info, "invalid context for 'toOpenArray'; " & "'toOpenArray' is only valid within a call expression") diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 771e1ede9..8dee6ee70 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -693,7 +693,7 @@ proc getOpenArrayDesc(m: BModule, t: PType, check: var IntSet; kind: TSymKind): result = getTypeName(m, t, sig) m.typeCache[sig] = result let elemType = getTypeDescWeak(m, t[0], check, kind) - m.s[cfsTypes].addf("typedef struct {$n$2* d;$nNI l;$n} $1;$n", + m.s[cfsTypes].addf("typedef struct {$n$2* Field0;$nNI Field1;$n} $1;$n", [result, elemType]) proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKind): Rope = @@ -724,7 +724,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin compileToCpp(m): "&" else: "*" var et = origTyp.skipTypes(abstractInst).lastSon var etB = et.skipTypes(abstractInst) - if mapType(m.config, t, kind) == ctPtrToArray: + if mapType(m.config, t, kind) == ctPtrToArray and (etB.kind != tyOpenArray or kind == skParam): if etB.kind == tySet: et = getSysType(m.g.graph, unknownLineInfo, tyUInt8) else: @@ -1030,7 +1030,7 @@ proc genTypeInfoAuxBase(m: BModule; typ, origType: PType; if tfIncompleteStruct in typ.flags: size = rope"void*" else: - size = getTypeDesc(m, origType) + size = getTypeDesc(m, origType, skVar) m.s[cfsTypeInit3].addf( "$1.size = sizeof($2);$n$1.align = NIM_ALIGNOF($2);$n$1.kind = $3;$n$1.base = $4;$n", [nameHcr, size, rope(nimtypeKind), base] @@ -1128,7 +1128,7 @@ proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope; m.s[cfsTypeInit3].addf("$1.kind = 3;$n" & "$1.offset = offsetof($2, $3);$n" & "$1.typ = $4;$n" & "$1.name = $5;$n" & "$1.sons = &$6[0];$n" & - "$1.len = $7;$n", [expr, getTypeDesc(m, origType), field.loc.r, + "$1.len = $7;$n", [expr, getTypeDesc(m, origType, skVar), field.loc.r, genTypeInfoV1(m, field.typ, info), makeCString(field.name.s), tmp, rope(L)]) @@ -1165,7 +1165,7 @@ proc genObjectFields(m: BModule, typ, origType: PType, n: PNode, expr: Rope; internalError(m.config, n.info, "genObjectFields") m.s[cfsTypeInit3].addf("$1.kind = 1;$n" & "$1.offset = offsetof($2, $3);$n" & "$1.typ = $4;$n" & - "$1.name = $5;$n", [expr, getTypeDesc(m, origType), + "$1.name = $5;$n", [expr, getTypeDesc(m, origType, skVar), field.loc.r, genTypeInfoV1(m, field.typ, info), makeCString(field.name.s)]) else: internalError(m.config, n.info, "genObjectFields") @@ -1201,7 +1201,7 @@ proc genTupleInfo(m: BModule, typ, origType: PType, name: Rope; info: TLineInfo) "$1.offset = offsetof($2, Field$3);$n" & "$1.typ = $4;$n" & "$1.name = \"Field$3\";$n", - [tmp2, getTypeDesc(m, origType), rope(i), genTypeInfoV1(m, a, info)]) + [tmp2, getTypeDesc(m, origType, skVar), rope(i), genTypeInfoV1(m, a, info)]) m.s[cfsTypeInit3].addf("$1.len = $2; $1.kind = 2; $1.sons = &$3[0];$n", [expr, rope(typ.len), tmp]) else: @@ -1387,6 +1387,15 @@ proc genTypeInfoV2(m: BModule, t: PType; info: TLineInfo): Rope = genTypeInfoV2Impl(m, t, origType, result, info) result = prefixTI.rope & result & ")".rope +proc openArrayToTuple(m: BModule; t: PType): PType = + result = newType(tyTuple, t.owner) + let p = newType(tyPtr, t.owner) + let a = newType(tyUncheckedArray, t.owner) + a.add t.lastSon + p.add a + result.add p + result.add getSysType(m.g.graph, t.owner.info, tyInt) + proc genTypeInfoV1(m: BModule, t: PType; info: TLineInfo): Rope = let origType = t var t = skipTypes(origType, irrelevantForBackend + tyUserTypeClasses) @@ -1461,6 +1470,9 @@ proc genTypeInfoV1(m: BModule, t: PType; info: TLineInfo): Rope = # BUGFIX: use consistently RTTI without proper field names; otherwise # results are not deterministic! genTupleInfo(m, t, origType, result, info) + of tyOpenArray: + let x = openArrayToTuple(m, t) + genTupleInfo(m, x, origType, result, info) else: internalError(m.config, "genTypeInfoV1(" & $t.kind & ')') if t.attachedOps[attachedDeepCopy] != nil: diff --git a/tests/views/tsplit_into_seq.nim b/tests/views/tsplit_into_seq.nim new file mode 100644 index 000000000..409451601 --- /dev/null +++ b/tests/views/tsplit_into_seq.nim @@ -0,0 +1,42 @@ +discard """ + output: '''asdf +asdf +231 +231 +''' + cmd: "nim c $file" +""" + +{.experimental: "views".} + +const + Whitespace = {' ', '\t', '\n', '\r'} + +proc split*(s: string, seps: set[char] = Whitespace, + maxsplit: int = -1): seq[openArray[char]] = + var last = 0 + var splits = maxsplit + result = @[] + + while last <= len(s): + var first = last + while last < len(s) and s[last] notin seps: + inc(last) + if splits == 0: last = len(s) + {.noSideEffect.}: + result.add toOpenArray(s, first, last-1) + result.add toOpenArray(s, first, last-1) + if splits == 0: break + dec(splits) + inc(last) + +proc `$`(x: openArray[char]): string = + result = newString(x.len) + for i in 0.. Date: Mon, 28 Sep 2020 10:51:13 +0200 Subject: [PATCH 0670/3768] cleanup lib/system/stacktraces.nim; refs #15416 (#15418) --- lib/system.nim | 6 ------ lib/system/stacktraces.nim | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/lib/system.nim b/lib/system.nim index a9c45f8fa..0784cacce 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1410,12 +1410,6 @@ type # these work for most platforms: culonglong* {.importc: "unsigned long long", nodecl.} = uint64 ## This is the same as the type ``unsigned long long`` in *C*. - # There is a disparity on macOS where Nim's `uint` is `unsigned long long` and - # `uintptr_t` is `unsigned long`. Even though both data types are the same - # size (64 bits), clang++ refuses to do automatic conversion between them. - cuintptr_t* {.importc: "uintptr_t", nodecl.} = uint - ## This is the same as the type ``uintptr_t`` in *C*. - cstringArray* {.importc: "char**", nodecl.} = ptr UncheckedArray[cstring] ## This is binary compatible to the type ``char**`` in *C*. The array's ## high value is large enough to disable bounds checking in practice. diff --git a/lib/system/stacktraces.nim b/lib/system/stacktraces.nim index 62de27260..6c7b1433c 100644 --- a/lib/system/stacktraces.nim +++ b/lib/system/stacktraces.nim @@ -18,7 +18,7 @@ const when defined(nimStackTraceOverride): ## Procedure types for overriding the default stack trace. type - cuintptr_t {.importc: "uintptr_t", nodecl.} = uint + cuintptr_t* {.importc: "uintptr_t", nodecl.} = uint ## This is the same as the type ``uintptr_t`` in C. StackTraceOverrideGetTracebackProc* = proc (): string {. From 50b6f6996ab604931d8ee7ca2a26bfdce5559933 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 28 Sep 2020 13:51:39 +0200 Subject: [PATCH 0671/3768] .noalias annotation; frontend support (#15419) * .noalias annotation; frontend support * added .noalias support to Nim; implements https://github.com/nim-lang/RFCs/issues/204 --- changelog.md | 5 ++++- compiler/ast.nim | 5 +++-- compiler/ccgtypes.nim | 2 ++ compiler/cgen.nim | 3 +++ compiler/pragmas.nim | 8 ++++++-- compiler/semtypes.nim | 10 ++++++---- compiler/wordrecg.nim | 4 ++-- doc/manual_experimental.rst | 15 +++++++++++++++ lib/impure/nre.nim | 2 +- lib/nimbase.h | 3 +++ tests/ccgbugs/tnoalias.nim | 8 ++++++++ 11 files changed, 53 insertions(+), 12 deletions(-) create mode 100644 tests/ccgbugs/tnoalias.nim diff --git a/changelog.md b/changelog.md index bd5674610..fffcdcb35 100644 --- a/changelog.md +++ b/changelog.md @@ -195,7 +195,7 @@ is cool! """ - ``` + ``` - Add `initUri(isIpv6: bool)` to `uri` module, now `uri` supports parsing ipv6 hostname. @@ -283,6 +283,9 @@ proc mydiv(a, b): int {.raises: [].} = an experimental feature. In other words, you don't have to write pragma `{.experimental: "forLoopMacros".}` if you want to use them. +- Added a ``.noalias`` pragma. It is mapped to C's ``restrict`` keyword for the increased + performance this keyword can enable. + ## Compiler changes diff --git a/compiler/ast.nim b/compiler/ast.nim index 4d37b284c..6bcc6845d 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -229,7 +229,7 @@ type TNodeKinds* = set[TNodeKind] type - TSymFlag* = enum # 43 flags! + TSymFlag* = enum # 46 flags! sfUsed, # read access of sym (for warnings) or simply used sfExported, # symbol is exported from module sfFromGeneric, # symbol is instantiation of a generic; this is needed @@ -293,7 +293,8 @@ type sfNeverRaises # proc can never raise an exception, not even OverflowDefect # or out-of-memory sfUsedInFinallyOrExcept # symbol is used inside an 'except' or 'finally' - sfSingleUsedTemp # For temporaries that we know will only be used once + sfSingleUsedTemp # For temporaries that we know will only be used once + sfNoalias # 'noalias' annotation, means C's 'restrict' TSymFlags* = set[TSymFlag] diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 8dee6ee70..ff7b17590 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -474,6 +474,8 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope, else: params.add(getTypeDescAux(m, param.typ, check, skParam)) params.add(~" ") + if sfNoalias in param.flags: + params.add(~"NIM_NOALIAS ") params.add(param.loc.r) # declare the len field for open arrays: var arr = param.typ diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 242d1f89b..f50676647 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -503,6 +503,7 @@ proc localVarDecl(p: BProc; n: PNode): Rope = #elif skipTypes(s.typ, abstractInst).kind in GcTypeKinds: # decl.add(" GC_GUARD") if sfVolatile in s.flags: result.add(" volatile") + if sfNoalias in s.flags: result.add(" NIM_NOALIAS") result.add(" ") result.add(s.loc.r) else: @@ -563,6 +564,7 @@ proc assignGlobalVar(p: BProc, n: PNode; value: Rope) = if p.hcrOn: decl.add("*") if sfRegister in s.flags: decl.add(" register") if sfVolatile in s.flags: decl.add(" volatile") + if sfNoalias in s.flags: decl.add(" NIM_NOALIAS") if value != nil: decl.addf(" $1 = $2;$n", [s.loc.r, value]) else: @@ -1261,6 +1263,7 @@ proc genVarPrototype(m: BModule, n: PNode) = if lfDynamicLib in sym.loc.flags: m.s[cfsVars].add("*") if sfRegister in sym.flags: m.s[cfsVars].add(" register") if sfVolatile in sym.flags: m.s[cfsVars].add(" volatile") + if sfNoalias in sym.flags: m.s[cfsVars].add(" NIM_NOALIAS") m.s[cfsVars].addf(" $1;$n", [sym.loc.r]) if m.hcrOn: m.initProc.procSec(cpsLocals).addf( "\t$1 = ($2*)hcrGetGlobal($3, \"$1\");$n", [sym.loc.r, diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 71bd6f81f..3b7ae175d 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -65,14 +65,15 @@ const wInheritable, wGensym, wInject, wRequiresInit, wUnchecked, wUnion, wPacked, wBorrow, wGcSafe, wPartial, wExplain, wPackage} fieldPragmas* = declPragmas + { - wGuard, wBitsize, wCursor, wRequiresInit} - {wExportNims, wNodecl} # why exclude these? + wGuard, wBitsize, wCursor, wRequiresInit, wNoalias} - {wExportNims, wNodecl} # why exclude these? varPragmas* = declPragmas + {wVolatile, wRegister, wThreadVar, wMagic, wHeader, wCompilerProc, wCore, wDynlib, wNoInit, wCompileTime, wGlobal, - wGensym, wInject, wCodegenDecl, wGuard, wGoto, wCursor} + wGensym, wInject, wCodegenDecl, wGuard, wGoto, wCursor, wNoalias} constPragmas* = declPragmas + {wHeader, wMagic, wGensym, wInject, wIntDefine, wStrDefine, wBoolDefine, wCompilerProc, wCore} + paramPragmas* = {wNoalias, wInject, wGensym} letPragmas* = varPragmas procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNoSideEffect, wThread, wRaises, wLocks, wTags, wGcSafe, @@ -864,6 +865,9 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of wRegister: noVal(c, it) incl(sym.flags, sfRegister) + of wNoalias: + noVal(c, it) + incl(sym.flags, sfNoalias) of wThreadVar: noVal(c, it) incl(sym.flags, {sfThread, sfGlobal}) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 4d912a46f..9bb7673fa 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1264,6 +1264,8 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, for j in 0.. Date: Mon, 28 Sep 2020 22:54:17 -0700 Subject: [PATCH 0672/3768] document that items no longer works with enum with holes (#15426) --- changelog.md | 1 + 1 file changed, 1 insertion(+) diff --git a/changelog.md b/changelog.md index fffcdcb35..474f1eae1 100644 --- a/changelog.md +++ b/changelog.md @@ -286,6 +286,7 @@ proc mydiv(a, b): int {.raises: [].} = - Added a ``.noalias`` pragma. It is mapped to C's ``restrict`` keyword for the increased performance this keyword can enable. +- `items` no longer compiles with enum with holes as its behavior was error prone, see #14004 ## Compiler changes From 22d0a3993268c334e62461f39ef02b1d02c64ca1 Mon Sep 17 00:00:00 2001 From: Yuriy Glukhov Date: Tue, 29 Sep 2020 17:55:53 +0300 Subject: [PATCH 0673/3768] Dont assert on setstacksize result in iOS (#15427) [backport:1.2] --- lib/system/threads.nim | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/system/threads.nim b/lib/system/threads.nim index bf93b5e01..cd1cf5e29 100644 --- a/lib/system/threads.nim +++ b/lib/system/threads.nim @@ -319,7 +319,10 @@ else: when hasSharedHeap: t.core.stackSize = ThreadStackSize var a {.noinit.}: Pthread_attr doAssert pthread_attr_init(a) == 0 - doAssert pthread_attr_setstacksize(a, ThreadStackSize) == 0 + let setstacksizeResult = pthread_attr_setstacksize(a, ThreadStackSize) + when not defined(ios): + # This fails on iOS + doAssert(setstacksizeResult == 0) if pthread_create(t.sys, a, threadProcWrapper[TArg], addr(t)) != 0: raise newException(ResourceExhaustedError, "cannot create thread") doAssert pthread_attr_destroy(a) == 0 From f8866598e7e0226aac7a6720adf4440278f9508d Mon Sep 17 00:00:00 2001 From: shirleyquirk <31934565+shirleyquirk@users.noreply.github.com> Date: Tue, 29 Sep 2020 16:08:24 +0100 Subject: [PATCH 0674/3768] odbc regression from #14357 (#15417) #14357 changed from sending 'val', an int, to a pointer to int, which is understandable, but not how SQLSetEnvAttr works. "Depending on the value of Attribute, ValuePtr will be a 32-bit integer value or point to a null-terminated character string" --- lib/impure/db_odbc.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/impure/db_odbc.nim b/lib/impure/db_odbc.nim index 8aadfae73..3568cf1cc 100644 --- a/lib/impure/db_odbc.nim +++ b/lib/impure/db_odbc.nim @@ -508,7 +508,7 @@ proc open*(connection, user, password, database: string): DbConn {. if res != SQL_SUCCESS: dbError("Error: unable to initialise ODBC environment.") res = SQLSetEnvAttr(result.env, SQL_ATTR_ODBC_VERSION.TSqlInteger, - cast[SqlPointer](val.addr), resLen.TSqlInteger) + cast[SqlPointer](val), 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) From 4058801607b37fa707eb1b41d129ce74557eccac Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 29 Sep 2020 23:42:38 +0200 Subject: [PATCH 0675/3768] spec for view types (#15424) * spec for view types * spec additions * refactoring; there are two different kinds of views * refactorings and spec additions * enforce that view types are initialized * enforce borrowing from the first formal parameter * enforce lifetimes for borrowing of locals * typo in the manual * clarify in the implementation what a borrow operation really is --- compiler/ast.nim | 3 - compiler/semexprs.nim | 6 +- compiler/sempass2.nim | 4 +- compiler/typeallowed.nim | 75 +++++-- compiler/varpartitions.nim | 196 ++++++++++++++++-- doc/manual.rst | 8 +- doc/manual_experimental.rst | 190 ++++++++++++++++- tests/views/tcannot_borrow.nim | 13 +- .../tmust_borrow_from_first_parameter.nim | 9 + tests/views/tsplit_into_seq.nim | 5 +- tests/views/tviews1.nim | 3 +- 11 files changed, 446 insertions(+), 66 deletions(-) create mode 100644 tests/views/tmust_borrow_from_first_parameter.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index 6bcc6845d..8adbfa15c 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1403,9 +1403,6 @@ proc copyType*(t: PType, owner: PSym, keepId: bool): PType = proc exactReplica*(t: PType): PType = copyType(t, t.owner, true) -template requiresInit*(t: PType): bool = - t.flags * {tfRequiresInit, tfNotNil} != {} - proc copySym*(s: PSym): PSym = result = newSym(s.kind, s.name, s.owner, s.info, s.options) #result.ast = nil # BUGFIX; was: s.ast which made problems diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index e26ea42dd..cb852c68a 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1601,7 +1601,7 @@ proc takeImplicitAddr(c: PContext, n: PNode; isLent: bool): PNode = proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} = if le.kind == nkHiddenDeref: var x = le[0] - if (x.typ.kind in {tyVar, tyLent} or isViewType(x.typ)) and x.kind == nkSym and x.sym.kind == skResult: + if (x.typ.kind in {tyVar, tyLent} or classifyViewType(x.typ) != noView) and x.kind == nkSym and x.sym.kind == skResult: n[0] = x # 'result[]' --> 'result' n[1] = takeImplicitAddr(c, ri, x.typ.kind == tyLent) x.typ.flags.incl tfVarIsPtr @@ -1712,8 +1712,8 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = if le == nil: localError(c.config, a.info, "expression has no type") elif (skipTypes(le, {tyGenericInst, tyAlias, tySink}).kind notin {tyVar} and - isAssignable(c, a) in {arNone, arLentValue}) or - skipTypes(le, abstractVar).kind in {tyOpenArray, tyVarargs}: + isAssignable(c, a) in {arNone, arLentValue}) or ( + skipTypes(le, abstractVar).kind in {tyOpenArray, tyVarargs} and views notin c.features): # Direct assignment to a discriminant is allowed! localError(c.config, a.info, errXCannotBeAssignedTo % renderTree(a, {renderNoComments})) diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 797aff7f7..d591da855 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -10,7 +10,7 @@ import intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees, wordrecg, strutils, options, guards, lineinfos, semfold, semdata, - modulegraphs, varpartitions + modulegraphs, varpartitions, typeallowed when defined(useDfa): import dfa @@ -1244,7 +1244,7 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) = var mutationInfo = MutationInfo() if {strictFuncs, views} * c.features != {}: - var partitions = computeGraphPartitions(s, body) + var partitions = computeGraphPartitions(s, body, g.config) if not t.hasSideEffect and t.hasDangerousAssign: t.hasSideEffect = varpartitions.hasSideEffect(partitions, mutationInfo) if views in c.features: diff --git a/compiler/typeallowed.nim b/compiler/typeallowed.nim index 651a21ade..aabaa46dd 100644 --- a/compiler/typeallowed.nim +++ b/compiler/typeallowed.nim @@ -187,42 +187,77 @@ proc typeAllowed*(t: PType, kind: TSymKind; c: PContext; flags: TTypeAllowedFlag var marker = initIntSet() result = typeAllowedAux(marker, t, kind, c, flags) -proc isViewTypeAux(marker: var IntSet, t: PType): bool +type + ViewTypeKind* = enum + noView, immutableView, mutableView -proc isViewTypeNode(marker: var IntSet, n: PNode): bool = +proc combine(dest: var ViewTypeKind, b: ViewTypeKind) {.inline.} = + case dest + of noView, mutableView: + dest = b + of immutableView: + if b == mutableView: dest = b + +proc classifyViewTypeAux(marker: var IntSet, t: PType): ViewTypeKind + +proc classifyViewTypeNode(marker: var IntSet, n: PNode): ViewTypeKind = case n.kind of nkSym: - result = isViewTypeAux(marker, n.typ) + result = classifyViewTypeAux(marker, n.typ) of nkOfBranch: - result = isViewTypeNode(marker, n.lastSon) + result = classifyViewTypeNode(marker, n.lastSon) else: + result = noView for child in n: - result = isViewTypeNode(marker, child) - if result: break + result.combine classifyViewTypeNode(marker, child) + if result == mutableView: break -proc isViewTypeAux(marker: var IntSet, t: PType): bool = - if containsOrIncl(marker, t.id): return false +proc classifyViewTypeAux(marker: var IntSet, t: PType): ViewTypeKind = + if containsOrIncl(marker, t.id): return noView case t.kind - of tyVar, tyLent, tyVarargs, tyOpenArray: - result = true + of tyVar: + result = mutableView + of tyLent, tyVarargs, tyOpenArray: + result = immutableView of tyGenericInst, tyDistinct, tyAlias, tyInferred, tySink, tyOwned, - tyUncheckedArray, tySequence, tyArray, tyRef, tyStatic, tyFromExpr: - result = isViewTypeAux(marker, lastSon(t)) + tyUncheckedArray, tySequence, tyArray, tyRef, tyStatic: + result = classifyViewTypeAux(marker, lastSon(t)) + of tyFromExpr: + if t.len > 0: + result = classifyViewTypeAux(marker, lastSon(t)) + else: + result = noView of tyTuple: + result = noView for i in 0.. 1: + if (n.typ != nil and classifyViewType(n.typ) != noView) or getMagic(n) == mSlice: + n = n[1] + else: + break + else: + break + else: + break + # borrowFromConstExpr(n) is correct here because we need 'node' + # stripped off the path suffixes: + if result == nil and borrowFromConstExpr(n): + result = n + proc allRoots(n: PNode; result: var seq[PSym]; followDotExpr = true) = case n.kind of nkSym: @@ -385,13 +478,57 @@ proc pretendOwnsData(c: var Partitions, s: PSym) = const explainCursors = false +proc borrowFrom(c: var Partitions; dest: PSym; src: PNode) = + const + url = "; see https://nim-lang.github.io/Nim/manual_experimental.html#view-types-algorithm-path-expressions for details" + + let s = pathExpr(src, c.owner) + if s == nil: + localError(c.config, src.info, "cannot borrow from " & $src & ", it is not a path expression; " & url) + elif s.kind == nkSym: + if dest.kind == skResult: + if s.sym.kind != skParam or s.sym.position != 0: + localError(c.config, src.info, "'result' must borrow from the first parameter") + + let vid = variableId(c, dest) + if vid >= 0: + var sourceIdx = variableId(c, s.sym) + if sourceIdx < 0: + sourceIdx = c.s.len + c.s.add VarIndex(con: Connection(kind: isEmptyRoot), sym: s.sym, reassignedTo: 0, + aliveStart: MinTime, aliveEnd: MaxTime) + + c.s[vid].borrowsFrom.add sourceIdx + else: + discard "a valid borrow location that is a deeply constant expression so we have nothing to track" + + +proc borrowingCall(c: var Partitions; destType: PType; n: PNode; i: int) = + let v = pathExpr(n[i], c.owner) + if v.kind == nkSym: + for j in i+1..= 0: + for b in par.s[rid].borrowsFrom: + let sid = root(par, b) + if sid >= 0: + if par.s[sid].con.kind == isRootOf and dangerousMutation(par.graphs[par.s[sid].con.graphIndex], par.s[i]): + cannotBorrow(config, v, par.graphs[par.s[sid].con.graphIndex]) + if par.s[sid].sym.kind != skParam and par.s[sid].aliveEnd < par.s[rid].aliveEnd: + localError(config, v.info, "'" & v.name.s & "' borrows from location '" & par.s[sid].sym.name.s & + "' which does not live long enough") + + #if par.s[rid].con.kind == isRootOf and dangerousMutation(par.graphs[par.s[rid].con.graphIndex], par.s[i]): + # cannotBorrow(config, s, par.graphs[par.s[rid].con.graphIndex]) proc computeCursors*(s: PSym; n: PNode; config: ConfigRef) = - var par = computeGraphPartitions(s, n, true) + var par = computeGraphPartitions(s, n, config, true) for i in 0 ..< par.s.len: let v = addr(par.s[i]) if v.flags * {ownsData, preventCursor} == {} and v.sym.kind notin {skParam, skResult} and diff --git a/doc/manual.rst b/doc/manual.rst index 8c167bf62..11ea6b99c 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -655,7 +655,7 @@ Precedence level Operators First ================ ======================================================= ================== =============== -Whether an operator is used a prefix operator is also affected by preceding +Whether an operator is used as a prefix operator is also affected by preceding whitespace (this parsing change was introduced with version 0.13.0): .. code-block:: nim @@ -3275,16 +3275,16 @@ programming and are inherently unsafe. .. code-block:: nim cast[int](x) - + The target type of a cast must be a concrete type, for instance, a target type that is a type class (which is non-concrete) would be invalid: .. code-block:: nim type Foo = int or float var x = cast[Foo](1) # Error: cannot cast to a non concrete type: 'Foo' - + Type casts should not be confused with *type conversions,* as mentioned in the -prior section. Unlike type conversions, a type cast cannot change the underlying +prior section. Unlike type conversions, a type cast cannot change the underlying bit pattern of the data being casted (aside from that the size of the target type may differ from the source type). Casting resembles *type punning* in other languages or C++'s ``reinterpret_cast`` and ``bit_cast`` features. diff --git a/doc/manual_experimental.rst b/doc/manual_experimental.rst index 459139f94..abb699d1f 100644 --- a/doc/manual_experimental.rst +++ b/doc/manual_experimental.rst @@ -1814,18 +1814,49 @@ For example: # an object reachable from 'n' is potentially mutated -The algorithm behind this analysis is currently not documented. +The algorithm behind this analysis is described in +the `view types section <#view-types-algorithm>`_. View types ========== -A view type is a type that contains one of the following types: +**Note**: ``--experimental:views`` is more effective +with ``--experimental:strictFuncs``. + +A view type is a type that is or contains one of the following types: - ``var T`` (mutable view into ``T``) - ``lent T`` (immutable view into ``T``) - ``openArray[T]`` (pair of (pointer to array of ``T``, size)) +For example: + +.. code-block:: nim + + type + View1 = var int + View2 = openArray[byte] + View3 = lent string + View4 = Table[openArray[char], int] + + +Exceptions to this rule are types constructed via ``ptr`` or ``proc``. +For example, the following types are **not** view types: + +.. code-block:: nim + + type + NotView1 = proc (x: openArray[int]) + NotView2 = ptr openArray[char] + NotView3 = ptr array[4, var int] + + +A *mutable* view type is a type that is or contains a ``var T`` type. +An *immutable* view type is a view type that is not a mutable view type. + +A *view* is a symbol (a let, var, const, etc.) that has a view type. + Since version 1.4 Nim allows view types to be used as local variables. This feature needs to be enabled via ``{.experimental: "views".}``. @@ -1860,10 +1891,159 @@ For example: main(@[11, 22, 33]) +A local variable of a view type can borrow from a location +derived from a parameter, another local variable, a global ``const`` or ``let`` +symbol or a thread-local ``var`` or ``let``. -If a view type is used as a return type, the location must borrow from the -first parameter that is passed to the proc. +Let ``p`` the proc that is analysed for the correctness of the borrow operation. + +Let ``source`` be one of: + +- A formal parameter of ``p``. Note that this does not cover parameters of + inner procs. +- The ``result`` symbol of ``p``. +- A local ``var`` or ``let`` or ``const`` of ``p``. Note that this does + not cover locals of inner procs. +- A thread-local ``var`` or ``let``. +- A global ``let`` or ``const``. +- A constant arraq/seq/object/tuple constructor. + + +Path expressions +---------------- + +A location derived from ``source`` is then defined as a path expression that +has ``source`` as the owner. A path expression ``e`` is defined recursively: + +- ``source`` itself is a path expression. +- Container access like ``e[i]`` is a path expression. +- Tuple access ``e[0]`` is a path expression. +- Object field access ``e.field`` is a path expression. +- ``system.toOpenArray(e, ...)`` is a path expression. +- Pointer dereference ``e[]`` is a path expression. +- An address ``addr e``, ``unsafeAddr e`` is a path expression. +- A type conversion ``T(e)`` is a path expression. +- A cast expression ``cast[T](e)`` is a path expression. +- ``f(e, ...)`` is a path expression if ``f``'s return type is a view type. + Because the view can only have been borrowed from ``e``, we then know + that owner of ``f(e, ...)`` is ``e``. + + +If a view type is used as a return type, the location must borrow from a location +that is derived from the first parameter that is passed to the proc. See https://nim-lang.org/docs/manual.html#procedures-var-return-type for details about how this is done for ``var T``. -The algorithm behind this analysis is currently not documented. +A mutable view can borrow from a mutable location, an immutable view can borrow +from both a mutable or an immutable location. + +The *duration* of a borrow is the span of commands beginning from the assignment +to the view and ending with the last usage of the view. + +For the duration of the borrow operation, no mutations to the borrowed locations +may be performed except via the potentially mutable view that borrowed from the +location. The borrowed location is said to be *sealed* during the borrow. + +.. code-block:: nim + + {.experimental: "views".} + + type + Obj = object + field: string + + proc dangerous(s: var seq[Obj]) = + let v: lent Obj = s[0] # seal 's' + s.setLen 0 # prevented at compile-time because 's' is sealed. + echo v.field + + +The scope of the view does not matter: + +.. code-block:: nim + + proc valid(s: var seq[Obj]) = + let v: lent Obj = s[0] # begin of borrow + echo v.field # end of borrow + s.setLen 0 # valid because 'v' isn't used afterwards + + +The analysis requires as much precision about mutations as is reasonably obtainable, +so it is more effective with the experimental `strict funcs <#strict-funcs>`_ +feature. In other words ``--experimental:views`` works better +with ``--experimental:strictFuncs``. + +The analysis is currently control flow insensitive: + +.. code-block:: nim + + proc invalid(s: var seq[Obj]) = + let v: lent Obj = s[0] + if false: + s.setLen 0 + echo v.field + +In this example, the compiler assumes that ``s.setLen 0`` invalidates the +borrow operation of ``v`` even though a human being can easily see that it +will never do that at runtime. + + +Reborrows +--------- + +A view ``v`` can borrow from multiple different locations. However, the borrow +is always the full span of ``v``'s lifetime and every location that is borrowed +from is sealed during ``v``'s lifetime. + + +Algorithm +--------- + +The following section is an outline of the algorithm that the current implementation +uses. The algorithm performs two traversals over the AST of the procedure or global +section of code that uses a view variable. No fixpoint iterations are performed, the +complexity of the analysis is O(N) where N is the number of nodes of the AST. + +The first pass over the AST computes the lifetime of each local variable based on +a notion of an "abstract time", in the implementation it's a simple integer that is +incremented for every visited node. + +In the second pass information about the underlying object "graphs" is computed. +Let ``v`` be a parameter or a local variable. Let ``G(v)`` be the graph +that ``v`` belongs to. A graph is defined by the set of variables that belong +to the graph. Initially for all ``v``: ``G(v) = {v}``. Every variable can only +be part of a single graph. + +Assignments like ``a = b`` "connect" two variables, both variables end up in the +same graph ``{a, b} = G(a) = G(b)``. Unfortunately, the pattern to look for is +much more complex than that and can involve multiple assignment targets +and sources:: + + f(x, y) = g(a, b) + +connects ``x`` and ``y`` to ``a`` and ``b``: ``G(x) = G(y) = G(a) = G(b) = {x, y, a, b}``. +A type based alias analysis rules out some of these combinations, for example +a ``string`` value cannot possibly be connected to a ``seq[int]``. + +A pattern like ``v[] = value`` or ``v.field = value`` marks ``G(v)`` as mutated. +After the second pass a set of disjoint graphs was computed. + +For strict functions it is then enforced that there is no graph that is both mutated +and has an element that is an immutable parameter (that is a parameter that is not +of type ``var T``). + +For borrow checking a different set of checks is performed. Let ``v`` be the view +and ``b`` the location that is borrowed from. + +- The lifetime of ``v`` must not exceed ``b``'s lifetime. Note: The lifetime of + a parameter is the complete proc body. +- If ``v`` is a mutable view and ``v`` is used to actually mutate the + borrowed location, then ``b`` has to be a mutable location. + Note: If it is not actually used for mutation, borrowing a mutable view from an + immutable location is allowed! This allows for many important idioms and will be + justified in an upcoming RFC. +- During ``v``'s lifetime, ``G(b)`` can only be modified by ``v`` (and only if + ``v`` is a mutable view). +- If ``v`` is ``result`` then ``b`` has to be a location derived from the first + formal parameter or from a constant location. +- A view cannot be used for a read or a write access before it was assigned to. diff --git a/tests/views/tcannot_borrow.nim b/tests/views/tcannot_borrow.nim index 699176b04..d1c194b25 100644 --- a/tests/views/tcannot_borrow.nim +++ b/tests/views/tcannot_borrow.nim @@ -1,17 +1,22 @@ discard """ errormsg: "cannot borrow" - nimout: '''tcannot_borrow.nim(16, 7) Error: cannot borrow meh; what it borrows from is potentially mutated -tcannot_borrow.nim(17, 3) the mutation is here -tcannot_borrow.nim(16, 7) is the statement that connected the mutation to the parameter''' - line: 16 + nimout: '''tcannot_borrow.nim(21, 7) Error: cannot borrow meh; what it borrows from is potentially mutated +tcannot_borrow.nim(22, 3) the mutation is here''' + line: 21 """ + {.experimental: "views".} type Foo = object field: string +proc valid(s: var seq[Foo]) = + let v: lent Foo = s[0] # begin of borrow + echo v.field # end of borrow + s.setLen 0 # valid because 'v' isn't used afterwards + proc dangerous(s: var seq[Foo]) = let meh: lent Foo = s[0] s.setLen 0 diff --git a/tests/views/tmust_borrow_from_first_parameter.nim b/tests/views/tmust_borrow_from_first_parameter.nim new file mode 100644 index 000000000..b2decfb38 --- /dev/null +++ b/tests/views/tmust_borrow_from_first_parameter.nim @@ -0,0 +1,9 @@ +discard """ + errormsg: "'result' must borrow from the first parameter" + line: 9 +""" + +{.experimental: "views".} + +proc p(a, b: openArray[char]): openArray[char] = + result = b diff --git a/tests/views/tsplit_into_seq.nim b/tests/views/tsplit_into_seq.nim index 409451601..49968f4c2 100644 --- a/tests/views/tsplit_into_seq.nim +++ b/tests/views/tsplit_into_seq.nim @@ -23,9 +23,8 @@ proc split*(s: string, seps: set[char] = Whitespace, while last < len(s) and s[last] notin seps: inc(last) if splits == 0: last = len(s) - {.noSideEffect.}: - result.add toOpenArray(s, first, last-1) - result.add toOpenArray(s, first, last-1) + result.add toOpenArray(s, first, last-1) + result.add toOpenArray(s, first, last-1) if splits == 0: break dec(splits) inc(last) diff --git a/tests/views/tviews1.nim b/tests/views/tviews1.nim index 3ce0bb6d8..51f17b9d6 100644 --- a/tests/views/tviews1.nim +++ b/tests/views/tviews1.nim @@ -4,6 +4,7 @@ discard """ 33 3 2 +3 3''' targets: "c cpp" """ @@ -22,6 +23,6 @@ proc main(s: seq[int]) = take(x.toOpenArray(0, 1)) let y = x take y - + take x main(@[11, 22, 33]) From eb2a4961c71029c874ebdacd1b23a349eb8e61d5 Mon Sep 17 00:00:00 2001 From: Miran Date: Tue, 29 Sep 2020 23:43:12 +0200 Subject: [PATCH 0676/3768] various documentation fixes [backport] (#15422) --- lib/impure/nre.nim | 8 ++++---- lib/pure/strutils.nim | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/impure/nre.nim b/lib/impure/nre.nim index 12ea7ab56..4696f7322 100644 --- a/lib/impure/nre.nim +++ b/lib/impure/nre.nim @@ -539,7 +539,7 @@ proc matchImpl(str: string, pattern: Regex, start, endpos: int, flags: int): Opt raise RegexInternalError(msg: "Unknown internal error: " & $execRet) proc match*(str: string, pattern: Regex, start = 0, endpos = int.high): Option[RegexMatch] = - ## Like ` ``find(...)`` <#proc-find>`_, but anchored to the start of the + ## Like `find(...)<#find,string,Regex,int>`_, but anchored to the start of the ## string. ## runnableExamples: @@ -549,11 +549,11 @@ proc match*(str: string, pattern: Regex, start = 0, endpos = int.high): Option[R return str.matchImpl(pattern, start, endpos, pcre.ANCHORED) iterator findIter*(str: string, pattern: Regex, start = 0, endpos = int.high): RegexMatch = - ## Works the same as ` ``find(...)`` <#proc-find>`_, but finds every + ## Works the same as `find(...)<#find,string,Regex,int>`_, but finds every ## non-overlapping match. ``"2222".find(re"22")`` is ``"22", "22"``, not ## ``"22", "22", "22"``. ## - ## Arguments are the same as ` ``find(...)`` <#proc-find>`_ + ## Arguments are the same as `find(...)<#find,string,Regex,int>`_ ## ## Variants: ## @@ -632,7 +632,7 @@ proc split*(str: string, pattern: Regex, maxSplit = -1, start = 0): seq[string] ## Splits the string with the given regex. This works according to the ## rules that Perl and Javascript use. ## - ## ``start`` behaves the same as in ` ``find(...)`` <#proc-find>`_. + ## ``start`` behaves the same as in `find(...)<#find,string,Regex,int>`_. ## runnableExamples: # - If the match is zero-width, then the string is still split: diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index d97ed9a07..20ad2f517 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -1930,7 +1930,7 @@ proc initSkipTable*(a: var SkipTable, sub: string) proc find*(a: SkipTable, s, sub: string, start: Natural = 0, last = 0): int {.noSideEffect, rtl, extern: "nsuFindStrA".} = - ## Searches for `sub` in `s` inside range `start`..`last` using preprocessed + ## Searches for `sub` in `s` inside range `start..last` using preprocessed ## table `a`. If `last` is unspecified, it defaults to `s.high` (the last ## element). ## From 9c86f4867e8475e38e1e8fb026b9cfd1555271a8 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Wed, 30 Sep 2020 16:52:49 +0800 Subject: [PATCH 0677/3768] fix doc search(escape HTML code) (#15433) * use release version * fix doc search --- tools/dochack/dochack.nim | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tools/dochack/dochack.nim b/tools/dochack/dochack.nim index 4c4db4638..471212a50 100644 --- a/tools/dochack/dochack.nim +++ b/tools/dochack/dochack.nim @@ -267,6 +267,19 @@ var template normalize(x: cstring): cstring = x.toLower.replace("_", "") +proc escapeCString(x: var cstring) = + var s = "" + for c in x: + case c + of '&': s.add("&") + of '<': s.add("<") + of '>': s.add(">") + of '"': s.add(""") + of '\'': s.add("'") + of '/': s.add("/") + else: s.add(c) + x = s.cstring + proc dosearch(value: cstring): Element = if db.len == 0: var stuff: Element @@ -305,6 +318,7 @@ proc dosearch(value: cstring): Element = matches.sort(proc(a, b: auto): int = b[1] - a[1]) for i in 0 ..< min(matches.len, 29): matches[i][0].innerHTML = matches[i][0].getAttribute("data-doc-search-tag") + escapeCString(matches[i][0].innerHTML) ul.add(tree("LI", cast[Element](matches[i][0]))) if ul.len == 0: result.add tree("B", text"no search results") From 86d7b63e2ae7177061ff13d7b4cf32ba512f22f6 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 30 Sep 2020 20:44:18 +0200 Subject: [PATCH 0678/3768] better support for view types (#15436) * you can put borrows into tables * enforces mutating views only mutate mutable data --- compiler/ccgcalls.nim | 6 ++-- compiler/typeallowed.nim | 2 +- compiler/varpartitions.nim | 54 ++++++++++++++++++++++++++----- tests/views/tdont_mutate.nim | 48 +++++++++++++++++++++++++++ tests/views/tsplit_into_table.nim | 40 +++++++++++++++++++++++ 5 files changed, 139 insertions(+), 11 deletions(-) create mode 100644 tests/views/tdont_mutate.nim create mode 100644 tests/views/tsplit_into_table.nim diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 6f0d327d9..6c5b721c7 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -142,8 +142,10 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc, proc genBoundsCheck(p: BProc; arr, a, b: TLoc) proc reifiedOpenArray(n: PNode): bool {.inline.} = - let x = trees.getRoot(n) - if x != nil and x.kind == skParam: + var x = n + while x.kind in {nkAddr, nkHiddenAddr, nkHiddenStdConv, nkHiddenDeref}: + x = x[0] + if x.kind == nkSym and x.sym.kind == skParam: result = false else: result = true diff --git a/compiler/typeallowed.nim b/compiler/typeallowed.nim index aabaa46dd..13eff35be 100644 --- a/compiler/typeallowed.nim +++ b/compiler/typeallowed.nim @@ -121,7 +121,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, {tyChar, tyEnum, tyInt..tyFloat128, tyInt..tyUInt64}: result = t of tyOpenArray: # you cannot nest openArrays/sinks/etc. - if (kind != skParam and views notin c.features) or taIsOpenArray in flags: + if (kind != skParam or taIsOpenArray in flags) and views notin c.features: result = t else: result = typeAllowedAux(marker, t[0], kind, c, flags+{taIsOpenArray}) diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim index 2047a90e1..1fb4bd93e 100644 --- a/compiler/varpartitions.nim +++ b/compiler/varpartitions.nim @@ -54,7 +54,9 @@ type VarFlag = enum ownsData, preventCursor, - isReassigned + isReassigned, + viewDoesMutate, + viewBorrowsFromConst VarIndexKind = enum isEmptyRoot, @@ -478,9 +480,12 @@ proc pretendOwnsData(c: var Partitions, s: PSym) = const explainCursors = false +proc isConstSym(s: PSym): bool = + result = s.kind in {skConst, skLet} or isConstParam(s) + proc borrowFrom(c: var Partitions; dest: PSym; src: PNode) = const - url = "; see https://nim-lang.github.io/Nim/manual_experimental.html#view-types-algorithm-path-expressions for details" + url = "see https://nim-lang.github.io/Nim/manual_experimental.html#view-types-algorithm-path-expressions for details" let s = pathExpr(src, c.owner) if s == nil: @@ -499,28 +504,50 @@ proc borrowFrom(c: var Partitions; dest: PSym; src: PNode) = aliveStart: MinTime, aliveEnd: MaxTime) c.s[vid].borrowsFrom.add sourceIdx + if isConstSym(s.sym): + c.s[vid].flags.incl viewBorrowsFromConst else: - discard "a valid borrow location that is a deeply constant expression so we have nothing to track" + let vid = variableId(c, dest) + if vid >= 0: + c.s[vid].flags.incl viewBorrowsFromConst + #discard "a valid borrow location that is a deeply constant expression so we have nothing to track" proc borrowingCall(c: var Partitions; destType: PType; n: PNode; i: int) = let v = pathExpr(n[i], c.owner) - if v.kind == nkSym: + if v != nil and v.kind == nkSym: + when false: + let isView = directViewType(destType) == immutableView + if n[0].kind == nkSym and n[0].sym.name.s == "[]=": + localError(c.config, n[i].info, "attempt to mutate an immutable view") + for j in i+1..= 0: + c.s[vid].flags.incl viewDoesMutate + of immutableView: + localError(c.config, dest.info, "attempt to mutate a borrowed location from an immutable view") + of noView: discard "nothing to do" proc deps(c: var Partitions; dest, src: PNode) = if not c.performCursorInference: - trackBorrow(c, dest, src) + borrowingAsgn(c, dest, src) var targets, sources: seq[PSym] allRoots(dest, targets) @@ -801,6 +828,7 @@ proc checkBorrowedLocations*(par: var Partitions; body: PNode; config: ConfigRef if v.kind != skParam and classifyViewType(v.typ) != noView: let rid = root(par, i) if rid >= 0: + var constViolation = false for b in par.s[rid].borrowsFrom: let sid = root(par, b) if sid >= 0: @@ -809,6 +837,16 @@ proc checkBorrowedLocations*(par: var Partitions; body: PNode; config: ConfigRef if par.s[sid].sym.kind != skParam and par.s[sid].aliveEnd < par.s[rid].aliveEnd: localError(config, v.info, "'" & v.name.s & "' borrows from location '" & par.s[sid].sym.name.s & "' which does not live long enough") + if viewDoesMutate in par.s[rid].flags and isConstSym(par.s[sid].sym): + localError(config, v.info, "'" & v.name.s & "' borrows from the immutable location '" & + par.s[sid].sym.name.s & "' and attempts to mutate it") + constViolation = true + if {viewDoesMutate, viewBorrowsFromConst} * par.s[rid].flags == {viewDoesMutate, viewBorrowsFromConst} and + not constViolation: + # we do not track the constant expressions we allow to borrow from so + # we can only produce a more generic error message: + localError(config, v.info, "'" & v.name.s & + "' borrows from an immutable location and attempts to mutate it") #if par.s[rid].con.kind == isRootOf and dangerousMutation(par.graphs[par.s[rid].con.graphIndex], par.s[i]): # cannotBorrow(config, s, par.graphs[par.s[rid].con.graphIndex]) diff --git a/tests/views/tdont_mutate.nim b/tests/views/tdont_mutate.nim new file mode 100644 index 000000000..907aff018 --- /dev/null +++ b/tests/views/tdont_mutate.nim @@ -0,0 +1,48 @@ +discard """ + cmd: "nim check --hints:off $file" +""" + +import tables + +{.experimental: "views".} + +const + Whitespace = {' ', '\t', '\n', '\r'} + +proc split*(s: string, seps: set[char] = Whitespace, + maxsplit: int = -1): Table[int, openArray[char]] = + var last = 0 + var splits = maxsplit + result = initTable[int, openArray[char]]() + + while last <= len(s): + var first = last + while last < len(s) and s[last] notin seps: + inc(last) + if splits == 0: last = len(s) + result[first] = toOpenArray(s, first, last-1) + + result[first][0] = 'c' #[tt.Error + attempt to mutate a borrowed location from an immutable view + ]# + + if splits == 0: break + dec(splits) + inc(last) + +proc `$`(x: openArray[char]): string = + result = newString(x.len) + for i in 0.. Date: Thu, 1 Oct 2020 04:22:22 -0300 Subject: [PATCH 0679/3768] Add 1 overload to apply (#15439) --- lib/pure/collections/sequtils.nim | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index d8ea3ad1f..803f89cc5 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -415,6 +415,11 @@ proc apply*[T](s: var openArray[T], op: proc (x: T): T {.closure.}) for i in 0 ..< s.len: s[i] = op(s[i]) +proc apply*[T](s: openArray[T], op: proc (x: T) {.closure.}) {.inline, since: (1, 3).} = + ## Same as `apply` but for proc that do not return and do not mutate `s` directly. + runnableExamples: apply([0, 1, 2, 3, 4], proc(item: int) = echo item) + for i in 0 ..< s.len: op(s[i]) + iterator filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): T = ## Iterates through a container `s` and yields every item that fulfills the ## predicate `pred` (function that returns a `bool`). From b703f02ad2a95975513f07d1a48c368b80825ddb Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 1 Oct 2020 09:46:50 +0200 Subject: [PATCH 0680/3768] added missing .noalias support for object fields (#15445) --- compiler/ccgtypes.nim | 8 +++++--- tests/ccgbugs/tnoalias.nim | 7 ++++++- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index ff7b17590..c1a214532 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -564,19 +564,21 @@ proc genRecordFieldsAux(m: BModule, n: PNode, # have to recurse via 'getTypeDescAux'. And not doing so prevents problems # with heavily templatized C++ code: if not isImportedCppType(rectype): + let noAlias = if sfNoalias in field.flags: ~" NIM_NOALIAS" else: nil + let fieldType = field.loc.lode.typ.skipTypes(abstractInst) if fieldType.kind == tyUncheckedArray: result.addf("$1 $2[SEQ_DECL_SIZE];$n", [getTypeDescAux(m, fieldType.elemType, check, skField), sname]) elif fieldType.kind == tySequence: # we need to use a weak dependency here for trecursive_table. - result.addf("$1 $2;$n", [getTypeDescWeak(m, field.loc.t, check, skField), sname]) + result.addf("$1$3 $2;$n", [getTypeDescWeak(m, field.loc.t, check, skField), sname, noAlias]) elif field.bitsize != 0: - result.addf("$1 $2:$3;$n", [getTypeDescAux(m, field.loc.t, check, skField), sname, rope($field.bitsize)]) + result.addf("$1$4 $2:$3;$n", [getTypeDescAux(m, field.loc.t, check, skField), sname, rope($field.bitsize), noAlias]) else: # don't use fieldType here because we need the # tyGenericInst for C++ template support - result.addf("$1 $2;$n", [getTypeDescAux(m, field.loc.t, check, skField), sname]) + result.addf("$1$3 $2;$n", [getTypeDescAux(m, field.loc.t, check, skField), sname, noAlias]) else: internalError(m.config, n.info, "genRecordFieldsAux()") proc getRecordFields(m: BModule, typ: PType, check: var IntSet): Rope = diff --git a/tests/ccgbugs/tnoalias.nim b/tests/ccgbugs/tnoalias.nim index 5ddfac5bb..0c6e84e44 100644 --- a/tests/ccgbugs/tnoalias.nim +++ b/tests/ccgbugs/tnoalias.nim @@ -1,8 +1,13 @@ discard """ - ccodecheck: "\\i@'NIM_CHAR* NIM_NOALIAS x,' @'void* NIM_NOALIAS q'" + ccodecheck: "\\i@'NI* NIM_NOALIAS field;' @'NIM_CHAR* NIM_NOALIAS x,' @'void* NIM_NOALIAS q'" """ +type + BigNum = object + field {.noalias.}: ptr UncheckedArray[int] + proc p(x {.noalias.}: openArray[char]) = var q {.noalias.}: pointer = unsafeAddr(x[0]) +var bn: BigNum p "abc" From 2b91845f1db165541be5d2ebad7fe1f956e90fb1 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 1 Oct 2020 11:16:04 +0200 Subject: [PATCH 0681/3768] refactoring, fixes yet another strictFuncs regression (#15446) --- compiler/sempass2.nim | 5 ++++- compiler/varpartitions.nim | 20 +++++++++++++------- tests/effects/tstrict_funcs.nim | 17 +++++++++++++++++ 3 files changed, 34 insertions(+), 8 deletions(-) create mode 100644 tests/effects/tstrict_funcs.nim diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index d591da855..cda156224 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -1244,7 +1244,10 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) = var mutationInfo = MutationInfo() if {strictFuncs, views} * c.features != {}: - var partitions = computeGraphPartitions(s, body, g.config) + var goals: set[Goal] = {} + if strictFuncs in c.features: goals.incl constParameters + if views in c.features: goals.incl borrowChecking + var partitions = computeGraphPartitions(s, body, g.config, goals) if not t.hasSideEffect and t.hasDangerousAssign: t.hasSideEffect = varpartitions.hasSideEffect(partitions, mutationInfo) if views in c.features: diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim index 1fb4bd93e..b416aa41d 100644 --- a/compiler/varpartitions.nim +++ b/compiler/varpartitions.nim @@ -84,11 +84,17 @@ type maxMutation, minConnection: AbstractTime mutations: seq[AbstractTime] + Goal* = enum + constParameters, + borrowChecking, + cursorInference + Partitions* = object abstractTime: AbstractTime s: seq[VarIndex] graphs: seq[MutationInfo] - unanalysableMutation, performCursorInference: bool + goals: set[Goal] + unanalysableMutation: bool inAsgnSource, inConstructor, inNoSideEffectSection: int owner: PSym config: ConfigRef @@ -546,7 +552,7 @@ proc borrowingAsgn(c: var Partitions; dest, src: PNode) = of noView: discard "nothing to do" proc deps(c: var Partitions; dest, src: PNode) = - if not c.performCursorInference: + if borrowChecking in c.goals: borrowingAsgn(c, dest, src) var targets, sources: seq[PSym] @@ -565,7 +571,7 @@ proc deps(c: var Partitions; dest, src: PNode) = for s in sources: connect(c, t, s, dest.info) - if c.performCursorInference and src.kind != nkEmpty: + if cursorInference in c.goals and src.kind != nkEmpty: if dest.kind == nkSym: let vid = variableId(c, dest.sym) if vid >= 0: @@ -647,7 +653,7 @@ proc traverse(c: var Partitions; n: PNode) = for r in roots: potentialMutation(c, r, it.info) for r in roots: noCursor(c, r) - if not c.performCursorInference: + if borrowChecking in c.goals: # a call like 'result.add toOpenArray()' can also be a borrow # operation. We know 'paramType' is a tyVar and we really care if # 'paramType[0]' is still a view type, this is not a typo! @@ -785,8 +791,8 @@ proc computeLiveRanges(c: var Partitions; n: PNode) = else: for child in n: computeLiveRanges(c, child) -proc computeGraphPartitions*(s: PSym; n: PNode; config: ConfigRef; cursorInference = false): Partitions = - result = Partitions(performCursorInference: cursorInference, owner: s, config: config) +proc computeGraphPartitions*(s: PSym; n: PNode; config: ConfigRef; goals: set[Goal]): Partitions = + result = Partitions(owner: s, config: config, goals: goals) if s.kind notin {skModule, skMacro}: let params = s.typ.n for i in 1.. Date: Thu, 1 Oct 2020 06:21:27 -0300 Subject: [PATCH 0682/3768] Clean out jssys (#15442) --- lib/system/jssys.nim | 6 ------ 1 file changed, 6 deletions(-) diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim index 705a6a208..df05a9a62 100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -772,12 +772,6 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat, start = 0): int { number = number * sign result = i - start -when defined(nodejs): - # Deprecated. Use `alert` defined in dom.nim - proc alert*(s: cstring) {.importc: "console.log", nodecl, deprecated.} -else: - # Deprecated. Use `alert` defined in dom.nim - proc alert*(s: cstring) {.importc, nodecl, deprecated.} # Workaround for IE, IE up to version 11 lacks 'Math.trunc'. We produce # 'Math.trunc' for Nim's ``div`` and ``mod`` operators: From 79a92da8efdb895ba5762ca9ed0471a3a5846068 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Thu, 1 Oct 2020 07:39:50 -0300 Subject: [PATCH 0683/3768] Clean out (#15440) --- changelog.md | 2 +- lib/pure/sugar.nim | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/changelog.md b/changelog.md index 474f1eae1..d610025d7 100644 --- a/changelog.md +++ b/changelog.md @@ -156,7 +156,7 @@ 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`. - +- Removed `sugar.distinctBase`, deprecated since `0.19`. - Export `asyncdispatch.PDispatcher.handles` so that an external library can register them. - Added `deques.toDeque`, which creates a deque from an openArray. The usage is diff --git a/lib/pure/sugar.nim b/lib/pure/sugar.nim index 8bfc7b7ff..49934b9bb 100644 --- a/lib/pure/sugar.nim +++ b/lib/pure/sugar.nim @@ -186,10 +186,6 @@ proc freshIdentNodes(ast: NimNode): NimNode = result.add inspect(child) result = inspect(ast) -template distinctBase*(T: typedesc): typedesc {.deprecated: "use distinctBase from typetraits instead".} = - ## reverses ``type T = distinct A``; works recursively. - typetraits.distinctBase(T) - macro capture*(locals: varargs[typed], body: untyped): untyped {.since: (1, 1).} = ## Useful when creating a closure in a loop to capture some local loop variables ## by their current iteration values. Example: From ab405c936e77a875199aa1ee0f99eddd3d5a0c10 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 1 Oct 2020 15:04:36 +0200 Subject: [PATCH 0684/3768] views: yet another bugfix (#15447) * views: yet another bugfix * views: extended the spec * views: take into account potential hidden mutations via proc calls --- compiler/semtempl.nim | 5 ----- compiler/trees.nim | 5 +++++ compiler/varpartitions.nim | 26 ++++++++++++++++++++++---- doc/manual_experimental.rst | 16 ++++++++++++++++ tests/effects/tstrict_funcs.nim | 2 +- 5 files changed, 44 insertions(+), 10 deletions(-) diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index 1ec8c07b0..df832c814 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -707,11 +707,6 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode = localError(c.c.config, n.info, "invalid expression") result = n - proc stupidStmtListExpr(n: PNode): bool = - for i in 0.. 0 and stupidStmtListExpr(n): + n = n.lastSon + else: + break of nkCallKinds: if n.len > 1: if (n.typ != nil and classifyViewType(n.typ) != noView) or getMagic(n) == mSlice: @@ -551,6 +556,10 @@ proc borrowingAsgn(c: var Partitions; dest, src: PNode) = localError(c.config, dest.info, "attempt to mutate a borrowed location from an immutable view") of noView: discard "nothing to do" +proc containsPointer(t: PType): bool = + proc wrap(t: PType): bool {.nimcall.} = t.kind in {tyRef, tyPtr} + result = types.searchTypeFor(t, wrap) + proc deps(c: var Partitions; dest, src: PNode) = if borrowChecking in c.goals: borrowingAsgn(c, dest, src) @@ -559,9 +568,7 @@ proc deps(c: var Partitions; dest, src: PNode) = allRoots(dest, targets) allRoots(src, sources) - proc wrap(t: PType): bool {.nimcall.} = t.kind in {tyRef, tyPtr} - - let destIsComplex = types.searchTypeFor(dest.typ, wrap) + let destIsComplex = containsPointer(dest.typ) for t in targets: if dest.kind != nkSym and c.inNoSideEffectSection == 0: @@ -607,6 +614,14 @@ const nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt, nkExportStmt, nkPragma, nkCommentStmt, nkBreakState, nkTypeOfExpr} +proc potentialMutationViaArg(c: var Partitions; n: PNode; callee: PType) = + if constParameters in c.goals and tfNoSideEffect in callee.flags: + discard "we know there are no hidden mutations through an immutable parameter" + elif c.inNoSideEffectSection == 0 and containsPointer(n.typ): + var roots: seq[PSym] + allRoots(n, roots) + for r in roots: potentialMutation(c, r, n.info) + proc traverse(c: var Partitions; n: PNode) = inc c.abstractTime case n.kind @@ -640,6 +655,7 @@ proc traverse(c: var Partitions; n: PNode) = let parameters = n[0].typ let L = if parameters != nil: parameters.len else: 0 + let m = getMagic(n) for i in 1.. Date: Thu, 1 Oct 2020 10:07:57 -0300 Subject: [PATCH 0685/3768] Clean out (#15448) --- compiler/options.nim | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/compiler/options.nim b/compiler/options.nim index 226f13c73..efb0aa2da 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -628,17 +628,6 @@ proc setDefaultLibpath*(conf: ConfigRef) = proc canonicalizePath*(conf: ConfigRef; path: AbsoluteFile): AbsoluteFile = result = AbsoluteFile path.string.expandFilename -proc shortenDir*(conf: ConfigRef; dir: string): string {. - deprecated: "use 'relativeTo' instead".} = - ## returns the interesting part of a dir - var prefix = conf.projectPath.string & DirSep - if startsWith(dir, prefix): - return substr(dir, prefix.len) - prefix = getPrefixDir(conf).string & DirSep - if startsWith(dir, prefix): - return substr(dir, prefix.len) - result = dir - proc removeTrailingDirSep*(path: string): string = if (path.len > 0) and (path[^1] == DirSep): result = substr(path, 0, path.len - 2) From 5e160bff1b07e813fd3ed75f2bc48c4331cbfa39 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Thu, 1 Oct 2020 10:11:10 -0300 Subject: [PATCH 0686/3768] Add critbits.toCritBitTree (#15444) * Add critbits.toCritBitTree * https://github.com/nim-lang/Nim/pull/15444#discussion_r498035342 --- changelog.md | 3 +++ lib/pure/collections/critbits.nim | 28 ++++++++++++++++++++-------- 2 files changed, 23 insertions(+), 8 deletions(-) diff --git a/changelog.md b/changelog.md index d610025d7..5061a90b6 100644 --- a/changelog.md +++ b/changelog.md @@ -288,6 +288,9 @@ proc mydiv(a, b): int {.raises: [].} = - `items` no longer compiles with enum with holes as its behavior was error prone, see #14004 +- Added `critbits.toCritBitTree`, similar to `tables.toTable`, creates a new `CritBitTree` with given arguments. + + ## Compiler changes - Specific warnings can now be turned into errors via `--warningAsError[X]:on|off`. diff --git a/lib/pure/collections/critbits.nim b/lib/pure/collections/critbits.nim index d25e31d66..4611c2540 100644 --- a/lib/pure/collections/critbits.nim +++ b/lib/pure/collections/critbits.nim @@ -33,7 +33,7 @@ type root: Node[T] count: int -proc len*[T](c: CritBitTree[T]): int = +func len*[T](c: CritBitTree[T]): int {.inline.} = ## Returns the number of elements in `c` in O(1). runnableExamples: var c: CritBitTree[void] @@ -53,7 +53,7 @@ proc rawGet[T](c: CritBitTree[T], key: string): Node[T] = else: return if it.key == key: it else: nil -proc contains*[T](c: CritBitTree[T], key: string): bool {.inline.} = +func contains*[T](c: CritBitTree[T], key: string): bool {.inline.} = ## Returns true if `c` contains the given `key`. runnableExamples: var c: CritBitTree[void] @@ -62,7 +62,7 @@ proc contains*[T](c: CritBitTree[T], key: string): bool {.inline.} = result = rawGet(c, key) != nil -proc hasKey*[T](c: CritBitTree[T], key: string): bool {.inline.} = +func hasKey*[T](c: CritBitTree[T], key: string): bool {.inline.} = ## Alias for `contains <#contains,CritBitTree[T],string>`_. result = rawGet(c, key) != nil @@ -116,7 +116,7 @@ proc rawInsert[T](c: var CritBitTree[T], key: string): Node[T] = wherep[] = inner inc c.count -proc exclImpl[T](c: var CritBitTree[T], key: string): int = +func exclImpl[T](c: var CritBitTree[T], key: string): int = var p = c.root var wherep = addr(c.root) var whereq: ptr Node[T] = nil @@ -285,7 +285,7 @@ template get[T](c: CritBitTree[T], key: string): T = n.val -proc `[]`*[T](c: CritBitTree[T], key: string): T {.inline.} = +func `[]`*[T](c: CritBitTree[T], key: string): T {.inline.} = ## Retrieves the value at ``c[key]``. If `key` is not in `t`, the ## ``KeyError`` exception is raised. One can check with ``hasKey`` whether ## the key exists. @@ -295,7 +295,7 @@ proc `[]`*[T](c: CritBitTree[T], key: string): T {.inline.} = ## * `[]= proc <#[]=,CritBitTree[T],string,T>`_ get(c, key) -proc `[]`*[T](c: var CritBitTree[T], key: string): var T {.inline.} = +func `[]`*[T](c: var CritBitTree[T], key: string): var T {.inline.} = ## Retrieves the value at ``c[key]``. The value can be modified. ## If `key` is not in `t`, the ``KeyError`` exception is raised. ## @@ -485,7 +485,7 @@ iterator mpairsWithPrefix*[T](c: var CritBitTree[T], let top = allprefixedAux(c, prefix, longestMatch) for x in leaves(top): yield (x.key, x.val) -proc `$`*[T](c: CritBitTree[T]): string = +func `$`*[T](c: CritBitTree[T]): string = ## Turns `c` into a string representation. Example outputs: ## ``{keyA: value, keyB: value}``, ``{:}`` ## If `T` is void the outputs look like: @@ -515,7 +515,7 @@ proc `$`*[T](c: CritBitTree[T]): string = result.addQuoted(val) result.add("}") -proc commonPrefixLen*[T](c: CritBitTree[T]): int {.inline, since((1, 3)).} = +func commonPrefixLen*[T](c: CritBitTree[T]): int {.inline, since((1, 3)).} = ## Returns longest common prefix length of all keys of `c`. ## If `c` is empty, returns 0. runnableExamples: @@ -531,6 +531,18 @@ proc commonPrefixLen*[T](c: CritBitTree[T]): int {.inline, since((1, 3)).} = else: c.root.byte else: 0 +func toCritBitTree*[A, B](pairs: openArray[(A, B)]): CritBitTree[A] {.since: (1, 3).} = + ## Creates a new `CritBitTree` that contains the given `pairs`. + runnableExamples: + doAssert {"a": "0", "b": "1", "c": "2"}.toCritBitTree is CritBitTree[string] + for item in pairs: result.incl item[0], item[1] + +func toCritBitTree*[T](items: openArray[T]): CritBitTree[void] {.since: (1, 3).} = + ## Creates a new `CritBitTree` that contains the given `items`. + runnableExamples: + doAssert ["a", "b", "c"].toCritBitTree is CritBitTree[void] + for item in items: result.incl item + runnableExamples: static: From 0bfa0fcaea69d247304ed06cb129bfcc80fcd864 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Thu, 1 Oct 2020 21:17:16 +0800 Subject: [PATCH 0687/3768] [docs minor] unify generates and Generates (#15434) * [docs] unify generates and Generates * fix typo --- lib/pure/htmlgen.nim | 220 +++++++++++++++++++++---------------------- 1 file changed, 110 insertions(+), 110 deletions(-) diff --git a/lib/pure/htmlgen.nim b/lib/pure/htmlgen.nim index 99af26c99..ba50a4a6e 100644 --- a/lib/pure/htmlgen.nim +++ b/lib/pure/htmlgen.nim @@ -119,82 +119,82 @@ proc xmlCheckedTag*(argsList: NimNode, tag: string, optAttr = "", reqAttr = "", result = nestList(!"&", result) macro a*(e: varargs[untyped]): untyped = - ## generates the HTML ``a`` element. + ## Generates the HTML ``a`` element. result = xmlCheckedTag(e, "a", "href target download rel hreflang type " & commonAttr) macro abbr*(e: varargs[untyped]): untyped = - ## generates the HTML ``abbr`` element. + ## Generates the HTML ``abbr`` element. result = xmlCheckedTag(e, "abbr", commonAttr) macro address*(e: varargs[untyped]): untyped = - ## generates the HTML ``address`` element. + ## Generates the HTML ``address`` element. result = xmlCheckedTag(e, "address", commonAttr) macro area*(e: varargs[untyped]): untyped = - ## generates the HTML ``area`` element. + ## Generates the HTML ``area`` element. result = xmlCheckedTag(e, "area", "coords download href hreflang rel " & "shape target type" & commonAttr, "alt", true) macro article*(e: varargs[untyped]): untyped = - ## generates the HTML ``article`` element. + ## Generates the HTML ``article`` element. result = xmlCheckedTag(e, "article", commonAttr) macro aside*(e: varargs[untyped]): untyped = - ## generates the HTML ``aside`` element. + ## Generates the HTML ``aside`` element. result = xmlCheckedTag(e, "aside", commonAttr) macro audio*(e: varargs[untyped]): untyped = - ## generates the HTML ``audio`` element. + ## Generates the HTML ``audio`` element. result = xmlCheckedTag(e, "audio", "src crossorigin preload " & "autoplay mediagroup loop muted controls" & commonAttr) macro b*(e: varargs[untyped]): untyped = - ## generates the HTML ``b`` element. + ## Generates the HTML ``b`` element. result = xmlCheckedTag(e, "b", commonAttr) macro base*(e: varargs[untyped]): untyped = - ## generates the HTML ``base`` element. + ## Generates the HTML ``base`` element. result = xmlCheckedTag(e, "base", "href target" & commonAttr, "", true) macro bdi*(e: varargs[untyped]): untyped = - ## generates the HTML ``bdi`` element. + ## Generates the HTML ``bdi`` element. result = xmlCheckedTag(e, "bdi", commonAttr) macro bdo*(e: varargs[untyped]): untyped = - ## generates the HTML ``bdo`` element. + ## Generates the HTML ``bdo`` element. result = xmlCheckedTag(e, "bdo", commonAttr) macro big*(e: varargs[untyped]): untyped = - ## generates the HTML ``big`` element. + ## Generates the HTML ``big`` element. result = xmlCheckedTag(e, "big", commonAttr) macro blockquote*(e: varargs[untyped]): untyped = - ## generates the HTML ``blockquote`` element. + ## Generates the HTML ``blockquote`` element. result = xmlCheckedTag(e, "blockquote", " cite" & commonAttr) macro body*(e: varargs[untyped]): untyped = - ## generates the HTML ``body`` element. + ## Generates the HTML ``body`` element. result = xmlCheckedTag(e, "body", "onafterprint onbeforeprint " & "onbeforeunload onhashchange onmessage onoffline ononline onpagehide " & "onpageshow onpopstate onstorage onunload" & commonAttr) macro br*(e: varargs[untyped]): untyped = - ## generates the HTML ``br`` element. + ## Generates the HTML ``br`` element. result = xmlCheckedTag(e, "br", commonAttr, "", true) macro button*(e: varargs[untyped]): untyped = - ## generates the HTML ``button`` element. + ## Generates the HTML ``button`` element. result = xmlCheckedTag(e, "button", "autofocus disabled form formaction " & "formenctype formmethod formnovalidate formtarget menu name type value" & commonAttr) macro canvas*(e: varargs[untyped]): untyped = - ## generates the HTML ``canvas`` element. + ## Generates the HTML ``canvas`` element. result = xmlCheckedTag(e, "canvas", "width height" & commonAttr) macro caption*(e: varargs[untyped]): untyped = - ## generates the HTML ``caption`` element. + ## Generates the HTML ``caption`` element. result = xmlCheckedTag(e, "caption", commonAttr) macro center*(e: varargs[untyped]): untyped = @@ -202,35 +202,35 @@ macro center*(e: varargs[untyped]): untyped = result = xmlCheckedTag(e, "center", commonAttr) macro cite*(e: varargs[untyped]): untyped = - ## generates the HTML ``cite`` element. + ## Generates the HTML ``cite`` element. result = xmlCheckedTag(e, "cite", commonAttr) macro code*(e: varargs[untyped]): untyped = - ## generates the HTML ``code`` element. + ## Generates the HTML ``code`` element. result = xmlCheckedTag(e, "code", commonAttr) macro col*(e: varargs[untyped]): untyped = - ## generates the HTML ``col`` element. + ## Generates the HTML ``col`` element. result = xmlCheckedTag(e, "col", "span" & commonAttr, "", true) macro colgroup*(e: varargs[untyped]): untyped = - ## generates the HTML ``colgroup`` element. + ## Generates the HTML ``colgroup`` element. result = xmlCheckedTag(e, "colgroup", "span" & commonAttr) macro data*(e: varargs[untyped]): untyped = - ## generates the HTML ``data`` element. + ## Generates the HTML ``data`` element. result = xmlCheckedTag(e, "data", "value" & commonAttr) macro datalist*(e: varargs[untyped]): untyped = - ## generates the HTML ``datalist`` element. + ## Generates the HTML ``datalist`` element. result = xmlCheckedTag(e, "datalist", commonAttr) macro dd*(e: varargs[untyped]): untyped = - ## generates the HTML ``dd`` element. + ## Generates the HTML ``dd`` element. result = xmlCheckedTag(e, "dd", commonAttr) macro del*(e: varargs[untyped]): untyped = - ## generates the HTML ``del`` element. + ## Generates the HTML ``del`` element. result = xmlCheckedTag(e, "del", "cite datetime" & commonAttr) macro details*(e: varargs[untyped]): untyped = @@ -238,7 +238,7 @@ macro details*(e: varargs[untyped]): untyped = result = xmlCheckedTag(e, "details", commonAttr & "open") macro dfn*(e: varargs[untyped]): untyped = - ## generates the HTML ``dfn`` element. + ## Generates the HTML ``dfn`` element. result = xmlCheckedTag(e, "dfn", commonAttr) macro dialog*(e: varargs[untyped]): untyped = @@ -246,103 +246,103 @@ macro dialog*(e: varargs[untyped]): untyped = result = xmlCheckedTag(e, "dialog", commonAttr & "open") macro `div`*(e: varargs[untyped]): untyped = - ## generates the HTML ``div`` element. + ## Generates the HTML ``div`` element. result = xmlCheckedTag(e, "div", commonAttr) macro dl*(e: varargs[untyped]): untyped = - ## generates the HTML ``dl`` element. + ## Generates the HTML ``dl`` element. result = xmlCheckedTag(e, "dl", commonAttr) macro dt*(e: varargs[untyped]): untyped = - ## generates the HTML ``dt`` element. + ## Generates the HTML ``dt`` element. result = xmlCheckedTag(e, "dt", commonAttr) macro em*(e: varargs[untyped]): untyped = - ## generates the HTML ``em`` element. + ## Generates the HTML ``em`` element. result = xmlCheckedTag(e, "em", commonAttr) macro embed*(e: varargs[untyped]): untyped = - ## generates the HTML ``embed`` element. + ## Generates the HTML ``embed`` element. result = xmlCheckedTag(e, "embed", "src type height width" & commonAttr, "", true) macro fieldset*(e: varargs[untyped]): untyped = - ## generates the HTML ``fieldset`` element. + ## Generates the HTML ``fieldset`` element. result = xmlCheckedTag(e, "fieldset", "disabled form name" & commonAttr) macro figure*(e: varargs[untyped]): untyped = - ## generates the HTML ``figure`` element. + ## Generates the HTML ``figure`` element. result = xmlCheckedTag(e, "figure", commonAttr) macro figcaption*(e: varargs[untyped]): untyped = - ## generates the HTML ``figcaption`` element. + ## Generates the HTML ``figcaption`` element. result = xmlCheckedTag(e, "figcaption", commonAttr) macro footer*(e: varargs[untyped]): untyped = - ## generates the HTML ``footer`` element. + ## Generates the HTML ``footer`` element. result = xmlCheckedTag(e, "footer", commonAttr) macro form*(e: varargs[untyped]): untyped = - ## generates the HTML ``form`` element. + ## Generates the HTML ``form`` element. result = xmlCheckedTag(e, "form", "accept-charset action autocomplete " & "enctype method name novalidate target" & commonAttr) macro h1*(e: varargs[untyped]): untyped = - ## generates the HTML ``h1`` element. + ## Generates the HTML ``h1`` element. result = xmlCheckedTag(e, "h1", commonAttr) macro h2*(e: varargs[untyped]): untyped = - ## generates the HTML ``h2`` element. + ## Generates the HTML ``h2`` element. result = xmlCheckedTag(e, "h2", commonAttr) macro h3*(e: varargs[untyped]): untyped = - ## generates the HTML ``h3`` element. + ## Generates the HTML ``h3`` element. result = xmlCheckedTag(e, "h3", commonAttr) macro h4*(e: varargs[untyped]): untyped = - ## generates the HTML ``h4`` element. + ## Generates the HTML ``h4`` element. result = xmlCheckedTag(e, "h4", commonAttr) macro h5*(e: varargs[untyped]): untyped = - ## generates the HTML ``h5`` element. + ## Generates the HTML ``h5`` element. result = xmlCheckedTag(e, "h5", commonAttr) macro h6*(e: varargs[untyped]): untyped = - ## generates the HTML ``h6`` element. + ## Generates the HTML ``h6`` element. result = xmlCheckedTag(e, "h6", commonAttr) macro head*(e: varargs[untyped]): untyped = - ## generates the HTML ``head`` element. + ## Generates the HTML ``head`` element. result = xmlCheckedTag(e, "head", commonAttr) macro header*(e: varargs[untyped]): untyped = - ## generates the HTML ``header`` element. + ## Generates the HTML ``header`` element. result = xmlCheckedTag(e, "header", commonAttr) macro html*(e: varargs[untyped]): untyped = - ## generates the HTML ``html`` element. + ## Generates the HTML ``html`` element. result = xmlCheckedTag(e, "html", "xmlns" & commonAttr, "") macro hr*(): untyped = - ## generates the HTML ``hr`` element. + ## Generates the HTML ``hr`` element. result = xmlCheckedTag(newNimNode(nnkArglist), "hr", commonAttr, "", true) macro i*(e: varargs[untyped]): untyped = - ## generates the HTML ``i`` element. + ## Generates the HTML ``i`` element. result = xmlCheckedTag(e, "i", commonAttr) macro iframe*(e: varargs[untyped]): untyped = - ## generates the HTML ``iframe`` element. + ## Generates the HTML ``iframe`` element. result = xmlCheckedTag(e, "iframe", "src srcdoc name sandbox width height" & commonAttr) macro img*(e: varargs[untyped]): untyped = - ## generates the HTML ``img`` element. + ## Generates the HTML ``img`` element. result = xmlCheckedTag(e, "img", "crossorigin usemap ismap height width" & commonAttr, "src alt", true) macro input*(e: varargs[untyped]): untyped = - ## generates the HTML ``input`` element. + ## Generates the HTML ``input`` element. result = xmlCheckedTag(e, "input", "accept alt autocomplete autofocus " & "checked dirname disabled form formaction formenctype formmethod " & "formnovalidate formtarget height inputmode list max maxlength min " & @@ -350,45 +350,45 @@ macro input*(e: varargs[untyped]): untyped = "src step type value width" & commonAttr, "", true) macro ins*(e: varargs[untyped]): untyped = - ## generates the HTML ``ins`` element. + ## Generates the HTML ``ins`` element. result = xmlCheckedTag(e, "ins", "cite datetime" & commonAttr) macro kbd*(e: varargs[untyped]): untyped = - ## generates the HTML ``kbd`` element. + ## Generates the HTML ``kbd`` element. result = xmlCheckedTag(e, "kbd", commonAttr) macro keygen*(e: varargs[untyped]): untyped = - ## generates the HTML ``keygen`` element. + ## Generates the HTML ``keygen`` element. result = xmlCheckedTag(e, "keygen", "autofocus challenge disabled " & "form keytype name" & commonAttr) macro label*(e: varargs[untyped]): untyped = - ## generates the HTML ``label`` element. + ## Generates the HTML ``label`` element. result = xmlCheckedTag(e, "label", "form for" & commonAttr) macro legend*(e: varargs[untyped]): untyped = - ## generates the HTML ``legend`` element. + ## Generates the HTML ``legend`` element. result = xmlCheckedTag(e, "legend", commonAttr) macro li*(e: varargs[untyped]): untyped = - ## generates the HTML ``li`` element. + ## Generates the HTML ``li`` element. result = xmlCheckedTag(e, "li", "value" & commonAttr) macro link*(e: varargs[untyped]): untyped = - ## generates the HTML ``link`` element. + ## Generates the HTML ``link`` element. result = xmlCheckedTag(e, "link", "href crossorigin rel media hreflang " & "type sizes" & commonAttr, "", true) macro main*(e: varargs[untyped]): untyped = - ## generates the HTML ``main`` element. + ## Generates the HTML ``main`` element. result = xmlCheckedTag(e, "main", commonAttr) macro map*(e: varargs[untyped]): untyped = - ## generates the HTML ``map`` element. + ## Generates the HTML ``map`` element. result = xmlCheckedTag(e, "map", "name" & commonAttr) macro mark*(e: varargs[untyped]): untyped = - ## generates the HTML ``mark`` element. + ## Generates the HTML ``mark`` element. result = xmlCheckedTag(e, "mark", commonAttr) macro marquee*(e: varargs[untyped]): untyped = @@ -398,51 +398,51 @@ macro marquee*(e: varargs[untyped]): untyped = "scrolldelay truespeed vspace width onbounce onfinish onstart") macro meta*(e: varargs[untyped]): untyped = - ## generates the HTML ``meta`` element. + ## Generates the HTML ``meta`` element. result = xmlCheckedTag(e, "meta", "name http-equiv content charset" & commonAttr, "", true) macro meter*(e: varargs[untyped]): untyped = - ## generates the HTML ``meter`` element. + ## Generates the HTML ``meter`` element. result = xmlCheckedTag(e, "meter", "value min max low high optimum" & commonAttr) macro nav*(e: varargs[untyped]): untyped = - ## generates the HTML ``nav`` element. + ## Generates the HTML ``nav`` element. result = xmlCheckedTag(e, "nav", commonAttr) macro noscript*(e: varargs[untyped]): untyped = - ## generates the HTML ``noscript`` element. + ## Generates the HTML ``noscript`` element. result = xmlCheckedTag(e, "noscript", commonAttr) macro `object`*(e: varargs[untyped]): untyped = - ## generates the HTML ``object`` element. + ## Generates the HTML ``object`` element. result = xmlCheckedTag(e, "object", "data type typemustmatch name usemap " & "form width height" & commonAttr) macro ol*(e: varargs[untyped]): untyped = - ## generates the HTML ``ol`` element. + ## Generates the HTML ``ol`` element. result = xmlCheckedTag(e, "ol", "reversed start type" & commonAttr) macro optgroup*(e: varargs[untyped]): untyped = - ## generates the HTML ``optgroup`` element. + ## Generates the HTML ``optgroup`` element. result = xmlCheckedTag(e, "optgroup", "disabled" & commonAttr, "label", false) macro option*(e: varargs[untyped]): untyped = - ## generates the HTML ``option`` element. + ## Generates the HTML ``option`` element. result = xmlCheckedTag(e, "option", "disabled label selected value" & commonAttr) macro output*(e: varargs[untyped]): untyped = - ## generates the HTML ``output`` element. + ## Generates the HTML ``output`` element. result = xmlCheckedTag(e, "output", "for form name" & commonAttr) macro p*(e: varargs[untyped]): untyped = - ## generates the HTML ``p`` element. + ## Generates the HTML ``p`` element. result = xmlCheckedTag(e, "p", commonAttr) macro param*(e: varargs[untyped]): untyped = - ## generates the HTML ``param`` element. + ## Generates the HTML ``param`` element. result = xmlCheckedTag(e, "param", commonAttr, "name value", true) macro picture*(e: varargs[untyped]): untyped = @@ -450,56 +450,56 @@ macro picture*(e: varargs[untyped]): untyped = result = xmlCheckedTag(e, "picture", commonAttr) macro pre*(e: varargs[untyped]): untyped = - ## generates the HTML ``pre`` element. + ## Generates the HTML ``pre`` element. result = xmlCheckedTag(e, "pre", commonAttr) macro progress*(e: varargs[untyped]): untyped = - ## generates the HTML ``progress`` element. + ## Generates the HTML ``progress`` element. result = xmlCheckedTag(e, "progress", "value max" & commonAttr) macro q*(e: varargs[untyped]): untyped = - ## generates the HTML ``q`` element. + ## Generates the HTML ``q`` element. result = xmlCheckedTag(e, "q", "cite" & commonAttr) macro rb*(e: varargs[untyped]): untyped = - ## generates the HTML ``rb`` element. + ## Generates the HTML ``rb`` element. result = xmlCheckedTag(e, "rb", commonAttr) macro rp*(e: varargs[untyped]): untyped = - ## generates the HTML ``rp`` element. + ## Generates the HTML ``rp`` element. result = xmlCheckedTag(e, "rp", commonAttr) macro rt*(e: varargs[untyped]): untyped = - ## generates the HTML ``rt`` element. + ## Generates the HTML ``rt`` element. result = xmlCheckedTag(e, "rt", commonAttr) macro rtc*(e: varargs[untyped]): untyped = - ## generates the HTML ``rtc`` element. + ## Generates the HTML ``rtc`` element. result = xmlCheckedTag(e, "rtc", commonAttr) macro ruby*(e: varargs[untyped]): untyped = - ## generates the HTML ``ruby`` element. + ## Generates the HTML ``ruby`` element. result = xmlCheckedTag(e, "ruby", commonAttr) macro s*(e: varargs[untyped]): untyped = - ## generates the HTML ``s`` element. + ## Generates the HTML ``s`` element. result = xmlCheckedTag(e, "s", commonAttr) macro samp*(e: varargs[untyped]): untyped = - ## generates the HTML ``samp`` element. + ## Generates the HTML ``samp`` element. result = xmlCheckedTag(e, "samp", commonAttr) macro script*(e: varargs[untyped]): untyped = - ## generates the HTML ``script`` element. + ## Generates the HTML ``script`` element. result = xmlCheckedTag(e, "script", "src type charset async defer " & "crossorigin" & commonAttr) macro section*(e: varargs[untyped]): untyped = - ## generates the HTML ``section`` element. + ## Generates the HTML ``section`` element. result = xmlCheckedTag(e, "section", commonAttr) macro select*(e: varargs[untyped]): untyped = - ## generates the HTML ``select`` element. + ## Generates the HTML ``select`` element. result = xmlCheckedTag(e, "select", "autofocus disabled form multiple " & "name required size" & commonAttr) @@ -508,27 +508,27 @@ macro slot*(e: varargs[untyped]): untyped = result = xmlCheckedTag(e, "slot", commonAttr) macro small*(e: varargs[untyped]): untyped = - ## generates the HTML ``small`` element. + ## Generates the HTML ``small`` element. result = xmlCheckedTag(e, "small", commonAttr) macro source*(e: varargs[untyped]): untyped = - ## generates the HTML ``source`` element. + ## Generates the HTML ``source`` element. result = xmlCheckedTag(e, "source", "type" & commonAttr, "src", true) macro span*(e: varargs[untyped]): untyped = - ## generates the HTML ``span`` element. + ## Generates the HTML ``span`` element. result = xmlCheckedTag(e, "span", commonAttr) macro strong*(e: varargs[untyped]): untyped = - ## generates the HTML ``strong`` element. + ## Generates the HTML ``strong`` element. result = xmlCheckedTag(e, "strong", commonAttr) macro style*(e: varargs[untyped]): untyped = - ## generates the HTML ``style`` element. + ## Generates the HTML ``style`` element. result = xmlCheckedTag(e, "style", "media type" & commonAttr) macro sub*(e: varargs[untyped]): untyped = - ## generates the HTML ``sub`` element. + ## Generates the HTML ``sub`` element. result = xmlCheckedTag(e, "sub", commonAttr) macro summary*(e: varargs[untyped]): untyped = @@ -536,84 +536,84 @@ macro summary*(e: varargs[untyped]): untyped = result = xmlCheckedTag(e, "summary", commonAttr) macro sup*(e: varargs[untyped]): untyped = - ## generates the HTML ``sup`` element. + ## Generates the HTML ``sup`` element. result = xmlCheckedTag(e, "sup", commonAttr) macro table*(e: varargs[untyped]): untyped = - ## generates the HTML ``table`` element. + ## Generates the HTML ``table`` element. result = xmlCheckedTag(e, "table", "border sortable" & commonAttr) macro tbody*(e: varargs[untyped]): untyped = - ## generates the HTML ``tbody`` element. + ## Generates the HTML ``tbody`` element. result = xmlCheckedTag(e, "tbody", commonAttr) macro td*(e: varargs[untyped]): untyped = - ## generates the HTML ``td`` element. + ## Generates the HTML ``td`` element. result = xmlCheckedTag(e, "td", "colspan rowspan headers" & commonAttr) macro `template`*(e: varargs[untyped]): untyped = - ## generates the HTML ``template`` element. + ## Generates the HTML ``template`` element. result = xmlCheckedTag(e, "template", commonAttr) macro textarea*(e: varargs[untyped]): untyped = - ## generates the HTML ``textarea`` element. + ## Generates the HTML ``textarea`` element. result = xmlCheckedTag(e, "textarea", "autocomplete autofocus cols " & "dirname disabled form inputmode maxlength minlength name placeholder " & "readonly required rows wrap" & commonAttr) macro tfoot*(e: varargs[untyped]): untyped = - ## generates the HTML ``tfoot`` element. + ## Generates the HTML ``tfoot`` element. result = xmlCheckedTag(e, "tfoot", commonAttr) macro th*(e: varargs[untyped]): untyped = - ## generates the HTML ``th`` element. + ## Generates the HTML ``th`` element. result = xmlCheckedTag(e, "th", "colspan rowspan headers abbr scope axis" & " sorted" & commonAttr) macro thead*(e: varargs[untyped]): untyped = - ## generates the HTML ``thead`` element. + ## Generates the HTML ``thead`` element. result = xmlCheckedTag(e, "thead", commonAttr) macro time*(e: varargs[untyped]): untyped = - ## generates the HTML ``time`` element. + ## Generates the HTML ``time`` element. result = xmlCheckedTag(e, "time", "datetime" & commonAttr) macro title*(e: varargs[untyped]): untyped = - ## generates the HTML ``title`` element. + ## Generates the HTML ``title`` element. result = xmlCheckedTag(e, "title", commonAttr) macro tr*(e: varargs[untyped]): untyped = - ## generates the HTML ``tr`` element. + ## Generates the HTML ``tr`` element. result = xmlCheckedTag(e, "tr", commonAttr) macro track*(e: varargs[untyped]): untyped = - ## generates the HTML ``track`` element. + ## Generates the HTML ``track`` element. result = xmlCheckedTag(e, "track", "kind srclang label default" & commonAttr, "src", true) macro tt*(e: varargs[untyped]): untyped = - ## generates the HTML ``tt`` element. + ## Generates the HTML ``tt`` element. result = xmlCheckedTag(e, "tt", commonAttr) macro u*(e: varargs[untyped]): untyped = - ## generates the HTML ``u`` element. + ## Generates the HTML ``u`` element. result = xmlCheckedTag(e, "u", commonAttr) macro ul*(e: varargs[untyped]): untyped = - ## generates the HTML ``ul`` element. + ## Generates the HTML ``ul`` element. result = xmlCheckedTag(e, "ul", commonAttr) macro `var`*(e: varargs[untyped]): untyped = - ## generates the HTML ``var`` element. + ## Generates the HTML ``var`` element. result = xmlCheckedTag(e, "var", commonAttr) macro video*(e: varargs[untyped]): untyped = - ## generates the HTML ``video`` element. + ## Generates the HTML ``video`` element. result = xmlCheckedTag(e, "video", "src crossorigin poster preload " & "autoplay mediagroup loop muted controls width height" & commonAttr) macro wbr*(e: varargs[untyped]): untyped = - ## generates the HTML ``wbr`` element. + ## Generates the HTML ``wbr`` element. result = xmlCheckedTag(e, "wbr", commonAttr, "", true) From 3919f0aa544d84c7a236e5095b87d7d0c8fb63b4 Mon Sep 17 00:00:00 2001 From: alaviss Date: Thu, 1 Oct 2020 10:20:38 -0500 Subject: [PATCH 0688/3768] koch: unify nimble building scripts [backport:1.4] (#15443) This commit gets rid of buildNimble in favor of the simpler bundleNimbleExe, which does pretty much the same thing. --- koch.nim | 34 +++++----------------------------- 1 file changed, 5 insertions(+), 29 deletions(-) diff --git a/koch.nim b/koch.nim index 68c5764be..714d70bfe 100644 --- a/koch.nim +++ b/koch.nim @@ -145,35 +145,11 @@ proc bundleC2nim(args: string) = proc bundleNimbleExe(latest: bool, args: string) = let commit = if latest: "HEAD" else: NimbleStableCommit - cloneDependency(distDir, "https://github.com/nim-lang/nimble.git", commit = commit) + cloneDependency(distDir, "https://github.com/nim-lang/nimble.git", + commit = commit, allowBundled = true) # installer.ini expects it under $nim/bin nimCompile("dist/nimble/src/nimble.nim", - options = "-d:release --nilseqs:on " & args) - -proc buildNimble(latest: bool, args: string) = - # if koch is used for a tar.xz, build the dist/nimble we shipped - # with the tarball: - var installDir = "dist/nimble" - if not latest and dirExists(installDir) and not dirExists("dist/nimble/.git"): - discard "don't do the git dance" - else: - if not dirExists("dist/nimble/.git"): - if dirExists(installDir): - var id = 0 - while dirExists("dist/nimble" & $id): - inc id - installDir = "dist/nimble" & $id - # consider using/adapting cloneDependency - exec("git clone -q https://github.com/nim-lang/nimble.git " & installDir) - withDir(installDir): - if latest: - exec("git checkout -f master") - exec("git pull") - else: - exec("git fetch") - exec("git checkout " & NimbleStableCommit) - nimCompile(installDir / "src/nimble.nim", - options = "--noNimblePath --nilseqs:on -d:release " & args) + options = "-d:release --noNimblePath " & args) proc bundleNimsuggest(args: string) = nimCompileFold("Compile nimsuggest", "nimsuggest/nimsuggest.nim", @@ -704,14 +680,14 @@ when isMainModule: of "temp": temp(op.cmdLineRest) of "xtemp": xtemp(op.cmdLineRest) of "wintools": bundleWinTools(op.cmdLineRest) - of "nimble": buildNimble(latest, op.cmdLineRest) + of "nimble": bundleNimbleExe(latest, op.cmdLineRest) of "nimsuggest": bundleNimsuggest(op.cmdLineRest) # toolsNoNimble is kept for backward compatibility with build scripts of "toolsnonimble", "toolsnoexternal": buildTools(op.cmdLineRest) of "tools": buildTools(op.cmdLineRest) - buildNimble(latest, op.cmdLineRest) + bundleNimbleExe(latest, op.cmdLineRest) bundleFusion(latest) of "pushcsource", "pushcsources": pushCsources() of "valgrind": valgrind(op.cmdLineRest) From 531ed2dc365d6d33c7bccdfbe22df561cf14af86 Mon Sep 17 00:00:00 2001 From: cooldome Date: Thu, 1 Oct 2020 16:39:48 +0100 Subject: [PATCH 0689/3768] fix #15405. deepcopy arc (#15410) * fix #15405 * fix tests * deepcopy for ARC has to be enabled via --deepcopy:on Co-authored-by: Araq --- changelog.md | 3 +++ compiler/ccgexprs.nim | 4 ++++ compiler/ccgtypes.nim | 7 +++++-- compiler/commands.nim | 3 ++- compiler/options.nim | 1 + doc/advopt.txt | 1 + lib/system.nim | 3 +++ tests/arc/tarcmisc.nim | 33 ++++++++++++++++++++++++++++++++- tests/arc/tdeepcopy.nim | 2 +- 9 files changed, 52 insertions(+), 5 deletions(-) diff --git a/changelog.md b/changelog.md index 5061a90b6..3bae911e9 100644 --- a/changelog.md +++ b/changelog.md @@ -287,6 +287,9 @@ proc mydiv(a, b): int {.raises: [].} = performance this keyword can enable. - `items` no longer compiles with enum with holes as its behavior was error prone, see #14004 +- `system.deepcopy` has to be enabled explicitly for `--gc:arc` and `--gc:orc` via + `--deepcopy:on`. + - Added `critbits.toCritBitTree`, similar to `tables.toTable`, creates a new `CritBitTree` with given arguments. diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index b6808a55e..49b1f3d16 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2394,6 +2394,10 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = let n = semparallel.liftParallel(p.module.g.graph, p.module.module, e) expr(p, n, d) of mDeepCopy: + if p.config.selectedGC in {gcArc, gcOrc} and optEnableDeepCopy notin p.config.globalOptions: + localError(p.config, e.info, + "for --gc:arc|orc 'deepcopy' support has to be enabled with --deepcopy:on") + var a, b: TLoc let x = if e[1].kind in {nkAddr, nkHiddenAddr}: e[1][0] else: e[1] initLocExpr(p, x, a) diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index c1a214532..0df7c8d2a 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -1355,6 +1355,9 @@ proc genTypeInfoV2Impl(m: BModule, t, origType: PType, name: Rope; info: TLineIn name, destroyImpl, getTypeDesc(m, t), typeName, traceImpl, disposeImpl]) + if t.kind == tyObject and t.len > 0 and t[0] != nil and optEnableDeepCopy in m.config.globalOptions: + discard genTypeInfoV1(m, t, info) + proc genTypeInfoV2(m: BModule, t: PType; info: TLineInfo): Rope = let origType = t var t = skipTypes(origType, irrelevantForBackend + tyUserTypeClasses) @@ -1454,12 +1457,12 @@ proc genTypeInfoV1(m: BModule, t: PType; info: TLineInfo): Rope = genTupleInfo(m, x, x, result, info) of tySequence: genTypeInfoAux(m, t, t, result, info) - if m.config.selectedGC >= gcMarkAndSweep: + if m.config.selectedGC in {gcMarkAndSweep, gcRefc, gcV2, gcGo}: let markerProc = genTraverseProc(m, origType, sig) m.s[cfsTypeInit3].addf("$1.marker = $2;$n", [tiNameForHcr(m, result), markerProc]) of tyRef: genTypeInfoAux(m, t, t, result, info) - if m.config.selectedGC >= gcMarkAndSweep: + if m.config.selectedGC in {gcMarkAndSweep, gcRefc, gcV2, gcGo}: let markerProc = genTraverseProc(m, origType, sig) m.s[cfsTypeInit3].addf("$1.marker = $2;$n", [tiNameForHcr(m, result), markerProc]) of tyPtr, tyRange, tyUncheckedArray: genTypeInfoAux(m, t, t, result, info) diff --git a/compiler/commands.nim b/compiler/commands.nim index 17124a759..19048065f 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -901,7 +901,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "sourcemap": conf.globalOptions.incl optSourcemap conf.options.incl optLineDir - # processOnOffSwitchG(conf, {optSourcemap, opt}, arg, pass, info) + of "deepcopy": + processOnOffSwitchG(conf, {optEnableDeepCopy}, arg, pass, info) of "": # comes from "-" in for example: `nim c -r -` (gets stripped from -) handleStdinInput(conf) else: diff --git a/compiler/options.nim b/compiler/options.nim index efb0aa2da..40d86a01d 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -96,6 +96,7 @@ type # please make sure we have under 32 options optNimV1Emulation # emulate Nim v1.0 optSourcemap optProfileVM # enable VM profiler + optEnableDeepCopy # ORC specific: enable 'deepcopy' for all types. TGlobalOptions* = set[TGlobalOption] diff --git a/doc/advopt.txt b/doc/advopt.txt index 830b8bfc6..60f5a1e10 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -153,3 +153,4 @@ Advanced options: --profileVM:on|off enable compile time VM profiler --sinkInference:on|off en-/disable sink parameter inference (default: on) --panics:on|off turn panics into process terminations (default: off) + --deepcopy:on|off enable 'system.deepCopy' for ``--gc:arc|orc`` diff --git a/lib/system.nim b/lib/system.nim index 0784cacce..fe73a70f5 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2895,6 +2895,9 @@ when hasAlloc and notJSnotNims: ## ## This is also used by the code generator ## for the implementation of ``spawn``. + ## + ## For ``--gc:arc`` or ``--gc:orc`` deepcopy support has to be enabled + ## via ``--deepcopy:on``. discard proc deepCopy*[T](y: T): T = diff --git a/tests/arc/tarcmisc.nim b/tests/arc/tarcmisc.nim index 542e4f316..b53d49df8 100644 --- a/tests/arc/tarcmisc.nim +++ b/tests/arc/tarcmisc.nim @@ -30,7 +30,7 @@ closed destroying variable: 20 destroying variable: 10 ''' - cmd: "nim c --gc:arc $file" + cmd: "nim c --gc:arc --deepcopy:on $file" """ proc takeSink(x: sink string): bool = true @@ -347,3 +347,34 @@ var data = { # For ARC listVal is empty for some reason doAssert data["examples"]["values"].listVal[0].strVal == "test" + + + + +############################################################################### +# bug #15405 +import parsexml +const test_xml_str = "value" +var stream = newStringStream(test_xml_str) +var xml: XmlParser +open(xml, stream, "test") +var xml2 = deepCopy(xml) + +proc text_parser(xml: var XmlParser) = + var test_passed = false + while true: + xml.next() + case xml.kind + of xmlElementStart: + if xml.elementName == "B": + xml.next() + if xml.kind == xmlCharData and xml.charData == "value": + test_passed = true + + of xmlEof: break + else: discard + xml.close() + doAssert(test_passed) + +text_parser(xml) +text_parser(xml2) \ No newline at end of file diff --git a/tests/arc/tdeepcopy.nim b/tests/arc/tdeepcopy.nim index 0eaf7ea40..91d93aa28 100644 --- a/tests/arc/tdeepcopy.nim +++ b/tests/arc/tdeepcopy.nim @@ -1,5 +1,5 @@ discard """ - cmd: "nim c --gc:arc $file" + cmd: "nim c --gc:arc --deepcopy:on $file" output: '''13 abc 13 abc 13 abc From 26d656e73e2354bc6f177073b948a7791e8f7427 Mon Sep 17 00:00:00 2001 From: c-blake Date: Thu, 1 Oct 2020 12:06:38 -0400 Subject: [PATCH 0690/3768] Add first draft of new osproc.readLines (#15429) * Add first draft of new osproc.readLines * Add test for new osproc.readLines * Rename test to start w/t to run; Also add newline to output * Suppress hint messages. * Output should match this time. * Shoulda picked a program with simpler syntax than ..lol * Address https://github.com/nim-lang/Nim/pull/15429#issuecomment-701890898 and https://github.com/nim-lang/Nim/pull/15429#issuecomment-701985976 by factoring `readLines` into `iterator lines` and a wrapper `proc`. * Address https://github.com/nim-lang/Nim/pull/15429#issuecomment-702127289 and also add a `ReadIOEffect` tag to the iterator (called by the wrapper.. so it should need no separate tag, if I understand correctly). --- changelog.md | 2 ++ lib/pure/osproc.nim | 51 +++++++++++++++++++++++++++++++++++++ tests/osproc/treadlines.nim | 20 +++++++++++++++ 3 files changed, 73 insertions(+) create mode 100644 tests/osproc/treadlines.nim diff --git a/changelog.md b/changelog.md index 3bae911e9..3cdf138ad 100644 --- a/changelog.md +++ b/changelog.md @@ -199,6 +199,8 @@ - Add `initUri(isIpv6: bool)` to `uri` module, now `uri` supports parsing ipv6 hostname. +- Add `readLines(p: Process)` to `osproc` module for `startProcess` convenience. + ## Language changes - The `=destroy` hook no longer has to reset its target, as the compiler now automatically inserts diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 5113695d8..2d48597dc 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -444,6 +444,57 @@ proc execProcesses*(cmds: openArray[string], if afterRunEvent != nil: afterRunEvent(i, p) close(p) +iterator lines*(p: Process): string {.since: (1, 3), tags: [ReadIOEffect].} = + ## Convenience iterator for working with `startProcess` to read data from a + ## background process. + ## + ## See also: + ## * `readLines proc <#readLines,Process>`_ + ## + ## Example: + ## + ## .. code-block:: Nim + ## const opts = {poUsePath, poDaemon, poStdErrToStdOut} + ## var ps: seq[Process] + ## for prog in ["a", "b"]: # run 2 progs in parallel + ## ps.add startProcess("nim", "", ["r", prog], nil, opts) + ## for p in ps: + ## var i = 0 + ## for line in p.lines: + ## echo line + ## i.inc + ## if i > 100: break + ## p.close + var outp = p.outputStream + var line = newStringOfCap(120) + while true: + if outp.readLine(line): + yield line + else: + if p.peekExitCode != -1: break + +proc readLines*(p: Process): (seq[string], int) {.since: (1, 3).} = + ## Convenience function for working with `startProcess` to read data from a + ## background process. + ## + ## See also: + ## * `lines iterator <#lines.i,Process>`_ + ## + ## Example: + ## + ## .. code-block:: Nim + ## const opts = {poUsePath, poDaemon, poStdErrToStdOut} + ## var ps: seq[Process] + ## for prog in ["a", "b"]: # run 2 progs in parallel + ## ps.add startProcess("nim", "", ["r", prog], nil, opts) + ## for p in ps: + ## let (lines, exCode) = p.readLines + ## if exCode != 0: + ## for line in lines: echo line + ## p.close + for line in p.lines: result[0].add(line) + result[1] = p.peekExitCode + when not defined(useNimRtl): proc execProcess(command: string, workingDir: string = "", args: openArray[string] = [], env: StringTableRef = nil, diff --git a/tests/osproc/treadlines.nim b/tests/osproc/treadlines.nim new file mode 100644 index 000000000..3a8303321 --- /dev/null +++ b/tests/osproc/treadlines.nim @@ -0,0 +1,20 @@ +discard """ + output: '''Error: cannot open 'a.nim' +Error: cannot open 'b.nim' +''' + targets: "c" +""" + +import osproc + +var ps: seq[Process] # compile & run 2 progs in parallel +for prog in ["a", "b"]: + ps.add startProcess("nim", "", + ["r", "--hint[Conf]=off", "--hint[Processing]=off", prog], + options = {poUsePath, poDaemon, poStdErrToStdOut}) + +for p in ps: + let (lines, exCode) = p.readLines + if exCode != 0: + for line in lines: echo line + p.close From 84d9b1227d036e2286cbceace0d8dd27c515c62b Mon Sep 17 00:00:00 2001 From: shirleyquirk <31934565+shirleyquirk@users.noreply.github.com> Date: Thu, 1 Oct 2020 17:07:55 +0100 Subject: [PATCH 0691/3768] fixes #15430, pop pragma takes invalid input (#15449) * fix #15430, pop pragma {.pop, experimental.} should be allowed, but not {.pop experimental.} --- compiler/pragmas.nim | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 3b7ae175d..67e574d52 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -1037,7 +1037,9 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of wPush: processPush(c, n, i + 1) result = true - of wPop: processPop(c, it) + of wPop: + processPop(c, it) + result = true of wPragma: if not sym.isNil and sym.kind == skTemplate: sym.flags.incl sfCustomPragma From 800d6204cd1e55919fb608ed8f151774894ee2b7 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Thu, 1 Oct 2020 19:58:08 +0200 Subject: [PATCH 0692/3768] Fix "arraq" typo :) --- doc/manual_experimental.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual_experimental.rst b/doc/manual_experimental.rst index a71fd00b9..109399d61 100644 --- a/doc/manual_experimental.rst +++ b/doc/manual_experimental.rst @@ -1906,7 +1906,7 @@ Let ``source`` be one of: not cover locals of inner procs. - A thread-local ``var`` or ``let``. - A global ``let`` or ``const``. -- A constant arraq/seq/object/tuple constructor. +- A constant array/seq/object/tuple constructor. Path expressions From 37e0d6f32d39752e0017685c471e19071570150c Mon Sep 17 00:00:00 2001 From: Clyybber Date: Thu, 1 Oct 2020 19:58:53 +0200 Subject: [PATCH 0693/3768] Fix typo --- doc/manual_experimental.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual_experimental.rst b/doc/manual_experimental.rst index 109399d61..655adae02 100644 --- a/doc/manual_experimental.rst +++ b/doc/manual_experimental.rst @@ -1994,7 +1994,7 @@ Start of a borrow A borrow starts with one of the following: - The assignment of a non-view-type to a view-type. -- The assingment of a location that is derived from a local parameter +- The assignment of a location that is derived from a local parameter to a view-type. From b4fe0c88624e3af03715a93733644b0c71dfdcce Mon Sep 17 00:00:00 2001 From: Euan Date: Thu, 1 Oct 2020 21:15:34 +0100 Subject: [PATCH 0694/3768] Fix #15452 - ip protocol not defined on NetBSD (#15453) --- tests/stdlib/tgetprotobyname.nim | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/stdlib/tgetprotobyname.nim b/tests/stdlib/tgetprotobyname.nim index c3d8da0d8..b4df05102 100644 --- a/tests/stdlib/tgetprotobyname.nim +++ b/tests/stdlib/tgetprotobyname.nim @@ -10,7 +10,10 @@ discard """ import nativesockets -doAssert getProtoByName("ip") == 0 +when not defined(netbsd): + # Ref: https://github.com/nim-lang/Nim/issues/15452 - NetBSD doesn't define an `ip` protocol + doAssert getProtoByName("ip") == 0 + doAssert getProtoByName("ipv6") == 41 doAssert getProtoByName("tcp") == 6 doAssert getProtoByName("udp") == 17 From d1eb76184b114736a40aef2299f98be6b0b384c7 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Thu, 1 Oct 2020 17:19:06 -0300 Subject: [PATCH 0695/3768] Clean out niminst (#15451) --- tools/niminst/niminst.nim | 105 +++++++++++--------------------------- 1 file changed, 31 insertions(+), 74 deletions(-) diff --git a/tools/niminst/niminst.nim b/tools/niminst/niminst.nim index a5fc388cd..2b6172390 100644 --- a/tools/niminst/niminst.nim +++ b/tools/niminst/niminst.nim @@ -7,12 +7,6 @@ # distribution, for details about the copyright. # -const - haveZipLib = false # zip not in stdlib anymore - -when haveZipLib: - import zipfiles - import os, strutils, parseopt, parsecfg, strtabs, streams, debcreation, std / sha1 @@ -36,7 +30,6 @@ type actionInno, # action: create Inno Setup installer actionNsis, # action: create NSIS installer actionScripts # action: create install and deinstall scripts - actionZip, # action: create zip file actionXz, # action: create xz file actionDeb # action: prepare deb package @@ -75,7 +68,7 @@ const "$configdir", "$datadir", "$docdir", "$libdir" ] -proc iniConfigData(c: var ConfigData) = +func iniConfigData(c: var ConfigData) = c.actions = {} for i in low(FileCategory)..high(FileCategory): c.cat[i] = @[] c.binPaths = @[] @@ -107,17 +100,17 @@ proc iniConfigData(c: var ConfigData) = c.debOpts.shortDesc = "" c.debOpts.licenses = @[] -proc firstBinPath(c: ConfigData): string = +func firstBinPath(c: ConfigData): string = if c.binPaths.len > 0: result = c.binPaths[0] else: result = "" -proc `\`(a, b: string): string = +func `\`(a, b: string): string = result = if a.len == 0: b else: a & '\\' & b template toUnix(s: string): string = s.replace('\\', '/') template toWin(s: string): string = s.replace('/', '\\') -proc skipRoot(f: string): string = +func skipRoot(f: string): string = # "abc/def/xyz" --> "def/xyz" var i = 0 result = "" @@ -146,7 +139,6 @@ Usage: Command: csource build C source code for source based installations scripts build install and deinstall scripts - zip build the ZIP file inno build the Inno Setup installer nsis build the NSIS Setup installer deb create files for debhelper @@ -175,7 +167,6 @@ proc parseCmdLine(c: var ConfigData) = case a of "csource": incl(c.actions, actionCSource) of "scripts": incl(c.actions, actionScripts) - of "zip": incl(c.actions, actionZip) of "xz": incl(c.actions, actionXz) of "inno": incl(c.actions, actionInno) of "nsis": incl(c.actions, actionNsis) @@ -221,11 +212,11 @@ proc eqT(a, b: string; t: proc (a: char): char{.nimcall.}): bool = inc j result = i >= a.len and j >= b.len -proc tPath(c: char): char = +func tPath(c: char): char = if c == '\\': '/' else: c -proc ignoreFile(f, explicit: string, allowHtml: bool): bool = +func ignoreFile(f, explicit: string, allowHtml: bool): bool = let (_, name, ext) = splitFile(f) let html = if not allowHtml: ".html" else: "" result = (ext in ["", ".exe", ".idx", ".o", ".obj", ".dylib"] or @@ -283,13 +274,13 @@ proc yesno(p: var CfgParser, v: string): bool = result = false else: quit(errorStr(p, "unknown value; use: yes|no")) -proc incl(s: var seq[string], x: string): int = +func incl(s: var seq[string], x: string): int = for i in 0 ..< s.len: if cmpIgnoreStyle(s[i], x) == 0: return i s.add(x) result = s.len-1 -proc platforms(c: var ConfigData, v: string) = +func platforms(c: var ConfigData, v: string) = for line in splitLines(v): let p = line.find(": ") if p <= 1: continue @@ -462,10 +453,10 @@ proc readCFiles(c: var ConfigData, osA, cpuA: int) = else: quit("Cannot open: " & f) -proc buildDir(os, cpu: int): string = - return "c_code" / ($os & "_" & $cpu) +func buildDir(os, cpu: int): string = + "c_code" / ($os & "_" & $cpu) -proc getOutputDir(c: var ConfigData): string = +func getOutputDir(c: var ConfigData): string = if c.outdir.len > 0: c.outdir else: "build" proc writeFile(filename, content, newline: string) = @@ -596,42 +587,6 @@ proc setupDist2(c: var ConfigData) = else: quit("External program failed") -# ------------------ generate ZIP file --------------------------------------- -when haveZipLib: - proc zipDist(c: var ConfigData) = - var proj = toLowerAscii(c.name) & "-" & c.version - var n = "$#.zip" % proj - if c.outdir.len == 0: n = "build" / n - else: n = c.outdir / n - var z: ZipArchive - if open(z, n, fmWrite): - addFile(z, proj / buildBatFile, "build" / buildBatFile) - addFile(z, proj / buildBatFile32, "build" / buildBatFile32) - addFile(z, proj / buildBatFile64, "build" / buildBatFile64) - addFile(z, proj / buildShFile, "build" / buildShFile) - addFile(z, proj / makeFile, "build" / makeFile) - addFile(z, proj / installShFile, installShFile) - addFile(z, proj / deinstallShFile, deinstallShFile) - - template addFileAux(src, dst) = addFile(z, dst, src) - gatherFiles(addFileAux, c.libpath, proj / "c_code") - for osA in 1..c.oses.len: - for cpuA in 1..c.cpus.len: - var dir = buildDir(osA, cpuA) - for k, f in walkDir("build" / dir): - if k == pcFile: addFile(z, proj / dir / extractFilename(f), f) - - for cat in items({fcConfig..fcOther, fcUnix, fcNimble}): - for f in items(c.cat[cat]): addFile(z, proj / f, f) - - # Copy the .nimble file over - let nimbleFile = c.nimblePkgName & ".nimble" - processFile(z, proj / nimbleFile, nimbleFile) - - close(z) - else: - quit("Cannot open for writing: " & n) - proc xzDist(c: var ConfigData; windowsZip=false) = let proj = toLowerAscii(c.name) & "-" & c.version let tmpDir = if c.outdir.len == 0: "build" else: c.outdir @@ -753,21 +708,23 @@ proc debDist(c: var ConfigData) = # ------------------- main ---------------------------------------------------- -var c: ConfigData -iniConfigData(c) -parseCmdLine(c) -parseIniFile(c) -if actionInno in c.actions: - setupDist(c) -if actionNsis in c.actions: - setupDist2(c) -if actionCSource in c.actions: - srcdist(c) -if actionScripts in c.actions: - writeInstallScripts(c) -if actionZip in c.actions: - xzDist(c, true) -if actionXz in c.actions: - xzDist(c) -if actionDeb in c.actions: - debDist(c) +proc main() = + var c: ConfigData + iniConfigData(c) + parseCmdLine(c) + parseIniFile(c) + if actionInno in c.actions: + setupDist(c) + if actionNsis in c.actions: + setupDist2(c) + if actionCSource in c.actions: + srcdist(c) + if actionScripts in c.actions: + writeInstallScripts(c) + if actionXz in c.actions: + xzDist(c) + if actionDeb in c.actions: + debDist(c) + +when isMainModule: + main() From 6e32a9e88c4f2a63a4645748e06a9ff51d384232 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Fri, 2 Oct 2020 14:47:24 +0800 Subject: [PATCH 0696/3768] use func in httpcore (#15457) --- lib/pure/httpcore.nim | 44 +++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/lib/pure/httpcore.nim b/lib/pure/httpcore.nim index 81e02a1f8..e7da77ae8 100644 --- a/lib/pure/httpcore.nim +++ b/lib/pure/httpcore.nim @@ -102,24 +102,24 @@ const const httpNewLine* = "\c\L" const headerLimit* = 10_000 -proc toTitleCase(s: string): string = +func toTitleCase(s: string): string = result = newString(len(s)) var upper = true for i in 0..len(s) - 1: result[i] = if upper: toUpperAscii(s[i]) else: toLowerAscii(s[i]) upper = s[i] == '-' -proc toCaseInsensitive(headers: HttpHeaders, s: string): string {.inline.} = +func toCaseInsensitive(headers: HttpHeaders, s: string): string {.inline.} = return if headers.isTitleCase: toTitleCase(s) else: toLowerAscii(s) -proc newHttpHeaders*(titleCase=false): HttpHeaders = +func newHttpHeaders*(titleCase=false): HttpHeaders = ## Returns a new ``HttpHeaders`` object. if ``titleCase`` is set to true, ## headers are passed to the server in title case (e.g. "Content-Length") new result result.table = newTable[string, seq[string]]() result.isTitleCase = titleCase -proc newHttpHeaders*(keyValuePairs: +func newHttpHeaders*(keyValuePairs: openArray[tuple[key: string, val: string]], titleCase=false): HttpHeaders = ## Returns a new ``HttpHeaders`` object from an array. if ``titleCase`` is set to true, ## headers are passed to the server in title case (e.g. "Content-Length") @@ -134,13 +134,13 @@ proc newHttpHeaders*(keyValuePairs: result.table[key] = @[pair.val] -proc `$`*(headers: HttpHeaders): string = +func `$`*(headers: HttpHeaders): string = return $headers.table proc clear*(headers: HttpHeaders) = headers.table.clear() -proc `[]`*(headers: HttpHeaders, key: string): HttpHeaderValues = +func `[]`*(headers: HttpHeaders, key: string): HttpHeaderValues = ## Returns the values associated with the given ``key``. If the returned ## values are passed to a procedure expecting a ``string``, the first ## value is automatically picked. If there are @@ -153,7 +153,7 @@ proc `[]`*(headers: HttpHeaders, key: string): HttpHeaderValues = converter toString*(values: HttpHeaderValues): string = return seq[string](values)[0] -proc `[]`*(headers: HttpHeaders, key: string, i: int): string = +func `[]`*(headers: HttpHeaders, key: string, i: int): string = ## Returns the ``i``'th value associated with the given key. If there are ## no values associated with the key or the ``i``'th value doesn't exist, ## an exception is raised. @@ -188,16 +188,16 @@ iterator pairs*(headers: HttpHeaders): tuple[key, value: string] = for value in v: yield (k, value) -proc contains*(values: HttpHeaderValues, value: string): bool = +func contains*(values: HttpHeaderValues, value: string): bool = ## Determines if ``value`` is one of the values inside ``values``. Comparison ## is performed without case sensitivity. for val in seq[string](values): if val.toLowerAscii == value.toLowerAscii: return true -proc hasKey*(headers: HttpHeaders, key: string): bool = +func hasKey*(headers: HttpHeaders, key: string): bool = return headers.table.hasKey(headers.toCaseInsensitive(key)) -proc getOrDefault*(headers: HttpHeaders, key: string, +func getOrDefault*(headers: HttpHeaders, key: string, default = @[""].HttpHeaderValues): HttpHeaderValues = ## Returns the values associated with the given ``key``. If there are no ## values associated with the key, then ``default`` is returned. @@ -206,9 +206,9 @@ proc getOrDefault*(headers: HttpHeaders, key: string, else: return default -proc len*(headers: HttpHeaders): int = return headers.table.len +func len*(headers: HttpHeaders): int = return headers.table.len -proc parseList(line: string, list: var seq[string], start: int): int = +func parseList(line: string, list: var seq[string], start: int): int = var i = 0 var current = "" while start+i < line.len and line[start + i] notin {'\c', '\l'}: @@ -219,7 +219,7 @@ proc parseList(line: string, list: var seq[string], start: int): int = i.inc # Skip , current.setLen(0) -proc parseHeader*(line: string): tuple[key: string, value: seq[string]] = +func parseHeader*(line: string): tuple[key: string, value: seq[string]] = ## Parses a single raw header HTTP line into key value pairs. ## ## Used by ``asynchttpserver`` and ``httpclient`` internally and should not @@ -239,7 +239,7 @@ proc parseHeader*(line: string): tuple[key: string, value: seq[string]] = else: result.value = @[] -proc `==`*(protocol: tuple[orig: string, major, minor: int], +func `==`*(protocol: tuple[orig: string, major, minor: int], ver: HttpVersion): bool = let major = case ver @@ -250,10 +250,10 @@ proc `==`*(protocol: tuple[orig: string, major, minor: int], of HttpVer10: 0 result = protocol.major == major and protocol.minor == minor -proc contains*(methods: set[HttpMethod], x: string): bool = +func contains*(methods: set[HttpMethod], x: string): bool = return parseEnum[HttpMethod](x) in methods -proc `$`*(code: HttpCode): string = +func `$`*(code: HttpCode): string = ## Converts the specified ``HttpCode`` into a HTTP status. ## ## For example: @@ -311,7 +311,7 @@ proc `$`*(code: HttpCode): string = of 505: "505 HTTP Version Not Supported" else: $(int(code)) -proc `==`*(a, b: HttpCode): bool {.borrow.} +func `==`*(a, b: HttpCode): bool {.borrow.} proc `==`*(rawCode: string, code: HttpCode): bool {.deprecated: "Deprecated since v1.2; use rawCode == $code instead".} = @@ -323,23 +323,23 @@ proc `==`*(rawCode: string, code: HttpCode): bool ## string form of itself. return cmpIgnoreCase(rawCode, $code) == 0 -proc is2xx*(code: HttpCode): bool = +func is2xx*(code: HttpCode): bool = ## Determines whether ``code`` is a 2xx HTTP status code. return code.int in {200 .. 299} -proc is3xx*(code: HttpCode): bool = +func is3xx*(code: HttpCode): bool = ## Determines whether ``code`` is a 3xx HTTP status code. return code.int in {300 .. 399} -proc is4xx*(code: HttpCode): bool = +func is4xx*(code: HttpCode): bool = ## Determines whether ``code`` is a 4xx HTTP status code. return code.int in {400 .. 499} -proc is5xx*(code: HttpCode): bool = +func is5xx*(code: HttpCode): bool = ## Determines whether ``code`` is a 5xx HTTP status code. return code.int in {500 .. 599} -proc `$`*(httpMethod: HttpMethod): string = +func `$`*(httpMethod: HttpMethod): string = return (system.`$`(httpMethod))[4 .. ^1].toUpperAscii() when isMainModule: From f785174c6009969ab853e0e2ce28f68c8b961776 Mon Sep 17 00:00:00 2001 From: Euan Date: Fri, 2 Oct 2020 10:17:31 +0100 Subject: [PATCH 0697/3768] Ref #14094 - disable hot code reloading tests on NetBSD (#15458) --- tests/dll/nimhcr_integration.nim | 1 + tests/dll/nimhcr_unit.nim | 1 + 2 files changed, 2 insertions(+) diff --git a/tests/dll/nimhcr_integration.nim b/tests/dll/nimhcr_integration.nim index f6c6d21bc..626502364 100644 --- a/tests/dll/nimhcr_integration.nim +++ b/tests/dll/nimhcr_integration.nim @@ -1,5 +1,6 @@ discard """ disabled: "openbsd" + disabled: "netbsd" disabled: "macosx" output: ''' main: HELLO! diff --git a/tests/dll/nimhcr_unit.nim b/tests/dll/nimhcr_unit.nim index 31f398258..0b924bdf7 100644 --- a/tests/dll/nimhcr_unit.nim +++ b/tests/dll/nimhcr_unit.nim @@ -1,5 +1,6 @@ discard """ disabled: "openbsd" +disabled: "netbsd" output: ''' fastcall_proc implementation #1 10 11 From aa1d7fe1e9d9c63691b46d7d1c4c37e2c814bfc5 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 2 Oct 2020 12:38:16 +0200 Subject: [PATCH 0698/3768] closureiters: fixes #15243 (#15454) [backport:1.2] * fixes #15243 [backport:1.2] --- compiler/ccgexprs.nim | 18 +++- compiler/closureiters.nim | 105 ++++++++++++++++++++++- compiler/nim.cfg | 7 ++ lib/pure/xmltree.nim | 4 +- tests/async/tasyncawait_cyclebreaker.nim | 66 -------------- tests/async/tbreak_must_exec_finally.nim | 25 ++++++ 6 files changed, 153 insertions(+), 72 deletions(-) delete mode 100644 tests/async/tasyncawait_cyclebreaker.nim create mode 100644 tests/async/tbreak_must_exec_finally.nim diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 49b1f3d16..b58519614 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -3050,7 +3050,23 @@ proc genConstSeqV2(p: BProc, n: PNode, t: PType; isConst: bool): Rope = proc genBracedInit(p: BProc, n: PNode; isConst: bool): Rope = case n.kind of nkHiddenStdConv, nkHiddenSubConv: - result = genBracedInit(p, n[1], isConst) + when false: + # XXX The frontend doesn't keep conversions to openArray for us. :-( + # We need to change 'transformConv' first, but that is hard. + if n.typ.kind == tyOpenArray: + assert n[1].kind == nkBracket + let data = genBracedInit(p, n[1], isConst) + + let payload = getTempName(p.module) + let ctype = getTypeDesc(p.module, n.typ.skipTypes(abstractInst)[0]) + let arrLen = n[1].len + appcg(p.module, cfsData, + "static $5 $1 $3[$2] = $4;$n", [ + ctype, arrLen, payload, data, + if isConst: "const" else: ""]) + result = "{($1*)&$2, $3}" % [ctype, payload, rope arrLen] + else: + result = genBracedInit(p, n[1], isConst) else: var ty = tyNone if n.typ == nil: diff --git a/compiler/closureiters.nim b/compiler/closureiters.nim index 912172e62..567bb3b91 100644 --- a/compiler/closureiters.nim +++ b/compiler/closureiters.nim @@ -130,7 +130,8 @@ import ast, msgs, idents, - renderer, magicsys, lowerings, lambdalifting, modulegraphs, lineinfos + renderer, magicsys, lowerings, lambdalifting, modulegraphs, lineinfos, + tables, options type Ctx = object @@ -1103,7 +1104,7 @@ proc skipEmptyStates(ctx: Ctx, stateIdx: int): int = result = ctx.states[stateIdx][0].intVal.int -proc skipThroughEmptyStates(ctx: var Ctx, n: PNode): PNode = +proc skipThroughEmptyStates(ctx: var Ctx, n: PNode): PNode= result = n case n.kind of nkSkip: @@ -1283,6 +1284,101 @@ proc deleteEmptyStates(ctx: var Ctx) = else: inc i +type + PreprocessContext = object + finallys: seq[PNode] + config: ConfigRef + blocks: seq[(PNode, int)] + FreshVarsContext = object + tab: Table[int, PSym] + config: ConfigRef + info: TLineInfo + +proc freshVars(n: PNode; c: var FreshVarsContext): PNode = + case n.kind + of nkSym: + let x = c.tab.getOrDefault(n.sym.id) + if x == nil: + result = n + else: + result = newSymNode(x, n.info) + of nkSkip - {nkSym}: + result = n + of nkLetSection, nkVarSection: + result = copyNode(n) + for it in n: + if it.kind in {nkIdentDefs, nkVarTuple}: + let idefs = copyNode(it) + for v in 0..it.len-3: + if it[v].kind == nkSym: + let x = copySym(it[v].sym) + c.tab[it[v].sym.id] = x + idefs.add newSymNode(x) + else: + idefs.add it[v] + + for rest in it.len-2 ..< it.len: idefs.add it[rest] + result.add idefs + else: + result.add it + of nkRaiseStmt: + localError(c.config, c.info, "unsupported control flow: 'finally: ... raise' duplicated because of 'break'") + else: + result = n + for i in 0..= 0: + result = newNodeI(nkStmtList, n.info) + for i in countdown(c.finallys.high, fin): + var vars = FreshVarsContext(tab: initTable[int, PSym](), config: c.config, info: n.info) + result.add freshVars(preprocess(c, c.finallys[i]), vars) + result.add n + of nkSkip: discard + else: + for i in 0 ..< n.len: + result[i] = preprocess(c, n[i]) + proc transformClosureIterator*(g: ModuleGraph; fn: PSym, n: PNode): PNode = var ctx: Ctx ctx.g = g @@ -1295,7 +1391,10 @@ proc transformClosureIterator*(g: ModuleGraph; fn: PSym, n: PNode): PNode = ctx.stateVarSym = newSym(skVar, getIdent(ctx.g.cache, ":state"), fn, fn.info) ctx.stateVarSym.typ = g.createClosureIterStateType(fn) ctx.stateLoopLabel = newSym(skLabel, getIdent(ctx.g.cache, ":stateLoop"), fn, fn.info) - var n = n.toStmtList + var pc = PreprocessContext(finallys: @[], config: g.config) + var n = preprocess(pc, n.toStmtList) + #echo "transformed into ", n + #var n = n.toStmtList discard ctx.newState(n, nil) let gotoOut = newTree(nkGotoState, g.newIntLit(n.info, -1)) diff --git a/compiler/nim.cfg b/compiler/nim.cfg index 8e733bdf8..33ddc6f0b 100644 --- a/compiler/nim.cfg +++ b/compiler/nim.cfg @@ -19,6 +19,13 @@ define:useStdoutAsStdmsg styleCheck:error @end +# die when the Nim compiler uses more than 4GB: +@if cpu32: + define:"nimMaxHeap=2000" +@else: + define:"nimMaxHeap=4000" +@end + #define:useNodeIds #gc:markAndSweep diff --git a/lib/pure/xmltree.nim b/lib/pure/xmltree.nim index 00e9a031b..df8db1bb7 100644 --- a/lib/pure/xmltree.nim +++ b/lib/pure/xmltree.nim @@ -45,7 +45,7 @@ type XmlNodeKind* = enum ## Different kinds of XML nodes. xnText, ## a text element - xnVerbatimText, ## + xnVerbatimText, ## xnElement, ## an element with 0 or more children xnCData, ## a CDATA node xnEntity, ## an entity (like ``&thing;``) @@ -103,7 +103,7 @@ proc newText*(text: string): XmlNode = result = newXmlNode(xnText) result.fText = text -proc newVerbatimText*(text: string): XmlNode {.since: (1, 3).} = +proc newVerbatimText*(text: string): XmlNode {.since: (1, 3).} = ## Creates a new ``XmlNode`` of kind ``xnVerbatimText`` with the text `text`. ## **Since**: Version 1.3. result = newXmlNode(xnVerbatimText) diff --git a/tests/async/tasyncawait_cyclebreaker.nim b/tests/async/tasyncawait_cyclebreaker.nim deleted file mode 100644 index 2229d99c0..000000000 --- a/tests/async/tasyncawait_cyclebreaker.nim +++ /dev/null @@ -1,66 +0,0 @@ -discard """ - output: "20000" - cmd: "nim c -d:nimTypeNames -d:nimCycleBreaker $file" -""" -import asyncdispatch, asyncnet, nativesockets, net, strutils -from stdtest/netutils import bindAvailablePort - -var msgCount = 0 - -const - swarmSize = 400 - messagesToSend = 50 - -var clientCount = 0 - -proc sendMessages(client: AsyncFD) {.async.} = - for i in 0 ..< messagesToSend: - await send(client, "Message " & $i & "\c\L") - -proc launchSwarm(port: Port) {.async.} = - for i in 0 ..< swarmSize: - var sock = createAsyncNativeSocket() - - await connect(sock, "localhost", port) - await sendMessages(sock) - closeSocket(sock) - -proc readMessages(client: AsyncFD) {.async.} = - # wrapping the AsyncFd into a AsyncSocket object - var sockObj = newAsyncSocket(client) - var (ipaddr, port) = sockObj.getPeerAddr() - doAssert ipaddr == "127.0.0.1" - (ipaddr, port) = sockObj.getLocalAddr() - doAssert ipaddr == "127.0.0.1" - while true: - var line = await recvLine(sockObj) - if line == "": - closeSocket(client) - clientCount.inc - break - else: - if line.startswith("Message "): - msgCount.inc - else: - doAssert false - -proc createServer(server: AsyncFD) {.async.} = - discard server.SocketHandle.listen() - while true: - asyncCheck readMessages(await accept(server)) - -let server = createAsyncNativeSocket() -let port = bindAvailablePort(server.SocketHandle) -asyncCheck createServer(server) -asyncCheck launchSwarm(port) -while true: - poll() - GC_collectZct() - - let (allocs, deallocs) = getMemCounters() - doAssert allocs < deallocs + 1000 - - if clientCount == swarmSize: break - -assert msgCount == swarmSize * messagesToSend -echo msgCount diff --git a/tests/async/tbreak_must_exec_finally.nim b/tests/async/tbreak_must_exec_finally.nim new file mode 100644 index 000000000..8780e6149 --- /dev/null +++ b/tests/async/tbreak_must_exec_finally.nim @@ -0,0 +1,25 @@ +discard """ + output: ''' +finally handler 8 +do not duplicate this one +''' +""" + +# bug #15243 + +import asyncdispatch + +proc f() {.async.} = + try: + while true: + try: + await sleepAsync(400) + break + finally: + var localHere = 8 + echo "finally handler ", localHere + finally: + echo "do not duplicate this one" + +when isMainModule: + waitFor f() From 1f4b9cebd4a073024f68e70156be1d8ceab7344f Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 2 Oct 2020 17:22:31 +0200 Subject: [PATCH 0699/3768] Added std/effecttraits.nim (#15462) --- changelog.md | 4 +++- compiler/vmops.nim | 9 +++++++++ lib/std/effecttraits.nim | 19 +++++++++++++++++++ tests/macros/tgetraiseslist.nim | 22 ++++++++++++++++++++++ 4 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 lib/std/effecttraits.nim create mode 100644 tests/macros/tgetraiseslist.nim diff --git a/changelog.md b/changelog.md index 3cdf138ad..219d085b1 100644 --- a/changelog.md +++ b/changelog.md @@ -292,7 +292,9 @@ proc mydiv(a, b): int {.raises: [].} = - `system.deepcopy` has to be enabled explicitly for `--gc:arc` and `--gc:orc` via `--deepcopy:on`. - +- Added a `std/effecttraits` module for introspection of the inferred `.raise` effects. + We hope this enables `async` macros that are precise about the possible exceptions that + can be raised. - Added `critbits.toCritBitTree`, similar to `tables.toTable`, creates a new `CritBitTree` with given arguments. diff --git a/compiler/vmops.nim b/compiler/vmops.nim index 298a02bb6..de6229ec7 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -261,3 +261,12 @@ proc registerAdditionalOps*(c: PCtx) = a.setResult osproc.execCmdEx(getString(a, 0), options).toLit registerCallback c, "stdlib.times.getTime", proc (a: VmArgs) {.nimcall.} = setResult(a, times.getTime().toLit) + + registerCallback c, "stdlib.effecttraits.getRaisesListImpl", proc (a: VmArgs) = + let fn = getNode(a, 0) + if fn.typ != nil and fn.typ.n != nil and fn.typ.n[0].len >= effectListLen and + fn.typ.n[0][exceptionEffects] != nil: + var list = newNodeI(nkBracket, fn.info) + for e in fn.typ.n[0][exceptionEffects]: + list.add opMapTypeInstToAst(c.cache, e.typ.skipTypes({tyRef}), e.info) + setResult(a, list) diff --git a/lib/std/effecttraits.nim b/lib/std/effecttraits.nim new file mode 100644 index 000000000..a5d799786 --- /dev/null +++ b/lib/std/effecttraits.nim @@ -0,0 +1,19 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2018 Nim contributors +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module provides access to the inferred .raises effects +## for Nim's macro system. + +import macros + +proc getRaisesListImpl(n: NimNode): NimNode = discard "see compiler/vmops.nim" + +proc getRaisesList*(call: NimNode): NimNode = + expectKind call, nnkCallKinds + result = getRaisesListImpl(call[0]) diff --git a/tests/macros/tgetraiseslist.nim b/tests/macros/tgetraiseslist.nim new file mode 100644 index 000000000..de7ba06c8 --- /dev/null +++ b/tests/macros/tgetraiseslist.nim @@ -0,0 +1,22 @@ +discard """ + nimout: '''##[ValueError, Gen[string]]##''' +""" + +import macros +import std / effecttraits + +type + Gen[T] = object of CatchableError + x: T + +macro m(call: typed): untyped = + echo "##", repr getRaisesList(call), "##" + result = call + +proc r(inp: int) = + if inp == 0: + raise newException(ValueError, "bah") + elif inp == 1: + raise newException(Gen[string], "bahB") + +m r(2) From 7d8c1be8fb1ba96dd16c40df990d4c2639515173 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 2 Oct 2020 18:26:52 +0200 Subject: [PATCH 0700/3768] parser hotfix: don't run into endless loops; regression (#15468) --- compiler/parser.nim | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/compiler/parser.nim b/compiler/parser.nim index 17dd092a4..1ec708057 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -529,15 +529,21 @@ proc semiStmtList(p: var Parser, result: PNode) = inc p.inSemiStmtList withInd(p): # Be lenient with the first stmt/expr - result.add if p.tok.tokType == tkIf: parseIfExpr(p, nkIfStmt) else: complexOrSimpleStmt(p) - while true: + let a = if p.tok.tokType == tkIf: parseIfExpr(p, nkIfStmt) else: complexOrSimpleStmt(p) + result.add a + + while p.tok.tokType != tkEof: if p.tok.tokType == tkSemiColon: getTok(p) if p.tok.tokType == tkParRi: break elif not (sameInd(p) or realInd(p)): parMessage(p, errInvalidIndentation) - result.add complexOrSimpleStmt(p) + let a = complexOrSimpleStmt(p) + if a.kind == nkEmpty: + parMessage(p, errExprExpected, p.tok) + else: + result.add a dec p.inSemiStmtList result.transitionSonsKind(nkStmtListExpr) @@ -2244,7 +2250,13 @@ proc parseStmt(p: var Parser): PNode = if p.tok.tokType in {tkElse, tkElif}: break # Allow this too, see tests/parser/tifexprs - result.add complexOrSimpleStmt(p) + let a = complexOrSimpleStmt(p) + if a.kind == nkEmpty and not p.hasProgress: + parMessage(p, errExprExpected, p.tok) + break + else: + result.add a + if not p.hasProgress and p.tok.tokType == tkEof: break else: # the case statement is only needed for better error messages: From ff70ff529d23d5ca6abe0e16d815172dd24a9dd1 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 2 Oct 2020 18:51:49 +0200 Subject: [PATCH 0701/3768] remove nim.cfg file change lefover [backport:1.2] (#15469) --- compiler/nim.cfg | 6 ------ 1 file changed, 6 deletions(-) diff --git a/compiler/nim.cfg b/compiler/nim.cfg index 33ddc6f0b..cb586a9cb 100644 --- a/compiler/nim.cfg +++ b/compiler/nim.cfg @@ -19,12 +19,6 @@ define:useStdoutAsStdmsg styleCheck:error @end -# die when the Nim compiler uses more than 4GB: -@if cpu32: - define:"nimMaxHeap=2000" -@else: - define:"nimMaxHeap=4000" -@end #define:useNodeIds #gc:markAndSweep From e3eae3f7c7e9db6962f30dcb84657f3269ff3476 Mon Sep 17 00:00:00 2001 From: alaviss Date: Fri, 2 Oct 2020 11:52:20 -0500 Subject: [PATCH 0702/3768] tools/deps: fix git dir check (#15470) On Windows, a successful call will have a trailing newline appended, so strip that away before doing any checks. --- tools/deps.nim | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tools/deps.nim b/tools/deps.nim index 394dc0d25..b238f12ed 100644 --- a/tools/deps.nim +++ b/tools/deps.nim @@ -1,4 +1,4 @@ -import os, uri, strformat, osproc +import os, uri, strformat, osproc, strutils proc exec(cmd: string) = echo "deps.cmd: " & cmd @@ -14,7 +14,11 @@ proc isGitRepo(dir: string): bool = # Using this, we can verify whether a folder is a git repository by checking # whether the command success and if the output is empty. let (output, status) = execEx fmt"git -C {quoteShell(dir)} rev-parse --show-cdup" - result = status == 0 and output == "" + # On Windows there will be a trailing newline on success, remove it. + # The value of a successful call typically won't have a whitespace (it's + # usually a series of ../), so we know that it's safe to unconditionally + # remove trailing whitespaces from the result. + result = status == 0 and output.strip() == "" const commitHead* = "HEAD" From 9828392df3ac05ead78e14bde7b5865309df8a95 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 2 Oct 2020 18:53:22 +0200 Subject: [PATCH 0703/3768] implements https://github.com/nim-lang/RFCs/issues/257 (#15466) --- compiler/parampatterns.nim | 2 ++ compiler/transf.nim | 7 ------- compiler/trees.nim | 7 +++++++ 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim index ed8f2c16a..c2be4b134 100644 --- a/compiler/parampatterns.nim +++ b/compiler/parampatterns.nim @@ -223,6 +223,8 @@ proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult result = arLValue elif isUnsafeAddr and n.sym.kind == skParam: result = arLValue + elif isUnsafeAddr and n.sym.kind == skConst and dontInlineConstant(n, n.sym.ast): + result = arLValue elif n.sym.kind in kinds: if owner != nil and owner == n.sym.owner and sfGlobal notin n.sym.flags: diff --git a/compiler/transf.nim b/compiler/transf.nim index 5dd275414..c8dbf5e04 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -850,13 +850,6 @@ proc transformExceptBranch(c: PTransf, n: PNode): PNode = else: result = transformSons(c, n) -proc dontInlineConstant(orig, cnst: PNode): bool {.inline.} = - # symbols that expand to a complex constant (array, etc.) should not be - # inlined, unless it's the empty array: - result = orig.kind == nkSym and - cnst.kind in {nkCurly, nkPar, nkTupleConstr, nkBracket} and - cnst.len != 0 - proc commonOptimizations*(g: ModuleGraph; c: PSym, n: PNode): PNode = result = n for i in 0.. Date: Fri, 2 Oct 2020 12:18:44 -0500 Subject: [PATCH 0704/3768] koch: remove c2nim from windows release builds (#15471) We don't ship this tool with any other OS, and the c2nim bundle is non-deterministic due to the lack of a pinned commit. --- koch.nim | 1 - 1 file changed, 1 deletion(-) diff --git a/koch.nim b/koch.nim index 714d70bfe..bbb37b702 100644 --- a/koch.nim +++ b/koch.nim @@ -168,7 +168,6 @@ proc bundleWinTools(args: string) = buildVccTool(args) nimCompile("tools/nimgrab.nim", options = "-d:ssl " & args) nimCompile("tools/nimgrep.nim", options = args) - bundleC2nim(args) nimCompile("testament/testament.nim", options = args) when false: # not yet a tool worth including From d48b356e493a7a7f6e2cf9855de893f811b2e9ad Mon Sep 17 00:00:00 2001 From: ee7 <45465154+ee7@users.noreply.github.com> Date: Fri, 2 Oct 2020 21:10:24 +0200 Subject: [PATCH 0705/3768] intsets.nim: Add `toIntSet` proc (#15460) Similar to: - `critbits.toCritBitTree` - `deques.toDeque` - `sets.toHashSet` - `tables.toTable` --- changelog.md | 5 +++++ lib/pure/collections/intsets.nim | 28 +++++++++++++++++++++++++--- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index 219d085b1..7d1217f97 100644 --- a/changelog.md +++ b/changelog.md @@ -201,6 +201,11 @@ - Add `readLines(p: Process)` to `osproc` module for `startProcess` convenience. +- Added `intsets.toIntSet`, which creates an IntSet from an openArray. The usage + is similar to procs such as `sets.toHashSet` and `tables.toTable`. Previously, + it was necessary to create an empty IntSet and add items manually. + + ## Language changes - The `=destroy` hook no longer has to reset its target, as the compiler now automatically inserts diff --git a/lib/pure/collections/intsets.nim b/lib/pure/collections/intsets.nim index eae3fa447..3be453e29 100644 --- a/lib/pure/collections/intsets.nim +++ b/lib/pure/collections/intsets.nim @@ -18,9 +18,8 @@ ## **See also:** ## * `sets module `_ for more general hash sets - -import - hashes +import std/private/since +import hashes type BitScalar = uint @@ -161,6 +160,9 @@ iterator items*(s: IntSet): int {.inline.} = proc initIntSet*: IntSet = ## Returns an empty IntSet. + ## + ## See also: + ## * `toIntSet proc <#toIntSet,openArray[int]>`_ runnableExamples: var a = initIntSet() assert len(a) == 0 @@ -251,6 +253,24 @@ proc incl*(s: var IntSet, other: IntSet) = for item in other: incl(s, item) +proc toIntSet*(x: openArray[int]): IntSet {.since: (1, 3).} = + ## Creates a new IntSet that contains the elements of `x`. + ## + ## Duplicates are removed. + ## + ## See also: + ## * `initIntSet proc <#initIntSet>`_ + runnableExamples: + var + a = toIntSet([5, 6, 7]) + b = toIntSet(@[1, 8, 8, 8]) + assert len(a) == 3 + assert len(b) == 2 + + result = initIntSet() + for item in items(x): + result.incl(item) + proc containsOrIncl*(s: var IntSet, key: int): bool = ## Includes `key` in the set `s` and tells if `key` was already in `s`. ## @@ -585,6 +605,8 @@ when isMainModule: x.incl(1044) x.excl(1044) + assert x == [1, 2, 7, 1056].toIntSet + assert x.containsOrIncl(888) == false assert 888 in x assert x.containsOrIncl(888) == true From 0a05176057dd0f98793b41e18f5599d662ab1672 Mon Sep 17 00:00:00 2001 From: ee7 <45465154+ee7@users.noreply.github.com> Date: Fri, 2 Oct 2020 22:01:03 +0200 Subject: [PATCH 0706/3768] heapqueue.nim: Add `toHeapQueue` proc (#15459) Similar to: - `critbits.toCritBitTree` - `deques.toDeque` - `sets.toHashSet` - `tables.toTable` Co-authored-by: Andreas Rumpf --- changelog.md | 4 ++++ lib/pure/collections/heapqueue.nim | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/changelog.md b/changelog.md index 7d1217f97..5a439285c 100644 --- a/changelog.md +++ b/changelog.md @@ -201,6 +201,10 @@ - Add `readLines(p: Process)` to `osproc` module for `startProcess` convenience. +- Added `heapqueue.toHeapQueue`, which creates a HeapQueue from an openArray. + The usage is similar to procs such as `sets.toHashSet` and `tables.toTable`. + Previously, it was necessary to create an empty HeapQueue and add items + manually. - Added `intsets.toIntSet`, which creates an IntSet from an openArray. The usage is similar to procs such as `sets.toHashSet` and `tables.toTable`. Previously, it was necessary to create an empty IntSet and add items manually. diff --git a/lib/pure/collections/heapqueue.nim b/lib/pure/collections/heapqueue.nim index c2952cf1b..ca0666651 100644 --- a/lib/pure/collections/heapqueue.nim +++ b/lib/pure/collections/heapqueue.nim @@ -58,6 +58,9 @@ type HeapQueue*[T] = object proc initHeapQueue*[T](): HeapQueue[T] = ## Create a new empty heap. + ## + ## See also: + ## * `toHeapQueue proc <#toHeapQueue,openArray[T]>`_ discard proc len*[T](heap: HeapQueue[T]): int {.inline.} = @@ -115,6 +118,20 @@ proc push*[T](heap: var HeapQueue[T], item: T) = heap.data.add(item) siftdown(heap, 0, len(heap)-1) +proc toHeapQueue*[T](x: openArray[T]): HeapQueue[T] {.since: (1, 3).} = + ## Creates a new HeapQueue that contains the elements of `x`. + ## + ## See also: + ## * `initHeapQueue proc <#initHeapQueue>`_ + runnableExamples: + var heap = toHeapQueue([9, 5, 8]) + assert heap.pop() == 5 + assert heap[0] == 8 + + result = initHeapQueue[T]() + for item in items(x): + result.push(item) + proc pop*[T](heap: var HeapQueue[T]): T = ## Pop and return the smallest item from `heap`, ## maintaining the heap invariant. @@ -195,6 +212,7 @@ when isMainModule: let data = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0] for item in data: push(heap, item) + doAssert(heap == data.toHeapQueue) doAssert(heap[0] == 0) doAssert(heap.toSortedSeq == @[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) From dcd0ce17759609084951e4d3ac7f24b3eec62a26 Mon Sep 17 00:00:00 2001 From: alaviss Date: Fri, 2 Oct 2020 15:48:35 -0500 Subject: [PATCH 0707/3768] niminst: restore ZIP building functionality (#15472) This is necessary for building Windows releases, was erroneously removed by #15451. --- tools/niminst/niminst.nim | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/niminst/niminst.nim b/tools/niminst/niminst.nim index 2b6172390..a0a0d9ad4 100644 --- a/tools/niminst/niminst.nim +++ b/tools/niminst/niminst.nim @@ -30,6 +30,7 @@ type actionInno, # action: create Inno Setup installer actionNsis, # action: create NSIS installer actionScripts # action: create install and deinstall scripts + actionZip # action: create zip file actionXz, # action: create xz file actionDeb # action: prepare deb package @@ -139,6 +140,7 @@ Usage: Command: csource build C source code for source based installations scripts build install and deinstall scripts + zip build the ZIP file inno build the Inno Setup installer nsis build the NSIS Setup installer deb create files for debhelper @@ -167,6 +169,7 @@ proc parseCmdLine(c: var ConfigData) = case a of "csource": incl(c.actions, actionCSource) of "scripts": incl(c.actions, actionScripts) + of "zip": incl(c.actions, actionZip) of "xz": incl(c.actions, actionXz) of "inno": incl(c.actions, actionInno) of "nsis": incl(c.actions, actionNsis) @@ -721,6 +724,8 @@ proc main() = srcdist(c) if actionScripts in c.actions: writeInstallScripts(c) + if actionZip in c.actions: + xzDist(c, true) if actionXz in c.actions: xzDist(c) if actionDeb in c.actions: From 662c5080755eb5a42df2c3acb84c044876571a46 Mon Sep 17 00:00:00 2001 From: supakeen <39536295+supakeen@users.noreply.github.com> Date: Sat, 3 Oct 2020 13:43:43 +0200 Subject: [PATCH 0708/3768] Return type of asyncftpclient to fix example. (#15476) Related to: #13641 --- lib/pure/asyncftpclient.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/asyncftpclient.nim b/lib/pure/asyncftpclient.nim index 208782c05..ba565059d 100644 --- a/lib/pure/asyncftpclient.nim +++ b/lib/pure/asyncftpclient.nim @@ -63,7 +63,7 @@ ## import asyncdispatch, asyncftpclient ## ## proc onProgressChanged(total, progress: BiggestInt, -## speed: float): Future[void] = +## speed: float) {.async.} = ## echo("Uploaded ", progress, " of ", total, " bytes") ## echo("Current speed: ", speed, " kb/s") ## From 2288188fe9a9d854a167dd270eb8d72d19676865 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 3 Oct 2020 17:34:42 +0200 Subject: [PATCH 0709/3768] fixes https://github.com/nim-lang/RFCs/issues/257 [backport:1.2] (#15479) --- compiler/trees.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/trees.nim b/compiler/trees.nim index 2cf6b4f67..c5e2cb7b4 100644 --- a/compiler/trees.nim +++ b/compiler/trees.nim @@ -200,5 +200,5 @@ proc dontInlineConstant*(orig, cnst: PNode): bool {.inline.} = # symbols that expand to a complex constant (array, etc.) should not be # inlined, unless it's the empty array: result = orig.kind == nkSym and - cnst.kind in {nkCurly, nkPar, nkTupleConstr, nkBracket} and - cnst.len != 0 + cnst.kind in {nkCurly, nkPar, nkTupleConstr, nkBracket, nkObjConstr} and + cnst.len > ord(cnst.kind == nkObjConstr) From fc973b2c0cf4fb550556ef05968e952ca174956f Mon Sep 17 00:00:00 2001 From: alaviss Date: Sat, 3 Oct 2020 17:39:56 -0500 Subject: [PATCH 0710/3768] renderer: use the biggest integer type for masking literals (#15482) On 32-bit system the mask would have a size of 32-bit, which is smaller than the BiggestInt (usually 64-bit) it was masked against. For some reason this only affect 32-bit Windows but not 32-bit Linux. Might just be a difference in how gcc handle out of bound shifts for Windows and Linux. --- compiler/renderer.nim | 2 +- tests/macros/tmacros_issues.nim | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 9c7609f9a..88b9adf27 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -336,7 +336,7 @@ proc litAux(g: TSrcGen; n: PNode, x: BiggestInt, size: int): string = if nfBase2 in n.flags: result = "0b" & toBin(x, size * 8) elif nfBase8 in n.flags: - var y = if size < sizeof(BiggestInt): x and ((1 shl (size*8)) - 1) + var y = if size < sizeof(BiggestInt): x and ((1.BiggestInt shl (size*8)) - 1) else: x result = "0o" & toOct(y, size * 3) elif nfBase16 in n.flags: result = "0x" & toHex(x, size * 2) diff --git a/tests/macros/tmacros_issues.nim b/tests/macros/tmacros_issues.nim index 3d992a27f..19c706a82 100644 --- a/tests/macros/tmacros_issues.nim +++ b/tests/macros/tmacros_issues.nim @@ -29,6 +29,7 @@ array[0 .. 100, int] 10 test 0o377'i8 +0o000000000755'i32 1 2 3 @@ -257,6 +258,7 @@ macro toRendererBug(n): untyped = result = newLit repr(n) echo toRendererBug(0o377'i8) +echo toRendererBug(0o755'i32) # bug #12129 macro foobar() = From ccd1f5818334c9e6d2bfca2fc60fed7656ac2b66 Mon Sep 17 00:00:00 2001 From: Abhishek Dubey Date: Sun, 4 Oct 2020 14:28:19 +0530 Subject: [PATCH 0711/3768] Installation Instruction (#15485) The instruction command was missing `install` for Ubuntu. --- lib/pure/distros.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pure/distros.nim b/lib/pure/distros.nim index ae2248b40..8debbf9c8 100644 --- a/lib/pure/distros.nim +++ b/lib/pure/distros.nim @@ -13,8 +13,8 @@ ## ## To complete the installation, run: ## -## sudo apt-get libblas-dev -## sudo apt-get libvoodoo +## sudo apt-get install libblas-dev +## sudo apt-get install libvoodoo ## ## The above output could be the result of a code snippet like: ## From bbe2eb20dff980c55f9c480158ceb90046935c34 Mon Sep 17 00:00:00 2001 From: supakeen <39536295+supakeen@users.noreply.github.com> Date: Sun, 4 Oct 2020 12:32:07 +0200 Subject: [PATCH 0712/3768] Provide a configurable progressInterval. (#15477) --- changelog.md | 3 +++ lib/pure/asyncftpclient.nim | 17 +++++++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/changelog.md b/changelog.md index 5a439285c..e905dd941 100644 --- a/changelog.md +++ b/changelog.md @@ -209,6 +209,9 @@ is similar to procs such as `sets.toHashSet` and `tables.toTable`. Previously, it was necessary to create an empty IntSet and add items manually. +- Added `progressInterval` argument to `asyncftpclient.newAsyncFtpClient` to control the interval + at which progress callbacks are called. + ## Language changes diff --git a/lib/pure/asyncftpclient.nim b/lib/pure/asyncftpclient.nim index ba565059d..b538e10c9 100644 --- a/lib/pure/asyncftpclient.nim +++ b/lib/pure/asyncftpclient.nim @@ -59,6 +59,9 @@ ## by specifying a ``onProgressChanged`` procedure to the ``store`` or ## ``retrFile`` procedures. ## +## Procs that take an ``onProgressChanged`` callback will call this every +## ``progressInterval`` milliseconds. +## ## .. code-block::nim ## import asyncdispatch, asyncftpclient ## @@ -68,7 +71,7 @@ ## echo("Current speed: ", speed, " kb/s") ## ## proc main() {.async.} = -## var ftp = newAsyncFtpClient("example.com", user = "test", pass = "test") +## var ftp = newAsyncFtpClient("example.com", user = "test", pass = "test", progressInterval = 500) ## await ftp.connect() ## await ftp.store("file.txt", "/home/user/file.txt", onProgressChanged) ## echo("File finished uploading") @@ -85,6 +88,7 @@ type user*, pass*: string address*: string port*: Port + progressInterval: int jobInProgress*: bool job*: FtpJob dsockConnected*: bool @@ -303,7 +307,7 @@ proc getFile(ftp: AsyncFtpClient, file: File, total: BiggestInt, assert ftp.dsockConnected var progress = 0 var progressInSecond = 0 - var countdownFut = sleepAsync(1000) + var countdownFut = sleepAsync(ftp.progressInterval) var dataFut = ftp.dsock.recv(BufferSize) while ftp.dsockConnected: await dataFut or countdownFut @@ -311,7 +315,7 @@ proc getFile(ftp: AsyncFtpClient, file: File, total: BiggestInt, asyncCheck onProgressChanged(total, progress, progressInSecond.float) progressInSecond = 0 - countdownFut = sleepAsync(1000) + countdownFut = sleepAsync(ftp.progressInterval) if dataFut.finished: let data = dataFut.read @@ -359,7 +363,7 @@ proc doUpload(ftp: AsyncFtpClient, file: File, var data = newString(4000) var progress = 0 var progressInSecond = 0 - var countdownFut = sleepAsync(1000) + var countdownFut = sleepAsync(ftp.progressInterval) var sendFut: Future[void] = nil while ftp.dsockConnected: if sendFut == nil or sendFut.finished: @@ -380,7 +384,7 @@ proc doUpload(ftp: AsyncFtpClient, file: File, if countdownFut.finished: asyncCheck onProgressChanged(total, progress, progressInSecond.float) progressInSecond = 0 - countdownFut = sleepAsync(1000) + countdownFut = sleepAsync(ftp.progressInterval) await countdownFut or sendFut @@ -415,13 +419,14 @@ proc removeDir*(ftp: AsyncFtpClient, dir: string) {.async.} = assertReply(await ftp.send("RMD " & dir), "250") proc newAsyncFtpClient*(address: string, port = Port(21), - user, pass = ""): AsyncFtpClient = + user, pass = "", progressInterval: int = 1000): AsyncFtpClient = ## Creates a new ``AsyncFtpClient`` object. new result result.user = user result.pass = pass result.address = address result.port = port + result.progressInterval = progressInterval result.dsockConnected = false result.csock = newAsyncSocket() From b760fdc9ca858dd146a34da86eabbc3d43fb0833 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 5 Oct 2020 15:57:23 +0800 Subject: [PATCH 0713/3768] make testing for prologue more stable (#15491) --- testament/important_packages.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index b4cc25f2a..e8d136151 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -98,7 +98,7 @@ pkg2 "patty" pkg2 "plotly", true, "nim c examples/all.nim" pkg2 "pnm" pkg2 "polypbren" -pkg2 "prologue", true, "nim c -d:release -r tests/test_compile/test_compile.nim" +pkg2 "prologue", true, "nimble tcompile" pkg2 "protobuf", true, "nim c -o:protobuff -r src/protobuf.nim" pkg2 "pylib" pkg2 "rbtree" From cc4c546f8f0846c44b295c9417c2fd98cb694ae8 Mon Sep 17 00:00:00 2001 From: Euan Date: Mon, 5 Oct 2020 11:00:06 +0100 Subject: [PATCH 0714/3768] Fix #15493 - disable TLS emulation for NetBSD (#15494) TLS emulation was already disabled for FreeBSD due to the same issue. --- tests/stdlib/tssl.nims | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/stdlib/tssl.nims b/tests/stdlib/tssl.nims index 3f24dc325..4739e7f07 100644 --- a/tests/stdlib/tssl.nims +++ b/tests/stdlib/tssl.nims @@ -1,5 +1,5 @@ --threads:on --d:ssl -when defined(freebsd): - # See https://github.com/nim-lang/Nim/pull/15066#issuecomment-665541265 +when defined(freebsd) or defined(netbsd): + # See https://github.com/nim-lang/Nim/pull/15066#issuecomment-665541265 and https://github.com/nim-lang/Nim/issues/15493 --tlsEmulation:off From 5967b6f60f669f343ed089a4cd09f324cbfc5c69 Mon Sep 17 00:00:00 2001 From: Luis Felipe Manfroni <33612042+luismanfroni@users.noreply.github.com> Date: Mon, 5 Oct 2020 07:02:08 -0300 Subject: [PATCH 0715/3768] doc(sugar): added description and examples to dup (#15455) --- lib/pure/sugar.nim | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/lib/pure/sugar.nim b/lib/pure/sugar.nim index 49934b9bb..be5e1f326 100644 --- a/lib/pure/sugar.nim +++ b/lib/pure/sugar.nim @@ -217,7 +217,10 @@ since (1, 1): macro dup*[T](arg: T, calls: varargs[untyped]): T = ## Turns an `in-place`:idx: algorithm into one that works on - ## a copy and returns this copy. + ## a copy and returns this copy, without modifying its input. + ## + ## This macro also allows for (otherwise in-place) function chaining. + ## ## **Since**: Version 1.2. runnableExamples: import algorithm @@ -234,6 +237,21 @@ since (1, 1): var s2 = "xyz" doAssert s1 & s2 == s1.dup(&= s2) + proc makePalindrome(s: var string) = + for i in countdown(s.len-2, 0): + s.add(s[i]) + + var c = "xyz" + + # An underscore (_) can be used to denote the place of the argument you're passing: + # b = "xyz" + var d = dup c: + makePalindrome # xyzyx + sort(_, SortOrder.Descending) # zyyxx + makePalindrome # zyyxxxyyz + + doAssert d == "zyyxxxyyz" + result = newNimNode(nnkStmtListExpr, arg) let tmp = genSym(nskVar, "dupResult") result.add newVarStmt(tmp, arg) From 112b11c154af1f56b8c842b5723e6097d40d9e60 Mon Sep 17 00:00:00 2001 From: Miran Date: Mon, 5 Oct 2020 17:03:14 +0200 Subject: [PATCH 0716/3768] group procs of the same name in TOC (#15487) * group procs of the same name in TOC * correctly show `sink` parameters in TOC * no need to reinvent the wheel - `mgetorPut` exists * better setting of text color [ci skip] * fix CSS for better alignment --- compiler/docgen.nim | 33 ++- compiler/docgen2.nim | 3 +- compiler/typesrenderer.nim | 5 + config/nimdoc.cfg | 14 ++ doc/nimdoc.css | 20 +- .../expected/index.html | 5 +- .../expected/subdir/subdir_b/utils.html | 5 +- nimdoc/testproject/expected/testproject.html | 208 +++++++++++++----- 8 files changed, 220 insertions(+), 73 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index c2ce12e50..fb15f3826 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -37,7 +37,8 @@ type modDesc: Rope # module description module: PSym modDeprecationMsg: Rope - toc, section: TSections + toc, toc2, section: TSections + tocTable: array[TSymKind, Table[string, Rope]] indexValFilename: string analytics: string # Google Analytics javascript, "" if doesn't exist seenSymbols: StringTableRef # avoids duplicate symbol generation for HTML. @@ -871,6 +872,14 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags) = itemIDRope, plainNameRope, plainSymbolRope, symbolOrIdRope, plainSymbolEncRope, symbolOrIdEncRope, attype])) + d.tocTable[k].mgetOrPut(cleanPlainSymbol, nil).add(ropeFormatNamedVars( + d.conf, getConfigVar(d.conf, "doc.item.tocTable"), + ["name", "header", "desc", "itemID", "header_plain", "itemSym", + "itemSymOrID", "itemSymEnc", "itemSymOrIDEnc", "attype"], + [rope(getName(d, nameNode, d.splitAfter)), result, comm, + itemIDRope, plainNameRope, plainSymbolRope, + symbolOrIdRope, plainSymbolEncRope, symbolOrIdEncRope, attype])) + # Ironically for types the complexSymbol is *cleaner* than the plainName # because it doesn't include object fields or documentation comments. So we # use the plain one for callable elements, and the complex for the rest. @@ -1167,7 +1176,7 @@ proc generateTags*(d: PDoc, n: PNode, r: var Rope) = generateTags(d, lastSon(n[0]), r) else: discard -proc genSection(d: PDoc, kind: TSymKind) = +proc genSection(d: PDoc, kind: TSymKind, groupedToc = false) = const sectionNames: array[skModule..skField, string] = [ "Imports", "Types", "Vars", "Lets", "Consts", "Vars", "Procs", "Funcs", "Methods", "Iterators", "Converters", "Macros", "Templates", "Exports" @@ -1177,14 +1186,23 @@ proc genSection(d: PDoc, kind: TSymKind) = d.section[kind] = ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.section"), [ "sectionid", "sectionTitle", "sectionTitleID", "content"], [ ord(kind).rope, title, rope(ord(kind) + 50), d.section[kind]]) + + var tocSource = d.toc + if groupedToc: + for p in d.tocTable[kind].keys: + d.toc2[kind].add ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.section.toc2"), [ + "sectionid", "sectionTitle", "sectionTitleID", "content", "plainName"], [ + ord(kind).rope, title, rope(ord(kind) + 50), d.tocTable[kind][p], p.rope]) + tocSource = d.toc2 + d.toc[kind] = ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.section.toc"), [ "sectionid", "sectionTitle", "sectionTitleID", "content"], [ - ord(kind).rope, title, rope(ord(kind) + 50), d.toc[kind]]) + ord(kind).rope, title, rope(ord(kind) + 50), tocSource[kind]]) proc relLink(outDir: AbsoluteDir, destFile: AbsoluteFile, linkto: RelativeFile): Rope = rope($relativeTo(outDir / linkto, destFile.splitFile().dir, '/')) -proc genOutFile(d: PDoc): Rope = +proc genOutFile(d: PDoc, groupedToc = false): Rope = var code, content: Rope title = "" @@ -1193,7 +1211,8 @@ proc genOutFile(d: PDoc): Rope = renderTocEntries(d[], j, 1, tmp) var toc = tmp.rope for i in TSymKind: - genSection(d, i) + var shouldSort = i in {skProc, skFunc} and groupedToc + genSection(d, i, shouldSort) toc.add(d.toc[i]) if toc != nil: toc = ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.toc"), ["content"], [toc]) @@ -1246,9 +1265,9 @@ proc updateOutfile(d: PDoc, outfile: AbsoluteFile) = if isAbsolute(d.conf.outFile.string): d.conf.outFile = splitPath(d.conf.outFile.string)[1].RelativeFile -proc writeOutput*(d: PDoc, useWarning = false) = +proc writeOutput*(d: PDoc, useWarning = false, groupedToc = false) = runAllExamples(d) - var content = genOutFile(d) + var content = genOutFile(d, groupedToc) if optStdout in d.conf.globalOptions: writeRope(stdout, content) else: diff --git a/compiler/docgen2.nim b/compiler/docgen2.nim index 438417ca0..9cb40a7fc 100644 --- a/compiler/docgen2.nim +++ b/compiler/docgen2.nim @@ -29,6 +29,7 @@ proc shouldProcess(g: PGen): bool = template closeImpl(body: untyped) {.dirty.} = var g = PGen(p) let useWarning = sfMainModule notin g.module.flags + let groupedToc = true if shouldProcess(g): body try: @@ -38,7 +39,7 @@ template closeImpl(body: untyped) {.dirty.} = proc close(graph: ModuleGraph; p: PPassContext, n: PNode): PNode = closeImpl: - writeOutput(g.doc, useWarning) + writeOutput(g.doc, useWarning, groupedToc) proc closeJson(graph: ModuleGraph; p: PPassContext, n: PNode): PNode = closeImpl: diff --git a/compiler/typesrenderer.nim b/compiler/typesrenderer.nim index 050a057f4..613173252 100644 --- a/compiler/typesrenderer.nim +++ b/compiler/typesrenderer.nim @@ -78,6 +78,11 @@ proc renderType(n: PNode): string = result = renderType(n[0]) & '[' for i in 1.. 1: result.add ", " + result.add(renderType(n[i])) else: result = "" diff --git a/config/nimdoc.cfg b/config/nimdoc.cfg index 9a56fdaae..a6a402bab 100644 --- a/config/nimdoc.cfg +++ b/config/nimdoc.cfg @@ -24,6 +24,12 @@ doc.section.toc = """ """ +doc.section.toc2 = """ +

    -
    proc c_printf(frmt: cstring): cint {...}{.importc: "printf", header: "<stdio.h>", varargs,
    -                                 discardable.}
    +
    proc c_printf(frmt: cstring): cint {...}{.importc: "printf", header: "<stdio.h>",
    +                                     varargs, discardable.}
    the c printf. etc.
    -
    proc c_nonexistant(frmt: cstring): cint {...}{.importc: "nonexistant", header: "<stdio.h>",
    -                                      varargs, discardable.}
    +
    proc c_nonexistant(frmt: cstring): cint {...}{.importc: "nonexistant",
    +    header: "<stdio.h>", varargs, discardable.}
    @@ -744,7 +744,8 @@ at indent 0
    -
    proc asyncFun1(): Future[int] {...}{.raises: [Exception, ValueError], tags: [RootEffect].}
    +
    proc asyncFun1(): Future[int] {...}{.raises: [Exception, ValueError],
    +                                tags: [RootEffect].}
    ok1 diff --git a/tests/errmsgs/tunknown_named_parameter.nim b/tests/errmsgs/tunknown_named_parameter.nim index b139310db..829ab9497 100644 --- a/tests/errmsgs/tunknown_named_parameter.nim +++ b/tests/errmsgs/tunknown_named_parameter.nim @@ -10,7 +10,8 @@ proc rsplit(s: string; sep: string; maxsplit: int = -1): seq[string] first type mismatch at position: 2 required type for sep: string but expression '{':'}' is of type: set[char] -proc rsplit(s: string; seps: set[char] = Whitespace; maxsplit: int = -1): seq[string] +proc rsplit(s: string; seps: set[char] = Whitespace; maxsplit: int = -1): seq[ + string] first type mismatch at position: 3 unknown named parameter: maxsplits @@ -22,6 +23,5 @@ expression: rsplit("abc:def", {':'}, maxsplits = 1) # bug #8043 - import strutils "abc:def".rsplit({':'}, maxsplits = 1) From fe187719abf5fa87e12e4092f87c270234fad25c Mon Sep 17 00:00:00 2001 From: cooldome Date: Thu, 8 Oct 2020 08:12:03 +0100 Subject: [PATCH 0734/3768] fix #15516 (#15518) --- compiler/sizealignoffsetimpl.nim | 10 ++++++---- tests/misc/tsizeof.nim | 18 +++++++++++++++++- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/compiler/sizealignoffsetimpl.nim b/compiler/sizealignoffsetimpl.nim index baa2c40f9..be35247f7 100644 --- a/compiler/sizealignoffsetimpl.nim +++ b/compiler/sizealignoffsetimpl.nim @@ -138,12 +138,13 @@ proc computeObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode, packed: bool, a accum.offset = szUnknownSize accum.maxAlign = szUnknownSize else: - # the union neds to be aligned first, before the offsets can be assigned + # the union needs to be aligned first, before the offsets can be assigned accum.align(maxChildAlign) let accumRoot = accum # copy, because each branch should start af the same offset for i in 1.. 10 - testSizeAlignOf(t,a,b,c,d,e,f,g,ro,go,po, e1, e2, e4, e8, eoa, eob, capo) + testSizeAlignOf(t,a,b,c,d,e,f,g,ro,go,po, e1, e2, e4, e8, eoa, eob, capo, issue15516) type WithBitsize {.objectconfig.} = object From 0ac74ddd749ddde26dcc76deebedbd5459a168eb Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 8 Oct 2020 10:28:27 +0200 Subject: [PATCH 0735/3768] fixes a regression --- compiler/lexer.nim | 1 + compiler/renderer.nim | 28 ++++++++++++++++++---------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/compiler/lexer.nim b/compiler/lexer.nim index ff433928c..0cf00e612 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -62,6 +62,7 @@ type tkComma, tkSemiColon, tkColon, tkColonColon, tkEquals, tkDot, tkDotDot, tkBracketLeColon, tkOpr, tkComment, tkAccent, + # these are fake tokens used by renderer.nim tkSpaces, tkInfixOpr, tkPrefixOpr, tkPostfixOpr TokTypes* = set[TokType] diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 1de56a296..b897aff5d 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -32,6 +32,7 @@ type TSrcGen* = object indent*: int lineLen*: int + col: int pos*: int # current position for iteration over the buffer idx*: int # current token index for iteration over the buffer tokens*: TRenderTokSeq @@ -119,11 +120,10 @@ proc initSrcGen(g: var TSrcGen, renderFlags: TRenderFlags; config: ConfigRef) = g.config = config proc addTok(g: var TSrcGen, kind: TokType, s: string; sym: PSym = nil) = - setLen(g.tokens, g.tokens.len + 1) - g.tokens[^1].kind = kind - g.tokens[^1].length = int16(s.len) - g.tokens[^1].sym = sym + g.tokens.add TRenderTok(kind: kind, length: int16(s.len), sym: sym) g.buf.add(s) + if kind != tkSpaces: + inc g.col, s.len proc addPendingNL(g: var TSrcGen) = if g.pendingNL >= 0: @@ -133,6 +133,7 @@ proc addPendingNL(g: var TSrcGen) = const newlines = "\n" addTok(g, tkSpaces, newlines & spaces(g.pendingNL)) g.lineLen = g.pendingNL + g.col = g.pendingNL g.pendingNL = - 1 g.pendingWhitespace = -1 elif g.pendingWhitespace >= 0: @@ -141,7 +142,10 @@ proc addPendingNL(g: var TSrcGen) = proc putNL(g: var TSrcGen, indent: int) = if g.pendingNL >= 0: addPendingNL(g) - else: addTok(g, tkSpaces, "\n") + else: + addTok(g, tkSpaces, "\n") + g.col = 0 + g.pendingNL = indent g.lineLen = indent g.pendingWhitespace = -1 @@ -185,14 +189,15 @@ proc put(g: var TSrcGen, kind: TokType, s: string; sym: PSym = nil) = addTok(g, kind, s, sym) else: g.pendingWhitespace = s.len + inc g.col, s.len inc(g.lineLen, s.len) proc putComment(g: var TSrcGen, s: string) = if s.len == 0: return var i = 0 let hi = s.len - 1 - var isCode = (s.len >= 2) and (s[1] != ' ') - var ind = g.lineLen + let isCode = (s.len >= 2) and (s[1] != ' ') + let ind = g.col var com = "## " while i <= hi: case s[i] @@ -218,7 +223,7 @@ proc putComment(g: var TSrcGen, s: string) = # compute length of the following word: var j = i while j <= hi and s[j] > ' ': inc(j) - if not isCode and (g.lineLen + (j - i) > MaxLineLen): + if not isCode and (g.col + (j - i) > MaxLineLen): put(g, tkComment, com) optNL(g, ind) com = "## " @@ -300,15 +305,18 @@ proc shouldRenderComment(g: var TSrcGen, n: PNode): bool = proc gcom(g: var TSrcGen, n: PNode) = assert(n != nil) if shouldRenderComment(g, n): + var oneSpaceAdded = 0 if (g.pendingNL < 0) and (g.buf.len > 0) and (g.buf[^1] != ' '): put(g, tkSpaces, Space) + oneSpaceAdded = 1 # Before long comments we cannot make sure that a newline is generated, # because this might be wrong. But it is no problem in practice. if (g.pendingNL < 0) and (g.buf.len > 0) and - (g.lineLen < LineCommentColumn): + (g.col < LineCommentColumn): var ml = maxLineLength(n.comment) if ml + LineCommentColumn <= MaxLineLen: - put(g, tkSpaces, spaces(LineCommentColumn - g.lineLen)) + put(g, tkSpaces, spaces(LineCommentColumn - g.col)) + dec g.col, oneSpaceAdded putComment(g, n.comment) #assert(g.comStack[high(g.comStack)] = n); proc gcoms(g: var TSrcGen) = From 4d91cc79508e27c35ea9b733cb0e778b1d7fdad1 Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 8 Oct 2020 10:52:18 +0200 Subject: [PATCH 0736/3768] changelog improvements --- changelog.md | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/changelog.md b/changelog.md index 3144c7cf5..7ab7784f0 100644 --- a/changelog.md +++ b/changelog.md @@ -19,10 +19,10 @@ - If `allowMissingKeys` is `true` Nim's object to which JSON is parsed is allowed to have fields without corresponding JSON keys. - Added `bindParams`, `bindParam` to `db_sqlite` for binding parameters into a `SqlPrepared` statement. -- Add `tryInsert`,`insert` procs to `db_*` libs accept primary key column name. +- Added `tryInsert`,`insert` procs to `db_*` libs accept primary key column name. - Added `xmltree.newVerbatimText` support create `style`'s,`script`'s text. - `uri` adds Data URI Base64, implements RFC-2397. -- Add [DOM Parser](https://developer.mozilla.org/en-US/docs/Web/API/DOMParser) +- Added [DOM Parser](https://developer.mozilla.org/en-US/docs/Web/API/DOMParser) to the `dom` module for the JavaScript target. - The default hash for `Ordinal` has changed to something more bit-scrambling. `import hashes; proc hash(x: myInt): Hash = hashIdentity(x)` recovers the old @@ -182,10 +182,10 @@ cannot be applied to every use case. The limitations and the (lack of) reliability of `round` are well documented. -- Add `getprotobyname` to `winlean`. Add `getProtoByname` to `nativesockets` which returns a protocol code +- Added `getprotobyname` to `winlean`. Added `getProtoByname` to `nativesockets` which returns a protocol code from the database that matches the protocol `name`. -- Add missing attributes and methods to `dom.Navigator` like `deviceMemory`, `onLine`, `vibrate()`, etc. +- Added missing attributes and methods to `dom.Navigator` like `deviceMemory`, `onLine`, `vibrate()`, etc. - Added `strutils.indentation` and `strutils.dedent` which enable indented string literals: ```nim @@ -197,9 +197,9 @@ """ ``` -- Add `initUri(isIpv6: bool)` to `uri` module, now `uri` supports parsing ipv6 hostname. +- Added `initUri(isIpv6: bool)` to `uri` module, now `uri` supports parsing ipv6 hostname. -- Add `readLines(p: Process)` to `osproc` module for `startProcess` convenience. +- Added `readLines(p: Process)` to `osproc` module for `startProcess` convenience. - Added `heapqueue.toHeapQueue`, which creates a HeapQueue from an openArray. The usage is similar to procs such as `sets.toHashSet` and `tables.toTable`. @@ -233,7 +233,6 @@ proc `=destroy`(x: MyObj) = if x.kind and x.y != nil: deallocShared(x.y) - x.y = nil ``` Refactor into: ```nim @@ -248,7 +247,6 @@ proc `=destroy`(x: MySubObj) = if x.val != nil: deallocShared(x.val) - x.val = nil ``` - `getImpl` on enum type symbols now returns field syms instead of idents. This helps with writing typed macros. Old behavior for backwards compatibility can be restored @@ -280,14 +278,14 @@ proc mydiv(a, b): int {.raises: [].} = The reason for this is that `DivByZeroDefect` inherits from `Defect` and with `--panics:on` `Defects` become unrecoverable errors. -- Added the `thiscall` calling convention as specified by Microsoft, mostly for hooking purpose +- Added the `thiscall` calling convention as specified by Microsoft, mostly for hooking purposes. - Deprecated `{.unroll.}` pragma, was ignored by the compiler anyways, was a nop. -- Remove `strutils.isNilOrWhitespace`, was deprecated. -- Remove `sharedtables.initSharedTable`, was deprecated and produces undefined behavior. +- Removed `strutils.isNilOrWhitespace`, was deprecated. +- Removed `sharedtables.initSharedTable`, was deprecated and produces undefined behavior. - Removed `asyncdispatch.newAsyncNativeSocket`, was deprecated since `0.18`. -- Remove `dom.releaseEvents` and `dom.captureEvents`, was deprecated. +- Removed `dom.releaseEvents` and `dom.captureEvents`, was deprecated. -- Remove `sharedlists.initSharedList`, was deprecated and produces undefined behaviour. +- Removed `sharedlists.initSharedList`, was deprecated and produces undefined behaviour. - There is a new experimental feature called "strictFuncs" which makes the definition of `.noSideEffect` stricter. [See](manual_experimental.html#stricts-funcs) @@ -331,7 +329,7 @@ proc mydiv(a, b): int {.raises: [].} = - `--hint:processing` is now supported and means `--hint:processing:on` (likewise with other hints and warnings), which is consistent with all other bool flags. (since 1.3.3). -- `nim doc -r main` and `nim rst2html -r main` now call openDefaultBrowser +- `nim doc -r main` and `nim rst2html -r main` now call `openDefaultBrowser`. - new hint: `--hint:msgOrigin` will show where a compiler msg (hint|warning|error) was generated; this helps in particular when it's non obvious where it came from either because multiple locations generate the same message, or because the From 1e9d83efb0e59d3a71bf3b077808b96f29c5e529 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 8 Oct 2020 14:17:27 +0200 Subject: [PATCH 0737/3768] fixes #15512 (#15521) --- compiler/injectdestructors.nim | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 28f2c93f1..bec8b49f4 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -593,6 +593,18 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false) = result.add processScope(c, bodyScope, bodyResult) dec c.inLoop + of nkParForStmt: + inc c.inLoop + result = shallowCopy(n) + let last = n.len-1 + for i in 0.. Date: Thu, 8 Oct 2020 14:21:20 +0200 Subject: [PATCH 0738/3768] changelog.md: Group the new `to` procs (#15522) * changelog.md: Group the new `to` procs [ci skip] This also moves the `critbits.toCritBitTree` entry from the "Language Changes" section to the "Standard library additions and changes" section. * changelog.md: Remove extra space --- changelog.md | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/changelog.md b/changelog.md index 7ab7784f0..6b4d4cf2b 100644 --- a/changelog.md +++ b/changelog.md @@ -159,10 +159,6 @@ - Removed `sugar.distinctBase`, deprecated since `0.19`. - Export `asyncdispatch.PDispatcher.handles` so that an external library can register them. -- Added `deques.toDeque`, which creates a deque from an openArray. The usage is - similar to procs such as `sets.toHashSet` and `tables.toTable`. Previously, - it was necessary to create an empty deque and add items manually. - - `std/with`, `sugar.dup` now support object field assignment expression: ```nim import std/with @@ -201,13 +197,14 @@ - Added `readLines(p: Process)` to `osproc` module for `startProcess` convenience. -- Added `heapqueue.toHeapQueue`, which creates a HeapQueue from an openArray. - The usage is similar to procs such as `sets.toHashSet` and `tables.toTable`. - Previously, it was necessary to create an empty HeapQueue and add items - manually. -- Added `intsets.toIntSet`, which creates an IntSet from an openArray. The usage - is similar to procs such as `sets.toHashSet` and `tables.toTable`. Previously, - it was necessary to create an empty IntSet and add items manually. +- Added the below `to` procs for collections. The usage is similar to procs such as + `sets.toHashSet` and `tables.toTable`. Previously, it was necessary to create the + respective empty collection and add items manually. + * `critbits.toCritBitTree`, which creates a `CritBitTree` from an `openArray` of + items or an `openArray` of pairs. + * `deques.toDeque`, which creates a `Deque` from an `openArray`. + * `heapqueue.toHeapQueue`, which creates a `HeapQueue` from an `openArray`. + * `intsets.toIntSet`, which creates an `IntSet` from an `openArray`. - Added `progressInterval` argument to `asyncftpclient.newAsyncFtpClient` to control the interval at which progress callbacks are called. @@ -305,7 +302,6 @@ proc mydiv(a, b): int {.raises: [].} = - Added a `std/effecttraits` module for introspection of the inferred effects. We hope this enables `async` macros that are precise about the possible exceptions that can be raised. -- Added `critbits.toCritBitTree`, similar to `tables.toTable`, creates a new `CritBitTree` with given arguments. - The pragma blocks `{.gcsafe.}: ...` and `{.noSideEffect.}: ...` can now also be written as `{.cast(gcsafe).}: ...` and `{.cast(noSideEffect).}: ...`. This is the new preferred way of writing these, emphasizing their unsafe nature. From cfba237d142a5ae53d593d69d6e6a560cd21863f Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 8 Oct 2020 15:28:23 +0200 Subject: [PATCH 0739/3768] fixes #15510 (#15523) --- compiler/sempass2.nim | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index f0d6c6c7a..5eb464bb8 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -1065,7 +1065,10 @@ proc track(tracked: PEffects, n: PNode) = for i in 0.. Date: Thu, 8 Oct 2020 17:09:28 +0200 Subject: [PATCH 0740/3768] fixes #15511 (#15524) --- compiler/injectdestructors.nim | 4 +++- compiler/types.nim | 7 +++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index bec8b49f4..b18bcf34e 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -411,7 +411,9 @@ proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode = "if possible, rearrange your program's control flow to prevent it") % $n) else: if c.graph.config.selectedGC in {gcArc, gcOrc}: - assert(not containsGarbageCollectedRef(n.typ)) + assert(not containsManagedMemory(n.typ)) + if n.typ.skipTypes(abstractInst).kind in {tyOpenArray, tyVarargs}: + localError(c.graph.config, n.info, "cannot create an implicit openArray copy to be passed to a sink parameter") result.add newTree(nkAsgn, tmp, p(n, c, s, normal)) # Since we know somebody will take over the produced copy, there is # no need to destroy it. diff --git a/compiler/types.nim b/compiler/types.nim index b9fc4d4a2..b20bf9c06 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -321,6 +321,13 @@ proc containsGarbageCollectedRef*(typ: PType): bool = # things that are garbage-collected) result = searchTypeFor(typ, isGCRef) +proc isManagedMemory(t: PType): bool = + result = t.kind in GcTypeKinds or + (t.kind == tyProc and t.callConv == ccClosure) + +proc containsManagedMemory*(typ: PType): bool = + result = searchTypeFor(typ, isManagedMemory) + proc isTyRef(t: PType): bool = result = t.kind == tyRef or (t.kind == tyProc and t.callConv == ccClosure) From 3eaacac2303ab9fc1aef62208a112fae0ddd9152 Mon Sep 17 00:00:00 2001 From: lihaifeng Date: Thu, 8 Oct 2020 23:17:00 +0800 Subject: [PATCH 0741/3768] Update parsecfg.nim (#15513) * Update parsecfg.nim Returns the specified default value if the specified key value does not exist. * Update lib/pure/parsecfg.nim Co-authored-by: flywind <43030857+xflywind@users.noreply.github.com> * Update lib/pure/parsecfg.nim Co-authored-by: flywind <43030857+xflywind@users.noreply.github.com> * Update lib/pure/parsecfg.nim Co-authored-by: flywind <43030857+xflywind@users.noreply.github.com> Co-authored-by: flywind <43030857+xflywind@users.noreply.github.com> --- lib/pure/parsecfg.nim | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/pure/parsecfg.nim b/lib/pure/parsecfg.nim index c8e936ef1..15868c219 100644 --- a/lib/pure/parsecfg.nim +++ b/lib/pure/parsecfg.nim @@ -560,15 +560,16 @@ proc writeConfig*(dict: Config, filename: string) = let fileStream = newFileStream(file) dict.writeConfig(fileStream) -proc getSectionValue*(dict: Config, section, key: string): string = - ## Gets the Key value of the specified Section, returns an empty string if the key does not exist. +proc getSectionValue*(dict: Config, section, key: string, defaultVal = ""): string = + ## Gets the key value of the specified Section. + ## Returns the specified default value if the specified key does not exist. if dict.hasKey(section): if dict[section].hasKey(key): result = dict[section][key] else: - result = "" + result = defaultVal else: - result = "" + result = defaultVal proc setSectionKey*(dict: var Config, section, key, value: string) = ## Sets the Key value of the specified Section. From 91ce4515c87c084ade6b2b74ebfc2e19e25b5261 Mon Sep 17 00:00:00 2001 From: cooldome Date: Fri, 9 Oct 2020 07:52:26 +0100 Subject: [PATCH 0742/3768] fix gc:arc in nimscript (#15525) --- compiler/scriptconfig.nim | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim index 13ab03426..b359c9a48 100644 --- a/compiler/scriptconfig.nim +++ b/compiler/scriptconfig.nim @@ -228,11 +228,14 @@ proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile; # watch out, "newruntime" can be set within NimScript itself and then we need # to remember this: + if conf.selectedGC == gcUnselected: + conf.selectedGC = oldSelectedGC if optOwnedRefs in oldGlobalOptions: conf.globalOptions.incl {optTinyRtti, optOwnedRefs, optSeqDestructors} defineSymbol(conf.symbols, "nimv2") - if conf.selectedGC == gcUnselected: - conf.selectedGC = oldSelectedGC + if conf.selectedGC in {gcArc, gcOrc}: + conf.globalOptions.incl {optTinyRtti, optSeqDestructors} + defineSymbol(conf.symbols, "nimv2") # ensure we load 'system.nim' again for the real non-config stuff! resetSystemArtifacts(graph) From 8fb57d74515348d04dcd8c132e5e3787be80b577 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 8 Oct 2020 23:56:09 -0700 Subject: [PATCH 0743/3768] close #13081 (#15529) * close #13081 * fixup --- tests/vm/tvmmisc.nim | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/tests/vm/tvmmisc.nim b/tests/vm/tvmmisc.nim index 20adb5b8f..7da1c6743 100644 --- a/tests/vm/tvmmisc.nim +++ b/tests/vm/tvmmisc.nim @@ -178,3 +178,34 @@ const static: doAssert ctor doAssert ctand + +block: # bug #13081 + type Kind = enum + k0, k1, k2, k3 + + type Foo = object + x0: float + case kind: Kind + of k0: discard + of k1: x1: int + of k2: x2: string + of k3: x3: string + + const j1 = Foo(x0: 1.2, kind: k1, x1: 12) + const j2 = Foo(x0: 1.3, kind: k2, x2: "abc") + const j3 = Foo(x0: 1.3, kind: k3, x3: "abc2") + static: + doAssert $j1 == "(x0: 1.2, kind: k1, x1: 12)" + doAssert $j2 == """(x0: 1.3, kind: k2, x2: "abc")""" + doAssert $j3 == """(x0: 1.3, kind: k3, x3: "abc2")""" + doAssert $j1 == "(x0: 1.2, kind: k1, x1: 12)" + doAssert $j2 == """(x0: 1.3, kind: k2, x2: "abc")""" + doAssert $j3 == """(x0: 1.3, kind: k3, x3: "abc2")""" + + when false: + # BUG: this doesn't work yet + # Error: unhandled exception: 'sons' is not accessible using discriminant 'kind' of type 'TNode' [FieldDefect] + discard j1.x1 + static: + # ditto + discard j1.x1 From 16e8005031cdc017405ea0c85ddd03c7adf0cef0 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 9 Oct 2020 02:28:21 -0700 Subject: [PATCH 0744/3768] fix gitignore for testament cruft (#15530) * fix gitignore for testament cruft * fix rendertree => renderTree --- .gitignore | 2 ++ compiler/ccgexprs.nim | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index a4577c0d0..e55f2c5f0 100644 --- a/.gitignore +++ b/.gitignore @@ -83,6 +83,8 @@ megatest.nim /outputExpected.txt /outputGotten.txt +/t15148.txt +/tests/vm/tfile_rw.txt /lib/pure/*.js diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 364bcc143..c1469fa60 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1410,7 +1410,7 @@ proc handleConstExpr(p: BProc, n: PNode, d: var TLoc): bool = result = false proc genObjConstr(p: BProc, e: PNode, d: var TLoc) = - #echo rendertree e, " ", e.isDeepConstExpr + #echo renderTree e, " ", e.isDeepConstExpr # inheritance in C++ does not allow struct initialization so # we skip this step here: if not p.module.compileToCpp and optSeqDestructors notin p.config.globalOptions: From d4302162885919d4b10a6d846a6f9d53ec9873eb Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 9 Oct 2020 16:18:36 +0200 Subject: [PATCH 0745/3768] fixes #15532 (#15534) --- compiler/semfold.nim | 2 ++ tests/arc/tcaseobj.nim | 21 +++++++++++++++++++++ 2 files changed, 23 insertions(+) diff --git a/compiler/semfold.nim b/compiler/semfold.nim index d59dacd9a..e94a15fbe 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -719,6 +719,8 @@ proc getConstExpr(m: PSym, n: PNode; g: ModuleGraph): PNode = result.typ = n.typ of nkBracketExpr: result = foldArrayAccess(m, n, g) of nkDotExpr: result = foldFieldAccess(m, n, g) + of nkCheckedFieldExpr: + result = foldFieldAccess(m, n[0], g) of nkStmtListExpr: var i = 0 while i <= n.len - 2: diff --git a/tests/arc/tcaseobj.nim b/tests/arc/tcaseobj.nim index 107cf9fc1..a967a509b 100644 --- a/tests/arc/tcaseobj.nim +++ b/tests/arc/tcaseobj.nim @@ -223,3 +223,24 @@ type MyObj = object var a = MyObj(kind: false, x0: 1234) a.kind = true doAssert(a.x1 == "") + +block: + # bug #15532 + type Kind = enum + k0, k1 + + type Foo = object + y: int + case kind: Kind + of k0: x0: int + of k1: x1: int + + const j0 = Foo(y: 1, kind: k0, x0: 2) + const j1 = Foo(y: 1, kind: k1, x1: 2) + + doAssert j0.y == 1 + doAssert j0.kind == k0 + doAssert j1.kind == k1 + + doAssert j1.x1 == 2 + doAssert j0.x0 == 2 From 0a560144cef00bb2d41ce52f218c9927c1b1f6e2 Mon Sep 17 00:00:00 2001 From: genotrance Date: Sat, 10 Oct 2020 00:04:03 -0500 Subject: [PATCH 0746/3768] Bump nimble (#15539) --- koch.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/koch.nim b/koch.nim index bbb37b702..51ebac659 100644 --- a/koch.nim +++ b/koch.nim @@ -10,7 +10,7 @@ # const - NimbleStableCommit = "26167cd3b7671006c8bd38cbef8708431ddf8687" # master + NimbleStableCommit = "5d9aed105b561d1e5117f958721307d23b94f858" # master FusionStableCommit = "319aac4d43b04113831b529f8003e82f4af6a4a5" when not defined(windows): From 442841037a57d3b0f36ed1c6fd1fffd7110253d2 Mon Sep 17 00:00:00 2001 From: genotrance Date: Sat, 10 Oct 2020 00:04:57 -0500 Subject: [PATCH 0747/3768] Fix #12027 (#15519) --- koch.nim | 8 ++++---- tools/niminst/niminst.nim | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/koch.nim b/koch.nim index 51ebac659..398a14e23 100644 --- a/koch.nim +++ b/koch.nim @@ -78,7 +78,7 @@ Boot options: Commands for core developers: runCI runs continuous integration (CI), eg from travis docs [options] generates the full documentation - csource -d:release builds the C sources for installation + csource -d:danger builds the C sources for installation pdf builds the PDF documentation zip builds the installation zip package xz builds the installation tar.xz package @@ -416,7 +416,7 @@ proc winRelease*() = "web/upload/download/docs-$1.zip" % VersionAsString when true: inFold "winrelease csource": - csource("-d:release") + csource("-d:danger") when sizeof(pointer) == 4: winReleaseArch "32" when sizeof(pointer) == 8: @@ -561,7 +561,7 @@ proc runCI(cmd: string) = proc pushCsources() = if not dirExists("../csources/.git"): quit "[Error] no csources git repository found" - csource("-d:release") + csource("-d:danger") let cwd = getCurrentDir() try: copyDir("build/c_code", "../csources/c_code") @@ -582,7 +582,7 @@ proc pushCsources() = setCurrentDir(cwd) proc testUnixInstall(cmdLineRest: string) = - csource("-d:release " & cmdLineRest) + csource("-d:danger" & cmdLineRest) xz(false, cmdLineRest) let oldCurrentDir = getCurrentDir() try: diff --git a/tools/niminst/niminst.nim b/tools/niminst/niminst.nim index a0a0d9ad4..47137f8f3 100644 --- a/tools/niminst/niminst.nim +++ b/tools/niminst/niminst.nim @@ -605,7 +605,7 @@ proc xzDist(c: var ConfigData; windowsZip=false) = if not windowsZip and not fileExists("build" / buildBatFile): quit("No C sources found in ./build/, please build by running " & - "./koch csource -d:release.") + "./koch csource -d:danger.") if not windowsZip: processFile(proj / buildBatFile, "build" / buildBatFile) From 8a5f5504919acb4ccbebe3025b22d11ca67ee82e Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 9 Oct 2020 22:05:27 -0700 Subject: [PATCH 0748/3768] followup after #15529 and #15534 (#15536) --- tests/vm/tvmmisc.nim | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/tests/vm/tvmmisc.nim b/tests/vm/tvmmisc.nim index 7da1c6743..7ac75c7c5 100644 --- a/tests/vm/tvmmisc.nim +++ b/tests/vm/tvmmisc.nim @@ -1,7 +1,6 @@ # bug #4462 import macros import os -import strutils block: proc foo(t: typedesc) {.compileTime.} = @@ -202,10 +201,6 @@ block: # bug #13081 doAssert $j2 == """(x0: 1.3, kind: k2, x2: "abc")""" doAssert $j3 == """(x0: 1.3, kind: k3, x3: "abc2")""" - when false: - # BUG: this doesn't work yet - # Error: unhandled exception: 'sons' is not accessible using discriminant 'kind' of type 'TNode' [FieldDefect] - discard j1.x1 - static: - # ditto - discard j1.x1 + doAssert j1.x1 == 12 + static: + doAssert j1.x1 == 12 From c7ccbfac39008903826d8c56d9eaa6572c9acea7 Mon Sep 17 00:00:00 2001 From: Christian Ulrich Date: Sat, 10 Oct 2020 07:06:13 +0200 Subject: [PATCH 0749/3768] close socket in getPrimaryIPAddr (#15538) [backport] --- lib/pure/net.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index f30b893a7..3f768470d 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -2006,4 +2006,5 @@ proc getPrimaryIPAddr*(dest = parseIpAddress("8.8.8.8")): IpAddress = else: newSocket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP) socket.connect($dest, 80.Port) - socket.getLocalAddr()[0].parseIpAddress() + result = socket.getLocalAddr()[0].parseIpAddress() + socket.close() From 992952d86844805231bcc8a92063c122f721658d Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 10 Oct 2020 17:46:22 +0200 Subject: [PATCH 0750/3768] nimpretty: do not produce 'line too long' messages (#15541) --- compiler/lexer.nim | 5 +++-- nimpretty/tester.nim | 3 --- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 0cf00e612..9b1dc642d 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -758,8 +758,9 @@ proc handleCRLF(L: var Lexer, pos: int): int = template registerLine = let col = L.getColNumber(pos) - if col > MaxLineLength: - lexMessagePos(L, hintLineTooLong, pos) + when not defined(nimpretty): + if col > MaxLineLength: + lexMessagePos(L, hintLineTooLong, pos) case L.buf[pos] of CR: diff --git a/nimpretty/tester.nim b/nimpretty/tester.nim index 6124eb66a..d646b25ce 100644 --- a/nimpretty/tester.nim +++ b/nimpretty/tester.nim @@ -60,7 +60,4 @@ for t in allFiles: testTogether(allFiles) removeDir(outputdir) - - - if failures > 0: quit($failures & " failures occurred.") From 3eac9b2344bda64b1f74e0475ce8a3b5afa63271 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 11 Oct 2020 11:43:01 -0700 Subject: [PATCH 0751/3768] os: add overload copyFile*(source, dest: string, isDir = false) (#15537) * os: add overload copyFile*(source, dest: string, isDir = false) * renamed to copyFileToDir --- changelog.md | 1 + lib/pure/os.nim | 8 +++++++- tests/stdlib/tos.nim | 19 +++++++++++++++++++ 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 6b4d4cf2b..07533c2d2 100644 --- a/changelog.md +++ b/changelog.md @@ -209,6 +209,7 @@ - Added `progressInterval` argument to `asyncftpclient.newAsyncFtpClient` to control the interval at which progress callbacks are called. +- Added `os.copyFileToDir` ## Language changes diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 5efbde268..2601f9d47 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -1682,7 +1682,7 @@ proc setFilePermissions*(filename: string, permissions: set[FilePermission]) {. proc copyFile*(source, dest: string) {.rtl, extern: "nos$1", tags: [ReadIOEffect, WriteIOEffect], noWeirdTarget.} = - ## Copies a file from `source` to `dest`. + ## Copies a file from `source` to `dest`, where `dest.parentDir` must exist. ## ## If this fails, `OSError` is raised. ## @@ -1738,6 +1738,12 @@ proc copyFile*(source, dest: string) {.rtl, extern: "nos$1", flushFile(d) close(d) +proc copyFileToDir*(source, dir: string) {.noWeirdTarget, since: (1,3,7).} = + ## Copies a file `source` into directory `dir`, which must exist. + if dir.len == 0: # treating "" as "." is error prone + raise newException(ValueError, "dest is empty") + copyFile(source, dir / source.lastPathPart) + when not declared(ENOENT) and not defined(Windows): when NoFakeVars: when not defined(haiku): diff --git a/tests/stdlib/tos.nim b/tests/stdlib/tos.nim index 814ff103d..019303ebf 100644 --- a/tests/stdlib/tos.nim +++ b/tests/stdlib/tos.nim @@ -36,6 +36,25 @@ block fileOperations: createDir(dname) doAssert dirExists(dname) + block: # copyFile, copyFileToDir + doAssertRaises(OSError): copyFile(dname/"nonexistant.txt", dname/"nonexistant.txt") + let fname = "D20201009T112235" + let fname2 = "D20201009T112235.2" + writeFile(dname/fname, "foo") + let sub = "sub" + doAssertRaises(OSError): copyFile(dname/fname, dname/sub/fname2) + doAssertRaises(OSError): copyFileToDir(dname/fname, dname/sub) + doAssertRaises(ValueError): copyFileToDir(dname/fname, "") + copyFile(dname/fname, dname/fname2) + doAssert fileExists(dname/fname2) + createDir(dname/sub) + copyFileToDir(dname/fname, dname/sub) + doAssert fileExists(dname/sub/fname) + removeDir(dname/sub) + doAssert not dirExists(dname/sub) + removeFile(dname/fname) + removeFile(dname/fname2) + # Test creating files and dirs for dir in dirs: createDir(dname/dir) From c2c7f6025922034073418e608a62f8f4d5a5b381 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 12 Oct 2020 02:43:44 +0800 Subject: [PATCH 0752/3768] docs minor (#15550) --- lib/pure/collections/deques.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pure/collections/deques.nim b/lib/pure/collections/deques.nim index 54756707d..c3eeb3156 100644 --- a/lib/pure/collections/deques.nim +++ b/lib/pure/collections/deques.nim @@ -13,7 +13,7 @@ ## None of the procs that get an individual value from the deque can be used ## on an empty deque. ## If compiled with `boundChecks` option, those procs will raise an `IndexDefect` -## on such access. This should not be relied upon, as `-d:release` will +## on such access. This should not be relied upon, as `-d:danger` or `--checks:off` will ## disable those checks and may return garbage or crash the program. ## ## As such, a check to see if the deque is empty is needed before any @@ -114,7 +114,7 @@ template emptyCheck(deq) = template xBoundsCheck(deq, i) = # Bounds check for the array like accesses. - when compileOption("boundChecks"): # d:release should disable this. + when compileOption("boundChecks"): # `-d:danger` or `--checks:off` should disable this. if unlikely(i >= deq.count): # x < deq.low is taken care by the Natural parameter raise newException(IndexDefect, "Out of bounds: " & $i & " > " & $(deq.count - 1)) From 3492178494484e0c0c1d8eec83aadc5b2ad06ede Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 11 Oct 2020 12:11:47 +0200 Subject: [PATCH 0753/3768] refactoring: removed cmdlinehelper.mainCommand callback --- compiler/cmdlinehelper.nim | 23 ++++++++++------------- compiler/nim.nim | 9 +++++---- drnim/drnim.nim | 11 +++++------ nimsuggest/nimsuggest.nim | 14 ++++++++------ tools/nimfind.nim | 7 ++++--- 5 files changed, 32 insertions(+), 32 deletions(-) diff --git a/compiler/cmdlinehelper.nim b/compiler/cmdlinehelper.nim index 4777af8df..a6b56b97e 100644 --- a/compiler/cmdlinehelper.nim +++ b/compiler/cmdlinehelper.nim @@ -34,7 +34,6 @@ type suggestMode*: bool supportsStdinFile*: bool processCmdLine*: proc(pass: TCmdLinePass, cmd: string; config: ConfigRef) - mainCommand*: proc(graph: ModuleGraph) proc initDefinesProg*(self: NimProg, conf: ConfigRef, name: string) = condsyms.initDefines(conf.symbols) @@ -56,21 +55,21 @@ proc processCmdLineAndProjectPath*(self: NimProg, conf: ConfigRef) = else: conf.projectPath = AbsoluteDir canonicalizePath(conf, AbsoluteFile getCurrentDir()) -proc loadConfigsAndRunMainCommand*(self: NimProg, cache: IdentCache; conf: ConfigRef): bool = +proc loadConfigsAndRunMainCommand*(self: NimProg, cache: IdentCache; conf: ConfigRef; + graph: ModuleGraph): bool = if self.suggestMode: conf.command = "nimsuggest" loadConfigs(DefaultConfig, cache, conf) # load all config files - block: + if not self.suggestMode: let scriptFile = conf.projectFull.changeFileExt("nims") - if not self.suggestMode: - # 'nim foo.nims' means to just run the NimScript file and do nothing more: - if fileExists(scriptFile) and scriptFile == conf.projectFull: - if conf.command == "": - conf.command = "e" - return false - elif conf.command.normalize == "e": - return false + # 'nim foo.nims' means to just run the NimScript file and do nothing more: + if fileExists(scriptFile) and scriptFile == conf.projectFull: + if conf.command == "": + conf.command = "e" + return false + elif conf.command.normalize == "e": + return false # now process command line arguments again, because some options in the # command line can overwrite the config file's settings @@ -79,7 +78,5 @@ proc loadConfigsAndRunMainCommand*(self: NimProg, cache: IdentCache; conf: Confi if conf.command == "": rawMessage(conf, errGenerated, "command missing") - let graph = newModuleGraph(cache, conf) graph.suggestMode = self.suggestMode - self.mainCommand(graph) return true diff --git a/compiler/nim.nim b/compiler/nim.nim index 2b0d78dd4..15aeccb33 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -22,7 +22,7 @@ import commands, options, msgs, extccomp, strutils, os, main, parseopt, idents, lineinfos, cmdlinehelper, - pathutils + pathutils, modulegraphs from std/browsers import openDefaultBrowser from nodejs import findNodeJs @@ -70,8 +70,7 @@ proc processCmdLine(pass: TCmdLinePass, cmd: string; config: ConfigRef) = proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = let self = NimProg( supportsStdinFile: true, - processCmdLine: processCmdLine, - mainCommand: mainCommand + processCmdLine: processCmdLine ) self.initDefinesProg(conf, "nim_compiler") if paramCount() == 0: @@ -79,7 +78,9 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = return self.processCmdLineAndProjectPath(conf) - if not self.loadConfigsAndRunMainCommand(cache, conf): return + var graph = newModuleGraph(cache, conf) + if not self.loadConfigsAndRunMainCommand(cache, conf, graph): return + mainCommand(graph) if conf.hasHint(hintGCStats): echo(GC_getStatistics()) #echo(GC_getStatistics()) if conf.errorCounter != 0: return diff --git a/drnim/drnim.nim b/drnim/drnim.nim index e17667ec7..b4761a398 100644 --- a/drnim/drnim.nim +++ b/drnim/drnim.nim @@ -1209,15 +1209,13 @@ proc mainCommand(graph: ModuleGraph) = else: "Debug" let sec = formatFloat(epochTime() - conf.lastCmdTime, ffDecimal, 3) let project = if optListFullPaths in conf.globalOptions: $conf.projectFull else: $conf.projectName - var output = $conf.absOutFile - if optListFullPaths notin conf.globalOptions: output = output.AbsoluteFile.extractFilename rawMessage(conf, hintSuccessX, [ "loc", loc, "sec", sec, "mem", mem, "build", build, "project", project, - "output", output, + "output", "" ]) proc processCmdLine(pass: TCmdLinePass, cmd: string; config: ConfigRef) = @@ -1262,8 +1260,7 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = incl conf.options, optStaticBoundsCheck let self = NimProg( supportsStdinFile: true, - processCmdLine: processCmdLine, - mainCommand: mainCommand + processCmdLine: processCmdLine ) self.initDefinesProg(conf, "drnim") if paramCount() == 0: @@ -1271,7 +1268,9 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = return self.processCmdLineAndProjectPath(conf) - if not self.loadConfigsAndRunMainCommand(cache, conf): return + var graph = newModuleGraph(cache, conf) + if not self.loadConfigsAndRunMainCommand(cache, conf, graph): return + mainCommand(graph) if conf.hasHint(hintGCStats): echo(GC_getStatistics()) when compileOption("gc", "v2") or compileOption("gc", "refc"): diff --git a/nimsuggest/nimsuggest.nim b/nimsuggest/nimsuggest.nim index 9a0962df3..afc692241 100644 --- a/nimsuggest/nimsuggest.nim +++ b/nimsuggest/nimsuggest.nim @@ -619,8 +619,7 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string; conf: ConfigRef) = proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = let self = NimProg( suggestMode: true, - processCmdLine: processCmdLine, - mainCommand: mainCommand + processCmdLine: processCmdLine ) self.initDefinesProg(conf, "nimsuggest") @@ -644,7 +643,9 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = #msgs.writelnHook = proc (line: string) = log(line) myLog("START " & conf.projectFull.string) - discard self.loadConfigsAndRunMainCommand(cache, conf) + var graph = newModuleGraph(cache, conf) + if self.loadConfigsAndRunMainCommand(cache, conf, graph): + mainCommand(graph) when isMainModule: handleCmdLine(newIdentCache(), newConfigRef()) @@ -698,8 +699,7 @@ else: conf = newConfigRef() self = NimProg( suggestMode: true, - processCmdLine: mockCmdLine, - mainCommand: mockCommand + processCmdLine: mockCmdLine ) self.initDefinesProg(conf, "nimsuggest") @@ -722,7 +722,9 @@ else: #msgs.writelnHook = proc (line: string) = log(line) myLog("START " & conf.projectFull.string) - discard self.loadConfigsAndRunMainCommand(cache, conf) + var graph = newModuleGraph(cache, conf) + if self.loadConfigsAndRunMainCommand(cache, conf, graph): + mockCommand(graph) if gLogging: for it in conf.searchPaths: log(it.string) diff --git a/tools/nimfind.nim b/tools/nimfind.nim index 4ad5fbb4f..f13e86aaa 100644 --- a/tools/nimfind.nim +++ b/tools/nimfind.nim @@ -208,8 +208,7 @@ proc processCmdLine*(pass: TCmdLinePass, cmd: string; conf: ConfigRef) = proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = let self = NimProg( suggestMode: true, - processCmdLine: processCmdLine, - mainCommand: mainCommand + processCmdLine: processCmdLine ) self.initDefinesProg(conf, "nimfind") @@ -228,6 +227,8 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = if not dirExists(conf.prefixDir / RelativeDir"lib"): conf.prefixDir = AbsoluteDir"" - discard self.loadConfigsAndRunMainCommand(cache, conf) + var graph = newModuleGraph(cache, conf) + if self.loadConfigsAndRunMainCommand(cache, conf, graph): + mainCommand(graph) handleCmdLine(newIdentCache(), newConfigRef()) From d66e274e1ac8e86266151ad0c48f7705b3bd1582 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 11 Oct 2020 18:03:40 +0200 Subject: [PATCH 0754/3768] refactoring: moved setOutFile to where it belongs --- compiler/main.nim | 14 -------------- compiler/options.nim | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/compiler/main.nim b/compiler/main.nim index ab074aa1f..d85aa43dc 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -66,20 +66,6 @@ when not defined(leanCompiler): compileProject(graph) finishDoc2Pass(graph.config.projectName) -proc setOutFile(conf: ConfigRef) = - proc libNameTmpl(conf: ConfigRef): string {.inline.} = - result = if conf.target.targetOS == osWindows: "$1.lib" else: "lib$1.a" - - if conf.outFile.isEmpty: - let base = conf.projectName - let targetName = if optGenDynLib in conf.globalOptions: - platform.OS[conf.target.targetOS].dllFrmt % base - elif optGenStaticLib in conf.globalOptions: - libNameTmpl(conf) % base - else: - base & platform.OS[conf.target.targetOS].exeExt - conf.outFile = RelativeFile targetName - proc commandCompileToC(graph: ModuleGraph) = let conf = graph.config setOutFile(conf) diff --git a/compiler/options.nim b/compiler/options.nim index 6054eb3a5..2f796afe6 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -884,3 +884,18 @@ proc floatInt64Align*(conf: ConfigRef): int16 = # to 4bytes (except with -malign-double) return 4 return 8 + +proc setOutFile*(conf: ConfigRef) = + proc libNameTmpl(conf: ConfigRef): string {.inline.} = + result = if conf.target.targetOS == osWindows: "$1.lib" else: "lib$1.a" + + if conf.outFile.isEmpty: + let base = conf.projectName + let targetName = + if optGenDynLib in conf.globalOptions: + platform.OS[conf.target.targetOS].dllFrmt % base + elif optGenStaticLib in conf.globalOptions: + libNameTmpl(conf) % base + else: + base & platform.OS[conf.target.targetOS].exeExt + conf.outFile = RelativeFile targetName From 9668a1106de6ccd86b377d2a2c566d8efb11479b Mon Sep 17 00:00:00 2001 From: cooldome Date: Mon, 12 Oct 2020 10:12:32 +0100 Subject: [PATCH 0755/3768] Fix 15543 (#15544) * fix #15543 * fix spacing * fix test * simplify test for freebsd platform --- compiler/ccgtypes.nim | 2 +- tests/arc/t14472.nim | 22 +++++++++++++++++++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 0df7c8d2a..6c82a140b 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -1487,7 +1487,7 @@ proc genTypeInfoV1(m: BModule, t: PType; info: TLineInfo): Rope = elif origType.attachedOps[attachedDeepCopy] != nil: genDeepCopyProc(m, origType.attachedOps[attachedDeepCopy], result) - if optTinyRtti in m.config.globalOptions and t.kind == tyObject: + if optTinyRtti in m.config.globalOptions and t.kind == tyObject and sfImportc notin t.sym.flags: let v2info = genTypeInfoV2(m, origType, info) addf(m.s[cfsTypeInit3], "$1->typeInfoV1 = (void*)&$2; $2.typeInfoV2 = (void*)$1;$n", [ v2info, result]) diff --git a/tests/arc/t14472.nim b/tests/arc/t14472.nim index 47ed77f4c..4ef661161 100644 --- a/tests/arc/t14472.nim +++ b/tests/arc/t14472.nim @@ -1,6 +1,6 @@ discard """ valgrind: true - cmd: "nim c --gc:arc -d:useMalloc $file" + cmd: "nim cpp --gc:arc -d:useMalloc --deepcopy:on $file" """ type @@ -21,3 +21,23 @@ proc bork() : ImportedScene = add(result.meshes, Mesh(material: mats[0])) var s = bork() + + +#------------------------------------------------------------------------ +# issue #15543 + +import tables + +type + cdbl {.importc: "double".} = object + + MyObject = ref object of RootObj + y: Table[string, cdbl] + + +proc test = + var x = new(MyObject) + +test() + + From 30b966bdf7ea898f5cda9bcd3d9470d17eba5634 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 12 Oct 2020 12:04:40 -0700 Subject: [PATCH 0756/3768] unbreak CI: fix logic for skipping ci (#15556) --- .github/workflows/ci_docs.yml | 2 +- .github/workflows/ci_packages.yml | 2 +- .github/workflows/ci_ssl.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci_docs.yml b/.github/workflows/ci_docs.yml index 6b21475f6..64314cce4 100644 --- a/.github/workflows/ci_docs.yml +++ b/.github/workflows/ci_docs.yml @@ -29,7 +29,7 @@ on: jobs: build: if: | - !contains(format('{0} {1} {2}', github.event.head_commit.message, github.event.pull_request.title), '[skip ci]') + !contains(format('{0} {1}', github.event.head_commit.message, github.event.pull_request.title), '[skip ci]') strategy: fail-fast: false matrix: diff --git a/.github/workflows/ci_packages.yml b/.github/workflows/ci_packages.yml index a59bcee9d..0d2f89aa6 100644 --- a/.github/workflows/ci_packages.yml +++ b/.github/workflows/ci_packages.yml @@ -4,7 +4,7 @@ on: [push, pull_request] jobs: build: if: | - !contains(format('{0} {1} {2}', github.event.head_commit.message, github.event.pull_request.title), '[skip ci]') + !contains(format('{0} {1}', github.event.head_commit.message, github.event.pull_request.title), '[skip ci]') strategy: fail-fast: false matrix: diff --git a/.github/workflows/ci_ssl.yml b/.github/workflows/ci_ssl.yml index 070e4aa1e..6a024eca0 100644 --- a/.github/workflows/ci_ssl.yml +++ b/.github/workflows/ci_ssl.yml @@ -13,7 +13,7 @@ on: jobs: build: if: | - !contains(format('{0} {1} {2}', github.event.head_commit.message, github.event.pull_request.title), '[skip ci]') + !contains(format('{0} {1}', github.event.head_commit.message, github.event.pull_request.title), '[skip ci]') strategy: fail-fast: false matrix: From 1f51a3399808218f99e820fd43f5bd9d2204ce61 Mon Sep 17 00:00:00 2001 From: Christian Ulrich Date: Mon, 12 Oct 2020 22:02:17 +0200 Subject: [PATCH 0757/3768] close socket in getPrimaryIPAddr even if exception occurs (#15558) --- lib/pure/net.nim | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 3f768470d..3ef41aa3c 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -2005,6 +2005,8 @@ proc getPrimaryIPAddr*(dest = parseIpAddress("8.8.8.8")): IpAddress = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) else: newSocket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP) - socket.connect($dest, 80.Port) - result = socket.getLocalAddr()[0].parseIpAddress() - socket.close() + try: + socket.connect($dest, 80.Port) + result = socket.getLocalAddr()[0].parseIpAddress() + finally: + socket.close() From f1d81dc6e6673a74710f15b93c0938c22b90c4e1 Mon Sep 17 00:00:00 2001 From: Christopher Dunn Date: Mon, 12 Oct 2020 23:33:53 -0500 Subject: [PATCH 0758/3768] Fix doc for CountTable (#15561) [backport] --- lib/pure/collections/tables.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index c5a7fca56..85440bd2b 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -2599,7 +2599,7 @@ proc `[]`*[A](t: CountTableRef[A], key: A): int = ## See also: ## * `getOrDefault<#getOrDefault,CountTableRef[A],A,int>`_ to return ## a custom value if the key doesn't exist - ## * `mget proc<#mget,CountTableRef[A],A>`_ + ## * `inc proc<#inc,CountTableRef[A],A>`_ to inc even if missing ## * `[]= proc<#[]%3D,CountTableRef[A],A,int>`_ for inserting a new ## (key, value) pair in the table ## * `hasKey proc<#hasKey,CountTableRef[A],A>`_ for checking if a key From 0134e34e035a139677413acb20e42b4f7f490021 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 12 Oct 2020 23:24:30 -0700 Subject: [PATCH 0759/3768] dup docs: add an example with `addQuoted` (#15548) * dup docs: add addQuoted example * fixup --- lib/pure/sugar.nim | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/pure/sugar.nim b/lib/pure/sugar.nim index be5e1f326..92e9f662a 100644 --- a/lib/pure/sugar.nim +++ b/lib/pure/sugar.nim @@ -244,6 +244,11 @@ since (1, 1): var c = "xyz" # An underscore (_) can be used to denote the place of the argument you're passing: + doAssert "".dup(addQuoted(_, "foo")) == "\"foo\"" + # but `_` is optional here since the substitution is in 1st position: + doAssert "".dup(addQuoted("foo")) == "\"foo\"" + + # chaining: # b = "xyz" var d = dup c: makePalindrome # xyzyx From d1af9587b889bc1c9b080a48b9858436d2482599 Mon Sep 17 00:00:00 2001 From: alaviss Date: Tue, 13 Oct 2020 11:39:32 -0500 Subject: [PATCH 0760/3768] terminal: fix fgColor/bgColor commands [backport] (#15554) Since #8296, fgSetColor is no longer a global. These commands were probably left out from the change as an oversight, so some tests have been added to make sure this won't happen again. --- lib/pure/terminal.nim | 7 +++---- tests/stdlib/tterminal.nim | 8 ++++++++ 2 files changed, 11 insertions(+), 4 deletions(-) create mode 100644 tests/stdlib/tterminal.nim diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index 968d6bd9a..ac6720771 100644 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -689,10 +689,9 @@ template styledEchoProcessArg(f: File, color: Color) = template styledEchoProcessArg(f: File, cmd: TerminalCmd) = when cmd == resetStyle: resetAttributes(f) - when cmd == fgColor: - fgSetColor = true - when cmd == bgColor: - fgSetColor = false + elif cmd in {fgColor, bgColor}: + let term = getTerminal() + term.fgSetColor = cmd == fgColor macro styledWrite*(f: File, m: varargs[typed]): untyped = ## Similar to ``write``, but treating terminal style arguments specially. diff --git a/tests/stdlib/tterminal.nim b/tests/stdlib/tterminal.nim new file mode 100644 index 000000000..364c8d82e --- /dev/null +++ b/tests/stdlib/tterminal.nim @@ -0,0 +1,8 @@ +discard """ + action: compile +""" + +import terminal, colors + +styledEcho fgColor, colRed, "Test" +styledEcho bgColor, colBlue, "Test" From 0c0f86b2c54d2ef367e5a401c8744b8f5295aaa5 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 14 Oct 2020 00:25:28 -0700 Subject: [PATCH 0761/3768] reference fusion docs (#15562) --- config/nimdoc.cfg | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/config/nimdoc.cfg b/config/nimdoc.cfg index a6a402bab..67d619ea1 100644 --- a/config/nimdoc.cfg +++ b/config/nimdoc.cfg @@ -132,7 +132,10 @@ doc.body_toc_group = """ Index
  • - Compiler docs + compiler docs +
  • +
  • + fusion docs
  • From fc90eb9f27082e36d2ccf3c36757f08bbd5b4ea5 Mon Sep 17 00:00:00 2001 From: narimiran Date: Wed, 14 Oct 2020 09:26:44 +0200 Subject: [PATCH 0762/3768] change case in nimdoc [ci skip] --- config/nimdoc.cfg | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/nimdoc.cfg b/config/nimdoc.cfg index 67d619ea1..13beb5af1 100644 --- a/config/nimdoc.cfg +++ b/config/nimdoc.cfg @@ -132,10 +132,10 @@ doc.body_toc_group = """ Index
  • - compiler docs + Compiler docs
  • - fusion docs + Fusion docs
  • From c664fb284349e2d03148890dfec169b844694eae Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 14 Oct 2020 09:55:47 +0200 Subject: [PATCH 0763/3768] sigmatch: hotfix [backport] (#15565) --- compiler/sigmatch.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 1441203e8..687634e93 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1107,7 +1107,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, let x = typeRel(c, f, branch, flags + {trDontBind}) if x == isNone: return isNone if x < result: result = x - return + return result of tyAnd: # XXX: deal with the current dual meaning of tyGenericParam @@ -1441,7 +1441,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, let ff = rootf[i] let aa = roota[i] let res = typeRel(c, ff, aa, nextFlags) - if res != isEqual: result = isGeneric + if res != isNone and res != isEqual: result = isGeneric if res notin {isEqual, isGeneric}: if trNoCovariance notin flags and ff.kind == aa.kind: let paramFlags = rootf.base[i-1].flags From 12c8ef5949aca6be288a304292c89c350db44f29 Mon Sep 17 00:00:00 2001 From: Benjamin Lee Date: Wed, 14 Oct 2020 05:20:59 -0400 Subject: [PATCH 0764/3768] Update the list of GC options when raising an error (closes #15547) (#15553) --- compiler/commands.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/commands.nim b/compiler/commands.nim index 19048065f..3c6cc5919 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -229,7 +229,7 @@ proc processCompile(conf: ConfigRef; filename: string) = extccomp.addExternalFileToCompile(conf, found) const - errNoneBoehmRefcExpectedButXFound = "'none', 'boehm' or 'refc' expected, but '$1' found" + errNoneBoehmRefcExpectedButXFound = "'arc', 'orc', 'markAndSweep', 'boehm', 'go', 'none', 'regions', or 'refc' expected, but '$1' found" errNoneSpeedOrSizeExpectedButXFound = "'none', 'speed' or 'size' expected, but '$1' found" errGuiConsoleOrLibExpectedButXFound = "'gui', 'console' or 'lib' expected, but '$1' found" errInvalidExceptionSystem = "'goto', 'setjump', 'cpp' or 'quirky' expected, but '$1' found" From 09cad44be9153211317f3f4a998f054192ffe9fc Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 14 Oct 2020 03:04:14 -0700 Subject: [PATCH 0765/3768] ci docs: add config/nimdoc.cfg to paths (#15566) --- .github/workflows/ci_docs.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci_docs.yml b/.github/workflows/ci_docs.yml index 64314cce4..718dcd7e3 100644 --- a/.github/workflows/ci_docs.yml +++ b/.github/workflows/ci_docs.yml @@ -4,6 +4,7 @@ on: paths: - 'compiler/docgen.nim' - 'compiler/renderverbatim.nim' + - 'config/nimdoc.cfg' - 'doc/**.rst' - 'doc/nimdoc.css' - 'lib/**.nim' @@ -17,6 +18,7 @@ on: paths: - 'compiler/docgen.nim' - 'compiler/renderverbatim.nim' + - 'config/nimdoc.cfg' - 'doc/**.rst' - 'doc/nimdoc.css' - 'lib/**.nim' From 89781124579fdf7fe884aca093ed9348195a57af Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 14 Oct 2020 13:58:36 +0200 Subject: [PATCH 0766/3768] fixes a C code generator regression, no need to backport, only the 1.4 line is affected (#15569) --- compiler/ccgcalls.nim | 2 +- tests/ccgbugs/t2procs.nim | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 tests/ccgbugs/t2procs.nim diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 6c5b721c7..242e9f5cf 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -249,7 +249,7 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode): Rope = else: internalError(p.config, "openArrayLoc: " & typeToString(a.t)) proc withTmpIfNeeded(p: BProc, a: TLoc, needsTmp: bool): TLoc = - if needsTmp: + if needsTmp and a.lode.typ != nil: var tmp: TLoc getTemp(p, a.lode.typ, tmp, needsInit=false) genAssignment(p, tmp, a, {}) diff --git a/tests/ccgbugs/t2procs.nim b/tests/ccgbugs/t2procs.nim new file mode 100644 index 000000000..d8b7a2815 --- /dev/null +++ b/tests/ccgbugs/t2procs.nim @@ -0,0 +1,18 @@ +discard """ + output: '''before +1 +before +2''' +""" + +proc fn[T1, T2](a: T1, b: T2) = + a(1) + b(2) + +fn( (proc(x: int) = + echo "before" # example block, can span multiple lines + echo x), + (proc (y: int) = + echo "before" + echo y) +) From 977bccdbff9db889bebfa963d1292c4d433f6779 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Wed, 14 Oct 2020 16:07:25 +0200 Subject: [PATCH 0767/3768] Make useVersion:1.0 disable the proc arg sym change (#15570) * Make useVersion:1.0 disable the proc arg sym change * Also do this for useVersion:1.2 --- changelog.md | 5 ++++- compiler/commands.nim | 6 ++++++ compiler/options.nim | 1 + compiler/semtypes.nim | 3 ++- 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index 07533c2d2..694d9635f 100644 --- a/changelog.md +++ b/changelog.md @@ -248,7 +248,10 @@ ``` - `getImpl` on enum type symbols now returns field syms instead of idents. This helps with writing typed macros. Old behavior for backwards compatibility can be restored - with command line switch `--useVersion:1.0`. + with `--useVersion:1.0`. +- The typed AST for proc headers will now have the arguments be syms instead of idents. + This helps with writing typed macros. Old behaviour for backwards compatibility can + be restored with `--useVersion:1.0`. - ``let`` statements can now be used without a value if declared with ``importc``/``importcpp``/``importjs``/``importobjc``. - The keyword `from` is now usable as an operator. diff --git a/compiler/commands.nim b/compiler/commands.nim index 3c6cc5919..b9199c1b0 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -883,6 +883,12 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; undefSymbol(conf.symbols, "nimDoesntTrackDefects") ast.eqTypeFlags.excl {tfGcSafe, tfNoSideEffect} conf.globalOptions.incl optNimV1Emulation + of "1.2": + defineSymbol(conf.symbols, "NimMajor", "1") + defineSymbol(conf.symbols, "NimMinor", "2") + # always be compatible with 1.2.100: + defineSymbol(conf.symbols, "NimPatch", "100") + conf.globalOptions.incl optNimV12Emulation else: localError(conf, info, "unknown Nim version; currently supported values are: {1.0}") of "benchmarkvm": diff --git a/compiler/options.nim b/compiler/options.nim index 2f796afe6..3be7c5099 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -94,6 +94,7 @@ type # please make sure we have under 32 options optProduceAsm # produce assembler code optPanics # turn panics (sysFatal) into a process termination optNimV1Emulation # emulate Nim v1.0 + optNimV12Emulation # emulate Nim v1.2 optSourcemap optProfileVM # enable VM profiler optEnableDeepCopy # ORC specific: enable 'deepcopy' for all types. diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 9bb7673fa..8ef393839 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1288,7 +1288,8 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, addParamOrResult(c, arg, kind) styleCheckDef(c.config, a[j].info, arg) onDef(a[j].info, arg) - a[j] = newSymNode(arg) + if {optNimV1Emulation, optNimV12Emulation} * c.config.globalOptions == {}: + a[j] = newSymNode(arg) var r: PType if n[0].kind != nkEmpty: From c2ba4ef9799a097fec8b2bb1b033448860d60f1b Mon Sep 17 00:00:00 2001 From: alaviss Date: Wed, 14 Oct 2020 10:08:56 -0500 Subject: [PATCH 0768/3768] suggest: try to find the implementation of a symbol when def is used (#15555) * suggest: try to find the implementation of a symbol when def is used * suggest: return all declarations of the symbol on `def` --- changelog.md | 4 ++++ compiler/semstmts.nim | 1 + compiler/suggest.nim | 22 +++++++++++++++------- nimsuggest/tests/tdef_forward.nim | 13 +++++++++++++ 4 files changed, 33 insertions(+), 7 deletions(-) create mode 100644 nimsuggest/tests/tdef_forward.nim diff --git a/changelog.md b/changelog.md index 694d9635f..2c8f9b5e2 100644 --- a/changelog.md +++ b/changelog.md @@ -361,3 +361,7 @@ proc mydiv(a, b): int {.raises: [].} = ## Tool changes + +- `nimsuggest` will now return both the forward declaration and the + implementation location upon a `def` query. Previously the behavior was + just to return the forward declaration. diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 44a31c2ea..27cd9019f 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1943,6 +1943,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, if not comesFromShadowScope: excl(proto.flags, sfForward) incl(proto.flags, sfWasForwarded) + suggestSym(c.config, s.info, proto, c.graph.usageSym) closeScope(c) # close scope with wrong parameter symbols openScope(c) # open scope for old (correct) parameter symbols if proto.ast[genericParamsPos].kind != nkEmpty: diff --git a/compiler/suggest.nim b/compiler/suggest.nim index 4943603ce..be337af12 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -119,7 +119,8 @@ proc getTokenLenFromSource(conf: ConfigRef; ident: string; info: TLineInfo): int proc symToSuggest(conf: ConfigRef; s: PSym, isLocal: bool, section: IdeCmd, info: TLineInfo; quality: range[0..100]; prefix: PrefixMatch; - inTypeContext: bool; scope: int): Suggest = + inTypeContext: bool; scope: int; + useSuppliedInfo = false): Suggest = new(result) result.section = section result.quality = quality @@ -156,7 +157,11 @@ proc symToSuggest(conf: ConfigRef; s: PSym, isLocal: bool, section: IdeCmd, info result.forth = "" when defined(nimsuggest) and not defined(noDocgen) and not defined(leanCompiler): result.doc = s.extractDocComment - let infox = if section in {ideUse, ideHighlight, ideOutline}: info else: s.info + let infox = + if useSuppliedInfo or section in {ideUse, ideHighlight, ideOutline}: + info + else: + s.info result.filePath = toFullPath(conf, infox) result.line = toLinenumber(infox) result.column = toColumn(infox) @@ -463,11 +468,14 @@ when defined(nimsuggest): let x = if info == s.info and info.col == s.info.col: ideDef else: ideUse suggestResult(conf, symToSuggest(conf, s, isLocal=false, x, info, 100, PrefixMatch.None, false, 0)) -proc findDefinition(conf: ConfigRef; info: TLineInfo; s: PSym) = +proc findDefinition(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym) = if s.isNil: return - if isTracked(info, conf.m.trackPos, s.name.s.len): - suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0)) - suggestQuit() + if isTracked(info, conf.m.trackPos, s.name.s.len) or (s == usageSym and sfForward notin s.flags): + suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0, useSuppliedInfo = s == usageSym)) + if sfForward notin s.flags: + suggestQuit() + else: + usageSym = s proc ensureIdx[T](x: var T, y: int) = if x.len <= y: x.setLen(y+1) @@ -487,7 +495,7 @@ proc suggestSym*(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym; if conf.ideCmd == ideUse: findUsages(conf, info, s, usageSym) elif conf.ideCmd == ideDef: - findDefinition(conf, info, s) + findDefinition(conf, info, s, usageSym) elif conf.ideCmd == ideDus and s != nil: if isTracked(info, conf.m.trackPos, s.name.s.len): suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0)) diff --git a/nimsuggest/tests/tdef_forward.nim b/nimsuggest/tests/tdef_forward.nim new file mode 100644 index 000000000..9bdd8b21d --- /dev/null +++ b/nimsuggest/tests/tdef_forward.nim @@ -0,0 +1,13 @@ +discard """ +$nimsuggest --tester $file +>def $1 +def;;skProc;;tdef_forward.hello;;proc (): string;;$file;;8;;5;;"";;100 +def;;skProc;;tdef_forward.hello;;proc (): string;;$file;;12;;5;;"";;100 +""" + +proc hello(): string + +hel#[!]#lo() + +proc hello(): string = + "Hello" From 644eb4dd5439b574f03b0d65db6d2b31b246a4be Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 14 Oct 2020 22:26:21 +0200 Subject: [PATCH 0769/3768] ORC: critical bugfix for the cycle analyser, introduce -d:nimStressOrc for easier stress testing (#15572) --- compiler/liftdestructors.nim | 2 +- lib/system/arc.nim | 6 +++++ lib/system/orc.nim | 45 +++++++++++++++++++++--------------- 3 files changed, 33 insertions(+), 20 deletions(-) diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index dd5332b38..2735fb417 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -518,7 +518,7 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(x, nkDerefExpr)) actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, x) - let isCyclic = c.g.config.selectedGC == gcOrc and types.canFormAcycle(t) + let isCyclic = c.g.config.selectedGC == gcOrc and types.canFormAcycle(elemType) var cond: PNode if isCyclic: diff --git a/lib/system/arc.nim b/lib/system/arc.nim index 973b951d4..3e24c2506 100644 --- a/lib/system/arc.nim +++ b/lib/system/arc.nim @@ -161,6 +161,12 @@ proc nimRawDispose(p: pointer) {.compilerRtl.} = if head(p).rc >= rcIncrement: cstderr.rawWrite "[FATAL] dangling references exist\n" quit 1 + + when defined(gcOrc) and defined(nimArcDebug): + if (head(p).rc and 0b100) != 0: + cstderr.rawWrite "[FATAL] cycle root freed\n" + quit 1 + when defined(nimArcDebug): # we do NOT really free the memory here in order to reliably detect use-after-frees if freedCells.data == nil: init(freedCells) diff --git a/lib/system/orc.nim b/lib/system/orc.nim index cf8d6379a..1d856696c 100644 --- a/lib/system/orc.nim +++ b/lib/system/orc.nim @@ -297,8 +297,10 @@ proc collectCyclesBacon(j: var GcEnv) = const defaultThreshold = when defined(nimAdaptiveOrc): 128 else: 10_000 -var - rootsThreshold = defaultThreshold +when defined(nimStressOrc): + const rootsThreshold = 10 # broken with -d:nimStressOrc: 10 and for havlak iterations 1..8 +else: + var rootsThreshold = defaultThreshold proc collectCycles() = ## Collect cycles. @@ -320,28 +322,31 @@ proc collectCycles() = deinit j.traceStack deinit roots - # compute the threshold based on the previous history - # of the cycle collector's effectiveness: - # we're effective when we collected 50% or more of the nodes - # we touched. If we're effective, we can reset the threshold: - if j.freed * 2 >= j.touched: - when defined(nimAdaptiveOrc): - rootsThreshold = max(rootsThreshold div 2, 16) - else: - rootsThreshold = defaultThreshold - #cfprintf(cstderr, "[collectCycles] freed %ld, touched %ld new threshold %ld\n", j.freed, j.touched, rootsThreshold) - elif rootsThreshold < high(int) div 4: - rootsThreshold = rootsThreshold * 3 div 2 + + when not defined(nimStressOrc): + # compute the threshold based on the previous history + # of the cycle collector's effectiveness: + # we're effective when we collected 50% or more of the nodes + # we touched. If we're effective, we can reset the threshold: + if j.freed * 2 >= j.touched: + when defined(nimAdaptiveOrc): + rootsThreshold = max(rootsThreshold div 2, 16) + else: + rootsThreshold = defaultThreshold + #cfprintf(cstderr, "[collectCycles] freed %ld, touched %ld new threshold %ld\n", j.freed, j.touched, rootsThreshold) + elif rootsThreshold < high(int) div 4: + rootsThreshold = rootsThreshold * 3 div 2 when logOrc: cfprintf(cstderr, "[collectCycles] end; freed %ld new threshold %ld touched: %ld mem: %ld\n", j.freed, rootsThreshold, j.touched, getOccupiedMem()) proc registerCycle(s: Cell; desc: PNimTypeV2) = + s.rootIdx = roots.len + if roots.d == nil: init(roots) + add(roots, s, desc) + if roots.len >= rootsThreshold: collectCycles() - if roots.d == nil: init(roots) - s.rootIdx = roots.len - add(roots, s, desc) #writeCell("[added root]", s) proc GC_fullCollect* = @@ -350,10 +355,12 @@ proc GC_fullCollect* = collectCycles() proc GC_enableMarkAndSweep*() = - rootsThreshold = defaultThreshold + when not defined(nimStressOrc): + rootsThreshold = defaultThreshold proc GC_disableMarkAndSweep*() = - rootsThreshold = high(int) + when not defined(nimStressOrc): + rootsThreshold = high(int) proc rememberCycle(isDestroyAction: bool; s: Cell; desc: PNimTypeV2) {.noinline.} = if isDestroyAction: From a4c28394beab558a160671ab216e736b36276a94 Mon Sep 17 00:00:00 2001 From: genotrance Date: Wed, 14 Oct 2020 15:26:44 -0500 Subject: [PATCH 0770/3768] Bump nimble (#15573) --- koch.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/koch.nim b/koch.nim index 398a14e23..7be35fed7 100644 --- a/koch.nim +++ b/koch.nim @@ -10,7 +10,7 @@ # const - NimbleStableCommit = "5d9aed105b561d1e5117f958721307d23b94f858" # master + NimbleStableCommit = "8f7af860c5ce9634af880a7081c6435e1f2a5148" # master FusionStableCommit = "319aac4d43b04113831b529f8003e82f4af6a4a5" when not defined(windows): From 1bbc20010e3c8efc0de2c73a8d0d3d844de97049 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 15 Oct 2020 09:42:43 +0200 Subject: [PATCH 0771/3768] harden the ORC asyncleak3 test case (#15580) * harden the ORC asyncleak3 test case * add another ORC stress test --- tests/arc/tasyncleak3.nim | 3 +- tests/arc/thavlak_orc_stress.nim | 414 +++++++++++++++++++++++++++++++ 2 files changed, 416 insertions(+), 1 deletion(-) create mode 100644 tests/arc/thavlak_orc_stress.nim diff --git a/tests/arc/tasyncleak3.nim b/tests/arc/tasyncleak3.nim index c1510895a..21e963b7f 100644 --- a/tests/arc/tasyncleak3.nim +++ b/tests/arc/tasyncleak3.nim @@ -1,6 +1,7 @@ discard """ - cmd: "nim c --gc:orc $file" + cmd: "nim c --gc:orc -d:useMalloc $file" output: "true" + valgrind: "true" """ import strutils diff --git a/tests/arc/thavlak_orc_stress.nim b/tests/arc/thavlak_orc_stress.nim new file mode 100644 index 000000000..3c61f10ef --- /dev/null +++ b/tests/arc/thavlak_orc_stress.nim @@ -0,0 +1,414 @@ +discard """ + cmd: "nim c --gc:orc -d:useMalloc -d:nimStressOrc $file" + valgrind: "true" + output: "done" +""" + +import tables +import sets +import net + +type + BasicBlock = object + inEdges: seq[ref BasicBlock] + outEdges: seq[ref BasicBlock] + name: int + +proc newBasicBlock(name: int): ref BasicBlock = + new(result) + result.inEdges = newSeq[ref BasicBlock]() + result.outEdges = newSeq[ref BasicBlock]() + result.name = name + +proc hash(x: ref BasicBlock): int {.inline.} = + result = x.name + +type + BasicBlockEdge = object + fr: ref BasicBlock + to: ref BasicBlock + + Cfg = object + basicBlockMap: Table[int, ref BasicBlock] + edgeList: seq[BasicBlockEdge] + startNode: ref BasicBlock + +proc newCfg(): Cfg = + result.basicBlockMap = initTable[int, ref BasicBlock]() + result.edgeList = newSeq[BasicBlockEdge]() + +proc createNode(self: var Cfg, name: int): ref BasicBlock = + #var node: ref BasicBlock + result = self.basicBlockMap.getOrDefault(name) + if result == nil: + result = newBasicBlock(name) + self.basicBlockMap.add name, result + + if self.startNode == nil: + self.startNode = result + +proc addEdge(self: var Cfg, edge: BasicBlockEdge) = + self.edgeList.add(edge) + +proc getNumNodes(self: Cfg): int = + self.basicBlockMap.len + +proc newBasicBlockEdge(cfg: var Cfg, fromName: int, toName: int) = + var newEdge = BasicBlockEdge() + newEdge.fr = cfg.createNode(fromName) + newEdge.to = cfg.createNode(toName) + newEdge.fr.outEdges.add(newEdge.to) + newEdge.to.inEdges.add(newEdge.fr) + cfg.addEdge(newEdge) + +type + SimpleLoop = object + basicBlocks: seq[ref BasicBlock] # TODO: set here + children: seq[ref SimpleLoop] # TODO: set here + parent: ref SimpleLoop + header: ref BasicBlock + isRoot: bool + isReducible: bool + counter: int + nestingLevel: int + depthLevel: int + +proc newSimpleLoop(): ref SimpleLoop = + new(result) + result.basicBlocks = newSeq[ref BasicBlock]() + result.children = newSeq[ref SimpleLoop]() + result.parent = nil + result.header = nil + result.isRoot = false + result.isReducible = true + result.counter = 0 + result.nestingLevel = 0 + result.depthLevel = 0 + +proc addNode(self: ref SimpleLoop, bb: ref BasicBlock) = + self.basicBlocks.add bb + +proc addChildLoop(self: ref SimpleLoop, loop: ref SimpleLoop) = + self.children.add loop + +proc setParent(self: ref SimpleLoop, parent: ref SimpleLoop) = + self.parent = parent + self.parent.addChildLoop(self) + +proc setHeader(self: ref SimpleLoop, bb: ref BasicBlock) = + self.basicBlocks.add(bb) + self.header = bb + +proc setNestingLevel(self: ref SimpleLoop, level: int) = + self.nestingLevel = level + if level == 0: self.isRoot = true + +var loop_counter: int = 0 + +type + Lsg = object + loops: seq[ref SimpleLoop] + root: ref SimpleLoop + +proc createNewLoop(self: var Lsg): ref SimpleLoop = + var s = newSimpleLoop() + loop_counter += 1 + s.counter = loop_counter + s + +proc addLoop(self: var Lsg, l: ref SimpleLoop) = + self.loops.add l + +proc newLsg(): Lsg = + result.loops = newSeq[ref SimpleLoop]() + result.root = result.createNewLoop() + result.root.setNestingLevel(0) + result.addLoop(result.root) + +proc getNumLoops(self: Lsg): int = + self.loops.len + +type + UnionFindNode = object + parent: ref UnionFindNode + bb: ref BasicBlock + l: ref SimpleLoop + dfsNumber: int + +proc newUnionFindNode(): ref UnionFindNode = + new(result) + result.parent = nil + result.bb = nil + result.l = nil + result.dfsNumber = 0 + +proc initNode(self: ref UnionFindNode, bb: ref BasicBlock, dfsNumber: int) = + self.parent = self + self.bb = bb + self.dfsNumber = dfsNumber + +proc findSet(self: ref UnionFindNode): ref UnionFindNode = + var nodeList = newSeq[ref UnionFindNode]() + var node = self + + while node != node.parent: + var parent = node.parent + if parent != parent.parent: nodeList.add node + node = parent + + for iter in nodeList: iter.parent = node.parent + node + +proc union(self: ref UnionFindNode, unionFindNode: ref UnionFindNode) = + self.parent = unionFindNode + + +const + BB_NONHEADER = 1 # a regular BB + BB_REDUCIBLE = 2 # reducible loop + BB_SELF = 3 # single BB loop + BB_IRREDUCIBLE = 4 # irreducible loop + BB_DEAD = 5 # a dead BB + + # # Marker for uninitialized nodes. + UNVISITED = -1 + + # # Safeguard against pathologic algorithm behavior. + MAXNONBACKPREDS = (32 * 1024) + +type + HavlakLoopFinder = object + cfg: Cfg + lsg: Lsg + +proc newHavlakLoopFinder(cfg: sink Cfg, lsg: sink Lsg): HavlakLoopFinder = + result.cfg = cfg + result.lsg = lsg + +proc isAncestor(w: int, v: int, last: seq[int]): bool = + w <= v and v <= last[w] + +proc dfs(currentNode: ref BasicBlock, nodes: var seq[ref UnionFindNode], number: var Table[ref BasicBlock, int], last: var seq[int], current: int): int = + nodes[current].initNode(currentNode, current) + number[currentNode] = current + + var lastid = current + for target in currentNode.outEdges: + if number[target] == UNVISITED: + lastid = dfs(target, nodes, number, last, lastid + 1) + + last[number[currentNode]] = lastid + return lastid + +proc findLoops(self: var HavlakLoopFinder): int = + var startNode = self.cfg.startNode + if startNode == nil: return 0 + var size = self.cfg.getNumNodes + + var nonBackPreds = newSeq[HashSet[int]]() + var backPreds = newSeq[seq[int]]() + var number = initTable[ref BasicBlock, int]() + var header = newSeq[int](size) + var types = newSeq[int](size) + var last = newSeq[int](size) + var nodes = newSeq[ref UnionFindNode]() + + for i in 1..size: + nonBackPreds.add initHashSet[int](1) + backPreds.add newSeq[int]() + nodes.add newUnionFindNode() + + # Step a: + # - initialize all nodes as unvisited. + # - depth-first traversal and numbering. + # - unreached BB's are marked as dead. + # + for v in self.cfg.basicBlockMap.values: number[v] = UNVISITED + discard dfs(startNode, nodes, number, last, 0) + + # Step b: + # - iterate over all nodes. + # + # A backedge comes from a descendant in the DFS tree, and non-backedges + # from non-descendants (following Tarjan). + # + # - check incoming edges 'v' and add them to either + # - the list of backedges (backPreds) or + # - the list of non-backedges (nonBackPreds) + # + for w in 0 ..< size: + header[w] = 0 + types[w] = BB_NONHEADER + + var nodeW = nodes[w].bb + if nodeW != nil: + for nodeV in nodeW.inEdges: + var v = number[nodeV] + if v != UNVISITED: + if isAncestor(w, v, last): + backPreds[w].add v + else: + nonBackPreds[w].incl v + else: + types[w] = BB_DEAD + + # Start node is root of all other loops. + header[0] = 0 + + # Step c: + # + # The outer loop, unchanged from Tarjan. It does nothing except + # for those nodes which are the destinations of backedges. + # For a header node w, we chase backward from the sources of the + # backedges adding nodes to the set P, representing the body of + # the loop headed by w. + # + # By running through the nodes in reverse of the DFST preorder, + # we ensure that inner loop headers will be processed before the + # headers for surrounding loops. + + for w in countdown(size - 1, 0): + # this is 'P' in Havlak's paper + var nodePool = newSeq[ref UnionFindNode]() + + var nodeW = nodes[w].bb + if nodeW != nil: # dead BB + # Step d: + for v in backPreds[w]: + if v != w: + nodePool.add nodes[v].findSet + else: + types[w] = BB_SELF + + # Copy nodePool to workList. + # + var workList = newSeq[ref UnionFindNode]() + for x in nodePool: workList.add x + + if nodePool.len != 0: types[w] = BB_REDUCIBLE + + # work the list... + # + while workList.len > 0: + var x = workList[0] + workList.del(0) + + # Step e: + # + # Step e represents the main difference from Tarjan's method. + # Chasing upwards from the sources of a node w's backedges. If + # there is a node y' that is not a descendant of w, w is marked + # the header of an irreducible loop, there is another entry + # into this loop that avoids w. + # + + # The algorithm has degenerated. Break and + # return in this case. + # + var nonBackSize = nonBackPreds[x.dfsNumber].len + if nonBackSize > MAXNONBACKPREDS: return 0 + + for iter in nonBackPreds[x.dfsNumber]: + var y = nodes[iter] + var ydash = y.findSet + + if not isAncestor(w, ydash.dfsNumber, last): + types[w] = BB_IRREDUCIBLE + nonBackPreds[w].incl ydash.dfsNumber + else: + if ydash.dfsNumber != w and not nodePool.contains(ydash): + workList.add ydash + nodePool.add ydash + + # Collapse/Unionize nodes in a SCC to a single node + # For every SCC found, create a loop descriptor and link it in. + # + if (nodePool.len > 0) or (types[w] == BB_SELF): + var l = self.lsg.createNewLoop + + l.setHeader(nodeW) + l.isReducible = types[w] != BB_IRREDUCIBLE + + # At this point, one can set attributes to the loop, such as: + # + # the bottom node: + # iter = backPreds(w).begin(); + # loop bottom is: nodes(iter).node; + # + # the number of backedges: + # backPreds(w).size() + # + # whether this loop is reducible: + # types(w) != BB_IRREDUCIBLE + # + nodes[w].l = l + + for node in nodePool: + # Add nodes to loop descriptor. + header[node.dfsNumber] = w + node.union(nodes[w]) + + # Nested loops are not added, but linked together. + var node_l = node.l + if node_l != nil: + node_l.setParent(l) + else: + l.addNode(node.bb) + + self.lsg.addLoop(l) + + result = self.lsg.getNumLoops + + +type + LoopTesterApp = object + cfg: Cfg + lsg: Lsg + +proc newLoopTesterApp(): LoopTesterApp = + result.cfg = newCfg() + result.lsg = newLsg() + +proc buildDiamond(self: var LoopTesterApp, start: int): int = + var bb0 = start + newBasicBlockEdge(self.cfg, bb0, bb0 + 1) + newBasicBlockEdge(self.cfg, bb0, bb0 + 2) + newBasicBlockEdge(self.cfg, bb0 + 1, bb0 + 3) + newBasicBlockEdge(self.cfg, bb0 + 2, bb0 + 3) + result = bb0 + 3 + +proc buildConnect(self: var LoopTesterApp, start1: int, end1: int) = + newBasicBlockEdge(self.cfg, start1, end1) + +proc buildStraight(self: var LoopTesterApp, start: int, n: int): int = + for i in 0..n-1: + self.buildConnect(start + i, start + i + 1) + result = start + n + +proc buildBaseLoop(self: var LoopTesterApp, from1: int): int = + var header = self.buildStraight(from1, 1) + var diamond1 = self.buildDiamond(header) + var d11 = self.buildStraight(diamond1, 1) + var diamond2 = self.buildDiamond(d11) + var footer = self.buildStraight(diamond2, 1) + + self.buildConnect(diamond2, d11) + self.buildConnect(diamond1, header) + self.buildConnect(footer, from1) + result = self.buildStraight(footer, 1) + +proc run(self: var LoopTesterApp) = + discard self.cfg.createNode(0) + discard self.buildBaseLoop(0) + discard self.cfg.createNode(1) + self.buildConnect(0, 2) + + for i in 1..8: + # yes, it's 8 and it's correct. + var h = newHavlakLoopFinder(self.cfg, newLsg()) + discard h.findLoops() + + echo "done" + +var l = newLoopTesterApp() +l.run() From fd9ab2ce6d3721545bec0d43c3e862eb3b458feb Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 15 Oct 2020 11:51:40 +0200 Subject: [PATCH 0772/3768] ORC: API extensions for 1.4 (#15581) --- lib/system/orc.nim | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/lib/system/orc.nim b/lib/system/orc.nim index 1d856696c..3c2327fd5 100644 --- a/lib/system/orc.nim +++ b/lib/system/orc.nim @@ -349,19 +349,36 @@ proc registerCycle(s: Cell; desc: PNimTypeV2) = collectCycles() #writeCell("[added root]", s) -proc GC_fullCollect* = - ## Forces a full garbage collection pass. With ``--gc:orc`` triggers the cycle - ## collector. +proc GC_runOrc* = + ## Forces a cycle collection pass. collectCycles() -proc GC_enableMarkAndSweep*() = +proc GC_enableOrc*() = + ## Enables the cycle collector subsystem of ``--gc:orc``. This is a ``--gc:orc`` + ## specific API. Check with ``when defined(gcOrc)`` for its existence. when not defined(nimStressOrc): rootsThreshold = defaultThreshold -proc GC_disableMarkAndSweep*() = +proc GC_disableOrc*() = + ## Disables the cycle collector subsystem of ``--gc:orc``. This is a ``--gc:orc`` + ## specific API. Check with ``when defined(gcOrc)`` for its existence. when not defined(nimStressOrc): rootsThreshold = high(int) + +proc GC_fullCollect* = + ## Forces a full garbage collection pass. With ``--gc:orc`` triggers the cycle + ## collector. This is an alias for ``GC_runOrc``. + collectCycles() + +proc GC_enableMarkAndSweep*() = + ## For ``--gc:orc`` an alias for ``GC_enableOrc``. + GC_enableOrc() + +proc GC_disableMarkAndSweep*() = + ## For ``--gc:orc`` an alias for ``GC_disableOrc``. + GC_disableOrc() + proc rememberCycle(isDestroyAction: bool; s: Cell; desc: PNimTypeV2) {.noinline.} = if isDestroyAction: if (s.rc and isCycleCandidate) != 0: From 42c180c665cb283bcb07e078a5ff91c69c84fa62 Mon Sep 17 00:00:00 2001 From: Igor Ribeiro de Assis Date: Thu, 15 Oct 2020 12:45:55 +0200 Subject: [PATCH 0773/3768] Fix crash in parsexml (#15582) (#15583) Co-authored-by: Igor Ribeiro de Assis --- lib/pure/parsexml.nim | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/pure/parsexml.nim b/lib/pure/parsexml.nim index 6ec77ebdb..79a9cc730 100644 --- a/lib/pure/parsexml.nim +++ b/lib/pure/parsexml.nim @@ -666,6 +666,9 @@ proc parseAttribute(my: var XmlParser) = parseEntity(my, my.b) my.kind = xmlAttribute # parseEntity overwrites my.kind! pos = my.bufpos + elif c == '/': + pos = lexbase.handleRefillChar(my, pos) + add(my.b, '/') else: add(my.b, c) inc(pos) From da4aa2e1fb8b0084fcf03b85edf4a5c1d0170856 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 15 Oct 2020 12:52:30 +0200 Subject: [PATCH 0774/3768] renamed '=' to '=copy' [backport:1.2] (#15585) * Assign hook name changed to `=copy` * Adapt destructors.rst * [nobackport] Duplicate tests for =copy hook * Fix tests * added a changelog entry Co-authored-by: Clyybber --- changelog.md | 4 + compiler/ast.nim | 2 +- compiler/injectdestructors.nim | 4 +- compiler/sempass2.nim | 3 +- compiler/semstmts.nim | 4 +- doc/destructors.rst | 22 +- tests/arc/tcaseobjcopy.nim | 246 ++++++++ tests/arc/tcomputedgotocopy.nim | 41 ++ tests/arc/tmovebugcopy.nim | 526 ++++++++++++++++++ tests/arc/topt_no_cursor.nim | 12 +- tests/arc/topt_wasmoved_destroy_pairs.nim | 2 +- tests/arc/tweavecopy.nim | 154 +++++ tests/destructor/tconsume_twice.nim | 2 +- tests/destructor/tprevent_assign.nim | 2 +- tests/destructor/tprevent_assign2.nim | 2 +- tests/destructor/tprevent_assign3.nim | 2 +- tests/destructor/tuse_ownedref_after_move.nim | 2 +- 17 files changed, 1001 insertions(+), 29 deletions(-) create mode 100644 tests/arc/tcaseobjcopy.nim create mode 100644 tests/arc/tcomputedgotocopy.nim create mode 100644 tests/arc/tmovebugcopy.nim create mode 100644 tests/arc/tweavecopy.nim diff --git a/changelog.md b/changelog.md index 2c8f9b5e2..14bcbe608 100644 --- a/changelog.md +++ b/changelog.md @@ -215,6 +215,10 @@ - The `=destroy` hook no longer has to reset its target, as the compiler now automatically inserts `wasMoved` calls where needed. +- The `=` hook is now called `=copy` for clarity. The old name `=` is still available so there + is no need to update your code. This change was backported to 1.2 too so you can use the + more readability `=copy` without loss of compatibility. + - In the newruntime it is now allowed to assign to the discriminator field without restrictions as long as case object doesn't have custom destructor. The discriminator value doesn't have to be a constant either. If you have a diff --git a/compiler/ast.nim b/compiler/ast.nim index 8adbfa15c..f796e64c2 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1331,7 +1331,7 @@ const MaxLockLevel* = 1000'i16 UnknownLockLevel* = TLockLevel(1001'i16) AttachedOpToStr*: array[TTypeAttachedOp, string] = [ - "=destroy", "=", "=sink", "=trace", "=dispose", "=deepcopy"] + "=destroy", "=copy", "=sink", "=trace", "=dispose", "=deepcopy"] proc `$`*(x: TLockLevel): string = if x.ord == UnspecifiedLockLevel.ord: result = "" diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index b18bcf34e..ea90299e2 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -235,7 +235,7 @@ template isUnpackedTuple(n: PNode): bool = proc checkForErrorPragma(c: Con; t: PType; ri: PNode; opname: string) = var m = "'" & opname & "' is not available for type <" & typeToString(t) & ">" - if opname == "=" and ri != nil: + if (opname == "=" or opname == "=copy") and ri != nil: m.add "; requires a copy because it's not the last read of '" m.add renderTree(ri) m.add '\'' @@ -319,7 +319,7 @@ proc genCopy(c: var Con; dest, ri: PNode): PNode = if tfHasOwned in t.flags and ri.kind != nkNilLit: # try to improve the error message here: if c.otherRead == nil: discard isLastRead(ri, c) - c.checkForErrorPragma(t, ri, "=") + c.checkForErrorPragma(t, ri, "=copy") result = c.genCopyNoCheck(dest, ri) proc genDiscriminantAsgn(c: var Con; s: var Scope; n: PNode): PNode = diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 5eb464bb8..aa04f7451 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -809,7 +809,8 @@ proc trackCall(tracked: PEffects; n: PNode) = if a.kind == nkSym and a.sym.name.s.len > 0 and a.sym.name.s[0] == '=' and tracked.owner.kind != skMacro: - let opKind = find(AttachedOpToStr, a.sym.name.s.normalize) + var opKind = find(AttachedOpToStr, a.sym.name.s.normalize) + if a.sym.name.s.normalize == "=": opKind = attachedAsgn.int if opKind != -1: # rebind type bounds operations after createTypeBoundOps call let t = n[1].typ.skipTypes({tyAlias, tyVar}) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 27cd9019f..b5e69c135 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1732,7 +1732,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = "signature for 'deepCopy' must be proc[T: ptr|ref](x: T): T") incl(s.flags, sfUsed) incl(s.flags, sfOverriden) - of "=", "=sink": + of "=", "=copy", "=sink": if s.magic == mAsgn: return incl(s.flags, sfUsed) incl(s.flags, sfOverriden) @@ -1754,7 +1754,7 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = # attach these ops to the canonical tySequence obj = canonType(c, obj) #echo "ATTACHING TO ", obj.id, " ", s.name.s, " ", cast[int](obj) - let k = if name == "=": attachedAsgn else: attachedSink + let k = if name == "=" or name == "=copy": attachedAsgn else: attachedSink if obj.attachedOps[k] == s: discard "forward declared op" elif obj.attachedOps[k].isNil and tfCheckedForDestructor notin obj.flags: diff --git a/doc/destructors.rst b/doc/destructors.rst index b433c38d7..b581fce3e 100644 --- a/doc/destructors.rst +++ b/doc/destructors.rst @@ -40,7 +40,7 @@ written as: for i in 0..= 0 and k <= n + var pos = newSeq[int](k) + var current = newSeq[T](k) + for i in 0..k-1: + pos[k-i-1] = i + var done = false + while not done: + for i in 0..k-1: + current[i] = s[pos[k-i-1]] + yield current + var i = 0 + while i < k: + pos[i] += 1 + if pos[i] < n-i: + for j in 0..i-1: + pos[j] = pos[i] + i - j + break + i += 1 + if i >= k: + break + +type + UndefEx = object of ValueError + +proc main2 = + var delayedSyms = @[1, 2, 3] + var unp: seq[int] + block myb: + for a in 1 .. 2: + if delayedSyms.len > a: + unp = delayedSyms + for t in unp.combinations(a + 1): + try: + var h = false + for k in t: + echo "fff" + if h: continue + if true: + raise newException(UndefEx, "forward declaration") + break myb + except UndefEx: + echo t.len + echo "mmm" + +main2() + + + +type ME = object + who: string + +proc `=copy`(x: var ME, y: ME) = + if y.who.len > 0: echo "assign ",y.who + +proc `=sink`(x: var ME, y: ME) = + if y.who.len > 0: echo "sink ",y.who + +var dump: ME +template use(x) = dump = x +template def(x) = x = dump + +var c = true + +proc shouldSink() = + var x = ME(who: "me (sink)") + use(x) # we analyse this + if c: def(x) + else: def(x) + use(x) # ok, with the [else] part. + +shouldSink() + +dump = ME() + +proc shouldNotSink() = + var x = ME(who: "me (not sink)") + use(x) # we analyse this + if c: def(x) + use(x) # Not ok without the '[else]' + +shouldNotSink() + +# bug #14568 +import os + +type O2 = object + s: seq[int] + +proc `=sink`(dest: var O2, src: O2) = + echo "sinked and not optimized to a bitcopy" + +var testSeq: O2 + +proc update() = + # testSeq.add(0) # uncommenting this line fixes the leak + testSeq = O2(s: @[]) + testSeq.s.add(0) + +for i in 1..3: + update() + + +# bug #14961 +type + Foo = object + data: seq[int] + +proc initFoo(len: int): Foo = + result = (let s = newSeq[int](len); Foo(data: s) ) + +var f = initFoo(2) +echo initFoo(2) + +proc initFoo2(len: int) = + echo if true: + let s = newSeq[int](len); Foo(data: s) + else: + let s = newSeq[int](len); Foo(data: s) + +initFoo2(2) + +proc initFoo3(len: int) = + echo (block: + let s = newSeq[int](len); Foo(data: s)) + +initFoo3(2) + +proc initFoo4(len: int) = + echo (let s = newSeq[int](len); Foo(data: s)) + +initFoo4(2) + +proc initFoo5(len: int) = + echo (case true + of true: + let s = newSeq[int](len); Foo(data: s) + of false: + let s = newSeq[int](len); Foo(data: s)) + +initFoo5(2) + +proc initFoo6(len: int) = + echo (block: + try: + let s = newSeq[int](len); Foo(data: s) + finally: discard) + +initFoo6(2) + +proc initFoo7(len: int) = + echo (block: + try: + raise newException(CatchableError, "sup") + let s = newSeq[int](len); Foo(data: s) + except CatchableError: + let s = newSeq[int](len); Foo(data: s) ) + +initFoo7(2) + + +# bug #14902 +iterator zip[T](s: openarray[T]): (T, T) = + var i = 0 + while i < 10: + yield (s[i mod 2], s[i mod 2 + 1]) + inc i + +var lastMem = int.high + +proc leak = + const len = 10 + var x = @[newString(len), newString(len), newString(len)] + + var c = 0 + for (a, b) in zip(x): + let newMem = getOccupiedMem() + assert newMem <= lastMem + lastMem = newMem + c += a.len + echo c + +leak() + + +proc consume(a: sink string) = echo a + +proc weirdScopes = + if (let a = "hey"; a.len > 0): + echo a + + while (let a = "hey"; a.len > 0): + echo a + break + + var a = block: (a: "a", b: 2) + echo a + (discard; a) = (echo "ho"; (a: "b", b: 3)) + echo a + + var b = try: (b: "b", a: 2) + except: raise + echo b + (discard; b) = (echo "ho"; (b: "a", a: 3)) + echo b + + var s = "break" + consume((echo "hey"; s)) + echo s + + echo (block: + var a = "hey" + (echo "hey"; "ho")) + + var b2 = "ho" + echo (block: + var a = "hey" + (echo "hey"; b2)) + echo b2 + + type status = enum + alive + + var king = "king" + echo (block: + var a = "a" + when true: + var b = "b" + case alive + of alive: + try: + var c = "c" + if true: + king + else: + "the abyss" + except: + echo "he ded" + "dead king") + echo "live long; long live" + echo king + +weirdScopes() + + +# bug #14985 +proc getScope(): string = + if true: + "hi" + else: + "else" + +echo getScope() + +proc getScope3(): string = + try: + "try" + except: + "except" + +echo getScope3() + +proc getScope2(): string = + case true + of true: + "bye" + else: + "else" + +echo getScope2() diff --git a/tests/arc/topt_no_cursor.nim b/tests/arc/topt_no_cursor.nim index 5fd21439a..d5811e91e 100644 --- a/tests/arc/topt_no_cursor.nim +++ b/tests/arc/topt_no_cursor.nim @@ -33,9 +33,9 @@ result = ( var sibling saved -`=`(sibling, target.parent.left) -`=`(saved, sibling.right) -`=`(sibling.right, saved.left) +`=copy`(sibling, target.parent.left) +`=copy`(saved, sibling.right) +`=copy`(sibling.right, saved.left) `=sink`(sibling.parent, saved) `=destroy`(sibling) -- end of expandArc ------------------------ @@ -46,7 +46,7 @@ var lvalue lnext _ -`=`(lresult, [123]) +`=copy`(lresult, [123]) _ = ( let blitTmp = lresult blitTmp, ";") @@ -67,10 +67,10 @@ try: var it_cursor = x a = ( wasMoved(:tmpD) - `=`(:tmpD, it_cursor.key) + `=copy`(:tmpD, it_cursor.key) :tmpD, wasMoved(:tmpD_1) - `=`(:tmpD_1, it_cursor.val) + `=copy`(:tmpD_1, it_cursor.val) :tmpD_1) echo [ :tmpD_2 = `$`(a) diff --git a/tests/arc/topt_wasmoved_destroy_pairs.nim b/tests/arc/topt_wasmoved_destroy_pairs.nim index 2a6391dd2..2f971f112 100644 --- a/tests/arc/topt_wasmoved_destroy_pairs.nim +++ b/tests/arc/topt_wasmoved_destroy_pairs.nim @@ -38,7 +38,7 @@ try: return add(a): wasMoved(:tmpD) - `=`(:tmpD, x) + `=copy`(:tmpD, x) :tmpD inc i_1, 1 if cond: diff --git a/tests/arc/tweavecopy.nim b/tests/arc/tweavecopy.nim new file mode 100644 index 000000000..fc796b352 --- /dev/null +++ b/tests/arc/tweavecopy.nim @@ -0,0 +1,154 @@ +discard """ + outputsub: '''Success''' + cmd: '''nim c --gc:arc --threads:on $file''' + disabled: "bsd" +""" + +# bug #13936 + +import std/atomics + +const MemBlockSize = 256 + +type + ChannelSPSCSingle* = object + full{.align: 128.}: Atomic[bool] + itemSize*: uint8 + buffer*{.align: 8.}: UncheckedArray[byte] + +proc `=copy`( + dest: var ChannelSPSCSingle, + source: ChannelSPSCSingle + ) {.error: "A channel cannot be copied".} + +proc initialize*(chan: var ChannelSPSCSingle, itemsize: SomeInteger) {.inline.} = + ## If ChannelSPSCSingle is used intrusive another data structure + ## be aware that it should be the last part due to ending by UncheckedArray + ## Also due to 128 bytes padding, it automatically takes half + ## of the default MemBlockSize + assert itemsize.int in 0 .. int high(uint8) + assert itemSize.int + + sizeof(chan.itemsize) + + sizeof(chan.full) < MemBlockSize + + chan.itemSize = uint8 itemsize + chan.full.store(false, moRelaxed) + +func isEmpty*(chan: var ChannelSPSCSingle): bool {.inline.} = + not chan.full.load(moAcquire) + +func tryRecv*[T](chan: var ChannelSPSCSingle, dst: var T): bool {.inline.} = + ## Try receiving the item buffered in the channel + ## Returns true if successful (channel was not empty) + ## + ## ⚠ Use only in the consumer thread that reads from the channel. + assert (sizeof(T) == chan.itemsize.int) or + # Support dummy object + (sizeof(T) == 0 and chan.itemsize == 1) + + let full = chan.full.load(moAcquire) + if not full: + return false + dst = cast[ptr T](chan.buffer.addr)[] + chan.full.store(false, moRelease) + return true + +func trySend*[T](chan: var ChannelSPSCSingle, src: sink T): bool {.inline.} = + ## Try sending an item into the channel + ## Reurns true if successful (channel was empty) + ## + ## ⚠ Use only in the producer thread that writes from the channel. + assert (sizeof(T) == chan.itemsize.int) or + # Support dummy object + (sizeof(T) == 0 and chan.itemsize == 1) + + let full = chan.full.load(moAcquire) + if full: + return false + cast[ptr T](chan.buffer.addr)[] = src + chan.full.store(true, moRelease) + return true + +# Sanity checks +# ------------------------------------------------------------------------------ +when isMainModule: + + when not compileOption("threads"): + {.error: "This requires --threads:on compilation flag".} + + template sendLoop[T](chan: var ChannelSPSCSingle, + data: sink T, + body: untyped): untyped = + while not chan.trySend(data): + body + + template recvLoop[T](chan: var ChannelSPSCSingle, + data: var T, + body: untyped): untyped = + while not chan.tryRecv(data): + body + + type + ThreadArgs = object + ID: WorkerKind + chan: ptr ChannelSPSCSingle + + WorkerKind = enum + Sender + Receiver + + template Worker(id: WorkerKind, body: untyped): untyped {.dirty.} = + if args.ID == id: + body + + proc thread_func(args: ThreadArgs) = + + # Worker RECEIVER: + # --------- + # <- chan + # <- chan + # <- chan + # + # Worker SENDER: + # --------- + # chan <- 42 + # chan <- 53 + # chan <- 64 + Worker(Receiver): + var val: int + for j in 0 ..< 10: + args.chan[].recvLoop(val): + # Busy loop, in prod we might want to yield the core/thread timeslice + discard + echo " Receiver got: ", val + doAssert val == 42 + j*11 + + Worker(Sender): + doAssert args.chan.full.load(moRelaxed) == false + for j in 0 ..< 10: + let val = 42 + j*11 + args.chan[].sendLoop(val): + # Busy loop, in prod we might want to yield the core/thread timeslice + discard + echo "Sender sent: ", val + + proc main() = + echo "Testing if 2 threads can send data" + echo "-----------------------------------" + var threads: array[2, Thread[ThreadArgs]] + + var chan = cast[ptr ChannelSPSCSingle](allocShared(MemBlockSize)) + chan[].initialize(itemSize = sizeof(int)) + + createThread(threads[0], thread_func, ThreadArgs(ID: Receiver, chan: chan)) + createThread(threads[1], thread_func, ThreadArgs(ID: Sender, chan: chan)) + + joinThread(threads[0]) + joinThread(threads[1]) + + freeShared(chan) + + echo "-----------------------------------" + echo "Success" + + main() diff --git a/tests/destructor/tconsume_twice.nim b/tests/destructor/tconsume_twice.nim index 0030267f8..b0a039e9b 100644 --- a/tests/destructor/tconsume_twice.nim +++ b/tests/destructor/tconsume_twice.nim @@ -1,6 +1,6 @@ discard """ cmd: "nim c --newruntime $file" - errormsg: "'=' is not available for type ; requires a copy because it's not the last read of 'a'; another read is done here: tconsume_twice.nim(13, 10); routine: consumeTwice" + errormsg: "'=copy' is not available for type ; requires a copy because it's not the last read of 'a'; another read is done here: tconsume_twice.nim(13, 10); routine: consumeTwice" line: 11 """ type diff --git a/tests/destructor/tprevent_assign.nim b/tests/destructor/tprevent_assign.nim index 108ccc371..4c484ebc1 100644 --- a/tests/destructor/tprevent_assign.nim +++ b/tests/destructor/tprevent_assign.nim @@ -1,5 +1,5 @@ discard """ - errormsg: "'=' is not available for type ; requires a copy because it's not the last read of 'otherTree'" + errormsg: "'=copy' is not available for type ; requires a copy because it's not the last read of 'otherTree'" line: 29 """ diff --git a/tests/destructor/tprevent_assign2.nim b/tests/destructor/tprevent_assign2.nim index 0e4481710..ef20672d5 100644 --- a/tests/destructor/tprevent_assign2.nim +++ b/tests/destructor/tprevent_assign2.nim @@ -1,5 +1,5 @@ discard """ - errormsg: "'=' is not available for type ; requires a copy because it's not the last read of 'otherTree'" + errormsg: "'=copy' is not available for type ; requires a copy because it's not the last read of 'otherTree'" file: "tprevent_assign2.nim" line: 48 """ diff --git a/tests/destructor/tprevent_assign3.nim b/tests/destructor/tprevent_assign3.nim index a8a35ea5e..0577aa5ff 100644 --- a/tests/destructor/tprevent_assign3.nim +++ b/tests/destructor/tprevent_assign3.nim @@ -1,5 +1,5 @@ discard """ - errormsg: "'=' is not available for type ; requires a copy because it's not the last read of 'otherTree'" + errormsg: "'=copy' is not available for type ; requires a copy because it's not the last read of 'otherTree'" file: "tprevent_assign3.nim" line: 46 """ diff --git a/tests/destructor/tuse_ownedref_after_move.nim b/tests/destructor/tuse_ownedref_after_move.nim index 46540837c..ce96b741e 100644 --- a/tests/destructor/tuse_ownedref_after_move.nim +++ b/tests/destructor/tuse_ownedref_after_move.nim @@ -1,6 +1,6 @@ discard """ cmd: '''nim c --newruntime $file''' - errormsg: "'=' is not available for type ; requires a copy because it's not the last read of ':envAlt.b1'; another read is done here: tuse_ownedref_after_move.nim(52, 4)" + errormsg: "'=copy' is not available for type ; requires a copy because it's not the last read of ':envAlt.b1'; another read is done here: tuse_ownedref_after_move.nim(52, 4)" line: 48 """ From 4ef255b69d00d66e4554bad72fab1780578792f5 Mon Sep 17 00:00:00 2001 From: shirleyquirk <31934565+shirleyquirk@users.noreply.github.com> Date: Thu, 15 Oct 2020 11:54:01 +0100 Subject: [PATCH 0775/3768] fix rlock compilation failure (#15584) * change SysLockType_Reentrant fix edge case where using SysLockType_Reentrant doesn't trigger an #include pthread.h * syslocktype_reentrant now a var * remove nodecl to remove empty system_syslocks.c * let is better than var. in reality SysLockType = enum, maybe that would be a better fix --- lib/core/rlocks.nim | 2 +- lib/system/syslocks.nim | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/core/rlocks.nim b/lib/core/rlocks.nim index 34d7687f9..27a9c5e60 100644 --- a/lib/core/rlocks.nim +++ b/lib/core/rlocks.nim @@ -24,7 +24,7 @@ proc initRLock*(lock: var RLock) {.inline.} = when defined(posix): var a: SysLockAttr initSysLockAttr(a) - setSysLockType(a, SysLockType_Reentrant()) + setSysLockType(a, SysLockType_Reentrant) initSysLock(lock, a.addr) else: initSysLock(lock) diff --git a/lib/system/syslocks.nim b/lib/system/syslocks.nim index b7f7aa6bf..07fb9cb8a 100644 --- a/lib/system/syslocks.nim +++ b/lib/system/syslocks.nim @@ -181,8 +181,8 @@ else: releaseSysAux(L) when insideRLocksModule: - proc SysLockType_Reentrant: SysLockType = - {.emit: "`result` = PTHREAD_MUTEX_RECURSIVE;".} + let SysLockType_Reentrant{.importc: "PTHREAD_MUTEX_RECURSIVE", + header: "".}: SysLockType proc initSysLockAttr(a: var SysLockAttr) {. importc: "pthread_mutexattr_init", header: "", noSideEffect.} proc setSysLockType(a: var SysLockAttr, t: SysLockType) {. From a43202ea57f49a3b49927f114669335a7d40dbb2 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 15 Oct 2020 15:49:00 +0200 Subject: [PATCH 0776/3768] fixes #15560 (#15587) --- lib/impure/db_mysql.nim | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/impure/db_mysql.nim b/lib/impure/db_mysql.nim index c3bcee677..82f27537c 100644 --- a/lib/impure/db_mysql.nim +++ b/lib/impure/db_mysql.nim @@ -134,8 +134,7 @@ proc dbQuote*(s: string): string = of '\'': result.add "\\'" of '\\': result.add "\\\\" of '_': result.add "\\_" - of Letters+Digits: result.add c - else: result.add "\\" & $ord(c) + else: result.add c add(result, '\'') proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string = From b50a0ee95c78cd5137b6a4291471bcbda1e69341 Mon Sep 17 00:00:00 2001 From: n5m <72841454+n5m@users.noreply.github.com> Date: Thu, 15 Oct 2020 23:43:20 +0000 Subject: [PATCH 0777/3768] fix option formatting (#15591) --- doc/gc.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/gc.rst b/doc/gc.rst index b118b1f8a..ee7f9000e 100644 --- a/doc/gc.rst +++ b/doc/gc.rst @@ -35,7 +35,7 @@ To choose the memory management strategy use the ``--gc:`` switch. It offers deterministic performance for `hard realtime`:idx: systems. Reference cycles cause memory leaks, beware. -- ``--gc:orc``. Same as ``-gc:arc`` but adds a cycle collector based on "trial deletion". +- ``--gc:orc``. Same as ``--gc:arc`` but adds a cycle collector based on "trial deletion". Unfortunately that makes its performance profile hard to reason about so it is less useful for hard realtime systems. From 6349001879b18c8934a3427d1dfec49ef2ac46e4 Mon Sep 17 00:00:00 2001 From: narimiran Date: Fri, 16 Oct 2020 07:16:46 +0200 Subject: [PATCH 0778/3768] create a changelog for 1.4.0 --- changelog.md | 355 +------------------------------- changelogs/changelog_1_4_0.md | 371 ++++++++++++++++++++++++++++++++++ 2 files changed, 372 insertions(+), 354 deletions(-) create mode 100644 changelogs/changelog_1_4_0.md diff --git a/changelog.md b/changelog.md index 14bcbe608..f585f4e15 100644 --- a/changelog.md +++ b/changelog.md @@ -1,371 +1,18 @@ -# v1.4.0 - yyyy-mm-dd +# v1.6.x - yyyy-mm-dd ## Standard library additions and changes -- Added some enhancements to `std/jsonutils` module. - * Added a possibility to deserialize JSON arrays directly to `HashSet` and - `OrderedSet` types and respectively to serialize those types to JSON arrays - via `jsonutils.fromJson` and `jsonutils.toJson` procedures. - * Added a possibility to deserialize JSON `null` objects to Nim option objects - and respectively to serialize Nim option object to JSON object if `isSome` - or to JSON null object if `isNone` via `jsonutils.fromJson` and - `jsonutils.toJson` procedures. - * Added `Joptions` parameter to `jsonutils.fromJson` procedure currently - containing two boolean options `allowExtraKeys` and `allowMissingKeys`. - - If `allowExtraKeys` is `true` Nim's object to which the JSON is parsed is - not required to have a field for every JSON key. - - If `allowMissingKeys` is `true` Nim's object to which JSON is parsed is - allowed to have fields without corresponding JSON keys. -- Added `bindParams`, `bindParam` to `db_sqlite` for binding parameters into a `SqlPrepared` statement. -- Added `tryInsert`,`insert` procs to `db_*` libs accept primary key column name. -- Added `xmltree.newVerbatimText` support create `style`'s,`script`'s text. -- `uri` adds Data URI Base64, implements RFC-2397. -- Added [DOM Parser](https://developer.mozilla.org/en-US/docs/Web/API/DOMParser) - to the `dom` module for the JavaScript target. -- The default hash for `Ordinal` has changed to something more bit-scrambling. - `import hashes; proc hash(x: myInt): Hash = hashIdentity(x)` recovers the old - one in an instantiation context while `-d:nimIntHash1` recovers it globally. -- `deques.peekFirst` and `deques.peekLast` now have `var Deque[T] -> var T` overloads. -- File handles created from high-level abstractions in the stdlib will no longer - be inherited by child processes. In particular, these modules are affected: - `asyncdispatch`, `asyncnet`, `system`, `nativesockets`, `net` and `selectors`. - For `asyncdispatch`, `asyncnet`, `net` and `nativesockets`, an `inheritable` - flag has been added to all `proc`s that create sockets, allowing the user to - control whether the resulting socket is inheritable. This flag is provided to - ease the writing of multi-process servers, where sockets inheritance is - desired. - - For a transition period, define `nimInheritHandles` to enable file handle - inheritance by default. This flag does **not** affect the `selectors` module - due to the differing semantics between operating systems. - - `asyncdispatch.setInheritable`, `system.setInheritable` and - `nativesockets.setInheritable` is also introduced for setting file handle or - socket inheritance. Not all platform have these `proc`s defined. - -- The file descriptors created for internal bookkeeping by `ioselector_kqueue` - and `ioselector_epoll` will no longer be leaked to child processes. - -- `strutils.formatFloat` with `precision = 0` has been restored to the version - 1 behaviour that produces a trailing dot, e.g. `formatFloat(3.14159, precision = 0)` - is now `3.`, not `3`. -- `critbits` adds `commonPrefixLen`. - -- `relativePath(rel, abs)` and `relativePath(abs, rel)` used to silently give wrong results - (see #13222); instead they now use `getCurrentDir` to resolve those cases, - and this can now throw in edge cases where `getCurrentDir` throws. - `relativePath` also now works for js with `-d:nodejs`. - -- JavaScript and NimScript standard library changes: `streams.StringStream` is - now supported in JavaScript, with the limitation that any buffer `pointer`s - used must be castable to `ptr string`, any incompatible pointer type will not - work. The `lexbase` and `streams` modules used to fail to compile on - NimScript due to a bug, but this has been fixed. - - The following modules now compile on both JS and NimScript: `parsecsv`, - `parsecfg`, `parsesql`, `xmlparser`, `htmlparser` and `ropes`. Additionally - supported for JS is `cstrutils.startsWith` and `cstrutils.endsWith`, for - NimScript: `json`, `parsejson`, `strtabs` and `unidecode`. - -- Added `streams.readStr` and `streams.peekStr` overloads to - accept an existing string to modify, which avoids memory - allocations, similar to `streams.readLine` (#13857). - -- Added high-level `asyncnet.sendTo` and `asyncnet.recvFrom` UDP functionality. - -- `dollars.$` now works for unsigned ints with `nim js` - -- Improvements to the `bitops` module, including bitslices, non-mutating versions - of the original masking functions, `mask`/`masked`, and varargs support for - `bitand`, `bitor`, and `bitxor`. - -- `sugar.=>` and `sugar.->` changes: Previously `(x, y: int)` was transformed - into `(x: auto, y: int)`, it now becomes `(x: int, y: int)` in consistency - with regular proc definitions (although you cannot use semicolons). - - Pragmas and using a name are now allowed on the lefthand side of `=>`. Here - is an aggregate example of these changes: - ```nim - import sugar - - foo(x, y: int) {.noSideEffect.} => x + y - - # is transformed into - - proc foo(x: int, y: int): auto {.noSideEffect.} = x + y - ``` - -- The fields of `times.DateTime` are now private, and are accessed with getters and deprecated setters. - -- The `times` module now handles the default value for `DateTime` more consistently. - Most procs raise an assertion error when given - an uninitialized `DateTime`, the exceptions are `==` and `$` (which returns `"Uninitialized DateTime"`). - The proc `times.isInitialized` has been added which can be used to check if - a `DateTime` has been initialized. - -- 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`. -- Removed `sugar.distinctBase`, deprecated since `0.19`. -- Export `asyncdispatch.PDispatcher.handles` so that an external library can register them. - -- `std/with`, `sugar.dup` now support object field assignment expression: - ```nim - import std/with - - type Foo = object - x, y: int - - var foo = Foo() - with foo: - x = 10 - y = 20 - - echo foo - ``` - -- Proc `math.round` is no longer deprecated. The advice to use `strformat` instead - cannot be applied to every use case. The limitations and the (lack of) reliability - of `round` are well documented. - -- Added `getprotobyname` to `winlean`. Added `getProtoByname` to `nativesockets` which returns a protocol code - from the database that matches the protocol `name`. - -- Added missing attributes and methods to `dom.Navigator` like `deviceMemory`, `onLine`, `vibrate()`, etc. - -- Added `strutils.indentation` and `strutils.dedent` which enable indented string literals: - ```nim - import strutils - echo dedent """ - This - is - cool! - """ - ``` - -- Added `initUri(isIpv6: bool)` to `uri` module, now `uri` supports parsing ipv6 hostname. - -- Added `readLines(p: Process)` to `osproc` module for `startProcess` convenience. - -- Added the below `to` procs for collections. The usage is similar to procs such as - `sets.toHashSet` and `tables.toTable`. Previously, it was necessary to create the - respective empty collection and add items manually. - * `critbits.toCritBitTree`, which creates a `CritBitTree` from an `openArray` of - items or an `openArray` of pairs. - * `deques.toDeque`, which creates a `Deque` from an `openArray`. - * `heapqueue.toHeapQueue`, which creates a `HeapQueue` from an `openArray`. - * `intsets.toIntSet`, which creates an `IntSet` from an `openArray`. - -- Added `progressInterval` argument to `asyncftpclient.newAsyncFtpClient` to control the interval - at which progress callbacks are called. - -- Added `os.copyFileToDir` ## Language changes -- The `=destroy` hook no longer has to reset its target, as the compiler now automatically inserts - `wasMoved` calls where needed. -- The `=` hook is now called `=copy` for clarity. The old name `=` is still available so there - is no need to update your code. This change was backported to 1.2 too so you can use the - more readability `=copy` without loss of compatibility. - -- In the newruntime it is now allowed to assign to the discriminator field - without restrictions as long as case object doesn't have custom destructor. - The discriminator value doesn't have to be a constant either. If you have a - custom destructor for a case object and you do want to freely assign discriminator - fields, it is recommended to refactor object into 2 objects like this: - - ```nim - type - MyObj = object - case kind: bool - of true: y: ptr UncheckedArray[float] - of false: z: seq[int] - - proc `=destroy`(x: MyObj) = - if x.kind and x.y != nil: - deallocShared(x.y) - ``` - Refactor into: - ```nim - type - MySubObj = object - val: ptr UncheckedArray[float] - MyObj = object - case kind: bool - of true: y: MySubObj - of false: z: seq[int] - - proc `=destroy`(x: MySubObj) = - if x.val != nil: - deallocShared(x.val) - ``` -- `getImpl` on enum type symbols now returns field syms instead of idents. This helps - with writing typed macros. Old behavior for backwards compatibility can be restored - with `--useVersion:1.0`. -- The typed AST for proc headers will now have the arguments be syms instead of idents. - This helps with writing typed macros. Old behaviour for backwards compatibility can - be restored with `--useVersion:1.0`. -- ``let`` statements can now be used without a value if declared with - ``importc``/``importcpp``/``importjs``/``importobjc``. -- The keyword `from` is now usable as an operator. -- Exceptions inheriting from `system.Defect` are no longer tracked with - the `.raises: []` exception tracking mechanism. This is more consistent with the - built-in operations. The following always used to compile (and still does): - -```nim - -proc mydiv(a, b): int {.raises: [].} = - a div b # can raise an DivByZeroDefect - -``` - - Now also this compiles: - -```nim - -proc mydiv(a, b): int {.raises: [].} = - if b == 0: raise newException(DivByZeroDefect, "division by zero") - else: result = a div b - -``` - - The reason for this is that `DivByZeroDefect` inherits from `Defect` and - with `--panics:on` `Defects` become unrecoverable errors. - -- Added the `thiscall` calling convention as specified by Microsoft, mostly for hooking purposes. -- Deprecated `{.unroll.}` pragma, was ignored by the compiler anyways, was a nop. -- Removed `strutils.isNilOrWhitespace`, was deprecated. -- Removed `sharedtables.initSharedTable`, was deprecated and produces undefined behavior. -- Removed `asyncdispatch.newAsyncNativeSocket`, was deprecated since `0.18`. -- Removed `dom.releaseEvents` and `dom.captureEvents`, was deprecated. - -- Removed `sharedlists.initSharedList`, was deprecated and produces undefined behaviour. - -- There is a new experimental feature called "strictFuncs" which makes the definition of - `.noSideEffect` stricter. [See](manual_experimental.html#stricts-funcs) - for more information. - -- "for-loop macros" (see [the manual](manual.html#macros-for-loop-macros)) are no longer - an experimental feature. In other words, you don't have to write pragma - `{.experimental: "forLoopMacros".}` if you want to use them. - -- Added a ``.noalias`` pragma. It is mapped to C's ``restrict`` keyword for the increased - performance this keyword can enable. - -- `items` no longer compiles with enum with holes as its behavior was error prone, see #14004 -- `system.deepcopy` has to be enabled explicitly for `--gc:arc` and `--gc:orc` via - `--deepcopy:on`. - -- Added a `std/effecttraits` module for introspection of the inferred effects. - We hope this enables `async` macros that are precise about the possible exceptions that - can be raised. -- The pragma blocks `{.gcsafe.}: ...` and `{.noSideEffect.}: ...` can now also be - written as `{.cast(gcsafe).}: ...` and `{.cast(noSideEffect).}: ...`. This is the new - preferred way of writing these, emphasizing their unsafe nature. ## Compiler changes -- Specific warnings can now be turned into errors via `--warningAsError[X]:on|off`. -- The `define` and `undef` pragmas have been de-deprecated. -- New command: `nim r main.nim [args...]` which compiles and runs main.nim, and implies `--usenimcache` - so that output is saved to $nimcache/main$exeExt, using the same logic as `nim c -r` to - avoid recompiling when sources don't change. This is now the preferred way to - run tests, avoiding the usual pain of clobbering your repo with binaries or - using tricky gitignore rules on posix. Example: - ```nim - nim r compiler/nim.nim --help # only compiled the first time - echo 'import os; echo getCurrentCompilerExe()' | nim r - # this works too - nim r compiler/nim.nim --fullhelp # no recompilation - nim r --nimcache:/tmp main # binary saved to /tmp/main - ``` -- `--hint:processing` is now supported and means `--hint:processing:on` - (likewise with other hints and warnings), which is consistent with all other bool flags. - (since 1.3.3). -- `nim doc -r main` and `nim rst2html -r main` now call `openDefaultBrowser`. -- new hint: `--hint:msgOrigin` will show where a compiler msg (hint|warning|error) - was generated; this helps in particular when it's non obvious where it came from - either because multiple locations generate the same message, or because the - message involves runtime formatting. -- new flag `--backend:js|c|cpp|objc` (or -b:js etc), to change backend; can be - used with any command (eg nim r, doc, check etc); safe to re-assign. -- new flag `--doccmd:cmd` to pass additional flags for runnableExamples, - e.g.: `--doccmd:-d:foo --threads` - use `--doccmd:skip` to skip runnableExamples and rst test snippets. -- new flag `--usenimcache` to output to nimcache (whatever it resolves to after - all commands are processed) - and avoids polluting both $pwd and $projectdir. It can be used with any command. -- `runnableExamples "-b:cpp -r:off": code` is now supported, allowing to override - how an example is compiled and run, for example to change backend or compile only. -- `nim doc` now outputs under `$projectPath/htmldocs` when `--outdir` is unspecified - (with or without `--project`); passing `--project` now automatically generates - an index and enables search. - See [docgen](docgen.html#introduction-quick-start) for details. -- Removed the `--oldNewlines` switch. -- Removed the `--laxStrings` switch for mutating the internal zero terminator on strings. -- Removed the `--oldast` switch. -- Removed the `--oldgensym` switch -- `$getType(untyped)` is now "untyped" instead of "expr", `$getType(typed)` is - now "typed" instead of "stmt". -- Sink inference is now disabled per default and has to enabled explicitly via - `--sinkInference:on`. *Note*: For the standard library sink inference remains - enabled. This change is most relevant for the `--gc:arc`, `--gc:orc` memory - management modes. ## Tool changes -- `nimsuggest` will now return both the forward declaration and the - implementation location upon a `def` query. Previously the behavior was - just to return the forward declaration. diff --git a/changelogs/changelog_1_4_0.md b/changelogs/changelog_1_4_0.md new file mode 100644 index 000000000..1e12a02b2 --- /dev/null +++ b/changelogs/changelog_1_4_0.md @@ -0,0 +1,371 @@ +# v1.4.0 - 2020-10-16 + + + +## Standard library additions and changes + +- Added some enhancements to `std/jsonutils` module. + * Added a possibility to deserialize JSON arrays directly to `HashSet` and + `OrderedSet` types and respectively to serialize those types to JSON arrays + via `jsonutils.fromJson` and `jsonutils.toJson` procedures. + * Added a possibility to deserialize JSON `null` objects to Nim option objects + and respectively to serialize Nim option object to JSON object if `isSome` + or to JSON null object if `isNone` via `jsonutils.fromJson` and + `jsonutils.toJson` procedures. + * Added `Joptions` parameter to `jsonutils.fromJson` procedure currently + containing two boolean options `allowExtraKeys` and `allowMissingKeys`. + - If `allowExtraKeys` is `true` Nim's object to which the JSON is parsed is + not required to have a field for every JSON key. + - If `allowMissingKeys` is `true` Nim's object to which JSON is parsed is + allowed to have fields without corresponding JSON keys. +- Added `bindParams`, `bindParam` to `db_sqlite` for binding parameters into a `SqlPrepared` statement. +- Added `tryInsert`,`insert` procs to `db_*` libs accept primary key column name. +- Added `xmltree.newVerbatimText` support create `style`'s,`script`'s text. +- `uri` adds Data URI Base64, implements RFC-2397. +- Added [DOM Parser](https://developer.mozilla.org/en-US/docs/Web/API/DOMParser) + to the `dom` module for the JavaScript target. +- The default hash for `Ordinal` has changed to something more bit-scrambling. + `import hashes; proc hash(x: myInt): Hash = hashIdentity(x)` recovers the old + one in an instantiation context while `-d:nimIntHash1` recovers it globally. +- `deques.peekFirst` and `deques.peekLast` now have `var Deque[T] -> var T` overloads. +- File handles created from high-level abstractions in the stdlib will no longer + be inherited by child processes. In particular, these modules are affected: + `asyncdispatch`, `asyncnet`, `system`, `nativesockets`, `net` and `selectors`. + + For `asyncdispatch`, `asyncnet`, `net` and `nativesockets`, an `inheritable` + flag has been added to all `proc`s that create sockets, allowing the user to + control whether the resulting socket is inheritable. This flag is provided to + ease the writing of multi-process servers, where sockets inheritance is + desired. + + For a transition period, define `nimInheritHandles` to enable file handle + inheritance by default. This flag does **not** affect the `selectors` module + due to the differing semantics between operating systems. + + `asyncdispatch.setInheritable`, `system.setInheritable` and + `nativesockets.setInheritable` is also introduced for setting file handle or + socket inheritance. Not all platform have these `proc`s defined. + +- The file descriptors created for internal bookkeeping by `ioselector_kqueue` + and `ioselector_epoll` will no longer be leaked to child processes. + +- `strutils.formatFloat` with `precision = 0` has been restored to the version + 1 behaviour that produces a trailing dot, e.g. `formatFloat(3.14159, precision = 0)` + is now `3.`, not `3`. +- `critbits` adds `commonPrefixLen`. + +- `relativePath(rel, abs)` and `relativePath(abs, rel)` used to silently give wrong results + (see #13222); instead they now use `getCurrentDir` to resolve those cases, + and this can now throw in edge cases where `getCurrentDir` throws. + `relativePath` also now works for js with `-d:nodejs`. + +- JavaScript and NimScript standard library changes: `streams.StringStream` is + now supported in JavaScript, with the limitation that any buffer `pointer`s + used must be castable to `ptr string`, any incompatible pointer type will not + work. The `lexbase` and `streams` modules used to fail to compile on + NimScript due to a bug, but this has been fixed. + + The following modules now compile on both JS and NimScript: `parsecsv`, + `parsecfg`, `parsesql`, `xmlparser`, `htmlparser` and `ropes`. Additionally + supported for JS is `cstrutils.startsWith` and `cstrutils.endsWith`, for + NimScript: `json`, `parsejson`, `strtabs` and `unidecode`. + +- Added `streams.readStr` and `streams.peekStr` overloads to + accept an existing string to modify, which avoids memory + allocations, similar to `streams.readLine` (#13857). + +- Added high-level `asyncnet.sendTo` and `asyncnet.recvFrom` UDP functionality. + +- `dollars.$` now works for unsigned ints with `nim js` + +- Improvements to the `bitops` module, including bitslices, non-mutating versions + of the original masking functions, `mask`/`masked`, and varargs support for + `bitand`, `bitor`, and `bitxor`. + +- `sugar.=>` and `sugar.->` changes: Previously `(x, y: int)` was transformed + into `(x: auto, y: int)`, it now becomes `(x: int, y: int)` in consistency + with regular proc definitions (although you cannot use semicolons). + + Pragmas and using a name are now allowed on the lefthand side of `=>`. Here + is an aggregate example of these changes: + ```nim + import sugar + + foo(x, y: int) {.noSideEffect.} => x + y + + # is transformed into + + proc foo(x: int, y: int): auto {.noSideEffect.} = x + y + ``` + +- The fields of `times.DateTime` are now private, and are accessed with getters and deprecated setters. + +- The `times` module now handles the default value for `DateTime` more consistently. + Most procs raise an assertion error when given + an uninitialized `DateTime`, the exceptions are `==` and `$` (which returns `"Uninitialized DateTime"`). + The proc `times.isInitialized` has been added which can be used to check if + a `DateTime` has been initialized. + +- 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`. +- Removed `sugar.distinctBase`, deprecated since `0.19`. +- Export `asyncdispatch.PDispatcher.handles` so that an external library can register them. + +- `std/with`, `sugar.dup` now support object field assignment expression: + ```nim + import std/with + + type Foo = object + x, y: int + + var foo = Foo() + with foo: + x = 10 + y = 20 + + echo foo + ``` + +- Proc `math.round` is no longer deprecated. The advice to use `strformat` instead + cannot be applied to every use case. The limitations and the (lack of) reliability + of `round` are well documented. + +- Added `getprotobyname` to `winlean`. Added `getProtoByname` to `nativesockets` which returns a protocol code + from the database that matches the protocol `name`. + +- Added missing attributes and methods to `dom.Navigator` like `deviceMemory`, `onLine`, `vibrate()`, etc. + +- Added `strutils.indentation` and `strutils.dedent` which enable indented string literals: + ```nim + import strutils + echo dedent """ + This + is + cool! + """ + ``` + +- Added `initUri(isIpv6: bool)` to `uri` module, now `uri` supports parsing ipv6 hostname. + +- Added `readLines(p: Process)` to `osproc` module for `startProcess` convenience. + +- Added the below `to` procs for collections. The usage is similar to procs such as + `sets.toHashSet` and `tables.toTable`. Previously, it was necessary to create the + respective empty collection and add items manually. + * `critbits.toCritBitTree`, which creates a `CritBitTree` from an `openArray` of + items or an `openArray` of pairs. + * `deques.toDeque`, which creates a `Deque` from an `openArray`. + * `heapqueue.toHeapQueue`, which creates a `HeapQueue` from an `openArray`. + * `intsets.toIntSet`, which creates an `IntSet` from an `openArray`. + +- Added `progressInterval` argument to `asyncftpclient.newAsyncFtpClient` to control the interval + at which progress callbacks are called. + +- Added `os.copyFileToDir` + +## Language changes + +- The `=destroy` hook no longer has to reset its target, as the compiler now automatically inserts + `wasMoved` calls where needed. +- The `=` hook is now called `=copy` for clarity. The old name `=` is still available so there + is no need to update your code. This change was backported to 1.2 too so you can use the + more readability `=copy` without loss of compatibility. + +- In the newruntime it is now allowed to assign to the discriminator field + without restrictions as long as case object doesn't have custom destructor. + The discriminator value doesn't have to be a constant either. If you have a + custom destructor for a case object and you do want to freely assign discriminator + fields, it is recommended to refactor object into 2 objects like this: + + ```nim + type + MyObj = object + case kind: bool + of true: y: ptr UncheckedArray[float] + of false: z: seq[int] + + proc `=destroy`(x: MyObj) = + if x.kind and x.y != nil: + deallocShared(x.y) + ``` + Refactor into: + ```nim + type + MySubObj = object + val: ptr UncheckedArray[float] + MyObj = object + case kind: bool + of true: y: MySubObj + of false: z: seq[int] + + proc `=destroy`(x: MySubObj) = + if x.val != nil: + deallocShared(x.val) + ``` +- `getImpl` on enum type symbols now returns field syms instead of idents. This helps + with writing typed macros. Old behavior for backwards compatibility can be restored + with `--useVersion:1.0`. +- The typed AST for proc headers will now have the arguments be syms instead of idents. + This helps with writing typed macros. Old behaviour for backwards compatibility can + be restored with `--useVersion:1.0`. +- ``let`` statements can now be used without a value if declared with + ``importc``/``importcpp``/``importjs``/``importobjc``. +- The keyword `from` is now usable as an operator. +- Exceptions inheriting from `system.Defect` are no longer tracked with + the `.raises: []` exception tracking mechanism. This is more consistent with the + built-in operations. The following always used to compile (and still does): + +```nim + +proc mydiv(a, b): int {.raises: [].} = + a div b # can raise an DivByZeroDefect + +``` + + Now also this compiles: + +```nim + +proc mydiv(a, b): int {.raises: [].} = + if b == 0: raise newException(DivByZeroDefect, "division by zero") + else: result = a div b + +``` + + The reason for this is that `DivByZeroDefect` inherits from `Defect` and + with `--panics:on` `Defects` become unrecoverable errors. + +- Added the `thiscall` calling convention as specified by Microsoft, mostly for hooking purposes. +- Deprecated `{.unroll.}` pragma, was ignored by the compiler anyways, was a nop. +- Removed `strutils.isNilOrWhitespace`, was deprecated. +- Removed `sharedtables.initSharedTable`, was deprecated and produces undefined behavior. +- Removed `asyncdispatch.newAsyncNativeSocket`, was deprecated since `0.18`. +- Removed `dom.releaseEvents` and `dom.captureEvents`, was deprecated. + +- Removed `sharedlists.initSharedList`, was deprecated and produces undefined behaviour. + +- There is a new experimental feature called "strictFuncs" which makes the definition of + `.noSideEffect` stricter. [See](manual_experimental.html#stricts-funcs) + for more information. + +- "for-loop macros" (see [the manual](manual.html#macros-for-loop-macros)) are no longer + an experimental feature. In other words, you don't have to write pragma + `{.experimental: "forLoopMacros".}` if you want to use them. + +- Added a ``.noalias`` pragma. It is mapped to C's ``restrict`` keyword for the increased + performance this keyword can enable. + +- `items` no longer compiles with enum with holes as its behavior was error prone, see #14004 +- `system.deepcopy` has to be enabled explicitly for `--gc:arc` and `--gc:orc` via + `--deepcopy:on`. + +- Added a `std/effecttraits` module for introspection of the inferred effects. + We hope this enables `async` macros that are precise about the possible exceptions that + can be raised. +- The pragma blocks `{.gcsafe.}: ...` and `{.noSideEffect.}: ...` can now also be + written as `{.cast(gcsafe).}: ...` and `{.cast(noSideEffect).}: ...`. This is the new + preferred way of writing these, emphasizing their unsafe nature. + + +## Compiler changes + +- Specific warnings can now be turned into errors via `--warningAsError[X]:on|off`. +- The `define` and `undef` pragmas have been de-deprecated. +- New command: `nim r main.nim [args...]` which compiles and runs main.nim, and implies `--usenimcache` + so that output is saved to $nimcache/main$exeExt, using the same logic as `nim c -r` to + avoid recompiling when sources don't change. This is now the preferred way to + run tests, avoiding the usual pain of clobbering your repo with binaries or + using tricky gitignore rules on posix. Example: + ```nim + nim r compiler/nim.nim --help # only compiled the first time + echo 'import os; echo getCurrentCompilerExe()' | nim r - # this works too + nim r compiler/nim.nim --fullhelp # no recompilation + nim r --nimcache:/tmp main # binary saved to /tmp/main + ``` +- `--hint:processing` is now supported and means `--hint:processing:on` + (likewise with other hints and warnings), which is consistent with all other bool flags. + (since 1.3.3). +- `nim doc -r main` and `nim rst2html -r main` now call `openDefaultBrowser`. +- new hint: `--hint:msgOrigin` will show where a compiler msg (hint|warning|error) + was generated; this helps in particular when it's non obvious where it came from + either because multiple locations generate the same message, or because the + message involves runtime formatting. +- new flag `--backend:js|c|cpp|objc` (or -b:js etc), to change backend; can be + used with any command (eg nim r, doc, check etc); safe to re-assign. +- new flag `--doccmd:cmd` to pass additional flags for runnableExamples, + e.g.: `--doccmd:-d:foo --threads` + use `--doccmd:skip` to skip runnableExamples and rst test snippets. +- new flag `--usenimcache` to output to nimcache (whatever it resolves to after + all commands are processed) + and avoids polluting both $pwd and $projectdir. It can be used with any command. +- `runnableExamples "-b:cpp -r:off": code` is now supported, allowing to override + how an example is compiled and run, for example to change backend or compile only. +- `nim doc` now outputs under `$projectPath/htmldocs` when `--outdir` is unspecified + (with or without `--project`); passing `--project` now automatically generates + an index and enables search. + See [docgen](docgen.html#introduction-quick-start) for details. +- Removed the `--oldNewlines` switch. +- Removed the `--laxStrings` switch for mutating the internal zero terminator on strings. +- Removed the `--oldast` switch. +- Removed the `--oldgensym` switch +- `$getType(untyped)` is now "untyped" instead of "expr", `$getType(typed)` is + now "typed" instead of "stmt". +- Sink inference is now disabled per default and has to enabled explicitly via + `--sinkInference:on`. *Note*: For the standard library sink inference remains + enabled. This change is most relevant for the `--gc:arc`, `--gc:orc` memory + management modes. + + +## Tool changes + +- `nimsuggest` will now return both the forward declaration and the + implementation location upon a `def` query. Previously the behavior was + just to return the forward declaration. From f8890a0804b6d05a14c9d64d71e1cb27a36bc5ca Mon Sep 17 00:00:00 2001 From: narimiran Date: Fri, 16 Oct 2020 07:36:09 +0200 Subject: [PATCH 0779/3768] change NimVersion to 1.5.1 --- lib/system.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/system.nim b/lib/system.nim index fe73a70f5..e41dfe1ca 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2102,11 +2102,11 @@ const ## when (NimMajor, NimMinor, NimPatch) >= (1, 3, 1): discard # see also std/private/since - NimMinor* {.intdefine.}: int = 3 + NimMinor* {.intdefine.}: int = 5 ## is the minor number of Nim's version. ## Odd for devel, even for releases. - NimPatch* {.intdefine.}: int = 7 + NimPatch* {.intdefine.}: int = 1 ## is the patch number of Nim's version. ## Odd for devel, even for releases. From 091ec916fd2737d495c5ef54afb5964ee62c049d Mon Sep 17 00:00:00 2001 From: narimiran Date: Fri, 16 Oct 2020 08:13:23 +0200 Subject: [PATCH 0780/3768] add bufixes for 1.4 in its changelog --- changelogs/changelog_1_4_0.md | 515 ++++++++++++++++++++++++++++++++++ 1 file changed, 515 insertions(+) diff --git a/changelogs/changelog_1_4_0.md b/changelogs/changelog_1_4_0.md index 1e12a02b2..b44e46ffb 100644 --- a/changelogs/changelog_1_4_0.md +++ b/changelogs/changelog_1_4_0.md @@ -369,3 +369,518 @@ proc mydiv(a, b): int {.raises: [].} = - `nimsuggest` will now return both the forward declaration and the implementation location upon a `def` query. Previously the behavior was just to return the forward declaration. + + +## Bugfixes + +- Fixed "repr() not available for uint{,8,16,32,64} under --gc:arc" + ([#13872](https://github.com/nim-lang/Nim/issues/13872)) +- Fixed "Critical: 1 completed Future, multiple await: Only 1 await will be awakened (the last one)" + ([#13889](https://github.com/nim-lang/Nim/issues/13889)) +- Fixed "crash on openarray interator with argument in stmtListExpr" + ([#13739](https://github.com/nim-lang/Nim/issues/13739)) +- Fixed "Some compilers on Windows don't work" + ([#13910](https://github.com/nim-lang/Nim/issues/13910)) +- Fixed "httpclient hangs if it recieves an HTTP 204 (No Content)" + ([#13894](https://github.com/nim-lang/Nim/issues/13894)) +- Fixed ""distinct uint64" type corruption on 32-bit, when using {.borrow.} operators" + ([#13902](https://github.com/nim-lang/Nim/issues/13902)) +- Fixed "Regression: impossible to use typed pragmas with proc types" + ([#13909](https://github.com/nim-lang/Nim/issues/13909)) +- Fixed "openssl wrapper corrupts stack on OpenSSL 1.1.1f + Android" + ([#13903](https://github.com/nim-lang/Nim/issues/13903)) +- Fixed "C compile error with --gc:arc on version 1.2.0 "unknown type name 'TGenericSeq'" + ([#13863](https://github.com/nim-lang/Nim/issues/13863)) +- Fixed "var return type for proc doesn't work at c++ backend" + ([#13848](https://github.com/nim-lang/Nim/issues/13848)) +- Fixed "TimeFormat() should raise an error but craches at compilation time" + ([#12864](https://github.com/nim-lang/Nim/issues/12864)) +- Fixed "gc:arc cannot fully support threadpool with FlowVar" + ([#13781](https://github.com/nim-lang/Nim/issues/13781)) +- Fixed "simple 'var openarray[char]' assignment crash when the openarray source is a local string and using gc:arc" + ([#14003](https://github.com/nim-lang/Nim/issues/14003)) +- Fixed "Cant use expressions with `when` in `type` sections." + ([#14007](https://github.com/nim-lang/Nim/issues/14007)) +- Fixed "`for a in MyEnum` gives incorrect results with enum with holes" + ([#14001](https://github.com/nim-lang/Nim/issues/14001)) +- Fixed "Trivial crash" + ([#12741](https://github.com/nim-lang/Nim/issues/12741)) +- Fixed "Enum with holes cannot be used as Table index" + ([#12834](https://github.com/nim-lang/Nim/issues/12834)) +- Fixed "spawn proc that uses typedesc crashes the compiler" + ([#14014](https://github.com/nim-lang/Nim/issues/14014)) +- Fixed "Docs Search `Results` box styling is not Dark Mode Friendly" + ([#13972](https://github.com/nim-lang/Nim/issues/13972)) +- Fixed "--gc:arc -d:useSysAssert undeclared identifier `cstderr` with newSeq" + ([#14038](https://github.com/nim-lang/Nim/issues/14038)) +- Fixed "issues in the manual" + ([#12486](https://github.com/nim-lang/Nim/issues/12486)) +- Fixed "Annoying warning: inherit from a more precise exception type like ValueError, IOError or OSError [InheritFromException]" + ([#14052](https://github.com/nim-lang/Nim/issues/14052)) +- Fixed "relativePath("foo", "/") and relativePath("/", "foo") is wrong" + ([#13222](https://github.com/nim-lang/Nim/issues/13222)) +- Fixed "[regression] `parseEnum` does not work anymore for enums with holes" + ([#14030](https://github.com/nim-lang/Nim/issues/14030)) +- Fixed "Exception types in the stdlib should inherit from `CatchableError` or `Defect`, not `Exception`" + ([#10288](https://github.com/nim-lang/Nim/issues/10288)) +- Fixed "Make debugSend and debugRecv procs public in smtp.nim" + ([#12189](https://github.com/nim-lang/Nim/issues/12189)) +- Fixed "xmltree need add raw text, when add style element" + ([#14064](https://github.com/nim-lang/Nim/issues/14064)) +- Fixed "raises requirement does not propagate to derived methods" + ([#8481](https://github.com/nim-lang/Nim/issues/8481)) +- Fixed "tests/stdlib/tgetaddrinfo.nim fails on NetBSD" + ([#14091](https://github.com/nim-lang/Nim/issues/14091)) +- Fixed "tests/niminaction/Chapter8/sdl/sdl_test.nim fails on NetBSD" + ([#14088](https://github.com/nim-lang/Nim/issues/14088)) +- Fixed "Incorrect escape sequence for example in jsffi library documentation" + ([#14110](https://github.com/nim-lang/Nim/issues/14110)) +- Fixed "HCR: Can not link exported const, in external library" + ([#13915](https://github.com/nim-lang/Nim/issues/13915)) +- Fixed "Cannot import std/unidecode" + ([#14112](https://github.com/nim-lang/Nim/issues/14112)) +- Fixed "macOS: dsymutil should not be called on static libraries" + ([#14132](https://github.com/nim-lang/Nim/issues/14132)) +- Fixed "nim jsondoc -o:doc.json filename.nim fails when sequences without a type are used" + ([#14066](https://github.com/nim-lang/Nim/issues/14066)) +- Fixed "algorithm.sortedByIt template corrupts tuple input under --gc:arc" + ([#14079](https://github.com/nim-lang/Nim/issues/14079)) +- Fixed "Invalid C code with lvalue conversion" + ([#14160](https://github.com/nim-lang/Nim/issues/14160)) +- Fixed "strformat: doc example fails" + ([#14054](https://github.com/nim-lang/Nim/issues/14054)) +- Fixed "Nim doc fail to run for nim 1.2.0 (nim 1.0.4 is ok)" + ([#13986](https://github.com/nim-lang/Nim/issues/13986)) +- Fixed "Exception when converting csize to clong" + ([#13698](https://github.com/nim-lang/Nim/issues/13698)) +- Fixed "[Documentation] overloading using named arguments works but is not documented" + ([#11932](https://github.com/nim-lang/Nim/issues/11932)) +- Fixed "import os + use of existsDir/dirExists/existsFile/fileExists/findExe in config.nims causes "ambiguous call' error" + ([#14142](https://github.com/nim-lang/Nim/issues/14142)) +- Fixed "import os + use of existsDir/dirExists/existsFile/fileExists/findExe in config.nims causes "ambiguous call' error" + ([#14142](https://github.com/nim-lang/Nim/issues/14142)) +- Fixed "runnableExamples doc gen crashes compiler with `except Exception as e` syntax" + ([#14177](https://github.com/nim-lang/Nim/issues/14177)) +- Fixed "[ARC] Segfault with cyclic references (?)" + ([#14159](https://github.com/nim-lang/Nim/issues/14159)) +- Fixed "Semcheck regression when accessing a static parameter in proc" + ([#14136](https://github.com/nim-lang/Nim/issues/14136)) +- Fixed "iterator walkDir doesn't work with -d:useWinAnsi" + ([#14201](https://github.com/nim-lang/Nim/issues/14201)) +- Fixed "cas is wrong for tcc" + ([#14151](https://github.com/nim-lang/Nim/issues/14151)) +- Fixed "proc execCmdEx doesn't work with -d:useWinAnsi" + ([#14203](https://github.com/nim-lang/Nim/issues/14203)) +- Fixed "Use -d:nimEmulateOverflowChecks by default?" + ([#14209](https://github.com/nim-lang/Nim/issues/14209)) +- Fixed "Old sequences with destructor objects bug" + ([#14217](https://github.com/nim-lang/Nim/issues/14217)) +- Fixed "[ARC] ICE when changing the discriminant of a return value" + ([#14244](https://github.com/nim-lang/Nim/issues/14244)) +- Fixed "[ARC] ICE with static objects" + ([#14236](https://github.com/nim-lang/Nim/issues/14236)) +- Fixed "[ARC] "internal error: environment misses: a" in a finalizer" + ([#14243](https://github.com/nim-lang/Nim/issues/14243)) +- Fixed "[ARC] compile failure using repr with object containing `ref seq[string]`" + ([#14270](https://github.com/nim-lang/Nim/issues/14270)) +- Fixed "[ARC] implicit move on last use happening on non-last use" + ([#14269](https://github.com/nim-lang/Nim/issues/14269)) +- Fixed "[ARC] Compiler crash with a recursive non-ref object variant" + ([#14294](https://github.com/nim-lang/Nim/issues/14294)) +- Fixed "htmlparser.parseHtml behaves differently using --gc:arc or --gc:orc" + ([#13946](https://github.com/nim-lang/Nim/issues/13946)) +- Fixed "Invalid return value of openProcess is NULL rather than INVALID_HANDLE_VALUE(-1) in windows" + ([#14289](https://github.com/nim-lang/Nim/issues/14289)) +- Fixed "ARC codegen bug with inline iterators" + ([#14219](https://github.com/nim-lang/Nim/issues/14219)) +- Fixed "Building koch on OpenBSD fails unless the Nim directory is in `$PATH`" + ([#13758](https://github.com/nim-lang/Nim/issues/13758)) +- Fixed "[gc:arc] case object assignment SIGSEGV: destroy not called for primitive type " + ([#14312](https://github.com/nim-lang/Nim/issues/14312)) +- Fixed "Crash when using thread and --gc:arc " + ([#13881](https://github.com/nim-lang/Nim/issues/13881)) +- Fixed "Getting "Warning: Cannot prove that 'result' is initialized" for an importcpp'd proc with `var T` return type" + ([#14314](https://github.com/nim-lang/Nim/issues/14314)) +- Fixed "`nim cpp -r --gc:arc` segfaults on caught AssertionError" + ([#13071](https://github.com/nim-lang/Nim/issues/13071)) +- Fixed "tests/async/tasyncawait.nim is recently very flaky" + ([#14320](https://github.com/nim-lang/Nim/issues/14320)) +- Fixed "Documentation nonexistent quitprocs module" + ([#14331](https://github.com/nim-lang/Nim/issues/14331)) +- Fixed "SIGSEV encountered when creating threads in a loop w/ --gc:arc" + ([#13935](https://github.com/nim-lang/Nim/issues/13935)) +- Fixed "nim-gdb is missing from all released packages" + ([#13104](https://github.com/nim-lang/Nim/issues/13104)) +- Fixed "sysAssert error with gc:arc on 3 line program" + ([#13862](https://github.com/nim-lang/Nim/issues/13862)) +- Fixed "compiler error with inline async proc and pragma" + ([#13998](https://github.com/nim-lang/Nim/issues/13998)) +- Fixed "[ARC] Compiler crash when adding to a seq[ref Object]" + ([#14333](https://github.com/nim-lang/Nim/issues/14333)) +- Fixed "nimvm: sysFatal: unhandled exception: 'sons' is not accessible using discriminant 'kind' of type 'TNode' [FieldError]" + ([#14340](https://github.com/nim-lang/Nim/issues/14340)) +- Fixed "[Regression] karax events are not firing " + ([#14350](https://github.com/nim-lang/Nim/issues/14350)) +- Fixed "odbcsql module has some wrong integer types" + ([#9771](https://github.com/nim-lang/Nim/issues/9771)) +- Fixed "db_sqlite needs sqlPrepared" + ([#13559](https://github.com/nim-lang/Nim/issues/13559)) +- Fixed "[Regression] `createThread` is not GC-safe" + ([#14370](https://github.com/nim-lang/Nim/issues/14370)) +- Fixed "Broken example on hot code reloading" + ([#14380](https://github.com/nim-lang/Nim/issues/14380)) +- Fixed "runnableExamples block with `except` on specified error fails with `nim doc`" + ([#12746](https://github.com/nim-lang/Nim/issues/12746)) +- Fixed "compiler as a library: findNimStdLibCompileTime fails to find system.nim" + ([#12293](https://github.com/nim-lang/Nim/issues/12293)) +- Fixed "5 bugs with importcpp exceptions" + ([#14369](https://github.com/nim-lang/Nim/issues/14369)) +- Fixed "Docs shouldn't collapse pragmas inside runnableExamples/code blocks" + ([#14174](https://github.com/nim-lang/Nim/issues/14174)) +- Fixed "Bad codegen/emit for hashes.hiXorLo in some contexts." + ([#14394](https://github.com/nim-lang/Nim/issues/14394)) +- Fixed "Boehm GC does not scan thread-local storage" + ([#14364](https://github.com/nim-lang/Nim/issues/14364)) +- Fixed "RVO not exception safe" + ([#14126](https://github.com/nim-lang/Nim/issues/14126)) +- Fixed "runnableExamples that are only compiled" + ([#10731](https://github.com/nim-lang/Nim/issues/10731)) +- Fixed "`foldr` raises IndexError when called on sequence" + ([#14404](https://github.com/nim-lang/Nim/issues/14404)) +- Fixed "moveFile does not overwrite destination file" + ([#14057](https://github.com/nim-lang/Nim/issues/14057)) +- Fixed "doc2 outputs in current work dir" + ([#6583](https://github.com/nim-lang/Nim/issues/6583)) +- Fixed "[docgen] proc doc comments silently omitted after 1st runnableExamples" + ([#9227](https://github.com/nim-lang/Nim/issues/9227)) +- Fixed "`nim doc --project` shows '@@/' instead of '../' for relative paths to submodules" + ([#14448](https://github.com/nim-lang/Nim/issues/14448)) +- Fixed "re, nre have wrong `start` semantics" + ([#14284](https://github.com/nim-lang/Nim/issues/14284)) +- Fixed "runnableExamples should preserve source code doc comments, strings, and (maybe) formatting" + ([#8871](https://github.com/nim-lang/Nim/issues/8871)) +- Fixed "`nim doc ..` fails when runnableExamples uses `$` [devel] [regression]" + ([#14485](https://github.com/nim-lang/Nim/issues/14485)) +- Fixed "`items` is 20%~30% slower than iteration via an index" + ([#14421](https://github.com/nim-lang/Nim/issues/14421)) +- Fixed "ARC: unreliable setLen " + ([#14495](https://github.com/nim-lang/Nim/issues/14495)) +- Fixed "lent is unsafe: after #14447 you can modify variables with "items" loop for sequences" + ([#14498](https://github.com/nim-lang/Nim/issues/14498)) +- Fixed "`var op = fn()` wrongly gives warning `ObservableStores` with `object of RootObj` type" + ([#14514](https://github.com/nim-lang/Nim/issues/14514)) +- Fixed "Compiler assertion" + ([#14562](https://github.com/nim-lang/Nim/issues/14562)) +- Fixed "Can't get `ord` of a value of a Range type in the JS backend " + ([#14570](https://github.com/nim-lang/Nim/issues/14570)) +- Fixed "js: can't take addr of param (including implicitly via `lent`)" + ([#14576](https://github.com/nim-lang/Nim/issues/14576)) +- Fixed "{.noinit.} ignored in for loop -> bad codegen for non-movable types" + ([#14118](https://github.com/nim-lang/Nim/issues/14118)) +- Fixed "generic destructor gives: `Error: unresolved generic parameter`" + ([#14315](https://github.com/nim-lang/Nim/issues/14315)) +- Fixed "Memory leak with arc gc" + ([#14568](https://github.com/nim-lang/Nim/issues/14568)) +- Fixed "escape analysis broken with `lent`" + ([#14557](https://github.com/nim-lang/Nim/issues/14557)) +- Fixed "`wrapWords` seems to ignore linebreaks when wrapping, leaving breaks in the wrong place" + ([#14579](https://github.com/nim-lang/Nim/issues/14579)) +- Fixed "`lent` gives wrong results with -d:release" + ([#14578](https://github.com/nim-lang/Nim/issues/14578)) +- Fixed "Nested await expressions regression: `await a(await expandValue())` doesnt compile" + ([#14279](https://github.com/nim-lang/Nim/issues/14279)) +- Fixed "windows CI docs fails with strange errors" + ([#14545](https://github.com/nim-lang/Nim/issues/14545)) +- Fixed "[CI] tests/async/tioselectors.nim flaky test for freebsd + OSX CI" + ([#13166](https://github.com/nim-lang/Nim/issues/13166)) +- Fixed "seq.setLen sometimes doesn't zero memory" + ([#14655](https://github.com/nim-lang/Nim/issues/14655)) +- Fixed "`nim dump` is roughly 100x slower in 1.3 versus 1.2" + ([#14179](https://github.com/nim-lang/Nim/issues/14179)) +- Fixed "Regression: devel docgen cannot generate document for method" + ([#14691](https://github.com/nim-lang/Nim/issues/14691)) +- Fixed "recently flaky tests/async/t7758.nim" + ([#14685](https://github.com/nim-lang/Nim/issues/14685)) +- Fixed "Bind no longer working in generic procs." + ([#11811](https://github.com/nim-lang/Nim/issues/11811)) +- Fixed "The pegs module doesn't work with generics!" + ([#14718](https://github.com/nim-lang/Nim/issues/14718)) +- Fixed "Defer is not properly working for asynchronous procedures." + ([#13899](https://github.com/nim-lang/Nim/issues/13899)) +- Fixed "Add an ARC test with threads in a loop" + ([#14690](https://github.com/nim-lang/Nim/issues/14690)) +- Fixed "[goto exceptions] {.noReturn.} pragma is not detected in a case expression" + ([#14458](https://github.com/nim-lang/Nim/issues/14458)) +- Fixed "[exceptions:goto] C compiler error with dynlib pragma calling a proc" + ([#14240](https://github.com/nim-lang/Nim/issues/14240)) +- Fixed "Cannot borrow var float64 in infix assignment" + ([#14440](https://github.com/nim-lang/Nim/issues/14440)) +- Fixed "lib/pure/memfiles.nim: compilation error with --taintMode:on" + ([#14760](https://github.com/nim-lang/Nim/issues/14760)) +- Fixed "newWideCString allocates a multiple of the memory needed" + ([#14750](https://github.com/nim-lang/Nim/issues/14750)) +- Fixed "Nim source archive install: 'install.sh' fails with error: cp: cannot stat 'bin/nim-gdb': No such file or directory" + ([#14748](https://github.com/nim-lang/Nim/issues/14748)) +- Fixed "`nim cpp -r tests/exception/t9657` hangs" + ([#10343](https://github.com/nim-lang/Nim/issues/10343)) +- Fixed "Detect tool fails on FreeBSD" + ([#14715](https://github.com/nim-lang/Nim/issues/14715)) +- Fixed "compiler crash: `findUnresolvedStatic` " + ([#14802](https://github.com/nim-lang/Nim/issues/14802)) +- Fixed "seq namespace (?) regression" + ([#4796](https://github.com/nim-lang/Nim/issues/4796)) +- Fixed "Possible out of bounds string access in std/colors parseColor and isColor" + ([#14839](https://github.com/nim-lang/Nim/issues/14839)) +- Fixed "compile error on latest devel with orc and ssl" + ([#14647](https://github.com/nim-lang/Nim/issues/14647)) +- Fixed "[minor] `$` wrong for type tuple" + ([#13432](https://github.com/nim-lang/Nim/issues/13432)) +- Fixed "Documentation missing on devel asyncftpclient" + ([#14846](https://github.com/nim-lang/Nim/issues/14846)) +- Fixed "nimpretty is confused with a trailing comma in enum definition" + ([#14401](https://github.com/nim-lang/Nim/issues/14401)) +- Fixed "Output arguments get ignored when compiling with --app:staticlib" + ([#12745](https://github.com/nim-lang/Nim/issues/12745)) +- Fixed "[ARC] destructive move destroys the object too early" + ([#14396](https://github.com/nim-lang/Nim/issues/14396)) +- Fixed "highlite.getNextToken() crashes if the buffer string is "echo "\""" + ([#14830](https://github.com/nim-lang/Nim/issues/14830)) +- Fixed "Memory corruption with --gc:arc with a seq of objects with an empty body." + ([#14472](https://github.com/nim-lang/Nim/issues/14472)) +- Fixed "Stropped identifiers don't work as field names in tuple literals" + ([#14911](https://github.com/nim-lang/Nim/issues/14911)) +- Fixed "Please revert my commit" + ([#14930](https://github.com/nim-lang/Nim/issues/14930)) +- Fixed "[ARC] C compiler error with inline iterators and imports" + ([#14864](https://github.com/nim-lang/Nim/issues/14864)) +- Fixed "AsyncHttpClient segfaults with gc:orc, possibly memory corruption" + ([#14402](https://github.com/nim-lang/Nim/issues/14402)) +- Fixed "[ARC] Template with a block evaluating to a GC'd value results in a compiler crash" + ([#14899](https://github.com/nim-lang/Nim/issues/14899)) +- Fixed "[ARC] Weird issue with if expressions and templates" + ([#14900](https://github.com/nim-lang/Nim/issues/14900)) +- Fixed "xmlparser does not compile on devel" + ([#14805](https://github.com/nim-lang/Nim/issues/14805)) +- Fixed "returning lent T from a var T param gives codegen errors or SIGSEGV" + ([#14878](https://github.com/nim-lang/Nim/issues/14878)) +- Fixed "[ARC] Weird issue with if expressions and templates" + ([#14900](https://github.com/nim-lang/Nim/issues/14900)) +- Fixed "threads:on + gc:orc + unittest = C compiler errors" + ([#14865](https://github.com/nim-lang/Nim/issues/14865)) +- Fixed "mitems, mpairs doesn't work at compile time anymore" + ([#12129](https://github.com/nim-lang/Nim/issues/12129)) +- Fixed "strange result from executing code in const expression" + ([#10465](https://github.com/nim-lang/Nim/issues/10465)) +- Fixed "Same warning printed 3 times" + ([#11009](https://github.com/nim-lang/Nim/issues/11009)) +Was unsure about: " add testcase for #4668 (#14946)" +- Fixed "type alias for generic typeclass doesn't work" + ([#4668](https://github.com/nim-lang/Nim/issues/4668)) +- Fixed "exceptions:goto Bug devel codegen lvalue NIM_FALSE=NIM_FALSE" + ([#14925](https://github.com/nim-lang/Nim/issues/14925)) +- Fixed "the --useVersion:1.0 no longer works in devel" + ([#14912](https://github.com/nim-lang/Nim/issues/14912)) +- Fixed "template declaration of iterator doesn't compile" + ([#4722](https://github.com/nim-lang/Nim/issues/4722)) +- Fixed "Compiler crash on type inheritance with static generic parameter and equality check" + ([#12571](https://github.com/nim-lang/Nim/issues/12571)) +- Fixed "Nim crashes while handling a cast in async circumstances." + ([#13815](https://github.com/nim-lang/Nim/issues/13815)) +- Fixed "[ARC] Internal compiler error when calling an iterator from an inline proc " + ([#14383](https://github.com/nim-lang/Nim/issues/14383)) +- Fixed ""Cannot instantiate" error when template uses generic type" + ([#5926](https://github.com/nim-lang/Nim/issues/5926)) +- Fixed "Different raises behaviour for newTerminal between Linux and Windows" + ([#12759](https://github.com/nim-lang/Nim/issues/12759)) +- Fixed "Expand on a type (that defines a proc type) in error message " + ([#6608](https://github.com/nim-lang/Nim/issues/6608)) +- Fixed "unittest require quits program with an exit code of 0" + ([#14475](https://github.com/nim-lang/Nim/issues/14475)) +- Fixed "Range type: Generics vs concrete type, semcheck difference." + ([#8426](https://github.com/nim-lang/Nim/issues/8426)) +- Fixed "[Macro] Type mismatch when parameter name is the same as a field" + ([#13253](https://github.com/nim-lang/Nim/issues/13253)) +- Fixed "Generic instantiation failure when converting a sequence of circular generic types to strings" + ([#10396](https://github.com/nim-lang/Nim/issues/10396)) +- Fixed "initOptParser ignores argument after value option with empty value." + ([#13086](https://github.com/nim-lang/Nim/issues/13086)) +- Fixed "[ARC] proc with both explicit and implicit return results in a C compiler error" + ([#14985](https://github.com/nim-lang/Nim/issues/14985)) +- Fixed "Alias type forgets implicit generic params depending on order" + ([#14990](https://github.com/nim-lang/Nim/issues/14990)) +- Fixed "[ARC] sequtils.insert has different behaviour between ARC/refc" + ([#14994](https://github.com/nim-lang/Nim/issues/14994)) +- Fixed "The documentation for "hot code reloading" references a non-existent npm package" + ([#13621](https://github.com/nim-lang/Nim/issues/13621)) +- Fixed "existsDir deprecated but breaking `dir` undeclared" + ([#15006](https://github.com/nim-lang/Nim/issues/15006)) +- Fixed "uri.decodeUrl crashes on incorrectly formatted input" + ([#14082](https://github.com/nim-lang/Nim/issues/14082)) +- Fixed "testament incorrectly reports time for tests, leading to wrong conclusions" + ([#14822](https://github.com/nim-lang/Nim/issues/14822)) +- Fixed "Calling peekChar with Stream returned from osproc.outputStream generate runtime error" + ([#14906](https://github.com/nim-lang/Nim/issues/14906)) +- Fixed "localPassC pragma should come *after* other flags" + ([#14194](https://github.com/nim-lang/Nim/issues/14194)) +- Fixed ""Could not load" dynamic library at runtime because of hidden dependency" + ([#2408](https://github.com/nim-lang/Nim/issues/2408)) +- Fixed "--gc:arc generate invalid code for {.global.} (`«nimErr_» in NIM_UNLIKELY`)" + ([#14480](https://github.com/nim-lang/Nim/issues/14480)) +- Fixed "Using `^` from stdlib/math along with converters gives a match for types that aren't SomeNumber" + ([#15033](https://github.com/nim-lang/Nim/issues/15033)) +- Fixed "[ARC] Weird exception behaviour from doAssertRaises" + ([#15026](https://github.com/nim-lang/Nim/issues/15026)) +- Fixed "[ARC] Compiler crash declaring a finalizer proc directly in 'new'" + ([#15044](https://github.com/nim-lang/Nim/issues/15044)) +- Fixed "[ARC] C compiler error when creating a var of a const seq" + ([#15036](https://github.com/nim-lang/Nim/issues/15036)) +- Fixed "code with named arguments in proc of winim/com can not been compiled" + ([#15056](https://github.com/nim-lang/Nim/issues/15056)) +- Fixed "javascript backend produces javascript code with syntax error in object syntax" + ([#14534](https://github.com/nim-lang/Nim/issues/14534)) +- Fixed "--gc:arc should be ignored in JS mode." + ([#14684](https://github.com/nim-lang/Nim/issues/14684)) +- Fixed "arc: C compilation error with imported global code using a closure iterator" + ([#12990](https://github.com/nim-lang/Nim/issues/12990)) +- Fixed "[ARC] Crash when modifying a string with mitems iterator" + ([#15052](https://github.com/nim-lang/Nim/issues/15052)) +- Fixed "[ARC] SIGSEGV when calling a closure as a tuple field in a seq" + ([#15038](https://github.com/nim-lang/Nim/issues/15038)) +- Fixed "pass varargs[seq[T]] to iterator give empty seq " + ([#12576](https://github.com/nim-lang/Nim/issues/12576)) +- Fixed "Compiler crashes when using string as object variant selector with else branch" + ([#14189](https://github.com/nim-lang/Nim/issues/14189)) +- Fixed "JS compiler error related to implicit return and return var type" + ([#11354](https://github.com/nim-lang/Nim/issues/11354)) +- Fixed "`nkRecWhen` causes internalAssert in semConstructFields" + ([#14698](https://github.com/nim-lang/Nim/issues/14698)) +- Fixed "Memory leaks with async (closure iterators?) under ORC" + ([#15076](https://github.com/nim-lang/Nim/issues/15076)) +- Fixed "strutil.insertSep() fails on negative numbers" + ([#11352](https://github.com/nim-lang/Nim/issues/11352)) +- Fixed "Constructing a uint64 range on a 32-bit machine leads to incorrect codegen" + ([#14616](https://github.com/nim-lang/Nim/issues/14616)) +- Fixed "heapqueue pushpop() proc doesn't compile" + ([#14139](https://github.com/nim-lang/Nim/issues/14139)) +- Fixed "[ARC] SIGSEGV when trying to swap in a literal/const string" + ([#15112](https://github.com/nim-lang/Nim/issues/15112)) +- Fixed "Defer and --gc:arc" + ([#15071](https://github.com/nim-lang/Nim/issues/15071)) +- Fixed "internal error: compiler/semobjconstr.nim(324, 20) example" + ([#15111](https://github.com/nim-lang/Nim/issues/15111)) +- Fixed "[ARC] Sequence "disappears" with a table inside of a table with an object variant" + ([#15122](https://github.com/nim-lang/Nim/issues/15122)) +- Fixed "[ARC] SIGSEGV with tuple assignment caused by cursor inference" + ([#15130](https://github.com/nim-lang/Nim/issues/15130)) +- Fixed "Issue with --gc:arc at compile time" + ([#15129](https://github.com/nim-lang/Nim/issues/15129)) +- Fixed "Writing an empty string to an AsyncFile raises an IndexDefect" + ([#15148](https://github.com/nim-lang/Nim/issues/15148)) +- Fixed "Compiler is confused about call convention of function with nested closure" + ([#5688](https://github.com/nim-lang/Nim/issues/5688)) +- Fixed "Nil check on each field fails in generic function" + ([#15101](https://github.com/nim-lang/Nim/issues/15101)) +- Fixed "{.nimcall.} convention won't avoid the creation of closures" + ([#8473](https://github.com/nim-lang/Nim/issues/8473)) +- Fixed "smtp.nim(161, 40) Error: type mismatch: got but expected 'SslContext = void'" + ([#15177](https://github.com/nim-lang/Nim/issues/15177)) +- Fixed "[strscans] scanf doesn't match a single character with $+ if it's the end of the string" + ([#15064](https://github.com/nim-lang/Nim/issues/15064)) +- Fixed "Crash and incorrect return values when using readPasswordFromStdin on Windows." + ([#15207](https://github.com/nim-lang/Nim/issues/15207)) +- Fixed "Possible capture error with fieldPairs and genericParams" + ([#15221](https://github.com/nim-lang/Nim/issues/15221)) +- Fixed "The StmtList processing of template parameters can lead to unexpected errors" + ([#5691](https://github.com/nim-lang/Nim/issues/5691)) +- Fixed "[ARC] C compiler error when passing a var openArray to a sink openArray" + ([#15035](https://github.com/nim-lang/Nim/issues/15035)) +- Fixed "Inconsistent unsigned -> signed RangeDefect usage across integer sizes" + ([#15210](https://github.com/nim-lang/Nim/issues/15210)) +- Fixed "toHex results in RangeDefect exception when used with large uint64" + ([#15257](https://github.com/nim-lang/Nim/issues/15257)) +- Fixed "Arc sink arg crash" + ([#15238](https://github.com/nim-lang/Nim/issues/15238)) +- Fixed "SQL escape in db_mysql is not enough" + ([#15219](https://github.com/nim-lang/Nim/issues/15219)) +- Fixed "Mixing 'return' with expressions is allowed in 1.2" + ([#15280](https://github.com/nim-lang/Nim/issues/15280)) +- Fixed "os.getFileInfo() causes ICE with --gc:arc on Windows" + ([#15286](https://github.com/nim-lang/Nim/issues/15286)) +- Fixed "[ARC] Sequence "disappears" with a table inside of a table with an object variant" + ([#15122](https://github.com/nim-lang/Nim/issues/15122)) +- Fixed "Documentation regression jsre module missing" + ([#15183](https://github.com/nim-lang/Nim/issues/15183)) +- Fixed "CountTable.smallest/largest() on empty table either asserts or gives bogus answer" + ([#15021](https://github.com/nim-lang/Nim/issues/15021)) +- Fixed "[Regression] Parser regression" + ([#15305](https://github.com/nim-lang/Nim/issues/15305)) +- Fixed "[ARC] SIGSEGV with tuple unpacking caused by cursor inference" + ([#15147](https://github.com/nim-lang/Nim/issues/15147)) +- Fixed "LwIP/FreeRTOS compile error - missing SIGPIPE and more " + ([#15302](https://github.com/nim-lang/Nim/issues/15302)) +- Fixed "Memory leaks with async (closure iterators?) under ORC" + ([#15076](https://github.com/nim-lang/Nim/issues/15076)) +- Fixed "Bug compiling with --gc:arg or --gc:orc" + ([#15325](https://github.com/nim-lang/Nim/issues/15325)) +- Fixed "memory corruption in tmarshall.nim" + ([#9754](https://github.com/nim-lang/Nim/issues/9754)) +- Fixed "typed macros break generic proc definitions" + ([#15326](https://github.com/nim-lang/Nim/issues/15326)) +- Fixed "nim doc2 ignores --docSeeSrcUrl parameter" + ([#6071](https://github.com/nim-lang/Nim/issues/6071)) +- Fixed "The decodeData Iterator from cgi module crash" + ([#15369](https://github.com/nim-lang/Nim/issues/15369)) +- Fixed "|| iterator generates invalid code when compiling with --debugger:native" + ([#9710](https://github.com/nim-lang/Nim/issues/9710)) +- Fixed "Wrong number of variables" + ([#15360](https://github.com/nim-lang/Nim/issues/15360)) +- Fixed "Coercions with distinct types should traverse pointer modifiers transparently." + ([#7165](https://github.com/nim-lang/Nim/issues/7165)) +- Fixed "Error with distinct generic TableRef" + ([#6060](https://github.com/nim-lang/Nim/issues/6060)) +- Fixed "Support images in nim docgen" + ([#6430](https://github.com/nim-lang/Nim/issues/6430)) +- Fixed "Regression. Double sem check for procs." + ([#15389](https://github.com/nim-lang/Nim/issues/15389)) +- Fixed "uri.nim url with literal ipv6 address is printed wrong, and cannot parsed again" + ([#15333](https://github.com/nim-lang/Nim/issues/15333)) +- Fixed "[ARC] Object variant gets corrupted with cursor inference" + ([#15361](https://github.com/nim-lang/Nim/issues/15361)) +- Fixed "`nim doc ..` compiler crash (regression 0.19.6 => 1.0)" + ([#14474](https://github.com/nim-lang/Nim/issues/14474)) +- Fixed "cannot borrow result; what it borrows from is potentially mutated" + ([#15403](https://github.com/nim-lang/Nim/issues/15403)) +- Fixed "memory corruption for seq.add(seq) with gc:arc and d:useMalloc " + ([#14983](https://github.com/nim-lang/Nim/issues/14983)) +- Fixed "DocGen HTML output appears improperly when encountering text immediately after/before inline monospace; in some cases won't compile" + ([#11537](https://github.com/nim-lang/Nim/issues/11537)) +- Fixed "Deepcopy in arc crashes" + ([#15405](https://github.com/nim-lang/Nim/issues/15405)) +- Fixed "pop pragma takes invalid input" + ([#15430](https://github.com/nim-lang/Nim/issues/15430)) +- Fixed "tests/stdlib/tgetprotobyname fails on NetBSD" + ([#15452](https://github.com/nim-lang/Nim/issues/15452)) +- Fixed "defer doesnt work with block, break and await" + ([#15243](https://github.com/nim-lang/Nim/issues/15243)) +- Fixed "tests/stdlib/tssl failing on NetBSD" + ([#15493](https://github.com/nim-lang/Nim/issues/15493)) +- Fixed "strictFuncs doesn't seem to catch simple ref mutation" + ([#15508](https://github.com/nim-lang/Nim/issues/15508)) +- Fixed "Sizeof of case object is incorrect. Showstopper" + ([#15516](https://github.com/nim-lang/Nim/issues/15516)) +- Fixed "[ARC] Internal error when trying to use a parallel for loop" + ([#15512](https://github.com/nim-lang/Nim/issues/15512)) +- Fixed "[ARC] Type-bound assign op is not being generated" + ([#15510](https://github.com/nim-lang/Nim/issues/15510)) +- Fixed "[ARC] Crash when adding openArray proc argument to a local seq" + ([#15511](https://github.com/nim-lang/Nim/issues/15511)) +- Fixed "VM: const case object gets some fields zeroed out at runtime" + ([#13081](https://github.com/nim-lang/Nim/issues/13081)) +- Fixed "regression(1.2.6 => devel): VM: const case object field access gives: 'sons' is not accessible" + ([#15532](https://github.com/nim-lang/Nim/issues/15532)) +- Fixed "Csources: huge size increase (x2.3) in 0.20" + ([#12027](https://github.com/nim-lang/Nim/issues/12027)) +- Fixed "Out of date error message for GC options" + ([#15547](https://github.com/nim-lang/Nim/issues/15547)) +- Fixed "dbQuote additional escape regression" + ([#15560](https://github.com/nim-lang/Nim/issues/15560)) From 6b8baab536d25c744150e0190d2e172165faf92e Mon Sep 17 00:00:00 2001 From: narimiran Date: Fri, 16 Oct 2020 13:18:31 +0200 Subject: [PATCH 0781/3768] cosmetic fixes for the 1.4 changelog [ci skip] --- changelogs/changelog_1_4_0.md | 144 ++++++++++++++++------------------ 1 file changed, 66 insertions(+), 78 deletions(-) diff --git a/changelogs/changelog_1_4_0.md b/changelogs/changelog_1_4_0.md index b44e46ffb..091048ad1 100644 --- a/changelogs/changelog_1_4_0.md +++ b/changelogs/changelog_1_4_0.md @@ -12,16 +12,16 @@ and respectively to serialize Nim option object to JSON object if `isSome` or to JSON null object if `isNone` via `jsonutils.fromJson` and `jsonutils.toJson` procedures. - * Added `Joptions` parameter to `jsonutils.fromJson` procedure currently + * Added a `Joptions` parameter to `jsonutils.fromJson` currently containing two boolean options `allowExtraKeys` and `allowMissingKeys`. - If `allowExtraKeys` is `true` Nim's object to which the JSON is parsed is not required to have a field for every JSON key. - If `allowMissingKeys` is `true` Nim's object to which JSON is parsed is allowed to have fields without corresponding JSON keys. - Added `bindParams`, `bindParam` to `db_sqlite` for binding parameters into a `SqlPrepared` statement. -- Added `tryInsert`,`insert` procs to `db_*` libs accept primary key column name. +- Added `tryInsert`,`insert` procs to `db_*` libs which accept primary key column name. - Added `xmltree.newVerbatimText` support create `style`'s,`script`'s text. -- `uri` adds Data URI Base64, implements RFC-2397. +- `uri` module now implements RFC-2397. - Added [DOM Parser](https://developer.mozilla.org/en-US/docs/Web/API/DOMParser) to the `dom` module for the JavaScript target. - The default hash for `Ordinal` has changed to something more bit-scrambling. @@ -43,8 +43,8 @@ due to the differing semantics between operating systems. `asyncdispatch.setInheritable`, `system.setInheritable` and - `nativesockets.setInheritable` is also introduced for setting file handle or - socket inheritance. Not all platform have these `proc`s defined. + `nativesockets.setInheritable` are also introduced for setting file handle or + socket inheritance. Not all platforms have these `proc`s defined. - The file descriptors created for internal bookkeeping by `ioselector_kqueue` and `ioselector_epoll` will no longer be leaked to child processes. @@ -52,7 +52,7 @@ - `strutils.formatFloat` with `precision = 0` has been restored to the version 1 behaviour that produces a trailing dot, e.g. `formatFloat(3.14159, precision = 0)` is now `3.`, not `3`. -- `critbits` adds `commonPrefixLen`. +- Added `commonPrefixLen` to `critbits`. - `relativePath(rel, abs)` and `relativePath(abs, rel)` used to silently give wrong results (see #13222); instead they now use `getCurrentDir` to resolve those cases, @@ -76,18 +76,18 @@ - Added high-level `asyncnet.sendTo` and `asyncnet.recvFrom` UDP functionality. -- `dollars.$` now works for unsigned ints with `nim js` +- `dollars.$` now works for unsigned ints with `nim js`. - Improvements to the `bitops` module, including bitslices, non-mutating versions of the original masking functions, `mask`/`masked`, and varargs support for `bitand`, `bitor`, and `bitxor`. - `sugar.=>` and `sugar.->` changes: Previously `(x, y: int)` was transformed - into `(x: auto, y: int)`, it now becomes `(x: int, y: int)` in consistency + into `(x: auto, y: int)`, it now becomes `(x: int, y: int)` for consistency with regular proc definitions (although you cannot use semicolons). Pragmas and using a name are now allowed on the lefthand side of `=>`. Here - is an aggregate example of these changes: + is an example of these changes: ```nim import sugar @@ -106,8 +106,8 @@ The proc `times.isInitialized` has been added which can be used to check if a `DateTime` has been initialized. -- 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). +- 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 (e.g. 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`. @@ -129,37 +129,35 @@ - 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 `rstgen.rstToLatex` a convenience proc for `renderRstToOut` and `initRstGenerator`. +- Added `os.normalizeExe`. +- `macros.newLit` now preserves named vs unnamed tuples. - 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. +- Added `typetraits.elementType` to get the 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 +- Added `macros.extractDocCommentsAndRunnables` helper. -- `strformat.fmt` and `strformat.&` support `= specifier`. `fmt"{expr=}"` now +- `strformat.fmt` and `strformat.&` support `specifier =`. `fmt"{expr=}"` now expands to `fmt"expr={expr}"`. -- deprecations: `os.existsDir` => `dirExists`, `os.existsFile` => `fileExists` +- Deprecations: instead of `os.existsDir` use `dirExists`, instead of `os.existsFile` use `fileExists`. -- Added `jsre` module, [Regular Expressions for the JavaScript target.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions) +- Added the `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, +- Changed `log` in `logging` to use proper log level for JavaScript, 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`. -- Removed `sugar.distinctBase`, deprecated since `0.19`. -- Export `asyncdispatch.PDispatcher.handles` so that an external library can register them. + `CountTable.inc` takes `val: int` again not `val: Positive`; i.e. it can "count down" again. +- Removed deprecated symbols from `macros` module, some of which were deprecated already in `0.15`. +- Removed `sugar.distinctBase`, deprecated since `0.19`. Use `typetraits.distinctBase`. +- `asyncdispatch.PDispatcher.handles` is exported so that an external low-level libraries can access it. -- `std/with`, `sugar.dup` now support object field assignment expression: +- `std/with`, `sugar.dup` now support object field assignment expressions: ```nim import std/with @@ -195,9 +193,9 @@ - Added `initUri(isIpv6: bool)` to `uri` module, now `uri` supports parsing ipv6 hostname. -- Added `readLines(p: Process)` to `osproc` module for `startProcess` convenience. +- Added `readLines(p: Process)` to `osproc`. -- Added the below `to` procs for collections. The usage is similar to procs such as +- Added the below `toX` procs for collections. The usage is similar to procs such as `sets.toHashSet` and `tables.toTable`. Previously, it was necessary to create the respective empty collection and add items manually. * `critbits.toCritBitTree`, which creates a `CritBitTree` from an `openArray` of @@ -209,7 +207,7 @@ - Added `progressInterval` argument to `asyncftpclient.newAsyncFtpClient` to control the interval at which progress callbacks are called. -- Added `os.copyFileToDir` +- Added `os.copyFileToDir`. ## Language changes @@ -217,20 +215,20 @@ `wasMoved` calls where needed. - The `=` hook is now called `=copy` for clarity. The old name `=` is still available so there is no need to update your code. This change was backported to 1.2 too so you can use the - more readability `=copy` without loss of compatibility. + more readable `=copy` without loss of compatibility. - In the newruntime it is now allowed to assign to the discriminator field - without restrictions as long as case object doesn't have custom destructor. + without restrictions as long as the case object doesn't have a custom destructor. The discriminator value doesn't have to be a constant either. If you have a custom destructor for a case object and you do want to freely assign discriminator - fields, it is recommended to refactor object into 2 objects like this: + fields, it is recommended to refactor the object into 2 objects like this: ```nim type MyObj = object case kind: bool - of true: y: ptr UncheckedArray[float] - of false: z: seq[int] + of true: y: ptr UncheckedArray[float] + of false: z: seq[int] proc `=destroy`(x: MyObj) = if x.kind and x.y != nil: @@ -251,10 +249,10 @@ deallocShared(x.val) ``` - `getImpl` on enum type symbols now returns field syms instead of idents. This helps - with writing typed macros. Old behavior for backwards compatibility can be restored + with writing typed macros. The old behavior for backwards compatibility can be restored with `--useVersion:1.0`. - The typed AST for proc headers will now have the arguments be syms instead of idents. - This helps with writing typed macros. Old behaviour for backwards compatibility can + This helps with writing typed macros. The old behaviour for backwards compatibility can be restored with `--useVersion:1.0`. - ``let`` statements can now be used without a value if declared with ``importc``/``importcpp``/``importjs``/``importobjc``. @@ -262,52 +260,46 @@ - Exceptions inheriting from `system.Defect` are no longer tracked with the `.raises: []` exception tracking mechanism. This is more consistent with the built-in operations. The following always used to compile (and still does): - -```nim - -proc mydiv(a, b): int {.raises: [].} = - a div b # can raise an DivByZeroDefect - -``` + ```nim + proc mydiv(a, b): int {.raises: [].} = + a div b # can raise an DivByZeroDefect + ``` Now also this compiles: - -```nim - -proc mydiv(a, b): int {.raises: [].} = - if b == 0: raise newException(DivByZeroDefect, "division by zero") - else: result = a div b - -``` + ```nim + proc mydiv(a, b): int {.raises: [].} = + if b == 0: raise newException(DivByZeroDefect, "division by zero") + else: result = a div b + ``` The reason for this is that `DivByZeroDefect` inherits from `Defect` and with `--panics:on` `Defects` become unrecoverable errors. - Added the `thiscall` calling convention as specified by Microsoft, mostly for hooking purposes. -- Deprecated `{.unroll.}` pragma, was ignored by the compiler anyways, was a nop. -- Removed `strutils.isNilOrWhitespace`, was deprecated. -- Removed `sharedtables.initSharedTable`, was deprecated and produces undefined behavior. -- Removed `asyncdispatch.newAsyncNativeSocket`, was deprecated since `0.18`. -- Removed `dom.releaseEvents` and `dom.captureEvents`, was deprecated. +- Deprecated the `{.unroll.}` pragma, because it was always ignored by the compiler anyway. +- Removed the deprecated `strutils.isNilOrWhitespace`. +- Removed the deprecated `sharedtables.initSharedTable`. +- Removed the deprecated `asyncdispatch.newAsyncNativeSocket`. +- Removed the deprecated `dom.releaseEvents` and `dom.captureEvents`. - Removed `sharedlists.initSharedList`, was deprecated and produces undefined behaviour. - There is a new experimental feature called "strictFuncs" which makes the definition of - `.noSideEffect` stricter. [See](manual_experimental.html#stricts-funcs) + `.noSideEffect` stricter. [See here](manual_experimental.html#stricts-funcs) for more information. - "for-loop macros" (see [the manual](manual.html#macros-for-loop-macros)) are no longer an experimental feature. In other words, you don't have to write pragma `{.experimental: "forLoopMacros".}` if you want to use them. -- Added a ``.noalias`` pragma. It is mapped to C's ``restrict`` keyword for the increased +- Added the ``.noalias`` pragma. It is mapped to C's ``restrict`` keyword for the increased performance this keyword can enable. -- `items` no longer compiles with enum with holes as its behavior was error prone, see #14004 +- `items` no longer compiles with enums with holes as its behavior was error prone, see #14004. - `system.deepcopy` has to be enabled explicitly for `--gc:arc` and `--gc:orc` via `--deepcopy:on`. -- Added a `std/effecttraits` module for introspection of the inferred effects. +- Added the `std/effecttraits` module for introspection of the inferred effects. We hope this enables `async` macros that are precise about the possible exceptions that can be raised. - The pragma blocks `{.gcsafe.}: ...` and `{.noSideEffect.}: ...` can now also be @@ -320,11 +312,10 @@ proc mydiv(a, b): int {.raises: [].} = - Specific warnings can now be turned into errors via `--warningAsError[X]:on|off`. - The `define` and `undef` pragmas have been de-deprecated. - New command: `nim r main.nim [args...]` which compiles and runs main.nim, and implies `--usenimcache` - so that output is saved to $nimcache/main$exeExt, using the same logic as `nim c -r` to - avoid recompiling when sources don't change. This is now the preferred way to - run tests, avoiding the usual pain of clobbering your repo with binaries or - using tricky gitignore rules on posix. Example: - ```nim + so that the output is saved to $nimcache/main$exeExt, using the same logic as `nim c -r` to + avoid recompilations when sources don't change. + Example: + ```bash nim r compiler/nim.nim --help # only compiled the first time echo 'import os; echo getCurrentCompilerExe()' | nim r - # this works too nim r compiler/nim.nim --fullhelp # no recompilation @@ -334,20 +325,18 @@ proc mydiv(a, b): int {.raises: [].} = (likewise with other hints and warnings), which is consistent with all other bool flags. (since 1.3.3). - `nim doc -r main` and `nim rst2html -r main` now call `openDefaultBrowser`. -- new hint: `--hint:msgOrigin` will show where a compiler msg (hint|warning|error) +- Added the new hint `--hint:msgOrigin` will show where a compiler msg (hint|warning|error) was generated; this helps in particular when it's non obvious where it came from either because multiple locations generate the same message, or because the message involves runtime formatting. -- new flag `--backend:js|c|cpp|objc` (or -b:js etc), to change backend; can be - used with any command (eg nim r, doc, check etc); safe to re-assign. -- new flag `--doccmd:cmd` to pass additional flags for runnableExamples, +- Added the new flag `--backend:js|c|cpp|objc` (or -b:js etc), to change the backend; can be + used with any command (e.g. nim r, doc, check etc); safe to re-assign. +- Added the new flag `--doccmd:cmd` to pass additional flags for runnableExamples, e.g.: `--doccmd:-d:foo --threads` use `--doccmd:skip` to skip runnableExamples and rst test snippets. -- new flag `--usenimcache` to output to nimcache (whatever it resolves to after - all commands are processed) - and avoids polluting both $pwd and $projectdir. It can be used with any command. +- Added the new flag `--usenimcache` to output binary files to nimcache. - `runnableExamples "-b:cpp -r:off": code` is now supported, allowing to override - how an example is compiled and run, for example to change backend or compile only. + how an example is compiled and run, for example to change the backend. - `nim doc` now outputs under `$projectPath/htmldocs` when `--outdir` is unspecified (with or without `--project`); passing `--project` now automatically generates an index and enables search. @@ -355,7 +344,7 @@ proc mydiv(a, b): int {.raises: [].} = - Removed the `--oldNewlines` switch. - Removed the `--laxStrings` switch for mutating the internal zero terminator on strings. - Removed the `--oldast` switch. -- Removed the `--oldgensym` switch +- Removed the `--oldgensym` switch. - `$getType(untyped)` is now "untyped" instead of "expr", `$getType(typed)` is now "typed" instead of "stmt". - Sink inference is now disabled per default and has to enabled explicitly via @@ -366,9 +355,9 @@ proc mydiv(a, b): int {.raises: [].} = ## Tool changes -- `nimsuggest` will now return both the forward declaration and the +- `nimsuggest` now returns both the forward declaration and the implementation location upon a `def` query. Previously the behavior was - just to return the forward declaration. + to return the forward declaration only. ## Bugfixes @@ -673,7 +662,6 @@ proc mydiv(a, b): int {.raises: [].} = ([#10465](https://github.com/nim-lang/Nim/issues/10465)) - Fixed "Same warning printed 3 times" ([#11009](https://github.com/nim-lang/Nim/issues/11009)) -Was unsure about: " add testcase for #4668 (#14946)" - Fixed "type alias for generic typeclass doesn't work" ([#4668](https://github.com/nim-lang/Nim/issues/4668)) - Fixed "exceptions:goto Bug devel codegen lvalue NIM_FALSE=NIM_FALSE" From 66cd9c2e57de027781a11f4434112ab0e5cfea7b Mon Sep 17 00:00:00 2001 From: Clyybber Date: Fri, 16 Oct 2020 18:03:01 +0200 Subject: [PATCH 0782/3768] Fix #15599 (#15601) * Fix #15599 * Add test --- compiler/semmagic.nim | 2 +- tests/arc/tarcmisc.nim | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 5cd7b28b0..cffd58b92 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -527,7 +527,7 @@ 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 + let fin = if n[^1].kind in {nkLambda, nkDo}: n[^1][namePos].sym else: n[^1].sym # check if we converted this finalizer into a destructor already: let t = whereToBindTypeHook(c, fin.typ[1].skipTypes(abstractInst+{tyRef})) diff --git a/tests/arc/tarcmisc.nim b/tests/arc/tarcmisc.nim index b53d49df8..8d857921e 100644 --- a/tests/arc/tarcmisc.nim +++ b/tests/arc/tarcmisc.nim @@ -26,6 +26,7 @@ new line after - @['a'] finalizer aaaaa hello +ok closed destroying variable: 20 destroying variable: 10 @@ -377,4 +378,15 @@ proc text_parser(xml: var XmlParser) = doAssert(test_passed) text_parser(xml) -text_parser(xml2) \ No newline at end of file +text_parser(xml2) + +# bug #15599 +type + PixelBuffer = ref object + +proc newPixelBuffer(): PixelBuffer = + new(result) do (buffer: PixelBuffer): + echo "ok" + +discard newPixelBuffer() + From b16b2eb06fa09b22ab50b3bd2703fe79c0ca4fc6 Mon Sep 17 00:00:00 2001 From: n5m <72841454+n5m@users.noreply.github.com> Date: Sat, 17 Oct 2020 18:30:12 +0000 Subject: [PATCH 0783/3768] document that Nim executable must be included (#15611) --- doc/testament.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/testament.rst b/doc/testament.rst index 82dfb75bd..d515ac2cb 100644 --- a/doc/testament.rst +++ b/doc/testament.rst @@ -94,7 +94,7 @@ Example "template" **to edit** and write a Testament unittest: valgrind: false # Can use Valgrind to check for memory leaks, or not (Linux 64Bit only). - cmd: "c -r $file" # Command the test should use to run. + cmd: "nim c -r $file" # Command the test should use to run. maxcodesize: 666 # Maximum generated temporary intermediate code file size for the test. From ba2a477e648513fac0183ae77990dfe16f890960 Mon Sep 17 00:00:00 2001 From: Euan Date: Sun, 18 Oct 2020 10:26:36 +0100 Subject: [PATCH 0784/3768] Fix FreeBSD build failures (#15613) The FreeBSD is/was failing, due to a conflict with installing Git. Git is guaranteed to be in the base image, so this removes that package from the package list. It also reverts back to the built-in `packages` tag since it's officially supported. Should the build fail in the future, the best place to go is the #sr.ht channel on freenode. --- .builds/freebsd.yml | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml index ef2e8f44d..6898a80fd 100644 --- a/.builds/freebsd.yml +++ b/.builds/freebsd.yml @@ -1,25 +1,19 @@ # see https://man.sr.ht/builds.sr.ht/compatibility.md#freebsd image: freebsd/latest - -# packages: -# - databases/sqlite3 -# - devel/boehm-gc-threaded -# - devel/pcre -# - devel/sdl20 -# - devel/sfml -# - www/node -# - devel/gmake -# - devel/git +packages: +- databases/sqlite3 +- devel/boehm-gc-threaded +- devel/pcre +- devel/sdl20 +- devel/sfml +- www/node +- devel/gmake sources: - https://github.com/nim-lang/Nim environment: CC: /usr/bin/clang tasks: - setup: | - # workaround https://github.com/timotheecour/Nim/issues/76 - sudo pkg update -q -f - sudo pkg install -y -q databases/sqlite3 devel/boehm-gc-threaded devel/pcre \ - devel/sdl20 devel/sfml www/node devel/gmake devel/git cd Nim git clone --depth 1 -q https://github.com/nim-lang/csources.git gmake -C csources -j $(sysctl -n hw.ncpu) From 78cbb52fa01d6c73307fe276bdf7d4c34d7a556c Mon Sep 17 00:00:00 2001 From: n5m <72841454+n5m@users.noreply.github.com> Date: Sun, 18 Oct 2020 16:57:13 +0000 Subject: [PATCH 0785/3768] add tests for #15584 (#15619) * add test for #15584 * Rename userlocks.nim to trlocks.nim * add bug comment * improve cmd * reference bugfix, not bug * add test that runs rlocks * disallow joining joining with other test cases (e.g., a test case that invokes `RLock.initRLock()`) may cause this test to compile, when the goal is to test if these lines can be compiled in isolation --- tests/stdlib/trlocks.nim | 14 ++++++++++++++ tests/stdlib/tuserlocks.nim | 13 +++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 tests/stdlib/trlocks.nim create mode 100644 tests/stdlib/tuserlocks.nim diff --git a/tests/stdlib/trlocks.nim b/tests/stdlib/trlocks.nim new file mode 100644 index 000000000..135d9b028 --- /dev/null +++ b/tests/stdlib/trlocks.nim @@ -0,0 +1,14 @@ +discard """ + action: "compile" + # Disallow joining to ensure it can compile in isolation. + # See #15584 + joinable: false + cmd: "nim $target --threads:on $options $file" +""" + +# bugfix #15584 + +import rlocks + +var r: RLock +r.initRLock() diff --git a/tests/stdlib/tuserlocks.nim b/tests/stdlib/tuserlocks.nim new file mode 100644 index 000000000..f6eafa05d --- /dev/null +++ b/tests/stdlib/tuserlocks.nim @@ -0,0 +1,13 @@ +discard """ + cmd: "nim $target --threads:on $options $file" +""" + +import rlocks + +var r: RLock +r.initRLock() +doAssert r.tryAcquire() +doAssert r.tryAcquire() +r.release() +r.release() +r.deinitRLock() From 9c34d6ee09e73b200d2bd7fc40ba727c3f9773f6 Mon Sep 17 00:00:00 2001 From: Danil Yarantsev Date: Mon, 19 Oct 2020 13:58:33 +0300 Subject: [PATCH 0786/3768] Add discord to the README (#15634) --- readme.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 4ef10f231..0ccfc8fa9 100644 --- a/readme.md +++ b/readme.md @@ -9,6 +9,7 @@ the latest release, check out [Nim's website][nim-site] or [bleeding edge docs]( ## Community [![Join the IRC chat][badge-nim-irc]][nim-irc] +[![Join the Discord server][badge-nim-discord]][nim-discord] [![Join the Gitter chat][badge-nim-gitter]][nim-gitter] [![Get help][badge-nim-forum-gethelp]][nim-forum] [![View Nim posts on Stack Overflow][badge-nim-stackoverflow]][nim-stackoverflow-newest] @@ -17,10 +18,12 @@ the latest release, check out [Nim's website][nim-site] or [bleeding edge docs]( * The [forum][nim-forum] - the best place to ask questions and to discuss Nim. * [#nim IRC Channel (Freenode)][nim-irc] - a place to discuss Nim in real-time. Also where most development decisions get made. +* [Discord][nim-discord] - an additional place to discuss Nim in real-time. Most + channels there are bridged to IRC. * [Gitter][nim-gitter] - an additional place to discuss Nim in real-time. There is a bridge between Gitter and the IRC channel. * [Telegram][nim-telegram] - an additional place to discuss Nim in real-time. There - is the official Telegram channel. + is the official Telegram channel. Not bridged to IRC. * [Stack Overflow][nim-stackoverflow] - a popular Q/A site for programming related topics that includes posts about Nim. * [Github Wiki][nim-wiki] - Misc user-contributed content. @@ -211,6 +214,7 @@ Copyright © 2006-2020 Andreas Rumpf, all rights reserved. [nim-twitter]: https://twitter.com/nim_lang [nim-stackoverflow]: https://stackoverflow.com/questions/tagged/nim-lang [nim-stackoverflow-newest]: https://stackoverflow.com/questions/tagged/nim-lang?sort=newest&pageSize=15 +[nim-discord]: https://discord.gg/nim [nim-gitter]: https://gitter.im/nim-lang/Nim [nim-telegram]: https://t.me/nim_lang [nim-bountysource]: https://www.bountysource.com/teams/nim @@ -220,6 +224,7 @@ Copyright © 2006-2020 Andreas Rumpf, all rights reserved. [csources-repo]: https://github.com/nim-lang/csources [badge-nim-travisci]: https://img.shields.io/travis/nim-lang/Nim/devel.svg?style=flat-square [badge-nim-irc]: https://img.shields.io/badge/chat-on_irc-blue.svg?style=flat-square +[badge-nim-discord]: https://img.shields.io/discord/371759389889003530?label=discord&logo=discord&logoColor=gold&style=flat-square [badge-nim-gitter]: https://img.shields.io/badge/chat-on_gitter-blue.svg?style=flat-square [badge-nim-forum-gethelp]: https://img.shields.io/badge/Forum-get%20help-4eb899.svg?style=flat-square [badge-nim-twitter]: https://img.shields.io/twitter/follow/nim_lang.svg?style=social From 528069a9678fb7f9691ea78e5fd4eec905767b78 Mon Sep 17 00:00:00 2001 From: Danil Yarantsev Date: Mon, 19 Oct 2020 14:02:51 +0300 Subject: [PATCH 0787/3768] Change discord's badge color to be consistent with other badges --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 0ccfc8fa9..8fcf4d2bb 100644 --- a/readme.md +++ b/readme.md @@ -224,7 +224,7 @@ Copyright © 2006-2020 Andreas Rumpf, all rights reserved. [csources-repo]: https://github.com/nim-lang/csources [badge-nim-travisci]: https://img.shields.io/travis/nim-lang/Nim/devel.svg?style=flat-square [badge-nim-irc]: https://img.shields.io/badge/chat-on_irc-blue.svg?style=flat-square -[badge-nim-discord]: https://img.shields.io/discord/371759389889003530?label=discord&logo=discord&logoColor=gold&style=flat-square +[badge-nim-discord]: https://img.shields.io/discord/371759389889003530?color=blue&label=discord&logo=discord&logoColor=gold&style=flat-square [badge-nim-gitter]: https://img.shields.io/badge/chat-on_gitter-blue.svg?style=flat-square [badge-nim-forum-gethelp]: https://img.shields.io/badge/Forum-get%20help-4eb899.svg?style=flat-square [badge-nim-twitter]: https://img.shields.io/twitter/follow/nim_lang.svg?style=social From d22ab0fb9688f4322ba438af88927b414f3a898c Mon Sep 17 00:00:00 2001 From: Danil Yarantsev Date: Mon, 19 Oct 2020 14:16:56 +0300 Subject: [PATCH 0788/3768] Add tests to #15363 (#15633) --- tests/vm/tvmmisc.nim | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/tests/vm/tvmmisc.nim b/tests/vm/tvmmisc.nim index 7ac75c7c5..73faac495 100644 --- a/tests/vm/tvmmisc.nim +++ b/tests/vm/tvmmisc.nim @@ -204,3 +204,29 @@ block: # bug #13081 doAssert j1.x1 == 12 static: doAssert j1.x1 == 12 + +# bug #15363 +import sequtils + +block: + func identity(a: bool): bool = a + + var a: seq[bool] = static: + newSeq[bool](0).mapIt(it) # segfaults + var b: seq[bool] = static: + newSeq[bool](0).filterIt(it) # does not segfault + var c: seq[bool] = static: + newSeq[bool](0).map(identity) # does not segfault + var d: seq[bool] = static: + newSeq[bool](0).map(proc (a: bool): bool = false) # segfaults + var e: seq[bool] = static: + newSeq[bool](0).filter(identity) # does not segfault + var f: seq[bool] = static: + newSeq[bool](0).filter(proc (a: bool): bool = false) # segfaults + + doAssert a == @[] + doAssert b == @[] + doAssert c == @[] + doAssert d == @[] + doAssert e == @[] + doAssert f == @[] From 04f8fcfbd06e72eb45f36e487a110316370e47c1 Mon Sep 17 00:00:00 2001 From: cooldome Date: Mon, 19 Oct 2020 13:13:26 +0100 Subject: [PATCH 0789/3768] Tables, use sink val arguments more actively (#15625) --- compiler/sempass2.nim | 39 +++++++++++++++--------------- lib/pure/collections/tableimpl.nim | 2 +- lib/pure/collections/tables.nim | 18 +++++++------- 3 files changed, 30 insertions(+), 29 deletions(-) diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index aa04f7451..352871412 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -514,25 +514,26 @@ proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) = #elif n.kind in nkSymChoices: # echo "came here" let paramType = paramType.skipTypesOrNil(abstractInst) - if paramType != nil and tfNotNil in paramType.flags and - n.typ != nil and tfNotNil notin n.typ.flags: - if isAddrNode(n): - # addr(x[]) can't be proven, but addr(x) can: - if not containsNode(n, {nkDerefExpr, nkHiddenDeref}): return - elif (n.kind == nkSym and n.sym.kind in routineKinds) or - (n.kind in procDefs+{nkObjConstr, nkBracket, nkClosure, nkStrLit..nkTripleStrLit}) or - (n.kind in nkCallKinds and n[0].kind == nkSym and n[0].sym.magic == mArrToSeq) or - n.typ.kind == tyTypeDesc: - # 'p' is not nil obviously: - return - case impliesNotNil(tracked.guards, n) - of impUnknown: - message(tracked.config, n.info, errGenerated, - "cannot prove '$1' is not nil" % n.renderTree) - of impNo: - message(tracked.config, n.info, errGenerated, - "'$1' is provably nil" % n.renderTree) - of impYes: discard + if paramType != nil and tfNotNil in paramType.flags and n.typ != nil: + let ntyp = n.typ.skipTypesOrNil({tyVar, tyLent, tySink}) + if ntyp != nil and tfNotNil notin ntyp.flags: + if isAddrNode(n): + # addr(x[]) can't be proven, but addr(x) can: + if not containsNode(n, {nkDerefExpr, nkHiddenDeref}): return + elif (n.kind == nkSym and n.sym.kind in routineKinds) or + (n.kind in procDefs+{nkObjConstr, nkBracket, nkClosure, nkStrLit..nkTripleStrLit}) or + (n.kind in nkCallKinds and n[0].kind == nkSym and n[0].sym.magic == mArrToSeq) or + n.typ.kind == tyTypeDesc: + # 'p' is not nil obviously: + return + case impliesNotNil(tracked.guards, n) + of impUnknown: + message(tracked.config, n.info, errGenerated, + "cannot prove '$1' is not nil" % n.renderTree) + of impNo: + message(tracked.config, n.info, errGenerated, + "'$1' is provably nil" % n.renderTree) + of impYes: discard proc assumeTheWorst(tracked: PEffects; n: PNode; op: PType) = addRaiseEffect(tracked, createRaise(tracked.graph, n), nil) diff --git a/lib/pure/collections/tableimpl.nim b/lib/pure/collections/tableimpl.nim index df9cb7517..c66da513c 100644 --- a/lib/pure/collections/tableimpl.nim +++ b/lib/pure/collections/tableimpl.nim @@ -27,7 +27,7 @@ proc rawGetDeep[X, A](t: X, key: A, hc: var Hash): int {.inline.} = rawGetDeepImpl() proc rawInsert[X, A, B](t: var X, data: var KeyValuePairSeq[A, B], - key: A, val: B, hc: Hash, h: Hash) = + key: A, val: sink B, hc: Hash, h: Hash) = rawInsertImpl() template checkIfInitialized() = diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index 85440bd2b..bd46bdd27 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -300,7 +300,7 @@ proc initTable*[A, B](initialSize = defaultInitialSize): Table[A, B] = b = initTable[char, seq[int]]() initImpl(result, initialSize) -proc `[]=`*[A, B](t: var Table[A, B], key: A, val: B) = +proc `[]=`*[A, B](t: var Table[A, B], key: A, val: sink B) = ## Inserts a ``(key, value)`` pair into ``t``. ## ## See also: @@ -484,7 +484,7 @@ proc len*[A, B](t: Table[A, B]): int = result = t.counter -proc add*[A, B](t: var Table[A, B], key: A, val: B) {.deprecated: +proc add*[A, B](t: var Table[A, B], key: A, val: sink B) {.deprecated: "Deprecated since v1.4; it was more confusing than useful, use `[]=`".} = ## Puts a new ``(key, value)`` pair into ``t`` even if ``t[key]`` already exists. ## @@ -839,7 +839,7 @@ proc `[]`*[A, B](t: TableRef[A, B], key: A): var B = result = t[][key] -proc `[]=`*[A, B](t: TableRef[A, B], key: A, val: B) = +proc `[]=`*[A, B](t: TableRef[A, B], key: A, val: sink B) = ## Inserts a ``(key, value)`` pair into ``t``. ## ## See also: @@ -968,7 +968,7 @@ proc len*[A, B](t: TableRef[A, B]): int = result = t.counter -proc add*[A, B](t: TableRef[A, B], key: A, val: B) {.deprecated: +proc add*[A, B](t: TableRef[A, B], key: A, val: sink B) {.deprecated: "Deprecated since v1.4; it was more confusing than useful, use `[]=`".} = ## Puts a new ``(key, value)`` pair into ``t`` even if ``t[key]`` already exists. ## @@ -1217,7 +1217,7 @@ proc rawGet[A, B](t: OrderedTable[A, B], key: A, hc: var Hash): int = proc rawInsert[A, B](t: var OrderedTable[A, B], data: var OrderedKeyValuePairSeq[A, B], - key: A, val: B, hc: Hash, h: Hash) = + key: A, val: sink B, hc: Hash, h: Hash) = rawInsertImpl() data[h].next = -1 if t.first < 0: t.first = h @@ -1268,7 +1268,7 @@ proc initOrderedTable*[A, B](initialSize = defaultInitialSize): OrderedTable[A, b = initOrderedTable[char, seq[int]]() initImpl(result, initialSize) -proc `[]=`*[A, B](t: var OrderedTable[A, B], key: A, val: B) = +proc `[]=`*[A, B](t: var OrderedTable[A, B], key: A, val: sink B) = ## Inserts a ``(key, value)`` pair into ``t``. ## ## See also: @@ -1455,7 +1455,7 @@ proc len*[A, B](t: OrderedTable[A, B]): int {.inline.} = result = t.counter -proc add*[A, B](t: var OrderedTable[A, B], key: A, val: B) {.deprecated: +proc add*[A, B](t: var OrderedTable[A, B], key: A, val: sink B) {.deprecated: "Deprecated since v1.4; it was more confusing than useful, use `[]=`".} = ## Puts a new ``(key, value)`` pair into ``t`` even if ``t[key]`` already exists. ## @@ -1810,7 +1810,7 @@ proc `[]`*[A, B](t: OrderedTableRef[A, B], key: A): var B = echo a['z'] result = t[][key] -proc `[]=`*[A, B](t: OrderedTableRef[A, B], key: A, val: B) = +proc `[]=`*[A, B](t: OrderedTableRef[A, B], key: A, val: sink B) = ## Inserts a ``(key, value)`` pair into ``t``. ## ## See also: @@ -1939,7 +1939,7 @@ proc len*[A, B](t: OrderedTableRef[A, B]): int {.inline.} = result = t.counter -proc add*[A, B](t: OrderedTableRef[A, B], key: A, val: B) {.deprecated: +proc add*[A, B](t: OrderedTableRef[A, B], key: A, val: sink B) {.deprecated: "Deprecated since v1.4; it was more confusing than useful, use `[]=`".} = ## Puts a new ``(key, value)`` pair into ``t`` even if ``t[key]`` already exists. ## From 7526472a6ed56db11ec141ca4898de467b62696b Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 19 Oct 2020 05:14:43 -0700 Subject: [PATCH 0790/3768] add a section on naming new stdlib module names (#15590) --- doc/contributing.rst | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/doc/contributing.rst b/doc/contributing.rst index a4baaef05..9c567cb0b 100644 --- a/doc/contributing.rst +++ b/doc/contributing.rst @@ -555,3 +555,17 @@ to existing modules is acceptable. For two reasons: Newly introduced issues have to be balanced against motivating new people. We know where to find perfectly designed pieces of software that have no bugs -- these are the systems that nobody uses. + +Conventions +----------- +1. New stdlib modules should go under `Nim/lib/std/`. The rationale is to require +users to import via `import std/foo` instead of `import foo`, which would cause +potential conflicts with nimble packages. Note that this still applies for new modules +in existing logical directories, eg: +use `lib/std/collections/foo.nim`, not `lib/pure/collections/foo.nim`. + +2. New module names should prefer plural form whenever possible, eg: +`std/sums.nim` instead of `std/sum.nim`. In particular, this reduces chances of conflicts +between module name and the symbols it defines. Furthermore, is should use `snake_case` +and not use capital letters, which cause issues when going from an OS without case +sensitivity to an OS without it. From 67afe72e9caa8df435738c9011135d7053e01eb2 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 19 Oct 2020 05:49:06 -0700 Subject: [PATCH 0791/3768] contributing docs: add instructions for economically restarting failed CI jobs, debugging failed CI (#15576) * contributing docs: add instructions for economically restarting failed CI jobs, debugging failed CI * fixup --- doc/contributing.rst | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/doc/contributing.rst b/doc/contributing.rst index 9c567cb0b..cc71a62f3 100644 --- a/doc/contributing.rst +++ b/doc/contributing.rst @@ -444,12 +444,31 @@ Continuous Integration (CI) `_ and `Travis `_. -2. Consider enabling CI (travis and appveyor) in your own Nim fork, and +2. Consider enabling CI (azure, github actions and builds.sr.ht) in your own Nim fork, and waiting for CI to be green in that fork (fixing bugs as needed) before opening your PR in original Nim repo, so as to reduce CI congestion. Same applies for updates on a PR: you can test commits on a separate private branch before updating the main PR. +Debugging CI failures, flaky tests, etc +--------------------------------------- + +1. First check the CI logs and search for `FAIL` to find why CI failed; if the + failure seems related to your PR, try to fix the code instead of restarting CI. + +2. If CI failure seems unrelated to your PR, it could be caused by a flaky test. + File a bug for it if it isn't already reported. A PR push (or opening/closing PR) + will re-trigger all CI jobs (even successful ones, which can be wasteful). Instead, + follow these instructions to only restart the jobs that failed: + + * Azure: if on your own fork, it's possible from inside azure console + (eg `dev.azure.com/username/username/_build/results?buildId=1430&view=results`) via `rerun failed jobs` on top. + If either on you own fork or in Nim repo, it's possible from inside github UI + under checks tab, see https://github.com/timotheecour/Nim/issues/211#issuecomment-629751569 + * github actions: under "Checks" tab, click "Re-run jobs" in the right. + * builds.sr.ht: create a sourcehut account so you can restart a PR job as illustrated + + Code reviews ------------ From 833035bacd91c2cf16ea006b64bf996e87eac75d Mon Sep 17 00:00:00 2001 From: cooldome Date: Mon, 19 Oct 2020 13:50:06 +0100 Subject: [PATCH 0792/3768] arc allocation method aligned (#15588) * progress * fix typo --- compiler/ccgexprs.nim | 24 ++++++++++++++------- lib/system/arc.nim | 8 ------- lib/system/assign.nim | 4 +--- lib/system/cellseqs_v2.nim | 16 +++++++------- lib/system/memalloc.nim | 2 +- lib/system/seqs_v2.nim | 15 ++++++++++--- lib/system/seqs_v2_reimpl.nim | 7 ++++++ lib/system/strs_v2.nim | 40 ++++++++++++++++++++++++++++------- lib/system/widestrs.nim | 10 +++++++-- 9 files changed, 85 insertions(+), 41 deletions(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index c1469fa60..004b8f41e 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2180,17 +2180,25 @@ proc genDestroy(p: BProc; n: PNode) = of tyString: var a: TLoc initLocExpr(p, arg, a) - linefmt(p, cpsStmts, "if ($1.p && !($1.p->cap & NIM_STRLIT_FLAG)) {$n" & - " #deallocShared($1.p);$n" & - "}$n", - [rdLoc(a)]) + if optThreads in p.config.globalOptions: + linefmt(p, cpsStmts, "if ($1.p && !($1.p->cap & NIM_STRLIT_FLAG)) {$n" & + " #deallocShared($1.p);$n" & + "}$n", [rdLoc(a)]) + else: + linefmt(p, cpsStmts, "if ($1.p && !($1.p->cap & NIM_STRLIT_FLAG)) {$n" & + " #dealloc($1.p);$n" & + "}$n", [rdLoc(a)]) of tySequence: var a: TLoc initLocExpr(p, arg, a) - linefmt(p, cpsStmts, "if ($1.p && !($1.p->cap & NIM_STRLIT_FLAG)) {$n" & - " #deallocShared($1.p);$n" & - "}$n", - [rdLoc(a), getTypeDesc(p.module, t.lastSon)]) + if optThreads in p.config.globalOptions: + linefmt(p, cpsStmts, "if ($1.p && !($1.p->cap & NIM_STRLIT_FLAG)) {$n" & + " #deallocShared($1.p);$n" & + "}$n", [rdLoc(a)]) + else: + linefmt(p, cpsStmts, "if ($1.p && !($1.p->cap & NIM_STRLIT_FLAG)) {$n" & + " #dealloc($1.p);$n" & + "}$n", [rdLoc(a)]) else: discard "nothing to do" else: let t = n[1].typ.skipTypes(abstractVar) diff --git a/lib/system/arc.nim b/lib/system/arc.nim index 3e24c2506..46579eaef 100644 --- a/lib/system/arc.nim +++ b/lib/system/arc.nim @@ -79,10 +79,6 @@ proc nimNewObj(size: int): pointer {.compilerRtl.} = let s = size + sizeof(RefHeader) when defined(nimscript): discard - elif defined(useMalloc): - var orig = c_malloc(cuint s) - nimZeroMem(orig, s) - result = orig +! sizeof(RefHeader) elif compileOption("threads"): result = allocShared0(s) +! sizeof(RefHeader) else: @@ -102,8 +98,6 @@ proc nimNewObjUninit(size: int): pointer {.compilerRtl.} = let s = size + sizeof(RefHeader) when defined(nimscript): discard - elif defined(useMalloc): - var orig = cast[ptr RefHeader](c_malloc(cuint s)) elif compileOption("threads"): var orig = cast[ptr RefHeader](allocShared(s)) else: @@ -171,8 +165,6 @@ proc nimRawDispose(p: pointer) {.compilerRtl.} = # we do NOT really free the memory here in order to reliably detect use-after-frees if freedCells.data == nil: init(freedCells) freedCells.incl head(p) - elif defined(useMalloc): - c_free(p -! sizeof(RefHeader)) elif compileOption("threads"): deallocShared(p -! sizeof(RefHeader)) else: diff --git a/lib/system/assign.nim b/lib/system/assign.nim index d332124cd..a809fa423 100644 --- a/lib/system/assign.nim +++ b/lib/system/assign.nim @@ -248,9 +248,7 @@ proc genericReset(dest: pointer, mt: PNimType) = unsureAsgnRef(cast[PPointer](dest), nil) of tySequence: when defined(nimSeqsV2): - var s = cast[ptr NimSeqV2Reimpl](dest) - if s.p != nil: - deallocShared(s.p) + frees(cast[ptr NimSeqV2Reimpl](dest)[]) zeroMem(dest, mt.size) else: unsureAsgnRef(cast[PPointer](dest), nil) diff --git a/lib/system/cellseqs_v2.nim b/lib/system/cellseqs_v2.nim index fdd9e3099..59cf67dcd 100644 --- a/lib/system/cellseqs_v2.nim +++ b/lib/system/cellseqs_v2.nim @@ -19,13 +19,13 @@ type proc add(s: var CellSeq, c: PT; t: PNimTypeV2) {.inline.} = if s.len >= s.cap: s.cap = s.cap * 3 div 2 - when defined(useMalloc): - var d = cast[CellArray](c_malloc(uint(s.cap * sizeof(CellTuple)))) + when compileOption("threads"): + var d = cast[CellArray](allocShared(uint(s.cap * sizeof(CellTuple)))) else: var d = cast[CellArray](alloc(s.cap * sizeof(CellTuple))) copyMem(d, s.d, s.len * sizeof(CellTuple)) - when defined(useMalloc): - c_free(s.d) + when compileOption("threads"): + deallocShared(s.d) else: dealloc(s.d) s.d = d @@ -36,15 +36,15 @@ proc add(s: var CellSeq, c: PT; t: PNimTypeV2) {.inline.} = proc init(s: var CellSeq, cap: int = 1024) = s.len = 0 s.cap = cap - when defined(useMalloc): - s.d = cast[CellArray](c_malloc(uint(s.cap * sizeof(CellTuple)))) + when compileOption("threads"): + s.d = cast[CellArray](allocShared(uint(s.cap * sizeof(CellTuple)))) else: s.d = cast[CellArray](alloc(s.cap * sizeof(CellTuple))) proc deinit(s: var CellSeq) = if s.d != nil: - when defined(useMalloc): - c_free(s.d) + when compileOption("threads"): + deallocShared(s.d) else: dealloc(s.d) s.d = nil diff --git a/lib/system/memalloc.nim b/lib/system/memalloc.nim index 178d199b8..142762fe7 100644 --- a/lib/system/memalloc.nim +++ b/lib/system/memalloc.nim @@ -174,7 +174,7 @@ when hasAlloc and not defined(js): ## from a shared heap. cast[ptr T](realloc(p, T.sizeof * newSize)) - template dealloc*(p: pointer) = + proc dealloc*(p: pointer) {.noconv, compilerproc, rtl, benign, raises: [], tags: [].} = ## Frees the memory allocated with ``alloc``, ``alloc0`` or ## ``realloc``. ## diff --git a/lib/system/seqs_v2.nim b/lib/system/seqs_v2.nim index 111299fff..d83a0009a 100644 --- a/lib/system/seqs_v2.nim +++ b/lib/system/seqs_v2.nim @@ -35,7 +35,10 @@ proc newSeqPayload(cap, elemSize, elemAlign: int): pointer {.compilerRtl, raises # we have to use type erasure here as Nim does not support generic # compilerProcs. Oh well, this will all be inlined anyway. if cap > 0: - var p = cast[ptr NimSeqPayloadBase](allocShared0(align(sizeof(NimSeqPayloadBase), elemAlign) + cap * elemSize)) + when compileOption("threads"): + var p = cast[ptr NimSeqPayloadBase](allocShared0(align(sizeof(NimSeqPayloadBase), elemAlign) + cap * elemSize)) + else: + var p = cast[ptr NimSeqPayloadBase](alloc0(align(sizeof(NimSeqPayloadBase), elemAlign) + cap * elemSize)) p.cap = cap result = p else: @@ -62,14 +65,20 @@ proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize, elemAlign: int): poin let oldCap = p.cap and not strlitFlag let newCap = max(resize(oldCap), len+addlen) if (p.cap and strlitFlag) == strlitFlag: - var q = cast[ptr NimSeqPayloadBase](allocShared0(headerSize + elemSize * newCap)) + when compileOption("threads"): + var q = cast[ptr NimSeqPayloadBase](allocShared0(headerSize + elemSize * newCap)) + else: + var q = cast[ptr NimSeqPayloadBase](alloc0(headerSize + elemSize * newCap)) copyMem(q +! headerSize, p +! headerSize, len * elemSize) q.cap = newCap result = q else: let oldSize = headerSize + elemSize * oldCap let newSize = headerSize + elemSize * newCap - var q = cast[ptr NimSeqPayloadBase](reallocShared0(p, oldSize, newSize)) + when compileOption("threads"): + var q = cast[ptr NimSeqPayloadBase](reallocShared0(p, oldSize, newSize)) + else: + var q = cast[ptr NimSeqPayloadBase](realloc0(p, oldSize, newSize)) q.cap = newCap result = q diff --git a/lib/system/seqs_v2_reimpl.nim b/lib/system/seqs_v2_reimpl.nim index 750f78c0d..09b7e7ac4 100644 --- a/lib/system/seqs_v2_reimpl.nim +++ b/lib/system/seqs_v2_reimpl.nim @@ -15,3 +15,10 @@ type NimSeqV2Reimpl = object len: int p: ptr NimSeqPayloadReimpl + +template frees(s: NimSeqV2Reimpl) = + if s.p != nil and (s.p.cap and strlitFlag) != strlitFlag: + when compileOption("threads"): + deallocShared(s.p) + else: + dealloc(s.p) \ No newline at end of file diff --git a/lib/system/strs_v2.nim b/lib/system/strs_v2.nim index 57fdd5ee1..f51791da2 100644 --- a/lib/system/strs_v2.nim +++ b/lib/system/strs_v2.nim @@ -29,7 +29,10 @@ template contentSize(cap): int = cap + 1 + sizeof(NimStrPayloadBase) template frees(s) = if not isLiteral(s): - deallocShared(s.p) + when compileOption("threads"): + deallocShared(s.p) + else: + dealloc(s.p) proc resize(old: int): int {.inline.} = if old <= 0: result = 4 @@ -40,7 +43,10 @@ proc prepareAdd(s: var NimStringV2; addlen: int) {.compilerRtl.} = if isLiteral(s): let oldP = s.p # can't mutate a literal, so we need a fresh copy here: - s.p = cast[ptr NimStrPayload](allocShared0(contentSize(s.len + addlen))) + when compileOption("threads"): + s.p = cast[ptr NimStrPayload](allocShared0(contentSize(s.len + addlen))) + else: + s.p = cast[ptr NimStrPayload](alloc0(contentSize(s.len + addlen))) s.p.cap = s.len + addlen if s.len > 0: # we are about to append, so there is no need to copy the \0 terminator: @@ -49,7 +55,10 @@ proc prepareAdd(s: var NimStringV2; addlen: int) {.compilerRtl.} = let oldCap = s.p.cap and not strlitFlag if s.len + addlen > oldCap: let newCap = max(s.len + addlen, resize(oldCap)) - s.p = cast[ptr NimStrPayload](reallocShared0(s.p, contentSize(oldCap), contentSize(newCap))) + when compileOption("threads"): + s.p = cast[ptr NimStrPayload](reallocShared0(s.p, contentSize(oldCap), contentSize(newCap))) + else: + s.p = cast[ptr NimStrPayload](realloc0(s.p, contentSize(oldCap), contentSize(newCap))) s.p.cap = newCap proc nimAddCharV1(s: var NimStringV2; c: char) {.compilerRtl, inline.} = @@ -63,7 +72,10 @@ proc toNimStr(str: cstring, len: int): NimStringV2 {.compilerproc.} = if len <= 0: result = NimStringV2(len: 0, p: nil) else: - var p = cast[ptr NimStrPayload](allocShared0(contentSize(len))) + when compileOption("threads"): + var p = cast[ptr NimStrPayload](allocShared0(contentSize(len))) + else: + var p = cast[ptr NimStrPayload](alloc0(contentSize(len))) p.cap = len if len > 0: # we are about to append, so there is no need to copy the \0 terminator: @@ -94,7 +106,10 @@ proc rawNewString(space: int): NimStringV2 {.compilerproc.} = if space <= 0: result = NimStringV2(len: 0, p: nil) else: - var p = cast[ptr NimStrPayload](allocShared0(contentSize(space))) + when compileOption("threads"): + var p = cast[ptr NimStrPayload](allocShared0(contentSize(space))) + else: + var p = cast[ptr NimStrPayload](alloc0(contentSize(space))) p.cap = space result = NimStringV2(len: 0, p: p) @@ -102,7 +117,10 @@ proc mnewString(len: int): NimStringV2 {.compilerproc.} = if len <= 0: result = NimStringV2(len: 0, p: nil) else: - var p = cast[ptr NimStrPayload](allocShared0(contentSize(len))) + when compileOption("threads"): + var p = cast[ptr NimStrPayload](allocShared0(contentSize(len))) + else: + var p = cast[ptr NimStrPayload](alloc0(contentSize(len))) p.cap = len result = NimStringV2(len: len, p: p) @@ -128,7 +146,10 @@ proc nimAsgnStrV2(a: var NimStringV2, b: NimStringV2) {.compilerRtl.} = # 'let y = newStringOfCap(); var x = y' # on the other hand... These get turned into moves now. frees(a) - a.p = cast[ptr NimStrPayload](allocShared0(contentSize(b.len))) + when compileOption("threads"): + a.p = cast[ptr NimStrPayload](allocShared0(contentSize(b.len))) + else: + a.p = cast[ptr NimStrPayload](alloc0(contentSize(b.len))) a.p.cap = b.len a.len = b.len copyMem(unsafeAddr a.p.data[0], unsafeAddr b.p.data[0], b.len+1) @@ -136,7 +157,10 @@ proc nimAsgnStrV2(a: var NimStringV2, b: NimStringV2) {.compilerRtl.} = proc nimPrepareStrMutationImpl(s: var NimStringV2) = let oldP = s.p # can't mutate a literal, so we need a fresh copy here: - s.p = cast[ptr NimStrPayload](allocShared0(contentSize(s.len))) + when compileOption("threads"): + s.p = cast[ptr NimStrPayload](allocShared0(contentSize(s.len))) + else: + s.p = cast[ptr NimStrPayload](alloc0(contentSize(s.len))) s.p.cap = s.len copyMem(unsafeAddr s.p.data[0], unsafeAddr oldP.data[0], s.len+1) diff --git a/lib/system/widestrs.nim b/lib/system/widestrs.nim index 83c11eb79..6d54bce9b 100644 --- a/lib/system/widestrs.nim +++ b/lib/system/widestrs.nim @@ -27,7 +27,10 @@ when defined(nimv2): proc `=destroy`(a: var WideCStringObj) = if a.data != nil: - deallocShared(a.data) + when compileOption("threads"): + deallocShared(a.data) + else: + dealloc(a.data) proc `=`(a: var WideCStringObj; b: WideCStringObj) {.error.} @@ -37,7 +40,10 @@ when defined(nimv2): proc createWide(a: var WideCStringObj; bytes: int) = a.bytes = bytes - a.data = cast[typeof(a.data)](allocShared0(bytes)) + when compileOption("threads"): + a.data = cast[typeof(a.data)](allocShared0(bytes)) + else: + a.data = cast[typeof(a.data)](alloc0(bytes)) template `[]`*(a: WideCStringObj; idx: int): Utf16Char = a.data[idx] template `[]=`*(a: WideCStringObj; idx: int; val: Utf16Char) = a.data[idx] = val From 436e1fa5b13d0a5b667a800b1716e5139f1c20fe Mon Sep 17 00:00:00 2001 From: n5m <72841454+n5m@users.noreply.github.com> Date: Mon, 19 Oct 2020 20:25:43 +0000 Subject: [PATCH 0793/3768] fix #15631 (#15632) * trigger valgrind failure on memory leak * remove non-malloc tests * remove ORC test is redundant because we already have an ARC test * only run valgrind tests on 64-bit Linux * disable freebsd and openbsd * Remove tleak_refc As to not test implementation details (or bug) * Fix test failures by removing redundant test Since this tests/shoulfail/tvalgrind.nim was specified here to fail this test itself fails since it will be skipped on non-linux CI * Remove test, reason detailed in the previous commit * Remove redundant disables * Revert removing disables * Add and use valgrind: leaks * Fix Co-authored-by: Clyybber Co-authored-by: n5m --- doc/testament.rst | 5 +++++ testament/specs.nim | 14 +++++++++----- testament/testament.nim | 7 +++++-- tests/arc/tasyncorc.nim | 2 +- tests/arc/thavlak_orc_stress.nim | 2 +- tests/valgrind/tleak_arc.nim | 14 ++++++++++++++ 6 files changed, 35 insertions(+), 9 deletions(-) create mode 100644 tests/valgrind/tleak_arc.nim diff --git a/doc/testament.rst b/doc/testament.rst index d515ac2cb..1b2b71c18 100644 --- a/doc/testament.rst +++ b/doc/testament.rst @@ -103,6 +103,11 @@ Example "template" **to edit** and write a Testament unittest: target: "c js" # Targets to run the test into (C, C++, JavaScript, etc). disabled: "bsd" # Disable the test by condition, here BSD is disabled just as an example. + disabled: "win" # Can disable multiple OSes at once + disabled: "32bit" # ...or architectures + disabled: "i386" + disabled: "azure" # ...or pipeline runners + disabled: true # ...or can disable the test entirely """ assert true diff --git a/testament/specs.nim b/testament/specs.nim index e0b9cbeec..eaae598bb 100644 --- a/testament/specs.nim +++ b/testament/specs.nim @@ -43,7 +43,7 @@ type reFilesDiffer, # expected and given filenames differ reLinesDiffer, # expected and given line numbers differ reOutputsDiffer, - reExitcodesDiffer, + reExitcodesDiffer, # exit codes of program or of valgrind differ reTimeout, reInvalidPeg, reCodegenFailure, @@ -67,6 +67,9 @@ type msg*: string line*, col*: int + ValgrindSpec* = enum + disabled, enabled, leaking + TSpec* = object action*: TTestAction file*, cmd*: string @@ -92,7 +95,7 @@ type # whether this test can be batchable via `NIM_TESTAMENT_BATCH`; only very # few tests are not batchable; the ones that are not could be turned batchable # by making the dependencies explicit - useValgrind*: bool + useValgrind*: ValgrindSpec timeout*: float # in seconds, fractions possible, # but don't rely on much precision inlineErrors*: seq[InlineError] # line information to error message @@ -306,14 +309,15 @@ proc parseSpec*(filename: string): TSpec = result.unjoinable = not parseCfgBool(e.value) of "valgrind": when defined(linux) and sizeof(int) == 8: - result.useValgrind = parseCfgBool(e.value) + result.useValgrind = if e.value.normalize == "leaks": leaking + else: ValgrindSpec(parseCfgBool(e.value)) result.unjoinable = true - if result.useValgrind: + if result.useValgrind != disabled: result.outputCheck = ocSubstr else: # Windows lacks valgrind. Silly OS. # Valgrind only supports OSX <= 17.x - result.useValgrind = false + result.useValgrind = disabled of "disabled": case e.value.normalize of "y", "yes", "true", "1", "on": result.err = reDisabled diff --git a/testament/testament.nim b/testament/testament.nim index 4afafb645..8fead76ef 100644 --- a/testament/testament.nim +++ b/testament/testament.nim @@ -501,8 +501,11 @@ proc testSpecHelper(r: var TResults, test: var TTest, expected: TSpec, args = concat(@[exeFile], args) else: exeCmd = exeFile.dup(normalizeExe) - if expected.useValgrind: - args = @["--error-exitcode=1"] & exeCmd & args + if expected.useValgrind != disabled: + var valgrindOptions = @["--error-exitcode=1"] + if expected.useValgrind != leaking: + valgrindOptions.add "--leak-check=yes" + args = valgrindOptions & exeCmd & args exeCmd = "valgrind" var (_, buf, exitCode) = execCmdEx2(exeCmd, args, input = expected.input) # Treat all failure codes from nodejs as 1. Older versions of nodejs used diff --git a/tests/arc/tasyncorc.nim b/tests/arc/tasyncorc.nim index 56b8909b1..63703b559 100644 --- a/tests/arc/tasyncorc.nim +++ b/tests/arc/tasyncorc.nim @@ -1,7 +1,7 @@ discard """ output: '''230000''' cmd: '''nim c --gc:orc -d:useMalloc $file''' - valgrind: "true" + valgrind: "leaks" """ # bug #14402 diff --git a/tests/arc/thavlak_orc_stress.nim b/tests/arc/thavlak_orc_stress.nim index 3c61f10ef..862e1a097 100644 --- a/tests/arc/thavlak_orc_stress.nim +++ b/tests/arc/thavlak_orc_stress.nim @@ -1,6 +1,6 @@ discard """ cmd: "nim c --gc:orc -d:useMalloc -d:nimStressOrc $file" - valgrind: "true" + valgrind: "leaks" output: "done" """ diff --git a/tests/valgrind/tleak_arc.nim b/tests/valgrind/tleak_arc.nim new file mode 100644 index 000000000..8dea7c62a --- /dev/null +++ b/tests/valgrind/tleak_arc.nim @@ -0,0 +1,14 @@ +discard """ +valgrind: true +cmd: "nim $target --gc:arc -d:useMalloc $options $file" +exitcode: 1 +outputsub: " definitely lost: 7 bytes in 2 blocks" +disabled: "freebsd" +disabled: "macosx" +disabled: "openbsd" +disabled: "windows" +disabled: "32bit" +""" + +discard alloc(3) +discard alloc(4) From 0f4b1ed270a0f0c380122723474ec0d36c1a7e00 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Mon, 19 Oct 2020 22:39:50 +0200 Subject: [PATCH 0794/3768] Fix #15639 (#15640) --- koch.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/koch.nim b/koch.nim index 7be35fed7..e34fdb4f4 100644 --- a/koch.nim +++ b/koch.nim @@ -15,7 +15,7 @@ const when not defined(windows): const - Z3StableCommit = "65de3f748a6812eecd7db7c478dcd5fc54424d368b" # the version of Z3 that DrNim uses + Z3StableCommit = "65de3f748a6812eecd7db7c478d5fc54424d368b" # the version of Z3 that DrNim uses when defined(gcc) and defined(windows): when defined(x86): From f20e485827df6df97b8396558b10d7c59ca1b7dc Mon Sep 17 00:00:00 2001 From: Clyybber Date: Tue, 20 Oct 2020 01:19:49 +0200 Subject: [PATCH 0795/3768] Fix commentOffsetA for doc comments (#15643) --- compiler/lexer.nim | 2 +- nimpretty/tests/expected/simple.nim | 5 +++++ nimpretty/tests/simple.nim | 5 +++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 9b1dc642d..c15ecddfc 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -1042,7 +1042,7 @@ proc scanComment(L: var Lexer, tok: var Token) = tok.iNumber = 0 assert L.buf[pos+1] == '#' when defined(nimpretty): - tok.commentOffsetA = L.offsetBase + pos - 1 + tok.commentOffsetA = L.offsetBase + pos if L.buf[pos+2] == '[': skipMultiLineComment(L, tok, pos+3, true) diff --git a/nimpretty/tests/expected/simple.nim b/nimpretty/tests/expected/simple.nim index 5126658ea..d13558621 100644 --- a/nimpretty/tests/expected/simple.nim +++ b/nimpretty/tests/expected/simple.nim @@ -11,3 +11,8 @@ proc a() = # comment 2 discard + +# bug #15596 +discard ## comment 3 + +discard # comment 4 diff --git a/nimpretty/tests/simple.nim b/nimpretty/tests/simple.nim index 5126658ea..435bd6bd2 100644 --- a/nimpretty/tests/simple.nim +++ b/nimpretty/tests/simple.nim @@ -11,3 +11,8 @@ proc a() = # comment 2 discard + +# bug #15596 +discard## comment 3 + +discard # comment 4 From 4ab1bd7e3301e22f0a5a9c62f9c90636932fde80 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 19 Oct 2020 20:06:06 -0700 Subject: [PATCH 0796/3768] workaround https://github.com/citycide/glob/issues/49 glob broken (#15647) --- testament/important_packages.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index e8d136151..92d720e60 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -42,7 +42,7 @@ pkg1 "fragments", false, "nim c -r fragments/dsl.nim" pkg1 "gara" pkg1 "ggplotnim", true, "nim c -d:noCairo -r tests/tests.nim" # pkg1 "gittyup", true, "nimble test", "https://github.com/disruptek/gittyup" -pkg1 "glob" +# pkg1 "glob" # pending https://github.com/citycide/glob/issues/49 pkg1 "gnuplot", false, "nim c gnuplot.nim" pkg1 "hts", false, "nim c -o:htss src/hts.nim" # pkg1 "httpauth", true From 0a686659d23b67aaa2d63646865a120c203bfe21 Mon Sep 17 00:00:00 2001 From: narimiran Date: Tue, 20 Oct 2020 10:47:43 +0200 Subject: [PATCH 0797/3768] it is not "eg", it is "e.g." --- doc/contributing.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/contributing.rst b/doc/contributing.rst index cc71a62f3..15017cd84 100644 --- a/doc/contributing.rst +++ b/doc/contributing.rst @@ -43,7 +43,7 @@ Stdlib ------ Each stdlib module (anything under ``lib/``, e.g. ``lib/pure/os.nim``) should -preferably have a corresponding separate test file, eg `tests/stdlib/tos.nim`. +preferably have a corresponding separate test file, e.g. `tests/stdlib/tos.nim`. The old convention was to add a ``when isMainModule:`` block in the source file, which only gets executed when the tester is building the file. @@ -71,7 +71,7 @@ Sample test: Always refer to a github issue using the following exact syntax: `bug #1234` as shown above, so that it's consistent and easier to search or for tooling. Some browser -extensions (eg https://github.com/sindresorhus/refined-github) will even turn those +extensions (e.g. https://github.com/sindresorhus/refined-github) will even turn those in clickable links when it works. Rationale for using a separate test file instead of `when isMainModule:` block: @@ -462,7 +462,7 @@ Debugging CI failures, flaky tests, etc follow these instructions to only restart the jobs that failed: * Azure: if on your own fork, it's possible from inside azure console - (eg `dev.azure.com/username/username/_build/results?buildId=1430&view=results`) via `rerun failed jobs` on top. + (e.g. `dev.azure.com/username/username/_build/results?buildId=1430&view=results`) via `rerun failed jobs` on top. If either on you own fork or in Nim repo, it's possible from inside github UI under checks tab, see https://github.com/timotheecour/Nim/issues/211#issuecomment-629751569 * github actions: under "Checks" tab, click "Re-run jobs" in the right. @@ -580,10 +580,10 @@ Conventions 1. New stdlib modules should go under `Nim/lib/std/`. The rationale is to require users to import via `import std/foo` instead of `import foo`, which would cause potential conflicts with nimble packages. Note that this still applies for new modules -in existing logical directories, eg: +in existing logical directories, e.g.: use `lib/std/collections/foo.nim`, not `lib/pure/collections/foo.nim`. -2. New module names should prefer plural form whenever possible, eg: +2. New module names should prefer plural form whenever possible, e.g.: `std/sums.nim` instead of `std/sum.nim`. In particular, this reduces chances of conflicts between module name and the symbols it defines. Furthermore, is should use `snake_case` and not use capital letters, which cause issues when going from an OS without case From 5e9dd81baa5781c15f95de45528cc072804ce969 Mon Sep 17 00:00:00 2001 From: narimiran Date: Tue, 20 Oct 2020 10:51:50 +0200 Subject: [PATCH 0798/3768] more "eg" fixes --- doc/advopt.txt | 2 +- doc/docstyle.rst | 2 +- doc/intern.rst | 2 +- doc/manual.rst | 2 +- doc/manual_experimental.rst | 2 +- doc/nep1.rst | 2 +- doc/nimc.rst | 2 +- doc/subexes.txt | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/advopt.txt b/doc/advopt.txt index 60f5a1e10..85899031f 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -81,7 +81,7 @@ Advanced options: if these are nonexistent, will use project path -b, --backend:c|cpp|js|objc sets backend to use with commands like `nim doc` or `nim r` --docCmd:cmd if `cmd == skip`, skips runnableExamples - else, runs runnableExamples with given options, eg: + else, runs runnableExamples with given options, e.g.: `--docCmd:"-d:foo --threads:on"` --docSeeSrcUrl:url activate 'see source' for doc and doc2 commands (see doc.item.seesrc in config/nimdoc.cfg) diff --git a/doc/docstyle.rst b/doc/docstyle.rst index 70ded0689..9a983097b 100644 --- a/doc/docstyle.rst +++ b/doc/docstyle.rst @@ -19,7 +19,7 @@ General Guidelines .. code-block:: nim proc someproc*(s: string, foo: int) = - ## Use single backticks for inline code, eg: `s` or `someExpr(true)`. + ## Use single backticks for inline code, e.g.: `s` or `someExpr(true)`. ## Use a backlash to follow with alphanumeric char: `int8`\s are great. diff --git a/doc/intern.rst b/doc/intern.rst index be183fd8e..89d477d09 100644 --- a/doc/intern.rst +++ b/doc/intern.rst @@ -125,7 +125,7 @@ you can rebuild the compiler as follows: A faster approach if you don't need to run the full bootstrapping implied by `koch boot`, is the following: * `pathto/nim c --lib:lib -d:release -o:bin/nim_temp compiler/nim.nim` -Where `pathto/nim` is any nim binary sufficiently recent (eg `bin/nim_cources` +Where `pathto/nim` is any nim binary sufficiently recent (e.g. `bin/nim_cources` built during bootstrap or `$HOME/.nimble/bin/nim` installed by `choosenim 1.2.0`) You can pass any additional options such as `-d:leanCompiler` if you don't need diff --git a/doc/manual.rst b/doc/manual.rst index 7258e7775..ddee5cabe 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -6707,7 +6707,7 @@ Example: embedsC() ``nimbase.h`` defines ``NIM_EXTERNC`` C macro that can be used for -``extern "C"`` code to work with both ``nim c`` and ``nim cpp``, eg: +``extern "C"`` code to work with both ``nim c`` and ``nim cpp``, e.g.: .. code-block:: Nim proc foobar() {.importc:"$1".} diff --git a/doc/manual_experimental.rst b/doc/manual_experimental.rst index 655adae02..caf3dfb48 100644 --- a/doc/manual_experimental.rst +++ b/doc/manual_experimental.rst @@ -1703,7 +1703,7 @@ noRewrite pragma Term rewriting macros and templates are currently greedy and they will rewrite as long as there is a match. There was no way to ensure some rewrite happens only once, -eg. when rewriting term to same term plus extra content. +e.g. when rewriting term to same term plus extra content. ``noRewrite`` pragma can actually prevent further rewriting on marked code, e.g. with given example ``echo("ab")`` will be rewritten just once: diff --git a/doc/nep1.rst b/doc/nep1.rst index 75343c85b..1ab45d680 100644 --- a/doc/nep1.rst +++ b/doc/nep1.rst @@ -149,7 +149,7 @@ changed in the future. an in-place version should get an ``-In`` suffix (``replaceIn`` for this example). -- Use `subjectVerb`, not `verbSubject`, eg: `fileExists`, not `existsFile`. +- Use `subjectVerb`, not `verbSubject`, e.g.: `fileExists`, not `existsFile`. The stdlib API is designed to be **easy to use** and consistent. Ease of use is measured by the number of calls to achieve a concrete high level action. The diff --git a/doc/nimc.rst b/doc/nimc.rst index 379d8b559..9a698312b 100644 --- a/doc/nimc.rst +++ b/doc/nimc.rst @@ -423,7 +423,7 @@ complete list. Define Effect ====================== ========================================================= ``release`` Turns on the optimizer. - More aggressive optimizations are possible, eg: + More aggressive optimizations are possible, e.g.: ``--passC:-ffast-math`` (but see issue #10305) ``danger`` Turns off all runtime checks and turns on the optimizer. ``useFork`` Makes ``osproc`` use ``fork`` instead of ``posix_spawn``. diff --git a/doc/subexes.txt b/doc/subexes.txt index 54304f033..62ddd1ec8 100644 --- a/doc/subexes.txt +++ b/doc/subexes.txt @@ -14,7 +14,7 @@ Notation meaning ===================== ===================================================== ``$#`` use first or next argument ``$name`` use named argument, you can wrap the named argument - in curly braces (eg. ``${name}``) to separate it from + in curly braces (e.g. ``${name}``) to separate it from the next characters. ``$$`` produces a single ``$`` ``$1`` use first argument From 725c7caa027ff9e04ad514bade72163522c8d28a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20H=C3=BCbelbauer?= Date: Tue, 20 Oct 2020 13:36:19 +0200 Subject: [PATCH 0799/3768] Remove bit about opening files not raising (#15654) Resolved #473 I am removing this bit because this advice to not throw on recoverable-from cases like a file failing to open is actually not respected by Nim standard library. `readFile` and `readLines` both throw and exceptions must be used as a method of control flow for the recovery from that. There are alternatives, like opening a file handle instead of using these helpers, but that's less convenient than these helper methods for cases where you really want to just slurp up a file to memory and use a fallback value if it doesn't exist or is for whatever reason not readable. --- doc/tut2.rst | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/doc/tut2.rst b/doc/tut2.rst index ac8e82b0a..577b65622 100644 --- a/doc/tut2.rst +++ b/doc/tut2.rst @@ -345,10 +345,8 @@ The compiler will prevent you from raising an exception created on the stack. All raised exceptions should at least specify the reason for being raised in the ``msg`` field. -A convention is that exceptions should be raised in *exceptional* cases: -For example, if a file cannot be opened, this should not raise an -exception since this is quite common (the file may not exist). - +A convention is that exceptions should be raised in *exceptional* cases, +they should not be used as an alternative method of control flow. Raise statement --------------- From b418c54ec46ac79718d0bc68abb4e6531930f398 Mon Sep 17 00:00:00 2001 From: narimiran Date: Tue, 20 Oct 2020 13:57:50 +0200 Subject: [PATCH 0800/3768] even more "eg" fixes [ci skip] --- compiler/ast.nim | 2 +- compiler/cmdlinehelper.nim | 2 +- compiler/docgen.nim | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index f796e64c2..d81ccf33b 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -875,7 +875,7 @@ type annex*: PLib # additional fields (seldom used, so we use a # reference to another object to save space) when hasFFI: - cname*: string # resolved C declaration name in importc decl, eg: + cname*: string # resolved C declaration name in importc decl, e.g.: # proc fun() {.importc: "$1aux".} => cname = funaux constraint*: PNode # additional constraints like 'lit|result'; also # misused for the codegenDecl pragma in the hope diff --git a/compiler/cmdlinehelper.nim b/compiler/cmdlinehelper.nim index a6b56b97e..5fb8ab471 100644 --- a/compiler/cmdlinehelper.nim +++ b/compiler/cmdlinehelper.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -## Helpers for binaries that use compiler passes, eg: nim, nimsuggest, nimfix +## Helpers for binaries that use compiler passes, e.g.: nim, nimsuggest, nimfix import options, idents, nimconf, extccomp, commands, msgs, diff --git a/compiler/docgen.nim b/compiler/docgen.nim index fb15f3826..e9cd08402 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -30,7 +30,7 @@ type ExampleGroup = ref object ## a group of runnableExamples with same rdoccmd rdoccmd: string ## from 1st arg in `runnableExamples(rdoccmd): body` - docCmd: string ## from user config, eg --doccmd:-d:foo + docCmd: string ## from user config, e.g. --doccmd:-d:foo code: string ## contains imports; each import contains `body` index: int ## group index TDocumentor = object of rstgen.RstGenerator @@ -457,7 +457,7 @@ proc writeExample(d: PDoc; ex: PNode, rdoccmd: string) = proc runAllExamples(d: PDoc) = # This used to be: `let backend = if isDefined(d.conf, "js"): "js"` (etc), however - # using `-d:js` (etc) cannot work properly, eg would fail with `importjs` + # using `-d:js` (etc) cannot work properly, e.g. would fail with `importjs` # since semantics are affected by `config.backend`, not by isDefined(d.conf, "js") let outputDir = d.exampleOutputDir for _, group in d.exampleGroups: @@ -1259,7 +1259,7 @@ proc generateIndex*(d: PDoc) = writeIndexFile(d[], dest.string) proc updateOutfile(d: PDoc, outfile: AbsoluteFile) = - if d.module == nil or sfMainModule in d.module.flags: # nil for eg for commandRst2Html + if d.module == nil or sfMainModule in d.module.flags: # nil for e.g. for commandRst2Html if d.conf.outFile.isEmpty: d.conf.outFile = outfile.relativeTo(d.conf.outDir) if isAbsolute(d.conf.outFile.string): From ed0f8a497333c75b9035c4e7837ee1158d53aa20 Mon Sep 17 00:00:00 2001 From: RecruitMain707 <37117366+RecruitMain707@users.noreply.github.com> Date: Tue, 20 Oct 2020 14:22:02 +0200 Subject: [PATCH 0801/3768] Fix compilation error for regions and memory profiling (#15641) (#15656) Co-authored-by: Recruit_main707 --- lib/system/gc_regions.nim | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/system/gc_regions.nim b/lib/system/gc_regions.nim index 9425e040a..394337192 100644 --- a/lib/system/gc_regions.nim +++ b/lib/system/gc_regions.nim @@ -8,6 +8,9 @@ # "Stack GC" for embedded devices or ultra performance requirements. +when defined(memProfiler): + proc nimProfile(requestedSize: int) {.benign.} + when defined(useMalloc): proc roundup(x, v: int): int {.inline.} = result = (x + (v-1)) and not (v-1) From 3cbe8d2c53cec1f41de116f3d3a9a7678a62da11 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 20 Oct 2020 05:26:37 -0700 Subject: [PATCH 0802/3768] $(uint|uint64) now works with nimscript (#15644) * $(uint|uint64) now works with nimscript * fixup --- lib/system/dollars.nim | 20 ++++++++++++++++++++ lib/system/mm/go.nim | 2 -- lib/system/strmantle.nim | 20 -------------------- tests/test_nimscript.nims | 3 +++ 4 files changed, 23 insertions(+), 22 deletions(-) diff --git a/lib/system/dollars.nim b/lib/system/dollars.nim index b2f0186b3..e87c237ad 100644 --- a/lib/system/dollars.nim +++ b/lib/system/dollars.nim @@ -19,6 +19,26 @@ when defined(js): ## 64bit ints. # pending https://github.com/nim-lang/RFCs/issues/187 $(cast[int](x)) +else: + proc `$`*(x: uint64): string {.noSideEffect, raises: [].} = + ## The stringify operator for an unsigned integer argument. Returns `x` + ## converted to a decimal string. + if x == 0: + result = "0" + else: + result = newString(60) + var i = 0 + var n = x + while n != 0: + let nn = n div 10'u64 + 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-1: swap(result[t], result[i-t-1]) proc `$`*(x: int64): string {.magic: "Int64ToStr", noSideEffect.} ## The stringify operator for an integer argument. Returns `x` diff --git a/lib/system/mm/go.nim b/lib/system/mm/go.nim index b6d5a1a44..9a8c47ea5 100644 --- a/lib/system/mm/go.nim +++ b/lib/system/mm/go.nim @@ -35,8 +35,6 @@ proc goMalloc(size: uint): pointer {.importc: "go_malloc", dynlib: goLib.} proc goSetFinalizer(obj: pointer, f: pointer) {.importc: "set_finalizer", codegenDecl:"$1 $2$3 __asm__ (\"main.Set_finalizer\");\n$1 $2$3", dynlib: goLib.} proc writebarrierptr(dest: PPointer, src: pointer) {.importc: "writebarrierptr", codegenDecl:"$1 $2$3 __asm__ (\"main.Atomic_store_pointer\");\n$1 $2$3", dynlib: goLib.} -proc `$`*(x: uint64): string {.noSideEffect, raises: [].} - proc GC_getStatistics(): string = var mstats = goMemStats() result = "[GC] total allocated memory: " & $(mstats.total_alloc) & "\n" & diff --git a/lib/system/strmantle.nim b/lib/system/strmantle.nim index 43a769b5f..21b790603 100644 --- a/lib/system/strmantle.nim +++ b/lib/system/strmantle.nim @@ -283,26 +283,6 @@ proc nimCharToStr(x: char): string {.compilerRtl.} = result = newString(1) result[0] = x -proc `$`*(x: uint64): string {.noSideEffect, raises: [].} = - ## The stringify operator for an unsigned integer argument. Returns `x` - ## converted to a decimal string. - if x == 0: - result = "0" - else: - result = newString(60) - var i = 0 - var n = x - while n != 0: - let nn = n div 10'u64 - 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-1: swap(result[t], result[i-t-1]) - when defined(gcDestructors): proc GC_getStatistics*(): string = result = "[GC] total memory: " diff --git a/tests/test_nimscript.nims b/tests/test_nimscript.nims index d9f477366..b94146b1e 100644 --- a/tests/test_nimscript.nims +++ b/tests/test_nimscript.nims @@ -73,6 +73,9 @@ echo "Nimscript imports are successful." block: doAssert "./foo//./bar/".normalizedPath == "foo/bar".unixToNativePath +block: + doAssert $3'u == "3" + doAssert $3'u64 == "3" block: # #14142 discard dirExists("/usr") From 877d5206162fcb491777c9d6cedf65810a3f421f Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 20 Oct 2020 15:00:16 +0200 Subject: [PATCH 0803/3768] gc.rst: updated the docs to reflect reality --- doc/gc.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/doc/gc.rst b/doc/gc.rst index ee7f9000e..84a1338c3 100644 --- a/doc/gc.rst +++ b/doc/gc.rst @@ -176,8 +176,7 @@ useful for us, so it might be useful for you. To get a heap dump, compile with ``-d:nimTypeNames`` and call ``dumpNumberOfInstances`` at a strategic place in your program. This produces a list of used types in your program and for every type the total amount of object instances for this type as well as the total -amount of bytes these instances take up. This list is currently unsorted! -You need to use external shell script hacking to sort it. +amount of bytes these instances take up. The numbers count the number of objects in all garbage collector heaps, they refer to all running threads, not only to the current thread. (The current thread From 675cf5760f35c4c178bdef1fbcfd8b95e918cdb2 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Tue, 20 Oct 2020 15:47:23 +0100 Subject: [PATCH 0804/3768] Fixes compilation for --os:any + --exception:setjmp (#15626) --- lib/system/excpt.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index 1a7473a76..c189f116d 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -554,7 +554,7 @@ proc nimFrame(s: PFrame) {.compilerRtl, inl, raises: [].} = when defined(cpp) and appType != "lib" and not gotoBasedExceptions and not defined(js) and not defined(nimscript) and - hostOS != "standalone" and not defined(noCppExceptions): + hostOS != "standalone" and hostOS != "any" and not defined(noCppExceptions): type StdException {.importcpp: "std::exception", header: "".} = object From 94a4b57280591d1b5e3fa63be17b5721776e2b30 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 20 Oct 2020 11:21:10 -0700 Subject: [PATCH 0805/3768] make patchFile more discoverable (#15659) --- doc/advopt.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/advopt.txt b/doc/advopt.txt index 85899031f..f1e6f376d 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -51,6 +51,7 @@ Advanced options: resolution --lib:PATH set the system library path --import:PATH add an automatically imported module + see also `patchFile` in nimscript which offers more flexibility. --include:PATH add an automatically included module --nimcache:PATH set the path used for generated files see also https://nim-lang.org/docs/nimc.html#compiler-usage-generated-c-code-directory From 9a059657caa5bc744dba2c026b4e03b6231c6736 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 20 Oct 2020 21:07:24 +0200 Subject: [PATCH 0806/3768] fixes bootstrapping for any machine that has a Nim already installed [backport:1.4] (#15660) --- compiler/docgen.nim | 22 +++++++++++----------- compiler/semcall.nim | 7 +++++++ lib/packages/docutils/rstgen.nim | 13 ++++++------- 3 files changed, 24 insertions(+), 18 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index e9cd08402..6c77a44ae 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -168,7 +168,7 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef, result.module = module result.conf = conf result.cache = cache - result.outDir = conf.outDir + result.outDir = conf.outDir.string initRstGenerator(result[], (if conf.cmd != cmdRst2tex: outHtml else: outLatex), conf.configVars, filename.string, {roSupportRawDirective, roSupportMarkdown}, docgenFindFile, compilerMsgHandler) @@ -230,8 +230,8 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef, if gotten != status: rawMessage(conf, errGenerated, "snippet failed: cmd: '$1' status: $2 expected: $3 output: $4" % [cmd, $gotten, $status, output]) result.emitted = initIntSet() - result.destFile = getOutFile2(conf, presentationPath(conf, filename), outExt, false) - result.thisDir = result.destFile.splitFile.dir + result.destFile = getOutFile2(conf, presentationPath(conf, filename), outExt, false).string + result.thisDir = result.destFile.AbsoluteFile.splitFile.dir template dispA(conf: ConfigRef; dest: var Rope, xml, tex: string, args: openArray[Rope]) = if conf.cmd != cmdRst2tex: dest.addf(xml, args) @@ -849,7 +849,7 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags) = symbolOrIdRope, plainSymbolEncRope, symbolOrIdEncRope, seeSrcRope, deprecationMsgRope])) - let external = d.destFile.relativeTo(d.conf.outDir, '/').changeFileExt(HtmlExt).string + let external = d.destFile.AbsoluteFile.relativeTo(d.conf.outDir, '/').changeFileExt(HtmlExt).string var attype: Rope if k in routineKinds and nameNode.kind == nkSym: @@ -1237,14 +1237,14 @@ proc genOutFile(d: PDoc, groupedToc = false): Rope = content = ropeFormatNamedVars(d.conf, getConfigVar(d.conf, bodyname), ["title", "tableofcontents", "moduledesc", "date", "time", "content", "deprecationMsg", "theindexhref", "body_toc_groupsection"], [title.rope, toc, d.modDesc, rope(getDateStr()), - rope(getClockStr()), code, d.modDeprecationMsg, relLink(d.conf.outDir, d.destFile, theindexFname.RelativeFile), groupsection.rope]) + rope(getClockStr()), code, d.modDeprecationMsg, relLink(d.conf.outDir, d.destFile.AbsoluteFile, theindexFname.RelativeFile), groupsection.rope]) if optCompileOnly notin d.conf.globalOptions: # XXX what is this hack doing here? 'optCompileOnly' means raw output!? code = ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.file"), [ "nimdoccss", "dochackjs", "title", "tableofcontents", "moduledesc", "date", "time", "content", "author", "version", "analytics", "deprecationMsg"], - [relLink(d.conf.outDir, d.destFile, nimdocOutCss.RelativeFile), - relLink(d.conf.outDir, d.destFile, docHackJsFname.RelativeFile), + [relLink(d.conf.outDir, d.destFile.AbsoluteFile, nimdocOutCss.RelativeFile), + relLink(d.conf.outDir, d.destFile.AbsoluteFile, docHackJsFname.RelativeFile), title.rope, toc, d.modDesc, rope(getDateStr()), rope(getClockStr()), content, d.meta[metaAuthor].rope, d.meta[metaVersion].rope, d.analytics.rope, d.modDeprecationMsg]) else: @@ -1271,7 +1271,7 @@ proc writeOutput*(d: PDoc, useWarning = false, groupedToc = false) = if optStdout in d.conf.globalOptions: writeRope(stdout, content) else: - template outfile: untyped = d.destFile + template outfile: untyped = d.destFile.AbsoluteFile #let outfile = getOutFile2(d.conf, shortenDir(d.conf, filename), outExt) let dir = outfile.splitFile.dir createDir(dir) @@ -1300,13 +1300,13 @@ proc writeOutputJson*(d: PDoc, useWarning = false) = write(stdout, $content) else: var f: File - if open(f, d.destFile.string, fmWrite): + if open(f, d.destFile, fmWrite): write(f, $content) close(f) - updateOutfile(d, d.destFile) + updateOutfile(d, d.destFile.AbsoluteFile) else: localError(d.conf, newLineInfo(d.conf, AbsoluteFile d.filename, -1, -1), - warnUser, "unable to open file \"" & d.destFile.string & + warnUser, "unable to open file \"" & d.destFile & "\" for writing") proc handleDocOutputOptions*(conf: ConfigRef) = diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 3e4ed096b..dedae225b 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -230,6 +230,9 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): doAssert err.firstMismatch.formal != nil candidates.add("\n required type for " & nameParam & ": ") candidates.add typeToString(wanted) + when false: + if wanted.sym != nil: + candidates.add "(" & (c.config $ wanted.sym.info) & ")" candidates.add "\n but expression '" if err.firstMismatch.kind == kVarNeeded: candidates.add renderNotLValue(nArg) @@ -239,6 +242,10 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): candidates.add "' is of type: " var got = nArg.typ candidates.add typeToString(got) + when false: + if got.sym != nil: + candidates.add "(" & (c.config $ got.sym.info) & ")" + doAssert wanted != nil if got != nil: effectProblem(wanted, got, candidates, c) of kUnknown: discard "do not break 'nim check'" diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 9f707f4e8..c70998edb 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -27,8 +27,7 @@ import strutils, os, hashes, strtabs, rstast, rst, highlite, tables, sequtils, algorithm, parseutils -import "$lib/../compiler/nimpaths" -import "$lib/../compiler/pathutils" + import ../../std/private/since const @@ -58,8 +57,8 @@ type options*: RstParseOptions findFile*: FindFileHandler msgHandler*: MsgHandler - outDir*: AbsoluteDir ## output directory, initialized by docgen.nim - destFile*: AbsoluteFile ## output (HTML) file, initialized by docgen.nim + outDir*: string ## output directory, initialized by docgen.nim + destFile*: string ## output (HTML) file, initialized by docgen.nim filename*: string ## source Nim or Rst file meta*: array[MetaEnum, string] currentSection: string ## \ @@ -84,7 +83,7 @@ type status: int proc prettyLink*(file: string): string = - changeFileExt(file, "").replace(dotdotMangle, "..") + changeFileExt(file, "").replace("_._", "..") proc init(p: var CodeBlockParams) = ## Default initialisation of CodeBlockParams to sane values. @@ -759,13 +758,13 @@ proc renderHeadline(d: PDoc, n: PRstNode, result: var string) = # Generate index entry using spaces to indicate TOC level for the output HTML. assert n.level >= 0 let - htmlFileRelPath = if d.outDir.isEmpty(): + htmlFileRelPath = if d.outDir.len == 0: # /foo/bar/zoo.nim -> zoo.html changeFileExt(extractFilename(d.filename), HtmlExt) else: # d is initialized in docgen.nim # outDir = /foo -\ # destFile = /foo/bar/zoo.html -|-> bar/zoo.html - d.destFile.relativeTo(d.outDir, '/').string + d.destFile.relativePath(d.outDir, '/') setIndexTerm(d, htmlFileRelPath, refname, tmp.stripTocHtml, spaces(max(0, n.level)) & tmp) From 4b0b3818c3deaa56aa8763bb015d835479b69f67 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 21 Oct 2020 02:49:59 -0700 Subject: [PATCH 0807/3768] fix cgen header copyright (#15649) * remove copyright Andreas Rumpf from cgen files --- compiler/cgen.nim | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 9a50b718c..afb36e787 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -1283,12 +1283,10 @@ proc addNimDefines(result: var Rope; conf: ConfigRef) {.inline.} = proc getCopyright(conf: ConfigRef; cfile: Cfile): Rope = if optCompileOnly in conf.globalOptions: result = ("/* Generated by Nim Compiler v$1 */$N" & - "/* (c) " & copyrightYear & " Andreas Rumpf */$N" & "/* The generated code is subject to the original license. */$N") % [rope(VersionAsString)] else: result = ("/* Generated by Nim Compiler v$1 */$N" & - "/* (c) " & copyrightYear & " Andreas Rumpf */$N" & "/* The generated code is subject to the original license. */$N" & "/* Compiled for: $2, $3, $4 */$N" & "/* Command for C compiler:$n $5 */$N") % @@ -1860,8 +1858,8 @@ proc myOpen(graph: ModuleGraph; module: PSym): PPassContext {.nosinks.} = incl g.generatedHeader.flags, isHeaderFile proc writeHeader(m: BModule) = + # could factor with 2 other instances var result = ("/* Generated by Nim Compiler v$1 */$N" & - "/* (c) 2017 Andreas Rumpf */$N" & "/* The generated code is subject to the original license. */$N") % [rope(VersionAsString)] From 05752cd5d027a746053897d124f9dae513a0e664 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 21 Oct 2020 06:37:29 -0700 Subject: [PATCH 0808/3768] add --declaredlocs (#15666) --- changelog.md | 2 +- compiler/commands.nim | 2 ++ compiler/lineinfos.nim | 6 ++++-- compiler/options.nim | 1 + compiler/semcall.nim | 11 +++++------ compiler/types.nim | 9 +++++++-- doc/advopt.txt | 1 + 7 files changed, 21 insertions(+), 11 deletions(-) diff --git a/changelog.md b/changelog.md index f585f4e15..dcaffe23c 100644 --- a/changelog.md +++ b/changelog.md @@ -11,7 +11,7 @@ ## Compiler changes - +add `--declaredlocs` to show symbol declaration location in messages ## Tool changes diff --git a/compiler/commands.nim b/compiler/commands.nim index b9199c1b0..325b362a9 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -794,6 +794,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; processOnOffSwitchG(conf, {optStdout}, arg, pass, info) of "listfullpaths": processOnOffSwitchG(conf, {optListFullPaths}, arg, pass, info) + of "declaredlocs": + processOnOffSwitchG(conf, {optDeclaredLocs}, arg, pass, info) of "dynliboverride": dynlibOverride(conf, switch, arg, pass, info) of "dynliboverrideall": diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index a0b1d1965..4a7c09276 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -69,6 +69,7 @@ type hintUser, hintUserRaw, hintExtendedContext, hintMsgOrigin, # since 1.3.5 + hintDeclaredLoc, # since 1.5.1 const MsgKindToStr*: array[TMsgKind, string] = [ @@ -159,6 +160,7 @@ const hintUserRaw: "$1", hintExtendedContext: "$1", hintMsgOrigin: "$1", + hintDeclaredLoc: "$1", ] const @@ -186,7 +188,7 @@ const "ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf", "Path", "CondTrue", "CondFalse", "Name", "Pattern", "Exec", "Link", "Dependency", "Source", "Performance", "StackTrace", "GCStats", "GlobalVar", "ExpandMacro", - "User", "UserRaw", "ExtendedContext", "MsgOrigin", + "User", "UserRaw", "ExtendedContext", "MsgOrigin", "DeclaredLoc" ] const @@ -215,7 +217,7 @@ type proc computeNotesVerbosity(): array[0..3, TNoteKinds] = result[3] = {low(TNoteKind)..high(TNoteKind)} - {warnObservableStores} - result[2] = result[3] - {hintStackTrace, warnUninit, hintExtendedContext} + result[2] = result[3] - {hintStackTrace, warnUninit, hintExtendedContext, hintDeclaredLoc} result[1] = result[2] - {warnProveField, warnProveIndex, warnGcUnsafe, hintPath, hintDependency, hintCodeBegin, hintCodeEnd, hintSource, hintGlobalVar, hintGCStats, hintMsgOrigin} diff --git a/compiler/options.nim b/compiler/options.nim index 3be7c5099..872ab9582 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -81,6 +81,7 @@ type # please make sure we have under 32 options optDocInternal # generate documentation for non-exported symbols optMixedMode # true if some module triggered C++ codegen optListFullPaths # use full paths in toMsgFilename + optDeclaredLocs # show declaration locations in messages optNoNimblePath optHotCodeReloading optDynlibOverrideAll diff --git a/compiler/semcall.nim b/compiler/semcall.nim index dedae225b..50df1085f 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -208,6 +208,7 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): {renderNoBody, renderNoComments, renderNoPragmas})) else: candidates.add(getProcHeader(c.config, err.sym, prefer)) + candidates.addDeclaredLocMaybe(c.config, err.sym) candidates.add("\n") let nArg = if err.firstMismatch.arg < n.len: n[err.firstMismatch.arg] else: nil let nameParam = if err.firstMismatch.formal != nil: err.firstMismatch.formal.name.s else: "" @@ -230,9 +231,8 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): doAssert err.firstMismatch.formal != nil candidates.add("\n required type for " & nameParam & ": ") candidates.add typeToString(wanted) - when false: - if wanted.sym != nil: - candidates.add "(" & (c.config $ wanted.sym.info) & ")" + if wanted.sym != nil: + candidates.addDeclaredLocMaybe(c.config, wanted.sym) candidates.add "\n but expression '" if err.firstMismatch.kind == kVarNeeded: candidates.add renderNotLValue(nArg) @@ -242,9 +242,8 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): candidates.add "' is of type: " var got = nArg.typ candidates.add typeToString(got) - when false: - if got.sym != nil: - candidates.add "(" & (c.config $ got.sym.info) & ")" + if got.sym != nil: + candidates.addDeclaredLocMaybe(c.config, got.sym) doAssert wanted != nil if got != nil: effectProblem(wanted, got, candidates, c) diff --git a/compiler/types.nim b/compiler/types.nim index b20bf9c06..75e12cac4 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -123,8 +123,13 @@ proc isIntLit*(t: PType): bool {.inline.} = proc isFloatLit*(t: PType): bool {.inline.} = result = t.kind == tyFloat and t.n != nil and t.n.kind == nkFloatLit -proc addDeclaredLoc(result: var string, conf: ConfigRef; sym: PSym) = - result.add " [declared in " & conf$sym.info & "]" +proc addDeclaredLoc*(result: var string, conf: ConfigRef; sym: PSym) = + # result.add " [declared in " & conf$sym.info & "]" + result.add " [declared in " & toFileLineCol(conf, sym.info) & "]" + +proc addDeclaredLocMaybe*(result: var string, conf: ConfigRef; sym: PSym) = + if optDeclaredLocs in conf.globalOptions: + addDeclaredLoc(result, conf, sym) proc addTypeHeader*(result: var string, conf: ConfigRef; typ: PType; prefer: TPreferedDesc = preferMixed; getDeclarationPath = true) = result.add typeToString(typ, prefer) diff --git a/doc/advopt.txt b/doc/advopt.txt index f1e6f376d..e5cf7d894 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -35,6 +35,7 @@ Advanced options: --stdout:on|off output to stdout --colors:on|off turn compiler messages coloring on|off --listFullPaths:on|off list full paths in messages + --declaredlocs:on|off show declaration locations in messages -w:on|off|list, --warnings:on|off|list turn all warnings on|off or list all available --warning[X]:on|off turn specific warning X on|off From ec6a1125a0d20909db63bd8305c66445bf7bf78e Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 21 Oct 2020 10:33:19 -0700 Subject: [PATCH 0809/3768] --declaredlocs => --declaredLocs (#15672) --- doc/advopt.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/advopt.txt b/doc/advopt.txt index e5cf7d894..1fc0c946e 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -35,7 +35,7 @@ Advanced options: --stdout:on|off output to stdout --colors:on|off turn compiler messages coloring on|off --listFullPaths:on|off list full paths in messages - --declaredlocs:on|off show declaration locations in messages + --declaredLocs:on|off show declaration locations in messages -w:on|off|list, --warnings:on|off|list turn all warnings on|off or list all available --warning[X]:on|off turn specific warning X on|off From e09c259bf3dc8beb2c4d5abf9334c787d18f99f5 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 21 Oct 2020 15:53:43 -0700 Subject: [PATCH 0810/3768] reuse existing toHumanStr (#15674) --- compiler/parampatterns.nim | 2 +- compiler/semtypes.nim | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim index c2be4b134..eb99004ab 100644 --- a/compiler/parampatterns.nim +++ b/compiler/parampatterns.nim @@ -100,7 +100,7 @@ proc compileConstraints(p: PNode, result: var TPatternCode; conf: ConfigRef) = # check all symkinds: internalAssert conf, int(high(TSymKind)) < 255 for i in TSymKind: - if cmpIgnoreStyle(($i).substr(2), spec) == 0: + if cmpIgnoreStyle(i.toHumanStr, spec) == 0: result.add(ppSymKind) result.add(chr(i.ord)) return diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 8ef393839..f2bb82a5c 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1913,7 +1913,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = localError(c.config, n.info, "type expected, but symbol '$1' has no type." % [s.name.s]) else: localError(c.config, n.info, "type expected, but got symbol '$1' of kind '$2'" % - [s.name.s, substr($s.kind, 2)]) + [s.name.s, s.kind.toHumanStr]) result = newOrPrevType(tyError, prev, c) of nkObjectTy: result = semObjectNode(c, n, prev, isInheritable=false) of nkTupleTy: result = semTuple(c, n, prev) From b2f51e29504160da7bb6ee98af464c21a9a96efd Mon Sep 17 00:00:00 2001 From: Gampol T Date: Thu, 22 Oct 2020 10:40:45 +0700 Subject: [PATCH 0811/3768] Fix #13609 (#15567) * add documentation that discardable pragma cannot use on template * Update doc/manual.rst Co-authored-by: Euan * Update doc/manual.rst Co-authored-by: Euan Co-authored-by: Euan --- doc/manual.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/doc/manual.rst b/doc/manual.rst index ddee5cabe..ca53999ac 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -2592,6 +2592,17 @@ been declared with the `discardable`:idx: pragma: p(3, 4) # now valid +however the discardable pragma does not work on templates as templates substitute the AST in place. For example: + +.. code-block:: nim + {.push discardable .} + template example(): string = "https://nim-lang.org" + {.pop.} + + example() + +This template will resolve into "https://nim-lang.org" which is a string literal and since {.discardable.} doesn't apply to literals, the compiler will error. + An empty ``discard`` statement is often used as a null statement: .. code-block:: nim From 09795241375839c2b9fbd1ba8b61dc9a920837d7 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 22 Oct 2020 01:44:28 -0700 Subject: [PATCH 0812/3768] add Source+Edit links on top of every docgend file (#15642) --- changelog.md | 1 + compiler/commands.nim | 2 + compiler/docgen.nim | 46 ++++++++++--------- config/nimdoc.cfg | 3 +- .../expected/index.html | 1 + .../expected/subdir/subdir_b/utils.html | 1 + nimdoc/testproject/expected/testproject.html | 1 + 7 files changed, 32 insertions(+), 23 deletions(-) diff --git a/changelog.md b/changelog.md index dcaffe23c..66c9131cd 100644 --- a/changelog.md +++ b/changelog.md @@ -12,6 +12,7 @@ ## Compiler changes add `--declaredlocs` to show symbol declaration location in messages +* Source+Edit links now appear on top of every docgen'd page when `nim doc --git.url:url ...` is given ## Tool changes diff --git a/compiler/commands.nim b/compiler/commands.nim index 325b362a9..93a202639 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -396,6 +396,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; var key, val: string case switch.normalize + of "input", "i": # PRTEMP + expectArg(conf, switch, arg, pass, info) of "path", "p": expectArg(conf, switch, arg, pass, info) for path in nimbleSubs(conf, arg): diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 6c77a44ae..3945a30f8 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -777,6 +777,26 @@ type DocFlags = enum kDefault kForceExport +proc genSeeSrcRope(d: PDoc, path: string, line: int): Rope = + let docItemSeeSrc = getConfigVar(d.conf, "doc.item.seesrc") + if docItemSeeSrc.len > 0: + let path = relativeTo(AbsoluteFile path, AbsoluteDir getCurrentDir(), '/') + when false: + let cwd = canonicalizePath(d.conf, getCurrentDir()) + var path = path + if path.startsWith(cwd): + path = path[cwd.len+1..^1].replace('\\', '/') + let gitUrl = getConfigVar(d.conf, "git.url") + if gitUrl.len > 0: + let defaultBranch = + if NimPatch mod 2 == 1: "devel" + else: "version-$1-$2" % [$NimMajor, $NimMinor] + let commit = getConfigVar(d.conf, "git.commit", defaultBranch) + let develBranch = getConfigVar(d.conf, "git.devel", "devel") + dispA(d.conf, result, "$1", "", [ropeFormatNamedVars(d.conf, docItemSeeSrc, + ["path", "line", "url", "commit", "devel"], [rope path.string, + rope($line), rope gitUrl, rope commit, rope develBranch])]) + proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags) = if (docFlags != kForceExport) and not isVisible(d, nameNode): return let @@ -822,26 +842,7 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags) = nodeToHighlightedHtml(d, n, result, {renderNoBody, renderNoComments, renderDocComments, renderSyms}, symbolOrIdEncRope) - var seeSrcRope: Rope = nil - let docItemSeeSrc = getConfigVar(d.conf, "doc.item.seesrc") - if docItemSeeSrc.len > 0: - let path = relativeTo(AbsoluteFile toFullPath(d.conf, n.info), AbsoluteDir getCurrentDir(), '/') - when false: - let cwd = canonicalizePath(d.conf, getCurrentDir()) - var path = toFullPath(d.conf, n.info) - if path.startsWith(cwd): - path = path[cwd.len+1..^1].replace('\\', '/') - let gitUrl = getConfigVar(d.conf, "git.url") - if gitUrl.len > 0: - let defaultBranch = - if NimPatch mod 2 == 1: "devel" - else: "version-$1-$2" % [$NimMajor, $NimMinor] - let commit = getConfigVar(d.conf, "git.commit", defaultBranch) - let develBranch = getConfigVar(d.conf, "git.devel", "devel") - dispA(d.conf, seeSrcRope, "$1", "", [ropeFormatNamedVars(d.conf, docItemSeeSrc, - ["path", "line", "url", "commit", "devel"], [rope path.string, - rope($n.info.line), rope gitUrl, rope commit, rope develBranch])]) - + let seeSrcRope = genSeeSrcRope(d, toFullPath(d.conf, n.info), n.info.line.int) d.section[k].add(ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.item"), ["name", "header", "desc", "itemID", "header_plain", "itemSym", "itemSymOrID", "itemSymEnc", "itemSymOrIDEnc", "seeSrc", "deprecationMsg"], @@ -1234,10 +1235,11 @@ proc genOutFile(d: PDoc, groupedToc = false): Rope = "doc.body_toc_group" elif d.hasToc: "doc.body_toc" else: "doc.body_no_toc" + let seeSrcRope = genSeeSrcRope(d, d.filename, 1) content = ropeFormatNamedVars(d.conf, getConfigVar(d.conf, bodyname), ["title", - "tableofcontents", "moduledesc", "date", "time", "content", "deprecationMsg", "theindexhref", "body_toc_groupsection"], + "tableofcontents", "moduledesc", "date", "time", "content", "deprecationMsg", "theindexhref", "body_toc_groupsection", "seeSrc"], [title.rope, toc, d.modDesc, rope(getDateStr()), - rope(getClockStr()), code, d.modDeprecationMsg, relLink(d.conf.outDir, d.destFile.AbsoluteFile, theindexFname.RelativeFile), groupsection.rope]) + rope(getClockStr()), code, d.modDeprecationMsg, relLink(d.conf.outDir, d.destFile.AbsoluteFile, theindexFname.RelativeFile), groupsection.rope, seeSrcRope]) if optCompileOnly notin d.conf.globalOptions: # XXX what is this hack doing here? 'optCompileOnly' means raw output!? code = ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.file"), [ diff --git a/config/nimdoc.cfg b/config/nimdoc.cfg index 13beb5af1..db86af021 100644 --- a/config/nimdoc.cfg +++ b/config/nimdoc.cfg @@ -80,7 +80,7 @@ doc.item.tocTable = """ doc.item.seesrc = """  Source -Edit +  Edit """ doc.deprecationmsg = """ @@ -187,6 +187,7 @@ doc.body_toc_group = """ $tableofcontents + $seeSrc
    $deprecationMsg diff --git a/nimdoc/test_out_index_dot_html/expected/index.html b/nimdoc/test_out_index_dot_html/expected/index.html index d6e318f7d..58ab4fc6d 100644 --- a/nimdoc/test_out_index_dot_html/expected/index.html +++ b/nimdoc/test_out_index_dot_html/expected/index.html @@ -106,6 +106,7 @@ function main() {
    +
    diff --git a/nimdoc/testproject/expected/subdir/subdir_b/utils.html b/nimdoc/testproject/expected/subdir/subdir_b/utils.html index ebbedc648..5acf196b0 100644 --- a/nimdoc/testproject/expected/subdir/subdir_b/utils.html +++ b/nimdoc/testproject/expected/subdir/subdir_b/utils.html @@ -132,6 +132,7 @@ function main() {
    +
    diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html index a5522a2e2..922319914 100644 --- a/nimdoc/testproject/expected/testproject.html +++ b/nimdoc/testproject/expected/testproject.html @@ -358,6 +358,7 @@ function main() {
    +
    From 773643b47b04242cb8c81d44a84ae24693ad2ded Mon Sep 17 00:00:00 2001 From: Clyybber Date: Thu, 22 Oct 2020 11:03:05 +0200 Subject: [PATCH 0813/3768] Cleanup --- compiler/commands.nim | 2 -- 1 file changed, 2 deletions(-) diff --git a/compiler/commands.nim b/compiler/commands.nim index 93a202639..325b362a9 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -396,8 +396,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; var key, val: string case switch.normalize - of "input", "i": # PRTEMP - expectArg(conf, switch, arg, pass, info) of "path", "p": expectArg(conf, switch, arg, pass, info) for path in nimbleSubs(conf, arg): From e47c3987c1a9004eb346b3ae64997b33c85ea046 Mon Sep 17 00:00:00 2001 From: narimiran Date: Thu, 22 Oct 2020 11:16:15 +0200 Subject: [PATCH 0814/3768] small changelog cleanup [ci skip] --- changelog.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index 66c9131cd..1885eaacd 100644 --- a/changelog.md +++ b/changelog.md @@ -11,8 +11,9 @@ ## Compiler changes -add `--declaredlocs` to show symbol declaration location in messages -* Source+Edit links now appear on top of every docgen'd page when `nim doc --git.url:url ...` is given + +- Added `--declaredlocs` to show symbol declaration location in messages. +- Source+Edit links now appear on top of every docgen'd page when `nim doc --git.url:url ...` is given. ## Tool changes From 8da0e01769231d795300c7ce6894178cf6025e19 Mon Sep 17 00:00:00 2001 From: n5m <72841454+n5m@users.noreply.github.com> Date: Thu, 22 Oct 2020 09:25:11 +0000 Subject: [PATCH 0815/3768] expect valgrind test failure on memory leak (#15669) * expect valgrind test failure on memory leak * alloc 1 instead of 0 --- testament/tests/shouldfail/tvalgrind.nim | 17 +++++++++++++++++ tests/testament/tshould_not_work.nim | 2 ++ 2 files changed, 19 insertions(+) create mode 100644 testament/tests/shouldfail/tvalgrind.nim diff --git a/testament/tests/shouldfail/tvalgrind.nim b/testament/tests/shouldfail/tvalgrind.nim new file mode 100644 index 000000000..4f699fd3b --- /dev/null +++ b/testament/tests/shouldfail/tvalgrind.nim @@ -0,0 +1,17 @@ +discard """ +valgrind: true +cmd: "nim $target --gc:arc -d:useMalloc $options $file" +""" + +# this is the same check used by testament/specs.nim whether or not valgrind +# tests are supported +when defined(linux) and sizeof(int) == 8: + # discarding this allocation will cause valgrind to fail (which is what we + # want), but valgrind only runs on 64-bit Linux machines... + discard alloc(1) +else: + # ...so on all other OS/architectures, simulate any non-zero exit code to + # mimic how valgrind would have failed on this test. We cannot use things like + # `disabled: "freebsd"` in the Testament configs above or else the tests will + # be SKIP-ed rather than FAIL-ed + quit(1) # choose 1 to match valgrind's `--error-exit=1`, but could be anything diff --git a/tests/testament/tshould_not_work.nim b/tests/testament/tshould_not_work.nim index e7790a4df..501e2c32f 100644 --- a/tests/testament/tshould_not_work.nim +++ b/tests/testament/tshould_not_work.nim @@ -29,6 +29,8 @@ FAIL: tests/shouldfail/tsortoutput.nim C Failure: reOutputsDiffer FAIL: tests/shouldfail/ttimeout.nim C Failure: reTimeout +FAIL: tests/shouldfail/tvalgrind.nim C +Failure: reExitcodesDiffer ''' """ From d8a6b0b04eb690e9d8611bec4ee9d99499cddff1 Mon Sep 17 00:00:00 2001 From: Miran Date: Thu, 22 Oct 2020 12:16:19 +0200 Subject: [PATCH 0816/3768] [backport: 1.4] Better linebreaks (#15658) --- compiler/docgen.nim | 14 +-- config/nimdoc.cfg | 4 +- doc/nimdoc.css | 1 - .../expected/index.html | 2 +- .../expected/subdir/subdir_b/utils.html | 10 +- nimdoc/testproject/expected/testproject.html | 110 +++++++++--------- 6 files changed, 67 insertions(+), 74 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 3945a30f8..1d950f835 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -867,19 +867,13 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags) = xmltree.escape(getPlainDocstring(e).docstringSummary)) d.toc[k].add(ropeFormatNamedVars(d.conf, getConfigVar(d.conf, "doc.item.toc"), - ["name", "header", "desc", "itemID", "header_plain", "itemSym", - "itemSymOrID", "itemSymEnc", "itemSymOrIDEnc", "attype"], - [rope(getName(d, nameNode, d.splitAfter)), result, comm, - itemIDRope, plainNameRope, plainSymbolRope, symbolOrIdRope, - plainSymbolEncRope, symbolOrIdEncRope, attype])) + ["name", "header_plain", "itemSymOrIDEnc"], + [nameRope, plainNameRope, symbolOrIdEncRope])) d.tocTable[k].mgetOrPut(cleanPlainSymbol, nil).add(ropeFormatNamedVars( d.conf, getConfigVar(d.conf, "doc.item.tocTable"), - ["name", "header", "desc", "itemID", "header_plain", "itemSym", - "itemSymOrID", "itemSymEnc", "itemSymOrIDEnc", "attype"], - [rope(getName(d, nameNode, d.splitAfter)), result, comm, - itemIDRope, plainNameRope, plainSymbolRope, - symbolOrIdRope, plainSymbolEncRope, symbolOrIdEncRope, attype])) + ["name", "header_plain", "itemSymOrID", "itemSymOrIDEnc"], + [nameRope, plainNameRope, rope(symbolOrId.replace(",", ",")), symbolOrIdEncRope])) # Ironically for types the complexSymbol is *cleaner* than the plainName # because it doesn't include object fields or documentation comments. So we diff --git a/config/nimdoc.cfg b/config/nimdoc.cfg index db86af021..a99af70ed 100644 --- a/config/nimdoc.cfg +++ b/config/nimdoc.cfg @@ -57,13 +57,13 @@ $seeSrc # See doc.item for available substitution variables. doc.item.toc = """
  • $name$attype
  • + title="$header_plain">$name """ # This is used for TOC items which are grouped by the same name (e.g. procs). doc.item.tocTable = """
  • $itemSymOrID$attype
  • + title="$header_plain">$itemSymOrID """ diff --git a/doc/nimdoc.css b/doc/nimdoc.css index e0c5a1de4..457f024b5 100644 --- a/doc/nimdoc.css +++ b/doc/nimdoc.css @@ -155,7 +155,6 @@ body { .three.columns { width: 22%; - line-break: anywhere; } .nine.columns { diff --git a/nimdoc/test_out_index_dot_html/expected/index.html b/nimdoc/test_out_index_dot_html/expected/index.html index 58ab4fc6d..65241d356 100644 --- a/nimdoc/test_out_index_dot_html/expected/index.html +++ b/nimdoc/test_out_index_dot_html/expected/index.html @@ -96,7 +96,7 @@ function main() {
        foo
      • foo
      • + title="foo()">foo
      diff --git a/nimdoc/testproject/expected/subdir/subdir_b/utils.html b/nimdoc/testproject/expected/subdir/subdir_b/utils.html index 5acf196b0..40de645e3 100644 --- a/nimdoc/testproject/expected/subdir/subdir_b/utils.html +++ b/nimdoc/testproject/expected/subdir/subdir_b/utils.html @@ -101,7 +101,7 @@ function main() {
      • SomeType
      • + enumValueA, enumValueB, enumValueC">SomeType
      @@ -110,7 +110,7 @@ function main() {
        @@ -120,11 +120,11 @@ function main() { Templates
        • aEnum
        • + title="aEnum(): untyped">aEnum
        • bEnum
        • + title="bEnum(): untyped">bEnum
        • fromUtilsGen
        • + title="fromUtilsGen(): untyped">fromUtilsGen
        diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html index 922319914..103dea25f 100644 --- a/nimdoc/testproject/expected/testproject.html +++ b/nimdoc/testproject/expected/testproject.html @@ -102,13 +102,13 @@ function main() {
        • A
        • + aA">A
        • B
        • + bB">B
        • Foo
        • + enumValueA2">Foo
        @@ -116,7 +116,7 @@ function main() { Vars
        • aVariable
        • + title="aVariable: array[1, int]">aVariable
        @@ -124,13 +124,13 @@ function main() { Consts
        • C_A
        • + title="C_A = 0x7FF0000000000000'f64">C_A
        • C_B
        • + title="C_B = 0o377'i8">C_B
        • C_C
        • + title="C_C = 0o277'i8">C_C
        • C_D
        • + title="C_D = 0o177777'i16">C_D
        @@ -139,149 +139,149 @@ function main() {
            z10
          • z10
          • + title="z10()">z10
            tripleStrLitTest
          • tripleStrLitTest
          • + title="tripleStrLitTest()">tripleStrLitTest
            z17
          • z17
          • + title="z17()">z17
            asyncFun3
          • asyncFun3
          • + title="asyncFun3(): owned(Future[void])">asyncFun3
            z2
          • z2
          • + title="z2()">z2
            bar
          • bar,T,T
          • + title="bar[T](a, b: T): T">bar,T,T
            fromUtils3
          • fromUtils3
          • + title="fromUtils3()">fromUtils3
            isValid
          • isValid,T
          • + title="isValid[T](x: T): bool">isValid,T
            z6
          • z6
          • + title="z6(): int">z6
            low
          • low,T
          • + title="low[T: Ordinal | enum | range](x: T): T">low,T
            p1
          • p1
          • + title="p1()">p1
            z11
          • z11
          • + title="z11()">z11
            buzz
          • buzz,T,T
          • + title="buzz[T](a, b: T): T">buzz,T,T
            low2
          • low2,T
          • + title="low2[T: Ordinal | enum | range](x: T): T">low2,T
            z7
          • z7
          • + title="z7(): int">z7
            z13
          • z13
          • + title="z13()">z13
            baz
          • baz,T,T
          • + title="baz[T](a, b: T): T">baz,T,T
          • baz
          • + title="baz()">baz
            addfBug14485
          • addfBug14485
          • + title="addfBug14485()">addfBug14485
            c_printf
          • c_printf,cstring
          • + title="c_printf(frmt: cstring): cint">c_printf,cstring
            z12
          • z12
          • + title="z12(): int">z12
            z3
          • z3
          • + title="z3()">z3
            z9
          • z9
          • + title="z9()">z9
            z4
          • z4
          • + title="z4()">z4
            z8
          • z8
          • + title="z8(): int">z8
            z1
          • z1Foo
          • + title="z1(): Foo">z1
            z5
          • z5
          • + title="z5(): int">z5
            asyncFun2
          • asyncFun2
          • + title="asyncFun2(): owned(Future[void])">asyncFun2
            asyncFun1
          • asyncFun1
          • + title="asyncFun1(): Future[int]">asyncFun1
          @@ -292,7 +292,7 @@ function main() {
              someFunc
            • someFunc
            • + title="someFunc()">someFunc
            @@ -302,11 +302,11 @@ function main() { Methods
            • method1
            • + title="method1(self: Moo)">method1
            • method2
            • + title="method2(self: Moo): int">method2
            • method3
            • + title="method3(self: Moo): int">method3
            @@ -314,11 +314,11 @@ function main() { Iterators
            • fromUtils1
            • + title="fromUtils1(): int">fromUtils1
            • iter1
            • + title="iter1(n: int): int">iter1
            • iter2
            • + title="iter2(n: int): int">iter2
            @@ -326,11 +326,11 @@ function main() { Macros
            • bar
            • + title="bar(): untyped">bar
            • z16
            • + title="z16()">z16
            • z18
            • + title="z18(): int">z18
            @@ -338,19 +338,19 @@ function main() { Templates
            • fromUtils2
            • + title="fromUtils2()">fromUtils2
            • z6t
            • + title="z6t(): int">z6t
            • foo
            • + title="foo(a, b: SomeType)">foo
            • myfn
            • + title="myfn()">myfn
            • z14
            • + title="z14()">z14
            • z15
            • + title="z15()">z15
            • testNimDocTrailingExample
            • + title="testNimDocTrailingExample()">testNimDocTrailingExample
            From 7c359a062c17696aab5dd5b75d595af0596c001c Mon Sep 17 00:00:00 2001 From: Clyybber Date: Thu, 22 Oct 2020 12:51:03 +0200 Subject: [PATCH 0817/3768] Add testcase for #12252 (#15676) --- tests/defaultprocparam/tdefaultprocparam.nim | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/defaultprocparam/tdefaultprocparam.nim b/tests/defaultprocparam/tdefaultprocparam.nim index 0d62cc955..90edfa8c9 100644 --- a/tests/defaultprocparam/tdefaultprocparam.nim +++ b/tests/defaultprocparam/tdefaultprocparam.nim @@ -81,3 +81,10 @@ block: echo p3(a) echo p3(b) echo p3(a, 0) + +# bug #12252 +proc foo(a = 0, b = a.high, c = high(typeof(a))) = + discard + +foo() + From 7435d912adcf411feed4ba124808527c7e04a44f Mon Sep 17 00:00:00 2001 From: Clyybber Date: Thu, 22 Oct 2020 13:23:39 +0200 Subject: [PATCH 0818/3768] Add testcase for #14601 (#15677) --- tests/destructor/tdestructor3.nim | 46 ++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/tests/destructor/tdestructor3.nim b/tests/destructor/tdestructor3.nim index ca9a891e1..eb49f3f15 100644 --- a/tests/destructor/tdestructor3.nim +++ b/tests/destructor/tdestructor3.nim @@ -127,4 +127,48 @@ proc test2() = echo "---------------" echo "app begin" test2() -echo "app end" \ No newline at end of file +echo "app end" + +# bug #14601 + +when true: # D20200607T202043 + type Foo2 = object + x: int + x2: array[10, int] + + type Vec = object + vals: seq[Foo2] + + proc `=destroy`*(a: var Foo2) {.inline.} = + discard + + proc initFoo2(x: int): Foo2 = Foo2(x: x) + + proc add2(v: var Vec, a: Foo2) = # ditto with `a: sink Foo2` + v.vals.add a + + proc add3(v: var Vec, a: Foo2) = # ditto with `a: sink Foo2` + v.vals = @[a] + + proc add4(v: var Vec, a: sink Foo2) = # ditto with `a: sink Foo2` + v.vals.add a + + proc add5(v: var Vec, a: sink Foo2) = # ditto with `a: sink Foo2` + v.vals = @[a] + + proc main2()= + var a: Vec + var b = Foo2(x: 10) + a.add2 b # ok + a.vals.add Foo2(x: 10) # ok + a.add2 initFoo2(x = 10) # ok + a.add2 Foo2(x: 10) # bug + a.add3 initFoo2(x = 10) # ok + a.add3 Foo2(x: 10) # bug + a.add4 initFoo2(x = 10) # ok + a.add4 Foo2(x: 10) # bug + a.add5 initFoo2(x = 10) # ok + a.add5 Foo2(x: 10) # bug + main2() + + From 8358974b73afe8db06822779f2a28adbafc58280 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 22 Oct 2020 17:28:43 +0200 Subject: [PATCH 0819/3768] fixes #15652 [backport:1.4] (#15679) --- compiler/ccgexprs.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 004b8f41e..b2a325632 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -760,7 +760,7 @@ proc genDeref(p: BProc, e: PNode, d: var TLoc) = # See tmissingderef. So we get rid of the deref instead. The codegen # ends up using 'memcpy' for the array assignment, # so the '&' and '*' cancel out: - putIntoDest(p, d, lodeTyp(a.t[0]), rdLoc(a), a.storage) + putIntoDest(p, d, e, rdLoc(a), a.storage) else: putIntoDest(p, d, e, "(*$1)" % [rdLoc(a)], a.storage) From 64016ddedcfc266c87d8fe7eece8b9476aa6db30 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 22 Oct 2020 18:52:05 +0200 Subject: [PATCH 0820/3768] fixes view types for sizeof() and --gc:orc (#15680) --- compiler/ccgexprs.nim | 6 +++--- compiler/sizealignoffsetimpl.nim | 2 +- tests/views/tsplit_into_seq.nim | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index b2a325632..5c897e1a4 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2328,10 +2328,10 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mNewSeqOfCap: genNewSeqOfCap(p, e, d) of mSizeOf: let t = e[1].typ.skipTypes({tyTypeDesc}) - putIntoDest(p, d, e, "((NI)sizeof($1))" % [getTypeDesc(p.module, t)]) + putIntoDest(p, d, e, "((NI)sizeof($1))" % [getTypeDesc(p.module, t, skVar)]) of mAlignOf: let t = e[1].typ.skipTypes({tyTypeDesc}) - putIntoDest(p, d, e, "((NI)NIM_ALIGNOF($1))" % [getTypeDesc(p.module, t)]) + putIntoDest(p, d, e, "((NI)NIM_ALIGNOF($1))" % [getTypeDesc(p.module, t, skVar)]) of mOffsetOf: var dotExpr: PNode if e[1].kind == nkDotExpr: @@ -2341,7 +2341,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = else: internalError(p.config, e.info, "unknown ast") let t = dotExpr[0].typ.skipTypes({tyTypeDesc}) - let tname = getTypeDesc(p.module, t) + let tname = getTypeDesc(p.module, t, skVar) let member = if t.kind == tyTuple: "Field" & rope(dotExpr[1].sym.position) diff --git a/compiler/sizealignoffsetimpl.nim b/compiler/sizealignoffsetimpl.nim index be35247f7..3e536f5d7 100644 --- a/compiler/sizealignoffsetimpl.nim +++ b/compiler/sizealignoffsetimpl.nim @@ -242,7 +242,7 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) = else: typ.size = conf.target.ptrSize typ.align = int16(conf.target.ptrSize) - of tyCString, tySequence, tyPtr, tyRef, tyVar, tyLent, tyOpenArray: + of tyCString, tySequence, tyPtr, tyRef, tyVar, tyLent: let base = typ.lastSon if base == typ: # this is not the correct location to detect ``type A = ptr A`` diff --git a/tests/views/tsplit_into_seq.nim b/tests/views/tsplit_into_seq.nim index 49968f4c2..a0861458b 100644 --- a/tests/views/tsplit_into_seq.nim +++ b/tests/views/tsplit_into_seq.nim @@ -4,7 +4,7 @@ asdf 231 231 ''' - cmd: "nim c $file" + cmd: "nim c --gc:orc $file" """ {.experimental: "views".} From 2cb484cefb13e4663afd8e0eb2ad81d43f097803 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 22 Oct 2020 12:02:18 -0700 Subject: [PATCH 0821/3768] followup #15642: make source + edit also work with stdlib (which uses -d:boot) (#15684) --- config/nimdoc.cfg | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/config/nimdoc.cfg b/config/nimdoc.cfg index a99af70ed..e4b485ca0 100644 --- a/config/nimdoc.cfg +++ b/config/nimdoc.cfg @@ -146,6 +146,7 @@ doc.body_toc_group = """ $body_toc_groupsection $tableofcontents
    + $seeSrc
    $deprecationMsg @@ -156,7 +157,7 @@ doc.body_toc_group = """ """ @else - +# keep in sink with other `doc.body_toc_group` or better, refactor doc.body_toc_group = """
    From 01a62320f03ebd3282a7def231351c4f84c319d2 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Fri, 23 Oct 2020 03:26:50 +0200 Subject: [PATCH 0822/3768] Fix #12410 (#15685) * Fix #12410, big thanks to @pmetras for this fix * Add testcase --- lib/pure/streams.nim | 2 +- tests/stdlib/tstreams.nim | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim index 77f384a79..dc00271a5 100644 --- a/lib/pure/streams.nim +++ b/lib/pure/streams.nim @@ -242,7 +242,7 @@ proc readDataStr*(s: Stream, buffer: var string, slice: Slice[int]): int = result = s.readDataStrImpl(s, buffer, slice) else: # fallback - result = s.readData(addr buffer[0], buffer.len) + result = s.readData(addr buffer[slice.a], slice.b + 1 - slice.a) template jsOrVmBlock(caseJsOrVm, caseElse: untyped): untyped = when nimvm: diff --git a/tests/stdlib/tstreams.nim b/tests/stdlib/tstreams.nim index 354bdf60f..6979bae38 100644 --- a/tests/stdlib/tstreams.nim +++ b/tests/stdlib/tstreams.nim @@ -6,6 +6,7 @@ Nice name: Arne fs is: nil threw exception +_heh_ ''' nimout: ''' I @@ -49,3 +50,15 @@ block tstreams3: for line in s.lines: echo line s.close + +# bug #12410 + +var a = newStringStream "hehohihahuhyh" +a.readDataStrImpl = nil + +var buffer = "_ooo_" + +doAssert a.readDataStr(buffer, 1..3) == 3 + +echo buffer + From 275354709d2a5e38b9e06b2cee823ddef756d62d Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 22 Oct 2020 23:03:09 -0700 Subject: [PATCH 0823/3768] make cgen header consistent with jsgen header (#15682) --- compiler/cgen.nim | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index afb36e787..5c60a3cf3 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -1280,18 +1280,15 @@ proc addNimDefines(result: var Rope; conf: ConfigRef) {.inline.} = if conf.isDefined("nimEmulateOverflowChecks"): result.add("#define NIM_EmulateOverflowChecks\L") +proc headerTop(): Rope = + result = "/* Generated by Nim Compiler v$1 */$N" % [rope(VersionAsString)] + proc getCopyright(conf: ConfigRef; cfile: Cfile): Rope = - if optCompileOnly in conf.globalOptions: - result = ("/* Generated by Nim Compiler v$1 */$N" & - "/* The generated code is subject to the original license. */$N") % - [rope(VersionAsString)] - else: - result = ("/* Generated by Nim Compiler v$1 */$N" & - "/* The generated code is subject to the original license. */$N" & - "/* Compiled for: $2, $3, $4 */$N" & - "/* Command for C compiler:$n $5 */$N") % - [rope(VersionAsString), - rope(platform.OS[conf.target.targetOS].name), + result = headerTop() + if optCompileOnly notin conf.globalOptions: + result.add ("/* Compiled for: $1, $2, $3 */$N" & + "/* Command for C compiler:$n $4 */$N") % + [rope(platform.OS[conf.target.targetOS].name), rope(platform.CPU[conf.target.targetCPU].name), rope(extccomp.CC[conf.cCompiler].name), rope(getCompileCFileCmd(conf, cfile))] @@ -1858,11 +1855,7 @@ proc myOpen(graph: ModuleGraph; module: PSym): PPassContext {.nosinks.} = incl g.generatedHeader.flags, isHeaderFile proc writeHeader(m: BModule) = - # could factor with 2 other instances - var result = ("/* Generated by Nim Compiler v$1 */$N" & - "/* The generated code is subject to the original license. */$N") % - [rope(VersionAsString)] - + var result = headerTop() var guard = "__$1__" % [m.filename.splitFile.name.rope] result.addf("#ifndef $1$n#define $1$n", [guard]) addNimDefines(result, m.config) From ae320b4e7dfd07ed6ae85a2693da3bb8568520c6 Mon Sep 17 00:00:00 2001 From: cooldome Date: Fri, 23 Oct 2020 10:14:54 +0100 Subject: [PATCH 0824/3768] fix #15662 (#15678) * fix #15662 * alternative fix * fix spacing --- compiler/vmgen.nim | 32 +++++++++++++++----------------- tests/vm/tconstobj.nim | 5 +++++ 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 6f81c6a9c..4dcd01458 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -586,9 +586,6 @@ proc genCall(c: PCtx; n: PNode; dest: var TDest) = # varargs need 'opcSetType' for the FFI support: let fntyp = skipTypes(n[0].typ, abstractInst) for i in 0.. 0 and i < fntyp.len: - # let paramType = fntyp.n[i] - # if paramType.typ.isCompileTimeOnly: continue var r: TRegister = x+i c.gen(n[i], r, {gfIsParam}) if i >= fntyp.len: @@ -1926,20 +1923,21 @@ proc genObjConstr(c: PCtx, n: PNode, dest: var TDest) = proc genTupleConstr(c: PCtx, n: PNode, dest: var TDest) = if dest < 0: dest = c.getTemp(n.typ) - c.gABx(n, opcLdNull, dest, c.genType(n.typ)) - # XXX x = (x.old, 22) produces wrong code ... stupid self assignments - for i in 0.. Date: Fri, 23 Oct 2020 23:16:47 +0200 Subject: [PATCH 0825/3768] close #8007 (#15695) --- tests/vm/tvmmisc.nim | 51 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/tests/vm/tvmmisc.nim b/tests/vm/tvmmisc.nim index 73faac495..61a187faf 100644 --- a/tests/vm/tvmmisc.nim +++ b/tests/vm/tvmmisc.nim @@ -230,3 +230,54 @@ block: doAssert d == @[] doAssert e == @[] doAssert f == @[] + +import tables + +block: # bug #8007 + type + CostKind = enum + Fixed, + Dynamic + + Cost = object + case kind*: CostKind + of Fixed: + cost*: int + of Dynamic: + handler*: proc(value: int): int {.nimcall.} + + proc foo(value: int): int {.nimcall.} = + sizeof(value) + + const a: array[2, Cost] =[ + Cost(kind: Fixed, cost: 999), + Cost(kind: Dynamic, handler: foo) + ] + + # OK with arrays & object variants + doAssert $a == "[(kind: Fixed, cost: 999), (kind: Dynamic, handler: ...)]" + + const b: Table[int, Cost] = { + 0: Cost(kind: Fixed, cost: 999), + 1: Cost(kind: Dynamic, handler: foo) + }.toTable + + # KO with Tables & object variants + # echo b # {0: (kind: Fixed, cost: 0), 1: (kind: Dynamic, handler: ...)} # <----- wrong behaviour + doAssert $b == "{0: (kind: Fixed, cost: 999), 1: (kind: Dynamic, handler: ...)}" + + const c: Table[int, int] = { + 0: 100, + 1: 999 + }.toTable + + # OK with Tables and primitive int + doAssert $c == "{0: 100, 1: 999}" + + # For some reason the following gives + # Error: invalid type for const: Cost + const d0 = Cost(kind: Fixed, cost: 999) + + # OK with seq & object variants + const d = @[Cost(kind: Fixed, cost: 999), Cost(kind: Dynamic, handler: foo)] + doAssert $d == "@[(kind: Fixed, cost: 999), (kind: Dynamic, handler: ...)]" From d4022ebe53062a7e1be5fd55637a3cce39b93a12 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Sat, 24 Oct 2020 11:45:18 +0200 Subject: [PATCH 0826/3768] Revert "fixes #15280 [backport:1.2] (#15281)" (#15700) This reverts commit 3f00a738dbc8319b4dd2b86bf5529c096f2dd243. --- compiler/sem.nim | 2 +- tests/arc/tmovebug.nim | 6 +-- tests/casestmt/tcasestmt.nim | 2 +- tests/semreject/tmixing_return_and_exprs.nim | 44 -------------------- 4 files changed, 5 insertions(+), 49 deletions(-) delete mode 100644 tests/semreject/tmixing_return_and_exprs.nim diff --git a/compiler/sem.nim b/compiler/sem.nim index bc6119460..705e1b72c 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -184,7 +184,7 @@ proc endsInNoReturn(n: PNode): bool = var it = n while it.kind in {nkStmtList, nkStmtListExpr} and it.len > 0: it = it.lastSon - result = it.kind in (nkLastBlockStmts-{nkReturnStmt}) or + result = it.kind in nkLastBlockStmts or it.kind in nkCallKinds and it[0].kind == nkSym and sfNoReturn in it[0].sym.flags proc commonType*(x: PType, y: PNode): PType = diff --git a/tests/arc/tmovebug.nim b/tests/arc/tmovebug.nim index c735ea1eb..3b7a7c5df 100644 --- a/tests/arc/tmovebug.nim +++ b/tests/arc/tmovebug.nim @@ -502,7 +502,7 @@ weirdScopes() # bug #14985 proc getScope(): string = if true: - "hi" + return "hi" else: "else" @@ -512,14 +512,14 @@ proc getScope3(): string = try: "try" except: - "except" + return "except" echo getScope3() proc getScope2(): string = case true of true: - "bye" + return "bye" else: "else" diff --git a/tests/casestmt/tcasestmt.nim b/tests/casestmt/tcasestmt.nim index 2151892aa..53cccdb64 100644 --- a/tests/casestmt/tcasestmt.nim +++ b/tests/casestmt/tcasestmt.nim @@ -258,7 +258,7 @@ func foo(input: string): int = try: parseInt(input) except: - 0 + return func foo2(b, input: string): int = case b: diff --git a/tests/semreject/tmixing_return_and_exprs.nim b/tests/semreject/tmixing_return_and_exprs.nim deleted file mode 100644 index 2848c47e4..000000000 --- a/tests/semreject/tmixing_return_and_exprs.nim +++ /dev/null @@ -1,44 +0,0 @@ -discard """ - errormsg: "expression 'len(src) shl 1' is of type 'int' and has to be used (or discarded)" - line: 19 -""" - -# bug #15280 - -type - HexFlags* {.pure.} = enum - LowerCase, ## Produce lowercase hexadecimal characters - PadOdd, ## Pads odd strings - SkipSpaces, ## Skips all the whitespace characters inside of string - SkipPrefix ## Skips `0x` and `x` prefixes at the begining of string - - -proc bytesToHex*(src: openarray[byte], dst: var openarray[char], - flags: set[HexFlags]): int = - if len(dst) == 0: - (len(src) shl 1) - else: - var halflast = false - let dstlen = len(dst) - var srclen = len(src) - - if dstlen < (srclen shl 1): - if (dstlen and 1) == 1: - srclen = (dstlen - 1) shr 1 - halflast = true - else: - srclen = (dstlen shr 1) - - let lowercase = (HexFlags.LowerCase in flags) - - var k = 0 - for i in 0 ..< srclen: - let x = int(src[i]) - inc(k, 2) - - if halflast: - let x = int(src[srclen]) - inc(k) - - return k - From e18fc698ed4fba3fc0e33e8116d3739d2161e618 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Sat, 24 Oct 2020 17:02:42 +0200 Subject: [PATCH 0827/3768] Try to fix CI failures (#15701) * Use cligens release version * Rework important_packages main loop * Fix * Fix * Create pkgs dir * Don't use nimble develop since it doesn't work for binary only packages, and always install head * Use git to get the latest release/tag instead * Tackle the root cause * Reduce diff * Cleanup --- testament/categories.nim | 75 ++++++------- testament/important_packages.nim | 175 +++++++++++++++---------------- 2 files changed, 120 insertions(+), 130 deletions(-) diff --git a/testament/categories.nim b/testament/categories.nim index 760c3c5be..07d35f6fb 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -452,15 +452,15 @@ type ppOne ppTwo -iterator listPackages(part: PkgPart): tuple[name, url, cmd: string, hasDeps: bool, useHead: bool] = +iterator listPackages(part: PkgPart): tuple[name, cmd, url: string, useHead: bool] = let packageList = parseFile(packageIndex) let importantList = case part of ppOne: important_packages.packages1 of ppTwo: important_packages.packages2 - for n, cmd, hasDeps, url, useHead in importantList.items: + for n, cmd, url, useHead in importantList.items: if url.len != 0: - yield (n, url, cmd, hasDeps, useHead) + yield (n, cmd, url, useHead) else: var found = false for package in packageList.items: @@ -468,7 +468,7 @@ iterator listPackages(part: PkgPart): tuple[name, url, cmd: string, hasDeps: boo if name == n: found = true let pUrl = package["url"].str - yield (name, pUrl, cmd, hasDeps, useHead) + yield (name, cmd, pUrl, useHead) break if not found: raise newException(ValueError, "Cannot find package '$#'." % n) @@ -479,16 +479,6 @@ proc makeSupTest(test, options: string, cat: Category): TTest = result.options = options result.startTime = epochTime() -proc actionRetry(maxRetry: int, backoffDuration: float, action: proc: bool): bool = - ## retry `action` up to `maxRetry` times with exponential backoff and initial - ## duraton of `backoffDuration` seconds - var t = backoffDuration - for i in 0.. Date: Sun, 25 Oct 2020 01:56:07 +0300 Subject: [PATCH 0828/3768] [minor] nimVMDebug: fix codeListing formatting for jump-to-file to work (#15711) --- compiler/vmgen.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 4dcd01458..44b10ea67 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -99,7 +99,7 @@ proc codeListing(c: PCtx, result: var string, start=0; last = -1) = inc i else: result.addf("\t$#\tr$#, $#", opc.toStr, x.regA, x.regBx-wordExcess) - result.add("\t#") + result.add("\t# ") result.add(debugInfo(c, c.debug[i])) result.add("\n") inc i From 7252a50fefb844d97ad4630c1db00063380ff3c4 Mon Sep 17 00:00:00 2001 From: rockcavera Date: Sun, 25 Oct 2020 04:29:57 -0300 Subject: [PATCH 0829/3768] fix #15698 (#15699) * fix in the net.`$` to print zeros that are not in the compressed group * Update lib/pure/net.nim Co-authored-by: Andreas Rumpf * Update net.nim Co-authored-by: Andreas Rumpf --- lib/pure/net.nim | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 3ef41aa3c..cd509463a 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -1827,6 +1827,10 @@ proc `$`*(address: IpAddress): string = result.add(chr(uint16(ord('a'))+val-0xA)) afterLeadingZeros = true mask = mask shr 4 + + if not afterLeadingZeros: + result.add '0' + printedLastGroup = true proc dial*(address: string, port: Port, From 226595515c25785eaf078834dfb6f0ac337a5278 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 25 Oct 2020 08:50:47 +0100 Subject: [PATCH 0830/3768] explicit ID generation for easier IC (#15559) * refactoring: idents don't need inheritance * refactoring: adding an IdGenerator (part 1) * refactoring: adding an IdGenerator (part 2) * refactoring: adding an IdGenerator (part 3) * refactoring: adding an IdGenerator (part 4) * refactoring: adding an IdGenerator (part 5) * refactoring: adding an IdGenerator (part 5) * IdGenerator must be a ref type; hello world works again * make bootstrapping work again * progress: add back the 'exactReplica' ideas * added back the missing exactReplica hacks * make tcompilerapi work again * make important packages green * attempt to fix the build for 32 bit machines (probably need a better solution here) --- compiler/ast.nim | 93 +++++++++++------- compiler/ccgexprs.nim | 20 ++-- compiler/ccgmerge.nim | 9 +- compiler/ccgstmts.nim | 4 +- compiler/ccgtypes.nim | 14 +-- compiler/cgen.nim | 11 ++- compiler/cgmeth.nim | 12 +-- compiler/closureiters.nim | 35 ++++--- compiler/cmdlinehelper.nim | 2 +- compiler/depends.nim | 2 +- compiler/docgen2.nim | 4 +- compiler/enumtostr.nim | 20 ++-- compiler/evaltempl.nim | 5 +- compiler/guards.nim | 5 +- compiler/hlo.nim | 4 +- compiler/idents.nim | 7 +- compiler/idgen.nim | 59 ------------ compiler/importer.nim | 2 +- compiler/injectdestructors.nim | 27 +++--- compiler/isolation_check.nim | 2 +- compiler/jsgen.nim | 18 ++-- compiler/lambdalifting.nim | 138 ++++++++++++++------------- compiler/liftdestructors.nim | 99 ++++++++++--------- compiler/liftlocals.nim | 8 +- compiler/lookups.nim | 2 +- compiler/lowerings.nim | 61 ++++++------ compiler/magicsys.nim | 30 +++--- compiler/main.nim | 17 ++-- compiler/modulegraphs.nim | 7 +- compiler/modules.nim | 21 ++-- compiler/nimconf.nim | 21 ++-- compiler/nimeval.nim | 14 ++- compiler/passaux.nim | 8 +- compiler/passes.nim | 32 ++----- compiler/plugins/itersgen.nim | 6 +- compiler/plugins/locals.nim | 4 +- compiler/pragmas.nim | 8 +- compiler/rod.nim | 4 +- compiler/rodimpl.nim | 4 +- compiler/scriptconfig.nim | 10 +- compiler/sem.nim | 44 ++++----- compiler/semcall.nim | 4 +- compiler/semdata.nim | 35 +++---- compiler/semexprs.nim | 54 +++++------ compiler/semfields.nim | 2 +- compiler/semfold.nim | 131 +++++++++---------------- compiler/semgnrc.nim | 10 +- compiler/seminst.nim | 18 ++-- compiler/semmagic.nim | 38 ++++---- compiler/semparallel.nim | 30 +++--- compiler/sempass2.nim | 18 ++-- compiler/semstmts.nim | 44 ++++----- compiler/semtempl.nim | 6 +- compiler/semtypes.nim | 84 ++++++++-------- compiler/semtypinst.nim | 19 ++-- compiler/sigmatch.nim | 18 ++-- compiler/sinkparameter_inference.nim | 12 +-- compiler/spawn.nim | 107 +++++++++++---------- compiler/transf.nim | 62 ++++++------ compiler/types.nim | 12 +-- compiler/vm.nim | 68 ++++++------- compiler/vmdef.nim | 7 +- compiler/vmdeps.nim | 51 +++++----- compiler/vmgen.nim | 6 +- compiler/vmmarshal.nim | 23 ++--- compiler/vmops.nim | 2 +- lib/system/iterators.nim | 2 +- 67 files changed, 853 insertions(+), 903 deletions(-) delete mode 100644 compiler/idgen.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index d81ccf33b..a9793fb21 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -10,7 +10,7 @@ # abstract syntax tree + symbol table import - lineinfos, hashes, options, ropes, idents, idgen, int128 + lineinfos, hashes, options, ropes, idents, int128 from strutils import toLowerAscii export int128 @@ -717,6 +717,14 @@ const mInSet, mRepr} type + ItemId* = object + module*: int32 + item*: int32 + + TIdObj* = object of RootObj + itemId*: ItemId + PIdObj* = ref TIdObj + PNode* = ref TNode TNodeSeq* = seq[PNode] PType* = ref TType @@ -926,7 +934,7 @@ type loc*: TLoc typeInst*: PType # for generic instantiations the tyGenericInst that led to this # type. - uniqueId*: int # due to a design mistake, we need to keep the real ID here as it + uniqueId*: ItemId # due to a design mistake, we need to keep the real ID here as it # required by the --incremental:on mode. TPair* = object @@ -1060,6 +1068,35 @@ proc getnimblePkg*(a: PSym): PSym = else: assert false, $result.kind +const + moduleShift = when defined(cpu32): 20 else: 24 + +template id*(a: PIdObj): int = + let x = a + (x.itemId.module.int shl moduleShift) + x.itemId.item.int + +type + IdGenerator* = ref ItemId # unfortunately, we really need the 'shared mutable' aspect here. + +const + PackageModuleId* = -3'i32 + +proc idGeneratorFromModule*(m: PSym): IdGenerator = + assert m.kind == skModule + result = IdGenerator(module: m.itemId.module, item: m.itemId.item) + +proc nextId*(x: IdGenerator): ItemId {.inline.} = + inc x.item + result = x[] + +when false: + proc storeBack*(dest: var IdGenerator; src: IdGenerator) {.inline.} = + assert dest.ItemId.module == src.ItemId.module + if dest.ItemId.item > src.ItemId.item: + echo dest.ItemId.item, " ", src.ItemId.item, " ", src.ItemId.module + assert dest.ItemId.item <= src.ItemId.item + dest = src + proc getnimblePkgId*(a: PSym): int = let b = a.getnimblePkg result = if b == nil: -1 else: b.id @@ -1164,13 +1201,11 @@ proc newTreeIT*(kind: TNodeKind; info: TLineInfo; typ: PType; children: varargs[ template previouslyInferred*(t: PType): PType = if t.sons.len > 1: t.lastSon else: nil -proc newSym*(symKind: TSymKind, name: PIdent, owner: PSym, +proc newSym*(symKind: TSymKind, name: PIdent, id: ItemId, owner: PSym, info: TLineInfo; options: TOptions = {}): PSym = # generates a symbol and initializes the hash field too - result = PSym(name: name, kind: symKind, flags: {}, info: info, id: getID(), + result = PSym(name: name, kind: symKind, flags: {}, info: info, itemId: id, options: options, owner: owner, offset: defaultOffset) - when debugIds: - registerId(result) proc astdef*(s: PSym): PNode = # get only the definition (initializer) portion of the ast @@ -1344,13 +1379,11 @@ proc `$`*(s: PSym): string = else: result = "" -proc newType*(kind: TTypeKind, owner: PSym): PType = - let id = getID() +proc newType*(kind: TTypeKind, id: ItemId; owner: PSym): PType = result = PType(kind: kind, owner: owner, size: defaultSize, - align: defaultAlignment, id: id, uniqueId: id, - lockLevel: UnspecifiedLockLevel) - when debugIds: - registerId(result) + align: defaultAlignment, itemId: id, + lockLevel: UnspecifiedLockLevel, + uniqueId: id) when false: if result.id == 76426: echo "KNID ", kind @@ -1392,22 +1425,18 @@ proc assignType*(dest, src: PType) = newSons(dest, src.len) for i in 0..` and ## returned. Otherwise ``typ`` is simply returned as-is. result = typ if typ.kind != kind: - result = newType(kind, typ.owner) + result = newType(kind, nextId(idgen), typ.owner) rawAddSon(result, typ) -proc toRef*(typ: PType): PType = +proc toRef*(typ: PType; idgen: IdGenerator): PType = ## If ``typ`` is a tyObject then it is converted into a `ref ` and ## returned. Otherwise ``typ`` is simply returned as-is. if typ.skipTypes({tyAlias, tyGenericInst}).kind == tyObject: - result = newType(tyRef, typ.owner) + result = newType(tyRef, nextId(idgen), typ.owner) rawAddSon(result, typ) proc toObject*(typ: PType): PType = @@ -1888,8 +1917,8 @@ proc isSinkParam*(s: PSym): bool {.inline.} = proc isSinkType*(t: PType): bool {.inline.} = t.kind == tySink or tfHasOwned in t.flags -proc newProcType*(info: TLineInfo; owner: PSym): PType = - result = newType(tyProc, owner) +proc newProcType*(info: TLineInfo; id: ItemId; owner: PSym): PType = + result = newType(tyProc, id, owner) result.n = newNodeI(nkFormalParams, info) rawAddSon(result, nil) # return type # result.n[0] used to be `nkType`, but now it's `nkEffectList` because diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 5c897e1a4..b6caec760 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1765,20 +1765,20 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) = else: putIntoDest(p, d, e, rope(lengthOrd(p.config, typ))) else: internalError(p.config, e.info, "genArrayLen()") -proc makePtrType(baseType: PType): PType = - result = newType(tyPtr, baseType.owner) - addSonSkipIntLit(result, baseType) +proc makePtrType(baseType: PType; idgen: IdGenerator): PType = + result = newType(tyPtr, nextId idgen, baseType.owner) + addSonSkipIntLit(result, baseType, idgen) -proc makeAddr(n: PNode): PNode = +proc makeAddr(n: PNode; idgen: IdGenerator): PNode = if n.kind == nkHiddenAddr: result = n else: result = newTree(nkHiddenAddr, n) - result.typ = makePtrType(n.typ) + result.typ = makePtrType(n.typ, idgen) proc genSetLengthSeq(p: BProc, e: PNode, d: var TLoc) = if optSeqDestructors in p.config.globalOptions: - e[1] = makeAddr(e[1]) + e[1] = makeAddr(e[1], p.module.idgen) genCall(p, e, d) return var a, b, call: TLoc @@ -2240,7 +2240,7 @@ proc genEnumToStr(p: BProc, e: PNode, d: var TLoc) = toStrProc = p break if toStrProc == nil: - toStrProc = genEnumToStrProc(t, e.info, p.module.g.graph) + toStrProc = genEnumToStrProc(t, e.info, p.module.g.graph, p.module.idgen) t.methods.add((ToStringProcSlot, toStrProc)) var n = copyTree(e) n[0] = newSymNode(toStrProc) @@ -2294,7 +2294,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mAppendStrStr: genStrAppend(p, e, d) of mAppendSeqElem: if optSeqDestructors in p.config.globalOptions: - e[1] = makeAddr(e[1]) + e[1] = makeAddr(e[1], p.module.idgen) genCall(p, e, d) else: genSeqElemAppend(p, e, d) @@ -2393,13 +2393,13 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = when defined(leanCompiler): quit "compiler built without support for the 'spawn' statement" else: - let n = spawn.wrapProcForSpawn(p.module.g.graph, p.module.module, e, e.typ, nil, nil) + let n = spawn.wrapProcForSpawn(p.module.g.graph, p.module.idgen, p.module.module, e, e.typ, nil, nil) expr(p, n, d) of mParallel: when defined(leanCompiler): quit "compiler built without support for the 'parallel' statement" else: - let n = semparallel.liftParallel(p.module.g.graph, p.module.module, e) + let n = semparallel.liftParallel(p.module.g.graph, p.module.idgen, p.module.module, e) expr(p, n, d) of mDeepCopy: if p.config.selectedGC in {gcArc, gcOrc} and optEnableDeepCopy notin p.config.globalOptions: diff --git a/compiler/ccgmerge.nim b/compiler/ccgmerge.nim index a87d5b609..40833a64e 100644 --- a/compiler/ccgmerge.nim +++ b/compiler/ccgmerge.nim @@ -180,10 +180,6 @@ proc readKey(L: var TBaseLexer, result: var string) = if L.buf[pos] != ':': doAssert(false, "ccgmerge: ':' expected") L.bufpos = pos + 1 # skip ':' -proc newFakeType(id: int): PType = - new(result) - result.id = id - proc readTypeCache(L: var TBaseLexer, result: var TypeCache) = if ^L.bufpos != '{': doAssert(false, "ccgmerge: '{' expected") inc L.bufpos @@ -192,10 +188,7 @@ proc readTypeCache(L: var TBaseLexer, result: var TypeCache) = var key = decodeStr(L.buf, L.bufpos) if ^L.bufpos != ':': doAssert(false, "ccgmerge: ':' expected") inc L.bufpos - var value = decodeStr(L.buf, L.bufpos) - # XXX implement me - when false: - idTablePut(result, newFakeType(key), value.rope) + discard decodeStr(L.buf, L.bufpos) inc L.bufpos proc readIntSet(L: var TBaseLexer, result: var IntSet) = diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 1282cf11f..aaed37508 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -66,7 +66,7 @@ proc genVarTuple(p: BProc, n: PNode) = # if we have a something that's been captured, use the lowering instead: for i in 0..= prc.ast.len: @@ -1844,9 +1844,10 @@ template injectG() {.dirty.} = when not defined(nimHasSinkInference): {.pragma: nosinks.} -proc myOpen(graph: ModuleGraph; module: PSym): PPassContext {.nosinks.} = +proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nosinks.} = injectG() result = newModule(g, module, graph.config) + result.idgen = idgen if optGenIndex in graph.config.globalOptions and g.generatedHeader == nil: let f = if graph.config.headerFile.len > 0: AbsoluteFile graph.config.headerFile else: graph.config.projectFull @@ -1920,9 +1921,9 @@ proc myProcess(b: PPassContext, n: PNode): PNode = m.initProc.options = initProcOptions(m) #softRnl = if optLineDir in m.config.options: noRnl else: rnl # XXX replicate this logic! - var transformedN = transformStmt(m.g.graph, m.module, n) + var transformedN = transformStmt(m.g.graph, m.idgen, m.module, n) if sfInjectDestructors in m.module.flags: - transformedN = injectDestructorCalls(m.g.graph, m.module, transformedN) + transformedN = injectDestructorCalls(m.g.graph, m.idgen, m.module, transformedN) if m.hcrOn: addHcrInitGuards(m.initProc, transformedN, m.inHcrInitGuard) diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim index 9b871a898..a0c16f2ed 100644 --- a/compiler/cgmeth.nim +++ b/compiler/cgmeth.nim @@ -107,11 +107,11 @@ proc attachDispatcher(s: PSym, dispatcher: PNode) = s.ast[resultPos] = newNodeI(nkEmpty, s.info) s.ast[dispatcherPos] = dispatcher -proc createDispatcher(s: PSym): PSym = - var disp = copySym(s) +proc createDispatcher(s: PSym; idgen: IdGenerator): PSym = + var disp = copySym(s, nextId(idgen)) incl(disp.flags, sfDispatcher) excl(disp.flags, sfExported) - disp.typ = copyType(disp.typ, disp.typ.owner, false) + disp.typ = copyType(disp.typ, nextId(idgen), disp.typ.owner) # we can't inline the dispatcher itself (for now): if disp.typ.callConv == ccInline: disp.typ.callConv = ccNimCall disp.ast = copyTree(s.ast) @@ -119,7 +119,7 @@ proc createDispatcher(s: PSym): PSym = disp.loc.r = nil if s.typ[0] != nil: if disp.ast.len > resultPos: - disp.ast[resultPos].sym = copySym(s.ast[resultPos].sym) + disp.ast[resultPos].sym = copySym(s.ast[resultPos].sym, nextId(idgen)) else: # We've encountered a method prototype without a filled-in # resultPos slot. We put a placeholder in there that will @@ -157,7 +157,7 @@ proc fixupDispatcher(meth, disp: PSym; conf: ConfigRef) = if disp.typ.lockLevel < meth.typ.lockLevel: disp.typ.lockLevel = meth.typ.lockLevel -proc methodDef*(g: ModuleGraph; s: PSym, fromCache: bool) = +proc methodDef*(g: ModuleGraph; idgen: IdGenerator; s: PSym, fromCache: bool) = var witness: PSym for i in 0..= 0: result = newNodeI(nkStmtList, n.info) for i in countdown(c.finallys.high, fin): - var vars = FreshVarsContext(tab: initTable[int, PSym](), config: c.config, info: n.info) + var vars = FreshVarsContext(tab: initTable[int, PSym](), config: c.config, info: n.info, idgen: c.idgen) result.add freshVars(preprocess(c, c.finallys[i]), vars) + c.idgen = vars.idgen result.add n of nkSkip: discard else: for i in 0 ..< n.len: result[i] = preprocess(c, n[i]) -proc transformClosureIterator*(g: ModuleGraph; fn: PSym, n: PNode): PNode = +proc transformClosureIterator*(g: ModuleGraph; idgen: IdGenerator; fn: PSym, n: PNode): PNode = var ctx: Ctx ctx.g = g ctx.fn = fn + ctx.idgen = idgen if getEnvParam(fn).isNil: # Lambda lifting was not done yet. Use temporary :state sym, which will # be handled specially by lambda lifting. Local temp vars (if needed) # should follow the same logic. - ctx.stateVarSym = newSym(skVar, getIdent(ctx.g.cache, ":state"), fn, fn.info) - ctx.stateVarSym.typ = g.createClosureIterStateType(fn) - ctx.stateLoopLabel = newSym(skLabel, getIdent(ctx.g.cache, ":stateLoop"), fn, fn.info) - var pc = PreprocessContext(finallys: @[], config: g.config) + ctx.stateVarSym = newSym(skVar, getIdent(ctx.g.cache, ":state"), nextId(idgen), fn, fn.info) + ctx.stateVarSym.typ = g.createClosureIterStateType(fn, idgen) + ctx.stateLoopLabel = newSym(skLabel, getIdent(ctx.g.cache, ":stateLoop"), nextId(idgen), fn, fn.info) + var pc = PreprocessContext(finallys: @[], config: g.config, idgen: idgen) var n = preprocess(pc, n.toStmtList) #echo "transformed into ", n #var n = n.toStmtList diff --git a/compiler/cmdlinehelper.nim b/compiler/cmdlinehelper.nim index 5fb8ab471..a415afce9 100644 --- a/compiler/cmdlinehelper.nim +++ b/compiler/cmdlinehelper.nim @@ -59,7 +59,7 @@ proc loadConfigsAndRunMainCommand*(self: NimProg, cache: IdentCache; conf: Confi graph: ModuleGraph): bool = if self.suggestMode: conf.command = "nimsuggest" - loadConfigs(DefaultConfig, cache, conf) # load all config files + loadConfigs(DefaultConfig, cache, conf, graph.idgen) # load all config files if not self.suggestMode: let scriptFile = conf.projectFull.changeFileExt("nims") diff --git a/compiler/depends.nim b/compiler/depends.nim index cc385da4b..7225b6b47 100644 --- a/compiler/depends.nim +++ b/compiler/depends.nim @@ -54,7 +54,7 @@ proc generateDot*(graph: ModuleGraph; project: AbsoluteFile) = when not defined(nimHasSinkInference): {.pragma: nosinks.} -proc myOpen(graph: ModuleGraph; module: PSym): PPassContext {.nosinks.} = +proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nosinks.} = var g: PGen new(g) g.module = module diff --git a/compiler/docgen2.nim b/compiler/docgen2.nim index 9cb40a7fc..cfbb33156 100644 --- a/compiler/docgen2.nim +++ b/compiler/docgen2.nim @@ -68,10 +68,10 @@ template myOpenImpl(ext: untyped) {.dirty.} = g.doc = d result = g -proc myOpen(graph: ModuleGraph; module: PSym): PPassContext = +proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext = myOpenImpl(HtmlExt) -proc myOpenJson(graph: ModuleGraph; module: PSym): PPassContext = +proc myOpenJson(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext = myOpenImpl(JsonExt) const docgen2Pass* = makePass(open = myOpen, process = processNode, close = close) diff --git a/compiler/enumtostr.nim b/compiler/enumtostr.nim index 3eb703ade..3274462d7 100644 --- a/compiler/enumtostr.nim +++ b/compiler/enumtostr.nim @@ -1,16 +1,16 @@ import ast, idents, lineinfos, modulegraphs, magicsys -proc genEnumToStrProc*(t: PType; info: TLineInfo; g: ModuleGraph): PSym = - result = newSym(skProc, getIdent(g.cache, "$"), t.owner, info) +proc genEnumToStrProc*(t: PType; info: TLineInfo; g: ModuleGraph; idgen: IdGenerator): PSym = + result = newSym(skProc, getIdent(g.cache, "$"), nextId idgen, t.owner, info) - let dest = newSym(skParam, getIdent(g.cache, "e"), result, info) + let dest = newSym(skParam, getIdent(g.cache, "e"), nextId idgen, result, info) dest.typ = t - let res = newSym(skResult, getIdent(g.cache, "result"), result, info) + let res = newSym(skResult, getIdent(g.cache, "result"), nextId idgen, result, info) res.typ = getSysType(g, info, tyString) - result.typ = newType(tyProc, t.owner) + result.typ = newType(tyProc, nextId idgen, t.owner) result.typ.n = newNodeI(nkFormalParams, info) rawAddSon(result.typ, res.typ) result.typ.n.add newNodeI(nkEffectList, info) @@ -62,16 +62,16 @@ proc searchObjCase(t: PType; field: PSym): PNode = result = searchObjCase(t[0].skipTypes({tyAlias, tyGenericInst, tyRef, tyPtr}), field) doAssert result != nil -proc genCaseObjDiscMapping*(t: PType; field: PSym; info: TLineInfo; g: ModuleGraph): PSym = - result = newSym(skProc, getIdent(g.cache, "objDiscMapping"), t.owner, info) +proc genCaseObjDiscMapping*(t: PType; field: PSym; info: TLineInfo; g: ModuleGraph; idgen: IdGenerator): PSym = + result = newSym(skProc, getIdent(g.cache, "objDiscMapping"), nextId idgen, t.owner, info) - let dest = newSym(skParam, getIdent(g.cache, "e"), result, info) + let dest = newSym(skParam, getIdent(g.cache, "e"), nextId idgen, result, info) dest.typ = field.typ - let res = newSym(skResult, getIdent(g.cache, "result"), result, info) + let res = newSym(skResult, getIdent(g.cache, "result"), nextId idgen, result, info) res.typ = getSysType(g, info, tyUInt8) - result.typ = newType(tyProc, t.owner) + result.typ = newType(tyProc, nextId idgen, t.owner) result.typ.n = newNodeI(nkFormalParams, info) rawAddSon(result.typ, res.typ) result.typ.n.add newNodeI(nkEffectList, info) diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim index c1024e2fe..218a597d8 100644 --- a/compiler/evaltempl.nim +++ b/compiler/evaltempl.nim @@ -22,6 +22,7 @@ type config: ConfigRef ic: IdentCache instID: int + idgen: IdGenerator proc copyNode(ctx: TemplCtx, a, b: PNode): PNode = result = copyNode(a) @@ -48,7 +49,7 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) = internalAssert c.config, sfGenSym in s.flags or s.kind == skType var x = PSym(idTableGet(c.mapping, s)) if x == nil: - x = copySym(s) + x = copySym(s, nextId(c.idgen)) # sem'check needs to set the owner properly later, see bug #9476 x.owner = nil # c.genSymOwner #if x.kind == skParam and x.owner.kind == skModule: @@ -168,6 +169,7 @@ proc wrapInComesFrom*(info: TLineInfo; sym: PSym; res: PNode): PNode = proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym; conf: ConfigRef; ic: IdentCache; instID: ref int; + idgen: IdGenerator; fromHlo=false): PNode = inc(conf.evalTemplateCounter) if conf.evalTemplateCounter > evalTemplateLimit: @@ -183,6 +185,7 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym; ctx.ic = ic initIdTable(ctx.mapping) ctx.instID = instID[] + ctx.idgen = idgen let body = tmpl.getBody #echo "instantion of ", renderTree(body, {renderIds}) diff --git a/compiler/guards.nim b/compiler/guards.nim index a6ca44978..4f07df201 100644 --- a/compiler/guards.nim +++ b/compiler/guards.nim @@ -993,8 +993,9 @@ proc addFactLt*(m: var TModel; a, b: PNode) = addFactLe(m, a, bb) proc settype(n: PNode): PType = - result = newType(tySet, n.typ.owner) - addSonSkipIntLit(result, n.typ) + result = newType(tySet, ItemId(module: -1, item: -1), n.typ.owner) + var idgen: IdGenerator + addSonSkipIntLit(result, n.typ, idgen) proc buildOf(it, loc: PNode; o: Operators): PNode = var s = newNodeI(nkCurly, it.info, it.len-1) diff --git a/compiler/hlo.nim b/compiler/hlo.nim index bb69a1477..af54cabbb 100644 --- a/compiler/hlo.nim +++ b/compiler/hlo.nim @@ -86,9 +86,9 @@ proc hlo(c: PContext, n: PNode): PNode = else: result = fitNode(c, n.typ, result, n.info) # optimization has been applied so check again: - result = commonOptimizations(c.graph, c.module, result) + result = commonOptimizations(c.graph, c.idgen, c.module, result) result = hlo(c, result) - result = commonOptimizations(c.graph, c.module, result) + result = commonOptimizations(c.graph, c.idgen, c.module, result) proc hloBody(c: PContext, n: PNode): PNode = # fast exit: diff --git a/compiler/idents.nim b/compiler/idents.nim index a035974f3..b621503ec 100644 --- a/compiler/idents.nim +++ b/compiler/idents.nim @@ -15,12 +15,9 @@ import hashes, wordrecg type - TIdObj* = object of RootObj - id*: int # unique id; use this for comparisons and not the pointers - - PIdObj* = ref TIdObj PIdent* = ref TIdent - TIdent*{.acyclic.} = object of TIdObj + TIdent*{.acyclic.} = object + id*: int # unique id; use this for comparisons and not the pointers s*: string next*: PIdent # for hash-table chaining h*: Hash # hash value of s diff --git a/compiler/idgen.nim b/compiler/idgen.nim deleted file mode 100644 index 7d49e33e3..000000000 --- a/compiler/idgen.nim +++ /dev/null @@ -1,59 +0,0 @@ -# -# -# The Nim Compiler -# (c) Copyright 2012 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## This module contains a simple persistent id generator. - -import idents, strutils, options, pathutils - -var gFrontEndId*: int - -const - debugIds* = false - -when debugIds: - import intsets - - var usedIds = initIntSet() - -proc registerID*(id: PIdObj) = - when debugIds: - if id.id == -1 or containsOrIncl(usedIds, id.id): - internalError("ID already used: " & $id.id) - -proc getID*(): int {.inline.} = - result = gFrontEndId - inc(gFrontEndId) - -proc setId*(id: int) {.inline.} = - gFrontEndId = max(gFrontEndId, id + 1) - -proc idSynchronizationPoint*(idRange: int) = - gFrontEndId = (gFrontEndId div idRange + 1) * idRange + 1 - -proc toGid(conf: ConfigRef; f: AbsoluteFile): string = - # we used to use ``f.addFileExt("gid")`` (aka ``$project.gid``), but this - # will cause strange bugs if multiple projects are in the same folder, so - # we simply use a project independent name: - result = options.completeGeneratedFilePath(conf, AbsoluteFile"nim.gid").string - -proc saveMaxIds*(conf: ConfigRef; project: AbsoluteFile) = - var f = open(toGid(conf, project), fmWrite) - f.writeLine($gFrontEndId) - f.close() - -proc loadMaxIds*(conf: ConfigRef; project: AbsoluteFile) = - var f: File - if open(f, toGid(conf, project), fmRead): - var line = newStringOfCap(20) - if f.readLine(line): - var frontEndId = parseInt(line) - if f.readLine(line): - # var backEndId = parseInt(line) - gFrontEndId = max(gFrontEndId, frontEndId) - f.close() diff --git a/compiler/importer.nim b/compiler/importer.nim index 34e76d7d9..6722eab15 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -136,7 +136,7 @@ proc importModuleAs(c: PContext; n: PNode, realModule: PSym): PSym = localError(c.config, n.info, "module alias must be an identifier") elif n[1].ident.id != realModule.name.id: # some misguided guy will write 'import abc.foo as foo' ... - result = createModuleAlias(realModule, n[1].ident, realModule.info, + result = createModuleAlias(realModule, nextId c.idgen, n[1].ident, realModule.info, c.config.options) proc myImportModule(c: PContext, n: PNode; importStmtResult: PNode): PSym = diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index ea90299e2..0b893ede8 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -40,6 +40,7 @@ type inLoop, inSpawn: int uninit: IntSet # set of uninit'ed vars uninitComputed: bool + idgen: IdGenerator ProcessMode = enum normal @@ -61,7 +62,7 @@ template dbg(body) = body proc getTemp(c: var Con; s: var Scope; typ: PType; info: TLineInfo): PNode = - let sym = newSym(skTemp, getIdent(c.graph.cache, ":tmpD"), c.owner, info) + let sym = newSym(skTemp, getIdent(c.graph.cache, ":tmpD"), nextId c.idgen, c.owner, info) sym.typ = typ s.vars.add(sym) result = newSymNode(sym) @@ -250,16 +251,16 @@ proc checkForErrorPragma(c: Con; t: PType; ri: PNode; opname: string) = m.add c.owner.name.s localError(c.graph.config, ri.info, errGenerated, m) -proc makePtrType(c: Con, baseType: PType): PType = - result = newType(tyPtr, c.owner) - addSonSkipIntLit(result, baseType) +proc makePtrType(c: var Con, baseType: PType): PType = + result = newType(tyPtr, nextId c.idgen, c.owner) + addSonSkipIntLit(result, baseType, c.idgen) -proc genOp(c: Con; op: PSym; dest: PNode): PNode = +proc genOp(c: var Con; op: PSym; dest: PNode): PNode = let addrExp = newNodeIT(nkHiddenAddr, dest.info, makePtrType(c, dest.typ)) addrExp.add(dest) result = newTree(nkCall, newSymNode(op), addrExp) -proc genOp(c: Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode = +proc genOp(c: var Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode = var op = t.attachedOps[kind] if op == nil or op.ast[genericParamsPos].kind != nkEmpty: # give up and find the canonical type instead: @@ -280,7 +281,7 @@ proc genOp(c: Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode = if sfError in op.flags: checkForErrorPragma(c, t, ri, AttachedOpToStr[kind]) c.genOp(op, dest) -proc genDestroy(c: Con; dest: PNode): PNode = +proc genDestroy(c: var Con; dest: PNode): PNode = let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink}) result = c.genOp(t, attachedDestructor, dest, nil) @@ -310,7 +311,7 @@ proc genSink(c: var Con; dest, ri: PNode, isDecl = false): PNode = # and copyMem(dest, source). This is efficient. result = newTree(nkStmtList, c.genDestroy(dest), newTree(nkFastAsgn, dest, ri)) -proc genCopyNoCheck(c: Con; dest, ri: PNode): PNode = +proc genCopyNoCheck(c: var Con; dest, ri: PNode): PNode = let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink}) result = c.genOp(t, attachedAsgn, dest, ri) @@ -346,7 +347,7 @@ It is best to factor out piece of object that needs custom destructor into separ return # generate: if le != tmp: `=destroy`(le) - let branchDestructor = produceDestructorForDiscriminator(c.graph, objType, leDotExpr[1].sym, n.info) + let branchDestructor = produceDestructorForDiscriminator(c.graph, objType, leDotExpr[1].sym, n.info, c.idgen) let cond = newNodeIT(nkInfix, n.info, getSysType(c.graph, unknownLineInfo, tyBool)) cond.add newSymNode(getMagicEqSymForType(c.graph, le.typ, n.info)) cond.add le @@ -377,7 +378,7 @@ proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): PNode = else: result = newNodeIT(nkStmtListExpr, n.info, n.typ) - var temp = newSym(skLet, getIdent(c.graph.cache, "blitTmp"), c.owner, n.info) + var temp = newSym(skLet, getIdent(c.graph.cache, "blitTmp"), nextId c.idgen, c.owner, n.info) temp.typ = n.typ var v = newNodeI(nkLetSection, n.info) let tempAsNode = newSymNode(temp) @@ -799,7 +800,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode = for it in n: var ri = it[^1] if it.kind == nkVarTuple and hasDestructor(c, ri.typ): - let x = lowerTupleUnpacking(c.graph, it, c.owner) + let x = lowerTupleUnpacking(c.graph, it, c.idgen, c.owner) result.add p(x, c, s, consumed) elif it.kind == nkIdentDefs and hasDestructor(c, it[0].typ) and not isCursor(it[0], c): for j in 0..> $2)", "($1 >> $2)") else: @@ -1804,7 +1804,7 @@ proc genVarStmt(p: PProc, n: PNode) = var a = n[i] if a.kind != nkCommentStmt: if a.kind == nkVarTuple: - let unpacked = lowerTupleUnpacking(p.module.graph, a, p.prc) + let unpacked = lowerTupleUnpacking(p.module.graph, a, p.module.idgen, p.prc) genStmt(p, unpacked) else: assert(a.kind == nkIdentDefs) @@ -2316,9 +2316,9 @@ proc genProc(oldProc: PProc, prc: PSym): Rope = else: returnStmt = "return $#;$n" % [a.res] - var transformedBody = transformBody(oldProc.module.graph, prc, cache = false) + var transformedBody = transformBody(p.module.graph, p.module.idgen, prc, cache = false) if sfInjectDestructors in prc.flags: - transformedBody = injectDestructorCalls(oldProc.module.graph, prc, transformedBody) + transformedBody = injectDestructorCalls(p.module.graph, p.module.idgen, prc, transformedBody) p.nested: genStmt(p, transformedBody) @@ -2607,9 +2607,9 @@ proc genModule(p: PProc, n: PNode) = p.body.add(frameCreate(p, makeJSString("module " & p.module.module.name.s), makeJSString(toFilenameOption(p.config, p.module.module.info.fileIndex, foStacktrace)))) - var transformedN = transformStmt(p.module.graph, p.module.module, n) + var transformedN = transformStmt(p.module.graph, p.module.idgen, p.module.module, n) if sfInjectDestructors in p.module.module.flags: - transformedN = injectDestructorCalls(p.module.graph, p.module.module, transformedN) + transformedN = injectDestructorCalls(p.module.graph, p.module.idgen, p.module.module, transformedN) if p.config.hcrOn and n.kind == nkStmtList: let moduleSym = p.module.module var moduleLoadedVar = rope(moduleSym.name.s) & "_loaded" & @@ -2686,7 +2686,9 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = writeFile(outFile.string & ".map", $(%map)) discard writeRopeIfNotEqual(code, outFile) -proc myOpen(graph: ModuleGraph; s: PSym): PPassContext = + +proc myOpen(graph: ModuleGraph; s: PSym; idgen: IdGenerator): PPassContext = result = newModule(graph, s) + result.idgen = idgen const JSgenPass* = makePass(myOpen, myProcess, myClose) diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index c23ca52cb..b552d9455 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -126,32 +126,32 @@ proc newCall(a: PSym, b: PNode): PNode = result.add newSymNode(a) result.add b -proc createClosureIterStateType*(g: ModuleGraph; iter: PSym): PType = +proc createClosureIterStateType*(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PType = var n = newNodeI(nkRange, iter.info) n.add newIntNode(nkIntLit, -1) n.add newIntNode(nkIntLit, 0) - result = newType(tyRange, iter) + result = newType(tyRange, nextId(idgen), iter) result.n = n var intType = nilOrSysInt(g) - if intType.isNil: intType = newType(tyInt, iter) + if intType.isNil: intType = newType(tyInt, nextId(idgen), iter) rawAddSon(result, intType) -proc createStateField(g: ModuleGraph; iter: PSym): PSym = - result = newSym(skField, getIdent(g.cache, ":state"), iter, iter.info) - result.typ = createClosureIterStateType(g, iter) +proc createStateField(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PSym = + result = newSym(skField, getIdent(g.cache, ":state"), nextId(idgen), iter, iter.info) + result.typ = createClosureIterStateType(g, iter, idgen) -proc createEnvObj(g: ModuleGraph; owner: PSym; info: TLineInfo): PType = +proc createEnvObj(g: ModuleGraph; idgen: IdGenerator; owner: PSym; info: TLineInfo): PType = # YYY meh, just add the state field for every closure for now, it's too # hard to figure out if it comes from a closure iterator: - result = createObj(g, owner, info, final=false) - rawAddField(result, createStateField(g, owner)) + result = createObj(g, idgen, owner, info, final=false) + rawAddField(result, createStateField(g, owner, idgen)) -proc getClosureIterResult*(g: ModuleGraph; iter: PSym): PSym = +proc getClosureIterResult*(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PSym = if resultPos < iter.ast.len: result = iter.ast[resultPos].sym else: # XXX a bit hacky: - result = newSym(skResult, getIdent(g.cache, ":result"), iter, iter.info, {}) + result = newSym(skResult, getIdent(g.cache, ":result"), nextId(idgen), iter, iter.info, {}) result.typ = iter.typ[0] incl(result.flags, sfUsed) iter.ast.add newSymNode(result) @@ -209,7 +209,7 @@ proc newAsgnStmt(le, ri: PNode, info: TLineInfo): PNode = result[0] = le result[1] = ri -proc makeClosure*(g: ModuleGraph; prc: PSym; env: PNode; info: TLineInfo): PNode = +proc makeClosure*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; env: PNode; info: TLineInfo): PNode = result = newNodeIT(nkClosure, info, prc.typ) result.add(newSymNode(prc)) if env == nil: @@ -219,7 +219,7 @@ proc makeClosure*(g: ModuleGraph; prc: PSym; env: PNode; info: TLineInfo): PNode localError(g.config, info, "internal error: taking closure of closure") result.add(env) #if isClosureIterator(result.typ): - createTypeBoundOps(g, nil, result.typ, info) + createTypeBoundOps(g, nil, result.typ, info, idgen) if tfHasAsgn in result.typ.flags or optSeqDestructors in g.config.globalOptions: prc.flags.incl sfInjectDestructors @@ -237,13 +237,13 @@ proc liftingHarmful(conf: ConfigRef; owner: PSym): bool {.inline.} = let isCompileTime = sfCompileTime in owner.flags or owner.kind == skMacro result = conf.backend == backendJs and not isCompileTime -proc createTypeBoundOpsLL(g: ModuleGraph; refType: PType; info: TLineInfo; owner: PSym) = - createTypeBoundOps(g, nil, refType.lastSon, info) - createTypeBoundOps(g, nil, refType, info) +proc createTypeBoundOpsLL(g: ModuleGraph; refType: PType; info: TLineInfo; idgen: IdGenerator; owner: PSym) = + createTypeBoundOps(g, nil, refType.lastSon, info, idgen) + createTypeBoundOps(g, nil, refType, info, idgen) if tfHasAsgn in refType.flags or optSeqDestructors in g.config.globalOptions: owner.flags.incl sfInjectDestructors -proc liftIterSym*(g: ModuleGraph; n: PNode; owner: PSym): PNode = +proc liftIterSym*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode = # transforms (iter) to (let env = newClosure[iter](); (iter, env)) if liftingHarmful(g.config, owner): return n let iter = n.sym @@ -255,10 +255,10 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; owner: PSym): PNode = var env: PNode if owner.isIterator: let it = getHiddenParam(g, owner) - addUniqueField(it.typ.skipTypes({tyOwned})[0], hp, g.cache) + addUniqueField(it.typ.skipTypes({tyOwned})[0], hp, g.cache, idgen) env = indirectAccess(newSymNode(it), hp, hp.info) else: - let e = newSym(skLet, iter.name, owner, n.info) + let e = newSym(skLet, iter.name, nextId(idgen), owner, n.info) e.typ = hp.typ e.flags = hp.flags env = newSymNode(e) @@ -267,13 +267,13 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; owner: PSym): PNode = result.add(v) # add 'new' statement: result.add newCall(getSysSym(g, n.info, "internalNew"), env) - createTypeBoundOpsLL(g, env.typ, n.info, owner) - result.add makeClosure(g, iter, env, n.info) + createTypeBoundOpsLL(g, env.typ, n.info, idgen, owner) + result.add makeClosure(g, idgen, iter, env, n.info) -proc freshVarForClosureIter*(g: ModuleGraph; s, owner: PSym): PNode = +proc freshVarForClosureIter*(g: ModuleGraph; s: PSym; idgen: IdGenerator; owner: PSym): PNode = let envParam = getHiddenParam(g, owner) let obj = envParam.typ.skipTypes({tyOwned, tyRef, tyPtr}) - addField(obj, s, g.cache) + addField(obj, s, g.cache, idgen) var access = newSymNode(envParam) assert obj.kind == tyObject @@ -305,13 +305,15 @@ type ownerToType: Table[int, PType] somethingToDo: bool graph: ModuleGraph + idgen: IdGenerator -proc initDetectionPass(g: ModuleGraph; fn: PSym): DetectionPass = +proc initDetectionPass(g: ModuleGraph; fn: PSym; idgen: IdGenerator): DetectionPass = result.processed = initIntSet() result.capturedVars = initIntSet() result.ownerToType = initTable[int, PType]() result.processed.incl(fn.id) result.graph = g + result.idgen = idgen discard """ proc outer = @@ -327,15 +329,15 @@ proc getEnvTypeForOwner(c: var DetectionPass; owner: PSym; info: TLineInfo): PType = result = c.ownerToType.getOrDefault(owner.id) if result.isNil: - result = newType(tyRef, owner) - let obj = createEnvObj(c.graph, owner, info) + result = newType(tyRef, nextId(c.idgen), owner) + let obj = createEnvObj(c.graph, c.idgen, owner, info) rawAddSon(result, obj) c.ownerToType[owner.id] = result -proc asOwnedRef(c: DetectionPass; t: PType): PType = +proc asOwnedRef(c: var DetectionPass; t: PType): PType = if optOwnedRefs in c.graph.config.globalOptions: assert t.kind == tyRef - result = newType(tyOwned, t.owner) + result = newType(tyOwned, nextId(c.idgen), t.owner) result.flags.incl tfHasOwned result.rawAddSon t else: @@ -344,7 +346,7 @@ proc asOwnedRef(c: DetectionPass; t: PType): PType = proc getEnvTypeForOwnerUp(c: var DetectionPass; owner: PSym; info: TLineInfo): PType = var r = c.getEnvTypeForOwner(owner, info) - result = newType(tyPtr, owner) + result = newType(tyPtr, nextId(c.idgen), owner) rawAddSon(result, r.skipTypes({tyOwned, tyRef, tyPtr})) proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) = @@ -372,7 +374,7 @@ proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) = if c.graph.config.selectedGC == gcDestructors and sfCursor notin upField.flags: localError(c.graph.config, dep.info, "internal error: up reference is not a .cursor") else: - let result = newSym(skField, upIdent, obj.owner, obj.owner.info) + let result = newSym(skField, upIdent, nextId(c.idgen), obj.owner, obj.owner.info) result.typ = fieldType when false: if c.graph.config.selectedGC == gcDestructors: @@ -410,7 +412,7 @@ proc addClosureParam(c: var DetectionPass; fn: PSym; info: TLineInfo) = let owner = if fn.kind == skIterator: fn else: fn.skipGenericOwner let t = c.getEnvTypeForOwner(owner, info) if cp == nil: - cp = newSym(skParam, getIdent(c.graph.cache, paramName), fn, fn.info) + cp = newSym(skParam, getIdent(c.graph.cache, paramName), nextId(c.idgen), fn, fn.info) incl(cp.flags, sfFromGeneric) cp.typ = t addHiddenParam(fn, cp) @@ -433,7 +435,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) = if innerProc: if s.isIterator: c.somethingToDo = true if not c.processed.containsOrIncl(s.id): - let body = transformBody(c.graph, s, cache = true) + let body = transformBody(c.graph, c.idgen, s, cache = true) detectCapturedVars(body, s, c) let ow = s.skipGenericOwner if ow == owner: @@ -446,9 +448,9 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) = #let obj = c.getEnvTypeForOwner(s.owner).skipTypes({tyOwned, tyRef, tyPtr}) if s.name.id == getIdent(c.graph.cache, ":state").id: - obj.n[0].sym.id = -s.id + obj.n[0].sym.itemId = ItemId(module: s.itemId.module, item: -s.itemId.item) else: - addField(obj, s, c.graph.cache) + addField(obj, s, c.graph.cache, c.idgen) # direct or indirect dependency: elif (innerProc and s.typ.callConv == ccClosure) or interestingVar(s): discard """ @@ -470,7 +472,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) = if interestingVar(s) and not c.capturedVars.containsOrIncl(s.id): let obj = c.getEnvTypeForOwner(ow, n.info).skipTypes({tyOwned, tyRef, tyPtr}) #getHiddenParam(owner).typ.skipTypes({tyOwned, tyRef, tyPtr}) - addField(obj, s, c.graph.cache) + addField(obj, s, c.graph.cache, c.idgen) # create required upFields: var w = owner.skipGenericOwner if isInnerProc(w) or owner.isIterator: @@ -535,8 +537,8 @@ proc accessViaEnvParam(g: ModuleGraph; n: PNode; owner: PSym): PNode = localError(g.config, n.info, "internal error: environment misses: " & s.name.s) result = n -proc newEnvVar(cache: IdentCache; owner: PSym; typ: PType; info: TLineInfo): PNode = - var v = newSym(skVar, getIdent(cache, envName), owner, info) +proc newEnvVar(cache: IdentCache; owner: PSym; typ: PType; info: TLineInfo; idgen: IdGenerator): PNode = + var v = newSym(skVar, getIdent(cache, envName), nextId(idgen), owner, info) v.flags = {sfShadowed, sfGeneratedOp} v.typ = typ result = newSymNode(v) @@ -548,7 +550,7 @@ proc newEnvVar(cache: IdentCache; owner: PSym; typ: PType; info: TLineInfo): PNo else: result = newSymNode(v) -proc setupEnvVar(owner: PSym; d: DetectionPass; +proc setupEnvVar(owner: PSym; d: var DetectionPass; c: var LiftingPass; info: TLineInfo): PNode = if owner.isIterator: return getHiddenParam(d.graph, owner).newSymNode @@ -557,10 +559,10 @@ proc setupEnvVar(owner: PSym; d: DetectionPass; let envVarType = d.ownerToType.getOrDefault(owner.id) if envVarType.isNil: localError d.graph.config, owner.info, "internal error: could not determine closure type" - result = newEnvVar(d.graph.cache, owner, asOwnedRef(d, envVarType), info) + result = newEnvVar(d.graph.cache, owner, asOwnedRef(d, envVarType), info, d.idgen) c.envVars[owner.id] = result if optOwnedRefs in d.graph.config.globalOptions: - var v = newSym(skVar, getIdent(d.graph.cache, envName & "Alt"), owner, info) + var v = newSym(skVar, getIdent(d.graph.cache, envName & "Alt"), nextId d.idgen, owner, info) v.flags = {sfShadowed, sfGeneratedOp} v.typ = envVarType c.unownedEnvVars[owner.id] = newSymNode(v) @@ -576,7 +578,7 @@ proc getUpViaParam(g: ModuleGraph; owner: PSym): PNode = result = rawIndirectAccess(result, upField, p.info) proc rawClosureCreation(owner: PSym; - d: DetectionPass; c: var LiftingPass; + d: var DetectionPass; c: var LiftingPass; info: TLineInfo): PNode = result = newNodeI(nkStmtList, owner.info) @@ -602,7 +604,7 @@ proc rawClosureCreation(owner: PSym; let env2 = copyTree(env) env2.typ = unowned.typ result.add newAsgnStmt(unowned, env2, env.info) - createTypeBoundOpsLL(d.graph, unowned.typ, env.info, owner) + createTypeBoundOpsLL(d.graph, unowned.typ, env.info, d.idgen, owner) # add assignment statements for captured parameters: for i in 1..= 0 or isDefined(graph.config, "nimBackendAssumesChange") -proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream): bool {.discardable.} = +proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator; + stream: PLLStream): bool {.discardable.} = if graph.stopCompile(): return true var p: Parser @@ -135,7 +123,7 @@ proc processModule*(graph: ModuleGraph; module: PSym, stream: PLLStream): bool { # new module caching mechanism: for i in 0.. evalTemplateLimit: globalError(c.config, s.info, "template instantiation too nested") c.friendModules.add(s.owner.getModule) - idSynchronizationPoint(5000) result = macroResult resetSemFlag result if s.typ[0] == nil: @@ -469,7 +468,7 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, #if c.evalContext == nil: # c.evalContext = c.createEvalContext(emStatic) - result = evalMacroCall(c.module, c.graph, c.templInstCounter, n, nOrig, sym) + result = evalMacroCall(c.module, c.idgen, c.graph, c.templInstCounter, n, nOrig, sym) if efNoSemCheck notin flags: result = semAfterMacroCall(c, n, result, sym, flags) if c.config.macrosToExpand.hasKey(sym.name.s): @@ -502,8 +501,9 @@ proc addCodeForGenerics(c: PContext, n: PNode) = n.add prc.ast c.lastGenericIdx = c.generics.len -proc myOpen(graph: ModuleGraph; module: PSym): PPassContext {.nosinks.} = +proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nosinks.} = var c = newContext(graph, module) + c.idgen = idgen if c.p != nil: internalError(graph.config, module.info, "sem.myOpen") c.semConstExpr = semConstExpr c.semExpr = semExpr diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 50df1085f..dd2398f87 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -688,9 +688,9 @@ proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): PSym = var x: PType if param.typ.kind == tyVar: x = newTypeS(param.typ.kind, c) - x.addSonSkipIntLit t.baseOfDistinct + x.addSonSkipIntLit(t.baseOfDistinct(c.idgen), c.idgen) else: - x = t.baseOfDistinct + x = t.baseOfDistinct(c.idgen) call.add(newNodeIT(nkEmpty, fn.info, x)) if hasDistinct: let filter = if fn.kind in {skProc, skFunc}: {skProc, skFunc} else: {fn.kind} diff --git a/compiler/semdata.nim b/compiler/semdata.nim index b15f65967..8d1c25e4d 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -277,14 +277,14 @@ proc addToLib*(lib: PLib, sym: PSym) = sym.annex = lib proc newTypeS*(kind: TTypeKind, c: PContext): PType = - result = newType(kind, getCurrOwner(c)) + result = newType(kind, nextId(c.idgen), getCurrOwner(c)) -proc makePtrType*(owner: PSym, baseType: PType): PType = - result = newType(tyPtr, owner) - addSonSkipIntLit(result, baseType) +proc makePtrType*(owner: PSym, baseType: PType; idgen: IdGenerator): PType = + result = newType(tyPtr, nextId(idgen), owner) + addSonSkipIntLit(result, baseType, idgen) proc makePtrType*(c: PContext, baseType: PType): PType = - makePtrType(getCurrOwner(c), baseType) + makePtrType(getCurrOwner(c), baseType, c.idgen) proc makeTypeWithModifier*(c: PContext, modifier: TTypeKind, @@ -295,21 +295,21 @@ proc makeTypeWithModifier*(c: PContext, result = baseType else: result = newTypeS(modifier, c) - addSonSkipIntLit(result, baseType) + addSonSkipIntLit(result, baseType, c.idgen) proc makeVarType*(c: PContext, baseType: PType; kind = tyVar): PType = if baseType.kind == kind: result = baseType else: result = newTypeS(kind, c) - addSonSkipIntLit(result, baseType) + addSonSkipIntLit(result, baseType, c.idgen) -proc makeVarType*(owner: PSym, baseType: PType; kind = tyVar): PType = +proc makeVarType*(owner: PSym, baseType: PType; idgen: IdGenerator; kind = tyVar): PType = if baseType.kind == kind: result = baseType else: - result = newType(kind, owner) - addSonSkipIntLit(result, baseType) + result = newType(kind, nextId(idgen), owner) + addSonSkipIntLit(result, baseType, idgen) proc makeTypeDesc*(c: PContext, typ: PType): PType = if typ.kind == tyTypeDesc: @@ -317,14 +317,14 @@ proc makeTypeDesc*(c: PContext, typ: PType): PType = else: result = newTypeS(tyTypeDesc, c) incl result.flags, tfCheckedForDestructor - result.addSonSkipIntLit(typ) + result.addSonSkipIntLit(typ, c.idgen) proc makeTypeSymNode*(c: PContext, typ: PType, info: TLineInfo): PNode = let typedesc = newTypeS(tyTypeDesc, c) incl typedesc.flags, tfCheckedForDestructor internalAssert(c.config, typ != nil) - typedesc.addSonSkipIntLit(typ) - let sym = newSym(skType, c.cache.idAnon, getCurrOwner(c), info, + typedesc.addSonSkipIntLit(typ, c.idgen) + let sym = newSym(skType, c.cache.idAnon, nextId(c.idgen), getCurrOwner(c), info, c.config.options).linkTo(typedesc) return newSymNode(sym, info) @@ -333,13 +333,14 @@ proc makeTypeFromExpr*(c: PContext, n: PNode): PType = assert n != nil result.n = n -proc newTypeWithSons*(owner: PSym, kind: TTypeKind, sons: seq[PType]): PType = - result = newType(kind, owner) +proc newTypeWithSons*(owner: PSym, kind: TTypeKind, sons: seq[PType]; + idgen: IdGenerator): PType = + result = newType(kind, nextId(idgen), owner) result.sons = sons proc newTypeWithSons*(c: PContext, kind: TTypeKind, sons: seq[PType]): PType = - result = newType(kind, getCurrOwner(c)) + result = newType(kind, nextId(c.idgen), getCurrOwner(c)) result.sons = sons proc makeStaticExpr*(c: PContext, n: PNode): PNode = @@ -418,7 +419,7 @@ proc makeRangeType*(c: PContext; first, last: BiggestInt; n.add newIntTypeNode(last, intType) result = newTypeS(tyRange, c) result.n = n - addSonSkipIntLit(result, intType) # basetype of range + addSonSkipIntLit(result, intType, c.idgen) # basetype of range proc markIndirect*(c: PContext, s: PSym) {.inline.} = if s.kind in {skProc, skFunc, skConverter, skMethod, skIterator}: diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index cb852c68a..2de25dadb 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -33,7 +33,8 @@ proc semTemplateExpr(c: PContext, n: PNode, s: PSym, # Note: This is n.info on purpose. It prevents template from creating an info # context when called from an another template pushInfoContext(c.config, n.info, s.detailedInfo) - result = evalTemplate(n, s, getCurrOwner(c), c.config, c.cache, c.templInstCounter, efFromHlo in flags) + result = evalTemplate(n, s, getCurrOwner(c), c.config, c.cache, + c.templInstCounter, c.idgen, efFromHlo in flags) if efNoSemCheck notin flags: result = semAfterMacroCall(c, n, result, s, flags) popInfoContext(c.config) @@ -384,7 +385,7 @@ proc fixupStaticType(c: PContext, n: PNode) = # apply this measure only in code that is enlightened to work # with static types. if n.typ.kind != tyStatic: - n.typ = newTypeWithSons(getCurrOwner(c), tyStatic, @[n.typ]) + n.typ = newTypeWithSons(getCurrOwner(c), tyStatic, @[n.typ], c.idgen) n.typ.n = n # XXX: cycles like the one here look dangerous. # Consider using `n.copyTree` @@ -555,7 +556,7 @@ proc arrayConstrType(c: PContext, n: PNode): PType = rawAddSon(typ, newTypeS(tyEmpty, c)) # needs an empty basetype! else: var t = skipTypes(n[0].typ, {tyGenericInst, tyVar, tyLent, tyOrdinal, tyAlias, tySink}) - addSonSkipIntLit(typ, t) + addSonSkipIntLit(typ, t, c.idgen) typ[0] = makeRangeType(c, 0, n.len - 1, n.info) result = typ @@ -605,11 +606,11 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = let xx = semExprWithType(c, x, flags*{efAllowDestructor}) result.add xx - typ = commonType(typ, xx.typ) + typ = commonType(c, typ, xx.typ) #n[i] = semExprWithType(c, x, flags*{efAllowDestructor}) #result.add fitNode(c, typ, n[i]) inc(lastIndex) - addSonSkipIntLit(result.typ, typ) + addSonSkipIntLit(result.typ, typ, c.idgen) for i in 0.. MaxSetElements: typ = makeRangeType(c, 0, MaxSetElements-1, n.info) - addSonSkipIntLit(result.typ, typ) + addSonSkipIntLit(result.typ, typ, c.idgen) for i in 0.. 2: - b = getConstExpr(m, n[2], g) + b = getConstExpr(m, n[2], idgen, g) if b == nil: return if n.len > 3: - c = getConstExpr(m, n[3], g) + c = getConstExpr(m, n[3], idgen, g) if c == nil: return result = evalOp(s.magic, n, a, b, c, g) @@ -451,19 +412,19 @@ proc foldConv(n, a: PNode; g: ModuleGraph; check = false): PNode = result = a result.typ = n.typ -proc getArrayConstr(m: PSym, n: PNode; g: ModuleGraph): PNode = +proc getArrayConstr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode = if n.kind == nkBracket: result = n else: - result = getConstExpr(m, n, g) + result = getConstExpr(m, n, idgen, g) if result == nil: result = n -proc foldArrayAccess(m: PSym, n: PNode; g: ModuleGraph): PNode = - var x = getConstExpr(m, n[0], g) +proc foldArrayAccess(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode = + var x = getConstExpr(m, n[0], idgen, g) if x == nil or x.typ.skipTypes({tyGenericInst, tyAlias, tySink}).kind == tyTypeDesc: return - var y = getConstExpr(m, n[1], g) + var y = getConstExpr(m, n[1], idgen, g) if y == nil: return var idx = toInt64(getOrdValue(y)) @@ -486,9 +447,9 @@ proc foldArrayAccess(m: PSym, n: PNode; g: ModuleGraph): PNode = localError(g.config, n.info, formatErrorIndexBound(idx, x.strVal.len-1) & $n) else: discard -proc foldFieldAccess(m: PSym, n: PNode; g: ModuleGraph): PNode = +proc foldFieldAccess(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode = # a real field access; proc calls have already been transformed - var x = getConstExpr(m, n[0], g) + var x = getConstExpr(m, n[0], idgen, g) if x == nil or x.kind notin {nkObjConstr, nkPar, nkTupleConstr}: return var field = n[1].sym @@ -504,23 +465,23 @@ proc foldFieldAccess(m: PSym, n: PNode; g: ModuleGraph): PNode = return localError(g.config, n.info, "field not found: " & field.name.s) -proc foldConStrStr(m: PSym, n: PNode; g: ModuleGraph): PNode = +proc foldConStrStr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode = result = newNodeIT(nkStrLit, n.info, n.typ) result.strVal = "" for i in 1.. 2 and operand2.kind == tyGenericParam): return traitCall ## too early to evaluate @@ -160,19 +160,19 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym) result.info = traitCall.info of "arity": result = newIntNode(nkIntLit, operand.len - ord(operand.kind==tyProc)) - result.typ = newType(tyInt, context) + result.typ = newType(tyInt, nextId c.idgen, context) result.info = traitCall.info of "genericHead": var arg = operand case arg.kind of tyGenericInst: - result = getTypeDescNode(arg.base, operand.owner, traitCall.info) + result = getTypeDescNode(c, arg.base, operand.owner, traitCall.info) # of tySequence: # this doesn't work # var resType = newType(tySequence, operand.owner) # result = toNode(resType, traitCall.info) # doesn't work yet else: localError(c.config, traitCall.info, "expected generic type, got: type $2 of kind $1" % [arg.kind.toHumanStr, typeToString(operand)]) - result = newType(tyError, context).toNode(traitCall.info) + result = newType(tyError, nextId c.idgen, context).toNode(traitCall.info) of "stripGenericParams": result = uninstantiate(operand).toNode(traitCall.info) of "supportsCopyMem": @@ -194,11 +194,11 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym) while arg.kind == tyDistinct: arg = arg.base arg = arg.skipTypes(skippedTypes + {tyGenericInst}) - result = getTypeDescNode(arg, operand.owner, traitCall.info) + result = getTypeDescNode(c, arg, operand.owner, traitCall.info) else: localError(c.config, traitCall.info, "distinctBase expects a distinct type as argument. The given type was " & typeToString(operand)) - result = newType(tyError, context).toNode(traitCall.info) + result = newType(tyError, nextId c.idgen, context).toNode(traitCall.info) else: localError(c.config, traitCall.info, "unknown trait: " & s) result = newNodeI(nkEmpty, traitCall.info) @@ -286,7 +286,7 @@ proc semDynamicBindSym(c: PContext, n: PNode): PNode = return semBindSym(c, n) if c.graph.vm.isNil: - setupGlobalCtx(c.module, c.graph) + setupGlobalCtx(c.module, c.graph, c.idgen) let vm = PCtx c.graph.vm @@ -370,7 +370,7 @@ proc semUnown(c: PContext; n: PNode): PNode = elems[i] = unownedType(c, t[i]) if elems[i] != t[i]: someChange = true if someChange: - result = newType(tyTuple, t.owner) + result = newType(tyTuple, nextId c.idgen, t.owner) # we have to use 'rawAddSon' here so that type flags are # properly computed: for e in elems: result.rawAddSon(e) @@ -381,7 +381,7 @@ proc semUnown(c: PContext; n: PNode): PNode = tyGenericInst, tyAlias: let b = unownedType(c, t[^1]) if b != t[^1]: - result = copyType(t, t.owner, keepId = false) + result = copyType(t, nextId c.idgen, t.owner) result[^1] = b result.flags.excl tfHasOwned else: @@ -399,7 +399,7 @@ proc turnFinalizerIntoDestructor(c: PContext; orig: PSym; info: TLineInfo): PSym # Replace nkDerefExpr by nkHiddenDeref # nkDeref is for 'ref T': x[].field # nkHiddenDeref is for 'var T': x.field - proc transform(procSym: PSym; n: PNode; old, fresh: PType; oldParam, newParam: PSym): PNode = + proc transform(c: PContext; procSym: PSym; n: PNode; old, fresh: PType; oldParam, newParam: PSym): PNode = result = shallowCopy(n) if sameTypeOrNil(n.typ, old): result.typ = fresh @@ -407,26 +407,26 @@ proc turnFinalizerIntoDestructor(c: PContext; orig: PSym; info: TLineInfo): PSym if n.sym == oldParam: result.sym = newParam elif n.sym.owner == orig: - result.sym = copySym(n.sym) + result.sym = copySym(n.sym, nextId c.idgen) result.sym.owner = procSym for i in 0 ..< safeLen(n): - result[i] = transform(procSym, n[i], old, fresh, oldParam, newParam) + result[i] = transform(c, procSym, n[i], old, fresh, oldParam, newParam) #if n.kind == nkDerefExpr and sameType(n[0].typ, old): # result = - result = copySym(orig) + result = copySym(orig, nextId c.idgen) result.info = info result.flags.incl sfFromGeneric result.owner = orig let origParamType = orig.typ[1] - let newParamType = makeVarType(result, origParamType.skipTypes(abstractPtrs)) + let newParamType = makeVarType(result, origParamType.skipTypes(abstractPtrs), c.idgen) let oldParam = orig.typ.n[1].sym - let newParam = newSym(skParam, oldParam.name, result, result.info) + let newParam = newSym(skParam, oldParam.name, nextId c.idgen, result, result.info) newParam.typ = newParamType # proc body: - result.ast = transform(result, orig.ast, origParamType, newParamType, oldParam, newParam) + result.ast = transform(c, result, orig.ast, origParamType, newParamType, oldParam, newParam) # proc signature: - result.typ = newProcType(result.info, result) + result.typ = newProcType(result.info, nextId c.idgen, result) result.typ.addParam newParam proc semQuantifier(c: PContext; n: PNode): PNode = diff --git a/compiler/semparallel.nim b/compiler/semparallel.nim index 578687e6c..82fb069db 100644 --- a/compiler/semparallel.nim +++ b/compiler/semparallel.nim @@ -416,13 +416,13 @@ proc transformSlices(g: ModuleGraph; n: PNode): PNode = else: result = n -proc transformSpawn(g: ModuleGraph; owner: PSym; n, barrier: PNode): PNode -proc transformSpawnSons(g: ModuleGraph; owner: PSym; n, barrier: PNode): PNode = +proc transformSpawn(g: ModuleGraph; idgen: IdGenerator;owner: PSym; n, barrier: PNode): PNode +proc transformSpawnSons(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n, barrier: PNode): PNode = result = shallowCopy(n) for i in 0.. 0: - result = transformSpawnSons(g, owner, n, barrier) + result = transformSpawnSons(g, idgen, owner, n, barrier) else: result = n @@ -464,7 +464,7 @@ proc checkArgs(a: var AnalysisCtx; n: PNode) = proc generateAliasChecks(a: AnalysisCtx; result: PNode) = discard "too implement" -proc liftParallel*(g: ModuleGraph; owner: PSym; n: PNode): PNode = +proc liftParallel*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n: PNode): PNode = # this needs to be called after the 'for' loop elimination # first pass: @@ -482,16 +482,16 @@ proc liftParallel*(g: ModuleGraph; owner: PSym; n: PNode): PNode = checkArgs(a, body) var varSection = newNodeI(nkVarSection, n.info) - var temp = newSym(skTemp, getIdent(g.cache, "barrier"), owner, n.info) + var temp = newSym(skTemp, getIdent(g.cache, "barrier"), nextId idgen, owner, n.info) temp.typ = magicsys.getCompilerProc(g, "Barrier").typ incl(temp.flags, sfFromGeneric) let tempNode = newSymNode(temp) varSection.addVar tempNode - let barrier = genAddrOf(tempNode) + let barrier = genAddrOf(tempNode, idgen) result = newNodeI(nkStmtList, n.info) generateAliasChecks(a, result) result.add varSection result.add callCodegenProc(g, "openBarrier", barrier.info, barrier) - result.add transformSpawn(g, owner, body, barrier) + result.add transformSpawn(g, idgen, owner, body, barrier) result.add callCodegenProc(g, "closeBarrier", barrier.info, barrier) diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 352871412..92a896a38 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -92,7 +92,7 @@ proc createTypeBoundOps(tracked: PEffects, typ: PType; info: TLineInfo) = optSeqDestructors in tracked.config.globalOptions: createTypeBoundOps(tracked.graph, tracked.c, realType.lastSon, info) - createTypeBoundOps(tracked.graph, tracked.c, typ, info) + createTypeBoundOps(tracked.graph, tracked.c, typ, info, tracked.c.idgen) if (tfHasAsgn in typ.flags) or optSeqDestructors in tracked.config.globalOptions: tracked.owner.flags.incl sfInjectDestructors @@ -219,7 +219,7 @@ proc markGcUnsafe(a: PEffects; reason: PNode) = if reason.kind == nkSym: a.owner.gcUnsafetyReason = reason.sym else: - a.owner.gcUnsafetyReason = newSym(skUnknown, a.owner.name, + a.owner.gcUnsafetyReason = newSym(skUnknown, a.owner.name, nextId a.c.idgen, a.owner, reason.info, {}) when true: @@ -755,7 +755,7 @@ proc trackCall(tracked: PEffects; n: PNode) = if n.typ != nil: if tracked.owner.kind != skMacro and n.typ.skipTypes(abstractVar).kind != tyOpenArray: createTypeBoundOps(tracked, n.typ, n.info) - if getConstExpr(tracked.ownerModule, n, tracked.graph) != nil: + if getConstExpr(tracked.ownerModule, n, tracked.c.idgen, tracked.graph) != nil: return if a.kind == nkCast and a[1].typ.kind == tyProc: a = a[1] @@ -829,7 +829,7 @@ proc trackCall(tracked: PEffects; n: PNode) = case op[i].kind of tySink: createTypeBoundOps(tracked, op[i][0], n.info) - checkForSink(tracked.config, tracked.owner, n[i]) + checkForSink(tracked.config, tracked.c.idgen, tracked.owner, n[i]) of tyVar: tracked.hasDangerousAssign = true #of tyOut: @@ -947,7 +947,7 @@ proc track(tracked: PEffects, n: PNode) = if tracked.owner.kind != skMacro: createTypeBoundOps(tracked, n[0].typ, n.info) if n[0].kind != nkSym or not isLocalVar(tracked, n[0].sym): - checkForSink(tracked.config, tracked.owner, n[1]) + checkForSink(tracked.config, tracked.c.idgen, tracked.owner, n[1]) if not tracked.hasDangerousAssign and n[0].kind != nkSym: tracked.hasDangerousAssign = true of nkVarSection, nkLetSection: @@ -1054,9 +1054,9 @@ proc track(tracked: PEffects, n: PNode) = if x.kind == nkExprColonExpr: if x[0].kind == nkSym: notNilCheck(tracked, x[1], x[0].sym.typ) - checkForSink(tracked.config, tracked.owner, x[1]) + checkForSink(tracked.config, tracked.c.idgen, tracked.owner, x[1]) else: - checkForSink(tracked.config, tracked.owner, x) + checkForSink(tracked.config, tracked.c.idgen, tracked.owner, x) setLen(tracked.guards.s, oldFacts) if tracked.owner.kind != skMacro: # XXX n.typ can be nil in runnableExamples, we need to do something about it. @@ -1071,7 +1071,7 @@ proc track(tracked: PEffects, n: PNode) = createTypeBoundOps(tracked, n[i][0].typ, n.info) else: createTypeBoundOps(tracked, n[i].typ, n.info) - checkForSink(tracked.config, tracked.owner, n[i]) + checkForSink(tracked.config, tracked.c.idgen, tracked.owner, n[i]) of nkPragmaBlock: let pragmaList = n[0] var bc = createBlockContext(tracked) @@ -1122,7 +1122,7 @@ proc track(tracked: PEffects, n: PNode) = of nkBracket: for i in 0.. 1: result.n = n[1] proc semRangeAux(c: PContext, n: PNode, prev: PType): PType = @@ -217,7 +217,7 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType = result.n = newNodeI(nkRange, n.info) # always create a 'valid' range type, but overwrite it later # because 'semExprWithType' can raise an exception. See bug #6895. - addSonSkipIntLit(result, errorType(c)) + addSonSkipIntLit(result, errorType(c), c.idgen) if (n[1].kind == nkEmpty) or (n[2].kind == nkEmpty): localError(c.config, n.info, "range is empty") @@ -228,7 +228,7 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType = var rangeT: array[2, PType] for i in 0..1: - rangeT[i] = range[i].typ.skipTypes({tyStatic}).skipIntLit + rangeT[i] = range[i].typ.skipTypes({tyStatic}).skipIntLit(c.idgen) let hasUnknownTypes = c.inGenericContext > 0 and rangeT[0].kind == tyFromExpr or rangeT[1].kind == tyFromExpr @@ -345,7 +345,7 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType = # bug #6682: Do not propagate initialization requirements etc for the # index type: rawAddSonNoPropagationOfTypeFlags(result, indx) - addSonSkipIntLit(result, base) + addSonSkipIntLit(result, base, c.idgen) else: localError(c.config, n.info, errArrayExpectsTwoTypeParams) result = newOrPrevType(tyError, prev, c) @@ -357,7 +357,7 @@ proc semOrdinal(c: PContext, n: PNode, prev: PType): PType = if base.kind != tyGenericParam: if not isOrdinalType(base): localError(c.config, n[1].info, errOrdinalTypeExpected) - addSonSkipIntLit(result, base) + addSonSkipIntLit(result, base, c.idgen) else: localError(c.config, n.info, errXExpectsOneTypeParam % "ordinal") result = newOrPrevType(tyError, prev, c) @@ -384,7 +384,7 @@ proc semTypeIdent(c: PContext, n: PNode): PSym = if result.typ.sym == nil: localError(c.config, n.info, errTypeExpected) return errorSym(c, n) - result = result.typ.sym.copySym + result = result.typ.sym.copySym(nextId c.idgen) result.typ = exactReplica(result.typ) result.typ.flags.incl tfUnresolved @@ -429,7 +429,7 @@ proc semAnonTuple(c: PContext, n: PNode, prev: PType): PType = localError(c.config, n.info, errTypeExpected) result = newOrPrevType(tyTuple, prev, c) for it in n: - addSonSkipIntLit(result, semTypeNode(c, it, nil)) + addSonSkipIntLit(result, semTypeNode(c, it, nil), c.idgen) proc semTuple(c: PContext, n: PNode, prev: PType): PType = var typ: PType @@ -457,15 +457,13 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType = localError(c.config, a[j].info, "attempt to redefine: '" & field.name.s & "'") else: result.n.add newSymNode(field) - addSonSkipIntLit(result, typ) + addSonSkipIntLit(result, typ, c.idgen) styleCheckDef(c.config, a[j].info, field) onDef(field.info, field) if result.n.len == 0: result.n = nil if isTupleRecursive(result): localError(c.config, n.info, errIllegalRecursionInTypeX % typeToString(result)) - - proc semIdentVis(c: PContext, kind: TSymKind, n: PNode, allowed: TSymFlags): PSym = # identifier with visibility @@ -925,11 +923,11 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType = elif region.skipTypes({tyGenericInst, tyAlias, tySink}).kind notin { tyError, tyObject}: message c.config, n[i].info, errGenerated, "region needs to be an object type" - addSonSkipIntLit(result, region) + addSonSkipIntLit(result, region, c.idgen) else: message(c.config, n.info, warnDeprecated, "region for pointer types is deprecated") - addSonSkipIntLit(result, region) - addSonSkipIntLit(result, t) + addSonSkipIntLit(result, region, c.idgen) + addSonSkipIntLit(result, t, c.idgen) if tfPartial in result.flags: if result.lastSon.kind == tyObject: incl(result.lastSon.flags, tfPartial) #if not isNilable: result.flags.incl tfNotNil @@ -963,7 +961,7 @@ proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) = if kind == skMacro: let staticType = findEnforcedStaticType(param.typ) if staticType != nil: - var a = copySym(param) + var a = copySym(param, nextId c.idgen) a.typ = staticType.base addDecl(c, a) #elif param.typ != nil and param.typ.kind == tyTypeDesc: @@ -971,7 +969,7 @@ proc addParamOrResult(c: PContext, param: PSym, kind: TSymKind) = else: # within a macro, every param has the type NimNode! let nn = getSysSym(c.graph, param.info, "NimNode") - var a = copySym(param) + var a = copySym(param, nextId c.idgen) a.typ = nn.typ addDecl(c, a) else: @@ -1001,7 +999,7 @@ proc addImplicitGeneric(c: PContext; typeClass: PType, typId: PIdent; let owner = if typeClass.sym != nil: typeClass.sym else: getCurrOwner(c) - var s = newSym(skType, finalTypId, owner, info) + var s = newSym(skType, finalTypId, nextId c.idgen, owner, info) if sfExplain in owner.flags: s.flags.incl sfExplain if typId == nil: s.flags.incl(sfAnon) s.linkTo(typeClass) @@ -1107,7 +1105,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, of tyGenericInst: if paramType.lastSon.kind == tyUserTypeClass: - var cp = copyType(paramType, getCurrOwner(c), false) + var cp = copyType(paramType, nextId c.idgen, getCurrOwner(c)) cp.kind = tyUserTypeClassInst return addImplicitGeneric(c, cp, paramTypId, info, genericParams, paramName) @@ -1144,7 +1142,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, of tyUserTypeClasses, tyBuiltInTypeClass, tyCompositeTypeClass, tyAnd, tyOr, tyNot: result = addImplicitGeneric(c, - copyType(paramType, getCurrOwner(c), false), paramTypId, + copyType(paramType, nextId c.idgen, getCurrOwner(c)), paramTypId, info, genericParams, paramName) of tyGenericParam: @@ -1274,7 +1272,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, typ = errorType(c) let lifted = liftParamType(c, kind, genericParams, typ, arg.name.s, arg.info) - let finalType = if lifted != nil: lifted else: typ.skipIntLit + let finalType = if lifted != nil: lifted else: typ.skipIntLit(c.idgen) arg.typ = finalType arg.position = counter arg.constraint = constraint @@ -1331,7 +1329,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, #if r.kind != tyGenericParam: #echo "came here for ", typeToString(r) r.flags.incl tfRetType - r = skipIntLit(r) + r = skipIntLit(r, c.idgen) if kind == skIterator: # see tchainediterators # in cases like iterator foo(it: iterator): type(it) @@ -1413,13 +1411,13 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = t = t.base result = newOrPrevType(tyGenericInvocation, prev, c) - addSonSkipIntLit(result, t) + addSonSkipIntLit(result, t, c.idgen) template addToResult(typ) = if typ.isNil: internalAssert c.config, false rawAddSon(result, typ) - else: addSonSkipIntLit(result, typ) + else: addSonSkipIntLit(result, typ, c.idgen) if t.kind == tyForward: for i in 1.. 0: t.n[i].sym.typ = skipped @@ -553,7 +558,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = result = n.typ of tyInt, tyFloat: - result = skipIntLit(t) + result = skipIntLit(t, cl.c.idgen) of tyTypeDesc: let lookup = cl.typeMap.lookup(t) @@ -617,7 +622,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = of tyProc: eraseVoidParams(result) - skipIntLiteralParams(result) + skipIntLiteralParams(result, cl.c.idgen) of tyRange: result[0] = result[0].skipTypes({tyStatic, tyDistinct}) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 687634e93..2cf2846c0 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -143,7 +143,7 @@ proc put(c: var TCandidate, key, val: PType) {.inline.} = writeStackTrace() if c.c.module.name.s == "temp3": echo "binding ", key, " -> ", val - idTablePut(c.bindings, key, val.skipIntLit) + idTablePut(c.bindings, key, val.skipIntLit(c.c.idgen)) proc initCandidate*(ctx: PContext, c: var TCandidate, callee: PSym, binding: PNode, calleeScope = -1, @@ -726,7 +726,7 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType = if alreadyBound != nil: typ = alreadyBound template paramSym(kind): untyped = - newSym(kind, typeParamName, typeClass.sym, typeClass.sym.info, {}) + newSym(kind, typeParamName, nextId(c.idgen), typeClass.sym, typeClass.sym.info, {}) block addTypeParam: for prev in typeParams: @@ -739,6 +739,7 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType = of tyStatic: param = paramSym skConst param.typ = typ.exactReplica + #copyType(typ, nextId(c.idgen), typ.owner) if typ.n == nil: param.typ.flags.incl tfInferrableStatic else: @@ -746,6 +747,7 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType = of tyUnknown: param = paramSym skVar param.typ = typ.exactReplica + #copyType(typ, nextId(c.idgen), typ.owner) else: param = paramSym skType param.typ = if typ.isMetaType: @@ -796,7 +798,8 @@ proc matchUserTypeClass*(m: var TCandidate; ff, a: PType): PType = if ff.kind == tyUserTypeClassInst: result = generateTypeInstance(c, m.bindings, typeClass.sym.info, ff) else: - result = copyType(ff, ff.owner, true) + result = ff.exactReplica + #copyType(ff, nextId(c.idgen), ff.owner) result.n = checkedBody @@ -1086,7 +1089,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, template bindingRet(res) = if doBind: - let bound = aOrig.skipTypes({tyRange}).skipIntLit + let bound = aOrig.skipTypes({tyRange}).skipIntLit(c.c.idgen) put(c, f, bound) return res @@ -1500,14 +1503,15 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, of tyGenericInvocation: var x = a.skipGenericAlias + var preventHack = false if x.kind == tyOwned and f[0].kind != tyOwned: preventHack = true x = x.lastSon # XXX: This is very hacky. It should be moved back into liftTypeParam if x.kind in {tyGenericInst, tyArray} and - c.calleeSym != nil and - c.calleeSym.kind in {skProc, skFunc} and c.call != nil and not preventHack: + c.calleeSym != nil and + c.calleeSym.kind in {skProc, skFunc} and c.call != nil and not preventHack: let inst = prepareMetatypeForSigmatch(c.c, c.bindings, c.call.info, f) #echo "inferred ", typeToString(inst), " for ", f return typeRel(c, inst, a, flags) @@ -2291,7 +2295,7 @@ proc arrayConstr(c: PContext, n: PNode): PType = result = newTypeS(tyArray, c) rawAddSon(result, makeRangeType(c, 0, 0, n.info)) addSonSkipIntLit(result, skipTypes(n.typ, - {tyGenericInst, tyVar, tyLent, tyOrdinal})) + {tyGenericInst, tyVar, tyLent, tyOrdinal}), c.idgen) proc arrayConstr(c: PContext, info: TLineInfo): PType = result = newTypeS(tyArray, c) diff --git a/compiler/sinkparameter_inference.nim b/compiler/sinkparameter_inference.nim index 182e44324..841f2de8e 100644 --- a/compiler/sinkparameter_inference.nim +++ b/compiler/sinkparameter_inference.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -proc checkForSink*(config: ConfigRef; owner: PSym; arg: PNode) = +proc checkForSink*(config: ConfigRef; idgen: IdGenerator; owner: PSym; arg: PNode) = #[ Patterns we seek to detect: someLocation = p # ---> p: sink T @@ -32,7 +32,7 @@ proc checkForSink*(config: ConfigRef; owner: PSym; arg: PNode) = if sfWasForwarded notin owner.flags: let argType = arg.sym.typ - let sinkType = newType(tySink, owner) + let sinkType = newType(tySink, nextId(idgen), owner) sinkType.size = argType.size sinkType.align = argType.align sinkType.paddingAtEnd = argType.paddingAtEnd @@ -52,18 +52,18 @@ proc checkForSink*(config: ConfigRef; owner: PSym; arg: PNode) = #echo config $ arg.info, " candidate for a sink parameter here" of nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr: if not isEmptyType(arg.typ): - checkForSink(config, owner, arg.lastSon) + checkForSink(config, idgen, owner, arg.lastSon) of nkIfStmt, nkIfExpr, nkWhen: for branch in arg: let value = branch.lastSon if not isEmptyType(value.typ): - checkForSink(config, owner, value) + checkForSink(config, idgen, owner, value) of nkCaseStmt: for i in 1.. 16) and n.getRoot != nil: # it is more efficient to pass a pointer instead: - let a = genAddrOf(n) + let a = genAddrOf(n, idgen) field.typ = a.typ - objType.addField(field, g.cache) + objType.addField(field, g.cache, idgen) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a) - let threadLocal = addLocalVar(g, varSection,nil, owner, field.typ, + let threadLocal = addLocalVar(g, varSection, nil, idgen, owner, field.typ, indirectAccess(castExpr, field, n.info), useShallowCopy=true) call.add(genDeref(threadLocal.newSymNode)) else: # boring case field.typ = argType - objType.addField(field, g.cache) + objType.addField(field, g.cache, idgen) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n) let threadLocal = addLocalVar(g, varSection, varInit, - owner, field.typ, + idgen, owner, field.typ, indirectAccess(castExpr, field, n.info), useShallowCopy=true) call.add(threadLocal.newSymNode) -proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: PType; +proc wrapProcForSpawn*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; spawnExpr: PNode; retType: PType; barrier, dest: PNode = nil): PNode = # if 'barrier' != nil, then it is in a 'parallel' section and we # generate quite different code @@ -330,9 +332,9 @@ proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: P var fn = n[0] let name = (if fn.kind == nkSym: fn.sym.name.s else: genPrefix) & "Wrapper" - wrapperProc = newSym(skProc, getIdent(g.cache, name), owner, fn.info, g.config.options) - threadParam = newSym(skParam, getIdent(g.cache, "thread"), wrapperProc, n.info, g.config.options) - argsParam = newSym(skParam, getIdent(g.cache, "args"), wrapperProc, n.info, g.config.options) + wrapperProc = newSym(skProc, getIdent(g.cache, name), nextId idgen, owner, fn.info, g.config.options) + threadParam = newSym(skParam, getIdent(g.cache, "thread"), nextId idgen, wrapperProc, n.info, g.config.options) + argsParam = newSym(skParam, getIdent(g.cache, "args"), nextId idgen, wrapperProc, n.info, g.config.options) wrapperProc.flags.incl sfInjectDestructors block: @@ -341,11 +343,11 @@ proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: P argsParam.typ = ptrType argsParam.position = 1 - var objType = createObj(g, owner, n.info) + var objType = createObj(g, idgen, owner, n.info) incl(objType.flags, tfFinal) - let castExpr = createCastExpr(argsParam, objType) + let castExpr = createCastExpr(argsParam, objType, idgen) - var scratchObj = newSym(skVar, getIdent(g.cache, "scratch"), owner, n.info, g.config.options) + var scratchObj = newSym(skVar, getIdent(g.cache, "scratch"), nextId idgen, owner, n.info, g.config.options) block: scratchObj.typ = objType incl(scratchObj.flags, sfFromGeneric) @@ -362,9 +364,9 @@ proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: P skFunc, skMethod, skConverter}): # for indirect calls we pass the function pointer in the scratchObj var argType = n[0].typ.skipTypes(abstractInst) - var field = newSym(skField, getIdent(g.cache, "fn"), owner, n.info, g.config.options) + var field = newSym(skField, getIdent(g.cache, "fn"), nextId idgen, owner, n.info, g.config.options) field.typ = argType - objType.addField(field, g.cache) + objType.addField(field, g.cache, idgen) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[0]) fn = indirectAccess(castExpr, field, n.info) elif fn.kind == nkSym and fn.sym.kind == skIterator: @@ -376,49 +378,48 @@ proc wrapProcForSpawn*(g: ModuleGraph; owner: PSym; spawnExpr: PNode; retType: P var varSection = newNodeI(nkVarSection, n.info) var varInit = newNodeI(nkStmtList, n.info) if barrier.isNil: - setupArgsForConcurrency(g, n, objType, wrapperProc, scratchObj, castExpr, call, + setupArgsForConcurrency(g, n, objType, idgen, wrapperProc, scratchObj, castExpr, call, varSection, varInit, result) else: - setupArgsForParallelism(g, n, objType, wrapperProc, scratchObj, castExpr, call, + setupArgsForParallelism(g, n, objType, idgen, wrapperProc, scratchObj, castExpr, call, varSection, varInit, result) var barrierAsExpr: PNode = nil if barrier != nil: - let typ = newType(tyPtr, owner) + let typ = newType(tyPtr, nextId idgen, owner) typ.rawAddSon(magicsys.getCompilerProc(g, "Barrier").typ) - var field = newSym(skField, getIdent(g.cache, "barrier"), owner, n.info, g.config.options) + var field = newSym(skField, getIdent(g.cache, "barrier"), nextId idgen, owner, n.info, g.config.options) field.typ = typ - objType.addField(field, g.cache) + objType.addField(field, g.cache, idgen) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), barrier) barrierAsExpr = indirectAccess(castExpr, field, n.info) var fvField, fvAsExpr: PNode = nil if spawnKind == srFlowVar: - var field = newSym(skField, getIdent(g.cache, "fv"), owner, n.info, g.config.options) + var field = newSym(skField, getIdent(g.cache, "fv"), nextId idgen, owner, n.info, g.config.options) field.typ = retType - objType.addField(field, g.cache) + objType.addField(field, g.cache, idgen) fvField = newDotExpr(scratchObj, field) fvAsExpr = indirectAccess(castExpr, field, n.info) # create flowVar: result.add newFastAsgnStmt(fvField, callProc(spawnExpr[^1])) if barrier == nil: - result.add callCodegenProc(g, "nimFlowVarCreateSemaphore", fvField.info, - fvField) + result.add callCodegenProc(g, "nimFlowVarCreateSemaphore", fvField.info, fvField) elif spawnKind == srByVar: - var field = newSym(skField, getIdent(g.cache, "fv"), owner, n.info, g.config.options) - field.typ = newType(tyPtr, objType.owner) + var field = newSym(skField, getIdent(g.cache, "fv"), nextId idgen, owner, n.info, g.config.options) + field.typ = newType(tyPtr, nextId idgen, objType.owner) field.typ.rawAddSon(retType) - objType.addField(field, g.cache) + objType.addField(field, g.cache, idgen) fvAsExpr = indirectAccess(castExpr, field, n.info) - result.add newFastAsgnStmt(newDotExpr(scratchObj, field), genAddrOf(dest)) + result.add newFastAsgnStmt(newDotExpr(scratchObj, field), genAddrOf(dest, idgen)) - createTypeBoundOps(g, nil, objType, n.info) + createTypeBoundOps(g, nil, objType, n.info, idgen) createWrapperProc(g, fn, threadParam, argsParam, varSection, varInit, call, - barrierAsExpr, fvAsExpr, spawnKind, wrapperProc) + barrierAsExpr, fvAsExpr, idgen, spawnKind, wrapperProc) result.add callCodegenProc(g, "nimSpawn" & $spawnExpr.len, wrapperProc.info, - wrapperProc.newSymNode, genAddrOf(scratchObj.newSymNode), nil, spawnExpr) + wrapperProc.newSymNode, genAddrOf(scratchObj.newSymNode, idgen), nil, spawnExpr) if spawnKind == srFlowVar: result.add fvField diff --git a/compiler/transf.nim b/compiler/transf.nim index 5922ca595..42a78c8e3 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -24,7 +24,7 @@ import lowerings, liftlocals, modulegraphs, lineinfos -proc transformBody*(g: ModuleGraph, prc: PSym, cache: bool): PNode +proc transformBody*(g: ModuleGraph; idgen: IdGenerator, prc: PSym, cache: bool): PNode import closureiters, lambdalifting @@ -40,7 +40,7 @@ type # if we encounter the 2nd yield statement next: PTransCon # for stacking - TTransfContext = object of TPassContext + TTransfContext = object module: PSym transCon: PTransCon # top of a TransCon stack inlining: int # > 0 if we are in inlining context (copy vars) @@ -48,6 +48,7 @@ type contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break' deferDetected, tooEarly: bool graph: ModuleGraph + idgen: IdGenerator PTransf = ref TTransfContext proc newTransNode(a: PNode): PNode {.inline.} = @@ -86,12 +87,12 @@ proc getCurrOwner(c: PTransf): PSym = else: result = c.module proc newTemp(c: PTransf, typ: PType, info: TLineInfo): PNode = - let r = newSym(skTemp, getIdent(c.graph.cache, genPrefix), getCurrOwner(c), info) + let r = newSym(skTemp, getIdent(c.graph.cache, genPrefix), nextId(c.idgen), getCurrOwner(c), info) r.typ = typ #skipTypes(typ, {tyGenericInst, tyAlias, tySink}) incl(r.flags, sfFromGeneric) let owner = getCurrOwner(c) if owner.isIterator and not c.tooEarly: - result = freshVarForClosureIter(c.graph, r, owner) + result = freshVarForClosureIter(c.graph, r, c.idgen, owner) else: result = newSymNode(r) @@ -111,13 +112,13 @@ proc transformSymAux(c: PTransf, n: PNode): PNode = let s = n.sym if s.typ != nil and s.typ.callConv == ccClosure: if s.kind in routineKinds: - discard transformBody(c.graph, s, true) + discard transformBody(c.graph, c.idgen, s, true) if s.kind == skIterator: if c.tooEarly: return n - else: return liftIterSym(c.graph, n, getCurrOwner(c)) + else: return liftIterSym(c.graph, n, c.idgen, getCurrOwner(c)) elif s.kind in {skProc, skFunc, skConverter, skMethod} and not c.tooEarly: # top level .closure procs are still somewhat supported for 'Nake': - return makeClosure(c.graph, s, nil, n.info) + return makeClosure(c.graph, c.idgen, s, nil, n.info) #elif n.sym.kind in {skVar, skLet} and n.sym.typ.callConv == ccClosure: # echo n.info, " come heer for ", c.tooEarly # if not c.tooEarly: @@ -160,9 +161,9 @@ proc transformSym(c: PTransf, n: PNode): PNode = proc freshVar(c: PTransf; v: PSym): PNode = let owner = getCurrOwner(c) if owner.isIterator and not c.tooEarly: - result = freshVarForClosureIter(c.graph, v, owner) + result = freshVarForClosureIter(c.graph, v, c.idgen, owner) else: - var newVar = copySym(v) + var newVar = copySym(v, nextId(c.idgen)) incl(newVar.flags, sfFromGeneric) newVar.owner = owner result = newSymNode(newVar) @@ -232,7 +233,7 @@ proc hasContinue(n: PNode): bool = if hasContinue(n[i]): return true proc newLabel(c: PTransf, n: PNode): PSym = - result = newSym(skLabel, nil, getCurrOwner(c), n.info) + result = newSym(skLabel, nil, nextId(c.idgen), getCurrOwner(c), n.info) result.name = getIdent(c.graph.cache, genPrefix) proc transformBlock(c: PTransf, n: PNode): PNode = @@ -423,7 +424,7 @@ proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PNode = if n.typ.skipTypes(abstractVar).kind != tyOpenArray: result.typ = n.typ elif n.typ.skipTypes(abstractInst).kind in {tyVar}: - result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind) + result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, c.idgen) of nkHiddenStdConv, nkHiddenSubConv, nkConv: var m = n[0][1] if m.kind == a or m.kind == b: @@ -433,7 +434,7 @@ proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PNode = if n.typ.skipTypes(abstractVar).kind != tyOpenArray: result.typ = n.typ elif n.typ.skipTypes(abstractInst).kind in {tyVar}: - result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind) + result.typ = toVar(result.typ, n.typ.skipTypes(abstractInst).kind, c.idgen) else: if n[0].kind == a or n[0].kind == b: # addr ( deref ( x )) --> x @@ -495,7 +496,7 @@ proc transformConv(c: PTransf, n: PNode): PNode = of tyOpenArray, tyVarargs: result = transform(c, n[1]) #result = transformSons(c, n) - result.typ = takeType(n.typ, n[1].typ) + result.typ = takeType(n.typ, n[1].typ, c.idgen) #echo n.info, " came here and produced ", typeToString(result.typ), # " from ", typeToString(n.typ), " and ", typeToString(n[1].typ) of tyCString: @@ -629,7 +630,7 @@ proc transformFor(c: PTransf, n: PNode): PNode = result[1] = n result[1][^1] = transformLoopBody(c, n[^1]) result[1][^2] = transform(c, n[^2]) - result[1] = lambdalifting.liftForLoop(c.graph, result[1], getCurrOwner(c)) + result[1] = lambdalifting.liftForLoop(c.graph, result[1], c.idgen, getCurrOwner(c)) discard c.breakSyms.pop return result @@ -698,7 +699,7 @@ proc transformFor(c: PTransf, n: PNode): PNode = stmtList.add(newAsgnStmt(c, nkFastAsgn, temp, arg)) idNodeTablePut(newC.mapping, formal, temp) - let body = transformBody(c.graph, iter, true) + let body = transformBody(c.graph, c.idgen, iter, true) pushInfoContext(c.graph.config, n.info) inc(c.inlining) stmtList.add(transform(c, body)) @@ -830,7 +831,7 @@ proc transformExceptBranch(c: PTransf, n: PNode): PNode = let convNode = newTransNode(nkHiddenSubConv, n[1].info, 2) convNode[0] = newNodeI(nkEmpty, n.info) convNode[1] = excCall - convNode.typ = excTypeNode.typ.toRef() + convNode.typ = excTypeNode.typ.toRef(c.idgen) # -> let exc = ... let identDefs = newTransNode(nkIdentDefs, n[1].info, 3) identDefs[0] = n[0][2] @@ -850,10 +851,10 @@ proc transformExceptBranch(c: PTransf, n: PNode): PNode = else: result = transformSons(c, n) -proc commonOptimizations*(g: ModuleGraph; c: PSym, n: PNode): PNode = +proc commonOptimizations*(g: ModuleGraph; idgen: IdGenerator; c: PSym, n: PNode): PNode = result = n for i in 0..= 3): result = newNodeIT(nkCall, n.info, n.typ) @@ -873,7 +874,7 @@ proc commonOptimizations*(g: ModuleGraph; c: PSym, n: PNode): PNode = result.add(a) if result.len == 2: result = result[1] else: - var cnst = getConstExpr(c, n, g) + var cnst = getConstExpr(c, n, idgen, g) # we inline constants if they are not complex constants: if cnst != nil and not dontInlineConstant(n, cnst): result = cnst @@ -1012,7 +1013,7 @@ proc transform(c: PTransf, n: PNode): PNode = n.typ != nil and n.typ.kind == tyPointer if not exprIsPointerCast: - var cnst = getConstExpr(c.module, result, c.graph) + var cnst = getConstExpr(c.module, result, c.idgen, c.graph) # we inline constants if they are not complex constants: if cnst != nil and not dontInlineConstant(n, cnst): result = cnst # do not miss an optimization @@ -1027,12 +1028,13 @@ proc processTransf(c: PTransf, n: PNode, owner: PSym): PNode = popTransCon(c) incl(result.flags, nfTransf) -proc openTransf(g: ModuleGraph; module: PSym, filename: string): PTransf = +proc openTransf(g: ModuleGraph; module: PSym, filename: string; idgen: IdGenerator): PTransf = new(result) result.contSyms = @[] result.breakSyms = @[] result.module = module result.graph = g + result.idgen = idgen proc flattenStmts(n: PNode) = var goOn = true @@ -1075,7 +1077,7 @@ template liftDefer(c, root) = if c.deferDetected: liftDeferAux(root) -proc transformBody*(g: ModuleGraph, prc: PSym, cache: bool): PNode = +proc transformBody*(g: ModuleGraph; idgen: IdGenerator; prc: PSym; cache: bool): PNode = assert prc.kind in routineKinds if prc.transformedBody != nil: @@ -1084,14 +1086,14 @@ proc transformBody*(g: ModuleGraph, prc: PSym, cache: bool): PNode = result = prc.ast[bodyPos] else: prc.transformedBody = newNode(nkEmpty) # protects from recursion - var c = openTransf(g, prc.getModule, "") - result = liftLambdas(g, prc, prc.ast[bodyPos], c.tooEarly) + var c = openTransf(g, prc.getModule, "", idgen) + result = liftLambdas(g, prc, prc.ast[bodyPos], c.tooEarly, c.idgen) result = processTransf(c, result, prc) liftDefer(c, result) - result = liftLocalsIfRequested(prc, result, g.cache, g.config) + result = liftLocalsIfRequested(prc, result, g.cache, g.config, c.idgen) if prc.isIterator: - result = g.transformClosureIterator(prc, result) + result = g.transformClosureIterator(c.idgen, prc, result) incl(result.flags, nfTransf) @@ -1105,21 +1107,21 @@ proc transformBody*(g: ModuleGraph, prc: PSym, cache: bool): PNode = #if prc.name.s == "main": # echo "transformed into ", renderTree(result, {renderIds}) -proc transformStmt*(g: ModuleGraph; module: PSym, n: PNode): PNode = +proc transformStmt*(g: ModuleGraph; idgen: IdGenerator; module: PSym, n: PNode): PNode = if nfTransf in n.flags: result = n else: - var c = openTransf(g, module, "") + var c = openTransf(g, module, "", idgen) result = processTransf(c, n, module) liftDefer(c, result) #result = liftLambdasForTopLevel(module, result) incl(result.flags, nfTransf) -proc transformExpr*(g: ModuleGraph; module: PSym, n: PNode): PNode = +proc transformExpr*(g: ModuleGraph; idgen: IdGenerator; module: PSym, n: PNode): PNode = if nfTransf in n.flags: result = n else: - var c = openTransf(g, module, "") + var c = openTransf(g, module, "", idgen) result = processTransf(c, n, module) liftDefer(c, result) # expressions are not to be injected with destructor calls as that diff --git a/compiler/types.nim b/compiler/types.nim index 75e12cac4..a3a4695b0 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -1296,11 +1296,11 @@ proc containsGenericTypeIter(t: PType, closure: RootRef): bool = proc containsGenericType*(t: PType): bool = result = iterOverType(t, containsGenericTypeIter, nil) -proc baseOfDistinct*(t: PType): PType = +proc baseOfDistinct*(t: PType; idgen: IdGenerator): PType = if t.kind == tyDistinct: result = t[0] else: - result = copyType(t, t.owner, false) + result = copyType(t, nextId idgen, t.owner) var parent: PType = nil var it = result while it.kind in {tyPtr, tyRef, tyOwned}: @@ -1438,7 +1438,7 @@ proc isEmptyContainer*(t: PType): bool = of tyGenericInst, tyAlias, tySink: result = isEmptyContainer(t.lastSon) else: result = false -proc takeType*(formal, arg: PType): PType = +proc takeType*(formal, arg: PType; idgen: IdGenerator): PType = # param: openArray[string] = [] # [] is an array constructor of length 0 of type string! if arg.kind == tyNil: @@ -1446,7 +1446,7 @@ proc takeType*(formal, arg: PType): PType = result = formal elif formal.kind in {tyOpenArray, tyVarargs, tySequence} and arg.isEmptyContainer: - let a = copyType(arg.skipTypes({tyGenericInst, tyAlias}), arg.owner, keepId=false) + let a = copyType(arg.skipTypes({tyGenericInst, tyAlias}), nextId(idgen), arg.owner) a[ord(arg.kind == tyArray)] = formal[0] result = a elif formal.kind in {tyTuple, tySet} and arg.kind == formal.kind: @@ -1454,14 +1454,14 @@ proc takeType*(formal, arg: PType): PType = else: result = arg -proc skipHiddenSubConv*(n: PNode): PNode = +proc skipHiddenSubConv*(n: PNode; idgen: IdGenerator): PNode = if n.kind == nkHiddenSubConv: # param: openArray[string] = [] # [] is an array constructor of length 0 of type string! let formal = n.typ result = n[1] let arg = result.typ - let dest = takeType(formal, arg) + let dest = takeType(formal, arg, idgen) if dest == arg and formal.kind != tyUntyped: #echo n.info, " came here for ", formal.typeToString result = n diff --git a/compiler/vm.nim b/compiler/vm.nim index bb217c4db..185975ec3 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1105,7 +1105,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let ast = a.sym.ast.shallowCopy for i in 0.. ord(high(TSymKind)): internalError(c.config, c.debug[pc], "request to create symbol of invalid kind") - var sym = newSym(k.TSymKind, getIdent(c.cache, name), c.module.owner, c.debug[pc]) + var sym = newSym(k.TSymKind, getIdent(c.cache, name), nextId c.idgen, c.module.owner, c.debug[pc]) incl(sym.flags, sfGenSym) regs[ra].node = newSymNode(sym) regs[ra].node.flags.incl nfIsRef @@ -2058,7 +2058,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let rb = instr.regB inc pc let typ = c.types[c.code[pc].regBx - wordExcess] - putIntoReg(regs[ra], loadAny(regs[rb].node.strVal, typ, c.cache, c.config)) + putIntoReg(regs[ra], loadAny(regs[rb].node.strVal, typ, c.cache, c.config, c.idgen)) of opcMarshalStore: decodeB(rkNode) inc pc @@ -2103,7 +2103,7 @@ proc execProc*(c: PCtx; sym: PSym; args: openArray[PNode]): PNode = "NimScript: attempt to call non-routine: " & sym.name.s) proc evalStmt*(c: PCtx, n: PNode) = - let n = transformExpr(c.graph, c.module, n) + let n = transformExpr(c.graph, c.idgen, c.module, n) let start = genStmt(c, n) # execute new instructions; this redundant opcEof check saves us lots # of allocations in 'execute': @@ -2111,7 +2111,7 @@ proc evalStmt*(c: PCtx, n: PNode) = discard execute(c, start) proc evalExpr*(c: PCtx, n: PNode): PNode = - let n = transformExpr(c.graph, c.module, n) + let n = transformExpr(c.graph, c.idgen, c.module, n) let start = genExpr(c, n) assert c.code[start].opcode != opcEof result = execute(c, start) @@ -2122,20 +2122,20 @@ proc getGlobalValue*(c: PCtx; s: PSym): PNode = include vmops -proc setupGlobalCtx*(module: PSym; graph: ModuleGraph) = +proc setupGlobalCtx*(module: PSym; graph: ModuleGraph; idgen: IdGenerator) = if graph.vm.isNil: - graph.vm = newCtx(module, graph.cache, graph) + graph.vm = newCtx(module, graph.cache, graph, idgen) registerAdditionalOps(PCtx graph.vm) else: - refresh(PCtx graph.vm, module) + refresh(PCtx graph.vm, module, idgen) -proc myOpen(graph: ModuleGraph; module: PSym): PPassContext {.nosinks.} = +proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nosinks.} = #var c = newEvalContext(module, emRepl) #c.features = {allowCast, allowInfiniteLoops} #pushStackFrame(c, newStackFrame()) # XXX produce a new 'globals' environment here: - setupGlobalCtx(module, graph) + setupGlobalCtx(module, graph, idgen) result = PCtx graph.vm proc myProcess(c: PPassContext, n: PNode): PNode = @@ -2149,16 +2149,16 @@ proc myProcess(c: PPassContext, n: PNode): PNode = c.oldErrorCount = c.config.errorCounter proc myClose(graph: ModuleGraph; c: PPassContext, n: PNode): PNode = - myProcess(c, n) + result = myProcess(c, n) const evalPass* = makePass(myOpen, myProcess, myClose) -proc evalConstExprAux(module: PSym; +proc evalConstExprAux(module: PSym; idgen: IdGenerator; g: ModuleGraph; prc: PSym, n: PNode, mode: TEvalMode): PNode = if g.config.errorCounter > 0: return n - let n = transformExpr(g, module, n) - setupGlobalCtx(module, g) + let n = transformExpr(g, idgen, module, n) + setupGlobalCtx(module, g, idgen) var c = PCtx g.vm let oldMode = c.mode c.mode = mode @@ -2173,17 +2173,17 @@ proc evalConstExprAux(module: PSym; if result.info.col < 0: result.info = n.info c.mode = oldMode -proc evalConstExpr*(module: PSym; g: ModuleGraph; e: PNode): PNode = - result = evalConstExprAux(module, g, nil, e, emConst) +proc evalConstExpr*(module: PSym; idgen: IdGenerator; g: ModuleGraph; e: PNode): PNode = + result = evalConstExprAux(module, idgen, g, nil, e, emConst) -proc evalStaticExpr*(module: PSym; g: ModuleGraph; e: PNode, prc: PSym): PNode = - result = evalConstExprAux(module, g, prc, e, emStaticExpr) +proc evalStaticExpr*(module: PSym; idgen: IdGenerator; g: ModuleGraph; e: PNode, prc: PSym): PNode = + result = evalConstExprAux(module, idgen, g, prc, e, emStaticExpr) -proc evalStaticStmt*(module: PSym; g: ModuleGraph; e: PNode, prc: PSym) = - discard evalConstExprAux(module, g, prc, e, emStaticStmt) +proc evalStaticStmt*(module: PSym; idgen: IdGenerator; g: ModuleGraph; e: PNode, prc: PSym) = + discard evalConstExprAux(module, idgen, g, prc, e, emStaticStmt) -proc setupCompileTimeVar*(module: PSym; g: ModuleGraph; n: PNode) = - discard evalConstExprAux(module, g, nil, n, emStaticStmt) +proc setupCompileTimeVar*(module: PSym; idgen: IdGenerator; g: ModuleGraph; n: PNode) = + discard evalConstExprAux(module, idgen, g, nil, n, emStaticStmt) proc prepareVMValue(arg: PNode): PNode = ## strip nkExprColonExpr from tuple values recurively. That is how @@ -2227,14 +2227,14 @@ iterator genericParamsInMacroCall*(macroSym: PSym, call: PNode): (PSym, PNode) = # to prevent endless recursion in macro instantiation const evalMacroLimit = 1000 -proc errorNode(owner: PSym, n: PNode): PNode = +proc errorNode(idgen: IdGenerator; owner: PSym, n: PNode): PNode = result = newNodeI(nkEmpty, n.info) - result.typ = newType(tyError, owner) + result.typ = newType(tyError, nextId idgen, owner) result.typ.flags.incl tfCheckedForDestructor -proc evalMacroCall*(module: PSym; g: ModuleGraph; templInstCounter: ref int; +proc evalMacroCall*(module: PSym; idgen: IdGenerator; g: ModuleGraph; templInstCounter: ref int; n, nOrig: PNode, sym: PSym): PNode = - if g.config.errorCounter > 0: return errorNode(module, n) + if g.config.errorCounter > 0: return errorNode(idgen, module, n) # XXX globalError() is ugly here, but I don't know a better solution for now inc(g.config.evalMacroCounter) @@ -2247,7 +2247,7 @@ proc evalMacroCall*(module: PSym; g: ModuleGraph; templInstCounter: ref int; globalError(g.config, n.info, "in call '$#' got $#, but expected $# argument(s)" % [ n.renderTree, $(n.safeLen-1), $(sym.typ.len-1)]) - setupGlobalCtx(module, g) + setupGlobalCtx(module, g, idgen) var c = PCtx g.vm let oldMode = c.mode c.mode = emStaticStmt diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index 59ffe9581..202087af7 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -282,17 +282,18 @@ type PEvalContext* = PCtx -proc newCtx*(module: PSym; cache: IdentCache; g: ModuleGraph): PCtx = +proc newCtx*(module: PSym; cache: IdentCache; g: ModuleGraph; idgen: IdGenerator): PCtx = PCtx(code: @[], debug: @[], globals: newNode(nkStmtListExpr), constants: newNode(nkStmtList), types: @[], prc: PProc(blocks: @[]), module: module, loopIterations: g.config.maxLoopIterationsVM, comesFromHeuristic: unknownLineInfo, callbacks: @[], errorFlag: "", - cache: cache, config: g.config, graph: g) + cache: cache, config: g.config, graph: g, idgen: idgen) -proc refresh*(c: PCtx, module: PSym) = +proc refresh*(c: PCtx, module: PSym; idgen: IdGenerator) = c.module = module c.prc = PProc(blocks: @[]) c.loopIterations = c.config.maxLoopIterationsVM + c.idgen = idgen proc registerCallback*(c: PCtx; name: string; callback: VmCallback): int {.discardable.} = result = c.callbacks.len diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim index 37e7e4b38..8083ae179 100644 --- a/compiler/vmdeps.nim +++ b/compiler/vmdeps.nim @@ -24,8 +24,9 @@ proc opSlurp*(file: string, info: TLineInfo, module: PSym; conf: ConfigRef): str localError(conf, info, "cannot open file: " & file) result = "" -proc atomicTypeX(cache: IdentCache; name: string; m: TMagic; t: PType; info: TLineInfo): PNode = - let sym = newSym(skType, getIdent(cache, name), t.owner, info) +proc atomicTypeX(cache: IdentCache; name: string; m: TMagic; t: PType; info: TLineInfo; + idgen: IdGenerator): PNode = + let sym = newSym(skType, getIdent(cache, name), nextId(idgen), t.owner, info) sym.magic = m sym.typ = t result = newSymNode(sym) @@ -35,44 +36,46 @@ proc atomicTypeX(s: PSym; info: TLineInfo): PNode = result = newSymNode(s) result.info = info -proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo; +proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo; idgen: IdGenerator; inst=false; allowRecursionX=false): PNode proc mapTypeToBracketX(cache: IdentCache; name: string; m: TMagic; t: PType; info: TLineInfo; + idgen: IdGenerator; inst=false): PNode = result = newNodeIT(nkBracketExpr, if t.n.isNil: info else: t.n.info, t) - result.add atomicTypeX(cache, name, m, t, info) + result.add atomicTypeX(cache, name, m, t, info, idgen) for i in 0.. 0: - result.add objectNode(cache, t.n) + result.add objectNode(cache, t.n, idgen) else: result.add newNodeI(nkEmpty, info) else: @@ -298,15 +301,15 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo; of tyOwned: result = mapTypeToBracket("owned", mBuiltinType, t, info) of tyOptDeprecated: doAssert false -proc opMapTypeToAst*(cache: IdentCache; t: PType; info: TLineInfo): PNode = - result = mapTypeToAstX(cache, t, info, inst=false, allowRecursionX=true) +proc opMapTypeToAst*(cache: IdentCache; t: PType; info: TLineInfo; idgen: IdGenerator): PNode = + result = mapTypeToAstX(cache, t, info, idgen, inst=false, allowRecursionX=true) # the "Inst" version includes generic parameters in the resulting type tree # and also tries to look like the corresponding Nim type declaration -proc opMapTypeInstToAst*(cache: IdentCache; t: PType; info: TLineInfo): PNode = - result = mapTypeToAstX(cache, t, info, inst=true, allowRecursionX=false) +proc opMapTypeInstToAst*(cache: IdentCache; t: PType; info: TLineInfo; idgen: IdGenerator): PNode = + result = mapTypeToAstX(cache, t, info, idgen, inst=true, allowRecursionX=false) # the "Impl" version includes generic parameters in the resulting type tree # and also tries to look like the corresponding Nim type implementation -proc opMapTypeImplToAst*(cache: IdentCache; t: PType; info: TLineInfo): PNode = - result = mapTypeToAstX(cache, t, info, inst=true, allowRecursionX=true) +proc opMapTypeImplToAst*(cache: IdentCache; t: PType; info: TLineInfo; idgen: IdGenerator): PNode = + result = mapTypeToAstX(cache, t, info, idgen, inst=true, allowRecursionX=true) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 44b10ea67..5ba15bb2b 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1118,7 +1118,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = c.freeTemp(d) of mSwap: unused(c, n, dest) - c.gen(lowerSwap(c.graph, n, if c.prc == nil: c.module else: c.prc.sym)) + c.gen(lowerSwap(c.graph, n, c.idgen, if c.prc == nil: c.module else: c.prc.sym)) of mIsNil: genUnaryABC(c, n, dest, opcIsNil) of mParseBiggestFloat: if dest < 0: dest = c.getTemp(n.typ) @@ -1819,7 +1819,7 @@ proc genVarSection(c: PCtx; n: PNode) = if a[i].kind == nkSym: if not a[i].sym.isGlobal: setSlot(c, a[i].sym) checkCanEval(c, a[i]) - c.gen(lowerTupleUnpacking(c.graph, a, c.getOwner)) + c.gen(lowerTupleUnpacking(c.graph, a, c.idgen, c.getOwner)) elif a[0].kind == nkSym: let s = a[0].sym checkCanEval(c, a[0]) @@ -2235,7 +2235,7 @@ proc genProc(c: PCtx; s: PSym): int = s.ast[miscPos] = x # thanks to the jmp we can add top level statements easily and also nest # procs easily: - let body = transformBody(c.graph, s, cache = not isCompileTimeProc(s)) + let body = transformBody(c.graph, c.idgen, s, cache = not isCompileTimeProc(s)) let procStart = c.xjmp(body, opcJmp, 0) var p = PProc(blocks: @[], sym: s) let oldPrc = c.prc diff --git a/compiler/vmmarshal.nim b/compiler/vmmarshal.nim index 696183943..979e5de8a 100644 --- a/compiler/vmmarshal.nim +++ b/compiler/vmmarshal.nim @@ -136,7 +136,8 @@ proc storeAny*(s: var string; t: PType; a: PNode; conf: ConfigRef) = proc loadAny(p: var JsonParser, t: PType, tab: var Table[BiggestInt, PNode]; cache: IdentCache; - conf: ConfigRef): PNode = + conf: ConfigRef; + idgen: IdGenerator): PNode = case t.kind of tyNone: assert false of tyBool: @@ -170,7 +171,7 @@ proc loadAny(p: var JsonParser, t: PType, next(p) result = newNode(nkBracket) while p.kind != jsonArrayEnd and p.kind != jsonEof: - result.add loadAny(p, t.elemType, tab, cache, conf) + result.add loadAny(p, t.elemType, tab, cache, conf, idgen) if p.kind == jsonArrayEnd: next(p) else: raiseParseErr(p, "']' end of array expected") of tySequence: @@ -182,7 +183,7 @@ proc loadAny(p: var JsonParser, t: PType, next(p) result = newNode(nkBracket) while p.kind != jsonArrayEnd and p.kind != jsonEof: - result.add loadAny(p, t.elemType, tab, cache, conf) + result.add loadAny(p, t.elemType, tab, cache, conf, idgen) if p.kind == jsonArrayEnd: next(p) else: raiseParseErr(p, "") else: @@ -198,7 +199,7 @@ proc loadAny(p: var JsonParser, t: PType, next(p) if i >= t.len: raiseParseErr(p, "too many fields to tuple type " & typeToString(t)) - result.add loadAny(p, t[i], tab, cache, conf) + result.add loadAny(p, t[i], tab, cache, conf, idgen) inc i if p.kind == jsonObjectEnd: next(p) else: raiseParseErr(p, "'}' end of object expected") @@ -219,8 +220,8 @@ proc loadAny(p: var JsonParser, t: PType, if pos >= result.len: setLen(result.sons, pos + 1) let fieldNode = newNode(nkExprColonExpr) - fieldNode.add newSymNode(newSym(skField, ident, nil, unknownLineInfo)) - fieldNode.add loadAny(p, field.typ, tab, cache, conf) + fieldNode.add newSymNode(newSym(skField, ident, nextId(idgen), nil, unknownLineInfo)) + fieldNode.add loadAny(p, field.typ, tab, cache, conf, idgen) result[pos] = fieldNode if p.kind == jsonObjectEnd: next(p) else: raiseParseErr(p, "'}' end of object expected") @@ -229,7 +230,7 @@ proc loadAny(p: var JsonParser, t: PType, next(p) result = newNode(nkCurly) while p.kind != jsonArrayEnd and p.kind != jsonEof: - result.add loadAny(p, t.lastSon, tab, cache, conf) + result.add loadAny(p, t.lastSon, tab, cache, conf, idgen) next(p) if p.kind == jsonArrayEnd: next(p) else: raiseParseErr(p, "']' end of array expected") @@ -248,7 +249,7 @@ proc loadAny(p: var JsonParser, t: PType, if p.kind == jsonInt: let idx = p.getInt next(p) - result = loadAny(p, t.lastSon, tab, cache, conf) + result = loadAny(p, t.lastSon, tab, cache, conf, idgen) tab[idx] = result else: raiseParseErr(p, "index for ref type expected") if p.kind == jsonArrayEnd: next(p) @@ -276,14 +277,14 @@ proc loadAny(p: var JsonParser, t: PType, return raiseParseErr(p, "float expected") of tyRange, tyGenericInst, tyAlias, tySink: - result = loadAny(p, t.lastSon, tab, cache, conf) + result = loadAny(p, t.lastSon, tab, cache, conf, idgen) else: internalError conf, "cannot marshal at compile-time " & t.typeToString -proc loadAny*(s: string; t: PType; cache: IdentCache; conf: ConfigRef): PNode = +proc loadAny*(s: string; t: PType; cache: IdentCache; conf: ConfigRef; idgen: IdGenerator): PNode = var tab = initTable[BiggestInt, PNode]() var p: JsonParser open(p, newStringStream(s), "unknown file") next(p) - result = loadAny(p, t, tab, cache, conf) + result = loadAny(p, t, tab, cache, conf, idgen) close(p) diff --git a/compiler/vmops.nim b/compiler/vmops.nim index a3d4d74db..47df3d24f 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -268,7 +268,7 @@ proc registerAdditionalOps*(c: PCtx) = fn.typ.n[0][effectIndex] != nil: var list = newNodeI(nkBracket, fn.info) for e in fn.typ.n[0][effectIndex]: - list.add opMapTypeInstToAst(c.cache, e.typ.skipTypes({tyRef}), e.info) + list.add opMapTypeInstToAst(c.cache, e.typ.skipTypes({tyRef}), e.info, c.idgen) setResult(a, list) registerCallback c, "stdlib.effecttraits.getRaisesListImpl", proc (a: VmArgs) = diff --git a/lib/system/iterators.nim b/lib/system/iterators.nim index 99ad6eef6..c8c0edbd4 100644 --- a/lib/system/iterators.nim +++ b/lib/system/iterators.nim @@ -82,7 +82,7 @@ iterator mitems*(a: var cstring): var char {.inline.} = yield a[i] inc(i) -iterator items*(E: typedesc[enum]): E = +iterator items*[T: enum](E: typedesc[T]): T = ## Iterates over the values of the enum ``E``. for v in low(E) .. high(E): yield v From b56a037183fa572c85d4f33d0f92cca77586019f Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 26 Oct 2020 03:10:38 +0200 Subject: [PATCH 0831/3768] workaround #15713 disable freebsd tssl.nim (#15718) --- tests/stdlib/tssl.nim | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/stdlib/tssl.nim b/tests/stdlib/tssl.nim index 3c2b6be2b..7625f3694 100644 --- a/tests/stdlib/tssl.nim +++ b/tests/stdlib/tssl.nim @@ -1,7 +1,9 @@ discard """ joinable: false + disabled: "freebsd" + disabled: "openbsd" """ - +# disabled: pending bug #15713 import net, nativesockets when defined(posix): import os, posix From d4c2e2c53ffe86a78c302d7002f6baf346540d17 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 26 Oct 2020 10:13:37 +0200 Subject: [PATCH 0832/3768] fix #15704 #15597 wrong VM register was freed (#15705) * fix #15704 #15597 wrong VM register was freed * same treatment for nkCheckedFieldExpr * note concerning HighRegisterPressure * bump NimPatch * Update lib/system.nim Co-authored-by: Andreas Rumpf --- compiler/vmgen.nim | 6 +- tests/vm/tvmmisc.nim | 130 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 134 insertions(+), 2 deletions(-) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 5ba15bb2b..f084ab7ba 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -247,6 +247,8 @@ proc freeTemp(c: PCtx; r: TRegister) = proc getTempRange(cc: PCtx; n: int; kind: TSlotKind): TRegister = # if register pressure is high, we re-use more aggressively: let c = cc.prc + # we could also customize via the following (with proper caching in ConfigRef): + # let highRegisterPressure = cc.config.getConfigVar("vm.highRegisterPressure", "40").parseInt if c.maxSlots >= HighRegisterPressure or c.maxSlots+n >= high(TRegister): for i in 0..c.maxSlots-n: if not c.slots[i].inUse: @@ -1508,14 +1510,14 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = let tmp = c.genx(ri) c.preventFalseAlias(le[0], opcWrObj, objR, idx, tmp) c.freeTemp(tmp) - c.freeTemp(idx) + # c.freeTemp(idx) # BUGFIX, see nkDotExpr c.freeTemp(objR) of nkDotExpr: let dest = c.genx(le[0], {gfNode}) let idx = genField(c, le[1]) let tmp = c.genx(ri) c.preventFalseAlias(le, opcWrObj, dest, idx, tmp) - c.freeTemp(idx) + # c.freeTemp(idx) # BUGFIX: idx is an immediate (field position), not a register c.freeTemp(tmp) c.freeTemp(dest) of nkDerefExpr, nkHiddenDeref: diff --git a/tests/vm/tvmmisc.nim b/tests/vm/tvmmisc.nim index 61a187faf..f05bb6dec 100644 --- a/tests/vm/tvmmisc.nim +++ b/tests/vm/tvmmisc.nim @@ -281,3 +281,133 @@ block: # bug #8007 # OK with seq & object variants const d = @[Cost(kind: Fixed, cost: 999), Cost(kind: Dynamic, handler: foo)] doAssert $d == "@[(kind: Fixed, cost: 999), (kind: Dynamic, handler: ...)]" + +block: # VM wrong register free causes errors in unrelated code + block: # bug #15597 + #[ + Error: unhandled exception: 'sym' is not accessible using discriminant 'kind' of type 'TNode' [FieldDefect] + in /Users/timothee/git_clone/nim/Nim_prs/compiler/vm.nim(1176) rawExecute + in opcIndCall + in let prc = if not isClosure: bb.sym else: bb[0].sym + ]# + proc bar2(head: string): string = "asdf" + proc gook(u1: int) = discard + + type PathEntry = object + kind: int + path: string + + iterator globOpt(): int = + var u1: int + + gook(u1) + gook(u1) + gook(u1) + gook(u1) + gook(u1) + gook(u1) + gook(u1) + gook(u1) + gook(u1) + gook(u1) + gook(u1) + gook(u1) + gook(u1) + gook(u1) + + var entry = PathEntry() + entry.path = bar2("") + if false: + echo "here2" + + proc processAux(a: float) = discard + + template bar(iter: untyped): untyped = + var ret: float + for x in iter: break + ret + + proc main() = + processAux(bar(globOpt())) + static: main() + + block: # ditto + # D20201024T133245 + type Deque = object + proc initDeque2(initialSize: int = 4): Deque = Deque() + proc len2(a: Deque): int = 2 + proc baz(dir: string): bool = true + proc bar2(head: string): string = "asdf" + proc bar3(path: var string) = path = path + + type PathEntry = object + kind: int + path: string + + proc initGlobOpt(dir: string, a1=false,a2=false,a3=false,a4=false): string = dir + + iterator globOpt(dir: string): int = + var stack = initDeque2() + doAssert baz("") + let z = stack.len2 + if stack.len2 >= 0: + var entry = PathEntry() + let current = if true: stack.len2 else: stack.len2 + entry.path = bar2("") + bar3(entry.path) + if false: + echo "here2" # comment here => you get same error as https://github.com/nim-lang/Nim/issues/15704 + + proc processAux(a: float) = discard + + template bar(iter: untyped): untyped = + var ret: float + for x in iter: break + ret + proc main() = + processAux(bar(globOpt(initGlobOpt(".")))) + static: main() + + block: # bug #15704 + #[ + Error: attempt to access a nil address kind: rkFloat + ]# + type Deque = object + proc initDeque2(initialSize: int = 4): Deque = Deque() + proc len2(a: Deque): int = 2 + + proc baz(dir: string): bool = true + proc bar2(head: string): string = "asdf" + proc bar3(path: var string) = path = path + + type PathEntry = object + kind: int + path: string + depth: int + + proc initGlobOpt(dir: string, a1=false,a2=false,a3=false,a4=false): string = + dir + + iterator globOpt(dir: string): int = + var stack = initDeque2() + doAssert baz("") + let z = stack.len2 + var a5: int + if stack.len2 >= 0: + var entry = PathEntry() + if false: + echo "here" + let current = if true: stack.len2 else: stack.len2 + entry.depth = 1 + entry.path = bar2("") + bar3(entry.path) + proc processAux(a: float) = discard + template bar(iter: untyped): untyped = + var ret: float + for x in iter: + break + ret + const dir = "." + proc main() = + processAux(bar(globOpt(initGlobOpt(dir)))) + static: main() From 86d7726fb8044d4e2fc55b0eac3d1d42f5ffc94f Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 26 Oct 2020 10:53:39 +0200 Subject: [PATCH 0833/3768] workaround #15725 disable freebsd tblocking_channel.nim (#15726) --- tests/parallel/tblocking_channel.nim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/parallel/tblocking_channel.nim b/tests/parallel/tblocking_channel.nim index 6ec0e1588..eb5fcb715 100644 --- a/tests/parallel/tblocking_channel.nim +++ b/tests/parallel/tblocking_channel.nim @@ -1,6 +1,8 @@ discard """ output: "" +disabled: "freebsd" """ +# disabled pending bug #15725 import threadpool, os var chan: Channel[int] From e284445e514283e09b705b890b4ad4c94288aba0 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Mon, 26 Oct 2020 06:36:12 -0300 Subject: [PATCH 0834/3768] Fix Prelude (#15714) * ReSync with Devel * Fix prelude for JS target * ReSync devel * Fix prelude for JS target --- changelog.md | 1 + lib/prelude.nim | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 1885eaacd..8bd556e33 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,7 @@ ## Standard library additions and changes +- `prelude` now works with the JavaScript target. ## Language changes diff --git a/lib/prelude.nim b/lib/prelude.nim index 5ad561158..d24f35513 100644 --- a/lib/prelude.nim +++ b/lib/prelude.nim @@ -18,4 +18,5 @@ # .. code-block:: nim # import os, strutils, times, parseutils, parseopt, hashes, tables, sets -import os, strutils, times, parseutils, parseopt, hashes, tables, sets +import os, strutils, times, parseutils, hashes, tables, sets +when not defined(js): import parseopt From 2ebca5e87e1181498261d5262611a4c7c7738341 Mon Sep 17 00:00:00 2001 From: Bo Lingen Date: Mon, 26 Oct 2020 04:54:15 -0500 Subject: [PATCH 0835/3768] Revert "workaround https://github.com/citycide/glob/issues/49 glob broken (#15647)" (#15728) This reverts commit 4ab1bd7e3301e22f0a5a9c62f9c90636932fde80. --- testament/important_packages.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index c94f3e98d..6a265f39a 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -39,9 +39,9 @@ pkg1 "elvis" pkg1 "fidget" pkg1 "fragments", "nim c -r fragments/dsl.nim" pkg1 "gara" +pkg1 "glob" pkg1 "ggplotnim", "nim c -d:noCairo -r tests/tests.nim" # pkg1 "gittyup", "nimble test", "https://github.com/disruptek/gittyup" -# pkg1 "glob" # pending https://github.com/citycide/glob/issues/49 pkg1 "gnuplot", "nim c gnuplot.nim" pkg1 "hts", "nim c -o:htss src/hts.nim" # pkg1 "httpauth" From b2740f5c63088c0f0f723086c1742dd057355bf6 Mon Sep 17 00:00:00 2001 From: Danil Yarantsev Date: Mon, 26 Oct 2020 13:03:44 +0300 Subject: [PATCH 0836/3768] Add support to the latest LibreSSL version (#15715) [backport:1.2] [backport:1.4] --- lib/wrappers/openssl.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim index becabad99..694152db1 100644 --- a/lib/wrappers/openssl.nim +++ b/lib/wrappers/openssl.nim @@ -64,9 +64,9 @@ elif useWinVersion: from winlean import SocketHandle else: when defined(osx): - const versions = "(.1.1|.38|.39|.41|.43|.44|.45|.46|.47|.10|.1.0.2|.1.0.1|.1.0.0|.0.9.9|.0.9.8|)" + const versions = "(.1.1|.38|.39|.41|.43|.44|.45|.46|.47|.48|.10|.1.0.2|.1.0.1|.1.0.0|.0.9.9|.0.9.8|)" else: - const versions = "(.1.1|.1.0.2|.1.0.1|.1.0.0|.0.9.9|.0.9.8|.47|.46|.45|.44|.43|.41|.39|.38|.10|)" + const versions = "(.1.1|.1.0.2|.1.0.1|.1.0.0|.0.9.9|.0.9.8|.48|.47|.46|.45|.44|.43|.41|.39|.38|.10|)" when defined(macosx): const From a87617956f9a521eac3663df6a6a9862bc7ae87e Mon Sep 17 00:00:00 2001 From: Jason Beetham Date: Mon, 26 Oct 2020 04:06:18 -0600 Subject: [PATCH 0837/3768] Fixed iteration limit hit from execproc (#15723) [backport:1.2] [backport:1.4] When calling procs from Nim in Nimscript you could hit the VM iteration limit even though the code is functioning properly. This resolves that by making the iteration limit reset eachtime you call a proc. --- compiler/vm.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/vm.nim b/compiler/vm.nim index 185975ec3..ac2feac26 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -2078,6 +2078,7 @@ proc execute(c: PCtx, start: int): PNode = result = rawExecute(c, start, tos).regToNode proc execProc*(c: PCtx; sym: PSym; args: openArray[PNode]): PNode = + c.loopIterations = c.config.maxLoopIterationsVM if sym.kind in routineKinds: if sym.typ.len-1 != args.len: localError(c.config, sym.info, From ea6c28249ae7107fba954f90dac31132564dcaad Mon Sep 17 00:00:00 2001 From: Andy Davidoff Date: Mon, 26 Oct 2020 09:15:56 -0400 Subject: [PATCH 0838/3768] add criterion to important packages (#15604) * add criterion to important packages * use a special nimscript for criterion tests * fix criterion speed differently Co-authored-by: Andreas Rumpf --- testament/important_packages.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 6a265f39a..ef7dded4b 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -31,6 +31,7 @@ pkg1 "cligen", "nim c --path:. -r cligen.nim" pkg1 "combparser", "nimble test --gc:orc" pkg1 "compactdict" pkg1 "comprehension", "nimble test", "https://github.com/alehander42/comprehension" +pkg1 "criterion" pkg1 "dashing", "nim c tests/functional.nim" pkg1 "delaunay" pkg1 "docopt" From ae8eb1ec3dab5a9384473c7b37c240da5861a784 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yanis=20Zafir=C3=B3pulos?= <1265028+drkameleon@users.noreply.github.com> Date: Mon, 26 Oct 2020 18:47:33 +0100 Subject: [PATCH 0839/3768] Copy editing (#15733) * Minor fixes * Minor fixes * Minor fixes * Minor fixes --- doc/contributing.rst | 36 ++++++++++++++++++------------------ doc/gc.rst | 32 ++++++++++++++++---------------- readme.md | 20 ++++++++++---------- security.md | 4 ++-- 4 files changed, 46 insertions(+), 46 deletions(-) diff --git a/doc/contributing.rst b/doc/contributing.rst index 15017cd84..f570336e2 100644 --- a/doc/contributing.rst +++ b/doc/contributing.rst @@ -49,7 +49,7 @@ which only gets executed when the tester is building the file. Each test should be in a separate ``block:`` statement, such that each has its own scope. Use boolean conditions and ``doAssert`` for the -testing by itself, don't rely on echo statements or similar; in particular avoid +testing by itself, don't rely on echo statements or similar; in particular, avoid things like `echo "done"`. Sample test: @@ -69,7 +69,7 @@ Sample test: # doAssert with `not` can now be done as follows: doAssert not (1 == 2) -Always refer to a github issue using the following exact syntax: `bug #1234` as shown +Always refer to a GitHub issue using the following exact syntax: `bug #1234` as shown above, so that it's consistent and easier to search or for tooling. Some browser extensions (e.g. https://github.com/sindresorhus/refined-github) will even turn those in clickable links when it works. @@ -100,7 +100,7 @@ Possible keys are: For a full spec, see here: ``testament/specs.nim`` -An example for a test: +An example of a test: .. code-block:: nim @@ -162,7 +162,7 @@ Comparing tests Test failures can be grepped using ``Failure:``. -The tester can compare two test runs. First, you need to create the +The tester can compare two test runs. First, you need to create a reference test. You'll also need to the commit id, because that's what the tester needs to know in order to compare the two. @@ -344,7 +344,7 @@ which for which ``nim doc`` ignores ``-d:release``). .. _delegate_printing: Delegate printing to caller: return ``string`` instead of calling ``echo`` -rationale: it's more flexible (e.g. allows caller to call custom printing, +rationale: it's more flexible (e.g. allows the caller to call custom printing, including prepending location info, writing to log files, etc). .. code-block:: nim @@ -364,7 +364,7 @@ unless stack allocation is needed (e.g. for efficiency). .. _use_doAssert_not_echo: Tests (including in testament) should always prefer assertions over ``echo``, except when that's not possible. It's more precise, easier for readers and -maintaners to where expected values refer to. See for example +maintainers to where expected values refer to. See for example https://github.com/nim-lang/Nim/pull/9335 and https://forum.nim-lang.org/t/4089 .. code-block:: nim @@ -386,7 +386,7 @@ General commit rules stable releases" and the tag ``[backport:$VERSION]`` for backporting to the given $VERSION. -2. If you introduce changes which affect backwards compatibility, +2. If you introduce changes which affect backward compatibility, make breaking changes, or have PR which is tagged as ``[feature]``, the changes should be mentioned in `the changelog `_. @@ -404,7 +404,7 @@ General commit rules 4. Changes should not introduce any trailing whitespace. Always check your changes for whitespace errors using ``git diff --check`` - or add following ``pre-commit`` hook: + or add the following ``pre-commit`` hook: .. code-block:: sh @@ -416,7 +416,7 @@ General commit rules ``Fixes #123; refs #124`` - indicates that issue ``#123`` is completely fixed (github may automatically + indicates that issue ``#123`` is completely fixed (GitHub may automatically close it when the PR is committed), wheres issue ``#124`` is referenced (e.g.: partially fixed) and won't close the issue when committed. @@ -431,7 +431,7 @@ General commit rules 7. Do not mix pure formatting changes (e.g. whitespace changes, nimpretty) or automated changes (e.g. nimfix) with other code changes: these should be in - separate commits (and the merge on github should not squash these into 1). + separate commits (and the merge on GitHub should not squash these into 1). Continuous Integration (CI) @@ -444,9 +444,9 @@ Continuous Integration (CI) `_ and `Travis `_. -2. Consider enabling CI (azure, github actions and builds.sr.ht) in your own Nim fork, and +2. Consider enabling CI (azure, GitHub actions and builds.sr.ht) in your own Nim fork, and waiting for CI to be green in that fork (fixing bugs as needed) before - opening your PR in original Nim repo, so as to reduce CI congestion. Same + opening your PR in the original Nim repo, so as to reduce CI congestion. Same applies for updates on a PR: you can test commits on a separate private branch before updating the main PR. @@ -463,21 +463,21 @@ Debugging CI failures, flaky tests, etc * Azure: if on your own fork, it's possible from inside azure console (e.g. `dev.azure.com/username/username/_build/results?buildId=1430&view=results`) via `rerun failed jobs` on top. - If either on you own fork or in Nim repo, it's possible from inside github UI + If either on you own fork or in Nim repo, it's possible from inside GitHub UI under checks tab, see https://github.com/timotheecour/Nim/issues/211#issuecomment-629751569 - * github actions: under "Checks" tab, click "Re-run jobs" in the right. + * GitHub actions: under "Checks" tab, click "Re-run jobs" in the right. * builds.sr.ht: create a sourcehut account so you can restart a PR job as illustrated Code reviews ------------ -1. Whenever possible, use github's new 'Suggested change' in code reviews, which +1. Whenever possible, use GitHub's new 'Suggested change' in code reviews, which saves time explaining the change or applying it; see also https://forum.nim-lang.org/t/4317 -2. When reviewing large diffs that may involve code moving around, github's interface - doesn't help much as it doesn't highlight moves. Instead you can use something +2. When reviewing large diffs that may involve code moving around, GitHub's interface + doesn't help much as it doesn't highlight moves. Instead, you can use something like this, see visual results `here `_: .. code-block:: sh @@ -485,7 +485,7 @@ Code reviews git fetch origin pull/10431/head && git checkout FETCH_HEAD git diff --color-moved-ws=allow-indentation-change --color-moved=blocks HEAD^ -3. In addition, you can view github-like diffs locally to identify what was changed +3. In addition, you can view GitHub-like diffs locally to identify what was changed within a code block using `diff-highlight` or `diff-so-fancy`, e.g.: .. code-block:: sh diff --git a/doc/gc.rst b/doc/gc.rst index 84a1338c3..23c4f64cf 100644 --- a/doc/gc.rst +++ b/doc/gc.rst @@ -26,8 +26,8 @@ To choose the memory management strategy use the ``--gc:`` switch. - ``--gc:refc``. This is the default GC. It's a deferred reference counting based garbage collector - with a simple Mark&Sweep backup GC in order to collect cycles. Heaps are thread local. -- ``--gc:markAndSweep``. Simple Mark-And-Sweep based garbage collector. Heaps are thread local. + with a simple Mark&Sweep backup GC in order to collect cycles. Heaps are thread-local. +- ``--gc:markAndSweep``. Simple Mark-And-Sweep based garbage collector. Heaps are thread-local. - ``--gc:boehm``. Boehm based garbage collector, it offers a shared heap. - ``--gc:go``. Go's garbage collector, useful for interoperability with Go. Offers a shared heap. - ``--gc:arc``. Plain reference counting with @@ -36,10 +36,10 @@ To choose the memory management strategy use the ``--gc:`` switch. cause memory leaks, beware. - ``--gc:orc``. Same as ``--gc:arc`` but adds a cycle collector based on "trial deletion". - Unfortunately that makes its performance profile hard to reason about so it is less - useful for hard realtime systems. + Unfortunately, that makes its performance profile hard to reason about so it is less + useful for hard real-time systems. -- ``--gc:none``. No memory management strategy nor garbage collector. Allocated memory is +- ``--gc:none``. No memory management strategy nor a garbage collector. Allocated memory is simply never freed. You should use ``--gc:arc`` instead. @@ -71,10 +71,10 @@ The cycle collector can be en-/disabled independently from the other parts of the garbage collector with ``GC_enableMarkAndSweep`` and ``GC_disableMarkAndSweep``. -Soft realtime support +Soft real-time support --------------------- -To enable realtime support, the symbol `useRealtimeGC`:idx: needs to be +To enable real-time support, the symbol `useRealtimeGC`:idx: needs to be defined via ``--define:useRealtimeGC`` (you can put this into your config file as well). With this switch the garbage collector supports the following operations: @@ -85,7 +85,7 @@ With this switch the garbage collector supports the following operations: The unit of the parameters ``maxPauseInUs`` and ``us`` is microseconds. -These two procs are the two modus operandi of the realtime garbage collector: +These two procs are the two modus operandi of the real-time garbage collector: (1) GC_SetMaxPause Mode @@ -98,7 +98,7 @@ These two procs are the two modus operandi of the realtime garbage collector: (2) GC_step Mode This allows the garbage collector to perform some work for up to ``us`` time. - This is useful to call in a main loop to ensure the garbage collector can do its work. + This is useful to call in the main loop to ensure the garbage collector can do its work. To bind all garbage collector activity to a ``GC_step`` call, deactivate the garbage collector with ``GC_disable`` at program startup. If ``strongAdvice`` is set to ``true``, @@ -106,13 +106,13 @@ These two procs are the two modus operandi of the realtime garbage collector: Otherwise, the garbage collector may decide not to do anything, if there is not much garbage to collect. You may also specify the current stack size via ``stackSize`` parameter. - It can improve performance, when you know that there are no unique Nim - references below certain point on the stack. Make sure the size you specify - is greater than the potential worst case size. + It can improve performance when you know that there are no unique Nim + references below a certain point on the stack. Make sure the size you specify + is greater than the potential worst-case size. -These procs provide a "best effort" realtime guarantee; in particular the +These procs provide a "best effort" real-time guarantee; in particular the cycle collector is not aware of deadlines. Deactivate it to get more -predictable realtime behaviour. Tests show that a 1ms max pause +predictable real-time behaviour. Tests show that a 1ms max pause time will be met in almost all cases on modern CPUs (with the cycle collector disabled). @@ -120,14 +120,14 @@ disabled). Time measurement with garbage collectors ---------------------------------------- -The garbage collectors's way of measuring time uses +The garbage collectors' way of measuring time uses (see ``lib/system/timers.nim`` for the implementation): 1) ``QueryPerformanceCounter`` and ``QueryPerformanceFrequency`` on Windows. 2) ``mach_absolute_time`` on Mac OS X. 3) ``gettimeofday`` on Posix systems. -As such it supports a resolution of nanoseconds internally; however the API +As such it supports a resolution of nanoseconds internally; however, the API uses microseconds for convenience. Define the symbol ``reportMissedDeadlines`` to make the diff --git a/readme.md b/readme.md index 8fcf4d2bb..bfd153143 100644 --- a/readme.md +++ b/readme.md @@ -2,7 +2,7 @@ [![Build Status](https://dev.azure.com/nim-lang/Nim/_apis/build/status/nim-lang.Nim?branchName=devel)](https://dev.azure.com/nim-lang/Nim/_build/latest?definitionId=1&branchName=devel) -This repository contains the Nim compiler, Nim's stdlib, tools and documentation. +This repository contains the Nim compiler, Nim's stdlib, tools, and documentation. For more information about Nim, including downloads and documentation for the latest release, check out [Nim's website][nim-site] or [bleeding edge docs](https://nim-lang.github.io/Nim/). @@ -37,7 +37,7 @@ architecture combinations: * Linux (most, if not all, distributions) - x86, x86_64, ppc64 and armv6l * Mac OS X (10.04 or greater) - x86, x86_64 and ppc64 -More platforms are supported, however they are not tested regularly and they +More platforms are supported, however, they are not tested regularly and they may not be as stable as the above-listed platforms. Compiling the Nim compiler is quite straightforward if you follow these steps: @@ -60,10 +60,10 @@ Next, to build from source you will need: * [MinGW32.7z](https://nim-lang.org/download/mingw32.7z) * [MinGW64.7z](https://nim-lang.org/download/mingw64.7z) -**Windows Note: Cygwin and similar posix runtime environments are not supported.** +**Windows Note: Cygwin and similar POSIX runtime environments are not supported.** Then, if you are on a \*nix system or Windows, the following steps should compile -Nim from source using ``gcc``, ``git`` and the ``koch`` build tool. +Nim from source using ``gcc``, ``git``, and the ``koch`` build tool. **Note: The following commands are for the development version of the compiler.** For most users, installing the latest stable version is enough. Check out @@ -72,14 +72,14 @@ the installation instructions on the website to do so: https://nim-lang.org/inst For package maintainers: see [packaging guidelines](https://nim-lang.github.io/Nim/packaging.html). -First get Nim from github: +First, get Nim from github: ``` git clone https://github.com/nim-lang/Nim.git cd Nim ``` -Next run the appropriate build shell script for your platform: +Next, run the appropriate build shell script for your platform: * `build_all.sh` (Linux, Mac) * `build_all.bat` (Windows) @@ -88,7 +88,7 @@ Windows requires a number of other dependencies that you may need to install inc PCRE and OpenSSL. Nim hosts a zip package containing known working versions of the required DLLs [here](https://nim-lang.org/download/dlls.zip). -Finally, once you have finished the build steps (on Windows, Mac or Linux) you +Finally, once you have finished the build steps (on Windows, Mac, or Linux) you should add the ``bin`` directory to your PATH. See also [rebuilding the compiler](doc/intern.rst#rebuilding-the-compiler). @@ -140,11 +140,11 @@ you should familiarize yourself with the following repository structure: * ``pure/`` - modules in the standard library written in pure Nim. * ``impure/`` - modules in the standard library written in pure Nim with dependencies written in other languages. - * ``wrappers/`` - modules which wrap dependencies written in other languages. + * ``wrappers/`` - modules that wrap dependencies written in other languages. * ``tests/`` - contains categorized tests for the compiler and standard library. * ``tools/`` - the tools including ``niminst`` and ``nimweb`` (mostly invoked via ``koch``). -* ``koch.nim`` - tool used to bootstrap Nim, generate C sources, build the website, +* ``koch.nim`` - the tool used to bootstrap Nim, generate C sources, build the website, and generate the documentation. If you are not familiar with making a pull request using GitHub and/or git, please @@ -198,7 +198,7 @@ You can also see a list of all our sponsors/backers from various payment service ## License The compiler and the standard library are licensed under the MIT license, except -for some modules which explicitly state otherwise. As a result you may use any +for some modules which explicitly state otherwise. As a result, you may use any compatible license (essentially any license) for your own programs developed with Nim. You are explicitly permitted to develop commercial applications using Nim. diff --git a/security.md b/security.md index f1eaf3290..e8d31b1b9 100644 --- a/security.md +++ b/security.md @@ -12,6 +12,6 @@ We do not backport security fixes to older releases. Please do not report vulnerabilities via GitHub issues. -If you have discovered a vulnerability, it is the best to notify us about it via -security@nim-lang.org in order to setup a meeting where we can discuss the next +If you have discovered a vulnerability, it is best to notify us about it via +security@nim-lang.org in order to set up a meeting where we can discuss the next steps. From 3a69f14621156358e2fc154881f60459d04814a1 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Mon, 26 Oct 2020 14:49:01 -0300 Subject: [PATCH 0840/3768] Documentation only iup (#15732) * ReSync with Devel * ReSync * Add Examples for IUP, based from official doc --- lib/wrappers/iup.nim | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/lib/wrappers/iup.nim b/lib/wrappers/iup.nim index 3c1ff1483..0242d7981 100644 --- a/lib/wrappers/iup.nim +++ b/lib/wrappers/iup.nim @@ -34,6 +34,46 @@ # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # **************************************************************************** + +## Wrapper for the `IUP native GUI library `_. +## +## Requires `iup.dll` on Windows, `libiup.so` on Linux, `libiup.dylib` on OS X. +## Compile with `-d:nimDebugDlOpen` to debug the shared library opening. +## +## Examples +## ======== +## +## .. image:: http://webserver2.tecgraf.puc-rio.br/iup/en/tutorial/example2_1.png +## +## .. code-block:: nim +## import os, iup +## var argc = create(cint) +## argc[] = paramCount().cint +## var argv = allocCstringArray(commandLineParams()) +## assert open(argc, argv.addr) == 0 # UIP requires calling open() +## message(r"Hello world 1", r"Hello world") # Message popup +## close() # UIP requires calling close() +## +## +## .. image:: http://webserver2.tecgraf.puc-rio.br/iup/en/tutorial/example3_1.png +## +## .. code-block:: nim +## import os, iup +## var argc = create(cint) +## argc[] = paramCount().cint +## var argv = allocCstringArray(commandLineParams()) +## assert open(argc, argv.addr) == 0 # UIP requires open() +## let textarea = text(nil) # Text widget. +## setAttribute(textarea, r"MULTILINE", r"YES") # Set text widget to multiline. +## setAttribute(textarea, r"EXPAND", r"YES") # Set text widget to auto expand. +## let layout = vbox(textarea) # Vertical layout. +## let window = dialog(layout) # Dialog window. +## setAttribute(window, "TITLE", "Nim Notepad") # Set window title. +## showXY(window, IUP_CENTER, IUP_CENTER) # Show window. +## mainLoop() # Main loop. +## close() # UIP requires calling close() + + when defined(windows): const dllname = "iup(|30|27|26|25|24).dll" elif defined(macosx): From cf01945f54f099c268850c720986ad6c9bbdb51e Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 26 Oct 2020 21:08:57 +0100 Subject: [PATCH 0841/3768] ensure the Nim compiler works with --experimental:strictFuncs --experimental:views [backport:1.4] (#15737) --- compiler/varpartitions.nim | 9 ++++++++- tests/views/tcan_compile_nim.nim | 4 ++++ tests/views/tdont_mutate.nim | 11 +++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 tests/views/tcan_compile_nim.nim diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim index f831d33cc..49221654d 100644 --- a/compiler/varpartitions.nim +++ b/compiler/varpartitions.nim @@ -545,6 +545,9 @@ proc borrowingCall(c: var Partitions; destType: PType; n: PNode; i: int) = localError(c.config, n[i].info, "cannot determine the target of the borrow") proc borrowingAsgn(c: var Partitions; dest, src: PNode) = + proc mutableParameter(n: PNode): bool {.inline.} = + result = n.kind == nkSym and n.sym.kind == skParam and n.sym.typ.kind == tyVar + if dest.kind == nkSym: if directViewType(dest.typ) != noView: borrowFrom(c, dest.sym, src) @@ -559,7 +562,11 @@ proc borrowingAsgn(c: var Partitions; dest, src: PNode) = if vid >= 0: c.s[vid].flags.incl viewDoesMutate of immutableView: - localError(c.config, dest.info, "attempt to mutate a borrowed location from an immutable view") + if dest.kind == nkBracketExpr and dest[0].kind == nkHiddenDeref and + mutableParameter(dest[0][0]): + discard "remains a mutable location anyhow" + else: + localError(c.config, dest.info, "attempt to mutate a borrowed location from an immutable view") of noView: discard "nothing to do" proc containsPointer(t: PType): bool = diff --git a/tests/views/tcan_compile_nim.nim b/tests/views/tcan_compile_nim.nim new file mode 100644 index 000000000..e990606cd --- /dev/null +++ b/tests/views/tcan_compile_nim.nim @@ -0,0 +1,4 @@ +discard """ + cmd: "nim check --hints:on --experimental:strictFuncs --experimental:views compiler/nim.nim" + action: "compile" +""" diff --git a/tests/views/tdont_mutate.nim b/tests/views/tdont_mutate.nim index 907aff018..d243c7c7a 100644 --- a/tests/views/tdont_mutate.nim +++ b/tests/views/tdont_mutate.nim @@ -46,3 +46,14 @@ proc main() = echo i, ": ", x main() + +# This has to continue to work: + +type + PNode = ref object + TSrcGen = object + comStack: seq[PNode] + +proc pushCom(g: var TSrcGen, n: PNode) = + setLen(g.comStack, g.comStack.len + 1) + g.comStack[^1] = n From 3bdc0005211b0d543e0ff48ccf6bc5a9f2a2a30b Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 26 Oct 2020 22:19:05 +0200 Subject: [PATCH 0842/3768] [backport] fix #15595 procvar `==` works in VM (#15724) --- compiler/vm.nim | 6 ++++-- tests/vm/tvmmisc.nim | 24 ++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/compiler/vm.nim b/compiler/vm.nim index ac2feac26..761186dc3 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -994,8 +994,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let nb = regs[rb].node let nc = regs[rc].node if nb.kind != nc.kind: discard - elif (nb == nc) or (nb.kind == nkNilLit): ret = true - elif nb.kind == nkIntLit and nb.intVal == nc.intVal: # TODO: nkPtrLit + elif (nb == nc) or (nb.kind == nkNilLit): ret = true # intentional + elif sameConstant(nb, nc): ret = true + # this also takes care of procvar's, represented as nkTupleConstr, eg (nil, nil) + elif nb.kind == nkIntLit and nc.kind == nkIntLit and nb.intVal == nc.intVal: # TODO: nkPtrLit let tb = nb.getTyp let tc = nc.getTyp ret = tb.kind in PtrLikeKinds and tc.kind == tb.kind diff --git a/tests/vm/tvmmisc.nim b/tests/vm/tvmmisc.nim index f05bb6dec..e4d6c308f 100644 --- a/tests/vm/tvmmisc.nim +++ b/tests/vm/tvmmisc.nim @@ -205,6 +205,30 @@ block: # bug #13081 static: doAssert j1.x1 == 12 +block: # bug #15595 + proc fn0()=echo 0 + proc fn1()=discard + proc main= + var local = 0 + proc fn2()=echo local + var a0 = fn0 + var a1 = fn1 + var a2 = fn2 + var a3: proc() + var a4: proc() + doAssert a0 == fn0 # bugfix + doAssert a1 == fn1 # ditto + doAssert a2 == fn2 # ditto + + doAssert fn0 != fn1 + + doAssert a2 != nil + doAssert a3 == nil # bugfix + + doAssert a3 == a4 # bugfix + static: main() + main() + # bug #15363 import sequtils From 25955440df3bf32b058ed27bdc2e619874f0d4ee Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Tue, 27 Oct 2020 07:27:02 +0100 Subject: [PATCH 0843/3768] Document implicit return values from procedures (#15738) --- doc/tut1.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/doc/tut1.rst b/doc/tut1.rst index 24874096c..68e74df52 100644 --- a/doc/tut1.rst +++ b/doc/tut1.rst @@ -641,6 +641,16 @@ initialised with the type's default value. Note that referential data types will be ``nil`` at the start of the procedure, and thus may require manual initialisation. +A procedure that does not have any ``return`` statement and does not use the +special ``result`` variable returns the value of its last expression. For example, +this procedure + +.. code-block:: nim + :test: "nim c $1" + proc helloWorld(): string = + "Hello, World!" + +returns the string "Hello, World!". Parameters ---------- From da476a872312a4353ed1623c5328be47d1d3b145 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 27 Oct 2020 08:35:10 +0200 Subject: [PATCH 0844/3768] fix #15740 --hint:conf now works more reliably (#15741) --- compiler/nimconf.nim | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim index 3e8c4d194..05f234ce6 100644 --- a/compiler/nimconf.nim +++ b/compiler/nimconf.nim @@ -287,16 +287,21 @@ proc loadConfigs*(cfg: RelativeFile; cache: IdentCache; conf: ConfigRef; idgen: if cfg == DefaultConfig: runNimScriptIfExists(pd / DefaultConfigNims) - for filename in configFiles: - # delayed to here so that `hintConf` is honored - rawMessage(conf, hintConf, filename.string) - let scriptFile = conf.projectFull.changeFileExt("nims") + let isMain = scriptFile == conf.projectFull + template showHintConf = + for filename in configFiles: + # delayed to here so that `hintConf` is honored + rawMessage(conf, hintConf, filename.string) + if isMain: + showHintConf() + configFiles.setLen 0 if conf.command != "nimsuggest": runNimScriptIfExists(scriptFile) else: - if scriptFile != conf.projectFull: + if not isMain: runNimScriptIfExists(scriptFile) else: # 'nimsuggest foo.nims' means to just auto-complete the NimScript file discard + showHintConf() From 58a1eec96a98adbc3fa36abc8cc770e1a1596443 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 27 Oct 2020 08:53:45 +0200 Subject: [PATCH 0845/3768] condsyms: add nimHasDeclaredLocs (#15735) --- compiler/condsyms.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 9771a48d3..1fbc30bbe 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -123,3 +123,4 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasStacktracesModule") defineSymbol("nimHasEffectTraitsModule") defineSymbol("nimHasCastPragmaBlocks") + defineSymbol("nimHasDeclaredLocs") From 066f3ebc2ae00af10f477dd9c07b94f1f161030d Mon Sep 17 00:00:00 2001 From: n5m <72841454+n5m@users.noreply.github.com> Date: Tue, 27 Oct 2020 07:48:14 +0000 Subject: [PATCH 0846/3768] add tests for Testament "reject" action (#15709) * add test for reject action * highlight that a compilation failure is expected * add the converse test for action=reject * add trailing newline * fix wording --- testament/tests/shouldfail/treject.nim | 7 +++++++ tests/testament/treject.nim | 6 ++++++ tests/testament/tshould_not_work.nim | 2 ++ 3 files changed, 15 insertions(+) create mode 100644 testament/tests/shouldfail/treject.nim create mode 100644 tests/testament/treject.nim diff --git a/testament/tests/shouldfail/treject.nim b/testament/tests/shouldfail/treject.nim new file mode 100644 index 000000000..aaf2b4a63 --- /dev/null +++ b/testament/tests/shouldfail/treject.nim @@ -0,0 +1,7 @@ +discard """ +action: "reject" +""" + +# Because we set action="reject", we expect this line not to compile. But the +# line does compile, therefore the test fails. +assert true diff --git a/tests/testament/treject.nim b/tests/testament/treject.nim new file mode 100644 index 000000000..be2db86a9 --- /dev/null +++ b/tests/testament/treject.nim @@ -0,0 +1,6 @@ +discard """ +action: "reject" +""" + +# this line does not compile, so the test should pass, since action="reject" +let x: int = "type mismatch" diff --git a/tests/testament/tshould_not_work.nim b/tests/testament/tshould_not_work.nim index 501e2c32f..e4d3c7be7 100644 --- a/tests/testament/tshould_not_work.nim +++ b/tests/testament/tshould_not_work.nim @@ -25,6 +25,8 @@ FAIL: tests/shouldfail/toutput.nim C Failure: reOutputsDiffer FAIL: tests/shouldfail/toutputsub.nim C Failure: reOutputsDiffer +FAIL: tests/shouldfail/treject.nim C +Failure: reFilesDiffer FAIL: tests/shouldfail/tsortoutput.nim C Failure: reOutputsDiffer FAIL: tests/shouldfail/ttimeout.nim C From 12143d90c828bb4142751a6511ce790aac8aca51 Mon Sep 17 00:00:00 2001 From: Regis Caillaud <35006197+Clonkk@users.noreply.github.com> Date: Tue, 27 Oct 2020 08:52:40 +0100 Subject: [PATCH 0847/3768] Add C function dup and dup2 posix to system/io (#15675) * * Add handle to dup and dup2 posix as duplicate and duplicateTo in std/ioutils. * Added small test & changelog entry * Fixed import in tioutils removed when isMainModule * * Nest test inside block. Rename proc var -> let in captureStdout * Renamed tmpfile to iotuils.txt * Added block: # duplicate, duplicateTo * Improved docstring * Clean non-idiomatic code * Added runnable examples * rm 2 trailing space in expected output * Made syntax prettier * Runnable example: file in getTempDir() * Tmp -> Temp * Fixed runnableExamples on windows --- changelog.md | 3 +- lib/std/ioutils.nim | 85 +++++++++++++++++++++++++++++++++++++++ tests/stdlib/tioutils.nim | 49 ++++++++++++++++++++++ 3 files changed, 136 insertions(+), 1 deletion(-) create mode 100644 lib/std/ioutils.nim create mode 100644 tests/stdlib/tioutils.nim diff --git a/changelog.md b/changelog.md index 8bd556e33..7f1034376 100644 --- a/changelog.md +++ b/changelog.md @@ -6,6 +6,8 @@ - `prelude` now works with the JavaScript target. +- Added `ioutils` module containing `duplicate` and `duplicateTo` to duplicate `FileHandle` using C function `dup` and `dup2`. + ## Language changes @@ -18,4 +20,3 @@ ## Tool changes - diff --git a/lib/std/ioutils.nim b/lib/std/ioutils.nim new file mode 100644 index 000000000..bef61c54a --- /dev/null +++ b/lib/std/ioutils.nim @@ -0,0 +1,85 @@ +when defined(windows): + proc c_dup(oldfd: FileHandle): FileHandle {. + importc: "_dup", header: "".} + proc c_dup2(oldfd: FileHandle, newfd: FileHandle): cint {. + importc: "_dup2", header: "".} +else: + proc c_dup(oldfd: FileHandle): FileHandle{. + importc: "dup", header: "".} + proc c_dup2(oldfd: FileHandle, newfd: FileHandle): cint {. + importc: "dup2", header: "".} + +# when false: +# const SupportIoctlInheritCtl = (defined(linux) or defined(bsd)) and +# not defined(nimscript) +# when SupportIoctlInheritCtl: +# var +# FIOCLEX {.importc, header: "".}: cint +# FIONCLEX {.importc, header: "".}: cint + +## Also defined in std/posix and system/io +proc strerror(errnum: cint): cstring {.importc, header: "".} +when not defined(nimscript): + var errno {.importc, header: "".}: cint ## error variable + +template checkError(ret: cint) = + if ret == -1: + when not defined(nimscript): + raise newException(IOError, $strerror(errno)) + else: + doAssert(false) + +proc duplicate*(oldfd: FileHandle): FileHandle = + ##[ + Return a copy of the file handle `oldfd`. + After a successful return, both `FileHandle` may be used interchangeably. + They refer to the same open file description and share file offset and status flags. + Calls POSIX function `dup` on Posix platform and `_dup` on Windows + ]## + runnableExamples: + # stdoutDuplicate is a copy of stdout FileHandle that points to STDOUT + let stdoutDuplicate = duplicate(stdout.getFileHandle()) + # Writing to stdoutDuplicate will write to stdout + doAssert(stdoutDuplicate != stdout.getFileHandle()) + # On windows, opening a file from a FileHandle does not work + when not defined(windows): + var f : File + let res = open(f, stdoutDuplicate, mode=fmWrite) + let msg = "This is a test message that will be displayed ! \n" + f.write(msg) + # Output "Test" + f.close() + + result = c_dup(oldfd) + checkError(result) + +proc duplicateTo*(oldfd: FileHandle, newfd: FileHandle) = + ##[ + Perform the same task a `duplicate` but instead of using the lowest unused file descriptor + it uses the FileHandle` specified by `newfd`. + Calls POSIX function `dup2` on Posix platform and `_dup2` on Windows. + ]## + runnableExamples: + import os + # Redirect stdout to a file temporarily + let tmpFileName = getTempDir() / "hidden_output.txt" + let stdoutFileno = stdout.getFileHandle() + let stdoutDupFd = duplicate(stdoutFileno) + + # Create a new file + let tmpFile: File = open(tmpFileName, fmAppend) + let tmpFileFd: FileHandle = tmpFile.getFileHandle() + + # stdoutFileno now writes to tmpFile + duplicateTo(tmpFileFd, stdoutFileno) + echo "This is not displayed, but written to tmpFile instead !" + + # Close file & restore stdout + tmpFile.close() + duplicateTo(stdoutDupFd, stdoutFileno) + + # stdout is now restored ! + echo "This is displayed" + + let retValue = c_dup2(oldfd, newfd) + checkError(retValue) diff --git a/tests/stdlib/tioutils.nim b/tests/stdlib/tioutils.nim new file mode 100644 index 000000000..6854a7c1c --- /dev/null +++ b/tests/stdlib/tioutils.nim @@ -0,0 +1,49 @@ +discard """ + output: ''' +hello1 +hello1 +''' +""" + +import std/ioutils +import os +from stdtest/specialpaths import buildDir + +block: # duplicate, duplicateTo + let tmpFileName = buildDir / "tioutils.txt" + template captureStdout(body) : untyped = + let stdoutFileno = stdout.getFileHandle() + # Duplicate stoudFileno + let stdout_dupfd = duplicate(stdoutFileno) + # Create a new file + # You can use append strategy if you'd like + let tmpFile: File = open(tmpFileName, fmWrite) + # Get the FileHandle (the file descriptor) of your file + let tmpFileFd: FileHandle = tmpFile.getFileHandle() + # dup2 tmpFileFd to stdoutFileno -> writing to stdoutFileno now writes to tmpFile + duplicateTo(tmpFileFd, stdoutFileno) + body + # Force flush + tmpFile.flushFile() + # Close tmp + tmpFile.close() + # Read tmp + let ret = readFile(tmpFileName) + # Restore stdout + duplicateTo(stdout_dupfd, stdoutFileno) + ret + + proc duplicateStdout() = + var msg = "hello" + echo msg & "1" + + let s = captureStdout: + echo msg & "2" + + doAssert s == "hello2\n" + + discard tryRemoveFile(tmpFileName) + duplicateStdout() + # Check it works twice + duplicateStdout() + doAssert tryRemoveFile(tmpFileName) From 218acfe3671a5a4b7a4c203b6010fdacbe32051c Mon Sep 17 00:00:00 2001 From: shirleyquirk <31934565+shirleyquirk@users.noreply.github.com> Date: Tue, 27 Oct 2020 12:38:46 +0000 Subject: [PATCH 0848/3768] fixes #10456,#12928 issues when chaining templates to sortedByIt (#15734) * update c_malloc's to csize_t fix for broken --os:ios * I'm an idiot sorry * Create talgorithm.nim * workaround for #10456 I don't understand the intricacies of how lambdalifting and template expansions interact with lent, so i don't know how to fix the real problem, but this sidesteps whatever issue that is. * working test, use typeof rather than auto --- lib/pure/algorithm.nim | 2 +- tests/stdlib/talgorithm.nim | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 tests/stdlib/talgorithm.nim diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim index a277d1429..ff835b57a 100644 --- a/lib/pure/algorithm.nim +++ b/lib/pure/algorithm.nim @@ -501,7 +501,7 @@ template sortedByIt*(seq1, op: untyped): untyped = # Nested sort assert people.sortedByIt((it.age, it.name)) == @[(name: "p2", age: 20), (name: "p3", age: 30), (name: "p4", age: 30), (name: "p1", age: 60)] - var result = sorted(seq1, proc(x, y: typeof(seq1[0])): int = + var result = sorted(seq1, proc(x, y: typeof(items(seq1), typeOfIter)): int = var it {.inject.} = x let a = op it = y diff --git a/tests/stdlib/talgorithm.nim b/tests/stdlib/talgorithm.nim new file mode 100644 index 000000000..85ae79219 --- /dev/null +++ b/tests/stdlib/talgorithm.nim @@ -0,0 +1,18 @@ +discard """ + output:'''@["3", "2", "1"] + ''' +""" +#12928,10456 +import sequtils, strutils, algorithm, json + +proc test() = + try: + let info = parseJson(""" + {"a": ["1", "2", "3"]} + """) + let prefixes = info["a"].getElems().mapIt(it.getStr()).sortedByIt(it).reversed() + echo prefixes + except: + discard + +test() From 0fb878324eeb39a4707be5ab0fd6ad8412950b78 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 27 Oct 2020 15:19:28 +0200 Subject: [PATCH 0849/3768] honor --declaredLocs in more places, including type mismatch errors; also show `kind` with --declaredLocs (#15673) * honor --declaredLocs in more places, including type mismatch errors * fix tests * show declaration location also when type mismatch names clash --- compiler/lookups.nim | 10 +++++---- compiler/semcall.nim | 9 +++----- compiler/semexprs.nim | 5 +++-- compiler/types.nim | 32 +++++++++++++++++++++------- tests/errmsgs/t8794.nim | 2 +- tests/errmsgs/undeclared_routime.nim | 4 ++-- tests/misc/tnoop.nim | 2 +- 7 files changed, 40 insertions(+), 24 deletions(-) diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 0a6a0b950..9225706b5 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -154,12 +154,14 @@ type scope*: PScope inSymChoice: IntSet -proc getSymRepr*(conf: ConfigRef; s: PSym): string = +proc getSymRepr*(conf: ConfigRef; s: PSym, getDeclarationPath = true): string = case s.kind of routineKinds, skType: - result = getProcHeader(conf, s) + result = getProcHeader(conf, s, getDeclarationPath = getDeclarationPath) else: - result = s.name.s + result = "'$1'" % s.name.s + if getDeclarationPath: + result.addDeclaredLoc(conf, s) proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) = # check if all symbols have been used and defined: @@ -172,7 +174,7 @@ proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) = # and slow 'suggest' down: if missingImpls == 0: localError(c.config, s.info, "implementation of '$1' expected" % - getSymRepr(c.config, s)) + getSymRepr(c.config, s, getDeclarationPath=false)) inc missingImpls elif {sfUsed, sfExported} * s.flags == {}: if s.kind notin {skForVar, skParam, skMethod, skUnknown, skGenericParam, skEnumField}: diff --git a/compiler/semcall.nim b/compiler/semcall.nim index dd2398f87..0446d28c2 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -231,8 +231,7 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): doAssert err.firstMismatch.formal != nil candidates.add("\n required type for " & nameParam & ": ") candidates.add typeToString(wanted) - if wanted.sym != nil: - candidates.addDeclaredLocMaybe(c.config, wanted.sym) + candidates.addDeclaredLocMaybe(c.config, wanted) candidates.add "\n but expression '" if err.firstMismatch.kind == kVarNeeded: candidates.add renderNotLValue(nArg) @@ -242,9 +241,7 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): candidates.add "' is of type: " var got = nArg.typ candidates.add typeToString(got) - if got.sym != nil: - candidates.addDeclaredLocMaybe(c.config, got.sym) - + candidates.addDeclaredLocMaybe(c.config, got) doAssert wanted != nil if got != nil: effectProblem(wanted, got, candidates, c) of kUnknown: discard "do not break 'nim check'" @@ -320,7 +317,7 @@ proc getMsgDiagnostic(c: PContext, flags: TExprFlags, n, f: PNode): string = var o: TOverloadIter var sym = initOverloadIter(o, c, f) while sym != nil: - result &= "\n found '$1' of kind '$2'" % [getSymRepr(c.config, sym), sym.kind.toHumanStr] + result &= "\n found $1" % [getSymRepr(c.config, sym)] sym = nextOverloadIter(o, c, f) let ident = considerQuotedIdent(c, f, n).s diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 2de25dadb..b2f0207e7 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -966,8 +966,9 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = # t.kind != tySequence(It is tyProc) if typ.sym != nil and sfAnon notin typ.sym.flags and typ.kind == tyProc: - msg.add(" = " & - typeToString(typ, preferDesc)) + # when can `typ.sym != nil` ever happen? + msg.add(" = " & typeToString(typ, preferDesc)) + msg.addDeclaredLocMaybe(c.config, typ) localError(c.config, n.info, msg) return errorNode(c, n) result = nil diff --git a/compiler/types.nim b/compiler/types.nim index a3a4695b0..7e56f5002 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -124,13 +124,22 @@ proc isFloatLit*(t: PType): bool {.inline.} = result = t.kind == tyFloat and t.n != nil and t.n.kind == nkFloatLit proc addDeclaredLoc*(result: var string, conf: ConfigRef; sym: PSym) = - # result.add " [declared in " & conf$sym.info & "]" - result.add " [declared in " & toFileLineCol(conf, sym.info) & "]" + result.add " [$1 declared in $2]" % [sym.kind.toHumanStr, toFileLineCol(conf, sym.info)] proc addDeclaredLocMaybe*(result: var string, conf: ConfigRef; sym: PSym) = - if optDeclaredLocs in conf.globalOptions: + if optDeclaredLocs in conf.globalOptions and sym != nil: addDeclaredLoc(result, conf, sym) +proc addDeclaredLoc(result: var string, conf: ConfigRef; typ: PType) = + let typ = typ.skipTypes(abstractInst - {tyRange}) + result.add " [$1" % typ.kind.toHumanStr + if typ.sym != nil: + result.add " declared in " & toFileLineCol(conf, typ.sym.info) + result.add "]" + +proc addDeclaredLocMaybe*(result: var string, conf: ConfigRef; typ: PType) = + if optDeclaredLocs in conf.globalOptions: addDeclaredLoc(result, conf, typ) + proc addTypeHeader*(result: var string, conf: ConfigRef; typ: PType; prefer: TPreferedDesc = preferMixed; getDeclarationPath = true) = result.add typeToString(typ, prefer) if getDeclarationPath: result.addDeclaredLoc(conf, typ.sym) @@ -1473,12 +1482,19 @@ proc skipHiddenSubConv*(n: PNode; idgen: IdGenerator): PNode = proc typeMismatch*(conf: ConfigRef; info: TLineInfo, formal, actual: PType) = if formal.kind != tyError and actual.kind != tyError: - let named = typeToString(formal) + let actualStr = typeToString(actual) + let formalStr = typeToString(formal) let desc = typeToString(formal, preferDesc) - let x = if named == desc: named else: named & " = " & desc - var msg = "type mismatch: got <" & - typeToString(actual) & "> " & - "but expected '" & x & "'" + let x = if formalStr == desc: formalStr else: formalStr & " = " & desc + let verbose = actualStr == formalStr or optDeclaredLocs in conf.globalOptions + var msg = "type mismatch:" + if verbose: msg.add "\n" + msg.add " got <$1>" % actualStr + if verbose: + msg.addDeclaredLoc(conf, actual) + msg.add "\n" + msg.add " but expected '$1'" % x + if verbose: msg.addDeclaredLoc(conf, formal) if formal.kind == tyProc and actual.kind == tyProc: case compatibleEffects(formal, actual) diff --git a/tests/errmsgs/t8794.nim b/tests/errmsgs/t8794.nim index 22e4014f1..9db54a9c7 100644 --- a/tests/errmsgs/t8794.nim +++ b/tests/errmsgs/t8794.nim @@ -2,7 +2,7 @@ discard """ cmd: "nim check $options $file" errormsg: "" nimout: ''' -t8794.nim(39, 27) Error: undeclared field: 'a3' for type m8794.Foo3 [declared in m8794.nim(1, 6)] +t8794.nim(39, 27) Error: undeclared field: 'a3' for type m8794.Foo3 [type declared in m8794.nim(1, 6)] ''' """ diff --git a/tests/errmsgs/undeclared_routime.nim b/tests/errmsgs/undeclared_routime.nim index 426507652..7ef12cc0e 100644 --- a/tests/errmsgs/undeclared_routime.nim +++ b/tests/errmsgs/undeclared_routime.nim @@ -2,8 +2,8 @@ discard """ cmd: '''nim c --hints:off $file''' errormsg: "attempting to call routine: 'myiter'" nimout: '''undeclared_routime.nim(13, 15) Error: attempting to call routine: 'myiter' - found 'undeclared_routime.myiter(a: string)[declared in undeclared_routime.nim(10, 9)]' of kind 'iterator' - found 'undeclared_routime.myiter()[declared in undeclared_routime.nim(11, 9)]' of kind 'iterator' + found 'undeclared_routime.myiter(a: string)[iterator declared in undeclared_routime.nim(10, 9)]' + found 'undeclared_routime.myiter()[iterator declared in undeclared_routime.nim(11, 9)]' ''' """ diff --git a/tests/misc/tnoop.nim b/tests/misc/tnoop.nim index a1365cfbe..7f5bb1045 100644 --- a/tests/misc/tnoop.nim +++ b/tests/misc/tnoop.nim @@ -1,6 +1,6 @@ discard """ nimout: ''' - found 'a' of kind 'var' + found 'a' [var declared in tnoop.nim(11, 3)] ''' file: "tnoop.nim" line: 13 From f8cac6bbbcfa8ddc649160945a17a6f5b7a4d9d6 Mon Sep 17 00:00:00 2001 From: cooldome Date: Tue, 27 Oct 2020 23:09:26 +0000 Subject: [PATCH 0850/3768] fix #15752 (#15754) --- compiler/sizealignoffsetimpl.nim | 8 ++++---- tests/enum/tenum.nim | 9 +++++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/compiler/sizealignoffsetimpl.nim b/compiler/sizealignoffsetimpl.nim index 3e536f5d7..2c764fe12 100644 --- a/compiler/sizealignoffsetimpl.nim +++ b/compiler/sizealignoffsetimpl.nim @@ -277,14 +277,14 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) = typ.size = 4 # use signed int32 typ.align = 4 else: - let length = toInt64(lastOrd(conf, typ)) # BUGFIX: use lastOrd! - if length + 1 < `shl`(1, 8): + let lastOrd = toInt64(lastOrd(conf, typ)) # BUGFIX: use lastOrd! + if lastOrd < `shl`(1, 8): typ.size = 1 typ.align = 1 - elif length + 1 < `shl`(1, 16): + elif lastOrd < `shl`(1, 16): typ.size = 2 typ.align = 2 - elif length + 1 < `shl`(BiggestInt(1), 32): + elif lastOrd < `shl`(BiggestInt(1), 32): typ.size = 4 typ.align = 4 else: diff --git a/tests/enum/tenum.nim b/tests/enum/tenum.nim index 8cc578d3c..aa338ee2c 100644 --- a/tests/enum/tenum.nim +++ b/tests/enum/tenum.nim @@ -154,3 +154,12 @@ block nonzero: # bug #6959 B C let slice = SomeEnum.low..SomeEnum.high + +block size_one_byte: #issue 15752 + type + Flag = enum + Disabled = 0x00 + Enabled = 0xFF + + static: + assert 1 == sizeof(Flag) \ No newline at end of file From b13fb9ddb869d46a6bcc5d9f8cb6576be17c3f22 Mon Sep 17 00:00:00 2001 From: narimiran Date: Wed, 28 Oct 2020 08:57:42 +0100 Subject: [PATCH 0851/3768] fix #15750 --- lib/pure/collections/tables.nim | 2 ++ tests/collections/ttables.nim | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index bd46bdd27..1d76d42ea 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -1619,6 +1619,8 @@ proc `==`*[A, B](s, t: OrderedTable[A, B]): bool = if s.counter != t.counter: return false + if s.counter == 0 and t.counter == 0: + return true var ht = t.first var hs = s.first while ht >= 0 and hs >= 0: diff --git a/tests/collections/ttables.nim b/tests/collections/ttables.nim index af8364488..4d94b3d95 100644 --- a/tests/collections/ttables.nim +++ b/tests/collections/ttables.nim @@ -456,3 +456,9 @@ block testNonPowerOf2: var b = initTable[int, int](9) b[1] = 10 assert b[1] == 10 + +block emptyOrdered: + var t1: OrderedTable[int, string] + var t2: OrderedTable[int, string] + assert t1 == t2 + From 732ddc02da9b6203e402d246078827d525434e6b Mon Sep 17 00:00:00 2001 From: narimiran Date: Wed, 28 Oct 2020 08:57:52 +0100 Subject: [PATCH 0852/3768] change/remove deprecated stuff --- tests/collections/ttables.nim | 28 +++++----------------------- 1 file changed, 5 insertions(+), 23 deletions(-) diff --git a/tests/collections/ttables.nim b/tests/collections/ttables.nim index 4d94b3d95..338a83fed 100644 --- a/tests/collections/ttables.nim +++ b/tests/collections/ttables.nim @@ -23,7 +23,7 @@ block tableadds: proc main = var tab = newTable[string, string]() for i in 0..1000: - tab.add "key", "value " & $i + tab["key"] = "value " & $i main() echo "done tableadds" @@ -133,8 +133,8 @@ block tindexby: doAssert indexBy(newSeq[int](), proc(x: int):int = x) == initTable[int, int](), "empty int table" var tbl1 = initTable[int, int]() - tbl1.add(1,1) - tbl1.add(2,2) + tbl1[1] = 1 + tbl1[2] = 2 doAssert indexBy(@[1,2], proc(x: int):int = x) == tbl1, "int table" type @@ -147,8 +147,8 @@ block tindexby: elem2 = TElem(foo: 2, bar: "baz") var tbl2 = initTable[string, TElem]() - tbl2.add("bar", elem1) - tbl2.add("baz", elem2) + tbl2["bar"] = elem1 + tbl2["baz"] = elem2 doAssert indexBy(@[elem1,elem2], proc(x: TElem): string = x.bar) == tbl2, "element table" @@ -197,24 +197,6 @@ block ttables2: run1() echo "2" -block allValues: - var t: Table[int, string] - var key = 0 - let n = 1000 - for i in 0.. Date: Wed, 28 Oct 2020 13:00:49 +0000 Subject: [PATCH 0853/3768] ARC now capable of custom extra alignment. Ref, closure and seq support. (#15697) --- compiler/ccgexprs.nim | 22 ++- compiler/liftdestructors.nim | 8 +- lib/core/typeinfo.nim | 4 +- lib/system/arc.nim | 47 +++--- lib/system/bitmasks.nim | 6 +- lib/system/deepcopy.nim | 2 +- lib/system/memalloc.nim | 81 ++++++++++ lib/system/orc.nim | 2 +- lib/system/seqs_v2.nim | 15 +- tests/arc/thard_alignment.nim | 146 ++++++++++++++++++ .../collections/thardalignmentconstraint.nim | 17 -- 11 files changed, 273 insertions(+), 77 deletions(-) create mode 100644 tests/arc/thard_alignment.nim delete mode 100644 tests/collections/thardalignmentconstraint.nim diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index b6caec760..1eb6caeb3 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1278,11 +1278,11 @@ proc rawGenNew(p: BProc, a: var TLoc, sizeExpr: Rope; needsInit: bool) = if optTinyRtti in p.config.globalOptions: if needsInit: - b.r = ropecg(p.module, "($1) #nimNewObj($2)", - [getTypeDesc(p.module, typ), sizeExpr]) + b.r = ropecg(p.module, "($1) #nimNewObj($2, NIM_ALIGNOF($3))", + [getTypeDesc(p.module, typ), sizeExpr, getTypeDesc(p.module, bt)]) else: - b.r = ropecg(p.module, "($1) #nimNewObjUninit($2)", - [getTypeDesc(p.module, typ), sizeExpr]) + b.r = ropecg(p.module, "($1) #nimNewObjUninit($2, NIM_ALIGNOF($3))", + [getTypeDesc(p.module, typ), sizeExpr, getTypeDesc(p.module, bt)]) genAssignment(p, a, b, {}) else: let ti = genTypeInfoV1(p.module, typ, a.lode.info) @@ -2191,14 +2191,10 @@ proc genDestroy(p: BProc; n: PNode) = of tySequence: var a: TLoc initLocExpr(p, arg, a) - if optThreads in p.config.globalOptions: - linefmt(p, cpsStmts, "if ($1.p && !($1.p->cap & NIM_STRLIT_FLAG)) {$n" & - " #deallocShared($1.p);$n" & - "}$n", [rdLoc(a)]) - else: - linefmt(p, cpsStmts, "if ($1.p && !($1.p->cap & NIM_STRLIT_FLAG)) {$n" & - " #dealloc($1.p);$n" & - "}$n", [rdLoc(a)]) + linefmt(p, cpsStmts, "if ($1.p && !($1.p->cap & NIM_STRLIT_FLAG)) {$n" & + " #alignedDealloc($1.p, NIM_ALIGNOF($2));$n" & + "}$n", + [rdLoc(a), getTypeDesc(p.module, t.lastSon)]) else: discard "nothing to do" else: let t = n[1].typ.skipTypes(abstractVar) @@ -2217,7 +2213,7 @@ proc genDispose(p: BProc; n: PNode) = if elemType.destructor != nil: var destroyCall = newNodeI(nkCall, n.info) genStmts(p, destroyCall) - lineCg(p, cpsStmts, ["#nimRawDispose($#)", rdLoc(a)]) + lineFmt(p, cpsStmts, "#nimRawDispose($1, NIM_ALIGNOF($2))", [rdLoc(a), getTypeDesc(p.module, elemType)]) else: # ``nimRawDisposeVirtual`` calls the ``finalizer`` which is the same as the # destructor, but it uses the runtime type. Afterwards the memory is freed: diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 30094875c..9b6c179db 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -515,7 +515,9 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = if isFinal(elemType): addDestructorCall(c, elemType, actions, genDeref(x, nkDerefExpr)) - actions.add callCodegenProc(c.g, "nimRawDispose", c.info, x) + var alignOf = genBuiltin(c.g, mAlignOf, "alignof", newNodeIT(nkType, c.info, elemType)) + alignOf.typ = getSysType(c.g, c.info, tyInt) + actions.add callCodegenProc(c.g, "nimRawDispose", c.info, x, alignOf) else: addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(x, nkDerefExpr)) actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, x) @@ -638,7 +640,9 @@ proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = if isFinal(elemType): addDestructorCall(c, elemType, actions, genDeref(x, nkDerefExpr)) - actions.add callCodegenProc(c.g, "nimRawDispose", c.info, x) + var alignOf = genBuiltin(c.g, mAlignOf, "alignof", newNodeIT(nkType, c.info, elemType)) + alignOf.typ = getSysType(c.g, c.info, tyInt) + actions.add callCodegenProc(c.g, "nimRawDispose", c.info, x, alignOf) else: addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(x, nkDerefExpr)) actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, x) diff --git a/lib/core/typeinfo.nim b/lib/core/typeinfo.nim index 9fd8ebf8c..5c4e1b601 100644 --- a/lib/core/typeinfo.nim +++ b/lib/core/typeinfo.nim @@ -108,7 +108,7 @@ when not defined(gcDestructors): proc newSeq(typ: PNimType, len: int): pointer {.importCompilerProc.} proc objectInit(dest: pointer, typ: PNimType) {.importCompilerProc.} else: - proc nimNewObj(size: int): pointer {.importCompilerProc.} + proc nimNewObj(size, align: int): pointer {.importCompilerProc.} proc newSeqPayload(cap, elemSize, elemAlign: int): pointer {.importCompilerProc.} proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize, elemAlign: int): pointer {. importCompilerProc.} @@ -178,7 +178,7 @@ proc invokeNew*(x: Any) = ## performs ``new(x)``. `x` needs to represent a ``ref``. assert x.rawType.kind == tyRef when defined(gcDestructors): - cast[ppointer](x.value)[] = nimNewObj(x.rawType.base.size) + cast[ppointer](x.value)[] = nimNewObj(x.rawType.base.size, x.rawType.base.align) else: var z = newObj(x.rawType, x.rawType.base.size) genericAssign(x.value, addr(z), x.rawType) diff --git a/lib/system/arc.nim b/lib/system/arc.nim index 46579eaef..0eecadd66 100644 --- a/lib/system/arc.nim +++ b/lib/system/arc.nim @@ -75,14 +75,13 @@ when defined(nimArcDebug): elif defined(nimArcIds): var gRefId: int -proc nimNewObj(size: int): pointer {.compilerRtl.} = - let s = size + sizeof(RefHeader) +proc nimNewObj(size, alignment: int): pointer {.compilerRtl.} = + let hdrSize = align(sizeof(RefHeader), alignment) + let s = size + hdrSize when defined(nimscript): discard - elif compileOption("threads"): - result = allocShared0(s) +! sizeof(RefHeader) else: - result = alloc0(s) +! sizeof(RefHeader) + result = alignedAlloc0(s, alignment) +! hdrSize when defined(nimArcDebug) or defined(nimArcIds): head(result).refId = gRefId atomicInc gRefId @@ -92,20 +91,18 @@ proc nimNewObj(size: int): pointer {.compilerRtl.} = when traceCollector: cprintf("[Allocated] %p result: %p\n", result -! sizeof(RefHeader), result) -proc nimNewObjUninit(size: int): pointer {.compilerRtl.} = +proc nimNewObjUninit(size, alignment: int): pointer {.compilerRtl.} = # Same as 'newNewObj' but do not initialize the memory to zero. # The codegen proved for us that this is not necessary. - let s = size + sizeof(RefHeader) + let hdrSize = align(sizeof(RefHeader), alignment) + let s = size + hdrSize when defined(nimscript): discard - elif compileOption("threads"): - var orig = cast[ptr RefHeader](allocShared(s)) else: - var orig = cast[ptr RefHeader](alloc(s)) - orig.rc = 0 + result = cast[ptr RefHeader](alignedAlloc0(s, alignment) +! hdrSize) + head(result).rc = 0 when defined(gcOrc): - orig.rootIdx = 0 - result = orig +! sizeof(RefHeader) + head(result).rootIdx = 0 when defined(nimArcDebug): head(result).refId = gRefId atomicInc gRefId @@ -147,7 +144,7 @@ when not defined(nimscript) and defined(nimArcDebug): else: result = 0 -proc nimRawDispose(p: pointer) {.compilerRtl.} = +proc nimRawDispose(p: pointer, alignment: int) {.compilerRtl.} = when not defined(nimscript): when traceCollector: cprintf("[Freed] %p\n", p -! sizeof(RefHeader)) @@ -155,27 +152,21 @@ proc nimRawDispose(p: pointer) {.compilerRtl.} = if head(p).rc >= rcIncrement: cstderr.rawWrite "[FATAL] dangling references exist\n" quit 1 - - when defined(gcOrc) and defined(nimArcDebug): - if (head(p).rc and 0b100) != 0: - cstderr.rawWrite "[FATAL] cycle root freed\n" - quit 1 - when defined(nimArcDebug): # we do NOT really free the memory here in order to reliably detect use-after-frees if freedCells.data == nil: init(freedCells) freedCells.incl head(p) - elif compileOption("threads"): - deallocShared(p -! sizeof(RefHeader)) else: - dealloc(p -! sizeof(RefHeader)) + let hdrSize = align(sizeof(RefHeader), alignment) + alignedDealloc(p -! hdrSize, alignment) -template dispose*[T](x: owned(ref T)) = nimRawDispose(cast[pointer](x)) +template dispose*[T](x: owned(ref T)) = nimRawDispose(cast[pointer](x), T.alignOf) #proc dispose*(x: pointer) = nimRawDispose(x) proc nimDestroyAndDispose(p: pointer) {.compilerRtl, raises: [].} = - let d = cast[ptr PNimTypeV2](p)[].destructor - if d != nil: cast[DestructorProc](d)(p) + let rti = cast[ptr PNimTypeV2](p) + if rti.destructor != nil: + cast[DestructorProc](rti.destructor)(p) when false: cstderr.rawWrite cast[ptr PNimTypeV2](p)[].name cstderr.rawWrite "\n" @@ -183,7 +174,7 @@ proc nimDestroyAndDispose(p: pointer) {.compilerRtl, raises: [].} = cstderr.rawWrite "bah, nil\n" else: cstderr.rawWrite "has destructor!\n" - nimRawDispose(p) + nimRawDispose(p, rti.align) when defined(gcOrc): when defined(nimThinout): @@ -216,7 +207,7 @@ proc GC_unref*[T](x: ref T) = if nimDecRefIsLast(cast[pointer](x)): # XXX this does NOT work for virtual destructors! `=destroy`(x[]) - nimRawDispose(cast[pointer](x)) + nimRawDispose(cast[pointer](x), T.alignOf) proc GC_ref*[T](x: ref T) = ## New runtime only supports this operation for 'ref T'. diff --git a/lib/system/bitmasks.nim b/lib/system/bitmasks.nim index 922ad5fb7..d7c55a4d9 100644 --- a/lib/system/bitmasks.nim +++ b/lib/system/bitmasks.nim @@ -15,7 +15,11 @@ const PageSize = 1 shl PageShift PageMask = PageSize-1 - MemAlign = 16 # also minimal allocatable memory block + MemAlign = # also minimal allocatable memory block + when defined(useMalloc): + when defined(amd64): 16 + else: 8 + else: 16 BitsPerPage = PageSize div MemAlign UnitsPerPage = BitsPerPage div (sizeof(int)*8) diff --git a/lib/system/deepcopy.nim b/lib/system/deepcopy.nim index b9dc594fa..1f30b8427 100644 --- a/lib/system/deepcopy.nim +++ b/lib/system/deepcopy.nim @@ -163,7 +163,7 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType; tab: var PtrTable) = when defined(nimSeqsV2): let typ = if mt.base.kind == tyObject: cast[PNimType](cast[ptr PNimTypeV2](s2)[].typeInfoV1) else: mt.base - let z = nimNewObj(typ.size) + let z = nimNewObj(typ.size, typ.align) cast[PPointer](dest)[] = z else: # this version should work for any other GC: diff --git a/lib/system/memalloc.nim b/lib/system/memalloc.nim index 142762fe7..5f4304502 100644 --- a/lib/system/memalloc.nim +++ b/lib/system/memalloc.nim @@ -304,6 +304,87 @@ when hasAlloc and not defined(js): ## or other memory may be corrupted. deallocShared(p) + include bitmasks + + template `+!`(p: pointer, s: SomeInteger): pointer = + cast[pointer](cast[int](p) +% int(s)) + + template `-!`(p: pointer, s: SomeInteger): pointer = + cast[pointer](cast[int](p) -% int(s)) + + proc alignedAlloc(size, align: Natural): pointer = + if align <= MemAlign: + when compileOption("threads"): + result = allocShared(size) + else: + result = alloc(size) + else: + # allocate (size + align - 1) necessary for alignment, + # plus 2 bytes to store offset + when compileOption("threads"): + let base = allocShared(size + align - 1 + sizeof(uint16)) + else: + let base = alloc(size + align - 1 + sizeof(uint16)) + # memory layout: padding + offset (2 bytes) + user_data + # in order to deallocate: read offset at user_data - 2 bytes, + # then deallocate user_data - offset + let offset = align - (cast[int](base) and (align - 1)) + cast[ptr uint16](base +! (offset - sizeof(uint16)))[] = uint16(offset) + result = base +! offset + + proc alignedAlloc0(size, align: Natural): pointer = + if align <= MemAlign: + when compileOption("threads"): + result = allocShared0(size) + else: + result = alloc0(size) + else: + # see comments for alignedAlloc + when compileOption("threads"): + let base = allocShared0(size + align - 1 + sizeof(uint16)) + else: + let base = alloc0(size + align - 1 + sizeof(uint16)) + let offset = align - (cast[int](base) and (align - 1)) + cast[ptr uint16](base +! (offset - sizeof(uint16)))[] = uint16(offset) + result = base +! offset + + proc alignedDealloc(p: pointer, align: int) {.compilerproc.} = + if align <= MemAlign: + when compileOption("threads"): + deallocShared(p) + else: + dealloc(p) + else: + # read offset at p - 2 bytes, then deallocate (p - offset) pointer + let offset = cast[ptr uint16](p -! sizeof(uint16))[] + when compileOption("threads"): + deallocShared(p -! offset) + else: + dealloc(p -! offset) + + proc alignedRealloc(p: pointer, oldSize, newSize, align: Natural): pointer = + if align <= MemAlign: + when compileOption("threads"): + result = reallocShared(p, newSize) + else: + result = realloc(p, newSize) + else: + result = alignedAlloc(newSize, align) + copyMem(result, p, oldSize) + alignedDealloc(p, align) + + proc alignedRealloc0(p: pointer, oldSize, newSize, align: Natural): pointer = + if align <= MemAlign: + when compileOption("threads"): + result = reallocShared0(p, oldSize, newSize) + else: + result = realloc0(p, oldSize, newSize) + else: + result = alignedAlloc(newSize, align) + copyMem(result, p, oldSize) + zeroMem(result +! oldSize, newSize - oldSize) + alignedDealloc(p, align) + {.pop.} # GC interface: diff --git a/lib/system/orc.nim b/lib/system/orc.nim index 3c2327fd5..28f8e5808 100644 --- a/lib/system/orc.nim +++ b/lib/system/orc.nim @@ -88,7 +88,7 @@ proc free(s: Cell; desc: PNimTypeV2) {.inline.} = else: cstderr.rawWrite "has dispose!\n" - nimRawDispose(p) + nimRawDispose(p, desc.align) proc nimTraceRef(q: pointer; desc: PNimTypeV2; env: pointer) {.compilerRtl, inline.} = let p = cast[ptr pointer](q) diff --git a/lib/system/seqs_v2.nim b/lib/system/seqs_v2.nim index d83a0009a..b7f24ecd5 100644 --- a/lib/system/seqs_v2.nim +++ b/lib/system/seqs_v2.nim @@ -35,10 +35,7 @@ proc newSeqPayload(cap, elemSize, elemAlign: int): pointer {.compilerRtl, raises # we have to use type erasure here as Nim does not support generic # compilerProcs. Oh well, this will all be inlined anyway. if cap > 0: - when compileOption("threads"): - var p = cast[ptr NimSeqPayloadBase](allocShared0(align(sizeof(NimSeqPayloadBase), elemAlign) + cap * elemSize)) - else: - var p = cast[ptr NimSeqPayloadBase](alloc0(align(sizeof(NimSeqPayloadBase), elemAlign) + cap * elemSize)) + var p = cast[ptr NimSeqPayloadBase](alignedAlloc0(align(sizeof(NimSeqPayloadBase), elemAlign) + cap * elemSize, elemAlign)) p.cap = cap result = p else: @@ -65,20 +62,14 @@ proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize, elemAlign: int): poin let oldCap = p.cap and not strlitFlag let newCap = max(resize(oldCap), len+addlen) if (p.cap and strlitFlag) == strlitFlag: - when compileOption("threads"): - var q = cast[ptr NimSeqPayloadBase](allocShared0(headerSize + elemSize * newCap)) - else: - var q = cast[ptr NimSeqPayloadBase](alloc0(headerSize + elemSize * newCap)) + var q = cast[ptr NimSeqPayloadBase](alignedAlloc0(headerSize + elemSize * newCap, elemAlign)) copyMem(q +! headerSize, p +! headerSize, len * elemSize) q.cap = newCap result = q else: let oldSize = headerSize + elemSize * oldCap let newSize = headerSize + elemSize * newCap - when compileOption("threads"): - var q = cast[ptr NimSeqPayloadBase](reallocShared0(p, oldSize, newSize)) - else: - var q = cast[ptr NimSeqPayloadBase](realloc0(p, oldSize, newSize)) + var q = cast[ptr NimSeqPayloadBase](alignedRealloc0(p, oldSize, newSize, elemAlign)) q.cap = newCap result = q diff --git a/tests/arc/thard_alignment.nim b/tests/arc/thard_alignment.nim new file mode 100644 index 000000000..e644572f0 --- /dev/null +++ b/tests/arc/thard_alignment.nim @@ -0,0 +1,146 @@ +discard """ +disabled: "arm64" +cmd: "nim c --gc:arc $file" +output: "y" +""" + +{.passC: "-march=native".} + +proc isAlignedCheck(p: pointer, alignment: int) = + doAssert (cast[uint](p) and uint(alignment - 1)) == 0 + +proc isAlignedCheck[T](p: ref T, alignment: int) = + isAlignedCheck(cast[pointer](p), alignment) + +type + m256d {.importc: "__m256d", header: "immintrin.h".} = object + +proc set1(x: float): m256d {.importc: "_mm256_set1_pd", header: "immintrin.h".} +func `+`(a,b: m256d): m256d {.importc: "_mm256_add_pd", header: "immintrin.h".} +proc `$`(a: m256d): string = + result = $(cast[ptr float](a.unsafeAddr)[]) + + +var res: seq[seq[m256d]] + +for _ in 1..1000: + var x = newSeq[m256d](1) + x[0] = set1(1.0) # test if operation causes segfault + isAlignedCheck(x[0].addr, alignof(m256d)) + res.add x + +var res2: seq[m256d] +for i in 1..10000: + res2.setLen(res2.len + 1) # check if realloc works + isAlignedCheck(res2[0].addr, alignof(m256d)) + +proc lambdaGen(a, b: float, z: ref m256d) : auto = + var x1 = new(m256d) + var x2 = new(m256d) + isAlignedCheck(x1, alignof(m256d)) + isAlignedCheck(x2, alignof(m256d)) + x1[] = set1(2.0 + a) + x2[] = set1(-23.0 - b) + let capturingLambda = proc(x: ref m256d): ref m256d = + var cc = new(m256d) + var bb = new(m256d) + isAlignedCheck(x1, alignof(m256d)) + isAlignedCheck(x2, alignof(m256d)) + isAlignedCheck(cc, alignof(m256d)) + isAlignedCheck(bb, alignof(m256d)) + isAlignedCheck(z, alignof(m256d)) + + cc[] = x1[] + x1[] + z[] + bb[] = x2[] + set1(12.5) + z[] + + result = new(m256d) + isAlignedCheck(result, alignof(m256d)) + result[] = cc[] + bb[] + x[] + + return capturingLambda + +var xx = new(m256d) +xx[] = set1(10) +isAlignedCheck(xx, alignOf(m256d)) + +let f1 = lambdaGen(2.0 , 2.221, xx) +let f2 = lambdaGen(-1.226 , 3.5, xx) +isAlignedCheck(f1(xx), alignOf(m256d)) +isAlignedCheck(f2(xx), alignOf(m256d)) + + +#----------------------------------------------------------------------------- + +type + MyAligned = object of RootObj + a{.align: 128.}: float + + +var f: MyAligned +isAlignedCheck(f.addr, MyAligned.alignOf) + +var fref = new(MyAligned) +isAlignedCheck(fref, MyAligned.alignOf) + +var fs: seq[MyAligned] +var fr: seq[RootRef] + +for i in 0..1000: + fs.add MyAligned() + isAlignedCheck(fs[^1].addr, MyAligned.alignOf) + fs[^1].a = i.float + + fr.add new(MyAligned) + isAlignedCheck(fr[^1], MyAligned.alignOf) + ((ref MyAligned)fr[^1])[].a = i.float + +for i in 0..1000: + doAssert(fs[i].a == i.float) + doAssert(((ref MyAligned)fr[i]).a == i.float) + + +proc lambdaTest2(a: MyAligned, z: ref MyAligned): auto = + var x1: MyAligned + x1.a = a.a + z.a + var x2: MyAligned + x2.a = a.a - z.a + let capturingLambda = proc(x: MyAligned): MyAligned = + var cc: MyAligned + var bb: MyAligned + isAlignedCheck(x1.addr, MyAligned.alignOf) + isAlignedCheck(x2.addr, MyAligned.alignOf) + isAlignedCheck(cc.addr, MyAligned.alignOf) + isAlignedCheck(bb.addr, MyAligned.alignOf) + isAlignedCheck(z, MyAligned.alignOf) + + cc.a = x1.a + x1.a + z.a + bb.a = x2.a - z.a + + isAlignedCheck(result.addr, MyAligned.alignOf) + result.a = cc.a + bb.a + x2.a + + return capturingLambda + + +let q1 = lambdaTest2(MyAligned(a: 1.0), (ref MyAligned)(a: 2.0)) +let q2 = lambdaTest2(MyAligned( a: -1.0), (ref MyAligned)(a: -2.0)) + +isAlignedCheck(rawEnv(q1), MyAligned.alignOf) +isAlignedCheck(rawEnv(q2), MyAligned.alignOf) +discard q1(MyAligned(a: 1.0)) +discard q2(MyAligned(a: -1.0)) + + +#----------------------------------------------------------------------------- + +block: + var s: seq[seq[MyAligned]] + for len in 0..128: + s.add newSeq[MyAligned](len) + for i in 0.. 0: + isAlignedCheck(s[^1][0].addr, MyAligned.alignOf) + +echo "y" diff --git a/tests/collections/thardalignmentconstraint.nim b/tests/collections/thardalignmentconstraint.nim deleted file mode 100644 index e3a3081b9..000000000 --- a/tests/collections/thardalignmentconstraint.nim +++ /dev/null @@ -1,17 +0,0 @@ -discard """ -disabled: true -""" - -# does not yet work - -{.passC: "-march=native".} - -type - m256d {.importc: "__m256d", header: "immintrin.h".} = object - -proc set1(x: float): m256d {.importc: "_mm256_set1_pd", header: "immintrin.h".} - -for _ in 1..1000: - var x = newSeq[m256d](1) - x[0] = set1(1.0) # test if operation causes segfault - doAssert (cast[uint](x[0].addr) and 31) == 0 From 610e9b2fe9d5908b9941939e975f394aba009b43 Mon Sep 17 00:00:00 2001 From: cooldome Date: Wed, 28 Oct 2020 13:24:38 +0000 Subject: [PATCH 0854/3768] fix #15756 (#15761) * fix #15756 * simplify test --- compiler/varpartitions.nim | 2 +- tests/effects/tstrict_funcs.nim | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim index 49221654d..34a64c759 100644 --- a/compiler/varpartitions.nim +++ b/compiler/varpartitions.nim @@ -126,7 +126,7 @@ proc hasSideEffect*(c: var Partitions; info: var MutationInfo): bool = return true return false -template isConstParam(a): bool = a.kind == skParam and a.typ.kind != tyVar +template isConstParam(a): bool = a.kind == skParam and a.typ.kind notin {tyVar, tySink} proc variableId(c: Partitions; x: PSym): int = for i in 0 ..< c.s.len: diff --git a/tests/effects/tstrict_funcs.nim b/tests/effects/tstrict_funcs.nim index 166ab77c1..75ed9c8d9 100644 --- a/tests/effects/tstrict_funcs.nim +++ b/tests/effects/tstrict_funcs.nim @@ -15,3 +15,15 @@ proc get_Contig2Reads(sin: Stream, fn: string, contig2len: TableRef[string, int] if contig2len.haskey(parser.row[1]): mgetOrPut(result, parser.row[1], @[]).add(parser.row[0]) + + +block: + # issue #15756 + func `&&&`[T](x: var seq[T], y: sink T): seq[T] = + newSeq(result, x.len + 1) + for i in 0..x.len-1: + result[i] = move(x[i]) + result[x.len] = move(y) + + var x = @[0, 1] + let z = x &&& 2 \ No newline at end of file From 94e2b9e83725d8bcaa43f4bb3e64b8c79d3bec95 Mon Sep 17 00:00:00 2001 From: Antonis Geralis <43617260+planetis-m@users.noreply.github.com> Date: Wed, 28 Oct 2020 18:54:42 +0200 Subject: [PATCH 0855/3768] Make newObjUninit proc to adhere to its name (#15764) Co-authored-by: b3liever --- lib/system/arc.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/system/arc.nim b/lib/system/arc.nim index 0eecadd66..4c97f1aa0 100644 --- a/lib/system/arc.nim +++ b/lib/system/arc.nim @@ -99,7 +99,7 @@ proc nimNewObjUninit(size, alignment: int): pointer {.compilerRtl.} = when defined(nimscript): discard else: - result = cast[ptr RefHeader](alignedAlloc0(s, alignment) +! hdrSize) + result = cast[ptr RefHeader](alignedAlloc(s, alignment) +! hdrSize) head(result).rc = 0 when defined(gcOrc): head(result).rootIdx = 0 @@ -165,7 +165,7 @@ template dispose*[T](x: owned(ref T)) = nimRawDispose(cast[pointer](x), T.alignO proc nimDestroyAndDispose(p: pointer) {.compilerRtl, raises: [].} = let rti = cast[ptr PNimTypeV2](p) - if rti.destructor != nil: + if rti.destructor != nil: cast[DestructorProc](rti.destructor)(p) when false: cstderr.rawWrite cast[ptr PNimTypeV2](p)[].name From 485d4ff802c361f363d2f455b98abc5db1a7082d Mon Sep 17 00:00:00 2001 From: Tim Smith <69870077+timscodebase@users.noreply.github.com> Date: Wed, 28 Oct 2020 09:56:56 -0700 Subject: [PATCH 0856/3768] Spelling and Grammer fixes (#15719) Co-authored-by: Tim Smith Co-authored-by: Miran --- doc/gc.rst | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/doc/gc.rst b/doc/gc.rst index 23c4f64cf..804481cd9 100644 --- a/doc/gc.rst +++ b/doc/gc.rst @@ -102,10 +102,14 @@ These two procs are the two modus operandi of the real-time garbage collector: To bind all garbage collector activity to a ``GC_step`` call, deactivate the garbage collector with ``GC_disable`` at program startup. If ``strongAdvice`` is set to ``true``, - then the garbage collector will be forced to perform collection cycle. + then the garbage collector will be forced to perform the collection cycle. Otherwise, the garbage collector may decide not to do anything, if there is not much garbage to collect. You may also specify the current stack size via ``stackSize`` parameter. + It can improve performance when you know that there are no unique Nim references + below a certain point on the stack. Make sure the size you specify is greater + than the potential worst-case size. + It can improve performance when you know that there are no unique Nim references below a certain point on the stack. Make sure the size you specify is greater than the potential worst-case size. @@ -174,7 +178,7 @@ Heap dump The heap dump feature is still in its infancy, but it already proved useful for us, so it might be useful for you. To get a heap dump, compile with ``-d:nimTypeNames`` and call ``dumpNumberOfInstances`` at a strategic place in your program. -This produces a list of used types in your program and for every type +This produces a list of the used types in your program and for every type the total amount of object instances for this type as well as the total amount of bytes these instances take up. From 0cae8ef2caccaf1104b21ab957dd877d5c2f461c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yanis=20Zafir=C3=B3pulos?= <1265028+drkameleon@users.noreply.github.com> Date: Thu, 29 Oct 2020 10:33:47 +0100 Subject: [PATCH 0857/3768] Massive documentation fixes + copy editing (#15747) --- doc/apis.rst | 2 +- doc/backends.rst | 41 ++--- doc/destructors.rst | 24 +-- doc/docgen.rst | 46 ++--- doc/drnim.rst | 8 +- doc/hcr.rst | 6 +- doc/koch.rst | 4 +- doc/lib.rst | 79 ++++---- doc/manual.rst | 357 ++++++++++++++++++------------------ doc/manual/var_t_return.rst | 2 +- doc/nimc.rst | 83 +++++---- doc/niminst.rst | 8 +- doc/testament.rst | 8 +- doc/tools.rst | 4 +- doc/tut1.rst | 106 +++++------ doc/tut3.rst | 34 ++-- 16 files changed, 401 insertions(+), 411 deletions(-) diff --git a/doc/apis.rst b/doc/apis.rst index d0327c76e..03374c2db 100644 --- a/doc/apis.rst +++ b/doc/apis.rst @@ -3,7 +3,7 @@ API naming design ================= The API is designed to be **easy to use** and consistent. Ease of use is -measured by the number of calls to achieve a concrete high level action. +measured by the number of calls to achieve a concrete high-level action. Naming scheme diff --git a/doc/backends.rst b/doc/backends.rst index e46941a12..b50c7e38f 100644 --- a/doc/backends.rst +++ b/doc/backends.rst @@ -15,16 +15,16 @@ Introduction The `Nim Compiler User Guide `_ documents the typical compiler invocation, using the ``compile`` or ``c`` command to transform a ``.nim`` file into one or more ``.c`` files which are then compiled with the -platform's C compiler into a static binary. However there are other commands -to compile to C++, Objective-C or JavaScript. This document tries to +platform's C compiler into a static binary. However, there are other commands +to compile to C++, Objective-C, or JavaScript. This document tries to concentrate in a single place all the backend and interfacing options. The Nim compiler supports mainly two backend families: the C, C++ and Objective-C targets and the JavaScript target. `The C like targets -<#backends-the-c-like-targets>`_ creates source files which can be compiled +<#backends-the-c-like-targets>`_ creates source files that can be compiled into a library or a final executable. `The JavaScript target <#backends-the-javascript-target>`_ can generate a ``.js`` file which you -reference from an HTML file or create a `standalone nodejs program +reference from an HTML file or create a `standalone Node.js program `_. On top of generating libraries or standalone applications, Nim offers @@ -48,7 +48,7 @@ The most significant difference between these commands is that if you look into the ``nimcache`` directory you will find ``.c``, ``.cpp`` or ``.m`` files, other than that all of them will produce a native binary for your project. This allows you to take the generated code and place it directly -into a project using any of these languages. Here are some typical command +into a project using any of these languages. Here are some typical command- line invocations:: $ nim c hallo.nim @@ -56,8 +56,8 @@ line invocations:: $ nim objc hallo.nim The compiler commands select the target backend, but if needed you can -`specify additional switches for cross compilation -`_ to select the target CPU, operative system +`specify additional switches for cross-compilation +`_ to select the target CPU, operative system or compiler/linker commands. @@ -79,7 +79,7 @@ available. This includes: * OS-specific operations * threading, coroutines * some modules of the standard library -* proper 64 bit integer arithmetic +* proper 64-bit integer arithmetic To compensate, the standard library has modules `catered to the JS backend `_ @@ -119,7 +119,7 @@ pragmas to call methods from classes. Whenever you use any of these pragmas you need to integrate native code into your final binary. In the case of JavaScript this is no problem at all, the -same html file which hosts the generated JavaScript will likely provide other +same HTML file which hosts the generated JavaScript will likely provide other JavaScript functions which you are importing with ``importc``. However, for the C like targets you need to link external code either @@ -167,7 +167,7 @@ With these two files in place, you can run ``nim c -r calculator.nim`` and the Nim compiler will compile the ``logic.c`` file in addition to ``calculator.nim`` and link both into an executable, which outputs ``10`` when run. Another way to link the C file statically and get the same effect would -be remove the line with the ``compile`` pragma and run the following typical +be to remove the line with the ``compile`` pragma and run the following typical Unix commands:: $ gcc -c logic.c @@ -211,7 +211,7 @@ calculator.nim`` and open ``host.html`` in a browser. If the browser supports javascript, you should see the value ``10`` in the browser's console. Use the `dom module `_ for specific DOM querying and modification procs or take a look at `karax `_ for how to -develop browser based applications. +develop browser-based applications. Backend code calling Nim @@ -220,7 +220,7 @@ Backend code calling Nim Backend code can interface with Nim code exposed through the `exportc pragma `_. The ``exportc`` pragma is the *generic* way of making Nim symbols available to -the backends. By default the Nim compiler will mangle all the Nim symbols to +the backends. By default, the Nim compiler will mangle all the Nim symbols to avoid any name collision, so the most significant thing the ``exportc`` pragma does is maintain the Nim symbol name, or if specified, use an alternative symbol for the backend in case the symbol rules don't match. @@ -233,7 +233,7 @@ the compiler will assume certain types for the return value and parameters which will likely make your program crash at runtime. The Nim compiler can generate a C interface header through the ``--header`` -command line switch. The generated header will contain all the exported +command-line switch. The generated header will contain all the exported symbols and the ``NimMain`` proc which you need to call before any other Nim code. @@ -322,8 +322,8 @@ from the previous section): Compile the Nim code to JavaScript with ``nim js -o:fib.js fib.nim`` and open ``mhost.html`` in a browser. If the browser supports javascript, you should see an alert box displaying the text ``Fib for 9 is 34``. As mentioned -earlier, JavaScript doesn't require an initialisation call to ``NimMain`` or -similar function and you can call the exported Nim proc directly. +earlier, JavaScript doesn't require an initialization call to ``NimMain`` or +a similar function and you can call the exported Nim proc directly. Nimcache naming logic @@ -333,15 +333,15 @@ The `nimcache`:idx: directory is generated during compilation and will hold either temporary or final files depending on your backend target. The default name for the directory depends on the used backend and on your OS but you can use the ``--nimcache`` `compiler switch -`_ to change it. +`_ to change it. Memory management ================= -In the previous sections the ``NimMain()`` function reared its head. Since +In the previous sections, the ``NimMain()`` function reared its head. Since JavaScript already provides automatic memory management, you can freely pass -objects between the two language without problems. In C and derivate languages +objects between the two languages without problems. In C and derivate languages you need to be careful about what you do and how you share memory. The previous examples only dealt with simple scalar values, but passing a Nim string to C, or reading back a C string in Nim already requires you to be @@ -355,7 +355,7 @@ The manual mentions that `Nim strings are implicitly convertible to cstrings `_ which makes interaction usually painless. Most C functions accepting a Nim string converted to a ``cstring`` will likely not need to keep this string around and by the time -they return the string won't be needed any more. However, for the rare cases +they return the string won't be needed anymore. However, for the rare cases where a Nim string has to be preserved and made available to the C backend as a ``cstring``, you will need to manually prevent the string data from being freed with `GC_ref `_ and `GC_unref @@ -388,7 +388,7 @@ to hand a Nim reference to C code, you will need to use `GC_ref `_ to mark the reference as used, so it does not get freed. And for the C backend you will need to expose the `GC_unref `_ proc to clean up this memory when it is not -required any more. +required anymore. Again, if you are wrapping a library which *mallocs* and *frees* data structures, you need to expose the appropriate *free* function to Nim so @@ -424,4 +424,3 @@ leaks by calling .. code-block:: nim system.tearDownForeignThreadGc() - diff --git a/doc/destructors.rst b/doc/destructors.rst index b581fce3e..ff09d6644 100644 --- a/doc/destructors.rst +++ b/doc/destructors.rst @@ -25,7 +25,7 @@ move semantics and destructors work in Nim. Motivating example ================== -With the language mechanisms described here a custom seq could be +With the language mechanisms described here, a custom seq could be written as: .. code-block:: nim @@ -88,7 +88,7 @@ Lifetime-tracking hooks ======================= The memory management for Nim's standard ``string`` and ``seq`` types as -well as other standard collections is performed via so called +well as other standard collections is performed via so-called "Lifetime-tracking hooks" or "type-bound operators". There are 3 different hooks for each (generic or concrete) object type ``T`` (``T`` can also be a ``distinct`` type) that are called implicitly by the compiler. @@ -128,13 +128,13 @@ The general pattern in ``=destroy`` looks like: ------------ A `=sink` hook moves an object around, the resources are stolen from the source -and passed to the destination. It is ensured that source's destructor does -not free the resources afterwards by setting the object to its default value +and passed to the destination. It is ensured that the source's destructor does +not free the resources afterward by setting the object to its default value (the value the object's state started in). Setting an object ``x`` back to its default value is written as ``wasMoved(x)``. When not provided the compiler is using a combination of `=destroy` and `copyMem` instead. This is efficient hence users rarely need to implement their own `=sink` operator, it is enough to -provide `=destroy` and `=copy`, compiler will take care about the rest. +provide `=destroy` and `=copy`, compiler will take care of the rest. The prototype of this hook for a type ``T`` needs to be: @@ -191,7 +191,7 @@ Move semantics ============== A "move" can be regarded as an optimized copy operation. If the source of the -copy operation is not used afterwards, the copy can be replaced by a move. This +copy operation is not used afterward, the copy can be replaced by a move. This document uses the notation ``lastReadOf(x)`` to describe that ``x`` is not used afterwards. This property is computed by a static control flow analysis but can also be enforced by using ``system.move`` explicitly. @@ -218,7 +218,7 @@ Sink parameters =============== To move a variable into a collection usually ``sink`` parameters are involved. -A location that is passed to a ``sink`` parameter should not be used afterwards. +A location that is passed to a ``sink`` parameter should not be used afterward. This is ensured by a static analysis over a control flow graph. If it cannot be proven to be the last usage of the location, a copy is done instead and this copy is then passed to the sink parameter. @@ -232,7 +232,7 @@ without any further overloads and ``put`` might not take ownership of ``k`` if not a linear type system. The employed static analysis is limited and only concerned with local variables; -however object and tuple fields are treated as separate entities: +however, object and tuple fields are treated as separate entities: .. code-block:: nim @@ -509,7 +509,7 @@ to avoid this overhead: In fact, ``.cursor`` more generally prevents object construction/destruction pairs and so can also be useful in other contexts. The alternative solution would be to use raw pointers (``ptr``) instead which is more cumbersome and also more dangerous -for Nim's evolution: Later on the compiler can try to prove ``.cursor`` annotations +for Nim's evolution: Later on, the compiler can try to prove ``.cursor`` annotations to be safe, but for ``ptr`` the compiler has to remain silent about possible problems. @@ -522,7 +522,7 @@ a form of copy elision. To see how and when we can do that, think about this question: In `dest = src` when do we really have to *materialize* the full copy? - Only if `dest` or `src` are mutated -afterwards. If `dest` is a local variable that is simple to analyse. And if `src` is a +afterward. If `dest` is a local variable that is simple to analyze. And if `src` is a location derived from a formal parameter, we also know it is not mutated! In other words, we do a compile-time copy-on-write analysis. @@ -547,9 +547,9 @@ other words, a copy ``x = y`` is implemented as ``x[0] = y[0]; x[1] = y[1]; ...``, likewise for ``=sink`` and ``=destroy``. Other value-based compound types like ``object`` and ``array`` are handled -correspondingly. For ``object`` however, the compiler generated hooks +correspondingly. For ``object`` however, the compiler-generated hooks can be overridden. This can also be important to use an alternative traversal -of the involved datastructure that is more efficient or in order to avoid +of the involved data structure that is more efficient or in order to avoid deep recursions. diff --git a/doc/docgen.rst b/doc/docgen.rst index 636af6574..07218982c 100644 --- a/doc/docgen.rst +++ b/doc/docgen.rst @@ -14,7 +14,7 @@ Introduction This document describes the `documentation generation tools`:idx: built into the `Nim compiler `_, which can generate HTML and JSON output from input .nim files and projects, as well as HTML and LaTeX from input RST -(reStructuredText) files. The output documentation will include module +(reStructuredText) files. The output documentation will include the module dependencies (``import``), any top-level documentation comments (##), and exported symbols (*), including procedures, types, and variables. @@ -107,7 +107,7 @@ Document Types HTML ---- -Generation of HTML documents is done via the ``doc`` command. This command +The generation of HTML documents is done via the ``doc`` command. This command takes either a single .nim file, outputting a single .html file with the same base filename, or multiple .nim files, outputting multiple .html files and, optionally, an index file. @@ -121,15 +121,15 @@ Partial Output:: ... The full output can be seen here: `docgen_sample.html `_. -It runs after semantic checking, and includes pragmas attached implicitly by the +It runs after semantic checking and includes pragmas attached implicitly by the compiler. JSON ---- -Generation of JSON documents is done via the ``jsondoc`` command. This command -takes in a .nim file, and outputs a .json file with the same base filename. Note +The generation of JSON documents is done via the ``jsondoc`` command. This command +takes in a .nim file and outputs a .json file with the same base filename. Note that this tool is built off of the ``doc`` command (previously ``doc2``), and contains the same information. @@ -153,8 +153,8 @@ Output:: ] } -Similarly to the old ``doc`` command the old ``jsondoc`` command has been -renamed ``jsondoc0``. +Similarly to the old ``doc`` command, the old ``jsondoc`` command has been +renamed to ``jsondoc0``. The ``jsondoc0`` command:: nim jsondoc0 sample @@ -197,8 +197,8 @@ Index switch This will generate an index of all the exported symbols in the input Nim module, and put it into a neighboring file with the extension of ``.idx``. The -index file is line oriented (newlines have to be escaped). Each line -represents a tab separated record of several columns, the first two mandatory, +index file is line-oriented (newlines have to be escaped). Each line +represents a tab-separated record of several columns, the first two mandatory, the rest optional. See the `Index (idx) file format`_ section for details. Once index files have been generated for one or more modules, the Nim @@ -231,7 +231,7 @@ You can edit ``config/nimdoc.cfg`` and modify the ``doc.item.seesrc`` value with In the case of Nim's own documentation, the ``commit`` value is just a commit hash to append to a formatted URL to https://github.com/nim-lang/Nim. The -``tools/nimweb.nim`` helper queries the current git commit hash during doc +``tools/nimweb.nim`` helper queries the current git commit hash during the doc generation, but since you might be working on an unpublished repository, it also allows specifying a ``githash`` value in ``web/website.ini`` to force a specific commit in the output. @@ -260,8 +260,8 @@ HTML anchor generation ====================== When you run the ``rst2html`` command, all sections in the RST document will -get an anchor you can hyperlink to. Usually you can guess the anchor lower -casing the section title and replacing spaces with dashes, and in any case you +get an anchor you can hyperlink to. Usually, you can guess the anchor lower +casing the section title and replacing spaces with dashes, and in any case, you can get it from the table of contents. But when you run the ``doc`` or ``doc2`` commands to generate API documentation, some symbol get one or two anchors at the same time: a numerical identifier, or a plain name plus a complex name. @@ -274,20 +274,20 @@ numbers may shuffle around. The plain name of a symbol is a simplified version of its fully exported signature. Variables or constants have the same plain name symbol as their complex name. The plain name for procs, templates, and other callable types -will be their unquoted value after removing parameters, return types and -pragmas. The plain name allows short and nice linking of symbols which works +will be their unquoted value after removing parameters, return types, and +pragmas. The plain name allows short and nice linking of symbols that works unless you have a module with collisions due to overloading. If you hyperlink a plain name symbol and there are other matches on the same HTML file, most browsers will go to the first one. To differentiate the rest, you will need to use the complex name. A complex name for a callable type is -made up from several parts: +made up of several parts: (**plain symbol**)(**.type**),(**first param**)?(**,param type**)\* The first thing to note is that all callable types have at least a comma, even if they don't have any parameters. If there are parameters, they are -represented by their types and will be comma separated. To the plain symbol a +represented by their types and will be comma-separated. To the plain symbol a suffix may be added depending on the type of the callable: ------------- -------------- @@ -337,15 +337,15 @@ references so they can be later concatenated into a big index file with `mergeIndexes() `_. This section documents the file format in detail. -Index files are line oriented and tab separated (newline and tab characters -have to be escaped). Each line represents a record with at least two fields, +Index files are line-oriented and tab-separated (newline and tab characters +have to be escaped). Each line represents a record with at least two fields but can have up to four (additional columns are ignored). The content of these columns is: 1. Mandatory term being indexed. Terms can include quoting according to Nim's rules (e.g. \`^\`). 2. Base filename plus anchor hyperlink (e.g. ``algorithm.html#*,int,SortOrder``). -3. Optional human readable string to display as hyperlink. If the value is not +3. Optional human-readable string to display as a hyperlink. If the value is not present or is the empty string, the hyperlink will be rendered using the term. Prefix whitespace indicates that this entry is not for an API symbol but for a TOC entry. @@ -361,14 +361,14 @@ human reading. To differentiate both types (documents and APIs), the index generator will add to the index of documents an entry with the title of the document. Since the title is the topmost element, it will be added with a second field containing -just the filename without any HTML anchor. By convention this entry without +just the filename without any HTML anchor. By convention, this entry without anchor is the *title entry*, and since entries in the index file are added as they are scanned, the title entry will be the first line. The title for APIs is not present because it can be generated concatenating the name of the file to the word **Module**. Normal symbols are added to the index with surrounding whitespaces removed. An -exception to this are table of content (TOC) entries. TOC entries are added to +exception to this are the table of content (TOC) entries. TOC entries are added to the index file with their third column having as much prefix spaces as their level is in the TOC (at least 1 character). The prefix whitespace helps to filter TOC entries from API or text symbols. This is important because the @@ -379,14 +379,14 @@ final index, and TOC entries found in ``.nim`` files are discarded. Additional resources ==================== -`Nim Compiler User Guide `_ +`Nim Compiler User Guide `_ `RST Quick Reference `_ The output for HTML and LaTeX comes from the ``config/nimdoc.cfg`` and ``config/nimdoc.tex.cfg`` configuration files. You can add and modify these -files to your project to change the look of docgen output. +files to your project to change the look of the docgen output. You can import the `packages/docutils/rstgen module `_ in your programs if you want to reuse the compiler's documentation generation procs. diff --git a/doc/drnim.rst b/doc/drnim.rst index 5351daac9..ee6e0ea17 100644 --- a/doc/drnim.rst +++ b/doc/drnim.rst @@ -13,8 +13,8 @@ Introduction This document describes the usage of the *DrNim* tool. DrNim combines the Nim frontend with the `Z3 `_ proof -engine in order to allow verify / validate software written in Nim. -DrNim's command line options are the same as the Nim compiler's. +engine, in order to allow verify/validate software written in Nim. +DrNim's command-line options are the same as the Nim compiler's. DrNim currently only checks the sections of your code that are marked @@ -140,8 +140,8 @@ Example: insertionSort swap a[t], a[t-1] dec t -Unfortunately the invariants required to prove this code correct take more -code than the imperative instructions. However this effort can be compensated +Unfortunately, the invariants required to prove that this code is correct take more +code than the imperative instructions. However, this effort can be compensated by the fact that the result needs very little testing. Be aware though that DrNim only proves that after ``insertionSort`` this condition holds:: diff --git a/doc/hcr.rst b/doc/hcr.rst index 55ccc06dc..d2e13e1c7 100644 --- a/doc/hcr.rst +++ b/doc/hcr.rst @@ -14,11 +14,11 @@ preserved. Basic workflow ============== -Currently hot code reloading does not work for the main module itself, +Currently, hot code reloading does not work for the main module itself, so we have to use a helper module where the major logic we want to change during development resides. -In this example we use SDL2 to create a window and we reload the logic +In this example, we use SDL2 to create a window and we reload the logic code when ``F9`` is pressed. The important lines are marked with ``#***``. To install SDL2 you can use ``nimble install sdl2``. @@ -125,7 +125,7 @@ Then recompile the project, but do not restart or quit the mymain.exe program! nim c --hotcodereloading:on mymain.nim -Now give the ``mymain`` SDL window the focus, press F9 and watch the +Now give the ``mymain`` SDL window the focus, press F9, and watch the updated version of the program. diff --git a/doc/koch.rst b/doc/koch.rst index e984a71ea..01c690816 100644 --- a/doc/koch.rst +++ b/doc/koch.rst @@ -75,8 +75,8 @@ from rst to HTML. It also repeats the same operation but places the result in the ``web/upload`` which can be used to update the website at https://nim-lang.org. -By default the documentation will be built in parallel using the number of -available CPU cores. If any documentation build sub commands fail, they will +By default, the documentation will be built in parallel using the number of +available CPU cores. If any documentation build sub-commands fail, they will be rerun in serial fashion so that meaningful error output can be gathered for inspection. The ``--parallelBuild:n`` switch or configuration option can be used to force a specific number of parallel jobs or run everything serially diff --git a/doc/lib.rst b/doc/lib.rst index e05657b42..2dd43109e 100644 --- a/doc/lib.rst +++ b/doc/lib.rst @@ -7,7 +7,7 @@ Nim Standard Library .. contents:: -Nim's library is divided into *pure libraries*, *impure libraries* and *wrappers*. +Nim's library is divided into *pure libraries*, *impure libraries*, and *wrappers*. Pure libraries do not depend on any external ``*.dll`` or ``lib*.so`` binary while impure libraries do. A wrapper is an impure library that is a very @@ -32,7 +32,7 @@ Automatic imports * `system `_ Basic procs and operators that every program needs. It also provides IO facilities for reading and writing text and binary files. It is imported - implicitly by the compiler. Do not import it directly. It relies on compiler + implicitly by the compiler. Do not import it directly. It relies on compiler magic to work. * `threads `_ @@ -48,7 +48,7 @@ Core ---- * `bitops `_ - Provides a series of low level methods for bit manipulation. + Provides a series of low-level methods for bit manipulation. * `cpuinfo `_ This module implements procs to determine the number of CPUs / cores. @@ -69,7 +69,7 @@ Core Reentrant locks for Nim. * `typeinfo `_ - Provides (unsafe) access to Nim's run time type information. + Provides (unsafe) access to Nim's run-time type information. * `typetraits `_ This module defines compile-time reflection procs for working with types. @@ -83,7 +83,7 @@ Algorithms ---------- * `algorithm `_ - Implements some common generic algorithms like sort or binary search. + This module implements some common generic algorithms like sort or binary search. * `sequtils `_ This module implements operations for the built-in seq type @@ -96,7 +96,7 @@ Collections * `critbits `_ This module implements a *crit bit tree* which is an efficient - container for a sorted set of strings, or for a sorted mapping of strings. + container for a sorted set of strings, or a sorted mapping of strings. * `deques `_ Implementation of a double-ended queue. @@ -119,13 +119,13 @@ Collections Nim hash and bit set support. * `sharedlist `_ - Nim shared linked list support. Contains shared singly linked list. + Nim shared linked list support. Contains a shared singly-linked list. * `sharedtables `_ Nim shared hash table support. Contains shared tables. * `tables `_ - Nim hash table support. Contains tables, ordered tables and count tables. + Nim hash table support. Contains tables, ordered tables, and count tables. @@ -154,11 +154,11 @@ String handling * `ropes `_ This module contains support for a *rope* data type. - Ropes can represent very long strings efficiently; especially concatenation - is done in O(1) instead of O(n). + Ropes can represent very long strings efficiently; + especially concatenation is done in O(1) instead of O(n). * `strformat `_ - Macro based standard string interpolation / formatting. Inspired by + Macro based standard string interpolation/formatting. Inspired by Python's ``f``-strings. * `strmisc `_ @@ -207,7 +207,7 @@ Generic Operating System Services and the OS's native package manager. Its primary purpose is to produce output for Nimble packages, but it also contains the widely used **Distribution** enum - that is useful for writing platform specific code. + that is useful for writing platform-specific code. See `packaging `_ for hints on distributing Nim using OS packages. * `dynlib `_ @@ -218,7 +218,7 @@ Generic Operating System Services data structures. * `memfiles `_ - This module provides support for memory mapped files (Posix's ``mmap``) + This module provides support for memory-mapped files (Posix's ``mmap``) on the different operating systems. * `os `_ @@ -231,8 +231,8 @@ Generic Operating System Services * `streams `_ This module provides a stream interface and two implementations thereof: - the `FileStream` and the `StringStream` which implement the stream - interface for Nim file objects (`File`) and strings. Other modules + the `FileStream` and the `StringStream` which implement the stream + interface for Nim file objects (`File`) and strings. Other modules may provide other implementations for this standard stream interface. * `terminal `_ @@ -245,7 +245,7 @@ Math libraries -------------- * `complex `_ - This module implements complex numbers and their mathematical operations. + This module implements complex numbers and relevant mathematical operations. * `fenv `_ Floating-point environment. Handling of floating-point rounding and @@ -261,7 +261,7 @@ Math libraries Fast and tiny random number generator. * `rationals `_ - This module implements rational numbers and their mathematical operations. + This module implements rational numbers and relevant mathematical operations. * `stats `_ Statistical analysis @@ -317,10 +317,10 @@ Internet Protocols and Support * `selectors `_ This module implements a selector API with backends specific to each OS. - Currently epoll on Linux and select on other operating systems. + Currently, epoll on Linux and select on other operating systems. * `smtp `_ - This module implement a simple SMTP client. + This module implements a simple SMTP client. * `uri `_ This module provides functions for working with URIs. @@ -340,47 +340,46 @@ Parsers This module parses an HTML document and creates its XML tree representation. * `json `_ - High performance JSON parser. + High-performance JSON parser. * `lexbase `_ - This is a low level module that implements an extremely efficient buffering + This is a low-level module that implements an extremely efficient buffering scheme for lexers and parsers. This is used by the diverse parsing modules. * `parsecfg `_ - The ``parsecfg`` module implements a high performance configuration file + The ``parsecfg`` module implements a high-performance configuration file parser. The configuration file's syntax is similar to the Windows ``.ini`` format, but much more powerful, as it is not a line based parser. String - literals, raw string literals and triple quote string literals are supported + literals, raw string literals, and triple quote string literals are supported as in the Nim programming language. * `parsecsv `_ - The ``parsecsv`` module implements a simple high performance CSV parser. + The ``parsecsv`` module implements a simple high-performance CSV parser. * `parseopt `_ The ``parseopt`` module implements a command line option parser. * `parsesql `_ - The ``parsesql`` module implements a simple high performance SQL parser. + The ``parsesql`` module implements a simple high-performance SQL parser. * `parsexml `_ The ``parsexml`` module implements a simple high performance XML/HTML parser. The only encoding that is supported is UTF-8. The parser has been designed - to be somewhat error correcting, so that even some "wild HTML" found on the - Web can be parsed with it. + to be somewhat error-correcting, so that even some "wild HTML" found on the + web can be parsed with it. Docutils -------- * `packages/docutils/highlite `_ - Source highlighter for programming or markup languages. Currently - only few languages are supported, other languages may be added. + Source highlighter for programming or markup languages. Currently, + only a few languages are supported, other languages may be added. The interface supports one language nested in another. * `packages/docutils/rst `_ - This module implements a reStructuredText parser. A large subset - is implemented. Some features of the markdown wiki syntax are - also supported. + This module implements a reStructuredText parser. A large subset + is implemented. Some features of the markdown wiki syntax are also supported. * `packages/docutils/rstast `_ This module implements an AST for the reStructuredText parser. @@ -404,8 +403,8 @@ Generators ---------- * `htmlgen `_ - This module implements a simple XML and HTML code - generator. Each commonly used HTML tag has a corresponding macro + This module implements a simple XML and HTML code + generator. Each commonly used HTML tag has a corresponding macro that generates a string with its HTML representation. @@ -425,7 +424,7 @@ Hashing * `oids `_ An OID is a global ID that consists of a timestamp, - a unique counter and a random value. This combination should suffice to + a unique counter, and a random value. This combination should suffice to produce a globally distributed unique ID. This implementation was extracted from the Mongodb interface and it thus binary compatible with a Mongo OID. @@ -460,7 +459,7 @@ Miscellaneous Implements a Unit testing DSL. * `std/varints `_ - Decode variable length integers that are compatible with SQLite. + Decode variable-length integers that are compatible with SQLite. Modules for JS backend @@ -476,7 +475,7 @@ Modules for JS backend Wrapper for the ``console`` object. * `jscore `_ - Wrapper of core JavaScript functions. For most purposes you should be using + The wrapper of core JavaScript functions. For most purposes, you should be using the ``math``, ``json``, and ``times`` stdlib modules instead of this module. * `jsffi `_ @@ -490,7 +489,7 @@ Regular expressions ------------------- * `re `_ - This module contains procedures and operators for handling regular + This module contains procedures and operators for handling regular expressions. The current implementation uses PCRE. @@ -517,7 +516,7 @@ The generated HTML for some of these wrappers is so huge that it is not contained in the distribution. You can then find them on the website. -Windows specific +Windows-specific ---------------- * `winlean `_ @@ -545,7 +544,7 @@ GUI libraries ------------- * `iup `_ - Wrapper of the IUP GUI library. + The wrapper of the IUP GUI library. Database support diff --git a/doc/manual.rst b/doc/manual.rst index ca53999ac..d09bb4c76 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -8,8 +8,8 @@ Nim Manual .. contents:: - "Complexity" seems to be a lot like "energy": you can transfer it from the end - user to one/some of the other players, but the total amount seems to remain + "Complexity" seems to be a lot like "energy": you can transfer it from the + end-user to one/some of the other players, but the total amount seems to remain pretty much constant for a given task. -- Ran @@ -22,7 +22,7 @@ precise wording. This manual is constantly evolving into a proper specification. **Note**: The experimental features of Nim are covered `here `_. -**Note**: Assignments, moves and destruction are specified in +**Note**: Assignments, moves, and destruction are specified in the `destructors `_ document. @@ -90,9 +90,9 @@ for example, be a native binary or JavaScript source code. In a typical Nim program, most of the code is compiled into the executable. However, some of the code may be executed at -`compile time`:idx:. This can include constant expressions, macro definitions, +`compile-time`:idx:. This can include constant expressions, macro definitions, and Nim procedures used by macro definitions. Most of the Nim language is -supported at compile time, but there are some restrictions -- see `Restrictions +supported at compile-time, but there are some restrictions -- see `Restrictions on Compile-Time Execution <#restrictions-on-compileminustime-execution>`_ for details. We use the term `runtime`:idx: to cover both compile-time execution and code execution in the executable. @@ -125,18 +125,18 @@ compiler may instead choose to allow the program to die with a fatal error. echo "invalid index" The current implementation allows to switch between these different behaviors -via ``--panics:on|off``. When panics are turned on, the program dies on a +via ``--panics:on|off``. When panics are turned on, the program dies with a panic, if they are turned off the runtime errors are turned into exceptions. The benefit of ``--panics:on`` is that it produces smaller binary code and the compiler has more freedom to optimize the code. An `unchecked runtime error`:idx: is an error that is not guaranteed to be -detected, and can cause the subsequent behavior of the computation to +detected and can cause the subsequent behavior of the computation to be arbitrary. Unchecked runtime errors cannot occur if only `safe`:idx: language features are used and if no runtime checks are disabled. A `constant expression`:idx: is an expression whose value can be computed during -semantic analysis of the code in which it appears. It is never an l-value and +a semantic analysis of the code in which it appears. It is never an l-value and never has side effects. Constant expressions are not limited to the capabilities of semantic analysis, such as constant folding; they can use all Nim language features that are supported for compile-time execution. Since constant @@ -163,7 +163,7 @@ encodings are not supported. Any of the standard platform line termination sequences can be used - the Unix form using ASCII LF (linefeed), the Windows form using the ASCII sequence CR LF (return followed by linefeed), or the old Macintosh form using the ASCII CR (return) character. All of these forms can be -used equally, regardless of platform. +used equally, regardless of the platform. Indentation @@ -180,7 +180,7 @@ lookahead. The parser uses a stack of indentation levels: the stack consists of integers counting the spaces. The indentation information is queried at strategic -places in the parser but ignored otherwise: The pseudo terminal ``IND{>}`` +places in the parser but ignored otherwise: The pseudo-terminal ``IND{>}`` denotes an indentation that consists of more spaces than the entry at the top of the stack; ``IND{=}`` an indentation that has the same number of spaces. ``DED`` is another pseudo terminal that describes the *action* of popping a value @@ -265,7 +265,7 @@ and underscores, with the following restrictions: digit ::= '0'..'9' IDENTIFIER ::= letter ( ['_'] (letter | digit) )* -Currently any Unicode character with an ordinal value > 127 (non ASCII) is +Currently, any Unicode character with an ordinal value > 127 (non-ASCII) is classified as a ``letter`` and may thus be part of an identifier but later versions of the language may assign some Unicode characters to belong to the operator characters instead. @@ -289,11 +289,11 @@ Two identifiers are considered equal if the following algorithm returns true: a[0] == b[0] and a.replace("_", "").toLowerAscii == b.replace("_", "").toLowerAscii -That means only the first letters are compared in a case sensitive manner. Other -letters are compared case insensitively within the ASCII range and underscores are ignored. +That means only the first letters are compared in a case-sensitive manner. Other +letters are compared case-insensitively within the ASCII range and underscores are ignored. This rather unorthodox way to do identifier comparisons is called -`partial case insensitivity`:idx: and has some advantages over the conventional +`partial case-insensitivity`:idx: and has some advantages over the conventional case sensitivity: It allows programmers to mostly use their own preferred @@ -455,7 +455,7 @@ literals: exactly two hex digits are allowed ================== =================================================== -A character is not an Unicode character but a single byte. The reason for this +A character is not a Unicode character but a single byte. The reason for this is efficiency: for the overwhelming majority of use-cases, the resulting programs will still handle UTF-8 properly as UTF-8 was specially designed for this. Another reason is that Nim can thus support ``array[char, int]`` or @@ -504,18 +504,18 @@ Numerical constants are of a single type and have the form:: As can be seen in the productions, numerical constants can contain underscores -for readability. Integer and floating point literals may be given in decimal (no -prefix), binary (prefix ``0b``), octal (prefix ``0o``) and hexadecimal +for readability. Integer and floating-point literals may be given in decimal (no +prefix), binary (prefix ``0b``), octal (prefix ``0o``), and hexadecimal (prefix ``0x``) notation. There exists a literal for each numerical type that is defined. The suffix starting with an apostrophe ('\'') is called a -`type suffix`:idx:. Literals without a type suffix are of an integer type, +`type suffix`:idx:. Literals without a type suffix are of an integer type unless the literal contains a dot or ``E|e`` in which case it is of type ``float``. This integer type is ``int`` if the literal is in the range ``low(i32)..high(i32)``, otherwise it is ``int64``. -For notational convenience the apostrophe of a type suffix -is optional if it is not ambiguous (only hexadecimal floating point literals +For notational convenience, the apostrophe of a type suffix +is optional if it is not ambiguous (only hexadecimal floating-point literals with a type suffix can be ambiguous). @@ -539,12 +539,12 @@ The type suffixes are: ``'f64`` float64 ================= ========================= -Floating point literals may also be in binary, octal or hexadecimal +Floating-point literals may also be in binary, octal or hexadecimal notation: ``0B0_10001110100_0000101001000111101011101111111011000101001101001001'f64`` -is approximately 1.72826e35 according to the IEEE floating point standard. +is approximately 1.72826e35 according to the IEEE floating-point standard. -Literals are bounds checked so that they fit the datatype. Non base-10 +Literals are bounds checked so that they fit the datatype. Non-base-10 literals are used mainly for flags and bit pattern representations, therefore bounds checking is done on bit width, not value range. If the literal fits in the bit width of the datatype, it is accepted. @@ -626,7 +626,7 @@ operator which binds stronger than a ``primarySuffix``: ``@x.abc`` is parsed as ``(@x).abc`` whereas ``$x.abc`` is parsed as ``$(x.abc)``. -For binary operators that are not keywords the precedence is determined by the +For binary operators that are not keywords, the precedence is determined by the following rules: Operators ending in either ``->``, ``~>`` or ``=>`` are called @@ -634,9 +634,9 @@ Operators ending in either ``->``, ``~>`` or ``=>`` are called If the operator ends with ``=`` and its first character is none of ``<``, ``>``, ``!``, ``=``, ``~``, ``?``, it is an *assignment operator* which -has the second lowest precedence. +has the second-lowest precedence. -Otherwise precedence is determined by the first character. +Otherwise, precedence is determined by the first character. ================ ======================================================= ================== =============== Precedence level Operators First character Terminal symbol @@ -664,7 +664,7 @@ whitespace (this parsing change was introduced with version 0.13.0): echo($foo) -Spacing also determines whether ``(a, b)`` is parsed as an the argument list +Spacing also determines whether ``(a, b)`` is parsed as an argument list of a call or whether it is parsed as a tuple constructor: .. code-block:: nim @@ -798,7 +798,7 @@ block. The ability to access and modify compile-time variables adds flexibility to constant expressions that may be surprising to those coming from other statically typed languages. For example, the following code echoes the beginning -of the Fibonacci series **at compile time**. (This is a demonstration of +of the Fibonacci series **at compile-time**. (This is a demonstration of flexibility in defining constants, not a recommended style for solving this problem!) @@ -854,7 +854,7 @@ Some or all of these restrictions are likely to be lifted over time. Types ===== -All expressions have a type which is known during semantic analysis. Nim +All expressions have a type that is known during semantic analysis. Nim is statically typed. One can declare new types, which is in essence defining an identifier that can be used to denote this custom type. @@ -862,7 +862,7 @@ These are the major type classes: * ordinal types (consist of integer, bool, character, enumeration (and subranges thereof) types) -* floating point types +* floating-point types * string type * structured types * reference (pointer) type @@ -877,12 +877,12 @@ Ordinal types have the following characteristics: - Ordinal types are countable and ordered. This property allows the operation of functions as ``inc``, ``ord``, ``dec`` on ordinal types to be defined. -- Ordinal values have a smallest possible value. Trying to count further +- Ordinal values have the smallest possible value. Trying to count further down than the smallest value produces a panic or a static error. -- Ordinal values have a largest possible value. Trying to count further +- Ordinal values have the largest possible value. Trying to count further than the largest value produces a panic or a static error. -Integers, bool, characters and enumeration types (and subranges of these +Integers, bool, characters, and enumeration types (and subranges of these types) belong to ordinal types. For reasons of simplicity of implementation the types ``uint`` and ``uint64`` are not ordinal types. (This will be changed in later versions of the language.) @@ -895,25 +895,23 @@ Pre-defined integer types These integer types are pre-defined: ``int`` - the generic signed integer type; its size is platform dependent and has the + the generic signed integer type; its size is platform-dependent and has the same size as a pointer. This type should be used in general. An integer literal that has no type suffix is of this type if it is in the range ``low(int32)..high(int32)`` otherwise the literal's type is ``int64``. intXX additional signed integer types of XX bits use this naming scheme - (example: int16 is a 16 bit wide integer). + (example: int16 is a 16-bit wide integer). The current implementation supports ``int8``, ``int16``, ``int32``, ``int64``. Literals of these types have the suffix 'iXX. ``uint`` - the generic `unsigned integer`:idx: type; its size is platform dependent and - has the same size as a pointer. An integer literal with the type - suffix ``'u`` is of this type. + the generic `unsigned integer`:idx: type; its size is platform-dependent and has the same size as a pointer. An integer literal with the type suffix ``'u`` is of this type. uintXX additional unsigned integer types of XX bits use this naming scheme - (example: uint16 is a 16 bit wide unsigned integer). + (example: uint16 is a 16-bit wide unsigned integer). The current implementation supports ``uint8``, ``uint16``, ``uint32``, ``uint64``. Literals of these types have the suffix 'uXX. Unsigned operations all wrap around; they cannot lead to over- or @@ -977,7 +975,7 @@ For further details, see `Convertible relation Subrange types -------------- -A subrange type is a range of values from an ordinal or floating point type (the base +A subrange type is a range of values from an ordinal or floating-point type (the base type). To define a subrange type, one must specify its limiting values -- the lowest and highest value of the type. For example: @@ -988,8 +986,8 @@ lowest and highest value of the type. For example: ``Subrange`` is a subrange of an integer which can only hold the values 0 -to 5. ``PositiveFloat`` defines a subrange of all positive floating point values. -NaN does not belong to any subrange of floating point types. +to 5. ``PositiveFloat`` defines a subrange of all positive floating-point values. +NaN does not belong to any subrange of floating-point types. Assigning any other value to a variable of type ``Subrange`` is a panic (or a static error if it can be determined during semantic analysis). Assignments from the base type to one of its subrange types @@ -999,27 +997,27 @@ A subrange type has the same size as its base type (``int`` in the Subrange example). -Pre-defined floating point types +Pre-defined floating-point types -------------------------------- -The following floating point types are pre-defined: +The following floating-point types are pre-defined: ``float`` - the generic floating point type; its size used to be platform dependent, + the generic floating-point type; its size used to be platform-dependent, but now it is always mapped to ``float64``. This type should be used in general. floatXX - an implementation may define additional floating point types of XX bits using - this naming scheme (example: float64 is a 64 bit wide float). The current + an implementation may define additional floating-point types of XX bits using + this naming scheme (example: float64 is a 64-bit wide float). The current implementation supports ``float32`` and ``float64``. Literals of these types have the suffix 'fXX. Automatic type conversion in expressions with different kinds -of floating point types is performed: See `Convertible relation`_ for further -details. Arithmetic performed on floating point types follows the IEEE -standard. Integer types are not converted to floating point types automatically +of floating-point types is performed: See `Convertible relation`_ for further +details. Arithmetic performed on floating-point types follows the IEEE +standard. Integer types are not converted to floating-point types automatically and vice versa. The IEEE standard defines five types of floating-point exceptions: @@ -1059,7 +1057,7 @@ combination of ``nanChecks`` and ``infChecks`` pragmas. ``floatChecks`` are turned off as default. The only operations that are affected by the ``floatChecks`` pragma are -the ``+``, ``-``, ``*``, ``/`` operators for floating point types. +the ``+``, ``-``, ``*``, ``/`` operators for floating-point types. An implementation should always use the maximum precision available to evaluate floating pointer values during semantic analysis; this means expressions like @@ -1094,9 +1092,9 @@ The size of the bool type is one byte. Character type -------------- The character type is named ``char`` in Nim. Its size is one byte. -Thus it cannot represent an UTF-8 character, but a part of it. +Thus it cannot represent a UTF-8 character, but a part of it. The reason for this is efficiency: for the overwhelming majority of use-cases, -the resulting programs will still handle UTF-8 properly as UTF-8 was specially +the resulting programs will still handle UTF-8 properly as UTF-8 was especially designed for this. Another reason is that Nim can support ``array[char, int]`` or ``set[char]`` efficiently as many algorithms rely on this feature. The @@ -1144,7 +1142,7 @@ An explicit ordered enum can have *holes*: TokenType = enum a = 2, b = 4, c = 89 # holes are valid -However, it is then not an ordinal anymore, so it is not possible to use these +However, it is then not ordinal anymore, so it is not possible to use these enums as an index type for arrays. The procedures ``inc``, ``dec``, ``succ`` and ``pred`` are not available for them either. @@ -1166,8 +1164,8 @@ As can be seen from the example, it is possible to both specify a field's ordinal value and its string value by using a tuple. It is also possible to only specify one of them. -An enum can be marked with the ``pure`` pragma so that it's fields are -added to a special module specific hidden scope that is only queried +An enum can be marked with the ``pure`` pragma so that its fields are +added to a special module-specific hidden scope that is only queried as the last attempt. Only non-ambiguous symbols are added to this scope. But one can always access these via type qualification written as ``MyEnum.value``: @@ -1254,7 +1252,7 @@ cstring type The ``cstring`` type meaning `compatible string` is the native representation of a string for the compilation backend. For the C backend the ``cstring`` type represents a pointer to a zero-terminated char array -compatible to the type ``char*`` in Ansi C. Its primary purpose lies in easy +compatible with the type ``char*`` in Ansi C. Its primary purpose lies in easy interfacing with C. The index operation ``s[i]`` means the i-th *char* of ``s``; however no bounds checking for ``cstring`` is performed making the index operation unsafe. @@ -1271,7 +1269,7 @@ variadic proc, it is implicitly converted to ``cstring`` too: Even though the conversion is implicit, it is not *safe*: The garbage collector does not consider a ``cstring`` to be a root and may collect the underlying -memory. However in practice this almost never happens as the GC considers +memory. However, in practice, this almost never happens as the GC considers stack roots conservatively. One can use the builtin procs ``GC_ref`` and ``GC_unref`` to keep the string data alive for the rare cases where it does not work. @@ -1288,7 +1286,7 @@ Structured types ---------------- A variable of a structured type can hold multiple values at the same time. Structured types can be nested to unlimited levels. Arrays, sequences, -tuples, objects and sets belong to the structured types. +tuples, objects, and sets belong to the structured types. Array and sequence types ------------------------ @@ -1341,7 +1339,7 @@ The notation ``x[i]`` can be used to access the i-th element of ``x``. Arrays are always bounds checked (statically or at runtime). These checks can be disabled via pragmas or invoking the compiler with the -``--boundChecks:off`` command line switch. +``--boundChecks:off`` command-line switch. An array constructor can have explicit indexes for readability: @@ -1456,7 +1454,7 @@ Unchecked arrays ---------------- The ``UncheckedArray[T]`` type is a special kind of ``array`` where its bounds are not checked. This is often useful to implement customized flexibly sized -arrays. Additionally an unchecked array is translated into a C array of +arrays. Additionally, an unchecked array is translated into a C array of undetermined size: .. code-block:: nim @@ -1526,7 +1524,7 @@ trailing comma: In fact, a trailing comma is allowed for every tuple construction. -The implementation aligns the fields for best access performance. The alignment +The implementation aligns the fields for the best access performance. The alignment is compatible with the way the C compiler does it. For consistency with ``object`` declarations, tuples in a ``type`` section @@ -1540,7 +1538,7 @@ can also be defined with indentation instead of ``[]``: Objects provide many features that tuples do not. Object provide inheritance and the ability to hide fields from other modules. Objects with inheritance enabled -have information about their type at runtime, so that the ``of`` operator can be +have information about their type at runtime so that the ``of`` operator can be used to determine the object's type. The ``of`` operator is similar to the ``instanceof`` operator in Java. @@ -1592,8 +1590,8 @@ For a ``ref object`` type ``system.new`` is invoked implicitly. Object variants --------------- -Often an object hierarchy is overkill in certain situations where simple variant -types are needed. Object variants are tagged unions discriminated via a +Often an object hierarchy is an overkill in certain situations where simple variant +types are needed. Object variants are tagged unions discriminated via an enumerated type used for runtime type flexibility, mirroring the concepts of *sum types* and *algebraic data types (ADTs)* as found in other languages. @@ -1638,14 +1636,14 @@ An example: # valid: does not change the active object branch: x.kind = nkSub -As can been seen from the example, an advantage to an object hierarchy is that +As can be seen from the example, an advantage to an object hierarchy is that no casting between different object types is needed. Yet, access to invalid object fields raises an exception. The syntax of ``case`` in an object declaration follows closely the syntax of the ``case`` statement: The branches in a ``case`` section may be indented too. -In the example the ``kind`` field is called the `discriminator`:idx:\: For +In the example, the ``kind`` field is called the `discriminator`:idx:\: For safety its address cannot be taken and assignments to it are restricted: The new value must not lead to a change of the active object branch. Also, when the fields of a particular branch are specified during object construction, the @@ -1708,16 +1706,16 @@ point to and modify the same location in memory (also called `aliasing`:idx:). Nim distinguishes between `traced`:idx: and `untraced`:idx: references. Untraced references are also called *pointers*. Traced references point to -objects of a garbage collected heap, untraced references point to -manually allocated objects or to objects somewhere else in memory. Thus -untraced references are *unsafe*. However for certain low-level operations +objects of a garbage-collected heap, untraced references point to +manually allocated objects or objects somewhere else in memory. Thus +untraced references are *unsafe*. However, for certain low-level operations (accessing the hardware) untraced references are unavoidable. Traced references are declared with the **ref** keyword, untraced references are declared with the **ptr** keyword. In general, a `ptr T` is implicitly convertible to the `pointer` type. -An empty subscript ``[]`` notation can be used to derefer a reference, +An empty subscript ``[]`` notation can be used to de-refer a reference, the ``addr`` procedure returns the address of an item. An address is always an untraced reference. Thus the usage of ``addr`` is an *unsafe* feature. @@ -1807,7 +1805,7 @@ Mixing GC'ed memory with ``ptr`` -------------------------------- Special care has to be taken if an untraced object contains traced objects like -traced references, strings or sequences: in order to free everything properly, +traced references, strings, or sequences: in order to free everything properly, the built-in procedure ``reset`` has to be called before freeing the untraced memory manually: @@ -1828,8 +1826,7 @@ memory manually: dealloc(d) Without the ``reset`` call the memory allocated for the ``d.s`` string would -never be freed. The example also demonstrates two important features for low -level programming: the ``sizeof`` proc returns the size of a type or value +never be freed. The example also demonstrates two important features for low-level programming: the ``sizeof`` proc returns the size of a type or value in bytes. The ``cast`` operator can circumvent the type system: the compiler is forced to treat the result of the ``alloc0`` call (which returns an untyped pointer) as if it would have the type ``ptr Data``. Casting should only be @@ -1838,8 +1835,8 @@ mysterious crashes. **Note**: The example only works because the memory is initialized to zero (``alloc0`` instead of ``alloc`` does this): ``d.s`` is thus initialized to -binary zero which the string assignment can handle. One needs to know low level -details like this when mixing garbage collected data with unmanaged memory. +binary zero which the string assignment can handle. One needs to know low-level +details like this when mixing garbage-collected data with unmanaged memory. .. XXX finalizers for traced objects @@ -1948,7 +1945,7 @@ accesses its environment. If it does so, it has the calling convention Distinct type ------------- -A ``distinct`` type is new type derived from a `base type`:idx: that is +A ``distinct`` type is a new type derived from a `base type`:idx: that is incompatible with its base type. In particular, it is an essential property of a distinct type that it **does not** imply a subtype relation between it and its base type. Explicit type conversions from a distinct type to its @@ -1958,7 +1955,7 @@ reverse operation. A distinct type is an ordinal type if its base type is an ordinal type. -Modelling currencies +Modeling currencies ~~~~~~~~~~~~~~~~~~~~ A distinct type can be used to model different physical `units`:idx: with a @@ -2002,7 +1999,7 @@ number without unit; and the same holds for division: This quickly gets tedious. The implementations are trivial and the compiler should not generate all this code only to optimize it away later - after all ``+`` for dollars should produce the same binary code as ``+`` for ints. -The pragma `borrow`:idx: has been designed to solve this problem; in principle +The pragma `borrow`:idx: has been designed to solve this problem; in principle, it generates the above trivial implementations: .. code-block:: nim @@ -2067,14 +2064,14 @@ certain builtin operations to be lifted: bb.a = 90 bb.s = "abc" -Currently only the dot accessor can be borrowed in this way. +Currently, only the dot accessor can be borrowed in this way. Avoiding SQL injection attacks ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ An SQL statement that is passed from Nim to an SQL database might be -modelled as a string. However, using string templates and filling in the +modeled as a string. However, using string templates and filling in the values is vulnerable to the famous `SQL injection attack`:idx:\: .. code-block:: nim @@ -2152,7 +2149,7 @@ Is the same as: .. code-block:: nim proc foo[T1, T2](a: T1, b: T2) = discard -However later versions of the language might change this to mean "infer the +However, later versions of the language might change this to mean "infer the parameters' types from the body". Then the above ``foo`` would be rejected as the parameters' types can not be inferred from an empty ``discard`` statement. @@ -2375,8 +2372,8 @@ of the argument. 1. Exact match: ``a`` and ``f`` are of the same type. 2. Literal match: ``a`` is an integer literal of value ``v`` and ``f`` is a signed or unsigned integer type and ``v`` is in ``f``'s - range. Or: ``a`` is a floating point literal of value ``v`` - and ``f`` is a floating point type and ``v`` is in ``f``'s + range. Or: ``a`` is a floating-point literal of value ``v`` + and ``f`` is a floating-point type and ``v`` is in ``f``'s range. 3. Generic match: ``f`` is a generic type and ``a`` matches, for instance ``a`` is ``int`` and ``f`` is a generic (constrained) parameter @@ -2384,7 +2381,7 @@ of the argument. 4. Subrange or subtype match: ``a`` is a ``range[T]`` and ``T`` matches ``f`` exactly. Or: ``a`` is a subtype of ``f``. 5. Integral conversion match: ``a`` is convertible to ``f`` and ``f`` and ``a`` - is some integer or floating point type. + is some integer or floating-point type. 6. Conversion match: ``a`` is convertible to ``f``, possibly via a user defined ``converter``. @@ -2507,7 +2504,7 @@ Lazy type resolution for untyped lookups and no type checking have been performed. Since templates and macros that are not declared as ``immediate`` participate -in overloading resolution it's essential to have a way to pass unresolved +in overloading resolution, it's essential to have a way to pass unresolved expressions to a template or macro. This is what the meta-type ``untyped`` accomplishes: @@ -2549,7 +2546,7 @@ statements. Statements are separated into `simple statements`:idx: and `complex statements`:idx:. Simple statements are statements that cannot contain other statements like -assignments, calls or the ``return`` statement; complex statements can +assignments, calls, or the ``return`` statement; complex statements can contain other statements. To avoid the `dangling else problem`:idx:, complex statements always have to be indented. The details can be found in the grammar. @@ -2559,7 +2556,7 @@ Statement list expression Statements can also occur in an expression context that looks like ``(stmt1; stmt2; ...; ex)``. This is called -an statement list expression or ``(;)``. The type +a statement list expression or ``(;)``. The type of ``(stmt1; stmt2; ...; ex)`` is the type of ``ex``. All the other statements must be of type ``void``. (One can use ``discard`` to produce a ``void`` type.) ``(;)`` does not introduce a new scope. @@ -2635,7 +2632,7 @@ Var statement ------------- Var statements declare new local and global variables and -initialize them. A comma separated list of variables can be used to specify +initialize them. A comma-separated list of variables can be used to specify variables of the same type: .. code-block:: nim @@ -2684,7 +2681,7 @@ If a proc is annotated with the ``noinit`` pragma this refers to its implicit The implicit initialization can be also prevented by the `requiresInit`:idx: type pragma. The compiler requires an explicit initialization for the object -and all of its fields. However it does a `control flow analysis`:idx: to prove +and all of its fields. However, it does a `control flow analysis`:idx: to prove the variable has been initialized and does not rely on syntactic properties: .. code-block:: nim @@ -2710,7 +2707,7 @@ statement, except that the keyword ``var`` is replaced by the keyword ``let``. Let variables are not l-values and can thus not be passed to ``var`` parameters nor can their address be taken. They cannot be assigned new values. -For let variables the same pragmas are available as for ordinary variables. +For let variables, the same pragmas are available as for ordinary variables. As ``let`` statements are immutable after creation they need to define a value when they are declared. The only exception to this is if the ``{.importc.}`` @@ -2837,7 +2834,7 @@ ordinal types. "All possible values" of ``expr`` are determined by ``expr``'s type. To suppress the static error an ``else`` part with an empty ``discard`` statement should be used. -For non ordinal types it is not possible to list every possible value and so +For non-ordinal types, it is not possible to list every possible value and so these always require an ``else`` part. Because case statements are checked for exhaustiveness during semantic analysis, @@ -2928,8 +2925,8 @@ within ``object`` definitions. When nimvm statement -------------------- -``nimvm`` is a special symbol, that may be used as expression of ``when nimvm`` -statement to differentiate execution path between compile time and the +``nimvm`` is a special symbol, that may be used as an expression of ``when nimvm`` +statement to differentiate execution path between compile-time and the executable. Example: @@ -2995,7 +2992,7 @@ Example: The ``yield`` statement is used instead of the ``return`` statement in iterators. It is only valid in iterators. Execution is returned to the body of the for loop that called the iterator. Yield does not end the iteration -process, but execution is passed back to the iterator if the next iteration +process, but the execution is passed back to the iterator if the next iteration starts. See the section about iterators (`Iterators and the for statement`_) for further information. @@ -3155,7 +3152,7 @@ name ``c`` should default to type ``Context``, ``n`` should default to The ``using`` section uses the same indentation based grouping syntax as a ``var`` or ``let`` section. -Note that ``using`` is not applied for ``template`` since untyped template +Note that ``using`` is not applied for ``template`` since the untyped template parameters default to the type ``system.untyped``. Mixing parameters that should use the ``using`` declaration with parameters @@ -3202,9 +3199,9 @@ Block expression ---------------- A `block expression` is almost like a block statement, but it is an expression -that uses last expression under the block as the value. +that uses the last expression under the block as the value. It is similar to the statement list expression, but the statement list expression -does not open new block scope. +does not open a new block scope. .. code-block:: nim let a = block: @@ -3236,7 +3233,7 @@ has lots of advantages: can easily put it into the executable's data section just like it can for arrays and the generated data section requires a minimal amount of memory. -* Every table implementation is treated equal syntactically. +* Every table implementation is treated equally syntactically. * Apart from the minimal syntactic sugar the language core does not need to know about tables. @@ -3251,9 +3248,9 @@ results in an exception (if it cannot be determined statically). Ordinary procs are often preferred over type conversions in Nim: For instance, ``$`` is the ``toString`` operator by convention and ``toFloat`` and ``toInt`` -can be used to convert from floating point to integer or vice versa. +can be used to convert from floating-point to integer or vice versa. -A type conversion can also be used to disambiguate overloaded routines: +Type conversion can also be used to disambiguate overloaded routines: .. code-block:: nim @@ -3347,7 +3344,7 @@ declaration consists of an identifier, zero or more formal parameters, a return value type and a block of code. Formal parameters are declared as a list of identifiers separated by either comma or semicolon. A parameter is given a type by ``: typename``. The type applies to all parameters immediately before it, -until either the beginning of the parameter list, a semicolon separator or an +until either the beginning of the parameter list, a semicolon separator, or an already typed parameter, is reached. The semicolon can be used to make separation of types and subsequent identifiers more distinct. @@ -3460,7 +3457,7 @@ current module: Method call syntax ------------------ -For object oriented programming, the syntax ``obj.method(args)`` can be used +For object-oriented programming, the syntax ``obj.method(args)`` can be used instead of ``method(obj, args)``. The parentheses can be omitted if there are no remaining arguments: ``obj.len`` (instead of ``len(obj)``). @@ -3494,7 +3491,7 @@ Properties ---------- Nim has no need for *get-properties*: Ordinary get-procedures that are called with the *method call syntax* achieve the same. But setting a value is -different; for this a special setter syntax is needed: +different; for this, a special setter syntax is needed: .. code-block:: nim # Module asocket @@ -3566,8 +3563,8 @@ more argument in this case: The command invocation syntax also can't have complex expressions as arguments. For example: (`anonymous procs <#procedures-anonymous-procs>`_), ``if``, -``case`` or ``try``. Function calls with no arguments still needs () to -distinguish between a call and the function itself as a first class value. +``case`` or ``try``. Function calls with no arguments still need () to +distinguish between a call and the function itself as a first-class value. Closures @@ -3603,7 +3600,7 @@ procedures: cmp(x.len, y.len)) -Procs as expressions can appear both as nested procs and inside top level +Procs as expressions can appear both as nested procs and inside top-level executable code. The `sugar `_ module contains the `=>` macro which enables a more succinct syntax for anonymous procedures resembling lambdas as they are in languages like JavaScript, C#, etc. @@ -3627,7 +3624,7 @@ Is short for: Nonoverloadable builtins ------------------------ -The following builtin procs cannot be overloaded for reasons of implementation +The following built-in procs cannot be overloaded for reasons of implementation simplicity (they require specialized semantic checking):: declared, defined, definedInScope, compiles, sizeof, @@ -3636,7 +3633,7 @@ simplicity (they require specialized semantic checking):: Thus they act more like keywords than like ordinary identifiers; unlike a keyword however, a redefinition may `shadow`:idx: the definition in the ``system`` module. From this list the following should not be written in dot -notation ``x.f`` since ``x`` cannot be type checked before it gets passed +notation ``x.f`` since ``x`` cannot be type-checked before it gets passed to ``f``:: declared, defined, definedInScope, compiles, getAst, astToStr @@ -3703,7 +3700,7 @@ free to pass arguments by reference if it considers it can speed up execution. Var return type --------------- -A proc, converter or iterator may return a ``var`` type which means that the +A proc, converter, or iterator may return a ``var`` type which means that the returned value is an l-value and can be modified by the caller: .. code-block:: nim @@ -3994,7 +3991,7 @@ the rewriting step, so that all overloads of ``items``/``pairs`` are taken into account. -First class iterators +First-class iterators --------------------- There are 2 kinds of iterators in Nim: *inline* and *closure* iterators. @@ -4009,7 +4006,7 @@ to avoid code bloat. Inline iterators are second class citizens; They can be passed as parameters only to other inlining code facilities like -templates, macros and other inline iterators. +templates, macros, and other inline iterators. In contrast to that, a `closure iterator`:idx: can be passed around more freely: @@ -4284,7 +4281,7 @@ above, ``IOError``), one must convert it explicitly: # "e" is now of the proper type However, this is seldom needed. The most common case is to extract an -error message from ``e``, and for such situations it is enough to use +error message from ``e``, and for such situations, it is enough to use ``getCurrentExceptionMsg``: .. code-block:: nim @@ -4339,7 +4336,7 @@ Is rewritten to: finally: close(f) -Top level ``defer`` statements are not supported +Top-level ``defer`` statements are not supported since it's unclear what such a statement should refer to. @@ -4474,9 +4471,7 @@ possibly raised exceptions; the algorithm operates on ``p``'s call graph: 1. Every indirect call via some proc type ``T`` is assumed to raise ``system.Exception`` (the base type of the exception hierarchy) and thus any exception unless ``T`` has an explicit ``raises`` list. - However if the call is of the form ``f(...)`` where ``f`` is a parameter - of the currently analysed routine it is ignored. The call is optimistically - assumed to have no effect. Rule 2 compensates for this case. + However, if the call is of the form ``f(...)`` where ``f`` is a parameter of the currently analyzed routine it is ignored. The call is optimistically assumed to have no effect. Rule 2 compensates for this case. 2. Every expression of some proc type within a call that is not a call itself (and not nil) is assumed to be called indirectly somehow and thus its raises list is added to ``p``'s raises list. @@ -4579,10 +4574,10 @@ Generics ======== Generics are Nim's means to parametrize procs, iterators or types with -`type parameters`:idx:. Depending on context, the brackets are used either to -introduce type parameters or to instantiate a generic proc, iterator or type. +`type parameters`:idx:. Depending on the context, the brackets are used either to +introduce type parameters or to instantiate a generic proc, iterator, or type. -The following example shows a generic binary tree can be modelled: +The following example shows a generic binary tree can be modeled: .. code-block:: nim :test: "nim c $1" @@ -4705,7 +4700,7 @@ more complex type classes: Whilst the syntax of type classes appears to resemble that of ADTs/algebraic data types in ML-like languages, it should be understood that type classes are static constraints to be enforced at type instantiations. Type classes are not really -types in themselves, but are instead a system of providing generic "checks" that +types in themselves but are instead a system of providing generic "checks" that ultimately *resolve* to some singular type. Type classes do not allow for runtime type dynamism, unlike object variants or methods. @@ -4742,11 +4737,11 @@ A type class can be used directly as the parameter's type. echo key, " = ", value -Procedures utilizing type classes in such manner are considered to be +Procedures utilizing type classes in such a manner are considered to be `implicitly generic`:idx:. They will be instantiated once for each unique combination of param types used within the program. -By default, during overload resolution each named type class will bind to +By default, during overload resolution, each named type class will bind to exactly one concrete type. We call such type classes `bind once`:idx: types. Here is an example taken directly from the system module to illustrate this: @@ -4899,7 +4894,7 @@ at definition and the context at instantiation are considered: echo a == b # works! -In the example the generic ``==`` for tuples (as defined in the system module) +In the example, the generic ``==`` for tuples (as defined in the system module) uses the ``==`` operators of the tuple's components. However, the ``==`` for the ``Index`` type is defined *after* the ``==`` for tuples; yet the example compiles as the instantiation takes the currently defined symbols into account @@ -5008,7 +5003,7 @@ example *undeclared* identifiers can be passed to the template: template declareInt(x: typed) = var x: int - declareInt(x) # invalid, because x has not been declared and so has no type + declareInt(x) # invalid, because x has not been declared and so it has no type A template where every parameter is ``untyped`` is called an `immediate`:idx: template. For historical reasons templates can be explicitly annotated with @@ -5344,7 +5339,7 @@ A macro is a special function that is executed at compile time. Normally the input for a macro is an abstract syntax tree (AST) of the code that is passed to it. The macro can then do transformations on it and return the transformed AST. This can be used to -add custom language features and implement `domain specific languages`:idx:. +add custom language features and implement `domain-specific languages`:idx:. Macro invocation is a case where semantic analysis does **not** entirely proceed top to bottom and left to right. Instead, semantic analysis happens at least @@ -5608,11 +5603,11 @@ possible type mismatch error. typedesc[T] ----------- -In many contexts, Nim allows to treat the names of types as regular -values. These values exists only during the compilation phase, but since +In many contexts, Nim treats the names of types as regular +values. These values exist only during the compilation phase, but since all values must have a type, ``typedesc`` is considered their special type. -``typedesc`` acts like a generic type. For instance, the type of the symbol +``typedesc`` acts as a generic type. For instance, the type of the symbol ``int`` is ``typedesc[int]``. Just like with regular generic types, when the generic param is omitted, ``typedesc`` denotes the type class of all types. As a syntactic convenience, one can also use ``typedesc`` as a modifier. @@ -5649,7 +5644,7 @@ Once bound, type params can appear in the rest of the proc signature: Overload resolution can be further influenced by constraining the set -of types that will match the type param. This works in practice to +of types that will match the type param. This works in practice by attaching attributes to types via templates. The constraint can be a concrete type or a type class. @@ -5705,7 +5700,7 @@ operator): If ``typeof`` is used to determine the result type of a proc/iterator/converter call ``c(X)`` (where ``X`` stands for a possibly empty list of arguments), the -interpretation where ``c`` is an iterator is preferred over the +interpretation, where ``c`` is an iterator, is preferred over the other interpretations, but this behavior can be changed by passing ``typeOfProc`` as the second argument to ``typeof``: @@ -5783,7 +5778,7 @@ imported: It is not checked that the ``except`` list is really exported from the module. -This feature allows to compile against an older version of the module that +This feature allows us to compile against an older version of the module that does not export these identifiers. The ``import`` statement is only allowed at the top level. @@ -5836,7 +5831,7 @@ and so one **cannot** do: import lib/pure/strutils echo lib/pure/strutils.toUpperAscii("abc") -Likewise the following does not make sense as the name is ``strutils`` already: +Likewise, the following does not make sense as the name is ``strutils`` already: .. code-block:: nim import lib/pure/strutils as strutils @@ -5858,7 +5853,7 @@ name is not a valid Nim identifier it needs to be a string literal: Pseudo import/include paths ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -A directory can also be a so called "pseudo directory". They can be used to +A directory can also be a so-called "pseudo directory". They can be used to avoid ambiguity when there are multiple modules with the same path. There are two pseudo directories: @@ -5875,7 +5870,7 @@ library locations*. In other words, it is the opposite of ``std``. From import statement ~~~~~~~~~~~~~~~~~~~~~ -After the ``from`` statement a module name follows followed by +After the ``from`` statement, a module name follows followed by an ``import`` to list the symbols one likes to use without explicit full qualification: @@ -5964,7 +5959,7 @@ the end of the module. Identifiers from indirectly dependent modules are *not* available. The `system`:idx: module is automatically imported in every module. If a module imports an identifier by two different modules, each occurrence of -the identifier has to be qualified, unless it is an overloaded procedure or +the identifier has to be qualified unless it is an overloaded procedure or iterator in which case the overloading resolution takes place: .. code-block:: nim @@ -6121,7 +6116,7 @@ Or if we directly use a ref object: left, right: Node data: string -In the example a tree structure is declared with the ``Node`` type. Note that +In the example, a tree structure is declared with the ``Node`` type. Note that the type definition is recursive and the GC has to assume that objects of this type may form a cyclic graph. The ``acyclic`` pragma passes the information that this cannot happen to the GC. If the programmer uses the @@ -6182,7 +6177,7 @@ assembler statements. error pragma ------------ The ``error`` pragma is used to make the compiler output an error message -with the given content. Compilation does not necessarily abort after an error +with the given content. The compilation does not necessarily abort after an error though. The ``error`` pragma can also be used to @@ -6198,7 +6193,7 @@ operation is valid due to overloading and type conversions: fatal pragma ------------ The ``fatal`` pragma is used to make the compiler output an error message -with the given content. In contrast to the ``error`` pragma, compilation +with the given content. In contrast to the ``error`` pragma, the compilation is guaranteed to be aborted by this pragma. Example: .. code-block:: nim @@ -6218,7 +6213,7 @@ the given content. Compilation continues after the hint. line pragma ----------- The ``line`` pragma can be used to affect line information of the annotated -statement as seen in stack backtraces: +statement, as seen in stack backtraces: .. code-block:: nim @@ -6251,9 +6246,9 @@ statement: In the example, the case branches ``0`` and ``1`` are much more common than the other cases. Therefore the generated assembler code should test for these -values first, so that the CPU's branch predictor has a good chance to succeed +values first so that the CPU's branch predictor has a good chance to succeed (avoiding an expensive CPU pipeline stall). The other cases might be put into a -jump table for O(1) overhead, but at the cost of a (very likely) pipeline +jump table for O(1) overhead but at the cost of a (very likely) pipeline stall. The ``linearScanEnd`` pragma should be put into the last branch that should be @@ -6383,7 +6378,7 @@ but are used to override the settings temporarily. Example: proc sample(): bool = true {.pop.} -For third party pragmas it depends on its implementation, but uses the same syntax. +For third party pragmas, it depends on its implementation but uses the same syntax. register pragma @@ -6445,7 +6440,7 @@ is particularly useful when the symbol was generated by a macro: implementArithOps(int) echoAdd 3, 5 -``used`` can also be used as a top level statement to mark a module as "used". +``used`` can also be used as a top-level statement to mark a module as "used". This prevents the "Unused import" warning: .. code-block:: nim @@ -6462,9 +6457,9 @@ experimental pragma ------------------- The ``experimental`` pragma enables experimental language features. Depending -on the concrete feature this means that the feature is either considered +on the concrete feature, this means that the feature is either considered too unstable for an otherwise stable release or that the future of the feature -is uncertain (it may be removed any time). +is uncertain (it may be removed at any time). Example: @@ -6483,9 +6478,9 @@ Example: useParallel() -As a top level statement, the experimental pragma enables a feature for the +As a top-level statement, the experimental pragma enables a feature for the rest of the module it's enabled in. This is problematic for macro and generic -instantiations that cross a module scope. Currently these usages have to be +instantiations that cross a module scope. Currently, these usages have to be put into a ``.push/pop`` environment: .. code-block:: nim @@ -6539,7 +6534,7 @@ The `align`:idx: pragma is for variables and object field members. It modifies the alignment requirement of the entity being declared. The argument must be a constant power of 2. Valid non-zero alignments that are weaker than other align pragmas on the same -declaration are ignored. Alignments that are weaker that the +declaration are ignored. Alignments that are weaker than the alignment requirement of the type are ignored. .. code-block:: Nim @@ -6562,7 +6557,7 @@ alignment requirement of the type are ignored. main() -This pragma has no effect for the JS backend. +This pragma has no effect on the JS backend. Volatile pragma @@ -6595,7 +6590,7 @@ Header pragma ------------- The ``header`` pragma is very similar to the ``noDecl`` pragma: It can be applied to almost any symbol and specifies that it should not be declared -and instead the generated code should contain an ``#include``: +and instead, the generated code should contain an ``#include``: .. code-block:: Nim type @@ -6630,7 +6625,7 @@ with the project: {.compile: "myfile.cpp".} **Note**: Nim computes a SHA1 checksum and only recompiles the file if it -has changed. One can use the ``-f`` command line option to force recompilation +has changed. One can use the ``-f`` command-line option to force the recompilation of the file. Since 1.4 the `compile` pragma is also available with this syntax: @@ -6653,7 +6648,7 @@ The ``link`` pragma can be used to link an additional file with the project: PassC pragma ------------ The ``passc`` pragma can be used to pass additional parameters to the C -compiler like one would using the commandline switch ``--passc``: +compiler like one would using the command-line switch ``--passc``: .. code-block:: Nim {.passc: "-Wall -Werror".} @@ -6681,7 +6676,7 @@ the pragma resides in: PassL pragma ------------ The ``passL`` pragma can be used to pass additional parameters to the linker -like one would using the commandline switch ``--passL``: +like one would be using the command-line switch ``--passL``: .. code-block:: Nim {.passL: "-lSDLmain -lSDL".} @@ -6728,7 +6723,7 @@ Example: void fun(){} """.} -For backwards compatibility, if the argument to the ``emit`` statement +For backward compatibility, if the argument to the ``emit`` statement is a single string literal, Nim symbols can be referred to via backticks. This usage is however deprecated. @@ -6828,8 +6823,7 @@ language for maximum flexibility: - A hash ``#`` symbol is replaced by the first or next argument. - A dot following the hash ``#.`` indicates that the call should use C++'s dot or arrow notation. -- An at symbol ``@`` is replaced by the remaining arguments, separated by - commas. +- An at symbol ``@`` is replaced by the remaining arguments, separated by commas. For example: @@ -6843,7 +6837,7 @@ Produces: .. code-block:: C x->CppMethod(1, 2, 3) -As a special rule to keep backwards compatibility with older versions of the +As a special rule to keep backward compatibility with older versions of the ``importcpp`` pragma, if there is no special pattern character (any of ``# ' @``) at all, C++'s dot or arrow notation is assumed, so the above example can also be written as: @@ -6862,7 +6856,7 @@ capabilities: - An apostrophe ``'`` followed by an integer ``i`` in the range 0..9 is replaced by the i'th parameter *type*. The 0th position is the result type. This can be used to pass types to C++ function templates. Between - the ``'`` and the digit an asterisk can be used to get to the base type + the ``'`` and the digit, an asterisk can be used to get to the base type of the type. (So it "takes away a star" from the type; ``T*`` becomes ``T``.) Two stars can be used to get to the element type of the element type etc. @@ -6929,7 +6923,7 @@ Wrapping destructors Since Nim generates C++ directly, any destructor is called implicitly by the C++ compiler at the scope exits. This means that often one can get away with -not wrapping the destructor at all! However when it needs to be invoked +not wrapping the destructor at all! However, when it needs to be invoked explicitly, it needs to be wrapped. The pattern language provides everything that is required: @@ -7046,7 +7040,7 @@ The ``codegenDecl`` pragma can be used to directly influence Nim's code generator. It receives a format string that determines how the variable or proc is declared in the generated code. -For variables $1 in the format string represents the type of the variable +For variables, $1 in the format string represents the type of the variable and $2 is the name of the variable. The following Nim code: @@ -7087,7 +7081,7 @@ debugging: # ... complex code here that produces crashes ... -compile time define pragmas +compile-time define pragmas --------------------------- The pragmas listed here can be used to optionally accept values from @@ -7112,7 +7106,7 @@ pragma description nim c -d:FooBar=42 foobar.nim In the above example, providing the -d flag causes the symbol -``FooBar`` to be overwritten at compile time, printing out 42. If the +``FooBar`` to be overwritten at compile-time, printing out 42. If the ``-d:FooBar=42`` were to be omitted, the default value of 5 would be used. To see if a value was provided, `defined(FooBar)` can be used. @@ -7125,9 +7119,8 @@ User-defined pragmas pragma pragma ------------- -The ``pragma`` pragma can be used to declare user defined pragmas. This is -useful because Nim's templates and macros do not affect pragmas. User -defined pragmas are in a different module-wide scope than all other symbols. +The ``pragma`` pragma can be used to declare user-defined pragmas. This is +useful because Nim's templates and macros do not affect pragmas. User-defined pragmas are in a different module-wide scope than all other symbols. They cannot be imported from a module. Example: @@ -7141,14 +7134,14 @@ Example: proc p*(a, b: int): int {.rtl.} = result = a+b -In the example a new pragma named ``rtl`` is introduced that either imports +In the example, a new pragma named ``rtl`` is introduced that either imports a symbol from a dynamic library or exports the symbol for dynamic library generation. Custom annotations ------------------ -It is possible to define custom typed pragmas. Custom pragmas do not effect +It is possible to define custom typed pragmas. Custom pragmas do not affect code generation directly, but their presence can be detected by macros. Custom pragmas are defined using templates annotated with pragma ``pragma``: @@ -7178,7 +7171,7 @@ Consider stylized example of possible Object Relation Mapping (ORM) implementati write_access: bool admin_acess: bool -In this example custom pragmas are used to describe how Nim objects are +In this example, custom pragmas are used to describe how Nim objects are mapped to the schema of the relational database. Custom pragmas can have zero or more arguments. In order to pass multiple arguments use one of template call syntaxes. All arguments are typed and follow standard @@ -7216,7 +7209,7 @@ Macro pragmas ------------- All macros and templates can also be used as pragmas. They can be attached -to routines (procs, iterators, etc), type names or type expressions. The +to routines (procs, iterators, etc), type names, or type expressions. The compiler will perform the following simple syntactic transformations: .. code-block:: nim @@ -7299,7 +7292,7 @@ is not set to C, other pragmas are available: .. code-block:: Nim proc p(s: cstring) {.importc: "prefix$1".} -In the example the external name of ``p`` is set to ``prefixp``. Only ``$1`` +In the example, the external name of ``p`` is set to ``prefixp``. Only ``$1`` is available and a literal dollar sign must be written as ``$$``. @@ -7339,7 +7332,7 @@ mangling. The string literal passed to ``extern`` can be a format string: proc p(s: string) {.extern: "prefix$1".} = echo s -In the example the external name of ``p`` is set to ``prefixp``. Only ``$1`` +In the example, the external name of ``p`` is set to ``prefixp``. Only ``$1`` is available and a literal dollar sign must be written as ``$$``. @@ -7430,7 +7423,7 @@ At runtime the dynamic library is searched for (in this order):: libtcl8.3.so.1 libtcl8.3.so.0 -The ``dynlib`` pragma supports not only constant strings as argument but also +The ``dynlib`` pragma supports not only constant strings as an argument but also string expressions in general: .. code-block:: nim @@ -7452,7 +7445,7 @@ strings, because they are precompiled. because of order of initialization problems. **Note**: A ``dynlib`` import can be overridden with -the ``--dynlibOverride:name`` command line option. The Compiler User Guide +the ``--dynlibOverride:name`` command-line option. The Compiler User Guide contains further information. @@ -7467,23 +7460,23 @@ conjunction with the ``exportc`` pragma: proc exportme(): int {.cdecl, exportc, dynlib.} This is only useful if the program is compiled as a dynamic library via the -``--app:lib`` command line option. +``--app:lib`` command-line option. Threads ======= -To enable thread support the ``--threads:on`` command line switch needs to +To enable thread support the ``--threads:on`` command-line switch needs to be used. The ``system`` module then contains several threading primitives. See the `threads `_ and `channels `_ modules -for the low level thread API. There are also high level parallelism constructs +for the low-level thread API. There are also high-level parallelism constructs available. See `spawn `_ for further details. Nim's memory model for threads is quite different than that of other common programming languages (C, Pascal, Java): Each thread has its own (garbage -collected) heap and sharing of memory is restricted to global variables. This +collected) heap, and sharing of memory is restricted to global variables. This helps to prevent race conditions. GC efficiency is improved quite a lot, because the GC never has to stop other threads and see what they reference. @@ -7495,7 +7488,7 @@ A proc that is executed as a new thread of execution should be marked by the ``thread`` pragma for reasons of readability. The compiler checks for violations of the `no heap sharing restriction`:idx:\: This restriction implies that it is invalid to construct a data structure that consists of memory -allocated from different (thread local) heaps. +allocated from different (thread-local) heaps. A thread proc is passed to ``createThread`` or ``spawn`` and invoked indirectly; so the ``thread`` pragma implies ``procvar``. @@ -7516,7 +7509,7 @@ any of its parameters contain a ``ref`` or ``closure`` type. This enforces the *no heap sharing restriction*. Routines that are imported from C are always assumed to be ``gcsafe``. -To disable the GC-safety checking the ``--threadAnalysis:off`` command line +To disable the GC-safety checking the ``--threadAnalysis:off`` command-line switch can be used. This is a temporary workaround to ease the porting effort from old code to the new threading model. @@ -7549,8 +7542,8 @@ of the ``global`` pragma. .. code-block:: nim var checkpoints* {.threadvar.}: seq[string] -Due to implementation restrictions thread local variables cannot be -initialized within the ``var`` section. (Every thread local variable needs to +Due to implementation restrictions thread-local variables cannot be +initialized within the ``var`` section. (Every thread-local variable needs to be replicated at thread creation.) diff --git a/doc/manual/var_t_return.rst b/doc/manual/var_t_return.rst index 2235d5aa4..c6de8cf7c 100644 --- a/doc/manual/var_t_return.rst +++ b/doc/manual/var_t_return.rst @@ -17,4 +17,4 @@ then it has to be derived from the routine's first parameter: In other words, the lifetime of what ``result`` points to is attached to the lifetime of the first parameter and that is enough knowledge to verify -memory safety at the callsite. +memory safety at the call site. diff --git a/doc/nimc.rst b/doc/nimc.rst index 9a698312b..57255a31f 100644 --- a/doc/nimc.rst +++ b/doc/nimc.rst @@ -26,9 +26,9 @@ Nim is free software; it is licensed under the Compiler Usage ============== -Command line switches +Command-line switches --------------------- -Basic command line switches are: +Basic command-line switches are: Usage: @@ -36,7 +36,7 @@ Usage: ---- -Advanced command line switches are: +Advanced command-line switches are: .. include:: advopt.txt @@ -62,7 +62,7 @@ SmallLshouldNotBeUsed The letter 'l' should not be used as an identifier. EachIdentIsTuple The code contains a confusing ``var`` declaration. -User Some user defined warning. +User Some user-defined warning. ========================== ============================================ @@ -118,7 +118,7 @@ Level Description `_. This is the default level. 2 Displays compilation statistics, enumerates the dynamic - libraries that will be loaded by the final binary and dumps to + libraries that will be loaded by the final binary, and dumps to standard output the result of applying `a filter to the source code `_ if any filter was used during compilation. 3 In addition to the previous levels dumps a debug stack trace @@ -126,10 +126,10 @@ Level Description ===== ============================================ -Compile time symbols +Compile-time symbols -------------------- -Through the ``-d:x`` or ``--define:x`` switch you can define compile time +Through the ``-d:x`` or ``--define:x`` switch you can define compile-time symbols for conditional compilation. The defined switches can be checked in source code with the `when statement `_ and @@ -139,14 +139,14 @@ enabled for better performance. Another common use is the ``-d:ssl`` switch to activate SSL sockets. Additionally, you may pass a value along with the symbol: ``-d:x=y`` -which may be used in conjunction with the `compile time define -pragmas`_ +which may be used in conjunction with the `compile-time define +pragmas`_ to override symbols during build time. -Compile time symbols are completely **case insensitive** and underscores are +Compile-time symbols are completely **case insensitive** and underscores are ignored too. ``--define:FOO`` and ``--define:foo`` are identical. -Compile time symbols starting with the ``nim`` prefix are reserved for the +Compile-time symbols starting with the ``nim`` prefix are reserved for the implementation and should not be used elsewhere. @@ -154,7 +154,7 @@ Configuration files ------------------- **Note:** The *project file name* is the name of the ``.nim`` file that is -passed as a command line argument to the compiler. +passed as a command-line argument to the compiler. The ``nim`` executable processes configuration files in the following @@ -162,12 +162,12 @@ directories (in this order; later files overwrite previous settings): 1) ``$nim/config/nim.cfg``, ``/etc/nim/nim.cfg`` (UNIX) or ``\config\nim.cfg`` (Windows). This file can be skipped with the ``--skipCfg`` command line option. 2) If environment variable ``XDG_CONFIG_HOME`` is defined, ``$XDG_CONFIG_HOME/nim/nim.cfg`` or ``~/.config/nim/nim.cfg`` (POSIX) or ``%APPDATA%/nim/nim.cfg`` (Windows). This file can be skipped with the ``--skipUserCfg`` command line option. -3) ``$parentDir/nim.cfg`` where ``$parentDir`` stands for any parent directory of the project file's path. These files can be skipped with the ``--skipParentCfg`` command line option. -4) ``$projectDir/nim.cfg`` where ``$projectDir`` stands for the project file's path. This file can be skipped with the ``--skipProjCfg`` command line option. -5) A project can also have a project specific configuration file named ``$project.nim.cfg`` that resides in the same directory as ``$project.nim``. This file can be skipped with the ``--skipProjCfg`` command line option. +3) ``$parentDir/nim.cfg`` where ``$parentDir`` stands for any parent directory of the project file's path. These files can be skipped with the ``--skipParentCfg`` command-line option. +4) ``$projectDir/nim.cfg`` where ``$projectDir`` stands for the project file's path. This file can be skipped with the ``--skipProjCfg`` command-line option. +5) A project can also have a project-specific configuration file named ``$project.nim.cfg`` that resides in the same directory as ``$project.nim``. This file can be skipped with the ``--skipProjCfg`` command-line option. -Command line settings have priority over configuration file settings. +Command-line settings have priority over configuration file settings. The default build of a project is a `debug build`:idx:. To compile a `release build`:idx: define the ``release`` symbol:: @@ -217,12 +217,12 @@ The ``_r`` suffix is used for release builds, ``_d`` is for debug builds. This makes it easy to delete all generated files. The ``--nimcache`` -`compiler switch <#compiler-usage-command-line-switches>`_ can be used to +`compiler switch <#compiler-usage-commandminusline-switches>`_ can be used to to change the ``nimcache`` directory. -However, the generated C code is not platform independent. C code generated for +However, the generated C code is not platform-independent. C code generated for Linux does not compile on Windows, for instance. The comment on top of the -C file lists the OS, CPU and CC the file has been compiled for. +C file lists the OS, CPU, and CC the file has been compiled for. Compiler Selection @@ -245,7 +245,7 @@ To use the ``CC`` environment variable, use ``nim c --cc:env myfile.nim``. To us since Nim version 1.4. -Cross compilation +Cross-compilation ================= To cross compile, use for example:: @@ -268,10 +268,10 @@ configuration file should contain something like:: arm.linux.gcc.exe = "arm-linux-gcc" arm.linux.gcc.linkerexe = "arm-linux-gcc" -Cross compilation for Windows +Cross-compilation for Windows ============================= -To cross compile for Windows from Linux or macOS using the MinGW-w64 toolchain:: +To cross-compile for Windows from Linux or macOS using the MinGW-w64 toolchain:: nim c -d:mingw myproject.nim @@ -284,15 +284,15 @@ The MinGW-w64 toolchain can be installed as follows:: OSX: brew install mingw-w64 -Cross compilation for Android +Cross-compilation for Android ============================= There are two ways to compile for Android: terminal programs (Termux) and with the NDK (Android Native Development Kit). -First one is to treat Android as a simple Linux and use +The first one is to treat Android as a simple Linux and use `Termux `_ to connect and run the Nim compiler -directly on android as if it was Linux. These programs are console only +directly on android as if it was Linux. These programs are console-only programs that can't be distributed in the Play Store. Use regular ``nim c`` inside termux to make Android terminal programs. @@ -300,8 +300,8 @@ Use regular ``nim c`` inside termux to make Android terminal programs. Normal Android apps are written in Java, to use Nim inside an Android app you need a small Java stub that calls out to a native library written in Nim using the `NDK `_. You can also use -`native-acitivty `_ -to have the Java stub be auto generated for you. +`native-activity `_ +to have the Java stub be auto-generated for you. Use ``nim c -c --cpu:arm --os:android -d:androidNDK --noMain:on`` to generate the C source files you need to include in your Android Studio @@ -323,10 +323,10 @@ of your program. NimMain() # initialize garbage collector memory, types and stack -Cross compilation for iOS +Cross-compilation for iOS ========================= -To cross compile for iOS you need to be on a MacOS computer and use XCode. +To cross-compile for iOS you need to be on a macOS computer and use XCode. Normal languages for iOS development are Swift and Objective C. Both of these use LLVM and can be compiled into object files linked together with C, C++ or Objective C code produced by Nim. @@ -338,8 +338,8 @@ sign everything. Because Nim is part of a library it can't have its own c style ``main()`` so you would need to define `main` that calls ``autoreleasepool`` and ``UIApplicationMain`` to do it, or use a library like SDL2 or GLFM. After -the iOS setup is done, it's very important to call ``NimMain()`` in order to -initialize Nim's garbage collector and to run the top level statements +the iOS setup is done, it's very important to call ``NimMain()`` to +initialize Nim's garbage collector and to run the top-level statements of your program. .. code-block:: Nim @@ -352,7 +352,7 @@ Note: XCode's "make clean" gets confused about the generated nim.c files, so you need to clean those files manually to do a clean build. -Cross compilation for Nintendo Switch +Cross-compilation for Nintendo Switch ===================================== Simply add --os:nintendoswitch @@ -374,7 +374,7 @@ The DevkitPro setup must be the same as the default with their new installer `here for Mac/Linux `_ or `here for Windows `_. -For example, with the above mentioned config:: +For example, with the above-mentioned config:: nim c --os:nintendoswitch switchhomebrew.nim @@ -479,8 +479,7 @@ debugging with GDB. StackTrace option ----------------- If the ``stackTrace`` option is turned on, the generated C contains code to -ensure that proper stack traces are given if the program crashes or an -uncaught exception is raised. +ensure that proper stack traces are given if the program crashes or some uncaught exception is raised. LineTrace option @@ -509,8 +508,8 @@ Backend language options The typical compiler usage involves using the ``compile`` or ``c`` command to transform a ``.nim`` file into one or more ``.c`` files which are then -compiled with the platform's C compiler into a static binary. However there -are other commands to compile to C++, Objective-C or JavaScript. More details +compiled with the platform's C compiler into a static binary. However, there +are other commands to compile to C++, Objective-C, or JavaScript. More details can be read in the `Nim Backend Integration document `_. @@ -584,7 +583,7 @@ The ``--opt:size`` flag instructs Nim to optimize code generation for small size (with the help of the C compiler), the ``flto`` flags enable link-time optimization in the compiler and linker. -Check the `Cross compilation` section for instructions how to compile the +Check the `Cross-compilation` section for instructions on how to compile the program for your target. Nim for realtime systems @@ -630,7 +629,7 @@ Optimizing string handling String assignments are sometimes expensive in Nim: They are required to copy the whole string. However, the compiler is often smart enough to not copy strings. Due to the argument passing semantics, strings are never copied when -passed to subroutines. The compiler does not copy strings that are a result from +passed to subroutines. The compiler does not copy strings that are a result of a procedure call, because the callee returns a new string anyway. Thus it is efficient to do: @@ -638,7 +637,7 @@ Thus it is efficient to do: var s = procA() # assignment will not copy the string; procA allocates a new # string already -However it is not efficient to do: +However, it is not efficient to do: .. code-block:: Nim var s = varA # assignment has to copy the whole string into a new buffer! @@ -649,12 +648,12 @@ For ``let`` symbols a copy is not always necessary: let s = varA # may only copy a pointer if it safe to do so -If you know what you're doing, you can also mark single string (or sequence) +If you know what you're doing, you can also mark single-string (or sequence) objects as `shallow`:idx:\: .. code-block:: Nim var s = "abc" - shallow(s) # mark 's' as shallow string + shallow(s) # mark 's' as a shallow string var x = s # now might not copy the string! Usage of ``shallow`` is always safe once you know the string won't be modified diff --git a/doc/niminst.rst b/doc/niminst.rst index bf5cb0f50..adb5e6f1f 100644 --- a/doc/niminst.rst +++ b/doc/niminst.rst @@ -59,7 +59,7 @@ Key description ``Description`` the project's description ``App`` the application's type: "Console" or "GUI". If "Console", niminst generates a special batch file - for Windows to open up the command line shell. + for Windows to open up the command-line shell. ``License`` the filename of the application's license ==================== ======================================================= @@ -114,7 +114,7 @@ Listed files will be installed into the OS's native library directory Windows section --------------- -The ``windows`` section supports the ``files`` key for Windows specific files. +The ``windows`` section supports the ``files`` key for Windows-specific files. Listed files will be installed into the application installation directory (``$appdir``). @@ -149,7 +149,7 @@ Key description ==================== ======================================================= ``InstallScript`` boolean flag whether an installation shell script should be generated. Example: ``InstallScript: "Yes"`` -``UninstallScript`` boolean flag whether a deinstallation shell script +``UninstallScript`` boolean flag whether a de-installation shell script should be generated. Example: ``UninstallScript: "Yes"`` ==================== ======================================================= @@ -184,7 +184,7 @@ Key description ==================== ======================================================= -Real world example +Real-world example ================== The installers for the Nim compiler itself are generated by niminst. Have a diff --git a/doc/testament.rst b/doc/testament.rst index 1b2b71c18..e2612711b 100644 --- a/doc/testament.rst +++ b/doc/testament.rst @@ -2,7 +2,7 @@ Testament is an advanced automatic unittests runner for Nim tests, is used for t offers process isolation for your tests, it can generate statistics about test cases, supports multiple targets (C, C++, ObjectiveC, JavaScript, etc), simulated `Dry-Runs `_, -has logging, can generate HTML reports, skip tests from a file and more, +has logging, can generate HTML reports, skip tests from a file, and more, so can be useful to run your tests, even the most complex ones. @@ -115,9 +115,9 @@ Example "template" **to edit** and write a Testament unittest: * As you can see the "Spec" is just a ``discard """ """``. -* Spec has sane defaults, so you dont need to provide them all, any simple assert will work Ok. +* Spec has sane defaults, so you don't need to provide them all, any simple assert will work just fine. * `This is not the full spec of Testament, check the Testament Spec on GitHub, see parseSpec(). `_ -* `Nim itself uses Testament, so theres plenty of test examples. `_ +* `Nim itself uses Testament, so there are plenty of test examples. `_ * Has some built-in CI compatibility, like Azure Pipelines, etc. * `Testament supports inlined error messages on Unittests, basically comments with the expected error directly on the code. `_ @@ -172,7 +172,7 @@ JavaScript tests: import jsconsole console.log("My Frontend Project") -Compile time tests: +Compile-time tests: .. code-block:: nim diff --git a/doc/tools.rst b/doc/tools.rst index df4d2e99f..f231cdc7d 100644 --- a/doc/tools.rst +++ b/doc/tools.rst @@ -14,7 +14,7 @@ The standard distribution ships with the following tools: - | `Nimsuggest for IDE support `_ | Through the ``nimsuggest`` tool, any IDE can query a ``.nim`` source file - and obtain useful information like definition of symbols or suggestions for + and obtain useful information like the definition of symbols or suggestions for completion. - | `C2nim `_ @@ -35,5 +35,5 @@ The standard distribution ships with the following tools: is used for the development of Nim itself, offers process isolation for your tests, it can generate statistics about test cases, supports multiple targets (C, JS, etc), `simulated Dry-Runs `_, - has logging, can generate HTML reports, skip tests from a file and more, + has logging, can generate HTML reports, skip tests from a file, and more, so can be useful to run your tests, even the most complex ones. diff --git a/doc/tut1.rst b/doc/tut1.rst index 68e74df52..415a44f5f 100644 --- a/doc/tut1.rst +++ b/doc/tut1.rst @@ -18,7 +18,7 @@ Introduction This document is a tutorial for the programming language *Nim*. This tutorial assumes that you are familiar with basic programming concepts -like variables, types or statements but is kept very basic. The `manual +like variables, types, or statements but is kept very basic. The `manual `_ contains many more examples of the advanced language features. All code examples in this tutorial, as well as the ones found in the rest of Nim's documentation, follow the `Nim style guide `_. @@ -41,9 +41,9 @@ Save this code to the file "greetings.nim". Now compile and run it:: nim compile --run greetings.nim -With the ``--run`` `switch `_ Nim +With the ``--run`` `switch `_ Nim executes the file automatically after compilation. You can give your program -command line arguments by appending them after the filename:: +command-line arguments by appending them after the filename:: nim compile --run greetings.nim arg1 arg2 @@ -55,17 +55,17 @@ To compile a release version use:: nim c -d:release greetings.nim -By default the Nim compiler generates a large amount of runtime checks +By default, the Nim compiler generates a large number of runtime checks aiming for your debugging pleasure. With ``-d:release`` some checks are `turned off and optimizations are turned on -`_. +`_. Though it should be pretty obvious what the program does, I will explain the syntax: statements which are not indented are executed when the program starts. Indentation is Nim's way of grouping statements. Indentation is done with spaces only, tabulators are not allowed. -String literals are enclosed in double quotes. The ``var`` statement declares +String literals are enclosed in double-quotes. The ``var`` statement declares a new variable named ``name`` of type ``string`` with the value that is returned by the `readLine `_ procedure. Since the compiler knows that `readLine `_ returns a string, @@ -96,16 +96,16 @@ keywords, comments, operators, and other punctuation marks. String and character literals ----------------------------- -String literals are enclosed in double quotes; character literals in single +String literals are enclosed in double-quotes; character literals in single quotes. Special characters are escaped with ``\``: ``\n`` means newline, ``\t`` means tabulator, etc. There are also *raw* string literals: .. code-block:: Nim r"C:\program files\nim" -In raw literals the backslash is not an escape character. +In raw literals, the backslash is not an escape character. -The third and last way to write string literals are *long string literals*. +The third and last way to write string literals is *long-string literals*. They are written with three quotes: ``""" ... """``; they can span over multiple lines and the ``\`` is not an escape character either. They are very useful for embedding HTML code templates for example. @@ -148,7 +148,7 @@ Numbers Numerical literals are written as in most other languages. As a special twist, underscores are allowed for better readability: ``1_000_000`` (one million). -A number that contains a dot (or 'e' or 'E') is a floating point literal: +A number that contains a dot (or 'e' or 'E') is a floating-point literal: ``1.0e9`` (one billion). Hexadecimal literals are prefixed with ``0x``, binary literals with ``0b`` and octal literals with ``0o``. A leading zero alone does not produce an octal. @@ -195,11 +195,11 @@ statement and all the variables will have the same value: echo "x ", x # outputs "x 42" echo "y ", y # outputs "y 3" -Note that declaring multiple variables with a single assignment which calls a +Note that declaring multiple variables with a single assignment that calls a procedure can have unexpected results: the compiler will *unroll* the assignments and end up calling the procedure several times. If the result of the procedure depends on side effects, your variables may end up having -different values! For safety use side-effect free procedures if making multiple +different values! For safety use side-effect-free procedures if making multiple assignments. @@ -296,10 +296,10 @@ a multi-branch: else: echo "Hi, ", name, "!" -As it can be seen, for an ``of`` branch a comma separated list of values is also +As it can be seen, for an ``of`` branch a comma-separated list of values is also allowed. -The case statement can deal with integers, other ordinal types and strings. +The case statement can deal with integers, other ordinal types, and strings. (What an ordinal type is will be explained soon.) For integers or other ordinal types value ranges are also possible: @@ -332,7 +332,7 @@ cannot fail and thus the error disappears. Note that it is impossible to cover all possible string values: that is why string cases always need an ``else`` branch. -In general the case statement is used for subrange types or enumerations where +In general, the case statement is used for subrange types or enumerations where it is of great help that the compiler checks that you covered any possible value. @@ -399,7 +399,7 @@ Since counting up occurs so often in programs, Nim also has a `.. ... Zero-indexed counting has two shortcuts ``..<`` and ``.. ^1`` -(`backwards index operator `_) to simplify +(`backward index operator `_) to simplify counting to one less than the higher index: .. code-block:: nim @@ -520,7 +520,7 @@ differences: * The compiler checks the semantics and produces code *only* for the statements that belong to the first condition that evaluates to ``true``. -The ``when`` statement is useful for writing platform specific code, similar to +The ``when`` statement is useful for writing platform-specific code, similar to the ``#ifdef`` construct in the C programming language. @@ -530,15 +530,15 @@ Statements and indentation Now that we covered the basic control flow statements, let's return to Nim indentation rules. -In Nim there is a distinction between *simple statements* and *complex +In Nim, there is a distinction between *simple statements* and *complex statements*. *Simple statements* cannot contain other statements: -Assignment, procedure calls or the ``return`` statement belong to the simple +Assignment, procedure calls, or the ``return`` statement are all simple statements. *Complex statements* like ``if``, ``when``, ``for``, ``while`` can contain other statements. To avoid ambiguities, complex statements must always be indented, but single simple statements do not: .. code-block:: nim - # no indentation needed for single assignment statement: + # no indentation needed for single-assignment statement: if x: x = false # indentation needed for nested if statement: @@ -554,8 +554,8 @@ be indented, but single simple statements do not: y = false -*Expressions* are parts of a statement which usually result in a value. The -condition in an if statement is an example for an expression. Expressions can +*Expressions* are parts of a statement that usually result in a value. The +condition in an if statement is an example of an expression. Expressions can contain indentation at certain places for better readability: .. code-block:: nim @@ -617,7 +617,7 @@ Some terminology: in the example ``question`` is called a (formal) *parameter*, Result variable --------------- A procedure that returns a value has an implicit ``result`` variable declared -that represents the return value. A ``return`` statement with no expression is a +that represents the return value. A ``return`` statement with no expression is shorthand for ``return result``. The ``result`` value is always returned automatically at the end of a procedure if there is no ``return`` statement at the exit. @@ -637,9 +637,9 @@ the exit. The ``result`` variable is already implicitly declared at the start of the function, so declaring it again with 'var result', for example, would shadow it with a normal variable of the same name. The result variable is also already -initialised with the type's default value. Note that referential data types will +initialized with the type's default value. Note that referential data types will be ``nil`` at the start of the procedure, and thus may require manual -initialisation. +initialization. A procedure that does not have any ``return`` statement and does not use the special ``result`` variable returns the value of its last expression. For example, @@ -798,7 +798,7 @@ Apart from a few built-in keyword operators such as ``and``, ``or``, ``not``, operators always consist of these characters: ``+ - * \ / < > = @ $ ~ & % ! ? ^ . |`` -User defined operators are allowed. Nothing stops you from defining your own +User-defined operators are allowed. Nothing stops you from defining your own ``@!?+~`` operator, but doing so may reduce readability. The operator's precedence is determined by its first character. The details @@ -824,7 +824,7 @@ Forward declarations Every variable, procedure, etc. needs to be declared before it can be used. (The reason for this is that it is non-trivial to avoid this need in a -language that supports meta programming as extensively as Nim does.) +language that supports metaprogramming as extensively as Nim does.) However, this cannot be done for mutually recursive procedures: .. code-block:: nim @@ -900,13 +900,12 @@ important differences: ``yield`` statement). * Iterators have no implicit ``result`` variable. * Iterators do not support recursion. -* Iterators cannot be forward declared, because the compiler must be able - to inline an iterator. (This restriction will be gone in a +* Iterators cannot be forward declared, because the compiler must be able to inline an iterator. (This restriction will be gone in a future version of the compiler.) However, you can also use a ``closure`` iterator to get a different set of -restrictions. See `first class iterators `_ -for details. Iterators can have the same name and parameters as a proc, since +restrictions. See `first-class iterators `_ +for details. Iterators can have the same name and parameters as a proc since essentially they have their own namespaces. Therefore it is common practice to wrap iterators in procs of the same name which accumulate the result of the iterator and return it as a sequence, like ``split`` from the `strutils module @@ -940,10 +939,10 @@ evaluation. For example: Characters ---------- The `character type` is called ``char``. Its size is always one byte, so -it cannot represent most UTF-8 characters; but it *can* represent one of the bytes +it cannot represent most UTF-8 characters, but it *can* represent one of the bytes that makes up a multi-byte UTF-8 character. The reason for this is efficiency: for the overwhelming majority of use-cases, -the resulting programs will still handle UTF-8 properly as UTF-8 was specially +the resulting programs will still handle UTF-8 properly as UTF-8 was especially designed for this. Character literals are enclosed in single quotes. @@ -995,7 +994,7 @@ Most often integers are used for counting objects that reside in memory, so ``int`` has the same size as a pointer. The common operators ``+ - * div mod < <= == != > >=`` are defined for -integers. The ``and or xor not`` operators are also defined for integers, and +integers. The ``and or xor not`` operators are also defined for integers and provide *bitwise* operations. Left bit shifting is done with the ``shl``, right shifting with the ``shr`` operator. Bit shifting operators always treat their arguments as *unsigned*. For `arithmetic bit shifts`:idx: ordinary @@ -1012,7 +1011,7 @@ cannot be detected at compile time). Floats ------ -Nim has these floating point types built-in: ``float float32 float64``. +Nim has these floating-point types built-in: ``float float32 float64``. The default float type is ``float``. In the current implementation, ``float`` is always 64-bits. @@ -1030,9 +1029,8 @@ type: The common operators ``+ - * / < <= == != > >=`` are defined for floats and follow the IEEE-754 standard. -Automatic type conversion in expressions with different kinds of floating -point types is performed: the smaller type is converted to the larger. Integer -types are **not** converted to floating point types automatically, nor vice +Automatic type conversion in expressions with different kinds of floating-point types is performed: the smaller type is converted to the larger. Integer +types are **not** converted to floating-point types automatically, nor vice versa. Use the `toInt `_ and `toFloat `_ procs for these conversions. @@ -1104,7 +1102,7 @@ Enumerations A variable of an enumeration type can only be assigned one of the enumeration's specified values. These values are a set of ordered symbols. Each symbol is mapped to an integer value internally. The first symbol is represented -at runtime by 0, the second by 1 and so on. For example: +at runtime by 0, the second by 1, and so on. For example: .. code-block:: nim :test: "nim c $1" @@ -1135,6 +1133,7 @@ Enumerations, integer types, ``char`` and ``bool`` (and subranges) are called ordinal types. Ordinal types have quite a few special operations: + ----------------- -------------------------------------------------------- Operation Comment ----------------- -------------------------------------------------------- @@ -1150,6 +1149,7 @@ Operation Comment ``pred(x, n)`` returns the `n`'th predecessor of `x` ----------------- -------------------------------------------------------- + The `inc `_, `dec `_, `succ `_ and `pred `_ operations can fail by raising an `EOutOfRange` or `EOverflow` exception. (If the code has been @@ -1187,7 +1187,7 @@ Sets Arrays ------ -An array is a simple fixed length container. Each element in +An array is a simple fixed-length container. Each element in an array has the same type. The array's index type can be any ordinal type. Arrays can be constructed using ``[]``: @@ -1240,7 +1240,7 @@ same index type as the others. In Nim you can have different dimensions with different index types, so the nesting syntax is slightly different. Building on the previous example where a level is defined as an array of enums indexed by yet another enum, we can add the following lines to add a light tower type -subdivided in height levels accessed through their integer index: +subdivided into height levels accessed through their integer index: .. code-block:: nim type @@ -1314,7 +1314,7 @@ The ``for`` statement can be used with one or two variables when used with a sequence. When you use the one variable form, the variable will hold the value provided by the sequence. The ``for`` statement is looping over the results from the `items() `_ iterator from the `system -`_ module. But if you use the two variable form, the first +`_ module. But if you use the two-variable form, the first variable will hold the index position and the second variable will hold the value. Here the ``for`` statement is looping over the results from the `pairs() `_ iterator from the `system @@ -1339,7 +1339,7 @@ Open arrays ----------- **Note**: Openarrays can only be used for parameters. -Often fixed size arrays turn out to be too inflexible; procedures should be +Often fixed-size arrays turn out to be too inflexible; procedures should be able to deal with arrays of different sizes. The `openarray`:idx: type allows this. Openarrays are always indexed with an ``int`` starting at position 0. The `len `_, `low `_ @@ -1591,10 +1591,10 @@ having the same field types. Tuples can be *unpacked* during variable assignment (and only then!). This can be handy to assign directly the fields of the tuples to individually named variables. An example of this is the `splitFile `_ -proc from the `os module `_ which returns the directory, name and +proc from the `os module `_ which returns the directory, name, and extension of a path at the same time. For tuple unpacking to work you must use parentheses around the values you want to assign the unpacking to, -otherwise you will be assigning the same value to all the individual +otherwise, you will be assigning the same value to all the individual variables! For example: .. code-block:: nim @@ -1626,15 +1626,15 @@ point to and modify the same location in memory. Nim distinguishes between `traced`:idx: and `untraced`:idx: references. Untraced references are also called *pointers*. Traced references point to -objects in a garbage collected heap, untraced references point to -manually allocated objects or to objects elsewhere in memory. Thus -untraced references are *unsafe*. However for certain low-level operations +objects in a garbage-collected heap, untraced references point to +manually allocated objects or objects elsewhere in memory. Thus +untraced references are *unsafe*. However, for certain low-level operations (e.g., accessing the hardware), untraced references are necessary. Traced references are declared with the **ref** keyword; untraced references are declared with the **ptr** keyword. -The empty ``[]`` subscript notation can be used to *derefer* a reference, +The empty ``[]`` subscript notation can be used to *de-refer* a reference, meaning to retrieve the item the reference points to. The ``.`` (access a tuple/object field operator) and ``[]`` (array/string/sequence index operator) operators perform implicit dereferencing operations for reference types: @@ -1688,9 +1688,9 @@ listed in the `manual `_. Distinct type ------------- -A Distinct type allows for the creation of new type that "does not imply a +A Distinct type allows for the creation of a new type that "does not imply a subtype relationship between it and its base type". -You must **explicitly** define all behaviour for the distinct type. +You must **explicitly** define all behavior for the distinct type. To help with this, both the distinct type and its base type can cast from one type to the other. Examples are provided in the `manual `_. @@ -1775,7 +1775,7 @@ Excluding symbols ----------------- The normal ``import`` statement will bring in all exported symbols. -These can be limited by naming symbols which should be excluded with +These can be limited by naming symbols that should be excluded using the ``except`` qualifier. .. code-block:: nim @@ -1794,7 +1794,7 @@ exported symbols. An alternative that only imports listed symbols is the The ``from`` statement can also force namespace qualification on symbols, thereby making symbols available, but needing to be qualified -to be used. +in order to be used. .. code-block:: nim from mymodule import x, y, z diff --git a/doc/tut3.rst b/doc/tut3.rst index a39074db9..a159f0d82 100644 --- a/doc/tut3.rst +++ b/doc/tut3.rst @@ -14,7 +14,7 @@ Introduction "With Great Power Comes Great Responsibility." -- Spider Man's Uncle This document is a tutorial about Nim's macro system. -A macro is a function that is executed at compile time and transforms +A macro is a function that is executed at compile-time and transforms a Nim syntax tree into a different tree. Examples of things that can be implemented in macros: @@ -35,7 +35,7 @@ Macro Arguments --------------- The types of macro arguments have two faces. One face is used for -the overload resolution, and the other face is used within the macro +the overload resolution and the other face is used within the macro body. For example, if ``macro foo(arg: int)`` is called in an expression ``foo(x)``, ``x`` has to be of a type compatible to int, but *within* the macro's body ``arg`` has the type ``NimNode``, not ``int``! @@ -52,10 +52,10 @@ Untyped Arguments Untyped macro arguments are passed to the macro before they are semantically checked. This means the syntax tree that is passed down to the macro does not need to make sense for Nim yet, the only -limitation is that it needs to be parsable. Usually the macro does +limitation is that it needs to be parsable. Usually, the macro does not check the argument either but uses it in the transformation's result somehow. The result of a macro expansion is always checked -by the compiler, so apart from weird error messages nothing bad +by the compiler, so apart from weird error messages, nothing bad can happen. The downside for an ``untyped`` argument is that these do not play @@ -73,7 +73,7 @@ For typed arguments, the semantic checker runs on the argument and does transformations on it, before it is passed to the macro. Here identifier nodes are resolved as symbols, implicit type conversions are visible in the tree as calls, templates are -expanded and probably most importantly, nodes have type information. +expanded, and probably most importantly, nodes have type information. Typed arguments can have the type ``typed`` in the arguments list. But all other types, such as ``int``, ``float`` or ``MyObjectType`` are typed arguments as well, and they are passed to the macro as a @@ -103,7 +103,7 @@ Code Blocks as Arguments ------------------------ It is possible to pass the last argument of a call expression in a -separate code block with indentation. For example the following code +separate code block with indentation. For example, the following code example is a valid (but not a recommended) way to call ``echo``: .. code-block:: nim @@ -126,10 +126,10 @@ look like so that the Nim compiler will understand it. The nodes of the Nim syntax tree are documented in the `macros `_ module. But a more interactive way to explore the Nim syntax tree is with ``macros.treeRepr``, it converts a syntax tree -into a multi line string for printing on the console. It can be used +into a multi-line string for printing on the console. It can be used to explore how the argument expressions are represented in tree form and for debug printing of generated syntax tree. ``dumpTree`` is a -predefined macro that just prints its argument in tree representation, +predefined macro that just prints its argument in a tree representation, but does nothing else. Here is an example of such a tree representation: .. code-block:: nim @@ -176,7 +176,7 @@ Generating Code There are two ways to generate the code. Either by creating the syntax tree with expressions that contain a lot of calls to ``newTree`` and ``newLit``, or with ``quote do:`` expressions. The first option offers -the best low level control for the syntax tree generation, but the +the best low-level control for the syntax tree generation, but the second option is much less verbose. If you choose to create the syntax tree with calls to ``newTree`` and ``newLit`` the macro ``macros.dumpAstGen`` can help you with the verbosity. ``quote do:`` @@ -226,8 +226,8 @@ Building Your First Macro To give a starting point to writing macros we will show now how to implement the ``myDebug`` macro mentioned earlier. The first thing to do is to build a simple example of the macro usage, and then just -print the argument. This way it is possible to get an idea of a -correct argument should be look like. +print the argument. This way it is possible to get an idea of what a +correct argument should look like. .. code-block:: nim :test: "nim c $1" @@ -250,9 +250,9 @@ correct argument should be look like. Ident "b" -From the output it is possible to see that the argument is an infix +From the output, it is possible to see that the argument is an infix operator (node kind is "Infix"), as well as that the two operands are -at index 1 and 2. With this information the actual macro can be +at index 1 and 2. With this information, the actual macro can be written. .. code-block:: nim @@ -292,13 +292,13 @@ used to get this output. With Power Comes Responsibility ------------------------------- -Macros are very powerful. A good advice is to use them as little as +Macros are very powerful. A piece of good advice is to use them as little as possible, but as much as necessary. Macros can change the semantics of expressions, making the code incomprehensible for anybody who does not know exactly what the macro does with it. So whenever a macro is not necessary and the same logic can be implemented using templates or generics, it is probably better not to use a macro. And when a macro -is used for something, the macro should better have a well written +is used for something, the macro should better have a well-written documentation. For all the people who claim to write only perfectly self-explanatory code: when it comes to macros, the implementation is not enough for documentation. @@ -309,7 +309,7 @@ Limitations Since macros are evaluated in the compiler in the NimVM, macros share all the limitations of the NimVM. They have to be implemented in pure Nim code. Macros can start external processes on the shell, but they -cannot call C functions except from those that are built in the +cannot call C functions except those that are built in the compiler. @@ -348,7 +348,7 @@ OpenGL Sandbox -------------- This project has a working Nim to GLSL compiler written entirely in -macros. It scans recursively though all used function symbols to +macros. It scans recursively through all used function symbols to compile them so that cross library functions can be executed on the GPU. `OpenGL Sandbox `_ From 53d9902347dad9e22171f643916e830c1b20872c Mon Sep 17 00:00:00 2001 From: Antonis Geralis <43617260+planetis-m@users.noreply.github.com> Date: Thu, 29 Oct 2020 12:14:22 +0200 Subject: [PATCH 0858/3768] Make default state public (#15763) * Make default state public * Address review comments Co-authored-by: b3liever --- changelog.md | 2 +- lib/pure/random.nim | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 7f1034376..7c7640207 100644 --- a/changelog.md +++ b/changelog.md @@ -7,7 +7,7 @@ - `prelude` now works with the JavaScript target. - Added `ioutils` module containing `duplicate` and `duplicateTo` to duplicate `FileHandle` using C function `dup` and `dup2`. - +- Added `randState` template that exposes the default random number generator. Useful for library authors. ## Language changes diff --git a/lib/pure/random.nim b/lib/pure/random.nim index 7c5d51ceb..3d9948d08 100644 --- a/lib/pure/random.nim +++ b/lib/pure/random.nim @@ -119,6 +119,12 @@ else: a0: 0x69B4C98CB8530805u64, a1: 0xFED1DD3004688D67CAu64) # global for backwards compatibility +since (1, 5): + template randState*(): untyped = + ## Makes the default Rand state accessible from other modules. + ## Useful for module authors. + state + proc rotl(x, k: Ui): Ui = result = (x shl k) or (x shr (Ui(64) - k)) From 87a60c1b28a777e35701c2f934fc1361818a216e Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 29 Oct 2020 17:32:56 +0100 Subject: [PATCH 0859/3768] fixes #15413 (#15768) * fixes #15413 * better hide it properly * see if this makes our list of important packages happy --- changelog.md | 7 ++++ lib/pure/json.nim | 82 +++++++++++++++++++++++++++++++----------- tests/stdlib/tjson.nim | 3 ++ 3 files changed, 71 insertions(+), 21 deletions(-) diff --git a/changelog.md b/changelog.md index 7c7640207..552e1e8df 100644 --- a/changelog.md +++ b/changelog.md @@ -7,6 +7,13 @@ - `prelude` now works with the JavaScript target. - Added `ioutils` module containing `duplicate` and `duplicateTo` to duplicate `FileHandle` using C function `dup` and `dup2`. + +- The JSON module can now handle integer literals and floating point literals of arbitrary length and precision. + Numbers that do not fit the underlying `BiggestInt` or `BiggestFloat` fields are kept as string literals and + one can use external BigNum libraries to handle these. The `parseFloat` family of functions also has now optional + `rawIntegers` and `rawFloats` parameters that can be used to enforce that all integer or float literals remain + in the "raw" string form so that client code can easily treat small and large numbers uniformly. + - Added `randState` template that exposes the default random number generator. Useful for library authors. ## Language changes diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 3f2e369f6..9766e32cb 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -177,6 +177,8 @@ type JsonNode* = ref JsonNodeObj ## JSON node JsonNodeObj* {.acyclic.} = object + isUnquoted: bool # the JString was a number-like token and + # so shouldn't be quoted case kind*: JsonNodeKind of JString: str*: string @@ -197,6 +199,13 @@ proc newJString*(s: string): JsonNode = ## Creates a new `JString JsonNode`. result = JsonNode(kind: JString, str: s) +proc newJRawNumber(s: string): JsonNode = + ## Creates a "raw JS number", that is a number that does not + ## fit into Nim's ``BiggestInt`` field. This is really a `JString` + ## with the additional information that it should be converted back + ## to the string representation without the quotes. + result = JsonNode(kind: JString, str: s, isUnquoted: true) + proc newJStringMove(s: string): JsonNode = result = JsonNode(kind: JString) shallowCopy(result.str, s) @@ -562,6 +571,7 @@ proc copy*(p: JsonNode): JsonNode = case p.kind of JString: result = newJString(p.str) + result.isUnquoted = p.isUnquoted of JInt: result = newJInt(p.num) of JFloat: @@ -652,7 +662,10 @@ proc toPretty(result: var string, node: JsonNode, indent = 2, ml = true, result.add("{}") of JString: if lstArr: result.indent(currIndent) - escapeJson(node.str, result) + if node.isUnquoted: + result.add node.str + else: + escapeJson(node.str, result) of JInt: if lstArr: result.indent(currIndent) when defined(js): result.add($node.num) @@ -734,7 +747,10 @@ proc toUgly*(result: var string, node: JsonNode) = result.toUgly value result.add "}" of JString: - node.str.escapeJson(result) + if node.isUnquoted: + result.add node.str + else: + node.str.escapeJson(result) of JInt: when defined(js): result.add($node.num) else: result.addInt(node.num) @@ -783,7 +799,7 @@ iterator mpairs*(node: var JsonNode): tuple[key: string, val: var JsonNode] = for key, val in mpairs(node.fields): yield (key, val) -proc parseJson(p: var JsonParser): JsonNode = +proc parseJson(p: var JsonParser; rawIntegers, rawFloats: bool): JsonNode = ## Parses JSON from a JSON Parser `p`. case p.tok of tkString: @@ -792,10 +808,22 @@ proc parseJson(p: var JsonParser): JsonNode = p.a = "" discard getTok(p) of tkInt: - result = newJInt(parseBiggestInt(p.a)) + if rawIntegers: + result = newJRawNumber(p.a) + else: + try: + result = newJInt(parseBiggestInt(p.a)) + except ValueError: + result = newJRawNumber(p.a) discard getTok(p) of tkFloat: - result = newJFloat(parseFloat(p.a)) + if rawFloats: + result = newJRawNumber(p.a) + else: + try: + result = newJFloat(parseFloat(p.a)) + except ValueError: + result = newJRawNumber(p.a) discard getTok(p) of tkTrue: result = newJBool(true) @@ -815,7 +843,7 @@ proc parseJson(p: var JsonParser): JsonNode = var key = p.a discard getTok(p) eat(p, tkColon) - var val = parseJson(p) + var val = parseJson(p, rawIntegers, rawFloats) result[key] = val if p.tok != tkComma: break discard getTok(p) @@ -824,39 +852,47 @@ proc parseJson(p: var JsonParser): JsonNode = result = newJArray() discard getTok(p) while p.tok != tkBracketRi: - result.add(parseJson(p)) + result.add(parseJson(p, rawIntegers, rawFloats)) if p.tok != tkComma: break discard getTok(p) eat(p, tkBracketRi) of tkError, tkCurlyRi, tkBracketRi, tkColon, tkComma, tkEof: raiseParseErr(p, "{") -iterator parseJsonFragments*(s: Stream, filename: string = ""): JsonNode = +iterator parseJsonFragments*(s: Stream, filename: string = ""; rawIntegers = false, rawFloats = false): JsonNode = ## Parses from a stream `s` into `JsonNodes`. `filename` is only needed ## for nice error messages. ## The JSON fragments are separated by whitespace. This can be substantially ## faster than the comparable loop ## ``for x in splitWhitespace(s): yield parseJson(x)``. ## This closes the stream `s` after it's done. + ## If `rawIntegers` is true, integer literals will not be converted to a `JInt` + ## field but kept as raw numbers via `JString`. + ## If `rawFloats` is true, floating point literals will not be converted to a `JFloat` + ## field but kept as raw numbers via `JString`. var p: JsonParser p.open(s, filename) try: discard getTok(p) # read first token while p.tok != tkEof: - yield p.parseJson() + yield p.parseJson(rawIntegers, rawFloats) finally: p.close() -proc parseJson*(s: Stream, filename: string = ""): JsonNode = +proc parseJson*(s: Stream, filename: string = ""; rawIntegers = false, rawFloats = false): JsonNode = ## Parses from a stream `s` into a `JsonNode`. `filename` is only needed ## for nice error messages. ## If `s` contains extra data, it will raise `JsonParsingError`. ## This closes the stream `s` after it's done. + ## If `rawIntegers` is true, integer literals will not be converted to a `JInt` + ## field but kept as raw numbers via `JString`. + ## If `rawFloats` is true, floating point literals will not be converted to a `JFloat` + ## field but kept as raw numbers via `JString`. var p: JsonParser p.open(s, filename) try: discard getTok(p) # read first token - result = p.parseJson() + result = p.parseJson(rawIntegers, rawFloats) eat(p, tkEof) # check if there is no extra data finally: p.close() @@ -924,6 +960,7 @@ when defined(js): of JFloat: result = newJFloat(cast[float](x)) of JString: + # Dunno what to do with isUnquoted here result = newJString($cast[cstring](x)) of JBool: result = newJBool(cast[bool](x)) @@ -937,10 +974,14 @@ when defined(js): return parseNativeJson(buffer).convertObject() else: - proc parseJson*(buffer: string): JsonNode = + proc parseJson*(buffer: string; rawIntegers = false, rawFloats = false): JsonNode = ## Parses JSON from `buffer`. ## If `buffer` contains extra data, it will raise `JsonParsingError`. - result = parseJson(newStringStream(buffer), "input") + ## If `rawIntegers` is true, integer literals will not be converted to a `JInt` + ## field but kept as raw numbers via `JString`. + ## If `rawFloats` is true, floating point literals will not be converted to a `JFloat` + ## field but kept as raw numbers via `JString`. + result = parseJson(newStringStream(buffer), "input", rawIntegers, rawFloats) proc parseFile*(filename: string): JsonNode = ## Parses `file` into a `JsonNode`. @@ -948,7 +989,7 @@ else: var stream = newFileStream(filename, fmRead) if stream == nil: raise newException(IOError, "cannot read from file: " & filename) - result = parseJson(stream, filename) + result = parseJson(stream, filename, rawIntegers=false, rawFloats=false) # -- Json deserialiser. -- @@ -986,9 +1027,9 @@ when defined(nimFixedForwardGeneric): proc initFromJson[T: SomeFloat](dst: var T; jsonNode: JsonNode; jsonPath: var string) proc initFromJson[T: enum](dst: var T; jsonNode: JsonNode; jsonPath: var string) proc initFromJson[T](dst: var seq[T]; jsonNode: JsonNode; jsonPath: var string) - proc initFromJson[S,T](dst: var array[S,T]; jsonNode: JsonNode; jsonPath: var string) - proc initFromJson[T](dst: var Table[string,T]; jsonNode: JsonNode; jsonPath: var string) - proc initFromJson[T](dst: var OrderedTable[string,T]; jsonNode: JsonNode; jsonPath: var string) + proc initFromJson[S, T](dst: var array[S, T]; jsonNode: JsonNode; jsonPath: var string) + proc initFromJson[T](dst: var Table[string, T]; jsonNode: JsonNode; jsonPath: var string) + proc initFromJson[T](dst: var OrderedTable[string, T]; jsonNode: JsonNode; jsonPath: var string) proc initFromJson[T](dst: var ref T; jsonNode: JsonNode; jsonPath: var string) proc initFromJson[T](dst: var Option[T]; jsonNode: JsonNode; jsonPath: var string) proc initFromJson[T: distinct](dst: var T; jsonNode: JsonNode; jsonPath: var string) @@ -1082,7 +1123,7 @@ when defined(nimFixedForwardGeneric): dst = some(default(T)) initFromJson(dst.get, jsonNode, jsonPath) - macro assignDistinctImpl[T : distinct](dst: var T;jsonNode: JsonNode; jsonPath: var string) = + macro assignDistinctImpl[T: distinct](dst: var T;jsonNode: JsonNode; jsonPath: var string) = let typInst = getTypeInst(dst) let typImpl = getTypeImpl(dst) let baseTyp = typImpl[0] @@ -1096,7 +1137,7 @@ when defined(nimFixedForwardGeneric): else: initFromJson( `baseTyp`(`dst`), `jsonNode`, `jsonPath`) - proc initFromJson[T : distinct](dst: var T; jsonNode: JsonNode; jsonPath: var string) = + proc initFromJson[T: distinct](dst: var T; jsonNode: JsonNode; jsonPath: var string) = assignDistinctImpl(dst, jsonNode, jsonPath) proc detectIncompatibleType(typeExpr, lineinfoNode: NimNode): void = @@ -1186,7 +1227,6 @@ when defined(nimFixedForwardGeneric): else: error("unhandled kind: " & $typeNode.kind, typeNode) - macro assignObjectImpl[T](dst: var T; jsonNode: JsonNode; jsonPath: var string) = let typeSym = getTypeInst(dst) let originalJsonPathLen = genSym(nskLet, "originalJsonPathLen") @@ -1201,7 +1241,7 @@ when defined(nimFixedForwardGeneric): else: foldObjectBody(result, typeSym.getTypeImpl, dst, jsonNode, jsonPath, originalJsonPathLen) - proc initFromJson[T : object|tuple](dst: var T; jsonNode: JsonNode; jsonPath: var string) = + proc initFromJson[T: object|tuple](dst: var T; jsonNode: JsonNode; jsonPath: var string) = assignObjectImpl(dst, jsonNode, jsonPath) proc to*[T](node: JsonNode, t: typedesc[T]): T = diff --git a/tests/stdlib/tjson.nim b/tests/stdlib/tjson.nim index bc7ff02b2..78f284abb 100644 --- a/tests/stdlib/tjson.nim +++ b/tests/stdlib/tjson.nim @@ -232,3 +232,6 @@ doAssert isRefSkipDistinct(MyRef) doAssert not isRefSkipDistinct(MyObject) doAssert isRefSkipDistinct(MyDistinct) doAssert isRefSkipDistinct(MyOtherDistinct) + +let x = parseJson("9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999") +doAssert x.kind == JString From 912d8f80ad5782ee371376f9f5664af3ac21995e Mon Sep 17 00:00:00 2001 From: Antonis Geralis <43617260+planetis-m@users.noreply.github.com> Date: Thu, 29 Oct 2020 18:54:28 +0200 Subject: [PATCH 0860/3768] Fix doc comment for sumKbn (#15769) Co-authored-by: b3liever --- lib/std/sums.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/sums.nim b/lib/std/sums.nim index a48360180..0f8c5ebfb 100644 --- a/lib/std/sums.nim +++ b/lib/std/sums.nim @@ -10,7 +10,7 @@ func sumKbn*[T](x: openArray[T]): T = - ## Kahan (compensated) summation: O(1) error growth, at the expense + ## Kahan-Babuška-Neumaier summation: O(1) error growth, at the expense ## of a considerable increase in computational expense. if len(x) == 0: return var sum = x[0] From 33e258c72fe9c06410b30c5bc4364626c4f6ecb1 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Fri, 30 Oct 2020 01:12:14 +0800 Subject: [PATCH 0861/3768] add tests for #7686 (#15771) --- tests/stdlib/t7686.nim | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 tests/stdlib/t7686.nim diff --git a/tests/stdlib/t7686.nim b/tests/stdlib/t7686.nim new file mode 100644 index 000000000..c174dfb51 --- /dev/null +++ b/tests/stdlib/t7686.nim @@ -0,0 +1,9 @@ +import strutils + +type + MyEnum = enum + A, + a + +doAssert parseEnum[MyEnum]("A") == A +doAssert parseEnum[MyEnum]("a") == a From d8fb2379c9a6e83b7e8151ba13116afb1bedc00f Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Fri, 30 Oct 2020 10:54:39 +0800 Subject: [PATCH 0862/3768] add testcase for #7127 (#15780) --- tests/js/t7127.nim | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 tests/js/t7127.nim diff --git a/tests/js/t7127.nim b/tests/js/t7127.nim new file mode 100644 index 000000000..364aedd4a --- /dev/null +++ b/tests/js/t7127.nim @@ -0,0 +1,2 @@ +doAssertRaises(DivByZeroDefect): discard 1 mod 0 +doAssertRaises(DivByZeroDefect): discard 1 div 0 \ No newline at end of file From f79ded3cb6a1a8eb754081fca6b074bef455224e Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Fri, 30 Oct 2020 16:16:47 +0800 Subject: [PATCH 0863/3768] fix #15638 (#15789) --- compiler/jsgen.nim | 1 + tests/js/tdiscard.nim | 3 +++ 2 files changed, 4 insertions(+) create mode 100644 tests/js/tdiscard.nim diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 4d913aa87..8b91ada8a 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -2534,6 +2534,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = if n[0].kind != nkEmpty: genLineDir(p, n) gen(p, n[0], r) + r.res = "var _ = " & r.res of nkAsmStmt: genAsmOrEmitStmt(p, n) of nkTryStmt, nkHiddenTryStmt: genTry(p, n, r) of nkRaiseStmt: genRaiseStmt(p, n) diff --git a/tests/js/tdiscard.nim b/tests/js/tdiscard.nim new file mode 100644 index 000000000..9aa6ea1b1 --- /dev/null +++ b/tests/js/tdiscard.nim @@ -0,0 +1,3 @@ +import dom + +discard Node() \ No newline at end of file From b5df82319186595b2cc35678203a0992588dca8c Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Fri, 30 Oct 2020 16:55:21 +0800 Subject: [PATCH 0864/3768] add testcase for #9091 (#15791) * add testcase for #9091 * more tests --- tests/stdlib/t9091.nim | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 tests/stdlib/t9091.nim diff --git a/tests/stdlib/t9091.nim b/tests/stdlib/t9091.nim new file mode 100644 index 000000000..8419479a7 --- /dev/null +++ b/tests/stdlib/t9091.nim @@ -0,0 +1,36 @@ +discard """ + targets: "c" + output: "test AObj" + action: "compile" + exitcode: 0 + timeout: 60.0 +""" +import streams + +block: + type Mine = ref object + a: int + + proc write(io: Stream, t: Mine) = + io.write("sure") + + let str = newStringStream() + let mi = new Mine + + str.write(mi) + +block: + type + AObj = object + x: int + + proc foo(a: int): string = "" + + proc test(args: varargs[string, foo]) = + echo "varargs" + + proc test(a: AObj) = + echo "test AObj" + + let x = AObj() + test(x) From c274d9aed637af05f7acf553edb57721bd91d021 Mon Sep 17 00:00:00 2001 From: Antonis Geralis <43617260+planetis-m@users.noreply.github.com> Date: Fri, 30 Oct 2020 10:56:28 +0200 Subject: [PATCH 0865/3768] add approxequal (#15766) * add approxequal * Address review comments, tests that pass * more wikipedia links [ci skip] * forgot since Co-authored-by: b3liever Co-authored-by: Andreas Rumpf --- changelog.md | 2 ++ lib/pure/math.nim | 25 ++++++++++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 552e1e8df..6bff75a44 100644 --- a/changelog.md +++ b/changelog.md @@ -7,6 +7,7 @@ - `prelude` now works with the JavaScript target. - Added `ioutils` module containing `duplicate` and `duplicateTo` to duplicate `FileHandle` using C function `dup` and `dup2`. +- Added `almostEqual` in `math` for comparing two float values using a machine epsilon. - The JSON module can now handle integer literals and floating point literals of arbitrary length and precision. Numbers that do not fit the underlying `BiggestInt` or `BiggestFloat` fields are kept as string literals and @@ -16,6 +17,7 @@ - Added `randState` template that exposes the default random number generator. Useful for library authors. + ## Language changes diff --git a/lib/pure/math.nim b/lib/pure/math.nim index 21d614270..2a2660524 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -56,7 +56,7 @@ import std/private/since {.push debugger: off.} # the user does not want to trace a part # of the standard library! -import bitops +import bitops, fenv proc binom*(n, k: int): int {.noSideEffect.} = ## Computes the `binomial coefficient `_. @@ -158,6 +158,29 @@ proc classify*(x: float): FloatClass = return fcSubnormal return fcNormal +proc almostEqual*[T: SomeFloat](x, y: T; unitsInLastPlace: Natural = 4): bool {. + since: (1, 5), inline, noSideEffect.} = + ## Checks if two float values are almost equal, using + ## `machine epsilon `_. + ## + ## `unitsInLastPlace` is the max number of + ## `units in last place `_ + ## difference tolerated when comparing two numbers. The larger the value, the + ## more error is allowed. A ``0`` value means that two numbers must be exactly the + ## same to be considered equal. + ## + ## The machine epsilon has to be scaled to the magnitude of the values used + ## and multiplied by the desired precision in ULPs unless the difference is + ## subnormal. + ## + # taken from: https://en.cppreference.com/w/cpp/types/numeric_limits/epsilon + runnableExamples: + doAssert almostEqual(3.141592653589793, 3.1415926535897936) + doAssert almostEqual(1.6777215e7'f32, 1.6777216e7'f32) + let diff = abs(x - y) + result = diff <= epsilon(T) * abs(x + y) * T(unitsInLastPlace) or + diff < minimumPositiveValue(T) + proc isPowerOfTwo*(x: int): bool {.noSideEffect.} = ## Returns ``true``, if ``x`` is a power of two, ``false`` otherwise. ## From 1655c04aa3b980bf0a9d65bf658c19efa6af7d94 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Fri, 30 Oct 2020 16:57:02 +0800 Subject: [PATCH 0866/3768] add testcase for #9165 (#15787) --- tests/converter/t9165.nim | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 tests/converter/t9165.nim diff --git a/tests/converter/t9165.nim b/tests/converter/t9165.nim new file mode 100644 index 000000000..d0225ffbc --- /dev/null +++ b/tests/converter/t9165.nim @@ -0,0 +1,11 @@ +type ustring = distinct string + +converter toUString(s: string): ustring = ustring(s) +converter toString(s: ustring): string = string(s) + +proc `[]=`*(s: var ustring, slice: Slice[int], replacement: ustring) {.inline.} = + s = replacement + +var s = ustring("123") +s[1..2] = "3" +doAssert s == "3" \ No newline at end of file From 2de90a14cbc5182efad2cff63cd33f820e3a8832 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Fri, 30 Oct 2020 16:57:26 +0800 Subject: [PATCH 0867/3768] add testcase for #8012 (#15785) --- tests/concepts/t8012.nim | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 tests/concepts/t8012.nim diff --git a/tests/concepts/t8012.nim b/tests/concepts/t8012.nim new file mode 100644 index 000000000..ec2aa6e5c --- /dev/null +++ b/tests/concepts/t8012.nim @@ -0,0 +1,15 @@ +type + MyTypeCon = concept c + c.counter is int + MyType = object + counter: int + +proc foo(conc: var MyTypeCon) = + conc.counter.inc + if conc.counter < 5: + foo(conc) + +var x: MyType + +x.foo +discard x.repr From 17f08b2939db224d533db846fc3fa8ab91cc9755 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Fri, 30 Oct 2020 16:58:55 +0800 Subject: [PATCH 0868/3768] closes #7658 (#15784) * fix #7658 * more --- compiler/pragmas.nim | 9 ++++++--- tests/js/tunion.nim | 7 +++++++ 2 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 tests/js/tunion.nim diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index c0c9cc06a..273b2b004 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -1110,9 +1110,12 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, else: sym.typ.kind = tyUncheckedArray of wUnion: - noVal(c, it) - if sym.typ == nil: invalidPragma(c, it) - else: incl(sym.typ.flags, tfUnion) + if c.config.backend == backendJs: + localError(c.config, it.info, "`{.union.}` is not implemented for js backend.") + else: + noVal(c, it) + if sym.typ == nil: invalidPragma(c, it) + else: incl(sym.typ.flags, tfUnion) of wRequiresInit: noVal(c, it) if sym.kind == skField: diff --git a/tests/js/tunion.nim b/tests/js/tunion.nim new file mode 100644 index 000000000..e185495ad --- /dev/null +++ b/tests/js/tunion.nim @@ -0,0 +1,7 @@ +discard """ + errormsg: "`{.union.}` is not implemented for js backend." +""" + +type Foo {.union.} = object + as_bytes: array[8, int8] + data: int64 From 3028c1be82a04c0271fcb98396f28ebc62775b3a Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Fri, 30 Oct 2020 16:59:38 +0800 Subject: [PATCH 0869/3768] add testcase for #7416 (#15782) --- tests/overload/t7416.nim | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 tests/overload/t7416.nim diff --git a/tests/overload/t7416.nim b/tests/overload/t7416.nim new file mode 100644 index 000000000..4a9b2e7cb --- /dev/null +++ b/tests/overload/t7416.nim @@ -0,0 +1,9 @@ +type + Foo[T] = object + IntFoo = Foo[int] + +proc bar(b: object|tuple) = discard +proc bar(b: IntFoo) = discard + +var f: IntFoo +bar(f) \ No newline at end of file From 1725db9295b037ac410f0e6d48952c01218aee89 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Fri, 30 Oct 2020 16:59:56 +0800 Subject: [PATCH 0870/3768] closes #7374 (#15781) * add testcase for #7374 * minor * fix test --- tests/template/tshadow.nim | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 tests/template/tshadow.nim diff --git a/tests/template/tshadow.nim b/tests/template/tshadow.nim new file mode 100644 index 000000000..a4de71592 --- /dev/null +++ b/tests/template/tshadow.nim @@ -0,0 +1,29 @@ +discard """ + output: '''fish +fish''' +""" + +import macros + +block: + template init(initHook: proc(s: string)) = + proc dostuff = + var s = "fish" + initHook(s) + dostuff() + + init do(s: string): + echo s + +block: + macro init(initHook: proc(s: string)) = + result = newStmtList( + newProc(name = ident("dostuff"), body = newStmtList( + newVarStmt(ident("s"), newStrLitNode("fish")), + newCall(initHook, ident("s")) + )), + newCall("dostuff") + ) + + init proc(s: string) = + echo s From 4374c638cd328b8cf9b72ff756cfe0564d6d9c78 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Fri, 30 Oct 2020 17:00:25 +0800 Subject: [PATCH 0871/3768] closes #6036 (#15779) * add testcase for #6036 * try * try again * make it work --- tests/ccgbugs/tcompile_time_var_at_runtime.nim | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 tests/ccgbugs/tcompile_time_var_at_runtime.nim diff --git a/tests/ccgbugs/tcompile_time_var_at_runtime.nim b/tests/ccgbugs/tcompile_time_var_at_runtime.nim new file mode 100644 index 000000000..c0de0390b --- /dev/null +++ b/tests/ccgbugs/tcompile_time_var_at_runtime.nim @@ -0,0 +1,11 @@ +discard """ + output: "1\n2\n2\n3" +""" +var a {.compileTime.} = 1 + +echo a +a = 2 +echo a +echo a +a = 3 +echo a \ No newline at end of file From c0fdc8b215b02736cdb1c275a92b1d5a513f8bad Mon Sep 17 00:00:00 2001 From: Miran Date: Fri, 30 Oct 2020 10:04:53 +0100 Subject: [PATCH 0872/3768] fix `toHex` - make it work with int literals (#15770) --- lib/pure/strutils.nim | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 20ad2f517..94bdafc10 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -981,6 +981,18 @@ proc toHex*(x: BiggestInt, len: Positive): string {.noSideEffect, doAssert c.toHex(6) == "FFFFF8" toHexImpl(cast[BiggestUInt](x), len, x < 0) +proc toHex*(x: int, len: Positive): string {.noSideEffect.} = + ## Converts `x` to its hexadecimal representation. + ## + ## The resulting string will be exactly `len` characters long. No prefix like + ## ``0x`` is generated. `x` is treated as an unsigned value. + runnableExamples: + doAssert toHex(62, 3) == "03E" + doAssert toHex(4097, 3) == "001" + doAssert toHex(4097, 4) == "1001" + doAssert toHex(-8, 6) == "FFFFF8" + toHexImpl(cast[BiggestUInt](x), len, x < 0) + proc toHex*[T: SomeInteger](x: T): string {.noSideEffect.} = ## Shortcut for ``toHex(x, T.sizeof * 2)`` runnableExamples: From ec059240b030161111d7ffdbd07a8c677bc17994 Mon Sep 17 00:00:00 2001 From: Miran Date: Fri, 30 Oct 2020 10:12:01 +0100 Subject: [PATCH 0873/3768] promote `collect` macro as a map+filter replacement (#15788) * promote `collect` macro as a map+filter replacement * Update lib/pure/collections/sequtils.nim --- lib/pure/collections/sequtils.nim | 27 +++++++++++++++++++++++++++ lib/pure/strutils.nim | 3 ++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index 803f89cc5..20a0f77cb 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -27,6 +27,7 @@ ## languages. ## ## For functional style programming you have different options at your disposal: +## * `sugar.collect macro`_ ## * pass `anonymous proc`_ ## * import `sugar module`_ and use ## `=> macro.m,untyped,untyped>`_ @@ -45,8 +46,14 @@ ## let ## foo = toSeq(1..10).map(x => x*2).filter(x => x mod 6 != 0) ## bar = toSeq(1..10).mapIt(it*2).filterIt(it mod 6 != 0) +## baz = collect(newSeq): +## for i in 1..10: +## let j = 2*i +## if j mod 6 != 0: +## j ## ## doAssert foo == bar +## doAssert foo == baz ## echo foo # @[2, 4, 8, 10, 14, 16, 20] ## ## echo foo.any(x => x > 17) # true @@ -361,7 +368,11 @@ proc map*[T, S](s: openArray[T], op: proc (x: T): S {.closure.}): ## Since the input is not modified you can use it to ## transform the type of the elements in the input container. ## + ## Instead of using `map` and `filter`, consider using the `collect` macro + ## from the `sugar` module. + ## ## See also: + ## * `sugar.collect macro`_ ## * `mapIt template<#mapIt.t,typed,untyped>`_ ## * `apply proc<#apply,openArray[T],proc(T)_2>`_ for the in-place version ## @@ -424,7 +435,11 @@ iterator filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): T = ## Iterates through a container `s` and yields every item that fulfills the ## predicate `pred` (function that returns a `bool`). ## + ## Instead of using `map` and `filter`, consider using the `collect` macro + ## from `sugar` module. + ## ## See also: + ## * `sugar.collect macro`_ ## * `fliter proc<#filter,openArray[T],proc(T)>`_ ## * `filterIt template<#filterIt.t,untyped,untyped>`_ ## @@ -444,7 +459,11 @@ proc filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): seq[T] ## Returns a new sequence with all the items of `s` that fulfilled the ## predicate `pred` (function that returns a `bool`). ## + ## Instead of using `map` and `filter`, consider using the `collect` macro + ## from `sugar` module. + ## ## See also: + ## * `sugar.collect macro`_ ## * `filterIt template<#filterIt.t,untyped,untyped>`_ ## * `filter iterator<#filter.i,openArray[T],proc(T)>`_ ## * `keepIf proc<#keepIf,seq[T],proc(T)>`_ for the in-place version @@ -560,7 +579,11 @@ template filterIt*(s, pred: untyped): untyped = ## the predicate needs to be an expression using the ``it`` variable ## for testing, like: ``filterIt("abcxyz", it == 'x')``. ## + ## Instead of using `mapIt` and `filterIt`, consider using the `collect` macro + ## from `sugar` module. + ## ## See also: + ## * `sugar.collect macro`_ ## * `fliter proc<#filter,openArray[T],proc(T)>`_ ## * `filter iterator<#filter.i,openArray[T],proc(T)>`_ ## @@ -916,7 +939,11 @@ template mapIt*(s: typed, op: untyped): untyped = ## The template injects the ``it`` variable which you can use directly in an ## expression. ## + ## Instead of using `mapIt` and `filterIt`, consider using the `collect` macro + ## from `sugar` module. + ## ## See also: + ## * `sugar.collect macro`_ ## * `map proc<#map,openArray[T],proc(T)>`_ ## * `applyIt template<#applyIt.t,untyped,untyped>`_ for the in-place version ## diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 94bdafc10..740a6e391 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -59,12 +59,13 @@ ## * `unicode module`_ for Unicode UTF-8 handling ## * `sequtils module`_ for operations on container ## types (including strings) +## * `parsecsv module`_ for a high-performance CSV parser ## * `parseutils module`_ for lower-level parsing of tokens, ## numbers, identifiers, etc. ## * `parseopt module`_ for command-line parsing +## * `pegs module`_ for PEG (Parsing Expression Grammar) support ## * `strtabs module`_ for efficient hash tables ## (dictionaries, in some programming languages) mapping from strings to strings -## * `pegs module`_ for PEG (Parsing Expression Grammar) support ## * `ropes module`_ for rope data type, which can represent very ## long strings efficiently ## * `re module`_ for regular expression (regex) support From 6fe2e8977d0227a454cfb606c821cc455a9d0e07 Mon Sep 17 00:00:00 2001 From: cooldome Date: Fri, 30 Oct 2020 11:42:06 +0000 Subject: [PATCH 0874/3768] canAlias improvement (#15773) * canAlias improvement * fix comment --- compiler/isolation_check.nim | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/compiler/isolation_check.nim b/compiler/isolation_check.nim index 8c40dd4ad..01f0a002a 100644 --- a/compiler/isolation_check.nim +++ b/compiler/isolation_check.nim @@ -64,9 +64,18 @@ proc canAlias(arg, ret: PType; marker: var IntSet): bool = else: result = false +proc isValueOnlyType(t: PType): bool = + # t doesn't contain pointers and references + proc wrap(t: PType): bool {.nimcall.} = t.kind in {tyRef, tyPtr, tyVar, tyLent} + result = not types.searchTypeFor(t, wrap) + proc canAlias*(arg, ret: PType): bool = - var marker = initIntSet() - result = canAlias(arg, ret, marker) + if isValueOnlyType(arg): + # can alias only with unsafeAddr(arg.x) and we don't care if it is not safe + result = false + else: + var marker = initIntSet() + result = canAlias(arg, ret, marker) proc checkIsolate*(n: PNode): bool = if types.containsTyRef(n.typ): From 2cfe5e0745cf33ed5698378dd16e01ce542f05e5 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Fri, 30 Oct 2020 22:34:07 +0800 Subject: [PATCH 0875/3768] [closes #11625 and closes #2488]add global and threadvar(with `--threads:off` mode ) pragmas supports for JS backend (#15772) * add global pragma support for js backend * globalThis * add support for threadvar * more tests * Update compiler/jsgen.nim Co-authored-by: Andreas Rumpf --- compiler/jsgen.nim | 7 ++++++- tests/js/tglobal.nim | 30 ++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 tests/js/tglobal.nim diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 8b91ada8a..766e6a80d 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -1736,6 +1736,7 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) = varCode: string varName = mangleName(p.module, v) useReloadingGuard = sfGlobal in v.flags and p.config.hcrOn + useGlobalPragmas = sfGlobal in v.flags and ({sfPure, sfThread} * v.flags != {}) if v.constraint.isNil: if useReloadingGuard: @@ -1743,6 +1744,10 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) = lineF(p, "if ($1 === undefined) {$n", varName) varCode = $varName inc p.extraIndent + elif useGlobalPragmas: + lineF(p, "if (globalThis.$1 === undefined) {$n", varName) + varCode = $varName + inc p.extraIndent else: varCode = "var $2" else: @@ -1795,7 +1800,7 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) = else: line(p, runtimeFormat(varCode & " = $3;$n", [returnType, v.loc.r, s])) - if useReloadingGuard: + if useReloadingGuard or useGlobalPragmas: dec p.extraIndent lineF(p, "}$n") diff --git a/tests/js/tglobal.nim b/tests/js/tglobal.nim new file mode 100644 index 000000000..38f5eec34 --- /dev/null +++ b/tests/js/tglobal.nim @@ -0,0 +1,30 @@ +block global: + proc getState(): int = + var state0 {.global.}: int + inc state0 + result = state0 + + for i in 0 ..< 3: + doAssert getState() == i + 1 + + for i in 0 ..< 3: + once: + doAssert i == 0 + + +block threadvar: + proc getThreadState0(): int = + var state0 {.threadvar.}: int + inc state0 + result = state0 + + for i in 0 ..< 3: + doAssert getThreadState0() == i + 1 + + proc getThreadState1(): int = + var state1 {.threadvar.}: int + inc state1 + result = state1 + + for i in 0 ..< 3: + doAssert getThreadState1() == i + 1 From ae86df12af6c00ea74659375b758178cc2d73311 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Fri, 30 Oct 2020 22:53:45 +0800 Subject: [PATCH 0876/3768] add testcase for #14227 (#15794) --- tests/macros/t14227.nim | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 tests/macros/t14227.nim diff --git a/tests/macros/t14227.nim b/tests/macros/t14227.nim new file mode 100644 index 000000000..4206e2c06 --- /dev/null +++ b/tests/macros/t14227.nim @@ -0,0 +1,23 @@ +discard """ + action: "compile" +""" +import sugar + + +block: + let y = @[@[1, 2], @[2, 4, 6]] + let x = collect(newSeq): + for i in y: + if i.len > 2: + for j in i: + j + echo(x) + +block: + let y = @[@[1, 2], @[2, 4, 6]] + let x = collect(newSeq): + for i in y: + for j in i: + if i.len > 2: + j + echo(x) From 7364794fedcf8ee20ca5a55f58c14c76dfa31723 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Fri, 30 Oct 2020 16:00:37 +0100 Subject: [PATCH 0877/3768] Grammar fixes --- lib/pure/collections/sequtils.nim | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index 20a0f77cb..ea2c02b8c 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -436,7 +436,7 @@ iterator filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): T = ## predicate `pred` (function that returns a `bool`). ## ## Instead of using `map` and `filter`, consider using the `collect` macro - ## from `sugar` module. + ## from the `sugar` module. ## ## See also: ## * `sugar.collect macro`_ @@ -460,7 +460,7 @@ proc filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): seq[T] ## predicate `pred` (function that returns a `bool`). ## ## Instead of using `map` and `filter`, consider using the `collect` macro - ## from `sugar` module. + ## from the `sugar` module. ## ## See also: ## * `sugar.collect macro`_ @@ -580,7 +580,7 @@ template filterIt*(s, pred: untyped): untyped = ## for testing, like: ``filterIt("abcxyz", it == 'x')``. ## ## Instead of using `mapIt` and `filterIt`, consider using the `collect` macro - ## from `sugar` module. + ## from the `sugar` module. ## ## See also: ## * `sugar.collect macro`_ @@ -940,7 +940,7 @@ template mapIt*(s: typed, op: untyped): untyped = ## expression. ## ## Instead of using `mapIt` and `filterIt`, consider using the `collect` macro - ## from `sugar` module. + ## from the `sugar` module. ## ## See also: ## * `sugar.collect macro`_ From 28bbcd9d45f15247af347785a46d0c2e2750b8ca Mon Sep 17 00:00:00 2001 From: Miran Date: Fri, 30 Oct 2020 16:30:00 +0100 Subject: [PATCH 0878/3768] fix #15702, show enum fields documentation (#15792) --- compiler/semtypes.nim | 3 ++- nimdoc/testproject/expected/testproject.html | 15 +++++++++++++++ nimdoc/testproject/expected/testproject.idx | 4 ++++ nimdoc/testproject/expected/theindex.html | 16 ++++++++++++++++ nimdoc/testproject/testproject.nim | 10 +++++++++- 5 files changed, 46 insertions(+), 2 deletions(-) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 97cf7c5ec..21667895a 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -133,7 +133,8 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = e.typ = result e.position = int(counter) let symNode = newSymNode(e) - if optNimV1Emulation notin c.config.globalOptions and identToReplace != nil: + if optNimV1Emulation notin c.config.globalOptions and identToReplace != nil and + c.config.cmd != cmdDoc: # A hack to produce documentation for enum fields. identToReplace[] = symNode if e.position == 0: hasNull = true if result.sym != nil and sfExported in result.sym.flags: diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html index 103dea25f..d9132d087 100644 --- a/nimdoc/testproject/expected/testproject.html +++ b/nimdoc/testproject/expected/testproject.html @@ -109,6 +109,11 @@ function main() {
  • Foo
  • +
  • Shapes
  • @@ -406,6 +411,16 @@ The enum B. +
    + +
    Shapes = enum
    +  Circle,                   ## A circle
    +  Triangle,                 ## A three-sided shape
    +  Rectangle                  ## A four-sided shape
    +
    + +Some shapes. +
    diff --git a/nimdoc/testproject/expected/testproject.idx b/nimdoc/testproject/expected/testproject.idx index 0b75b29d2..8d132acc4 100644 --- a/nimdoc/testproject/expected/testproject.idx +++ b/nimdoc/testproject/expected/testproject.idx @@ -54,3 +54,7 @@ asyncFun1 testproject.html#asyncFun1 testproject: asyncFun1(): Future[int] asyncFun2 testproject.html#asyncFun2 testproject: asyncFun2(): owned(Future[void]) asyncFun3 testproject.html#asyncFun3 testproject: asyncFun3(): owned(Future[void]) testNimDocTrailingExample testproject.html#testNimDocTrailingExample.t testproject: testNimDocTrailingExample() +Circle testproject.html#Circle Shapes.Circle +Triangle testproject.html#Triangle Shapes.Triangle +Rectangle testproject.html#Rectangle Shapes.Rectangle +Shapes testproject.html#Shapes testproject: Shapes diff --git a/nimdoc/testproject/expected/theindex.html b/nimdoc/testproject/expected/theindex.html index 66dea92fc..34d24e900 100644 --- a/nimdoc/testproject/expected/theindex.html +++ b/nimdoc/testproject/expected/theindex.html @@ -132,6 +132,10 @@ function main() {
  • testproject: C_D
  • +
    Circle:
    c_nonexistant:
    +
    Rectangle:
    +
    Shapes:
    someFunc:
    +
    Triangle:
    tripleStrLitTest:
    • testproject: tripleStrLitTest()
    • diff --git a/nimdoc/testproject/testproject.nim b/nimdoc/testproject/testproject.nim index 25cdf39a4..ffddc356f 100644 --- a/nimdoc/testproject/testproject.nim +++ b/nimdoc/testproject/testproject.nim @@ -363,4 +363,12 @@ when true: # where runnableExamples would not show if there was not at least 2 "\n" after # the last character of runnableExamples runnableExamples: - discard 2 \ No newline at end of file + discard 2 + +when true: # issue #15702 + type + Shapes* = enum + ## Some shapes. + Circle, ## A circle + Triangle, ## A three-sided shape + Rectangle ## A four-sided shape From bc9a52127092680c662e0456797cbd1278203360 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sat, 31 Oct 2020 10:03:51 +0800 Subject: [PATCH 0879/3768] [closes #12682]add testcase for #12682 (#15796) * add testcase for #12682 * fix --- tests/misc/tlocals.nim | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/misc/tlocals.nim b/tests/misc/tlocals.nim index ad9e7d032..a6df68224 100644 --- a/tests/misc/tlocals.nim +++ b/tests/misc/tlocals.nim @@ -62,3 +62,14 @@ proc foo3[T](y: T) = bar2(y) foo3(12) + +block: # bug #12682 + template foo(): untyped = + var c1 = locals() + 1 + + proc testAll()= + doAssert foo() == 1 + let c2=locals() + + testAll() From 8115b655e77297e3d1ede5889de211a6a4dbad7b Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sat, 31 Oct 2020 17:05:01 +0800 Subject: [PATCH 0880/3768] support par expression as checkpoint (#15802) --- lib/pure/unittest.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim index 31eaa657b..0536956e9 100644 --- a/lib/pure/unittest.nim +++ b/lib/pure/unittest.nim @@ -641,7 +641,7 @@ macro check*(conditions: untyped): untyped = let paramAst = exp[i] if exp[i].kind == nnkIdent: result.printOuts.add getAst(print(argStr, paramAst)) - if exp[i].kind in nnkCallKinds + {nnkDotExpr, nnkBracketExpr}: + if exp[i].kind in nnkCallKinds + {nnkDotExpr, nnkBracketExpr, nnkPar}: let callVar = newIdentNode(":c" & $counter) result.assigns.add getAst(asgn(callVar, paramAst)) result.check[i] = callVar From 80b0748d75fe70febd89f02cf419e1644f67350d Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sat, 31 Oct 2020 19:06:13 +0800 Subject: [PATCH 0881/3768] fix #15651 (#15800) * fix * minor --- compiler/jsgen.nim | 1 + tests/js/treprinifexpr.nim | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 tests/js/treprinifexpr.nim diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 766e6a80d..cd14f1cf9 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -1927,6 +1927,7 @@ proc genRepr(p: PProc, n: PNode, r: var TCompRes) = genReprAux(p, n, r, "reprJSONStringify") else: genReprAux(p, n, r, "reprAny", genTypeInfo(p, t)) + r.kind = resExpr proc genOf(p: PProc, n: PNode, r: var TCompRes) = var x: TCompRes diff --git a/tests/js/treprinifexpr.nim b/tests/js/treprinifexpr.nim new file mode 100644 index 000000000..09ded18b9 --- /dev/null +++ b/tests/js/treprinifexpr.nim @@ -0,0 +1,18 @@ +type + Enum = enum A + +let + enumVal = A + tmp = if true: $enumVal else: $enumVal + +let + intVal = 12 + tmp2 = if true: repr(intVal) else: $enumVal + +let + strVal = "123" + tmp3 = if true: repr(strVal) else: $strVal + +let + floatVal = 12.4 + tmp4 = if true: repr(floatVal) else: $floatVal \ No newline at end of file From bcb30566fc5bdddcf8eb283efc12d57663717901 Mon Sep 17 00:00:00 2001 From: n5m <72841454+n5m@users.noreply.github.com> Date: Sat, 31 Oct 2020 16:45:14 +0000 Subject: [PATCH 0882/3768] improve public Testament docs (#15710) * improve glob docs * punctuation * move spec descriptions above example notably, change the description of timeout to reference seconds, not microseconds * document nimout * document sortoutput * explain the action options --- doc/testament.rst | 93 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 73 insertions(+), 20 deletions(-) diff --git a/doc/testament.rst b/doc/testament.rst index e2612711b..b74030183 100644 --- a/doc/testament.rst +++ b/doc/testament.rst @@ -9,12 +9,15 @@ so can be useful to run your tests, even the most complex ones. Test files location =================== -By default Testament looks for test files on ``"./tests/*.nim"``, -you can overwrite this pattern glob using ``pattern ``, -the default working directory path can be changed using ``--directory:"folder/subfolder/"``. +By default Testament looks for test files on ``"./tests/*.nim"``. +You can overwrite this pattern glob using ``pattern ``. +The default working directory path can be changed using +``--directory:"folder/subfolder/"``. -Testament uses the nim compiler on ``PATH`` you can change that using ``--nim:"folder/subfolder/nim"``, -running JavaScript tests with ``--targets:"js"`` requires a working NodeJS on ``PATH``. +Testament uses the ``nim`` compiler on ``PATH``. +You can change that using ``--nim:"folder/subfolder/nim"``. +Running JavaScript tests with ``--targets:"js"`` requires a working NodeJS on +``PATH``. Options @@ -47,8 +50,6 @@ not very useful for production, but easy to understand: $ testament r test0 PASS: tests/test0.nim C ( 0.2 sec) - $ - Running all tests from a directory ================================== @@ -57,6 +58,12 @@ Running all tests from a directory $ testament pattern "tests/*.nim" +To recursively search for all tests in a directory, use + +.. code:: + + $ testament pattern "tests/**/*.nim" + HTML Reports ============ @@ -78,32 +85,78 @@ Example "template" **to edit** and write a Testament unittest: discard """ - action: "run" # What to do, one of "compile" OR "run". + # What actions to expect completion on. + # Options: + # "compile": expect successful compilation + # "run": expect successful compilation and execution + # "reject": expect failed compilation + action: "run" - exitcode: 0 # This is the Exit Code the test should return, zero typically. + # The exit code that the test is expected to return. Typically, the default + # value of 0 is fine. Note that if the test will be run by valgrind, then + # the test will exit with either a code of 0 on success or 1 on failure. + exitcode: 0 - output: "" # This is the Standard Output the test should print, if any. + # Provide an `output` string to assert that the test prints to standard out + # exatly the expected string. Provide an `outputsub` string to assert that + # the string given here is a substring of the standard out output of the + # test. + output: "" + outputsub: "" - input: "" # This is the Standard Input the test should take, if any. + # Whether to sort the output lines before comparing them to the desired + # output. + sortoutput: true - errormsg: "" # Error message the test should print, if any. + # Each line in the stringgiven here appear in the same order in the + # compiler output, but there may be more lines that appear before, after, or + # in between them. + nimout: ''' + a very long, + multi-line + string''' - batchable: true # Can be run in batch mode, or not. + # This is the Standard Input the test should take, if any. + input: "" - joinable: true # Can be run Joined with other tests to run all togheter, or not. + # Error message the test should print, if any. + errormsg: "" + # Can be run in batch mode, or not. + batchable: true + + # Can be run Joined with other tests to run all togheter, or not. + joinable: true + + # On Linux 64-bit machines, whether to use Valgrind to check for bad memory + # accesses or memory leaks. On other architectures, the test will be run + # as-is, without Valgrind. + # Options: + # true: run the test with Valgrind + # false: run the without Valgrind + # "leaks": run the test with Valgrind, but do not check for memory leaks valgrind: false # Can use Valgrind to check for memory leaks, or not (Linux 64Bit only). - cmd: "nim c -r $file" # Command the test should use to run. + # Command the test should use to run. If left out or an empty string is + # provided, the command is taken to be: + # "nim $target --hints:on -d:testing --nimblePath:tests/deps $options $file" + # You can use the $target, $options, and $file placeholders in your own + # command, too. + cmd: "nim c -r $file" - maxcodesize: 666 # Maximum generated temporary intermediate code file size for the test. + # Maximum generated temporary intermediate code file size for the test. + maxcodesize: 666 - timeout: 666 # Timeout microseconds to run the test. + # Timeout seconds to run the test. Fractional values are supported. + timeout: 1.5 - target: "c js" # Targets to run the test into (C, C++, JavaScript, etc). + # Targets to run the test into (C, C++, JavaScript, etc). + target: "c js" - disabled: "bsd" # Disable the test by condition, here BSD is disabled just as an example. - disabled: "win" # Can disable multiple OSes at once + # Conditions that will skip this test. Use of multiple "disabled" clauses + # is permitted. + disabled: "bsd" # Can disable OSes... + disabled: "win" disabled: "32bit" # ...or architectures disabled: "i386" disabled: "azure" # ...or pipeline runners From 1eb4a38a50f6b0d02e7896203dcc14b049348e05 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Sat, 31 Oct 2020 17:47:09 +0100 Subject: [PATCH 0883/3768] Typos --- doc/testament.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/testament.rst b/doc/testament.rst index b74030183..18501f4cd 100644 --- a/doc/testament.rst +++ b/doc/testament.rst @@ -108,7 +108,7 @@ Example "template" **to edit** and write a Testament unittest: # output. sortoutput: true - # Each line in the stringgiven here appear in the same order in the + # Each line in the string given here appears in the same order in the # compiler output, but there may be more lines that appear before, after, or # in between them. nimout: ''' From 98cec6b6922da62d9fc304de5c1d7cfb28fca879 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 2 Nov 2020 02:53:00 +0800 Subject: [PATCH 0884/3768] closes #3670 [add testcase for #3670] (#15808) * add testcase * Update tests/template/twhenintemplates.nim Co-authored-by: Clyybber --- tests/template/twhenintemplates.nim | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 tests/template/twhenintemplates.nim diff --git a/tests/template/twhenintemplates.nim b/tests/template/twhenintemplates.nim new file mode 100644 index 000000000..6fded856d --- /dev/null +++ b/tests/template/twhenintemplates.nim @@ -0,0 +1,11 @@ +# bug #3670 + +template someTempl(someConst: bool) = + when someConst: + var a : int + if true: + when not someConst: + var a : int + a = 5 + +someTempl(true) From 27eb19cc48ad76868de921795a0895ddf89aefb3 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 2 Nov 2020 15:56:51 +0800 Subject: [PATCH 0885/3768] fix #15145 (#15816) --- compiler/types.nim | 10 ++++++---- tests/enum/tenum_self.nim | 11 +++++++++++ 2 files changed, 17 insertions(+), 4 deletions(-) create mode 100644 tests/enum/tenum_self.nim diff --git a/compiler/types.nim b/compiler/types.nim index 7e56f5002..9875f5e1b 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -747,8 +747,9 @@ proc firstOrd*(conf: ConfigRef; t: PType): Int128 = if t.len > 0 and t[0] != nil: result = firstOrd(conf, t[0]) else: - assert(t.n[0].kind == nkSym) - result = toInt128(t.n[0].sym.position) + if t.n.len > 0: + assert(t.n[0].kind == nkSym) + result = toInt128(t.n[0].sym.position) of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink, tyStatic, tyInferred, tyUserTypeClasses, tyLent: result = firstOrd(conf, lastSon(t)) @@ -804,8 +805,9 @@ proc lastOrd*(conf: ConfigRef; t: PType): Int128 = of tyUInt64: result = toInt128(0xFFFFFFFFFFFFFFFF'u64) of tyEnum: - assert(t.n[^1].kind == nkSym) - result = toInt128(t.n[^1].sym.position) + if t.n.len > 0: + assert(t.n[^1].kind == nkSym) + result = toInt128(t.n[^1].sym.position) of tyGenericInst, tyDistinct, tyTypeDesc, tyAlias, tySink, tyStatic, tyInferred, tyUserTypeClasses, tyLent: result = lastOrd(conf, lastSon(t)) diff --git a/tests/enum/tenum_self.nim b/tests/enum/tenum_self.nim new file mode 100644 index 000000000..27b1c3204 --- /dev/null +++ b/tests/enum/tenum_self.nim @@ -0,0 +1,11 @@ +discard """ + errormsg: "1 can't be converted to ErrorFoo" +""" + + +type + Foo = enum + Bar = 0.Foo + + ErrorFoo = enum + eBar = 1.ErrorFoo From 919593395c7325ffd1cb93fb787c2772da2195f0 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 2 Nov 2020 17:22:26 +0800 Subject: [PATCH 0886/3768] fix #15815 (#15817) --- lib/pure/json.nim | 5 ++++- tests/stdlib/toptions.nim | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 tests/stdlib/toptions.nim diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 9766e32cb..f3145301f 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -1120,7 +1120,10 @@ when defined(nimFixedForwardGeneric): proc initFromJson[T](dst: var Option[T]; jsonNode: JsonNode; jsonPath: var string) = if jsonNode != nil and jsonNode.kind != JNull: - dst = some(default(T)) + when T is ref: + dst = some(new(T)) + else: + dst = some(default(T)) initFromJson(dst.get, jsonNode, jsonPath) macro assignDistinctImpl[T: distinct](dst: var T;jsonNode: JsonNode; jsonPath: var string) = diff --git a/tests/stdlib/toptions.nim b/tests/stdlib/toptions.nim new file mode 100644 index 000000000..b982d747d --- /dev/null +++ b/tests/stdlib/toptions.nim @@ -0,0 +1,16 @@ +discard """ + output: '''{"foo":{"test":"123"}}''' +""" + +import json, options + +type + Foo = ref object + test: string + Test = object + foo: Option[Foo] + +let js = """{"foo": {"test": "123"}}""" +let parsed = parseJson(js) +let a = parsed.to(Test) +echo $(%*a) \ No newline at end of file From 558115fa2906abeda11e6fd128ac139e9aac1838 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 2 Nov 2020 17:27:48 +0800 Subject: [PATCH 0887/3768] fixes #15717 --- compiler/vmgen.nim | 2 ++ tests/vm/tnewseqofcap.nim | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 tests/vm/tnewseqofcap.nim diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index f084ab7ba..0c0c491e0 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -683,6 +683,8 @@ proc genNewSeq(c: PCtx; n: PNode) = proc genNewSeqOfCap(c: PCtx; n: PNode; dest: var TDest) = let t = n.typ + if dest < 0: + dest = c.getTemp(n.typ) let tmp = c.getTemp(n[1].typ) c.gABx(n, opcLdNull, dest, c.genType(t)) c.gABx(n, opcLdImmInt, tmp, 0) diff --git a/tests/vm/tnewseqofcap.nim b/tests/vm/tnewseqofcap.nim new file mode 100644 index 000000000..c61c3c485 --- /dev/null +++ b/tests/vm/tnewseqofcap.nim @@ -0,0 +1,19 @@ +discard """ + output: '''@["aaa", "bbb", "ccc"]''' +""" + + +const + foo = @["aaa", "bbb", "ccc"] + +proc myTuple: tuple[n: int, bar: seq[string]] = + result.n = 42 + result.bar = newSeqOfCap[string](foo.len) + for f in foo: + result.bar.add(f) + +# It works if you change the below `const` to `let` +const + (n, bar) = myTuple() + +echo bar \ No newline at end of file From 5298366f862861a900c1a3b66d417cd54122a5ec Mon Sep 17 00:00:00 2001 From: Thomas Tay Date: Mon, 2 Nov 2020 01:33:16 -0800 Subject: [PATCH 0888/3768] Update tables documentation (#15807) Added a case where a user might use mgetOrPut and create an accidental copy of a seq. --- lib/pure/collections/tables.nim | 34 +++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index 1d76d42ea..e94e2a00b 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -460,6 +460,13 @@ proc mgetOrPut*[A, B](t: var Table[A, B], key: A, val: B): var B = ## Retrieves value at ``t[key]`` or puts ``val`` if not present, either way ## returning a value which can be modified. ## + ## + ## Note that while the value returned is of type `var B`, + ## it is easy to accidentally create an copy of the value at `t[key]`. + ## Remember that seqs and strings are value types, and therefore + ## cannot be copied into a separate variable for modification. + ## See the example below. + ## ## See also: ## * `[] proc<#[],Table[A,B],A>`_ for retrieving a value of a key ## * `hasKey proc<#hasKey,Table[A,B],A>`_ @@ -474,6 +481,17 @@ proc mgetOrPut*[A, B](t: var Table[A, B], key: A, val: B): var B = doAssert a.mgetOrPut('z', 99) == 99 doAssert a == {'a': 5, 'b': 9, 'z': 99}.toTable + # An example of accidentally creating a copy + var t = initTable[int, seq[int]]() + # In this example, we expect t[10] to be modified, + # but it is not. + var copiedSeq = t.mgetOrPut(10, @[10]) + copiedSeq.add(20) + doAssert t[10] == @[10] + # Correct + t.mgetOrPut(25, @[25]).add(35) + doAssert t[25] == @[25, 35] + mgetOrPutImpl(enlarge) proc len*[A, B](t: Table[A, B]): int = @@ -944,6 +962,12 @@ proc mgetOrPut*[A, B](t: TableRef[A, B], key: A, val: B): var B = ## Retrieves value at ``t[key]`` or puts ``val`` if not present, either way ## returning a value which can be modified. ## + ## Note that while the value returned is of type `var B`, + ## it is easy to accidentally create an copy of the value at `t[key]`. + ## Remember that seqs and strings are value types, and therefore + ## cannot be copied into a separate variable for modification. + ## See the example below. + ## ## See also: ## * `[] proc<#[],TableRef[A,B],A>`_ for retrieving a value of a key ## * `hasKey proc<#hasKey,TableRef[A,B],A>`_ @@ -958,6 +982,16 @@ proc mgetOrPut*[A, B](t: TableRef[A, B], key: A, val: B): var B = doAssert a.mgetOrPut('z', 99) == 99 doAssert a == {'a': 5, 'b': 9, 'z': 99}.newTable + # An example of accidentally creating a copy + var t = newTable[int, seq[int]]() + # In this example, we expect t[10] to be modified, + # but it is not. + var copiedSeq = t.mgetOrPut(10, @[10]) + copiedSeq.add(20) + doAssert t[10] == @[10] + # Correct + t.mgetOrPut(25, @[25]).add(35) + doAssert t[25] == @[25, 35] t[].mgetOrPut(key, val) proc len*[A, B](t: TableRef[A, B]): int = From 00b495de9520a91fe24d81e55da109960189a966 Mon Sep 17 00:00:00 2001 From: cooldome Date: Mon, 2 Nov 2020 09:35:24 +0000 Subject: [PATCH 0889/3768] Use modern enums in compiler (#15775) --- compiler/ast.nim | 26 ++--- compiler/astalgo.nim | 2 +- compiler/commands.nim | 8 +- compiler/condsyms.nim | 10 +- compiler/docgen.nim | 2 +- compiler/idents.nim | 4 +- compiler/lambdalifting.nim | 2 +- compiler/layouter.nim | 18 +-- compiler/lexer.nim | 103 +++++++---------- compiler/lineinfos.nim | 106 ++++++----------- compiler/linter.nim | 2 +- compiler/main.nim | 4 +- compiler/msgs.nim | 6 +- compiler/parser.nim | 2 +- compiler/pragmas.nim | 14 +-- compiler/renderer.nim | 2 +- compiler/types.nim | 2 +- compiler/wordrecg.nim | 229 ++++++++++++++----------------------- 18 files changed, 212 insertions(+), 330 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index a9793fb21..ca931ab09 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -17,21 +17,17 @@ export int128 type TCallingConvention* = enum - ccNimCall # nimcall, also the default - ccStdCall # procedure is stdcall - ccCDecl # cdecl - ccSafeCall # safecall - ccSysCall # system call - ccInline # proc should be inlined - ccNoInline # proc should not be inlined - ccFastCall # fastcall (pass parameters in registers) - ccThisCall # thiscall (parameters are pushed right-to-left) - ccClosure # proc has a closure - ccNoConvention # needed for generating proper C procs sometimes - -const CallingConvToStr*: array[TCallingConvention, string] = ["nimcall", "stdcall", - "cdecl", "safecall", "syscall", "inline", "noinline", "fastcall", "thiscall", - "closure", "noconv"] + ccNimCall = "nimcall" # nimcall, also the default + ccStdCall = "stdcall" # procedure is stdcall + ccCDecl = "cdecl" # cdecl + ccSafeCall = "safecall" # safecall + ccSysCall = "syscall" # system call + ccInline = "inline" # proc should be inlined + ccNoInline = "noinline" # proc should not be inlined + ccFastCall = "fastcall" # fastcall (pass parameters in registers) + ccThisCall = "thiscall" # thiscall (parameters are pushed right-to-left) + ccClosure = "closure" # proc has a closure + ccNoConvention = "noconv" # needed for generating proper C procs sometimes type TNodeKind* = enum # order is extremely important, because ranges are used diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 6aecb1dcb..d9cd5ade1 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -345,7 +345,7 @@ proc typeToYamlAux(conf: ConfigRef; n: PType, marker: var IntSet, indent: int, result.addf("$N$1\"n\": $2", [istr, treeToYamlAux(conf, n.n, marker, indent + 2, maxRecDepth - 1)]) if card(n.flags) > 0: result.addf("$N$1\"flags\": $2", [istr, flagsToStr(n.flags)]) - result.addf("$N$1\"callconv\": $2", [istr, makeYamlString(CallingConvToStr[n.callConv])]) + result.addf("$N$1\"callconv\": $2", [istr, makeYamlString($n.callConv)]) result.addf("$N$1\"size\": $2", [istr, rope(n.size)]) result.addf("$N$1\"align\": $2", [istr, rope(n.align)]) result.addf("$N$1\"sons\": $2", [istr, sonsRope]) diff --git a/compiler/commands.nim b/compiler/commands.nim index 325b362a9..a35d1dae7 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -193,12 +193,12 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass, elif i < arg.len and (arg[i] in {':', '='}): inc(i) else: invalidCmdLineOption(conf, pass, orig, info) if state == wHint: - let x = findStr(lineinfos.HintsToStr, id) - if x >= 0: n = TNoteKind(x + ord(hintMin)) + let x = findStr(hintMin..hintMax, id, errUnknown) + if x != errUnknown: n = TNoteKind(x) else: localError(conf, info, "unknown hint: " & id) else: - let x = findStr(lineinfos.WarningsToStr, id) - if x >= 0: n = TNoteKind(x + ord(warnMin)) + let x = findStr(warnMin..warnMax, id, errUnknown) + if x != errUnknown: n = TNoteKind(x) else: localError(conf, info, "unknown warning: " & id) var val = substr(arg, i).normalize diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 1fbc30bbe..ee849435f 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -13,7 +13,7 @@ import strtabs from options import Feature -from lineinfos import HintsToStr, WarningsToStr +from lineinfos import hintMin, hintMax, warnMin, warnMax proc defineSymbol*(symbols: StringTableRef; symbol: string, value: string = "true") = symbols[symbol] = value @@ -89,10 +89,10 @@ proc initDefines*(symbols: StringTableRef) = for f in Feature: defineSymbol("nimHas" & $f) - for s in WarningsToStr: - defineSymbol("nimHasWarning" & s) - for s in HintsToStr: - defineSymbol("nimHasHint" & s) + for s in warnMin..warnMax: + defineSymbol("nimHasWarning" & $s) + for s in hintMin..hintMax: + defineSymbol("nimHasHint" & $s) defineSymbol("nimFixedOwned") defineSymbol("nimHasStyleChecks") diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 1d950f835..7609c96ee 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -997,7 +997,7 @@ proc documentEffect(cache: IdentCache; n, x: PNode, effectType: TSpecialWord, id effects[i].typ = real[i].typ result = newTreeI(nkExprColonExpr, n.info, - newIdentNode(getIdent(cache, specialWords[effectType]), n.info), effects) + newIdentNode(getIdent(cache, $effectType), n.info), effects) proc documentWriteEffect(cache: IdentCache; n: PNode; flag: TSymFlag; pragmaName: string): PNode = let s = n[namePos].sym diff --git a/compiler/idents.nim b/compiler/idents.nim index b621503ec..1e8c912d9 100644 --- a/compiler/idents.nim +++ b/compiler/idents.nim @@ -107,8 +107,8 @@ proc newIdentCache*(): IdentCache = result.idDelegator = result.getIdent":delegator" result.emptyIdent = result.getIdent("") # initialize the keywords: - for s in succ(low(specialWords))..high(specialWords): - result.getIdent(specialWords[s], hashIgnoreStyle(specialWords[s])).id = ord(s) + for s in succ(low(TSpecialWord))..high(TSpecialWord): + result.getIdent($s, hashIgnoreStyle($s)).id = ord(s) proc whichKeyword*(id: PIdent): TSpecialWord = if id.id < 0: result = wInvalid diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index b552d9455..95e50d00f 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -295,7 +295,7 @@ proc markAsClosure(g: ModuleGraph; owner: PSym; n: PNode) = [s.name.s, typeToString(s.typ), g.config$s.info]) elif not (owner.typ.callConv == ccClosure or owner.typ.callConv == ccNimCall and tfExplicitCallConv notin owner.typ.flags): localError(g.config, n.info, "illegal capture '$1' because '$2' has the calling convention: <$3>" % - [s.name.s, owner.name.s, CallingConvToStr[owner.typ.callConv]]) + [s.name.s, owner.name.s, $owner.typ.callConv]) incl(owner.typ.flags, tfCapturesEnv) owner.typ.callConv = ccClosure diff --git a/compiler/layouter.nim b/compiler/layouter.nim index ee45d357f..c603c16ff 100644 --- a/compiler/layouter.nim +++ b/compiler/layouter.nim @@ -494,7 +494,7 @@ proc emitTok*(em: var Emitter; L: Lexer; tok: Token) = wrSpace em if not em.inquote: - wr(em, TokTypeToStr[tok.tokType], ltKeyword) + wr(em, $tok.tokType, ltKeyword) if tok.tokType in {tkAnd, tkOr, tkIn, tkNotin}: rememberSplit(splitIn) wrSpace em @@ -503,28 +503,28 @@ proc emitTok*(em: var Emitter; L: Lexer; tok: Token) = wr(em, tok.ident.s, ltIdent) of tkColon: - wr(em, TokTypeToStr[tok.tokType], ltOther) + wr(em, $tok.tokType, ltOther) wrSpace em of tkSemiColon, tkComma: - wr(em, TokTypeToStr[tok.tokType], ltOther) + wr(em, $tok.tokType, ltOther) rememberSplit(splitComma) wrSpace em of openPars: if tok.strongSpaceA > 0 and not em.endsInWhite and (not em.wasExportMarker or tok.tokType == tkCurlyDotLe): wrSpace em - wr(em, TokTypeToStr[tok.tokType], ltSomeParLe) + wr(em, $tok.tokType, ltSomeParLe) rememberSplit(splitParLe) of closedPars: - wr(em, TokTypeToStr[tok.tokType], ltSomeParRi) + wr(em, $tok.tokType, ltSomeParRi) of tkColonColon: - wr(em, TokTypeToStr[tok.tokType], ltOther) + wr(em, $tok.tokType, ltOther) of tkDot: lastTokWasTerse = true - wr(em, TokTypeToStr[tok.tokType], ltOther) + wr(em, $tok.tokType, ltOther) of tkEquals: if not em.inquote and not em.endsInWhite: wrSpace(em) - wr(em, TokTypeToStr[tok.tokType], ltOther) + wr(em, $tok.tokType, ltOther) if not em.inquote: wrSpace(em) of tkOpr, tkDotDot: if em.inquote or ((tok.strongSpaceA == 0 and tok.strongSpaceB == 0) and @@ -544,7 +544,7 @@ proc emitTok*(em: var Emitter; L: Lexer; tok: Token) = wrSpace(em) of tkAccent: if not em.inquote and endsInAlpha(em): wrSpace(em) - wr(em, TokTypeToStr[tok.tokType], ltOther) + wr(em, $tok.tokType, ltOther) em.inquote = not em.inquote of tkComment: if not preventComment: diff --git a/compiler/lexer.nim b/compiler/lexer.nim index c15ecddfc..e020236b1 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -31,37 +31,44 @@ const type TokType* = enum - tkInvalid, tkEof, # order is important here! - tkSymbol, # keywords: - tkAddr, tkAnd, tkAs, tkAsm, - tkBind, tkBlock, tkBreak, tkCase, tkCast, - tkConcept, tkConst, tkContinue, tkConverter, - tkDefer, tkDiscard, tkDistinct, tkDiv, tkDo, - tkElif, tkElse, tkEnd, tkEnum, tkExcept, tkExport, - tkFinally, tkFor, tkFrom, tkFunc, - tkIf, tkImport, tkIn, tkInclude, tkInterface, - tkIs, tkIsnot, tkIterator, - tkLet, - tkMacro, tkMethod, tkMixin, tkMod, tkNil, tkNot, tkNotin, - tkObject, tkOf, tkOr, tkOut, - tkProc, tkPtr, tkRaise, tkRef, tkReturn, - tkShl, tkShr, tkStatic, - tkTemplate, - tkTry, tkTuple, tkType, tkUsing, - tkVar, tkWhen, tkWhile, tkXor, - tkYield, # end of keywords - tkIntLit, tkInt8Lit, tkInt16Lit, tkInt32Lit, tkInt64Lit, - tkUIntLit, tkUInt8Lit, tkUInt16Lit, tkUInt32Lit, tkUInt64Lit, - tkFloatLit, tkFloat32Lit, tkFloat64Lit, tkFloat128Lit, - tkStrLit, tkRStrLit, tkTripleStrLit, - tkGStrLit, tkGTripleStrLit, tkCharLit, tkParLe, tkParRi, tkBracketLe, - tkBracketRi, tkCurlyLe, tkCurlyRi, - tkBracketDotLe, tkBracketDotRi, # [. and .] - tkCurlyDotLe, tkCurlyDotRi, # {. and .} - tkParDotLe, tkParDotRi, # (. and .) - tkComma, tkSemiColon, - tkColon, tkColonColon, tkEquals, tkDot, tkDotDot, tkBracketLeColon, - tkOpr, tkComment, tkAccent, + tkInvalid = "tkInvalid", tkEof = "[EOF]", # order is important here! + tkSymbol = "tkSymbol", # keywords: + tkAddr = "addr", tkAnd = "and", tkAs = "as", tkAsm = "asm", + tkBind = "bind", tkBlock = "block", tkBreak = "break", tkCase = "case", tkCast = "cast", + tkConcept = "concept", tkConst = "const", tkContinue = "continue", tkConverter = "converter", + tkDefer = "defer", tkDiscard = "discard", tkDistinct = "distinct", tkDiv = "div", tkDo = "do", + tkElif = "elif", tkElse = "else", tkEnd = "end", tkEnum = "enum", tkExcept = "except", tkExport = "export", + tkFinally = "finally", tkFor = "for", tkFrom = "from", tkFunc = "func", + tkIf = "if", tkImport = "import", tkIn = "in", tkInclude = "include", tkInterface = "interface", + tkIs = "is", tkIsnot = "isnot", tkIterator = "iterator", + tkLet = "let", + tkMacro = "macro", tkMethod = "method", tkMixin = "mixin", tkMod = "mod", tkNil = "nil", tkNot = "not", tkNotin = "notin", + tkObject = "object", tkOf = "of", tkOr = "or", tkOut = "out", + tkProc = "proc", tkPtr = "ptr", tkRaise = "raise", tkRef = "ref", tkReturn = "return", + tkShl = "shl", tkShr = "shr", tkStatic = "static", + tkTemplate = "template", + tkTry = "try", tkTuple = "tuple", tkType = "type", tkUsing = "using", + tkVar = "var", tkWhen = "when", tkWhile = "while", tkXor = "xor", + tkYield = "yield", # end of keywords + + tkIntLit = "tkIntLit", tkInt8Lit = "tkInt8Lit", tkInt16Lit = "tkInt16Lit", + tkInt32Lit = "tkInt32Lit", tkInt64Lit = "tkInt64Lit", + tkUIntLit = "tkUIntLit", tkUInt8Lit = "tkUInt8Lit", tkUInt16Lit = "tkUInt16Lit", + tkUInt32Lit = "tkUInt32Lit", tkUInt64Lit = "tkUInt64Lit", + tkFloatLit = "tkFloatLit", tkFloat32Lit = "tkFloat32Lit", + tkFloat64Lit = "tkFloat64Lit", tkFloat128Lit = "tkFloat128Lit", + tkStrLit = "tkStrLit", tkRStrLit = "tkRStrLit", tkTripleStrLit = "tkTripleStrLit", + tkGStrLit = "tkGStrLit", tkGTripleStrLit = "tkGTripleStrLit", tkCharLit = "tkCharLit", + + tkParLe = "(", tkParRi = ")", tkBracketLe = "[", + tkBracketRi = "]", tkCurlyLe = "{", tkCurlyRi = "}", + tkBracketDotLe = "[.", tkBracketDotRi = ".]", + tkCurlyDotLe = "{.", tkCurlyDotRi = ".}", + tkParDotLe = "(.", tkParDotRi = ".)", + tkComma = ",", tkSemiColon = ";", + tkColon = ":", tkColonColon = "::", tkEquals = "=", + tkDot = ".", tkDotDot = "..", tkBracketLeColon = "[:", + tkOpr, tkComment, tkAccent = "`", # these are fake tokens used by renderer.nim tkSpaces, tkInfixOpr, tkPrefixOpr, tkPostfixOpr @@ -74,35 +81,6 @@ const # tokens that should not be considered for previousToken tokKeywordLow* = succ(tkSymbol) tokKeywordHigh* = pred(tkIntLit) - TokTypeToStr*: array[TokType, string] = ["tkInvalid", "[EOF]", - "tkSymbol", - "addr", "and", "as", "asm", - "bind", "block", "break", "case", "cast", - "concept", "const", "continue", "converter", - "defer", "discard", "distinct", "div", "do", - "elif", "else", "end", "enum", "except", "export", - "finally", "for", "from", "func", "if", - "import", "in", "include", "interface", "is", "isnot", "iterator", - "let", - "macro", "method", "mixin", "mod", - "nil", "not", "notin", "object", "of", "or", - "out", "proc", "ptr", "raise", "ref", "return", - "shl", "shr", "static", - "template", - "try", "tuple", "type", "using", - "var", "when", "while", "xor", - "yield", - "tkIntLit", "tkInt8Lit", "tkInt16Lit", "tkInt32Lit", "tkInt64Lit", - "tkUIntLit", "tkUInt8Lit", "tkUInt16Lit", "tkUInt32Lit", "tkUInt64Lit", - "tkFloatLit", "tkFloat32Lit", "tkFloat64Lit", "tkFloat128Lit", - "tkStrLit", "tkRStrLit", - "tkTripleStrLit", "tkGStrLit", "tkGTripleStrLit", "tkCharLit", "(", - ")", "[", "]", "{", "}", "[.", ".]", "{.", ".}", "(.", ".)", - ",", ";", - ":", "::", "=", ".", "..", "[:", - "tkOpr", "tkComment", "`", - "tkSpaces", "tkInfixOpr", - "tkPrefixOpr", "tkPostfixOpr"] type NumericalBase* = enum @@ -171,7 +149,7 @@ proc `$`*(tok: Token): string = of tkIntLit..tkInt64Lit: $tok.iNumber of tkFloatLit..tkFloat64Lit: $tok.fNumber of tkInvalid, tkStrLit..tkCharLit, tkComment: tok.literal - of tkParLe..tkColon, tkEof, tkAccent: TokTypeToStr[tok.tokType] + of tkParLe..tkColon, tkEof, tkAccent: $tok.tokType else: if tok.ident != nil: tok.ident.s @@ -183,8 +161,7 @@ proc prettyTok*(tok: Token): string = else: $tok proc printTok*(conf: ConfigRef; tok: Token) = - msgWriteln(conf, $tok.line & ":" & $tok.col & "\t" & - TokTypeToStr[tok.tokType] & " " & $tok) + msgWriteln(conf, $tok.line & ":" & $tok.col & "\t" & $tok.tokType & " " & $tok) proc initToken*(L: var Token) = L.tokType = tkInvalid diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index 4a7c09276..759002312 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -36,40 +36,40 @@ type errProveInit, # deadcode errGenerated, errUser, - warnCannotOpenFile, - warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, - warnDeprecated, warnConfigDeprecated, - warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel, - warnUnknownSubstitutionX, warnLanguageXNotSupported, - warnFieldXNotSupported, warnCommentXIgnored, - warnTypelessParam, - warnUseBase, warnWriteToForeignHeap, warnUnsafeCode, - warnUnusedImportX, - warnInheritFromException, - warnEachIdentIsTuple, - warnUnsafeSetLen, - warnUnsafeDefault, - warnProveInit, warnProveField, warnProveIndex, - warnUnreachableElse, warnUnreachableCode, - warnStaticIndexCheck, warnGcUnsafe, warnGcUnsafe2, - warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed, - warnInconsistentSpacing, warnCaseTransition, warnCycleCreated, - warnObservableStores, - warnUser, - hintSuccess, hintSuccessX, hintCC, - hintLineTooLong, hintXDeclaredButNotUsed, - hintXCannotRaiseY, - hintConvToBaseNotNeeded, - hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled, - hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath, - hintConditionAlwaysTrue, hintConditionAlwaysFalse, hintName, hintPattern, - hintExecuting, hintLinking, hintDependency, - hintSource, hintPerformance, hintStackTrace, hintGCStats, - hintGlobalVar, hintExpandMacro, - hintUser, hintUserRaw, - hintExtendedContext, - hintMsgOrigin, # since 1.3.5 - hintDeclaredLoc, # since 1.5.1 + + warnCannotOpenFile = "CannotOpenFile", warnOctalEscape = "OctalEscape", + warnXIsNeverRead = "XIsNeverRead", warnXmightNotBeenInit = "XmightNotBeenInit", + warnDeprecated = "Deprecated", warnConfigDeprecated = "ConfigDeprecated", + warnSmallLshouldNotBeUsed = "SmallLshouldNotBeUsed", warnUnknownMagic = "UnknownMagic", + warnRedefinitionOfLabel = "RedefinitionOfLabel", warnUnknownSubstitutionX = "UnknownSubstitutionX", + warnLanguageXNotSupported = "LanguageXNotSupported", warnFieldXNotSupported = "FieldXNotSupported", + warnCommentXIgnored = "CommentXIgnored", warnTypelessParam = "TypelessParam", + warnUseBase = "UseBase", warnWriteToForeignHeap = "WriteToForeignHeap", + warnUnsafeCode = "UnsafeCode", warnUnusedImportX = "UnusedImport", + warnInheritFromException = "InheritFromException", warnEachIdentIsTuple = "EachIdentIsTuple", + warnUnsafeSetLen = "UnsafeSetLen", warnUnsafeDefault = "UnsafeDefault", + warnProveInit = "ProveInit", warnProveField = "ProveField", warnProveIndex = "ProveIndex", + warnUnreachableElse = "UnreachableElse", warnUnreachableCode = "UnreachableCode", + warnStaticIndexCheck = "IndexCheck", warnGcUnsafe = "GcUnsafe", warnGcUnsafe2 = "GcUnsafe2", + warnUninit = "Uninit", warnGcMem = "GcMem", warnDestructor = "Destructor", + warnLockLevel = "LockLevel", warnResultShadowed = "ResultShadowed", + warnInconsistentSpacing = "Spacing", warnCaseTransition = "CaseTransition", + warnCycleCreated = "CycleCreated", warnObservableStores = "ObservableStores", + warnUser = "User", + + hintSuccess = "Success", hintSuccessX = "SuccessX", hintCC = "CC", + hintLineTooLong = "LineTooLong", hintXDeclaredButNotUsed = "XDeclaredButNotUsed", + hintXCannotRaiseY = "XCannotRaiseY", hintConvToBaseNotNeeded = "ConvToBaseNotNeeded", + hintConvFromXtoItselfNotNeeded = "ConvFromXtoItselfNotNeeded", hintExprAlwaysX = "ExprAlwaysX", + hintQuitCalled = "QuitCalled", hintProcessing = "Processing", hintCodeBegin = "CodeBegin", + hintCodeEnd = "CodeEnd", hintConf = "Conf", hintPath = "Path", + hintConditionAlwaysTrue = "CondTrue", hintConditionAlwaysFalse = "CondFalse", hintName = "Name", + hintPattern = "Pattern", hintExecuting = "Exec", hintLinking = "Link", hintDependency = "Dependency", + hintSource = "Source", hintPerformance = "Performance", hintStackTrace = "StackTrace", + hintGCStats = "GCStats", hintGlobalVar = "GlobalVar", hintExpandMacro = "ExpandMacro", + hintUser = "User", hintUserRaw = "UserRaw", hintExtendedContext = "ExtendedContext", + hintMsgOrigin = "MsgOrigin", # since 1.3.5 + hintDeclaredLoc = "DeclaredLoc", # since 1.5.1 const MsgKindToStr*: array[TMsgKind, string] = [ @@ -163,34 +163,6 @@ const hintDeclaredLoc: "$1", ] -const - WarningsToStr* = ["CannotOpenFile", "OctalEscape", - "XIsNeverRead", "XmightNotBeenInit", - "Deprecated", "ConfigDeprecated", - "SmallLshouldNotBeUsed", "UnknownMagic", - "RedefinitionOfLabel", "UnknownSubstitutionX", - "LanguageXNotSupported", "FieldXNotSupported", - "CommentXIgnored", - "TypelessParam", "UseBase", "WriteToForeignHeap", - "UnsafeCode", "UnusedImport", "InheritFromException", - "EachIdentIsTuple", - "UnsafeSetLen", "UnsafeDefault", - "ProveInit", "ProveField", "ProveIndex", "UnreachableElse", "UnreachableCode", - "IndexCheck", "GcUnsafe", "GcUnsafe2", "Uninit", - "GcMem", "Destructor", "LockLevel", "ResultShadowed", - "Spacing", "CaseTransition", "CycleCreated", - "ObservableStores", "User"] - - HintsToStr* = [ - "Success", "SuccessX", "CC", "LineTooLong", - "XDeclaredButNotUsed", "XCannotRaiseY", - "ConvToBaseNotNeeded", "ConvFromXtoItselfNotNeeded", - "ExprAlwaysX", "QuitCalled", "Processing", "CodeBegin", "CodeEnd", "Conf", - "Path", "CondTrue", "CondFalse", "Name", "Pattern", "Exec", "Link", "Dependency", - "Source", "Performance", "StackTrace", "GCStats", "GlobalVar", "ExpandMacro", - "User", "UserRaw", "ExtendedContext", "MsgOrigin", "DeclaredLoc" - ] - const fatalMin* = errUnknown fatalMax* = errInternal @@ -201,16 +173,6 @@ const hintMin* = hintSuccess hintMax* = high(TMsgKind) -proc msgToStr*(msg: TMsgKind): string = - case msg - of warnMin..warnMax: WarningsToStr[ord(msg) - ord(warnMin)] - of hintMin..hintMax: HintsToStr[ord(msg) - ord(hintMin)] - else: "" # we could at least do $msg - prefix `err` - -static: - doAssert HintsToStr.len == ord(hintMax) - ord(hintMin) + 1 - doAssert WarningsToStr.len == ord(warnMax) - ord(warnMin) + 1 - type TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints TNoteKinds* = set[TNoteKind] diff --git a/compiler/linter.nim b/compiler/linter.nim index a229958e6..77e0a84e4 100644 --- a/compiler/linter.nim +++ b/compiler/linter.nim @@ -128,6 +128,6 @@ proc styleCheckUse*(conf: ConfigRef; info: TLineInfo; s: PSym) = lintReport(conf, info, newName, oldName) proc checkPragmaUse*(conf: ConfigRef; info: TLineInfo; w: TSpecialWord; pragmaName: string) = - let wanted = specialWords[w] + let wanted = $w if pragmaName != wanted: lintReport(conf, info, wanted, pragmaName) diff --git a/compiler/main.nim b/compiler/main.nim index 210933231..af9102414 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -303,10 +303,10 @@ proc mainCommand*(graph: ModuleGraph) = var hints = newJObject() # consider factoring with `listHints` for a in hintMin..hintMax: - hints[a.msgToStr] = %(a in conf.notes) + hints[$a] = %(a in conf.notes) var warnings = newJObject() for a in warnMin..warnMax: - warnings[a.msgToStr] = %(a in conf.notes) + warnings[$a] = %(a in conf.notes) var dumpdata = %[ (key: "version", val: %VersionAsString), diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 7e2b8e8a6..0f5879ff6 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -489,7 +489,7 @@ proc liMessage*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string, color: ForegroundColor ignoreMsg = false sev: Severity - let kind = if msg != hintUserRaw: msg.msgToStr else: "" # xxx not sure why hintUserRaw is special + let kind = if msg in warnMin..hintMax and msg != hintUserRaw: $msg else: "" # xxx not sure why hintUserRaw is special case msg of errMin..errMax: sev = Severity.Error @@ -536,7 +536,7 @@ proc liMessage*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string, if hintMsgOrigin in conf.mainPackageNotes: styledMsgWriteln(styleBright, toFileLineCol(info2), resetStyle, " compiler msg initiated here", KindColor, - KindFormat % hintMsgOrigin.msgToStr, + KindFormat % $hintMsgOrigin, resetStyle) handleError(conf, msg, eh, s) @@ -612,7 +612,7 @@ proc quotedFilename*(conf: ConfigRef; i: TLineInfo): Rope = template listMsg(title, r) = msgWriteln(conf, title) - for a in r: msgWriteln(conf, " [$1] $2" % [if a in conf.notes: "x" else: " ", a.msgToStr]) + for a in r: msgWriteln(conf, " [$1] $2" % [if a in conf.notes: "x" else: " ", $a]) proc listWarnings*(conf: ConfigRef) = listMsg("Warnings:", warnMin..warnMax) proc listHints*(conf: ConfigRef) = listMsg("Hints:", hintMin..hintMax) diff --git a/compiler/parser.nim b/compiler/parser.nim index e3cf54b38..01ec6fe95 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -219,7 +219,7 @@ proc eat(p: var Parser, tokType: TokType) = getTok(p) else: lexMessage(p.lex, errGenerated, - "expected: '" & TokTypeToStr[tokType] & "', but got: '" & prettyTok(p.tok) & "'") + "expected: '" & $tokType & "', but got: '" & prettyTok(p.tok) & "'") proc parLineInfo(p: Parser): TLineInfo = ## Retrieve the line information associated with the parser's current state. diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 273b2b004..c1d54c4a2 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -330,10 +330,10 @@ proc processDynLib(c: PContext, n: PNode, sym: PSym) = sym.typ.callConv = ccCDecl proc processNote(c: PContext, n: PNode) = - template handleNote(toStrArray, msgMin, notes) = - let x = findStr(toStrArray, n[0][1].ident.s) - if x >= 0: - nk = TNoteKind(x + ord(msgMin)) + template handleNote(enumVals, notes) = + let x = findStr(enumVals, n[0][1].ident.s, errUnknown) + if x != errUnknown: + nk = TNoteKind(x) let x = c.semConstBoolExpr(c, n[1]) n[1] = x if x.kind == nkIntLit and x.intVal != 0: incl(notes, nk) @@ -347,9 +347,9 @@ proc processNote(c: PContext, n: PNode) = n[0][1].kind == nkIdent and n[0][0].kind == nkIdent: var nk: TNoteKind case whichKeyword(n[0][0].ident) - of wHint: handleNote(HintsToStr, hintMin, c.config.notes) - of wWarning: handleNote(WarningsToStr, warnMin, c.config.notes) - of wWarningAsError: handleNote(WarningsToStr, warnMin, c.config.warningAsErrors) + of wHint: handleNote(hintMin .. hintMax, c.config.notes) + of wWarning: handleNote(warnMin .. warnMax, c.config.notes) + of wWarningAsError: handleNote(warnMin .. warnMax, c.config.warningAsErrors) else: invalidPragma(c, n) else: invalidPragma(c, n) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index b897aff5d..a4152cf29 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -597,7 +597,7 @@ proc gcommaAux(g: var TSrcGen, n: PNode, ind: int, start: int = 0, gsub(g, n[i]) if c: if g.tokens.len > oldLen: - putWithSpace(g, separator, TokTypeToStr[separator]) + putWithSpace(g, separator, $separator) if hasCom(n[i]): gcoms(g) optNL(g, ind) diff --git a/compiler/types.nim b/compiler/types.nim index 9875f5e1b..42b7de416 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -700,7 +700,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = if i < t.len - 1: result.add(", ") result.add(')') if t.len > 0 and t[0] != nil: result.add(": " & typeToString(t[0])) - var prag = if t.callConv == ccNimCall and tfExplicitCallConv notin t.flags: "" else: CallingConvToStr[t.callConv] + var prag = if t.callConv == ccNimCall and tfExplicitCallConv notin t.flags: "" else: $t.callConv if tfNoSideEffect in t.flags: addSep(prag) prag.add("noSideEffect") diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index f6fc92dc8..e2e3a1d2f 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -15,78 +15,99 @@ from strutils import cmpIgnoreStyle -# Keywords must be kept sorted and within a range - type TSpecialWord* = enum - wInvalid, + wInvalid = "", + wAddr = "addr", wAnd = "and", wAs = "as", wAsm = "asm", + wBind = "bind", wBlock = "block", wBreak = "break", wCase = "case", wCast = "cast", + wConcept = "concept", wConst = "const", wContinue = "continue", wConverter = "converter", + wDefer = "defer", wDiscard = "discard", wDistinct = "distinct", wDiv = "div", wDo = "do", + wElif = "elif", wElse = "else", wEnd = "end", wEnum = "enum", wExcept = "except", + wExport = "export", wFinally = "finally", wFor = "for", wFrom = "from", wFunc = "func", + wIf = "if", wImport = "import", wIn = "in", wInclude = "include", wInterface = "interface", + wIs = "is", wIsnot = "isnot", wIterator = "iterator", wLet = "let", wMacro = "macro", + wMethod = "method", wMixin = "mixin", wMod = "mod", wNil = "nil", wNot = "not", wNotin = "notin", + wObject = "object", wOf = "of", wOr = "or", wOut = "out", wProc = "proc", wPtr = "ptr", + wRaise = "raise", wRef = "ref", wReturn = "return", wShl = "shl", wShr = "shr", wStatic = "static", + wTemplate = "template", wTry = "try", wTuple = "tuple", wType = "type", wUsing = "using", + wVar = "var", wWhen = "when", wWhile = "while", wXor = "xor", wYield = "yield", - wAddr, wAnd, wAs, wAsm, - wBind, wBlock, wBreak, wCase, wCast, wConcept, wConst, - wContinue, wConverter, wDefer, wDiscard, wDistinct, wDiv, wDo, - wElif, wElse, wEnd, wEnum, wExcept, wExport, - wFinally, wFor, wFrom, wFunc, wIf, wImport, wIn, - wInclude, wInterface, wIs, wIsnot, wIterator, wLet, - wMacro, wMethod, wMixin, wMod, wNil, - wNot, wNotin, wObject, wOf, wOr, wOut, wProc, wPtr, wRaise, wRef, wReturn, - wShl, wShr, wStatic, wTemplate, wTry, wTuple, wType, wUsing, wVar, - wWhen, wWhile, wXor, wYield, + wColon = ":", wColonColon = "::", wEquals = "=", wDot = ".", wDotDot = "..", + wStar = "*", wMinus = "-", + wMagic = "magic", wThread = "thread", wFinal = "final", wProfiler = "profiler", + wMemTracker = "memtracker", wObjChecks = "objchecks", + wIntDefine = "intdefine", wStrDefine = "strdefine", wBoolDefine = "booldefine", + wCursor = "cursor", wNoalias = "noalias", - wColon, wColonColon, wEquals, wDot, wDotDot, - wStar, wMinus, - wMagic, wThread, wFinal, wProfiler, wMemTracker, wObjChecks, - wIntDefine, wStrDefine, wBoolDefine, wCursor, wNoalias, + wImmediate = "immediate", wConstructor = "constructor", wDestructor = "destructor", + wDelegator = "delegator", wOverride = "override", wImportCpp = "importcpp", + wImportObjC = "importobjc", wImportCompilerProc = "importcompilerproc", + wImportc = "importc", wImportJs = "importjs", wExportc = "exportc", wExportCpp = "exportcpp", + wExportNims = "exportnims", + wIncompleteStruct = "incompleteStruct", # deprecated + wCompleteStruct = "completeStruct", wRequiresInit = "requiresInit", wAlign = "align", + wNodecl = "nodecl", wPure = "pure", wSideEffect = "sideEffect", wHeader = "header", + wNoSideEffect = "noSideEffect", wGcSafe = "gcsafe", wNoreturn = "noreturn", + wNosinks = "nosinks", wMerge = "merge", wLib = "lib", wDynlib = "dynlib", + wCompilerProc = "compilerproc", wCore = "core", wProcVar = "procvar", + wBase = "base", wUsed = "used", wFatal = "fatal", wError = "error", wWarning = "warning", + wHint = "hint", wWarningAsError = "warningAsError", wLine = "line", wPush = "push", + wPop = "pop", wDefine = "define", wUndef = "undef", wLineDir = "lineDir", + wStackTrace = "stackTrace", wLineTrace = "lineTrace", wLink = "link", wCompile = "compile", + wLinksys = "linksys", wDeprecated = "deprecated", wVarargs = "varargs", wCallconv = "callconv", + wDebugger = "debugger", wNimcall = "nimcall", wStdcall = "stdcall", wCdecl = "cdecl", + wSafecall = "safecall", wSyscall = "syscall", wInline = "inline", wNoInline = "noinline", + wFastcall = "fastcall", wThiscall = "thiscall", wClosure = "closure", wNoconv = "noconv", + wOn = "on", wOff = "off", wChecks = "checks", wRangeChecks = "rangeChecks", + wBoundChecks = "boundChecks", wOverflowChecks = "overflowChecks", wNilChecks = "nilChecks", + wFloatChecks = "floatChecks", wNanChecks = "nanChecks", wInfChecks = "infChecks", + wStyleChecks = "styleChecks", wStaticBoundchecks = "staticBoundChecks", + wNonReloadable = "nonReloadable", wExecuteOnReload = "executeOnReload", - wImmediate, wConstructor, wDestructor, wDelegator, wOverride, - wImportCpp, wImportObjC, - wImportCompilerProc, - wImportc, wImportJs, wExportc, wExportCpp, wExportNims, - wIncompleteStruct, # deprecated - wCompleteStruct, - wRequiresInit, - wAlign, wNodecl, wPure, wSideEffect, wHeader, - wNoSideEffect, wGcSafe, wNoreturn, wNosinks, wMerge, wLib, wDynlib, - wCompilerProc, wCore, wProcVar, wBase, wUsed, - wFatal, wError, wWarning, wHint, wWarningAsError, wLine, wPush, wPop, wDefine, wUndef, - wLineDir, wStackTrace, wLineTrace, wLink, wCompile, - wLinksys, wDeprecated, wVarargs, wCallconv, wDebugger, - wNimcall, wStdcall, wCdecl, wSafecall, wSyscall, wInline, wNoInline, - wFastcall, wThiscall, wClosure, wNoconv, wOn, wOff, wChecks, wRangeChecks, - wBoundChecks, wOverflowChecks, wNilChecks, - wFloatChecks, wNanChecks, wInfChecks, wStyleChecks, wStaticBoundchecks, - wNonReloadable, wExecuteOnReload, - wAssertions, wPatterns, wTrMacros, wSinkInference, wWarnings, - wHints, wOptimization, wRaises, wWrites, wReads, wSize, wEffects, wTags, - wRequires, wEnsures, wInvariant, wAssume, wAssert, - wDeadCodeElimUnused, # deprecated, dead code elim always happens - wSafecode, wPackage, wNoForward, wReorder, wNoRewrite, wNoDestroy, - wPragma, - wCompileTime, wNoInit, - wPassc, wPassl, wLocalPassc, wBorrow, wDiscardable, - wFieldChecks, - wSubsChar, wAcyclic, wShallow, wUnroll, wLinearScanEnd, wComputedGoto, - wInjectStmt, wExperimental, - wWrite, wGensym, wInject, wDirty, wInheritable, wThreadVar, wEmit, - wAsmNoStackFrame, - wImplicitStatic, wGlobal, wCodegenDecl, wUnchecked, wGuard, wLocks, - wPartial, wExplain, wLiftLocals, + wAssertions = "assertions", wPatterns = "patterns", wTrMacros = "trmacros", + wSinkInference = "sinkInference", wWarnings = "warnings", + wHints = "hints", wOptimization = "optimization", wRaises = "raises", + wWrites = "writes", wReads = "reads", wSize = "size", wEffects = "effects", wTags = "tags", + wRequires = "requires", wEnsures = "ensures", wInvariant = "invariant", + wAssume = "assume", wAssert = "assert", + wDeadCodeElimUnused = "deadCodeElim", # deprecated, dead code elim always happens + wSafecode = "safecode", wPackage = "package", wNoForward = "noforward", wReorder = "reorder", + wNoRewrite = "norewrite", wNoDestroy = "nodestroy", wPragma = "pragma", + wCompileTime = "compileTime", wNoInit = "noinit", wPassc = "passc", wPassl = "passl", + wLocalPassc = "localPassC", wBorrow = "borrow", wDiscardable = "discardable", + wFieldChecks = "fieldChecks", wSubsChar = "subschar", wAcyclic = "acyclic", + wShallow = "shallow", wUnroll = "unroll", wLinearScanEnd = "linearScanEnd", + wComputedGoto = "computedGoto", wInjectStmt = "injectStmt", wExperimental = "experimental", + wWrite = "write", wGensym = "gensym", wInject = "inject", wDirty = "dirty", + wInheritable = "inheritable", wThreadVar = "threadvar", wEmit = "emit", + wAsmNoStackFrame = "asmNoStackFrame", wImplicitStatic = "implicitStatic", + wGlobal = "global", wCodegenDecl = "codegenDecl", wUnchecked = "unchecked", + wGuard = "guard", wLocks = "locks", wPartial = "partial", wExplain = "explain", + wLiftLocals = "liftlocals", - wAuto, wBool, wCatch, wChar, wClass, wCompl - wConst_cast, wDefault, wDelete, wDouble, wDynamic_cast, - wExplicit, wExtern, wFalse, wFloat, wFriend, - wGoto, wInt, wLong, wMutable, wNamespace, wNew, wOperator, - wPrivate, wProtected, wPublic, wRegister, wReinterpret_cast, wRestrict, - wShort, wSigned, wSizeof, wStatic_cast, wStruct, wSwitch, - wThis, wThrow, wTrue, wTypedef, wTypeid, wTypeof, wTypename, - wUnion, wPacked, wUnsigned, wVirtual, wVoid, wVolatile, wWchar_t, + wAuto = "auto", wBool = "bool", wCatch = "catch", wChar = "char", + wClass = "class", wCompl = "compl", wConst_cast = "const_cast", wDefault = "default", + wDelete = "delete", wDouble = "double", wDynamic_cast = "dynamic_cast", + wExplicit = "explicit", wExtern = "extern", wFalse = "false", wFloat = "float", + wFriend = "friend", wGoto = "goto", wInt = "int", wLong = "long", wMutable = "mutable", + wNamespace = "namespace", wNew = "new", wOperator = "operator", wPrivate = "private", + wProtected = "protected", wPublic = "public", wRegister = "register", + wReinterpret_cast = "reinterpret_cast", wRestrict = "restrict", wShort = "short", + wSigned = "signed", wSizeof = "sizeof", wStatic_cast = "static_cast", wStruct = "struct", + wSwitch = "switch", wThis = "this", wThrow = "throw", wTrue = "true", wTypedef = "typedef", + wTypeid = "typeid", wTypeof = "typeof", wTypename = "typename", + wUnion = "union", wPacked = "packed", wUnsigned = "unsigned", wVirtual = "virtual", + wVoid = "void", wVolatile = "volatile", wWchar_t = "wchar_t", - wAlignas, wAlignof, wConstexpr, wDecltype, wNullptr, wNoexcept, - wThread_local, wStatic_assert, wChar16_t, wChar32_t, + wAlignas = "alignas", wAlignof = "alignof", wConstexpr = "constexpr", wDecltype = "decltype", + wNullptr = "nullptr", wNoexcept = "noexcept", + wThread_local = "thread_local", wStatic_assert = "static_assert", + wChar16_t = "char16_t", wChar32_t = "char32_t", - wStdIn, wStdOut, wStdErr, + wStdIn = "stdin", wStdOut = "stdout", wStdErr = "stderr", - wInOut, wByCopy, wByRef, wOneWay, - wBitsize + wInOut = "inout", wByCopy = "bycopy", wByRef = "byref", wOneWay = "oneway", + wBitsize = "bitsize" TSpecialWords* = set[TSpecialWord] @@ -104,82 +125,8 @@ const wAsm, wBreak, wCase, wConst, wContinue, wDo, wElse, wEnum, wExport, wFor, wIf, wReturn, wStatic, wTemplate, wTry, wWhile, wUsing} - specialWords*: array[TSpecialWord, string] = ["", - "addr", "and", "as", "asm", - "bind", "block", "break", "case", "cast", - "concept", "const", "continue", "converter", - "defer", "discard", "distinct", "div", "do", - "elif", "else", "end", "enum", "except", "export", - "finally", "for", "from", "func", "if", - "import", "in", "include", "interface", "is", "isnot", "iterator", - "let", - "macro", "method", "mixin", "mod", "nil", "not", "notin", - "object", "of", "or", - "out", "proc", "ptr", "raise", "ref", "return", - "shl", "shr", "static", - "template", "try", "tuple", "type", "using", "var", - "when", "while", "xor", - "yield", - - ":", "::", "=", ".", "..", - "*", "-", - "magic", "thread", "final", "profiler", "memtracker", "objchecks", - "intdefine", "strdefine", "booldefine", "cursor", "noalias", - - "immediate", "constructor", "destructor", "delegator", "override", - "importcpp", "importobjc", - "importCompilerProc", "importc", "importjs", "exportc", "exportcpp", "exportnims", - "incompleteStruct", - "completeStruct", - "requiresInit", "align", "nodecl", "pure", "sideEffect", - "header", "noSideEffect", "gcsafe", "noreturn", "nosinks", "merge", "lib", "dynlib", - "compilerproc", "core", "procvar", "base", "used", - "fatal", "error", "warning", "hint", "warningAsError", "line", - "push", "pop", "define", "undef", "lineDir", "stackTrace", "lineTrace", - "link", "compile", "linksys", "deprecated", "varargs", - "callconv", "debugger", "nimcall", "stdcall", - "cdecl", "safecall", "syscall", "inline", "noinline", "fastcall", "thiscall", "closure", - "noconv", "on", "off", "checks", "rangeChecks", "boundChecks", - "overflowChecks", "nilChecks", - "floatChecks", "nanChecks", "infChecks", "styleChecks", "staticBoundChecks", - "nonReloadable", "executeOnReload", - - "assertions", "patterns", "trmacros", "sinkinference", "warnings", "hints", - "optimization", "raises", "writes", "reads", "size", "effects", "tags", - "requires", "ensures", "invariant", "assume", "assert", - "deadCodeElim", # deprecated, dead code elim always happens - "safecode", "package", "noforward", "reorder", "norewrite", "nodestroy", - "pragma", - "compileTime", "noinit", - "passc", "passl", "localPassC", "borrow", "discardable", "fieldChecks", - "subschar", "acyclic", "shallow", "unroll", "linearScanEnd", - "computedGoto", "injectStmt", "experimental", - "write", "gensym", "inject", "dirty", "inheritable", "threadvar", "emit", - "asmNoStackFrame", "implicitStatic", "global", "codegenDecl", "unchecked", - "guard", "locks", "partial", "explain", "liftLocals", - - "auto", "bool", "catch", "char", "class", "compl", - "const_cast", "default", "delete", "double", - "dynamic_cast", "explicit", "extern", "false", - "float", "friend", "goto", "int", "long", "mutable", - "namespace", "new", "operator", - "private", "protected", "public", "register", "reinterpret_cast", "restrict", - "short", "signed", "sizeof", "static_cast", "struct", "switch", - "this", "throw", "true", "typedef", "typeid", "typeof", - "typename", "union", "packed", "unsigned", "virtual", "void", "volatile", - "wchar_t", - - "alignas", "alignof", "constexpr", "decltype", "nullptr", "noexcept", - "thread_local", "static_assert", "char16_t", "char32_t", - - "stdin", "stdout", "stderr", - - "inout", "bycopy", "byref", "oneway", - "bitsize" - ] - -proc findStr*(a: openArray[string], s: string): int = - for i in low(a)..high(a): - if cmpIgnoreStyle(a[i], s) == 0: +proc findStr*[T:enum](a: Slice[T], s: string, default: T): T = + for i in a: + if cmpIgnoreStyle($i, s) == 0: return i - result = - 1 + result = default \ No newline at end of file From 544cb107c75523196ceee9408f9b1d5d89c82e48 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 2 Nov 2020 17:41:44 +0800 Subject: [PATCH 0890/3768] fix #8821 (#15809) --- compiler/jsgen.nim | 5 +++++ tests/js/t8821.nim | 12 ++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 tests/js/t8821.nim diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index cd14f1cf9..10c072253 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -865,6 +865,7 @@ proc genRaiseStmt(p: PProc, n: PNode) = proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) = var cond, stmt: TCompRes + totalRange = 0 genLineDir(p, n) gen(p, n[0], cond) let stringSwitch = skipTypes(n[0].typ, abstractVar).kind == tyString @@ -884,6 +885,10 @@ proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) = let e = it[j] if e.kind == nkRange: var v = copyNode(e[0]) + inc(totalRange, int(e[1].intVal - v.intVal)) + if totalRange > 65535: + localError(p.config, n.info, + "Your case statement contains too many branches, consider using if/else instead!") while v.intVal <= e[1].intVal: gen(p, v, cond) lineF(p, "case $1:$n", [cond.rdLoc]) diff --git a/tests/js/t8821.nim b/tests/js/t8821.nim new file mode 100644 index 000000000..43cf3f6f2 --- /dev/null +++ b/tests/js/t8821.nim @@ -0,0 +1,12 @@ +discard """ + errormsg: "Your case statement contains too many branches, consider using if/else instead!" +""" + +proc isInt32(i: int): bool = + case i + of 1 .. 70000: + return true + else: + return false + +discard isInt32(1) \ No newline at end of file From 5b4c17b5e7166220c7b5c149946579f245f54645 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 2 Nov 2020 17:58:14 +0800 Subject: [PATCH 0891/3768] Closure iterators are not supported by VM (#15818) --- compiler/vmgen.nim | 2 ++ tests/vm/tclosureiterator.nim | 9 +++++++++ 2 files changed, 11 insertions(+) create mode 100644 tests/vm/tclosureiterator.nim diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 0c0c491e0..61c7fefd9 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1982,6 +1982,8 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = genRdVar(c, n, dest, flags) of skProc, skFunc, skConverter, skMacro, skTemplate, skMethod, skIterator: # 'skTemplate' is only allowed for 'getAst' support: + if s.kind == skIterator and s.typ.callConv == TCallingConvention.ccClosure: + globalError(c.config, n.info, "Closure iterators are not supported by VM!") if procIsCallback(c, s): discard elif importcCond(s): c.importcSym(n.info, s) genLit(c, n, dest) diff --git a/tests/vm/tclosureiterator.nim b/tests/vm/tclosureiterator.nim new file mode 100644 index 000000000..c909392d5 --- /dev/null +++ b/tests/vm/tclosureiterator.nim @@ -0,0 +1,9 @@ +discard """ + errormsg: "Closure iterators are not supported by VM!" +""" + +iterator iter*(): int {.closure.} = + yield 3 + +static: + var x = iter From 4fe56b56ce2b856ff8fae9051cceb766ab7729e0 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 2 Nov 2020 20:01:19 +0800 Subject: [PATCH 0892/3768] more clear (#15812) --- lib/pure/asynchttpserver.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index afd452b01..ec7f2a0de 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -11,8 +11,8 @@ ## ## This HTTP server has not been designed to be used in production, but ## for testing applications locally. Because of this, when deploying your -## application you should use a reverse proxy (for example nginx) instead of -## allowing users to connect directly to this server. +## application in production you should use a reverse proxy (for example nginx) +## instead of allowing users to connect directly to this server. ## ## Basic usage ## =========== From 6fa82a5b3afbe644eef3fb41647a341d8e9b21c4 Mon Sep 17 00:00:00 2001 From: Regis Caillaud <35006197+Clonkk@users.noreply.github.com> Date: Mon, 2 Nov 2020 13:02:55 +0100 Subject: [PATCH 0893/3768] Handle BLOB column type in SQLite as binary data (#15681) * Fixed not handling blob correctly in sqlite * Fixed setLen commented by mistake * Added binary example as db_sqlite doc * Added tests for sqlite binary data --- lib/impure/db_sqlite.nim | 93 ++++++++++++++++++++++++++------ tests/stdlib/tsqlitebindatas.nim | 50 +++++++++++++++++ 2 files changed, 128 insertions(+), 15 deletions(-) create mode 100644 tests/stdlib/tsqlitebindatas.nim diff --git a/lib/impure/db_sqlite.nim b/lib/impure/db_sqlite.nim index 80eee59fb..ce57996be 100644 --- a/lib/impure/db_sqlite.nim +++ b/lib/impure/db_sqlite.nim @@ -94,6 +94,57 @@ ## ## db.close() ## +## Storing binary data example +##---------------------------- +## +## .. code-block:: nim +## +## import random +## +## ## Generate random float datas +## var orig = newSeq[float64](150) +## randomize() +## for x in orig.mitems: +## x = rand(1.0)/10.0 +## +## let db = open("mysqlite.db", "", "", "") +## block: ## Create database +## ## Binary datas needs to be of type BLOB in SQLite +## let createTableStr = sql"""CREATE TABLE test( +## id INTEGER NOT NULL PRIMARY KEY, +## data BLOB +## ) +## """ +## db.exec(createTableStr) +## +## block: ## Insert data +## var id = 1 +## ## Data needs to be converted to seq[byte] to be interpreted as binary by bindParams +## var dbuf = newSeq[byte](orig.len*sizeof(float64)) +## copyMem(unsafeAddr(dbuf[0]), unsafeAddr(orig[0]), dbuf.len) +## +## ## Use prepared statement to insert binary data into database +## var insertStmt = db.prepare("INSERT INTO test (id, data) VALUES (?, ?)") +## insertStmt.bindParams(id, dbuf) +## let bres = db.tryExec(insertStmt) +## ## Check insert +## doAssert(bres) +## # Destroy statement +## finalize(insertStmt) +## +## block: ## Use getValue to select data +## var dataTest = db.getValue(sql"SELECT data FROM test WHERE id = ?", 1) +## ## Calculate sequence size from buffer size +## let seqSize = int(dataTest.len*sizeof(byte)/sizeof(float64)) +## ## Copy binary string data in dataTest into a seq +## var res: seq[float64] = newSeq[float64](seqSize) +## copyMem(unsafeAddr(res[0]), addr(dataTest[0]), dataTest.len) +## +## ## Check datas obtained is identical +## doAssert res == orig +## +## db.close() +## ## ## Note ## ==== @@ -242,10 +293,14 @@ proc setupQuery(db: DbConn, stmtName: SqlPrepared): SqlPrepared {.since: (1, 3). proc setRow(stmt: PStmt, r: var Row, cols: cint) = for col in 0'i32..cols-1: - setLen(r[col], column_bytes(stmt, col)) # set capacity - setLen(r[col], 0) - let x = column_text(stmt, col) - if not isNil(x): add(r[col], x) + let cb = column_bytes(stmt, col) + setLen(r[col], cb) # set capacity + if column_type(stmt, col) == SQLITE_BLOB: + copyMem(addr(r[col][0]), column_blob(stmt, col), cb) + else: + setLen(r[col], 0) + let x = column_text(stmt, col) + if not isNil(x): add(r[col], x) iterator fastRows*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} = @@ -288,7 +343,7 @@ iterator fastRows*(db: DbConn, query: SqlQuery, finally: if finalize(stmt) != SQLITE_OK: dbError(db) -iterator fastRows*(db: DbConn, stmtName: SqlPrepared): Row +iterator fastRows*(db: DbConn, stmtName: SqlPrepared): Row {.tags: [ReadDbEffect,WriteDbEffect], since: (1, 3).} = discard setupQuery(db, stmtName) var L = (column_count(stmtName.PStmt)) @@ -491,7 +546,7 @@ proc getAllRows*(db: DbConn, query: SqlQuery, for r in fastRows(db, query, args): result.add(r) -proc getAllRows*(db: DbConn, stmtName: SqlPrepared): seq[Row] +proc getAllRows*(db: DbConn, stmtName: SqlPrepared): seq[Row] {.tags: [ReadDbEffect,WriteDbEffect], since: (1, 3).} = result = @[] for r in fastRows(db, stmtName): @@ -524,7 +579,7 @@ iterator rows*(db: DbConn, query: SqlQuery, ## db.close() for r in fastRows(db, query, args): yield r -iterator rows*(db: DbConn, stmtName: SqlPrepared): Row +iterator rows*(db: DbConn, stmtName: SqlPrepared): Row {.tags: [ReadDbEffect,WriteDbEffect], since: (1, 3).} = for r in fastRows(db, stmtName): yield r @@ -558,13 +613,17 @@ proc getValue*(db: DbConn, query: SqlQuery, if cb == 0: result = "" else: - result = newStringOfCap(cb) - add(result, column_text(stmt, 0)) + if column_type(stmt, 0) == SQLITE_BLOB: + result.setLen(cb) + copyMem(addr(result[0]), column_blob(stmt, 0), cb) + else: + result = newStringOfCap(cb) + add(result, column_text(stmt, 0)) else: result = "" if finalize(stmt) != SQLITE_OK: dbError(db) -proc getValue*(db: DbConn, stmtName: SqlPrepared): string +proc getValue*(db: DbConn, stmtName: SqlPrepared): string {.tags: [ReadDbEffect,WriteDbEffect], since: (1, 3).} = var stmt = setupQuery(db, stmtName).PStmt if step(stmt) == SQLITE_ROW: @@ -572,8 +631,12 @@ proc getValue*(db: DbConn, stmtName: SqlPrepared): string if cb == 0: result = "" else: - result = newStringOfCap(cb) - add(result, column_text(stmt, 0)) + if column_type(stmt, 0) == SQLITE_BLOB: + result.setLen(cb) + copyMem(addr(result[0]), column_blob(stmt, 0), cb) + else: + result = newStringOfCap(cb) + add(result, column_text(stmt, 0)) else: result = "" @@ -671,7 +734,7 @@ proc execAffectedRows*(db: DbConn, query: SqlQuery, exec(db, query, args) result = changes(db) -proc execAffectedRows*(db: DbConn, stmtName: SqlPrepared): int64 +proc execAffectedRows*(db: DbConn, stmtName: SqlPrepared): int64 {.tags: [ReadDbEffect, WriteDbEffect],since: (1, 3).} = exec(db, stmtName) result = changes(db) @@ -723,7 +786,7 @@ proc setEncoding*(connection: DbConn, encoding: string): bool {. exec(connection, sql"PRAGMA encoding = ?", [encoding]) result = connection.getValue(sql"PRAGMA encoding") == encoding -proc finalize*(sqlPrepared:SqlPrepared) {.discardable, since: (1, 3).} = +proc finalize*(sqlPrepared:SqlPrepared) {.discardable, since: (1, 3).} = discard finalize(sqlPrepared.PStmt) template dbBindParamError*(paramIdx: int, val: varargs[untyped]) = @@ -756,7 +819,7 @@ proc bindParam*(ps: SqlPrepared, paramIdx: int, val: float64) {.since: (1, 3).} dbBindParamError(paramIdx, val) proc bindNull*(ps: SqlPrepared, paramIdx: int) {.since: (1, 3).} = - ## Sets the bindparam at the specified paramIndex to null + ## Sets the bindparam at the specified paramIndex to null ## (default behaviour by sqlite). if bind_null(ps.PStmt, paramIdx.int32) != SQLITE_OK: dbBindParamError(paramIdx) diff --git a/tests/stdlib/tsqlitebindatas.nim b/tests/stdlib/tsqlitebindatas.nim new file mode 100644 index 000000000..643f1e2e6 --- /dev/null +++ b/tests/stdlib/tsqlitebindatas.nim @@ -0,0 +1,50 @@ +discard """ + action: "run" + exitcode: 0 +""" +import db_sqlite +import random +import os +from stdtest/specialpaths import buildDir + +block tsqlitebindatas: ## db_sqlite binary data + const dbName = buildDir / "tsqlitebindatas.db" + + let origName = "Bobby" + var orig = newSeq[float64](150) + randomize() + for x in orig.mitems: + x = rand(1.0)/10.0 + + discard tryRemoveFile(dbName) + let db = open(dbName, "", "", "") + let createTableStr = sql"""CREATE TABLE test( + id INTEGER NOT NULL PRIMARY KEY, + name TEXT, + data BLOB + ) + """ + db.exec(createTableStr) + + var dbuf = newSeq[byte](orig.len*sizeof(float64)) + copyMem(unsafeAddr(dbuf[0]), unsafeAddr(orig[0]), dbuf.len) + + var insertStmt = db.prepare("INSERT INTO test (id, name, data) VALUES (?, ?, ?)") + insertStmt.bindParams(1, origName, dbuf) + let bres = db.tryExec(insertStmt) + doAssert(bres) + + finalize(insertStmt) + + var nameTest = db.getValue(sql"SELECT name FROM test WHERE id = ?", 1) + doAssert nameTest == origName + + var dataTest = db.getValue(sql"SELECT data FROM test WHERE id = ?", 1) + let seqSize = int(dataTest.len*sizeof(byte)/sizeof(float64)) + var res: seq[float64] = newSeq[float64](seqSize) + copyMem(unsafeAddr(res[0]), addr(dataTest[0]), dataTest.len) + doAssert res.len == orig.len + doAssert res == orig + + db.close() + doAssert tryRemoveFile(dbName) From eb429988cd15555f2af35389d52256b06e00e900 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 2 Nov 2020 21:16:33 +0800 Subject: [PATCH 0894/3768] fixes #15594 (#15819) --- lib/pure/sugar.nim | 2 ++ tests/closure/t15594.nim | 10 ++++++++++ 2 files changed, 12 insertions(+) create mode 100644 tests/closure/t15594.nim diff --git a/lib/pure/sugar.nim b/lib/pure/sugar.nim index 92e9f662a..dd44c731d 100644 --- a/lib/pure/sugar.nim +++ b/lib/pure/sugar.nim @@ -207,6 +207,8 @@ macro capture*(locals: varargs[typed], body: untyped): untyped {.since: (1, 1).} let locals = if locals.len == 1 and locals[0].kind == nnkBracket: locals[0] else: locals for arg in locals: + if arg.strVal == "result": + error("The variable name cannot be `result`!", arg) params.add(newIdentDefs(ident(arg.strVal), freshIdentNodes getTypeInst arg)) result = newNimNode(nnkCall) result.add(newProc(newEmptyNode(), params, body, nnkProcDef)) diff --git a/tests/closure/t15594.nim b/tests/closure/t15594.nim new file mode 100644 index 000000000..aacd9ed84 --- /dev/null +++ b/tests/closure/t15594.nim @@ -0,0 +1,10 @@ +discard """ + errormsg: "The variable name cannot be `result`!" +""" + +import sugar + +proc begin(): int = + capture result: + echo 1+1 + result From dfd8a83f3557164fd41714c11c6f92b65eef9018 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 2 Nov 2020 14:17:09 +0100 Subject: [PATCH 0895/3768] fixes #15804 (#15820) * fixes #15804 * fix the existing test * add the testcase for #15804 Co-authored-by: narimiran --- lib/pure/asyncmacro.nim | 21 +++++++++------------ tests/async/t15804.nim | 15 +++++++++++++++ tests/async/tasync_noasync.nim | 6 +++--- 3 files changed, 27 insertions(+), 15 deletions(-) create mode 100644 tests/async/t15804.nim diff --git a/lib/pure/asyncmacro.nim b/lib/pure/asyncmacro.nim index 65cf2f3b9..579e9fede 100644 --- a/lib/pure/asyncmacro.nim +++ b/lib/pure/asyncmacro.nim @@ -129,6 +129,15 @@ proc verifyReturnType(typeName: string, node: NimNode = nil) {.compileTime.} = error("Expected return type of 'Future' got '$1'" % typeName, node) +template await*(f: typed): untyped {.used.} = + static: + error "await expects Future[T], got " & $typeof(f) + +template await*[T](f: Future[T]): auto {.used.} = + var internalTmpFuture: FutureBase = f + yield internalTmpFuture + (cast[typeof(f)](internalTmpFuture)).read() + proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} = ## This macro transforms a single procedure into a closure iterator. ## The ``async`` macro supports a stmtList holding multiple async procedures. @@ -262,20 +271,8 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} = result.params[0] = parseExpr("owned(Future[void])") # based on the yglukhov's patch to chronos: https://github.com/status-im/nim-chronos/pull/47 - # however here the overloads are placed inside each expanded async - var awaitDefinition = quote: - template await(f: typed): untyped {.used.} = - static: - error "await expects Future[T], got " & $typeof(f) - - template await[T](f: Future[T]): auto {.used.} = - var internalTmpFuture: FutureBase = f - yield internalTmpFuture - (cast[type(f)](internalTmpFuture)).read() - if procBody.kind != nnkEmpty: body2.add quote do: - `awaitDefinition` `outerProcBody` result.body = body2 diff --git a/tests/async/t15804.nim b/tests/async/t15804.nim new file mode 100644 index 000000000..8de012196 --- /dev/null +++ b/tests/async/t15804.nim @@ -0,0 +1,15 @@ +import asyncdispatch + +type + Foo*[E] = ref object + op: proc(): Future[bool] {.gcsafe.} + +proc newFoo*[E](): Foo[E] = + result = Foo[E]() + result.op = proc(): Future[bool] {.gcsafe,async.} = + await sleepAsync(100) + result = false + +when isMainModule: + let f = newFoo[int]() + echo waitFor f.op() diff --git a/tests/async/tasync_noasync.nim b/tests/async/tasync_noasync.nim index 812b40da6..54f4f597f 100644 --- a/tests/async/tasync_noasync.nim +++ b/tests/async/tasync_noasync.nim @@ -1,7 +1,7 @@ discard """ - errormsg: "undeclared identifier: 'await'" + errormsg: "'yield' only allowed in an iterator" cmd: "nim c $file" - file: "tasync_noasync.nim" + file: "asyncmacro.nim" """ import async @@ -12,4 +12,4 @@ await a() # if we overload a fallback handler to get # await only available within {.async.} -# we would need `{.dirty.}` templates for await \ No newline at end of file +# we would need `{.dirty.}` templates for await From 235e4930ab2f5a9a4c94d6cc66d62701ee58550d Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 2 Nov 2020 14:48:36 +0100 Subject: [PATCH 0896/3768] simplify toHex (#15821) --- lib/pure/strutils.nim | 31 ++----------------------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 740a6e391..491760137 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -951,11 +951,11 @@ proc toHexImpl(x: BiggestUInt, len: Positive, handleNegative: bool): string {.no # handle negative overflow if n == 0 and handleNegative: n = not(BiggestUInt 0) -proc toHex*(x: BiggestUInt, len: Positive): string {.noSideEffect.} = +proc toHex*[T: SomeInteger](x: T, len: Positive): string {.noSideEffect.} = ## Converts `x` to its hexadecimal representation. ## ## The resulting string will be exactly `len` characters long. No prefix like - ## ``0x`` is generated. + ## ``0x`` is generated. `x` is treated as an unsigned value. runnableExamples: let a = 62'u64 @@ -963,34 +963,7 @@ proc toHex*(x: BiggestUInt, len: Positive): string {.noSideEffect.} = doAssert a.toHex(3) == "03E" doAssert b.toHex(3) == "001" doAssert b.toHex(4) == "1001" - toHexImpl(x, len, false) - -proc toHex*(x: BiggestInt, len: Positive): string {.noSideEffect, - rtl, extern: "nsuToHex".} = - ## Converts `x` to its hexadecimal representation. - ## - ## The resulting string will be exactly `len` characters long. No prefix like - ## ``0x`` is generated. `x` is treated as an unsigned value. - runnableExamples: - let - a = 62 - b = 4097 - c = -8 - doAssert a.toHex(3) == "03E" - doAssert b.toHex(3) == "001" - doAssert b.toHex(4) == "1001" - doAssert c.toHex(6) == "FFFFF8" - toHexImpl(cast[BiggestUInt](x), len, x < 0) - -proc toHex*(x: int, len: Positive): string {.noSideEffect.} = - ## Converts `x` to its hexadecimal representation. - ## - ## The resulting string will be exactly `len` characters long. No prefix like - ## ``0x`` is generated. `x` is treated as an unsigned value. - runnableExamples: doAssert toHex(62, 3) == "03E" - doAssert toHex(4097, 3) == "001" - doAssert toHex(4097, 4) == "1001" doAssert toHex(-8, 6) == "FFFFF8" toHexImpl(cast[BiggestUInt](x), len, x < 0) From c243639979b795d28a69f19ad988726225e22fcd Mon Sep 17 00:00:00 2001 From: Miran Date: Mon, 2 Nov 2020 16:52:54 +0100 Subject: [PATCH 0897/3768] `ioutils` are moved to `fusion` (#15822) --- lib/std/ioutils.nim | 85 --------------------------------------- tests/stdlib/tioutils.nim | 49 ---------------------- 2 files changed, 134 deletions(-) delete mode 100644 lib/std/ioutils.nim delete mode 100644 tests/stdlib/tioutils.nim diff --git a/lib/std/ioutils.nim b/lib/std/ioutils.nim deleted file mode 100644 index bef61c54a..000000000 --- a/lib/std/ioutils.nim +++ /dev/null @@ -1,85 +0,0 @@ -when defined(windows): - proc c_dup(oldfd: FileHandle): FileHandle {. - importc: "_dup", header: "".} - proc c_dup2(oldfd: FileHandle, newfd: FileHandle): cint {. - importc: "_dup2", header: "".} -else: - proc c_dup(oldfd: FileHandle): FileHandle{. - importc: "dup", header: "".} - proc c_dup2(oldfd: FileHandle, newfd: FileHandle): cint {. - importc: "dup2", header: "".} - -# when false: -# const SupportIoctlInheritCtl = (defined(linux) or defined(bsd)) and -# not defined(nimscript) -# when SupportIoctlInheritCtl: -# var -# FIOCLEX {.importc, header: "".}: cint -# FIONCLEX {.importc, header: "".}: cint - -## Also defined in std/posix and system/io -proc strerror(errnum: cint): cstring {.importc, header: "".} -when not defined(nimscript): - var errno {.importc, header: "".}: cint ## error variable - -template checkError(ret: cint) = - if ret == -1: - when not defined(nimscript): - raise newException(IOError, $strerror(errno)) - else: - doAssert(false) - -proc duplicate*(oldfd: FileHandle): FileHandle = - ##[ - Return a copy of the file handle `oldfd`. - After a successful return, both `FileHandle` may be used interchangeably. - They refer to the same open file description and share file offset and status flags. - Calls POSIX function `dup` on Posix platform and `_dup` on Windows - ]## - runnableExamples: - # stdoutDuplicate is a copy of stdout FileHandle that points to STDOUT - let stdoutDuplicate = duplicate(stdout.getFileHandle()) - # Writing to stdoutDuplicate will write to stdout - doAssert(stdoutDuplicate != stdout.getFileHandle()) - # On windows, opening a file from a FileHandle does not work - when not defined(windows): - var f : File - let res = open(f, stdoutDuplicate, mode=fmWrite) - let msg = "This is a test message that will be displayed ! \n" - f.write(msg) - # Output "Test" - f.close() - - result = c_dup(oldfd) - checkError(result) - -proc duplicateTo*(oldfd: FileHandle, newfd: FileHandle) = - ##[ - Perform the same task a `duplicate` but instead of using the lowest unused file descriptor - it uses the FileHandle` specified by `newfd`. - Calls POSIX function `dup2` on Posix platform and `_dup2` on Windows. - ]## - runnableExamples: - import os - # Redirect stdout to a file temporarily - let tmpFileName = getTempDir() / "hidden_output.txt" - let stdoutFileno = stdout.getFileHandle() - let stdoutDupFd = duplicate(stdoutFileno) - - # Create a new file - let tmpFile: File = open(tmpFileName, fmAppend) - let tmpFileFd: FileHandle = tmpFile.getFileHandle() - - # stdoutFileno now writes to tmpFile - duplicateTo(tmpFileFd, stdoutFileno) - echo "This is not displayed, but written to tmpFile instead !" - - # Close file & restore stdout - tmpFile.close() - duplicateTo(stdoutDupFd, stdoutFileno) - - # stdout is now restored ! - echo "This is displayed" - - let retValue = c_dup2(oldfd, newfd) - checkError(retValue) diff --git a/tests/stdlib/tioutils.nim b/tests/stdlib/tioutils.nim deleted file mode 100644 index 6854a7c1c..000000000 --- a/tests/stdlib/tioutils.nim +++ /dev/null @@ -1,49 +0,0 @@ -discard """ - output: ''' -hello1 -hello1 -''' -""" - -import std/ioutils -import os -from stdtest/specialpaths import buildDir - -block: # duplicate, duplicateTo - let tmpFileName = buildDir / "tioutils.txt" - template captureStdout(body) : untyped = - let stdoutFileno = stdout.getFileHandle() - # Duplicate stoudFileno - let stdout_dupfd = duplicate(stdoutFileno) - # Create a new file - # You can use append strategy if you'd like - let tmpFile: File = open(tmpFileName, fmWrite) - # Get the FileHandle (the file descriptor) of your file - let tmpFileFd: FileHandle = tmpFile.getFileHandle() - # dup2 tmpFileFd to stdoutFileno -> writing to stdoutFileno now writes to tmpFile - duplicateTo(tmpFileFd, stdoutFileno) - body - # Force flush - tmpFile.flushFile() - # Close tmp - tmpFile.close() - # Read tmp - let ret = readFile(tmpFileName) - # Restore stdout - duplicateTo(stdout_dupfd, stdoutFileno) - ret - - proc duplicateStdout() = - var msg = "hello" - echo msg & "1" - - let s = captureStdout: - echo msg & "2" - - doAssert s == "hello2\n" - - discard tryRemoveFile(tmpFileName) - duplicateStdout() - # Check it works twice - duplicateStdout() - doAssert tryRemoveFile(tmpFileName) From fde17b159fdc96eda5609fd98e1fe3c7a34ef40b Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Tue, 3 Nov 2020 01:27:39 +0800 Subject: [PATCH 0898/3768] follow #15818 and close #7109 (#15823) * follow #15818 and close #7109 * Update compiler/jsgen.nim Co-authored-by: Juan Carlos Co-authored-by: Juan Carlos --- compiler/jsgen.nim | 7 +++++-- tests/js/t7109.nim | 8 ++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 tests/js/t7109.nim diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 10c072253..d124ae5fe 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -2549,9 +2549,12 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = of nkAsmStmt: genAsmOrEmitStmt(p, n) of nkTryStmt, nkHiddenTryStmt: genTry(p, n, r) of nkRaiseStmt: genRaiseStmt(p, n) - of nkTypeSection, nkCommentStmt, nkIteratorDef, nkIncludeStmt, + of nkTypeSection, nkCommentStmt, nkIncludeStmt, nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt, nkFromStmt, nkTemplateDef, nkMacroDef, nkStaticStmt: discard + of nkIteratorDef: + if n[0].sym.typ.callConv == TCallingConvention.ccClosure: + globalError(p.config, n.info, "Closure iterators are not supported by JS backend!") of nkPragma: genPragma(p, n) of nkProcDef, nkFuncDef, nkMethodDef, nkConverterDef: var s = n[namePos].sym @@ -2559,7 +2562,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = genSym(p, n[namePos], r) r.res = nil of nkGotoState, nkState: - internalError(p.config, n.info, "first class iterators not implemented") + globalError(p.config, n.info, "First class iterators not implemented") of nkPragmaBlock: gen(p, n.lastSon, r) of nkComesFrom: discard "XXX to implement for better stack traces" diff --git a/tests/js/t7109.nim b/tests/js/t7109.nim new file mode 100644 index 000000000..0d071dbbf --- /dev/null +++ b/tests/js/t7109.nim @@ -0,0 +1,8 @@ +discard """ + errormsg: "Closure iterators are not supported by JS backend!" +""" + +iterator iter*(): int {.closure.} = + yield 3 + +var x = iter From b8bcf236dd29ee2a6ec7587dc3f028f14023ebec Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Tue, 3 Nov 2020 18:41:41 +0800 Subject: [PATCH 0899/3768] fix #12640 (#15829) --- compiler/ccgstmts.nim | 8 +++++++- tests/pragmas/t12640.nim | 25 +++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) create mode 100644 tests/pragmas/t12640.nim diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index aaed37508..367e693e9 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -382,7 +382,13 @@ proc genSingleVar(p: BProc, v: PSym; vn, value: PNode) = proc genSingleVar(p: BProc, a: PNode) = let v = a[0].sym - if sfCompileTime in v.flags: return + if sfCompileTime in v.flags: + # fix issue #12640 + # {.global, compileTime.} pragma in proc + if sfGlobal in v.flags and p.prc != nil and p.prc.kind == skProc: + discard + else: + return genSingleVar(p, v, a[0], a[2]) proc genClosureVar(p: BProc, a: PNode) = diff --git a/tests/pragmas/t12640.nim b/tests/pragmas/t12640.nim new file mode 100644 index 000000000..60177d034 --- /dev/null +++ b/tests/pragmas/t12640.nim @@ -0,0 +1,25 @@ +discard """ + nimout: '''1 +2 +3 +[1, 2, 3]''' + + output: '''1 +2 +3 +[1, 2, 3]''' +""" + + +proc doIt(a: openArray[int]) = + echo a + +proc foo() = + var bug {.global, compiletime.}: seq[int] + bug = @[1, 2 ,3] + for i in 0 .. high(bug): echo bug[i] + doIt(bug) + +static: + foo() +foo() From d62f3627aa1946cd5fa9d3e52abedf9576db0e8e Mon Sep 17 00:00:00 2001 From: cooldome Date: Tue, 3 Nov 2020 15:26:16 +0000 Subject: [PATCH 0900/3768] EnumUtils, speed up findStr in compiler (#15777) * add parseEnumRange * fix runnable example * update changelog * use parseEnumRange in compiler * reorganise code * add changelog, make single normalizer argument --- changelog.md | 1 + compiler/commands.nim | 4 +-- compiler/pragmas.nim | 2 +- compiler/wordrecg.nim | 25 ++++++++++++----- lib/pure/strutils.nim | 61 ++--------------------------------------- lib/std/enumutils.nim | 64 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 89 insertions(+), 68 deletions(-) create mode 100644 lib/std/enumutils.nim diff --git a/changelog.md b/changelog.md index 6bff75a44..d28edd34c 100644 --- a/changelog.md +++ b/changelog.md @@ -17,6 +17,7 @@ - Added `randState` template that exposes the default random number generator. Useful for library authors. +- Added std/enumutils module containing `genEnumCaseStmt` macro that generates case statement to parse string to enum. ## Language changes diff --git a/compiler/commands.nim b/compiler/commands.nim index a35d1dae7..cb7517f06 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -193,11 +193,11 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass, elif i < arg.len and (arg[i] in {':', '='}): inc(i) else: invalidCmdLineOption(conf, pass, orig, info) if state == wHint: - let x = findStr(hintMin..hintMax, id, errUnknown) + let x = findStr(hintMin, hintMax, id, errUnknown) if x != errUnknown: n = TNoteKind(x) else: localError(conf, info, "unknown hint: " & id) else: - let x = findStr(warnMin..warnMax, id, errUnknown) + let x = findStr(warnMin, warnMax, id, errUnknown) if x != errUnknown: n = TNoteKind(x) else: localError(conf, info, "unknown warning: " & id) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index c1d54c4a2..8affccbf3 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -331,7 +331,7 @@ proc processDynLib(c: PContext, n: PNode, sym: PSym) = proc processNote(c: PContext, n: PNode) = template handleNote(enumVals, notes) = - let x = findStr(enumVals, n[0][1].ident.s, errUnknown) + let x = findStr(enumVals.a, enumVals.b, n[0][1].ident.s, errUnknown) if x != errUnknown: nk = TNoteKind(x) let x = c.semConstBoolExpr(c, n[1]) diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index e2e3a1d2f..170d04df1 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -13,8 +13,6 @@ # does not support strings. Without this the code would # be slow and unreadable. -from strutils import cmpIgnoreStyle - type TSpecialWord* = enum wInvalid = "", @@ -125,8 +123,21 @@ const wAsm, wBreak, wCase, wConst, wContinue, wDo, wElse, wEnum, wExport, wFor, wIf, wReturn, wStatic, wTemplate, wTry, wWhile, wUsing} -proc findStr*[T:enum](a: Slice[T], s: string, default: T): T = - for i in a: - if cmpIgnoreStyle($i, s) == 0: - return i - result = default \ No newline at end of file + +const enumUtilsExist = compiles: + import std/enumutils + +when enumUtilsExist: + from std/enumutils import genEnumCaseStmt + from strutils import normalize + proc findStr*[T: enum](a, b: static[T], s: string, default: T): T = + genEnumCaseStmt(T, s, default, ord(a), ord(b), normalize) + +else: + from strutils import cmpIgnoreStyle + proc findStr*[T: enum](a, b: static[T], s: string, default: T): T {.deprecated.} = + # used for compiler bootstrapping only + for i in a..b: + if cmpIgnoreStyle($i, s) == 0: + return i + result = default \ No newline at end of file diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 491760137..19a37425e 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -76,7 +76,7 @@ import parseutils from math import pow, floor, log10 from algorithm import reverse -import macros # for `parseEnum` +import std/enumutils when defined(nimVmExportFixed): from unicode import toLower, toUpper @@ -1264,61 +1264,6 @@ proc parseBool*(s: string): bool = of "n", "no", "false", "0", "off": result = false else: raise newException(ValueError, "cannot interpret as a bool: " & s) -proc addOfBranch(s: string, field, enumType: NimNode): NimNode = - result = nnkOfBranch.newTree( - newLit s, - nnkCall.newTree(enumType, field) # `T()` - ) - -macro genEnumStmt(typ: typedesc, argSym: typed, default: typed): untyped = - # generates a case stmt, which assigns the correct enum field given - # a normalized string comparison to the `argSym` input. - # NOTE: for an enum with fields Foo, Bar, ... we cannot generate - # `of "Foo".nimIdentNormalize: Foo`. - # This will fail, if the enum is not defined at top level (e.g. in a block). - # Thus we check for the field value of the (possible holed enum) and convert - # the integer value to the generic argument `typ`. - let typ = typ.getTypeInst[1] - let impl = typ.getImpl[2] - expectKind impl, nnkEnumTy - result = nnkCaseStmt.newTree(newCall(bindSym"nimIdentNormalize", argSym)) - # stores all processed field strings to give error msg for ambiguous enums - var foundFields: seq[string] = @[] - var fStr = "" # string of current field - var fNum = BiggestInt(0) # int value of current field - for f in impl: - case f.kind - of nnkEmpty: continue # skip first node of `enumTy` - of nnkSym, nnkIdent: fStr = f.strVal - of nnkEnumFieldDef: - case f[1].kind - of nnkStrLit: fStr = f[1].strVal - of nnkTupleConstr: - fStr = f[1][1].strVal - fNum = f[1][0].intVal - of nnkIntLit: - fStr = f[0].strVal - fNum = f[1].intVal - else: error("Invalid tuple syntax!", f[1]) - else: error("Invalid node for enum type!", f) - # add field if string not already added - fStr = nimIdentNormalize(fStr) - if fStr notin foundFields: - result.add addOfBranch(fStr, newLit fNum, typ) - foundFields.add fStr - else: - error("Ambiguous enums cannot be parsed, field " & $fStr & - " appears multiple times!", f) - inc fNum - # finally add else branch to raise or use default - if default == nil: - let raiseStmt = quote do: - raise newException(ValueError, "Invalid enum value: " & $`argSym`) - result.add nnkElse.newTree(raiseStmt) - else: - expectKind(default, nnkSym) - result.add nnkElse.newTree(default) - proc parseEnum*[T: enum](s: string): T = ## Parses an enum ``T``. This errors at compile time, if the given enum ## type contains multiple fields with the same string value. @@ -1337,7 +1282,7 @@ proc parseEnum*[T: enum](s: string): T = doAssertRaises(ValueError): echo parseEnum[MyEnum]("third") - genEnumStmt(T, s, default = nil) + genEnumCaseStmt(T, s, default = nil, ord(low(T)), ord(high(T)), nimIdentNormalize) proc parseEnum*[T: enum](s: string, default: T): T = ## Parses an enum ``T``. This errors at compile time, if the given enum @@ -1356,7 +1301,7 @@ proc parseEnum*[T: enum](s: string, default: T): T = doAssert parseEnum[MyEnum]("second") == second doAssert parseEnum[MyEnum]("last", third) == third - genEnumStmt(T, s, default) + genEnumCaseStmt(T, s, default, ord(low(T)), ord(high(T)), nimIdentNormalize) proc repeat*(c: char, count: Natural): string {.noSideEffect, rtl, extern: "nsuRepeatChar".} = diff --git a/lib/std/enumutils.nim b/lib/std/enumutils.nim new file mode 100644 index 000000000..704e42de5 --- /dev/null +++ b/lib/std/enumutils.nim @@ -0,0 +1,64 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2020 Nim contributors +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +import macros + +macro genEnumCaseStmt*(typ: typedesc, argSym: typed, default: typed, + userMin, userMax: static[int], normalizer: static[proc(s :string): string]): untyped = + # generates a case stmt, which assigns the correct enum field given + # a normalized string comparison to the `argSym` input. + # string normalization is done using passed normalizer. + # NOTE: for an enum with fields Foo, Bar, ... we cannot generate + # `of "Foo".nimIdentNormalize: Foo`. + # This will fail, if the enum is not defined at top level (e.g. in a block). + # Thus we check for the field value of the (possible holed enum) and convert + # the integer value to the generic argument `typ`. + let typ = typ.getTypeInst[1] + let impl = typ.getImpl[2] + expectKind impl, nnkEnumTy + let normalizerNode = quote: `normalizer` + expectKind normalizerNode, nnkSym + result = nnkCaseStmt.newTree(newCall(normalizerNode, argSym)) + # stores all processed field strings to give error msg for ambiguous enums + var foundFields: seq[string] = @[] + var fStr = "" # string of current field + var fNum = BiggestInt(0) # int value of current field + for f in impl: + case f.kind + of nnkEmpty: continue # skip first node of `enumTy` + of nnkSym, nnkIdent: fStr = f.strVal + of nnkEnumFieldDef: + case f[1].kind + of nnkStrLit: fStr = f[1].strVal + of nnkTupleConstr: + fStr = f[1][1].strVal + fNum = f[1][0].intVal + of nnkIntLit: + fStr = f[0].strVal + fNum = f[1].intVal + else: error("Invalid tuple syntax!", f[1]) + else: error("Invalid node for enum type!", f) + # add field if string not already added + if fNum >= userMin and fNum <= userMax: + fStr = normalizer(fStr) + if fStr notin foundFields: + result.add nnkOfBranch.newTree(newLit fStr, nnkCall.newTree(typ, newLit fNum)) + foundFields.add fStr + else: + error("Ambiguous enums cannot be parsed, field " & $fStr & + " appears multiple times!", f) + inc fNum + # finally add else branch to raise or use default + if default == nil: + let raiseStmt = quote do: + raise newException(ValueError, "Invalid enum value: " & $`argSym`) + result.add nnkElse.newTree(raiseStmt) + else: + expectKind(default, nnkSym) + result.add nnkElse.newTree(default) \ No newline at end of file From 75dc69417a235c3be1edb3d89ddb49104bdd438d Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Tue, 3 Nov 2020 23:31:16 +0800 Subject: [PATCH 0901/3768] fix deprecated messages regarding high (#15832) --- lib/system/gc.nim | 2 +- lib/system/gc_ms.nim | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/system/gc.nim b/lib/system/gc.nim index 304eda19a..1f7164266 100644 --- a/lib/system/gc.nim +++ b/lib/system/gc.nim @@ -856,7 +856,7 @@ when not defined(useNimRtl): gch.cycleThreshold = InitialCycleThreshold proc GC_disableMarkAndSweep() = - gch.cycleThreshold = high(gch.cycleThreshold)-1 + gch.cycleThreshold = high(typeof(gch.cycleThreshold))-1 # set to the max value to suppress the cycle detector proc GC_fullCollect() = diff --git a/lib/system/gc_ms.nim b/lib/system/gc_ms.nim index 6683b9e34..852f5d7aa 100644 --- a/lib/system/gc_ms.nim +++ b/lib/system/gc_ms.nim @@ -507,7 +507,7 @@ when not defined(useNimRtl): gch.cycleThreshold = InitialThreshold proc GC_disableMarkAndSweep() = - gch.cycleThreshold = high(gch.cycleThreshold)-1 + gch.cycleThreshold = high(typeof(gch.cycleThreshold))-1 # set to the max value to suppress the cycle detector when defined(nimTracing): From b0e26d8fbffd044aa3f23fc680e6a2e666593399 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Wed, 4 Nov 2020 03:43:51 +0800 Subject: [PATCH 0902/3768] remove iup from stdlib in contrast to #15828 (#15830) * remove iup from stdlib * Update changelog.md --- changelog.md | 2 + doc/lib.rst | 7 - lib/wrappers/iup.nim | 995 ------------------------------------------- tools/kochdocs.nim | 1 - 4 files changed, 2 insertions(+), 1003 deletions(-) delete mode 100644 lib/wrappers/iup.nim diff --git a/changelog.md b/changelog.md index d28edd34c..871fb229d 100644 --- a/changelog.md +++ b/changelog.md @@ -19,6 +19,8 @@ - Added std/enumutils module containing `genEnumCaseStmt` macro that generates case statement to parse string to enum. +- Removed deprecated `iup` module from stdlib, it has already moved to [nimble](https://github.com/nim-lang/iup). + ## Language changes diff --git a/doc/lib.rst b/doc/lib.rst index 2dd43109e..3c80fd343 100644 --- a/doc/lib.rst +++ b/doc/lib.rst @@ -540,13 +540,6 @@ Regular expressions Wrapper for the PCRE library. -GUI libraries -------------- - -* `iup `_ - The wrapper of the IUP GUI library. - - Database support ---------------- diff --git a/lib/wrappers/iup.nim b/lib/wrappers/iup.nim deleted file mode 100644 index 0242d7981..000000000 --- a/lib/wrappers/iup.nim +++ /dev/null @@ -1,995 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2012 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -# C header files translated by hand -# Licence of IUP follows: - - -# **************************************************************************** -# Copyright (C) 1994-2009 Tecgraf, PUC-Rio. -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -# SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# **************************************************************************** - - -## Wrapper for the `IUP native GUI library `_. -## -## Requires `iup.dll` on Windows, `libiup.so` on Linux, `libiup.dylib` on OS X. -## Compile with `-d:nimDebugDlOpen` to debug the shared library opening. -## -## Examples -## ======== -## -## .. image:: http://webserver2.tecgraf.puc-rio.br/iup/en/tutorial/example2_1.png -## -## .. code-block:: nim -## import os, iup -## var argc = create(cint) -## argc[] = paramCount().cint -## var argv = allocCstringArray(commandLineParams()) -## assert open(argc, argv.addr) == 0 # UIP requires calling open() -## message(r"Hello world 1", r"Hello world") # Message popup -## close() # UIP requires calling close() -## -## -## .. image:: http://webserver2.tecgraf.puc-rio.br/iup/en/tutorial/example3_1.png -## -## .. code-block:: nim -## import os, iup -## var argc = create(cint) -## argc[] = paramCount().cint -## var argv = allocCstringArray(commandLineParams()) -## assert open(argc, argv.addr) == 0 # UIP requires open() -## let textarea = text(nil) # Text widget. -## setAttribute(textarea, r"MULTILINE", r"YES") # Set text widget to multiline. -## setAttribute(textarea, r"EXPAND", r"YES") # Set text widget to auto expand. -## let layout = vbox(textarea) # Vertical layout. -## let window = dialog(layout) # Dialog window. -## setAttribute(window, "TITLE", "Nim Notepad") # Set window title. -## showXY(window, IUP_CENTER, IUP_CENTER) # Show window. -## mainLoop() # Main loop. -## close() # UIP requires calling close() - - -when defined(windows): - const dllname = "iup(|30|27|26|25|24).dll" -elif defined(macosx): - const dllname = "libiup(|3.0|2.7|2.6|2.5|2.4).dylib" -else: - const dllname = "libiup(|3.0|2.7|2.6|2.5|2.4).so(|.1)" - -const - IUP_NAME* = "IUP - Portable User Interface" - IUP_COPYRIGHT* = "Copyright (C) 1994-2009 Tecgraf, PUC-Rio." - IUP_DESCRIPTION* = "Portable toolkit for building graphical user interfaces." - constIUP_VERSION* = "3.0" - constIUP_VERSION_NUMBER* = 300000 - constIUP_VERSION_DATE* = "2009/07/18" - -type - Ihandle = object - PIhandle* = ptr Ihandle - - Icallback* = proc (arg: PIhandle): cint {.cdecl.} - -# pre-defined dialogs -proc fileDlg*: PIhandle {.importc: "IupFileDlg", dynlib: dllname, cdecl.} -proc messageDlg*: PIhandle {.importc: "IupMessageDlg", dynlib: dllname, cdecl.} -proc colorDlg*: PIhandle {.importc: "IupColorDlg", dynlib: dllname, cdecl.} -proc fontDlg*: PIhandle {.importc: "IupFontDlg", dynlib: dllname, cdecl.} - -proc getFile*(arq: cstring): cint {. - importc: "IupGetFile", dynlib: dllname, cdecl.} -proc message*(title, msg: cstring) {. - importc: "IupMessage", dynlib: dllname, cdecl.} -proc messagef*(title, format: cstring) {. - importc: "IupMessagef", dynlib: dllname, cdecl, varargs.} -proc alarm*(title, msg, b1, b2, b3: cstring): cint {. - importc: "IupAlarm", dynlib: dllname, cdecl.} -proc scanf*(format: cstring): cint {. - importc: "IupScanf", dynlib: dllname, cdecl, varargs.} -proc listDialog*(theType: cint, title: cstring, size: cint, - list: cstringArray, op, maxCol, maxLin: cint, - marks: ptr cint): cint {. - importc: "IupListDialog", dynlib: dllname, cdecl.} -proc getText*(title, text: cstring): cint {. - importc: "IupGetText", dynlib: dllname, cdecl.} -proc getColor*(x, y: cint, r, g, b: var byte): cint {. - importc: "IupGetColor", dynlib: dllname, cdecl.} - -type - Iparamcb* = proc (dialog: PIhandle, paramIndex: cint, - userData: pointer): cint {.cdecl.} - -proc getParam*(title: cstring, action: Iparamcb, userData: pointer, - format: cstring): cint {. - importc: "IupGetParam", cdecl, varargs, dynlib: dllname.} -proc getParamv*(title: cstring, action: Iparamcb, userData: pointer, - format: cstring, paramCount, paramExtra: cint, - paramData: pointer): cint {. - importc: "IupGetParamv", cdecl, dynlib: dllname.} - - -# Functions - -proc open*(argc: ptr cint, argv: ptr cstringArray): cint {. - importc: "IupOpen", cdecl, dynlib: dllname.} -proc close*() {.importc: "IupClose", cdecl, dynlib: dllname.} -proc imageLibOpen*() {.importc: "IupImageLibOpen", cdecl, dynlib: dllname.} - -proc mainLoop*(): cint {.importc: "IupMainLoop", cdecl, dynlib: dllname, - discardable.} -proc loopStep*(): cint {.importc: "IupLoopStep", cdecl, dynlib: dllname, - discardable.} -proc mainLoopLevel*(): cint {.importc: "IupMainLoopLevel", cdecl, - dynlib: dllname, discardable.} -proc flush*() {.importc: "IupFlush", cdecl, dynlib: dllname.} -proc exitLoop*() {.importc: "IupExitLoop", cdecl, dynlib: dllname.} - -proc update*(ih: PIhandle) {.importc: "IupUpdate", cdecl, dynlib: dllname.} -proc updateChildren*(ih: PIhandle) {.importc: "IupUpdateChildren", cdecl, dynlib: dllname.} -proc redraw*(ih: PIhandle, children: cint) {.importc: "IupRedraw", cdecl, dynlib: dllname.} -proc refresh*(ih: PIhandle) {.importc: "IupRefresh", cdecl, dynlib: dllname.} - -proc mapFont*(iupfont: cstring): cstring {.importc: "IupMapFont", cdecl, dynlib: dllname.} -proc unMapFont*(driverfont: cstring): cstring {.importc: "IupUnMapFont", cdecl, dynlib: dllname.} -proc help*(url: cstring): cint {.importc: "IupHelp", cdecl, dynlib: dllname.} -proc load*(filename: cstring): cstring {.importc: "IupLoad", cdecl, dynlib: dllname.} - -proc iupVersion*(): cstring {.importc: "IupVersion", cdecl, dynlib: dllname.} -proc iupVersionDate*(): cstring {.importc: "IupVersionDate", cdecl, dynlib: dllname.} -proc iupVersionNumber*(): cint {.importc: "IupVersionNumber", cdecl, dynlib: dllname.} -proc setLanguage*(lng: cstring) {.importc: "IupSetLanguage", cdecl, dynlib: dllname.} -proc getLanguage*(): cstring {.importc: "IupGetLanguage", cdecl, dynlib: dllname.} - -proc destroy*(ih: PIhandle) {.importc: "IupDestroy", cdecl, dynlib: dllname.} -proc detach*(child: PIhandle) {.importc: "IupDetach", cdecl, dynlib: dllname.} -proc append*(ih, child: PIhandle): PIhandle {. - importc: "IupAppend", cdecl, dynlib: dllname, discardable.} -proc insert*(ih, refChild, child: PIhandle): PIhandle {. - importc: "IupInsert", cdecl, dynlib: dllname, discardable.} -proc getChild*(ih: PIhandle, pos: cint): PIhandle {. - importc: "IupGetChild", cdecl, dynlib: dllname.} -proc getChildPos*(ih, child: PIhandle): cint {. - importc: "IupGetChildPos", cdecl, dynlib: dllname.} -proc getChildCount*(ih: PIhandle): cint {. - importc: "IupGetChildCount", cdecl, dynlib: dllname.} -proc getNextChild*(ih, child: PIhandle): PIhandle {. - importc: "IupGetNextChild", cdecl, dynlib: dllname.} -proc getBrother*(ih: PIhandle): PIhandle {. - importc: "IupGetBrother", cdecl, dynlib: dllname.} -proc getParent*(ih: PIhandle): PIhandle {. - importc: "IupGetParent", cdecl, dynlib: dllname.} -proc getDialog*(ih: PIhandle): PIhandle {. - importc: "IupGetDialog", cdecl, dynlib: dllname.} -proc getDialogChild*(ih: PIhandle, name: cstring): PIhandle {. - importc: "IupGetDialogChild", cdecl, dynlib: dllname.} -proc reparent*(ih, newParent: PIhandle): cint {. - importc: "IupReparent", cdecl, dynlib: dllname.} - -proc popup*(ih: PIhandle, x, y: cint): cint {. - importc: "IupPopup", cdecl, dynlib: dllname, discardable.} -proc show*(ih: PIhandle): cint {. - importc: "IupShow", cdecl, dynlib: dllname, discardable.} -proc showXY*(ih: PIhandle, x, y: cint): cint {. - importc: "IupShowXY", cdecl, dynlib: dllname, discardable.} -proc hide*(ih: PIhandle): cint {. - importc: "IupHide", cdecl, dynlib: dllname, discardable.} -proc map*(ih: PIhandle): cint {. - importc: "IupMap", cdecl, dynlib: dllname, discardable.} -proc unmap*(ih: PIhandle) {. - importc: "IupUnmap", cdecl, dynlib: dllname, discardable.} - -proc setAttribute*(ih: PIhandle, name, value: cstring) {. - importc: "IupSetAttribute", cdecl, dynlib: dllname.} -proc storeAttribute*(ih: PIhandle, name, value: cstring) {. - importc: "IupStoreAttribute", cdecl, dynlib: dllname.} -proc setAttributes*(ih: PIhandle, str: cstring): PIhandle {. - importc: "IupSetAttributes", cdecl, dynlib: dllname.} -proc getAttribute*(ih: PIhandle, name: cstring): cstring {. - importc: "IupGetAttribute", cdecl, dynlib: dllname.} -proc getAttributes*(ih: PIhandle): cstring {. - importc: "IupGetAttributes", cdecl, dynlib: dllname.} -proc getInt*(ih: PIhandle, name: cstring): cint {. - importc: "IupGetInt", cdecl, dynlib: dllname.} -proc getInt2*(ih: PIhandle, name: cstring): cint {. - importc: "IupGetInt2", cdecl, dynlib: dllname.} -proc getIntInt*(ih: PIhandle, name: cstring, i1, i2: var cint): cint {. - importc: "IupGetIntInt", cdecl, dynlib: dllname.} -proc getFloat*(ih: PIhandle, name: cstring): cfloat {. - importc: "IupGetFloat", cdecl, dynlib: dllname.} -proc setfAttribute*(ih: PIhandle, name, format: cstring) {. - importc: "IupSetfAttribute", cdecl, dynlib: dllname, varargs.} -proc getAllAttributes*(ih: PIhandle, names: cstringArray, n: cint): cint {. - importc: "IupGetAllAttributes", cdecl, dynlib: dllname.} -proc setAtt*(handleName: cstring, ih: PIhandle, name: cstring): PIhandle {. - importc: "IupSetAtt", cdecl, dynlib: dllname, varargs, discardable.} - -proc setGlobal*(name, value: cstring) {. - importc: "IupSetGlobal", cdecl, dynlib: dllname.} -proc storeGlobal*(name, value: cstring) {. - importc: "IupStoreGlobal", cdecl, dynlib: dllname.} -proc getGlobal*(name: cstring): cstring {. - importc: "IupGetGlobal", cdecl, dynlib: dllname.} - -proc setFocus*(ih: PIhandle): PIhandle {. - importc: "IupSetFocus", cdecl, dynlib: dllname.} -proc getFocus*(): PIhandle {. - importc: "IupGetFocus", cdecl, dynlib: dllname.} -proc previousField*(ih: PIhandle): PIhandle {. - importc: "IupPreviousField", cdecl, dynlib: dllname.} -proc nextField*(ih: PIhandle): PIhandle {. - importc: "IupNextField", cdecl, dynlib: dllname.} - -proc getCallback*(ih: PIhandle, name: cstring): Icallback {. - importc: "IupGetCallback", cdecl, dynlib: dllname.} -proc setCallback*(ih: PIhandle, name: cstring, fn: Icallback): Icallback {. - importc: "IupSetCallback", cdecl, dynlib: dllname, discardable.} - -proc setCallbacks*(ih: PIhandle, name: cstring, fn: Icallback): PIhandle {. - importc: "IupSetCallbacks", cdecl, dynlib: dllname, varargs, discardable.} - -proc getFunction*(name: cstring): Icallback {. - importc: "IupGetFunction", cdecl, dynlib: dllname.} -proc setFunction*(name: cstring, fn: Icallback): Icallback {. - importc: "IupSetFunction", cdecl, dynlib: dllname, discardable.} -proc getActionName*(): cstring {. - importc: "IupGetActionName", cdecl, dynlib: dllname.} - -proc getHandle*(name: cstring): PIhandle {. - importc: "IupGetHandle", cdecl, dynlib: dllname.} -proc setHandle*(name: cstring, ih: PIhandle): PIhandle {. - importc: "IupSetHandle", cdecl, dynlib: dllname.} -proc getAllNames*(names: cstringArray, n: cint): cint {. - importc: "IupGetAllNames", cdecl, dynlib: dllname.} -proc getAllDialogs*(names: cstringArray, n: cint): cint {. - importc: "IupGetAllDialogs", cdecl, dynlib: dllname.} -proc getName*(ih: PIhandle): cstring {. - importc: "IupGetName", cdecl, dynlib: dllname.} - -proc setAttributeHandle*(ih: PIhandle, name: cstring, ihNamed: PIhandle) {. - importc: "IupSetAttributeHandle", cdecl, dynlib: dllname.} -proc getAttributeHandle*(ih: PIhandle, name: cstring): PIhandle {. - importc: "IupGetAttributeHandle", cdecl, dynlib: dllname.} - -proc getClassName*(ih: PIhandle): cstring {. - importc: "IupGetClassName", cdecl, dynlib: dllname.} -proc getClassType*(ih: PIhandle): cstring {. - importc: "IupGetClassType", cdecl, dynlib: dllname.} -proc getClassAttributes*(classname: cstring, names: cstringArray, - n: cint): cint {. - importc: "IupGetClassAttributes", cdecl, dynlib: dllname.} -proc saveClassAttributes*(ih: PIhandle) {. - importc: "IupSaveClassAttributes", cdecl, dynlib: dllname.} -proc setClassDefaultAttribute*(classname, name, value: cstring) {. - importc: "IupSetClassDefaultAttribute", cdecl, dynlib: dllname.} - -proc create*(classname: cstring): PIhandle {. - importc: "IupCreate", cdecl, dynlib: dllname.} -proc createv*(classname: cstring, params: pointer): PIhandle {. - importc: "IupCreatev", cdecl, dynlib: dllname.} -proc createp*(classname: cstring, first: pointer): PIhandle {. - importc: "IupCreatep", cdecl, dynlib: dllname, varargs.} - -proc fill*(): PIhandle {.importc: "IupFill", cdecl, dynlib: dllname.} -proc radio*(child: PIhandle): PIhandle {. - importc: "IupRadio", cdecl, dynlib: dllname.} -proc vbox*(child: PIhandle): PIhandle {. - importc: "IupVbox", cdecl, dynlib: dllname, varargs.} -proc vboxv*(children: ptr PIhandle): PIhandle {. - importc: "IupVboxv", cdecl, dynlib: dllname.} -proc zbox*(child: PIhandle): PIhandle {. - importc: "IupZbox", cdecl, dynlib: dllname, varargs.} -proc zboxv*(children: ptr PIhandle): PIhandle {. - importc: "IupZboxv", cdecl, dynlib: dllname.} -proc hbox*(child: PIhandle): PIhandle {. - importc: "IupHbox", cdecl, dynlib: dllname, varargs.} -proc hboxv*(children: ptr PIhandle): PIhandle {. - importc: "IupHboxv", cdecl, dynlib: dllname.} - -proc normalizer*(ihFirst: PIhandle): PIhandle {. - importc: "IupNormalizer", cdecl, dynlib: dllname, varargs.} -proc normalizerv*(ihList: ptr PIhandle): PIhandle {. - importc: "IupNormalizerv", cdecl, dynlib: dllname.} - -proc cbox*(child: PIhandle): PIhandle {. - importc: "IupCbox", cdecl, dynlib: dllname, varargs.} -proc cboxv*(children: ptr PIhandle): PIhandle {. - importc: "IupCboxv", cdecl, dynlib: dllname.} -proc sbox*(child: PIhandle): PIhandle {. - importc: "IupSbox", cdecl, dynlib: dllname.} - -proc frame*(child: PIhandle): PIhandle {. - importc: "IupFrame", cdecl, dynlib: dllname.} - -proc image*(width, height: cint, pixmap: pointer): PIhandle {. - importc: "IupImage", cdecl, dynlib: dllname.} -proc imageRGB*(width, height: cint, pixmap: pointer): PIhandle {. - importc: "IupImageRGB", cdecl, dynlib: dllname.} -proc imageRGBA*(width, height: cint, pixmap: pointer): PIhandle {. - importc: "IupImageRGBA", cdecl, dynlib: dllname.} - -proc item*(title, action: cstring): PIhandle {. - importc: "IupItem", cdecl, dynlib: dllname.} -proc submenu*(title: cstring, child: PIhandle): PIhandle {. - importc: "IupSubmenu", cdecl, dynlib: dllname.} -proc separator*(): PIhandle {. - importc: "IupSeparator", cdecl, dynlib: dllname.} -proc menu*(child: PIhandle): PIhandle {. - importc: "IupMenu", cdecl, dynlib: dllname, varargs.} -proc menuv*(children: ptr PIhandle): PIhandle {. - importc: "IupMenuv", cdecl, dynlib: dllname.} - -proc button*(title, action: cstring): PIhandle {. - importc: "IupButton", cdecl, dynlib: dllname.} -proc link*(url, title: cstring): PIhandle {. - importc: "IupLink", cdecl, dynlib: dllname.} -proc canvas*(action: cstring): PIhandle {. - importc: "IupCanvas", cdecl, dynlib: dllname.} -proc dialog*(child: PIhandle): PIhandle {. - importc: "IupDialog", cdecl, dynlib: dllname.} -proc user*(): PIhandle {. - importc: "IupUser", cdecl, dynlib: dllname.} -proc label*(title: cstring): PIhandle {. - importc: "IupLabel", cdecl, dynlib: dllname.} -proc list*(action: cstring): PIhandle {. - importc: "IupList", cdecl, dynlib: dllname.} -proc text*(action: cstring): PIhandle {. - importc: "IupText", cdecl, dynlib: dllname.} -proc multiLine*(action: cstring): PIhandle {. - importc: "IupMultiLine", cdecl, dynlib: dllname.} -proc toggle*(title, action: cstring): PIhandle {. - importc: "IupToggle", cdecl, dynlib: dllname.} -proc timer*(): PIhandle {. - importc: "IupTimer", cdecl, dynlib: dllname.} -proc progressBar*(): PIhandle {. - importc: "IupProgressBar", cdecl, dynlib: dllname.} -proc val*(theType: cstring): PIhandle {. - importc: "IupVal", cdecl, dynlib: dllname.} -proc tabs*(child: PIhandle): PIhandle {. - importc: "IupTabs", cdecl, dynlib: dllname, varargs.} -proc tabsv*(children: ptr PIhandle): PIhandle {. - importc: "IupTabsv", cdecl, dynlib: dllname.} -proc tree*(): PIhandle {.importc: "IupTree", cdecl, dynlib: dllname.} - -proc spin*(): PIhandle {.importc: "IupSpin", cdecl, dynlib: dllname.} -proc spinbox*(child: PIhandle): PIhandle {. - importc: "IupSpinbox", cdecl, dynlib: dllname.} - -# IupText utilities -proc textConvertLinColToPos*(ih: PIhandle, lin, col: cint, pos: var cint) {. - importc: "IupTextConvertLinColToPos", cdecl, dynlib: dllname.} -proc textConvertPosToLinCol*(ih: PIhandle, pos: cint, lin, col: var cint) {. - importc: "IupTextConvertPosToLinCol", cdecl, dynlib: dllname.} - -proc convertXYToPos*(ih: PIhandle, x, y: cint): cint {. - importc: "IupConvertXYToPos", cdecl, dynlib: dllname.} - -# IupTree utilities -proc treeSetUserId*(ih: PIhandle, id: cint, userid: pointer): cint {. - importc: "IupTreeSetUserId", cdecl, dynlib: dllname, discardable.} -proc treeGetUserId*(ih: PIhandle, id: cint): pointer {. - importc: "IupTreeGetUserId", cdecl, dynlib: dllname.} -proc treeGetId*(ih: PIhandle, userid: pointer): cint {. - importc: "IupTreeGetId", cdecl, dynlib: dllname.} - -proc treeSetAttribute*(ih: PIhandle, name: cstring, id: cint, value: cstring) {. - importc: "IupTreeSetAttribute", cdecl, dynlib: dllname.} -proc treeStoreAttribute*(ih: PIhandle, name: cstring, id: cint, value: cstring) {. - importc: "IupTreeStoreAttribute", cdecl, dynlib: dllname.} -proc treeGetAttribute*(ih: PIhandle, name: cstring, id: cint): cstring {. - importc: "IupTreeGetAttribute", cdecl, dynlib: dllname.} -proc treeGetInt*(ih: PIhandle, name: cstring, id: cint): cint {. - importc: "IupTreeGetInt", cdecl, dynlib: dllname.} -proc treeGetFloat*(ih: PIhandle, name: cstring, id: cint): cfloat {. - importc: "IupTreeGetFloat", cdecl, dynlib: dllname.} -proc treeSetfAttribute*(ih: PIhandle, name: cstring, id: cint, format: cstring) {. - importc: "IupTreeSetfAttribute", cdecl, dynlib: dllname, varargs.} - - -# Common Return Values -const - IUP_ERROR* = cint(1) - IUP_NOERROR* = cint(0) - IUP_OPENED* = cint(-1) - IUP_INVALID* = cint(-1) - - # Callback Return Values - IUP_IGNORE* = cint(-1) - IUP_DEFAULT* = cint(-2) - IUP_CLOSE* = cint(-3) - IUP_CONTINUE* = cint(-4) - - # IupPopup and IupShowXY Parameter Values - IUP_CENTER* = cint(0xFFFF) - IUP_LEFT* = cint(0xFFFE) - IUP_RIGHT* = cint(0xFFFD) - IUP_MOUSEPOS* = cint(0xFFFC) - IUP_CURRENT* = cint(0xFFFB) - IUP_CENTERPARENT* = cint(0xFFFA) - IUP_TOP* = IUP_LEFT - IUP_BOTTOM* = IUP_RIGHT - - # SHOW_CB Callback Values - IUP_SHOW* = cint(0) - IUP_RESTORE* = cint(1) - IUP_MINIMIZE* = cint(2) - IUP_MAXIMIZE* = cint(3) - IUP_HIDE* = cint(4) - - # SCROLL_CB Callback Values - IUP_SBUP* = cint(0) - IUP_SBDN* = cint(1) - IUP_SBPGUP* = cint(2) - IUP_SBPGDN* = cint(3) - IUP_SBPOSV* = cint(4) - IUP_SBDRAGV* = cint(5) - IUP_SBLEFT* = cint(6) - IUP_SBRIGHT* = cint(7) - IUP_SBPGLEFT* = cint(8) - IUP_SBPGRIGHT* = cint(9) - IUP_SBPOSH* = cint(10) - IUP_SBDRAGH* = cint(11) - - # Mouse Button Values and Macros - IUP_BUTTON1* = cint(ord('1')) - IUP_BUTTON2* = cint(ord('2')) - IUP_BUTTON3* = cint(ord('3')) - IUP_BUTTON4* = cint(ord('4')) - IUP_BUTTON5* = cint(ord('5')) - -proc isShift*(s: cstring): bool = return s[0] == 'S' -proc isControl*(s: cstring): bool = return s[1] == 'C' -proc isButton1*(s: cstring): bool = return s[2] == '1' -proc isButton2*(s: cstring): bool = return s[3] == '2' -proc isbutton3*(s: cstring): bool = return s[4] == '3' -proc isDouble*(s: cstring): bool = return s[5] == 'D' -proc isAlt*(s: cstring): bool = return s[6] == 'A' -proc isSys*(s: cstring): bool = return s[7] == 'Y' -proc isButton4*(s: cstring): bool = return s[8] == '4' -proc isButton5*(s: cstring): bool = return s[9] == '5' - -# Pre-Defined Masks -const - IUP_MASK_FLOAT* = "[+/-]?(/d+/.?/d*|/./d+)" - IUP_MASK_UFLOAT* = "(/d+/.?/d*|/./d+)" - IUP_MASK_EFLOAT* = "[+/-]?(/d+/.?/d*|/./d+)([eE][+/-]?/d+)?" - IUP_MASK_INT* = "[+/-]?/d+" - IUP_MASK_UINT* = "/d+" - -# from 32 to 126, all character sets are equal, -# the key code i the same as the character code. -const - K_SP* = cint(ord(' ')) - K_exclam* = cint(ord('!')) - K_quotedbl* = cint(ord('\"')) - K_numbersign* = cint(ord('#')) - K_dollar* = cint(ord('$')) - K_percent* = cint(ord('%')) - K_ampersand* = cint(ord('&')) - K_apostrophe* = cint(ord('\'')) - K_parentleft* = cint(ord('(')) - K_parentright* = cint(ord(')')) - K_asterisk* = cint(ord('*')) - K_plus* = cint(ord('+')) - K_comma* = cint(ord(',')) - K_minus* = cint(ord('-')) - K_period* = cint(ord('.')) - K_slash* = cint(ord('/')) - K_0* = cint(ord('0')) - K_1* = cint(ord('1')) - K_2* = cint(ord('2')) - K_3* = cint(ord('3')) - K_4* = cint(ord('4')) - K_5* = cint(ord('5')) - K_6* = cint(ord('6')) - K_7* = cint(ord('7')) - K_8* = cint(ord('8')) - K_9* = cint(ord('9')) - K_colon* = cint(ord(':')) - K_semicolon* = cint(ord(';')) - K_less* = cint(ord('<')) - K_equal* = cint(ord('=')) - K_greater* = cint(ord('>')) - K_question* = cint(ord('?')) - K_at* = cint(ord('@')) - K_upperA* = cint(ord('A')) - K_upperB* = cint(ord('B')) - K_upperC* = cint(ord('C')) - K_upperD* = cint(ord('D')) - K_upperE* = cint(ord('E')) - K_upperF* = cint(ord('F')) - K_upperG* = cint(ord('G')) - K_upperH* = cint(ord('H')) - K_upperI* = cint(ord('I')) - K_upperJ* = cint(ord('J')) - K_upperK* = cint(ord('K')) - K_upperL* = cint(ord('L')) - K_upperM* = cint(ord('M')) - K_upperN* = cint(ord('N')) - K_upperO* = cint(ord('O')) - K_upperP* = cint(ord('P')) - K_upperQ* = cint(ord('Q')) - K_upperR* = cint(ord('R')) - K_upperS* = cint(ord('S')) - K_upperT* = cint(ord('T')) - K_upperU* = cint(ord('U')) - K_upperV* = cint(ord('V')) - K_upperW* = cint(ord('W')) - K_upperX* = cint(ord('X')) - K_upperY* = cint(ord('Y')) - K_upperZ* = cint(ord('Z')) - K_bracketleft* = cint(ord('[')) - K_backslash* = cint(ord('\\')) - K_bracketright* = cint(ord(']')) - K_circum* = cint(ord('^')) - K_underscore* = cint(ord('_')) - K_grave* = cint(ord('`')) - K_lowera* = cint(ord('a')) - K_lowerb* = cint(ord('b')) - K_lowerc* = cint(ord('c')) - K_lowerd* = cint(ord('d')) - K_lowere* = cint(ord('e')) - K_lowerf* = cint(ord('f')) - K_lowerg* = cint(ord('g')) - K_lowerh* = cint(ord('h')) - K_loweri* = cint(ord('i')) - K_lowerj* = cint(ord('j')) - K_lowerk* = cint(ord('k')) - K_lowerl* = cint(ord('l')) - K_lowerm* = cint(ord('m')) - K_lowern* = cint(ord('n')) - K_lowero* = cint(ord('o')) - K_lowerp* = cint(ord('p')) - K_lowerq* = cint(ord('q')) - K_lowerr* = cint(ord('r')) - K_lowers* = cint(ord('s')) - K_lowert* = cint(ord('t')) - K_loweru* = cint(ord('u')) - K_lowerv* = cint(ord('v')) - K_lowerw* = cint(ord('w')) - K_lowerx* = cint(ord('x')) - K_lowery* = cint(ord('y')) - K_lowerz* = cint(ord('z')) - K_braceleft* = cint(ord('{')) - K_bar* = cint(ord('|')) - K_braceright* = cint(ord('}')) - K_tilde* = cint(ord('~')) - -proc isPrint*(c: cint): bool = return c > 31 and c < 127 - -# also define the escape sequences that have keys associated -const - K_BS* = cint(ord('\b')) - K_TAB* = cint(ord('\t')) - K_LF* = cint(10) - K_CR* = cint(13) - -# IUP Extended Key Codes, range start at 128 -# Modifiers use 256 interval -# These key code definitions are specific to IUP - -proc isXkey*(c: cint): bool = return c > 128 -proc isShiftXkey*(c: cint): bool = return c > 256 and c < 512 -proc isCtrlXkey*(c: cint): bool = return c > 512 and c < 768 -proc isAltXkey*(c: cint): bool = return c > 768 and c < 1024 -proc isSysXkey*(c: cint): bool = return c > 1024 and c < 1280 - -proc iUPxCODE*(c: cint): cint = return c + cint(128) # Normal (must be above 128) -proc iUPsxCODE*(c: cint): cint = - return c + cint(256) - # Shift (must have range to include the standard keys and the normal - # extended keys, so must be above 256 - -proc iUPcxCODE*(c: cint): cint = return c + cint(512) # Ctrl -proc iUPmxCODE*(c: cint): cint = return c + cint(768) # Alt -proc iUPyxCODE*(c: cint): cint = return c + cint(1024) # Sys (Win or Apple) - -const - IUP_NUMMAXCODES* = 1280 ## 5*256=1280 Normal+Shift+Ctrl+Alt+Sys - - K_HOME* = iUPxCODE(1) - K_UP* = iUPxCODE(2) - K_PGUP* = iUPxCODE(3) - K_LEFT* = iUPxCODE(4) - K_MIDDLE* = iUPxCODE(5) - K_RIGHT* = iUPxCODE(6) - K_END* = iUPxCODE(7) - K_DOWN* = iUPxCODE(8) - K_PGDN* = iUPxCODE(9) - K_INS* = iUPxCODE(10) - K_DEL* = iUPxCODE(11) - K_PAUSE* = iUPxCODE(12) - K_ESC* = iUPxCODE(13) - K_ccedilla* = iUPxCODE(14) - K_F1* = iUPxCODE(15) - K_F2* = iUPxCODE(16) - K_F3* = iUPxCODE(17) - K_F4* = iUPxCODE(18) - K_F5* = iUPxCODE(19) - K_F6* = iUPxCODE(20) - K_F7* = iUPxCODE(21) - K_F8* = iUPxCODE(22) - K_F9* = iUPxCODE(23) - K_F10* = iUPxCODE(24) - K_F11* = iUPxCODE(25) - K_F12* = iUPxCODE(26) - K_Print* = iUPxCODE(27) - K_Menu* = iUPxCODE(28) - - K_acute* = iUPxCODE(29) # no Shift/Ctrl/Alt - - K_sHOME* = iUPsxCODE(K_HOME) - K_sUP* = iUPsxCODE(K_UP) - K_sPGUP* = iUPsxCODE(K_PGUP) - K_sLEFT* = iUPsxCODE(K_LEFT) - K_sMIDDLE* = iUPsxCODE(K_MIDDLE) - K_sRIGHT* = iUPsxCODE(K_RIGHT) - K_sEND* = iUPsxCODE(K_END) - K_sDOWN* = iUPsxCODE(K_DOWN) - K_sPGDN* = iUPsxCODE(K_PGDN) - K_sINS* = iUPsxCODE(K_INS) - K_sDEL* = iUPsxCODE(K_DEL) - K_sSP* = iUPsxCODE(K_SP) - K_sTAB* = iUPsxCODE(K_TAB) - K_sCR* = iUPsxCODE(K_CR) - K_sBS* = iUPsxCODE(K_BS) - K_sPAUSE* = iUPsxCODE(K_PAUSE) - K_sESC* = iUPsxCODE(K_ESC) - K_sCcedilla* = iUPsxCODE(K_ccedilla) - K_sF1* = iUPsxCODE(K_F1) - K_sF2* = iUPsxCODE(K_F2) - K_sF3* = iUPsxCODE(K_F3) - K_sF4* = iUPsxCODE(K_F4) - K_sF5* = iUPsxCODE(K_F5) - K_sF6* = iUPsxCODE(K_F6) - K_sF7* = iUPsxCODE(K_F7) - K_sF8* = iUPsxCODE(K_F8) - K_sF9* = iUPsxCODE(K_F9) - K_sF10* = iUPsxCODE(K_F10) - K_sF11* = iUPsxCODE(K_F11) - K_sF12* = iUPsxCODE(K_F12) - K_sPrint* = iUPsxCODE(K_Print) - K_sMenu* = iUPsxCODE(K_Menu) - - K_cHOME* = iUPcxCODE(K_HOME) - K_cUP* = iUPcxCODE(K_UP) - K_cPGUP* = iUPcxCODE(K_PGUP) - K_cLEFT* = iUPcxCODE(K_LEFT) - K_cMIDDLE* = iUPcxCODE(K_MIDDLE) - K_cRIGHT* = iUPcxCODE(K_RIGHT) - K_cEND* = iUPcxCODE(K_END) - K_cDOWN* = iUPcxCODE(K_DOWN) - K_cPGDN* = iUPcxCODE(K_PGDN) - K_cINS* = iUPcxCODE(K_INS) - K_cDEL* = iUPcxCODE(K_DEL) - K_cSP* = iUPcxCODE(K_SP) - K_cTAB* = iUPcxCODE(K_TAB) - K_cCR* = iUPcxCODE(K_CR) - K_cBS* = iUPcxCODE(K_BS) - K_cPAUSE* = iUPcxCODE(K_PAUSE) - K_cESC* = iUPcxCODE(K_ESC) - K_cCcedilla* = iUPcxCODE(K_ccedilla) - K_cF1* = iUPcxCODE(K_F1) - K_cF2* = iUPcxCODE(K_F2) - K_cF3* = iUPcxCODE(K_F3) - K_cF4* = iUPcxCODE(K_F4) - K_cF5* = iUPcxCODE(K_F5) - K_cF6* = iUPcxCODE(K_F6) - K_cF7* = iUPcxCODE(K_F7) - K_cF8* = iUPcxCODE(K_F8) - K_cF9* = iUPcxCODE(K_F9) - K_cF10* = iUPcxCODE(K_F10) - K_cF11* = iUPcxCODE(K_F11) - K_cF12* = iUPcxCODE(K_F12) - K_cPrint* = iUPcxCODE(K_Print) - K_cMenu* = iUPcxCODE(K_Menu) - - K_mHOME* = iUPmxCODE(K_HOME) - K_mUP* = iUPmxCODE(K_UP) - K_mPGUP* = iUPmxCODE(K_PGUP) - K_mLEFT* = iUPmxCODE(K_LEFT) - K_mMIDDLE* = iUPmxCODE(K_MIDDLE) - K_mRIGHT* = iUPmxCODE(K_RIGHT) - K_mEND* = iUPmxCODE(K_END) - K_mDOWN* = iUPmxCODE(K_DOWN) - K_mPGDN* = iUPmxCODE(K_PGDN) - K_mINS* = iUPmxCODE(K_INS) - K_mDEL* = iUPmxCODE(K_DEL) - K_mSP* = iUPmxCODE(K_SP) - K_mTAB* = iUPmxCODE(K_TAB) - K_mCR* = iUPmxCODE(K_CR) - K_mBS* = iUPmxCODE(K_BS) - K_mPAUSE* = iUPmxCODE(K_PAUSE) - K_mESC* = iUPmxCODE(K_ESC) - K_mCcedilla* = iUPmxCODE(K_ccedilla) - K_mF1* = iUPmxCODE(K_F1) - K_mF2* = iUPmxCODE(K_F2) - K_mF3* = iUPmxCODE(K_F3) - K_mF4* = iUPmxCODE(K_F4) - K_mF5* = iUPmxCODE(K_F5) - K_mF6* = iUPmxCODE(K_F6) - K_mF7* = iUPmxCODE(K_F7) - K_mF8* = iUPmxCODE(K_F8) - K_mF9* = iUPmxCODE(K_F9) - K_mF10* = iUPmxCODE(K_F10) - K_mF11* = iUPmxCODE(K_F11) - K_mF12* = iUPmxCODE(K_F12) - K_mPrint* = iUPmxCODE(K_Print) - K_mMenu* = iUPmxCODE(K_Menu) - - K_yHOME* = iUPyxCODE(K_HOME) - K_yUP* = iUPyxCODE(K_UP) - K_yPGUP* = iUPyxCODE(K_PGUP) - K_yLEFT* = iUPyxCODE(K_LEFT) - K_yMIDDLE* = iUPyxCODE(K_MIDDLE) - K_yRIGHT* = iUPyxCODE(K_RIGHT) - K_yEND* = iUPyxCODE(K_END) - K_yDOWN* = iUPyxCODE(K_DOWN) - K_yPGDN* = iUPyxCODE(K_PGDN) - K_yINS* = iUPyxCODE(K_INS) - K_yDEL* = iUPyxCODE(K_DEL) - K_ySP* = iUPyxCODE(K_SP) - K_yTAB* = iUPyxCODE(K_TAB) - K_yCR* = iUPyxCODE(K_CR) - K_yBS* = iUPyxCODE(K_BS) - K_yPAUSE* = iUPyxCODE(K_PAUSE) - K_yESC* = iUPyxCODE(K_ESC) - K_yCcedilla* = iUPyxCODE(K_ccedilla) - K_yF1* = iUPyxCODE(K_F1) - K_yF2* = iUPyxCODE(K_F2) - K_yF3* = iUPyxCODE(K_F3) - K_yF4* = iUPyxCODE(K_F4) - K_yF5* = iUPyxCODE(K_F5) - K_yF6* = iUPyxCODE(K_F6) - K_yF7* = iUPyxCODE(K_F7) - K_yF8* = iUPyxCODE(K_F8) - K_yF9* = iUPyxCODE(K_F9) - K_yF10* = iUPyxCODE(K_F10) - K_yF11* = iUPyxCODE(K_F11) - K_yF12* = iUPyxCODE(K_F12) - K_yPrint* = iUPyxCODE(K_Print) - K_yMenu* = iUPyxCODE(K_Menu) - - K_sPlus* = iUPsxCODE(K_plus) - K_sComma* = iUPsxCODE(K_comma) - K_sMinus* = iUPsxCODE(K_minus) - K_sPeriod* = iUPsxCODE(K_period) - K_sSlash* = iUPsxCODE(K_slash) - K_sAsterisk* = iUPsxCODE(K_asterisk) - - K_cupperA* = iUPcxCODE(K_upperA) - K_cupperB* = iUPcxCODE(K_upperB) - K_cupperC* = iUPcxCODE(K_upperC) - K_cupperD* = iUPcxCODE(K_upperD) - K_cupperE* = iUPcxCODE(K_upperE) - K_cupperF* = iUPcxCODE(K_upperF) - K_cupperG* = iUPcxCODE(K_upperG) - K_cupperH* = iUPcxCODE(K_upperH) - K_cupperI* = iUPcxCODE(K_upperI) - K_cupperJ* = iUPcxCODE(K_upperJ) - K_cupperK* = iUPcxCODE(K_upperK) - K_cupperL* = iUPcxCODE(K_upperL) - K_cupperM* = iUPcxCODE(K_upperM) - K_cupperN* = iUPcxCODE(K_upperN) - K_cupperO* = iUPcxCODE(K_upperO) - K_cupperP* = iUPcxCODE(K_upperP) - K_cupperQ* = iUPcxCODE(K_upperQ) - K_cupperR* = iUPcxCODE(K_upperR) - K_cupperS* = iUPcxCODE(K_upperS) - K_cupperT* = iUPcxCODE(K_upperT) - K_cupperU* = iUPcxCODE(K_upperU) - K_cupperV* = iUPcxCODE(K_upperV) - K_cupperW* = iUPcxCODE(K_upperW) - K_cupperX* = iUPcxCODE(K_upperX) - K_cupperY* = iUPcxCODE(K_upperY) - K_cupperZ* = iUPcxCODE(K_upperZ) - K_c1* = iUPcxCODE(K_1) - K_c2* = iUPcxCODE(K_2) - K_c3* = iUPcxCODE(K_3) - K_c4* = iUPcxCODE(K_4) - K_c5* = iUPcxCODE(K_5) - K_c6* = iUPcxCODE(K_6) - K_c7* = iUPcxCODE(K_7) - K_c8* = iUPcxCODE(K_8) - K_c9* = iUPcxCODE(K_9) - K_c0* = iUPcxCODE(K_0) - K_cPlus* = iUPcxCODE(K_plus) - K_cComma* = iUPcxCODE(K_comma) - K_cMinus* = iUPcxCODE(K_minus) - K_cPeriod* = iUPcxCODE(K_period) - K_cSlash* = iUPcxCODE(K_slash) - K_cSemicolon* = iUPcxCODE(K_semicolon) - K_cEqual* = iUPcxCODE(K_equal) - K_cBracketleft* = iUPcxCODE(K_bracketleft) - K_cBracketright* = iUPcxCODE(K_bracketright) - K_cBackslash* = iUPcxCODE(K_backslash) - K_cAsterisk* = iUPcxCODE(K_asterisk) - - K_mupperA* = iUPmxCODE(K_upperA) - K_mupperB* = iUPmxCODE(K_upperB) - K_mupperC* = iUPmxCODE(K_upperC) - K_mupperD* = iUPmxCODE(K_upperD) - K_mupperE* = iUPmxCODE(K_upperE) - K_mupperF* = iUPmxCODE(K_upperF) - K_mupperG* = iUPmxCODE(K_upperG) - K_mupperH* = iUPmxCODE(K_upperH) - K_mupperI* = iUPmxCODE(K_upperI) - K_mupperJ* = iUPmxCODE(K_upperJ) - K_mupperK* = iUPmxCODE(K_upperK) - K_mupperL* = iUPmxCODE(K_upperL) - K_mupperM* = iUPmxCODE(K_upperM) - K_mupperN* = iUPmxCODE(K_upperN) - K_mupperO* = iUPmxCODE(K_upperO) - K_mupperP* = iUPmxCODE(K_upperP) - K_mupperQ* = iUPmxCODE(K_upperQ) - K_mupperR* = iUPmxCODE(K_upperR) - K_mupperS* = iUPmxCODE(K_upperS) - K_mupperT* = iUPmxCODE(K_upperT) - K_mupperU* = iUPmxCODE(K_upperU) - K_mupperV* = iUPmxCODE(K_upperV) - K_mupperW* = iUPmxCODE(K_upperW) - K_mupperX* = iUPmxCODE(K_upperX) - K_mupperY* = iUPmxCODE(K_upperY) - K_mupperZ* = iUPmxCODE(K_upperZ) - K_m1* = iUPmxCODE(K_1) - K_m2* = iUPmxCODE(K_2) - K_m3* = iUPmxCODE(K_3) - K_m4* = iUPmxCODE(K_4) - K_m5* = iUPmxCODE(K_5) - K_m6* = iUPmxCODE(K_6) - K_m7* = iUPmxCODE(K_7) - K_m8* = iUPmxCODE(K_8) - K_m9* = iUPmxCODE(K_9) - K_m0* = iUPmxCODE(K_0) - K_mPlus* = iUPmxCODE(K_plus) - K_mComma* = iUPmxCODE(K_comma) - K_mMinus* = iUPmxCODE(K_minus) - K_mPeriod* = iUPmxCODE(K_period) - K_mSlash* = iUPmxCODE(K_slash) - K_mSemicolon* = iUPmxCODE(K_semicolon) - K_mEqual* = iUPmxCODE(K_equal) - K_mBracketleft* = iUPmxCODE(K_bracketleft) - K_mBracketright* = iUPmxCODE(K_bracketright) - K_mBackslash* = iUPmxCODE(K_backslash) - K_mAsterisk* = iUPmxCODE(K_asterisk) - - K_yA* = iUPyxCODE(K_upperA) - K_yB* = iUPyxCODE(K_upperB) - K_yC* = iUPyxCODE(K_upperC) - K_yD* = iUPyxCODE(K_upperD) - K_yE* = iUPyxCODE(K_upperE) - K_yF* = iUPyxCODE(K_upperF) - K_yG* = iUPyxCODE(K_upperG) - K_yH* = iUPyxCODE(K_upperH) - K_yI* = iUPyxCODE(K_upperI) - K_yJ* = iUPyxCODE(K_upperJ) - K_yK* = iUPyxCODE(K_upperK) - K_yL* = iUPyxCODE(K_upperL) - K_yM* = iUPyxCODE(K_upperM) - K_yN* = iUPyxCODE(K_upperN) - K_yO* = iUPyxCODE(K_upperO) - K_yP* = iUPyxCODE(K_upperP) - K_yQ* = iUPyxCODE(K_upperQ) - K_yR* = iUPyxCODE(K_upperR) - K_yS* = iUPyxCODE(K_upperS) - K_yT* = iUPyxCODE(K_upperT) - K_yU* = iUPyxCODE(K_upperU) - K_yV* = iUPyxCODE(K_upperV) - K_yW* = iUPyxCODE(K_upperW) - K_yX* = iUPyxCODE(K_upperX) - K_yY* = iUPyxCODE(K_upperY) - K_yZ* = iUPyxCODE(K_upperZ) - K_y1* = iUPyxCODE(K_1) - K_y2* = iUPyxCODE(K_2) - K_y3* = iUPyxCODE(K_3) - K_y4* = iUPyxCODE(K_4) - K_y5* = iUPyxCODE(K_5) - K_y6* = iUPyxCODE(K_6) - K_y7* = iUPyxCODE(K_7) - K_y8* = iUPyxCODE(K_8) - K_y9* = iUPyxCODE(K_9) - K_y0* = iUPyxCODE(K_0) - K_yPlus* = iUPyxCODE(K_plus) - K_yComma* = iUPyxCODE(K_comma) - K_yMinus* = iUPyxCODE(K_minus) - K_yPeriod* = iUPyxCODE(K_period) - K_ySlash* = iUPyxCODE(K_slash) - K_ySemicolon* = iUPyxCODE(K_semicolon) - K_yEqual* = iUPyxCODE(K_equal) - K_yBracketleft* = iUPyxCODE(K_bracketleft) - K_yBracketright* = iUPyxCODE(K_bracketright) - K_yBackslash* = iUPyxCODE(K_backslash) - K_yAsterisk* = iUPyxCODE(K_asterisk) - -proc controlsOpen*(): cint {.cdecl, importc: "IupControlsOpen", dynlib: dllname.} -proc controlsClose*() {.cdecl, importc: "IupControlsClose", dynlib: dllname.} - -proc oldValOpen*() {.cdecl, importc: "IupOldValOpen", dynlib: dllname.} -proc oldTabsOpen*() {.cdecl, importc: "IupOldTabsOpen", dynlib: dllname.} - -proc colorbar*(): PIhandle {.cdecl, importc: "IupColorbar", dynlib: dllname.} -proc cells*(): PIhandle {.cdecl, importc: "IupCells", dynlib: dllname.} -proc colorBrowser*(): PIhandle {.cdecl, importc: "IupColorBrowser", dynlib: dllname.} -proc gauge*(): PIhandle {.cdecl, importc: "IupGauge", dynlib: dllname.} -proc dial*(theType: cstring): PIhandle {.cdecl, importc: "IupDial", dynlib: dllname.} -proc matrix*(action: cstring): PIhandle {.cdecl, importc: "IupMatrix", dynlib: dllname.} - -# IupMatrix utilities -proc matSetAttribute*(ih: PIhandle, name: cstring, lin, col: cint, - value: cstring) {. - cdecl, importc: "IupMatSetAttribute", dynlib: dllname.} -proc matStoreAttribute*(ih: PIhandle, name: cstring, lin, col: cint, - value: cstring) {.cdecl, - importc: "IupMatStoreAttribute", dynlib: dllname.} -proc matGetAttribute*(ih: PIhandle, name: cstring, lin, col: cint): cstring {. - cdecl, importc: "IupMatGetAttribute", dynlib: dllname.} -proc matGetInt*(ih: PIhandle, name: cstring, lin, col: cint): cint {. - cdecl, importc: "IupMatGetInt", dynlib: dllname.} -proc matGetFloat*(ih: PIhandle, name: cstring, lin, col: cint): cfloat {. - cdecl, importc: "IupMatGetFloat", dynlib: dllname.} -proc matSetfAttribute*(ih: PIhandle, name: cstring, lin, col: cint, - format: cstring) {.cdecl, - importc: "IupMatSetfAttribute", - dynlib: dllname, varargs.} - -# Used by IupColorbar -const - IUP_PRIMARY* = -1 - IUP_SECONDARY* = -2 - -# Initialize PPlot widget class -proc pPlotOpen*() {.cdecl, importc: "IupPPlotOpen", dynlib: dllname.} - -# Create an PPlot widget instance -proc pPlot*: PIhandle {.cdecl, importc: "IupPPlot", dynlib: dllname.} - -# Add dataset to plot -proc pPlotBegin*(ih: PIhandle, strXdata: cint) {. - cdecl, importc: "IupPPlotBegin", dynlib: dllname.} -proc pPlotAdd*(ih: PIhandle, x, y: cfloat) {. - cdecl, importc: "IupPPlotAdd", dynlib: dllname.} -proc pPlotAddStr*(ih: PIhandle, x: cstring, y: cfloat) {. - cdecl, importc: "IupPPlotAddStr", dynlib: dllname.} -proc pPlotEnd*(ih: PIhandle): cint {. - cdecl, importc: "IupPPlotEnd", dynlib: dllname.} - -proc pPlotInsertStr*(ih: PIhandle, index, sampleIndex: cint, x: cstring, - y: cfloat) {.cdecl, importc: "IupPPlotInsertStr", - dynlib: dllname.} -proc pPlotInsert*(ih: PIhandle, index, sampleIndex: cint, - x, y: cfloat) {. - cdecl, importc: "IupPPlotInsert", dynlib: dllname.} - -# convert from plot coordinates to pixels -proc pPlotTransform*(ih: PIhandle, x, y: cfloat, ix, iy: var cint) {. - cdecl, importc: "IupPPlotTransform", dynlib: dllname.} - -# Plot on the given device. Uses a "cdCanvas*". -proc pPlotPaintTo*(ih: PIhandle, cnv: pointer) {. - cdecl, importc: "IupPPlotPaintTo", dynlib: dllname.} - - diff --git a/tools/kochdocs.nim b/tools/kochdocs.nim index ba2963cfd..81ab265f2 100644 --- a/tools/kochdocs.nim +++ b/tools/kochdocs.nim @@ -127,7 +127,6 @@ lib/system/channels.nim withoutIndex = """ lib/wrappers/mysql.nim -lib/wrappers/iup.nim lib/wrappers/sqlite3.nim lib/wrappers/postgres.nim lib/wrappers/tinyc.nim From 7d640e0943679a772f9595ef3be56fe2158b0912 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Wed, 4 Nov 2020 14:24:40 +0800 Subject: [PATCH 0903/3768] fix #15835 (#15838) * fix #15835 * add tests --- lib/pure/json.nim | 2 ++ tests/stdlib/t15835.nim | 17 +++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 tests/stdlib/t15835.nim diff --git a/lib/pure/json.nim b/lib/pure/json.nim index f3145301f..a3f7a782e 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -1052,6 +1052,8 @@ when defined(nimFixedForwardGeneric): dst = jsonNode.bval proc initFromJson(dst: var JsonNode; jsonNode: JsonNode; jsonPath: var string) = + if jsonNode == nil: + raise newException(KeyError, "key not found: " & jsonPath) dst = jsonNode.copy proc initFromJson[T: SomeInteger](dst: var T; jsonNode: JsonNode, jsonPath: var string) = diff --git a/tests/stdlib/t15835.nim b/tests/stdlib/t15835.nim new file mode 100644 index 000000000..bddfa87aa --- /dev/null +++ b/tests/stdlib/t15835.nim @@ -0,0 +1,17 @@ +import json + +type + Foo = object + ii*: int + data*: JsonNode + +block: + const jt = """{"ii": 123, "data": ["some", "data"]}""" + let js = parseJson(jt) + discard js.to(Foo) + +block: + const jt = """{"ii": 123}""" + let js = parseJson(jt) + doAssertRaises(KeyError): + echo js.to(Foo) From f17555770e816580b475674be23458da51b10dfd Mon Sep 17 00:00:00 2001 From: c-blake Date: Wed, 4 Nov 2020 10:56:22 -0500 Subject: [PATCH 0904/3768] Clarify the sense in which Nim supports recursive iterators in the (#15834) manual, the tutorial, and the `tbintree` test. --- doc/manual.rst | 43 ++++++++++++++++++++++++++++++++++--- doc/tut2.rst | 4 ++-- tests/generics/tbintree.nim | 4 ++-- 3 files changed, 44 insertions(+), 7 deletions(-) diff --git a/doc/manual.rst b/doc/manual.rst index d09bb4c76..d9f9cc9b9 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -4031,10 +4031,13 @@ Closure iterators and inline iterators have some restrictions: 1. For now, a closure iterator cannot be executed at compile time. 2. ``return`` is allowed in a closure iterator but not in an inline iterator (but rarely useful) and ends the iteration. -3. Neither inline nor closure iterators can be recursive. +3. Neither inline nor closure iterators can be (directly)* recursive. 4. Neither inline nor closure iterators have the special ``result`` variable. 5. Closure iterators are not supported by the js backend. +(*) Closure iterators can be co-recursive with a factory proc which results +in similar syntax to a recursive iterator. More details follow. + Iterators that are neither marked ``{.closure.}`` nor ``{.inline.}`` explicitly default to being inline, but this may change in future versions of the implementation. @@ -4129,7 +4132,41 @@ parameters of an outer factory proc: for f in foo(): echo f +The call can be made more like an inline iterator with a for loop macro: +.. code-block:: nim + import macros + macro toItr(x: ForLoopStmt): untyped = + let expr = x[0] + let call = x[1][1] # Get foo out of toItr(foo) + let body = x[2] + result = quote do: + block: + let itr = `call` + for `expr` in itr(): + `body` + + for f in toItr(mycount(1, 4)): # using early `proc mycount` + echo f + +Because of full backend function call aparatus involvment, closure iterator +invocation is typically higher cost than inline iterators. Adornment by +a macro wrapper at the call site like this is a possibly useful reminder. + +The factory ``proc``, as an ordinary procedure, can be recursive. The +above macro allows such recursion to look much like a recursive iterator +would. For example: + +.. code-block:: nim + proc recCountDown(n: int): iterator(): int = + result = iterator(): int = + if n > 0: + yield n + for e in toItr(recCountDown(n - 1)): + yield e + + for i in toItr(recCountDown(6)): # Emits: 6 5 4 3 2 1 + echo i Converters ========== @@ -4619,8 +4656,8 @@ The following example shows a generic binary tree can be modeled: iterator preorder*[T](root: BinaryTree[T]): T = # Preorder traversal of a binary tree. - # Since recursive iterators are not yet implemented, - # this uses an explicit stack (which is more efficient anyway): + # This uses an explicit stack (which is more efficient than + # a recursive iterator factory). var stack: seq[BinaryTree[T]] = @[root] while stack.len > 0: var n = stack.pop() diff --git a/doc/tut2.rst b/doc/tut2.rst index 577b65622..94725a5d8 100644 --- a/doc/tut2.rst +++ b/doc/tut2.rst @@ -511,8 +511,8 @@ containers: iterator preorder*[T](root: BinaryTree[T]): T = # Preorder traversal of a binary tree. - # Since recursive iterators are not yet implemented, - # this uses an explicit stack (which is more efficient anyway): + # This uses an explicit stack (which is more efficient than + # a recursive iterator factory). var stack: seq[BinaryTree[T]] = @[root] while stack.len > 0: var n = stack.pop() diff --git a/tests/generics/tbintree.nim b/tests/generics/tbintree.nim index a1a13c7b5..83f14406b 100644 --- a/tests/generics/tbintree.nim +++ b/tests/generics/tbintree.nim @@ -55,8 +55,8 @@ proc find*[Ty2](b: PBinaryTree[Ty2], data: Ty2): bool = iterator preorder*[T](root: PBinaryTree[T]): T = # Preorder traversal of a binary tree. - # Since recursive iterators are not yet implemented, - # this uses an explicit stack: + # This uses an explicit stack (which is more efficient than + # a recursive iterator factory). var stack: seq[PBinaryTree[T]] = @[root] while stack.len > 0: var n = stack.pop() From 4c19c5dfae01638ae66fc2c1561762d94fb36a1a Mon Sep 17 00:00:00 2001 From: cooldome Date: Wed, 4 Nov 2020 18:52:53 +0000 Subject: [PATCH 0905/3768] fix static[Slice[T]] as argument issue (#15842) --- compiler/sigmatch.nim | 2 +- tests/types/tisopr.nim | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 2cf2846c0..d5039fcad 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1079,7 +1079,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, return typeRel(c, f, lastSon(aOrig), flags) if a.kind == tyGenericInst and - skipTypes(f, {tyVar, tyLent, tySink}).kind notin { + skipTypes(f, {tyStatic, tyVar, tyLent, tySink}).kind notin { tyGenericBody, tyGenericInvocation, tyGenericInst, tyGenericParam} + tyTypeClasses: return typeRel(c, f, lastSon(a), flags) diff --git a/tests/types/tisopr.nim b/tests/types/tisopr.nim index 1bbd0da94..533cf05be 100644 --- a/tests/types/tisopr.nim +++ b/tests/types/tisopr.nim @@ -109,3 +109,15 @@ block: doAssert Foo2[int,float|int] is Foo doAssert Foo2[int,float|int] isnot Bar doAssert int is (int|float) + + +block: + # Slice[T] as static type issue + type + MyEnum = enum + x1, x2, x3, x4, x5, x6 + + proc enumGen[T: enum](s: static[Slice[T]]) = + doAssert($s.a & " " & $s.b == "x1 x3") + + enumGen(x1..x3) From fdd25ed19c7adccaf94984e02d4222596ec08224 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Thu, 5 Nov 2020 18:31:44 +0800 Subject: [PATCH 0906/3768] close #8457 (#15844) --- tests/pragmas/tcustom_pragma.nim | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/pragmas/tcustom_pragma.nim b/tests/pragmas/tcustom_pragma.nim index 756396529..a4a200c34 100644 --- a/tests/pragmas/tcustom_pragma.nim +++ b/tests/pragmas/tcustom_pragma.nim @@ -367,3 +367,14 @@ block: let x = RefType2() for fieldName, fieldSym in fieldPairs(x[]): doAssert hasCustomPragma(fieldSym, myCustomPragma) + +# bug 8457 +block: + template world {.pragma.} + + type + Hello = ref object + a: float32 + b {.world.}: int + + discard Hello(a: 1.0, b: 12) From dfa2f011d7e7856074729d3d809d2848b9b897c1 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Thu, 5 Nov 2020 18:55:42 +0800 Subject: [PATCH 0907/3768] close #10307(add testcase for #10307) (#15840) --- tests/magics/t10307.nim | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 tests/magics/t10307.nim diff --git a/tests/magics/t10307.nim b/tests/magics/t10307.nim new file mode 100644 index 000000000..111b0d221 --- /dev/null +++ b/tests/magics/t10307.nim @@ -0,0 +1,23 @@ +discard """ + cmd: "nim c -d:useGcAssert $file" + output: '''running someProc(true) +res: yes +yes +running someProc(false) +res: +''' +""" + +proc someProc(x:bool):cstring = + var res:string = "" + if x: + res = "yes" + echo "res: ", res + GC_ref(res) + result = res + +echo "running someProc(true)" +echo someProc(true) + +echo "running someProc(false)" +echo someProc(false) \ No newline at end of file From cae92f670a00ca1a343667dfeb5dfef208086f21 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Thu, 5 Nov 2020 20:11:22 +0800 Subject: [PATCH 0908/3768] change non-working example to runnableExamples (#15841) --- lib/pure/sugar.nim | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/lib/pure/sugar.nim b/lib/pure/sugar.nim index dd44c731d..d0f85ad17 100644 --- a/lib/pure/sugar.nim +++ b/lib/pure/sugar.nim @@ -57,13 +57,11 @@ proc createProcType(p, b: NimNode): NimNode {.compileTime.} = macro `=>`*(p, b: untyped): untyped = ## Syntax sugar for anonymous procedures. - ## - ## .. code-block:: nim - ## - ## proc passTwoAndTwo(f: (int, int) -> int): int = - ## f(2, 2) - ## - ## passTwoAndTwo((x, y) => x + y) # 4 + runnableExamples: + proc passTwoAndTwo(f: (int, int) -> int): int = + f(2, 2) + + doAssert passTwoAndTwo((x, y) => x + y) == 4 var params = @[ident"auto"] From a8af664e8ba8672e736c72971a18c0e00ba9b086 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Thu, 5 Nov 2020 21:00:08 +0800 Subject: [PATCH 0909/3768] fix #15463 (#15831) --- compiler/lowerings.nim | 1 + compiler/vmgen.nim | 2 +- tests/vm/tswap.nim | 12 +++++++++++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index 40d61b779..e9e704075 100644 --- a/compiler/lowerings.nim +++ b/compiler/lowerings.nim @@ -138,6 +138,7 @@ proc lowerSwap*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNod var temp = newSym(skVar, getIdent(g.cache, genPrefix), nextId(idgen), owner, n.info, owner.options) temp.typ = n[1].typ incl(temp.flags, sfFromGeneric) + incl(temp.flags, sfGenSym) var v = newNodeI(nkVarSection, n.info) let tempAsNode = newSymNode(temp) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 61c7fefd9..bcb7faa47 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1122,7 +1122,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = c.freeTemp(d) of mSwap: unused(c, n, dest) - c.gen(lowerSwap(c.graph, n, c.idgen, if c.prc == nil: c.module else: c.prc.sym)) + c.gen(lowerSwap(c.graph, n, c.idgen, if c.prc == nil or c.prc.sym == nil: c.module else: c.prc.sym)) of mIsNil: genUnaryABC(c, n, dest, opcIsNil) of mParseBiggestFloat: if dest < 0: dest = c.getTemp(n.typ) diff --git a/tests/vm/tswap.nim b/tests/vm/tswap.nim index 4243b5a71..bdbe5528c 100644 --- a/tests/vm/tswap.nim +++ b/tests/vm/tswap.nim @@ -3,7 +3,9 @@ nimout: ''' x.data = @[10] y = @[11] x.data = @[11] -y = @[10]''' +y = @[10] +@[3, 2, 1] +''' """ # bug #2946 @@ -22,3 +24,11 @@ proc testSwap(): int {.compiletime.} = result = 99 const something = testSwap() + +# bug #15463 +block: + static: + var s = @[1, 2, 3] + swap(s[0], s[2]) + + echo s From 8e1fa84b0d44bc6fb995992becf659d62b36713a Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Thu, 5 Nov 2020 21:00:49 +0800 Subject: [PATCH 0910/3768] fix #15663 (#15839) [backport:1.4] --- lib/system/widestrs.nim | 7 +++++++ tests/stdlib/t15663.nim | 7 +++++++ 2 files changed, 14 insertions(+) create mode 100644 tests/stdlib/t15663.nim diff --git a/lib/system/widestrs.nim b/lib/system/widestrs.nim index 6d54bce9b..955fab46a 100644 --- a/lib/system/widestrs.nim +++ b/lib/system/widestrs.nim @@ -214,3 +214,10 @@ proc `$`*(w: WideCString, estimate: int, replacement: int = 0xFFFD): string = proc `$`*(s: WideCString): string = result = s $ 80 + +when defined(nimv2): + proc `$`*(s: WideCStringObj, estimate: int, replacement: int = 0xFFFD): string = + `$`(s.data, estimate, replacement) + + proc `$`*(s: WideCStringObj): string = + $(s.data) diff --git a/tests/stdlib/t15663.nim b/tests/stdlib/t15663.nim new file mode 100644 index 000000000..1ad5677fd --- /dev/null +++ b/tests/stdlib/t15663.nim @@ -0,0 +1,7 @@ +discard """ + cmd: "nim c --gc:arc $file" + output: "Test" +""" + +let ws = newWideCString("Test") +echo ws \ No newline at end of file From c4cc907433a3ac2e5373cb0190ece145bfc047bb Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Thu, 5 Nov 2020 21:01:28 +0800 Subject: [PATCH 0911/3768] fix adding empty sequence to HTTP headers (#15783) * fix adding empty sequence to HTTP headers * add tests --- lib/pure/httpcore.nim | 11 +++++++---- tests/stdlib/thttpcore.nim | 27 +++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/lib/pure/httpcore.nim b/lib/pure/httpcore.nim index af1bfc088..d796c788d 100644 --- a/lib/pure/httpcore.nim +++ b/lib/pure/httpcore.nim @@ -166,9 +166,12 @@ proc `[]=`*(headers: HttpHeaders, key, value: string) = proc `[]=`*(headers: HttpHeaders, key: string, value: seq[string]) = ## Sets the header entries associated with ``key`` to the specified list of - ## values. - ## Replaces any existing values. - headers.table[headers.toCaseInsensitive(key)] = value + ## values. Replaces any existing values. If ``value`` is empty, + ## deletes the header entries associated with ``key``. + if value.len > 0: + headers.table[headers.toCaseInsensitive(key)] = value + else: + headers.table.del(headers.toCaseInsensitive(key)) proc add*(headers: HttpHeaders, key, value: string) = ## Adds the specified value to the specified key. Appends to any existing @@ -179,7 +182,7 @@ proc add*(headers: HttpHeaders, key, value: string) = headers.table[headers.toCaseInsensitive(key)].add(value) proc del*(headers: HttpHeaders, key: string) = - ## Delete the header entries associated with ``key`` + ## Deletes the header entries associated with ``key`` headers.table.del(headers.toCaseInsensitive(key)) iterator pairs*(headers: HttpHeaders): tuple[key, value: string] = diff --git a/tests/stdlib/thttpcore.nim b/tests/stdlib/thttpcore.nim index 33c24453e..cb1a0875f 100644 --- a/tests/stdlib/thttpcore.nim +++ b/tests/stdlib/thttpcore.nim @@ -51,3 +51,30 @@ suite "httpcore": doAssert parseHeader("Accept: foo, bar") == (key: "Accept", value: @["foo", "bar"]) doAssert parseHeader("Accept: foo, bar, prologue") == (key: "Accept", value: @["foo", "bar", "prologue"]) doAssert parseHeader("Accept: foo, bar, prologue, starlight") == (key: "Accept", value: @["foo", "bar", "prologue", "starlight"]) + + test "add empty sequence to HTTP headers": + block: + var headers = newHttpHeaders() + headers["empty"] = @[] + + doAssert not headers.hasKey("empty") + + block: + var headers = newHttpHeaders() + headers["existing"] = "true" + headers["existing"] = @[] + + doAssert not headers.hasKey("existing") + + block: + var headers = newHttpHeaders() + headers["existing"] = @["true"] + headers["existing"] = @[] + + doAssert not headers.hasKey("existing") + + block: + var headers = newHttpHeaders() + headers["existing"] = @[] + headers["existing"] = @["true"] + doAssert headers.hasKey("existing") From adde5ba6b06e12f74c7746f93883ad009cfb00b8 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Thu, 5 Nov 2020 21:04:24 +0800 Subject: [PATCH 0912/3768] document #15618 (#15810) * document #15618 * Update lib/pure/unittest.nim * Update lib/pure/unittest.nim Co-authored-by: Juan Carlos * Update lib/pure/unittest.nim Co-authored-by: Andreas Rumpf Co-authored-by: Juan Carlos --- lib/pure/unittest.nim | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim index 0536956e9..617b8ab78 100644 --- a/lib/pure/unittest.nim +++ b/lib/pure/unittest.nim @@ -92,6 +92,15 @@ ## discard v[4] ## ## echo "suite teardown: run once after the tests" +## +## Limitations/Bugs +## ================ +## Since `check` will rewrite some programs for +## supporting checkpoints (namely assigns expressions to variables). +## Some type conversions are not supported. +## For example `check 4.0 == 2 + 2` won't work. But `doAssert 4.0 == 2 + 2` works. +## Make sure both sides of the operator (such as `==`, `>=` and so on) have the same type. +## import std/private/since import std/exitprocs From 9455a0c3e324e13a4a49da7ccecc098bfe895a7f Mon Sep 17 00:00:00 2001 From: cooldome Date: Thu, 5 Nov 2020 13:51:45 +0000 Subject: [PATCH 0913/3768] close #11142 (#15847) --- tests/types/tisopr.nim | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/types/tisopr.nim b/tests/types/tisopr.nim index 533cf05be..67f1ce0c1 100644 --- a/tests/types/tisopr.nim +++ b/tests/types/tisopr.nim @@ -121,3 +121,17 @@ block: doAssert($s.a & " " & $s.b == "x1 x3") enumGen(x1..x3) + +block: + # issue #11142 + type + MyObjParam[N: static int] = object + x: int + + MyObj[P: static MyObjParam] = object + y: int + + const P = MyObjParam[256](x: 2) + let Q = MyObj[P](y: 2) + doAssert($Q == "(y: 2)") + From 3aab6a409d0a02874717b1e10ca54a32da35cd1a Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 5 Nov 2020 14:56:44 +0100 Subject: [PATCH 0914/3768] micro improvements (#15849) --- doc/destructors.rst | 2 +- lib/system/excpt.nim | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/destructors.rst b/doc/destructors.rst index ff09d6644..66f1043b3 100644 --- a/doc/destructors.rst +++ b/doc/destructors.rst @@ -522,7 +522,7 @@ a form of copy elision. To see how and when we can do that, think about this question: In `dest = src` when do we really have to *materialize* the full copy? - Only if `dest` or `src` are mutated -afterward. If `dest` is a local variable that is simple to analyze. And if `src` is a +afterwards. If `dest` is a local variable that is simple to analyze. And if `src` is a location derived from a formal parameter, we also know it is not mutated! In other words, we do a compile-time copy-on-write analysis. diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index c189f116d..04c5d3ce8 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -554,7 +554,8 @@ proc nimFrame(s: PFrame) {.compilerRtl, inl, raises: [].} = when defined(cpp) and appType != "lib" and not gotoBasedExceptions and not defined(js) and not defined(nimscript) and - hostOS != "standalone" and hostOS != "any" and not defined(noCppExceptions): + hostOS != "standalone" and hostOS != "any" and not defined(noCppExceptions) and + not defined(nimQuirky): type StdException {.importcpp: "std::exception", header: "".} = object From 3af7818af4bb541e24146fa8f8334cf7c0b53128 Mon Sep 17 00:00:00 2001 From: cooldome Date: Thu, 5 Nov 2020 17:55:52 +0000 Subject: [PATCH 0915/3768] Fix #12636 (#15850) * close #11142 * fix #12636 * undo unwanted changes * fix illegal recursion case --- compiler/sizealignoffsetimpl.nim | 8 ++++++-- tests/misc/tsizeof.nim | 14 +++++++++++++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/compiler/sizealignoffsetimpl.nim b/compiler/sizealignoffsetimpl.nim index 2c764fe12..b50777c9e 100644 --- a/compiler/sizealignoffsetimpl.nim +++ b/compiler/sizealignoffsetimpl.nim @@ -258,12 +258,16 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) = of tyArray: computeSizeAlign(conf, typ[1]) - let elemSize = typ[1].size + let elemSize = typ[1].size + let len = lengthOrd(conf, typ[0]) if elemSize < 0: typ.size = elemSize typ.align = int16(elemSize) + elif len < 0: + typ.size = szUnknownSize + typ.align = szUnknownSize else: - typ.size = toInt64Checked(lengthOrd(conf, typ[0]) * int32(elemSize), szTooBigSize) + typ.size = toInt64Checked(len * int32(elemSize), szTooBigSize) typ.align = typ[1].align of tyUncheckedArray: diff --git a/tests/misc/tsizeof.nim b/tests/misc/tsizeof.nim index a09265667..0d96a5e04 100644 --- a/tests/misc/tsizeof.nim +++ b/tests/misc/tsizeof.nim @@ -361,6 +361,16 @@ testinstance: y: int32 z: AnotherEnum + Stack[N: static int, T: object] = object + pad: array[128 - sizeof(array[N, ptr T]) - sizeof(int) - sizeof(pointer), byte] + stack: array[N, ptr T] + len*: int + rawMem: ptr array[N, T] + + Stack2[T: object] = object + pad: array[128 - sizeof(array[sizeof(T), ptr T]), byte] + + const trivialSize = sizeof(TrivialType) # needs to be able to evaluate at compile time proc main(): void = @@ -377,6 +387,8 @@ testinstance: var po : PaddingOfSetEnum33 var capo: MyCustomAlignPackedObject var issue15516: MyObject + var issue12636_1: Stack[5, MyObject] + var issue12636_2: Stack2[MyObject] var e1: Enum1 @@ -395,7 +407,7 @@ testinstance: else: doAssert sizeof(SimpleAlignment) > 10 - testSizeAlignOf(t,a,b,c,d,e,f,g,ro,go,po, e1, e2, e4, e8, eoa, eob, capo, issue15516) + testSizeAlignOf(t,a,b,c,d,e,f,g,ro,go,po, e1, e2, e4, e8, eoa, eob, capo, issue15516, issue12636_1, issue12636_2) type WithBitsize {.objectconfig.} = object From d1bea1f71f0318f08ddd80b75706a602753b55d4 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Thu, 5 Nov 2020 21:02:14 +0100 Subject: [PATCH 0916/3768] Tiny unittest doc fix --- lib/pure/unittest.nim | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim index 617b8ab78..c0b2e7c1e 100644 --- a/lib/pure/unittest.nim +++ b/lib/pure/unittest.nim @@ -95,9 +95,8 @@ ## ## Limitations/Bugs ## ================ -## Since `check` will rewrite some programs for -## supporting checkpoints (namely assigns expressions to variables). -## Some type conversions are not supported. +## Since `check` will rewrite some expressions for supporting checkpoints +## (namely assigns expressions to variables), some type conversions are not supported. ## For example `check 4.0 == 2 + 2` won't work. But `doAssert 4.0 == 2 + 2` works. ## Make sure both sides of the operator (such as `==`, `>=` and so on) have the same type. ## From fa5f225efc7161110b0d4cf57e86646f2b0d97f8 Mon Sep 17 00:00:00 2001 From: cooldome Date: Thu, 5 Nov 2020 22:29:05 +0000 Subject: [PATCH 0917/3768] fix #15609 (#15856) * fix #15609 * fix test --- compiler/injectdestructors.nim | 19 +++++++++---------- tests/arc/tmovebug.nim | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 0b893ede8..7e64ebfdc 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -824,14 +824,10 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode = of nkAsgn, nkFastAsgn: if hasDestructor(c, n[0].typ) and n[1].kind notin {nkProcDef, nkDo, nkLambda} and not isCursor(n[0], c): - # rule (self-assignment-removal): - if n[1].kind == nkSym and n[0].kind == nkSym and n[0].sym == n[1].sym: - result = newNodeI(nkEmpty, n.info) - else: - if n[0].kind in {nkDotExpr, nkCheckedFieldExpr}: - cycleCheck(n, c) - assert n[1].kind notin {nkAsgn, nkFastAsgn} - result = moveOrCopy(p(n[0], c, s, mode), n[1], c, s) + if n[0].kind in {nkDotExpr, nkCheckedFieldExpr}: + cycleCheck(n, c) + assert n[1].kind notin {nkAsgn, nkFastAsgn} + result = moveOrCopy(p(n[0], c, s, mode), n[1], c, s) elif isDiscriminantField(n[0]): result = c.genDiscriminantAsgn(s, n) else: @@ -954,8 +950,11 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, isDecl = false): PNod result = c.genSink(dest, p(ri, c, s, consumed), isDecl) of nkObjConstr, nkTupleConstr, nkClosure, nkCharLit..nkNilLit: result = c.genSink(dest, p(ri, c, s, consumed), isDecl) - of nkSym: - if isSinkParam(ri.sym) and isLastRead(ri, c): + of nkSym: + if dest.kind == nkSym and dest.sym == ri.sym: + # rule (self-assignment-removal): + result = newNodeI(nkEmpty, dest.info) + elif isSinkParam(ri.sym) and isLastRead(ri, c): # Rule 3: `=sink`(x, z); wasMoved(z) let snk = c.genSink(dest, ri, isDecl) result = newTree(nkStmtList, snk, c.genWasMoved(ri)) diff --git a/tests/arc/tmovebug.nim b/tests/arc/tmovebug.nim index 3b7a7c5df..61105b44e 100644 --- a/tests/arc/tmovebug.nim +++ b/tests/arc/tmovebug.nim @@ -70,6 +70,9 @@ king hi try bye +() +() +() ''' """ @@ -524,3 +527,33 @@ proc getScope2(): string = "else" echo getScope2() + + +#-------------------------------------------------------------------- +#bug #15609 + +type + Wrapper = object + discard + +proc newWrapper(): ref Wrapper = + new(result) + result + + +proc newWrapper2(a: int): ref Wrapper = + new(result) + if a > 0: + result + else: + new(Wrapper) + + +let w1 = newWrapper() +echo $w1[] + +let w2 = newWrapper2(1) +echo $w2[] + +let w3 = newWrapper2(-1) +echo $w3[] \ No newline at end of file From cdd459dd60e7cf69868ac23e754e8047df6afddc Mon Sep 17 00:00:00 2001 From: cooldome Date: Fri, 6 Nov 2020 09:25:43 +0000 Subject: [PATCH 0918/3768] static[T] related fixes (#15853) * close #9679 * close #7546 * close #9520 * close #6177 --- compiler/semtypes.nim | 2 +- tests/statictypes/tstatictypes.nim | 101 +++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+), 1 deletion(-) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 21667895a..467645aee 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -159,7 +159,7 @@ proc semSet(c: PContext, n: PNode, prev: PType): PType = var base = semTypeNode(c, n[1], nil) addSonSkipIntLit(result, base, c.idgen) if base.kind in {tyGenericInst, tyAlias, tySink}: base = lastSon(base) - if base.kind != tyGenericParam: + if base.kind notin {tyGenericParam, tyGenericInvocation}: if not isOrdinalType(base, allowEnumWithHoles = true): localError(c.config, n.info, errOrdinalTypeExpected) elif lengthOrd(c.config, base) > MaxSetElements: diff --git a/tests/statictypes/tstatictypes.nim b/tests/statictypes/tstatictypes.nim index 50dc60e09..b5c45850a 100644 --- a/tests/statictypes/tstatictypes.nim +++ b/tests/statictypes/tstatictypes.nim @@ -10,6 +10,8 @@ b is 2 times a 17 ['\x00', '\x00', '\x00', '\x00'] heyho +Val1 +Val1 ''' """ @@ -239,3 +241,102 @@ let t = T[true](foo: "hey") let u = U[false](bar: "ho") echo t.foo, u.bar + +#------------------------------------------------------------------------------ +# issue #9679 + +discard """ + output: '''''' +""" +type + Foo*[T] = object + bar*: int + dummy: T + +proc initFoo(T: type, bar: int): Foo[T] = + result.bar = 1 + +proc fails[T](x: static Foo[T]) = # Change to non-static and it compiles + doAssert($x == "(bar: 1, dummy: 0)") + +block: + const foo = initFoo(int, 2) + fails(foo) + + +import macros, tables + +var foo{.compileTime.} = [ + "Foo", + "Bar" +] + +var bar{.compileTime.} = { + 0: "Foo", + 1: "Bar" +}.toTable() + +macro fooM(): untyped = + for i, val in foo: + echo i, ": ", val + +macro barM(): untyped = + for i, val in bar: + echo i, ": ", val + +macro fooParam(x: static array[2, string]): untyped = + for i, val in x: + echo i, ": ", val + +macro barParam(x: static Table[int, string]): untyped = + for i, val in x: + echo i, ": ", val + +fooM() +barM() +fooParam(foo) +barParam(bar) + + +#----------------------------------------------------------------------------------------- +# issue #7546 +type + rangeB[N: static[int16]] = range[0'i16 .. N] + setB[N: static[int16]] = set[rangeB[N]] + +block: + var s : setB[14'i16] + + +#----------------------------------------------------------------------------------------- +# issue #9520 + +type + MyEnum = enum + Val1, Val2 + +proc myproc(a: static[MyEnum], b: int) = + if b < 0: + myproc(a, -b) + + echo $a + +myproc(Val1, -10) + + +#------------------------------------------------------------------------------------------ +# issue #6177 + +type + G[N,M:static[int], T] = object + o: T + +proc newG[N,M:static[int],T](x:var G[N,M,T], y:T) = + x.o = y+10*N+100*M + +proc newG[N,M:static[int],T](x:T):G[N,M,T] = result.newG(x) + +var x:G[2,3,int] +x.newG(4) +var y = newG[2,3,int](4) + From 218ff27b70c05340069d262204f785290467aa00 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Fri, 6 Nov 2020 18:40:14 +0800 Subject: [PATCH 0919/3768] fix #15851 (#15852) * fix #15851 * {.cast(noSideEffect).} --- lib/pure/httpcore.nim | 16 ++++++++++------ tests/effects/tstrict_funcs.nim | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/lib/pure/httpcore.nim b/lib/pure/httpcore.nim index d796c788d..b3d1da8d6 100644 --- a/lib/pure/httpcore.nim +++ b/lib/pure/httpcore.nim @@ -126,12 +126,14 @@ func newHttpHeaders*(keyValuePairs: new result result.table = newTable[string, seq[string]]() result.isTitleCase = titleCase + for pair in keyValuePairs: let key = result.toCaseInsensitive(pair.key) - if key in result.table: - result.table[key].add(pair.val) - else: - result.table[key] = @[pair.val] + {.cast(noSideEffect).}: + if key in result.table: + result.table[key].add(pair.val) + else: + result.table[key] = @[pair.val] func `$`*(headers: HttpHeaders): string {.inline.} = @@ -148,7 +150,8 @@ func `[]`*(headers: HttpHeaders, key: string): HttpHeaderValues = ## ## To access multiple values of a key, use the overloaded ``[]`` below or ## to get all of them access the ``table`` field directly. - return headers.table[headers.toCaseInsensitive(key)].HttpHeaderValues + {.cast(noSideEffect).}: + return headers.table[headers.toCaseInsensitive(key)].HttpHeaderValues converter toString*(values: HttpHeaderValues): string = return seq[string](values)[0] @@ -157,7 +160,8 @@ func `[]`*(headers: HttpHeaders, key: string, i: int): string = ## Returns the ``i``'th value associated with the given key. If there are ## no values associated with the key or the ``i``'th value doesn't exist, ## an exception is raised. - return headers.table[headers.toCaseInsensitive(key)][i] + {.cast(noSideEffect).}: + return headers.table[headers.toCaseInsensitive(key)][i] proc `[]=`*(headers: HttpHeaders, key, value: string) = ## Sets the header entries associated with ``key`` to the specified value. diff --git a/tests/effects/tstrict_funcs.nim b/tests/effects/tstrict_funcs.nim index 75ed9c8d9..0028e7d7e 100644 --- a/tests/effects/tstrict_funcs.nim +++ b/tests/effects/tstrict_funcs.nim @@ -2,7 +2,7 @@ discard """ cmd: "nim c --experimental:strictFuncs --experimental:views $file" """ -import tables, streams, nre, parsecsv, uri +import tables, streams, nre, parsecsv, uri, httpcore type Contig2Reads = TableRef[string, seq[string]] From 9fd67958b97c0254c3b4d8762a7003e317ec21bb Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Fri, 6 Nov 2020 18:42:43 +0800 Subject: [PATCH 0920/3768] follow #11707(add pragmas examples for =>) (#15863) --- lib/pure/sugar.nim | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/pure/sugar.nim b/lib/pure/sugar.nim index d0f85ad17..46dbb1ece 100644 --- a/lib/pure/sugar.nim +++ b/lib/pure/sugar.nim @@ -57,12 +57,22 @@ proc createProcType(p, b: NimNode): NimNode {.compileTime.} = macro `=>`*(p, b: untyped): untyped = ## Syntax sugar for anonymous procedures. + ## It also supports pragmas. runnableExamples: proc passTwoAndTwo(f: (int, int) -> int): int = f(2, 2) doAssert passTwoAndTwo((x, y) => x + y) == 4 + type + Bot = object + call: proc (): string {.nosideEffect.} + + var myBot = Bot() + + myBot.call = () {.nosideEffect.} => "I'm a bot." + doAssert myBot.call() == "I'm a bot." + var params = @[ident"auto"] name = newEmptyNode() From fb60e35a5495c530f8cf5c7a8cc3005495d089f8 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Fri, 6 Nov 2020 23:39:20 +0800 Subject: [PATCH 0921/3768] close #8829(add testcase for #8829) (#15866) --- tests/overload/t8829.nim | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 tests/overload/t8829.nim diff --git a/tests/overload/t8829.nim b/tests/overload/t8829.nim new file mode 100644 index 000000000..a688e18cb --- /dev/null +++ b/tests/overload/t8829.nim @@ -0,0 +1,18 @@ +block: + let txt = "Hello World" + + template `[]`[T](p: ptr T, span: Slice[int]): untyped = + toOpenArray(cast[ptr array[0, T]](p)[], span.a, span.b) + + doAssert $cast[ptr uint8](txt[0].unsafeAddr)[0 ..< txt.len] == + "[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]" + + +block: + let txt = "Hello World" + + template `[]`[T](p: ptr T, span: Slice[int]): untyped = + toOpenArray(cast[ptr array[0, T]](p)[], span.a, span.b) + + doAssert $cast[ptr uint8](txt[0].unsafeAddr)[0 ..< txt.len] == + "[72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]" From 60c364fb224a8b55dc7cacc5a589c569cf202ec5 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Fri, 6 Nov 2020 16:49:51 +0100 Subject: [PATCH 0922/3768] Closes #12897 (#15867) --- tests/statictypes/tstatictypes.nim | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/statictypes/tstatictypes.nim b/tests/statictypes/tstatictypes.nim index b5c45850a..359ae4c9f 100644 --- a/tests/statictypes/tstatictypes.nim +++ b/tests/statictypes/tstatictypes.nim @@ -340,3 +340,12 @@ var x:G[2,3,int] x.newG(4) var y = newG[2,3,int](4) + +#------------------------------------------------------------------------------------------ +# issue #12897 + +type + TileCT[n: static int] = object + a: array[n, int] + Tile = TileCT #Commenting this out to make it work + From 3c85aa9e53cbaae5fae83f9fd29de3bdb107c7aa Mon Sep 17 00:00:00 2001 From: Ivan Bobev Date: Fri, 6 Nov 2020 20:56:09 +0200 Subject: [PATCH 0923/3768] Make `{.requiresInit.}` to work for distinct types (#15869) Make `requiresInit` pragma to work for distinct types in addition to objects. Tagging of distinct types with `requiresInit` pragma was already supported, but its impact wasn't applied. Now its behavior when applied on distinct types is as follows. Given the following distinct type definitions: ```nim type DistinctObject {.requiresInit, borrow: `.`.} = distinct MyObject DistinctString {.requiresInit.} = distinct string ``` The following code blocks will fail to compile: ```nim var foo: DistinctFoo foo.x = "test" doAssert foo.x == "test" ``` ```nim var s: DistinctString s = "test" doAssert s == "test" ``` But these ones will compile successfully: ```nim let foo = DistinctFoo(Foo(x: "test")) doAssert foo.x == "test" ``` ```nim let s = "test" doAssert s == "test" ``` --- changelog.md | 2 ++ compiler/semobjconstr.nim | 21 ++++++++++++++------- compiler/semstmts.nim | 3 ++- compiler/typeallowed.nim | 3 ++- doc/manual.rst | 30 ++++++++++++++++++++++++++++++ tests/distinct/tdistinct.nim | 32 ++++++++++++++++++++++++++++++++ 6 files changed, 82 insertions(+), 9 deletions(-) diff --git a/changelog.md b/changelog.md index 871fb229d..53730ecc5 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,8 @@ ## Standard library additions and changes +- Make `{.requiresInit.}` pragma to work for `distinct` types. + - `prelude` now works with the JavaScript target. - Added `ioutils` module containing `duplicate` and `duplicateTo` to duplicate `FileHandle` using C function `dup` and `dup2`. diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim index 64a27d1db..42529e1e5 100644 --- a/compiler/semobjconstr.nim +++ b/compiler/semobjconstr.nim @@ -355,15 +355,22 @@ proc computeRequiresInit(c: PContext, t: PType): bool = proc defaultConstructionError(c: PContext, t: PType, info: TLineInfo) = var objType = t - while objType.kind != tyObject: + while objType.kind notin {tyObject, tyDistinct}: objType = objType.lastSon assert objType != nil - var constrCtx = initConstrContext(objType, newNodeI(nkObjConstr, info)) - let initResult = semConstructTypeAux(c, constrCtx, {}) - assert constrCtx.missingFields.len > 0 - localError(c.config, info, - "The $1 type doesn't have a default value. The following fields must be initialized: $2.", - [typeToString(t), listSymbolNames(constrCtx.missingFields)]) + if objType.kind == tyObject: + var constrCtx = initConstrContext(objType, newNodeI(nkObjConstr, info)) + let initResult = semConstructTypeAux(c, constrCtx, {}) + assert constrCtx.missingFields.len > 0 + localError(c.config, info, + "The $1 type doesn't have a default value. The following fields must " & + "be initialized: $2.", + [typeToString(t), listSymbolNames(constrCtx.missingFields)]) + elif objType.kind == tyDistinct: + localError(c.config, info, + "The $1 distinct type doesn't have a default value.", [typeToString(t)]) + else: + assert false, "Must not enter here." proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = var t = semTypeNode(c, n[0], nil) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 2a058eee2..4b79cd546 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -611,7 +611,8 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = if def.kind == nkEmpty: let actualType = v.typ.skipTypes({tyGenericInst, tyAlias, tyUserTypeClassInst}) - if actualType.kind == tyObject and actualType.requiresInit: + if actualType.kind in {tyObject, tyDistinct} and + actualType.requiresInit: defaultConstructionError(c, v.typ, v.info) else: checkNilable(c, v) diff --git a/compiler/typeallowed.nim b/compiler/typeallowed.nim index a8210508c..e163b914a 100644 --- a/compiler/typeallowed.nim +++ b/compiler/typeallowed.nim @@ -260,4 +260,5 @@ proc directViewType*(t: PType): ViewTypeKind = result = noView proc requiresInit*(t: PType): bool = - (t.flags * {tfRequiresInit, tfNotNil} != {}) or classifyViewType(t) != noView + (t.flags * {tfRequiresInit, tfNeedsFullInit, tfNotNil} != {}) or + classifyViewType(t) != noView diff --git a/doc/manual.rst b/doc/manual.rst index d9f9cc9b9..4e5b861b1 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -2697,6 +2697,36 @@ the variable has been initialized and does not rely on syntactic properties: x = a() # use x +`requiresInit` pragma can also be applyied to `distinct` types. + +Given the following distinct type definitions: + +.. code-block:: nim + type + DistinctObject {.requiresInit, borrow: `.`.} = distinct MyObject + DistinctString {.requiresInit.} = distinct string + +The following code blocks will fail to compile: + +.. code-block:: nim + var foo: DistinctFoo + foo.x = "test" + doAssert foo.x == "test" + +.. code-block:: nim + var s: DistinctString + s = "test" + doAssert s == "test" + +But these ones will compile successfully: + +.. code-block:: nim + let foo = DistinctFoo(Foo(x: "test")) + doAssert foo.x == "test" + +.. code-block:: nim + let s = "test" + doAssert s == "test" Let statement ------------- diff --git a/tests/distinct/tdistinct.nim b/tests/distinct/tdistinct.nim index 2c0196745..d64f33443 100644 --- a/tests/distinct/tdistinct.nim +++ b/tests/distinct/tdistinct.nim @@ -106,3 +106,35 @@ type FooD = distinct int proc `<=`(a, b: FooD): bool {.borrow.} for f in [FooD(0): "Foo"]: echo f + +block tRequiresInit: + template accept(x) = + static: doAssert compiles(x) + + template reject(x) = + static: doAssert not compiles(x) + + type + Foo = object + x: string + + DistinctFoo {.requiresInit, borrow: `.`.} = distinct Foo + DistinctString {.requiresInit.} = distinct string + + reject: + var foo: DistinctFoo + foo.x = "test" + doAssert foo.x == "test" + + accept: + let foo = DistinctFoo(Foo(x: "test")) + doAssert foo.x == "test" + + reject: + var s: DistinctString + s = "test" + doAssert s == "test" + + accept: + let s = "test" + doAssert s == "test" From 5828620d47b3100e30da280442946e02c5eca18d Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sat, 7 Nov 2020 15:58:52 +0800 Subject: [PATCH 0924/3768] uint and uint64 is Ordinal type since nim 1.0.0 (#15873) --- doc/manual.rst | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/doc/manual.rst b/doc/manual.rst index 4e5b861b1..fa28582a7 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -883,9 +883,7 @@ Ordinal types have the following characteristics: than the largest value produces a panic or a static error. Integers, bool, characters, and enumeration types (and subranges of these -types) belong to ordinal types. For reasons of simplicity of implementation -the types ``uint`` and ``uint64`` are not ordinal types. (This will be changed -in later versions of the language.) +types) belong to ordinal types. A distinct type is an ordinal type if its base type is an ordinal type. From 8f7a013cc7bf0a1f47aec8d5b2037951a36eb4f1 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sat, 7 Nov 2020 15:59:47 +0800 Subject: [PATCH 0925/3768] close #8558(add testcase for #8558) (#15872) --- tests/concepts/t8558.nim | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 tests/concepts/t8558.nim diff --git a/tests/concepts/t8558.nim b/tests/concepts/t8558.nim new file mode 100644 index 000000000..acb2de30e --- /dev/null +++ b/tests/concepts/t8558.nim @@ -0,0 +1,26 @@ +discard """ + output: '''10 +9 +8 +7 +6 +5 +4 +3 +2 +1 +go! +''' +""" + +type Integral = concept x + x == 0 is bool + x - 1 is type(x) + +proc countToZero(n: Integral) = + if n == 0: echo "go!" + else: + echo n + countToZero(n-1) + +countToZero(10) \ No newline at end of file From c07807bedf1749fa9d3e826d3d736907fefbe568 Mon Sep 17 00:00:00 2001 From: Bung Date: Sat, 7 Nov 2020 16:00:41 +0800 Subject: [PATCH 0926/3768] Fix #15706 (#15846) * fix #15706 Underflow not detected when using dec on distinct ranges * fix #15846 first, jsgen even doesn't care of range in this op --- compiler/ccgexprs.nim | 2 +- tests/overflw/tdistinct_range.nim | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 tests/overflw/tdistinct_range.nim diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 1eb6caeb3..a3fbeca5f 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2269,7 +2269,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = initLocExpr(p, e[1], a) initLocExpr(p, e[2], b) - let ranged = skipTypes(e[1].typ, {tyGenericInst, tyAlias, tySink, tyVar, tyLent}) + let ranged = skipTypes(e[1].typ, {tyGenericInst, tyAlias, tySink, tyVar, tyLent, tyDistinct}) let res = binaryArithOverflowRaw(p, ranged, a, b, if underlying.kind == tyInt64: fun64[op] else: fun[op]) diff --git a/tests/overflw/tdistinct_range.nim b/tests/overflw/tdistinct_range.nim new file mode 100644 index 000000000..f53515d45 --- /dev/null +++ b/tests/overflw/tdistinct_range.nim @@ -0,0 +1,6 @@ +discard """ + outputsub: "Error: unhandled exception: over- or underflow [OverflowDefect]" + exitcode: "1" +""" +var x: distinct range[0..5] +dec(x) \ No newline at end of file From 552075d10ea579a40216843815d419261d327c03 Mon Sep 17 00:00:00 2001 From: sherbst Date: Sat, 7 Nov 2020 08:58:49 -0600 Subject: [PATCH 0927/3768] Fix typo in lexer.nim (#15876) --- compiler/lexer.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/lexer.nim b/compiler/lexer.nim index e020236b1..b55dd3585 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -10,7 +10,7 @@ # This scanner is handwritten for efficiency. I used an elegant buffering # scheme which I have not seen anywhere else: # We guarantee that a whole line is in the buffer. Thus only when scanning -# the \n or \r character we have to check wether we need to read in the next +# the \n or \r character we have to check whether we need to read in the next # chunk. (\n or \r already need special handling for incrementing the line # counter; choosing both \n and \r allows the scanner to properly read Unix, # DOS or Macintosh text files, even when it is not the native format. From 203bddf307493b1065325759fef549499f2a3fc2 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sun, 8 Nov 2020 11:02:12 +0800 Subject: [PATCH 0928/3768] close #11637(add testcase for #11637) (#15879) * close #11637(add testcase for #11637) * Update tests/vm/t11637.nim --- tests/vm/t11637.nim | 52 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 tests/vm/t11637.nim diff --git a/tests/vm/t11637.nim b/tests/vm/t11637.nim new file mode 100644 index 000000000..c061c6641 --- /dev/null +++ b/tests/vm/t11637.nim @@ -0,0 +1,52 @@ +type Foo = ref object + val: int + +proc `+`(a, b: Foo): Foo = + Foo(val: a.val + b.val) + +proc `*`(a: Foo, b: int): Foo = + Foo(val: a.val * b) + +proc `+=`(a: var Foo, b: Foo) = + a = Foo( + val: a.val + b.val + ) + +proc foobar(a, b, c: Foo): tuple[bar, baz, buzz: Foo] = + + let foo = a + b + c + result.bar = foo * 2 + + result.baz = foo * 3 + result.buzz = result.baz + + result.buzz += a * 10000 + result.baz += b + result.buzz += b + + +block: # Compile-Time + let + a {.compileTime.} = Foo(val: 1) + b {.compileTime.} = Foo(val: 2) + c {.compileTime.} = Foo(val: 3) + r {.compileTime.} = foobar(a, b, c) + + static: + doAssert r.bar.val == 12 + doAssert r.baz.val == 20 + doAssert r.buzz.val == 10020 + +#################################### + +block: # Run-time + let + a = Foo(val: 1) + b = Foo(val: 2) + c = Foo(val: 3) + r = foobar(a, b, c) + + # Expected values + doAssert r.bar.val == 12 + doAssert r.baz.val == 20 + doAssert r.buzz.val == 10020 From 49f68122e658ba7bcc13340fe157e276b4033a90 Mon Sep 17 00:00:00 2001 From: Luca Guzzon Date: Sun, 8 Nov 2020 04:51:40 +0100 Subject: [PATCH 0929/3768] Console apps in Windows can raise OSError (#15874) Console apps in Windows implementation can raise OSError so I've fixed it, see below some examples for reference: - https://github.com/nim-lang/Nim/blob/c07807bedf1749fa9d3e826d3d736907fefbe568/lib/pure/terminal.nim#L170 - https://github.com/nim-lang/Nim/blob/c07807bedf1749fa9d3e826d3d736907fefbe568/lib/pure/terminal.nim#L178 - https://github.com/nim-lang/Nim/blob/c07807bedf1749fa9d3e826d3d736907fefbe568/lib/pure/terminal.nim#L191-L192 - https://github.com/nim-lang/Nim/blob/c07807bedf1749fa9d3e826d3d736907fefbe568/lib/pure/terminal.nim#L196-L197 - ... --- lib/pure/terminal.nim | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index ac6720771..3bda5b7aa 100644 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -37,7 +37,10 @@ type var gTerm {.threadvar.}: owned(PTerminal) -proc newTerminal(): owned(PTerminal) {.gcsafe, raises: [].} +when defined(windows) and defined(consoleapp): + proc newTerminal(): owned(PTerminal) {.gcsafe, raises: [OSError].} +else: + proc newTerminal(): owned(PTerminal) {.gcsafe, raises: [].} proc getTerminal(): PTerminal {.inline.} = if isNil(gTerm): From 5db181f377e30f3a06e70b48e14337722d4dc114 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Mon, 9 Nov 2020 11:19:06 +0300 Subject: [PATCH 0930/3768] Nimgrep improvements 2 (#15612) * nimgrep: speed up by threads and Channels * nimgrep: add --bin, --text, --count options * nimgrep: add --sortTime option * allow Peg in all matches including --includeFile, --excludeFile, --excludeDir * add --match and --noMatch options * add --includeDir option * add --limit (-m) and --onlyAscii (-o) options * fix performance regression introduced in nimgrep improvements #12779 * better error handling * add option --fit * fix groups in --replace * fix flushing, --replace, improve --count * use "." as the default directory, not full path * fix --fit for Windows * force target to C for macosx * validate non-negative int input for options #15318 * switch nimgrep to using --gc:orc * address review: implement cropping in matches,... * implement stdin/pipe & revise --help * address stylistic review & add limitations --- testament/categories.nim | 3 + tools/nimgrep.nim | 1346 +++++++++++++++++++++++++++++--------- tools/nimgrep.nim.cfg | 9 +- 3 files changed, 1055 insertions(+), 303 deletions(-) diff --git a/testament/categories.nim b/testament/categories.nim index 07d35f6fb..fa9391055 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -262,6 +262,9 @@ proc debuggerTests(r: var TResults, cat: Category, options: string) = if fileExists("tools/nimgrep.nim"): var t = makeTest("tools/nimgrep", options & " --debugger:on", cat) t.spec.action = actionCompile + # force target to C because of MacOS 10.15 SDK headers bug + # https://github.com/nim-lang/Nim/pull/15612#issuecomment-712471879 + t.spec.targets = { targetC } testSpec r, t # ------------------------- JS tests ------------------------------------------ diff --git a/tools/nimgrep.nim b/tools/nimgrep.nim index 138f1680c..1e563435c 100644 --- a/tools/nimgrep.nim +++ b/tools/nimgrep.nim @@ -8,86 +8,247 @@ # import - os, strutils, parseopt, pegs, re, terminal + os, strutils, parseopt, pegs, re, terminal, osproc, tables, algorithm, times const - Version = "1.5" - Usage = "nimgrep - Nim Grep Utility Version " & Version & """ + Version = "1.6.0" + Usage = "nimgrep - Nim Grep Searching and Replacement Utility Version " & + Version & """ + + (c) 2012-2020 Andreas Rumpf - (c) 2012 Andreas Rumpf Usage: - nimgrep [options] [pattern] [replacement] (file/directory)* +* To search: + nimgrep [options] PATTERN [(FILE/DIRECTORY)*/-] +* To replace: + nimgrep [options] PATTERN --replace REPLACEMENT (FILE/DIRECTORY)*/- +* To list file names: + nimgrep [options] --filenames [PATTERN] [(FILE/DIRECTORY)*] + +Positional arguments, from left to right: +* PATERN is either Regex (default) or Peg if --peg is specified. + PATTERN and REPLACEMENT should be skipped when --stdin is specified. +* REPLACEMENT supports $1, $# notations for captured groups in PATTERN. + Note: --replace mode DOES NOT ask confirmation unless --confirm is specified! +* Final arguments are a list of paths (FILE/DIRECTORY) or a standalone + minus '-' (pipe) or not specified (empty). Note for the empty case: when + no FILE/DIRECTORY/- is specified nimgrep DOES NOT read the pipe, but + searches files in the current dir instead! + - read buffer once from stdin: pipe or terminal input; + in --replace mode the result is directed to stdout; + it's not compatible with --stdin, --filenames, --confirm + (empty) current directory '.' is assumed (not with --replace) + For any given DIRECTORY nimgrep searches only its immediate files without + traversing sub-directories unless --recursive is specified. + In replacement mode all 3 positional arguments are required to avoid damaging. + Options: - --find, -f find the pattern (default) - --replace, -! replace the pattern - --peg pattern is a peg - --re pattern is a regular expression (default) - --rex, -x use the "extended" syntax for the regular expression - so that whitespace is not significant - --recursive, -r process directories recursively - --follow follow all symlinks when processing recursively +* Mode of operation: + --find, -f find the PATTERN (default) + --replace, -! replace the PATTERN to REPLACEMENT, rewriting the files --confirm confirm each occurrence/replacement; there is a chance to abort any time without touching the file - --stdin read pattern from stdin (to avoid the shell's confusing - quoting rules) - --word, -w the match should have word boundaries (buggy for pegs!) - --ignoreCase, -i be case insensitive - --ignoreStyle, -y be style insensitive + --filenames just list filenames. Provide a PATTERN to find it in + the filenames (not in the contents of a file) or run + with empty pattern to just list all files: + nimgrep --filenames # In current directory + nimgrep --filenames "" DIRECTORY # Note empty pattern "" + +* Interprete patterns: + --peg PATTERN and PAT are Peg + --re PATTERN and PAT are regular expressions (default) + --rex, -x use the "extended" syntax for the regular expression + so that whitespace is not significant + --word, -w matches should have word boundaries (buggy for pegs!) + --ignoreCase, -i be case insensitive in PATTERN and PAT + --ignoreStyle, -y be style insensitive in PATTERN and PAT + NOTE: PATERN and patterns PAT (see below in other options) are all either + Regex or Peg simultaneously and options --rex, --word, --ignoreCase, + --ignoreStyle are applied to all of them. + +* File system walk: + --recursive, -r process directories recursively + --follow follow all symlinks when processing recursively --ext:EX1|EX2|... only search the files with the given extension(s), empty one ("--ext") means files with missing extension --noExt:EX1|... exclude files having given extension(s), use empty one to skip files with no extension (like some binary files are) - --includeFile:PAT include only files whose names match the given regex PAT - --excludeFile:PAT skip files whose names match the given regex pattern PAT - --excludeDir:PAT skip directories whose names match the given regex PAT - --nocolor output will be given without any colours - --color[:always] force color even if output is redirected + --includeFile:PAT search only files whose names contain pattern PAT + --excludeFile:PAT skip files whose names contain pattern PAT + --includeDir:PAT search only files with whole directory path containing PAT + --excludeDir:PAT skip directories whose name (not path) contain pattern PAT + --if,--ef,--id,--ed abbreviations of 4 options above + --sortTime order files by the last modification time (default: off): + -s[:asc|desc] ascending (recent files go last) or descending + +* Filter file content: + --match:PAT select files containing a (not displayed) match of PAT + --noMatch:PAT select files not containing any match of PAT + --bin:on|off|only process binary files? (detected by \0 in first 1K bytes) + (default: on - binary and text files treated the same way) + --text, -t process only text files, the same as --bin:off + +* Represent results: + --nocolor output will be given without any colors + --color[:on] force color even if output is redirected (default: auto) --colorTheme:THEME select color THEME from 'simple' (default), 'bnw' (black and white) ,'ack', or 'gnu' (GNU grep) + --count only print counts of matches for files that matched + --context:N, -c:N print N lines of leading context before every match and + N lines of trailing context after it (default N: 0) --afterContext:N, -a:N print N lines of trailing context after every match --beforeContext:N, -b:N print N lines of leading context before every match - --context:N, -c:N print N lines of leading context before every match and - N lines of trailing context after it --group, -g group matches by file --newLine, -l display every matching line starting from a new line + --cols[:N] limit max displayed columns/width of output lines from + files by N characters, cropping overflows (default: off) + --cols:auto, -% calculate columns from terminal width for every line + --onlyAscii, -@ display only printable ASCII Latin characters 0x20-0x7E + substitutions: 0 -> ^@, 1 -> ^A, ... 0x1F -> ^_, + 0x7F -> '7F, ..., 0xFF -> 'FF +* Miscellaneous: + --threads:N, -j:N speed up search by N additional workers (default: 0, off) + --stdin read PATTERN from stdin (to avoid the shell's confusing + quoting rules) and, if --replace given, REPLACEMENT --verbose be verbose: list every processed file - --filenames find the pattern in the filenames, not in the contents - of the file --help, -h shows this help --version, -v shows the version """ +# Limitations / ideas / TODO: +# * No unicode support with --cols +# * Consider making --onlyAscii default, since dumping binary data has +# stability and security repercussions +# * Mode - reads entire buffer by whole from stdin, which is bad for streaming. +# To implement line-by-line reading after adding option to turn off +# multiline matches +# * Add some form of file pre-processing, e.g. feed binary files to utility +# `strings` and then do the search inside these strings +# * Add --showCol option to also show column (of match), not just line; it +# makes it easier when jump to line+col in an editor or on terminal + + +# Search results for a file are modelled by these levels: +# FileResult -> Block -> Output/Chunk -> SubLine +# +# 1. SubLine is an entire line or its part. +# +# 2. Chunk, which is a sequence of SubLine, represents a match and its +# surrounding context. +# Output is a Chunk or one of auxiliary results like an openError. +# +# 3. Block, which is a sequence of Chunks, is not present as a separate type. +# It will just be separated from another Block by newline when there is +# more than 3 lines in it. +# Here is an example of a Block where only 1 match is found and +# 1 line before and 1 line after of context are required: +# +# ...a_line_before...................................... <<\n") - elif newLine: + elif newLine and optFilenames notin options and optPipe notin options: if oneline: printBlockFile(filename) printBlockLineN(":" & $line & ":") @@ -231,234 +405,434 @@ proc blockHeader(filename: string, line: int|string, replMode=false) = printBlockLineN($line.`$`.align(alignment) & ":") stdout.write("\n") -proc lineHeader(filename: string, line: int|string, isMatch: bool) = +proc newLn(curCol: var Column) = + stdout.write("\n") + curCol.file = 0 + curCol.terminal = 0 + +# We reserve 10+3 chars on the right in --cols mode (optLimitChars). +# If the current match touches this right margin, subLine before it will +# be cropped (even if space is enough for subLine after the match — we +# currently don't have a way to know it since we get it afterwards). +const matchPaddingFromRight = 10 +const ellipsis = "..." + +proc lineHeader(filename: string, line: int|string, isMatch: bool, + curCol: var Column) = let lineSym = if isMatch: $line & ":" else: $line & " " - if not newLine: + if not newLine and optFilenames notin options and optPipe notin options: if oneline: printFile(filename) printLineN(":" & lineSym, isMatch) + curcol.terminal += filename.len + 1 + lineSym.len else: printLineN(lineSym.align(alignment+1), isMatch) - stdout.write(" ") + curcol.terminal += lineSym.align(alignment+1).len + stdout.write(" "); curCol.terminal += 1 + curCol.terminal = curCol.terminal mod termWidth + if optLimitChars in options and + curCol.terminal > limitCharUsr - matchPaddingFromRight - ellipsis.len: + newLn(curCol) -proc printMatch(fileName: string, mi: MatchInfo) = - let lines = mi.match.splitLines() - for i, l in lines: - if i > 0: - lineHeader(filename, mi.lineBeg + i, isMatch = true) - writeColored(l) - if i < lines.len - 1: - stdout.write("\n") - -proc printLinesBefore(si: SearchInfo, curMi: MatchInfo, nLines: int, - replMode=false) = - # start block: print 'linesBefore' lines before current match `curMi` - let first = beforePattern(si.buf, curMi.first-1, nLines) - let lines = splitLines(substr(si.buf, first, curMi.first-1)) - let startLine = curMi.lineBeg - lines.len + 1 - blockHeader(si.filename, curMi.lineBeg, replMode=replMode) - for i, l in lines: - lineHeader(si.filename, startLine + i, isMatch = (i == lines.len - 1)) - stdout.write(l) - if i < lines.len - 1: - stdout.write("\n") - -proc printLinesAfter(si: SearchInfo, mi: MatchInfo, nLines: int) = - # finish block: print 'linesAfter' lines after match `mi` - let s = si.buf - let last = afterPattern(s, mi.last+1, nLines) - let lines = splitLines(substr(s, mi.last+1, last)) - if lines.len == 0: # EOF - stdout.write("\n") +proc reserveChars(mi: MatchInfo): int = + if optLimitChars in options: + let patternChars = afterPattern(mi.match, 0) + 1 + result = patternChars + ellipsis.len + matchPaddingFromRight else: - stdout.write(lines[0]) # complete the line after match itself - stdout.write("\n") - let skipLine = # workaround posix line ending at the end of file - if last == s.len-1 and s.len >= 2 and s[^1] == '\l' and s[^2] != '\c': 1 - else: 0 - for i in 1 ..< lines.len - skipLine: - lineHeader(si.filename, mi.lineEnd + i, isMatch = false) - stdout.write(lines[i]) - stdout.write("\n") - if linesAfter + linesBefore >= 2 and not newLine: stdout.write("\n") + result = 0 -proc printBetweenMatches(si: SearchInfo, prevMi: MatchInfo, curMi: MatchInfo) = +# Our substitutions of non-printable symbol to ASCII character are similar to +# those of programm 'less'. +const lowestAscii = 0x20 # lowest ASCII Latin printable symbol (@) +const largestAscii = 0x7e +const by2ascii = 2 # number of ASCII chars to represent chars < lowestAscii +const by3ascii = 3 # number of ASCII chars to represent chars > largestAscii + +proc printExpanded(s: string, curCol: var Column, isMatch: bool, + limitChar: int) = + # Print taking into account tabs and optOnlyAscii (and also optLimitChar: + # the proc called from printCropped but we need to check column < limitChar + # also here, since exact cut points are known only after tab expansion). + # With optOnlyAscii non-ascii chars are highlighted even in matches. + # + # use buffer because: + # 1) we need to print non-ascii character inside matches while keeping the + # amount of color escape sequences minimal. + # 2) there is a report that fwrite buffering is slow on MacOS + # https://github.com/nim-lang/Nim/pull/15612#discussion_r510538326 + const bufSize = 8192 # typical for fwrite too + var buffer: string + const normal = 0 + const special = 1 + var lastAdded = normal + template dumpBuf() = + if lastAdded == normal: + printContents(buffer, isMatch) + else: + printSpecial(buffer) + template addBuf(i: int, s: char|string, size: int) = + if lastAdded != i or buffer.len + size > bufSize: + dumpBuf() + buffer.setlen(0) + buffer.add s + lastAdded = i + for c in s: + let charsAllowed = limitChar - curCol.terminal + if charsAllowed <= 0: + break + if lowestAscii <= int(c) and int(c) <= largestAscii: # ASCII latin + addBuf(normal, c, 1) + curCol.file += 1; curCol.terminal += 1 + elif (not optOnlyAscii) and c != '\t': # the same, print raw + addBuf(normal, c, 1) + curCol.file += 1; curCol.terminal += 1 + elif c == '\t': + let spaces = 8 - (curCol.file mod 8) + let spacesAllowed = min(spaces, charsAllowed) + curCol.file += spaces + curCol.terminal += spacesAllowed + if expandTabs: + if optOnlyAscii: # print a nice box for tab + addBuf(special, " ", 1) + addBuf(normal, " ".repeat(spacesAllowed-1), spacesAllowed-1) + else: + addBuf(normal, " ".repeat(spacesAllowed), spacesAllowed) + else: + addBuf(normal, '\t', 1) + else: # substitute characters that are not ACSII Latin + if int(c) < lowestAscii: + let substitute = char(int(c) + 0x40) # use common "control codes" + addBuf(special, "^" & substitute, by2ascii) + curCol.terminal += by2ascii + else: # int(c) > largestAscii + curCol.terminal += by3ascii + let substitute = '\'' & c.BiggestUInt.toHex(2) + addBuf(special, substitute, by3ascii) + curCol.file += 1 + if buffer.len > 0: + dumpBuf() + +template nextCharacter(c: char, file: var int, term: var int) = + if lowestAscii <= int(c) and int(c) <= largestAscii: # ASCII latin + file += 1 + term += 1 + elif (not optOnlyAscii) and c != '\t': # the same, print raw + file += 1 + term += 1 + elif c == '\t': + term += 8 - (file mod 8) + file += 8 - (file mod 8) + elif int(c) < lowestAscii: + file += 1 + term += by2ascii + else: # int(c) > largestAscii: + file += 1 + term += by3ascii + +proc calcTermLen(s: string, firstCol: int, chars: int, fromLeft: bool): int = + # calculate additional length added by Tabs expansion and substitutions + var col = firstCol + var first, last: int + if fromLeft: + first = max(0, s.len - chars) + last = s.len - 1 + else: + first = 0 + last = min(s.len - 1, chars - 1) + for c in s[first .. last]: + nextCharacter(c, col, result) + +proc printCropped(s: string, curCol: var Column, fromLeft: bool, + limitChar: int, isMatch = false) = + # print line `s`, may be cropped if option --cols was set + const eL = ellipsis.len + if optLimitChars notin options: + if not expandTabs and not optOnlyAscii: # for speed mostly + printContents(s, isMatch) + else: + printExpanded(s, curCol, isMatch, limitChar) + else: # limit columns, expand Tabs is also forced + var charsAllowed = limitChar - curCol.terminal + if fromLeft and charsAllowed < eL: + charsAllowed = eL + if (not fromLeft) and charsAllowed <= 0: + # already overflown and ellipsis shold be in place + return + let fullLenWithin = calcTermLen(s, curCol.file, charsAllowed, fromLeft) + # additional length from Tabs and special symbols + let addLen = fullLenWithin - min(s.len, charsAllowed) + # determine that the string is guaranteed to fit within `charsAllowed` + let fits = + if s.len > charsAllowed: + false + else: + if isMatch: fullLenWithin <= charsAllowed - eL + else: fullLenWithin <= charsAllowed + if fits: + printExpanded(s, curCol, isMatch, limitChar = high(int)) + else: + if fromLeft: + printBold ellipsis + curCol.terminal += eL + # find position `pos` where the right side of line will fit charsAllowed + var col = 0 + var term = 0 + var pos = min(s.len, max(0, s.len - (charsAllowed - eL))) + while pos <= s.len - 1: + let c = s[pos] + nextCharacter(c, col, term) + if term >= addLen: + break + inc pos + curCol.file = pos + # TODO don't expand tabs when cropped from the left - difficult, meaningless + printExpanded(s[pos .. s.len - 1], curCol, isMatch, + limitChar = high(int)) + else: + let last = max(-1, min(s.len - 1, charsAllowed - eL - 1)) + printExpanded(s[0 .. last], curCol, isMatch, limitChar-eL) + let numDots = limitChar - curCol.terminal + printBold ".".repeat(numDots) + curCol.terminal = limitChar + +proc printMatch(fileName: string, mi: MatchInfo, curCol: var Column) = + let sLines = mi.match.splitLines() + for i, l in sLines: + if i > 0: + lineHeader(filename, mi.lineBeg + i, isMatch = true, curCol) + let charsAllowed = limitCharUsr - curCol.terminal + if charsAllowed > 0: + printCropped(l, curCol, fromLeft = false, limitCharUsr, isMatch = true) + else: + curCol.overflowMatches += 1 + if i < sLines.len - 1: + newLn(curCol) + +proc getSubLinesBefore(buf: string, curMi: MatchInfo): string = + let first = beforePattern(buf, curMi.first-1, linesBefore+1) + result = substr(buf, first, curMi.first-1) + +proc printSubLinesBefore(filename: string, beforeMatch: string, lineBeg: int, + curCol: var Column, reserveChars: int, + replMode=false) = + # start block: print 'linesBefore' lines before current match `curMi` + let sLines = splitLines(beforeMatch) + let startLine = lineBeg - sLines.len + 1 + blockHeader(filename, lineBeg, replMode=replMode) + for i, l in sLines: + let isLastLine = i == sLines.len - 1 + lineHeader(filename, startLine + i, isMatch = isLastLine, curCol) + let limit = if isLastLine: limitCharUsr - reserveChars else: limitCharUsr + l.printCropped(curCol, fromLeft = isLastLine, limitChar = limit) + if not isLastLine: + newLn(curCol) + +proc getSubLinesAfter(buf: string, mi: MatchInfo): string = + let last = afterPattern(buf, mi.last+1, 1+linesAfter) + let skipByte = # workaround posix: suppress extra line at the end of file + if (last == buf.len-1 and buf.len >= 2 and + buf[^1] == '\l' and buf[^2] != '\c'): 1 + else: 0 + result = substr(buf, mi.last+1, last - skipByte) + +proc printOverflow(filename: string, line: int, curCol: var Column) = + if curCol.overflowMatches > 0: + lineHeader(filename, line, isMatch = true, curCol) + printBold("(" & $curCol.overflowMatches & " matches skipped)") + newLn(curCol) + curCol.overflowMatches = 0 + +proc printSubLinesAfter(filename: string, afterMatch: string, matchLineEnd: int, + curCol: var Column) = + # finish block: print 'linesAfter' lines after match `mi` + let sLines = splitLines(afterMatch) + if sLines.len == 0: # EOF + newLn(curCol) + else: + sLines[0].printCropped(curCol, fromLeft = false, limitCharUsr) + # complete the line after the match itself + newLn(curCol) + printOverflow(filename, matchLineEnd, curCol) + for i in 1 ..< sLines.len: + lineHeader(filename, matchLineEnd + i, isMatch = false, curCol) + sLines[i].printCropped(curCol, fromLeft = false, limitCharUsr) + newLn(curCol) + +proc getSubLinesBetween(buf: string, prevMi: MatchInfo, + curMi: MatchInfo): string = + buf.substr(prevMi.last+1, curMi.first-1) + +proc printBetweenMatches(filename: string, betweenMatches: string, + lastLineBeg: int, + curCol: var Column, reserveChars: int) = # continue block: print between `prevMi` and `curMi` - let lines = si.buf.substr(prevMi.last+1, curMi.first-1).splitLines() - stdout.write(lines[0]) # finish the line of previous Match - if lines.len > 1: - stdout.write("\n") - for i in 1 ..< lines.len: - lineHeader(si.filename, prevMi.lineEnd + i, - isMatch = (i == lines.len - 1)) - stdout.write(lines[i]) - if i < lines.len - 1: - stdout.write("\n") + let sLines = betweenMatches.splitLines() + sLines[0].printCropped(curCol, fromLeft = false, limitCharUsr) + # finish the line of previous Match + if sLines.len > 1: + newLn(curCol) + printOverflow(filename, lastLineBeg - sLines.len + 1, curCol) + for i in 1 ..< sLines.len: + let isLastLine = i == sLines.len - 1 + lineHeader(filename, lastLineBeg - sLines.len + i + 1, + isMatch = isLastLine, curCol) + let limit = if isLastLine: limitCharUsr - reserveChars else: limitCharUsr + sLines[i].printCropped(curCol, fromLeft = isLastLine, limitChar = limit) + if not isLastLine: + newLn(curCol) -proc printContextBetween(si: SearchInfo, prevMi, curMi: MatchInfo) = - # print context after previous match prevMi and before current match curMi - let nLinesBetween = curMi.lineBeg - prevMi.lineEnd - if nLinesBetween <= linesAfter + linesBefore + 1: # print as 1 block - printBetweenMatches(si, prevMi, curMi) - else: # finalize previous block and then print next block - printLinesAfter(si, prevMi, 1+linesAfter) - printLinesBefore(si, curMi, linesBefore+1) - -proc printReplacement(si: SearchInfo, mi: MatchInfo, repl: string, - showRepl: bool, curPos: int, +proc printReplacement(fileName: string, buf: string, mi: MatchInfo, + repl: string, showRepl: bool, curPos: int, newBuf: string, curLine: int) = - printLinesBefore(si, mi, linesBefore+1) - printMatch(si.fileName, mi) - printLinesAfter(si, mi, 1+linesAfter) + var curCol: Column + printSubLinesBefore(fileName, getSubLinesBefore(buf, mi), mi.lineBeg, + curCol, reserveChars(mi)) + printMatch(fileName, mi, curCol) + printSubLinesAfter(fileName, getSubLinesAfter(buf, mi), mi.lineEnd, curCol) stdout.flushFile() if showRepl: - let newSi: SearchInfo = (buf: newBuf, filename: si.filename) let miForNewBuf: MatchInfo = (first: newBuf.len, last: newBuf.len, lineBeg: curLine, lineEnd: curLine, match: "") - printLinesBefore(newSi, miForNewBuf, linesBefore+1, replMode=true) + printSubLinesBefore(fileName, getSubLinesBefore(newBuf, miForNewBuf), + miForNewBuf.lineBeg, curCol, reserveChars(miForNewBuf), + replMode=true) let replLines = countLineBreaks(repl, 0, repl.len-1) let miFixLines: MatchInfo = (first: mi.first, last: mi.last, lineBeg: curLine, lineEnd: curLine + replLines, match: repl) - printMatch(si.fileName, miFixLines) - printLinesAfter(si, miFixLines, 1+linesAfter) + printMatch(fileName, miFixLines, curCol) + printSubLinesAfter(fileName, getSubLinesAfter(buf, miFixLines), + miFixLines.lineEnd, curCol) + if linesAfter + linesBefore >= 2 and not newLine: stdout.write("\n") stdout.flushFile() -proc doReplace(si: SearchInfo, mi: MatchInfo, i: int, r: string; - newBuf: var string, curLine: var int, reallyReplace: var bool) = - newBuf.add(si.buf.substr(i, mi.first-1)) - inc(curLine, countLineBreaks(si.buf, i, mi.first-1)) +proc replace1match(filename: string, buf: string, mi: MatchInfo, i: int, + r: string; newBuf: var string, curLine: var int): bool = + newBuf.add(buf.substr(i, mi.first-1)) + inc(curLine, countLineBreaks(buf, i, mi.first-1)) if optConfirm in options: - printReplacement(si, mi, r, showRepl=true, i, newBuf, curLine) + printReplacement(filename, buf, mi, r, showRepl=true, i, newBuf, curLine) case confirm() of ceAbort: quit(0) - of ceYes: reallyReplace = true + of ceYes: gVar.reallyReplace = true of ceAll: - reallyReplace = true + gVar.reallyReplace = true options.excl(optConfirm) of ceNo: - reallyReplace = false + gVar.reallyReplace = false of ceNone: - reallyReplace = false + gVar.reallyReplace = false options.excl(optConfirm) - else: - printReplacement(si, mi, r, showRepl=reallyReplace, i, newBuf, curLine) - if reallyReplace: + elif optPipe notin options: + printReplacement(filename, buf, mi, r, showRepl=gVar.reallyReplace, i, + newBuf, curLine) + if gVar.reallyReplace: + result = true newBuf.add(r) inc(curLine, countLineBreaks(r, 0, r.len-1)) else: newBuf.add(mi.match) inc(curLine, countLineBreaks(mi.match, 0, mi.match.len-1)) -proc processFile(pattern; filename: string; counter: var int, errors: var int) = - var filenameShown = false - template beforeHighlight = - if not filenameShown and optVerbose notin options and not oneline: - printBlockFile(filename) - stdout.write("\n") - stdout.flushFile() - filenameShown = true +template updateCounters(output: Output) = + case output.kind + of blockFirstMatch, blockNextMatch: inc(gVar.matches) + of justCount: inc(gVar.matches, output.matches) + of openError: inc(gVar.errors) + of rejected, blockEnd, fileContents, outputFileName: discard - var buffer: string - if optFilenames in options: - buffer = filename - else: - try: - buffer = system.readFile(filename) - except IOError: - printError "Error: cannot open file: " & filename - inc(errors) - return - if optVerbose in options: - printFile(filename) - stdout.write("\n") - stdout.flushFile() - var result: string +proc printInfo(filename:string, output: Output) = + case output.kind + of openError: + printError("can not open path " & filename & " " & output.msg) + of rejected: + if optVerbose in options: + echo "(rejected: ", output.reason, ")" + of justCount: + echo " (" & $output.matches & " matches)" + of blockFirstMatch, blockNextMatch, blockEnd, fileContents, outputFileName: + discard - if optReplace in options: - result = newStringOfCap(buffer.len) +proc printOutput(filename: string, output: Output, curCol: var Column) = + case output.kind + of openError, rejected, justCount: printInfo(filename, output) + of fileContents: discard # impossible + of outputFileName: + printCropped(output.name, curCol, fromLeft=false, limitCharUsr) + newLn(curCol) + of blockFirstMatch: + printSubLinesBefore(filename, output.pre, output.match.lineBeg, + curCol, reserveChars(output.match)) + printMatch(filename, output.match, curCol) + of blockNextMatch: + printBetweenMatches(filename, output.pre, output.match.lineBeg, + curCol, reserveChars(output.match)) + printMatch(filename, output.match, curCol) + of blockEnd: + printSubLinesAfter(filename, output.blockEnding, output.firstLine, curCol) + if linesAfter + linesBefore >= 2 and not newLine and + optFilenames notin options: stdout.write("\n") - var lineRepl = 1 - let si: SearchInfo = (buf: buffer, filename: filename) +iterator searchFile(pattern: Pattern; buffer: string): Output = var prevMi, curMi: MatchInfo - curMi.lineEnd = 1 + prevMi.lineEnd = 1 var i = 0 var matches: array[0..re.MaxSubpatterns-1, string] for j in 0..high(matches): matches[j] = "" - var reallyReplace = true - while i < buffer.len: + while true: let t = findBounds(buffer, pattern, matches, i) if t.first < 0 or t.last < t.first: - if optReplace notin options and prevMi.lineBeg != 0: # finalize last match - printLinesAfter(si, prevMi, 1+linesAfter) - stdout.flushFile() + if prevMi.lineBeg != 0: # finalize last match + yield Output(kind: blockEnd, + blockEnding: getSubLinesAfter(buffer, prevMi), + firstLine: prevMi.lineEnd) break - let lineBeg = curMi.lineEnd + countLineBreaks(buffer, i, t.first-1) + let lineBeg = prevMi.lineEnd + countLineBreaks(buffer, i, t.first-1) curMi = (first: t.first, last: t.last, lineBeg: lineBeg, lineEnd: lineBeg + countLineBreaks(buffer, t.first, t.last), match: buffer.substr(t.first, t.last)) - beforeHighlight() - inc counter - if optReplace notin options: - if prevMi.lineBeg == 0: # no previous match, so no previous block to finalize - printLinesBefore(si, curMi, linesBefore+1) - else: - printContextBetween(si, prevMi, curMi) - printMatch(si.fileName, curMi) - if t.last == buffer.len - 1: - stdout.write("\n") - stdout.flushFile() + if prevMi.lineBeg == 0: # no prev. match, so no prev. block to finalize + let pre = getSubLinesBefore(buffer, curMi) + prevMi = curMi + yield Output(kind: blockFirstMatch, pre: pre, match: move(curMi)) else: - let r = replace(curMi.match, pattern, replacement % matches) - doReplace(si, curMi, i, r, result, lineRepl, reallyReplace) - + let nLinesBetween = curMi.lineBeg - prevMi.lineEnd + if nLinesBetween <= linesAfter + linesBefore + 1: # print as 1 block + let pre = getSubLinesBetween(buffer, prevMi, curMi) + prevMi = curMi + yield Output(kind: blockNextMatch, pre: pre, match: move(curMi)) + else: # finalize previous block and then print next block + let after = getSubLinesAfter(buffer, prevMi) + yield Output(kind: blockEnd, blockEnding: after, + firstLine: prevMi.lineEnd) + let pre = getSubLinesBefore(buffer, curMi) + prevMi = curMi + yield Output(kind: blockFirstMatch, + pre: pre, + match: move(curMi)) i = t.last+1 - prevMi = curMi - if optReplace in options: - result.add(substr(buffer, i)) # finalize new buffer after last match - var f: File - if open(f, filename, fmWrite): - f.write(result) - f.close() - else: - quit "cannot open file for overwriting: " & filename +func detectBin(buffer: string): bool = + for i in 0 ..< min(1024, buffer.len): + if buffer[i] == '\0': + return true -proc hasRightFileName(path: string): bool = - let filename = path.lastPathPart - let ex = filename.splitFile.ext.substr(1) # skip leading '.' - if extensions.len != 0: - var matched = false - for x in items(extensions): - if os.cmpPaths(x, ex) == 0: - matched = true - break - if not matched: return false - for x in items(skipExtensions): - if os.cmpPaths(x, ex) == 0: return false - if includeFile.len != 0: - var matched = false - for x in items(includeFile): - if filename.match(x): - matched = true - break - if not matched: return false - for x in items(excludeFile): - if filename.match(x): return false - result = true - -proc hasRightDirectory(path: string): bool = - let dirname = path.lastPathPart - for x in items(excludeDir): - if dirname.match(x): return false - result = true +proc compilePeg(initPattern: string): Peg = + var pattern = initPattern + if optWord in options: + pattern = r"(^ / !\letter)(" & pattern & r") !\letter" + if optIgnoreStyle in options: + pattern = "\\y " & pattern + elif optIgnoreCase in options: + pattern = "\\i " & pattern + result = peg(pattern) proc styleInsensitive(s: string): string = template addx = @@ -494,28 +868,358 @@ proc styleInsensitive(s: string): string = addx() else: addx() -proc walker(pattern; dir: string; counter: var int, errors: var int) = - if dirExists(dir): - for kind, path in walkDir(dir): +proc compileRegex(initPattern: string): Regex = + var pattern = initPattern + var reflags = {reStudy} + if optIgnoreStyle in options: + pattern = styleInsensitive(pattern) + if optWord in options: + # see https://github.com/nim-lang/Nim/issues/13528#issuecomment-592786443 + pattern = r"(^|\W)(:?" & pattern & r")($|\W)" + if {optIgnoreCase, optIgnoreStyle} * options != {}: + reflags.incl reIgnoreCase + result = if optRex in options: rex(pattern, reflags) + else: re(pattern, reflags) + +template declareCompiledPatterns(compiledStruct: untyped, + StructType: untyped, + body: untyped) = + {.hint[XDeclaredButNotUsed]: off.} + if optRegex notin options: + var compiledStruct: StructType[Peg] + template compile1Pattern(p: string, pat: Peg) = + if p!="": pat = p.compilePeg() + proc compileArray(initPattern: seq[string]): seq[Peg] = + for pat in initPattern: + result.add pat.compilePeg() + body + else: + var compiledStruct: StructType[Regex] + template compile1Pattern(p: string, pat: Regex) = + if p!="": pat = p.compileRegex() + proc compileArray(initPattern: seq[string]): seq[Regex] = + for pat in initPattern: + result.add pat.compileRegex() + body + {.hint[XDeclaredButNotUsed]: on.} + +iterator processFile(searchOptC: SearchOptComp[Pattern], filename: string, + yieldContents=false): Output = + var buffer: string + + var error = false + if optFilenames in options: + buffer = filename + elif optPipe in options: + buffer = stdin.readAll() + else: + try: + buffer = system.readFile(filename) + except IOError as e: + yield Output(kind: openError, msg: "readFile failed") + error = true + + if not error: + var reject = false + var reason: string + if searchOpt.checkBin in {biOff, biOnly}: + let isBin = detectBin(buffer) + if isBin and searchOpt.checkBin == biOff: + reject = true + reason = "binary file" + if (not isBin) and searchOpt.checkBin == biOnly: + reject = true + reason = "text file" + + if not reject: + if searchOpt.checkMatch != "": + reject = not contains(buffer, searchOptC.checkMatch, 0) + reason = "doesn't contain a requested match" + + if not reject: + if searchOpt.checkNoMatch != "": + reject = contains(buffer, searchOptC.checkNoMatch, 0) + reason = "contains a forbidden match" + + if reject: + yield Output(kind: rejected, reason: move(reason)) + elif optFilenames in options and searchOpt.pattern == "": + yield Output(kind: outputFileName, name: move(buffer)) + else: + var found = false + var cnt = 0 + for output in searchFile(searchOptC.pattern, buffer): + found = true + if optCount notin options: + yield output + else: + if output.kind in {blockFirstMatch, blockNextMatch}: + inc(cnt) + if optCount in options and cnt > 0: + yield Output(kind: justCount, matches: cnt) + if yieldContents and found and optCount notin options: + yield Output(kind: fileContents, buffer: move(buffer)) + + +proc hasRightFileName(path: string, walkOptC: WalkOptComp[Pattern]): bool = + let filename = path.lastPathPart + let ex = filename.splitFile.ext.substr(1) # skip leading '.' + if walkOpt.extensions.len != 0: + var matched = false + for x in walkOpt.extensions: + if os.cmpPaths(x, ex) == 0: + matched = true + break + if not matched: return false + for x in walkOpt.skipExtensions: + if os.cmpPaths(x, ex) == 0: return false + if walkOptC.includeFile.len != 0: + var matched = false + for pat in walkOptC.includeFile: + if filename.contains(pat): + matched = true + break + if not matched: return false + for pat in walkOptC.excludeFile: + if filename.contains(pat): return false + let dirname = path.parentDir + if walkOptC.includeDir.len != 0: + var matched = false + for pat in walkOptC.includeDir: + if dirname.contains(pat): + matched = true + break + if not matched: return false + result = true + +proc hasRightDirectory(path: string, walkOptC: WalkOptComp[Pattern]): bool = + let dirname = path.lastPathPart + for pat in walkOptC.excludeDir: + if dirname.contains(pat): return false + result = true + +iterator walkDirBasic(dir: string, walkOptC: WalkOptComp[Pattern]): string = + var dirStack = @[dir] # stack of directories + var timeFiles = newSeq[(times.Time, string)]() + while dirStack.len > 0: + let d = dirStack.pop() + var files = newSeq[string]() + var dirs = newSeq[string]() + for kind, path in walkDir(d): case kind of pcFile: - if path.hasRightFileName: - processFile(pattern, path, counter, errors) + if path.hasRightFileName(walkOptC): + files.add(path) of pcLinkToFile: - if optFollow in options and path.hasRightFileName: - processFile(pattern, path, counter, errors) + if optFollow in options and path.hasRightFileName(walkOptC): + files.add(path) of pcDir: - if optRecursive in options and path.hasRightDirectory: - walker(pattern, path, counter, errors) + if optRecursive in options and path.hasRightDirectory(walkOptC): + dirs.add path of pcLinkToDir: if optFollow in options and optRecursive in options and - path.hasRightDirectory: - walker(pattern, path, counter, errors) - elif fileExists(dir): - processFile(pattern, dir, counter, errors) + path.hasRightDirectory(walkOptC): + dirs.add path + if sortTime: # sort by time - collect files before yielding + for file in files: + var time: Time + try: + time = getLastModificationTime(file) # can fail for broken symlink + except: + discard + timeFiles.add((time, file)) + else: # alphanumeric sort, yield immediately after sorting + files.sort() + for file in files: + yield file + dirs.sort(order = SortOrder.Descending) + for dir in dirs: + dirStack.add(dir) + if sortTime: + timeFiles.sort(sortTimeOrder) + for (_, file) in timeFiles: + yield file + +iterator walkRec(paths: seq[string]): (string, string) = + declareCompiledPatterns(walkOptC, WalkOptComp): + walkOptC.excludeFile.add walkOpt.excludeFile.compileArray() + walkOptC.includeFile.add walkOpt.includeFile.compileArray() + walkOptC.includeDir.add walkOpt.includeDir.compileArray() + walkOptC.excludeDir.add walkOpt.excludeDir.compileArray() + for path in paths: + if dirExists(path): + for p in walkDirBasic(path, walkOptC): + yield ("", p) + else: + yield ( + if fileExists(path): ("", path) + else: ("Error: no such file or directory: ", path)) + +proc replaceMatches(pattern: Pattern; filename: string, buffer: string, + fileResult: FileResult) = + var newBuf = newStringOfCap(buffer.len) + + var changed = false + var lineRepl = 1 + var i = 0 + for output in fileResult: + if output.kind in {blockFirstMatch, blockNextMatch}: + let curMi = output.match + let r = replacef(curMi.match, pattern, replacement) + if replace1match(filename, buffer, curMi, i, r, newBuf, lineRepl): + changed = true + i = curMi.last + 1 + if changed and optPipe notin options: + newBuf.add(substr(buffer, i)) # finalize new buffer after last match + var f: File + if open(f, filename, fmWrite): + f.write(newBuf) + f.close() + else: + printError "cannot open file for overwriting: " & filename + inc(gVar.errors) + elif optPipe in options: # always print new buffer to stdout in pipe mode + newBuf.add(substr(buffer, i)) # finalize new buffer after last match + stdout.write(newBuf) + +template processFileResult(pattern: Pattern; filename: string, + fileResult: untyped) = + var filenameShown = false + template showFilename = + if not filenameShown: + printBlockFile(filename) + stdout.write("\n") + stdout.flushFile() + filenameShown = true + if optVerbose in options: + showFilename + if optReplace notin options: + var curCol: Column + var toFlush: bool + for output in fileResult: + updateCounters(output) + toFlush = true + if output.kind notin {rejected, openError, justCount} and not oneline: + showFilename + if output.kind == justCount and oneline: + printFile(filename & ":") + printOutput(filename, output, curCol) + if nWorkers == 0 and output.kind in {blockFirstMatch, blockNextMatch}: + stdout.flushFile() # flush immediately in single thread mode + if toFlush: stdout.flushFile() else: - printError "Error: no such file or directory: " & dir - inc(errors) + var buffer = "" + var matches: FileResult + for output in fileResult: + updateCounters(output) + case output.kind + of rejected, openError, justCount, outputFileName: + printInfo(filename, output) + of blockFirstMatch, blockNextMatch, blockEnd: + matches.add(output) + of fileContents: buffer = output.buffer + if matches.len > 0: + replaceMatches(pattern, filename, buffer, matches) + +proc run1Thread() = + declareCompiledPatterns(searchOptC, SearchOptComp): + compile1Pattern(searchOpt.pattern, searchOptC.pattern) + compile1Pattern(searchOpt.checkMatch, searchOptC.checkMatch) + compile1Pattern(searchOpt.checkNoMatch, searchOptC.checkNoMatch) + if optPipe in options: + processFileResult(searchOptC.pattern, "-", + processFile(searchOptC, "-", + yieldContents=optReplace in options)) + for (err, filename) in walkRec(paths): + if err != "": + inc(gVar.errors) + printError (err & filename) + continue + processFileResult(searchOptC.pattern, filename, + processFile(searchOptC, filename, + yieldContents=optReplace in options)) + +# Multi-threaded version: all printing is being done in the Main thread. +# Totally nWorkers+1 additional threads are created (workers + pathProducer). +# An example of case nWorkers=2: +# +# ------------------ initial paths ------------------- +# | Main thread |----------------->| pathProducer | +# ------------------ ------------------- +# ^ | | +# resultsChan | walking errors, | | searchRequestsChan +# | number of files | -----+----- +# ----+--------------------------- | | +# | | (when walking finished) |a path |a path to file +# | | | | +# | | V V +# | | ------------ ------------ +# | | | worker 1 | | worker 2 | +# | | ------------ ------------ +# | | matches in the file | | +# | -------------------------------- | +# | matches in the file | +# ---------------------------------------------- +# +# The matches from each file are passed at once as FileResult type. + +proc worker(initSearchOpt: SearchOpt) {.thread.} = + searchOpt = initSearchOpt # init thread-local var + declareCompiledPatterns(searchOptC, SearchOptComp): + compile1Pattern(searchOpt.pattern, searchOptC.pattern) + compile1Pattern(searchOpt.checkMatch, searchOptC.checkMatch) + compile1Pattern(searchOpt.checkNoMatch, searchOptC.checkNoMatch) + while true: + let (fileNo, filename) = searchRequestsChan.recv() + var fileResult: FileResult + for output in processFile(searchOptC, filename, + yieldContents=(optReplace in options)): + fileResult.add(output) + resultsChan.send((false, fileNo, filename, move(fileResult))) + +proc pathProducer(arg: (seq[string], WalkOpt)) {.thread.} = + let paths = arg[0] + walkOpt = arg[1] # init thread-local copy of opt + var + nextFileN = 0 + for (err, filename) in walkRec(paths): + if err == "": + searchRequestsChan.send((nextFileN,filename)) + else: + resultsChan.send((false, nextFileN, + filename, @[Output(kind: openError, msg: err)])) + nextFileN += 1 + resultsChan.send((true, nextFileN, "", @[])) # pass total number of files + +proc runMultiThread() = + var + workers = newSeq[Thread[SearchOpt]](nWorkers) + storage = newTable[int, (string, FileResult) ]() + # file number -> tuple[filename, fileResult - accumulated data structure] + firstUnprocessedFile = 0 # for always processing files in the same order + open(searchRequestsChan) + open(resultsChan) + for n in 0 ..< nWorkers: + createThread(workers[n], worker, searchOpt) + var producerThread: Thread[(seq[string], WalkOpt)] + createThread(producerThread, pathProducer, (paths, walkOpt)) + declareCompiledPatterns(pat, SinglePattern): + compile1Pattern(searchOpt.pattern, pat.pattern) + template add1fileResult(fileNo: int, fname: string, fResult: FileResult) = + storage[fileNo] = (fname, fResult) + while storage.haskey(firstUnprocessedFile): + let fileResult = storage[firstUnprocessedFile][1] + let filename = storage[firstUnprocessedFile][0] + processFileResult(pat.pattern, filename, fileResult) + storage.del(firstUnprocessedFile) + firstUnprocessedFile += 1 + var totalFiles = -1 # will be known when pathProducer finishes + while totalFiles == -1 or firstUnprocessedFile < totalFiles: + let msg = resultsChan.recv() + if msg.finished: + totalFiles = msg.fileNo + else: + add1fileResult(msg.fileNo, msg.filename, msg.fileResult) proc reportError(msg: string) = printError "Error: " & msg @@ -535,6 +1239,15 @@ proc checkOptions(subset: TOptions, a, b: string) = if subset <= options: quit("cannot specify both '$#' and '$#'" % [a, b]) +proc parseNonNegative(str: string, key: string): int = + try: + result = parseInt(str) + except ValueError: + reportError("Option " & key & " requires an integer but '" & + str & "' was given") + if result < 0: + reportError("A positive integer is expected for option " & key) + when defined(posix): useWriteStyled = terminal.isatty(stdout) # that should be before option processing to allow override of useWriteStyled @@ -543,13 +1256,15 @@ for kind, key, val in getopt(): case kind of cmdArgument: if options.contains(optStdin): - filenames.add(key) - elif pattern.len == 0: - pattern = key - elif options.contains(optReplace) and replacement.len == 0: + paths.add(key) + elif not searchOpt.patternSet: + searchOpt.pattern = key + searchOpt.patternSet = true + elif options.contains(optReplace) and not replacementSet: replacement = key + replacementSet = true else: - filenames.add(key) + paths.add(key) of cmdLongOption, cmdShortOption: case normalize(key) of "find", "f": incl(options, optFind) @@ -569,98 +1284,133 @@ for kind, key, val in getopt(): of "confirm": incl(options, optConfirm) of "stdin": incl(options, optStdin) of "word", "w": incl(options, optWord) - of "ignorecase", "i": incl(options, optIgnoreCase) - of "ignorestyle", "y": incl(options, optIgnoreStyle) - of "ext": extensions.add val.split('|') - of "noext": skipExtensions.add val.split('|') - of "excludedir", "exclude-dir": excludeDir.add rex(val) - of "includefile", "include-file": includeFile.add rex(val) - of "excludefile", "exclude-file": excludeFile.add rex(val) - of "nocolor": useWriteStyled = false + of "ignorecase", "ignore-case", "i": incl(options, optIgnoreCase) + of "ignorestyle", "ignore-style", "y": incl(options, optIgnoreStyle) + of "threads", "j": + if val == "": + nWorkers = countProcessors() + else: + nWorkers = parseNonNegative(val, key) + of "ext": walkOpt.extensions.add val.split('|') + of "noext", "no-ext": walkOpt.skipExtensions.add val.split('|') + of "excludedir", "exclude-dir", "ed": walkOpt.excludeDir.add val + of "includedir", "include-dir", "id": walkOpt.includeDir.add val + of "includefile", "include-file", "if": walkOpt.includeFile.add val + of "excludefile", "exclude-file", "ef": walkOpt.excludeFile.add val + of "match": searchOpt.checkMatch = val + of "nomatch": + searchOpt.checkNoMatch = val + of "bin": + case val + of "on": searchOpt.checkBin = biOn + of "off": searchOpt.checkBin = biOff + of "only": searchOpt.checkBin = biOnly + else: reportError("unknown value for --bin") + of "text", "t": searchOpt.checkBin = biOff + of "count": incl(options, optCount) + of "sorttime", "sort-time", "s": + case normalize(val) + of "off": sortTime = false + of "", "on", "asc", "ascending": + sortTime = true + sortTimeOrder = SortOrder.Ascending + of "desc", "descending": + sortTime = true + sortTimeOrder = SortOrder.Descending + else: reportError("invalid value '" & val & "' for --sortTime") + of "nocolor", "no-color": useWriteStyled = false of "color": case val of "auto": discard - of "never", "false": useWriteStyled = false - of "", "always", "true": useWriteStyled = true + of "off", "never", "false": useWriteStyled = false + of "", "on", "always", "true": useWriteStyled = true else: reportError("invalid value '" & val & "' for --color") - of "colortheme": + of "colortheme", "color-theme": colortheme = normalize(val) if colortheme notin ["simple", "bnw", "ack", "gnu"]: reportError("unknown colortheme '" & val & "'") of "beforecontext", "before-context", "b": - try: - linesBefore = parseInt(val) - except ValueError: - reportError("option " & key & " requires an integer but '" & - val & "' was given") + linesBefore = parseNonNegative(val, key) of "aftercontext", "after-context", "a": - try: - linesAfter = parseInt(val) - except ValueError: - reportError("option " & key & " requires an integer but '" & - val & "' was given") + linesAfter = parseNonNegative(val, key) of "context", "c": - try: - linesContext = parseInt(val) - except ValueError: - reportError("option --context requires an integer but '" & - val & "' was given") - of "newline", "l": newLine = true - of "oneline": oneline = true - of "group", "g": oneline = false + linesContext = parseNonNegative(val, key) + of "newline", "l": + newLine = true + # Tabs are aligned automatically for --group, --newLine, --filenames + expandTabs = false + of "group", "g": + oneline = false + expandTabs = false + of "cols", "%": + incl(options, optLimitChars) + termWidth = terminalWidth() + if val == "auto" or key == "%": + limitCharUsr = termWidth + when defined(windows): # Windows cmd & powershell add an empty line + limitCharUsr -= 1 # when printing '\n' right after the last column + elif val == "": + limitCharUsr = 80 + else: + limitCharUsr = parseNonNegative(val, key) + of "onlyascii", "only-ascii", "@": + if val == "" or val == "on" or key == "@": + optOnlyAscii = true + elif val == "off": + optOnlyAscii = false + else: + printError("unknown value for --onlyAscii option") of "verbose": incl(options, optVerbose) - of "filenames": incl(options, optFilenames) + of "filenames": + incl(options, optFilenames) + expandTabs = false of "help", "h": writeHelp() of "version", "v": writeVersion() + of "": incl(options, optPipe) else: reportError("unrecognized option '" & key & "'") of cmdEnd: assert(false) # cannot happen checkOptions({optFind, optReplace}, "find", "replace") +checkOptions({optCount, optReplace}, "count", "replace") checkOptions({optPeg, optRegex}, "peg", "re") checkOptions({optIgnoreCase, optIgnoreStyle}, "ignore_case", "ignore_style") checkOptions({optFilenames, optReplace}, "filenames", "replace") +checkOptions({optPipe, optStdin}, "-", "stdin") +checkOptions({optPipe, optFilenames}, "-", "filenames") +checkOptions({optPipe, optConfirm}, "-", "confirm") +checkOptions({optPipe, optRecursive}, "-", "recursive") linesBefore = max(linesBefore, linesContext) linesAfter = max(linesAfter, linesContext) +if optPipe in options and paths.len != 0: + reportError("both - and paths are specified") + if optStdin in options: - pattern = ask("pattern [ENTER to exit]: ") - if pattern.len == 0: quit(0) + searchOpt.pattern = ask("pattern [ENTER to exit]: ") + if searchOpt.pattern.len == 0: quit(0) if optReplace in options: replacement = ask("replacement [supports $1, $# notations]: ") -if pattern.len == 0: +if optReplace in options and not replacementSet: + reportError("provide REPLACEMENT as second argument (use \"\" for empty one)") +if optReplace in options and paths.len == 0 and optPipe notin options: + reportError("provide paths for replacement explicitly (use . for current directory)") + +if searchOpt.pattern == "" and optFilenames notin options: reportError("empty pattern was given") else: - var counter = 0 - var errors = 0 - if filenames.len == 0: - filenames.add(os.getCurrentDir()) - if optRegex notin options: - if optWord in options: - pattern = r"(^ / !\letter)(" & pattern & r") !\letter" - if optIgnoreStyle in options: - pattern = "\\y " & pattern - elif optIgnoreCase in options: - pattern = "\\i " & pattern - let pegp = peg(pattern) - for f in items(filenames): - walker(pegp, f, counter, errors) + if paths.len == 0 and optPipe notin options: + paths.add(".") + if optPipe in options or nWorkers == 0: + run1Thread() else: - var reflags = {reStudy} - if optIgnoreStyle in options: - pattern = styleInsensitive(pattern) - if optWord in options: - # see https://github.com/nim-lang/Nim/issues/13528#issuecomment-592786443 - pattern = r"(^|\W)(:?" & pattern & r")($|\W)" - if {optIgnoreCase, optIgnoreStyle} * options != {}: - reflags.incl reIgnoreCase - let rep = if optRex in options: rex(pattern, reflags) - else: re(pattern, reflags) - for f in items(filenames): - walker(rep, f, counter, errors) - if errors != 0: - printError $errors & " errors" - stdout.write($counter & " matches\n") - if errors != 0: + runMultiThread() + if gVar.errors != 0: + printError $gVar.errors & " errors" + if searchOpt.pattern != "": + # PATTERN allowed to be empty if --filenames is given + printBold($gVar.matches & " matches") + stdout.write("\n") + if gVar.errors != 0: quit(1) diff --git a/tools/nimgrep.nim.cfg b/tools/nimgrep.nim.cfg index 6d0ea5aad..64d3edc7a 100644 --- a/tools/nimgrep.nim.cfg +++ b/tools/nimgrep.nim.cfg @@ -1,5 +1,4 @@ -# The GC is stable enough now: - -#--gc:none - - +# don't use --gc:refc because of bug +# https://github.com/nim-lang/Nim/issues/14138 . +# --gc:orc and --gc:markandsweep work well. +--threads:on --gc:orc From d03f24147a185091344c67a720a13ca0b3c98dc2 Mon Sep 17 00:00:00 2001 From: Ivan Bobev Date: Mon, 9 Nov 2020 11:02:01 +0200 Subject: [PATCH 0931/3768] Add a macro returning enum items count (#15824) Add a macro `enumLen` which is used to determine the number of items in an enumeration type to the `typetraits.nim` module. Also, add unit tests for it in the `ttypetraits.nim` module. Related to nimlang/Nim#15824 --- changelog.md | 2 ++ lib/pure/typetraits.nim | 13 +++++++++++ tests/metatype/ttypetraits.nim | 40 ++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+) diff --git a/changelog.md b/changelog.md index 53730ecc5..d00d07fa1 100644 --- a/changelog.md +++ b/changelog.md @@ -6,6 +6,8 @@ - Make `{.requiresInit.}` pragma to work for `distinct` types. +- Added a macros `enumLen` for returning the number of items in an enum to the `typetraits.nim` module. + - `prelude` now works with the JavaScript target. - Added `ioutils` module containing `duplicate` and `duplicateTo` to duplicate `FileHandle` using C function `dup` and `dup2`. diff --git a/lib/pure/typetraits.nim b/lib/pure/typetraits.nim index 0a05b5ded..7b6c94cbd 100644 --- a/lib/pure/typetraits.nim +++ b/lib/pure/typetraits.nim @@ -107,6 +107,19 @@ since (1, 3, 5): import std/macros +macro enumLen*(T: typedesc[enum]): int = + ## Returns the number of items in the enum `T`. + + runnableExamples: + type Foo = enum fooItem1 fooItem2 + doAssert Foo.enumLen == 2 + + let bracketExpr = getType(T) + expectKind(bracketExpr, nnkBracketExpr) + let enumTy = bracketExpr[1] + expectKind(enumTy, nnkEnumTy) + result = newLit(enumTy.len - 1) + macro genericParamsImpl(T: typedesc): untyped = # auxiliary macro needed, can't do it directly in `genericParams` result = newNimNode(nnkTupleConstr) diff --git a/tests/metatype/ttypetraits.nim b/tests/metatype/ttypetraits.nim index 50c474fe9..326faac82 100644 --- a/tests/metatype/ttypetraits.nim +++ b/tests/metatype/ttypetraits.nim @@ -303,3 +303,43 @@ block: # elementType var a: seq[int] doAssert elementType(a) is int doAssert elementType(seq[char].default) is char + +block: # enum.len + type + Direction = enum + north, east, south, west + + Direction2 = Direction + Direction3 = Direction2 + + TokenType = enum + a = 2, b = 4, c = 89 + + MyEnum = enum + ##[This is test of enum with a doc comment. + + Which is also a multi line one.]## + valueA = (0, "my value A"), + ## The items are also commented. This has both integer and string + ## values. + valueB = "value B", + ## This item has only a string value, + valueC = 2, + ## and this one only an integer. + valueD = (3, "abc") + ## Both, integer and string values again. + + OtherEnum {.pure.} = enum + valueX, valueY, valueZ + + MyFlag {.size: sizeof(cint).} = enum + A, B, C, D + + static: + doAssert Direction.enumLen == 4 + doAssert Direction2.enumLen == 4 + doAssert Direction3.enumLen == 4 + doAssert TokenType.enumLen == 3 + doAssert MyEnum.enumLen == 4 + doAssert OtherEnum.enumLen == 3 + doAssert MyFlag.enumLen == 4 From 3be404af0488805493a46329d35ad276d739151d Mon Sep 17 00:00:00 2001 From: n5m <72841454+n5m@users.noreply.github.com> Date: Mon, 9 Nov 2020 10:45:43 +0000 Subject: [PATCH 0932/3768] include example of error-marked copy proc (#15886) * include example of error-marked copy proc * Update destructors.rst --- doc/destructors.rst | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/doc/destructors.rst b/doc/destructors.rst index 66f1043b3..e48e360dd 100644 --- a/doc/destructors.rst +++ b/doc/destructors.rst @@ -184,8 +184,14 @@ The general pattern in ``=copy`` looks like: The ``=copy`` proc can be marked with the ``{.error.}`` pragma. Then any assignment -that otherwise would lead to a copy is prevented at compile-time. +that otherwise would lead to a copy is prevented at compile-time. This looks like: +.. code-block:: nim + + proc `=copy`(dest: var T; source: T) {.error.} + +but a custom error message (e.g., ``{.error: "custom error".}``) will not be emitted +by the compiler. Notice that there is no ``=`` before the ``{.error.}`` pragma. Move semantics ============== From 673c5990c6b2130dd3b1a4854eb5d85c73a90cd0 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 9 Nov 2020 18:46:25 +0800 Subject: [PATCH 0933/3768] fix #12558 (#15864) * fix #12558 * Update compiler/pragmas.nim --- compiler/pragmas.nim | 6 +++++- tests/pragmas/t12558.nim | 15 +++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 tests/pragmas/t12558.nim diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 8affccbf3..1ec0aa112 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -896,7 +896,11 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of wMagic: processMagic(c, it, sym) of wCompileTime: noVal(c, it) - incl(sym.flags, sfCompileTime) + if comesFromPush: + if sym.kind in {skProc, skFunc}: + incl(sym.flags, sfCompileTime) + else: + incl(sym.flags, sfCompileTime) #incl(sym.loc.flags, lfNoDecl) of wGlobal: noVal(c, it) diff --git a/tests/pragmas/t12558.nim b/tests/pragmas/t12558.nim new file mode 100644 index 000000000..14fc74cee --- /dev/null +++ b/tests/pragmas/t12558.nim @@ -0,0 +1,15 @@ +discard """ + nimout: '''@["1", "2", "3"]''' +""" + +import sequtils + +{.push compile_time.} + +proc foo = + echo map_it([1, 2, 3], $it) + +{.pop.} + +static: + foo() From 7b192177984d77355ce67f8cfa4aee227be73e7b Mon Sep 17 00:00:00 2001 From: cooldome Date: Mon, 9 Nov 2020 10:47:29 +0000 Subject: [PATCH 0934/3768] fix #15707 (#15870) --- compiler/astalgo.nim | 4 ++-- compiler/semmagic.nim | 26 ++++++++++++++++++-------- compiler/sempass2.nim | 4 ++-- tests/arc/t14383.nim | 14 ++++++++++++++ 4 files changed, 36 insertions(+), 12 deletions(-) diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index d9cd5ade1..832f8819a 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -110,14 +110,14 @@ proc iiTablePut*(t: var TIITable, key, val: int) # implementation -proc skipConvAndClosure*(n: PNode): PNode = +proc skipConvCastAndClosure*(n: PNode): PNode = result = n while true: case result.kind of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64, nkClosure: result = result[0] - of nkHiddenStdConv, nkHiddenSubConv, nkConv: + of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkCast: result = result[1] else: break diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 5e6b4dbe2..3368bcfbf 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -527,14 +527,24 @@ 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 in {nkLambda, nkDo}: n[^1][namePos].sym - else: n[^1].sym - # check if we converted this finalizer into a destructor already: - 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, fin, n.info), n, attachedDestructor) + let nfin = skipConvCastAndClosure(n[^1]) + let fin = case nfin.kind + of nkSym: nfin.sym + of nkLambda, nkDo: nfin[namePos].sym + else: + localError(c.config, n.info, "finalizer must be a direct reference to a proc") + nil + if fin != nil: + if fin.kind notin {skProc, skFunc}: + # calling convention is checked in codegen + localError(c.config, n.info, "finalizer must be a direct reference to a proc") + + # check if we converted this finalizer into a destructor already: + 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, fin, n.info), n, attachedDestructor) result = n of mDestroy: result = n diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 92a896a38..39ba1b307 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -510,7 +510,7 @@ proc procVarCheck(n: PNode; conf: ConfigRef) = proc notNilCheck(tracked: PEffects, n: PNode, paramType: PType) = let n = n.skipConv if paramType.isNil or paramType.kind != tyTypeDesc: - procVarCheck skipConvAndClosure(n), tracked.config + procVarCheck skipConvCastAndClosure(n), tracked.config #elif n.kind in nkSymChoices: # echo "came here" let paramType = paramType.skipTypesOrNil(abstractInst) @@ -556,7 +556,7 @@ proc isTrival(caller: PNode): bool {.inline.} = result = caller.kind == nkSym and caller.sym.magic in {mEqProc, mIsNil, mMove, mWasMoved, mSwap} proc trackOperandForIndirectCall(tracked: PEffects, n: PNode, paramType: PType; caller: PNode) = - let a = skipConvAndClosure(n) + let a = skipConvCastAndClosure(n) let op = a.typ # assume indirect calls are taken here: if op != nil and op.kind == tyProc and n.skipConv.kind != nkNilLit and not isTrival(caller): diff --git a/tests/arc/t14383.nim b/tests/arc/t14383.nim index f112a1a6c..834c50def 100644 --- a/tests/arc/t14383.nim +++ b/tests/arc/t14383.nim @@ -34,3 +34,17 @@ echo x import std/os discard getFileInfo(".") + + +#------------------------------------------------------------------------------ +# Issue #15707 +#------------------------------------------------------------------------------ + +type + JVMObject = ref object +proc freeJVMObject(o: JVMObject) = + discard +proc fromJObject(T: typedesc[JVMObject]): T = + result.new(cast[proc(r: T) {.nimcall.}](freeJVMObject)) + +discard JVMObject.fromJObject() \ No newline at end of file From 204435ad97d7de20316d75cd7299cfa807361427 Mon Sep 17 00:00:00 2001 From: Bung Date: Mon, 9 Nov 2020 19:05:17 +0800 Subject: [PATCH 0935/3768] export PrettyOptions,prettyPrint from nimpretty (#15865) --- nimpretty/nimpretty.nim | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/nimpretty/nimpretty.nim b/nimpretty/nimpretty.nim index 0fb2c6a9e..a7940349d 100644 --- a/nimpretty/nimpretty.nim +++ b/nimpretty/nimpretty.nim @@ -44,11 +44,11 @@ proc writeVersion() = quit(0) type - PrettyOptions = object - indWidth: Natural - maxLineLen: Positive + PrettyOptions* = object + indWidth*: Natural + maxLineLen*: Positive -proc prettyPrint(infile, outfile: string, opt: PrettyOptions) = +proc prettyPrint*(infile, outfile: string, opt: PrettyOptions) = var conf = newConfigRef() let fileIdx = fileInfoIdx(conf, AbsoluteFile infile) let f = splitFile(outfile.expandTilde) @@ -119,4 +119,5 @@ proc main = os.copyFile(source = infile, dest = infileBackup) prettyPrint(infile, outfile, opt) -main() +when isMainModule: + main() From 3bff12b96612be4e47e20d880d3ba9e57e8bf713 Mon Sep 17 00:00:00 2001 From: n5m <72841454+n5m@users.noreply.github.com> Date: Mon, 9 Nov 2020 11:20:55 +0000 Subject: [PATCH 0936/3768] improve Testament docs (#15881) * fix mentions of recursive test discovery * document that {.fatal.} pragmas cannot be caught --- doc/testament.rst | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/doc/testament.rst b/doc/testament.rst index 18501f4cd..09c248d1f 100644 --- a/doc/testament.rst +++ b/doc/testament.rst @@ -58,12 +58,12 @@ Running all tests from a directory $ testament pattern "tests/*.nim" -To recursively search for all tests in a directory, use +To search for tests deeper in a directory, use .. code:: - $ testament pattern "tests/**/*.nim" - + $ testament pattern "tests/**/*.nim" # one level deeper + $ testament pattern "tests/**/**/*.nim" # two levels deeper HTML Reports ============ @@ -89,7 +89,9 @@ Example "template" **to edit** and write a Testament unittest: # Options: # "compile": expect successful compilation # "run": expect successful compilation and execution - # "reject": expect failed compilation + # "reject": expect failed compilation. The "reject" action can catch + # {.error.} pragmas but not {.fatal.} pragmas because + # {.fatal.} pragmas guarantee that compilation will be aborted. action: "run" # The exit code that the test is expected to return. Typically, the default From d5a0a5dfffcf77e3221e8522e023e5485a2c37cc Mon Sep 17 00:00:00 2001 From: cooldome Date: Mon, 9 Nov 2020 11:21:56 +0000 Subject: [PATCH 0937/3768] Fix #15858 (#15887) * fix #15858 * fix space * fix #15629 * Revert "fix #15629" --- compiler/semtypes.nim | 2 +- tests/statictypes/tstatictypes.nim | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 467645aee..f111f6308 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -306,7 +306,7 @@ proc semArrayIndex(c: PContext, n: PNode): PType = result = makeRangeWithStaticExpr(c, e) if c.inGenericContext > 0: result.flags.incl tfUnresolved elif e.kind in (nkCallKinds + {nkBracketExpr}) and hasUnresolvedArgs(c, e): - if not isOrdinalType(e.typ): + if not isOrdinalType(e.typ.skipTypes({tyStatic, tyAlias, tyGenericInst, tySink})): localError(c.config, n[1].info, errOrdinalTypeExpected) # This is an int returning call, depending on an # yet unknown generic param (see tgenericshardcases). diff --git a/tests/statictypes/tstatictypes.nim b/tests/statictypes/tstatictypes.nim index 359ae4c9f..fe1d1c8ca 100644 --- a/tests/statictypes/tstatictypes.nim +++ b/tests/statictypes/tstatictypes.nim @@ -349,3 +349,11 @@ type a: array[n, int] Tile = TileCT #Commenting this out to make it work + +#------------------------------------------------------------------------------------------ +# issue #15858 + +proc fn(N1: static int, N2: static int, T: typedesc): array[N1 * N2, T] = + doAssert(len(result) == N1 * N2) + +let yy = fn(5, 10, float) From 69fe7070250e0dbbcc0fafff4e341d1d805b7d59 Mon Sep 17 00:00:00 2001 From: cooldome Date: Mon, 9 Nov 2020 11:26:12 +0000 Subject: [PATCH 0938/3768] Fix 15629 (#15888) * fix #15858 * fix space * fix #15629 * Revert "fix space" * Revert "fix #15858" --- compiler/varpartitions.nim | 23 +++++++++++++---------- tests/arc/topt_cursor2.nim | 29 +++++++++++++++++++++++++++-- 2 files changed, 40 insertions(+), 12 deletions(-) diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim index 34a64c759..2e781c041 100644 --- a/compiler/varpartitions.nim +++ b/compiler/varpartitions.nim @@ -45,6 +45,8 @@ proc `<`(a, b: AbstractTime): bool {.borrow.} proc inc(x: var AbstractTime; diff = 1) {.borrow.} proc dec(x: var AbstractTime; diff = 1) {.borrow.} +proc `$`(x: AbstractTime): string {.borrow.} + type SubgraphFlag = enum isMutated, # graph might be mutated @@ -591,33 +593,34 @@ proc deps(c: var Partitions; dest, src: PNode) = for s in sources: connect(c, t, s, dest.info) - if cursorInference in c.goals and src.kind != nkEmpty: - if dest.kind == nkSym: - let vid = variableId(c, dest.sym) + if cursorInference in c.goals and src.kind != nkEmpty: + let d = pathExpr(dest, c.owner) + if d != nil and d.kind == nkSym: + let vid = variableId(c, d.sym) if vid >= 0: destMightOwn(c, c.s[vid], src) for s in sources: - if s == dest.sym: + if s == d.sym: discard "assignments like: it = it.next are fine" elif {sfGlobal, sfThread} * s.flags != {} or hasDisabledAsgn(s.typ): # do not borrow from a global variable or from something with a # disabled assignment operator. c.s[vid].flags.incl preventCursor - when explainCursors: echo "A not a cursor: ", dest.sym, " ", s + when explainCursors: echo "A not a cursor: ", d.sym, " ", s else: let srcid = variableId(c, s) if srcid >= 0: if s.kind notin {skResult, skParam} and ( c.s[srcid].aliveEnd < c.s[vid].aliveEnd): # you cannot borrow from a local that lives shorter than 'vid': - when explainCursors: echo "B not a cursor ", dest.sym, " ", c.s[srcid].aliveEnd, " ", c.s[vid].aliveEnd + when explainCursors: echo "B not a cursor ", d.sym, " ", c.s[srcid].aliveEnd, " ", c.s[vid].aliveEnd c.s[vid].flags.incl preventCursor elif {isReassigned, preventCursor} * c.s[srcid].flags != {}: # you cannot borrow from something that is re-assigned: - when explainCursors: echo "C not a cursor ", dest.sym, " ", c.s[srcid].flags, " reassignedTo ", c.s[srcid].reassignedTo + when explainCursors: echo "C not a cursor ", d.sym, " ", c.s[srcid].flags, " reassignedTo ", c.s[srcid].reassignedTo c.s[vid].flags.incl preventCursor - elif c.s[srcid].reassignedTo != 0 and c.s[srcid].reassignedTo != dest.sym.id: - when explainCursors: echo "D not a cursor ", dest.sym, " reassignedTo ", c.s[srcid].reassignedTo + elif c.s[srcid].reassignedTo != 0 and c.s[srcid].reassignedTo != d.sym.id: + when explainCursors: echo "D not a cursor ", d.sym, " reassignedTo ", c.s[srcid].reassignedTo c.s[vid].flags.incl preventCursor const @@ -900,4 +903,4 @@ proc computeCursors*(s: PSym; n: PNode; config: ConfigRef) = discard "cannot cursor into a graph that is mutated" else: v.sym.flags.incl sfCursor - #echo "this is now a cursor ", v.sym, " ", par.s[rid].flags, " ", config $ v.sym.info + #echo "this is now a cursor ", v.sym, " ", par.s[rid].flags, " ", config $ v.sym.info \ No newline at end of file diff --git a/tests/arc/topt_cursor2.nim b/tests/arc/topt_cursor2.nim index 2e6fb256f..4b566ed24 100644 --- a/tests/arc/topt_cursor2.nim +++ b/tests/arc/topt_cursor2.nim @@ -1,6 +1,8 @@ -discard """ - output: '''emptyemptyempty''' +discard """ cmd: '''nim c --gc:arc $file''' + output: '''emptyemptyempty +inner destroy +''' """ # bug #15039 @@ -49,3 +51,26 @@ proc parse() = echo children parse() + + +#------------------------------------------------------------------------------ +# issue #15629 + +type inner = object +type outer = ref inner + +proc `=destroy`(b: var inner) = + echo "inner destroy" + +proc newOuter(): outer = + new(result) + +type holder = object + contents: outer + +proc main() = + var t: holder + t.contents = newOuter() + +main() + From 3948b40bcd79a3883529608bf4bd156c2e9de5f8 Mon Sep 17 00:00:00 2001 From: ee7 <45465154+ee7@users.noreply.github.com> Date: Mon, 9 Nov 2020 13:08:17 +0100 Subject: [PATCH 0939/3768] CI(actions): Replace deprecated `add-path` commands (#15892) This commit resolves the following warning in the CI logs: > Error: The `add-path` command is deprecated and will be disabled soon. > Please upgrade to using Environment Files. For more information see: > https://github.blog/changelog/2020-10-01-github-actions-deprecating-set-env-and-add-path-commands/ The deprecation is due to an injection vulnerability (CVE-2020-15228). See: - https://bugs.chromium.org/p/project-zero/issues/detail?id=2070 - https://github.com/nim-lang/Nim/runs/1373146963#step:8:1 --- .github/workflows/ci.yml.disabled | 4 ++-- .github/workflows/ci_docs.yml | 4 ++-- .github/workflows/ci_packages.yml | 4 ++-- .github/workflows/ci_ssl.yml | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.github/workflows/ci.yml.disabled b/.github/workflows/ci.yml.disabled index 446560663..183576456 100644 --- a/.github/workflows/ci.yml.disabled +++ b/.github/workflows/ci.yml.disabled @@ -85,11 +85,11 @@ jobs: curl -L https://nim-lang.org/download/dlls.zip -o dist/dlls.zip 7z x dist/mingw64.7z -odist 7z x dist/dlls.zip -obin - echo "::add-path::${{ github.workspace }}/dist/mingw64/bin" + echo "${{ github.workspace }}/dist/mingw64/bin" >> "${GITHUB_PATH}" - name: 'Add build binaries to PATH' shell: bash - run: echo "::add-path::${{ github.workspace }}/bin" + run: echo "${{ github.workspace }}/bin" >> "${GITHUB_PATH}" - name: 'Build csources' shell: bash diff --git a/.github/workflows/ci_docs.yml b/.github/workflows/ci_docs.yml index 718dcd7e3..74ca804f3 100644 --- a/.github/workflows/ci_docs.yml +++ b/.github/workflows/ci_docs.yml @@ -64,11 +64,11 @@ jobs: curl -L https://nim-lang.org/download/dlls.zip -o dist/dlls.zip 7z x dist/mingw64.7z -odist 7z x dist/dlls.zip -obin - echo "::add-path::${{ github.workspace }}/dist/mingw64/bin" + echo "${{ github.workspace }}/dist/mingw64/bin" >> "${GITHUB_PATH}" - name: 'Add build binaries to PATH' shell: bash - run: echo "::add-path::${{ github.workspace }}/bin" + run: echo "${{ github.workspace }}/bin" >> "${GITHUB_PATH}" - name: 'Get current csources version' id: csources-version diff --git a/.github/workflows/ci_packages.yml b/.github/workflows/ci_packages.yml index 0d2f89aa6..59e918f58 100644 --- a/.github/workflows/ci_packages.yml +++ b/.github/workflows/ci_packages.yml @@ -48,11 +48,11 @@ jobs: curl -L https://nim-lang.org/download/dlls.zip -o dist/dlls.zip 7z x dist/mingw64.7z -odist 7z x dist/dlls.zip -obin - echo "::add-path::${{ github.workspace }}/dist/mingw64/bin" + echo "${{ github.workspace }}/dist/mingw64/bin" >> "${GITHUB_PATH}" - name: 'Add build binaries to PATH' shell: bash - run: echo "::add-path::${{ github.workspace }}/bin" + run: echo "${{ github.workspace }}/bin" >> "${GITHUB_PATH}" - name: 'Build csources' shell: bash diff --git a/.github/workflows/ci_ssl.yml b/.github/workflows/ci_ssl.yml index 6a024eca0..0ec7aeb3b 100644 --- a/.github/workflows/ci_ssl.yml +++ b/.github/workflows/ci_ssl.yml @@ -49,11 +49,11 @@ jobs: curl -L https://nim-lang.org/download/dlls.zip -o dist/dlls.zip 7z x dist/mingw64.7z -odist 7z x dist/dlls.zip -obin - echo "::add-path::${{ github.workspace }}/dist/mingw64/bin" + echo "${{ github.workspace }}/dist/mingw64/bin" >> "${GITHUB_PATH}" - name: 'Add build binaries to PATH' shell: bash - run: echo "::add-path::${{ github.workspace }}/bin" + run: echo "${{ github.workspace }}/bin" >> "${GITHUB_PATH}" - name: 'Build 1-stage compiler from csources' shell: bash From 53eca459f1d96c710238be95bd3fb048b336f2f3 Mon Sep 17 00:00:00 2001 From: Aethylia Date: Mon, 9 Nov 2020 13:14:06 +0000 Subject: [PATCH 0940/3768] Added [:T] syntax explanation to generics tutorial. (#15890) * Added [:T] syntax explanation to generics tutorial. * Update doc/tut2.rst Co-authored-by: flywind <43030857+xflywind@users.noreply.github.com> * Update doc/tut2.rst Co-authored-by: flywind <43030857+xflywind@users.noreply.github.com> * Made second generics example runnable and added test line. * Update doc/tut2.rst * Update doc/tut2.rst * Update doc/tut2.rst Co-authored-by: flywind <43030857+xflywind@users.noreply.github.com> Co-authored-by: Andreas Rumpf --- doc/tut2.rst | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/doc/tut2.rst b/doc/tut2.rst index 94725a5d8..32faf452c 100644 --- a/doc/tut2.rst +++ b/doc/tut2.rst @@ -534,6 +534,19 @@ iterator or type. As the example shows, generics work with overloading: the best match of ``add`` is used. The built-in ``add`` procedure for sequences is not hidden and is used in the ``preorder`` iterator. +There is a special ``[:T]`` syntax when using generics with the method call syntax: + +.. code-block:: nim + :test: "nim c $1" + proc foo[T](i: T) = + discard + + var i: int + + # i.foo[int]() # Error: expression 'foo(i)' has no type (or is ambiguous) + + i.foo[:int]() # Success + Templates ========= From c1664f93b0024b558ff359f13ab6cd2e6c1b5cc7 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 9 Nov 2020 09:07:22 -0600 Subject: [PATCH 0941/3768] new: `nim -e:cmd` to run a command directly; also fixes #15731 (#15687) * new: `nim -i cmd` * rename -i to -e (for eval); consistent with majority of other programing languages * `nim e -e:cmd` now works; bugfix: `echo cmd | nim e -` now works * honor --betterRun * address comments * --eval alias for -e (replaces undocumented --eval which was a noop) * --eval now defaults to e (nimscript) instead of r * address comment: remove -e, only keep --eval * address comment * fixup * Update compiler/nimconf.nim Co-authored-by: Andreas Rumpf --- changelog.md | 1 + compiler/cmdlinehelper.nim | 6 +++++- compiler/commands.nim | 25 +++++++++++++++++++------ compiler/extccomp.nim | 11 +++++++++++ compiler/main.nim | 5 +++-- compiler/modules.nim | 13 +++++++++---- compiler/msgs.nim | 1 + compiler/nim.nim | 3 ++- compiler/nimconf.nim | 21 +++++++++++++-------- compiler/options.nim | 5 +++-- compiler/scriptconfig.nim | 4 ++-- compiler/vm.nim | 3 +++ doc/basicopt.txt | 3 +++ 13 files changed, 75 insertions(+), 26 deletions(-) diff --git a/changelog.md b/changelog.md index d00d07fa1..54a32b2a1 100644 --- a/changelog.md +++ b/changelog.md @@ -33,6 +33,7 @@ - Added `--declaredlocs` to show symbol declaration location in messages. - Source+Edit links now appear on top of every docgen'd page when `nim doc --git.url:url ...` is given. +- Added `nim --eval:cmd` to evaluate a command directly, see `nim --help` ## Tool changes diff --git a/compiler/cmdlinehelper.nim b/compiler/cmdlinehelper.nim index a415afce9..d5a26717d 100644 --- a/compiler/cmdlinehelper.nim +++ b/compiler/cmdlinehelper.nim @@ -41,7 +41,9 @@ proc initDefinesProg*(self: NimProg, conf: ConfigRef, name: string) = proc processCmdLineAndProjectPath*(self: NimProg, conf: ConfigRef) = self.processCmdLine(passCmd1, "", conf) - if self.supportsStdinFile and conf.projectName == "-": + if conf.projectIsCmd and conf.projectName in ["-", ""]: + handleCmdInput(conf) + elif self.supportsStdinFile and conf.projectName == "-": handleStdinInput(conf) elif conf.projectName != "": try: @@ -59,6 +61,8 @@ proc loadConfigsAndRunMainCommand*(self: NimProg, cache: IdentCache; conf: Confi graph: ModuleGraph): bool = if self.suggestMode: conf.command = "nimsuggest" + if conf.command == "e": + incl(conf.globalOptions, optWasNimscript) loadConfigs(DefaultConfig, cache, conf, graph.idgen) # load all config files if not self.suggestMode: diff --git a/compiler/commands.nim b/compiler/commands.nim index cb7517f06..7a94fb612 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -383,19 +383,33 @@ proc dynlibOverride(conf: ConfigRef; switch, arg: string, pass: TCmdLinePass, in expectArg(conf, switch, arg, pass, info) options.inclDynlibOverride(conf, arg) -proc handleStdinInput*(conf: ConfigRef) = - conf.projectName = "stdinfile" +template handleStdinOrCmdInput = conf.projectFull = conf.projectName.AbsoluteFile conf.projectPath = AbsoluteDir getCurrentDir() - conf.projectIsStdin = true if conf.outDir.isEmpty: conf.outDir = getNimcacheDir(conf) +proc handleStdinInput*(conf: ConfigRef) = + conf.projectName = "stdinfile" + conf.projectIsStdin = true + handleStdinOrCmdInput() + +proc handleCmdInput*(conf: ConfigRef) = + conf.projectName = "cmdfile" + handleStdinOrCmdInput() + proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; conf: ConfigRef) = var key, val: string case switch.normalize + of "eval": + expectArg(conf, switch, arg, pass, info) + conf.projectIsCmd = true + conf.cmdInput = arg # can be empty (a nim file with empty content is valid too) + if conf.command == "": + conf.command = "e" # better than "r" as a default + conf.implicitCmd = true of "path", "p": expectArg(conf, switch, arg, pass, info) for path in nimbleSubs(conf, arg): @@ -781,9 +795,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "def": expectNoArg(conf, switch, arg, pass, info) conf.ideCmd = ideDef - of "eval": - expectArg(conf, switch, arg, pass, info) - conf.evalExpr = arg of "context": expectNoArg(conf, switch, arg, pass, info) conf.ideCmd = ideCon @@ -936,6 +947,8 @@ proc processSwitch*(pass: TCmdLinePass; p: OptParser; config: ConfigRef) = proc processArgument*(pass: TCmdLinePass; p: OptParser; argsCount: var int; config: ConfigRef): bool = + if argsCount == 0 and config.implicitCmd: + argsCount.inc if argsCount == 0: # nim filename.nims is the same as "nim e filename.nims": if p.key.endsWith(".nims"): diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 11b001f9d..d97f9a887 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -1022,6 +1022,10 @@ proc writeJsonBuildInstructions*(conf: ConfigRef) = lit ",\L\"stdinInput\": " lit $(%* conf.projectIsStdin) + lit ",\L\"projectIsCmd\": " + lit $(%* conf.projectIsCmd) + lit ",\L\"cmdInput\": " + lit $(%* conf.cmdInput) if optRun in conf.globalOptions or isDefined(conf, "nimBetterRun"): lit ",\L\"cmdline\": " @@ -1051,11 +1055,18 @@ proc changeDetectedViaJsonBuildInstructions*(conf: ConfigRef; projectfile: Absol return true if not data.hasKey("stdinInput"): return true let stdinInput = data["stdinInput"].getBool + let projectIsCmd = data["projectIsCmd"].getBool if conf.projectIsStdin or stdinInput: # could optimize by returning false if stdin input was the same, # but I'm not sure how to get full stding input return true + if conf.projectIsCmd or projectIsCmd: + if not (conf.projectIsCmd and projectIsCmd): return true + if not data.hasKey("cmdInput"): return true + let cmdInput = data["cmdInput"].getStr + if cmdInput != conf.cmdInput: return true + let depfilesPairs = data["depfiles"] doAssert depfilesPairs.kind == JArray for p in depfilesPairs: diff --git a/compiler/main.nim b/compiler/main.nim index af9102414..242bc0719 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -348,11 +348,12 @@ proc mainCommand*(graph: ModuleGraph) = conf.cmd = cmdInteractive commandInteractive(graph) of "e": - if not fileExists(conf.projectFull): + if conf.projectIsCmd or conf.projectIsStdin: discard + elif not fileExists(conf.projectFull): rawMessage(conf, errGenerated, "NimScript file does not exist: " & conf.projectFull.string) elif not conf.projectFull.string.endsWith(".nims"): rawMessage(conf, errGenerated, "not a NimScript file: " & conf.projectFull.string) - # main NimScript logic handled in cmdlinehelper.nim. + # main NimScript logic handled in `loadConfigs`. of "nop", "help": # prevent the "success" message: conf.cmd = cmdDump diff --git a/compiler/modules.nim b/compiler/modules.nim index 87a6bc507..748b74953 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -82,6 +82,13 @@ proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags): P var flags = flags if fileIdx == graph.config.projectMainIdx2: flags.incl sfMainModule result = graph.getModule(fileIdx) + + template processModuleAux = + var s: PLLStream + if sfMainModule in flags: + if graph.config.projectIsStdin: s = stdin.llStreamOpen + elif graph.config.projectIsCmd: s = llStreamOpen(graph.config.cmdInput) + discard processModule(graph, result, idGeneratorFromModule(result), s) if result == nil: let filename = AbsoluteFile toFullPath(graph.config, fileIdx) result = loadModuleSym(graph, fileIdx, filename) @@ -91,15 +98,13 @@ proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags): P registerModule(graph, result) else: partialInitModule(result, graph, fileIdx, filename) - discard processModule(graph, result, idGeneratorFromModule(result), - if sfMainModule in flags and graph.config.projectIsStdin: stdin.llStreamOpen else: nil) + processModuleAux() elif graph.isDirty(result): result.flags.excl sfDirty # reset module fields: initStrTable(result.tab) result.ast = nil - discard processModule(graph, result, idGeneratorFromModule(result), - if sfMainModule in flags and graph.config.projectIsStdin: stdin.llStreamOpen else: nil) + processModuleAux() graph.markClientsDirty(fileIdx) proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PSym = diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 0f5879ff6..7b691323f 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -523,6 +523,7 @@ proc liMessage*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string, let s = if isRaw: arg else: getMessageStr(msg, arg) if not ignoreMsg: let loc = if info != unknownLineInfo: conf.toFileLineCol(info) & " " else: "" + # we could also show `conf.cmdInput` here for `projectIsCmd` var kindmsg = if kind.len > 0: KindFormat % kind else: "" if conf.structuredErrorHook != nil: conf.structuredErrorHook(conf, info, s & kindmsg, sev) diff --git a/compiler/nim.nim b/compiler/nim.nim index 15aeccb33..9ff0eedbc 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -79,7 +79,8 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = self.processCmdLineAndProjectPath(conf) var graph = newModuleGraph(cache, conf) - if not self.loadConfigsAndRunMainCommand(cache, conf, graph): return + if not self.loadConfigsAndRunMainCommand(cache, conf, graph): + return mainCommand(graph) if conf.hasHint(hintGCStats): echo(GC_getStatistics()) #echo(GC_getStatistics()) diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim index 05f234ce6..b6ec1bbc8 100644 --- a/compiler/nimconf.nim +++ b/compiler/nimconf.nim @@ -248,11 +248,16 @@ proc loadConfigs*(cfg: RelativeFile; cache: IdentCache; conf: ConfigRef; idgen: if readConfigFile(configPath, cache, conf): configFiles.add(configPath) - template runNimScriptIfExists(path: AbsoluteFile) = + template runNimScriptIfExists(path: AbsoluteFile, isMain = false) = let p = path # eval once - if fileExists(p): + var s: PLLStream + if isMain and optWasNimscript in conf.globalOptions: + if conf.projectIsStdin: s = stdin.llStreamOpen + elif conf.projectIsCmd: s = llStreamOpen(conf.cmdInput) + if s == nil and fileExists(p): s = llStreamOpen(p, fmRead) + if s != nil: configFiles.add(p) - runNimScript(cache, p, idgen, freshDefines = false, conf) + runNimScript(cache, p, idgen, freshDefines = false, conf, s) if optSkipSystemConfigFile notin conf.globalOptions: readConfigFile(getSystemConfigPath(conf, cfg)) @@ -288,19 +293,19 @@ proc loadConfigs*(cfg: RelativeFile; cache: IdentCache; conf: ConfigRef; idgen: runNimScriptIfExists(pd / DefaultConfigNims) let scriptFile = conf.projectFull.changeFileExt("nims") - let isMain = scriptFile == conf.projectFull + let scriptIsProj = scriptFile == conf.projectFull template showHintConf = for filename in configFiles: # delayed to here so that `hintConf` is honored rawMessage(conf, hintConf, filename.string) - if isMain: + if scriptIsProj: showHintConf() configFiles.setLen 0 if conf.command != "nimsuggest": - runNimScriptIfExists(scriptFile) + runNimScriptIfExists(scriptFile, isMain = true) else: - if not isMain: - runNimScriptIfExists(scriptFile) + if not scriptIsProj: + runNimScriptIfExists(scriptFile, isMain = true) else: # 'nimsuggest foo.nims' means to just auto-complete the NimScript file discard diff --git a/compiler/options.nim b/compiler/options.nim index 872ab9582..58d09938c 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -246,11 +246,13 @@ type evalMacroCounter*: int exitcode*: int8 cmd*: TCommands # the command + cmdInput*: string # input command + projectIsCmd*: bool # whether we're compiling from a command input + implicitCmd*: bool # whether some flag triggered an implicit `command` selectedGC*: TGCMode # the selected GC (+) exc*: ExceptionSystem verbosity*: int # how verbose the compiler is numberOfProcessors*: int # number of processors - evalExpr*: string # expression for idetools --eval lastCmdTime*: float # when caas is enabled, we measure each command symbolFiles*: SymbolFilesOption @@ -418,7 +420,6 @@ proc newConfigRef*(): ConfigRef = macrosToExpand: newStringTable(modeStyleInsensitive), arcToExpand: newStringTable(modeStyleInsensitive), m: initMsgConfig(), - evalExpr: "", cppDefines: initHashSet[string](), headerFile: "", features: {}, legacyFeatures: {}, foreignPackageNotes: {hintProcessing, warnUnknownMagic, hintQuitCalled, hintExecuting}, diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim index ab7e5e0b5..a353278d6 100644 --- a/compiler/scriptconfig.nim +++ b/compiler/scriptconfig.nim @@ -199,7 +199,7 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string; proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile; idgen: IdGenerator; - freshDefines=true; conf: ConfigRef) = + freshDefines=true; conf: ConfigRef, stream: PLLStream) = let oldSymbolFiles = conf.symbolFiles conf.symbolFiles = disabledSf @@ -226,7 +226,7 @@ proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile; graph.vm = vm graph.compileSystemModule() - discard graph.processModule(m, vm.idgen, llStreamOpen(scriptName, fmRead)) + discard graph.processModule(m, vm.idgen, stream) # watch out, "newruntime" can be set within NimScript itself and then we need # to remember this: diff --git a/compiler/vm.nim b/compiler/vm.nim index 761186dc3..8aac11ff7 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -2114,6 +2114,9 @@ proc evalStmt*(c: PCtx, n: PNode) = discard execute(c, start) proc evalExpr*(c: PCtx, n: PNode): PNode = + # deadcode + # `nim --eval:"expr"` might've used it at some point for idetools; could + # be revived for nimsuggest let n = transformExpr(c.graph, c.idgen, c.module, n) let start = genExpr(c, n) assert c.code[start].opcode != opcEof diff --git a/doc/basicopt.txt b/doc/basicopt.txt index 7ea55cf6c..a2db1c92e 100644 --- a/doc/basicopt.txt +++ b/doc/basicopt.txt @@ -31,6 +31,9 @@ Options: --app:console|gui|lib|staticlib generate a console app|GUI app|DLL|static library -r, --run run the compiled program with given arguments + --eval:cmd evaluates nim code directly; e.g.: `nim --eval:"echo 1"` + defaults to `e` (nimscript) but customizable: + `nim r --eval:'for a in stdin.lines: echo a'` --fullhelp show all command line switches -h, --help show this help -v, --version show detailed version information From 338602a4021771c8a1cbcedfe30738c67b957646 Mon Sep 17 00:00:00 2001 From: cooldome Date: Mon, 9 Nov 2020 18:24:32 +0000 Subject: [PATCH 0942/3768] fix #15825 (#15894) * fix #15825 * better fix --- compiler/semtypes.nim | 2 +- tests/destructor/tdestructor3.nim | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index f111f6308..5feb25a59 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1056,7 +1056,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, # disable the bindOnce behavior for the type class result = recurse(paramType.base, true) - of tyAlias, tyOwned: + of tyAlias, tyOwned, tySink: result = recurse(paramType.base) of tySequence, tySet, tyArray, tyOpenArray, diff --git a/tests/destructor/tdestructor3.nim b/tests/destructor/tdestructor3.nim index eb49f3f15..3f5eb2cc1 100644 --- a/tests/destructor/tdestructor3.nim +++ b/tests/destructor/tdestructor3.nim @@ -172,3 +172,14 @@ when true: # D20200607T202043 main2() + +#------------------------------------------------------------ +# Issue #15825 + +type + Union = string | int | char + +proc run(a: sink Union) = + discard + +run("123") From d8e7caf5dd4ab70b7429896c1587cde992e7855a Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Tue, 10 Nov 2020 09:23:58 +0800 Subject: [PATCH 0943/3768] follow #15874(add testcase for #15874) (#15893) --- tests/stdlib/tterminal_15874.nim | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 tests/stdlib/tterminal_15874.nim diff --git a/tests/stdlib/tterminal_15874.nim b/tests/stdlib/tterminal_15874.nim new file mode 100644 index 000000000..c3455c350 --- /dev/null +++ b/tests/stdlib/tterminal_15874.nim @@ -0,0 +1,8 @@ +discard """ + cmd: "nim c --app:console $file" + action: "compile" +""" + +import terminal + +writeStyled("hello", {styleBright}) From ee78d7610853f4866c9b3d1e81d523b7ef339da0 Mon Sep 17 00:00:00 2001 From: Miran Date: Tue, 10 Nov 2020 09:41:26 +0100 Subject: [PATCH 0944/3768] rst: add support for markdown tables (#15854) * rst: add support for markdown tables * change template into proc * don't create unnecessary `seq[string]` --- compiler/docgen.nim | 1 + compiler/lineinfos.nim | 30 +++++++------ lib/packages/docutils/rst.nim | 73 ++++++++++++++++++++++++++++++++ lib/packages/docutils/rstast.nim | 2 +- lib/packages/docutils/rstgen.nim | 2 +- tests/stdlib/trstgen.nim | 24 +++++++++++ 6 files changed, 116 insertions(+), 16 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 7609c96ee..8f6b7a560 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -127,6 +127,7 @@ template declareClosures = of meCannotOpenFile: k = errCannotOpenFile of meExpected: k = errXExpected of meGridTableNotImplemented: k = errGridTableNotImplemented + of meMarkdownIllformedTable: k = errMarkdownIllformedTable of meNewSectionExpected: k = errNewSectionExpected of meGeneralParseError: k = errGeneralParseError of meInvalidDirective: k = errInvalidDirectiveX diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index 759002312..f9ea90caf 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -30,42 +30,43 @@ type errUnknown, errInternal, errIllFormedAstX, errCannotOpenFile, errXExpected, errGridTableNotImplemented, + errMarkdownIllformedTable, errGeneralParseError, errNewSectionExpected, errInvalidDirectiveX, errProveInit, # deadcode errGenerated, errUser, - - warnCannotOpenFile = "CannotOpenFile", warnOctalEscape = "OctalEscape", + + warnCannotOpenFile = "CannotOpenFile", warnOctalEscape = "OctalEscape", warnXIsNeverRead = "XIsNeverRead", warnXmightNotBeenInit = "XmightNotBeenInit", warnDeprecated = "Deprecated", warnConfigDeprecated = "ConfigDeprecated", - warnSmallLshouldNotBeUsed = "SmallLshouldNotBeUsed", warnUnknownMagic = "UnknownMagic", - warnRedefinitionOfLabel = "RedefinitionOfLabel", warnUnknownSubstitutionX = "UnknownSubstitutionX", - warnLanguageXNotSupported = "LanguageXNotSupported", warnFieldXNotSupported = "FieldXNotSupported", + warnSmallLshouldNotBeUsed = "SmallLshouldNotBeUsed", warnUnknownMagic = "UnknownMagic", + warnRedefinitionOfLabel = "RedefinitionOfLabel", warnUnknownSubstitutionX = "UnknownSubstitutionX", + warnLanguageXNotSupported = "LanguageXNotSupported", warnFieldXNotSupported = "FieldXNotSupported", warnCommentXIgnored = "CommentXIgnored", warnTypelessParam = "TypelessParam", - warnUseBase = "UseBase", warnWriteToForeignHeap = "WriteToForeignHeap", + warnUseBase = "UseBase", warnWriteToForeignHeap = "WriteToForeignHeap", warnUnsafeCode = "UnsafeCode", warnUnusedImportX = "UnusedImport", warnInheritFromException = "InheritFromException", warnEachIdentIsTuple = "EachIdentIsTuple", warnUnsafeSetLen = "UnsafeSetLen", warnUnsafeDefault = "UnsafeDefault", warnProveInit = "ProveInit", warnProveField = "ProveField", warnProveIndex = "ProveIndex", warnUnreachableElse = "UnreachableElse", warnUnreachableCode = "UnreachableCode", warnStaticIndexCheck = "IndexCheck", warnGcUnsafe = "GcUnsafe", warnGcUnsafe2 = "GcUnsafe2", - warnUninit = "Uninit", warnGcMem = "GcMem", warnDestructor = "Destructor", - warnLockLevel = "LockLevel", warnResultShadowed = "ResultShadowed", - warnInconsistentSpacing = "Spacing", warnCaseTransition = "CaseTransition", - warnCycleCreated = "CycleCreated", warnObservableStores = "ObservableStores", + warnUninit = "Uninit", warnGcMem = "GcMem", warnDestructor = "Destructor", + warnLockLevel = "LockLevel", warnResultShadowed = "ResultShadowed", + warnInconsistentSpacing = "Spacing", warnCaseTransition = "CaseTransition", + warnCycleCreated = "CycleCreated", warnObservableStores = "ObservableStores", warnUser = "User", hintSuccess = "Success", hintSuccessX = "SuccessX", hintCC = "CC", hintLineTooLong = "LineTooLong", hintXDeclaredButNotUsed = "XDeclaredButNotUsed", hintXCannotRaiseY = "XCannotRaiseY", hintConvToBaseNotNeeded = "ConvToBaseNotNeeded", - hintConvFromXtoItselfNotNeeded = "ConvFromXtoItselfNotNeeded", hintExprAlwaysX = "ExprAlwaysX", - hintQuitCalled = "QuitCalled", hintProcessing = "Processing", hintCodeBegin = "CodeBegin", + hintConvFromXtoItselfNotNeeded = "ConvFromXtoItselfNotNeeded", hintExprAlwaysX = "ExprAlwaysX", + hintQuitCalled = "QuitCalled", hintProcessing = "Processing", hintCodeBegin = "CodeBegin", hintCodeEnd = "CodeEnd", hintConf = "Conf", hintPath = "Path", - hintConditionAlwaysTrue = "CondTrue", hintConditionAlwaysFalse = "CondFalse", hintName = "Name", + hintConditionAlwaysTrue = "CondTrue", hintConditionAlwaysFalse = "CondFalse", hintName = "Name", hintPattern = "Pattern", hintExecuting = "Exec", hintLinking = "Link", hintDependency = "Dependency", - hintSource = "Source", hintPerformance = "Performance", hintStackTrace = "StackTrace", + hintSource = "Source", hintPerformance = "Performance", hintStackTrace = "StackTrace", hintGCStats = "GCStats", hintGlobalVar = "GlobalVar", hintExpandMacro = "ExpandMacro", hintUser = "User", hintUserRaw = "UserRaw", hintExtendedContext = "ExtendedContext", hintMsgOrigin = "MsgOrigin", # since 1.3.5 @@ -79,6 +80,7 @@ const errCannotOpenFile: "cannot open '$1'", errXExpected: "'$1' expected", errGridTableNotImplemented: "grid table is not implemented", + errMarkdownIllformedTable: "illformed delimiter row of a markdown table", errGeneralParseError: "general parse error", errNewSectionExpected: "new section expected", errInvalidDirectiveX: "invalid directive: '$1'", diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 56bfe580b..c04a6e07c 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -36,6 +36,7 @@ type meCannotOpenFile, meExpected, meGridTableNotImplemented, + meMarkdownIllformedTable, meNewSectionExpected, meGeneralParseError, meInvalidDirective, @@ -53,6 +54,7 @@ const meCannotOpenFile: "cannot open '$1'", meExpected: "'$1' expected", meGridTableNotImplemented: "grid table is not implemented", + meMarkdownIllformedTable: "illformed delimiter row of a markdown table", meNewSectionExpected: "new section expected", meGeneralParseError: "general parse error", meInvalidDirective: "invalid directive: '$1'", @@ -1091,6 +1093,13 @@ proc isMarkdownHeadline(p: RstParser): bool = if p.tok[p.idx+2].kind in {tkWord, tkOther, tkPunct}: result = true +proc findPipe(p: RstParser, start: int): bool = + var i = start + while true: + if p.tok[i].symbol == "|": return true + if p.tok[i].kind in {tkIndent, tkEof}: return false + inc i + proc whichSection(p: RstParser): RstNodeKind = case p.tok[p.idx].kind of tkAdornment: @@ -1104,6 +1113,9 @@ proc whichSection(p: RstParser): RstNodeKind = of tkPunct: if isMarkdownHeadline(p): result = rnHeadline + elif roSupportMarkdown in p.s.options and predNL(p) and + match(p, p.idx, "| w") and findPipe(p, p.idx+3): + result = rnMarkdownTable elif p.tok[p.idx].symbol == "```": result = rnCodeBlock elif match(p, tokenAfterNewline(p), "ai"): @@ -1206,6 +1218,9 @@ proc parseHeadline(p: var RstParser): PRstNode = type IntSeq = seq[int] + ColumnLimits = tuple + first, last: int + ColSeq = seq[ColumnLimits] proc tokEnd(p: RstParser): int = result = p.tok[p.idx].col + len(p.tok[p.idx].symbol) - 1 @@ -1280,6 +1295,63 @@ proc parseSimpleTable(p: var RstParser): PRstNode = add(a, b) add(result, a) +proc readTableRow(p: var RstParser): ColSeq = + if p.tok[p.idx].symbol == "|": inc p.idx + while p.tok[p.idx].kind notin {tkIndent, tkEof}: + var limits: ColumnLimits + limits.first = p.idx + while p.tok[p.idx].kind notin {tkIndent, tkEof}: + if p.tok[p.idx].symbol == "|" and p.tok[p.idx-1].symbol != "\\": break + inc p.idx + limits.last = p.idx + result.add(limits) + if p.tok[p.idx].kind in {tkIndent, tkEof}: break + inc p.idx + p.idx = tokenAfterNewline(p) + +proc getColContents(p: var RstParser, colLim: ColumnLimits): string = + for i in colLim.first ..< colLim.last: + result.add(p.tok[i].symbol) + result.strip + +proc isValidDelimiterRow(p: var RstParser, colNum: int): bool = + let row = readTableRow(p) + if row.len != colNum: return false + for limits in row: + let content = getColContents(p, limits) + if content.len < 3 or not (content.startsWith("--") or content.startsWith(":-")): + return false + return true + +proc parseMarkdownTable(p: var RstParser): PRstNode = + var + row: ColSeq + colNum: int + a, b: PRstNode + q: RstParser + result = newRstNode(rnMarkdownTable) + + proc parseRow(p: var RstParser, cellKind: RstNodeKind, result: PRstNode) = + row = readTableRow(p) + if colNum == 0: colNum = row.len # table header + elif row.len < colNum: row.setLen(colNum) + a = newRstNode(rnTableRow) + for j in 0 ..< colNum: + b = newRstNode(cellKind) + initParser(q, p.s) + q.col = p.col + q.line = p.tok[p.idx].line - 1 + q.filename = p.filename + q.col += getTokens(getColContents(p, row[j]), false, q.tok) + b.add(parseDoc(q)) + a.add(b) + result.add(a) + + parseRow(p, rnTableHeaderCell, result) + if not isValidDelimiterRow(p, colNum): rstMessage(p, meMarkdownIllformedTable) + while predNL(p) and p.tok[p.idx].symbol == "|": + parseRow(p, rnTableDataCell, result) + proc parseTransition(p: var RstParser): PRstNode = result = newRstNode(rnTransition) inc(p.idx) @@ -1461,6 +1533,7 @@ proc parseSection(p: var RstParser, result: PRstNode) = of rnHeadline: a = parseHeadline(p) of rnOverline: a = parseOverline(p) of rnTable: a = parseSimpleTable(p) + of rnMarkdownTable: a = parseMarkdownTable(p) of rnOptionList: a = parseOptionList(p) else: #InternalError("rst.parseSection()") diff --git a/lib/packages/docutils/rstast.nim b/lib/packages/docutils/rstast.nim index 044ea2c14..8b2159aeb 100644 --- a/lib/packages/docutils/rstast.nim +++ b/lib/packages/docutils/rstast.nim @@ -37,7 +37,7 @@ type rnLineBlock, # the | thingie rnLineBlockItem, # sons of the | thing rnBlockQuote, # text just indented - rnTable, rnGridTable, rnTableRow, rnTableHeaderCell, rnTableDataCell, + rnTable, rnGridTable, rnMarkdownTable, rnTableRow, rnTableHeaderCell, rnTableDataCell, rnLabel, # used for footnotes and other things rnFootnote, # a footnote rnCitation, # similar to footnote diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index c70998edb..604cc3ca9 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -1094,7 +1094,7 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = of rnBlockQuote: renderAux(d, n, "

      $1

      \n", "\\begin{quote}$1\\end{quote}\n", result) - of rnTable, rnGridTable: + of rnTable, rnGridTable, rnMarkdownTable: renderAux(d, n, "$1
      ", "\\begin{table}\\begin{rsttab}{" & diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim index 8fdbf3911..d35bc5821 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -153,3 +153,27 @@ suite "YAML syntax highlighting": assert a == """(( Nim ))""" assert b == """((Nim))""" assert c == """[Nim]""" + + test "Markdown tables": + let input1 = """ +| A1 header | A2 \| not fooled +| :--- | ----: | +| C1 | C2 **bold** | ignored | +| D1 `code \|` | D2 | also ignored +| E1 \| text | +| | F2 without pipe +not in table""" + let output1 = rstToHtml(input1, {roSupportMarkdown}, defaultConfig()) + assert output1 == """ + + + + +
      A1 headerA2 | not fooled
      C1C2 bold
      D1 code |D2
      E1 | text
      F2 without pipe

      not in table

      +""" + let input2 = """ +| A1 header | A2 | +| --- | --- |""" + let output2 = rstToHtml(input2, {roSupportMarkdown}, defaultConfig()) + assert output2 == """ +
      A1 headerA2
      """ From 17acf146a07e89ecb7499ee0910417f83fd1ee25 Mon Sep 17 00:00:00 2001 From: narimiran Date: Tue, 10 Nov 2020 12:46:13 +0100 Subject: [PATCH 0945/3768] followup #15854: add a changelog entry about markdown tables support --- changelog.md | 45 +++++++++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/changelog.md b/changelog.md index 54a32b2a1..801d50b94 100644 --- a/changelog.md +++ b/changelog.md @@ -6,24 +6,35 @@ - Make `{.requiresInit.}` pragma to work for `distinct` types. -- Added a macros `enumLen` for returning the number of items in an enum to the `typetraits.nim` module. +- Added a macros `enumLen` for returning the number of items in an enum to the + `typetraits.nim` module. - `prelude` now works with the JavaScript target. -- Added `ioutils` module containing `duplicate` and `duplicateTo` to duplicate `FileHandle` using C function `dup` and `dup2`. +- Added `ioutils` module containing `duplicate` and `duplicateTo` to duplicate + `FileHandle` using C function `dup` and `dup2`. + - Added `almostEqual` in `math` for comparing two float values using a machine epsilon. -- The JSON module can now handle integer literals and floating point literals of arbitrary length and precision. - Numbers that do not fit the underlying `BiggestInt` or `BiggestFloat` fields are kept as string literals and - one can use external BigNum libraries to handle these. The `parseFloat` family of functions also has now optional - `rawIntegers` and `rawFloats` parameters that can be used to enforce that all integer or float literals remain - in the "raw" string form so that client code can easily treat small and large numbers uniformly. +- The JSON module can now handle integer literals and floating point literals of + arbitrary length and precision. + Numbers that do not fit the underlying `BiggestInt` or `BiggestFloat` fields are + kept as string literals and one can use external BigNum libraries to handle these. + The `parseFloat` family of functions also has now optional `rawIntegers` and + `rawFloats` parameters that can be used to enforce that all integer or float + literals remain in the "raw" string form so that client code can easily treat + small and large numbers uniformly. -- Added `randState` template that exposes the default random number generator. Useful for library authors. +- Added `randState` template that exposes the default random number generator. + Useful for library authors. + +- Added std/enumutils module containing `genEnumCaseStmt` macro that generates + case statement to parse string to enum. + +- Removed deprecated `iup` module from stdlib, it has already moved to + [nimble](https://github.com/nim-lang/iup). -- Added std/enumutils module containing `genEnumCaseStmt` macro that generates case statement to parse string to enum. -- Removed deprecated `iup` module from stdlib, it has already moved to [nimble](https://github.com/nim-lang/iup). ## Language changes @@ -32,8 +43,18 @@ ## Compiler changes - Added `--declaredlocs` to show symbol declaration location in messages. -- Source+Edit links now appear on top of every docgen'd page when `nim doc --git.url:url ...` is given. -- Added `nim --eval:cmd` to evaluate a command directly, see `nim --help` + +- Source+Edit links now appear on top of every docgen'd page when + `nim doc --git.url:url ...` is given. + +- Added `nim --eval:cmd` to evaluate a command directly, see `nim --help`. + ## Tool changes + +- The rst parser now supports markdown table syntax. + Known limitations: + - cell alignment is not supported, i.e. alignment annotations in a delimiter + row (`:---`, `:--:`, `---:`) are ignored, + - every table row must start with `|`, e.g. `| cell 1 | cell 2 |`. From b38f0403fd3c66fdeab3806a12e1a19bcb696e03 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Tue, 10 Nov 2020 20:15:16 +0800 Subject: [PATCH 0946/3768] fix #12471 (#15901) * fix #12471 * add testcase * fix * test nimdoc is tricky * Delete t12471.nim --- lib/pure/selectors.nim | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim index e89cc7447..933b5491a 100644 --- a/lib/pure/selectors.nim +++ b/lib/pure/selectors.nim @@ -47,6 +47,9 @@ when defined(nimdoc): Selector*[T] = ref object ## An object which holds descriptors to be checked for read/write status + IOSelectorsException* = object of CatchableError + ## Exception that is raised if an IOSelectors error occurs. + Event* {.pure.} = enum ## An enum which hold event types Read, ## Descriptor is available for read From c44ae647a9f3c987eed2b2c1c31f127f986c24f9 Mon Sep 17 00:00:00 2001 From: narimiran Date: Tue, 10 Nov 2020 13:25:05 +0100 Subject: [PATCH 0947/3768] fix `norm` package testing command --- testament/important_packages.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index ef7dded4b..22d58ba3e 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -88,7 +88,7 @@ pkg2 "nimterop", "nimble minitest" pkg2 "nimwc", "nim c nimwc.nim" # pkg2 "nimx", "nim c --threads:on test/main.nim" # pkg2 "nitter", "nim c src/nitter.nim", "https://github.com/zedeus/nitter" -pkg2 "norm", "nim c -r tests/tsqliterows.nim" +pkg2 "norm", "nim c -r tests/sqlite/trows.nim" pkg2 "npeg", "nimble testarc" pkg2 "numericalnim", "nim c -r tests/test_integrate.nim" pkg2 "optionsutils" From bc3c0487d3059900864d6e8074cf83555a5c446d Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 10 Nov 2020 10:54:10 -0600 Subject: [PATCH 0948/3768] remove ioutils mention in changelog (#15907) --- changelog.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/changelog.md b/changelog.md index 801d50b94..bc8ad2295 100644 --- a/changelog.md +++ b/changelog.md @@ -11,9 +11,6 @@ - `prelude` now works with the JavaScript target. -- Added `ioutils` module containing `duplicate` and `duplicateTo` to duplicate - `FileHandle` using C function `dup` and `dup2`. - - Added `almostEqual` in `math` for comparing two float values using a machine epsilon. - The JSON module can now handle integer literals and floating point literals of From bbe49a14ae827b6474d692042406716a3b3dd71f Mon Sep 17 00:00:00 2001 From: Miran Date: Tue, 10 Nov 2020 21:53:25 +0100 Subject: [PATCH 0949/3768] Correct all eggs (#15906) * "eg" is a misspelled "egg", "e.g." is "exempli gratia" * Also, "ie" is "i.e.". --- build_all.sh | 4 ++-- compiler/main.nim | 2 +- compiler/msgs.nim | 8 ++++---- compiler/packagehandling.nim | 4 ++-- compiler/renderer.nim | 2 +- compiler/semcall.nim | 2 +- compiler/semexprs.nim | 2 +- compiler/semstmts.nim | 2 +- compiler/sigmatch.nim | 4 ++-- compiler/strutils2.nim | 2 +- compiler/types.nim | 2 +- compiler/vm.nim | 4 ++-- koch.nim | 8 ++++---- lib/core/macros.nim | 4 ++-- lib/pure/times.nim | 2 +- lib/std/jsonutils.nim | 2 +- lib/system/jssys.nim | 2 +- lib/system/strmantle.nim | 2 +- nimdoc/testproject/testproject.nim | 2 +- testament/categories.nim | 4 ++-- tests/config.nims | 2 +- tests/deps/jester-#head/jester/request.nim | 2 +- tests/deps/x11-1.0/xvlib.nim | 2 +- tests/misc/trunner.nim | 2 +- tests/stdlib/tjsonutils.nim | 2 +- tools/ci_generate.nim | 2 +- 26 files changed, 38 insertions(+), 38 deletions(-) diff --git a/build_all.sh b/build_all.sh index d3bba2211..e66980e56 100755 --- a/build_all.sh +++ b/build_all.sh @@ -1,7 +1,7 @@ #! /bin/sh # build development version of the compiler; can be rerun safely. -# arguments can be passed, eg `--os freebsd` +# arguments can be passed, e.g. `--os freebsd` set -u # error on undefined variables set -e # exit on first error @@ -24,7 +24,7 @@ build_nim_csources(){ # avoid changing dir in case of failure ( if [ $# -ne 0 ]; then - # some args were passed (eg: `--cpu i386`), need to call build.sh + # some args were passed (e.g.: `--cpu i386`), need to call build.sh build_nim_csources_via_script "$@" else # no args, use multiple Make jobs (5X faster on 16 cores: 10s instead of 50s) diff --git a/compiler/main.nim b/compiler/main.nim index 242bc0719..f9b0dd413 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -260,7 +260,7 @@ proc mainCommand*(graph: ModuleGraph) = conf.setNoteDefaults(warnLockLevel, false) # issue #13218 conf.setNoteDefaults(warnRedefinitionOfLabel, false) # issue #13218 # because currently generates lots of false positives due to conflation - # of labels links in doc comments, eg for random.rand: + # of labels links in doc comments, e.g. for random.rand: # ## * `rand proc<#rand,Rand,Natural>`_ that returns an integer # ## * `rand proc<#rand,Rand,range[]>`_ that returns a float commandDoc2(graph, false) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 7b691323f..be3dc5ec4 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -233,11 +233,11 @@ template toFullPathConsiderDirty*(conf: ConfigRef; info: TLineInfo): string = string toFullPathConsiderDirty(conf, info.fileIndex) type FilenameOption* = enum - foAbs # absolute path, eg: /pathto/bar/foo.nim - foRelProject # relative to project path, eg: ../foo.nim + foAbs # absolute path, e.g.: /pathto/bar/foo.nim + foRelProject # relative to project path, e.g.: ../foo.nim foMagicSauce # magic sauce, shortest of (foAbs, foRelProject) - foName # lastPathPart, eg: foo.nim - foShort # foName without extension, eg: foo + foName # lastPathPart, e.g.: foo.nim + foShort # foName without extension, e.g.: foo foStacktrace # if optExcessiveStackTrace: foAbs else: foName proc toFilenameOption*(conf: ConfigRef, fileIdx: FileIndex, opt: FilenameOption): string = diff --git a/compiler/packagehandling.nim b/compiler/packagehandling.nim index 9a3224de2..a781f1d51 100644 --- a/compiler/packagehandling.nim +++ b/compiler/packagehandling.nim @@ -16,7 +16,7 @@ iterator myParentDirs(p: string): string = yield current proc getNimbleFile*(conf: ConfigRef; path: string): string = - ## returns absolute path to nimble file, eg: /pathto/cligen.nimble + ## returns absolute path to nimble file, e.g.: /pathto/cligen.nimble var parents = 0 block packageSearch: for d in myParentDirs(path): @@ -35,7 +35,7 @@ proc getNimbleFile*(conf: ConfigRef; path: string): string = if parents <= 0: break proc getPackageName*(conf: ConfigRef; path: string): string = - ## returns nimble package name, eg: `cligen` + ## returns nimble package name, e.g.: `cligen` let path = getNimbleFile(conf, path) result = path.splitFile.name diff --git a/compiler/renderer.nim b/compiler/renderer.nim index a4152cf29..765bcded5 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -1226,7 +1226,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = infixArgument(g, n, 1) put(g, tkSpaces, Space) gsub(g, n, 0) # binary operator - # eg: `n1 == n2` decompses as following sum: + # e.g.: `n1 == n2` decompses as following sum: if n.len == 3 and not fits(g, oldLineLen + lsub(g, n[1]) + lsub(g, n[2]) + lsub(g, n[0]) + len(" ")): optNL(g, g.indent + longIndentWid) else: diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 0446d28c2..70a7e099c 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -326,7 +326,7 @@ proc getMsgDiagnostic(c: PContext, flags: TExprFlags, n, f: PNode): string = var typeHint = "" if sym == nil: # Perhaps we're in a `compiles(foo.bar)` expression, or - # in a concept, eg: + # in a concept, e.g.: # ExplainedConcept {.explain.} = concept x # x.foo is int # We could use: `(c.config $ n[1].info)` to get more context. diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index b2f0207e7..cb119b3e8 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -432,7 +432,7 @@ proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode = m.diagnostics = @[] m.diagnosticsEnabled = true res = typeRel(m, t2, t1) >= isSubtype # isNone - # `res = sameType(t1, t2)` would be wrong, eg for `int is (int|float)` + # `res = sameType(t1, t2)` would be wrong, e.g. for `int is (int|float)` result = newIntNode(nkIntLit, ord(res)) result.typ = n.typ diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 4b79cd546..f83864775 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -435,7 +435,7 @@ proc semLowerLetVarCustomPragma(c: PContext, a: PNode, n: PNode): PNode = var b = a[0] if b.kind == nkPragmaExpr: if b[1].len != 1: - # we could in future support pragmas w args eg: `var foo {.bar:"goo".} = expr` + # we could in future support pragmas w args e.g.: `var foo {.bar:"goo".} = expr` return nil let nodePragma = b[1][0] # see: `singlePragma` diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index d5039fcad..2e332f105 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1555,7 +1555,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, for i in 1.. varargs[T]) but we have already instantiated + # formal (e.g. seq[T] -> varargs[T]) but we have already instantiated # a container #assert arg.kind == nkHiddenStdConv # for 'nim check' # this assertion can be off diff --git a/compiler/strutils2.nim b/compiler/strutils2.nim index 6cb50347f..f44b811c7 100644 --- a/compiler/strutils2.nim +++ b/compiler/strutils2.nim @@ -12,7 +12,7 @@ proc dataPointer*[T](a: T): pointer = ## same as C++ `data` that works with std::string, std::vector etc. ## Note: safe to use when a.len == 0 but whether the result is nil or not ## is implementation defined for performance reasons. - # this could be improved with ocmpiler support to avoid the `if`, eg in C++ + # this could be improved with ocmpiler support to avoid the `if`, e.g. in C++ # `&a[0]` is well defined even if a.size() == 0 when T is string | seq: if a.len == 0: nil else: cast[pointer](a[0].unsafeAddr) diff --git a/compiler/types.nim b/compiler/types.nim index 42b7de416..1f2ee9d20 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -23,7 +23,7 @@ type preferTypeName, preferResolved, # fully resolved symbols preferMixed, - # most useful, shows: symbol + resolved symbols if it differs, eg: + # most useful, shows: symbol + resolved symbols if it differs, e.g.: # tuple[a: MyInt{int}, b: float] proc typeToString*(typ: PType; prefer: TPreferedDesc = preferName): string diff --git a/compiler/vm.nim b/compiler/vm.nim index 8aac11ff7..a6478f3f0 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -765,7 +765,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = if regs[rb].node.kind == nkRefTy: regs[ra].node = regs[rb].node[0] elif not maybeHandlePtr(regs[rb].node, regs[ra], false): - ## eg: typ.kind = tyObject + ## e.g.: typ.kind = tyObject ensureKind(rkNode) regs[ra].node = regs[rb].node else: @@ -996,7 +996,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = if nb.kind != nc.kind: discard elif (nb == nc) or (nb.kind == nkNilLit): ret = true # intentional elif sameConstant(nb, nc): ret = true - # this also takes care of procvar's, represented as nkTupleConstr, eg (nil, nil) + # this also takes care of procvar's, represented as nkTupleConstr, e.g. (nil, nil) elif nb.kind == nkIntLit and nc.kind == nkIntLit and nb.intVal == nc.intVal: # TODO: nkPtrLit let tb = nb.getTyp let tc = nc.getTyp diff --git a/koch.nim b/koch.nim index e34fdb4f4..47bde9d18 100644 --- a/koch.nim +++ b/koch.nim @@ -63,7 +63,7 @@ Possible Commands: distrohelper [bindir] helper for distro packagers tools builds Nim related tools toolsNoExternal builds Nim related tools (except external tools, - ie. nimble) + e.g. nimble) doesn't require network connectivity nimble builds the Nimble tool fusion clone fusion into the working tree @@ -76,14 +76,14 @@ Boot options: for bootstrapping Commands for core developers: - runCI runs continuous integration (CI), eg from travis + runCI runs continuous integration (CI), e.g. from travis docs [options] generates the full documentation csource -d:danger builds the C sources for installation pdf builds the PDF documentation zip builds the installation zip package xz builds the installation tar.xz package testinstall test tar.xz package; Unix only! - installdeps [options] installs external dependency (eg tinyc) to dist/ + installdeps [options] installs external dependency (e.g. tinyc) to dist/ tests [options] run the testsuite (run a subset of tests by specifying a category, e.g. `tests cat async`) temp options creates a temporary compiler for testing @@ -93,7 +93,7 @@ Web options: build the official docs, use UA-48159761-1 """ -let kochExe* = when isMainModule: os.getAppFilename() # always correct when koch is main program, even if `koch` exe renamed eg: `nim c -o:koch_debug koch.nim` +let kochExe* = when isMainModule: os.getAppFilename() # always correct when koch is main program, even if `koch` exe renamed e.g.: `nim c -o:koch_debug koch.nim` else: getAppDir() / "koch".exe # works for winrelease proc kochExec*(cmd: string) = diff --git a/lib/core/macros.nim b/lib/core/macros.nim index e4a56d6f9..d2985bc72 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -160,7 +160,7 @@ proc `==`*(a, b: NimSym): bool {.magic: "EqNimrodNode", noSideEffect, deprecated proc sameType*(a, b: NimNode): bool {.magic: "SameNodeType", noSideEffect.} = ## Compares two Nim nodes' types. Return true if the types are the same, - ## eg. true when comparing alias with original type. + ## e.g. true when comparing alias with original type. discard proc len*(n: NimNode): int {.magic: "NLen", noSideEffect.} @@ -280,7 +280,7 @@ else: # bootstrapping substitute when (NimMajor, NimMinor, NimPatch) >= (1, 3, 5) or defined(nimSymImplTransform): proc getImplTransformed*(symbol: NimNode): NimNode {.magic: "GetImplTransf", noSideEffect.} ## For a typed proc returns the AST after transformation pass; this is useful - ## for debugging how the compiler transforms code (eg: `defer`, `for`) but + ## for debugging how the compiler transforms code (e.g.: `defer`, `for`) but ## note that code transformations are implementation dependent and subject to change. ## See an example in `tests/macros/tmacros_various.nim`. diff --git a/lib/pure/times.nim b/lib/pure/times.nim index fb2f5e430..35dda131b 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -1049,7 +1049,7 @@ since((1, 3)): export isInitialized proc isLeapDay*(dt: DateTime): bool {.since: (1, 1).} = - ## returns whether `t` is a leap day, ie, Feb 29 in a leap year. This matters + ## Returns whether `t` is a leap day, i.e. Feb 29 in a leap year. This matters ## as it affects time offset calculations. runnableExamples: let dt = initDateTime(29, mFeb, 2020, 00, 00, 00, utc()) diff --git a/lib/std/jsonutils.nim b/lib/std/jsonutils.nim index dd174303a..50dcd9675 100644 --- a/lib/std/jsonutils.nim +++ b/lib/std/jsonutils.nim @@ -17,7 +17,7 @@ import std/[json,strutils,tables,sets,strtabs,options] #[ Future directions: -add a way to customize serialization, for eg: +add a way to customize serialization, for e.g.: * field renaming * allow serializing `enum` and `char` as `string` instead of `int` (enum is more compact/efficient, and robust to enum renamings, but string diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim index df05a9a62..c4c671ea3 100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -485,7 +485,7 @@ proc negInt64(a: int64): int64 {.compilerproc.} = result = a*(-1) proc nimFloatToString(a: float): cstring {.compilerproc.} = - ## ensures the result doesn't print like an integer, ie return 2.0, not 2 + ## ensures the result doesn't print like an integer, i.e. return 2.0, not 2 asm """ function nimOnlyDigitsOrMinus(n) { return n.toString().match(/^-?\d+$/); diff --git a/lib/system/strmantle.nim b/lib/system/strmantle.nim index 21b790603..fa6ff411b 100644 --- a/lib/system/strmantle.nim +++ b/lib/system/strmantle.nim @@ -113,7 +113,7 @@ when defined(nimHasInvariant): proc nimParseBiggestFloat(s: string, number: var BiggestFloat, start = 0): int {.compilerproc.} = # This routine attempt to parse float that can parsed quickly. - # ie whose integer part can fit inside a 53bits integer. + # i.e. whose integer part can fit inside a 53bits integer. # their real exponent must also be <= 22. If the float doesn't follow # these restrictions, transform the float into this form: # INTEGER * 10 ^ exponent and leave the work to standard `strtod()`. diff --git a/nimdoc/testproject/testproject.nim b/nimdoc/testproject/testproject.nim index ffddc356f..3cb7fddc5 100644 --- a/nimdoc/testproject/testproject.nim +++ b/nimdoc/testproject/testproject.nim @@ -49,7 +49,7 @@ proc isValid*[T](x: T): bool = x.len > 0 when true: # these cases appear redundant but they're actually (almost) all different at - # AST level and needed to ensure docgen keeps working, eg because of issues + # AST level and needed to ensure docgen keeps working, e.g. because of issues # like D20200526T163511 type Foo* = enum diff --git a/testament/categories.nim b/testament/categories.nim index fa9391055..0d19842ec 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -413,7 +413,7 @@ proc testStdlib(r: var TResults, pattern, options: string, cat: Category) = proc isValid(file: string): bool = for dir in parentDirs(file, inclusive = false): if dir.lastPathPart in ["includes", "nimcache"]: - # eg: lib/pure/includes/osenv.nim gives: Error: This is an include file for os.nim! + # e.g.: lib/pure/includes/osenv.nim gives: Error: This is an include file for os.nim! return false let name = extractFilename(file) if name.splitFile.ext != ".nim": return false @@ -433,7 +433,7 @@ proc testStdlib(r: var TResults, pattern, options: string, cat: Category) = #[ todo: this logic is fragile: - false positives (if appears in a comment), or false negatives, eg + false positives (if appears in a comment), or false negatives, e.g. `when defined(osx) and isMainModule`. Instead of fixing this, see https://github.com/nim-lang/Nim/issues/10045 for a much better way. diff --git a/tests/config.nims b/tests/config.nims index e91c3aa4f..640df9cad 100644 --- a/tests/config.nims +++ b/tests/config.nims @@ -1,7 +1,7 @@ switch("path", "$lib/../testament/lib") # so we can `import stdtest/foo` inside tests # Using $lib/../ instead of $nim/ so you can use a different nim to run tests - # during local testing, eg nim --lib:lib. + # during local testing, e.g. nim --lib:lib. ## prevent common user config settings to interfere with testament expectations ## Indifidual tests can override this if needed to test for these options. diff --git a/tests/deps/jester-#head/jester/request.nim b/tests/deps/jester-#head/jester/request.nim index 7c6a1a961..20e5c840f 100644 --- a/tests/deps/jester-#head/jester/request.nim +++ b/tests/deps/jester-#head/jester/request.nim @@ -56,7 +56,7 @@ proc path*(req: Request): string = return u.path proc reqMethod*(req: Request): HttpMethod = - ## Request method, eg. HttpGet, HttpPost + ## Request method, e.g. HttpGet, HttpPost when useHttpBeast: req.req.httpMethod.get() else: diff --git a/tests/deps/x11-1.0/xvlib.nim b/tests/deps/x11-1.0/xvlib.nim index e642cb350..354a4b93b 100644 --- a/tests/deps/x11-1.0/xvlib.nim +++ b/tests/deps/x11-1.0/xvlib.nim @@ -155,7 +155,7 @@ type vert_y_period*: cuint vert_u_period*: cuint vert_v_period*: cuint - component_order*: array[0..31, char] # eg. UYVY + component_order*: array[0..31, char] # e.g. UYVY scanline_order*: cint # XvTopToBottom, XvBottomToTop PXvImage* = ptr TXvImage diff --git a/tests/misc/trunner.nim b/tests/misc/trunner.nim index d67547d62..018916e67 100644 --- a/tests/misc/trunner.nim +++ b/tests/misc/trunner.nim @@ -149,7 +149,7 @@ mmain.html doAssert exitCode == 0, output block: let (output, exitCode) = runCmd(file, "-d:checkAbi -d:caseBad") - # on platforms that support _StaticAssert natively, errors will show full context, eg: + # on platforms that support _StaticAssert natively, errors will show full context, e.g.: # error: static_assert failed due to requirement 'sizeof(unsigned char) == 8' # "backend & Nim disagree on size for: BadImportcType{int64} [declared in mabi_check.nim(1, 6)]" check2 "sizeof(unsigned char) == 8" diff --git a/tests/stdlib/tjsonutils.nim b/tests/stdlib/tjsonutils.nim index fefd412e7..28f05ecbe 100644 --- a/tests/stdlib/tjsonutils.nim +++ b/tests/stdlib/tjsonutils.nim @@ -57,7 +57,7 @@ template fn() = """[1.1,"fo",120,[10,11],[true,false],[{"mode":"modeCaseSensitive","table":{"y":"Y","z":"Z"}},{"mode":"modeCaseSensitive","table":{}}],[0,3],-4,{"foo":0.5,"bar":{"a1":"abc"},"bar2":null}]""" block: - # edge case when user defined `==` doesn't handle `nil` well, eg: + # edge case when user defined `==` doesn't handle `nil` well, e.g.: # https://github.com/nim-lang/nimble/blob/63695f490728e3935692c29f3d71944d83bb1e83/src/nimblepkg/version.nim#L105 testRoundtrip(@[Foo(id: 10), nil]): """[{"id":10},null]""" diff --git a/tools/ci_generate.nim b/tools/ci_generate.nim index bfed8d88f..dba10d74a 100644 --- a/tools/ci_generate.nim +++ b/tools/ci_generate.nim @@ -53,7 +53,7 @@ triggers: proc main()= let num = 3 # if you reduce this, make sure to remove files that shouldn't be generated, - # or better, do the cleanup logic here eg: `rm .builds/openbsd_*` + # or better, do the cleanup logic here e.g.: `rm .builds/openbsd_*` for i in 0.. Date: Wed, 11 Nov 2020 10:29:17 +0800 Subject: [PATCH 0950/3768] close #4318(add testcase for #4318) (#15904) * close #4318(add testcase for #4318) * Update tests/objects/t4318.nim Co-authored-by: Juan Carlos Co-authored-by: Juan Carlos --- tests/objects/t4318.nim | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 tests/objects/t4318.nim diff --git a/tests/objects/t4318.nim b/tests/objects/t4318.nim new file mode 100644 index 000000000..34ff722f5 --- /dev/null +++ b/tests/objects/t4318.nim @@ -0,0 +1,12 @@ +type + A = object of RootObj + B = object of A + +method identify(a:A) {.base.} = echo "A" +method identify(b:B) = echo "B" + +var b: B + +doAssertRaises(ObjectAssignmentDefect): + var a: A = b + discard a From 42c434fa3d48356bda8ba0b85cd940385f42edfb Mon Sep 17 00:00:00 2001 From: ee7 <45465154+ee7@users.noreply.github.com> Date: Wed, 11 Nov 2020 03:37:14 +0100 Subject: [PATCH 0951/3768] Docs(strutils): Fix broken links (#15912) The `dedent` and `unindent` links were introduced by commit d67c5cb75171 (#15264). --- lib/pure/strutils.nim | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 19a37425e..0eff80863 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -1443,7 +1443,7 @@ proc indent*(s: string, count: Natural, padding: string = " "): string ## * `alignLeft proc<#alignLeft,string,Natural,char>`_ ## * `spaces proc<#spaces,Natural>`_ ## * `unindent proc<#unindent,string,Natural,string>`_ - ## * `dedent proc<#dedent,string,Natural,string>`_ + ## * `dedent proc<#dedent,string,Natural>`_ runnableExamples: doAssert indent("First line\c\l and second line.", 2) == " First line\l and second line." @@ -1464,7 +1464,7 @@ proc unindent*(s: string, count: Natural = int.high, padding: string = " "): str ## **Note:** This does not preserve the new line characters used in ``s``. ## ## See also: - ## * `dedent proc<#dedent,string,Natural,string>` + ## * `dedent proc<#dedent,string,Natural>`_ ## * `align proc<#align,string,Natural,char>`_ ## * `alignLeft proc<#alignLeft,string,Natural,char>`_ ## * `spaces proc<#spaces,Natural>`_ @@ -1506,14 +1506,15 @@ proc indentation*(s: string): Natural {.since: (1, 3).} = proc dedent*(s: string, count: Natural = indentation(s)): string {.noSideEffect, rtl, extern: "nsuDedent", since: (1, 3).} = ## Unindents each line in ``s`` by ``count`` amount of ``padding``. - ## The only difference between this and `unindent proc<#unindent,string,Natural,string>` - ## is that this by default only cuts off the amount of indentation that all - ## lines of ``s`` share as opposed to all indentation. It only supports spcaes as padding. + ## The only difference between this and the + ## `unindent proc<#unindent,string,Natural,string>`_ is that this by default + ## only cuts off the amount of indentation that all lines of ``s`` share as + ## opposed to all indentation. It only supports spaces as padding. ## ## **Note:** This does not preserve the new line characters used in ``s``. ## ## See also: - ## * `unindent proc<#unindent,string,Natural,string>` + ## * `unindent proc<#unindent,string,Natural,string>`_ ## * `align proc<#align,string,Natural,char>`_ ## * `alignLeft proc<#alignLeft,string,Natural,char>`_ ## * `spaces proc<#spaces,Natural>`_ @@ -1905,7 +1906,7 @@ proc find*(s: string, sub: char, start: Natural = 0, last = 0): int {.noSideEffe ## Use `s[start..last].rfind` for a ``start``-origin index. ## ## See also: - ## * `rfind proc<#rfind,string,char,Natural,int>`_ + ## * `rfind proc<#rfind,string,char,Natural>`_ ## * `replace proc<#replace,string,char,char>`_ let last = if last == 0: s.high else: last when nimvm: @@ -1933,7 +1934,7 @@ proc find*(s: string, chars: set[char], start: Natural = 0, last = 0): int {.noS ## Use `s[start..last].find` for a ``start``-origin index. ## ## See also: - ## * `rfind proc<#rfind,string,set[char],Natural,int>`_ + ## * `rfind proc<#rfind,string,set[char],Natural>`_ ## * `multiReplace proc<#multiReplace,string,varargs[]>`_ let last = if last == 0: s.high else: last for i in int(start)..last: @@ -1950,7 +1951,7 @@ proc find*(s, sub: string, start: Natural = 0, last = 0): int {.noSideEffect, ## Use `s[start..last].find` for a ``start``-origin index. ## ## See also: - ## * `rfind proc<#rfind,string,string,Natural,int>`_ + ## * `rfind proc<#rfind,string,string,Natural>`_ ## * `replace proc<#replace,string,string,string>`_ if sub.len > s.len: return -1 if sub.len == 1: return find(s, sub[0], start, last) From b831033db6c0bb7b11128383e8f186912f620fd1 Mon Sep 17 00:00:00 2001 From: narimiran Date: Wed, 11 Nov 2020 14:02:35 +0100 Subject: [PATCH 0952/3768] remove `codeowners` [ci skip] --- doc/codeowners.rst | 65 -------------------------------------------- doc/contributing.rst | 5 +--- 2 files changed, 1 insertion(+), 69 deletions(-) delete mode 100644 doc/codeowners.rst diff --git a/doc/codeowners.rst b/doc/codeowners.rst deleted file mode 100644 index 4b4a5810e..000000000 --- a/doc/codeowners.rst +++ /dev/null @@ -1,65 +0,0 @@ -=========== -Code owners -=========== - - -Subsystems and code owners --------------------------- - -*Note*: This list is incomplete, in doubt dom96 is responsible for the standard -library, araq is responsible for the compiler. - - -Compiler -~~~~~~~~ - -=========================== ====================================================== -subsystem owner(s) -=========================== ====================================================== -Parsing, Lexing araq -Renderer cooldome, araq -Order of passes cooldome -Semantic Checking araq -Virtual machine jangko, GULPF, araq -Sempass2: effects tracking cooldome, araq -type system, concepts zahary -transf cooldome, araq -semfold constant folding araq -template instantiation zahary, araq -term rewriting macros cooldome, araq -closure iterators yglukhov, araq -lambda lifting yglukhov, araq -c, cpp codegen lemonboy, araq -js codegen yglukhov, lemonboy -alias analysis araq -dfa, writetracking araq -parallel, multithreading araq -incremental araq -sizeof computations krux02 -Exception handling cooldome, araq -=========================== ====================================================== - - - -Standard library -~~~~~~~~~~~~~~~~ - -====================== ====================================================== -subsystem owner(s) -====================== ====================================================== -async dom96 -strutils araq -sequtils dom96, araq -times GULPF -os dom96, araq -re araq -nre flaviu -math, fenv krux02, cooldome -io dom96 -garbage collector araq -Go garbage collector stefantalpalaru -coroutines Rokas Kupstys -collections GULPF -parseopt araq -json dom96 -====================== ====================================================== diff --git a/doc/contributing.rst b/doc/contributing.rst index f570336e2..6ddbd65bc 100644 --- a/doc/contributing.rst +++ b/doc/contributing.rst @@ -8,11 +8,8 @@ Contributing Contributing happens via "Pull requests" (PR) on github. Every PR needs to be reviewed before it can be merged and the Continuous Integration should be green. -The PR has to be approved (and is often merged too) by one "code owner", either -by the code owner who is responsible for the subsystem the PR belongs to or by -two core developers or by Araq. +The PR has to be approved by two core developers or by Araq. -See `codeowners `_ for more details. Writing tests From f02c7542fcd21d4b3d7b82908325512be4e5404a Mon Sep 17 00:00:00 2001 From: narimiran Date: Wed, 11 Nov 2020 15:59:41 +0100 Subject: [PATCH 0953/3768] nimdoc: items of ordered lists now have numbers instead of circles --- doc/nimdoc.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/nimdoc.css b/doc/nimdoc.css index 457f024b5..2d9533ceb 100644 --- a/doc/nimdoc.css +++ b/doc/nimdoc.css @@ -410,7 +410,7 @@ ol ul { margin-bottom: 0; margin-left: 1.25em; } -li { +ul.simple > li { list-style-type: circle; } From a81434a890d70e7146c440c59caadc701c6099b3 Mon Sep 17 00:00:00 2001 From: Bung Date: Thu, 12 Nov 2020 01:41:49 +0800 Subject: [PATCH 0954/3768] Fix 14127 js from int to int casting (#15918) * fix #14127 from int to int casting * add test for #14127 * use template for test, also test uint2int * move to tests/types/t14127_cast_number.nim targets:c cpp js --- compiler/jsgen.nim | 3 +-- tests/types/t14127_cast_number.nim | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) create mode 100644 tests/types/t14127_cast_number.nim diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index d124ae5fe..87021de11 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -2406,8 +2406,7 @@ proc genCast(p: PProc, n: PNode, r: var TCompRes) = r.res = "($1 $2)" % [r.res, trimmer] elif toInt: if fromInt: - let trimmer = unsignedTrimmer(dest.size) - r.res = "($1 $2)" % [r.res, trimmer] + return elif fromUint: if src.size == 4 and dest.size == 4: # XXX prevent multi evaluations diff --git a/tests/types/t14127_cast_number.nim b/tests/types/t14127_cast_number.nim new file mode 100644 index 000000000..4bb23f22f --- /dev/null +++ b/tests/types/t14127_cast_number.nim @@ -0,0 +1,30 @@ +discard """ + targets: "c cpp js" +""" +block: # bug #14127 + template int2uint(T) = + var a = -1 + let b = cast[T](a) + doAssert b < 0 + let c = b + 1 + doAssert c is T + doAssert c == 0 + + int2uint(int8) + int2uint(int16) + int2uint(int32) + int2uint(int64) + +block: # maybe related + template uint2int(T) = + var a = 3 + let b = cast[T](a) + doAssert b > 0 + let c = b - 1 + doAssert c is T + doAssert c == 2 + + uint2int(uint8) + uint2int(uint16) + uint2int(uint32) + uint2int(uint64) From 826e62953faa1f6cd9bac582246186935af4b8e6 Mon Sep 17 00:00:00 2001 From: Bung Date: Thu, 12 Nov 2020 16:16:40 +0800 Subject: [PATCH 0955/3768] fix #12726 Cannot take the compile-time sizeof Atomic types (#15928) * fix #12726 Cannot take the compile-time sizeof Atomic types * fix for arch 32 --- lib/pure/concurrency/atomics.nim | 15 ++++++++------- tests/stdlib/concurrency/tatomics_size.nim | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+), 7 deletions(-) create mode 100644 tests/stdlib/concurrency/tatomics_size.nim diff --git a/lib/pure/concurrency/atomics.nim b/lib/pure/concurrency/atomics.nim index eaa811aa6..dc575c6c0 100644 --- a/lib/pure/concurrency/atomics.nim +++ b/lib/pure/concurrency/atomics.nim @@ -51,10 +51,11 @@ when defined(cpp) or defined(nimdoc): ## with other moSequentiallyConsistent operations. type - Atomic* {.importcpp: "std::atomic".} [T] = object + Atomic*[T] {.importcpp: "std::atomic", completeStruct.} = object ## An atomic object with underlying type `T`. + raw: T - AtomicFlag* {.importcpp: "std::atomic_flag".} = object + AtomicFlag* {.importcpp: "std::atomic_flag", size: 1.} = object ## An atomic boolean state. # Access operations @@ -249,10 +250,10 @@ else: type # Atomic* {.importcpp: "_Atomic('0)".} [T] = object - AtomicInt8 {.importc: "_Atomic NI8".} = object - AtomicInt16 {.importc: "_Atomic NI16".} = object - AtomicInt32 {.importc: "_Atomic NI32".} = object - AtomicInt64 {.importc: "_Atomic NI64".} = object + AtomicInt8 {.importc: "_Atomic NI8", size: 1.} = object + AtomicInt16 {.importc: "_Atomic NI16", size: 2.} = object + AtomicInt32 {.importc: "_Atomic NI32", size: 4.} = object + AtomicInt64 {.importc: "_Atomic NI64", size: 8.} = object template atomicType*(T: typedesc[Trivial]): untyped = # Maps the size of a trivial type to it's internal atomic type @@ -262,7 +263,7 @@ else: elif sizeof(T) == 8: AtomicInt64 type - AtomicFlag* {.importc: "atomic_flag".} = object + AtomicFlag* {.importc: "atomic_flag", size: 1.} = object Atomic*[T] = object when T is Trivial: diff --git a/tests/stdlib/concurrency/tatomics_size.nim b/tests/stdlib/concurrency/tatomics_size.nim new file mode 100644 index 000000000..49387c0c1 --- /dev/null +++ b/tests/stdlib/concurrency/tatomics_size.nim @@ -0,0 +1,18 @@ +discard """ + targets: "c cpp" +""" +import std/atomics + +block testSize: # issue 12726 + type + Node = ptr object + # works + next: Atomic[pointer] + f:AtomicFlag + MyChannel = object + # type not defined completely + back: Atomic[ptr int] + f: AtomicFlag + static: + doAssert sizeof(Node) == sizeof(pointer) + doAssert sizeof(MyChannel) == sizeof(pointer) * 2 \ No newline at end of file From 1f9bf43100f7236d8ccbcaa14c43bc18f7e6e5d8 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Thu, 12 Nov 2020 16:20:10 +0800 Subject: [PATCH 0956/3768] fix #14157 (#15877) * fix #14157 * Update compiler/jsgen.nim * add changelog * Update compiler/jsgen.nim * Update tests/js/tmodify_cstring.nim Co-authored-by: Andreas Rumpf --- changelog.md | 2 ++ compiler/jsgen.nim | 4 ++++ tests/js/tmodify_cstring.nim | 6 ++++++ 3 files changed, 12 insertions(+) create mode 100644 tests/js/tmodify_cstring.nim diff --git a/changelog.md b/changelog.md index bc8ad2295..3bdf8a511 100644 --- a/changelog.md +++ b/changelog.md @@ -35,6 +35,8 @@ ## Language changes +- The `cstring` doesn't support `[]=` operator in JS backend. + ## Compiler changes diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 87021de11..357e18000 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -1049,6 +1049,10 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = var a, b: TCompRes var xtyp = mapType(p, x.typ) + # disable `[]=` for cstring + if x.kind == nkBracketExpr and x.len >= 2 and x[0].typ.skipTypes(abstractInst).kind == tyCString: + localError(p.config, x.info, "cstring doesn't support `[]=` operator") + gen(p, x, a) genLineDir(p, y) gen(p, y, b) diff --git a/tests/js/tmodify_cstring.nim b/tests/js/tmodify_cstring.nim new file mode 100644 index 000000000..82f8ccb23 --- /dev/null +++ b/tests/js/tmodify_cstring.nim @@ -0,0 +1,6 @@ +discard """ + errormsg: "cstring doesn't support `[]=` operator" +""" + +var x = cstring"abcd" +x[0] = 'x' From e5db5316c2101a27efd744ba10126fdb57a7eb7c Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 12 Nov 2020 02:25:41 -0600 Subject: [PATCH 0957/3768] doAssertRaises improvements; nimscript supports `except Exception as e` (#15765) * doAssertRaises now correctly handles foreign exceptions; now shows which exception is raised on mismatch * nimscript now handles `Exception as e` * remove catch-all doAssertRaises overload from this PR Co-authored-by: Andreas Rumpf --- changelog.md | 3 ++- lib/system.nim | 3 ++- lib/system/assertions.nim | 19 ++++++++++--------- tests/test_nimscript.nims | 6 ++++++ 4 files changed, 20 insertions(+), 11 deletions(-) diff --git a/changelog.md b/changelog.md index 3bdf8a511..8b427bfc5 100644 --- a/changelog.md +++ b/changelog.md @@ -31,10 +31,11 @@ - Removed deprecated `iup` module from stdlib, it has already moved to [nimble](https://github.com/nim-lang/iup). - +- `doAssertRaises` now correctly handles foreign exceptions. ## Language changes +- `nimscript` now handles `except Exception as e` - The `cstring` doesn't support `[]=` operator in JS backend. diff --git a/lib/system.nim b/lib/system.nim index e41dfe1ca..fd5eb2dac 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2360,7 +2360,8 @@ when notJSnotNims and hostOS != "standalone": ## ## **Warning**: Only use this if you know what you are doing. currException = exc - +elif defined(nimscript): + proc getCurrentException*(): ref Exception {.compilerRtl.} = discard when notJSnotNims: {.push stackTrace: off, profiler: off.} diff --git a/lib/system/assertions.nim b/lib/system/assertions.nim index c6283c89c..1a4fda123 100644 --- a/lib/system/assertions.nim +++ b/lib/system/assertions.nim @@ -81,20 +81,23 @@ template onFailedAssert*(msg, code: untyped): untyped {.dirty.} = code template doAssertRaises*(exception: typedesc, code: untyped) = - ## Raises ``AssertionDefect`` if specified ``code`` does not raise the - ## specified exception. Example: + ## Raises ``AssertionDefect`` if specified ``code`` does not raise `exception`. + ## Example: ## ## .. code-block:: nim ## doAssertRaises(ValueError): ## raise newException(ValueError, "Hello World") var wrong = false + const begin = "expected raising '" & astToStr(exception) & "', instead" + const msgEnd = " by: " & astToStr(code) + template raisedForeign = raiseAssert(begin & " raised foreign exception" & msgEnd) when Exception is exception: try: if true: code wrong = true - except Exception: - discard + except Exception as e: discard + except: raisedForeign() else: try: if true: @@ -102,9 +105,7 @@ template doAssertRaises*(exception: typedesc, code: untyped) = wrong = true except exception: discard - except Exception: - raiseAssert(astToStr(exception) & - " wasn't raised, another error was raised instead by:\n"& - astToStr(code)) + except Exception as e: raiseAssert(begin & " raised '" & $e.name & "'" & msgEnd) + except: raisedForeign() if wrong: - raiseAssert(astToStr(exception) & " wasn't raised by:\n" & astToStr(code)) + raiseAssert(begin & " nothing was raised" & msgEnd) diff --git a/tests/test_nimscript.nims b/tests/test_nimscript.nims index b94146b1e..ea640cac6 100644 --- a/tests/test_nimscript.nims +++ b/tests/test_nimscript.nims @@ -81,3 +81,9 @@ block: # #14142 discard dirExists("/usr") discard fileExists("/usr/foo") discard findExe("nim") + +block: + doAssertRaises(AssertionDefect): doAssert false + try: doAssert false + except Exception as e: + discard From 7d4f70280ef33192dc5862f8b4c4d23e9d956b40 Mon Sep 17 00:00:00 2001 From: Fanael Linithien Date: Thu, 12 Nov 2020 09:28:32 +0100 Subject: [PATCH 0958/3768] Fix #15909 (#15914) --- compiler/varpartitions.nim | 1 + tests/arc/t15909.nim | 16 ++++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 tests/arc/t15909.nim diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim index 2e781c041..63c18d94a 100644 --- a/compiler/varpartitions.nim +++ b/compiler/varpartitions.nim @@ -399,6 +399,7 @@ proc allRoots(n: PNode; result: var seq[PSym]; followDotExpr = true) = proc destMightOwn(c: var Partitions; dest: var VarIndex; n: PNode) = ## Analyse if 'n' is an expression that owns the data, if so mark 'dest' ## with 'ownsData'. + if n.typ == nil: return case n.kind of nkEmpty, nkCharLit..nkNilLit: # primitive literals including the empty are harmless: diff --git a/tests/arc/t15909.nim b/tests/arc/t15909.nim new file mode 100644 index 000000000..f25c89daf --- /dev/null +++ b/tests/arc/t15909.nim @@ -0,0 +1,16 @@ +discard """ + action: run + cmd: "nim c --gc:arc $file" +""" + +proc f1() {.noreturn.} = raise newException(CatchableError, "") + +proc f2(y: int): int = + if y != 0: + y + else: + f1() + +doAssert f2(5) == 5 +doAssertRaises(CatchableError): + discard f2(0) From 402df0b06139183fc8b982058774807a13534daf Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Thu, 12 Nov 2020 16:31:35 +0800 Subject: [PATCH 0959/3768] fix #15916 (#15917) [backport] * fix #15916 * add testcase for #15916 * add comments --- compiler/docgen.nim | 6 ++++-- tests/nimdoc/t15916.nim | 16 ++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) create mode 100644 tests/nimdoc/t15916.nim diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 8f6b7a560..57fdad12a 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -592,8 +592,10 @@ proc getRoutineBody(n: PNode): PNode = (0 or more) doc comments and runnableExamples. ]## result = n[bodyPos] - if result.kind == nkAsgn and n.len > bodyPos+1 and n[bodyPos+1].kind == nkSym: - doAssert result[0].kind == nkSym + + # This won't be transformed: result.id = 10. Namely result[0].kind != nkSym. + if result.kind == nkAsgn and result[0].kind == nkSym and + n.len > bodyPos+1 and n[bodyPos+1].kind == nkSym: doAssert result.len == 2 result = result[1] diff --git a/tests/nimdoc/t15916.nim b/tests/nimdoc/t15916.nim new file mode 100644 index 000000000..c6c09d94b --- /dev/null +++ b/tests/nimdoc/t15916.nim @@ -0,0 +1,16 @@ +discard """ +cmd: "nim doc --hints:off $file" +action: "compile" +joinable: false +""" + +type + Test* = object + id: int + +proc initTest*(id: int): Test = + result.id = id + +proc hello*() = + runnableExamples: + discard From c6ceefea6de2a0159f33722839bfe9a0ce9134e1 Mon Sep 17 00:00:00 2001 From: Christopher Dunn Date: Thu, 12 Nov 2020 02:32:38 -0600 Subject: [PATCH 0960/3768] Fix a problem for long symlinks in conda (#15908) [backport] * https://github.com/conda-forge/nim-feedstock/commit/d8c80ea69dca30d99073d33845ddd79f9892755b#diff-e1d8e552330911f9f779f85b6f2c00a15e790dcc3fbb3b28f5da1d660a30c5b8 --- lib/pure/os.nim | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 2601f9d47..234d82bc2 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -1172,6 +1172,7 @@ when not defined(windows) and not weirdTarget: else: result = S_ISLNK(rawInfo.st_mode) const + maxSymlinkLen = 1024 ExeExts* = ## Platform specific file extension for executables. ## On Windows ``["exe", "cmd", "bat"]``, on Posix ``[""]``. when defined(windows): ["exe", "cmd", "bat"] else: [""] @@ -1213,11 +1214,11 @@ proc findExe*(exe: string, followSymlinks: bool = true; when not defined(windows): while followSymlinks: # doubles as if here if x.checkSymlink: - var r = newString(256) - var len = readlink(x, r, 256) + var r = newString(maxSymlinkLen) + var len = readlink(x, r, maxSymlinkLen) if len < 0: raiseOSError(osLastError(), exe) - if len > 256: + if len > maxSymlinkLen: r = newString(len+1) len = readlink(x, r, len) setLen(r, len) @@ -2561,11 +2562,11 @@ proc expandSymlink*(symlinkPath: string): string {.noWeirdTarget.} = when defined(windows): result = symlinkPath else: - result = newString(256) - var len = readlink(symlinkPath, result, 256) + result = newString(maxSymlinkLen) + var len = readlink(symlinkPath, result, maxSymlinkLen) if len < 0: raiseOSError(osLastError(), symlinkPath) - if len > 256: + if len > maxSymlinkLen: result = newString(len+1) len = readlink(symlinkPath, result, len) setLen(result, len) @@ -2863,9 +2864,9 @@ when not weirdTarget and (defined(freebsd) or defined(dragonfly) or defined(netb when not weirdTarget and (defined(linux) or defined(solaris) or defined(bsd) or defined(aix)): proc getApplAux(procPath: string): string = - result = newString(256) - var len = readlink(procPath, result, 256) - if len > 256: + result = newString(maxSymlinkLen) + var len = readlink(procPath, result, maxSymlinkLen) + if len > maxSymlinkLen: result = newString(len+1) len = readlink(procPath, result, len) setLen(result, len) From 63c559e781cafaab6bcc9e62d5631e93749450bb Mon Sep 17 00:00:00 2001 From: lenoil98 Date: Thu, 12 Nov 2020 04:43:13 -0500 Subject: [PATCH 0961/3768] Add support for FreeBSD/PowerPC64 Little Endian (#15927) * Update installer.ini Add support for powerpc64 little endian * Update buildsh.nimf Add support for powerpc64 little endian * Update makefile.nimf Add support for freebsd/powerpc64 little endian --- compiler/installer.ini | 2 +- tools/niminst/buildsh.nimf | 5 ++++- tools/niminst/makefile.nimf | 3 +++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/compiler/installer.ini b/compiler/installer.ini index 01b7b8c6b..f0756ab3e 100644 --- a/compiler/installer.ini +++ b/compiler/installer.ini @@ -9,7 +9,7 @@ Platforms: """ linux: i386;hppa;ia64;alpha;amd64;powerpc64;arm;sparc;sparc64;m68k;mips;mipsel;mips64;mips64el;powerpc;powerpc64el;arm64;riscv64 macosx: i386;amd64;powerpc64 solaris: i386;amd64;sparc;sparc64 - freebsd: i386;amd64;powerpc64;arm;arm64;riscv64;sparc64;mips;mipsel;mips64;mips64el;powerpc + freebsd: i386;amd64;powerpc64;arm;arm64;riscv64;sparc64;mips;mipsel;mips64;mips64el;powerpc;powerpc64el netbsd: i386;amd64 openbsd: i386;amd64;arm;arm64 dragonfly: i386;amd64 diff --git a/tools/niminst/buildsh.nimf b/tools/niminst/buildsh.nimf index 5d532a0fd..5ec5d5c2c 100644 --- a/tools/niminst/buildsh.nimf +++ b/tools/niminst/buildsh.nimf @@ -159,7 +159,10 @@ case $ucpu in COMP_FLAGS="$COMP_FLAGS -m64" LINK_FLAGS="$LINK_FLAGS -m64" mycpu=`uname -p` - else + if [ "$(mycpu)" = "powerpc64le"] ; then + mycpu="powerpc64el" + fi + else mycpu="powerpc" fi ;; diff --git a/tools/niminst/makefile.nimf b/tools/niminst/makefile.nimf index ad9d55c03..aff0c5da0 100644 --- a/tools/niminst/makefile.nimf +++ b/tools/niminst/makefile.nimf @@ -124,6 +124,9 @@ ifeq ($(ucpu),powerpc) mycpu = $(shell sh -c 'uname -p | tr "[:upper:]" "[:lower:]"') CFLAGS += -m64 LDFLAGS += -m64 + ifeq ($(mycpu),powerpc64le) + mycpu = powerpc64el + endif endif endif ifeq ($(ucpu),ppc) From ef2677f5f3f58ad5bd514f82a0efd68d4d17e7f9 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Thu, 12 Nov 2020 17:47:56 +0800 Subject: [PATCH 0962/3768] change some code-blocks to runnableExamples and correct some errors in docs (#15900) * original docs are wrong * fix pathnorm * Update lib/pure/pathnorm.nim Co-authored-by: Juan Carlos --- lib/pure/httpcore.nim | 7 ++---- lib/pure/json.nim | 57 +++++++++++++++++++++---------------------- lib/pure/math.nim | 30 +++++++++++------------ lib/pure/pathnorm.nim | 14 +++++------ 4 files changed, 51 insertions(+), 57 deletions(-) diff --git a/lib/pure/httpcore.nim b/lib/pure/httpcore.nim index b3d1da8d6..401e78b9b 100644 --- a/lib/pure/httpcore.nim +++ b/lib/pure/httpcore.nim @@ -262,11 +262,8 @@ func contains*(methods: set[HttpMethod], x: string): bool = func `$`*(code: HttpCode): string = ## Converts the specified ``HttpCode`` into a HTTP status. - ## - ## For example: - ## - ## .. code-block:: nim - ## doAssert($Http404 == "404 Not Found") + runnableExamples: + doAssert($Http404 == "404 Not Found") case code.int of 100: "100 Continue" of 101: "101 Switching Protocols" diff --git a/lib/pure/json.nim b/lib/pure/json.nim index a3f7a782e..063fad8b4 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -519,9 +519,10 @@ proc `{}`*(node: JsonNode, keys: varargs[string]): JsonNode = ## This proc can be used to create tree structures on the ## fly (sometimes called `autovivification`:idx:): ## - ## .. code-block:: nim - ## myjson{"parent", "child", "grandchild"} = newJInt(1) - ## + runnableExamples: + var myjson = %* {"parent": {"child": {"grandchild": 1}}} + doAssert myjson{"parent", "child", "grandchild"} == newJInt(1) + result = node for key in keys: if isNil(result) or result.kind != JObject: @@ -1258,32 +1259,30 @@ when defined(nimFixedForwardGeneric): ## * Sets in object variants are not supported. ## * Not nil annotations are not supported. ## - ## Example: - ## - ## .. code-block:: Nim - ## let jsonNode = parseJson(""" - ## { - ## "person": { - ## "name": "Nimmer", - ## "age": 21 - ## }, - ## "list": [1, 2, 3, 4] - ## } - ## """) - ## - ## type - ## Person = object - ## name: string - ## age: int - ## - ## Data = object - ## person: Person - ## list: seq[int] - ## - ## var data = to(jsonNode, Data) - ## doAssert data.person.name == "Nimmer" - ## doAssert data.person.age == 21 - ## doAssert data.list == @[1, 2, 3, 4] + runnableExamples: + let jsonNode = parseJson(""" + { + "person": { + "name": "Nimmer", + "age": 21 + }, + "list": [1, 2, 3, 4] + } + """) + + type + Person = object + name: string + age: int + + Data = object + person: Person + list: seq[int] + + var data = to(jsonNode, Data) + doAssert data.person.name == "Nimmer" + doAssert data.person.age == 21 + doAssert data.list == @[1, 2, 3, 4] var jsonPath = "" initFromJson(result, node, jsonPath) diff --git a/lib/pure/math.nim b/lib/pure/math.nim index 2a2660524..59368fbd2 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -862,10 +862,10 @@ when not defined(js): ## float value) equals ``m * 2**n``. frexp stores n in `exponent` and returns ## m. ## - ## .. code-block:: nim - ## var x: int - ## echo frexp(5.0, x) # 0.625 - ## echo x # 3 + runnableExamples: + var x: int + doAssert frexp(5.0, x) == 0.625 + doAssert x == 3 var exp: int32 result = c_frexp(x, exp) exponent = exp @@ -929,9 +929,9 @@ proc splitDecimal*[T: float32|float64](x: T): tuple[intpart: T, floatpart: T] = ## Both parts have the same sign as ``x``. Analogous to the ``modf`` ## function in C. ## - ## .. code-block:: nim - ## echo splitDecimal(5.25) # (intpart: 5.0, floatpart: 0.25) - ## echo splitDecimal(-2.73) # (intpart: -2.0, floatpart: -0.73) + runnableExamples: + doAssert splitDecimal(5.25) == (intpart: 5.0, floatpart: 0.25) + doAssert splitDecimal(-2.73) == (intpart: -2.0, floatpart: -0.73) var absolute: T absolute = abs(x) @@ -949,8 +949,8 @@ proc degToRad*[T: float32|float64](d: T): T {.inline.} = ## See also: ## * `radToDeg proc <#radToDeg,T>`_ ## - ## .. code-block:: nim - ## echo degToRad(180.0) # 3.141592653589793 + runnableExamples: + doAssert degToRad(180.0) == 3.141592653589793 result = T(d) * RadPerDeg proc radToDeg*[T: float32|float64](d: T): T {.inline.} = @@ -959,8 +959,8 @@ proc radToDeg*[T: float32|float64](d: T): T {.inline.} = ## See also: ## * `degToRad proc <#degToRad,T>`_ ## - ## .. code-block:: nim - ## echo degToRad(2 * PI) # 360.0 + runnableExamples: + doAssert radToDeg(2 * PI) == 360.0 result = T(d) / RadPerDeg proc sgn*[T: SomeNumber](x: T): int {.inline.} = @@ -971,10 +971,10 @@ proc sgn*[T: SomeNumber](x: T): int {.inline.} = ## * `1` for positive numbers and ``Inf``, ## * `0` for positive zero, negative zero and ``NaN`` ## - ## .. code-block:: nim - ## echo sgn(5) # 1 - ## echo sgn(0) # 0 - ## echo sgn(-4.1) # -1 + runnableExamples: + doAssert sgn(5) == 1 + doAssert sgn(0) == 0 + doAssert sgn(-4.1) == -1 ord(T(0) < x) - ord(x < T(0)) {.pop.} diff --git a/lib/pure/pathnorm.nim b/lib/pure/pathnorm.nim index 03a65bd60..5bc66f0b7 100644 --- a/lib/pure/pathnorm.nim +++ b/lib/pure/pathnorm.nim @@ -100,15 +100,13 @@ proc addNormalizePath*(x: string; result: var string; state: var int; if result == "" and x != "": result = "." proc normalizePath*(path: string; dirSep = DirSep): string = - ## Example: - ## - ## .. code-block:: nim - ## assert normalizePath("./foo//bar/../baz") == "foo/baz" - ## - ## + runnableExamples: + when defined(posix): + doAssert normalizePath("./foo//bar/../baz") == "foo/baz" + ## - Turns multiple slashes into single slashes. - ## - Resolves '/foo/../bar' to '/bar'. - ## - Removes './' from the path (but "foo/.." becomes ".") + ## - Resolves `'/foo/../bar'` to `'/bar'`. + ## - Removes `'./'` from the path, but `"foo/.."` becomes `"."`. result = newStringOfCap(path.len) var state = 0 addNormalizePath(path, result, state, dirSep) From 527e792827497002ded742ad815c567a4a056471 Mon Sep 17 00:00:00 2001 From: hlaaftana <10591326+hlaaftana@users.noreply.github.com> Date: Thu, 12 Nov 2020 13:44:21 +0300 Subject: [PATCH 0963/3768] make var string return var char w/ BackwardsIndex (#15461) * make var string return var char w/ BackwardsIndex fixes #14497 * work around VM bug * properly workaround again --- lib/pure/pathnorm.nim | 6 +++--- lib/system.nim | 1 + tests/stdlib/tstring.nim | 7 +++++++ 3 files changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/pure/pathnorm.nim b/lib/pure/pathnorm.nim index 5bc66f0b7..7834f8d95 100644 --- a/lib/pure/pathnorm.nim +++ b/lib/pure/pathnorm.nim @@ -69,7 +69,7 @@ proc addNormalizePath*(x: string; result: var string; state: var int; while hasNext(it, x): let b = next(it, x) if (state shr 1 == 0) and isSlash(x, b): - if result.len == 0 or result[^1] notin {DirSep, AltSep}: + if result.len == 0 or result[result.len - 1] notin {DirSep, AltSep}: result.add dirSep state = state or 1 elif isDotDot(x, b): @@ -87,13 +87,13 @@ proc addNormalizePath*(x: string; result: var string; state: var int; setLen(result, d-1) dec state, 2 else: - if result.len > 0 and result[^1] notin {DirSep, AltSep}: + if result.len > 0 and result[result.len - 1] notin {DirSep, AltSep}: result.add dirSep result.add substr(x, b[0], b[1]) elif isDot(x, b): discard "discard the dot" elif b[1] >= b[0]: - if result.len > 0 and result[^1] notin {DirSep, AltSep}: + if result.len > 0 and result[result.len - 1] notin {DirSep, AltSep}: result.add dirSep result.add substr(x, b[0], b[1]) inc state, 2 diff --git a/lib/system.nim b/lib/system.nim index fd5eb2dac..1104cb17f 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2579,6 +2579,7 @@ proc `[]`*[T](s: var openArray[T]; i: BackwardsIndex): var T {.inline.} = system.`[]`(s, s.len - int(i)) proc `[]`*[Idx, T](a: var array[Idx, T]; i: BackwardsIndex): var T {.inline.} = a[Idx(a.len - int(i) + int low(a))] +proc `[]`*(s: var string; i: BackwardsIndex): var char {.inline.} = s[s.len - int(i)] proc `[]=`*[T](s: var openArray[T]; i: BackwardsIndex; x: T) {.inline.} = system.`[]=`(s, s.len - int(i), x) diff --git a/tests/stdlib/tstring.nim b/tests/stdlib/tstring.nim index 0a7bf0511..ff3d41b49 100644 --- a/tests/stdlib/tstring.nim +++ b/tests/stdlib/tstring.nim @@ -91,3 +91,10 @@ proc tester[T](x: T) = tester(1) +# #14497 +func reverse*(a: string): string = + result = a + for i in 0 ..< a.len div 2: + swap(result[i], result[^(i + 1)]) + +doAssert reverse("hello") == "olleh" From bc007a3cd32890d30501c7c0921681281be4ebb9 Mon Sep 17 00:00:00 2001 From: Bung Date: Thu, 12 Nov 2020 19:36:42 +0800 Subject: [PATCH 0964/3768] add parent property to window in dom.nim (#15922) --- lib/js/dom.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/js/dom.nim b/lib/js/dom.nim index a74c63231..d2e5776f0 100644 --- a/lib/js/dom.nim +++ b/lib/js/dom.nim @@ -154,6 +154,7 @@ type performance*: Performance onpopstate*: proc (event: Event) localStorage*: LocalStorage + parent*: Window Frame* = ref FrameObj FrameObj {.importc.} = object of WindowObj From cc882917feda383a283eb128f80bf49681921995 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 12 Nov 2020 07:36:57 -0600 Subject: [PATCH 0965/3768] js -d:nodejs now supports osenv: `getEnv`, `putEnv`, `envPairs`, `delEnv`, `existsEnv` (v2) (#15826) * js -d:nodejs now supports osenv: `getEnv`, `putEnv`, `envPairs`, `delEnv`, `existsEnv` * refactor to osenv * fix for js (without -d:nodejs) + VM Co-authored-by: Andreas Rumpf --- changelog.md | 2 + lib/pure/includes/osenv.nim | 452 +++++++++++++++++++----------------- tests/js/tos.nim | 26 +++ 3 files changed, 272 insertions(+), 208 deletions(-) diff --git a/changelog.md b/changelog.md index 8b427bfc5..ac1ec1163 100644 --- a/changelog.md +++ b/changelog.md @@ -31,6 +31,8 @@ - Removed deprecated `iup` module from stdlib, it has already moved to [nimble](https://github.com/nim-lang/iup). +- Removed deprecated `iup` module from stdlib, it has already moved to [nimble](https://github.com/nim-lang/iup). +- nodejs now supports osenv: `getEnv`, `putEnv`, `envPairs`, `delEnv`, `existsEnv` - `doAssertRaises` now correctly handles foreign exceptions. ## Language changes diff --git a/lib/pure/includes/osenv.nim b/lib/pure/includes/osenv.nim index 01abf12df..55ace92f2 100644 --- a/lib/pure/includes/osenv.nim +++ b/lib/pure/includes/osenv.nim @@ -3,228 +3,264 @@ when not declared(os) and not declared(ospaths): {.error: "This is an include file for os.nim!".} -when defined(windows): - from parseutils import skipIgnoreCase +when defined(nodejs): + proc getEnv*(key: string, default = ""): TaintedString {.tags: [ReadEnvEffect].} = + var ret: cstring + let key2 = key.cstring + {.emit: "`ret` = process.env[`key2`];".} + result = $ret -proc c_getenv(env: cstring): cstring {. - importc: "getenv", header: "".} -proc c_putenv(env: cstring): cint {. - importc: "putenv", header: "".} -proc c_unsetenv(env: cstring): cint {. - importc: "unsetenv", header: "".} + proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} = + var key2 = key.cstring + var ret: bool + {.emit: "`ret` = `key2` in process.env;".} + result = ret -# Environment handling cannot be put into RTL, because the ``envPairs`` -# iterator depends on ``environment``. + proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} = + var key2 = key.cstring + var val2 = val.cstring + {.emit: "process.env[`key2`] = `val2`;".} -var - envComputed {.threadvar.}: bool - environment {.threadvar.}: seq[string] + proc delEnv*(key: string) {.tags: [WriteEnvEffect].} = + var key2 = key.cstring + {.emit: "delete process.env[`key2`];".} -when defined(nimV2): - proc unpairedEnvAllocs*(): int = - result = environment.len - if result > 0: inc result + iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [ReadEnvEffect].} = + var num: int + var keys: RootObj + {.emit: "`keys` = Object.keys(process.env); `num` = `keys`.length;".} + for i in 0..".} - else: - proc strEnd(cstr: WideCString, c = 0'i32): WideCString {. - importc: "wcschr", header: "".} - else: - proc strEnd(cstr: cstring, c = 0'i32): cstring {. - importc: "strchr", header: "".} - - proc getEnvVarsC() = - if not envComputed: - environment = @[] - when useWinUnicode: - var - env = getEnvironmentStringsW() - e = env - if e == nil: return # an error occurred - while true: - var eend = strEnd(e) - add(environment, $e) - e = cast[WideCString](cast[ByteAddress](eend)+2) - if eend[1].int == 0: break - discard freeEnvironmentStringsW(env) - else: - var - env = getEnvironmentStringsA() - e = env - if e == nil: return # an error occurred - while true: - var eend = strEnd(e) - add(environment, $e) - e = cast[cstring](cast[ByteAddress](eend)+1) - if eend[1] == '\0': break - discard freeEnvironmentStringsA(env) - envComputed = true +# commented because it must keep working with js+VM +# elif defined(js): +# {.error: "requires -d:nodejs".} else: - const - useNSGetEnviron = (defined(macosx) and not defined(ios) and not defined(emscripten)) or defined(nimscript) + when defined(windows): + from parseutils import skipIgnoreCase - when useNSGetEnviron: - # From the manual: - # Shared libraries and bundles don't have direct access to environ, - # which is only available to the loader ld(1) when a complete program - # is being linked. - # The environment routines can still be used, but if direct access to - # environ is needed, the _NSGetEnviron() routine, defined in - # , can be used to retrieve the address of environ - # at runtime. - proc NSGetEnviron(): ptr cstringArray {. - importc: "_NSGetEnviron", header: "".} - elif defined(haiku): - var gEnv {.importc: "environ", header: "".}: cstringArray - else: - var gEnv {.importc: "environ".}: cstringArray + proc c_getenv(env: cstring): cstring {. + importc: "getenv", header: "".} + proc c_putenv(env: cstring): cint {. + importc: "putenv", header: "".} + proc c_unsetenv(env: cstring): cint {. + importc: "unsetenv", header: "".} - proc getEnvVarsC() = - # retrieves the variables of char** env of C's main proc - if not envComputed: - environment = @[] - when useNSGetEnviron: - var gEnv = NSGetEnviron()[] - var i = 0 - while gEnv[i] != nil: - add environment, $gEnv[i] - inc(i) - envComputed = true + # Environment handling cannot be put into RTL, because the ``envPairs`` + # iterator depends on ``environment``. -proc findEnvVar(key: string): int = - getEnvVarsC() - var temp = key & '=' - for i in 0..high(environment): - when defined(windows): - if skipIgnoreCase(environment[i], temp) == len(temp): return i - else: - if startsWith(environment[i], temp): return i - return -1 + var + envComputed {.threadvar.}: bool + environment {.threadvar.}: seq[string] -proc getEnv*(key: string, default = ""): TaintedString {.tags: [ReadEnvEffect].} = - ## Returns the value of the `environment variable`:idx: named `key`. - ## - ## If the variable does not exist, `""` is returned. To distinguish - ## whether a variable exists or it's value is just `""`, call - ## `existsEnv(key) proc <#existsEnv,string>`_. - ## - ## See also: - ## * `existsEnv proc <#existsEnv,string>`_ - ## * `putEnv proc <#putEnv,string,string>`_ - ## * `delEnv proc <#delEnv,string>`_ - ## * `envPairs iterator <#envPairs.i>`_ - runnableExamples: - assert getEnv("unknownEnv") == "" - assert getEnv("unknownEnv", "doesn't exist") == "doesn't exist" + when defined(nimV2): + proc unpairedEnvAllocs*(): int = + result = environment.len + if result > 0: inc result - when nimvm: - discard "built into the compiler" - else: - var i = findEnvVar(key) - if i >= 0: - return TaintedString(substr(environment[i], find(environment[i], '=')+1)) - else: - var env = c_getenv(key) - if env == nil: return TaintedString(default) - result = TaintedString($env) - -proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} = - ## Checks whether the environment variable named `key` exists. - ## Returns true if it exists, false otherwise. - ## - ## See also: - ## * `getEnv proc <#getEnv,string,string>`_ - ## * `putEnv proc <#putEnv,string,string>`_ - ## * `delEnv proc <#delEnv,string>`_ - ## * `envPairs iterator <#envPairs.i>`_ - runnableExamples: - assert not existsEnv("unknownEnv") - - when nimvm: - discard "built into the compiler" - else: - if c_getenv(key) != nil: return true - else: return findEnvVar(key) >= 0 - -proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} = - ## Sets the value of the `environment variable`:idx: named `key` to `val`. - ## If an error occurs, `OSError` is raised. - ## - ## See also: - ## * `getEnv proc <#getEnv,string,string>`_ - ## * `existsEnv proc <#existsEnv,string>`_ - ## * `delEnv proc <#delEnv,string>`_ - ## * `envPairs iterator <#envPairs.i>`_ - - # Note: by storing the string in the environment sequence, - # we guarantee that we don't free the memory before the program - # ends (this is needed for POSIX compliance). It is also needed so that - # the process itself may access its modified environment variables! - when nimvm: - discard "built into the compiler" - else: - var indx = findEnvVar(key) - if indx >= 0: - environment[indx] = key & '=' & val - else: - add environment, (key & '=' & val) - indx = high(environment) - when defined(windows) and not defined(nimscript): - when useWinUnicode: - var k = newWideCString(key) - var v = newWideCString(val) - if setEnvironmentVariableW(k, v) == 0'i32: raiseOSError(osLastError()) + when defined(windows) and not defined(nimscript): + # because we support Windows GUI applications, things get really + # messy here... + when useWinUnicode: + when defined(cpp): + proc strEnd(cstr: WideCString, c = 0'i32): WideCString {. + importcpp: "(NI16*)wcschr((const wchar_t *)#, #)", header: "".} else: - if setEnvironmentVariableA(key, val) == 0'i32: raiseOSError(osLastError()) + proc strEnd(cstr: WideCString, c = 0'i32): WideCString {. + importc: "wcschr", header: "".} else: - if c_putenv(environment[indx]) != 0'i32: - raiseOSError(osLastError()) + proc strEnd(cstr: cstring, c = 0'i32): cstring {. + importc: "strchr", header: "".} + + proc getEnvVarsC() = + if not envComputed: + environment = @[] + when useWinUnicode: + var + env = getEnvironmentStringsW() + e = env + if e == nil: return # an error occurred + while true: + var eend = strEnd(e) + add(environment, $e) + e = cast[WideCString](cast[ByteAddress](eend)+2) + if eend[1].int == 0: break + discard freeEnvironmentStringsW(env) + else: + var + env = getEnvironmentStringsA() + e = env + if e == nil: return # an error occurred + while true: + var eend = strEnd(e) + add(environment, $e) + e = cast[cstring](cast[ByteAddress](eend)+1) + if eend[1] == '\0': break + discard freeEnvironmentStringsA(env) + envComputed = true -proc delEnv*(key: string) {.tags: [WriteEnvEffect].} = - ## Deletes the `environment variable`:idx: named `key`. - ## If an error occurs, `OSError` is raised. - ## - ## See also:ven - ## * `getEnv proc <#getEnv,string,string>`_ - ## * `existsEnv proc <#existsEnv,string>`_ - ## * `putEnv proc <#putEnv,string,string>`_ - ## * `envPairs iterator <#envPairs.i>`_ - when nimvm: - discard "built into the compiler" else: - var indx = findEnvVar(key) - if indx < 0: return # Do nothing if the env var is not already set - when defined(windows) and not defined(nimscript): - when useWinUnicode: - var k = newWideCString(key) - if setEnvironmentVariableW(k, nil) == 0'i32: raiseOSError(osLastError()) - else: - if setEnvironmentVariableA(key, nil) == 0'i32: raiseOSError(osLastError()) - else: - if c_unsetenv(key) != 0'i32: - raiseOSError(osLastError()) - environment.delete(indx) + const + useNSGetEnviron = (defined(macosx) and not defined(ios) and not defined(emscripten)) or defined(nimscript) -iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [ReadEnvEffect].} = - ## Iterate over all `environments variables`:idx:. - ## - ## In the first component of the tuple is the name of the current variable stored, - ## in the second its value. - ## - ## See also: - ## * `getEnv proc <#getEnv,string,string>`_ - ## * `existsEnv proc <#existsEnv,string>`_ - ## * `putEnv proc <#putEnv,string,string>`_ - ## * `delEnv proc <#delEnv,string>`_ - getEnvVarsC() - for i in 0..high(environment): - var p = find(environment[i], '=') - yield (TaintedString(substr(environment[i], 0, p-1)), - TaintedString(substr(environment[i], p+1))) + when useNSGetEnviron: + # From the manual: + # Shared libraries and bundles don't have direct access to environ, + # which is only available to the loader ld(1) when a complete program + # is being linked. + # The environment routines can still be used, but if direct access to + # environ is needed, the _NSGetEnviron() routine, defined in + # , can be used to retrieve the address of environ + # at runtime. + proc NSGetEnviron(): ptr cstringArray {. + importc: "_NSGetEnviron", header: "".} + elif defined(haiku): + var gEnv {.importc: "environ", header: "".}: cstringArray + else: + var gEnv {.importc: "environ".}: cstringArray + + proc getEnvVarsC() = + # retrieves the variables of char** env of C's main proc + if not envComputed: + environment = @[] + when useNSGetEnviron: + var gEnv = NSGetEnviron()[] + var i = 0 + while gEnv[i] != nil: + add environment, $gEnv[i] + inc(i) + envComputed = true + + proc findEnvVar(key: string): int = + getEnvVarsC() + var temp = key & '=' + for i in 0..high(environment): + when defined(windows): + if skipIgnoreCase(environment[i], temp) == len(temp): return i + else: + if startsWith(environment[i], temp): return i + return -1 + + proc getEnv*(key: string, default = ""): TaintedString {.tags: [ReadEnvEffect].} = + ## Returns the value of the `environment variable`:idx: named `key`. + ## + ## If the variable does not exist, `""` is returned. To distinguish + ## whether a variable exists or it's value is just `""`, call + ## `existsEnv(key) proc <#existsEnv,string>`_. + ## + ## See also: + ## * `existsEnv proc <#existsEnv,string>`_ + ## * `putEnv proc <#putEnv,string,string>`_ + ## * `delEnv proc <#delEnv,string>`_ + ## * `envPairs iterator <#envPairs.i>`_ + runnableExamples: + assert getEnv("unknownEnv") == "" + assert getEnv("unknownEnv", "doesn't exist") == "doesn't exist" + + when nimvm: + discard "built into the compiler" + else: + var i = findEnvVar(key) + if i >= 0: + return TaintedString(substr(environment[i], find(environment[i], '=')+1)) + else: + var env = c_getenv(key) + if env == nil: return TaintedString(default) + result = TaintedString($env) + + proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} = + ## Checks whether the environment variable named `key` exists. + ## Returns true if it exists, false otherwise. + ## + ## See also: + ## * `getEnv proc <#getEnv,string,string>`_ + ## * `putEnv proc <#putEnv,string,string>`_ + ## * `delEnv proc <#delEnv,string>`_ + ## * `envPairs iterator <#envPairs.i>`_ + runnableExamples: + assert not existsEnv("unknownEnv") + + when nimvm: + discard "built into the compiler" + else: + if c_getenv(key) != nil: return true + else: return findEnvVar(key) >= 0 + + proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} = + ## Sets the value of the `environment variable`:idx: named `key` to `val`. + ## If an error occurs, `OSError` is raised. + ## + ## See also: + ## * `getEnv proc <#getEnv,string,string>`_ + ## * `existsEnv proc <#existsEnv,string>`_ + ## * `delEnv proc <#delEnv,string>`_ + ## * `envPairs iterator <#envPairs.i>`_ + + # Note: by storing the string in the environment sequence, + # we guarantee that we don't free the memory before the program + # ends (this is needed for POSIX compliance). It is also needed so that + # the process itself may access its modified environment variables! + when nimvm: + discard "built into the compiler" + else: + var indx = findEnvVar(key) + if indx >= 0: + environment[indx] = key & '=' & val + else: + add environment, (key & '=' & val) + indx = high(environment) + when defined(windows) and not defined(nimscript): + when useWinUnicode: + var k = newWideCString(key) + var v = newWideCString(val) + if setEnvironmentVariableW(k, v) == 0'i32: raiseOSError(osLastError()) + else: + if setEnvironmentVariableA(key, val) == 0'i32: raiseOSError(osLastError()) + else: + if c_putenv(environment[indx]) != 0'i32: + raiseOSError(osLastError()) + + proc delEnv*(key: string) {.tags: [WriteEnvEffect].} = + ## Deletes the `environment variable`:idx: named `key`. + ## If an error occurs, `OSError` is raised. + ## + ## See also:ven + ## * `getEnv proc <#getEnv,string,string>`_ + ## * `existsEnv proc <#existsEnv,string>`_ + ## * `putEnv proc <#putEnv,string,string>`_ + ## * `envPairs iterator <#envPairs.i>`_ + when nimvm: + discard "built into the compiler" + else: + var indx = findEnvVar(key) + if indx < 0: return # Do nothing if the env var is not already set + when defined(windows) and not defined(nimscript): + when useWinUnicode: + var k = newWideCString(key) + if setEnvironmentVariableW(k, nil) == 0'i32: raiseOSError(osLastError()) + else: + if setEnvironmentVariableA(key, nil) == 0'i32: raiseOSError(osLastError()) + else: + if c_unsetenv(key) != 0'i32: + raiseOSError(osLastError()) + environment.delete(indx) + + iterator envPairs*(): tuple[key, value: TaintedString] {.tags: [ReadEnvEffect].} = + ## Iterate over all `environments variables`:idx:. + ## + ## In the first component of the tuple is the name of the current variable stored, + ## in the second its value. + ## + ## See also: + ## * `getEnv proc <#getEnv,string,string>`_ + ## * `existsEnv proc <#existsEnv,string>`_ + ## * `putEnv proc <#putEnv,string,string>`_ + ## * `delEnv proc <#delEnv,string>`_ + getEnvVarsC() + for i in 0..high(environment): + var p = find(environment[i], '=') + yield (TaintedString(substr(environment[i], 0, p-1)), + TaintedString(substr(environment[i], p+1))) diff --git a/tests/js/tos.nim b/tests/js/tos.nim index 30c19c1ae..07eb3aaa3 100644 --- a/tests/js/tos.nim +++ b/tests/js/tos.nim @@ -19,3 +19,29 @@ block: if not isWindows: doAssert cwd.isAbsolute doAssert relativePath(getCurrentDir() / "foo", "bar") == "../foo" + +import std/sequtils + +template main = + putEnv("foo", "bar") + doAssert getEnv("foo") == "bar" + doAssert existsEnv("foo") + + putEnv("foo", "") + doAssert existsEnv("foo") + putEnv("foo", "bar2") + doAssert getEnv("foo") == "bar2" + + when nimvm: + discard + else: + # need support in vmops: envPairs, delEnv + let s = toSeq(envPairs()) + doAssert ("foo", "bar2") in s + doAssert ("foo", "bar") notin s + + delEnv("foo") + doAssert not existsEnv("foo") + +static: main() +main() From 4663319bf566fc39df4252100d0e5d5f55da336e Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Thu, 12 Nov 2020 22:28:45 +0800 Subject: [PATCH 0966/3768] follow #8463 #14157 and document cstring literals modification is not allowed (#15878) * follow #8463 #14157 and document cstring literals * Update doc/manual.rst Co-authored-by: Juan Carlos Co-authored-by: Andreas Rumpf --- doc/manual.rst | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/doc/manual.rst b/doc/manual.rst index fa28582a7..bfcce24cf 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -1280,6 +1280,20 @@ string from a cstring: var cstr: cstring = str var newstr: string = $cstr +``cstring`` literals shouldn't be modified. + +.. code-block:: nim + var x = cstring"literals" + x[1] = 'A' # This is wrong!!! + +If the ``cstring`` originates from a regular memory (not read-only memory), +it can be modified: + +.. code-block:: nim + var x = "123456" + var s: cstring = x + s[0] = 'u' # This is ok + Structured types ---------------- A variable of a structured type can hold multiple values at the same From 077eba7b388259ecca7e9e7990f48efe9e3641c5 Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 12 Nov 2020 23:24:45 +0800 Subject: [PATCH 0967/3768] make workaround for #15934 and #15620 --- lib/core/typeinfo.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/core/typeinfo.nim b/lib/core/typeinfo.nim index 5c4e1b601..2f213956b 100644 --- a/lib/core/typeinfo.nim +++ b/lib/core/typeinfo.nim @@ -327,7 +327,8 @@ proc setPointer*(x: Any, y: pointer) = ## ``akString``, ``akCString``, ``akProc``, ``akRef``, ``akPtr``, ## ``akPointer``, ``akSequence``. assert x.rawType.kind in pointerLike - genericAssign(x.value, y, x.rawType) + if y != nil: + genericAssign(x.value, y, x.rawType) proc fieldsAux(p: pointer, n: ptr TNimNode, ret: var seq[tuple[name: cstring, any: Any]]) = From 8ec3c141ae28616382a82235b2f4049eaa0ab0d7 Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 12 Nov 2020 23:58:42 +0800 Subject: [PATCH 0968/3768] add testcase --- tests/stdlib/tmarshal.nim | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/stdlib/tmarshal.nim b/tests/stdlib/tmarshal.nim index fc5703a03..315036631 100644 --- a/tests/stdlib/tmarshal.nim +++ b/tests/stdlib/tmarshal.nim @@ -4,6 +4,9 @@ true true alpha 100 omega 200 +Some(null) +None[JsonNode] +(numeric: "") ''' joinable: false """ @@ -123,3 +126,25 @@ var foo = Foo(a2: "", a4: @[], a6: @[1]) foo.a6.setLen 0 doAssert $$foo == """{"a1": "", "a2": "", "a3": [], "a4": [], "a5": [], "a6": []}""" testit(foo) + +import options, json + +# bug #15934 +block: + let + a1 = some(newJNull()) + a2 = none(JsonNode) + echo ($$a1).to[:Option[JsonNode]] + echo ($$a2).to[:Option[JsonNode]] + + +# bug #15620 +block: + let str = """{"numeric": null}""" + + type + LegacyEntry = object + numeric: string + + let test = to[LegacyEntry](str) + echo test From 1413818aa14fd694d362326f428b470fa22e1a0a Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 12 Nov 2020 09:36:07 -0800 Subject: [PATCH 0969/3768] Update changelog.md --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index ac1ec1163..11930cf92 100644 --- a/changelog.md +++ b/changelog.md @@ -31,8 +31,8 @@ - Removed deprecated `iup` module from stdlib, it has already moved to [nimble](https://github.com/nim-lang/iup). -- Removed deprecated `iup` module from stdlib, it has already moved to [nimble](https://github.com/nim-lang/iup). - nodejs now supports osenv: `getEnv`, `putEnv`, `envPairs`, `delEnv`, `existsEnv` + - `doAssertRaises` now correctly handles foreign exceptions. ## Language changes From d0c4c738b955f2641baa55ece6b5b90d4e632d8c Mon Sep 17 00:00:00 2001 From: Miran Date: Thu, 12 Nov 2020 22:25:23 +0100 Subject: [PATCH 0970/3768] add a tester for rst2html (#15936) --- koch.nim | 1 + nimdoc/rst2html/expected/rst_examples.html | 319 +++++++++++++ nimdoc/rst2html/source/rst_examples.rst | 510 +++++++++++++++++++++ nimdoc/rsttester.nim | 34 ++ 4 files changed, 864 insertions(+) create mode 100644 nimdoc/rst2html/expected/rst_examples.html create mode 100644 nimdoc/rst2html/source/rst_examples.rst create mode 100644 nimdoc/rsttester.nim diff --git a/koch.nim b/koch.nim index 47bde9d18..628c81098 100644 --- a/koch.nim +++ b/koch.nim @@ -554,6 +554,7 @@ proc runCI(cmd: string) = execFold("test with -d:nimHasLibFFI", "$1 $2 -r testament/testament --nim:$1 r tests/misc/trunner.nim -d:nimTrunnerFfi" % [nimFFI, backend]) execFold("Run nimdoc tests", "nim c -r nimdoc/tester") + execFold("Run rst2html tests", "nim c -r nimdoc/rsttester") execFold("Run nimpretty tests", "nim c -r nimpretty/tester.nim") when defined(posix): execFold("Run nimsuggest tests", "nim c -r nimsuggest/tester") diff --git a/nimdoc/rst2html/expected/rst_examples.html b/nimdoc/rst2html/expected/rst_examples.html new file mode 100644 index 000000000..8a08e8df5 --- /dev/null +++ b/nimdoc/rst2html/expected/rst_examples.html @@ -0,0 +1,319 @@ + + + + + + + + + + + + + + + + + + +Not a Nim Manual + + + + + + + + +
      +
      +

      Not a Nim Manual

      +
      + + +
      +
      + +

      + +
      Authors: Andreas Rumpf, Zahary Karadjov
      Version: |nimversion|

      "Complexity" seems to be a lot like "energy": you can transfer it from the end-user to one/some of the other players, but the total amount seems to remain pretty much constant for a given task. -- Ran

      + +

      About this document

      Note: This document is a draft! Several of Nim's features may need more precise wording. This manual is constantly evolving into a proper specification.

      +

      Note: The experimental features of Nim are covered here.

      +

      Note: Assignments, moves, and destruction are specified in the destructors document.

      +

      The language constructs are explained using an extended BNF, in which (a)* means 0 or more a's, a+ means 1 or more a's, and (a)? means an optional a. Parentheses may be used to group elements.

      +

      & is the lookahead operator; &a means that an a is expected but not consumed. It will be consumed in the following rule.

      +

      Non-terminals start with a lowercase letter, abstract terminal symbols are in UPPERCASE. Verbatim terminal symbols (including keywords) are quoted with '. An example:

      +ifStmt = 'if' expr ':' stmts ('elif' expr ':' stmts)* ('else' stmts)?
      +

      +

      In a typical Nim program, most of the code is compiled into the executable. However, some of the code may be executed at compile-time. This can include constant expressions, macro definitions, and Nim procedures used by macro definitions. Most of the Nim language is supported at compile-time, but there are some restrictions -- see Restrictions on Compile-Time Execution for details. We use the term runtime to cover both compile-time execution and code execution in the executable.

      +
      var a: array[0..1, char]
      +let i = 5
      +try:
      +  a[i] = 'N'
      +except IndexDefect:
      +  echo "invalid index"
      +

      Encoding

      All Nim source files are in the UTF-8 encoding (or its ASCII subset). Other encodings are not supported. Any of the standard platform line termination sequences can be used - the Unix form using ASCII LF (linefeed), the Windows form using the ASCII sequence CR LF (return followed by linefeed), or the old Macintosh form using the ASCII CR (return) character. All of these forms can be used equally, regardless of the platform.

      + +

      Indentation

      Nim's standard grammar describes an indentation sensitive language. This means that all the control structures are recognized by indentation. Indentation consists only of spaces; tabulators are not allowed.

      +

      With this notation we can now easily define the core of the grammar: A block of statements (simplified example):

      +ifStmt = 'if' expr ':' stmt
      +         (IND{=} 'elif' expr ':' stmt)*
      +         (IND{=} 'else' ':' stmt)?
      +
      +simpleStmt = ifStmt / ...
      +
      +stmt = IND{>} stmt ^+ IND{=} DED  # list of statements
      +     / simpleStmt                 # or a simple statement
      +

      +

      String literals can be delimited by matching double quotes, and can contain the following escape sequences:

      + + + + + + + + + + + + + + + + + +
      Escape sequenceMeaning
      \pplatform specific newline: CRLF on Windows, LF on Unix
      \r, \ccarriage return
      \n, \lline feed (often called newline)
      \fform feed
      \ttabulator
      \vvertical tabulator
      \\backslash
      \"quotation mark
      \'apostrophe
      \ '0'..'9'+character with decimal value d; all decimal digits directly following are used for the character
      \aalert
      \bbackspace
      \eescape [ESC]
      \x HHcharacter with hex value HH; exactly two hex digits are allowed
      \u HHHHunicode codepoint with hex value HHHH; exactly four hex digits are allowed
      \u {H+}unicode codepoint; all hex digits enclosed in {} are used for the codepoint
      """"long string within quotes""""

      Produces:

      +"long string within quotes"
      +

      + +

      Operators

      Nim allows user defined operators. An operator is any combination of the following characters:

      +=     +     -     *     /     <     >
      +@     $     ~     &     %     |
      +!     ?     ^     .     :     \
      +

      +

      (The grammar uses the terminal OPR to refer to operator symbols as defined here.)

      +

      The following strings denote other tokens:

      +`   (    )     {    }     [    ]    ,  ;   [.    .]  {.   .}  (.  .)  [:
      +

      +

      Otherwise, precedence is determined by the first character.

      + + + + + + + + + + + + +
      Precedence levelOperatorsFirst characterTerminal symbol
      10 (highest)$ ^OP10
      9* / div mod shl shr %* % \ /OP9
      8+ -+ - ~ |OP8
      7&&OP7
      6...OP6
      5== <= < >= > != in notin is isnot not of as from= < > !OP5
      4andOP4
      3or xorOP3
      2@ : ?OP2
      1assignment operator (like +=, *=)OP1
      0 (lowest)arrow like operator (like ->, =>)OP0
      +

      Constants and Constant Expressions

      A constant is a symbol that is bound to the value of a constant expression. Constant expressions are restricted to depend only on the following categories of values and operations, because these are either built into the language or declared and evaluated before semantic analysis of the constant expression:

      +
      • literals
      • +
      • built-in operators
      • +
      • previously declared constants and compile-time variables
      • +
      • previously declared macros and templates
      • +
      • previously declared procedures that have no side effects beyond possibly modifying compile-time variables
      • +
      +

      These integer types are pre-defined:

      +
      int
      +
      the generic signed integer type; its size is platform-dependent and has the same size as a pointer. This type should be used in general. An integer literal that has no type suffix is of this type if it is in the range low(int32)..high(int32) otherwise the literal's type is int64.
      +
      intXX
      +
      additional signed integer types of XX bits use this naming scheme (example: int16 is a 16-bit wide integer). The current implementation supports int8, int16, int32, int64. Literals of these types have the suffix 'iXX.
      +
      uint
      +
      the generic unsigned integer type; its size is platform-dependent and has the same size as a pointer. An integer literal with the type suffix 'u is of this type.
      +
      +

      Let T's be p's return type. NRVO applies for T if sizeof(T) >= N (where N is implementation dependent), in other words, it applies for "big" structures.

      +

      Apart from built-in operations like array indexing, memory allocation, etc. the raise statement is the only way to raise an exception.

      +

      typedesc used as a parameter type also introduces an implicit generic. typedesc has its own set of rules:

      +

      The !=, >, >=, in, notin, isnot operators are in fact templates:

      +

      a > b is transformed into b < a.
      a in b is transformed into contains(b, a).
      notin and isnot have the obvious meanings.

      A template where every parameter is untyped is called an immediate template. For historical reasons templates can be explicitly annotated with an immediate pragma and then these templates do not take part in overloading resolution and the parameters' types are ignored by the compiler. Explicit immediate templates are now deprecated.

      + +

      Symbol lookup in generics

      +

      Open and Closed symbols

      The symbol binding rules in generics are slightly subtle: There are "open" and "closed" symbols. A "closed" symbol cannot be re-bound in the instantiation context, an "open" symbol can. Per default overloaded symbols are open and every other symbol is closed.

      +

      In templates identifiers can be constructed with the backticks notation:

      +
      template typedef(name: untyped, typ: typedesc) =
      +  type
      +    `T name`* {.inject.} = typ
      +    `P name`* {.inject.} = ref `T name`
      +
      +typedef(myint, int)
      +var x: PMyInt

      In the example name is instantiated with myint, so `T name` becomes Tmyint.

      +

      Only top-level symbols that are marked with an asterisk (*) are exported.

      +

      The algorithm for compiling modules is:

      +
      • compile the whole module as usual, following import statements recursively
      • +
      • if there is a cycle only import the already parsed symbols (that are exported); if an unknown identifier occurs then abort
      • +
      + +

      Collective imports from a directory

      The syntax import dir / [moduleA, moduleB] can be used to import multiple modules from the same directory.

      + +

      Pragmas

      Pragmas are Nim's method to give the compiler additional information / commands without introducing a massive number of new keywords. Pragmas are processed on the fly during semantic checking. Pragmas are enclosed in the special {. and .} curly brackets. Pragmas are also often used as a first implementation to play with a language feature before a nicer syntax to access the feature becomes available.

      + +

      deprecated pragma

      The deprecated pragma is used to mark a symbol as deprecated:

      +

      Note: c2nim can parse a large subset of C++ and knows about the importcpp pragma pattern language. It is not necessary to know all the details described here.

      +

      Pure libraries do not depend on any external *.dll or lib*.so binary while impure libraries do. A wrapper is an impure library that is a very low-level interface to a C library.

      + +

      Pure libraries

      +

      Automatic imports

      • system Basic procs and operators that every program needs. It also provides IO facilities for reading and writing text and binary files. It is imported implicitly by the compiler. Do not import it directly. It relies on compiler magic to work.
      • +
      • threads Basic Nim thread support. Note: This is part of the system module. Do not import it explicitly. Enabled with --threads:on.
      • +
      + +

      Code reordering

      The code reordering feature can implicitly rearrange procedure, template, and macro definitions along with variable declarations and initializations at the top level scope so that, to a large extent, a programmer should not have to worry about ordering definitions correctly or be forced to use forward declarations to preface definitions inside a module.

      +

      Example:

      +
      {.experimental: "codeReordering".}
      +
      +proc foo(x: int) =
      +  bar(x)
      +
      +proc bar(x: int) =
      +  echo(x)
      +
      +foo(10)

      It is important to note that reordering only works for symbols at top level scope. Therefore, the following will fail to compile:

      + +

      Parameter constraints

      The parameter constraint expression can use the operators | (or), & (and) and ~ (not) and the following predicates:

      + +

      The ~ operator

      The ~ operator is the not operator in patterns:

      + +

      The ** operator

      The ** is much like the * operator, except that it gathers not only all the arguments, but also the matched operators in reverse polish notation:

      +

      Nim significantly improves on the safety of these features via additional pragmas:

      +
      1. A guard annotation is introduced to prevent data races.
      2. +
      3. Every access of a guarded memory location needs to happen in an appropriate locks statement.
      4. +
      5. Locks and routines can be annotated with lock levels to allow potential deadlocks to be detected during semantic analysis.
      6. +
      +
      1. Two output parameters should never be aliased.
      2. +
      3. An input and an output parameter should not be aliased.
      4. +
      5. An output parameter should never be aliased with a global or thread local variable referenced by the called proc.
      6. +
      7. An input parameter should not be aliased with a global or thread local variable updated by the called proc.
      8. +
      +

      One problem with rules 3 and 4 is that they affect specific global or thread local variables, but Nim's effect tracking only tracks "uses no global variable" via .noSideEffect. The rules 3 and 4 can also be approximated by a different rule:

      +
      1. A global or thread local variable (or a location derived from such a location) can only passed to a parameter of a .noSideEffect proc.
      2. +
      +

      These two procs are the two modus operandi of the real-time garbage collector:

      +

      (1) GC_SetMaxPause Mode

      +

      You can call GC_SetMaxPause at program startup and then each triggered garbage collector run tries to not take longer than maxPause time. However, it is possible (and common) that the work is nevertheless not evenly distributed as each call to new can trigger the garbage collector and thus take maxPause time.

      +

      (2) GC_step Mode

      +

      This allows the garbage collector to perform some work for up to us time. This is useful to call in the main loop to ensure the garbage collector can do its work. To bind all garbage collector activity to a GC_step call, deactivate the garbage collector with GC_disable at program startup. If strongAdvice is set to true, then the garbage collector will be forced to perform the collection cycle. Otherwise, the garbage collector may decide not to do anything, if there is not much garbage to collect. You may also specify the current stack size via stackSize parameter. It can improve performance when you know that there are no unique Nim references below a certain point on the stack. Make sure the size you specify is greater than the potential worst-case size.

      +

      It can improve performance when you know that there are no unique Nim references below a certain point on the stack. Make sure the size you specify is greater than the potential worst-case size.

      +

      +

      These procs provide a "best effort" real-time guarantee; in particular the cycle collector is not aware of deadlines. Deactivate it to get more predictable real-time behaviour. Tests show that a 1ms max pause time will be met in almost all cases on modern CPUs (with the cycle collector disabled).

      + +

      Time measurement with garbage collectors

      The garbage collectors' way of measuring time uses (see lib/system/timers.nim for the implementation):

      +
      1. QueryPerformanceCounter and QueryPerformanceFrequency on Windows.
      2. +
      3. mach_absolute_time on Mac OS X.
      4. +
      5. gettimeofday on Posix systems.
      6. +
      +

      As such it supports a resolution of nanoseconds internally; however, the API uses microseconds for convenience.

      + +

      Introduction

      +"Der Mensch ist doch ein Augentier -- schöne Dinge wünsch ich mir." +

      This document is a tutorial for the programming language Nim. This tutorial assumes that you are familiar with basic programming concepts like variables, types, or statements but is kept very basic. The manual contains many more examples of the advanced language features. All code examples in this tutorial, as well as the ones found in the rest of Nim's documentation, follow the Nim style guide.

      +

      However, this does not work. The problem is that the procedure should not only return, but return and continue after an iteration has finished. This return and continue is called a yield statement. Now the only thing left to do is to replace the proc keyword by iterator and here it is - our first iterator:

      + + + + + +
      A1 headerA2 | not fooled
      C1C2 bold
      D1 code |D2
      E1 | text
      F2 without pipe

      not in table

      +

      + +
      +
      + +
      + +
      +
      +
      + + + diff --git a/nimdoc/rst2html/source/rst_examples.rst b/nimdoc/rst2html/source/rst_examples.rst new file mode 100644 index 000000000..c8d15aef8 --- /dev/null +++ b/nimdoc/rst2html/source/rst_examples.rst @@ -0,0 +1,510 @@ +========== +Not a Nim Manual +========== + +:Authors: Andreas Rumpf, Zahary Karadjov +:Version: |nimversion| + +.. contents:: + + + "Complexity" seems to be a lot like "energy": you can transfer it from the + end-user to one/some of the other players, but the total amount seems to remain + pretty much constant for a given task. -- Ran + + +About this document +=================== + +**Note**: This document is a draft! Several of Nim's features may need more +precise wording. This manual is constantly evolving into a proper specification. + +**Note**: The experimental features of Nim are +covered `here `_. + +**Note**: Assignments, moves, and destruction are specified in +the `destructors `_ document. + +The language constructs are explained using an extended BNF, in which ``(a)*`` +means 0 or more ``a``'s, ``a+`` means 1 or more ``a``'s, and ``(a)?`` means an +optional *a*. Parentheses may be used to group elements. + +``&`` is the lookahead operator; ``&a`` means that an ``a`` is expected but +not consumed. It will be consumed in the following rule. + +Non-terminals start with a lowercase letter, abstract terminal symbols are in +UPPERCASE. Verbatim terminal symbols (including keywords) are quoted +with ``'``. An example:: + + ifStmt = 'if' expr ':' stmts ('elif' expr ':' stmts)* ('else' stmts)? + +In a typical Nim program, most of the code is compiled into the executable. +However, some of the code may be executed at +`compile-time`:idx:. This can include constant expressions, macro definitions, +and Nim procedures used by macro definitions. Most of the Nim language is +supported at compile-time, but there are some restrictions -- see `Restrictions +on Compile-Time Execution <#restrictions-on-compileminustime-execution>`_ for +details. We use the term `runtime`:idx: to cover both compile-time execution +and code execution in the executable. + +.. code-block:: nim + var a: array[0..1, char] + let i = 5 + try: + a[i] = 'N' + except IndexDefect: + echo "invalid index" + +Encoding +-------- + +All Nim source files are in the UTF-8 encoding (or its ASCII subset). Other +encodings are not supported. Any of the standard platform line termination +sequences can be used - the Unix form using ASCII LF (linefeed), the Windows +form using the ASCII sequence CR LF (return followed by linefeed), or the old +Macintosh form using the ASCII CR (return) character. All of these forms can be +used equally, regardless of the platform. + + +Indentation +----------- + +Nim's standard grammar describes an `indentation sensitive`:idx: language. +This means that all the control structures are recognized by indentation. +Indentation consists only of spaces; tabulators are not allowed. + +With this notation we can now easily define the core of the grammar: A block of +statements (simplified example):: + + ifStmt = 'if' expr ':' stmt + (IND{=} 'elif' expr ':' stmt)* + (IND{=} 'else' ':' stmt)? + + simpleStmt = ifStmt / ... + + stmt = IND{>} stmt ^+ IND{=} DED # list of statements + / simpleStmt # or a simple statement + + +String literals can be delimited by matching double quotes, and can +contain the following `escape sequences`:idx:\ : + +================== =================================================== + Escape sequence Meaning +================== =================================================== + ``\p`` platform specific newline: CRLF on Windows, + LF on Unix + ``\r``, ``\c`` `carriage return`:idx: + ``\n``, ``\l`` `line feed`:idx: (often called `newline`:idx:) + ``\f`` `form feed`:idx: + ``\t`` `tabulator`:idx: + ``\v`` `vertical tabulator`:idx: + ``\\`` `backslash`:idx: + ``\"`` `quotation mark`:idx: + ``\'`` `apostrophe`:idx: + ``\`` '0'..'9'+ `character with decimal value d`:idx:; + all decimal digits directly + following are used for the character + ``\a`` `alert`:idx: + ``\b`` `backspace`:idx: + ``\e`` `escape`:idx: `[ESC]`:idx: + ``\x`` HH `character with hex value HH`:idx:; + exactly two hex digits are allowed + ``\u`` HHHH `unicode codepoint with hex value HHHH`:idx:; + exactly four hex digits are allowed + ``\u`` {H+} `unicode codepoint`:idx:; + all hex digits enclosed in ``{}`` are used for + the codepoint +================== =================================================== + +.. code-block:: nim + """"long string within quotes"""" + +Produces:: + + "long string within quotes" + +Operators +--------- + +Nim allows user defined operators. An operator is any combination of the +following characters:: + + = + - * / < > + @ $ ~ & % | + ! ? ^ . : \ + +(The grammar uses the terminal OPR to refer to operator symbols as +defined here.) + +The following strings denote other tokens:: + + ` ( ) { } [ ] , ; [. .] {. .} (. .) [: + + +Otherwise, precedence is determined by the first character. + +================ ======================================================= ================== =============== +Precedence level Operators First character Terminal symbol +================ ======================================================= ================== =============== + 10 (highest) ``$ ^`` OP10 + 9 ``* / div mod shl shr %`` ``* % \ /`` OP9 + 8 ``+ -`` ``+ - ~ |`` OP8 + 7 ``&`` ``&`` OP7 + 6 ``..`` ``.`` OP6 + 5 ``== <= < >= > != in notin is isnot not of as from`` ``= < > !`` OP5 + 4 ``and`` OP4 + 3 ``or xor`` OP3 + 2 ``@ : ?`` OP2 + 1 *assignment operator* (like ``+=``, ``*=``) OP1 + 0 (lowest) *arrow like operator* (like ``->``, ``=>``) OP0 +================ ======================================================= ================== =============== + + +Constants and Constant Expressions +================================== + +A `constant`:idx: is a symbol that is bound to the value of a constant +expression. Constant expressions are restricted to depend only on the following +categories of values and operations, because these are either built into the +language or declared and evaluated before semantic analysis of the constant +expression: + +* literals +* built-in operators +* previously declared constants and compile-time variables +* previously declared macros and templates +* previously declared procedures that have no side effects beyond + possibly modifying compile-time variables + +These integer types are pre-defined: + +``int`` + the generic signed integer type; its size is platform-dependent and has the + same size as a pointer. This type should be used in general. An integer + literal that has no type suffix is of this type if it is in the range + ``low(int32)..high(int32)`` otherwise the literal's type is ``int64``. + +intXX + additional signed integer types of XX bits use this naming scheme + (example: int16 is a 16-bit wide integer). + The current implementation supports ``int8``, ``int16``, ``int32``, ``int64``. + Literals of these types have the suffix 'iXX. + +``uint`` + the generic `unsigned integer`:idx: type; its size is platform-dependent and has the same size as a pointer. An integer literal with the type suffix ``'u`` is of this type. + +Let ``T``'s be ``p``'s return type. NRVO applies for ``T`` +if ``sizeof(T) >= N`` (where ``N`` is implementation dependent), +in other words, it applies for "big" structures. + +Apart from built-in operations like array indexing, memory allocation, etc. +the ``raise`` statement is the only way to raise an exception. + +.. XXX document this better! + +`typedesc` used as a parameter type also introduces an implicit +generic. `typedesc` has its own set of rules: + +The ``!=``, ``>``, ``>=``, ``in``, ``notin``, ``isnot`` operators are in fact +templates: + +| ``a > b`` is transformed into ``b < a``. +| ``a in b`` is transformed into ``contains(b, a)``. +| ``notin`` and ``isnot`` have the obvious meanings. + +A template where every parameter is ``untyped`` is called an `immediate`:idx: +template. For historical reasons templates can be explicitly annotated with +an ``immediate`` pragma and then these templates do not take part in +overloading resolution and the parameters' types are *ignored* by the +compiler. Explicit immediate templates are now deprecated. + + + +Symbol lookup in generics +------------------------- + +Open and Closed symbols +~~~~~~~~~~~~~~~~~~~~~~~ + +The symbol binding rules in generics are slightly subtle: There are "open" and +"closed" symbols. A "closed" symbol cannot be re-bound in the instantiation +context, an "open" symbol can. Per default overloaded symbols are open +and every other symbol is closed. + +In templates identifiers can be constructed with the backticks notation: + +.. code-block:: nim + :test: "nim c $1" + + template typedef(name: untyped, typ: typedesc) = + type + `T name`* {.inject.} = typ + `P name`* {.inject.} = ref `T name` + + typedef(myint, int) + var x: PMyInt + +In the example ``name`` is instantiated with ``myint``, so \`T name\` becomes +``Tmyint``. + +Only top-level symbols that are marked with an asterisk (``*``) are +exported. + +The algorithm for compiling modules is: + +- compile the whole module as usual, following import statements recursively + +- if there is a cycle only import the already parsed symbols (that are + exported); if an unknown identifier occurs then abort + + +Collective imports from a directory +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The syntax ``import dir / [moduleA, moduleB]`` can be used to import multiple modules +from the same directory. + + +Pragmas +======= + +Pragmas are Nim's method to give the compiler additional information / +commands without introducing a massive number of new keywords. Pragmas are +processed on the fly during semantic checking. Pragmas are enclosed in the +special ``{.`` and ``.}`` curly brackets. Pragmas are also often used as a +first implementation to play with a language feature before a nicer syntax +to access the feature becomes available. + + +deprecated pragma +----------------- + +The deprecated pragma is used to mark a symbol as deprecated: + +**Note**: `c2nim `_ can parse a large subset of C++ and knows +about the ``importcpp`` pragma pattern language. It is not necessary +to know all the details described here. + + + +Pure libraries do not depend on any external ``*.dll`` or ``lib*.so`` binary +while impure libraries do. A wrapper is an impure library that is a very +low-level interface to a C library. + + +Pure libraries +============== + +Automatic imports +----------------- + +* `system `_ + Basic procs and operators that every program needs. It also provides IO + facilities for reading and writing text and binary files. It is imported + implicitly by the compiler. Do not import it directly. It relies on compiler + magic to work. + +* `threads `_ + Basic Nim thread support. **Note**: This is part of the system module. Do not + import it explicitly. Enabled with ``--threads:on``. + +Code reordering +=============== + +The code reordering feature can implicitly rearrange procedure, template, and +macro definitions along with variable declarations and initializations at the top +level scope so that, to a large extent, a programmer should not have to worry +about ordering definitions correctly or be forced to use forward declarations to +preface definitions inside a module. + +.. + NOTE: The following was documentation for the code reordering precursor, + which was {.noForward.}. + + In this mode, procedure definitions may appear out of order and the compiler + will postpone their semantic analysis and compilation until it actually needs + to generate code using the definitions. In this regard, this mode is similar + to the modus operandi of dynamic scripting languages, where the function + calls are not resolved until the code is executed. Here is the detailed + algorithm taken by the compiler: + + 1. When a callable symbol is first encountered, the compiler will only note + the symbol callable name and it will add it to the appropriate overload set + in the current scope. At this step, it won't try to resolve any of the type + expressions used in the signature of the symbol (so they can refer to other + not yet defined symbols). + + 2. When a top level call is encountered (usually at the very end of the + module), the compiler will try to determine the actual types of all of the + symbols in the matching overload set. This is a potentially recursive process + as the signatures of the symbols may include other call expressions, whose + types will be resolved at this point too. + + 3. Finally, after the best overload is picked, the compiler will start + compiling the body of the respective symbol. This in turn will lead the + compiler to discover more call expressions that need to be resolved and steps + 2 and 3 will be repeated as necessary. + + Please note that if a callable symbol is never used in this scenario, its + body will never be compiled. This is the default behavior leading to best + compilation times, but if exhaustive compilation of all definitions is + required, using ``nim check`` provides this option as well. + +Example: + +.. code-block:: nim + + {.experimental: "codeReordering".} + + proc foo(x: int) = + bar(x) + + proc bar(x: int) = + echo(x) + + foo(10) + + +.. + TODO: Let's table this for now. This is an *experimental feature* and so the + specific manner in which ``declared`` operates with it can be decided in + eventuality, because right now it works a bit weirdly. + + The values of expressions involving ``declared`` are decided *before* the + code reordering process, and not after. As an example, the output of this + code is the same as it would be with code reordering disabled. + + .. code-block:: nim + {.experimental: "codeReordering".} + + proc x() = + echo(declared(foo)) + + var foo = 4 + + x() # "false" + +It is important to note that reordering *only* works for symbols at top level +scope. Therefore, the following will *fail to compile:* + + +Parameter constraints +--------------------- + +The `parameter constraint`:idx: expression can use the operators ``|`` (or), +``&`` (and) and ``~`` (not) and the following predicates: + + +The ``~`` operator +~~~~~~~~~~~~~~~~~~ + +The ``~`` operator is the **not** operator in patterns: + + +The ``**`` operator +~~~~~~~~~~~~~~~~~~~ + +The ``**`` is much like the ``*`` operator, except that it gathers not only +all the arguments, but also the matched operators in reverse polish notation: + +Nim significantly improves on the safety of these features via additional +pragmas: + +1) A `guard`:idx: annotation is introduced to prevent data races. +2) Every access of a guarded memory location needs to happen in an + appropriate `locks`:idx: statement. +3) Locks and routines can be annotated with `lock levels`:idx: to allow + potential deadlocks to be detected during semantic analysis. + +1. Two output parameters should never be aliased. +2. An input and an output parameter should not be aliased. +3. An output parameter should never be aliased with a global or thread local + variable referenced by the called proc. +4. An input parameter should not be aliased with a global or thread local + variable updated by the called proc. + +One problem with rules 3 and 4 is that they affect specific global or thread +local variables, but Nim's effect tracking only tracks "uses no global variable" +via ``.noSideEffect``. The rules 3 and 4 can also be approximated by a different rule: + +5. A global or thread local variable (or a location derived from such a location) + can only passed to a parameter of a ``.noSideEffect`` proc. + +These two procs are the two modus operandi of the real-time garbage collector: + +(1) GC_SetMaxPause Mode + + You can call ``GC_SetMaxPause`` at program startup and then each triggered + garbage collector run tries to not take longer than ``maxPause`` time. However, it is + possible (and common) that the work is nevertheless not evenly distributed + as each call to ``new`` can trigger the garbage collector and thus take ``maxPause`` + time. + +(2) GC_step Mode + + This allows the garbage collector to perform some work for up to ``us`` time. + This is useful to call in the main loop to ensure the garbage collector can do its work. + To bind all garbage collector activity to a ``GC_step`` call, + deactivate the garbage collector with ``GC_disable`` at program startup. + If ``strongAdvice`` is set to ``true``, + then the garbage collector will be forced to perform the collection cycle. + Otherwise, the garbage collector may decide not to do anything, + if there is not much garbage to collect. + You may also specify the current stack size via ``stackSize`` parameter. + It can improve performance when you know that there are no unique Nim references + below a certain point on the stack. Make sure the size you specify is greater + than the potential worst-case size. + + It can improve performance when you know that there are no unique Nim + references below a certain point on the stack. Make sure the size you specify + is greater than the potential worst-case size. + +These procs provide a "best effort" real-time guarantee; in particular the +cycle collector is not aware of deadlines. Deactivate it to get more +predictable real-time behaviour. Tests show that a 1ms max pause +time will be met in almost all cases on modern CPUs (with the cycle collector +disabled). + +Time measurement with garbage collectors +---------------------------------------- + +The garbage collectors' way of measuring time uses +(see ``lib/system/timers.nim`` for the implementation): + +1) ``QueryPerformanceCounter`` and ``QueryPerformanceFrequency`` on Windows. +2) ``mach_absolute_time`` on Mac OS X. +3) ``gettimeofday`` on Posix systems. + +As such it supports a resolution of nanoseconds internally; however, the API +uses microseconds for convenience. + +Introduction +============ + +.. raw:: html +

      + "Der Mensch ist doch ein Augentier -- schöne Dinge wünsch ich mir." +

      + + +This document is a tutorial for the programming language *Nim*. +This tutorial assumes that you are familiar with basic programming concepts +like variables, types, or statements but is kept very basic. The `manual +`_ contains many more examples of the advanced language features. +All code examples in this tutorial, as well as the ones found in the rest of +Nim's documentation, follow the `Nim style guide `_. + +However, this does not work. The problem is that the procedure should not +only ``return``, but return and **continue** after an iteration has +finished. This *return and continue* is called a `yield` statement. Now +the only thing left to do is to replace the ``proc`` keyword by ``iterator`` +and here it is - our first iterator: + +| A1 header | A2 \| not fooled +| :--- | ----: | +| C1 | C2 **bold** | ignored | +| D1 `code \|` | D2 | also ignored +| E1 \| text | +| | F2 without pipe +not in table diff --git a/nimdoc/rsttester.nim b/nimdoc/rsttester.nim new file mode 100644 index 000000000..6d41ffb86 --- /dev/null +++ b/nimdoc/rsttester.nim @@ -0,0 +1,34 @@ +import os, strutils + +const + baseDir = "nimdoc/rst2html" + +var failures = 0 + +proc exec(cmd: string) = + if execShellCmd(cmd) != 0: + quit("FAILURE: " & cmd) + +proc testRst2Html(fixup = false) = + putEnv("SOURCE_DATE_EPOCH", "100000") + const nimExe = getCurrentCompilerExe() # so that `bin/nim_temp r nimdoc/tester.nim` works + + for expectedHtml in walkDir(baseDir / "expected"): + let expectedHtml = expectedHtml.path + let sourceFile = expectedHtml.replace('\\', '/').replace("/expected/", "/source/").replace(".html", ".rst") + exec("$1 rst2html $2" % [nimExe, sourceFile]) + let producedHtml = expectedHtml.replace('\\', '/').replace("/expected/", "/source/htmldocs/") + if readFile(expectedHtml) != readFile(producedHtml): + discard execShellCmd("diff -uNdr " & expectedHtml & " " & producedHtml) + inc failures + if fixup: + copyFile(producedHtml, expectedHtml) + else: + echo "SUCCESS: files identical: ", producedHtml + if failures == 0: + removeDir(baseDir / "source/htmldocs") + +testRst2Html(defined(fixup)) + +# Check for failures +if failures > 0: quit($failures & " failures occurred.") From 492e9afa4c32be278b61498be20ae0afc3ef3188 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Fri, 13 Nov 2020 15:11:00 +0800 Subject: [PATCH 0971/3768] fix #15941 (#15948) * fix #15941 * add testcase * update --- lib/pure/collections/sharedlist.nim | 11 +++++++---- tests/stdlib/tsharedlist.nim | 17 +++++++++++++++++ 2 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 tests/stdlib/tsharedlist.nim diff --git a/lib/pure/collections/sharedlist.nim b/lib/pure/collections/sharedlist.nim index 790529b79..c72477675 100644 --- a/lib/pure/collections/sharedlist.nim +++ b/lib/pure/collections/sharedlist.nim @@ -65,11 +65,14 @@ iterator items*[A](x: var SharedList[A]): A = proc add*[A](x: var SharedList[A]; y: A) = withLock(x): var node: SharedListNode[A] - if x.tail == nil or x.tail.dataLen == ElemsPerNode: - node = cast[type node](allocShared0(sizeof(node[]))) - node.next = x.tail + if x.tail == nil: + node = cast[typeof node](allocShared0(sizeof(node[]))) + x.tail = node + x.head = node + elif x.tail.dataLen == ElemsPerNode: + node = cast[typeof node](allocShared0(sizeof(node[]))) + x.tail.next = node x.tail = node - if x.head == nil: x.head = node else: node = x.tail node.d[node.dataLen] = y diff --git a/tests/stdlib/tsharedlist.nim b/tests/stdlib/tsharedlist.nim new file mode 100644 index 000000000..a795be0f3 --- /dev/null +++ b/tests/stdlib/tsharedlist.nim @@ -0,0 +1,17 @@ +import sharedlist + +var + list: SharedList[int] + count: int + +init(list) + +for i in 1 .. 250: + list.add i + +for i in list: + inc count + +doAssert count == 250 + +deinitSharedList(list) From 83432aecfc1c4820837911b53b450e23936a4da7 Mon Sep 17 00:00:00 2001 From: treeform Date: Thu, 12 Nov 2020 23:12:03 -0800 Subject: [PATCH 0972/3768] Add documentElement to Document. (#15943) --- lib/js/dom.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/js/dom.nim b/lib/js/dom.nim index d2e5776f0..b73a85291 100644 --- a/lib/js/dom.nim +++ b/lib/js/dom.nim @@ -204,6 +204,7 @@ type Document* = ref DocumentObj DocumentObj {.importc.} = object of NodeObj activeElement*: Element + documentElement*: Element alinkColor*: cstring bgColor*: cstring body*: Element From 1fdfa234d76d59fd8d5cafd9d53cfdf8bfbf5c0a Mon Sep 17 00:00:00 2001 From: wilypomegranate Date: Fri, 13 Nov 2020 02:15:46 -0500 Subject: [PATCH 0973/3768] Don't use os for Standalone (#15944) --- lib/pure/strtabs.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pure/strtabs.nim b/lib/pure/strtabs.nim index d9f6bbc54..7b726e134 100644 --- a/lib/pure/strtabs.nim +++ b/lib/pure/strtabs.nim @@ -53,7 +53,7 @@ import std/private/since import hashes, strutils -when defined(js) or defined(nimscript): +when defined(js) or defined(nimscript) or defined(Standalone): {.pragma: rtlFunc.} else: {.pragma: rtlFunc, rtl.} @@ -301,7 +301,7 @@ proc raiseFormatException(s: string) = proc getValue(t: StringTableRef, flags: set[FormatFlag], key: string): string = if hasKey(t, key): return t.getOrDefault(key) # hm difficult: assume safety in taint mode here. XXX This is dangerous! - when defined(js) or defined(nimscript): + when defined(js) or defined(nimscript) or defined(Standalone): result = "" else: if useEnvironment in flags: result = getEnv(key).string From 778914c3ca9d6c57b0158d8bf69f92b01b0bd2e1 Mon Sep 17 00:00:00 2001 From: lenoil98 Date: Fri, 13 Nov 2020 02:16:20 -0500 Subject: [PATCH 0974/3768] Update buildsh.nimf (#15945) fix powerpc64le detection --- tools/niminst/buildsh.nimf | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/tools/niminst/buildsh.nimf b/tools/niminst/buildsh.nimf index 5ec5d5c2c..e13221781 100644 --- a/tools/niminst/buildsh.nimf +++ b/tools/niminst/buildsh.nimf @@ -159,9 +159,10 @@ case $ucpu in COMP_FLAGS="$COMP_FLAGS -m64" LINK_FLAGS="$LINK_FLAGS -m64" mycpu=`uname -p` - if [ "$(mycpu)" = "powerpc64le"] ; then - mycpu="powerpc64el" - fi + case $mycpu in + powerpc64le) + mycpu="powerpc64el" + esac else mycpu="powerpc" fi From 0f53b129b78ae480f62a68877778600ba100e96c Mon Sep 17 00:00:00 2001 From: flywind Date: Fri, 13 Nov 2020 15:28:22 +0800 Subject: [PATCH 0975/3768] nil --- lib/core/typeinfo.nim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/core/typeinfo.nim b/lib/core/typeinfo.nim index 2f213956b..00f7c176c 100644 --- a/lib/core/typeinfo.nim +++ b/lib/core/typeinfo.nim @@ -329,6 +329,8 @@ proc setPointer*(x: Any, y: pointer) = assert x.rawType.kind in pointerLike if y != nil: genericAssign(x.value, y, x.rawType) + else: + cast[ppointer](x.value)[] = nil proc fieldsAux(p: pointer, n: ptr TNimNode, ret: var seq[tuple[name: cstring, any: Any]]) = From a968e7dd9b2a22adeae8a71f7ca1e8dfb2e56a2c Mon Sep 17 00:00:00 2001 From: flywind Date: Fri, 13 Nov 2020 15:42:59 +0800 Subject: [PATCH 0976/3768] add testcase for #9754 --- tests/stdlib/t9754.nim | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 tests/stdlib/t9754.nim diff --git a/tests/stdlib/t9754.nim b/tests/stdlib/t9754.nim new file mode 100644 index 000000000..6e81fd8bc --- /dev/null +++ b/tests/stdlib/t9754.nim @@ -0,0 +1,2 @@ +import tmarshal +import tparsesql \ No newline at end of file From a86aa310813d326d88959534c36b5499870dcb95 Mon Sep 17 00:00:00 2001 From: flywind Date: Fri, 13 Nov 2020 16:15:13 +0800 Subject: [PATCH 0977/3768] minor --- tests/stdlib/t9754.nim | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/stdlib/t9754.nim b/tests/stdlib/t9754.nim index 6e81fd8bc..971b5a8fb 100644 --- a/tests/stdlib/t9754.nim +++ b/tests/stdlib/t9754.nim @@ -1,2 +1,6 @@ +discard """ + joinable: false +""" + import tmarshal import tparsesql \ No newline at end of file From 8a21f94c7263f10c12c62ec2beb736ab8ce183c9 Mon Sep 17 00:00:00 2001 From: Miran Date: Fri, 13 Nov 2020 10:25:00 +0100 Subject: [PATCH 0978/3768] refactoring: modernize rst.nim (#15953) --- lib/packages/docutils/rst.nim | 958 ++++++++++++++++------------------ 1 file changed, 449 insertions(+), 509 deletions(-) diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index c04a6e07c..a72ee17b6 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -33,37 +33,22 @@ type mcError = "Error" MsgKind* = enum ## the possible messages - meCannotOpenFile, - meExpected, - meGridTableNotImplemented, - meMarkdownIllformedTable, - meNewSectionExpected, - meGeneralParseError, - meInvalidDirective, - mwRedefinitionOfLabel, - mwUnknownSubstitution, - mwUnsupportedLanguage, - mwUnsupportedField + meCannotOpenFile = "cannot open '$1'", + meExpected = "'$1' expected", + meGridTableNotImplemented = "grid table is not implemented", + meMarkdownIllformedTable = "illformed delimiter row of a markdown table", + meNewSectionExpected = "new section expected", + meGeneralParseError = "general parse error", + meInvalidDirective = "invalid directive: '$1'", + mwRedefinitionOfLabel = "redefinition of label '$1'", + mwUnknownSubstitution = "unknown substitution '$1'", + mwUnsupportedLanguage = "language '$1' not supported", + mwUnsupportedField = "field '$1' not supported" MsgHandler* = proc (filename: string, line, col: int, msgKind: MsgKind, arg: string) {.closure, gcsafe.} ## what to do in case of an error FindFileHandler* = proc (filename: string): string {.closure, gcsafe.} -const - messages: array[MsgKind, string] = [ - meCannotOpenFile: "cannot open '$1'", - meExpected: "'$1' expected", - meGridTableNotImplemented: "grid table is not implemented", - meMarkdownIllformedTable: "illformed delimiter row of a markdown table", - meNewSectionExpected: "new section expected", - meGeneralParseError: "general parse error", - meInvalidDirective: "invalid directive: '$1'", - mwRedefinitionOfLabel: "redefinition of label '$1'", - mwUnknownSubstitution: "unknown substitution '$1'", - mwUnsupportedLanguage: "language '$1' not supported", - mwUnsupportedField: "field '$1' not supported" - ] - proc rstnodeToRefname*(n: PRstNode): string proc addNodes*(n: PRstNode): string proc getFieldValue*(n: PRstNode, fieldname: string): string @@ -136,10 +121,10 @@ proc getThing(L: var Lexer, tok: var Token, s: set[char]) = tok.col = L.col var pos = L.bufpos while true: - add(tok.symbol, L.buf[pos]) - inc(pos) + tok.symbol.add(L.buf[pos]) + inc pos if L.buf[pos] notin s: break - inc(L.col, pos - L.bufpos) + inc L.col, pos - L.bufpos L.bufpos = pos proc getAdornment(L: var Lexer, tok: var Token) = @@ -147,19 +132,19 @@ proc getAdornment(L: var Lexer, tok: var Token) = tok.line = L.line tok.col = L.col var pos = L.bufpos - var c = L.buf[pos] + let c = L.buf[pos] while true: - add(tok.symbol, L.buf[pos]) - inc(pos) + tok.symbol.add(L.buf[pos]) + inc pos if L.buf[pos] != c: break - inc(L.col, pos - L.bufpos) + inc L.col, pos - L.bufpos L.bufpos = pos proc getBracket(L: var Lexer, tok: var Token) = tok.kind = tkPunct tok.line = L.line tok.col = L.col - add(tok.symbol, L.buf[L.bufpos]) + tok.symbol.add(L.buf[L.bufpos]) inc L.col inc L.bufpos @@ -167,26 +152,26 @@ proc getIndentAux(L: var Lexer, start: int): int = var pos = start # skip the newline (but include it in the token!) if L.buf[pos] == '\x0D': - if L.buf[pos + 1] == '\x0A': inc(pos, 2) - else: inc(pos) + if L.buf[pos + 1] == '\x0A': inc pos, 2 + else: inc pos elif L.buf[pos] == '\x0A': - inc(pos) + inc pos if L.skipPounds: - if L.buf[pos] == '#': inc(pos) - if L.buf[pos] == '#': inc(pos) + if L.buf[pos] == '#': inc pos + if L.buf[pos] == '#': inc pos while true: case L.buf[pos] of ' ', '\x0B', '\x0C': - inc(pos) - inc(result) + inc pos + inc result of '\x09': - inc(pos) + inc pos result = result - (result mod 8) + 8 else: break # EndOfFile also leaves the loop if L.buf[pos] == '\0': result = 0 - elif (L.buf[pos] == '\x0A') or (L.buf[pos] == '\x0D'): + elif L.buf[pos] == '\x0A' or L.buf[pos] == '\x0D': # look at the next line for proper indentation: result = getIndentAux(L, pos) L.bufpos = pos # no need to set back buf @@ -219,7 +204,7 @@ proc rawGetTok(L: var Lexer, tok: var Token) = '/', ':', ';', '<', '=', '>', '?', '@', '\\', '^', '_', '`', '|', '~': getAdornment(L, tok) - if len(tok.symbol) <= 3: tok.kind = tkPunct + if tok.symbol.len <= 3: tok.kind = tkPunct of '(', ')', '[', ']', '{', '}': getBracket(L, tok) else: @@ -229,39 +214,39 @@ proc rawGetTok(L: var Lexer, tok: var Token) = tok.kind = tkEof else: tok.kind = tkOther - add(tok.symbol, c) - inc(L.bufpos) - inc(L.col) + tok.symbol.add(c) + inc L.bufpos + inc L.col tok.col = max(tok.col - L.baseIndent, 0) proc getTokens(buffer: string, skipPounds: bool, tokens: var TokenSeq): int = var L: Lexer - var length = len(tokens) + var length = tokens.len L.buf = cstring(buffer) L.line = 0 # skip UTF-8 BOM - if (L.buf[0] == '\xEF') and (L.buf[1] == '\xBB') and (L.buf[2] == '\xBF'): - inc(L.bufpos, 3) + if L.buf[0] == '\xEF' and L.buf[1] == '\xBB' and L.buf[2] == '\xBF': + inc L.bufpos, 3 L.skipPounds = skipPounds if skipPounds: if L.buf[L.bufpos] == '#': - inc(L.bufpos) - inc(result) + inc L.bufpos + inc result if L.buf[L.bufpos] == '#': - inc(L.bufpos) - inc(result) + inc L.bufpos + inc result L.baseIndent = 0 while L.buf[L.bufpos] == ' ': - inc(L.bufpos) - inc(L.baseIndent) - inc(result) + inc L.bufpos + inc L.baseIndent + inc result while true: - inc(length) + inc length setLen(tokens, length) rawGetTok(L, tokens[length - 1]) if tokens[length - 1].kind == tkEof: break if tokens[0].kind == tkWhite: # BUGFIX - tokens[0].ival = len(tokens[0].symbol) + tokens[0].ival = tokens[0].symbol.len tokens[0].kind = tkIndent type @@ -298,6 +283,10 @@ type EParseError* = object of ValueError +template currentTok(p: RstParser): Token = p.tok[p.idx] +template prevTok(p: RstParser): Token = p.tok[p.idx - 1] +template nextTok(p: RstParser): Token = p.tok[p.idx + 1] + proc whichMsgClass*(k: MsgKind): MsgClass = ## returns which message class `k` belongs to. case ($k)[1] @@ -309,7 +298,7 @@ proc whichMsgClass*(k: MsgKind): MsgClass = proc defaultMsgHandler*(filename: string, line, col: int, msgkind: MsgKind, arg: string) = let mc = msgkind.whichMsgClass - let a = messages[msgkind] % arg + let a = $msgkind % arg let message = "$1($2, $3) $4: $5" % [filename, $line, $col, $mc, a] if mc == mcError: raise newException(EParseError, message) else: writeLine(stdout, message) @@ -334,26 +323,26 @@ proc findRelativeFile(p: RstParser; filename: string): string = result = p.s.findFile(filename) proc rstMessage(p: RstParser, msgKind: MsgKind, arg: string) = - p.s.msgHandler(p.filename, p.line + p.tok[p.idx].line, - p.col + p.tok[p.idx].col, msgKind, arg) + p.s.msgHandler(p.filename, p.line + currentTok(p).line, + p.col + currentTok(p).col, msgKind, arg) proc rstMessage(p: RstParser, msgKind: MsgKind, arg: string, line, col: int) = p.s.msgHandler(p.filename, p.line + line, p.col + col, msgKind, arg) proc rstMessage(p: RstParser, msgKind: MsgKind) = - p.s.msgHandler(p.filename, p.line + p.tok[p.idx].line, - p.col + p.tok[p.idx].col, msgKind, - p.tok[p.idx].symbol) + p.s.msgHandler(p.filename, p.line + currentTok(p).line, + p.col + currentTok(p).col, msgKind, + currentTok(p).symbol) proc currInd(p: RstParser): int = result = p.indentStack[high(p.indentStack)] proc pushInd(p: var RstParser, ind: int) = - add(p.indentStack, ind) + p.indentStack.add(ind) proc popInd(p: var RstParser) = - if len(p.indentStack) > 1: setLen(p.indentStack, len(p.indentStack) - 1) + if p.indentStack.len > 1: setLen(p.indentStack, p.indentStack.len - 1) proc initParser(p: var RstParser, sharedState: PSharedState) = p.indentStack = @[0] @@ -367,41 +356,40 @@ proc initParser(p: var RstParser, sharedState: PSharedState) = proc addNodesAux(n: PRstNode, result: var string) = if n.kind == rnLeaf: - add(result, n.text) + result.add(n.text) else: - for i in countup(0, len(n) - 1): addNodesAux(n.sons[i], result) + for i in 0 ..< n.len: addNodesAux(n.sons[i], result) proc addNodes(n: PRstNode): string = - result = "" - addNodesAux(n, result) + n.addNodesAux(result) proc rstnodeToRefnameAux(n: PRstNode, r: var string, b: var bool) = template special(s) = if b: - add(r, '-') + r.add('-') b = false - add(r, s) + r.add(s) if n == nil: return if n.kind == rnLeaf: - for i in countup(0, len(n.text) - 1): + for i in 0 ..< n.text.len: case n.text[i] of '0'..'9': if b: - add(r, '-') + r.add('-') b = false - if len(r) == 0: add(r, 'Z') - add(r, n.text[i]) + if r.len == 0: r.add('Z') + r.add(n.text[i]) of 'a'..'z', '\128'..'\255': if b: - add(r, '-') + r.add('-') b = false - add(r, n.text[i]) + r.add(n.text[i]) of 'A'..'Z': if b: - add(r, '-') + r.add('-') b = false - add(r, chr(ord(n.text[i]) - ord('A') + ord('a'))) + r.add(chr(ord(n.text[i]) - ord('A') + ord('a'))) of '$': special "dollar" of '%': special "percent" of '&': special "amp" @@ -422,12 +410,11 @@ proc rstnodeToRefnameAux(n: PRstNode, r: var string, b: var bool) = of '@': special "at" of '|': special "bar" else: - if len(r) > 0: b = true + if r.len > 0: b = true else: - for i in countup(0, len(n) - 1): rstnodeToRefnameAux(n.sons[i], r, b) + for i in 0 ..< n.len: rstnodeToRefnameAux(n.sons[i], r, b) proc rstnodeToRefname(n: PRstNode): string = - result = "" var b = false rstnodeToRefnameAux(n, result, b) @@ -443,27 +430,22 @@ proc findSub(p: var RstParser, n: PRstNode): int = result = -1 proc setSub(p: var RstParser, key: string, value: PRstNode) = - var length = len(p.s.subs) - for i in countup(0, length - 1): + var length = p.s.subs.len + for i in 0 ..< length: if key == p.s.subs[i].key: p.s.subs[i].value = value return - setLen(p.s.subs, length + 1) - p.s.subs[length].key = key - p.s.subs[length].value = value + p.s.subs.add(Substitution(key: key, value: value)) proc setRef(p: var RstParser, key: string, value: PRstNode) = - var length = len(p.s.refs) - for i in countup(0, length - 1): + var length = p.s.refs.len + for i in 0 ..< length: if key == p.s.refs[i].key: if p.s.refs[i].value.addNodes != value.addNodes: rstMessage(p, mwRedefinitionOfLabel, key) - p.s.refs[i].value = value return - setLen(p.s.refs, length + 1) - p.s.refs[length].key = key - p.s.refs[length].value = value + p.s.refs.add(Substitution(key: key, value: value)) proc findRef(p: var RstParser, key: string): PRstNode = for i in countup(0, high(p.s.refs)): @@ -471,73 +453,73 @@ proc findRef(p: var RstParser, key: string): PRstNode = return p.s.refs[i].value proc newLeaf(p: var RstParser): PRstNode = - result = newRstNode(rnLeaf, p.tok[p.idx].symbol) + result = newRstNode(rnLeaf, currentTok(p).symbol) proc getReferenceName(p: var RstParser, endStr: string): PRstNode = var res = newRstNode(rnInner) while true: - case p.tok[p.idx].kind + case currentTok(p).kind of tkWord, tkOther, tkWhite: - add(res, newLeaf(p)) + res.add(newLeaf(p)) of tkPunct: - if p.tok[p.idx].symbol == endStr: - inc(p.idx) + if currentTok(p).symbol == endStr: + inc p.idx break else: - add(res, newLeaf(p)) + res.add(newLeaf(p)) else: rstMessage(p, meExpected, endStr) break - inc(p.idx) + inc p.idx result = res proc untilEol(p: var RstParser): PRstNode = result = newRstNode(rnInner) - while not (p.tok[p.idx].kind in {tkIndent, tkEof}): - add(result, newLeaf(p)) - inc(p.idx) + while currentTok(p).kind notin {tkIndent, tkEof}: + result.add(newLeaf(p)) + inc p.idx proc expect(p: var RstParser, tok: string) = - if p.tok[p.idx].symbol == tok: inc(p.idx) + if currentTok(p).symbol == tok: inc p.idx else: rstMessage(p, meExpected, tok) proc isInlineMarkupEnd(p: RstParser, markup: string): bool = - result = p.tok[p.idx].symbol == markup - if not result: - return # Rule 3: - result = not (p.tok[p.idx - 1].kind in {tkIndent, tkWhite}) - if not result: - return # Rule 4: - result = (p.tok[p.idx + 1].kind in {tkIndent, tkWhite, tkEof}) or - (markup in ["``", "`"] and p.tok[p.idx + 1].kind in {tkIndent, tkWhite, tkWord, tkEof}) or - (p.tok[p.idx + 1].symbol[0] in - {'\'', '\"', ')', ']', '}', '>', '-', '/', '\\', ':', '.', ',', ';', '!', - '?', '_'}) - if not result: - return # Rule 7: + # rst rules: https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#inline-markup-recognition-rules + result = currentTok(p).symbol == markup + if not result: return + # Rule 2: + result = prevTok(p).kind notin {tkIndent, tkWhite} + if not result: return + # Rule 7: + result = nextTok(p).kind in {tkIndent, tkWhite, tkEof} or + markup in ["``", "`"] and nextTok(p).kind in {tkIndent, tkWhite, tkWord, tkEof} or + nextTok(p).symbol[0] in + {'\'', '\"', ')', ']', '}', '>', '-', '/', '\\', ':', '.', ',', ';', '!', '?', '_'} + if not result: return + # Rule 4: if p.idx > 0: - if (markup != "``") and (p.tok[p.idx - 1].symbol == "\\"): + if markup != "``" and prevTok(p).symbol == "\\": result = false proc isInlineMarkupStart(p: RstParser, markup: string): bool = + # rst rules: https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#inline-markup-recognition-rules var d: char - result = p.tok[p.idx].symbol == markup - if not result: - return # Rule 1: - result = (p.idx == 0) or (p.tok[p.idx - 1].kind in {tkIndent, tkWhite}) or - (markup in ["``", "`"] and p.tok[p.idx - 1].kind in {tkIndent, tkWhite, tkWord}) or - (p.tok[p.idx - 1].symbol[0] in - {'\'', '\"', '(', '[', '{', '<', '-', '/', ':', '_'}) - if not result: - return # Rule 2: - result = not (p.tok[p.idx + 1].kind in {tkIndent, tkWhite, tkEof}) - if not result: - return # Rule 5 & 7: + result = currentTok(p).symbol == markup + if not result: return + # Rule 6: + result = p.idx == 0 or prevTok(p).kind in {tkIndent, tkWhite} or + (markup in ["``", "`"] and prevTok(p).kind in {tkIndent, tkWhite, tkWord}) or + prevTok(p).symbol[0] in {'\'', '\"', '(', '[', '{', '<', '-', '/', ':', '_'} + if not result: return + # Rule 1: + result = nextTok(p).kind notin {tkIndent, tkWhite, tkEof} + if not result: return + # Rules 4 & 5: if p.idx > 0: - if p.tok[p.idx - 1].symbol == "\\": + if prevTok(p).symbol == "\\": result = false else: - var c = p.tok[p.idx - 1].symbol[0] + var c = prevTok(p).symbol[0] case c of '\'', '\"': d = c of '(': d = ')' @@ -545,7 +527,7 @@ proc isInlineMarkupStart(p: RstParser, markup: string): bool = of '{': d = '}' of '<': d = '>' else: d = '\0' - if d != '\0': result = p.tok[p.idx + 1].symbol[0] != d + if d != '\0': result = nextTok(p).symbol[0] != d proc match(p: RstParser, start: int, expr: string): bool = # regular expressions are: @@ -560,7 +542,7 @@ proc match(p: RstParser, start: int, expr: string): bool = # 'e' tkWord or '#' (for enumeration lists) var i = 0 var j = start - var last = len(expr) - 1 + var last = expr.len - 1 while i <= last: case expr[i] of 'w': result = p.tok[j].kind == tkWord @@ -572,79 +554,79 @@ proc match(p: RstParser, start: int, expr: string): bool = of 'T': result = true of 'E': result = p.tok[j].kind in {tkEof, tkWhite, tkIndent} of 'e': - result = (p.tok[j].kind == tkWord) or (p.tok[j].symbol == "#") + result = p.tok[j].kind == tkWord or p.tok[j].symbol == "#" if result: case p.tok[j].symbol[0] - of 'a'..'z', 'A'..'Z', '#': result = len(p.tok[j].symbol) == 1 + of 'a'..'z', 'A'..'Z', '#': result = p.tok[j].symbol.len == 1 of '0'..'9': result = allCharsInSet(p.tok[j].symbol, {'0'..'9'}) else: result = false else: var c = expr[i] var length = 0 - while (i <= last) and (expr[i] == c): - inc(i) - inc(length) - dec(i) - result = (p.tok[j].kind in {tkPunct, tkAdornment}) and - (len(p.tok[j].symbol) == length) and (p.tok[j].symbol[0] == c) + while i <= last and expr[i] == c: + inc i + inc length + dec i + result = p.tok[j].kind in {tkPunct, tkAdornment} and + p.tok[j].symbol.len == length and p.tok[j].symbol[0] == c if not result: return - inc(j) - inc(i) + inc j + inc i result = true proc fixupEmbeddedRef(n, a, b: PRstNode) = var sep = - 1 - for i in countdown(len(n) - 2, 0): + for i in countdown(n.len - 2, 0): if n.sons[i].text == "<": sep = i break - var incr = if (sep > 0) and (n.sons[sep - 1].text[0] == ' '): 2 else: 1 - for i in countup(0, sep - incr): add(a, n.sons[i]) - for i in countup(sep + 1, len(n) - 2): add(b, n.sons[i]) + var incr = if sep > 0 and n.sons[sep - 1].text[0] == ' ': 2 else: 1 + for i in countup(0, sep - incr): a.add(n.sons[i]) + for i in countup(sep + 1, n.len - 2): b.add(n.sons[i]) proc parsePostfix(p: var RstParser, n: PRstNode): PRstNode = result = n if isInlineMarkupEnd(p, "_") or isInlineMarkupEnd(p, "__"): - inc(p.idx) + inc p.idx if p.tok[p.idx-2].symbol == "`" and p.tok[p.idx-3].symbol == ">": var a = newRstNode(rnInner) var b = newRstNode(rnInner) fixupEmbeddedRef(n, a, b) - if len(a) == 0: + if a.len == 0: result = newRstNode(rnStandaloneHyperlink) - add(result, b) + result.add(b) else: result = newRstNode(rnHyperlink) - add(result, a) - add(result, b) + result.add(a) + result.add(b) setRef(p, rstnodeToRefname(a), b) elif n.kind == rnInterpretedText: n.kind = rnRef else: result = newRstNode(rnRef) - add(result, n) + result.add(n) elif match(p, p.idx, ":w:"): # a role: - if p.tok[p.idx + 1].symbol == "idx": + if nextTok(p).symbol == "idx": n.kind = rnIdx - elif p.tok[p.idx + 1].symbol == "literal": + elif nextTok(p).symbol == "literal": n.kind = rnInlineLiteral - elif p.tok[p.idx + 1].symbol == "strong": + elif nextTok(p).symbol == "strong": n.kind = rnStrongEmphasis - elif p.tok[p.idx + 1].symbol == "emphasis": + elif nextTok(p).symbol == "emphasis": n.kind = rnEmphasis - elif (p.tok[p.idx + 1].symbol == "sub") or - (p.tok[p.idx + 1].symbol == "subscript"): + elif nextTok(p).symbol == "sub" or + nextTok(p).symbol == "subscript": n.kind = rnSub - elif (p.tok[p.idx + 1].symbol == "sup") or - (p.tok[p.idx + 1].symbol == "supscript"): + elif nextTok(p).symbol == "sup" or + nextTok(p).symbol == "supscript": n.kind = rnSup else: result = newRstNode(rnGeneralRole) n.kind = rnInner - add(result, n) - add(result, newRstNode(rnLeaf, p.tok[p.idx + 1].symbol)) - inc(p.idx, 3) + result.add(n) + result.add(newRstNode(rnLeaf, nextTok(p).symbol)) + inc p.idx, 3 proc matchVerbatim(p: RstParser, start: int, expr: string): int = result = start @@ -656,7 +638,7 @@ proc matchVerbatim(p: RstParser, start: int, expr: string): int = if j < expr.len: result = 0 proc parseSmiley(p: var RstParser): PRstNode = - if p.tok[p.idx].symbol[0] notin SmileyStartChars: return + if currentTok(p).symbol[0] notin SmileyStartChars: return for key, val in items(Smilies): let m = matchVerbatim(p, p.idx, key) if m > 0: @@ -665,135 +647,105 @@ proc parseSmiley(p: var RstParser): PRstNode = result.text = val return -when false: - const - urlChars = {'A'..'Z', 'a'..'z', '0'..'9', ':', '#', '@', '%', '/', ';', - '$', '(', ')', '~', '_', '?', '+', '-', '=', '\\', '.', '&', - '\128'..'\255'} - proc isUrl(p: RstParser, i: int): bool = - result = (p.tok[i+1].symbol == ":") and (p.tok[i+2].symbol == "//") and - (p.tok[i+3].kind == tkWord) and - (p.tok[i].symbol in ["http", "https", "ftp", "telnet", "file"]) + result = p.tok[i+1].symbol == ":" and p.tok[i+2].symbol == "//" and + p.tok[i+3].kind == tkWord and + p.tok[i].symbol in ["http", "https", "ftp", "telnet", "file"] proc parseUrl(p: var RstParser, father: PRstNode) = - #if p.tok[p.idx].symbol[strStart] == '<': + #if currentTok(p).symbol[strStart] == '<': if isUrl(p, p.idx): var n = newRstNode(rnStandaloneHyperlink) while true: - case p.tok[p.idx].kind + case currentTok(p).kind of tkWord, tkAdornment, tkOther: discard of tkPunct: - if p.tok[p.idx+1].kind notin {tkWord, tkAdornment, tkOther, tkPunct}: + if nextTok(p).kind notin {tkWord, tkAdornment, tkOther, tkPunct}: break else: break - add(n, newLeaf(p)) - inc(p.idx) - add(father, n) + n.add(newLeaf(p)) + inc p.idx + father.add(n) else: var n = newLeaf(p) - inc(p.idx) - if p.tok[p.idx].symbol == "_": n = parsePostfix(p, n) - add(father, n) + inc p.idx + if currentTok(p).symbol == "_": n = parsePostfix(p, n) + father.add(n) proc parseBackslash(p: var RstParser, father: PRstNode) = - assert(p.tok[p.idx].kind == tkPunct) - if p.tok[p.idx].symbol == "\\\\": - add(father, newRstNode(rnLeaf, "\\")) - inc(p.idx) - elif p.tok[p.idx].symbol == "\\": + assert(currentTok(p).kind == tkPunct) + if currentTok(p).symbol == "\\\\": + father.add(newRstNode(rnLeaf, "\\")) + inc p.idx + elif currentTok(p).symbol == "\\": # XXX: Unicode? - inc(p.idx) - if p.tok[p.idx].kind != tkWhite: add(father, newLeaf(p)) - if p.tok[p.idx].kind != tkEof: inc(p.idx) + inc p.idx + if currentTok(p).kind != tkWhite: father.add(newLeaf(p)) + if currentTok(p).kind != tkEof: inc p.idx else: - add(father, newLeaf(p)) - inc(p.idx) - -when false: - proc parseAdhoc(p: var RstParser, father: PRstNode, verbatim: bool) = - if not verbatim and isURL(p, p.idx): - var n = newRstNode(rnStandaloneHyperlink) - while true: - case p.tok[p.idx].kind - of tkWord, tkAdornment, tkOther: nil - of tkPunct: - if p.tok[p.idx+1].kind notin {tkWord, tkAdornment, tkOther, tkPunct}: - break - else: break - add(n, newLeaf(p)) - inc(p.idx) - add(father, n) - elif not verbatim and roSupportSmilies in p.sharedState.options: - let n = parseSmiley(p) - if s != nil: - add(father, n) - else: - var n = newLeaf(p) - inc(p.idx) - if p.tok[p.idx].symbol == "_": n = parsePostfix(p, n) - add(father, n) + father.add(newLeaf(p)) + inc p.idx proc parseUntil(p: var RstParser, father: PRstNode, postfix: string, interpretBackslash: bool) = let - line = p.tok[p.idx].line - col = p.tok[p.idx].col + line = currentTok(p).line + col = currentTok(p).col inc p.idx while true: - case p.tok[p.idx].kind + case currentTok(p).kind of tkPunct: if isInlineMarkupEnd(p, postfix): - inc(p.idx) + inc p.idx break elif interpretBackslash: parseBackslash(p, father) else: - add(father, newLeaf(p)) - inc(p.idx) + father.add(newLeaf(p)) + inc p.idx of tkAdornment, tkWord, tkOther: - add(father, newLeaf(p)) - inc(p.idx) + father.add(newLeaf(p)) + inc p.idx of tkIndent: - add(father, newRstNode(rnLeaf, " ")) - inc(p.idx) - if p.tok[p.idx].kind == tkIndent: + father.add(newRstNode(rnLeaf, " ")) + inc p.idx + if currentTok(p).kind == tkIndent: rstMessage(p, meExpected, postfix, line, col) break of tkWhite: - add(father, newRstNode(rnLeaf, " ")) - inc(p.idx) + father.add(newRstNode(rnLeaf, " ")) + inc p.idx else: rstMessage(p, meExpected, postfix, line, col) proc parseMarkdownCodeblock(p: var RstParser): PRstNode = var args = newRstNode(rnDirArg) - if p.tok[p.idx].kind == tkWord: - add(args, newLeaf(p)) - inc(p.idx) + if currentTok(p).kind == tkWord: + args.add(newLeaf(p)) + inc p.idx else: args = nil var n = newRstNode(rnLeaf, "") while true: - case p.tok[p.idx].kind + case currentTok(p).kind of tkEof: rstMessage(p, meExpected, "```") break of tkPunct: - if p.tok[p.idx].symbol == "```": - inc(p.idx) + if currentTok(p).symbol == "```": + inc p.idx break else: - add(n.text, p.tok[p.idx].symbol) - inc(p.idx) + n.text.add(currentTok(p).symbol) + inc p.idx else: - add(n.text, p.tok[p.idx].symbol) - inc(p.idx) + n.text.add(currentTok(p).symbol) + inc p.idx var lb = newRstNode(rnLiteralBlock) - add(lb, n) + lb.add(n) result = newRstNode(rnCodeBlock) - add(result, args) - add(result, PRstNode(nil)) - add(result, lb) + result.add(args) + result.add(PRstNode(nil)) + result.add(lb) proc parseMarkdownLink(p: var RstParser; father: PRstNode): bool = result = true @@ -821,78 +773,78 @@ proc parseMarkdownLink(p: var RstParser; father: PRstNode): bool = result = true proc parseInline(p: var RstParser, father: PRstNode) = - case p.tok[p.idx].kind + case currentTok(p).kind of tkPunct: if isInlineMarkupStart(p, "***"): var n = newRstNode(rnTripleEmphasis) parseUntil(p, n, "***", true) - add(father, n) + father.add(n) elif isInlineMarkupStart(p, "**"): var n = newRstNode(rnStrongEmphasis) parseUntil(p, n, "**", true) - add(father, n) + father.add(n) elif isInlineMarkupStart(p, "*"): var n = newRstNode(rnEmphasis) parseUntil(p, n, "*", true) - add(father, n) - elif roSupportMarkdown in p.s.options and p.tok[p.idx].symbol == "```": - inc(p.idx) - add(father, parseMarkdownCodeblock(p)) + father.add(n) + elif roSupportMarkdown in p.s.options and currentTok(p).symbol == "```": + inc p.idx + father.add(parseMarkdownCodeblock(p)) elif isInlineMarkupStart(p, "``"): var n = newRstNode(rnInlineLiteral) parseUntil(p, n, "``", false) - add(father, n) + father.add(n) elif isInlineMarkupStart(p, "`"): var n = newRstNode(rnInterpretedText) parseUntil(p, n, "`", true) n = parsePostfix(p, n) - add(father, n) + father.add(n) elif isInlineMarkupStart(p, "|"): var n = newRstNode(rnSubstitutionReferences) parseUntil(p, n, "|", false) - add(father, n) + father.add(n) elif roSupportMarkdown in p.s.options and - p.tok[p.idx].symbol == "[" and p.tok[p.idx+1].symbol != "[" and + currentTok(p).symbol == "[" and nextTok(p).symbol != "[" and parseMarkdownLink(p, father): discard "parseMarkdownLink already processed it" else: if roSupportSmilies in p.s.options: let n = parseSmiley(p) if n != nil: - add(father, n) + father.add(n) return parseBackslash(p, father) of tkWord: if roSupportSmilies in p.s.options: let n = parseSmiley(p) if n != nil: - add(father, n) + father.add(n) return parseUrl(p, father) of tkAdornment, tkOther, tkWhite: if roSupportSmilies in p.s.options: let n = parseSmiley(p) if n != nil: - add(father, n) + father.add(n) return - add(father, newLeaf(p)) - inc(p.idx) + father.add(newLeaf(p)) + inc p.idx else: discard proc getDirective(p: var RstParser): string = - if p.tok[p.idx].kind == tkWhite and p.tok[p.idx+1].kind == tkWord: + if currentTok(p).kind == tkWhite and nextTok(p).kind == tkWord: var j = p.idx - inc(p.idx) - result = p.tok[p.idx].symbol - inc(p.idx) - while p.tok[p.idx].kind in {tkWord, tkPunct, tkAdornment, tkOther}: - if p.tok[p.idx].symbol == "::": break - add(result, p.tok[p.idx].symbol) - inc(p.idx) - if p.tok[p.idx].kind == tkWhite: inc(p.idx) - if p.tok[p.idx].symbol == "::": - inc(p.idx) - if (p.tok[p.idx].kind == tkWhite): inc(p.idx) + inc p.idx + result = currentTok(p).symbol + inc p.idx + while currentTok(p).kind in {tkWord, tkPunct, tkAdornment, tkOther}: + if currentTok(p).symbol == "::": break + result.add(currentTok(p).symbol) + inc p.idx + if currentTok(p).kind == tkWhite: inc p.idx + if currentTok(p).symbol == "::": + inc p.idx + if currentTok(p).kind == tkWhite: inc p.idx else: p.idx = j # set back result = "" # error @@ -900,23 +852,23 @@ proc getDirective(p: var RstParser): string = result = "" proc parseComment(p: var RstParser): PRstNode = - case p.tok[p.idx].kind + case currentTok(p).kind of tkIndent, tkEof: - if p.tok[p.idx].kind != tkEof and p.tok[p.idx + 1].kind == tkIndent: - inc(p.idx) # empty comment + if currentTok(p).kind != tkEof and nextTok(p).kind == tkIndent: + inc p.idx # empty comment else: - var indent = p.tok[p.idx].ival + var indent = currentTok(p).ival while true: - case p.tok[p.idx].kind + case currentTok(p).kind of tkEof: break of tkIndent: - if (p.tok[p.idx].ival < indent): break + if currentTok(p).ival < indent: break else: discard - inc(p.idx) + inc p.idx else: - while p.tok[p.idx].kind notin {tkIndent, tkEof}: inc(p.idx) + while currentTok(p).kind notin {tkIndent, tkEof}: inc p.idx result = nil type @@ -936,13 +888,13 @@ proc getDirKind(s: string): DirKind = proc parseLine(p: var RstParser, father: PRstNode) = while true: - case p.tok[p.idx].kind + case currentTok(p).kind of tkWhite, tkWord, tkOther, tkPunct: parseInline(p, father) else: break proc parseUntilNewline(p: var RstParser, father: PRstNode) = while true: - case p.tok[p.idx].kind + case currentTok(p).kind of tkWhite, tkWord, tkAdornment, tkOther, tkPunct: parseInline(p, father) of tkEof, tkIndent: break @@ -952,19 +904,19 @@ proc parseField(p: var RstParser): PRstNode = ## ## rnField nodes have two children nodes, a rnFieldName and a rnFieldBody. result = newRstNode(rnField) - var col = p.tok[p.idx].col + var col = currentTok(p).col var fieldname = newRstNode(rnFieldName) parseUntil(p, fieldname, ":", false) var fieldbody = newRstNode(rnFieldBody) - if p.tok[p.idx].kind != tkIndent: parseLine(p, fieldbody) - if p.tok[p.idx].kind == tkIndent: - var indent = p.tok[p.idx].ival + if currentTok(p).kind != tkIndent: parseLine(p, fieldbody) + if currentTok(p).kind == tkIndent: + var indent = currentTok(p).ival if indent > col: pushInd(p, indent) parseSection(p, fieldbody) popInd(p) - add(result, fieldname) - add(result, fieldbody) + result.add(fieldname) + result.add(fieldbody) proc parseFields(p: var RstParser): PRstNode = ## Parses fields for a section or directive block. @@ -973,16 +925,16 @@ proc parseFields(p: var RstParser): PRstNode = ## otherwise it will return a node of rnFieldList type with children. result = nil var atStart = p.idx == 0 and p.tok[0].symbol == ":" - if (p.tok[p.idx].kind == tkIndent) and (p.tok[p.idx + 1].symbol == ":") or + if currentTok(p).kind == tkIndent and nextTok(p).symbol == ":" or atStart: - var col = if atStart: p.tok[p.idx].col else: p.tok[p.idx].ival + var col = if atStart: currentTok(p).col else: currentTok(p).ival result = newRstNode(rnFieldList) - if not atStart: inc(p.idx) + if not atStart: inc p.idx while true: - add(result, parseField(p)) - if (p.tok[p.idx].kind == tkIndent) and (p.tok[p.idx].ival == col) and - (p.tok[p.idx + 1].symbol == ":"): - inc(p.idx) + result.add(parseField(p)) + if currentTok(p).kind == tkIndent and currentTok(p).ival == col and + nextTok(p).symbol == ":": + inc p.idx else: break @@ -999,13 +951,12 @@ proc getFieldValue*(n: PRstNode): string = result = addNodes(n.sons[1]).strip proc getFieldValue(n: PRstNode, fieldname: string): string = - result = "" if n.sons[1] == nil: return - if (n.sons[1].kind != rnFieldList): + if n.sons[1].kind != rnFieldList: #InternalError("getFieldValue (2): " & $n.sons[1].kind) # We don't like internal errors here anymore as that would break the forum! return - for i in countup(0, len(n.sons[1]) - 1): + for i in 0 ..< n.sons[1].len: var f = n.sons[1].sons[i] if cmpIgnoreStyle(addNodes(f.sons[0]), fieldname) == 0: result = addNodes(f.sons[1]) @@ -1020,32 +971,32 @@ proc parseDotDot(p: var RstParser): PRstNode {.gcsafe.} proc parseLiteralBlock(p: var RstParser): PRstNode = result = newRstNode(rnLiteralBlock) var n = newRstNode(rnLeaf, "") - if p.tok[p.idx].kind == tkIndent: - var indent = p.tok[p.idx].ival - inc(p.idx) + if currentTok(p).kind == tkIndent: + var indent = currentTok(p).ival + inc p.idx while true: - case p.tok[p.idx].kind + case currentTok(p).kind of tkEof: break of tkIndent: - if (p.tok[p.idx].ival < indent): + if currentTok(p).ival < indent: break else: - add(n.text, "\n") - add(n.text, spaces(p.tok[p.idx].ival - indent)) - inc(p.idx) + n.text.add("\n") + n.text.add(spaces(currentTok(p).ival - indent)) + inc p.idx else: - add(n.text, p.tok[p.idx].symbol) - inc(p.idx) + n.text.add(currentTok(p).symbol) + inc p.idx else: - while not (p.tok[p.idx].kind in {tkIndent, tkEof}): - add(n.text, p.tok[p.idx].symbol) - inc(p.idx) - add(result, n) + while currentTok(p).kind notin {tkIndent, tkEof}: + n.text.add(currentTok(p).symbol) + inc p.idx + result.add(n) proc getLevel(map: var LevelMap, lvl: var int, c: char): int = if map[c] == 0: - inc(lvl) + inc lvl map[c] = lvl result = map[c] @@ -1056,26 +1007,26 @@ proc tokenAfterNewline(p: RstParser): int = of tkEof: break of tkIndent: - inc(result) + inc result break - else: inc(result) + else: inc result proc isLineBlock(p: RstParser): bool = var j = tokenAfterNewline(p) - result = (p.tok[p.idx].col == p.tok[j].col) and (p.tok[j].symbol == "|") or - (p.tok[j].col > p.tok[p.idx].col) + result = currentTok(p).col == p.tok[j].col and p.tok[j].symbol == "|" or + p.tok[j].col > currentTok(p).col proc predNL(p: RstParser): bool = result = true if p.idx > 0: - result = p.tok[p.idx-1].kind == tkIndent and - p.tok[p.idx-1].ival == currInd(p) + result = prevTok(p).kind == tkIndent and + prevTok(p).ival == currInd(p) proc isDefList(p: RstParser): bool = var j = tokenAfterNewline(p) - result = (p.tok[p.idx].col < p.tok[j].col) and - (p.tok[j].kind in {tkWord, tkOther, tkPunct}) and - (p.tok[j - 2].symbol != "::") + result = currentTok(p).col < p.tok[j].col and + p.tok[j].kind in {tkWord, tkOther, tkPunct} and + p.tok[j - 2].symbol != "::" proc isOptionList(p: RstParser): bool = result = match(p, p.idx, "-w") or match(p, p.idx, "--w") or @@ -1089,7 +1040,7 @@ proc isMarkdownHeadlinePattern(s: string): bool = proc isMarkdownHeadline(p: RstParser): bool = if roSupportMarkdown in p.s.options: - if isMarkdownHeadlinePattern(p.tok[p.idx].symbol) and p.tok[p.idx+1].kind == tkWhite: + if isMarkdownHeadlinePattern(currentTok(p).symbol) and nextTok(p).kind == tkWhite: if p.tok[p.idx+2].kind in {tkWord, tkOther, tkPunct}: result = true @@ -1101,7 +1052,7 @@ proc findPipe(p: RstParser, start: int): bool = inc i proc whichSection(p: RstParser): RstNodeKind = - case p.tok[p.idx].kind + case currentTok(p).kind of tkAdornment: if match(p, p.idx + 1, "ii"): result = rnTransition elif match(p, p.idx + 1, " a"): result = rnTable @@ -1116,22 +1067,21 @@ proc whichSection(p: RstParser): RstNodeKind = elif roSupportMarkdown in p.s.options and predNL(p) and match(p, p.idx, "| w") and findPipe(p, p.idx+3): result = rnMarkdownTable - elif p.tok[p.idx].symbol == "```": + elif currentTok(p).symbol == "```": result = rnCodeBlock elif match(p, tokenAfterNewline(p), "ai"): result = rnHeadline - elif p.tok[p.idx].symbol == "::": + elif currentTok(p).symbol == "::": result = rnLiteralBlock elif predNL(p) and - ((p.tok[p.idx].symbol == "+") or (p.tok[p.idx].symbol == "*") or - (p.tok[p.idx].symbol == "-")) and (p.tok[p.idx + 1].kind == tkWhite): + currentTok(p).symbol in ["+", "*", "-"] and nextTok(p).kind == tkWhite: result = rnBulletList - elif (p.tok[p.idx].symbol == "|") and isLineBlock(p): + elif currentTok(p).symbol == "|" and isLineBlock(p): result = rnLineBlock - elif (p.tok[p.idx].symbol == "..") and predNL(p): + elif currentTok(p).symbol == ".." and predNL(p): result = rnDirective elif match(p, p.idx, ":w:") and predNL(p): - # (p.tok[p.idx].symbol == ":") + # (currentTok(p).symbol == ":") result = rnFieldList elif match(p, p.idx, "(e) ") or match(p, p.idx, "e. "): result = rnEnumList @@ -1153,47 +1103,46 @@ proc whichSection(p: RstParser): RstNodeKind = proc parseLineBlock(p: var RstParser): PRstNode = result = nil - if p.tok[p.idx + 1].kind == tkWhite: - var col = p.tok[p.idx].col + if nextTok(p).kind == tkWhite: + var col = currentTok(p).col result = newRstNode(rnLineBlock) pushInd(p, p.tok[p.idx + 2].col) - inc(p.idx, 2) + inc p.idx, 2 while true: var item = newRstNode(rnLineBlockItem) parseSection(p, item) - add(result, item) - if (p.tok[p.idx].kind == tkIndent) and (p.tok[p.idx].ival == col) and - (p.tok[p.idx + 1].symbol == "|") and - (p.tok[p.idx + 2].kind == tkWhite): - inc(p.idx, 3) + result.add(item) + if currentTok(p).kind == tkIndent and currentTok(p).ival == col and + nextTok(p).symbol == "|" and p.tok[p.idx + 2].kind == tkWhite: + inc p.idx, 3 else: break popInd(p) proc parseParagraph(p: var RstParser, result: PRstNode) = while true: - case p.tok[p.idx].kind + case currentTok(p).kind of tkIndent: - if p.tok[p.idx + 1].kind == tkIndent: - inc(p.idx) + if nextTok(p).kind == tkIndent: + inc p.idx break - elif (p.tok[p.idx].ival == currInd(p)): - inc(p.idx) + elif currentTok(p).ival == currInd(p): + inc p.idx case whichSection(p) of rnParagraph, rnLeaf, rnHeadline, rnOverline, rnDirective: - add(result, newRstNode(rnLeaf, " ")) + result.add(newRstNode(rnLeaf, " ")) of rnLineBlock: - addIfNotNil(result, parseLineBlock(p)) + result.addIfNotNil(parseLineBlock(p)) else: break else: break of tkPunct: - if (p.tok[p.idx].symbol == "::") and - (p.tok[p.idx + 1].kind == tkIndent) and - (currInd(p) < p.tok[p.idx + 1].ival): - add(result, newRstNode(rnLeaf, ":")) - inc(p.idx) # skip '::' - add(result, parseLiteralBlock(p)) + if currentTok(p).symbol == "::" and + nextTok(p).kind == tkIndent and + currInd(p) < nextTok(p).ival: + result.add(newRstNode(rnLeaf, ":")) + inc p.idx # skip '::' + result.add(parseLiteralBlock(p)) break else: parseInline(p, result) @@ -1204,16 +1153,16 @@ proc parseParagraph(p: var RstParser, result: PRstNode) = proc parseHeadline(p: var RstParser): PRstNode = result = newRstNode(rnHeadline) if isMarkdownHeadline(p): - result.level = p.tok[p.idx].symbol.len - assert(p.tok[p.idx+1].kind == tkWhite) + result.level = currentTok(p).symbol.len + assert(nextTok(p).kind == tkWhite) inc p.idx, 2 parseUntilNewline(p, result) else: parseUntilNewline(p, result) - assert(p.tok[p.idx].kind == tkIndent) - assert(p.tok[p.idx + 1].kind == tkAdornment) - var c = p.tok[p.idx + 1].symbol[0] - inc(p.idx, 2) + assert(currentTok(p).kind == tkIndent) + assert(nextTok(p).kind == tkAdornment) + var c = nextTok(p).symbol[0] + inc p.idx, 2 result.level = getLevel(p.s.underlineToLevel, p.s.uLevel, c) type @@ -1223,20 +1172,20 @@ type ColSeq = seq[ColumnLimits] proc tokEnd(p: RstParser): int = - result = p.tok[p.idx].col + len(p.tok[p.idx].symbol) - 1 + result = currentTok(p).col + currentTok(p).symbol.len - 1 proc getColumns(p: var RstParser, cols: var IntSeq) = var L = 0 while true: - inc(L) + inc L setLen(cols, L) cols[L - 1] = tokEnd(p) - assert(p.tok[p.idx].kind == tkAdornment) - inc(p.idx) - if p.tok[p.idx].kind != tkWhite: break - inc(p.idx) - if p.tok[p.idx].kind != tkAdornment: break - if p.tok[p.idx].kind == tkIndent: inc(p.idx) + assert(currentTok(p).kind == tkAdornment) + inc p.idx + if currentTok(p).kind != tkWhite: break + inc p.idx + if currentTok(p).kind != tkAdornment: break + if currentTok(p).kind == tkIndent: inc p.idx # last column has no limit: cols[L - 1] = 32000 @@ -1254,35 +1203,35 @@ proc parseSimpleTable(p: var RstParser): PRstNode = cols = @[] row = @[] a = nil - c = p.tok[p.idx].symbol[0] + c = currentTok(p).symbol[0] while true: - if p.tok[p.idx].kind == tkAdornment: + if currentTok(p).kind == tkAdornment: last = tokenAfterNewline(p) if p.tok[last].kind in {tkEof, tkIndent}: # skip last adornment line: p.idx = last break getColumns(p, cols) - setLen(row, len(cols)) + setLen(row, cols.len) if a != nil: - for j in 0..len(a)-1: a.sons[j].kind = rnTableHeaderCell - if p.tok[p.idx].kind == tkEof: break + for j in 0 ..< a.len: a.sons[j].kind = rnTableHeaderCell + if currentTok(p).kind == tkEof: break for j in countup(0, high(row)): row[j] = "" # the following while loop iterates over the lines a single cell may span: - line = p.tok[p.idx].line + line = currentTok(p).line while true: i = 0 - while not (p.tok[p.idx].kind in {tkIndent, tkEof}): - if (tokEnd(p) <= cols[i]): - add(row[i], p.tok[p.idx].symbol) - inc(p.idx) + while currentTok(p).kind notin {tkIndent, tkEof}: + if tokEnd(p) <= cols[i]: + row[i].add(currentTok(p).symbol) + inc p.idx else: - if p.tok[p.idx].kind == tkWhite: inc(p.idx) - inc(i) - if p.tok[p.idx].kind == tkIndent: inc(p.idx) + if currentTok(p).kind == tkWhite: inc p.idx + inc i + if currentTok(p).kind == tkIndent: inc p.idx if tokEnd(p) <= cols[0]: break - if p.tok[p.idx].kind in {tkEof, tkAdornment}: break - for j in countup(1, high(row)): add(row[j], '\x0A') + if currentTok(p).kind in {tkEof, tkAdornment}: break + for j in countup(1, high(row)): row[j].add('\x0A') a = newRstNode(rnTableRow) for j in countup(0, high(row)): initParser(q, p.s) @@ -1291,21 +1240,21 @@ proc parseSimpleTable(p: var RstParser): PRstNode = q.filename = p.filename q.col += getTokens(row[j], false, q.tok) b = newRstNode(rnTableDataCell) - add(b, parseDoc(q)) - add(a, b) - add(result, a) + b.add(parseDoc(q)) + a.add(b) + result.add(a) proc readTableRow(p: var RstParser): ColSeq = - if p.tok[p.idx].symbol == "|": inc p.idx - while p.tok[p.idx].kind notin {tkIndent, tkEof}: + if currentTok(p).symbol == "|": inc p.idx + while currentTok(p).kind notin {tkIndent, tkEof}: var limits: ColumnLimits limits.first = p.idx - while p.tok[p.idx].kind notin {tkIndent, tkEof}: - if p.tok[p.idx].symbol == "|" and p.tok[p.idx-1].symbol != "\\": break + while currentTok(p).kind notin {tkIndent, tkEof}: + if currentTok(p).symbol == "|" and prevTok(p).symbol != "\\": break inc p.idx limits.last = p.idx result.add(limits) - if p.tok[p.idx].kind in {tkIndent, tkEof}: break + if currentTok(p).kind in {tkIndent, tkEof}: break inc p.idx p.idx = tokenAfterNewline(p) @@ -1340,7 +1289,7 @@ proc parseMarkdownTable(p: var RstParser): PRstNode = b = newRstNode(cellKind) initParser(q, p.s) q.col = p.col - q.line = p.tok[p.idx].line - 1 + q.line = currentTok(p).line - 1 q.filename = p.filename q.col += getTokens(getColContents(p, row[j]), false, q.tok) b.add(parseDoc(q)) @@ -1349,50 +1298,50 @@ proc parseMarkdownTable(p: var RstParser): PRstNode = parseRow(p, rnTableHeaderCell, result) if not isValidDelimiterRow(p, colNum): rstMessage(p, meMarkdownIllformedTable) - while predNL(p) and p.tok[p.idx].symbol == "|": + while predNL(p) and currentTok(p).symbol == "|": parseRow(p, rnTableDataCell, result) proc parseTransition(p: var RstParser): PRstNode = result = newRstNode(rnTransition) - inc(p.idx) - if p.tok[p.idx].kind == tkIndent: inc(p.idx) - if p.tok[p.idx].kind == tkIndent: inc(p.idx) + inc p.idx + if currentTok(p).kind == tkIndent: inc p.idx + if currentTok(p).kind == tkIndent: inc p.idx proc parseOverline(p: var RstParser): PRstNode = - var c = p.tok[p.idx].symbol[0] - inc(p.idx, 2) + var c = currentTok(p).symbol[0] + inc p.idx, 2 result = newRstNode(rnOverline) while true: parseUntilNewline(p, result) - if p.tok[p.idx].kind == tkIndent: - inc(p.idx) - if p.tok[p.idx - 1].ival > currInd(p): - add(result, newRstNode(rnLeaf, " ")) + if currentTok(p).kind == tkIndent: + inc p.idx + if prevTok(p).ival > currInd(p): + result.add(newRstNode(rnLeaf, " ")) else: break else: break result.level = getLevel(p.s.overlineToLevel, p.s.oLevel, c) - if p.tok[p.idx].kind == tkAdornment: - inc(p.idx) # XXX: check? - if p.tok[p.idx].kind == tkIndent: inc(p.idx) + if currentTok(p).kind == tkAdornment: + inc p.idx # XXX: check? + if currentTok(p).kind == tkIndent: inc p.idx proc parseBulletList(p: var RstParser): PRstNode = result = nil - if p.tok[p.idx + 1].kind == tkWhite: - var bullet = p.tok[p.idx].symbol - var col = p.tok[p.idx].col + if nextTok(p).kind == tkWhite: + var bullet = currentTok(p).symbol + var col = currentTok(p).col result = newRstNode(rnBulletList) pushInd(p, p.tok[p.idx + 2].col) - inc(p.idx, 2) + inc p.idx, 2 while true: var item = newRstNode(rnBulletItem) parseSection(p, item) - add(result, item) - if (p.tok[p.idx].kind == tkIndent) and (p.tok[p.idx].ival == col) and - (p.tok[p.idx + 1].symbol == bullet) and - (p.tok[p.idx + 2].kind == tkWhite): - inc(p.idx, 3) + result.add(item) + if currentTok(p).kind == tkIndent and currentTok(p).ival == col and + nextTok(p).symbol == bullet and + p.tok[p.idx + 2].kind == tkWhite: + inc p.idx, 3 else: break popInd(p) @@ -1404,63 +1353,62 @@ proc parseOptionList(p: var RstParser): PRstNode = var a = newRstNode(rnOptionGroup) var b = newRstNode(rnDescription) var c = newRstNode(rnOptionListItem) - if match(p, p.idx, "//w"): inc(p.idx) - while not (p.tok[p.idx].kind in {tkIndent, tkEof}): - if (p.tok[p.idx].kind == tkWhite) and (len(p.tok[p.idx].symbol) > 1): - inc(p.idx) + if match(p, p.idx, "//w"): inc p.idx + while currentTok(p).kind notin {tkIndent, tkEof}: + if currentTok(p).kind == tkWhite and currentTok(p).symbol.len > 1: + inc p.idx break - add(a, newLeaf(p)) - inc(p.idx) + a.add(newLeaf(p)) + inc p.idx var j = tokenAfterNewline(p) - if (j > 0) and (p.tok[j - 1].kind == tkIndent) and - (p.tok[j - 1].ival > currInd(p)): + if j > 0 and p.tok[j - 1].kind == tkIndent and p.tok[j - 1].ival > currInd(p): pushInd(p, p.tok[j - 1].ival) parseSection(p, b) popInd(p) else: parseLine(p, b) - if (p.tok[p.idx].kind == tkIndent): inc(p.idx) - add(c, a) - add(c, b) - add(result, c) + if currentTok(p).kind == tkIndent: inc p.idx + c.add(a) + c.add(b) + result.add(c) else: break proc parseDefinitionList(p: var RstParser): PRstNode = result = nil var j = tokenAfterNewline(p) - 1 - if (j >= 1) and (p.tok[j].kind == tkIndent) and - (p.tok[j].ival > currInd(p)) and (p.tok[j - 1].symbol != "::"): - var col = p.tok[p.idx].col + if j >= 1 and p.tok[j].kind == tkIndent and + p.tok[j].ival > currInd(p) and p.tok[j - 1].symbol != "::": + var col = currentTok(p).col result = newRstNode(rnDefList) while true: j = p.idx var a = newRstNode(rnDefName) parseLine(p, a) - if (p.tok[p.idx].kind == tkIndent) and - (p.tok[p.idx].ival > currInd(p)) and - (p.tok[p.idx + 1].symbol != "::") and - not (p.tok[p.idx + 1].kind in {tkIndent, tkEof}): - pushInd(p, p.tok[p.idx].ival) + if currentTok(p).kind == tkIndent and + currentTok(p).ival > currInd(p) and + nextTok(p).symbol != "::" and + nextTok(p).kind notin {tkIndent, tkEof}: + pushInd(p, currentTok(p).ival) var b = newRstNode(rnDefBody) parseSection(p, b) var c = newRstNode(rnDefItem) - add(c, a) - add(c, b) - add(result, c) + c.add(a) + c.add(b) + result.add(c) popInd(p) else: p.idx = j break - if (p.tok[p.idx].kind == tkIndent) and (p.tok[p.idx].ival == col): - inc(p.idx) + if currentTok(p).kind == tkIndent and currentTok(p).ival == col: + inc p.idx j = tokenAfterNewline(p) - 1 if j >= 1 and p.tok[j].kind == tkIndent and p.tok[j].ival > col and p.tok[j-1].symbol != "::" and p.tok[j+1].kind != tkIndent: discard else: break - if len(result) == 0: result = nil + if result.len == 0: result = nil proc parseEnumList(p: var RstParser): PRstNode = const @@ -1470,54 +1418,54 @@ proc parseEnumList(p: var RstParser): PRstNode = var w = 0 while w <= 2: if match(p, p.idx, wildcards[w]): break - inc(w) + inc w if w <= 2: - var col = p.tok[p.idx].col + var col = currentTok(p).col result = newRstNode(rnEnumList) - inc(p.idx, wildpos[w] + 3) + inc p.idx, wildpos[w] + 3 var j = tokenAfterNewline(p) - if (p.tok[j].col == p.tok[p.idx].col) or match(p, j, wildcards[w]): - pushInd(p, p.tok[p.idx].col) + if p.tok[j].col == currentTok(p).col or match(p, j, wildcards[w]): + pushInd(p, currentTok(p).col) while true: var item = newRstNode(rnEnumItem) parseSection(p, item) - add(result, item) - if (p.tok[p.idx].kind == tkIndent) and (p.tok[p.idx].ival == col) and + result.add(item) + if currentTok(p).kind == tkIndent and currentTok(p).ival == col and match(p, p.idx + 1, wildcards[w]): - inc(p.idx, wildpos[w] + 4) + inc p.idx, wildpos[w] + 4 else: break popInd(p) else: - dec(p.idx, wildpos[w] + 3) + dec p.idx, wildpos[w] + 3 result = nil proc sonKind(father: PRstNode, i: int): RstNodeKind = result = rnLeaf - if i < len(father): result = father.sons[i].kind + if i < father.len: result = father.sons[i].kind proc parseSection(p: var RstParser, result: PRstNode) = while true: var leave = false assert(p.idx >= 0) - while p.tok[p.idx].kind == tkIndent: - if currInd(p) == p.tok[p.idx].ival: - inc(p.idx) - elif p.tok[p.idx].ival > currInd(p): - pushInd(p, p.tok[p.idx].ival) + while currentTok(p).kind == tkIndent: + if currInd(p) == currentTok(p).ival: + inc p.idx + elif currentTok(p).ival > currInd(p): + pushInd(p, currentTok(p).ival) var a = newRstNode(rnBlockQuote) parseSection(p, a) - add(result, a) + result.add(a) popInd(p) else: leave = true break - if leave or p.tok[p.idx].kind == tkEof: break + if leave or currentTok(p).kind == tkEof: break var a: PRstNode = nil var k = whichSection(p) case k of rnLiteralBlock: - inc(p.idx) # skip '::' + inc p.idx # skip '::' a = parseLiteralBlock(p) of rnBulletList: a = parseBulletList(p) of rnLineBlock: a = parseLineBlock(p) @@ -1527,7 +1475,7 @@ proc parseSection(p: var RstParser, result: PRstNode) = of rnParagraph: discard of rnDefList: a = parseDefinitionList(p) of rnFieldList: - if p.idx > 0: dec(p.idx) + if p.idx > 0: dec p.idx a = parseFields(p) of rnTransition: a = parseTransition(p) of rnHeadline: a = parseHeadline(p) @@ -1541,14 +1489,14 @@ proc parseSection(p: var RstParser, result: PRstNode) = if a == nil and k != rnDirective: a = newRstNode(rnParagraph) parseParagraph(p, a) - addIfNotNil(result, a) + result.addIfNotNil(a) if sonKind(result, 0) == rnParagraph and sonKind(result, 1) != rnParagraph: result.sons[0].kind = rnInner proc parseSectionWrapper(p: var RstParser): PRstNode = result = newRstNode(rnInner) parseSection(p, result) - while (result.kind == rnInner) and (len(result) == 1): + while result.kind == rnInner and result.len == 1: result = result.sons[0] proc `$`(t: Token): string = @@ -1556,15 +1504,7 @@ proc `$`(t: Token): string = proc parseDoc(p: var RstParser): PRstNode = result = parseSectionWrapper(p) - if p.tok[p.idx].kind != tkEof: - when false: - assert isAllocatedPtr(cast[pointer](p.tok)) - for i in 0 .. high(p.tok): - assert isNil(p.tok[i].symbol) or - isAllocatedPtr(cast[pointer](p.tok[i].symbol)) - echo "index: ", p.idx, " length: ", high(p.tok), "##", - p.tok[p.idx-1], p.tok[p.idx], p.tok[p.idx+1] - #assert isAllocatedPtr(cast[pointer](p.indentStack)) + if currentTok(p).kind != tkEof: rstMessage(p, meGeneralParseError) type @@ -1590,29 +1530,29 @@ proc parseDirective(p: var RstParser, flags: DirFlags): PRstNode = args = newRstNode(rnDirArg) if argIsFile in flags: while true: - case p.tok[p.idx].kind + case currentTok(p).kind of tkWord, tkOther, tkPunct, tkAdornment: - add(args, newLeaf(p)) - inc(p.idx) + args.add(newLeaf(p)) + inc p.idx else: break elif argIsWord in flags: - while p.tok[p.idx].kind == tkWhite: inc(p.idx) - if p.tok[p.idx].kind == tkWord: - add(args, newLeaf(p)) - inc(p.idx) + while currentTok(p).kind == tkWhite: inc p.idx + if currentTok(p).kind == tkWord: + args.add(newLeaf(p)) + inc p.idx else: args = nil else: parseLine(p, args) - add(result, args) + result.add(args) if hasOptions in flags: - if (p.tok[p.idx].kind == tkIndent) and (p.tok[p.idx].ival >= 3) and - (p.tok[p.idx + 1].symbol == ":"): + if currentTok(p).kind == tkIndent and currentTok(p).ival >= 3 and + nextTok(p).symbol == ":": options = parseFields(p) - add(result, options) + result.add(options) proc indFollows(p: RstParser): bool = - result = p.tok[p.idx].kind == tkIndent and p.tok[p.idx].ival > currInd(p) + result = currentTok(p).kind == tkIndent and currentTok(p).ival > currInd(p) proc parseDirective(p: var RstParser, flags: DirFlags, contentParser: SectionParser): PRstNode = @@ -1621,16 +1561,16 @@ proc parseDirective(p: var RstParser, flags: DirFlags, ## The children are rnDirArg, rnFieldList and rnLineBlock. Any might be nil. result = parseDirective(p, flags) if not isNil(contentParser) and indFollows(p): - pushInd(p, p.tok[p.idx].ival) + pushInd(p, currentTok(p).ival) var content = contentParser(p) popInd(p) - add(result, content) + result.add(content) else: - add(result, PRstNode(nil)) + result.add(PRstNode(nil)) proc parseDirBody(p: var RstParser, contentParser: SectionParser): PRstNode = if indFollows(p): - pushInd(p, p.tok[p.idx].ival) + pushInd(p, currentTok(p).ival) result = contentParser(p) popInd(p) @@ -1666,7 +1606,7 @@ proc dirInclude(p: var RstParser): PRstNode = # XXX: error handling; recursive file inclusion! if getFieldValue(n, "literal") != "": result = newRstNode(rnLiteralBlock) - add(result, newRstNode(rnLeaf, readFile(path))) + result.add(newRstNode(rnLeaf, readFile(path))) else: let inputString = readFile(path).string() let startPosition = @@ -1674,7 +1614,7 @@ proc dirInclude(p: var RstParser): PRstNode = let searchFor = n.getFieldValue("start-after").strip() if searchFor != "": let pos = inputString.find(searchFor) - if pos != -1: pos + searchFor.len() + if pos != -1: pos + searchFor.len else: 0 else: 0 @@ -1724,7 +1664,7 @@ proc dirCodeBlock(p: var RstParser, nimExtension = false): PRstNode = var path = p.findRelativeFile(filename) if path == "": rstMessage(p, meCannotOpenFile, filename) var n = newRstNode(rnLiteralBlock) - add(n, newRstNode(rnLeaf, readFile(path))) + n.add(newRstNode(rnLeaf, readFile(path))) result.sons[2] = n # Extend the field block if we are using our custom extension. @@ -1745,7 +1685,7 @@ proc dirCodeBlock(p: var RstParser, nimExtension = false): PRstNode = proc dirContainer(p: var RstParser): PRstNode = result = parseDirective(p, {hasArg}, parseSectionWrapper) assert(result.kind == rnDirective) - assert(len(result) == 3) + assert(result.len == 3) result.kind = rnContainer proc dirImage(p: var RstParser): PRstNode = @@ -1779,10 +1719,10 @@ proc dirRawAux(p: var RstParser, result: var PRstNode, kind: RstNodeKind, else: var f = readFile(path) result = newRstNode(kind) - add(result, newRstNode(rnLeaf, f)) + result.add(newRstNode(rnLeaf, f)) else: result.kind = kind - add(result, parseDirBody(p, contentParser)) + result.add(parseDirBody(p, contentParser)) proc dirRaw(p: var RstParser): PRstNode = # @@ -1806,8 +1746,8 @@ proc dirRaw(p: var RstParser): PRstNode = proc parseDotDot(p: var RstParser): PRstNode = result = nil - var col = p.tok[p.idx].col - inc(p.idx) + var col = currentTok(p).col + inc p.idx var d = getDirective(p) if d != "": pushInd(p, col) @@ -1830,32 +1770,32 @@ proc parseDotDot(p: var RstParser): PRstNode = popInd(p) elif match(p, p.idx, " _"): # hyperlink target: - inc(p.idx, 2) + inc p.idx, 2 var a = getReferenceName(p, ":") - if p.tok[p.idx].kind == tkWhite: inc(p.idx) + if currentTok(p).kind == tkWhite: inc p.idx var b = untilEol(p) setRef(p, rstnodeToRefname(a), b) elif match(p, p.idx, " |"): # substitution definitions: - inc(p.idx, 2) + inc p.idx, 2 var a = getReferenceName(p, "|") var b: PRstNode - if p.tok[p.idx].kind == tkWhite: inc(p.idx) - if cmpIgnoreStyle(p.tok[p.idx].symbol, "replace") == 0: - inc(p.idx) + if currentTok(p).kind == tkWhite: inc p.idx + if cmpIgnoreStyle(currentTok(p).symbol, "replace") == 0: + inc p.idx expect(p, "::") b = untilEol(p) - elif cmpIgnoreStyle(p.tok[p.idx].symbol, "image") == 0: - inc(p.idx) + elif cmpIgnoreStyle(currentTok(p).symbol, "image") == 0: + inc p.idx b = dirImage(p) else: - rstMessage(p, meInvalidDirective, p.tok[p.idx].symbol) + rstMessage(p, meInvalidDirective, currentTok(p).symbol) setSub(p, addNodes(a), b) elif match(p, p.idx, " ["): # footnotes, citations - inc(p.idx, 2) + inc p.idx, 2 var a = getReferenceName(p, "]") - if p.tok[p.idx].kind == tkWhite: inc(p.idx) + if currentTok(p).kind == tkWhite: inc p.idx var b = untilEol(p) setRef(p, rstnodeToRefname(a), b) else: @@ -1879,14 +1819,14 @@ proc resolveSubs(p: var RstParser, n: PRstNode): PRstNode = if y != nil: result = newRstNode(rnHyperlink) n.kind = rnInner - add(result, n) - add(result, y) + result.add(n) + result.add(y) of rnLeaf: discard of rnContents: p.hasToc = true else: - for i in countup(0, len(n) - 1): n.sons[i] = resolveSubs(p, n.sons[i]) + for i in 0 ..< n.len: n.sons[i] = resolveSubs(p, n.sons[i]) proc rstParse*(text, filename: string, line, column: int, hasToc: var bool, From 7d51ad96e9add19c45b3c679f30fcb70d7fbe138 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Fri, 13 Nov 2020 18:57:56 +0800 Subject: [PATCH 0979/3768] close #2771(add testcase for #2771) (#15932) --- tests/iter/t2771.nim | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 tests/iter/t2771.nim diff --git a/tests/iter/t2771.nim b/tests/iter/t2771.nim new file mode 100644 index 000000000..49befb0a9 --- /dev/null +++ b/tests/iter/t2771.nim @@ -0,0 +1,21 @@ +template t1(i: int): int= + i+1 +template t2(i: int): int= + i+1 + +doAssert t1(10).t2() == 12 + + +template it1(i: int): iterator(): int = + iterator result(): int {.closure, gensym.} = + yield i+1 + result + +template it2(iter: iterator(): int): iterator(): int = + iterator result(): int {.closure, gensym.} = + yield iter()+1 + result + +let x2 = it1(10).it2() + +doAssert x2() == 12 From d802a4a669aa8661b4ba80abbd9310b93b9fe605 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Fri, 13 Nov 2020 19:49:30 +0800 Subject: [PATCH 0980/3768] close #13062(add testcase for #13062) (#15956) --- tests/ccgbugs/t13062.nim | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 tests/ccgbugs/t13062.nim diff --git a/tests/ccgbugs/t13062.nim b/tests/ccgbugs/t13062.nim new file mode 100644 index 000000000..fed32a1f7 --- /dev/null +++ b/tests/ccgbugs/t13062.nim @@ -0,0 +1,27 @@ +discard """ + output: "[p = nil]" + targets: "c cpp" +""" + +import atomics + +type + Pledge* {.exportc.} = object + p: PledgePtr + + PledgeKind {.exportc.} = enum + Single + Iteration + + PledgePtr {.exportc.} = ptr object + case kind: PledgeKind + of Single: + impl: PledgeImpl + of Iteration: + discard + + PledgeImpl {.exportc.} = object + fulfilled: Atomic[bool] + +var x: Pledge +echo x.repr From 8778d4a6f36fee5a4c31f6f8f355b8607f2a592c Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 13 Nov 2020 13:03:14 +0100 Subject: [PATCH 0981/3768] attempt to make asynchttpserver better; fixes #15925; [backport:1.0] --- changelog.md | 6 ++ lib/pure/asyncdispatch.nim | 22 ++++ lib/pure/asynchttpserver.nim | 125 +++++++++++++++------- lib/pure/ioselects/ioselectors_epoll.nim | 4 +- lib/pure/ioselects/ioselectors_kqueue.nim | 8 +- lib/pure/ioselects/ioselectors_poll.nim | 4 +- lib/pure/ioselects/ioselectors_select.nim | 4 +- 7 files changed, 123 insertions(+), 50 deletions(-) diff --git a/changelog.md b/changelog.md index 11930cf92..e50be6de3 100644 --- a/changelog.md +++ b/changelog.md @@ -35,6 +35,12 @@ - `doAssertRaises` now correctly handles foreign exceptions. +- Added `asyncdispatch.activeDescriptors` that returns the number of currently + active async event handles/file descriptors +- Added `asyncdispatch.maxDescriptors` that returns the maximum number of + active async event handles/file descriptors. + + ## Language changes - `nimscript` now handles `except Exception as e` diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index d8b274c34..0afc60864 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -1934,3 +1934,25 @@ proc waitFor*[T](fut: Future[T]): T = poll() fut.read + +proc activeDescriptors*(): int {.inline.} = + ## Returns the current number of active file descriptors for the current + ## event loop. This is a cheap operation that does not involve a system call. + when defined(windows): + result = getGlobalDispatcher().handles.len + else: + result = getGlobalDispatcher().selector.count + +when defined(posix): + import posix + +proc maxDescriptors*(): int {.raises: OSError.} = + ## Returns the maximum number of active file descriptors for the current + ## process. This involves a system call. + when defined(windows): + result = 16_700_000 + else: + var fdLim: RLimit + if getrlimit(RLIMIT_NOFILE, fdLim) < 0: + raiseOSError(osLastError()) + result = int(fdLim.rlim_cur) - 1 diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index ec7f2a0de..74b7d17b3 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -7,28 +7,44 @@ # distribution, for details about the copyright. # -## This module implements a high performance asynchronous HTTP server. -## -## This HTTP server has not been designed to be used in production, but -## for testing applications locally. Because of this, when deploying your -## application in production you should use a reverse proxy (for example nginx) -## instead of allowing users to connect directly to this server. -## -## Basic usage -## =========== -## -## This example will create an HTTP server on port 8080. The server will -## respond to all requests with a ``200 OK`` response code and "Hello World" -## as the response body. -## -## .. code-block::nim -## import asynchttpserver, asyncdispatch -## -## var server = newAsyncHttpServer() -## proc cb(req: Request) {.async.} = -## await req.respond(Http200, "Hello World") -## -## waitFor server.serve(Port(8080), cb) +##[ This module implements a high performance asynchronous HTTP server. + +This HTTP server has not been designed to be used in production, but +for testing applications locally. Because of this, when deploying your +application in production you should use a reverse proxy (for example nginx) +instead of allowing users to connect directly to this server. + +Basic usage +=========== + +This example will create an HTTP server on port 8080. The server will +respond to all requests with a ``200 OK`` response code and "Hello World" +as the response body. + +.. code-block::nim + import asynchttpserver, asyncdispatch + + proc main {.async.} = + var server = newAsyncHttpServer() + proc cb(req: Request) {.async.} = + #echo(req.reqMethod, " ", req.url) + #echo(req.headers) + let headers = {"Date": "Tue, 29 Apr 2014 23:40:08 GMT", + "Content-type": "text/plain; charset=utf-8"} + await req.respond(Http200, "Hello World", headers.newHttpHeaders()) + + server.listen Port(5555) + while true: + if server.shouldAcceptRequest(5): + var (address, client) = await server.socket.acceptAddr() + asyncCheck processClient(server, client, address, cb) + else: + poll() + + asyncCheck main() + runForever() + +]## import asyncnet, asyncdispatch, parseutils, uri, strutils import httpcore @@ -58,14 +74,12 @@ type reuseAddr: bool reusePort: bool maxBody: int ## The maximum content-length that will be read for the body. + maxFDs: int proc newAsyncHttpServer*(reuseAddr = true, reusePort = false, maxBody = 8388608): AsyncHttpServer = ## Creates a new ``AsyncHttpServer`` instance. - new result - result.reuseAddr = reuseAddr - result.reusePort = reusePort - result.maxBody = maxBody + result = AsyncHttpServer(reuseAddr: reuseAddr, reusePort: reusePort, maxBody: maxBody) proc addHeaders(msg: var string, headers: HttpHeaders) = for k, v in headers: @@ -279,7 +293,7 @@ proc processRequest( request.client.close() return false -proc processClient(server: AsyncHttpServer, client: AsyncSocket, address: string, +proc processClient*(server: AsyncHttpServer, client: AsyncSocket, address: string, callback: proc (request: Request): Future[void] {.closure, gcsafe.}) {.async.} = var request = newFutureVar[Request]("asynchttpserver.processClient") @@ -294,13 +308,8 @@ proc processClient(server: AsyncHttpServer, client: AsyncSocket, address: string ) if not retry: break -proc serve*(server: AsyncHttpServer, port: Port, - callback: proc (request: Request): Future[void] {.closure, gcsafe.}, - address = "") {.async.} = - ## Starts the process of listening for incoming HTTP connections on the - ## specified address and port. - ## - ## When a request is made by a client the specified callback will be called. +proc listen*(server: AsyncHttpServer; port: Port; address = "") = + server.maxFDs = maxDescriptors() server.socket = newAsyncSocket() if server.reuseAddr: server.socket.setSockOpt(OptReuseAddr, true) @@ -309,9 +318,38 @@ proc serve*(server: AsyncHttpServer, port: Port, server.socket.bindAddr(port, address) server.socket.listen() +proc shouldAcceptRequest*(server: AsyncHttpServer; + assumedDescriptorsPerRequest = 5): bool {.inline.} = + ## Returns true if the process's current number of opened file + ## descriptors is still within the maximum limit and so it's reasonable to + ## accept yet another request. + result = assumedDescriptorsPerRequest < 0 or + (activeDescriptors() + assumedDescriptorsPerRequest < server.maxFDs) + +proc acceptRequest*(server: AsyncHttpServer, port: Port, + callback: proc (request: Request): Future[void] {.closure, gcsafe.}) {.async.} = + ## Accepts a single request. + var (address, client) = await server.socket.acceptAddr() + asyncCheck processClient(server, client, address, callback) + +proc serve*(server: AsyncHttpServer, port: Port, + callback: proc (request: Request): Future[void] {.closure, gcsafe.}, + address = ""; + assumedDescriptorsPerRequest = 5) {.async.} = + ## Starts the process of listening for incoming HTTP connections on the + ## specified address and port. + ## + ## When a request is made by a client the specified callback will be called. + ## + ## If `flowControl` is true the server cares about the process's maximum + ## file descriptor limit. + listen server, port, address while true: - var (address, client) = await server.socket.acceptAddr() - asyncCheck processClient(server, client, address, callback) + if shouldAcceptRequest(server, assumedDescriptorsPerRequest): + var (address, client) = await server.socket.acceptAddr() + asyncCheck processClient(server, client, address, callback) + else: + poll() #echo(f.isNil) #echo(f.repr) @@ -320,7 +358,7 @@ proc close*(server: AsyncHttpServer) = server.socket.close() when not defined(testing) and isMainModule: - proc main = + proc main {.async.} = var server = newAsyncHttpServer() proc cb(req: Request) {.async.} = #echo(req.reqMethod, " ", req.url) @@ -329,6 +367,13 @@ when not defined(testing) and isMainModule: "Content-type": "text/plain; charset=utf-8"} await req.respond(Http200, "Hello World", headers.newHttpHeaders()) - asyncCheck server.serve(Port(5555), cb) - runForever() - main() + server.listen Port(5555) + while true: + if server.shouldAcceptRequest(5): + var (address, client) = await server.socket.acceptAddr() + asyncCheck processClient(server, client, address, cb) + else: + poll() + + asyncCheck main() + runForever() diff --git a/lib/pure/ioselects/ioselectors_epoll.nim b/lib/pure/ioselects/ioselectors_epoll.nim index 3dcf547bd..b62b4c2db 100644 --- a/lib/pure/ioselects/ioselectors_epoll.nim +++ b/lib/pure/ioselects/ioselectors_epoll.nim @@ -55,7 +55,7 @@ when hasThreadSupport: maxFD: int numFD: int fds: ptr SharedArray[SelectorKey[T]] - count: int + count*: int Selector*[T] = ptr SelectorImpl[T] else: type @@ -64,7 +64,7 @@ else: maxFD: int numFD: int fds: seq[SelectorKey[T]] - count: int + count*: int Selector*[T] = ref SelectorImpl[T] type SelectEventImpl = object diff --git a/lib/pure/ioselects/ioselectors_kqueue.nim b/lib/pure/ioselects/ioselectors_kqueue.nim index a65be9842..68be969c7 100644 --- a/lib/pure/ioselects/ioselectors_kqueue.nim +++ b/lib/pure/ioselects/ioselectors_kqueue.nim @@ -30,7 +30,7 @@ when defined(macosx) or defined(freebsd) or defined(dragonfly): proc sysctl(name: ptr cint, namelen: cuint, oldp: pointer, oldplen: ptr csize_t, newp: pointer, newplen: csize_t): cint {.importc: "sysctl",header: """#include - #include """} + #include """.} elif defined(netbsd) or defined(openbsd): # OpenBSD and NetBSD don't have KERN_MAXFILESPERPROC, so we are using # KERN_MAXFILES, because KERN_MAXFILES is always bigger, @@ -39,7 +39,7 @@ elif defined(netbsd) or defined(openbsd): proc sysctl(name: ptr cint, namelen: cuint, oldp: pointer, oldplen: ptr csize_t, newp: pointer, newplen: csize_t): cint {.importc: "sysctl",header: """#include - #include """} + #include """.} when hasThreadSupport: type @@ -48,7 +48,7 @@ when hasThreadSupport: maxFD: int changes: ptr SharedArray[KEvent] fds: ptr SharedArray[SelectorKey[T]] - count: int + count*: int changesLock: Lock changesSize: int changesLength: int @@ -61,7 +61,7 @@ else: maxFD: int changes: seq[KEvent] fds: seq[SelectorKey[T]] - count: int + count*: int sock: cint Selector*[T] = ref SelectorImpl[T] diff --git a/lib/pure/ioselects/ioselectors_poll.nim b/lib/pure/ioselects/ioselectors_poll.nim index 1af2a46db..00e2f3fe9 100644 --- a/lib/pure/ioselects/ioselectors_poll.nim +++ b/lib/pure/ioselects/ioselectors_poll.nim @@ -21,7 +21,7 @@ when hasThreadSupport: pollcnt: int fds: ptr SharedArray[SelectorKey[T]] pollfds: ptr SharedArray[TPollFd] - count: int + count*: int lock: Lock Selector*[T] = ptr SelectorImpl[T] else: @@ -31,7 +31,7 @@ else: pollcnt: int fds: seq[SelectorKey[T]] pollfds: seq[TPollFd] - count: int + count*: int Selector*[T] = ref SelectorImpl[T] type diff --git a/lib/pure/ioselects/ioselectors_select.nim b/lib/pure/ioselects/ioselectors_select.nim index eed64a34d..2fd9ac0ba 100644 --- a/lib/pure/ioselects/ioselectors_select.nim +++ b/lib/pure/ioselects/ioselectors_select.nim @@ -58,7 +58,7 @@ when hasThreadSupport: eSet: FdSet maxFD: int fds: ptr SharedArray[SelectorKey[T]] - count: int + count*: int lock: Lock Selector*[T] = ptr SelectorImpl[T] else: @@ -69,7 +69,7 @@ else: eSet: FdSet maxFD: int fds: seq[SelectorKey[T]] - count: int + count*: int Selector*[T] = ref SelectorImpl[T] type From cb19dc53ca49a237770271a3559772cc545e747f Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 13 Nov 2020 13:07:48 +0100 Subject: [PATCH 0982/3768] better documentation --- lib/pure/asynchttpserver.nim | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index 74b7d17b3..b69f3f9a4 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -293,7 +293,7 @@ proc processRequest( request.client.close() return false -proc processClient*(server: AsyncHttpServer, client: AsyncSocket, address: string, +proc processClient(server: AsyncHttpServer, client: AsyncSocket, address: string, callback: proc (request: Request): Future[void] {.closure, gcsafe.}) {.async.} = var request = newFutureVar[Request]("asynchttpserver.processClient") @@ -309,6 +309,7 @@ proc processClient*(server: AsyncHttpServer, client: AsyncSocket, address: strin if not retry: break proc listen*(server: AsyncHttpServer; port: Port; address = "") = + ## Listen to the given port and address. server.maxFDs = maxDescriptors() server.socket = newAsyncSocket() if server.reuseAddr: @@ -328,7 +329,8 @@ proc shouldAcceptRequest*(server: AsyncHttpServer; proc acceptRequest*(server: AsyncHttpServer, port: Port, callback: proc (request: Request): Future[void] {.closure, gcsafe.}) {.async.} = - ## Accepts a single request. + ## Accepts a single request. Write an explicit loop around this proc so that + ## errors can be handled properly. var (address, client) = await server.socket.acceptAddr() asyncCheck processClient(server, client, address, callback) @@ -341,8 +343,13 @@ proc serve*(server: AsyncHttpServer, port: Port, ## ## When a request is made by a client the specified callback will be called. ## - ## If `flowControl` is true the server cares about the process's maximum - ## file descriptor limit. + ## If `assumedDescriptorsPerRequest` is 0 or greater the server cares about + ## the process's maximum file descriptor limit. It then ensures that the + ## process still has the resources for `assumedDescriptorsPerRequest` + ## file descriptors before accepting a connection. + ## + ## You should prefer to call `acceptRequest` instead with a custom server + ## loop so that you're in control over the error handling and logging. listen server, port, address while true: if shouldAcceptRequest(server, assumedDescriptorsPerRequest): From 797cb2e67b084f28474cc5d4251e57275c1e8f5f Mon Sep 17 00:00:00 2001 From: Bung Date: Fri, 13 Nov 2020 20:44:48 +0800 Subject: [PATCH 0983/3768] Fix #8404 JS backend doesn't handle float->int type conversion (#15950) [backport] * Fix #8404 JS backend doesn't handle float->int type conversion * handle conv to uint as cast, discard other cases * limit to int32, times use int64 * toInt including tyInt64 break times timezones lib, ignore for now * also affect to vm * move to tests/misc/t8404.nim --- compiler/jsgen.nim | 12 +++++++++--- tests/misc/t8404.nim | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 3 deletions(-) create mode 100644 tests/misc/t8404.nim diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 357e18000..34ebd306a 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -2208,11 +2208,17 @@ proc genConv(p: PProc, n: PNode, r: var TCompRes) = if dest.kind == src.kind: # no-op conversion return - case dest.kind: - of tyBool: + let toInt = (dest.kind in tyInt..tyInt32) + let fromInt = (src.kind in tyInt..tyInt32) + let toUint = (dest.kind in tyUInt..tyUInt32) + let fromUint = (src.kind in tyUInt..tyUInt32) + if toUint and (fromInt or fromUint): + let trimmer = unsignedTrimmer(dest.size) + r.res = "($1 $2)" % [r.res, trimmer] + elif dest.kind == tyBool: r.res = "(!!($1))" % [r.res] r.kind = resExpr - of tyInt: + elif toInt: r.res = "(($1)|0)" % [r.res] else: # TODO: What types must we handle here? diff --git a/tests/misc/t8404.nim b/tests/misc/t8404.nim new file mode 100644 index 000000000..87991071c --- /dev/null +++ b/tests/misc/t8404.nim @@ -0,0 +1,33 @@ +discard """ + targets: "c cpp js" +""" +template main() = + block: # bug #8404 + # can conv + template float2int(T) = + var a = -1.0 + let b = T(a) + doAssert b < 0 + let c = b + 1 + doAssert c is T + doAssert c == 0 + + float2int(int8) + float2int(int16) + float2int(int32) + float2int(int64) + + block: + # can handle middle conv + # `/` can trigger int to float + template float2int(T) = + let n = T(1 / 256) + doAssert n == 0 + + float2int(int8) + float2int(int16) + float2int(int32) + # float2int(int64) +main() +static: + main() From 19d52033f885a686f39b3f1b52564246b0fade83 Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 13 Nov 2020 13:50:57 +0100 Subject: [PATCH 0984/3768] fixes 'nim doc' --- lib/pure/asyncdispatch.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 0afc60864..0bacf3bb5 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -1940,7 +1940,7 @@ proc activeDescriptors*(): int {.inline.} = ## event loop. This is a cheap operation that does not involve a system call. when defined(windows): result = getGlobalDispatcher().handles.len - else: + elif not defined(nimdoc): result = getGlobalDispatcher().selector.count when defined(posix): From a9bd4c4e80a73fde73ae4a976d1ee4d65a4a3a66 Mon Sep 17 00:00:00 2001 From: c-blake Date: Fri, 13 Nov 2020 08:04:40 -0500 Subject: [PATCH 0985/3768] Alternate to https://github.com/nim-lang/Nim/pull/15915 (#15937) * Alternate PR to https://github.com/nim-lang/Nim/pull/15915 to resolve the problem mentioned there (`hash() == 0`) as well as to close https://github.com/nim-lang/Nim/issues/15624 * Address https://github.com/nim-lang/Nim/pull/15937#discussion_r522759669 { though this was only a move from 2 copies to 3 copies. ;-) } --- lib/pure/hashes.nim | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim index 1024ce26f..3191d4030 100644 --- a/lib/pure/hashes.nim +++ b/lib/pure/hashes.nim @@ -113,8 +113,12 @@ proc hashWangYi1*(x: int64|uint64|Hash): Hash {.inline.} = const P0 = 0xa0761d6478bd642f'u64 const P1 = 0xe7037ed1a0b428db'u64 const P58 = 0xeb44accab455d165'u64 xor 8'u64 + template h(x): untyped = hiXorLo(hiXorLo(P0, uint64(x) xor P1), P58) when nimvm: - cast[Hash](hiXorLo(hiXorLo(P0, uint64(x) xor P1), P58)) + when defined(js): # Nim int64<->JS Number & VM match => JS gets 32-bit hash + result = cast[Hash](h(x)) and cast[Hash](0xFFFFFFFF) + else: + result = cast[Hash](h(x)) else: when defined(js): asm """ @@ -132,8 +136,9 @@ proc hashWangYi1*(x: int64|uint64|Hash): Hash {.inline.} = var res = hi_xor_lo_js(hi_xor_lo_js(P0, BigInt(`x`) ^ P1), P58); `result` = Number(res & ((BigInt(1) << BigInt(53)) - BigInt(1))); }""" + result = result and cast[Hash](0xFFFFFFFF) else: - cast[Hash](hiXorLo(hiXorLo(P0, uint64(x) xor P1), P58)) + result = cast[Hash](h(x)) proc hashData*(data: pointer, size: int): Hash = ## Hashes an array of bytes of size `size`. From c39fa0d49535da7f803d9972abdb15d09eed92a6 Mon Sep 17 00:00:00 2001 From: landerlo Date: Fri, 13 Nov 2020 13:12:51 +0000 Subject: [PATCH 0986/3768] Make IntSet a generic ordinal set OrdSet[A] (#15564) * Make IntSet an ordinal set OrdSet[A: Ordinal] Backward compatibility with IntSet is maintained. IntSet is an alias for OrdSet[int] * move ordsets to new file, intsets exports it * ordset, move to lib/std folder * Fix `$` for ordsets and test cleanup * Fix ordsets compilation in doc example * Rename ordsets to packedsets --- lib/pure/collections/intsets.nim | 694 +------------------------------ lib/std/packedsets.nim | 607 +++++++++++++++++++++++++++ tests/stdlib/tintsets.nim | 65 --- tests/stdlib/tpackedsets.nim | 259 ++++++++++++ 4 files changed, 873 insertions(+), 752 deletions(-) create mode 100644 lib/std/packedsets.nim delete mode 100644 tests/stdlib/tintsets.nim create mode 100644 tests/stdlib/tpackedsets.nim diff --git a/lib/pure/collections/intsets.nim b/lib/pure/collections/intsets.nim index 3be453e29..e4c216d08 100644 --- a/lib/pure/collections/intsets.nim +++ b/lib/pure/collections/intsets.nim @@ -7,698 +7,18 @@ # distribution, for details about the copyright. # -## The ``intsets`` module implements an efficient `int` set implemented as a -## `sparse bit set`:idx:. -## -## **Note**: Currently the assignment operator ``=`` for ``IntSet`` -## performs some rather meaningless shallow copy. Since Nim currently does -## not allow the assignment operator to be overloaded, use `assign proc -## <#assign,IntSet,IntSet>`_ to get a deep copy. -## +## Deprecated by the generic `PackedSet` for ordinal sparse sets. ## **See also:** -## * `sets module `_ for more general hash sets +## * `Ordinal packed sets module `_ for more general packed sets import std/private/since -import hashes +import std/packedsets +export packedsets type - BitScalar = uint + IntSet* = PackedSet[int] -const - InitIntSetSize = 8 # must be a power of two! - TrunkShift = 9 - BitsPerTrunk = 1 shl TrunkShift # needs to be a power of 2 and - # divisible by 64 - TrunkMask = BitsPerTrunk - 1 - IntsPerTrunk = BitsPerTrunk div (sizeof(BitScalar) * 8) - IntShift = 5 + ord(sizeof(BitScalar) == 8) # 5 or 6, depending on int width - IntMask = 1 shl IntShift - 1 +proc toIntSet*(x: openArray[int]): IntSet {.since: (1, 3), inline.} = toPackedSet[int](x) -type - PTrunk = ref Trunk - Trunk = object - next: PTrunk # all nodes are connected with this pointer - key: int # start address at bit 0 - bits: array[0..IntsPerTrunk - 1, BitScalar] # a bit vector +proc initIntSet*(): IntSet {.inline.} = initPackedSet[int]() - TrunkSeq = seq[PTrunk] - IntSet* = object ## An efficient set of `int` implemented as a sparse bit set. - elems: int # only valid for small numbers - counter, max: int - head: PTrunk - data: TrunkSeq - a: array[0..33, int] # profiling shows that 34 elements are enough - -proc mustRehash[T](t: T): bool {.inline.} = - let length = t.max + 1 - assert length > t.counter - result = (length * 2 < t.counter * 3) or (length - t.counter < 4) - -proc nextTry(h, maxHash: Hash, perturb: var Hash): Hash {.inline.} = - const PERTURB_SHIFT = 5 - var perturb2 = cast[uint](perturb) shr PERTURB_SHIFT - perturb = cast[Hash](perturb2) - result = ((5*h) + 1 + perturb) and maxHash - -proc intSetGet(t: IntSet, key: int): PTrunk = - var h = key and t.max - var perturb = key - while t.data[h] != nil: - if t.data[h].key == key: - return t.data[h] - h = nextTry(h, t.max, perturb) - result = nil - -proc intSetRawInsert(t: IntSet, data: var TrunkSeq, desc: PTrunk) = - var h = desc.key and t.max - var perturb = desc.key - while data[h] != nil: - assert(data[h] != desc) - h = nextTry(h, t.max, perturb) - assert(data[h] == nil) - data[h] = desc - -proc intSetEnlarge(t: var IntSet) = - var n: TrunkSeq - var oldMax = t.max - t.max = ((t.max + 1) * 2) - 1 - newSeq(n, t.max + 1) - for i in countup(0, oldMax): - if t.data[i] != nil: intSetRawInsert(t, n, t.data[i]) - swap(t.data, n) - -proc intSetPut(t: var IntSet, key: int): PTrunk = - var h = key and t.max - var perturb = key - while t.data[h] != nil: - if t.data[h].key == key: - return t.data[h] - h = nextTry(h, t.max, perturb) - if mustRehash(t): intSetEnlarge(t) - inc(t.counter) - h = key and t.max - perturb = key - while t.data[h] != nil: h = nextTry(h, t.max, perturb) - assert(t.data[h] == nil) - new(result) - result.next = t.head - result.key = key - t.head = result - t.data[h] = result - -proc bitincl(s: var IntSet, key: int) {.inline.} = - var ret: PTrunk - var t = intSetPut(s, `shr`(key, TrunkShift)) - var u = key and TrunkMask - t.bits[u shr IntShift] = t.bits[u shr IntShift] or - (BitScalar(1) shl (u and IntMask)) - -proc exclImpl(s: var IntSet, key: int) = - if s.elems <= s.a.len: - for i in 0.. 1: result.add(", ") - result.add($key) - result.add("}") - - -iterator items*(s: IntSet): int {.inline.} = - ## Iterates over any included element of `s`. - if s.elems <= s.a.len: - for i in 0..`_ - runnableExamples: - var a = initIntSet() - assert len(a) == 0 - - # newSeq(result.data, InitIntSetSize) - # result.max = InitIntSetSize-1 - result = IntSet( - elems: 0, - counter: 0, - max: 0, - head: nil, - data: when defined(nimNoNilSeqs): @[] else: nil) - # a: array[0..33, int] # profiling shows that 34 elements are enough - -proc contains*(s: IntSet, key: int): bool = - ## Returns true if `key` is in `s`. - ## - ## This allows the usage of `in` operator. - runnableExamples: - var a = initIntSet() - for x in [1, 3, 5]: - a.incl(x) - assert a.contains(3) - assert 3 in a - assert(not a.contains(8)) - assert 8 notin a - - if s.elems <= s.a.len: - for i in 0..`_ for excluding an element - ## * `incl proc <#incl,IntSet,IntSet>`_ for including other set - ## * `containsOrIncl proc <#containsOrIncl,IntSet,int>`_ - runnableExamples: - var a = initIntSet() - a.incl(3) - a.incl(3) - assert len(a) == 1 - - if s.elems <= s.a.len: - for i in 0..`_. - ## - ## See also: - ## * `excl proc <#excl,IntSet,IntSet>`_ for excluding other set - ## * `incl proc <#incl,IntSet,int>`_ for including an element - ## * `containsOrIncl proc <#containsOrIncl,IntSet,int>`_ - runnableExamples: - var - a = initIntSet() - b = initIntSet() - a.incl(1) - b.incl(5) - a.incl(b) - assert len(a) == 2 - assert 5 in a - - for item in other: incl(s, item) - -proc toIntSet*(x: openArray[int]): IntSet {.since: (1, 3).} = - ## Creates a new IntSet that contains the elements of `x`. - ## - ## Duplicates are removed. - ## - ## See also: - ## * `initIntSet proc <#initIntSet>`_ - runnableExamples: - var - a = toIntSet([5, 6, 7]) - b = toIntSet(@[1, 8, 8, 8]) - assert len(a) == 3 - assert len(b) == 2 - - result = initIntSet() - for item in items(x): - result.incl(item) - -proc containsOrIncl*(s: var IntSet, key: int): bool = - ## Includes `key` in the set `s` and tells if `key` was already in `s`. - ## - ## The difference with regards to the `incl proc <#incl,IntSet,int>`_ is - ## that this proc returns `true` if `s` already contained `key`. The - ## proc will return `false` if `key` was added as a new value to `s` during - ## this call. - ## - ## See also: - ## * `incl proc <#incl,IntSet,int>`_ for including an element - ## * `missingOrExcl proc <#missingOrExcl,IntSet,int>`_ - runnableExamples: - var a = initIntSet() - assert a.containsOrIncl(3) == false - assert a.containsOrIncl(3) == true - assert a.containsOrIncl(4) == false - - if s.elems <= s.a.len: - for i in 0..`_ for including an element - ## * `excl proc <#excl,IntSet,IntSet>`_ for excluding other set - ## * `missingOrExcl proc <#missingOrExcl,IntSet,int>`_ - runnableExamples: - var a = initIntSet() - a.incl(3) - a.excl(3) - a.excl(3) - a.excl(99) - assert len(a) == 0 - exclImpl(s, key) - -proc excl*(s: var IntSet, other: IntSet) = - ## Excludes all elements from `other` from `s`. - ## - ## This is the in-place version of `s - other <#-,IntSet,IntSet>`_. - ## - ## See also: - ## * `incl proc <#incl,IntSet,IntSet>`_ for including other set - ## * `excl proc <#excl,IntSet,int>`_ for excluding an element - ## * `missingOrExcl proc <#missingOrExcl,IntSet,int>`_ - runnableExamples: - var - a = initIntSet() - b = initIntSet() - a.incl(1) - a.incl(5) - b.incl(5) - a.excl(b) - assert len(a) == 1 - assert 5 notin a - - for item in other: excl(s, item) - -proc len*(s: IntSet): int {.inline.} = - ## Returns the number of elements in `s`. - if s.elems < s.a.len: - result = s.elems - else: - result = 0 - for _ in s: - inc(result) - -proc missingOrExcl*(s: var IntSet, key: int): bool = - ## Excludes `key` in the set `s` and tells if `key` was already missing from `s`. - ## - ## The difference with regards to the `excl proc <#excl,IntSet,int>`_ is - ## that this proc returns `true` if `key` was missing from `s`. - ## The proc will return `false` if `key` was in `s` and it was removed - ## during this call. - ## - ## See also: - ## * `excl proc <#excl,IntSet,int>`_ for excluding an element - ## * `excl proc <#excl,IntSet,IntSet>`_ for excluding other set - ## * `containsOrIncl proc <#containsOrIncl,IntSet,int>`_ - runnableExamples: - var a = initIntSet() - a.incl(5) - assert a.missingOrExcl(5) == false - assert a.missingOrExcl(5) == true - - var count = s.len - exclImpl(s, key) - result = count == s.len - -proc clear*(result: var IntSet) = - ## Clears the IntSet back to an empty state. - runnableExamples: - var a = initIntSet() - a.incl(5) - a.incl(7) - clear(a) - assert len(a) == 0 - - # setLen(result.data, InitIntSetSize) - # for i in 0..InitIntSetSize-1: result.data[i] = nil - # result.max = InitIntSetSize-1 - when defined(nimNoNilSeqs): - result.data = @[] - else: - result.data = nil - result.max = 0 - result.counter = 0 - result.head = nil - result.elems = 0 - -proc isNil*(x: IntSet): bool {.inline.} = x.head.isNil and x.elems == 0 - -proc assign*(dest: var IntSet, src: IntSet) = - ## Copies `src` to `dest`. - ## `dest` does not need to be initialized by `initIntSet proc <#initIntSet>`_. - runnableExamples: - var - a = initIntSet() - b = initIntSet() - b.incl(5) - b.incl(7) - a.assign(b) - assert len(a) == 2 - - if src.elems <= src.a.len: - when defined(nimNoNilSeqs): - dest.data = @[] - else: - dest.data = nil - dest.max = 0 - dest.counter = src.counter - dest.head = nil - dest.elems = src.elems - dest.a = src.a - else: - dest.counter = src.counter - dest.max = src.max - dest.elems = src.elems - newSeq(dest.data, src.data.len) - - var it = src.head - while it != nil: - var h = it.key and dest.max - var perturb = it.key - while dest.data[h] != nil: h = nextTry(h, dest.max, perturb) - assert(dest.data[h] == nil) - var n: PTrunk - new(n) - n.next = dest.head - n.key = it.key - n.bits = it.bits - dest.head = n - dest.data[h] = n - it = it.next - -proc union*(s1, s2: IntSet): IntSet = - ## Returns the union of the sets `s1` and `s2`. - ## - ## The same as `s1 + s2 <#+,IntSet,IntSet>`_. - runnableExamples: - var - a = initIntSet() - b = initIntSet() - a.incl(1); a.incl(2); a.incl(3) - b.incl(3); b.incl(4); b.incl(5) - assert union(a, b).len == 5 - ## {1, 2, 3, 4, 5} - - result.assign(s1) - incl(result, s2) - -proc intersection*(s1, s2: IntSet): IntSet = - ## Returns the intersection of the sets `s1` and `s2`. - ## - ## The same as `s1 * s2 <#*,IntSet,IntSet>`_. - runnableExamples: - var - a = initIntSet() - b = initIntSet() - a.incl(1); a.incl(2); a.incl(3) - b.incl(3); b.incl(4); b.incl(5) - assert intersection(a, b).len == 1 - ## {3} - - result = initIntSet() - for item in s1: - if contains(s2, item): - incl(result, item) - -proc difference*(s1, s2: IntSet): IntSet = - ## Returns the difference of the sets `s1` and `s2`. - ## - ## The same as `s1 - s2 <#-,IntSet,IntSet>`_. - runnableExamples: - var - a = initIntSet() - b = initIntSet() - a.incl(1); a.incl(2); a.incl(3) - b.incl(3); b.incl(4); b.incl(5) - assert difference(a, b).len == 2 - ## {1, 2} - - result = initIntSet() - for item in s1: - if not contains(s2, item): - incl(result, item) - -proc symmetricDifference*(s1, s2: IntSet): IntSet = - ## Returns the symmetric difference of the sets `s1` and `s2`. - runnableExamples: - var - a = initIntSet() - b = initIntSet() - a.incl(1); a.incl(2); a.incl(3) - b.incl(3); b.incl(4); b.incl(5) - assert symmetricDifference(a, b).len == 4 - ## {1, 2, 4, 5} - - result.assign(s1) - for item in s2: - if containsOrIncl(result, item): excl(result, item) - -proc `+`*(s1, s2: IntSet): IntSet {.inline.} = - ## Alias for `union(s1, s2) <#union,IntSet,IntSet>`_. - result = union(s1, s2) - -proc `*`*(s1, s2: IntSet): IntSet {.inline.} = - ## Alias for `intersection(s1, s2) <#intersection,IntSet,IntSet>`_. - result = intersection(s1, s2) - -proc `-`*(s1, s2: IntSet): IntSet {.inline.} = - ## Alias for `difference(s1, s2) <#difference,IntSet,IntSet>`_. - result = difference(s1, s2) - -proc disjoint*(s1, s2: IntSet): bool = - ## Returns true if the sets `s1` and `s2` have no items in common. - runnableExamples: - var - a = initIntSet() - b = initIntSet() - a.incl(1); a.incl(2) - b.incl(2); b.incl(3) - assert disjoint(a, b) == false - b.excl(2) - assert disjoint(a, b) == true - - for item in s1: - if contains(s2, item): - return false - return true - -proc card*(s: IntSet): int {.inline.} = - ## Alias for `len() <#len,IntSet>`_. - result = s.len() - -proc `<=`*(s1, s2: IntSet): bool = - ## Returns true if `s1` is subset of `s2`. - ## - ## A subset `s1` has all of its elements in `s2`, and `s2` doesn't necessarily - ## have more elements than `s1`. That is, `s1` can be equal to `s2`. - runnableExamples: - var - a = initIntSet() - b = initIntSet() - a.incl(1) - b.incl(1); b.incl(2) - assert a <= b - a.incl(2) - assert a <= b - a.incl(3) - assert(not (a <= b)) - - for item in s1: - if not s2.contains(item): - return false - return true - -proc `<`*(s1, s2: IntSet): bool = - ## Returns true if `s1` is proper subset of `s2`. - ## - ## A strict or proper subset `s1` has all of its elements in `s2`, but `s2` has - ## more elements than `s1`. - runnableExamples: - var - a = initIntSet() - b = initIntSet() - a.incl(1) - b.incl(1); b.incl(2) - assert a < b - a.incl(2) - assert(not (a < b)) - return s1 <= s2 and not (s2 <= s1) - -proc `==`*(s1, s2: IntSet): bool = - ## Returns true if both `s1` and `s2` have the same elements and set size. - return s1 <= s2 and s2 <= s1 - -proc `$`*(s: IntSet): string = - ## The `$` operator for int sets. - ## - ## Converts the set `s` to a string, mostly for logging and printing purposes. - dollarImpl() - - - -when isMainModule: - import sequtils, algorithm - - var x = initIntSet() - x.incl(1) - x.incl(2) - x.incl(7) - x.incl(1056) - - x.incl(1044) - x.excl(1044) - - assert x == [1, 2, 7, 1056].toIntSet - - assert x.containsOrIncl(888) == false - assert 888 in x - assert x.containsOrIncl(888) == true - - assert x.missingOrExcl(888) == false - assert 888 notin x - assert x.missingOrExcl(888) == true - - var xs = toSeq(items(x)) - xs.sort(cmp[int]) - assert xs == @[1, 2, 7, 1056] - - var y: IntSet - assign(y, x) - var ys = toSeq(items(y)) - ys.sort(cmp[int]) - assert ys == @[1, 2, 7, 1056] - - assert x == y - - var z: IntSet - for i in 0..1000: - incl z, i - assert z.len() == i+1 - for i in 0..1000: - assert z.contains(i) - - var w = initIntSet() - w.incl(1) - w.incl(4) - w.incl(50) - w.incl(1001) - w.incl(1056) - - var xuw = x.union(w) - var xuws = toSeq(items(xuw)) - xuws.sort(cmp[int]) - assert xuws == @[1, 2, 4, 7, 50, 1001, 1056] - - var xiw = x.intersection(w) - var xiws = toSeq(items(xiw)) - xiws.sort(cmp[int]) - assert xiws == @[1, 1056] - - var xdw = x.difference(w) - var xdws = toSeq(items(xdw)) - xdws.sort(cmp[int]) - assert xdws == @[2, 7] - - var xsw = x.symmetricDifference(w) - var xsws = toSeq(items(xsw)) - xsws.sort(cmp[int]) - assert xsws == @[2, 4, 7, 50, 1001] - - x.incl(w) - xs = toSeq(items(x)) - xs.sort(cmp[int]) - assert xs == @[1, 2, 4, 7, 50, 1001, 1056] - - assert w <= x - - assert w < x - - assert(not disjoint(w, x)) - - var u = initIntSet() - u.incl(3) - u.incl(5) - u.incl(500) - assert disjoint(u, x) - - var v = initIntSet() - v.incl(2) - v.incl(50) - - x.excl(v) - xs = toSeq(items(x)) - xs.sort(cmp[int]) - assert xs == @[1, 4, 7, 1001, 1056] - - proc bug12366 = - var - x = initIntSet() - y = initIntSet() - n = 3584 - - for i in 0..n: - x.incl(i) - y.incl(i) - - let z = symmetricDifference(x, y) - doAssert z.len == 0 - doAssert $z == "{}" - - bug12366() diff --git a/lib/std/packedsets.nim b/lib/std/packedsets.nim new file mode 100644 index 000000000..20002d07f --- /dev/null +++ b/lib/std/packedsets.nim @@ -0,0 +1,607 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2012 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## The ``packedsets`` module implements an efficient `Ordinal`set implemented as a +## `sparse bit set`:idx:. +## +## Supports any Ordinal type. +## +## **Note**: Currently the assignment operator ``=`` for ``PackedSet[A]`` +## performs some rather meaningless shallow copy. Since Nim currently does +## not allow the assignment operator to be overloaded, use `assign proc +## <#assign,PackedSet[A],PackedSet[A]>`_ to get a deep copy. +## +## **See also:** +## * `sets module `_ for more general hash sets + +import std/private/since +import hashes + +type + BitScalar = uint + +const + InitIntSetSize = 8 # must be a power of two! + TrunkShift = 9 + BitsPerTrunk = 1 shl TrunkShift # needs to be a power of 2 and + # divisible by 64 + TrunkMask = BitsPerTrunk - 1 + IntsPerTrunk = BitsPerTrunk div (sizeof(BitScalar) * 8) + IntShift = 5 + ord(sizeof(BitScalar) == 8) # 5 or 6, depending on int width + IntMask = 1 shl IntShift - 1 + +type + PTrunk = ref Trunk + Trunk = object + next: PTrunk # all nodes are connected with this pointer + key: int # start address at bit 0 + bits: array[0..IntsPerTrunk - 1, BitScalar] # a bit vector + + TrunkSeq = seq[PTrunk] + + ## An efficient set of `Ordinal` types implemented as a sparse bit set. + PackedSet*[A: Ordinal] = object + elems: int # only valid for small numbers + counter, max: int + head: PTrunk + data: TrunkSeq + a: array[0..33, int] # profiling shows that 34 elements are enough + +proc mustRehash[T](t: T): bool {.inline.} = + let length = t.max + 1 + assert length > t.counter + result = (length * 2 < t.counter * 3) or (length - t.counter < 4) + +proc nextTry(h, maxHash: Hash, perturb: var Hash): Hash {.inline.} = + const PERTURB_SHIFT = 5 + var perturb2 = cast[uint](perturb) shr PERTURB_SHIFT + perturb = cast[Hash](perturb2) + result = ((5*h) + 1 + perturb) and maxHash + +proc packedSetGet[A](t: PackedSet[A], key: int): PTrunk = + var h = key and t.max + var perturb = key + while t.data[h] != nil: + if t.data[h].key == key: + return t.data[h] + h = nextTry(h, t.max, perturb) + result = nil + +proc intSetRawInsert[A](t: PackedSet[A], data: var TrunkSeq, desc: PTrunk) = + var h = desc.key and t.max + var perturb = desc.key + while data[h] != nil: + assert(data[h] != desc) + h = nextTry(h, t.max, perturb) + assert(data[h] == nil) + data[h] = desc + +proc intSetEnlarge[A](t: var PackedSet[A]) = + var n: TrunkSeq + var oldMax = t.max + t.max = ((t.max + 1) * 2) - 1 + newSeq(n, t.max + 1) + for i in countup(0, oldMax): + if t.data[i] != nil: intSetRawInsert(t, n, t.data[i]) + swap(t.data, n) + +proc intSetPut[A](t: var PackedSet[A], key: int): PTrunk = + var h = key and t.max + var perturb = key + while t.data[h] != nil: + if t.data[h].key == key: + return t.data[h] + h = nextTry(h, t.max, perturb) + if mustRehash(t): intSetEnlarge(t) + inc(t.counter) + h = key and t.max + perturb = key + while t.data[h] != nil: h = nextTry(h, t.max, perturb) + assert(t.data[h] == nil) + new(result) + result.next = t.head + result.key = key + t.head = result + t.data[h] = result + +proc bitincl[A](s: var PackedSet[A], key: int) {.inline.} = + var ret: PTrunk + var t = intSetPut(s, `shr`(key, TrunkShift)) + var u = key and TrunkMask + t.bits[u shr IntShift] = t.bits[u shr IntShift] or + (BitScalar(1) shl (u and IntMask)) + +proc exclImpl[A](s: var PackedSet[A], key: int) = + if s.elems <= s.a.len: + for i in 0.. 1: result.add(", ") + result.add $key + result.add("}") + +iterator items*[A](s: PackedSet[A]): A {.inline.} = + ## Iterates over any included element of `s`. + if s.elems <= s.a.len: + for i in 0..`_ + runnableExamples: + var a = initPackedSet[int]() + assert len(a) == 0 + + type Id = distinct int + var ids = initPackedSet[Id]() + ids.incl(3.Id) + + result = PackedSet[A]( + elems: 0, + counter: 0, + max: 0, + head: nil, + data: when defined(nimNoNilSeqs): @[] else: nil) + # a: array[0..33, int] # profiling shows that 34 elements are enough + +proc contains*[A](s: PackedSet[A], key: A): bool = + ## Returns true if `key` is in `s`. + ## + ## This allows the usage of `in` operator. + runnableExamples: + type ABCD = enum A, B, C, D + + var a = initPackedSet[int]() + for x in [1, 3, 5]: + a.incl(x) + assert a.contains(3) + assert 3 in a + assert(not a.contains(8)) + assert 8 notin a + + var letters = initPackedSet[ABCD]() + for x in [A, C]: + letters.incl(x) + assert A in letters + assert C in letters + assert B notin letters + + if s.elems <= s.a.len: + for i in 0..`_ for excluding an element + ## * `incl proc <#incl,PackedSet[A],PackedSet[A]>`_ for including other set + ## * `containsOrIncl proc <#containsOrIncl,PackedSet[A],A>`_ + runnableExamples: + var a = initPackedSet[int]() + a.incl(3) + a.incl(3) + assert len(a) == 1 + + if s.elems <= s.a.len: + for i in 0..`_. + ## + ## See also: + ## * `excl proc <#excl,PackedSet[A],PackedSet[A]>`_ for excluding other set + ## * `incl proc <#incl,PackedSet[A],A>`_ for including an element + ## * `containsOrIncl proc <#containsOrIncl,PackedSet[A],A>`_ + runnableExamples: + var + a = initPackedSet[int]() + b = initPackedSet[int]() + a.incl(1) + b.incl(5) + a.incl(b) + assert len(a) == 2 + assert 5 in a + + for item in other: incl(s, item) + +proc toPackedSet*[A](x: openArray[A]): PackedSet[A] {.since: (1, 3).} = + ## Creates a new PackedSet[A] that contains the elements of `x`. + ## + ## Duplicates are removed. + ## + ## See also: + ## * `initPackedSet[A] proc <#initPackedSet[A]>`_ + runnableExamples: + var + a = toPackedSet([5, 6, 7]) + b = toPackedSet(@[1, 8, 8, 8]) + assert len(a) == 3 + assert len(b) == 2 + + result = initPackedSet[A]() + for item in x: + result.incl(item) + +proc containsOrIncl*[A](s: var PackedSet[A], key: A): bool = + ## Includes `key` in the set `s` and tells if `key` was already in `s`. + ## + ## The difference with regards to the `incl proc <#incl,PackedSet[A],A>`_ is + ## that this proc returns `true` if `s` already contained `key`. The + ## proc will return `false` if `key` was added as a new value to `s` during + ## this call. + ## + ## See also: + ## * `incl proc <#incl,PackedSet[A],A>`_ for including an element + ## * `missingOrExcl proc <#missingOrExcl,PackedSet[A],A>`_ + runnableExamples: + var a = initPackedSet[int]() + assert a.containsOrIncl(3) == false + assert a.containsOrIncl(3) == true + assert a.containsOrIncl(4) == false + + if s.elems <= s.a.len: + for i in 0..`_ for including an element + ## * `excl proc <#excl,PackedSet[A],PackedSet[A]>`_ for excluding other set + ## * `missingOrExcl proc <#missingOrExcl,PackedSet[A],A>`_ + runnableExamples: + var a = initPackedSet[int]() + a.incl(3) + a.excl(3) + a.excl(3) + a.excl(99) + assert len(a) == 0 + exclImpl[A](s, cast[int](key)) + +proc excl*[A](s: var PackedSet[A], other: PackedSet[A]) = + ## Excludes all elements from `other` from `s`. + ## + ## This is the in-place version of `s - other <#-,PackedSet[A],PackedSet[A]>`_. + ## + ## See also: + ## * `incl proc <#incl,PackedSet[A],PackedSet[A]>`_ for including other set + ## * `excl proc <#excl,PackedSet[A],A>`_ for excluding an element + ## * `missingOrExcl proc <#missingOrExcl,PackedSet[A],A>`_ + runnableExamples: + var + a = initPackedSet[int]() + b = initPackedSet[int]() + a.incl(1) + a.incl(5) + b.incl(5) + a.excl(b) + assert len(a) == 1 + assert 5 notin a + + for item in other: + excl(s, item) + +proc len*[A](s: PackedSet[A]): int {.inline.} = + ## Returns the number of elements in `s`. + if s.elems < s.a.len: + result = s.elems + else: + result = 0 + for _ in s: + inc(result) + +proc missingOrExcl*[A](s: var PackedSet[A], key: A): bool = + ## Excludes `key` in the set `s` and tells if `key` was already missing from `s`. + ## + ## The difference with regards to the `excl proc <#excl,PackedSet[A],A>`_ is + ## that this proc returns `true` if `key` was missing from `s`. + ## The proc will return `false` if `key` was in `s` and it was removed + ## during this call. + ## + ## See also: + ## * `excl proc <#excl,PackedSet[A],A>`_ for excluding an element + ## * `excl proc <#excl,PackedSet[A],PackedSet[A]>`_ for excluding other set + ## * `containsOrIncl proc <#containsOrIncl,PackedSet[A],A>`_ + runnableExamples: + var a = initPackedSet[int]() + a.incl(5) + assert a.missingOrExcl(5) == false + assert a.missingOrExcl(5) == true + + var count = s.len + exclImpl(s, cast[int](key)) + result = count == s.len + +proc clear*[A](result: var PackedSet[A]) = + ## Clears the PackedSet[A] back to an empty state. + runnableExamples: + var a = initPackedSet[int]() + a.incl(5) + a.incl(7) + clear(a) + assert len(a) == 0 + + # setLen(result.data, InitIntSetSize) + # for i in 0..InitIntSetSize-1: result.data[i] = nil + # result.max = InitIntSetSize-1 + when defined(nimNoNilSeqs): + result.data = @[] + else: + result.data = nil + result.max = 0 + result.counter = 0 + result.head = nil + result.elems = 0 + +proc isNil*[A](x: PackedSet[A]): bool {.inline.} = x.head.isNil and x.elems == 0 + +proc assign*[A](dest: var PackedSet[A], src: PackedSet[A]) = + ## Copies `src` to `dest`. + ## `dest` does not need to be initialized by `initPackedSet[A] proc <#initPackedSet[A]>`_. + runnableExamples: + var + a = initPackedSet[int]() + b = initPackedSet[int]() + b.incl(5) + b.incl(7) + a.assign(b) + assert len(a) == 2 + + if src.elems <= src.a.len: + when defined(nimNoNilSeqs): + dest.data = @[] + else: + dest.data = nil + dest.max = 0 + dest.counter = src.counter + dest.head = nil + dest.elems = src.elems + dest.a = src.a + else: + dest.counter = src.counter + dest.max = src.max + dest.elems = src.elems + newSeq(dest.data, src.data.len) + + var it = src.head + while it != nil: + var h = it.key and dest.max + var perturb = it.key + while dest.data[h] != nil: h = nextTry(h, dest.max, perturb) + assert(dest.data[h] == nil) + var n: PTrunk + new(n) + n.next = dest.head + n.key = it.key + n.bits = it.bits + dest.head = n + dest.data[h] = n + it = it.next + +proc union*[A](s1, s2: PackedSet[A]): PackedSet[A] = + ## Returns the union of the sets `s1` and `s2`. + ## + ## The same as `s1 + s2 <#+,PackedSet[A],PackedSet[A]>`_. + runnableExamples: + var + a = initPackedSet[int]() + b = initPackedSet[int]() + a.incl(1); a.incl(2); a.incl(3) + b.incl(3); b.incl(4); b.incl(5) + assert union(a, b).len == 5 + ## {1, 2, 3, 4, 5} + + result.assign(s1) + incl(result, s2) + +proc intersection*[A](s1, s2: PackedSet[A]): PackedSet[A] = + ## Returns the intersection of the sets `s1` and `s2`. + ## + ## The same as `s1 * s2 <#*,PackedSet[A],PackedSet[A]>`_. + runnableExamples: + var + a = initPackedSet[int]() + b = initPackedSet[int]() + a.incl(1); a.incl(2); a.incl(3) + b.incl(3); b.incl(4); b.incl(5) + assert intersection(a, b).len == 1 + ## {3} + + result = initPackedSet[A]() + for item in s1: + if contains(s2, item): + incl(result, item) + +proc difference*[A](s1, s2: PackedSet[A]): PackedSet[A] = + ## Returns the difference of the sets `s1` and `s2`. + ## + ## The same as `s1 - s2 <#-,PackedSet[A],PackedSet[A]>`_. + runnableExamples: + var + a = initPackedSet[int]() + b = initPackedSet[int]() + a.incl(1); a.incl(2); a.incl(3) + b.incl(3); b.incl(4); b.incl(5) + assert difference(a, b).len == 2 + ## {1, 2} + + result = initPackedSet[A]() + for item in s1: + if not contains(s2, item): + incl(result, item) + +proc symmetricDifference*[A](s1, s2: PackedSet[A]): PackedSet[A] = + ## Returns the symmetric difference of the sets `s1` and `s2`. + runnableExamples: + var + a = initPackedSet[int]() + b = initPackedSet[int]() + a.incl(1); a.incl(2); a.incl(3) + b.incl(3); b.incl(4); b.incl(5) + assert symmetricDifference(a, b).len == 4 + ## {1, 2, 4, 5} + + result.assign(s1) + for item in s2: + if containsOrIncl(result, item): excl(result, item) + +proc `+`*[A](s1, s2: PackedSet[A]): PackedSet[A] {.inline.} = + ## Alias for `union(s1, s2) <#union,PackedSet[A],PackedSet[A]>`_. + result = union(s1, s2) + +proc `*`*[A](s1, s2: PackedSet[A]): PackedSet[A] {.inline.} = + ## Alias for `intersection(s1, s2) <#intersection,PackedSet[A],PackedSet[A]>`_. + result = intersection(s1, s2) + +proc `-`*[A](s1, s2: PackedSet[A]): PackedSet[A] {.inline.} = + ## Alias for `difference(s1, s2) <#difference,PackedSet[A],PackedSet[A]>`_. + result = difference(s1, s2) + +proc disjoint*[A](s1, s2: PackedSet[A]): bool = + ## Returns true if the sets `s1` and `s2` have no items in common. + runnableExamples: + var + a = initPackedSet[int]() + b = initPackedSet[int]() + a.incl(1); a.incl(2) + b.incl(2); b.incl(3) + assert disjoint(a, b) == false + b.excl(2) + assert disjoint(a, b) == true + + for item in s1: + if contains(s2, item): + return false + return true + +proc card*[A](s: PackedSet[A]): int {.inline.} = + ## Alias for `len() <#len,PackedSet[A]>`_. + result = s.len() + +proc `<=`*[A](s1, s2: PackedSet[A]): bool = + ## Returns true if `s1` is subset of `s2`. + ## + ## A subset `s1` has all of its elements in `s2`, and `s2` doesn't necessarily + ## have more elements than `s1`. That is, `s1` can be equal to `s2`. + runnableExamples: + var + a = initPackedSet[int]() + b = initPackedSet[int]() + a.incl(1) + b.incl(1); b.incl(2) + assert a <= b + a.incl(2) + assert a <= b + a.incl(3) + assert(not (a <= b)) + + for item in s1: + if not s2.contains(item): + return false + return true + +proc `<`*[A](s1, s2: PackedSet[A]): bool = + ## Returns true if `s1` is proper subset of `s2`. + ## + ## A strict or proper subset `s1` has all of its elements in `s2`, but `s2` has + ## more elements than `s1`. + runnableExamples: + var + a = initPackedSet[int]() + b = initPackedSet[int]() + a.incl(1) + b.incl(1); b.incl(2) + assert a < b + a.incl(2) + assert(not (a < b)) + return s1 <= s2 and not (s2 <= s1) + +proc `==`*[A](s1, s2: PackedSet[A]): bool = + ## Returns true if both `s1` and `s2` have the same elements and set size. + return s1 <= s2 and s2 <= s1 + +proc `$`*[A](s: PackedSet[A]): string = + ## The `$` operator for int sets. + ## + ## Converts the set `s` to a string, mostly for logging and printing purposes. + dollarImpl() diff --git a/tests/stdlib/tintsets.nim b/tests/stdlib/tintsets.nim deleted file mode 100644 index f859b87ae..000000000 --- a/tests/stdlib/tintsets.nim +++ /dev/null @@ -1,65 +0,0 @@ -import intsets -import std/sets - -from sequtils import toSeq -from algorithm import sorted - -proc sortedPairs[T](t: T): auto = toSeq(t.pairs).sorted -template sortedItems(t: untyped): untyped = sorted(toSeq(t)) - -block: # we use HashSet as groundtruth, it's well tested elsewhere - template testDel(t, t0) = - - template checkEquals() = - doAssert t.len == t0.len - for k in t0: - doAssert k in t - for k in t: - doAssert k in t0 - - doAssert sortedItems(t) == sortedItems(t0) - - template incl2(i) = - t.incl i - t0.incl i - - template excl2(i) = - t.excl i - t0.excl i - - block: - var expected: seq[int] - let n = 100 - let n2 = n*2 - for i in 0.. Date: Fri, 13 Nov 2020 14:18:01 +0100 Subject: [PATCH 0987/3768] makes test green again --- tests/errmsgs/tgcsafety.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/errmsgs/tgcsafety.nim b/tests/errmsgs/tgcsafety.nim index e6a62204e..721677e95 100644 --- a/tests/errmsgs/tgcsafety.nim +++ b/tests/errmsgs/tgcsafety.nim @@ -5,8 +5,8 @@ nimout: ''' type mismatch: got .}> but expected one of: proc serve(server: AsyncHttpServer; port: Port; - callback: proc (request: Request): Future[void] {.closure, gcsafe.}; - address = ""): owned(Future[void]) + callback: proc (request: Request): Future[void] {.closure, gcsafe.}; + address = ""; assumedDescriptorsPerRequest = 5): owned(Future[void]) first type mismatch at position: 3 required type for callback: proc (request: Request): Future[system.void]{.closure, gcsafe.} but expression 'cb' is of type: proc (req: Request): Future[system.void]{.locks: .} From 9f566881f15bed58276d3df4b5b2cb2289016396 Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 13 Nov 2020 17:03:07 +0100 Subject: [PATCH 0988/3768] ported to FreeRTOS --- lib/pure/asyncdispatch.nim | 22 ++++++++++++---------- lib/pure/asynchttpserver.nim | 11 ++++++++++- 2 files changed, 22 insertions(+), 11 deletions(-) diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 0bacf3bb5..90345676f 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -1946,13 +1946,15 @@ proc activeDescriptors*(): int {.inline.} = when defined(posix): import posix -proc maxDescriptors*(): int {.raises: OSError.} = - ## Returns the maximum number of active file descriptors for the current - ## process. This involves a system call. - when defined(windows): - result = 16_700_000 - else: - var fdLim: RLimit - if getrlimit(RLIMIT_NOFILE, fdLim) < 0: - raiseOSError(osLastError()) - result = int(fdLim.rlim_cur) - 1 +when defined(linux) or defined(windows) or defined(macosx) or defined(bsd): + proc maxDescriptors*(): int {.raises: OSError.} = + ## Returns the maximum number of active file descriptors for the current + ## process. This involves a system call. For now `maxDescriptors` is + ## supported on the following OSes: Windows, Linux, OSX, BSD. + when defined(windows): + result = 16_700_000 + else: + var fdLim: RLimit + if getrlimit(RLIMIT_NOFILE, fdLim) < 0: + raiseOSError(osLastError()) + result = int(fdLim.rlim_cur) - 1 diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index b69f3f9a4..a5f6a0bc8 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -308,9 +308,18 @@ proc processClient(server: AsyncHttpServer, client: AsyncSocket, address: string ) if not retry: break +const + nimMaxDescriptorsFallback* {.intdefine.} = 16_000 ## fallback value for \ + ## when `maxDescriptors` is not available. + ## This can be set on the command line during compilation + ## via `-d:nimMaxDescriptorsFallback=N` + proc listen*(server: AsyncHttpServer; port: Port; address = "") = ## Listen to the given port and address. - server.maxFDs = maxDescriptors() + when declared(maxDescriptors): + server.maxFDs = try: maxDescriptors() except: nimMaxDescriptorsFallback + else: + server.maxFDs = nimMaxDescriptorsFallback server.socket = newAsyncSocket() if server.reuseAddr: server.socket.setSockOpt(OptReuseAddr, true) From 02f8b11a716843be8270f8bf7b2dac94f1c7dc1a Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 13 Nov 2020 17:16:00 +0100 Subject: [PATCH 0989/3768] fixes the doc rendering --- lib/pure/asynchttpserver.nim | 73 +++++++++++++++++------------------- 1 file changed, 35 insertions(+), 38 deletions(-) diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index a5f6a0bc8..f3d4b3dd2 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -7,44 +7,41 @@ # distribution, for details about the copyright. # -##[ This module implements a high performance asynchronous HTTP server. - -This HTTP server has not been designed to be used in production, but -for testing applications locally. Because of this, when deploying your -application in production you should use a reverse proxy (for example nginx) -instead of allowing users to connect directly to this server. - -Basic usage -=========== - -This example will create an HTTP server on port 8080. The server will -respond to all requests with a ``200 OK`` response code and "Hello World" -as the response body. - -.. code-block::nim - import asynchttpserver, asyncdispatch - - proc main {.async.} = - var server = newAsyncHttpServer() - proc cb(req: Request) {.async.} = - #echo(req.reqMethod, " ", req.url) - #echo(req.headers) - let headers = {"Date": "Tue, 29 Apr 2014 23:40:08 GMT", - "Content-type": "text/plain; charset=utf-8"} - await req.respond(Http200, "Hello World", headers.newHttpHeaders()) - - server.listen Port(5555) - while true: - if server.shouldAcceptRequest(5): - var (address, client) = await server.socket.acceptAddr() - asyncCheck processClient(server, client, address, cb) - else: - poll() - - asyncCheck main() - runForever() - -]## +## This module implements a high performance asynchronous HTTP server. +## +## This HTTP server has not been designed to be used in production, but +## for testing applications locally. Because of this, when deploying your +## application in production you should use a reverse proxy (for example nginx) +## instead of allowing users to connect directly to this server. +## +## Basic usage +## =========== +## +## This example will create an HTTP server on port 8080. The server will +## respond to all requests with a ``200 OK`` response code and "Hello World" +## as the response body. +## +## .. code-block:: Nim +## +## import asynchttpserver, asyncdispatch +## +## proc main {.async.} = +## var server = newAsyncHttpServer() +## proc cb(req: Request) {.async.} = +## let headers = {"Date": "Tue, 29 Apr 2014 23:40:08 GMT", +## "Content-type": "text/plain; charset=utf-8"} +## await req.respond(Http200, "Hello World", headers.newHttpHeaders()) +## +## server.listen Port(5555) +## while true: +## if server.shouldAcceptRequest(5): +## var (address, client) = await server.socket.acceptAddr() +## asyncCheck processClient(server, client, address, cb) +## else: +## poll() +## +## asyncCheck main() +## runForever() import asyncnet, asyncdispatch, parseutils, uri, strutils import httpcore From edd84bd9bc8a35d3b96f427d8d81829cb45f29dc Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Sat, 14 Nov 2020 01:21:27 +0300 Subject: [PATCH 0990/3768] nimgrep: change 2 iterators to closure --- tools/nimgrep.nim | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/tools/nimgrep.nim b/tools/nimgrep.nim index 1e563435c..cb46f30b8 100644 --- a/tools/nimgrep.nim +++ b/tools/nimgrep.nim @@ -998,7 +998,8 @@ proc hasRightDirectory(path: string, walkOptC: WalkOptComp[Pattern]): bool = if dirname.contains(pat): return false result = true -iterator walkDirBasic(dir: string, walkOptC: WalkOptComp[Pattern]): string = +iterator walkDirBasic(dir: string, walkOptC: WalkOptComp[Pattern]): string + {.closure.} = var dirStack = @[dir] # stack of directories var timeFiles = newSeq[(times.Time, string)]() while dirStack.len > 0: @@ -1040,7 +1041,8 @@ iterator walkDirBasic(dir: string, walkOptC: WalkOptComp[Pattern]): string = for (_, file) in timeFiles: yield file -iterator walkRec(paths: seq[string]): (string, string) = +iterator walkRec(paths: seq[string]): tuple[error: string, filename: string] + {.closure.} = declareCompiledPatterns(walkOptC, WalkOptComp): walkOptC.excludeFile.add walkOpt.excludeFile.compileArray() walkOptC.includeFile.add walkOpt.includeFile.compileArray() @@ -1130,13 +1132,13 @@ proc run1Thread() = processFileResult(searchOptC.pattern, "-", processFile(searchOptC, "-", yieldContents=optReplace in options)) - for (err, filename) in walkRec(paths): - if err != "": + for entry in walkRec(paths): + if entry.error != "": inc(gVar.errors) - printError (err & filename) + printError (entry.error & entry.filename) continue - processFileResult(searchOptC.pattern, filename, - processFile(searchOptC, filename, + processFileResult(searchOptC.pattern, entry.filename, + processFile(searchOptC, entry.filename, yieldContents=optReplace in options)) # Multi-threaded version: all printing is being done in the Main thread. @@ -1182,12 +1184,12 @@ proc pathProducer(arg: (seq[string], WalkOpt)) {.thread.} = walkOpt = arg[1] # init thread-local copy of opt var nextFileN = 0 - for (err, filename) in walkRec(paths): - if err == "": - searchRequestsChan.send((nextFileN,filename)) + for entry in walkRec(paths): + if entry.error == "": + searchRequestsChan.send((nextFileN, entry.filename)) else: - resultsChan.send((false, nextFileN, - filename, @[Output(kind: openError, msg: err)])) + resultsChan.send((false, nextFileN, entry.filename, + @[Output(kind: openError, msg: entry.error)])) nextFileN += 1 resultsChan.send((true, nextFileN, "", @[])) # pass total number of files From 1a47fc2291b285ceb6b2f330f45997944b754b3a Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 13 Nov 2020 14:44:44 -0800 Subject: [PATCH 0991/3768] strengthen taddr.nim: add test case for #14578; reference other issues; test cpp (#15960) --- tests/{js => misc}/taddr.nim | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) rename tests/{js => misc}/taddr.nim (84%) diff --git a/tests/js/taddr.nim b/tests/misc/taddr.nim similarity index 84% rename from tests/js/taddr.nim rename to tests/misc/taddr.nim index 9c45621a8..4d0aebc01 100644 --- a/tests/js/taddr.nim +++ b/tests/misc/taddr.nim @@ -1,5 +1,6 @@ discard """ - targets: "c js" + targets: "c cpp js" + matrix: "; -d:release" """ type T = object @@ -79,7 +80,7 @@ someGlobalPtr[] = 10 doAssert(someGlobal == 10) block: - # issue #14576 + # bug #14576 # lots of these used to give: Error: internal error: genAddr: 2 proc byLent[T](a: T): lent T = a proc byPtr[T](a: T): ptr T = a.unsafeAddr @@ -89,24 +90,21 @@ block: let (x,y) = byLent(a) doAssert (x,y) == a - block: - when defined(c) and defined(release): - # bug; pending https://github.com/nim-lang/Nim/issues/14578 - discard - else: - let a = 10 - doAssert byLent(a) == 10 - let a2 = byLent(a) - doAssert a2 == 10 + block: # (with -d:release) bug #14578 + let a = 10 + doAssert byLent(a) == 10 + let a2 = byLent(a) + doAssert a2 == 10 block: - let a = [11,12] - doAssert byLent(a) == [11,12] + when not defined(cpp): # pending bug #15958 + let a = [11,12] + doAssert byLent(a) == [11,12] let a2 = (11,) doAssert byLent(a2) == (11,) block: - when defined(c) and defined(release): + when (defined(c) or defined(cpp)) and defined(release): discard # probably not a bug since optimizer is free to pass by value, and `unsafeAddr` is used else: var a = @[12] @@ -134,6 +132,6 @@ block: bar(a2).inc doAssert a2 == 13 - block: # xxx: bug this doesn't work + block: # pending bug #15959 when false: proc byLent2[T](a: T): lent type(a[0]) = a[0] From 96930b9caec2ed51777af5d8570f64b00bfaf2a8 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 14 Nov 2020 01:08:15 -0800 Subject: [PATCH 0992/3768] targets: use cpp instead of c++ everywhere (was by far the most common) (#15961) --- doc/testament.rst | 2 +- testament/testament.nim | 2 +- tests/float/tfloatmod.nim | 2 +- tests/osproc/passenv.nim | 2 +- tests/stdlib/tfrexp1.nim | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/testament.rst b/doc/testament.rst index 09c248d1f..1a1909022 100644 --- a/doc/testament.rst +++ b/doc/testament.rst @@ -26,7 +26,7 @@ Options * ``--print`` Also print results to the console * ``--simulate`` See what tests would be run but don't run them (for debugging) * ``--failing`` Only show failing/ignored tests -* ``--targets:"c c++ js objc"`` Run tests for specified targets (default: all) +* ``--targets:"c cpp js objc"`` Run tests for specified targets (default: all) * ``--nim:path`` Use a particular nim executable (default: ``$PATH/nim``) * ``--directory:dir`` Change to directory dir before reading the tests or doing anything else. * ``--colors:on|off`` Turn messages coloring on|off. diff --git a/testament/testament.nim b/testament/testament.nim index 8fead76ef..2912b03ab 100644 --- a/testament/testament.nim +++ b/testament/testament.nim @@ -41,7 +41,7 @@ Options: --print also print results to the console --simulate see what tests would be run but don't run them (for debugging) --failing only show failing/ignored tests - --targets:"c c++ js objc" run tests for specified targets (default: all) + --targets:"c cpp js objc" run tests for specified targets (default: all) --nim:path use a particular nim executable (default: $$PATH/nim) --directory:dir Change to directory dir before reading the tests or doing anything else. --colors:on|off Turn messages coloring on|off. diff --git a/tests/float/tfloatmod.nim b/tests/float/tfloatmod.nim index 0b84ec7e0..37546a64c 100644 --- a/tests/float/tfloatmod.nim +++ b/tests/float/tfloatmod.nim @@ -1,5 +1,5 @@ discard """ - targets: "c c++ js" + targets: "c cpp js" output: "ok" exitcode: "0" """ diff --git a/tests/osproc/passenv.nim b/tests/osproc/passenv.nim index 815f7536f..40b1c3f7c 100644 --- a/tests/osproc/passenv.nim +++ b/tests/osproc/passenv.nim @@ -1,7 +1,7 @@ discard """ file: "passenv.nim" output: "123" - targets: "c c++ objc" + targets: "c cpp objc" """ import osproc, os, strtabs diff --git a/tests/stdlib/tfrexp1.nim b/tests/stdlib/tfrexp1.nim index 6da185420..21ecc7491 100644 --- a/tests/stdlib/tfrexp1.nim +++ b/tests/stdlib/tfrexp1.nim @@ -1,5 +1,5 @@ discard """ - targets: "js c c++" + targets: "js c cpp" output: '''ok''' """ From 5af798c21b5d32bb6c2b9cff21e8288dc829a41a Mon Sep 17 00:00:00 2001 From: flywind Date: Sat, 14 Nov 2020 18:47:17 +0800 Subject: [PATCH 0993/3768] [docs minor]add some tips yo intern.rst --- doc/intern.rst | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/doc/intern.rst b/doc/intern.rst index 89d477d09..ea9e1a07c 100644 --- a/doc/intern.rst +++ b/doc/intern.rst @@ -153,13 +153,21 @@ are also lots of procs that aid in debugging: debug(symbol) # pretty prints the Nim ast, but annotates symbol IDs: echo renderTree(someNode, {renderIds}) - if n.info ?? "temp.nim": + if `??`(conf, n.info, "temp.nim"): # only output when it comes from "temp.nim" echo renderTree(n) - if n.info ?? "temp.nim": + if `??`(conf, n.info, "temp.nim"): # why does it process temp.nim here? writeStackTrace() +These procs may not be imported by module, you can import them for debuging: + +.. code-block:: nim + from astalgo import debug + from types import typeToString + from renderer import renderTree + from msgs import `??` + To create a new compiler for each run, use ``koch temp``:: ./koch temp c /tmp/test.nim From bfa387d384762cf0b46431407669ec1889b8c4a3 Mon Sep 17 00:00:00 2001 From: flywind Date: Sat, 14 Nov 2020 18:51:05 +0800 Subject: [PATCH 0994/3768] minor --- doc/intern.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/intern.rst b/doc/intern.rst index ea9e1a07c..3f8a2ce56 100644 --- a/doc/intern.rst +++ b/doc/intern.rst @@ -160,7 +160,7 @@ are also lots of procs that aid in debugging: # why does it process temp.nim here? writeStackTrace() -These procs may not be imported by module, you can import them for debuging: +These procs may not be imported by a module. You can import them directly for debugging: .. code-block:: nim from astalgo import debug From 47ce24f680dbf5aed8cd393f011739ea5d7a9928 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sat, 14 Nov 2020 19:51:23 +0800 Subject: [PATCH 0995/3768] fix #12303 (#15964) --- compiler/jsgen.nim | 9 +++++++-- tests/js/t12303.nim | 16 ++++++++++++++++ tests/varres/tvarres0.nim | 3 +++ 3 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 tests/js/t12303.nim diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 34ebd306a..3a1ef7fbb 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -1074,8 +1074,13 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc]) else: useMagic(p, "nimCopy") - lineF(p, "nimCopy($1, $2, $3);$n", - [a.res, b.res, genTypeInfo(p, y.typ)]) + # supports proc getF(): var T + if x.kind in {nkHiddenDeref, nkDerefExpr} and x[0].kind in nkCallKinds: + lineF(p, "nimCopy($1, $2, $3);$n", + [a.res, b.res, genTypeInfo(p, y.typ)]) + else: + lineF(p, "$1 = nimCopy($1, $2, $3);$n", + [a.res, b.res, genTypeInfo(p, y.typ)]) of etyBaseIndex: if a.typ != etyBaseIndex or b.typ != etyBaseIndex: if y.kind == nkCall: diff --git a/tests/js/t12303.nim b/tests/js/t12303.nim new file mode 100644 index 000000000..270d82ced --- /dev/null +++ b/tests/js/t12303.nim @@ -0,0 +1,16 @@ +discard """ + output: "{ b: 2 }" +""" + +import jsconsole, jsffi + +type + A = ref object + b: B + + B = object + b: int + +var a = cast[A](js{}) +a.b = B(b: 2) +console.log a.b diff --git a/tests/varres/tvarres0.nim b/tests/varres/tvarres0.nim index 0f89c7e71..94bdb4a06 100644 --- a/tests/varres/tvarres0.nim +++ b/tests/varres/tvarres0.nim @@ -4,6 +4,7 @@ discard """ 123 1234 12345 +123456 ''' """ @@ -28,6 +29,8 @@ getF().a = 1234 echo getF().a getF() = Foo(a: 12345) echo getF().a +(addr getF())[] = Foo(a: 123456) +echo getF().a block: # #13848 From d0dda9efab6bd67d033e30e2ba16820450c38cbe Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Sat, 14 Nov 2020 15:31:59 -0300 Subject: [PATCH 0996/3768] Fix #15806 --- doc/manual.rst | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/doc/manual.rst b/doc/manual.rst index bfcce24cf..bbc8bb225 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -8,7 +8,7 @@ Nim Manual .. contents:: - "Complexity" seems to be a lot like "energy": you can transfer it from the + "Complexity" seems to be a lot like "energy": you can transfer it from the end-user to one/some of the other players, but the total amount seems to remain pretty much constant for a given task. -- Ran @@ -313,6 +313,35 @@ it was not case-sensitive and underscores were ignored and there was not even a distinction between ``foo`` and ``Foo``. +Stropping +--------- + +`Stropping `_ +allows the same letter sequence to be used both as a keyword and as an identifier, and simplifies parsing. +For example, allowing a variable named `if` without clashing with the keyword `if`. +In Nim, this is achieved via backticks, allowing any reserved word to be used as an identifier. + +Examples + +.. code-block:: nim + var `var` = "Hello Stropping" + +.. code-block:: nim + type Type = object + `int`: int + + let `object` = Type(`int`: 9) + assert `object` is Type + assert `object`.`int` == 9 + + var `var` = 42 + let `let` = 8 + assert `var` + `let` == 50 + + const `assert` = true + assert `assert` + + String literals --------------- From 3cebae492e61ccbb29cfc38fbaa1f54270dfeb8a Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Sat, 14 Nov 2020 15:39:03 -0300 Subject: [PATCH 0997/3768] Fix #15806 --- doc/manual.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/manual.rst b/doc/manual.rst index bbc8bb225..0656dfa76 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -317,7 +317,8 @@ Stropping --------- `Stropping `_ -allows the same letter sequence to be used both as a keyword and as an identifier, and simplifies parsing. +allows the same letter sequence to be used both as a keyword and as an identifier, +this simplifies parsing and FFI with languages where that identifier is not a reserved keyword. For example, allowing a variable named `if` without clashing with the keyword `if`. In Nim, this is achieved via backticks, allowing any reserved word to be used as an identifier. From 9ab1a86b5bfb8f5647b9f9cb4ab48a5b5eb55707 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Sat, 14 Nov 2020 15:43:31 -0300 Subject: [PATCH 0998/3768] Fix #15806 --- doc/manual.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/manual.rst b/doc/manual.rst index 0656dfa76..6dfff55a6 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -317,8 +317,9 @@ Stropping --------- `Stropping `_ -allows the same letter sequence to be used both as a keyword and as an identifier, -this simplifies parsing and FFI with languages where that identifier is not a reserved keyword. +allows the same letter sequence to be used both as a keyword and as an identifier, this simplifies parsing and +`FFI `_ +with languages where that identifier is not a reserved keyword. For example, allowing a variable named `if` without clashing with the keyword `if`. In Nim, this is achieved via backticks, allowing any reserved word to be used as an identifier. From c56a6133bb3c1d2e862726e10e445d477dd6d15e Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Sat, 14 Nov 2020 15:45:13 -0300 Subject: [PATCH 0999/3768] Fix #15806 --- doc/manual.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/manual.rst b/doc/manual.rst index 6dfff55a6..c80b60ea0 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -320,6 +320,7 @@ Stropping allows the same letter sequence to be used both as a keyword and as an identifier, this simplifies parsing and `FFI `_ with languages where that identifier is not a reserved keyword. + For example, allowing a variable named `if` without clashing with the keyword `if`. In Nim, this is achieved via backticks, allowing any reserved word to be used as an identifier. @@ -330,7 +331,7 @@ Examples .. code-block:: nim type Type = object - `int`: int + `int`: int let `object` = Type(`int`: 9) assert `object` is Type From 1b9f37c6dc99b95a2ccee0a51535faf224b31f31 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Sat, 14 Nov 2020 19:57:06 -0300 Subject: [PATCH 1000/3768] https://github.com/nim-lang/Nim/pull/15968/files#r523468677 --- doc/manual.rst | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/doc/manual.rst b/doc/manual.rst index c80b60ea0..9a4ae378d 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -316,13 +316,7 @@ distinction between ``foo`` and ``Foo``. Stropping --------- -`Stropping `_ -allows the same letter sequence to be used both as a keyword and as an identifier, this simplifies parsing and -`FFI `_ -with languages where that identifier is not a reserved keyword. - -For example, allowing a variable named `if` without clashing with the keyword `if`. -In Nim, this is achieved via backticks, allowing any reserved word to be used as an identifier. +If a keyword is enclosed in backticks it loses its keyword property and becomes an ordinary identifier. Examples From 122f22d1632255cf7d0539c620a30155dc84fd69 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 15 Nov 2020 07:27:13 +0100 Subject: [PATCH 1001/3768] asynchttpserver cleanups [backport:1.0] (#15966) * asynchttpserver cleanups [backport:1.0] --- lib/pure/asynchttpserver.nim | 20 +++++++++----------- tests/errmsgs/tgcsafety.nim | 2 +- 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index f3d4b3dd2..f3f59baf8 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -14,8 +14,8 @@ ## application in production you should use a reverse proxy (for example nginx) ## instead of allowing users to connect directly to this server. ## -## Basic usage -## =========== +## Example +## ======= ## ## This example will create an HTTP server on port 8080. The server will ## respond to all requests with a ``200 OK`` response code and "Hello World" @@ -32,11 +32,10 @@ ## "Content-type": "text/plain; charset=utf-8"} ## await req.respond(Http200, "Hello World", headers.newHttpHeaders()) ## -## server.listen Port(5555) +## server.listen Port(8080) ## while true: -## if server.shouldAcceptRequest(5): -## var (address, client) = await server.socket.acceptAddr() -## asyncCheck processClient(server, client, address, cb) +## if server.shouldAcceptRequest(): +## asyncCheck server.acceptRequest(cb) ## else: ## poll() ## @@ -333,7 +332,7 @@ proc shouldAcceptRequest*(server: AsyncHttpServer; result = assumedDescriptorsPerRequest < 0 or (activeDescriptors() + assumedDescriptorsPerRequest < server.maxFDs) -proc acceptRequest*(server: AsyncHttpServer, port: Port, +proc acceptRequest*(server: AsyncHttpServer, callback: proc (request: Request): Future[void] {.closure, gcsafe.}) {.async.} = ## Accepts a single request. Write an explicit loop around this proc so that ## errors can be handled properly. @@ -343,7 +342,7 @@ proc acceptRequest*(server: AsyncHttpServer, port: Port, proc serve*(server: AsyncHttpServer, port: Port, callback: proc (request: Request): Future[void] {.closure, gcsafe.}, address = ""; - assumedDescriptorsPerRequest = 5) {.async.} = + assumedDescriptorsPerRequest = -1) {.async.} = ## Starts the process of listening for incoming HTTP connections on the ## specified address and port. ## @@ -382,9 +381,8 @@ when not defined(testing) and isMainModule: server.listen Port(5555) while true: - if server.shouldAcceptRequest(5): - var (address, client) = await server.socket.acceptAddr() - asyncCheck processClient(server, client, address, cb) + if server.shouldAcceptRequest(): + asyncCheck server.acceptRequest(cb) else: poll() diff --git a/tests/errmsgs/tgcsafety.nim b/tests/errmsgs/tgcsafety.nim index 721677e95..77515b74f 100644 --- a/tests/errmsgs/tgcsafety.nim +++ b/tests/errmsgs/tgcsafety.nim @@ -6,7 +6,7 @@ type mismatch: got .} From 92da06e64ece2f87e259c17439c085074d77ef97 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Sun, 15 Nov 2020 15:08:27 +0100 Subject: [PATCH 1002/3768] Fix nimsuggest/#117 (#15602) --- compiler/extccomp.nim | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index d97f9a887..e37e867da 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -627,10 +627,7 @@ proc footprint(conf: ConfigRef; cfile: Cfile): SecureHash = getCompileCFileCmd(conf, cfile)) proc externalFileChanged(conf: ConfigRef; cfile: Cfile): bool = - case conf.backend - of backendInvalid: doAssert false - of backendJs: return false # pre-existing behavior, but not sure it's good - else: discard + if conf.backend == backendJs: return false # pre-existing behavior, but not sure it's good var hashFile = toGeneratedFile(conf, conf.withPackageName(cfile.cname), "sha1") var currentHash = footprint(conf, cfile) From 7eb34d170a489c2a0ed90d921f3f7cc6813f19c1 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 15 Nov 2020 15:47:42 +0100 Subject: [PATCH 1003/3768] fixes #15753 [backport:1.4] (#15971) --- compiler/liftdestructors.nim | 95 ++++++++++++++++++++++++++++------- tests/arc/torc_selfcycles.nim | 33 ++++++++++++ 2 files changed, 109 insertions(+), 19 deletions(-) create mode 100644 tests/arc/torc_selfcycles.nim diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 9b6c179db..943dd074e 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -392,6 +392,16 @@ proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode = v.addVar(result, lowerings.newIntLit(c.g, body.info, first)) body.add v +proc declareTempOf(c: var TLiftCtx; body: PNode; value: PNode): PNode = + var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), nextId(c.idgen), c.fn, c.info) + temp.typ = value.typ + incl(temp.flags, sfFromGeneric) + + var v = newNodeI(nkVarSection, c.info) + result = newSymNode(temp) + v.addVar(result, value) + body.add v + proc addIncStmt(c: var TLiftCtx; body, i: PNode) = let incCall = genBuiltin(c.g, mInc, "inc", i) incCall.add lowerings.newIntLit(c.g, c.info, 1) @@ -508,43 +518,75 @@ proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = discard "strings are atomic and have no inner elements that are to trace" proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = + #[ bug #15753 is really subtle. Usually the classical write barrier for reference + counting looks like this:: + + incRef source # increment first; this takes care of self-assignments1 + decRef dest + dest[] = source + + However, 'decRef dest' might trigger a cycle collection and then the collector + traverses the graph. It is crucial that when it follows the pointers the assignment + 'dest[] = source' already happened so that we don't do trial deletion on a wrong + graph -- this causes premature freeing of objects! The correct barrier looks like + this:: + + let tmp = dest + incRef source + dest[] = source + decRef tmp + + ]# var actions = newNodeI(nkStmtList, c.info) let elemType = t.lastSon createTypeBoundOps(c.g, c.c, elemType, c.info, c.idgen) + let isCyclic = c.g.config.selectedGC == gcOrc and types.canFormAcycle(elemType) + + let tmp = + if isCyclic and c.kind in {attachedAsgn, attachedSink}: + declareTempOf(c, body, x) + else: + x if isFinal(elemType): - addDestructorCall(c, elemType, actions, genDeref(x, nkDerefExpr)) + addDestructorCall(c, elemType, actions, genDeref(tmp, nkDerefExpr)) var alignOf = genBuiltin(c.g, mAlignOf, "alignof", newNodeIT(nkType, c.info, elemType)) alignOf.typ = getSysType(c.g, c.info, tyInt) - actions.add callCodegenProc(c.g, "nimRawDispose", c.info, x, alignOf) + actions.add callCodegenProc(c.g, "nimRawDispose", c.info, tmp, alignOf) else: - addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(x, nkDerefExpr)) - actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, x) - - let isCyclic = c.g.config.selectedGC == gcOrc and types.canFormAcycle(elemType) + addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(tmp, nkDerefExpr)) + actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, tmp) var cond: PNode if isCyclic: if isFinal(elemType): let typInfo = genBuiltin(c.g, mGetTypeInfoV2, "getTypeInfoV2", newNodeIT(nkType, x.info, elemType)) typInfo.typ = getSysType(c.g, c.info, tyPointer) - cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicStatic", c.info, x, typInfo) + cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicStatic", c.info, tmp, typInfo) else: - cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicDyn", c.info, x) + cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicDyn", c.info, tmp) else: cond = callCodegenProc(c.g, "nimDecRefIsLast", c.info, x) cond.typ = getSysType(c.g, x.info, tyBool) case c.kind of attachedSink: - body.add genIf(c, cond, actions) - body.add newAsgnStmt(x, y) + if isCyclic: + body.add newAsgnStmt(x, y) + body.add genIf(c, cond, actions) + else: + body.add genIf(c, cond, actions) + body.add newAsgnStmt(x, y) of attachedAsgn: body.add genIf(c, y, callCodegenProc(c.g, if isCyclic: "nimIncRefCyclic" else: "nimIncRef", c.info, y)) - body.add genIf(c, cond, actions) - body.add newAsgnStmt(x, y) + if isCyclic: + body.add newAsgnStmt(x, y) + body.add genIf(c, cond, actions) + else: + body.add genIf(c, cond, actions) + body.add newAsgnStmt(x, y) of attachedDestructor: body.add genIf(c, cond, actions) of attachedDeepCopy: assert(false, "cannot happen") @@ -571,19 +613,30 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = let xenv = genBuiltin(c.g, mAccessEnv, "accessEnv", x) xenv.typ = getSysType(c.g, c.info, tyPointer) + let isCyclic = c.g.config.selectedGC == gcOrc + let tmp = + if isCyclic and c.kind in {attachedAsgn, attachedSink}: + declareTempOf(c, body, xenv) + else: + xenv + var actions = newNodeI(nkStmtList, c.info) - actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, xenv) + actions.add callCodegenProc(c.g, "nimDestroyAndDispose", c.info, tmp) let decRefProc = - if c.g.config.selectedGC == gcOrc: "nimDecRefIsLastCyclicDyn" + if isCyclic: "nimDecRefIsLastCyclicDyn" else: "nimDecRefIsLast" - let cond = callCodegenProc(c.g, decRefProc, c.info, xenv) + let cond = callCodegenProc(c.g, decRefProc, c.info, tmp) cond.typ = getSysType(c.g, x.info, tyBool) case c.kind of attachedSink: - body.add genIf(c, cond, actions) - body.add newAsgnStmt(x, y) + if isCyclic: + body.add newAsgnStmt(x, y) + body.add genIf(c, cond, actions) + else: + body.add genIf(c, cond, actions) + body.add newAsgnStmt(x, y) of attachedAsgn: let yenv = genBuiltin(c.g, mAccessEnv, "accessEnv", y) yenv.typ = getSysType(c.g, c.info, tyPointer) @@ -591,8 +644,12 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = if c.g.config.selectedGC == gcOrc: "nimIncRefCyclic" else: "nimIncRef" body.add genIf(c, yenv, callCodegenProc(c.g, incRefProc, c.info, yenv)) - body.add genIf(c, cond, actions) - body.add newAsgnStmt(x, y) + if isCyclic: + body.add newAsgnStmt(x, y) + body.add genIf(c, cond, actions) + else: + body.add genIf(c, cond, actions) + body.add newAsgnStmt(x, y) of attachedDestructor: body.add genIf(c, cond, actions) of attachedDeepCopy: assert(false, "cannot happen") diff --git a/tests/arc/torc_selfcycles.nim b/tests/arc/torc_selfcycles.nim new file mode 100644 index 000000000..ac4fa52ce --- /dev/null +++ b/tests/arc/torc_selfcycles.nim @@ -0,0 +1,33 @@ +discard """ + output: '''ok''' + cmd: '''nim c --gc:orc -d:useMalloc -d:nimStressOrc $file''' + valgrind: "leaks" +""" + +# bug #15753 + +type + NodeKind = enum + nkDancing, + nkColumn + + DancingNode = ref object + right: DancingNode + column: DancingNode + kind: NodeKind + +proc newColumnNode(): DancingNode = + result = DancingNode(kind: nkColumn) + result.right = result + result.column = result + +proc createDLXList(): DancingNode = + result = newColumnNode() + + for i in 0 .. 15: + let n = newColumnNode() + n.right = result.right + result = n + echo "ok" + +var dlxlist = createDLXList() From a4d02f591c685b261a41a7b9b51aaba05c542499 Mon Sep 17 00:00:00 2001 From: cooldome Date: Mon, 16 Nov 2020 11:39:28 +0000 Subject: [PATCH 1004/3768] fix #15910 (#15984) * fix #15910 * produce op of op is nil * Trigger build --- compiler/liftdestructors.nim | 16 ++++++--- tests/arc/t14383.nim | 66 +++++++++++++++++++++++++++++++++++- 2 files changed, 77 insertions(+), 5 deletions(-) diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 943dd074e..45a66e795 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -42,6 +42,9 @@ proc at(a, i: PNode, elemType: PType): PNode = result[1] = i result.typ = elemType +proc destructorOverriden(t: PType): bool = + t.attachedOps[attachedDestructor] != nil and sfOverriden in t.attachedOps[attachedDestructor].flags + proc fillBodyTup(c: var TLiftCtx; t: PType; body, x, y: PNode) = for i in 0.. Date: Mon, 16 Nov 2020 22:19:26 +0800 Subject: [PATCH 1005/3768] rename: stmt -> typed and expr -> untyped (#15989) * stmt -> typed * expr -> untyped * minor --- compiler/ast.nim | 2 +- compiler/semtypes.nim | 2 +- compiler/sigmatch.nim | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index ca931ab09..3d14aba66 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1804,7 +1804,7 @@ proc isAtom*(n: PNode): bool {.inline.} = result = n.kind >= nkNone and n.kind <= nkNilLit proc isEmptyType*(t: PType): bool {.inline.} = - ## 'void' and 'stmt' types are often equivalent to 'nil' these days: + ## 'void' and 'typed' types are often equivalent to 'nil' these days: result = t == nil or t.kind in {tyVoid, tyTyped} proc makeStmtList*(n: PNode): PNode = diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 5feb25a59..0ec7140e0 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1315,7 +1315,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, "' is only valid for macros and templates") # 'auto' as a return type does not imply a generic: elif r.kind == tyAnything: - # 'p(): auto' and 'p(): expr' are equivalent, but the rest of the + # 'p(): auto' and 'p(): untyped' are equivalent, but the rest of the # compiler is hardly aware of 'auto': r = newTypeS(tyUntyped, c) elif r.kind == tyStatic: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 2e332f105..b674c3d9e 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1240,8 +1240,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, if result < isGeneric: result = isNone else: discard of tyOpenArray, tyVarargs: - # varargs[expr] is special too but handled earlier. So we only need to - # handle varargs[stmt] which is the same as varargs[typed]: + # varargs[untyped] is special too but handled earlier. So we only need to + # handle varargs[typed]: if f.kind == tyVarargs: if tfVarargs in a.flags: return typeRel(c, f.base, a.lastSon, flags) From 3e5e029ef8c480d73e847fde546551c9416c948e Mon Sep 17 00:00:00 2001 From: narimiran Date: Mon, 16 Nov 2020 17:46:48 +0100 Subject: [PATCH 1006/3768] telling us once about a change is enough [ci skip] --- changelog.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index e50be6de3..200214ff3 100644 --- a/changelog.md +++ b/changelog.md @@ -37,13 +37,12 @@ - Added `asyncdispatch.activeDescriptors` that returns the number of currently active async event handles/file descriptors -- Added `asyncdispatch.maxDescriptors` that returns the maximum number of - active async event handles/file descriptors. ## Language changes - `nimscript` now handles `except Exception as e` + - The `cstring` doesn't support `[]=` operator in JS backend. From a8858eb93ee666d669c272ec2ba1ade89e81177c Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Mon, 16 Nov 2020 15:15:05 -0300 Subject: [PATCH 1007/3768] htmlgen: Add lazy loading (#15986) --- lib/pure/htmlgen.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pure/htmlgen.nim b/lib/pure/htmlgen.nim index ba50a4a6e..acec05832 100644 --- a/lib/pure/htmlgen.nim +++ b/lib/pure/htmlgen.nim @@ -333,12 +333,12 @@ macro i*(e: varargs[untyped]): untyped = macro iframe*(e: varargs[untyped]): untyped = ## Generates the HTML ``iframe`` element. - result = xmlCheckedTag(e, "iframe", "src srcdoc name sandbox width height" & + result = xmlCheckedTag(e, "iframe", "src srcdoc name sandbox width height loading" & commonAttr) macro img*(e: varargs[untyped]): untyped = ## Generates the HTML ``img`` element. - result = xmlCheckedTag(e, "img", "crossorigin usemap ismap height width" & + result = xmlCheckedTag(e, "img", "crossorigin usemap ismap height width loading" & commonAttr, "src alt", true) macro input*(e: varargs[untyped]): untyped = From 61352d5a203e76e3334b21766bdbd4ebc71c300a Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 16 Nov 2020 13:52:02 -0800 Subject: [PATCH 1008/3768] remove unused and misleading FilenameOption.foShort (#15982) --- compiler/msgs.nim | 2 -- 1 file changed, 2 deletions(-) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index be3dc5ec4..d0209db5d 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -237,7 +237,6 @@ type FilenameOption* = enum foRelProject # relative to project path, e.g.: ../foo.nim foMagicSauce # magic sauce, shortest of (foAbs, foRelProject) foName # lastPathPart, e.g.: foo.nim - foShort # foName without extension, e.g.: foo foStacktrace # if optExcessiveStackTrace: foAbs else: foName proc toFilenameOption*(conf: ConfigRef, fileIdx: FileIndex, opt: FilenameOption): string = @@ -255,7 +254,6 @@ proc toFilenameOption*(conf: ConfigRef, fileIdx: FileIndex, opt: FilenameOption) else: relPath of foName: result = toProjPath(conf, fileIdx).lastPathPart - of foShort: result = toFilename(conf, fileIdx) of foStacktrace: if optExcessiveStackTrace in conf.globalOptions: result = toFilenameOption(conf, fileIdx, foAbs) From 50d035b7896b85184fc6e97e22f32a189d76b81b Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 16 Nov 2020 23:40:17 -0800 Subject: [PATCH 1009/3768] defer: improve manual, clarify difference wrt try/finally (#16010) --- doc/manual.rst | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/doc/manual.rst b/doc/manual.rst index 9a4ae378d..1a23c5997 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -4414,7 +4414,9 @@ Custom exceptions can be raised like any others, e.g.: Defer statement --------------- -Instead of a ``try finally`` statement a ``defer`` statement can be used. +Instead of a ``try finally`` statement a ``defer`` statement can be used, which +avoids lexical nesting and offers more flexibility in terms of scoping as shown +below. Any statements following the ``defer`` in the current block will be considered to be in an implicit try block: @@ -4423,7 +4425,7 @@ to be in an implicit try block: :test: "nim c $1" proc main = - var f = open("numbers.txt") + var f = open("numbers.txt", fmWrite) defer: close(f) f.write "abc" f.write "def" @@ -4441,6 +4443,33 @@ Is rewritten to: finally: close(f) +When `defer` is at the outermost scope of a template/macro, its scope extends +to the block where the template is called from: + +.. code-block:: nim + :test: "nim c $1" + + template safeOpenDefer(f, path) = + var f = open(path, fmWrite) + defer: close(f) + + template safeOpenFinally(f, path, body) = + var f = open(path, fmWrite) + try: body # without `defer`, `body` must be specified as parameter + finally: close(f) + + block: + safeOpenDefer(f, "/tmp/z01.txt") + f.write "abc" + block: + safeOpenFinally(f, "/tmp/z01.txt"): + f.write "abc" # adds a lexical scope + block: + var f = open("/tmp/z01.txt", fmWrite) + try: + f.write "abc" # adds a lexical scope + finally: close(f) + Top-level ``defer`` statements are not supported since it's unclear what such a statement should refer to. From fec19c980e36e62747eb119f0740cb785e858ac3 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Tue, 17 Nov 2020 18:47:01 +0800 Subject: [PATCH 1010/3768] follow #16009 VM supports cast nil to ptr (#16012) * follow #16009 VM supports cast nil to ptr * more testcase --- compiler/vmgen.nim | 3 +++ tests/vm/tcastnil.nim | 30 ++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 tests/vm/tcastnil.nim diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index bcb7faa47..069b8cbf5 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -901,6 +901,9 @@ proc genCastIntFloat(c: PCtx; n: PNode; dest: var TDest) = c.gABx(n, opcSetType, dest, c.genType(dst)) c.gABC(n, opcCastIntToPtr, dest, tmp) c.freeTemp(tmp) + elif src.kind == tyNil and dst.kind in PtrLikeKinds: + if dest < 0: dest = c.getTemp(n[0].typ) + genLit(c, n[1], dest) else: # todo: support cast from tyInt to tyRef globalError(c.config, n.info, "VM does not support 'cast' from " & $src.kind & " to " & $dst.kind) diff --git a/tests/vm/tcastnil.nim b/tests/vm/tcastnil.nim new file mode 100644 index 000000000..5a4684f95 --- /dev/null +++ b/tests/vm/tcastnil.nim @@ -0,0 +1,30 @@ +discard """ + nimout: '''nil +nil +nil +nil +nil +''' +""" + +block: + static: + let a = cast[pointer](nil) + echo a.repr + +block: + static: + echo cast[ptr int](nil).repr + +block: + const str = cast[ptr int](nil) + static: + echo str.repr + +block: + static: + echo cast[ptr int](nil).repr + +block: + static: + echo cast[RootRef](nil).repr From 0f7f159a35bc10a43ff15bcb6c20eefdc574138e Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Wed, 18 Nov 2020 01:05:20 +0800 Subject: [PATCH 1011/3768] fix #15623 (#16009) * fix #15623 * add testcase for #15623 * fix * add testcase --- compiler/semfold.nim | 2 +- lib/posix/posix_linux_amd64_consts.nim | 2 +- tests/ccgbugs/t15623.nim | 11 +++++++++++ tests/ccgbugs/t15623_2.nim | 13 +++++++++++++ 4 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 tests/ccgbugs/t15623.nim create mode 100644 tests/ccgbugs/t15623_2.nim diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 9b21c5fa3..f38359a64 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -674,7 +674,7 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode of nkCast: var a = getConstExpr(m, n[1], idgen, g) if a == nil: return - if n.typ != nil and n.typ.kind in NilableTypes: + if n.typ != nil and n.typ.kind in NilableTypes and a.kind != nkNilLit: # we allow compile-time 'cast' for pointer types: result = a result.typ = n.typ diff --git a/lib/posix/posix_linux_amd64_consts.nim b/lib/posix/posix_linux_amd64_consts.nim index f3230f71d..917c1f966 100644 --- a/lib/posix/posix_linux_amd64_consts.nim +++ b/lib/posix/posix_linux_amd64_consts.nim @@ -365,7 +365,7 @@ const SCHED_RR* = cint(2) const SCHED_OTHER* = cint(0) # -const SEM_FAILED* = cast[pointer]((nil)) +const SEM_FAILED* = nil # const SIGEV_NONE* = cint(1) diff --git a/tests/ccgbugs/t15623.nim b/tests/ccgbugs/t15623.nim new file mode 100644 index 000000000..c8246c85b --- /dev/null +++ b/tests/ccgbugs/t15623.nim @@ -0,0 +1,11 @@ +discard """ + action: "compile" +""" + +# bug #15623 +block: + echo cast[ptr int](nil)[] + +block: + var x: ref int = nil + echo cast[ptr int](x)[] diff --git a/tests/ccgbugs/t15623_2.nim b/tests/ccgbugs/t15623_2.nim new file mode 100644 index 000000000..4f33bc745 --- /dev/null +++ b/tests/ccgbugs/t15623_2.nim @@ -0,0 +1,13 @@ +discard """ + output: '''0 +0 +''' +""" + +# bug #15623 +block: + echo cast[int](cast[ptr int](nil)) + +block: + var x: ref int = nil + echo cast[int](cast[ptr int](x)) From 1663ad77e3398fec0b5d6b00516f91565ce12cce Mon Sep 17 00:00:00 2001 From: "Andrey R (cooldome)" Date: Tue, 17 Nov 2020 18:54:14 +0000 Subject: [PATCH 1012/3768] Revert "fix #15623 (#16009)" This reverts commit 0f7f159a35bc10a43ff15bcb6c20eefdc574138e. --- compiler/semfold.nim | 2 +- lib/posix/posix_linux_amd64_consts.nim | 2 +- tests/ccgbugs/t15623.nim | 11 ----------- tests/ccgbugs/t15623_2.nim | 13 ------------- 4 files changed, 2 insertions(+), 26 deletions(-) delete mode 100644 tests/ccgbugs/t15623.nim delete mode 100644 tests/ccgbugs/t15623_2.nim diff --git a/compiler/semfold.nim b/compiler/semfold.nim index f38359a64..9b21c5fa3 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -674,7 +674,7 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode of nkCast: var a = getConstExpr(m, n[1], idgen, g) if a == nil: return - if n.typ != nil and n.typ.kind in NilableTypes and a.kind != nkNilLit: + if n.typ != nil and n.typ.kind in NilableTypes: # we allow compile-time 'cast' for pointer types: result = a result.typ = n.typ diff --git a/lib/posix/posix_linux_amd64_consts.nim b/lib/posix/posix_linux_amd64_consts.nim index 917c1f966..f3230f71d 100644 --- a/lib/posix/posix_linux_amd64_consts.nim +++ b/lib/posix/posix_linux_amd64_consts.nim @@ -365,7 +365,7 @@ const SCHED_RR* = cint(2) const SCHED_OTHER* = cint(0) # -const SEM_FAILED* = nil +const SEM_FAILED* = cast[pointer]((nil)) # const SIGEV_NONE* = cint(1) diff --git a/tests/ccgbugs/t15623.nim b/tests/ccgbugs/t15623.nim deleted file mode 100644 index c8246c85b..000000000 --- a/tests/ccgbugs/t15623.nim +++ /dev/null @@ -1,11 +0,0 @@ -discard """ - action: "compile" -""" - -# bug #15623 -block: - echo cast[ptr int](nil)[] - -block: - var x: ref int = nil - echo cast[ptr int](x)[] diff --git a/tests/ccgbugs/t15623_2.nim b/tests/ccgbugs/t15623_2.nim deleted file mode 100644 index 4f33bc745..000000000 --- a/tests/ccgbugs/t15623_2.nim +++ /dev/null @@ -1,13 +0,0 @@ -discard """ - output: '''0 -0 -''' -""" - -# bug #15623 -block: - echo cast[int](cast[ptr int](nil)) - -block: - var x: ref int = nil - echo cast[int](cast[ptr int](x)) From 632af8afad92594da050a5f7fd784ea111c3cc54 Mon Sep 17 00:00:00 2001 From: "Andrey R (cooldome)" Date: Tue, 17 Nov 2020 18:54:33 +0000 Subject: [PATCH 1013/3768] Revert "follow #16009 VM supports cast nil to ptr (#16012)" This reverts commit fec19c980e36e62747eb119f0740cb785e858ac3. --- compiler/vmgen.nim | 3 --- tests/vm/tcastnil.nim | 30 ------------------------------ 2 files changed, 33 deletions(-) delete mode 100644 tests/vm/tcastnil.nim diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 069b8cbf5..bcb7faa47 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -901,9 +901,6 @@ proc genCastIntFloat(c: PCtx; n: PNode; dest: var TDest) = c.gABx(n, opcSetType, dest, c.genType(dst)) c.gABC(n, opcCastIntToPtr, dest, tmp) c.freeTemp(tmp) - elif src.kind == tyNil and dst.kind in PtrLikeKinds: - if dest < 0: dest = c.getTemp(n[0].typ) - genLit(c, n[1], dest) else: # todo: support cast from tyInt to tyRef globalError(c.config, n.info, "VM does not support 'cast' from " & $src.kind & " to " & $dst.kind) diff --git a/tests/vm/tcastnil.nim b/tests/vm/tcastnil.nim deleted file mode 100644 index 5a4684f95..000000000 --- a/tests/vm/tcastnil.nim +++ /dev/null @@ -1,30 +0,0 @@ -discard """ - nimout: '''nil -nil -nil -nil -nil -''' -""" - -block: - static: - let a = cast[pointer](nil) - echo a.repr - -block: - static: - echo cast[ptr int](nil).repr - -block: - const str = cast[ptr int](nil) - static: - echo str.repr - -block: - static: - echo cast[ptr int](nil).repr - -block: - static: - echo cast[RootRef](nil).repr From 0869d2a47777685d221ddde927f0175096c911b0 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Wed, 18 Nov 2020 08:14:14 +0800 Subject: [PATCH 1014/3768] fix #15972 (#15994) * fix #15972 * add testcase * more --- compiler/semstmts.nim | 4 ++-- .../stmt/tforloop_tuple_multiple_underscore.nim | 10 ++++++++++ tests/stmt/tforloop_tuple_underscore.nim | 16 ++++++++++++++++ tests/stmt/tforloop_underscore.nim | 6 ++++++ 4 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 tests/stmt/tforloop_tuple_multiple_underscore.nim create mode 100644 tests/stmt/tforloop_tuple_underscore.nim create mode 100644 tests/stmt/tforloop_underscore.nim diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index f83864775..cd7428b62 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -739,7 +739,7 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode = else: v.typ = iter[i] n[0][i] = newSymNode(v) - if sfGenSym notin v.flags: addDecl(c, v) + if sfGenSym notin v.flags and not isDiscardUnderscore(v): addDecl(c, v) elif v.owner == nil: v.owner = getCurrOwner(c) else: var v = symForVar(c, n[0]) @@ -749,7 +749,7 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode = # for an example: v.typ = iterBase n[0] = newSymNode(v) - if sfGenSym notin v.flags: addDecl(c, v) + if sfGenSym notin v.flags and not isDiscardUnderscore(v): addDecl(c, v) elif v.owner == nil: v.owner = getCurrOwner(c) else: localError(c.config, n.info, errWrongNumberOfVariables) diff --git a/tests/stmt/tforloop_tuple_multiple_underscore.nim b/tests/stmt/tforloop_tuple_multiple_underscore.nim new file mode 100644 index 000000000..2afdb0b77 --- /dev/null +++ b/tests/stmt/tforloop_tuple_multiple_underscore.nim @@ -0,0 +1,10 @@ +discard """ + errormsg: "undeclared identifier: '_'" +""" + +iterator iter(): (int, int, int) = + yield (1, 1, 2) + + +for (_, i, _) in iter(): + echo _ \ No newline at end of file diff --git a/tests/stmt/tforloop_tuple_underscore.nim b/tests/stmt/tforloop_tuple_underscore.nim new file mode 100644 index 000000000..8cbb0fc10 --- /dev/null +++ b/tests/stmt/tforloop_tuple_underscore.nim @@ -0,0 +1,16 @@ +discard """ + errormsg: "undeclared identifier: '_'" +""" + +iterator iter(): (int, int) = + yield (1, 2) + yield (3, 4) + yield (1, 2) + yield (3, 4) + yield (1, 2) + yield (3, 4) + + +for (_, i) in iter(): + echo _ + diff --git a/tests/stmt/tforloop_underscore.nim b/tests/stmt/tforloop_underscore.nim new file mode 100644 index 000000000..c5b49da77 --- /dev/null +++ b/tests/stmt/tforloop_underscore.nim @@ -0,0 +1,6 @@ +discard """ + errormsg: "undeclared identifier: '_'" +""" + +for _ in ["a"]: + echo _ From e6e1e9574de8c090cb9de7a809064037e2af39f7 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Wed, 18 Nov 2020 15:28:05 +0800 Subject: [PATCH 1015/3768] combine PR#16009 and PR#16012 (#16024) * fix #15623 * add testcase for #15623 * add testcase * combine #16009 * support casting nil to NilableTypes in VM --- compiler/semfold.nim | 2 +- compiler/vmgen.nim | 5 +++++ tests/misc/tcast.nim | 44 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 50 insertions(+), 1 deletion(-) diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 9b21c5fa3..f38359a64 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -674,7 +674,7 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode of nkCast: var a = getConstExpr(m, n[1], idgen, g) if a == nil: return - if n.typ != nil and n.typ.kind in NilableTypes: + if n.typ != nil and n.typ.kind in NilableTypes and a.kind != nkNilLit: # we allow compile-time 'cast' for pointer types: result = a result.typ = n.typ diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index bcb7faa47..84ff1a91d 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -901,6 +901,11 @@ proc genCastIntFloat(c: PCtx; n: PNode; dest: var TDest) = c.gABx(n, opcSetType, dest, c.genType(dst)) c.gABC(n, opcCastIntToPtr, dest, tmp) c.freeTemp(tmp) + elif src.kind == tyNil and dst.kind in NilableTypes: + # supports casting nil literals to NilableTypes in VM + # see #16024 + if dest < 0: dest = c.getTemp(n[0].typ) + genLit(c, n[1], dest) else: # todo: support cast from tyInt to tyRef globalError(c.config, n.info, "VM does not support 'cast' from " & $src.kind & " to " & $dst.kind) diff --git a/tests/misc/tcast.nim b/tests/misc/tcast.nim index 745b9e221..12179cb1c 100644 --- a/tests/misc/tcast.nim +++ b/tests/misc/tcast.nim @@ -29,3 +29,47 @@ reject: discard cast[enum](0) proc a = echo "hi" reject: discard cast[ptr](a) + +# bug #15623 +block: + if false: + echo cast[ptr int](nil)[] + +block: + if false: + var x: ref int = nil + echo cast[ptr int](x)[] + +block: + doAssert cast[int](cast[ptr int](nil)) == 0 + +block: + var x: ref int = nil + doAssert cast[int](cast[ptr int](x)) == 0 + +block: + block: + static: + let a = cast[pointer](nil) + doAssert a.repr == "nil" + + block: + static: + doAssert cast[ptr int](nil).repr == "nil" + + block: + const str = cast[ptr int](nil) + static: + doAssert str.repr == "nil" + + block: + static: + doAssert cast[ptr int](nil).repr == "nil" + + block: + static: + doAssert cast[RootRef](nil).repr == "nil" + + block: + static: + doAssert cast[cstring](nil).repr == "nil" From 581bcfe29094f525d2ab4e27455bed18a3eac811 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Wed, 18 Nov 2020 15:56:31 +0800 Subject: [PATCH 1016/3768] fix #6497 (#16027) * fix #6497 * add testcase for #16027 --- compiler/ast.nim | 1 + tests/ccgbugs/t16027.nim | 13 +++++++++++++ 2 files changed, 14 insertions(+) create mode 100644 tests/ccgbugs/t16027.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index 3d14aba66..c75a149c0 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1441,6 +1441,7 @@ proc copySym*(s: PSym; id: ItemId): PSym = result.position = s.position result.loc = s.loc result.annex = s.annex # BUGFIX + result.constraint = s.constraint if result.kind in {skVar, skLet, skField}: result.guard = s.guard result.bitsize = s.bitsize diff --git a/tests/ccgbugs/t16027.nim b/tests/ccgbugs/t16027.nim new file mode 100644 index 000000000..58f15eb6e --- /dev/null +++ b/tests/ccgbugs/t16027.nim @@ -0,0 +1,13 @@ +discard """ + ccodecheck: "__restrict__" + action: compile + joinable: false +""" + +# see bug #16027 +iterator myitems(s: seq[int]): int = + var data {.codegenDecl: "$# __restrict__ $#".} : ptr int = nil + yield 1 + +for i in @[1].myitems: + discard From 33d79b9e64f8f67adcf8415de61d1568630e36cb Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 18 Nov 2020 03:01:43 -0800 Subject: [PATCH 1017/3768] fix https://github.com/nim-lang/RFCs/issues/286: add FileInfo.blockSize (#16023) --- changelog.md | 2 ++ lib/pure/os.nim | 71 ++++++++++++++++++++++++++----------------------- 2 files changed, 39 insertions(+), 34 deletions(-) diff --git a/changelog.md b/changelog.md index 200214ff3..ce06a23bc 100644 --- a/changelog.md +++ b/changelog.md @@ -38,6 +38,8 @@ - Added `asyncdispatch.activeDescriptors` that returns the number of currently active async event handles/file descriptors +- `os.FileInfo` (returned by `getFileInfo`) now contains `blockSize`, + determining preferred I/O block size for this file object. ## Language changes diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 234d82bc2..aa2a1aaa8 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -1550,40 +1550,6 @@ proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1", else: result = a.st_dev == b.st_dev and a.st_ino == b.st_ino -proc sameFileContent*(path1, path2: string): bool {.rtl, extern: "nos$1", - tags: [ReadIOEffect], noWeirdTarget.} = - ## Returns true if both pathname arguments refer to files with identical - ## binary content. - ## - ## See also: - ## * `sameFile proc <#sameFile,string,string>`_ - const - bufSize = 8192 # 8K buffer - var - a, b: File - if not open(a, path1): return false - if not open(b, path2): - close(a) - return false - var bufA = alloc(bufSize) - var bufB = alloc(bufSize) - while true: - var readA = readBuffer(a, bufA, bufSize) - var readB = readBuffer(b, bufB, bufSize) - if readA != readB: - result = false - break - if readA == 0: - result = true - break - result = equalMem(bufA, bufB, readA) - if not result: break - if readA != bufSize: break # end of file - dealloc(bufA) - dealloc(bufB) - close(a) - close(b) - type FilePermission* = enum ## File access permission, modelled after UNIX. ## @@ -3074,6 +3040,8 @@ type lastAccessTime*: times.Time ## Time file was last accessed. lastWriteTime*: times.Time ## Time file was last modified/written to. creationTime*: times.Time ## Time file was created. Not supported on all systems! + blockSize*: int ## Preferred I/O block size for this object. + ## In some filesystems, this may vary from file to file. template rawToFormalFileInfo(rawInfo, path, formalInfo): untyped = ## Transforms the native file info structure into the one nim uses. @@ -3088,6 +3056,7 @@ template rawToFormalFileInfo(rawInfo, path, formalInfo): untyped = formalInfo.lastAccessTime = fromWinTime(rdFileTime(rawInfo.ftLastAccessTime)) formalInfo.lastWriteTime = fromWinTime(rdFileTime(rawInfo.ftLastWriteTime)) formalInfo.creationTime = fromWinTime(rdFileTime(rawInfo.ftCreationTime)) + formalInfo.blockSize = 8192 # xxx use windows API instead of hardcoding # Retrieve basic permissions if (rawInfo.dwFileAttributes and FILE_ATTRIBUTE_READONLY) != 0'i32: @@ -3114,6 +3083,7 @@ template rawToFormalFileInfo(rawInfo, path, formalInfo): untyped = formalInfo.lastAccessTime = rawInfo.st_atim.toTime formalInfo.lastWriteTime = rawInfo.st_mtim.toTime formalInfo.creationTime = rawInfo.st_ctim.toTime + formalInfo.blockSize = rawInfo.st_blksize formalInfo.permissions = {} checkAndIncludeMode(S_IRUSR, fpUserRead) @@ -3217,6 +3187,39 @@ proc getFileInfo*(path: string, followSymlink = true): FileInfo {.noWeirdTarget. raiseOSError(osLastError(), path) rawToFormalFileInfo(rawInfo, path, result) +proc sameFileContent*(path1, path2: string): bool {.rtl, extern: "nos$1", + tags: [ReadIOEffect], noWeirdTarget.} = + ## Returns true if both pathname arguments refer to files with identical + ## binary content. + ## + ## See also: + ## * `sameFile proc <#sameFile,string,string>`_ + var + a, b: File + if not open(a, path1): return false + if not open(b, path2): + close(a) + return false + let bufSize = getFileInfo(a).blockSize + var bufA = alloc(bufSize) + var bufB = alloc(bufSize) + while true: + var readA = readBuffer(a, bufA, bufSize) + var readB = readBuffer(b, bufB, bufSize) + if readA != readB: + result = false + break + if readA == 0: + result = true + break + result = equalMem(bufA, bufB, readA) + if not result: break + if readA != bufSize: break # end of file + dealloc(bufA) + dealloc(bufB) + close(a) + close(b) + proc isHidden*(path: string): bool {.noWeirdTarget.} = ## Determines whether ``path`` is hidden or not, using `this ## reference `_. From 87d3e5331a7838aa255deca8a840c2080b5424ce Mon Sep 17 00:00:00 2001 From: cooldome Date: Wed, 18 Nov 2020 17:19:57 +0000 Subject: [PATCH 1018/3768] Semfold for nil cast (#16030) * bring back the semfold of nil * remove space * fix test * proc type can't be dereferenced --- compiler/ccgexprs.nim | 4 +++- compiler/semfold.nim | 2 +- tests/misc/tcast.nim | 7 ++++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index a3fbeca5f..ec28eb1ec 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -71,8 +71,10 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope = p.module.s[cfsData].addf( "static NIM_CONST $1 $2 = {NIM_NIL,NIM_NIL};$n", [getTypeDesc(p.module, ty), result]) - else: + elif k in {tyPointer, tyNil, tyProc}: result = rope("NIM_NIL") + else: + result = "(($1) NIM_NIL)" % [getTypeDesc(p.module, ty)] of nkStrLit..nkTripleStrLit: let k = if ty == nil: tyString else: skipTypes(ty, abstractVarRange + {tyStatic, tyUserTypeClass, tyUserTypeClassInst}).kind diff --git a/compiler/semfold.nim b/compiler/semfold.nim index f38359a64..9b21c5fa3 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -674,7 +674,7 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode of nkCast: var a = getConstExpr(m, n[1], idgen, g) if a == nil: return - if n.typ != nil and n.typ.kind in NilableTypes and a.kind != nkNilLit: + if n.typ != nil and n.typ.kind in NilableTypes: # we allow compile-time 'cast' for pointer types: result = a result.typ = n.typ diff --git a/tests/misc/tcast.nim b/tests/misc/tcast.nim index 12179cb1c..6c6de6f8d 100644 --- a/tests/misc/tcast.nim +++ b/tests/misc/tcast.nim @@ -70,6 +70,7 @@ block: static: doAssert cast[RootRef](nil).repr == "nil" - block: - static: - doAssert cast[cstring](nil).repr == "nil" + # Issue #15730, not fixed yet + # block: + # static: + # doAssert cast[cstring](nil).repr == "nil" From baaa19b92751598b6519a64eed2b1beb06b2e662 Mon Sep 17 00:00:00 2001 From: Igor Ribeiro de Assis Date: Wed, 18 Nov 2020 20:55:49 +0100 Subject: [PATCH 1019/3768] Do not read the whole file to compute SHA1 hash (fixes 15997) (#16006) * Do not read the whole file to compute SHA1 hash (fixes 15997) * Update lib/std/sha1.nim Co-authored-by: Andreas Rumpf * Update lib/std/sha1.nim Co-authored-by: Andreas Rumpf * Directly break from loop Co-authored-by: Andreas Rumpf [backport:1.2] [backport:1.4] --- lib/std/sha1.nim | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/std/sha1.nim b/lib/std/sha1.nim index 8f35a44ff..9763b286e 100644 --- a/lib/std/sha1.nim +++ b/lib/std/sha1.nim @@ -213,7 +213,22 @@ proc secureHashFile*(filename: string): SecureHash = ## **See also:** ## * `secureHash proc <#secureHash,openArray[char]>`_ for generating a ``SecureHash`` from a string ## * `parseSecureHash proc <#parseSecureHash,string>`_ for converting a string ``hash`` to ``SecureHash`` - secureHash(readFile(filename)) + const BufferLength = 8192 + + let f = open(filename) + var state = newSha1State() + var buffer = newString(BufferLength) + while true: + let length = readChars(f, buffer, 0, BufferLength) + if length == 0: + break + buffer.setLen(length) + state.update(buffer) + if length != BufferLength: + break + close(f) + + SecureHash(state.finalize()) proc `$`*(self: SecureHash): string = ## Returns the string representation of a ``SecureHash``. From b9eb91cbb5c8104955e595808d4b4d388894d406 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 18 Nov 2020 22:31:06 +0100 Subject: [PATCH 1020/3768] ORC: prepare for another patent-pending optimization (#15996) * ORC: prepare for another patent-pending optimization * bugfix * '=copy' for refs can take a cyclic parameter for more ORC optimizations * ORC: exploit the common 'it = it.next' pattern * can't hurt to check for nil * use an algorithm that is not obviously broken * restore the test case * final cleanups for --gc:orc --- changelog.md | 4 +++ compiler/injectdestructors.nim | 49 ++++++++++++++++++++++++++++-- compiler/liftdestructors.nim | 55 +++++++++++++++++++++++++++------- lib/system/arc.nim | 17 ++++++----- lib/system/cyclebreaker.nim | 4 ++- lib/system/orc.nim | 55 +++++++++++++++++++++++----------- tests/arc/tasyncleak.nim | 2 +- 7 files changed, 147 insertions(+), 39 deletions(-) diff --git a/changelog.md b/changelog.md index ce06a23bc..7cbd4bb66 100644 --- a/changelog.md +++ b/changelog.md @@ -38,6 +38,10 @@ - Added `asyncdispatch.activeDescriptors` that returns the number of currently active async event handles/file descriptors +- ``--gc:orc`` is now 10% faster than previously for common workloads. If + you have trouble with its changed behavior, compile with ``-d:nimOldOrc``. + + - `os.FileInfo` (returned by `getFileInfo`) now contains `blockSize`, determining preferred I/O block size for this file object. diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 7e64ebfdc..869e8ecc8 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -311,6 +311,43 @@ proc genSink(c: var Con; dest, ri: PNode, isDecl = false): PNode = # and copyMem(dest, source). This is efficient. result = newTree(nkStmtList, c.genDestroy(dest), newTree(nkFastAsgn, dest, ri)) +proc isCriticalLink(dest: PNode): bool {.inline.} = + #[ + Lins's idea that only "critical" links can introduce a cycle. This is + critical for the performance gurantees that we strive for: If you + traverse a data structure, no tracing will be performed at all. + ORC is about this promise: The GC only touches the memory that the + mutator touches too. + + These constructs cannot possibly create cycles:: + + local = ... + + new(x) + dest = ObjectConstructor(field: noalias(dest)) + + But since 'ObjectConstructor' is already moved into 'dest' all we really have + to look for is assignments to local variables. + ]# + result = dest.kind != nkSym + +proc finishCopy(c: var Con; result, dest: PNode; isFromSink: bool) = + if c.graph.config.selectedGC == gcOrc: + let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct}) + if cyclicType(t): + result.add boolLit(c.graph, result.info, isFromSink or isCriticalLink(dest)) + +proc genMarkCyclic(c: var Con; result, dest: PNode) = + if c.graph.config.selectedGC == gcOrc: + let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink, tyDistinct}) + if cyclicType(t): + if t.kind == tyRef: + result.add callCodegenProc(c.graph, "nimMarkCyclic", dest.info, dest) + else: + let xenv = genBuiltin(c.graph, mAccessEnv, "accessEnv", dest) + xenv.typ = getSysType(c.graph, dest.info, tyPointer) + result.add callCodegenProc(c.graph, "nimMarkCyclic", dest.info, xenv) + proc genCopyNoCheck(c: var Con; dest, ri: PNode): PNode = let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink}) result = c.genOp(t, attachedAsgn, dest, ri) @@ -390,7 +427,9 @@ proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): PNode = v.add(vpart) result.add v - let wasMovedCall = c.genWasMoved(skipConv(n)) + let nn = skipConv(n) + c.genMarkCyclic(result, nn) + let wasMovedCall = c.genWasMoved(nn) result.add wasMovedCall result.add tempAsNode @@ -405,6 +444,7 @@ proc passCopyToSink(n: PNode; c: var Con; s: var Scope): PNode = result.add c.genWasMoved(tmp) var m = c.genCopy(tmp, n) m.add p(n, c, s, normal) + c.finishCopy(m, n, isFromSink = true) result.add m if isLValue(n) and not isCapturedVar(n) and n.typ.skipTypes(abstractInst).kind != tyRef and c.inSpawn == 0: message(c.graph.config, n.info, hintPerformance, @@ -667,6 +707,7 @@ proc pRaiseStmt(n: PNode, c: var Con; s: var Scope): PNode = let tmp = c.getTemp(s, n[0].typ, n.info) var m = c.genCopyNoCheck(tmp, n[0]) m.add p(n[0], c, s, normal) + c.finishCopy(m, n[0], isFromSink = false) result = newTree(nkStmtList, c.genWasMoved(tmp), m) var toDisarm = n[0] if toDisarm.kind == nkStmtListExpr: toDisarm = toDisarm.lastSon @@ -941,16 +982,18 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, isDecl = false): PNod else: result = c.genCopy(dest, ri) result.add p(ri, c, s, consumed) + c.finishCopy(result, dest, isFromSink = false) of nkBracket: # array constructor if ri.len > 0 and isDangerousSeq(ri.typ): result = c.genCopy(dest, ri) result.add p(ri, c, s, consumed) + c.finishCopy(result, dest, isFromSink = false) else: result = c.genSink(dest, p(ri, c, s, consumed), isDecl) of nkObjConstr, nkTupleConstr, nkClosure, nkCharLit..nkNilLit: result = c.genSink(dest, p(ri, c, s, consumed), isDecl) - of nkSym: + of nkSym: if dest.kind == nkSym and dest.sym == ri.sym: # rule (self-assignment-removal): result = newNodeI(nkEmpty, dest.info) @@ -966,6 +1009,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, isDecl = false): PNod else: result = c.genCopy(dest, ri) result.add p(ri, c, s, consumed) + c.finishCopy(result, dest, isFromSink = false) of nkHiddenSubConv, nkHiddenStdConv, nkConv, nkObjDownConv, nkObjUpConv: result = c.genSink(dest, p(ri, c, s, sinkArg), isDecl) of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt: @@ -983,6 +1027,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, isDecl = false): PNod else: result = c.genCopy(dest, ri) result.add p(ri, c, s, consumed) + c.finishCopy(result, dest, isFromSink = false) proc computeUninit(c: var Con) = if not c.uninitComputed: diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 45a66e795..512fb6190 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -65,7 +65,7 @@ proc newAsgnStmt(le, ri: PNode): PNode = result[0] = le result[1] = ri -proc genBuiltin(g: ModuleGraph; magic: TMagic; name: string; i: PNode): PNode = +proc genBuiltin*(g: ModuleGraph; magic: TMagic; name: string; i: PNode): PNode = result = newNodeI(nkCall, i.info) result.add createMagic(g, name, magic).newSymNode result.add i @@ -248,6 +248,19 @@ proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) = else: fillBodyObjTImpl(c, t, body, x, y) +proc boolLit*(g: ModuleGraph; info: TLineInfo; value: bool): PNode = + result = newIntLit(g, info, ord value) + result.typ = getSysType(g, info, tyBool) + +proc getCycleParam(c: TLiftCtx): PNode = + assert c.kind == attachedAsgn + if c.fn.typ.len == 4: + result = c.fn.typ.n.lastSon + assert result.kind == nkSym + assert result.sym.name.s == "cyclic" + else: + result = boolLit(c.g, c.info, true) + proc newHookCall(c: var TLiftCtx; op: PSym; x, y: PNode): PNode = #if sfError in op.flags: # localError(c.config, x.info, "usage of '$1' is a user-defined error" % op.name.s) @@ -261,6 +274,13 @@ proc newHookCall(c: var TLiftCtx; op: PSym; x, y: PNode): PNode = result.add x if y != nil: result.add y + if op.typ.len == 4: + assert y != nil + if c.fn.typ.len == 4: + result.add getCycleParam(c) + else: + # assume the worst: A cycle is created: + result.add boolLit(c.g, y.info, true) proc newOpCall(c: var TLiftCtx; op: PSym; x: PNode): PNode = result = newNodeIT(nkCall, x.info, op.typ[0]) @@ -526,6 +546,12 @@ proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = of attachedTrace: discard "strings are atomic and have no inner elements that are to trace" +proc cyclicType*(t: PType): bool = + case t.kind + of tyRef: result = types.canFormAcycle(t.lastSon) + of tyProc: result = t.callConv == ccClosure + else: result = false + proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = #[ bug #15753 is really subtle. Usually the classical write barrier for reference counting looks like this:: @@ -588,12 +614,13 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = body.add genIf(c, cond, actions) body.add newAsgnStmt(x, y) of attachedAsgn: - body.add genIf(c, y, callCodegenProc(c.g, - if isCyclic: "nimIncRefCyclic" else: "nimIncRef", c.info, y)) if isCyclic: + body.add genIf(c, y, callCodegenProc(c.g, + "nimIncRefCyclic", c.info, y, getCycleParam(c))) body.add newAsgnStmt(x, y) body.add genIf(c, cond, actions) else: + body.add genIf(c, y, callCodegenProc(c.g, "nimIncRef", c.info, y)) body.add genIf(c, cond, actions) body.add newAsgnStmt(x, y) of attachedDestructor: @@ -649,14 +676,13 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = of attachedAsgn: let yenv = genBuiltin(c.g, mAccessEnv, "accessEnv", y) yenv.typ = getSysType(c.g, c.info, tyPointer) - let incRefProc = - if c.g.config.selectedGC == gcOrc: "nimIncRefCyclic" - else: "nimIncRef" - body.add genIf(c, yenv, callCodegenProc(c.g, incRefProc, c.info, yenv)) if isCyclic: + body.add genIf(c, yenv, callCodegenProc(c.g, "nimIncRefCyclic", c.info, yenv, getCycleParam(c))) body.add newAsgnStmt(x, y) body.add genIf(c, cond, actions) else: + body.add genIf(c, yenv, callCodegenProc(c.g, "nimIncRef", c.info, yenv)) + body.add genIf(c, cond, actions) body.add newAsgnStmt(x, y) of attachedDestructor: @@ -888,6 +914,13 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp if kind notin {attachedDestructor, attachedDispose}: result.typ.addParam src + if kind == attachedAsgn and g.config.selectedGC == gcOrc and + cyclicType(typ.skipTypes(abstractInst)): + let cycleParam = newSym(skParam, getIdent(g.cache, "cyclic"), + nextId(idgen), result, info) + cycleParam.typ = getSysType(g, info, tyBool) + result.typ.addParam cycleParam + var n = newNodeI(nkProcDef, info, bodyPos+1) for i in 0..= roots.len or idx < 0: cprintf("[Bug!] %ld\n", idx) quit 1 roots.d[idx] = roots.d[roots.len-1] - roots.d[idx][0].rootIdx = idx + roots.d[idx][0].rootIdx = idx+1 dec roots.len + s.rootIdx = 0 proc scanBlack(s: Cell; desc: PNimTypeV2; j: var GcEnv) = #[ @@ -245,7 +263,7 @@ proc collectWhite(s: Cell; desc: PNimTypeV2; j: var GcEnv) = collectWhite(t) free(s) # watch out, a bug here! ]# - if s.color == colWhite and (s.rc and isCycleCandidate) == 0: + if s.color == colWhite and s.rootIdx == 0: orcAssert(j.traceStack.len == 0, "collectWhite: trace stack not empty") s.setColor(colBlack) @@ -253,7 +271,7 @@ proc collectWhite(s: Cell; desc: PNimTypeV2; j: var GcEnv) = trace(s, desc, j) while j.traceStack.len > 0: let (t, desc) = j.traceStack.pop() - if t.color == colWhite and (t.rc and isCycleCandidate) == 0: + if t.color == colWhite and t.rootIdx == 0: j.toFree.add(t, desc) t.setColor(colBlack) trace(t, desc, j) @@ -284,7 +302,7 @@ proc collectCyclesBacon(j: var GcEnv) = init j.toFree for i in 0 ..< roots.len: let s = roots.d[i][0] - s.rc = s.rc and not isCycleCandidate + s.rootIdx = 0 collectWhite(s, roots.d[i][1], j) for i in 0 ..< j.toFree.len: @@ -341,13 +359,14 @@ proc collectCycles() = getOccupiedMem()) proc registerCycle(s: Cell; desc: PNimTypeV2) = - s.rootIdx = roots.len + s.rootIdx = roots.len+1 if roots.d == nil: init(roots) add(roots, s, desc) if roots.len >= rootsThreshold: collectCycles() - #writeCell("[added root]", s) + when logOrc: + writeCell("[added root]", s, desc) proc GC_runOrc* = ## Forces a cycle collection pass. @@ -379,17 +398,19 @@ proc GC_disableMarkAndSweep*() = ## For ``--gc:orc`` an alias for ``GC_disableOrc``. GC_disableOrc() +when optimizedOrc: + template markedAsCyclic(s: Cell): bool = (s.rc and maybeCycle) != 0 +else: + template markedAsCyclic(s: Cell): bool = true + proc rememberCycle(isDestroyAction: bool; s: Cell; desc: PNimTypeV2) {.noinline.} = if isDestroyAction: - if (s.rc and isCycleCandidate) != 0: - s.rc = s.rc and not isCycleCandidate + if s.rootIdx > 0: unregisterCycle(s) else: # do not call 'rememberCycle' again unless this cell # got an 'incRef' event: - #s.setColor colGreen # XXX This is wrong! - if (s.rc and isCycleCandidate) == 0: - s.rc = s.rc or isCycleCandidate + if s.rootIdx == 0 and markedAsCyclic(s): s.setColor colBlack registerCycle(s, desc) diff --git a/tests/arc/tasyncleak.nim b/tests/arc/tasyncleak.nim index 72aa3d4c2..417b67edb 100644 --- a/tests/arc/tasyncleak.nim +++ b/tests/arc/tasyncleak.nim @@ -1,5 +1,5 @@ discard """ - outputsub: "(allocCount: 4016, deallocCount: 4014)" + outputsub: "(allocCount: 4014, deallocCount: 4012)" cmd: "nim c --gc:orc -d:nimAllocStats $file" """ From 2773efa03463edc6c98614d895cc21221080f6cf Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 18 Nov 2020 23:06:16 +0100 Subject: [PATCH 1021/3768] fixes db_mysql broken quoting; refs https://github.com/nim-lang/Nim/commit/c16ee37a7106c645a0d17cc6bd8d399e20f61d96#r44209990 [backport:1.4] (#16035) --- lib/impure/db_mysql.nim | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/impure/db_mysql.nim b/lib/impure/db_mysql.nim index 82f27537c..f0d9d8540 100644 --- a/lib/impure/db_mysql.nim +++ b/lib/impure/db_mysql.nim @@ -130,7 +130,6 @@ proc dbQuote*(s: string): string = of '\r': result.add "\\r" of '\x1a': result.add "\\Z" of '"': result.add "\\\"" - of '%': result.add "\\%" of '\'': result.add "\\'" of '\\': result.add "\\\\" of '_': result.add "\\_" From 557dcfd87d6801138702e087935ad3c7053d5e87 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 18 Nov 2020 14:28:52 -0800 Subject: [PATCH 1022/3768] fix #16033 nim js --gc:arc works and ignores --gc:arc (#16036) --- compiler/commands.nim | 5 ++++- tests/arc/t16033.nim | 10 ++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 tests/arc/t16033.nim diff --git a/compiler/commands.nim b/compiler/commands.nim index 7a94fb612..1ad5ec64c 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -499,7 +499,10 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "project": processOnOffSwitchG(conf, {optWholeProject, optGenIndex}, arg, pass, info) of "gc": - if conf.backend == backendJs: + if conf.backend == backendJs or conf.command == "js": + # for: bug #16033 + # This might still be imperfect, in rarse corner cases + # (where command is reset in nimscript, maybe). return expectArg(conf, switch, arg, pass, info) if pass in {passCmd2, passPP}: diff --git a/tests/arc/t16033.nim b/tests/arc/t16033.nim new file mode 100644 index 000000000..59ed22e4d --- /dev/null +++ b/tests/arc/t16033.nim @@ -0,0 +1,10 @@ +discard """ + targets: "c js" + matrix: "--gc:arc" +""" + +# bug #16033 +when defined js: + doAssert not compileOption("gc", "arc") +else: + doAssert compileOption("gc", "arc") From bf8421a2fc7148c41a1073f3e8648d18d78f8f7e Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 18 Nov 2020 14:29:32 -0800 Subject: [PATCH 1023/3768] contributing docs: always mentioned referenced issues in PR body (#16038) --- doc/contributing.rst | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/doc/contributing.rst b/doc/contributing.rst index 6ddbd65bc..0e87ebc1d 100644 --- a/doc/contributing.rst +++ b/doc/contributing.rst @@ -378,7 +378,7 @@ General commit rules 1. Important, critical bugfixes that have a tiny chance of breaking somebody's code should be backported to the latest stable release - branch (currently 1.2.x) and maybe also to the 1.0 branch. + branch (currently 1.4.x) and maybe also all the way back to the 1.0.x branch. The commit message should contain the tag ``[backport]`` for "backport to all stable releases" and the tag ``[backport:$VERSION]`` for backporting to the given $VERSION. @@ -417,7 +417,14 @@ General commit rules close it when the PR is committed), wheres issue ``#124`` is referenced (e.g.: partially fixed) and won't close the issue when committed. -6. Commits should be always be rebased against devel (so a fast forward +6. PR body (not just PR title) should contain references to fixed/referenced github + issues, e.g.: `fix #123` or `refs #123`. This is so that you get proper cross + referencing from linked issue to the PR (github won't make those links with just + PR title, and commit messages aren't always sufficient to ensure that, e.g. + can't be changed after a PR is merged). + + +7. Commits should be always be rebased against devel (so a fast forward merge can happen) e.g.: use ``git pull --rebase origin devel``. This is to avoid messing up @@ -426,7 +433,7 @@ General commit rules squash all commits using the script shown in https://github.com/nim-lang/Nim/pull/9356 -7. Do not mix pure formatting changes (e.g. whitespace changes, nimpretty) or +8. Do not mix pure formatting changes (e.g. whitespace changes, nimpretty) or automated changes (e.g. nimfix) with other code changes: these should be in separate commits (and the merge on GitHub should not squash these into 1). From acf8316e502cf46358df220ef4250b7ca96c6c64 Mon Sep 17 00:00:00 2001 From: cooldome Date: Wed, 18 Nov 2020 22:30:28 +0000 Subject: [PATCH 1024/3768] Disallow nil dereference at compile time (#16032) * bring back the semfold of nil * remove space * fix test * proc type can't be dereferenced * disallow nil dereference at compile time * changelog --- changelog.md | 2 +- compiler/semexprs.nim | 5 +++++ compiler/semfold.nim | 4 ++++ tests/misc/tcast.nim | 3 ++- 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index 7cbd4bb66..d53f5b038 100644 --- a/changelog.md +++ b/changelog.md @@ -51,7 +51,7 @@ - The `cstring` doesn't support `[]=` operator in JS backend. - +- nil dereference is not allowed at compile time. `cast[ptr int](nil)[]` is rejected at compile time. ## Compiler changes diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index cb119b3e8..608a82715 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1440,6 +1440,11 @@ proc buildOverloadedSubscripts(n: PNode, ident: PIdent): PNode = proc semDeref(c: PContext, n: PNode): PNode = checkSonsLen(n, 1, c.config) n[0] = semExprWithType(c, n[0]) + let a = getConstExpr(c.module, n[0], c.idgen, c.graph) + if a != nil: + if a.kind == nkNilLit: + localError(c.config, n.info, "nil dereference is not allowed") + n[0] = a result = n var t = skipTypes(n[0].typ, {tyGenericInst, tyVar, tyLent, tyAlias, tySink, tyOwned}) case t.kind diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 9b21c5fa3..b92895b19 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -671,6 +671,10 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode var a = getConstExpr(m, n[1], idgen, g) if a == nil: return result = foldConv(n, a, g, check=true) + of nkDerefExpr, nkHiddenDeref: + let a = getConstExpr(m, n[0], idgen, g) + if a != nil and a.kind == nkNilLit: + localError(g.config, n.info, "nil dereference is not allowed") of nkCast: var a = getConstExpr(m, n[1], idgen, g) if a == nil: return diff --git a/tests/misc/tcast.nim b/tests/misc/tcast.nim index 6c6de6f8d..454801a2d 100644 --- a/tests/misc/tcast.nim +++ b/tests/misc/tcast.nim @@ -33,7 +33,8 @@ reject: discard cast[ptr](a) # bug #15623 block: if false: - echo cast[ptr int](nil)[] + let x = cast[ptr int](nil) + echo x[] block: if false: From 44442520bc4e402ca8fe5fd60f2601c40f28a9a5 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 18 Nov 2020 19:41:01 -0800 Subject: [PATCH 1025/3768] fix numbering in contributing.rst (#16042) --- doc/contributing.rst | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/doc/contributing.rst b/doc/contributing.rst index 0e87ebc1d..90330e4f4 100644 --- a/doc/contributing.rst +++ b/doc/contributing.rst @@ -407,7 +407,6 @@ General commit rules #!/bin/sh git diff --check --cached || exit $? - 5. Describe your commit and use your common sense. Example commit message: @@ -418,11 +417,10 @@ General commit rules (e.g.: partially fixed) and won't close the issue when committed. 6. PR body (not just PR title) should contain references to fixed/referenced github - issues, e.g.: `fix #123` or `refs #123`. This is so that you get proper cross - referencing from linked issue to the PR (github won't make those links with just - PR title, and commit messages aren't always sufficient to ensure that, e.g. - can't be changed after a PR is merged). - + issues, e.g.: `fix #123` or `refs #123`. This is so that you get proper cross + referencing from linked issue to the PR (github won't make those links with just + PR title, and commit messages aren't always sufficient to ensure that, e.g. + can't be changed after a PR is merged). 7. Commits should be always be rebased against devel (so a fast forward merge can happen) From 743f2f5faabecc8ed86837d7ae39319300c68817 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 19 Nov 2020 08:30:23 +0100 Subject: [PATCH 1026/3768] makes parsesql .gcsafe [backport:1.0] (#16039) --- lib/pure/parsesql.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/pure/parsesql.nim b/lib/pure/parsesql.nim index 68e75e5fa..0232baf8d 100644 --- a/lib/pure/parsesql.nim +++ b/lib/pure/parsesql.nim @@ -679,8 +679,8 @@ proc getPrecedence(p: SqlParser): int = else: result = - 1 -proc parseExpr(p: var SqlParser): SqlNode -proc parseSelect(p: var SqlParser): SqlNode +proc parseExpr(p: var SqlParser): SqlNode {.gcsafe.} +proc parseSelect(p: var SqlParser): SqlNode {.gcsafe.} proc identOrLiteral(p: var SqlParser): SqlNode = case p.tok.kind @@ -1208,7 +1208,7 @@ proc addIden(s: var SqlWriter, thing: string) = iden = '"' & iden & '"' s.add(iden) -proc ra(n: SqlNode, s: var SqlWriter) +proc ra(n: SqlNode, s: var SqlWriter) {.gcsafe.} proc rs(n: SqlNode, s: var SqlWriter, prefix = "(", suffix = ")", sep = ", ") = if n.len > 0: From 1bcc3521371ec6a8c136e42d902a0be7c50d9745 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Thu, 19 Nov 2020 15:31:54 +0800 Subject: [PATCH 1027/3768] close #14847(add testcase for #14847) (#16045) --- tests/macros/t14847.nim | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 tests/macros/t14847.nim diff --git a/tests/macros/t14847.nim b/tests/macros/t14847.nim new file mode 100644 index 000000000..0e6d0dd2d --- /dev/null +++ b/tests/macros/t14847.nim @@ -0,0 +1,20 @@ +discard """ + output: "98" +""" +import macros + +#bug #14847 +proc hello*(b: string) = + echo b + +macro dispatch(pro: typed, params: untyped): untyped = + var impl = pro.getImpl + let id = ident(pro.strVal & "_coverage") + impl[0] = id + let call = newCall(id, params) + + result = newStmtList() + result.add(impl) + result.add(call) + +dispatch(hello, "98") From 1efd11e2666403fecf1b9b3b933c1adca13d8069 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 19 Nov 2020 13:42:16 +0100 Subject: [PATCH 1028/3768] fixes #15942 [backport:1.2] [backport:1.4] (#16051) --- compiler/cgen.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index f0120c435..c2a31b775 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -244,7 +244,7 @@ proc safeLineNm(info: TLineInfo): int = proc genCLineDir(r: var Rope, filename: string, line: int; conf: ConfigRef) = assert line >= 0 - if optLineDir in conf.options: + if optLineDir in conf.options and line > 0: r.addf("$N#line $2 $1$N", [rope(makeSingleLineCString(filename)), rope(line)]) From b3c3557c338c6cb0992abf9d2d877e927d815ea3 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 19 Nov 2020 12:12:51 -0800 Subject: [PATCH 1029/3768] fix #16025 repr now consistent: does not insert trailing newline (#16034) --- changelog.md | 2 + lib/system.nim | 3 + lib/system/repr.nim | 3 +- lib/system/reprjs.nim | 3 +- tests/arc/trepr.nim | 3 + tests/array/tarray.nim | 3 - tests/js/trepr.nim | 73 ++++++++++------------- tests/metatype/tmetatype_various.nim | 3 +- tests/misc/tunsignedconv.nim | 3 +- tests/stdlib/trepr.nim | 87 +++++++++++++++++++--------- tests/stdlib/tstreams.nim | 1 - tests/vm/tmisc_vm.nim | 5 +- 12 files changed, 105 insertions(+), 84 deletions(-) diff --git a/changelog.md b/changelog.md index d53f5b038..4c18ddf1e 100644 --- a/changelog.md +++ b/changelog.md @@ -44,6 +44,8 @@ - `os.FileInfo` (returned by `getFileInfo`) now contains `blockSize`, determining preferred I/O block size for this file object. +- `repr` now doesn't insert trailing newline; previous behavior was very inconsistent, + see #16034. Use `-d:nimLegacyReprWithNewline` for previous behavior. ## Language changes diff --git a/lib/system.nim b/lib/system.nim index 1104cb17f..716eb296a 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1334,6 +1334,9 @@ proc insert*[T](x: var seq[T], item: sink T, i = 0.Natural) {.noSideEffect.} = when not defined(nimV2): proc repr*[T](x: T): string {.magic: "Repr", noSideEffect.} ## Takes any Nim variable and returns its string representation. + ## No trailing newline is inserted (so `echo` won't add an empty newline). + ## Use `-d:nimLegacyReprWithNewline` to revert to old behavior where newlines + ## were added in some cases. ## ## It works even for complex data graphs with cycles. This is a great ## debugging tool. diff --git a/lib/system/repr.nim b/lib/system/repr.nim index 318e95ebb..ae51a4aab 100644 --- a/lib/system/repr.nim +++ b/lib/system/repr.nim @@ -325,5 +325,6 @@ when not defined(useNimRtl): else: var p = p reprAux(result, addr(p), typ, cl) - add result, "\n" + when defined(nimLegacyReprWithNewline): # see PR #16034 + add result, "\n" deinitReprClosure(cl) diff --git a/lib/system/reprjs.nim b/lib/system/reprjs.nim index 9c27a4721..36972024a 100644 --- a/lib/system/reprjs.nim +++ b/lib/system/reprjs.nim @@ -237,4 +237,5 @@ proc reprAny(p: pointer, typ: PNimType): string {.compilerRtl.} = var cl: ReprClosure initReprClosure(cl) reprAux(result, p, typ, cl) - add(result, "\n") + when defined(nimLegacyReprWithNewline): # see PR #16034 + add result, "\n" diff --git a/tests/arc/trepr.nim b/tests/arc/trepr.nim index 9eb2ed43f..3c1e4129c 100644 --- a/tests/arc/trepr.nim +++ b/tests/arc/trepr.nim @@ -11,6 +11,9 @@ Obj(member: ref @["hello"]) ref (member: ref @["hello"]) ''' """ + +# xxx consider merging with `tests/stdlib/trepr.nim` to increase overall test coverage + import tables type diff --git a/tests/array/tarray.nim b/tests/array/tarray.nim index 27288bab3..eadb53ac1 100644 --- a/tests/array/tarray.nim +++ b/tests/array/tarray.nim @@ -1,11 +1,8 @@ discard """ output: ''' [4, 5, 6] - [16, 25, 36] - [16, 25, 36] - apple banana Fruit diff --git a/tests/js/trepr.nim b/tests/js/trepr.nim index eb285f70d..c63b64d3a 100644 --- a/tests/js/trepr.nim +++ b/tests/js/trepr.nim @@ -1,6 +1,4 @@ -discard """ - action: run -""" +# xxx consider merging with `tests/stdlib/trepr.nim` to increase overall test coverage block ints: let @@ -137,15 +135,13 @@ block tuples: when defined js: doAssert(repr(ot) == """ [Field0 = true, -Field1 = 120] -""") +Field1 = 120]""") doAssert(repr(t) == """ [Field0 = 42, Field1 = 12.34, Field2 = "tuple", Field3 = [Field0 = true, -Field1 = 120]] -""") +Field1 = 120]]""") block objects: type @@ -162,14 +158,12 @@ block objects: doAssert(repr(oo) == """ [a = true, -b = 120] -""") +b = 120]""") doAssert(repr(o) == """ [a = 42, b = 12.34, c = [a = true, -b = 120]] -""") +b = 120]]""") block arrays: type @@ -183,15 +177,14 @@ block arrays: c = [o, o, o] d = ["hi", "array", "!"] - doAssert(repr(a) == "[0.0, 1.0, 2.0]\n") - doAssert(repr(b) == "[[0.0, 1.0, 2.0], [0.0, 1.0, 2.0], [0.0, 1.0, 2.0]]\n") + doAssert(repr(a) == "[0.0, 1.0, 2.0]") + doAssert(repr(b) == "[[0.0, 1.0, 2.0], [0.0, 1.0, 2.0], [0.0, 1.0, 2.0]]") doAssert(repr(c) == """ [[x = 42, y = [0.0, 1.0, 2.0]], [x = 42, y = [0.0, 1.0, 2.0]], [x = 42, -y = [0.0, 1.0, 2.0]]] -""") - doAssert(repr(d) == "[\"hi\", \"array\", \"!\"]\n") +y = [0.0, 1.0, 2.0]]]""") + doAssert(repr(d) == "[\"hi\", \"array\", \"!\"]") block seqs: type @@ -205,15 +198,14 @@ block seqs: c = @[o, o, o] d = @["hi", "array", "!"] - doAssert(repr(a) == "@[0.0, 1.0, 2.0]\n") - doAssert(repr(b) == "@[@[0.0, 1.0, 2.0], @[0.0, 1.0, 2.0], @[0.0, 1.0, 2.0]]\n") + doAssert(repr(a) == "@[0.0, 1.0, 2.0]") + doAssert(repr(b) == "@[@[0.0, 1.0, 2.0], @[0.0, 1.0, 2.0], @[0.0, 1.0, 2.0]]") doAssert(repr(c) == """ @[[x = 42, y = @[0.0, 1.0, 2.0]], [x = 42, y = @[0.0, 1.0, 2.0]], [x = 42, -y = @[0.0, 1.0, 2.0]]] -""") - doAssert(repr(d) == "@[\"hi\", \"array\", \"!\"]\n") +y = @[0.0, 1.0, 2.0]]]""") + doAssert(repr(d) == "@[\"hi\", \"array\", \"!\"]") block ptrs: type @@ -226,13 +218,12 @@ block ptrs: c = addr a[2] d = AObj() - doAssert(repr(a) == "[12.0, 13.0, 14.0]\n") - doAssert(repr(b) == "ref 0 --> 12.0\n") - doAssert(repr(c) == "ref 2 --> 14.0\n") + doAssert(repr(a) == "[12.0, 13.0, 14.0]") + doAssert(repr(b) == "ref 0 --> 12.0") + doAssert(repr(c) == "ref 2 --> 14.0") doAssert(repr(d) == """ [x = nil, -y = 0] -""") +y = 0]""") block ptrs: type @@ -248,8 +239,7 @@ block ptrs: [x = ref 0 --> [[x = nil, y = 0], [x = nil, y = 0]], -y = 0] -""") +y = 0]""") block procs: proc test(): int = @@ -258,9 +248,9 @@ block procs: ptest = test nilproc: proc(): int - doAssert(repr(test) == "0\n") - doAssert(repr(ptest) == "0\n") - doAssert(repr(nilproc) == "nil\n") + doAssert(repr(test) == "0") + doAssert(repr(ptest) == "0") + doAssert(repr(nilproc) == "nil") block bunch: type @@ -322,8 +312,7 @@ o = [Field0 = [a = "", b = @[]], Field1 = ""], p = nil, -q = nil] -""") +q = nil]""") doAssert(repr(cc) == """ [a = 12, b = 1, @@ -346,8 +335,7 @@ o = [Field0 = [a = "inner", b = @['o', 'b', 'j']], Field1 = "tuple!"], p = 0, -q = "cstringtest"] -""") +q = "cstringtest"]""") block another: type @@ -358,9 +346,9 @@ block another: Size3 = enum s3e=0, s3f=2000000000 - doAssert(repr([s1a, s1b]) == "[s1a, s1b]\n") - doAssert(repr([s2c, s2d]) == "[s2c, s2d]\n") - doAssert(repr([s3e, s3f]) == "[s3e, s3f]\n") + doAssert(repr([s1a, s1b]) == "[s1a, s1b]") + doAssert(repr([s2c, s2d]) == "[s2c, s2d]") + doAssert(repr([s3e, s3f]) == "[s3e, s3f]") block another2: @@ -395,15 +383,13 @@ block another2: y = 13, z = 45, s = ["abc", "xyz"], -e = en6] -""") +e = en6]""") doAssert(repr(q) == """ ref 0 --> [x = 0, y = 13, z = 45, s = ["abc", "xyz"], -e = en6] -""") +e = en6]""") doAssert(repr(s) == """ @[ref 0 --> [x = 0, y = 13, @@ -421,8 +407,7 @@ e = en6], ref 3 --> [x = 0, y = 13, z = 45, s = ["abc", "xyz"], -e = en6]] -""") +e = en6]]""") doAssert(repr(en4) == "en4") doAssert(repr({'a'..'p'}) == "{'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p'}") diff --git a/tests/metatype/tmetatype_various.nim b/tests/metatype/tmetatype_various.nim index 0dd948293..9faeeec4a 100644 --- a/tests/metatype/tmetatype_various.nim +++ b/tests/metatype/tmetatype_various.nim @@ -1,6 +1,5 @@ discard """ - output: '''[1, 0, 0, 0, 0, 0, 0, 0] - CTBool[Ct[system.uint32]]''' + output: '''[1, 0, 0, 0, 0, 0, 0, 0] CTBool[Ct[system.uint32]]''' """ block tconstraints: diff --git a/tests/misc/tunsignedconv.nim b/tests/misc/tunsignedconv.nim index e9a2a2189..b9463b5f0 100644 --- a/tests/misc/tunsignedconv.nim +++ b/tests/misc/tunsignedconv.nim @@ -67,8 +67,7 @@ let limit = 1'u64 let rangeVar = 0'u64 ..< limit doAssert repr(rangeVar) == """[a = 0, -b = 0] -""" +b = 0]""" # bug #15210 diff --git a/tests/stdlib/trepr.nim b/tests/stdlib/trepr.nim index c1941bd38..1d89d37b2 100644 --- a/tests/stdlib/trepr.nim +++ b/tests/stdlib/trepr.nim @@ -1,36 +1,69 @@ discard """ - output: '''{a, b}{'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'} -[1, 2, 3, 4, 5, 6]''' + targets: "c cpp js" + matrix: ";--gc:arc" """ -type - TEnum = enum - a, b +# if excessive, could remove 'cpp' from targets -var val = {a, b} -stdout.write(repr(val)) -stdout.writeLine(repr({'a'..'z', 'A'..'Z'})) +from strutils import endsWith, contains -type - TObj {.pure, inheritable.} = object - data: int - TFoo = ref object of TObj - d2: float -var foo: TFoo -new(foo) +template main() = + doAssert repr({3,5}) == "{3, 5}" -when false: - # cannot capture this output as it contains a memory address :-/ - echo foo.repr -#var testseq: seq[string] = @[ -# "a", "b", "c", "d", "e" -#] -#echo(repr(testseq)) + block: + type TEnum = enum a, b + var val = {a, b} + when nimvm: + discard + #[ + # BUG: + {0, 1} + {97..99, 65..67} + ]# + else: + doAssert repr(val) == "{a, b}" + doAssert repr({'a'..'c', 'A'..'C'}) == "{'A', 'B', 'C', 'a', 'b', 'c'}" -# bug #7878 -proc test(variable: var openarray[int]) = - echo repr(variable) + type + TObj {.pure, inheritable.} = object + data: int + TFoo = ref object of TObj + d2: float + var foo: TFoo + new(foo) -var arr = [1, 2, 3, 4, 5, 6] + #[ + BUG: + --gc:arc returns `"abc"` + regular gc returns with address, e.g. 0x1068aae60"abc", but only + for c,cpp backends (not js, vm) + ]# + block: + doAssert repr("abc").endsWith "\"abc\"" + var b: cstring = "def" + doAssert repr(b).endsWith "\"def\"" -test(arr) + block: + var c = @[1,2] + when nimvm: + discard # BUG: this shows [1, 2] instead of @[1, 2] + else: + # BUG (already mentioned above): some backends / gc show address, others don't + doAssert repr(c).endsWith "@[1, 2]" + + let d = @["foo", "bar"] + let s = repr(d) + # depending on backend/gc, we get 0x106a1c350@[0x106a1c390"foo", 0x106a1c3c0"bar"] + doAssert "\"foo\"," in s + + var arr = [1, 2, 3] + doAssert repr(arr) == "[1, 2, 3]" + + block: # bug #7878 + proc reprOpenarray(variable: var openarray[int]): string = repr(variable) + when defined(js): discard # BUG: doesn't work + else: + doAssert reprOpenarray(arr) == "[1, 2, 3]" + +static: main() +main() diff --git a/tests/stdlib/tstreams.nim b/tests/stdlib/tstreams.nim index 6979bae38..0fe6b6933 100644 --- a/tests/stdlib/tstreams.nim +++ b/tests/stdlib/tstreams.nim @@ -4,7 +4,6 @@ discard """ Hello! What is your name? Nice name: Arne fs is: nil - threw exception _heh_ ''' diff --git a/tests/vm/tmisc_vm.nim b/tests/vm/tmisc_vm.nim index 6eeecb869..2d3e30c5e 100644 --- a/tests/vm/tmisc_vm.nim +++ b/tests/vm/tmisc_vm.nim @@ -1,7 +1,6 @@ discard """ - output: '''[127, 127, 0, 255] -[127, 127, 0, 255] - + output: ''' +[127, 127, 0, 255][127, 127, 0, 255] (data: 1) ''' From 59332ec2352aa1d1c0fa4401eff7fefe8f4187da Mon Sep 17 00:00:00 2001 From: Antonis Geralis <43617260+planetis-m@users.noreply.github.com> Date: Fri, 20 Nov 2020 02:09:30 +0200 Subject: [PATCH 1030/3768] Improve enumerate (#16053) * Improve enumerate * updates * add tests --- lib/std/enumerate.nim | 53 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/lib/std/enumerate.nim b/lib/std/enumerate.nim index 4620af94d..3def653c8 100644 --- a/lib/std/enumerate.nim +++ b/lib/std/enumerate.nim @@ -28,29 +28,62 @@ macro enumerate*(x: ForLoopStmt): untyped {.since: (1, 3).} = let c = "abcd" var d: seq[(int, char)] - for i, x in enumerate(97, c): + for (i, x) in enumerate(97, c): d.add((i, x)) assert d == @[(97, 'a'), (98, 'b'), (99, 'c'), (100, 'd')] + template genCounter(x): untyped = + # We strip off the first for loop variable and use it as an integer counter. + # We must immediately decrement it by one, because it gets incremented before + # the loop body - to be able to use the final expression in other macros. + newVarStmt(x, infix(countStart, "-", newLit(1))) + + template genInc(x): untyped = + newCall(bindSym"inc", x) + expectKind x, nnkForStmt # check if the starting count is specified: var countStart = if x[^2].len == 2: newLit(0) else: x[^2][1] result = newStmtList() - # We strip off the first for loop variable and use it as an integer counter. - # We must immediately decrement it by one, because it gets incremented before - # the loop body - to be able to use the final expression in other macros. - result.add newVarStmt(x[0], infix(countStart, "-", newLit(1))) var body = x[^1] if body.kind != nnkStmtList: body = newTree(nnkStmtList, body) - body.insert(0, newCall(bindSym"inc", x[0])) var newFor = newTree(nnkForStmt) - for i in 1..x.len-3: - newFor.add x[i] + if x.len == 3: # single iteration variable + if x[0].kind == nnkVarTuple: # for (x, y, ...) in iter + result.add genCounter(x[0][0]) + body.insert(0, genInc(x[0][0])) + for i in 1 .. x[0].len-2: + newFor.add x[0][i] + else: + error("Missing second for loop variable") # for x in iter + else: # for x, y, ... in iter + result.add genCounter(x[0]) + body.insert(0, genInc(x[0])) + for i in 1 .. x.len-3: + newFor.add x[i] # transform enumerate(X) to 'X' newFor.add x[^2][^1] newFor.add body result.add newFor # now wrap the whole macro in a block to create a new scope - result = quote do: - block: `result` + result = newBlockStmt(result) + +when isMainModule: + let a = @[1, 3, 5, 7] + + block: + var res: seq[(int, int)] + for i, x in enumerate(a): + res.add (i, x) + assert res == @[(0, 1), (1, 3), (2, 5), (3, 7)] + block: + var res: seq[(int, int)] + for (i, x) in enumerate(a.items): + res.add (i, x) + assert res == @[(0, 1), (1, 3), (2, 5), (3, 7)] + block: + var res: seq[(int, int)] + for i, x in enumerate(3, a): + res.add (i, x) + assert res == @[(3, 1), (4, 3), (5, 5), (6, 7)] From 109cc45398bcdafc22f0a5e49cdfda8234bc46de Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 19 Nov 2020 23:07:51 -0800 Subject: [PATCH 1031/3768] packedsets fix regression introduced in #15564 (#16060) * packedsets fix regression introduced in #15564 * add tests --- lib/std/packedsets.nim | 17 +++++++++-------- tests/stdlib/mintsets.nim | 10 ++++++++++ tests/stdlib/tintsets.nim | 6 ++++++ 3 files changed, 25 insertions(+), 8 deletions(-) create mode 100644 tests/stdlib/mintsets.nim create mode 100644 tests/stdlib/tintsets.nim diff --git a/lib/std/packedsets.nim b/lib/std/packedsets.nim index 20002d07f..be0555d3a 100644 --- a/lib/std/packedsets.nim +++ b/lib/std/packedsets.nim @@ -265,7 +265,7 @@ proc incl*[A](s: var PackedSet[A], other: PackedSet[A]) = assert len(a) == 2 assert 5 in a - for item in other: incl(s, item) + for item in other.items: incl(s, item) proc toPackedSet*[A](x: openArray[A]): PackedSet[A] {.since: (1, 3).} = ## Creates a new PackedSet[A] that contains the elements of `x`. @@ -358,7 +358,7 @@ proc excl*[A](s: var PackedSet[A], other: PackedSet[A]) = assert len(a) == 1 assert 5 notin a - for item in other: + for item in other.items: excl(s, item) proc len*[A](s: PackedSet[A]): int {.inline.} = @@ -367,7 +367,8 @@ proc len*[A](s: PackedSet[A]): int {.inline.} = result = s.elems else: result = 0 - for _ in s: + for _ in s.items: + # pending bug #11167; when fixed, check each explicit `items` to see if it can be removed inc(result) proc missingOrExcl*[A](s: var PackedSet[A], key: A): bool = @@ -488,7 +489,7 @@ proc intersection*[A](s1, s2: PackedSet[A]): PackedSet[A] = ## {3} result = initPackedSet[A]() - for item in s1: + for item in s1.items: if contains(s2, item): incl(result, item) @@ -506,7 +507,7 @@ proc difference*[A](s1, s2: PackedSet[A]): PackedSet[A] = ## {1, 2} result = initPackedSet[A]() - for item in s1: + for item in s1.items: if not contains(s2, item): incl(result, item) @@ -522,7 +523,7 @@ proc symmetricDifference*[A](s1, s2: PackedSet[A]): PackedSet[A] = ## {1, 2, 4, 5} result.assign(s1) - for item in s2: + for item in s2.items: if containsOrIncl(result, item): excl(result, item) proc `+`*[A](s1, s2: PackedSet[A]): PackedSet[A] {.inline.} = @@ -549,7 +550,7 @@ proc disjoint*[A](s1, s2: PackedSet[A]): bool = b.excl(2) assert disjoint(a, b) == true - for item in s1: + for item in s1.items: if contains(s2, item): return false return true @@ -575,7 +576,7 @@ proc `<=`*[A](s1, s2: PackedSet[A]): bool = a.incl(3) assert(not (a <= b)) - for item in s1: + for item in s1.items: if not s2.contains(item): return false return true diff --git a/tests/stdlib/mintsets.nim b/tests/stdlib/mintsets.nim new file mode 100644 index 000000000..b4d9ed516 --- /dev/null +++ b/tests/stdlib/mintsets.nim @@ -0,0 +1,10 @@ +import std/intsets + +proc test1*[]() = + let a = initIntSet() + doAssert len(a) == 0 + +proc test2*[]() = + var a = initIntSet() + var b = initIntSet() + a.incl b diff --git a/tests/stdlib/tintsets.nim b/tests/stdlib/tintsets.nim new file mode 100644 index 000000000..191ef117e --- /dev/null +++ b/tests/stdlib/tintsets.nim @@ -0,0 +1,6 @@ +import ./mintsets + +block: # bug https://github.com/nim-lang/Nim/pull/15564#issuecomment-729878104 + # related to bug #11167 + test1() + test2() From 6cf5ca1dc2c1c0388e0998a24d26f4a9c9b7e60e Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Fri, 20 Nov 2020 16:06:18 +0800 Subject: [PATCH 1032/3768] alternative way to fix #16022 (#16064) [backport:1.4] * alternative way to fix #16022 * add testcase for #16022 [backport:1.4] --- lib/core/typeinfo.nim | 4 ++-- tests/stdlib/tmarshal.nim | 9 ++++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/lib/core/typeinfo.nim b/lib/core/typeinfo.nim index 00f7c176c..ca531b4b9 100644 --- a/lib/core/typeinfo.nim +++ b/lib/core/typeinfo.nim @@ -327,10 +327,10 @@ proc setPointer*(x: Any, y: pointer) = ## ``akString``, ``akCString``, ``akProc``, ``akRef``, ``akPtr``, ## ``akPointer``, ``akSequence``. assert x.rawType.kind in pointerLike - if y != nil: + if y != nil and x.rawType.kind != tyPointer: genericAssign(x.value, y, x.rawType) else: - cast[ppointer](x.value)[] = nil + cast[ppointer](x.value)[] = y proc fieldsAux(p: pointer, n: ptr TNimNode, ret: var seq[tuple[name: cstring, any: Any]]) = diff --git a/tests/stdlib/tmarshal.nim b/tests/stdlib/tmarshal.nim index 315036631..d76be73f3 100644 --- a/tests/stdlib/tmarshal.nim +++ b/tests/stdlib/tmarshal.nim @@ -7,6 +7,7 @@ omega 200 Some(null) None[JsonNode] (numeric: "") +hello world ''' joinable: false """ @@ -138,7 +139,7 @@ block: echo ($$a2).to[:Option[JsonNode]] -# bug #15620 +# bug #15620 block: let str = """{"numeric": null}""" @@ -148,3 +149,9 @@ block: let test = to[LegacyEntry](str) echo test + +# bug #16022 +block: + let p: proc () = proc () = echo "hello world" + let poc = (to[typeof(p)]($$p)) + poc() From 7815ed69d4a3fc7b96c397c7820cef290f5b0a4d Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 20 Nov 2020 01:51:14 -0800 Subject: [PATCH 1033/3768] rename loadConfigsAndRunMainCommand => loadConfigsAndProcessCmdLine, reflect reality (#16057) --- compiler/cmdlinehelper.nim | 2 +- compiler/nim.nim | 2 +- drnim/drnim.nim | 2 +- nimsuggest/nimsuggest.nim | 4 ++-- tools/nimfind.nim | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/cmdlinehelper.nim b/compiler/cmdlinehelper.nim index d5a26717d..ffbb0d40b 100644 --- a/compiler/cmdlinehelper.nim +++ b/compiler/cmdlinehelper.nim @@ -57,7 +57,7 @@ proc processCmdLineAndProjectPath*(self: NimProg, conf: ConfigRef) = else: conf.projectPath = AbsoluteDir canonicalizePath(conf, AbsoluteFile getCurrentDir()) -proc loadConfigsAndRunMainCommand*(self: NimProg, cache: IdentCache; conf: ConfigRef; +proc loadConfigsAndProcessCmdLine*(self: NimProg, cache: IdentCache; conf: ConfigRef; graph: ModuleGraph): bool = if self.suggestMode: conf.command = "nimsuggest" diff --git a/compiler/nim.nim b/compiler/nim.nim index 9ff0eedbc..e3725f803 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -79,7 +79,7 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = self.processCmdLineAndProjectPath(conf) var graph = newModuleGraph(cache, conf) - if not self.loadConfigsAndRunMainCommand(cache, conf, graph): + if not self.loadConfigsAndProcessCmdLine(cache, conf, graph): return mainCommand(graph) if conf.hasHint(hintGCStats): echo(GC_getStatistics()) diff --git a/drnim/drnim.nim b/drnim/drnim.nim index b4761a398..d68175c83 100644 --- a/drnim/drnim.nim +++ b/drnim/drnim.nim @@ -1269,7 +1269,7 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = self.processCmdLineAndProjectPath(conf) var graph = newModuleGraph(cache, conf) - if not self.loadConfigsAndRunMainCommand(cache, conf, graph): return + if not self.loadConfigsAndProcessCmdLine(cache, conf, graph): return mainCommand(graph) if conf.hasHint(hintGCStats): echo(GC_getStatistics()) diff --git a/nimsuggest/nimsuggest.nim b/nimsuggest/nimsuggest.nim index afc692241..d438e663c 100644 --- a/nimsuggest/nimsuggest.nim +++ b/nimsuggest/nimsuggest.nim @@ -644,7 +644,7 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = myLog("START " & conf.projectFull.string) var graph = newModuleGraph(cache, conf) - if self.loadConfigsAndRunMainCommand(cache, conf, graph): + if self.loadConfigsAndProcessCmdLine(cache, conf, graph): mainCommand(graph) when isMainModule: @@ -723,7 +723,7 @@ else: myLog("START " & conf.projectFull.string) var graph = newModuleGraph(cache, conf) - if self.loadConfigsAndRunMainCommand(cache, conf, graph): + if self.loadConfigsAndProcessCmdLine(cache, conf, graph): mockCommand(graph) if gLogging: for it in conf.searchPaths: diff --git a/tools/nimfind.nim b/tools/nimfind.nim index f13e86aaa..b120ccd1f 100644 --- a/tools/nimfind.nim +++ b/tools/nimfind.nim @@ -228,7 +228,7 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = conf.prefixDir = AbsoluteDir"" var graph = newModuleGraph(cache, conf) - if self.loadConfigsAndRunMainCommand(cache, conf, graph): + if self.loadConfigsAndProcessCmdLine(cache, conf, graph): mainCommand(graph) handleCmdLine(newIdentCache(), newConfigRef()) From 81a03238eda4955dba2a6949aa6f3fe62d186ea7 Mon Sep 17 00:00:00 2001 From: satoru Date: Fri, 20 Nov 2020 17:55:14 +0800 Subject: [PATCH 1034/3768] colors: Reuse the HexDigits constant in strutils (#16065) Co-authored-by: Xuecong Liao --- lib/pure/colors.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/colors.nim b/lib/pure/colors.nim index dbcb1b4a2..57b854103 100644 --- a/lib/pure/colors.nim +++ b/lib/pure/colors.nim @@ -475,7 +475,7 @@ proc isColor*(name: string): bool = if name.len == 0: return false if name[0] == '#': for i in 1 .. name.len-1: - if name[i] notin {'0'..'9', 'a'..'f', 'A'..'F'}: return false + if name[i] notin HexDigits: return false result = true else: result = binarySearch(colorNames, name, colorNameCmp) >= 0 From 1ad7e4f30b687564d76272fcd25f13cf98ff3d69 Mon Sep 17 00:00:00 2001 From: Miran Date: Fri, 20 Nov 2020 12:31:06 +0100 Subject: [PATCH 1035/3768] fix #16047 (#16066) --- doc/tut1.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/doc/tut1.rst b/doc/tut1.rst index 415a44f5f..cb4dc1cc9 100644 --- a/doc/tut1.rst +++ b/doc/tut1.rst @@ -472,13 +472,15 @@ innermost construct, unless a label of a block is given: echo "looping" break # leaves the loop, but not the block echo "still in block" + echo "outside the block" block myblock2: echo "entering block" while true: echo "looping" break myblock2 # leaves the block (and the loop) - echo "still in block" + echo "still in block" # it won't be printed + echo "outside the block" Continue statement From 07ad608fb2a9387fefc61a8eef9e0777660516a9 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Fri, 20 Nov 2020 14:11:41 +0100 Subject: [PATCH 1036/3768] Retain backwards compatibility after #16057 (#16068) * Retain backwards compatibility * Fix typo --- compiler/cmdlinehelper.nim | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compiler/cmdlinehelper.nim b/compiler/cmdlinehelper.nim index ffbb0d40b..4f1ede5d1 100644 --- a/compiler/cmdlinehelper.nim +++ b/compiler/cmdlinehelper.nim @@ -84,3 +84,7 @@ proc loadConfigsAndProcessCmdLine*(self: NimProg, cache: IdentCache; conf: Confi graph.suggestMode = self.suggestMode return true + +proc loadConfigsAndRunMainCommand*(self: NimProg, cache: IdentCache; conf: ConfigRef; graph: ModuleGraph): bool = + ## Alias for loadConfigsAndProcessCmdLine, here for backwards compatibility + loadConfigsAndProcessCmdLine(self, cache, conf, graph) From fcb2ec4ed69fe924e7e3899e0084f534124255d4 Mon Sep 17 00:00:00 2001 From: cooldome Date: Fri, 20 Nov 2020 13:58:16 +0000 Subject: [PATCH 1037/3768] fix #15958 (#15970) [backport:1.4] * fix #15958 * also cover openArray and VarArgs * more tests * cover even more types * cover even more types * Trigger build * Trigger build * cover sets passed as arrays --- compiler/ccgtypes.nim | 3 ++- tests/lent/tbasic_lent_check.nim | 39 ++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index e3f832d68..142fec056 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -282,7 +282,8 @@ proc ccgIntroducedPtr(conf: ConfigRef; s: PSym, retType: PType): bool = result = false # first parameter and return type is 'lent T'? --> use pass by pointer if s.position == 0 and retType != nil and retType.kind == tyLent: - result = pt.kind != tyVar + result = not (pt.kind in {tyVar, tyArray, tyOpenArray, tyVarargs, tyRef, tyPtr, tyPointer} or + pt.kind == tySet and mapSetType(conf, pt) == ctArray) proc fillResult(conf: ConfigRef; param: PNode) = fillLoc(param.sym.loc, locParam, param, ~"Result", diff --git a/tests/lent/tbasic_lent_check.nim b/tests/lent/tbasic_lent_check.nim index 4389cbc6e..e22f7bb50 100644 --- a/tests/lent/tbasic_lent_check.nim +++ b/tests/lent/tbasic_lent_check.nim @@ -1,4 +1,5 @@ discard """ + targets: "c cpp" output: "1" """ @@ -14,3 +15,41 @@ proc main = doAssert(not compiles(passToVar(viewInto(x)))) main() + + +#------------------------------------------------------------------------------ +# issue #15958 + +block: + proc byLent[T](a: T): lent T = a + let a = [11,12] + let b = @[21,23] + let ss = {1, 2, 3, 5} + doAssert byLent(a) == [11,12] + doAssert byLent(a).unsafeAddr == a.unsafeAddr + doAssert byLent(b) == @[21,23] + doAssert byLent(b).unsafeAddr == b.unsafeAddr + doAssert byLent(ss) == {1, 2, 3, 5} + doAssert byLent(ss).unsafeAddr == ss.unsafeAddr + + let r = new(float) + r[] = 10.0 + doAssert byLent(r)[] == 10.0 + + let p = create(float) + p[] = 20.0 + doAssert byLent(p)[] == 20.0 + + proc byLent2[T](a: openarray[T]): lent T = a[0] + doAssert byLent2(a) == 11 + doAssert byLent2(a).unsafeAddr == a[0].unsafeAddr + doAssert byLent2(b) == 21 + doAssert byLent2(b).unsafeAddr == b[0].unsafeAddr + + proc byLent3[T](a: varargs[T]): lent T = a[1] + let + x = 10 + y = 20 + z = 30 + doAssert byLent3(x, y, z) == 20 + From d9038ed792b923cfbb9593ab82825fdb48728adc Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 20 Nov 2020 18:14:15 +0100 Subject: [PATCH 1038/3768] fixes #15671 [backport:1.4] (#15690) * fixes #15671 [backport:1.4] * progress --- compiler/typeallowed.nim | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/compiler/typeallowed.nim b/compiler/typeallowed.nim index e163b914a..c81819250 100644 --- a/compiler/typeallowed.nim +++ b/compiler/typeallowed.nim @@ -52,7 +52,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, result = nil if typ == nil: return nil if containsOrIncl(marker, typ.id): return nil - var t = skipTypes(typ, abstractInst-{tyTypeDesc}) + var t = skipTypes(typ, abstractInst-{tyTypeDesc, tySink}) case t.kind of tyVar, tyLent: if kind in {skProc, skFunc, skConst} and (views notin c.features): @@ -60,7 +60,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, elif t.kind == tyLent and kind != skResult and (views notin c.features): result = t else: - var t2 = skipTypes(t[0], abstractInst-{tyTypeDesc}) + var t2 = skipTypes(t[0], abstractInst-{tyTypeDesc, tySink}) case t2.kind of tyVar, tyLent: if taHeap notin flags: result = t2 # ``var var`` is illegal on the heap @@ -70,6 +70,8 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, of tyUncheckedArray: if kind != skParam and views notin c.features: result = t else: result = typeAllowedAux(marker, t2[0], kind, c, flags) + of tySink: + result = t else: if kind notin {skParam, skResult} and views notin c.features: result = t else: result = typeAllowedAux(marker, t2, kind, c, flags) @@ -125,12 +127,18 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, result = t else: result = typeAllowedAux(marker, t[0], kind, c, flags+{taIsOpenArray}) - of tyVarargs, tySink: + of tyVarargs: # you cannot nest openArrays/sinks/etc. if kind != skParam or taIsOpenArray in flags: result = t else: result = typeAllowedAux(marker, t[0], kind, c, flags+{taIsOpenArray}) + of tySink: + # you cannot nest openArrays/sinks/etc. + if kind != skParam or taIsOpenArray in flags or t[0].kind in {tySink, tyLent, tyVar}: + result = t + else: + result = typeAllowedAux(marker, t[0], kind, c, flags) of tyUncheckedArray: if kind != skParam and taHeap notin flags: result = t From 5e8ac485a28f12cf66fb6637e4ef86a9afc117e8 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 20 Nov 2020 14:16:59 -0800 Subject: [PATCH 1039/3768] followup #15970 strenghten regression tests (#16075) * followup #15970 strenghten regression tests * _ --- tests/lent/tbasic_lent_check.nim | 27 +++++++++++++++------------ tests/misc/taddr.nim | 5 ++--- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/tests/lent/tbasic_lent_check.nim b/tests/lent/tbasic_lent_check.nim index e22f7bb50..049fa276a 100644 --- a/tests/lent/tbasic_lent_check.nim +++ b/tests/lent/tbasic_lent_check.nim @@ -1,5 +1,5 @@ discard """ - targets: "c cpp" + targets: "c cpp js" output: "1" """ @@ -16,29 +16,28 @@ proc main = main() - -#------------------------------------------------------------------------------ -# issue #15958 - -block: +template main2 = # bug #15958 proc byLent[T](a: T): lent T = a let a = [11,12] - let b = @[21,23] + let b = @[21,23] let ss = {1, 2, 3, 5} doAssert byLent(a) == [11,12] doAssert byLent(a).unsafeAddr == a.unsafeAddr doAssert byLent(b) == @[21,23] - doAssert byLent(b).unsafeAddr == b.unsafeAddr + when not defined(js): # pending bug #16073 + doAssert byLent(b).unsafeAddr == b.unsafeAddr doAssert byLent(ss) == {1, 2, 3, 5} doAssert byLent(ss).unsafeAddr == ss.unsafeAddr let r = new(float) r[] = 10.0 - doAssert byLent(r)[] == 10.0 + when not defined(js): # pending bug #16073 + doAssert byLent(r)[] == 10.0 - let p = create(float) - p[] = 20.0 - doAssert byLent(p)[] == 20.0 + when not defined(js): # pending bug https://github.com/timotheecour/Nim/issues/372 + let p = create(float) + p[] = 20.0 + doAssert byLent(p)[] == 20.0 proc byLent2[T](a: openarray[T]): lent T = a[0] doAssert byLent2(a) == 11 @@ -53,3 +52,7 @@ block: z = 30 doAssert byLent3(x, y, z) == 20 +main2() +when false: + # bug: Error: unhandled exception: 'node' is not accessible using discriminant 'kind' of type 'TFullReg' [FieldDefect] + static: main2() diff --git a/tests/misc/taddr.nim b/tests/misc/taddr.nim index 4d0aebc01..17fccfabe 100644 --- a/tests/misc/taddr.nim +++ b/tests/misc/taddr.nim @@ -97,9 +97,8 @@ block: doAssert a2 == 10 block: - when not defined(cpp): # pending bug #15958 - let a = [11,12] - doAssert byLent(a) == [11,12] + let a = [11,12] + doAssert byLent(a) == [11,12] # bug #15958 let a2 = (11,) doAssert byLent(a2) == (11,) From f3887dea2cc1969981fc62d5c91f1bafef755582 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sun, 22 Nov 2020 04:16:35 +0800 Subject: [PATCH 1040/3768] heapqueue minor improvement (#16088) --- lib/pure/collections/heapqueue.nim | 72 +++++------------------------- tests/stdlib/theapqueue.nim | 53 ++++++++++++++++++++++ 2 files changed, 63 insertions(+), 62 deletions(-) create mode 100644 tests/stdlib/theapqueue.nim diff --git a/lib/pure/collections/heapqueue.nim b/lib/pure/collections/heapqueue.nim index ca0666651..244ccbbee 100644 --- a/lib/pure/collections/heapqueue.nim +++ b/lib/pure/collections/heapqueue.nim @@ -57,26 +57,26 @@ type HeapQueue*[T] = object data: seq[T] proc initHeapQueue*[T](): HeapQueue[T] = - ## Create a new empty heap. + ## Creates a new empty heap. ## ## See also: ## * `toHeapQueue proc <#toHeapQueue,openArray[T]>`_ discard proc len*[T](heap: HeapQueue[T]): int {.inline.} = - ## Return the number of elements of `heap`. + ## Returns the number of elements of `heap`. heap.data.len proc `[]`*[T](heap: HeapQueue[T], i: Natural): T {.inline.} = - ## Access the i-th element of `heap`. + ## Accesses the i-th element of `heap`. heap.data[i] proc heapCmp[T](x, y: T): bool {.inline.} = return (x < y) proc siftdown[T](heap: var HeapQueue[T], startpos, p: int) = - ## 'heap' is a heap at all indices >= startpos, except possibly for pos. pos - ## is the index of a leaf with a possibly out-of-order value. Restore the + ## 'heap' is a heap at all indices >= startpos, except possibly for `pos`. `pos` + ## is the index of a leaf with a possibly out-of-order value. Restores the ## heap invariant. var pos = p var newitem = heap[pos] @@ -114,7 +114,7 @@ proc siftup[T](heap: var HeapQueue[T], p: int) = siftdown(heap, startpos, pos) proc push*[T](heap: var HeapQueue[T], item: T) = - ## Push `item` onto heap, maintaining the heap invariant. + ## Pushes `item` onto heap, maintaining the heap invariant. heap.data.add(item) siftdown(heap, 0, len(heap)-1) @@ -133,7 +133,7 @@ proc toHeapQueue*[T](x: openArray[T]): HeapQueue[T] {.since: (1, 3).} = result.push(item) proc pop*[T](heap: var HeapQueue[T]): T = - ## Pop and return the smallest item from `heap`, + ## Pops and returns the smallest item from `heap`, ## maintaining the heap invariant. let lastelt = heap.data.pop() if heap.len > 0: @@ -158,7 +158,7 @@ proc del*[T](heap: var HeapQueue[T], index: Natural) = heap.siftup(index) proc replace*[T](heap: var HeapQueue[T], item: T): T = - ## Pop and return the current smallest value, and add the new item. + ## Pops and returns the current smallest value, and add the new item. ## This is more efficient than pop() followed by push(), and can be ## more appropriate when using a fixed-size heap. Note that the value ## returned may be larger than item! That constrains reasonable uses of @@ -179,7 +179,7 @@ proc pushpop*[T](heap: var HeapQueue[T], item: T): T = siftup(heap, 0) proc clear*[T](heap: var HeapQueue[T]) = - ## Remove all elements from `heap`, making it empty. + ## Removes all elements from `heap`, making it empty. runnableExamples: var heap = initHeapQueue[int]() heap.push(1) @@ -188,7 +188,7 @@ proc clear*[T](heap: var HeapQueue[T]) = heap.data.setLen(0) proc `$`*[T](heap: HeapQueue[T]): string = - ## Turn a heap into its string representation. + ## Turns a heap into its string representation. runnableExamples: var heap = initHeapQueue[int]() heap.push(1) @@ -199,55 +199,3 @@ proc `$`*[T](heap: HeapQueue[T]): string = if result.len > 1: result.add(", ") result.addQuoted(x) result.add("]") - -when isMainModule: - proc toSortedSeq[T](h: HeapQueue[T]): seq[T] = - var tmp = h - result = @[] - while tmp.len > 0: - result.add(pop(tmp)) - - block: # Simple sanity test - var heap = initHeapQueue[int]() - let data = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0] - for item in data: - push(heap, item) - doAssert(heap == data.toHeapQueue) - doAssert(heap[0] == 0) - doAssert(heap.toSortedSeq == @[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) - - block: # Test del - var heap = initHeapQueue[int]() - let data = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0] - for item in data: push(heap, item) - - heap.del(0) - doAssert(heap[0] == 1) - - heap.del(heap.find(7)) - doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 5, 6, 8, 9]) - - heap.del(heap.find(5)) - doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 6, 8, 9]) - - heap.del(heap.find(6)) - doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 8, 9]) - - heap.del(heap.find(2)) - doAssert(heap.toSortedSeq == @[1, 3, 4, 8, 9]) - - doAssert(heap.find(2) == -1) - - block: # Test del last - var heap = initHeapQueue[int]() - let data = [1, 2, 3] - for item in data: push(heap, item) - - heap.del(2) - doAssert(heap.toSortedSeq == @[1, 2]) - - heap.del(1) - doAssert(heap.toSortedSeq == @[1]) - - heap.del(0) - doAssert(heap.toSortedSeq == @[]) diff --git a/tests/stdlib/theapqueue.nim b/tests/stdlib/theapqueue.nim new file mode 100644 index 000000000..20e23e878 --- /dev/null +++ b/tests/stdlib/theapqueue.nim @@ -0,0 +1,53 @@ +import heapqueue + + +proc toSortedSeq[T](h: HeapQueue[T]): seq[T] = + var tmp = h + result = @[] + while tmp.len > 0: + result.add(pop(tmp)) + +block: # Simple sanity test + var heap = initHeapQueue[int]() + let data = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0] + for item in data: + push(heap, item) + doAssert(heap == data.toHeapQueue) + doAssert(heap[0] == 0) + doAssert(heap.toSortedSeq == @[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) + +block: # Test del + var heap = initHeapQueue[int]() + let data = [1, 3, 5, 7, 9, 2, 4, 6, 8, 0] + for item in data: push(heap, item) + + heap.del(0) + doAssert(heap[0] == 1) + + heap.del(heap.find(7)) + doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 5, 6, 8, 9]) + + heap.del(heap.find(5)) + doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 6, 8, 9]) + + heap.del(heap.find(6)) + doAssert(heap.toSortedSeq == @[1, 2, 3, 4, 8, 9]) + + heap.del(heap.find(2)) + doAssert(heap.toSortedSeq == @[1, 3, 4, 8, 9]) + + doAssert(heap.find(2) == -1) + +block: # Test del last + var heap = initHeapQueue[int]() + let data = [1, 2, 3] + for item in data: push(heap, item) + + heap.del(2) + doAssert(heap.toSortedSeq == @[1, 2]) + + heap.del(1) + doAssert(heap.toSortedSeq == @[1]) + + heap.del(0) + doAssert(heap.toSortedSeq == @[]) From dd57d46f2f3a35878f48a1cd19bc85541f85c463 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sun, 22 Nov 2020 04:20:33 +0800 Subject: [PATCH 1041/3768] complex minor improvement (#16086) --- lib/pure/complex.nim | 150 ++++++-------------------------------- tests/stdlib/tcomplex.nim | 108 +++++++++++++++++++++++++++ 2 files changed, 129 insertions(+), 129 deletions(-) create mode 100644 tests/stdlib/tcomplex.nim diff --git a/lib/pure/complex.nim b/lib/pure/complex.nim index d57adeb92..d1056e6e8 100644 --- a/lib/pure/complex.nim +++ b/lib/pure/complex.nim @@ -42,38 +42,38 @@ template im*(arg: float32): Complex32 = complex[float32](0, arg) template im*(arg: float64): Complex64 = complex[float64](0, arg) proc abs*[T](z: Complex[T]): T = - ## Return the distance from (0,0) to ``z``. + ## Returns the distance from (0,0) to ``z``. result = hypot(z.re, z.im) proc abs2*[T](z: Complex[T]): T = - ## Return the squared distance from (0,0) to ``z``. + ## Returns the squared distance from (0,0) to ``z``. result = z.re*z.re + z.im*z.im proc conjugate*[T](z: Complex[T]): Complex[T] = - ## Conjugate of complex number ``z``. + ## Conjugates of complex number ``z``. result.re = z.re result.im = -z.im proc inv*[T](z: Complex[T]): Complex[T] = - ## Multiplicative inverse of complex number ``z``. + ## Multiplicatives inverse of complex number ``z``. conjugate(z) / abs2(z) proc `==` *[T](x, y: Complex[T]): bool = - ## Compare two complex numbers ``x`` and ``y`` for equality. + ## Compares two complex numbers ``x`` and ``y`` for equality. result = x.re == y.re and x.im == y.im proc `+` *[T](x: T; y: Complex[T]): Complex[T] = - ## Add a real number to a complex number. + ## Adds a real number to a complex number. result.re = x + y.re result.im = y.im proc `+` *[T](x: Complex[T]; y: T): Complex[T] = - ## Add a complex number to a real number. + ## Adds a complex number to a real number. result.re = x.re + y result.im = x.im proc `+` *[T](x, y: Complex[T]): Complex[T] = - ## Add two complex numbers. + ## Adds two complex numbers. result.re = x.re + y.re result.im = x.im + y.im @@ -83,30 +83,30 @@ proc `-` *[T](z: Complex[T]): Complex[T] = result.im = -z.im proc `-` *[T](x: T; y: Complex[T]): Complex[T] = - ## Subtract a complex number from a real number. + ## Subtracts a complex number from a real number. x + (-y) proc `-` *[T](x: Complex[T]; y: T): Complex[T] = - ## Subtract a real number from a complex number. + ## Subtracts a real number from a complex number. result.re = x.re - y result.im = x.im proc `-` *[T](x, y: Complex[T]): Complex[T] = - ## Subtract two complex numbers. + ## Subtracts two complex numbers. result.re = x.re - y.re result.im = x.im - y.im proc `/` *[T](x: Complex[T]; y: T): Complex[T] = - ## Divide complex number ``x`` by real number ``y``. + ## Divides complex number ``x`` by real number ``y``. result.re = x.re / y result.im = x.im / y proc `/` *[T](x: T; y: Complex[T]): Complex[T] = - ## Divide real number ``x`` by complex number ``y``. + ## Divides real number ``x`` by complex number ``y``. result = x * inv(y) proc `/` *[T](x, y: Complex[T]): Complex[T] = - ## Divide ``x`` by ``y``. + ## Divides ``x`` by ``y``. var r, den: T if abs(y.re) < abs(y.im): r = y.re / y.im @@ -120,39 +120,39 @@ proc `/` *[T](x, y: Complex[T]): Complex[T] = result.im = (x.im - r * x.re) / den proc `*` *[T](x: T; y: Complex[T]): Complex[T] = - ## Multiply a real number and a complex number. + ## Multiplies a real number and a complex number. result.re = x * y.re result.im = x * y.im proc `*` *[T](x: Complex[T]; y: T): Complex[T] = - ## Multiply a complex number with a real number. + ## Multiplies a complex number with a real number. result.re = x.re * y result.im = x.im * y proc `*` *[T](x, y: Complex[T]): Complex[T] = - ## Multiply ``x`` with ``y``. + ## Multiplies ``x`` with ``y``. result.re = x.re * y.re - x.im * y.im result.im = x.im * y.re + x.re * y.im proc `+=` *[T](x: var Complex[T]; y: Complex[T]) = - ## Add ``y`` to ``x``. + ## Adds ``y`` to ``x``. x.re += y.re x.im += y.im proc `-=` *[T](x: var Complex[T]; y: Complex[T]) = - ## Subtract ``y`` from ``x``. + ## Subtracts ``y`` from ``x``. x.re -= y.re x.im -= y.im proc `*=` *[T](x: var Complex[T]; y: Complex[T]) = - ## Multiply ``y`` to ``x``. + ## Multiplies ``y`` to ``x``. let im = x.im * y.re + x.re * y.im x.re = x.re * y.re - x.im * y.im x.im = im proc `/=` *[T](x: var Complex[T]; y: Complex[T]) = - ## Divide ``x`` by ``y`` in place. + ## Divides ``x`` by ``y`` in place. x = x / y @@ -346,111 +346,3 @@ proc `$`*(z: Complex): string = result = "(" & $z.re & ", " & $z.im & ")" {.pop.} - - -when isMainModule: - proc `=~`[T](x, y: Complex[T]): bool = - result = abs(x.re-y.re) < 1e-6 and abs(x.im-y.im) < 1e-6 - - proc `=~`[T](x: Complex[T]; y: T): bool = - result = abs(x.re-y) < 1e-6 and abs(x.im) < 1e-6 - - var - z: Complex64 = complex(0.0, 0.0) - oo: Complex64 = complex(1.0, 1.0) - a: Complex64 = complex(1.0, 2.0) - b: Complex64 = complex(-1.0, -2.0) - m1: Complex64 = complex(-1.0, 0.0) - i: Complex64 = complex(0.0, 1.0) - one: Complex64 = complex(1.0, 0.0) - tt: Complex64 = complex(10.0, 20.0) - ipi: Complex64 = complex(0.0, -PI) - - doAssert(a/2.0 =~ complex(0.5, 1.0)) - doAssert(a == a) - doAssert((a-a) == z) - doAssert((a+b) == z) - doAssert((a+b) =~ 0.0) - doAssert((a/b) == m1) - doAssert((1.0/a) =~ complex(0.2, -0.4)) - doAssert((a*b) == complex(3.0, -4.0)) - doAssert(10.0*a == tt) - doAssert(a*10.0 == tt) - doAssert(tt/10.0 == a) - doAssert(oo+(-1.0) == i) - doAssert( (-1.0)+oo == i) - doAssert(abs(oo) == sqrt(2.0)) - doAssert(conjugate(a) == complex(1.0, -2.0)) - doAssert(sqrt(m1) == i) - doAssert(exp(ipi) =~ m1) - - doAssert(pow(a, b) =~ complex(-3.72999124927876, -1.68815826725068)) - doAssert(pow(z, a) =~ complex(0.0, 0.0)) - doAssert(pow(z, z) =~ complex(1.0, 0.0)) - doAssert(pow(a, one) =~ a) - doAssert(pow(a, m1) =~ complex(0.2, -0.4)) - doAssert(pow(a, 2.0) =~ complex(-3.0, 4.0)) - doAssert(pow(a, 2) =~ complex(-3.0, 4.0)) - doAssert(not(pow(a, 2.0) =~ a)) - - doAssert(ln(a) =~ complex(0.804718956217050, 1.107148717794090)) - doAssert(log10(a) =~ complex(0.349485002168009, 0.480828578784234)) - doAssert(log2(a) =~ complex(1.16096404744368, 1.59727796468811)) - - doAssert(sin(a) =~ complex(3.16577851321617, 1.95960104142161)) - doAssert(cos(a) =~ complex(2.03272300701967, -3.05189779915180)) - doAssert(tan(a) =~ complex(0.0338128260798967, 1.0147936161466335)) - doAssert(cot(a) =~ 1.0 / tan(a)) - doAssert(sec(a) =~ 1.0 / cos(a)) - doAssert(csc(a) =~ 1.0 / sin(a)) - doAssert(arcsin(a) =~ complex(0.427078586392476, 1.528570919480998)) - doAssert(arccos(a) =~ complex(1.14371774040242, -1.52857091948100)) - doAssert(arctan(a) =~ complex(1.338972522294494, 0.402359478108525)) - doAssert(arccot(a) =~ complex(0.2318238045004031, -0.402359478108525)) - doAssert(arcsec(a) =~ complex(1.384478272687081, 0.3965682301123288)) - doAssert(arccsc(a) =~ complex(0.1863180541078155, -0.3965682301123291)) - - doAssert(cosh(a) =~ complex(-0.642148124715520, 1.068607421382778)) - doAssert(sinh(a) =~ complex(-0.489056259041294, 1.403119250622040)) - doAssert(tanh(a) =~ complex(1.1667362572409199, -0.243458201185725)) - doAssert(sech(a) =~ 1.0 / cosh(a)) - doAssert(csch(a) =~ 1.0 / sinh(a)) - doAssert(coth(a) =~ 1.0 / tanh(a)) - doAssert(arccosh(a) =~ complex(1.528570919480998, 1.14371774040242)) - doAssert(arcsinh(a) =~ complex(1.469351744368185, 1.06344002357775)) - doAssert(arctanh(a) =~ complex(0.173286795139986, 1.17809724509617)) - doAssert(arcsech(a) =~ arccosh(1.0/a)) - doAssert(arccsch(a) =~ arcsinh(1.0/a)) - doAssert(arccoth(a) =~ arctanh(1.0/a)) - - doAssert(phase(a) == 1.1071487177940904) - var t = polar(a) - doAssert(rect(t.r, t.phi) =~ a) - doAssert(rect(1.0, 2.0) =~ complex(-0.4161468365471424, 0.9092974268256817)) - - - var - i64: Complex32 = complex(0.0f, 1.0f) - a64: Complex32 = 2.0f*i64 + 1.0.float32 - b64: Complex32 = complex(-1.0'f32, -2.0'f32) - - doAssert(a64 == a64) - doAssert(a64 == -b64) - doAssert(a64 + b64 =~ 0.0'f32) - doAssert(not(pow(a64, b64) =~ a64)) - doAssert(pow(a64, 0.5f) =~ sqrt(a64)) - doAssert(pow(a64, 2) =~ complex(-3.0'f32, 4.0'f32)) - doAssert(sin(arcsin(b64)) =~ b64) - doAssert(cosh(arccosh(a64)) =~ a64) - - doAssert(phase(a64) - 1.107149f < 1e-6) - var t64 = polar(a64) - doAssert(rect(t64.r, t64.phi) =~ a64) - doAssert(rect(1.0f, 2.0f) =~ complex(-0.4161468f, 0.90929742f)) - doAssert(sizeof(a64) == 8) - doAssert(sizeof(a) == 16) - - doAssert 123.0.im + 456.0 == complex64(456, 123) - - var localA = complex(0.1'f32) - doAssert localA.im is float32 diff --git a/tests/stdlib/tcomplex.nim b/tests/stdlib/tcomplex.nim new file mode 100644 index 000000000..8c9fa3055 --- /dev/null +++ b/tests/stdlib/tcomplex.nim @@ -0,0 +1,108 @@ +import complex, math + + +proc `=~`[T](x, y: Complex[T]): bool = + result = abs(x.re-y.re) < 1e-6 and abs(x.im-y.im) < 1e-6 + +proc `=~`[T](x: Complex[T]; y: T): bool = + result = abs(x.re-y) < 1e-6 and abs(x.im) < 1e-6 + +var + z: Complex64 = complex(0.0, 0.0) + oo: Complex64 = complex(1.0, 1.0) + a: Complex64 = complex(1.0, 2.0) + b: Complex64 = complex(-1.0, -2.0) + m1: Complex64 = complex(-1.0, 0.0) + i: Complex64 = complex(0.0, 1.0) + one: Complex64 = complex(1.0, 0.0) + tt: Complex64 = complex(10.0, 20.0) + ipi: Complex64 = complex(0.0, -PI) + +doAssert(a/2.0 =~ complex(0.5, 1.0)) +doAssert(a == a) +doAssert((a-a) == z) +doAssert((a+b) == z) +doAssert((a+b) =~ 0.0) +doAssert((a/b) == m1) +doAssert((1.0/a) =~ complex(0.2, -0.4)) +doAssert((a*b) == complex(3.0, -4.0)) +doAssert(10.0*a == tt) +doAssert(a*10.0 == tt) +doAssert(tt/10.0 == a) +doAssert(oo+(-1.0) == i) +doAssert( (-1.0)+oo == i) +doAssert(abs(oo) == sqrt(2.0)) +doAssert(conjugate(a) == complex(1.0, -2.0)) +doAssert(sqrt(m1) == i) +doAssert(exp(ipi) =~ m1) + +doAssert(pow(a, b) =~ complex(-3.72999124927876, -1.68815826725068)) +doAssert(pow(z, a) =~ complex(0.0, 0.0)) +doAssert(pow(z, z) =~ complex(1.0, 0.0)) +doAssert(pow(a, one) =~ a) +doAssert(pow(a, m1) =~ complex(0.2, -0.4)) +doAssert(pow(a, 2.0) =~ complex(-3.0, 4.0)) +doAssert(pow(a, 2) =~ complex(-3.0, 4.0)) +doAssert(not(pow(a, 2.0) =~ a)) + +doAssert(ln(a) =~ complex(0.804718956217050, 1.107148717794090)) +doAssert(log10(a) =~ complex(0.349485002168009, 0.480828578784234)) +doAssert(log2(a) =~ complex(1.16096404744368, 1.59727796468811)) + +doAssert(sin(a) =~ complex(3.16577851321617, 1.95960104142161)) +doAssert(cos(a) =~ complex(2.03272300701967, -3.05189779915180)) +doAssert(tan(a) =~ complex(0.0338128260798967, 1.0147936161466335)) +doAssert(cot(a) =~ 1.0 / tan(a)) +doAssert(sec(a) =~ 1.0 / cos(a)) +doAssert(csc(a) =~ 1.0 / sin(a)) +doAssert(arcsin(a) =~ complex(0.427078586392476, 1.528570919480998)) +doAssert(arccos(a) =~ complex(1.14371774040242, -1.52857091948100)) +doAssert(arctan(a) =~ complex(1.338972522294494, 0.402359478108525)) +doAssert(arccot(a) =~ complex(0.2318238045004031, -0.402359478108525)) +doAssert(arcsec(a) =~ complex(1.384478272687081, 0.3965682301123288)) +doAssert(arccsc(a) =~ complex(0.1863180541078155, -0.3965682301123291)) + +doAssert(cosh(a) =~ complex(-0.642148124715520, 1.068607421382778)) +doAssert(sinh(a) =~ complex(-0.489056259041294, 1.403119250622040)) +doAssert(tanh(a) =~ complex(1.1667362572409199, -0.243458201185725)) +doAssert(sech(a) =~ 1.0 / cosh(a)) +doAssert(csch(a) =~ 1.0 / sinh(a)) +doAssert(coth(a) =~ 1.0 / tanh(a)) +doAssert(arccosh(a) =~ complex(1.528570919480998, 1.14371774040242)) +doAssert(arcsinh(a) =~ complex(1.469351744368185, 1.06344002357775)) +doAssert(arctanh(a) =~ complex(0.173286795139986, 1.17809724509617)) +doAssert(arcsech(a) =~ arccosh(1.0/a)) +doAssert(arccsch(a) =~ arcsinh(1.0/a)) +doAssert(arccoth(a) =~ arctanh(1.0/a)) + +doAssert(phase(a) == 1.1071487177940904) +var t = polar(a) +doAssert(rect(t.r, t.phi) =~ a) +doAssert(rect(1.0, 2.0) =~ complex(-0.4161468365471424, 0.9092974268256817)) + + +var + i64: Complex32 = complex(0.0f, 1.0f) + a64: Complex32 = 2.0f*i64 + 1.0.float32 + b64: Complex32 = complex(-1.0'f32, -2.0'f32) + +doAssert(a64 == a64) +doAssert(a64 == -b64) +doAssert(a64 + b64 =~ 0.0'f32) +doAssert(not(pow(a64, b64) =~ a64)) +doAssert(pow(a64, 0.5f) =~ sqrt(a64)) +doAssert(pow(a64, 2) =~ complex(-3.0'f32, 4.0'f32)) +doAssert(sin(arcsin(b64)) =~ b64) +doAssert(cosh(arccosh(a64)) =~ a64) + +doAssert(phase(a64) - 1.107149f < 1e-6) +var t64 = polar(a64) +doAssert(rect(t64.r, t64.phi) =~ a64) +doAssert(rect(1.0f, 2.0f) =~ complex(-0.4161468f, 0.90929742f)) +doAssert(sizeof(a64) == 8) +doAssert(sizeof(a) == 16) + +doAssert 123.0.im + 456.0 == complex64(456, 123) + +var localA = complex(0.1'f32) +doAssert localA.im is float32 From 3040f0550534848f61aa87d42e195e3fb5a70262 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sun, 22 Nov 2020 04:26:51 +0800 Subject: [PATCH 1042/3768] xmltree minor improvement (#16085) --- lib/pure/xmltree.nim | 90 ++------------------------------------- tests/stdlib/txmltree.nim | 85 ++++++++++++++++++++++++++++++++++++ 2 files changed, 88 insertions(+), 87 deletions(-) create mode 100644 tests/stdlib/txmltree.nim diff --git a/lib/pure/xmltree.nim b/lib/pure/xmltree.nim index df8db1bb7..6ad90b042 100644 --- a/lib/pure/xmltree.nim +++ b/lib/pure/xmltree.nim @@ -312,7 +312,7 @@ proc add*(father, son: XmlNode) {.inline.} = add(father.s, son) proc insert*(father, son: XmlNode, index: int) {.inline.} = - ## Insert the child `son` to a given position in `father`. + ## Inserts the child `son` to a given position in `father`. ## ## `father` and `son` must be of `xnElement` kind. ## @@ -335,7 +335,7 @@ proc insert*(father, son: XmlNode, index: int) {.inline.} = insert(father.s, son, len(father.s)) proc delete*(n: XmlNode, i: Natural) = - ## Delete the `i`'th child of `n`. + ## Deletes the `i`'th child of `n`. ## ## See also: ## * `add proc <#add,XmlNode,XmlNode>`_ @@ -388,7 +388,7 @@ proc `[]`*(n: var XmlNode, i: int): var XmlNode {.inline.} = result = n.s[i] proc clear*(n: var XmlNode) = - ## Recursively clear all children of an XmlNode. + ## Recursively clears all children of an XmlNode. ## ## .. code-block:: ## var g = newElement("myTag") @@ -788,87 +788,3 @@ macro `<>`*(x: untyped): untyped = ## Nim rules. ## result = xmlConstructor(x) - - -when isMainModule: - var - x: XmlNode - - x = <>a(href = "http://nim-lang.org", newText("Nim rules.")) - assert $x == """Nim rules.""" - - x = <>outer(<>inner()) - assert $x == """ - -""" - - x = <>outer(<>middle(<>inner1(), <>inner2(), <>inner3(), <>inner4())) - assert $x == """ - - - - - - -""" - - x = <>l0(<>l1(<>l2(<>l3(<>l4())))) - assert $x == """ - - - - - - - -""" - - x = <>l0(<>l1p1(), <>l1p2(), <>l1p3()) - assert $x == """ - - - -""" - - x = <>l0(<>l1(<>l2p1(), <>l2p2())) - assert $x == """ - - - - -""" - - x = <>l0(<>l1(<>l2_1(), <>l2_2(<>l3_1(), <>l3_2(), <>l3_3(<>l4_1(), <>l4_2(), <>l4_3())), <>l2_3(), <>l2_4())) - assert $x == """ - - - - - - - - - - - - - - -""" - - let - innermost = newElement("innermost") - middle = newXmlTree("middle", [innermost]) - innermost.add newText("innermost text") - x = newXmlTree("outer", [middle]) - assert $x == """ - - innermost text - -""" - - x = newElement("myTag") - x.add newText("my text") - x.add newElement("sonTag") - x.add newEntity("my entity") - assert $x == "my text&my entity;" diff --git a/tests/stdlib/txmltree.nim b/tests/stdlib/txmltree.nim new file mode 100644 index 000000000..034435dd7 --- /dev/null +++ b/tests/stdlib/txmltree.nim @@ -0,0 +1,85 @@ +import xmltree + + +block: + var + x: XmlNode + + x = <>a(href = "http://nim-lang.org", newText("Nim rules.")) + assert $x == """Nim rules.""" + + x = <>outer(<>inner()) + assert $x == """ + +""" + + x = <>outer(<>middle(<>inner1(), <>inner2(), <>inner3(), <>inner4())) + assert $x == """ + + + + + + +""" + + x = <>l0(<>l1(<>l2(<>l3(<>l4())))) + assert $x == """ + + + + + + + +""" + + x = <>l0(<>l1p1(), <>l1p2(), <>l1p3()) + assert $x == """ + + + +""" + + x = <>l0(<>l1(<>l2p1(), <>l2p2())) + assert $x == """ + + + + +""" + + x = <>l0(<>l1(<>l2_1(), <>l2_2(<>l3_1(), <>l3_2(), <>l3_3(<>l4_1(), <>l4_2(), <>l4_3())), <>l2_3(), <>l2_4())) + assert $x == """ + + + + + + + + + + + + + + +""" + + let + innermost = newElement("innermost") + middle = newXmlTree("middle", [innermost]) + innermost.add newText("innermost text") + x = newXmlTree("outer", [middle]) + assert $x == """ + + innermost text + +""" + + x = newElement("myTag") + x.add newText("my text") + x.add newElement("sonTag") + x.add newEntity("my entity") + assert $x == "my text&my entity;" From c9371ef59dc1c0b1037e0798dae8f0a90661db5d Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sun, 22 Nov 2020 04:30:04 +0800 Subject: [PATCH 1043/3768] deques minor improvement (#16084) --- lib/pure/collections/deques.nim | 147 ++++---------------------------- tests/stdlib/tdeque.nim | 111 ++++++++++++++++++++++++ 2 files changed, 129 insertions(+), 129 deletions(-) create mode 100644 tests/stdlib/tdeque.nim diff --git a/lib/pure/collections/deques.nim b/lib/pure/collections/deques.nim index c3eeb3156..6df34bd3a 100644 --- a/lib/pure/collections/deques.nim +++ b/lib/pure/collections/deques.nim @@ -77,7 +77,7 @@ template checkIfInitialized(deq: typed) = initImpl(deq, defaultInitialSize) proc initDeque*[T](initialSize: int = 4): Deque[T] = - ## Create a new empty deque. + ## Creates a new empty deque. ## ## Optionally, the initial capacity can be reserved via `initialSize` ## as a performance optimization. @@ -103,7 +103,7 @@ proc toDeque*[T](x: openArray[T]): Deque[T] {.since: (1, 3).} = result.addLast(item) proc len*[T](deq: Deque[T]): int {.inline.} = - ## Return the number of elements of `deq`. + ## Returns the number of elements of `deq`. result = deq.count template emptyCheck(deq) = @@ -123,7 +123,7 @@ template xBoundsCheck(deq, i) = "Out of bounds: " & $i & " < 0") proc `[]`*[T](deq: Deque[T], i: Natural): T {.inline.} = - ## Access the i-th element of `deq`. + ## Accesses the i-th element of `deq`. runnableExamples: var a = initDeque[int]() for i in 1 .. 5: @@ -136,7 +136,7 @@ proc `[]`*[T](deq: Deque[T], i: Natural): T {.inline.} = return deq.data[(deq.head + i) and deq.mask] proc `[]`*[T](deq: var Deque[T], i: Natural): var T {.inline.} = - ## Access the i-th element of `deq` and return a mutable + ## Accesses the i-th element of `deq` and return a mutable ## reference to it. runnableExamples: var a = initDeque[int]() @@ -150,7 +150,7 @@ proc `[]`*[T](deq: var Deque[T], i: Natural): var T {.inline.} = return deq.data[(deq.head + i) and deq.mask] proc `[]=`*[T](deq: var Deque[T], i: Natural, val: T) {.inline.} = - ## Change the i-th element of `deq`. + ## Changes the i-th element of `deq`. runnableExamples: var a = initDeque[int]() for i in 1 .. 5: @@ -164,7 +164,7 @@ proc `[]=`*[T](deq: var Deque[T], i: Natural, val: T) {.inline.} = deq.data[(deq.head + i) and deq.mask] = val proc `[]`*[T](deq: Deque[T], i: BackwardsIndex): T {.inline.} = - ## Access the backwards indexed i-th element. + ## Accesses the backwards indexed i-th element. ## ## `deq[^1]` is the last element. runnableExamples: @@ -179,7 +179,7 @@ proc `[]`*[T](deq: Deque[T], i: BackwardsIndex): T {.inline.} = return deq[deq.len - int(i)] proc `[]`*[T](deq: var Deque[T], i: BackwardsIndex): var T {.inline.} = - ## Access the backwards indexed i-th element. + ## Accesses the backwards indexed i-th element. ## ## `deq[^1]` is the last element. runnableExamples: @@ -194,7 +194,7 @@ proc `[]`*[T](deq: var Deque[T], i: BackwardsIndex): var T {.inline.} = return deq[deq.len - int(i)] proc `[]=`*[T](deq: var Deque[T], i: BackwardsIndex, x: T) {.inline.} = - ## Change the backwards indexed i-th element. + ## Changes the backwards indexed i-th element. ## ## `deq[^1]` is the last element. runnableExamples: @@ -210,7 +210,7 @@ proc `[]=`*[T](deq: var Deque[T], i: BackwardsIndex, x: T) {.inline.} = deq[deq.len - int(i)] = x iterator items*[T](deq: Deque[T]): T = - ## Yield every element of `deq`. + ## Yields every element of `deq`. ## ## **Examples:** ## @@ -232,7 +232,7 @@ iterator items*[T](deq: Deque[T]): T = i = (i + 1) and deq.mask iterator mitems*[T](deq: var Deque[T]): var T = - ## Yield every element of `deq`, which can be modified. + ## Yields every element of `deq`, which can be modified. runnableExamples: var a = initDeque[int]() for i in 1 .. 5: @@ -248,7 +248,7 @@ iterator mitems*[T](deq: var Deque[T]): var T = i = (i + 1) and deq.mask iterator pairs*[T](deq: Deque[T]): tuple[key: int, val: T] = - ## Yield every (position, value) of `deq`. + ## Yields every (position, value) of `deq`. ## ## **Examples:** ## @@ -270,7 +270,7 @@ iterator pairs*[T](deq: Deque[T]): tuple[key: int, val: T] = i = (i + 1) and deq.mask proc contains*[T](deq: Deque[T], item: T): bool {.inline.} = - ## Return true if `item` is in `deq` or false if not found. + ## Returns true if `item` is in `deq` or false if not found. ## ## Usually used via the ``in`` operator. ## It is the equivalent of ``deq.find(item) >= 0``. @@ -298,7 +298,7 @@ proc expandIfNeeded[T](deq: var Deque[T]) = deq.head = 0 proc addFirst*[T](deq: var Deque[T], item: T) = - ## Add an `item` to the beginning of the `deq`. + ## Adds an `item` to the beginning of the `deq`. ## ## See also: ## * `addLast proc <#addLast,Deque[T],T>`_ @@ -318,7 +318,7 @@ proc addFirst*[T](deq: var Deque[T], item: T) = deq.data[deq.head] = item proc addLast*[T](deq: var Deque[T], item: T) = - ## Add an `item` to the end of the `deq`. + ## Adds an `item` to the end of the `deq`. ## ## See also: ## * `addFirst proc <#addFirst,Deque[T],T>`_ @@ -421,7 +421,7 @@ template destroy(x: untyped) = reset(x) proc popFirst*[T](deq: var Deque[T]): T {.inline, discardable.} = - ## Remove and returns the first element of the `deq`. + ## Removes and returns the first element of the `deq`. ## ## See also: ## * `addFirst proc <#addFirst,Deque[T],T>`_ @@ -446,7 +446,7 @@ proc popFirst*[T](deq: var Deque[T]): T {.inline, discardable.} = deq.head = (deq.head + 1) and deq.mask proc popLast*[T](deq: var Deque[T]): T {.inline, discardable.} = - ## Remove and returns the last element of the `deq`. + ## Removes and returns the last element of the `deq`. ## ## See also: ## * `addFirst proc <#addFirst,Deque[T],T>`_ @@ -489,7 +489,7 @@ proc clear*[T](deq: var Deque[T]) {.inline.} = deq.tail = deq.head proc shrink*[T](deq: var Deque[T], fromFirst = 0, fromLast = 0) = - ## Remove `fromFirst` elements from the front of the deque and + ## Removes `fromFirst` elements from the front of the deque and ## `fromLast` elements from the back. ## ## If the supplied number of elements exceeds the total number of elements @@ -520,120 +520,9 @@ proc shrink*[T](deq: var Deque[T], fromFirst = 0, fromLast = 0) = dec deq.count, fromFirst + fromLast proc `$`*[T](deq: Deque[T]): string = - ## Turn a deque into its string representation. + ## Turns a deque into its string representation. result = "[" for x in deq: if result.len > 1: result.add(", ") result.addQuoted(x) result.add("]") - - - -when isMainModule: - var deq = initDeque[int](1) - deq.addLast(4) - deq.addFirst(9) - deq.addFirst(123) - var first = deq.popFirst() - deq.addLast(56) - assert(deq.peekLast() == 56) - deq.addLast(6) - assert(deq.peekLast() == 6) - var second = deq.popFirst() - deq.addLast(789) - assert(deq.peekLast() == 789) - - assert first == 123 - assert second == 9 - assert($deq == "[4, 56, 6, 789]") - assert deq == [4, 56, 6, 789].toDeque - - assert deq[0] == deq.peekFirst and deq.peekFirst == 4 - #assert deq[^1] == deq.peekLast and deq.peekLast == 789 - deq[0] = 42 - deq[deq.len - 1] = 7 - - assert 6 in deq and 789 notin deq - assert deq.find(6) >= 0 - assert deq.find(789) < 0 - - block: - var d = initDeque[int](1) - d.addLast 7 - d.addLast 8 - d.addLast 10 - d.addFirst 5 - d.addFirst 2 - d.addFirst 1 - d.addLast 20 - d.shrink(fromLast = 2) - doAssert($d == "[1, 2, 5, 7, 8]") - d.shrink(2, 1) - doAssert($d == "[5, 7]") - d.shrink(2, 2) - doAssert d.len == 0 - - for i in -2 .. 10: - if i in deq: - assert deq.contains(i) and deq.find(i) >= 0 - else: - assert(not deq.contains(i) and deq.find(i) < 0) - - when compileOption("boundChecks"): - try: - echo deq[99] - assert false - except IndexDefect: - discard - - try: - assert deq.len == 4 - for i in 0 ..< 5: deq.popFirst() - assert false - except IndexDefect: - discard - - # grabs some types of resize error. - deq = initDeque[int]() - for i in 1 .. 4: deq.addLast i - deq.popFirst() - deq.popLast() - for i in 5 .. 8: deq.addFirst i - assert $deq == "[8, 7, 6, 5, 2, 3]" - - # Similar to proc from the documentation example - proc foo(a, b: Positive) = # assume random positive values for `a` and `b`. - var deq = initDeque[int]() - assert deq.len == 0 - for i in 1 .. a: deq.addLast i - - if b < deq.len: # checking before indexed access. - assert deq[b] == b + 1 - - # The following two lines don't need any checking on access due to the logic - # of the program, but that would not be the case if `a` could be 0. - assert deq.peekFirst == 1 - assert deq.peekLast == a - - while deq.len > 0: # checking if the deque is empty - assert deq.popFirst() > 0 - - #foo(0,0) - foo(8, 5) - foo(10, 9) - foo(1, 1) - foo(2, 1) - foo(1, 5) - foo(3, 2) - - import sets - block t13310: - proc main() = - var q = initDeque[HashSet[int16]](2) - q.addFirst([1'i16].toHashSet) - q.addFirst([2'i16].toHashSet) - q.addFirst([3'i16].toHashSet) - assert $q == "[{3}, {2}, {1}]" - - static: - main() diff --git a/tests/stdlib/tdeque.nim b/tests/stdlib/tdeque.nim new file mode 100644 index 000000000..50e5d0ab1 --- /dev/null +++ b/tests/stdlib/tdeque.nim @@ -0,0 +1,111 @@ +import deques + + +var deq = initDeque[int](1) +deq.addLast(4) +deq.addFirst(9) +deq.addFirst(123) +var first = deq.popFirst() +deq.addLast(56) +assert(deq.peekLast() == 56) +deq.addLast(6) +assert(deq.peekLast() == 6) +var second = deq.popFirst() +deq.addLast(789) +assert(deq.peekLast() == 789) + +assert first == 123 +assert second == 9 +assert($deq == "[4, 56, 6, 789]") +assert deq == [4, 56, 6, 789].toDeque + +assert deq[0] == deq.peekFirst and deq.peekFirst == 4 +#assert deq[^1] == deq.peekLast and deq.peekLast == 789 +deq[0] = 42 +deq[deq.len - 1] = 7 + +assert 6 in deq and 789 notin deq +assert deq.find(6) >= 0 +assert deq.find(789) < 0 + +block: + var d = initDeque[int](1) + d.addLast 7 + d.addLast 8 + d.addLast 10 + d.addFirst 5 + d.addFirst 2 + d.addFirst 1 + d.addLast 20 + d.shrink(fromLast = 2) + doAssert($d == "[1, 2, 5, 7, 8]") + d.shrink(2, 1) + doAssert($d == "[5, 7]") + d.shrink(2, 2) + doAssert d.len == 0 + +for i in -2 .. 10: + if i in deq: + assert deq.contains(i) and deq.find(i) >= 0 + else: + assert(not deq.contains(i) and deq.find(i) < 0) + +when compileOption("boundChecks"): + try: + echo deq[99] + assert false + except IndexDefect: + discard + + try: + assert deq.len == 4 + for i in 0 ..< 5: deq.popFirst() + assert false + except IndexDefect: + discard + +# grabs some types of resize error. +deq = initDeque[int]() +for i in 1 .. 4: deq.addLast i +deq.popFirst() +deq.popLast() +for i in 5 .. 8: deq.addFirst i +assert $deq == "[8, 7, 6, 5, 2, 3]" + +# Similar to proc from the documentation example +proc foo(a, b: Positive) = # assume random positive values for `a` and `b`. + var deq = initDeque[int]() + assert deq.len == 0 + for i in 1 .. a: deq.addLast i + + if b < deq.len: # checking before indexed access. + assert deq[b] == b + 1 + + # The following two lines don't need any checking on access due to the logic + # of the program, but that would not be the case if `a` could be 0. + assert deq.peekFirst == 1 + assert deq.peekLast == a + + while deq.len > 0: # checking if the deque is empty + assert deq.popFirst() > 0 + +#foo(0,0) +foo(8, 5) +foo(10, 9) +foo(1, 1) +foo(2, 1) +foo(1, 5) +foo(3, 2) + +import sets + +block t13310: + proc main() = + var q = initDeque[HashSet[int16]](2) + q.addFirst([1'i16].toHashSet) + q.addFirst([2'i16].toHashSet) + q.addFirst([3'i16].toHashSet) + assert $q == "[{3}, {2}, {1}]" + + static: + main() From f1764aaa9ed2a4e3488ccc5632aed77e3cae8cbf Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sun, 22 Nov 2020 04:50:23 +0800 Subject: [PATCH 1044/3768] sets minor improvement (#16087) --- lib/pure/collections/sets.nim | 270 +------------------------------- tests/collections/thashsets.nim | 261 ++++++++++++++++++++++++++++++ 2 files changed, 262 insertions(+), 269 deletions(-) diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim index 98dedc5ff..18f91f516 100644 --- a/lib/pure/collections/sets.nim +++ b/lib/pure/collections/sets.nim @@ -321,7 +321,7 @@ proc missingOrExcl*[A](s: var HashSet[A], key: A): bool = exclImpl(s, key) proc pop*[A](s: var HashSet[A]): A = - ## Remove and return an arbitrary element from the set `s`. + ## Removes and returns an arbitrary element from the set `s`. ## ## Raises KeyError if the set `s` is empty. ## @@ -915,271 +915,3 @@ iterator pairs*[A](s: OrderedSet[A]): tuple[a: int, b: A] = forAllOrderedPairs: yield (idx, s.data[h].key) - -# ----------------------------------------------------------------------- - - - -when isMainModule and not defined(release): - proc testModule() = - ## Internal micro test to validate docstrings and such. - block lenTest: - var values: HashSet[int] - assert values.len == 0 - assert values.card == 0 - - block setIterator: - type pair = tuple[a, b: int] - var a, b = initHashSet[pair]() - a.incl((2, 3)) - a.incl((3, 2)) - a.incl((2, 3)) - for x, y in a.items: - b.incl((x - 2, y + 1)) - assert a.len == b.card - assert a.len == 2 - #echo b - - block setContains: - var values = initHashSet[int]() - assert(not values.contains(2)) - values.incl(2) - assert values.contains(2) - values.excl(2) - assert(not values.contains(2)) - - values.incl(4) - var others = toHashSet([6, 7]) - values.incl(others) - assert values.len == 3 - - values.init - assert values.containsOrIncl(2) == false - assert values.containsOrIncl(2) == true - var - a = toHashSet([1, 2]) - b = toHashSet([1]) - b.incl(2) - assert a == b - - block exclusions: - var s = toHashSet([2, 3, 6, 7]) - s.excl(2) - s.excl(2) - assert s.len == 3 - - var - numbers = toHashSet([1, 2, 3, 4, 5]) - even = toHashSet([2, 4, 6, 8]) - numbers.excl(even) - #echo numbers - # --> {1, 3, 5} - - block toSeqAndString: - var a = toHashSet([2, 7, 5]) - var b = initHashSet[int](a.len) - for x in [2, 7, 5]: b.incl(x) - assert($a == $b) - #echo a - #echo toHashSet(["no", "esc'aping", "is \" provided"]) - - #block orderedToSeqAndString: - # echo toOrderedSet([2, 4, 5]) - # echo toOrderedSet(["no", "esc'aping", "is \" provided"]) - - block setOperations: - var - a = toHashSet(["a", "b"]) - b = toHashSet(["b", "c"]) - c = union(a, b) - assert c == toHashSet(["a", "b", "c"]) - var d = intersection(a, b) - assert d == toHashSet(["b"]) - var e = difference(a, b) - assert e == toHashSet(["a"]) - var f = symmetricDifference(a, b) - assert f == toHashSet(["a", "c"]) - assert d < a and d < b - assert((a < a) == false) - assert d <= a and d <= b - assert((a <= a)) - # Alias test. - assert a + b == toHashSet(["a", "b", "c"]) - assert a * b == toHashSet(["b"]) - assert a - b == toHashSet(["a"]) - assert a -+- b == toHashSet(["a", "c"]) - assert disjoint(a, b) == false - assert disjoint(a, b - a) == true - - block mapSet: - var a = toHashSet([1, 2, 3]) - var b = a.map(proc (x: int): string = $x) - assert b == toHashSet(["1", "2", "3"]) - - block lenTest: - var values: OrderedSet[int] - assert values.len == 0 - assert values.card == 0 - - block setIterator: - type pair = tuple[a, b: int] - var a, b = initOrderedSet[pair]() - a.incl((2, 3)) - a.incl((3, 2)) - a.incl((2, 3)) - for x, y in a.items: - b.incl((x - 2, y + 1)) - assert a.len == b.card - assert a.len == 2 - - block setPairsIterator: - var s = toOrderedSet([1, 3, 5, 7]) - var items = newSeq[tuple[a: int, b: int]]() - for idx, item in s: items.add((idx, item)) - assert items == @[(0, 1), (1, 3), (2, 5), (3, 7)] - - block exclusions: - var s = toOrderedSet([1, 2, 3, 6, 7, 4]) - - s.excl(3) - s.excl(3) - s.excl(1) - s.excl(4) - - var items = newSeq[int]() - for item in s: items.add item - assert items == @[2, 6, 7] - - block: #9005 - var s = initOrderedSet[(int, int)]() - for i in 0 .. 30: incl(s, (i, 0)) - for i in 0 .. 30: excl(s, (i, 0)) - doAssert s.len == 0 - - #block orderedSetIterator: - # var a = initOrderedSet[int]() - # for value in [9, 2, 1, 5, 1, 8, 4, 2]: - # a.incl(value) - # for value in a.items: - # echo "Got ", value - - block setContains: - var values = initOrderedSet[int]() - assert(not values.contains(2)) - values.incl(2) - assert values.contains(2) - - block toSeqAndString: - var a = toOrderedSet([2, 4, 5]) - var b = initOrderedSet[int]() - for x in [2, 4, 5]: b.incl(x) - assert($a == $b) - assert(a == b) # https://github.com/Araq/Nim/issues/1413 - - block initBlocks: - var a: OrderedSet[int] - a.init(4) - a.incl(2) - a.init - assert a.len == 0 - a = initOrderedSet[int](4) - a.incl(2) - assert a.len == 1 - - var b: HashSet[int] - b.init(4) - b.incl(2) - b.init - assert b.len == 0 - b = initHashSet[int](4) - b.incl(2) - assert b.len == 1 - - block missingOrExcl: - var s = toOrderedSet([2, 3, 6, 7]) - assert s.missingOrExcl(4) == true - assert s.missingOrExcl(6) == false - - block orderedSetEquality: - type pair = tuple[a, b: int] - - var aa = initOrderedSet[pair]() - var bb = initOrderedSet[pair]() - - var x = (a: 1, b: 2) - var y = (a: 3, b: 4) - - aa.incl(x) - aa.incl(y) - - bb.incl(x) - bb.incl(y) - assert aa == bb - - block setsWithoutInit: - var - a: HashSet[int] - b: HashSet[int] - c: HashSet[int] - d: HashSet[int] - e: HashSet[int] - - doAssert a.containsOrIncl(3) == false - doAssert a.contains(3) - doAssert a.len == 1 - doAssert a.containsOrIncl(3) - a.incl(3) - doAssert a.len == 1 - a.incl(6) - doAssert a.len == 2 - - b.incl(5) - doAssert b.len == 1 - b.excl(5) - b.excl(c) - doAssert b.missingOrExcl(5) - doAssert b.disjoint(c) - - d = b + c - doAssert d.len == 0 - d = b * c - doAssert d.len == 0 - d = b - c - doAssert d.len == 0 - d = b -+- c - doAssert d.len == 0 - - doAssert (d < e) == false - doAssert d <= e - doAssert d == e - - block setsWithoutInit: - var - a: OrderedSet[int] - b: OrderedSet[int] - c: OrderedSet[int] - d: HashSet[int] - - - doAssert a.containsOrIncl(3) == false - doAssert a.contains(3) - doAssert a.len == 1 - doAssert a.containsOrIncl(3) - a.incl(3) - doAssert a.len == 1 - a.incl(6) - doAssert a.len == 2 - - b.incl(5) - doAssert b.len == 1 - doAssert b.missingOrExcl(5) == false - doAssert b.missingOrExcl(5) - - doAssert c.missingOrExcl(9) - d.incl(c) - doAssert d.len == 0 - - when not defined(testing): - echo "Micro tests run successfully." - - testModule() diff --git a/tests/collections/thashsets.nim b/tests/collections/thashsets.nim index 4ab3a66b7..359eaa51e 100644 --- a/tests/collections/thashsets.nim +++ b/tests/collections/thashsets.nim @@ -119,3 +119,264 @@ block hashForOrderdSet: reversed = !$reversed doAssert hash(r) == reversed doAssert hash(s1) != reversed + + +proc testModule() = + ## Internal micro test to validate docstrings and such. + block lenTest: + var values: HashSet[int] + doAssert values.len == 0 + doAssert values.card == 0 + + block setIterator: + type pair = tuple[a, b: int] + var a, b = initHashSet[pair]() + a.incl((2, 3)) + a.incl((3, 2)) + a.incl((2, 3)) + for x, y in a.items: + b.incl((x - 2, y + 1)) + doAssert a.len == b.card + doAssert a.len == 2 + #echo b + + block setContains: + var values = initHashSet[int]() + doAssert(not values.contains(2)) + values.incl(2) + doAssert values.contains(2) + values.excl(2) + doAssert(not values.contains(2)) + + values.incl(4) + var others = toHashSet([6, 7]) + values.incl(others) + doAssert values.len == 3 + + values.init + doAssert values.containsOrIncl(2) == false + doAssert values.containsOrIncl(2) == true + var + a = toHashSet([1, 2]) + b = toHashSet([1]) + b.incl(2) + doAssert a == b + + block exclusions: + var s = toHashSet([2, 3, 6, 7]) + s.excl(2) + s.excl(2) + doAssert s.len == 3 + + var + numbers = toHashSet([1, 2, 3, 4, 5]) + even = toHashSet([2, 4, 6, 8]) + numbers.excl(even) + #echo numbers + # --> {1, 3, 5} + + block toSeqAndString: + var a = toHashSet([2, 7, 5]) + var b = initHashSet[int](a.len) + for x in [2, 7, 5]: b.incl(x) + doAssert($a == $b) + #echo a + #echo toHashSet(["no", "esc'aping", "is \" provided"]) + + #block orderedToSeqAndString: + # echo toOrderedSet([2, 4, 5]) + # echo toOrderedSet(["no", "esc'aping", "is \" provided"]) + + block setOperations: + var + a = toHashSet(["a", "b"]) + b = toHashSet(["b", "c"]) + c = union(a, b) + doAssert c == toHashSet(["a", "b", "c"]) + var d = intersection(a, b) + doAssert d == toHashSet(["b"]) + var e = difference(a, b) + doAssert e == toHashSet(["a"]) + var f = symmetricDifference(a, b) + doAssert f == toHashSet(["a", "c"]) + doAssert d < a and d < b + doAssert((a < a) == false) + doAssert d <= a and d <= b + doAssert((a <= a)) + # Alias test. + doAssert a + b == toHashSet(["a", "b", "c"]) + doAssert a * b == toHashSet(["b"]) + doAssert a - b == toHashSet(["a"]) + doAssert a -+- b == toHashSet(["a", "c"]) + doAssert disjoint(a, b) == false + doAssert disjoint(a, b - a) == true + + block mapSet: + var a = toHashSet([1, 2, 3]) + var b = a.map(proc (x: int): string = $x) + doAssert b == toHashSet(["1", "2", "3"]) + + block lenTest: + var values: OrderedSet[int] + doAssert values.len == 0 + doAssert values.card == 0 + + block setIterator: + type pair = tuple[a, b: int] + var a, b = initOrderedSet[pair]() + a.incl((2, 3)) + a.incl((3, 2)) + a.incl((2, 3)) + for x, y in a.items: + b.incl((x - 2, y + 1)) + doAssert a.len == b.card + doAssert a.len == 2 + + block setPairsIterator: + var s = toOrderedSet([1, 3, 5, 7]) + var items = newSeq[tuple[a: int, b: int]]() + for idx, item in s: items.add((idx, item)) + doAssert items == @[(0, 1), (1, 3), (2, 5), (3, 7)] + + block exclusions: + var s = toOrderedSet([1, 2, 3, 6, 7, 4]) + + s.excl(3) + s.excl(3) + s.excl(1) + s.excl(4) + + var items = newSeq[int]() + for item in s: items.add item + doAssert items == @[2, 6, 7] + + block: #9005 + var s = initOrderedSet[(int, int)]() + for i in 0 .. 30: incl(s, (i, 0)) + for i in 0 .. 30: excl(s, (i, 0)) + doAssert s.len == 0 + + #block orderedSetIterator: + # var a = initOrderedSet[int]() + # for value in [9, 2, 1, 5, 1, 8, 4, 2]: + # a.incl(value) + # for value in a.items: + # echo "Got ", value + + block setContains: + var values = initOrderedSet[int]() + doAssert(not values.contains(2)) + values.incl(2) + doAssert values.contains(2) + + block toSeqAndString: + var a = toOrderedSet([2, 4, 5]) + var b = initOrderedSet[int]() + for x in [2, 4, 5]: b.incl(x) + doAssert($a == $b) + doAssert(a == b) # https://github.com/Araq/Nim/issues/1413 + + block initBlocks: + var a: OrderedSet[int] + a.init(4) + a.incl(2) + a.init + doAssert a.len == 0 + a = initOrderedSet[int](4) + a.incl(2) + doAssert a.len == 1 + + var b: HashSet[int] + b.init(4) + b.incl(2) + b.init + doAssert b.len == 0 + b = initHashSet[int](4) + b.incl(2) + doAssert b.len == 1 + + block missingOrExcl: + var s = toOrderedSet([2, 3, 6, 7]) + doAssert s.missingOrExcl(4) == true + doAssert s.missingOrExcl(6) == false + + block orderedSetEquality: + type pair = tuple[a, b: int] + + var aa = initOrderedSet[pair]() + var bb = initOrderedSet[pair]() + + var x = (a: 1, b: 2) + var y = (a: 3, b: 4) + + aa.incl(x) + aa.incl(y) + + bb.incl(x) + bb.incl(y) + doAssert aa == bb + + block setsWithoutInit: + var + a: HashSet[int] + b: HashSet[int] + c: HashSet[int] + d: HashSet[int] + e: HashSet[int] + + doAssert a.containsOrIncl(3) == false + doAssert a.contains(3) + doAssert a.len == 1 + doAssert a.containsOrIncl(3) + a.incl(3) + doAssert a.len == 1 + a.incl(6) + doAssert a.len == 2 + + b.incl(5) + doAssert b.len == 1 + b.excl(5) + b.excl(c) + doAssert b.missingOrExcl(5) + doAssert b.disjoint(c) + + d = b + c + doAssert d.len == 0 + d = b * c + doAssert d.len == 0 + d = b - c + doAssert d.len == 0 + d = b -+- c + doAssert d.len == 0 + + doAssert (d < e) == false + doAssert d <= e + doAssert d == e + + block setsWithoutInit: + var + a: OrderedSet[int] + b: OrderedSet[int] + c: OrderedSet[int] + d: HashSet[int] + + + doAssert a.containsOrIncl(3) == false + doAssert a.contains(3) + doAssert a.len == 1 + doAssert a.containsOrIncl(3) + a.incl(3) + doAssert a.len == 1 + a.incl(6) + doAssert a.len == 2 + + b.incl(5) + doAssert b.len == 1 + doAssert b.missingOrExcl(5) == false + doAssert b.missingOrExcl(5) + + doAssert c.missingOrExcl(9) + d.incl(c) + doAssert d.len == 0 + +testModule() From 2841cc6e97da3822eb778b0cbd138ebd1fc5cec0 Mon Sep 17 00:00:00 2001 From: Bad Dog Date: Sun, 22 Nov 2020 02:11:05 -0500 Subject: [PATCH 1045/3768] add gram to important packages (#16037) --- testament/important_packages.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 22d58ba3e..ffc626589 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -44,6 +44,7 @@ pkg1 "glob" pkg1 "ggplotnim", "nim c -d:noCairo -r tests/tests.nim" # pkg1 "gittyup", "nimble test", "https://github.com/disruptek/gittyup" pkg1 "gnuplot", "nim c gnuplot.nim" +pkg1 "gram", "nim c -r --gc:arc --define:danger tests/test.nim", "https://github.com/disruptek/gram" pkg1 "hts", "nim c -o:htss src/hts.nim" # pkg1 "httpauth" pkg1 "illwill", "nimble examples" From 6f1a72069a6992850813cd99d4d3aec4e595c438 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 23 Nov 2020 00:16:42 +0800 Subject: [PATCH 1046/3768] fix #9695 asyncmacro: tfuturevar fails when activated [backport: 1.0] (#16090) * fix asyncmacro * Apply suggestions from code review * Update lib/pure/asyncmacro.nim --- lib/pure/asyncmacro.nim | 3 ++- tests/async/tfuturevar.nim | 5 ----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/lib/pure/asyncmacro.nim b/lib/pure/asyncmacro.nim index 579e9fede..a70508736 100644 --- a/lib/pure/asyncmacro.nim +++ b/lib/pure/asyncmacro.nim @@ -118,7 +118,8 @@ proc getFutureVarIdents(params: NimNode): seq[NimNode] {.compileTime.} = for i in 1 ..< len(params): expectKind(params[i], nnkIdentDefs) if params[i][1].kind == nnkBracketExpr and - params[i][1][0].eqIdent("futurevar"): + params[i][1][0].eqIdent(FutureVar.astToStr): + ## eqIdent: first char is case sensitive!!! result.add(params[i][0]) proc isInvalidReturnType(typeName: string): bool = diff --git a/tests/async/tfuturevar.nim b/tests/async/tfuturevar.nim index 9e3134261..b70f1d166 100644 --- a/tests/async/tfuturevar.nim +++ b/tests/async/tfuturevar.nim @@ -1,8 +1,3 @@ -discard """ -action: compile -""" -# XXX: action should be run! - import asyncdispatch proc completeOnReturn(fut: FutureVar[string], x: bool) {.async.} = From 6a37c0682ad30701aeef22e146d5cd5bff33b830 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 23 Nov 2020 04:01:17 +0800 Subject: [PATCH 1047/3768] ast minor (#16079) --- compiler/ast.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/ast.nim b/compiler/ast.nim index c75a149c0..da3f3e7d7 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1834,6 +1834,7 @@ proc toVar*(typ: PType; kind: TTypeKind; idgen: IdGenerator): PType = proc toRef*(typ: PType; idgen: IdGenerator): PType = ## If ``typ`` is a tyObject then it is converted into a `ref ` and ## returned. Otherwise ``typ`` is simply returned as-is. + result = typ if typ.skipTypes({tyAlias, tyGenericInst}).kind == tyObject: result = newType(tyRef, nextId(idgen), typ.owner) rawAddSon(result, typ) From ab2c082cb3e9e411571fa0936776762bc0c8fd94 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 23 Nov 2020 18:27:09 +0800 Subject: [PATCH 1048/3768] fix rope index (#16100) * fix rope index * add testcase --- lib/pure/ropes.nim | 2 +- tests/stdlib/tropes.nim | 66 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 tests/stdlib/tropes.nim diff --git a/lib/pure/ropes.nim b/lib/pure/ropes.nim index 41d6211b4..0797bbe06 100644 --- a/lib/pure/ropes.nim +++ b/lib/pure/ropes.nim @@ -204,8 +204,8 @@ proc `[]`*(r: Rope, i: int): char {.rtl, extern: "nroCharAt".} = if x.left.length > j: x = x.left else: + dec(j, x.left.length) x = x.right - dec(j, x.len) iterator leaves*(r: Rope): string = ## iterates over any leaf string in the rope `r`. diff --git a/tests/stdlib/tropes.nim b/tests/stdlib/tropes.nim new file mode 100644 index 000000000..9678e26c3 --- /dev/null +++ b/tests/stdlib/tropes.nim @@ -0,0 +1,66 @@ +import ropes + + +block: + let r: Rope = nil + doAssert r[0] == '\0' + +block: + var + r1 = rope("Hello") + r2 = rope("Nim-Lang") + + let r = r1 & r2 + let s = $r + for i in 0 ..< r.len: + doAssert r[i] == s[i] + + doAssert r[66] == '\0' + +block: + let r = rope("Hello, Nim-Lang") + + let s = $r + for i in 0 ..< r.len: + doAssert r[i] == s[i] + + doAssert r[66] == '\0' + +block: + var r: Rope + r.add rope("Nim ") + r.add rope("is ") + r.add rope("a ") + r.add rope("great ") + r.add rope("language") + + let s = $r + for i in 0 ..< r.len: + doAssert r[i] == s[i] + + doAssert r[66] == '\0' + +block: + var r: Rope + r.add rope("My Conquest") + r.add rope(" is ") + r.add rope("the Sea of Stars") + + let s = $r + for i in 0 ..< r.len: + doAssert r[i] == s[i] + + doAssert r[66] == '\0' + +block: + var r: Rope + r.add rope("My Conquest") + r.add rope(" is ") + r.add rope("the Sea of Stars") + + var i: int + for item in r: + doAssert r[i] == item + inc i + + doAssert r[66] == '\0' From 79ec1bdcbb6a9d957def958bc9eff3b4c4a7f1d4 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 23 Nov 2020 18:36:05 +0800 Subject: [PATCH 1049/3768] correct errors in xmltree docs (#16104) --- lib/pure/xmltree.nim | 153 ++++++++++++++++++++----------------------- 1 file changed, 72 insertions(+), 81 deletions(-) diff --git a/lib/pure/xmltree.nim b/lib/pure/xmltree.nim index 6ad90b042..62a62eb4b 100644 --- a/lib/pure/xmltree.nim +++ b/lib/pure/xmltree.nim @@ -9,26 +9,22 @@ ## A simple XML tree generator. ## -## .. code-block:: -## import xmltree -## -## var g = newElement("myTag") -## g.add newText("some text") -## g.add newComment("this is comment") -## -## var h = newElement("secondTag") -## h.add newEntity("some entity") -## -## let att = {"key1": "first value", "key2": "second value"}.toXmlAttributes -## let k = newXmlTree("treeTag", [g, h], att) -## -## echo k -## # -## # some text -## # &some entity; -## # -## -## +runnableExamples: + var g = newElement("myTag") + g.add newText("some text") + g.add newComment("this is comment") + + var h = newElement("secondTag") + h.add newEntity("some entity") + + let att = {"key1": "first value", "key2": "second value"}.toXmlAttributes + let k = newXmlTree("treeTag", [g, h], att) + + doAssert $k == """ + some text + &some entity; +""" + ## **See also:** ## * `xmlparser module `_ for high-level XML parsing ## * `parsexml module `_ for low-level XML parsing @@ -146,21 +142,20 @@ proc newXmlTree*(tag: string, children: openArray[XmlNode], ## See also: ## * `newElement proc <#newElement,string>`_ ## * [<> macro](#<>.m,untyped) - ## - ## .. code-block:: - ## var g = newElement("myTag") - ## g.add newText("some text") - ## g.add newComment("this is comment") - ## var h = newElement("secondTag") - ## h.add newEntity("some entity") - ## let att = {"key1": "first value", "key2": "second value"}.toXmlAttributes - ## let k = newXmlTree("treeTag", [g, h], att) - ## - ## echo k - ## ## - ## ## some text - ## ## &some entity; - ## ## + + runnableExamples: + var g = newElement("myTag") + g.add newText("some text") + g.add newComment("this is comment") + var h = newElement("secondTag") + h.add newEntity("some entity") + let att = {"key1": "first value", "key2": "second value"}.toXmlAttributes + let k = newXmlTree("treeTag", [g, h], att) + + doAssert $k == """ + some text + &some entity; +""" result = newXmlNode(xnElement) result.fTag = tag @@ -390,26 +385,24 @@ proc `[]`*(n: var XmlNode, i: int): var XmlNode {.inline.} = proc clear*(n: var XmlNode) = ## Recursively clears all children of an XmlNode. ## - ## .. code-block:: - ## var g = newElement("myTag") - ## g.add newText("some text") - ## g.add newComment("this is comment") - ## - ## var h = newElement("secondTag") - ## h.add newEntity("some entity") - ## - ## let att = {"key1": "first value", "key2": "second value"}.toXmlAttributes - ## var k = newXmlTree("treeTag", [g, h], att) - ## - ## echo k - ## ## - ## ## some text - ## ## &some entity; - ## ## - ## - ## clear(k) - ## echo k - ## ## + runnableExamples: + var g = newElement("myTag") + g.add newText("some text") + g.add newComment("this is comment") + + var h = newElement("secondTag") + h.add newEntity("some entity") + + let att = {"key1": "first value", "key2": "second value"}.toXmlAttributes + var k = newXmlTree("treeTag", [g, h], att) + + doAssert $k == """ + some text + &some entity; +""" + + clear(k) + doAssert $k == """""" for i in 0 ..< n.len: clear(n[i]) @@ -419,25 +412,24 @@ proc clear*(n: var XmlNode) = iterator items*(n: XmlNode): XmlNode {.inline.} = ## Iterates over all direct children of `n`. - ## - ## **Examples:** - ## - ## .. code-block:: - ## var g = newElement("myTag") - ## g.add newText("some text") - ## g.add newComment("this is comment") - ## - ## var h = newElement("secondTag") - ## h.add newEntity("some entity") - ## g.add h - ## - ## assert $g == "some text&some entity;" - ## for x in g: # the same as `for x in items(g):` - ## echo x - ## - ## # some text - ## # - ## # &some entity; + + runnableExamples: + var g = newElement("myTag") + g.add newText("some text") + g.add newComment("this is comment") + + var h = newElement("secondTag") + h.add newEntity("some entity") + g.add h + + assert $g == "some text&some entity;" + + # for x in g: # the same as `for x in items(g):` + # echo x + + # some text + # + # &some entity; assert n.k == xnElement for i in 0 .. n.len-1: yield n[i] @@ -451,13 +443,12 @@ proc toXmlAttributes*(keyValuePairs: varargs[tuple[key, val: string]]): XmlAttributes = ## Converts `{key: value}` pairs into `XmlAttributes`. ## - ## .. code-block:: - ## let att = {"key1": "first value", "key2": "second value"}.toXmlAttributes - ## var j = newElement("myTag") - ## j.attrs = att - ## - ## echo j - ## ## + runnableExamples: + let att = {"key1": "first value", "key2": "second value"}.toXmlAttributes + var j = newElement("myTag") + j.attrs = att + + doAssert $j == """""" newStringTable(keyValuePairs) From ffccdf3c1a91e3a4ac79b209ae4f034ad253d97b Mon Sep 17 00:00:00 2001 From: RokkuCode Date: Mon, 23 Nov 2020 15:15:55 +0100 Subject: [PATCH 1050/3768] fixes #16080 (#16091) [backport:1.2] * fixes #16080 db_sqlite: Error: undeclared field: 'untypedLen' * redacting fix --- lib/impure/db_sqlite.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/impure/db_sqlite.nim b/lib/impure/db_sqlite.nim index ce57996be..5c79b8c2a 100644 --- a/lib/impure/db_sqlite.nim +++ b/lib/impure/db_sqlite.nim @@ -855,7 +855,7 @@ macro untypedLen(args: varargs[untyped]): int = template exec*(db: DbConn, stmtName: SqlPrepared, args: varargs[typed]): untyped = - when args.untypedLen > 0: + when untypedLen(args) > 0: if reset(stmtName.PStmt) != SQLITE_OK: dbError(db) if clear_bindings(stmtName.PStmt) != SQLITE_OK: From 7fc0c3dfbf5e27805fa75745acabf5ce2f0af693 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Tue, 24 Nov 2020 00:49:40 +0800 Subject: [PATCH 1051/3768] ref #5617 add lineinfo to complete (#16102) --- lib/pure/asyncmacro.nim | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/pure/asyncmacro.nim b/lib/pure/asyncmacro.nim index a70508736..dd2393a63 100644 --- a/lib/pure/asyncmacro.nim +++ b/lib/pure/asyncmacro.nim @@ -11,6 +11,13 @@ import macros, strutils, asyncfutures + +# TODO: Ref https://github.com/nim-lang/Nim/issues/5617 +# TODO: Add more line infos +proc newCallWithLineInfo(fromNode: NimNode; theProc: NimNode, args: varargs[NimNode]): NimNode = + result = newCall(theProc, args) + result.copyLineInfo(fromNode) + template createCb(retFutureSym, iteratorNameSym, strName, identName, futureVarCompletions: untyped) = bind finished @@ -52,15 +59,13 @@ proc createFutureVarCompletions(futureVarIdents: seq[NimNode], # Add calls to complete each FutureVar parameter. for ident in futureVarIdents: # Only complete them if they have not been completed already by the user. - # TODO: Once https://github.com/nim-lang/Nim/issues/5617 is fixed. - # TODO: Add line info to the complete() call! # In the meantime, this was really useful for debugging :) #result.add(newCall(newIdentNode("echo"), newStrLitNode(fromNode.lineinfo))) result.add newIfStmt( ( newCall(newIdentNode("not"), newDotExpr(ident, newIdentNode("finished"))), - newCall(newIdentNode("complete"), ident) + newCallWithLineInfo(fromNode, newIdentNode("complete"), ident) ) ) From 0c6c4be0e783893cc21352fa686fb18c158d3e43 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Tue, 24 Nov 2020 02:23:52 +0800 Subject: [PATCH 1052/3768] fix ropes format errors (#16106) [backport:1.0] * fix rope index * add testcase * fix ropes format --- lib/pure/ropes.nim | 2 +- tests/stdlib/tropes.nim | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/lib/pure/ropes.nim b/lib/pure/ropes.nim index 0797bbe06..a39533e58 100644 --- a/lib/pure/ropes.nim +++ b/lib/pure/ropes.nim @@ -262,7 +262,7 @@ proc `%`*(frmt: string, args: openArray[Rope]): Rope {. while true: j = j * 10 + ord(frmt[i]) - ord('0') inc(i) - if frmt[i] notin {'0'..'9'}: break + if i >= frmt.len or frmt[i] notin {'0'..'9'}: break add(result, args[j-1]) of '{': inc(i) diff --git a/tests/stdlib/tropes.nim b/tests/stdlib/tropes.nim index 9678e26c3..0c95d5c5f 100644 --- a/tests/stdlib/tropes.nim +++ b/tests/stdlib/tropes.nim @@ -64,3 +64,10 @@ block: inc i doAssert r[66] == '\0' + +block: + let r1 = "$1 $2 $3" % [rope("Nim"), rope("is"), rope("a great language")] + doAssert $r1 == "Nim is a great language" + + let r2 = "$# $# $#" % [rope("Nim"), rope("is"), rope("a great language")] + doAssert $r2 == "Nim is a great language" From a764a5b9bced8159d61647c42f6168a44453faff Mon Sep 17 00:00:00 2001 From: kemifl Date: Mon, 23 Nov 2020 19:53:57 +0100 Subject: [PATCH 1053/3768] fix #14056 (#16071) fix #14056, incorporate review comments --- lib/pure/xmlparser.nim | 20 ++++++++++---------- tests/stdlib/tmitems.nim | 29 +++++++++++++++++++++++++++-- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/lib/pure/xmlparser.nim b/lib/pure/xmlparser.nim index 2a2d19dca..3d9c288ed 100644 --- a/lib/pure/xmlparser.nim +++ b/lib/pure/xmlparser.nim @@ -99,11 +99,11 @@ proc parse(x: var XmlParser, errors: var seq[string]): XmlNode = of xmlEof: discard proc parseXml*(s: Stream, filename: string, - errors: var seq[string]): XmlNode = + errors: var seq[string], options: set[XmlParseOption] = {reportComments}): XmlNode = ## Parses the XML from stream ``s`` and returns a ``XmlNode``. Every ## occurred parsing error is added to the ``errors`` sequence. var x: XmlParser - open(x, s, filename, {reportComments}) + open(x, s, filename, options) while true: x.next() case x.kind @@ -118,32 +118,32 @@ proc parseXml*(s: Stream, filename: string, break close(x) -proc parseXml*(s: Stream): XmlNode = +proc parseXml*(s: Stream, options: set[XmlParseOption] = {reportComments}): XmlNode = ## Parses the XML from stream ``s`` and returns a ``XmlNode``. All parsing ## errors are turned into an ``XmlError`` exception. var errors: seq[string] = @[] - result = parseXml(s, "unknown_xml_doc", errors) + result = parseXml(s, "unknown_xml_doc", errors, options) if errors.len > 0: raiseInvalidXml(errors) -proc parseXml*(str: string): XmlNode = +proc parseXml*(str: string, options: set[XmlParseOption] = {reportComments}): XmlNode = ## Parses the XML from string ``str`` and returns a ``XmlNode``. All parsing ## errors are turned into an ``XmlError`` exception. - parseXml(newStringStream(str)) + parseXml(newStringStream(str), options) -proc loadXml*(path: string, errors: var seq[string]): XmlNode = +proc loadXml*(path: string, errors: var seq[string], options: set[XmlParseOption] = {reportComments}): XmlNode = ## Loads and parses XML from file specified by ``path``, and returns ## a ``XmlNode``. Every occurred parsing error is added to the ``errors`` ## sequence. var s = newFileStream(path, fmRead) if s == nil: raise newException(IOError, "Unable to read file: " & path) - result = parseXml(s, path, errors) + result = parseXml(s, path, errors, options) -proc loadXml*(path: string): XmlNode = +proc loadXml*(path: string, options: set[XmlParseOption] = {reportComments}): XmlNode = ## Loads and parses XML from file specified by ``path``, and returns ## a ``XmlNode``. All parsing errors are turned into an ``XmlError`` ## exception. var errors: seq[string] = @[] - result = loadXml(path, errors) + result = loadXml(path, errors, options) if errors.len > 0: raiseInvalidXml(errors) when isMainModule: diff --git a/tests/stdlib/tmitems.nim b/tests/stdlib/tmitems.nim index 17265e1f7..c0ced7cab 100644 --- a/tests/stdlib/tmitems.nim +++ b/tests/stdlib/tmitems.nim @@ -16,7 +16,18 @@ fpqeew -''' + + + This is a Docbook title + + This is a Docbook paragraph containing emphasized, + literal and replaceable + text. Sometimes scrunched together like this: + literalreplaceable + and sometimes not: + literal replaceable + +''' """ block: @@ -123,7 +134,7 @@ block: x.num += 10 echo j -import xmltree, xmlparser, streams, strtabs +import xmltree, xmlparser, parsexml, streams, strtabs block: var d = parseXml(newStringStream """ @@ -134,3 +145,17 @@ block: x = <>Student(Name=x.attrs["Name"] & "foo") d[1].attrs["Name"] = "bar" echo d + +block: + var d = parseXml(newStringStream """ + This is a Docbook title + + This is a Docbook paragraph containing emphasized, + literal and replaceable + text. Sometimes scrunched together like this: + literalreplaceable + and sometimes not: + literal replaceable + +""",{reportComments, reportWhitespace}) + echo d From e4d0f9f3de3b62549fd6503e724a047abbadf03f Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Tue, 24 Nov 2020 05:56:30 +0800 Subject: [PATCH 1054/3768] typeinfo minor improvement (#16083) * typeinfo minor improvement * minor * Update lib/core/typeinfo.nim * rename typeinfo --- lib/core/typeinfo.nim | 163 +++++++------------- tests/cpp/{ttypeinfo.nim => ttypeinfo1.nim} | 0 tests/stdlib/ttypeinfo.nim | 60 +++++++ 3 files changed, 112 insertions(+), 111 deletions(-) rename tests/cpp/{ttypeinfo.nim => ttypeinfo1.nim} (100%) create mode 100644 tests/stdlib/ttypeinfo.nim diff --git a/lib/core/typeinfo.nim b/lib/core/typeinfo.nim index ca531b4b9..fff237718 100644 --- a/lib/core/typeinfo.nim +++ b/lib/core/typeinfo.nim @@ -142,7 +142,7 @@ proc newAny(value: pointer, rawType: PNimType): Any {.inline.} = when declared(system.VarSlot): proc toAny*(x: VarSlot): Any {.inline.} = - ## constructs a ``Any`` object from a variable slot ``x``. + ## Constructs a ``Any`` object from a variable slot ``x``. ## This captures `x`'s address, so `x` can be modified with its ## ``Any`` wrapper! The client needs to ensure that the wrapper ## **does not** live longer than `x`! @@ -151,31 +151,31 @@ when declared(system.VarSlot): result.rawType = x.typ proc toAny*[T](x: var T): Any {.inline.} = - ## constructs a ``Any`` object from `x`. This captures `x`'s address, so + ## Constructs a ``Any`` object from `x`. This captures `x`'s address, so ## `x` can be modified with its ``Any`` wrapper! The client needs to ensure ## that the wrapper **does not** live longer than `x`! newAny(addr(x), cast[PNimType](getTypeInfo(x))) proc kind*(x: Any): AnyKind {.inline.} = - ## get the type kind + ## Gets the type kind. result = AnyKind(ord(x.rawType.kind)) proc size*(x: Any): int {.inline.} = - ## returns the size of `x`'s type. + ## Returns the size of `x`'s type. result = x.rawType.size proc baseTypeKind*(x: Any): AnyKind {.inline.} = - ## get the base type's kind; ``akNone`` is returned if `x` has no base type. + ## Gets the base type's kind; ``akNone`` is returned if `x` has no base type. if x.rawType.base != nil: result = AnyKind(ord(x.rawType.base.kind)) proc baseTypeSize*(x: Any): int {.inline.} = - ## returns the size of `x`'s basetype. + ## Returns the size of `x`'s basetype. if x.rawType.base != nil: result = x.rawType.base.size proc invokeNew*(x: Any) = - ## performs ``new(x)``. `x` needs to represent a ``ref``. + ## Performs ``new(x)``. `x` needs to represent a ``ref``. assert x.rawType.kind == tyRef when defined(gcDestructors): cast[ppointer](x.value)[] = nimNewObj(x.rawType.base.size, x.rawType.base.align) @@ -184,7 +184,7 @@ proc invokeNew*(x: Any) = genericAssign(x.value, addr(z), x.rawType) proc invokeNewSeq*(x: Any, len: int) = - ## performs ``newSeq(x, len)``. `x` needs to represent a ``seq``. + ## Performs ``newSeq(x, len)``. `x` needs to represent a ``seq``. assert x.rawType.kind == tySequence when defined(gcDestructors): var s = cast[ptr NimSeqV2Reimpl](x.value) @@ -196,7 +196,7 @@ proc invokeNewSeq*(x: Any, len: int) = genericShallowAssign(x.value, addr(z), x.rawType) proc extendSeq*(x: Any) = - ## performs ``setLen(x, x.len+1)``. `x` needs to represent a ``seq``. + ## Performs ``setLen(x, x.len+1)``. `x` needs to represent a ``seq``. assert x.rawType.kind == tySequence when defined(gcDestructors): var s = cast[ptr NimSeqV2Reimpl](x.value) @@ -212,7 +212,7 @@ proc extendSeq*(x: Any) = #genericShallowAssign(x.value, addr(z), x.rawType) proc setObjectRuntimeType*(x: Any) = - ## this needs to be called to set `x`'s runtime object type field. + ## This needs to be called to set `x`'s runtime object type field. assert x.rawType.kind == tyObject when defined(gcDestructors): cast[ppointer](x.value)[] = x.rawType.typeInfoV2 @@ -227,7 +227,7 @@ proc align(address, alignment: int): int = result = (address + (alignment - 1)) and not (alignment - 1) proc `[]`*(x: Any, i: int): Any = - ## accessor for an any `x` that represents an array or a sequence. + ## Accessor for an any `x` that represents an array or a sequence. case x.rawType.kind of tyArray: let bs = x.rawType.base.size @@ -253,7 +253,7 @@ proc `[]`*(x: Any, i: int): Any = else: assert false proc `[]=`*(x: Any, i: int, y: Any) = - ## accessor for an any `x` that represents an array or a sequence. + ## Accessor for an any `x` that represents an array or a sequence. case x.rawType.kind of tyArray: var bs = x.rawType.base.size @@ -282,7 +282,7 @@ proc `[]=`*(x: Any, i: int, y: Any) = else: assert false proc len*(x: Any): int = - ## len for an any `x` that represents an array or a sequence. + ## `len` for an any `x` that represents an array or a sequence. case x.rawType.kind of tyArray: result = x.rawType.size div x.rawType.base.size @@ -299,7 +299,7 @@ proc len*(x: Any): int = proc base*(x: Any): Any = - ## returns base Any (useful for inherited object types). + ## Returns base Any (useful for inherited object types). result.rawType = x.rawType.base result.value = x.value @@ -316,14 +316,14 @@ const tySequence, tyProc} proc getPointer*(x: Any): pointer = - ## retrieve the pointer value out of `x`. ``x`` needs to be of kind + ## Retrieves the pointer value out of `x`. ``x`` needs to be of kind ## ``akString``, ``akCString``, ``akProc``, ``akRef``, ``akPtr``, ## ``akPointer``, ``akSequence``. assert x.rawType.kind in pointerLike result = cast[ppointer](x.value)[] proc setPointer*(x: Any, y: pointer) = - ## sets the pointer value of `x`. ``x`` needs to be of kind + ## Sets the pointer value of `x`. ``x`` needs to be of kind ## ``akString``, ``akCString``, ``akProc``, ``akRef``, ``akPtr``, ## ``akPointer``, ``akSequence``. assert x.rawType.kind in pointerLike @@ -347,7 +347,7 @@ proc fieldsAux(p: pointer, n: ptr TNimNode, if m != nil: fieldsAux(p, m, ret) iterator fields*(x: Any): tuple[name: string, any: Any] = - ## iterates over every active field of the any `x` that represents an object + ## Iterates over every active field of the any `x` that represents an object ## or a tuple. assert x.rawType.kind in {tyTuple, tyObject} var p = x.value @@ -401,7 +401,7 @@ proc getFieldNode(p: pointer, n: ptr TNimNode, if m != nil: result = getFieldNode(p, m, name) proc `[]=`*(x: Any, fieldName: string, value: Any) = - ## sets a field of `x`; `x` represents an object or a tuple. + ## Sets a field of `x`; `x` represents an object or a tuple. var t = x.rawType # XXX BUG: does not work yet, however is questionable anyway when false: @@ -415,7 +415,7 @@ proc `[]=`*(x: Any, fieldName: string, value: Any) = raise newException(ValueError, "invalid field name: " & fieldName) proc `[]`*(x: Any, fieldName: string): Any = - ## gets a field of `x`; `x` represents an object or a tuple. + ## Gets a field of `x`; `x` represents an object or a tuple. var t = x.rawType # XXX BUG: does not work yet, however is questionable anyway when false: @@ -431,44 +431,44 @@ proc `[]`*(x: Any, fieldName: string): Any = raise newException(ValueError, "invalid field name: " & fieldName) proc `[]`*(x: Any): Any = - ## dereference operation for the any `x` that represents a ptr or a ref. + ## Dereference operation for the any `x` that represents a ptr or a ref. assert x.rawType.kind in {tyRef, tyPtr} result.value = cast[ppointer](x.value)[] result.rawType = x.rawType.base proc `[]=`*(x, y: Any) = - ## dereference operation for the any `x` that represents a ptr or a ref. + ## Dereference operation for the any `x` that represents a ptr or a ref. assert x.rawType.kind in {tyRef, tyPtr} assert y.rawType == x.rawType.base genericAssign(cast[ppointer](x.value)[], y.value, y.rawType) proc getInt*(x: Any): int = - ## retrieve the int value out of `x`. `x` needs to represent an int. + ## Retrieves the int value out of `x`. `x` needs to represent an int. assert skipRange(x.rawType).kind == tyInt result = cast[ptr int](x.value)[] proc getInt8*(x: Any): int8 = - ## retrieve the int8 value out of `x`. `x` needs to represent an int8. + ## Retrieves the int8 value out of `x`. `x` needs to represent an int8. assert skipRange(x.rawType).kind == tyInt8 result = cast[ptr int8](x.value)[] proc getInt16*(x: Any): int16 = - ## retrieve the int16 value out of `x`. `x` needs to represent an int16. + ## Retrieves the int16 value out of `x`. `x` needs to represent an int16. assert skipRange(x.rawType).kind == tyInt16 result = cast[ptr int16](x.value)[] proc getInt32*(x: Any): int32 = - ## retrieve the int32 value out of `x`. `x` needs to represent an int32. + ## Retrieves the int32 value out of `x`. `x` needs to represent an int32. assert skipRange(x.rawType).kind == tyInt32 result = cast[ptr int32](x.value)[] proc getInt64*(x: Any): int64 = - ## retrieve the int64 value out of `x`. `x` needs to represent an int64. + ## Retrieves the int64 value out of `x`. `x` needs to represent an int64. assert skipRange(x.rawType).kind == tyInt64 result = cast[ptr int64](x.value)[] proc getBiggestInt*(x: Any): BiggestInt = - ## retrieve the integer value out of `x`. `x` needs to represent + ## Retrieves the integer value out of `x`. `x` needs to represent ## some integer, a bool, a char, an enum or a small enough bit set. ## The value might be sign-extended to ``BiggestInt``. var t = skipRange(x.rawType) @@ -494,7 +494,7 @@ proc getBiggestInt*(x: Any): BiggestInt = else: assert false proc setBiggestInt*(x: Any, y: BiggestInt) = - ## sets the integer value of `x`. `x` needs to represent + ## Sets the integer value of `x`. `x` needs to represent ## some integer, a bool, a char, an enum or a small enough bit set. var t = skipRange(x.rawType) case t.kind @@ -519,36 +519,36 @@ proc setBiggestInt*(x: Any, y: BiggestInt) = else: assert false proc getUInt*(x: Any): uint = - ## retrieve the uint value out of `x`, `x` needs to represent an uint. + ## Retrieves the uint value out of `x`, `x` needs to represent an uint. assert skipRange(x.rawType).kind == tyUInt result = cast[ptr uint](x.value)[] proc getUInt8*(x: Any): uint8 = - ## retrieve the uint8 value out of `x`, `x` needs to represent an + ## Retrieves the uint8 value out of `x`, `x` needs to represent an ## uint8. assert skipRange(x.rawType).kind == tyUInt8 result = cast[ptr uint8](x.value)[] proc getUInt16*(x: Any): uint16 = - ## retrieve the uint16 value out of `x`, `x` needs to represent an + ## Retrieves the uint16 value out of `x`, `x` needs to represent an ## uint16. assert skipRange(x.rawType).kind == tyUInt16 result = cast[ptr uint16](x.value)[] proc getUInt32*(x: Any): uint32 = - ## retrieve the uint32 value out of `x`, `x` needs to represent an + ## Retrieves the uint32 value out of `x`, `x` needs to represent an ## uint32. assert skipRange(x.rawType).kind == tyUInt32 result = cast[ptr uint32](x.value)[] proc getUInt64*(x: Any): uint64 = - ## retrieve the uint64 value out of `x`, `x` needs to represent an + ## Retrieves the uint64 value out of `x`, `x` needs to represent an ## uint64. assert skipRange(x.rawType).kind == tyUInt64 result = cast[ptr uint64](x.value)[] proc getBiggestUint*(x: Any): uint64 = - ## retrieve the unsigned integer value out of `x`. `x` needs to + ## Retrieves the unsigned integer value out of `x`. `x` needs to ## represent an unsigned integer. var t = skipRange(x.rawType) case t.kind @@ -560,7 +560,7 @@ proc getBiggestUint*(x: Any): uint64 = else: assert false proc setBiggestUint*(x: Any; y: uint64) = - ## sets the unsigned integer value of `c`. `c` needs to represent an + ## Sets the unsigned integer value of `c`. `c` needs to represent an ## unsigned integer. var t = skipRange(x.rawType) case t.kind: @@ -572,25 +572,25 @@ proc setBiggestUint*(x: Any; y: uint64) = else: assert false proc getChar*(x: Any): char = - ## retrieve the char value out of `x`. `x` needs to represent a char. + ## Retrieves the char value out of `x`. `x` needs to represent a char. var t = skipRange(x.rawType) assert t.kind == tyChar result = cast[ptr char](x.value)[] proc getBool*(x: Any): bool = - ## retrieve the bool value out of `x`. `x` needs to represent a bool. + ## Retrieves the bool value out of `x`. `x` needs to represent a bool. var t = skipRange(x.rawType) assert t.kind == tyBool result = cast[ptr bool](x.value)[] proc skipRange*(x: Any): Any = - ## skips the range information of `x`. + ## Skips the range information of `x`. assert x.rawType.kind == tyRange result.rawType = x.rawType.base result.value = x.value proc getEnumOrdinal*(x: Any, name: string): int = - ## gets the enum field ordinal from `name`. `x` needs to represent an enum + ## Gets the enum field ordinal from `name`. `x` needs to represent an enum ## but is only used to access the type information. In case of an error ## ``low(int)`` is returned. var typ = skipRange(x.rawType) @@ -606,7 +606,7 @@ proc getEnumOrdinal*(x: Any, name: string): int = result = low(int) proc getEnumField*(x: Any, ordinalValue: int): string = - ## gets the enum field name as a string. `x` needs to represent an enum + ## Gets the enum field name as a string. `x` needs to represent an enum ## but is only used to access the type information. The field name of ## `ordinalValue` is returned. var typ = skipRange(x.rawType) @@ -624,26 +624,26 @@ proc getEnumField*(x: Any, ordinalValue: int): string = result = $e proc getEnumField*(x: Any): string = - ## gets the enum field name as a string. `x` needs to represent an enum. + ## Gets the enum field name as a string. `x` needs to represent an enum. result = getEnumField(x, getBiggestInt(x).int) proc getFloat*(x: Any): float = - ## retrieve the float value out of `x`. `x` needs to represent an float. + ## Retrieves the float value out of `x`. `x` needs to represent an float. assert skipRange(x.rawType).kind == tyFloat result = cast[ptr float](x.value)[] proc getFloat32*(x: Any): float32 = - ## retrieve the float32 value out of `x`. `x` needs to represent an float32. + ## Retrieves the float32 value out of `x`. `x` needs to represent an float32. assert skipRange(x.rawType).kind == tyFloat32 result = cast[ptr float32](x.value)[] proc getFloat64*(x: Any): float64 = - ## retrieve the float64 value out of `x`. `x` needs to represent an float64. + ## Retrieves the float64 value out of `x`. `x` needs to represent an float64. assert skipRange(x.rawType).kind == tyFloat64 result = cast[ptr float64](x.value)[] proc getBiggestFloat*(x: Any): BiggestFloat = - ## retrieve the float value out of `x`. `x` needs to represent + ## Retrieves the float value out of `x`. `x` needs to represent ## some float. The value is extended to ``BiggestFloat``. case skipRange(x.rawType).kind of tyFloat: result = BiggestFloat(cast[ptr float](x.value)[]) @@ -652,7 +652,7 @@ proc getBiggestFloat*(x: Any): BiggestFloat = else: assert false proc setBiggestFloat*(x: Any, y: BiggestFloat) = - ## sets the float value of `x`. `x` needs to represent + ## Sets the float value of `x`. `x` needs to represent ## some float. case skipRange(x.rawType).kind of tyFloat: cast[ptr float](x.value)[] = y @@ -661,7 +661,7 @@ proc setBiggestFloat*(x: Any, y: BiggestFloat) = else: assert false proc getString*(x: Any): string = - ## retrieve the string value out of `x`. `x` needs to represent a string. + ## Retrieves the string value out of `x`. `x` needs to represent a string. assert x.rawType.kind == tyString when defined(gcDestructors): result = cast[ptr string](x.value)[] @@ -670,23 +670,23 @@ proc getString*(x: Any): string = result = cast[ptr string](x.value)[] proc setString*(x: Any, y: string) = - ## sets the string value of `x`. `x` needs to represent a string. + ## Sets the string value of `x`. `x` needs to represent a string. assert x.rawType.kind == tyString cast[ptr string](x.value)[] = y # also correct for gcDestructors proc getCString*(x: Any): cstring = - ## retrieve the cstring value out of `x`. `x` needs to represent a cstring. + ## Retrieves the cstring value out of `x`. `x` needs to represent a cstring. assert x.rawType.kind == tyCString result = cast[ptr cstring](x.value)[] proc assign*(x, y: Any) = - ## copies the value of `y` to `x`. The assignment operator for ``Any`` + ## Copies the value of `y` to `x`. The assignment operator for ``Any`` ## does NOT do this; it performs a shallow copy instead! assert y.rawType == x.rawType genericAssign(x.value, y.value, y.rawType) iterator elements*(x: Any): int = - ## iterates over every element of `x` that represents a Nim bitset. + ## Iterates over every element of `x` that represents a Nim bitset. assert x.rawType.kind == tySet var typ = x.rawType var p = x.value @@ -708,7 +708,7 @@ iterator elements*(x: Any): int = yield i+typ.node.len proc inclSetElement*(x: Any, elem: int) = - ## includes an element `elem` in `x`. `x` needs to represent a Nim bitset. + ## Includes an element `elem` in `x`. `x` needs to represent a Nim bitset. assert x.rawType.kind == tySet var typ = x.rawType var p = x.value @@ -730,62 +730,3 @@ proc inclSetElement*(x: Any, elem: int) = else: var a = cast[pbyteArray](p) a[e shr 3] = toU8(a[e shr 3] or (1 shl (e and 7))) - -when isMainModule: - type - TE = enum - blah, blah2 - - TestObj = object - test, asd: int - case test2: TE - of blah: - help: string - else: - nil - - var test = @[0,1,2,3,4] - var x = toAny(test) - var y = 78 - x[4] = toAny(y) - assert cast[ptr int](x[2].value)[] == 2 - - var test2: tuple[name: string, s: int] = ("test", 56) - var x2 = toAny(test2) - var i = 0 - for n, a in fields(x2): - case i - of 0: assert n == "name" and $a.kind == "akString" - of 1: assert n == "s" and $a.kind == "akInt" - else: assert false - inc i - - var test3: TestObj - test3.test = 42 - test3 = TestObj(test2: blah2) - var x3 = toAny(test3) - i = 0 - for n, a in fields(x3): - case i - of 0: assert n == "test" and $a.kind == "akInt" - of 1: assert n == "asd" and $a.kind == "akInt" - of 2: assert n == "test2" and $a.kind == "akEnum" - else: assert false - inc i - - var test4: ref string - new(test4) - test4[] = "test" - var x4 = toAny(test4) - assert($x4[].kind() == "akString") - - block: - # gimme a new scope dammit - var myarr: array[0..4, array[0..4, string]] = [ - ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"], - ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"], - ["test", "1", "2", "3", "4"]] - var m = toAny(myArr) - for i in 0 .. m.len-1: - for j in 0 .. m[i].len-1: - echo getString(m[i][j]) diff --git a/tests/cpp/ttypeinfo.nim b/tests/cpp/ttypeinfo1.nim similarity index 100% rename from tests/cpp/ttypeinfo.nim rename to tests/cpp/ttypeinfo1.nim diff --git a/tests/stdlib/ttypeinfo.nim b/tests/stdlib/ttypeinfo.nim new file mode 100644 index 000000000..3155a0f88 --- /dev/null +++ b/tests/stdlib/ttypeinfo.nim @@ -0,0 +1,60 @@ +import typeinfo + +type + TE = enum + blah, blah2 + + TestObj = object + test, asd: int + case test2: TE + of blah: + help: string + else: + nil + + +var test = @[0,1,2,3,4] +var x = toAny(test) +var y = 78 +x[4] = toAny(y) +assert x[2].getInt == 2 + +var test2: tuple[name: string, s: int] = ("test", 56) +var x2 = toAny(test2) +var i = 0 +for n, a in fields(x2): + case i + of 0: assert n == "Field0" and $a.kind == "akString" + of 1: assert n == "Field1" and $a.kind == "akInt" + else: assert false + inc i + +var test3: TestObj +test3.test = 42 +test3 = TestObj(test2: blah2) +var x3 = toAny(test3) +i = 0 +for n, a in fields(x3): + case i + of 0: assert n == "test" and $a.kind == "akInt" + of 1: assert n == "asd" and $a.kind == "akInt" + of 2: assert n == "test2" and $a.kind == "akEnum" + else: assert false + inc i + +var test4: ref string +new(test4) +test4[] = "test" +var x4 = toAny(test4) +assert($x4[].kind() == "akString") + +block: + # gimme a new scope dammit + var myarr: array[0..4, array[0..4, string]] = [ + ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"], + ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"], + ["test", "1", "2", "3", "4"]] + var m = toAny(myArr) + for i in 0 .. m.len-1: + for j in 0 .. m[i].len-1: + doAssert getString(m[i][j]) == myArr[i][j] From 823a71380d9c52c224669fe0dfcf5601619978bd Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Tue, 24 Nov 2020 19:37:41 +0800 Subject: [PATCH 1055/3768] fix #16103 (#16109) [backport:1.0] * fix #16103 * docs --- lib/impure/nre.nim | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/lib/impure/nre.nim b/lib/impure/nre.nim index 4696f7322..bd17c713e 100644 --- a/lib/impure/nre.nim +++ b/lib/impure/nre.nim @@ -68,7 +68,6 @@ from pcre import nil import nre/private/util import tables from strutils import `%` -from math import ceil import options from unicode import runeLenAt @@ -742,8 +741,25 @@ proc replace*(str: string, pattern: Regex, sub: string): string = replaceImpl(str, pattern, formatStr(sub, match.captures[name], match.captures[id - 1])) -let SpecialCharMatcher = re"([\\+*?[^\]$(){}=!<>|:-])" -proc escapeRe*(str: string): string = - ## Escapes the string so it doesn’t match any special characters. +proc escapeRe*(str: string): string {.gcsafe.} = + ## Escapes the string so it doesn't match any special characters. ## Incompatible with the Extra flag (``X``). - str.replace(SpecialCharMatcher, "\\$1") + ## + ## Escaped char: `\ + * ? [ ^ ] $ ( ) { } = ! < > | : -` + runnableExamples: + doAssert escapeRe("fly+wind") == "fly\\+wind" + doAssert escapeRe("!") == "\\!" + doAssert escapeRe("nim*") == "nim\\*" + + #([\\+*?[^\]$(){}=!<>|:-]) + const SpecialCharMatcher = {'\\', '+', '*', '?', '[', '^', ']', '$', '(', + ')', '{', '}', '=', '!', '<', '>', '|', ':', + '-'} + + for c in items(str): + case c + of SpecialCharMatcher: + result.add("\\") + result.add(c) + else: + result.add(c) From f8b9d8c1904c662f07e5ee1138357695a1df9ac3 Mon Sep 17 00:00:00 2001 From: "Andrey R (cooldome)" Date: Tue, 24 Nov 2020 11:53:55 +0000 Subject: [PATCH 1056/3768] fix #16110 --- tests/macros/tgetimpl.nim | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/macros/tgetimpl.nim b/tests/macros/tgetimpl.nim index d231a4336..54fd2b3e5 100644 --- a/tests/macros/tgetimpl.nim +++ b/tests/macros/tgetimpl.nim @@ -65,3 +65,22 @@ macro check_gen_proc(ex: typed): (bool, bool) = let a = @[1,2,3] assert: check_gen_proc(len(a)) == (false, true) + + +#--------------------------------------------------------------- +# issue #16110 + +macro check(x: type): untyped = + let z = getType(x) + let y = getImpl(z[1]) + echo z.treeRepr + expectKind(z[1], nnkSym) + expectKind(y[0], nnkSym) + doAssert(y[0] == z[1]) + +type + TirePtr = ptr object + code: int + +var z: TirePtr +check(typeof(z[])) \ No newline at end of file From 2bab2a2cd8476423414819db3ad79309f6033e13 Mon Sep 17 00:00:00 2001 From: "Andrey R (cooldome)" Date: Tue, 24 Nov 2020 11:58:02 +0000 Subject: [PATCH 1057/3768] fix #16110 --- compiler/semstmts.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index cd7428b62..713867b76 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1278,7 +1278,8 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = incl st.flags, tfRefsAnonObj let obj = newSym(skType, getIdent(c.cache, s.name.s & ":ObjectType"), nextId c.idgen, getCurrOwner(c), s.info) - obj.ast = a + obj.ast = a.copyTree + obj.ast[0] = newSymNode(obj) if sfPure in s.flags: obj.flags.incl sfPure obj.typ = st.lastSon From baae8ef38fc932060f225c96146f4579135e1152 Mon Sep 17 00:00:00 2001 From: "Andrey R (cooldome)" Date: Tue, 24 Nov 2020 12:10:43 +0000 Subject: [PATCH 1058/3768] Revert "fix #16110" This reverts commit 2bab2a2cd8476423414819db3ad79309f6033e13. --- compiler/semstmts.nim | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 713867b76..cd7428b62 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1278,8 +1278,7 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = incl st.flags, tfRefsAnonObj let obj = newSym(skType, getIdent(c.cache, s.name.s & ":ObjectType"), nextId c.idgen, getCurrOwner(c), s.info) - obj.ast = a.copyTree - obj.ast[0] = newSymNode(obj) + obj.ast = a if sfPure in s.flags: obj.flags.incl sfPure obj.typ = st.lastSon From 9a2a83ad71ce3766a7b19d7a04613291e6007546 Mon Sep 17 00:00:00 2001 From: "Andrey R (cooldome)" Date: Tue, 24 Nov 2020 12:11:13 +0000 Subject: [PATCH 1059/3768] Revert "fix #16110" This reverts commit f8b9d8c1904c662f07e5ee1138357695a1df9ac3. --- tests/macros/tgetimpl.nim | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/tests/macros/tgetimpl.nim b/tests/macros/tgetimpl.nim index 54fd2b3e5..d231a4336 100644 --- a/tests/macros/tgetimpl.nim +++ b/tests/macros/tgetimpl.nim @@ -65,22 +65,3 @@ macro check_gen_proc(ex: typed): (bool, bool) = let a = @[1,2,3] assert: check_gen_proc(len(a)) == (false, true) - - -#--------------------------------------------------------------- -# issue #16110 - -macro check(x: type): untyped = - let z = getType(x) - let y = getImpl(z[1]) - echo z.treeRepr - expectKind(z[1], nnkSym) - expectKind(y[0], nnkSym) - doAssert(y[0] == z[1]) - -type - TirePtr = ptr object - code: int - -var z: TirePtr -check(typeof(z[])) \ No newline at end of file From 9a86198ed5c27c11bf005588bd4e70927d30230e Mon Sep 17 00:00:00 2001 From: Miran Date: Tue, 24 Nov 2020 14:26:52 +0100 Subject: [PATCH 1060/3768] fix export links in the documentation (#16114) [backport:1.4] * fix export links in the documentation * fix `len` problem --- compiler/docgen.nim | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 57fdad12a..6ee03e292 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -718,7 +718,7 @@ proc complexName(k: TSymKind, n: PNode, baseName: string): string = of skTemplate: result.add(".t") of skConverter: result.add(".c") else: discard - if n.len > paramsPos and n[paramsPos].kind == nkFormalParams: + if n.safeLen > paramsPos and n[paramsPos].kind == nkFormalParams: let params = renderParamTypes(n[paramsPos]) if params.len > 0: result.add(defaultParamSeparator) @@ -968,13 +968,17 @@ proc exportSym(d: PDoc; s: PSym) = elif s.kind != skModule and s.owner != nil: let module = originatingModule(s) if belongsToPackage(d.conf, module): - let external = externalDep(d, module) + let + complexSymbol = complexName(s.kind, s.ast, s.name.s) + symbolOrIdRope = rope(d.newUniquePlainSymbol(complexSymbol)) + external = externalDep(d, module) if d.section[k] != nil: d.section[k].add(", ") # XXX proper anchor generation here dispA(d.conf, d.section[k], - "$1", + "$1", "$1", [rope esc(d.target, s.name.s), - rope changeFileExt(external, "html")]) + rope changeFileExt(external, "html"), + symbolOrIdRope]) proc documentNewEffect(cache: IdentCache; n: PNode): PNode = let s = n[namePos].sym @@ -1077,7 +1081,7 @@ proc generateDoc*(d: PDoc, n, orig: PNode, docFlags: DocFlags = kDefault) = if it.kind == nkSym: if d.module != nil and d.module == it.sym.owner: generateDoc(d, it.sym.ast, orig, kForceExport) - else: + elif it.sym.ast != nil: exportSym(d, it.sym) of nkExportExceptStmt: discard "transformed into nkExportStmt by semExportExcept" of nkFromStmt, nkImportExceptStmt: traceDeps(d, n[0]) From afb8b69c0a61fbc821cd3ce014c017a3523bd3d2 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Tue, 24 Nov 2020 21:29:34 +0800 Subject: [PATCH 1061/3768] improve document for heapqueue (#16107) --- lib/pure/collections/heapqueue.nim | 103 ++++++++++++++++++----------- 1 file changed, 63 insertions(+), 40 deletions(-) diff --git a/lib/pure/collections/heapqueue.nim b/lib/pure/collections/heapqueue.nim index 244ccbbee..00e7a3b9d 100644 --- a/lib/pure/collections/heapqueue.nim +++ b/lib/pure/collections/heapqueue.nim @@ -6,50 +6,48 @@ # See the file "copying.txt", included in this # distribution, for details about the copyright. -##[ - The `heapqueue` module implements a - `heap data structure`_ - that can be used as a - `priority queue`_. - Heaps are arrays for which `a[k] <= a[2*k+1]` and `a[k] <= a[2*k+2]` for - all `k`, counting elements from 0. The interesting property of a heap is that - `a[0]` is always its smallest element. - Basic usage - ----------- - .. code-block:: Nim - import heapqueue +## The `heapqueue` module implements a +## `heap data structure`_ +## that can be used as a +## `priority queue`_. +## Heaps are arrays for which `a[k] <= a[2*k+1]` and `a[k] <= a[2*k+2]` for +## all `k`, counting elements from 0. The interesting property of a heap is that +## `a[0]` is always its smallest element. +## +## Basic usage +## ----------- +## - var heap = initHeapQueue[int]() - heap.push(8) - heap.push(2) - heap.push(5) - # The first element is the lowest element - assert heap[0] == 2 - # Remove and return the lowest element - assert heap.pop() == 2 - # The lowest element remaining is 5 - assert heap[0] == 5 +runnableExamples: + var heap = initHeapQueue[int]() + heap.push(8) + heap.push(2) + heap.push(5) + # The first element is the lowest element + assert heap[0] == 2 + # Remove and return the lowest element + assert heap.pop() == 2 + # The lowest element remaining is 5 + assert heap[0] == 5 - Usage with custom object - ------------------------ - To use a `HeapQueue` with a custom object, the `<` operator must be - implemented. +## Usage with custom object +## ------------------------ +## To use a `HeapQueue` with a custom object, the `<` operator must be +## implemented. - .. code-block:: Nim - import heapqueue +runnableExamples: + type Job = object + priority: int - type Job = object - priority: int + proc `<`(a, b: Job): bool = a.priority < b.priority - proc `<`(a, b: Job): bool = a.priority < b.priority + var jobs = initHeapQueue[Job]() + jobs.push(Job(priority: 1)) + jobs.push(Job(priority: 2)) - var jobs = initHeapQueue[Job]() - jobs.push(Job(priority: 1)) - jobs.push(Job(priority: 2)) + assert jobs[0].priority == 1 - assert jobs[0].priority == 1 -]## import std/private/since type HeapQueue*[T] = object @@ -135,6 +133,9 @@ proc toHeapQueue*[T](x: openArray[T]): HeapQueue[T] {.since: (1, 3).} = proc pop*[T](heap: var HeapQueue[T]): T = ## Pops and returns the smallest item from `heap`, ## maintaining the heap invariant. + runnableExamples: + var heap = toHeapQueue([9, 5, 8]) + assert heap.pop() == 5 let lastelt = heap.data.pop() if heap.len > 0: result = heap[0] @@ -145,12 +146,22 @@ proc pop*[T](heap: var HeapQueue[T]): T = proc find*[T](heap: HeapQueue[T], x: T): int {.since: (1, 3).} = ## Linear scan to find index of item ``x`` or -1 if not found. + runnableExamples: + var heap = toHeapQueue([9, 5, 8]) + assert heap.find(5) == 0 + assert heap.find(9) == 1 + assert heap.find(777) == -1 result = -1 for i in 0 ..< heap.len: if heap[i] == x: return i proc del*[T](heap: var HeapQueue[T], index: Natural) = ## Removes the element at `index` from `heap`, maintaining the heap invariant. + runnableExamples: + var heap = toHeapQueue([9, 5, 8]) + heap.del(1) + assert heap[0] == 5 + assert heap[1] == 8 swap(heap.data[^1], heap.data[index]) let newLen = heap.len - 1 heap.data.setLen(newLen) @@ -163,16 +174,28 @@ proc replace*[T](heap: var HeapQueue[T], item: T): T = ## more appropriate when using a fixed-size heap. Note that the value ## returned may be larger than item! That constrains reasonable uses of ## this routine unless written as part of a conditional replacement: - ## - ## .. code-block:: nim - ## if item > heap[0]: - ## item = replace(heap, item) + runnableExamples: + var heap = initHeapQueue[int]() + heap.push(5) + heap.push(12) + assert heap.replace(6) == 5 + assert heap.len == 2 + assert heap[0] == 6 + assert heap.replace(4) == 6 result = heap[0] heap.data[0] = item siftup(heap, 0) proc pushpop*[T](heap: var HeapQueue[T], item: T): T = ## Fast version of a push followed by a pop. + runnableExamples: + var heap = initHeapQueue[int]() + heap.push(5) + heap.push(12) + assert heap.pushpop(6) == 5 + assert heap.len == 2 + assert heap[0] == 6 + assert heap.pushpop(4) == 4 result = item if heap.len > 0 and heapCmp(heap.data[0], item): swap(result, heap.data[0]) From 1d14b2c9e61c5e9389f8b52839814156dcf259dd Mon Sep 17 00:00:00 2001 From: cooldome Date: Tue, 24 Nov 2020 14:49:14 +0000 Subject: [PATCH 1062/3768] fix #16110 (#16117) --- compiler/semstmts.nim | 3 ++- tests/macros/tgetimpl.nim | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index cd7428b62..713867b76 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1278,7 +1278,8 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = incl st.flags, tfRefsAnonObj let obj = newSym(skType, getIdent(c.cache, s.name.s & ":ObjectType"), nextId c.idgen, getCurrOwner(c), s.info) - obj.ast = a + obj.ast = a.copyTree + obj.ast[0] = newSymNode(obj) if sfPure in s.flags: obj.flags.incl sfPure obj.typ = st.lastSon diff --git a/tests/macros/tgetimpl.nim b/tests/macros/tgetimpl.nim index d231a4336..de655561b 100644 --- a/tests/macros/tgetimpl.nim +++ b/tests/macros/tgetimpl.nim @@ -65,3 +65,21 @@ macro check_gen_proc(ex: typed): (bool, bool) = let a = @[1,2,3] assert: check_gen_proc(len(a)) == (false, true) + +#--------------------------------------------------------------- +# issue #16110 + +macro check(x: type): untyped = + let z = getType(x) + let y = getImpl(z[1]) + echo z.treeRepr + expectKind(z[1], nnkSym) + expectKind(y[0], nnkSym) + doAssert(y[0] == z[1]) + +type + TirePtr = ptr object + code: int + +var z: TirePtr +check(typeof(z[])) \ No newline at end of file From d306a04466b7f1129620dc3ab35443119ed4c867 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 24 Nov 2020 16:09:00 +0100 Subject: [PATCH 1063/3768] fixes #16069; [backport:1.2] [backport:1.4] (#16115) * fixes #16069; refs https://github.com/nim-lang/RFCs/issues/257 [backport:1.2] [backport:1.4] * make tests green again --- compiler/ast.nim | 2 +- compiler/ccgexprs.nim | 1 + compiler/semfold.nim | 1 + compiler/transf.nim | 5 +++++ compiler/trees.nim | 2 +- tests/arc/topt_no_cursor.nim | 2 +- tests/misc/taddr.nim | 11 ----------- tests/vm/tconstobj.nim | 20 +++++++++++++++++++- 8 files changed, 29 insertions(+), 15 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index da3f3e7d7..2e710fd21 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1122,7 +1122,7 @@ proc safeLen*(n: PNode): int {.inline.} = proc safeArrLen*(n: PNode): int {.inline.} = ## works for array-like objects (strings passed as openArray in VM). - if n.kind in {nkStrLit..nkTripleStrLit}:result = n.strVal.len + if n.kind in {nkStrLit..nkTripleStrLit}: result = n.strVal.len elif n.kind in {nkNone..nkFloat128Lit}: result = 0 else: result = n.len diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index ec28eb1ec..ef3ceae62 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -881,6 +881,7 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) = [rdLoc(test), strLit, raiseInstr(p)]) proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) = + assert e[0].kind == nkDotExpr if optFieldCheck in p.options: var a: TLoc genRecordFieldAux(p, e[0], d, a) diff --git a/compiler/semfold.nim b/compiler/semfold.nim index b92895b19..958d8c14e 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -685,6 +685,7 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode of nkBracketExpr: result = foldArrayAccess(m, n, idgen, g) of nkDotExpr: result = foldFieldAccess(m, n, idgen, g) of nkCheckedFieldExpr: + assert n[0].kind == nkDotExpr result = foldFieldAccess(m, n[0], idgen, g) of nkStmtListExpr: var i = 0 diff --git a/compiler/transf.nim b/compiler/transf.nim index 42a78c8e3..ae233d322 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -1002,6 +1002,11 @@ proc transform(c: PTransf, n: PNode): PNode = return n of nkExceptBranch: result = transformExceptBranch(c, n) + of nkCheckedFieldExpr: + result = transformSons(c, n) + if result[0].kind != nkDotExpr: + # simplfied beyond a dot expression --> simplify further. + result = result[0] else: result = transformSons(c, n) when false: diff --git a/compiler/trees.nim b/compiler/trees.nim index 63459babe..05c060595 100644 --- a/compiler/trees.nim +++ b/compiler/trees.nim @@ -211,6 +211,6 @@ proc stupidStmtListExpr*(n: PNode): bool = proc dontInlineConstant*(orig, cnst: PNode): bool {.inline.} = # symbols that expand to a complex constant (array, etc.) should not be # inlined, unless it's the empty array: - result = orig.kind == nkSym and + result = orig.kind != cnst.kind and cnst.kind in {nkCurly, nkPar, nkTupleConstr, nkBracket, nkObjConstr} and cnst.len > ord(cnst.kind == nkObjConstr) diff --git a/tests/arc/topt_no_cursor.nim b/tests/arc/topt_no_cursor.nim index d5811e91e..3d37e6269 100644 --- a/tests/arc/topt_no_cursor.nim +++ b/tests/arc/topt_no_cursor.nim @@ -46,7 +46,7 @@ var lvalue lnext _ -`=copy`(lresult, [123]) +lresult = @[123] _ = ( let blitTmp = lresult blitTmp, ";") diff --git a/tests/misc/taddr.nim b/tests/misc/taddr.nim index 17fccfabe..49a577798 100644 --- a/tests/misc/taddr.nim +++ b/tests/misc/taddr.nim @@ -102,17 +102,6 @@ block: let a2 = (11,) doAssert byLent(a2) == (11,) - block: - when (defined(c) or defined(cpp)) and defined(release): - discard # probably not a bug since optimizer is free to pass by value, and `unsafeAddr` is used - else: - var a = @[12] - doAssert byPtr(a)[] == @[12] - let a2 = [13] - doAssert byPtr(a2)[] == [13] - let a3 = 14 - doAssert byPtr(a3)[] == 14 - block: proc byLent2[T](a: seq[T]): lent T = a[1] var a = @[20,21,22] diff --git a/tests/vm/tconstobj.nim b/tests/vm/tconstobj.nim index 93d0e1d7d..7dc20a0ba 100644 --- a/tests/vm/tconstobj.nim +++ b/tests/vm/tconstobj.nim @@ -3,6 +3,7 @@ discard """ (name: "hello") (-1, 0) (FirstName: "James", LastName: "Franco") +[1, 2, 3] ''' """ @@ -74,4 +75,21 @@ static: # issue #11861 static: # issue #15662 proc a(T: typedesc) = echo T.type - a((int, int)) \ No newline at end of file + a((int, int)) + +# bug #16069 +type + E = enum + val1, val2 + Obj = object + case k: E + of val1: + x: array[3, int] + of val2: + y: uint32 + +const + foo = [1, 2, 3] + arr = Obj(k: val1, x: foo) + +echo arr.x From cbc793b30b10b8e82af7313ac8cd438abf97f7d7 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Wed, 25 Nov 2020 02:06:41 +0800 Subject: [PATCH 1064/3768] move tests to testament (#16101) * move tests to testament * minor * fix random * disable test random --- lib/pure/algorithm.nim | 98 ---------- lib/pure/collections/critbits.nim | 63 ------ lib/pure/collections/tables.nim | 314 ------------------------------ lib/pure/cookies.nim | 15 -- lib/pure/fenv.nim | 8 - lib/pure/hashes.nim | 43 ---- lib/pure/httpcore.nim | 25 --- lib/pure/math.nim | 125 ------------ lib/pure/md5.nim | 7 - lib/pure/options.nim | 141 -------------- lib/pure/parsecsv.nim | 32 --- lib/pure/parseutils.nim | 43 ---- lib/pure/punycode.nim | 6 - lib/pure/random.nim | 54 ----- lib/pure/rationals.nim | 97 --------- lib/pure/stats.nim | 47 ----- lib/pure/streams.nim | 14 -- lib/pure/strformat.nim | 142 -------------- lib/pure/strmisc.nim | 21 -- lib/pure/strscans.nim | 112 ----------- lib/pure/strtabs.nim | 12 -- lib/pure/strutils.nim | 217 --------------------- lib/pure/unicode.nim | 216 -------------------- lib/pure/unidecode/unidecode.nim | 5 - lib/pure/uri.nim | 285 --------------------------- lib/std/enumerate.nim | 19 -- lib/std/monotimes.nim | 16 -- lib/std/sha1.nim | 13 -- tests/stdlib/talgorithm.nim | 97 +++++++++ tests/stdlib/tcookies.nim | 15 ++ tests/stdlib/tcritbits.nim | 61 ++++++ tests/stdlib/tenumerate.nim | 19 ++ tests/stdlib/tfenv.nim | 8 + tests/stdlib/thashes.nim | 87 ++++++--- tests/stdlib/thttpcore.nim | 42 ++-- tests/stdlib/tmath.nim | 126 ++++++++++++ tests/stdlib/tmd5.nim | 7 + tests/stdlib/tmonotimes.nim | 16 ++ tests/stdlib/toptions.nim | 144 +++++++++++++- tests/stdlib/tparsecsv.nim | 31 +++ tests/stdlib/tparseutils.nim | 43 ++++ tests/stdlib/tpunycode.nim | 5 + tests/stdlib/trandom.nim | 59 ++++++ tests/stdlib/trationals.nim | 98 ++++++++++ tests/stdlib/tsha1.nim | 13 ++ tests/stdlib/tstats.nim | 46 +++++ tests/stdlib/tstreams.nim | 14 ++ tests/stdlib/tstrformat.nim | 139 ++++++++++++- tests/stdlib/tstrmiscs.nim | 22 +++ tests/stdlib/tstrscans.nim | 112 +++++++++++ tests/stdlib/tstrtabs.nim | 12 ++ tests/stdlib/tstrutil.nim | 217 +++++++++++++++++++++ tests/stdlib/ttables.nim | 311 +++++++++++++++++++++++++++++ tests/stdlib/tunicode.nim | 215 ++++++++++++++++++++ tests/stdlib/tunidecode.nim | 3 +- tests/stdlib/turi.nim | 285 ++++++++++++++++++++++++++- 56 files changed, 2205 insertions(+), 2232 deletions(-) create mode 100644 tests/stdlib/tcookies.nim create mode 100644 tests/stdlib/tcritbits.nim create mode 100644 tests/stdlib/tenumerate.nim create mode 100644 tests/stdlib/tfenv.nim create mode 100644 tests/stdlib/tmd5.nim create mode 100644 tests/stdlib/tmonotimes.nim create mode 100644 tests/stdlib/tparsecsv.nim create mode 100644 tests/stdlib/tparseutils.nim create mode 100644 tests/stdlib/tpunycode.nim create mode 100644 tests/stdlib/trandom.nim create mode 100644 tests/stdlib/trationals.nim create mode 100644 tests/stdlib/tsha1.nim create mode 100644 tests/stdlib/tstats.nim create mode 100644 tests/stdlib/tstrmiscs.nim create mode 100644 tests/stdlib/ttables.nim create mode 100644 tests/stdlib/tunicode.nim diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim index ff835b57a..2430e7330 100644 --- a/lib/pure/algorithm.nim +++ b/lib/pure/algorithm.nim @@ -665,36 +665,6 @@ proc prevPermutation*[T](x: var openArray[T]): bool {.discardable.} = result = true -when isMainModule: - # Tests for lowerBound - var arr = @[1, 2, 3, 5, 6, 7, 8, 9] - assert arr.lowerBound(0) == 0 - assert arr.lowerBound(4) == 3 - assert arr.lowerBound(5) == 3 - assert arr.lowerBound(10) == 8 - arr = @[1, 5, 10] - assert arr.lowerBound(4) == 1 - assert arr.lowerBound(5) == 1 - assert arr.lowerBound(6) == 2 - # Tests for isSorted - var srt1 = [1, 2, 3, 4, 4, 4, 4, 5] - var srt2 = ["iello", "hello"] - var srt3 = [1.0, 1.0, 1.0] - var srt4: seq[int] - assert srt1.isSorted(cmp) == true - assert srt2.isSorted(cmp) == false - assert srt3.isSorted(cmp) == true - assert srt4.isSorted(cmp) == true - var srtseq = newSeq[int]() - assert srtseq.isSorted(cmp) == true - # Tests for reversed - var arr1 = @[0, 1, 2, 3, 4] - assert arr1.reversed() == @[4, 3, 2, 1, 0] - for i in 0 .. high(arr1): - assert arr1.reversed(0, i) == arr1.reversed()[high(arr1) - i .. high(arr1)] - assert arr1.reversed(i, high(arr1)) == arr1.reversed()[0 .. high(arr1) - i] - - proc rotateInternal[T](arg: var openArray[T]; first, middle, last: int): int = ## A port of std::rotate from c++. Ported from `this reference `_. result = first + last - middle @@ -852,71 +822,3 @@ proc rotatedLeft*[T](arg: openArray[T]; dist: int): seq[T] = let arglen = arg.len let distLeft = ((dist mod arglen) + arglen) mod arglen arg.rotatedInternal(0, distLeft, arg.len) - -when isMainModule: - var list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - let list2 = list.rotatedLeft(1 ..< 9, 3) - let expected = [0, 4, 5, 6, 7, 8, 1, 2, 3, 9, 10] - - doAssert list.rotateLeft(1 ..< 9, 3) == 6 - doAssert list == expected - doAssert list2 == @expected - - var s0, s1, s2, s3, s4, s5 = "xxxabcdefgxxx" - - doAssert s0.rotateLeft(3 ..< 10, 3) == 7 - doAssert s0 == "xxxdefgabcxxx" - doAssert s1.rotateLeft(3 ..< 10, 2) == 8 - doAssert s1 == "xxxcdefgabxxx" - doAssert s2.rotateLeft(3 ..< 10, 4) == 6 - doAssert s2 == "xxxefgabcdxxx" - doAssert s3.rotateLeft(3 ..< 10, -3) == 6 - doAssert s3 == "xxxefgabcdxxx" - doAssert s4.rotateLeft(3 ..< 10, -10) == 6 - doAssert s4 == "xxxefgabcdxxx" - doAssert s5.rotateLeft(3 ..< 10, 11) == 6 - doAssert s5 == "xxxefgabcdxxx" - - block product: - doAssert product(newSeq[seq[int]]()) == newSeq[seq[int]](), "empty input" - doAssert product(@[newSeq[int](), @[], @[]]) == newSeq[seq[int]](), "bit more empty input" - doAssert product(@[@[1, 2]]) == @[@[1, 2]], "a simple case of one element" - doAssert product(@[@[1, 2], @[3, 4]]) == @[@[2, 4], @[1, 4], @[2, 3], @[1, - 3]], "two elements" - doAssert product(@[@[1, 2], @[3, 4], @[5, 6]]) == @[@[2, 4, 6], @[1, 4, 6], - @[2, 3, 6], @[1, 3, 6], @[2, 4, 5], @[1, 4, 5], @[2, 3, 5], @[1, 3, 5]], "three elements" - doAssert product(@[@[1, 2], @[]]) == newSeq[seq[int]](), "two elements, but one empty" - - block lowerBound: - doAssert lowerBound([1, 2, 4], 3, system.cmp[int]) == 2 - doAssert lowerBound([1, 2, 2, 3], 4, system.cmp[int]) == 4 - doAssert lowerBound([1, 2, 3, 10], 11) == 4 - - block upperBound: - doAssert upperBound([1, 2, 4], 3, system.cmp[int]) == 2 - doAssert upperBound([1, 2, 2, 3], 3, system.cmp[int]) == 4 - doAssert upperBound([1, 2, 3, 5], 3) == 3 - - block fillEmptySeq: - var s = newSeq[int]() - s.fill(0) - - block testBinarySearch: - var noData: seq[int] - doAssert binarySearch(noData, 7) == -1 - let oneData = @[1] - doAssert binarySearch(oneData, 1) == 0 - doAssert binarySearch(onedata, 7) == -1 - let someData = @[1, 3, 4, 7] - doAssert binarySearch(someData, 1) == 0 - doAssert binarySearch(somedata, 7) == 3 - doAssert binarySearch(someData, -1) == -1 - doAssert binarySearch(someData, 5) == -1 - doAssert binarySearch(someData, 13) == -1 - let moreData = @[1, 3, 5, 7, 4711] - doAssert binarySearch(moreData, -1) == -1 - doAssert binarySearch(moreData, 1) == 0 - doAssert binarySearch(moreData, 5) == 2 - doAssert binarySearch(moreData, 6) == -1 - doAssert binarySearch(moreData, 4711) == 4 - doAssert binarySearch(moreData, 4712) == -1 diff --git a/lib/pure/collections/critbits.nim b/lib/pure/collections/critbits.nim index 4611c2540..695f44646 100644 --- a/lib/pure/collections/critbits.nim +++ b/lib/pure/collections/critbits.nim @@ -567,66 +567,3 @@ runnableExamples: doAssert critbitAsDict["key"] == -int.high critbitAsDict["key"] = int.high doAssert critbitAsDict["key"] == int.high - - -when isMainModule: - import sequtils - - var r: CritBitTree[void] - r.incl "abc" - r.incl "xyz" - r.incl "def" - r.incl "definition" - r.incl "prefix" - r.incl "foo" - - doAssert r.contains"def" - - r.excl "def" - assert r.missingOrExcl("foo") == false - assert "foo" notin toSeq(r.items) - - assert r.missingOrExcl("foo") == true - - assert toSeq(r.items) == @["abc", "definition", "prefix", "xyz"] - - assert toSeq(r.itemsWithPrefix("de")) == @["definition"] - var c = CritBitTree[int]() - - c.inc("a") - assert c["a"] == 1 - - c.inc("a", 4) - assert c["a"] == 5 - - c.inc("a", -5) - assert c["a"] == 0 - - c.inc("b", 2) - assert c["b"] == 2 - - c.inc("c", 3) - assert c["c"] == 3 - - c.inc("a", 1) - assert c["a"] == 1 - - var cf = CritBitTree[float]() - - cf.incl("a", 1.0) - assert cf["a"] == 1.0 - - cf.incl("b", 2.0) - assert cf["b"] == 2.0 - - cf.incl("c", 3.0) - assert cf["c"] == 3.0 - - assert cf.len == 3 - cf.excl("c") - assert cf.len == 2 - - var cb: CritBitTree[string] - cb.incl("help", "help") - for k in cb.keysWithPrefix("helpp"): - doAssert false, "there is no prefix helpp" diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index e94e2a00b..a0efcdce2 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -2873,317 +2873,3 @@ iterator mvalues*[A](t: CountTableRef[A]): var int = if t.data[h].val != 0: yield t.data[h].val assert(len(t) == L, "the length of the table changed while iterating over it") - - - - -when isMainModule: - type - Person = object - firstName, lastName: string - - proc hash(x: Person): Hash = - ## Piggyback on the already available string hash proc. - ## - ## Without this proc nothing works! - result = x.firstName.hash !& x.lastName.hash - result = !$result - - var - salaries = initTable[Person, int]() - p1, p2: Person - p1.firstName = "Jon" - p1.lastName = "Ross" - salaries[p1] = 30_000 - p2.firstName = "소진" - p2.lastName = "박" - salaries[p2] = 45_000 - var - s2 = initOrderedTable[Person, int]() - s3 = initCountTable[Person]() - s2[p1] = 30_000 - s2[p2] = 45_000 - s3[p1] = 30_000 - s3[p2] = 45_000 - - block: # Ordered table should preserve order after deletion - var - s4 = initOrderedTable[int, int]() - s4[1] = 1 - s4[2] = 2 - s4[3] = 3 - - var prev = 0 - for i in s4.values: - doAssert(prev < i) - prev = i - - s4.del(2) - doAssert(2 notin s4) - doAssert(s4.len == 2) - prev = 0 - for i in s4.values: - doAssert(prev < i) - prev = i - - block: # Deletion from OrderedTable should account for collision groups. See issue #5057. - # The bug is reproducible only with exact keys - const key1 = "boy_jackpot.inGamma" - const key2 = "boy_jackpot.outBlack" - - var t = { - key1: 0, - key2: 0 - }.toOrderedTable() - - t.del(key1) - assert(t.len == 1) - assert(key2 in t) - - var - t1 = initCountTable[string]() - t2 = initCountTable[string]() - t1.inc("foo") - t1.inc("bar", 2) - t1.inc("baz", 3) - t2.inc("foo", 4) - t2.inc("bar") - t2.inc("baz", 11) - merge(t1, t2) - assert(t1["foo"] == 5) - assert(t1["bar"] == 3) - assert(t1["baz"] == 14) - - let - t1r = newCountTable[string]() - t2r = newCountTable[string]() - t1r.inc("foo") - t1r.inc("bar", 2) - t1r.inc("baz", 3) - t2r.inc("foo", 4) - t2r.inc("bar") - t2r.inc("baz", 11) - merge(t1r, t2r) - assert(t1r["foo"] == 5) - assert(t1r["bar"] == 3) - assert(t1r["baz"] == 14) - - var - t1l = initCountTable[string]() - t2l = initCountTable[string]() - t1l.inc("foo") - t1l.inc("bar", 2) - t1l.inc("baz", 3) - t2l.inc("foo", 4) - t2l.inc("bar") - t2l.inc("baz", 11) - - block: - const testKey = "TESTKEY" - let t: CountTableRef[string] = newCountTable[string]() - - # Before, does not compile with error message: - #test_counttable.nim(7, 43) template/generic instantiation from here - #lib/pure/collections/tables.nim(117, 21) template/generic instantiation from here - #lib/pure/collections/tableimpl.nim(32, 27) Error: undeclared field: 'hcode - doAssert 0 == t[testKey] - t.inc(testKey, 3) - doAssert 3 == t[testKey] - - block: - # Clear tests - var clearTable = newTable[int, string]() - clearTable[42] = "asd" - clearTable[123123] = "piuyqwb " - doAssert clearTable[42] == "asd" - clearTable.clear() - doAssert(not clearTable.hasKey(123123)) - doAssert clearTable.getOrDefault(42) == "" - - block: #5482 - var a = [("wrong?", "foo"), ("wrong?", "foo2")].newOrderedTable() - var b = newOrderedTable[string, string](initialSize = 2) - b["wrong?"] = "foo" - b["wrong?"] = "foo2" - assert a == b - - block: #5482 - var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable() - var b = newOrderedTable[string, string](initialSize = 2) - b["wrong?"] = "foo" - b["wrong?"] = "foo2" - assert a == b - - block: #5487 - var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable() - var b = newOrderedTable[string, string]() # notice, default size! - b["wrong?"] = "foo" - b["wrong?"] = "foo2" - assert a == b - - block: #5487 - var a = [("wrong?", "foo"), ("wrong?", "foo2")].newOrderedTable() - var b = newOrderedTable[string, string]() # notice, default size! - b["wrong?"] = "foo" - b["wrong?"] = "foo2" - assert a == b - - block: - var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable() - var b = [("wrong?", "foo"), ("wrong?", "foo2")].newOrderedTable() - var c = newOrderedTable[string, string]() # notice, default size! - c["wrong?"] = "foo" - c["wrong?"] = "foo2" - assert a == b - assert a == c - - block: #6250 - let - a = {3: 1}.toOrderedTable - b = {3: 2}.toOrderedTable - assert((a == b) == false) - assert((b == a) == false) - - block: #6250 - let - a = {3: 2}.toOrderedTable - b = {3: 2}.toOrderedTable - assert((a == b) == true) - assert((b == a) == true) - - block: # CountTable.smallest - let t = toCountTable([0, 0, 5, 5, 5]) - doAssert t.smallest == (0, 2) - - block: #10065 - let t = toCountTable("abracadabra") - doAssert t['z'] == 0 - - var t_mut = toCountTable("abracadabra") - doAssert t_mut['z'] == 0 - # the previous read may not have modified the table. - doAssert t_mut.hasKey('z') == false - t_mut['z'] = 1 - doAssert t_mut['z'] == 1 - doAssert t_mut.hasKey('z') == true - - block: #12813 #13079 - var t = toCountTable("abracadabra") - doAssert len(t) == 5 - - t['a'] = 0 # remove a key - doAssert len(t) == 4 - - block: - var tp: Table[string, string] = initTable[string, string]() - doAssert "test1" == tp.getOrDefault("test1", "test1") - tp["test2"] = "test2" - doAssert "test2" == tp.getOrDefault("test2", "test1") - var tr: TableRef[string, string] = newTable[string, string]() - doAssert "test1" == tr.getOrDefault("test1", "test1") - tr["test2"] = "test2" - doAssert "test2" == tr.getOrDefault("test2", "test1") - var op: OrderedTable[string, string] = initOrderedTable[string, string]() - doAssert "test1" == op.getOrDefault("test1", "test1") - op["test2"] = "test2" - doAssert "test2" == op.getOrDefault("test2", "test1") - var orf: OrderedTableRef[string, string] = newOrderedTable[string, string]() - doAssert "test1" == orf.getOrDefault("test1", "test1") - orf["test2"] = "test2" - doAssert "test2" == orf.getOrDefault("test2", "test1") - - block tableWithoutInit: - var - a: Table[string, int] - b: Table[string, int] - c: Table[string, int] - d: Table[string, int] - e: Table[string, int] - - a["a"] = 7 - doAssert a.hasKey("a") - doAssert a.len == 1 - doAssert a["a"] == 7 - a["a"] = 9 - doAssert a.len == 1 - doAssert a["a"] == 9 - - doAssert b.hasKeyOrPut("b", 5) == false - doAssert b.hasKey("b") - doAssert b.hasKeyOrPut("b", 8) - doAssert b["b"] == 5 - - doAssert c.getOrDefault("a") == 0 - doAssert c.getOrDefault("a", 3) == 3 - c["a"] = 6 - doAssert c.getOrDefault("a", 3) == 6 - - doAssert d.mgetOrPut("a", 3) == 3 - doAssert d.mgetOrPut("a", 6) == 3 - - var x = 99 - doAssert e.pop("a", x) == false - doAssert x == 99 - e["a"] = 77 - doAssert e.pop("a", x) - doAssert x == 77 - - block orderedTableWithoutInit: - var - a: OrderedTable[string, int] - b: OrderedTable[string, int] - c: OrderedTable[string, int] - d: OrderedTable[string, int] - - a["a"] = 7 - doAssert a.hasKey("a") - doAssert a.len == 1 - doAssert a["a"] == 7 - a["a"] = 9 - doAssert a.len == 1 - doAssert a["a"] == 9 - - doAssert b.hasKeyOrPut("b", 5) == false - doAssert b.hasKey("b") - doAssert b.hasKeyOrPut("b", 8) - doAssert b["b"] == 5 - - doAssert c.getOrDefault("a") == 0 - doAssert c.getOrDefault("a", 3) == 3 - c["a"] = 6 - doAssert c.getOrDefault("a", 3) == 6 - - doAssert d.mgetOrPut("a", 3) == 3 - doAssert d.mgetOrPut("a", 6) == 3 - - block countTableWithoutInit: - var - a: CountTable[string] - b: CountTable[string] - c: CountTable[string] - d: CountTable[string] - e: CountTable[string] - - a["a"] = 7 - doAssert a.hasKey("a") - doAssert a.len == 1 - doAssert a["a"] == 7 - a["a"] = 9 - doAssert a.len == 1 - doAssert a["a"] == 9 - - doAssert b["b"] == 0 - b.inc("b") - doAssert b["b"] == 1 - - doAssert c.getOrDefault("a") == 0 - doAssert c.getOrDefault("a", 3) == 3 - c["a"] = 6 - doAssert c.getOrDefault("a", 3) == 6 - - e["f"] = 3 - merge(d, e) - doAssert d.hasKey("f") - d.inc("f") - merge(d, e) - doAssert d["f"] == 7 diff --git a/lib/pure/cookies.nim b/lib/pure/cookies.nim index eaff86ae6..7e686d44f 100644 --- a/lib/pure/cookies.nim +++ b/lib/pure/cookies.nim @@ -59,18 +59,3 @@ proc setCookie*(key, value: string, expires: DateTime|Time, return setCookie(key, value, domain, path, format(expires.utc, "ddd',' dd MMM yyyy HH:mm:ss 'GMT'"), noname, secure, httpOnly) - -when isMainModule: - let expire = fromUnix(0) + 1.seconds - - let cookies = [ - setCookie("test", "value", expire), - setCookie("test", "value", expire.local), - setCookie("test", "value", expire.utc) - ] - let expected = "Set-Cookie: test=value; Expires=Thu, 01 Jan 1970 00:00:01 GMT" - doAssert cookies == [expected, expected, expected] - - let table = parseCookies("uid=1; kp=2") - doAssert table["uid"] == "1" - doAssert table["kp"] == "2" diff --git a/lib/pure/fenv.nim b/lib/pure/fenv.nim index 53b3e3b50..abddaf4cb 100644 --- a/lib/pure/fenv.nim +++ b/lib/pure/fenv.nim @@ -178,11 +178,3 @@ template maximumPositiveValue*(T: typedesc[float64]): float64 = DBL_MAX template epsilon*(T: typedesc[float64]): float64 = DBL_EPSILON ## The difference between 1.0 and the smallest number greater than ## 1.0 that can be represented in a 64-bit floating-point type. - - -when isMainModule: - func is_significant(x: float): bool = - if x > minimumPositiveValue(float) and x < maximumPositiveValue(float): true - else: false - - doAssert is_significant(10.0) diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim index 3191d4030..667f27e95 100644 --- a/lib/pure/hashes.nim +++ b/lib/pure/hashes.nim @@ -485,46 +485,3 @@ proc hash*[A](x: set[A]): Hash = for it in items(x): result = result !& hash(it) result = !$result - - -when isMainModule: - block empty: - var - a = "" - b = newSeq[char]() - c = newSeq[int]() - d = cstring"" - e = "abcd" - doAssert hash(a) == 0 - doAssert hash(b) == 0 - doAssert hash(c) == 0 - doAssert hash(d) == 0 - doAssert hashIgnoreCase(a) == 0 - doAssert hashIgnoreStyle(a) == 0 - doAssert hash(e, 3, 2) == 0 - block sameButDifferent: - doAssert hash("aa bb aaaa1234") == hash("aa bb aaaa1234", 0, 13) - doAssert hash("aa bb aaaa1234") == hash(cstring"aa bb aaaa1234") - doAssert hashIgnoreCase("aA bb aAAa1234") == hashIgnoreCase("aa bb aaaa1234") - doAssert hashIgnoreStyle("aa_bb_AAaa1234") == hashIgnoreCase("aaBBAAAa1234") - block smallSize: # no multibyte hashing - let - xx = @['H', 'i'] - ii = @[72'u8, 105] - ss = "Hi" - doAssert hash(xx) == hash(ii) - doAssert hash(xx) == hash(ss) - doAssert hash(xx) == hash(xx, 0, xx.high) - doAssert hash(ss) == hash(ss, 0, ss.high) - block largeSize: # longer than 4 characters - let - xx = @['H', 'e', 'l', 'l', 'o'] - xxl = @['H', 'e', 'l', 'l', 'o', 'w', 'e', 'e', 'n', 's'] - ssl = "Helloweens" - doAssert hash(xxl) == hash(ssl) - doAssert hash(xxl) == hash(xxl, 0, xxl.high) - doAssert hash(ssl) == hash(ssl, 0, ssl.high) - doAssert hash(xx) == hash(xxl, 0, 4) - doAssert hash(xx) == hash(ssl, 0, 4) - doAssert hash(xx, 0, 3) == hash(xxl, 0, 3) - doAssert hash(xx, 0, 3) == hash(ssl, 0, 3) diff --git a/lib/pure/httpcore.nim b/lib/pure/httpcore.nim index 401e78b9b..7ead64d83 100644 --- a/lib/pure/httpcore.nim +++ b/lib/pure/httpcore.nim @@ -345,28 +345,3 @@ func is5xx*(code: HttpCode): bool {.inline.} = func `$`*(httpMethod: HttpMethod): string = return (system.`$`(httpMethod))[4 .. ^1].toUpperAscii() - -when isMainModule: - var test = newHttpHeaders() - test["Connection"] = @["Upgrade", "Close"] - doAssert test["Connection", 0] == "Upgrade" - doAssert test["Connection", 1] == "Close" - test.add("Connection", "Test") - doAssert test["Connection", 2] == "Test" - doAssert "upgrade" in test["Connection"] - - # Bug #5344. - doAssert parseHeader("foobar: ") == ("foobar", @[""]) - let (key, value) = parseHeader("foobar: ") - test = newHttpHeaders() - test[key] = value - doAssert test["foobar"] == "" - - doAssert parseHeader("foobar:") == ("foobar", @[""]) - - block: # test title case - var testTitleCase = newHttpHeaders(titleCase=true) - testTitleCase.add("content-length", "1") - doAssert testTitleCase.hasKey("Content-Length") - for key, val in testTitleCase: - doAssert key == "Content-Length" diff --git a/lib/pure/math.nim b/lib/pure/math.nim index 59368fbd2..af31a5785 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -1101,128 +1101,3 @@ proc lcm*[T](x: openArray[T]): T {.since: (1, 1).} = while i < x.len: result = lcm(result, x[i]) inc(i) - -when isMainModule and not defined(js) and not windowsCC89: - # Check for no side effect annotation - proc mySqrt(num: float): float {.noSideEffect.} = - return sqrt(num) - - # check gamma function - assert(gamma(5.0) == 24.0) # 4! - assert(lgamma(1.0) == 0.0) # ln(1.0) == 0.0 - assert(erf(6.0) > erf(5.0)) - assert(erfc(6.0) < erfc(5.0)) - -when isMainModule: - # Function for approximate comparison of floats - proc `==~`(x, y: float): bool = (abs(x-y) < 1e-9) - - block: # prod - doAssert prod([1, 2, 3, 4]) == 24 - doAssert prod([1.5, 3.4]) == 5.1 - let x: seq[float] = @[] - doAssert prod(x) == 1.0 - - block: # round() tests - # Round to 0 decimal places - doAssert round(54.652) ==~ 55.0 - doAssert round(54.352) ==~ 54.0 - doAssert round(-54.652) ==~ -55.0 - doAssert round(-54.352) ==~ -54.0 - doAssert round(0.0) ==~ 0.0 - - block: # splitDecimal() tests - doAssert splitDecimal(54.674).intpart ==~ 54.0 - doAssert splitDecimal(54.674).floatpart ==~ 0.674 - doAssert splitDecimal(-693.4356).intpart ==~ -693.0 - doAssert splitDecimal(-693.4356).floatpart ==~ -0.4356 - doAssert splitDecimal(0.0).intpart ==~ 0.0 - doAssert splitDecimal(0.0).floatpart ==~ 0.0 - - block: # trunc tests for vcc - doAssert(trunc(-1.1) == -1) - doAssert(trunc(1.1) == 1) - doAssert(trunc(-0.1) == -0) - doAssert(trunc(0.1) == 0) - - #special case - doAssert(classify(trunc(1e1000000)) == fcInf) - doAssert(classify(trunc(-1e1000000)) == fcNegInf) - doAssert(classify(trunc(0.0/0.0)) == fcNan) - doAssert(classify(trunc(0.0)) == fcZero) - - #trick the compiler to produce signed zero - let - f_neg_one = -1.0 - f_zero = 0.0 - f_nan = f_zero / f_zero - - doAssert(classify(trunc(f_neg_one*f_zero)) == fcNegZero) - - doAssert(trunc(-1.1'f32) == -1) - doAssert(trunc(1.1'f32) == 1) - doAssert(trunc(-0.1'f32) == -0) - doAssert(trunc(0.1'f32) == 0) - doAssert(classify(trunc(1e1000000'f32)) == fcInf) - doAssert(classify(trunc(-1e1000000'f32)) == fcNegInf) - doAssert(classify(trunc(f_nan.float32)) == fcNan) - doAssert(classify(trunc(0.0'f32)) == fcZero) - - block: # sgn() tests - assert sgn(1'i8) == 1 - assert sgn(1'i16) == 1 - assert sgn(1'i32) == 1 - assert sgn(1'i64) == 1 - assert sgn(1'u8) == 1 - assert sgn(1'u16) == 1 - assert sgn(1'u32) == 1 - assert sgn(1'u64) == 1 - assert sgn(-12342.8844'f32) == -1 - assert sgn(123.9834'f64) == 1 - assert sgn(0'i32) == 0 - assert sgn(0'f32) == 0 - assert sgn(NegInf) == -1 - assert sgn(Inf) == 1 - assert sgn(NaN) == 0 - - block: # fac() tests - try: - discard fac(-1) - except AssertionDefect: - discard - - doAssert fac(0) == 1 - doAssert fac(1) == 1 - doAssert fac(2) == 2 - doAssert fac(3) == 6 - doAssert fac(4) == 24 - - block: # floorMod/floorDiv - doAssert floorDiv(8, 3) == 2 - doAssert floorMod(8, 3) == 2 - - doAssert floorDiv(8, -3) == -3 - doAssert floorMod(8, -3) == -1 - - doAssert floorDiv(-8, 3) == -3 - doAssert floorMod(-8, 3) == 1 - - doAssert floorDiv(-8, -3) == 2 - doAssert floorMod(-8, -3) == -2 - - doAssert floorMod(8.0, -3.0) ==~ -1.0 - doAssert floorMod(-8.5, 3.0) ==~ 0.5 - - block: # log - doAssert log(4.0, 3.0) ==~ ln(4.0) / ln(3.0) - doAssert log2(8.0'f64) == 3.0'f64 - doAssert log2(4.0'f64) == 2.0'f64 - doAssert log2(2.0'f64) == 1.0'f64 - doAssert log2(1.0'f64) == 0.0'f64 - doAssert classify(log2(0.0'f64)) == fcNegInf - - doAssert log2(8.0'f32) == 3.0'f32 - doAssert log2(4.0'f32) == 2.0'f32 - doAssert log2(2.0'f32) == 1.0'f32 - doAssert log2(1.0'f32) == 0.0'f32 - doAssert classify(log2(0.0'f32)) == fcNegInf diff --git a/lib/pure/md5.nim b/lib/pure/md5.nim index 1ea71afd2..ba944ba81 100644 --- a/lib/pure/md5.nim +++ b/lib/pure/md5.nim @@ -278,12 +278,5 @@ proc md5Final*(c: var MD5Context, digest: var MD5Digest) = zeroMem(addr(c), sizeof(MD5Context)) -when isMainModule: - assert(getMD5("Franz jagt im komplett verwahrlosten Taxi quer durch Bayern") == - "a3cca2b2aa1e3b5b3b5aad99a8529074") - assert(getMD5("Frank jagt im komplett verwahrlosten Taxi quer durch Bayern") == - "7e716d0e702df0505fc72e2b89467910") - assert($toMD5("") == "d41d8cd98f00b204e9800998ecf8427e") - when defined(nimHasStyleChecks): {.pop.} #{.push styleChecks: off.} diff --git a/lib/pure/options.nim b/lib/pure/options.nim index 076cf3707..8de4430c1 100644 --- a/lib/pure/options.nim +++ b/lib/pure/options.nim @@ -371,144 +371,3 @@ proc unsafeGet*[T](self: Option[T]): lent T {.inline.}= ## Generally, using `get proc <#get,Option[T]>`_ is preferred. assert self.isSome result = self.val - -when isMainModule: - import unittest, sequtils - - # RefPerson is used to test that overloaded `==` operator is not called by - # options. It is defined here in the global scope, because otherwise the test - # will not even consider the `==` operator. Different bug? - type RefPerson = ref object - name: string - - proc `==`(a, b: RefPerson): bool = - assert(not a.isNil and not b.isNil) - a.name == b.name - - suite "options": - # work around a bug in unittest - let intNone = none(int) - let stringNone = none(string) - - test "example": - proc find(haystack: string, needle: char): Option[int] = - for i, c in haystack: - if c == needle: - return some i - - check("abc".find('c').get() == 2) - - let result = "team".find('i') - - check result == intNone - check result.isNone - - test "some": - check some(6).get() == 6 - check some("a").unsafeGet() == "a" - check some(6).isSome - check some("a").isSome - - test "none": - expect UnpackDefect: - discard none(int).get() - check(none(int).isNone) - check(not none(string).isSome) - - test "equality": - check some("a") == some("a") - check some(7) != some(6) - check some("a") != stringNone - check intNone == intNone - - when compiles(some("a") == some(5)): - check false - when compiles(none(string) == none(int)): - check false - - test "get with a default value": - check(some("Correct").get("Wrong") == "Correct") - check(stringNone.get("Correct") == "Correct") - - test "$": - check($(some("Correct")) == "Some(\"Correct\")") - check($(stringNone) == "None[string]") - - test "map with a void result": - var procRan = 0 - some(123).map(proc (v: int) = procRan = v) - check procRan == 123 - intNone.map(proc (v: int) = check false) - - test "map": - check(some(123).map(proc (v: int): int = v * 2) == some(246)) - check(intNone.map(proc (v: int): int = v * 2).isNone) - - test "filter": - check(some(123).filter(proc (v: int): bool = v == 123) == some(123)) - check(some(456).filter(proc (v: int): bool = v == 123).isNone) - check(intNone.filter(proc (v: int): bool = check false).isNone) - - test "flatMap": - proc addOneIfNotZero(v: int): Option[int] = - if v != 0: - result = some(v + 1) - else: - result = none(int) - - check(some(1).flatMap(addOneIfNotZero) == some(2)) - check(some(0).flatMap(addOneIfNotZero) == none(int)) - check(some(1).flatMap(addOneIfNotZero).flatMap(addOneIfNotZero) == some(3)) - - proc maybeToString(v: int): Option[string] = - if v != 0: - result = some($v) - else: - result = none(string) - - check(some(1).flatMap(maybeToString) == some("1")) - - proc maybeExclaim(v: string): Option[string] = - if v != "": - result = some v & "!" - else: - result = none(string) - - check(some(1).flatMap(maybeToString).flatMap(maybeExclaim) == some("1!")) - check(some(0).flatMap(maybeToString).flatMap(maybeExclaim) == none(string)) - - test "SomePointer": - var intref: ref int - check(option(intref).isNone) - intref.new - check(option(intref).isSome) - - let tmp = option(intref) - check(sizeof(tmp) == sizeof(ptr int)) - - var prc = proc (x: int): int = x + 1 - check(option(prc).isSome) - prc = nil - check(option(prc).isNone) - - test "none[T]": - check(none[int]().isNone) - check(none(int) == none[int]()) - - test "$ on typed with .name": - type Named = object - name: string - - let nobody = none(Named) - check($nobody == "None[Named]") - - test "$ on type with name()": - type Person = object - myname: string - - let noperson = none(Person) - check($noperson == "None[Person]") - - test "Ref type with overloaded `==`": - let p = some(RefPerson.new()) - check p.isSome diff --git a/lib/pure/parsecsv.nim b/lib/pure/parsecsv.nim index 2c0dbb802..4a7117dc5 100644 --- a/lib/pure/parsecsv.nim +++ b/lib/pure/parsecsv.nim @@ -357,35 +357,3 @@ when not defined(testing) and isMainModule: for val in items(x.row): echo "##", val, "##" close(x) - -when isMainModule: - import os - import strutils - block: # Tests for reading the header row - let content = "\nOne,Two,Three,Four\n1,2,3,4\n10,20,30,40,\n100,200,300,400\n" - writeFile("temp.csv", content) - - var p: CsvParser - p.open("temp.csv") - p.readHeaderRow() - while p.readRow(): - let zeros = repeat('0', p.currRow-2) - doAssert p.rowEntry("One") == "1" & zeros - doAssert p.rowEntry("Two") == "2" & zeros - doAssert p.rowEntry("Three") == "3" & zeros - doAssert p.rowEntry("Four") == "4" & zeros - p.close() - - when not defined(testing): - var parser: CsvParser - parser.open("temp.csv") - parser.readHeaderRow() - while parser.readRow(): - echo "new row: " - for col in items(parser.headers): - echo "##", col, ":", parser.rowEntry(col), "##" - parser.close() - removeFile("temp.csv") - - # Tidy up - removeFile("temp.csv") diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim index 4b123117a..29159816d 100644 --- a/lib/pure/parseutils.nim +++ b/lib/pure/parseutils.nim @@ -651,47 +651,4 @@ iterator interpolatedFragments*(s: string): tuple[kind: InterpolatedKind, break i = j -when isMainModule: - import sequtils - let input = "$test{} $this is ${an{ example}} " - let expected = @[(ikVar, "test"), (ikStr, "{} "), (ikVar, "this"), - (ikStr, " is "), (ikExpr, "an{ example}"), (ikStr, " ")] - doAssert toSeq(interpolatedFragments(input)) == expected - - var value = 0 - discard parseHex("0x38", value) - doAssert value == 56 - - value = -1 - doAssert(parseSaturatedNatural("848", value) == 3) - doAssert value == 848 - - value = -1 - discard parseSaturatedNatural("84899999999999999999324234243143142342135435342532453", value) - doAssert value == high(int) - - value = -1 - discard parseSaturatedNatural("9223372036854775808", value) - doAssert value == high(int) - - value = -1 - discard parseSaturatedNatural("9223372036854775807", value) - doAssert value == high(int) - - value = -1 - discard parseSaturatedNatural("18446744073709551616", value) - doAssert value == high(int) - - value = -1 - discard parseSaturatedNatural("18446744073709551615", value) - doAssert value == high(int) - - value = -1 - doAssert(parseSaturatedNatural("1_000_000", value) == 9) - doAssert value == 1_000_000 - - var i64Value: int64 - discard parseBiggestInt("9223372036854775807", i64Value) - doAssert i64Value == 9223372036854775807 - {.pop.} diff --git a/lib/pure/punycode.nim b/lib/pure/punycode.nim index fc80fabfd..e9e11a1c3 100644 --- a/lib/pure/punycode.nim +++ b/lib/pure/punycode.nim @@ -206,9 +206,3 @@ runnableExamples: doAssert decode("Mnchen-3ya-") == "Mnchen-3ya" doAssert decode("Mnchen-Ost-9db") == "München-Ost" doAssert decode("Bahnhof Mnchen-Ost-u6b") == "Bahnhof München-Ost" - - -when isMainModule: - assert(decode(encode("", "bücher")) == "bücher") - assert(decode(encode("münchen")) == "münchen") - assert encode("xn--", "münchen") == "xn--mnchen-3ya" diff --git a/lib/pure/random.nim b/lib/pure/random.nim index 3d9948d08..683140cd8 100644 --- a/lib/pure/random.nim +++ b/lib/pure/random.nim @@ -651,57 +651,3 @@ when not defined(nimscript) and not defined(standalone): randomize(convert(Seconds, Nanoseconds, now.toUnix) + now.nanosecond) {.pop.} - -when isMainModule: - import stats - - proc main = - var occur: array[1000, int] - - var x = 8234 - for i in 0..100_000: - x = rand(high(occur)) - inc occur[x] - for i, oc in occur: - if oc < 69: - doAssert false, "too few occurrences of " & $i - elif oc > 150: - doAssert false, "too many occurrences of " & $i - - when false: - var rs: RunningStat - for j in 1..5: - for i in 1 .. 1_000: - rs.push(gauss()) - echo("mean: ", rs.mean, - " stdDev: ", rs.standardDeviation(), - " min: ", rs.min, - " max: ", rs.max) - rs.clear() - - var a = [0, 1] - shuffle(a) - doAssert a[0] == 1 - doAssert a[1] == 0 - - doAssert rand(0) == 0 - doAssert sample("a") == 'a' - - when compileOption("rangeChecks"): - try: - discard rand(-1) - doAssert false - except RangeDefect: - discard - - try: - discard rand(-1.0) - doAssert false - except RangeDefect: - discard - - - # don't use causes integer overflow - doAssert compiles(rand[int](low(int) .. high(int))) - - main() diff --git a/lib/pure/rationals.nim b/lib/pure/rationals.nim index c7af91edd..38a88ebc3 100644 --- a/lib/pure/rationals.nim +++ b/lib/pure/rationals.nim @@ -279,100 +279,3 @@ proc hash*[T](x: Rational[T]): Hash = h = h !& hash(copy.num) h = h !& hash(copy.den) result = !$h - -when isMainModule: - var - z = Rational[int](num: 0, den: 1) - o = initRational(num = 1, den = 1) - a = initRational(1, 2) - b = -1 // -2 - m1 = -1 // 1 - tt = 10 // 2 - - assert(a == a) - assert( (a-a) == z) - assert( (a+b) == o) - assert( (a/b) == o) - assert( (a*b) == 1 // 4) - assert( (3/a) == 6 // 1) - assert( (a/3) == 1 // 6) - assert(a*b == 1 // 4) - assert(tt*z == z) - assert(10*a == tt) - assert(a*10 == tt) - assert(tt/10 == a) - assert(a-m1 == 3 // 2) - assert(a+m1 == -1 // 2) - assert(m1+tt == 16 // 4) - assert(m1-tt == 6 // -1) - - assert(z < o) - assert(z <= o) - assert(z == z) - assert(cmp(z, o) < 0) - assert(cmp(o, z) > 0) - - assert(o == o) - assert(o >= o) - assert(not(o > o)) - assert(cmp(o, o) == 0) - assert(cmp(z, z) == 0) - assert(hash(o) == hash(o)) - - assert(a == b) - assert(a >= b) - assert(not(b > a)) - assert(cmp(a, b) == 0) - assert(hash(a) == hash(b)) - - var x = 1//3 - - x *= 5//1 - assert(x == 5//3) - x += 2 // 9 - assert(x == 17//9) - x -= 9//18 - assert(x == 25//18) - x /= 1//2 - assert(x == 50//18) - - var y = 1//3 - - y *= 4 - assert(y == 4//3) - y += 5 - assert(y == 19//3) - y -= 2 - assert(y == 13//3) - y /= 9 - assert(y == 13//27) - - assert toRational(5) == 5//1 - assert abs(toFloat(y) - 0.4814814814814815) < 1.0e-7 - assert toInt(z) == 0 - - when sizeof(int) == 8: - assert toRational(0.98765432) == 2111111029 // 2137499919 - assert toRational(PI) == 817696623 // 260280919 - when sizeof(int) == 4: - assert toRational(0.98765432) == 80 // 81 - assert toRational(PI) == 355 // 113 - - assert toRational(0.1) == 1 // 10 - assert toRational(0.9) == 9 // 10 - - assert toRational(0.0) == 0 // 1 - assert toRational(-0.25) == 1 // -4 - assert toRational(3.2) == 16 // 5 - assert toRational(0.33) == 33 // 100 - assert toRational(0.22) == 11 // 50 - assert toRational(10.0) == 10 // 1 - - assert (1//1) div (3//10) == 3 - assert (-1//1) div (3//10) == -3 - assert (3//10) mod (1//1) == 3//10 - assert (-3//10) mod (1//1) == -3//10 - assert floorDiv(1//1, 3//10) == 3 - assert floorDiv(-1//1, 3//10) == -4 - assert floorMod(3//10, 1//1) == 3//10 - assert floorMod(-3//10, 1//1) == 7//10 diff --git a/lib/pure/stats.nim b/lib/pure/stats.nim index 8684cb60a..20fd1fdcd 100644 --- a/lib/pure/stats.nim +++ b/lib/pure/stats.nim @@ -335,50 +335,3 @@ runnableExamples: doAssert statistics.skewnessS() === 1.018350154434631 doAssert statistics.kurtosis() === -1.0 doAssert statistics.kurtosisS() === -0.7000000000000008 - - -when isMainModule: - proc clean(x: float): float = - result = round(1.0e8*x).float * 1.0e-8 - - var rs: RunningStat - rs.push(@[1.0, 2.0, 1.0, 4.0, 1.0, 4.0, 1.0, 2.0]) - doAssert(rs.n == 8) - doAssert(clean(rs.mean) == 2.0) - doAssert(clean(rs.variance()) == 1.5) - doAssert(clean(rs.varianceS()) == 1.71428571) - doAssert(clean(rs.skewness()) == 0.81649658) - doAssert(clean(rs.skewnessS()) == 1.01835015) - doAssert(clean(rs.kurtosis()) == -1.0) - doAssert(clean(rs.kurtosisS()) == -0.7000000000000001) - - var rs1, rs2: RunningStat - rs1.push(@[1.0, 2.0, 1.0, 4.0]) - rs2.push(@[1.0, 4.0, 1.0, 2.0]) - let rs3 = rs1 + rs2 - doAssert(clean(rs3.mom2) == clean(rs.mom2)) - doAssert(clean(rs3.mom3) == clean(rs.mom3)) - doAssert(clean(rs3.mom4) == clean(rs.mom4)) - rs1 += rs2 - doAssert(clean(rs1.mom2) == clean(rs.mom2)) - doAssert(clean(rs1.mom3) == clean(rs.mom3)) - doAssert(clean(rs1.mom4) == clean(rs.mom4)) - rs1.clear() - rs1.push(@[1.0, 2.2, 1.4, 4.9]) - doAssert(rs1.sum == 9.5) - doAssert(rs1.mean() == 2.375) - - when not defined(cpu32): - # XXX For some reason on 32bit CPUs these results differ - var rr: RunningRegress - rr.push(@[0.0, 1.0, 2.8, 3.0, 4.0], @[0.0, 1.0, 2.3, 3.0, 4.0]) - doAssert(rr.slope() == 0.9695585996955861) - doAssert(rr.intercept() == -0.03424657534246611) - doAssert(rr.correlation() == 0.9905100362239381) - var rr1, rr2: RunningRegress - rr1.push(@[0.0, 1.0], @[0.0, 1.0]) - rr2.push(@[2.8, 3.0, 4.0], @[2.3, 3.0, 4.0]) - let rr3 = rr1 + rr2 - doAssert(rr3.correlation() == rr.correlation()) - doAssert(clean(rr3.slope()) == clean(rr.slope())) - doAssert(clean(rr3.intercept()) == clean(rr.intercept())) diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim index dc00271a5..de3f41f09 100644 --- a/lib/pure/streams.nim +++ b/lib/pure/streams.nim @@ -1520,17 +1520,3 @@ when false: var handle = open(filename, flags) if handle < 0: raise newEOS("posix.open() call failed") result = newFileHandleStream(handle) - -when isMainModule and defined(testing): - var ss = newStringStream("The quick brown fox jumped over the lazy dog.\nThe lazy dog ran") - assert(ss.getPosition == 0) - assert(ss.peekStr(5) == "The q") - assert(ss.getPosition == 0) # haven't moved - assert(ss.readStr(5) == "The q") - assert(ss.getPosition == 5) # did move - assert(ss.peekLine() == "uick brown fox jumped over the lazy dog.") - assert(ss.getPosition == 5) # haven't moved - var str = newString(100) - assert(ss.peekLine(str)) - assert(str == "uick brown fox jumped over the lazy dog.") - assert(ss.getPosition == 5) # haven't moved diff --git a/lib/pure/strformat.nim b/lib/pure/strformat.nim index c37b6b1c0..ddf996e48 100644 --- a/lib/pure/strformat.nim +++ b/lib/pure/strformat.nim @@ -660,145 +660,3 @@ macro fmt*(pattern: string; openChar, closeChar: char): untyped = doAssert """(()"foo" & "bar"())""".fmt(')', '(') == "(foobar)" doAssert """ ""{"123+123"}"" """.fmt('"', '"') == " \"{246}\" " strformatImpl(pattern, openChar.intVal.char, closeChar.intVal.char) - -when isMainModule: - template check(actual, expected: string) = - doAssert actual == expected - - from strutils import toUpperAscii, repeat - - # Basic tests - let s = "string" - check &"{0} {s}", "0 string" - check &"{s[0..2].toUpperAscii}", "STR" - check &"{-10:04}", "-010" - check &"{-10:<04}", "-010" - check &"{-10:>04}", "-010" - check &"0x{10:02X}", "0x0A" - - check &"{10:#04X}", "0x0A" - - check &"""{"test":#>5}""", "#test" - check &"""{"test":>5}""", " test" - - check &"""{"test":#^7}""", "#test##" - - check &"""{"test": <5}""", "test " - check &"""{"test":<5}""", "test " - check &"{1f:.3f}", "1.000" - check &"Hello, {s}!", "Hello, string!" - - # Tests for identifiers without parenthesis - check &"{s} works{s}", "string worksstring" - check &"{s:>7}", " string" - doAssert(not compiles(&"{s_works}")) # parsed as identifier `s_works` - - # Misc general tests - check &"{{}}", "{}" - check &"{0}%", "0%" - check &"{0}%asdf", "0%asdf" - check &("\n{\"\\n\"}\n"), "\n\n\n" - check &"""{"abc"}s""", "abcs" - - # String tests - check &"""{"abc"}""", "abc" - check &"""{"abc":>4}""", " abc" - check &"""{"abc":<4}""", "abc " - check &"""{"":>4}""", " " - check &"""{"":<4}""", " " - - # Int tests - check &"{12345}", "12345" - check &"{ - 12345}", "-12345" - check &"{12345:6}", " 12345" - check &"{12345:>6}", " 12345" - check &"{12345:4}", "12345" - check &"{12345:08}", "00012345" - check &"{-12345:08}", "-0012345" - check &"{0:0}", "0" - check &"{0:02}", "00" - check &"{-1:3}", " -1" - check &"{-1:03}", "-01" - check &"{10}", "10" - check &"{16:#X}", "0x10" - check &"{16:^#7X}", " 0x10 " - check &"{16:^+#7X}", " +0x10 " - - # Hex tests - check &"{0:x}", "0" - check &"{-0:x}", "0" - check &"{255:x}", "ff" - check &"{255:X}", "FF" - check &"{-255:x}", "-ff" - check &"{-255:X}", "-FF" - check &"{255:x} uNaffeCteD CaSe", "ff uNaffeCteD CaSe" - check &"{255:X} uNaffeCteD CaSe", "FF uNaffeCteD CaSe" - check &"{255:4x}", " ff" - check &"{255:04x}", "00ff" - check &"{-255:4x}", " -ff" - check &"{-255:04x}", "-0ff" - - # Float tests - check &"{123.456}", "123.456" - check &"{-123.456}", "-123.456" - check &"{123.456:.3f}", "123.456" - check &"{123.456:+.3f}", "+123.456" - check &"{-123.456:+.3f}", "-123.456" - check &"{-123.456:.3f}", "-123.456" - check &"{123.456:1g}", "123.456" - check &"{123.456:.1f}", "123.5" - check &"{123.456:.0f}", "123." - check &"{123.456:>9.3f}", " 123.456" - check &"{123.456:9.3f}", " 123.456" - check &"{123.456:>9.4f}", " 123.4560" - check &"{123.456:>9.0f}", " 123." - check &"{123.456:<9.4f}", "123.4560 " - - # Float (scientific) tests - check &"{123.456:e}", "1.234560e+02" - check &"{123.456:>13e}", " 1.234560e+02" - check &"{123.456:<13e}", "1.234560e+02 " - check &"{123.456:.1e}", "1.2e+02" - check &"{123.456:.2e}", "1.23e+02" - check &"{123.456:.3e}", "1.235e+02" - - # Note: times.format adheres to the format protocol. Test that this - # works: - import times - - var dt = initDateTime(01, mJan, 2000, 00, 00, 00) - check &"{dt:yyyy-MM-dd}", "2000-01-01" - - var tm = fromUnix(0) - discard &"{tm}" - - var noww = now() - check &"{noww}", $noww - - # Unicode string tests - check &"""{"αβγ"}""", "αβγ" - check &"""{"αβγ":>5}""", " αβγ" - check &"""{"αβγ":<5}""", "αβγ " - check &"""a{"a"}α{"α"}€{"€"}𐍈{"𐍈"}""", "aaαα€€𐍈𐍈" - check &"""a{"a":2}α{"α":2}€{"€":2}𐍈{"𐍈":2}""", "aa αα €€ 𐍈𐍈 " - # Invalid unicode sequences should be handled as plain strings. - # Invalid examples taken from: https://stackoverflow.com/a/3886015/1804173 - let invalidUtf8 = [ - "\xc3\x28", "\xa0\xa1", - "\xe2\x28\xa1", "\xe2\x82\x28", - "\xf0\x28\x8c\xbc", "\xf0\x90\x28\xbc", "\xf0\x28\x8c\x28" - ] - for s in invalidUtf8: - check &"{s:>5}", repeat(" ", 5-s.len) & s - - # bug #11089 - let flfoo: float = 1.0 - check &"{flfoo}", "1.0" - - # bug #11092 - check &"{high(int64)}", "9223372036854775807" - check &"{low(int64)}", "-9223372036854775808" - - doAssert fmt"{'a'} {'b'}" == "a b" - - echo("All tests ok") diff --git a/lib/pure/strmisc.nim b/lib/pure/strmisc.nim index 061c2063b..5060deb78 100644 --- a/lib/pure/strmisc.nim +++ b/lib/pure/strmisc.nim @@ -84,24 +84,3 @@ proc rpartition*(s: string, sep: string): (string, string, string) doAssert rpartition("foofoobar", "bar") == ("foofoo", "bar", "") return partition(s, sep, right = true) - -when isMainModule: - doAssert expandTabs("\t", 4) == " " - doAssert expandTabs("\tfoo\t", 4) == " foo " - doAssert expandTabs("\tfoo\tbar", 4) == " foo bar" - doAssert expandTabs("\tfoo\tbar\t", 4) == " foo bar " - doAssert expandTabs("", 4) == "" - doAssert expandTabs("", 0) == "" - doAssert expandTabs("\t\t\t", 0) == "" - - doAssert partition("foo:bar", ":") == ("foo", ":", "bar") - doAssert partition("foobarbar", "bar") == ("foo", "bar", "bar") - doAssert partition("foobarbar", "bank") == ("foobarbar", "", "") - doAssert partition("foobarbar", "foo") == ("", "foo", "barbar") - doAssert partition("foofoobar", "bar") == ("foofoo", "bar", "") - - doAssert rpartition("foo:bar", ":") == ("foo", ":", "bar") - doAssert rpartition("foobarbar", "bar") == ("foobar", "bar", "") - doAssert rpartition("foobarbar", "bank") == ("", "", "foobarbar") - doAssert rpartition("foobarbar", "foo") == ("", "foo", "barbar") - doAssert rpartition("foofoobar", "bar") == ("foofoo", "bar", "") diff --git a/lib/pure/strscans.nim b/lib/pure/strscans.nim index f078b0d20..f7032f428 100644 --- a/lib/pure/strscans.nim +++ b/lib/pure/strscans.nim @@ -633,115 +633,3 @@ macro scanp*(input, idx: typed; pattern: varargs[untyped]): bool = result.add res when defined(debugScanp): echo repr result - - -when isMainModule: - proc twoDigits(input: string; x: var int; start: int): int = - if start+1 < input.len and input[start] == '0' and input[start+1] == '0': - result = 2 - x = 13 - else: - result = 0 - - proc someSep(input: string; start: int; seps: set[char] = {';', ',', '-', '.'}): int = - result = 0 - while start+result < input.len and input[start+result] in seps: inc result - - proc demangle(s: string; res: var string; start: int): int = - while result+start < s.len and s[result+start] in {'_', '@'}: inc result - res = "" - while result+start < s.len and s[result+start] > ' ' and s[result+start] != '_': - res.add s[result+start] - inc result - while result+start < s.len and s[result+start] > ' ': - inc result - - proc parseGDB(resp: string): seq[string] = - const - digits = {'0'..'9'} - hexdigits = digits + {'a'..'f', 'A'..'F'} - whites = {' ', '\t', '\C', '\L'} - result = @[] - var idx = 0 - while true: - var prc = "" - var info = "" - if scanp(resp, idx, *`whites`, '#', *`digits`, +`whites`, ?("0x", *`hexdigits`, " in "), - demangle($input, prc, $index), *`whites`, '(', * ~ ')', ')', - *`whites`, "at ", +(~{'\C', '\L'} -> info.add($_))): - result.add prc & " " & info - else: - break - - var key, val: string - var intval: int - var floatval: float - doAssert scanf("abc:: xyz 89 33.25", "$w$s::$s$w$s$i $f", key, val, intval, floatVal) - doAssert key == "abc" - doAssert val == "xyz" - doAssert intval == 89 - doAssert floatVal == 33.25 - - var binval: int - var octval: int - var hexval: int - doAssert scanf("0b0101 0o1234 0xabcd", "$b$s$o$s$h", binval, octval, hexval) - doAssert binval == 0b0101 - doAssert octval == 0o1234 - doAssert hexval == 0xabcd - - let xx = scanf("$abc", "$$$i", intval) - doAssert xx == false - - - let xx2 = scanf("$1234", "$$$i", intval) - doAssert xx2 - - let yy = scanf(";.--Breakpoint00 [output]", - "$[someSep]Breakpoint${twoDigits}$[someSep({';','.','-'})] [$+]$.", - intVal, key) - doAssert yy - doAssert key == "output" - doAssert intVal == 13 - - var ident = "" - var idx = 0 - let zz = scanp("foobar x x x xWZ", idx, +{'a'..'z'} -> add(ident, $_), *(*{ - ' ', '\t'}, "x"), ~'U', "Z") - doAssert zz - doAssert ident == "foobar" - - const digits = {'0'..'9'} - var year = 0 - var idx2 = 0 - if scanp("201655-8-9", idx2, `digits`{4, 6} -> (year = year * 10 + ord($_) - - ord('0')), "-8", "-9"): - doAssert year == 201655 - - const gdbOut = """ - #0 @foo_96013_1208911747@8 (x0=...) - at c:/users/anwender/projects/nim/temp.nim:11 - #1 0x00417754 in tempInit000 () at c:/users/anwender/projects/nim/temp.nim:13 - #2 0x0041768d in NimMainInner () - at c:/users/anwender/projects/nim/lib/system.nim:2605 - #3 0x004176b1 in NimMain () - at c:/users/anwender/projects/nim/lib/system.nim:2613 - #4 0x004176db in main (argc=1, args=0x712cc8, env=0x711ca8) - at c:/users/anwender/projects/nim/lib/system.nim:2620""" - const result = @["foo c:/users/anwender/projects/nim/temp.nim:11", - "tempInit000 c:/users/anwender/projects/nim/temp.nim:13", - "NimMainInner c:/users/anwender/projects/nim/lib/system.nim:2605", - "NimMain c:/users/anwender/projects/nim/lib/system.nim:2613", - "main c:/users/anwender/projects/nim/lib/system.nim:2620"] - #doAssert parseGDB(gdbOut) == result - - # bug #6487 - var count = 0 - - proc test(): string = - inc count - result = ",123123" - - var a: int - discard scanf(test(), ",$i", a) - doAssert count == 1 diff --git a/lib/pure/strtabs.nim b/lib/pure/strtabs.nim index 7b726e134..7cf668d19 100644 --- a/lib/pure/strtabs.nim +++ b/lib/pure/strtabs.nim @@ -420,15 +420,3 @@ proc `%`*(f: string, t: StringTableRef, flags: set[FormatFlag] = {}): string {. else: add(result, f[i]) inc(i) - -when isMainModule: - var x = {"k": "v", "11": "22", "565": "67"}.newStringTable - assert x["k"] == "v" - assert x["11"] == "22" - assert x["565"] == "67" - x["11"] = "23" - assert x["11"] == "23" - - x.clear(modeCaseInsensitive) - x["11"] = "22" - assert x["11"] == "22" diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 0eff80863..f0b447de7 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -2920,220 +2920,3 @@ proc isEmptyOrWhitespace*(s: string): bool {.noSideEffect, rtl, extern: "nsuIsEmptyOrWhitespace".} = ## Checks if `s` is empty or consists entirely of whitespace characters. result = s.allCharsInSet(Whitespace) - - -when isMainModule: - proc nonStaticTests = - doAssert formatBiggestFloat(1234.567, ffDecimal, -1) == "1234.567000" - when not defined(js): - doAssert formatBiggestFloat(1234.567, ffDecimal, 0) == "1235." # bugs 8242, 12586 - doAssert formatBiggestFloat(1234.567, ffDecimal, 1) == "1234.6" - doAssert formatBiggestFloat(0.00000000001, ffDecimal, 11) == "0.00000000001" - doAssert formatBiggestFloat(0.00000000001, ffScientific, 1, ',') in - ["1,0e-11", "1,0e-011"] - # bug #6589 - when not defined(js): - doAssert formatFloat(123.456, ffScientific, precision = -1) == "1.234560e+02" - - doAssert "$# $3 $# $#" % ["a", "b", "c"] == "a c b c" - doAssert "${1}12 ${-1}$2" % ["a", "b"] == "a12 bb" - - block: # formatSize tests - when not defined(js): - doAssert formatSize((1'i64 shl 31) + (300'i64 shl 20)) == "2.293GiB" # <=== bug #8231 - doAssert formatSize((2.234*1024*1024).int) == "2.234MiB" - doAssert formatSize(4096) == "4KiB" - doAssert formatSize(4096, prefix = bpColloquial, includeSpace = true) == "4 kB" - doAssert formatSize(4096, includeSpace = true) == "4 KiB" - doAssert formatSize(5_378_934, prefix = bpColloquial, decimalSep = ',') == "5,13MB" - - block: # formatEng tests - doAssert formatEng(0, 2, trim = false) == "0.00" - doAssert formatEng(0, 2) == "0" - doAssert formatEng(53, 2, trim = false) == "53.00" - doAssert formatEng(0.053, 2, trim = false) == "53.00e-3" - doAssert formatEng(0.053, 4, trim = false) == "53.0000e-3" - doAssert formatEng(0.053, 4, trim = true) == "53e-3" - doAssert formatEng(0.053, 0) == "53e-3" - doAssert formatEng(52731234) == "52.731234e6" - doAssert formatEng(-52731234) == "-52.731234e6" - doAssert formatEng(52731234, 1) == "52.7e6" - doAssert formatEng(-52731234, 1) == "-52.7e6" - doAssert formatEng(52731234, 1, decimalSep = ',') == "52,7e6" - doAssert formatEng(-52731234, 1, decimalSep = ',') == "-52,7e6" - - doAssert formatEng(4100, siPrefix = true, unit = "V") == "4.1 kV" - doAssert formatEng(4.1, siPrefix = true, unit = "V", - useUnitSpace = true) == "4.1 V" - doAssert formatEng(4.1, siPrefix = true) == "4.1" # Note lack of space - doAssert formatEng(4100, siPrefix = true) == "4.1 k" - doAssert formatEng(4.1, siPrefix = true, unit = "", - useUnitSpace = true) == "4.1 " # Includes space - doAssert formatEng(4100, siPrefix = true, unit = "") == "4.1 k" - doAssert formatEng(4100) == "4.1e3" - doAssert formatEng(4100, unit = "V", useUnitSpace = true) == "4.1e3 V" - doAssert formatEng(4100, unit = "", useUnitSpace = true) == "4.1e3 " - # Don't use SI prefix as number is too big - doAssert formatEng(3.1e22, siPrefix = true, unit = "a", - useUnitSpace = true) == "31e21 a" - # Don't use SI prefix as number is too small - doAssert formatEng(3.1e-25, siPrefix = true, unit = "A", - useUnitSpace = true) == "310e-27 A" - - proc staticTests = - doAssert align("abc", 4) == " abc" - doAssert align("a", 0) == "a" - doAssert align("1232", 6) == " 1232" - doAssert align("1232", 6, '#') == "##1232" - - doAssert alignLeft("abc", 4) == "abc " - doAssert alignLeft("a", 0) == "a" - doAssert alignLeft("1232", 6) == "1232 " - doAssert alignLeft("1232", 6, '#') == "1232##" - - doAssert "$animal eats $food." % ["animal", "The cat", "food", "fish"] == - "The cat eats fish." - - doAssert "-ld a-ldz -ld".replaceWord("-ld") == " a-ldz " - doAssert "-lda-ldz -ld abc".replaceWord("-ld") == "-lda-ldz abc" - - doAssert "-lda-ldz -ld abc".replaceWord("") == "-lda-ldz -ld abc" - doAssert "oo".replace("", "abc") == "oo" - - type MyEnum = enum enA, enB, enC, enuD, enE - doAssert parseEnum[MyEnum]("enu_D") == enuD - - doAssert parseEnum("invalid enum value", enC) == enC - - doAssert center("foo", 13) == " foo " - doAssert center("foo", 0) == "foo" - doAssert center("foo", 3, fillChar = 'a') == "foo" - doAssert center("foo", 10, fillChar = '\t') == "\t\t\tfoo\t\t\t\t" - - doAssert count("foofoofoo", "foofoo") == 1 - doAssert count("foofoofoo", "foofoo", overlapping = true) == 2 - doAssert count("foofoofoo", 'f') == 3 - doAssert count("foofoofoobar", {'f', 'b'}) == 4 - - doAssert strip(" foofoofoo ") == "foofoofoo" - doAssert strip("sfoofoofoos", chars = {'s'}) == "foofoofoo" - doAssert strip("barfoofoofoobar", chars = {'b', 'a', 'r'}) == "foofoofoo" - doAssert strip("stripme but don't strip this stripme", - chars = {'s', 't', 'r', 'i', 'p', 'm', 'e'}) == - " but don't strip this " - doAssert strip("sfoofoofoos", leading = false, chars = {'s'}) == "sfoofoofoo" - doAssert strip("sfoofoofoos", trailing = false, chars = {'s'}) == "foofoofoos" - - doAssert " foo\n bar".indent(4, "Q") == "QQQQ foo\nQQQQ bar" - - doAssert "abba".multiReplace(("a", "b"), ("b", "a")) == "baab" - doAssert "Hello World.".multiReplace(("ello", "ELLO"), ("World.", - "PEOPLE!")) == "HELLO PEOPLE!" - doAssert "aaaa".multiReplace(("a", "aa"), ("aa", "bb")) == "aaaaaaaa" - - doAssert isAlphaAscii('r') - doAssert isAlphaAscii('A') - doAssert(not isAlphaAscii('$')) - - doAssert isAlphaNumeric('3') - doAssert isAlphaNumeric('R') - doAssert(not isAlphaNumeric('!')) - - doAssert isDigit('3') - doAssert(not isDigit('a')) - doAssert(not isDigit('%')) - - doAssert isSpaceAscii('\t') - doAssert isSpaceAscii('\l') - doAssert(not isSpaceAscii('A')) - - doAssert(isEmptyOrWhitespace("")) - doAssert(isEmptyOrWhitespace(" ")) - doAssert(isEmptyOrWhitespace("\t\l \v\r\f")) - doAssert(not isEmptyOrWhitespace("ABc \td")) - - doAssert isLowerAscii('a') - doAssert isLowerAscii('z') - doAssert(not isLowerAscii('A')) - doAssert(not isLowerAscii('5')) - doAssert(not isLowerAscii('&')) - doAssert(not isLowerAscii(' ')) - - doAssert isUpperAscii('A') - doAssert(not isUpperAscii('b')) - doAssert(not isUpperAscii('5')) - doAssert(not isUpperAscii('%')) - - doAssert rsplit("foo bar", seps = Whitespace) == @["foo", "bar"] - doAssert rsplit(" foo bar", seps = Whitespace, maxsplit = 1) == @[" foo", "bar"] - doAssert rsplit(" foo bar ", seps = Whitespace, maxsplit = 1) == @[ - " foo bar", ""] - doAssert rsplit(":foo:bar", sep = ':') == @["", "foo", "bar"] - doAssert rsplit(":foo:bar", sep = ':', maxsplit = 2) == @["", "foo", "bar"] - doAssert rsplit(":foo:bar", sep = ':', maxsplit = 3) == @["", "foo", "bar"] - doAssert rsplit("foothebar", sep = "the") == @["foo", "bar"] - - doAssert(unescape(r"\x013", "", "") == "\x013") - - doAssert join(["foo", "bar", "baz"]) == "foobarbaz" - doAssert join(@["foo", "bar", "baz"], ", ") == "foo, bar, baz" - doAssert join([1, 2, 3]) == "123" - doAssert join(@[1, 2, 3], ", ") == "1, 2, 3" - - doAssert """~~!!foo -~~!!bar -~~!!baz""".unindent(2, "~~!!") == "foo\nbar\nbaz" - - doAssert """~~!!foo -~~!!bar -~~!!baz""".unindent(2, "~~!!aa") == "~~!!foo\n~~!!bar\n~~!!baz" - doAssert """~~foo -~~ bar -~~ baz""".unindent(4, "~") == "foo\n bar\n baz" - doAssert """foo -bar - baz - """.unindent(4) == "foo\nbar\nbaz\n" - doAssert """foo - bar - baz - """.unindent(2) == "foo\n bar\n baz\n" - doAssert """foo - bar - baz - """.unindent(100) == "foo\nbar\nbaz\n" - - doAssert """foo - foo - bar - """.unindent() == "foo\nfoo\nbar\n" - - let s = " this is an example " - let s2 = ":this;is;an:example;;" - - doAssert s.split() == @["", "this", "is", "an", "example", "", ""] - doAssert s2.split(seps = {':', ';'}) == @["", "this", "is", "an", "example", - "", ""] - doAssert s.split(maxsplit = 4) == @["", "this", "is", "an", "example "] - doAssert s.split(' ', maxsplit = 1) == @["", "this is an example "] - doAssert s.split(" ", maxsplit = 4) == @["", "this", "is", "an", "example "] - - doAssert s.splitWhitespace() == @["this", "is", "an", "example"] - doAssert s.splitWhitespace(maxsplit = 1) == @["this", "is an example "] - doAssert s.splitWhitespace(maxsplit = 2) == @["this", "is", "an example "] - doAssert s.splitWhitespace(maxsplit = 3) == @["this", "is", "an", "example "] - doAssert s.splitWhitespace(maxsplit = 4) == @["this", "is", "an", "example"] - - block: # startsWith / endsWith char tests - var s = "abcdef" - doAssert s.startsWith('a') - doAssert s.startsWith('b') == false - doAssert s.endsWith('f') - doAssert s.endsWith('a') == false - doAssert s.endsWith('\0') == false - - #echo("strutils tests passed") - - nonStaticTests() - staticTests() - static: staticTests() diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim index 8839a7767..a28e98caf 100644 --- a/lib/pure/unicode.nim +++ b/lib/pure/unicode.nim @@ -1169,219 +1169,3 @@ proc alignLeft*(s: string, count: Natural, padding = ' '.Rune): string {. result.add padStr else: result = s - - -when isMainModule: - - proc asRune(s: static[string]): Rune = - ## Compile-time conversion proc for converting string literals to a Rune - ## value. Returns the first Rune of the specified string. - ## - ## Shortcuts code like ``"å".runeAt(0)`` to ``"å".asRune`` and returns a - ## compile-time constant. - if s.len == 0: Rune(0) - else: s.runeAt(0) - - let - someString = "öÑ" - someRunes = toRunes(someString) - compared = (someString == $someRunes) - doAssert compared == true - - proc testReplacements(word: string): string = - case word - of "two": - return "2" - of "foo": - return "BAR" - of "βeta": - return "beta" - of "alpha": - return "αlpha" - else: - return "12345" - - doAssert translate("two not alpha foo βeta", testReplacements) == "2 12345 αlpha BAR beta" - doAssert translate(" two not foo βeta ", testReplacements) == " 2 12345 BAR beta " - - doAssert title("foo bar") == "Foo Bar" - doAssert title("αlpha βeta γamma") == "Αlpha Βeta Γamma" - doAssert title("") == "" - - doAssert capitalize("βeta") == "Βeta" - doAssert capitalize("foo") == "Foo" - doAssert capitalize("") == "" - - doAssert swapCase("FooBar") == "fOObAR" - doAssert swapCase(" ") == " " - doAssert swapCase("Αlpha Βeta Γamma") == "αLPHA βETA γAMMA" - doAssert swapCase("a✓B") == "A✓b" - doAssert swapCase("Јамогујестистаклоитоминештети") == "јАМОГУЈЕСТИСТАКЛОИТОМИНЕШТЕТИ" - doAssert swapCase("ὕαλονϕαγεῖνδύναμαιτοῦτοοὔμεβλάπτει") == "ὝΑΛΟΝΦΑΓΕῖΝΔΎΝΑΜΑΙΤΟῦΤΟΟὔΜΕΒΛΆΠΤΕΙ" - doAssert swapCase("Կրնամապակիուտեևինծիանհանգիստչըներ") == "կՐՆԱՄԱՊԱԿԻՈՒՏԵևԻՆԾԻԱՆՀԱՆԳԻՍՏՉԸՆԵՐ" - doAssert swapCase("") == "" - - doAssert isAlpha("r") - doAssert isAlpha("α") - doAssert isAlpha("ϙ") - doAssert isAlpha("ஶ") - doAssert(not isAlpha("$")) - doAssert(not isAlpha("")) - - doAssert isAlpha("Βeta") - doAssert isAlpha("Args") - doAssert isAlpha("𐌼𐌰𐌲𐌲𐌻𐌴𐍃𐍄𐌰𐌽") - doAssert isAlpha("ὕαλονϕαγεῖνδύναμαιτοῦτοοὔμεβλάπτει") - doAssert isAlpha("Јамогујестистаклоитоминештети") - doAssert isAlpha("Կրնամապակիուտեևինծիանհանգիստչըներ") - doAssert(not isAlpha("$Foo✓")) - doAssert(not isAlpha("⠙⠕⠑⠎⠝⠞")) - - doAssert isSpace("\t") - doAssert isSpace("\l") - doAssert(not isSpace("Β")) - doAssert(not isSpace("Βeta")) - - doAssert isSpace("\t\l \v\r\f") - doAssert isSpace(" ") - doAssert(not isSpace("")) - doAssert(not isSpace("ΑΓc \td")) - - doAssert(not isLower(' '.Rune)) - - doAssert(not isUpper(' '.Rune)) - - doAssert toUpper("Γ") == "Γ" - doAssert toUpper("b") == "B" - doAssert toUpper("α") == "Α" - doAssert toUpper("✓") == "✓" - doAssert toUpper("ϙ") == "Ϙ" - doAssert toUpper("") == "" - - doAssert toUpper("ΑΒΓ") == "ΑΒΓ" - doAssert toUpper("AAccβ") == "AACCΒ" - doAssert toUpper("A✓$β") == "A✓$Β" - - doAssert toLower("a") == "a" - doAssert toLower("γ") == "γ" - doAssert toLower("Γ") == "γ" - doAssert toLower("4") == "4" - doAssert toLower("Ϙ") == "ϙ" - doAssert toLower("") == "" - - doAssert toLower("abcdγ") == "abcdγ" - doAssert toLower("abCDΓ") == "abcdγ" - doAssert toLower("33aaΓ") == "33aaγ" - - doAssert reversed("Reverse this!") == "!siht esreveR" - doAssert reversed("先秦兩漢") == "漢兩秦先" - doAssert reversed("as⃝df̅") == "f̅ds⃝a" - doAssert reversed("a⃞b⃞c⃞") == "c⃞b⃞a⃞" - doAssert reversed("ὕαλονϕαγεῖνδύναμαιτοῦτοοὔμεβλάπτει") == "ιετπάλβεμὔοοτῦοτιαμανύδνῖεγαϕνολαὕ" - doAssert reversed("Јамогујестистаклоитоминештети") == "итетшенимотиолкатситсејугомаЈ" - doAssert reversed("Կրնամապակիուտեևինծիանհանգիստչըներ") == "րենըչտսիգնահնաիծնիևետւոիկապամանրԿ" - doAssert len(toRunes("as⃝df̅")) == runeLen("as⃝df̅") - const test = "as⃝" - doAssert lastRune(test, test.len-1)[1] == 3 - doAssert graphemeLen("è", 0) == 2 - - # test for rune positioning and runeSubStr() - let s = "Hänsel ««: 10,00€" - - var t = "" - for c in s.utf8: - t.add c - - doAssert(s == t) - - doAssert(runeReverseOffset(s, 1) == (20, 18)) - doAssert(runeReverseOffset(s, 19) == (-1, 18)) - - doAssert(runeStrAtPos(s, 0) == "H") - doAssert(runeSubStr(s, 0, 1) == "H") - doAssert(runeStrAtPos(s, 10) == ":") - doAssert(runeSubStr(s, 10, 1) == ":") - doAssert(runeStrAtPos(s, 9) == "«") - doAssert(runeSubStr(s, 9, 1) == "«") - doAssert(runeStrAtPos(s, 17) == "€") - doAssert(runeSubStr(s, 17, 1) == "€") - # echo runeStrAtPos(s, 18) # index error - - doAssert(runeSubStr(s, 0) == "Hänsel ««: 10,00€") - doAssert(runeSubStr(s, -18) == "Hänsel ««: 10,00€") - doAssert(runeSubStr(s, 10) == ": 10,00€") - doAssert(runeSubStr(s, 18) == "") - doAssert(runeSubStr(s, 0, 10) == "Hänsel ««") - - doAssert(runeSubStr(s, 12) == "10,00€") - doAssert(runeSubStr(s, -6) == "10,00€") - - doAssert(runeSubStr(s, 12, 5) == "10,00") - doAssert(runeSubStr(s, 12, -1) == "10,00") - doAssert(runeSubStr(s, -6, 5) == "10,00") - doAssert(runeSubStr(s, -6, -1) == "10,00") - - doAssert(runeSubStr(s, 0, 100) == "Hänsel ««: 10,00€") - doAssert(runeSubStr(s, -100, 100) == "Hänsel ««: 10,00€") - doAssert(runeSubStr(s, 0, -100) == "") - doAssert(runeSubStr(s, 100, -100) == "") - - block splitTests: - let s = " this is an example " - let s2 = ":this;is;an:example;;" - let s3 = ":this×is×an:example××" - doAssert s.split() == @["", "this", "is", "an", "example", "", ""] - doAssert s2.split(seps = [':'.Rune, ';'.Rune]) == @["", "this", "is", "an", - "example", "", ""] - doAssert s3.split(seps = [':'.Rune, "×".asRune]) == @["", "this", "is", - "an", "example", "", ""] - doAssert s.split(maxsplit = 4) == @["", "this", "is", "an", "example "] - doAssert s.split(' '.Rune, maxsplit = 1) == @["", "this is an example "] - doAssert s3.split("×".runeAt(0)) == @[":this", "is", "an:example", "", ""] - - block stripTests: - doAssert(strip("") == "") - doAssert(strip(" ") == "") - doAssert(strip("y") == "y") - doAssert(strip(" foofoofoo ") == "foofoofoo") - doAssert(strip("sfoofoofoos", runes = ['s'.Rune]) == "foofoofoo") - - block: - let stripTestRunes = ['b'.Rune, 'a'.Rune, 'r'.Rune] - doAssert(strip("barfoofoofoobar", runes = stripTestRunes) == "foofoofoo") - doAssert(strip("sfoofoofoos", leading = false, runes = ['s'.Rune]) == "sfoofoofoo") - doAssert(strip("sfoofoofoos", trailing = false, runes = ['s'.Rune]) == "foofoofoos") - - block: - let stripTestRunes = ["«".asRune, "»".asRune] - doAssert(strip("«TEXT»", runes = stripTestRunes) == "TEXT") - doAssert(strip("copyright©", leading = false, runes = ["©".asRune]) == "copyright") - doAssert(strip("¿Question?", trailing = false, runes = ["¿".asRune]) == "Question?") - doAssert(strip("×text×", leading = false, runes = ["×".asRune]) == "×text") - doAssert(strip("×text×", trailing = false, runes = ["×".asRune]) == "text×") - - block repeatTests: - doAssert repeat('c'.Rune, 5) == "ccccc" - doAssert repeat("×".asRune, 5) == "×××××" - - block alignTests: - doAssert align("abc", 4) == " abc" - doAssert align("a", 0) == "a" - doAssert align("1232", 6) == " 1232" - doAssert align("1232", 6, '#'.Rune) == "##1232" - doAssert align("1232", 6, "×".asRune) == "××1232" - doAssert alignLeft("abc", 4) == "abc " - doAssert alignLeft("a", 0) == "a" - doAssert alignLeft("1232", 6) == "1232 " - doAssert alignLeft("1232", 6, '#'.Rune) == "1232##" - doAssert alignLeft("1232", 6, "×".asRune) == "1232××" - - block differentSizes: - # upper and lower variants have different number of bytes - doAssert toLower("AẞC") == "aßc" - doAssert toLower("ȺẞCD") == "ⱥßcd" - doAssert toUpper("ⱥbc") == "ȺBC" - doAssert toUpper("rsⱦuv") == "RSȾUV" - doAssert swapCase("ⱥbCd") == "ȺBcD" - doAssert swapCase("XyꟆaB") == "xYᶎAb" - doAssert swapCase("aᵹcᲈd") == "AꝽCꙊD" diff --git a/lib/pure/unidecode/unidecode.nim b/lib/pure/unidecode/unidecode.nim index 30aaa7cd8..9a10ccc00 100644 --- a/lib/pure/unidecode/unidecode.nim +++ b/lib/pure/unidecode/unidecode.nim @@ -66,8 +66,3 @@ proc unidecode*(s: string): string = var c = int(r) if c <=% 127: add(result, chr(c)) elif c <% translationTable.len: add(result, translationTable[c-128]) - -when isMainModule: - #loadUnidecodeTable("lib/pure/unidecode/unidecode.dat") - doAssert unidecode("Äußerst") == "Ausserst" - doAssert unidecode("北京") == "Bei Jing " diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim index 3e107a6b8..15d0996a7 100644 --- a/lib/pure/uri.nim +++ b/lib/pure/uri.nim @@ -503,288 +503,3 @@ proc getDataUri*(data, mime: string, encoding = "utf-8"): string {.since: (1, 3) runnableExamples: static: doAssert getDataUri("Nim", "text/plain") == "data:text/plain;charset=utf-8;base64,Tmlt" assert encoding.len > 0 and mime.len > 0 # Must *not* be URL-Safe, see RFC-2397 result = "data:" & mime & ";charset=" & encoding & ";base64," & base64.encode(data) - - -when isMainModule: - block: - const test1 = "abc\L+def xyz" - doAssert encodeUrl(test1) == "abc%0A%2Bdef+xyz" - doAssert decodeUrl(encodeUrl(test1)) == test1 - doAssert encodeUrl(test1, false) == "abc%0A%2Bdef%20xyz" - doAssert decodeUrl(encodeUrl(test1, false), false) == test1 - doAssert decodeUrl(encodeUrl(test1)) == test1 - - block: - let str = "http://localhost" - let test = parseUri(str) - doAssert test.path == "" - - block: - let str = "http://localhost/" - let test = parseUri(str) - doAssert test.path == "/" - - block: - let str = "http://localhost:8080/test" - let test = parseUri(str) - doAssert test.scheme == "http" - doAssert test.port == "8080" - doAssert test.path == "/test" - doAssert test.hostname == "localhost" - doAssert($test == str) - - block: - let str = "foo://username:password@example.com:8042/over/there" & - "/index.dtb?type=animal&name=narwhal#nose" - let test = parseUri(str) - doAssert test.scheme == "foo" - doAssert test.username == "username" - doAssert test.password == "password" - doAssert test.hostname == "example.com" - doAssert test.port == "8042" - doAssert test.path == "/over/there/index.dtb" - doAssert test.query == "type=animal&name=narwhal" - doAssert test.anchor == "nose" - doAssert($test == str) - - block: - # IPv6 address - let str = "foo://[::1]:1234/bar?baz=true&qux#quux" - let uri = parseUri(str) - doAssert uri.scheme == "foo" - doAssert uri.hostname == "::1" - doAssert uri.port == "1234" - doAssert uri.path == "/bar" - doAssert uri.query == "baz=true&qux" - doAssert uri.anchor == "quux" - - block: - let str = "urn:example:animal:ferret:nose" - let test = parseUri(str) - doAssert test.scheme == "urn" - doAssert test.path == "example:animal:ferret:nose" - doAssert($test == str) - - block: - let str = "mailto:username@example.com?subject=Topic" - let test = parseUri(str) - doAssert test.scheme == "mailto" - doAssert test.username == "username" - doAssert test.hostname == "example.com" - doAssert test.query == "subject=Topic" - doAssert($test == str) - - block: - let str = "magnet:?xt=urn:sha1:72hsga62ba515sbd62&dn=foobar" - let test = parseUri(str) - doAssert test.scheme == "magnet" - doAssert test.query == "xt=urn:sha1:72hsga62ba515sbd62&dn=foobar" - doAssert($test == str) - - block: - let str = "/test/foo/bar?q=2#asdf" - let test = parseUri(str) - doAssert test.scheme == "" - doAssert test.path == "/test/foo/bar" - doAssert test.query == "q=2" - doAssert test.anchor == "asdf" - doAssert($test == str) - - block: - let str = "test/no/slash" - let test = parseUri(str) - doAssert test.path == "test/no/slash" - doAssert($test == str) - - block: - let str = "//git@github.com:dom96/packages" - let test = parseUri(str) - doAssert test.scheme == "" - doAssert test.username == "git" - doAssert test.hostname == "github.com" - doAssert test.port == "dom96" - doAssert test.path == "/packages" - - block: - let str = "file:///foo/bar/baz.txt" - let test = parseUri(str) - doAssert test.scheme == "file" - doAssert test.username == "" - doAssert test.hostname == "" - doAssert test.port == "" - doAssert test.path == "/foo/bar/baz.txt" - - # Remove dot segments tests - block: - doAssert removeDotSegments("/foo/bar/baz") == "/foo/bar/baz" - - # Combine tests - block: - let concat = combine(parseUri("http://google.com/foo/bar/"), parseUri("baz")) - doAssert concat.path == "/foo/bar/baz" - doAssert concat.hostname == "google.com" - doAssert concat.scheme == "http" - - block: - let concat = combine(parseUri("http://google.com/foo"), parseUri("/baz")) - doAssert concat.path == "/baz" - doAssert concat.hostname == "google.com" - doAssert concat.scheme == "http" - - block: - let concat = combine(parseUri("http://google.com/foo/test"), parseUri("bar")) - doAssert concat.path == "/foo/bar" - - block: - let concat = combine(parseUri("http://google.com/foo/test"), parseUri("/bar")) - doAssert concat.path == "/bar" - - block: - let concat = combine(parseUri("http://google.com/foo/test"), parseUri("bar")) - doAssert concat.path == "/foo/bar" - - block: - let concat = combine(parseUri("http://google.com/foo/test/"), parseUri("bar")) - doAssert concat.path == "/foo/test/bar" - - block: - let concat = combine(parseUri("http://google.com/foo/test/"), parseUri("bar/")) - doAssert concat.path == "/foo/test/bar/" - - block: - let concat = combine(parseUri("http://google.com/foo/test/"), parseUri("bar/"), - parseUri("baz")) - doAssert concat.path == "/foo/test/bar/baz" - - # `/` tests - block: - let test = parseUri("http://example.com/foo") / "bar/asd" - doAssert test.path == "/foo/bar/asd" - - block: - let test = parseUri("http://example.com/foo/") / "/bar/asd" - doAssert test.path == "/foo/bar/asd" - - # removeDotSegments tests - block: - # empty test - doAssert removeDotSegments("") == "" - - # bug #3207 - block: - doAssert parseUri("http://qq/1").combine(parseUri("https://qqq")).`$` == "https://qqq" - - # bug #4959 - block: - let foo = parseUri("http://example.com") / "/baz" - doAssert foo.path == "/baz" - - # bug found on stream 13/10/17 - block: - let foo = parseUri("http://localhost:9515") / "status" - doAssert $foo == "http://localhost:9515/status" - - # bug #6649 #6652 - block: - var foo = parseUri("http://example.com") - foo.hostname = "example.com" - foo.path = "baz" - doAssert $foo == "http://example.com/baz" - - foo.hostname = "example.com/" - foo.path = "baz" - doAssert $foo == "http://example.com/baz" - - foo.hostname = "example.com" - foo.path = "/baz" - doAssert $foo == "http://example.com/baz" - - foo.hostname = "example.com/" - foo.path = "/baz" - doAssert $foo == "http://example.com/baz" - - foo.hostname = "example.com/" - foo.port = "8000" - foo.path = "baz" - doAssert $foo == "http://example.com:8000/baz" - - foo = parseUri("file:/dir/file") - foo.path = "relative" - doAssert $foo == "file:relative" - - # isAbsolute tests - block: - doAssert "www.google.com".parseUri().isAbsolute() == false - doAssert "http://www.google.com".parseUri().isAbsolute() == true - doAssert "file:/dir/file".parseUri().isAbsolute() == true - doAssert "file://localhost/dir/file".parseUri().isAbsolute() == true - doAssert "urn:ISSN:1535-3613".parseUri().isAbsolute() == true - - # path-relative URL *relative - doAssert "about".parseUri().isAbsolute == false - doAssert "about/staff.html".parseUri().isAbsolute == false - doAssert "about/staff.html?".parseUri().isAbsolute == false - doAssert "about/staff.html?parameters".parseUri().isAbsolute == false - - # absolute-path-relative URL *relative - doAssert "/".parseUri().isAbsolute == false - doAssert "/about".parseUri().isAbsolute == false - doAssert "/about/staff.html".parseUri().isAbsolute == false - doAssert "/about/staff.html?".parseUri().isAbsolute == false - doAssert "/about/staff.html?parameters".parseUri().isAbsolute == false - - # scheme-relative URL *relative - doAssert "//username:password@example.com:8888".parseUri().isAbsolute == false - doAssert "//username@example.com".parseUri().isAbsolute == false - doAssert "//example.com".parseUri().isAbsolute == false - doAssert "//example.com/".parseUri().isAbsolute == false - doAssert "//example.com/about".parseUri().isAbsolute == false - doAssert "//example.com/about/staff.html".parseUri().isAbsolute == false - doAssert "//example.com/about/staff.html?".parseUri().isAbsolute == false - doAssert "//example.com/about/staff.html?parameters".parseUri().isAbsolute == false - - # absolute URL *absolute - doAssert "https://username:password@example.com:8888".parseUri().isAbsolute == true - doAssert "https://username@example.com".parseUri().isAbsolute == true - doAssert "https://example.com".parseUri().isAbsolute == true - doAssert "https://example.com/".parseUri().isAbsolute == true - doAssert "https://example.com/about".parseUri().isAbsolute == true - doAssert "https://example.com/about/staff.html".parseUri().isAbsolute == true - doAssert "https://example.com/about/staff.html?".parseUri().isAbsolute == true - doAssert "https://example.com/about/staff.html?parameters".parseUri().isAbsolute == true - - # encodeQuery tests - block: - doAssert encodeQuery({:}) == "" - doAssert encodeQuery({"foo": "bar"}) == "foo=bar" - doAssert encodeQuery({"foo": "bar & baz"}) == "foo=bar+%26+baz" - doAssert encodeQuery({"foo": "bar & baz"}, usePlus = false) == "foo=bar%20%26%20baz" - doAssert encodeQuery({"foo": ""}) == "foo" - doAssert encodeQuery({"foo": ""}, omitEq = false) == "foo=" - doAssert encodeQuery({"a": "1", "b": "", "c": "3"}) == "a=1&b&c=3" - doAssert encodeQuery({"a": "1", "b": "", "c": "3"}, omitEq = false) == "a=1&b=&c=3" - - block: - var foo = parseUri("http://example.com") / "foo" ? {"bar": "1", "baz": "qux"} - var foo1 = parseUri("http://example.com/foo?bar=1&baz=qux") - doAssert foo == foo1 - - block: - var foo = parseUri("http://example.com") / "foo" ? {"do": "do", "bar": ""} - var foo1 = parseUri("http://example.com/foo?do=do&bar") - doAssert foo == foo1 - - block dataUriBase64: - doAssert getDataUri("", "text/plain") == "data:text/plain;charset=utf-8;base64," - doAssert getDataUri(" ", "text/plain") == "data:text/plain;charset=utf-8;base64,IA==" - doAssert getDataUri("c\xf7>", "text/plain") == "data:text/plain;charset=utf-8;base64,Y/c+" - doAssert getDataUri("Hello World", "text/plain") == "data:text/plain;charset=utf-8;base64,SGVsbG8gV29ybGQ=" - doAssert getDataUri("leasure.", "text/plain") == "data:text/plain;charset=utf-8;base64,bGVhc3VyZS4=" - doAssert getDataUri("""!@#$%^&*()_+""", "text/plain") == "data:text/plain;charset=utf-8;base64,IUAjJCVeJiooKV8r" - doAssert(getDataUri("the quick brown dog jumps over the lazy fox", "text/plain") == - "data:text/plain;charset=utf-8;base64,dGhlIHF1aWNrIGJyb3duIGRvZyBqdW1wcyBvdmVyIHRoZSBsYXp5IGZveA==") - doAssert(getDataUri("""The present is theirs - The future, for which I really worked, is mine.""", "text/plain") == - "data:text/plain;charset=utf-8;base64,VGhlIHByZXNlbnQgaXMgdGhlaXJzCiAgICAgIFRoZSBmdXR1cmUsIGZvciB3aGljaCBJIHJlYWxseSB3b3JrZWQsIGlzIG1pbmUu") - - echo("All good!") diff --git a/lib/std/enumerate.nim b/lib/std/enumerate.nim index 3def653c8..a8f0e1ba7 100644 --- a/lib/std/enumerate.nim +++ b/lib/std/enumerate.nim @@ -68,22 +68,3 @@ macro enumerate*(x: ForLoopStmt): untyped {.since: (1, 3).} = result.add newFor # now wrap the whole macro in a block to create a new scope result = newBlockStmt(result) - -when isMainModule: - let a = @[1, 3, 5, 7] - - block: - var res: seq[(int, int)] - for i, x in enumerate(a): - res.add (i, x) - assert res == @[(0, 1), (1, 3), (2, 5), (3, 7)] - block: - var res: seq[(int, int)] - for (i, x) in enumerate(a.items): - res.add (i, x) - assert res == @[(0, 1), (1, 3), (2, 5), (3, 7)] - block: - var res: seq[(int, int)] - for i, x in enumerate(3, a): - res.add (i, x) - assert res == @[(3, 1), (4, 3), (5, 5), (6, 7)] diff --git a/lib/std/monotimes.nim b/lib/std/monotimes.nim index f819cbc8d..c56bf77b3 100644 --- a/lib/std/monotimes.nim +++ b/lib/std/monotimes.nim @@ -152,19 +152,3 @@ proc high*(typ: typedesc[MonoTime]): MonoTime = proc low*(typ: typedesc[MonoTime]): MonoTime = ## Returns the lowest representable `MonoTime`. MonoTime(ticks: low(int64)) - -when isMainModule: - let d = initDuration(nanoseconds = 10) - let t1 = getMonoTime() - let t2 = t1 + d - - doAssert t2 - t1 == d - doAssert t1 == t1 - doAssert t1 != t2 - doAssert t2 - d == t1 - doAssert t1 < t2 - doAssert t1 <= t2 - doAssert t1 <= t1 - doAssert not(t2 < t1) - doAssert t1 < high(MonoTime) - doAssert low(MonoTime) < t1 diff --git a/lib/std/sha1.nim b/lib/std/sha1.nim index 9763b286e..3c1674bcb 100644 --- a/lib/std/sha1.nim +++ b/lib/std/sha1.nim @@ -267,16 +267,3 @@ proc `==`*(a, b: SecureHash): bool = assert a == c # Not a constant-time comparison, but that's acceptable in this context Sha1Digest(a) == Sha1Digest(b) - -when isMainModule: - let hash1 = secureHash("a93tgj0p34jagp9[agjp98ajrhp9aej]") - doAssert hash1 == hash1 - doAssert parseSecureHash($hash1) == hash1 - - template checkVector(s, exp: string) = - doAssert secureHash(s) == parseSecureHash(exp) - - checkVector("", "da39a3ee5e6b4b0d3255bfef95601890afd80709") - checkVector("abc", "a9993e364706816aba3e25717850c26c9cd0d89d") - checkVector("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", - "84983e441c3bd26ebaae4aa1f95129e5e54670f1") diff --git a/tests/stdlib/talgorithm.nim b/tests/stdlib/talgorithm.nim index 85ae79219..59336e70d 100644 --- a/tests/stdlib/talgorithm.nim +++ b/tests/stdlib/talgorithm.nim @@ -16,3 +16,100 @@ proc test() = discard test() + +block: + # Tests for lowerBound + var arr = @[1, 2, 3, 5, 6, 7, 8, 9] + assert arr.lowerBound(0) == 0 + assert arr.lowerBound(4) == 3 + assert arr.lowerBound(5) == 3 + assert arr.lowerBound(10) == 8 + arr = @[1, 5, 10] + assert arr.lowerBound(4) == 1 + assert arr.lowerBound(5) == 1 + assert arr.lowerBound(6) == 2 + # Tests for isSorted + var srt1 = [1, 2, 3, 4, 4, 4, 4, 5] + var srt2 = ["iello", "hello"] + var srt3 = [1.0, 1.0, 1.0] + var srt4: seq[int] + assert srt1.isSorted(cmp) == true + assert srt2.isSorted(cmp) == false + assert srt3.isSorted(cmp) == true + assert srt4.isSorted(cmp) == true + var srtseq = newSeq[int]() + assert srtseq.isSorted(cmp) == true + # Tests for reversed + var arr1 = @[0, 1, 2, 3, 4] + assert arr1.reversed() == @[4, 3, 2, 1, 0] + for i in 0 .. high(arr1): + assert arr1.reversed(0, i) == arr1.reversed()[high(arr1) - i .. high(arr1)] + assert arr1.reversed(i, high(arr1)) == arr1.reversed()[0 .. high(arr1) - i] + +block: + var list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + let list2 = list.rotatedLeft(1 ..< 9, 3) + let expected = [0, 4, 5, 6, 7, 8, 1, 2, 3, 9, 10] + + doAssert list.rotateLeft(1 ..< 9, 3) == 6 + doAssert list == expected + doAssert list2 == @expected + + var s0, s1, s2, s3, s4, s5 = "xxxabcdefgxxx" + + doAssert s0.rotateLeft(3 ..< 10, 3) == 7 + doAssert s0 == "xxxdefgabcxxx" + doAssert s1.rotateLeft(3 ..< 10, 2) == 8 + doAssert s1 == "xxxcdefgabxxx" + doAssert s2.rotateLeft(3 ..< 10, 4) == 6 + doAssert s2 == "xxxefgabcdxxx" + doAssert s3.rotateLeft(3 ..< 10, -3) == 6 + doAssert s3 == "xxxefgabcdxxx" + doAssert s4.rotateLeft(3 ..< 10, -10) == 6 + doAssert s4 == "xxxefgabcdxxx" + doAssert s5.rotateLeft(3 ..< 10, 11) == 6 + doAssert s5 == "xxxefgabcdxxx" + + block product: + doAssert product(newSeq[seq[int]]()) == newSeq[seq[int]](), "empty input" + doAssert product(@[newSeq[int](), @[], @[]]) == newSeq[seq[int]](), "bit more empty input" + doAssert product(@[@[1, 2]]) == @[@[1, 2]], "a simple case of one element" + doAssert product(@[@[1, 2], @[3, 4]]) == @[@[2, 4], @[1, 4], @[2, 3], @[1, + 3]], "two elements" + doAssert product(@[@[1, 2], @[3, 4], @[5, 6]]) == @[@[2, 4, 6], @[1, 4, 6], + @[2, 3, 6], @[1, 3, 6], @[2, 4, 5], @[1, 4, 5], @[2, 3, 5], @[1, 3, 5]], "three elements" + doAssert product(@[@[1, 2], @[]]) == newSeq[seq[int]](), "two elements, but one empty" + + block lowerBound: + doAssert lowerBound([1, 2, 4], 3, system.cmp[int]) == 2 + doAssert lowerBound([1, 2, 2, 3], 4, system.cmp[int]) == 4 + doAssert lowerBound([1, 2, 3, 10], 11) == 4 + + block upperBound: + doAssert upperBound([1, 2, 4], 3, system.cmp[int]) == 2 + doAssert upperBound([1, 2, 2, 3], 3, system.cmp[int]) == 4 + doAssert upperBound([1, 2, 3, 5], 3) == 3 + + block fillEmptySeq: + var s = newSeq[int]() + s.fill(0) + + block testBinarySearch: + var noData: seq[int] + doAssert binarySearch(noData, 7) == -1 + let oneData = @[1] + doAssert binarySearch(oneData, 1) == 0 + doAssert binarySearch(onedata, 7) == -1 + let someData = @[1, 3, 4, 7] + doAssert binarySearch(someData, 1) == 0 + doAssert binarySearch(somedata, 7) == 3 + doAssert binarySearch(someData, -1) == -1 + doAssert binarySearch(someData, 5) == -1 + doAssert binarySearch(someData, 13) == -1 + let moreData = @[1, 3, 5, 7, 4711] + doAssert binarySearch(moreData, -1) == -1 + doAssert binarySearch(moreData, 1) == 0 + doAssert binarySearch(moreData, 5) == 2 + doAssert binarySearch(moreData, 6) == -1 + doAssert binarySearch(moreData, 4711) == 4 + doAssert binarySearch(moreData, 4712) == -1 diff --git a/tests/stdlib/tcookies.nim b/tests/stdlib/tcookies.nim new file mode 100644 index 000000000..95b431b19 --- /dev/null +++ b/tests/stdlib/tcookies.nim @@ -0,0 +1,15 @@ +import cookies, times, strtabs + +let expire = fromUnix(0) + 1.seconds + +let theCookies = [ + setCookie("test", "value", expire), + setCookie("test", "value", expire.local), + setCookie("test", "value", expire.utc) +] +let expected = "Set-Cookie: test=value; Expires=Thu, 01 Jan 1970 00:00:01 GMT" +doAssert theCookies == [expected, expected, expected] + +let table = parseCookies("uid=1; kp=2") +doAssert table["uid"] == "1" +doAssert table["kp"] == "2" diff --git a/tests/stdlib/tcritbits.nim b/tests/stdlib/tcritbits.nim new file mode 100644 index 000000000..51cbb7bf2 --- /dev/null +++ b/tests/stdlib/tcritbits.nim @@ -0,0 +1,61 @@ +import sequtils, critbits + + +var r: CritBitTree[void] +r.incl "abc" +r.incl "xyz" +r.incl "def" +r.incl "definition" +r.incl "prefix" +r.incl "foo" + +doAssert r.contains"def" + +r.excl "def" +assert r.missingOrExcl("foo") == false +assert "foo" notin toSeq(r.items) + +assert r.missingOrExcl("foo") == true + +assert toSeq(r.items) == @["abc", "definition", "prefix", "xyz"] + +assert toSeq(r.itemsWithPrefix("de")) == @["definition"] +var c = CritBitTree[int]() + +c.inc("a") +assert c["a"] == 1 + +c.inc("a", 4) +assert c["a"] == 5 + +c.inc("a", -5) +assert c["a"] == 0 + +c.inc("b", 2) +assert c["b"] == 2 + +c.inc("c", 3) +assert c["c"] == 3 + +c.inc("a", 1) +assert c["a"] == 1 + +var cf = CritBitTree[float]() + +cf.incl("a", 1.0) +assert cf["a"] == 1.0 + +cf.incl("b", 2.0) +assert cf["b"] == 2.0 + +cf.incl("c", 3.0) +assert cf["c"] == 3.0 + +assert cf.len == 3 +cf.excl("c") +assert cf.len == 2 + +var cb: CritBitTree[string] +cb.incl("help", "help") +for k in cb.keysWithPrefix("helpp"): + doAssert false, "there is no prefix helpp" diff --git a/tests/stdlib/tenumerate.nim b/tests/stdlib/tenumerate.nim new file mode 100644 index 000000000..e5f21bc15 --- /dev/null +++ b/tests/stdlib/tenumerate.nim @@ -0,0 +1,19 @@ +import std/enumerate + +let a = @[1, 3, 5, 7] + +block: + var res: seq[(int, int)] + for i, x in enumerate(a): + res.add (i, x) + assert res == @[(0, 1), (1, 3), (2, 5), (3, 7)] +block: + var res: seq[(int, int)] + for (i, x) in enumerate(a.items): + res.add (i, x) + assert res == @[(0, 1), (1, 3), (2, 5), (3, 7)] +block: + var res: seq[(int, int)] + for i, x in enumerate(3, a): + res.add (i, x) + assert res == @[(3, 1), (4, 3), (5, 5), (6, 7)] diff --git a/tests/stdlib/tfenv.nim b/tests/stdlib/tfenv.nim new file mode 100644 index 000000000..f58acf1c8 --- /dev/null +++ b/tests/stdlib/tfenv.nim @@ -0,0 +1,8 @@ +import fenv + + +func is_significant(x: float): bool = + if x > minimumPositiveValue(float) and x < maximumPositiveValue(float): true + else: false + +doAssert is_significant(10.0) diff --git a/tests/stdlib/thashes.nim b/tests/stdlib/thashes.nim index 259ced2aa..d155111ff 100644 --- a/tests/stdlib/thashes.nim +++ b/tests/stdlib/thashes.nim @@ -1,34 +1,71 @@ +import hashes -discard """ -output: ''' -[Suite] hashes +block hashes: + block hashing: + var dummy = 0.0 + doAssert hash(dummy) == hash(-dummy) -[Suite] hashing + # "VM and runtime should make the same hash value (hashIdentity)" + block: + const hi123 = hashIdentity(123) + doAssert hashIdentity(123) == hi123 -''' -""" + # "VM and runtime should make the same hash value (hashWangYi1)" + block: + const wy123 = hashWangYi1(123) + doAssert hashWangYi1(123) == wy123 -import unittest, hashes + # "hashIdentity value incorrect at 456" + block: + doAssert hashIdentity(456) == 456 -suite "hashes": - suite "hashing": - test "0.0 and -0.0 should have the same hash value": - var dummy = 0.0 - check hash(dummy) == hash(-dummy) + # "hashWangYi1 value incorrect at 456" + block: + when Hash.sizeof < 8: + doAssert hashWangYi1(456) == 1293320666 + else: + doAssert hashWangYi1(456) == -6421749900419628582 - test "VM and runtime should make the same hash value (hashIdentity)": - const hi123 = hashIdentity(123) - check hashIdentity(123) == hi123 +block empty: + var + a = "" + b = newSeq[char]() + c = newSeq[int]() + d = cstring"" + e = "abcd" + doAssert hash(a) == 0 + doAssert hash(b) == 0 + doAssert hash(c) == 0 + doAssert hash(d) == 0 + doAssert hashIgnoreCase(a) == 0 + doAssert hashIgnoreStyle(a) == 0 + doAssert hash(e, 3, 2) == 0 - test "VM and runtime should make the same hash value (hashWangYi1)": - const wy123 = hashWangYi1(123) - check hashWangYi1(123) == wy123 +block sameButDifferent: + doAssert hash("aa bb aaaa1234") == hash("aa bb aaaa1234", 0, 13) + doAssert hash("aa bb aaaa1234") == hash(cstring"aa bb aaaa1234") + doAssert hashIgnoreCase("aA bb aAAa1234") == hashIgnoreCase("aa bb aaaa1234") + doAssert hashIgnoreStyle("aa_bb_AAaa1234") == hashIgnoreCase("aaBBAAAa1234") - test "hashIdentity value incorrect at 456": - check hashIdentity(456) == 456 +block smallSize: # no multibyte hashing + let + xx = @['H', 'i'] + ii = @[72'u8, 105] + ss = "Hi" + doAssert hash(xx) == hash(ii) + doAssert hash(xx) == hash(ss) + doAssert hash(xx) == hash(xx, 0, xx.high) + doAssert hash(ss) == hash(ss, 0, ss.high) - test "hashWangYi1 value incorrect at 456": - when Hash.sizeof < 8: - check hashWangYi1(456) == 1293320666 - else: - check hashWangYi1(456) == -6421749900419628582 +block largeSize: # longer than 4 characters + let + xx = @['H', 'e', 'l', 'l', 'o'] + xxl = @['H', 'e', 'l', 'l', 'o', 'w', 'e', 'e', 'n', 's'] + ssl = "Helloweens" + doAssert hash(xxl) == hash(ssl) + doAssert hash(xxl) == hash(xxl, 0, xxl.high) + doAssert hash(ssl) == hash(ssl, 0, ssl.high) + doAssert hash(xx) == hash(xxl, 0, 4) + doAssert hash(xx) == hash(ssl, 0, 4) + doAssert hash(xx, 0, 3) == hash(xxl, 0, 3) + doAssert hash(xx, 0, 3) == hash(ssl, 0, 3) diff --git a/tests/stdlib/thttpcore.nim b/tests/stdlib/thttpcore.nim index cb1a0875f..fd5e1d90c 100644 --- a/tests/stdlib/thttpcore.nim +++ b/tests/stdlib/thttpcore.nim @@ -1,19 +1,12 @@ -discard """ -output: "[Suite] httpcore" -""" - -import unittest - import httpcore, strutils -suite "httpcore": - - test "HttpCode": +block: + block HttpCode: assert $Http418 == "418 I'm a teapot" assert Http418.is4xx() == true assert Http418.is2xx() == false - test "headers": + block headers: var h = newHttpHeaders() assert h.len == 0 h.add("Cookie", "foo") @@ -35,7 +28,7 @@ suite "httpcore": assert seq[string](h1["a"]).join(",") == "1,2,3" - test "test cookies with comma": + block test_cookies_with_comma: doAssert parseHeader("cookie: foo, bar") == ("cookie", @["foo, bar"]) doAssert parseHeader("cookie: foo, bar, prologue") == ("cookie", @["foo, bar, prologue"]) doAssert parseHeader("cookie: foo, bar, prologue, starlight") == ("cookie", @["foo, bar, prologue, starlight"]) @@ -52,7 +45,7 @@ suite "httpcore": doAssert parseHeader("Accept: foo, bar, prologue") == (key: "Accept", value: @["foo", "bar", "prologue"]) doAssert parseHeader("Accept: foo, bar, prologue, starlight") == (key: "Accept", value: @["foo", "bar", "prologue", "starlight"]) - test "add empty sequence to HTTP headers": + block add_empty_sequence_to_HTTP_headers: block: var headers = newHttpHeaders() headers["empty"] = @[] @@ -78,3 +71,28 @@ suite "httpcore": headers["existing"] = @[] headers["existing"] = @["true"] doAssert headers.hasKey("existing") + +block: + var test = newHttpHeaders() + test["Connection"] = @["Upgrade", "Close"] + doAssert test["Connection", 0] == "Upgrade" + doAssert test["Connection", 1] == "Close" + test.add("Connection", "Test") + doAssert test["Connection", 2] == "Test" + doAssert "upgrade" in test["Connection"] + + # Bug #5344. + doAssert parseHeader("foobar: ") == ("foobar", @[""]) + let (key, value) = parseHeader("foobar: ") + test = newHttpHeaders() + test[key] = value + doAssert test["foobar"] == "" + + doAssert parseHeader("foobar:") == ("foobar", @[""]) + + block: # test title case + var testTitleCase = newHttpHeaders(titleCase=true) + testTitleCase.add("content-length", "1") + doAssert testTitleCase.hasKey("Content-Length") + for key, val in testTitleCase: + doAssert key == "Content-Length" diff --git a/tests/stdlib/tmath.nim b/tests/stdlib/tmath.nim index 0de09a858..ff1f32d36 100644 --- a/tests/stdlib/tmath.nim +++ b/tests/stdlib/tmath.nim @@ -145,3 +145,129 @@ suite "^": check: compiles(5.5 ^ 2.uint) check: compiles(5.5 ^ 2.uint8) check: not compiles(5.5 ^ 2.2) + +block: + when not defined(js): + # Check for no side effect annotation + proc mySqrt(num: float): float {.noSideEffect.} = + return sqrt(num) + + # check gamma function + assert(gamma(5.0) == 24.0) # 4! + assert(lgamma(1.0) == 0.0) # ln(1.0) == 0.0 + assert(erf(6.0) > erf(5.0)) + assert(erfc(6.0) < erfc(5.0)) + + + # Function for approximate comparison of floats + proc `==~`(x, y: float): bool = (abs(x-y) < 1e-9) + + block: # prod + doAssert prod([1, 2, 3, 4]) == 24 + doAssert prod([1.5, 3.4]) == 5.1 + let x: seq[float] = @[] + doAssert prod(x) == 1.0 + + block: # round() tests + # Round to 0 decimal places + doAssert round(54.652) ==~ 55.0 + doAssert round(54.352) ==~ 54.0 + doAssert round(-54.652) ==~ -55.0 + doAssert round(-54.352) ==~ -54.0 + doAssert round(0.0) ==~ 0.0 + + block: # splitDecimal() tests + doAssert splitDecimal(54.674).intpart ==~ 54.0 + doAssert splitDecimal(54.674).floatpart ==~ 0.674 + doAssert splitDecimal(-693.4356).intpart ==~ -693.0 + doAssert splitDecimal(-693.4356).floatpart ==~ -0.4356 + doAssert splitDecimal(0.0).intpart ==~ 0.0 + doAssert splitDecimal(0.0).floatpart ==~ 0.0 + + block: # trunc tests for vcc + doAssert(trunc(-1.1) == -1) + doAssert(trunc(1.1) == 1) + doAssert(trunc(-0.1) == -0) + doAssert(trunc(0.1) == 0) + + #special case + doAssert(classify(trunc(1e1000000)) == fcInf) + doAssert(classify(trunc(-1e1000000)) == fcNegInf) + doAssert(classify(trunc(0.0/0.0)) == fcNan) + doAssert(classify(trunc(0.0)) == fcZero) + + #trick the compiler to produce signed zero + let + f_neg_one = -1.0 + f_zero = 0.0 + f_nan = f_zero / f_zero + + doAssert(classify(trunc(f_neg_one*f_zero)) == fcNegZero) + + doAssert(trunc(-1.1'f32) == -1) + doAssert(trunc(1.1'f32) == 1) + doAssert(trunc(-0.1'f32) == -0) + doAssert(trunc(0.1'f32) == 0) + doAssert(classify(trunc(1e1000000'f32)) == fcInf) + doAssert(classify(trunc(-1e1000000'f32)) == fcNegInf) + doAssert(classify(trunc(f_nan.float32)) == fcNan) + doAssert(classify(trunc(0.0'f32)) == fcZero) + + block: # sgn() tests + assert sgn(1'i8) == 1 + assert sgn(1'i16) == 1 + assert sgn(1'i32) == 1 + assert sgn(1'i64) == 1 + assert sgn(1'u8) == 1 + assert sgn(1'u16) == 1 + assert sgn(1'u32) == 1 + assert sgn(1'u64) == 1 + assert sgn(-12342.8844'f32) == -1 + assert sgn(123.9834'f64) == 1 + assert sgn(0'i32) == 0 + assert sgn(0'f32) == 0 + assert sgn(NegInf) == -1 + assert sgn(Inf) == 1 + assert sgn(NaN) == 0 + + block: # fac() tests + try: + discard fac(-1) + except AssertionDefect: + discard + + doAssert fac(0) == 1 + doAssert fac(1) == 1 + doAssert fac(2) == 2 + doAssert fac(3) == 6 + doAssert fac(4) == 24 + + block: # floorMod/floorDiv + doAssert floorDiv(8, 3) == 2 + doAssert floorMod(8, 3) == 2 + + doAssert floorDiv(8, -3) == -3 + doAssert floorMod(8, -3) == -1 + + doAssert floorDiv(-8, 3) == -3 + doAssert floorMod(-8, 3) == 1 + + doAssert floorDiv(-8, -3) == 2 + doAssert floorMod(-8, -3) == -2 + + doAssert floorMod(8.0, -3.0) ==~ -1.0 + doAssert floorMod(-8.5, 3.0) ==~ 0.5 + + block: # log + doAssert log(4.0, 3.0) ==~ ln(4.0) / ln(3.0) + doAssert log2(8.0'f64) == 3.0'f64 + doAssert log2(4.0'f64) == 2.0'f64 + doAssert log2(2.0'f64) == 1.0'f64 + doAssert log2(1.0'f64) == 0.0'f64 + doAssert classify(log2(0.0'f64)) == fcNegInf + + doAssert log2(8.0'f32) == 3.0'f32 + doAssert log2(4.0'f32) == 2.0'f32 + doAssert log2(2.0'f32) == 1.0'f32 + doAssert log2(1.0'f32) == 0.0'f32 + doAssert classify(log2(0.0'f32)) == fcNegInf diff --git a/tests/stdlib/tmd5.nim b/tests/stdlib/tmd5.nim new file mode 100644 index 000000000..736fa05a7 --- /dev/null +++ b/tests/stdlib/tmd5.nim @@ -0,0 +1,7 @@ +import md5 + +assert(getMD5("Franz jagt im komplett verwahrlosten Taxi quer durch Bayern") == + "a3cca2b2aa1e3b5b3b5aad99a8529074") +assert(getMD5("Frank jagt im komplett verwahrlosten Taxi quer durch Bayern") == + "7e716d0e702df0505fc72e2b89467910") +assert($toMD5("") == "d41d8cd98f00b204e9800998ecf8427e") diff --git a/tests/stdlib/tmonotimes.nim b/tests/stdlib/tmonotimes.nim new file mode 100644 index 000000000..cf25ef025 --- /dev/null +++ b/tests/stdlib/tmonotimes.nim @@ -0,0 +1,16 @@ +import std/monotimes, times + +let d = initDuration(nanoseconds = 10) +let t1 = getMonoTime() +let t2 = t1 + d + +doAssert t2 - t1 == d +doAssert t1 == t1 +doAssert t1 != t2 +doAssert t2 - d == t1 +doAssert t1 < t2 +doAssert t1 <= t2 +doAssert t1 <= t1 +doAssert not(t2 < t1) +doAssert t1 < high(MonoTime) +doAssert low(MonoTime) < t1 diff --git a/tests/stdlib/toptions.nim b/tests/stdlib/toptions.nim index b982d747d..04544ffb7 100644 --- a/tests/stdlib/toptions.nim +++ b/tests/stdlib/toptions.nim @@ -13,4 +13,146 @@ type let js = """{"foo": {"test": "123"}}""" let parsed = parseJson(js) let a = parsed.to(Test) -echo $(%*a) \ No newline at end of file +echo $(%*a) + + +# RefPerson is used to test that overloaded `==` operator is not called by +# options. It is defined here in the global scope, because otherwise the test +# will not even consider the `==` operator. Different bug? +type RefPerson = ref object + name: string + +proc `==`(a, b: RefPerson): bool = + assert(not a.isNil and not b.isNil) + a.name == b.name + +block options: + # work around a bug in unittest + let intNone = none(int) + let stringNone = none(string) + + block example: + proc find(haystack: string, needle: char): Option[int] = + for i, c in haystack: + if c == needle: + return some i + + doAssert("abc".find('c').get() == 2) + + let result = "team".find('i') + + doAssert result == intNone + doAssert result.isNone + + block some: + doAssert some(6).get() == 6 + doAssert some("a").unsafeGet() == "a" + doAssert some(6).isSome + doAssert some("a").isSome + + block none: + doAssertRaises UnpackDefect: + discard none(int).get() + doAssert(none(int).isNone) + doAssert(not none(string).isSome) + + block equality: + doAssert some("a") == some("a") + doAssert some(7) != some(6) + doAssert some("a") != stringNone + doAssert intNone == intNone + + when compiles(some("a") == some(5)): + doAssert false + when compiles(none(string) == none(int)): + doAssert false + + block get_with_a_default_value: + doAssert(some("Correct").get("Wrong") == "Correct") + doAssert(stringNone.get("Correct") == "Correct") + + block stringify: + doAssert($(some("Correct")) == "Some(\"Correct\")") + doAssert($(stringNone) == "None[string]") + + block map_with_a_void_result: + var procRan = 0 + some(123).map(proc (v: int) = procRan = v) + doAssert procRan == 123 + intNone.map(proc (v: int) = doAssert false) + + block map: + doAssert(some(123).map(proc (v: int): int = v * 2) == some(246)) + doAssert(intNone.map(proc (v: int): int = v * 2).isNone) + + block filter: + doAssert(some(123).filter(proc (v: int): bool = v == 123) == some(123)) + doAssert(some(456).filter(proc (v: int): bool = v == 123).isNone) + doAssert(intNone.filter(proc (v: int): bool = doAssert false).isNone) + + block flatMap: + proc addOneIfNotZero(v: int): Option[int] = + if v != 0: + result = some(v + 1) + else: + result = none(int) + + doAssert(some(1).flatMap(addOneIfNotZero) == some(2)) + doAssert(some(0).flatMap(addOneIfNotZero) == none(int)) + doAssert(some(1).flatMap(addOneIfNotZero).flatMap(addOneIfNotZero) == some(3)) + + proc maybeToString(v: int): Option[string] = + if v != 0: + result = some($v) + else: + result = none(string) + + doAssert(some(1).flatMap(maybeToString) == some("1")) + + proc maybeExclaim(v: string): Option[string] = + if v != "": + result = some v & "!" + else: + result = none(string) + + doAssert(some(1).flatMap(maybeToString).flatMap(maybeExclaim) == some("1!")) + doAssert(some(0).flatMap(maybeToString).flatMap(maybeExclaim) == none(string)) + + block SomePointer: + var intref: ref int + doAssert(option(intref).isNone) + intref.new + doAssert(option(intref).isSome) + + let tmp = option(intref) + doAssert(sizeof(tmp) == sizeof(ptr int)) + + var prc = proc (x: int): int = x + 1 + doAssert(option(prc).isSome) + prc = nil + doAssert(option(prc).isNone) + + block: + doAssert(none[int]().isNone) + doAssert(none(int) == none[int]()) + + # "$ on typed with .name" + block: + type Named = object + name: string + + let nobody = none(Named) + doAssert($nobody == "None[Named]") + + # "$ on type with name()" + block: + type Person = object + myname: string + + let noperson = none(Person) + doAssert($noperson == "None[Person]") + + # "Ref type with overloaded `==`" + block: + let p = some(RefPerson.new()) + doAssert p.isSome diff --git a/tests/stdlib/tparsecsv.nim b/tests/stdlib/tparsecsv.nim new file mode 100644 index 000000000..0d004d45d --- /dev/null +++ b/tests/stdlib/tparsecsv.nim @@ -0,0 +1,31 @@ +include parsecsv +import strutils, os + +block: # Tests for reading the header row + let content = "\nOne,Two,Three,Four\n1,2,3,4\n10,20,30,40,\n100,200,300,400\n" + writeFile("temp.csv", content) + + var p: CsvParser + p.open("temp.csv") + p.readHeaderRow() + while p.readRow(): + let zeros = repeat('0', p.currRow-2) + doAssert p.rowEntry("One") == "1" & zeros + doAssert p.rowEntry("Two") == "2" & zeros + doAssert p.rowEntry("Three") == "3" & zeros + doAssert p.rowEntry("Four") == "4" & zeros + p.close() + + when not defined(testing): + var parser: CsvParser + parser.open("temp.csv") + parser.readHeaderRow() + while parser.readRow(): + echo "new row: " + for col in items(parser.headers): + echo "##", col, ":", parser.rowEntry(col), "##" + parser.close() + removeFile("temp.csv") + + # Tidy up + removeFile("temp.csv") diff --git a/tests/stdlib/tparseutils.nim b/tests/stdlib/tparseutils.nim new file mode 100644 index 000000000..3bc54dff1 --- /dev/null +++ b/tests/stdlib/tparseutils.nim @@ -0,0 +1,43 @@ +import parseutils +import sequtils + +let input = "$test{} $this is ${an{ example}} " +let expected = @[(ikVar, "test"), (ikStr, "{} "), (ikVar, "this"), + (ikStr, " is "), (ikExpr, "an{ example}"), (ikStr, " ")] +doAssert toSeq(interpolatedFragments(input)) == expected + +var value = 0 +discard parseHex("0x38", value) +doAssert value == 56 + +value = -1 +doAssert(parseSaturatedNatural("848", value) == 3) +doAssert value == 848 + +value = -1 +discard parseSaturatedNatural("84899999999999999999324234243143142342135435342532453", value) +doAssert value == high(int) + +value = -1 +discard parseSaturatedNatural("9223372036854775808", value) +doAssert value == high(int) + +value = -1 +discard parseSaturatedNatural("9223372036854775807", value) +doAssert value == high(int) + +value = -1 +discard parseSaturatedNatural("18446744073709551616", value) +doAssert value == high(int) + +value = -1 +discard parseSaturatedNatural("18446744073709551615", value) +doAssert value == high(int) + +value = -1 +doAssert(parseSaturatedNatural("1_000_000", value) == 9) +doAssert value == 1_000_000 + +var i64Value: int64 +discard parseBiggestInt("9223372036854775807", i64Value) +doAssert i64Value == 9223372036854775807 diff --git a/tests/stdlib/tpunycode.nim b/tests/stdlib/tpunycode.nim new file mode 100644 index 000000000..998bd3bdf --- /dev/null +++ b/tests/stdlib/tpunycode.nim @@ -0,0 +1,5 @@ +import punycode + +assert(decode(encode("", "bücher")) == "bücher") +assert(decode(encode("münchen")) == "münchen") +assert encode("xn--", "münchen") == "xn--mnchen-3ya" diff --git a/tests/stdlib/trandom.nim b/tests/stdlib/trandom.nim new file mode 100644 index 000000000..c7c6a3f56 --- /dev/null +++ b/tests/stdlib/trandom.nim @@ -0,0 +1,59 @@ +discard """ + action: compile +""" + +import random + +proc main = + var occur: array[1000, int] + + var x = 8234 + for i in 0..100_000: + x = rand(high(occur)) + inc occur[x] + for i, oc in occur: + if oc < 69: + doAssert false, "too few occurrences of " & $i + elif oc > 150: + doAssert false, "too many occurrences of " & $i + + when false: + var rs: RunningStat + for j in 1..5: + for i in 1 .. 1_000: + rs.push(gauss()) + echo("mean: ", rs.mean, + " stdDev: ", rs.standardDeviation(), + " min: ", rs.min, + " max: ", rs.max) + rs.clear() + + var a = [0, 1] + shuffle(a) + doAssert a[0] == 1 + doAssert a[1] == 0 + + doAssert rand(0) == 0 + doAssert sample("a") == 'a' + + when compileOption("rangeChecks"): + try: + discard rand(-1) + doAssert false + except RangeDefect: + discard + + try: + discard rand(-1.0) + doAssert false + except RangeDefect: + discard + + + # don't use causes integer overflow + doAssert compiles(rand[int](low(int) .. high(int))) + +randomize(223) + +for i in 0 .. 10: + main() diff --git a/tests/stdlib/trationals.nim b/tests/stdlib/trationals.nim new file mode 100644 index 000000000..17238af07 --- /dev/null +++ b/tests/stdlib/trationals.nim @@ -0,0 +1,98 @@ +import rationals, math + + +var + z = Rational[int](num: 0, den: 1) + o = initRational(num = 1, den = 1) + a = initRational(1, 2) + b = -1 // -2 + m1 = -1 // 1 + tt = 10 // 2 + +assert(a == a) +assert( (a-a) == z) +assert( (a+b) == o) +assert( (a/b) == o) +assert( (a*b) == 1 // 4) +assert( (3/a) == 6 // 1) +assert( (a/3) == 1 // 6) +assert(a*b == 1 // 4) +assert(tt*z == z) +assert(10*a == tt) +assert(a*10 == tt) +assert(tt/10 == a) +assert(a-m1 == 3 // 2) +assert(a+m1 == -1 // 2) +assert(m1+tt == 16 // 4) +assert(m1-tt == 6 // -1) + +assert(z < o) +assert(z <= o) +assert(z == z) +assert(cmp(z, o) < 0) +assert(cmp(o, z) > 0) + +assert(o == o) +assert(o >= o) +assert(not(o > o)) +assert(cmp(o, o) == 0) +assert(cmp(z, z) == 0) +assert(hash(o) == hash(o)) + +assert(a == b) +assert(a >= b) +assert(not(b > a)) +assert(cmp(a, b) == 0) +assert(hash(a) == hash(b)) + +var x = 1//3 + +x *= 5//1 +assert(x == 5//3) +x += 2 // 9 +assert(x == 17//9) +x -= 9//18 +assert(x == 25//18) +x /= 1//2 +assert(x == 50//18) + +var y = 1//3 + +y *= 4 +assert(y == 4//3) +y += 5 +assert(y == 19//3) +y -= 2 +assert(y == 13//3) +y /= 9 +assert(y == 13//27) + +assert toRational(5) == 5//1 +assert abs(toFloat(y) - 0.4814814814814815) < 1.0e-7 +assert toInt(z) == 0 + +when sizeof(int) == 8: + assert toRational(0.98765432) == 2111111029 // 2137499919 + assert toRational(PI) == 817696623 // 260280919 +when sizeof(int) == 4: + assert toRational(0.98765432) == 80 // 81 + assert toRational(PI) == 355 // 113 + +assert toRational(0.1) == 1 // 10 +assert toRational(0.9) == 9 // 10 + +assert toRational(0.0) == 0 // 1 +assert toRational(-0.25) == 1 // -4 +assert toRational(3.2) == 16 // 5 +assert toRational(0.33) == 33 // 100 +assert toRational(0.22) == 11 // 50 +assert toRational(10.0) == 10 // 1 + +assert (1//1) div (3//10) == 3 +assert (-1//1) div (3//10) == -3 +assert (3//10) mod (1//1) == 3//10 +assert (-3//10) mod (1//1) == -3//10 +assert floorDiv(1//1, 3//10) == 3 +assert floorDiv(-1//1, 3//10) == -4 +assert floorMod(3//10, 1//1) == 3//10 +assert floorMod(-3//10, 1//1) == 7//10 diff --git a/tests/stdlib/tsha1.nim b/tests/stdlib/tsha1.nim new file mode 100644 index 000000000..7e67ccaf6 --- /dev/null +++ b/tests/stdlib/tsha1.nim @@ -0,0 +1,13 @@ +import std/sha1 + +let hash1 = secureHash("a93tgj0p34jagp9[agjp98ajrhp9aej]") +doAssert hash1 == hash1 +doAssert parseSecureHash($hash1) == hash1 + +template checkVector(s, exp: string) = + doAssert secureHash(s) == parseSecureHash(exp) + +checkVector("", "da39a3ee5e6b4b0d3255bfef95601890afd80709") +checkVector("abc", "a9993e364706816aba3e25717850c26c9cd0d89d") +checkVector("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", + "84983e441c3bd26ebaae4aa1f95129e5e54670f1") diff --git a/tests/stdlib/tstats.nim b/tests/stdlib/tstats.nim new file mode 100644 index 000000000..37240c884 --- /dev/null +++ b/tests/stdlib/tstats.nim @@ -0,0 +1,46 @@ +include stats + +proc clean(x: float): float = + result = round(1.0e8*x).float * 1.0e-8 + +var rs: RunningStat +rs.push(@[1.0, 2.0, 1.0, 4.0, 1.0, 4.0, 1.0, 2.0]) +doAssert(rs.n == 8) +doAssert(clean(rs.mean) == 2.0) +doAssert(clean(rs.variance()) == 1.5) +doAssert(clean(rs.varianceS()) == 1.71428571) +doAssert(clean(rs.skewness()) == 0.81649658) +doAssert(clean(rs.skewnessS()) == 1.01835015) +doAssert(clean(rs.kurtosis()) == -1.0) +doAssert(clean(rs.kurtosisS()) == -0.7000000000000001) + +var rs1, rs2: RunningStat +rs1.push(@[1.0, 2.0, 1.0, 4.0]) +rs2.push(@[1.0, 4.0, 1.0, 2.0]) +let rs3 = rs1 + rs2 +doAssert(clean(rs3.mom2) == clean(rs.mom2)) +doAssert(clean(rs3.mom3) == clean(rs.mom3)) +doAssert(clean(rs3.mom4) == clean(rs.mom4)) +rs1 += rs2 +doAssert(clean(rs1.mom2) == clean(rs.mom2)) +doAssert(clean(rs1.mom3) == clean(rs.mom3)) +doAssert(clean(rs1.mom4) == clean(rs.mom4)) +rs1.clear() +rs1.push(@[1.0, 2.2, 1.4, 4.9]) +doAssert(rs1.sum == 9.5) +doAssert(rs1.mean() == 2.375) + +when not defined(cpu32): + # XXX For some reason on 32bit CPUs these results differ + var rr: RunningRegress + rr.push(@[0.0, 1.0, 2.8, 3.0, 4.0], @[0.0, 1.0, 2.3, 3.0, 4.0]) + doAssert(rr.slope() == 0.9695585996955861) + doAssert(rr.intercept() == -0.03424657534246611) + doAssert(rr.correlation() == 0.9905100362239381) + var rr1, rr2: RunningRegress + rr1.push(@[0.0, 1.0], @[0.0, 1.0]) + rr2.push(@[2.8, 3.0, 4.0], @[2.3, 3.0, 4.0]) + let rr3 = rr1 + rr2 + doAssert(rr3.correlation() == rr.correlation()) + doAssert(clean(rr3.slope()) == clean(rr.slope())) + doAssert(clean(rr3.intercept()) == clean(rr.intercept())) \ No newline at end of file diff --git a/tests/stdlib/tstreams.nim b/tests/stdlib/tstreams.nim index 0fe6b6933..e0310b5f5 100644 --- a/tests/stdlib/tstreams.nim +++ b/tests/stdlib/tstreams.nim @@ -61,3 +61,17 @@ doAssert a.readDataStr(buffer, 1..3) == 3 echo buffer + +block: + var ss = newStringStream("The quick brown fox jumped over the lazy dog.\nThe lazy dog ran") + assert(ss.getPosition == 0) + assert(ss.peekStr(5) == "The q") + assert(ss.getPosition == 0) # haven't moved + assert(ss.readStr(5) == "The q") + assert(ss.getPosition == 5) # did move + assert(ss.peekLine() == "uick brown fox jumped over the lazy dog.") + assert(ss.getPosition == 5) # haven't moved + var str = newString(100) + assert(ss.peekLine(str)) + assert(str == "uick brown fox jumped over the lazy dog.") + assert(ss.getPosition == 5) # haven't moved diff --git a/tests/stdlib/tstrformat.nim b/tests/stdlib/tstrformat.nim index b1f3340a4..d483093a7 100644 --- a/tests/stdlib/tstrformat.nim +++ b/tests/stdlib/tstrformat.nim @@ -6,7 +6,7 @@ output: '''Received (name: "Foo", species: "Bar")''' # issue #7632 import genericstrformat -import strutils +import strutils, times doAssert works(5) == "formatted 5" @@ -376,3 +376,140 @@ block: doAssert fmt"{x.foo(y) =}" == "x.foo(y) =18" doAssert fmt"{x.foo(y)= }" == "x.foo(y)= 18" doAssert fmt"{x.foo(y) = }" == "x.foo(y) = 18" + +block: + template check(actual, expected: string) = + doAssert actual == expected + + # Basic tests + let s = "string" + check &"{0} {s}", "0 string" + check &"{s[0..2].toUpperAscii}", "STR" + check &"{-10:04}", "-010" + check &"{-10:<04}", "-010" + check &"{-10:>04}", "-010" + check &"0x{10:02X}", "0x0A" + + check &"{10:#04X}", "0x0A" + + check &"""{"test":#>5}""", "#test" + check &"""{"test":>5}""", " test" + + check &"""{"test":#^7}""", "#test##" + + check &"""{"test": <5}""", "test " + check &"""{"test":<5}""", "test " + check &"{1f:.3f}", "1.000" + check &"Hello, {s}!", "Hello, string!" + + # Tests for identifiers without parenthesis + check &"{s} works{s}", "string worksstring" + check &"{s:>7}", " string" + doAssert(not compiles(&"{s_works}")) # parsed as identifier `s_works` + + # Misc general tests + check &"{{}}", "{}" + check &"{0}%", "0%" + check &"{0}%asdf", "0%asdf" + check &("\n{\"\\n\"}\n"), "\n\n\n" + check &"""{"abc"}s""", "abcs" + + # String tests + check &"""{"abc"}""", "abc" + check &"""{"abc":>4}""", " abc" + check &"""{"abc":<4}""", "abc " + check &"""{"":>4}""", " " + check &"""{"":<4}""", " " + + # Int tests + check &"{12345}", "12345" + check &"{ - 12345}", "-12345" + check &"{12345:6}", " 12345" + check &"{12345:>6}", " 12345" + check &"{12345:4}", "12345" + check &"{12345:08}", "00012345" + check &"{-12345:08}", "-0012345" + check &"{0:0}", "0" + check &"{0:02}", "00" + check &"{-1:3}", " -1" + check &"{-1:03}", "-01" + check &"{10}", "10" + check &"{16:#X}", "0x10" + check &"{16:^#7X}", " 0x10 " + check &"{16:^+#7X}", " +0x10 " + + # Hex tests + check &"{0:x}", "0" + check &"{-0:x}", "0" + check &"{255:x}", "ff" + check &"{255:X}", "FF" + check &"{-255:x}", "-ff" + check &"{-255:X}", "-FF" + check &"{255:x} uNaffeCteD CaSe", "ff uNaffeCteD CaSe" + check &"{255:X} uNaffeCteD CaSe", "FF uNaffeCteD CaSe" + check &"{255:4x}", " ff" + check &"{255:04x}", "00ff" + check &"{-255:4x}", " -ff" + check &"{-255:04x}", "-0ff" + + # Float tests + check &"{123.456}", "123.456" + check &"{-123.456}", "-123.456" + check &"{123.456:.3f}", "123.456" + check &"{123.456:+.3f}", "+123.456" + check &"{-123.456:+.3f}", "-123.456" + check &"{-123.456:.3f}", "-123.456" + check &"{123.456:1g}", "123.456" + check &"{123.456:.1f}", "123.5" + check &"{123.456:.0f}", "123." + check &"{123.456:>9.3f}", " 123.456" + check &"{123.456:9.3f}", " 123.456" + check &"{123.456:>9.4f}", " 123.4560" + check &"{123.456:>9.0f}", " 123." + check &"{123.456:<9.4f}", "123.4560 " + + # Float (scientific) tests + check &"{123.456:e}", "1.234560e+02" + check &"{123.456:>13e}", " 1.234560e+02" + check &"{123.456:<13e}", "1.234560e+02 " + check &"{123.456:.1e}", "1.2e+02" + check &"{123.456:.2e}", "1.23e+02" + check &"{123.456:.3e}", "1.235e+02" + + # Note: times.format adheres to the format protocol. Test that this + # works: + + var dt = initDateTime(01, mJan, 2000, 00, 00, 00) + check &"{dt:yyyy-MM-dd}", "2000-01-01" + + var tm = fromUnix(0) + discard &"{tm}" + + var noww = now() + check &"{noww}", $noww + + # Unicode string tests + check &"""{"αβγ"}""", "αβγ" + check &"""{"αβγ":>5}""", " αβγ" + check &"""{"αβγ":<5}""", "αβγ " + check &"""a{"a"}α{"α"}€{"€"}𐍈{"𐍈"}""", "aaαα€€𐍈𐍈" + check &"""a{"a":2}α{"α":2}€{"€":2}𐍈{"𐍈":2}""", "aa αα €€ 𐍈𐍈 " + # Invalid unicode sequences should be handled as plain strings. + # Invalid examples taken from: https://stackoverflow.com/a/3886015/1804173 + let invalidUtf8 = [ + "\xc3\x28", "\xa0\xa1", + "\xe2\x28\xa1", "\xe2\x82\x28", + "\xf0\x28\x8c\xbc", "\xf0\x90\x28\xbc", "\xf0\x28\x8c\x28" + ] + for s in invalidUtf8: + check &"{s:>5}", repeat(" ", 5-s.len) & s + + # bug #11089 + let flfoo: float = 1.0 + check &"{flfoo}", "1.0" + + # bug #11092 + check &"{high(int64)}", "9223372036854775807" + check &"{low(int64)}", "-9223372036854775808" + + doAssert fmt"{'a'} {'b'}" == "a b" \ No newline at end of file diff --git a/tests/stdlib/tstrmiscs.nim b/tests/stdlib/tstrmiscs.nim new file mode 100644 index 000000000..2e9131ff8 --- /dev/null +++ b/tests/stdlib/tstrmiscs.nim @@ -0,0 +1,22 @@ +import strmisc + + +doAssert expandTabs("\t", 4) == " " +doAssert expandTabs("\tfoo\t", 4) == " foo " +doAssert expandTabs("\tfoo\tbar", 4) == " foo bar" +doAssert expandTabs("\tfoo\tbar\t", 4) == " foo bar " +doAssert expandTabs("", 4) == "" +doAssert expandTabs("", 0) == "" +doAssert expandTabs("\t\t\t", 0) == "" + +doAssert partition("foo:bar", ":") == ("foo", ":", "bar") +doAssert partition("foobarbar", "bar") == ("foo", "bar", "bar") +doAssert partition("foobarbar", "bank") == ("foobarbar", "", "") +doAssert partition("foobarbar", "foo") == ("", "foo", "barbar") +doAssert partition("foofoobar", "bar") == ("foofoo", "bar", "") + +doAssert rpartition("foo:bar", ":") == ("foo", ":", "bar") +doAssert rpartition("foobarbar", "bar") == ("foobar", "bar", "") +doAssert rpartition("foobarbar", "bank") == ("", "", "foobarbar") +doAssert rpartition("foobarbar", "foo") == ("", "foo", "barbar") +doAssert rpartition("foofoobar", "bar") == ("foofoo", "bar", "") diff --git a/tests/stdlib/tstrscans.nim b/tests/stdlib/tstrscans.nim index 6f952368c..9d6f51025 100644 --- a/tests/stdlib/tstrscans.nim +++ b/tests/stdlib/tstrscans.nim @@ -98,3 +98,115 @@ block issue15064: doAssert scanf(" ", "<$+> $*", nick3, msg3) doAssert nick3 == "abcd" doAssert msg3 == "" + + +block: + proc twoDigits(input: string; x: var int; start: int): int = + if start+1 < input.len and input[start] == '0' and input[start+1] == '0': + result = 2 + x = 13 + else: + result = 0 + + proc someSep(input: string; start: int; seps: set[char] = {';', ',', '-', '.'}): int = + result = 0 + while start+result < input.len and input[start+result] in seps: inc result + + proc demangle(s: string; res: var string; start: int): int = + while result+start < s.len and s[result+start] in {'_', '@'}: inc result + res = "" + while result+start < s.len and s[result+start] > ' ' and s[result+start] != '_': + res.add s[result+start] + inc result + while result+start < s.len and s[result+start] > ' ': + inc result + + proc parseGDB(resp: string): seq[string] = + const + digits = {'0'..'9'} + hexdigits = digits + {'a'..'f', 'A'..'F'} + whites = {' ', '\t', '\C', '\L'} + result = @[] + var idx = 0 + while true: + var prc = "" + var info = "" + if scanp(resp, idx, *`whites`, '#', *`digits`, +`whites`, ?("0x", *`hexdigits`, " in "), + demangle($input, prc, $index), *`whites`, '(', * ~ ')', ')', + *`whites`, "at ", +(~{'\C', '\L'} -> info.add($_))): + result.add prc & " " & info + else: + break + + var key, val: string + var intval: int + var floatval: float + doAssert scanf("abc:: xyz 89 33.25", "$w$s::$s$w$s$i $f", key, val, intval, floatVal) + doAssert key == "abc" + doAssert val == "xyz" + doAssert intval == 89 + doAssert floatVal == 33.25 + + var binval: int + var octval: int + var hexval: int + doAssert scanf("0b0101 0o1234 0xabcd", "$b$s$o$s$h", binval, octval, hexval) + doAssert binval == 0b0101 + doAssert octval == 0o1234 + doAssert hexval == 0xabcd + + let xx = scanf("$abc", "$$$i", intval) + doAssert xx == false + + + let xx2 = scanf("$1234", "$$$i", intval) + doAssert xx2 + + let yy = scanf(";.--Breakpoint00 [output]", + "$[someSep]Breakpoint${twoDigits}$[someSep({';','.','-'})] [$+]$.", + intVal, key) + doAssert yy + doAssert key == "output" + doAssert intVal == 13 + + var ident = "" + var idx = 0 + let zz = scanp("foobar x x x xWZ", idx, +{'a'..'z'} -> add(ident, $_), *(*{ + ' ', '\t'}, "x"), ~'U', "Z") + doAssert zz + doAssert ident == "foobar" + + const digits = {'0'..'9'} + var year = 0 + var idx2 = 0 + if scanp("201655-8-9", idx2, `digits`{4, 6} -> (year = year * 10 + ord($_) - + ord('0')), "-8", "-9"): + doAssert year == 201655 + + const gdbOut = """ + #0 @foo_96013_1208911747@8 (x0=...) + at c:/users/anwender/projects/nim/temp.nim:11 + #1 0x00417754 in tempInit000 () at c:/users/anwender/projects/nim/temp.nim:13 + #2 0x0041768d in NimMainInner () + at c:/users/anwender/projects/nim/lib/system.nim:2605 + #3 0x004176b1 in NimMain () + at c:/users/anwender/projects/nim/lib/system.nim:2613 + #4 0x004176db in main (argc=1, args=0x712cc8, env=0x711ca8) + at c:/users/anwender/projects/nim/lib/system.nim:2620""" + const result = @["foo c:/users/anwender/projects/nim/temp.nim:11", + "tempInit000 c:/users/anwender/projects/nim/temp.nim:13", + "NimMainInner c:/users/anwender/projects/nim/lib/system.nim:2605", + "NimMain c:/users/anwender/projects/nim/lib/system.nim:2613", + "main c:/users/anwender/projects/nim/lib/system.nim:2620"] + doAssert parseGDB(gdbOut) == result + + # bug #6487 + var count = 0 + + proc test(): string = + inc count + result = ",123123" + + var a: int + discard scanf(test(), ",$i", a) + doAssert count == 1 diff --git a/tests/stdlib/tstrtabs.nim b/tests/stdlib/tstrtabs.nim index 5d7e8c762..d4344f95f 100644 --- a/tests/stdlib/tstrtabs.nim +++ b/tests/stdlib/tstrtabs.nim @@ -102,3 +102,15 @@ writeLine(stdout, "length of table ", $tab.len) writeLine(stdout, `%`("$key1 = $key2", tab, {useEnvironment})) tab.clear writeLine(stdout, "length of table ", $tab.len) + +block: + var x = {"k": "v", "11": "22", "565": "67"}.newStringTable + assert x["k"] == "v" + assert x["11"] == "22" + assert x["565"] == "67" + x["11"] = "23" + assert x["11"] == "23" + + x.clear(modeCaseInsensitive) + x["11"] = "22" + assert x["11"] == "22" diff --git a/tests/stdlib/tstrutil.nim b/tests/stdlib/tstrutil.nim index 162b8ea1b..149d9bb87 100644 --- a/tests/stdlib/tstrutil.nim +++ b/tests/stdlib/tstrutil.nim @@ -457,3 +457,220 @@ hey assert 0 == indentation "" assert 0 == indentation " \n \n" assert 0 == indentation " " + + +block: + proc nonStaticTests = + doAssert formatBiggestFloat(1234.567, ffDecimal, -1) == "1234.567000" + when not defined(js): + doAssert formatBiggestFloat(1234.567, ffDecimal, 0) == "1235." # bugs 8242, 12586 + doAssert formatBiggestFloat(1234.567, ffDecimal, 1) == "1234.6" + doAssert formatBiggestFloat(0.00000000001, ffDecimal, 11) == "0.00000000001" + doAssert formatBiggestFloat(0.00000000001, ffScientific, 1, ',') in + ["1,0e-11", "1,0e-011"] + # bug #6589 + when not defined(js): + doAssert formatFloat(123.456, ffScientific, precision = -1) == "1.234560e+02" + + doAssert "$# $3 $# $#" % ["a", "b", "c"] == "a c b c" + doAssert "${1}12 ${-1}$2" % ["a", "b"] == "a12 bb" + + block: # formatSize tests + when not defined(js): + doAssert formatSize((1'i64 shl 31) + (300'i64 shl 20)) == "2.293GiB" # <=== bug #8231 + doAssert formatSize((2.234*1024*1024).int) == "2.234MiB" + doAssert formatSize(4096) == "4KiB" + doAssert formatSize(4096, prefix = bpColloquial, includeSpace = true) == "4 kB" + doAssert formatSize(4096, includeSpace = true) == "4 KiB" + doAssert formatSize(5_378_934, prefix = bpColloquial, decimalSep = ',') == "5,13MB" + + block: # formatEng tests + doAssert formatEng(0, 2, trim = false) == "0.00" + doAssert formatEng(0, 2) == "0" + doAssert formatEng(53, 2, trim = false) == "53.00" + doAssert formatEng(0.053, 2, trim = false) == "53.00e-3" + doAssert formatEng(0.053, 4, trim = false) == "53.0000e-3" + doAssert formatEng(0.053, 4, trim = true) == "53e-3" + doAssert formatEng(0.053, 0) == "53e-3" + doAssert formatEng(52731234) == "52.731234e6" + doAssert formatEng(-52731234) == "-52.731234e6" + doAssert formatEng(52731234, 1) == "52.7e6" + doAssert formatEng(-52731234, 1) == "-52.7e6" + doAssert formatEng(52731234, 1, decimalSep = ',') == "52,7e6" + doAssert formatEng(-52731234, 1, decimalSep = ',') == "-52,7e6" + + doAssert formatEng(4100, siPrefix = true, unit = "V") == "4.1 kV" + doAssert formatEng(4.1, siPrefix = true, unit = "V", + useUnitSpace = true) == "4.1 V" + doAssert formatEng(4.1, siPrefix = true) == "4.1" # Note lack of space + doAssert formatEng(4100, siPrefix = true) == "4.1 k" + doAssert formatEng(4.1, siPrefix = true, unit = "", + useUnitSpace = true) == "4.1 " # Includes space + doAssert formatEng(4100, siPrefix = true, unit = "") == "4.1 k" + doAssert formatEng(4100) == "4.1e3" + doAssert formatEng(4100, unit = "V", useUnitSpace = true) == "4.1e3 V" + doAssert formatEng(4100, unit = "", useUnitSpace = true) == "4.1e3 " + # Don't use SI prefix as number is too big + doAssert formatEng(3.1e22, siPrefix = true, unit = "a", + useUnitSpace = true) == "31e21 a" + # Don't use SI prefix as number is too small + doAssert formatEng(3.1e-25, siPrefix = true, unit = "A", + useUnitSpace = true) == "310e-27 A" + + proc staticTests = + doAssert align("abc", 4) == " abc" + doAssert align("a", 0) == "a" + doAssert align("1232", 6) == " 1232" + doAssert align("1232", 6, '#') == "##1232" + + doAssert alignLeft("abc", 4) == "abc " + doAssert alignLeft("a", 0) == "a" + doAssert alignLeft("1232", 6) == "1232 " + doAssert alignLeft("1232", 6, '#') == "1232##" + + doAssert "$animal eats $food." % ["animal", "The cat", "food", "fish"] == + "The cat eats fish." + + doAssert "-ld a-ldz -ld".replaceWord("-ld") == " a-ldz " + doAssert "-lda-ldz -ld abc".replaceWord("-ld") == "-lda-ldz abc" + + doAssert "-lda-ldz -ld abc".replaceWord("") == "-lda-ldz -ld abc" + doAssert "oo".replace("", "abc") == "oo" + + type MyEnum = enum enA, enB, enC, enuD, enE + doAssert parseEnum[MyEnum]("enu_D") == enuD + + doAssert parseEnum("invalid enum value", enC) == enC + + doAssert center("foo", 13) == " foo " + doAssert center("foo", 0) == "foo" + doAssert center("foo", 3, fillChar = 'a') == "foo" + doAssert center("foo", 10, fillChar = '\t') == "\t\t\tfoo\t\t\t\t" + + doAssert count("foofoofoo", "foofoo") == 1 + doAssert count("foofoofoo", "foofoo", overlapping = true) == 2 + doAssert count("foofoofoo", 'f') == 3 + doAssert count("foofoofoobar", {'f', 'b'}) == 4 + + doAssert strip(" foofoofoo ") == "foofoofoo" + doAssert strip("sfoofoofoos", chars = {'s'}) == "foofoofoo" + doAssert strip("barfoofoofoobar", chars = {'b', 'a', 'r'}) == "foofoofoo" + doAssert strip("stripme but don't strip this stripme", + chars = {'s', 't', 'r', 'i', 'p', 'm', 'e'}) == + " but don't strip this " + doAssert strip("sfoofoofoos", leading = false, chars = {'s'}) == "sfoofoofoo" + doAssert strip("sfoofoofoos", trailing = false, chars = {'s'}) == "foofoofoos" + + doAssert " foo\n bar".indent(4, "Q") == "QQQQ foo\nQQQQ bar" + + doAssert "abba".multiReplace(("a", "b"), ("b", "a")) == "baab" + doAssert "Hello World.".multiReplace(("ello", "ELLO"), ("World.", + "PEOPLE!")) == "HELLO PEOPLE!" + doAssert "aaaa".multiReplace(("a", "aa"), ("aa", "bb")) == "aaaaaaaa" + + doAssert isAlphaAscii('r') + doAssert isAlphaAscii('A') + doAssert(not isAlphaAscii('$')) + + doAssert isAlphaNumeric('3') + doAssert isAlphaNumeric('R') + doAssert(not isAlphaNumeric('!')) + + doAssert isDigit('3') + doAssert(not isDigit('a')) + doAssert(not isDigit('%')) + + doAssert isSpaceAscii('\t') + doAssert isSpaceAscii('\l') + doAssert(not isSpaceAscii('A')) + + doAssert(isEmptyOrWhitespace("")) + doAssert(isEmptyOrWhitespace(" ")) + doAssert(isEmptyOrWhitespace("\t\l \v\r\f")) + doAssert(not isEmptyOrWhitespace("ABc \td")) + + doAssert isLowerAscii('a') + doAssert isLowerAscii('z') + doAssert(not isLowerAscii('A')) + doAssert(not isLowerAscii('5')) + doAssert(not isLowerAscii('&')) + doAssert(not isLowerAscii(' ')) + + doAssert isUpperAscii('A') + doAssert(not isUpperAscii('b')) + doAssert(not isUpperAscii('5')) + doAssert(not isUpperAscii('%')) + + doAssert rsplit("foo bar", seps = Whitespace) == @["foo", "bar"] + doAssert rsplit(" foo bar", seps = Whitespace, maxsplit = 1) == @[" foo", "bar"] + doAssert rsplit(" foo bar ", seps = Whitespace, maxsplit = 1) == @[ + " foo bar", ""] + doAssert rsplit(":foo:bar", sep = ':') == @["", "foo", "bar"] + doAssert rsplit(":foo:bar", sep = ':', maxsplit = 2) == @["", "foo", "bar"] + doAssert rsplit(":foo:bar", sep = ':', maxsplit = 3) == @["", "foo", "bar"] + doAssert rsplit("foothebar", sep = "the") == @["foo", "bar"] + + doAssert(unescape(r"\x013", "", "") == "\x013") + + doAssert join(["foo", "bar", "baz"]) == "foobarbaz" + doAssert join(@["foo", "bar", "baz"], ", ") == "foo, bar, baz" + doAssert join([1, 2, 3]) == "123" + doAssert join(@[1, 2, 3], ", ") == "1, 2, 3" + + doAssert """~~!!foo +~~!!bar +~~!!baz""".unindent(2, "~~!!") == "foo\nbar\nbaz" + + doAssert """~~!!foo +~~!!bar +~~!!baz""".unindent(2, "~~!!aa") == "~~!!foo\n~~!!bar\n~~!!baz" + doAssert """~~foo +~~ bar +~~ baz""".unindent(4, "~") == "foo\n bar\n baz" + doAssert """foo +bar + baz + """.unindent(4) == "foo\nbar\nbaz\n" + doAssert """foo + bar + baz + """.unindent(2) == "foo\n bar\n baz\n" + doAssert """foo + bar + baz + """.unindent(100) == "foo\nbar\nbaz\n" + + doAssert """foo + foo + bar + """.unindent() == "foo\nfoo\nbar\n" + + let s = " this is an example " + let s2 = ":this;is;an:example;;" + + doAssert s.split() == @["", "this", "is", "an", "example", "", ""] + doAssert s2.split(seps = {':', ';'}) == @["", "this", "is", "an", "example", + "", ""] + doAssert s.split(maxsplit = 4) == @["", "this", "is", "an", "example "] + doAssert s.split(' ', maxsplit = 1) == @["", "this is an example "] + doAssert s.split(" ", maxsplit = 4) == @["", "this", "is", "an", "example "] + + doAssert s.splitWhitespace() == @["this", "is", "an", "example"] + doAssert s.splitWhitespace(maxsplit = 1) == @["this", "is an example "] + doAssert s.splitWhitespace(maxsplit = 2) == @["this", "is", "an example "] + doAssert s.splitWhitespace(maxsplit = 3) == @["this", "is", "an", "example "] + doAssert s.splitWhitespace(maxsplit = 4) == @["this", "is", "an", "example"] + + block: # startsWith / endsWith char tests + var s = "abcdef" + doAssert s.startsWith('a') + doAssert s.startsWith('b') == false + doAssert s.endsWith('f') + doAssert s.endsWith('a') == false + doAssert s.endsWith('\0') == false + + #echo("strutils tests passed") + + nonStaticTests() + staticTests() + static: staticTests() \ No newline at end of file diff --git a/tests/stdlib/ttables.nim b/tests/stdlib/ttables.nim new file mode 100644 index 000000000..656d7be6b --- /dev/null +++ b/tests/stdlib/ttables.nim @@ -0,0 +1,311 @@ +import tables, hashes + +type + Person = object + firstName, lastName: string + +proc hash(x: Person): Hash = + ## Piggyback on the already available string hash proc. + ## + ## Without this proc nothing works! + result = x.firstName.hash !& x.lastName.hash + result = !$result + +var + salaries = initTable[Person, int]() + p1, p2: Person +p1.firstName = "Jon" +p1.lastName = "Ross" +salaries[p1] = 30_000 +p2.firstName = "소진" +p2.lastName = "박" +salaries[p2] = 45_000 +var + s2 = initOrderedTable[Person, int]() + s3 = initCountTable[Person]() +s2[p1] = 30_000 +s2[p2] = 45_000 +s3[p1] = 30_000 +s3[p2] = 45_000 + +block: # Ordered table should preserve order after deletion + var + s4 = initOrderedTable[int, int]() + s4[1] = 1 + s4[2] = 2 + s4[3] = 3 + + var prev = 0 + for i in s4.values: + doAssert(prev < i) + prev = i + + s4.del(2) + doAssert(2 notin s4) + doAssert(s4.len == 2) + prev = 0 + for i in s4.values: + doAssert(prev < i) + prev = i + +block: # Deletion from OrderedTable should account for collision groups. See issue #5057. + # The bug is reproducible only with exact keys + const key1 = "boy_jackpot.inGamma" + const key2 = "boy_jackpot.outBlack" + + var t = { + key1: 0, + key2: 0 + }.toOrderedTable() + + t.del(key1) + assert(t.len == 1) + assert(key2 in t) + +var + t1 = initCountTable[string]() + t2 = initCountTable[string]() +t1.inc("foo") +t1.inc("bar", 2) +t1.inc("baz", 3) +t2.inc("foo", 4) +t2.inc("bar") +t2.inc("baz", 11) +merge(t1, t2) +assert(t1["foo"] == 5) +assert(t1["bar"] == 3) +assert(t1["baz"] == 14) + +let + t1r = newCountTable[string]() + t2r = newCountTable[string]() +t1r.inc("foo") +t1r.inc("bar", 2) +t1r.inc("baz", 3) +t2r.inc("foo", 4) +t2r.inc("bar") +t2r.inc("baz", 11) +merge(t1r, t2r) +assert(t1r["foo"] == 5) +assert(t1r["bar"] == 3) +assert(t1r["baz"] == 14) + +var + t1l = initCountTable[string]() + t2l = initCountTable[string]() +t1l.inc("foo") +t1l.inc("bar", 2) +t1l.inc("baz", 3) +t2l.inc("foo", 4) +t2l.inc("bar") +t2l.inc("baz", 11) + +block: + const testKey = "TESTKEY" + let t: CountTableRef[string] = newCountTable[string]() + + # Before, does not compile with error message: + #test_counttable.nim(7, 43) template/generic instantiation from here + #lib/pure/collections/tables.nim(117, 21) template/generic instantiation from here + #lib/pure/collections/tableimpl.nim(32, 27) Error: undeclared field: 'hcode + doAssert 0 == t[testKey] + t.inc(testKey, 3) + doAssert 3 == t[testKey] + +block: + # Clear tests + var clearTable = newTable[int, string]() + clearTable[42] = "asd" + clearTable[123123] = "piuyqwb " + doAssert clearTable[42] == "asd" + clearTable.clear() + doAssert(not clearTable.hasKey(123123)) + doAssert clearTable.getOrDefault(42) == "" + +block: #5482 + var a = [("wrong?", "foo"), ("wrong?", "foo2")].newOrderedTable() + var b = newOrderedTable[string, string](initialSize = 2) + b["wrong?"] = "foo" + b["wrong?"] = "foo2" + assert a == b + +block: #5482 + var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable() + var b = newOrderedTable[string, string](initialSize = 2) + b["wrong?"] = "foo" + b["wrong?"] = "foo2" + assert a == b + +block: #5487 + var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable() + var b = newOrderedTable[string, string]() # notice, default size! + b["wrong?"] = "foo" + b["wrong?"] = "foo2" + assert a == b + +block: #5487 + var a = [("wrong?", "foo"), ("wrong?", "foo2")].newOrderedTable() + var b = newOrderedTable[string, string]() # notice, default size! + b["wrong?"] = "foo" + b["wrong?"] = "foo2" + assert a == b + +block: + var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable() + var b = [("wrong?", "foo"), ("wrong?", "foo2")].newOrderedTable() + var c = newOrderedTable[string, string]() # notice, default size! + c["wrong?"] = "foo" + c["wrong?"] = "foo2" + assert a == b + assert a == c + +block: #6250 + let + a = {3: 1}.toOrderedTable + b = {3: 2}.toOrderedTable + assert((a == b) == false) + assert((b == a) == false) + +block: #6250 + let + a = {3: 2}.toOrderedTable + b = {3: 2}.toOrderedTable + assert((a == b) == true) + assert((b == a) == true) + +block: # CountTable.smallest + let t = toCountTable([0, 0, 5, 5, 5]) + doAssert t.smallest == (0, 2) + +block: #10065 + let t = toCountTable("abracadabra") + doAssert t['z'] == 0 + + var t_mut = toCountTable("abracadabra") + doAssert t_mut['z'] == 0 + # the previous read may not have modified the table. + doAssert t_mut.hasKey('z') == false + t_mut['z'] = 1 + doAssert t_mut['z'] == 1 + doAssert t_mut.hasKey('z') == true + +block: #12813 #13079 + var t = toCountTable("abracadabra") + doAssert len(t) == 5 + + t['a'] = 0 # remove a key + doAssert len(t) == 4 + +block: + var tp: Table[string, string] = initTable[string, string]() + doAssert "test1" == tp.getOrDefault("test1", "test1") + tp["test2"] = "test2" + doAssert "test2" == tp.getOrDefault("test2", "test1") + var tr: TableRef[string, string] = newTable[string, string]() + doAssert "test1" == tr.getOrDefault("test1", "test1") + tr["test2"] = "test2" + doAssert "test2" == tr.getOrDefault("test2", "test1") + var op: OrderedTable[string, string] = initOrderedTable[string, string]() + doAssert "test1" == op.getOrDefault("test1", "test1") + op["test2"] = "test2" + doAssert "test2" == op.getOrDefault("test2", "test1") + var orf: OrderedTableRef[string, string] = newOrderedTable[string, string]() + doAssert "test1" == orf.getOrDefault("test1", "test1") + orf["test2"] = "test2" + doAssert "test2" == orf.getOrDefault("test2", "test1") + +block tableWithoutInit: + var + a: Table[string, int] + b: Table[string, int] + c: Table[string, int] + d: Table[string, int] + e: Table[string, int] + + a["a"] = 7 + doAssert a.hasKey("a") + doAssert a.len == 1 + doAssert a["a"] == 7 + a["a"] = 9 + doAssert a.len == 1 + doAssert a["a"] == 9 + + doAssert b.hasKeyOrPut("b", 5) == false + doAssert b.hasKey("b") + doAssert b.hasKeyOrPut("b", 8) + doAssert b["b"] == 5 + + doAssert c.getOrDefault("a") == 0 + doAssert c.getOrDefault("a", 3) == 3 + c["a"] = 6 + doAssert c.getOrDefault("a", 3) == 6 + + doAssert d.mgetOrPut("a", 3) == 3 + doAssert d.mgetOrPut("a", 6) == 3 + + var x = 99 + doAssert e.pop("a", x) == false + doAssert x == 99 + e["a"] = 77 + doAssert e.pop("a", x) + doAssert x == 77 + +block orderedTableWithoutInit: + var + a: OrderedTable[string, int] + b: OrderedTable[string, int] + c: OrderedTable[string, int] + d: OrderedTable[string, int] + + a["a"] = 7 + doAssert a.hasKey("a") + doAssert a.len == 1 + doAssert a["a"] == 7 + a["a"] = 9 + doAssert a.len == 1 + doAssert a["a"] == 9 + + doAssert b.hasKeyOrPut("b", 5) == false + doAssert b.hasKey("b") + doAssert b.hasKeyOrPut("b", 8) + doAssert b["b"] == 5 + + doAssert c.getOrDefault("a") == 0 + doAssert c.getOrDefault("a", 3) == 3 + c["a"] = 6 + doAssert c.getOrDefault("a", 3) == 6 + + doAssert d.mgetOrPut("a", 3) == 3 + doAssert d.mgetOrPut("a", 6) == 3 + +block countTableWithoutInit: + var + a: CountTable[string] + b: CountTable[string] + c: CountTable[string] + d: CountTable[string] + e: CountTable[string] + + a["a"] = 7 + doAssert a.hasKey("a") + doAssert a.len == 1 + doAssert a["a"] == 7 + a["a"] = 9 + doAssert a.len == 1 + doAssert a["a"] == 9 + + doAssert b["b"] == 0 + b.inc("b") + doAssert b["b"] == 1 + + doAssert c.getOrDefault("a") == 0 + doAssert c.getOrDefault("a", 3) == 3 + c["a"] = 6 + doAssert c.getOrDefault("a", 3) == 6 + + e["f"] = 3 + merge(d, e) + doAssert d.hasKey("f") + d.inc("f") + merge(d, e) + doAssert d["f"] == 7 diff --git a/tests/stdlib/tunicode.nim b/tests/stdlib/tunicode.nim new file mode 100644 index 000000000..a346106f9 --- /dev/null +++ b/tests/stdlib/tunicode.nim @@ -0,0 +1,215 @@ +import unicode + + +proc asRune(s: static[string]): Rune = + ## Compile-time conversion proc for converting string literals to a Rune + ## value. Returns the first Rune of the specified string. + ## + ## Shortcuts code like ``"å".runeAt(0)`` to ``"å".asRune`` and returns a + ## compile-time constant. + if s.len == 0: Rune(0) + else: s.runeAt(0) + +let + someString = "öÑ" + someRunes = toRunes(someString) + compared = (someString == $someRunes) +doAssert compared == true + +proc testReplacements(word: string): string = + case word + of "two": + return "2" + of "foo": + return "BAR" + of "βeta": + return "beta" + of "alpha": + return "αlpha" + else: + return "12345" + +doAssert translate("two not alpha foo βeta", testReplacements) == "2 12345 αlpha BAR beta" +doAssert translate(" two not foo βeta ", testReplacements) == " 2 12345 BAR beta " + +doAssert title("foo bar") == "Foo Bar" +doAssert title("αlpha βeta γamma") == "Αlpha Βeta Γamma" +doAssert title("") == "" + +doAssert capitalize("βeta") == "Βeta" +doAssert capitalize("foo") == "Foo" +doAssert capitalize("") == "" + +doAssert swapCase("FooBar") == "fOObAR" +doAssert swapCase(" ") == " " +doAssert swapCase("Αlpha Βeta Γamma") == "αLPHA βETA γAMMA" +doAssert swapCase("a✓B") == "A✓b" +doAssert swapCase("Јамогујестистаклоитоминештети") == "јАМОГУЈЕСТИСТАКЛОИТОМИНЕШТЕТИ" +doAssert swapCase("ὕαλονϕαγεῖνδύναμαιτοῦτοοὔμεβλάπτει") == "ὝΑΛΟΝΦΑΓΕῖΝΔΎΝΑΜΑΙΤΟῦΤΟΟὔΜΕΒΛΆΠΤΕΙ" +doAssert swapCase("Կրնամապակիուտեևինծիանհանգիստչըներ") == "կՐՆԱՄԱՊԱԿԻՈՒՏԵևԻՆԾԻԱՆՀԱՆԳԻՍՏՉԸՆԵՐ" +doAssert swapCase("") == "" + +doAssert isAlpha("r") +doAssert isAlpha("α") +doAssert isAlpha("ϙ") +doAssert isAlpha("ஶ") +doAssert(not isAlpha("$")) +doAssert(not isAlpha("")) + +doAssert isAlpha("Βeta") +doAssert isAlpha("Args") +doAssert isAlpha("𐌼𐌰𐌲𐌲𐌻𐌴𐍃𐍄𐌰𐌽") +doAssert isAlpha("ὕαλονϕαγεῖνδύναμαιτοῦτοοὔμεβλάπτει") +doAssert isAlpha("Јамогујестистаклоитоминештети") +doAssert isAlpha("Կրնամապակիուտեևինծիանհանգիստչըներ") +doAssert(not isAlpha("$Foo✓")) +doAssert(not isAlpha("⠙⠕⠑⠎⠝⠞")) + +doAssert isSpace("\t") +doAssert isSpace("\l") +doAssert(not isSpace("Β")) +doAssert(not isSpace("Βeta")) + +doAssert isSpace("\t\l \v\r\f") +doAssert isSpace(" ") +doAssert(not isSpace("")) +doAssert(not isSpace("ΑΓc \td")) + +doAssert(not isLower(' '.Rune)) + +doAssert(not isUpper(' '.Rune)) + +doAssert toUpper("Γ") == "Γ" +doAssert toUpper("b") == "B" +doAssert toUpper("α") == "Α" +doAssert toUpper("✓") == "✓" +doAssert toUpper("ϙ") == "Ϙ" +doAssert toUpper("") == "" + +doAssert toUpper("ΑΒΓ") == "ΑΒΓ" +doAssert toUpper("AAccβ") == "AACCΒ" +doAssert toUpper("A✓$β") == "A✓$Β" + +doAssert toLower("a") == "a" +doAssert toLower("γ") == "γ" +doAssert toLower("Γ") == "γ" +doAssert toLower("4") == "4" +doAssert toLower("Ϙ") == "ϙ" +doAssert toLower("") == "" + +doAssert toLower("abcdγ") == "abcdγ" +doAssert toLower("abCDΓ") == "abcdγ" +doAssert toLower("33aaΓ") == "33aaγ" + +doAssert reversed("Reverse this!") == "!siht esreveR" +doAssert reversed("先秦兩漢") == "漢兩秦先" +doAssert reversed("as⃝df̅") == "f̅ds⃝a" +doAssert reversed("a⃞b⃞c⃞") == "c⃞b⃞a⃞" +doAssert reversed("ὕαλονϕαγεῖνδύναμαιτοῦτοοὔμεβλάπτει") == "ιετπάλβεμὔοοτῦοτιαμανύδνῖεγαϕνολαὕ" +doAssert reversed("Јамогујестистаклоитоминештети") == "итетшенимотиолкатситсејугомаЈ" +doAssert reversed("Կրնամապակիուտեևինծիանհանգիստչըներ") == "րենըչտսիգնահնաիծնիևետւոիկապամանրԿ" +doAssert len(toRunes("as⃝df̅")) == runeLen("as⃝df̅") +const test = "as⃝" +doAssert lastRune(test, test.len-1)[1] == 3 +doAssert graphemeLen("è", 0) == 2 + +# test for rune positioning and runeSubStr() +let s = "Hänsel ««: 10,00€" + +var t = "" +for c in s.utf8: + t.add c + +doAssert(s == t) + +doAssert(runeReverseOffset(s, 1) == (20, 18)) +doAssert(runeReverseOffset(s, 19) == (-1, 18)) + +doAssert(runeStrAtPos(s, 0) == "H") +doAssert(runeSubStr(s, 0, 1) == "H") +doAssert(runeStrAtPos(s, 10) == ":") +doAssert(runeSubStr(s, 10, 1) == ":") +doAssert(runeStrAtPos(s, 9) == "«") +doAssert(runeSubStr(s, 9, 1) == "«") +doAssert(runeStrAtPos(s, 17) == "€") +doAssert(runeSubStr(s, 17, 1) == "€") +# echo runeStrAtPos(s, 18) # index error + +doAssert(runeSubStr(s, 0) == "Hänsel ««: 10,00€") +doAssert(runeSubStr(s, -18) == "Hänsel ««: 10,00€") +doAssert(runeSubStr(s, 10) == ": 10,00€") +doAssert(runeSubStr(s, 18) == "") +doAssert(runeSubStr(s, 0, 10) == "Hänsel ««") + +doAssert(runeSubStr(s, 12) == "10,00€") +doAssert(runeSubStr(s, -6) == "10,00€") + +doAssert(runeSubStr(s, 12, 5) == "10,00") +doAssert(runeSubStr(s, 12, -1) == "10,00") +doAssert(runeSubStr(s, -6, 5) == "10,00") +doAssert(runeSubStr(s, -6, -1) == "10,00") + +doAssert(runeSubStr(s, 0, 100) == "Hänsel ««: 10,00€") +doAssert(runeSubStr(s, -100, 100) == "Hänsel ««: 10,00€") +doAssert(runeSubStr(s, 0, -100) == "") +doAssert(runeSubStr(s, 100, -100) == "") + +block splitTests: + let s = " this is an example " + let s2 = ":this;is;an:example;;" + let s3 = ":this×is×an:example××" + doAssert s.split() == @["", "this", "is", "an", "example", "", ""] + doAssert s2.split(seps = [':'.Rune, ';'.Rune]) == @["", "this", "is", "an", + "example", "", ""] + doAssert s3.split(seps = [':'.Rune, "×".asRune]) == @["", "this", "is", + "an", "example", "", ""] + doAssert s.split(maxsplit = 4) == @["", "this", "is", "an", "example "] + doAssert s.split(' '.Rune, maxsplit = 1) == @["", "this is an example "] + doAssert s3.split("×".runeAt(0)) == @[":this", "is", "an:example", "", ""] + +block stripTests: + doAssert(strip("") == "") + doAssert(strip(" ") == "") + doAssert(strip("y") == "y") + doAssert(strip(" foofoofoo ") == "foofoofoo") + doAssert(strip("sfoofoofoos", runes = ['s'.Rune]) == "foofoofoo") + + block: + let stripTestRunes = ['b'.Rune, 'a'.Rune, 'r'.Rune] + doAssert(strip("barfoofoofoobar", runes = stripTestRunes) == "foofoofoo") + doAssert(strip("sfoofoofoos", leading = false, runes = ['s'.Rune]) == "sfoofoofoo") + doAssert(strip("sfoofoofoos", trailing = false, runes = ['s'.Rune]) == "foofoofoos") + + block: + let stripTestRunes = ["«".asRune, "»".asRune] + doAssert(strip("«TEXT»", runes = stripTestRunes) == "TEXT") + doAssert(strip("copyright©", leading = false, runes = ["©".asRune]) == "copyright") + doAssert(strip("¿Question?", trailing = false, runes = ["¿".asRune]) == "Question?") + doAssert(strip("×text×", leading = false, runes = ["×".asRune]) == "×text") + doAssert(strip("×text×", trailing = false, runes = ["×".asRune]) == "text×") + +block repeatTests: + doAssert repeat('c'.Rune, 5) == "ccccc" + doAssert repeat("×".asRune, 5) == "×××××" + +block alignTests: + doAssert align("abc", 4) == " abc" + doAssert align("a", 0) == "a" + doAssert align("1232", 6) == " 1232" + doAssert align("1232", 6, '#'.Rune) == "##1232" + doAssert align("1232", 6, "×".asRune) == "××1232" + doAssert alignLeft("abc", 4) == "abc " + doAssert alignLeft("a", 0) == "a" + doAssert alignLeft("1232", 6) == "1232 " + doAssert alignLeft("1232", 6, '#'.Rune) == "1232##" + doAssert alignLeft("1232", 6, "×".asRune) == "1232××" + +block differentSizes: + # upper and lower variants have different number of bytes + doAssert toLower("AẞC") == "aßc" + doAssert toLower("ȺẞCD") == "ⱥßcd" + doAssert toUpper("ⱥbc") == "ȺBC" + doAssert toUpper("rsⱦuv") == "RSȾUV" + doAssert swapCase("ⱥbCd") == "ȺBcD" + doAssert swapCase("XyꟆaB") == "xYᶎAb" + doAssert swapCase("aᵹcᲈd") == "AꝽCꙊD" diff --git a/tests/stdlib/tunidecode.nim b/tests/stdlib/tunidecode.nim index 576bfa2cb..7784fffb4 100644 --- a/tests/stdlib/tunidecode.nim +++ b/tests/stdlib/tunidecode.nim @@ -9,6 +9,5 @@ import std/unidecode # #14112 loadUnidecodeTable("lib/pure/unidecode/unidecode.dat") -#assert unidecode("\x53\x17\x4E\xB0") == "Bei Jing" +# assert unidecode("\x53\x17\x4E\xB0") == "Bei Jing" echo unidecode("Äußerst") - diff --git a/tests/stdlib/turi.nim b/tests/stdlib/turi.nim index 9fb349e88..62fb17e4d 100644 --- a/tests/stdlib/turi.nim +++ b/tests/stdlib/turi.nim @@ -6,7 +6,7 @@ discard """ exitcode: 0 timeout: 60.0 """ -import uri +include uri block: @@ -15,3 +15,286 @@ block: doAssert url.hostname == "2001:0db8:85a3:0000:0000:8a2e:0370:7334" # true let newUrl = parseUri($url) doAssert newUrl.hostname == "2001:0db8:85a3:0000:0000:8a2e:0370:7334" # true + + +block: + block: + const test1 = "abc\L+def xyz" + doAssert encodeUrl(test1) == "abc%0A%2Bdef+xyz" + doAssert decodeUrl(encodeUrl(test1)) == test1 + doAssert encodeUrl(test1, false) == "abc%0A%2Bdef%20xyz" + doAssert decodeUrl(encodeUrl(test1, false), false) == test1 + doAssert decodeUrl(encodeUrl(test1)) == test1 + + block: + let str = "http://localhost" + let test = parseUri(str) + doAssert test.path == "" + + block: + let str = "http://localhost/" + let test = parseUri(str) + doAssert test.path == "/" + + block: + let str = "http://localhost:8080/test" + let test = parseUri(str) + doAssert test.scheme == "http" + doAssert test.port == "8080" + doAssert test.path == "/test" + doAssert test.hostname == "localhost" + doAssert($test == str) + + block: + let str = "foo://username:password@example.com:8042/over/there" & + "/index.dtb?type=animal&name=narwhal#nose" + let test = parseUri(str) + doAssert test.scheme == "foo" + doAssert test.username == "username" + doAssert test.password == "password" + doAssert test.hostname == "example.com" + doAssert test.port == "8042" + doAssert test.path == "/over/there/index.dtb" + doAssert test.query == "type=animal&name=narwhal" + doAssert test.anchor == "nose" + doAssert($test == str) + + block: + # IPv6 address + let str = "foo://[::1]:1234/bar?baz=true&qux#quux" + let uri = parseUri(str) + doAssert uri.scheme == "foo" + doAssert uri.hostname == "::1" + doAssert uri.port == "1234" + doAssert uri.path == "/bar" + doAssert uri.query == "baz=true&qux" + doAssert uri.anchor == "quux" + + block: + let str = "urn:example:animal:ferret:nose" + let test = parseUri(str) + doAssert test.scheme == "urn" + doAssert test.path == "example:animal:ferret:nose" + doAssert($test == str) + + block: + let str = "mailto:username@example.com?subject=Topic" + let test = parseUri(str) + doAssert test.scheme == "mailto" + doAssert test.username == "username" + doAssert test.hostname == "example.com" + doAssert test.query == "subject=Topic" + doAssert($test == str) + + block: + let str = "magnet:?xt=urn:sha1:72hsga62ba515sbd62&dn=foobar" + let test = parseUri(str) + doAssert test.scheme == "magnet" + doAssert test.query == "xt=urn:sha1:72hsga62ba515sbd62&dn=foobar" + doAssert($test == str) + + block: + let str = "/test/foo/bar?q=2#asdf" + let test = parseUri(str) + doAssert test.scheme == "" + doAssert test.path == "/test/foo/bar" + doAssert test.query == "q=2" + doAssert test.anchor == "asdf" + doAssert($test == str) + + block: + let str = "test/no/slash" + let test = parseUri(str) + doAssert test.path == "test/no/slash" + doAssert($test == str) + + block: + let str = "//git@github.com:dom96/packages" + let test = parseUri(str) + doAssert test.scheme == "" + doAssert test.username == "git" + doAssert test.hostname == "github.com" + doAssert test.port == "dom96" + doAssert test.path == "/packages" + + block: + let str = "file:///foo/bar/baz.txt" + let test = parseUri(str) + doAssert test.scheme == "file" + doAssert test.username == "" + doAssert test.hostname == "" + doAssert test.port == "" + doAssert test.path == "/foo/bar/baz.txt" + + # Remove dot segments tests + block: + doAssert removeDotSegments("/foo/bar/baz") == "/foo/bar/baz" + + # Combine tests + block: + let concat = combine(parseUri("http://google.com/foo/bar/"), parseUri("baz")) + doAssert concat.path == "/foo/bar/baz" + doAssert concat.hostname == "google.com" + doAssert concat.scheme == "http" + + block: + let concat = combine(parseUri("http://google.com/foo"), parseUri("/baz")) + doAssert concat.path == "/baz" + doAssert concat.hostname == "google.com" + doAssert concat.scheme == "http" + + block: + let concat = combine(parseUri("http://google.com/foo/test"), parseUri("bar")) + doAssert concat.path == "/foo/bar" + + block: + let concat = combine(parseUri("http://google.com/foo/test"), parseUri("/bar")) + doAssert concat.path == "/bar" + + block: + let concat = combine(parseUri("http://google.com/foo/test"), parseUri("bar")) + doAssert concat.path == "/foo/bar" + + block: + let concat = combine(parseUri("http://google.com/foo/test/"), parseUri("bar")) + doAssert concat.path == "/foo/test/bar" + + block: + let concat = combine(parseUri("http://google.com/foo/test/"), parseUri("bar/")) + doAssert concat.path == "/foo/test/bar/" + + block: + let concat = combine(parseUri("http://google.com/foo/test/"), parseUri("bar/"), + parseUri("baz")) + doAssert concat.path == "/foo/test/bar/baz" + + # `/` tests + block: + let test = parseUri("http://example.com/foo") / "bar/asd" + doAssert test.path == "/foo/bar/asd" + + block: + let test = parseUri("http://example.com/foo/") / "/bar/asd" + doAssert test.path == "/foo/bar/asd" + + # removeDotSegments tests + block: + # empty test + doAssert removeDotSegments("") == "" + + # bug #3207 + block: + doAssert parseUri("http://qq/1").combine(parseUri("https://qqq")).`$` == "https://qqq" + + # bug #4959 + block: + let foo = parseUri("http://example.com") / "/baz" + doAssert foo.path == "/baz" + + # bug found on stream 13/10/17 + block: + let foo = parseUri("http://localhost:9515") / "status" + doAssert $foo == "http://localhost:9515/status" + + # bug #6649 #6652 + block: + var foo = parseUri("http://example.com") + foo.hostname = "example.com" + foo.path = "baz" + doAssert $foo == "http://example.com/baz" + + foo.hostname = "example.com/" + foo.path = "baz" + doAssert $foo == "http://example.com/baz" + + foo.hostname = "example.com" + foo.path = "/baz" + doAssert $foo == "http://example.com/baz" + + foo.hostname = "example.com/" + foo.path = "/baz" + doAssert $foo == "http://example.com/baz" + + foo.hostname = "example.com/" + foo.port = "8000" + foo.path = "baz" + doAssert $foo == "http://example.com:8000/baz" + + foo = parseUri("file:/dir/file") + foo.path = "relative" + doAssert $foo == "file:relative" + + # isAbsolute tests + block: + doAssert "www.google.com".parseUri().isAbsolute() == false + doAssert "http://www.google.com".parseUri().isAbsolute() == true + doAssert "file:/dir/file".parseUri().isAbsolute() == true + doAssert "file://localhost/dir/file".parseUri().isAbsolute() == true + doAssert "urn:ISSN:1535-3613".parseUri().isAbsolute() == true + + # path-relative URL *relative + doAssert "about".parseUri().isAbsolute == false + doAssert "about/staff.html".parseUri().isAbsolute == false + doAssert "about/staff.html?".parseUri().isAbsolute == false + doAssert "about/staff.html?parameters".parseUri().isAbsolute == false + + # absolute-path-relative URL *relative + doAssert "/".parseUri().isAbsolute == false + doAssert "/about".parseUri().isAbsolute == false + doAssert "/about/staff.html".parseUri().isAbsolute == false + doAssert "/about/staff.html?".parseUri().isAbsolute == false + doAssert "/about/staff.html?parameters".parseUri().isAbsolute == false + + # scheme-relative URL *relative + doAssert "//username:password@example.com:8888".parseUri().isAbsolute == false + doAssert "//username@example.com".parseUri().isAbsolute == false + doAssert "//example.com".parseUri().isAbsolute == false + doAssert "//example.com/".parseUri().isAbsolute == false + doAssert "//example.com/about".parseUri().isAbsolute == false + doAssert "//example.com/about/staff.html".parseUri().isAbsolute == false + doAssert "//example.com/about/staff.html?".parseUri().isAbsolute == false + doAssert "//example.com/about/staff.html?parameters".parseUri().isAbsolute == false + + # absolute URL *absolute + doAssert "https://username:password@example.com:8888".parseUri().isAbsolute == true + doAssert "https://username@example.com".parseUri().isAbsolute == true + doAssert "https://example.com".parseUri().isAbsolute == true + doAssert "https://example.com/".parseUri().isAbsolute == true + doAssert "https://example.com/about".parseUri().isAbsolute == true + doAssert "https://example.com/about/staff.html".parseUri().isAbsolute == true + doAssert "https://example.com/about/staff.html?".parseUri().isAbsolute == true + doAssert "https://example.com/about/staff.html?parameters".parseUri().isAbsolute == true + + # encodeQuery tests + block: + doAssert encodeQuery({:}) == "" + doAssert encodeQuery({"foo": "bar"}) == "foo=bar" + doAssert encodeQuery({"foo": "bar & baz"}) == "foo=bar+%26+baz" + doAssert encodeQuery({"foo": "bar & baz"}, usePlus = false) == "foo=bar%20%26%20baz" + doAssert encodeQuery({"foo": ""}) == "foo" + doAssert encodeQuery({"foo": ""}, omitEq = false) == "foo=" + doAssert encodeQuery({"a": "1", "b": "", "c": "3"}) == "a=1&b&c=3" + doAssert encodeQuery({"a": "1", "b": "", "c": "3"}, omitEq = false) == "a=1&b=&c=3" + + block: + var foo = parseUri("http://example.com") / "foo" ? {"bar": "1", "baz": "qux"} + var foo1 = parseUri("http://example.com/foo?bar=1&baz=qux") + doAssert foo == foo1 + + block: + var foo = parseUri("http://example.com") / "foo" ? {"do": "do", "bar": ""} + var foo1 = parseUri("http://example.com/foo?do=do&bar") + doAssert foo == foo1 + + block dataUriBase64: + doAssert getDataUri("", "text/plain") == "data:text/plain;charset=utf-8;base64," + doAssert getDataUri(" ", "text/plain") == "data:text/plain;charset=utf-8;base64,IA==" + doAssert getDataUri("c\xf7>", "text/plain") == "data:text/plain;charset=utf-8;base64,Y/c+" + doAssert getDataUri("Hello World", "text/plain") == "data:text/plain;charset=utf-8;base64,SGVsbG8gV29ybGQ=" + doAssert getDataUri("leasure.", "text/plain") == "data:text/plain;charset=utf-8;base64,bGVhc3VyZS4=" + doAssert getDataUri("""!@#$%^&*()_+""", "text/plain") == "data:text/plain;charset=utf-8;base64,IUAjJCVeJiooKV8r" + doAssert(getDataUri("the quick brown dog jumps over the lazy fox", "text/plain") == + "data:text/plain;charset=utf-8;base64,dGhlIHF1aWNrIGJyb3duIGRvZyBqdW1wcyBvdmVyIHRoZSBsYXp5IGZveA==") + doAssert(getDataUri("""The present is theirs + The future, for which I really worked, is mine.""", "text/plain") == + "data:text/plain;charset=utf-8;base64,VGhlIHByZXNlbnQgaXMgdGhlaXJzCiAgICAgIFRoZSBmdXR1cmUsIGZvciB3aGljaCBJIHJlYWxseSB3b3JrZWQsIGlzIG1pbmUu") From d2c6ac4750ffc60631fee2170c7ee4a884210225 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Wed, 25 Nov 2020 02:19:03 +0800 Subject: [PATCH 1065/3768] add simple runnableExamples for atomics (#16116) --- lib/pure/concurrency/atomics.nim | 40 ++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/lib/pure/concurrency/atomics.nim b/lib/pure/concurrency/atomics.nim index dc575c6c0..895944177 100644 --- a/lib/pure/concurrency/atomics.nim +++ b/lib/pure/concurrency/atomics.nim @@ -11,6 +11,46 @@ ## ## Unstable API. +runnableExamples: + # Atomic + var loc: Atomic[int] + loc.store(4) + assert loc.load == 4 + loc.store(2) + assert loc.load(moRelaxed) == 2 + loc.store(9) + assert loc.load(moAcquire) == 9 + loc.store(0, moRelease) + assert loc.load == 0 + + assert loc.exchange(7) == 0 + assert loc.load == 7 + + var expected = 7 + assert loc.compareExchange(expected, 5, moRelaxed, moRelaxed) + assert expected == 7 + assert loc.load == 5 + + assert not loc.compareExchange(expected, 12, moRelaxed, moRelaxed) + assert expected == 5 + assert loc.load == 5 + + assert loc.fetchAdd(1) == 5 + assert loc.fetchAdd(2) == 6 + assert loc.fetchSub(3) == 8 + + loc.atomicInc(1) + assert loc.load == 6 + + # AtomicFlag + var flag: AtomicFlag + + assert not flag.testAndSet + assert flag.testAndSet + flag.clear(moRelaxed) + assert not flag.testAndSet + + import macros when defined(cpp) or defined(nimdoc): From 3fed85437b981a01cc368166841d9d89d99434f0 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Wed, 25 Nov 2020 02:30:17 +0800 Subject: [PATCH 1066/3768] ref #16054 remove typed array (#16077) --- compiler/jsgen.nim | 8 -------- 1 file changed, 8 deletions(-) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 3a1ef7fbb..675a24c92 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -2598,14 +2598,6 @@ proc genHeader(): Rope = var framePtr = null; var excHandler = 0; var lastJSError = null; - if (typeof Int8Array === 'undefined') Int8Array = Array; - if (typeof Int16Array === 'undefined') Int16Array = Array; - if (typeof Int32Array === 'undefined') Int32Array = Array; - if (typeof Uint8Array === 'undefined') Uint8Array = Array; - if (typeof Uint16Array === 'undefined') Uint16Array = Array; - if (typeof Uint32Array === 'undefined') Uint32Array = Array; - if (typeof Float32Array === 'undefined') Float32Array = Array; - if (typeof Float64Array === 'undefined') Float64Array = Array; """.unindent.format(VersionAsString)) proc addHcrInitGuards(p: PProc, n: PNode, From 19e224866ba6cfe2c6cdc49e435a275b532f0acb Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Wed, 25 Nov 2020 02:45:06 +0800 Subject: [PATCH 1067/3768] add simple writeStackTrace for JS backend (#16016) * add simple writeStackTrace for JS backend * add testcase for writeStackTrace * changelog --- changelog.md | 2 ++ lib/system.nim | 2 +- lib/system/jssys.nim | 5 +++++ tests/js/twritestacktrace.nim | 12 ++++++++++++ 4 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 tests/js/twritestacktrace.nim diff --git a/changelog.md b/changelog.md index 4c18ddf1e..bfbe59e1a 100644 --- a/changelog.md +++ b/changelog.md @@ -47,6 +47,8 @@ - `repr` now doesn't insert trailing newline; previous behavior was very inconsistent, see #16034. Use `-d:nimLegacyReprWithNewline` for previous behavior. +- `writeStackTrace` is available in JS backend now. + ## Language changes - `nimscript` now handles `except Exception as e` diff --git a/lib/system.nim b/lib/system.nim index 716eb296a..be41c1893 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1765,7 +1765,7 @@ when notJSnotNims and defined(nimSeqsV2): {.pop.} -when notJSnotNims: +when not defined(nimscript): proc writeStackTrace*() {.tags: [], gcsafe, raises: [].} ## Writes the current stack trace to ``stderr``. This is only works ## for debug builds. Since it's usually used for debugging, this diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim index c4c671ea3..8865558fe 100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -105,6 +105,11 @@ proc rawWriteStackTrace(): string = else: result = "No stack traceback available\n" +proc writeStackTrace() = + var trace = rawWriteStackTrace() + trace.setLen(trace.len - 1) + echo trace + proc getStackTrace*(): string = rawWriteStackTrace() proc getStackTrace*(e: ref Exception): string = e.trace diff --git a/tests/js/twritestacktrace.nim b/tests/js/twritestacktrace.nim new file mode 100644 index 000000000..2fe2b1987 --- /dev/null +++ b/tests/js/twritestacktrace.nim @@ -0,0 +1,12 @@ +discard """ + cmd: "nim js --panics:on $file" + output: '''Traceback (most recent call last) +twritestacktrace.nim(12) at module twritestacktrace +twritestacktrace.nim(10) at twritestacktrace.hello +''' +""" + +proc hello() = + writeStackTrace() + +hello() From b7809cc9921421ea0e8326a24d66861748e3136b Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Wed, 25 Nov 2020 14:42:25 +0800 Subject: [PATCH 1068/3768] improve the documentation of ropes (#16111) --- lib/pure/ropes.nim | 117 +++++++++++++++++++++++++++++++++++++-------- 1 file changed, 98 insertions(+), 19 deletions(-) diff --git a/lib/pure/ropes.nim b/lib/pure/ropes.nim index a39533e58..83850e03e 100644 --- a/lib/pure/ropes.nim +++ b/lib/pure/ropes.nim @@ -33,7 +33,7 @@ type RopeObj {.acyclic.} = object left, right: Rope length: int - data: string # != nil if a leaf + data: string # not empty if a leaf # Note that the left and right pointers are not needed for leafs. # Leaves have relatively high memory overhead (~30 bytes on a 32 @@ -44,7 +44,7 @@ type # pointers. proc len*(a: Rope): int {.rtl, extern: "nro$1".} = - ## the rope's length + ## The rope's length. if a == nil: result = 0 else: result = a.length @@ -127,6 +127,9 @@ proc insertInCache(s: string, tree: Rope): Rope = proc rope*(s: string = ""): Rope {.rtl, extern: "nro$1Str".} = ## Converts a string to a rope. + runnableExamples: + var r = rope("I'm a rope") + doAssert $r == "I'm a rope" if s.len == 0: result = nil else: @@ -142,10 +145,16 @@ proc rope*(s: string = ""): Rope {.rtl, extern: "nro$1Str".} = proc rope*(i: BiggestInt): Rope {.rtl, extern: "nro$1BiggestInt".} = ## Converts an int to a rope. + runnableExamples: + var r = rope(429) + doAssert $r == "429" result = rope($i) proc rope*(f: BiggestFloat): Rope {.rtl, extern: "nro$1BiggestFloat".} = ## Converts a float to a rope. + runnableExamples: + var r = rope(4.29) + doAssert $r == "4.29" result = rope($f) proc enableCache*() {.rtl, extern: "nro$1".} = @@ -154,12 +163,19 @@ proc enableCache*() {.rtl, extern: "nro$1".} = cacheEnabled = true proc disableCache*() {.rtl, extern: "nro$1".} = - ## the cache is discarded and disabled. The GC will reuse its used memory. + ## The cache is discarded and disabled. The GC will reuse its used memory. cache = nil cacheEnabled = false proc `&`*(a, b: Rope): Rope {.rtl, extern: "nroConcRopeRope".} = - ## the concatenation operator for ropes. + ## The concatenation operator for ropes. + runnableExamples: + var + r1 = rope("Hello, ") + r2 = rope("Nim!") + + let r = r1 & r2 + doAssert $r == "Hello, Nim!" if a == nil: result = b elif b == nil: @@ -171,28 +187,73 @@ proc `&`*(a, b: Rope): Rope {.rtl, extern: "nroConcRopeRope".} = result.right = b proc `&`*(a: Rope, b: string): Rope {.rtl, extern: "nroConcRopeStr".} = - ## the concatenation operator for ropes. + ## The concatenation operator for ropes. + runnableExamples: + var + r1 = rope("Hello, ") + r2 = "Nim!" + + let r = r1 & r2 + doAssert $r == "Hello, Nim!" result = a & rope(b) proc `&`*(a: string, b: Rope): Rope {.rtl, extern: "nroConcStrRope".} = - ## the concatenation operator for ropes. + ## The concatenation operator for ropes. + runnableExamples: + var + r1 = "Hello, " + r2 = rope("Nim!") + + let r = r1 & r2 + doAssert $r == "Hello, Nim!" result = rope(a) & b proc `&`*(a: openArray[Rope]): Rope {.rtl, extern: "nroConcOpenArray".} = - ## the concatenation operator for an openarray of ropes. - for i in countup(0, high(a)): result = result & a[i] + ## The concatenation operator for an openarray of ropes. + runnableExamples: + let s = @[rope("Hello, "), rope("Nim"), rope("!")] + let r = &s + doAssert $r == "Hello, Nim!" + for item in a: result = result & item proc add*(a: var Rope, b: Rope) {.rtl, extern: "nro$1Rope".} = - ## adds `b` to the rope `a`. + ## Adds `b` to the rope `a`. + runnableExamples: + var + r1 = rope("Hello, ") + r2 = rope("Nim!") + + r1.add(r2) + doAssert $r1 == "Hello, Nim!" a = a & b proc add*(a: var Rope, b: string) {.rtl, extern: "nro$1Str".} = - ## adds `b` to the rope `a`. + ## Adds `b` to the rope `a`. + runnableExamples: + var + r1 = rope("Hello, ") + r2 = "Nim!" + + r1.add(r2) + doAssert $r1 == "Hello, Nim!" a = a & b proc `[]`*(r: Rope, i: int): char {.rtl, extern: "nroCharAt".} = - ## returns the character at position `i` in the rope `r`. This is quite + ## Returns the character at position `i` in the rope `r`. This is quite ## expensive! Worst-case: O(n). If ``i >= r.len``, ``\0`` is returned. + runnableExamples: + let r1 = rope("Hello, Nim!") + + doAssert r1[0] == 'H' + doAssert r1[7] == 'N' + doAssert r1[22] == '\0' + + let r2 = rope("Hello") & rope(", Nim!") + + doAssert r2[0] == 'H' + doAssert r2[7] == 'N' + doAssert r2[22] == '\0' + var x = r var j = i if x == nil: return @@ -208,7 +269,15 @@ proc `[]`*(r: Rope, i: int): char {.rtl, extern: "nroCharAt".} = x = x.right iterator leaves*(r: Rope): string = - ## iterates over any leaf string in the rope `r`. + ## Iterates over any leaf string in the rope `r`. + runnableExamples: + let r = rope("Hello") & rope(", Nim!") + let s = ["Hello", ", Nim!"] + var index = 0 + for leave in r.leaves: + doAssert leave == s[index] + inc index + if r != nil: var stack = @[r] while stack.len > 0: @@ -221,20 +290,20 @@ iterator leaves*(r: Rope): string = yield it.data iterator items*(r: Rope): char = - ## iterates over any character in the rope `r`. + ## Iterates over any character in the rope `r`. for s in leaves(r): for c in items(s): yield c proc write*(f: File, r: Rope) {.rtl, extern: "nro$1".} = - ## writes a rope to a file. + ## Writes a rope to a file. for s in leaves(r): write(f, s) proc write*(s: Stream, r: Rope) {.rtl, extern: "nroWriteStream".} = - ## writes a rope to a stream. + ## Writes a rope to a stream. for rs in leaves(r): write(s, rs) proc `$`*(r: Rope): string {.rtl, extern: "nroToString".} = - ## converts a rope back to a string. + ## Converts a rope back to a string. result = newStringOfCap(r.len) for s in leaves(r): add(result, s) @@ -242,6 +311,12 @@ proc `%`*(frmt: string, args: openArray[Rope]): Rope {. rtl, extern: "nroFormat".} = ## `%` substitution operator for ropes. Does not support the ``$identifier`` ## nor ``${identifier}`` notations. + runnableExamples: + let r1 = "$1 $2 $3" % [rope("Nim"), rope("is"), rope("a great language")] + doAssert $r1 == "Nim is a great language" + + let r2 = "$# $# $#" % [rope("Nim"), rope("is"), rope("a great language")] + doAssert $r2 == "Nim is a great language" var i = 0 var length = len(frmt) result = nil @@ -284,7 +359,11 @@ proc `%`*(frmt: string, args: openArray[Rope]): Rope {. proc addf*(c: var Rope, frmt: string, args: openArray[Rope]) {. rtl, extern: "nro$1".} = - ## shortcut for ``add(c, frmt % args)``. + ## Shortcut for ``add(c, frmt % args)``. + runnableExamples: + var r = rope("Dash: ") + r.addf "$1 $2 $3", [rope("Nim"), rope("is"), rope("a great language")] + doAssert $r == "Dash: Nim is a great language" add(c, frmt % args) when not defined(js) and not defined(nimscript): @@ -292,7 +371,7 @@ when not defined(js) and not defined(nimscript): bufSize = 1024 # 1 KB is reasonable proc equalsFile*(r: Rope, f: File): bool {.rtl, extern: "nro$1File".} = - ## returns true if the contents of the file `f` equal `r`. + ## Returns true if the contents of the file `f` equal `r`. var buf: array[bufSize, char] bpos = buf.len @@ -321,7 +400,7 @@ when not defined(js) and not defined(nimscript): result = readBuffer(f, addr(buf[0]), 1) == 0 # check that we've read all proc equalsFile*(r: Rope, filename: string): bool {.rtl, extern: "nro$1Str".} = - ## returns true if the contents of the file `f` equal `r`. If `f` does not + ## Returns true if the contents of the file `f` equal `r`. If `f` does not ## exist, false is returned. var f: File result = open(f, filename) From 57bd64582ceee697dc4f079baf6a4e6137ff45b4 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Wed, 25 Nov 2020 14:48:27 +0800 Subject: [PATCH 1069/3768] move tests under the compiler directory to testament (#16096) --- compiler/btrees.nim | 65 ----------- compiler/int128.nim | 187 ------------------------------ compiler/nimblecmd.nim | 27 ----- compiler/pathutils.nim | 16 --- compiler/prefixmatches.nim | 33 ------ tests/compiler/tbrees.nim | 84 ++++++++++++++ tests/compiler/tint128.nim | 184 +++++++++++++++++++++++++++++ tests/compiler/tnimblecmd.nim | 26 +++++ tests/compiler/tpathutils.nim | 18 +++ tests/compiler/tprefixmatches.nim | 32 +++++ 10 files changed, 344 insertions(+), 328 deletions(-) create mode 100644 tests/compiler/tbrees.nim create mode 100644 tests/compiler/tint128.nim create mode 100644 tests/compiler/tnimblecmd.nim create mode 100644 tests/compiler/tpathutils.nim create mode 100644 tests/compiler/tprefixmatches.nim diff --git a/compiler/btrees.nim b/compiler/btrees.nim index 473cbd1b0..5f78c07fe 100644 --- a/compiler/btrees.nim +++ b/compiler/btrees.nim @@ -168,68 +168,3 @@ iterator pairs*[Key, Val](b: BTree[Key, Val]): (Key, Val) = yield (k, v) proc len*[Key, Val](b: BTree[Key, Val]): int {.inline.} = b.entries - -when isMainModule: - - import random, tables - - proc main = - var st = initBTree[string, string]() - st.add("www.cs.princeton.edu", "abc") - st.add("www.princeton.edu", "128.112.128.15") - st.add("www.yale.edu", "130.132.143.21") - st.add("www.simpsons.com", "209.052.165.60") - st.add("www.apple.com", "17.112.152.32") - st.add("www.amazon.com", "207.171.182.16") - st.add("www.ebay.com", "66.135.192.87") - st.add("www.cnn.com", "64.236.16.20") - st.add("www.google.com", "216.239.41.99") - st.add("www.nytimes.com", "199.239.136.200") - st.add("www.microsoft.com", "207.126.99.140") - st.add("www.dell.com", "143.166.224.230") - st.add("www.slashdot.org", "66.35.250.151") - st.add("www.espn.com", "199.181.135.201") - st.add("www.weather.com", "63.111.66.11") - st.add("www.yahoo.com", "216.109.118.65") - - assert st.getOrDefault("www.cs.princeton.edu") == "abc" - assert st.getOrDefault("www.harvardsucks.com") == "" - - assert st.getOrDefault("www.simpsons.com") == "209.052.165.60" - assert st.getOrDefault("www.apple.com") == "17.112.152.32" - assert st.getOrDefault("www.ebay.com") == "66.135.192.87" - assert st.getOrDefault("www.dell.com") == "143.166.224.230" - assert(st.entries == 16) - - for k, v in st: - echo k, ": ", v - - when false: - var b2 = initBTree[string, string]() - const iters = 10_000 - for i in 1..iters: - b2.add($i, $(iters - i)) - for i in 1..iters: - let x = b2.getOrDefault($i) - if x != $(iters - i): - echo "got ", x, ", but expected ", iters - i - echo b2.entries - - when true: - var b2 = initBTree[int, string]() - var t2 = initTable[int, string]() - const iters = 100_000 - for i in 1..iters: - let x = rand(high(int)) - if not t2.hasKey(x): - doAssert b2.getOrDefault(x).len == 0, " what, tree has this element " & $x - t2[x] = $x - b2.add(x, $x) - - doAssert b2.entries == t2.len - echo "unique entries ", b2.entries - for k, v in t2: - doAssert $k == v - doAssert b2.getOrDefault(k) == $k - - main() diff --git a/compiler/int128.nim b/compiler/int128.nim index f9b6f1d80..e266b9cd0 100644 --- a/compiler/int128.nim +++ b/compiler/int128.nim @@ -592,190 +592,3 @@ proc maskBytes*(arg: Int128, numbytes: int): Int128 {.noinit.} = return maskUInt64(arg) else: assert(false, "masking only implemented for 1, 2, 4 and 8 bytes") - - - - -when isMainModule: - let (a,b) = divMod(Ten,Ten) - - doAssert $One == "1" - doAssert $Ten == "10" - doAssert $Zero == "0" - let c = parseDecimalInt128("12345678989876543210123456789") - doAssert $c == "12345678989876543210123456789" - - var d : array[39, Int128] - d[0] = parseDecimalInt128("1") - d[1] = parseDecimalInt128("10") - d[2] = parseDecimalInt128("100") - d[3] = parseDecimalInt128("1000") - d[4] = parseDecimalInt128("10000") - d[5] = parseDecimalInt128("100000") - d[6] = parseDecimalInt128("1000000") - d[7] = parseDecimalInt128("10000000") - d[8] = parseDecimalInt128("100000000") - d[9] = parseDecimalInt128("1000000000") - d[10] = parseDecimalInt128("10000000000") - d[11] = parseDecimalInt128("100000000000") - d[12] = parseDecimalInt128("1000000000000") - d[13] = parseDecimalInt128("10000000000000") - d[14] = parseDecimalInt128("100000000000000") - d[15] = parseDecimalInt128("1000000000000000") - d[16] = parseDecimalInt128("10000000000000000") - d[17] = parseDecimalInt128("100000000000000000") - d[18] = parseDecimalInt128("1000000000000000000") - d[19] = parseDecimalInt128("10000000000000000000") - d[20] = parseDecimalInt128("100000000000000000000") - d[21] = parseDecimalInt128("1000000000000000000000") - d[22] = parseDecimalInt128("10000000000000000000000") - d[23] = parseDecimalInt128("100000000000000000000000") - d[24] = parseDecimalInt128("1000000000000000000000000") - d[25] = parseDecimalInt128("10000000000000000000000000") - d[26] = parseDecimalInt128("100000000000000000000000000") - d[27] = parseDecimalInt128("1000000000000000000000000000") - d[28] = parseDecimalInt128("10000000000000000000000000000") - d[29] = parseDecimalInt128("100000000000000000000000000000") - d[30] = parseDecimalInt128("1000000000000000000000000000000") - d[31] = parseDecimalInt128("10000000000000000000000000000000") - d[32] = parseDecimalInt128("100000000000000000000000000000000") - d[33] = parseDecimalInt128("1000000000000000000000000000000000") - d[34] = parseDecimalInt128("10000000000000000000000000000000000") - d[35] = parseDecimalInt128("100000000000000000000000000000000000") - d[36] = parseDecimalInt128("1000000000000000000000000000000000000") - d[37] = parseDecimalInt128("10000000000000000000000000000000000000") - d[38] = parseDecimalInt128("100000000000000000000000000000000000000") - - for i in 0..= 0: - doAssert d[i] div d[j] == d[i-j] - - var sum: Int128 - - for it in d: - sum += it - - doAssert $sum == "111111111111111111111111111111111111111" - - for it in d.mitems: - it = -it - - for i in 0..= 0: - doAssert d[i] div d[j] == -d[i-j] - - doAssert $high(Int128) == "170141183460469231731687303715884105727" - doAssert $low(Int128) == "-170141183460469231731687303715884105728" - - var ma = 100'i64 - var mb = 13 - - doAssert toInt128(ma) * toInt128(0) == toInt128(0) - doAssert toInt128(-ma) * toInt128(0) == toInt128(0) - - # sign correctness - doAssert divMod(toInt128( ma),toInt128( mb)) == (toInt128( ma div mb), toInt128( ma mod mb)) - doAssert divMod(toInt128(-ma),toInt128( mb)) == (toInt128(-ma div mb), toInt128(-ma mod mb)) - doAssert divMod(toInt128( ma),toInt128(-mb)) == (toInt128( ma div -mb), toInt128( ma mod -mb)) - doAssert divMod(toInt128(-ma),toInt128(-mb)) == (toInt128(-ma div -mb), toInt128(-ma mod -mb)) - - doAssert divMod(toInt128( mb),toInt128( mb)) == (toInt128( mb div mb), toInt128( mb mod mb)) - doAssert divMod(toInt128(-mb),toInt128( mb)) == (toInt128(-mb div mb), toInt128(-mb mod mb)) - doAssert divMod(toInt128( mb),toInt128(-mb)) == (toInt128( mb div -mb), toInt128( mb mod -mb)) - doAssert divMod(toInt128(-mb),toInt128(-mb)) == (toInt128(-mb div -mb), toInt128(-mb mod -mb)) - - doAssert divMod(toInt128( mb),toInt128( ma)) == (toInt128( mb div ma), toInt128( mb mod ma)) - doAssert divMod(toInt128(-mb),toInt128( ma)) == (toInt128(-mb div ma), toInt128(-mb mod ma)) - doAssert divMod(toInt128( mb),toInt128(-ma)) == (toInt128( mb div -ma), toInt128( mb mod -ma)) - doAssert divMod(toInt128(-mb),toInt128(-ma)) == (toInt128(-mb div -ma), toInt128(-mb mod -ma)) - - let e = parseDecimalInt128("70997106675279150998592376708984375") - - let strArray = [ - # toHex(e shr 0), toHex(e shr 1), toHex(e shr 2), toHex(e shr 3) - "000dac6d782d266a37300c32591eee37", "0006d636bc1693351b9806192c8f771b", "00036b1b5e0b499a8dcc030c9647bb8d", "0001b58daf05a4cd46e601864b23ddc6", - "0000dac6d782d266a37300c32591eee3", "00006d636bc1693351b9806192c8f771", "000036b1b5e0b499a8dcc030c9647bb8", "00001b58daf05a4cd46e601864b23ddc", - "00000dac6d782d266a37300c32591eee", "000006d636bc1693351b9806192c8f77", "0000036b1b5e0b499a8dcc030c9647bb", "000001b58daf05a4cd46e601864b23dd", - "000000dac6d782d266a37300c32591ee", "0000006d636bc1693351b9806192c8f7", "00000036b1b5e0b499a8dcc030c9647b", "0000001b58daf05a4cd46e601864b23d", - "0000000dac6d782d266a37300c32591e", "00000006d636bc1693351b9806192c8f", "000000036b1b5e0b499a8dcc030c9647", "00000001b58daf05a4cd46e601864b23", - "00000000dac6d782d266a37300c32591", "000000006d636bc1693351b9806192c8", "0000000036b1b5e0b499a8dcc030c964", "000000001b58daf05a4cd46e601864b2", - "000000000dac6d782d266a37300c3259", "0000000006d636bc1693351b9806192c", "00000000036b1b5e0b499a8dcc030c96", "0000000001b58daf05a4cd46e601864b", - "0000000000dac6d782d266a37300c325", "00000000006d636bc1693351b9806192", "000000000036b1b5e0b499a8dcc030c9", "00000000001b58daf05a4cd46e601864", - "00000000000dac6d782d266a37300c32", "000000000006d636bc1693351b980619", "0000000000036b1b5e0b499a8dcc030c", "000000000001b58daf05a4cd46e60186", - "000000000000dac6d782d266a37300c3", "0000000000006d636bc1693351b98061", "00000000000036b1b5e0b499a8dcc030", "0000000000001b58daf05a4cd46e6018", - "0000000000000dac6d782d266a37300c", "00000000000006d636bc1693351b9806", "000000000000036b1b5e0b499a8dcc03", "00000000000001b58daf05a4cd46e601", - "00000000000000dac6d782d266a37300", "000000000000006d636bc1693351b980", "0000000000000036b1b5e0b499a8dcc0", "000000000000001b58daf05a4cd46e60", - "000000000000000dac6d782d266a3730", "0000000000000006d636bc1693351b98", "00000000000000036b1b5e0b499a8dcc", "0000000000000001b58daf05a4cd46e6", - "0000000000000000dac6d782d266a373", "00000000000000006d636bc1693351b9", "000000000000000036b1b5e0b499a8dc", "00000000000000001b58daf05a4cd46e", - "00000000000000000dac6d782d266a37", "000000000000000006d636bc1693351b", "0000000000000000036b1b5e0b499a8d", "000000000000000001b58daf05a4cd46", - "000000000000000000dac6d782d266a3", "0000000000000000006d636bc1693351", "00000000000000000036b1b5e0b499a8", "0000000000000000001b58daf05a4cd4", - "0000000000000000000dac6d782d266a", "00000000000000000006d636bc169335", "000000000000000000036b1b5e0b499a", "00000000000000000001b58daf05a4cd", - "00000000000000000000dac6d782d266", "000000000000000000006d636bc16933", "0000000000000000000036b1b5e0b499", "000000000000000000001b58daf05a4c", - "000000000000000000000dac6d782d26", "0000000000000000000006d636bc1693", "00000000000000000000036b1b5e0b49", "0000000000000000000001b58daf05a4", - "0000000000000000000000dac6d782d2", "00000000000000000000006d636bc169", "000000000000000000000036b1b5e0b4", "00000000000000000000001b58daf05a", - "00000000000000000000000dac6d782d", "000000000000000000000006d636bc16", "0000000000000000000000036b1b5e0b", "000000000000000000000001b58daf05", - "000000000000000000000000dac6d782", "0000000000000000000000006d636bc1", "00000000000000000000000036b1b5e0", "0000000000000000000000001b58daf0", - "0000000000000000000000000dac6d78", "00000000000000000000000006d636bc", "000000000000000000000000036b1b5e", "00000000000000000000000001b58daf", - "00000000000000000000000000dac6d7", "000000000000000000000000006d636b", "0000000000000000000000000036b1b5", "000000000000000000000000001b58da", - "000000000000000000000000000dac6d", "0000000000000000000000000006d636", "00000000000000000000000000036b1b", "0000000000000000000000000001b58d", - "0000000000000000000000000000dac6", "00000000000000000000000000006d63", "000000000000000000000000000036b1", "00000000000000000000000000001b58", - "00000000000000000000000000000dac", "000000000000000000000000000006d6", "0000000000000000000000000000036b", "000000000000000000000000000001b5", - "000000000000000000000000000000da", "0000000000000000000000000000006d", "00000000000000000000000000000036", "0000000000000000000000000000001b", - "0000000000000000000000000000000d", "00000000000000000000000000000006", "00000000000000000000000000000003", "00000000000000000000000000000001", - "00000000000000000000000000000000", "00000000000000000000000000000000", "00000000000000000000000000000000", "00000000000000000000000000000000", - "00000000000000000000000000000000", "00000000000000000000000000000000", "00000000000000000000000000000000", "00000000000000000000000000000000", - "00000000000000000000000000000000", "00000000000000000000000000000000", "00000000000000000000000000000000", "00000000000000000000000000000000", - ] - - for i in 0 ..< 128: - let str1 = toHex(e shr i) - let str2 = strArray[i] - doAssert str1 == str2 - - let strArray2 = [ - "000dac6d782d266a37300c32591eee37", "001b58daf05a4cd46e601864b23ddc6e", "0036b1b5e0b499a8dcc030c9647bb8dc", "006d636bc1693351b9806192c8f771b8", - "00dac6d782d266a37300c32591eee370", "01b58daf05a4cd46e601864b23ddc6e0", "036b1b5e0b499a8dcc030c9647bb8dc0", "06d636bc1693351b9806192c8f771b80", - "0dac6d782d266a37300c32591eee3700", "1b58daf05a4cd46e601864b23ddc6e00", "36b1b5e0b499a8dcc030c9647bb8dc00", "6d636bc1693351b9806192c8f771b800", - "dac6d782d266a37300c32591eee37000", "b58daf05a4cd46e601864b23ddc6e000", "6b1b5e0b499a8dcc030c9647bb8dc000", "d636bc1693351b9806192c8f771b8000", - "ac6d782d266a37300c32591eee370000", "58daf05a4cd46e601864b23ddc6e0000", "b1b5e0b499a8dcc030c9647bb8dc0000", "636bc1693351b9806192c8f771b80000", - "c6d782d266a37300c32591eee3700000", "8daf05a4cd46e601864b23ddc6e00000", "1b5e0b499a8dcc030c9647bb8dc00000", "36bc1693351b9806192c8f771b800000", - "6d782d266a37300c32591eee37000000", "daf05a4cd46e601864b23ddc6e000000", "b5e0b499a8dcc030c9647bb8dc000000", "6bc1693351b9806192c8f771b8000000", - "d782d266a37300c32591eee370000000", "af05a4cd46e601864b23ddc6e0000000", "5e0b499a8dcc030c9647bb8dc0000000", "bc1693351b9806192c8f771b80000000", - "782d266a37300c32591eee3700000000", "f05a4cd46e601864b23ddc6e00000000", "e0b499a8dcc030c9647bb8dc00000000", "c1693351b9806192c8f771b800000000", - "82d266a37300c32591eee37000000000", "05a4cd46e601864b23ddc6e000000000", "0b499a8dcc030c9647bb8dc000000000", "1693351b9806192c8f771b8000000000", - "2d266a37300c32591eee370000000000", "5a4cd46e601864b23ddc6e0000000000", "b499a8dcc030c9647bb8dc0000000000", "693351b9806192c8f771b80000000000", - "d266a37300c32591eee3700000000000", "a4cd46e601864b23ddc6e00000000000", "499a8dcc030c9647bb8dc00000000000", "93351b9806192c8f771b800000000000", - "266a37300c32591eee37000000000000", "4cd46e601864b23ddc6e000000000000", "99a8dcc030c9647bb8dc000000000000", "3351b9806192c8f771b8000000000000", - "66a37300c32591eee370000000000000", "cd46e601864b23ddc6e0000000000000", "9a8dcc030c9647bb8dc0000000000000", "351b9806192c8f771b80000000000000", - "6a37300c32591eee3700000000000000", "d46e601864b23ddc6e00000000000000", "a8dcc030c9647bb8dc00000000000000", "51b9806192c8f771b800000000000000", - "a37300c32591eee37000000000000000", "46e601864b23ddc6e000000000000000", "8dcc030c9647bb8dc000000000000000", "1b9806192c8f771b8000000000000000", - "37300c32591eee370000000000000000", "6e601864b23ddc6e0000000000000000", "dcc030c9647bb8dc0000000000000000", "b9806192c8f771b80000000000000000", - "7300c32591eee3700000000000000000", "e601864b23ddc6e00000000000000000", "cc030c9647bb8dc00000000000000000", "9806192c8f771b800000000000000000", - "300c32591eee37000000000000000000", "601864b23ddc6e000000000000000000", "c030c9647bb8dc000000000000000000", "806192c8f771b8000000000000000000", - "00c32591eee370000000000000000000", "01864b23ddc6e0000000000000000000", "030c9647bb8dc0000000000000000000", "06192c8f771b80000000000000000000", - "0c32591eee3700000000000000000000", "1864b23ddc6e00000000000000000000", "30c9647bb8dc00000000000000000000", "6192c8f771b800000000000000000000", - "c32591eee37000000000000000000000", "864b23ddc6e000000000000000000000", "0c9647bb8dc000000000000000000000", "192c8f771b8000000000000000000000", - "32591eee370000000000000000000000", "64b23ddc6e0000000000000000000000", "c9647bb8dc0000000000000000000000", "92c8f771b80000000000000000000000", - "2591eee3700000000000000000000000", "4b23ddc6e00000000000000000000000", "9647bb8dc00000000000000000000000", "2c8f771b800000000000000000000000", - "591eee37000000000000000000000000", "b23ddc6e000000000000000000000000", "647bb8dc000000000000000000000000", "c8f771b8000000000000000000000000", - "91eee370000000000000000000000000", "23ddc6e0000000000000000000000000", "47bb8dc0000000000000000000000000", "8f771b80000000000000000000000000", - "1eee3700000000000000000000000000", "3ddc6e00000000000000000000000000", "7bb8dc00000000000000000000000000", "f771b800000000000000000000000000", - "eee37000000000000000000000000000", "ddc6e000000000000000000000000000", "bb8dc000000000000000000000000000", "771b8000000000000000000000000000", - "ee370000000000000000000000000000", "dc6e0000000000000000000000000000", "b8dc0000000000000000000000000000", "71b80000000000000000000000000000", - "e3700000000000000000000000000000", "c6e00000000000000000000000000000", "8dc00000000000000000000000000000", "1b800000000000000000000000000000", - "37000000000000000000000000000000", "6e000000000000000000000000000000", "dc000000000000000000000000000000", "b8000000000000000000000000000000", - "70000000000000000000000000000000", "e0000000000000000000000000000000", "c0000000000000000000000000000000", "80000000000000000000000000000000", - ] - - for i in 0 ..< 128: - let str1 = toHex(e shl i) - let str2 = strArray2[i] - doAssert str1 == str2 diff --git a/compiler/nimblecmd.nim b/compiler/nimblecmd.nim index fbc3fcee1..28398289c 100644 --- a/compiler/nimblecmd.nim +++ b/compiler/nimblecmd.nim @@ -134,30 +134,3 @@ proc nimblePath*(conf: ConfigRef; path: AbsoluteDir, info: TLineInfo) = if i != -1: conf.nimblePaths.delete(i) conf.nimblePaths.insert(path, 0) - -when isMainModule: - proc v(s: string): Version = s.newVersion - # #head is special in the sense that it's assumed to always be newest. - doAssert v"1.0" < v"#head" - doAssert v"1.0" < v"1.1" - doAssert v"1.0.1" < v"1.1" - doAssert v"1" < v"1.1" - doAssert v"#aaaqwe" < v"1.1" # We cannot assume that a branch is newer. - doAssert v"#a111" < v"#head" - - let conf = newConfigRef() - var rr = newStringTable() - addPackage conf, rr, "irc-#a111", unknownLineInfo - addPackage conf, rr, "irc-#head", unknownLineInfo - addPackage conf, rr, "irc-0.1.0", unknownLineInfo - #addPackage conf, rr, "irc", unknownLineInfo - #addPackage conf, rr, "another", unknownLineInfo - addPackage conf, rr, "another-0.1", unknownLineInfo - - addPackage conf, rr, "ab-0.1.3", unknownLineInfo - addPackage conf, rr, "ab-0.1", unknownLineInfo - addPackage conf, rr, "justone-1.0", unknownLineInfo - - doAssert toSeq(rr.chosen) == - @["irc-#head", "another-0.1", "ab-0.1.3", "justone-1.0"] - diff --git a/compiler/pathutils.nim b/compiler/pathutils.nim index 888e0a572..d8f3613b0 100644 --- a/compiler/pathutils.nim +++ b/compiler/pathutils.nim @@ -99,19 +99,3 @@ when true: proc addFileExt*(x: RelativeFile; ext: string): RelativeFile {.borrow.} proc writeFile*(x: AbsoluteFile; content: string) {.borrow.} - -when isMainModule: - doAssert AbsoluteDir"/Users/me///" / RelativeFile"z.nim" == AbsoluteFile"/Users/me/z.nim" - doAssert AbsoluteDir"/Users/me" / RelativeFile"../z.nim" == AbsoluteFile"/Users/z.nim" - doAssert AbsoluteDir"/Users/me/" / RelativeFile"../z.nim" == AbsoluteFile"/Users/z.nim" - doAssert relativePath("/foo/bar.nim", "/foo/", '/') == "bar.nim" - doAssert $RelativeDir"foo/bar" == "foo/bar" - doAssert RelativeDir"foo/bar" == RelativeDir"foo/bar" - doAssert RelativeFile"foo/bar".changeFileExt(".txt") == RelativeFile"foo/bar.txt" - doAssert RelativeFile"foo/bar".addFileExt(".txt") == RelativeFile"foo/bar.txt" - doAssert not RelativeDir"foo/bar".isEmpty - doAssert RelativeDir"".isEmpty - -when isMainModule and defined(windows): - let nasty = string(AbsoluteDir(r"C:\Users\rumpf\projects\nim\tests\nimble\nimbleDir\linkedPkgs\pkgB-#head\../../simplePkgs/pkgB-#head/") / RelativeFile"pkgA/module.nim") - doAssert nasty.replace('/', '\\') == r"C:\Users\rumpf\projects\nim\tests\nimble\nimbleDir\simplePkgs\pkgB-#head\pkgA\module.nim" diff --git a/compiler/prefixmatches.nim b/compiler/prefixmatches.nim index b03d1d76e..71ae9a844 100644 --- a/compiler/prefixmatches.nim +++ b/compiler/prefixmatches.nim @@ -54,36 +54,3 @@ proc prefixMatch*(p, s: string): PrefixMatch = else: return PrefixMatch.None return PrefixMatch.None - -when isMainModule: - import macros - - macro check(val, body: untyped): untyped = - result = newStmtList() - expectKind body, nnkStmtList - for b in body: - expectKind b, nnkPar - expectLen b, 2 - let p = b[0] - let s = b[1] - result.add quote do: - echo prefixMatch(`p`, `s`) == `val` - - check PrefixMatch.Prefix: - ("abc", "abc") - ("a", "abc") - ("xyz", "X_yzzzZe") - - check PrefixMatch.Substr: - ("b", "abc") - ("abc", "fooabcabc") - ("abC", "foo_AB_c") - - check PrefixMatch.Abbrev: - ("abc", "AxxxBxxxCxxx") - ("xyz", "X_yabcZe") - - check PrefixMatch.None: - ("foobar", "afkslfjd_as") - ("xyz", "X_yuuZuuZe") - ("ru", "remotes") diff --git a/tests/compiler/tbrees.nim b/tests/compiler/tbrees.nim new file mode 100644 index 000000000..364b51b22 --- /dev/null +++ b/tests/compiler/tbrees.nim @@ -0,0 +1,84 @@ +discard """ + output: ''' +www.amazon.com: 207.171.182.16 +www.apple.com: 17.112.152.32 +www.cnn.com: 64.236.16.20 +www.cs.princeton.edu: abc +www.dell.com: 143.166.224.230 +www.ebay.com: 66.135.192.87 +www.espn.com: 199.181.135.201 +www.google.com: 216.239.41.99 +www.microsoft.com: 207.126.99.140 +www.nytimes.com: 199.239.136.200 +www.princeton.edu: 128.112.128.15 +www.simpsons.com: 209.052.165.60 +www.slashdot.org: 66.35.250.151 +www.weather.com: 63.111.66.11 +www.yahoo.com: 216.109.118.65 +www.yale.edu: 130.132.143.21 +''' +""" + +import compiler/btrees + +import random, tables + +proc main = + var st = initBTree[string, string]() + st.add("www.cs.princeton.edu", "abc") + st.add("www.princeton.edu", "128.112.128.15") + st.add("www.yale.edu", "130.132.143.21") + st.add("www.simpsons.com", "209.052.165.60") + st.add("www.apple.com", "17.112.152.32") + st.add("www.amazon.com", "207.171.182.16") + st.add("www.ebay.com", "66.135.192.87") + st.add("www.cnn.com", "64.236.16.20") + st.add("www.google.com", "216.239.41.99") + st.add("www.nytimes.com", "199.239.136.200") + st.add("www.microsoft.com", "207.126.99.140") + st.add("www.dell.com", "143.166.224.230") + st.add("www.slashdot.org", "66.35.250.151") + st.add("www.espn.com", "199.181.135.201") + st.add("www.weather.com", "63.111.66.11") + st.add("www.yahoo.com", "216.109.118.65") + + assert st.getOrDefault("www.cs.princeton.edu") == "abc" + assert st.getOrDefault("www.harvardsucks.com") == "" + + assert st.getOrDefault("www.simpsons.com") == "209.052.165.60" + assert st.getOrDefault("www.apple.com") == "17.112.152.32" + assert st.getOrDefault("www.ebay.com") == "66.135.192.87" + assert st.getOrDefault("www.dell.com") == "143.166.224.230" + assert(st.len == 16) + + for k, v in st: + echo k, ": ", v + + when false: + var b2 = initBTree[string, string]() + const iters = 10_000 + for i in 1..iters: + b2.add($i, $(iters - i)) + for i in 1..iters: + let x = b2.getOrDefault($i) + if x != $(iters - i): + echo "got ", x, ", but expected ", iters - i + echo b2.len + + when true: + var b2 = initBTree[int, string]() + var t2 = initTable[int, string]() + const iters = 100_000 + for i in 1..iters: + let x = rand(high(int)) + if not t2.hasKey(x): + doAssert b2.getOrDefault(x).len == 0, " what, tree has this element " & $x + t2[x] = $x + b2.add(x, $x) + + doAssert(b2.len == t2.len, "unique entries " & $b2.len) + for k, v in t2: + doAssert $k == v + doAssert b2.getOrDefault(k) == $k + +main() diff --git a/tests/compiler/tint128.nim b/tests/compiler/tint128.nim new file mode 100644 index 000000000..a30b1b1cc --- /dev/null +++ b/tests/compiler/tint128.nim @@ -0,0 +1,184 @@ +import compiler/int128 + +let (a,b) = divMod(Ten,Ten) + +doAssert $One == "1" +doAssert $Ten == "10" +doAssert $Zero == "0" +let c = parseDecimalInt128("12345678989876543210123456789") +doAssert $c == "12345678989876543210123456789" + +var d : array[39, Int128] +d[0] = parseDecimalInt128("1") +d[1] = parseDecimalInt128("10") +d[2] = parseDecimalInt128("100") +d[3] = parseDecimalInt128("1000") +d[4] = parseDecimalInt128("10000") +d[5] = parseDecimalInt128("100000") +d[6] = parseDecimalInt128("1000000") +d[7] = parseDecimalInt128("10000000") +d[8] = parseDecimalInt128("100000000") +d[9] = parseDecimalInt128("1000000000") +d[10] = parseDecimalInt128("10000000000") +d[11] = parseDecimalInt128("100000000000") +d[12] = parseDecimalInt128("1000000000000") +d[13] = parseDecimalInt128("10000000000000") +d[14] = parseDecimalInt128("100000000000000") +d[15] = parseDecimalInt128("1000000000000000") +d[16] = parseDecimalInt128("10000000000000000") +d[17] = parseDecimalInt128("100000000000000000") +d[18] = parseDecimalInt128("1000000000000000000") +d[19] = parseDecimalInt128("10000000000000000000") +d[20] = parseDecimalInt128("100000000000000000000") +d[21] = parseDecimalInt128("1000000000000000000000") +d[22] = parseDecimalInt128("10000000000000000000000") +d[23] = parseDecimalInt128("100000000000000000000000") +d[24] = parseDecimalInt128("1000000000000000000000000") +d[25] = parseDecimalInt128("10000000000000000000000000") +d[26] = parseDecimalInt128("100000000000000000000000000") +d[27] = parseDecimalInt128("1000000000000000000000000000") +d[28] = parseDecimalInt128("10000000000000000000000000000") +d[29] = parseDecimalInt128("100000000000000000000000000000") +d[30] = parseDecimalInt128("1000000000000000000000000000000") +d[31] = parseDecimalInt128("10000000000000000000000000000000") +d[32] = parseDecimalInt128("100000000000000000000000000000000") +d[33] = parseDecimalInt128("1000000000000000000000000000000000") +d[34] = parseDecimalInt128("10000000000000000000000000000000000") +d[35] = parseDecimalInt128("100000000000000000000000000000000000") +d[36] = parseDecimalInt128("1000000000000000000000000000000000000") +d[37] = parseDecimalInt128("10000000000000000000000000000000000000") +d[38] = parseDecimalInt128("100000000000000000000000000000000000000") + +for i in 0..= 0: + doAssert d[i] div d[j] == d[i-j] + +var sum: Int128 + +for it in d: + sum += it + +doAssert $sum == "111111111111111111111111111111111111111" + +for it in d.mitems: + it = -it + +for i in 0..= 0: + doAssert d[i] div d[j] == -d[i-j] + +doAssert $high(Int128) == "170141183460469231731687303715884105727" +doAssert $low(Int128) == "-170141183460469231731687303715884105728" + +var ma = 100'i64 +var mb = 13 + +doAssert toInt128(ma) * toInt128(0) == toInt128(0) +doAssert toInt128(-ma) * toInt128(0) == toInt128(0) + +# sign correctness +doAssert divMod(toInt128( ma),toInt128( mb)) == (toInt128( ma div mb), toInt128( ma mod mb)) +doAssert divMod(toInt128(-ma),toInt128( mb)) == (toInt128(-ma div mb), toInt128(-ma mod mb)) +doAssert divMod(toInt128( ma),toInt128(-mb)) == (toInt128( ma div -mb), toInt128( ma mod -mb)) +doAssert divMod(toInt128(-ma),toInt128(-mb)) == (toInt128(-ma div -mb), toInt128(-ma mod -mb)) + +doAssert divMod(toInt128( mb),toInt128( mb)) == (toInt128( mb div mb), toInt128( mb mod mb)) +doAssert divMod(toInt128(-mb),toInt128( mb)) == (toInt128(-mb div mb), toInt128(-mb mod mb)) +doAssert divMod(toInt128( mb),toInt128(-mb)) == (toInt128( mb div -mb), toInt128( mb mod -mb)) +doAssert divMod(toInt128(-mb),toInt128(-mb)) == (toInt128(-mb div -mb), toInt128(-mb mod -mb)) + +doAssert divMod(toInt128( mb),toInt128( ma)) == (toInt128( mb div ma), toInt128( mb mod ma)) +doAssert divMod(toInt128(-mb),toInt128( ma)) == (toInt128(-mb div ma), toInt128(-mb mod ma)) +doAssert divMod(toInt128( mb),toInt128(-ma)) == (toInt128( mb div -ma), toInt128( mb mod -ma)) +doAssert divMod(toInt128(-mb),toInt128(-ma)) == (toInt128(-mb div -ma), toInt128(-mb mod -ma)) + +let e = parseDecimalInt128("70997106675279150998592376708984375") + +let strArray = [ + # toHex(e shr 0), toHex(e shr 1), toHex(e shr 2), toHex(e shr 3) + "000dac6d782d266a37300c32591eee37", "0006d636bc1693351b9806192c8f771b", "00036b1b5e0b499a8dcc030c9647bb8d", "0001b58daf05a4cd46e601864b23ddc6", + "0000dac6d782d266a37300c32591eee3", "00006d636bc1693351b9806192c8f771", "000036b1b5e0b499a8dcc030c9647bb8", "00001b58daf05a4cd46e601864b23ddc", + "00000dac6d782d266a37300c32591eee", "000006d636bc1693351b9806192c8f77", "0000036b1b5e0b499a8dcc030c9647bb", "000001b58daf05a4cd46e601864b23dd", + "000000dac6d782d266a37300c32591ee", "0000006d636bc1693351b9806192c8f7", "00000036b1b5e0b499a8dcc030c9647b", "0000001b58daf05a4cd46e601864b23d", + "0000000dac6d782d266a37300c32591e", "00000006d636bc1693351b9806192c8f", "000000036b1b5e0b499a8dcc030c9647", "00000001b58daf05a4cd46e601864b23", + "00000000dac6d782d266a37300c32591", "000000006d636bc1693351b9806192c8", "0000000036b1b5e0b499a8dcc030c964", "000000001b58daf05a4cd46e601864b2", + "000000000dac6d782d266a37300c3259", "0000000006d636bc1693351b9806192c", "00000000036b1b5e0b499a8dcc030c96", "0000000001b58daf05a4cd46e601864b", + "0000000000dac6d782d266a37300c325", "00000000006d636bc1693351b9806192", "000000000036b1b5e0b499a8dcc030c9", "00000000001b58daf05a4cd46e601864", + "00000000000dac6d782d266a37300c32", "000000000006d636bc1693351b980619", "0000000000036b1b5e0b499a8dcc030c", "000000000001b58daf05a4cd46e60186", + "000000000000dac6d782d266a37300c3", "0000000000006d636bc1693351b98061", "00000000000036b1b5e0b499a8dcc030", "0000000000001b58daf05a4cd46e6018", + "0000000000000dac6d782d266a37300c", "00000000000006d636bc1693351b9806", "000000000000036b1b5e0b499a8dcc03", "00000000000001b58daf05a4cd46e601", + "00000000000000dac6d782d266a37300", "000000000000006d636bc1693351b980", "0000000000000036b1b5e0b499a8dcc0", "000000000000001b58daf05a4cd46e60", + "000000000000000dac6d782d266a3730", "0000000000000006d636bc1693351b98", "00000000000000036b1b5e0b499a8dcc", "0000000000000001b58daf05a4cd46e6", + "0000000000000000dac6d782d266a373", "00000000000000006d636bc1693351b9", "000000000000000036b1b5e0b499a8dc", "00000000000000001b58daf05a4cd46e", + "00000000000000000dac6d782d266a37", "000000000000000006d636bc1693351b", "0000000000000000036b1b5e0b499a8d", "000000000000000001b58daf05a4cd46", + "000000000000000000dac6d782d266a3", "0000000000000000006d636bc1693351", "00000000000000000036b1b5e0b499a8", "0000000000000000001b58daf05a4cd4", + "0000000000000000000dac6d782d266a", "00000000000000000006d636bc169335", "000000000000000000036b1b5e0b499a", "00000000000000000001b58daf05a4cd", + "00000000000000000000dac6d782d266", "000000000000000000006d636bc16933", "0000000000000000000036b1b5e0b499", "000000000000000000001b58daf05a4c", + "000000000000000000000dac6d782d26", "0000000000000000000006d636bc1693", "00000000000000000000036b1b5e0b49", "0000000000000000000001b58daf05a4", + "0000000000000000000000dac6d782d2", "00000000000000000000006d636bc169", "000000000000000000000036b1b5e0b4", "00000000000000000000001b58daf05a", + "00000000000000000000000dac6d782d", "000000000000000000000006d636bc16", "0000000000000000000000036b1b5e0b", "000000000000000000000001b58daf05", + "000000000000000000000000dac6d782", "0000000000000000000000006d636bc1", "00000000000000000000000036b1b5e0", "0000000000000000000000001b58daf0", + "0000000000000000000000000dac6d78", "00000000000000000000000006d636bc", "000000000000000000000000036b1b5e", "00000000000000000000000001b58daf", + "00000000000000000000000000dac6d7", "000000000000000000000000006d636b", "0000000000000000000000000036b1b5", "000000000000000000000000001b58da", + "000000000000000000000000000dac6d", "0000000000000000000000000006d636", "00000000000000000000000000036b1b", "0000000000000000000000000001b58d", + "0000000000000000000000000000dac6", "00000000000000000000000000006d63", "000000000000000000000000000036b1", "00000000000000000000000000001b58", + "00000000000000000000000000000dac", "000000000000000000000000000006d6", "0000000000000000000000000000036b", "000000000000000000000000000001b5", + "000000000000000000000000000000da", "0000000000000000000000000000006d", "00000000000000000000000000000036", "0000000000000000000000000000001b", + "0000000000000000000000000000000d", "00000000000000000000000000000006", "00000000000000000000000000000003", "00000000000000000000000000000001", + "00000000000000000000000000000000", "00000000000000000000000000000000", "00000000000000000000000000000000", "00000000000000000000000000000000", + "00000000000000000000000000000000", "00000000000000000000000000000000", "00000000000000000000000000000000", "00000000000000000000000000000000", + "00000000000000000000000000000000", "00000000000000000000000000000000", "00000000000000000000000000000000", "00000000000000000000000000000000", +] + +for i in 0 ..< 128: + let str1 = toHex(e shr i) + let str2 = strArray[i] + doAssert str1 == str2 + +let strArray2 = [ + "000dac6d782d266a37300c32591eee37", "001b58daf05a4cd46e601864b23ddc6e", "0036b1b5e0b499a8dcc030c9647bb8dc", "006d636bc1693351b9806192c8f771b8", + "00dac6d782d266a37300c32591eee370", "01b58daf05a4cd46e601864b23ddc6e0", "036b1b5e0b499a8dcc030c9647bb8dc0", "06d636bc1693351b9806192c8f771b80", + "0dac6d782d266a37300c32591eee3700", "1b58daf05a4cd46e601864b23ddc6e00", "36b1b5e0b499a8dcc030c9647bb8dc00", "6d636bc1693351b9806192c8f771b800", + "dac6d782d266a37300c32591eee37000", "b58daf05a4cd46e601864b23ddc6e000", "6b1b5e0b499a8dcc030c9647bb8dc000", "d636bc1693351b9806192c8f771b8000", + "ac6d782d266a37300c32591eee370000", "58daf05a4cd46e601864b23ddc6e0000", "b1b5e0b499a8dcc030c9647bb8dc0000", "636bc1693351b9806192c8f771b80000", + "c6d782d266a37300c32591eee3700000", "8daf05a4cd46e601864b23ddc6e00000", "1b5e0b499a8dcc030c9647bb8dc00000", "36bc1693351b9806192c8f771b800000", + "6d782d266a37300c32591eee37000000", "daf05a4cd46e601864b23ddc6e000000", "b5e0b499a8dcc030c9647bb8dc000000", "6bc1693351b9806192c8f771b8000000", + "d782d266a37300c32591eee370000000", "af05a4cd46e601864b23ddc6e0000000", "5e0b499a8dcc030c9647bb8dc0000000", "bc1693351b9806192c8f771b80000000", + "782d266a37300c32591eee3700000000", "f05a4cd46e601864b23ddc6e00000000", "e0b499a8dcc030c9647bb8dc00000000", "c1693351b9806192c8f771b800000000", + "82d266a37300c32591eee37000000000", "05a4cd46e601864b23ddc6e000000000", "0b499a8dcc030c9647bb8dc000000000", "1693351b9806192c8f771b8000000000", + "2d266a37300c32591eee370000000000", "5a4cd46e601864b23ddc6e0000000000", "b499a8dcc030c9647bb8dc0000000000", "693351b9806192c8f771b80000000000", + "d266a37300c32591eee3700000000000", "a4cd46e601864b23ddc6e00000000000", "499a8dcc030c9647bb8dc00000000000", "93351b9806192c8f771b800000000000", + "266a37300c32591eee37000000000000", "4cd46e601864b23ddc6e000000000000", "99a8dcc030c9647bb8dc000000000000", "3351b9806192c8f771b8000000000000", + "66a37300c32591eee370000000000000", "cd46e601864b23ddc6e0000000000000", "9a8dcc030c9647bb8dc0000000000000", "351b9806192c8f771b80000000000000", + "6a37300c32591eee3700000000000000", "d46e601864b23ddc6e00000000000000", "a8dcc030c9647bb8dc00000000000000", "51b9806192c8f771b800000000000000", + "a37300c32591eee37000000000000000", "46e601864b23ddc6e000000000000000", "8dcc030c9647bb8dc000000000000000", "1b9806192c8f771b8000000000000000", + "37300c32591eee370000000000000000", "6e601864b23ddc6e0000000000000000", "dcc030c9647bb8dc0000000000000000", "b9806192c8f771b80000000000000000", + "7300c32591eee3700000000000000000", "e601864b23ddc6e00000000000000000", "cc030c9647bb8dc00000000000000000", "9806192c8f771b800000000000000000", + "300c32591eee37000000000000000000", "601864b23ddc6e000000000000000000", "c030c9647bb8dc000000000000000000", "806192c8f771b8000000000000000000", + "00c32591eee370000000000000000000", "01864b23ddc6e0000000000000000000", "030c9647bb8dc0000000000000000000", "06192c8f771b80000000000000000000", + "0c32591eee3700000000000000000000", "1864b23ddc6e00000000000000000000", "30c9647bb8dc00000000000000000000", "6192c8f771b800000000000000000000", + "c32591eee37000000000000000000000", "864b23ddc6e000000000000000000000", "0c9647bb8dc000000000000000000000", "192c8f771b8000000000000000000000", + "32591eee370000000000000000000000", "64b23ddc6e0000000000000000000000", "c9647bb8dc0000000000000000000000", "92c8f771b80000000000000000000000", + "2591eee3700000000000000000000000", "4b23ddc6e00000000000000000000000", "9647bb8dc00000000000000000000000", "2c8f771b800000000000000000000000", + "591eee37000000000000000000000000", "b23ddc6e000000000000000000000000", "647bb8dc000000000000000000000000", "c8f771b8000000000000000000000000", + "91eee370000000000000000000000000", "23ddc6e0000000000000000000000000", "47bb8dc0000000000000000000000000", "8f771b80000000000000000000000000", + "1eee3700000000000000000000000000", "3ddc6e00000000000000000000000000", "7bb8dc00000000000000000000000000", "f771b800000000000000000000000000", + "eee37000000000000000000000000000", "ddc6e000000000000000000000000000", "bb8dc000000000000000000000000000", "771b8000000000000000000000000000", + "ee370000000000000000000000000000", "dc6e0000000000000000000000000000", "b8dc0000000000000000000000000000", "71b80000000000000000000000000000", + "e3700000000000000000000000000000", "c6e00000000000000000000000000000", "8dc00000000000000000000000000000", "1b800000000000000000000000000000", + "37000000000000000000000000000000", "6e000000000000000000000000000000", "dc000000000000000000000000000000", "b8000000000000000000000000000000", + "70000000000000000000000000000000", "e0000000000000000000000000000000", "c0000000000000000000000000000000", "80000000000000000000000000000000", +] + +for i in 0 ..< 128: + let str1 = toHex(e shl i) + let str2 = strArray2[i] + doAssert str1 == str2 diff --git a/tests/compiler/tnimblecmd.nim b/tests/compiler/tnimblecmd.nim new file mode 100644 index 000000000..d39179be1 --- /dev/null +++ b/tests/compiler/tnimblecmd.nim @@ -0,0 +1,26 @@ +include compiler/[nimblecmd] + +proc v(s: string): Version = s.newVersion +# #head is special in the sense that it's assumed to always be newest. +doAssert v"1.0" < v"#head" +doAssert v"1.0" < v"1.1" +doAssert v"1.0.1" < v"1.1" +doAssert v"1" < v"1.1" +doAssert v"#aaaqwe" < v"1.1" # We cannot assume that a branch is newer. +doAssert v"#a111" < v"#head" + +let conf = newConfigRef() +var rr = newStringTable() +addPackage conf, rr, "irc-#a111", unknownLineInfo +addPackage conf, rr, "irc-#head", unknownLineInfo +addPackage conf, rr, "irc-0.1.0", unknownLineInfo +#addPackage conf, rr, "irc", unknownLineInfo +#addPackage conf, rr, "another", unknownLineInfo +addPackage conf, rr, "another-0.1", unknownLineInfo + +addPackage conf, rr, "ab-0.1.3", unknownLineInfo +addPackage conf, rr, "ab-0.1", unknownLineInfo +addPackage conf, rr, "justone-1.0", unknownLineInfo + +doAssert toSeq(rr.chosen) == + @["irc-#head", "ab-0.1.3", "justone-1.0", "another-0.1"] diff --git a/tests/compiler/tpathutils.nim b/tests/compiler/tpathutils.nim new file mode 100644 index 000000000..47ff0f06a --- /dev/null +++ b/tests/compiler/tpathutils.nim @@ -0,0 +1,18 @@ +import compiler/pathutils +import os, strutils + + +doAssert AbsoluteDir"/Users/me///" / RelativeFile"z.nim" == AbsoluteFile"/Users/me/z.nim" +doAssert AbsoluteDir"/Users/me" / RelativeFile"../z.nim" == AbsoluteFile"/Users/z.nim" +doAssert AbsoluteDir"/Users/me/" / RelativeFile"../z.nim" == AbsoluteFile"/Users/z.nim" +doAssert relativePath("/foo/bar.nim", "/foo/", '/') == "bar.nim" +doAssert $RelativeDir"foo/bar" == "foo/bar" +doAssert RelativeDir"foo/bar" == RelativeDir"foo/bar" +doAssert RelativeFile"foo/bar".changeFileExt(".txt") == RelativeFile"foo/bar.txt" +doAssert RelativeFile"foo/bar".addFileExt(".txt") == RelativeFile"foo/bar.txt" +doAssert not RelativeDir"foo/bar".isEmpty +doAssert RelativeDir"".isEmpty + +when defined(windows): + let nasty = string(AbsoluteDir(r"C:\Users\rumpf\projects\nim\tests\nimble\nimbleDir\linkedPkgs\pkgB-#head\../../simplePkgs/pkgB-#head/") / RelativeFile"pkgA/module.nim") + doAssert nasty.replace('/', '\\') == r"C:\Users\rumpf\projects\nim\tests\nimble\nimbleDir\simplePkgs\pkgB-#head\pkgA\module.nim" diff --git a/tests/compiler/tprefixmatches.nim b/tests/compiler/tprefixmatches.nim new file mode 100644 index 000000000..a89a6f613 --- /dev/null +++ b/tests/compiler/tprefixmatches.nim @@ -0,0 +1,32 @@ +import compiler/prefixmatches +import macros + +macro check(val, body: untyped): untyped = + result = newStmtList() + expectKind body, nnkStmtList + for b in body: + expectKind b, nnkPar + expectLen b, 2 + let p = b[0] + let s = b[1] + result.add quote do: + doAssert prefixMatch(`p`, `s`) == `val` + +check PrefixMatch.Prefix: + ("abc", "abc") + ("a", "abc") + ("xyz", "X_yzzzZe") + +check PrefixMatch.Substr: + ("b", "abc") + ("abc", "fooabcabc") + ("abC", "foo_AB_c") + +check PrefixMatch.Abbrev: + ("abc", "AxxxBxxxCxxx") + ("xyz", "X_yabcZe") + +check PrefixMatch.None: + ("foobar", "afkslfjd_as") + ("xyz", "X_yuuZuuZe") + ("ru", "remotes") From e220f756e795b972b72d4e8b9d729d1d9b73d2e2 Mon Sep 17 00:00:00 2001 From: Antonis Geralis <43617260+planetis-m@users.noreply.github.com> Date: Wed, 25 Nov 2020 11:15:34 +0200 Subject: [PATCH 1070/3768] fix for comparing infinities (#16122) --- lib/pure/math.nim | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/pure/math.nim b/lib/pure/math.nim index af31a5785..ccd766f46 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -177,6 +177,17 @@ proc almostEqual*[T: SomeFloat](x, y: T; unitsInLastPlace: Natural = 4): bool {. runnableExamples: doAssert almostEqual(3.141592653589793, 3.1415926535897936) doAssert almostEqual(1.6777215e7'f32, 1.6777216e7'f32) + doAssert almostEqual(Inf, Inf) + doAssert almostEqual(-Inf, -Inf) + doAssert almostEqual(Inf, -Inf) == false + doAssert almostEqual(-Inf, Inf) == false + doAssert almostEqual(Inf, NaN) == false + doAssert almostEqual(NaN, NaN) == false + + if x == y: + # short circuit exact equality -- needed to catch two infinities of + # the same sign. And perhaps speeds things up a bit sometimes. + return true let diff = abs(x - y) result = diff <= epsilon(T) * abs(x + y) * T(unitsInLastPlace) or diff < minimumPositiveValue(T) From 7848da7abe683f98019f54f9b99ce531c99e7eaf Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Wed, 25 Nov 2020 21:09:03 +0800 Subject: [PATCH 1071/3768] inc and dec are atomic in JS backend (#16129) --- lib/system.nim | 2 +- tests/system/tatomics1.nim | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 tests/system/tatomics1.nim diff --git a/lib/system.nim b/lib/system.nim index be41c1893..68385ef63 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1774,7 +1774,7 @@ when not defined(nimscript): when not declared(sysFatal): include "system/fatal" -when notJSnotNims: +when not defined(nimscript): {.push stackTrace: off, profiler: off.} proc atomicInc*(memLoc: var int, x: int = 1): int {.inline, diff --git a/tests/system/tatomics1.nim b/tests/system/tatomics1.nim new file mode 100644 index 000000000..88ab705a8 --- /dev/null +++ b/tests/system/tatomics1.nim @@ -0,0 +1,9 @@ +discard """ + target: "c cpp js" +""" + +var x = 10 +atomicInc(x) +doAssert x == 11 +atomicDec(x) +doAssert x == 10 From 3e7077ac7d2f4867ecabff09b730b6bc9356979d Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 25 Nov 2020 11:48:10 -0800 Subject: [PATCH 1072/3768] fix #14339, #13511, #14420: fixes limited VM support for addr (#16002) * fix #14339: fixes limited VM support for addr * strengthen test * reference bug #16003 * also fixes #13511 * also fixes #14420 --- compiler/vm.nim | 7 ++- tests/macros/tmacros_various.nim | 25 +++++++++ tests/misc/taddr.nim | 80 +++++++++++++++++++++++++++ tests/stdlib/tcritbits.nim | 92 +++++++++++++++++--------------- 4 files changed, 160 insertions(+), 44 deletions(-) diff --git a/compiler/vm.nim b/compiler/vm.nim index a6478f3f0..17bbe65fc 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -746,10 +746,13 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].regAddr = addr(regs[rb]) of opcAddrNode: decodeB(rkNodeAddr) - if regs[rb].kind == rkNode: + case regs[rb].kind + of rkNode: regs[ra].nodeAddr = addr(regs[rb].node) + of rkNodeAddr: # bug #14339 + regs[ra].nodeAddr = regs[rb].nodeAddr else: - stackTrace(c, tos, pc, "limited VM support for 'addr'") + stackTrace(c, tos, pc, "limited VM support for 'addr', got kind: " & $regs[rb].kind) of opcLdDeref: # a = b[] let ra = instr.regA diff --git a/tests/macros/tmacros_various.nim b/tests/macros/tmacros_various.nim index 056830837..00d74cb5c 100644 --- a/tests/macros/tmacros_various.nim +++ b/tests/macros/tmacros_various.nim @@ -245,3 +245,28 @@ xbenchmark: discard inputtest fastSHA("hey") + +block: # bug #13511 + type + Builder = ref object + components: seq[Component] + Component = object + + proc add(builder: var Builder, component: Component) {.compileTime.} = + builder.components.add(component) + + macro debugAst(arg: typed): untyped = + ## just for debugging purpose. + discard arg.treeRepr + return arg + + static: + var component = Component() + var builder = Builder() + + template foo(): untyped = + ## WAS: this doc comment causes compilation failure. + builder + + debugAst: + add(foo(), component) diff --git a/tests/misc/taddr.nim b/tests/misc/taddr.nim index 49a577798..d743d4b4a 100644 --- a/tests/misc/taddr.nim +++ b/tests/misc/taddr.nim @@ -123,3 +123,83 @@ block: block: # pending bug #15959 when false: proc byLent2[T](a: T): lent type(a[0]) = a[0] + +proc test14420() = # bug #14420 + # s/proc/template/ would hit bug #16005 + block: + type Foo = object + x: float + + proc fn(a: var Foo): var float = + ## WAS: discard <- turn this into a comment (or a `discard`) and error disappears + # result = a.x # this works + a.x # WAS: Error: limited VM support for 'addr' + + proc fn2(a: var Foo): var float = + result = a.x # this works + a.x # WAS: Error: limited VM support for 'addr' + + var a = Foo() + discard fn(a) + discard fn2(a) + + block: + proc byLent2[T](a: T): lent T = + runnableExamples: discard + a + proc byLent3[T](a: T): lent T = + runnableExamples: discard + result = a + var a = 10 + let x3 = byLent3(a) # works + let x2 = byLent2(a) # WAS: Error: internal error: genAddr: nkStmtListExpr + + block: + type MyOption[T] = object + case has: bool + of true: + value: T + of false: + discard + func some[T](val: T): MyOption[T] = + result = MyOption[T](has: true, value: val) + func get[T](opt: MyOption[T]): lent T = + doAssert opt.has + # result = opt.value # this was ok + opt.value # this had the bug + let x = some(10) + doAssert x.get() == 10 + +template test14339() = # bug #14339 + block: + type + Node = ref object + val: int + proc bar(c: Node): var int = + var n = c # was: Error: limited VM support for 'addr' + c.val + var a = Node() + discard a.bar() + block: + type + Node = ref object + val: int + proc bar(c: Node): var int = + var n = c + doAssert n.val == n[].val + n.val + var a = Node(val: 3) + a.bar() = 5 + when nimvm: + doAssert a.val == 5 + else: + when not defined(js): # pending bug #16003 + doAssert a.val == 5 + +template main = + # xxx wrap all other tests here like that so they're also tested in VM + test14420() + test14339() + +static: main() +main() diff --git a/tests/stdlib/tcritbits.nim b/tests/stdlib/tcritbits.nim index 51cbb7bf2..4b05cd950 100644 --- a/tests/stdlib/tcritbits.nim +++ b/tests/stdlib/tcritbits.nim @@ -1,61 +1,69 @@ -import sequtils, critbits +import std/[sequtils,critbits] +template main = + var r: CritBitTree[void] + r.incl "abc" + r.incl "xyz" + r.incl "def" + r.incl "definition" + r.incl "prefix" + r.incl "foo" -var r: CritBitTree[void] -r.incl "abc" -r.incl "xyz" -r.incl "def" -r.incl "definition" -r.incl "prefix" -r.incl "foo" + doAssert r.contains"def" -doAssert r.contains"def" + r.excl "def" + assert r.missingOrExcl("foo") == false + assert "foo" notin toSeq(r.items) -r.excl "def" -assert r.missingOrExcl("foo") == false -assert "foo" notin toSeq(r.items) + assert r.missingOrExcl("foo") == true -assert r.missingOrExcl("foo") == true + assert toSeq(r.items) == @["abc", "definition", "prefix", "xyz"] -assert toSeq(r.items) == @["abc", "definition", "prefix", "xyz"] + assert toSeq(r.itemsWithPrefix("de")) == @["definition"] + var c = CritBitTree[int]() -assert toSeq(r.itemsWithPrefix("de")) == @["definition"] -var c = CritBitTree[int]() + c.inc("a") + assert c["a"] == 1 -c.inc("a") -assert c["a"] == 1 + c.inc("a", 4) + assert c["a"] == 5 -c.inc("a", 4) -assert c["a"] == 5 + c.inc("a", -5) + assert c["a"] == 0 -c.inc("a", -5) -assert c["a"] == 0 + c.inc("b", 2) + assert c["b"] == 2 -c.inc("b", 2) -assert c["b"] == 2 + c.inc("c", 3) + assert c["c"] == 3 -c.inc("c", 3) -assert c["c"] == 3 + c.inc("a", 1) + assert c["a"] == 1 -c.inc("a", 1) -assert c["a"] == 1 + var cf = CritBitTree[float]() -var cf = CritBitTree[float]() + cf.incl("a", 1.0) + assert cf["a"] == 1.0 -cf.incl("a", 1.0) -assert cf["a"] == 1.0 + cf.incl("b", 2.0) + assert cf["b"] == 2.0 -cf.incl("b", 2.0) -assert cf["b"] == 2.0 + cf.incl("c", 3.0) + assert cf["c"] == 3.0 -cf.incl("c", 3.0) -assert cf["c"] == 3.0 + assert cf.len == 3 + cf.excl("c") + assert cf.len == 2 -assert cf.len == 3 -cf.excl("c") -assert cf.len == 2 + var cb: CritBitTree[string] + cb.incl("help", "help") + for k in cb.keysWithPrefix("helpp"): + doAssert false, "there is no prefix helpp" -var cb: CritBitTree[string] -cb.incl("help", "help") -for k in cb.keysWithPrefix("helpp"): - doAssert false, "there is no prefix helpp" + block: # bug #14339 + var strings: CritBitTree[int] + discard strings.containsOrIncl("foo", 3) + doAssert strings["foo"] == 3 + +main() +static: main() From da753c6a2eded5a382faf22dbf2a2ec1b1fc328f Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 26 Nov 2020 10:24:52 +0100 Subject: [PATCH 1073/3768] fixes #15076 (#16143) * fixes #15076 * heapqueue: optimized for ARC * added another test case [backport:1.4] * code cleanup --- compiler/injectdestructors.nim | 8 ++-- lib/pure/collections/heapqueue.nim | 10 ++-- tests/arc/tasyncleak4.nim | 21 +++++++++ tests/arc/tdestroy_in_loopcond.nim | 74 ++++++++++++++++++++++++++++++ 4 files changed, 105 insertions(+), 8 deletions(-) create mode 100644 tests/arc/tasyncleak4.nim create mode 100644 tests/arc/tdestroy_in_loopcond.nim diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 869e8ecc8..34c11e06c 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -37,7 +37,7 @@ type g: ControlFlowGraph graph: ModuleGraph otherRead: PNode - inLoop, inSpawn: int + inLoop, inSpawn, inLoopCond: int uninit: IntSet # set of uninit'ed vars uninitComputed: bool idgen: IdGenerator @@ -296,8 +296,8 @@ proc isNoInit(dest: PNode): bool {.inline.} = result = dest.kind == nkSym and sfNoInit in dest.sym.flags proc genSink(c: var Con; dest, ri: PNode, isDecl = false): PNode = - if isUnpackedTuple(dest) or isDecl or - (isAnalysableFieldAccess(dest, c.owner) and isFirstWrite(dest, c)) or + if (c.inLoopCond == 0 and (isUnpackedTuple(dest) or isDecl or + (isAnalysableFieldAccess(dest, c.owner) and isFirstWrite(dest, c)))) or isNoInit(dest): # optimize sink call into a bitwise memcopy result = newTree(nkFastAsgn, dest, ri) @@ -629,8 +629,10 @@ template handleNestedTempl(n, processCall: untyped, willProduceStmt = false) = of nkWhileStmt: inc c.inLoop + inc c.inLoopCond result = copyNode(n) result.add p(n[0], c, s, normal) + dec c.inLoopCond var bodyScope = nestedScope(s) let bodyResult = p(n[1], c, bodyScope, normal) result.add processScope(c, bodyScope, bodyResult) diff --git a/lib/pure/collections/heapqueue.nim b/lib/pure/collections/heapqueue.nim index 00e7a3b9d..30fa5dae8 100644 --- a/lib/pure/collections/heapqueue.nim +++ b/lib/pure/collections/heapqueue.nim @@ -65,7 +65,7 @@ proc len*[T](heap: HeapQueue[T]): int {.inline.} = ## Returns the number of elements of `heap`. heap.data.len -proc `[]`*[T](heap: HeapQueue[T], i: Natural): T {.inline.} = +proc `[]`*[T](heap: HeapQueue[T], i: Natural): lent T {.inline.} = ## Accesses the i-th element of `heap`. heap.data[i] @@ -111,7 +111,7 @@ proc siftup[T](heap: var HeapQueue[T], p: int) = heap.data[pos] = newitem siftdown(heap, startpos, pos) -proc push*[T](heap: var HeapQueue[T], item: T) = +proc push*[T](heap: var HeapQueue[T], item: sink T) = ## Pushes `item` onto heap, maintaining the heap invariant. heap.data.add(item) siftdown(heap, 0, len(heap)-1) @@ -168,7 +168,7 @@ proc del*[T](heap: var HeapQueue[T], index: Natural) = if index < newLen: heap.siftup(index) -proc replace*[T](heap: var HeapQueue[T], item: T): T = +proc replace*[T](heap: var HeapQueue[T], item: sink T): T = ## Pops and returns the current smallest value, and add the new item. ## This is more efficient than pop() followed by push(), and can be ## more appropriate when using a fixed-size heap. Note that the value @@ -186,7 +186,7 @@ proc replace*[T](heap: var HeapQueue[T], item: T): T = heap.data[0] = item siftup(heap, 0) -proc pushpop*[T](heap: var HeapQueue[T], item: T): T = +proc pushpop*[T](heap: var HeapQueue[T], item: sink T): T = ## Fast version of a push followed by a pop. runnableExamples: var heap = initHeapQueue[int]() @@ -197,7 +197,7 @@ proc pushpop*[T](heap: var HeapQueue[T], item: T): T = assert heap[0] == 6 assert heap.pushpop(4) == 4 result = item - if heap.len > 0 and heapCmp(heap.data[0], item): + if heap.len > 0 and heapCmp(heap.data[0], result): swap(result, heap.data[0]) siftup(heap, 0) diff --git a/tests/arc/tasyncleak4.nim b/tests/arc/tasyncleak4.nim new file mode 100644 index 000000000..58cd7f0b7 --- /dev/null +++ b/tests/arc/tasyncleak4.nim @@ -0,0 +1,21 @@ +discard """ + cmd: "nim c --gc:orc -d:useMalloc $file" + output: '''ok''' + valgrind: "leaks" +""" + +# bug #15076 +import asyncdispatch + +var futures: seq[Future[void]] + +for i in 1..20: + futures.add sleepAsync 1 + futures.add sleepAsync 1 + + futures.all.waitFor() + futures.setLen 0 + +setGlobalDispatcher nil +GC_fullCollect() +echo "ok" diff --git a/tests/arc/tdestroy_in_loopcond.nim b/tests/arc/tdestroy_in_loopcond.nim new file mode 100644 index 000000000..62532664d --- /dev/null +++ b/tests/arc/tdestroy_in_loopcond.nim @@ -0,0 +1,74 @@ +discard """ + output: '''400 true''' + cmd: "nim c --gc:orc $file" +""" + +type HeapQueue*[T] = object + data: seq[T] + + +proc len*[T](heap: HeapQueue[T]): int {.inline.} = + heap.data.len + +proc `[]`*[T](heap: HeapQueue[T], i: Natural): T {.inline.} = + heap.data[i] + +proc push*[T](heap: var HeapQueue[T], item: T) = + heap.data.add(item) + +proc pop*[T](heap: var HeapQueue[T]): T = + result = heap.data.pop + +proc clear*[T](heap: var HeapQueue[T]) = heap.data.setLen 0 + + +type + Future = ref object of RootObj + s: string + callme: proc() + +var called = 0 + +proc consume(f: Future) = + inc called + +proc newFuture(s: string): Future = + var r: Future + r = Future(s: s, callme: proc() = + consume r) + result = r + +var q: HeapQueue[tuple[finishAt: int64, fut: Future]] + +proc sleep(f: int64): Future = + q.push (finishAt: f, fut: newFuture("async-sleep")) + +proc processTimers = + # Pop the timers in the order in which they will expire (smaller `finishAt`). + var count = q.len + let t = high(int64) + while count > 0 and t >= q[0].finishAt: + q.pop().fut.callme() + dec count + +var futures: seq[Future] + +proc main = + for i in 1..200: + futures.add sleep(56020904056300) + futures.add sleep(56020804337500) + #futures.add sleep(2.0) + + #futures.add sleep(4.0) + + processTimers() + #q.pop()[1].callme() + #q.pop()[1].callme() + + futures.setLen 0 + + q.clear() + +main() +GC_fullCollect() +echo called, " ", getOccupiedMem() < 160 From 0b3a45692c26960f528f70d890f2a2d387aac120 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Thu, 26 Nov 2020 17:57:05 +0800 Subject: [PATCH 1074/3768] improve docs for prelude (#16135) * improve docs for prelude * better --- lib/prelude.nim | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/lib/prelude.nim b/lib/prelude.nim index d24f35513..47a5af89e 100644 --- a/lib/prelude.nim +++ b/lib/prelude.nim @@ -7,16 +7,17 @@ # distribution, for details about the copyright. # -# This is an include file that simply imports common modules for your -# convenience: -# -# .. code-block:: nim -# include prelude -# -# Same as: -# -# .. code-block:: nim -# import os, strutils, times, parseutils, parseopt, hashes, tables, sets +## This is an include file that simply imports common modules for your +## convenience: +## +## .. code-block:: nim +## include prelude +## +## Same as: +## +## .. code-block:: nim +## import os, strutils, times, parseutils, hashes, tables, sets +## when not defined(js): import parseopt import os, strutils, times, parseutils, hashes, tables, sets when not defined(js): import parseopt From e7e9007c4c061fa690c1978f118cf62badb19bd5 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Thu, 26 Nov 2020 18:08:53 +0800 Subject: [PATCH 1075/3768] ref #16054 undefine some stuff in JS backend (#16070) --- lib/system.nim | 91 +++++++++++++++++++++++++------------------------- 1 file changed, 46 insertions(+), 45 deletions(-) diff --git a/lib/system.nim b/lib/system.nim index 68385ef63..c13b419ef 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1897,53 +1897,54 @@ else: # however, stack-traces are available for most parts # of the code -var - globalRaiseHook*: proc (e: ref Exception): bool {.nimcall, benign.} - ## With this hook you can influence exception handling on a global level. - ## If not nil, every 'raise' statement ends up calling this hook. - ## - ## **Warning**: Ordinary application code should never set this hook! - ## You better know what you do when setting this. - ## - ## If ``globalRaiseHook`` returns false, the exception is caught and does - ## not propagate further through the call stack. +when notJSnotNims: + var + globalRaiseHook*: proc (e: ref Exception): bool {.nimcall, benign.} + ## With this hook you can influence exception handling on a global level. + ## If not nil, every 'raise' statement ends up calling this hook. + ## + ## **Warning**: Ordinary application code should never set this hook! + ## You better know what you do when setting this. + ## + ## If ``globalRaiseHook`` returns false, the exception is caught and does + ## not propagate further through the call stack. - localRaiseHook* {.threadvar.}: proc (e: ref Exception): bool {.nimcall, benign.} - ## With this hook you can influence exception handling on a - ## thread local level. - ## If not nil, every 'raise' statement ends up calling this hook. - ## - ## **Warning**: Ordinary application code should never set this hook! - ## You better know what you do when setting this. - ## - ## If ``localRaiseHook`` returns false, the exception - ## is caught and does not propagate further through the call stack. + localRaiseHook* {.threadvar.}: proc (e: ref Exception): bool {.nimcall, benign.} + ## With this hook you can influence exception handling on a + ## thread local level. + ## If not nil, every 'raise' statement ends up calling this hook. + ## + ## **Warning**: Ordinary application code should never set this hook! + ## You better know what you do when setting this. + ## + ## If ``localRaiseHook`` returns false, the exception + ## is caught and does not propagate further through the call stack. - outOfMemHook*: proc () {.nimcall, tags: [], benign, raises: [].} - ## Set this variable to provide a procedure that should be called - ## in case of an `out of memory`:idx: event. The standard handler - ## writes an error message and terminates the program. - ## - ## `outOfMemHook` can be used to raise an exception in case of OOM like so: - ## - ## .. code-block:: Nim - ## - ## var gOutOfMem: ref EOutOfMemory - ## new(gOutOfMem) # need to be allocated *before* OOM really happened! - ## gOutOfMem.msg = "out of memory" - ## - ## proc handleOOM() = - ## raise gOutOfMem - ## - ## system.outOfMemHook = handleOOM - ## - ## If the handler does not raise an exception, ordinary control flow - ## continues and the program is terminated. - unhandledExceptionHook*: proc (e: ref Exception) {.nimcall, tags: [], benign, raises: [].} - ## Set this variable to provide a procedure that should be called - ## in case of an `unhandle exception` event. The standard handler - ## writes an error message and terminates the program, except when - ## using `--os:any` + outOfMemHook*: proc () {.nimcall, tags: [], benign, raises: [].} + ## Set this variable to provide a procedure that should be called + ## in case of an `out of memory`:idx: event. The standard handler + ## writes an error message and terminates the program. + ## + ## `outOfMemHook` can be used to raise an exception in case of OOM like so: + ## + ## .. code-block:: Nim + ## + ## var gOutOfMem: ref EOutOfMemory + ## new(gOutOfMem) # need to be allocated *before* OOM really happened! + ## gOutOfMem.msg = "out of memory" + ## + ## proc handleOOM() = + ## raise gOutOfMem + ## + ## system.outOfMemHook = handleOOM + ## + ## If the handler does not raise an exception, ordinary control flow + ## continues and the program is terminated. + unhandledExceptionHook*: proc (e: ref Exception) {.nimcall, tags: [], benign, raises: [].} + ## Set this variable to provide a procedure that should be called + ## in case of an `unhandle exception` event. The standard handler + ## writes an error message and terminates the program, except when + ## using `--os:any` type PFrame* = ptr TFrame ## Represents a runtime frame of the call stack; From 8c12d3e29d06383f1752b24eeabcbf0439e920d5 Mon Sep 17 00:00:00 2001 From: cooldome Date: Thu, 26 Nov 2020 13:37:34 +0000 Subject: [PATCH 1076/3768] fix #16120 (#16145) --- compiler/sempass2.nim | 106 +++++++++++++++++++++--------------------- tests/arc/t14383.nim | 16 ++++++- 2 files changed, 68 insertions(+), 54 deletions(-) diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 39ba1b307..35b1473c5 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -755,63 +755,66 @@ proc trackCall(tracked: PEffects; n: PNode) = if n.typ != nil: if tracked.owner.kind != skMacro and n.typ.skipTypes(abstractVar).kind != tyOpenArray: createTypeBoundOps(tracked, n.typ, n.info) - if getConstExpr(tracked.ownerModule, n, tracked.c.idgen, tracked.graph) != nil: - return - if a.kind == nkCast and a[1].typ.kind == tyProc: - a = a[1] - # XXX: in rare situations, templates and macros will reach here after - # calling getAst(templateOrMacro()). Currently, templates and macros - # are indistinguishable from normal procs (both have tyProc type) and - # we can detect them only by checking for attached nkEffectList. - if op != nil and op.kind == tyProc and op.n[0].kind == nkEffectList: - if a.kind == nkSym: - if a.sym == tracked.owner: tracked.isRecursive = true - # even for recursive calls we need to check the lock levels (!): - mergeLockLevels(tracked, n, a.sym.getLockLevel) - if sfSideEffect in a.sym.flags: markSideEffect(tracked, a) - else: - mergeLockLevels(tracked, n, op.lockLevel) - var effectList = op.n[0] - if a.kind == nkSym and a.sym.kind == skMethod: - propagateEffects(tracked, n, a.sym) - elif isNoEffectList(effectList): - if isForwardedProc(a): - propagateEffects(tracked, n, a.sym) - elif isIndirectCall(a, tracked.owner): - assumeTheWorst(tracked, n, op) - gcsafeAndSideeffectCheck() - else: - mergeRaises(tracked, effectList[exceptionEffects], n) - mergeTags(tracked, effectList[tagEffects], n) - gcsafeAndSideeffectCheck() - if a.kind != nkSym or a.sym.magic != mNBindSym: - for i in 1.. 0: - createTypeBoundOps(tracked, n[1].typ.lastSon, n.info) - createTypeBoundOps(tracked, n[1].typ, n.info) - # new(x, finalizer): Problem: how to move finalizer into 'createTypeBoundOps'? + # check required for 'nim check': + if n[1].typ.len > 0: + createTypeBoundOps(tracked, n[1].typ.lastSon, n.info) + createTypeBoundOps(tracked, n[1].typ, n.info) + # new(x, finalizer): Problem: how to move finalizer into 'createTypeBoundOps'? - elif a.kind == nkSym and a.sym.magic in {mArrGet, mArrPut} and - optStaticBoundsCheck in tracked.currOptions: - checkBounds(tracked, n[1], n[2]) + elif a.kind == nkSym and a.sym.magic in {mArrGet, mArrPut} and + optStaticBoundsCheck in tracked.currOptions: + checkBounds(tracked, n[1], n[2]) + + if a.kind != nkSym or a.sym.magic != mRunnableExamples: + for i in 0.. 0 and a.sym.name.s[0] == '=' and tracked.owner.kind != skMacro: var opKind = find(AttachedOpToStr, a.sym.name.s.normalize) - if a.sym.name.s.normalize == "=": opKind = attachedAsgn.int + if a.sym.name.s == "=": opKind = attachedAsgn.int if opKind != -1: # rebind type bounds operations after createTypeBoundOps call let t = n[1].typ.skipTypes({tyAlias, tyVar}) @@ -821,9 +824,6 @@ proc trackCall(tracked: PEffects; n: PNode) = if op != nil: n[0].sym = op - if a.kind != nkSym or a.sym.magic != mRunnableExamples: - for i in 0.. Date: Thu, 26 Nov 2020 13:44:06 +0000 Subject: [PATCH 1077/3768] More on #16110 (#16130) * fix #16110 * refs #16110 * fix comment * Trigger build * use shallowCopy for efficiency --- compiler/semstmts.nim | 13 +++++++++++-- tests/macros/tgetimpl.nim | 27 +++++++++++++++++++++------ 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 713867b76..d7f66208e 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1278,8 +1278,17 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = incl st.flags, tfRefsAnonObj let obj = newSym(skType, getIdent(c.cache, s.name.s & ":ObjectType"), nextId c.idgen, getCurrOwner(c), s.info) - obj.ast = a.copyTree - obj.ast[0] = newSymNode(obj) + let symNode = newSymNode(obj) + obj.ast = a.shallowCopy + case a[0].kind + of nkSym: obj.ast[0] = symNode + of nkPragmaExpr: + obj.ast[0] = a[0].shallowCopy + obj.ast[0][0] = symNode + obj.ast[0][1] = a[0][1] + else: assert(false) + obj.ast[1] = a[1] + obj.ast[2] = a[2][0] if sfPure in s.flags: obj.flags.incl sfPure obj.typ = st.lastSon diff --git a/tests/macros/tgetimpl.nim b/tests/macros/tgetimpl.nim index de655561b..de132f253 100644 --- a/tests/macros/tgetimpl.nim +++ b/tests/macros/tgetimpl.nim @@ -71,15 +71,30 @@ assert: check_gen_proc(len(a)) == (false, true) macro check(x: type): untyped = let z = getType(x) - let y = getImpl(z[1]) - echo z.treeRepr + let y = getImpl(z[1]) + let sym = if y[0].kind == nnkSym: y[0] else: y[0][0] expectKind(z[1], nnkSym) - expectKind(y[0], nnkSym) - doAssert(y[0] == z[1]) + expectKind(sym, nnkSym) + expectKind(y[2], nnkObjectTy) + doAssert(sym == z[1]) type TirePtr = ptr object code: int -var z: TirePtr -check(typeof(z[])) \ No newline at end of file + TireRef* = ref object + code: int + + TireRef2* {.inheritable.} = ref object + code: int + + TireRef3* {.inheritable.} = object + code: int + +var z1: TirePtr +check(typeof(z1[])) +var z2: TireRef +check(typeof(z2[])) +var z3: TireRef2 +check(typeof(z3[])) +check(TireRef3) From cf516713d070277d1455a20a14de705a1513c0d7 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 26 Nov 2020 19:05:21 +0100 Subject: [PATCH 1078/3768] fixes https://github.com/status-im/nimbus-eth2/issues/1549 (#16146) * fixes https://github.com/status-im/nimbus-eth2/issues/1549 [backport:1.4] * test fixup --- compiler/ccgcalls.nim | 14 ++++++++------ compiler/cgen.nim | 8 ++++++++ tests/ccgbugs/targ_lefttoright.nim | 1 + 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 242e9f5cf..948f43ee1 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -249,13 +249,15 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode): Rope = else: internalError(p.config, "openArrayLoc: " & typeToString(a.t)) proc withTmpIfNeeded(p: BProc, a: TLoc, needsTmp: bool): TLoc = - if needsTmp and a.lode.typ != nil: - var tmp: TLoc - getTemp(p, a.lode.typ, tmp, needsInit=false) - genAssignment(p, tmp, a, {}) - tmp + # Bug https://github.com/status-im/nimbus-eth2/issues/1549 + # Aliasing is preferred over stack overflows. + # Also don't regress for non ARC-builds, too risky. + if needsTmp and a.lode.typ != nil and p.config.selectedGC in {gcArc, gcOrc} and + getSize(p.config, a.lode.typ) < 1024: + getTemp(p, a.lode.typ, result, needsInit=false) + genAssignment(p, result, a, {}) else: - a + result = a proc genArgStringToCString(p: BProc, n: PNode, needsTmp: bool): Rope {.inline.} = var a: TLoc diff --git a/compiler/cgen.nim b/compiler/cgen.nim index c2a31b775..15b85097c 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -471,6 +471,14 @@ proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) = result.storage = OnStack result.flags = {} constructLoc(p, result, not needsInit) + when false: + # XXX Introduce a compiler switch in order to detect these easily. + if getSize(p.config, t) > 1024 * 1024: + if p.prc != nil: + echo "ENORMOUS TEMPORARY! ", p.config $ p.prc.info + else: + echo "ENORMOUS TEMPORARY! ", p.config $ p.lastLineInfo + writeStackTrace() proc getTempCpp(p: BProc, t: PType, result: var TLoc; value: Rope) = inc(p.labels) diff --git a/tests/ccgbugs/targ_lefttoright.nim b/tests/ccgbugs/targ_lefttoright.nim index b107b2327..a49b87739 100644 --- a/tests/ccgbugs/targ_lefttoright.nim +++ b/tests/ccgbugs/targ_lefttoright.nim @@ -16,6 +16,7 @@ discard """ 2,2 1,2 ''' + cmd: "nim c --gc:orc $file" """ template test = From 4fdaded227070c651c4eb6a2517041a195617e64 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 26 Nov 2020 20:00:31 +0100 Subject: [PATCH 1079/3768] updated repr tests (#16147) --- tests/ccgbugs/tmissingderef.nim | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/ccgbugs/tmissingderef.nim b/tests/ccgbugs/tmissingderef.nim index 26418800a..eb7da3023 100644 --- a/tests/ccgbugs/tmissingderef.nim +++ b/tests/ccgbugs/tmissingderef.nim @@ -1,6 +1,5 @@ discard """ output: '''[10, 0, 0, 0, 0, 0, 0, 0] - 255 1 1 0.5''' From 70a1c4254230eb8f3c4d24ccbc253c0a28065d03 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Fri, 27 Nov 2020 03:30:19 +0800 Subject: [PATCH 1080/3768] add `**` to jsffi (#16141) * fix rope index * add testcase * fix ropes format * add `**` to jsffi * add testcase * changelog Co-authored-by: Andreas Rumpf --- changelog.md | 1 + lib/js/jsffi.nim | 1 + tests/js/tjsffi.nim | 34 +++++++++++++++++++++++++++++++++- 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index bfbe59e1a..ba1bc9438 100644 --- a/changelog.md +++ b/changelog.md @@ -47,6 +47,7 @@ - `repr` now doesn't insert trailing newline; previous behavior was very inconsistent, see #16034. Use `-d:nimLegacyReprWithNewline` for previous behavior. +- Added `**` to jsffi. - `writeStackTrace` is available in JS backend now. ## Language changes diff --git a/lib/js/jsffi.nim b/lib/js/jsffi.nim index 0dce6f3b9..eedf45bb0 100644 --- a/lib/js/jsffi.nim +++ b/lib/js/jsffi.nim @@ -180,6 +180,7 @@ proc `>` *(x, y: JsObject): JsObject {.importcpp: "(# > #)".} proc `<` *(x, y: JsObject): JsObject {.importcpp: "(# < #)".} proc `>=` *(x, y: JsObject): JsObject {.importcpp: "(# >= #)".} proc `<=` *(x, y: JsObject): JsObject {.importcpp: "(# <= #)".} +proc `**` *(x, y: JsObject): JsObject {.importcpp: "((#) ** #)".} proc `and`*(x, y: JsObject): JsObject {.importcpp: "(# && #)".} proc `or` *(x, y: JsObject): JsObject {.importcpp: "(# || #)".} proc `not`*(x: JsObject): JsObject {.importcpp: "(!#)".} diff --git a/tests/js/tjsffi.nim b/tests/js/tjsffi.nim index af33cee8a..143ac4a25 100644 --- a/tests/js/tjsffi.nim +++ b/tests/js/tjsffi.nim @@ -323,7 +323,6 @@ block: console.log jsarguments[0] block: - echo jsUndefined == jsNull echo jsUndefined == nil echo jsNull == nil @@ -331,3 +330,36 @@ block: echo jsNull.isNil echo jsNull.isNull echo jsUndefined.isUndefined + +block: # test ** + var a = toJs(0) + var b = toJs(0) + doAssert to(a ** b, int) == 1 + a = toJs(1) + b = toJs(1) + doAssert to(a ** b, int) == 1 + a = toJs(-1) + b = toJs(-1) + doAssert to(a ** b, int) == -1 + a = toJs(6) + b = toJs(6) + doAssert to(a ** b, int) == 46656 + a = toJs(5.5) + b = toJs(3) + doAssert to(a ** b, float) == 166.375 + a = toJs(5) + b = toJs(3.0) + doAssert to(a ** b, float) == 125.0 + a = toJs(7.0) + b = toJS(6.0) + doAssert to(a ** b, float) == 117649.0 + a = toJs(8) + b = toJS(-2) + doAssert to(a ** b, float) == 0.015625 + + a = toJs(1) + b = toJs(1) + doAssert to(`**`(a + a, b), int) == 2 + + doAssert to(`**`(toJs(1) + toJs(1), toJs(2)), int) == 4 + From 13b1b19a83000743df3c844a34652e7ad0d308f5 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 26 Nov 2020 11:54:18 -0800 Subject: [PATCH 1081/3768] remove all mentions of doc2, jsondoc2 (except 1 mentioning the alias) (#15683) --- compiler/options.nim | 2 +- compiler/typesrenderer.nim | 2 +- doc/advopt.txt | 4 ++-- doc/docgen.rst | 8 ++++---- doc/docstyle.rst | 2 +- doc/nimc.rst | 2 +- doc/tut2.rst | 2 +- tools/kochdocs.nim | 2 +- tools/nimweb.nim | 6 +++--- 9 files changed, 15 insertions(+), 15 deletions(-) diff --git a/compiler/options.nim b/compiler/options.nim index 58d09938c..9fce5f830 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -77,7 +77,7 @@ type # please make sure we have under 32 options optIdeTerse # idetools: use terse descriptions optExcessiveStackTrace # fully qualified module filenames optShowAllMismatches # show all overloading resolution candidates - optWholeProject # for 'doc2': output any dependency + optWholeProject # for 'doc': output any dependency optDocInternal # generate documentation for non-exported symbols optMixedMode # true if some module triggered C++ codegen optListFullPaths # use full paths in toMsgFilename diff --git a/compiler/typesrenderer.nim b/compiler/typesrenderer.nim index 613173252..0da05d70d 100644 --- a/compiler/typesrenderer.nim +++ b/compiler/typesrenderer.nim @@ -90,7 +90,7 @@ proc renderParamTypes(found: var seq[string], n: PNode) = ## Recursive helper, adds to `found` any types, or keeps diving the AST. ## ## The normal `doc` generator doesn't include .typ information, so the - ## function won't render types for parameters with default values. The `doc2` + ## function won't render types for parameters with default values. The `doc` ## generator does include the information. case n.kind of nkFormalParams: diff --git a/doc/advopt.txt b/doc/advopt.txt index 1fc0c946e..d87b00f91 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -73,7 +73,7 @@ Advanced options: --clibdir:DIR modify the linker library search path --clib:LIBNAME link an additional C library (you should omit platform-specific extensions) - --project document the whole project (doc2) + --project document the whole project (doc) --docRoot:path `nim doc --docRoot:/foo --project --outdir:docs /foo/sub/main.nim` generates: docs/sub/main.html if path == @pkg, will use nimble file enclosing dir @@ -85,7 +85,7 @@ Advanced options: --docCmd:cmd if `cmd == skip`, skips runnableExamples else, runs runnableExamples with given options, e.g.: `--docCmd:"-d:foo --threads:on"` - --docSeeSrcUrl:url activate 'see source' for doc and doc2 commands + --docSeeSrcUrl:url activate 'see source' for doc command (see doc.item.seesrc in config/nimdoc.cfg) --docInternal also generate documentation for non-exported symbols --lineDir:on|off generation of #line directive on|off diff --git a/doc/docgen.rst b/doc/docgen.rst index 07218982c..612465671 100644 --- a/doc/docgen.rst +++ b/doc/docgen.rst @@ -193,7 +193,7 @@ Index switch ------------ :: - nim doc2 --index:on filename.nim + nim doc --index:on filename.nim This will generate an index of all the exported symbols in the input Nim module, and put it into a neighboring file with the extension of ``.idx``. The @@ -210,7 +210,7 @@ See source switch ----------------- :: - nim doc2 --git.url: filename.nim + nim doc --git.url: filename.nim With the ``git.url`` switch the *See source* hyperlink will appear below each documented item in your source code pointing to the implementation of that @@ -262,8 +262,8 @@ HTML anchor generation When you run the ``rst2html`` command, all sections in the RST document will get an anchor you can hyperlink to. Usually, you can guess the anchor lower casing the section title and replacing spaces with dashes, and in any case, you -can get it from the table of contents. But when you run the ``doc`` or ``doc2`` -commands to generate API documentation, some symbol get one or two anchors at +can get it from the table of contents. But when you run the ``doc`` +command to generate API documentation, some symbol get one or two anchors at the same time: a numerical identifier, or a plain name plus a complex name. The numerical identifier is just a random number. The number gets assigned diff --git a/doc/docstyle.rst b/doc/docstyle.rst index 9a983097b..50ff81ac3 100644 --- a/doc/docstyle.rst +++ b/doc/docstyle.rst @@ -78,7 +78,7 @@ Whenever an example of usage would be helpful to the user, you should include on doAssert addThree(3, 125, 6) == -122 result = x +% y +% z -The commands ``nim doc`` and ``nim doc2`` will then correctly syntax highlight the Nim code within the documentation. +The command ``nim doc`` will then correctly syntax highlight the Nim code within the documentation. Types ----- diff --git a/doc/nimc.rst b/doc/nimc.rst index 57255a31f..f92e40617 100644 --- a/doc/nimc.rst +++ b/doc/nimc.rst @@ -516,7 +516,7 @@ can be read in the `Nim Backend Integration document `_. Nim documentation tools ======================= -Nim provides the `doc`:idx: and `doc2`:idx: commands to generate HTML +Nim provides the `doc`:idx: command to generate HTML documentation from ``.nim`` source files. Only exported symbols will appear in the output. For more details `see the docgen documentation `_. diff --git a/doc/tut2.rst b/doc/tut2.rst index 32faf452c..e3b00555d 100644 --- a/doc/tut2.rst +++ b/doc/tut2.rst @@ -457,7 +457,7 @@ If you want to add the ``{.raises.}`` pragma to existing code, the compiler can also help you. You can add the ``{.effects.}`` pragma statement to your proc and the compiler will output all inferred effects up to that point (exception tracking is part of Nim's effect system). Another more roundabout way to -find out the list of exceptions raised by a proc is to use the Nim ``doc2`` +find out the list of exceptions raised by a proc is to use the Nim ``doc`` command which generates documentation for a whole module and decorates all procs with the list of raised exceptions. You can read more about Nim's `effect system and related pragmas in the manual `_. diff --git a/tools/kochdocs.nim b/tools/kochdocs.nim index 81ab265f2..0cfedce47 100644 --- a/tools/kochdocs.nim +++ b/tools/kochdocs.nim @@ -265,7 +265,7 @@ proc buildDoc(nimArgs, destPath: string) = [extra, nimArgs2, gitUrl, destPath, d] i.inc for d in items(withoutIndex): - commands[i] = nim & " doc2 $# --git.url:$# -o:$# $#" % + commands[i] = nim & " doc $# --git.url:$# -o:$# $#" % [nimArgs, gitUrl, destPath / changeFileExt(splitFile(d).name, "html"), d] i.inc diff --git a/tools/nimweb.nim b/tools/nimweb.nim index 10319be87..f71b5f3be 100644 --- a/tools/nimweb.nim +++ b/tools/nimweb.nim @@ -326,7 +326,7 @@ proc buildDoc(c: var TConfigData, destPath: string) = destPath / changeFileExt(splitFile(d).name, "html"), d] i.inc for d in items(c.srcdoc2): - commands[i] = findNim(c) & " doc2 $# --git.url:$# -o:$# --index:on $#" % + commands[i] = findNim(c) & " doc $# --git.url:$# -o:$# --index:on $#" % [c.nimArgs, c.gitURL, destPath / changeFileExt(splitFile(d).name, "html"), d] i.inc @@ -361,7 +361,7 @@ proc buildAddDoc(c: var TConfigData, destPath: string) = # build additional documentation (without the index): var commands = newSeq[string](c.webdoc.len) for i, doc in pairs(c.webdoc): - commands[i] = findNim(c) & " doc2 $# --git.url:$# -o:$# $#" % + commands[i] = findNim(c) & " doc $# --git.url:$# -o:$# $#" % [c.nimArgs, c.gitURL, destPath / changeFileExt(splitFile(doc).name, "html"), doc] mexec(commands, c.numProcessors) @@ -537,7 +537,7 @@ proc json2(c: var TConfigData) = var i = 0 for d in items(c.srcdoc2): createDir(destPath / splitFile(d).dir) - commands[i] = findNim(c) & " jsondoc2 $# --git.url:$# -o:$# --index:on $#" % + commands[i] = findNim(c) & " jsondoc $# --git.url:$# -o:$# --index:on $#" % [c.nimArgs, c.gitURL, destPath / changeFileExt(d, "json"), d] i.inc From 9f1c5f64c5a9a3de5f4f39040b3df484de649ef2 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 26 Nov 2020 23:16:43 +0100 Subject: [PATCH 1082/3768] fixes #16119 [backport:1.4] (#16149) --- compiler/semstmts.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index d7f66208e..d3d8c0fb8 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -477,7 +477,7 @@ proc semLowerLetVarCustomPragma(c: PContext, a: PNode, n: PNode): PNode = proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = if n.len == 1: result = semLowerLetVarCustomPragma(c, n[0], n) - if result!=nil: return result + if result != nil: return result var b: PNode result = copyNode(n) @@ -513,7 +513,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = def = fitNode(c, typ, def, def.info) #changeType(def.skipConv, typ, check=true) else: - typ = def.typ.skipTypes({tyStatic}).skipIntLit(c.idgen) + typ = def.typ.skipTypes({tyStatic, tySink}).skipIntLit(c.idgen) if typ.kind in tyUserTypeClasses and typ.isResolvedUserTypeClass: typ = typ.lastSon if hasEmpty(typ): @@ -1282,7 +1282,7 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = obj.ast = a.shallowCopy case a[0].kind of nkSym: obj.ast[0] = symNode - of nkPragmaExpr: + of nkPragmaExpr: obj.ast[0] = a[0].shallowCopy obj.ast[0][0] = symNode obj.ast[0][1] = a[0][1] From 52829fc8d1324c8192bc1674ff47e504648675fa Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 26 Nov 2020 15:55:56 -0800 Subject: [PATCH 1083/3768] cmdline: improve command processing (#16056) --- compiler/ast.nim | 2 +- compiler/cgen.nim | 2 +- compiler/cmdlinehelper.nim | 16 ++---- compiler/commands.nim | 57 ++++++++++++++++++---- compiler/lookups.nim | 2 +- compiler/main.nim | 99 +++++++++++++------------------------- compiler/nim.nim | 8 +-- compiler/nimconf.nim | 2 +- compiler/nimeval.nim | 4 +- compiler/nimfix/nimfix.nim | 2 +- compiler/options.nim | 64 +++++++++++++----------- compiler/pragmas.nim | 2 +- compiler/scriptconfig.nim | 3 +- compiler/semexprs.nim | 4 +- compiler/semtypes.nim | 2 +- drnim/drnim.nim | 6 +-- nimsuggest/nimsuggest.nim | 4 +- tools/nimfind.nim | 2 +- 18 files changed, 144 insertions(+), 137 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 2e710fd21..fbad9e44e 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -765,7 +765,7 @@ type locOther # location is something other TLocFlag* = enum lfIndirect, # backend introduced a pointer - lfFullExternalName, # only used when 'conf.cmd == cmdPretty': Indicates + lfFullExternalName, # only used when 'conf.cmd == cmdNimfix': Indicates # that the symbol has been imported via 'importc: "fullname"' and # no format string. lfNoDeepCopy, # no need for a deep copy diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 15b85097c..a3d3425e3 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -1986,7 +1986,7 @@ proc writeModule(m: BModule, pending: bool) = var code = genModule(m, cf) if code != nil or m.config.symbolFiles != disabledSf: when hasTinyCBackend: - if m.config.cmd == cmdRun: + if m.config.cmd == cmdTcc: tccgen.compileCCode($code, m.config) onExit() return diff --git a/compiler/cmdlinehelper.nim b/compiler/cmdlinehelper.nim index 4f1ede5d1..c6a0f200a 100644 --- a/compiler/cmdlinehelper.nim +++ b/compiler/cmdlinehelper.nim @@ -13,8 +13,6 @@ import options, idents, nimconf, extccomp, commands, msgs, lineinfos, modulegraphs, condsyms, os, pathutils, parseopt -from strutils import normalize - proc prependCurDir*(f: AbsoluteFile): AbsoluteFile = when defined(unix): if os.isAbsolute(f.string): result = f @@ -60,8 +58,8 @@ proc processCmdLineAndProjectPath*(self: NimProg, conf: ConfigRef) = proc loadConfigsAndProcessCmdLine*(self: NimProg, cache: IdentCache; conf: ConfigRef; graph: ModuleGraph): bool = if self.suggestMode: - conf.command = "nimsuggest" - if conf.command == "e": + conf.setCmd cmdIdeTools + if conf.cmd == cmdNimscript: incl(conf.globalOptions, optWasNimscript) loadConfigs(DefaultConfig, cache, conf, graph.idgen) # load all config files @@ -69,17 +67,13 @@ proc loadConfigsAndProcessCmdLine*(self: NimProg, cache: IdentCache; conf: Confi let scriptFile = conf.projectFull.changeFileExt("nims") # 'nim foo.nims' means to just run the NimScript file and do nothing more: if fileExists(scriptFile) and scriptFile == conf.projectFull: - if conf.command == "": - conf.command = "e" - return false - elif conf.command.normalize == "e": - return false - + if conf.cmd == cmdNone: conf.setCmd cmdNimscript + if conf.cmd == cmdNimscript: return false # now process command line arguments again, because some options in the # command line can overwrite the config file's settings extccomp.initVars(conf) self.processCmdLine(passCmd2, "", conf) - if conf.command == "": + if conf.cmd == cmdNone: rawMessage(conf, errGenerated, "command missing") graph.suggestMode = self.suggestMode diff --git a/compiler/commands.nim b/compiler/commands.nim index 1ad5ec64c..916dfd66b 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -398,6 +398,48 @@ proc handleCmdInput*(conf: ConfigRef) = conf.projectName = "cmdfile" handleStdinOrCmdInput() +proc parseCommand*(command: string): Command = + case command.normalize + of "c", "cc", "compile", "compiletoc": cmdCompileToC + of "cpp", "compiletocpp": cmdCompileToCpp + of "objc", "compiletooc": cmdCompileToOC + of "js", "compiletojs": cmdCompileToJS + of "r": cmdCrun + of "run": cmdTcc + of "check": cmdCheck + of "e": cmdNimscript + of "doc0": cmdDoc0 + of "doc2", "doc": cmdDoc2 + of "rst2html": cmdRst2html + of "rst2tex": cmdRst2tex + of "jsondoc0": cmdJsondoc0 + of "jsondoc2", "jsondoc": cmdJsondoc + of "ctags": cmdCtags + of "buildindex": cmdBuildindex + of "gendepend": cmdGendepend + of "dump": cmdDump + of "parse": cmdParse + of "scan": cmdScan + of "secret": cmdInteractive + of "nop", "help": cmdNop + of "jsonscript": cmdJsonscript + else: cmdUnknown + +proc setCmd*(conf: ConfigRef, cmd: Command) = + ## sets cmd, backend so subsequent flags can query it (e.g. so --gc:arc can be ignored for backendJs) + # Note that `--backend` can override the backend, so the logic here must remain reversible. + conf.cmd = cmd + case cmd + of cmdCompileToC, cmdCrun, cmdTcc: conf.backend = backendC + of cmdCompileToCpp: conf.backend = backendCpp + of cmdCompileToOC: conf.backend = backendObjc + of cmdCompileToJS: conf.backend = backendJs + else: discard + +proc setCommandEarly*(conf: ConfigRef, command: string) = + conf.command = command + setCmd(conf, command.parseCommand) + proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; conf: ConfigRef) = var @@ -407,8 +449,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; expectArg(conf, switch, arg, pass, info) conf.projectIsCmd = true conf.cmdInput = arg # can be empty (a nim file with empty content is valid too) - if conf.command == "": - conf.command = "e" # better than "r" as a default + if conf.cmd == cmdNone: + conf.setCmd cmdNimscript # better than `cmdCrun` as a default conf.implicitCmd = true of "path", "p": expectArg(conf, switch, arg, pass, info) @@ -499,11 +541,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "project": processOnOffSwitchG(conf, {optWholeProject, optGenIndex}, arg, pass, info) of "gc": - if conf.backend == backendJs or conf.command == "js": - # for: bug #16033 - # This might still be imperfect, in rarse corner cases - # (where command is reset in nimscript, maybe). - return + if conf.backend == backendJs: return # for: bug #16033 expectArg(conf, switch, arg, pass, info) if pass in {passCmd2, passPP}: case arg.normalize @@ -955,13 +993,12 @@ proc processArgument*(pass: TCmdLinePass; p: OptParser; if argsCount == 0: # nim filename.nims is the same as "nim e filename.nims": if p.key.endsWith(".nims"): - config.command = "e" + config.setCmd cmdNimscript incl(config.globalOptions, optWasNimscript) config.projectName = unixToNativePath(p.key) config.arguments = cmdLineRest(p) result = true - elif pass != passCmd2: - config.command = p.key + elif pass != passCmd2: setCommandEarly(config, p.key) else: if pass == passCmd1: config.commandArgs.add p.key if argsCount == 1: diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 9225706b5..0c624425d 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -85,7 +85,7 @@ proc skipAlias*(s: PSym; n: PNode; conf: ConfigRef): PSym = result = s else: result = s.owner - if conf.cmd == cmdPretty: + if conf.cmd == cmdNimfix: prettybase.replaceDeprecated(conf, n.info, s, result) else: message(conf, n.info, warnDeprecated, "use " & result.name.s & " instead; " & diff --git a/compiler/main.nim b/compiler/main.nim index f9b0dd413..6bdbb9efd 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -87,7 +87,7 @@ proc commandCompileToC(graph: ModuleGraph) = if graph.config.errorCounter > 0: return # issue #9933 cgenWriteModules(graph.backend, conf) - if conf.cmd != cmdRun: + if conf.cmd != cmdTcc: extccomp.callCCompiler(conf) # for now we do not support writing out a .json file with the build instructions when HCR is on if not conf.hcrOn: @@ -195,12 +195,8 @@ proc mainCommand*(graph: ModuleGraph) = if conf.selectedGC in {gcArc, gcOrc} and conf.backend != backendCpp: conf.exc = excGoto - var commandAlreadyProcessed = false - - proc compileToBackend(backend: TBackend, cmd = cmdCompileToBackend) = - commandAlreadyProcessed = true - conf.cmd = cmd - customizeForBackend(backend) + proc compileToBackend() = + customizeForBackend(conf.backend) case conf.backend of backendC: commandCompileToC(graph) of backendCpp: commandCompileToC(graph) @@ -213,18 +209,13 @@ proc mainCommand*(graph: ModuleGraph) = quit "compiler wasn't built with documentation generator" else: wantMainModule(conf) - conf.cmd = cmdDoc loadConfigs(DocConfig, cache, conf, graph.idgen) defineSymbol(conf.symbols, "nimdoc") body block: ## command prepass - var docLikeCmd2 = false # includes what calls `docLikeCmd` + some more - case conf.command.normalize - of "r": conf.globalOptions.incl {optRun, optUseNimcache} - of "doc0", "doc2", "doc", "rst2html", "rst2tex", "jsondoc0", "jsondoc2", - "jsondoc", "ctags", "buildindex": docLikeCmd2 = true - else: discard + if conf.cmd == cmdCrun: conf.globalOptions.incl {optRun, optUseNimcache} + if conf.cmd notin cmdBackends + {cmdTcc}: customizeForBackend(backendC) if conf.outDir.isEmpty: # doc like commands can generate a lot of files (especially with --project) # so by default should not end up in $PWD nor in $projectPath. @@ -232,30 +223,22 @@ proc mainCommand*(graph: ModuleGraph) = var ret = if optUseNimcache in conf.globalOptions: getNimcacheDir(conf) else: conf.projectPath doAssert ret.string.isAbsolute # `AbsoluteDir` is not a real guarantee - if docLikeCmd2: ret = ret / htmldocsDir + if conf.cmd in cmdDocLike + {cmdRst2html, cmdRst2tex}: ret = ret / htmldocsDir ret - ## process all backend commands - case conf.command.normalize - of "c", "cc", "compile", "compiletoc": compileToBackend(backendC) # compile means compileToC currently - of "cpp", "compiletocpp": compileToBackend(backendCpp) - of "objc", "compiletooc": compileToBackend(backendObjc) - of "js", "compiletojs": compileToBackend(backendJs) - of "r": compileToBackend(backendC) # different from `"run"`! - of "run": + ## process all commands + case conf.cmd + of cmdBackends: compileToBackend() + of cmdTcc: when hasTinyCBackend: extccomp.setCC(conf, "tcc", unknownLineInfo) - if conf.backend notin {backendC, backendInvalid}: + if conf.backend != backendC: rawMessage(conf, errGenerated, "'run' requires c backend, got: '$1'" % $conf.backend) - compileToBackend(backendC, cmd = cmdRun) + compileToBackend() else: rawMessage(conf, errGenerated, "'run' command not available; rebuild with -d:tinyc") - else: customizeForBackend(backendC) # fallback for other commands - - ## process all other commands - case conf.command.normalize # synchronize with `cmdUsingHtmlDocs` - of "doc0": docLikeCmd commandDoc(cache, conf) - of "doc2", "doc": + of cmdDoc0: docLikeCmd commandDoc(cache, conf) + of cmdDoc2: docLikeCmd(): conf.setNoteDefaults(warnLockLevel, false) # issue #13218 conf.setNoteDefaults(warnRedefinitionOfLabel, false) # issue #13218 @@ -266,30 +249,25 @@ proc mainCommand*(graph: ModuleGraph) = commandDoc2(graph, false) if optGenIndex in conf.globalOptions and optWholeProject in conf.globalOptions: commandBuildIndex(conf, $conf.outDir) - of "rst2html": + of cmdRst2html: conf.setNoteDefaults(warnRedefinitionOfLabel, false) # similar to issue #13218 when defined(leanCompiler): quit "compiler wasn't built with documentation generator" else: - conf.cmd = cmdRst2html loadConfigs(DocConfig, cache, conf, graph.idgen) commandRst2Html(cache, conf) - of "rst2tex": + of cmdRst2tex: when defined(leanCompiler): quit "compiler wasn't built with documentation generator" else: - conf.cmd = cmdRst2tex loadConfigs(DocTexConfig, cache, conf, graph.idgen) commandRst2TeX(cache, conf) - of "jsondoc0": docLikeCmd commandJson(cache, conf) - of "jsondoc2", "jsondoc": docLikeCmd commandDoc2(graph, true) - of "ctags": docLikeCmd commandTags(cache, conf) - of "buildindex": docLikeCmd commandBuildIndex(conf, $conf.projectFull, conf.outFile) - of "gendepend": - conf.cmd = cmdGenDepend - commandGenDepend(graph) - of "dump": - conf.cmd = cmdDump + of cmdJsondoc0: docLikeCmd commandJson(cache, conf) + of cmdJsondoc: docLikeCmd commandDoc2(graph, true) + of cmdCtags: docLikeCmd commandTags(cache, conf) + of cmdBuildindex: docLikeCmd commandBuildIndex(conf, $conf.projectFull, conf.outFile) + of cmdGendepend: commandGenDepend(graph) + of cmdDump: if getConfigVar(conf, "dump.format") == "json": wantMainModule(conf) @@ -332,41 +310,30 @@ proc mainCommand*(graph: ModuleGraph) = msgWriteln(conf, "-- end of list --", {msgStdout, msgSkipHook}) for it in conf.searchPaths: msgWriteln(conf, it.string) - of "check": - conf.cmd = cmdCheck - commandCheck(graph) - of "parse": - conf.cmd = cmdParse + of cmdCheck: commandCheck(graph) + of cmdParse: wantMainModule(conf) discard parseFile(conf.projectMainIdx, cache, conf) - of "scan": - conf.cmd = cmdScan + of cmdScan: wantMainModule(conf) commandScan(cache, conf) msgWriteln(conf, "Beware: Indentation tokens depend on the parser's state!") - of "secret": - conf.cmd = cmdInteractive - commandInteractive(graph) - of "e": + of cmdInteractive: commandInteractive(graph) + of cmdNimscript: if conf.projectIsCmd or conf.projectIsStdin: discard elif not fileExists(conf.projectFull): rawMessage(conf, errGenerated, "NimScript file does not exist: " & conf.projectFull.string) elif not conf.projectFull.string.endsWith(".nims"): rawMessage(conf, errGenerated, "not a NimScript file: " & conf.projectFull.string) # main NimScript logic handled in `loadConfigs`. - of "nop", "help": - # prevent the "success" message: - conf.cmd = cmdDump - of "jsonscript": - conf.cmd = cmdJsonScript + of cmdNop: discard + of cmdJsonscript: setOutFile(graph.config) commandJsonScript(graph) - elif commandAlreadyProcessed: discard # already handled - else: + of cmdUnknown, cmdNone, cmdIdeTools, cmdNimfix: rawMessage(conf, errGenerated, "invalid command: " & conf.command) - if conf.errorCounter == 0 and - conf.cmd notin {cmdInterpret, cmdRun, cmdDump}: + if conf.errorCounter == 0 and conf.cmd notin {cmdTcc, cmdDump, cmdNop}: let mem = when declared(system.getMaxMem): formatSize(getMaxMem()) & " peakmem" else: formatSize(getTotalMem()) & " totmem" @@ -378,9 +345,9 @@ proc mainCommand*(graph: ModuleGraph) = let project = if optListFullPaths in conf.globalOptions: $conf.projectFull else: $conf.projectName var output: string - if optCompileOnly in conf.globalOptions and conf.cmd != cmdJsonScript: + if optCompileOnly in conf.globalOptions and conf.cmd != cmdJsonscript: output = $conf.jsonBuildFile - elif conf.outFile.isEmpty and conf.cmd notin {cmdJsonScript, cmdCompileToBackend, cmdDoc}: + elif conf.outFile.isEmpty and conf.cmd notin {cmdJsonscript} + cmdDocLike + cmdBackends: # for some cmd we expect a valid absOutFile output = "unknownOutput" else: diff --git a/compiler/nim.nim b/compiler/nim.nim index e3725f803..46654e352 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -64,7 +64,7 @@ proc processCmdLine(pass: TCmdLinePass, cmd: string; config: ConfigRef) = if processArgument(pass, p, argsCount, config): break if pass == passCmd2: if {optRun, optWasNimscript} * config.globalOptions == {} and - config.arguments.len > 0 and config.command.normalize notin ["run", "e", "r"]: + config.arguments.len > 0 and config.cmd notin {cmdTcc, cmdNimscript, cmdCrun}: rawMessage(config, errGenerated, errArgsNeedRunOption) proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = @@ -86,19 +86,19 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = #echo(GC_getStatistics()) if conf.errorCounter != 0: return when hasTinyCBackend: - if conf.cmd == cmdRun: + if conf.cmd == cmdTcc: tccgen.run(conf, conf.arguments) if optRun in conf.globalOptions: let output = conf.absOutFile case conf.cmd - of cmdCompileToBackend: + of cmdBackends, cmdTcc: var cmdPrefix = "" case conf.backend of backendC, backendCpp, backendObjc: discard of backendJs: cmdPrefix = findNodeJs() & " " else: doAssert false, $conf.backend execExternalProgram(conf, cmdPrefix & output.quoteShell & ' ' & conf.arguments) - of cmdDoc, cmdRst2html: + of cmdDocLike, cmdRst2html, cmdRst2tex: # bugfix(cmdRst2tex was missing) if conf.arguments.len > 0: # reserved for future use rawMessage(conf, errGenerated, "'$1 cannot handle arguments" % [$conf.cmd]) diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim index b6ec1bbc8..01a79c1e3 100644 --- a/compiler/nimconf.nim +++ b/compiler/nimconf.nim @@ -301,7 +301,7 @@ proc loadConfigs*(cfg: RelativeFile; cache: IdentCache; conf: ConfigRef; idgen: if scriptIsProj: showHintConf() configFiles.setLen 0 - if conf.command != "nimsuggest": + if conf.cmd != cmdIdeTools: runNimScriptIfExists(scriptFile, isMain = true) else: if not scriptIsProj: diff --git a/compiler/nimeval.nim b/compiler/nimeval.nim index 3ffe347c2..b93129852 100644 --- a/compiler/nimeval.nim +++ b/compiler/nimeval.nim @@ -10,7 +10,7 @@ ## exposes the Nim VM to clients. import ast, astalgo, modules, passes, condsyms, - options, sem, semdata, llstream, lineinfos, vm, + options, sem, llstream, lineinfos, vm, vmdef, modulegraphs, idents, os, pathutils, passaux, scriptconfig @@ -153,7 +153,7 @@ proc runRepl*(r: TLLRepl; conf.searchPaths.add(AbsoluteDir p) if conf.libpath.isEmpty: conf.libpath = AbsoluteDir p - conf.cmd = cmdInteractive + conf.cmd = cmdInteractive # see also `setCmd` conf.setErrorMaxHighMaybe initDefines(conf.symbols) defineSymbol(conf.symbols, "nimscript") diff --git a/compiler/nimfix/nimfix.nim b/compiler/nimfix/nimfix.nim index 96007477f..4d93279c9 100644 --- a/compiler/nimfix/nimfix.nim +++ b/compiler/nimfix/nimfix.nim @@ -38,7 +38,7 @@ In addition, all command line options of Nim are supported. proc mainCommand = registerPass verbosePass registerPass semPass - conf.cmd = cmdPretty + conf.setCmd cmdNimfix searchPaths.add options.libpath if gProjectFull.len != 0: # current path is always looked first for modules diff --git a/compiler/options.nim b/compiler/options.nim index 9fce5f830..42a907777 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -47,7 +47,7 @@ type # please make sure we have under 32 options TOptions* = set[TOption] TGlobalOption* = enum # **keep binary compatible** gloptNone, optForceFullMake, - optWasNimscript, + optWasNimscript, # redundant with `cmdNimscript`, could be removed optListCmd, optCompileOnly, optNoLinking, optCDebug, # turn on debugging information optGenDynLib, # generate a dynamic library @@ -110,34 +110,44 @@ type TBackend* = enum backendInvalid = "" # for parseEnum backendC = "c" - backendCpp = "cpp" # was cmdCompileToCpp - backendJs = "js" # was cmdCompileToJS - backendObjc = "objc" # was cmdCompileToOC + backendCpp = "cpp" + backendJs = "js" + backendObjc = "objc" # backendNimscript = "nimscript" # this could actually work # backendLlvm = "llvm" # probably not well supported; was cmdCompileToLLVM + Command* = enum ## Nim's commands + cmdNone # not yet processed command + cmdUnknown # command unmapped + cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToJS + cmdCrun # compile and run in nimache + cmdTcc # run the project via TCC backend + cmdCheck # semantic checking for whole project + cmdParse # parse a single file (for debugging) + cmdScan # scan a single file (for debugging) + cmdIdeTools # ide tools (e.g. nimsuggest) + cmdNimscript # evaluate nimscript + cmdDoc0 + cmdDoc2 + cmdRst2html # convert a reStructuredText file to HTML + cmdRst2tex # convert a reStructuredText file to TeX + cmdJsondoc0 + cmdJsondoc + cmdCtags + cmdBuildindex + cmdGendepend + cmdDump + cmdInteractive # start interactive session + cmdNop + cmdJsonscript # compile a .json build file + cmdNimfix + # old unused: cmdInterpret, cmdDef: def feature (find definition for IDEs) + +const + cmdBackends* = {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToJS, cmdCrun} + cmdDocLike* = {cmdDoc0, cmdDoc2, cmdJsondoc0, cmdJsondoc, cmdCtags, cmdBuildindex} + type - TCommands* = enum # Nim's commands - # **keep binary compatible** - cmdNone, - cmdCompileToC, # deadcode - cmdCompileToCpp, # deadcode - cmdCompileToOC, # deadcode - cmdCompileToJS, # deadcode - cmdCompileToLLVM, # deadcode - cmdInterpret, cmdPretty, cmdDoc, - cmdGenDepend, cmdDump, - cmdCheck, # semantic checking for whole project - cmdParse, # parse a single file (for debugging) - cmdScan, # scan a single file (for debugging) - cmdIdeTools, # ide tools - cmdDef, # def feature (find definition for IDEs) - cmdRst2html, # convert a reStructuredText file to HTML - cmdRst2tex, # convert a reStructuredText file to TeX - cmdInteractive, # start interactive session - cmdRun, # run the project via TCC backend - cmdJsonScript # compile a .json build file - cmdCompileToBackend, # compile to backend in TBackend TStringSeq* = seq[string] TGCMode* = enum # the selected GC gcUnselected, gcNone, gcBoehm, gcRegions, gcArc, gcOrc, @@ -245,7 +255,7 @@ type evalTemplateCounter*: int evalMacroCounter*: int exitcode*: int8 - cmd*: TCommands # the command + cmd*: Command # raw command parsed as enum cmdInput*: string # input command projectIsCmd*: bool # whether we're compiling from a command input implicitCmd*: bool # whether some flag triggered an implicit `command` @@ -531,7 +541,7 @@ proc isDefined*(conf: ConfigRef; symbol: string): bool = osDragonfly, osMacosx} else: discard -proc importantComments*(conf: ConfigRef): bool {.inline.} = conf.cmd in {cmdDoc, cmdIdeTools} +proc importantComments*(conf: ConfigRef): bool {.inline.} = conf.cmd notin cmdDocLike + {cmdIdeTools} proc usesWriteBarrier*(conf: ConfigRef): bool {.inline.} = conf.selectedGC >= gcRefc template compilationCachePresent*(conf: ConfigRef): untyped = diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 1ec0aa112..5ee3dc414 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -155,7 +155,7 @@ proc setExternName(c: PContext; s: PSym, extname: string, info: TLineInfo) = localError(c.config, info, "invalid extern name: '" & extname & "'. (Forgot to escape '$'?)") when hasFFI: s.cname = $s.loc.r - if c.config.cmd == cmdPretty and '$' notin extname: + if c.config.cmd == cmdNimfix and '$' notin extname: # note that '{.importc.}' is transformed into '{.importc: "$1".}' s.loc.flags.incl(lfFullExternalName) diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim index a353278d6..ef1ceb12b 100644 --- a/compiler/scriptconfig.nim +++ b/compiler/scriptconfig.nim @@ -150,7 +150,8 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string; cbconf cmpIgnoreCase: setResult(a, strutils.cmpIgnoreCase(a.getString 0, a.getString 1)) cbconf setCommand: - conf.command = a.getString 0 + conf.setCommandEarly(a.getString 0) + # xxx move remaining logic to commands.nim or other let arg = a.getString 1 incl(conf.globalOptions, optWasNimscript) if arg.len > 0: diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 608a82715..8728af27c 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2280,7 +2280,7 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = result = magicsAfterOverloadResolution(c, result, flags) of mRunnableExamples: markUsed(c, n.info, s) - if c.config.cmd == cmdDoc and n.len >= 2 and n.lastSon.kind == nkStmtList: + if c.config.cmd in cmdDocLike and n.len >= 2 and n.lastSon.kind == nkStmtList: when false: # some of this dead code was moved to `prepareExamples` if sfMainModule in c.module.flags: @@ -2724,7 +2724,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = let mode = if nfDotField in n.flags: {} else: {checkUndeclared} var s = qualifiedLookUp(c, n[0], mode) if s != nil: - #if c.config.cmd == cmdPretty and n[0].kind == nkDotExpr: + #if c.config.cmd == cmdNimfix and n[0].kind == nkDotExpr: # pretty.checkUse(n[0][1].info, s) case s.kind of skMacro, skTemplate: diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 0ec7140e0..a9d00d2ee 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -134,7 +134,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = e.position = int(counter) let symNode = newSymNode(e) if optNimV1Emulation notin c.config.globalOptions and identToReplace != nil and - c.config.cmd != cmdDoc: # A hack to produce documentation for enum fields. + c.config.cmd notin cmdDocLike: # A hack to produce documentation for enum fields. identToReplace[] = symNode if e.position == 0: hasNull = true if result.sym != nil and sfExported in result.sym.flags: diff --git a/drnim/drnim.nim b/drnim/drnim.nim index d68175c83..f2a20fa62 100644 --- a/drnim/drnim.nim +++ b/drnim/drnim.nim @@ -1223,9 +1223,7 @@ proc processCmdLine(pass: TCmdLinePass, cmd: string; config: ConfigRef) = var argsCount = 1 config.commandLine.setLen 0 - config.command = "check" - config.cmd = cmdCheck - + config.setCmd cmdCheck while true: parseopt.next(p) case p.kind @@ -1253,7 +1251,7 @@ proc processCmdLine(pass: TCmdLinePass, cmd: string; config: ConfigRef) = if processArgument(pass, p, argsCount, config): break if pass == passCmd2: if {optRun, optWasNimscript} * config.globalOptions == {} and - config.arguments.len > 0 and config.command.normalize notin ["run", "e"]: + config.arguments.len > 0 and config.cmd notin {cmdTcc, cmdNimscript}: rawMessage(config, errGenerated, errArgsNeedRunOption) proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = diff --git a/nimsuggest/nimsuggest.nim b/nimsuggest/nimsuggest.nim index d438e663c..2e28a09ee 100644 --- a/nimsuggest/nimsuggest.nim +++ b/nimsuggest/nimsuggest.nim @@ -517,7 +517,7 @@ proc mainCommand(graph: ModuleGraph) = clearPasses(graph) registerPass graph, verbosePass registerPass graph, semPass - conf.cmd = cmdIdeTools + conf.setCmd cmdIdeTools wantMainModule(conf) if not fileExists(conf.projectFull): @@ -666,7 +666,7 @@ else: clearPasses(graph) registerPass graph, verbosePass registerPass graph, semPass - conf.cmd = cmdIdeTools + conf.setCmd cmdIdeTools wantMainModule(conf) if not fileExists(conf.projectFull): diff --git a/tools/nimfind.nim b/tools/nimfind.nim index b120ccd1f..9c8247606 100644 --- a/tools/nimfind.nim +++ b/tools/nimfind.nim @@ -158,7 +158,7 @@ proc mainCommand(graph: ModuleGraph) = clearPasses(graph) registerPass graph, verbosePass registerPass graph, semPass - conf.cmd = cmdIdeTools + conf.setCmd cmdIdeTools wantMainModule(conf) setupDb(graph, dbfile) From c0215d8d0dcefaf963f6138e4fcf48dee8ef0199 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Fri, 27 Nov 2020 10:53:30 +0800 Subject: [PATCH 1084/3768] clean up changelog (#16155) * fix rope index * add testcase * fix ropes format * add `**` to jsffi * add testcase * changelog * clean up changelog Co-authored-by: Andreas Rumpf --- changelog.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index ba1bc9438..d62e96bbe 100644 --- a/changelog.md +++ b/changelog.md @@ -31,12 +31,12 @@ - Removed deprecated `iup` module from stdlib, it has already moved to [nimble](https://github.com/nim-lang/iup). -- nodejs now supports osenv: `getEnv`, `putEnv`, `envPairs`, `delEnv`, `existsEnv` +- `nodejs` backend now supports osenv: `getEnv`, `putEnv`, `envPairs`, `delEnv`, `existsEnv`. - `doAssertRaises` now correctly handles foreign exceptions. - Added `asyncdispatch.activeDescriptors` that returns the number of currently - active async event handles/file descriptors + active async event handles/file descriptors. - ``--gc:orc`` is now 10% faster than previously for common workloads. If you have trouble with its changed behavior, compile with ``-d:nimOldOrc``. @@ -44,15 +44,17 @@ - `os.FileInfo` (returned by `getFileInfo`) now contains `blockSize`, determining preferred I/O block size for this file object. + - `repr` now doesn't insert trailing newline; previous behavior was very inconsistent, see #16034. Use `-d:nimLegacyReprWithNewline` for previous behavior. - Added `**` to jsffi. + - `writeStackTrace` is available in JS backend now. ## Language changes -- `nimscript` now handles `except Exception as e` +- `nimscript` now handles `except Exception as e`. - The `cstring` doesn't support `[]=` operator in JS backend. From 6b77e16c6f86e5c05795fe881a25be3d17e9c22a Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Fri, 27 Nov 2020 12:41:22 +0800 Subject: [PATCH 1085/3768] tunidecode minor improvement (#16157) --- tests/stdlib/tunidecode.nim | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/stdlib/tunidecode.nim b/tests/stdlib/tunidecode.nim index 7784fffb4..be8e0523c 100644 --- a/tests/stdlib/tunidecode.nim +++ b/tests/stdlib/tunidecode.nim @@ -1,6 +1,5 @@ discard """ cmd: "nim $target --hints:on -d:embedUnidecodeTable $options $file" - output: "Ausserst" """ import unidecode @@ -9,5 +8,5 @@ import std/unidecode # #14112 loadUnidecodeTable("lib/pure/unidecode/unidecode.dat") -# assert unidecode("\x53\x17\x4E\xB0") == "Bei Jing" -echo unidecode("Äußerst") +doAssert unidecode("北京") == "Bei Jing " +doAssert unidecode("Äußerst") == "Ausserst" From 3510216c8afeee36e2997bd0f20a2b5f3c70a260 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Fri, 27 Nov 2020 18:57:16 +0800 Subject: [PATCH 1086/3768] add testcase (#16156) --- tests/statictypes/tstatictypes.nim | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/statictypes/tstatictypes.nim b/tests/statictypes/tstatictypes.nim index fe1d1c8ca..f3a0f0fcb 100644 --- a/tests/statictypes/tstatictypes.nim +++ b/tests/statictypes/tstatictypes.nim @@ -357,3 +357,12 @@ proc fn(N1: static int, N2: static int, T: typedesc): array[N1 * N2, T] = doAssert(len(result) == N1 * N2) let yy = fn(5, 10, float) + + +block: + block: + type Foo[N: static int] = array[cint(0) .. cint(N), float] + type T = Foo[3] + block: + type Foo[N: static int] = array[int32(0) .. int32(N), float] + type T = Foo[3] From c9a10bb9e47c5227b32f49f5876e965cc2308541 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 27 Nov 2020 15:26:57 +0100 Subject: [PATCH 1087/3768] fixes #16154; underlying system.add for seq is the real cause; will be addressed in a follow-up PR (#16161) --- lib/pure/asyncdispatch.nim | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 90345676f..e111f9e7d 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -1199,9 +1199,10 @@ else: not p.selector.isEmpty() or p.timers.len != 0 or p.callbacks.len != 0 proc prependSeq(dest: var seq[Callback]; src: sink seq[Callback]) = - let old = move dest + var old = move dest dest = src - dest.add old + for i in 0..high(old): + dest.add(move old[i]) proc processBasicCallbacks( fd: AsyncFD, event: Event From bc1db0d6f163e4aba51f27b2a8cefd05476bd5e4 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sat, 28 Nov 2020 03:47:49 +0800 Subject: [PATCH 1088/3768] move rest of tests to testament (#16140) * move rest of tests to testament * Update tests/stdlib/tsums.nim --- lib/packages/docutils/rstgen.nim | 6 -- lib/pure/sugar.nim | 97 -------------------------------- lib/std/editdistance.nim | 41 -------------- lib/std/sums.nim | 24 -------- lib/std/with.nim | 20 ------- lib/std/wordwrap.nim | 44 --------------- tests/stdlib/teditdistance.nim | 40 +++++++++++++ tests/stdlib/trstgen.nim | 7 ++- tests/stdlib/tsugar.nim | 94 +++++++++++++++++++++++++++++++ tests/stdlib/tsums.nim | 22 ++++++++ tests/stdlib/twith.nim | 23 ++++++++ tests/stdlib/twordwrap.nim | 43 ++++++++++++++ 12 files changed, 228 insertions(+), 233 deletions(-) create mode 100644 tests/stdlib/teditdistance.nim create mode 100644 tests/stdlib/tsugar.nim create mode 100644 tests/stdlib/tsums.nim create mode 100644 tests/stdlib/twith.nim create mode 100644 tests/stdlib/twordwrap.nim diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 604cc3ca9..47b4189ba 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -1364,9 +1364,3 @@ proc rstToLatex*(rstSource: string; options: RstParseOptions): string {.inline, var rstGenera: RstGenerator rstGenera.initRstGenerator(outLatex, defaultConfig(), "input", options) rstGenera.renderRstToOut(rstParse(rstSource, "", 1, 1, option, options), result) - - -when isMainModule: - assert rstToHtml("*Hello* **world**!", {}, - newStringTable(modeStyleInsensitive)) == - "Hello world!" diff --git a/lib/pure/sugar.nim b/lib/pure/sugar.nim index 46dbb1ece..d9c98c360 100644 --- a/lib/pure/sugar.nim +++ b/lib/pure/sugar.nim @@ -363,100 +363,3 @@ macro collect*(init, body: untyped): untyped {.since: (1, 1).} = for i in 1 ..< init.len: call.add init[i] result = newTree(nnkStmtListExpr, newVarStmt(res, call), resBody, res) - - -when isMainModule: - since (1, 1): - block dup_with_field: - type - Foo = object - col, pos: int - name: string - - proc inc_col(foo: var Foo) = inc(foo.col) - proc inc_pos(foo: var Foo) = inc(foo.pos) - proc name_append(foo: var Foo, s: string) = foo.name &= s - - let a = Foo(col: 1, pos: 2, name: "foo") - block: - let b = a.dup(inc_col, inc_pos): - _.pos = 3 - name_append("bar") - inc_pos - - doAssert(b == Foo(col: 2, pos: 4, name: "foobar")) - - block: - let b = a.dup(inc_col, pos = 3, name = "bar"): - name_append("bar") - inc_pos - - doAssert(b == Foo(col: 2, pos: 4, name: "barbar")) - - import algorithm - - var a = @[1, 2, 3, 4, 5, 6, 7, 8, 9] - doAssert dup(a, sort(_)) == sorted(a) - doAssert a.dup(sort) == sorted(a) - #Chaining: - var aCopy = a - aCopy.insert(10) - doAssert a.dup(insert(10)).dup(sort()) == sorted(aCopy) - - import random - - const b = @[0, 1, 2] - let c = b.dup shuffle() - doAssert c[0] == 1 - doAssert c[1] == 0 - - #test collect - import sets, tables - - let data = @["bird", "word"] # if this gets stuck in your head, its not my fault - assert collect(newSeq, for (i, d) in data.pairs: (if i mod 2 == 0: d)) == @["bird"] - assert collect(initTable(2), for (i, d) in data.pairs: {i: d}) == {0: "bird", - 1: "word"}.toTable - assert initHashSet.collect(for d in data.items: {d}) == data.toHashSet - - let x = collect(newSeqOfCap(4)): - for (i, d) in data.pairs: - if i mod 2 == 0: d - assert x == @["bird"] - - # bug #12874 - - let bug1 = collect( - newSeq, - for (i, d) in data.pairs:( - block: - if i mod 2 == 0: - d - else: - d & d - ) - ) - assert bug1 == @["bird", "wordword"] - - import strutils - let y = collect(newSeq): - for (i, d) in data.pairs: - try: parseInt(d) except: 0 - assert y == @[0, 0] - - let z = collect(newSeq): - for (i, d) in data.pairs: - case d - of "bird": "word" - else: d - assert z == @["word", "word"] - - - proc tforum = - let ans = collect(newSeq): - for y in 0..10: - if y mod 5 == 2: - for x in 0..y: - x - - tforum() diff --git a/lib/std/editdistance.nim b/lib/std/editdistance.nim index f365d4ab2..2c9203d64 100644 --- a/lib/std/editdistance.nim +++ b/lib/std/editdistance.nim @@ -264,44 +264,3 @@ proc editDistanceAscii*(a, b: string): int {.noSideEffect.} = if x > c3: x = c3 row[p] = x result = row[e] - - -when isMainModule: - doAssert editDistance("", "") == 0 - doAssert editDistance("kitten", "sitting") == 3 # from Wikipedia - doAssert editDistance("flaw", "lawn") == 2 # from Wikipedia - - doAssert editDistance("привет", "превет") == 1 - doAssert editDistance("Åge", "Age") == 1 - # editDistance, one string is longer in bytes, but shorter in rune length - # first string: 4 bytes, second: 6 bytes, but only 3 runes - doAssert editDistance("aaaa", "×××") == 4 - - block veryLongStringEditDistanceTest: - const cap = 256 - var - s1 = newStringOfCap(cap) - s2 = newStringOfCap(cap) - while len(s1) < cap: - s1.add 'a' - while len(s2) < cap: - s2.add 'b' - doAssert editDistance(s1, s2) == cap - - block combiningCodePointsEditDistanceTest: - const s = "A\xCC\x8Age" - doAssert editDistance(s, "Age") == 1 - - doAssert editDistanceAscii("", "") == 0 - doAssert editDistanceAscii("kitten", "sitting") == 3 # from Wikipedia - doAssert editDistanceAscii("flaw", "lawn") == 2 # from Wikipedia - - - assert(editDistance("prefix__hallo_suffix", "prefix__hallo_suffix") == 0) - assert(editDistance("prefix__hallo_suffix", "prefix__hallo_suffi1") == 1) - assert(editDistance("prefix__hallo_suffix", "prefix__HALLO_suffix") == 5) - assert(editDistance("prefix__hallo_suffix", "prefix__ha_suffix") == 3) - assert(editDistance("prefix__hallo_suffix", "prefix") == 14) - assert(editDistance("prefix__hallo_suffix", "suffix") == 14) - assert(editDistance("prefix__hallo_suffix", "prefix__hao_suffix") == 2) - assert(editDistance("main", "malign") == 2) diff --git a/lib/std/sums.nim b/lib/std/sums.nim index 0f8c5ebfb..cc964b91a 100644 --- a/lib/std/sums.nim +++ b/lib/std/sums.nim @@ -60,27 +60,3 @@ runnableExamples: const data = [1, 2, 3, 4, 5, 6, 7, 8, 9] doAssert sumKbn(data) == 45 doAssert sumPairs(data) == 45 - - -when isMainModule: - from math import pow - - var epsilon = 1.0 - while 1.0 + epsilon != 1.0: - epsilon /= 2.0 - let data = @[1.0, epsilon, -epsilon] - assert sumKbn(data) == 1.0 - assert sumPairs(data) != 1.0 # known to fail - assert (1.0 + epsilon) - epsilon != 1.0 - - var tc1: seq[float] - for n in 1 .. 1000: - tc1.add 1.0 / n.float - assert sumKbn(tc1) == 7.485470860550345 - assert sumPairs(tc1) == 7.485470860550345 - - var tc2: seq[float] - for n in 1 .. 1000: - tc2.add pow(-1.0, n.float) / n.float - assert sumKbn(tc2) == -0.6926474305598203 - assert sumPairs(tc2) == -0.6926474305598204 diff --git a/lib/std/with.nim b/lib/std/with.nim index ea26065a1..e6784478c 100644 --- a/lib/std/with.nim +++ b/lib/std/with.nim @@ -36,23 +36,3 @@ macro with*(arg: typed; calls: varargs[untyped]): untyped = result = newNimNode(nnkStmtList, arg) underscoredCalls(result, calls, arg) - -when isMainModule: - type - Foo = object - col, pos: string - name: string - - proc setColor(f: var Foo; r, g, b: int) = f.col = $(r, g, b) - proc setPosition(f: var Foo; x, y: float) = f.pos = $(x, y) - - var f: Foo - with(f, setColor(2, 3, 4), setPosition(0.0, 1.0)) - echo f - - f = Foo() - with f: - col = $(2, 3, 4) - pos = $(0.0, 1.0) - _.name = "bar" - echo f diff --git a/lib/std/wordwrap.nim b/lib/std/wordwrap.nim index 27df229bf..7dcfc7f59 100644 --- a/lib/std/wordwrap.nim +++ b/lib/std/wordwrap.nim @@ -72,47 +72,3 @@ proc wrapWords*(s: string, maxLineWidth = 80, for k in i..A1 headerA2 """ + + +assert rstToHtml("*Hello* **world**!", {}, + newStringTable(modeStyleInsensitive)) == + "Hello world!" diff --git a/tests/stdlib/tsugar.nim b/tests/stdlib/tsugar.nim new file mode 100644 index 000000000..a6fd70f34 --- /dev/null +++ b/tests/stdlib/tsugar.nim @@ -0,0 +1,94 @@ +import sugar + +block dup_with_field: + type + Foo = object + col, pos: int + name: string + + proc inc_col(foo: var Foo) = inc(foo.col) + proc inc_pos(foo: var Foo) = inc(foo.pos) + proc name_append(foo: var Foo, s: string) = foo.name &= s + + let a = Foo(col: 1, pos: 2, name: "foo") + block: + let b = a.dup(inc_col, inc_pos): + _.pos = 3 + name_append("bar") + inc_pos + + doAssert(b == Foo(col: 2, pos: 4, name: "foobar")) + + block: + let b = a.dup(inc_col, pos = 3, name = "bar"): + name_append("bar") + inc_pos + + doAssert(b == Foo(col: 2, pos: 4, name: "barbar")) + +import algorithm + +var a = @[1, 2, 3, 4, 5, 6, 7, 8, 9] +doAssert dup(a, sort(_)) == sorted(a) +doAssert a.dup(sort) == sorted(a) +#Chaining: +var aCopy = a +aCopy.insert(10) +doAssert a.dup(insert(10)).dup(sort()) == sorted(aCopy) + +import random + +const b = @[0, 1, 2] +let c = b.dup shuffle() +doAssert c.len == 3 + +#test collect +import sets, tables + +let data = @["bird", "word"] # if this gets stuck in your head, its not my fault +assert collect(newSeq, for (i, d) in data.pairs: (if i mod 2 == 0: d)) == @["bird"] +assert collect(initTable(2), for (i, d) in data.pairs: {i: d}) == {0: "bird", + 1: "word"}.toTable +assert initHashSet.collect(for d in data.items: {d}) == data.toHashSet + +let x = collect(newSeqOfCap(4)): + for (i, d) in data.pairs: + if i mod 2 == 0: d +assert x == @["bird"] + +# bug #12874 + +let bug1 = collect( + newSeq, + for (i, d) in data.pairs:( + block: + if i mod 2 == 0: + d + else: + d & d + ) +) +assert bug1 == @["bird", "wordword"] + +import strutils +let y = collect(newSeq): + for (i, d) in data.pairs: + try: parseInt(d) except: 0 +assert y == @[0, 0] + +let z = collect(newSeq): + for (i, d) in data.pairs: + case d + of "bird": "word" + else: d +assert z == @["word", "word"] + + +proc tforum = + let ans = collect(newSeq): + for y in 0..10: + if y mod 5 == 2: + for x in 0..y: + x + +tforum() diff --git a/tests/stdlib/tsums.nim b/tests/stdlib/tsums.nim new file mode 100644 index 000000000..979ffc391 --- /dev/null +++ b/tests/stdlib/tsums.nim @@ -0,0 +1,22 @@ +import std/sums +from math import pow + +var epsilon = 1.0 +while 1.0 + epsilon != 1.0: + epsilon /= 2.0 +let data = @[1.0, epsilon, -epsilon] +assert sumKbn(data) == 1.0 +# assert sumPairs(data) != 1.0 # known to fail in 64 bits +assert (1.0 + epsilon) - epsilon != 1.0 + +var tc1: seq[float] +for n in 1 .. 1000: + tc1.add 1.0 / n.float +assert sumKbn(tc1) == 7.485470860550345 +assert sumPairs(tc1) == 7.485470860550345 + +var tc2: seq[float] +for n in 1 .. 1000: + tc2.add pow(-1.0, n.float) / n.float +assert sumKbn(tc2) == -0.6926474305598203 +assert sumPairs(tc2) == -0.6926474305598204 diff --git a/tests/stdlib/twith.nim b/tests/stdlib/twith.nim new file mode 100644 index 000000000..80382f7c4 --- /dev/null +++ b/tests/stdlib/twith.nim @@ -0,0 +1,23 @@ +import std/with + +type + Foo = object + col, pos: string + name: string + +proc setColor(f: var Foo; r, g, b: int) = f.col = $(r, g, b) +proc setPosition(f: var Foo; x, y: float) = f.pos = $(x, y) + +var f: Foo +with(f, setColor(2, 3, 4), setPosition(0.0, 1.0)) +doAssert f.col == "(2, 3, 4)" +doAssert f.pos == "(0.0, 1.0)" + +f = Foo() +with f: + col = $(2, 3, 4) + pos = $(0.0, 1.0) + _.name = "bar" +doAssert f.col == "(2, 3, 4)" +doAssert f.pos == "(0.0, 1.0)" +doAssert f.name == "bar" diff --git a/tests/stdlib/twordwrap.nim b/tests/stdlib/twordwrap.nim new file mode 100644 index 000000000..c90dd9581 --- /dev/null +++ b/tests/stdlib/twordwrap.nim @@ -0,0 +1,43 @@ +import std/wordwrap + +when true: + let + inp = """ this is a long text -- muchlongerthan10chars and here + it goes""" + outp = " this is a\nlong text\n--\nmuchlongerthan10chars\nand here\nit goes" + doAssert wrapWords(inp, 10, false) == outp + + let + longInp = """ThisIsOneVeryLongStringWhichWeWillSplitIntoEightSeparatePartsNow""" + longOutp = "ThisIsOn\neVeryLon\ngStringW\nhichWeWi\nllSplitI\nntoEight\nSeparate\nPartsNow" + doAssert wrapWords(longInp, 8, true) == longOutp + +# test we don't break Umlauts into invalid bytes: +let fies = "äöüöäöüöäöüöäöüööäöüöäößßßßüöäößßßßßß" +let fiesRes = "ä\nö\nü\nö\nä\nö\nü\nö\nä\nö\nü\nö\nä\nö\nü\nö\nö\nä\nö\nü\nö\nä\nö\nß\nß\nß\nß\nü\nö\nä\nö\nß\nß\nß\nß\nß\nß" +doAssert wrapWords(fies, 1, true) == fiesRes + +let longlongword = """abc uitdaeröägfßhydüäpydqfü,träpydqgpmüdträpydföägpydörztdüöäfguiaeowäzjdtrüöäp psnrtuiydrözenrüöäpyfdqazpesnrtulocjtüö +äzydgyqgfqfgprtnwjlcydkqgfüöezmäzydydqüüöäpdtrnvwfhgckdumböäpydfgtdgfhtdrntdrntydfogiayqfguiatrnydrntüöärtniaoeydfgaoeiqfglwcßqfgxvlcwgtfhiaoen +rsüöäapmböäptdrniaoydfglckqfhouenrtsüöäptrniaoeyqfgulocfqclgwxßqflgcwßqfxglcwrniatrnmüböäpmöäbpümöäbpüöämpbaoestnriaesnrtdiaesrtdniaesdrtnaetdr +iaoenvlcyfglwckßqfgvwkßqgfvlwkßqfgvlwckßqvlwkgfUIαοιαοιαχολωχσωχνωκψρχκψρτιεαοσηζϵηζιοεννκεωνιαλωσωκνκψρκγτφγτχκγτεκργτιχνκιωχσιλωσλωχξλξλξωχωχ +ξχλωωχαοεοιαεοαεοιαεοαεοιαοεσναοεκνρκψγκψφϵιηαααοε""" +let longlongwordRes = """ +abc uitdaeröägfßhydüäpydqfü,träpydqgpmüdträpydföägpydörztdüöäfguiaeowäzjdtrüöäp +psnrtuiydrözenrüöäpyfdqazpesnrtulocjtüöäzydgyqgfqfgprtnwjlcydkqgfüöezmäzydydqüü +öäpdtrnvwfhgckdumböäpydfgtdgfhtdrntdrntydfogiayqfguiatrnydrntüöärtniaoeydfgaoeiq +fglwcßqfgxvlcwgtfhiaoenrsüöäapmböäptdrniaoydfglckqfhouenrtsüöäptrniaoeyqfgulocf +qclgwxßqflgcwßqfxglcwrniatrnmüböäpmöäbpümöäbpüöämpbaoestnriaesnrtdiaesrtdniaesdr +tnaetdriaoenvlcyfglwckßqfgvwkßqgfvlwkßqfgvlwckßqvlwkgfUIαοιαοιαχολωχσωχνωκψρχκψ +ρτιεαοσηζϵηζιοεννκεωνιαλωσωκνκψρκγτφγτχκγτεκργτιχνκιωχσιλωσλωχξλξλξωχωχ +ξχλωωχαοεοιαεοαεοιαεοαεοιαοεσναοεκνρκψγκψφϵιηαααοε""" +doAssert wrapWords(longlongword) == longlongwordRes + +# bug #14579 +const input60 = """ +This string is wrapped to 60 characters. If we call +wrapwords on it it will be re-wrapped to 80 characters. +""" +const input60Res = """This string is wrapped to 60 characters. If we call wrapwords on it it will be +re-wrapped to 80 characters.""" +doAssert wrapWords(input60) == input60Res \ No newline at end of file From c55552522710ef918e1adfd7a0f90ee0834b277f Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sat, 28 Nov 2020 04:04:36 +0800 Subject: [PATCH 1089/3768] fix #13115 (#15930) * fix #13115 * fix testament --- lib/system/ansi_c.nim | 5 +++++ lib/system/excpt.nim | 25 +++++++++++++++---------- tests/exception/t13115.nim | 13 +++++++++++++ 3 files changed, 33 insertions(+), 10 deletions(-) create mode 100644 tests/exception/t13115.nim diff --git a/lib/system/ansi_c.nim b/lib/system/ansi_c.nim index 0b4b25992..4839407c1 100644 --- a/lib/system/ansi_c.nim +++ b/lib/system/ansi_c.nim @@ -154,6 +154,11 @@ proc c_fwrite*(buf: pointer, size, n: csize_t, f: CFilePtr): cint {. proc c_fflush(f: CFilePtr): cint {. importc: "fflush", header: "".} +proc rawWriteString*(f: CFilePtr, s: cstring, length: int) {.compilerproc, nonReloadable, inline.} = + # we cannot throw an exception here! + discard c_fwrite(s, 1, cast[csize_t](length), f) + discard c_fflush(f) + proc rawWrite*(f: CFilePtr, s: cstring) {.compilerproc, nonReloadable, inline.} = # we cannot throw an exception here! discard c_fwrite(s, 1, cast[csize_t](s.len), f) diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index 04c5d3ce8..f73a88e30 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -26,13 +26,17 @@ when defined(windows): when not defined(windows) or not defined(guiapp): proc writeToStdErr(msg: cstring) = rawWrite(cstderr, msg) + proc writeToStdErr(msg: cstring, length: int) = + rawWriteString(cstderr, msg, length) else: proc MessageBoxA(hWnd: pointer, lpText, lpCaption: cstring, uType: int): int32 {. header: "", nodecl.} proc writeToStdErr(msg: cstring) = discard MessageBoxA(nil, msg, nil, 0) + proc writeToStdErr(msg: cstring, length: int) = + discard MessageBoxA(nil, msg, nil, 0) -proc showErrorMessage(data: cstring) {.gcsafe, raises: [].} = +proc showErrorMessage(data: cstring, length: int) {.gcsafe, raises: [].} = var toWrite = true if errorMessageWriter != nil: try: @@ -45,7 +49,7 @@ proc showErrorMessage(data: cstring) {.gcsafe, raises: [].} = # stderr not available by default, use the LOG session echo data else: - writeToStdErr(data) + writeToStdErr(data, length) proc chckIndx(i, a, b: int): int {.inline, compilerproc, benign.} proc chckRange(i, a, b: int): int {.inline, compilerproc, benign.} @@ -359,7 +363,7 @@ proc reportUnhandledErrorAux(e: ref Exception) {.nodestroy.} = if onUnhandledException != nil: onUnhandledException(buf) else: - showErrorMessage(buf) + showErrorMessage(buf, buf.len) `=destroy`(buf) else: # ugly, but avoids heap allocations :-) @@ -388,7 +392,7 @@ proc reportUnhandledErrorAux(e: ref Exception) {.nodestroy.} = if onUnhandledException != nil: onUnhandledException($tbuf()) else: - showErrorMessage(tbuf()) + showErrorMessage(tbuf(), L) proc reportUnhandledError(e: ref Exception) {.nodestroy.} = if unhandledExceptionHook != nil: @@ -500,9 +504,9 @@ proc writeStackTrace() = when hasSomeStackTrace: var s = "" rawWriteStackTrace(s) - cast[proc (s: cstring) {.noSideEffect, tags: [], nimcall, raises: [].}](showErrorMessage)(s) + cast[proc (s: cstring, length: int) {.noSideEffect, tags: [], nimcall, raises: [].}](showErrorMessage)(s, s.len) else: - cast[proc (s: cstring) {.noSideEffect, tags: [], nimcall, raises: [].}](showErrorMessage)("No stack traceback available\n") + cast[proc (s: cstring, length: int) {.noSideEffect, tags: [], nimcall, raises: [].}](showErrorMessage)("No stack traceback available\n", 32) proc getStackTrace(): string = when hasSomeStackTrace: @@ -535,10 +539,11 @@ const nimCallDepthLimit {.intdefine.} = 2000 proc callDepthLimitReached() {.noinline.} = writeStackTrace() - showErrorMessage("Error: call depth limit reached in a debug build (" & + let msg = "Error: call depth limit reached in a debug build (" & $nimCallDepthLimit & " function calls). You can change it with " & "-d:nimCallDepthLimit= but really try to avoid deep " & - "recursions instead.\n") + "recursions instead.\n" + showErrorMessage(msg, msg.len) quit(1) proc nimFrame(s: PFrame) {.compilerRtl, inl, raises: [].} = @@ -622,14 +627,14 @@ when not defined(noSignalHandler) and not defined(useNimRtl): var buf = newStringOfCap(2000) rawWriteStackTrace(buf) processSignal(sign, buf.add) # nice hu? currying a la Nim :-) - showErrorMessage(buf) + showErrorMessage(buf, buf.len) when not usesDestructors: GC_enable() else: var msg: cstring template asgn(y) = msg = y processSignal(sign, asgn) - showErrorMessage(msg) + showErrorMessage(msg, msg.len) quit(1) # always quit when SIGABRT proc registerSignalHandler() = diff --git a/tests/exception/t13115.nim b/tests/exception/t13115.nim new file mode 100644 index 000000000..9d88bbeda --- /dev/null +++ b/tests/exception/t13115.nim @@ -0,0 +1,13 @@ +discard """ + exitcode: 1 + targets: "c" + matrix: "-d:debug; -d:release" + outputsub: '''t13115.nim(13) t13115 +Error: unhandled exception: This char is''' + outputsub: ''' and works fine! [Exception]''' +""" + +const b_null: char = 0.char +var msg = "This char is `" & $b_null & "` and works fine!" + +raise newException(Exception, msg) \ No newline at end of file From 157e7820f1bf120d0015f3b564ccc45c42ab03f4 Mon Sep 17 00:00:00 2001 From: Antonis Geralis <43617260+planetis-m@users.noreply.github.com> Date: Sat, 28 Nov 2020 01:05:19 +0200 Subject: [PATCH 1090/3768] Add package manu to tested packages (#16121) * Add package manu to tested packages * Bump --- testament/important_packages.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index ffc626589..2da10e749 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -11,7 +11,7 @@ var packages2*: seq[tuple[name, cmd: string; url: string, useHead: bool]] = @[] # pkg1 "alea" pkg1 "argparse" pkg1 "arraymancer", "nim c tests/tests_cpu.nim" -#pkg1 "ast_pattern_matching", "nim c -r --oldgensym:on tests/test1.nim" +# pkg1 "ast_pattern_matching", "nim c -r --oldgensym:on tests/test1.nim" pkg1 "awk" pkg1 "bigints", url = "https://github.com/Araq/nim-bigints" pkg1 "binaryheap", "nim c -r binaryheap.nim" @@ -56,6 +56,7 @@ pkg1 "karax", "nim c -r tests/tester.nim" pkg1 "kdtree", "nimble test", "https://github.com/jblindsay/kdtree" pkg1 "loopfusion" pkg1 "macroutils" +pkg1 "manu" pkg1 "markdown" pkg1 "memo" pkg1 "msgpack4nim", "nim c -r tests/test_spec.nim" From b809562c7cc38e391c1e3240d8ca77ba035c3f75 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 28 Nov 2020 00:09:31 -0800 Subject: [PATCH 1091/3768] make megatest consistent with unjoined tests wrt newlines, honor newlines in output spec (#16151) * fix megatest newlines * still allow missing trailing newline for now but in a more strict way than before --- testament/categories.nim | 22 ++++---- testament/specs.nim | 2 +- testament/testament.nim | 14 +++-- tests/arc/tstrformat.nim | 4 +- tests/async/tasyncfilewrite.nim | 3 +- tests/ccgbugs/tret_arg_init.nim | 2 + tests/concepts/treversable.nim | 2 +- tests/destructor/ttuple.nim | 2 +- tests/exception/texcas.nim | 5 +- tests/exception/texceptions.nim | 1 + tests/generics/treentranttypes.nim | 8 --- tests/js/tunittests.nim | 4 +- tests/lookups/test.nim | 2 +- tests/macros/tastrepr.nim | 1 + tests/magics/t10307.nim | 5 +- tests/metatype/tmatrix3.nim | 2 +- tests/metatype/ttensor.nim | 6 +- tests/misc/temptyecho.nim | 4 ++ tests/objects/tobject.nim | 2 +- tests/overload/tstatic_with_converter.nim | 5 +- tests/specialops/tdotops.nim | 4 +- tests/statictypes/tpassthruarith.nim | 4 -- tests/stdlib/talgorithm.nim | 2 +- tests/stdlib/tcgi.nim | 1 + tests/stdlib/tmath.nim | 5 +- tests/stdlib/tnet_ll.nim | 1 + tests/stdlib/tnre.nim | 4 +- tests/stdlib/tparseuints.nim | 13 +++-- tests/stdlib/trst.nim | 1 + tests/stdlib/tsequtils.nim | 3 - tests/stdlib/tunittest.nim | 68 +++++++++++------------ tests/system/tenum_array_repr.nim | 2 - tests/template/tgensymregression.nim | 2 - 33 files changed, 110 insertions(+), 96 deletions(-) diff --git a/testament/categories.nim b/testament/categories.nim index 0d19842ec..4f6e40659 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -591,12 +591,14 @@ proc isJoinableSpec(spec: TSpec): bool = if spec.file.readFile.contains "when isMainModule": result = false -proc norm(s: var string) = - while true: - let tmp = s.replace("\n\n", "\n") - if tmp == s: break - s = tmp - s = s.strip +when false: + proc norm(s: var string) = + ## strip empty newlines + while true: + let tmp = s.replace("\n\n", "\n") + if tmp == s: break + s = tmp + s = s.strip proc quoted(a: string): string = # todo: consider moving to system.nim @@ -654,16 +656,16 @@ proc runJoinedTest(r: var TResults, cat: Category, testsDir: string) = echo buf.string quit(failString & "megatest execution failed") - norm buf.string const outputExceptedFile = "outputExpected.txt" const outputGottenFile = "outputGotten.txt" writeFile(outputGottenFile, buf.string) var outputExpected = "" for i, runSpec in specs: outputExpected.add marker & runSpec.file & "\n" - outputExpected.add runSpec.output.strip - outputExpected.add '\n' - norm outputExpected + if runSpec.output.len > 0: + outputExpected.add runSpec.output + if not runSpec.output.endsWith "\n": + outputExpected.add '\n' if buf.string != outputExpected: writeFile(outputExceptedFile, outputExpected) diff --git a/testament/specs.nim b/testament/specs.nim index eaae598bb..37fe12275 100644 --- a/testament/specs.nim +++ b/testament/specs.nim @@ -280,7 +280,7 @@ proc parseSpec*(filename: string): TSpec = of "output": if result.outputCheck != ocSubstr: result.outputCheck = ocEqual - result.output = strip(e.value) + result.output = e.value of "input": result.input = e.value of "outputsub": diff --git a/testament/testament.nim b/testament/testament.nim index 2912b03ab..94f66c2db 100644 --- a/testament/testament.nim +++ b/testament/testament.nim @@ -469,6 +469,10 @@ proc checkDisabled(r: var TResults, test: TTest): bool = var count = 0 +proc equalModuloLastNewline(a, b: string): bool = + # allow lazy output spec that omits last newline, but really those should be fixed instead + result = a == b or b.endsWith("\n") and a == b[0 ..< ^1] + proc testSpecHelper(r: var TResults, test: var TTest, expected: TSpec, target: TTarget, nimcache: string, extraOptions = "") = test.startTime = epochTime() @@ -513,16 +517,18 @@ proc testSpecHelper(r: var TResults, test: var TTest, expected: TSpec, if exitCode != 0: exitCode = 1 let bufB = if expected.sortoutput: - var x = splitLines(strip(buf.string)) + var buf2 = buf.string + buf2.stripLineEnd + var x = splitLines(buf2) sort(x, system.cmp) - join(x, "\n") + join(x, "\n") & "\n" else: - strip(buf.string) + buf.string if exitCode != expected.exitCode: r.addResult(test, target, "exitcode: " & $expected.exitCode, "exitcode: " & $exitCode & "\n\nOutput:\n" & bufB, reExitcodesDiffer) - elif (expected.outputCheck == ocEqual and expected.output != bufB) or + elif (expected.outputCheck == ocEqual and not expected.output.equalModuloLastNewline(bufB)) or (expected.outputCheck == ocSubstr and expected.output notin bufB): given.err = reOutputsDiffer r.addResult(test, target, expected.output, bufB, reOutputsDiffer) diff --git a/tests/arc/tstrformat.nim b/tests/arc/tstrformat.nim index 00d7a6679..641f323da 100644 --- a/tests/arc/tstrformat.nim +++ b/tests/arc/tstrformat.nim @@ -1,5 +1,7 @@ discard """ - output: '''verstuff''' + output: ''' +verstuff +''' cmd: "nim c --gc:arc $file" """ diff --git a/tests/async/tasyncfilewrite.nim b/tests/async/tasyncfilewrite.nim index 3baf2bbc6..72a2df0b0 100644 --- a/tests/async/tasyncfilewrite.nim +++ b/tests/async/tasyncfilewrite.nim @@ -1,7 +1,8 @@ discard """ output: '''string 1 string 2 -string 3''' +string 3 +''' """ # bug #5532 import os, asyncfile, asyncdispatch diff --git a/tests/ccgbugs/tret_arg_init.nim b/tests/ccgbugs/tret_arg_init.nim index 5cd67de3e..e39e5a0de 100644 --- a/tests/ccgbugs/tret_arg_init.nim +++ b/tests/ccgbugs/tret_arg_init.nim @@ -1,6 +1,8 @@ discard """ output: ''' + + ''' """ diff --git a/tests/concepts/treversable.nim b/tests/concepts/treversable.nim index 6ebc077d9..d30ba0a3c 100644 --- a/tests/concepts/treversable.nim +++ b/tests/concepts/treversable.nim @@ -3,7 +3,7 @@ discard """ output: ''' z e - ''' +''' """ type diff --git a/tests/destructor/ttuple.nim b/tests/destructor/ttuple.nim index 5a2126105..d0ea72c60 100644 --- a/tests/destructor/ttuple.nim +++ b/tests/destructor/ttuple.nim @@ -2,7 +2,7 @@ discard """ output: '''5.0 10.0 =destroy -=destroy +=destroy ''' """ diff --git a/tests/exception/texcas.nim b/tests/exception/texcas.nim index 7108e334c..ad6819f11 100644 --- a/tests/exception/texcas.nim +++ b/tests/exception/texcas.nim @@ -1,8 +1,9 @@ discard """ targets: "c cpp" - output: '''Hello + output: ''' Hello - ''' +Hello +''' """ proc test[T]() = try: diff --git a/tests/exception/texceptions.nim b/tests/exception/texceptions.nim index 5d4d0fa9e..adee5d1d5 100644 --- a/tests/exception/texceptions.nim +++ b/tests/exception/texceptions.nim @@ -1,5 +1,6 @@ discard """ output: ''' + BEFORE FINALLY diff --git a/tests/generics/treentranttypes.nim b/tests/generics/treentranttypes.nim index 31fa25293..40ff1647b 100644 --- a/tests/generics/treentranttypes.nim +++ b/tests/generics/treentranttypes.nim @@ -2,21 +2,13 @@ discard """ output: ''' (10, ("test", 1.2)) 3x3 Matrix [[0.0, 2.0, 3.0], [2.0, 0.0, 5.0], [2.0, 0.0, 5.0]] - 2x3 Matrix [[0.0, 2.0, 3.0], [2.0, 0.0, 5.0]] - 2x3 Literal [[0.0, 2.0, 3.0], [2.0, 0.0, 5.0]] - 2x3 Matrix [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]] - 2x2 ArrayArray[[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]] - 2x3 ArrayVector[[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]] - 2x3 VectorVector [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]] - 2x3 VectorArray [[0.0, 0.0, 0.0], [0.0, 0.0, 0.0]] - @[1, 2] @[1, 2] @[1, 2]@[3, 4] diff --git a/tests/js/tunittests.nim b/tests/js/tunittests.nim index 7c2e70563..0d934a82b 100644 --- a/tests/js/tunittests.nim +++ b/tests/js/tunittests.nim @@ -1,7 +1,9 @@ discard """ output: ''' + [Suite] Bacon - [OK] >:)''' + [OK] >:) +''' """ import unittest diff --git a/tests/lookups/test.nim b/tests/lookups/test.nim index 97dfa131a..dfacaf5b5 100644 --- a/tests/lookups/test.nim +++ b/tests/lookups/test.nim @@ -1,7 +1,7 @@ discard """ output: ''' -[Suite] memoization +[Suite] memoization ''' """ diff --git a/tests/macros/tastrepr.nim b/tests/macros/tastrepr.nim index e4271f019..759ca55b5 100644 --- a/tests/macros/tastrepr.nim +++ b/tests/macros/tastrepr.nim @@ -1,5 +1,6 @@ discard """ output: ''' + var data = @[(1, "one"), (2, "two")] for (i, d) in pairs(data): discard diff --git a/tests/magics/t10307.nim b/tests/magics/t10307.nim index 111b0d221..b5bbfdfa8 100644 --- a/tests/magics/t10307.nim +++ b/tests/magics/t10307.nim @@ -4,7 +4,8 @@ discard """ res: yes yes running someProc(false) -res: +res: + ''' """ @@ -20,4 +21,4 @@ echo "running someProc(true)" echo someProc(true) echo "running someProc(false)" -echo someProc(false) \ No newline at end of file +echo someProc(false) diff --git a/tests/metatype/tmatrix3.nim b/tests/metatype/tmatrix3.nim index a143e2bc9..28e85fcee 100644 --- a/tests/metatype/tmatrix3.nim +++ b/tests/metatype/tmatrix3.nim @@ -1,5 +1,5 @@ discard """ - output: "" + output: "\n" """ type Matrix[M,N: static[int]] = array[M, array[N, float]] diff --git a/tests/metatype/ttensor.nim b/tests/metatype/ttensor.nim index f2f24ba8c..89a0bf007 100644 --- a/tests/metatype/ttensor.nim +++ b/tests/metatype/ttensor.nim @@ -1,13 +1,11 @@ discard """ - output: '''before tensor2: + output: ''' +before tensor2: [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0] - before tensor3: [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0] - after tensor3: [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0, 26.0] - a3: [1000.0, 1001.0, 1002.0, 1003.0, 1004.0, 1005.0, 1006.0, 1007.0, 1008.0, 1009.0, 1010.0, 1011.0, 1012.0, 1013.0, 1014.0, 1015.0, 1016.0, 1017.0, 1018.0, 1019.0, 1020.0, 1021.0, 1022.0, 1023.0, 1024.0, 1025.0, 1026.0]''' """ diff --git a/tests/misc/temptyecho.nim b/tests/misc/temptyecho.nim index 5f1aa6515..a3c407897 100644 --- a/tests/misc/temptyecho.nim +++ b/tests/misc/temptyecho.nim @@ -1,2 +1,6 @@ +discard """ +output: "\n" +""" + echo() diff --git a/tests/objects/tobject.nim b/tests/objects/tobject.nim index a79c2bdfd..d166d5385 100644 --- a/tests/objects/tobject.nim +++ b/tests/objects/tobject.nim @@ -1,5 +1,5 @@ discard """ -output: "[Suite] object basic methods" +output: "\n[Suite] object basic methods" """ import unittest diff --git a/tests/overload/tstatic_with_converter.nim b/tests/overload/tstatic_with_converter.nim index 2871744eb..8209e8399 100644 --- a/tests/overload/tstatic_with_converter.nim +++ b/tests/overload/tstatic_with_converter.nim @@ -1,6 +1,8 @@ discard """ output: ''' -9.0''' +9.0 + +''' """ ### bug #6773 @@ -41,6 +43,7 @@ proc `^`(x: vfloat, exp: static[float]): vfloat = proc `$`(x: vfloat): string = let y = cast[ptr float](unsafeAddr x) + # xxx not sure if intentional in this issue, but this returns "" echo y[] let x = set1(9.0) diff --git a/tests/specialops/tdotops.nim b/tests/specialops/tdotops.nim index 227204f51..b1c75ab33 100644 --- a/tests/specialops/tdotops.nim +++ b/tests/specialops/tdotops.nim @@ -1,5 +1,6 @@ discard """ output: ''' + 10 assigning z = 20 reading field y @@ -12,7 +13,8 @@ no params call to b 100 one param call to c with 10 100 -0 4''' +0 4 +''' """ type diff --git a/tests/statictypes/tpassthruarith.nim b/tests/statictypes/tpassthruarith.nim index 857e5b0af..33e4e0303 100644 --- a/tests/statictypes/tpassthruarith.nim +++ b/tests/statictypes/tpassthruarith.nim @@ -1,13 +1,9 @@ discard """ output: ''' [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]] - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - [1, 2, 3, 4] ''' """ diff --git a/tests/stdlib/talgorithm.nim b/tests/stdlib/talgorithm.nim index 59336e70d..9dec68f03 100644 --- a/tests/stdlib/talgorithm.nim +++ b/tests/stdlib/talgorithm.nim @@ -1,6 +1,6 @@ discard """ output:'''@["3", "2", "1"] - ''' +''' """ #12928,10456 import sequtils, strutils, algorithm, json diff --git a/tests/stdlib/tcgi.nim b/tests/stdlib/tcgi.nim index 222a3bf6d..cec188e35 100644 --- a/tests/stdlib/tcgi.nim +++ b/tests/stdlib/tcgi.nim @@ -1,5 +1,6 @@ discard """ output: ''' + [Suite] Test cgi module (key: "a", value: "1") (key: "b", value: "0") diff --git a/tests/stdlib/tmath.nim b/tests/stdlib/tmath.nim index ff1f32d36..ac5ce4c90 100644 --- a/tests/stdlib/tmath.nim +++ b/tests/stdlib/tmath.nim @@ -1,6 +1,8 @@ discard """ action: run - output: '''[Suite] random int + output: ''' + +[Suite] random int [Suite] random float @@ -9,7 +11,6 @@ discard """ [Suite] random sample [Suite] ^ - ''' """ diff --git a/tests/stdlib/tnet_ll.nim b/tests/stdlib/tnet_ll.nim index 2ac272fd1..affa21947 100644 --- a/tests/stdlib/tnet_ll.nim +++ b/tests/stdlib/tnet_ll.nim @@ -1,6 +1,7 @@ discard """ action: run output: ''' + [Suite] inet_ntop tests ''' """ diff --git a/tests/stdlib/tnre.nim b/tests/stdlib/tnre.nim index 0929956cb..d2dc1a7c5 100644 --- a/tests/stdlib/tnre.nim +++ b/tests/stdlib/tnre.nim @@ -4,7 +4,9 @@ discard """ # codes to differ and be reported as a failure output: - '''[Suite] Test NRE initialization + ''' + +[Suite] Test NRE initialization [Suite] captures diff --git a/tests/stdlib/tparseuints.nim b/tests/stdlib/tparseuints.nim index 6b228d933..72041da66 100644 --- a/tests/stdlib/tparseuints.nim +++ b/tests/stdlib/tparseuints.nim @@ -1,13 +1,14 @@ discard """ action: run output: ''' -[Suite] parseutils''' + +[Suite] parseutils +''' """ import unittest, strutils suite "parseutils": - test "uint": - check: parseBiggestUInt("0") == 0'u64 - check: parseBiggestUInt("18446744073709551615") == 0xFFFF_FFFF_FFFF_FFFF'u64 - expect(ValueError): - discard parseBiggestUInt("18446744073709551616") + check: parseBiggestUInt("0") == 0'u64 + check: parseBiggestUInt("18446744073709551615") == 0xFFFF_FFFF_FFFF_FFFF'u64 + expect(ValueError): + discard parseBiggestUInt("18446744073709551616") diff --git a/tests/stdlib/trst.nim b/tests/stdlib/trst.nim index 797010a22..6a6e6fdc0 100644 --- a/tests/stdlib/trst.nim +++ b/tests/stdlib/trst.nim @@ -1,5 +1,6 @@ discard """ output: ''' + [Suite] RST include directive ''' """ diff --git a/tests/stdlib/tsequtils.nim b/tests/stdlib/tsequtils.nim index ea3f06982..efcc9f126 100644 --- a/tests/stdlib/tsequtils.nim +++ b/tests/stdlib/tsequtils.nim @@ -455,6 +455,3 @@ block: yield i doAssert: iter(3).mapIt(2*it).foldl(a + b) == 6 - -when not defined(testing): - echo "Finished doc tests" diff --git a/tests/stdlib/tunittest.nim b/tests/stdlib/tunittest.nim index 9ef689e32..8e82a0c97 100644 --- a/tests/stdlib/tunittest.nim +++ b/tests/stdlib/tunittest.nim @@ -1,5 +1,7 @@ discard """ - output: '''[Suite] suite with only teardown + output: ''' + +[Suite] suite with only teardown [Suite] suite with only setup @@ -16,7 +18,6 @@ discard """ [Suite] test suite [Suite] test name filtering - ''' """ @@ -141,38 +142,37 @@ suite "test suite": check(a == b) -when defined(testing): - suite "test name filtering": - test "test name": - check matchFilter("suite1", "foo", "") - check matchFilter("suite1", "foo", "foo") - check matchFilter("suite1", "foo", "::") - check matchFilter("suite1", "foo", "*") - check matchFilter("suite1", "foo", "::foo") - check matchFilter("suite1", "::foo", "::foo") +suite "test name filtering": + test "test name": + check matchFilter("suite1", "foo", "") + check matchFilter("suite1", "foo", "foo") + check matchFilter("suite1", "foo", "::") + check matchFilter("suite1", "foo", "*") + check matchFilter("suite1", "foo", "::foo") + check matchFilter("suite1", "::foo", "::foo") - test "test name - glob": - check matchFilter("suite1", "foo", "f*") - check matchFilter("suite1", "foo", "*oo") - check matchFilter("suite1", "12345", "12*345") - check matchFilter("suite1", "q*wefoo", "q*wefoo") - check false == matchFilter("suite1", "foo", "::x") - check false == matchFilter("suite1", "foo", "::x*") - check false == matchFilter("suite1", "foo", "::*x") - # overlap - check false == matchFilter("suite1", "12345", "123*345") - check matchFilter("suite1", "ab*c::d*e::f", "ab*c::d*e::f") + test "test name - glob": + check matchFilter("suite1", "foo", "f*") + check matchFilter("suite1", "foo", "*oo") + check matchFilter("suite1", "12345", "12*345") + check matchFilter("suite1", "q*wefoo", "q*wefoo") + check false == matchFilter("suite1", "foo", "::x") + check false == matchFilter("suite1", "foo", "::x*") + check false == matchFilter("suite1", "foo", "::*x") + # overlap + check false == matchFilter("suite1", "12345", "123*345") + check matchFilter("suite1", "ab*c::d*e::f", "ab*c::d*e::f") - test "suite name": - check matchFilter("suite1", "foo", "suite1::") - check false == matchFilter("suite1", "foo", "suite2::") - check matchFilter("suite1", "qwe::foo", "qwe::foo") - check matchFilter("suite1", "qwe::foo", "suite1::qwe::foo") + test "suite name": + check matchFilter("suite1", "foo", "suite1::") + check false == matchFilter("suite1", "foo", "suite2::") + check matchFilter("suite1", "qwe::foo", "qwe::foo") + check matchFilter("suite1", "qwe::foo", "suite1::qwe::foo") - test "suite name - glob": - check matchFilter("suite1", "foo", "::*") - check matchFilter("suite1", "foo", "*::*") - check matchFilter("suite1", "foo", "*::foo") - check false == matchFilter("suite1", "foo", "*ite2::") - check matchFilter("suite1", "q**we::foo", "q**we::foo") - check matchFilter("suite1", "a::b*c::d*e", "a::b*c::d*e") + test "suite name - glob": + check matchFilter("suite1", "foo", "::*") + check matchFilter("suite1", "foo", "*::*") + check matchFilter("suite1", "foo", "*::foo") + check false == matchFilter("suite1", "foo", "*ite2::") + check matchFilter("suite1", "q**we::foo", "q**we::foo") + check matchFilter("suite1", "a::b*c::d*e", "a::b*c::d*e") diff --git a/tests/system/tenum_array_repr.nim b/tests/system/tenum_array_repr.nim index 3634692e3..39b1a5f9a 100644 --- a/tests/system/tenum_array_repr.nim +++ b/tests/system/tenum_array_repr.nim @@ -2,10 +2,8 @@ discard """ output: ''' 1 [a, b] - 2 [c, d] - 4 [e, f]''' """ diff --git a/tests/template/tgensymregression.nim b/tests/template/tgensymregression.nim index f84c96403..2a5dca934 100644 --- a/tests/template/tgensymregression.nim +++ b/tests/template/tgensymregression.nim @@ -1,8 +1,6 @@ discard """ output: '''[0.0, 0.0, 0.0] - [0.0, 0.0, 0.0, 0.0] - 5050 123''' """ From 1d786c0a54d3ec914d16079d5e228605fbcb2cb6 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 28 Nov 2020 10:33:36 -0800 Subject: [PATCH 1092/3768] fix #14242 `testament r tests/js/foo` now works; testament now honors --targets (#16163) * fix #14242 `testament r tests/js/foo` now works; testament now honors --targets * fix shouldfail --- testament/categories.nim | 14 +++++++------- testament/specs.nim | 8 ++++---- testament/testament.nim | 14 +++++++++----- tests/testament/tshould_not_work.nim | 28 ++++++++++++++-------------- 4 files changed, 34 insertions(+), 30 deletions(-) diff --git a/testament/categories.nim b/testament/categories.nim index 4f6e40659..3429b760a 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -563,13 +563,13 @@ proc `&.?`(a, b: string): string = # candidate for the stdlib? result = if b.startsWith(a): b else: a & b -proc processSingleTest(r: var TResults, cat: Category, options, test: string) = - let test = testsDir &.? cat.string / test - let target = if cat.string.normalize == "js": targetJS else: targetC - if fileExists(test): - testSpec r, makeTest(test, options, cat), {target} - else: - doAssert false, test & " test does not exist" +proc processSingleTest(r: var TResults, cat: Category, options, test: string, targets: set[TTarget], targetsSet: bool) = + var targets = targets + if not targetsSet: + let target = if cat.string.normalize == "js": targetJS else: targetC + targets = {target} + doAssert fileExists(test), test & " test does not exist" + testSpec r, makeTest(test, options, cat), targets proc isJoinableSpec(spec: TSpec): bool = result = not spec.sortoutput and diff --git a/testament/specs.nim b/testament/specs.nim index 37fe12275..c60ed7e20 100644 --- a/testament/specs.nim +++ b/testament/specs.nim @@ -57,10 +57,10 @@ type reInvalidSpec # test had problems to parse the spec TTarget* = enum - targetC = "C" - targetCpp = "C++" - targetObjC = "ObjC" - targetJS = "JS" + targetC = "c" + targetCpp = "cpp" + targetObjC = "objc" + targetJS = "js" InlineError* = object kind*: string diff --git a/testament/testament.nim b/testament/testament.nim index 94f66c2db..c014a79a6 100644 --- a/testament/testament.nim +++ b/testament/testament.nim @@ -81,6 +81,7 @@ let pegOfInterest = pegLineError / pegOtherError var gTargets = {low(TTarget)..high(TTarget)} +var targetsSet = false proc isSuccess(input: string): bool = # not clear how to do the equivalent of pkg/regex's: re"FOO(.*?)BAR" in pegs @@ -691,6 +692,7 @@ proc main() = of "targets": targetsStr = p.val.string gTargets = parseTargets(targetsStr) + targetsSet = true of "nim": compilerPrefix = addFileExt(p.val.string, ExeExt) of "directory": @@ -800,14 +802,16 @@ proc main() = p.next processPattern(r, pattern, p.cmdLineRest.string, simulate) of "r", "run": + # "/pathto/tests/stdlib/nre/captures.nim" -> "stdlib" + "tests/stdlib/nre/captures.nim" var subPath = p.key.string - if subPath.isAbsolute: subPath = subPath.relativePath(getCurrentDir()) + let nimRoot = currentSourcePath / "../.." + # makes sure points to this regardless of cwd or which nim is used to compile this. + doAssert existsDir(nimRoot/testsDir) # sanity check + if subPath.isAbsolute: subPath = subPath.relativePath(nimRoot) # at least one directory is required in the path, to use as a category name - let pathParts = split(subPath, {DirSep, AltSep}) - # "stdlib/nre/captures.nim" -> "stdlib" + "nre/captures.nim" + let pathParts = subPath.relativePath(testsDir).split({DirSep, AltSep}) let cat = Category(pathParts[0]) - subPath = joinPath(pathParts[1..^1]) - processSingleTest(r, cat, p.cmdLineRest.string, subPath) + processSingleTest(r, cat, p.cmdLineRest.string, subPath, gTargets, targetsSet) of "html": generateHtml(resultsFile, optFailing) else: diff --git a/tests/testament/tshould_not_work.nim b/tests/testament/tshould_not_work.nim index e4d3c7be7..a7a877fe8 100644 --- a/tests/testament/tshould_not_work.nim +++ b/tests/testament/tshould_not_work.nim @@ -2,36 +2,36 @@ discard """ cmd: "testament/testament --directory:testament --colors:off --backendLogging:off --nim:../compiler/nim category shouldfail" action: compile nimout: ''' -FAIL: tests/shouldfail/tccodecheck.nim C +FAIL: tests/shouldfail/tccodecheck.nim c Failure: reCodegenFailure Expected: baz -FAIL: tests/shouldfail/tcolumn.nim C +FAIL: tests/shouldfail/tcolumn.nim c Failure: reLinesDiffer -FAIL: tests/shouldfail/terrormsg.nim C +FAIL: tests/shouldfail/terrormsg.nim c Failure: reMsgsDiffer -FAIL: tests/shouldfail/texitcode1.nim C +FAIL: tests/shouldfail/texitcode1.nim c Failure: reExitcodesDiffer -FAIL: tests/shouldfail/tfile.nim C +FAIL: tests/shouldfail/tfile.nim c Failure: reFilesDiffer -FAIL: tests/shouldfail/tline.nim C +FAIL: tests/shouldfail/tline.nim c Failure: reLinesDiffer -FAIL: tests/shouldfail/tmaxcodesize.nim C +FAIL: tests/shouldfail/tmaxcodesize.nim c Failure: reCodegenFailure max allowed size: 1 -FAIL: tests/shouldfail/tnimout.nim C +FAIL: tests/shouldfail/tnimout.nim c Failure: reMsgsDiffer -FAIL: tests/shouldfail/toutput.nim C +FAIL: tests/shouldfail/toutput.nim c Failure: reOutputsDiffer -FAIL: tests/shouldfail/toutputsub.nim C +FAIL: tests/shouldfail/toutputsub.nim c Failure: reOutputsDiffer -FAIL: tests/shouldfail/treject.nim C +FAIL: tests/shouldfail/treject.nim c Failure: reFilesDiffer -FAIL: tests/shouldfail/tsortoutput.nim C +FAIL: tests/shouldfail/tsortoutput.nim c Failure: reOutputsDiffer -FAIL: tests/shouldfail/ttimeout.nim C +FAIL: tests/shouldfail/ttimeout.nim c Failure: reTimeout -FAIL: tests/shouldfail/tvalgrind.nim C +FAIL: tests/shouldfail/tvalgrind.nim c Failure: reExitcodesDiffer ''' """ From d29eddf92a1b8147c4709fd828f77152c1f3aab2 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 28 Nov 2020 11:59:08 -0800 Subject: [PATCH 1093/3768] experimental/diff: move isMainModule block to tests/stdlib/tdiff.nim (#16172) --- lib/experimental/diff.nim | 80 --------------------------------------- tests/stdlib/tdiff.nim | 73 +++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 80 deletions(-) create mode 100644 tests/stdlib/tdiff.nim diff --git a/lib/experimental/diff.nim b/lib/experimental/diff.nim index 5a69687a3..d1274d771 100644 --- a/lib/experimental/diff.nim +++ b/lib/experimental/diff.nim @@ -331,83 +331,3 @@ proc diffText*(textA, textB: string): seq[Item] = optimize(dataA) optimize(dataB) result = createDiffs(dataA, dataB) - -when isMainModule: - - proc testHelper(f: seq[Item]): string = - for it in f: - result.add( - $it.deletedA & "." & $it.insertedB & "." & $it.startA & "." & $it.startB & "*" - ) - - proc main() = - var a, b: string - - stdout.writeLine("Diff Self Test...") - - # test all changes - a = "a,b,c,d,e,f,g,h,i,j,k,l".replace(',', '\n') - b = "0,1,2,3,4,5,6,7,8,9".replace(',', '\n') - assert(testHelper(diffText(a, b)) == - "12.10.0.0*", - "all-changes test failed.") - stdout.writeLine("all-changes test passed.") - # test all same - a = "a,b,c,d,e,f,g,h,i,j,k,l".replace(',', '\n') - b = a - assert(testHelper(diffText(a, b)) == - "", - "all-same test failed.") - stdout.writeLine("all-same test passed.") - - # test snake - a = "a,b,c,d,e,f".replace(',', '\n') - b = "b,c,d,e,f,x".replace(',', '\n') - assert(testHelper(diffText(a, b)) == - "1.0.0.0*0.1.6.5*", - "snake test failed.") - stdout.writeLine("snake test passed.") - - # 2002.09.20 - repro - a = "c1,a,c2,b,c,d,e,g,h,i,j,c3,k,l".replace(',', '\n') - b = "C1,a,C2,b,c,d,e,I1,e,g,h,i,j,C3,k,I2,l".replace(',', '\n') - assert(testHelper(diffText(a, b)) == - "1.1.0.0*1.1.2.2*0.2.7.7*1.1.11.13*0.1.13.15*", - "repro20020920 test failed.") - stdout.writeLine("repro20020920 test passed.") - - # 2003.02.07 - repro - a = "F".replace(',', '\n') - b = "0,F,1,2,3,4,5,6,7".replace(',', '\n') - assert(testHelper(diffText(a, b)) == - "0.1.0.0*0.7.1.2*", - "repro20030207 test failed.") - stdout.writeLine("repro20030207 test passed.") - - # Muegel - repro - a = "HELLO\nWORLD" - b = "\n\nhello\n\n\n\nworld\n" - assert(testHelper(diffText(a, b)) == - "2.8.0.0*", - "repro20030409 test failed.") - stdout.writeLine("repro20030409 test passed.") - - # test some differences - a = "a,b,-,c,d,e,f,f".replace(',', '\n') - b = "a,b,x,c,e,f".replace(',', '\n') - assert(testHelper(diffText(a, b)) == - "1.1.2.2*1.0.4.4*1.0.7.6*", - "some-changes test failed.") - stdout.writeLine("some-changes test passed.") - - # test one change within long chain of repeats - a = "a,a,a,a,a,a,a,a,a,a".replace(',', '\n') - b = "a,a,a,a,-,a,a,a,a,a".replace(',', '\n') - assert(testHelper(diffText(a, b)) == - "0.1.4.4*1.0.9.10*", - "long chain of repeats test failed.") - - stdout.writeLine("End.") - stdout.flushFile - - main() diff --git a/tests/stdlib/tdiff.nim b/tests/stdlib/tdiff.nim new file mode 100644 index 000000000..694ac6198 --- /dev/null +++ b/tests/stdlib/tdiff.nim @@ -0,0 +1,73 @@ +discard """ + targets: "c js" +""" + +import experimental/diff +import std/strutils + +proc testHelper(f: seq[Item]): string = + for it in f: + result.add( + $it.deletedA & "." & $it.insertedB & "." & $it.startA & "." & $it.startB & "*" + ) + +proc main() = + var a, b: string + + # Diff Self Test + # test all changes + a = "a,b,c,d,e,f,g,h,i,j,k,l".replace(',', '\n') + b = "0,1,2,3,4,5,6,7,8,9".replace(',', '\n') + doAssert(testHelper(diffText(a, b)) == + "12.10.0.0*", + "all-changes test failed.") + # test all same + a = "a,b,c,d,e,f,g,h,i,j,k,l".replace(',', '\n') + b = a + doAssert(testHelper(diffText(a, b)) == + "", + "all-same test failed.") + + # test snake + a = "a,b,c,d,e,f".replace(',', '\n') + b = "b,c,d,e,f,x".replace(',', '\n') + doAssert(testHelper(diffText(a, b)) == + "1.0.0.0*0.1.6.5*", + "snake test failed.") + + # 2002.09.20 - repro + a = "c1,a,c2,b,c,d,e,g,h,i,j,c3,k,l".replace(',', '\n') + b = "C1,a,C2,b,c,d,e,I1,e,g,h,i,j,C3,k,I2,l".replace(',', '\n') + doAssert(testHelper(diffText(a, b)) == + "1.1.0.0*1.1.2.2*0.2.7.7*1.1.11.13*0.1.13.15*", + "repro20020920 test failed.") + + # 2003.02.07 - repro + a = "F".replace(',', '\n') + b = "0,F,1,2,3,4,5,6,7".replace(',', '\n') + doAssert(testHelper(diffText(a, b)) == + "0.1.0.0*0.7.1.2*", + "repro20030207 test failed.") + + # Muegel - repro + a = "HELLO\nWORLD" + b = "\n\nhello\n\n\n\nworld\n" + doAssert(testHelper(diffText(a, b)) == + "2.8.0.0*", + "repro20030409 test failed.") + + # test some differences + a = "a,b,-,c,d,e,f,f".replace(',', '\n') + b = "a,b,x,c,e,f".replace(',', '\n') + doAssert(testHelper(diffText(a, b)) == + "1.1.2.2*1.0.4.4*1.0.7.6*", + "some-changes test failed.") + + # test one change within long chain of repeats + a = "a,a,a,a,a,a,a,a,a,a".replace(',', '\n') + b = "a,a,a,a,-,a,a,a,a,a".replace(',', '\n') + doAssert(testHelper(diffText(a, b)) == + "0.1.4.4*1.0.9.10*", + "long chain of repeats test failed.") +main() +static: main() From 6114df3c24e9c23bfbdbaa204bb100de9d170995 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 29 Nov 2020 10:42:50 -0800 Subject: [PATCH 1094/3768] testament: error instead of silently overwrite a spec (#16166) --- testament/specs.nim | 18 +++++++++++++----- testament/testament.nim | 5 ++--- tests/casestmt/tcaseexpr1.nim | 26 ++++++++++++++++---------- tests/exception/t13115.nim | 9 ++++----- tests/generics/tmetafield.nim | 20 ++++++++++++++++---- 5 files changed, 51 insertions(+), 27 deletions(-) diff --git a/testament/specs.nim b/testament/specs.nim index c60ed7e20..d2f37b96c 100644 --- a/testament/specs.nim +++ b/testament/specs.nim @@ -8,7 +8,7 @@ # import sequtils, parseutils, strutils, os, streams, parsecfg, - tables, hashes + tables, hashes, sets type TestamentData* = ref object # better to group globals under 1 object; could group the other ones here too @@ -82,7 +82,7 @@ type tline*, tcolumn*: int exitCode*: int msg*: string - ccodeCheck*: string + ccodeCheck*: seq[string] maxCodeSize*: int err*: TResultEnum inCurrentBatch*: bool @@ -244,11 +244,19 @@ proc parseSpec*(filename: string): TSpec = var ss = newStringStream(specStr) var p: CfgParser open(p, ss, filename, 1) + var flags: HashSet[string] while true: var e = next(p) case e.kind of cfgKeyValuePair: - case normalize(e.key) + let key = e.key.normalize + const whiteListMulti = ["disabled", "ccodecheck"] + ## list of flags that are correctly handled when passed multiple times + ## (instead of being overwritten) + if key notin whiteListMulti: + doAssert key notin flags, $(key, filename) + flags.incl key + case key of "action": case e.value.normalize of "compile": @@ -298,7 +306,7 @@ proc parseSpec*(filename: string): TSpec = result.msg = e.value if result.action != actionRun: result.action = actionCompile - of "errormsg", "errmsg": + of "errormsg", "errmsg": # xxx just use errormsg, no need for such aliases result.msg = e.value result.action = actionReject of "nimout": @@ -361,7 +369,7 @@ proc parseSpec*(filename: string): TSpec = else: result.cmd = e.value of "ccodecheck": - result.ccodeCheck = e.value + result.ccodeCheck.add e.value of "maxcodesize": discard parseInt(e.value, result.maxCodeSize) of "timeout": diff --git a/testament/testament.nim b/testament/testament.nim index c014a79a6..f2ac9c338 100644 --- a/testament/testament.nim +++ b/testament/testament.nim @@ -402,9 +402,8 @@ proc codegenCheck(test: TTest, target: TTarget, spec: TSpec, expectedMsg: var st try: let genFile = generatedFile(test, target) let contents = readFile(genFile).string - let check = spec.ccodeCheck - if check.len > 0: - if check[0] == '\\': + for check in spec.ccodeCheck: + if check.len > 0 and check[0] == '\\': # little hack to get 'match' support: if not contents.match(check.peg): given.err = reCodegenFailure diff --git a/tests/casestmt/tcaseexpr1.nim b/tests/casestmt/tcaseexpr1.nim index 3d4b6c087..82af2410b 100644 --- a/tests/casestmt/tcaseexpr1.nim +++ b/tests/casestmt/tcaseexpr1.nim @@ -1,17 +1,23 @@ discard """ - errormsg: "type mismatch: got but expected 'int'" - line: 33 - file: "tcaseexpr1.nim" - - errormsg: "not all cases are covered; missing: {C}" - line: 27 - file: "tcaseexpr1.nim" + cmd: "nim check $options $file" + action: "reject" + nimout: ''' +tcaseexpr1.nim(33, 10) Error: not all cases are covered; missing: {C} +''' """ -# NOTE: This spec is wrong. Spec doesn't support multiple error -# messages. The first one is simply overridden by the second one. -# This just has never been noticed. +#[ +# xxx make nimout comparison use nimoutCheck instead of: +elif expected.nimout.len > 0 and expected.nimout.normalizeMsg notin given.nimout.normalizeMsg: +and then use nimout: ''' +tcaseexpr1.nim(33, 10) Error: not all cases are covered2; missing: {C} +tcaseexpr1.nim(39, 12) Error: type mismatch: got but expected 'int literal(10)' +''' +]# + + +# line 20 type E = enum A, B, C diff --git a/tests/exception/t13115.nim b/tests/exception/t13115.nim index 9d88bbeda..53e078076 100644 --- a/tests/exception/t13115.nim +++ b/tests/exception/t13115.nim @@ -2,12 +2,11 @@ discard """ exitcode: 1 targets: "c" matrix: "-d:debug; -d:release" - outputsub: '''t13115.nim(13) t13115 -Error: unhandled exception: This char is''' outputsub: ''' and works fine! [Exception]''' """ -const b_null: char = 0.char -var msg = "This char is `" & $b_null & "` and works fine!" +# bug #13115 +# xxx bug: doesn't yet work for cpp -raise newException(Exception, msg) \ No newline at end of file +var msg = "This char is `" & '\0' & "` and works fine!" +raise newException(Exception, msg) diff --git a/tests/generics/tmetafield.nim b/tests/generics/tmetafield.nim index 7a2375abe..cf30a936d 100644 --- a/tests/generics/tmetafield.nim +++ b/tests/generics/tmetafield.nim @@ -1,10 +1,23 @@ discard """ cmd: "nim check $options $file" - errormsg: "'proc' is not a concrete type" - errormsg: "'Foo' is not a concrete type." - errormsg: "invalid type: 'proc' in this context: 'TBaseMed'" + action: "reject" + nimout: ''' +tmetafield.nim(26, 5) Error: 'proc' is not a concrete type; for a callback without parameters use 'proc()' +tmetafield.nim(27, 5) Error: 'Foo' is not a concrete type +tmetafield.nim(29, 5) Error: invalid type: 'proc' in this context: 'TBaseMed' for var +''' """ +# bug #188 + + + + + + + + +# line 20 type Foo[T] = object x: T @@ -15,4 +28,3 @@ type var a: TBaseMed -# issue 188 From 31a8cf16bbad69efbfbc6f7a593f2141cba06381 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 29 Nov 2020 17:32:34 -0800 Subject: [PATCH 1095/3768] testament spec: remove errmsg alias of errormsg (#16188) --- testament/specs.nim | 2 +- testament/tests/shouldfail/tfile.nim | 2 +- tests/async/tdiscardableproc.nim | 2 +- tests/destructor/tdestructor_too_late.nim | 2 +- tests/errmsgs/t8610.nim | 2 +- tests/errmsgs/t9768.nim | 2 +- tests/errmsgs/tstaticexprscope.nim | 2 +- tests/errmsgs/ttypeAllowed.nim | 2 +- tests/lent/tnot_allowed_lent.nim | 2 +- tests/lent/tnot_allowed_lent2.nim | 2 +- tests/nimble/tnimblepathdollar_fault.nim | 2 +- tests/proc/tfunc_type.nim | 2 +- tests/proc/tillegalreturntype.nim | 2 +- tests/varres/tprevent_forloopvar_mutations.nim | 2 +- 14 files changed, 14 insertions(+), 14 deletions(-) diff --git a/testament/specs.nim b/testament/specs.nim index d2f37b96c..e15dcb85c 100644 --- a/testament/specs.nim +++ b/testament/specs.nim @@ -306,7 +306,7 @@ proc parseSpec*(filename: string): TSpec = result.msg = e.value if result.action != actionRun: result.action = actionCompile - of "errormsg", "errmsg": # xxx just use errormsg, no need for such aliases + of "errormsg": result.msg = e.value result.action = actionReject of "nimout": diff --git a/testament/tests/shouldfail/tfile.nim b/testament/tests/shouldfail/tfile.nim index a6c2ad359..20d4bd1f3 100644 --- a/testament/tests/shouldfail/tfile.nim +++ b/testament/tests/shouldfail/tfile.nim @@ -1,5 +1,5 @@ discard """ -errmsg: "undeclared identifier: 'undefined'" +errormsg: "undeclared identifier: 'undefined'" file: "notthisfile.nim" """ diff --git a/tests/async/tdiscardableproc.nim b/tests/async/tdiscardableproc.nim index 80286ece0..93cd83be9 100644 --- a/tests/async/tdiscardableproc.nim +++ b/tests/async/tdiscardableproc.nim @@ -1,5 +1,5 @@ discard """ - errmsg: "Cannot make async proc discardable. Futures have to be checked with `asyncCheck` instead of discarded" + errormsg: "Cannot make async proc discardable. Futures have to be checked with `asyncCheck` instead of discarded" """ import async diff --git a/tests/destructor/tdestructor_too_late.nim b/tests/destructor/tdestructor_too_late.nim index d279280ba..76d1dde84 100644 --- a/tests/destructor/tdestructor_too_late.nim +++ b/tests/destructor/tdestructor_too_late.nim @@ -1,5 +1,5 @@ discard """ - errmsg: "cannot bind another '=destroy' to: Obj; previous declaration was constructed here implicitly: tdestructor_too_late.nim(7, 16)" + errormsg: "cannot bind another '=destroy' to: Obj; previous declaration was constructed here implicitly: tdestructor_too_late.nim(7, 16)" """ type Obj* = object v*: int diff --git a/tests/errmsgs/t8610.nim b/tests/errmsgs/t8610.nim index 6a253f7ab..d3405bc91 100644 --- a/tests/errmsgs/t8610.nim +++ b/tests/errmsgs/t8610.nim @@ -1,5 +1,5 @@ discard """ - errmsg: "invalid type: 'type int' for const" + errormsg: "invalid type: 'type int' for const" """ ## issue #8610 const Foo = int diff --git a/tests/errmsgs/t9768.nim b/tests/errmsgs/t9768.nim index d369150a5..b72a158c7 100644 --- a/tests/errmsgs/t9768.nim +++ b/tests/errmsgs/t9768.nim @@ -1,5 +1,5 @@ discard """ - errmsg: "unhandled exception:" + errormsg: "unhandled exception:" file: "system/fatal.nim" nimout: ''' stack trace: (most recent call last) diff --git a/tests/errmsgs/tstaticexprscope.nim b/tests/errmsgs/tstaticexprscope.nim index 7af5bf9b3..6969e389e 100644 --- a/tests/errmsgs/tstaticexprscope.nim +++ b/tests/errmsgs/tstaticexprscope.nim @@ -1,5 +1,5 @@ discard """ - errmsg: "undeclared identifier: 'z'" + errormsg: "undeclared identifier: 'z'" line: 11 """ diff --git a/tests/errmsgs/ttypeAllowed.nim b/tests/errmsgs/ttypeAllowed.nim index 9efbc6ead..516b616e0 100644 --- a/tests/errmsgs/ttypeAllowed.nim +++ b/tests/errmsgs/ttypeAllowed.nim @@ -1,6 +1,6 @@ discard """ cmd: "nim check $file" -errmsg: "" +errormsg: "" nimout: ''' ttypeAllowed.nim(13, 5) Error: invalid type: 'iterator (a: int, b: int, step: Positive): int{.inline, noSideEffect, gcsafe, locks: 0.}' for let ttypeAllowed.nim(17, 7) Error: invalid type: 'iterator (a: int, b: int, step: Positive): int{.inline, noSideEffect, gcsafe, locks: 0.}' for const diff --git a/tests/lent/tnot_allowed_lent.nim b/tests/lent/tnot_allowed_lent.nim index 2de18d4af..a1db6d184 100644 --- a/tests/lent/tnot_allowed_lent.nim +++ b/tests/lent/tnot_allowed_lent.nim @@ -1,5 +1,5 @@ discard """ - errmsg: "expression has no address" + errormsg: "expression has no address" """ type MyObject = object diff --git a/tests/lent/tnot_allowed_lent2.nim b/tests/lent/tnot_allowed_lent2.nim index e61cae922..d0c958df3 100644 --- a/tests/lent/tnot_allowed_lent2.nim +++ b/tests/lent/tnot_allowed_lent2.nim @@ -1,5 +1,5 @@ discard """ - errmsg: "'x' cannot be assigned to" + errormsg: "'x' cannot be assigned to" line: 10 """ diff --git a/tests/nimble/tnimblepathdollar_fault.nim b/tests/nimble/tnimblepathdollar_fault.nim index 628802030..3f0270123 100644 --- a/tests/nimble/tnimblepathdollar_fault.nim +++ b/tests/nimble/tnimblepathdollar_fault.nim @@ -1,5 +1,5 @@ discard """ - errmsg: "cannot open file: pkgA/module" + errormsg: "cannot open file: pkgA/module" """ # see nims file; comment out `switch("noNimblePath")` there and there would be no error diff --git a/tests/proc/tfunc_type.nim b/tests/proc/tfunc_type.nim index 2a583fcb9..93697acb1 100644 --- a/tests/proc/tfunc_type.nim +++ b/tests/proc/tfunc_type.nim @@ -1,6 +1,6 @@ discard """ - errmsg: "func keyword is not allowed in type descriptions, use proc with {.noSideEffect.} pragma instead" + errormsg: "func keyword is not allowed in type descriptions, use proc with {.noSideEffect.} pragma instead" """ type diff --git a/tests/proc/tillegalreturntype.nim b/tests/proc/tillegalreturntype.nim index e26c44bea..799cad53a 100644 --- a/tests/proc/tillegalreturntype.nim +++ b/tests/proc/tillegalreturntype.nim @@ -1,6 +1,6 @@ discard """ cmd: "nim check $file" - errmsg: "" + errormsg: "" nimout: ''' tillegalreturntype.nim(11, 11) Error: return type 'typed' is only valid for macros and templates tillegalreturntype.nim(14, 11) Error: return type 'untyped' is only valid for macros and templates diff --git a/tests/varres/tprevent_forloopvar_mutations.nim b/tests/varres/tprevent_forloopvar_mutations.nim index ac62608af..15f31d8a3 100644 --- a/tests/varres/tprevent_forloopvar_mutations.nim +++ b/tests/varres/tprevent_forloopvar_mutations.nim @@ -1,5 +1,5 @@ discard """ - errmsg: "type mismatch: got " + errormsg: "type mismatch: got " line: 17 nimout: '''type mismatch: got but expected one of: From 358963193f31fd762b8e26ea4cad162c950785db Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 30 Nov 2020 09:41:50 +0800 Subject: [PATCH 1096/3768] cleanup old codes (#16173) --- tests/tuples/tuple_with_nil.nim | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/tests/tuples/tuple_with_nil.nim b/tests/tuples/tuple_with_nil.nim index b3ed2a96b..7136c8cab 100644 --- a/tests/tuples/tuple_with_nil.nim +++ b/tests/tuples/tuple_with_nil.nim @@ -1,23 +1,16 @@ import macros -from strutils import IdentStartChars import parseutils import unicode import math -import fenv import pegs import streams type - FormatError = object of Exception ## Error in the format string. + FormatError = object of CatchableError ## Error in the format string. Writer = concept W ## Writer to output a character `c`. - when (NimMajor, NimMinor, NimPatch) > (0, 10, 2): - write(W, 'c') - else: - block: - var x: W - write(x, char) + write(W, 'c') FmtAlign = enum ## Format alignment faDefault ## default for given format type From dd5405cbcf7b12a41bd14591fdb2c706c87bf42f Mon Sep 17 00:00:00 2001 From: narimiran Date: Mon, 30 Nov 2020 08:54:49 +0100 Subject: [PATCH 1097/3768] disable testing of `argparse` package until it is fixed --- testament/important_packages.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 2da10e749..6d89df3e5 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -9,7 +9,7 @@ var packages2*: seq[tuple[name, cmd: string; url: string, useHead: bool]] = @[] # packages A-M # pkg1 "alea" -pkg1 "argparse" +# pkg1 "argparse" pkg1 "arraymancer", "nim c tests/tests_cpu.nim" # pkg1 "ast_pattern_matching", "nim c -r --oldgensym:on tests/test1.nim" pkg1 "awk" From 735c06d7f12ee3e4733754f57a1710e9d0dba5d9 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 30 Nov 2020 17:18:47 +0800 Subject: [PATCH 1098/3768] small runnableExamples changes (#16181) --- lib/pure/algorithm.nim | 58 ++++++++++++++++++++---------------------- 1 file changed, 28 insertions(+), 30 deletions(-) diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim index 2430e7330..f2e4848df 100644 --- a/lib/pure/algorithm.nim +++ b/lib/pure/algorithm.nim @@ -11,36 +11,34 @@ ## ## Basic usage ## =========== -## -## .. code-block:: -## import algorithm -## -## type People = tuple -## year: int -## name: string -## -## var a: seq[People] -## -## a.add((2000, "John")) -## a.add((2005, "Marie")) -## a.add((2010, "Jane")) -## -## # Sorting with default system.cmp -## a.sort() -## assert a == @[(year: 2000, name: "John"), (year: 2005, name: "Marie"), -## (year: 2010, name: "Jane")] -## -## proc myCmp(x, y: People): int = -## if x.name < y.name: -1 -## elif x.name == y.name: 0 -## else: 1 -## -## # Sorting with custom proc -## a.sort(myCmp) -## assert a == @[(year: 2010, name: "Jane"), (year: 2000, name: "John"), -## (year: 2005, name: "Marie")] -## -## +## + +runnableExamples: + type People = tuple + year: int + name: string + + var a: seq[People] + + a.add((2000, "John")) + a.add((2005, "Marie")) + a.add((2010, "Jane")) + + # Sorting with default system.cmp + a.sort() + assert a == @[(year: 2000, name: "John"), (year: 2005, name: "Marie"), + (year: 2010, name: "Jane")] + + proc myCmp(x, y: People): int = + if x.name < y.name: -1 + elif x.name == y.name: 0 + else: 1 + + # Sorting with custom proc + a.sort(myCmp) + assert a == @[(year: 2010, name: "Jane"), (year: 2000, name: "John"), + (year: 2005, name: "Marie")] + ## See also ## ======== ## * `sequtils module`_ for working with the built-in seq type From 5a43a20f53d826e5f5e47ca49aa7423fd9cba21b Mon Sep 17 00:00:00 2001 From: Bung Date: Tue, 1 Dec 2020 01:24:12 +0800 Subject: [PATCH 1099/3768] toXXAscii use xor op, saving 30%~50% time (#16193) * toXXAscii use xor op, saving 30%~50% time * Update lib/pure/strutils.nim Co-authored-by: hlaaftana <10591326+hlaaftana@users.noreply.github.com> * Update lib/pure/strutils.nim Co-authored-by: Andreas Rumpf Co-authored-by: hlaaftana <10591326+hlaaftana@users.noreply.github.com> --- lib/pure/strutils.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index f0b447de7..8cbc947bf 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -209,7 +209,7 @@ proc toLowerAscii*(c: char): char {.noSideEffect, doAssert toLowerAscii('A') == 'a' doAssert toLowerAscii('e') == 'e' if c in {'A'..'Z'}: - result = chr(ord(c) + (ord('a') - ord('A'))) + result = char(uint8(c) xor 0b0010_0000'u8) else: result = c @@ -248,7 +248,7 @@ proc toUpperAscii*(c: char): char {.noSideEffect, doAssert toUpperAscii('a') == 'A' doAssert toUpperAscii('E') == 'E' if c in {'a'..'z'}: - result = chr(ord(c) - (ord('a') - ord('A'))) + result = char(uint8(c) xor 0b0010_0000'u8) else: result = c From bb4b27a2ca414f06fbb9d14ff76fa02a088ac141 Mon Sep 17 00:00:00 2001 From: cooldome Date: Mon, 30 Nov 2020 17:45:37 +0000 Subject: [PATCH 1100/3768] fix #16185 (#16195) * fix #16185 * fix test * fix comment * fix comment * better approach --- compiler/injectdestructors.nim | 121 +++++++++++++++++---------------- compiler/trees.nim | 15 ++++ tests/arc/t14383.nim | 51 +++++++++++++- 3 files changed, 126 insertions(+), 61 deletions(-) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 34c11e06c..eba270eea 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -19,7 +19,7 @@ import lineinfos, parampatterns, sighashes, liftdestructors, optimizer, varpartitions -from trees import exprStructuralEquivalent, getRoot +from trees import exprStructuralEquivalent, getRoot, sameLocation type Scope = object # well we do scope-based memory management. \ @@ -969,67 +969,68 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode = internalError(c.graph.config, n.info, "cannot inject destructors to node kind: " & $n.kind) proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, isDecl = false): PNode = - case ri.kind - of nkCallKinds: - result = c.genSink(dest, p(ri, c, s, consumed), isDecl) - of nkBracketExpr: - if isUnpackedTuple(ri[0]): - # unpacking of tuple: take over the elements - result = c.genSink(dest, p(ri, c, s, consumed), isDecl) - elif isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c) and - not aliases(dest, ri): - # Rule 3: `=sink`(x, z); wasMoved(z) - var snk = c.genSink(dest, ri, isDecl) - result = newTree(nkStmtList, snk, c.genWasMoved(ri)) - else: - result = c.genCopy(dest, ri) - result.add p(ri, c, s, consumed) - c.finishCopy(result, dest, isFromSink = false) - of nkBracket: - # array constructor - if ri.len > 0 and isDangerousSeq(ri.typ): - result = c.genCopy(dest, ri) - result.add p(ri, c, s, consumed) - c.finishCopy(result, dest, isFromSink = false) - else: - result = c.genSink(dest, p(ri, c, s, consumed), isDecl) - of nkObjConstr, nkTupleConstr, nkClosure, nkCharLit..nkNilLit: - result = c.genSink(dest, p(ri, c, s, consumed), isDecl) - of nkSym: - if dest.kind == nkSym and dest.sym == ri.sym: - # rule (self-assignment-removal): - result = newNodeI(nkEmpty, dest.info) - elif isSinkParam(ri.sym) and isLastRead(ri, c): - # Rule 3: `=sink`(x, z); wasMoved(z) - let snk = c.genSink(dest, ri, isDecl) - result = newTree(nkStmtList, snk, c.genWasMoved(ri)) - elif ri.sym.kind != skParam and ri.sym.owner == c.owner and - isLastRead(ri, c) and canBeMoved(c, dest.typ) and not isCursor(ri, c): - # Rule 3: `=sink`(x, z); wasMoved(z) - let snk = c.genSink(dest, ri, isDecl) - result = newTree(nkStmtList, snk, c.genWasMoved(ri)) - else: - result = c.genCopy(dest, ri) - result.add p(ri, c, s, consumed) - c.finishCopy(result, dest, isFromSink = false) - of nkHiddenSubConv, nkHiddenStdConv, nkConv, nkObjDownConv, nkObjUpConv: - result = c.genSink(dest, p(ri, c, s, sinkArg), isDecl) - of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt: - template process(child, s): untyped = moveOrCopy(dest, child, c, s, isDecl) - # We know the result will be a stmt so we use that fact to optimize - handleNestedTempl(ri, process, willProduceStmt = true) - of nkRaiseStmt: - result = pRaiseStmt(ri, c, s) + if sameLocation(dest, ri): + # rule (self-assignment-removal): + result = newNodeI(nkEmpty, dest.info) else: - if isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c) and - canBeMoved(c, dest.typ): - # Rule 3: `=sink`(x, z); wasMoved(z) - let snk = c.genSink(dest, ri, isDecl) - result = newTree(nkStmtList, snk, c.genWasMoved(ri)) + case ri.kind + of nkCallKinds: + result = c.genSink(dest, p(ri, c, s, consumed), isDecl) + of nkBracketExpr: + if isUnpackedTuple(ri[0]): + # unpacking of tuple: take over the elements + result = c.genSink(dest, p(ri, c, s, consumed), isDecl) + elif isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c) and + not aliases(dest, ri): + # Rule 3: `=sink`(x, z); wasMoved(z) + var snk = c.genSink(dest, ri, isDecl) + result = newTree(nkStmtList, snk, c.genWasMoved(ri)) + else: + result = c.genCopy(dest, ri) + result.add p(ri, c, s, consumed) + c.finishCopy(result, dest, isFromSink = false) + of nkBracket: + # array constructor + if ri.len > 0 and isDangerousSeq(ri.typ): + result = c.genCopy(dest, ri) + result.add p(ri, c, s, consumed) + c.finishCopy(result, dest, isFromSink = false) + else: + result = c.genSink(dest, p(ri, c, s, consumed), isDecl) + of nkObjConstr, nkTupleConstr, nkClosure, nkCharLit..nkNilLit: + result = c.genSink(dest, p(ri, c, s, consumed), isDecl) + of nkSym: + if isSinkParam(ri.sym) and isLastRead(ri, c): + # Rule 3: `=sink`(x, z); wasMoved(z) + let snk = c.genSink(dest, ri, isDecl) + result = newTree(nkStmtList, snk, c.genWasMoved(ri)) + elif ri.sym.kind != skParam and ri.sym.owner == c.owner and + isLastRead(ri, c) and canBeMoved(c, dest.typ) and not isCursor(ri, c): + # Rule 3: `=sink`(x, z); wasMoved(z) + let snk = c.genSink(dest, ri, isDecl) + result = newTree(nkStmtList, snk, c.genWasMoved(ri)) + else: + result = c.genCopy(dest, ri) + result.add p(ri, c, s, consumed) + c.finishCopy(result, dest, isFromSink = false) + of nkHiddenSubConv, nkHiddenStdConv, nkConv, nkObjDownConv, nkObjUpConv: + result = c.genSink(dest, p(ri, c, s, sinkArg), isDecl) + of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt: + template process(child, s): untyped = moveOrCopy(dest, child, c, s, isDecl) + # We know the result will be a stmt so we use that fact to optimize + handleNestedTempl(ri, process, willProduceStmt = true) + of nkRaiseStmt: + result = pRaiseStmt(ri, c, s) else: - result = c.genCopy(dest, ri) - result.add p(ri, c, s, consumed) - c.finishCopy(result, dest, isFromSink = false) + if isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c) and + canBeMoved(c, dest.typ): + # Rule 3: `=sink`(x, z); wasMoved(z) + let snk = c.genSink(dest, ri, isDecl) + result = newTree(nkStmtList, snk, c.genWasMoved(ri)) + else: + result = c.genCopy(dest, ri) + result.add p(ri, c, s, consumed) + c.finishCopy(result, dest, isFromSink = false) proc computeUninit(c: var Con) = if not c.uninitComputed: diff --git a/compiler/trees.nim b/compiler/trees.nim index 05c060595..eb88f8d67 100644 --- a/compiler/trees.nim +++ b/compiler/trees.nim @@ -54,6 +54,21 @@ proc exprStructuralEquivalent*(a, b: PNode; strictSymEquality=false): bool = strictSymEquality): return result = true +proc sameLocation*(a, b: PNode): bool = + if a == b: true + elif a == nil or b == nil: false + elif a.kind == b.kind: + case a.kind: + of nkCheckedFieldExpr, nkDerefExpr, nkHiddenDeref, + nkAddr, nkHiddenAddr, nkObjDownConv, nkObjUpConv: + sameLocation(a[0], b[0]) + of nkDotExpr, nkBracketExpr: + sameLocation(a[0], b[0]) and sameLocation(a[1], b[1]) + of nkHiddenStdConv, nkHiddenSubConv: sameLocation(a[1], b[1]) + of nkNone..nkNilLit: exprStructuralEquivalent(a, b, strictSymEquality = true) + else: false + else: false + proc sameTree*(a, b: PNode): bool = if a == b: result = true diff --git a/tests/arc/t14383.nim b/tests/arc/t14383.nim index 85f90d1c8..0ce7b7d10 100644 --- a/tests/arc/t14383.nim +++ b/tests/arc/t14383.nim @@ -125,4 +125,53 @@ proc main = let rankdef = avals echo avals.len, " ", rankdef.len -main() \ No newline at end of file +main() + + + + +#------------------------------------------------------------------------------ +# Issue #16185, complex self-assingment elimination +#------------------------------------------------------------------------------ + +type + CpuStorage*[T] = ref CpuStorageObj[T] + CpuStorageObj[T] = object + size*: int + raw_buffer*: ptr UncheckedArray[T] + Tensor[T] = object + buf*: CpuStorage[T] + TestObject = object + x: Tensor[float] + +proc `=destroy`[T](s: var CpuStorageObj[T]) = + if s.raw_buffer != nil: + s.raw_buffer.deallocShared() + s.size = 0 + s.raw_buffer = nil + +proc `=`[T](a: var CpuStorageObj[T]; b: CpuStorageObj[T]) {.error.} + +proc allocCpuStorage[T](s: var CpuStorage[T], size: int) = + new(s) + s.raw_buffer = cast[ptr UncheckedArray[T]](allocShared0(sizeof(T) * size)) + s.size = size + +proc newTensor[T](size: int): Tensor[T] = + allocCpuStorage(result.buf, size) + +proc `[]`[T](t: Tensor[T], idx: int): T = t.buf.raw_buffer[idx] +proc `[]=`[T](t: Tensor[T], idx: int, val: T) = t.buf.raw_buffer[idx] = val + +proc toTensor[T](s: seq[T]): Tensor[T] = + result = newTensor[T](s.len) + for i, x in s: + result[i] = x + +proc main2() = + var t: TestObject + t.x = toTensor(@[1.0, 2, 3, 4]) + t.x = t.x + doAssert(t.x.buf != nil) # self-assignment above should be eliminated + +main2() From 2e4b6b94e675a7de96fcf2b3e8ed4224dccd5747 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 30 Nov 2020 23:36:38 +0100 Subject: [PATCH 1101/3768] Revert "fix #16185 (#16195)" (#16197) This reverts commit bb4b27a2ca414f06fbb9d14ff76fa02a088ac141. --- compiler/injectdestructors.nim | 121 ++++++++++++++++----------------- compiler/trees.nim | 15 ---- tests/arc/t14383.nim | 51 +------------- 3 files changed, 61 insertions(+), 126 deletions(-) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index eba270eea..34c11e06c 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -19,7 +19,7 @@ import lineinfos, parampatterns, sighashes, liftdestructors, optimizer, varpartitions -from trees import exprStructuralEquivalent, getRoot, sameLocation +from trees import exprStructuralEquivalent, getRoot type Scope = object # well we do scope-based memory management. \ @@ -969,68 +969,67 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode = internalError(c.graph.config, n.info, "cannot inject destructors to node kind: " & $n.kind) proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, isDecl = false): PNode = - if sameLocation(dest, ri): - # rule (self-assignment-removal): - result = newNodeI(nkEmpty, dest.info) - else: - case ri.kind - of nkCallKinds: + case ri.kind + of nkCallKinds: + result = c.genSink(dest, p(ri, c, s, consumed), isDecl) + of nkBracketExpr: + if isUnpackedTuple(ri[0]): + # unpacking of tuple: take over the elements result = c.genSink(dest, p(ri, c, s, consumed), isDecl) - of nkBracketExpr: - if isUnpackedTuple(ri[0]): - # unpacking of tuple: take over the elements - result = c.genSink(dest, p(ri, c, s, consumed), isDecl) - elif isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c) and - not aliases(dest, ri): - # Rule 3: `=sink`(x, z); wasMoved(z) - var snk = c.genSink(dest, ri, isDecl) - result = newTree(nkStmtList, snk, c.genWasMoved(ri)) - else: - result = c.genCopy(dest, ri) - result.add p(ri, c, s, consumed) - c.finishCopy(result, dest, isFromSink = false) - of nkBracket: - # array constructor - if ri.len > 0 and isDangerousSeq(ri.typ): - result = c.genCopy(dest, ri) - result.add p(ri, c, s, consumed) - c.finishCopy(result, dest, isFromSink = false) - else: - result = c.genSink(dest, p(ri, c, s, consumed), isDecl) - of nkObjConstr, nkTupleConstr, nkClosure, nkCharLit..nkNilLit: - result = c.genSink(dest, p(ri, c, s, consumed), isDecl) - of nkSym: - if isSinkParam(ri.sym) and isLastRead(ri, c): - # Rule 3: `=sink`(x, z); wasMoved(z) - let snk = c.genSink(dest, ri, isDecl) - result = newTree(nkStmtList, snk, c.genWasMoved(ri)) - elif ri.sym.kind != skParam and ri.sym.owner == c.owner and - isLastRead(ri, c) and canBeMoved(c, dest.typ) and not isCursor(ri, c): - # Rule 3: `=sink`(x, z); wasMoved(z) - let snk = c.genSink(dest, ri, isDecl) - result = newTree(nkStmtList, snk, c.genWasMoved(ri)) - else: - result = c.genCopy(dest, ri) - result.add p(ri, c, s, consumed) - c.finishCopy(result, dest, isFromSink = false) - of nkHiddenSubConv, nkHiddenStdConv, nkConv, nkObjDownConv, nkObjUpConv: - result = c.genSink(dest, p(ri, c, s, sinkArg), isDecl) - of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt: - template process(child, s): untyped = moveOrCopy(dest, child, c, s, isDecl) - # We know the result will be a stmt so we use that fact to optimize - handleNestedTempl(ri, process, willProduceStmt = true) - of nkRaiseStmt: - result = pRaiseStmt(ri, c, s) + elif isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c) and + not aliases(dest, ri): + # Rule 3: `=sink`(x, z); wasMoved(z) + var snk = c.genSink(dest, ri, isDecl) + result = newTree(nkStmtList, snk, c.genWasMoved(ri)) else: - if isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c) and - canBeMoved(c, dest.typ): - # Rule 3: `=sink`(x, z); wasMoved(z) - let snk = c.genSink(dest, ri, isDecl) - result = newTree(nkStmtList, snk, c.genWasMoved(ri)) - else: - result = c.genCopy(dest, ri) - result.add p(ri, c, s, consumed) - c.finishCopy(result, dest, isFromSink = false) + result = c.genCopy(dest, ri) + result.add p(ri, c, s, consumed) + c.finishCopy(result, dest, isFromSink = false) + of nkBracket: + # array constructor + if ri.len > 0 and isDangerousSeq(ri.typ): + result = c.genCopy(dest, ri) + result.add p(ri, c, s, consumed) + c.finishCopy(result, dest, isFromSink = false) + else: + result = c.genSink(dest, p(ri, c, s, consumed), isDecl) + of nkObjConstr, nkTupleConstr, nkClosure, nkCharLit..nkNilLit: + result = c.genSink(dest, p(ri, c, s, consumed), isDecl) + of nkSym: + if dest.kind == nkSym and dest.sym == ri.sym: + # rule (self-assignment-removal): + result = newNodeI(nkEmpty, dest.info) + elif isSinkParam(ri.sym) and isLastRead(ri, c): + # Rule 3: `=sink`(x, z); wasMoved(z) + let snk = c.genSink(dest, ri, isDecl) + result = newTree(nkStmtList, snk, c.genWasMoved(ri)) + elif ri.sym.kind != skParam and ri.sym.owner == c.owner and + isLastRead(ri, c) and canBeMoved(c, dest.typ) and not isCursor(ri, c): + # Rule 3: `=sink`(x, z); wasMoved(z) + let snk = c.genSink(dest, ri, isDecl) + result = newTree(nkStmtList, snk, c.genWasMoved(ri)) + else: + result = c.genCopy(dest, ri) + result.add p(ri, c, s, consumed) + c.finishCopy(result, dest, isFromSink = false) + of nkHiddenSubConv, nkHiddenStdConv, nkConv, nkObjDownConv, nkObjUpConv: + result = c.genSink(dest, p(ri, c, s, sinkArg), isDecl) + of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt: + template process(child, s): untyped = moveOrCopy(dest, child, c, s, isDecl) + # We know the result will be a stmt so we use that fact to optimize + handleNestedTempl(ri, process, willProduceStmt = true) + of nkRaiseStmt: + result = pRaiseStmt(ri, c, s) + else: + if isAnalysableFieldAccess(ri, c.owner) and isLastRead(ri, c) and + canBeMoved(c, dest.typ): + # Rule 3: `=sink`(x, z); wasMoved(z) + let snk = c.genSink(dest, ri, isDecl) + result = newTree(nkStmtList, snk, c.genWasMoved(ri)) + else: + result = c.genCopy(dest, ri) + result.add p(ri, c, s, consumed) + c.finishCopy(result, dest, isFromSink = false) proc computeUninit(c: var Con) = if not c.uninitComputed: diff --git a/compiler/trees.nim b/compiler/trees.nim index eb88f8d67..05c060595 100644 --- a/compiler/trees.nim +++ b/compiler/trees.nim @@ -54,21 +54,6 @@ proc exprStructuralEquivalent*(a, b: PNode; strictSymEquality=false): bool = strictSymEquality): return result = true -proc sameLocation*(a, b: PNode): bool = - if a == b: true - elif a == nil or b == nil: false - elif a.kind == b.kind: - case a.kind: - of nkCheckedFieldExpr, nkDerefExpr, nkHiddenDeref, - nkAddr, nkHiddenAddr, nkObjDownConv, nkObjUpConv: - sameLocation(a[0], b[0]) - of nkDotExpr, nkBracketExpr: - sameLocation(a[0], b[0]) and sameLocation(a[1], b[1]) - of nkHiddenStdConv, nkHiddenSubConv: sameLocation(a[1], b[1]) - of nkNone..nkNilLit: exprStructuralEquivalent(a, b, strictSymEquality = true) - else: false - else: false - proc sameTree*(a, b: PNode): bool = if a == b: result = true diff --git a/tests/arc/t14383.nim b/tests/arc/t14383.nim index 0ce7b7d10..85f90d1c8 100644 --- a/tests/arc/t14383.nim +++ b/tests/arc/t14383.nim @@ -125,53 +125,4 @@ proc main = let rankdef = avals echo avals.len, " ", rankdef.len -main() - - - - -#------------------------------------------------------------------------------ -# Issue #16185, complex self-assingment elimination -#------------------------------------------------------------------------------ - -type - CpuStorage*[T] = ref CpuStorageObj[T] - CpuStorageObj[T] = object - size*: int - raw_buffer*: ptr UncheckedArray[T] - Tensor[T] = object - buf*: CpuStorage[T] - TestObject = object - x: Tensor[float] - -proc `=destroy`[T](s: var CpuStorageObj[T]) = - if s.raw_buffer != nil: - s.raw_buffer.deallocShared() - s.size = 0 - s.raw_buffer = nil - -proc `=`[T](a: var CpuStorageObj[T]; b: CpuStorageObj[T]) {.error.} - -proc allocCpuStorage[T](s: var CpuStorage[T], size: int) = - new(s) - s.raw_buffer = cast[ptr UncheckedArray[T]](allocShared0(sizeof(T) * size)) - s.size = size - -proc newTensor[T](size: int): Tensor[T] = - allocCpuStorage(result.buf, size) - -proc `[]`[T](t: Tensor[T], idx: int): T = t.buf.raw_buffer[idx] -proc `[]=`[T](t: Tensor[T], idx: int, val: T) = t.buf.raw_buffer[idx] = val - -proc toTensor[T](s: seq[T]): Tensor[T] = - result = newTensor[T](s.len) - for i, x in s: - result[i] = x - -proc main2() = - var t: TestObject - t.x = toTensor(@[1.0, 2, 3, 4]) - t.x = t.x - doAssert(t.x.buf != nil) # self-assignment above should be eliminated - -main2() +main() \ No newline at end of file From 1d1b3f79fdccfdd359781dbde3c5aea3b67bf05b Mon Sep 17 00:00:00 2001 From: hlaaftana <10591326+hlaaftana@users.noreply.github.com> Date: Tue, 1 Dec 2020 14:01:36 +0300 Subject: [PATCH 1102/3768] fix broken => for typed procs with pragmas (#16190) * fix broken => for typed procs with pragmas * add more sugar tests * add ending quote --- lib/pure/sugar.nim | 15 ++++++--------- tests/macros/tclosuremacro.nim | 13 +++++++++++++ 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/lib/pure/sugar.nim b/lib/pure/sugar.nim index d9c98c360..047104972 100644 --- a/lib/pure/sugar.nim +++ b/lib/pure/sugar.nim @@ -17,10 +17,7 @@ proc checkPragma(ex, prag: var NimNode) = since (1, 3): if ex.kind == nnkPragmaExpr: prag = ex[1] - if ex[0].kind == nnkPar and ex[0].len == 1: - ex = ex[0][0] - else: - ex = ex[0] + ex = ex[0] proc createProcType(p, b: NimNode): NimNode {.compileTime.} = result = newNimNode(nnkProcTy) @@ -66,12 +63,12 @@ macro `=>`*(p, b: untyped): untyped = type Bot = object - call: proc (): string {.nosideEffect.} + call: proc (name: string): string {.noSideEffect.} var myBot = Bot() - myBot.call = () {.nosideEffect.} => "I'm a bot." - doAssert myBot.call() == "I'm a bot." + myBot.call = (name: string) {.noSideEffect.} => "Hello " & name & ", I'm a bot." + doAssert myBot.call("John") == "Hello John, I'm a bot." var params = @[ident"auto"] @@ -89,7 +86,7 @@ macro `=>`*(p, b: untyped): untyped = checkPragma(p, pragma) # check again after -> transform since (1, 3): - if p.kind == nnkCall: + if p.kind in {nnkCall, nnkObjConstr}: # foo(x, y) => x + y kind = nnkProcDef name = p[0] @@ -129,7 +126,7 @@ macro `=>`*(p, b: untyped): untyped = error("Expected proc type (->) got (" & c[0].strVal & ").", c) break else: - error("Incorrect procedure parameter list.", c) + error("Incorrect procedure parameter.", c) params.add(identDefs) of nnkIdent: var identDefs = newNimNode(nnkIdentDefs) diff --git a/tests/macros/tclosuremacro.nim b/tests/macros/tclosuremacro.nim index 5c41c317a..2a7847b9e 100644 --- a/tests/macros/tclosuremacro.nim +++ b/tests/macros/tclosuremacro.nim @@ -71,3 +71,16 @@ m: echo "yes" sugarWithPragma() {.m.} => echo "sugarWithPragma called" + +typedParamAndPragma(x, y: int) {.used.} -> int => x + y +doAssert typedParamAndPragma(1, 2) == 3 + +type + Bot = object + call: proc (): string {.noSideEffect.} + +var myBot = Bot() +myBot.call = () {.noSideEffect.} => "I'm a bot." +doAssert myBot.call() == "I'm a bot." + + From c65f95417a85a04707712a3b481ffa0b94c99437 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Wed, 2 Dec 2020 03:19:39 +0800 Subject: [PATCH 1103/3768] better addInt (#16160) * better addint * Update lib/system/strmantle.nim Co-authored-by: Juan Carlos --- lib/system/strmantle.nim | 96 +++++++++++++++++++++++++++++++------ tests/system/tstrmantle.nim | 46 ++++++++++++++++++ 2 files changed, 127 insertions(+), 15 deletions(-) create mode 100644 tests/system/tstrmantle.nim diff --git a/lib/system/strmantle.nim b/lib/system/strmantle.nim index fa6ff411b..b5d275e25 100644 --- a/lib/system/strmantle.nim +++ b/lib/system/strmantle.nim @@ -9,6 +9,72 @@ # Compilerprocs for strings that do not depend on the string implementation. +const digitsTable = "0001020304050607080910111213141516171819" & + "2021222324252627282930313233343536373839" & + "4041424344454647484950515253545556575859" & + "6061626364656667686970717273747576777879" & + "8081828384858687888990919293949596979899" + # Inspired by https://engineering.fb.com/2013/03/15/developer-tools/three-optimization-tips-for-c + # Generates: + # .. code-block:: nim + # var res = "" + # for i in 0 .. 99: + # if i < 10: + # res.add "0" & $i + # else: + # res.add $i + # doAssert res == digitsTable + + +func digits10(num: uint64): int {.noinline.} = + if num < 10: + result = 1 + elif num < 100: + result = 2 + elif num < 1_000: + result = 3 + elif num < 10_000: + result = 4 + elif num < 100_000: + result = 5 + elif num < 1_000_000: + result = 6 + elif num < 10_000_000: + result = 7 + elif num < 100_000_000: + result = 8 + elif num < 1_000_000_000: + result = 9 + elif num < 10_000_000_000'u64: + result = 10 + elif num < 100_000_000_000'u64: + result = 11 + elif num < 1_000_000_000_000'u64: + result = 12 + else: + result = 12 + digits10(num div 1_000_000_000_000'u64) + +template numToString(result: var string, origin: uint64, length: int) = + var num = origin + var next = length - 1 + const nbatch = 100 + + while num >= nbatch: + let originNum = num + num = num div nbatch + let index = (originNum - num * nbatch) shl 1 + result[next] = digitsTable[index + 1] + result[next - 1] = digitsTable[index] + dec(next, 2) + + # process last 1-2 digits + if num < 10: + result[next] = chr(ord('0') + num) + else: + let index = num * 2 + result[next] = digitsTable[index + 1] + result[next - 1] = digitsTable[index] + proc cmpStrings(a, b: string): int {.inline, compilerproc.} = let alen = a.len let blen = b.len @@ -49,22 +115,22 @@ proc addInt*(result: var string; x: int64) = ## b = 45 ## a.addInt(b) # a <- "12345" let base = result.len - setLen(result, base + sizeof(x)*4) - var i = 0 - var y = x - while true: - var d = y div 10 - result[base+i] = chr(abs(int(y - d*10)) + ord('0')) - inc(i) - y = d - if y == 0: break + var length: int + var num: uint64 + if x < 0: - result[base+i] = '-' - inc(i) - setLen(result, base+i) - # mirror the string: - for j in 0..i div 2 - 1: - swap(result[base+j], result[base+i-j-1]) + if x == low(int64): + num = uint64(x) + else: + num = uint64(-x) + length = base + digits10(num) + 1 + setLen(result, length) + result[base] = '-' + else: + num = uint64(x) + length = base + digits10(num) + setLen(result, length) + numToString(result, num, length) proc nimIntToStr(x: int): string {.compilerRtl.} = result = newStringOfCap(sizeof(x)*4) diff --git a/tests/system/tstrmantle.nim b/tests/system/tstrmantle.nim new file mode 100644 index 000000000..5b232193e --- /dev/null +++ b/tests/system/tstrmantle.nim @@ -0,0 +1,46 @@ +var res = newStringOfCap(24) + +for i in 0 .. 9: + res.addInt int64(i) + +doAssert res == "0123456789" + +res = newStringOfCap(24) + +for i in -9 .. 0: + res.addInt int64(i) + +doAssert res == "-9-8-7-6-5-4-3-2-10" + +res = newStringOfCap(24) +res.addInt high(int64) +doAssert res == "9223372036854775807" + +res = newStringOfCap(24) +res.addInt low(int64) +doAssert res == "-9223372036854775808" + +res = newStringOfCap(12) +res.addInt high(int32) +doAssert res == "2147483647" + +res = newStringOfCap(12) +res.addInt low(int32) +doAssert res == "-2147483648" + +res = newStringOfCap(12) +res.addInt high(int16) +doAssert res == "32767" + +res = newStringOfCap(212) +res.addInt low(int16) +doAssert res == "-32768" + + +res = newStringOfCap(12) +res.addInt high(int8) +doAssert res == "127" + +res = newStringOfCap(12) +res.addInt low(int8) +doAssert res == "-128" From fd9870568020939aae902ef5ccb26d12e45673b6 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 1 Dec 2020 11:34:53 -0800 Subject: [PATCH 1104/3768] error instead of echo when testament category is empty; fix realtimeGC (#16162) --- testament/categories.nim | 4 ++- tests/realtimeGC/cmain.c | 4 ++- tests/realtimeGC/main.nim.cfg | 6 ----- tests/realtimeGC/readme.txt | 23 +++++----------- tests/realtimeGC/shared.nim | 5 ---- tests/realtimeGC/shared.nim.cfg | 5 ---- tests/realtimeGC/{nmain.nim => tmain.nim} | 32 ++++++++++++++--------- 7 files changed, 32 insertions(+), 47 deletions(-) delete mode 100644 tests/realtimeGC/main.nim.cfg delete mode 100644 tests/realtimeGC/shared.nim.cfg rename tests/realtimeGC/{nmain.nim => tmain.nim} (50%) diff --git a/testament/categories.nim b/testament/categories.nim index 3429b760a..c48d611b1 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -748,7 +748,9 @@ proc processCategory(r: var TResults, cat: Category, testSpec r, test inc testsRun if testsRun == 0: - echo "[Warning] - Invalid category specified \"", cat.string, "\", no tests were run" + const whiteListedDirs = ["deps"] + doAssert cat.string in whiteListedDirs, + "Invalid category specified: '$#' not in whilelist: $#" % [cat.string, $whiteListedDirs] proc processPattern(r: var TResults, pattern, options: string; simulate: bool) = var testsRun = 0 diff --git a/tests/realtimeGC/cmain.c b/tests/realtimeGC/cmain.c index 0d4bb096a..4bc5bd026 100644 --- a/tests/realtimeGC/cmain.c +++ b/tests/realtimeGC/cmain.c @@ -1,3 +1,5 @@ +/* xxx consider removing, this seems redundant with tmain.nim */ + #ifdef WIN #include #else @@ -28,7 +30,7 @@ int main(int argc, char* argv[]) status = (pFunc)GetProcAddress((HMODULE) hndl, (char const*)"status"); count = (pFunc)GetProcAddress((HMODULE) hndl, (char const*)"count"); checkOccupiedMem = (pFunc)GetProcAddress((HMODULE) hndl, (char const*)"checkOccupiedMem"); -#else /* OSX || NIX */ +#else /* OSX || NIX xxx: OSX assumes dylib*/ hndl = (void*) dlopen((char const*)"./tests/realtimeGC/libshared.so", RTLD_LAZY); status = (pFunc) dlsym(hndl, (char const*)"status"); count = (pFunc) dlsym(hndl, (char const*)"count"); diff --git a/tests/realtimeGC/main.nim.cfg b/tests/realtimeGC/main.nim.cfg deleted file mode 100644 index fed4fa471..000000000 --- a/tests/realtimeGC/main.nim.cfg +++ /dev/null @@ -1,6 +0,0 @@ - ---app:console ---threads:on - --d:release --d:useRealtimeGC diff --git a/tests/realtimeGC/readme.txt b/tests/realtimeGC/readme.txt index b2e37a1f0..b2788520e 100644 --- a/tests/realtimeGC/readme.txt +++ b/tests/realtimeGC/readme.txt @@ -1,21 +1,10 @@ Test the realtime GC without linking nimrtl.dll/so. -Note, this is a long running test, default is 35 minutes. To change the -the run time see RUNTIME in nmain.nim and cmain.c. +To build by hand and run the test for 35 minutes: + $ nim r --threads:on -d:runtimeSecs:2100 tests/realtimeGC/nmain.nim -You can build shared.nim, nmain.nim, and cmain.c by running make (nix systems) -or make.bat (Windows systems). They both assume GCC and that it's in your -path. Output: shared.(dll/so), cmain(.exe), nmain(.exe). - -To run the test: execute either nmain or cmain in a shell window. - -To build by hand: - - - build the shared object (shared.nim): - - $ nim c tests/realtimeGC/shared.nim - - - build the client executables: - - $ nim c --threads:on tests/realtimeGC/nmain.nim +``` +xxx do we still need tests/realtimeGC/cmain.c? +if so, tests/realtimeGC/cmain.c needs to updated and factorized with nmain.nim to avoid duplication (even if it's a C file) +``` $ gcc -o tests/realtimeGC/cmain tests/realtimeGC/cmain.c -ldl diff --git a/tests/realtimeGC/shared.nim b/tests/realtimeGC/shared.nim index 1abe42089..8b5c05e47 100644 --- a/tests/realtimeGC/shared.nim +++ b/tests/realtimeGC/shared.nim @@ -1,8 +1,3 @@ -discard """ -cmd: "nim $target --debuginfo --hints:on --app:lib $options $file" -action: compile -""" - import strutils # Global state, accessing with threads, no locks. Don't do this at diff --git a/tests/realtimeGC/shared.nim.cfg b/tests/realtimeGC/shared.nim.cfg deleted file mode 100644 index e153b26fa..000000000 --- a/tests/realtimeGC/shared.nim.cfg +++ /dev/null @@ -1,5 +0,0 @@ ---app:lib ---threads:on - --d:release --d:useRealtimeGC diff --git a/tests/realtimeGC/nmain.nim b/tests/realtimeGC/tmain.nim similarity index 50% rename from tests/realtimeGC/nmain.nim rename to tests/realtimeGC/tmain.nim index c9f558dbc..f1515a549 100644 --- a/tests/realtimeGC/nmain.nim +++ b/tests/realtimeGC/tmain.nim @@ -1,18 +1,27 @@ discard """ - cmd: "nim $target --debuginfo $options $file" - output: "Done" + cmd: "nim $target --threads:on -d:release -d:useRealtimeGC $options $file" + joinable:false """ -import times, os, threadpool +#[ +was: cmd: "nim $target --debuginfo $options $file" +these dont' seem needed --debuginfo +nor these from the previous main.nim.cfg: --app:console +]# -const RUNTIME = 15 * 60 # 15 minutes +import times, os, strformat, strutils +from stdtest/specialpaths import buildDir +# import threadpool -when defined(windows): - const dllname = "./tests/realtimeGC/shared.dll" -elif defined(macosx): - const dllname = "./tests/realtimeGC/libshared.dylib" -else: - const dllname = "./tests/realtimeGC/libshared.so" +const runtimeSecs {.intdefine.} = 5 + +const file = "shared.nim" +const dllname = buildDir / (DynlibFormat % file) + +static: + let nim = getCurrentCompilerExe() + let (output, exitCode) = gorgeEx(fmt"{nim} c -o:{dllname} --debuginfo --app:lib --threads:on -d:release -d:useRealtimeGC {file}") + doAssert exitCode == 0, output proc status() {.importc: "status", dynlib: dllname.} proc count() {.importc: "count", dynlib: dllname.} @@ -20,7 +29,7 @@ proc checkOccupiedMem() {.importc: "checkOccupiedMem", dynlib: dllname.} proc process() = let startTime = getTime() - let runTime = cast[Time](RUNTIME) # + let runTime = cast[Time](runtimeSecs) var accumTime: Time while accumTime < runTime: for i in 0..10: @@ -41,6 +50,5 @@ proc main() = # for i in 0..0: # spawn process() # sync() - echo("Done") main() From 62eb1312a099aeae0115beb038ed8449ccd095a7 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Wed, 2 Dec 2020 04:06:37 +0800 Subject: [PATCH 1105/3768] minor improve the docs of parsecfg (#16208) --- lib/pure/parsecfg.nim | 52 ++++++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/lib/pure/parsecfg.nim b/lib/pure/parsecfg.nim index 15868c219..319f8f625 100644 --- a/lib/pure/parsecfg.nim +++ b/lib/pure/parsecfg.nim @@ -7,8 +7,8 @@ # distribution, for details about the copyright. # -## The ``parsecfg`` module implements a high performance configuration file -## parser. The configuration file's syntax is similar to the Windows ``.ini`` +## The `parsecfg` module implements a high performance configuration file +## parser. The configuration file's syntax is similar to the Windows `.ini` ## format, but much more powerful, as it is not a line based parser. String ## literals, raw string literals and triple quoted string literals are supported ## as in the Nim programming language. @@ -116,9 +116,9 @@ include "system/inclrtl" type CfgEventKind* = enum ## enumeration of all events that may occur when parsing cfgEof, ## end of file reached - cfgSectionStart, ## a ``[section]`` has been parsed - cfgKeyValuePair, ## a ``key=value`` pair has been detected - cfgOption, ## a ``--key=value`` command line option + cfgSectionStart, ## a `[section]` has been parsed + cfgKeyValuePair, ## a `key=value` pair has been detected + cfgOption, ## a `--key=value` command line option cfgError ## an error occurred during parsing CfgEvent* = object of RootObj ## describes a parsing event @@ -126,12 +126,12 @@ type of cfgEof: nil of cfgSectionStart: section*: string ## `section` contains the name of the - ## parsed section start (syntax: ``[section]``) + ## parsed section start (syntax: `[section]`) of cfgKeyValuePair, cfgOption: key*, value*: string ## contains the (key, value) pair if an option - ## of the form ``--key: value`` or an ordinary - ## ``key= value`` pair has been parsed. - ## ``value==""`` if it was not specified in the + ## of the form `--key: value` or an ordinary + ## `key= value` pair has been parsed. + ## `value==""` if it was not specified in the ## configuration file. of cfgError: ## the parser encountered an error: `msg` msg*: string ## contains the error message. No exceptions @@ -157,7 +157,7 @@ proc rawGetTok(c: var CfgParser, tok: var Token) {.gcsafe.} proc open*(c: var CfgParser, input: Stream, filename: string, lineOffset = 0) {.rtl, extern: "npc$1".} = - ## initializes the parser with an input stream. `Filename` is only used + ## Initializes the parser with an input stream. `Filename` is only used ## for nice error messages. `lineOffset` can be used to influence the line ## number information in the generated error messages. lexbase.open(c, input) @@ -168,19 +168,19 @@ proc open*(c: var CfgParser, input: Stream, filename: string, rawGetTok(c, c.tok) proc close*(c: var CfgParser) {.rtl, extern: "npc$1".} = - ## closes the parser `c` and its associated input stream. + ## Closes the parser `c` and its associated input stream. lexbase.close(c) proc getColumn*(c: CfgParser): int {.rtl, extern: "npc$1".} = - ## get the current column the parser has arrived at. + ## Gets the current column the parser has arrived at. result = getColNumber(c, c.bufpos) proc getLine*(c: CfgParser): int {.rtl, extern: "npc$1".} = - ## get the current line the parser has arrived at. + ## Gets the current line the parser has arrived at. result = c.lineNumber proc getFilename*(c: CfgParser): string {.rtl, extern: "npc$1".} = - ## get the filename of the file that the parser processes. + ## Gets the filename of the file that the parser processes. result = c.filename proc handleHexChar(c: var CfgParser, xi: var int) = @@ -367,19 +367,19 @@ proc rawGetTok(c: var CfgParser, tok: var Token) = else: getSymbol(c, tok) proc errorStr*(c: CfgParser, msg: string): string {.rtl, extern: "npc$1".} = - ## returns a properly formatted error message containing current line and + ## Returns a properly formatted error message containing current line and ## column information. result = `%`("$1($2, $3) Error: $4", [c.filename, $getLine(c), $getColumn(c), msg]) proc warningStr*(c: CfgParser, msg: string): string {.rtl, extern: "npc$1".} = - ## returns a properly formatted warning message containing current line and + ## Returns a properly formatted warning message containing current line and ## column information. result = `%`("$1($2, $3) Warning: $4", [c.filename, $getLine(c), $getColumn(c), msg]) proc ignoreMsg*(c: CfgParser, e: CfgEvent): string {.rtl, extern: "npc$1".} = - ## returns a properly formatted warning message containing that + ## Returns a properly formatted warning message containing that ## an entry is ignored. case e.kind of cfgSectionStart: result = c.warningStr("section ignored: " & e.section) @@ -410,7 +410,7 @@ proc getKeyValPair(c: var CfgParser, kind: CfgEventKind): CfgEvent = rawGetTok(c, c.tok) proc next*(c: var CfgParser): CfgEvent {.rtl, extern: "npc$1".} = - ## retrieves the first/next event. This controls the parser. + ## Retrieves the first/next event. This controls the parser. case c.tok.kind of tkEof: result = CfgEvent(kind: cfgEof) @@ -442,12 +442,12 @@ type Config* = OrderedTableRef[string, OrderedTableRef[string, string]] proc newConfig*(): Config = - ## Create a new configuration table. + ## Creates a new configuration table. ## Useful when wanting to create a configuration file. result = newOrderedTable[string, OrderedTableRef[string, string]]() proc loadConfig*(stream: Stream, filename: string = "[stream]"): Config = - ## Load the specified configuration from stream into a new Config instance. + ## Loadw the specified configuration from stream into a new Config instance. ## `filename` parameter is only used for nicer error messages. var dict = newOrderedTable[string, OrderedTableRef[string, string]]() var curSection = "" ## Current section, @@ -480,7 +480,7 @@ proc loadConfig*(stream: Stream, filename: string = "[stream]"): Config = result = dict proc loadConfig*(filename: string): Config = - ## Load the specified configuration file into a new Config instance. + ## Loads the specified configuration file into a new Config instance. let file = open(filename, fmRead) let fileStream = newFileStream(file) defer: fileStream.close() @@ -505,7 +505,7 @@ proc replace(s: string): string = result = d proc writeConfig*(dict: Config, stream: Stream) = - ## Writes the contents of the table to the specified stream + ## Writes the contents of the table to the specified stream. ## ## **Note:** Comment statement will be ignored. for section, sectionData in dict.pairs(): @@ -546,7 +546,8 @@ proc writeConfig*(dict: Config, stream: Stream) = proc `$`*(dict: Config): string = ## Writes the contents of the table to string. - ## Note: Comment statement will be ignored. + ## + ## **Note:** Comment statement will be ignored. let stream = newStringStream() defer: stream.close() dict.writeConfig(stream) @@ -554,7 +555,8 @@ proc `$`*(dict: Config): string = proc writeConfig*(dict: Config, filename: string) = ## Writes the contents of the table to the specified configuration file. - ## Note: Comment statement will be ignored. + ## + ## **Note:** Comment statement will be ignored. let file = open(filename, fmWrite) defer: file.close() let fileStream = newFileStream(file) @@ -584,7 +586,7 @@ proc delSection*(dict: var Config, section: string) = dict.del(section) proc delSectionKey*(dict: var Config, section, key: string) = - ## Delete the key of the specified section. + ## Deletes the key of the specified section. if dict.hasKey(section): if dict[section].hasKey(key): if dict[section].len == 1: From e0b4f05053e87b5b498c53338eed643787783b18 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 1 Dec 2020 12:12:40 -0800 Subject: [PATCH 1106/3768] nimout now consistently uses nimoutCheck (#16189) --- testament/lib/stdtest/testutils.nim | 11 +++++++++++ testament/testament.nim | 16 ++++------------ tests/casestmt/tcaseexpr1.nim | 16 ++++++++-------- tests/stdlib/ttestutils.nim | 6 ++++++ 4 files changed, 29 insertions(+), 20 deletions(-) create mode 100644 tests/stdlib/ttestutils.nim diff --git a/testament/lib/stdtest/testutils.nim b/testament/lib/stdtest/testutils.nim index 8083a63e8..34aa3b751 100644 --- a/testament/lib/stdtest/testutils.nim +++ b/testament/lib/stdtest/testutils.nim @@ -1,4 +1,5 @@ import std/private/miscdollars +import std/strutils template flakyAssert*(cond: untyped, msg = "", notifySuccess = true) = ## API to deal with flaky or failing tests. This avoids disabling entire tests @@ -23,3 +24,13 @@ template flakyAssert*(cond: untyped, msg = "", notifySuccess = true) = msg2.add " FLAKY_FAILURE " msg2.add $expr & " " & msg echo msg2 + +proc greedyOrderedSubsetLines*(lhs, rhs: string): bool = + ## returns true if each stripped line in `lhs` appears in rhs, using a greedy matching. + let rhs = rhs.strip + var currentPos = 0 + for line in lhs.strip.splitLines: + currentPos = rhs.find(line.strip, currentPos) + if currentPos < 0: + return false + return true diff --git a/testament/testament.nim b/testament/testament.nim index f2ac9c338..58b3a8040 100644 --- a/testament/testament.nim +++ b/testament/testament.nim @@ -15,6 +15,7 @@ import algorithm, times, md5, sequtils, azure, intsets from std/sugar import dup import compiler/nodejs +import lib/stdtest/testutils var useColors = true var backendLogging = true @@ -364,7 +365,7 @@ proc cmpMsgs(r: var TResults, expected, given: TSpec, test: TTest, target: TTarg checkForInlineErrors(r, expected, given, test, target) elif strip(expected.msg) notin strip(given.msg): r.addResult(test, target, expected.msg, given.msg, reMsgsDiffer) - elif expected.nimout.len > 0 and expected.nimout.normalizeMsg notin given.nimout.normalizeMsg: + elif expected.nimout.len > 0 and not greedyOrderedSubsetLines(expected.nimout, given.nimout): r.addResult(test, target, expected.nimout, given.nimout, reMsgsDiffer) elif expected.tfile == "" and extractFilename(expected.file) != extractFilename(given.file) and "internal error:" notin expected.msg: @@ -422,17 +423,8 @@ proc codegenCheck(test: TTest, target: TTarget, spec: TSpec, expectedMsg: var st echo getCurrentExceptionMsg() proc nimoutCheck(test: TTest; expectedNimout: string; given: var TSpec) = - let giv = given.nimout.strip - var currentPos = 0 - # Only check that nimout contains all expected lines in that order. - # There may be more output in nimout. It is ignored here. - for line in expectedNimout.strip.splitLines: - currentPos = giv.find(line.strip, currentPos) - if currentPos < 0: - given.err = reMsgsDiffer - break - - + if not greedyOrderedSubsetLines(expectedNimout, given.nimout): + given.err = reMsgsDiffer proc compilerOutputTests(test: TTest, target: TTarget, given: var TSpec, expected: TSpec; r: var TResults) = diff --git a/tests/casestmt/tcaseexpr1.nim b/tests/casestmt/tcaseexpr1.nim index 82af2410b..4f5bbf100 100644 --- a/tests/casestmt/tcaseexpr1.nim +++ b/tests/casestmt/tcaseexpr1.nim @@ -3,18 +3,18 @@ discard """ action: "reject" nimout: ''' tcaseexpr1.nim(33, 10) Error: not all cases are covered; missing: {C} +tcaseexpr1.nim(39, 12) Error: type mismatch: got but expected 'int literal(10)' ''' """ -#[ -# xxx make nimout comparison use nimoutCheck instead of: -elif expected.nimout.len > 0 and expected.nimout.normalizeMsg notin given.nimout.normalizeMsg: -and then use nimout: ''' -tcaseexpr1.nim(33, 10) Error: not all cases are covered2; missing: {C} -tcaseexpr1.nim(39, 12) Error: type mismatch: got but expected 'int literal(10)' -''' -]# + + + + + + + # line 20 diff --git a/tests/stdlib/ttestutils.nim b/tests/stdlib/ttestutils.nim new file mode 100644 index 000000000..1a50d311b --- /dev/null +++ b/tests/stdlib/ttestutils.nim @@ -0,0 +1,6 @@ +import stdtest/testutils + +block: # greedyOrderedSubsetLines + doAssert greedyOrderedSubsetLines("a1\na3", "a0\na1\na2\na3\na4") + doAssert not greedyOrderedSubsetLines("a3\na1", "a0\na1\na2\na3\na4") # out of order + doAssert not greedyOrderedSubsetLines("a1\na5", "a0\na1\na2\na3\na4") # a5 not in lhs From ae9fe2b2f996abbab1f7f7d037a955a7cffa9b6a Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 1 Dec 2020 22:34:52 +0100 Subject: [PATCH 1107/3768] ORC: API extensions (#16126) * ORC: API extensions * ORC: exploit a common special case --- lib/system/orc.nim | 59 +++++++++++++++++++++++++++++++++++----------- 1 file changed, 45 insertions(+), 14 deletions(-) diff --git a/lib/system/orc.nim b/lib/system/orc.nim index b0497a836..4bd8952b7 100644 --- a/lib/system/orc.nim +++ b/lib/system/orc.nim @@ -71,7 +71,7 @@ type when useJumpStack: jumpStack: CellSeq # Lins' jump stack in order to speed up traversals toFree: CellSeq - freed, touched: int + freed, touched, edges, rcSum: int proc trace(s: Cell; desc: PNimTypeV2; j: var GcEnv) {.inline.} = if desc.traceImpl != nil: @@ -174,11 +174,14 @@ proc markGray(s: Cell; desc: PNimTypeV2; j: var GcEnv) = if s.color != colGray: s.setColor colGray inc j.touched + # keep in mind that refcounts are zero based so add 1 here: + inc j.rcSum, (s.rc shr rcShift) + 1 orcAssert(j.traceStack.len == 0, "markGray: trace stack not empty") trace(s, desc, j) while j.traceStack.len > 0: let (t, desc) = j.traceStack.pop() dec t.rc, rcIncrement + inc j.edges when useJumpStack: if (t.rc shr rcShift) >= 0 and (t.rc and jumpStackFlag) == 0: t.rc = t.rc or jumpStackFlag @@ -188,6 +191,8 @@ proc markGray(s: Cell; desc: PNimTypeV2; j: var GcEnv) = if t.color != colGray: t.setColor colGray inc j.touched + # we already decremented its refcount so account for that: + inc j.rcSum, (t.rc shr rcShift) + 2 trace(t, desc, j) proc scan(s: Cell; desc: PNimTypeV2; j: var GcEnv) = @@ -254,8 +259,10 @@ when false: cfprintf(cstderr, "%s %p root index: %ld; RC: %ld; color: %ld\n", msg, s, s.rootIdx, s.rc shr rcShift, s.color) -proc collectWhite(s: Cell; desc: PNimTypeV2; j: var GcEnv) = +proc collectColor(s: Cell; desc: PNimTypeV2; col: int; j: var GcEnv) = #[ + was: 'collectWhite'. + proc collectWhite(s: Cell) = if s.color == colWhite and not buffered(s): s.setColor(colBlack) @@ -263,7 +270,7 @@ proc collectWhite(s: Cell; desc: PNimTypeV2; j: var GcEnv) = collectWhite(t) free(s) # watch out, a bug here! ]# - if s.color == colWhite and s.rootIdx == 0: + if s.color == col and s.rootIdx == 0: orcAssert(j.traceStack.len == 0, "collectWhite: trace stack not empty") s.setColor(colBlack) @@ -271,12 +278,12 @@ proc collectWhite(s: Cell; desc: PNimTypeV2; j: var GcEnv) = trace(s, desc, j) while j.traceStack.len > 0: let (t, desc) = j.traceStack.pop() - if t.color == colWhite and t.rootIdx == 0: + if t.color == col and t.rootIdx == 0: j.toFree.add(t, desc) t.setColor(colBlack) trace(t, desc, j) -proc collectCyclesBacon(j: var GcEnv) = +proc collectCyclesBacon(j: var GcEnv; lowMark: int) = # pretty direct translation from # https://researcher.watson.ibm.com/researcher/files/us-bacon/Bacon01Concurrent.pdf # Fig. 2. Synchronous Cycle Collection @@ -290,20 +297,27 @@ proc collectCyclesBacon(j: var GcEnv) = s.buffered = false collectWhite(s) ]# + let last = roots.len - 1 when logOrc: - for i in 0 ..< roots.len: + for i in countdown(last, lowMark): writeCell("root", roots.d[i][0], roots.d[i][1]) - for i in 0 ..< roots.len: + for i in countdown(last, lowMark): markGray(roots.d[i][0], roots.d[i][1], j) - for i in 0 ..< roots.len: - scan(roots.d[i][0], roots.d[i][1], j) + + var colToCollect = colWhite + if j.rcSum == j.edges: + # short-cut: we know everything is garbage: + colToCollect = colGray + else: + for i in countdown(last, lowMark): + scan(roots.d[i][0], roots.d[i][1], j) init j.toFree for i in 0 ..< roots.len: let s = roots.d[i][0] s.rootIdx = 0 - collectWhite(s, roots.d[i][1], j) + collectColor(s, roots.d[i][1], colToCollect, j) for i in 0 ..< j.toFree.len: free(j.toFree.d[i][0], j.toFree.d[i][1]) @@ -320,6 +334,19 @@ when defined(nimStressOrc): else: var rootsThreshold = defaultThreshold +proc partialCollect(lowMark: int) = + if roots.len < 10 + lowMark: return + when logOrc: + cfprintf(cstderr, "[partialCollect] begin\n") + var j: GcEnv + init j.traceStack + collectCyclesBacon(j, lowMark) + when logOrc: + cfprintf(cstderr, "[partialCollect] end; freed %ld touched: %ld work: %ld\n", j.freed, j.touched, + roots.len - lowMark) + roots.len = lowMark + deinit j.traceStack + proc collectCycles() = ## Collect cycles. when logOrc: @@ -329,14 +356,14 @@ proc collectCycles() = init j.traceStack when useJumpStack: init j.jumpStack - collectCyclesBacon(j) + collectCyclesBacon(j, 0) while j.jumpStack.len > 0: let (t, desc) = j.jumpStack.pop # not in jump stack anymore! t.rc = t.rc and not jumpStackFlag deinit j.jumpStack else: - collectCyclesBacon(j) + collectCyclesBacon(j, 0) deinit j.traceStack deinit roots @@ -355,8 +382,8 @@ proc collectCycles() = elif rootsThreshold < high(int) div 4: rootsThreshold = rootsThreshold * 3 div 2 when logOrc: - cfprintf(cstderr, "[collectCycles] end; freed %ld new threshold %ld touched: %ld mem: %ld\n", j.freed, rootsThreshold, j.touched, - getOccupiedMem()) + cfprintf(cstderr, "[collectCycles] end; freed %ld new threshold %ld touched: %ld mem: %ld rcSum: %ld edges: %ld\n", j.freed, rootsThreshold, j.touched, + getOccupiedMem(), j.rcSum, j.edges) proc registerCycle(s: Cell; desc: PNimTypeV2) = s.rootIdx = roots.len+1 @@ -384,6 +411,10 @@ proc GC_disableOrc*() = when not defined(nimStressOrc): rootsThreshold = high(int) +proc GC_prepareOrc*(): int {.inline.} = roots.len + +proc GC_partialCollect*(limit: int) = + partialCollect(limit) proc GC_fullCollect* = ## Forces a full garbage collection pass. With ``--gc:orc`` triggers the cycle From cabfc7ec5cee16f9fca401716e429d3631dbe85c Mon Sep 17 00:00:00 2001 From: treeform Date: Tue, 1 Dec 2020 14:29:15 -0800 Subject: [PATCH 1108/3768] Don't add content-length header on GET request when there is no body. (#16196) * Don't add content-length header on GET request when there is no body. * Add space between the not and the (. * Take Dom's suggestion. --- lib/pure/httpclient.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 0cf19f031..880c05765 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -980,7 +980,7 @@ proc requestAux(client: HttpClient | AsyncHttpClient, url, httpMethod: string, var data: seq[string] if multipart != nil and multipart.content.len > 0: data = await client.format(multipart) - else: + elif httpMethod in ["POST", "PATCH", "PUT"] or body.len != 0: client.headers["Content-Length"] = $body.len when client is AsyncHttpClient: From 9fa5826d62156c1f6fdc765403bea2d3a098c10e Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Wed, 2 Dec 2020 10:23:38 +0800 Subject: [PATCH 1109/3768] minor typo (#16225) --- lib/pure/parsecfg.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/parsecfg.nim b/lib/pure/parsecfg.nim index 319f8f625..9b140a809 100644 --- a/lib/pure/parsecfg.nim +++ b/lib/pure/parsecfg.nim @@ -447,7 +447,7 @@ proc newConfig*(): Config = result = newOrderedTable[string, OrderedTableRef[string, string]]() proc loadConfig*(stream: Stream, filename: string = "[stream]"): Config = - ## Loadw the specified configuration from stream into a new Config instance. + ## Loads the specified configuration from stream into a new Config instance. ## `filename` parameter is only used for nicer error messages. var dict = newOrderedTable[string, OrderedTableRef[string, string]]() var curSection = "" ## Current section, From 84f2ad3068cb77247ee6a263b2d375685b28b850 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 1 Dec 2020 21:09:08 -0800 Subject: [PATCH 1110/3768] add test for --eval; fix a minor bug (#16224) --- compiler/commands.nim | 1 + tests/misc/trunner.nim | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/compiler/commands.nim b/compiler/commands.nim index 916dfd66b..11bf628b4 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -450,6 +450,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; conf.projectIsCmd = true conf.cmdInput = arg # can be empty (a nim file with empty content is valid too) if conf.cmd == cmdNone: + conf.command = "e" conf.setCmd cmdNimscript # better than `cmdCrun` as a default conf.implicitCmd = true of "path", "p": diff --git a/tests/misc/trunner.nim b/tests/misc/trunner.nim index 018916e67..e288a56c7 100644 --- a/tests/misc/trunner.nim +++ b/tests/misc/trunner.nim @@ -216,3 +216,9 @@ mmain.html let file = testsDir / "misc/mimportc.nim" let cmd = fmt"{nim} r -b:cpp --hints:off --nimcache:{nimcache} --warningAsError:ProveInit {file}" check execCmdEx(cmd) == ("witness\n", 0) + + block: # nim --eval + let opt = "--hints:off" + check fmt"""{nim} {opt} --eval:"echo defined(nimscript)"""".execCmdEx == ("true\n", 0) + check fmt"""{nim} r {opt} --eval:"echo defined(c)"""".execCmdEx == ("true\n", 0) + check fmt"""{nim} r -b:js {opt} --eval:"echo defined(js)"""".execCmdEx == ("true\n", 0) From 5c23ba75456086e660d4baa1c43ec90ef8a2319d Mon Sep 17 00:00:00 2001 From: ihlec Date: Wed, 2 Dec 2020 09:15:29 +0100 Subject: [PATCH 1111/3768] fixed article duplication typos (#16216) --- compiler/nimlexbase.nim | 2 +- doc/manual.rst | 2 +- doc/regexprs.txt | 2 +- lib/packages/docutils/rstgen.nim | 2 +- lib/pure/collections/sequtils.nim | 2 +- lib/pure/math.nim | 2 +- lib/pure/streams.nim | 2 +- tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim | 2 +- tests/realtimeGC/readme.txt | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/compiler/nimlexbase.nim b/compiler/nimlexbase.nim index 6132e6f66..99d07d093 100644 --- a/compiler/nimlexbase.nim +++ b/compiler/nimlexbase.nim @@ -61,7 +61,7 @@ proc handleCR*(L: var TBaseLexer, pos: int): int # position to continue the scanning from. `pos` must be the position # of the CR. proc handleLF*(L: var TBaseLexer, pos: int): int - # Call this if you scanned over LF in the buffer; it returns the the + # Call this if you scanned over LF in the buffer; it returns the # position to continue the scanning from. `pos` must be the position # of the LF. # implementation diff --git a/doc/manual.rst b/doc/manual.rst index 1a23c5997..97dbbda13 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -1948,7 +1948,7 @@ Nim supports these `calling conventions`:idx:\: hardware stack. `inline`:idx: - The inline convention means the the caller should not call the procedure, + The inline convention means the caller should not call the procedure, but inline its code directly. Note that Nim does not inline, but leaves this to the C compiler; it generates ``__inline`` procedures. This is only a hint for the compiler: it may completely ignore it and diff --git a/doc/regexprs.txt b/doc/regexprs.txt index 83dbd2eeb..b7370d858 100644 --- a/doc/regexprs.txt +++ b/doc/regexprs.txt @@ -224,7 +224,7 @@ current matching point is at the end of the subject string, all of them fail, since there is no character to match. For compatibility with Perl, ``\s`` does not match the VT character (code 11). -This makes it different from the the POSIX "space" class. The ``\s`` characters +This makes it different from the POSIX "space" class. The ``\s`` characters are HT (9), LF (10), FF (12), CR (13), and space (32). A "word" character is an underscore or any character less than 256 that is diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 47b4189ba..d6bd26ad7 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -112,7 +112,7 @@ proc initRstGenerator*(g: var RstGenerator, target: OutputTarget, ## it helps to prettify the generated index if no title is found. ## ## The ``RstParseOptions``, ``FindFileHandler`` and ``MsgHandler`` types - ## are defined in the the `packages/docutils/rst module `_. + ## are defined in the `packages/docutils/rst module `_. ## ``options`` selects the behaviour of the rst parser. ## ## ``findFile`` is a proc used by the rst ``include`` directive among others. diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index ea2c02b8c..71558fe0d 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -1101,7 +1101,7 @@ macro mapLiterals*(constructor, op: untyped; iterator items*[T](xs: iterator: T): T = ## iterates over each element yielded by a closure iterator. This may ## not seem particularly useful on its own, but this allows closure - ## iterators to be used by the the mapIt, filterIt, allIt, anyIt, etc. + ## iterators to be used by the mapIt, filterIt, allIt, anyIt, etc. ## templates. for x in xs(): yield x diff --git a/lib/pure/math.nim b/lib/pure/math.nim index ccd766f46..8f5b9a6f5 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -622,7 +622,7 @@ when not defined(js): # C ## Note: Not available for JS backend. proc gamma*(x: float32): float32 {.importc: "tgammaf", header: "".} proc gamma*(x: float64): float64 {.importc: "tgamma", header: "".} - ## Computes the the `gamma function `_ for ``x``. + ## Computes the `gamma function `_ for ``x``. ## ## Note: Not available for JS backend. ## diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim index de3f41f09..62a074bd8 100644 --- a/lib/pure/streams.nim +++ b/lib/pure/streams.nim @@ -346,7 +346,7 @@ proc write*[T](s: Stream, x: T) = writeData(s, unsafeAddr(x), sizeof(x)) proc write*(s: Stream, x: string) = - ## Writes the string `x` to the the stream `s`. No length field or + ## Writes the string `x` to the stream `s`. No length field or ## terminating zero is written. runnableExamples: var strm = newStringStream("") diff --git a/tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim b/tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim index 552b3e756..6eb1b3844 100644 --- a/tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim +++ b/tests/manyloc/keineschweine/dependencies/chipmunk/chipmunk.nim @@ -106,7 +106,7 @@ type #/ Collision begin event function callback type. #/ Returning false from a begin callback causes the collision to be ignored until - #/ the the separate callback is called when the objects stop colliding. + #/ the separate callback is called when the objects stop colliding. TCollisionBeginFunc* = proc (arb: PArbiter; space: PSpace; data: pointer): bool{. cdecl.} #/ Collision pre-solve event function callback type. diff --git a/tests/realtimeGC/readme.txt b/tests/realtimeGC/readme.txt index b2788520e..c5837ee14 100644 --- a/tests/realtimeGC/readme.txt +++ b/tests/realtimeGC/readme.txt @@ -7,4 +7,4 @@ To build by hand and run the test for 35 minutes: xxx do we still need tests/realtimeGC/cmain.c? if so, tests/realtimeGC/cmain.c needs to updated and factorized with nmain.nim to avoid duplication (even if it's a C file) ``` - $ gcc -o tests/realtimeGC/cmain tests/realtimeGC/cmain.c -ldl + $ gcc -o tests/realtimeGC/cmain tests/realtimeGC/cmain.c -ldl \ No newline at end of file From e875f27ee6f57a24e0c9be36f3ab2d8780085a0c Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Wed, 2 Dec 2020 16:34:34 +0800 Subject: [PATCH 1112/3768] [docs minor] better comparisons docs (#16201) --- lib/system/comparisons.nim | 179 ++++++++++++++++++------------------- 1 file changed, 89 insertions(+), 90 deletions(-) diff --git a/lib/system/comparisons.nim b/lib/system/comparisons.nim index 60ac554a6..71096addd 100644 --- a/lib/system/comparisons.nim +++ b/lib/system/comparisons.nim @@ -1,24 +1,24 @@ # comparison operators: -proc `==`*[Enum: enum](x, y: Enum): bool {.magic: "EqEnum", noSideEffect.} +proc `==`*[Enum: enum](x, y: Enum): bool {.magic: "EqEnum", noSideEffect.} = ## Checks whether values within the *same enum* have the same underlying value. - ## - ## .. code-block:: Nim - ## type - ## Enum1 = enum - ## Field1 = 3, Field2 - ## Enum2 = enum - ## Place1, Place2 = 3 - ## var - ## e1 = Field1 - ## e2 = Enum1(Place2) - ## echo (e1 == e2) # true - ## echo (e1 == Place2) # raises error -proc `==`*(x, y: pointer): bool {.magic: "EqRef", noSideEffect.} - ## .. code-block:: Nim - ## var # this is a wildly dangerous example - ## a = cast[pointer](0) - ## b = cast[pointer](nil) - ## echo (a == b) # true due to the special meaning of `nil`/0 as a pointer + runnableExamples: + type + Enum1 = enum + Field1 = 3, Field2 + Enum2 = enum + Place1, Place2 = 3 + var + e1 = Field1 + e2 = Enum1(Place2) + assert e1 == e2 + assert not compiles(e1 == Place2) # raises error +proc `==`*(x, y: pointer): bool {.magic: "EqRef", noSideEffect.} = + ## Checks for equality between two `pointer` variables. + runnableExamples: + var # this is a wildly dangerous example + a = cast[pointer](0) + b = cast[pointer](nil) + assert a == b # true due to the special meaning of `nil`/0 as a pointer proc `==`*(x, y: string): bool {.magic: "EqStr", noSideEffect.} ## Checks for equality between two `string` variables. @@ -26,13 +26,11 @@ proc `==`*(x, y: char): bool {.magic: "EqCh", noSideEffect.} ## Checks for equality between two `char` variables. proc `==`*(x, y: bool): bool {.magic: "EqB", noSideEffect.} ## Checks for equality between two `bool` variables. -proc `==`*[T](x, y: set[T]): bool {.magic: "EqSet", noSideEffect.} +proc `==`*[T](x, y: set[T]): bool {.magic: "EqSet", noSideEffect.} = ## Checks for equality between two variables of type `set`. - ## - ## .. code-block:: Nim - ## var a = {1, 2, 2, 3} # duplication in sets is ignored - ## var b = {1, 2, 3} - ## echo (a == b) # true + runnableExamples: + assert {1, 2, 2, 3} == {1, 2, 3} # duplication in sets is ignored + proc `==`*[T](x, y: ref T): bool {.magic: "EqRef", noSideEffect.} ## Checks that two `ref` variables refer to the same item. proc `==`*[T](x, y: ptr T): bool {.magic: "EqRef", noSideEffect.} @@ -41,87 +39,87 @@ proc `==`*[T: proc](x, y: T): bool {.magic: "EqProc", noSideEffect.} ## Checks that two `proc` variables refer to the same procedure. proc `<=`*[Enum: enum](x, y: Enum): bool {.magic: "LeEnum", noSideEffect.} -proc `<=`*(x, y: string): bool {.magic: "LeStr", noSideEffect.} +proc `<=`*(x, y: string): bool {.magic: "LeStr", noSideEffect.} = ## Compares two strings and returns true if `x` is lexicographically ## before `y` (uppercase letters come before lowercase letters). - ## - ## .. code-block:: Nim - ## let - ## a = "abc" - ## b = "abd" - ## c = "ZZZ" - ## assert a <= b - ## assert a <= a - ## assert (a <= c) == false -proc `<=`*(x, y: char): bool {.magic: "LeCh", noSideEffect.} + runnableExamples: + let + a = "abc" + b = "abd" + c = "ZZZ" + assert a <= b + assert a <= a + assert not (a <= c) + +proc `<=`*(x, y: char): bool {.magic: "LeCh", noSideEffect.} = ## Compares two chars and returns true if `x` is lexicographically ## before `y` (uppercase letters come before lowercase letters). - ## - ## .. code-block:: Nim - ## let - ## a = 'a' - ## b = 'b' - ## c = 'Z' - ## assert a <= b - ## assert a <= a - ## assert (a <= c) == false -proc `<=`*[T](x, y: set[T]): bool {.magic: "LeSet", noSideEffect.} + runnableExamples: + let + a = 'a' + b = 'b' + c = 'Z' + assert a <= b + assert a <= a + assert not (a <= c) + +proc `<=`*[T](x, y: set[T]): bool {.magic: "LeSet", noSideEffect.} = ## Returns true if `x` is a subset of `y`. ## ## A subset `x` has all of its members in `y` and `y` doesn't necessarily ## have more members than `x`. That is, `x` can be equal to `y`. - ## - ## .. code-block:: Nim - ## let - ## a = {3, 5} - ## b = {1, 3, 5, 7} - ## c = {2} - ## assert a <= b - ## assert a <= a - ## assert (a <= c) == false + runnableExamples: + let + a = {3, 5} + b = {1, 3, 5, 7} + c = {2} + assert a <= b + assert a <= a + assert not (a <= c) + proc `<=`*(x, y: bool): bool {.magic: "LeB", noSideEffect.} proc `<=`*[T](x, y: ref T): bool {.magic: "LePtr", noSideEffect.} proc `<=`*(x, y: pointer): bool {.magic: "LePtr", noSideEffect.} proc `<`*[Enum: enum](x, y: Enum): bool {.magic: "LtEnum", noSideEffect.} -proc `<`*(x, y: string): bool {.magic: "LtStr", noSideEffect.} +proc `<`*(x, y: string): bool {.magic: "LtStr", noSideEffect.} = ## Compares two strings and returns true if `x` is lexicographically ## before `y` (uppercase letters come before lowercase letters). - ## - ## .. code-block:: Nim - ## let - ## a = "abc" - ## b = "abd" - ## c = "ZZZ" - ## assert a < b - ## assert (a < a) == false - ## assert (a < c) == false -proc `<`*(x, y: char): bool {.magic: "LtCh", noSideEffect.} + runnableExamples: + let + a = "abc" + b = "abd" + c = "ZZZ" + assert a < b + assert not (a < a) + assert not (a < c) + +proc `<`*(x, y: char): bool {.magic: "LtCh", noSideEffect.} = ## Compares two chars and returns true if `x` is lexicographically ## before `y` (uppercase letters come before lowercase letters). - ## - ## .. code-block:: Nim - ## let - ## a = 'a' - ## b = 'b' - ## c = 'Z' - ## assert a < b - ## assert (a < a) == false - ## assert (a < c) == false -proc `<`*[T](x, y: set[T]): bool {.magic: "LtSet", noSideEffect.} + runnableExamples: + let + a = 'a' + b = 'b' + c = 'Z' + assert a < b + assert not (a < a) + assert not (a < c) + +proc `<`*[T](x, y: set[T]): bool {.magic: "LtSet", noSideEffect.} = ## Returns true if `x` is a strict or proper subset of `y`. ## ## A strict or proper subset `x` has all of its members in `y` but `y` has ## more elements than `y`. - ## - ## .. code-block:: Nim - ## let - ## a = {3, 5} - ## b = {1, 3, 5, 7} - ## c = {2} - ## assert a < b - ## assert (a < a) == false - ## assert (a < c) == false + runnableExamples: + let + a = {3, 5} + b = {1, 3, 5, 7} + c = {2} + assert a < b + assert not (a < a) + assert not (a < c) + proc `<`*(x, y: bool): bool {.magic: "LtB", noSideEffect.} proc `<`*[T](x, y: ref T): bool {.magic: "LtPtr", noSideEffect.} proc `<`*[T](x, y: ptr T): bool {.magic: "LtPtr", noSideEffect.} @@ -252,11 +250,12 @@ proc max*[T](x: openArray[T]): T = proc clamp*[T](x, a, b: T): T = - ## Limits the value ``x`` within the interval [a, b]. - ## - ## .. code-block:: Nim - ## assert((1.4).clamp(0.0, 1.0) == 1.0) - ## assert((0.5).clamp(0.0, 1.0) == 0.5) + ## Limits the value `x` within the interval [a, b]. + ## This proc is equivalent to `max(a, min(b, x))`. + runnableExamples: + assert (1.4).clamp(0.0, 1.0) == 1.0 + assert (0.5).clamp(0.0, 1.0) == 0.5 + assert 4.clamp(1, 3) == max(1, min(3, 4)) if x < a: return a if x > b: return b return x From c0b76ef3cb08dc8d1547ed5d791427ba0c146802 Mon Sep 17 00:00:00 2001 From: Miran Date: Wed, 2 Dec 2020 15:36:08 +0100 Subject: [PATCH 1113/3768] fix #16164, render doc comments (#16230) * fix #16164, render doc comments * add a test --- compiler/options.nim | 2 +- nimdoc/testproject/expected/testproject.html | 11 ++++++++++- nimdoc/testproject/expected/testproject.idx | 1 + nimdoc/testproject/expected/theindex.html | 4 ++++ nimdoc/testproject/testproject.nim | 3 +++ 5 files changed, 19 insertions(+), 2 deletions(-) diff --git a/compiler/options.nim b/compiler/options.nim index 42a907777..bf0a9a4ee 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -541,7 +541,7 @@ proc isDefined*(conf: ConfigRef; symbol: string): bool = osDragonfly, osMacosx} else: discard -proc importantComments*(conf: ConfigRef): bool {.inline.} = conf.cmd notin cmdDocLike + {cmdIdeTools} +proc importantComments*(conf: ConfigRef): bool {.inline.} = conf.cmd in cmdDocLike + {cmdIdeTools} proc usesWriteBarrier*(conf: ConfigRef): bool {.inline.} = conf.selectedGC >= gcRefc template compilationCachePresent*(conf: ConfigRef): untyped = diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html index d9132d087..ee09b46af 100644 --- a/nimdoc/testproject/expected/testproject.html +++ b/nimdoc/testproject/expected/testproject.html @@ -120,7 +120,9 @@ function main() {
    • Vars @@ -427,6 +429,13 @@ Some shapes.

      Vars

      + +
      someVariable: bool
      +
      + +This should be visible. + +
      aVariable: array[1, int]
      diff --git a/nimdoc/testproject/expected/testproject.idx b/nimdoc/testproject/expected/testproject.idx index 8d132acc4..5714efac0 100644 --- a/nimdoc/testproject/expected/testproject.idx +++ b/nimdoc/testproject/expected/testproject.idx @@ -1,3 +1,4 @@ +someVariable testproject.html#someVariable testproject: someVariable C_A testproject.html#C_A testproject: C_A C_B testproject.html#C_B testproject: C_B C_C testproject.html#C_C testproject: C_C diff --git a/nimdoc/testproject/expected/theindex.html b/nimdoc/testproject/expected/theindex.html index 34d24e900..03d9c72fe 100644 --- a/nimdoc/testproject/expected/theindex.html +++ b/nimdoc/testproject/expected/theindex.html @@ -244,6 +244,10 @@ function main() {
    • utils: someType(): SomeType
    +
    someVariable:
    testNimDocTrailingExample:
    • testproject: testNimDocTrailingExample()
    • diff --git a/nimdoc/testproject/testproject.nim b/nimdoc/testproject/testproject.nim index 3cb7fddc5..eea399f82 100644 --- a/nimdoc/testproject/testproject.nim +++ b/nimdoc/testproject/testproject.nim @@ -9,6 +9,9 @@ runnableExamples: # bug #11078 for x in "xx": discard + +var someVariable*: bool ## This should be visible. + when true: ## top2 runnableExamples: From 148d614dffbc93f1d8e6e6de3e8e64f64115ccdd Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Wed, 2 Dec 2020 23:24:59 +0800 Subject: [PATCH 1114/3768] js module also uses runnableExamples (#16229) --- lib/js/jsffi.nim | 51 ++++++++++++++++++++++-------------------------- lib/js/jsre.nim | 21 +++++++++----------- 2 files changed, 32 insertions(+), 40 deletions(-) diff --git a/lib/js/jsffi.nim b/lib/js/jsffi.nim index eedf45bb0..5140ff372 100644 --- a/lib/js/jsffi.nim +++ b/lib/js/jsffi.nim @@ -12,26 +12,25 @@ ## and ``JsAssoc`` together with the provided macros allows for smoother ## interfacing with JavaScript, allowing for example quick and easy imports of ## JavaScript variables: -## -## .. code-block:: nim -## -## # Here, we are using jQuery for just a few calls and do not want to wrap the -## # whole library: -## -## # import the document object and the console -## var document {.importc, nodecl.}: JsObject -## var console {.importc, nodecl.}: JsObject -## # import the "$" function -## proc jq(selector: JsObject): JsObject {.importcpp: "$$(#)".} -## -## # Use jQuery to make the following code run, after the document is ready. -## # This uses an experimental ``.()`` operator for ``JsObject``, to emit -## # JavaScript calls, when no corresponding proc exists for ``JsObject``. -## proc main = -## jq(document).ready(proc() = -## console.log("Hello JavaScript!") -## ) -## + +runnableExamples: + # Here, we are using jQuery for just a few calls and do not want to wrap the + # whole library: + + # import the document object and the console + var document {.importc, nodecl.}: JsObject + var console {.importc, nodecl.}: JsObject + # import the "$" function + proc jq(selector: JsObject): JsObject {.importcpp: "$$(#)".} + + # Use jQuery to make the following code run, after the document is ready. + # This uses an experimental ``.()`` operator for ``JsObject``, to emit + # JavaScript calls, when no corresponding proc exists for ``JsObject``. + proc main = + jq(document).ready(proc() = + console.log("Hello JavaScript!") + ) + when not defined(js) and not defined(nimdoc) and not defined(nimsuggest): {.fatal: "Module jsFFI is designed to be used with the JavaScript backend.".} @@ -221,14 +220,10 @@ proc `==`*(x, y: JsRoot): bool {.importcpp: "(# === #)".} macro `.`*(obj: JsObject, field: untyped): JsObject = ## Experimental dot accessor (get) for type JsObject. ## Returns the value of a property of name `field` from a JsObject `x`. - ## - ## Example: - ## - ## .. code-block:: nim - ## - ## let obj = newJsObject() - ## obj.a = 20 - ## console.log(obj.a) # puts 20 onto the console. + runnableExamples: + let obj = newJsObject() + obj.a = 20 + assert obj.a.to(int) == 20 if validJsName($field): let importString = "#." & $field result = quote do: diff --git a/lib/js/jsre.nim b/lib/js/jsre.nim index ff80056ea..f5c3cc1ac 100644 --- a/lib/js/jsre.nim +++ b/lib/js/jsre.nim @@ -1,17 +1,14 @@ ## Regular Expressions for the JavaScript target. ## * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions -## -## Examples -## ======== -## -## .. code-block::nim -## let jsregex: RegExp = newRegExp(r"\s+", r"i") -## jsregex.compile(r"\w+", r"i") -## doAssert jsregex.test(r"nim javascript") -## doAssert jsregex.exec(r"nim javascript") == @["nim".cstring] -## doAssert jsregex.toString() == r"/\w+/i" -## jsregex.compile(r"[0-9]", r"i") -## doAssert jsregex.test(r"0123456789abcd") + +runnableExamples: + let jsregex: RegExp = newRegExp(r"\s+", r"i") + jsregex.compile(r"\w+", r"i") + doAssert jsregex.test(r"nim javascript") + doAssert jsregex.exec(r"nim javascript") == @["nim".cstring] + doAssert jsregex.toString() == r"/\w+/i" + jsregex.compile(r"[0-9]", r"i") + doAssert jsregex.test(r"0123456789abcd") when not defined(js) and not defined(Nimdoc): From d2bf0fb43c40253e3959715fb1fdf866c04559c2 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Thu, 3 Dec 2020 00:06:23 +0800 Subject: [PATCH 1115/3768] small style changes and clarify clamp (#16228) * [docs minor]better comparisons docs * switch from PRNG to random module * apply suggestions * not compile * Revert "switch from PRNG to random module" This reverts commit 83b4d8946d7d677edac43b7675c41e230ba4e382. * small style changes and clarify clamp --- lib/system/comparisons.nim | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/system/comparisons.nim b/lib/system/comparisons.nim index 71096addd..d9c7f6782 100644 --- a/lib/system/comparisons.nim +++ b/lib/system/comparisons.nim @@ -4,14 +4,14 @@ proc `==`*[Enum: enum](x, y: Enum): bool {.magic: "EqEnum", noSideEffect.} = runnableExamples: type Enum1 = enum - Field1 = 3, Field2 + field1 = 3, field2 Enum2 = enum - Place1, Place2 = 3 + place1, place2 = 3 var - e1 = Field1 - e2 = Enum1(Place2) + e1 = field1 + e2 = Enum1(place2) assert e1 == e2 - assert not compiles(e1 == Place2) # raises error + assert not compiles(e1 == place2) # raises error proc `==`*(x, y: pointer): bool {.magic: "EqRef", noSideEffect.} = ## Checks for equality between two `pointer` variables. runnableExamples: @@ -251,7 +251,9 @@ proc max*[T](x: openArray[T]): T = proc clamp*[T](x, a, b: T): T = ## Limits the value `x` within the interval [a, b]. - ## This proc is equivalent to `max(a, min(b, x))`. + ## This proc is equivalent to but fatser than `max(a, min(b, x))`. + ## + ## **Note that** `a < b` is assuemed and no check for it. runnableExamples: assert (1.4).clamp(0.0, 1.0) == 1.0 assert (0.5).clamp(0.0, 1.0) == 0.5 From af984a3db969d28961a9e765190c091c9aa30880 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Wed, 2 Dec 2020 17:18:51 +0100 Subject: [PATCH 1116/3768] Fix typo and improve grammar for clamps note --- lib/system/comparisons.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/system/comparisons.nim b/lib/system/comparisons.nim index d9c7f6782..a20e21064 100644 --- a/lib/system/comparisons.nim +++ b/lib/system/comparisons.nim @@ -253,7 +253,7 @@ proc clamp*[T](x, a, b: T): T = ## Limits the value `x` within the interval [a, b]. ## This proc is equivalent to but fatser than `max(a, min(b, x))`. ## - ## **Note that** `a < b` is assuemed and no check for it. + ## **Note:** `a < b` is assumed and will not be checked. runnableExamples: assert (1.4).clamp(0.0, 1.0) == 1.0 assert (0.5).clamp(0.0, 1.0) == 0.5 From 139075e96565f4b7436e8d2368f85f11852aa668 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Thu, 3 Dec 2020 02:30:55 +0800 Subject: [PATCH 1117/3768] move decode_helpers to std/private (#16209) --- lib/pure/cgi.nim | 2 +- lib/pure/uri.nim | 2 +- lib/{pure/includes => std/private}/decode_helpers.nim | 8 +++----- 3 files changed, 5 insertions(+), 7 deletions(-) rename lib/{pure/includes => std/private}/decode_helpers.nim (69%) diff --git a/lib/pure/cgi.nim b/lib/pure/cgi.nim index 1f0e0736a..cb64a3b1b 100644 --- a/lib/pure/cgi.nim +++ b/lib/pure/cgi.nim @@ -32,7 +32,7 @@ import strutils, os, strtabs, cookies, uri export uri.encodeUrl, uri.decodeUrl -include includes/decode_helpers +import std/private/decode_helpers proc addXmlChar(dest: var string, c: char) {.inline.} = case c diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim index 15d0996a7..5b16bfcb1 100644 --- a/lib/pure/uri.nim +++ b/lib/pure/uri.nim @@ -47,7 +47,7 @@ import std/private/since import strutils, parseutils, base64 -include includes/decode_helpers +import std/private/decode_helpers type diff --git a/lib/pure/includes/decode_helpers.nim b/lib/std/private/decode_helpers.nim similarity index 69% rename from lib/pure/includes/decode_helpers.nim rename to lib/std/private/decode_helpers.nim index 74fe37d07..586c7cae6 100644 --- a/lib/pure/includes/decode_helpers.nim +++ b/lib/std/private/decode_helpers.nim @@ -1,14 +1,12 @@ -# Include file that implements 'decodePercent' and friends. Do not import it! - -proc handleHexChar(c: char, x: var int, f: var bool) {.inline.} = +proc handleHexChar*(c: char, x: var int, f: var bool) {.inline.} = case c of '0'..'9': x = (x shl 4) or (ord(c) - ord('0')) of 'a'..'f': x = (x shl 4) or (ord(c) - ord('a') + 10) of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10) else: f = true -proc decodePercent(s: string, i: var int): char = - ## Converts `%xx` hexadecimal to the charracter with ordinal number `xx`. +proc decodePercent*(s: openArray[char], i: var int): char = + ## Converts `%xx` hexadecimal to the character with ordinal number `xx`. ## ## If `xx` is not a valid hexadecimal value, it is left intact: only the ## leading `%` is returned as-is, and `xx` characters will be processed in the From 629b22e3d50e9903512ec893174eb797d9ed431b Mon Sep 17 00:00:00 2001 From: ee7 <45465154+ee7@users.noreply.github.com> Date: Wed, 2 Dec 2020 20:48:16 +0100 Subject: [PATCH 1118/3768] styleCheck: Fix error for `sugar` and `std/with` (#16176) With this commit, we no longer see an error if we pass `--styleCheck:error` when compiling a file that contains `import sugar` or `import std/with`. The problem was that those modules (and only those modules) import `std/private/underscored_calls`, which contained a styleCheck issue: its spelling of `nnkArgList` didn't match the `nnkArglist` spelling in `macros.nim`. This commit fixes the issue by renaming `nnkArgList` to `nnkArglist` repo-wide. The other way around would be a breaking change for code that used `nnkArglist` and `--styleCheck:error`. Fixes: #16174 --- doc/manual_experimental.rst | 2 +- lib/std/private/underscored_calls.nim | 2 +- tests/astspec/tastspec.nim | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/manual_experimental.rst b/doc/manual_experimental.rst index caf3dfb48..dadcf62d4 100644 --- a/doc/manual_experimental.rst +++ b/doc/manual_experimental.rst @@ -1250,7 +1250,7 @@ all the arguments, but also the matched operators in reverse polish notation: echo x + y * z - x This passes the expression ``x + y * z - x`` to the ``optM`` macro as -an ``nnkArgList`` node containing:: +an ``nnkArglist`` node containing:: Arglist Sym "x" diff --git a/lib/std/private/underscored_calls.nim b/lib/std/private/underscored_calls.nim index f0bcbcc74..6d0a99ab5 100644 --- a/lib/std/private/underscored_calls.nim +++ b/lib/std/private/underscored_calls.nim @@ -39,7 +39,7 @@ proc underscoredCall(n, arg0: NimNode): NimNode = result.add arg0 proc underscoredCalls*(result, calls, arg0: NimNode) = - expectKind calls, {nnkArgList, nnkStmtList, nnkStmtListExpr} + expectKind calls, {nnkArglist, nnkStmtList, nnkStmtListExpr} for call in calls: if call.kind in {nnkStmtList, nnkStmtListExpr}: diff --git a/tests/astspec/tastspec.nim b/tests/astspec/tastspec.nim index e2cfed277..3413d32f3 100644 --- a/tests/astspec/tastspec.nim +++ b/tests/astspec/tastspec.nim @@ -929,7 +929,7 @@ static: (x & y & z) is string ast.peelOff({nnkStmtList, nnkTypeSection}).matchAst: - of nnkTypeDef(_, _, nnkTypeClassTy(nnkArgList, _, _, nnkStmtList)): + of nnkTypeDef(_, _, nnkTypeClassTy(nnkArglist, _, _, nnkStmtList)): # note this isn't nnkConceptTy! echo "ok" From 849bc36edac47d5f62222ffc6beb015a770fc1f6 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Thu, 3 Dec 2020 19:15:36 +0800 Subject: [PATCH 1119/3768] merge two parsecfg tests into one (#16237) * try to fix #16206 * merge two parsecfg tests into one * Revert "fix" This reverts commit 668bdec2c499cf9967abfb7aad24975a04b092eb. * Revert "try to fix #16206" This reverts commit c399cc2153190299c9cbb0ad83f6ce85a7bbbb89. --- tests/stdlib/tparscfg.nim | 56 ----------------------- tests/stdlib/tparsecfg.nim | 91 +++++++++++++++++++++++++++++++------- 2 files changed, 74 insertions(+), 73 deletions(-) delete mode 100644 tests/stdlib/tparscfg.nim diff --git a/tests/stdlib/tparscfg.nim b/tests/stdlib/tparscfg.nim deleted file mode 100644 index ddb9b02b7..000000000 --- a/tests/stdlib/tparscfg.nim +++ /dev/null @@ -1,56 +0,0 @@ -discard """ - targets: "c js" -""" -import parsecfg, streams - -## Creating a configuration file. -var dict1=newConfig() -dict1.setSectionKey("","charset","utf-8") -dict1.setSectionKey("Package","name","hello") -dict1.setSectionKey("Package","--threads","on") -dict1.setSectionKey("Author","name","lihf8515") -dict1.setSectionKey("Author","qq","10214028") -dict1.setSectionKey("Author","email","lihaifeng@wxm.com") -var ss = newStringStream() -dict1.writeConfig(ss) - -## Reading a configuration file. -var dict2 = loadConfig(newStringStream(ss.data)) -var charset = dict2.getSectionValue("","charset") -var threads = dict2.getSectionValue("Package","--threads") -var pname = dict2.getSectionValue("Package","name") -var name = dict2.getSectionValue("Author","name") -var qq = dict2.getSectionValue("Author","qq") -var email = dict2.getSectionValue("Author","email") -doAssert charset == "utf-8" -doAssert threads == "on" -doAssert pname == "hello" -doAssert name == "lihf8515" -doAssert qq == "10214028" -doAssert email == "lihaifeng@wxm.com" - -## Modifying a configuration file. -var dict3 = loadConfig(newStringStream(ss.data)) -dict3.setSectionKey("Author","name","lhf") -doAssert $dict3 == """charset=utf-8 -[Package] -name=hello ---threads:on -[Author] -name=lhf -qq=10214028 -email="lihaifeng@wxm.com" -""" - -## Deleting a section key in a configuration file. -var dict4 = loadConfig(newStringStream(ss.data)) -dict4.delSectionKey("Author","email") -doAssert $dict4 == """charset=utf-8 -[Package] -name=hello ---threads:on -[Author] -name=lihf8515 -qq=10214028 -""" - diff --git a/tests/stdlib/tparsecfg.nim b/tests/stdlib/tparsecfg.nim index 1c214e5d2..45dea0531 100644 --- a/tests/stdlib/tparsecfg.nim +++ b/tests/stdlib/tparsecfg.nim @@ -1,23 +1,80 @@ discard """ - output: '''OK''' + targets: "c js" """ -#bug #6046 -import parsecfg +import parsecfg, streams -var config = newConfig() -config.setSectionKey("foo","bar","-1") -config.setSectionKey("foo","foo","abc") -config.writeConfig("test.ini") +when not defined(js): + # bug #6046 + var config = newConfig() + config.setSectionKey("foo", "bar", "-1") + config.setSectionKey("foo", "foo", "abc") -# test.ini now contains -# [foo] -# bar=-1 -# foo=abc + from stdtest/specialpaths import buildDir + import os -var config2 = loadConfig("test.ini") -let bar = config2.getSectionValue("foo","bar") -let foo = config2.getSectionValue("foo","foo") -assert(bar == "-1") -assert(foo == "abc") -echo "OK" + const file = buildDir / "tparsecfg.ini" + config.writeConfig(file) + + # file now contains + # [foo] + # bar=-1 + # foo=abc + + var config2 = loadConfig(file) + let bar = config2.getSectionValue("foo", "bar") + let foo = config2.getSectionValue("foo", "foo") + assert(bar == "-1") + assert(foo == "abc") + + +## Creating a configuration file. +var dict1 = newConfig() +dict1.setSectionKey("", "charset", "utf-8") +dict1.setSectionKey("Package", "name", "hello") +dict1.setSectionKey("Package", "--threads", "on") +dict1.setSectionKey("Author", "name", "lihf8515") +dict1.setSectionKey("Author", "qq", "10214028") +dict1.setSectionKey("Author", "email", "lihaifeng@wxm.com") +var ss = newStringStream() +dict1.writeConfig(ss) + +## Reading a configuration file. +var dict2 = loadConfig(newStringStream(ss.data)) +var charset = dict2.getSectionValue("", "charset") +var threads = dict2.getSectionValue("Package", "--threads") +var pname = dict2.getSectionValue("Package", "name") +var name = dict2.getSectionValue("Author", "name") +var qq = dict2.getSectionValue("Author", "qq") +var email = dict2.getSectionValue("Author", "email") +doAssert charset == "utf-8" +doAssert threads == "on" +doAssert pname == "hello" +doAssert name == "lihf8515" +doAssert qq == "10214028" +doAssert email == "lihaifeng@wxm.com" + +## Modifying a configuration file. +var dict3 = loadConfig(newStringStream(ss.data)) +dict3.setSectionKey("Author", "name", "lhf") +doAssert $dict3 == """charset=utf-8 +[Package] +name=hello +--threads:on +[Author] +name=lhf +qq=10214028 +email="lihaifeng@wxm.com" +""" + +## Deleting a section key in a configuration file. +var dict4 = loadConfig(newStringStream(ss.data)) +dict4.delSectionKey("Author", "email") +doAssert $dict4 == """charset=utf-8 +[Package] +name=hello +--threads:on +[Author] +name=lihf8515 +qq=10214028 +""" From 23447ffdce7415e2313654a61bdd9a23803f6538 Mon Sep 17 00:00:00 2001 From: Aditya Siram Date: Thu, 3 Dec 2020 06:40:28 -0600 Subject: [PATCH 1120/3768] Fixes #16219, `hasArgOfName` ignoring argument sets. (#16233) * Fixes #16219, `hasArgOfName` ignoring argument sets. * Fix test and simplify ident traversal. * Moved test into a block and removed some boilerplate. * Fix some argument formatting. * use ..< * Change the preceding line too Co-authored-by: Clyybber --- lib/core/macros.nim | 8 ++++---- tests/stdlib/tmacros.nim | 9 +++++++++ 2 files changed, 13 insertions(+), 4 deletions(-) create mode 100644 tests/stdlib/tmacros.nim diff --git a/lib/core/macros.nim b/lib/core/macros.nim index d2985bc72..97c3a46c5 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -1434,10 +1434,10 @@ proc expectIdent*(n: NimNode, name: string) {.compileTime, since: (1,1).} = proc hasArgOfName*(params: NimNode; name: string): bool {.compileTime.}= ## Search ``nnkFormalParams`` for an argument. expectKind(params, nnkFormalParams) - for i in 1 ..< params.len: - template node: untyped = params[i] - if name.eqIdent( $ node[0]): - return true + for i in 1.. Date: Thu, 3 Dec 2020 21:47:44 +0800 Subject: [PATCH 1121/3768] better setops docs (#16236) * js module also uses runnableExamples * better setops docs * Update lib/system/setops.nim * better * Update lib/system/setops.nim * Update lib/system/setops.nim * better * better --- lib/system/setops.nim | 136 ++++++++++++++++++++---------------------- 1 file changed, 64 insertions(+), 72 deletions(-) diff --git a/lib/system/setops.nim b/lib/system/setops.nim index 97f042d93..755eafdb8 100644 --- a/lib/system/setops.nim +++ b/lib/system/setops.nim @@ -1,94 +1,86 @@ -proc incl*[T](x: var set[T], y: T) {.magic: "Incl", noSideEffect.} - ## Includes element ``y`` in the set ``x``. +func incl*[T](x: var set[T], y: T) {.magic: "Incl".} = + ## Includes element `y` in the set `x`. ## - ## This is the same as ``x = x + {y}``, but it might be more efficient. - ## - ## .. code-block:: Nim - ## var a = {1, 3, 5} - ## a.incl(2) # a <- {1, 2, 3, 5} - ## a.incl(4) # a <- {1, 2, 3, 4, 5} + ## This is the same as `x = x + {y}`, but it might be more efficient. + runnableExamples: + var a = {1, 3, 5} + a.incl(2) + assert a == {1, 2, 3, 5} + a.incl(4) + assert a == {1, 2, 3, 4, 5} template incl*[T](x: var set[T], y: set[T]) = - ## Includes the set ``y`` in the set ``x``. - ## - ## .. code-block:: Nim - ## var a = {1, 3, 5, 7} - ## var b = {4, 5, 6} - ## a.incl(b) # a <- {1, 3, 4, 5, 6, 7} + ## Includes the set `y` in the set `x`. + runnableExamples: + var a = {1, 3, 5, 7} + var b = {4, 5, 6} + a.incl(b) + assert a == {1, 3, 4, 5, 6, 7} x = x + y -proc excl*[T](x: var set[T], y: T) {.magic: "Excl", noSideEffect.} - ## Excludes element ``y`` from the set ``x``. +func excl*[T](x: var set[T], y: T) {.magic: "Excl".} = + ## Excludes element `y` from the set `x`. ## - ## This is the same as ``x = x - {y}``, but it might be more efficient. - ## - ## .. code-block:: Nim - ## var b = {2, 3, 5, 6, 12, 545} - ## b.excl(5) # b <- {2, 3, 6, 12, 545} + ## This is the same as `x = x - {y}`, but it might be more efficient. + runnableExamples: + var b = {2, 3, 5, 6, 12, 545} + b.excl(5) + assert b == {2, 3, 6, 12, 545} template excl*[T](x: var set[T], y: set[T]) = - ## Excludes the set ``y`` from the set ``x``. - ## - ## .. code-block:: Nim - ## var a = {1, 3, 5, 7} - ## var b = {3, 4, 5} - ## a.excl(b) # a <- {1, 7} + ## Excludes the set `y` from the set `x`. + runnableExamples: + var a = {1, 3, 5, 7} + var b = {3, 4, 5} + a.excl(b) + assert a == {1, 7} x = x - y -proc card*[T](x: set[T]): int {.magic: "Card", noSideEffect.} - ## Returns the cardinality of the set ``x``, i.e. the number of elements +func card*[T](x: set[T]): int {.magic: "Card".} = + ## Returns the cardinality of the set `x`, i.e. the number of elements ## in the set. - ## - ## .. code-block:: Nim - ## var a = {1, 3, 5, 7} - ## echo card(a) # => 4 + runnableExamples: + var a = {1, 3, 5, 7} + assert card(a) == 4 + var b = {1, 3, 5, 7, 5} + assert card(b) == 4 # repeated 5 doesn't count -proc len*[T](x: set[T]): int {.magic: "Card", noSideEffect.} +func len*[T](x: set[T]): int {.magic: "Card".} ## An alias for `card(x)`. -proc `*`*[T](x, y: set[T]): set[T] {.magic: "MulSet", noSideEffect.} +func `*`*[T](x, y: set[T]): set[T] {.magic: "MulSet".} = ## This operator computes the intersection of two sets. - ## - ## .. code-block:: Nim - ## let - ## a = {1, 2, 3} - ## b = {2, 3, 4} - ## echo a * b # => {2, 3} -proc `+`*[T](x, y: set[T]): set[T] {.magic: "PlusSet", noSideEffect.} - ## This operator computes the union of two sets. - ## - ## .. code-block:: Nim - ## let - ## a = {1, 2, 3} - ## b = {2, 3, 4} - ## echo a + b # => {1, 2, 3, 4} -proc `-`*[T](x, y: set[T]): set[T] {.magic: "MinusSet", noSideEffect.} - ## This operator computes the difference of two sets. - ## - ## .. code-block:: Nim - ## let - ## a = {1, 2, 3} - ## b = {2, 3, 4} - ## echo a - b # => {1} + runnableExamples: + assert {1, 2, 3} * {2, 3, 4} == {2, 3} -proc contains*[T](x: set[T], y: T): bool {.magic: "InSet", noSideEffect.} - ## One should overload this proc if one wants to overload the ``in`` operator. +func `+`*[T](x, y: set[T]): set[T] {.magic: "PlusSet".} = + ## This operator computes the union of two sets. + runnableExamples: + assert {1, 2, 3} + {2, 3, 4} == {1, 2, 3, 4} + +func `-`*[T](x, y: set[T]): set[T] {.magic: "MinusSet".} = + ## This operator computes the difference of two sets. + runnableExamples: + assert {1, 2, 3} - {2, 3, 4} == {1} + +func contains*[T](x: set[T], y: T): bool {.magic: "InSet".} = + ## One should overload this proc if one wants to overload the `in` operator. ## - ## The parameters are in reverse order! ``a in b`` is a template for - ## ``contains(b, a)``. + ## The parameters are in reverse order! `a in b` is a template for + ## `contains(b, a)`. ## This is because the unification algorithm that Nim uses for overload ## resolution works from left to right. - ## But for the ``in`` operator that would be the wrong direction for this + ## But for the `in` operator that would be the wrong direction for this ## piece of code: - ## - ## .. code-block:: Nim - ## var s: set[range['a'..'z']] = {'a'..'c'} - ## assert s.contains('c') - ## assert 'b' in s - ## - ## If ``in`` had been declared as ``[T](elem: T, s: set[T])`` then ``T`` would - ## have been bound to ``char``. But ``s`` is not compatible to type - ## ``set[char]``! The solution is to bind ``T`` to ``range['a'..'z']``. This - ## is achieved by reversing the parameters for ``contains``; ``in`` then + runnableExamples: + var s: set[range['a'..'z']] = {'a'..'c'} + assert s.contains('c') + assert 'b' in s + assert 'd' notin s + assert set['a'..'z'] is set[range['a'..'z']] + ## If `in` had been declared as `[T](elem: T, s: set[T])` then `T` would + ## have been bound to `char`. But `s` is not compatible to type + ## `set[char]`! The solution is to bind `T` to `range['a'..'z']`. This + ## is achieved by reversing the parameters for `contains`; `in` then ## passes its arguments in reverse order. From c731f7ab149a539782b0b000853ecef058bc3dde Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 3 Dec 2020 07:55:43 -0800 Subject: [PATCH 1122/3768] fixes #15939, fixes #15464, fixes #16169, fixes #16226 VM now supports `addr(mystring[ind])` (index + index assignment) (#15987) * fix #15939, fix #15464 VM now supports `addr(mystring[ind])` (index + index assignment), var char return etc * cleanups * cstring tests * add test for bug #15464 * improve test coverage --- changelog.md | 1 + compiler/vm.nim | 21 +++++++++++++- compiler/vmdef.nim | 1 + compiler/vmgen.nim | 10 ++++--- tests/misc/taddr.nim | 66 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 94 insertions(+), 5 deletions(-) diff --git a/changelog.md b/changelog.md index d62e96bbe..5b22a174b 100644 --- a/changelog.md +++ b/changelog.md @@ -69,6 +69,7 @@ - Added `nim --eval:cmd` to evaluate a command directly, see `nim --help`. +- VM now supports `addr(mystring[ind])` (index + index assignment) ## Tool changes diff --git a/compiler/vm.nim b/compiler/vm.nim index 17bbe65fc..34d76f21e 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -20,6 +20,7 @@ import from semfold import leValueConv, ordinalValToString from evaltempl import evalTemplate +from magicsys import getSysType const traceCode = defined(nimVMDebug) @@ -123,7 +124,7 @@ proc derefPtrToReg(address: BiggestInt, typ: PType, r: var TFullReg, isAssign: b else: r.ensureKind(rkind) let val = cast[ptr T](address)[] - when T is SomeInteger: + when T is SomeInteger | char: r.field = BiggestInt(val) else: r.field = val @@ -131,6 +132,7 @@ proc derefPtrToReg(address: BiggestInt, typ: PType, r: var TFullReg, isAssign: b ## see also typeinfo.getBiggestInt case typ.kind + of tyChar: fun(intVal, char, rkInt) of tyInt: fun(intVal, int, rkInt) of tyInt8: fun(intVal, int8, rkInt) of tyInt16: fun(intVal, int16, rkInt) @@ -677,6 +679,23 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = regs[ra].intVal = s[idx].ord else: stackTrace(c, tos, pc, formatErrorIndexBound(idx, s.len-1)) + of opcLdStrIdxAddr: + # a = addr(b[c]); similar to opcLdArrAddr + decodeBC(rkNode) + if regs[rc].intVal > high(int): + stackTrace(c, tos, pc, formatErrorIndexBound(regs[rc].intVal, high(int))) + let idx = regs[rc].intVal.int + let s = regs[rb].node.strVal.addr # or `byaddr` + if idx <% s[].len: + # `makePtrType` not accessible from vm.nim + let typ = newType(tyPtr, nextId c.idgen, c.module.owner) + typ.add getSysType(c.graph, c.debug[pc], tyChar) + let node = newNodeIT(nkIntLit, c.debug[pc], typ) # xxx nkPtrLit + node.intVal = cast[int](s[][idx].addr) + node.flags.incl nfIsPtr + regs[ra].node = node + else: + stackTrace(c, tos, pc, formatErrorIndexBound(idx, s[].len-1)) of opcWrArr: # a[b] = c decodeBC(rkNode) diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index 202087af7..6d6c55250 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -78,6 +78,7 @@ type opcWrDeref, opcWrStrIdx, opcLdStrIdx, # a = b[c] + opcLdStrIdxAddr, # a = addr(b[c]) opcAddInt, opcAddImmInt, diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 84ff1a91d..29ab8739d 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1654,8 +1654,8 @@ proc genArrAccessOpcode(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; let a = c.genx(n[0], flags) let b = c.genIndex(n[1], n[0].typ) if dest < 0: dest = c.getTemp(n.typ) - if opc == opcLdArr and {gfNodeAddr} * flags != {}: - c.gABC(n, opcLdArrAddr, dest, a, b) + if opc in {opcLdArrAddr, opcLdStrIdxAddr} and gfNodeAddr in flags: + c.gABC(n, opc, dest, a, b) elif needsRegLoad(): var cc = c.getTemp(n.typ) c.gABC(n, opc, cc, a, b) @@ -1749,11 +1749,13 @@ proc genCheckedObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = proc genArrAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = let arrayType = n[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind if arrayType in {tyString, tyCString}: - genArrAccessOpcode(c, n, dest, opcLdStrIdx, {}) + let opc = if gfNodeAddr in flags: opcLdStrIdxAddr else: opcLdStrIdx + genArrAccessOpcode(c, n, dest, opc, flags) elif arrayType == tyTypeDesc: c.genTypeLit(n.typ, dest) else: - genArrAccessOpcode(c, n, dest, opcLdArr, flags) + let opc = if gfNodeAddr in flags: opcLdArrAddr else: opcLdArr + genArrAccessOpcode(c, n, dest, opc, flags) proc getNullValueAux(t: PType; obj: PNode, result: PNode; conf: ConfigRef; currPosition: var int) = if t != nil and t.len > 0 and t[0] != nil: diff --git a/tests/misc/taddr.nim b/tests/misc/taddr.nim index d743d4b4a..bac26896d 100644 --- a/tests/misc/taddr.nim +++ b/tests/misc/taddr.nim @@ -196,10 +196,76 @@ template test14339() = # bug #14339 when not defined(js): # pending bug #16003 doAssert a.val == 5 +template testStatic15464() = # bug #15464 + proc access(s: var seq[char], i: int): var char = s[i] + proc access(s: var string, i: int): var char = s[i] + static: + var s = @['a', 'b', 'c'] + access(s, 2) = 'C' + doAssert access(s, 2) == 'C' + static: + var s = "abc" + access(s, 2) = 'C' + doAssert access(s, 2) == 'C' + +proc test15464() = # bug #15464 (v2) + proc access(s: var seq[char], i: int): var char = s[i] + proc access(s: var string, i: int): var char = s[i] + block: + var s = @['a', 'b', 'c'] + access(s, 2) = 'C' + doAssert access(s, 2) == 'C' + block: + var s = "abc" + access(s, 2) = 'C' + doAssert access(s, 2) == 'C' + +block: # bug #15939 + block: + const foo = "foo" + proc proc1(s: var string) = + if s[^1] notin {'a'..'z'}: + s = "" + proc proc2(f: string): string = + result = f + proc1(result) + const bar = proc2(foo) + doAssert bar == "foo" + +proc test15939() = # bug #15939 (v2) + template fn(a) = + let pa = a[0].addr + doAssert pa != nil + doAssert pa[] == 'a' + pa[] = 'x' + doAssert pa[] == 'x' + doAssert a == "xbc" + when not defined js: # otherwise overflows + let pa2 = cast[ptr char](cast[int](pa) + 1) + doAssert pa2[] == 'b' + pa2[] = 'B' + doAssert a == "xBc" + + # mystring[ind].addr + var a = "abc" + fn(a) + + # mycstring[ind].addr + template cstringTest = + var a2 = "abc" + var b2 = a2.cstring + fn(b2) + when nimvm: cstringTest() + else: # can't take address of cstring element in js + when not defined(js): cstringTest() + template main = # xxx wrap all other tests here like that so they're also tested in VM test14420() test14339() + test15464() + test15939() +testStatic15464() static: main() main() From e223a05123144ae8a030b2c22f4b7d8b23784314 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Thu, 3 Dec 2020 19:09:54 +0300 Subject: [PATCH 1123/3768] minor fix to Posix part of walkDir (#16234) * change break->continue and rewrite one-yield-style * use case statement for clarity * Tiny nit Co-authored-by: Clyybber --- lib/pure/os.nim | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index aa2a1aaa8..77499deea 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -2120,21 +2120,27 @@ iterator walkDir*(dir: string; relative = false, checkDir = false): y = path var k = pcFile + template kSetGeneric() = # pure Posix component `k` resolution + if lstat(path, s) < 0'i32: continue # don't yield + elif S_ISDIR(s.st_mode): + k = pcDir + elif S_ISLNK(s.st_mode): + k = getSymlinkFileKind(path) + when defined(linux) or defined(macosx) or defined(bsd) or defined(genode) or defined(nintendoswitch): - if x.d_type != DT_UNKNOWN: - if x.d_type == DT_DIR: k = pcDir - if x.d_type == DT_LNK: - if dirExists(path): k = pcLinkToDir - else: k = pcLinkToFile - yield (k, y) - continue + case x.d_type + of DT_DIR: k = pcDir + of DT_LNK: + if dirExists(path): k = pcLinkToDir + else: k = pcLinkToFile + of DT_UNKNOWN: + kSetGeneric() + else: # e.g. DT_REG etc + discard # leave it as pcFile + else: # assuming that field `d_type` is not present + kSetGeneric() - if lstat(path, s) < 0'i32: break - if S_ISDIR(s.st_mode): - k = pcDir - elif S_ISLNK(s.st_mode): - k = getSymlinkFileKind(path) yield (k, y) iterator walkDirRec*(dir: string, From 545c406cbeb9d312e29f7363727c0eb9b37b7da7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alf-Andr=C3=A9=20Walla?= Date: Thu, 3 Dec 2020 17:33:42 +0100 Subject: [PATCH 1124/3768] Add 32-bit RISC-V support (#16231) --- compiler/installer.ini | 2 +- compiler/platform.nim | 3 ++- lib/system.nim | 2 +- lib/system/platforms.nim | 4 +++- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/compiler/installer.ini b/compiler/installer.ini index f0756ab3e..ecd1f7475 100644 --- a/compiler/installer.ini +++ b/compiler/installer.ini @@ -6,7 +6,7 @@ Name: "Nim" Version: "$version" Platforms: """ windows: i386;amd64 - linux: i386;hppa;ia64;alpha;amd64;powerpc64;arm;sparc;sparc64;m68k;mips;mipsel;mips64;mips64el;powerpc;powerpc64el;arm64;riscv64 + linux: i386;hppa;ia64;alpha;amd64;powerpc64;arm;sparc;sparc64;m68k;mips;mipsel;mips64;mips64el;powerpc;powerpc64el;arm64;riscv32;riscv64 macosx: i386;amd64;powerpc64 solaris: i386;amd64;sparc;sparc64 freebsd: i386;amd64;powerpc64;arm;arm64;riscv64;sparc64;mips;mipsel;mips64;mips64el;powerpc;powerpc64el diff --git a/compiler/platform.nim b/compiler/platform.nim index 78c523c1a..714946315 100644 --- a/compiler/platform.nim +++ b/compiler/platform.nim @@ -193,7 +193,7 @@ type cpuNone, cpuI386, cpuM68k, cpuAlpha, cpuPowerpc, cpuPowerpc64, cpuPowerpc64el, cpuSparc, cpuVm, cpuHppa, cpuIa64, cpuAmd64, cpuMips, cpuMipsel, cpuArm, cpuArm64, cpuJS, cpuNimVM, cpuAVR, cpuMSP430, - cpuSparc64, cpuMips64, cpuMips64el, cpuRiscV64, cpuEsp, cpuWasm32 + cpuSparc64, cpuMips64, cpuMips64el, cpuRiscV32, cpuRiscV64, cpuEsp, cpuWasm32 type TEndian* = enum @@ -226,6 +226,7 @@ const (name: "sparc64", intSize: 64, endian: bigEndian, floatSize: 64, bit: 64), (name: "mips64", intSize: 64, endian: bigEndian, floatSize: 64, bit: 64), (name: "mips64el", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64), + (name: "riscv32", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32), (name: "riscv64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64), (name: "esp", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32), (name: "wasm32", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32)] diff --git a/lib/system.nim b/lib/system.nim index c13b419ef..76bcd4eec 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1068,7 +1068,7 @@ const ## Possible values: ## `"i386"`, `"alpha"`, `"powerpc"`, `"powerpc64"`, `"powerpc64el"`, ## `"sparc"`, `"amd64"`, `"mips"`, `"mipsel"`, `"arm"`, `"arm64"`, - ## `"mips64"`, `"mips64el"`, `"riscv64"`. + ## `"mips64"`, `"mips64el"`, `"riscv32"`, `"riscv64"`. seqShallowFlag = low(int) strlitFlag = 1 shl (sizeof(int)*8 - 2) # later versions of the codegen \ diff --git a/lib/system/platforms.nim b/lib/system/platforms.nim index 6e39dc7f2..59a1333f1 100644 --- a/lib/system/platforms.nim +++ b/lib/system/platforms.nim @@ -33,7 +33,8 @@ type vm, ## Some Virtual machine: Nim's VM or JavaScript avr, ## AVR based processor msp430, ## TI MSP430 microcontroller - riscv64 ## RISC-V 64-bit processor + riscv32, ## RISC-V 32-bit processor + riscv64, ## RISC-V 64-bit processor wasm32 ## WASM, 32-bit OsPlatform* {.pure.} = enum ## the OS this program will run on. @@ -91,6 +92,7 @@ const elif defined(vm): CpuPlatform.vm elif defined(avr): CpuPlatform.avr elif defined(msp430): CpuPlatform.msp430 + elif defined(riscv32): CpuPlatform.riscv32 elif defined(riscv64): CpuPlatform.riscv64 elif defined(wasm32): CpuPlatform.wasm32 else: CpuPlatform.none From 2220aaeaef74cb6018f4689af8f280db22cb30dd Mon Sep 17 00:00:00 2001 From: Miran Date: Thu, 3 Dec 2020 17:34:30 +0100 Subject: [PATCH 1125/3768] add support for parsing chars in `scanf` macro (#16240) --- changelog.md | 4 ++++ lib/pure/parseutils.nim | 4 ++++ lib/pure/strscans.nim | 7 +++++++ tests/stdlib/tstrscans.nim | 19 ++++++++++++++++++- 4 files changed, 33 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 5b22a174b..05958118b 100644 --- a/changelog.md +++ b/changelog.md @@ -52,6 +52,9 @@ - `writeStackTrace` is available in JS backend now. +- `strscans.scanf` now supports parsing single characters. + + ## Language changes - `nimscript` now handles `except Exception as e`. @@ -60,6 +63,7 @@ - nil dereference is not allowed at compile time. `cast[ptr int](nil)[]` is rejected at compile time. + ## Compiler changes - Added `--declaredlocs` to show symbol declaration location in messages. diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim index 29159816d..ccfac2a7e 100644 --- a/lib/pure/parseutils.nim +++ b/lib/pure/parseutils.nim @@ -241,6 +241,10 @@ proc parseIdent*(s: string, start = 0): string = while i < s.len and s[i] in IdentChars: inc(i) result = substr(s, start, i-1) +proc parseChar*(s: string, ident: var char, start = 0): int = + ident = s[start] + result = 1 + proc skipWhitespace*(s: string, start = 0): int {.inline.} = ## Skips the whitespace starting at ``s[start]``. Returns the number of ## skipped characters. diff --git a/lib/pure/strscans.nim b/lib/pure/strscans.nim index f7032f428..347dbc2ef 100644 --- a/lib/pure/strscans.nim +++ b/lib/pure/strscans.nim @@ -37,6 +37,7 @@ substrings starting with ``$``. These constructions are available: ``$h`` Matches a hex integer. This uses ``parseutils.parseHex``. ``$f`` Matches a floating pointer number. Uses ``parseFloat``. ``$w`` Matches an ASCII identifier: ``[A-Za-z_][A-Za-z_0-9]*``. +``$c`` Matches a single ASCII character. ``$s`` Skips optional whitespace. ``$$`` Matches a single dollar sign. ``$.`` Matches if the end of the input string has been reached. @@ -345,6 +346,12 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b else: matchError inc i + of 'c': + if i < results.len and getType(results[i]).typeKind == ntyChar: + matchBind "parseChar" + else: + matchError + inc i of 'b': if i < results.len and getType(results[i]).typeKind == ntyInt: matchBind "parseBin" diff --git a/tests/stdlib/tstrscans.nim b/tests/stdlib/tstrscans.nim index 9d6f51025..8ca167837 100644 --- a/tests/stdlib/tstrscans.nim +++ b/tests/stdlib/tstrscans.nim @@ -2,7 +2,7 @@ discard """ output: "" """ -import strscans +import strscans, strutils block ParsePasswd: proc parsePasswd(content: string): seq[string] = @@ -210,3 +210,20 @@ block: var a: int discard scanf(test(), ",$i", a) doAssert count == 1 + + +block: + let input = """1-3 s: abc +15-18 9: def +15-18 A: ghi +15-18 _: jkl +""" + var + lo, hi: int + w: string + c: char + res: int + for line in input.splitLines: + if line.scanf("$i-$i $c: $w", lo, hi, c, w): + inc res + doAssert res == 4 From 808ab7eae22536167445818c9a4650d36e87d39a Mon Sep 17 00:00:00 2001 From: Antonis Geralis <43617260+planetis-m@users.noreply.github.com> Date: Thu, 3 Dec 2020 21:32:18 +0200 Subject: [PATCH 1126/3768] add collect with infered init, refs #16078 fixes #14332 (#16089) * changelog * add testcase, fixes #14332 --- changelog.md | 3 ++ lib/pure/sugar.nim | 100 ++++++++++++++++++++++++++-------------- tests/stdlib/tsugar.nim | 22 +++++++-- 3 files changed, 88 insertions(+), 37 deletions(-) diff --git a/changelog.md b/changelog.md index 05958118b..bb49dc7f6 100644 --- a/changelog.md +++ b/changelog.md @@ -22,6 +22,9 @@ literals remain in the "raw" string form so that client code can easily treat small and large numbers uniformly. +- Added an overload for the `collect` macro that inferes the container type based + on the syntax of the last expression. Works with std seqs, tables and sets. + - Added `randState` template that exposes the default random number generator. Useful for library authors. diff --git a/lib/pure/sugar.nim b/lib/pure/sugar.nim index 047104972..686729515 100644 --- a/lib/pure/sugar.nim +++ b/lib/pure/sugar.nim @@ -58,7 +58,7 @@ macro `=>`*(p, b: untyped): untyped = runnableExamples: proc passTwoAndTwo(f: (int, int) -> int): int = f(2, 2) - + doAssert passTwoAndTwo((x, y) => x + y) == 4 type @@ -270,54 +270,79 @@ since (1, 1): underscoredCalls(result, calls, tmp) result.add tmp - -proc transLastStmt(n, res, bracketExpr: NimNode): (NimNode, NimNode, NimNode) {.since: (1, 1).} = +proc trans(n, res, bracketExpr: NimNode): (NimNode, NimNode, NimNode) {.since: (1, 1).} = # Looks for the last statement of the last statement, etc... case n.kind - of nnkIfExpr, nnkIfStmt, nnkTryStmt, nnkCaseStmt: + of nnkIfExpr, nnkIfStmt, nnkTryStmt, nnkCaseStmt, nnkWhenStmt: result[0] = copyNimTree(n) result[1] = copyNimTree(n) result[2] = copyNimTree(n) - for i in ord(n.kind == nnkCaseStmt)..= 1: - (result[0][^1], result[1][^1], result[2][^1]) = transLastStmt(n[^1], res, bracketExpr) + (result[0][^1], result[1][^1], result[2][^1]) = trans(n[^1], + res, bracketExpr) of nnkTableConstr: result[1] = n[0][0] result[2] = n[0][1] + if bracketExpr.len == 0: + bracketExpr.add(ident"initTable") # don't import tables if bracketExpr.len == 1: - bracketExpr.add([newCall(bindSym"typeof", newEmptyNode()), newCall( - bindSym"typeof", newEmptyNode())]) + bracketExpr.add([newCall(bindSym"typeof", + newEmptyNode()), newCall(bindSym"typeof", newEmptyNode())]) template adder(res, k, v) = res[k] = v result[0] = getAst(adder(res, n[0][0], n[0][1])) of nnkCurly: result[2] = n[0] + if bracketExpr.len == 0: + bracketExpr.add(ident"initHashSet") if bracketExpr.len == 1: bracketExpr.add(newCall(bindSym"typeof", newEmptyNode())) template adder(res, v) = res.incl(v) result[0] = getAst(adder(res, n[0])) else: result[2] = n + if bracketExpr.len == 0: + bracketExpr.add(bindSym"newSeq") if bracketExpr.len == 1: bracketExpr.add(newCall(bindSym"typeof", newEmptyNode())) template adder(res, v) = res.add(v) result[0] = getAst(adder(res, n)) +proc collectImpl(init, body: NimNode): NimNode {.since: (1, 1).} = + let res = genSym(nskVar, "collectResult") + var bracketExpr: NimNode + if init != nil: + expectKind init, {nnkCall, nnkIdent, nnkSym} + bracketExpr = newTree(nnkBracketExpr, + if init.kind == nnkCall: freshIdentNodes(init[0]) else: freshIdentNodes(init)) + else: + bracketExpr = newTree(nnkBracketExpr) + let (resBody, keyType, valueType) = trans(body, res, bracketExpr) + if bracketExpr.len == 3: + bracketExpr[1][1] = keyType + bracketExpr[2][1] = valueType + else: + bracketExpr[1][1] = valueType + let call = newTree(nnkCall, bracketExpr) + if init != nil and init.kind == nnkCall: + for i in 1 ..< init.len: + call.add init[i] + result = newTree(nnkStmtListExpr, newVarStmt(res, call), resBody, res) + macro collect*(init, body: untyped): untyped {.since: (1, 1).} = - ## Comprehension for seq/set/table collections. ``init`` is - ## the init call, and so custom collections are supported. + ## Comprehension for seqs/sets/tables. ## - ## The last statement of ``body`` has special syntax that specifies - ## the collection's add operation. Use ``{e}`` for set's ``incl``, - ## ``{k: v}`` for table's ``[]=`` and ``e`` for seq's ``add``. - ## - ## The ``init`` proc can be called with any number of arguments, - ## i.e. ``initTable(initialSize)``. + ## The last expression of `body` has special syntax that specifies + ## the collection's add operation. Use `{e}` for set's `incl`, + ## `{k: v}` for table's `[]=` and `e` for seq's `add`. + # analyse the body, find the deepest expression 'it' and replace it via + # 'result.add it' runnableExamples: import sets, tables let data = @["bird", "word"] @@ -343,20 +368,27 @@ macro collect*(init, body: untyped): untyped {.since: (1, 1).} = for i, d in data.pairs: {i: d} assert z == {0: "bird", 1: "word"}.toTable - # analyse the body, find the deepest expression 'it' and replace it via - # 'result.add it' - let res = genSym(nskVar, "collectResult") - expectKind init, {nnkCall, nnkIdent, nnkSym} - let bracketExpr = newTree(nnkBracketExpr, - if init.kind == nnkCall: init[0] else: init) - let (resBody, keyType, valueType) = transLastStmt(body, res, bracketExpr) - if bracketExpr.len == 3: - bracketExpr[1][1] = keyType - bracketExpr[2][1] = valueType - else: - bracketExpr[1][1] = valueType - let call = newTree(nnkCall, bracketExpr) - if init.kind == nnkCall: - for i in 1 ..< init.len: - call.add init[i] - result = newTree(nnkStmtListExpr, newVarStmt(res, call), resBody, res) + result = collectImpl(init, body) + +macro collect*(body: untyped): untyped {.since: (1, 5).} = + ## Same as `collect` but without an `init` parameter. + runnableExamples: + import sets, tables + # Seq: + let data = @["bird", "word"] + let k = collect: + for i, d in data.pairs: + if i mod 2 == 0: d + + assert k == @["bird"] + ## HashSet: + let n = collect: + for d in data.items: {d} + + assert n == data.toHashSet + ## Table: + let m = collect: + for i, d in data.pairs: {i: d} + + assert m == {0: "bird", 1: "word"}.toTable + result = collectImpl(nil, body) \ No newline at end of file diff --git a/tests/stdlib/tsugar.nim b/tests/stdlib/tsugar.nim index a6fd70f34..cca1fe75a 100644 --- a/tests/stdlib/tsugar.nim +++ b/tests/stdlib/tsugar.nim @@ -40,7 +40,8 @@ import random const b = @[0, 1, 2] let c = b.dup shuffle() -doAssert c.len == 3 +doAssert b[0] == 0 +doAssert b[1] == 1 #test collect import sets, tables @@ -83,12 +84,27 @@ let z = collect(newSeq): else: d assert z == @["word", "word"] - proc tforum = let ans = collect(newSeq): for y in 0..10: if y mod 5 == 2: for x in 0..y: x - tforum() + +block: + let x = collect: + for d in data.items: + when d is int: "word" + else: d + assert x == @["bird", "word"] +assert collect(for (i, d) in pairs(data): (i, d)) == @[(0, "bird"), (1, "word")] +assert collect(for d in data.items: (try: parseInt(d) except: 0)) == @[0, 0] +assert collect(for (i, d) in pairs(data): {i: d}) == {1: "word", + 0: "bird"}.toTable +assert collect(for d in data.items: {d}) == data.toHashSet + +# bug #14332 +template foo = + discard collect(newSeq, for i in 1..3: i) +foo() \ No newline at end of file From e4e5a0c65ab10f48746f8f9b2417e8ca69724a04 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Fri, 4 Dec 2020 05:59:17 +0800 Subject: [PATCH 1127/3768] cleanup docs and tests (#16235) * js module also uses runnableExamples * cleanup docs and tests --- lib/system/comparisons.nim | 2 +- tests/system/tstrmantle.nim | 18 +++++++++--------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/system/comparisons.nim b/lib/system/comparisons.nim index a20e21064..c57cfa965 100644 --- a/lib/system/comparisons.nim +++ b/lib/system/comparisons.nim @@ -253,7 +253,7 @@ proc clamp*[T](x, a, b: T): T = ## Limits the value `x` within the interval [a, b]. ## This proc is equivalent to but fatser than `max(a, min(b, x))`. ## - ## **Note:** `a < b` is assumed and will not be checked. + ## **Note:** `a <= b` is assumed and will not be checked. runnableExamples: assert (1.4).clamp(0.0, 1.0) == 1.0 assert (0.5).clamp(0.0, 1.0) == 0.5 diff --git a/tests/system/tstrmantle.nim b/tests/system/tstrmantle.nim index 5b232193e..1f195adde 100644 --- a/tests/system/tstrmantle.nim +++ b/tests/system/tstrmantle.nim @@ -5,42 +5,42 @@ for i in 0 .. 9: doAssert res == "0123456789" -res = newStringOfCap(24) +res.setLen(0) for i in -9 .. 0: res.addInt int64(i) doAssert res == "-9-8-7-6-5-4-3-2-10" -res = newStringOfCap(24) +res.setLen(0) res.addInt high(int64) doAssert res == "9223372036854775807" -res = newStringOfCap(24) +res.setLen(0) res.addInt low(int64) doAssert res == "-9223372036854775808" -res = newStringOfCap(12) +res.setLen(0) res.addInt high(int32) doAssert res == "2147483647" -res = newStringOfCap(12) +res.setLen(0) res.addInt low(int32) doAssert res == "-2147483648" -res = newStringOfCap(12) +res.setLen(0) res.addInt high(int16) doAssert res == "32767" -res = newStringOfCap(212) +res.setLen(0) res.addInt low(int16) doAssert res == "-32768" -res = newStringOfCap(12) +res.setLen(0) res.addInt high(int8) doAssert res == "127" -res = newStringOfCap(12) +res.setLen(0) res.addInt low(int8) doAssert res == "-128" From 6877e0c8a3881e86ed40ddac3acba8926c8fc6ca Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Fri, 4 Dec 2020 10:50:17 +0300 Subject: [PATCH 1128/3768] RST tables: fix latex col number; allow less than three of `=` (#16040) --- doc/nep1.rst | 4 +- lib/packages/docutils/rst.nim | 97 +++++++++--- lib/packages/docutils/rstgen.nim | 3 +- nimdoc/rst2html/source/rst_examples.rst | 4 +- tests/stdlib/trstgen.nim | 186 +++++++++++++++++++++++- 5 files changed, 263 insertions(+), 31 deletions(-) diff --git a/doc/nep1.rst b/doc/nep1.rst index 1ab45d680..73e4d8433 100644 --- a/doc/nep1.rst +++ b/doc/nep1.rst @@ -1,6 +1,6 @@ -============================================== +========================================================== Nim Enhancement Proposal #1 - Standard Library Style Guide -============================================== +========================================================== :Author: Clay Sweetser, Dominik Picheta :Version: |nimversion| diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index a72ee17b6..bc3efa272 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -101,7 +101,12 @@ const type TokType = enum - tkEof, tkIndent, tkWhite, tkWord, tkAdornment, tkPunct, tkOther + tkEof, tkIndent, + tkWhite, tkWord, + tkAdornment, # used for chapter adornment, transitions and + # horizontal table borders + tkPunct, # one or many punctuation characters + tkOther Token = object # a RST token kind*: TokType # the type of the token ival*: int # the indentation or parsed integer value @@ -114,6 +119,7 @@ type bufpos*: int line*, col*, baseIndent*: int skipPounds*: bool + adornmentLine*: bool proc getThing(L: var Lexer, tok: var Token, s: set[char]) = tok.kind = tkWord @@ -127,8 +133,26 @@ proc getThing(L: var Lexer, tok: var Token, s: set[char]) = inc L.col, pos - L.bufpos L.bufpos = pos -proc getAdornment(L: var Lexer, tok: var Token) = - tok.kind = tkAdornment +proc isCurrentLineAdornment(L: var Lexer): bool = + var pos = L.bufpos + let c = L.buf[pos] + while true: + inc pos + if L.buf[pos] in {'\c', '\l', '\0'}: + break + if c == '+': # grid table + if L.buf[pos] notin {'-', '=', '+'}: + return false + else: # section adornment or table horizontal border + if L.buf[pos] notin {c, ' ', '\t', '\v', '\f'}: + return false + result = true + +proc getPunctAdornment(L: var Lexer, tok: var Token) = + if L.adornmentLine: + tok.kind = tkAdornment + else: + tok.kind = tkPunct tok.line = L.line tok.col = L.col var pos = L.bufpos @@ -139,6 +163,8 @@ proc getAdornment(L: var Lexer, tok: var Token) = if L.buf[pos] != c: break inc L.col, pos - L.bufpos L.bufpos = pos + if tok.symbol == "\\": tok.kind = tkPunct + # nim extension: standalone \ can not be adornment proc getBracket(L: var Lexer, tok: var Token) = tok.kind = tkPunct @@ -189,6 +215,8 @@ proc getIndent(L: var Lexer, tok: var Token) = proc rawGetTok(L: var Lexer, tok: var Token) = tok.symbol = "" tok.ival = 0 + if L.col == 0: + L.adornmentLine = false var c = L.buf[L.bufpos] case c of 'a'..'z', 'A'..'Z', '\x80'..'\xFF', '0'..'9': @@ -200,11 +228,13 @@ proc rawGetTok(L: var Lexer, tok: var Token) = rawGetTok(L, tok) # ignore spaces before \n of '\x0D', '\x0A': getIndent(L, tok) + L.adornmentLine = false of '!', '\"', '#', '$', '%', '&', '\'', '*', '+', ',', '-', '.', '/', ':', ';', '<', '=', '>', '?', '@', '\\', '^', '_', '`', '|', '~': - getAdornment(L, tok) - if tok.symbol.len <= 3: tok.kind = tkPunct + if L.col == 0: + L.adornmentLine = L.isCurrentLineAdornment() + getPunctAdornment(L, tok) of '(', ')', '[', ']', '{', '}': getBracket(L, tok) else: @@ -730,7 +760,7 @@ proc parseMarkdownCodeblock(p: var RstParser): PRstNode = of tkEof: rstMessage(p, meExpected, "```") break - of tkPunct: + of tkPunct, tkAdornment: if currentTok(p).symbol == "```": inc p.idx break @@ -822,6 +852,10 @@ proc parseInline(p: var RstParser, father: PRstNode) = return parseUrl(p, father) of tkAdornment, tkOther, tkWhite: + if roSupportMarkdown in p.s.options and currentTok(p).symbol == "```": + inc p.idx + father.add(parseMarkdownCodeblock(p)) + return if roSupportSmilies in p.s.options: let n = parseSmiley(p) if n != nil: @@ -1011,6 +1045,18 @@ proc tokenAfterNewline(p: RstParser): int = break else: inc result +proc isAdornmentHeadline(p: RstParser, adornmentIdx: int): bool = + var headlineLen = 0 + if p.idx < adornmentIdx: # underline + for i in p.idx ..< adornmentIdx-1: # adornmentIdx-1 is a linebreak + headlineLen += p.tok[i].symbol.len + else: # overline + var i = p.idx + 2 + while p.tok[i].kind notin {tkEof, tkIndent}: + headlineLen += p.tok[i].symbol.len + inc i + return p.tok[adornmentIdx].symbol.len >= headlineLen + proc isLineBlock(p: RstParser): bool = var j = tokenAfterNewline(p) result = currentTok(p).col == p.tok[j].col and p.tok[j].symbol == "|" or @@ -1052,13 +1098,26 @@ proc findPipe(p: RstParser, start: int): bool = inc i proc whichSection(p: RstParser): RstNodeKind = + if currentTok(p).kind in {tkAdornment, tkPunct}: + # for punctuation sequences that can be both tkAdornment and tkPunct + if roSupportMarkdown in p.s.options and currentTok(p).symbol == "```": + return rnCodeBlock + elif currentTok(p).symbol == "::": + return rnLiteralBlock + elif currentTok(p).symbol == ".." and predNL(p): + return rnDirective case currentTok(p).kind of tkAdornment: - if match(p, p.idx + 1, "ii"): result = rnTransition + if match(p, p.idx + 1, "ii") and currentTok(p).symbol.len >= 4: + result = rnTransition + elif match(p, p.idx, "+a+"): + result = rnGridTable + rstMessage(p, meGridTableNotImplemented) elif match(p, p.idx + 1, " a"): result = rnTable - elif match(p, p.idx + 1, "i"): result = rnOverline - elif isMarkdownHeadline(p): - result = rnHeadline + elif currentTok(p).symbol == "|" and isLineBlock(p): + result = rnLineBlock + elif match(p, p.idx + 1, "i") and isAdornmentHeadline(p, p.idx): + result = rnOverline else: result = rnLeaf of tkPunct: @@ -1067,27 +1126,18 @@ proc whichSection(p: RstParser): RstNodeKind = elif roSupportMarkdown in p.s.options and predNL(p) and match(p, p.idx, "| w") and findPipe(p, p.idx+3): result = rnMarkdownTable - elif currentTok(p).symbol == "```": - result = rnCodeBlock + elif currentTok(p).symbol == "|" and isLineBlock(p): + result = rnLineBlock elif match(p, tokenAfterNewline(p), "ai"): result = rnHeadline - elif currentTok(p).symbol == "::": - result = rnLiteralBlock elif predNL(p) and currentTok(p).symbol in ["+", "*", "-"] and nextTok(p).kind == tkWhite: result = rnBulletList - elif currentTok(p).symbol == "|" and isLineBlock(p): - result = rnLineBlock - elif currentTok(p).symbol == ".." and predNL(p): - result = rnDirective elif match(p, p.idx, ":w:") and predNL(p): # (currentTok(p).symbol == ":") result = rnFieldList elif match(p, p.idx, "(e) ") or match(p, p.idx, "e. "): result = rnEnumList - elif match(p, p.idx, "+a+"): - result = rnGridTable - rstMessage(p, meGridTableNotImplemented) elif isDefList(p): result = rnDefList elif isOptionList(p): @@ -1095,7 +1145,10 @@ proc whichSection(p: RstParser): RstNodeKind = else: result = rnParagraph of tkWord, tkOther, tkWhite: - if match(p, tokenAfterNewline(p), "ai"): result = rnHeadline + let tokIdx = tokenAfterNewline(p) + if match(p, tokIdx, "ai"): + if isAdornmentHeadline(p, tokIdx): result = rnHeadline + else: result = rnParagraph elif match(p, p.idx, "e) ") or match(p, p.idx, "e. "): result = rnEnumList elif isDefList(p): result = rnDefList else: result = rnParagraph diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index d6bd26ad7..d88f2ed77 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -1009,7 +1009,8 @@ proc renderContainer(d: PDoc, n: PRstNode, result: var string) = proc texColumns(n: PRstNode): string = result = "" - for i in countup(1, len(n)): add(result, "|X") + let nColumns = if n.sons.len > 0: len(n.sons[0]) else: 1 + for i in countup(1, nColumns): add(result, "|X") proc renderField(d: PDoc, n: PRstNode, result: var string) = var b = false diff --git a/nimdoc/rst2html/source/rst_examples.rst b/nimdoc/rst2html/source/rst_examples.rst index c8d15aef8..54f0124c8 100644 --- a/nimdoc/rst2html/source/rst_examples.rst +++ b/nimdoc/rst2html/source/rst_examples.rst @@ -1,6 +1,6 @@ -========== +================ Not a Nim Manual -========== +================ :Authors: Andreas Rumpf, Zahary Karadjov :Version: |nimversion| diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim index 946104e7c..1bf73e146 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -6,7 +6,7 @@ outputsub: "" import ../../lib/packages/docutils/rstgen import ../../lib/packages/docutils/rst -import unittest, strtabs +import unittest, strutils, strtabs suite "YAML syntax highlighting": test "Basics": @@ -144,6 +144,12 @@ suite "YAML syntax highlighting": ?not a map key""" +suite "RST/Markdown general": + test "RST emphasis": + assert rstToHtml("*Hello* **world**!", {}, + newStringTable(modeStyleInsensitive)) == + "Hello world!" + test "Markdown links": let a = rstToHtml("(( [Nim](https://nim-lang.org/) ))", {roSupportMarkdown}, defaultConfig()) @@ -178,7 +184,179 @@ not in table""" assert output2 == """
      A1 headerA2
      """ + test "RST tables": + let input1 = """ +Test 2 column/4 rows table: +==== === +H0 H1 +==== === +A0 A1 +==== === +A2 A3 +==== === +A4 A5 +==== === """ + let output1 = rstToLatex(input1, {}) + assert "{|X|X|}" in output1 # 2 columns + assert count(output1, "\\\\") == 4 # 4 rows + for cell in ["H0", "H1", "A0", "A1", "A2", "A3", "A4", "A5"]: + assert cell in output1 -assert rstToHtml("*Hello* **world**!", {}, - newStringTable(modeStyleInsensitive)) == - "Hello world!" + let input2 = """ +Now test 3 columns / 2 rows, and also borders containing 4 =, 3 =, 1 = signs: + +==== === = +H0 H1 H +==== === = +A0 A1 X + Ax Y +==== === = """ + let output2 = rstToLatex(input2, {}) + assert "{|X|X|X|}" in output2 # 3 columns + assert count(output2, "\\\\") == 2 # 2 rows + for cell in ["H0", "H1", "H", "A0", "A1", "X", "Ax", "Y"]: + assert cell in output2 + + + test "RST adornments": + let input1 = """ +Check that a few punctuation symbols are not parsed as adornments: +:word1: word2 .... word3 """ + let output1 = rstToHtml(input1, {roSupportMarkdown}, defaultConfig()) + discard output1 + + test "RST sections": + let input1 = """ +Long chapter name +''''''''''''''''''' +""" + let output1 = rstToHtml(input1, {roSupportMarkdown}, defaultConfig()) + assert "Long chapter name" in output1 and "" in output1 + + test "Markdown code block": + let input1 = """ +``` +let x = 1 +``` """ + let output1 = rstToHtml(input1, {roSupportMarkdown}, defaultConfig()) + assert "" in output1 + assert "other line
      " in output1 + let output1l = rstToLatex(input1, {}) + assert "line block\\\\" in output1l + assert "other line\\\\" in output1l From edce5897a52aac308fbe9b14f155139f15da02cd Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 4 Dec 2020 17:47:41 +0100 Subject: [PATCH 1129/3768] fixes #16214 [backport] (#16252) --- lib/system/alloc.nim | 8 ++++---- lib/system/mmdisp.nim | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim index 95658c49a..76c744f59 100644 --- a/lib/system/alloc.nim +++ b/lib/system/alloc.nim @@ -253,7 +253,7 @@ proc llAlloc(a: var MemRegion, size: int): pointer = sysAssert roundup(size+sizeof(LLChunk), PageSize) == PageSize, "roundup 6" var old = a.llmem # can be nil and is correct with nil a.llmem = cast[PLLChunk](osAllocPages(PageSize)) - when defined(avlcorruption): + when defined(nimAvlcorruption): trackLocation(a.llmem, PageSize) incCurrMem(a, PageSize) a.llmem.size = PageSize - sizeof(LLChunk) @@ -276,7 +276,7 @@ proc allocAvlNode(a: var MemRegion, key, upperBound: int): PAvlNode = a.freeAvlNodes = a.freeAvlNodes.link[0] else: result = cast[PAvlNode](llAlloc(a, sizeof(AvlNode))) - when defined(avlcorruption): + when defined(nimAvlcorruption): cprintf("tracking location: %p\n", result) result.key = key result.upperBound = upperBound @@ -284,7 +284,7 @@ proc allocAvlNode(a: var MemRegion, key, upperBound: int): PAvlNode = result.link[0] = bottom result.link[1] = bottom result.level = 1 - #when defined(avlcorruption): + #when defined(nimAvlcorruption): # track("allocAvlNode", result, sizeof(AvlNode)) sysAssert(bottom == addr(a.bottomData), "bottom data") sysAssert(bottom.link[0] == bottom, "bottom link[0]") @@ -1017,7 +1017,7 @@ when defined(nimTypeNames): template instantiateForRegion(allocator: untyped) {.dirty.} = {.push stackTrace: off.} - when defined(fulldebug): + when defined(nimFulldebug): proc interiorAllocatedPtr*(p: pointer): pointer = result = interiorAllocatedPtr(allocator, p) diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim index 5fe1960d1..8589b678d 100644 --- a/lib/system/mmdisp.nim +++ b/lib/system/mmdisp.nim @@ -17,10 +17,10 @@ const debugGC = false # we wish to debug the GC... logGC = false traceGC = false # extensive debugging - alwaysCycleGC = defined(smokeCycles) - alwaysGC = defined(fulldebug) # collect after every memory + alwaysCycleGC = defined(nimSmokeCycles) + alwaysGC = defined(nimFulldebug) # collect after every memory # allocation (for debugging) - leakDetector = defined(leakDetector) + leakDetector = defined(nimLeakDetector) overwriteFree = defined(nimBurnFree) # overwrite memory with 0xFF before free trackAllocationSource = leakDetector @@ -30,7 +30,7 @@ const coalescRight = true coalescLeft = true logAlloc = false - useCellIds = defined(corruption) + useCellIds = defined(nimCorruption) type PPointer = ptr pointer From 8178388a78a013c990e2a7a8e97e4595449e5944 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 5 Dec 2020 00:36:06 +0100 Subject: [PATCH 1130/3768] fixes #16249 [backport:1.4] (#16251) --- compiler/cgen.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index a3d3425e3..c4544bbc5 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -2044,7 +2044,7 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = if m.config.exc == excGoto and getCompilerProc(graph, "nimTestErrorFlag") != nil: discard cgsym(m, "nimTestErrorFlag") - if {optGenStaticLib, optGenDynLib} * m.config.globalOptions == {}: + if {optGenStaticLib, optGenDynLib, optNoMain} * m.config.globalOptions == {}: for i in countdown(high(graph.globalDestructors), 0): n.add graph.globalDestructors[i] if passes.skipCodegen(m.config, n): return From d7f244180e84e4fd0fe7cd34298c8082bba14069 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Sat, 5 Dec 2020 18:41:08 +0100 Subject: [PATCH 1131/3768] Retry commands in testament again (#16262) --- testament/categories.nim | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/testament/categories.nim b/testament/categories.nim index c48d611b1..c894bc9f9 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -482,6 +482,17 @@ proc makeSupTest(test, options: string, cat: Category): TTest = result.options = options result.startTime = epochTime() +const maxRetries = 3 +template retryCommand(call): untyped = + var res: typeof(call) + var backoff = 1 + for i in 0.. Date: Sun, 6 Dec 2020 05:05:16 +0800 Subject: [PATCH 1132/3768] bring back argparse (#16242) --- testament/important_packages.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 6d89df3e5..2da10e749 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -9,7 +9,7 @@ var packages2*: seq[tuple[name, cmd: string; url: string, useHead: bool]] = @[] # packages A-M # pkg1 "alea" -# pkg1 "argparse" +pkg1 "argparse" pkg1 "arraymancer", "nim c tests/tests_cpu.nim" # pkg1 "ast_pattern_matching", "nim c -r --oldgensym:on tests/test1.nim" pkg1 "awk" From af7f7726fe727b68238533f690fb7cd98552e4e4 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 5 Dec 2020 23:15:17 +0100 Subject: [PATCH 1133/3768] ORC: make the adaptive strategy the default in order to fight memory consumption (#16250) * ORC: make the adaptive strategy the default in order to fight memory consumption * added missing test case --- lib/system/orc.nim | 19 +++++++++++++------ tests/arc/tasyncleak.nim | 2 +- tests/arc/torcbench.nim | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+), 7 deletions(-) create mode 100644 tests/arc/torcbench.nim diff --git a/lib/system/orc.nim b/lib/system/orc.nim index 4bd8952b7..803f3d410 100644 --- a/lib/system/orc.nim +++ b/lib/system/orc.nim @@ -39,7 +39,8 @@ template setColor(c, col) = c.rc = c.rc and not colorMask or col const - optimizedOrc = not defined(nimOldOrc) + optimizedOrc = false # not defined(nimOldOrc) +# XXX Still incorrect, see tests/arc/tdestroy_in_loopcond proc nimIncRefCyclic(p: pointer; cyclic: bool) {.compilerRtl, inl.} = let h = head(p) @@ -72,6 +73,7 @@ type jumpStack: CellSeq # Lins' jump stack in order to speed up traversals toFree: CellSeq freed, touched, edges, rcSum: int + keepThreshold: bool proc trace(s: Cell; desc: PNimTypeV2; j: var GcEnv) {.inline.} = if desc.traceImpl != nil: @@ -309,6 +311,8 @@ proc collectCyclesBacon(j: var GcEnv; lowMark: int) = if j.rcSum == j.edges: # short-cut: we know everything is garbage: colToCollect = colGray + # remember the fact that we got so lucky: + j.keepThreshold = true else: for i in countdown(last, lowMark): scan(roots.d[i][0], roots.d[i][1], j) @@ -327,7 +331,7 @@ proc collectCyclesBacon(j: var GcEnv; lowMark: int) = #roots.len = 0 const - defaultThreshold = when defined(nimAdaptiveOrc): 128 else: 10_000 + defaultThreshold = when defined(nimFixedOrc): 10_000 else: 128 when defined(nimStressOrc): const rootsThreshold = 10 # broken with -d:nimStressOrc: 10 and for havlak iterations 1..8 @@ -335,7 +339,8 @@ else: var rootsThreshold = defaultThreshold proc partialCollect(lowMark: int) = - if roots.len < 10 + lowMark: return + when false: + if roots.len < 10 + lowMark: return when logOrc: cfprintf(cstderr, "[partialCollect] begin\n") var j: GcEnv @@ -373,9 +378,11 @@ proc collectCycles() = # of the cycle collector's effectiveness: # we're effective when we collected 50% or more of the nodes # we touched. If we're effective, we can reset the threshold: - if j.freed * 2 >= j.touched: - when defined(nimAdaptiveOrc): - rootsThreshold = max(rootsThreshold div 2, 16) + if j.keepThreshold and rootsThreshold <= defaultThreshold: + discard + elif j.freed * 2 >= j.touched: + when not defined(nimFixedOrc): + rootsThreshold = max(rootsThreshold div 3 * 2, 16) else: rootsThreshold = defaultThreshold #cfprintf(cstderr, "[collectCycles] freed %ld, touched %ld new threshold %ld\n", j.freed, j.touched, rootsThreshold) diff --git a/tests/arc/tasyncleak.nim b/tests/arc/tasyncleak.nim index 417b67edb..eb0c45213 100644 --- a/tests/arc/tasyncleak.nim +++ b/tests/arc/tasyncleak.nim @@ -1,5 +1,5 @@ discard """ - outputsub: "(allocCount: 4014, deallocCount: 4012)" + outputsub: "(allocCount: 4302, deallocCount: 4300)" cmd: "nim c --gc:orc -d:nimAllocStats $file" """ diff --git a/tests/arc/torcbench.nim b/tests/arc/torcbench.nim new file mode 100644 index 000000000..4c9e65fee --- /dev/null +++ b/tests/arc/torcbench.nim @@ -0,0 +1,38 @@ +discard """ + output: '''true peak memory: true''' + cmd: "nim c --gc:orc -d:release $file" +""" + +import lists, strutils, times + +type + Base = ref object of RootObj + + Node = ref object of Base + parent: DoublyLinkedList[string] + le, ri: Node + self: Node # in order to create a cycle + +proc buildTree(parent: DoublyLinkedList[string]; depth: int): Node = + if depth == 0: + result = nil + elif depth == 1: + result = Node(parent: parent, le: nil, ri: nil, self: nil) + when not defined(gcArc): + result.self = result + else: + result = Node(parent: parent, le: buildTree(parent, depth - 1), ri: buildTree(parent, depth - 2), self: nil) + result.self = result + +proc main() = + for i in countup(1, 100): + var leakList = initDoublyLinkedList[string]() + for j in countup(1, 5000): + leakList.append(newString(200)) + #GC_fullCollect() + for i in 0..400: + discard buildTree(leakList, 8) + +main() +GC_fullCollect() +echo getOccupiedMem() < 10 * 1024 * 1024, " peak memory: ", getMaxMem() < 10 * 1024 * 1024 From 1aaa67fc18879bd2aa8ae8f5e7dc763f826c73b6 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sun, 6 Dec 2020 12:51:29 +0800 Subject: [PATCH 1134/3768] [docs minor] remove unicode in docs comments (#16267) --- lib/pure/uri.nim | 2 +- lib/system/atomics.nim | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim index 5b16bfcb1..e993c240d 100644 --- a/lib/pure/uri.nim +++ b/lib/pure/uri.nim @@ -11,7 +11,7 @@ ## ## A Uniform Resource Identifier (URI) provides a simple and extensible ## means for identifying a resource. A URI can be further classified -## as a locator, a name, or both. The term “Uniform Resource Locator” +## as a locator, a name, or both. The term "Uniform Resource Locator" ## (URL) refers to the subset of URIs. ## ## Basic usage diff --git a/lib/system/atomics.nim b/lib/system/atomics.nim index 15e8a56a7..f69cb753d 100644 --- a/lib/system/atomics.nim +++ b/lib/system/atomics.nim @@ -125,8 +125,8 @@ when someGcc and hasThreadSupport: proc atomicTestAndSet*(p: pointer, mem: AtomMemModel): bool {. importc: "__atomic_test_and_set", nodecl.} ## This built-in function performs an atomic test-and-set operation on the byte at p. - ## The byte is set to some implementation defined nonzero “set” value and the return - ## value is true if and only if the previous contents were “set”. + ## The byte is set to some implementation defined nonzero "set" value and the return + ## value is true if and only if the previous contents were "set". ## All memory models are valid. proc atomicClear*(p: pointer, mem: AtomMemModel) {. From 48d7c40553f0769c22b4b47f8079dd96c3e23323 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sun, 6 Dec 2020 16:50:46 +0800 Subject: [PATCH 1135/3768] fix #16206 (#16207) * better docs and tests * a bit better only clean trailing whitespace --- lib/pure/parsecfg.nim | 70 +++++++++++++++++++++++++++++- tests/stdlib/tparsecfg.nim | 89 ++++++++++++++++++++++++++++++-------- 2 files changed, 141 insertions(+), 18 deletions(-) diff --git a/lib/pure/parsecfg.nim b/lib/pure/parsecfg.nim index 9b140a809..c87010122 100644 --- a/lib/pure/parsecfg.nim +++ b/lib/pure/parsecfg.nim @@ -107,6 +107,70 @@ ## var dict = loadConfig("config.ini") ## dict.delSectionKey("Author","email") ## dict.writeConfig("config.ini") +## +## Supported INI File structure +## ---------------------------- +## The examples below are supported: +## + +# taken from https://docs.python.org/3/library/configparser.html#supported-ini-file-structure +runnableExamples: + import streams + + var dict = loadConfig(newStringStream("""[Simple Values] + key=value + spaces in keys=allowed + spaces in values=allowed as well + spaces around the delimiter = obviously + you can also use : to delimit keys from values + [All Values Are Strings] + values like this: 19990429 + or this: 3.14159265359 + are they treated as numbers : no + integers floats and booleans are held as: strings + can use the API to get converted values directly: true + [No Values] + key_without_value + # empty string value is not allowed = + [ Seletion A ] + space around section name will be ignored + [You can use comments] + # like this + ; or this + # By default only in an empty line. + # Inline comments can be harmful because they prevent users + # from using the delimiting characters as parts of values. + # That being said, this can be customized. + [Sections Can Be Indented] + can_values_be_as_well = True + does_that_mean_anything_special = False + purpose = formatting for readability + # Did I mention we can indent comments, too? + """) + ) + + let section1 = "Simple Values" + doAssert dict.getSectionValue(section1, "key") == "value" + doAssert dict.getSectionValue(section1, "spaces in keys") == "allowed" + doAssert dict.getSectionValue(section1, "spaces in values") == "allowed as well" + doAssert dict.getSectionValue(section1, "spaces around the delimiter") == "obviously" + doAssert dict.getSectionValue(section1, "you can also use") == "to delimit keys from values" + + let section2 = "All Values Are Strings" + doAssert dict.getSectionValue(section2, "values like this") == "19990429" + doAssert dict.getSectionValue(section2, "or this") == "3.14159265359" + doAssert dict.getSectionValue(section2, "are they treated as numbers") == "no" + doAssert dict.getSectionValue(section2, "integers floats and booleans are held as") == "strings" + doAssert dict.getSectionValue(section2, "can use the API to get converted values directly") == "true" + + let section3 = "Seletion A" + doAssert dict.getSectionValue(section3, + "space around section name will be ignored", "not an empty value") == "" + + let section4 = "Sections Can Be Indented" + doAssert dict.getSectionValue(section4, "can_values_be_as_well") == "True" + doAssert dict.getSectionValue(section4, "does_that_mean_anything_special") == "False" + doAssert dict.getSectionValue(section4, "purpose") == "formatting for readability" import strutils, lexbase, streams, tables @@ -151,7 +215,7 @@ type # implementation const - SymChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\x80'..'\xFF', '.', '/', '\\', '-'} + SymChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', ' ', '\x80'..'\xFF', '.', '/', '\\', '-'} proc rawGetTok(c: var CfgParser, tok: var Token) {.gcsafe.} @@ -306,6 +370,10 @@ proc getSymbol(c: var CfgParser, tok: var Token) = add(tok.literal, c.buf[pos]) inc(pos) if not (c.buf[pos] in SymChars): break + + while tok.literal.len > 0 and tok.literal[^1] == ' ': + tok.literal.setLen(tok.literal.len - 1) + c.bufpos = pos tok.kind = tkSymbol diff --git a/tests/stdlib/tparsecfg.nim b/tests/stdlib/tparsecfg.nim index 45dea0531..bbf88ee03 100644 --- a/tests/stdlib/tparsecfg.nim +++ b/tests/stdlib/tparsecfg.nim @@ -5,28 +5,27 @@ discard """ import parsecfg, streams when not defined(js): - # bug #6046 - var config = newConfig() - config.setSectionKey("foo", "bar", "-1") - config.setSectionKey("foo", "foo", "abc") - from stdtest/specialpaths import buildDir import os + # bug #6046 + block: + var config = newConfig() + config.setSectionKey("foo", "bar", "-1") + config.setSectionKey("foo", "foo", "abc") - const file = buildDir / "tparsecfg.ini" - config.writeConfig(file) + const file = buildDir / "tparsecfg.ini" + config.writeConfig(file) - # file now contains - # [foo] - # bar=-1 - # foo=abc - - var config2 = loadConfig(file) - let bar = config2.getSectionValue("foo", "bar") - let foo = config2.getSectionValue("foo", "foo") - assert(bar == "-1") - assert(foo == "abc") + # file now contains + # [foo] + # bar=-1 + # foo=abc + var config2 = loadConfig(file) + let bar = config2.getSectionValue("foo", "bar") + let foo = config2.getSectionValue("foo", "foo") + assert(bar == "-1") + assert(foo == "abc") ## Creating a configuration file. var dict1 = newConfig() @@ -78,3 +77,59 @@ name=hello name=lihf8515 qq=10214028 """ + +block: + var dict = loadConfig(newStringStream("""[Simple Values] + key=value + spaces in keys=allowed + spaces in values=allowed as well + spaces around the delimiter = obviously + you can also use : to delimit keys from values + [All Values Are Strings] + values like this: 19990429 + or this: 3.14159265359 + are they treated as numbers : no + integers floats and booleans are held as: strings + can use the API to get converted values directly: true + [No Values] + key_without_value + # empty string value is not allowed = + [ Seletion A ] + space around section name will be ignored + [You can use comments] + # like this + ; or this + # By default only in an empty line. + # Inline comments can be harmful because they prevent users + # from using the delimiting characters as parts of values. + # That being said, this can be customized. + [Sections Can Be Indented] + can_values_be_as_well = True + does_that_mean_anything_special = False + purpose = formatting for readability + # Did I mention we can indent comments, too? + """) + ) + + let section1 = "Simple Values" + doAssert dict.getSectionValue(section1, "key") == "value" + doAssert dict.getSectionValue(section1, "spaces in keys") == "allowed" + doAssert dict.getSectionValue(section1, "spaces in values") == "allowed as well" + doAssert dict.getSectionValue(section1, "spaces around the delimiter") == "obviously" + doAssert dict.getSectionValue(section1, "you can also use") == "to delimit keys from values" + + let section2 = "All Values Are Strings" + doAssert dict.getSectionValue(section2, "values like this") == "19990429" + doAssert dict.getSectionValue(section2, "or this") == "3.14159265359" + doAssert dict.getSectionValue(section2, "are they treated as numbers") == "no" + doAssert dict.getSectionValue(section2, "integers floats and booleans are held as") == "strings" + doAssert dict.getSectionValue(section2, "can use the API to get converted values directly") == "true" + + let section3 = "Seletion A" + doAssert dict.getSectionValue(section3, + "space around section name will be ignored", "not an empty value") == "" + + let section4 = "Sections Can Be Indented" + doAssert dict.getSectionValue(section4, "can_values_be_as_well") == "True" + doAssert dict.getSectionValue(section4, "does_that_mean_anything_special") == "False" + doAssert dict.getSectionValue(section4, "purpose") == "formatting for readability" From 1e320bc8c5112b152ebc3092331b8b1fe0ae5191 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sun, 6 Dec 2020 16:54:32 +0800 Subject: [PATCH 1136/3768] [docs minor]space for code-block (#16266) * [docs minor]space for code-block * correct more errors * to runnableExamples * add newline --- compiler/astalgo.nim | 4 ++-- compiler/sigmatch.nim | 4 +++- compiler/vmgen.nim | 2 +- lib/pure/unidecode/unidecode.nim | 12 +++--------- 4 files changed, 9 insertions(+), 13 deletions(-) diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 832f8819a..300089d81 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -222,9 +222,9 @@ proc getNamedParamFromList*(list: PNode, ident: PIdent): PSym = ## gensym'ed and then they have '\`' suffix that we need to ## ignore, see compiler / evaltempl.nim, snippet: ## - ##..code-block:: nim + ## .. code-block:: nim ## - ## result.add newIdentNode(getIdent(c.ic, x.name.s & "\`gensym" & $x.id), + ## result.add newIdentNode(getIdent(c.ic, x.name.s & "\`gensym" & $x.id), ## if c.instLines: actual.info else: templ.info) for i in 1.. Date: Sun, 6 Dec 2020 10:13:19 +0100 Subject: [PATCH 1137/3768] nimdoc: Initialize theme switch and pragma dots on DOMContentLoaded (#16247) The default HTML template for nimdoc currently initializes the dark mode switch and pragma dots when the onload event is fired. But since the onload event does not fire until all external resources (images, stylesheets, fonts, etc.) have been loaded, the light theme is shown for a brief moment before the document is fully loaded, and it switches to the dark theme. This is quite jarring, especially on slower internet connections. So let's instead initialize these things on the DOMContentLoaded event, which is fired right after the document has been parsed and the initial DOM structure is ready. This means that we now display the dark mode immediately, without having to wait for external resources to load first. For reference, see: - https://developer.mozilla.org/en-US/docs/Web/API/Window/load_event - https://developer.mozilla.org/en-US/docs/Web/API/Window/DOMContentLoaded_event I have updated the snapshot tests in `nimdoc/`, and done some manual testing of both the theme switch and the pragma dots, to confirm that this does not break anything. Please let me know if I've missed anything. --- config/nimdoc.cfg | 4 +++- nimdoc/rst2html/expected/rst_examples.html | 4 +++- nimdoc/test_out_index_dot_html/expected/index.html | 4 +++- nimdoc/test_out_index_dot_html/expected/theindex.html | 4 +++- nimdoc/testproject/expected/subdir/subdir_b/utils.html | 4 +++- nimdoc/testproject/expected/testproject.html | 4 +++- nimdoc/testproject/expected/theindex.html | 4 +++- 7 files changed, 21 insertions(+), 7 deletions(-) diff --git a/config/nimdoc.cfg b/config/nimdoc.cfg index e4b485ca0..585b3cc49 100644 --- a/config/nimdoc.cfg +++ b/config/nimdoc.cfg @@ -268,10 +268,12 @@ function main() { } } } + +window.addEventListener('DOMContentLoaded', main); - +

      $title

      diff --git a/nimdoc/rst2html/expected/rst_examples.html b/nimdoc/rst2html/expected/rst_examples.html index 8a08e8df5..0b9207688 100644 --- a/nimdoc/rst2html/expected/rst_examples.html +++ b/nimdoc/rst2html/expected/rst_examples.html @@ -56,10 +56,12 @@ function main() { } } } + +window.addEventListener('DOMContentLoaded', main); - +

      Not a Nim Manual

      diff --git a/nimdoc/test_out_index_dot_html/expected/index.html b/nimdoc/test_out_index_dot_html/expected/index.html index 65241d356..35a5dfb56 100644 --- a/nimdoc/test_out_index_dot_html/expected/index.html +++ b/nimdoc/test_out_index_dot_html/expected/index.html @@ -56,10 +56,12 @@ function main() { } } } + +window.addEventListener('DOMContentLoaded', main); - +

      foo

      diff --git a/nimdoc/test_out_index_dot_html/expected/theindex.html b/nimdoc/test_out_index_dot_html/expected/theindex.html index b2863f99a..aa9ca2645 100644 --- a/nimdoc/test_out_index_dot_html/expected/theindex.html +++ b/nimdoc/test_out_index_dot_html/expected/theindex.html @@ -56,10 +56,12 @@ function main() { } } } + +window.addEventListener('DOMContentLoaded', main); - +

      Index

      diff --git a/nimdoc/testproject/expected/subdir/subdir_b/utils.html b/nimdoc/testproject/expected/subdir/subdir_b/utils.html index 40de645e3..27cef017b 100644 --- a/nimdoc/testproject/expected/subdir/subdir_b/utils.html +++ b/nimdoc/testproject/expected/subdir/subdir_b/utils.html @@ -56,10 +56,12 @@ function main() { } } } + +window.addEventListener('DOMContentLoaded', main); - +

      subdir/subdir_b/utils

      diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html index ee09b46af..a32e1180c 100644 --- a/nimdoc/testproject/expected/testproject.html +++ b/nimdoc/testproject/expected/testproject.html @@ -56,10 +56,12 @@ function main() { } } } + +window.addEventListener('DOMContentLoaded', main); - +

      testproject

      diff --git a/nimdoc/testproject/expected/theindex.html b/nimdoc/testproject/expected/theindex.html index 03d9c72fe..978560e0f 100644 --- a/nimdoc/testproject/expected/theindex.html +++ b/nimdoc/testproject/expected/theindex.html @@ -56,10 +56,12 @@ function main() { } } } + +window.addEventListener('DOMContentLoaded', main); - +

      Index

      From 57d2c293d3f3fd86c003b35b215f701bfa22dbd1 Mon Sep 17 00:00:00 2001 From: Pietro Peterlongo Date: Sun, 6 Dec 2020 10:14:55 +0100 Subject: [PATCH 1138/3768] fix parseChar see #16240 (#16245) * fix parseChar * do not introduce new double backticks --- lib/pure/parseutils.nim | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim index ccfac2a7e..5ceff26a0 100644 --- a/lib/pure/parseutils.nim +++ b/lib/pure/parseutils.nim @@ -211,6 +211,7 @@ proc parseHex*[T: SomeInteger](s: string, number: var T, start = 0, proc parseIdent*(s: string, ident: var string, start = 0): int = ## Parses an identifier and stores it in ``ident``. Returns ## the number of the parsed characters or 0 in case of an error. + ## If error, the value of `ident` is not changed. runnableExamples: var res: string doAssert parseIdent("Hello World", res, 0) == 5 @@ -241,9 +242,19 @@ proc parseIdent*(s: string, start = 0): string = while i < s.len and s[i] in IdentChars: inc(i) result = substr(s, start, i-1) -proc parseChar*(s: string, ident: var char, start = 0): int = - ident = s[start] - result = 1 +proc parseChar*(s: string, c: var char, start = 0): int = + ## Parses a single character, stores it in `c` and returns 1. + ## In case of error (if start >= s.len) it returns 0 + ## and the value of `c` is unchanged. + runnableExamples: + var c: char + doAssert "nim".parseChar(c, 3) == 0 + doAssert c == '\0' + doAssert "nim".parseChar(c, 0) == 1 + doAssert c == 'n' + if start < s.len: + c = s[start] + result = 1 proc skipWhitespace*(s: string, start = 0): int {.inline.} = ## Skips the whitespace starting at ``s[start]``. Returns the number of From baf37b5cc71c14e1772e55e29147c9223b4521d7 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 7 Dec 2020 17:41:25 +0800 Subject: [PATCH 1139/3768] use funcs and fix links in strutils (#16277) * use funcs and inline in strutils * use funcs --- lib/pure/strutils.nim | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 8cbc947bf..94d63984c 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -121,8 +121,7 @@ const ## doAssert "01234".find(invalid) == -1 ## doAssert "01A34".find(invalid) == 2 -proc isAlphaAscii*(c: char): bool {.noSideEffect, - rtl, extern: "nsuIsAlphaAsciiChar".} = +func isAlphaAscii*(c: char): bool {.rtl, extern: "nsuIsAlphaAsciiChar".} = ## Checks whether or not character `c` is alphabetical. ## ## This checks a-z, A-Z ASCII characters only. @@ -133,8 +132,7 @@ proc isAlphaAscii*(c: char): bool {.noSideEffect, doAssert isAlphaAscii('8') == false return c in Letters -proc isAlphaNumeric*(c: char): bool {.noSideEffect, - rtl, extern: "nsuIsAlphaNumericChar".} = +func isAlphaNumeric*(c: char): bool {.rtl, extern: "nsuIsAlphaNumericChar".} = ## Checks whether or not `c` is alphanumeric. ## ## This checks a-z, A-Z, 0-9 ASCII characters only. @@ -144,8 +142,7 @@ proc isAlphaNumeric*(c: char): bool {.noSideEffect, doAssert isAlphaNumeric(' ') == false return c in Letters+Digits -proc isDigit*(c: char): bool {.noSideEffect, - rtl, extern: "nsuIsDigitChar".} = +func isDigit*(c: char): bool {.rtl, extern: "nsuIsDigitChar".} = ## Checks whether or not `c` is a number. ## ## This checks 0-9 ASCII characters only. @@ -154,8 +151,7 @@ proc isDigit*(c: char): bool {.noSideEffect, doAssert isDigit('8') == true return c in Digits -proc isSpaceAscii*(c: char): bool {.noSideEffect, - rtl, extern: "nsuIsSpaceAsciiChar".} = +func isSpaceAscii*(c: char): bool {.rtl, extern: "nsuIsSpaceAsciiChar".} = ## Checks whether or not `c` is a whitespace character. runnableExamples: doAssert isSpaceAscii('n') == false @@ -163,8 +159,7 @@ proc isSpaceAscii*(c: char): bool {.noSideEffect, doAssert isSpaceAscii('\t') == true return c in Whitespace -proc isLowerAscii*(c: char): bool {.noSideEffect, - rtl, extern: "nsuIsLowerAsciiChar".} = +func isLowerAscii*(c: char): bool {.rtl, extern: "nsuIsLowerAsciiChar".} = ## Checks whether or not `c` is a lower case character. ## ## This checks ASCII characters only. @@ -178,8 +173,7 @@ proc isLowerAscii*(c: char): bool {.noSideEffect, doAssert isLowerAscii('7') == false return c in {'a'..'z'} -proc isUpperAscii*(c: char): bool {.noSideEffect, - rtl, extern: "nsuIsUpperAsciiChar".} = +func isUpperAscii*(c: char): bool {.rtl, extern: "nsuIsUpperAsciiChar".} = ## Checks whether or not `c` is an upper case character. ## ## This checks ASCII characters only. @@ -203,7 +197,7 @@ proc toLowerAscii*(c: char): char {.noSideEffect, ## character. ## ## See also: - ## * `isLowerAscii proc<#isLowerAscii,char>`_ + ## * `isLowerAscii func<#isLowerAscii,char>`_ ## * `toLowerAscii proc<#toLowerAscii,string>`_ for converting a string runnableExamples: doAssert toLowerAscii('A') == 'a' @@ -241,7 +235,7 @@ proc toUpperAscii*(c: char): char {.noSideEffect, ## character. ## ## See also: - ## * `isLowerAscii proc<#isLowerAscii,char>`_ + ## * `isUpperAscii func<#isUpperAscii,char>`_ ## * `toUpperAscii proc<#toUpperAscii,string>`_ for converting a string ## * `capitalizeAscii proc<#capitalizeAscii,string>`_ runnableExamples: From 94bb816de35953674a3895999838fc67169f8310 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 7 Dec 2020 10:50:30 +0100 Subject: [PATCH 1140/3768] OSX: support for M1 [backport:1.0] (#16279) --- compiler/installer.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/installer.ini b/compiler/installer.ini index ecd1f7475..2f11d0df9 100644 --- a/compiler/installer.ini +++ b/compiler/installer.ini @@ -7,7 +7,7 @@ Version: "$version" Platforms: """ windows: i386;amd64 linux: i386;hppa;ia64;alpha;amd64;powerpc64;arm;sparc;sparc64;m68k;mips;mipsel;mips64;mips64el;powerpc;powerpc64el;arm64;riscv32;riscv64 - macosx: i386;amd64;powerpc64 + macosx: i386;amd64;powerpc64;arm64 solaris: i386;amd64;sparc;sparc64 freebsd: i386;amd64;powerpc64;arm;arm64;riscv64;sparc64;mips;mipsel;mips64;mips64el;powerpc;powerpc64el netbsd: i386;amd64 From 71e2a9e569ab3380fd057e4fc16e6d4ccd66f1f4 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 7 Dec 2020 23:38:13 +0800 Subject: [PATCH 1141/3768] use generics in random (#16283) * use generics in random * fix --- lib/pure/random.nim | 48 ++++++++++++++------------------------------- 1 file changed, 15 insertions(+), 33 deletions(-) diff --git a/lib/pure/random.nim b/lib/pure/random.nim index 683140cd8..0ee75e027 100644 --- a/lib/pure/random.nim +++ b/lib/pure/random.nim @@ -217,8 +217,7 @@ proc rand*(r: var Rand; max: Natural): int {.benign.} = ## Returns a random integer in the range `0..max` using the given state. ## ## See also: - ## * `rand proc<#rand,int>`_ that returns an integer using the default - ## random number generator + ## * `rand proc<#rand,T>`_ `T` are integers, floats, and enums without holes. ## * `rand proc<#rand,Rand,range[]>`_ that returns a float ## * `rand proc<#rand,Rand,HSlice[T,T]>`_ that accepts a slice ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type @@ -233,28 +232,6 @@ proc rand*(r: var Rand; max: Natural): int {.benign.} = if x <= randMax - (randMax mod Ui(max)): return int(x mod (uint64(max)+1u64)) -proc rand*(max: int): int {.benign.} = - ## Returns a random integer in the range `0..max`. - ## - ## If `randomize<#randomize>`_ has not been called, the sequence of random - ## numbers returned from this proc will always be the same. - ## - ## This proc uses the default random number generator. Thus, it is **not** - ## thread-safe. - ## - ## See also: - ## * `rand proc<#rand,Rand,Natural>`_ that returns an integer using a - ## provided state - ## * `rand proc<#rand,float>`_ that returns a float - ## * `rand proc<#rand,HSlice[T,T]>`_ that accepts a slice - ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type - runnableExamples: - randomize(123) - doAssert rand(100) == 0 - doAssert rand(100) == 96 - doAssert rand(100) == 66 - rand(state, max) - proc rand*(r: var Rand; max: range[0.0 .. high(float)]): float {.benign.} = ## Returns a random floating point number in the range `0.0..max` ## using the given state. @@ -276,8 +253,10 @@ proc rand*(r: var Rand; max: range[0.0 .. high(float)]): float {.benign.} = let u = (0x3FFu64 shl 52u64) or (x shr 12u64) result = (cast[float](u) - 1.0) * max -proc rand*(max: float): float {.benign.} = - ## Returns a random floating point number in the range `0.0..max`. +proc rand*[T: Ordinal or SomeFloat](max: T): T {.benign.} = + ## Returns a random floating point number in the range `T(0) .. max`. + ## + ## Allowed types for `T` are integers, floats, and enums without holes. ## ## If `randomize<#randomize>`_ has not been called, the sequence of random ## numbers returned from this proc will always be the same. @@ -286,16 +265,21 @@ proc rand*(max: float): float {.benign.} = ## thread-safe. ## ## See also: + ## * `rand proc<#rand,Rand,Natural>`_ that returns an integer using a + ## provided state ## * `rand proc<#rand,Rand,range[]>`_ that returns a float using a ## provided state - ## * `rand proc<#rand,int>`_ that returns an integer ## * `rand proc<#rand,HSlice[T,T]>`_ that accepts a slice ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type runnableExamples: - randomize(234) + randomize(123) + doAssert rand(100) == 0 + doAssert rand(100) == 96 + doAssert rand(100) == 66 + let f = rand(1.0) ## f = 8.717181376738381e-07 - rand(state, max) + result = T(rand(state, max)) proc rand*[T: Ordinal or SomeFloat](r: var Rand; x: HSlice[T, T]): T = ## For a slice `a..b`, returns a value in the range `a..b` using the given @@ -335,8 +319,7 @@ proc rand*[T: Ordinal or SomeFloat](x: HSlice[T, T]): T = ## See also: ## * `rand proc<#rand,Rand,HSlice[T,T]>`_ that accepts a slice and uses ## a provided state - ## * `rand proc<#rand,int>`_ that returns an integer - ## * `rand proc<#rand,float>`_ that returns a floating point number + ## * `rand proc<#rand,T>`_ `T` are integers, floats, and enums without holes. ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type runnableExamples: randomize(345) @@ -355,8 +338,7 @@ proc rand*[T: SomeInteger](t: typedesc[T]): T = ## thread-safe. ## ## See also: - ## * `rand proc<#rand,int>`_ that returns an integer - ## * `rand proc<#rand,float>`_ that returns a floating point number + ## * `rand proc<#rand,T>`_ `T` are integers, floats, and enums without holes. ## * `rand proc<#rand,HSlice[T,T]>`_ that accepts a slice runnableExamples: randomize(567) From 84fea7c1445475aba51d889668f250b0261443e4 Mon Sep 17 00:00:00 2001 From: ee7 <45465154+ee7@users.noreply.github.com> Date: Mon, 7 Dec 2020 17:07:12 +0100 Subject: [PATCH 1142/3768] strutils.nim: Use `func` everywhere (#16281) * strutils.nim: procs with {.noSideEffect.} -> func * strutils.nim: procs without {.noSideEffect.} -> func * strutils.nim: proc -> func for links * strutils.nim: proc -> func in doc comments * test: add strutils to strictFuncs test * test: proc -> func in errmsg test --- lib/pure/strutils.nim | 619 ++++++++++----------- tests/effects/tstrict_funcs.nim | 4 +- tests/errmsgs/tunknown_named_parameter.nim | 6 +- 3 files changed, 299 insertions(+), 330 deletions(-) diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 94d63984c..c937cd259 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -113,7 +113,7 @@ const ## A set with all the possible characters. ## ## Not very useful by its own, you can use it to create *inverted* sets to - ## make the `find proc<#find,string,set[char],Natural,int>`_ + ## make the `find func<#find,string,set[char],Natural,int>`_ ## find **invalid** characters in strings. Example: ## ## .. code-block:: nim @@ -166,7 +166,7 @@ func isLowerAscii*(c: char): bool {.rtl, extern: "nsuIsLowerAsciiChar".} = ## Use `Unicode module`_ for UTF-8 support. ## ## See also: - ## * `toLowerAscii proc<#toLowerAscii,char>`_ + ## * `toLowerAscii func<#toLowerAscii,char>`_ runnableExamples: doAssert isLowerAscii('e') == true doAssert isLowerAscii('E') == false @@ -180,7 +180,7 @@ func isUpperAscii*(c: char): bool {.rtl, extern: "nsuIsUpperAsciiChar".} = ## Use `Unicode module`_ for UTF-8 support. ## ## See also: - ## * `toUpperAscii proc<#toUpperAscii,char>`_ + ## * `toUpperAscii func<#toUpperAscii,char>`_ runnableExamples: doAssert isUpperAscii('e') == false doAssert isUpperAscii('E') == true @@ -188,8 +188,7 @@ func isUpperAscii*(c: char): bool {.rtl, extern: "nsuIsUpperAsciiChar".} = return c in {'A'..'Z'} -proc toLowerAscii*(c: char): char {.noSideEffect, - rtl, extern: "nsuToLowerAsciiChar".} = +func toLowerAscii*(c: char): char {.rtl, extern: "nsuToLowerAsciiChar".} = ## Returns the lower case version of character ``c``. ## ## This works only for the letters ``A-Z``. See `unicode.toLower @@ -198,7 +197,7 @@ proc toLowerAscii*(c: char): char {.noSideEffect, ## ## See also: ## * `isLowerAscii func<#isLowerAscii,char>`_ - ## * `toLowerAscii proc<#toLowerAscii,string>`_ for converting a string + ## * `toLowerAscii func<#toLowerAscii,string>`_ for converting a string runnableExamples: doAssert toLowerAscii('A') == 'a' doAssert toLowerAscii('e') == 'e' @@ -212,8 +211,7 @@ template toImpl(call) = for i in 0..len(s) - 1: result[i] = call(s[i]) -proc toLowerAscii*(s: string): string {.noSideEffect, - rtl, extern: "nsuToLowerAsciiStr".} = +func toLowerAscii*(s: string): string {.rtl, extern: "nsuToLowerAsciiStr".} = ## Converts string `s` into lower case. ## ## This works only for the letters ``A-Z``. See `unicode.toLower @@ -221,13 +219,12 @@ proc toLowerAscii*(s: string): string {.noSideEffect, ## character. ## ## See also: - ## * `normalize proc<#normalize,string>`_ + ## * `normalize func<#normalize,string>`_ runnableExamples: doAssert toLowerAscii("FooBar!") == "foobar!" toImpl toLowerAscii -proc toUpperAscii*(c: char): char {.noSideEffect, - rtl, extern: "nsuToUpperAsciiChar".} = +func toUpperAscii*(c: char): char {.rtl, extern: "nsuToUpperAsciiChar".} = ## Converts character `c` into upper case. ## ## This works only for the letters ``A-Z``. See `unicode.toUpper @@ -236,8 +233,8 @@ proc toUpperAscii*(c: char): char {.noSideEffect, ## ## See also: ## * `isUpperAscii func<#isUpperAscii,char>`_ - ## * `toUpperAscii proc<#toUpperAscii,string>`_ for converting a string - ## * `capitalizeAscii proc<#capitalizeAscii,string>`_ + ## * `toUpperAscii func<#toUpperAscii,string>`_ for converting a string + ## * `capitalizeAscii func<#capitalizeAscii,string>`_ runnableExamples: doAssert toUpperAscii('a') == 'A' doAssert toUpperAscii('E') == 'E' @@ -246,8 +243,7 @@ proc toUpperAscii*(c: char): char {.noSideEffect, else: result = c -proc toUpperAscii*(s: string): string {.noSideEffect, - rtl, extern: "nsuToUpperAsciiStr".} = +func toUpperAscii*(s: string): string {.rtl, extern: "nsuToUpperAsciiStr".} = ## Converts string `s` into upper case. ## ## This works only for the letters ``A-Z``. See `unicode.toUpper @@ -255,27 +251,26 @@ proc toUpperAscii*(s: string): string {.noSideEffect, ## character. ## ## See also: - ## * `capitalizeAscii proc<#capitalizeAscii,string>`_ + ## * `capitalizeAscii func<#capitalizeAscii,string>`_ runnableExamples: doAssert toUpperAscii("FooBar!") == "FOOBAR!" toImpl toUpperAscii -proc capitalizeAscii*(s: string): string {.noSideEffect, - rtl, extern: "nsuCapitalizeAscii".} = +func capitalizeAscii*(s: string): string {.rtl, extern: "nsuCapitalizeAscii".} = ## Converts the first character of string `s` into upper case. ## ## This works only for the letters ``A-Z``. ## Use `Unicode module`_ for UTF-8 support. ## ## See also: - ## * `toUpperAscii proc<#toUpperAscii,char>`_ + ## * `toUpperAscii func<#toUpperAscii,char>`_ runnableExamples: doAssert capitalizeAscii("foo") == "Foo" doAssert capitalizeAscii("-bar") == "-bar" if s.len == 0: result = "" else: result = toUpperAscii(s[0]) & substr(s, 1) -proc nimIdentNormalize*(s: string): string = +func nimIdentNormalize*(s: string): string = ## Normalizes the string `s` as a Nim identifier. ## ## That means to convert to lower case and remove any '_' on all characters @@ -295,15 +290,14 @@ proc nimIdentNormalize*(s: string): string = inc j if j != s.len: setLen(result, j) -proc normalize*(s: string): string {.noSideEffect, - rtl, extern: "nsuNormalize".} = +func normalize*(s: string): string {.rtl, extern: "nsuNormalize".} = ## Normalizes the string `s`. ## ## That means to convert it to lower case and remove any '_'. This ## should NOT be used to normalize Nim identifier names. ## ## See also: - ## * `toLowerAscii proc<#toLowerAscii,string>`_ + ## * `toLowerAscii func<#toLowerAscii,string>`_ runnableExamples: doAssert normalize("Foo_bar") == "foobar" doAssert normalize("Foo Bar") == "foo bar" @@ -318,8 +312,7 @@ proc normalize*(s: string): string {.noSideEffect, inc j if j != s.len: setLen(result, j) -proc cmpIgnoreCase*(a, b: string): int {.noSideEffect, - rtl, extern: "nsuCmpIgnoreCase".} = +func cmpIgnoreCase*(a, b: string): int {.rtl, extern: "nsuCmpIgnoreCase".} = ## Compares two strings in a case insensitive manner. Returns: ## ## | 0 if a == b @@ -340,8 +333,7 @@ proc cmpIgnoreCase*(a, b: string): int {.noSideEffect, {.push checks: off, line_trace: off.} # this is a hot-spot in the compiler! # thus we compile without checks here -proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect, - rtl, extern: "nsuCmpIgnoreStyle".} = +func cmpIgnoreStyle*(a, b: string): int {.rtl, extern: "nsuCmpIgnoreStyle".} = ## Semantically the same as ``cmp(normalize(a), normalize(b))``. It ## is just optimized to not allocate temporary strings. This should ## NOT be used to compare Nim identifier names. @@ -378,7 +370,7 @@ proc cmpIgnoreStyle*(a, b: string): int {.noSideEffect, # --------- Private templates for different split separators ----------- -proc substrEq(s: string, pos: int, substr: string): bool = +func substrEq(s: string, pos: int, substr: string): bool = var i = 0 var length = substr.len while i < length and pos+i < s.len and s[pos+i] == substr[i]: @@ -456,7 +448,7 @@ iterator split*(s: string, sep: char, maxsplit: int = -1): string = ## * `rsplit iterator<#rsplit.i,string,char,int>`_ ## * `splitLines iterator<#splitLines.i,string>`_ ## * `splitWhitespace iterator<#splitWhitespace.i,string,int>`_ - ## * `split proc<#split,string,char,int>`_ + ## * `split func<#split,string,char,int>`_ splitCommon(s, sep, maxsplit, 1) iterator split*(s: string, seps: set[char] = Whitespace, @@ -505,7 +497,7 @@ iterator split*(s: string, seps: set[char] = Whitespace, ## * `rsplit iterator<#rsplit.i,string,set[char],int>`_ ## * `splitLines iterator<#splitLines.i,string>`_ ## * `splitWhitespace iterator<#splitWhitespace.i,string,int>`_ - ## * `split proc<#split,string,set[char],int>`_ + ## * `split func<#split,string,set[char],int>`_ splitCommon(s, seps, maxsplit, 1) iterator split*(s: string, sep: string, maxsplit: int = -1): string = @@ -529,7 +521,7 @@ iterator split*(s: string, sep: string, maxsplit: int = -1): string = ## * `rsplit iterator<#rsplit.i,string,string,int,bool>`_ ## * `splitLines iterator<#splitLines.i,string>`_ ## * `splitWhitespace iterator<#splitWhitespace.i,string,int>`_ - ## * `split proc<#split,string,string,int>`_ + ## * `split func<#split,string,string,int>`_ splitCommon(s, sep, maxsplit, sep.len) @@ -579,7 +571,7 @@ iterator rsplit*(s: string, sep: char, ## * `split iterator<#split.i,string,char,int>`_ ## * `splitLines iterator<#splitLines.i,string>`_ ## * `splitWhitespace iterator<#splitWhitespace.i,string,int>`_ - ## * `rsplit proc<#rsplit,string,char,int>`_ + ## * `rsplit func<#rsplit,string,char,int>`_ rsplitCommon(s, sep, maxsplit, 1) iterator rsplit*(s: string, seps: set[char] = Whitespace, @@ -604,7 +596,7 @@ iterator rsplit*(s: string, seps: set[char] = Whitespace, ## * `split iterator<#split.i,string,set[char],int>`_ ## * `splitLines iterator<#splitLines.i,string>`_ ## * `splitWhitespace iterator<#splitWhitespace.i,string,int>`_ - ## * `rsplit proc<#rsplit,string,set[char],int>`_ + ## * `rsplit func<#rsplit,string,set[char],int>`_ rsplitCommon(s, seps, maxsplit, 1) iterator rsplit*(s: string, sep: string, maxsplit: int = -1, @@ -629,7 +621,7 @@ iterator rsplit*(s: string, sep: string, maxsplit: int = -1, ## * `split iterator<#split.i,string,string,int>`_ ## * `splitLines iterator<#splitLines.i,string>`_ ## * `splitWhitespace iterator<#splitWhitespace.i,string,int>`_ - ## * `rsplit proc<#rsplit,string,string,int>`_ + ## * `rsplit func<#rsplit,string,string,int>`_ rsplitCommon(s, sep, maxsplit, sep.len) iterator splitLines*(s: string, keepEol = false): string = @@ -659,7 +651,7 @@ iterator splitLines*(s: string, keepEol = false): string = ## ## See also: ## * `splitWhitespace iterator<#splitWhitespace.i,string,int>`_ - ## * `splitLines proc<#splitLines,string>`_ + ## * `splitLines func<#splitLines,string>`_ var first = 0 var last = 0 var eolpos = 0 @@ -716,43 +708,43 @@ iterator splitWhitespace*(s: string, maxsplit: int = -1): string = ## ## See also: ## * `splitLines iterator<#splitLines.i,string>`_ - ## * `splitWhitespace proc<#splitWhitespace,string,int>`_ + ## * `splitWhitespace func<#splitWhitespace,string,int>`_ oldSplit(s, Whitespace, maxsplit) -proc split*(s: string, sep: char, maxsplit: int = -1): seq[string] {.noSideEffect, - rtl, extern: "nsuSplitChar".} = +func split*(s: string, sep: char, maxsplit: int = -1): seq[string] {.rtl, + extern: "nsuSplitChar".} = ## The same as the `split iterator <#split.i,string,char,int>`_ (see its - ## documentation), but is a proc that returns a sequence of substrings. + ## documentation), but is a func that returns a sequence of substrings. ## ## See also: ## * `split iterator <#split.i,string,char,int>`_ - ## * `rsplit proc<#rsplit,string,char,int>`_ - ## * `splitLines proc<#splitLines,string>`_ - ## * `splitWhitespace proc<#splitWhitespace,string,int>`_ + ## * `rsplit func<#rsplit,string,char,int>`_ + ## * `splitLines func<#splitLines,string>`_ + ## * `splitWhitespace func<#splitWhitespace,string,int>`_ runnableExamples: doAssert "a,b,c".split(',') == @["a", "b", "c"] doAssert "".split(' ') == @[""] accResult(split(s, sep, maxsplit)) -proc split*(s: string, seps: set[char] = Whitespace, maxsplit: int = -1): seq[string] {. - noSideEffect, rtl, extern: "nsuSplitCharSet".} = +func split*(s: string, seps: set[char] = Whitespace, maxsplit: int = -1): seq[ + string] {.rtl, extern: "nsuSplitCharSet".} = ## The same as the `split iterator <#split.i,string,set[char],int>`_ (see its - ## documentation), but is a proc that returns a sequence of substrings. + ## documentation), but is a func that returns a sequence of substrings. ## ## See also: ## * `split iterator <#split.i,string,set[char],int>`_ - ## * `rsplit proc<#rsplit,string,set[char],int>`_ - ## * `splitLines proc<#splitLines,string>`_ - ## * `splitWhitespace proc<#splitWhitespace,string,int>`_ + ## * `rsplit func<#rsplit,string,set[char],int>`_ + ## * `splitLines func<#splitLines,string>`_ + ## * `splitWhitespace func<#splitWhitespace,string,int>`_ runnableExamples: doAssert "a,b;c".split({',', ';'}) == @["a", "b", "c"] doAssert "".split({' '}) == @[""] accResult(split(s, seps, maxsplit)) -proc split*(s: string, sep: string, maxsplit: int = -1): seq[string] {.noSideEffect, - rtl, extern: "nsuSplitString".} = +func split*(s: string, sep: string, maxsplit: int = -1): seq[string] {.rtl, + extern: "nsuSplitString".} = ## Splits the string `s` into substrings using a string separator. ## ## Substrings are separated by the string `sep`. This is a wrapper around the @@ -760,9 +752,9 @@ proc split*(s: string, sep: string, maxsplit: int = -1): seq[string] {.noSideEff ## ## See also: ## * `split iterator <#split.i,string,string,int>`_ - ## * `rsplit proc<#rsplit,string,string,int>`_ - ## * `splitLines proc<#splitLines,string>`_ - ## * `splitWhitespace proc<#splitWhitespace,string,int>`_ + ## * `rsplit func<#rsplit,string,string,int>`_ + ## * `splitLines func<#splitLines,string>`_ + ## * `splitWhitespace func<#splitWhitespace,string,int>`_ runnableExamples: doAssert "a,b,c".split(",") == @["a", "b", "c"] doAssert "a man a plan a canal panama".split("a ") == @["", "man ", "plan ", "canal panama"] @@ -774,9 +766,9 @@ proc split*(s: string, sep: string, maxsplit: int = -1): seq[string] {.noSideEff accResult(split(s, sep, maxsplit)) -proc rsplit*(s: string, sep: char, maxsplit: int = -1): seq[string] - {.noSideEffect, rtl, extern: "nsuRSplitChar".} = - ## The same as the `rsplit iterator <#rsplit.i,string,char,int>`_, but is a proc +func rsplit*(s: string, sep: char, maxsplit: int = -1): seq[string] {.rtl, + extern: "nsuRSplitChar".} = + ## The same as the `rsplit iterator <#rsplit.i,string,char,int>`_, but is a func ## that returns a sequence of substrings. ## ## A possible common use case for `rsplit` is path manipulation, @@ -795,17 +787,17 @@ proc rsplit*(s: string, sep: char, maxsplit: int = -1): seq[string] ## ## See also: ## * `rsplit iterator <#rsplit.i,string,char,int>`_ - ## * `split proc<#split,string,char,int>`_ - ## * `splitLines proc<#splitLines,string>`_ - ## * `splitWhitespace proc<#splitWhitespace,string,int>`_ + ## * `split func<#split,string,char,int>`_ + ## * `splitLines func<#splitLines,string>`_ + ## * `splitWhitespace func<#splitWhitespace,string,int>`_ accResult(rsplit(s, sep, maxsplit)) result.reverse() -proc rsplit*(s: string, seps: set[char] = Whitespace, +func rsplit*(s: string, seps: set[char] = Whitespace, maxsplit: int = -1): seq[string] - {.noSideEffect, rtl, extern: "nsuRSplitCharSet".} = + {.rtl, extern: "nsuRSplitCharSet".} = ## The same as the `rsplit iterator <#rsplit.i,string,set[char],int>`_, but is a - ## proc that returns a sequence of substrings. + ## func that returns a sequence of substrings. ## ## A possible common use case for `rsplit` is path manipulation, ## particularly on systems that don't use a common delimiter. @@ -823,15 +815,15 @@ proc rsplit*(s: string, seps: set[char] = Whitespace, ## ## See also: ## * `rsplit iterator <#rsplit.i,string,set[char],int>`_ - ## * `split proc<#split,string,set[char],int>`_ - ## * `splitLines proc<#splitLines,string>`_ - ## * `splitWhitespace proc<#splitWhitespace,string,int>`_ + ## * `split func<#split,string,set[char],int>`_ + ## * `splitLines func<#splitLines,string>`_ + ## * `splitWhitespace func<#splitWhitespace,string,int>`_ accResult(rsplit(s, seps, maxsplit)) result.reverse() -proc rsplit*(s: string, sep: string, maxsplit: int = -1): seq[string] - {.noSideEffect, rtl, extern: "nsuRSplitString".} = - ## The same as the `rsplit iterator <#rsplit.i,string,string,int,bool>`_, but is a proc +func rsplit*(s: string, sep: string, maxsplit: int = -1): seq[string] {.rtl, + extern: "nsuRSplitString".} = + ## The same as the `rsplit iterator <#rsplit.i,string,string,int,bool>`_, but is a func ## that returns a sequence of substrings. ## ## A possible common use case for `rsplit` is path manipulation, @@ -850,9 +842,9 @@ proc rsplit*(s: string, sep: string, maxsplit: int = -1): seq[string] ## ## See also: ## * `rsplit iterator <#rsplit.i,string,string,int,bool>`_ - ## * `split proc<#split,string,string,int>`_ - ## * `splitLines proc<#splitLines,string>`_ - ## * `splitWhitespace proc<#splitWhitespace,string,int>`_ + ## * `split func<#split,string,string,int>`_ + ## * `splitLines func<#splitLines,string>`_ + ## * `splitWhitespace func<#splitWhitespace,string,int>`_ runnableExamples: doAssert "a largely spaced sentence".rsplit(" ", maxsplit = 1) == @[ "a largely spaced", "sentence"] @@ -865,29 +857,28 @@ proc rsplit*(s: string, sep: string, maxsplit: int = -1): seq[string] accResult(rsplit(s, sep, maxsplit)) result.reverse() -proc splitLines*(s: string, keepEol = false): seq[string] {.noSideEffect, - rtl, extern: "nsuSplitLines".} = +func splitLines*(s: string, keepEol = false): seq[string] {.rtl, + extern: "nsuSplitLines".} = ## The same as the `splitLines iterator<#splitLines.i,string>`_ (see its - ## documentation), but is a proc that returns a sequence of substrings. + ## documentation), but is a func that returns a sequence of substrings. ## ## See also: ## * `splitLines iterator<#splitLines.i,string>`_ - ## * `splitWhitespace proc<#splitWhitespace,string,int>`_ - ## * `countLines proc<#countLines,string>`_ + ## * `splitWhitespace func<#splitWhitespace,string,int>`_ + ## * `countLines func<#countLines,string>`_ accResult(splitLines(s, keepEol = keepEol)) -proc splitWhitespace*(s: string, maxsplit: int = -1): seq[string] {.noSideEffect, - rtl, extern: "nsuSplitWhitespace".} = +func splitWhitespace*(s: string, maxsplit: int = -1): seq[string] {.rtl, + extern: "nsuSplitWhitespace".} = ## The same as the `splitWhitespace iterator <#splitWhitespace.i,string,int>`_ - ## (see its documentation), but is a proc that returns a sequence of substrings. + ## (see its documentation), but is a func that returns a sequence of substrings. ## ## See also: ## * `splitWhitespace iterator <#splitWhitespace.i,string,int>`_ - ## * `splitLines proc<#splitLines,string>`_ + ## * `splitLines func<#splitLines,string>`_ accResult(splitWhitespace(s, maxsplit)) -proc toBin*(x: BiggestInt, len: Positive): string {.noSideEffect, - rtl, extern: "nsuToBin".} = +func toBin*(x: BiggestInt, len: Positive): string {.rtl, extern: "nsuToBin".} = ## Converts `x` into its binary representation. ## ## The resulting string is always `len` characters long. No leading ``0b`` @@ -909,14 +900,13 @@ proc toBin*(x: BiggestInt, len: Positive): string {.noSideEffect, inc shift mask = mask shl BiggestUInt(1) -proc toOct*(x: BiggestInt, len: Positive): string {.noSideEffect, - rtl, extern: "nsuToOct".} = +func toOct*(x: BiggestInt, len: Positive): string {.rtl, extern: "nsuToOct".} = ## Converts `x` into its octal representation. ## ## The resulting string is always `len` characters long. No leading ``0o`` ## prefix is generated. ## - ## Do not confuse it with `toOctal proc<#toOctal,char>`_. + ## Do not confuse it with `toOctal func<#toOctal,char>`_. runnableExamples: let a = 62 @@ -934,7 +924,7 @@ proc toOct*(x: BiggestInt, len: Positive): string {.noSideEffect, inc shift, 3 mask = mask shl BiggestUInt(3) -proc toHexImpl(x: BiggestUInt, len: Positive, handleNegative: bool): string {.noSideEffect.} = +func toHexImpl(x: BiggestUInt, len: Positive, handleNegative: bool): string = const HexChars = "0123456789ABCDEF" var n = x @@ -945,7 +935,7 @@ proc toHexImpl(x: BiggestUInt, len: Positive, handleNegative: bool): string {.no # handle negative overflow if n == 0 and handleNegative: n = not(BiggestUInt 0) -proc toHex*[T: SomeInteger](x: T, len: Positive): string {.noSideEffect.} = +func toHex*[T: SomeInteger](x: T, len: Positive): string = ## Converts `x` to its hexadecimal representation. ## ## The resulting string will be exactly `len` characters long. No prefix like @@ -961,21 +951,21 @@ proc toHex*[T: SomeInteger](x: T, len: Positive): string {.noSideEffect.} = doAssert toHex(-8, 6) == "FFFFF8" toHexImpl(cast[BiggestUInt](x), len, x < 0) -proc toHex*[T: SomeInteger](x: T): string {.noSideEffect.} = +func toHex*[T: SomeInteger](x: T): string = ## Shortcut for ``toHex(x, T.sizeof * 2)`` runnableExamples: doAssert toHex(1984'i64) == "00000000000007C0" doAssert toHex(1984'i16) == "07C0" toHexImpl(cast[BiggestUInt](x), 2*sizeof(T), x < 0) -proc toHex*(s: string): string {.noSideEffect, rtl.} = +func toHex*(s: string): string {.rtl.} = ## Converts a bytes string to its hexadecimal representation. ## ## The output is twice the input long. No prefix like ## ``0x`` is generated. ## ## See also: - ## * `parseHexStr proc<#parseHexStr,string>`_ for the reverse operation + ## * `parseHexStr func<#parseHexStr,string>`_ for the reverse operation runnableExamples: let a = "1" @@ -993,13 +983,13 @@ proc toHex*(s: string): string {.noSideEffect, rtl.} = n = n shr 4 result[pos * 2] = HexChars[n] -proc toOctal*(c: char): string {.noSideEffect, rtl, extern: "nsuToOctal".} = +func toOctal*(c: char): string {.rtl, extern: "nsuToOctal".} = ## Converts a character `c` to its octal representation. ## ## The resulting string may not have a leading zero. Its length is always ## exactly 3. ## - ## Do not confuse it with `toOct proc<#toOct,BiggestInt,Positive>`_. + ## Do not confuse it with `toOct func<#toOct,BiggestInt,Positive>`_. runnableExamples: doAssert toOctal('1') == "061" doAssert toOctal('A') == "101" @@ -1012,7 +1002,7 @@ proc toOctal*(c: char): string {.noSideEffect, rtl, extern: "nsuToOctal".} = result[i] = chr(val mod 8 + ord('0')) val = val div 8 -proc fromBin*[T: SomeInteger](s: string): T = +func fromBin*[T: SomeInteger](s: string): T = ## Parses a binary integer value from a string `s`. ## ## If `s` is not a valid binary integer, `ValueError` is raised. `s` can have @@ -1035,7 +1025,7 @@ proc fromBin*[T: SomeInteger](s: string): T = if p != s.len or p == 0: raise newException(ValueError, "invalid binary integer: " & s) -proc fromOct*[T: SomeInteger](s: string): T = +func fromOct*[T: SomeInteger](s: string): T = ## Parses an octal integer value from a string `s`. ## ## If `s` is not a valid octal integer, `ValueError` is raised. `s` can have @@ -1058,7 +1048,7 @@ proc fromOct*[T: SomeInteger](s: string): T = if p != s.len or p == 0: raise newException(ValueError, "invalid oct integer: " & s) -proc fromHex*[T: SomeInteger](s: string): T = +func fromHex*[T: SomeInteger](s: string): T = ## Parses a hex integer value from a string `s`. ## ## If `s` is not a valid hex integer, `ValueError` is raised. `s` can have @@ -1081,8 +1071,8 @@ proc fromHex*[T: SomeInteger](s: string): T = if p != s.len or p == 0: raise newException(ValueError, "invalid hex integer: " & s) -proc intToStr*(x: int, minchars: Positive = 1): string {.noSideEffect, - rtl, extern: "nsuIntToStr".} = +func intToStr*(x: int, minchars: Positive = 1): string {.rtl, + extern: "nsuIntToStr".} = ## Converts `x` to its decimal representation. ## ## The resulting string will be minimally `minchars` characters long. This is @@ -1096,8 +1086,7 @@ proc intToStr*(x: int, minchars: Positive = 1): string {.noSideEffect, if x < 0: result = '-' & result -proc parseInt*(s: string): int {.noSideEffect, - rtl, extern: "nsuParseInt".} = +func parseInt*(s: string): int {.rtl, extern: "nsuParseInt".} = ## Parses a decimal integer value contained in `s`. ## ## If `s` is not a valid integer, `ValueError` is raised. @@ -1108,8 +1097,8 @@ proc parseInt*(s: string): int {.noSideEffect, if L != s.len or L == 0: raise newException(ValueError, "invalid integer: " & s) -proc parseBiggestInt*(s: string): BiggestInt {.noSideEffect, - rtl, extern: "nsuParseBiggestInt".} = +func parseBiggestInt*(s: string): BiggestInt {.rtl, + extern: "nsuParseBiggestInt".} = ## Parses a decimal integer value contained in `s`. ## ## If `s` is not a valid integer, `ValueError` is raised. @@ -1118,8 +1107,7 @@ proc parseBiggestInt*(s: string): BiggestInt {.noSideEffect, if L != s.len or L == 0: raise newException(ValueError, "invalid integer: " & s) -proc parseUInt*(s: string): uint {.noSideEffect, - rtl, extern: "nsuParseUInt".} = +func parseUInt*(s: string): uint {.rtl, extern: "nsuParseUInt".} = ## Parses a decimal unsigned integer value contained in `s`. ## ## If `s` is not a valid integer, `ValueError` is raised. @@ -1128,8 +1116,8 @@ proc parseUInt*(s: string): uint {.noSideEffect, if L != s.len or L == 0: raise newException(ValueError, "invalid unsigned integer: " & s) -proc parseBiggestUInt*(s: string): BiggestUInt {.noSideEffect, - rtl, extern: "nsuParseBiggestUInt".} = +func parseBiggestUInt*(s: string): BiggestUInt {.rtl, + extern: "nsuParseBiggestUInt".} = ## Parses a decimal unsigned integer value contained in `s`. ## ## If `s` is not a valid integer, `ValueError` is raised. @@ -1138,8 +1126,7 @@ proc parseBiggestUInt*(s: string): BiggestUInt {.noSideEffect, if L != s.len or L == 0: raise newException(ValueError, "invalid unsigned integer: " & s) -proc parseFloat*(s: string): float {.noSideEffect, - rtl, extern: "nsuParseFloat".} = +func parseFloat*(s: string): float {.rtl, extern: "nsuParseFloat".} = ## Parses a decimal floating point value contained in `s`. ## ## If `s` is not a valid floating point number, `ValueError` is raised. @@ -1152,8 +1139,7 @@ proc parseFloat*(s: string): float {.noSideEffect, if L != s.len or L == 0: raise newException(ValueError, "invalid float: " & s) -proc parseBinInt*(s: string): int {.noSideEffect, - rtl, extern: "nsuParseBinInt".} = +func parseBinInt*(s: string): int {.rtl, extern: "nsuParseBinInt".} = ## Parses a binary integer value contained in `s`. ## ## If `s` is not a valid binary integer, `ValueError` is raised. `s` can have @@ -1171,8 +1157,7 @@ proc parseBinInt*(s: string): int {.noSideEffect, if L != s.len or L == 0: raise newException(ValueError, "invalid binary integer: " & s) -proc parseOctInt*(s: string): int {.noSideEffect, - rtl, extern: "nsuParseOctInt".} = +func parseOctInt*(s: string): int {.rtl, extern: "nsuParseOctInt".} = ## Parses an octal integer value contained in `s`. ## ## If `s` is not a valid oct integer, `ValueError` is raised. `s` can have one @@ -1183,8 +1168,7 @@ proc parseOctInt*(s: string): int {.noSideEffect, if L != s.len or L == 0: raise newException(ValueError, "invalid oct integer: " & s) -proc parseHexInt*(s: string): int {.noSideEffect, - rtl, extern: "nsuParseHexInt".} = +func parseHexInt*(s: string): int {.rtl, extern: "nsuParseHexInt".} = ## Parses a hexadecimal integer value contained in `s`. ## ## If `s` is not a valid hex integer, `ValueError` is raised. `s` can have one @@ -1195,7 +1179,7 @@ proc parseHexInt*(s: string): int {.noSideEffect, if L != s.len or L == 0: raise newException(ValueError, "invalid hex integer: " & s) -proc generateHexCharToValueMap(): string = +func generateHexCharToValueMap(): string = ## Generate a string to map a hex digit to uint value result = "" for inp in 0..255: @@ -1210,15 +1194,14 @@ proc generateHexCharToValueMap(): string = const hexCharToValueMap = generateHexCharToValueMap() -proc parseHexStr*(s: string): string {.noSideEffect, - rtl, extern: "nsuParseHexStr".} = +func parseHexStr*(s: string): string {.rtl, extern: "nsuParseHexStr".} = ## Convert hex-encoded string to byte string, e.g.: ## ## Raises ``ValueError`` for an invalid hex values. The comparison is ## case-insensitive. ## ## See also: - ## * `toHex proc<#toHex,string>`_ for the reverse operation + ## * `toHex func<#toHex,string>`_ for the reverse operation runnableExamples: let a = "41" @@ -1242,7 +1225,7 @@ proc parseHexStr*(s: string): string {.noSideEffect, else: result[pos div 2] = chr(val + buf shl 4) -proc parseBool*(s: string): bool = +func parseBool*(s: string): bool = ## Parses a value into a `bool`. ## ## If ``s`` is one of the following values: ``y, yes, true, 1, on``, then @@ -1258,7 +1241,7 @@ proc parseBool*(s: string): bool = of "n", "no", "false", "0", "off": result = false else: raise newException(ValueError, "cannot interpret as a bool: " & s) -proc parseEnum*[T: enum](s: string): T = +func parseEnum*[T: enum](s: string): T = ## Parses an enum ``T``. This errors at compile time, if the given enum ## type contains multiple fields with the same string value. ## @@ -1278,7 +1261,7 @@ proc parseEnum*[T: enum](s: string): T = genEnumCaseStmt(T, s, default = nil, ord(low(T)), ord(high(T)), nimIdentNormalize) -proc parseEnum*[T: enum](s: string, default: T): T = +func parseEnum*[T: enum](s: string, default: T): T = ## Parses an enum ``T``. This errors at compile time, if the given enum ## type contains multiple fields with the same string value. ## @@ -1297,8 +1280,7 @@ proc parseEnum*[T: enum](s: string, default: T): T = genEnumCaseStmt(T, s, default, ord(low(T)), ord(high(T)), nimIdentNormalize) -proc repeat*(c: char, count: Natural): string {.noSideEffect, - rtl, extern: "nsuRepeatChar".} = +func repeat*(c: char, count: Natural): string {.rtl, extern: "nsuRepeatChar".} = ## Returns a string of length `count` consisting only of ## the character `c`. runnableExamples: @@ -1307,8 +1289,7 @@ proc repeat*(c: char, count: Natural): string {.noSideEffect, result = newString(count) for i in 0..count-1: result[i] = c -proc repeat*(s: string, n: Natural): string {.noSideEffect, - rtl, extern: "nsuRepeatStr".} = +func repeat*(s: string, n: Natural): string {.rtl, extern: "nsuRepeatStr".} = ## Returns string `s` concatenated `n` times. runnableExamples: doAssert "+ foo +".repeat(3) == "+ foo ++ foo ++ foo +" @@ -1316,15 +1297,15 @@ proc repeat*(s: string, n: Natural): string {.noSideEffect, result = newStringOfCap(n * s.len) for i in 1..n: result.add(s) -proc spaces*(n: Natural): string {.inline.} = - ## Returns a string with `n` space characters. You can use this proc +func spaces*(n: Natural): string {.inline.} = + ## Returns a string with `n` space characters. You can use this func ## to left align strings. ## ## See also: - ## * `align proc<#align,string,Natural,char>`_ - ## * `alignLeft proc<#alignLeft,string,Natural,char>`_ - ## * `indent proc<#indent,string,Natural,string>`_ - ## * `center proc<#center,string,int,char>`_ + ## * `align func<#align,string,Natural,char>`_ + ## * `alignLeft func<#alignLeft,string,Natural,char>`_ + ## * `indent func<#indent,string,Natural,string>`_ + ## * `center func<#center,string,int,char>`_ runnableExamples: let width = 15 @@ -1336,20 +1317,20 @@ proc spaces*(n: Natural): string {.inline.} = "This is a very long string|" repeat(' ', n) -proc align*(s: string, count: Natural, padding = ' '): string {. - noSideEffect, rtl, extern: "nsuAlignString".} = +func align*(s: string, count: Natural, padding = ' '): string {.rtl, + extern: "nsuAlignString".} = ## Aligns a string `s` with `padding`, so that it is of length `count`. ## ## `padding` characters (by default spaces) are added before `s` resulting in ## right alignment. If ``s.len >= count``, no spaces are added and `s` is ## returned unchanged. If you need to left align a string use the `alignLeft - ## proc <#alignLeft,string,Natural,char>`_. + ## func<#alignLeft,string,Natural,char>`_. ## ## See also: - ## * `alignLeft proc<#alignLeft,string,Natural,char>`_ - ## * `spaces proc<#spaces,Natural>`_ - ## * `indent proc<#indent,string,Natural,string>`_ - ## * `center proc<#center,string,int,char>`_ + ## * `alignLeft func<#alignLeft,string,Natural,char>`_ + ## * `spaces func<#spaces,Natural>`_ + ## * `indent func<#indent,string,Natural,string>`_ + ## * `center func<#center,string,int,char>`_ runnableExamples: assert align("abc", 4) == " abc" assert align("a", 0) == "a" @@ -1363,20 +1344,19 @@ proc align*(s: string, count: Natural, padding = ' '): string {. else: result = s -proc alignLeft*(s: string, count: Natural, padding = ' '): string {. - noSideEffect.} = +func alignLeft*(s: string, count: Natural, padding = ' '): string = ## Left-Aligns a string `s` with `padding`, so that it is of length `count`. ## ## `padding` characters (by default spaces) are added after `s` resulting in ## left alignment. If ``s.len >= count``, no spaces are added and `s` is ## returned unchanged. If you need to right align a string use the `align - ## proc <#align,string,Natural,char>`_. + ## func<#align,string,Natural,char>`_. ## ## See also: - ## * `align proc<#align,string,Natural,char>`_ - ## * `spaces proc<#spaces,Natural>`_ - ## * `indent proc<#indent,string,Natural,string>`_ - ## * `center proc<#center,string,int,char>`_ + ## * `align func<#align,string,Natural,char>`_ + ## * `spaces func<#spaces,Natural>`_ + ## * `indent func<#indent,string,Natural,string>`_ + ## * `center func<#center,string,int,char>`_ runnableExamples: assert alignLeft("abc", 4) == "abc " assert alignLeft("a", 0) == "a" @@ -1391,8 +1371,8 @@ proc alignLeft*(s: string, count: Natural, padding = ' '): string {. else: result = s -proc center*(s: string, width: int, fillChar: char = ' '): string {. - noSideEffect, rtl, extern: "nsuCenterString".} = +func center*(s: string, width: int, fillChar: char = ' '): string {.rtl, + extern: "nsuCenterString".} = ## Return the contents of `s` centered in a string `width` long using ## `fillChar` (default: space) as padding. ## @@ -1400,10 +1380,10 @@ proc center*(s: string, width: int, fillChar: char = ' '): string {. ## to `s.len`. ## ## See also: - ## * `align proc<#align,string,Natural,char>`_ - ## * `alignLeft proc<#alignLeft,string,Natural,char>`_ - ## * `spaces proc<#spaces,Natural>`_ - ## * `indent proc<#indent,string,Natural,string>`_ + ## * `align func<#align,string,Natural,char>`_ + ## * `alignLeft func<#alignLeft,string,Natural,char>`_ + ## * `spaces func<#spaces,Natural>`_ + ## * `indent func<#indent,string,Natural,string>`_ runnableExamples: let a = "foo" doAssert a.center(2) == "foo" @@ -1426,18 +1406,18 @@ proc center*(s: string, width: int, fillChar: char = ' '): string {. # the string s should go result[i] = fillChar -proc indent*(s: string, count: Natural, padding: string = " "): string - {.noSideEffect, rtl, extern: "nsuIndent".} = +func indent*(s: string, count: Natural, padding: string = " "): string {.rtl, + extern: "nsuIndent".} = ## Indents each line in ``s`` by ``count`` amount of ``padding``. ## ## **Note:** This does not preserve the new line characters used in ``s``. ## ## See also: - ## * `align proc<#align,string,Natural,char>`_ - ## * `alignLeft proc<#alignLeft,string,Natural,char>`_ - ## * `spaces proc<#spaces,Natural>`_ - ## * `unindent proc<#unindent,string,Natural,string>`_ - ## * `dedent proc<#dedent,string,Natural>`_ + ## * `align func<#align,string,Natural,char>`_ + ## * `alignLeft func<#alignLeft,string,Natural,char>`_ + ## * `spaces func<#spaces,Natural>`_ + ## * `unindent func<#unindent,string,Natural,string>`_ + ## * `dedent func<#dedent,string,Natural>`_ runnableExamples: doAssert indent("First line\c\l and second line.", 2) == " First line\l and second line." @@ -1451,18 +1431,18 @@ proc indent*(s: string, count: Natural, padding: string = " "): string result.add(line) i.inc -proc unindent*(s: string, count: Natural = int.high, padding: string = " "): string - {.noSideEffect, rtl, extern: "nsuUnindent".} = +func unindent*(s: string, count: Natural = int.high, + padding: string = " "): string {.rtl, extern: "nsuUnindent".} = ## Unindents each line in ``s`` by ``count`` amount of ``padding``. ## ## **Note:** This does not preserve the new line characters used in ``s``. ## ## See also: - ## * `dedent proc<#dedent,string,Natural>`_ - ## * `align proc<#align,string,Natural,char>`_ - ## * `alignLeft proc<#alignLeft,string,Natural,char>`_ - ## * `spaces proc<#spaces,Natural>`_ - ## * `indent proc<#indent,string,Natural,string>`_ + ## * `dedent func<#dedent,string,Natural>`_ + ## * `align func<#align,string,Natural,char>`_ + ## * `alignLeft func<#alignLeft,string,Natural,char>`_ + ## * `spaces func<#spaces,Natural>`_ + ## * `indent func<#indent,string,Natural,string>`_ runnableExamples: let x = """ Hello @@ -1484,7 +1464,7 @@ proc unindent*(s: string, count: Natural = int.high, padding: string = " "): str result.add(line[indentCount*padding.len .. ^1]) i.inc -proc indentation*(s: string): Natural {.since: (1, 3).} = +func indentation*(s: string): Natural {.since: (1, 3).} = ## Returns the amount of indentation all lines of ``s`` have in common, ## ignoring lines that consist only of whitespace. result = int.high @@ -1497,22 +1477,22 @@ proc indentation*(s: string): Natural {.since: (1, 3).} = if result == int.high: result = 0 -proc dedent*(s: string, count: Natural = indentation(s)): string - {.noSideEffect, rtl, extern: "nsuDedent", since: (1, 3).} = +func dedent*(s: string, count: Natural = indentation(s)): string {.rtl, + extern: "nsuDedent", since: (1, 3).} = ## Unindents each line in ``s`` by ``count`` amount of ``padding``. ## The only difference between this and the - ## `unindent proc<#unindent,string,Natural,string>`_ is that this by default + ## `unindent func<#unindent,string,Natural,string>`_ is that this by default ## only cuts off the amount of indentation that all lines of ``s`` share as ## opposed to all indentation. It only supports spaces as padding. ## ## **Note:** This does not preserve the new line characters used in ``s``. ## ## See also: - ## * `unindent proc<#unindent,string,Natural,string>`_ - ## * `align proc<#align,string,Natural,char>`_ - ## * `alignLeft proc<#alignLeft,string,Natural,char>`_ - ## * `spaces proc<#spaces,Natural>`_ - ## * `indent proc<#indent,string,Natural,string>`_ + ## * `unindent func<#unindent,string,Natural,string>`_ + ## * `align func<#align,string,Natural,char>`_ + ## * `alignLeft func<#alignLeft,string,Natural,char>`_ + ## * `spaces func<#spaces,Natural>`_ + ## * `indent func<#indent,string,Natural,string>`_ runnableExamples: let x = """ Hello @@ -1522,8 +1502,7 @@ proc dedent*(s: string, count: Natural = indentation(s)): string doAssert x == "Hello\n There\n" unindent(s, count, " ") -proc delete*(s: var string, first, last: int) {.noSideEffect, - rtl, extern: "nsuDelete".} = +func delete*(s: var string, first, last: int) {.rtl, extern: "nsuDelete".} = ## Deletes in `s` (must be declared as ``var``) the characters at positions ## ``first ..last`` (both ends included). ## @@ -1550,29 +1529,28 @@ proc delete*(s: var string, first, last: int) {.noSideEffect, setLen(s, newLen) -proc startsWith*(s: string, prefix: char): bool {.noSideEffect, inline.} = +func startsWith*(s: string, prefix: char): bool {.inline.} = ## Returns true if ``s`` starts with character ``prefix``. ## ## See also: - ## * `endsWith proc<#endsWith,string,char>`_ - ## * `continuesWith proc<#continuesWith,string,string,Natural>`_ - ## * `removePrefix proc<#removePrefix,string,char>`_ + ## * `endsWith func<#endsWith,string,char>`_ + ## * `continuesWith func<#continuesWith,string,string,Natural>`_ + ## * `removePrefix func<#removePrefix,string,char>`_ runnableExamples: let a = "abracadabra" doAssert a.startsWith('a') == true doAssert a.startsWith('b') == false result = s.len > 0 and s[0] == prefix -proc startsWith*(s, prefix: string): bool {.noSideEffect, - rtl, extern: "nsuStartsWith".} = +func startsWith*(s, prefix: string): bool {.rtl, extern: "nsuStartsWith".} = ## Returns true if ``s`` starts with string ``prefix``. ## ## If ``prefix == ""`` true is returned. ## ## See also: - ## * `endsWith proc<#endsWith,string,string>`_ - ## * `continuesWith proc<#continuesWith,string,string,Natural>`_ - ## * `removePrefix proc<#removePrefix,string,string>`_ + ## * `endsWith func<#endsWith,string,string>`_ + ## * `continuesWith func<#continuesWith,string,string,Natural>`_ + ## * `removePrefix func<#removePrefix,string,string>`_ runnableExamples: let a = "abracadabra" doAssert a.startsWith("abra") == true @@ -1583,29 +1561,28 @@ proc startsWith*(s, prefix: string): bool {.noSideEffect, if i >= s.len or s[i] != prefix[i]: return false inc(i) -proc endsWith*(s: string, suffix: char): bool {.noSideEffect, inline.} = +func endsWith*(s: string, suffix: char): bool {.inline.} = ## Returns true if ``s`` ends with ``suffix``. ## ## See also: - ## * `startsWith proc<#startsWith,string,char>`_ - ## * `continuesWith proc<#continuesWith,string,string,Natural>`_ - ## * `removeSuffix proc<#removeSuffix,string,char>`_ + ## * `startsWith func<#startsWith,string,char>`_ + ## * `continuesWith func<#continuesWith,string,string,Natural>`_ + ## * `removeSuffix func<#removeSuffix,string,char>`_ runnableExamples: let a = "abracadabra" doAssert a.endsWith('a') == true doAssert a.endsWith('b') == false result = s.len > 0 and s[s.high] == suffix -proc endsWith*(s, suffix: string): bool {.noSideEffect, - rtl, extern: "nsuEndsWith".} = +func endsWith*(s, suffix: string): bool {.rtl, extern: "nsuEndsWith".} = ## Returns true if ``s`` ends with ``suffix``. ## ## If ``suffix == ""`` true is returned. ## ## See also: - ## * `startsWith proc<#startsWith,string,string>`_ - ## * `continuesWith proc<#continuesWith,string,string,Natural>`_ - ## * `removeSuffix proc<#removeSuffix,string,string>`_ + ## * `startsWith func<#startsWith,string,string>`_ + ## * `continuesWith func<#continuesWith,string,string,Natural>`_ + ## * `removeSuffix func<#removeSuffix,string,string>`_ runnableExamples: let a = "abracadabra" doAssert a.endsWith("abra") == true @@ -1617,15 +1594,15 @@ proc endsWith*(s, suffix: string): bool {.noSideEffect, inc(i) if i >= suffix.len: return true -proc continuesWith*(s, substr: string, start: Natural): bool {.noSideEffect, - rtl, extern: "nsuContinuesWith".} = +func continuesWith*(s, substr: string, start: Natural): bool {.rtl, + extern: "nsuContinuesWith".} = ## Returns true if ``s`` continues with ``substr`` at position ``start``. ## ## If ``substr == ""`` true is returned. ## ## See also: - ## * `startsWith proc<#startsWith,string,string>`_ - ## * `endsWith proc<#endsWith,string,string>`_ + ## * `startsWith func<#startsWith,string,string>`_ + ## * `endsWith func<#endsWith,string,string>`_ runnableExamples: let a = "abracadabra" doAssert a.continuesWith("ca", 4) == true @@ -1638,13 +1615,13 @@ proc continuesWith*(s, substr: string, start: Natural): bool {.noSideEffect, inc(i) -proc removePrefix*(s: var string, chars: set[char] = Newlines) {. - rtl, extern: "nsuRemovePrefixCharSet".} = +func removePrefix*(s: var string, chars: set[char] = Newlines) {.rtl, + extern: "nsuRemovePrefixCharSet".} = ## Removes all characters from `chars` from the start of the string `s` ## (in-place). ## ## See also: - ## * `removeSuffix proc<#removeSuffix,string,set[char]>`_ + ## * `removeSuffix func<#removeSuffix,string,set[char]>`_ runnableExamples: var userInput = "\r\n*~Hello World!" userInput.removePrefix @@ -1660,27 +1637,27 @@ proc removePrefix*(s: var string, chars: set[char] = Newlines) {. while start < s.len and s[start] in chars: start += 1 if start > 0: s.delete(0, start - 1) -proc removePrefix*(s: var string, c: char) {. - rtl, extern: "nsuRemovePrefixChar".} = +func removePrefix*(s: var string, c: char) {.rtl, + extern: "nsuRemovePrefixChar".} = ## Removes all occurrences of a single character (in-place) from the start ## of a string. ## ## See also: - ## * `removeSuffix proc<#removeSuffix,string,char>`_ - ## * `startsWith proc<#startsWith,string,char>`_ + ## * `removeSuffix func<#removeSuffix,string,char>`_ + ## * `startsWith func<#startsWith,string,char>`_ runnableExamples: var ident = "pControl" ident.removePrefix('p') doAssert ident == "Control" removePrefix(s, chars = {c}) -proc removePrefix*(s: var string, prefix: string) {. - rtl, extern: "nsuRemovePrefixString".} = +func removePrefix*(s: var string, prefix: string) {.rtl, + extern: "nsuRemovePrefixString".} = ## Remove the first matching prefix (in-place) from a string. ## ## See also: - ## * `removeSuffix proc<#removeSuffix,string,string>`_ - ## * `startsWith proc<#startsWith,string,string>`_ + ## * `removeSuffix func<#removeSuffix,string,string>`_ + ## * `startsWith func<#startsWith,string,string>`_ runnableExamples: var answers = "yesyes" answers.removePrefix("yes") @@ -1688,13 +1665,13 @@ proc removePrefix*(s: var string, prefix: string) {. if s.startsWith(prefix): s.delete(0, prefix.len - 1) -proc removeSuffix*(s: var string, chars: set[char] = Newlines) {. - rtl, extern: "nsuRemoveSuffixCharSet".} = +func removeSuffix*(s: var string, chars: set[char] = Newlines) {.rtl, + extern: "nsuRemoveSuffixCharSet".} = ## Removes all characters from `chars` from the end of the string `s` ## (in-place). ## ## See also: - ## * `removePrefix proc<#removePrefix,string,set[char]>`_ + ## * `removePrefix func<#removePrefix,string,set[char]>`_ runnableExamples: var userInput = "Hello World!*~\r\n" userInput.removeSuffix @@ -1711,14 +1688,14 @@ proc removeSuffix*(s: var string, chars: set[char] = Newlines) {. while last > -1 and s[last] in chars: last -= 1 s.setLen(last + 1) -proc removeSuffix*(s: var string, c: char) {. - rtl, extern: "nsuRemoveSuffixChar".} = +func removeSuffix*(s: var string, c: char) {.rtl, + extern: "nsuRemoveSuffixChar".} = ## Removes all occurrences of a single character (in-place) from the end ## of a string. ## ## See also: - ## * `removePrefix proc<#removePrefix,string,char>`_ - ## * `endsWith proc<#endsWith,string,char>`_ + ## * `removePrefix func<#removePrefix,string,char>`_ + ## * `endsWith func<#endsWith,string,char>`_ runnableExamples: var table = "users" table.removeSuffix('s') @@ -1730,13 +1707,13 @@ proc removeSuffix*(s: var string, c: char) {. removeSuffix(s, chars = {c}) -proc removeSuffix*(s: var string, suffix: string) {. - rtl, extern: "nsuRemoveSuffixString".} = +func removeSuffix*(s: var string, suffix: string) {.rtl, + extern: "nsuRemoveSuffixString".} = ## Remove the first matching suffix (in-place) from a string. ## ## See also: - ## * `removePrefix proc<#removePrefix,string,string>`_ - ## * `endsWith proc<#endsWith,string,string>`_ + ## * `removePrefix func<#removePrefix,string,string>`_ + ## * `endsWith func<#endsWith,string,string>`_ runnableExamples: var answers = "yeses" answers.removeSuffix("es") @@ -1747,8 +1724,7 @@ proc removeSuffix*(s: var string, suffix: string) {. s.setLen(newLen) -proc addSep*(dest: var string, sep = ", ", startLen: Natural = 0) - {.noSideEffect, inline.} = +func addSep*(dest: var string, sep = ", ", startLen: Natural = 0) {.inline.} = ## Adds a separator to `dest` only if its length is bigger than `startLen`. ## ## A shorthand for: @@ -1770,7 +1746,7 @@ proc addSep*(dest: var string, sep = ", ", startLen: Natural = 0) if dest.len > startLen: add(dest, sep) -proc allCharsInSet*(s: string, theSet: set[char]): bool = +func allCharsInSet*(s: string, theSet: set[char]): bool = ## Returns true if every character of `s` is in the set `theSet`. runnableExamples: doAssert allCharsInSet("aeea", {'a', 'e'}) == true @@ -1780,7 +1756,7 @@ proc allCharsInSet*(s: string, theSet: set[char]): bool = if c notin theSet: return false return true -proc abbrev*(s: string, possibilities: openArray[string]): int = +func abbrev*(s: string, possibilities: openArray[string]): int = ## Returns the index of the first item in ``possibilities`` which starts ## with ``s``, if not ambiguous. ## @@ -1802,8 +1778,8 @@ proc abbrev*(s: string, possibilities: openArray[string]): int = # --------------------------------------------------------------------------- -proc join*(a: openArray[string], sep: string = ""): string {. - noSideEffect, rtl, extern: "nsuJoinSep".} = +func join*(a: openArray[string], sep: string = ""): string {.rtl, + extern: "nsuJoinSep".} = ## Concatenates all strings in the container `a`, separating them with `sep`. runnableExamples: doAssert join(["A", "B", "Conclusion"], " -> ") == "A -> B -> Conclusion" @@ -1819,8 +1795,7 @@ proc join*(a: openArray[string], sep: string = ""): string {. else: result = "" -proc join*[T: not string](a: openArray[T], sep: string = ""): string {. - noSideEffect, rtl.} = +func join*[T: not string](a: openArray[T], sep: string = ""): string {.rtl.} = ## Converts all elements in the container `a` to strings using `$`, ## and concatenates them with `sep`. runnableExamples: @@ -1835,8 +1810,8 @@ proc join*[T: not string](a: openArray[T], sep: string = ""): string {. type SkipTable* = array[char, int] -proc initSkipTable*(a: var SkipTable, sub: string) - {.noSideEffect, rtl, extern: "nsuInitSkipTable".} = +func initSkipTable*(a: var SkipTable, sub: string) {.rtl, + extern: "nsuInitSkipTable".} = ## Preprocess table `a` for `sub`. let m = len(sub) var i = 0 @@ -1854,8 +1829,8 @@ proc initSkipTable*(a: var SkipTable, sub: string) for i in 0 ..< m - 1: a[sub[i]] = m - 1 - i -proc find*(a: SkipTable, s, sub: string, start: Natural = 0, last = 0): int - {.noSideEffect, rtl, extern: "nsuFindStrA".} = +func find*(a: SkipTable, s, sub: string, start: Natural = 0, last = 0): int {. + rtl, extern: "nsuFindStrA".} = ## Searches for `sub` in `s` inside range `start..last` using preprocessed ## table `a`. If `last` is unspecified, it defaults to `s.high` (the last ## element). @@ -1884,14 +1859,14 @@ proc find*(a: SkipTable, s, sub: string, start: Natural = 0, last = 0): int return -1 when not (defined(js) or defined(nimdoc) or defined(nimscript)): - proc c_memchr(cstr: pointer, c: char, n: csize_t): pointer {. + func c_memchr(cstr: pointer, c: char, n: csize_t): pointer {. importc: "memchr", header: "".} const hasCStringBuiltin = true else: const hasCStringBuiltin = false -proc find*(s: string, sub: char, start: Natural = 0, last = 0): int {.noSideEffect, - rtl, extern: "nsuFindChar".} = +func find*(s: string, sub: char, start: Natural = 0, last = 0): int {.rtl, + extern: "nsuFindChar".} = ## Searches for `sub` in `s` inside range ``start..last`` (both ends included). ## If `last` is unspecified, it defaults to `s.high` (the last element). ## @@ -1900,8 +1875,8 @@ proc find*(s: string, sub: char, start: Natural = 0, last = 0): int {.noSideEffe ## Use `s[start..last].rfind` for a ``start``-origin index. ## ## See also: - ## * `rfind proc<#rfind,string,char,Natural>`_ - ## * `replace proc<#replace,string,char,char>`_ + ## * `rfind func<#rfind,string,char,Natural>`_ + ## * `replace func<#replace,string,char,char>`_ let last = if last == 0: s.high else: last when nimvm: for i in int(start)..last: @@ -1918,8 +1893,8 @@ proc find*(s: string, sub: char, start: Natural = 0, last = 0): int {.noSideEffe if sub == s[i]: return i return -1 -proc find*(s: string, chars: set[char], start: Natural = 0, last = 0): int {.noSideEffect, - rtl, extern: "nsuFindCharSet".} = +func find*(s: string, chars: set[char], start: Natural = 0, last = 0): int {. + rtl, extern: "nsuFindCharSet".} = ## Searches for `chars` in `s` inside range ``start..last`` (both ends included). ## If `last` is unspecified, it defaults to `s.high` (the last element). ## @@ -1928,15 +1903,15 @@ proc find*(s: string, chars: set[char], start: Natural = 0, last = 0): int {.noS ## Use `s[start..last].find` for a ``start``-origin index. ## ## See also: - ## * `rfind proc<#rfind,string,set[char],Natural>`_ - ## * `multiReplace proc<#multiReplace,string,varargs[]>`_ + ## * `rfind func<#rfind,string,set[char],Natural>`_ + ## * `multiReplace func<#multiReplace,string,varargs[]>`_ let last = if last == 0: s.high else: last for i in int(start)..last: if s[i] in chars: return i return -1 -proc find*(s, sub: string, start: Natural = 0, last = 0): int {.noSideEffect, - rtl, extern: "nsuFindStr".} = +func find*(s, sub: string, start: Natural = 0, last = 0): int {.rtl, + extern: "nsuFindStr".} = ## Searches for `sub` in `s` inside range ``start..last`` (both ends included). ## If `last` is unspecified, it defaults to `s.high` (the last element). ## @@ -1945,16 +1920,16 @@ proc find*(s, sub: string, start: Natural = 0, last = 0): int {.noSideEffect, ## Use `s[start..last].find` for a ``start``-origin index. ## ## See also: - ## * `rfind proc<#rfind,string,string,Natural>`_ - ## * `replace proc<#replace,string,string,string>`_ + ## * `rfind func<#rfind,string,string,Natural>`_ + ## * `replace func<#replace,string,string,string>`_ if sub.len > s.len: return -1 if sub.len == 1: return find(s, sub[0], start, last) var a {.noinit.}: SkipTable initSkipTable(a, sub) result = find(a, s, sub, start, last) -proc rfind*(s: string, sub: char, start: Natural = 0, last = -1): int {.noSideEffect, - rtl, extern: "nsuRFindChar".} = +func rfind*(s: string, sub: char, start: Natural = 0, last = -1): int {.rtl, + extern: "nsuRFindChar".} = ## Searches for `sub` in `s` inside range ``start..last`` (both ends included) ## in reverse -- starting at high indexes and moving lower to the first ## character or ``start``. If `last` is unspecified, it defaults to `s.high` @@ -1965,14 +1940,14 @@ proc rfind*(s: string, sub: char, start: Natural = 0, last = -1): int {.noSideEf ## Use `s[start..last].find` for a ``start``-origin index. ## ## See also: - ## * `find proc<#find,string,char,Natural,int>`_ + ## * `find func<#find,string,char,Natural,int>`_ let last = if last == -1: s.high else: last for i in countdown(last, start): if sub == s[i]: return i return -1 -proc rfind*(s: string, chars: set[char], start: Natural = 0, last = -1): int {.noSideEffect, - rtl, extern: "nsuRFindCharSet".} = +func rfind*(s: string, chars: set[char], start: Natural = 0, last = -1): int {. + rtl, extern: "nsuRFindCharSet".} = ## Searches for `chars` in `s` inside range ``start..last`` (both ends ## included) in reverse -- starting at high indexes and moving lower to the ## first character or ``start``. If `last` is unspecified, it defaults to @@ -1983,14 +1958,14 @@ proc rfind*(s: string, chars: set[char], start: Natural = 0, last = -1): int {.n ## Use `s[start..last].rfind` for a ``start``-origin index. ## ## See also: - ## * `find proc<#find,string,set[char],Natural,int>`_ + ## * `find func<#find,string,set[char],Natural,int>`_ let last = if last == -1: s.high else: last for i in countdown(last, start): if s[i] in chars: return i return -1 -proc rfind*(s, sub: string, start: Natural = 0, last = -1): int {.noSideEffect, - rtl, extern: "nsuRFindStr".} = +func rfind*(s, sub: string, start: Natural = 0, last = -1): int {.rtl, + extern: "nsuRFindStr".} = ## Searches for `sub` in `s` inside range ``start..last`` (both ends included) ## included) in reverse -- starting at high indexes and moving lower to the ## first character or ``start``. If `last` is unspecified, it defaults to @@ -2001,7 +1976,7 @@ proc rfind*(s, sub: string, start: Natural = 0, last = -1): int {.noSideEffect, ## Use `s[start..last].rfind` for a ``start``-origin index. ## ## See also: - ## * `find proc<#find,string,string,Natural,int>`_ + ## * `find func<#find,string,string,Natural,int>`_ if sub.len == 0: return -1 let last = if last == -1: s.high else: last @@ -2016,35 +1991,34 @@ proc rfind*(s, sub: string, start: Natural = 0, last = -1): int {.noSideEffect, return -1 -proc count*(s: string, sub: char): int {.noSideEffect, - rtl, extern: "nsuCountChar".} = +func count*(s: string, sub: char): int {.rtl, extern: "nsuCountChar".} = ## Count the occurrences of the character `sub` in the string `s`. ## ## See also: - ## * `countLines proc<#countLines,string>`_ + ## * `countLines func<#countLines,string>`_ result = 0 for c in s: if c == sub: inc result -proc count*(s: string, subs: set[char]): int {.noSideEffect, - rtl, extern: "nsuCountCharSet".} = +func count*(s: string, subs: set[char]): int {.rtl, + extern: "nsuCountCharSet".} = ## Count the occurrences of the group of character `subs` in the string `s`. ## ## See also: - ## * `countLines proc<#countLines,string>`_ + ## * `countLines func<#countLines,string>`_ doAssert card(subs) > 0 result = 0 for c in s: if c in subs: inc result -proc count*(s: string, sub: string, overlapping: bool = false): int {. - noSideEffect, rtl, extern: "nsuCountString".} = +func count*(s: string, sub: string, overlapping: bool = false): int {.rtl, + extern: "nsuCountString".} = ## Count the occurrences of a substring `sub` in the string `s`. ## Overlapping occurrences of `sub` only count when `overlapping` ## is set to true (default: false). ## ## See also: - ## * `countLines proc<#countLines,string>`_ + ## * `countLines func<#countLines,string>`_ doAssert sub.len > 0 result = 0 var i = 0 @@ -2055,8 +2029,7 @@ proc count*(s: string, sub: string, overlapping: bool = false): int {. else: i += sub.len inc result -proc countLines*(s: string): int {.noSideEffect, - rtl, extern: "nsuCountLines".} = +func countLines*(s: string): int {.rtl, extern: "nsuCountLines".} = ## Returns the number of lines in the string `s`. ## ## This is the same as ``len(splitLines(s))``, but much more efficient @@ -2068,7 +2041,7 @@ proc countLines*(s: string): int {.noSideEffect, ## A line can be an empty string. ## ## See also: - ## * `splitLines proc<#splitLines,string>`_ + ## * `splitLines func<#splitLines,string>`_ runnableExamples: doAssert countLines("First line\l and second line.") == 2 result = 1 @@ -2083,30 +2056,30 @@ proc countLines*(s: string): int {.noSideEffect, inc i -proc contains*(s, sub: string): bool {.noSideEffect.} = +func contains*(s, sub: string): bool = ## Same as ``find(s, sub) >= 0``. ## ## See also: - ## * `find proc<#find,string,string,Natural,int>`_ + ## * `find func<#find,string,string,Natural,int>`_ return find(s, sub) >= 0 -proc contains*(s: string, chars: set[char]): bool {.noSideEffect.} = +func contains*(s: string, chars: set[char]): bool = ## Same as ``find(s, chars) >= 0``. ## ## See also: - ## * `find proc<#find,string,set[char],Natural,int>`_ + ## * `find func<#find,string,set[char],Natural,int>`_ return find(s, chars) >= 0 -proc replace*(s, sub: string, by = ""): string {.noSideEffect, - rtl, extern: "nsuReplaceStr".} = +func replace*(s, sub: string, by = ""): string {.rtl, + extern: "nsuReplaceStr".} = ## Replaces `sub` in `s` by the string `by`. ## ## See also: - ## * `find proc<#find,string,string,Natural,int>`_ - ## * `replace proc<#replace,string,char,char>`_ for replacing + ## * `find func<#find,string,string,Natural,int>`_ + ## * `replace func<#replace,string,char,char>`_ for replacing ## single characters - ## * `replaceWord proc<#replaceWord,string,string,string>`_ - ## * `multiReplace proc<#multiReplace,string,varargs[]>`_ + ## * `replaceWord func<#replaceWord,string,string,string>`_ + ## * `multiReplace func<#multiReplace,string,varargs[]>`_ result = "" let subLen = sub.len if subLen == 0: @@ -2139,17 +2112,17 @@ proc replace*(s, sub: string, by = ""): string {.noSideEffect, # copy the rest: add result, substr(s, i) -proc replace*(s: string, sub, by: char): string {.noSideEffect, - rtl, extern: "nsuReplaceChar".} = +func replace*(s: string, sub, by: char): string {.rtl, + extern: "nsuReplaceChar".} = ## Replaces `sub` in `s` by the character `by`. ## ## Optimized version of `replace <#replace,string,string,string>`_ for ## characters. ## ## See also: - ## * `find proc<#find,string,char,Natural,int>`_ - ## * `replaceWord proc<#replaceWord,string,string,string>`_ - ## * `multiReplace proc<#multiReplace,string,varargs[]>`_ + ## * `find func<#find,string,char,Natural,int>`_ + ## * `replaceWord func<#replaceWord,string,string,string>`_ + ## * `multiReplace func<#multiReplace,string,varargs[]>`_ result = newString(s.len) var i = 0 while i < s.len: @@ -2157,8 +2130,8 @@ proc replace*(s: string, sub, by: char): string {.noSideEffect, else: result[i] = s[i] inc(i) -proc replaceWord*(s, sub: string, by = ""): string {.noSideEffect, - rtl, extern: "nsuReplaceWord".} = +func replaceWord*(s, sub: string, by = ""): string {.rtl, + extern: "nsuReplaceWord".} = ## Replaces `sub` in `s` by the string `by`. ## ## Each occurrence of `sub` has to be surrounded by word boundaries @@ -2188,8 +2161,7 @@ proc replaceWord*(s, sub: string, by = ""): string {.noSideEffect, # copy the rest: add result, substr(s, i) -proc multiReplace*(s: string, replacements: varargs[(string, string)]): - string {.noSideEffect.} = +func multiReplace*(s: string, replacements: varargs[(string, string)]): string = ## Same as replace, but specialized for doing multiple replacements in a single ## pass through the input string. ## @@ -2224,8 +2196,8 @@ proc multiReplace*(s: string, replacements: varargs[(string, string)]): -proc insertSep*(s: string, sep = '_', digits = 3): string {.noSideEffect, - rtl, extern: "nsuInsertSep".} = +func insertSep*(s: string, sep = '_', digits = 3): string {.rtl, + extern: "nsuInsertSep".} = ## Inserts the separator `sep` after `digits` characters (default: 3) ## from right to left. ## @@ -2258,8 +2230,8 @@ proc insertSep*(s: string, sep = '_', digits = 3): string {.noSideEffect, inc(j) dec(L) -proc escape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect, - rtl, extern: "nsuEscape".} = +func escape*(s: string, prefix = "\"", suffix = "\""): string {.rtl, + extern: "nsuEscape".} = ## Escapes a string `s`. See `system.addEscapedChar ## `_ for the escaping scheme. ## @@ -2267,7 +2239,7 @@ proc escape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect, ## Both may be empty strings. ## ## See also: - ## * `unescape proc<#unescape,string,string,string>`_ for the opposite + ## * `unescape func<#unescape,string,string,string>`_ for the opposite ## operation result = newStringOfCap(s.len + s.len shr 2) result.add(prefix) @@ -2282,11 +2254,11 @@ proc escape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect, else: add(result, c) add(result, suffix) -proc unescape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect, - rtl, extern: "nsuUnescape".} = +func unescape*(s: string, prefix = "\"", suffix = "\""): string {.rtl, + extern: "nsuUnescape".} = ## Unescapes a string `s`. ## - ## This complements `escape proc<#escape,string,string,string>`_ + ## This complements `escape func<#escape,string,string,string>`_ ## as it performs the opposite operations. ## ## If `s` does not begin with ``prefix`` and end with ``suffix`` a @@ -2325,8 +2297,7 @@ proc unescape*(s: string, prefix = "\"", suffix = "\""): string {.noSideEffect, raise newException(ValueError, "String does not end in: " & suffix) -proc validIdentifier*(s: string): bool {.noSideEffect, - rtl, extern: "nsuValidIdentifier".} = +func validIdentifier*(s: string): bool {.rtl, extern: "nsuValidIdentifier".} = ## Returns true if `s` is a valid identifier. ## ## A valid identifier starts with a character of the set `IdentStartChars` @@ -2342,8 +2313,8 @@ proc validIdentifier*(s: string): bool {.noSideEffect, # floating point formatting: when not defined(js): - proc c_sprintf(buf, frmt: cstring): cint {.header: "", - importc: "sprintf", varargs, noSideEffect.} + func c_sprintf(buf, frmt: cstring): cint {.header: "", + importc: "sprintf", varargs} type FloatFormatMode* = enum @@ -2352,10 +2323,9 @@ type ffDecimal, ## use decimal floating point notation ffScientific ## use scientific notation (using ``e`` character) -proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault, +func formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault, precision: range[-1..32] = 16; - decimalSep = '.'): string {. - noSideEffect, rtl, extern: "nsu$1".} = + decimalSep = '.'): string {.rtl, extern: "nsu$1".} = ## Converts a floating point value `f` to a string. ## ## If ``format == ffDecimal`` then precision is the number of digits to @@ -2432,9 +2402,9 @@ proc formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault, result[^2] = result[^1] result.setLen(result.len - 1) -proc formatFloat*(f: float, format: FloatFormatMode = ffDefault, +func formatFloat*(f: float, format: FloatFormatMode = ffDefault, precision: range[-1..32] = 16; decimalSep = '.'): string {. - noSideEffect, rtl, extern: "nsu$1".} = + rtl, extern: "nsu$1".} = ## Converts a floating point value `f` to a string. ## ## If ``format == ffDecimal`` then precision is the number of digits to @@ -2453,7 +2423,7 @@ proc formatFloat*(f: float, format: FloatFormatMode = ffDefault, result = formatBiggestFloat(f, format, precision, decimalSep) -proc trimZeros*(x: var string; decimalSep = '.') {.noSideEffect.} = +func trimZeros*(x: var string; decimalSep = '.') = ## Trim trailing zeros from a formatted floating point ## value `x` (must be declared as ``var``). ## @@ -2477,10 +2447,10 @@ type bpIEC, # use the IEC/ISO standard prefixes such as kibi bpColloquial # use the colloquial kilo, mega etc -proc formatSize*(bytes: int64, +func formatSize*(bytes: int64, decimalSep = '.', prefix = bpIEC, - includeSpace = false): string {.noSideEffect.} = + includeSpace = false): string = ## Rounds and formats `bytes`. ## ## By default, uses the IEC/ISO standard binary prefixes, so 1024 will be @@ -2533,13 +2503,13 @@ proc formatSize*(bytes: int64, result &= prefixes[matchedIndex] result &= "B" -proc formatEng*(f: BiggestFloat, +func formatEng*(f: BiggestFloat, precision: range[0..32] = 10, trim: bool = true, siPrefix: bool = false, unit: string = "", decimalSep = '.', - useUnitSpace = false): string {.noSideEffect.} = + useUnitSpace = false): string = ## Converts a floating point value `f` to a string using engineering notation. ## ## Numbers in of the range -1000.0`_). + ## `% func<#%25,string,openArray[string]>`_). result = newStringOfCap(formatstr.len + a.len) addf(result, formatstr, [a]) -proc format*(formatstr: string, a: varargs[string, `$`]): string {.noSideEffect, - rtl, extern: "nsuFormatVarargs".} = +func format*(formatstr: string, a: varargs[string, `$`]): string {.rtl, + extern: "nsuFormatVarargs".} = ## This is the same as ``formatstr % a`` (see - ## `% proc<#%25,string,openArray[string]>`_) except that it supports + ## `% func<#%25,string,openArray[string]>`_) except that it supports ## auto stringification. ## ## See also: @@ -2814,9 +2784,8 @@ proc format*(formatstr: string, a: varargs[string, `$`]): string {.noSideEffect, addf(result, formatstr, a) -proc strip*(s: string, leading = true, trailing = true, - chars: set[char] = Whitespace): string - {.noSideEffect, rtl, extern: "nsuStrip".} = +func strip*(s: string, leading = true, trailing = true, + chars: set[char] = Whitespace): string {.rtl, extern: "nsuStrip".} = ## Strips leading or trailing `chars` (default: whitespace characters) ## from `s` and returns the resulting string. ## @@ -2825,7 +2794,7 @@ proc strip*(s: string, leading = true, trailing = true, ## If both are false, the string is returned unchanged. ## ## See also: - ## * `stripLineEnd proc<#stripLineEnd,string>`_ + ## * `stripLineEnd func<#stripLineEnd,string>`_ runnableExamples: let a = " vhellov " let b = strip(a) @@ -2850,7 +2819,7 @@ proc strip*(s: string, leading = true, trailing = true, while last >= 0 and s[last] in chars: dec(last) result = substr(s, first, last) -proc stripLineEnd*(s: var string) = +func stripLineEnd*(s: var string) = ## Returns ``s`` stripped from one of these suffixes: ## ``\r, \n, \r\n, \f, \v`` (at most once instance). ## For example, can be useful in conjunction with ``osproc.execCmdEx``. @@ -2910,7 +2879,7 @@ iterator tokenize*(s: string, seps: set[char] = Whitespace): tuple[ break i = j -proc isEmptyOrWhitespace*(s: string): bool {.noSideEffect, rtl, +func isEmptyOrWhitespace*(s: string): bool {.rtl, extern: "nsuIsEmptyOrWhitespace".} = ## Checks if `s` is empty or consists entirely of whitespace characters. result = s.allCharsInSet(Whitespace) diff --git a/tests/effects/tstrict_funcs.nim b/tests/effects/tstrict_funcs.nim index 0028e7d7e..715278f51 100644 --- a/tests/effects/tstrict_funcs.nim +++ b/tests/effects/tstrict_funcs.nim @@ -2,7 +2,7 @@ discard """ cmd: "nim c --experimental:strictFuncs --experimental:views $file" """ -import tables, streams, nre, parsecsv, uri, httpcore +import tables, streams, nre, parsecsv, uri, httpcore, strutils type Contig2Reads = TableRef[string, seq[string]] @@ -26,4 +26,4 @@ block: result[x.len] = move(y) var x = @[0, 1] - let z = x &&& 2 \ No newline at end of file + let z = x &&& 2 diff --git a/tests/errmsgs/tunknown_named_parameter.nim b/tests/errmsgs/tunknown_named_parameter.nim index 829ab9497..d3dd6cd2d 100644 --- a/tests/errmsgs/tunknown_named_parameter.nim +++ b/tests/errmsgs/tunknown_named_parameter.nim @@ -2,15 +2,15 @@ discard """ cmd: "nim check $file" errormsg: "type mismatch: got " nimout: ''' -proc rsplit(s: string; sep: char; maxsplit: int = -1): seq[string] +func rsplit(s: string; sep: char; maxsplit: int = -1): seq[string] first type mismatch at position: 2 required type for sep: char but expression '{':'}' is of type: set[char] -proc rsplit(s: string; sep: string; maxsplit: int = -1): seq[string] +func rsplit(s: string; sep: string; maxsplit: int = -1): seq[string] first type mismatch at position: 2 required type for sep: string but expression '{':'}' is of type: set[char] -proc rsplit(s: string; seps: set[char] = Whitespace; maxsplit: int = -1): seq[ +func rsplit(s: string; seps: set[char] = Whitespace; maxsplit: int = -1): seq[ string] first type mismatch at position: 3 unknown named parameter: maxsplits From 2297b9623843bb4c1dbd201a8fb57f9ec334c164 Mon Sep 17 00:00:00 2001 From: ee7 <45465154+ee7@users.noreply.github.com> Date: Mon, 7 Dec 2020 23:26:14 +0100 Subject: [PATCH 1143/3768] math.nim: Use `func` everywhere (#16285) * math.nim: procs with {.noSideEffect} -> funcs * math.nim: procs without {.noSideEffect.} -> funcs * math.nim: proc -> func in links * math.nim: proc -> func in doc comments * test: add `math` to strictFuncs test --- lib/pure/math.nim | 526 ++++++++++++++++---------------- tests/effects/tstrict_funcs.nim | 10 +- 2 files changed, 271 insertions(+), 265 deletions(-) diff --git a/lib/pure/math.nim b/lib/pure/math.nim index 8f5b9a6f5..b2433d309 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -58,7 +58,7 @@ import std/private/since import bitops, fenv -proc binom*(n, k: int): int {.noSideEffect.} = +func binom*(n, k: int): int = ## Computes the `binomial coefficient `_. runnableExamples: doAssert binom(6, 2) == binom(6, 4) @@ -71,17 +71,17 @@ proc binom*(n, k: int): int {.noSideEffect.} = for i in countup(2, k): result = (result * (n + 1 - i)) div i -proc createFactTable[N: static[int]]: array[N, int] = +func createFactTable[N: static[int]]: array[N, int] = result[0] = 1 for i in 1 ..< N: result[i] = result[i - 1] * i -proc fac*(n: int): int = +func fac*(n: int): int = ## Computes the `factorial `_ of ## a non-negative integer ``n``. ## ## See also: - ## * `prod proc <#prod,openArray[T]>`_ + ## * `prod func <#prod,openArray[T]>`_ runnableExamples: doAssert fac(3) == 6 doAssert fac(4) == 24 @@ -124,7 +124,7 @@ const type FloatClass* = enum ## Describes the class a floating point value belongs to. ## This is the type that is returned by - ## `classify proc <#classify,float>`_. + ## `classify func <#classify,float>`_. fcNormal, ## value is an ordinary nonzero floating point value fcSubnormal, ## value is a subnormal (a very small) floating point value fcZero, ## value is zero @@ -133,7 +133,7 @@ type fcInf, ## value is positive infinity fcNegInf ## value is negative infinity -proc classify*(x: float): FloatClass = +func classify*(x: float): FloatClass = ## Classifies a floating point value. ## ## Returns ``x``'s class as specified by `FloatClass enum<#FloatClass>`_. @@ -158,8 +158,8 @@ proc classify*(x: float): FloatClass = return fcSubnormal return fcNormal -proc almostEqual*[T: SomeFloat](x, y: T; unitsInLastPlace: Natural = 4): bool {. - since: (1, 5), inline, noSideEffect.} = +func almostEqual*[T: SomeFloat](x, y: T; unitsInLastPlace: Natural = 4): bool {. + since: (1, 5), inline.} = ## Checks if two float values are almost equal, using ## `machine epsilon `_. ## @@ -192,13 +192,13 @@ proc almostEqual*[T: SomeFloat](x, y: T; unitsInLastPlace: Natural = 4): bool {. result = diff <= epsilon(T) * abs(x + y) * T(unitsInLastPlace) or diff < minimumPositiveValue(T) -proc isPowerOfTwo*(x: int): bool {.noSideEffect.} = +func isPowerOfTwo*(x: int): bool = ## Returns ``true``, if ``x`` is a power of two, ``false`` otherwise. ## ## Zero and negative numbers are not a power of two. ## ## See also: - ## * `nextPowerOfTwo proc<#nextPowerOfTwo,int>`_ + ## * `nextPowerOfTwo func<#nextPowerOfTwo,int>`_ runnableExamples: doAssert isPowerOfTwo(16) == true doAssert isPowerOfTwo(5) == false @@ -206,13 +206,13 @@ proc isPowerOfTwo*(x: int): bool {.noSideEffect.} = doAssert isPowerOfTwo(-16) == false return (x > 0) and ((x and (x - 1)) == 0) -proc nextPowerOfTwo*(x: int): int {.noSideEffect.} = +func nextPowerOfTwo*(x: int): int = ## Returns ``x`` rounded up to the nearest power of two. ## ## Zero and negative numbers get rounded up to 1. ## ## See also: - ## * `isPowerOfTwo proc<#isPowerOfTwo,int>`_ + ## * `isPowerOfTwo func<#isPowerOfTwo,int>`_ runnableExamples: doAssert nextPowerOfTwo(16) == 16 doAssert nextPowerOfTwo(5) == 8 @@ -230,40 +230,40 @@ proc nextPowerOfTwo*(x: int): int {.noSideEffect.} = result = result or (result shr 1) result += 1 + ord(x <= 0) -proc sum*[T](x: openArray[T]): T {.noSideEffect.} = +func sum*[T](x: openArray[T]): T = ## Computes the sum of the elements in ``x``. ## ## If ``x`` is empty, 0 is returned. ## ## See also: - ## * `prod proc <#prod,openArray[T]>`_ - ## * `cumsum proc <#cumsum,openArray[T]>`_ - ## * `cumsummed proc <#cumsummed,openArray[T]>`_ + ## * `prod func <#prod,openArray[T]>`_ + ## * `cumsum func <#cumsum,openArray[T]>`_ + ## * `cumsummed func <#cumsummed,openArray[T]>`_ runnableExamples: doAssert sum([1, 2, 3, 4]) == 10 doAssert sum([-1.5, 2.7, -0.1]) == 1.1 for i in items(x): result = result + i -proc prod*[T](x: openArray[T]): T {.noSideEffect.} = +func prod*[T](x: openArray[T]): T = ## Computes the product of the elements in ``x``. ## ## If ``x`` is empty, 1 is returned. ## ## See also: - ## * `sum proc <#sum,openArray[T]>`_ - ## * `fac proc <#fac,int>`_ + ## * `sum func <#sum,openArray[T]>`_ + ## * `fac func <#fac,int>`_ runnableExamples: doAssert prod([1, 2, 3, 4]) == 24 doAssert prod([-4, 3, 5]) == -60 result = 1.T for i in items(x): result = result * i -proc cumsummed*[T](x: openArray[T]): seq[T] = +func cumsummed*[T](x: openArray[T]): seq[T] = ## Return cumulative (aka prefix) summation of ``x``. ## ## See also: - ## * `sum proc <#sum,openArray[T]>`_ - ## * `cumsum proc <#cumsum,openArray[T]>`_ for the in-place version + ## * `sum func <#sum,openArray[T]>`_ + ## * `cumsum func <#cumsum,openArray[T]>`_ for the in-place version runnableExamples: let a = [1, 2, 3, 4] doAssert cumsummed(a) == @[1, 3, 6, 10] @@ -271,13 +271,13 @@ proc cumsummed*[T](x: openArray[T]): seq[T] = result[0] = x[0] for i in 1 ..< x.len: result[i] = result[i-1] + x[i] -proc cumsum*[T](x: var openArray[T]) = +func cumsum*[T](x: var openArray[T]) = ## Transforms ``x`` in-place (must be declared as `var`) into its ## cumulative (aka prefix) summation. ## ## See also: - ## * `sum proc <#sum,openArray[T]>`_ - ## * `cumsummed proc <#cumsummed,openArray[T]>`_ for a version which + ## * `sum func <#sum,openArray[T]>`_ + ## * `cumsummed func <#cumsummed,openArray[T]>`_ for a version which ## returns cumsummed sequence runnableExamples: var a = [1, 2, 3, 4] @@ -285,40 +285,39 @@ proc cumsum*[T](x: var openArray[T]) = doAssert a == @[1, 3, 6, 10] for i in 1 ..< x.len: x[i] = x[i-1] + x[i] -{.push noSideEffect.} when not defined(js): # C - proc sqrt*(x: float32): float32 {.importc: "sqrtf", header: "".} - proc sqrt*(x: float64): float64 {.importc: "sqrt", header: "".} + func sqrt*(x: float32): float32 {.importc: "sqrtf", header: "".} + func sqrt*(x: float64): float64 {.importc: "sqrt", header: "".} ## Computes the square root of ``x``. ## ## See also: - ## * `cbrt proc <#cbrt,float64>`_ for cubic root + ## * `cbrt func <#cbrt,float64>`_ for cubic root ## ## .. code-block:: nim ## echo sqrt(4.0) ## 2.0 ## echo sqrt(1.44) ## 1.2 ## echo sqrt(-4.0) ## nan - proc cbrt*(x: float32): float32 {.importc: "cbrtf", header: "".} - proc cbrt*(x: float64): float64 {.importc: "cbrt", header: "".} + func cbrt*(x: float32): float32 {.importc: "cbrtf", header: "".} + func cbrt*(x: float64): float64 {.importc: "cbrt", header: "".} ## Computes the cubic root of ``x``. ## ## See also: - ## * `sqrt proc <#sqrt,float64>`_ for square root + ## * `sqrt func <#sqrt,float64>`_ for square root ## ## .. code-block:: nim ## echo cbrt(8.0) ## 2.0 ## echo cbrt(2.197) ## 1.3 ## echo cbrt(-27.0) ## -3.0 - proc ln*(x: float32): float32 {.importc: "logf", header: "".} - proc ln*(x: float64): float64 {.importc: "log", header: "".} + func ln*(x: float32): float32 {.importc: "logf", header: "".} + func ln*(x: float64): float64 {.importc: "log", header: "".} ## Computes the `natural logarithm `_ ## of ``x``. ## ## See also: - ## * `log proc <#log,T,T>`_ - ## * `log10 proc <#log10,float64>`_ - ## * `log2 proc <#log2,float64>`_ - ## * `exp proc <#exp,float64>`_ + ## * `log func <#log,T,T>`_ + ## * `log10 func <#log10,float64>`_ + ## * `log2 func <#log2,float64>`_ + ## * `exp func <#exp,float64>`_ ## ## .. code-block:: nim ## echo ln(exp(4.0)) ## 4.0 @@ -326,23 +325,23 @@ when not defined(js): # C ## echo ln(0.0) ## -inf ## echo ln(-7.0) ## nan else: # JS - proc sqrt*(x: float32): float32 {.importc: "Math.sqrt", nodecl.} - proc sqrt*(x: float64): float64 {.importc: "Math.sqrt", nodecl.} + func sqrt*(x: float32): float32 {.importc: "Math.sqrt", nodecl.} + func sqrt*(x: float64): float64 {.importc: "Math.sqrt", nodecl.} - proc cbrt*(x: float32): float32 {.importc: "Math.cbrt", nodecl.} - proc cbrt*(x: float64): float64 {.importc: "Math.cbrt", nodecl.} + func cbrt*(x: float32): float32 {.importc: "Math.cbrt", nodecl.} + func cbrt*(x: float64): float64 {.importc: "Math.cbrt", nodecl.} - proc ln*(x: float32): float32 {.importc: "Math.log", nodecl.} - proc ln*(x: float64): float64 {.importc: "Math.log", nodecl.} + func ln*(x: float32): float32 {.importc: "Math.log", nodecl.} + func ln*(x: float64): float64 {.importc: "Math.log", nodecl.} -proc log*[T: SomeFloat](x, base: T): T = +func log*[T: SomeFloat](x, base: T): T = ## Computes the logarithm of ``x`` to base ``base``. ## ## See also: - ## * `ln proc <#ln,float64>`_ - ## * `log10 proc <#log10,float64>`_ - ## * `log2 proc <#log2,float64>`_ - ## * `exp proc <#exp,float64>`_ + ## * `ln func <#ln,float64>`_ + ## * `log10 func <#log10,float64>`_ + ## * `log2 func <#log2,float64>`_ + ## * `exp func <#exp,float64>`_ ## ## .. code-block:: nim ## echo log(9.0, 3.0) ## 2.0 @@ -353,256 +352,256 @@ proc log*[T: SomeFloat](x, base: T): T = ln(x) / ln(base) when not defined(js): # C - proc log10*(x: float32): float32 {.importc: "log10f", header: "".} - proc log10*(x: float64): float64 {.importc: "log10", header: "".} + func log10*(x: float32): float32 {.importc: "log10f", header: "".} + func log10*(x: float64): float64 {.importc: "log10", header: "".} ## Computes the common logarithm (base 10) of ``x``. ## ## See also: - ## * `ln proc <#ln,float64>`_ - ## * `log proc <#log,T,T>`_ - ## * `log2 proc <#log2,float64>`_ - ## * `exp proc <#exp,float64>`_ + ## * `ln func <#ln,float64>`_ + ## * `log func <#log,T,T>`_ + ## * `log2 func <#log2,float64>`_ + ## * `exp func <#exp,float64>`_ ## ## .. code-block:: nim ## echo log10(100.0) ## 2.0 ## echo log10(0.0) ## nan ## echo log10(-100.0) ## -inf - proc exp*(x: float32): float32 {.importc: "expf", header: "".} - proc exp*(x: float64): float64 {.importc: "exp", header: "".} + func exp*(x: float32): float32 {.importc: "expf", header: "".} + func exp*(x: float64): float64 {.importc: "exp", header: "".} ## Computes the exponential function of ``x`` (e^x). ## ## See also: - ## * `ln proc <#ln,float64>`_ - ## * `log proc <#log,T,T>`_ - ## * `log10 proc <#log10,float64>`_ - ## * `log2 proc <#log2,float64>`_ + ## * `ln func <#ln,float64>`_ + ## * `log func <#log,T,T>`_ + ## * `log10 func <#log10,float64>`_ + ## * `log2 func <#log2,float64>`_ ## ## .. code-block:: nim ## echo exp(1.0) ## 2.718281828459045 ## echo ln(exp(4.0)) ## 4.0 ## echo exp(0.0) ## 1.0 ## echo exp(-1.0) ## 0.3678794411714423 - proc sin*(x: float32): float32 {.importc: "sinf", header: "".} - proc sin*(x: float64): float64 {.importc: "sin", header: "".} + func sin*(x: float32): float32 {.importc: "sinf", header: "".} + func sin*(x: float64): float64 {.importc: "sin", header: "".} ## Computes the sine of ``x``. ## ## See also: - ## * `cos proc <#cos,float64>`_ - ## * `tan proc <#tan,float64>`_ - ## * `arcsin proc <#arcsin,float64>`_ - ## * `sinh proc <#sinh,float64>`_ + ## * `cos func <#cos,float64>`_ + ## * `tan func <#tan,float64>`_ + ## * `arcsin func <#arcsin,float64>`_ + ## * `sinh func <#sinh,float64>`_ ## ## .. code-block:: nim ## echo sin(PI / 6) ## 0.4999999999999999 ## echo sin(degToRad(90.0)) ## 1.0 - proc cos*(x: float32): float32 {.importc: "cosf", header: "".} - proc cos*(x: float64): float64 {.importc: "cos", header: "".} + func cos*(x: float32): float32 {.importc: "cosf", header: "".} + func cos*(x: float64): float64 {.importc: "cos", header: "".} ## Computes the cosine of ``x``. ## ## See also: - ## * `sin proc <#sin,float64>`_ - ## * `tan proc <#tan,float64>`_ - ## * `arccos proc <#arccos,float64>`_ - ## * `cosh proc <#cosh,float64>`_ + ## * `sin func <#sin,float64>`_ + ## * `tan func <#tan,float64>`_ + ## * `arccos func <#arccos,float64>`_ + ## * `cosh func <#cosh,float64>`_ ## ## .. code-block:: nim ## echo cos(2 * PI) ## 1.0 ## echo cos(degToRad(60.0)) ## 0.5000000000000001 - proc tan*(x: float32): float32 {.importc: "tanf", header: "".} - proc tan*(x: float64): float64 {.importc: "tan", header: "".} + func tan*(x: float32): float32 {.importc: "tanf", header: "".} + func tan*(x: float64): float64 {.importc: "tan", header: "".} ## Computes the tangent of ``x``. ## ## See also: - ## * `sin proc <#sin,float64>`_ - ## * `cos proc <#cos,float64>`_ - ## * `arctan proc <#arctan,float64>`_ - ## * `tanh proc <#tanh,float64>`_ + ## * `sin func <#sin,float64>`_ + ## * `cos func <#cos,float64>`_ + ## * `arctan func <#arctan,float64>`_ + ## * `tanh func <#tanh,float64>`_ ## ## .. code-block:: nim ## echo tan(degToRad(45.0)) ## 0.9999999999999999 ## echo tan(PI / 4) ## 0.9999999999999999 - proc sinh*(x: float32): float32 {.importc: "sinhf", header: "".} - proc sinh*(x: float64): float64 {.importc: "sinh", header: "".} + func sinh*(x: float32): float32 {.importc: "sinhf", header: "".} + func sinh*(x: float64): float64 {.importc: "sinh", header: "".} ## Computes the `hyperbolic sine `_ of ``x``. ## ## See also: - ## * `cosh proc <#cosh,float64>`_ - ## * `tanh proc <#tanh,float64>`_ - ## * `arcsinh proc <#arcsinh,float64>`_ - ## * `sin proc <#sin,float64>`_ + ## * `cosh func <#cosh,float64>`_ + ## * `tanh func <#tanh,float64>`_ + ## * `arcsinh func <#arcsinh,float64>`_ + ## * `sin func <#sin,float64>`_ ## ## .. code-block:: nim ## echo sinh(0.0) ## 0.0 ## echo sinh(1.0) ## 1.175201193643801 ## echo sinh(degToRad(90.0)) ## 2.301298902307295 - proc cosh*(x: float32): float32 {.importc: "coshf", header: "".} - proc cosh*(x: float64): float64 {.importc: "cosh", header: "".} + func cosh*(x: float32): float32 {.importc: "coshf", header: "".} + func cosh*(x: float64): float64 {.importc: "cosh", header: "".} ## Computes the `hyperbolic cosine `_ of ``x``. ## ## See also: - ## * `sinh proc <#sinh,float64>`_ - ## * `tanh proc <#tanh,float64>`_ - ## * `arccosh proc <#arccosh,float64>`_ - ## * `cos proc <#cos,float64>`_ + ## * `sinh func <#sinh,float64>`_ + ## * `tanh func <#tanh,float64>`_ + ## * `arccosh func <#arccosh,float64>`_ + ## * `cos func <#cos,float64>`_ ## ## .. code-block:: nim ## echo cosh(0.0) ## 1.0 ## echo cosh(1.0) ## 1.543080634815244 ## echo cosh(degToRad(90.0)) ## 2.509178478658057 - proc tanh*(x: float32): float32 {.importc: "tanhf", header: "".} - proc tanh*(x: float64): float64 {.importc: "tanh", header: "".} + func tanh*(x: float32): float32 {.importc: "tanhf", header: "".} + func tanh*(x: float64): float64 {.importc: "tanh", header: "".} ## Computes the `hyperbolic tangent `_ of ``x``. ## ## See also: - ## * `sinh proc <#sinh,float64>`_ - ## * `cosh proc <#cosh,float64>`_ - ## * `arctanh proc <#arctanh,float64>`_ - ## * `tan proc <#tan,float64>`_ + ## * `sinh func <#sinh,float64>`_ + ## * `cosh func <#cosh,float64>`_ + ## * `arctanh func <#arctanh,float64>`_ + ## * `tan func <#tan,float64>`_ ## ## .. code-block:: nim ## echo tanh(0.0) ## 0.0 ## echo tanh(1.0) ## 0.7615941559557649 ## echo tanh(degToRad(90.0)) ## 0.9171523356672744 - proc arccos*(x: float32): float32 {.importc: "acosf", header: "".} - proc arccos*(x: float64): float64 {.importc: "acos", header: "".} + func arccos*(x: float32): float32 {.importc: "acosf", header: "".} + func arccos*(x: float64): float64 {.importc: "acos", header: "".} ## Computes the arc cosine of ``x``. ## ## See also: - ## * `arcsin proc <#arcsin,float64>`_ - ## * `arctan proc <#arctan,float64>`_ - ## * `arctan2 proc <#arctan2,float64,float64>`_ - ## * `cos proc <#cos,float64>`_ + ## * `arcsin func <#arcsin,float64>`_ + ## * `arctan func <#arctan,float64>`_ + ## * `arctan2 func <#arctan2,float64,float64>`_ + ## * `cos func <#cos,float64>`_ ## ## .. code-block:: nim ## echo radToDeg(arccos(0.0)) ## 90.0 ## echo radToDeg(arccos(1.0)) ## 0.0 - proc arcsin*(x: float32): float32 {.importc: "asinf", header: "".} - proc arcsin*(x: float64): float64 {.importc: "asin", header: "".} + func arcsin*(x: float32): float32 {.importc: "asinf", header: "".} + func arcsin*(x: float64): float64 {.importc: "asin", header: "".} ## Computes the arc sine of ``x``. ## ## See also: - ## * `arccos proc <#arccos,float64>`_ - ## * `arctan proc <#arctan,float64>`_ - ## * `arctan2 proc <#arctan2,float64,float64>`_ - ## * `sin proc <#sin,float64>`_ + ## * `arccos func <#arccos,float64>`_ + ## * `arctan func <#arctan,float64>`_ + ## * `arctan2 func <#arctan2,float64,float64>`_ + ## * `sin func <#sin,float64>`_ ## ## .. code-block:: nim ## echo radToDeg(arcsin(0.0)) ## 0.0 ## echo radToDeg(arcsin(1.0)) ## 90.0 - proc arctan*(x: float32): float32 {.importc: "atanf", header: "".} - proc arctan*(x: float64): float64 {.importc: "atan", header: "".} + func arctan*(x: float32): float32 {.importc: "atanf", header: "".} + func arctan*(x: float64): float64 {.importc: "atan", header: "".} ## Calculate the arc tangent of ``x``. ## ## See also: - ## * `arcsin proc <#arcsin,float64>`_ - ## * `arccos proc <#arccos,float64>`_ - ## * `arctan2 proc <#arctan2,float64,float64>`_ - ## * `tan proc <#tan,float64>`_ + ## * `arcsin func <#arcsin,float64>`_ + ## * `arccos func <#arccos,float64>`_ + ## * `arctan2 func <#arctan2,float64,float64>`_ + ## * `tan func <#tan,float64>`_ ## ## .. code-block:: nim ## echo arctan(1.0) ## 0.7853981633974483 ## echo radToDeg(arctan(1.0)) ## 45.0 - proc arctan2*(y, x: float32): float32 {.importc: "atan2f", + func arctan2*(y, x: float32): float32 {.importc: "atan2f", header: "".} - proc arctan2*(y, x: float64): float64 {.importc: "atan2", header: "".} + func arctan2*(y, x: float64): float64 {.importc: "atan2", header: "".} ## Calculate the arc tangent of ``y`` / ``x``. ## ## It produces correct results even when the resulting angle is near ## pi/2 or -pi/2 (``x`` near 0). ## ## See also: - ## * `arcsin proc <#arcsin,float64>`_ - ## * `arccos proc <#arccos,float64>`_ - ## * `arctan proc <#arctan,float64>`_ - ## * `tan proc <#tan,float64>`_ + ## * `arcsin func <#arcsin,float64>`_ + ## * `arccos func <#arccos,float64>`_ + ## * `arctan func <#arctan,float64>`_ + ## * `tan func <#tan,float64>`_ ## ## .. code-block:: nim ## echo arctan2(1.0, 0.0) ## 1.570796326794897 ## echo radToDeg(arctan2(1.0, 0.0)) ## 90.0 - proc arcsinh*(x: float32): float32 {.importc: "asinhf", header: "".} - proc arcsinh*(x: float64): float64 {.importc: "asinh", header: "".} + func arcsinh*(x: float32): float32 {.importc: "asinhf", header: "".} + func arcsinh*(x: float64): float64 {.importc: "asinh", header: "".} ## Computes the inverse hyperbolic sine of ``x``. - proc arccosh*(x: float32): float32 {.importc: "acoshf", header: "".} - proc arccosh*(x: float64): float64 {.importc: "acosh", header: "".} + func arccosh*(x: float32): float32 {.importc: "acoshf", header: "".} + func arccosh*(x: float64): float64 {.importc: "acosh", header: "".} ## Computes the inverse hyperbolic cosine of ``x``. - proc arctanh*(x: float32): float32 {.importc: "atanhf", header: "".} - proc arctanh*(x: float64): float64 {.importc: "atanh", header: "".} + func arctanh*(x: float32): float32 {.importc: "atanhf", header: "".} + func arctanh*(x: float64): float64 {.importc: "atanh", header: "".} ## Computes the inverse hyperbolic tangent of ``x``. else: # JS - proc log10*(x: float32): float32 {.importc: "Math.log10", nodecl.} - proc log10*(x: float64): float64 {.importc: "Math.log10", nodecl.} - proc log2*(x: float32): float32 {.importc: "Math.log2", nodecl.} - proc log2*(x: float64): float64 {.importc: "Math.log2", nodecl.} - proc exp*(x: float32): float32 {.importc: "Math.exp", nodecl.} - proc exp*(x: float64): float64 {.importc: "Math.exp", nodecl.} + func log10*(x: float32): float32 {.importc: "Math.log10", nodecl.} + func log10*(x: float64): float64 {.importc: "Math.log10", nodecl.} + func log2*(x: float32): float32 {.importc: "Math.log2", nodecl.} + func log2*(x: float64): float64 {.importc: "Math.log2", nodecl.} + func exp*(x: float32): float32 {.importc: "Math.exp", nodecl.} + func exp*(x: float64): float64 {.importc: "Math.exp", nodecl.} - proc sin*[T: float32|float64](x: T): T {.importc: "Math.sin", nodecl.} - proc cos*[T: float32|float64](x: T): T {.importc: "Math.cos", nodecl.} - proc tan*[T: float32|float64](x: T): T {.importc: "Math.tan", nodecl.} + func sin*[T: float32|float64](x: T): T {.importc: "Math.sin", nodecl.} + func cos*[T: float32|float64](x: T): T {.importc: "Math.cos", nodecl.} + func tan*[T: float32|float64](x: T): T {.importc: "Math.tan", nodecl.} - proc sinh*[T: float32|float64](x: T): T {.importc: "Math.sinh", nodecl.} - proc cosh*[T: float32|float64](x: T): T {.importc: "Math.cosh", nodecl.} - proc tanh*[T: float32|float64](x: T): T {.importc: "Math.tanh", nodecl.} + func sinh*[T: float32|float64](x: T): T {.importc: "Math.sinh", nodecl.} + func cosh*[T: float32|float64](x: T): T {.importc: "Math.cosh", nodecl.} + func tanh*[T: float32|float64](x: T): T {.importc: "Math.tanh", nodecl.} - proc arcsin*[T: float32|float64](x: T): T {.importc: "Math.asin", nodecl.} - proc arccos*[T: float32|float64](x: T): T {.importc: "Math.acos", nodecl.} - proc arctan*[T: float32|float64](x: T): T {.importc: "Math.atan", nodecl.} - proc arctan2*[T: float32|float64](y, x: T): T {.importc: "Math.atan2", nodecl.} + func arcsin*[T: float32|float64](x: T): T {.importc: "Math.asin", nodecl.} + func arccos*[T: float32|float64](x: T): T {.importc: "Math.acos", nodecl.} + func arctan*[T: float32|float64](x: T): T {.importc: "Math.atan", nodecl.} + func arctan2*[T: float32|float64](y, x: T): T {.importc: "Math.atan2", nodecl.} - proc arcsinh*[T: float32|float64](x: T): T {.importc: "Math.asinh", nodecl.} - proc arccosh*[T: float32|float64](x: T): T {.importc: "Math.acosh", nodecl.} - proc arctanh*[T: float32|float64](x: T): T {.importc: "Math.atanh", nodecl.} + func arcsinh*[T: float32|float64](x: T): T {.importc: "Math.asinh", nodecl.} + func arccosh*[T: float32|float64](x: T): T {.importc: "Math.acosh", nodecl.} + func arctanh*[T: float32|float64](x: T): T {.importc: "Math.atanh", nodecl.} -proc cot*[T: float32|float64](x: T): T = 1.0 / tan(x) +func cot*[T: float32|float64](x: T): T = 1.0 / tan(x) ## Computes the cotangent of ``x`` (1 / tan(x)). -proc sec*[T: float32|float64](x: T): T = 1.0 / cos(x) +func sec*[T: float32|float64](x: T): T = 1.0 / cos(x) ## Computes the secant of ``x`` (1 / cos(x)). -proc csc*[T: float32|float64](x: T): T = 1.0 / sin(x) +func csc*[T: float32|float64](x: T): T = 1.0 / sin(x) ## Computes the cosecant of ``x`` (1 / sin(x)). -proc coth*[T: float32|float64](x: T): T = 1.0 / tanh(x) +func coth*[T: float32|float64](x: T): T = 1.0 / tanh(x) ## Computes the hyperbolic cotangent of ``x`` (1 / tanh(x)). -proc sech*[T: float32|float64](x: T): T = 1.0 / cosh(x) +func sech*[T: float32|float64](x: T): T = 1.0 / cosh(x) ## Computes the hyperbolic secant of ``x`` (1 / cosh(x)). -proc csch*[T: float32|float64](x: T): T = 1.0 / sinh(x) +func csch*[T: float32|float64](x: T): T = 1.0 / sinh(x) ## Computes the hyperbolic cosecant of ``x`` (1 / sinh(x)). -proc arccot*[T: float32|float64](x: T): T = arctan(1.0 / x) +func arccot*[T: float32|float64](x: T): T = arctan(1.0 / x) ## Computes the inverse cotangent of ``x``. -proc arcsec*[T: float32|float64](x: T): T = arccos(1.0 / x) +func arcsec*[T: float32|float64](x: T): T = arccos(1.0 / x) ## Computes the inverse secant of ``x``. -proc arccsc*[T: float32|float64](x: T): T = arcsin(1.0 / x) +func arccsc*[T: float32|float64](x: T): T = arcsin(1.0 / x) ## Computes the inverse cosecant of ``x``. -proc arccoth*[T: float32|float64](x: T): T = arctanh(1.0 / x) +func arccoth*[T: float32|float64](x: T): T = arctanh(1.0 / x) ## Computes the inverse hyperbolic cotangent of ``x``. -proc arcsech*[T: float32|float64](x: T): T = arccosh(1.0 / x) +func arcsech*[T: float32|float64](x: T): T = arccosh(1.0 / x) ## Computes the inverse hyperbolic secant of ``x``. -proc arccsch*[T: float32|float64](x: T): T = arcsinh(1.0 / x) +func arccsch*[T: float32|float64](x: T): T = arcsinh(1.0 / x) ## Computes the inverse hyperbolic cosecant of ``x``. const windowsCC89 = defined(windows) and defined(bcc) when not defined(js): # C - proc hypot*(x, y: float32): float32 {.importc: "hypotf", header: "".} - proc hypot*(x, y: float64): float64 {.importc: "hypot", header: "".} + func hypot*(x, y: float32): float32 {.importc: "hypotf", header: "".} + func hypot*(x, y: float64): float64 {.importc: "hypot", header: "".} ## Computes the hypotenuse of a right-angle triangle with ``x`` and ## ``y`` as its base and height. Equivalent to ``sqrt(x*x + y*y)``. ## ## .. code-block:: nim ## echo hypot(4.0, 3.0) ## 5.0 - proc pow*(x, y: float32): float32 {.importc: "powf", header: "".} - proc pow*(x, y: float64): float64 {.importc: "pow", header: "".} + func pow*(x, y: float32): float32 {.importc: "powf", header: "".} + func pow*(x, y: float64): float64 {.importc: "pow", header: "".} ## Computes x to power raised of y. ## - ## To compute power between integers (e.g. 2^6), use `^ proc<#^,T,Natural>`_. + ## To compute power between integers (e.g. 2^6), use `^ func<#^,T,Natural>`_. ## ## See also: - ## * `^ proc<#^,T,Natural>`_ - ## * `sqrt proc <#sqrt,float64>`_ - ## * `cbrt proc <#cbrt,float64>`_ + ## * `^ func<#^,T,Natural>`_ + ## * `sqrt func <#sqrt,float64>`_ + ## * `cbrt func <#cbrt,float64>`_ ## ## .. code-block:: nim ## echo pow(100, 1.5) ## 1000.0 @@ -610,38 +609,38 @@ when not defined(js): # C # TODO: add C89 version on windows when not windowsCC89: - proc erf*(x: float32): float32 {.importc: "erff", header: "".} - proc erf*(x: float64): float64 {.importc: "erf", header: "".} + func erf*(x: float32): float32 {.importc: "erff", header: "".} + func erf*(x: float64): float64 {.importc: "erf", header: "".} ## Computes the `error function `_ for ``x``. ## ## Note: Not available for JS backend. - proc erfc*(x: float32): float32 {.importc: "erfcf", header: "".} - proc erfc*(x: float64): float64 {.importc: "erfc", header: "".} + func erfc*(x: float32): float32 {.importc: "erfcf", header: "".} + func erfc*(x: float64): float64 {.importc: "erfc", header: "".} ## Computes the `complementary error function `_ for ``x``. ## ## Note: Not available for JS backend. - proc gamma*(x: float32): float32 {.importc: "tgammaf", header: "".} - proc gamma*(x: float64): float64 {.importc: "tgamma", header: "".} + func gamma*(x: float32): float32 {.importc: "tgammaf", header: "".} + func gamma*(x: float64): float64 {.importc: "tgamma", header: "".} ## Computes the `gamma function `_ for ``x``. ## ## Note: Not available for JS backend. ## ## See also: - ## * `lgamma proc <#lgamma,float64>`_ for a natural log of gamma function + ## * `lgamma func <#lgamma,float64>`_ for a natural log of gamma function ## ## .. code-block:: Nim ## echo gamma(1.0) # 1.0 ## echo gamma(4.0) # 6.0 ## echo gamma(11.0) # 3628800.0 ## echo gamma(-1.0) # nan - proc lgamma*(x: float32): float32 {.importc: "lgammaf", header: "".} - proc lgamma*(x: float64): float64 {.importc: "lgamma", header: "".} + func lgamma*(x: float32): float32 {.importc: "lgammaf", header: "".} + func lgamma*(x: float64): float64 {.importc: "lgamma", header: "".} ## Computes the natural log of the gamma function for ``x``. ## ## Note: Not available for JS backend. ## ## See also: - ## * `gamma proc <#gamma,float64>`_ for gamma function + ## * `gamma func <#gamma,float64>`_ for gamma function ## ## .. code-block:: Nim ## echo lgamma(1.0) # 1.0 @@ -649,29 +648,29 @@ when not defined(js): # C ## echo lgamma(11.0) # 15.10441257307552 ## echo lgamma(-1.0) # inf - proc floor*(x: float32): float32 {.importc: "floorf", header: "".} - proc floor*(x: float64): float64 {.importc: "floor", header: "".} + func floor*(x: float32): float32 {.importc: "floorf", header: "".} + func floor*(x: float64): float64 {.importc: "floor", header: "".} ## Computes the floor function (i.e., the largest integer not greater than ``x``). ## ## See also: - ## * `ceil proc <#ceil,float64>`_ - ## * `round proc <#round,float64>`_ - ## * `trunc proc <#trunc,float64>`_ + ## * `ceil func <#ceil,float64>`_ + ## * `round func <#round,float64>`_ + ## * `trunc func <#trunc,float64>`_ ## ## .. code-block:: nim ## echo floor(2.1) ## 2.0 ## echo floor(2.9) ## 2.0 ## echo floor(-3.5) ## -4.0 - proc ceil*(x: float32): float32 {.importc: "ceilf", header: "".} - proc ceil*(x: float64): float64 {.importc: "ceil", header: "".} + func ceil*(x: float32): float32 {.importc: "ceilf", header: "".} + func ceil*(x: float64): float64 {.importc: "ceil", header: "".} ## Computes the ceiling function (i.e., the smallest integer not smaller ## than ``x``). ## ## See also: - ## * `floor proc <#floor,float64>`_ - ## * `round proc <#round,float64>`_ - ## * `trunc proc <#trunc,float64>`_ + ## * `floor func <#floor,float64>`_ + ## * `round func <#round,float64>`_ + ## * `trunc func <#trunc,float64>`_ ## ## .. code-block:: nim ## echo ceil(2.1) ## 3.0 @@ -681,7 +680,7 @@ when not defined(js): # C when windowsCC89: # MSVC 2010 don't have trunc/truncf # this implementation was inspired by Go-lang Math.Trunc - proc truncImpl(f: float64): float64 = + func truncImpl(f: float64): float64 = const mask: uint64 = 0x7FF shift: uint64 = 64 - 12 @@ -701,7 +700,7 @@ when not defined(js): # C result = cast[float64](x) - proc truncImpl(f: float32): float32 = + func truncImpl(f: float32): float32 = const mask: uint32 = 0xFF shift: uint32 = 32 - 9 @@ -721,57 +720,57 @@ when not defined(js): # C result = cast[float32](x) - proc trunc*(x: float64): float64 = + func trunc*(x: float64): float64 = if classify(x) in {fcZero, fcNegZero, fcNan, fcInf, fcNegInf}: return x result = truncImpl(x) - proc trunc*(x: float32): float32 = + func trunc*(x: float32): float32 = if classify(x) in {fcZero, fcNegZero, fcNan, fcInf, fcNegInf}: return x result = truncImpl(x) - proc round*[T: float32|float64](x: T): T = + func round*[T: float32|float64](x: T): T = ## Windows compilers prior to MSVC 2012 do not implement 'round', ## 'roundl' or 'roundf'. result = if x < 0.0: ceil(x - T(0.5)) else: floor(x + T(0.5)) else: - proc round*(x: float32): float32 {.importc: "roundf", header: "".} - proc round*(x: float64): float64 {.importc: "round", header: "".} + func round*(x: float32): float32 {.importc: "roundf", header: "".} + func round*(x: float64): float64 {.importc: "round", header: "".} ## Rounds a float to zero decimal places. ## - ## Used internally by the `round proc <#round,T,int>`_ + ## Used internally by the `round func <#round,T,int>`_ ## when the specified number of places is 0. ## ## See also: - ## * `round proc <#round,T,int>`_ for rounding to the specific + ## * `round func <#round,T,int>`_ for rounding to the specific ## number of decimal places - ## * `floor proc <#floor,float64>`_ - ## * `ceil proc <#ceil,float64>`_ - ## * `trunc proc <#trunc,float64>`_ + ## * `floor func <#floor,float64>`_ + ## * `ceil func <#ceil,float64>`_ + ## * `trunc func <#trunc,float64>`_ ## ## .. code-block:: nim ## echo round(3.4) ## 3.0 ## echo round(3.5) ## 4.0 ## echo round(4.5) ## 5.0 - proc trunc*(x: float32): float32 {.importc: "truncf", header: "".} - proc trunc*(x: float64): float64 {.importc: "trunc", header: "".} + func trunc*(x: float32): float32 {.importc: "truncf", header: "".} + func trunc*(x: float64): float64 {.importc: "trunc", header: "".} ## Truncates ``x`` to the decimal point. ## ## See also: - ## * `floor proc <#floor,float64>`_ - ## * `ceil proc <#ceil,float64>`_ - ## * `round proc <#round,float64>`_ + ## * `floor func <#floor,float64>`_ + ## * `ceil func <#ceil,float64>`_ + ## * `round func <#round,float64>`_ ## ## .. code-block:: nim ## echo trunc(PI) # 3.0 ## echo trunc(-1.85) # -1.0 - proc `mod`*(x, y: float32): float32 {.importc: "fmodf", header: "".} - proc `mod`*(x, y: float64): float64 {.importc: "fmod", header: "".} + func `mod`*(x, y: float32): float32 {.importc: "fmodf", header: "".} + func `mod`*(x, y: float64): float64 {.importc: "fmod", header: "".} ## Computes the modulo operation for float values (the remainder of ``x`` divided by ``y``). ## ## See also: - ## * `floorMod proc <#floorMod,T,T>`_ for Python-like (% operator) behavior + ## * `floorMod func <#floorMod,T,T>`_ for Python-like (% operator) behavior ## ## .. code-block:: nim ## ( 6.5 mod 2.5) == 1.5 @@ -780,20 +779,20 @@ when not defined(js): # C ## (-6.5 mod -2.5) == -1.5 else: # JS - proc hypot*(x, y: float32): float32 {.importc: "Math.hypot", varargs, nodecl.} - proc hypot*(x, y: float64): float64 {.importc: "Math.hypot", varargs, nodecl.} - proc pow*(x, y: float32): float32 {.importc: "Math.pow", nodecl.} - proc pow*(x, y: float64): float64 {.importc: "Math.pow", nodecl.} - proc floor*(x: float32): float32 {.importc: "Math.floor", nodecl.} - proc floor*(x: float64): float64 {.importc: "Math.floor", nodecl.} - proc ceil*(x: float32): float32 {.importc: "Math.ceil", nodecl.} - proc ceil*(x: float64): float64 {.importc: "Math.ceil", nodecl.} - proc round*(x: float): float {.importc: "Math.round", nodecl.} - proc trunc*(x: float32): float32 {.importc: "Math.trunc", nodecl.} - proc trunc*(x: float64): float64 {.importc: "Math.trunc", nodecl.} + func hypot*(x, y: float32): float32 {.importc: "Math.hypot", varargs, nodecl.} + func hypot*(x, y: float64): float64 {.importc: "Math.hypot", varargs, nodecl.} + func pow*(x, y: float32): float32 {.importc: "Math.pow", nodecl.} + func pow*(x, y: float64): float64 {.importc: "Math.pow", nodecl.} + func floor*(x: float32): float32 {.importc: "Math.floor", nodecl.} + func floor*(x: float64): float64 {.importc: "Math.floor", nodecl.} + func ceil*(x: float32): float32 {.importc: "Math.ceil", nodecl.} + func ceil*(x: float64): float64 {.importc: "Math.ceil", nodecl.} + func round*(x: float): float {.importc: "Math.round", nodecl.} + func trunc*(x: float32): float32 {.importc: "Math.trunc", nodecl.} + func trunc*(x: float64): float64 {.importc: "Math.trunc", nodecl.} - proc `mod`*(x, y: float32): float32 {.importcpp: "# % #".} - proc `mod`*(x, y: float64): float64 {.importcpp: "# % #".} + func `mod`*(x, y: float32): float32 {.importcpp: "# % #".} + func `mod`*(x, y: float64): float64 {.importcpp: "# % #".} ## Computes the modulo operation for float values (the remainder of ``x`` divided by ``y``). ## ## .. code-block:: nim @@ -802,7 +801,7 @@ else: # JS ## ( 6.5 mod -2.5) == 1.5 ## (-6.5 mod -2.5) == -1.5 -proc round*[T: float32|float64](x: T, places: int): T = +func round*[T: float32|float64](x: T, places: int): T = ## Decimal rounding on a binary floating point number. ## ## This function is NOT reliable. Floating point numbers cannot hold @@ -823,7 +822,7 @@ proc round*[T: float32|float64](x: T, places: int): T = var mult = pow(10.0, places.T) result = round(x*mult)/mult -proc floorDiv*[T: SomeInteger](x, y: T): T = +func floorDiv*[T: SomeInteger](x, y: T): T = ## Floor division is conceptually defined as ``floor(x / y)``. ## ## This is different from the `system.div `_ @@ -832,7 +831,7 @@ proc floorDiv*[T: SomeInteger](x, y: T): T = ## ## See also: ## * `system.div proc `_ for integer division - ## * `floorMod proc <#floorMod,T,T>`_ for Python-like (% operator) behavior + ## * `floorMod func <#floorMod,T,T>`_ for Python-like (% operator) behavior ## ## .. code-block:: nim ## echo floorDiv( 13, 3) # 4 @@ -843,14 +842,14 @@ proc floorDiv*[T: SomeInteger](x, y: T): T = let r = x mod y if (r > 0 and y < 0) or (r < 0 and y > 0): result.dec 1 -proc floorMod*[T: SomeNumber](x, y: T): T = +func floorMod*[T: SomeNumber](x, y: T): T = ## Floor modulus is conceptually defined as ``x - (floorDiv(x, y) * y)``. ## - ## This proc behaves the same as the ``%`` operator in Python. + ## This func behaves the same as the ``%`` operator in Python. ## ## See also: - ## * `mod proc <#mod,float64,float64>`_ - ## * `floorDiv proc <#floorDiv,T,T>`_ + ## * `mod func <#mod,float64,float64>`_ + ## * `floorDiv func <#floorDiv,T,T>`_ ## ## .. code-block:: nim ## echo floorMod( 13, 3) # 1 @@ -861,11 +860,11 @@ proc floorMod*[T: SomeNumber](x, y: T): T = if (result > 0 and y < 0) or (result < 0 and y > 0): result += y when not defined(js): - proc c_frexp*(x: float32, exponent: var int32): float32 {. - importc: "frexp", header: "".} - proc c_frexp*(x: float64, exponent: var int32): float64 {. - importc: "frexp", header: "".} - proc frexp*[T, U](x: T, exponent: var U): T = + func c_frexp*(x: float32, exponent: var int32): float32 {. + importc: "frexp", header: "".} + func c_frexp*(x: float64, exponent: var int32): float64 {. + importc: "frexp", header: "".} + func frexp*[T, U](x: T, exponent: var U): T = ## Split a number into mantissa and exponent. ## ## ``frexp`` calculates the mantissa m (a float greater than or equal to 0.5 @@ -892,21 +891,21 @@ when not defined(js): if frac == 0.5: return T(exp - 1) log10(frac)*(1/ln2) + T(exp) - proc log2*(x: float32): float32 = log2Impl(x) - proc log2*(x: float64): float64 = log2Impl(x) + func log2*(x: float32): float32 = log2Impl(x) + func log2*(x: float64): float64 = log2Impl(x) ## Log2 returns the binary logarithm of x. ## The special cases are the same as for Log. else: - proc log2*(x: float32): float32 {.importc: "log2f", header: "".} - proc log2*(x: float64): float64 {.importc: "log2", header: "".} + func log2*(x: float32): float32 {.importc: "log2f", header: "".} + func log2*(x: float64): float64 {.importc: "log2", header: "".} ## Computes the binary logarithm (base 2) of ``x``. ## ## See also: - ## * `log proc <#log,T,T>`_ - ## * `log10 proc <#log10,float64>`_ - ## * `ln proc <#ln,float64>`_ - ## * `exp proc <#exp,float64>`_ + ## * `log func <#log,T,T>`_ + ## * `log10 func <#log10,float64>`_ + ## * `ln func <#ln,float64>`_ + ## * `exp func <#exp,float64>`_ ## ## .. code-block:: Nim ## echo log2(8.0) # 3.0 @@ -915,7 +914,7 @@ when not defined(js): ## echo log2(-2.0) # nan else: - proc frexp*[T: float32|float64](x: T, exponent: var int): T = + func frexp*[T: float32|float64](x: T, exponent: var int): T = if x == 0.0: exponent = 0 result = 0.0 @@ -931,7 +930,7 @@ else: if exponent == 1024 and result == 0.0: result = 0.99999999999999988898 -proc splitDecimal*[T: float32|float64](x: T): tuple[intpart: T, floatpart: T] = +func splitDecimal*[T: float32|float64](x: T): tuple[intpart: T, floatpart: T] = ## Breaks ``x`` into an integer and a fractional part. ## ## Returns a tuple containing ``intpart`` and ``floatpart`` representing @@ -952,29 +951,28 @@ proc splitDecimal*[T: float32|float64](x: T): tuple[intpart: T, floatpart: T] = result.intpart = -result.intpart result.floatpart = -result.floatpart -{.pop.} -proc degToRad*[T: float32|float64](d: T): T {.inline.} = +func degToRad*[T: float32|float64](d: T): T {.inline.} = ## Convert from degrees to radians. ## ## See also: - ## * `radToDeg proc <#radToDeg,T>`_ + ## * `radToDeg func <#radToDeg,T>`_ ## runnableExamples: doAssert degToRad(180.0) == 3.141592653589793 result = T(d) * RadPerDeg -proc radToDeg*[T: float32|float64](d: T): T {.inline.} = +func radToDeg*[T: float32|float64](d: T): T {.inline.} = ## Convert from radians to degrees. ## ## See also: - ## * `degToRad proc <#degToRad,T>`_ + ## * `degToRad func <#degToRad,T>`_ ## runnableExamples: doAssert radToDeg(2 * PI) == 360.0 result = T(d) / RadPerDeg -proc sgn*[T: SomeNumber](x: T): int {.inline.} = +func sgn*[T: SomeNumber](x: T): int {.inline.} = ## Sign function. ## ## Returns: @@ -991,17 +989,17 @@ proc sgn*[T: SomeNumber](x: T): int {.inline.} = {.pop.} {.pop.} -proc `^`*[T: SomeNumber](x: T, y: Natural): T = +func `^`*[T: SomeNumber](x: T, y: Natural): T = ## Computes ``x`` to the power ``y``. ## ## Exponent ``y`` must be non-negative, use - ## `pow proc <#pow,float64,float64>`_ for negative exponents. + ## `pow func <#pow,float64,float64>`_ for negative exponents. ## ## See also: - ## * `pow proc <#pow,float64,float64>`_ for negative exponent or + ## * `pow func <#pow,float64,float64>`_ for negative exponent or ## floats - ## * `sqrt proc <#sqrt,float64>`_ - ## * `cbrt proc <#cbrt,float64>`_ + ## * `sqrt func <#sqrt,float64>`_ + ## * `cbrt func <#cbrt,float64>`_ ## runnableExamples: assert -3.0^0 == 1.0 @@ -1026,7 +1024,7 @@ proc `^`*[T: SomeNumber](x: T, y: Natural): T = break x *= x -proc gcd*[T](x, y: T): T = +func gcd*[T](x, y: T): T = ## Computes the greatest common (positive) divisor of ``x`` and ``y``. ## ## Note that for floats, the result cannot always be interpreted as @@ -1034,8 +1032,8 @@ proc gcd*[T](x, y: T): T = ## where N and M are positive integers." ## ## See also: - ## * `gcd proc <#gcd,SomeInteger,SomeInteger>`_ for integer version - ## * `lcm proc <#lcm,T,T>`_ + ## * `gcd func <#gcd,SomeInteger,SomeInteger>`_ for integer version + ## * `lcm func <#lcm,T,T>`_ runnableExamples: doAssert gcd(13.5, 9.0) == 4.5 var (x, y) = (x, y) @@ -1044,13 +1042,13 @@ proc gcd*[T](x, y: T): T = swap x, y abs x -proc gcd*(x, y: SomeInteger): SomeInteger = +func gcd*(x, y: SomeInteger): SomeInteger = ## Computes the greatest common (positive) divisor of ``x`` and ``y``, ## using binary GCD (aka Stein's) algorithm. ## ## See also: - ## * `gcd proc <#gcd,T,T>`_ for floats version - ## * `lcm proc <#lcm,T,T>`_ + ## * `gcd func <#gcd,T,T>`_ for floats version + ## * `lcm func <#lcm,T,T>`_ runnableExamples: doAssert gcd(12, 8) == 4 doAssert gcd(17, 63) == 1 @@ -1077,11 +1075,11 @@ proc gcd*(x, y: SomeInteger): SomeInteger = x -= y y shl shift -proc gcd*[T](x: openArray[T]): T {.since: (1, 1).} = +func gcd*[T](x: openArray[T]): T {.since: (1, 1).} = ## Computes the greatest common (positive) divisor of the elements of ``x``. ## ## See also: - ## * `gcd proc <#gcd,T,T>`_ for integer version + ## * `gcd func <#gcd,T,T>`_ for integer version runnableExamples: doAssert gcd(@[13.5, 9.0]) == 4.5 result = x[0] @@ -1090,21 +1088,21 @@ proc gcd*[T](x: openArray[T]): T {.since: (1, 1).} = result = gcd(result, x[i]) inc(i) -proc lcm*[T](x, y: T): T = +func lcm*[T](x, y: T): T = ## Computes the least common multiple of ``x`` and ``y``. ## ## See also: - ## * `gcd proc <#gcd,T,T>`_ + ## * `gcd func <#gcd,T,T>`_ runnableExamples: doAssert lcm(24, 30) == 120 doAssert lcm(13, 39) == 39 x div gcd(x, y) * y -proc lcm*[T](x: openArray[T]): T {.since: (1, 1).} = +func lcm*[T](x: openArray[T]): T {.since: (1, 1).} = ## Computes the least common multiple of the elements of ``x``. ## ## See also: - ## * `gcd proc <#gcd,T,T>`_ for integer version + ## * `gcd func <#gcd,T,T>`_ for integer version runnableExamples: doAssert lcm(@[24, 30]) == 120 result = x[0] diff --git a/tests/effects/tstrict_funcs.nim b/tests/effects/tstrict_funcs.nim index 715278f51..532c6e75e 100644 --- a/tests/effects/tstrict_funcs.nim +++ b/tests/effects/tstrict_funcs.nim @@ -2,7 +2,15 @@ discard """ cmd: "nim c --experimental:strictFuncs --experimental:views $file" """ -import tables, streams, nre, parsecsv, uri, httpcore, strutils +import tables, streams, parsecsv +# We import the below modules to check that they compile with `strictFuncs`. +# They are otherwise unused in this file. +import + httpcore, + math, + nre, + strutils, + uri type Contig2Reads = TableRef[string, seq[string]] From 4c533b2d92162980dc8fac2332b0eb0739dbda25 Mon Sep 17 00:00:00 2001 From: Xabier Bello Date: Tue, 8 Dec 2020 10:44:07 +0100 Subject: [PATCH 1144/3768] Added Python to highlite module. (#16286) * Added Python to highlite module. * Added the keywords for Python * Refactored the 'pythonLikeNextToken' into 'nimNextToken': - `lang` property added to GeneralTokenizer object, is set in `getNextToken`. - `nimNextToken` accepts `keywords` parameter, used for languages different from Nim. - Multiline comment available only for `langNim`. --- lib/packages/docutils/highlite.nim | 44 ++++++++++++++++++++---------- tests/stdlib/trstgen.nim | 24 +++++++++++++--- 2 files changed, 50 insertions(+), 18 deletions(-) diff --git a/lib/packages/docutils/highlite.nim b/lib/packages/docutils/highlite.nim index df74dd980..fcc42add0 100644 --- a/lib/packages/docutils/highlite.nim +++ b/lib/packages/docutils/highlite.nim @@ -45,6 +45,9 @@ import from algorithm import binarySearch type + SourceLanguage* = enum + langNone, langNim, langCpp, langCsharp, langC, langJava, + langYaml, langPython TokenClass* = enum gtEof, gtNone, gtWhitespace, gtDecNumber, gtBinNumber, gtHexNumber, gtOctNumber, gtFloatNumber, gtIdentifier, gtKeyword, gtStringLit, @@ -59,14 +62,11 @@ type buf: cstring pos: int state: TokenClass - - SourceLanguage* = enum - langNone, langNim, langCpp, langCsharp, langC, langJava, - langYaml + lang: SourceLanguage const sourceLanguageToStr*: array[SourceLanguage, string] = ["none", - "Nim", "C++", "C#", "C", "Java", "Yaml"] + "Nim", "C++", "C#", "C", "Java", "Yaml", "Python"] tokenClassToStr*: array[TokenClass, string] = ["Eof", "None", "Whitespace", "DecNumber", "BinNumber", "HexNumber", "OctNumber", "FloatNumber", "Identifier", "Keyword", "StringLit", "LongStringLit", "CharLit", @@ -101,6 +101,7 @@ proc initGeneralTokenizer*(g: var GeneralTokenizer, buf: cstring) = g.start = 0 g.length = 0 g.state = low(TokenClass) + g.lang = low(SourceLanguage) var pos = 0 # skip initial whitespace: while g.buf[pos] in {' ', '\x09'..'\x0D'}: inc(pos) g.pos = pos @@ -161,7 +162,13 @@ const OpChars = {'+', '-', '*', '/', '\\', '<', '>', '!', '?', '^', '.', '|', '=', '%', '&', '$', '@', '~', ':'} -proc nimNextToken(g: var GeneralTokenizer) = +proc isKeyword(x: openArray[string], y: string): int = + binarySearch(x, y) + +proc isKeywordIgnoreCase(x: openArray[string], y: string): int = + binarySearch(x, y, cmpIgnoreCase) + +proc nimNextToken(g: var GeneralTokenizer, keywords: openArray[string] = @[]) = const hexChars = {'0'..'9', 'A'..'F', 'a'..'f', '_'} octChars = {'0'..'7', '_'} @@ -207,7 +214,7 @@ proc nimNextToken(g: var GeneralTokenizer) = if g.buf[pos] == '#': inc(pos) isDoc = true - if g.buf[pos] == '[': + if g.buf[pos] == '[' and g.lang == langNim: g.kind = gtLongComment var nesting = 0 while true: @@ -265,7 +272,10 @@ proc nimNextToken(g: var GeneralTokenizer) = inc(pos) if g.buf[pos] == '\"': inc(pos) else: - g.kind = nimGetKeyword(id) + if g.lang == langNim: + g.kind = nimGetKeyword(id) + elif isKeyword(keywords, id) >= 0: + g.kind = gtKeyword of '0': inc(pos) case g.buf[pos] @@ -394,12 +404,6 @@ proc generalStrLit(g: var GeneralTokenizer, position: int): int = inc(pos) result = pos -proc isKeyword(x: openArray[string], y: string): int = - binarySearch(x, y) - -proc isKeywordIgnoreCase(x: openArray[string], y: string): int = - binarySearch(x, y, cmpIgnoreCase) - type TokenizerFlag = enum hasPreprocessor, hasNestedComments @@ -886,7 +890,18 @@ proc yamlNextToken(g: var GeneralTokenizer) = g.length = pos - g.pos g.pos = pos +proc pythonNextToken(g: var GeneralTokenizer) = + const + keywords: array[0..34, string] = [ + "False", "None", "True", "and", "as", "assert", "async", "await", + "break", "class", "continue", "def", "del", "elif", "else", "except", + "finally", "for", "from", "global", "if", "import", "in", "is", "lambda", + "nonlocal", "not", "or", "pass", "raise", "return", "try", "while", + "with", "yield"] + nimNextToken(g, keywords) + proc getNextToken*(g: var GeneralTokenizer, lang: SourceLanguage) = + g.lang = lang case lang of langNone: assert false of langNim: nimNextToken(g) @@ -895,6 +910,7 @@ proc getNextToken*(g: var GeneralTokenizer, lang: SourceLanguage) = of langC: cNextToken(g) of langJava: javaNextToken(g) of langYaml: yamlNextToken(g) + of langPython: pythonNextToken(g) when isMainModule: var keywords: seq[string] diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim index 1bf73e146..8b12a89c4 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -188,13 +188,13 @@ not in table""" let input1 = """ Test 2 column/4 rows table: ==== === -H0 H1 +H0 H1 ==== === -A0 A1 +A0 A1 ==== === -A2 A3 +A2 A3 ==== === -A4 A5 +A4 A5 ==== === """ let output1 = rstToLatex(input1, {}) assert "{|X|X|}" in output1 # 2 columns @@ -360,3 +360,19 @@ Test1 let output1l = rstToLatex(input1, {}) assert "line block\\\\" in output1l assert "other line\\\\" in output1l + +suite "RST/Code highlight": + test "Basic Python code highlight": + let pythonCode = """ + .. code-block:: python + + def f_name(arg=42): + print(f"{arg}") + + """ + + let expected = """

      def f_name(arg=42): + print(f"{arg}")

      """ + + check strip(rstToHtml(pythonCode, {}, newStringTable(modeCaseSensitive))) == + strip(expected) From a32acc351e929286e1df4a058f397dffcb6435ef Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Wed, 9 Dec 2020 09:36:46 +0800 Subject: [PATCH 1145/3768] Revert "use generics in random (#16283)" (#16291) This reverts commit 71e2a9e569ab3380fd057e4fc16e6d4ccd66f1f4. --- lib/pure/random.nim | 48 +++++++++++++++++++++++++++++++-------------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/lib/pure/random.nim b/lib/pure/random.nim index 0ee75e027..683140cd8 100644 --- a/lib/pure/random.nim +++ b/lib/pure/random.nim @@ -217,7 +217,8 @@ proc rand*(r: var Rand; max: Natural): int {.benign.} = ## Returns a random integer in the range `0..max` using the given state. ## ## See also: - ## * `rand proc<#rand,T>`_ `T` are integers, floats, and enums without holes. + ## * `rand proc<#rand,int>`_ that returns an integer using the default + ## random number generator ## * `rand proc<#rand,Rand,range[]>`_ that returns a float ## * `rand proc<#rand,Rand,HSlice[T,T]>`_ that accepts a slice ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type @@ -232,6 +233,28 @@ proc rand*(r: var Rand; max: Natural): int {.benign.} = if x <= randMax - (randMax mod Ui(max)): return int(x mod (uint64(max)+1u64)) +proc rand*(max: int): int {.benign.} = + ## Returns a random integer in the range `0..max`. + ## + ## If `randomize<#randomize>`_ has not been called, the sequence of random + ## numbers returned from this proc will always be the same. + ## + ## This proc uses the default random number generator. Thus, it is **not** + ## thread-safe. + ## + ## See also: + ## * `rand proc<#rand,Rand,Natural>`_ that returns an integer using a + ## provided state + ## * `rand proc<#rand,float>`_ that returns a float + ## * `rand proc<#rand,HSlice[T,T]>`_ that accepts a slice + ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type + runnableExamples: + randomize(123) + doAssert rand(100) == 0 + doAssert rand(100) == 96 + doAssert rand(100) == 66 + rand(state, max) + proc rand*(r: var Rand; max: range[0.0 .. high(float)]): float {.benign.} = ## Returns a random floating point number in the range `0.0..max` ## using the given state. @@ -253,10 +276,8 @@ proc rand*(r: var Rand; max: range[0.0 .. high(float)]): float {.benign.} = let u = (0x3FFu64 shl 52u64) or (x shr 12u64) result = (cast[float](u) - 1.0) * max -proc rand*[T: Ordinal or SomeFloat](max: T): T {.benign.} = - ## Returns a random floating point number in the range `T(0) .. max`. - ## - ## Allowed types for `T` are integers, floats, and enums without holes. +proc rand*(max: float): float {.benign.} = + ## Returns a random floating point number in the range `0.0..max`. ## ## If `randomize<#randomize>`_ has not been called, the sequence of random ## numbers returned from this proc will always be the same. @@ -265,21 +286,16 @@ proc rand*[T: Ordinal or SomeFloat](max: T): T {.benign.} = ## thread-safe. ## ## See also: - ## * `rand proc<#rand,Rand,Natural>`_ that returns an integer using a - ## provided state ## * `rand proc<#rand,Rand,range[]>`_ that returns a float using a ## provided state + ## * `rand proc<#rand,int>`_ that returns an integer ## * `rand proc<#rand,HSlice[T,T]>`_ that accepts a slice ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type runnableExamples: - randomize(123) - doAssert rand(100) == 0 - doAssert rand(100) == 96 - doAssert rand(100) == 66 - + randomize(234) let f = rand(1.0) ## f = 8.717181376738381e-07 - result = T(rand(state, max)) + rand(state, max) proc rand*[T: Ordinal or SomeFloat](r: var Rand; x: HSlice[T, T]): T = ## For a slice `a..b`, returns a value in the range `a..b` using the given @@ -319,7 +335,8 @@ proc rand*[T: Ordinal or SomeFloat](x: HSlice[T, T]): T = ## See also: ## * `rand proc<#rand,Rand,HSlice[T,T]>`_ that accepts a slice and uses ## a provided state - ## * `rand proc<#rand,T>`_ `T` are integers, floats, and enums without holes. + ## * `rand proc<#rand,int>`_ that returns an integer + ## * `rand proc<#rand,float>`_ that returns a floating point number ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type runnableExamples: randomize(345) @@ -338,7 +355,8 @@ proc rand*[T: SomeInteger](t: typedesc[T]): T = ## thread-safe. ## ## See also: - ## * `rand proc<#rand,T>`_ `T` are integers, floats, and enums without holes. + ## * `rand proc<#rand,int>`_ that returns an integer + ## * `rand proc<#rand,float>`_ that returns a floating point number ## * `rand proc<#rand,HSlice[T,T]>`_ that accepts a slice runnableExamples: randomize(567) From 40255f6721154c35a8f66653826928123178f1bb Mon Sep 17 00:00:00 2001 From: Miran Date: Wed, 9 Dec 2020 10:56:28 +0100 Subject: [PATCH 1146/3768] put both funcs and procs under the same section in the documentation (#16301) * both funcs and procs are under the same section in the documentation * update the test --- compiler/docgen.nim | 10 ++---- nimdoc/testproject/expected/testproject.html | 35 +++++++------------- 2 files changed, 14 insertions(+), 31 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 6ee03e292..7b3b62174 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -1045,12 +1045,9 @@ proc generateDoc*(d: PDoc, n, orig: PNode, docFlags: DocFlags = kDefault) = let pragmaNode = findPragma(n, wDeprecated) d.modDeprecationMsg.add(genDeprecationMsg(d, pragmaNode)) of nkCommentStmt: d.modDesc.add(genComment(d, n)) - of nkProcDef: + of nkProcDef, nkFuncDef: when useEffectSystem: documentRaises(d.cache, n) genItemAux(skProc) - of nkFuncDef: - when useEffectSystem: documentRaises(d.cache, n) - genItemAux(skFunc) of nkMethodDef: when useEffectSystem: documentRaises(d.cache, n) genItemAux(skMethod) @@ -1101,12 +1098,9 @@ proc generateJson*(d: PDoc, n: PNode, includeComments: bool = true) = d.add %*{"comment": genComment(d, n)} else: d.modDesc.add(genComment(d, n)) - of nkProcDef: + of nkProcDef, nkFuncDef: when useEffectSystem: documentRaises(d.cache, n) d.add genJsonItem(d, n, n[namePos], skProc) - of nkFuncDef: - when useEffectSystem: documentRaises(d.cache, n) - d.add genJsonItem(d, n, n[namePos], skFunc) of nkMethodDef: when useEffectSystem: documentRaises(d.cache, n) d.add genJsonItem(d, n, n[namePos], skMethod) diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html index a32e1180c..2bf8855c7 100644 --- a/nimdoc/testproject/expected/testproject.html +++ b/nimdoc/testproject/expected/testproject.html @@ -262,6 +262,11 @@ window.addEventListener('DOMContentLoaded', main);
    • z4
    • +
    +
      z8
    • -
    • - Funcs - -
    • Methods
        @@ -509,6 +503,13 @@ This is deprecated without message. This is deprecated with a message. +
    + +
    func someFunc() {...}{.raises: [], tags: [].}
    +
    + +My someFunc. Stuff in quotes here. Some link +
    proc fromUtils3() {...}{.raises: [], tags: [].}
    @@ -795,18 +796,6 @@ ok1 - -
    -

    Funcs

    -
    - -
    func someFunc() {...}{.raises: [], tags: [].}
    -
    - -My someFunc. Stuff in quotes here. Some link - -
    -

    Methods

    From 140ebe6019d097eacd5bb81f2937647d2b7d9954 Mon Sep 17 00:00:00 2001 From: ee7 <45465154+ee7@users.noreply.github.com> Date: Wed, 9 Dec 2020 10:57:12 +0100 Subject: [PATCH 1147/3768] complex.nim: Use `func` everywhere (#16294) --- lib/pure/complex.nim | 120 ++++++++++++++++---------------- tests/effects/tstrict_funcs.nim | 1 + 2 files changed, 61 insertions(+), 60 deletions(-) diff --git a/lib/pure/complex.nim b/lib/pure/complex.nim index d1056e6e8..04e5e8e56 100644 --- a/lib/pure/complex.nim +++ b/lib/pure/complex.nim @@ -24,15 +24,15 @@ type Complex32* = Complex[float32] ## Alias for a pair of 32-bit floats. -proc complex*[T: SomeFloat](re: T; im: T = 0.0): Complex[T] = +func complex*[T: SomeFloat](re: T; im: T = 0.0): Complex[T] = result.re = re result.im = im -proc complex32*(re: float32; im: float32 = 0.0): Complex[float32] = +func complex32*(re: float32; im: float32 = 0.0): Complex[float32] = result.re = re result.im = im -proc complex64*(re: float64; im: float64 = 0.0): Complex[float64] = +func complex64*(re: float64; im: float64 = 0.0): Complex[float64] = result.re = re result.im = im @@ -41,71 +41,71 @@ template im*(arg: typedesc[float64]): Complex64 = complex[float64](0, 1) template im*(arg: float32): Complex32 = complex[float32](0, arg) template im*(arg: float64): Complex64 = complex[float64](0, arg) -proc abs*[T](z: Complex[T]): T = +func abs*[T](z: Complex[T]): T = ## Returns the distance from (0,0) to ``z``. result = hypot(z.re, z.im) -proc abs2*[T](z: Complex[T]): T = +func abs2*[T](z: Complex[T]): T = ## Returns the squared distance from (0,0) to ``z``. result = z.re*z.re + z.im*z.im -proc conjugate*[T](z: Complex[T]): Complex[T] = +func conjugate*[T](z: Complex[T]): Complex[T] = ## Conjugates of complex number ``z``. result.re = z.re result.im = -z.im -proc inv*[T](z: Complex[T]): Complex[T] = +func inv*[T](z: Complex[T]): Complex[T] = ## Multiplicatives inverse of complex number ``z``. conjugate(z) / abs2(z) -proc `==` *[T](x, y: Complex[T]): bool = +func `==` *[T](x, y: Complex[T]): bool = ## Compares two complex numbers ``x`` and ``y`` for equality. result = x.re == y.re and x.im == y.im -proc `+` *[T](x: T; y: Complex[T]): Complex[T] = +func `+` *[T](x: T; y: Complex[T]): Complex[T] = ## Adds a real number to a complex number. result.re = x + y.re result.im = y.im -proc `+` *[T](x: Complex[T]; y: T): Complex[T] = +func `+` *[T](x: Complex[T]; y: T): Complex[T] = ## Adds a complex number to a real number. result.re = x.re + y result.im = x.im -proc `+` *[T](x, y: Complex[T]): Complex[T] = +func `+` *[T](x, y: Complex[T]): Complex[T] = ## Adds two complex numbers. result.re = x.re + y.re result.im = x.im + y.im -proc `-` *[T](z: Complex[T]): Complex[T] = +func `-` *[T](z: Complex[T]): Complex[T] = ## Unary minus for complex numbers. result.re = -z.re result.im = -z.im -proc `-` *[T](x: T; y: Complex[T]): Complex[T] = +func `-` *[T](x: T; y: Complex[T]): Complex[T] = ## Subtracts a complex number from a real number. x + (-y) -proc `-` *[T](x: Complex[T]; y: T): Complex[T] = +func `-` *[T](x: Complex[T]; y: T): Complex[T] = ## Subtracts a real number from a complex number. result.re = x.re - y result.im = x.im -proc `-` *[T](x, y: Complex[T]): Complex[T] = +func `-` *[T](x, y: Complex[T]): Complex[T] = ## Subtracts two complex numbers. result.re = x.re - y.re result.im = x.im - y.im -proc `/` *[T](x: Complex[T]; y: T): Complex[T] = +func `/` *[T](x: Complex[T]; y: T): Complex[T] = ## Divides complex number ``x`` by real number ``y``. result.re = x.re / y result.im = x.im / y -proc `/` *[T](x: T; y: Complex[T]): Complex[T] = +func `/` *[T](x: T; y: Complex[T]): Complex[T] = ## Divides real number ``x`` by complex number ``y``. result = x * inv(y) -proc `/` *[T](x, y: Complex[T]): Complex[T] = +func `/` *[T](x, y: Complex[T]): Complex[T] = ## Divides ``x`` by ``y``. var r, den: T if abs(y.re) < abs(y.im): @@ -119,44 +119,44 @@ proc `/` *[T](x, y: Complex[T]): Complex[T] = result.re = (x.re + r * x.im) / den result.im = (x.im - r * x.re) / den -proc `*` *[T](x: T; y: Complex[T]): Complex[T] = +func `*` *[T](x: T; y: Complex[T]): Complex[T] = ## Multiplies a real number and a complex number. result.re = x * y.re result.im = x * y.im -proc `*` *[T](x: Complex[T]; y: T): Complex[T] = +func `*` *[T](x: Complex[T]; y: T): Complex[T] = ## Multiplies a complex number with a real number. result.re = x.re * y result.im = x.im * y -proc `*` *[T](x, y: Complex[T]): Complex[T] = +func `*` *[T](x, y: Complex[T]): Complex[T] = ## Multiplies ``x`` with ``y``. result.re = x.re * y.re - x.im * y.im result.im = x.im * y.re + x.re * y.im -proc `+=` *[T](x: var Complex[T]; y: Complex[T]) = +func `+=` *[T](x: var Complex[T]; y: Complex[T]) = ## Adds ``y`` to ``x``. x.re += y.re x.im += y.im -proc `-=` *[T](x: var Complex[T]; y: Complex[T]) = +func `-=` *[T](x: var Complex[T]; y: Complex[T]) = ## Subtracts ``y`` from ``x``. x.re -= y.re x.im -= y.im -proc `*=` *[T](x: var Complex[T]; y: Complex[T]) = +func `*=` *[T](x: var Complex[T]; y: Complex[T]) = ## Multiplies ``y`` to ``x``. let im = x.im * y.re + x.re * y.im x.re = x.re * y.re - x.im * y.im x.im = im -proc `/=` *[T](x: var Complex[T]; y: Complex[T]) = +func `/=` *[T](x: var Complex[T]; y: Complex[T]) = ## Divides ``x`` by ``y`` in place. x = x / y -proc sqrt*[T](z: Complex[T]): Complex[T] = +func sqrt*[T](z: Complex[T]): Complex[T] = ## Square root for a complex number ``z``. var x, y, w, r: T @@ -179,7 +179,7 @@ proc sqrt*[T](z: Complex[T]): Complex[T] = result.im = if z.im >= 0.0: w else: -w result.re = z.im / (result.im + result.im) -proc exp*[T](z: Complex[T]): Complex[T] = +func exp*[T](z: Complex[T]): Complex[T] = ## ``e`` raised to the power ``z``. var rho = exp(z.re) @@ -187,20 +187,20 @@ proc exp*[T](z: Complex[T]): Complex[T] = result.re = rho * cos(theta) result.im = rho * sin(theta) -proc ln*[T](z: Complex[T]): Complex[T] = +func ln*[T](z: Complex[T]): Complex[T] = ## Returns the natural log of ``z``. result.re = ln(abs(z)) result.im = arctan2(z.im, z.re) -proc log10*[T](z: Complex[T]): Complex[T] = +func log10*[T](z: Complex[T]): Complex[T] = ## Returns the log base 10 of ``z``. result = ln(z) / ln(10.0) -proc log2*[T](z: Complex[T]): Complex[T] = +func log2*[T](z: Complex[T]): Complex[T] = ## Returns the log base 2 of ``z``. result = ln(z) / ln(2.0) -proc pow*[T](x, y: Complex[T]): Complex[T] = +func pow*[T](x, y: Complex[T]): Complex[T] = ## ``x`` raised to the power ``y``. if x.re == 0.0 and x.im == 0.0: if y.re == 0.0 and y.im == 0.0: @@ -222,118 +222,118 @@ proc pow*[T](x, y: Complex[T]): Complex[T] = result.re = s * cos(r) result.im = s * sin(r) -proc pow*[T](x: Complex[T]; y: T): Complex[T] = +func pow*[T](x: Complex[T]; y: T): Complex[T] = ## Complex number ``x`` raised to the power ``y``. pow(x, complex[T](y)) -proc sin*[T](z: Complex[T]): Complex[T] = +func sin*[T](z: Complex[T]): Complex[T] = ## Returns the sine of ``z``. result.re = sin(z.re) * cosh(z.im) result.im = cos(z.re) * sinh(z.im) -proc arcsin*[T](z: Complex[T]): Complex[T] = +func arcsin*[T](z: Complex[T]): Complex[T] = ## Returns the inverse sine of ``z``. result = -im(T) * ln(im(T) * z + sqrt(T(1.0) - z*z)) -proc cos*[T](z: Complex[T]): Complex[T] = +func cos*[T](z: Complex[T]): Complex[T] = ## Returns the cosine of ``z``. result.re = cos(z.re) * cosh(z.im) result.im = -sin(z.re) * sinh(z.im) -proc arccos*[T](z: Complex[T]): Complex[T] = +func arccos*[T](z: Complex[T]): Complex[T] = ## Returns the inverse cosine of ``z``. result = -im(T) * ln(z + sqrt(z*z - T(1.0))) -proc tan*[T](z: Complex[T]): Complex[T] = +func tan*[T](z: Complex[T]): Complex[T] = ## Returns the tangent of ``z``. result = sin(z) / cos(z) -proc arctan*[T](z: Complex[T]): Complex[T] = +func arctan*[T](z: Complex[T]): Complex[T] = ## Returns the inverse tangent of ``z``. result = T(0.5)*im(T) * (ln(T(1.0) - im(T)*z) - ln(T(1.0) + im(T)*z)) -proc cot*[T](z: Complex[T]): Complex[T] = +func cot*[T](z: Complex[T]): Complex[T] = ## Returns the cotangent of ``z``. result = cos(z)/sin(z) -proc arccot*[T](z: Complex[T]): Complex[T] = +func arccot*[T](z: Complex[T]): Complex[T] = ## Returns the inverse cotangent of ``z``. result = T(0.5)*im(T) * (ln(T(1.0) - im(T)/z) - ln(T(1.0) + im(T)/z)) -proc sec*[T](z: Complex[T]): Complex[T] = +func sec*[T](z: Complex[T]): Complex[T] = ## Returns the secant of ``z``. result = T(1.0) / cos(z) -proc arcsec*[T](z: Complex[T]): Complex[T] = +func arcsec*[T](z: Complex[T]): Complex[T] = ## Returns the inverse secant of ``z``. result = -im(T) * ln(im(T) * sqrt(1.0 - 1.0/(z*z)) + T(1.0)/z) -proc csc*[T](z: Complex[T]): Complex[T] = +func csc*[T](z: Complex[T]): Complex[T] = ## Returns the cosecant of ``z``. result = T(1.0) / sin(z) -proc arccsc*[T](z: Complex[T]): Complex[T] = +func arccsc*[T](z: Complex[T]): Complex[T] = ## Returns the inverse cosecant of ``z``. result = -im(T) * ln(sqrt(T(1.0) - T(1.0)/(z*z)) + im(T)/z) -proc sinh*[T](z: Complex[T]): Complex[T] = +func sinh*[T](z: Complex[T]): Complex[T] = ## Returns the hyperbolic sine of ``z``. result = T(0.5) * (exp(z) - exp(-z)) -proc arcsinh*[T](z: Complex[T]): Complex[T] = +func arcsinh*[T](z: Complex[T]): Complex[T] = ## Returns the inverse hyperbolic sine of ``z``. result = ln(z + sqrt(z*z + 1.0)) -proc cosh*[T](z: Complex[T]): Complex[T] = +func cosh*[T](z: Complex[T]): Complex[T] = ## Returns the hyperbolic cosine of ``z``. result = T(0.5) * (exp(z) + exp(-z)) -proc arccosh*[T](z: Complex[T]): Complex[T] = +func arccosh*[T](z: Complex[T]): Complex[T] = ## Returns the inverse hyperbolic cosine of ``z``. result = ln(z + sqrt(z*z - T(1.0))) -proc tanh*[T](z: Complex[T]): Complex[T] = +func tanh*[T](z: Complex[T]): Complex[T] = ## Returns the hyperbolic tangent of ``z``. result = sinh(z) / cosh(z) -proc arctanh*[T](z: Complex[T]): Complex[T] = +func arctanh*[T](z: Complex[T]): Complex[T] = ## Returns the inverse hyperbolic tangent of ``z``. result = T(0.5) * (ln((T(1.0)+z) / (T(1.0)-z))) -proc sech*[T](z: Complex[T]): Complex[T] = +func sech*[T](z: Complex[T]): Complex[T] = ## Returns the hyperbolic secant of ``z``. result = T(2.0) / (exp(z) + exp(-z)) -proc arcsech*[T](z: Complex[T]): Complex[T] = +func arcsech*[T](z: Complex[T]): Complex[T] = ## Returns the inverse hyperbolic secant of ``z``. result = ln(1.0/z + sqrt(T(1.0)/z+T(1.0)) * sqrt(T(1.0)/z-T(1.0))) -proc csch*[T](z: Complex[T]): Complex[T] = +func csch*[T](z: Complex[T]): Complex[T] = ## Returns the hyperbolic cosecant of ``z``. result = T(2.0) / (exp(z) - exp(-z)) -proc arccsch*[T](z: Complex[T]): Complex[T] = +func arccsch*[T](z: Complex[T]): Complex[T] = ## Returns the inverse hyperbolic cosecant of ``z``. result = ln(T(1.0)/z + sqrt(T(1.0)/(z*z) + T(1.0))) -proc coth*[T](z: Complex[T]): Complex[T] = +func coth*[T](z: Complex[T]): Complex[T] = ## Returns the hyperbolic cotangent of ``z``. result = cosh(z) / sinh(z) -proc arccoth*[T](z: Complex[T]): Complex[T] = +func arccoth*[T](z: Complex[T]): Complex[T] = ## Returns the inverse hyperbolic cotangent of ``z``. result = T(0.5) * (ln(T(1.0) + T(1.0)/z) - ln(T(1.0) - T(1.0)/z)) -proc phase*[T](z: Complex[T]): T = +func phase*[T](z: Complex[T]): T = ## Returns the phase of ``z``. arctan2(z.im, z.re) -proc polar*[T](z: Complex[T]): tuple[r, phi: T] = +func polar*[T](z: Complex[T]): tuple[r, phi: T] = ## Returns ``z`` in polar coordinates. (r: abs(z), phi: phase(z)) -proc rect*[T](r, phi: T): Complex[T] = +func rect*[T](r, phi: T): Complex[T] = ## Returns the complex number with polar coordinates ``r`` and ``phi``. ## ## | ``result.re = r * cos(phi)`` @@ -341,7 +341,7 @@ proc rect*[T](r, phi: T): Complex[T] = complex(r * cos(phi), r * sin(phi)) -proc `$`*(z: Complex): string = +func `$`*(z: Complex): string = ## Returns ``z``'s string representation as ``"(re, im)"``. result = "(" & $z.re & ", " & $z.im & ")" diff --git a/tests/effects/tstrict_funcs.nim b/tests/effects/tstrict_funcs.nim index 532c6e75e..89ac68108 100644 --- a/tests/effects/tstrict_funcs.nim +++ b/tests/effects/tstrict_funcs.nim @@ -6,6 +6,7 @@ import tables, streams, parsecsv # We import the below modules to check that they compile with `strictFuncs`. # They are otherwise unused in this file. import + complex, httpcore, math, nre, From c43dca29013127c29578a241881d23d49c37231f Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Wed, 9 Dec 2020 18:19:02 +0800 Subject: [PATCH 1148/3768] disbale deprecated CI in fork repo (#16299) --- appveyor.yml => appveyor.yml.disabled | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename appveyor.yml => appveyor.yml.disabled (100%) diff --git a/appveyor.yml b/appveyor.yml.disabled similarity index 100% rename from appveyor.yml rename to appveyor.yml.disabled From 6f57ebae349f073d696761fbba6d3f24f4b3dde9 Mon Sep 17 00:00:00 2001 From: ee7 <45465154+ee7@users.noreply.github.com> Date: Wed, 9 Dec 2020 12:11:16 +0100 Subject: [PATCH 1149/3768] sequtils.nim: Use `func` (#16293) * sequtils.nim: proc -> func * sequtils.nim: proc -> func in links * sequtils.nim: proc -> func in non-link doc comments * test: add `sequtils` to strictFuncs test --- lib/pure/collections/sequtils.nim | 92 +++++++++++++++---------------- lib/pure/collections/sets.nim | 2 +- tests/effects/tstrict_funcs.nim | 1 + 3 files changed, 48 insertions(+), 47 deletions(-) diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index 71558fe0d..a7e392026 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -110,12 +110,12 @@ macro evalOnceAs(expAlias, exp: untyped, newProc(name = genSym(nskTemplate, $expAlias), params = [getType(untyped)], body = val, procType = nnkTemplateDef)) -proc concat*[T](seqs: varargs[seq[T]]): seq[T] = +func concat*[T](seqs: varargs[seq[T]]): seq[T] = ## Takes several sequences' items and returns them inside a new sequence. ## All sequences must be of the same type. ## ## See also: - ## * `distribute proc<#distribute,seq[T],Positive>`_ for a reverse + ## * `distribute func<#distribute,seq[T],Positive>`_ for a reverse ## operation ## runnableExamples: @@ -135,7 +135,7 @@ proc concat*[T](seqs: varargs[seq[T]]): seq[T] = result[i] = itm inc(i) -proc count*[T](s: openArray[T], x: T): int = +func count*[T](s: openArray[T], x: T): int = ## Returns the number of occurrences of the item `x` in the container `s`. ## runnableExamples: @@ -150,7 +150,7 @@ proc count*[T](s: openArray[T], x: T): int = if itm == x: inc result -proc cycle*[T](s: openArray[T], n: Natural): seq[T] = +func cycle*[T](s: openArray[T], n: Natural): seq[T] = ## Returns a new sequence with the items of the container `s` repeated ## `n` times. ## `n` must be a non-negative number (zero or more). @@ -168,7 +168,7 @@ proc cycle*[T](s: openArray[T], n: Natural): seq[T] = result[o] = e inc o -proc repeat*[T](x: T, n: Natural): seq[T] = +func repeat*[T](x: T, n: Natural): seq[T] = ## Returns a new sequence with the item `x` repeated `n` times. ## `n` must be a non-negative number (zero or more). ## @@ -181,7 +181,7 @@ proc repeat*[T](x: T, n: Natural): seq[T] = for i in 0 ..< n: result[i] = x -proc deduplicate*[T](s: openArray[T], isSorted: bool = false): seq[T] = +func deduplicate*[T](s: openArray[T], isSorted: bool = false): seq[T] = ## Returns a new sequence without duplicates. ## ## Setting the optional argument ``isSorted`` to ``true`` (default: false) @@ -209,7 +209,7 @@ proc deduplicate*[T](s: openArray[T], isSorted: bool = false): seq[T] = for itm in items(s): if not result.contains(itm): result.add(itm) -proc minIndex*[T](s: openArray[T]): int {.since: (1, 1).} = +func minIndex*[T](s: openArray[T]): int {.since: (1, 1).} = ## Returns the index of the minimum value of `s`. ## ``T`` needs to have a ``<`` operator. runnableExamples: @@ -226,7 +226,7 @@ proc minIndex*[T](s: openArray[T]): int {.since: (1, 1).} = for i in 1..high(s): if s[i] < s[result]: result = i -proc maxIndex*[T](s: openArray[T]): int {.since: (1, 1).} = +func maxIndex*[T](s: openArray[T]): int {.since: (1, 1).} = ## Returns the index of the maximum value of `s`. ## ``T`` needs to have a ``<`` operator. runnableExamples: @@ -245,7 +245,7 @@ proc maxIndex*[T](s: openArray[T]): int {.since: (1, 1).} = template zipImpl(s1, s2, retType: untyped): untyped = - proc zip*[S, T](s1: openArray[S], s2: openArray[T]): retType = + func zip*[S, T](s1: openArray[S], s2: openArray[T]): retType = ## Returns a new sequence with a combination of the two input containers. ## ## The input containers can be of different types. @@ -288,7 +288,7 @@ when (NimMajor, NimMinor) <= (1, 0): else: zipImpl(s1, s2, seq[(S, T)]) -proc unzip*[S, T](s: openArray[(S, T)]): (seq[S], seq[T]) {.since: (1, 1).} = +func unzip*[S, T](s: openArray[(S, T)]): (seq[S], seq[T]) {.since: (1, 1).} = ## Returns a tuple of two sequences split out from a sequence of 2-field tuples. runnableExamples: let @@ -303,19 +303,19 @@ proc unzip*[S, T](s: openArray[(S, T)]): (seq[S], seq[T]) {.since: (1, 1).} = result[0][i] = s[i][0] result[1][i] = s[i][1] -proc distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] = +func distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] = ## Splits and distributes a sequence `s` into `num` sub-sequences. ## ## Returns a sequence of `num` sequences. For *some* input values this is the - ## inverse of the `concat <#concat,varargs[seq[T]]>`_ proc. + ## inverse of the `concat <#concat,varargs[seq[T]]>`_ func. ## The input sequence `s` can be empty, which will produce ## `num` empty sequences. ## ## If `spread` is false and the length of `s` is not a multiple of `num`, the - ## proc will max out the first sub-sequence with ``1 + len(s) div num`` + ## func will max out the first sub-sequence with ``1 + len(s) div num`` ## entries, leaving the remainder of elements to the last sequence. ## - ## On the other hand, if `spread` is true, the proc will distribute evenly + ## On the other hand, if `spread` is true, the func will distribute evenly ## the remainder of the division across all sequences, which makes the result ## more suited to multithreading where you are passing equal sized work units ## to a thread pool and want to maximize core usage. @@ -360,7 +360,7 @@ proc distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] = result[i].add(s[g]) first = last -proc map*[T, S](s: openArray[T], op: proc (x: T): S {.closure.}): +func map*[T, S](s: openArray[T], op: proc (x: T): S {.closure.}): seq[S]{.inline.} = ## Returns a new sequence with the results of `op` proc applied to every ## item in the container `s`. @@ -374,7 +374,7 @@ proc map*[T, S](s: openArray[T], op: proc (x: T): S {.closure.}): ## See also: ## * `sugar.collect macro`_ ## * `mapIt template<#mapIt.t,typed,untyped>`_ - ## * `apply proc<#apply,openArray[T],proc(T)_2>`_ for the in-place version + ## * `apply func<#apply,openArray[T],proc(T)_2>`_ for the in-place version ## runnableExamples: let @@ -386,7 +386,7 @@ proc map*[T, S](s: openArray[T], op: proc (x: T): S {.closure.}): for i in 0 ..< s.len: result[i] = op(s[i]) -proc apply*[T](s: var openArray[T], op: proc (x: var T) {.closure.}) +func apply*[T](s: var openArray[T], op: proc (x: var T) {.closure.}) {.inline.} = ## Applies `op` to every item in `s` modifying it directly. ## @@ -397,7 +397,7 @@ proc apply*[T](s: var openArray[T], op: proc (x: var T) {.closure.}) ## ## See also: ## * `applyIt template<#applyIt.t,untyped,untyped>`_ - ## * `map proc<#map,openArray[T],proc(T)>`_ + ## * `map func<#map,openArray[T],proc(T)>`_ ## runnableExamples: var a = @["1", "2", "3", "4"] @@ -406,7 +406,7 @@ proc apply*[T](s: var openArray[T], op: proc (x: var T) {.closure.}) for i in 0 ..< s.len: op(s[i]) -proc apply*[T](s: var openArray[T], op: proc (x: T): T {.closure.}) +func apply*[T](s: var openArray[T], op: proc (x: T): T {.closure.}) {.inline.} = ## Applies `op` to every item in `s` modifying it directly. ## @@ -417,7 +417,7 @@ proc apply*[T](s: var openArray[T], op: proc (x: T): T {.closure.}) ## ## See also: ## * `applyIt template<#applyIt.t,untyped,untyped>`_ - ## * `map proc<#map,openArray[T],proc(T)>`_ + ## * `map func<#map,openArray[T],proc(T)>`_ ## runnableExamples: var a = @["1", "2", "3", "4"] @@ -426,7 +426,7 @@ proc apply*[T](s: var openArray[T], op: proc (x: T): T {.closure.}) for i in 0 ..< s.len: s[i] = op(s[i]) -proc apply*[T](s: openArray[T], op: proc (x: T) {.closure.}) {.inline, since: (1, 3).} = +func apply*[T](s: openArray[T], op: proc (x: T) {.closure.}) {.inline, since: (1, 3).} = ## Same as `apply` but for proc that do not return and do not mutate `s` directly. runnableExamples: apply([0, 1, 2, 3, 4], proc(item: int) = echo item) for i in 0 ..< s.len: op(s[i]) @@ -440,7 +440,7 @@ iterator filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): T = ## ## See also: ## * `sugar.collect macro`_ - ## * `fliter proc<#filter,openArray[T],proc(T)>`_ + ## * `fliter func<#filter,openArray[T],proc(T)>`_ ## * `filterIt template<#filterIt.t,untyped,untyped>`_ ## runnableExamples: @@ -454,7 +454,7 @@ iterator filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): T = if pred(s[i]): yield s[i] -proc filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): seq[T] +func filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): seq[T] {.inline.} = ## Returns a new sequence with all the items of `s` that fulfilled the ## predicate `pred` (function that returns a `bool`). @@ -466,7 +466,7 @@ proc filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): seq[T] ## * `sugar.collect macro`_ ## * `filterIt template<#filterIt.t,untyped,untyped>`_ ## * `filter iterator<#filter.i,openArray[T],proc(T)>`_ - ## * `keepIf proc<#keepIf,seq[T],proc(T)>`_ for the in-place version + ## * `keepIf func<#keepIf,seq[T],proc(T)>`_ for the in-place version ## runnableExamples: let @@ -481,19 +481,19 @@ proc filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): seq[T] if pred(s[i]): result.add(s[i]) -proc keepIf*[T](s: var seq[T], pred: proc(x: T): bool {.closure.}) +func keepIf*[T](s: var seq[T], pred: proc(x: T): bool {.closure.}) {.inline.} = ## Keeps the items in the passed sequence `s` if they fulfilled the ## predicate `pred` (function that returns a `bool`). ## ## Note that `s` must be declared as a ``var``. ## - ## Similar to the `filter proc<#filter,openArray[T],proc(T)>`_, + ## Similar to the `filter func<#filter,openArray[T],proc(T)>`_, ## but modifies the sequence directly. ## ## See also: ## * `keepItIf template<#keepItIf.t,seq,untyped>`_ - ## * `filter proc<#filter,openArray[T],proc(T)>`_ + ## * `filter func<#filter,openArray[T],proc(T)>`_ ## runnableExamples: var floats = @[13.0, 12.5, 5.8, 2.0, 6.1, 9.9, 10.1] @@ -511,7 +511,7 @@ proc keepIf*[T](s: var seq[T], pred: proc(x: T): bool {.closure.}) inc(pos) setLen(s, pos) -proc delete*[T](s: var seq[T]; first, last: Natural) = +func delete*[T](s: var seq[T]; first, last: Natural) = ## Deletes in the items of a sequence `s` at positions ``first..last`` ## (including both ends of a range). ## This modifies `s` itself, it does not return a copy. @@ -536,7 +536,7 @@ proc delete*[T](s: var seq[T]; first, last: Natural) = inc(j) setLen(s, newLen) -proc insert*[T](dest: var seq[T], src: openArray[T], pos = 0) = +func insert*[T](dest: var seq[T], src: openArray[T], pos = 0) = ## Inserts items from `src` into `dest` at position `pos`. This modifies ## `dest` itself, it does not return a copy. ## @@ -574,7 +574,7 @@ template filterIt*(s, pred: untyped): untyped = ## Returns a new sequence with all the items of `s` that fulfilled the ## predicate `pred`. ## - ## Unlike the `filter proc<#filter,openArray[T],proc(T)>`_ and + ## Unlike the `filter func<#filter,openArray[T],proc(T)>`_ and ## `filter iterator<#filter.i,openArray[T],proc(T)>`_, ## the predicate needs to be an expression using the ``it`` variable ## for testing, like: ``filterIt("abcxyz", it == 'x')``. @@ -584,7 +584,7 @@ template filterIt*(s, pred: untyped): untyped = ## ## See also: ## * `sugar.collect macro`_ - ## * `fliter proc<#filter,openArray[T],proc(T)>`_ + ## * `fliter func<#filter,openArray[T],proc(T)>`_ ## * `filter iterator<#filter.i,openArray[T],proc(T)>`_ ## runnableExamples: @@ -604,12 +604,12 @@ template keepItIf*(varSeq: seq, pred: untyped) = ## Keeps the items in the passed sequence (must be declared as a ``var``) ## if they fulfilled the predicate. ## - ## Unlike the `keepIf proc<#keepIf,seq[T],proc(T)>`_, + ## Unlike the `keepIf func<#keepIf,seq[T],proc(T)>`_, ## the predicate needs to be an expression using ## the ``it`` variable for testing, like: ``keepItIf("abcxyz", it == 'x')``. ## ## See also: - ## * `keepIf proc<#keepIf,seq[T],proc(T)>`_ + ## * `keepIf func<#keepIf,seq[T],proc(T)>`_ ## * `filterIt template<#filterIt.t,untyped,untyped>`_ ## runnableExamples: @@ -648,13 +648,13 @@ since (1, 1): if pred: result += 1 result -proc all*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): bool = +func all*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): bool = ## Iterates through a container and checks if every item fulfills the ## predicate. ## ## See also: ## * `allIt template<#allIt.t,untyped,untyped>`_ - ## * `any proc<#any,openArray[T],proc(T)>`_ + ## * `any func<#any,openArray[T],proc(T)>`_ ## runnableExamples: let numbers = @[1, 4, 5, 8, 9, 7, 4] @@ -670,12 +670,12 @@ template allIt*(s, pred: untyped): bool = ## Iterates through a container and checks if every item fulfills the ## predicate. ## - ## Unlike the `all proc<#all,openArray[T],proc(T)>`_, + ## Unlike the `all func<#all,openArray[T],proc(T)>`_, ## the predicate needs to be an expression using ## the ``it`` variable for testing, like: ``allIt("abba", it == 'a')``. ## ## See also: - ## * `all proc<#all,openArray[T],proc(T)>`_ + ## * `all func<#all,openArray[T],proc(T)>`_ ## * `anyIt template<#anyIt.t,untyped,untyped>`_ ## runnableExamples: @@ -690,13 +690,13 @@ template allIt*(s, pred: untyped): bool = break result -proc any*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): bool = +func any*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): bool = ## Iterates through a container and checks if some item fulfills the ## predicate. ## ## See also: ## * `anyIt template<#anyIt.t,untyped,untyped>`_ - ## * `all proc<#all,openArray[T],proc(T)>`_ + ## * `all func<#all,openArray[T],proc(T)>`_ ## runnableExamples: let numbers = @[1, 4, 5, 8, 9, 7, 4] @@ -712,12 +712,12 @@ template anyIt*(s, pred: untyped): bool = ## Iterates through a container and checks if some item fulfills the ## predicate. ## - ## Unlike the `any proc<#any,openArray[T],proc(T)>`_, + ## Unlike the `any func<#any,openArray[T],proc(T)>`_, ## the predicate needs to be an expression using ## the ``it`` variable for testing, like: ``anyIt("abba", it == 'a')``. ## ## See also: - ## * `any proc<#any,openArray[T],proc(T)>`_ + ## * `any func<#any,openArray[T],proc(T)>`_ ## * `allIt template<#allIt.t,untyped,untyped>`_ ## runnableExamples: @@ -840,7 +840,7 @@ template foldl*(sequence, operation: untyped): untyped = procs = @["proc", "Is", "Also", "Fine"] - proc foo(acc, cur: string): string = + func foo(acc, cur: string): string = result = acc & cur assert addition == 25, "Addition is (((5)+9)+11)" @@ -944,7 +944,7 @@ template mapIt*(s: typed, op: untyped): untyped = ## ## See also: ## * `sugar.collect macro`_ - ## * `map proc<#map,openArray[T],proc(T)>`_ + ## * `map func<#map,openArray[T],proc(T)>`_ ## * `applyIt template<#applyIt.t,untyped,untyped>`_ for the in-place version ## runnableExamples: @@ -1005,14 +1005,14 @@ template mapIt*(s: typed, op: untyped): untyped = map(s, f) template applyIt*(varSeq, op: untyped) = - ## Convenience template around the mutable ``apply`` proc to reduce typing. + ## Convenience template around the mutable ``apply`` func to reduce typing. ## ## The template injects the ``it`` variable which you can use directly in an ## expression. The expression has to return the same type as the sequence you ## are mutating. ## ## See also: - ## * `apply proc<#apply,openArray[T],proc(T)_2>`_ + ## * `apply func<#apply,openArray[T],proc(T)_2>`_ ## * `mapIt template<#mapIt.t,typed,untyped>`_ ## runnableExamples: @@ -1048,7 +1048,7 @@ template newSeqWith*(len: int, init: untyped): untyped = result[i] = init result -proc mapLitsImpl(constructor: NimNode; op: NimNode; nested: bool; +func mapLitsImpl(constructor: NimNode; op: NimNode; nested: bool; filter = nnkLiterals): NimNode = if constructor.kind in filter: result = newNimNode(nnkCall, lineInfoFrom = constructor) diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim index 18f91f516..faa2f8c71 100644 --- a/lib/pure/collections/sets.nim +++ b/lib/pure/collections/sets.nim @@ -16,7 +16,7 @@ ## ## Common usages of sets: ## * removing duplicates from a container by converting it with `toHashSet proc -## <#toHashSet,openArray[A]>`_ (see also `sequtils.deduplicate proc +## <#toHashSet,openArray[A]>`_ (see also `sequtils.deduplicate func ## `_) ## * membership testing ## * mathematical operations on two sets, such as diff --git a/tests/effects/tstrict_funcs.nim b/tests/effects/tstrict_funcs.nim index 89ac68108..7a0f1ee32 100644 --- a/tests/effects/tstrict_funcs.nim +++ b/tests/effects/tstrict_funcs.nim @@ -10,6 +10,7 @@ import httpcore, math, nre, + sequtils, strutils, uri From 9ce2f87a0a02d85e943b2b181bcae66fbd644f11 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Wed, 9 Dec 2020 19:18:32 +0800 Subject: [PATCH 1150/3768] clean up old codes (#16284) * clean up old codes * fix docs and links * clean --- compiler/condsyms.nim | 1 - lib/system/iterators_1.nim | 249 +++++++++++++++---------------------- 2 files changed, 103 insertions(+), 147 deletions(-) diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index ee849435f..131f32f32 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -58,7 +58,6 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimGenericInOutFlags") when false: defineSymbol("nimHasOpt") defineSymbol("nimNoArrayToCstringConversion") - defineSymbol("nimNewRoof") defineSymbol("nimHasRunnableExamples") defineSymbol("nimNewDot") defineSymbol("nimHasNilChecks") diff --git a/lib/system/iterators_1.nim b/lib/system/iterators_1.nim index 262bd6aab..2bc58c2b7 100644 --- a/lib/system/iterators_1.nim +++ b/lib/system/iterators_1.nim @@ -9,15 +9,20 @@ iterator countdown*[T](a, b: T, step: Positive = 1): T {.inline.} = ## ## `T` may be any ordinal type, `step` may only be positive. ## - ## **Note**: This fails to count to ``low(int)`` if T = int for + ## **Note**: This fails to count to `low(int)` if T = int for ## efficiency reasons. - ## - ## .. code-block:: Nim - ## for i in countdown(7, 3): - ## echo i # => 7; 6; 5; 4; 3 - ## - ## for i in countdown(9, 2, 3): - ## echo i # => 9; 6; 3 + runnableExamples: + import sugar + let x = collect(newSeq): + for i in countdown(7, 3): + i + + assert x == @[7, 6, 5, 4, 3] + + let y = collect(newseq): + for i in countdown(9, 2, 3): + i + assert y == @[9, 6, 3] when T is (uint|uint64): var res = a while res >= b: @@ -35,148 +40,100 @@ iterator countdown*[T](a, b: T, step: Positive = 1): T {.inline.} = yield res dec(res, step) -when defined(nimNewRoof): - iterator countup*[T](a, b: T, step: Positive = 1): T {.inline.} = - ## Counts from ordinal value `a` to `b` (inclusive) with the given - ## step count. - ## - ## `T` may be any ordinal type, `step` may only be positive. - ## - ## **Note**: This fails to count to ``high(int)`` if T = int for - ## efficiency reasons. - ## - ## .. code-block:: Nim - ## for i in countup(3, 7): - ## echo i # => 3; 4; 5; 6; 7 - ## - ## for i in countup(2, 9, 3): - ## echo i # => 2; 5; 8 - mixin inc - when T is IntLikeForCount and T is Ordinal: - var res = int(a) - while res <= int(b): - yield T(res) - inc(res, step) - else: - var res = a - while res <= b: - yield res - inc(res, step) +iterator countup*[T](a, b: T, step: Positive = 1): T {.inline.} = + ## Counts from ordinal value `a` to `b` (inclusive) with the given + ## step count. + ## + ## `T` may be any ordinal type, `step` may only be positive. + ## + ## **Note**: This fails to count to `high(int)` if T = int for + ## efficiency reasons. + runnableExamples: + import sugar + let x = collect(newSeq): + for i in countup(3, 7): + i + + assert x == @[3, 4, 5, 6, 7] - iterator `..`*[T](a, b: T): T {.inline.} = - ## An alias for `countup(a, b, 1)`. + let y = collect(newseq): + for i in countup(2, 9, 3): + i + assert y == @[2, 5, 8] + mixin inc + when T is IntLikeForCount and T is Ordinal: + var res = int(a) + while res <= int(b): + yield T(res) + inc(res, step) + else: + var res = a + while res <= b: + yield res + inc(res, step) + +iterator `..`*[T](a, b: T): T {.inline.} = + ## An alias for `countup(a, b, 1)`. + ## + ## See also: + ## * [..<](#..<.i,T,T) + runnableExamples: + import sugar + + let x = collect(newSeq): + for i in 3 .. 7: + i + + assert x == @[3, 4, 5, 6, 7] + mixin inc + when T is IntLikeForCount and T is Ordinal: + var res = int(a) + while res <= int(b): + yield T(res) + inc(res) + else: + var res = a + while res <= b: + yield res + inc(res) + +template dotdotImpl(t) {.dirty.} = + iterator `..`*(a, b: t): t {.inline.} = + ## A type specialized version of `..` for convenience so that + ## mixing integer types works better. ## ## See also: ## * [..<](#..<.i,T,T) - ## - ## .. code-block:: Nim - ## for i in 3 .. 7: - ## echo i # => 3; 4; 5; 6; 7 - mixin inc - when T is IntLikeForCount and T is Ordinal: - var res = int(a) - while res <= int(b): - yield T(res) - inc(res) - else: - var res = a - while res <= b: - yield res - inc(res) + var res = a + while res <= b: + yield res + inc(res) - template dotdotImpl(t) {.dirty.} = - iterator `..`*(a, b: t): t {.inline.} = - ## A type specialized version of ``..`` for convenience so that - ## mixing integer types works better. - ## - ## See also: - ## * [..<](#..<.i,T,T) - var res = a - while res <= b: - yield res - inc(res) +dotdotImpl(int64) +dotdotImpl(int32) +dotdotImpl(uint64) +dotdotImpl(uint32) - dotdotImpl(int64) - dotdotImpl(int32) - dotdotImpl(uint64) - dotdotImpl(uint32) +iterator `..<`*[T](a, b: T): T {.inline.} = + mixin inc + var i = a + while i < b: + yield i + inc i - iterator `..<`*[T](a, b: T): T {.inline.} = - mixin inc - var i = a - while i < b: - yield i - inc i - - template dotdotLessImpl(t) {.dirty.} = - iterator `..<`*(a, b: t): t {.inline.} = - ## A type specialized version of ``..<`` for convenience so that - ## mixing integer types works better. - var res = a - while res < b: - yield res - inc(res) - - dotdotLessImpl(int64) - dotdotLessImpl(int32) - dotdotLessImpl(uint64) - dotdotLessImpl(uint32) - -else: # not defined(nimNewRoof) - iterator countup*[S, T](a: S, b: T, step = 1): T {.inline.} = - ## Counts from ordinal value `a` up to `b` (inclusive) with the given - ## step count. - ## - ## `S`, `T` may be any ordinal type, `step` may only be positive. - ## - ## **Note**: This fails to count to ``high(int)`` if T = int for - ## efficiency reasons. - ## - ## .. code-block:: Nim - ## for i in countup(3, 7): - ## echo i # => 3; 4; 5; 6; 7 - ## - ## for i in countup(2, 9, 3): - ## echo i # => 2; 5; 8 - when T is IntLikeForCount and T is Ordinal: - var res = int(a) - while res <= int(b): - yield T(res) - inc(res, step) - else: - var res = T(a) - while res <= b: - yield res - inc(res, step) - - iterator `..`*[S, T](a: S, b: T): T {.inline.} = - ## An alias for `countup(a, b, 1)`. - ## - ## See also: - ## * [..<](#..<.i,T,T) - ## - ## .. code-block:: Nim - ## for i in 3 .. 7: - ## echo i # => 3; 4; 5; 6; 7 - mixin inc - when T is IntLikeForCount and T is Ordinal: - var res = int(a) - while res <= int(b): - yield T(res) - inc(res) - else: - var res = T(a) - while res <= b: - yield res - inc(res) - - iterator `..<`*[S, T](a: S, b: T): T {.inline.} = - mixin inc - var i = T(a) - while i < b: - yield i - inc i +template dotdotLessImpl(t) {.dirty.} = + iterator `..<`*(a, b: t): t {.inline.} = + ## A type specialized version of `..<` for convenience so that + ## mixing integer types works better. + var res = a + while res < b: + yield res + inc(res) +dotdotLessImpl(int64) +dotdotLessImpl(int32) +dotdotLessImpl(uint64) +dotdotLessImpl(uint32) iterator `||`*[S, T](a: S, b: T, annotation: static string = "parallel for"): T {. inline, magic: "OmpParFor", sideEffect.} = @@ -189,9 +146,9 @@ iterator `||`*[S, T](a: S, b: T, annotation: static string = "parallel for"): T ## for further information. ## ## Note that the compiler maps that to - ## the ``#pragma omp parallel for`` construct of `OpenMP`:idx: and as + ## the `#pragma omp parallel for` construct of `OpenMP`:idx: and as ## such isn't aware of the parallelism in your code! Be careful! Later - ## versions of ``||`` will get proper support by Nim's code generator + ## versions of `||` will get proper support by Nim's code generator ## and GC. discard @@ -207,8 +164,8 @@ iterator `||`*[S, T](a: S, b: T, step: Positive, annotation: static string = "pa ## for further information. ## ## Note that the compiler maps that to - ## the ``#pragma omp parallel for`` construct of `OpenMP`:idx: and as + ## the `#pragma omp parallel for` construct of `OpenMP`:idx: and as ## such isn't aware of the parallelism in your code! Be careful! Later - ## versions of ``||`` will get proper support by Nim's code generator + ## versions of `||` will get proper support by Nim's code generator ## and GC. discard From 17a835a7b27c557ebb89c3d1bb44a9e5e6ce8ffb Mon Sep 17 00:00:00 2001 From: Federico Ceratto Date: Wed, 9 Dec 2020 15:11:51 +0000 Subject: [PATCH 1151/3768] Zsh completion (#16261) * Add --cpu to zsh completion * Add --gc to zsh completion * Add --os to zsh completion * Add --panics to zsh completion * Add --verbosity to zsh completion * Add --hints to zsh completion * Update tools/nim.zsh-completion Co-authored-by: Juan Carlos * Update tools/nim.zsh-completion Co-authored-by: Andreas Rumpf --- tools/nim.zsh-completion | 74 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/tools/nim.zsh-completion b/tools/nim.zsh-completion index 38ed93f73..f7aeb72c6 100644 --- a/tools/nim.zsh-completion +++ b/tools/nim.zsh-completion @@ -68,6 +68,80 @@ _nim() { '*--app=gui[generate a GUI app]' \ '*--app=lib[generate a dynamic library]' \ '*--app=staticlib[generate a static library]' \ + '*--cpu=alpha[compile for Alpha architecture]' \ + '*--cpu=amd64[compile for x86_64 architecture]' \ + '*--cpu=arm[compile for ARM architecture]' \ + '*--cpu=arm64[compile for ARM64 architecture]' \ + '*--cpu=avr[compile for AVR architecture]' \ + '*--cpu=esp[compile for ESP architecture]' \ + '*--cpu=hppa[compile for HPPA architecture]' \ + '*--cpu=i386[compile for i386 architecture]' \ + '*--cpu=ia64[compile for ia64 architecture]' \ + '*--cpu=js[compile to JavaScript]' \ + '*--cpu=m68k[compile for m68k architecture]' \ + '*--cpu=mips[compile for MIPS architecture]' \ + '*--cpu=mipsel[compile for MIPS EL architecture]' \ + '*--cpu=mips64[compile for MIPS64 architecture]' \ + '*--cpu=mips64el[compile for MIPS64 EL architecture]' \ + '*--cpu=msp430[compile for msp430 architecture]' \ + '*--cpu=nimvm[compile for Nim VM]' \ + '*--cpu=powerpc[compile for PowerPC architecture]' \ + '*--cpu=powerpc64[compile for PowerPC64 architecture]' \ + '*--cpu=powerpc64el[compile for PowerPC64 EL architecture]' \ + '*--cpu=riscv32[compile for RISC-V 32 architecture]' \ + '*--cpu=riscv64[compile for RISC-V 64 architecture]' \ + '*--cpu=sparc[compile for SPARC architecture]' \ + '*--cpu=sparc64[compile for SPARC64 architecture]' \ + '*--cpu=vm[compile for Nim VM]' \ + '*--cpu=wasm32[compile to WASM 32]' \ + '*--gc=refc[use reference counting garbage collection]' \ + '*--gc=arc[use ARC garbage collection]' \ + '*--gc=orc[use ORC garbage collection]' \ + '*--gc=markAndSweep[use mark-and-sweep garbage collection]' \ + '*--gc=boehm[use Boehm garbage collection]' \ + '*--gc=go[use Go garbage collection]' \ + '*--gc=regions[use region-based memory management]' \ + '*--gc=none[disable garbage collection]' \ + '*--os=Standalone[generate a stand-alone executable]' \ + '*--os=AIX[compile for AIX]' \ + '*--os=Amiga[compile for Amiga OS]' \ + '*--os=Android[compile for Android]' \ + '*--os=Any[compile for any OS]' \ + '*--os=Atari[compile for Atari]' \ + '*--os=DOS[compile for DOS]' \ + '*--os=DragonFly[compile for DragonFly]' \ + '*--os=FreeBSD[compile for FreeBSD]' \ + '*--os=FreeRTOS[compile for FreeRTOS]' \ + '*--os=Genode[compile for Genode]' \ + '*--os=Haiku[compile for Haiku]' \ + '*--os=iOS[compile for iOS]' \ + '*--os=Irix[compile for Irix]' \ + '*--os=Linux[compile for Linux]' \ + '*--os=MacOS[compile for MacOS]' \ + '*--os=MacOSX[compile for MacOSX]' \ + '*--os=MorphOS[compile for MorphOS]' \ + '*--os=NetBSD[compile for NetBSD]' \ + '*--os=Netware[compile for Netware]' \ + '*--os=NimVM[compile for NimVM]' \ + '*--os=NintendoSwitch[compile for NintendoSwitch]' \ + '*--os=OS2[compile for OS2]' \ + '*--os=OpenBSD[compile for OpenBSD]' \ + '*--os=PalmOS[compile for PalmOS]' \ + '*--os=QNX[compile for QNX]' \ + '*--os=SkyOS[compile for SkyOS]' \ + '*--os=Solaris[compile for Solaris]' \ + '*--os=VxWorks[compile for VxWorks]' \ + '*--os=Windows[compile for Windows]' \ + '*--os=JS[generate javascript]' \ + '*--panics=off[turn panics into process terminations: off by default]' \ + '*--panics=on[turn panics into process terminations]' \ + '*--verbosity=0[set verbosity to 0]' \ + '*--verbosity=1[set verbosity to 1 (default)]' \ + '*--verbosity=2[set verbosity to 2]' \ + '*--verbosity=3[set verbosity to 3]' \ + '*--hints=on[print compilation hints]' \ + '*--hints=off[disable compilation hints]' \ + '*--hints=list[print compilation hints list]' \ ':filename:_files -g"*.nim"' } From 87e634aab3182d6e9022f108bd6936a58682f01c Mon Sep 17 00:00:00 2001 From: ee7 <45465154+ee7@users.noreply.github.com> Date: Wed, 9 Dec 2020 16:17:50 +0100 Subject: [PATCH 1152/3768] rationals.nim: Use `func` everywhere (#16302) --- lib/pure/rationals.nim | 82 ++++++++++++++++----------------- tests/effects/tstrict_funcs.nim | 1 + 2 files changed, 42 insertions(+), 41 deletions(-) diff --git a/lib/pure/rationals.nim b/lib/pure/rationals.nim index 38a88ebc3..2a1a97d3b 100644 --- a/lib/pure/rationals.nim +++ b/lib/pure/rationals.nim @@ -18,28 +18,28 @@ type Rational*[T] = object ## a rational number, consisting of a numerator and denominator num*, den*: T -proc initRational*[T: SomeInteger](num, den: T): Rational[T] = +func initRational*[T: SomeInteger](num, den: T): Rational[T] = ## Create a new rational number. assert(den != 0, "a denominator of zero value is invalid") result.num = num result.den = den -proc `//`*[T](num, den: T): Rational[T] = initRational[T](num, den) +func `//`*[T](num, den: T): Rational[T] = initRational[T](num, den) ## A friendlier version of `initRational`. Example usage: ## ## .. code-block:: nim ## var x = 1//3 + 1//5 -proc `$`*[T](x: Rational[T]): string = +func `$`*[T](x: Rational[T]): string = ## Turn a rational number into a string. result = $x.num & "/" & $x.den -proc toRational*[T: SomeInteger](x: T): Rational[T] = +func toRational*[T: SomeInteger](x: T): Rational[T] = ## Convert some integer `x` to a rational number. result.num = x result.den = 1 -proc toRational*(x: float, +func toRational*(x: float, n: int = high(int) shr (sizeof(int) div 2 * 8)): Rational[int] = ## Calculates the best rational numerator and denominator ## that approximates to `x`, where the denominator is @@ -74,16 +74,16 @@ proc toRational*(x: float, ai = int(x) result = m11 // m21 -proc toFloat*[T](x: Rational[T]): float = +func toFloat*[T](x: Rational[T]): float = ## Convert a rational number `x` to a float. x.num / x.den -proc toInt*[T](x: Rational[T]): int = +func toInt*[T](x: Rational[T]): int = ## Convert a rational number `x` to an int. Conversion rounds towards 0 if ## `x` does not contain an integer value. x.num div x.den -proc reduce*[T: SomeInteger](x: var Rational[T]) = +func reduce*[T: SomeInteger](x: var Rational[T]) = ## Reduce rational `x`. let common = gcd(x.num, x.den) if x.den > 0: @@ -95,97 +95,97 @@ proc reduce*[T: SomeInteger](x: var Rational[T]) = else: raise newException(DivByZeroDefect, "division by zero") -proc `+` *[T](x, y: Rational[T]): Rational[T] = +func `+` *[T](x, y: Rational[T]): Rational[T] = ## Add two rational numbers. let common = lcm(x.den, y.den) result.num = common div x.den * x.num + common div y.den * y.num result.den = common reduce(result) -proc `+` *[T](x: Rational[T], y: T): Rational[T] = +func `+` *[T](x: Rational[T], y: T): Rational[T] = ## Add rational `x` to int `y`. result.num = x.num + y * x.den result.den = x.den -proc `+` *[T](x: T, y: Rational[T]): Rational[T] = +func `+` *[T](x: T, y: Rational[T]): Rational[T] = ## Add int `x` to rational `y`. result.num = x * y.den + y.num result.den = y.den -proc `+=` *[T](x: var Rational[T], y: Rational[T]) = +func `+=` *[T](x: var Rational[T], y: Rational[T]) = ## Add rational `y` to rational `x`. let common = lcm(x.den, y.den) x.num = common div x.den * x.num + common div y.den * y.num x.den = common reduce(x) -proc `+=` *[T](x: var Rational[T], y: T) = +func `+=` *[T](x: var Rational[T], y: T) = ## Add int `y` to rational `x`. x.num += y * x.den -proc `-` *[T](x: Rational[T]): Rational[T] = +func `-` *[T](x: Rational[T]): Rational[T] = ## Unary minus for rational numbers. result.num = -x.num result.den = x.den -proc `-` *[T](x, y: Rational[T]): Rational[T] = +func `-` *[T](x, y: Rational[T]): Rational[T] = ## Subtract two rational numbers. let common = lcm(x.den, y.den) result.num = common div x.den * x.num - common div y.den * y.num result.den = common reduce(result) -proc `-` *[T](x: Rational[T], y: T): Rational[T] = +func `-` *[T](x: Rational[T], y: T): Rational[T] = ## Subtract int `y` from rational `x`. result.num = x.num - y * x.den result.den = x.den -proc `-` *[T](x: T, y: Rational[T]): Rational[T] = +func `-` *[T](x: T, y: Rational[T]): Rational[T] = ## Subtract rational `y` from int `x`. result.num = x * y.den - y.num result.den = y.den -proc `-=` *[T](x: var Rational[T], y: Rational[T]) = +func `-=` *[T](x: var Rational[T], y: Rational[T]) = ## Subtract rational `y` from rational `x`. let common = lcm(x.den, y.den) x.num = common div x.den * x.num - common div y.den * y.num x.den = common reduce(x) -proc `-=` *[T](x: var Rational[T], y: T) = +func `-=` *[T](x: var Rational[T], y: T) = ## Subtract int `y` from rational `x`. x.num -= y * x.den -proc `*` *[T](x, y: Rational[T]): Rational[T] = +func `*` *[T](x, y: Rational[T]): Rational[T] = ## Multiply two rational numbers. result.num = x.num * y.num result.den = x.den * y.den reduce(result) -proc `*` *[T](x: Rational[T], y: T): Rational[T] = +func `*` *[T](x: Rational[T], y: T): Rational[T] = ## Multiply rational `x` with int `y`. result.num = x.num * y result.den = x.den reduce(result) -proc `*` *[T](x: T, y: Rational[T]): Rational[T] = +func `*` *[T](x: T, y: Rational[T]): Rational[T] = ## Multiply int `x` with rational `y`. result.num = x * y.num result.den = y.den reduce(result) -proc `*=` *[T](x: var Rational[T], y: Rational[T]) = +func `*=` *[T](x: var Rational[T], y: Rational[T]) = ## Multiply rationals `y` to `x`. x.num *= y.num x.den *= y.den reduce(x) -proc `*=` *[T](x: var Rational[T], y: T) = +func `*=` *[T](x: var Rational[T], y: T) = ## Multiply int `y` to rational `x`. x.num *= y reduce(x) -proc reciprocal*[T](x: Rational[T]): Rational[T] = +func reciprocal*[T](x: Rational[T]): Rational[T] = ## Calculate the reciprocal of `x`. (1/x) if x.num > 0: result.num = x.den @@ -196,63 +196,63 @@ proc reciprocal*[T](x: Rational[T]): Rational[T] = else: raise newException(DivByZeroDefect, "division by zero") -proc `/`*[T](x, y: Rational[T]): Rational[T] = +func `/`*[T](x, y: Rational[T]): Rational[T] = ## Divide rationals `x` by `y`. result.num = x.num * y.den result.den = x.den * y.num reduce(result) -proc `/`*[T](x: Rational[T], y: T): Rational[T] = +func `/`*[T](x: Rational[T], y: T): Rational[T] = ## Divide rational `x` by int `y`. result.num = x.num result.den = x.den * y reduce(result) -proc `/`*[T](x: T, y: Rational[T]): Rational[T] = +func `/`*[T](x: T, y: Rational[T]): Rational[T] = ## Divide int `x` by Rational `y`. result.num = x * y.den result.den = y.num reduce(result) -proc `/=`*[T](x: var Rational[T], y: Rational[T]) = +func `/=`*[T](x: var Rational[T], y: Rational[T]) = ## Divide rationals `x` by `y` in place. x.num *= y.den x.den *= y.num reduce(x) -proc `/=`*[T](x: var Rational[T], y: T) = +func `/=`*[T](x: var Rational[T], y: T) = ## Divide rational `x` by int `y` in place. x.den *= y reduce(x) -proc cmp*(x, y: Rational): int = +func cmp*(x, y: Rational): int = ## Compares two rationals. (x - y).num -proc `<` *(x, y: Rational): bool = +func `<` *(x, y: Rational): bool = (x - y).num < 0 -proc `<=` *(x, y: Rational): bool = +func `<=` *(x, y: Rational): bool = (x - y).num <= 0 -proc `==` *(x, y: Rational): bool = +func `==` *(x, y: Rational): bool = (x - y).num == 0 -proc abs*[T](x: Rational[T]): Rational[T] = +func abs*[T](x: Rational[T]): Rational[T] = result.num = abs x.num result.den = abs x.den -proc `div`*[T: SomeInteger](x, y: Rational[T]): T = +func `div`*[T: SomeInteger](x, y: Rational[T]): T = ## Computes the rational truncated division. (x.num * y.den) div (y.num * x.den) -proc `mod`*[T: SomeInteger](x, y: Rational[T]): Rational[T] = +func `mod`*[T: SomeInteger](x, y: Rational[T]): Rational[T] = ## Computes the rational modulo by truncated division (remainder). ## This is same as ``x - (x div y) * y``. result = ((x.num * y.den) mod (y.num * x.den)) // (x.den * y.den) reduce(result) -proc floorDiv*[T: SomeInteger](x, y: Rational[T]): T = +func floorDiv*[T: SomeInteger](x, y: Rational[T]): T = ## Computes the rational floor division. ## ## Floor division is conceptually defined as ``floor(x / y)``. @@ -261,15 +261,15 @@ proc floorDiv*[T: SomeInteger](x, y: Rational[T]): T = ## rounds down. floorDiv(x.num * y.den, y.num * x.den) -proc floorMod*[T: SomeInteger](x, y: Rational[T]): Rational[T] = +func floorMod*[T: SomeInteger](x, y: Rational[T]): Rational[T] = ## Computes the rational modulo by floor division (modulo). ## ## This is same as ``x - floorDiv(x, y) * y``. - ## This proc behaves the same as the ``%`` operator in python. + ## This func behaves the same as the ``%`` operator in python. result = floorMod(x.num * y.den, y.num * x.den) // (x.den * y.den) reduce(result) -proc hash*[T](x: Rational[T]): Hash = +func hash*[T](x: Rational[T]): Hash = ## Computes hash for rational `x` # reduce first so that hash(x) == hash(y) for x == y var copy = x diff --git a/tests/effects/tstrict_funcs.nim b/tests/effects/tstrict_funcs.nim index 7a0f1ee32..d30326123 100644 --- a/tests/effects/tstrict_funcs.nim +++ b/tests/effects/tstrict_funcs.nim @@ -10,6 +10,7 @@ import httpcore, math, nre, + rationals, sequtils, strutils, uri From f344a704123a4b55e2b3b8a4f736a6739d0683bb Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 9 Dec 2020 07:19:39 -0800 Subject: [PATCH 1153/3768] fix #16150 improve type mismatch errors (#16152) * fix #16150 improve type mismatch errors * allow -d:nimLegacyTypeMismatch * address comment --- changelog.md | 2 ++ compiler/sem.nim | 2 +- compiler/semcall.nim | 2 +- compiler/semexprs.nim | 2 +- compiler/semfields.nim | 5 +++-- compiler/types.nim | 7 +++++-- tests/config.nims | 1 + 7 files changed, 14 insertions(+), 7 deletions(-) diff --git a/changelog.md b/changelog.md index bb49dc7f6..0e96c868a 100644 --- a/changelog.md +++ b/changelog.md @@ -77,6 +77,8 @@ - Added `nim --eval:cmd` to evaluate a command directly, see `nim --help`. - VM now supports `addr(mystring[ind])` (index + index assignment) +- Type mismatch errors now show more context, use `-d:nimLegacyTypeMismatch` for previous + behavior. ## Tool changes diff --git a/compiler/sem.nim b/compiler/sem.nim index f90d9e1f9..0fec8b7e3 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -96,7 +96,7 @@ proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode = else: result = indexTypesMatch(c, formal, arg.typ, arg) if result == nil: - typeMismatch(c.config, info, formal, arg.typ) + typeMismatch(c.config, info, formal, arg.typ, arg) # error correction: result = copyTree(arg) result.typ = formal diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 70a7e099c..1e8da0298 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -476,7 +476,7 @@ proc inferWithMetatype(c: PContext, formal: PType, result.typ = generateTypeInstance(c, m.bindings, arg.info, formal.skipTypes({tyCompositeTypeClass})) else: - typeMismatch(c.config, arg.info, formal, arg.typ) + typeMismatch(c.config, arg.info, formal, arg.typ, arg) # error correction: result = copyTree(arg) result.typ = formal diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 8728af27c..3bbf353b1 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1745,7 +1745,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = c.p.resultSym.typ = rhsTyp c.p.owner.typ[0] = rhsTyp else: - typeMismatch(c.config, n.info, lhs.typ, rhsTyp) + typeMismatch(c.config, n.info, lhs.typ, rhsTyp, rhs) borrowCheck(c, n, lhs, rhs) n[1] = fitNode(c, le, rhs, goodLineInfo(n[1])) diff --git a/compiler/semfields.nim b/compiler/semfields.nim index d7563e33f..602a7199d 100644 --- a/compiler/semfields.nim +++ b/compiler/semfields.nim @@ -127,9 +127,10 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode = localError(c.config, n.info, errGenerated, "no object or tuple type") return result for i in 1.." % actualStr + if conf.isDefined("nimLegacyTypeMismatch"): + msg.add " got <$1>" % actualStr + else: + msg.add " got '$1' for '$2'" % [actualStr, n.renderTree] if verbose: msg.addDeclaredLoc(conf, actual) msg.add "\n" diff --git a/tests/config.nims b/tests/config.nims index 640df9cad..ac5e019f5 100644 --- a/tests/config.nims +++ b/tests/config.nims @@ -8,3 +8,4 @@ switch("path", "$lib/../testament/lib") switch("colors", "off") switch("listFullPaths", "off") switch("excessiveStackTrace", "off") +switch("define", "nimLegacyTypeMismatch") From 82bb4db4b72e4ca61e2853755fd8ed8527c1a42a Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 9 Dec 2020 15:57:52 -0800 Subject: [PATCH 1154/3768] unittest: use defines instead of env vars (#16165) * unittest: use defines instead of env vars * use defines in testament * fixup Co-authored-by: Andreas Rumpf --- lib/pure/unittest.nim | 90 +++++++++++++++++++++++--------------- testament/testament.nim | 3 -- tests/config.nims | 5 +++ tests/js/tunittests.nim | 13 ------ tests/stdlib/ttimes.nim | 6 --- tests/stdlib/tunittest.nim | 1 + 6 files changed, 61 insertions(+), 57 deletions(-) delete mode 100644 tests/js/tunittests.nim diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim index c0b2e7c1e..ab45f78dc 100644 --- a/lib/pure/unittest.nim +++ b/lib/pure/unittest.nim @@ -139,21 +139,19 @@ type ConsoleOutputFormatter* = ref object of OutputFormatter colorOutput: bool ## Have test results printed in color. - ## Default is true for the non-js target, - ## for which ``stdout`` is a tty. - ## Setting the environment variable - ## ``NIMTEST_COLOR`` to ``always`` or - ## ``never`` changes the default for the - ## non-js target to true or false respectively. - ## The deprecated environment variable - ## ``NIMTEST_NO_COLOR``, when set, - ## changes the default to true, if - ## ``NIMTEST_COLOR`` is undefined. + ## Default is `auto` depending on `isatty(stdout)`, or override it with + ## `-d:nimUnittestColor:auto|on|off`. + ## + ## Deprecated: Setting the environment variable `NIMTEST_COLOR` to `always` + ## or `never` changes the default for the non-js target to true or false respectively. + ## Deprecated: the environment variable `NIMTEST_NO_COLOR`, when set, changes the + ## default to true, if `NIMTEST_COLOR` is undefined. outputLevel: OutputLevel ## Set the verbosity of test results. - ## Default is ``PRINT_ALL``, unless - ## the ``NIMTEST_OUTPUT_LVL`` environment - ## variable is set for the non-js target. + ## Default is `PRINT_ALL`, or override with: + ## `-d:nimUnittestOutputLevel:PRINT_ALL|PRINT_FAILURES|PRINT_NONE`. + ## + ## Deprecated: the `NIMTEST_OUTPUT_LVL` environment variable is set for the non-js target. isInSuite: bool isInTest: bool @@ -166,17 +164,31 @@ type var abortOnError* {.threadvar.}: bool ## Set to true in order to quit ## immediately on fail. Default is false, - ## unless the ``NIMTEST_ABORT_ON_ERROR`` - ## environment variable is set for - ## the non-js target. + ## or override with `-d:nimUnittestAbortOnError:on|off`. + ## + ## Deprecated: can also override depending on whether + ## `NIMTEST_ABORT_ON_ERROR` environment variable is set. checkpoints {.threadvar.}: seq[string] formatters {.threadvar.}: seq[OutputFormatter] testsFilters {.threadvar.}: HashSet[string] disabledParamFiltering {.threadvar.}: bool +const + outputLevelDefault = PRINT_ALL + nimUnittestOutputLevel {.strdefine.} = $outputLevelDefault + nimUnittestColor {.strdefine.} = "auto" ## auto|on|off + nimUnittestAbortOnError {.booldefine.} = false + +template deprecateEnvVarHere() = + # xxx issue a runtime warning to deprecate this envvar. + discard + +abortOnError = nimUnittestAbortOnError when declared(stdout): - abortOnError = existsEnv("NIMTEST_ABORT_ON_ERROR") + if existsEnv("NIMTEST_ABORT_ON_ERROR"): + deprecateEnvVarHere() + abortOnError = true method suiteStarted*(formatter: OutputFormatter, suiteName: string) {.base, gcsafe.} = discard @@ -202,36 +214,44 @@ proc delOutputFormatter*(formatter: OutputFormatter) = proc resetOutputFormatters* {.since: (1, 1).} = formatters = @[] -proc newConsoleOutputFormatter*(outputLevel: OutputLevel = OutputLevel.PRINT_ALL, +proc newConsoleOutputFormatter*(outputLevel: OutputLevel = outputLevelDefault, colorOutput = true): ConsoleOutputFormatter = ConsoleOutputFormatter( outputLevel: outputLevel, colorOutput: colorOutput ) -proc defaultConsoleFormatter*(): ConsoleOutputFormatter = +proc colorOutput(): bool = + let color = nimUnittestColor + case color + of "auto": + when declared(stdout): result = isatty(stdout) + else: result = false + of "on": result = true + of "off": result = false + else: doAssert false, $color + when declared(stdout): - # Reading settings - # On a terminal this branch is executed - var envOutLvl = os.getEnv("NIMTEST_OUTPUT_LVL").string - var colorOutput = isatty(stdout) if existsEnv("NIMTEST_COLOR"): + deprecateEnvVarHere() let colorEnv = getEnv("NIMTEST_COLOR") if colorEnv == "never": - colorOutput = false + result = false elif colorEnv == "always": - colorOutput = true + result = true elif existsEnv("NIMTEST_NO_COLOR"): - colorOutput = false - var outputLevel = OutputLevel.PRINT_ALL - if envOutLvl.len > 0: - for opt in countup(low(OutputLevel), high(OutputLevel)): - if $opt == envOutLvl: - outputLevel = opt - break - result = newConsoleOutputFormatter(outputLevel, colorOutput) - else: - result = newConsoleOutputFormatter() + deprecateEnvVarHere() + result = false + +proc defaultConsoleFormatter*(): ConsoleOutputFormatter = + var colorOutput = colorOutput() + var outputLevel = nimUnittestOutputLevel.parseEnum[:OutputLevel] + when declared(stdout): + const a = "NIMTEST_OUTPUT_LVL" + if existsEnv(a): + # xxx issue a warning to deprecate this envvar. + outputLevel = getEnv(a).parseEnum[:OutputLevel] + result = newConsoleOutputFormatter(outputLevel, colorOutput) method suiteStarted*(formatter: ConsoleOutputFormatter, suiteName: string) = template rawPrint() = echo("\n[Suite] ", suiteName) diff --git a/testament/testament.nim b/testament/testament.nim index 58b3a8040..a301b018c 100644 --- a/testament/testament.nim +++ b/testament/testament.nim @@ -661,9 +661,6 @@ proc loadSkipFrom(name: string): seq[string] = result.add sline proc main() = - os.putEnv "NIMTEST_COLOR", "never" - os.putEnv "NIMTEST_OUTPUT_LVL", "PRINT_FAILURES" - azure.init() backend.open() var optPrintResults = false diff --git a/tests/config.nims b/tests/config.nims index ac5e019f5..e0b8502ee 100644 --- a/tests/config.nims +++ b/tests/config.nims @@ -8,4 +8,9 @@ switch("path", "$lib/../testament/lib") switch("colors", "off") switch("listFullPaths", "off") switch("excessiveStackTrace", "off") + +# for std/unittest +switch("define", "nimUnittestOutputLevel:PRINT_FAILURES") +switch("define", "nimUnittestColor:off") + switch("define", "nimLegacyTypeMismatch") diff --git a/tests/js/tunittests.nim b/tests/js/tunittests.nim deleted file mode 100644 index 0d934a82b..000000000 --- a/tests/js/tunittests.nim +++ /dev/null @@ -1,13 +0,0 @@ -discard """ - output: ''' - -[Suite] Bacon - [OK] >:) -''' -""" - -import unittest - -suite "Bacon": - test ">:)": - check(true == true) diff --git a/tests/stdlib/ttimes.nim b/tests/stdlib/ttimes.nim index e6305b2d0..4d89ffa1a 100644 --- a/tests/stdlib/ttimes.nim +++ b/tests/stdlib/ttimes.nim @@ -7,12 +7,6 @@ import times, strutils, unittest when not defined(js): import os -# Normally testament configures unittest with environment variables, -# but that doesn't work for the JS target. So instead we must set the correct -# settings here. -addOutputFormatter( - newConsoleOutputFormatter(PRINT_FAILURES, colorOutput = false)) - proc staticTz(hours, minutes, seconds: int = 0): Timezone {.noSideEffect.} = let offset = hours * 3600 + minutes * 60 + seconds diff --git a/tests/stdlib/tunittest.nim b/tests/stdlib/tunittest.nim index 8e82a0c97..4b82df67b 100644 --- a/tests/stdlib/tunittest.nim +++ b/tests/stdlib/tunittest.nim @@ -19,6 +19,7 @@ discard """ [Suite] test name filtering ''' +targets: "c js" """ import unittest, sequtils From 73299b048f57838d83a3a8159909386a0905e553 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Thu, 10 Dec 2020 14:58:42 +0800 Subject: [PATCH 1155/3768] enable tests for random (#16297) --- tests/stdlib/trandom.nim | 73 ++++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 33 deletions(-) diff --git a/tests/stdlib/trandom.nim b/tests/stdlib/trandom.nim index c7c6a3f56..f8797944e 100644 --- a/tests/stdlib/trandom.nim +++ b/tests/stdlib/trandom.nim @@ -1,59 +1,66 @@ discard """ - action: compile + joinable: false + targets: "c js" """ -import random +import std/[random, stats] + +randomize(233) proc main = var occur: array[1000, int] - var x = 8234 for i in 0..100_000: - x = rand(high(occur)) + let x = rand(high(occur)) inc occur[x] - for i, oc in occur: - if oc < 69: - doAssert false, "too few occurrences of " & $i - elif oc > 150: - doAssert false, "too many occurrences of " & $i - when false: - var rs: RunningStat - for j in 1..5: - for i in 1 .. 1_000: - rs.push(gauss()) - echo("mean: ", rs.mean, - " stdDev: ", rs.standardDeviation(), - " min: ", rs.min, - " max: ", rs.max) - rs.clear() + doAssert max(occur) <= 140 and min(occur) >= 60 # gives some slack var a = [0, 1] shuffle(a) - doAssert a[0] == 1 - doAssert a[1] == 0 + doAssert a in [[0,1], [1,0]] doAssert rand(0) == 0 doAssert sample("a") == 'a' when compileOption("rangeChecks"): - try: + doAssertRaises(RangeDefect): discard rand(-1) - doAssert false - except RangeDefect: - discard - try: + doAssertRaises(RangeDefect): discard rand(-1.0) - doAssert false - except RangeDefect: - discard - # don't use causes integer overflow doAssert compiles(rand[int](low(int) .. high(int))) -randomize(223) -for i in 0 .. 10: - main() +main() + +import math + +block: + when not defined(js): + doAssert almostEqual(rand(12.5), 4.012897747078944) + doAssert almostEqual(rand(2233.3322), 879.702755321298) + + type DiceRoll = range[0..6] + doAssert rand(DiceRoll).int == 4 + +var rs: RunningStat +for j in 1..5: + for i in 1 .. 100_000: + rs.push(gauss()) + doAssert abs(rs.mean-0) < 0.08, $rs.mean + doAssert abs(rs.standardDeviation()-1.0) < 0.1 + let bounds = [3.5, 5.0] + for a in [rs.max, -rs.min]: + doAssert a >= bounds[0] and a <= bounds[1] + rs.clear() + +block: + type DiceRoll = range[3..6] + var flag = false + for i in 0..<100: + if rand(5.DiceRoll) < 3: + flag = true + doAssert flag # because of: rand(max: int): int From 1a07f572f4871244efb657ff5ebcb27636a0b18e Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 10 Dec 2020 15:24:44 -0800 Subject: [PATCH 1156/3768] unbreak CI, disable fidget, refs #16315 (#16316) --- testament/important_packages.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 2da10e749..361ecaa88 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -37,7 +37,7 @@ pkg1 "delaunay" pkg1 "docopt" pkg1 "easygl", "nim c -o:egl -r src/easygl.nim", "https://github.com/jackmott/easygl" pkg1 "elvis" -pkg1 "fidget" +# pkg1 "fidget" # pending bug #16315 and https://github.com/guzba/supersnappy/issues/3 pkg1 "fragments", "nim c -r fragments/dsl.nim" pkg1 "gara" pkg1 "glob" From bc84bd5d66e62c0e39ed781ddeb6a03e6d4163b2 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Fri, 11 Dec 2020 13:02:43 +0800 Subject: [PATCH 1157/3768] bring back fidget (#16319) --- testament/important_packages.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 361ecaa88..2da10e749 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -37,7 +37,7 @@ pkg1 "delaunay" pkg1 "docopt" pkg1 "easygl", "nim c -o:egl -r src/easygl.nim", "https://github.com/jackmott/easygl" pkg1 "elvis" -# pkg1 "fidget" # pending bug #16315 and https://github.com/guzba/supersnappy/issues/3 +pkg1 "fidget" pkg1 "fragments", "nim c -r fragments/dsl.nim" pkg1 "gara" pkg1 "glob" From bb1c962286922487c5243ca7304fd95fdd27ea53 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 11 Dec 2020 01:35:05 -0800 Subject: [PATCH 1158/3768] fix partially #13115 (now works for cpp; but still fails for js on openbsd) (#16167) * fix partially #13115 properly (works for c,js,cpp,vm; still fails for js on openbsd) * address comment: also test with -d:danger, -d:debug --- lib/system/excpt.nim | 32 +++++++++++++++++--------- tests/exception/t13115.nim | 46 +++++++++++++++++++++++++++++--------- 2 files changed, 58 insertions(+), 20 deletions(-) diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index f73a88e30..aec25e3f3 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -13,6 +13,8 @@ import std/private/miscdollars import stacktraces +const noStacktraceAvailable = "No stack traceback available\n" + var errorMessageWriter*: (proc(msg: string) {.tags: [WriteIOEffect], benign, nimcall.}) @@ -36,6 +38,10 @@ else: proc writeToStdErr(msg: cstring, length: int) = discard MessageBoxA(nil, msg, nil, 0) +proc writeToStdErr(msg: string) {.inline.} = + # fix bug #13115: handles correctly '\0' unlike default implicit conversion to cstring + writeToStdErr(msg.cstring, msg.len) + proc showErrorMessage(data: cstring, length: int) {.gcsafe, raises: [].} = var toWrite = true if errorMessageWriter != nil: @@ -51,6 +57,9 @@ proc showErrorMessage(data: cstring, length: int) {.gcsafe, raises: [].} = else: writeToStdErr(data, length) +proc showErrorMessage2(data: string) {.inline.} = + showErrorMessage(data.cstring, data.len) + proc chckIndx(i, a, b: int): int {.inline, compilerproc, benign.} proc chckRange(i, a, b: int): int {.inline, compilerproc, benign.} proc chckRangeF(x, a, b: float): float {.inline, compilerproc, benign.} @@ -123,11 +132,11 @@ proc popSafePoint {.compilerRtl, inl.} = proc pushCurrentException(e: sink(ref Exception)) {.compilerRtl, inl.} = e.up = currException currException = e - #showErrorMessage "A" + #showErrorMessage2 "A" proc popCurrentException {.compilerRtl, inl.} = currException = currException.up - #showErrorMessage "B" + #showErrorMessage2 "B" proc popCurrentExceptionEx(id: uint) {.compilerRtl.} = discard "only for bootstrapping compatbility" @@ -305,7 +314,7 @@ when hasSomeStackTrace: auxWriteStackTraceWithOverride(s) elif NimStackTrace: if framePtr == nil: - add(s, "No stack traceback available\n") + add(s, noStacktraceAvailable) else: add(s, "Traceback (most recent call last)\n") auxWriteStackTrace(framePtr, s) @@ -313,7 +322,7 @@ when hasSomeStackTrace: add(s, "Traceback from system (most recent call last)\n") auxWriteStackTraceWithBacktrace(s) else: - add(s, "No stack traceback available\n") + add(s, noStacktraceAvailable) proc rawWriteStackTrace(s: var seq[StackTraceEntry]) = when defined(nimStackTraceOverride): @@ -363,7 +372,7 @@ proc reportUnhandledErrorAux(e: ref Exception) {.nodestroy.} = if onUnhandledException != nil: onUnhandledException(buf) else: - showErrorMessage(buf, buf.len) + showErrorMessage2(buf) `=destroy`(buf) else: # ugly, but avoids heap allocations :-) @@ -504,16 +513,16 @@ proc writeStackTrace() = when hasSomeStackTrace: var s = "" rawWriteStackTrace(s) - cast[proc (s: cstring, length: int) {.noSideEffect, tags: [], nimcall, raises: [].}](showErrorMessage)(s, s.len) else: - cast[proc (s: cstring, length: int) {.noSideEffect, tags: [], nimcall, raises: [].}](showErrorMessage)("No stack traceback available\n", 32) + let s = noStacktraceAvailable + cast[proc (s: string) {.noSideEffect, tags: [], nimcall, raises: [].}](showErrorMessage2)(s) proc getStackTrace(): string = when hasSomeStackTrace: result = "" rawWriteStackTrace(result) else: - result = "No stack traceback available\n" + result = noStacktraceAvailable proc getStackTrace(e: ref Exception): string = if not isNil(e): @@ -543,7 +552,7 @@ proc callDepthLimitReached() {.noinline.} = $nimCallDepthLimit & " function calls). You can change it with " & "-d:nimCallDepthLimit= but really try to avoid deep " & "recursions instead.\n" - showErrorMessage(msg, msg.len) + showErrorMessage2(msg) quit(1) proc nimFrame(s: PFrame) {.compilerRtl, inl, raises: [].} = @@ -627,13 +636,16 @@ when not defined(noSignalHandler) and not defined(useNimRtl): var buf = newStringOfCap(2000) rawWriteStackTrace(buf) processSignal(sign, buf.add) # nice hu? currying a la Nim :-) - showErrorMessage(buf, buf.len) + showErrorMessage2(buf) when not usesDestructors: GC_enable() else: var msg: cstring template asgn(y) = msg = y processSignal(sign, asgn) + # xxx use string for msg instead of cstring, and here use showErrorMessage2(msg) + # unless there's a good reason to use cstring in signal handler to avoid + # using gc? showErrorMessage(msg, msg.len) quit(1) # always quit when SIGABRT diff --git a/tests/exception/t13115.nim b/tests/exception/t13115.nim index 53e078076..ee1daed26 100644 --- a/tests/exception/t13115.nim +++ b/tests/exception/t13115.nim @@ -1,12 +1,38 @@ -discard """ - exitcode: 1 - targets: "c" - matrix: "-d:debug; -d:release" - outputsub: ''' and works fine! [Exception]''' -""" +const msg = "This char is `" & '\0' & "` and works fine!" -# bug #13115 -# xxx bug: doesn't yet work for cpp +when defined nim_t13115: + # bug #13115 + template fn = + raise newException(Exception, msg) + when defined nim_t13115_static: + static: fn() + fn() +else: + import std/[osproc,strformat,os,strutils] + proc main = + const nim = getCurrentCompilerExe() + const file = currentSourcePath + for b in "c js cpp".split: + when defined(openbsd): + if b == "js": + # xxx bug: pending #13115 + # remove special case once nodejs updated >= 12.16.2 + # refs https://github.com/nim-lang/Nim/pull/16167#issuecomment-738270751 + continue -var msg = "This char is `" & '\0' & "` and works fine!" -raise newException(Exception, msg) + # save CI time by avoiding mostly redundant combinations as far as this bug is concerned + var opts = case b + of "c": @["", "-d:nim_t13115_static", "-d:danger", "-d:debug"] + of "js": @["", "-d:nim_t13115_static"] + else: @[""] + + for opt in opts: + let cmd = fmt"{nim} r -b:{b} -d:nim_t13115 {opt} --hints:off {file}" + let (outp, exitCode) = execCmdEx(cmd) + when defined windows: + # `\0` not preserved on windows + doAssert "` and works fine!" in outp, cmd & "\n" & msg + else: + doAssert msg in outp, cmd & "\n" & msg + doAssert exitCode == 1 + main() From 0b73106ccf983565d34654ee8b1167827b53b01a Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 11 Dec 2020 02:01:43 -0800 Subject: [PATCH 1159/3768] add math.isNaN (#16179) * add math.isNaN * isNaN now works with --passc:-ffast-math; tests * Update lib/pure/math.nim Co-authored-by: Andreas Rumpf --- changelog.md | 2 ++ lib/js/dom.nim | 1 + lib/pure/math.nim | 21 +++++++++++++++++++++ tests/stdlib/tmath.nim | 31 ++++++++++++++++++++++++++----- 4 files changed, 50 insertions(+), 5 deletions(-) diff --git a/changelog.md b/changelog.md index 0e96c868a..ab323337c 100644 --- a/changelog.md +++ b/changelog.md @@ -58,6 +58,8 @@ - `strscans.scanf` now supports parsing single characters. +- Added `math.isNaN`. + ## Language changes - `nimscript` now handles `except Exception as e`. diff --git a/lib/js/dom.nim b/lib/js/dom.nim index b73a85291..b74a58a1a 100644 --- a/lib/js/dom.nim +++ b/lib/js/dom.nim @@ -1683,6 +1683,7 @@ proc decodeURIComponent*(uri: cstring): cstring {.importc, nodecl.} proc encodeURIComponent*(uri: cstring): cstring {.importc, nodecl.} proc isFinite*(x: BiggestFloat): bool {.importc, nodecl.} proc isNaN*(x: BiggestFloat): bool {.importc, nodecl.} + ## see also `math.isNaN`. proc newEvent*(name: cstring): Event {.importcpp: "new Event(@)", constructor.} diff --git a/lib/pure/math.nim b/lib/pure/math.nim index b2433d309..5f3261f1a 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -58,6 +58,10 @@ import std/private/since import bitops, fenv +when defined(c) or defined(cpp): + proc c_isnan(x: float): bool {.importc: "isnan", header: "".} + # a generic like `x: SomeFloat` might work too if this is implemented via a C macro. + func binom*(n, k: int): int = ## Computes the `binomial coefficient `_. runnableExamples: @@ -133,10 +137,27 @@ type fcInf, ## value is positive infinity fcNegInf ## value is negative infinity +func isNaN*(x: SomeFloat): bool {.inline, since: (1,5,1).} = + ## Returns whether `x` is a `NaN`, more efficiently than via `classify(x) == fcNan`. + ## Works even with: `--passc:-ffast-math`. + runnableExamples: + doAssert NaN.isNaN + doAssert not Inf.isNaN + doAssert isNaN(Inf - Inf) + doAssert not isNan(3.1415926) + doAssert not isNan(0'f32) + + template fn: untyped = result = x != x + when nimvm: fn() + else: + when defined(js): fn() + else: result = c_isnan(x) + func classify*(x: float): FloatClass = ## Classifies a floating point value. ## ## Returns ``x``'s class as specified by `FloatClass enum<#FloatClass>`_. + ## Doesn't work with: `--passc:-ffast-math`. runnableExamples: doAssert classify(0.3) == fcNormal doAssert classify(0.0) == fcZero diff --git a/tests/stdlib/tmath.nim b/tests/stdlib/tmath.nim index ac5ce4c90..18e42cbdc 100644 --- a/tests/stdlib/tmath.nim +++ b/tests/stdlib/tmath.nim @@ -12,8 +12,11 @@ discard """ [Suite] ^ ''' +matrix:"; -d:nimTmathCase2 -d:danger --passc:-ffast-math" """ +# xxx: fix bugs for js then add: targets:"c js" + import math, random, os import unittest import sets, tables @@ -37,12 +40,16 @@ suite "random int": var rand: int for i in 1..1000: rand = rand(100..1000) - check rand < 1000 + when defined(js): # xxx bug: otherwise fails + check rand <= 1000 + else: + check rand < 1000 check rand >= 100 test " again gives new numbers": var rand1 = rand(1000000) - os.sleep(200) + when not defined(js): + os.sleep(200) var rand2 = rand(1000000) check rand1 != rand2 @@ -72,7 +79,8 @@ suite "random float": test " again gives new numbers": var rand1:float = rand(1000000.0) - os.sleep(200) + when not defined(js): + os.sleep(200) var rand2:float = rand(1000000.0) check rand1 != rand2 @@ -151,6 +159,7 @@ block: when not defined(js): # Check for no side effect annotation proc mySqrt(num: float): float {.noSideEffect.} = + # xxx unused return sqrt(num) # check gamma function @@ -194,7 +203,8 @@ block: #special case doAssert(classify(trunc(1e1000000)) == fcInf) doAssert(classify(trunc(-1e1000000)) == fcNegInf) - doAssert(classify(trunc(0.0/0.0)) == fcNan) + when not defined(nimTmathCase2): + doAssert(classify(trunc(0.0/0.0)) == fcNan) doAssert(classify(trunc(0.0)) == fcZero) #trick the compiler to produce signed zero @@ -211,7 +221,8 @@ block: doAssert(trunc(0.1'f32) == 0) doAssert(classify(trunc(1e1000000'f32)) == fcInf) doAssert(classify(trunc(-1e1000000'f32)) == fcNegInf) - doAssert(classify(trunc(f_nan.float32)) == fcNan) + when not defined(nimTmathCase2): + doAssert(classify(trunc(f_nan.float32)) == fcNan) doAssert(classify(trunc(0.0'f32)) == fcZero) block: # sgn() tests @@ -272,3 +283,13 @@ block: doAssert log2(2.0'f32) == 1.0'f32 doAssert log2(1.0'f32) == 0.0'f32 doAssert classify(log2(0.0'f32)) == fcNegInf + +template main = + # xxx wrap all under `main` so it also gets tested in vm. + block: # isNaN + doAssert NaN.isNaN + doAssert not Inf.isNaN + doAssert isNaN(Inf - Inf) + +main() +static: main() From b1c232aca1baf7d9ccc6c7a86e9ac5a127bc2354 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 12 Dec 2020 00:33:29 -0800 Subject: [PATCH 1160/3768] improve tests for collections (#16328) * improve tests for collections * remove remaining code blocks in deques.nim * improve runnableExamples --- lib/pure/collections/deques.nim | 108 ++++++++++------------ tests/collections/tcollections.nim | 69 ++------------ tests/stdlib/tcritbits.nim | 18 ++++ tests/stdlib/tdeque.nim | 111 ----------------------- tests/stdlib/tdeques.nim | 139 +++++++++++++++++++++++++++++ tests/stdlib/tlists.nim | 24 ++++- 6 files changed, 227 insertions(+), 242 deletions(-) delete mode 100644 tests/stdlib/tdeque.nim create mode 100644 tests/stdlib/tdeques.nim diff --git a/lib/pure/collections/deques.nim b/lib/pure/collections/deques.nim index 6df34bd3a..f6d0f945e 100644 --- a/lib/pure/collections/deques.nim +++ b/lib/pure/collections/deques.nim @@ -18,35 +18,32 @@ ## ## As such, a check to see if the deque is empty is needed before any ## access, unless your program logic guarantees it indirectly. -## -## .. code-block:: Nim -## import deques -## -## var a = initDeque[int]() -## -## doAssertRaises(IndexDefect, echo a[0]) -## -## for i in 1 .. 5: -## a.addLast(10*i) -## assert $a == "[10, 20, 30, 40, 50]" -## -## assert a.peekFirst == 10 -## assert a.peekLast == 50 -## assert len(a) == 5 -## -## assert a.popFirst == 10 -## assert a.popLast == 50 -## assert len(a) == 3 -## -## a.addFirst(11) -## a.addFirst(22) -## a.addFirst(33) -## assert $a == "[33, 22, 11, 20, 30, 40]" -## -## a.shrink(fromFirst = 1, fromLast = 2) -## assert $a == "[22, 11, 20]" -## -## + +runnableExamples: + var a = initDeque[int]() + + doAssertRaises(IndexDefect, echo a[0]) + + for i in 1 .. 5: + a.addLast(10*i) + assert $a == "[10, 20, 30, 40, 50]" + + assert a.peekFirst == 10 + assert a.peekLast == 50 + assert len(a) == 5 + + assert a.popFirst == 10 + assert a.popLast == 50 + assert len(a) == 3 + + a.addFirst(11) + a.addFirst(22) + a.addFirst(33) + assert $a == "[33, 22, 11, 20, 30, 40]" + + a.shrink(fromFirst = 1, fromLast = 2) + assert $a == "[22, 11, 20]" + ## **See also:** ## * `lists module `_ for singly and doubly linked lists and rings ## * `channels module `_ for inter-thread communication @@ -211,21 +208,14 @@ proc `[]=`*[T](deq: var Deque[T], i: BackwardsIndex, x: T) {.inline.} = iterator items*[T](deq: Deque[T]): T = ## Yields every element of `deq`. - ## - ## **Examples:** - ## - ## .. code-block:: - ## var a = initDeque[int]() - ## for i in 1 .. 3: - ## a.addLast(10*i) - ## - ## for x in a: # the same as: for x in items(a): - ## echo x - ## - ## # 10 - ## # 20 - ## # 30 - ## + runnableExamples: + var a = initDeque[int]() + for i in 1 .. 3: + a.addLast(10*i) + from sugar import collect + doAssert collect(for x in a: x) == [10, 20, 30] + # same as above: + doAssert collect(for x in items(a): x) == [10, 20, 30] var i = deq.head for c in 0 ..< deq.count: yield deq.data[i] @@ -249,21 +239,12 @@ iterator mitems*[T](deq: var Deque[T]): var T = iterator pairs*[T](deq: Deque[T]): tuple[key: int, val: T] = ## Yields every (position, value) of `deq`. - ## - ## **Examples:** - ## - ## .. code-block:: - ## var a = initDeque[int]() - ## for i in 1 .. 3: - ## a.addLast(10*i) - ## - ## for k, v in pairs(a): - ## echo "key: ", k, ", value: ", v - ## - ## # key: 0, value: 10 - ## # key: 1, value: 20 - ## # key: 2, value: 30 - ## + runnableExamples: + var a = initDeque[int]() + for i in 1 .. 3: + a.addLast(10*i) + from sugar import collect + doAssert collect(for k, v in pairs(a): (k, v)) == @[(0, 10), (1, 20), (2, 30)] var i = deq.head for c in 0 ..< deq.count: yield (c, deq.data[i]) @@ -274,10 +255,11 @@ proc contains*[T](deq: Deque[T], item: T): bool {.inline.} = ## ## Usually used via the ``in`` operator. ## It is the equivalent of ``deq.find(item) >= 0``. - ## - ## .. code-block:: Nim - ## if x in q: - ## assert q.contains(x) + runnableExamples: + var q = [7, 9].toDeque + assert 7 in q + assert q.contains 7 + assert 8 notin q for e in deq: if e == item: return true return false diff --git a/tests/collections/tcollections.nim b/tests/collections/tcollections.nim index 1e2858fb4..7677f7c1a 100644 --- a/tests/collections/tcollections.nim +++ b/tests/collections/tcollections.nim @@ -1,9 +1,10 @@ discard """ - output: "" + targets: "c js" """ -import sets, tables, deques, lists, critbits, sequtils +# see also: tdeques, tlists, tcritbits +import sets, tables, sequtils block tapply: var x = @[1, 2, 3] @@ -12,19 +13,6 @@ block tapply: x.applyIt(it+5000) doAssert x == @[5111, 5112, 5113] - -block tdeques: - proc index(self: Deque[int], idx: Natural): int = - self[idx] - - proc main = - var testDeque = initDeque[int]() - testDeque.addFirst(1) - assert testDeque.index(0) == 1 - - main() - - block tmapit: var x = @[1, 2, 3] # This mapIt call will run with preallocation because ``len`` is available. @@ -75,6 +63,8 @@ doAssert $(toOrderedSet([1, 2, 3])) == "{1, 2, 3}" doAssert $(toOrderedSet(["1", "2", "3"])) == """{"1", "2", "3"}""" doAssert $(toOrderedSet(['1', '2', '3'])) == """{'1', '2', '3'}""" +# see also: tcritbitsToString, tlistsToString + # Tests for tables when defined(nimIntHash1): doAssert $({1: "1", 2: "2"}.toTable) == """{1: "1", 2: "2"}""" @@ -83,55 +73,6 @@ else: let tabStr = $({"1": 1, "2": 2}.toTable) doAssert (tabStr == """{"2": 2, "1": 1}""" or tabStr == """{"1": 1, "2": 2}""") -# Tests for deques -block: - var d = initDeque[int]() - d.addLast(1) - doAssert $d == "[1]" -block: - var d = initDeque[string]() - d.addLast("1") - doAssert $d == """["1"]""" -block: - var d = initDeque[char]() - d.addLast('1') - doAssert $d == "['1']" - -# Tests for lists -block: - var l = initDoublyLinkedList[int]() - l.append(1) - l.append(2) - l.append(3) - doAssert $l == "[1, 2, 3]" -block: - var l = initDoublyLinkedList[string]() - l.append("1") - l.append("2") - l.append("3") - doAssert $l == """["1", "2", "3"]""" -block: - var l = initDoublyLinkedList[char]() - l.append('1') - l.append('2') - l.append('3') - doAssert $l == """['1', '2', '3']""" - -# Tests for critbits -block: - var t: CritBitTree[int] - t["a"] = 1 - doAssert $t == """{"a": 1}""" -block: - var t: CritBitTree[string] - t["a"] = "1" - doAssert $t == """{"a": "1"}""" -block: - var t: CritBitTree[char] - t["a"] = '1' - doAssert $t == """{"a": '1'}""" - - # Test escaping behavior block: var s = "" diff --git a/tests/stdlib/tcritbits.nim b/tests/stdlib/tcritbits.nim index 4b05cd950..7b2dde1c8 100644 --- a/tests/stdlib/tcritbits.nim +++ b/tests/stdlib/tcritbits.nim @@ -1,3 +1,7 @@ +discard """ + targets: "c js" +""" + import std/[sequtils,critbits] template main = @@ -65,5 +69,19 @@ template main = discard strings.containsOrIncl("foo", 3) doAssert strings["foo"] == 3 + block tcritbitsToString: + block: + var t: CritBitTree[int] + t["a"] = 1 + doAssert $t == """{"a": 1}""" + block: + var t: CritBitTree[string] + t["a"] = "1" + doAssert $t == """{"a": "1"}""" + block: + var t: CritBitTree[char] + t["a"] = '1' + doAssert $t == """{"a": '1'}""" + main() static: main() diff --git a/tests/stdlib/tdeque.nim b/tests/stdlib/tdeque.nim deleted file mode 100644 index 50e5d0ab1..000000000 --- a/tests/stdlib/tdeque.nim +++ /dev/null @@ -1,111 +0,0 @@ -import deques - - -var deq = initDeque[int](1) -deq.addLast(4) -deq.addFirst(9) -deq.addFirst(123) -var first = deq.popFirst() -deq.addLast(56) -assert(deq.peekLast() == 56) -deq.addLast(6) -assert(deq.peekLast() == 6) -var second = deq.popFirst() -deq.addLast(789) -assert(deq.peekLast() == 789) - -assert first == 123 -assert second == 9 -assert($deq == "[4, 56, 6, 789]") -assert deq == [4, 56, 6, 789].toDeque - -assert deq[0] == deq.peekFirst and deq.peekFirst == 4 -#assert deq[^1] == deq.peekLast and deq.peekLast == 789 -deq[0] = 42 -deq[deq.len - 1] = 7 - -assert 6 in deq and 789 notin deq -assert deq.find(6) >= 0 -assert deq.find(789) < 0 - -block: - var d = initDeque[int](1) - d.addLast 7 - d.addLast 8 - d.addLast 10 - d.addFirst 5 - d.addFirst 2 - d.addFirst 1 - d.addLast 20 - d.shrink(fromLast = 2) - doAssert($d == "[1, 2, 5, 7, 8]") - d.shrink(2, 1) - doAssert($d == "[5, 7]") - d.shrink(2, 2) - doAssert d.len == 0 - -for i in -2 .. 10: - if i in deq: - assert deq.contains(i) and deq.find(i) >= 0 - else: - assert(not deq.contains(i) and deq.find(i) < 0) - -when compileOption("boundChecks"): - try: - echo deq[99] - assert false - except IndexDefect: - discard - - try: - assert deq.len == 4 - for i in 0 ..< 5: deq.popFirst() - assert false - except IndexDefect: - discard - -# grabs some types of resize error. -deq = initDeque[int]() -for i in 1 .. 4: deq.addLast i -deq.popFirst() -deq.popLast() -for i in 5 .. 8: deq.addFirst i -assert $deq == "[8, 7, 6, 5, 2, 3]" - -# Similar to proc from the documentation example -proc foo(a, b: Positive) = # assume random positive values for `a` and `b`. - var deq = initDeque[int]() - assert deq.len == 0 - for i in 1 .. a: deq.addLast i - - if b < deq.len: # checking before indexed access. - assert deq[b] == b + 1 - - # The following two lines don't need any checking on access due to the logic - # of the program, but that would not be the case if `a` could be 0. - assert deq.peekFirst == 1 - assert deq.peekLast == a - - while deq.len > 0: # checking if the deque is empty - assert deq.popFirst() > 0 - -#foo(0,0) -foo(8, 5) -foo(10, 9) -foo(1, 1) -foo(2, 1) -foo(1, 5) -foo(3, 2) - -import sets - -block t13310: - proc main() = - var q = initDeque[HashSet[int16]](2) - q.addFirst([1'i16].toHashSet) - q.addFirst([2'i16].toHashSet) - q.addFirst([3'i16].toHashSet) - assert $q == "[{3}, {2}, {1}]" - - static: - main() diff --git a/tests/stdlib/tdeques.nim b/tests/stdlib/tdeques.nim new file mode 100644 index 000000000..db392c6cc --- /dev/null +++ b/tests/stdlib/tdeques.nim @@ -0,0 +1,139 @@ +discard """ + targets: "c js" +""" + +import std/deques + +block: + proc index(self: Deque[int], idx: Natural): int = + self[idx] + + proc main = + var testDeque = initDeque[int]() + testDeque.addFirst(1) + assert testDeque.index(0) == 1 + + main() + +block: + var d = initDeque[int]() + d.addLast(1) + doAssert $d == "[1]" +block: + var d = initDeque[string]() + d.addLast("1") + doAssert $d == """["1"]""" +block: + var d = initDeque[char]() + d.addLast('1') + doAssert $d == "['1']" + +block: + var deq = initDeque[int](1) + deq.addLast(4) + deq.addFirst(9) + deq.addFirst(123) + var first = deq.popFirst() + deq.addLast(56) + assert(deq.peekLast() == 56) + deq.addLast(6) + assert(deq.peekLast() == 6) + var second = deq.popFirst() + deq.addLast(789) + assert(deq.peekLast() == 789) + + assert first == 123 + assert second == 9 + assert($deq == "[4, 56, 6, 789]") + assert deq == [4, 56, 6, 789].toDeque + + assert deq[0] == deq.peekFirst and deq.peekFirst == 4 + #assert deq[^1] == deq.peekLast and deq.peekLast == 789 + deq[0] = 42 + deq[deq.len - 1] = 7 + + assert 6 in deq and 789 notin deq + assert deq.find(6) >= 0 + assert deq.find(789) < 0 + + block: + var d = initDeque[int](1) + d.addLast 7 + d.addLast 8 + d.addLast 10 + d.addFirst 5 + d.addFirst 2 + d.addFirst 1 + d.addLast 20 + d.shrink(fromLast = 2) + doAssert($d == "[1, 2, 5, 7, 8]") + d.shrink(2, 1) + doAssert($d == "[5, 7]") + d.shrink(2, 2) + doAssert d.len == 0 + + for i in -2 .. 10: + if i in deq: + assert deq.contains(i) and deq.find(i) >= 0 + else: + assert(not deq.contains(i) and deq.find(i) < 0) + + when compileOption("boundChecks"): + try: + echo deq[99] + assert false + except IndexDefect: + discard + + try: + assert deq.len == 4 + for i in 0 ..< 5: deq.popFirst() + assert false + except IndexDefect: + discard + + # grabs some types of resize error. + deq = initDeque[int]() + for i in 1 .. 4: deq.addLast i + deq.popFirst() + deq.popLast() + for i in 5 .. 8: deq.addFirst i + assert $deq == "[8, 7, 6, 5, 2, 3]" + + # Similar to proc from the documentation example + proc foo(a, b: Positive) = # assume random positive values for `a` and `b`. + var deq = initDeque[int]() + assert deq.len == 0 + for i in 1 .. a: deq.addLast i + + if b < deq.len: # checking before indexed access. + assert deq[b] == b + 1 + + # The following two lines don't need any checking on access due to the logic + # of the program, but that would not be the case if `a` could be 0. + assert deq.peekFirst == 1 + assert deq.peekLast == a + + while deq.len > 0: # checking if the deque is empty + assert deq.popFirst() > 0 + + #foo(0,0) + foo(8, 5) + foo(10, 9) + foo(1, 1) + foo(2, 1) + foo(1, 5) + foo(3, 2) + +import sets + +block t13310: + proc main() = + var q = initDeque[HashSet[int16]](2) + q.addFirst([1'i16].toHashSet) + q.addFirst([2'i16].toHashSet) + q.addFirst([3'i16].toHashSet) + assert $q == "[{3}, {2}, {1}]" + + static: + main() diff --git a/tests/stdlib/tlists.nim b/tests/stdlib/tlists.nim index a288af781..fe814bb92 100644 --- a/tests/stdlib/tlists.nim +++ b/tests/stdlib/tlists.nim @@ -1,5 +1,5 @@ discard """ - output: '''true''' + targets: "c js" """ import lists @@ -62,6 +62,22 @@ block DoublyLinkedRingTest1: assert($L == "[]") assert(4 notin L) - -echo "true" - +block tlistsToString: + block: + var l = initDoublyLinkedList[int]() + l.append(1) + l.append(2) + l.append(3) + doAssert $l == "[1, 2, 3]" + block: + var l = initDoublyLinkedList[string]() + l.append("1") + l.append("2") + l.append("3") + doAssert $l == """["1", "2", "3"]""" + block: + var l = initDoublyLinkedList[char]() + l.append('1') + l.append('2') + l.append('3') + doAssert $l == """['1', '2', '3']""" From d15f63a0f2d431a516601530196ac01c26e00f53 Mon Sep 17 00:00:00 2001 From: rockcavera Date: Sat, 12 Dec 2020 05:35:18 -0300 Subject: [PATCH 1161/3768] Fix #14259 #15621 (#16322) [backport:1.4] * IPv6 text representation according to RFC 5952 * Revert IPv6 text representation according to RFC 5952 * fix #14259 #15621 fix #14259 #15621 * Update lib/system/io.nim * reverted IoHandle removal * adaptation of types for WinAPI Co-authored-by: Andreas Rumpf --- lib/system/io.nim | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/lib/system/io.nim b/lib/system/io.nim index c3b2976fb..b3e1725b4 100644 --- a/lib/system/io.nim +++ b/lib/system/io.nim @@ -278,7 +278,12 @@ elif defined(posix) and not defined(lwip) and not defined(nimscript): proc c_fcntl(fd: cint, cmd: cint): cint {. importc: "fcntl", header: "", varargs.} elif defined(windows): - const HANDLE_FLAG_INHERIT = culong 0x1 + type + WinDWORD = culong + WinBOOL = cint + + const HANDLE_FLAG_INHERIT = 1.WinDWORD + proc getOsfhandle(fd: cint): int {. importc: "_get_osfhandle", header: "".} @@ -286,8 +291,10 @@ elif defined(windows): IoHandle = distinct pointer ## Windows' HANDLE type. Defined as an untyped pointer but is **not** ## one. Named like this to avoid collision with other `system` modules. - proc setHandleInformation(handle: IoHandle, mask, flags: culong): cint {. - importc: "SetHandleInformation", header: "".} + + proc setHandleInformation(hObject: IoHandle, dwMask, dwFlags: WinDWORD): + WinBOOL {.stdcall, dynlib: "kernel32", + importc: "SetHandleInformation".} const BufSize = 4000 @@ -346,7 +353,7 @@ when defined(nimdoc) or (defined(posix) and not defined(nimscript)) or defined(w result = c_fcntl(f, F_SETFD, flags) != -1 else: result = setHandleInformation(cast[IoHandle](f), HANDLE_FLAG_INHERIT, - culong inheritable) != 0 + inheritable.WinDWORD) != 0 proc readLine*(f: File, line: var TaintedString): bool {.tags: [ReadIOEffect], benign.} = From 5a58440ebe4ba794f0127ebb6ad33824a92dfb58 Mon Sep 17 00:00:00 2001 From: Jason Beetham Date: Sat, 12 Dec 2020 15:29:20 -0700 Subject: [PATCH 1162/3768] Added strscans.scanTuple (#16300) * Added since and changelog --- changelog.md | 1 + lib/pure/strscans.nim | 51 +++++++++++++++++++++++++++++++++++++- tests/stdlib/tstrscans.nim | 51 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index ab323337c..688b87da8 100644 --- a/changelog.md +++ b/changelog.md @@ -56,6 +56,7 @@ - `writeStackTrace` is available in JS backend now. - `strscans.scanf` now supports parsing single characters. +- `strscans.scanTuple` added which uses `strscans.scanf` internally, returning a tuple which can be unpacked for easier usage of `scanf`. - Added `math.isNaN`. diff --git a/lib/pure/strscans.nim b/lib/pure/strscans.nim index 347dbc2ef..9c55bf3e3 100644 --- a/lib/pure/strscans.nim +++ b/lib/pure/strscans.nim @@ -284,6 +284,7 @@ efficiency and perform different checks. import macros, parseutils +import std/private/since proc conditionsToIfChain(n, idx, res: NimNode; start: int): NimNode = assert n.kind == nnkStmtList @@ -465,6 +466,54 @@ macro scanf*(input: string; pattern: static[string]; results: varargs[typed]): b else: result.add res +macro scanTuple*(input: untyped; pattern: static[string]; matcherTypes: varargs[untyped]): untyped {.since: (1, 5).}= + ## Works identically as scanf, but instead of predeclaring variables it returns a tuple. + ## Tuple is started with a bool which indicates if the scan was successful + ## followed by the requested data. + ## If using a user defined matcher, provide the types in order they appear after pattern: + ## `line.scanTuple("${yourMatcher()}", int)` + runnableExamples: + let (success, year, month, day, time) = scanTuple("1000-01-01 00:00:00", "$i-$i-$i$s$+") + if success: + assert year == 1000 + assert month == 1 + assert day == 1 + assert time == "00:00:00" + var + p = 0 + userMatches = 0 + arguments: seq[NimNode] + result = newStmtList() + template addVar(typ: string) = + let varIdent = ident("temp" & $arguments.len) + result.add(newNimNode(nnkVarSection).add(newIdentDefs(varIdent, ident(typ), newEmptyNode()))) + arguments.add(varIdent) + while p < pattern.len: + if pattern[p] == '$': + inc p + case pattern[p] + of 'w', '*', '+': + addVar("string") + of 'c': + addVar("char") + of 'b', 'o', 'i', 'h': + addVar("int") + of 'f': + addVar("float") + of '{': + if userMatches < matcherTypes.len: + let varIdent = ident("temp" & $arguments.len) + result.add(newNimNode(nnkVarSection).add(newIdentDefs(varIdent, matcherTypes[userMatches], newEmptyNode()))) + arguments.add(varIdent) + inc userMatches + else: discard + inc p + result.add newPar(newCall(ident("scanf"), input, newStrLitNode(pattern))) + for arg in arguments: + result[^1][0].add arg + result[^1].add arg + result = newBlockStmt(result) + template atom*(input: string; idx: int; c: char): bool = ## Used in scanp for the matching of atoms (usually chars). ## EOF is matched as ``'\0'``. @@ -639,4 +688,4 @@ macro scanp*(input, idx: typed; pattern: varargs[untyped]): bool = result.add toIfChain(conds, idx, res, 0) result.add res when defined(debugScanp): - echo repr result + echo repr result \ No newline at end of file diff --git a/tests/stdlib/tstrscans.nim b/tests/stdlib/tstrscans.nim index 8ca167837..d443c5dda 100644 --- a/tests/stdlib/tstrscans.nim +++ b/tests/stdlib/tstrscans.nim @@ -227,3 +227,54 @@ block: if line.scanf("$i-$i $c: $w", lo, hi, c, w): inc res doAssert res == 4 + +block: + #whenscanf testing + let input = """1-3 s: abc +15-18 9: def +15-18 A: ghi +15-18 _: jkl +""" + proc twoDigits(input: string; x: var int; start: int): int = + if start+1 < input.len and input[start] == '0' and input[start+1] == '0': + result = 2 + x = 13 + else: + result = 0 + + proc someSep(input: string; start: int; seps: set[char] = {';', ',', '-', '.'}): int = + result = 0 + while start+result < input.len and input[start+result] in seps: inc result + + var res = 0 + for line in input.splitLines: + let (success, lo, hi, c ,w) = scanTuple(line, "$i-$i $c: $w") + if success: + inc res + doAssert res == 4 + + let (_, key, val, intval, floatVal) = scanTuple("abc:: xyz 89 33.25", "$w$s::$s$w$s$i $f") + doAssert key == "abc" + doAssert val == "xyz" + doAssert intval == 89 + doAssert floatVal == 33.25 + + + let (_, binVal, octVal, hexVal) = scanTuple("0b0101 0o1234 0xabcd", "$b$s$o$s$h", binval, octval, hexval) + doAssert binval == 0b0101 + doAssert octval == 0o1234 + doAssert hexval == 0xabcd + + var (xx,_) = scanTuple("$abc", "$$$i") + doAssert xx == false + + + let (xx2, _) = block: scanTuple("$1234", "$$$i") + doAssert xx2 + + var (yy, intval2, key2) = scanTuple(";.--Breakpoint00 [output]", + "$[someSep]Breakpoint${twoDigits}$[someSep({';','.','-'})] [$+]$.", + int) + doAssert yy + doAssert key2 == "output" + doAssert intVal2 == 13 \ No newline at end of file From 5514b299ebc1de4ef0c1c182d4f21a02e3c2fa90 Mon Sep 17 00:00:00 2001 From: Bung Date: Sun, 13 Dec 2020 11:52:38 +0800 Subject: [PATCH 1163/3768] add testcase for #15937 (#15933) * test for issue #15624 and PR #15915 for patch #13823 * Update thashes.nim no need mention PR #15915, fixed in https://github.com/nim-lang/Nim/pull/15937 * rebase to devel(issue maybe fixed), ignore ouputs * Apply suggestions from code review Co-authored-by: flywind <43030857+xflywind@users.noreply.github.com> --- tests/stdlib/thashes.nim | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/stdlib/thashes.nim b/tests/stdlib/thashes.nim index d155111ff..92154825e 100644 --- a/tests/stdlib/thashes.nim +++ b/tests/stdlib/thashes.nim @@ -1,3 +1,7 @@ +discard """ + targets: '''c c++ js''' +""" + import hashes block hashes: @@ -13,8 +17,10 @@ block hashes: # "VM and runtime should make the same hash value (hashWangYi1)" block: const wy123 = hashWangYi1(123) + doAssert wy123 != 0 doAssert hashWangYi1(123) == wy123 + # "hashIdentity value incorrect at 456" block: doAssert hashIdentity(456) == 456 From 7e1ae35195c9b1397d569ebb3b3a0d11fdcb16f4 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 14 Dec 2020 01:58:29 -0800 Subject: [PATCH 1164/3768] testament: error instead of silently ignore invalid targets; remove pointless alias target vs targets; document matrix; DRY (#16343) * testament: error instead of silently ignore invalid targets * s/target/targets/ * fix test; refs #16344 * address comments * Update testament/specs.nim Co-authored-by: Andreas Rumpf --- doc/testament.rst | 9 ++++++--- testament/specs.nim | 20 ++++++------------- tests/align/talign.nim | 2 +- tests/arc/trtree.nim | 2 +- tests/ccgbugs/tmissingvolatile.nim | 2 +- tests/ccgbugs/tprogmem.nim | 2 +- tests/ccgbugs2/tinefficient_const_table.nim | 2 +- tests/closure/tclosure.nim | 2 +- tests/compiler/tcppCompileToNamespace.nim | 1 - tests/coroutines/texceptions.nim | 2 +- tests/coroutines/tgc.nim | 2 +- tests/coroutines/titerators.nim | 2 +- tests/coroutines/twait.nim | 2 +- tests/cpp/tevalorder.nim | 2 +- tests/destructor/tmove_objconstr.nim | 2 +- .../destructor/tuse_result_prevents_sinks.nim | 2 +- tests/exception/t9657.nim | 2 +- tests/flags/tgenscript.nim | 2 +- tests/iter/titervaropenarray.nim | 2 +- tests/objects/tobjcov.nim | 2 +- tests/stdlib/t10231.nim | 2 +- tests/stdlib/thashes.nim | 2 +- tests/stdlib/ttimes.nim | 2 +- tests/system/tatomics1.nim | 2 +- 24 files changed, 33 insertions(+), 39 deletions(-) diff --git a/doc/testament.rst b/doc/testament.rst index 1a1909022..253afb33d 100644 --- a/doc/testament.rst +++ b/doc/testament.rst @@ -152,8 +152,11 @@ Example "template" **to edit** and write a Testament unittest: # Timeout seconds to run the test. Fractional values are supported. timeout: 1.5 - # Targets to run the test into (C, C++, JavaScript, etc). - target: "c js" + # Targets to run the test into (c, cpp, objc, js). + targets: "c js" + + # flags with which to run the test, delimited by `;` + matrix: "; -d:release; -d:caseFoo -d:release" # Conditions that will skip this test. Use of multiple "disabled" clauses # is permitted. @@ -221,7 +224,7 @@ JavaScript tests: .. code-block:: nim discard """ - target: "js" + targets: "js" """ when defined(js): import jsconsole diff --git a/testament/specs.nim b/testament/specs.nim index e15dcb85c..a7f0fd4bb 100644 --- a/testament/specs.nim +++ b/testament/specs.nim @@ -218,7 +218,7 @@ proc parseTargets*(value: string): set[TTarget] = of "cpp", "c++": result.incl(targetCpp) of "objc": result.incl(targetObjC) of "js": result.incl(targetJS) - else: echo "target ignored: " & v + else: raise newException(ValueError, "invalid target: '$#'" % v) proc addLine*(self: var string; a: string) = self.add a @@ -377,19 +377,11 @@ proc parseSpec*(filename: string): TSpec = result.timeout = parseFloat(e.value) except ValueError: result.parseErrors.addLine "cannot interpret as a float: ", e.value - of "target", "targets": - for v in e.value.normalize.splitWhitespace: - case v - of "c": - result.targets.incl(targetC) - of "cpp", "c++": - result.targets.incl(targetCpp) - of "objc": - result.targets.incl(targetObjC) - of "js": - result.targets.incl(targetJS) - else: - result.parseErrors.addLine "cannot interpret as a target: ", e.value + of "targets", "target": + try: + result.targets.incl parseTargets(e.value) + except ValueError as e: + result.parseErrors.addLine e.msg of "matrix": for v in e.value.split(';'): result.matrix.add(v.strip) diff --git a/tests/align/talign.nim b/tests/align/talign.nim index e0503cc70..3b8f6b4df 100644 --- a/tests/align/talign.nim +++ b/tests/align/talign.nim @@ -1,6 +1,6 @@ discard """ ccodeCheck: "\\i @'NIM_ALIGN(128) NI mylocal1' .*" -target: "c cpp" +targets: "c cpp" output: "align ok" """ diff --git a/tests/arc/trtree.nim b/tests/arc/trtree.nim index 986268f51..5659a5bdc 100644 --- a/tests/arc/trtree.nim +++ b/tests/arc/trtree.nim @@ -1,7 +1,7 @@ discard """ output: '''1 [2, 3, 4, 7] [0, 0]''' - target: "c" + targets: "c" joinable: false disabled: 32bit cmd: "nim c --gc:arc $file" diff --git a/tests/ccgbugs/tmissingvolatile.nim b/tests/ccgbugs/tmissingvolatile.nim index 60b1771dc..1eccdc6b1 100644 --- a/tests/ccgbugs/tmissingvolatile.nim +++ b/tests/ccgbugs/tmissingvolatile.nim @@ -2,7 +2,7 @@ discard """ output: "1" cmd: r"nim c --hints:on $options -d:release $file" ccodecheck: "'NI volatile state;'" - target: "C" + targets: "c" """ # bug #1539 diff --git a/tests/ccgbugs/tprogmem.nim b/tests/ccgbugs/tprogmem.nim index 884ca158a..58a20583a 100644 --- a/tests/ccgbugs/tprogmem.nim +++ b/tests/ccgbugs/tprogmem.nim @@ -2,7 +2,7 @@ discard """ output: "5" cmd: r"nim c --hints:on $options -d:release $file" ccodecheck: "'/*PROGMEM*/ myLetVariable = {'" - target: "C" + targets: "c" """ var myLetVariable {.exportc, codegenDecl: "$# /*PROGMEM*/ $#".} = [1, 2, 3] diff --git a/tests/ccgbugs2/tinefficient_const_table.nim b/tests/ccgbugs2/tinefficient_const_table.nim index a1b89e220..8ab895cb0 100644 --- a/tests/ccgbugs2/tinefficient_const_table.nim +++ b/tests/ccgbugs2/tinefficient_const_table.nim @@ -6,7 +6,7 @@ of words''' cmd: r"nim c --hints:on $options -d:release $file" ccodecheck: "! @'genericSeqAssign'" - target: "c" + targets: "c" """ # bug #4354 diff --git a/tests/closure/tclosure.nim b/tests/closure/tclosure.nim index 7e2ec7a77..546d7026d 100644 --- a/tests/closure/tclosure.nim +++ b/tests/closure/tclosure.nim @@ -1,5 +1,5 @@ discard """ - target: "c" + targets: "c" output: ''' 1 3 6 11 20 foo foo88 diff --git a/tests/compiler/tcppCompileToNamespace.nim b/tests/compiler/tcppCompileToNamespace.nim index 1d0977236..4e895c38b 100644 --- a/tests/compiler/tcppCompileToNamespace.nim +++ b/tests/compiler/tcppCompileToNamespace.nim @@ -1,6 +1,5 @@ discard """ cmd: "nim cpp --cppCompileToNamespace:foo $options -r $file" -target: cpp """ # Theoretically nim could just ignore the flag cppCompileToNamespace diff --git a/tests/coroutines/texceptions.nim b/tests/coroutines/texceptions.nim index 7b5d57ec0..31feffdff 100644 --- a/tests/coroutines/texceptions.nim +++ b/tests/coroutines/texceptions.nim @@ -1,5 +1,5 @@ discard """ - target: "c" + targets: "c" disabled: true """ diff --git a/tests/coroutines/tgc.nim b/tests/coroutines/tgc.nim index 46f4f8e83..311ec5efc 100644 --- a/tests/coroutines/tgc.nim +++ b/tests/coroutines/tgc.nim @@ -1,5 +1,5 @@ discard """ - target: "c" + targets: "c" """ import coro diff --git a/tests/coroutines/titerators.nim b/tests/coroutines/titerators.nim index d12a5debe..1ea134811 100644 --- a/tests/coroutines/titerators.nim +++ b/tests/coroutines/titerators.nim @@ -1,5 +1,5 @@ discard """ - target: "c" + targets: "c" disabled: true """ diff --git a/tests/coroutines/twait.nim b/tests/coroutines/twait.nim index b88801869..348007afb 100644 --- a/tests/coroutines/twait.nim +++ b/tests/coroutines/twait.nim @@ -1,6 +1,6 @@ discard """ output: "Exit 1\nExit 2" - target: "c" + targets: "c" """ import coro diff --git a/tests/cpp/tevalorder.nim b/tests/cpp/tevalorder.nim index f130cef6c..4764f3aca 100644 --- a/tests/cpp/tevalorder.nim +++ b/tests/cpp/tevalorder.nim @@ -2,7 +2,7 @@ discard """ output: '''0 1 2''' -target: "cpp" +targets: "cpp" """ # bug #8202 diff --git a/tests/destructor/tmove_objconstr.nim b/tests/destructor/tmove_objconstr.nim index 16d0daa22..5faaabb8b 100644 --- a/tests/destructor/tmove_objconstr.nim +++ b/tests/destructor/tmove_objconstr.nim @@ -8,7 +8,7 @@ test destroyed 0 4 Pony is dying!''' joinable: false -target: "C" +targets: "c" """ # bug #4214 diff --git a/tests/destructor/tuse_result_prevents_sinks.nim b/tests/destructor/tuse_result_prevents_sinks.nim index 6eac5c902..d2777bd97 100644 --- a/tests/destructor/tuse_result_prevents_sinks.nim +++ b/tests/destructor/tuse_result_prevents_sinks.nim @@ -1,6 +1,6 @@ discard """ output: "" - target: "C" + targets: "c" """ # bug #9594 diff --git a/tests/exception/t9657.nim b/tests/exception/t9657.nim index 514d29353..6ac525a70 100644 --- a/tests/exception/t9657.nim +++ b/tests/exception/t9657.nim @@ -1,7 +1,7 @@ discard """ action: run exitcode: 1 - target: "c cpp" + targets: "c cpp" disabled: "openbsd" disabled: "netbsd" """ diff --git a/tests/flags/tgenscript.nim b/tests/flags/tgenscript.nim index bf83ab972..d58395a40 100644 --- a/tests/flags/tgenscript.nim +++ b/tests/flags/tgenscript.nim @@ -1,5 +1,5 @@ discard """ - target: "c" + targets: "c" action: compile """ diff --git a/tests/iter/titervaropenarray.nim b/tests/iter/titervaropenarray.nim index 4469fdcf5..ad1192bd8 100644 --- a/tests/iter/titervaropenarray.nim +++ b/tests/iter/titervaropenarray.nim @@ -1,6 +1,6 @@ discard """ output: "123" - targets: "C" + targets: "c" """ # Try to break the transformation pass: iterator iterAndZero(a: var openArray[int]): int = diff --git a/tests/objects/tobjcov.nim b/tests/objects/tobjcov.nim index 6c587e04d..a12f74702 100644 --- a/tests/objects/tobjcov.nim +++ b/tests/objects/tobjcov.nim @@ -1,6 +1,6 @@ discard """ action: compile -target: "c" +targets: "c" """ # Covariance is not type safe: diff --git a/tests/stdlib/t10231.nim b/tests/stdlib/t10231.nim index 2bb64b475..3b2b684f3 100644 --- a/tests/stdlib/t10231.nim +++ b/tests/stdlib/t10231.nim @@ -1,5 +1,5 @@ discard """ - target: cpp + targets: "cpp" action: run exitcode: 0 """ diff --git a/tests/stdlib/thashes.nim b/tests/stdlib/thashes.nim index 92154825e..520b27e26 100644 --- a/tests/stdlib/thashes.nim +++ b/tests/stdlib/thashes.nim @@ -1,5 +1,5 @@ discard """ - targets: '''c c++ js''' + targets: "c cpp js" """ import hashes diff --git a/tests/stdlib/ttimes.nim b/tests/stdlib/ttimes.nim index 4d89ffa1a..a7677edf9 100644 --- a/tests/stdlib/ttimes.nim +++ b/tests/stdlib/ttimes.nim @@ -1,5 +1,5 @@ discard """ - target: "c js" + targets: "c js" """ import times, strutils, unittest diff --git a/tests/system/tatomics1.nim b/tests/system/tatomics1.nim index 88ab705a8..217fd07fa 100644 --- a/tests/system/tatomics1.nim +++ b/tests/system/tatomics1.nim @@ -1,5 +1,5 @@ discard """ - target: "c cpp js" + targets: "c cpp js" """ var x = 10 From 9ac2ee7b8d80a6b4a6369626018492142889d158 Mon Sep 17 00:00:00 2001 From: rockcavera Date: Mon, 14 Dec 2020 07:07:55 -0300 Subject: [PATCH 1165/3768] Fix #11506 (#16348) fixes the problem of compiling vccexe.exe when it is in use. --- koch.nim | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/koch.nim b/koch.nim index 628c81098..5da262884 100644 --- a/koch.nim +++ b/koch.nim @@ -156,7 +156,13 @@ proc bundleNimsuggest(args: string) = options = "-d:release -d:danger " & args) proc buildVccTool(args: string) = - nimCompileFold("Compile Vcc", "tools/vccexe/vccexe.nim ", options = args) + let input = "tools/vccexe/vccexe.nim" + if contains(args, "--cc:vcc"): + nimCompileFold("Compile Vcc", input, "build", options = args) + let fileName = input.splitFile.name + moveFile(exe("build" / fileName), exe("bin" / fileName)) + else: + nimCompileFold("Compile Vcc", input, options = args) proc bundleNimpretty(args: string) = nimCompileFold("Compile nimpretty", "nimpretty/nimpretty.nim", From 4850f9b5969d52980177400a75af890f72d579d0 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 14 Dec 2020 02:17:24 -0800 Subject: [PATCH 1166/3768] fix #16265; fix #13999 (HCR on OSX); cgen now does not line wrap string litterals (#16329) * fix #16265: cgen now does not line wrap string litterals which, in combination with other hacks, caused a really obscure looking bug * fix #13999; nimhcr_integration.nim now works for osx * try to make appveyor CI disappear * disable openbsd + add diagnostic for openbsd * enable for openbsd * PTEMP * re-disable openbsd --- compiler/cgen.nim | 3 +++ compiler/msgs.nim | 8 +++++--- tests/dll/nimhcr_integration.nim | 17 +++++++++++++++-- 3 files changed, 23 insertions(+), 5 deletions(-) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index c4544bbc5..220b8e3c1 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -1556,6 +1556,9 @@ proc registerModuleToMain(g: BModuleList; m: BModule) = # nasty nasty hack to get the command line functionality working with HCR # register the 2 variables on behalf of the os module which might not even # be loaded (in which case it will get collected but that is not a problem) + # EDIT: indeed, this hack, in combination with another un-necessary one + # (`makeCString` was doing line wrap of string litterals) was root cause for + # bug #16265. let osModulePath = ($systemModulePath).replace("stdlib_system", "stdlib_os").rope g.mainDatInit.addf("\thcrAddModule($1);\n", [osModulePath]) g.mainDatInit.add("\tint* cmd_count;\n") diff --git a/compiler/msgs.nim b/compiler/msgs.nim index d0209db5d..1a005788e 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -34,13 +34,15 @@ proc toCChar*(c: char; result: var string) = result.add c proc makeCString*(s: string): Rope = - const MaxLineLength = 64 result = nil var res = newStringOfCap(int(s.len.toFloat * 1.1) + 1) res.add("\"") for i in 0.. 0 let (stdout, exitcode) = execCmdEx(cmd) if exitcode != 0: echo "COMPILATION ERROR!" From 555cfd1d59e773e92a7158b7c3b5908017f1ddac Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 14 Dec 2020 02:37:27 -0800 Subject: [PATCH 1167/3768] fix #16330 tshould_not_work (#16342) --- testament/testament.nim | 4 ++-- tests/testament/tshould_not_work.nim | 4 +--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/testament/testament.nim b/testament/testament.nim index a301b018c..7b1a25bf0 100644 --- a/testament/testament.nim +++ b/testament/testament.nim @@ -144,7 +144,7 @@ proc prepareTestArgs(cmdTemplate, filename, options, nimcache: string, options.add " " & extraOptions result = parseCmdLine(cmdTemplate % ["target", targetToCmd[target], "options", options, "file", filename.quoteShell, - "filedir", filename.getFileDir()]) + "filedir", filename.getFileDir(), "nim", compilerPrefix]) proc callCompiler(cmdTemplate, filename, options, nimcache: string, target: TTarget, extraOptions = ""): TSpec = @@ -682,7 +682,7 @@ proc main() = gTargets = parseTargets(targetsStr) targetsSet = true of "nim": - compilerPrefix = addFileExt(p.val.string, ExeExt) + compilerPrefix = addFileExt(p.val.string.absolutePath, ExeExt) of "directory": setCurrentDir(p.val.string) of "colors": diff --git a/tests/testament/tshould_not_work.nim b/tests/testament/tshould_not_work.nim index a7a877fe8..a0b4d6a36 100644 --- a/tests/testament/tshould_not_work.nim +++ b/tests/testament/tshould_not_work.nim @@ -1,5 +1,5 @@ discard """ -cmd: "testament/testament --directory:testament --colors:off --backendLogging:off --nim:../compiler/nim category shouldfail" +cmd: "testament/testament --directory:testament --colors:off --backendLogging:off --nim:$nim category shouldfail" action: compile nimout: ''' FAIL: tests/shouldfail/tccodecheck.nim c @@ -35,5 +35,3 @@ FAIL: tests/shouldfail/tvalgrind.nim c Failure: reExitcodesDiffer ''' """ - -# xxx `--nim:../compiler/nim`, doesn't seem correct (and should also honor `testament --nim`) From 5e8e948ffce951d8284fe38489cd880255e56e7b Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Mon, 14 Dec 2020 07:50:45 -0300 Subject: [PATCH 1168/3768] Clean out dead code (#16345) --- tools/nimrepl.nim | 172 ---------------------------------------------- 1 file changed, 172 deletions(-) delete mode 100644 tools/nimrepl.nim diff --git a/tools/nimrepl.nim b/tools/nimrepl.nim deleted file mode 100644 index ac82d8b75..000000000 --- a/tools/nimrepl.nim +++ /dev/null @@ -1,172 +0,0 @@ -# -# -# Nim REPL -# (c) Copyright 2012 Dominik Picheta -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -import glib2, gtk2, gdk2, os, osproc, dialogs, strutils - -when defined(tinyc): - const runCmd = "run" -else: - const runCmd = "c -r" - -var nimExe = findExe("nim") -if nimExe.len == 0: nimExe = "../bin" / addFileExt("nim", os.ExeExt) - -proc execCode(code: string): string = - var f: File - if open(f, "temp.nim", fmWrite): - f.write(code) - f.close() - result = osproc.execProcess( - "$# $# --verbosity:0 --hint[Conf]:off temp.nim" % [nimExe, runCmd], - options = {poStdErrToStdOut}) - else: - result = "cannot open file 'temp.nim'" - -var shiftPressed = false -var w: gtk2.PWindow -var inputTextBuffer: PTextBuffer -var outputTextBuffer: PTextBuffer - -proc destroy(widget: PWidget, data: pointer){.cdecl.} = - main_quit() - -proc fileOpenClicked(menuitem: PMenuItem, userdata: pointer) {.cdecl.} = - var path = chooseFileToOpen(w) - if path != "": - var file = readFile(path) - if file != nil: - set_text(inputTextBuffer, file, len(file).gint) - else: - error(w, "Unable to read from file") - -proc fileSaveClicked(menuitem: PMenuItem, userdata: pointer) {.cdecl.} = - var path = chooseFileToSave(w) - - if path == "": return - var startIter: TTextIter - var endIter: TTextIter - get_start_iter(inputTextBuffer, addr(startIter)) - get_end_iter(inputTextBuffer, addr(endIter)) - var inputText = get_text(inputTextBuffer, addr(startIter), - addr(endIter), false) - var f: File - if open(f, path, fmWrite): - f.write(inputText) - f.close() - else: - error(w, "Unable to write to file") - -proc inputKeyPressed(widget: PWidget, event: PEventKey, - userdata: pointer): bool {.cdecl.} = - if ($keyval_name(event.keyval)).tolower() == "shift_l": - # SHIFT is pressed - shiftPressed = true - -proc setError(msg: string) = - outputTextBuffer.setText(msg, msg.len.gint) - -proc inputKeyReleased(widget: PWidget, event: PEventKey, - userdata: pointer): bool {.cdecl.} = - #echo(keyval_name(event.keyval)) - if ($keyval_name(event.keyval)).tolower() == "shift_l": - # SHIFT is released - shiftPressed = false - - if ($keyval_name(event.keyval)).tolower() == "return": - #echo($keyval_name(event.keyval), "Shift_L") - # Enter pressed - if not shiftPressed: - var startIter: TTextIter - var endIter: TTextIter - get_start_iter(inputTextBuffer, addr(startIter)) - get_end_iter(inputTextBuffer, addr(endIter)) - var inputText = get_text(inputTextBuffer, addr(startIter), - addr(endIter), false) - - try: - var r = execCode($inputText) - set_text(outputTextBuffer, r, len(r).gint) - except IOError: - setError("Error: Could not open file temp.nim") - - -proc initControls() = - w = window_new(gtk2.WINDOW_TOPLEVEL) - set_default_size(w, 500, 600) - set_title(w, "Nim REPL") - discard signal_connect(w, "destroy", SIGNAL_FUNC(nimrepl.destroy), nil) - - # MainBox (vbox) - var mainBox = vbox_new(false, 0) - add(w, mainBox) - - # TopMenu (MenuBar) - var topMenu = menu_bar_new() - show(topMenu) - - var fileMenu = menu_new() - var openMenuItem = menu_item_new("Open") - append(fileMenu, openMenuItem) - show(openMenuItem) - discard signal_connect(openMenuItem, "activate", - SIGNAL_FUNC(fileOpenClicked), nil) - var saveMenuItem = menu_item_new("Save...") - append(fileMenu, saveMenuItem) - show(saveMenuItem) - discard signal_connect(saveMenuItem, "activate", - SIGNAL_FUNC(fileSaveClicked), nil) - var fileMenuItem = menu_item_new("File") - - - set_submenu(fileMenuItem, fileMenu) - show(fileMenuItem) - append(topMenu, fileMenuItem) - - pack_start(mainBox, topMenu, false, false, 0) - - # VPaned - Separates the InputTextView and the OutputTextView - var paned = vpaned_new() - set_position(paned, 450) - pack_start(mainBox, paned, true, true, 0) - show(paned) - - # Init the TextBuffers - inputTextBuffer = text_buffer_new(nil) - outputTextBuffer = text_buffer_new(nil) - - # InputTextView (TextView) - var inputScrolledWindow = scrolled_window_new(nil, nil) - set_policy(inputScrolledWindow, POLICY_AUTOMATIC, POLICY_AUTOMATIC) - var inputTextView = text_view_new(inputTextBuffer) - add_with_viewport(inputScrolledWindow, inputTextView) - add1(paned, inputScrolledWindow) - show(inputScrolledWindow) - show(inputTextView) - - discard signal_connect(inputTextView, "key-release-event", - SIGNAL_FUNC(inputKeyReleased), nil) - discard signal_connect(inputTextView, "key-press-event", - SIGNAL_FUNC(inputKeyPressed), nil) - - # OutputTextView (TextView) - var outputScrolledWindow = scrolled_window_new(nil, nil) - set_policy(outputScrolledWindow, POLICY_AUTOMATIC, POLICY_AUTOMATIC) - var outputTextView = text_view_new(outputTextBuffer) - add_with_viewport(outputScrolledWindow, outputTextView) - add2(paned, outputScrolledWindow) - show(outputScrolledWindow) - show(outputTextView) - - show(w) - show(mainBox) - -nim_init() -initControls() -main() - From b6443c96a0114994fe43e266b358b24c183c1017 Mon Sep 17 00:00:00 2001 From: RSDuck Date: Mon, 14 Dec 2020 13:28:03 +0100 Subject: [PATCH 1169/3768] fix #16334 (#16335) * fix #16334 * rename isstdout -> isStdout * separate lastMsgWasDot for stdout and stderr * simplify logic --- compiler/msgs.nim | 14 +++++++++----- compiler/options.nim | 6 +++++- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 1a005788e..d027ce960 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -16,11 +16,15 @@ import strutils2 type InstantiationInfo* = typeof(instantiationInfo()) template instLoc(): InstantiationInfo = instantiationInfo(-2, fullPaths = true) -template flushDot(conf, stdorr) = +template toStdOrrKind(stdOrr): untyped = + if stdOrr == stdout: stdOrrStdout else: stdOrrStderr + +template flushDot(conf, stdOrr) = ## safe to call multiple times - if conf.lastMsgWasDot: - conf.lastMsgWasDot = false - write(stdorr, "\n") + let stdOrrKind = stdOrr.toStdOrrKind() + if stdOrrKind in conf.lastMsgWasDot: + conf.lastMsgWasDot.excl stdOrrKind + write(stdOrr, "\n") proc toCChar*(c: char; result: var string) = case c @@ -357,7 +361,7 @@ proc msgWrite(conf: ConfigRef; s: string) = stderr write(stdOrr, s) flushFile(stdOrr) - conf.lastMsgWasDot = true # subsequent writes need `flushDot` + conf.lastMsgWasDot.incl stdOrr.toStdOrrKind() # subsequent writes need `flushDot` template styledMsgWriteln*(args: varargs[typed]) = if not isNil(conf.writelnHook): diff --git a/compiler/options.nim b/compiler/options.nim index bf0a9a4ee..73c4c627d 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -240,6 +240,10 @@ type ProfileData* = ref object data*: TableRef[TLineInfo, ProfileInfo] + StdOrrKind* = enum + stdOrrStdout + stdOrrStderr + ConfigRef* = ref object ## every global configuration ## fields marked with '*' are subject to ## the incremental compilation mechanisms @@ -304,7 +308,7 @@ type projectPath*: AbsoluteDir # holds a path like /home/alice/projects/nim/compiler/ projectFull*: AbsoluteFile # projectPath/projectName projectIsStdin*: bool # whether we're compiling from stdin - lastMsgWasDot*: bool # the last compiler message was a single '.' + lastMsgWasDot*: set[StdOrrKind] # the last compiler message was a single '.' projectMainIdx*: FileIndex # the canonical path id of the main module projectMainIdx2*: FileIndex # consider merging with projectMainIdx command*: string # the main command (e.g. cc, check, scan, etc) From 8f6e07a9a2b8aaa05671b3e3bdd4f95f5c2f1b71 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 14 Dec 2020 09:38:02 -0600 Subject: [PATCH 1170/3768] clean the docs of sequtils (#16332) --- lib/pure/collections/sequtils.nim | 186 ++++++++++++++---------------- 1 file changed, 89 insertions(+), 97 deletions(-) diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index a7e392026..449faadc7 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -7,19 +7,19 @@ # distribution, for details about the copyright. # -## Although this module has ``seq`` in its name, it implements operations +## Although this module has `seq` in its name, it implements operations ## not only for `seq`:idx: type, but for three built-in container types under -## the ``openArray`` umbrella: +## the `openArray` umbrella: ## * sequences ## * strings ## * array ## ## The system module defines several common functions, such as: -## * ``newSeq[T]`` for creating new sequences of type ``T`` -## * ``@`` for converting arrays and strings to sequences -## * ``add`` for adding new elements to strings and sequences -## * ``&`` for string and seq concatenation -## * ``in`` (alias for ``contains``) and ``notin`` for checking if an item is +## * `newSeq[T]` for creating new sequences of type `T` +## * `@` for converting arrays and strings to sequences +## * `add` for adding new elements to strings and sequences +## * `&` for string and seq concatenation +## * `in` (alias for `contains`) and `notin` for checking if an item is ## in a container ## ## This module builds upon that, providing additional functionality in form of @@ -37,41 +37,40 @@ ## ## The chaining of functions is possible thanks to the ## `method call syntax`_. -## -## .. code-block:: -## import sequtils, sugar -## -## # Creating a sequence from 1 to 10, multiplying each member by 2, -## # keeping only the members which are not divisible by 6. -## let -## foo = toSeq(1..10).map(x => x*2).filter(x => x mod 6 != 0) -## bar = toSeq(1..10).mapIt(it*2).filterIt(it mod 6 != 0) -## baz = collect(newSeq): -## for i in 1..10: -## let j = 2*i -## if j mod 6 != 0: -## j -## -## doAssert foo == bar -## doAssert foo == baz -## echo foo # @[2, 4, 8, 10, 14, 16, 20] -## -## echo foo.any(x => x > 17) # true -## echo bar.allIt(it < 20) # false -## echo foo.foldl(a + b) # 74; sum of all members -## -## .. code-block:: -## import sequtils -## from strutils import join -## -## let -## vowels = @"aeiou" # creates a sequence @['a', 'e', 'i', 'o', 'u'] -## foo = "sequtils is an awesome module" -## -## echo foo.filterIt(it notin vowels).join # "sqtls s n wsm mdl" -## -## ---- -## + +runnableExamples: + import sugar + + # Creating a sequence from 1 to 10, multiplying each member by 2, + # keeping only the members which are not divisible by 6. + let + foo = toSeq(1..10).map(x => x*2).filter(x => x mod 6 != 0) + bar = toSeq(1..10).mapIt(it*2).filterIt(it mod 6 != 0) + baz = collect: + for i in 1..10: + let j = 2*i + if j mod 6 != 0: + j + + doAssert foo == bar + doAssert foo == baz + doAssert foo == @[2, 4, 8, 10, 14, 16, 20] + + doAssert foo.any(x => x > 17) + doAssert not bar.allIt(it < 20) + doAssert foo.foldl(a + b) == 74 # sum of all members + + +runnableExamples: + from strutils import join + + let + vowels = @"aeiou" + foo = "sequtils is an awesome module" + + doAssert (vowels is seq[char]) and (vowels == @['a', 'e', 'i', 'o', 'u']) + doAssert foo.filterIt(it notin vowels).join == "sqtls s n wsm mdl" + ## **See also**: ## * `strutils module`_ for common string functions ## * `sugar module`_ for syntactic sugar macros @@ -90,12 +89,12 @@ when not defined(nimhygiene): macro evalOnceAs(expAlias, exp: untyped, letAssigneable: static[bool]): untyped = - ## Injects ``expAlias`` in caller scope, to avoid bugs involving multiple + ## Injects `expAlias` in caller scope, to avoid bugs involving multiple ## substitution in macro arguments such as ## https://github.com/nim-lang/Nim/issues/7187 - ## ``evalOnceAs(myAlias, myExp)`` will behave as ``let myAlias = myExp`` - ## except when ``letAssigneable`` is false (e.g. to handle openArray) where - ## it just forwards ``exp`` unchanged + ## `evalOnceAs(myAlias, myExp)` will behave as `let myAlias = myExp` + ## except when `letAssigneable` is false (e.g. to handle openArray) where + ## it just forwards `exp` unchanged expectKind(expAlias, nnkIdent) var val = exp @@ -428,7 +427,10 @@ func apply*[T](s: var openArray[T], op: proc (x: T): T {.closure.}) func apply*[T](s: openArray[T], op: proc (x: T) {.closure.}) {.inline, since: (1, 3).} = ## Same as `apply` but for proc that do not return and do not mutate `s` directly. - runnableExamples: apply([0, 1, 2, 3, 4], proc(item: int) = echo item) + runnableExamples: + var message: string + apply([0, 1, 2, 3, 4], proc(item: int) = message.addInt item) + assert message == "01234" for i in 0 ..< s.len: op(s[i]) iterator filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): T = @@ -576,8 +578,8 @@ template filterIt*(s, pred: untyped): untyped = ## ## Unlike the `filter func<#filter,openArray[T],proc(T)>`_ and ## `filter iterator<#filter.i,openArray[T],proc(T)>`_, - ## the predicate needs to be an expression using the ``it`` variable - ## for testing, like: ``filterIt("abcxyz", it == 'x')``. + ## the predicate needs to be an expression using the `it` variable + ## for testing, like: `filterIt("abcxyz", it == 'x')`. ## ## Instead of using `mapIt` and `filterIt`, consider using the `collect` macro ## from the `sugar` module. @@ -601,12 +603,12 @@ template filterIt*(s, pred: untyped): untyped = result template keepItIf*(varSeq: seq, pred: untyped) = - ## Keeps the items in the passed sequence (must be declared as a ``var``) + ## Keeps the items in the passed sequence (must be declared as a `var`) ## if they fulfilled the predicate. ## ## Unlike the `keepIf func<#keepIf,seq[T],proc(T)>`_, ## the predicate needs to be an expression using - ## the ``it`` variable for testing, like: ``keepItIf("abcxyz", it == 'x')``. + ## the `it` variable for testing, like: `keepItIf("abcxyz", it == 'x')`. ## ## See also: ## * `keepIf func<#keepIf,seq[T],proc(T)>`_ @@ -634,7 +636,7 @@ since (1, 1): ## Returns a count of all the items that fulfilled the predicate. ## ## The predicate needs to be an expression using - ## the ``it`` variable for testing, like: ``countIt(@[1, 2, 3], it > 2)``. + ## the `it` variable for testing, like: `countIt(@[1, 2, 3], it > 2)`. ## runnableExamples: let numbers = @[-3, -2, -1, 0, 1, 2, 3, 4, 5, 6] @@ -672,7 +674,7 @@ template allIt*(s, pred: untyped): bool = ## ## Unlike the `all func<#all,openArray[T],proc(T)>`_, ## the predicate needs to be an expression using - ## the ``it`` variable for testing, like: ``allIt("abba", it == 'a')``. + ## the `it` variable for testing, like: `allIt("abba", it == 'a')`. ## ## See also: ## * `all func<#all,openArray[T],proc(T)>`_ @@ -714,7 +716,7 @@ template anyIt*(s, pred: untyped): bool = ## ## Unlike the `any func<#any,openArray[T],proc(T)>`_, ## the predicate needs to be an expression using - ## the ``it`` variable for testing, like: ``anyIt("abba", it == 'a')``. + ## the `it` variable for testing, like: `anyIt("abba", it == 'a')`. ## ## See also: ## * `any func<#any,openArray[T],proc(T)>`_ @@ -817,10 +819,10 @@ template foldl*(sequence, operation: untyped): untyped = ## The sequence is required to have at least a single element. Debug versions ## of your program will assert in this situation but release versions will ## happily go ahead. If the sequence has a single element it will be returned - ## without applying ``operation``. + ## without applying `operation`. ## - ## The ``operation`` parameter should be an expression which uses the - ## variables ``a`` and ``b`` for each step of the fold. Since this is a left + ## The `operation` parameter should be an expression which uses the + ## variables `a` and `b` for each step of the fold. Since this is a left ## fold, for non associative binary operations like subtraction think that ## the sequence of numbers 1, 2 and 3 will be parenthesized as (((1) - 2) - ## 3). @@ -863,12 +865,12 @@ template foldl*(sequence, operation: untyped): untyped = template foldl*(sequence, operation, first): untyped = ## Template to fold a sequence from left to right, returning the accumulation. ## - ## This version of ``foldl`` gets a **starting parameter**. This makes it possible + ## This version of `foldl` gets a **starting parameter**. This makes it possible ## to accumulate the sequence into a different type than the sequence elements. ## - ## The ``operation`` parameter should be an expression which uses the variables - ## ``a`` and ``b`` for each step of the fold. The ``first`` parameter is the - ## start value (the first ``a``) and therefor defines the type of the result. + ## The `operation` parameter should be an expression which uses the variables + ## `a` and `b` for each step of the fold. The `first` parameter is the + ## start value (the first `a`) and therefor defines the type of the result. ## ## See also: ## * `foldr template<#foldr.t,untyped,untyped>`_ @@ -893,10 +895,10 @@ template foldr*(sequence, operation: untyped): untyped = ## The sequence is required to have at least a single element. Debug versions ## of your program will assert in this situation but release versions will ## happily go ahead. If the sequence has a single element it will be returned - ## without applying ``operation``. + ## without applying `operation`. ## - ## The ``operation`` parameter should be an expression which uses the - ## variables ``a`` and ``b`` for each step of the fold. Since this is a right + ## The `operation` parameter should be an expression which uses the + ## variables `a` and `b` for each step of the fold. Since this is a right ## fold, for non associative binary operations like subtraction think that ## the sequence of numbers 1, 2 and 3 will be parenthesized as (1 - (2 - ## (3))). @@ -936,7 +938,7 @@ template mapIt*(s: typed, op: untyped): untyped = ## Since the input is not modified you can use it to ## transform the type of the elements in the input container. ## - ## The template injects the ``it`` variable which you can use directly in an + ## The template injects the `it` variable which you can use directly in an ## expression. ## ## Instead of using `mapIt` and `filterIt`, consider using the `collect` macro @@ -1005,9 +1007,9 @@ template mapIt*(s: typed, op: untyped): untyped = map(s, f) template applyIt*(varSeq, op: untyped) = - ## Convenience template around the mutable ``apply`` func to reduce typing. + ## Convenience template around the mutable `apply` func to reduce typing. ## - ## The template injects the ``it`` variable which you can use directly in an + ## The template injects the `it` variable which you can use directly in an ## expression. The expression has to return the same type as the sequence you ## are mutating. ## @@ -1064,42 +1066,32 @@ func mapLitsImpl(constructor: NimNode; op: NimNode; nested: bool; macro mapLiterals*(constructor, op: untyped; nested = true): untyped = - ## Applies ``op`` to each of the **atomic** literals like ``3`` - ## or ``"abc"`` in the specified ``constructor`` AST. This can + ## Applies `op` to each of the **atomic** literals like `3` + ## or `"abc"` in the specified `constructor` AST. This can ## be used to map every array element to some target type: - ## - ## Example: - ## - ## .. code-block:: - ## let x = mapLiterals([0.1, 1.2, 2.3, 3.4], int) - ## doAssert x is array[4, int] - ## - ## Short notation for: - ## - ## .. code-block:: - ## let x = [int(0.1), int(1.2), int(2.3), int(3.4)] - ## - ## If ``nested`` is true (which is the default), the literals are replaced - ## everywhere in the ``constructor`` AST, otherwise only the first level + runnableExamples: + let x = mapLiterals([0.1, 1.2, 2.3, 3.4], int) + doAssert x is array[4, int] + doAssert x == [int(0.1), int(1.2), int(2.3), int(3.4)] + ## If `nested` is true (which is the default), the literals are replaced + ## everywhere in the `constructor` AST, otherwise only the first level ## is considered: - ## - ## .. code-block:: - ## let a = mapLiterals((1.2, (2.3, 3.4), 4.8), int) - ## let b = mapLiterals((1.2, (2.3, 3.4), 4.8), int, nested=false) - ## assert a == (1, (2, 3), 4) - ## assert b == (1, (2.3, 3.4), 4) - ## - ## let c = mapLiterals((1, (2, 3), 4, (5, 6)), `$`) - ## let d = mapLiterals((1, (2, 3), 4, (5, 6)), `$`, nested=false) - ## assert c == ("1", ("2", "3"), "4", ("5", "6")) - ## assert d == ("1", (2, 3), "4", (5, 6)) - ## - ## There are no constraints for the ``constructor`` AST, it + runnableExamples: + let a = mapLiterals((1.2, (2.3, 3.4), 4.8), int) + let b = mapLiterals((1.2, (2.3, 3.4), 4.8), int, nested=false) + assert a == (1, (2, 3), 4) + assert b == (1, (2.3, 3.4), 4) + + let c = mapLiterals((1, (2, 3), 4, (5, 6)), `$`) + let d = mapLiterals((1, (2, 3), 4, (5, 6)), `$`, nested=false) + assert c == ("1", ("2", "3"), "4", ("5", "6")) + assert d == ("1", (2, 3), "4", (5, 6)) + ## There are no constraints for the `constructor` AST, it ## works for nested tuples of arrays of sets etc. result = mapLitsImpl(constructor, op, nested.boolVal) iterator items*[T](xs: iterator: T): T = - ## iterates over each element yielded by a closure iterator. This may + ## Iterates over each element yielded by a closure iterator. This may ## not seem particularly useful on its own, but this allows closure ## iterators to be used by the mapIt, filterIt, allIt, anyIt, etc. ## templates. From 2728711dd3dbe8b1c2cd7dd6f15f696b58b77ea6 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 14 Dec 2020 08:49:39 -0800 Subject: [PATCH 1171/3768] fix #16248 forward --lib to runnableExamples (#16350) --- compiler/docgen.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 7b3b62174..824fafd6b 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -468,10 +468,11 @@ proc runAllExamples(d: PDoc) = writeFile(outp, group.code) # most useful semantics is that `docCmd` comes after `rdoccmd`, so that we can (temporarily) override # via command line - let cmd = "$nim $backend -r --warning:UnusedImport:off --path:$path --nimcache:$nimcache $rdoccmd $docCmd $file" % [ + let cmd = "$nim $backend -r --lib:$libpath --warning:UnusedImport:off --path:$path --nimcache:$nimcache $rdoccmd $docCmd $file" % [ "nim", os.getAppFilename(), "backend", $d.conf.backend, "path", quoteShell(d.conf.projectPath), + "libpath", quoteShell(d.conf.libpath), "nimcache", quoteShell(outputDir), "file", quoteShell(outp), "rdoccmd", group.rdoccmd, From e843492b1334ded1d8fcd0e0eb6dc94fcf970aba Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Mon, 14 Dec 2020 20:10:39 +0300 Subject: [PATCH 1172/3768] doc/rst2html: some few fixes for enumerated and bullet lists (#16295) * fix bullet/enumarated lists with many blank lines * fix enumerated list parsing * fix parse failure when next line after list empty * implement arbitrary start of enumerator * check that enumerators are in order * remove redundant start=x if x=1 or a * add some doc on implemented features * update start in rst_examples.rst * allow upper-case letters + more docs --- config/nimdoc.tex.cfg | 1 + lib/packages/docutils/rst.nim | 137 ++++++++++++++++---- lib/packages/docutils/rstast.nim | 2 +- lib/packages/docutils/rstgen.nim | 54 +++++++- nimdoc/rst2html/expected/rst_examples.html | 14 ++- tests/stdlib/trstgen.nim | 138 +++++++++++++++++++++ 6 files changed, 309 insertions(+), 37 deletions(-) diff --git a/config/nimdoc.tex.cfg b/config/nimdoc.tex.cfg index 76fef4afa..3e5e5d38b 100644 --- a/config/nimdoc.tex.cfg +++ b/config/nimdoc.tex.cfg @@ -50,6 +50,7 @@ doc.file = """ \usepackage{fancyvrb, courier} \usepackage{tabularx} \usepackage{hyperref} +\usepackage{enumitem} \begin{document} \title{$title $version} diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index bc3efa272..709173526 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -11,6 +11,59 @@ ## subset is implemented. Some features of the `markdown`:idx: wiki syntax are ## also supported. ## +## Supported RST features: +## +## * body elements +## + sections +## + transitions +## + paragraphs +## + bullet lists using \+, \*, \- +## + enumerated lists using arabic numerals or alphabet +## characters: 1. ... 2. ... *or* a. ... b. ... *or* A. ... B. ... +## + definition lists +## + field lists +## + option lists +## + indented literal blocks +## + simple tables +## + directives +## - image, figure +## - code-block +## - substitution definitions: replace and image +## - ... a few more +## + comments +## * inline markup +## + *emphasis*, **strong emphasis**, `interpreted text`, +## ``inline literals``, hyperlink references, substitution references, +## standalone hyperlinks +## +## Additional features: +## +## * ***triple emphasis*** (bold and italic) using \*\*\* +## +## Optional additional features, turned on by ``options: RstParseOption`` in +## `rstParse proc <#rstParse,string,string,int,int,bool,RstParseOptions,FindFileHandler,MsgHandler>`_: +## +## * emoji / smiley symbols +## * markdown tables +## * markdown code blocks +## * markdown links +## * markdown headlines +## +## Limitations: +## +## * no Unicode support in character width calculations +## * body elements +## - no roman numerals in enumerated lists +## - no quoted literal blocks +## - no doctest blocks +## - no grid tables +## - directives: no support for admonitions (notes, caution) +## - no footnotes & citations support +## - no inline internal targets +## * inline markup +## - no simple-inline-markup +## - no embedded URI and aliases +## ## **Note:** Import ``packages/docutils/rst`` to use this module import @@ -569,7 +622,9 @@ proc match(p: RstParser, start: int, expr: string): bool = # 'p' tkPunct # 'T' always true # 'E' whitespace, indent or eof - # 'e' tkWord or '#' (for enumeration lists) + # 'e' any enumeration sequence or '#' (for enumeration lists) + # 'x' a..z or '#' (for enumeration lists) + # 'n' 0..9 or '#' (for enumeration lists) var i = 0 var j = start var last = expr.len - 1 @@ -583,12 +638,16 @@ proc match(p: RstParser, start: int, expr: string): bool = of 'o': result = p.tok[j].kind == tkOther of 'T': result = true of 'E': result = p.tok[j].kind in {tkEof, tkWhite, tkIndent} - of 'e': + of 'e', 'x', 'n': result = p.tok[j].kind == tkWord or p.tok[j].symbol == "#" if result: case p.tok[j].symbol[0] - of 'a'..'z', 'A'..'Z', '#': result = p.tok[j].symbol.len == 1 - of '0'..'9': result = allCharsInSet(p.tok[j].symbol, {'0'..'9'}) + of '#': result = true + of 'a'..'z', 'A'..'Z': + result = expr[i] in {'e', 'x'} and p.tok[j].symbol.len == 1 + of '0'..'9': + result = expr[i] in {'e', 'n'} and + allCharsInSet(p.tok[j].symbol, {'0'..'9'}) else: result = false else: var c = expr[i] @@ -1465,33 +1524,55 @@ proc parseDefinitionList(p: var RstParser): PRstNode = proc parseEnumList(p: var RstParser): PRstNode = const - wildcards: array[0..2, string] = ["(e) ", "e) ", "e. "] - wildpos: array[0..2, int] = [1, 0, 0] - result = nil + wildcards: array[0..5, string] = ["(n) ", "n) ", "n. ", + "(x) ", "x) ", "x. "] + # enumerator patterns, where 'x' means letter and 'n' means number + wildToken: array[0..5, int] = [4, 3, 3, 4, 3, 3] # number of tokens + wildIndex: array[0..5, int] = [1, 0, 0, 1, 0, 0] + # position of enumeration sequence (number/letter) in enumerator + result = newRstNode(rnEnumList) + let col = currentTok(p).col var w = 0 - while w <= 2: + while w < wildcards.len: if match(p, p.idx, wildcards[w]): break inc w - if w <= 2: - var col = currentTok(p).col - result = newRstNode(rnEnumList) - inc p.idx, wildpos[w] + 3 - var j = tokenAfterNewline(p) - if p.tok[j].col == currentTok(p).col or match(p, j, wildcards[w]): - pushInd(p, currentTok(p).col) - while true: - var item = newRstNode(rnEnumItem) - parseSection(p, item) - result.add(item) - if currentTok(p).kind == tkIndent and currentTok(p).ival == col and - match(p, p.idx + 1, wildcards[w]): - inc p.idx, wildpos[w] + 4 - else: - break - popInd(p) + assert w < wildcards.len + for i in 0 ..< wildToken[w]-1: # add first enumerator with (, ), and . + if p.tok[p.idx + i].symbol == "#": + result.text.add "1" else: - dec p.idx, wildpos[w] + 3 - result = nil + result.text.add p.tok[p.idx + i].symbol + var prevEnum = p.tok[p.idx + wildIndex[w]].symbol + inc p.idx, wildToken[w] + while true: + var item = newRstNode(rnEnumItem) + pushInd(p, currentTok(p).col) + parseSection(p, item) + popInd(p) + result.add(item) + if currentTok(p).kind == tkIndent and currentTok(p).ival == col and + match(p, p.idx+1, wildcards[w]): + let enumerator = p.tok[p.idx + 1 + wildIndex[w]].symbol + # check that it's in sequence: enumerator == next(prevEnum) + if "n" in wildcards[w]: # arabic numeral + let prevEnumI = try: parseInt(prevEnum) except: 1 + let curEnum = + if enumerator == "#": prevEnumI + 1 + else: (try: parseInt(enumerator) except: 1) + if curEnum - prevEnumI != 1: + break + prevEnum = enumerator + else: # a..z + let prevEnumI = ord(prevEnum[0]) + let curEnum = + if enumerator == "#": prevEnumI + 1 + else: ord(enumerator[0]) + if curEnum - prevEnumI != 1: + break + prevEnum = $chr(curEnum) + inc p.idx, 1 + wildToken[w] + else: + break proc sonKind(father: PRstNode, i: int): RstNodeKind = result = rnLeaf @@ -1511,6 +1592,8 @@ proc parseSection(p: var RstParser, result: PRstNode) = result.add(a) popInd(p) else: + while currentTok(p).kind != tkEof and nextTok(p).kind == tkIndent: + inc p.idx # skip blank lines leave = true break if leave or currentTok(p).kind == tkEof: break diff --git a/lib/packages/docutils/rstast.nim b/lib/packages/docutils/rstast.nim index 8b2159aeb..5e2d21c04 100644 --- a/lib/packages/docutils/rstast.nim +++ b/lib/packages/docutils/rstast.nim @@ -69,7 +69,7 @@ type RstNode* {.acyclic, final.} = object ## an RST node's description kind*: RstNodeKind ## the node's kind text*: string ## valid for leafs in the AST; and the title of - ## the document or the section + ## the document or the section; and rnEnumList level*: int ## valid for some node kinds sons*: RstNodeSeq ## the node's sons diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index d88f2ed77..4d056a83e 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -1029,6 +1029,56 @@ proc renderField(d: PDoc, n: PRstNode, result: var string) = if not b: renderAux(d, n, "$1\n", "$1", result) +proc renderEnumList(d: PDoc, n: PRstNode, result: var string) = + var + specifier = "" + specStart = "" + i1 = 0 + pre = "" + i2 = n.text.len-1 + post = "" + if n.text[0] == '(': + i1 = 1 + pre = "(" + if n.text[^1] == ')' or n.text[^1] == '.': + i2 = n.text.len-2 + post = $n.text[^1] + let enumR = i1 .. i2 # enumerator range without surrounding (, ), . + if d.target == outLatex: + result.add ("\n%"&n.text&"\n") + # use enumerate parameters from package enumitem + if n.text[i1].isDigit: + var labelDef = "" + if pre != "" or post != "": + labelDef = "label=" & pre & "\\arabic*" & post & "," + if n.text[enumR] != "1": + specStart = "start=$1" % [n.text[enumR]] + if labelDef != "" or specStart != "": + specifier = "[$1$2]" % [labelDef, specStart] + else: + let (first, labelDef) = + if n.text[i1].isUpperAscii: ('A', "label=" & pre & "\\Alph*" & post) + else: ('a', "label=" & pre & "\\alph*" & post) + if n.text[i1] != first: + specStart = ",start=" & $(ord(n.text[i1]) - ord(first) + 1) + specifier = "[$1$2]" % [labelDef, specStart] + else: # HTML + # TODO: implement enumerator formatting using pre and post ( and ) for HTML + if n.text[i1].isDigit: + if n.text[enumR] != "1": + specStart = " start=\"$1\"" % [n.text[enumR]] + specifier = "class=\"simple\"" & specStart + else: + let (first, labelDef) = + if n.text[i1].isUpperAscii: ('A', "class=\"upperalpha simple\"") + else: ('a', "class=\"loweralpha simple\"") + if n.text[i1] != first: + specStart = " start=\"$1\"" % [ $(ord(n.text[i1]) - ord(first) + 1) ] + specifier = labelDef & specStart + renderAux(d, n, "
      $1
    \n", + "\\begin{enumerate}" & specifier & "$1\\end{enumerate}\n", + result) + proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = if n == nil: return case n.kind @@ -1042,9 +1092,7 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = "\\begin{itemize}$1\\end{itemize}\n", result) of rnBulletItem, rnEnumItem: renderAux(d, n, "
  • $1
  • \n", "\\item $1\n", result) - of rnEnumList: - renderAux(d, n, "
      $1
    \n", - "\\begin{enumerate}$1\\end{enumerate}\n", result) + of rnEnumList: renderEnumList(d, n, result) of rnDefList: renderAux(d, n, "
    $1
    \n", "\\begin{description}$1\\end{description}\n", result) diff --git a/nimdoc/rst2html/expected/rst_examples.html b/nimdoc/rst2html/expected/rst_examples.html index 0b9207688..a95fac0bd 100644 --- a/nimdoc/rst2html/expected/rst_examples.html +++ b/nimdoc/rst2html/expected/rst_examples.html @@ -274,15 +274,17 @@ stmt = IND{>} stmt ^+ IND{=} DED # list of statements
  • An input parameter should not be aliased with a global or thread local variable updated by the called proc.
  • One problem with rules 3 and 4 is that they affect specific global or thread local variables, but Nim's effect tracking only tracks "uses no global variable" via .noSideEffect. The rules 3 and 4 can also be approximated by a different rule:

    -
    1. A global or thread local variable (or a location derived from such a location) can only passed to a parameter of a .noSideEffect proc.
    2. +
      1. A global or thread local variable (or a location derived from such a location) can only passed to a parameter of a .noSideEffect proc.

      These two procs are the two modus operandi of the real-time garbage collector:

      -

      (1) GC_SetMaxPause Mode

      -

      You can call GC_SetMaxPause at program startup and then each triggered garbage collector run tries to not take longer than maxPause time. However, it is possible (and common) that the work is nevertheless not evenly distributed as each call to new can trigger the garbage collector and thus take maxPause time.

      -

      (2) GC_step Mode

      -

      This allows the garbage collector to perform some work for up to us time. This is useful to call in the main loop to ensure the garbage collector can do its work. To bind all garbage collector activity to a GC_step call, deactivate the garbage collector with GC_disable at program startup. If strongAdvice is set to true, then the garbage collector will be forced to perform the collection cycle. Otherwise, the garbage collector may decide not to do anything, if there is not much garbage to collect. You may also specify the current stack size via stackSize parameter. It can improve performance when you know that there are no unique Nim references below a certain point on the stack. Make sure the size you specify is greater than the potential worst-case size.

      +
      1. GC_SetMaxPause Mode

        +

        You can call GC_SetMaxPause at program startup and then each triggered garbage collector run tries to not take longer than maxPause time. However, it is possible (and common) that the work is nevertheless not evenly distributed as each call to new can trigger the garbage collector and thus take maxPause time.

        +
      2. +
      3. GC_step Mode

        +

        This allows the garbage collector to perform some work for up to us time. This is useful to call in the main loop to ensure the garbage collector can do its work. To bind all garbage collector activity to a GC_step call, deactivate the garbage collector with GC_disable at program startup. If strongAdvice is set to true, then the garbage collector will be forced to perform the collection cycle. Otherwise, the garbage collector may decide not to do anything, if there is not much garbage to collect. You may also specify the current stack size via stackSize parameter. It can improve performance when you know that there are no unique Nim references below a certain point on the stack. Make sure the size you specify is greater than the potential worst-case size.

        It can improve performance when you know that there are no unique Nim references below a certain point on the stack. Make sure the size you specify is greater than the potential worst-case size.

        -

      + +

    These procs provide a "best effort" real-time guarantee; in particular the cycle collector is not aware of deadlines. Deactivate it to get more predictable real-time behaviour. Tests show that a 1ms max pause time will be met in almost all cases on modern CPUs (with the cycle collector disabled).

    Time measurement with garbage collectors

    The garbage collectors' way of measuring time uses (see lib/system/timers.nim for the implementation):

    diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim index 8b12a89c4..f3a8aa59a 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -361,6 +361,144 @@ Test1 assert "line block\\\\" in output1l assert "other line\\\\" in output1l + test "RST enumerated lists": + let input1 = dedent """ + 1. line1 + 1 + 2. line2 + 2 + + 3. line3 + 3 + + + 4. line4 + 4 + + + + 5. line5 + 5 + """ + let output1 = rstToHtml(input1, {roSupportMarkdown}, defaultConfig()) + for i in 1..5: + assert ($i & ". line" & $i) notin output1 + assert ("
  • line" & $i & " " & $i & "
  • ") in output1 + + let input2 = dedent """ + 3. line3 + + 4. line4 + + + 5. line5 + + + + 7. line7 + + + + + 8. line8 + """ + let output2 = rstToHtml(input2, {roSupportMarkdown}, defaultConfig()) + for i in [3, 4, 5, 7, 8]: + assert ($i & ". line" & $i) notin output2 + assert ("
  • line" & $i & "
  • ") in output2 + + # check that nested enumerated lists work + let input3 = dedent """ + 1. a) string1 + 2. string2 + """ + let output3 = rstToHtml(input3, {roSupportMarkdown}, defaultConfig()) + assert count(output3, "
      ") == 2 + assert "
    1. string1
    2. " in output3 and "
    3. string2
    4. " in output3 + + let input4 = dedent """ + Check that enumeration specifiers are respected + + 9. string1 + 10. string2 + 12. string3 + + b) string4 + c) string5 + e) string6 + """ + let output4 = rstToHtml(input4, {roSupportMarkdown}, defaultConfig()) + assert count(output4, "
        ") == 4 + for enumerator in [9, 12]: + assert "start=\"$1\"" % [$enumerator] in output4 + for enumerator in [2, 5]: # 2=b, 5=e + assert "start=\"$1\"" % [$enumerator] in output4 + + let input5 = dedent """ + Check that auto-numbered enumeration lists work. + + #. string1 + #. string2 + #. string3 + """ + let output5 = rstToHtml(input5, {roSupportMarkdown}, defaultConfig()) + assert count(output5, "
          ") == 1 + assert count(output5, "
        1. ") == 3 + + let input6 = dedent """ + ... And for alphabetic enumerators too! + + b. string1 + #. string2 + #. string3 + """ + let output6 = rstToHtml(input6, {roSupportMarkdown}, defaultConfig()) + assert count(output6, "
            ") == 1 + assert count(output6, "
          1. ") == 3 + assert "start=\"2\"" in output6 and "class=\"loweralpha simple\"" in output6 + + let input7 = dedent """ + ... And for uppercase alphabetic enumerators. + + C. string1 + #. string2 + #. string3 + """ + let output7 = rstToHtml(input7, {roSupportMarkdown}, defaultConfig()) + assert count(output7, "
              ") == 1 + assert count(output7, "
            1. ") == 3 + assert "start=\"3\"" in output7 and "class=\"upperalpha simple\"" in output7 + + test "RST bullet lists": + let input1 = dedent """ + * line1 + 1 + * line2 + 2 + + * line3 + 3 + + + * line4 + 4 + + + + * line5 + 5 + """ + let output1 = rstToHtml(input1, {roSupportMarkdown}, defaultConfig()) + for i in 1..5: + assert ("
            2. line" & $i & " " & $i & "
            3. ") in output1 + assert count(output1, "
                ") == 1 + suite "RST/Code highlight": test "Basic Python code highlight": let pythonCode = """ From f3d57761abc425f546985af407f178d71db20b78 Mon Sep 17 00:00:00 2001 From: Elliot Waite Date: Mon, 14 Dec 2020 11:13:12 -0800 Subject: [PATCH 1173/3768] Fix broken links in docs (#16336) * Fix broken links in docs * Fix rand HSlice links --- doc/docgen.rst | 2 +- doc/manual.rst | 4 +-- doc/tut1.rst | 4 +-- lib/js/dom.nim | 2 +- lib/pure/asyncdispatch.nim | 2 +- lib/pure/collections/sets.nim | 20 +++++------ lib/pure/collections/tables.nim | 54 ++++++++++++++--------------- lib/pure/concurrency/threadpool.nim | 2 +- lib/pure/httpclient.nim | 4 +-- lib/pure/logging.nim | 8 ++--- lib/pure/net.nim | 2 +- lib/pure/osproc.nim | 2 +- lib/pure/random.nim | 28 +++++++++------ lib/pure/smtp.nim | 4 +-- lib/pure/unicode.nim | 2 +- lib/std/jsonutils.nim | 8 ++--- lib/std/packedsets.nim | 6 ++-- lib/system.nim | 24 ++++++------- lib/system/assertions.nim | 2 +- lib/system/memalloc.nim | 24 ++++++------- lib/system_overview.rst | 42 +++++++++++----------- 21 files changed, 126 insertions(+), 120 deletions(-) diff --git a/doc/docgen.rst b/doc/docgen.rst index 612465671..bbf0838bb 100644 --- a/doc/docgen.rst +++ b/doc/docgen.rst @@ -316,7 +316,7 @@ symbols in the `system module `_. * ``proc len[T](x: seq[T]): int {.magic: "LengthSeq", noSideEffect.}`` **=>** `#len,seq[T] `_ * ``iterator pairs[T](a: seq[T]): tuple[key: int, val: T] {.inline.}`` **=>** - `#pairs.i,seq[T] `_ + `#pairs.i,seq[T] `_ * ``template newException[](exceptn: typedesc; message: string; parentException: ref Exception = nil): untyped`` **=>** `#newException.t,typedesc,string,ref.Exception diff --git a/doc/manual.rst b/doc/manual.rst index 97dbbda13..cb35ee885 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -1778,7 +1778,7 @@ dereferencing operations for reference types: Automatic dereferencing can be performed for the first argument of a routine call, but this is an experimental feature and is described `here -`_. +`_. In order to simplify structural type checking, recursive tuples are not valid: @@ -3653,7 +3653,7 @@ Creating closures in loops Since closures capture local variables by reference it is often not wanted behavior inside loop bodies. See `closureScope `_ and `capture -`_ for details on how to change this behavior. +`_ for details on how to change this behavior. Anonymous Procs --------------- diff --git a/doc/tut1.rst b/doc/tut1.rst index cb4dc1cc9..397782330 100644 --- a/doc/tut1.rst +++ b/doc/tut1.rst @@ -1315,11 +1315,11 @@ Sequence variables are initialized with ``@[]``. The ``for`` statement can be used with one or two variables when used with a sequence. When you use the one variable form, the variable will hold the value provided by the sequence. The ``for`` statement is looping over the results -from the `items() `_ iterator from the `system +from the `items() `_ iterator from the `system `_ module. But if you use the two-variable form, the first variable will hold the index position and the second variable will hold the value. Here the ``for`` statement is looping over the results from the -`pairs() `_ iterator from the `system +`pairs() `_ iterator from the `system `_ module. Examples: .. code-block:: nim diff --git a/lib/js/dom.nim b/lib/js/dom.nim index b74a58a1a..4dbb4f77a 100644 --- a/lib/js/dom.nim +++ b/lib/js/dom.nim @@ -107,7 +107,7 @@ type timing*: PerformanceTiming Range* {.importc.} = ref object - ## see `docs{https://developer.mozilla.org/en-US/docs/Web/API/Range}`_ + ## see `docs`_ collapsed*: bool commonAncestorContainer*: Node endContainer*: Node diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index e111f9e7d..1b3548268 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -235,7 +235,7 @@ template implementSetInheritable() {.dirty.} = ## Returns ``true`` on success. ## ## This procedure is not guaranteed to be available for all platforms. - ## Test for availability with `declared()`_. + ## Test for availability with `declared() `_. fd.FileHandle.setInheritable(inheritable) when defined(windows) or defined(nimdoc): diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim index faa2f8c71..a9df9ded8 100644 --- a/lib/pure/collections/sets.nim +++ b/lib/pure/collections/sets.nim @@ -64,7 +64,7 @@ type HashSet*[A] {.myShallow.} = object ## \ ## A generic hash set. ## - ## Use `init proc <#init,HashSet[A],int>`_ or `initHashSet proc <#initHashSet,int>`_ + ## Use `init proc <#init,HashSet[A]>`_ or `initHashSet proc <#initHashSet,int>`_ ## before calling other procs on it. data: KeyValuePairSeq[A] counter: int @@ -76,8 +76,8 @@ type OrderedSet*[A] {.myShallow.} = object ## \ ## A generic hash set that remembers insertion order. ## - ## Use `init proc <#init,OrderedSet[A],int>`_ or `initOrderedSet proc - ## <#initOrderedSet,int>`_ before calling other procs on it. + ## Use `init proc <#init,OrderedSet[A]>`_ or `initOrderedSet proc + ## <#initOrderedSet>`_ before calling other procs on it. data: OrderedKeyValuePairSeq[A] counter, first, last: int SomeSet*[A] = HashSet[A] | OrderedSet[A] @@ -104,7 +104,7 @@ proc init*[A](s: var HashSet[A], initialSize = defaultInitialSize) = ## existing values and calling `excl() <#excl,HashSet[A],A>`_ on them. ## ## See also: - ## * `initHashSet proc <#initHashSet,int>`_ + ## * `initHashSet proc <#initHashSet>`_ ## * `toHashSet proc <#toHashSet,openArray[A]>`_ runnableExamples: var a: HashSet[int] @@ -113,7 +113,7 @@ proc init*[A](s: var HashSet[A], initialSize = defaultInitialSize) = initImpl(s, initialSize) proc initHashSet*[A](initialSize = defaultInitialSize): HashSet[A] = - ## Wrapper around `init proc <#init,HashSet[A],int>`_ for initialization of + ## Wrapper around `init proc <#init,HashSet[A]>`_ for initialization of ## hash sets. ## ## Returns an empty hash set you can assign directly in ``var`` blocks in a @@ -209,7 +209,7 @@ proc toHashSet*[A](keys: openArray[A]): HashSet[A] = ## Duplicates are removed. ## ## See also: - ## * `initHashSet proc <#initHashSet,int>`_ + ## * `initHashSet proc <#initHashSet>`_ runnableExamples: let a = toHashSet([5, 3, 2]) @@ -601,7 +601,7 @@ proc toSet*[A](keys: openArray[A]): HashSet[A] {.deprecated: proc isValid*[A](s: HashSet[A]): bool {.deprecated: "Deprecated since v0.20; sets are initialized by default".} = ## Returns `true` if the set has been initialized (with `initHashSet proc - ## <#initHashSet,int>`_ or `init proc <#init,HashSet[A],int>`_). + ## <#initHashSet>`_ or `init proc <#init,HashSet[A]>`_). ## ## **Examples:** ## @@ -640,7 +640,7 @@ proc init*[A](s: var OrderedSet[A], initialSize = defaultInitialSize) = ## existing values and calling `excl() <#excl,HashSet[A],A>`_ on them. ## ## See also: - ## * `initOrderedSet proc <#initOrderedSet,int>`_ + ## * `initOrderedSet proc <#initOrderedSet>`_ ## * `toOrderedSet proc <#toOrderedSet,openArray[A]>`_ runnableExamples: var a: OrderedSet[int] @@ -649,7 +649,7 @@ proc init*[A](s: var OrderedSet[A], initialSize = defaultInitialSize) = initImpl(s, initialSize) proc initOrderedSet*[A](initialSize = defaultInitialSize): OrderedSet[A] = - ## Wrapper around `init proc <#init,OrderedSet[A],int>`_ for initialization of + ## Wrapper around `init proc <#init,OrderedSet[A]>`_ for initialization of ## ordered hash sets. ## ## Returns an empty ordered hash set you can assign directly in ``var`` blocks @@ -674,7 +674,7 @@ proc toOrderedSet*[A](keys: openArray[A]): OrderedSet[A] = ## Duplicates are removed. ## ## See also: - ## * `initOrderedSet proc <#initOrderedSet,int>`_ + ## * `initOrderedSet proc <#initOrderedSet>`_ runnableExamples: let a = toOrderedSet([5, 3, 2]) diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index a0efcdce2..d7376b065 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -135,7 +135,7 @@ ## ## The same could have been achieved by manually iterating over a container ## and increasing each key's value with `inc proc -## <#inc,CountTable[A],A,Positive>`_: +## <#inc,CountTable[A],A,int>`_: ## ## .. code-block:: ## import tables @@ -230,13 +230,13 @@ type ## `data` and `counter` are internal implementation details which ## can't be accessed. ## - ## For creating an empty Table, use `initTable proc<#initTable,int>`_. + ## For creating an empty Table, use `initTable proc<#initTable>`_. data: KeyValuePairSeq[A, B] counter: int TableRef*[A, B] = ref Table[A, B] ## Ref version of `Table<#Table>`_. ## ## For creating a new empty TableRef, use `newTable proc - ## <#newTable,int>`_. + ## <#newTable>`_. const defaultInitialSize* = 32 @@ -293,7 +293,7 @@ proc initTable*[A, B](initialSize = defaultInitialSize): Table[A, B] = ## ## See also: ## * `toTable proc<#toTable,openArray[]>`_ - ## * `newTable proc<#newTable,int>`_ for creating a `TableRef` + ## * `newTable proc<#newTable>`_ for creating a `TableRef` runnableExamples: let a = initTable[int, string]() @@ -322,7 +322,7 @@ proc toTable*[A, B](pairs: openArray[(A, B)]): Table[A, B] = ## ``pairs`` is a container consisting of ``(key, value)`` tuples. ## ## See also: - ## * `initTable proc<#initTable,int>`_ + ## * `initTable proc<#initTable>`_ ## * `newTable proc<#newTable,openArray[]>`_ for a `TableRef` version runnableExamples: let a = [('a', 5), ('b', 9)] @@ -771,7 +771,7 @@ iterator allValues*[A, B](t: Table[A, B]; key: A): B {.deprecated: ## Iterates over any value in the table ``t`` that belongs to the given ``key``. ## ## Used if you have a table with duplicate keys (as a result of using - ## `add proc<#add,Table[A,B],A,B>`_). + ## `add proc<#add,Table[A,B],A,sinkB>`_). ## runnableExamples: import sequtils, algorithm @@ -801,7 +801,7 @@ proc newTable*[A, B](initialSize = defaultInitialSize): TableRef[A, B] = ## See also: ## * `newTable proc<#newTable,openArray[]>`_ for creating a `TableRef` ## from a collection of `(key, value)` pairs - ## * `initTable proc<#initTable,int>`_ for creating a `Table` + ## * `initTable proc<#initTable>`_ for creating a `Table` runnableExamples: let a = newTable[int, string]() @@ -816,7 +816,7 @@ proc newTable*[A, B](pairs: openArray[(A, B)]): TableRef[A, B] = ## ``pairs`` is a container consisting of ``(key, value)`` tuples. ## ## See also: - ## * `newTable proc<#newTable,int>`_ + ## * `newTable proc<#newTable>`_ ## * `toTable proc<#toTable,openArray[]>`_ for a `Table` version runnableExamples: let a = [('a', 5), ('b', 9)] @@ -845,7 +845,7 @@ proc `[]`*[A, B](t: TableRef[A, B], key: A): var B = ## a default value (e.g. zero for int) if the key doesn't exist ## * `getOrDefault proc<#getOrDefault,TableRef[A,B],A,B>`_ to return ## a custom value if the key doesn't exist - ## * `[]= proc<#[]=,TableRef[A,B],A,B>`_ for inserting a new + ## * `[]= proc<#[]=,TableRef[A,B],A,sinkB>`_ for inserting a new ## (key, value) pair in the table ## * `hasKey proc<#hasKey,TableRef[A,B],A>`_ for checking if a key is in ## the table @@ -1008,7 +1008,7 @@ proc add*[A, B](t: TableRef[A, B], key: A, val: sink B) {.deprecated: ## ## **This can introduce duplicate keys into the table!** ## - ## Use `[]= proc<#[]=,TableRef[A,B],A,B>`_ for inserting a new + ## Use `[]= proc<#[]=,TableRef[A,B],A,sinkB>`_ for inserting a new ## (key, value) pair in the table without introducing duplicates. t[].add(key, val) @@ -1228,14 +1228,14 @@ type ## Hash table that remembers insertion order. ## ## For creating an empty OrderedTable, use `initOrderedTable proc - ## <#initOrderedTable,int>`_. + ## <#initOrderedTable>`_. data: OrderedKeyValuePairSeq[A, B] counter, first, last: int OrderedTableRef*[A, B] = ref OrderedTable[A, B] ## Ref version of ## `OrderedTable<#OrderedTable>`_. ## ## For creating a new empty OrderedTableRef, use `newOrderedTable proc - ## <#newOrderedTable,int>`_. + ## <#newOrderedTable>`_. # ------------------------------ helpers --------------------------------- @@ -1294,7 +1294,7 @@ proc initOrderedTable*[A, B](initialSize = defaultInitialSize): OrderedTable[A, ## ## See also: ## * `toOrderedTable proc<#toOrderedTable,openArray[]>`_ - ## * `newOrderedTable proc<#newOrderedTable,int>`_ for creating an + ## * `newOrderedTable proc<#newOrderedTable>`_ for creating an ## `OrderedTableRef` runnableExamples: let @@ -1324,7 +1324,7 @@ proc toOrderedTable*[A, B](pairs: openArray[(A, B)]): OrderedTable[A, B] = ## ``pairs`` is a container consisting of ``(key, value)`` tuples. ## ## See also: - ## * `initOrderedTable proc<#initOrderedTable,int>`_ + ## * `initOrderedTable proc<#initOrderedTable>`_ ## * `newOrderedTable proc<#newOrderedTable,openArray[]>`_ for an ## `OrderedTableRef` version runnableExamples: @@ -1347,7 +1347,7 @@ proc `[]`*[A, B](t: OrderedTable[A, B], key: A): B = ## a default value (e.g. zero for int) if the key doesn't exist ## * `getOrDefault proc<#getOrDefault,OrderedTable[A,B],A,B>`_ to return ## a custom value if the key doesn't exist - ## * `[]= proc<#[]=,OrderedTable[A,B],A,B>`_ for inserting a new + ## * `[]= proc<#[]=,OrderedTable[A,B],A,sinkB>`_ for inserting a new ## (key, value) pair in the table ## * `hasKey proc<#hasKey,OrderedTable[A,B],A>`_ for checking if a ## key is in the table @@ -1369,7 +1369,7 @@ proc `[]`*[A, B](t: var OrderedTable[A, B], key: A): var B = ## a default value (e.g. zero for int) if the key doesn't exist ## * `getOrDefault proc<#getOrDefault,OrderedTable[A,B],A,B>`_ to return ## a custom value if the key doesn't exist - ## * `[]= proc<#[]=,OrderedTable[A,B],A,B>`_ for inserting a new + ## * `[]= proc<#[]=,OrderedTable[A,B],A,sinkB>`_ for inserting a new ## (key, value) pair in the table ## * `hasKey proc<#hasKey,OrderedTable[A,B],A>`_ for checking if a ## key is in the table @@ -1495,7 +1495,7 @@ proc add*[A, B](t: var OrderedTable[A, B], key: A, val: sink B) {.deprecated: ## ## **This can introduce duplicate keys into the table!** ## - ## Use `[]= proc<#[]=,OrderedTable[A,B],A,B>`_ for inserting a new + ## Use `[]= proc<#[]=,OrderedTable[A,B],A,sinkB>`_ for inserting a new ## (key, value) pair in the table without introducing duplicates. addImpl(enlarge) @@ -1796,7 +1796,7 @@ proc newOrderedTable*[A, B](initialSize = defaultInitialSize): OrderedTableR ## See also: ## * `newOrderedTable proc<#newOrderedTable,openArray[]>`_ for creating ## an `OrderedTableRef` from a collection of `(key, value)` pairs - ## * `initOrderedTable proc<#initOrderedTable,int>`_ for creating an + ## * `initOrderedTable proc<#initOrderedTable>`_ for creating an ## `OrderedTable` runnableExamples: let @@ -1811,7 +1811,7 @@ proc newOrderedTable*[A, B](pairs: openArray[(A, B)]): OrderedTableRef[A, B] ## ``pairs`` is a container consisting of ``(key, value)`` tuples. ## ## See also: - ## * `newOrderedTable proc<#newOrderedTable,int>`_ + ## * `newOrderedTable proc<#newOrderedTable>`_ ## * `toOrderedTable proc<#toOrderedTable,openArray[]>`_ for an ## `OrderedTable` version runnableExamples: @@ -1835,7 +1835,7 @@ proc `[]`*[A, B](t: OrderedTableRef[A, B], key: A): var B = ## a default value (e.g. zero for int) if the key doesn't exist ## * `getOrDefault proc<#getOrDefault,OrderedTableRef[A,B],A,B>`_ to return ## a custom value if the key doesn't exist - ## * `[]= proc<#[]=,OrderedTableRef[A,B],A,B>`_ for inserting a new + ## * `[]= proc<#[]=,OrderedTableRef[A,B],A,sinkB>`_ for inserting a new ## (key, value) pair in the table ## * `hasKey proc<#hasKey,OrderedTableRef[A,B],A>`_ for checking if ## a key is in the table @@ -1981,7 +1981,7 @@ proc add*[A, B](t: OrderedTableRef[A, B], key: A, val: sink B) {.deprecated: ## ## **This can introduce duplicate keys into the table!** ## - ## Use `[]= proc<#[]=,OrderedTableRef[A,B],A,B>`_ for inserting a new + ## Use `[]= proc<#[]=,OrderedTableRef[A,B],A,sinkB>`_ for inserting a new ## (key, value) pair in the table without introducing duplicates. t[].add(key, val) @@ -2208,7 +2208,7 @@ type ## Hash table that counts the number of each key. ## ## For creating an empty CountTable, use `initCountTable proc - ## <#initCountTable,int>`_. + ## <#initCountTable>`_. data: seq[tuple[key: A, val: int]] counter: int isSorted: bool @@ -2216,7 +2216,7 @@ type ## `CountTable<#CountTable>`_. ## ## For creating a new empty CountTableRef, use `newCountTable proc - ## <#newCountTable,int>`_. + ## <#newCountTable>`_. # ------------------------------ helpers --------------------------------- @@ -2260,7 +2260,7 @@ proc initCountTable*[A](initialSize = defaultInitialSize): CountTable[A] = ## ## See also: ## * `toCountTable proc<#toCountTable,openArray[A]>`_ - ## * `newCountTable proc<#newCountTable,int>`_ for creating a + ## * `newCountTable proc<#newCountTable>`_ for creating a ## `CountTableRef` initImpl(result, initialSize) @@ -2294,7 +2294,7 @@ proc `[]=`*[A](t: var CountTable[A], key: A, val: int) = ## ## See also: ## * `[] proc<#[],CountTable[A],A>`_ for retrieving a value of a key - ## * `inc proc<#inc,CountTable[A],A,Positive>`_ for incrementing a + ## * `inc proc<#inc,CountTable[A],A,int>`_ for incrementing a ## value of a key assert(not t.isSorted, "CountTable must not be used after sorting") assert val >= 0 @@ -2617,7 +2617,7 @@ proc newCountTable*[A](initialSize = defaultInitialSize): CountTableRef[A] = ## See also: ## * `newCountTable proc<#newCountTable,openArray[A]>`_ for creating ## a `CountTableRef` from a collection - ## * `initCountTable proc<#initCountTable,int>`_ for creating a + ## * `initCountTable proc<#initCountTable>`_ for creating a ## `CountTable` new(result) result[] = initCountTable[A](initialSize) @@ -2635,7 +2635,7 @@ proc `[]`*[A](t: CountTableRef[A], key: A): int = ## See also: ## * `getOrDefault<#getOrDefault,CountTableRef[A],A,int>`_ to return ## a custom value if the key doesn't exist - ## * `inc proc<#inc,CountTableRef[A],A>`_ to inc even if missing + ## * `inc proc<#inc,CountTableRef[A],A,int>`_ to inc even if missing ## * `[]= proc<#[]%3D,CountTableRef[A],A,int>`_ for inserting a new ## (key, value) pair in the table ## * `hasKey proc<#hasKey,CountTableRef[A],A>`_ for checking if a key diff --git a/lib/pure/concurrency/threadpool.nim b/lib/pure/concurrency/threadpool.nim index f0269f526..34a65ead2 100644 --- a/lib/pure/concurrency/threadpool.nim +++ b/lib/pure/concurrency/threadpool.nim @@ -468,7 +468,7 @@ template spawnX*(call) = ## Spawns a new task if a CPU core is ready, otherwise executes the ## call in the calling thread. ## - ## Usually it is advised to use `spawn proc <#spawn,typed>`_ in order to + ## Usually it is advised to use `spawn proc <#spawn,sinktyped>`_ in order to ## not block the producer for an unknown amount of time. ## ## ``call`` has to be proc call ``p(...)`` where ``p`` is gcsafe and has a diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 880c05765..3093f5564 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -129,7 +129,7 @@ ## A set of directories and files from the `ssl_certs `_ ## module are scanned to locate CA certificates. ## -## See `newContext `_ to tweak or disable certificate validation. +## See `newContext `_ to tweak or disable certificate validation. ## ## Timeouts ## ======== @@ -565,7 +565,7 @@ proc newHttpClient*(userAgent = defUserAgent, maxRedirects = 5, ## default is 5. ## ## ``sslContext`` specifies the SSL context to use for HTTPS requests. - ## See `SSL/TLS support <##ssl-tls-support>`_ + ## See `SSL/TLS support <#sslslashtls-support>`_ ## ## ``proxy`` specifies an HTTP proxy to use for this HTTP client's ## connections. diff --git a/lib/pure/logging.nim b/lib/pure/logging.nim index fa6285a04..06987d395 100644 --- a/lib/pure/logging.nim +++ b/lib/pure/logging.nim @@ -246,7 +246,7 @@ when not defined(js): ## rotation. ## ## Create a new ``RollingFileLogger`` with the `newRollingFileLogger proc - ## <#newRollingFileLogger,FileMode,int,int>`_. + ## <#newRollingFileLogger,FileMode,Positive,int>`_. ## ## **Note:** This logger is not available for the JavaScript backend. ## @@ -395,7 +395,7 @@ proc newConsoleLogger*(levelThreshold = lvlAll, fmtStr = defaultFmtStr, ## * `newFileLogger proc<#newFileLogger,File>`_ that uses a file handle ## * `newFileLogger proc<#newFileLogger,FileMode,int>`_ ## that accepts a filename - ## * `newRollingFileLogger proc<#newRollingFileLogger,FileMode,int,int>`_ + ## * `newRollingFileLogger proc<#newRollingFileLogger,FileMode,Positive,int>`_ ## ## **Examples:** ## @@ -460,7 +460,7 @@ when not defined(js): ## * `newConsoleLogger proc<#newConsoleLogger>`_ ## * `newFileLogger proc<#newFileLogger,FileMode,int>`_ ## that accepts a filename - ## * `newRollingFileLogger proc<#newRollingFileLogger,FileMode,int,int>`_ + ## * `newRollingFileLogger proc<#newRollingFileLogger,FileMode,Positive,int>`_ ## ## **Examples:** ## @@ -496,7 +496,7 @@ when not defined(js): ## See also: ## * `newConsoleLogger proc<#newConsoleLogger>`_ ## * `newFileLogger proc<#newFileLogger,File>`_ that uses a file handle - ## * `newRollingFileLogger proc<#newRollingFileLogger,FileMode,int,int>`_ + ## * `newRollingFileLogger proc<#newRollingFileLogger,FileMode,Positive,int>`_ ## ## **Examples:** ## diff --git a/lib/pure/net.nim b/lib/pure/net.nim index cd509463a..02441e8a0 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -21,7 +21,7 @@ ## ## In order to use the SSL procedures defined in this module, you will need to ## compile your application with the ``-d:ssl`` flag. See the -## `newContext`_ +## `newContext`_ ## procedure for additional details. ## ## Examples diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index a7c6c3754..d6943c7f8 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -106,7 +106,7 @@ proc execCmd*(command: string): int {.rtl, extern: "nosp$1", ## This operation is also often called `system`:idx:. ## ## See also: - ## * `execCmdEx proc <#execCmdEx,string,set[ProcessOption]>`_ + ## * `execCmdEx proc <#execCmdEx,string,set[ProcessOption],StringTableRef,string,string>`_ ## * `startProcess proc ## <#startProcess,string,string,openArray[string],StringTableRef,set[ProcessOption]>`_ ## * `execProcess proc diff --git a/lib/pure/random.nim b/lib/pure/random.nim index 683140cd8..d599727c3 100644 --- a/lib/pure/random.nim +++ b/lib/pure/random.nim @@ -11,7 +11,7 @@ ## ## Its implementation is based on the ``xoroshiro128+`` ## (xor/rotate/shift/rotate) library. -## * More information: http://xoroshiro.di.unimi.it/ +## * More information: http://xoroshiro.di.unimi.it ## * C implementation: http://xoroshiro.di.unimi.it/xoroshiro128plus.c ## ## **Do not use this module for cryptographic purposes!** @@ -135,7 +135,8 @@ proc next*(r: var Rand): uint64 = ## * `rand proc<#rand,Rand,Natural>`_ that returns an integer between zero and ## a given upper bound ## * `rand proc<#rand,Rand,range[]>`_ that returns a float - ## * `rand proc<#rand,Rand,HSlice[T,T]>`_ that accepts a slice + ## * `rand proc<#rand,Rand,HSlice[T: Ordinal or float or float32 or float64,T: Ordinal or float or float32 or float64]>`_ + ## that accepts a slice ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type ## * `skipRandomNumbers proc<#skipRandomNumbers,Rand>`_ runnableExamples: @@ -220,7 +221,8 @@ proc rand*(r: var Rand; max: Natural): int {.benign.} = ## * `rand proc<#rand,int>`_ that returns an integer using the default ## random number generator ## * `rand proc<#rand,Rand,range[]>`_ that returns a float - ## * `rand proc<#rand,Rand,HSlice[T,T]>`_ that accepts a slice + ## * `rand proc<#rand,Rand,HSlice[T: Ordinal or float or float32 or float64,T: Ordinal or float or float32 or float64]>`_ + ## that accepts a slice ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type runnableExamples: var r = initRand(123) @@ -246,7 +248,8 @@ proc rand*(max: int): int {.benign.} = ## * `rand proc<#rand,Rand,Natural>`_ that returns an integer using a ## provided state ## * `rand proc<#rand,float>`_ that returns a float - ## * `rand proc<#rand,HSlice[T,T]>`_ that accepts a slice + ## * `rand proc<#rand,HSlice[T: Ordinal or float or float32 or float64,T: Ordinal or float or float32 or float64]>`_ + ## that accepts a slice ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type runnableExamples: randomize(123) @@ -263,7 +266,8 @@ proc rand*(r: var Rand; max: range[0.0 .. high(float)]): float {.benign.} = ## * `rand proc<#rand,float>`_ that returns a float using the default ## random number generator ## * `rand proc<#rand,Rand,Natural>`_ that returns an integer - ## * `rand proc<#rand,Rand,HSlice[T,T]>`_ that accepts a slice + ## * `rand proc<#rand,Rand,HSlice[T: Ordinal or float or float32 or float64,T: Ordinal or float or float32 or float64]>`_ + ## that accepts a slice ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type runnableExamples: var r = initRand(234) @@ -289,7 +293,8 @@ proc rand*(max: float): float {.benign.} = ## * `rand proc<#rand,Rand,range[]>`_ that returns a float using a ## provided state ## * `rand proc<#rand,int>`_ that returns an integer - ## * `rand proc<#rand,HSlice[T,T]>`_ that accepts a slice + ## * `rand proc<#rand,HSlice[T: Ordinal or float or float32 or float64,T: Ordinal or float or float32 or float64]>`_ + ## that accepts a slice ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type runnableExamples: randomize(234) @@ -304,8 +309,8 @@ proc rand*[T: Ordinal or SomeFloat](r: var Rand; x: HSlice[T, T]): T = ## Allowed types for `T` are integers, floats, and enums without holes. ## ## See also: - ## * `rand proc<#rand,HSlice[T,T]>`_ that accepts a slice and uses the - ## default random number generator + ## * `rand proc<#rand,HSlice[T: Ordinal or float or float32 or float64,T: Ordinal or float or float32 or float64]>`_ + ## that accepts a slice and uses the default random number generator ## * `rand proc<#rand,Rand,Natural>`_ that returns an integer ## * `rand proc<#rand,Rand,range[]>`_ that returns a float ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type @@ -333,8 +338,8 @@ proc rand*[T: Ordinal or SomeFloat](x: HSlice[T, T]): T = ## thread-safe. ## ## See also: - ## * `rand proc<#rand,Rand,HSlice[T,T]>`_ that accepts a slice and uses - ## a provided state + ## * `rand proc<#rand,Rand,HSlice[T: Ordinal or float or float32 or float64,T: Ordinal or float or float32 or float64]>`_ + ## that accepts a slice and uses a provided state ## * `rand proc<#rand,int>`_ that returns an integer ## * `rand proc<#rand,float>`_ that returns a floating point number ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type @@ -357,7 +362,8 @@ proc rand*[T: SomeInteger](t: typedesc[T]): T = ## See also: ## * `rand proc<#rand,int>`_ that returns an integer ## * `rand proc<#rand,float>`_ that returns a floating point number - ## * `rand proc<#rand,HSlice[T,T]>`_ that accepts a slice + ## * `rand proc<#rand,HSlice[T: Ordinal or float or float32 or float64,T: Ordinal or float or float32 or float64]>`_ + ## that accepts a slice runnableExamples: randomize(567) doAssert rand(int8) == 55 diff --git a/lib/pure/smtp.nim b/lib/pure/smtp.nim index 5eafc6680..c0b6665b2 100644 --- a/lib/pure/smtp.nim +++ b/lib/pure/smtp.nim @@ -99,7 +99,7 @@ proc debugRecv*(smtp: Smtp | AsyncSmtp): Future[TaintedString] {.multisync.} = ## this is if you are implementing any ## `SMTP extensions`_. ## - ## See `checkReply(reply)<#checkReply,,string>`_. + ## See `checkReply(reply)<#checkReply,AsyncSmtp,string>`_. result = await smtp.sock.recvLine() if smtp.debug: @@ -211,7 +211,7 @@ proc quitExcpt(smtp: AsyncSmtp, msg: string): Future[void] = return retFuture proc checkReply*(smtp: Smtp | AsyncSmtp, reply: string) {.multisync.} = - ## Calls `debugRecv<#debugRecv>`_ and checks that the received + ## Calls `debugRecv<#debugRecv,AsyncSmtp>`_ and checks that the received ## data starts with ``reply``. If the received data does not start ## with ``reply``, then a ``QUIT`` command will be sent to the SMTP ## server and a ``ReplyError`` exception will be raised. diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim index a28e98caf..8939186a7 100644 --- a/lib/pure/unicode.nim +++ b/lib/pure/unicode.nim @@ -813,7 +813,7 @@ proc toRunes*(s: string): seq[Rune] = ## Obtains a sequence containing the Runes in ``s``. ## ## See also: - ## * `$ proc <#$,seq[T][Rune]>`_ for a reverse operation + ## * `$ proc <#$,Rune>`_ for a reverse operation runnableExamples: let a = toRunes("aáä") doAssert a == @["a".runeAt(0), "á".runeAt(0), "ä".runeAt(0)] diff --git a/lib/std/jsonutils.nim b/lib/std/jsonutils.nim index 50dcd9675..24935f511 100644 --- a/lib/std/jsonutils.nim +++ b/lib/std/jsonutils.nim @@ -278,7 +278,7 @@ proc fromJsonHook*[K, V](t: var (Table[K, V] | OrderedTable[K, V]), ## Enables `fromJson` for `Table` and `OrderedTable` types. ## ## See also: - ## * `toJsonHook proc<#toJsonHook,(Table[K,V]|OrderedTable[K,V])>`_ + ## * `toJsonHook proc<#toJsonHook>`_ runnableExamples: import tables, json var foo: tuple[t: Table[string, int], ot: OrderedTable[string, int]] @@ -298,7 +298,7 @@ proc toJsonHook*[K, V](t: (Table[K, V] | OrderedTable[K, V])): JsonNode = ## Enables `toJson` for `Table` and `OrderedTable` types. ## ## See also: - ## * `fromJsonHook proc<#fromJsonHook,(Table[K,V]|OrderedTable[K,V]),JsonNode>`_ + ## * `fromJsonHook proc<#fromJsonHook,,JsonNode>`_ runnableExamples: import tables, json let foo = ( @@ -383,7 +383,7 @@ proc fromJsonHook*(a: var StringTableRef, b: JsonNode) = ## Enables `fromJson` for `StringTableRef` type. ## ## See also: - ## * `toJsonHook` proc<#toJsonHook,StringTableRef>`_ + ## * `toJsonHook proc<#toJsonHook,StringTableRef>`_ runnableExamples: import strtabs, json var t = newStringTable(modeCaseSensitive) @@ -401,7 +401,7 @@ proc toJsonHook*(a: StringTableRef): JsonNode = ## Enables `toJson` for `StringTableRef` type. ## ## See also: - ## * `fromJsonHook` proc<#fromJsonHook,StringTableRef,JsonNode>`_ + ## * `fromJsonHook proc<#fromJsonHook,StringTableRef,JsonNode>`_ runnableExamples: import strtabs, json let t = newStringTable("name", "John", "surname", "Doe", modeCaseSensitive) diff --git a/lib/std/packedsets.nim b/lib/std/packedsets.nim index be0555d3a..a1402a7ff 100644 --- a/lib/std/packedsets.nim +++ b/lib/std/packedsets.nim @@ -165,7 +165,7 @@ proc initPackedSet*[A]: PackedSet[A] = ## A must be Ordinal ## ## See also: - ## * `toPackedSet[A] proc <#toPackedSet[A],openArray[int]>`_ + ## * `toPackedSet[A] proc <#toPackedSet,openArray[A]>`_ runnableExamples: var a = initPackedSet[int]() assert len(a) == 0 @@ -273,7 +273,7 @@ proc toPackedSet*[A](x: openArray[A]): PackedSet[A] {.since: (1, 3).} = ## Duplicates are removed. ## ## See also: - ## * `initPackedSet[A] proc <#initPackedSet[A]>`_ + ## * `initPackedSet[A] proc <#initPackedSet>`_ runnableExamples: var a = toPackedSet([5, 6, 7]) @@ -418,7 +418,7 @@ proc isNil*[A](x: PackedSet[A]): bool {.inline.} = x.head.isNil and x.elems == 0 proc assign*[A](dest: var PackedSet[A], src: PackedSet[A]) = ## Copies `src` to `dest`. - ## `dest` does not need to be initialized by `initPackedSet[A] proc <#initPackedSet[A]>`_. + ## `dest` does not need to be initialized by `initPackedSet[A] proc <#initPackedSet>`_. runnableExamples: var a = initPackedSet[int]() diff --git a/lib/system.nim b/lib/system.nim index 76bcd4eec..7eb161666 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -96,7 +96,7 @@ proc defined*(x: untyped): bool {.magic: "Defined", noSideEffect, compileTime.} ## defined. ## ## `x` is an external symbol introduced through the compiler's - ## `-d:x switch `_ to enable + ## `-d:x switch `_ to enable ## build time conditionals: ## ## .. code-block:: Nim @@ -1152,7 +1152,7 @@ when defined(nimdoc): ## Stops the program immediately with an exit code. ## ## Before stopping the program the "exit procedures" are called in the - ## opposite order they were added with `addExitProc `_. + ## opposite order they were added with `addExitProc `_. ## ``quit`` never returns and ignores any exception that may have been raised ## by the quit procedures. It does *not* call the garbage collector to free ## all the memory, unless a quit procedure calls `GC_fullCollect @@ -1225,7 +1225,7 @@ when defined(gcDestructors): ## respected. ## ## See also: - ## * `& proc <#&,seq[T][T],seq[T][T]>`_ + ## * `& proc <#&,seq[T],seq[T]>`_ ## ## .. code-block:: Nim ## var s: seq[string] = @["test2","test2"] @@ -1250,7 +1250,7 @@ else: ## respected. ## ## See also: - ## * `& proc <#&,seq[T][T],seq[T][T]>`_ + ## * `& proc <#&,seq[T],seq[T]>`_ ## ## .. code-block:: Nim ## var s: seq[string] = @["test2","test2"] @@ -1274,7 +1274,7 @@ proc del*[T](x: var seq[T], i: Natural) {.noSideEffect.} = ## This is an `O(1)` operation. ## ## See also: - ## * `delete <#delete,seq[T][T],Natural>`_ for preserving the order + ## * `delete <#delete,seq[T],Natural>`_ for preserving the order ## ## .. code-block:: Nim ## var i = @[1, 2, 3, 4, 5] @@ -1289,7 +1289,7 @@ proc delete*[T](x: var seq[T], i: Natural) {.noSideEffect.} = ## This is an `O(n)` operation. ## ## See also: - ## * `del <#delete,seq[T][T],Natural>`_ for O(1) operation + ## * `del <#delete,seq[T],Natural>`_ for O(1) operation ## ## .. code-block:: Nim ## var i = @[1, 2, 3, 4, 5] @@ -1575,7 +1575,7 @@ proc isNil*(x: string): bool {.noSideEffect, magic: "IsNil", nilError.} ## Requires `--nilseqs:on`. ## ## See also: - ## * `isNil(seq[T]) <#isNil,seq[T][T]>`_ + ## * `isNil(seq[T]) <#isNil,seq[T]>`_ proc isNil*[T](x: ptr T): bool {.noSideEffect, magic: "IsNil".} proc isNil*(x: pointer): bool {.noSideEffect, magic: "IsNil".} @@ -1602,7 +1602,7 @@ when defined(nimSeqsV2): ## Requires copying of the sequences. ## ## See also: - ## * `add(var seq[T], openArray[T]) <#add,seq[T][T],openArray[T]>`_ + ## * `add(var seq[T], openArray[T]) <#add,seq[T],openArray[T]>`_ ## ## .. code-block:: Nim ## assert(@[1, 2, 3, 4] & @[5, 6] == @[1, 2, 3, 4, 5, 6]) @@ -1618,7 +1618,7 @@ when defined(nimSeqsV2): ## Requires copying of the sequence. ## ## See also: - ## * `add(var seq[T], T) <#add,seq[T][T],T>`_ + ## * `add(var seq[T], T) <#add,seq[T],sinkT>`_ ## ## .. code-block:: Nim ## assert(@[1, 2, 3] & 4 == @[1, 2, 3, 4]) @@ -1647,7 +1647,7 @@ else: ## Requires copying of the sequences. ## ## See also: - ## * `add(var seq[T], openArray[T]) <#add,seq[T][T],openArray[T]>`_ + ## * `add(var seq[T], openArray[T]) <#add,seq[T],openArray[T]>`_ ## ## .. code-block:: Nim ## assert(@[1, 2, 3, 4] & @[5, 6] == @[1, 2, 3, 4, 5, 6]) @@ -1663,7 +1663,7 @@ else: ## Requires copying of the sequence. ## ## See also: - ## * `add(var seq[T], T) <#add,seq[T][T],T>`_ + ## * `add(var seq[T], T) <#add,seq[T],sinkT>`_ ## ## .. code-block:: Nim ## assert(@[1, 2, 3] & 4 == @[1, 2, 3, 4]) @@ -2950,7 +2950,7 @@ template closureScope*(body: untyped): untyped = ## their current iteration values. ## ## Note: This template may not work in some cases, use - ## `capture `_ instead. + ## `capture `_ instead. ## ## Example: ## diff --git a/lib/system/assertions.nim b/lib/system/assertions.nim index 1a4fda123..4c25d2a56 100644 --- a/lib/system/assertions.nim +++ b/lib/system/assertions.nim @@ -48,7 +48,7 @@ template assert*(cond: untyped, msg = "") = ## ## The compiler may not generate any code at all for ``assert`` if it is ## advised to do so through the ``-d:danger`` or ``--assertions:off`` - ## `command line switches `_. + ## `command line switches `_. ## ## .. code-block:: nim ## static: assert 1 == 9, "This assertion generates code when not built with -d:danger or --assertions:off" diff --git a/lib/system/memalloc.nim b/lib/system/memalloc.nim index 5f4304502..f5e8b2363 100644 --- a/lib/system/memalloc.nim +++ b/lib/system/memalloc.nim @@ -77,16 +77,16 @@ when hasAlloc and not defined(js): template alloc*(size: Natural): pointer = ## Allocates a new memory block with at least ``size`` bytes. ## - ## The block has to be freed with `realloc(block, 0) <#realloc,pointer,Natural>`_ + ## The block has to be freed with `realloc(block, 0) <#realloc.t,pointer,Natural>`_ ## or `dealloc(block) <#dealloc,pointer>`_. ## The block is not initialized, so reading ## from it before writing to it is undefined behaviour! ## ## The allocated memory belongs to its allocating thread! - ## Use `allocShared <#allocShared,Natural>`_ to allocate from a shared heap. + ## Use `allocShared <#allocShared.t,Natural>`_ to allocate from a shared heap. ## ## See also: - ## * `alloc0 <#alloc0,Natural>`_ + ## * `alloc0 <#alloc0.t,Natural>`_ incStat(allocCount) allocImpl(size) @@ -108,13 +108,13 @@ when hasAlloc and not defined(js): template alloc0*(size: Natural): pointer = ## Allocates a new memory block with at least ``size`` bytes. ## - ## The block has to be freed with `realloc(block, 0) <#realloc,pointer,Natural>`_ + ## The block has to be freed with `realloc(block, 0) <#realloc.t,pointer,Natural>`_ ## or `dealloc(block) <#dealloc,pointer>`_. ## The block is initialized with all bytes containing zero, so it is - ## somewhat safer than `alloc <#alloc,Natural>`_. + ## somewhat safer than `alloc <#alloc.t,Natural>`_. ## ## The allocated memory belongs to its allocating thread! - ## Use `allocShared0 <#allocShared0,Natural>`_ to allocate from a shared heap. + ## Use `allocShared0 <#allocShared0.t,Natural>`_ to allocate from a shared heap. incStat(allocCount) alloc0Impl(size) @@ -140,7 +140,7 @@ when hasAlloc and not defined(js): ## `dealloc(block) <#dealloc,pointer>`_. ## ## The allocated memory belongs to its allocating thread! - ## Use `reallocShared <#reallocShared,pointer,Natural>`_ to reallocate + ## Use `reallocShared <#reallocShared.t,pointer,Natural>`_ to reallocate ## from a shared heap. reallocImpl(p, newSize) @@ -157,7 +157,7 @@ when hasAlloc and not defined(js): ## somewhat safer then realloc ## ## The allocated memory belongs to its allocating thread! - ## Use `reallocShared <#reallocShared,pointer,Natural>`_ to reallocate + ## Use `reallocShared <#reallocShared.t,pointer,Natural>`_ to reallocate ## from a shared heap. realloc0Impl(p, oldSize, newSize) @@ -193,14 +193,14 @@ when hasAlloc and not defined(js): ## least ``size`` bytes. ## ## The block has to be freed with - ## `reallocShared(block, 0) <#reallocShared,pointer,Natural>`_ + ## `reallocShared(block, 0) <#reallocShared.t,pointer,Natural>`_ ## or `deallocShared(block) <#deallocShared,pointer>`_. ## ## The block is not initialized, so reading from it before writing ## to it is undefined behaviour! ## ## See also: - ## `allocShared0 <#allocShared0,Natural>`_. + ## `allocShared0 <#allocShared0.t,Natural>`_. incStat(allocCount) allocSharedImpl(size) @@ -225,12 +225,12 @@ when hasAlloc and not defined(js): ## least ``size`` bytes. ## ## The block has to be freed with - ## `reallocShared(block, 0) <#reallocShared,pointer,Natural>`_ + ## `reallocShared(block, 0) <#reallocShared.t,pointer,Natural>`_ ## or `deallocShared(block) <#deallocShared,pointer>`_. ## ## The block is initialized with all bytes ## containing zero, so it is somewhat safer than - ## `allocShared <#allocShared,Natural>`_. + ## `allocShared <#allocShared.t,Natural>`_. incStat(allocCount) allocShared0Impl(size) diff --git a/lib/system_overview.rst b/lib/system_overview.rst index 9125c29ba..2e437e596 100644 --- a/lib/system_overview.rst +++ b/lib/system_overview.rst @@ -53,9 +53,9 @@ Proc Usage and a given capacity `setLen<#setLen,seq[T],Natural>`_ Set the length of a sequence `len<#len,seq[T]>`_ Return the length of a sequence -`@<#@>`_ Turn an array into a sequence -`add<#add,seq[T],>`_ Add an item to the sequence -`insert<#insert,seq[T],>`_ Insert an item at a specific position +`@<#@,openArray[T]>`_ Turn an array into a sequence +`add<#add,seq[T],sinkT>`_ Add an item to the sequence +`insert<#insert,seq[T],sinkT>`_ Insert an item at a specific position `delete<#delete,seq[T],Natural>`_ Delete an item while preserving the order of elements (`O(n)` operation) `del<#del,seq[T],Natural>`_ `O(1)` removal, doesn't preserve the order @@ -152,21 +152,21 @@ Proc Usage Misc ---- -============================================= ============================================ -Proc Usage -============================================= ============================================ -`is<#is,T,S>`_ Check if two arguments are of the same type -`isnot<#isnot.t,untyped,untyped>`_ Negated version of `is` -`!=<#!%3D.t,untyped,untyped>`_ Not equals -`addr<#addr,T>`_ Take the address of a memory location -`T and F<#and,bool,bool>`_ Boolean `and` -`T or F<#or,bool,bool>`_ Boolean `or` -`T xor F<#xor,bool,bool>`_ Boolean `xor` (exclusive or) -`not T<#not,bool>`_ Boolean `not` -`a[^x]<#^.t,int>`_ Take the element at the reversed index `x` -`a .. b<#..,T,U>`_ Binary slice that constructs an interval - `[a, b]` -`a ..^ b<#..^.t,untyped,untyped>`_ Interval `[a, b]` but `b` as reversed index -[a ..< b](#..<.t,untyped,untyped) Interval `[a, b)` (excluded upper bound) -[runnableExamples](#runnableExamples,untyped) Create testable documentation -============================================= ============================================ +==================================================== ============================================ +Proc Usage +==================================================== ============================================ +`is<#is,T,S>`_ Check if two arguments are of the same type +`isnot<#isnot.t,untyped,untyped>`_ Negated version of `is` +`!=<#!%3D.t,untyped,untyped>`_ Not equals +`addr<#addr,T>`_ Take the address of a memory location +`T and F<#and,bool,bool>`_ Boolean `and` +`T or F<#or,bool,bool>`_ Boolean `or` +`T xor F<#xor,bool,bool>`_ Boolean `xor` (exclusive or) +`not T<#not,bool>`_ Boolean `not` +`a[^x]<#^.t,int>`_ Take the element at the reversed index `x` +`a .. b<#..,sinkT,sinkU>`_ Binary slice that constructs an interval + `[a, b]` +`a ..^ b<#..^.t,untyped,untyped>`_ Interval `[a, b]` but `b` as reversed index +[a ..< b](#..<.t,untyped,untyped) Interval `[a, b)` (excluded upper bound) +[runnableExamples](#runnableExamples,string,untyped) Create testable documentation +==================================================== ============================================ From 233c6a2fba9e6b93f8c19a242265beabf34b215c Mon Sep 17 00:00:00 2001 From: lqdev Date: Mon, 14 Dec 2020 20:13:43 +0100 Subject: [PATCH 1174/3768] disabled sink openArray[T] for adding to seqs (#16352) [backport:1.4] --- lib/system.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/system.nim b/lib/system.nim index 7eb161666..57b46a937 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1215,7 +1215,7 @@ when defined(nimscript) or not defined(nimSeqsV2): ## Generic code becomes much easier to write if the Nim naming scheme is ## respected. -when defined(gcDestructors): +when false: # defined(gcDestructors): proc add*[T](x: var seq[T], y: sink openArray[T]) {.noSideEffect.} = ## Generic proc for adding a container `y` to a container `x`. ## From 38eb021f81589c11453bf5b175f4395d6b25ef86 Mon Sep 17 00:00:00 2001 From: ee7 <45465154+ee7@users.noreply.github.com> Date: Mon, 14 Dec 2020 20:14:49 +0100 Subject: [PATCH 1175/3768] sequtils.nim: Change some `func` back to `proc` (#16309) This commit changes the funcs that take a `proc` parameter back to procs. This reverts some of commit 6f57ebae349f: sequtils.nim: Use `func` (#16293) See also: - https://github.com/nim-lang/Nim/issues/16303 - https://github.com/nim-lang/Nim/pull/16304 --- lib/pure/collections/sequtils.nim | 56 +++++++++++++++---------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index 449faadc7..7aa885794 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -359,7 +359,7 @@ func distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] = result[i].add(s[g]) first = last -func map*[T, S](s: openArray[T], op: proc (x: T): S {.closure.}): +proc map*[T, S](s: openArray[T], op: proc (x: T): S {.closure.}): seq[S]{.inline.} = ## Returns a new sequence with the results of `op` proc applied to every ## item in the container `s`. @@ -373,7 +373,7 @@ func map*[T, S](s: openArray[T], op: proc (x: T): S {.closure.}): ## See also: ## * `sugar.collect macro`_ ## * `mapIt template<#mapIt.t,typed,untyped>`_ - ## * `apply func<#apply,openArray[T],proc(T)_2>`_ for the in-place version + ## * `apply proc<#apply,openArray[T],proc(T)_2>`_ for the in-place version ## runnableExamples: let @@ -385,7 +385,7 @@ func map*[T, S](s: openArray[T], op: proc (x: T): S {.closure.}): for i in 0 ..< s.len: result[i] = op(s[i]) -func apply*[T](s: var openArray[T], op: proc (x: var T) {.closure.}) +proc apply*[T](s: var openArray[T], op: proc (x: var T) {.closure.}) {.inline.} = ## Applies `op` to every item in `s` modifying it directly. ## @@ -396,7 +396,7 @@ func apply*[T](s: var openArray[T], op: proc (x: var T) {.closure.}) ## ## See also: ## * `applyIt template<#applyIt.t,untyped,untyped>`_ - ## * `map func<#map,openArray[T],proc(T)>`_ + ## * `map proc<#map,openArray[T],proc(T)>`_ ## runnableExamples: var a = @["1", "2", "3", "4"] @@ -405,7 +405,7 @@ func apply*[T](s: var openArray[T], op: proc (x: var T) {.closure.}) for i in 0 ..< s.len: op(s[i]) -func apply*[T](s: var openArray[T], op: proc (x: T): T {.closure.}) +proc apply*[T](s: var openArray[T], op: proc (x: T): T {.closure.}) {.inline.} = ## Applies `op` to every item in `s` modifying it directly. ## @@ -416,7 +416,7 @@ func apply*[T](s: var openArray[T], op: proc (x: T): T {.closure.}) ## ## See also: ## * `applyIt template<#applyIt.t,untyped,untyped>`_ - ## * `map func<#map,openArray[T],proc(T)>`_ + ## * `map proc<#map,openArray[T],proc(T)>`_ ## runnableExamples: var a = @["1", "2", "3", "4"] @@ -425,7 +425,7 @@ func apply*[T](s: var openArray[T], op: proc (x: T): T {.closure.}) for i in 0 ..< s.len: s[i] = op(s[i]) -func apply*[T](s: openArray[T], op: proc (x: T) {.closure.}) {.inline, since: (1, 3).} = +proc apply*[T](s: openArray[T], op: proc (x: T) {.closure.}) {.inline, since: (1, 3).} = ## Same as `apply` but for proc that do not return and do not mutate `s` directly. runnableExamples: var message: string @@ -442,7 +442,7 @@ iterator filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): T = ## ## See also: ## * `sugar.collect macro`_ - ## * `fliter func<#filter,openArray[T],proc(T)>`_ + ## * `fliter proc<#filter,openArray[T],proc(T)>`_ ## * `filterIt template<#filterIt.t,untyped,untyped>`_ ## runnableExamples: @@ -456,7 +456,7 @@ iterator filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): T = if pred(s[i]): yield s[i] -func filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): seq[T] +proc filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): seq[T] {.inline.} = ## Returns a new sequence with all the items of `s` that fulfilled the ## predicate `pred` (function that returns a `bool`). @@ -468,7 +468,7 @@ func filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): seq[T] ## * `sugar.collect macro`_ ## * `filterIt template<#filterIt.t,untyped,untyped>`_ ## * `filter iterator<#filter.i,openArray[T],proc(T)>`_ - ## * `keepIf func<#keepIf,seq[T],proc(T)>`_ for the in-place version + ## * `keepIf proc<#keepIf,seq[T],proc(T)>`_ for the in-place version ## runnableExamples: let @@ -483,19 +483,19 @@ func filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): seq[T] if pred(s[i]): result.add(s[i]) -func keepIf*[T](s: var seq[T], pred: proc(x: T): bool {.closure.}) +proc keepIf*[T](s: var seq[T], pred: proc(x: T): bool {.closure.}) {.inline.} = ## Keeps the items in the passed sequence `s` if they fulfilled the ## predicate `pred` (function that returns a `bool`). ## ## Note that `s` must be declared as a ``var``. ## - ## Similar to the `filter func<#filter,openArray[T],proc(T)>`_, + ## Similar to the `filter proc<#filter,openArray[T],proc(T)>`_, ## but modifies the sequence directly. ## ## See also: ## * `keepItIf template<#keepItIf.t,seq,untyped>`_ - ## * `filter func<#filter,openArray[T],proc(T)>`_ + ## * `filter proc<#filter,openArray[T],proc(T)>`_ ## runnableExamples: var floats = @[13.0, 12.5, 5.8, 2.0, 6.1, 9.9, 10.1] @@ -576,7 +576,7 @@ template filterIt*(s, pred: untyped): untyped = ## Returns a new sequence with all the items of `s` that fulfilled the ## predicate `pred`. ## - ## Unlike the `filter func<#filter,openArray[T],proc(T)>`_ and + ## Unlike the `filter proc<#filter,openArray[T],proc(T)>`_ and ## `filter iterator<#filter.i,openArray[T],proc(T)>`_, ## the predicate needs to be an expression using the `it` variable ## for testing, like: `filterIt("abcxyz", it == 'x')`. @@ -586,7 +586,7 @@ template filterIt*(s, pred: untyped): untyped = ## ## See also: ## * `sugar.collect macro`_ - ## * `fliter func<#filter,openArray[T],proc(T)>`_ + ## * `fliter proc<#filter,openArray[T],proc(T)>`_ ## * `filter iterator<#filter.i,openArray[T],proc(T)>`_ ## runnableExamples: @@ -606,12 +606,12 @@ template keepItIf*(varSeq: seq, pred: untyped) = ## Keeps the items in the passed sequence (must be declared as a `var`) ## if they fulfilled the predicate. ## - ## Unlike the `keepIf func<#keepIf,seq[T],proc(T)>`_, + ## Unlike the `keepIf proc<#keepIf,seq[T],proc(T)>`_, ## the predicate needs to be an expression using ## the `it` variable for testing, like: `keepItIf("abcxyz", it == 'x')`. ## ## See also: - ## * `keepIf func<#keepIf,seq[T],proc(T)>`_ + ## * `keepIf proc<#keepIf,seq[T],proc(T)>`_ ## * `filterIt template<#filterIt.t,untyped,untyped>`_ ## runnableExamples: @@ -650,13 +650,13 @@ since (1, 1): if pred: result += 1 result -func all*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): bool = +proc all*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): bool = ## Iterates through a container and checks if every item fulfills the ## predicate. ## ## See also: ## * `allIt template<#allIt.t,untyped,untyped>`_ - ## * `any func<#any,openArray[T],proc(T)>`_ + ## * `any proc<#any,openArray[T],proc(T)>`_ ## runnableExamples: let numbers = @[1, 4, 5, 8, 9, 7, 4] @@ -672,12 +672,12 @@ template allIt*(s, pred: untyped): bool = ## Iterates through a container and checks if every item fulfills the ## predicate. ## - ## Unlike the `all func<#all,openArray[T],proc(T)>`_, + ## Unlike the `all proc<#all,openArray[T],proc(T)>`_, ## the predicate needs to be an expression using ## the `it` variable for testing, like: `allIt("abba", it == 'a')`. ## ## See also: - ## * `all func<#all,openArray[T],proc(T)>`_ + ## * `all proc<#all,openArray[T],proc(T)>`_ ## * `anyIt template<#anyIt.t,untyped,untyped>`_ ## runnableExamples: @@ -692,13 +692,13 @@ template allIt*(s, pred: untyped): bool = break result -func any*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): bool = +proc any*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): bool = ## Iterates through a container and checks if some item fulfills the ## predicate. ## ## See also: ## * `anyIt template<#anyIt.t,untyped,untyped>`_ - ## * `all func<#all,openArray[T],proc(T)>`_ + ## * `all proc<#all,openArray[T],proc(T)>`_ ## runnableExamples: let numbers = @[1, 4, 5, 8, 9, 7, 4] @@ -714,12 +714,12 @@ template anyIt*(s, pred: untyped): bool = ## Iterates through a container and checks if some item fulfills the ## predicate. ## - ## Unlike the `any func<#any,openArray[T],proc(T)>`_, + ## Unlike the `any proc<#any,openArray[T],proc(T)>`_, ## the predicate needs to be an expression using ## the `it` variable for testing, like: `anyIt("abba", it == 'a')`. ## ## See also: - ## * `any func<#any,openArray[T],proc(T)>`_ + ## * `any proc<#any,openArray[T],proc(T)>`_ ## * `allIt template<#allIt.t,untyped,untyped>`_ ## runnableExamples: @@ -946,7 +946,7 @@ template mapIt*(s: typed, op: untyped): untyped = ## ## See also: ## * `sugar.collect macro`_ - ## * `map func<#map,openArray[T],proc(T)>`_ + ## * `map proc<#map,openArray[T],proc(T)>`_ ## * `applyIt template<#applyIt.t,untyped,untyped>`_ for the in-place version ## runnableExamples: @@ -1007,14 +1007,14 @@ template mapIt*(s: typed, op: untyped): untyped = map(s, f) template applyIt*(varSeq, op: untyped) = - ## Convenience template around the mutable `apply` func to reduce typing. + ## Convenience template around the mutable `apply` proc to reduce typing. ## ## The template injects the `it` variable which you can use directly in an ## expression. The expression has to return the same type as the sequence you ## are mutating. ## ## See also: - ## * `apply func<#apply,openArray[T],proc(T)_2>`_ + ## * `apply proc<#apply,openArray[T],proc(T)_2>`_ ## * `mapIt template<#mapIt.t,typed,untyped>`_ ## runnableExamples: From 1082d62b089e54a584e15b0f97a4b247e214d9ef Mon Sep 17 00:00:00 2001 From: Jason Beetham Date: Mon, 14 Dec 2020 16:20:21 -0700 Subject: [PATCH 1176/3768] Adds toSet to create sets from iterables (#16276) --- changelog.md | 2 ++ lib/std/setutils.nim | 32 ++++++++++++++++++++++++++++++++ tests/stdlib/tsetutils.nim | 14 ++++++++++++++ 3 files changed, 48 insertions(+) create mode 100644 lib/std/setutils.nim create mode 100644 tests/stdlib/tsetutils.nim diff --git a/changelog.md b/changelog.md index 688b87da8..eec754a89 100644 --- a/changelog.md +++ b/changelog.md @@ -58,6 +58,8 @@ - `strscans.scanf` now supports parsing single characters. - `strscans.scanTuple` added which uses `strscans.scanf` internally, returning a tuple which can be unpacked for easier usage of `scanf`. +- Added `setutils.toSet` that can take any iterable and convert it to a built-in set, + if the iterable yields a built-in settable type. - Added `math.isNaN`. diff --git a/lib/std/setutils.nim b/lib/std/setutils.nim new file mode 100644 index 000000000..c6385180e --- /dev/null +++ b/lib/std/setutils.nim @@ -0,0 +1,32 @@ +# +# +# The Nim Compiler +# (c) Copyright 2020 Nim Contributors +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module adds functionality to the built-in `set` type. +## See also std/packedsets, std/sets + +import typetraits + +#[ + type SetElement* = char|byte|bool|int16|uint16|enum|uint8|int8 + ## The allowed types of a built-in set. +]# + +template toSet*(iter: untyped): untyped = + ## Return a built-in set from the elements of iterable `iter` + runnableExamples: + assert "helloWorld".toSet == {'W', 'd', 'e', 'h', 'l', 'o', 'r'} + assert toSet([10u16, 20, 30]) == {10u16, 20, 30} + assert [30u8, 100, 10].toSet == {10u8, 30, 100} + assert toSet(@[1321i16, 321, 90]) == {90i16, 321, 1321} + assert toSet([false]) == {false} + assert toSet(0u8..10) == {0u8..10} + var result: set[elementType(iter)] + for x in iter: + incl(result, x) + result diff --git a/tests/stdlib/tsetutils.nim b/tests/stdlib/tsetutils.nim new file mode 100644 index 000000000..d99807f92 --- /dev/null +++ b/tests/stdlib/tsetutils.nim @@ -0,0 +1,14 @@ +discard """ + targets: "c js" +""" +import std/setutils + +template main = + doAssert "abcbb".toSet == {'a', 'b', 'c'} + doAssert toSet([10u8, 12, 13]) == {10u8, 12, 13} + doAssert toSet(0u16..30) == {0u16..30} + type A = distinct char + doAssert [A('x')].toSet == {A('x')} + +main() +static: main() \ No newline at end of file From 9ede9566620b4d301f3209e76be0a0c5dba5d1fc Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 15 Dec 2020 05:55:41 -0800 Subject: [PATCH 1177/3768] improve tstrutils: test c, cpp, js, vm; cleanups (#16357) * renamed: tests/stdlib/tstrutil.nim -> tests/stdlib/tstrutils.nim * improve test * enable tstrutils for js, vm --- tests/stdlib/tstrutil.nim | 676 ------------------------------------ tests/stdlib/tstrutils.nim | 689 +++++++++++++++++++++++++++++++++++++ 2 files changed, 689 insertions(+), 676 deletions(-) delete mode 100644 tests/stdlib/tstrutil.nim create mode 100644 tests/stdlib/tstrutils.nim diff --git a/tests/stdlib/tstrutil.nim b/tests/stdlib/tstrutil.nim deleted file mode 100644 index 149d9bb87..000000000 --- a/tests/stdlib/tstrutil.nim +++ /dev/null @@ -1,676 +0,0 @@ -# test the new strutils module - -import - strutils - -template rejectParse(e) = - try: - discard e - raise newException(AssertionDefect, "This was supposed to fail: $#!" % astToStr(e)) - except ValueError: discard - -proc testStrip() = - doAssert strip(" ha ") == "ha" - -proc testRemoveSuffix = - var s = "hello\n\r" - s.removeSuffix - assert s == "hello" - s.removeSuffix - assert s == "hello" - - s = "hello\n\n" - s.removeSuffix - assert s == "hello" - - s = "hello\r" - s.removeSuffix - assert s == "hello" - - s = "hello \n there" - s.removeSuffix - assert s == "hello \n there" - - s = "hello" - s.removeSuffix("llo") - assert s == "he" - s.removeSuffix('e') - assert s == "h" - - s = "hellos" - s.removeSuffix({'s','z'}) - assert s == "hello" - s.removeSuffix({'l','o'}) - assert s == "he" - - s = "aeiou" - s.removeSuffix("") - assert s == "aeiou" - - s = "" - s.removeSuffix("") - assert s == "" - - s = " " - s.removeSuffix - assert s == " " - - s = " " - s.removeSuffix("") - assert s == " " - - s = " " - s.removeSuffix(" ") - assert s == " " - - s = " " - s.removeSuffix(' ') - assert s == "" - - # Contrary to Chomp in other languages - # empty string does not change behaviour - s = "hello\r\n\r\n" - s.removeSuffix("") - assert s == "hello\r\n\r\n" - -proc testRemovePrefix = - var s = "\n\rhello" - s.removePrefix - assert s == "hello" - s.removePrefix - assert s == "hello" - - s = "\n\nhello" - s.removePrefix - assert s == "hello" - - s = "\rhello" - s.removePrefix - assert s == "hello" - - s = "hello \n there" - s.removePrefix - assert s == "hello \n there" - - s = "hello" - s.removePrefix("hel") - assert s == "lo" - s.removePrefix('l') - assert s == "o" - - s = "hellos" - s.removePrefix({'h','e'}) - assert s == "llos" - s.removePrefix({'l','o'}) - assert s == "s" - - s = "aeiou" - s.removePrefix("") - assert s == "aeiou" - - s = "" - s.removePrefix("") - assert s == "" - - s = " " - s.removePrefix - assert s == " " - - s = " " - s.removePrefix("") - assert s == " " - - s = " " - s.removePrefix(" ") - assert s == " " - - s = " " - s.removePrefix(' ') - assert s == "" - - # Contrary to Chomp in other languages - # empty string does not change behaviour - s = "\r\n\r\nhello" - s.removePrefix("") - assert s == "\r\n\r\nhello" - -proc main() = - testStrip() - testRemoveSuffix() - testRemovePrefix() - var ret: seq[string] # or use `toSeq` - for p in split("/home/a1:xyz:/usr/bin", {':'}): ret.add p - doAssert ret == @["/home/a1", "xyz", "/usr/bin"] - -proc testDelete = - var s = "0123456789ABCDEFGH" - delete(s, 4, 5) - assert s == "01236789ABCDEFGH" - delete(s, s.len-1, s.len-1) - assert s == "01236789ABCDEFG" - delete(s, 0, 0) - assert s == "1236789ABCDEFG" - -proc testFind = - assert "0123456789ABCDEFGH".find('A') == 10 - assert "0123456789ABCDEFGH".find('A', 5) == 10 - assert "0123456789ABCDEFGH".find('A', 5, 10) == 10 - assert "0123456789ABCDEFGH".find('A', 5, 9) == -1 - assert "0123456789ABCDEFGH".find("A") == 10 - assert "0123456789ABCDEFGH".find("A", 5) == 10 - assert "0123456789ABCDEFGH".find("A", 5, 10) == 10 - assert "0123456789ABCDEFGH".find("A", 5, 9) == -1 - assert "0123456789ABCDEFGH".find({'A'..'C'}) == 10 - assert "0123456789ABCDEFGH".find({'A'..'C'}, 5) == 10 - assert "0123456789ABCDEFGH".find({'A'..'C'}, 5, 10) == 10 - assert "0123456789ABCDEFGH".find({'A'..'C'}, 5, 9) == -1 - -proc testRFind = - assert "0123456789ABCDEFGAH".rfind('A') == 17 - assert "0123456789ABCDEFGAH".rfind('A', last=13) == 10 - assert "0123456789ABCDEFGAH".rfind('H', last=13) == -1 - assert "0123456789ABCDEFGAH".rfind("A") == 17 - assert "0123456789ABCDEFGAH".rfind("A", last=13) == 10 - assert "0123456789ABCDEFGAH".rfind("H", last=13) == -1 - assert "0123456789ABCDEFGAH".rfind({'A'..'C'}) == 17 - assert "0123456789ABCDEFGAH".rfind({'A'..'C'}, last=13) == 12 - assert "0123456789ABCDEFGAH".rfind({'G'..'H'}, last=13) == -1 - assert "0123456789ABCDEFGAH".rfind('A', start=18) == -1 - assert "0123456789ABCDEFGAH".rfind('A', start=11, last=17) == 17 - assert "0123456789ABCDEFGAH".rfind("0", start=0) == 0 - assert "0123456789ABCDEFGAH".rfind("0", start=1) == -1 - assert "0123456789ABCDEFGAH".rfind("H", start=11) == 18 - assert "0123456789ABCDEFGAH".rfind({'0'..'9'}, start=5) == 9 - assert "0123456789ABCDEFGAH".rfind({'0'..'9'}, start=10) == -1 - -proc testTrimZeros() = - var x = "1200" - x.trimZeros() - assert x == "1200" - x = "120.0" - x.trimZeros() - assert x == "120" - x = "0." - x.trimZeros() - assert x == "0" - x = "1.0e2" - x.trimZeros() - assert x == "1e2" - x = "78.90" - x.trimZeros() - assert x == "78.9" - x = "1.23e4" - x.trimZeros() - assert x == "1.23e4" - x = "1.01" - x.trimZeros() - assert x == "1.01" - x = "1.1001" - x.trimZeros() - assert x == "1.1001" - x = "0.0" - x.trimZeros() - assert x == "0" - x = "0.01" - x.trimZeros() - assert x == "0.01" - x = "1e0" - x.trimZeros() - assert x == "1e0" - -proc testSplitLines() = - let fixture = "a\nb\rc\r\nd" - assert len(fixture.splitLines) == 4 - assert splitLines(fixture) == @["a", "b", "c", "d"] - assert splitLines(fixture, keepEol=true) == @["a\n", "b\r", "c\r\n", "d"] - -proc testCountLines = - proc assertCountLines(s: string) = assert s.countLines == s.splitLines.len - assertCountLines("") - assertCountLines("\n") - assertCountLines("\n\n") - assertCountLines("abc") - assertCountLines("abc\n123") - assertCountLines("abc\n123\n") - assertCountLines("\nabc\n123") - assertCountLines("\nabc\n123\n") - -proc testParseInts = - # binary - assert "0b1111".parseBinInt == 15 - assert "0B1111".parseBinInt == 15 - assert "1111".parseBinInt == 15 - assert "1110".parseBinInt == 14 - assert "1_1_1_1".parseBinInt == 15 - assert "0b1_1_1_1".parseBinInt == 15 - rejectParse "".parseBinInt - rejectParse "_".parseBinInt - rejectParse "0b".parseBinInt - rejectParse "0b1234".parseBinInt - # hex - assert "0x72".parseHexInt == 114 - assert "0X72".parseHexInt == 114 - assert "#72".parseHexInt == 114 - assert "72".parseHexInt == 114 - assert "FF".parseHexInt == 255 - assert "ff".parseHexInt == 255 - assert "fF".parseHexInt == 255 - assert "0x7_2".parseHexInt == 114 - rejectParse "".parseHexInt - rejectParse "_".parseHexInt - rejectParse "0x".parseHexInt - rejectParse "0xFFG".parseHexInt - rejectParse "reject".parseHexInt - # octal - assert "0o17".parseOctInt == 15 - assert "0O17".parseOctInt == 15 - assert "17".parseOctInt == 15 - assert "10".parseOctInt == 8 - assert "0o1_0_0".parseOctInt == 64 - rejectParse "".parseOctInt - rejectParse "_".parseOctInt - rejectParse "0o".parseOctInt - rejectParse "9".parseOctInt - rejectParse "0o9".parseOctInt - rejectParse "reject".parseOctInt - -testDelete() -testFind() -testRFind() -testTrimZeros() -testSplitLines() -testCountLines() -testParseInts() - -assert(insertSep($1000_000) == "1_000_000") -assert(insertSep($232) == "232") -assert(insertSep($12345, ',') == "12,345") -assert(insertSep($0) == "0") - -assert "/1/2/3".rfind('/') == 4 -assert "/1/2/3".rfind('/', last=1) == 0 -assert "/1/2/3".rfind('0') == -1 - -assert(toHex(100i16, 32) == "00000000000000000000000000000064") -assert(toHex(-100i16, 32) == "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9C") - -assert(toHex(high(uint64)) == "FFFFFFFFFFFFFFFF") -assert(toHex(high(uint64), 16) == "FFFFFFFFFFFFFFFF") -assert(toHex(high(uint64), 32) == "0000000000000000FFFFFFFFFFFFFFFF") - -assert "".parseHexStr == "" -assert "00Ff80".parseHexStr == "\0\xFF\x80" -try: - discard "00Ff8".parseHexStr - assert false, "Should raise ValueError" -except ValueError: - discard - -try: - discard "0k".parseHexStr - assert false, "Should raise ValueError" -except ValueError: - discard - -assert "".toHex == "" -assert "\x00\xFF\x80".toHex == "00FF80" -assert "0123456789abcdef".parseHexStr.toHex == "0123456789ABCDEF" - -assert(' '.repeat(8) == " ") -assert(" ".repeat(8) == " ") -assert(spaces(8) == " ") - -assert(' '.repeat(0) == "") -assert(" ".repeat(0) == "") -assert(spaces(0) == "") - -# bug #11369 - -var num: int64 = -1 -assert num.toBin(64) == "1111111111111111111111111111111111111111111111111111111111111111" -assert num.toOct(24) == "001777777777777777777777" - - -# bug #8911 -when true: - static: - let a = "" - let a2 = a.replace("\n", "\\n") - -when true: - static: - let b = "b" - let b2 = b.replace("\n", "\\n") - -when true: - let c = "" - let c2 = c.replace("\n", "\\n") - -main() -#OUT ha/home/a1xyz/usr/bin - - -# `parseEnum`, ref issue #14030 -# check enum defined at top level -type - Foo = enum - A = -10 - B = "bb" - C = (-5, "ccc") - D = 15 - E = "ee" # check that we count enum fields correctly - -block: - let a = parseEnum[Foo]("A") - let b = parseEnum[Foo]("bb") - let c = parseEnum[Foo]("ccc") - let d = parseEnum[Foo]("D") - let e = parseEnum[Foo]("ee") - doAssert a == A - doAssert b == B - doAssert c == C - doAssert d == D - doAssert e == E - try: - let f = parseEnum[Foo]("Bar") - doAssert false - except ValueError: - discard - - # finally using default - let g = parseEnum[Foo]("Bar", A) - doAssert g == A - -block: - # check enum defined in block - type - Bar = enum - V - W = "ww" - X = (3, "xx") - Y = 10 - Z = "zz" # check that we count enum fields correctly - - let a = parseEnum[Bar]("V") - let b = parseEnum[Bar]("ww") - let c = parseEnum[Bar]("xx") - let d = parseEnum[Bar]("Y") - let e = parseEnum[Bar]("zz") - doAssert a == V - doAssert b == W - doAssert c == X - doAssert d == Y - doAssert e == Z - try: - let f = parseEnum[Bar]("Baz") - doAssert false - except ValueError: - discard - - # finally using default - let g = parseEnum[Bar]("Baz", V) - doAssert g == V - -block: - # check ambiguous enum fails to parse - type - Ambig = enum - f1 = "A" - f2 = "B" - f3 = "A" - - doAssert not compiles((let a = parseEnum[Ambig]("A"))) - -block: - # check almost ambiguous enum - type - AlmostAmbig = enum - f1 = "someA" - f2 = "someB" - f3 = "SomeA" - - let a = parseEnum[AlmostAmbig]("someA") - let b = parseEnum[AlmostAmbig]("someB") - let c = parseEnum[AlmostAmbig]("SomeA") - doAssert a == f1 - doAssert b == f2 - doAssert c == f3 - -block: - assert 0 == indentation """ -hey - low - there -""" - assert 2 == indentation """ - hey - low - there -""" - assert 2 == indentation """ hey - low - there -""" - assert 2 == indentation """ hey - low - there""" - assert 0 == indentation "" - assert 0 == indentation " \n \n" - assert 0 == indentation " " - - -block: - proc nonStaticTests = - doAssert formatBiggestFloat(1234.567, ffDecimal, -1) == "1234.567000" - when not defined(js): - doAssert formatBiggestFloat(1234.567, ffDecimal, 0) == "1235." # bugs 8242, 12586 - doAssert formatBiggestFloat(1234.567, ffDecimal, 1) == "1234.6" - doAssert formatBiggestFloat(0.00000000001, ffDecimal, 11) == "0.00000000001" - doAssert formatBiggestFloat(0.00000000001, ffScientific, 1, ',') in - ["1,0e-11", "1,0e-011"] - # bug #6589 - when not defined(js): - doAssert formatFloat(123.456, ffScientific, precision = -1) == "1.234560e+02" - - doAssert "$# $3 $# $#" % ["a", "b", "c"] == "a c b c" - doAssert "${1}12 ${-1}$2" % ["a", "b"] == "a12 bb" - - block: # formatSize tests - when not defined(js): - doAssert formatSize((1'i64 shl 31) + (300'i64 shl 20)) == "2.293GiB" # <=== bug #8231 - doAssert formatSize((2.234*1024*1024).int) == "2.234MiB" - doAssert formatSize(4096) == "4KiB" - doAssert formatSize(4096, prefix = bpColloquial, includeSpace = true) == "4 kB" - doAssert formatSize(4096, includeSpace = true) == "4 KiB" - doAssert formatSize(5_378_934, prefix = bpColloquial, decimalSep = ',') == "5,13MB" - - block: # formatEng tests - doAssert formatEng(0, 2, trim = false) == "0.00" - doAssert formatEng(0, 2) == "0" - doAssert formatEng(53, 2, trim = false) == "53.00" - doAssert formatEng(0.053, 2, trim = false) == "53.00e-3" - doAssert formatEng(0.053, 4, trim = false) == "53.0000e-3" - doAssert formatEng(0.053, 4, trim = true) == "53e-3" - doAssert formatEng(0.053, 0) == "53e-3" - doAssert formatEng(52731234) == "52.731234e6" - doAssert formatEng(-52731234) == "-52.731234e6" - doAssert formatEng(52731234, 1) == "52.7e6" - doAssert formatEng(-52731234, 1) == "-52.7e6" - doAssert formatEng(52731234, 1, decimalSep = ',') == "52,7e6" - doAssert formatEng(-52731234, 1, decimalSep = ',') == "-52,7e6" - - doAssert formatEng(4100, siPrefix = true, unit = "V") == "4.1 kV" - doAssert formatEng(4.1, siPrefix = true, unit = "V", - useUnitSpace = true) == "4.1 V" - doAssert formatEng(4.1, siPrefix = true) == "4.1" # Note lack of space - doAssert formatEng(4100, siPrefix = true) == "4.1 k" - doAssert formatEng(4.1, siPrefix = true, unit = "", - useUnitSpace = true) == "4.1 " # Includes space - doAssert formatEng(4100, siPrefix = true, unit = "") == "4.1 k" - doAssert formatEng(4100) == "4.1e3" - doAssert formatEng(4100, unit = "V", useUnitSpace = true) == "4.1e3 V" - doAssert formatEng(4100, unit = "", useUnitSpace = true) == "4.1e3 " - # Don't use SI prefix as number is too big - doAssert formatEng(3.1e22, siPrefix = true, unit = "a", - useUnitSpace = true) == "31e21 a" - # Don't use SI prefix as number is too small - doAssert formatEng(3.1e-25, siPrefix = true, unit = "A", - useUnitSpace = true) == "310e-27 A" - - proc staticTests = - doAssert align("abc", 4) == " abc" - doAssert align("a", 0) == "a" - doAssert align("1232", 6) == " 1232" - doAssert align("1232", 6, '#') == "##1232" - - doAssert alignLeft("abc", 4) == "abc " - doAssert alignLeft("a", 0) == "a" - doAssert alignLeft("1232", 6) == "1232 " - doAssert alignLeft("1232", 6, '#') == "1232##" - - doAssert "$animal eats $food." % ["animal", "The cat", "food", "fish"] == - "The cat eats fish." - - doAssert "-ld a-ldz -ld".replaceWord("-ld") == " a-ldz " - doAssert "-lda-ldz -ld abc".replaceWord("-ld") == "-lda-ldz abc" - - doAssert "-lda-ldz -ld abc".replaceWord("") == "-lda-ldz -ld abc" - doAssert "oo".replace("", "abc") == "oo" - - type MyEnum = enum enA, enB, enC, enuD, enE - doAssert parseEnum[MyEnum]("enu_D") == enuD - - doAssert parseEnum("invalid enum value", enC) == enC - - doAssert center("foo", 13) == " foo " - doAssert center("foo", 0) == "foo" - doAssert center("foo", 3, fillChar = 'a') == "foo" - doAssert center("foo", 10, fillChar = '\t') == "\t\t\tfoo\t\t\t\t" - - doAssert count("foofoofoo", "foofoo") == 1 - doAssert count("foofoofoo", "foofoo", overlapping = true) == 2 - doAssert count("foofoofoo", 'f') == 3 - doAssert count("foofoofoobar", {'f', 'b'}) == 4 - - doAssert strip(" foofoofoo ") == "foofoofoo" - doAssert strip("sfoofoofoos", chars = {'s'}) == "foofoofoo" - doAssert strip("barfoofoofoobar", chars = {'b', 'a', 'r'}) == "foofoofoo" - doAssert strip("stripme but don't strip this stripme", - chars = {'s', 't', 'r', 'i', 'p', 'm', 'e'}) == - " but don't strip this " - doAssert strip("sfoofoofoos", leading = false, chars = {'s'}) == "sfoofoofoo" - doAssert strip("sfoofoofoos", trailing = false, chars = {'s'}) == "foofoofoos" - - doAssert " foo\n bar".indent(4, "Q") == "QQQQ foo\nQQQQ bar" - - doAssert "abba".multiReplace(("a", "b"), ("b", "a")) == "baab" - doAssert "Hello World.".multiReplace(("ello", "ELLO"), ("World.", - "PEOPLE!")) == "HELLO PEOPLE!" - doAssert "aaaa".multiReplace(("a", "aa"), ("aa", "bb")) == "aaaaaaaa" - - doAssert isAlphaAscii('r') - doAssert isAlphaAscii('A') - doAssert(not isAlphaAscii('$')) - - doAssert isAlphaNumeric('3') - doAssert isAlphaNumeric('R') - doAssert(not isAlphaNumeric('!')) - - doAssert isDigit('3') - doAssert(not isDigit('a')) - doAssert(not isDigit('%')) - - doAssert isSpaceAscii('\t') - doAssert isSpaceAscii('\l') - doAssert(not isSpaceAscii('A')) - - doAssert(isEmptyOrWhitespace("")) - doAssert(isEmptyOrWhitespace(" ")) - doAssert(isEmptyOrWhitespace("\t\l \v\r\f")) - doAssert(not isEmptyOrWhitespace("ABc \td")) - - doAssert isLowerAscii('a') - doAssert isLowerAscii('z') - doAssert(not isLowerAscii('A')) - doAssert(not isLowerAscii('5')) - doAssert(not isLowerAscii('&')) - doAssert(not isLowerAscii(' ')) - - doAssert isUpperAscii('A') - doAssert(not isUpperAscii('b')) - doAssert(not isUpperAscii('5')) - doAssert(not isUpperAscii('%')) - - doAssert rsplit("foo bar", seps = Whitespace) == @["foo", "bar"] - doAssert rsplit(" foo bar", seps = Whitespace, maxsplit = 1) == @[" foo", "bar"] - doAssert rsplit(" foo bar ", seps = Whitespace, maxsplit = 1) == @[ - " foo bar", ""] - doAssert rsplit(":foo:bar", sep = ':') == @["", "foo", "bar"] - doAssert rsplit(":foo:bar", sep = ':', maxsplit = 2) == @["", "foo", "bar"] - doAssert rsplit(":foo:bar", sep = ':', maxsplit = 3) == @["", "foo", "bar"] - doAssert rsplit("foothebar", sep = "the") == @["foo", "bar"] - - doAssert(unescape(r"\x013", "", "") == "\x013") - - doAssert join(["foo", "bar", "baz"]) == "foobarbaz" - doAssert join(@["foo", "bar", "baz"], ", ") == "foo, bar, baz" - doAssert join([1, 2, 3]) == "123" - doAssert join(@[1, 2, 3], ", ") == "1, 2, 3" - - doAssert """~~!!foo -~~!!bar -~~!!baz""".unindent(2, "~~!!") == "foo\nbar\nbaz" - - doAssert """~~!!foo -~~!!bar -~~!!baz""".unindent(2, "~~!!aa") == "~~!!foo\n~~!!bar\n~~!!baz" - doAssert """~~foo -~~ bar -~~ baz""".unindent(4, "~") == "foo\n bar\n baz" - doAssert """foo -bar - baz - """.unindent(4) == "foo\nbar\nbaz\n" - doAssert """foo - bar - baz - """.unindent(2) == "foo\n bar\n baz\n" - doAssert """foo - bar - baz - """.unindent(100) == "foo\nbar\nbaz\n" - - doAssert """foo - foo - bar - """.unindent() == "foo\nfoo\nbar\n" - - let s = " this is an example " - let s2 = ":this;is;an:example;;" - - doAssert s.split() == @["", "this", "is", "an", "example", "", ""] - doAssert s2.split(seps = {':', ';'}) == @["", "this", "is", "an", "example", - "", ""] - doAssert s.split(maxsplit = 4) == @["", "this", "is", "an", "example "] - doAssert s.split(' ', maxsplit = 1) == @["", "this is an example "] - doAssert s.split(" ", maxsplit = 4) == @["", "this", "is", "an", "example "] - - doAssert s.splitWhitespace() == @["this", "is", "an", "example"] - doAssert s.splitWhitespace(maxsplit = 1) == @["this", "is an example "] - doAssert s.splitWhitespace(maxsplit = 2) == @["this", "is", "an example "] - doAssert s.splitWhitespace(maxsplit = 3) == @["this", "is", "an", "example "] - doAssert s.splitWhitespace(maxsplit = 4) == @["this", "is", "an", "example"] - - block: # startsWith / endsWith char tests - var s = "abcdef" - doAssert s.startsWith('a') - doAssert s.startsWith('b') == false - doAssert s.endsWith('f') - doAssert s.endsWith('a') == false - doAssert s.endsWith('\0') == false - - #echo("strutils tests passed") - - nonStaticTests() - staticTests() - static: staticTests() \ No newline at end of file diff --git a/tests/stdlib/tstrutils.nim b/tests/stdlib/tstrutils.nim new file mode 100644 index 000000000..b62c82ffb --- /dev/null +++ b/tests/stdlib/tstrutils.nim @@ -0,0 +1,689 @@ +discard """ + targets: "c cpp js" +""" + +import std/strutils + +# xxx each instance of `disableVm` and `when not defined js:` should eventually be fixed + +template rejectParse(e) = + try: + discard e + raise newException(AssertionDefect, "This was supposed to fail: $#!" % astToStr(e)) + except ValueError: discard + +template disableVm(body) = + when nimvm: discard + else: body + +template main() = + block: # strip + doAssert strip(" ha ") == "ha" + doAssert strip(" foofoofoo ") == "foofoofoo" + doAssert strip("sfoofoofoos", chars = {'s'}) == "foofoofoo" + doAssert strip("barfoofoofoobar", chars = {'b', 'a', 'r'}) == "foofoofoo" + doAssert strip("stripme but don't strip this stripme", + chars = {'s', 't', 'r', 'i', 'p', 'm', 'e'}) == + " but don't strip this " + doAssert strip("sfoofoofoos", leading = false, chars = {'s'}) == "sfoofoofoo" + doAssert strip("sfoofoofoos", trailing = false, chars = {'s'}) == "foofoofoos" + + block: # split + var ret: seq[string] # or use `toSeq` or `collect` + for p in split("/home/a1:xyz:/usr/bin", {':'}): ret.add p + doAssert ret == @["/home/a1", "xyz", "/usr/bin"] + + let s = " this is an example " + let s2 = ":this;is;an:example;;" + + doAssert s.split() == @["", "this", "is", "an", "example", "", ""] + doAssert s2.split(seps = {':', ';'}) == @["", "this", "is", "an", "example", + "", ""] + doAssert s.split(maxsplit = 4) == @["", "this", "is", "an", "example "] + doAssert s.split(' ', maxsplit = 1) == @["", "this is an example "] + doAssert s.split(" ", maxsplit = 4) == @["", "this", "is", "an", "example "] + + block: # splitLines + let fixture = "a\nb\rc\r\nd" + assert len(fixture.splitLines) == 4 + assert splitLines(fixture) == @["a", "b", "c", "d"] + assert splitLines(fixture, keepEol=true) == @["a\n", "b\r", "c\r\n", "d"] + + block: # rsplit + doAssert rsplit("foo bar", seps = Whitespace) == @["foo", "bar"] + doAssert rsplit(" foo bar", seps = Whitespace, maxsplit = 1) == @[" foo", "bar"] + doAssert rsplit(" foo bar ", seps = Whitespace, maxsplit = 1) == @[ + " foo bar", ""] + doAssert rsplit(":foo:bar", sep = ':') == @["", "foo", "bar"] + doAssert rsplit(":foo:bar", sep = ':', maxsplit = 2) == @["", "foo", "bar"] + doAssert rsplit(":foo:bar", sep = ':', maxsplit = 3) == @["", "foo", "bar"] + doAssert rsplit("foothebar", sep = "the") == @["foo", "bar"] + + block: # splitWhitespace + let s = " this is an example " + doAssert s.splitWhitespace() == @["this", "is", "an", "example"] + doAssert s.splitWhitespace(maxsplit = 1) == @["this", "is an example "] + doAssert s.splitWhitespace(maxsplit = 2) == @["this", "is", "an example "] + doAssert s.splitWhitespace(maxsplit = 3) == @["this", "is", "an", "example "] + doAssert s.splitWhitespace(maxsplit = 4) == @["this", "is", "an", "example"] + + block: # removeSuffix + var s = "hello\n\r" + s.removeSuffix + assert s == "hello" + s.removeSuffix + assert s == "hello" + + s = "hello\n\n" + s.removeSuffix + assert s == "hello" + + s = "hello\r" + s.removeSuffix + assert s == "hello" + + s = "hello \n there" + s.removeSuffix + assert s == "hello \n there" + + s = "hello" + s.removeSuffix("llo") + assert s == "he" + s.removeSuffix('e') + assert s == "h" + + s = "hellos" + s.removeSuffix({'s','z'}) + assert s == "hello" + s.removeSuffix({'l','o'}) + assert s == "he" + + s = "aeiou" + s.removeSuffix("") + assert s == "aeiou" + + s = "" + s.removeSuffix("") + assert s == "" + + s = " " + s.removeSuffix + assert s == " " + + s = " " + s.removeSuffix("") + assert s == " " + + s = " " + s.removeSuffix(" ") + assert s == " " + + s = " " + s.removeSuffix(' ') + assert s == "" + + # Contrary to Chomp in other languages + # empty string does not change behaviour + s = "hello\r\n\r\n" + s.removeSuffix("") + assert s == "hello\r\n\r\n" + + block: # removePrefix + var s = "\n\rhello" + s.removePrefix + assert s == "hello" + s.removePrefix + assert s == "hello" + + s = "\n\nhello" + s.removePrefix + assert s == "hello" + + s = "\rhello" + s.removePrefix + assert s == "hello" + + s = "hello \n there" + s.removePrefix + assert s == "hello \n there" + + s = "hello" + s.removePrefix("hel") + assert s == "lo" + s.removePrefix('l') + assert s == "o" + + s = "hellos" + s.removePrefix({'h','e'}) + assert s == "llos" + s.removePrefix({'l','o'}) + assert s == "s" + + s = "aeiou" + s.removePrefix("") + assert s == "aeiou" + + s = "" + s.removePrefix("") + assert s == "" + + s = " " + s.removePrefix + assert s == " " + + s = " " + s.removePrefix("") + assert s == " " + + s = " " + s.removePrefix(" ") + assert s == " " + + s = " " + s.removePrefix(' ') + assert s == "" + + # Contrary to Chomp in other languages + # empty string does not change behaviour + s = "\r\n\r\nhello" + s.removePrefix("") + assert s == "\r\n\r\nhello" + + block: # delete + var s = "0123456789ABCDEFGH" + delete(s, 4, 5) + assert s == "01236789ABCDEFGH" + delete(s, s.len-1, s.len-1) + assert s == "01236789ABCDEFG" + delete(s, 0, 0) + assert s == "1236789ABCDEFG" + + block: # find + assert "0123456789ABCDEFGH".find('A') == 10 + assert "0123456789ABCDEFGH".find('A', 5) == 10 + assert "0123456789ABCDEFGH".find('A', 5, 10) == 10 + assert "0123456789ABCDEFGH".find('A', 5, 9) == -1 + assert "0123456789ABCDEFGH".find("A") == 10 + assert "0123456789ABCDEFGH".find("A", 5) == 10 + assert "0123456789ABCDEFGH".find("A", 5, 10) == 10 + assert "0123456789ABCDEFGH".find("A", 5, 9) == -1 + assert "0123456789ABCDEFGH".find({'A'..'C'}) == 10 + assert "0123456789ABCDEFGH".find({'A'..'C'}, 5) == 10 + assert "0123456789ABCDEFGH".find({'A'..'C'}, 5, 10) == 10 + assert "0123456789ABCDEFGH".find({'A'..'C'}, 5, 9) == -1 + + block: # rfind + assert "0123456789ABCDEFGAH".rfind('A') == 17 + assert "0123456789ABCDEFGAH".rfind('A', last=13) == 10 + assert "0123456789ABCDEFGAH".rfind('H', last=13) == -1 + assert "0123456789ABCDEFGAH".rfind("A") == 17 + assert "0123456789ABCDEFGAH".rfind("A", last=13) == 10 + assert "0123456789ABCDEFGAH".rfind("H", last=13) == -1 + assert "0123456789ABCDEFGAH".rfind({'A'..'C'}) == 17 + assert "0123456789ABCDEFGAH".rfind({'A'..'C'}, last=13) == 12 + assert "0123456789ABCDEFGAH".rfind({'G'..'H'}, last=13) == -1 + assert "0123456789ABCDEFGAH".rfind('A', start=18) == -1 + assert "0123456789ABCDEFGAH".rfind('A', start=11, last=17) == 17 + assert "0123456789ABCDEFGAH".rfind("0", start=0) == 0 + assert "0123456789ABCDEFGAH".rfind("0", start=1) == -1 + assert "0123456789ABCDEFGAH".rfind("H", start=11) == 18 + assert "0123456789ABCDEFGAH".rfind({'0'..'9'}, start=5) == 9 + assert "0123456789ABCDEFGAH".rfind({'0'..'9'}, start=10) == -1 + + assert "/1/2/3".rfind('/') == 4 + assert "/1/2/3".rfind('/', last=1) == 0 + assert "/1/2/3".rfind('0') == -1 + + block: # trimZeros + var x = "1200" + x.trimZeros() + assert x == "1200" + x = "120.0" + x.trimZeros() + assert x == "120" + x = "0." + x.trimZeros() + assert x == "0" + x = "1.0e2" + x.trimZeros() + assert x == "1e2" + x = "78.90" + x.trimZeros() + assert x == "78.9" + x = "1.23e4" + x.trimZeros() + assert x == "1.23e4" + x = "1.01" + x.trimZeros() + assert x == "1.01" + x = "1.1001" + x.trimZeros() + assert x == "1.1001" + x = "0.0" + x.trimZeros() + assert x == "0" + x = "0.01" + x.trimZeros() + assert x == "0.01" + x = "1e0" + x.trimZeros() + assert x == "1e0" + + block: # countLines + proc assertCountLines(s: string) = assert s.countLines == s.splitLines.len + assertCountLines("") + assertCountLines("\n") + assertCountLines("\n\n") + assertCountLines("abc") + assertCountLines("abc\n123") + assertCountLines("abc\n123\n") + assertCountLines("\nabc\n123") + assertCountLines("\nabc\n123\n") + + block: # parseBinInt, parseHexInt, parseOctInt + # binary + assert "0b1111".parseBinInt == 15 + assert "0B1111".parseBinInt == 15 + assert "1111".parseBinInt == 15 + assert "1110".parseBinInt == 14 + assert "1_1_1_1".parseBinInt == 15 + assert "0b1_1_1_1".parseBinInt == 15 + rejectParse "".parseBinInt + rejectParse "_".parseBinInt + rejectParse "0b".parseBinInt + rejectParse "0b1234".parseBinInt + # hex + assert "0x72".parseHexInt == 114 + assert "0X72".parseHexInt == 114 + assert "#72".parseHexInt == 114 + assert "72".parseHexInt == 114 + assert "FF".parseHexInt == 255 + assert "ff".parseHexInt == 255 + assert "fF".parseHexInt == 255 + assert "0x7_2".parseHexInt == 114 + rejectParse "".parseHexInt + rejectParse "_".parseHexInt + rejectParse "0x".parseHexInt + rejectParse "0xFFG".parseHexInt + rejectParse "reject".parseHexInt + # octal + assert "0o17".parseOctInt == 15 + assert "0O17".parseOctInt == 15 + assert "17".parseOctInt == 15 + assert "10".parseOctInt == 8 + assert "0o1_0_0".parseOctInt == 64 + rejectParse "".parseOctInt + rejectParse "_".parseOctInt + rejectParse "0o".parseOctInt + rejectParse "9".parseOctInt + rejectParse "0o9".parseOctInt + rejectParse "reject".parseOctInt + + block: # parseHexStr + assert "".parseHexStr == "" + assert "00Ff80".parseHexStr == "\0\xFF\x80" + try: + discard "00Ff8".parseHexStr + assert false, "Should raise ValueError" + except ValueError: + discard + + try: + discard "0k".parseHexStr + assert false, "Should raise ValueError" + except ValueError: + discard + + assert "".toHex == "" + assert "\x00\xFF\x80".toHex == "00FF80" + assert "0123456789abcdef".parseHexStr.toHex == "0123456789ABCDEF" + + block: # toHex + assert(toHex(100i16, 32) == "00000000000000000000000000000064") + assert(toHex(-100i16, 32) == "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9C") + when not defined js: + assert(toHex(high(uint64)) == "FFFFFFFFFFFFFFFF") + assert(toHex(high(uint64), 16) == "FFFFFFFFFFFFFFFF") + assert(toHex(high(uint64), 32) == "0000000000000000FFFFFFFFFFFFFFFF") + + block: # insertSep + assert(insertSep($1000_000) == "1_000_000") + assert(insertSep($232) == "232") + assert(insertSep($12345, ',') == "12,345") + assert(insertSep($0) == "0") + + block: # repeat, spaces + assert(' '.repeat(8) == " ") + assert(" ".repeat(8) == " ") + assert(spaces(8) == " ") + + assert(' '.repeat(0) == "") + assert(" ".repeat(0) == "") + assert(spaces(0) == "") + + block: # toBin, toOct + block:# bug #11369 + var num: int64 = -1 + when not defined js: + assert num.toBin(64) == "1111111111111111111111111111111111111111111111111111111111111111" + assert num.toOct(24) == "001777777777777777777777" + + block: # replace + doAssert "oo".replace("", "abc") == "oo" + # bug #8911 + static: + let a = "" + let a2 = a.replace("\n", "\\n") + + static: + let b = "b" + let b2 = b.replace("\n", "\\n") + + block: + let c = "" + let c2 = c.replace("\n", "\\n") + + block: # replaceWord + doAssert "-ld a-ldz -ld".replaceWord("-ld") == " a-ldz " + doAssert "-lda-ldz -ld abc".replaceWord("-ld") == "-lda-ldz abc" + doAssert "-lda-ldz -ld abc".replaceWord("") == "-lda-ldz -ld abc" + + block: # multiReplace + doAssert "abba".multiReplace(("a", "b"), ("b", "a")) == "baab" + doAssert "Hello World.".multiReplace(("ello", "ELLO"), ("World.", + "PEOPLE!")) == "HELLO PEOPLE!" + doAssert "aaaa".multiReplace(("a", "aa"), ("aa", "bb")) == "aaaaaaaa" + + # `parseEnum`, ref issue #14030 + # check enum defined at top level # xxx this is probably irrelevant, and pollutes scope + # for remaining tests + type + Foo = enum + A = -10 + B = "bb" + C = (-5, "ccc") + D = 15 + E = "ee" # check that we count enum fields correctly + + block: # parseEnum + block: + let a = parseEnum[Foo]("A") + let b = parseEnum[Foo]("bb") + let c = parseEnum[Foo]("ccc") + let d = parseEnum[Foo]("D") + let e = parseEnum[Foo]("ee") + doAssert a == A + doAssert b == B + doAssert c == C + doAssert d == D + doAssert e == E + try: + let f = parseEnum[Foo]("Bar") + doAssert false + except ValueError: + discard + + # finally using default + let g = parseEnum[Foo]("Bar", A) + doAssert g == A + + block: # check enum defined in block + type + Bar = enum + V + W = "ww" + X = (3, "xx") + Y = 10 + Z = "zz" # check that we count enum fields correctly + + let a = parseEnum[Bar]("V") + let b = parseEnum[Bar]("ww") + let c = parseEnum[Bar]("xx") + let d = parseEnum[Bar]("Y") + let e = parseEnum[Bar]("zz") + doAssert a == V + doAssert b == W + doAssert c == X + doAssert d == Y + doAssert e == Z + try: + let f = parseEnum[Bar]("Baz") + doAssert false + except ValueError: + discard + + # finally using default + let g = parseEnum[Bar]("Baz", V) + doAssert g == V + + block: # check ambiguous enum fails to parse + type + Ambig = enum + f1 = "A" + f2 = "B" + f3 = "A" + + doAssert not compiles((let a = parseEnum[Ambig]("A"))) + + block: # check almost ambiguous enum + type + AlmostAmbig = enum + f1 = "someA" + f2 = "someB" + f3 = "SomeA" + + let a = parseEnum[AlmostAmbig]("someA") + let b = parseEnum[AlmostAmbig]("someB") + let c = parseEnum[AlmostAmbig]("SomeA") + doAssert a == f1 + doAssert b == f2 + doAssert c == f3 + + block: # parseEnum TODO: merge above + type MyEnum = enum enA, enB, enC, enuD, enE + doAssert parseEnum[MyEnum]("enu_D") == enuD + + doAssert parseEnum("invalid enum value", enC) == enC + + block: # indentation + assert 0 == indentation """ +hey + low + there +""" + assert 2 == indentation """ + hey + low + there +""" + assert 2 == indentation """ hey + low + there +""" + assert 2 == indentation """ hey + low + there""" + assert 0 == indentation "" + assert 0 == indentation " \n \n" + assert 0 == indentation " " + + block: # indent + doAssert " foo\n bar".indent(4, "Q") == "QQQQ foo\nQQQQ bar" + + block: # unindent + doAssert """~~!!foo +~~!!bar +~~!!baz""".unindent(2, "~~!!") == "foo\nbar\nbaz" + + doAssert """~~!!foo +~~!!bar +~~!!baz""".unindent(2, "~~!!aa") == "~~!!foo\n~~!!bar\n~~!!baz" + doAssert """~~foo +~~ bar +~~ baz""".unindent(4, "~") == "foo\n bar\n baz" + doAssert """foo +bar + baz + """.unindent(4) == "foo\nbar\nbaz\n" + doAssert """foo + bar + baz + """.unindent(2) == "foo\n bar\n baz\n" + doAssert """foo + bar + baz + """.unindent(100) == "foo\nbar\nbaz\n" + + doAssert """foo + foo + bar + """.unindent() == "foo\nfoo\nbar\n" + + block: # formatBiggestFloat + disableVm: + doAssert formatBiggestFloat(1234.567, ffDecimal, -1) == "1234.567000" + when not defined(js): + doAssert formatBiggestFloat(1234.567, ffDecimal, 0) == "1235." # bugs 8242, 12586 + doAssert formatBiggestFloat(1234.567, ffDecimal, 1) == "1234.6" + doAssert formatBiggestFloat(0.00000000001, ffDecimal, 11) == "0.00000000001" + doAssert formatBiggestFloat(0.00000000001, ffScientific, 1, ',') in + ["1,0e-11", "1,0e-011"] + block: # formatFloat + disableVm: + # bug #6589 + when not defined(js): + doAssert formatFloat(123.456, ffScientific, precision = -1) == "1.234560e+02" + + block: # `%` + doAssert "$# $3 $# $#" % ["a", "b", "c"] == "a c b c" + doAssert "${1}12 ${-1}$2" % ["a", "b"] == "a12 bb" + doAssert "$animal eats $food." % ["animal", "The cat", "food", "fish"] == + "The cat eats fish." + + block: # formatSize + disableVm: + when not defined(js): + doAssert formatSize((1'i64 shl 31) + (300'i64 shl 20)) == "2.293GiB" # <=== bug #8231 + doAssert formatSize((2.234*1024*1024).int) == "2.234MiB" + doAssert formatSize(4096) == "4KiB" + doAssert formatSize(4096, prefix = bpColloquial, includeSpace = true) == "4 kB" + doAssert formatSize(4096, includeSpace = true) == "4 KiB" + doAssert formatSize(5_378_934, prefix = bpColloquial, decimalSep = ',') == "5,13MB" + + block: # formatEng + disableVm: + doAssert formatEng(0, 2, trim = false) == "0.00" + doAssert formatEng(0, 2) == "0" + doAssert formatEng(53, 2, trim = false) == "53.00" + doAssert formatEng(0.053, 2, trim = false) == "53.00e-3" + doAssert formatEng(0.053, 4, trim = false) == "53.0000e-3" + doAssert formatEng(0.053, 4, trim = true) == "53e-3" + doAssert formatEng(0.053, 0) == "53e-3" + doAssert formatEng(52731234) == "52.731234e6" + doAssert formatEng(-52731234) == "-52.731234e6" + doAssert formatEng(52731234, 1) == "52.7e6" + doAssert formatEng(-52731234, 1) == "-52.7e6" + doAssert formatEng(52731234, 1, decimalSep = ',') == "52,7e6" + doAssert formatEng(-52731234, 1, decimalSep = ',') == "-52,7e6" + + doAssert formatEng(4100, siPrefix = true, unit = "V") == "4.1 kV" + doAssert formatEng(4.1, siPrefix = true, unit = "V", + useUnitSpace = true) == "4.1 V" + doAssert formatEng(4.1, siPrefix = true) == "4.1" # Note lack of space + doAssert formatEng(4100, siPrefix = true) == "4.1 k" + doAssert formatEng(4.1, siPrefix = true, unit = "", + useUnitSpace = true) == "4.1 " # Includes space + doAssert formatEng(4100, siPrefix = true, unit = "") == "4.1 k" + doAssert formatEng(4100) == "4.1e3" + doAssert formatEng(4100, unit = "V", useUnitSpace = true) == "4.1e3 V" + doAssert formatEng(4100, unit = "", useUnitSpace = true) == "4.1e3 " + # Don't use SI prefix as number is too big + doAssert formatEng(3.1e22, siPrefix = true, unit = "a", + useUnitSpace = true) == "31e21 a" + # Don't use SI prefix as number is too small + doAssert formatEng(3.1e-25, siPrefix = true, unit = "A", + useUnitSpace = true) == "310e-27 A" + + block: # align + doAssert align("abc", 4) == " abc" + doAssert align("a", 0) == "a" + doAssert align("1232", 6) == " 1232" + doAssert align("1232", 6, '#') == "##1232" + + block: # alignLeft + doAssert alignLeft("abc", 4) == "abc " + doAssert alignLeft("a", 0) == "a" + doAssert alignLeft("1232", 6) == "1232 " + doAssert alignLeft("1232", 6, '#') == "1232##" + + block: # center + doAssert center("foo", 13) == " foo " + doAssert center("foo", 0) == "foo" + doAssert center("foo", 3, fillChar = 'a') == "foo" + doAssert center("foo", 10, fillChar = '\t') == "\t\t\tfoo\t\t\t\t" + + block: # count + doAssert count("foofoofoo", "foofoo") == 1 + doAssert count("foofoofoo", "foofoo", overlapping = true) == 2 + doAssert count("foofoofoo", 'f') == 3 + doAssert count("foofoofoobar", {'f', 'b'}) == 4 + + block: # isAlphaAscii + doAssert isAlphaAscii('r') + doAssert isAlphaAscii('A') + doAssert(not isAlphaAscii('$')) + + block: # isAlphaNumeric + doAssert isAlphaNumeric('3') + doAssert isAlphaNumeric('R') + doAssert(not isAlphaNumeric('!')) + + block: # isDigit + doAssert isDigit('3') + doAssert(not isDigit('a')) + doAssert(not isDigit('%')) + + block: # isSpaceAscii + doAssert isSpaceAscii('\t') + doAssert isSpaceAscii('\l') + doAssert(not isSpaceAscii('A')) + + block: # isEmptyOrWhitespace + doAssert(isEmptyOrWhitespace("")) + doAssert(isEmptyOrWhitespace(" ")) + doAssert(isEmptyOrWhitespace("\t\l \v\r\f")) + doAssert(not isEmptyOrWhitespace("ABc \td")) + + block: # isLowerAscii + doAssert isLowerAscii('a') + doAssert isLowerAscii('z') + doAssert(not isLowerAscii('A')) + doAssert(not isLowerAscii('5')) + doAssert(not isLowerAscii('&')) + doAssert(not isLowerAscii(' ')) + + block: # isUpperAscii + doAssert isUpperAscii('A') + doAssert(not isUpperAscii('b')) + doAssert(not isUpperAscii('5')) + doAssert(not isUpperAscii('%')) + + block: # unescape + doAssert(unescape(r"\x013", "", "") == "\x013") + + block: # join + doAssert join(["foo", "bar", "baz"]) == "foobarbaz" + doAssert join(@["foo", "bar", "baz"], ", ") == "foo, bar, baz" + doAssert join([1, 2, 3]) == "123" + doAssert join(@[1, 2, 3], ", ") == "1, 2, 3" + + block: # startsWith / endsWith + var s = "abcdef" + doAssert s.startsWith('a') + doAssert s.startsWith('b') == false + doAssert s.endsWith('f') + doAssert s.endsWith('a') == false + doAssert s.endsWith('\0') == false + +static: main() +main() From 9ef7c0182058141b81a41e32b699be8f2e42f3e9 Mon Sep 17 00:00:00 2001 From: Saem Ghani Date: Tue, 15 Dec 2020 05:57:11 -0800 Subject: [PATCH 1178/3768] Syntax (RST) fix Internals of the Compiler (#16341) 'Rebuilding the Compiler' and a few other sections required syntax fixes: * new lines before and after lists to fix rendering * use internal reference instead of Markdown style link --- doc/intern.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/intern.rst b/doc/intern.rst index 3f8a2ce56..5de0c35d5 100644 --- a/doc/intern.rst +++ b/doc/intern.rst @@ -118,20 +118,22 @@ Rebuilding the compiler After an initial build via `sh build_all.sh` on posix or `build_all.bat` on windows, you can rebuild the compiler as follows: + * `nim c koch` if you need to rebuild koch * `./koch boot -d:release` this ensures the compiler can rebuild itself (use `koch` instead of `./koch` on windows), which builds the compiler 3 times. A faster approach if you don't need to run the full bootstrapping implied by `koch boot`, is the following: + * `pathto/nim c --lib:lib -d:release -o:bin/nim_temp compiler/nim.nim` + Where `pathto/nim` is any nim binary sufficiently recent (e.g. `bin/nim_cources` built during bootstrap or `$HOME/.nimble/bin/nim` installed by `choosenim 1.2.0`) You can pass any additional options such as `-d:leanCompiler` if you don't need certain features or `-d:debug --stacktrace:on --excessiveStackTrace --stackTraceMsgs` -for debugging the compiler. See also -[Debugging the compiler](intern.html#debugging-the-compiler). +for debugging the compiler. See also `Debugging the compiler`_. Debugging the compiler ====================== @@ -174,8 +176,7 @@ To create a new compiler for each run, use ``koch temp``:: ``koch temp`` creates a debug build of the compiler, which is useful to create stacktraces for compiler debugging. See also -[Rebuilding the compiler](intern.html#rebuilding-the-compiler) if you need -more control. +`Rebuilding the compiler`_ if you need more control. Bisecting for regressions ========================= @@ -189,8 +190,7 @@ current commit.:: You can also bisect using custom options to build the compiler, for example if you don't need a debug version of the compiler (which runs slower), you can replace -`./koch temp` by explicit compilation command, see -[Rebuilding the compiler](intern.html#rebuilding-the-compiler). +`./koch temp` by explicit compilation command, see `Rebuilding the compiler`_. The compiler's architecture =========================== From 3b963a8150e3c541859931a1ef9ecb338e46907f Mon Sep 17 00:00:00 2001 From: Maurizio Tomasi Date: Wed, 16 Dec 2020 17:05:59 +0100 Subject: [PATCH 1179/3768] Add note about minimum Node.js version (#16370) Fix the error message --- doc/backends.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/backends.rst b/doc/backends.rst index b50c7e38f..070cadbfd 100644 --- a/doc/backends.rst +++ b/doc/backends.rst @@ -93,7 +93,10 @@ file. However, you can also run the code with `nodejs`:idx: nim js -d:nodejs -r examples/hallo.nim +If you experience errors saying that ``globalThis`` is not defined, be +sure to run a recent version of Node.js (at least 12.0). + Interfacing =========== From 979148e863c4142a00632059f3e2a57f4ab0f960 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 17 Dec 2020 08:01:36 +0100 Subject: [PATCH 1180/3768] refactorings to prepare the compiler for IC (#15935) * added ic specific Nim code; WIP * make the symbol import mechanism lazy; WIP * ensure that modules can be imported multiple times * ambiguity checking * handle converters and TR macros properly * make 'enum' test category green again * special logic for semi-pure enums * makes nimsuggest tests green again * fixes nimdata * makes nimpy green again * makes more important packages work --- compiler/ast.nim | 4 +- compiler/ic/bitabs.nim | 119 ++++++ compiler/ic/design.rst | 42 +++ compiler/ic/from_packed_ast.nim | 12 + compiler/ic/packed_ast.nim | 461 ++++++++++++++++++++++++ compiler/ic/to_packed_ast.nim | 90 +++++ compiler/importer.nim | 167 +++++++-- compiler/lookups.nim | 301 +++++++++++++--- compiler/main.nim | 6 +- compiler/modulegraphs.nim | 35 +- compiler/modules.nim | 10 +- compiler/plugins/locals.nim | 3 +- compiler/pragmas.nim | 4 +- compiler/rod.nim | 2 +- compiler/sem.nim | 8 +- compiler/semdata.nim | 24 +- compiler/semexprs.nim | 31 +- compiler/semgnrc.nim | 12 +- compiler/semstmts.nim | 10 +- compiler/semtypes.nim | 8 +- compiler/suggest.nim | 50 +-- lib/system.nim | 2 +- testament/important_packages.nim | 4 +- tests/modules/mforwarded_pure_enum.nim | 3 + tests/modules/mforwarded_pure_enum2.nim | 4 + tests/modules/tfowarded_pure_enum.nim | 7 + 26 files changed, 1241 insertions(+), 178 deletions(-) create mode 100644 compiler/ic/bitabs.nim create mode 100644 compiler/ic/design.rst create mode 100644 compiler/ic/from_packed_ast.nim create mode 100644 compiler/ic/packed_ast.nim create mode 100644 compiler/ic/to_packed_ast.nim create mode 100644 tests/modules/mforwarded_pure_enum.nim create mode 100644 tests/modules/mforwarded_pure_enum2.nim create mode 100644 tests/modules/tfowarded_pure_enum.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index fbad9e44e..4ca5035ed 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -799,6 +799,7 @@ type libHeader, libDynamic TLib* = object # also misused for headers! + # keep in sync with PackedLib kind*: TLibKind generated*: bool # needed for the backends: isOverriden*: bool @@ -823,7 +824,7 @@ type PScope* = ref TScope PLib* = ref TLib - TSym* {.acyclic.} = object of TIdObj + TSym* {.acyclic.} = object of TIdObj # Keep in sync with PackedSym # proc and type instantiations are cached in the generic symbol case kind*: TSymKind of skType, skGenericParam: @@ -904,6 +905,7 @@ type # types are identical iff they have the # same id; there may be multiple copies of a type # in memory! + # Keep in sync with PackedType kind*: TTypeKind # kind of type callConv*: TCallingConvention # for procs flags*: TTypeFlags # flags of the type diff --git a/compiler/ic/bitabs.nim b/compiler/ic/bitabs.nim new file mode 100644 index 000000000..91221ea49 --- /dev/null +++ b/compiler/ic/bitabs.nim @@ -0,0 +1,119 @@ +## A BiTable is a table that can be seen as an optimized pair +## of (Table[LitId, Val], Table[Val, LitId]). + +import hashes + +type + LitId* = distinct uint32 + + BiTable*[T] = object + vals: seq[T] # indexed by LitId + keys: seq[LitId] # indexed by hash(val) + +proc nextTry(h, maxHash: Hash): Hash {.inline.} = + result = (h + 1) and maxHash + +template maxHash(t): untyped = high(t.keys) +template isFilled(x: LitId): bool = x.uint32 > 0'u32 + +proc `$`*(x: LitId): string {.borrow.} +proc `<`*(x, y: LitId): bool {.borrow.} +proc `<=`*(x, y: LitId): bool {.borrow.} +proc `==`*(x, y: LitId): bool {.borrow.} +proc hash*(x: LitId): Hash {.borrow.} + + +proc len*[T](t: BiTable[T]): int = t.vals.len + +proc mustRehash(length, counter: int): bool {.inline.} = + assert(length > counter) + result = (length * 2 < counter * 3) or (length - counter < 4) + +const + idStart = 256 # Ids do not start with 0 but with this value. The IR needs it. + +template idToIdx(x: LitId): int = x.int - idStart + +proc enlarge[T](t: var BiTable[T]) = + var n: seq[LitId] + newSeq(n, len(t.keys) * 2) + swap(t.keys, n) + for i in 0..high(n): + let eh = n[i] + if isFilled(eh): + var j = hash(t.vals[idToIdx eh]) and maxHash(t) + while isFilled(t.keys[j]): + j = nextTry(j, maxHash(t)) + t.keys[j] = move n[i] + +proc getKeyId*[T](t: BiTable[T]; v: T): LitId = + let origH = hash(v) + var h = origH and maxHash(t) + if t.keys.len != 0: + while true: + let litId = t.keys[h] + if not isFilled(litId): break + if t.vals[idToIdx t.keys[h]] == v: return litId + h = nextTry(h, maxHash(t)) + return LitId(0) + +proc getOrIncl*[T](t: var BiTable[T]; v: T): LitId = + let origH = hash(v) + var h = origH and maxHash(t) + if t.keys.len != 0: + while true: + let litId = t.keys[h] + if not isFilled(litId): break + if t.vals[idToIdx t.keys[h]] == v: return litId + h = nextTry(h, maxHash(t)) + # not found, we need to insert it: + if mustRehash(t.keys.len, t.vals.len): + enlarge(t) + # recompute where to insert: + h = origH and maxHash(t) + while true: + let litId = t.keys[h] + if not isFilled(litId): break + h = nextTry(h, maxHash(t)) + else: + setLen(t.keys, 16) + h = origH and maxHash(t) + + result = LitId(t.vals.len + idStart) + t.keys[h] = result + t.vals.add v + + +proc `[]`*[T](t: var BiTable[T]; LitId: LitId): var T {.inline.} = + let idx = idToIdx LitId + assert idx < t.vals.len + result = t.vals[idx] + +proc `[]`*[T](t: BiTable[T]; LitId: LitId): lent T {.inline.} = + let idx = idToIdx LitId + assert idx < t.vals.len + result = t.vals[idx] + +when isMainModule: + + var t: BiTable[string] + + echo getOrIncl(t, "hello") + + echo getOrIncl(t, "hello") + echo getOrIncl(t, "hello3") + echo getOrIncl(t, "hello4") + echo getOrIncl(t, "helloasfasdfdsa") + echo getOrIncl(t, "hello") + echo getKeyId(t, "hello") + echo getKeyId(t, "none") + + for i in 0 ..< 100_000: + discard t.getOrIncl($i & "___" & $i) + + for i in 0 ..< 100_000: + assert t.getOrIncl($i & "___" & $i).idToIdx == i + 4 + echo t.vals.len + + echo t.vals[0] + echo t.vals[1004] diff --git a/compiler/ic/design.rst b/compiler/ic/design.rst new file mode 100644 index 000000000..1a33f6a27 --- /dev/null +++ b/compiler/ic/design.rst @@ -0,0 +1,42 @@ +==================================== + Incremental Recompilations +==================================== + +We split the Nim compiler into a frontend and a backend. +The frontend produces a set of `.rod` files. Every `.nim` module +produces its own `.rod` file. + +- The IR must be a faithful representation of the AST in memory. +- The backend can do its own caching but doesn't have to. +- We know by comparing 'nim check compiler/nim' against 'nim c compiler/nim' + that 2/3 of the compiler's runtime is spent in the frontend. Hence we + implement IC for the frontend first and only later for the backend. The + backend will recompile everything until we implement its own caching + mechanisms. + +Advantage of the "set of files" vs the previous global database: +- By construction, we either read from the `.rod` file or from the + `.nim` file, there can be no inconsistency. There can also be no + partial updates. +- No dependency to external packages (SQLite). SQLite simply is too + slow and the old way of serialization was too slow too. We use a + format designed for Nim and expect to base further tools on this + file format. + +References to external modules must be (moduleId, symId) pairs. +The symbol IDs are module specific. This way no global ID increment +mechanism needs to be implemented that we could get wrong. ModuleIds +are rod-file specific too. + + +Configuration setup changes +--------------------------- + +For a MVP these are not detected. Later the configuration will be +stored in every `.rod` file. + + +Global state +------------ + +Global persistent state will be kept in a project specific `.rod` file. diff --git a/compiler/ic/from_packed_ast.nim b/compiler/ic/from_packed_ast.nim new file mode 100644 index 000000000..cb2ecee79 --- /dev/null +++ b/compiler/ic/from_packed_ast.nim @@ -0,0 +1,12 @@ +# +# +# The Nim Compiler +# (c) Copyright 2020 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +import std / [hashes, tables] +import bitabs +import ".." / [ast, lineinfos, options, pathutils] diff --git a/compiler/ic/packed_ast.nim b/compiler/ic/packed_ast.nim new file mode 100644 index 000000000..ef609e8c8 --- /dev/null +++ b/compiler/ic/packed_ast.nim @@ -0,0 +1,461 @@ +# +# +# The Nim Compiler +# (c) Copyright 2020 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Packed AST representation, mostly based on a seq of nodes. +## For IC support. Far future: Rewrite the compiler passes to +## use this representation directly in all the transformations, +## it is superior. + +import std / [hashes, tables] +import bitabs +import ".." / [ast, lineinfos, options, pathutils] + +const + localNamePos* = 0 + localExportMarkerPos* = 1 + localPragmaPos* = 2 + localTypePos* = 3 + localValuePos* = 4 + + typeNamePos* = 0 + typeExportMarkerPos* = 1 + typeGenericParamsPos* = 2 + typePragmaPos* = 3 + typeBodyPos* = 4 + + routineNamePos* = 0 + routineExportMarkerPos* = 1 + routinePatternPos* = 2 + routineGenericParamsPos* = 3 + routineParamsPos* = 4 + routineResultPos* = 5 + routinePragmasPos* = 6 + routineBodyPos* = 7 + +const + nkModuleRef = nkNone # pair of (ModuleId, SymId) + +type + SymId* = distinct int32 + TypeId* = distinct int32 + ModuleId* = distinct int32 + NodePos* = distinct int + + NodeId* = distinct int32 + + PackedLineInfo* = object + line*: uint16 + col*: int16 + file*: LitId + + PackedLib* = object + kind*: TLibKind + generated*: bool + isOverriden*: bool + name*: LitId + path*: NodeId + + PackedSym* = object + kind*: TSymKind + name*: LitId + typeId*: TypeId + flags*: TSymFlags + magic*: TMagic + info*: PackedLineInfo + ast*: NodePos + owner*: ItemId + guard*: ItemId + bitsize*: int + alignment*: int # for alignment + options*: TOptions + position*: int + offset*: int + externalName*: LitId # instead of TLoc + annex*: PackedLib + when hasFFI: + cname*: LitId + constraint*: NodeId + + PackedType* = object + kind*: TTypeKind + nodekind*: TNodeKind + flags*: TTypeFlags + types*: int32 + nodes*: int32 + methods*: int32 + nodeflags*: TNodeFlags + info*: PackedLineInfo + sym*: ItemId + owner*: ItemId + attachedOps*: array[TTypeAttachedOp, ItemId] + size*: BiggestInt + align*: int16 + paddingAtEnd*: int16 + lockLevel*: TLockLevel # lock level as required for deadlock checking + # not serialized: loc*: TLoc because it is backend-specific + typeInst*: TypeId + nonUniqueId*: ItemId + + Node* = object # 20 bytes + kind*: TNodeKind + flags*: TNodeFlags + operand*: int32 # for kind in {nkSym, nkSymDef}: SymId + # for kind in {nkStrLit, nkIdent, nkNumberLit}: LitId + # for kind in nkInt32Lit: direct value + # for non-atom kinds: the number of nodes (for easy skipping) + typeId*: TypeId + info*: PackedLineInfo + + ModulePhase* = enum + preLookup, lookedUpTopLevelStmts + + Module* = object + name*: string + file*: AbsoluteFile + ast*: PackedTree + phase*: ModulePhase + iface*: Table[string, seq[SymId]] # 'seq' because of overloading + + Program* = ref object + modules*: seq[Module] + + Shared* = ref object # shared between different versions of 'Module'. + # (though there is always exactly one valid + # version of a module) + syms*: seq[PackedSym] + types*: seq[seq[Node]] + strings*: BiTable[string] # we could share these between modules. + integers*: BiTable[BiggestInt] + floats*: BiTable[BiggestFloat] + config*: ConfigRef + #thisModule*: ModuleId + #program*: Program + + PackedTree* = object ## usually represents a full Nim module + nodes*: seq[Node] + toPosition*: Table[SymId, NodePos] + sh*: Shared + +proc `==`*(a, b: SymId): bool {.borrow.} +proc hash*(a: SymId): Hash {.borrow.} + +proc `==`*(a, b: NodePos): bool {.borrow.} +proc `==`*(a, b: TypeId): bool {.borrow.} +proc `==`*(a, b: ModuleId): bool {.borrow.} + +proc declareSym*(tree: var PackedTree; kind: TSymKind; + name: LitId; info: PackedLineInfo): SymId = + result = SymId(tree.sh.syms.len) + tree.sh.syms.add PackedSym(kind: kind, name: name, flags: {}, magic: mNone, info: info) + +proc newTreeFrom*(old: PackedTree): PackedTree = + result.nodes = @[] + result.sh = old.sh + +proc litIdFromName*(tree: PackedTree; name: string): LitId = + result = tree.sh.strings.getOrIncl(name) + +proc add*(tree: var PackedTree; kind: TNodeKind; token: string; info: PackedLineInfo) = + tree.nodes.add Node(kind: kind, operand: int32 getOrIncl(tree.sh.strings, token), info: info) + +proc add*(tree: var PackedTree; kind: TNodeKind; info: PackedLineInfo) = + tree.nodes.add Node(kind: kind, operand: 0, info: info) + +proc throwAwayLastNode*(tree: var PackedTree) = + tree.nodes.setLen(tree.nodes.len-1) + +proc addIdent*(tree: var PackedTree; s: LitId; info: PackedLineInfo) = + tree.nodes.add Node(kind: nkIdent, operand: int32(s), info: info) + +proc addSym*(tree: var PackedTree; s: SymId; info: PackedLineInfo) = + tree.nodes.add Node(kind: nkSym, operand: int32(s), info: info) + +proc addModuleId*(tree: var PackedTree; s: ModuleId; info: PackedLineInfo) = + tree.nodes.add Node(kind: nkInt32Lit, operand: int32(s), info: info) + +proc addSymDef*(tree: var PackedTree; s: SymId; info: PackedLineInfo) = + tree.nodes.add Node(kind: nkSym, operand: int32(s), info: info) + +proc isAtom*(tree: PackedTree; pos: int): bool {.inline.} = tree.nodes[pos].kind <= nkNilLit + +proc copyTree*(dest: var PackedTree; tree: PackedTree; n: NodePos) = + # and this is why the IR is superior. We can copy subtrees + # via a linear scan. + let pos = n.int + let L = if isAtom(tree, pos): 1 else: tree.nodes[pos].operand + let d = dest.nodes.len + dest.nodes.setLen(d + L) + for i in 0.. nkNilLit + let distance = int32(tree.nodes.len - pos) + tree.nodes[pos].operand = distance + +proc len*(tree: PackedTree): int {.inline.} = tree.nodes.len + +proc `[]`*(tree: PackedTree; i: int): lent Node {.inline.} = tree.nodes[i] + +proc nextChild(tree: PackedTree; pos: var int) {.inline.} = + if tree.nodes[pos].kind > nkNilLit: + assert tree.nodes[pos].operand > 0 + inc pos, tree.nodes[pos].operand + else: + inc pos + +iterator sonsReadonly*(tree: PackedTree; n: NodePos): NodePos = + var pos = n.int + assert tree.nodes[pos].kind > nkNilLit + let last = pos + tree.nodes[pos].operand + inc pos + while pos < last: + yield NodePos pos + nextChild tree, pos + +iterator sons*(dest: var PackedTree; tree: PackedTree; n: NodePos): NodePos = + let patchPos = prepare(dest, tree, n) + for x in sonsReadonly(tree, n): yield x + patch dest, patchPos + +iterator isons*(dest: var PackedTree; tree: PackedTree; n: NodePos): (int, NodePos) = + var i = 0 + for ch0 in sons(dest, tree, n): + yield (i, ch0) + inc i + +iterator sonsFrom1*(tree: PackedTree; n: NodePos): NodePos = + var pos = n.int + assert tree.nodes[pos].kind > nkNilLit + let last = pos + tree.nodes[pos].operand + inc pos + if pos < last: + nextChild tree, pos + while pos < last: + yield NodePos pos + nextChild tree, pos + +iterator sonsWithoutLast2*(tree: PackedTree; n: NodePos): NodePos = + var count = 0 + for child in sonsReadonly(tree, n): + inc count + var pos = n.int + assert tree.nodes[pos].kind > nkNilLit + let last = pos + tree.nodes[pos].operand + inc pos + while pos < last and count > 2: + yield NodePos pos + dec count + nextChild tree, pos + +proc parentImpl(tree: PackedTree; n: NodePos): NodePos = + # finding the parent of a node is rather easy: + var pos = n.int - 1 + while pos >= 0 and isAtom(tree, pos) or (pos + tree.nodes[pos].operand - 1 < n.int): + dec pos + assert pos >= 0, "node has no parent" + result = NodePos(pos) + +template parent*(n: NodePos): NodePos = parentImpl(tree, n) + +proc hasXsons*(tree: PackedTree; n: NodePos; x: int): bool = + var count = 0 + if tree.nodes[n.int].kind > nkNilLit: + for child in sonsReadonly(tree, n): inc count + result = count == x + +proc hasAtLeastXsons*(tree: PackedTree; n: NodePos; x: int): bool = + if tree.nodes[n.int].kind > nkNilLit: + var count = 0 + for child in sonsReadonly(tree, n): + inc count + if count >= x: return true + return false + +proc firstSon*(tree: PackedTree; n: NodePos): NodePos {.inline.} = NodePos(n.int+1) +proc kind*(tree: PackedTree; n: NodePos): TNodeKind {.inline.} = tree.nodes[n.int].kind +proc litId*(tree: PackedTree; n: NodePos): LitId {.inline.} = LitId tree.nodes[n.int].operand +proc info*(tree: PackedTree; n: NodePos): PackedLineInfo {.inline.} = tree.nodes[n.int].info + +proc span(tree: PackedTree; pos: int): int {.inline.} = + if isAtom(tree, pos): 1 else: tree.nodes[pos].operand + +proc sons2*(tree: PackedTree; n: NodePos): (NodePos, NodePos) = + assert(not isAtom(tree, n.int)) + let a = n.int+1 + let b = a + span(tree, a) + result = (NodePos a, NodePos b) + +proc sons3*(tree: PackedTree; n: NodePos): (NodePos, NodePos, NodePos) = + assert(not isAtom(tree, n.int)) + let a = n.int+1 + let b = a + span(tree, a) + let c = b + span(tree, b) + result = (NodePos a, NodePos b, NodePos c) + +proc ithSon*(tree: PackedTree; n: NodePos; i: int): NodePos = + if tree.nodes[n.int].kind > nkNilLit: + var count = 0 + for child in sonsReadonly(tree, n): + if count == i: return child + inc count + assert false, "node has no i-th child" + +proc `@`*(tree: PackedTree; lit: LitId): lent string {.inline.} = tree.sh.strings[lit] + +template kind*(n: NodePos): TNodeKind = tree.nodes[n.int].kind +template info*(n: NodePos): PackedLineInfo = tree.nodes[n.int].info +template litId*(n: NodePos): LitId = LitId tree.nodes[n.int].operand + +template symId*(n: NodePos): SymId = SymId tree.nodes[n.int].operand + +proc firstSon*(n: NodePos): NodePos {.inline.} = NodePos(n.int+1) + +proc strLit*(tree: PackedTree; n: NodePos): lent string = + assert n.kind == nkStrLit + result = tree.sh.strings[LitId tree.nodes[n.int].operand] + +proc strVal*(tree: PackedTree; n: NodePos): string = + assert n.kind == nkStrLit + result = tree.sh.strings[LitId tree.nodes[n.int].operand] + #result = cookedStrLit(raw) + +proc filenameVal*(tree: PackedTree; n: NodePos): string = + case n.kind + of nkStrLit: + result = strVal(tree, n) + of nkIdent: + result = tree.sh.strings[n.litId] + of nkSym: + result = tree.sh.strings[tree.sh.syms[int n.symId].name] + else: + result = "" + +proc identAsStr*(tree: PackedTree; n: NodePos): lent string = + assert n.kind == nkIdent + result = tree.sh.strings[LitId tree.nodes[n.int].operand] + +const + externIntLit* = {nkCharLit, + nkIntLit, + nkInt8Lit, + nkInt16Lit, + nkInt64Lit, + nkUIntLit, + nkUInt8Lit, + nkUInt16Lit, + nkUInt32Lit, + nkUInt64Lit} # nkInt32Lit is missing by design! + + externSIntLit* = {nkIntLit, nkInt8Lit, nkInt16Lit, nkInt64Lit} + externUIntLit* = {nkUIntLit, nkUInt8Lit, nkUInt16Lit, nkUInt32Lit, nkUInt64Lit} + directIntLit* = nkInt32Lit + +proc toString*(tree: PackedTree; n: NodePos; nesting: int; result: var string) = + let pos = n.int + if result.len > 0 and result[^1] notin {' ', '\n'}: + result.add ' ' + + result.add $tree[pos].kind + case tree.nodes[pos].kind + of nkNone, nkEmpty, nkNilLit, nkType: discard + of nkIdent, nkStrLit..nkTripleStrLit: + result.add " " + result.add tree.sh.strings[LitId tree.nodes[pos].operand] + of nkSym: + result.add " " + result.add tree.sh.strings[tree.sh.syms[tree.nodes[pos].operand].name] + of directIntLit: + result.add " " + result.addInt tree.nodes[pos].operand + of externSIntLit: + result.add " " + result.addInt tree.sh.integers[LitId tree.nodes[pos].operand] + of externUIntLit: + result.add " " + result.add $cast[uint64](tree.sh.integers[LitId tree.nodes[pos].operand]) + else: + result.add "(\n" + for i in 1..(nesting+1)*2: result.add ' ' + for child in sonsReadonly(tree, n): + toString(tree, child, nesting + 1, result) + result.add "\n" + for i in 1..nesting*2: result.add ' ' + result.add ")" + #for i in 1..nesting*2: result.add ' ' + + +proc toString*(tree: PackedTree; n: NodePos): string = + result = "" + toString(tree, n, 0, result) + +proc debug*(tree: PackedTree) = + stdout.write toString(tree, NodePos 0) + +proc identIdImpl(tree: PackedTree; n: NodePos): LitId = + if n.kind == nkIdent: + result = n.litId + elif n.kind == nkSym: + result = tree.sh.syms[int n.symId].name + else: + result = LitId(0) + +template identId*(n: NodePos): LitId = identIdImpl(tree, n) + +template copyInto*(dest, n, body) = + let patchPos = prepare(dest, tree, n) + body + patch dest, patchPos + +template copyIntoKind*(dest, kind, info, body) = + let patchPos = prepare(dest, kind, info) + body + patch dest, patchPos + +proc hasPragma*(tree: PackedTree; n: NodePos; pragma: string): bool = + let litId = tree.sh.strings.getKeyId(pragma) + if litId == LitId(0): + return false + assert n.kind == nkPragma + for ch0 in sonsReadonly(tree, n): + if ch0.kind == nkExprColonExpr: + if ch0.firstSon.identId == litId: + return true + elif ch0.identId == litId: + return true + +when false: + proc produceError*(dest: var PackedTree; tree: PackedTree; n: NodePos; msg: string) = + let patchPos = prepare(dest, nkError, n.info) + dest.add nkStrLit, msg, n.info + copyTree(dest, tree, n) + patch dest, patchPos diff --git a/compiler/ic/to_packed_ast.nim b/compiler/ic/to_packed_ast.nim new file mode 100644 index 000000000..85fec9081 --- /dev/null +++ b/compiler/ic/to_packed_ast.nim @@ -0,0 +1,90 @@ +# +# +# The Nim Compiler +# (c) Copyright 2020 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +import std / [hashes, tables] +import packed_ast, bitabs +import ".." / [ast, idents, lineinfos, options, pathutils, msgs] + +type + Context = object + thisModule: int32 + lastFile: FileIndex # remember the last lookup entry. + lastLit: LitId + filenames: Table[FileIndex, LitId] + +proc toLitId(x: FileIndex; ir: var PackedTree; c: var Context): LitId = + if x == c.lastFile: + result = c.lastLit + else: + result = c.filenames.getOrDefault(x) + if result == LitId(0): + let p = msgs.toFullPath(ir.sh.config, x) + result = getOrIncl(ir.sh.strings, p) + c.filenames[x] = result + c.lastFile = x + c.lastLit = result + +proc toPackedInfo(x: TLineInfo; ir: var PackedTree; c: var Context): PackedLineInfo = + PackedLineInfo(line: x.line, col: x.col, file: toLitId(x.fileIndex, ir, c)) + +proc toPackedType(t: PType; ir: var PackedTree; c: var Context): TypeId = + result = TypeId(0) + +proc toPackedSym(s: PSym; ir: var PackedTree; c: var Context): SymId = + result = SymId(0) + +proc toPackedSymNode(n: PNode; ir: var PackedTree; c: var Context) = + assert n.kind == nkSym + let t = toPackedType(n.typ, ir, c) + + if n.sym.itemId.module == c.thisModule: + # it is a symbol that belongs to the module we're currently + # packing: + let sid = toPackedSym(n.sym, ir, c) + ir.nodes.add Node(kind: n.kind, flags: n.flags, operand: int32(sid), + typeId: t, info: toPackedInfo(n.info, ir, c)) + else: + # store it as an external module reference: + # nkModuleRef + discard + + +proc toPackedNode*(n: PNode; ir: var PackedTree; c: var Context) = + template toP(x: TLineInfo): PackedLineInfo = toPackedInfo(x, ir, c) + + case n.kind + of nkNone, nkEmpty, nkNilLit: + ir.nodes.add Node(kind: n.kind, flags: n.flags, operand: 0, + typeId: toPackedType(n.typ, ir, c), info: toP n.info) + of nkIdent: + ir.nodes.add Node(kind: n.kind, flags: n.flags, operand: int32 getOrIncl(ir.sh.strings, n.ident.s), + typeId: toPackedType(n.typ, ir, c), info: toP n.info) + of nkSym: + toPackedSymNode(n, ir, c) + of directIntLit: + ir.nodes.add Node(kind: n.kind, flags: n.flags, operand: int32(n.intVal), + typeId: toPackedType(n.typ, ir, c), info: toP n.info) + of externIntLit: + ir.nodes.add Node(kind: n.kind, flags: n.flags, operand: int32 getOrIncl(ir.sh.integers, n.intVal), + typeId: toPackedType(n.typ, ir, c), info: toP n.info) + of nkStrLit..nkTripleStrLit: + ir.nodes.add Node(kind: n.kind, flags: n.flags, operand: int32 getOrIncl(ir.sh.strings, n.strVal), + typeId: toPackedType(n.typ, ir, c), info: toP n.info) + of nkFloatLit..nkFloat128Lit: + ir.nodes.add Node(kind: n.kind, flags: n.flags, operand: int32 getOrIncl(ir.sh.floats, n.floatVal), + typeId: toPackedType(n.typ, ir, c), info: toP n.info) + else: + let patchPos = ir.prepare(n.kind, n.flags, toPackedType(n.typ, ir, c), toP n.info) + for i in 0.. 0: + result = candidates[0] + amb = candidates.len > 1 + if amb and checkAmbiguity in flags: + errorUseQualifier(c, n.info, candidates) + if result == nil: + let candidates = allPureEnumFields(c, ident) + if candidates.len > 0: + result = candidates[0] + amb = candidates.len > 1 + if amb and checkAmbiguity in flags: + errorUseQualifier(c, n.info, candidates) + if result == nil and checkUndeclared in flags: fixSpelling(n, ident, searchInScopes) errorUndeclaredIdentifier(c, n.info, ident.s) result = errorSym(c, n) - elif checkAmbiguity in flags and result != nil and result.id in c.ambiguousSymbols: + elif checkAmbiguity in flags and result != nil and amb: errorUseQualifier(c, n.info, result) + c.isAmbiguous = amb of nkSym: result = n.sym - if checkAmbiguity in flags and contains(c.ambiguousSymbols, result.id): - errorUseQualifier(c, n.info, n.sym) of nkDotExpr: result = nil - var m = qualifiedLookUp(c, n[0], (flags*{checkUndeclared})+{checkModule}) + var m = qualifiedLookUp(c, n[0], (flags * {checkUndeclared}) + {checkModule}) if m != nil and m.kind == skModule: var ident: PIdent = nil if n[1].kind == nkIdent: @@ -379,18 +493,29 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym = if result != nil and result.kind == skStub: loadStub(result) proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = + o.importIdx = -1 + o.marked = initIntSet() case n.kind of nkIdent, nkAccQuoted: var ident = considerQuotedIdent(c, n) - o.scope = c.currentScope + var scope = c.currentScope o.mode = oimNoQualifier while true: - result = initIdentIter(o.it, o.scope.symbols, ident).skipAlias(n, c.config) + result = initIdentIter(o.it, scope.symbols, ident).skipAlias(n, c.config) if result != nil: + o.currentScope = scope break else: - o.scope = o.scope.parent - if o.scope == nil: break + scope = scope.parent + if scope == nil: + for i in 0..c.imports.high: + result = initIdentIter(o.it, o.marked, c.imports[i], ident).skipAlias(n, c.config) + if result != nil: + o.currentScope = nil + o.importIdx = i + return result + return nil + of nkSym: result = n.sym o.mode = oimDone @@ -422,31 +547,69 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = o.mode = oimDone return nil o.symChoiceIndex = 1 - o.inSymChoice = initIntSet() - incl(o.inSymChoice, result.id) + o.marked = initIntSet() + incl(o.marked, result.id) else: discard when false: if result != nil and result.kind == skStub: loadStub(result) proc lastOverloadScope*(o: TOverloadIter): int = case o.mode - of oimNoQualifier: result = if o.scope.isNil: -1 else: o.scope.depthLevel + of oimNoQualifier: + result = if o.importIdx >= 0: 0 + elif o.currentScope.isNil: -1 + else: o.currentScope.depthLevel of oimSelfModule: result = 1 of oimOtherModule: result = 0 else: result = -1 +proc nextOverloadIterImports(o: var TOverloadIter, c: PContext, n: PNode): PSym = + assert o.currentScope == nil + var idx = o.importIdx+1 + o.importIdx = c.imports.len # assume the other imported modules lack this symbol too + while idx < c.imports.len: + result = initIdentIter(o.it, o.marked, c.imports[idx], o.it.name).skipAlias(n, c.config) + if result != nil: + # oh, we were wrong, some other module had the symbol, so remember that: + o.importIdx = idx + break + inc idx + +proc symChoiceExtension(o: var TOverloadIter; c: PContext; n: PNode): PSym = + assert o.currentScope == nil + while o.importIdx < c.imports.len: + result = initIdentIter(o.it, o.marked, c.imports[o.importIdx], o.it.name).skipAlias(n, c.config) + #while result != nil and result.id in o.marked: + # result = nextIdentIter(o.it, o.marked, c.imports[o.importIdx]) + if result != nil: + #assert result.id notin o.marked + return result + inc o.importIdx + proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = case o.mode of oimDone: result = nil of oimNoQualifier: - if o.scope != nil: - result = nextIdentIter(o.it, o.scope.symbols).skipAlias(n, c.config) + if o.currentScope != nil: + assert o.importIdx < 0 + result = nextIdentIter(o.it, o.currentScope.symbols).skipAlias(n, c.config) while result == nil: - o.scope = o.scope.parent - if o.scope == nil: break - result = initIdentIter(o.it, o.scope.symbols, o.it.name).skipAlias(n, c.config) - # BUGFIX: o.it.name <-> n.ident + o.currentScope = o.currentScope.parent + if o.currentScope != nil: + result = initIdentIter(o.it, o.currentScope.symbols, o.it.name).skipAlias(n, c.config) + # BUGFIX: o.it.name <-> n.ident + else: + o.importIdx = 0 + if c.imports.len > 0: + result = initIdentIter(o.it, o.marked, c.imports[o.importIdx], o.it.name).skipAlias(n, c.config) + if result == nil: + result = nextOverloadIterImports(o, c, n) + break + elif o.importIdx < c.imports.len: + result = nextIdentIter(o.it, o.marked, c.imports[o.importIdx]).skipAlias(n, c.config) + if result == nil: + result = nextOverloadIterImports(o, c, n) else: result = nil of oimSelfModule: @@ -456,26 +619,48 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = of oimSymChoice: if o.symChoiceIndex < n.len: result = n[o.symChoiceIndex].sym - incl(o.inSymChoice, result.id) + incl(o.marked, result.id) inc o.symChoiceIndex elif n.kind == nkOpenSymChoice: # try 'local' symbols too for Koenig's lookup: o.mode = oimSymChoiceLocalLookup - o.scope = c.currentScope - result = firstIdentExcluding(o.it, o.scope.symbols, - n[0].sym.name, o.inSymChoice).skipAlias(n, c.config) + o.currentScope = c.currentScope + result = firstIdentExcluding(o.it, o.currentScope.symbols, + n[0].sym.name, o.marked).skipAlias(n, c.config) while result == nil: - o.scope = o.scope.parent - if o.scope == nil: break - result = firstIdentExcluding(o.it, o.scope.symbols, - n[0].sym.name, o.inSymChoice).skipAlias(n, c.config) + o.currentScope = o.currentScope.parent + if o.currentScope != nil: + result = firstIdentExcluding(o.it, o.currentScope.symbols, + n[0].sym.name, o.marked).skipAlias(n, c.config) + else: + o.importIdx = 0 + result = symChoiceExtension(o, c, n) + break + if result != nil: + incl o.marked, result.id of oimSymChoiceLocalLookup: - result = nextIdentExcluding(o.it, o.scope.symbols, o.inSymChoice).skipAlias(n, c.config) - while result == nil: - o.scope = o.scope.parent - if o.scope == nil: break - result = firstIdentExcluding(o.it, o.scope.symbols, - n[0].sym.name, o.inSymChoice).skipAlias(n, c.config) + if o.currentScope != nil: + result = nextIdentExcluding(o.it, o.currentScope.symbols, o.marked).skipAlias(n, c.config) + while result == nil: + o.currentScope = o.currentScope.parent + if o.currentScope != nil: + result = firstIdentExcluding(o.it, o.currentScope.symbols, + n[0].sym.name, o.marked).skipAlias(n, c.config) + else: + o.importIdx = 0 + result = symChoiceExtension(o, c, n) + break + if result != nil: + incl o.marked, result.id + + elif o.importIdx < c.imports.len: + result = nextIdentIter(o.it, o.marked, c.imports[o.importIdx]).skipAlias(n, c.config) + #assert result.id notin o.marked + #while result != nil and result.id in o.marked: + # result = nextIdentIter(o.it, c.imports[o.importIdx]).skipAlias(n, c.config) + if result == nil: + inc o.importIdx + result = symChoiceExtension(o, c, n) when false: if result != nil and result.kind == skStub: loadStub(result) diff --git a/compiler/main.nim b/compiler/main.nim index 6bdbb9efd..73676ffd5 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -31,9 +31,9 @@ proc semanticPasses(g: ModuleGraph) = proc writeDepsFile(g: ModuleGraph) = let fname = g.config.nimcacheDir / RelativeFile(g.config.projectName & ".deps") let f = open(fname.string, fmWrite) - for m in g.modules: - if m != nil: - f.writeLine(toFullPath(g.config, m.position.FileIndex)) + for m in g.ifaces: + if m.module != nil: + f.writeLine(toFullPath(g.config, m.module.position.FileIndex)) for k in g.inclToMod.keys: if g.getModule(k).isNil: # don't repeat includes which are also modules f.writeLine(toFullPath(g.config, k)) diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index 9e7a487cf..abbefa9b6 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -28,11 +28,20 @@ import ast, intsets, tables, options, lineinfos, hashes, idents, incremental, btrees, md5 +import ic / packed_ast + type SigHash* = distinct MD5Digest + Iface* = object ## data we don't want to store directly in the + ## ast.PSym type for s.kind == skModule + module*: PSym ## module this "Iface" belongs to + converters*: seq[PSym] + patterns*: seq[PSym] + pureEnums*: seq[PSym] + ModuleGraph* = ref object - modules*: seq[PSym] ## indexed by int32 fileIdx + ifaces*: seq[Iface] ## indexed by int32 fileIdx packageSyms*: TStrTable deps*: IntSet # the dependency graph or potentially its transitive closure. importDeps*: Table[FileIndex, seq[FileIndex]] # explicit import module dependencies @@ -171,13 +180,21 @@ proc createMagic*(g: ModuleGraph; name: string, m: TMagic): PSym = result.magic = m result.flags = {sfNeverRaises} +proc registerModule*(g: ModuleGraph; m: PSym) = + assert m != nil + assert m.kind == skModule + + if m.position >= g.ifaces.len: + setLen(g.ifaces, m.position + 1) + g.ifaces[m.position] = Iface(module: m, converters: @[], patterns: @[]) + proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph = result = ModuleGraph() result.idgen = IdGenerator(module: -1'i32, item: 0'i32) initStrTable(result.packageSyms) result.deps = initIntSet() result.importDeps = initTable[FileIndex, seq[FileIndex]]() - result.modules = @[] + result.ifaces = @[] result.importStack = @[] result.inclToMod = initTable[FileIndex, FileIndex]() result.config = config @@ -201,7 +218,7 @@ proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph = proc resetAllModules*(g: ModuleGraph) = initStrTable(g.packageSyms) g.deps = initIntSet() - g.modules = @[] + g.ifaces = @[] g.importStack = @[] g.inclToMod = initTable[FileIndex, FileIndex]() g.usageSym = nil @@ -211,8 +228,8 @@ proc resetAllModules*(g: ModuleGraph) = initStrTable(g.exposed) proc getModule*(g: ModuleGraph; fileIdx: FileIndex): PSym = - if fileIdx.int32 >= 0 and fileIdx.int32 < g.modules.len: - result = g.modules[fileIdx.int32] + if fileIdx.int32 >= 0 and fileIdx.int32 < g.ifaces.len: + result = g.ifaces[fileIdx.int32].module proc dependsOn(a, b: int): int {.inline.} = (a shl 15) + b @@ -233,7 +250,7 @@ proc parentModule*(g: ModuleGraph; fileIdx: FileIndex): FileIndex = ## returns 'fileIdx' if the file belonging to this index is ## directly used as a module or else the module that first ## references this include file. - if fileIdx.int32 >= 0 and fileIdx.int32 < g.modules.len and g.modules[fileIdx.int32] != nil: + if fileIdx.int32 >= 0 and fileIdx.int32 < g.ifaces.len and g.ifaces[fileIdx.int32].module != nil: result = fileIdx else: result = g.inclToMod.getOrDefault(fileIdx) @@ -257,11 +274,11 @@ proc markClientsDirty*(g: ModuleGraph; fileIdx: FileIndex) = # cleared but D still needs to be remembered as 'dirty'. if g.invalidTransitiveClosure: g.invalidTransitiveClosure = false - transitiveClosure(g.deps, g.modules.len) + transitiveClosure(g.deps, g.ifaces.len) # every module that *depends* on this file is also dirty: - for i in 0i32..= graph.modules.len: - setLen(graph.modules, int(fileIdx) + 1) - graph.modules[result.position] = result + graph.registerModule(result) initStrTable(result.tab) - strTableAdd(result.tab, result) # a module knows itself + when false: + strTableAdd(result.tab, result) # a module knows itself + # This is now implemented via + # c.moduleScope.addSym(module) # a module knows itself + # in sem.nim, around line 527 strTableAdd(packSym.tab, result) proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym = diff --git a/compiler/plugins/locals.nim b/compiler/plugins/locals.nim index efa8b1e94..6b0594197 100644 --- a/compiler/plugins/locals.nim +++ b/compiler/plugins/locals.nim @@ -19,8 +19,7 @@ proc semLocals*(c: PContext, n: PNode): PNode = tupleType.n = newNodeI(nkRecList, n.info) let owner = getCurrOwner(c) # for now we skip openarrays ... - for scope in walkScopes(c.currentScope): - if scope == c.topLevelScope: break + for scope in localScopesFrom(c, c.currentScope): for it in items(scope.symbols): if it.kind in skLocalVars and it.typ.skipTypes({tyGenericInst, tyVar}).kind notin diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 5ee3dc414..dca8a94e9 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -559,7 +559,9 @@ proc semAsmOrEmit*(con: PContext, n: PNode, marker: char): PNode = if c < 0: sub = substr(str, b + 1) else: sub = substr(str, b + 1, c - 1) if sub != "": - var e = searchInScopes(con, getIdent(con.cache, sub)) + var amb = false + var e = searchInScopes(con, getIdent(con.cache, sub), amb) + # XXX what to do here if 'amb' is true? if e != nil: when false: if e.kind == skStub: loadStub(e) diff --git a/compiler/rod.nim b/compiler/rod.nim index 13a4a53c9..711f5b2cb 100644 --- a/compiler/rod.nim +++ b/compiler/rod.nim @@ -22,7 +22,7 @@ when not nimIncremental: template storeRemaining*(g: ModuleGraph; module: PSym) = discard - template registerModule*(g: ModuleGraph; module: PSym) = discard + #template registerModule*(g: ModuleGraph; module: PSym) = discard else: include rodimpl diff --git a/compiler/sem.nim b/compiler/sem.nim index 0fec8b7e3..e95f3799c 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -522,8 +522,10 @@ proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext pushProcCon(c, module) pushOwner(c, c.module) - c.importTable = openScope(c) - c.importTable.addSym(module) # a module knows itself + + c.moduleScope = openScope(c) + c.moduleScope.addSym(module) # a module knows itself + if sfSystemModule in module.flags: graph.systemModule = module c.topLevelScope = openScope(c) @@ -557,7 +559,7 @@ proc isEmptyTree(n: PNode): bool = proc semStmtAndGenerateGenerics(c: PContext, n: PNode): PNode = if c.topStmts == 0 and not isImportSystemStmt(c.graph, n): if sfSystemModule notin c.module.flags and not isEmptyTree(n): - c.importTable.addSym c.graph.systemModule # import the "System" identifier + c.moduleScope.addSym c.graph.systemModule # import the "System" identifier importAllSymbols(c, c.graph.systemModule) inc c.topStmts else: diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 8d1c25e4d..a6660e14c 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -70,13 +70,25 @@ type TExprFlags* = set[TExprFlag] + ImportMode* = enum + importAll, importSet, importExcept + ImportedModule* = object + m*: PSym + case mode*: ImportMode + of importAll: discard + of importSet: + imported*: IntSet + of importExcept: + exceptSet*: IntSet + PContext* = ref TContext TContext* = object of TPassContext # a context represents the module # that is currently being compiled enforceVoidContext*: PType module*: PSym # the module sym belonging to the context currentScope*: PScope # current scope - importTable*: PScope # scope for all imported symbols + moduleScope*: PScope # scope for modules + imports*: seq[ImportedModule] # scope for all imported symbols topLevelScope*: PScope # scope for all top-level symbols p*: PProcCon # procedure context matchedConcept*: ptr TMatchedConcept # the current concept being matched @@ -85,9 +97,6 @@ type # can access private object fields instCounter*: int # to prevent endless instantiations templInstCounter*: ref int # gives every template instantiation a unique id - - ambiguousSymbols*: IntSet # ids of all ambiguous symbols (cannot - # store this info in the syms themselves!) inGenericContext*: int # > 0 if we are in a generic type inStaticContext*: int # > 0 if we are inside a static: block inUnrolledContext*: int # > 0 if we are unrolling a loop @@ -134,6 +143,7 @@ type signatures*: TStrTable recursiveDep*: string suggestionsMade*: bool + isAmbiguous*: bool # little hack features*: set[Feature] inTypeContext*: int typesWithOps*: seq[(PType, PType)] #\ @@ -238,7 +248,6 @@ proc popOptionEntry*(c: PContext) = proc newContext*(graph: ModuleGraph; module: PSym): PContext = new(result) result.enforceVoidContext = PType(kind: tyTyped) - result.ambiguousSymbols = initIntSet() result.optionStack = @[newOptionEntry(graph.config)] result.libs = @[] result.module = module @@ -263,9 +272,14 @@ proc inclSym(sq: var seq[PSym], s: PSym) = proc addConverter*(c: PContext, conv: PSym) = inclSym(c.converters, conv) + inclSym(c.graph.ifaces[c.module.position].converters, conv) + +proc addPureEnum*(c: PContext, e: PSym) = + inclSym(c.graph.ifaces[c.module.position].pureEnums, e) proc addPattern*(c: PContext, p: PSym) = inclSym(c.patterns, p) + inclSym(c.graph.ifaces[c.module.position].patterns, p) proc newLib*(kind: TLibKind): PLib = new(result) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 3bbf353b1..bcb48515f 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -495,7 +495,8 @@ proc semOpAux(c: PContext, n: PNode) = proc overloadedCallOpr(c: PContext, n: PNode): PNode = # quick check if there is *any* () operator overloaded: var par = getIdent(c.cache, "()") - if searchInScopes(c, par) == nil: + var amb = false + if searchInScopes(c, par, amb) == nil: result = nil else: result = newNodeI(nkCall, n.info) @@ -653,7 +654,8 @@ proc hasUnresolvedArgs(c: PContext, n: PNode): bool = return isUnresolvedSym(n.sym) of nkIdent, nkAccQuoted: let ident = considerQuotedIdent(c, n) - let sym = searchInScopes(c, ident) + var amb = false + let sym = searchInScopes(c, ident, amb) if sym != nil: return isUnresolvedSym(sym) else: @@ -1880,10 +1882,12 @@ proc semDefined(c: PContext, n: PNode): PNode = proc lookUpForDeclared(c: PContext, n: PNode, onlyCurrentScope: bool): PSym = case n.kind of nkIdent, nkAccQuoted: + var amb = false + let ident = considerQuotedIdent(c, n) result = if onlyCurrentScope: - localSearchInScope(c, considerQuotedIdent(c, n)) + localSearchInScope(c, ident) else: - searchInScopes(c, considerQuotedIdent(c, n)) + searchInScopes(c, ident, amb) of nkDotExpr: result = nil if onlyCurrentScope: return @@ -2531,6 +2535,11 @@ proc semExportExcept(c: PContext, n: PNode): PNode = markUsed(c, n.info, exported) proc semExport(c: PContext, n: PNode): PNode = + proc specialSyms(c: PContext; s: PSym) {.inline.} = + if s.kind == skConverter: addConverter(c, s) + elif s.kind == skType and s.typ != nil and s.typ.kind == tyEnum and sfPure in s.flags: + addPureEnum(c, s) + result = newNodeI(nkExportStmt, n.info) for i in 0.. 0: + let s = candidates[0] # XXX take into account the other candidates! isMacro = s.kind in {skTemplate, skMacro} if withinBind in flags or s.id in ctx.toBind: result = newDot(result, symChoice(c, n, s, scClosed)) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index d3d8c0fb8..b4026f3d7 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -298,8 +298,7 @@ proc fitRemoveHiddenConv(c: PContext, typ: PType, n: PNode): PNode = changeType(c, result, typ, check=false) proc findShadowedVar(c: PContext, v: PSym): PSym = - for scope in walkScopes(c.currentScope.parent): - if scope == c.topLevelScope: break + for scope in localScopesFrom(c, c.currentScope.parent): let shadowed = strTableGet(scope.symbols, v.name) if shadowed != nil and shadowed.kind in skLocalVars: return shadowed @@ -451,7 +450,9 @@ proc semLowerLetVarCustomPragma(c: PContext, a: PNode, n: PNode): PNode = n.kind == nkConstSection and w in constPragmas: return nil - let sym = searchInScopes(c, ident) + var amb = false + let sym = searchInScopes(c, ident, amb) + # XXX what if amb is true? if sym == nil or sfCustomPragma in sym.flags: return nil # skip if not in scope; skip `template myAttr() {.pragma.}` let lhs = b[0] @@ -1476,7 +1477,8 @@ proc semProcAnnotation(c: PContext, prc: PNode; if strTableGet(c.userPragmas, ident) != nil: continue # User defined pragma else: - let sym = searchInScopes(c, ident) + var amb = false + let sym = searchInScopes(c, ident, amb) if sym != nil and sfCustomPragma in sym.flags: continue # User custom pragma diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index a9d00d2ee..827087c3d 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -146,10 +146,12 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = onDef(e.info, e) if sfGenSym notin e.flags: if not isPure: addDecl(c, e) - else: importPureEnumField(c, e) + else: declarePureEnumField(c, e) if isPure and (let conflict = strTableInclReportConflict(symbols, e); conflict != nil): wrongRedefinition(c, e.info, e.name.s, conflict.info) inc(counter) + if isPure and sfExported in result.sym.flags: + addPureEnum(c, result.sym) if tfNotNil in e.typ.flags and not hasNull: result.flags.incl tfRequiresInit @@ -1593,7 +1595,9 @@ proc applyTypeSectionPragmas(c: PContext; pragmas, operand: PNode): PNode = if strTableGet(c.userPragmas, ident) != nil: discard "User-defined pragma" else: - let sym = searchInScopes(c, ident) + var amb = false + let sym = searchInScopes(c, ident, amb) + # XXX: What to do here if amb is true? if sym != nil and sfCustomPragma in sym.flags: discard "Custom user pragma" else: diff --git a/compiler/suggest.nim b/compiler/suggest.nim index be337af12..186b23cd9 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -271,17 +271,12 @@ proc getQuality(s: PSym): range[0..100] = return 100 template wholeSymTab(cond, section: untyped) {.dirty.} = - var isLocal = true - var scopeN = 0 - for scope in walkScopes(c.currentScope): - if scope == c.topLevelScope: isLocal = false - dec scopeN - for item in scope.symbols: - let it = item - var pm: PrefixMatch - if cond: - outputs.add(symToSuggest(c.config, it, isLocal = isLocal, section, info, getQuality(it), - pm, c.inTypeContext > 0, scopeN)) + for (item, scopeN, isLocal) in allSyms(c): + let it = item + var pm: PrefixMatch + if cond: + outputs.add(symToSuggest(c.config, it, isLocal = isLocal, section, info, getQuality(it), + pm, c.inTypeContext > 0, scopeN)) proc suggestSymList(c: PContext, list, f: PNode; info: TLineInfo, outputs: var Suggestions) = for i in 0.. 0, scopeN)) - #if scope == c.topLevelScope and f.isNil: break + for (it, scopeN, isLocal) in allSyms(c): + var pm: PrefixMatch + if filterSym(it, f, pm): + outputs.add(symToSuggest(c.config, it, isLocal = isLocal, ideSug, n.info, 0, pm, + c.inTypeContext > 0, scopeN)) proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions) = # special code that deals with ``myObj.``. `n` is NOT the nkDotExpr-node, but @@ -656,17 +645,12 @@ proc suggestSentinel*(c: PContext) = inc(c.compilesContextId) var outputs: Suggestions = @[] # suggest everything: - var isLocal = true - var scopeN = 0 - for scope in walkScopes(c.currentScope): - if scope == c.topLevelScope: isLocal = false - dec scopeN - for it in items(scope.symbols): - var pm: PrefixMatch - if filterSymNoOpr(it, nil, pm): - outputs.add(symToSuggest(c.config, it, isLocal = isLocal, ideSug, - newLineInfo(c.config.m.trackPos.fileIndex, 0, -1), 0, - PrefixMatch.None, false, scopeN)) + for (it, scopeN, isLocal) in allSyms(c): + var pm: PrefixMatch + if filterSymNoOpr(it, nil, pm): + outputs.add(symToSuggest(c.config, it, isLocal = isLocal, ideSug, + newLineInfo(c.config.m.trackPos.fileIndex, 0, -1), 0, + PrefixMatch.None, false, scopeN)) dec(c.compilesContextId) produceOutput(outputs, c.config) diff --git a/lib/system.nim b/lib/system.nim index 57b46a937..afd8e467d 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -627,7 +627,7 @@ proc newSeq*[T](s: var seq[T], len: Natural) {.magic: "NewSeq", noSideEffect.} ## the sequence instead of adding them. Example: ## ## .. code-block:: Nim - ## var inputStrings : seq[string] + ## var inputStrings: seq[string] ## newSeq(inputStrings, 3) ## assert len(inputStrings) == 3 ## inputStrings[0] = "The fourth" diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 2da10e749..aae560939 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -76,7 +76,9 @@ pkg2 "nigui", "nim c -o:niguii -r src/nigui.nim" pkg2 "NimData", "nim c -o:nimdataa src/nimdata.nim" pkg2 "nimes", "nim c src/nimes.nim" pkg2 "nimfp", "nim c -o:nfp -r src/fp.nim" -pkg2 "nimgame2", "nim c nimgame2/nimgame.nim" +when false: + pkg2 "nimgame2", "nim c nimgame2/nimgame.nim" + # XXX Doesn't work with deprecated 'randomize', will create a PR. pkg2 "nimgen", "nim c -o:nimgenn -r src/nimgen/runcfg.nim" pkg2 "nimlsp" pkg2 "nimly", "nim c -r tests/test_readme_example.nim" diff --git a/tests/modules/mforwarded_pure_enum.nim b/tests/modules/mforwarded_pure_enum.nim new file mode 100644 index 000000000..3f03390a5 --- /dev/null +++ b/tests/modules/mforwarded_pure_enum.nim @@ -0,0 +1,3 @@ + +import mforwarded_pure_enum2 +export mforwarded_pure_enum2.PureEnum diff --git a/tests/modules/mforwarded_pure_enum2.nim b/tests/modules/mforwarded_pure_enum2.nim new file mode 100644 index 000000000..e5d5d2a71 --- /dev/null +++ b/tests/modules/mforwarded_pure_enum2.nim @@ -0,0 +1,4 @@ + +type + PureEnum* {.pure.} = enum + x, y, z diff --git a/tests/modules/tfowarded_pure_enum.nim b/tests/modules/tfowarded_pure_enum.nim new file mode 100644 index 000000000..1d2c4f342 --- /dev/null +++ b/tests/modules/tfowarded_pure_enum.nim @@ -0,0 +1,7 @@ +discard """ + output: '''z''' +""" + +import mforwarded_pure_enum as t2 + +echo z From b87bcb6d9245dbb700abb420fe1c8a84a844adbf Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 17 Dec 2020 13:35:02 +0100 Subject: [PATCH 1181/3768] fixes #16359 [backport] (#16377) --- lib/pure/includes/oserr.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/includes/oserr.nim b/lib/pure/includes/oserr.nim index 138aa127d..673422973 100644 --- a/lib/pure/includes/oserr.nim +++ b/lib/pure/includes/oserr.nim @@ -114,7 +114,7 @@ proc osLastError*(): OSErrorCode {.sideEffect.} = when defined(nimscript): discard elif defined(windows): - result = OSErrorCode(getLastError()) + result = cast[OSErrorCode](getLastError()) else: result = OSErrorCode(errno) {.pop.} From 8cd3655deeb369cac325b3ab2a466c110ce2bb27 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Thu, 17 Dec 2020 06:37:32 -0600 Subject: [PATCH 1182/3768] make the docs of strutils a bit better (#16368) --- lib/pure/strutils.nim | 303 +++++++++++++++++++++--------------------- 1 file changed, 150 insertions(+), 153 deletions(-) diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index c937cd259..04ba71315 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -9,46 +9,43 @@ ## The system module defines several common functions for working with strings, ## such as: -## * ``$`` for converting other data-types to strings -## * ``&`` for string concatenation -## * ``add`` for adding a new character or a string to the existing one -## * ``in`` (alias for ``contains``) and ``notin`` for checking if a character +## * `$` for converting other data-types to strings +## * `&` for string concatenation +## * `add` for adding a new character or a string to the existing one +## * `in` (alias for `contains`) and `notin` for checking if a character ## is in a string ## ## This module builds upon that, providing additional functionality in form of ## procedures, iterators and templates for strings. -## -## .. code-block:: -## import strutils -## -## let -## numbers = @[867, 5309] -## multiLineString = "first line\nsecond line\nthird line" -## -## let jenny = numbers.join("-") -## assert jenny == "867-5309" -## -## assert splitLines(multiLineString) == -## @["first line", "second line", "third line"] -## assert split(multiLineString) == @["first", "line", "second", -## "line", "third", "line"] -## assert indent(multiLineString, 4) == -## " first line\n second line\n third line" -## assert 'z'.repeat(5) == "zzzzz" -## + +runnableExamples: + let + numbers = @[867, 5309] + multiLineString = "first line\nsecond line\nthird line" + + let jenny = numbers.join("-") + assert jenny == "867-5309" + + assert splitLines(multiLineString) == + @["first line", "second line", "third line"] + assert split(multiLineString) == @["first", "line", "second", + "line", "third", "line"] + assert indent(multiLineString, 4) == + " first line\n second line\n third line" + assert 'z'.repeat(5) == "zzzzz" + ## The chaining of functions is possible thanks to the ## `method call syntax`_: -## -## .. code-block:: -## import strutils -## from sequtils import map -## -## let jenny = "867-5309" -## assert jenny.split('-').map(parseInt) == @[867, 5309] -## -## assert "Beetlejuice".indent(1).repeat(3).strip == -## "Beetlejuice Beetlejuice Beetlejuice" -## + +runnableExamples: + from sequtils import map + + let jenny = "867-5309" + assert jenny.split('-').map(parseInt) == @[867, 5309] + + assert "Beetlejuice".indent(1).repeat(3).strip == + "Beetlejuice Beetlejuice Beetlejuice" + ## This module is available for the `JavaScript target ## `_. ## @@ -69,7 +66,7 @@ ## * `ropes module`_ for rope data type, which can represent very ## long strings efficiently ## * `re module`_ for regular expression (regex) support -## * `strscans`_ for ``scanf`` and ``scanp`` macros, which offer +## * `strscans`_ for `scanf` and `scanp` macros, which offer ## easier substring extraction than regular expressions @@ -88,26 +85,26 @@ import std/private/since const Whitespace* = {' ', '\t', '\v', '\r', '\l', '\f'} ## All the characters that count as whitespace (space, tab, vertical tab, - ## carriage return, new line, form feed) + ## carriage return, new line, form feed). Letters* = {'A'..'Z', 'a'..'z'} - ## the set of letters + ## The set of letters. Digits* = {'0'..'9'} - ## the set of digits + ## The set of digits. HexDigits* = {'0'..'9', 'A'..'F', 'a'..'f'} - ## the set of hexadecimal digits + ## The set of hexadecimal digits. IdentChars* = {'a'..'z', 'A'..'Z', '0'..'9', '_'} - ## the set of characters an identifier can consist of + ## The set of characters an identifier can consist of. IdentStartChars* = {'a'..'z', 'A'..'Z', '_'} - ## the set of characters an identifier can start with + ## The set of characters an identifier can start with. Newlines* = {'\13', '\10'} - ## the set of characters a newline terminator can start with (carriage - ## return, line feed) + ## The set of characters a newline terminator can start with (carriage + ## return, line feed). AllChars* = {'\x00'..'\xFF'} ## A set with all the possible characters. @@ -189,9 +186,9 @@ func isUpperAscii*(c: char): bool {.rtl, extern: "nsuIsUpperAsciiChar".} = func toLowerAscii*(c: char): char {.rtl, extern: "nsuToLowerAsciiChar".} = - ## Returns the lower case version of character ``c``. + ## Returns the lower case version of character `c`. ## - ## This works only for the letters ``A-Z``. See `unicode.toLower + ## This works only for the letters `A-Z`. See `unicode.toLower ## `_ for a version that works for any Unicode ## character. ## @@ -214,7 +211,7 @@ template toImpl(call) = func toLowerAscii*(s: string): string {.rtl, extern: "nsuToLowerAsciiStr".} = ## Converts string `s` into lower case. ## - ## This works only for the letters ``A-Z``. See `unicode.toLower + ## This works only for the letters `A-Z`. See `unicode.toLower ## `_ for a version that works for any Unicode ## character. ## @@ -227,7 +224,7 @@ func toLowerAscii*(s: string): string {.rtl, extern: "nsuToLowerAsciiStr".} = func toUpperAscii*(c: char): char {.rtl, extern: "nsuToUpperAsciiChar".} = ## Converts character `c` into upper case. ## - ## This works only for the letters ``A-Z``. See `unicode.toUpper + ## This works only for the letters `A-Z`. See `unicode.toUpper ## `_ for a version that works for any Unicode ## character. ## @@ -246,7 +243,7 @@ func toUpperAscii*(c: char): char {.rtl, extern: "nsuToUpperAsciiChar".} = func toUpperAscii*(s: string): string {.rtl, extern: "nsuToUpperAsciiStr".} = ## Converts string `s` into upper case. ## - ## This works only for the letters ``A-Z``. See `unicode.toUpper + ## This works only for the letters `A-Z`. See `unicode.toUpper ## `_ for a version that works for any Unicode ## character. ## @@ -259,7 +256,7 @@ func toUpperAscii*(s: string): string {.rtl, extern: "nsuToUpperAsciiStr".} = func capitalizeAscii*(s: string): string {.rtl, extern: "nsuCapitalizeAscii".} = ## Converts the first character of string `s` into upper case. ## - ## This works only for the letters ``A-Z``. + ## This works only for the letters `A-Z`. ## Use `Unicode module`_ for UTF-8 support. ## ## See also: @@ -334,7 +331,7 @@ func cmpIgnoreCase*(a, b: string): int {.rtl, extern: "nsuCmpIgnoreCase".} = # thus we compile without checks here func cmpIgnoreStyle*(a, b: string): int {.rtl, extern: "nsuCmpIgnoreStyle".} = - ## Semantically the same as ``cmp(normalize(a), normalize(b))``. It + ## Semantically the same as `cmp(normalize(a), normalize(b))`. It ## is just optimized to not allocate temporary strings. This should ## NOT be used to compare Nim identifier names. ## Use `macros.eqIdent`_ for that. @@ -629,8 +626,8 @@ iterator splitLines*(s: string, keepEol = false): string = ## ## Every `character literal `_ ## newline combination (CR, LF, CR-LF) is supported. The result strings - ## contain no trailing end of line characters unless parameter ``keepEol`` - ## is set to ``true``. + ## contain no trailing end of line characters unless parameter `keepEol` + ## is set to `true`. ## ## Example: ## @@ -674,9 +671,9 @@ iterator splitLines*(s: string, keepEol = false): string = first = last iterator splitWhitespace*(s: string, maxsplit: int = -1): string = - ## Splits the string ``s`` at whitespace stripping leading and trailing - ## whitespace if necessary. If ``maxsplit`` is specified and is positive, - ## no more than ``maxsplit`` splits is made. + ## Splits the string `s` at whitespace stripping leading and trailing + ## whitespace if necessary. If `maxsplit` is specified and is positive, + ## no more than `maxsplit` splits is made. ## ## The following code: ## @@ -881,7 +878,7 @@ func splitWhitespace*(s: string, maxsplit: int = -1): seq[string] {.rtl, func toBin*(x: BiggestInt, len: Positive): string {.rtl, extern: "nsuToBin".} = ## Converts `x` into its binary representation. ## - ## The resulting string is always `len` characters long. No leading ``0b`` + ## The resulting string is always `len` characters long. No leading `0b` ## prefix is generated. runnableExamples: let @@ -903,7 +900,7 @@ func toBin*(x: BiggestInt, len: Positive): string {.rtl, extern: "nsuToBin".} = func toOct*(x: BiggestInt, len: Positive): string {.rtl, extern: "nsuToOct".} = ## Converts `x` into its octal representation. ## - ## The resulting string is always `len` characters long. No leading ``0o`` + ## The resulting string is always `len` characters long. No leading `0o` ## prefix is generated. ## ## Do not confuse it with `toOctal func<#toOctal,char>`_. @@ -939,7 +936,7 @@ func toHex*[T: SomeInteger](x: T, len: Positive): string = ## Converts `x` to its hexadecimal representation. ## ## The resulting string will be exactly `len` characters long. No prefix like - ## ``0x`` is generated. `x` is treated as an unsigned value. + ## `0x` is generated. `x` is treated as an unsigned value. runnableExamples: let a = 62'u64 @@ -952,7 +949,7 @@ func toHex*[T: SomeInteger](x: T, len: Positive): string = toHexImpl(cast[BiggestUInt](x), len, x < 0) func toHex*[T: SomeInteger](x: T): string = - ## Shortcut for ``toHex(x, T.sizeof * 2)`` + ## Shortcut for `toHex(x, T.sizeof * 2)` runnableExamples: doAssert toHex(1984'i64) == "00000000000007C0" doAssert toHex(1984'i16) == "07C0" @@ -962,7 +959,7 @@ func toHex*(s: string): string {.rtl.} = ## Converts a bytes string to its hexadecimal representation. ## ## The output is twice the input long. No prefix like - ## ``0x`` is generated. + ## `0x` is generated. ## ## See also: ## * `parseHexStr func<#parseHexStr,string>`_ for the reverse operation @@ -1130,7 +1127,7 @@ func parseFloat*(s: string): float {.rtl, extern: "nsuParseFloat".} = ## Parses a decimal floating point value contained in `s`. ## ## If `s` is not a valid floating point number, `ValueError` is raised. - ##``NAN``, ``INF``, ``-INF`` are also supported (case insensitive comparison). + ##`NAN`, `INF`, `-INF` are also supported (case insensitive comparison). runnableExamples: doAssert parseFloat("3.14") == 3.14 doAssert parseFloat("inf") == 1.0/0 @@ -1143,7 +1140,7 @@ func parseBinInt*(s: string): int {.rtl, extern: "nsuParseBinInt".} = ## Parses a binary integer value contained in `s`. ## ## If `s` is not a valid binary integer, `ValueError` is raised. `s` can have - ## one of the following optional prefixes: ``0b``, ``0B``. Underscores within + ## one of the following optional prefixes: `0b`, `0B`. Underscores within ## `s` are ignored. runnableExamples: let @@ -1161,7 +1158,7 @@ func parseOctInt*(s: string): int {.rtl, extern: "nsuParseOctInt".} = ## Parses an octal integer value contained in `s`. ## ## If `s` is not a valid oct integer, `ValueError` is raised. `s` can have one - ## of the following optional prefixes: ``0o``, ``0O``. Underscores within + ## of the following optional prefixes: `0o`, `0O`. Underscores within ## `s` are ignored. result = 0 let L = parseutils.parseOct(s, result, 0) @@ -1172,7 +1169,7 @@ func parseHexInt*(s: string): int {.rtl, extern: "nsuParseHexInt".} = ## Parses a hexadecimal integer value contained in `s`. ## ## If `s` is not a valid hex integer, `ValueError` is raised. `s` can have one - ## of the following optional prefixes: ``0x``, ``0X``, ``#``. Underscores + ## of the following optional prefixes: `0x`, `0X`, `#`. Underscores ## within `s` are ignored. result = 0 let L = parseutils.parseHex(s, result, 0) @@ -1180,24 +1177,24 @@ func parseHexInt*(s: string): int {.rtl, extern: "nsuParseHexInt".} = raise newException(ValueError, "invalid hex integer: " & s) func generateHexCharToValueMap(): string = - ## Generate a string to map a hex digit to uint value + ## Generates a string to map a hex digit to uint value. result = "" for inp in 0..255: let ch = chr(inp) let o = - case ch: - of '0'..'9': inp - ord('0') - of 'a'..'f': inp - ord('a') + 10 - of 'A'..'F': inp - ord('A') + 10 - else: 17 # indicates an invalid hex char + case ch + of '0'..'9': inp - ord('0') + of 'a'..'f': inp - ord('a') + 10 + of 'A'..'F': inp - ord('A') + 10 + else: 17 # indicates an invalid hex char result.add chr(o) const hexCharToValueMap = generateHexCharToValueMap() func parseHexStr*(s: string): string {.rtl, extern: "nsuParseHexStr".} = - ## Convert hex-encoded string to byte string, e.g.: + ## Converts hex-encoded string to byte string, e.g.: ## - ## Raises ``ValueError`` for an invalid hex values. The comparison is + ## Raises `ValueError` for an invalid hex values. The comparison is ## case-insensitive. ## ## See also: @@ -1228,10 +1225,10 @@ func parseHexStr*(s: string): string {.rtl, extern: "nsuParseHexStr".} = func parseBool*(s: string): bool = ## Parses a value into a `bool`. ## - ## If ``s`` is one of the following values: ``y, yes, true, 1, on``, then - ## returns `true`. If ``s`` is one of the following values: ``n, no, false, - ## 0, off``, then returns `false`. If ``s`` is something else a - ## ``ValueError`` exception is raised. + ## If `s` is one of the following values: `y, yes, true, 1, on`, then + ## returns `true`. If `s` is one of the following values: `n, no, false, + ## 0, off`, then returns `false`. If `s` is something else a + ## `ValueError` exception is raised. runnableExamples: let a = "n" doAssert parseBool(a) == false @@ -1242,10 +1239,10 @@ func parseBool*(s: string): bool = else: raise newException(ValueError, "cannot interpret as a bool: " & s) func parseEnum*[T: enum](s: string): T = - ## Parses an enum ``T``. This errors at compile time, if the given enum + ## Parses an enum `T`. This errors at compile time, if the given enum ## type contains multiple fields with the same string value. ## - ## Raises ``ValueError`` for an invalid value in `s`. The comparison is + ## Raises `ValueError` for an invalid value in `s`. The comparison is ## done in a style insensitive way. runnableExamples: type @@ -1262,7 +1259,7 @@ func parseEnum*[T: enum](s: string): T = genEnumCaseStmt(T, s, default = nil, ord(low(T)), ord(high(T)), nimIdentNormalize) func parseEnum*[T: enum](s: string, default: T): T = - ## Parses an enum ``T``. This errors at compile time, if the given enum + ## Parses an enum `T`. This errors at compile time, if the given enum ## type contains multiple fields with the same string value. ## ## Uses `default` for an invalid value in `s`. The comparison is done in a @@ -1322,7 +1319,7 @@ func align*(s: string, count: Natural, padding = ' '): string {.rtl, ## Aligns a string `s` with `padding`, so that it is of length `count`. ## ## `padding` characters (by default spaces) are added before `s` resulting in - ## right alignment. If ``s.len >= count``, no spaces are added and `s` is + ## right alignment. If `s.len >= count`, no spaces are added and `s` is ## returned unchanged. If you need to left align a string use the `alignLeft ## func<#alignLeft,string,Natural,char>`_. ## @@ -1348,7 +1345,7 @@ func alignLeft*(s: string, count: Natural, padding = ' '): string = ## Left-Aligns a string `s` with `padding`, so that it is of length `count`. ## ## `padding` characters (by default spaces) are added after `s` resulting in - ## left alignment. If ``s.len >= count``, no spaces are added and `s` is + ## left alignment. If `s.len >= count`, no spaces are added and `s` is ## returned unchanged. If you need to right align a string use the `align ## func<#align,string,Natural,char>`_. ## @@ -1408,9 +1405,9 @@ func center*(s: string, width: int, fillChar: char = ' '): string {.rtl, func indent*(s: string, count: Natural, padding: string = " "): string {.rtl, extern: "nsuIndent".} = - ## Indents each line in ``s`` by ``count`` amount of ``padding``. + ## Indents each line in `s` by `count` amount of `padding`. ## - ## **Note:** This does not preserve the new line characters used in ``s``. + ## **Note:** This does not preserve the new line characters used in `s`. ## ## See also: ## * `align func<#align,string,Natural,char>`_ @@ -1433,9 +1430,9 @@ func indent*(s: string, count: Natural, padding: string = " "): string {.rtl, func unindent*(s: string, count: Natural = int.high, padding: string = " "): string {.rtl, extern: "nsuUnindent".} = - ## Unindents each line in ``s`` by ``count`` amount of ``padding``. + ## Unindents each line in `s` by `count` amount of `padding`. ## - ## **Note:** This does not preserve the new line characters used in ``s``. + ## **Note:** This does not preserve the new line characters used in `s`. ## ## See also: ## * `dedent func<#dedent,string,Natural>`_ @@ -1465,7 +1462,7 @@ func unindent*(s: string, count: Natural = int.high, i.inc func indentation*(s: string): Natural {.since: (1, 3).} = - ## Returns the amount of indentation all lines of ``s`` have in common, + ## Returns the amount of indentation all lines of `s` have in common, ## ignoring lines that consist only of whitespace. result = int.high for line in s.splitLines: @@ -1479,13 +1476,13 @@ func indentation*(s: string): Natural {.since: (1, 3).} = func dedent*(s: string, count: Natural = indentation(s)): string {.rtl, extern: "nsuDedent", since: (1, 3).} = - ## Unindents each line in ``s`` by ``count`` amount of ``padding``. + ## Unindents each line in `s` by `count` amount of `padding`. ## The only difference between this and the ## `unindent func<#unindent,string,Natural,string>`_ is that this by default - ## only cuts off the amount of indentation that all lines of ``s`` share as + ## only cuts off the amount of indentation that all lines of `s` share as ## opposed to all indentation. It only supports spaces as padding. ## - ## **Note:** This does not preserve the new line characters used in ``s``. + ## **Note:** This does not preserve the new line characters used in `s`. ## ## See also: ## * `unindent func<#unindent,string,Natural,string>`_ @@ -1503,8 +1500,8 @@ func dedent*(s: string, count: Natural = indentation(s)): string {.rtl, unindent(s, count, " ") func delete*(s: var string, first, last: int) {.rtl, extern: "nsuDelete".} = - ## Deletes in `s` (must be declared as ``var``) the characters at positions - ## ``first ..last`` (both ends included). + ## Deletes in `s` (must be declared as `var`) the characters at positions + ## `first .. last` (both ends included). ## ## This modifies `s` itself, it does not return a copy. runnableExamples: @@ -1530,7 +1527,7 @@ func delete*(s: var string, first, last: int) {.rtl, extern: "nsuDelete".} = func startsWith*(s: string, prefix: char): bool {.inline.} = - ## Returns true if ``s`` starts with character ``prefix``. + ## Returns true if `s` starts with character `prefix`. ## ## See also: ## * `endsWith func<#endsWith,string,char>`_ @@ -1543,9 +1540,9 @@ func startsWith*(s: string, prefix: char): bool {.inline.} = result = s.len > 0 and s[0] == prefix func startsWith*(s, prefix: string): bool {.rtl, extern: "nsuStartsWith".} = - ## Returns true if ``s`` starts with string ``prefix``. + ## Returns true if `s` starts with string `prefix`. ## - ## If ``prefix == ""`` true is returned. + ## If `prefix == ""` true is returned. ## ## See also: ## * `endsWith func<#endsWith,string,string>`_ @@ -1562,7 +1559,7 @@ func startsWith*(s, prefix: string): bool {.rtl, extern: "nsuStartsWith".} = inc(i) func endsWith*(s: string, suffix: char): bool {.inline.} = - ## Returns true if ``s`` ends with ``suffix``. + ## Returns true if `s` ends with `suffix`. ## ## See also: ## * `startsWith func<#startsWith,string,char>`_ @@ -1575,9 +1572,9 @@ func endsWith*(s: string, suffix: char): bool {.inline.} = result = s.len > 0 and s[s.high] == suffix func endsWith*(s, suffix: string): bool {.rtl, extern: "nsuEndsWith".} = - ## Returns true if ``s`` ends with ``suffix``. + ## Returns true if `s` ends with `suffix`. ## - ## If ``suffix == ""`` true is returned. + ## If `suffix == ""` true is returned. ## ## See also: ## * `startsWith func<#startsWith,string,string>`_ @@ -1596,9 +1593,9 @@ func endsWith*(s, suffix: string): bool {.rtl, extern: "nsuEndsWith".} = func continuesWith*(s, substr: string, start: Natural): bool {.rtl, extern: "nsuContinuesWith".} = - ## Returns true if ``s`` continues with ``substr`` at position ``start``. + ## Returns true if `s` continues with `substr` at position `start`. ## - ## If ``substr == ""`` true is returned. + ## If `substr == ""` true is returned. ## ## See also: ## * `startsWith func<#startsWith,string,string>`_ @@ -1757,8 +1754,8 @@ func allCharsInSet*(s: string, theSet: set[char]): bool = return true func abbrev*(s: string, possibilities: openArray[string]): int = - ## Returns the index of the first item in ``possibilities`` which starts - ## with ``s``, if not ambiguous. + ## Returns the index of the first item in `possibilities` which starts + ## with `s`, if not ambiguous. ## ## Returns -1 if no item has been found and -2 if multiple items match. runnableExamples: @@ -1867,12 +1864,12 @@ else: func find*(s: string, sub: char, start: Natural = 0, last = 0): int {.rtl, extern: "nsuFindChar".} = - ## Searches for `sub` in `s` inside range ``start..last`` (both ends included). + ## Searches for `sub` in `s` inside range `start..last` (both ends included). ## If `last` is unspecified, it defaults to `s.high` (the last element). ## ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned. - ## Otherwise the index returned is relative to ``s[0]``, not ``start``. - ## Use `s[start..last].rfind` for a ``start``-origin index. + ## Otherwise the index returned is relative to `s[0]`, not `start`. + ## Use `s[start..last].rfind` for a `start`-origin index. ## ## See also: ## * `rfind func<#rfind,string,char,Natural>`_ @@ -1895,12 +1892,12 @@ func find*(s: string, sub: char, start: Natural = 0, last = 0): int {.rtl, func find*(s: string, chars: set[char], start: Natural = 0, last = 0): int {. rtl, extern: "nsuFindCharSet".} = - ## Searches for `chars` in `s` inside range ``start..last`` (both ends included). + ## Searches for `chars` in `s` inside range `start..last` (both ends included). ## If `last` is unspecified, it defaults to `s.high` (the last element). ## ## If `s` contains none of the characters in `chars`, -1 is returned. - ## Otherwise the index returned is relative to ``s[0]``, not ``start``. - ## Use `s[start..last].find` for a ``start``-origin index. + ## Otherwise the index returned is relative to `s[0]`, not `start`. + ## Use `s[start..last].find` for a `start`-origin index. ## ## See also: ## * `rfind func<#rfind,string,set[char],Natural>`_ @@ -1912,12 +1909,12 @@ func find*(s: string, chars: set[char], start: Natural = 0, last = 0): int {. func find*(s, sub: string, start: Natural = 0, last = 0): int {.rtl, extern: "nsuFindStr".} = - ## Searches for `sub` in `s` inside range ``start..last`` (both ends included). + ## Searches for `sub` in `s` inside range `start..last` (both ends included). ## If `last` is unspecified, it defaults to `s.high` (the last element). ## ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned. - ## Otherwise the index returned is relative to ``s[0]``, not ``start``. - ## Use `s[start..last].find` for a ``start``-origin index. + ## Otherwise the index returned is relative to `s[0]`, not `start`. + ## Use `s[start..last].find` for a `start`-origin index. ## ## See also: ## * `rfind func<#rfind,string,string,Natural>`_ @@ -1930,14 +1927,14 @@ func find*(s, sub: string, start: Natural = 0, last = 0): int {.rtl, func rfind*(s: string, sub: char, start: Natural = 0, last = -1): int {.rtl, extern: "nsuRFindChar".} = - ## Searches for `sub` in `s` inside range ``start..last`` (both ends included) + ## Searches for `sub` in `s` inside range `start..last` (both ends included) ## in reverse -- starting at high indexes and moving lower to the first - ## character or ``start``. If `last` is unspecified, it defaults to `s.high` + ## character or `start`. If `last` is unspecified, it defaults to `s.high` ## (the last element). ## ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned. - ## Otherwise the index returned is relative to ``s[0]``, not ``start``. - ## Use `s[start..last].find` for a ``start``-origin index. + ## Otherwise the index returned is relative to `s[0]`, not `start`. + ## Use `s[start..last].find` for a `start`-origin index. ## ## See also: ## * `find func<#find,string,char,Natural,int>`_ @@ -1948,14 +1945,14 @@ func rfind*(s: string, sub: char, start: Natural = 0, last = -1): int {.rtl, func rfind*(s: string, chars: set[char], start: Natural = 0, last = -1): int {. rtl, extern: "nsuRFindCharSet".} = - ## Searches for `chars` in `s` inside range ``start..last`` (both ends + ## Searches for `chars` in `s` inside range `start..last` (both ends ## included) in reverse -- starting at high indexes and moving lower to the - ## first character or ``start``. If `last` is unspecified, it defaults to + ## first character or `start`. If `last` is unspecified, it defaults to ## `s.high` (the last element). ## ## If `s` contains none of the characters in `chars`, -1 is returned. - ## Otherwise the index returned is relative to ``s[0]``, not ``start``. - ## Use `s[start..last].rfind` for a ``start``-origin index. + ## Otherwise the index returned is relative to `s[0]`, not `start`. + ## Use `s[start..last].rfind` for a `start`-origin index. ## ## See also: ## * `find func<#find,string,set[char],Natural,int>`_ @@ -1966,14 +1963,14 @@ func rfind*(s: string, chars: set[char], start: Natural = 0, last = -1): int {. func rfind*(s, sub: string, start: Natural = 0, last = -1): int {.rtl, extern: "nsuRFindStr".} = - ## Searches for `sub` in `s` inside range ``start..last`` (both ends included) + ## Searches for `sub` in `s` inside range `start..last` (both ends included) ## included) in reverse -- starting at high indexes and moving lower to the - ## first character or ``start``. If `last` is unspecified, it defaults to + ## first character or `start`. If `last` is unspecified, it defaults to ## `s.high` (the last element). ## ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned. - ## Otherwise the index returned is relative to ``s[0]``, not ``start``. - ## Use `s[start..last].rfind` for a ``start``-origin index. + ## Otherwise the index returned is relative to `s[0]`, not `start`. + ## Use `s[start..last].rfind` for a `start`-origin index. ## ## See also: ## * `find func<#find,string,string,Natural,int>`_ @@ -1992,7 +1989,7 @@ func rfind*(s, sub: string, start: Natural = 0, last = -1): int {.rtl, func count*(s: string, sub: char): int {.rtl, extern: "nsuCountChar".} = - ## Count the occurrences of the character `sub` in the string `s`. + ## Counts the occurrences of the character `sub` in the string `s`. ## ## See also: ## * `countLines func<#countLines,string>`_ @@ -2002,7 +1999,7 @@ func count*(s: string, sub: char): int {.rtl, extern: "nsuCountChar".} = func count*(s: string, subs: set[char]): int {.rtl, extern: "nsuCountCharSet".} = - ## Count the occurrences of the group of character `subs` in the string `s`. + ## Counts the occurrences of the group of character `subs` in the string `s`. ## ## See also: ## * `countLines func<#countLines,string>`_ @@ -2013,7 +2010,7 @@ func count*(s: string, subs: set[char]): int {.rtl, func count*(s: string, sub: string, overlapping: bool = false): int {.rtl, extern: "nsuCountString".} = - ## Count the occurrences of a substring `sub` in the string `s`. + ## Counts the occurrences of a substring `sub` in the string `s`. ## Overlapping occurrences of `sub` only count when `overlapping` ## is set to true (default: false). ## @@ -2032,7 +2029,7 @@ func count*(s: string, sub: string, overlapping: bool = false): int {.rtl, func countLines*(s: string): int {.rtl, extern: "nsuCountLines".} = ## Returns the number of lines in the string `s`. ## - ## This is the same as ``len(splitLines(s))``, but much more efficient + ## This is the same as `len(splitLines(s))`, but much more efficient ## because it doesn't modify the string creating temporal objects. Every ## `character literal `_ ## newline combination (CR, LF, CR-LF) is supported. @@ -2057,14 +2054,14 @@ func countLines*(s: string): int {.rtl, extern: "nsuCountLines".} = func contains*(s, sub: string): bool = - ## Same as ``find(s, sub) >= 0``. + ## Same as `find(s, sub) >= 0`. ## ## See also: ## * `find func<#find,string,string,Natural,int>`_ return find(s, sub) >= 0 func contains*(s: string, chars: set[char]): bool = - ## Same as ``find(s, chars) >= 0``. + ## Same as `find(s, chars) >= 0`. ## ## See also: ## * `find func<#find,string,set[char],Natural,int>`_ @@ -2135,7 +2132,7 @@ func replaceWord*(s, sub: string, by = ""): string {.rtl, ## Replaces `sub` in `s` by the string `by`. ## ## Each occurrence of `sub` has to be surrounded by word boundaries - ## (comparable to ``\b`` in regular expressions), otherwise it is not + ## (comparable to `\b` in regular expressions), otherwise it is not ## replaced. if sub.len == 0: return s const wordChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\128'..'\255'} @@ -2261,7 +2258,7 @@ func unescape*(s: string, prefix = "\"", suffix = "\""): string {.rtl, ## This complements `escape func<#escape,string,string,string>`_ ## as it performs the opposite operations. ## - ## If `s` does not begin with ``prefix`` and end with ``suffix`` a + ## If `s` does not begin with `prefix` and end with `suffix` a ## ValueError exception will be raised. result = newStringOfCap(s.len) var i = prefix.len @@ -2318,24 +2315,24 @@ when not defined(js): type FloatFormatMode* = enum - ## the different modes of floating point formatting + ## The different modes of floating point formatting. ffDefault, ## use the shorter floating point notation ffDecimal, ## use decimal floating point notation - ffScientific ## use scientific notation (using ``e`` character) + ffScientific ## use scientific notation (using `e` character) func formatBiggestFloat*(f: BiggestFloat, format: FloatFormatMode = ffDefault, precision: range[-1..32] = 16; decimalSep = '.'): string {.rtl, extern: "nsu$1".} = ## Converts a floating point value `f` to a string. ## - ## If ``format == ffDecimal`` then precision is the number of digits to + ## If `format == ffDecimal` then precision is the number of digits to ## be printed after the decimal point. - ## If ``format == ffScientific`` then precision is the maximum number + ## If `format == ffScientific` then precision is the maximum number ## of significant digits to be printed. ## `precision`'s default value is the maximum number of meaningful digits - ## after the decimal point for Nim's ``biggestFloat`` type. + ## after the decimal point for Nim's `biggestFloat` type. ## - ## If ``precision == -1``, it tries to format it nicely. + ## If `precision == -1`, it tries to format it nicely. runnableExamples: let x = 123.456 doAssert x.formatBiggestFloat() == "123.4560000000000" @@ -2407,14 +2404,14 @@ func formatFloat*(f: float, format: FloatFormatMode = ffDefault, rtl, extern: "nsu$1".} = ## Converts a floating point value `f` to a string. ## - ## If ``format == ffDecimal`` then precision is the number of digits to + ## If `format == ffDecimal` then precision is the number of digits to ## be printed after the decimal point. - ## If ``format == ffScientific`` then precision is the maximum number + ## If `format == ffScientific` then precision is the maximum number ## of significant digits to be printed. ## `precision`'s default value is the maximum number of meaningful digits - ## after the decimal point for Nim's ``float`` type. + ## after the decimal point for Nim's `float` type. ## - ## If ``precision == -1``, it tries to format it nicely. + ## If `precision == -1`, it tries to format it nicely. runnableExamples: let x = 123.456 doAssert x.formatFloat() == "123.4560000000000" @@ -2425,7 +2422,7 @@ func formatFloat*(f: float, format: FloatFormatMode = ffDefault, func trimZeros*(x: var string; decimalSep = '.') = ## Trim trailing zeros from a formatted floating point - ## value `x` (must be declared as ``var``). + ## value `x` (must be declared as `var`). ## ## This modifies `x` itself, it does not return a copy. runnableExamples: @@ -2443,7 +2440,7 @@ func trimZeros*(x: var string; decimalSep = '.') = x.delete(pos, last) type - BinaryPrefixMode* = enum ## the different names for binary prefixes + BinaryPrefixMode* = enum ## The different names for binary prefixes. bpIEC, # use the IEC/ISO standard prefixes such as kibi bpColloquial # use the colloquial kilo, mega etc @@ -2656,7 +2653,7 @@ func invalidFormatString() {.noinline.} = func addf*(s: var string, formatstr: string, a: varargs[string, `$`]) {.rtl, extern: "nsuAddf".} = - ## The same as ``add(s, formatstr % a)``, but more efficient. + ## The same as `add(s, formatstr % a)`, but more efficient. const PatternChars = {'a'..'z', 'A'..'Z', '0'..'9', '\128'..'\255', '_'} var i = 0 var num = 0 @@ -2735,17 +2732,17 @@ func `%` *(formatstr: string, a: openArray[string]): string {.rtl, ## .. code-block:: nim ## "The cat eats fish." ## - ## The substitution variables (the thing after the ``$``) are enumerated - ## from 1 to ``a.len``. - ## To produce a verbatim ``$``, use ``$$``. - ## The notation ``$#`` can be used to refer to the next substitution + ## The substitution variables (the thing after the `$`) are enumerated + ## from 1 to `a.len`. + ## To produce a verbatim `$`, use `$$`. + ## The notation `$#` can be used to refer to the next substitution ## variable: ## ## .. code-block:: nim ## "$# eats $#." % ["The cat", "fish"] ## ## Substitution variables can also be words (that is - ## ``[A-Za-z_]+[A-Za-z0-9_]*``) in which case the arguments in `a` with even + ## `[A-Za-z_]+[A-Za-z0-9_]*`) in which case the arguments in `a` with even ## indices are keys and with odd indices are the corresponding values. ## An example: ## @@ -2767,14 +2764,14 @@ func `%` *(formatstr: string, a: openArray[string]): string {.rtl, func `%` *(formatstr, a: string): string {.rtl, extern: "nsuFormatSingleElem".} = - ## This is the same as ``formatstr % [a]`` (see + ## This is the same as `formatstr % [a]` (see ## `% func<#%25,string,openArray[string]>`_). result = newStringOfCap(formatstr.len + a.len) addf(result, formatstr, [a]) func format*(formatstr: string, a: varargs[string, `$`]): string {.rtl, extern: "nsuFormatVarargs".} = - ## This is the same as ``formatstr % a`` (see + ## This is the same as `formatstr % a` (see ## `% func<#%25,string,openArray[string]>`_) except that it supports ## auto stringification. ## @@ -2820,9 +2817,9 @@ func strip*(s: string, leading = true, trailing = true, result = substr(s, first, last) func stripLineEnd*(s: var string) = - ## Returns ``s`` stripped from one of these suffixes: - ## ``\r, \n, \r\n, \f, \v`` (at most once instance). - ## For example, can be useful in conjunction with ``osproc.execCmdEx``. + ## Returns `s` stripped from one of these suffixes: + ## `\r, \n, \r\n, \f, \v` (at most once instance). + ## For example, can be useful in conjunction with `osproc.execCmdEx`. ## aka: `chomp`:idx: runnableExamples: var s = "foo\n\n" From e1e069dd6c4e3439ee568686a2aed0a58bd771b0 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Thu, 17 Dec 2020 06:41:05 -0600 Subject: [PATCH 1183/3768] use hexchar in stdlib (#16290) --- lib/pure/parsecfg.nim | 24 ++++++------------------ lib/pure/parsejson.nim | 13 +++---------- lib/pure/parsesql.nim | 24 ++++++------------------ lib/pure/pegs.nim | 20 +++++--------------- lib/std/private/decode_helpers.nim | 25 +++++++++++++++++++------ nimsuggest/sexp.nim | 10 ++-------- tests/stdlib/tdecode_helpers.nim | 27 +++++++++++++++++++++++++++ 7 files changed, 68 insertions(+), 75 deletions(-) create mode 100644 tests/stdlib/tdecode_helpers.nim diff --git a/lib/pure/parsecfg.nim b/lib/pure/parsecfg.nim index c87010122..79cf522f0 100644 --- a/lib/pure/parsecfg.nim +++ b/lib/pure/parsecfg.nim @@ -172,8 +172,8 @@ runnableExamples: doAssert dict.getSectionValue(section4, "does_that_mean_anything_special") == "False" doAssert dict.getSectionValue(section4, "purpose") == "formatting for readability" -import - strutils, lexbase, streams, tables +import strutils, lexbase, streams, tables +import std/private/decode_helpers include "system/inclrtl" @@ -247,20 +247,6 @@ proc getFilename*(c: CfgParser): string {.rtl, extern: "npc$1".} = ## Gets the filename of the file that the parser processes. result = c.filename -proc handleHexChar(c: var CfgParser, xi: var int) = - case c.buf[c.bufpos] - of '0'..'9': - xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('0')) - inc(c.bufpos) - of 'a'..'f': - xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('a') + 10) - inc(c.bufpos) - of 'A'..'F': - xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('A') + 10) - inc(c.bufpos) - else: - discard - proc handleDecChars(c: var CfgParser, xi: var int) = while c.buf[c.bufpos] in {'0'..'9'}: xi = (xi * 10) + (ord(c.buf[c.bufpos]) - ord('0')) @@ -305,8 +291,10 @@ proc getEscapedChar(c: var CfgParser, tok: var Token) = of 'x', 'X': inc(c.bufpos) var xi = 0 - handleHexChar(c, xi) - handleHexChar(c, xi) + if handleHexChar(c.buf[c.bufpos], xi): + inc(c.bufpos) + if handleHexChar(c.buf[c.bufpos], xi): + inc(c.bufpos) add(tok.literal, chr(xi)) of '0'..'9': var xi = 0 diff --git a/lib/pure/parsejson.nim b/lib/pure/parsejson.nim index 18e6037f3..8a0e04298 100644 --- a/lib/pure/parsejson.nim +++ b/lib/pure/parsejson.nim @@ -11,8 +11,8 @@ ## and exported by the ``json`` standard library ## module, but can also be used in its own right. -import - strutils, lexbase, streams, unicode +import strutils, lexbase, streams, unicode +import std/private/decode_helpers type JsonEventKind* = enum ## enumeration of all events that may occur when parsing @@ -162,18 +162,11 @@ proc errorMsgExpected*(my: JsonParser, e: string): string = result = "$1($2, $3) Error: $4" % [ my.filename, $getLine(my), $getColumn(my), e & " expected"] -proc handleHexChar(c: char, x: var int): bool = - result = true # Success - case c - of '0'..'9': x = (x shl 4) or (ord(c) - ord('0')) - of 'a'..'f': x = (x shl 4) or (ord(c) - ord('a') + 10) - of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10) - else: result = false # error - proc parseEscapedUTF16*(buf: cstring, pos: var int): int = result = 0 #UTF-16 escape is always 4 bytes. for _ in 0..3: + # if char in '0' .. '9', 'a' .. 'f', 'A' .. 'F' if handleHexChar(buf[pos], result): inc(pos) else: diff --git a/lib/pure/parsesql.nim b/lib/pure/parsesql.nim index 0232baf8d..23d43dfe0 100644 --- a/lib/pure/parsesql.nim +++ b/lib/pure/parsesql.nim @@ -12,8 +12,8 @@ ## ## Unstable API. -import - strutils, lexbase +import strutils, lexbase +import std/private/decode_helpers # ------------------- scanner ------------------------------------------------- @@ -72,20 +72,6 @@ proc getColumn(L: SqlLexer): int = proc getLine(L: SqlLexer): int = result = L.lineNumber -proc handleHexChar(c: var SqlLexer, xi: var int) = - case c.buf[c.bufpos] - of '0'..'9': - xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('0')) - inc(c.bufpos) - of 'a'..'f': - xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('a') + 10) - inc(c.bufpos) - of 'A'..'F': - xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('A') + 10) - inc(c.bufpos) - else: - discard - proc handleOctChar(c: var SqlLexer, xi: var int) = if c.buf[c.bufpos] in {'0'..'7'}: xi = (xi shl 3) or (ord(c.buf[c.bufpos]) - ord('0')) @@ -130,8 +116,10 @@ proc getEscapedChar(c: var SqlLexer, tok: var Token) = of 'x', 'X': inc(c.bufpos) var xi = 0 - handleHexChar(c, xi) - handleHexChar(c, xi) + if handleHexChar(c.buf[c.bufpos], xi): + inc(c.bufpos) + if handleHexChar(c.buf[c.bufpos], xi): + inc(c.bufpos) add(tok.literal, chr(xi)) of '0'..'7': var xi = 0 diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim index 8abd3deb5..f9868b619 100644 --- a/lib/pure/pegs.nim +++ b/lib/pure/pegs.nim @@ -21,6 +21,7 @@ const useUnicode = true ## change this to deactivate proper UTF-8 support import strutils, macros +import std/private/decode_helpers when useUnicode: import unicode @@ -1466,19 +1467,6 @@ proc errorStr(L: PegLexer, msg: string, line = -1, col = -1): string = var col = if col < 0: getColumn(L) else: col result = "$1($2, $3) Error: $4" % [L.filename, $line, $col, msg] -proc handleHexChar(c: var PegLexer, xi: var int) = - case c.buf[c.bufpos] - of '0'..'9': - xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('0')) - inc(c.bufpos) - of 'a'..'f': - xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('a') + 10) - inc(c.bufpos) - of 'A'..'F': - xi = (xi shl 4) or (ord(c.buf[c.bufpos]) - ord('A') + 10) - inc(c.bufpos) - else: discard - proc getEscapedChar(c: var PegLexer, tok: var Token) = inc(c.bufpos) if c.bufpos >= len(c.buf): @@ -1515,8 +1503,10 @@ proc getEscapedChar(c: var PegLexer, tok: var Token) = tok.kind = tkInvalid return var xi = 0 - handleHexChar(c, xi) - handleHexChar(c, xi) + if handleHexChar(c.buf[c.bufpos], xi): + inc(c.bufpos) + if handleHexChar(c.buf[c.bufpos], xi): + inc(c.bufpos) if xi == 0: tok.kind = tkInvalid else: add(tok.literal, chr(xi)) of '0'..'9': diff --git a/lib/std/private/decode_helpers.nim b/lib/std/private/decode_helpers.nim index 586c7cae6..e28651690 100644 --- a/lib/std/private/decode_helpers.nim +++ b/lib/std/private/decode_helpers.nim @@ -1,9 +1,25 @@ -proc handleHexChar*(c: char, x: var int, f: var bool) {.inline.} = +proc handleHexChar*(c: char, x: var int): bool {.inline.} = + ## Converts `%xx` hexadecimal to the ordinal number and adds the result to `x`. + ## Returns `true` if `c` is hexadecimal. + ## + ## When `c` is hexadecimal, the proc is equal to `x = x shl 4 + hex2Int(c)`. + runnableExamples: + var x = 0 + assert handleHexChar('a', x) + assert x == 10 + + assert handleHexChar('B', x) + assert x == 171 # 10 shl 4 + 11 + + assert not handleHexChar('?', x) + assert x == 171 # unchanged + result = true case c of '0'..'9': x = (x shl 4) or (ord(c) - ord('0')) of 'a'..'f': x = (x shl 4) or (ord(c) - ord('a') + 10) of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10) - else: f = true + else: + result = false proc decodePercent*(s: openArray[char], i: var int): char = ## Converts `%xx` hexadecimal to the character with ordinal number `xx`. @@ -14,9 +30,6 @@ proc decodePercent*(s: openArray[char], i: var int): char = result = '%' if i+2 < s.len: var x = 0 - var failed = false - handleHexChar(s[i+1], x, failed) - handleHexChar(s[i+2], x, failed) - if not failed: + if handleHexChar(s[i+1], x) and handleHexChar(s[i+2], x): result = chr(x) inc(i, 2) diff --git a/nimsuggest/sexp.nim b/nimsuggest/sexp.nim index a4368a558..cee538b6e 100644 --- a/nimsuggest/sexp.nim +++ b/nimsuggest/sexp.nim @@ -12,6 +12,8 @@ import hashes, strutils, lexbase, streams, unicode, macros +import std/private/decode_helpers + type SexpEventKind* = enum ## enumeration of all events that may occur when parsing sexpError, ## an error occurred during parsing @@ -113,14 +115,6 @@ proc errorMsgExpected*(my: SexpParser, e: string): string = ## other error messages result = "($1, $2) Error: $3" % [$getLine(my), $getColumn(my), e & " expected"] -proc handleHexChar(c: char, x: var int): bool = - result = true # Success - case c - of '0'..'9': x = (x shl 4) or (ord(c) - ord('0')) - of 'a'..'f': x = (x shl 4) or (ord(c) - ord('a') + 10) - of 'A'..'F': x = (x shl 4) or (ord(c) - ord('A') + 10) - else: result = false # error - proc parseString(my: var SexpParser): TTokKind = result = tkString var pos = my.bufpos + 1 diff --git a/tests/stdlib/tdecode_helpers.nim b/tests/stdlib/tdecode_helpers.nim new file mode 100644 index 000000000..626a014fc --- /dev/null +++ b/tests/stdlib/tdecode_helpers.nim @@ -0,0 +1,27 @@ +import std/private/decode_helpers + + +block: + var i = 0 + let c = decodePercent("%t9", i) + doAssert (i, c) == (0, '%') + +block: + var i = 0 + let c = decodePercent("19", i) + doAssert (i, c) == (0, '%') + +block: + var i = 0 + let c = decodePercent("%19", i) + doAssert (i, c) == (2, '\x19') + +block: + var i = 0 + let c = decodePercent("%A9", i) + doAssert (i, c) == (2, '\xA9') + +block: + var i = 0 + let c = decodePercent("%Aa", i) + doAssert (i, c) == (2, '\xAA') From 5d8f862bc644e1db076e7e8cc8a2fe8c99009329 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Thu, 17 Dec 2020 14:14:16 -0600 Subject: [PATCH 1184/3768] fix #16364 (#16379) [backport] * fix #16364 * add comments * Update tools/dochack/dochack.nim Co-authored-by: Clyybber Co-authored-by: Clyybber --- tools/dochack/dochack.nim | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tools/dochack/dochack.nim b/tools/dochack/dochack.nim index 471212a50..4a491cf88 100644 --- a/tools/dochack/dochack.nim +++ b/tools/dochack/dochack.nim @@ -268,15 +268,13 @@ var template normalize(x: cstring): cstring = x.toLower.replace("_", "") proc escapeCString(x: var cstring) = + # Original strings are already escaped except HTML tags, so + # we only escape `<` and `>`. var s = "" for c in x: case c - of '&': s.add("&") of '<': s.add("<") of '>': s.add(">") - of '"': s.add(""") - of '\'': s.add("'") - of '/': s.add("/") else: s.add(c) x = s.cstring From 868c31e88a68ce16c5c0d9283a41fb3cfcec4c41 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 17 Dec 2020 23:21:55 +0100 Subject: [PATCH 1185/3768] fixes #16365 [backport] (#16381) --- lib/system/strs_v2.nim | 13 ++++----- tests/destructor/tnewruntime_strutils.nim | 32 ++++++++++++++++++++++- 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/lib/system/strs_v2.nim b/lib/system/strs_v2.nim index f51791da2..fe117997b 100644 --- a/lib/system/strs_v2.nim +++ b/lib/system/strs_v2.nim @@ -40,21 +40,22 @@ proc resize(old: int): int {.inline.} = else: result = old * 3 div 2 # for large arrays * 3/2 is better proc prepareAdd(s: var NimStringV2; addlen: int) {.compilerRtl.} = + let newLen = s.len + addlen if isLiteral(s): let oldP = s.p # can't mutate a literal, so we need a fresh copy here: when compileOption("threads"): - s.p = cast[ptr NimStrPayload](allocShared0(contentSize(s.len + addlen))) + s.p = cast[ptr NimStrPayload](allocShared0(contentSize(newLen))) else: - s.p = cast[ptr NimStrPayload](alloc0(contentSize(s.len + addlen))) - s.p.cap = s.len + addlen + s.p = cast[ptr NimStrPayload](alloc0(contentSize(newLen))) + s.p.cap = newLen if s.len > 0: # we are about to append, so there is no need to copy the \0 terminator: - copyMem(unsafeAddr s.p.data[0], unsafeAddr oldP.data[0], s.len) + copyMem(unsafeAddr s.p.data[0], unsafeAddr oldP.data[0], min(s.len, newLen)) else: let oldCap = s.p.cap and not strlitFlag - if s.len + addlen > oldCap: - let newCap = max(s.len + addlen, resize(oldCap)) + if newLen > oldCap: + let newCap = max(newLen, resize(oldCap)) when compileOption("threads"): s.p = cast[ptr NimStrPayload](reallocShared0(s.p, contentSize(oldCap), contentSize(newCap))) else: diff --git a/tests/destructor/tnewruntime_strutils.nim b/tests/destructor/tnewruntime_strutils.nim index b0bceb4b5..0725718d7 100644 --- a/tests/destructor/tnewruntime_strutils.nim +++ b/tests/destructor/tnewruntime_strutils.nim @@ -2,7 +2,10 @@ discard """ valgrind: true cmd: '''nim c -d:nimAllocStats --newruntime -d:useMalloc $file''' output: ''' -@[(input: @["KXSC", "BGMC"]), (input: @["PXFX"]), (input: @["WXRQ", "ZSCZD"])]''' +@[(input: @["KXSC", "BGMC"]), (input: @["PXFX"]), (input: @["WXRQ", "ZSCZD"])] +14 +First tasks completed. +Second tasks completed.''' """ import strutils, os, std / wordwrap @@ -211,3 +214,30 @@ staticTests() # bug #12965 let xaa = @[""].join() let xbb = @["", ""].join() + +# bug #16365 + +# Task 1: +when true: + # Task 1_a: + var test_string_a = "name_something" + echo test_string_a.len() + let new_len_a = test_string_a.len - "_something".len() + test_string_a.setLen new_len_a + + echo "First tasks completed." + +# Task 2: +when true: + # Task 2_a + var test_string: string + let some_string = "something" + for i in some_string.items: + test_string.add $i + + # Task 2_b + var test_string_b = "name_something" + let new_len_b = test_string_b.len - "_something".len() + test_string_b.setLen new_len_b + + echo "Second tasks completed." From df17cf5e9e1afae333d9ab755d19d1c9d4d60a0f Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 17 Dec 2020 23:53:43 -0800 Subject: [PATCH 1186/3768] misc cleanups (#16383) --- compiler/docgen.nim | 7 ++++--- compiler/modulegraphs.nim | 2 +- lib/system.nim | 6 +++--- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 824fafd6b..d21db7634 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -7,20 +7,21 @@ # distribution, for details about the copyright. # -# This is the documentation generator. It is currently pretty simple: No -# semantic checking is done for the code. Cross-references are generated +# This is the documentation generator. Cross-references are generated # by knowing how the anchors are going to be named. import ast, strutils, strtabs, options, msgs, os, ropes, idents, wordrecg, syntaxes, renderer, lexer, packages/docutils/rstast, packages/docutils/rst, packages/docutils/rstgen, - json, xmltree, cgi, trees, types, + json, xmltree, trees, types, typesrenderer, astalgo, lineinfos, intsets, pathutils, trees, tables, nimpaths, renderverbatim, osproc +from uri import encodeUrl from std/private/globs import nativeToUnixPath + const exportSection = skField docCmdSkip = "skip" diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index abbefa9b6..e3c54eeae 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -28,7 +28,7 @@ import ast, intsets, tables, options, lineinfos, hashes, idents, incremental, btrees, md5 -import ic / packed_ast +# import ic / packed_ast type SigHash* = distinct MD5Digest diff --git a/lib/system.nim b/lib/system.nim index afd8e467d..0797b7caf 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -873,9 +873,9 @@ else: template ``*(t: untyped): untyped = t template disarm*(x: typed) = - ## Useful for ``disarming`` dangling pointers explicitly for the - ## --newruntime. Regardless of whether --newruntime is used or not - ## this sets the pointer or callback ``x`` to ``nil``. This is an + ## Useful for `disarming` dangling pointers explicitly for `--newruntime`. + ## Regardless of whether `--newruntime` is used or not + ## this sets the pointer or callback `x` to `nil`. This is an ## experimental API! x = nil From 23d23ecb081be6702d74024be8f96d92d9f88a59 Mon Sep 17 00:00:00 2001 From: Matt Haggard Date: Fri, 18 Dec 2020 04:06:13 -0500 Subject: [PATCH 1187/3768] Make 'echo' raise IOErrors when appropriate (#16367) * Make 'echo' raise IOError when fwrite/fflush fail * Fix fwrite return value comparison * Add test for echo raising error and don't fail to release locks in echo * Fix exitcode expectation * Make 'echo' raise IOError on Windows if it fails * Add nimLegacyEchoNoRaise for prior no-IOError echo behavior * Use checkErrMaybe template --- changelog.md | 3 +++ lib/system/io.nim | 24 ++++++++++++++++-------- tests/exception/t16366.nim | 11 +++++++++++ 3 files changed, 30 insertions(+), 8 deletions(-) create mode 100644 tests/exception/t16366.nim diff --git a/changelog.md b/changelog.md index eec754a89..01eff1cd7 100644 --- a/changelog.md +++ b/changelog.md @@ -63,6 +63,9 @@ - Added `math.isNaN`. +- `echo` and `debugEcho` will now raise `IOError` if writing to stdout fails. Previous behavior + silently ignored errors. See #16366. Use `-d:nimLegacyEchoNoRaise` for previous behavior. + ## Language changes - `nimscript` now handles `except Exception as e`. diff --git a/lib/system/io.nim b/lib/system/io.nim index b3e1725b4..22059c0a8 100644 --- a/lib/system/io.nim +++ b/lib/system/io.nim @@ -223,6 +223,9 @@ when defined(windows): # But we cannot call printf directly as the string might contain \0. # So we have to loop over all the sections separated by potential \0s. var i = c_fprintf(f, "%s", s) + if i < 0: + if doRaise: raiseEIO("cannot write string to file") + return while i < s.len: if s[i] == '\0': let w = c_fputc('\0', f) @@ -780,6 +783,13 @@ when declared(stdout): not defined(nintendoswitch) and not defined(freertos) and hostOS != "any" + const echoDoRaise = not defined(nimLegacyEchoNoRaise) # see PR #16366 + + template checkErrMaybe(succeeded: bool): untyped = + if not succeeded: + when echoDoRaise: + checkErr(stdout) + proc echoBinSafe(args: openArray[string]) {.compilerproc.} = when defined(androidNDK): var s = "" @@ -792,20 +802,18 @@ when declared(stdout): proc flockfile(f: File) {.importc, nodecl.} proc funlockfile(f: File) {.importc, nodecl.} flockfile(stdout) + defer: funlockfile(stdout) when defined(windows) and compileOption("threads"): acquireSys echoLock + defer: releaseSys echoLock for s in args: when defined(windows): - writeWindows(stdout, s) + writeWindows(stdout, s, doRaise = echoDoRaise) else: - discard c_fwrite(s.cstring, cast[csize_t](s.len), 1, stdout) + checkErrMaybe(c_fwrite(s.cstring, cast[csize_t](s.len), 1, stdout) == s.len) const linefeed = "\n" - discard c_fwrite(linefeed.cstring, linefeed.len, 1, stdout) - discard c_fflush(stdout) - when stdOutLock: - funlockfile(stdout) - when defined(windows) and compileOption("threads"): - releaseSys echoLock + checkErrMaybe(c_fwrite(linefeed.cstring, linefeed.len, 1, stdout) == linefeed.len) + checkErrMaybe(c_fflush(stdout) == 0) when defined(windows) and not defined(nimscript) and not defined(js): diff --git a/tests/exception/t16366.nim b/tests/exception/t16366.nim new file mode 100644 index 000000000..4bcad79ed --- /dev/null +++ b/tests/exception/t16366.nim @@ -0,0 +1,11 @@ +discard """ + action: run + exitcode: 0 + targets: "c cpp" + disabled: openbsd +""" + +echo "foo1" +close stdout +doAssertRaises(IOError): + echo "foo" From dcdbae798c366134cac3d00419fee9ea3dd7bf70 Mon Sep 17 00:00:00 2001 From: Tomohiro Date: Fri, 18 Dec 2020 18:17:19 +0900 Subject: [PATCH 1188/3768] Fix osproc so that it doesn't close pipe/process/thread handles twice (#16385) [backport] * Add error check to closeHandle and fix closing handle twice in osproc * Fix compile error on Linux --- lib/pure/osproc.nim | 58 +++++++++++++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index d6943c7f8..9c198e6fa 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -414,6 +414,8 @@ proc execProcesses*(cmds: openArray[string], raiseOSError(err) if rexit >= 0: + when defined(windows): + let processHandle = q[rexit].fProcessHandle result = max(result, abs(q[rexit].peekExitCode())) if afterRunEvent != nil: afterRunEvent(idxs[rexit], q[rexit]) close(q[rexit]) @@ -428,7 +430,7 @@ proc execProcesses*(cmds: openArray[string], else: when defined(windows): for k in 0..wcount - 1: - if w[k] == q[rexit].fProcessHandle: + if w[k] == processHandle: w[k] = w[wcount - 1] w[wcount - 1] = 0 dec(wcount) @@ -525,10 +527,17 @@ when defined(Windows) and not defined(useNimRtl): handle: Handle atTheEnd: bool + proc closeHandleCheck(handle: Handle) {.inline.} = + if handle.closeHandle() == 0: + raiseOSError(osLastError()) + + proc fileClose[T: Handle | FileHandle](h: var T) {.inline.} = + if h > 4: + closeHandleCheck(h) + h = INVALID_HANDLE_VALUE.T + proc hsClose(s: Stream) = - # xxx here + elsewhere: check instead of discard; ignoring errors leads to - # hard to track bugs - discard FileHandleStream(s).handle.closeHandle + FileHandleStream(s).handle.fileClose() proc hsAtEnd(s: Stream): bool = return FileHandleStream(s).atTheEnd @@ -633,8 +642,8 @@ when defined(Windows) and not defined(useNimRtl): stdin = myDup(pipeIn, 0) stdout = myDup(pipeOut, 0) - discard closeHandle(pipeIn) - discard closeHandle(pipeOut) + closeHandleCheck(pipeIn) + closeHandleCheck(pipeOut) stderr = stdout proc createPipeHandles(rdHandle, wrHandle: var Handle) = @@ -645,9 +654,6 @@ when defined(Windows) and not defined(useNimRtl): if createPipe(rdHandle, wrHandle, sa, 0) == 0'i32: raiseOSError(osLastError()) - proc fileClose(h: Handle) {.inline.} = - if h > 4: discard closeHandle(h) - proc startProcess(command: string, workingDir: string = "", args: openArray[string] = [], env: StringTableRef = nil, options: set[ProcessOption] = {poStdErrToStdOut}): @@ -742,13 +748,31 @@ when defined(Windows) and not defined(useNimRtl): result.id = procInfo.dwProcessId result.exitFlag = false + proc closeThreadAndProcessHandle(p: Process) = + if p.fThreadHandle != 0: + closeHandleCheck(p.fThreadHandle) + p.fThreadHandle = 0 + + if p.fProcessHandle != 0: + closeHandleCheck(p.fProcessHandle) + p.fProcessHandle = 0 + proc close(p: Process) = if poParentStreams notin p.options: - discard closeHandle(p.inHandle) - discard closeHandle(p.outHandle) - discard closeHandle(p.errHandle) - discard closeHandle(p.fThreadHandle) - discard closeHandle(p.fProcessHandle) + if p.inStream == nil: + p.inHandle.fileClose() + else: + # p.inHandle can be already closed via inputStream. + p.inStream.close + + # You may NOT close outputStream and errorStream. + assert p.outStream == nil or FileHandleStream(p.outStream).handle != INVALID_HANDLE_VALUE + assert p.errStream == nil or FileHandleStream(p.errStream).handle != INVALID_HANDLE_VALUE + + if p.outHandle != p.errHandle: + p.errHandle.fileClose() + p.outHandle.fileClose() + p.closeThreadAndProcessHandle() proc suspend(p: Process) = discard suspendThread(p.fThreadHandle) @@ -782,8 +806,7 @@ when defined(Windows) and not defined(useNimRtl): if status != STILL_ACTIVE: p.exitFlag = true p.exitStatus = status - discard closeHandle(p.fThreadHandle) - discard closeHandle(p.fProcessHandle) + p.closeThreadAndProcessHandle() result = status else: result = -1 @@ -799,8 +822,7 @@ when defined(Windows) and not defined(useNimRtl): discard getExitCodeProcess(p.fProcessHandle, status) p.exitFlag = true p.exitStatus = status - discard closeHandle(p.fThreadHandle) - discard closeHandle(p.fProcessHandle) + p.closeThreadAndProcessHandle() result = status proc inputStream(p: Process): Stream = From 90dbb6f3fbab684afad729903ff322a9792e1706 Mon Sep 17 00:00:00 2001 From: Code Hz Date: Fri, 18 Dec 2020 17:40:43 +0800 Subject: [PATCH 1189/3768] removing `out T` from docs since it no longer working (#16378) [backport] * remove `out T` from docs see https://github.com/nim-lang/Nim/issues/16131 * remove `out T` in title * remove entire paragraph --- doc/manual.rst | 31 ++++++++++--------------------- 1 file changed, 10 insertions(+), 21 deletions(-) diff --git a/doc/manual.rst b/doc/manual.rst index cb35ee885..fd0ac0529 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -2499,13 +2499,13 @@ matches) is preferred: gen(ri) # "ref T" -Overloading based on 'var T' / 'out T' +Overloading based on 'var T' -------------------------------------- -If the formal parameter ``f`` is of type ``var T`` (or ``out T``) -in addition to the ordinary -type checking, the argument is checked to be an `l-value`:idx:. -``var T`` (or ``out T``) matches better than just ``T`` then. +If the formal parameter ``f`` is of type ``var T`` +in addition to the ordinary type checking, +the argument is checked to be an `l-value`:idx:. +``var T`` matches better than just ``T`` then. .. code-block:: nim proc sayHi(x: int): string = @@ -2524,17 +2524,6 @@ type checking, the argument is checked to be an `l-value`:idx:. # 13 -An l-value matches ``var T`` and ``out T`` equally well, hence -the following is ambiguous: - -.. code-block:: nim - - proc p(x: out string) = x = "" - proc p(x: var string) = x = "" - var v: string - p(v) # ambiguous - - Lazy type resolution for untyped -------------------------------- @@ -4975,7 +4964,7 @@ of "typedesc"-ness is stripped off: Generic inference restrictions ------------------------------ -The types ``var T``, ``out T`` and ``typedesc[T]`` cannot be inferred in a generic +The types ``var T`` and ``typedesc[T]`` cannot be inferred in a generic instantiation. The following is not allowed: .. code-block:: nim @@ -6155,10 +6144,10 @@ noSideEffect pragma The ``noSideEffect`` pragma is used to mark a proc/iterator to have no side effects. This means that the proc/iterator only changes locations that are reachable from its parameters and the return value only depends on the -arguments. If none of its parameters have the type ``var T`` or ``out T`` -or ``ref T`` or ``ptr T`` this means no locations are modified. It is a static -error to mark a proc/iterator to have no side effect if the compiler cannot -verify this. +arguments. If none of its parameters have the type ``var T`` or ``ref T`` +or ``ptr T`` this means no locations are modified. It is a static error to +mark a proc/iterator to have no side effect if the compiler cannot verify +this. As a special semantic rule, the built-in `debugEcho `_ pretends to be free of side effects, From 78acf1becb20a26637d0ac03bd8fcc6296aaa0f8 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Fri, 18 Dec 2020 09:29:36 -0600 Subject: [PATCH 1190/3768] fix cmpIgnoreStyle bug (#16392) --- lib/core/typeinfo.nim | 1 + tests/stdlib/ttypeinfo.nim | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/lib/core/typeinfo.nim b/lib/core/typeinfo.nim index fff237718..c15f6dc1f 100644 --- a/lib/core/typeinfo.nim +++ b/lib/core/typeinfo.nim @@ -372,6 +372,7 @@ proc cmpIgnoreStyle(a, b: cstring): int {.noSideEffect.} = else: result = c var i = 0 var j = 0 + if a[0] != b[0]: return 1 while true: while a[i] == '_': inc(i) while b[j] == '_': inc(j) # BUGFIX: typo diff --git a/tests/stdlib/ttypeinfo.nim b/tests/stdlib/ttypeinfo.nim index 3155a0f88..fc0bd3e53 100644 --- a/tests/stdlib/ttypeinfo.nim +++ b/tests/stdlib/ttypeinfo.nim @@ -58,3 +58,14 @@ block: for i in 0 .. m.len-1: for j in 0 .. m[i].len-1: doAssert getString(m[i][j]) == myArr[i][j] + +block: + type + Test = enum + Hello, he_llo + + var x = hello + var y = x.toAny + + doAssert getEnumOrdinal(y, "Hello") == 0 + doAssert getEnumOrdinal(y, "hello") == 1 From 0b7847ba3cbbc2baaeeedf6bbe66823079686662 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Fri, 18 Dec 2020 09:45:51 -0600 Subject: [PATCH 1191/3768] active windows tests and use doAssert (#16395) --- tests/stdlib/tstreams.nim | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/tests/stdlib/tstreams.nim b/tests/stdlib/tstreams.nim index e0310b5f5..c2ceed624 100644 --- a/tests/stdlib/tstreams.nim +++ b/tests/stdlib/tstreams.nim @@ -12,7 +12,6 @@ I AM GROOT ''' -disabled: "windows" """ @@ -64,14 +63,14 @@ echo buffer block: var ss = newStringStream("The quick brown fox jumped over the lazy dog.\nThe lazy dog ran") - assert(ss.getPosition == 0) - assert(ss.peekStr(5) == "The q") - assert(ss.getPosition == 0) # haven't moved - assert(ss.readStr(5) == "The q") - assert(ss.getPosition == 5) # did move - assert(ss.peekLine() == "uick brown fox jumped over the lazy dog.") - assert(ss.getPosition == 5) # haven't moved + doAssert(ss.getPosition == 0) + doAssert(ss.peekStr(5) == "The q") + doAssert(ss.getPosition == 0) # haven't moved + doAssert(ss.readStr(5) == "The q") + doAssert(ss.getPosition == 5) # did move + doAssert(ss.peekLine() == "uick brown fox jumped over the lazy dog.") + doAssert(ss.getPosition == 5) # haven't moved var str = newString(100) - assert(ss.peekLine(str)) - assert(str == "uick brown fox jumped over the lazy dog.") - assert(ss.getPosition == 5) # haven't moved + doAssert(ss.peekLine(str)) + doAssert(str == "uick brown fox jumped over the lazy dog.") + doAssert(ss.getPosition == 5) # haven't moved From 9674eb3ca67042bb6feb442ce4bcfc64933b5ab4 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Sat, 19 Dec 2020 12:33:10 +0300 Subject: [PATCH 1192/3768] RST enumlist followup (#16382) * fix matching of enumerator #) * RST: markdown list with auto-enumerator `1` --- lib/packages/docutils/rst.nim | 25 ++++++++++++++------ tests/stdlib/trstgen.nim | 44 +++++++++++++++++++++++++++++++---- 2 files changed, 58 insertions(+), 11 deletions(-) diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 709173526..0d86edcdb 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -48,6 +48,10 @@ ## * markdown code blocks ## * markdown links ## * markdown headlines +## * using ``1`` as auto-enumerator in enumerated lists like RST ``#`` +## (auto-enumerator ``1`` can not be used with ``#`` in the same list) +## +## **Note:** By default nim has ``roSupportMarkdown`` turned **on**. ## ## Limitations: ## @@ -1195,7 +1199,8 @@ proc whichSection(p: RstParser): RstNodeKind = elif match(p, p.idx, ":w:") and predNL(p): # (currentTok(p).symbol == ":") result = rnFieldList - elif match(p, p.idx, "(e) ") or match(p, p.idx, "e. "): + elif match(p, p.idx, "(e) ") or match(p, p.idx, "e) ") or + match(p, p.idx, "e. "): result = rnEnumList elif isDefList(p): result = rnDefList @@ -1537,8 +1542,12 @@ proc parseEnumList(p: var RstParser): PRstNode = if match(p, p.idx, wildcards[w]): break inc w assert w < wildcards.len + let autoEnums = if roSupportMarkdown in p.s.options: @["#", "1"] else: @["#"] + var prevAE = "" # so as not allow mixing auto-enumerators `1` and `#` + var curEnum = 1 for i in 0 ..< wildToken[w]-1: # add first enumerator with (, ), and . if p.tok[p.idx + i].symbol == "#": + prevAE = "#" result.text.add "1" else: result.text.add p.tok[p.idx + i].symbol @@ -1556,17 +1565,19 @@ proc parseEnumList(p: var RstParser): PRstNode = # check that it's in sequence: enumerator == next(prevEnum) if "n" in wildcards[w]: # arabic numeral let prevEnumI = try: parseInt(prevEnum) except: 1 - let curEnum = - if enumerator == "#": prevEnumI + 1 - else: (try: parseInt(enumerator) except: 1) + if enumerator in autoEnums: + if prevAE != "" and enumerator != prevAE: + break + prevAE = enumerator + curEnum = prevEnumI + 1 + else: curEnum = (try: parseInt(enumerator) except: 1) if curEnum - prevEnumI != 1: break prevEnum = enumerator else: # a..z let prevEnumI = ord(prevEnum[0]) - let curEnum = - if enumerator == "#": prevEnumI + 1 - else: ord(enumerator[0]) + if enumerator == "#": curEnum = prevEnumI + 1 + else: curEnum = ord(enumerator[0]) if curEnum - prevEnumI != 1: break prevEnum = $chr(curEnum) diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim index f3a8aa59a..85a96056a 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -440,13 +440,30 @@ Test1 Check that auto-numbered enumeration lists work. #. string1 + + #. string2 + + #. string3 + + #) string5 + #) string6 + """ + let output5 = rstToHtml(input5, {roSupportMarkdown}, defaultConfig()) + assert count(output5, "
                  ") == 2 + assert count(output5, "
                1. ") == 5 + + let input5a = dedent """ + Auto-numbered RST list can start with 1 even when Markdown support is on. + + 1. string1 #. string2 #. string3 """ - let output5 = rstToHtml(input5, {roSupportMarkdown}, defaultConfig()) - assert count(output5, "
                    ") == 1 - assert count(output5, "
                  1. ") == 3 + let output5a = rstToHtml(input5a, {roSupportMarkdown}, defaultConfig()) + assert count(output5a, "
                      ") == 1 + assert count(output5a, "
                    1. ") == 3 let input6 = dedent """ ... And for alphabetic enumerators too! @@ -474,6 +491,25 @@ Test1 assert count(output7, "
                    2. ") == 3 assert "start=\"3\"" in output7 and "class=\"upperalpha simple\"" in output7 + test "Markdown enumerated lists": + let input1 = dedent """ + Below are 2 enumerated lists: Markdown-style (5 items) and RST (1 item) + 1. line1 + 1. line2 + 1. line3 + 1. line4 + + 1. line5 + + #. lineA + """ + let output1 = rstToHtml(input1, {roSupportMarkdown}, defaultConfig()) + for i in 1..5: + assert ($i & ". line" & $i) notin output1 + assert ("
                    3. line" & $i & "
                    4. ") in output1 + assert count(output1, "
                        ") == 2 + test "RST bullet lists": let input1 = dedent """ * line1 From 051477b314acae9141eb697ff369385643f1bbbf Mon Sep 17 00:00:00 2001 From: Peter Salvi Date: Sun, 20 Dec 2020 13:09:35 +0100 Subject: [PATCH 1193/3768] O(1) concatenation of singly- and doubly linked lists. (#16362) * O(1) concatenation of singly- and doubly linked lists. There is also new `toSinglyLinkedList` and `toDoublyLinkedList` functions for conversion from `openArray`s, similarly to `toHashSet` or `toTable`. * Add `sequtils` import to runnable examples with `toSeq`. * Added missing call to runnable examples. * Add .since annotation, changelog, and tests. * Rename `lists.concat` as an overload to `lists.append`. * Renamed `append` to `add` in lists. * Remove faulty `add` for `DoublyLinkedList`s. * Improved tests for lists. * `lists.add` moves the second list; added `lists.copy` for shallow copy. * More tests for `lists.add` and `lists.copy`. * More compact tests for lists with templates. * List concatenation with copying (`add`) and moving (tentatively `addMove`) * Renamed `addMove` to `addMoved`; renamed arguments according to the style guide. * Added extended example to `lists.copy`. * Corrected .since annotations to 1.6 * Add .since annotation, changelog, and tests. * Rename `lists.concat` as an overload to `lists.append`. * Renamed `append` to `add` in lists. * Remove faulty `add` for `DoublyLinkedList`s. * `lists.add` moves the second list; added `lists.copy` for shallow copy. * More tests for `lists.add` and `lists.copy`. * List concatenation with copying (`add`) and moving (tentatively `addMove`) * Renamed `addMove` to `addMoved`; renamed arguments according to the style guide. * Since declarations changed to (1,5,1). * Add .since annotation, changelog, and tests. * Rename `lists.concat` as an overload to `lists.append`. * Renamed `append` to `add` in lists. * Remove faulty `add` for `DoublyLinkedList`s. * `lists.add` moves the second list; added `lists.copy` for shallow copy. * More tests for `lists.add` and `lists.copy`. * List concatenation with copying (`add`) and moving (tentatively `addMove`) * Renamed `addMove` to `addMoved`; renamed arguments according to the style guide. * Changelog update. * Fix rebasing errors. * Self-adding with `lists.addMove` results in a cycle now. Added some extra tests. --- changelog.md | 5 ++ lib/pure/collections/lists.nim | 146 +++++++++++++++++++++++++++++++++ tests/stdlib/tlists.nim | 69 +++++++++++++++- 3 files changed, 219 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 01eff1cd7..89d8fd423 100644 --- a/changelog.md +++ b/changelog.md @@ -66,6 +66,11 @@ - `echo` and `debugEcho` will now raise `IOError` if writing to stdout fails. Previous behavior silently ignored errors. See #16366. Use `-d:nimLegacyEchoNoRaise` for previous behavior. +- Added new operations for singly- and doubly linked lists: `lists.toSinglyLinkedList` + and `lists.toDoublyLinkedList` convert from `openArray`s; `lists.copy` implements + shallow copying; `lists.add` concatenates two lists - an O(1) variation that consumes + its argument, `addMoved`, is also supplied. + ## Language changes - `nimscript` now handles `except Exception as e`. diff --git a/lib/pure/collections/lists.nim b/lib/pure/collections/lists.nim index bd22949bf..8af0ed826 100644 --- a/lib/pure/collections/lists.nim +++ b/lib/pure/collections/lists.nim @@ -72,6 +72,7 @@ ## * `deques module `_ for double-ended queues ## * `sharedlist module `_ for shared singly-linked lists +import std/private/since when not defined(nimhygiene): {.pragma: dirty.} @@ -178,6 +179,26 @@ proc newSinglyLinkedNode*[T](value: T): (SinglyLinkedNode[T]) = new(result) result.value = value +func toSinglyLinkedList*[T](elems: openArray[T]): SinglyLinkedList[T] {.since: (1, 5, 1).} = + ## Creates a new `SinglyLinkedList` from members of `elems`. + runnableExamples: + import sequtils + let a = [1, 2, 3, 4, 5].toSinglyLinkedList + assert a.toSeq == [1, 2, 3, 4, 5] + result = initSinglyLinkedList[T]() + for elem in elems.items: + result.append(elem) + +func toDoublyLinkedList*[T](elems: openArray[T]): DoublyLinkedList[T] {.since: (1, 5, 1).} = + ## Creates a new `DoublyLinkedList` from members of `elems`. + runnableExamples: + import sequtils + let a = [1, 2, 3, 4, 5].toDoublyLinkedList + assert a.toSeq == [1, 2, 3, 4, 5] + result = initDoublyLinkedList[T]() + for elem in elems.items: + result.append(elem) + template itemsListImpl() {.dirty.} = var it = L.head while it != nil: @@ -435,7 +456,60 @@ proc prepend*[T](L: var SinglyLinkedList[T], value: T) {.inline.} = assert a.contains(9) prepend(L, newSinglyLinkedNode(value)) +func copy*[T](a: SinglyLinkedList[T]): SinglyLinkedList[T] {.since: (1, 5, 1).} = + ## Creates a shallow copy of `a`. + runnableExamples: + import sequtils + type Foo = ref object + x: int + var + f = Foo(x: 1) + a = [f].toSinglyLinkedList + let b = a.copy + a.add [f].toSinglyLinkedList + assert a.toSeq == [f, f] + assert b.toSeq == [f] # b isn't modified... + f.x = 42 + assert a.head.value.x == 42 + assert b.head.value.x == 42 # ... but the elements are not deep copied + let c = [1, 2, 3].toSinglyLinkedList + assert $c == $c.copy + result = initSinglyLinkedList[T]() + for x in a.items: + result.append(x) + +proc addMoved*[T](a, b: var SinglyLinkedList[T]) {.since: (1, 5, 1).} = + ## Moves `b` to the end of `a`. Efficiency: O(1). + ## Note that `b` becomes empty after the operation unless it has the same address as `a`. + ## Self-adding results in a cycle. + ## + ## See also: + ## * `add proc <#add,T,T>`_ + ## for adding a copy of a list + runnableExamples: + import sequtils, std/enumerate, std/sugar + var + a = [1, 2, 3].toSinglyLinkedList + b = [4, 5].toSinglyLinkedList + c = [0, 1].toSinglyLinkedList + a.addMoved b + assert a.toSeq == [1, 2, 3, 4, 5] + assert b.toSeq == [] + c.addMoved c + let s = collect: + for i, ci in enumerate(c): + if i == 6: break + ci + assert s == [0, 1, 0, 1, 0, 1] + if a.tail != nil: + a.tail.next = b.head + a.tail = b.tail + if a.head == nil: + a.head = b.head + if a.addr != b.addr: + b.head = nil + b.tail = nil proc append*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) = ## Appends (adds to the end) a node `n` to `L`. Efficiency: O(1). @@ -523,6 +597,78 @@ proc prepend*[T](L: var DoublyLinkedList[T], value: T) = assert a.contains(9) prepend(L, newDoublyLinkedNode(value)) +func copy*[T](a: DoublyLinkedList[T]): DoublyLinkedList[T] {.since: (1, 5, 1).} = + ## Creates a shallow copy of `a`. + runnableExamples: + type Foo = ref object + x: int + var f = Foo(x: 1) + let + a = [f].toDoublyLinkedList + b = a.copy + f.x = 42 + assert a.head.value.x == 42 + assert b.head.value.x == 42 + + let c = [1, 2, 3].toDoublyLinkedList + assert $c == $c.copy + result = initDoublyLinkedList[T]() + for x in a.items: + result.append(x) + +proc addMoved*[T](a, b: var DoublyLinkedList[T]) {.since: (1, 5, 1).} = + ## Moves `b` to the end of `a`. Efficiency: O(1). + ## Note that `b` becomes empty after the operation unless it has the same address as `a`. + ## Self-adding results in a cycle. + ## + ## See also: + ## * `add proc <#add,T,T>`_ + ## for adding a copy of a list + runnableExamples: + import sequtils, std/enumerate, std/sugar + var + a = [1, 2, 3].toDoublyLinkedList + b = [4, 5].toDoublyLinkedList + c = [0, 1].toDoublyLinkedList + a.addMoved b + assert a.toSeq == [1, 2, 3, 4, 5] + assert b.toSeq == [] + c.addMoved c + let s = collect: + for i, ci in enumerate(c): + if i == 6: break + ci + assert s == [0, 1, 0, 1, 0, 1] + if b.head != nil: + b.head.prev = a.tail + if a.tail != nil: + a.tail.next = b.head + a.tail = b.tail + if a.head == nil: + a.head = b.head + if a.addr != b.addr: + b.head = nil + b.tail = nil + +proc add*[T: SomeLinkedList](a: var T, b: T) {.since: (1, 5, 1).} = + ## Appends a shallow copy of `b` to the end of `a`. + ## + ## See also: + ## * `addMoved proc <#addMoved,SinglyLinkedList[T],SinglyLinkedList[T]>`_ + ## * `addMoved proc <#addMoved,DoublyLinkedList[T],DoublyLinkedList[T]>`_ + ## for moving the second list instead of copying + runnableExamples: + import sequtils + var a = [1, 2, 3].toSinglyLinkedList + let b = [4, 5].toSinglyLinkedList + a.add b + assert a.toSeq == [1, 2, 3, 4, 5] + assert b.toSeq == [4, 5] + a.add a + assert a.toSeq == [1, 2, 3, 4, 5, 1, 2, 3, 4, 5] + var tmp = b.copy + a.addMoved tmp + proc remove*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) = ## Removes a node `n` from `L`. Efficiency: O(1). runnableExamples: diff --git a/tests/stdlib/tlists.nim b/tests/stdlib/tlists.nim index fe814bb92..59ac8d7ee 100644 --- a/tests/stdlib/tlists.nim +++ b/tests/stdlib/tlists.nim @@ -2,7 +2,7 @@ discard """ targets: "c js" """ -import lists +import lists, sequtils, std/enumerate, std/sugar const data = [1, 2, 3, 4, 5, 6] @@ -81,3 +81,70 @@ block tlistsToString: l.append('2') l.append('3') doAssert $l == """['1', '2', '3']""" + +template testCommon(initList, toList) = + + block: # toSinglyLinkedList, toDoublyLinkedList + let l = seq[int].default + doAssert l.toList.toSeq == [] + doAssert [1].toList.toSeq == [1] + doAssert [1, 2, 3].toList.toSeq == [1, 2, 3] + + block copy: + doAssert array[0, int].default.toList.copy.toSeq == [] + doAssert [1].toList.copy.toSeq == [1] + doAssert [1, 2].toList.copy.toSeq == [1, 2] + doAssert [1, 2, 3].toList.copy.toSeq == [1, 2, 3] + type Foo = ref object + x: int + var f0 = Foo(x: 0) + let f1 = Foo(x: 1) + var a = [f0].toList + var b = a.copy + b.append f1 + doAssert a.toSeq == [f0] + doAssert b.toSeq == [f0, f1] + f0.x = 42 + assert a.head.value.x == 42 + assert b.head.value.x == 42 + + block: # add, addMoved + block: + var + l0 = initList[int]() + l1 = [1].toList + l2 = [2, 3].toList + l3 = [4, 5, 6].toList + l0.add l3 + l1.add l3 + l2.addMoved l3 + doAssert l0.toSeq == [4, 5, 6] + doAssert l1.toSeq == [1, 4, 5, 6] + doAssert l2.toSeq == [2, 3, 4, 5, 6] + doAssert l3.toSeq == [] + l2.add l3 # re-adding l3 that was destroyed is now a no-op + doAssert l2.toSeq == [2, 3, 4, 5, 6] + doAssert l3.toSeq == [] + block: + var + l0 = initList[int]() + l1 = [1].toList + l2 = [2, 3].toList + l3 = [4, 5, 6].toList + l3.addMoved l0 + l2.addMoved l1 + doAssert l3.toSeq == [4, 5, 6] + doAssert l2.toSeq == [2, 3, 1] + l3.add l0 + doAssert l3.toSeq == [4, 5, 6] + block: + var c = [0, 1].toList + c.addMoved c + let s = collect: + for i, ci in enumerate(c): + if i == 6: break + ci + doAssert s == [0, 1, 0, 1, 0, 1] + +testCommon initSinglyLinkedList, toSinglyLinkedList +testCommon initDoublyLinkedList, toDoublyLinkedList From d435bfcb3bf4882626686d53da238a38d4c753e1 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sun, 20 Dec 2020 20:59:32 -0600 Subject: [PATCH 1194/3768] disable ggplot and plotly (#16418) --- testament/important_packages.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index aae560939..ebe7bcf01 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -41,7 +41,7 @@ pkg1 "fidget" pkg1 "fragments", "nim c -r fragments/dsl.nim" pkg1 "gara" pkg1 "glob" -pkg1 "ggplotnim", "nim c -d:noCairo -r tests/tests.nim" +# pkg1 "ggplotnim", "nim c -d:noCairo -r tests/tests.nim" # pending bug #16418 # pkg1 "gittyup", "nimble test", "https://github.com/disruptek/gittyup" pkg1 "gnuplot", "nim c gnuplot.nim" pkg1 "gram", "nim c -r --gc:arc --define:danger tests/test.nim", "https://github.com/disruptek/gram" @@ -99,7 +99,7 @@ pkg2 "optionsutils" pkg2 "ormin", "nim c -o:orminn ormin.nim" pkg2 "parsetoml" pkg2 "patty" -pkg2 "plotly", "nim c examples/all.nim" +# pkg2 "plotly", "nim c examples/all.nim" # pending bug #16418 pkg2 "pnm" pkg2 "polypbren" pkg2 "prologue", "nimble tcompile" From d00a4121297fbb8824cffeecd7aab132a5769b11 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sun, 20 Dec 2020 21:53:27 -0600 Subject: [PATCH 1195/3768] docs minor for math (#16407) --- lib/pure/math.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pure/math.nim b/lib/pure/math.nim index 5f3261f1a..0e6ffe053 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -144,8 +144,8 @@ func isNaN*(x: SomeFloat): bool {.inline, since: (1,5,1).} = doAssert NaN.isNaN doAssert not Inf.isNaN doAssert isNaN(Inf - Inf) - doAssert not isNan(3.1415926) - doAssert not isNan(0'f32) + doAssert not isNaN(3.1415926) + doAssert not isNaN(0'f32) template fn: untyped = result = x != x when nimvm: fn() From 29c9a1c14b67ababca7b8ad7f80a0b84d74cfc9b Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 21 Dec 2020 03:34:00 -0600 Subject: [PATCH 1196/3768] bring back important packages; closes #16419 (#16424) --- testament/important_packages.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index ebe7bcf01..aae560939 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -41,7 +41,7 @@ pkg1 "fidget" pkg1 "fragments", "nim c -r fragments/dsl.nim" pkg1 "gara" pkg1 "glob" -# pkg1 "ggplotnim", "nim c -d:noCairo -r tests/tests.nim" # pending bug #16418 +pkg1 "ggplotnim", "nim c -d:noCairo -r tests/tests.nim" # pkg1 "gittyup", "nimble test", "https://github.com/disruptek/gittyup" pkg1 "gnuplot", "nim c gnuplot.nim" pkg1 "gram", "nim c -r --gc:arc --define:danger tests/test.nim", "https://github.com/disruptek/gram" @@ -99,7 +99,7 @@ pkg2 "optionsutils" pkg2 "ormin", "nim c -o:orminn ormin.nim" pkg2 "parsetoml" pkg2 "patty" -# pkg2 "plotly", "nim c examples/all.nim" # pending bug #16418 +pkg2 "plotly", "nim c examples/all.nim" pkg2 "pnm" pkg2 "polypbren" pkg2 "prologue", "nimble tcompile" From 297c8e403d110dd872e070563328f4e0c734cd01 Mon Sep 17 00:00:00 2001 From: ee7 <45465154+ee7@users.noreply.github.com> Date: Mon, 21 Dec 2020 18:41:56 +0100 Subject: [PATCH 1197/3768] styleCheck: Fix some inconsistent identifiers (#16177) --- lib/experimental/diff.nim | 4 ++-- lib/js/dom.nim | 10 +++++----- lib/js/jsffi.nim | 4 ++-- lib/nimhcr.nim | 4 ++-- lib/pure/asyncmacro.nim | 2 +- lib/pure/concurrency/threadpool.nim | 24 +++++++++++------------ lib/pure/ioselects/ioselectors_poll.nim | 2 +- lib/pure/ioselects/ioselectors_select.nim | 2 +- lib/pure/memfiles.nim | 4 ++-- lib/pure/nativesockets.nim | 2 +- lib/pure/net.nim | 8 ++++---- lib/pure/punycode.nim | 8 ++++---- lib/pure/selectors.nim | 2 +- lib/pure/smtp.nim | 16 +++++++-------- lib/pure/ssl_certs.nim | 4 ++-- lib/pure/stats.nim | 4 ++-- lib/system/gc_regions.nim | 8 ++++---- lib/system/mm/go.nim | 4 ++-- lib/system/nimscript.nim | 2 +- lib/system/sysspawn.nim | 14 ++++++------- 20 files changed, 64 insertions(+), 64 deletions(-) diff --git a/lib/experimental/diff.nim b/lib/experimental/diff.nim index d1274d771..9a92aba72 100644 --- a/lib/experimental/diff.nim +++ b/lib/experimental/diff.nim @@ -138,7 +138,7 @@ proc sms(dataA: var DiffData; lowerA, upperA: int; dataB: DiffData; lowerB, uppe for D in 0 .. maxD: # Extend the forward path. - for k in countUp(downK - D, downK + D, 2): + for k in countup(downK - D, downK + D, 2): # find the only or better starting point var x: int if k == downK - D: @@ -164,7 +164,7 @@ proc sms(dataA: var DiffData; lowerA, upperA: int; dataB: DiffData; lowerB, uppe y: downVector[downOffset + k] - k) # Extend the reverse path. - for k in countUp(upK - D, upK + D, 2): + for k in countup(upK - D, upK + D, 2): # find the only or better starting point var x: int if k == upK + D: diff --git a/lib/js/dom.nim b/lib/js/dom.nim index 4dbb4f77a..d4e8ce86a 100644 --- a/lib/js/dom.nim +++ b/lib/js/dom.nim @@ -1460,8 +1460,8 @@ else: proc createTextNode*(d: Document, identifier: cstring): Node {.importcpp.} proc createComment*(d: Document, data: cstring): Node {.importcpp.} -proc setTimeout*(action: proc(); ms: int): Timeout {.importc, nodecl.} -proc clearTimeout*(t: Timeout) {.importc, nodecl.} +proc setTimeout*(action: proc(); ms: int): TimeOut {.importc, nodecl.} +proc clearTimeout*(t: TimeOut) {.importc, nodecl.} {.push importcpp.} @@ -1719,9 +1719,9 @@ proc offsetTop*(e: Node): int {.importcpp: "#.offsetTop", nodecl.} proc offsetLeft*(e: Node): int {.importcpp: "#.offsetLeft", nodecl.} since (1, 3): - func newDomParser*(): DOMParser {.importcpp: "new DOMParser()".} + func newDomParser*(): DomParser {.importcpp: "new DOMParser()".} ## DOM Parser constructor. - func parseFromString*(this: DOMParser; str: cstring; mimeType: cstring): Document {.importcpp.} + func parseFromString*(this: DomParser; str: cstring; mimeType: cstring): Document {.importcpp.} ## Parse from string to `Document`. proc newDomException*(): DomException {.importcpp: "new DomException()", constructor.} @@ -1733,7 +1733,7 @@ since (1, 3): proc newFileReader*(): FileReader {.importcpp: "new FileReader()", constructor.} ## File Reader constructor - proc error*(f: FileReader): DOMException {.importcpp: "#.error", nodecl.} + proc error*(f: FileReader): DomException {.importcpp: "#.error", nodecl.} ## https://developer.mozilla.org/en-US/docs/Web/API/FileReader/error proc readyState*(f: FileReader): FileReaderState {.importcpp: "#.readyState", nodecl.} ## https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readyState diff --git a/lib/js/jsffi.nim b/lib/js/jsffi.nim index 5140ff372..9fe934f89 100644 --- a/lib/js/jsffi.nim +++ b/lib/js/jsffi.nim @@ -380,7 +380,7 @@ iterator pairs*[K: JsKey, V](assoc: JsAssoc[K, V]): (K,V) = yield (k.toJsKey(K), v) {.emit: "}".} -iterator items*[K, V](assoc: JSAssoc[K, V]): V = +iterator items*[K, V](assoc: JsAssoc[K, V]): V = ## Yields the `values` in a JsAssoc. var v: V {.emit: "for (var k in `assoc`) {".} @@ -389,7 +389,7 @@ iterator items*[K, V](assoc: JSAssoc[K, V]): V = yield v {.emit: "}".} -iterator keys*[K: JsKey, V](assoc: JSAssoc[K, V]): K = +iterator keys*[K: JsKey, V](assoc: JsAssoc[K, V]): K = ## Yields the `keys` in a JsAssoc. var k: cstring {.emit: "for (var `k` in `assoc`) {".} diff --git a/lib/nimhcr.nim b/lib/nimhcr.nim index 89bece6dc..fcc0ffdbe 100644 --- a/lib/nimhcr.nim +++ b/lib/nimhcr.nim @@ -230,7 +230,7 @@ when defined(createNimHcr): when defined(testNimHcr): return ($arg).splitFile.name.splitFile.name else: return $arg - {.pragma: nimhcr, compilerProc, exportc, dynlib.} + {.pragma: nimhcr, compilerproc, exportc, dynlib.} # XXX these types are CPU specific and need ARM etc support type @@ -622,7 +622,7 @@ elif defined(hotcodereloading) or defined(testNimHcr): elif defined(macosx): "libnimhcr." & dllExt else: "libnimhcr." & dllExt - {.pragma: nimhcr, compilerProc, importc, dynlib: nimhcrLibname.} + {.pragma: nimhcr, compilerproc, importc, dynlib: nimhcrLibname.} proc hcrRegisterProc*(module: cstring, name: cstring, fn: pointer): pointer {.nimhcr.} diff --git a/lib/pure/asyncmacro.nim b/lib/pure/asyncmacro.nim index dd2393a63..c60c5e4ca 100644 --- a/lib/pure/asyncmacro.nim +++ b/lib/pure/asyncmacro.nim @@ -182,7 +182,7 @@ proc asyncSingleProc(prc: NimNode): NimNode {.compileTime.} = elif returnType.kind == nnkEmpty: baseType = returnType else: - verifyReturnType(repr(returnType), returntype) + verifyReturnType(repr(returnType), returnType) let subtypeIsVoid = returnType.kind == nnkEmpty or (baseType.kind == nnkIdent and returnType[1].eqIdent("void")) diff --git a/lib/pure/concurrency/threadpool.nim b/lib/pure/concurrency/threadpool.nim index 34a65ead2..a4460165d 100644 --- a/lib/pure/concurrency/threadpool.nim +++ b/lib/pure/concurrency/threadpool.nim @@ -54,7 +54,7 @@ proc signal(cv: var Semaphore) = const CacheLineSize = 32 # true for most archs type - Barrier {.compilerProc.} = object + Barrier {.compilerproc.} = object entered: int cv: Semaphore # Semaphore takes 3 words at least when sizeof(int) < 8: @@ -63,26 +63,26 @@ type cacheAlign2: array[CacheLineSize-sizeof(int), byte] interest: bool # whether the master is interested in the "all done" event -proc barrierEnter(b: ptr Barrier) {.compilerProc, inline.} = +proc barrierEnter(b: ptr Barrier) {.compilerproc, inline.} = # due to the signaling between threads, it is ensured we are the only # one with access to 'entered' so we don't need 'atomicInc' here: inc b.entered # also we need no 'fence' instructions here as soon 'nimArgsPassingDone' # will be called which already will perform a fence for us. -proc barrierLeave(b: ptr Barrier) {.compilerProc, inline.} = +proc barrierLeave(b: ptr Barrier) {.compilerproc, inline.} = atomicInc b.left when not defined(x86): fence() # We may not have seen the final value of b.entered yet, # so we need to check for >= instead of ==. if b.interest and b.left >= b.entered: signal(b.cv) -proc openBarrier(b: ptr Barrier) {.compilerProc, inline.} = +proc openBarrier(b: ptr Barrier) {.compilerproc, inline.} = b.entered = 0 b.left = 0 b.interest = false -proc closeBarrier(b: ptr Barrier) {.compilerProc.} = +proc closeBarrier(b: ptr Barrier) {.compilerproc.} = fence() if b.left != b.entered: b.cv.initSemaphore() @@ -114,7 +114,7 @@ type FlowVarObj[T] = object of FlowVarBaseObj blob: T - FlowVar*{.compilerProc.}[T] = ref FlowVarObj[T] ## A data flow variable. + FlowVar*{.compilerproc.}[T] = ref FlowVarObj[T] ## A data flow variable. ToFreeQueue = object len: int @@ -212,14 +212,14 @@ proc `=destroy`[T](fv: var FlowVarObj[T]) = finished(fv) `=destroy`(fv.blob) -proc nimCreateFlowVar[T](): FlowVar[T] {.compilerProc.} = +proc nimCreateFlowVar[T](): FlowVar[T] {.compilerproc.} = new(result) -proc nimFlowVarCreateSemaphore(fv: FlowVarBase) {.compilerProc.} = +proc nimFlowVarCreateSemaphore(fv: FlowVarBase) {.compilerproc.} = fv.cv.initSemaphore() fv.usesSemaphore = true -proc nimFlowVarSignal(fv: FlowVarBase) {.compilerProc.} = +proc nimFlowVarSignal(fv: FlowVarBase) {.compilerproc.} = if fv.ai != nil: acquire(fv.ai.cv.L) fv.ai.idx = fv.idx @@ -305,7 +305,7 @@ proc isReady*(fv: FlowVarBase): bool = else: result = true -proc nimArgsPassingDone(p: pointer) {.compilerProc.} = +proc nimArgsPassingDone(p: pointer) {.compilerproc.} = let w = cast[ptr Worker](p) signal(w.taskStarted) @@ -489,7 +489,7 @@ var initLock stateLock -proc nimSpawn3(fn: WorkerProc; data: pointer) {.compilerProc.} = +proc nimSpawn3(fn: WorkerProc; data: pointer) {.compilerproc.} = # implementation of 'spawn' that is used by the code generator. while true: if selectWorker(readyWorker, fn, data): return @@ -574,7 +574,7 @@ var initLock distinguishedLock -proc nimSpawn4(fn: WorkerProc; data: pointer; id: ThreadId) {.compilerProc.} = +proc nimSpawn4(fn: WorkerProc; data: pointer; id: ThreadId) {.compilerproc.} = acquire(distinguishedLock) if not distinguishedData[id].initialized: activateDistinguishedThread(id) diff --git a/lib/pure/ioselects/ioselectors_poll.nim b/lib/pure/ioselects/ioselectors_poll.nim index 00e2f3fe9..6b10e19ae 100644 --- a/lib/pure/ioselects/ioselectors_poll.nim +++ b/lib/pure/ioselects/ioselectors_poll.nim @@ -209,7 +209,7 @@ proc close*(ev: SelectEvent) = raiseIOSelectorsError(osLastError()) proc selectInto*[T](s: Selector[T], timeout: int, - results: var openarray[ReadyKey]): int = + results: var openArray[ReadyKey]): int = var maxres = MAX_POLL_EVENTS if maxres > len(results): maxres = len(results) diff --git a/lib/pure/ioselects/ioselectors_select.nim b/lib/pure/ioselects/ioselectors_select.nim index 2fd9ac0ba..6f216ac85 100644 --- a/lib/pure/ioselects/ioselectors_select.nim +++ b/lib/pure/ioselects/ioselectors_select.nim @@ -305,7 +305,7 @@ proc unregister*[T](s: Selector[T], ev: SelectEvent) = s.delKey(fd) proc selectInto*[T](s: Selector[T], timeout: int, - results: var openarray[ReadyKey]): int = + results: var openArray[ReadyKey]): int = var tv = Timeval() var ptv = addr tv var rset, wset, eset: FdSet diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim index 09db80936..5e6571e11 100644 --- a/lib/pure/memfiles.nim +++ b/lib/pure/memfiles.nim @@ -232,8 +232,8 @@ proc open*(filename: string, mode: FileMode = fmRead, if newFileSize != -1: flags = flags or O_CREAT or O_TRUNC - var permissions_mode = S_IRUSR or S_IWUSR - result.handle = open(filename, flags, permissions_mode) + var permissionsMode = S_IRUSR or S_IWUSR + result.handle = open(filename, flags, permissionsMode) else: result.handle = open(filename, flags) diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim index 1f597f8eb..f1445fca5 100644 --- a/lib/pure/nativesockets.nim +++ b/lib/pure/nativesockets.nim @@ -209,7 +209,7 @@ proc getProtoByName*(name: string): int {.since: (1, 3, 5).} = let protoent = posix.getprotobyname(name.cstring) if protoent == nil: - raise newException(OsError, "protocol not found") + raise newException(OSError, "protocol not found") result = protoent.p_proto.int diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 02441e8a0..7b92a5e0d 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -551,7 +551,7 @@ when defineSsl: proc newContext*(protVersion = protSSLv23, verifyMode = CVerifyPeer, certFile = "", keyFile = "", cipherList = CiphersIntermediate, - caDir = "", caFile = ""): SSLContext = + caDir = "", caFile = ""): SslContext = ## Creates an SSL context. ## ## Protocol version specifies the protocol to use. SSLv2, SSLv3, TLSv1 @@ -644,7 +644,7 @@ when defineSsl: if not found: raise newException(IOError, "No SSL/TLS CA certificates found.") - result = SSLContext(context: newCTX, referencedData: initHashSet[int](), + result = SslContext(context: newCTX, referencedData: initHashSet[int](), extraInternal: new(SslContextExtraInternal)) proc getExtraInternal(ctx: SslContext): SslContextExtraInternal = @@ -750,7 +750,7 @@ when defineSsl: ## Wildcards match only in the left-most label. ## When name starts with a dot it will be matched by a certificate valid for any subdomain when not defined(nimDisableCertificateValidation) and not defined(windows): - assert socket.isSSL + assert socket.isSsl let certificate = socket.sslHandle.SSL_get_peer_certificate() if certificate.isNil: raiseSSLError("No SSL certificate found.") @@ -763,7 +763,7 @@ when defineSsl: if match != 1: raiseSSLError("SSL Certificate check failed.") - proc wrapConnectedSocket*(ctx: SSLContext, socket: Socket, + proc wrapConnectedSocket*(ctx: SslContext, socket: Socket, handshake: SslHandshakeType, hostname: string = "") = ## Wraps a connected socket in an SSL context. This function effectively diff --git a/lib/pure/punycode.nim b/lib/pure/punycode.nim index e9e11a1c3..dfeeea35e 100644 --- a/lib/pure/punycode.nim +++ b/lib/pure/punycode.nim @@ -161,13 +161,13 @@ proc decode*(encoded: string): string {.raises: [PunyError].} = break w *= Base - t k += Base - bias = adapt(i - oldi, runelen(result) + 1, oldi == 0) + bias = adapt(i - oldi, runeLen(result) + 1, oldi == 0) - if i div (runelen(result) + 1) > high(int32) - n: + if i div (runeLen(result) + 1) > high(int32) - n: raise newException(PunyError, "Value too large") - n += i div (runelen(result) + 1) - i = i mod (runelen(result) + 1) + n += i div (runeLen(result) + 1) + i = i mod (runeLen(result) + 1) insert(result, $Rune(n), i) inc i diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim index 933b5491a..20bf1b3b0 100644 --- a/lib/pure/selectors.nim +++ b/lib/pure/selectors.nim @@ -162,7 +162,7 @@ when defined(nimdoc): ## Unregisters file/socket descriptor ``fd`` from selector ``s``. proc selectInto*[T](s: Selector[T], timeout: int, - results: var openarray[ReadyKey]): int = + results: var openArray[ReadyKey]): int = ## Waits for events registered in selector ``s``. ## ## The ``timeout`` argument specifies the maximum number of milliseconds diff --git a/lib/pure/smtp.nim b/lib/pure/smtp.nim index c0b6665b2..66d181d40 100644 --- a/lib/pure/smtp.nim +++ b/lib/pure/smtp.nim @@ -112,17 +112,17 @@ proc quitExcpt(smtp: Smtp, msg: string) = const compiledWithSsl = defined(ssl) when not defined(ssl): - let defaultSSLContext: SSLContext = nil + let defaultSSLContext: SslContext = nil else: - var defaultSSLContext {.threadvar.}: SSLContext + var defaultSSLContext {.threadvar.}: SslContext - proc getSSLContext(): SSLContext = + proc getSSLContext(): SslContext = if defaultSSLContext == nil: defaultSSLContext = newContext(verifyMode = CVerifyNone) result = defaultSSLContext proc createMessage*(mSubject, mBody: string, mTo, mCc: seq[string], - otherHeaders: openarray[tuple[name, value: string]]): Message = + otherHeaders: openArray[tuple[name, value: string]]): Message = ## Creates a new MIME compliant message. ## ## You need to make sure that ``mSubject``, ``mTo`` and ``mCc`` don't contain @@ -172,7 +172,7 @@ proc `$`*(msg: Message): string = result.add(msg.msgBody) proc newSmtp*(useSsl = false, debug = false, - sslContext: SSLContext = nil): Smtp = + sslContext: SslContext = nil): Smtp = ## Creates a new ``Smtp`` instance. new result result.debug = debug @@ -187,7 +187,7 @@ proc newSmtp*(useSsl = false, debug = false, {.error: "SMTP module compiled without SSL support".} proc newAsyncSmtp*(useSsl = false, debug = false, - sslContext: SSLContext = nil): AsyncSmtp = + sslContext: SslContext = nil): AsyncSmtp = ## Creates a new ``AsyncSmtp`` instance. new result result.debug = debug @@ -222,7 +222,7 @@ proc checkReply*(smtp: Smtp | AsyncSmtp, reply: string) {.multisync.} = ## `SMTP extensions`_. var line = await smtp.debugRecv() - if not line.startswith(reply): + if not line.startsWith(reply): await quitExcpt(smtp, "Expected " & reply & " reply, got: " & line) proc helo*(smtp: Smtp | AsyncSmtp) {.multisync.} = @@ -239,7 +239,7 @@ proc connect*(smtp: Smtp | AsyncSmtp, await smtp.checkReply("220") await smtp.helo() -proc startTls*(smtp: Smtp | AsyncSmtp, sslContext: SSLContext = nil) {.multisync.} = +proc startTls*(smtp: Smtp | AsyncSmtp, sslContext: SslContext = nil) {.multisync.} = ## Put the SMTP connection in TLS (Transport Layer Security) mode. ## May fail with ReplyError await smtp.debugSend("STARTTLS\c\L") diff --git a/lib/pure/ssl_certs.nim b/lib/pure/ssl_certs.nim index d04244307..c1003f445 100644 --- a/lib/pure/ssl_certs.nim +++ b/lib/pure/ssl_certs.nim @@ -20,7 +20,7 @@ import strutils # FWIW look for files before scanning entire dirs. -const certificate_paths = [ +const certificatePaths = [ # Debian, Ubuntu, Arch: maintained by update-ca-certificates, SUSE, Gentoo # NetBSD (security/mozilla-rootcerts) # SLES10/SLES11, https://golang.org/issue/12139 @@ -77,7 +77,7 @@ iterator scanSSLCertificates*(useEnvVars = false): string = else: when not defined(haiku): - for p in certificate_paths: + for p in certificatePaths: if p.endsWith(".pem") or p.endsWith(".crt"): if fileExists(p): yield p diff --git a/lib/pure/stats.nim b/lib/pure/stats.nim index 20fd1fdcd..af6cffef2 100644 --- a/lib/pure/stats.nim +++ b/lib/pure/stats.nim @@ -103,7 +103,7 @@ proc push*(s: var RunningStat, x: int) = ## and the other push operation is called. s.push(toFloat(x)) -proc push*(s: var RunningStat, x: openarray[float|int]) = +proc push*(s: var RunningStat, x: openArray[float|int]) = ## pushes all values of `x` for processing. ## ## Int values of `x` are simply converted to ``float`` and @@ -277,7 +277,7 @@ proc push*(r: var RunningRegress, x, y: int) {.inline.} = ## and the other push operation is called. r.push(toFloat(x), toFloat(y)) -proc push*(r: var RunningRegress, x, y: openarray[float|int]) = +proc push*(r: var RunningRegress, x, y: openArray[float|int]) = ## pushes two sets of values `x` and `y` for processing. assert(x.len == y.len) for i in 0.. Date: Mon, 21 Dec 2020 17:05:21 -0800 Subject: [PATCH 1198/3768] [backport => 1.0] fix #16428 vmops now works for generic procs (#16429) * fix #16428 vmops now works for generic procs * remove duplication --- compiler/vmgen.nim | 12 ++---------- lib/pure/math.nim | 2 ++ tests/vm/tvmops.nim | 4 ++++ 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 9086b17d9..2fbb78c8f 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1954,14 +1954,6 @@ proc genProc*(c: PCtx; s: PSym): int proc matches(s: PSym; x: string): bool = let y = x.split('.') - var s = s - for i in 1..y.len: - if s == nil or (y[^i].cmpIgnoreStyle(s.name.s) != 0 and y[^i] != "*"): - return false - s = s.owner - result = true - -proc matches(s: PSym; y: varargs[string]): bool = var s = s for i in 1..y.len: if s == nil or (y[^i].cmpIgnoreStyle(s.name.s) != 0 and y[^i] != "*"): @@ -2024,11 +2016,11 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = elif s.kind == skMethod: localError(c.config, n.info, "cannot call method " & s.name.s & " at compile time") - elif matches(s, "stdlib", "marshal", "to"): + elif matches(s, "stdlib.marshal.to"): # XXX marshal load&store should not be opcodes, but use the # general callback mechanisms. genMarshalLoad(c, n, dest) - elif matches(s, "stdlib", "marshal", "$$"): + elif matches(s, "stdlib.marshal.$$"): genMarshalStore(c, n, dest) else: genCall(c, n, dest) diff --git a/lib/pure/math.nim b/lib/pure/math.nim index 0e6ffe053..c05e47545 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -567,6 +567,8 @@ else: # JS func tanh*[T: float32|float64](x: T): T {.importc: "Math.tanh", nodecl.} func arcsin*[T: float32|float64](x: T): T {.importc: "Math.asin", nodecl.} + # keep this as generic or update test in `tvmops.nim` to make sure we + # keep testing that generic importc procs work func arccos*[T: float32|float64](x: T): T {.importc: "Math.acos", nodecl.} func arctan*[T: float32|float64](x: T): T {.importc: "Math.atan", nodecl.} func arctan2*[T: float32|float64](y, x: T): T {.importc: "Math.atan2", nodecl.} diff --git a/tests/vm/tvmops.nim b/tests/vm/tvmops.nim index 6442c49d6..c8febd982 100644 --- a/tests/vm/tvmops.nim +++ b/tests/vm/tvmops.nim @@ -1,3 +1,7 @@ +discard """ + targets: "c cpp js" +""" + #[ test for vmops.nim ]# From 7256afb00d4451f96b525039b39109d21561a14d Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Tue, 22 Dec 2020 04:18:13 -0600 Subject: [PATCH 1199/3768] mark abs as inline (#16432) --- lib/system.nim | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/system.nim b/lib/system.nim index 0797b7caf..85ef15e08 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2029,15 +2029,15 @@ proc getTypeInfo*[T](x: T): pointer {.magic: "GetTypeInfo", benign.} ## `_ instead. {.push stackTrace: off.} -proc abs*(x: int): int {.magic: "AbsI", noSideEffect.} = +func abs*(x: int): int {.magic: "AbsI", inline.} = if x < 0: -x else: x -proc abs*(x: int8): int8 {.magic: "AbsI", noSideEffect.} = +func abs*(x: int8): int8 {.magic: "AbsI", inline.} = if x < 0: -x else: x -proc abs*(x: int16): int16 {.magic: "AbsI", noSideEffect.} = +func abs*(x: int16): int16 {.magic: "AbsI", inline.} = if x < 0: -x else: x -proc abs*(x: int32): int32 {.magic: "AbsI", noSideEffect.} = +func abs*(x: int32): int32 {.magic: "AbsI", inline.} = if x < 0: -x else: x -proc abs*(x: int64): int64 {.magic: "AbsI", noSideEffect.} = +func abs*(x: int64): int64 {.magic: "AbsI", inline.} = ## Returns the absolute value of `x`. ## ## If `x` is ``low(x)`` (that is -MININT for its type), From 6e2c2d11db1c753c7129337ac0453ee42b57ff3d Mon Sep 17 00:00:00 2001 From: shadowninja55 <49539636+shadowninja55@users.noreply.github.com> Date: Tue, 22 Dec 2020 21:23:45 -0500 Subject: [PATCH 1200/3768] add sequtils to prelude (#16431) * add sequtils to prelude i would argue that sequtils is used just as often as the other imports in prelude, and it'd be nice for it to be included. * updated doc comment to add sequtils * added sequtils import to changelog --- changelog.md | 2 ++ lib/prelude.nim | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index 89d8fd423..c2a146c01 100644 --- a/changelog.md +++ b/changelog.md @@ -70,6 +70,8 @@ and `lists.toDoublyLinkedList` convert from `openArray`s; `lists.copy` implements shallow copying; `lists.add` concatenates two lists - an O(1) variation that consumes its argument, `addMoved`, is also supplied. + +- Added `sequtils` import to `prelude`. ## Language changes diff --git a/lib/prelude.nim b/lib/prelude.nim index 47a5af89e..4d8c7d7f0 100644 --- a/lib/prelude.nim +++ b/lib/prelude.nim @@ -16,8 +16,8 @@ ## Same as: ## ## .. code-block:: nim -## import os, strutils, times, parseutils, hashes, tables, sets +## import os, strutils, times, parseutils, hashes, tables, sets, sequtils ## when not defined(js): import parseopt -import os, strutils, times, parseutils, hashes, tables, sets +import os, strutils, times, parseutils, hashes, tables, sets, sequtils when not defined(js): import parseopt From c8fadf2c749604fe7a4a4ca2e41f74bded3daa10 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 22 Dec 2020 19:12:35 -0800 Subject: [PATCH 1201/3768] disable pkg/criterion refs https://github.com/disruptek/criterion/issues/3 (#16443) --- testament/important_packages.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index aae560939..f57045298 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -31,7 +31,7 @@ pkg1 "cligen", "nim c --path:. -r cligen.nim" pkg1 "combparser", "nimble test --gc:orc" pkg1 "compactdict" pkg1 "comprehension", "nimble test", "https://github.com/alehander42/comprehension" -pkg1 "criterion" +# pkg1 "criterion" # pending https://github.com/disruptek/criterion/issues/3 (wrongly closed) pkg1 "dashing", "nim c tests/functional.nim" pkg1 "delaunay" pkg1 "docopt" From 8d913a5921d7245ad80e92a040b5bc8ba6f97f44 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Tue, 22 Dec 2020 22:27:34 -0600 Subject: [PATCH 1202/3768] add zippy to important packages (#16442) * add zippy to important packages * minor --- testament/important_packages.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index f57045298..5a9775bad 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -136,3 +136,4 @@ pkg2 "with" pkg2 "ws" pkg2 "yaml", "nim build" pkg2 "zero_functional", "nim c -r -d:nimWorkaround14447 test.nim" +pkg2 "zippy" From 417c2509c42367edc4130a8761fae80d529d8574 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Wed, 23 Dec 2020 04:57:48 -0600 Subject: [PATCH 1203/3768] strip minor improvement (#16444) * strip minor improvement * add more tests * Update tests/stdlib/tstrutils.nim Co-authored-by: Timothee Cour --- lib/pure/strutils.nim | 2 +- tests/stdlib/tstrutils.nim | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 04ba71315..0028ac4fb 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -2813,7 +2813,7 @@ func strip*(s: string, leading = true, trailing = true, if leading: while first <= last and s[first] in chars: inc(first) if trailing: - while last >= 0 and s[last] in chars: dec(last) + while last >= first and s[last] in chars: dec(last) result = substr(s, first, last) func stripLineEnd*(s: var string) = diff --git a/tests/stdlib/tstrutils.nim b/tests/stdlib/tstrutils.nim index b62c82ffb..8d6fe75ae 100644 --- a/tests/stdlib/tstrutils.nim +++ b/tests/stdlib/tstrutils.nim @@ -28,6 +28,19 @@ template main() = doAssert strip("sfoofoofoos", leading = false, chars = {'s'}) == "sfoofoofoo" doAssert strip("sfoofoofoos", trailing = false, chars = {'s'}) == "foofoofoos" + block: + let a = "xxxxxx" + doAssert a.strip(chars={'x'}).len == 0 + + doAssert "".strip(chars={'x'}).len == 0 + doAssert " ".strip(chars={'x'}) == " " + doAssert "xxx xxx".strip(chars={'x'}) == " " + doAssert "xxx wind".strip(chars={'x'}) == " wind" + doAssert "xxx iii".strip(chars={'i'}) == "xxx " + doAssert "x".strip(leading = false, chars={'x'}).len == 0 + doAssert "x".strip(trailing = false, chars={'x'}).len == 0 + doAssert "x".strip(leading = false, trailing = false, chars={'x'}) == "x" + block: # split var ret: seq[string] # or use `toSeq` or `collect` for p in split("/home/a1:xyz:/usr/bin", {':'}): ret.add p From b1c3dab2089bb4327627c2152a1148b299f1a930 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Wed, 23 Dec 2020 05:04:38 -0600 Subject: [PATCH 1204/3768] add `euclDiv` and `euclMod` to `math` (#16414) * add `euclDiv` and `euclMod` to `math` * use abs * Update lib/pure/math.nim Co-authored-by: Clyybber --- changelog.md | 2 ++ lib/pure/math.nim | 26 ++++++++++++++++++++++++++ tests/stdlib/tmath.nim | 28 ++++++++++++++++++++++++++++ 3 files changed, 56 insertions(+) diff --git a/changelog.md b/changelog.md index c2a146c01..a74e06373 100644 --- a/changelog.md +++ b/changelog.md @@ -73,6 +73,8 @@ - Added `sequtils` import to `prelude`. +- Added `euclDiv` and `euclMod` to `math`. + ## Language changes - `nimscript` now handles `except Exception as e`. diff --git a/lib/pure/math.nim b/lib/pure/math.nim index c05e47545..5b19a8ec0 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -882,6 +882,32 @@ func floorMod*[T: SomeNumber](x, y: T): T = result = x mod y if (result > 0 and y < 0) or (result < 0 and y > 0): result += y +func euclDiv*[T: SomeInteger](x, y: T): T {.since: (1, 5, 1).} = + ## Returns euclidean division of `x` by `y`. + runnableExamples: + assert euclDiv(13, 3) == 4 + assert euclDiv(-13, 3) == -5 + assert euclDiv(13, -3) == -4 + assert euclDiv(-13, -3) == 5 + result = x div y + if x mod y < 0: + if y > 0: + dec result + else: + inc result + +func euclMod*[T: SomeNumber](x, y: T): T {.since: (1, 5, 1).} = + ## Returns euclidean modulo of `x` by `y`. + ## `euclMod(x, y)` is non-negative. + runnableExamples: + assert euclMod(13, 3) == 1 + assert euclMod(-13, 3) == 2 + assert euclMod(13, -3) == 1 + assert euclMod(-13, -3) == 2 + result = x mod y + if result < 0: + result += abs(y) + when not defined(js): func c_frexp*(x: float32, exponent: var int32): float32 {. importc: "frexp", header: "".} diff --git a/tests/stdlib/tmath.nim b/tests/stdlib/tmath.nim index 18e42cbdc..43d19f9e0 100644 --- a/tests/stdlib/tmath.nim +++ b/tests/stdlib/tmath.nim @@ -270,6 +270,34 @@ block: doAssert floorMod(8.0, -3.0) ==~ -1.0 doAssert floorMod(-8.5, 3.0) ==~ 0.5 + block: # euclDiv/euclMod + doAssert euclDiv(8, 3) == 2 + doAssert euclMod(8, 3) == 2 + + doAssert euclDiv(8, -3) == -2 + doAssert euclMod(8, -3) == 2 + + doAssert euclDiv(-8, 3) == -3 + doAssert euclMod(-8, 3) == 1 + + doAssert euclDiv(-8, -3) == 3 + doAssert euclMod(-8, -3) == 1 + + doAssert euclMod(8.0, -3.0) ==~ 2.0 + doAssert euclMod(-8.5, 3.0) ==~ 0.5 + + doAssert euclDiv(9, 3) == 3 + doAssert euclMod(9, 3) == 0 + + doAssert euclDiv(9, -3) == -3 + doAssert euclMod(9, -3) == 0 + + doAssert euclDiv(-9, 3) == -3 + doAssert euclMod(-9, 3) == 0 + + doAssert euclDiv(-9, -3) == 3 + doAssert euclMod(-9, -3) == 0 + block: # log doAssert log(4.0, 3.0) ==~ ln(4.0) / ln(3.0) doAssert log2(8.0'f64) == 3.0'f64 From a29bbeee41e43f00ba7a3b1951445ff5d21ccc52 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Wed, 23 Dec 2020 17:26:37 -0300 Subject: [PATCH 1205/3768] Add missing HTTP codes (#16454) --- changelog.md | 2 ++ lib/pure/httpcore.nim | 57 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index a74e06373..a6a865514 100644 --- a/changelog.md +++ b/changelog.md @@ -74,6 +74,8 @@ - Added `sequtils` import to `prelude`. - Added `euclDiv` and `euclMod` to `math`. +- Added `httpcore.is1xx` and missing HTTP codes. + ## Language changes diff --git a/lib/pure/httpcore.nim b/lib/pure/httpcore.nim index 7ead64d83..9287e9864 100644 --- a/lib/pure/httpcore.nim +++ b/lib/pure/httpcore.nim @@ -11,7 +11,7 @@ ## ``asynchttpserver`` modules. ## ## Unstable API. - +import std/private/since import tables, strutils, parseutils type @@ -52,6 +52,8 @@ type const Http100* = HttpCode(100) Http101* = HttpCode(101) + Http102* = HttpCode(102) ## https://tools.ietf.org/html/rfc2518.html WebDAV + Http103* = HttpCode(103) ## https://tools.ietf.org/html/rfc8297.html Early hints Http200* = HttpCode(200) Http201* = HttpCode(201) Http202* = HttpCode(202) @@ -59,6 +61,9 @@ const Http204* = HttpCode(204) Http205* = HttpCode(205) Http206* = HttpCode(206) + Http207* = HttpCode(207) ## https://tools.ietf.org/html/rfc4918.html WebDAV + Http208* = HttpCode(208) ## https://tools.ietf.org/html/rfc5842.html WebDAV, Section 7.1 + Http226* = HttpCode(226) ## https://tools.ietf.org/html/rfc3229.html Delta encoding, Section 10.4.1 Http300* = HttpCode(300) Http301* = HttpCode(301) Http302* = HttpCode(302) @@ -69,6 +74,7 @@ const Http308* = HttpCode(308) Http400* = HttpCode(400) Http401* = HttpCode(401) + Http402* = HttpCode(402) ## https://tools.ietf.org/html/rfc7231.html Payment required, Section 6.5.2 Http403* = HttpCode(403) Http404* = HttpCode(404) Http405* = HttpCode(405) @@ -87,6 +93,9 @@ const Http418* = HttpCode(418) Http421* = HttpCode(421) Http422* = HttpCode(422) + Http423* = HttpCode(423) ## https://tools.ietf.org/html/rfc4918.html WebDAV, Section 11.3 + Http424* = HttpCode(424) ## https://tools.ietf.org/html/rfc4918.html WebDAV, Section 11.3 + Http425* = HttpCode(425) ## https://tools.ietf.org/html/rfc8470.html Early data Http426* = HttpCode(426) Http428* = HttpCode(428) Http429* = HttpCode(429) @@ -98,6 +107,12 @@ const Http503* = HttpCode(503) Http504* = HttpCode(504) Http505* = HttpCode(505) + Http506* = HttpCode(506) ## https://tools.ietf.org/html/rfc2295.html Content negotiation, Section 8.1 + Http507* = HttpCode(507) ## https://tools.ietf.org/html/rfc4918.html WebDAV, Section 11.5 + Http508* = HttpCode(508) ## https://tools.ietf.org/html/rfc5842.html WebDAV, Section 7.2 + Http510* = HttpCode(510) ## https://tools.ietf.org/html/rfc2774.html Extension framework, Section 7 + Http511* = HttpCode(511) ## https://tools.ietf.org/html/rfc6585.html Additional status code, Section 6 + const httpNewLine* = "\c\L" const headerLimit* = 10_000 @@ -267,6 +282,8 @@ func `$`*(code: HttpCode): string = case code.int of 100: "100 Continue" of 101: "101 Switching Protocols" + of 102: "102 Processing" + of 103: "103 Early Hints" of 200: "200 OK" of 201: "201 Created" of 202: "202 Accepted" @@ -274,6 +291,9 @@ func `$`*(code: HttpCode): string = of 204: "204 No Content" of 205: "205 Reset Content" of 206: "206 Partial Content" + of 207: "207 Multi-Status" + of 208: "208 Already Reported" + of 226: "226 IM Used" of 300: "300 Multiple Choices" of 301: "301 Moved Permanently" of 302: "302 Found" @@ -284,6 +304,7 @@ func `$`*(code: HttpCode): string = of 308: "308 Permanent Redirect" of 400: "400 Bad Request" of 401: "401 Unauthorized" + of 402: "402 Payment Required" of 403: "403 Forbidden" of 404: "404 Not Found" of 405: "405 Method Not Allowed" @@ -302,6 +323,9 @@ func `$`*(code: HttpCode): string = of 418: "418 I'm a teapot" of 421: "421 Misdirected Request" of 422: "422 Unprocessable Entity" + of 423: "423 Locked" + of 424: "424 Failed Dependency" + of 425: "425 Too Early" of 426: "426 Upgrade Required" of 428: "428 Precondition Required" of 429: "429 Too Many Requests" @@ -313,6 +337,11 @@ func `$`*(code: HttpCode): string = of 503: "503 Service Unavailable" of 504: "504 Gateway Timeout" of 505: "505 HTTP Version Not Supported" + of 506: "506 Variant Also Negotiates" + of 507: "507 Insufficient Storage" + of 508: "508 Loop Detected" + of 510: "510 Not Extended" + of 511: "511 Network Authentication Required" else: $(int(code)) func `==`*(a, b: HttpCode): bool {.borrow.} @@ -327,6 +356,13 @@ proc `==`*(rawCode: string, code: HttpCode): bool ## string form of itself. return cmpIgnoreCase(rawCode, $code) == 0 +func is1xx*(code: HttpCode): bool {.inline, since: (1, 5).} = + ## Determines whether ``code`` is a 1xx HTTP status code. + runnableExamples: + doAssert is1xx(HttpCode(103)) + + code.int in {100 .. 199} + func is2xx*(code: HttpCode): bool {.inline.} = ## Determines whether ``code`` is a 2xx HTTP status code. code.int in {200 .. 299} @@ -343,5 +379,20 @@ func is5xx*(code: HttpCode): bool {.inline.} = ## Determines whether ``code`` is a 5xx HTTP status code. code.int in {500 .. 599} -func `$`*(httpMethod: HttpMethod): string = - return (system.`$`(httpMethod))[4 .. ^1].toUpperAscii() +func `$`*(httpMethod: HttpMethod): string {.inline.} = + runnableExamples: + doAssert $HttpHead == "HEAD" + doAssert $HttpPatch == "PATCH" + doAssert $HttpGet == "GET" + doAssert $HttpPost == "POST" + + result = case httpMethod + of HttpHead: "HEAD" + of HttpGet: "GET" + of HttpPost: "POST" + of HttpPut: "PUT" + of HttpDelete: "DELETE" + of HttpTrace: "TRACE" + of HttpOptions: "OPTIONS" + of HttpConnect: "CONNECT" + of HttpPatch: "PATCH" From e84354666ac141959104f50be3fb22f1932dbaf4 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 26 Dec 2020 14:28:56 -0800 Subject: [PATCH 1206/3768] fix #16346 rst2html now honors SuccessX (#16347) * fix #16346 SuccessX rst2html * cleanups * _ * _ * _ --- compiler/commands.nim | 7 +++++++ compiler/options.nim | 8 ++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/compiler/commands.nim b/compiler/commands.nim index 11bf628b4..4b8755ab4 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -439,6 +439,13 @@ proc setCmd*(conf: ConfigRef, cmd: Command) = proc setCommandEarly*(conf: ConfigRef, command: string) = conf.command = command setCmd(conf, command.parseCommand) + # command early customizations + # must be handled here to honor subsequent `--hint:x:on|off` + case conf.cmd + of cmdRst2html, cmdRst2tex: # xxx see whether to add others: cmdGendepend, etc. + conf.foreignPackageNotes = {hintSuccessX} + else: + conf.foreignPackageNotes = foreignPackageNotesDefault proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; conf: ConfigRef) = diff --git a/compiler/options.nim b/compiler/options.nim index 73c4c627d..a5f262f01 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -424,6 +424,8 @@ template newPackageCache*(): untyped = proc newProfileData(): ProfileData = ProfileData(data: newTable[TLineInfo, ProfileInfo]()) +const foreignPackageNotesDefault* = {hintProcessing, warnUnknownMagic, hintQuitCalled, hintExecuting} + proc newConfigRef*(): ConfigRef = result = ConfigRef( selectedGC: gcRefc, @@ -435,8 +437,7 @@ proc newConfigRef*(): ConfigRef = arcToExpand: newStringTable(modeStyleInsensitive), m: initMsgConfig(), cppDefines: initHashSet[string](), - headerFile: "", features: {}, legacyFeatures: {}, foreignPackageNotes: {hintProcessing, warnUnknownMagic, - hintQuitCalled, hintExecuting}, + headerFile: "", features: {}, legacyFeatures: {}, foreignPackageNotes: foreignPackageNotesDefault, notes: NotesVerbosity[1], mainPackageNotes: NotesVerbosity[1], configVars: newStringTable(modeStyleInsensitive), symbols: newStringTable(modeStyleInsensitive), @@ -490,8 +491,7 @@ proc newPartialConfigRef*(): ConfigRef = verbosity: 1, options: DefaultOptions, globalOptions: DefaultGlobalOptions, - foreignPackageNotes: {hintProcessing, warnUnknownMagic, - hintQuitCalled, hintExecuting}, + foreignPackageNotes: foreignPackageNotesDefault, notes: NotesVerbosity[1], mainPackageNotes: NotesVerbosity[1]) proc cppDefine*(c: ConfigRef; define: string) = From 1e859fa320e6f6a0fa3773c1ecf37707dd656b6c Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sun, 27 Dec 2020 02:04:58 -0600 Subject: [PATCH 1207/3768] minor (#16478) --- compiler/llstream.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/llstream.nim b/compiler/llstream.nim index 6df927c60..bd335c23d 100644 --- a/compiler/llstream.nim +++ b/compiler/llstream.nim @@ -110,7 +110,7 @@ proc llReadFromStdin(s: PLLStream, buf: pointer, bufLen: int): int = s.rd = 0 var line = newStringOfCap(120) var triples = 0 - while readLineFromStdin(if s.s.len == 0: ">>> " else: "... ", line): + while readLineFromStdin(if s.s.len == 0: "\n>>> " else: "... ", line): s.s.add(line) s.s.add("\n") inc triples, countTriples(line) From 1d615dfda7102c5d7f190b077c0ae87abac34228 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sun, 27 Dec 2020 02:16:53 -0600 Subject: [PATCH 1208/3768] fix #16474 `unittest.check type1 is type2` gives CT error (#16476) * fix #16474 * more tests --- lib/pure/unittest.nim | 3 ++- tests/stdlib/tunittestpass.nim | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 tests/stdlib/tunittestpass.nim diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim index ab45f78dc..18b09e4c0 100644 --- a/lib/pure/unittest.nim +++ b/lib/pure/unittest.nim @@ -669,7 +669,8 @@ macro check*(conditions: untyped): untyped = let paramAst = exp[i] if exp[i].kind == nnkIdent: result.printOuts.add getAst(print(argStr, paramAst)) - if exp[i].kind in nnkCallKinds + {nnkDotExpr, nnkBracketExpr, nnkPar}: + if exp[i].kind in nnkCallKinds + {nnkDotExpr, nnkBracketExpr, nnkPar} and + (exp[i].typeKind notin {ntyTypeDesc} or $exp[0] notin ["is", "isnot"]): let callVar = newIdentNode(":c" & $counter) result.assigns.add getAst(asgn(callVar, paramAst)) result.check[i] = callVar diff --git a/tests/stdlib/tunittestpass.nim b/tests/stdlib/tunittestpass.nim new file mode 100644 index 000000000..cff37a3b7 --- /dev/null +++ b/tests/stdlib/tunittestpass.nim @@ -0,0 +1,19 @@ +discard """ + targets: "c js" +""" + + +import unittest + +block: + check (type(1.0)) is float + check type(1.0) is float + check (typeof(1)) isnot float + check typeof(1) isnot float + + check 1.0 is float + check 1 isnot float + + type T = type(0.1) + check T is float + check T isnot int From fa1a04188ffdc66f1edc909e5b465e548532617a Mon Sep 17 00:00:00 2001 From: Jonah Snider Date: Sun, 27 Dec 2020 00:33:51 -0800 Subject: [PATCH 1209/3768] Avoid creating a holey array in makeNimstrLit for JS target (#16461) * Avoid creating a holey array in makeNimstrLit * Use array index instead of push --- lib/system/jssys.nim | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim index 8865558fe..e2ceedc2c 100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -189,9 +189,8 @@ proc setConstr() {.varargs, asmNoStackFrame, compilerproc.} = proc makeNimstrLit(c: cstring): string {.asmNoStackFrame, compilerproc.} = {.emit: """ - var ln = `c`.length; - var result = new Array(ln); - for (var i = 0; i < ln; ++i) { + var result = []; + for (var i = 0; i < `c`.length; ++i) { result[i] = `c`.charCodeAt(i); } return result; From 4cf605dcf6bdeacbb3f2ff8c7f17f5ff1afbe316 Mon Sep 17 00:00:00 2001 From: Saem Ghani Date: Sun, 27 Dec 2020 01:08:28 -0800 Subject: [PATCH 1210/3768] nimsuggest: fix and re-enable old tests (#16401) A number of nimsuggest tests were disabled for various reasons, sometimes due to brittleness. These tests have been fixed where needed and most have are now enabled -- details below. The updates are meant to provide better regression coverage for future nimsuggest improvements. To avoid brittleness some tests were refactored. Impact: * test coverage has now increased * faster execution of the test suite * tests are less likely to break due to stdlib changes Re-enabled Test & Test Description: * `tchk1.nim`: check (chk) via nimsuggest works at end of file * `tdot4.nim`: prioritize already used completion * `tinclude.nim`: definition lookup (def) with includes * `tstrutils.nim` -> `tdef2.nim`: test template definition lookup (def) * `tsug_regression.nim`: regression test for [nimsuggest #52](https://github.com/nim-lang/nimsuggest/issues/52) * `ttemplate_highlight.nim`: per the file name * `twithin_macro_prefix.nim`: suggest within a macro with a prefix Tests Not Re-Enabled: * `twithin_macro.nim` still disabled as it doesn't provide a good test signal * EPC highlight tests remain disabled -- requires out of scope tester changes Additional Notes: * todos added in comments for follow-up work --- nimsuggest/tests/fixtures/mclass_macro.nim | 164 ++++++++++++++++ .../{dep_v1.nim => fixtures/mdep_v1.nim} | 0 .../{dep_v2.nim => fixtures/mdep_v2.nim} | 0 nimsuggest/tests/fixtures/mfakeassert.nim | 5 + nimsuggest/tests/fixtures/minclude_import.nim | 15 ++ .../tests/fixtures/minclude_include.nim | 4 + nimsuggest/tests/fixtures/minclude_types.nim | 6 + nimsuggest/tests/fixtures/mstrutils.nim | 19 ++ nimsuggest/tests/tchk1.nim | 9 +- nimsuggest/tests/tdef2.nim | 13 ++ nimsuggest/tests/tdot3.nim | 4 +- nimsuggest/tests/tdot4.nim | 22 ++- nimsuggest/tests/tinclude.nim | 23 ++- nimsuggest/tests/tstrutils.nim | 10 - nimsuggest/tests/tsug_regression.nim | 13 +- ..._highlight.nim => ttemplate_highlight.nim} | 0 nimsuggest/tests/ttype_decl.nim | 7 +- nimsuggest/tests/twithin_macro.nim | 177 +----------------- nimsuggest/tests/twithin_macro_prefix.nim | 177 ++---------------- 19 files changed, 292 insertions(+), 376 deletions(-) create mode 100644 nimsuggest/tests/fixtures/mclass_macro.nim rename nimsuggest/tests/{dep_v1.nim => fixtures/mdep_v1.nim} (100%) rename nimsuggest/tests/{dep_v2.nim => fixtures/mdep_v2.nim} (100%) create mode 100644 nimsuggest/tests/fixtures/mfakeassert.nim create mode 100644 nimsuggest/tests/fixtures/minclude_import.nim create mode 100644 nimsuggest/tests/fixtures/minclude_include.nim create mode 100644 nimsuggest/tests/fixtures/minclude_types.nim create mode 100644 nimsuggest/tests/fixtures/mstrutils.nim create mode 100644 nimsuggest/tests/tdef2.nim delete mode 100644 nimsuggest/tests/tstrutils.nim rename nimsuggest/tests/{disabled_ttemplate_highlight.nim => ttemplate_highlight.nim} (100%) diff --git a/nimsuggest/tests/fixtures/mclass_macro.nim b/nimsuggest/tests/fixtures/mclass_macro.nim new file mode 100644 index 000000000..cfca0bf3f --- /dev/null +++ b/nimsuggest/tests/fixtures/mclass_macro.nim @@ -0,0 +1,164 @@ + +import macros + +macro class*(head, body: untyped): untyped = + # The macro is immediate, since all its parameters are untyped. + # This means, it doesn't resolve identifiers passed to it. + + var typeName, baseName: NimNode + + # flag if object should be exported + var exported: bool + + if head.kind == nnkInfix and head[0].kind == nnkIdent and $head[0] == "of": + # `head` is expression `typeName of baseClass` + # echo head.treeRepr + # -------------------- + # Infix + # Ident !"of" + # Ident !"Animal" + # Ident !"RootObj" + typeName = head[1] + baseName = head[2] + + elif head.kind == nnkInfix and head[0].kind == nnkIdent and + $head[0] == "*" and head[2].kind == nnkPrefix and + head[2][0].kind == nnkIdent and $head[2][0] == "of": + # `head` is expression `typeName* of baseClass` + # echo head.treeRepr + # -------------------- + # Infix + # Ident !"*" + # Ident !"Animal" + # Prefix + # Ident !"of" + # Ident !"RootObj" + typeName = head[1] + baseName = head[2][1] + exported = true + + else: + quit "Invalid node: " & head.lispRepr + + # The following prints out the AST structure: + # + # import macros + # dumptree: + # type X = ref object of Y + # z: int + # -------------------- + # StmtList + # TypeSection + # TypeDef + # Ident !"X" + # Empty + # RefTy + # ObjectTy + # Empty + # OfInherit + # Ident !"Y" + # RecList + # IdentDefs + # Ident !"z" + # Ident !"int" + # Empty + + # create a type section in the result + result = newNimNode(nnkStmtList) + result.add( + if exported: + # mark `typeName` with an asterisk + quote do: + type `typeName`* = ref object of `baseName` + else: + quote do: + type `typeName` = ref object of `baseName` + ) + + # echo treeRepr(body) + # -------------------- + # StmtList + # VarSection + # IdentDefs + # Ident !"name" + # Ident !"string" + # Empty + # IdentDefs + # Ident !"age" + # Ident !"int" + # Empty + # MethodDef + # Ident !"vocalize" + # Empty + # Empty + # FormalParams + # Ident !"string" + # Empty + # Empty + # StmtList + # StrLit ... + # MethodDef + # Ident !"age_human_yrs" + # Empty + # Empty + # FormalParams + # Ident !"int" + # Empty + # Empty + # StmtList + # DotExpr + # Ident !"this" + # Ident !"age" + + # var declarations will be turned into object fields + var recList = newNimNode(nnkRecList) + + # expected name of constructor + let ctorName = newIdentNode("new" & $typeName) + + # Iterate over the statements, adding `this: T` + # to the parameters of functions, unless the + # function is a constructor + for node in body.children: + case node.kind: + + of nnkMethodDef, nnkProcDef: + # check if it is the ctor proc + if node.name.kind != nnkAccQuoted and node.name.basename == ctorName: + # specify the return type of the ctor proc + node.params[0] = typeName + else: + # inject `self: T` into the arguments + node.params.insert(1, newIdentDefs(ident("self"), typeName)) + result.add(node) + + of nnkVarSection: + # variables get turned into fields of the type. + for n in node.children: + recList.add(n) + + else: + result.add(node) + + # Inspect the tree structure: + # + # echo result.treeRepr + # -------------------- + # StmtList + # TypeSection + # TypeDef + # Ident !"Animal" + # Empty + # RefTy + # ObjectTy + # Empty + # OfInherit + # Ident !"RootObj" + # Empty <= We want to replace this + # MethodDef + # ... + + result[0][0][2][0][2] = recList + + # Lets inspect the human-readable version of the output + #echo repr(result) diff --git a/nimsuggest/tests/dep_v1.nim b/nimsuggest/tests/fixtures/mdep_v1.nim similarity index 100% rename from nimsuggest/tests/dep_v1.nim rename to nimsuggest/tests/fixtures/mdep_v1.nim diff --git a/nimsuggest/tests/dep_v2.nim b/nimsuggest/tests/fixtures/mdep_v2.nim similarity index 100% rename from nimsuggest/tests/dep_v2.nim rename to nimsuggest/tests/fixtures/mdep_v2.nim diff --git a/nimsuggest/tests/fixtures/mfakeassert.nim b/nimsuggest/tests/fixtures/mfakeassert.nim new file mode 100644 index 000000000..765831ba7 --- /dev/null +++ b/nimsuggest/tests/fixtures/mfakeassert.nim @@ -0,0 +1,5 @@ +# Template for testing defs + +template fakeAssert*(cond: untyped, msg: string = "") = + ## template to allow def lookup testing + if not cond: quit(1) diff --git a/nimsuggest/tests/fixtures/minclude_import.nim b/nimsuggest/tests/fixtures/minclude_import.nim new file mode 100644 index 000000000..5fa9e5142 --- /dev/null +++ b/nimsuggest/tests/fixtures/minclude_import.nim @@ -0,0 +1,15 @@ +# Creates an awkward set of dependencies between this, import, and include. +# This pattern appears in the compiler, compiler/(sem|ast|semexprs).nim. + +import mfakeassert +import minclude_types + +proc say*(g: Greet): string = + fakeAssert(true, "always works") + g.greeting & ", " & g.subject & "!" + +include minclude_include + +proc say*(): string = + fakeAssert(1 + 1 == 2, "math works") + say(create()) diff --git a/nimsuggest/tests/fixtures/minclude_include.nim b/nimsuggest/tests/fixtures/minclude_include.nim new file mode 100644 index 000000000..23f9892cc --- /dev/null +++ b/nimsuggest/tests/fixtures/minclude_include.nim @@ -0,0 +1,4 @@ +# this file is included and relies on imports within the include + +proc create*(greeting: string = "Hello", subject: string = "World"): Greet = + Greet(greeting: greeting, subject: subject) diff --git a/nimsuggest/tests/fixtures/minclude_types.nim b/nimsuggest/tests/fixtures/minclude_types.nim new file mode 100644 index 000000000..3e85ee540 --- /dev/null +++ b/nimsuggest/tests/fixtures/minclude_types.nim @@ -0,0 +1,6 @@ +# types used by minclude_* (import or include), to find with def in include + +type + Greet* = object + greeting*: string + subject*: string \ No newline at end of file diff --git a/nimsuggest/tests/fixtures/mstrutils.nim b/nimsuggest/tests/fixtures/mstrutils.nim new file mode 100644 index 000000000..d6f25571b --- /dev/null +++ b/nimsuggest/tests/fixtures/mstrutils.nim @@ -0,0 +1,19 @@ +import mfakeassert + +func rereplace*(s, sub: string; by: string = ""): string {.used.} = + ## competes for priority in suggestion, here first, but never used in test + + fakeAssert(true, "always works") + result = by + +func replace*(s, sub: string; by: string = ""): string = + ## this is a test version of strutils.replace, it simply returns `by` + + fakeAssert("".len == 0, "empty string is empty") + result = by + +func rerereplace*(s, sub: string; by: string = ""): string {.used.} = + ## isn't used and appears last, lowest priority + + fakeAssert(false, "never works") + result = by diff --git a/nimsuggest/tests/tchk1.nim b/nimsuggest/tests/tchk1.nim index 2b60ed094..c28b88b9b 100644 --- a/nimsuggest/tests/tchk1.nim +++ b/nimsuggest/tests/tchk1.nim @@ -15,14 +15,13 @@ proc main = #[!]# discard """ -disabled:true $nimsuggest --tester $file >chk $1 -chk;;skUnknown;;;;Hint;;???;;-1;;-1;;"tchk1 [Processing]";;0 -chk;;skUnknown;;;;Error;;$file;;12;;0;;"identifier expected, but found \'keyword template\'";;0 -chk;;skUnknown;;;;Error;;$file;;14;;0;;"complex statement requires indentation";;0 +chk;;skUnknown;;;;Hint;;???;;0;;-1;;"tchk1 [Processing]";;0 +chk;;skUnknown;;;;Error;;$file;;12;;0;;"identifier expected, but got \'keyword template\'";;0 +chk;;skUnknown;;;;Error;;$file;;14;;0;;"nestable statement requires indentation";;0 chk;;skUnknown;;;;Error;;$file;;12;;0;;"implementation of \'foo\' expected";;0 chk;;skUnknown;;;;Error;;$file;;17;;0;;"invalid indentation";;0 chk;;skUnknown;;;;Hint;;$file;;12;;9;;"\'foo\' is declared but not used [XDeclaredButNotUsed]";;0 -chk;;skUnknown;;;;Hint;;$file;;14;;5;;"\'tchk1.main()[declared in tchk1.nim(14, 5)]\' is declared but not used [XDeclaredButNotUsed]";;0 +chk;;skUnknown;;;;Hint;;$file;;14;;5;;"\'main\' is declared but not used [XDeclaredButNotUsed]";;0 """ diff --git a/nimsuggest/tests/tdef2.nim b/nimsuggest/tests/tdef2.nim new file mode 100644 index 000000000..299b83a3d --- /dev/null +++ b/nimsuggest/tests/tdef2.nim @@ -0,0 +1,13 @@ +# Test def with template and boundaries for the cursor + +import fixtures/mstrutils + +discard """ +$nimsuggest --tester $file +>def $path/fixtures/mstrutils.nim:6:4 +def;;skTemplate;;mfakeassert.fakeAssert;;template (cond: untyped, msg: string);;*fixtures/mfakeassert.nim;;3;;9;;"template to allow def lookup testing";;100 +>def $path/fixtures/mstrutils.nim:12:3 +def;;skTemplate;;mfakeassert.fakeAssert;;template (cond: untyped, msg: string);;*fixtures/mfakeassert.nim;;3;;9;;"template to allow def lookup testing";;100 +>def $path/fixtures/mstrutils.nim:18:11 +def;;skTemplate;;mfakeassert.fakeAssert;;template (cond: untyped, msg: string);;*fixtures/mfakeassert.nim;;3;;9;;"template to allow def lookup testing";;100 +""" diff --git a/nimsuggest/tests/tdot3.nim b/nimsuggest/tests/tdot3.nim index 15fc1cd1c..30dd60591 100644 --- a/nimsuggest/tests/tdot3.nim +++ b/nimsuggest/tests/tdot3.nim @@ -9,14 +9,14 @@ proc main(f: Foo) = # this way, the line numbers more often stay the same discard """ -!copy dep_v1.nim dep.nim +!copy fixtures/mdep_v1.nim dep.nim $nimsuggest --tester $file >sug $1 sug;;skField;;x;;int;;*dep.nim;;8;;4;;"";;100;;None sug;;skField;;y;;int;;*dep.nim;;8;;8;;"";;100;;None sug;;skProc;;tdot3.main;;proc (f: Foo);;$file;;5;;5;;"";;100;;None -!copy dep_v2.nim dep.nim +!copy fixtures/mdep_v2.nim dep.nim >mod $path/dep.nim >sug $1 sug;;skField;;x;;int;;*dep.nim;;8;;4;;"";;100;;None diff --git a/nimsuggest/tests/tdot4.nim b/nimsuggest/tests/tdot4.nim index 762534310..e1ff96553 100644 --- a/nimsuggest/tests/tdot4.nim +++ b/nimsuggest/tests/tdot4.nim @@ -1,17 +1,21 @@ -discard """ -disabled:true -$nimsuggest --tester --maxresults:2 $file ->sug $1 -sug;;skProc;;tdot4.main;;proc (inp: string): string;;$file;;10;;5;;"";;100;;None -sug;;skProc;;strutils.replace;;proc (s: string, sub: string, by: string): string{.noSideEffect, gcsafe, locks: 0.};;$lib/pure/strutils.nim;;1506;;5;;"Replaces `sub` in `s` by the string `by`.";;100;;None -""" +# Test that already used suggestions are prioritized -import strutils +from system import string, echo +import fixtures/mstrutils proc main(inp: string): string = # use replace here and see if it occurs in the result, it should gain # priority: result = inp.replace(" ", "a").replace("b", "c") - echo "string literal here".#[!]# + +# priority still tested, but limit results to avoid failures from other output +discard """ +$nimsuggest --tester --maxresults:2 $file +>sug $1 +sug;;skProc;;tdot4.main;;proc (inp: string): string;;$file;;6;;5;;"";;100;;None +sug;;skFunc;;mstrutils.replace;;proc (s: string, sub: string, by: string): string{.noSideEffect, gcsafe, locks: 0.};;*fixtures/mstrutils.nim;;9;;5;;"this is a test version of strutils.replace, it simply returns `by`";;100;;None +""" + +# TODO - determine appropriate behaviour for further suggest output and test it diff --git a/nimsuggest/tests/tinclude.nim b/nimsuggest/tests/tinclude.nim index 0fda43911..23aa2d727 100644 --- a/nimsuggest/tests/tinclude.nim +++ b/nimsuggest/tests/tinclude.nim @@ -1,8 +1,19 @@ +# import that has an include, def calls must work into and out of includes +import fixtures/minclude_import + +proc go() = + discard create().say() + +go() + discard """ -disabled:true -$nimsuggest --tester compiler/nim.nim ->def compiler/semexprs.nim:25:50 -def;;skType;;ast.PSym;;PSym;;*ast.nim;;707;;2;;"";;100 ->def compiler/semexprs.nim:25:50 -def;;skType;;ast.PSym;;PSym;;*ast.nim;;707;;2;;"";;100 +$nimsuggest --tester $file +>def $path/tinclude.nim:5:14 +def;;skProc;;minclude_import.create;;proc (greeting: string, subject: string): Greet{.noSideEffect, gcsafe, locks: 0.};;*fixtures/minclude_include.nim;;3;;5;;"";;100 +>def $path/fixtures/minclude_include.nim:3:71 +def;;skType;;minclude_types.Greet;;Greet;;*fixtures/minclude_types.nim;;4;;2;;"";;100 +>def $path/fixtures/minclude_include.nim:3:71 +def;;skType;;minclude_types.Greet;;Greet;;*fixtures/minclude_types.nim;;4;;2;;"";;100 """ + +# TODO test/fix if the first `def` is not first or repeated we get no results diff --git a/nimsuggest/tests/tstrutils.nim b/nimsuggest/tests/tstrutils.nim deleted file mode 100644 index 9462c3d99..000000000 --- a/nimsuggest/tests/tstrutils.nim +++ /dev/null @@ -1,10 +0,0 @@ -discard """ -disabled:true -$nimsuggest --tester lib/pure/strutils.nim ->def lib/pure/strutils.nim:2529:6 -def;;skTemplate;;system.doAssert;;proc (cond: bool, msg: string): typed;;*/lib/system.nim;;*;;9;;"same as `assert` but is always turned on and not affected by the\x0A``--assertions`` command line switch.";;100 -""" - -# Line 2529 in strutils.nim is doAssert and this is unlikely to change -# soon since there are a whole lot of doAsserts there. - diff --git a/nimsuggest/tests/tsug_regression.nim b/nimsuggest/tests/tsug_regression.nim index 1607f5244..ba2034bd3 100644 --- a/nimsuggest/tests/tsug_regression.nim +++ b/nimsuggest/tests/tsug_regression.nim @@ -17,13 +17,14 @@ proc main = map0.#[!]# discard """ -disabled:true $nimsuggest --tester $file >sug $1 -sug;;skProc;;tables.getOrDefault;;proc (t: Table[getOrDefault.A, getOrDefault.B], key: A): B;;$lib/pure/collections/tables.nim;;178;;5;;"";;100;;None -sug;;skProc;;tables.hasKey;;proc (t: Table[hasKey.A, hasKey.B], key: A): bool;;$lib/pure/collections/tables.nim;;233;;5;;"returns true iff `key` is in the table `t`.";;100;;None -sug;;skProc;;tables.add;;proc (t: var Table[add.A, add.B], key: A, val: B);;$lib/pure/collections/tables.nim;;309;;5;;"puts a new (key, value)-pair into `t` even if ``t[key]`` already exists.";;100;;None -sug;;skIterator;;tables.allValues;;iterator (t: Table[allValues.A, allValues.B], key: A): B{.inline.};;$lib/pure/collections/tables.nim;;225;;9;;"iterates over any value in the table `t` that belongs to the given `key`.";;100;;None -sug;;skProc;;tables.clear;;proc (t: var Table[clear.A, clear.B]);;$lib/pure/collections/tables.nim;;121;;5;;"Resets the table so that it is empty.";;100;;None +sug;;skProc;;tables.hasKey;;proc (t: Table[hasKey.A, hasKey.B], key: A): bool;;*/lib/pure/collections/tables.nim;;374;;5;;"Returns true if*";;100;;None +sug;;skProc;;tables.add;;proc (t: var Table[add.A, add.B], key: A, val: sink B);;*/lib/pure/collections/tables.nim;;505;;5;;"Puts a new*";;100;;None +sug;;skIterator;;tables.allValues;;iterator (t: Table[allValues.A, allValues.B], key: A): B{.inline.};;*/lib/pure/collections/tables.nim;;769;;9;;"Iterates over any*";;100;;None +sug;;skProc;;tables.clear;;proc (t: var Table[clear.A, clear.B]);;*/lib/pure/collections/tables.nim;;567;;5;;"Resets the table so that it is empty.*";;100;;None +sug;;skProc;;tables.contains;;proc (t: Table[contains.A, contains.B], key: A): bool;;*/lib/pure/collections/tables.nim;;392;;5;;"Alias of `hasKey*";;100;;None * """ + +# TODO: test/fix suggestion sorting - deprecated suggestions should rank lower diff --git a/nimsuggest/tests/disabled_ttemplate_highlight.nim b/nimsuggest/tests/ttemplate_highlight.nim similarity index 100% rename from nimsuggest/tests/disabled_ttemplate_highlight.nim rename to nimsuggest/tests/ttemplate_highlight.nim diff --git a/nimsuggest/tests/ttype_decl.nim b/nimsuggest/tests/ttype_decl.nim index 6d9817ed2..d7ed63ed0 100644 --- a/nimsuggest/tests/ttype_decl.nim +++ b/nimsuggest/tests/ttype_decl.nim @@ -1,10 +1,9 @@ discard """ -disabled:true $nimsuggest --tester --maxresults:3 $file >sug $1 -sug;;skType;;ttype_decl.Other;;Other;;$file;;11;;2;;"";;0;;None -sug;;skType;;system.int;;int;;$lib/system/basic_types.nim;;2;;2;;"";;0;;None -sug;;skType;;system.string;;string;;$lib/system.nim;;34;;2;;"";;0;;None +sug;;skType;;ttype_decl.Other;;Other;;$file;;10;;2;;"";;0;;None +sug;;skType;;system.int;;int;;*/lib/system/basic_types.nim;;2;;2;;"";;0;;None +sug;;skType;;system.string;;string;;*/lib/system.nim;;34;;2;;"";;0;;None """ import strutils type diff --git a/nimsuggest/tests/twithin_macro.nim b/nimsuggest/tests/twithin_macro.nim index 9c36ffd0f..2f5e278c4 100644 --- a/nimsuggest/tests/twithin_macro.nim +++ b/nimsuggest/tests/twithin_macro.nim @@ -1,166 +1,5 @@ - -import macros - -macro class*(head, body: untyped): untyped = - # The macro is immediate, since all its parameters are untyped. - # This means, it doesn't resolve identifiers passed to it. - - var typeName, baseName: NimNode - - # flag if object should be exported - var exported: bool - - if head.kind == nnkInfix and head[0].ident == !"of": - # `head` is expression `typeName of baseClass` - # echo head.treeRepr - # -------------------- - # Infix - # Ident !"of" - # Ident !"Animal" - # Ident !"RootObj" - typeName = head[1] - baseName = head[2] - - elif head.kind == nnkInfix and head[0].ident == !"*" and - head[2].kind == nnkPrefix and head[2][0].ident == !"of": - # `head` is expression `typeName* of baseClass` - # echo head.treeRepr - # -------------------- - # Infix - # Ident !"*" - # Ident !"Animal" - # Prefix - # Ident !"of" - # Ident !"RootObj" - typeName = head[1] - baseName = head[2][1] - exported = true - - else: - quit "Invalid node: " & head.lispRepr - - # The following prints out the AST structure: - # - # import macros - # dumptree: - # type X = ref object of Y - # z: int - # -------------------- - # StmtList - # TypeSection - # TypeDef - # Ident !"X" - # Empty - # RefTy - # ObjectTy - # Empty - # OfInherit - # Ident !"Y" - # RecList - # IdentDefs - # Ident !"z" - # Ident !"int" - # Empty - - # create a type section in the result - result = - if exported: - # mark `typeName` with an asterisk - quote do: - type `typeName`* = ref object of `baseName` - else: - quote do: - type `typeName` = ref object of `baseName` - - # echo treeRepr(body) - # -------------------- - # StmtList - # VarSection - # IdentDefs - # Ident !"name" - # Ident !"string" - # Empty - # IdentDefs - # Ident !"age" - # Ident !"int" - # Empty - # MethodDef - # Ident !"vocalize" - # Empty - # Empty - # FormalParams - # Ident !"string" - # Empty - # Empty - # StmtList - # StrLit ... - # MethodDef - # Ident !"age_human_yrs" - # Empty - # Empty - # FormalParams - # Ident !"int" - # Empty - # Empty - # StmtList - # DotExpr - # Ident !"this" - # Ident !"age" - - # var declarations will be turned into object fields - var recList = newNimNode(nnkRecList) - - # expected name of constructor - let ctorName = newIdentNode("new" & $typeName) - - # Iterate over the statements, adding `this: T` - # to the parameters of functions, unless the - # function is a constructor - for node in body.children: - case node.kind: - - of nnkMethodDef, nnkProcDef: - # check if it is the ctor proc - if node.name.kind != nnkAccQuoted and node.name.basename == ctorName: - # specify the return type of the ctor proc - node.params[0] = typeName - else: - # inject `self: T` into the arguments - node.params.insert(1, newIdentDefs(ident("self"), typeName)) - result.add(node) - - of nnkVarSection: - # variables get turned into fields of the type. - for n in node.children: - recList.add(n) - - else: - result.add(node) - - # Inspect the tree structure: - # - # echo result.treeRepr - # -------------------- - # StmtList - # TypeSection - # TypeDef - # Ident !"Animal" - # Empty - # RefTy - # ObjectTy - # Empty - # OfInherit - # Ident !"RootObj" - # Empty <= We want to replace this - # MethodDef - # ... - - result[0][0][2][0][2] = recList - - # Lets inspect the human-readable version of the output - #echo repr(result) - -# --- +from system import string, int, seq, `&`, `$`, `*`, `@`, echo, add, items, RootObj +import fixtures/mclass_macro class Animal of RootObj: var name: string @@ -205,10 +44,12 @@ discard """ disabled:true $nimsuggest --tester $file >sug $1 -sug;;skField;;age;;int;;$file;;167;;6;;"";;100;;None -sug;;skField;;name;;string;;$file;;166;;6;;"";;100;;None -sug;;skMethod;;twithin_macro.age_human_yrs;;proc (self: Animal): int;;$file;;169;;9;;"";;100;;None -sug;;skMethod;;twithin_macro.vocalize;;proc (self: Animal): string;;$file;;168;;9;;"";;100;;None -sug;;skMethod;;twithin_macro.vocalize;;proc (self: Rabbit): string;;$file;;184;;9;;"";;100;;None +sug;;skField;;age;;int;;$file;;6;;6;;"";;100;;None +sug;;skField;;name;;string;;$file;;5;;6;;"";;100;;None +sug;;skMethod;;twithin_macro.age_human_yrs;;proc (self: Animal): int;;$file;;8;;9;;"";;100;;None +sug;;skMethod;;twithin_macro.vocalize;;proc (self: Animal): string;;$file;;7;;9;;"";;100;;None +sug;;skMethod;;twithin_macro.vocalize;;proc (self: Rabbit): string;;$file;;23;;9;;"";;100;;None sug;;skMacro;;twithin_macro.class;;proc (head: untyped, body: untyped): untyped{.gcsafe, locks: .};;$file;;4;;6;;"";;50;;None* """ + +# TODO: disabled due to semantic error reporting in nimsuggest results diff --git a/nimsuggest/tests/twithin_macro_prefix.nim b/nimsuggest/tests/twithin_macro_prefix.nim index 1402b762a..1c06397c5 100644 --- a/nimsuggest/tests/twithin_macro_prefix.nim +++ b/nimsuggest/tests/twithin_macro_prefix.nim @@ -1,166 +1,5 @@ - -import macros - -macro class*(head, body: untyped): untyped = - # The macro is immediate, since all its parameters are untyped. - # This means, it doesn't resolve identifiers passed to it. - - var typeName, baseName: NimNode - - # flag if object should be exported - var exported: bool - - if head.kind == nnkInfix and head[0].ident == !"of": - # `head` is expression `typeName of baseClass` - # echo head.treeRepr - # -------------------- - # Infix - # Ident !"of" - # Ident !"Animal" - # Ident !"RootObj" - typeName = head[1] - baseName = head[2] - - elif head.kind == nnkInfix and head[0].ident == !"*" and - head[2].kind == nnkPrefix and head[2][0].ident == !"of": - # `head` is expression `typeName* of baseClass` - # echo head.treeRepr - # -------------------- - # Infix - # Ident !"*" - # Ident !"Animal" - # Prefix - # Ident !"of" - # Ident !"RootObj" - typeName = head[1] - baseName = head[2][1] - exported = true - - else: - quit "Invalid node: " & head.lispRepr - - # The following prints out the AST structure: - # - # import macros - # dumptree: - # type X = ref object of Y - # z: int - # -------------------- - # StmtList - # TypeSection - # TypeDef - # Ident !"X" - # Empty - # RefTy - # ObjectTy - # Empty - # OfInherit - # Ident !"Y" - # RecList - # IdentDefs - # Ident !"z" - # Ident !"int" - # Empty - - # create a type section in the result - result = - if exported: - # mark `typeName` with an asterisk - quote do: - type `typeName`* = ref object of `baseName` - else: - quote do: - type `typeName` = ref object of `baseName` - - # echo treeRepr(body) - # -------------------- - # StmtList - # VarSection - # IdentDefs - # Ident !"name" - # Ident !"string" - # Empty - # IdentDefs - # Ident !"age" - # Ident !"int" - # Empty - # MethodDef - # Ident !"vocalize" - # Empty - # Empty - # FormalParams - # Ident !"string" - # Empty - # Empty - # StmtList - # StrLit ... - # MethodDef - # Ident !"age_human_yrs" - # Empty - # Empty - # FormalParams - # Ident !"int" - # Empty - # Empty - # StmtList - # DotExpr - # Ident !"this" - # Ident !"age" - - # var declarations will be turned into object fields - var recList = newNimNode(nnkRecList) - - # expected name of constructor - let ctorName = newIdentNode("new" & $typeName) - - # Iterate over the statements, adding `this: T` - # to the parameters of functions, unless the - # function is a constructor - for node in body.children: - case node.kind: - - of nnkMethodDef, nnkProcDef: - # check if it is the ctor proc - if node.name.kind != nnkAccQuoted and node.name.basename == ctorName: - # specify the return type of the ctor proc - node.params[0] = typeName - else: - # inject `self: T` into the arguments - node.params.insert(1, newIdentDefs(ident("self"), typeName)) - result.add(node) - - of nnkVarSection: - # variables get turned into fields of the type. - for n in node.children: - recList.add(n) - - else: - result.add(node) - - # Inspect the tree structure: - # - # echo result.treeRepr - # -------------------- - # StmtList - # TypeSection - # TypeDef - # Ident !"Animal" - # Empty - # RefTy - # ObjectTy - # Empty - # OfInherit - # Ident !"RootObj" - # Empty <= We want to replace this - # MethodDef - # ... - - result[0][0][2][0][2] = recList - - # Lets inspect the human-readable version of the output - #echo repr(result) - -# --- +from system import string, int, seq, `&`, `$`, `*`, `@`, echo, add, RootObj +import fixtures/mclass_macro class Animal of RootObj: var name: string @@ -202,9 +41,15 @@ echo r.age_human_yrs() echo r discard """ -disabled:true $nimsuggest --tester $file >sug $1 -sug;;skField;;age;;int;;$file;;167;;6;;"";;100;;Prefix -sug;;skMethod;;twithin_macro_prefix.age_human_yrs;;proc (self: Animal): int;;$file;;169;;9;;"";;100;;Prefix +sug;;skField;;age;;int;;$file;;6;;6;;"";;100;;Prefix +sug;;skMethod;;twithin_macro_prefix.age_human_yrs;;proc (self: Animal): int;;$file;;8;;9;;"";;100;;Prefix """ + +#[ +TODO: additional calls to `>sug $1` produces different output with errors, + possibly related to cached results from the first analysis, which refers + to expanded macros/templates which rely on imported symbols from `system` + module that are not present in this module. +]# From 3f9a2ebea5c0b02bcfcfe77ada36c33187bbf8bb Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sun, 27 Dec 2020 03:13:57 -0600 Subject: [PATCH 1211/3768] fix nim js cmp fails at CT (#16473) --- lib/system.nim | 10 ++-------- lib/system/jssys.nim | 7 ++++++- tests/misc/tstrtabs.nim | 20 ++++++++++++++++++++ tests/stdlib/tstring.nim | 36 +++++++++++++++++++++--------------- 4 files changed, 49 insertions(+), 24 deletions(-) create mode 100644 tests/misc/tstrtabs.nim diff --git a/lib/system.nim b/lib/system.nim index 85ef15e08..fb008dc45 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2394,14 +2394,8 @@ when notJSnotNims: """.} when defined(js): - when not defined(nimscript): - include "system/jssys" - include "system/reprjs" - else: - proc cmp(x, y: string): int = - if x == y: return 0 - if x < y: return -1 - return 1 + include "system/jssys" + include "system/reprjs" when defined(js) or defined(nimscript): proc addInt*(result: var string; x: int64) = diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim index e2ceedc2c..5f18f01cb 100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -340,7 +340,12 @@ proc cmpStrings(a, b: string): int {.asmNoStackFrame, compilerproc.} = """ proc cmp(x, y: string): int = - return cmpStrings(x, y) + when nimvm: + if x == y: result = 0 + elif x < y: result = -1 + else: result = 1 + else: + result = cmpStrings(x, y) proc eqStrings(a, b: string): bool {.asmNoStackFrame, compilerproc.} = asm """ diff --git a/tests/misc/tstrtabs.nim b/tests/misc/tstrtabs.nim new file mode 100644 index 000000000..2f7eda9f7 --- /dev/null +++ b/tests/misc/tstrtabs.nim @@ -0,0 +1,20 @@ +discard """ + targets: "c cpp js" +""" + +import std/strtabs + +proc fun()= + let ret = newStringTable(modeCaseSensitive) + ret["foo"] = "bar" + + doAssert $ret == "{foo: bar}" + + let b = ret["foo"] + doAssert b == "bar" + +proc main()= + static: fun() + fun() + +main() diff --git a/tests/stdlib/tstring.nim b/tests/stdlib/tstring.nim index ff3d41b49..4d5a15940 100644 --- a/tests/stdlib/tstring.nim +++ b/tests/stdlib/tstring.nim @@ -1,16 +1,14 @@ discard """ - output: '''OK -@[@[], @[], @[], @[], @[]] -''' + targets: "c cpp js" """ + const characters = "abcdefghijklmnopqrstuvwxyz" const numbers = "1234567890" -var s: string - proc test_string_slice() = # test "slice of length == len(characters)": # replace characters completely by numbers + var s: string s = characters s[0..^1] = numbers doAssert s == numbers @@ -51,11 +49,13 @@ proc test_string_slice() = s[2..0] = numbers doAssert s == "ab1234567890cdefghijklmnopqrstuvwxyz" - # bug #6223 - doAssertRaises(IndexDefect): - discard s[0..999] + when nimvm: + discard + else: + # bug #6223 + doAssertRaises(IndexDefect): + discard s[0..999] - echo("OK") proc test_string_cmp() = let world = "hello\0world" @@ -76,9 +76,6 @@ proc test_string_cmp() = doAssert cmp(world, hello) > 0 doAssert cmp(world, goodbye) > 0 -test_string_slice() -test_string_cmp() - #-------------------------- # bug #7816 @@ -87,9 +84,9 @@ import sequtils proc tester[T](x: T) = let test = toSeq(0..4).map(i => newSeq[int]()) - echo test + doAssert $test == "@[@[], @[], @[], @[], @[]]" + -tester(1) # #14497 func reverse*(a: string): string = @@ -97,4 +94,13 @@ func reverse*(a: string): string = for i in 0 ..< a.len div 2: swap(result[i], result[^(i + 1)]) -doAssert reverse("hello") == "olleh" + +proc main() = + test_string_slice() + test_string_cmp() + + tester(1) + doAssert reverse("hello") == "olleh" + +static: main() +main() From b57df6d0b335595d3197eb6907fb8d1a8237fc7e Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sun, 27 Dec 2020 03:15:57 -0600 Subject: [PATCH 1212/3768] Don't use `unittest.suite` and `unittest.test` (#16464) --- doc/contributing.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/contributing.rst b/doc/contributing.rst index 90330e4f4..34c963410 100644 --- a/doc/contributing.rst +++ b/doc/contributing.rst @@ -47,7 +47,7 @@ which only gets executed when the tester is building the file. Each test should be in a separate ``block:`` statement, such that each has its own scope. Use boolean conditions and ``doAssert`` for the testing by itself, don't rely on echo statements or similar; in particular, avoid -things like `echo "done"`. +things like `echo "done"`. Don't use `unittest.suite` and `unittest.test`. Sample test: From 626c2bc6589101bd1b0231a2608e32b51f73abba Mon Sep 17 00:00:00 2001 From: treeform Date: Sun, 27 Dec 2020 01:45:30 -0800 Subject: [PATCH 1213/3768] Add docs for nnkHiddenStdConv (#16408) Add it to devel branch this time. I hope this works. --- doc/astspec.txt | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/doc/astspec.txt b/doc/astspec.txt index c41aee96f..019b735f5 100644 --- a/doc/astspec.txt +++ b/doc/astspec.txt @@ -1392,6 +1392,17 @@ Macro declaration Macros behave like templates, but ``nnkTemplateDef`` is replaced with ``nnkMacroDef``. +Hidden Standard Conversion +-------------------------- + +.. code-block:: nim + var f: float = 1 + +The type of "f" is ``float`` but the type of "1" is actually ``int``. Inserting +``int`` into a ``float`` is a type error. Nim inserts the ``nnkHiddenStdConv`` +node around the ``nnkIntLit`` node so that the new node has the correct type of +``float``. This works for any auto converted nodes and makes the conversion +explicit. Special node kinds ================== From 2bdc479622c465e570fdb87df112dd56ddc9030f Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Sun, 27 Dec 2020 13:16:12 +0300 Subject: [PATCH 1214/3768] RST: implement admonitions (#16438) --- config/nimdoc.tex.cfg | 9 ++ doc/nimdoc.css | 34 +++++++ lib/packages/docutils/rst.nim | 155 ++++++++++++++++++++----------- lib/packages/docutils/rstast.nim | 22 ++++- lib/packages/docutils/rstgen.nim | 24 +++++ tests/stdlib/trstgen.nim | 49 ++++++++++ 6 files changed, 239 insertions(+), 54 deletions(-) diff --git a/config/nimdoc.tex.cfg b/config/nimdoc.tex.cfg index 3e5e5d38b..6d7ae413d 100644 --- a/config/nimdoc.tex.cfg +++ b/config/nimdoc.tex.cfg @@ -52,6 +52,15 @@ doc.file = """ \usepackage{hyperref} \usepackage{enumitem} +\usepackage{xcolor} +\usepackage[tikz]{mdframed} +\usetikzlibrary{shadows} +\mdfsetup{% +linewidth=3, +topline=false, +rightline=false, +bottomline=false} + \begin{document} \title{$title $version} \author{$author} diff --git a/doc/nimdoc.css b/doc/nimdoc.css index 2d9533ceb..7d4a399e1 100644 --- a/doc/nimdoc.css +++ b/doc/nimdoc.css @@ -14,6 +14,9 @@ Modified by Boyd Greenfield and narimiran --primary-background: #fff; --secondary-background: ghostwhite; --third-background: #e8e8e8; + --info-background: #50c050; + --warning-background: #c0a000; + --error-background: #e04040; --border: #dde; --text: #222; --anchor: #07b; @@ -39,6 +42,9 @@ Modified by Boyd Greenfield and narimiran --primary-background: #171921; --secondary-background: #1e202a; --third-background: #2b2e3b; + --info-background: #008000; + --warning-background: #807000; + --error-background: #c03000; --border: #0e1014; --text: #fff; --anchor: #8be9fd; @@ -609,6 +615,34 @@ table.borderless td, table.borderless th { The right padding separates the table cells. */ padding: 0 0.5em 0 0 !important; } +.admonition { + padding: 0.3em; + background-color: var(--secondary-background); + border-left: 0.4em solid #7f7f84; + margin-bottom: 0.5em; + -webkit-box-shadow: 0 5px 8px -6px rgba(0,0,0,.2); + -moz-box-shadow: 0 5px 8px -6px rgba(0,0,0,.2); + box-shadow: 0 5px 8px -6px rgba(0,0,0,.2); +} +.admonition-info { + border-color: var(--info-background); +} +.admonition-info-text { + color: var(--info-background); +} +.admonition-warning { + border-color: var(--warning-background); +} +.admonition-warning-text { + color: var(--warning-background); +} +.admonition-error { + border-color: var(--error-background); +} +.admonition-error-text { + color: var(--error-background); +} + .first { /* Override more specific margin styles with "! important". */ margin-top: 0 !important; } diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 0d86edcdb..7dbcaf482 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -7,11 +7,18 @@ # distribution, for details about the copyright. # -## This module implements a `reStructuredText`:idx: parser. A large -## subset is implemented. Some features of the `markdown`:idx: wiki syntax are -## also supported. +## This module implements a `reStructuredText`:idx: (RST) parser. A large +## subset is implemented. Some features of the `markdown`:idx: syntax are +## also supported. Nim can output the result to HTML (command ``rst2html``) +## or Latex (command ``rst2tex``). ## -## Supported RST features: +## If you are new to RST please consider reading the following: +## +## 1) a short `quick introduction`_ +## 2) an `RST reference`_: a comprehensive cheatsheet for RST +## 3) a more formal 50-page `RST specification`_. +## +## Supported standard RST features: ## ## * body elements ## + sections @@ -25,20 +32,29 @@ ## + option lists ## + indented literal blocks ## + simple tables -## + directives -## - image, figure -## - code-block -## - substitution definitions: replace and image -## - ... a few more +## + directives (see official documentation in `RST directives list`_): +## - ``image``, ``figure`` for including images and videos +## - ``code`` +## - ``contents`` (table of contents), ``container``, ``raw`` +## - ``include`` +## - admonitions: "attention", "caution", "danger", "error", "hint", +## "important", "note", "tip", "warning", "admonition" +## - substitution definitions: `replace` and `image` ## + comments ## * inline markup -## + *emphasis*, **strong emphasis**, `interpreted text`, +## + *emphasis*, **strong emphasis**, ## ``inline literals``, hyperlink references, substitution references, ## standalone hyperlinks +## + \`interpreted text\` with roles ``:literal:``, ``:strong:``, +## ``emphasis``, ``:sub:``/``:subscript:``, ``:sup:``/``:supscript:`` +## (see `RST roles list`_ for description). ## ## Additional features: ## +## * directives: ``code-block``, ``title``, ``index`` ## * ***triple emphasis*** (bold and italic) using \*\*\* +## * ``:idx:`` role for \`interpreted text\` to include the link to this +## text into an index (example: `Nim index`_). ## ## Optional additional features, turned on by ``options: RstParseOption`` in ## `rstParse proc <#rstParse,string,string,int,int,bool,RstParseOptions,FindFileHandler,MsgHandler>`_: @@ -51,7 +67,11 @@ ## * using ``1`` as auto-enumerator in enumerated lists like RST ``#`` ## (auto-enumerator ``1`` can not be used with ``#`` in the same list) ## -## **Note:** By default nim has ``roSupportMarkdown`` turned **on**. +## .. Note:: By default Nim has ``roSupportMarkdown`` and +## ``roSupportRawDirective`` turned **on**. +## +## .. warning:: Using Nim-specific features can cause other RST implementations +## to fail on your document. ## ## Limitations: ## @@ -61,14 +81,32 @@ ## - no quoted literal blocks ## - no doctest blocks ## - no grid tables -## - directives: no support for admonitions (notes, caution) +## - some directives are missing (check official `RST directives list`_): +## ``parsed-literal``, ``sidebar``, ``topic``, ``math``, ``rubric``, +## ``epigraph``, ``highlights``, ``pull-quote``, ``compound``, +## ``table``, ``csv-table``, ``list-table``, ``section-numbering``, +## ``header``, ``footer``, ``meta``, ``class`` +## - no ``role`` directives and no custom interpreted text roles +## - some standard roles are not supported (check `RST roles list`_) ## - no footnotes & citations support ## - no inline internal targets ## * inline markup ## - no simple-inline-markup ## - no embedded URI and aliases ## -## **Note:** Import ``packages/docutils/rst`` to use this module +## .. _quick introduction: https://docutils.sourceforge.io/docs/user/rst/quickstart.html +## .. _RST reference: https://docutils.sourceforge.io/docs/user/rst/quickref.html +## .. _RST specification: https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html +## .. _RST directives list: https://docutils.sourceforge.io/docs/ref/rst/directives.html +## .. _RST roles list: https://docutils.sourceforge.io/docs/ref/rst/roles.html +## .. _Nim index: https://nim-lang.org/docs/theindex.html +## +## See `Nim DocGen Tools Guide `_ for the details about +## ``nim doc``, ``nim rst2html`` and ``nim rst2tex`` commands. +## +## .. note:: Import ``packages/docutils/rst`` to use this module. +## +## See also `packages/docutils/rstgen module `_. import os, strutils, rstast @@ -947,6 +985,7 @@ proc getDirective(p: var RstParser): string = result = "" # error else: result = "" + result = result.toLowerAscii() proc parseComment(p: var RstParser): PRstNode = case currentTok(p).kind @@ -968,21 +1007,6 @@ proc parseComment(p: var RstParser): PRstNode = while currentTok(p).kind notin {tkIndent, tkEof}: inc p.idx result = nil -type - DirKind = enum # must be ordered alphabetically! - dkNone, dkAuthor, dkAuthors, dkCode, dkCodeBlock, dkContainer, dkContents, - dkFigure, dkImage, dkInclude, dkIndex, dkRaw, dkTitle - -const - DirIds: array[0..12, string] = ["", "author", "authors", "code", - "code-block", "container", "contents", "figure", "image", "include", - "index", "raw", "title"] - -proc getDirKind(s: string): DirKind = - let i = find(DirIds, s) - if i >= 0: result = DirKind(i) - else: result = dkNone - proc parseLine(p: var RstParser, father: PRstNode) = while true: case currentTok(p).kind @@ -1191,7 +1215,8 @@ proc whichSection(p: RstParser): RstNodeKind = result = rnMarkdownTable elif currentTok(p).symbol == "|" and isLineBlock(p): result = rnLineBlock - elif match(p, tokenAfterNewline(p), "ai"): + elif match(p, tokenAfterNewline(p), "ai") and + isAdornmentHeadline(p, tokenAfterNewline(p)): result = rnHeadline elif predNL(p) and currentTok(p).symbol in ["+", "*", "-"] and nextTok(p).kind == tkWhite: @@ -1664,8 +1689,8 @@ proc parseDirective(p: var RstParser, flags: DirFlags): PRstNode = ## Parses arguments and options for a directive block. ## ## A directive block will always have three sons: the arguments for the - ## directive (rnDirArg), the options (rnFieldList) and the block - ## (rnLineBlock). This proc parses the two first nodes, the block is left to + ## directive (rnDirArg), the options (rnFieldList) and the directive + ## content block. This proc parses the two first nodes, the 3rd is left to ## the outer `parseDirective` call. ## ## Both rnDirArg and rnFieldList children nodes might be nil, so you need to @@ -1703,12 +1728,20 @@ proc indFollows(p: RstParser): bool = proc parseDirective(p: var RstParser, flags: DirFlags, contentParser: SectionParser): PRstNode = - ## Returns a generic rnDirective tree. + ## A helper proc that does main work for specific directive procs. + ## Always returns a generic rnDirective tree with these 3 children: ## - ## The children are rnDirArg, rnFieldList and rnLineBlock. Any might be nil. + ## 1) rnDirArg + ## 2) rnFieldList + ## 3) a node returned by `contentParser`. + ## + ## .. warning:: Any of the 3 children may be nil. result = parseDirective(p, flags) - if not isNil(contentParser) and indFollows(p): - pushInd(p, currentTok(p).ival) + if not isNil(contentParser): + var nextIndent = p.tok[tokenAfterNewline(p)-1].ival + if nextIndent <= currInd(p): # parse only this line + nextIndent = currentTok(p).col + pushInd(p, nextIndent) var content = contentParser(p) popInd(p) result.add(content) @@ -1814,7 +1847,7 @@ proc dirCodeBlock(p: var RstParser, nimExtension = false): PRstNode = n.add(newRstNode(rnLeaf, readFile(path))) result.sons[2] = n - # Extend the field block if we are using our custom extension. + # Extend the field block if we are using our custom Nim extension. if nimExtension: # Create a field block if the input block didn't have any. if result.sons[1].isNil: result.sons[1] = newRstNode(rnFieldList) @@ -1856,6 +1889,11 @@ proc dirIndex(p: var RstParser): PRstNode = result = parseDirective(p, {}, parseSectionWrapper) result.kind = rnIndex +proc dirAdmonition(p: var RstParser, d: string): PRstNode = + result = parseDirective(p, {}, parseSectionWrapper) + result.kind = rnAdmonition + result.text = d + proc dirRawAux(p: var RstParser, result: var PRstNode, kind: RstNodeKind, contentParser: SectionParser) = var filename = getFieldValue(result, "file") @@ -1891,29 +1929,42 @@ proc dirRaw(p: var RstParser): PRstNode = else: dirRawAux(p, result, rnRaw, parseSectionWrapper) +proc selectDir(p: var RstParser, d: string): PRstNode = + result = nil + case d + of "admonition", "attention", "caution": result = dirAdmonition(p, d) + of "code": result = dirCodeBlock(p) + of "code-block": result = dirCodeBlock(p, nimExtension = true) + of "container": result = dirContainer(p) + of "contents": result = dirContents(p) + of "danger", "error": result = dirAdmonition(p, d) + of "figure": result = dirFigure(p) + of "hint": result = dirAdmonition(p, d) + of "image": result = dirImage(p) + of "important": result = dirAdmonition(p, d) + of "include": result = dirInclude(p) + of "index": result = dirIndex(p) + of "note": result = dirAdmonition(p, d) + of "raw": + if roSupportRawDirective in p.s.options: + result = dirRaw(p) + else: + rstMessage(p, meInvalidDirective, d) + of "tip": result = dirAdmonition(p, d) + of "title": result = dirTitle(p) + of "warning": result = dirAdmonition(p, d) + else: + rstMessage(p, meInvalidDirective, d) + proc parseDotDot(p: var RstParser): PRstNode = + # parse "explicit markup blocks" result = nil var col = currentTok(p).col inc p.idx var d = getDirective(p) if d != "": pushInd(p, col) - case getDirKind(d) - of dkInclude: result = dirInclude(p) - of dkImage: result = dirImage(p) - of dkFigure: result = dirFigure(p) - of dkTitle: result = dirTitle(p) - of dkContainer: result = dirContainer(p) - of dkContents: result = dirContents(p) - of dkRaw: - if roSupportRawDirective in p.s.options: - result = dirRaw(p) - else: - rstMessage(p, meInvalidDirective, d) - of dkCode: result = dirCodeBlock(p) - of dkCodeBlock: result = dirCodeBlock(p, nimExtension = true) - of dkIndex: result = dirIndex(p) - else: rstMessage(p, meInvalidDirective, d) + result = selectDir(p, d) popInd(p) elif match(p, p.idx, " _"): # hyperlink target: diff --git a/lib/packages/docutils/rstast.nim b/lib/packages/docutils/rstast.nim index 5e2d21c04..f01bcada1 100644 --- a/lib/packages/docutils/rstast.nim +++ b/lib/packages/docutils/rstast.nim @@ -41,8 +41,11 @@ type rnLabel, # used for footnotes and other things rnFootnote, # a footnote rnCitation, # similar to footnote - rnStandaloneHyperlink, rnHyperlink, rnRef, rnDirective, # a directive - rnDirArg, rnRaw, rnTitle, rnContents, rnImage, rnFigure, rnCodeBlock, + rnStandaloneHyperlink, rnHyperlink, rnRef, + rnDirective, # a general directive + rnDirArg, # a directive argument (for some directives). + # here are directives that are not rnDirective: + rnRaw, rnTitle, rnContents, rnImage, rnFigure, rnCodeBlock, rnAdmonition, rnRawHtml, rnRawLatex, rnContainer, # ``container`` directive rnIndex, # index directve: @@ -70,6 +73,7 @@ type kind*: RstNodeKind ## the node's kind text*: string ## valid for leafs in the AST; and the title of ## the document or the section; and rnEnumList + ## and rnAdmonition level*: int ## valid for some node kinds sons*: RstNodeSeq ## the node's sons @@ -316,3 +320,17 @@ proc renderRstToJson*(node: PRstNode): string = ## "sons":optional node array ## } renderRstToJsonNode(node).pretty + +proc renderRstToStr*(node: PRstNode, indent=0): string = + ## Writes the parsed RST `node` into a compact string + ## representation in the format (one line per every sub-node): + ## ``indent - kind - text - level (if non-zero)`` + ## (suitable for debugging of RST parsing). + if node == nil: + result.add " ".repeat(indent) & "[nil]\n" + return + result.add " ".repeat(indent) & $node.kind & "\t" & + (if node.text == "": "" else: "'" & node.text & "'") & + (if node.level == 0: "" else: "\tlevel=" & $node.level) & "\n" + for son in node.sons: + result.add renderRstToStr(son, indent=indent+2) diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 4d056a83e..5aa2b03d4 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -1079,6 +1079,29 @@ proc renderEnumList(d: PDoc, n: PRstNode, result: var string) = "\\begin{enumerate}" & specifier & "$1\\end{enumerate}\n", result) +proc renderAdmonition(d: PDoc, n: PRstNode, result: var string) = + var + htmlCls = "admonition_warning" + texSz = "\\large" + texColor = "orange" + case n.text + of "hint", "note", "tip": + htmlCls = "admonition-info"; texSz = "\\normalsize"; texColor = "green" + of "attention", "admonition", "important", "warning": + htmlCls = "admonition-warning"; texSz = "\\large"; texColor = "orange" + of "danger", "error": + htmlCls = "admonition-error"; texSz = "\\Large"; texColor = "red" + else: discard + let txt = n.text.capitalizeAscii() + let htmlHead = "
                        " + renderAux(d, n, + htmlHead & "" & txt & + ":\n" & "$1
                        \n", + "\n\n\\begin{mdframed}[linecolor=" & texColor & "]\n" & + "{" & texSz & "\\color{" & texColor & "}{\\textbf{" & txt & ":}}} " & + "$1\n\\end{mdframed}\n", + result) + proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = if n == nil: return case n.kind @@ -1143,6 +1166,7 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = of rnBlockQuote: renderAux(d, n, "

                        $1

                        \n", "\\begin{quote}$1\\end{quote}\n", result) + of rnAdmonition: renderAdmonition(d, n, result) of rnTable, rnGridTable, rnMarkdownTable: renderAux(d, n, "$1
                        ", diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim index 85a96056a..3283af8c6 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -535,6 +535,55 @@ Test1 assert count(output1, "
                          ") == 1 + test "RST admonitions": + # check that all admonitions are implemented + let input0 = dedent """ + .. admonition:: endOf admonition + .. attention:: endOf attention + .. caution:: endOf caution + .. danger:: endOf danger + .. error:: endOf error + .. hint:: endOf hint + .. important:: endOf important + .. note:: endOf note + .. tip:: endOf tip + .. warning:: endOf warning + """ + let output0 = rstToHtml(input0, {roSupportMarkdown}, defaultConfig()) + for a in ["admonition", "attention", "caution", "danger", "error", "hint", + "important", "note", "tip", "warning" ]: + assert "endOf " & a & "
    " in output0 + + # Test that admonition does not swallow up the next paragraph. + let input1 = dedent """ + .. error:: endOfError + + Test paragraph. + """ + let output1 = rstToHtml(input1, {roSupportMarkdown}, defaultConfig()) + assert "endOfError" in output1 + assert "

    Test paragraph.

    " in output1 + assert "class=\"admonition admonition-error\"" in output1 + + # Test that second line is parsed as continuation of the first line. + let input2 = dedent """ + .. error:: endOfError + Test2p. + + Test paragraph. + """ + let output2 = rstToHtml(input2, {roSupportMarkdown}, defaultConfig()) + assert "endOfError Test2p." in output2 + assert "

    Test paragraph.

    " in output2 + assert "class=\"admonition admonition-error\"" in output2 + + let input3 = dedent """ + .. note:: endOfNote + """ + let output3 = rstToHtml(input3, {roSupportMarkdown}, defaultConfig()) + assert "endOfNote" in output3 + assert "class=\"admonition admonition-info\"" in output3 + suite "RST/Code highlight": test "Basic Python code highlight": let pythonCode = """ From 689504081f476ec23ebfd7123ad2f8b27c387a97 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sun, 27 Dec 2020 04:59:32 -0600 Subject: [PATCH 1215/3768] follow #15357 and move decodeQuery (#15860) * follow #15357 and move decodeQuery * solve problem one * minor * deprecate decodeData * add changelog and since * add testcase for decodeQuery --- changelog.md | 1 + lib/pure/cgi.nim | 50 +++++++++++------------------------ lib/pure/uri.nim | 43 ++++++++++++++++++++++++++++++ tests/stdlib/tdecodequery.nim | 7 +++++ 4 files changed, 67 insertions(+), 34 deletions(-) create mode 100644 tests/stdlib/tdecodequery.nim diff --git a/changelog.md b/changelog.md index a6a865514..6b12891c5 100644 --- a/changelog.md +++ b/changelog.md @@ -55,6 +55,7 @@ - `writeStackTrace` is available in JS backend now. +- Added `decodeQuery` to `std/uri`. - `strscans.scanf` now supports parsing single characters. - `strscans.scanTuple` added which uses `strscans.scanf` internally, returning a tuple which can be unpacked for easier usage of `scanf`. diff --git a/lib/pure/cgi.nim b/lib/pure/cgi.nim index cb64a3b1b..d3a762911 100644 --- a/lib/pure/cgi.nim +++ b/lib/pure/cgi.nim @@ -32,8 +32,10 @@ import strutils, os, strtabs, cookies, uri export uri.encodeUrl, uri.decodeUrl + import std/private/decode_helpers + proc addXmlChar(dest: var string, c: char) {.inline.} = case c of '&': add(dest, "&") @@ -53,18 +55,15 @@ proc xmlEncode*(s: string): string = for i in 0..len(s)-1: addXmlChar(result, s[i]) type - CgiError* = object of IOError ## exception that is raised if a CGI error occurs + CgiError* = object of IOError ## Exception that is raised if a CGI error occurs RequestMethod* = enum ## the used request method methodNone, ## no REQUEST_METHOD environment variable methodPost, ## query uses the POST method methodGet ## query uses the GET method proc cgiError*(msg: string) {.noreturn.} = - ## raises an ECgi exception with message `msg`. - var e: ref CgiError - new(e) - e.msg = msg - raise e + ## Raises a ``CgiError`` exception with message `msg`. + raise newException(CgiError, msg) proc getEncodedData(allowedMethods: set[RequestMethod]): string = case getEnv("REQUEST_METHOD").string @@ -88,40 +87,23 @@ proc getEncodedData(allowedMethods: set[RequestMethod]): string = iterator decodeData*(data: string): tuple[key, value: TaintedString] = ## Reads and decodes CGI data and yields the (name, value) pairs the ## data consists of. - proc parseData(data: string, i: int, field: var string): int = - result = i - while result < data.len: - case data[result] - of '%': add(field, decodePercent(data, result)) - of '+': add(field, ' ') - of '=', '&': break - else: add(field, data[result]) - inc(result) - - var i = 0 - var name = "" - var value = "" - # decode everything in one pass: - while i < data.len: - setLen(name, 0) # reuse memory - i = parseData(data, i, name) - setLen(value, 0) # reuse memory - if i < data.len and data[i] == '=': - inc(i) # skip '=' - i = parseData(data, i, value) - yield (name.TaintedString, value.TaintedString) - if i < data.len: - if data[i] == '&': inc(i) - else: cgiError("'&' expected") + try: + for (key, value) in uri.decodeQuery(data): + yield (key, value) + except UriParseError as e: + cgiError(e.msg) iterator decodeData*(allowedMethods: set[RequestMethod] = {methodNone, methodPost, methodGet}): tuple[key, value: TaintedString] = ## Reads and decodes CGI data and yields the (name, value) pairs the ## data consists of. If the client does not use a method listed in the - ## `allowedMethods` set, an `ECgi` exception is raised. + ## `allowedMethods` set, a ``CgiError`` exception is raised. let data = getEncodedData(allowedMethods) - for key, value in decodeData(data): - yield (key, value) + try: + for (key, value) in uri.decodeQuery(data): + yield (key, value) + except UriParseError as e: + cgiError(e.msg) proc readData*(allowedMethods: set[RequestMethod] = {methodNone, methodPost, methodGet}): StringTableRef = diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim index e993c240d..7f553be1a 100644 --- a/lib/pure/uri.nim +++ b/lib/pure/uri.nim @@ -59,6 +59,13 @@ type opaque*: bool isIpv6: bool # not expose it for compatibility. + UriParseError* = object of ValueError + + +proc uriParseError*(msg: string) {.noreturn.} = + ## Raises a ``UriParseError`` exception with message `msg`. + raise newException(UriParseError, msg) + func encodeUrl*(s: string, usePlus = true): string = ## Encodes a URL according to RFC3986. ## @@ -153,6 +160,42 @@ func encodeQuery*(query: openArray[(string, string)], usePlus = true, result.add('=') result.add(encodeUrl(val, usePlus)) +iterator decodeQuery*(data: string): tuple[key, value: TaintedString] = + ## Reads and decodes query string ``data`` and yields the (key, value) pairs the + ## data consists of. + runnableExamples: + import std/sugar + let s = collect(newSeq): + for k, v in decodeQuery("foo=1&bar=2"): (k, v) + doAssert s == @[("foo", "1"), ("bar", "2")] + + proc parseData(data: string, i: int, field: var string): int = + result = i + while result < data.len: + case data[result] + of '%': add(field, decodePercent(data, result)) + of '+': add(field, ' ') + of '=', '&': break + else: add(field, data[result]) + inc(result) + + var i = 0 + var name = "" + var value = "" + # decode everything in one pass: + while i < data.len: + setLen(name, 0) # reuse memory + i = parseData(data, i, name) + setLen(value, 0) # reuse memory + if i < data.len and data[i] == '=': + inc(i) # skip '=' + i = parseData(data, i, value) + yield (name.TaintedString, value.TaintedString) + if i < data.len: + if data[i] == '&': inc(i) + else: + uriParseError("'&' expected at index '$#' for '$#'" % [$i, data]) + func parseAuthority(authority: string, result: var Uri) = var i = 0 var inPort = false diff --git a/tests/stdlib/tdecodequery.nim b/tests/stdlib/tdecodequery.nim new file mode 100644 index 000000000..ae180742f --- /dev/null +++ b/tests/stdlib/tdecodequery.nim @@ -0,0 +1,7 @@ +import std/[uri, sequtils] + + +block: + doAssert toSeq(decodeQuery("a=1&b=0")) == @[("a", "1"), ("b", "0")] + doAssertRaises(UriParseError): + discard toSeq(decodeQuery("a=1&b=2c=6")) From 0c8ce2dccf6f779dae01a6a66cf7a29b50796481 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Sun, 27 Dec 2020 08:02:10 -0300 Subject: [PATCH 1216/3768] Save some alloc on base64 using encodeSize (#16465) --- lib/pure/base64.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pure/base64.nim b/lib/pure/base64.nim index 40c9af905..2abdc805d 100644 --- a/lib/pure/base64.nim +++ b/lib/pure/base64.nim @@ -193,6 +193,7 @@ proc encodeMime*(s: string, lineLen = 75, newLine = "\r\n"): string = ## * `decode proc<#decode,string>`_ for decoding a string runnableExamples: assert encodeMime("Hello World", 4, "\n") == "SGVs\nbG8g\nV29y\nbGQ=" + result = newStringOfCap(encodeSize(s.len)) for i, c in encode(s): if i != 0 and (i mod lineLen == 0): result.add(newLine) From 357729639ff970ba934a0dea2ae06ff063e37910 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 27 Dec 2020 05:35:01 -0800 Subject: [PATCH 1217/3768] fix #16469 vm float constants: do not conflate -0.0 and 0.0 (#16470) * fix #16469 vm float constants: do not conflate -0.0 and 0.0 * fix test for 32bit --- compiler/vmgen.nim | 7 ++++++- tests/float/tfloatnan.nim | 15 ++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 2fbb78c8f..9f36fc736 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -453,7 +453,12 @@ proc sameConstant*(a, b: PNode): bool = of nkSym: result = a.sym == b.sym of nkIdent: result = a.ident.id == b.ident.id of nkCharLit..nkUInt64Lit: result = a.intVal == b.intVal - of nkFloatLit..nkFloat64Lit: result = a.floatVal == b.floatVal + of nkFloatLit..nkFloat64Lit: + result = cast[uint64](a.floatVal) == cast[uint64](b.floatVal) + # refs bug #16469 + # if we wanted to only distinguish 0.0 vs -0.0: + # if a.floatVal == 0.0: result = cast[uint64](a.floatVal) == cast[uint64](b.floatVal) + # else: result = a.floatVal == b.floatVal of nkStrLit..nkTripleStrLit: result = a.strVal == b.strVal of nkType, nkNilLit: result = a.typ == b.typ of nkEmpty: result = true diff --git a/tests/float/tfloatnan.nim b/tests/float/tfloatnan.nim index 8f384c3d9..9e3dd94f6 100644 --- a/tests/float/tfloatnan.nim +++ b/tests/float/tfloatnan.nim @@ -15,7 +15,7 @@ echo "Nim: ", f32, " (float)" let f64: float64 = NaN echo "Nim: ", f64, " (double)" -block: # issue #10305 +block: # bug #10305 # with `-O3 -ffast-math`, generated C/C++ code is not nan compliant # user can pass `--passC:-ffast-math` if he doesn't care. proc fun() = @@ -42,3 +42,16 @@ block: # issue #10305 fun() fun2(0) +template main() = + # xxx move all tests under here + block: # bug #16469 + let a1 = 0.0 + let a2 = -0.0 + let a3 = 1.0 / a1 + let a4 = 1.0 / a2 + doAssert a3 == Inf + doAssert a4 == -Inf + doAssert $(a1, a2, a3, a4) == "(0.0, -0.0, inf, -inf)" + +static: main() +main() From 271f68259b5c42f515a707cd51bd500a298ec4a0 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sun, 27 Dec 2020 07:45:57 -0600 Subject: [PATCH 1218/3768] remove some noises in tests (#16448) --- tests/metatype/utypeclasses.nim | 4 +- tests/objects/tobject.nim | 10 +--- tests/stdlib/nre/captures.nim | 16 ++--- tests/stdlib/nre/escape.nim | 4 +- tests/stdlib/nre/find.nim | 12 ++-- tests/stdlib/nre/init.nim | 10 ++-- tests/stdlib/nre/match.nim | 8 +-- tests/stdlib/nre/misc.nim | 6 +- tests/stdlib/nre/replace.nim | 8 +-- tests/stdlib/nre/split.nim | 14 ++--- tests/stdlib/tmath.nim | 55 +++++++---------- tests/stdlib/tnet.nim | 30 +++++----- tests/stdlib/tnre.nim | 19 ------ tests/stdlib/tparseuints.nim | 9 +-- tests/stdlib/ttimes.nim | 102 ++++++++++++++++---------------- tests/system/tio.nim | 8 +-- tests/template/utemplates.nim | 6 +- 17 files changed, 140 insertions(+), 181 deletions(-) diff --git a/tests/metatype/utypeclasses.nim b/tests/metatype/utypeclasses.nim index 06bab375e..f94b39742 100644 --- a/tests/metatype/utypeclasses.nim +++ b/tests/metatype/utypeclasses.nim @@ -3,11 +3,11 @@ import unittest proc concat(a, b): string = result = $a & $b -test "if proc param types are not supplied, the params are assumed to be generic": +block: # if proc param types are not supplied, the params are assumed to be generic check concat(1, "test") == "1test" check concat(1, 20) == "120" check concat("foo", "bar") == "foobar" -test "explicit param types can still be specified": +block: # explicit param types can still be specified check concat[cstring, cstring]("x", "y") == "xy" diff --git a/tests/objects/tobject.nim b/tests/objects/tobject.nim index d166d5385..543a86376 100644 --- a/tests/objects/tobject.nim +++ b/tests/objects/tobject.nim @@ -1,7 +1,3 @@ -discard """ -output: "\n[Suite] object basic methods" -""" - import unittest type Obj = object @@ -10,12 +6,12 @@ type Obj = object proc makeObj(x: int): Obj = result.foo = x -suite "object basic methods": - test "it should convert an object to a string": +block: # object basic methods + block: # it should convert an object to a string var obj = makeObj(1) # Should be "obj: (foo: 1)" or similar. check($obj == "(foo: 1)") - test "it should test equality based on fields": + block: # it should test equality based on fields check(makeObj(1) == makeObj(1)) # bug #10203 diff --git a/tests/stdlib/nre/captures.nim b/tests/stdlib/nre/captures.nim index bd5e83ecc..acc141baf 100644 --- a/tests/stdlib/nre/captures.nim +++ b/tests/stdlib/nre/captures.nim @@ -1,12 +1,12 @@ import unittest, optional_nonstrict include nre -suite "captures": - test "map capture names to numbers": +block: # captures + block: # map capture names to numbers check(getNameToNumberTable(re("(?1(?2(?3))(?'v4'4))()")) == { "v1" : 0, "v2" : 1, "v3" : 2, "v4" : 3 }.toTable()) - test "capture bounds are correct": + block: # capture bounds are correct let ex1 = re("([0-9])") check("1 23".find(ex1).matchBounds == 0 .. 0) check("1 23".find(ex1).captureBounds[0] == 0 .. 0) @@ -20,7 +20,7 @@ suite "captures": let ex3 = re("([0-9]+)") check("824".find(ex3).captureBounds[0] == 0 .. 2) - test "named captures": + block: # named captures let ex1 = "foobar".find(re("(?foo)(?bar)")) check(ex1.captures["foo"] == "foo") check(ex1.captures["bar"] == "bar") @@ -32,7 +32,7 @@ suite "captures": expect KeyError: discard ex2.captures["bar"] - test "named capture bounds": + block: # named capture bounds let ex1 = "foo".find(re("(?foo)(?bar)?")) check("foo" in ex1.captureBounds) check(ex1.captureBounds["foo"] == 0..2) @@ -40,12 +40,12 @@ suite "captures": expect KeyError: discard ex1.captures["bar"] - test "capture count": + block: # capture count let ex1 = re("(?foo)(?bar)?") check(ex1.captureCount == 2) check(ex1.captureNameId == {"foo" : 0, "bar" : 1}.toTable()) - test "named capture table": + block: # named capture table let ex1 = "foo".find(re("(?foo)(?bar)?")) check(ex1.captures.toTable == {"foo" : "foo"}.toTable()) check(ex1.captureBounds.toTable == {"foo" : 0..2}.toTable()) @@ -53,7 +53,7 @@ suite "captures": let ex2 = "foobar".find(re("(?foo)(?bar)?")) check(ex2.captures.toTable == {"foo" : "foo", "bar" : "bar"}.toTable()) - test "capture sequence": + block: # capture sequence let ex1 = "foo".find(re("(?foo)(?bar)?")) check(ex1.captures.toSeq == @[some("foo"), none(string)]) check(ex1.captureBounds.toSeq == @[some(0..2), none(Slice[int])]) diff --git a/tests/stdlib/nre/escape.nim b/tests/stdlib/nre/escape.nim index db5e8a001..5e7dc0c0e 100644 --- a/tests/stdlib/nre/escape.nim +++ b/tests/stdlib/nre/escape.nim @@ -1,7 +1,7 @@ import nre, unittest -suite "escape strings": - test "escape strings": +block: # escape strings + block: # escape strings check("123".escapeRe() == "123") check("[]".escapeRe() == r"\[\]") check("()".escapeRe() == r"\(\)") diff --git a/tests/stdlib/nre/find.nim b/tests/stdlib/nre/find.nim index caa953ff4..7e7555d73 100644 --- a/tests/stdlib/nre/find.nim +++ b/tests/stdlib/nre/find.nim @@ -3,23 +3,23 @@ import nre except toSeq import optional_nonstrict import times, strutils -suite "find": - test "find text": +block: # find + block: # find text check("3213a".find(re"[a-z]").match == "a") check(toSeq(findIter("1 2 3 4 5 6 7 8 ", re" ")).map( proc (a: RegexMatch): string = a.match ) == @[" ", " ", " ", " ", " ", " ", " ", " "]) - test "find bounds": + block: # find bounds check(toSeq(findIter("1 2 3 4 5 ", re" ")).map( proc (a: RegexMatch): Slice[int] = a.matchBounds ) == @[1..1, 3..3, 5..5, 7..7, 9..9]) - test "overlapping find": + block: # overlapping find check("222".findAll(re"22") == @["22"]) check("2222".findAll(re"22") == @["22", "22"]) - test "len 0 find": + block: # len 0 find check("".findAll(re"\ ") == newSeq[string]()) check("".findAll(re"") == @[""]) check("abc".findAll(re"") == @["", "", "", ""]) @@ -27,7 +27,7 @@ suite "find": check("word\r\lword".findAll(re"(*ANYCRLF)(?m)$") == @["", ""]) check("слово слово".findAll(re"(*U)\b") == @["", "", "", ""]) - test "bail early": + block: # bail early ## we expect nothing to be found and we should be bailing out early which means that ## the timing difference between searching in small and large data should be well ## within a tolerance margin diff --git a/tests/stdlib/nre/init.nim b/tests/stdlib/nre/init.nim index 26e668104..f0c8e0a00 100644 --- a/tests/stdlib/nre/init.nim +++ b/tests/stdlib/nre/init.nim @@ -1,12 +1,12 @@ import unittest include nre -suite "Test NRE initialization": - test "correct initialization": +block: # Test NRE initialization + block: # correct initialization check(re("[0-9]+") != nil) check(re("(?i)[0-9]+") != nil) - test "options": + block: # options check(extractOptions("(*NEVER_UTF)") == ("", pcre.NEVER_UTF, true)) check(extractOptions("(*UTF8)(*ANCHORED)(*UCP)z") == @@ -19,14 +19,14 @@ suite "Test NRE initialization": check(extractOptions("(*LIMIT_MATCH=6)(*ANCHORED)z") == ("(*LIMIT_MATCH=6)z", pcre.ANCHORED, true)) - test "incorrect options": + block: # incorrect options for s in ["CR", "(CR", "(*CR", "(*abc)", "(*abc)CR", "(?i)", "(*LIMIT_MATCH=5", "(*NO_AUTO_POSSESS=5)"]: let ss = s & "(*NEVER_UTF)" check(extractOptions(ss) == (ss, 0, true)) - test "invalid regex": + block: # invalid regex expect(SyntaxError): discard re("[0-9") try: discard re("[0-9") diff --git a/tests/stdlib/nre/match.nim b/tests/stdlib/nre/match.nim index 06b69fd04..7e09a4b2f 100644 --- a/tests/stdlib/nre/match.nim +++ b/tests/stdlib/nre/match.nim @@ -1,12 +1,12 @@ include nre, unittest, optional_nonstrict -suite "match": - test "upper bound must be inclusive": +block: # match + block: # upper bound must be inclusive check("abc".match(re"abc", endpos = -1) == none(RegexMatch)) check("abc".match(re"abc", endpos = 1) == none(RegexMatch)) check("abc".match(re"abc", endpos = 2) != none(RegexMatch)) - test "match examples": + block: # match examples check("abc".match(re"(\w)").captures[0] == "a") check("abc".match(re"(?\w)").captures["letter"] == "a") check("abc".match(re"(\w)\w").captures[-1] == "ab") @@ -14,5 +14,5 @@ suite "match": check("abc".match(re"").captureBounds[-1] == 0 .. -1) check("abc".match(re"abc").captureBounds[-1] == 0 .. 2) - test "match test cases": + block: # match test cases check("123".match(re"").matchBounds == 0 .. -1) diff --git a/tests/stdlib/nre/misc.nim b/tests/stdlib/nre/misc.nim index f4a88b639..dbb0ecdf9 100644 --- a/tests/stdlib/nre/misc.nim +++ b/tests/stdlib/nre/misc.nim @@ -1,11 +1,11 @@ import unittest, nre, strutils, optional_nonstrict -suite "Misc tests": - test "unicode": +block: # Misc tests + block: # unicode check("".find(re"(*UTF8)").match == "") check("перевірка".replace(re"(*U)\w", "") == "") - test "empty or non-empty match": + block: # empty or non-empty match check("abc".findall(re"|.").join(":") == ":a::b::c:") check("abc".findall(re".|").join(":") == "a:b:c:") diff --git a/tests/stdlib/nre/replace.nim b/tests/stdlib/nre/replace.nim index 6f3436410..5cf659f21 100644 --- a/tests/stdlib/nre/replace.nim +++ b/tests/stdlib/nre/replace.nim @@ -1,13 +1,13 @@ include nre import unittest -suite "replace": - test "replace with 0-length strings": +block: # replace + block: # replace with 0-length strings check("".replace(re"1", proc (v: RegexMatch): string = "1") == "") check(" ".replace(re"", proc (v: RegexMatch): string = "1") == "1 1") check("".replace(re"", proc (v: RegexMatch): string = "1") == "1") - test "regular replace": + block: # regular replace check("123".replace(re"\d", "foo") == "foofoofoo") check("123".replace(re"(\d)", "$1$1") == "112233") check("123".replace(re"(\d)(\d)", "$1$2") == "123") @@ -15,7 +15,7 @@ suite "replace": check("123".replace(re"(?\d)(\d)", "$foo$#$#") == "1123") check("123".replace(re"(?\d)(\d)", "${foo}$#$#") == "1123") - test "replacing missing captures should throw instead of segfaulting": + block: # replacing missing captures should throw instead of segfaulting expect IndexDefect: discard "ab".replace(re"(a)|(b)", "$1$2") expect IndexDefect: discard "b".replace(re"(a)?(b)", "$1$2") expect KeyError: discard "b".replace(re"(a)?", "${foo}") diff --git a/tests/stdlib/nre/split.nim b/tests/stdlib/nre/split.nim index 9d57ea7d8..3cd57bb82 100644 --- a/tests/stdlib/nre/split.nim +++ b/tests/stdlib/nre/split.nim @@ -1,8 +1,8 @@ import unittest, strutils include nre -suite "string splitting": - test "splitting strings": +block: # string splitting + block: # splitting strings check("1 2 3 4 5 6 ".split(re" ") == @["1", "2", "3", "4", "5", "6", ""]) check("1 2 ".split(re(" ")) == @["1", "", "2", "", ""]) check("1 2".split(re(" ")) == @["1", "2"]) @@ -10,22 +10,22 @@ suite "string splitting": check("".split(re"foo") == @[""]) check("9".split(re"\son\s") == @["9"]) - test "captured patterns": + block: # captured patterns check("12".split(re"(\d)") == @["", "1", "", "2", ""]) - test "maxsplit": + block: # maxsplit check("123".split(re"", maxsplit = 2) == @["1", "23"]) check("123".split(re"", maxsplit = 1) == @["123"]) check("123".split(re"", maxsplit = -1) == @["1", "2", "3"]) - test "split with 0-length match": + block: # split with 0-length match check("12345".split(re("")) == @["1", "2", "3", "4", "5"]) check("".split(re"") == newSeq[string]()) check("word word".split(re"\b") == @["word", " ", "word"]) check("word\r\lword".split(re"(*ANYCRLF)(?m)$") == @["word", "\r\lword"]) check("слово слово".split(re"(*U)(\b)") == @["", "слово", "", " ", "", "слово", ""]) - test "perl split tests": + block: # perl split tests check("forty-two" .split(re"") .join(",") == "f,o,r,t,y,-,t,w,o") check("forty-two" .split(re"", 3) .join(",") == "f,o,rty-two") check("split this string" .split(re" ") .join(",") == "split,this,string") @@ -47,7 +47,7 @@ suite "string splitting": check("" .split(re"") .len == 0) check(":" .split(re"") .len == 1) - test "start position": + block: # start position check("abc".split(re"", start = 1) == @["b", "c"]) check("abc".split(re"", start = 2) == @["c"]) check("abc".split(re"", start = 3) == newSeq[string]()) diff --git a/tests/stdlib/tmath.nim b/tests/stdlib/tmath.nim index 43d19f9e0..0f66a94d1 100644 --- a/tests/stdlib/tmath.nim +++ b/tests/stdlib/tmath.nim @@ -1,18 +1,6 @@ discard """ action: run - output: ''' - -[Suite] random int - -[Suite] random float - -[Suite] cumsum - -[Suite] random sample - -[Suite] ^ -''' -matrix:"; -d:nimTmathCase2 -d:danger --passc:-ffast-math" + matrix:"; -d:nimTmathCase2 -d:danger --passc:-ffast-math" """ # xxx: fix bugs for js then add: targets:"c js" @@ -21,22 +9,22 @@ import math, random, os import unittest import sets, tables -suite "random int": - test "there might be some randomness": +block: # random int + block: # there might be some randomness var set = initHashSet[int](128) for i in 1..1000: incl(set, rand(high(int))) check len(set) == 1000 - test "single number bounds work": + block: # single number bounds work var rand: int for i in 1..1000: rand = rand(1000) check rand < 1000 check rand > -1 - test "slice bounds work": + block: # slice bounds work var rand: int for i in 1..1000: rand = rand(100..1000) @@ -45,8 +33,8 @@ suite "random int": else: check rand < 1000 check rand >= 100 - test " again gives new numbers": + block: # again gives new numbers var rand1 = rand(1000000) when not defined(js): os.sleep(200) @@ -55,28 +43,29 @@ suite "random int": check rand1 != rand2 -suite "random float": - test "there might be some randomness": +block: # random float + block: # there might be some randomness var set = initHashSet[float](128) for i in 1..100: incl(set, rand(1.0)) check len(set) == 100 - test "single number bounds work": + block: # single number bounds work var rand: float for i in 1..1000: rand = rand(1000.0) check rand < 1000.0 check rand > -1.0 - test "slice bounds work": + block: # slice bounds work var rand: float for i in 1..1000: rand = rand(100.0..1000.0) check rand < 1000.0 check rand >= 100.0 - test " again gives new numbers": + + block: # again gives new numbers var rand1:float = rand(1000000.0) when not defined(js): @@ -85,27 +74,27 @@ suite "random float": var rand2:float = rand(1000000.0) check rand1 != rand2 -suite "cumsum": - test "cumsum int seq return": +block: # cumsum + block: # cumsum int seq return let counts = [ 1, 2, 3, 4 ] check counts.cumsummed == [ 1, 3, 6, 10 ] - test "cumsum float seq return": + block: # cumsum float seq return let counts = [ 1.0, 2.0, 3.0, 4.0 ] check counts.cumsummed == [ 1.0, 3.0, 6.0, 10.0 ] - test "cumsum int in-place": + block: # cumsum int in-place var counts = [ 1, 2, 3, 4 ] counts.cumsum check counts == [ 1, 3, 6, 10 ] - test "cumsum float in-place": + block: # cumsum float in-place var counts = [ 1.0, 2.0, 3.0, 4.0 ] counts.cumsum check counts == [ 1.0, 3.0, 6.0, 10.0 ] -suite "random sample": - test "non-uniform array sample unnormalized int CDF": +block: # random sample + block: # "non-uniform array sample unnormalized int CDF let values = [ 10, 20, 30, 40, 50 ] # values let counts = [ 4, 3, 2, 1, 0 ] # weights aka unnormalized probabilities var histo = initCountTable[int]() @@ -127,7 +116,7 @@ suite "random sample": let stdDev = sqrt(n * p * (1.0 - p)) check abs(float(histo[values[i]]) - expected) <= 3.0 * stdDev - test "non-uniform array sample normalized float CDF": + block: # non-uniform array sample normalized float CDF let values = [ 10, 20, 30, 40, 50 ] # values let counts = [ 0.4, 0.3, 0.2, 0.1, 0 ] # probabilities var histo = initCountTable[int]() @@ -146,8 +135,8 @@ suite "random sample": # NOTE: like unnormalized int CDF test, P(wholeTestFails) =~ 0.01. check abs(float(histo[values[i]]) - expected) <= 3.0 * stdDev -suite "^": - test "compiles for valid types": +block: # ^ + block: # compiles for valid types check: compiles(5 ^ 2) check: compiles(5.5 ^ 2) check: compiles(5.5 ^ 2.int8) diff --git a/tests/stdlib/tnet.nim b/tests/stdlib/tnet.nim index 2dd22796c..b19d31f6c 100644 --- a/tests/stdlib/tnet.nim +++ b/tests/stdlib/tnet.nim @@ -5,48 +5,48 @@ outputsub: "" import net, nativesockets import unittest -suite "isIpAddress tests": - test "127.0.0.1 is valid": +block: # isIpAddress tests + block: # 127.0.0.1 is valid check isIpAddress("127.0.0.1") == true - test "ipv6 localhost is valid": + block: # ipv6 localhost is valid check isIpAddress("::1") == true - test "fqdn is not an ip address": + block: # fqdn is not an ip address check isIpAddress("example.com") == false - test "random string is not an ipaddress": + block: # random string is not an ipaddress check isIpAddress("foo bar") == false - test "5127.0.0.1 is invalid": + block: # 5127.0.0.1 is invalid check isIpAddress("5127.0.0.1") == false - test "ipv6 is valid": + block: # ipv6 is valid check isIpAddress("2001:cdba:0000:0000:0000:0000:3257:9652") == true - test "invalid ipv6": + block: # invalid ipv6 check isIpAddress("gggg:cdba:0000:0000:0000:0000:3257:9652") == false -suite "parseIpAddress tests": - test "127.0.0.1 is valid": +block: # parseIpAddress tests + block: # 127.0.0.1 is valid discard parseIpAddress("127.0.0.1") - test "ipv6 localhost is valid": + block: # ipv6 localhost is valid discard parseIpAddress("::1") - test "fqdn is not an ip address": + block: # fqdn is not an ip address expect(ValueError): discard parseIpAddress("example.com") - test "random string is not an ipaddress": + block: # random string is not an ipaddress expect(ValueError): discard parseIpAddress("foo bar") - test "ipv6 is valid": + block: # ipv6 is valid discard parseIpAddress("2001:cdba:0000:0000:0000:0000:3257:9652") - test "invalid ipv6": + block: # invalid ipv6 expect(ValueError): discard parseIpAddress("gggg:cdba:0000:0000:0000:0000:3257:9652") diff --git a/tests/stdlib/tnre.nim b/tests/stdlib/tnre.nim index d2dc1a7c5..f13c16052 100644 --- a/tests/stdlib/tnre.nim +++ b/tests/stdlib/tnre.nim @@ -2,25 +2,6 @@ discard """ # Since the tests for nre are all bundled together we treat failure in one test as an nre failure # When running 'testament/tester' a failed check() in the test suite will cause the exit # codes to differ and be reported as a failure - - output: - ''' - -[Suite] Test NRE initialization - -[Suite] captures - -[Suite] find - -[Suite] string splitting - -[Suite] match - -[Suite] replace - -[Suite] escape strings - -[Suite] Misc tests''' """ import nre diff --git a/tests/stdlib/tparseuints.nim b/tests/stdlib/tparseuints.nim index 72041da66..ef8c782b3 100644 --- a/tests/stdlib/tparseuints.nim +++ b/tests/stdlib/tparseuints.nim @@ -1,13 +1,6 @@ -discard """ - action: run - output: ''' - -[Suite] parseutils -''' -""" import unittest, strutils -suite "parseutils": +block: # parseutils check: parseBiggestUInt("0") == 0'u64 check: parseBiggestUInt("18446744073709551615") == 0xFFFF_FFFF_FFFF_FFFF'u64 expect(ValueError): diff --git a/tests/stdlib/ttimes.nim b/tests/stdlib/ttimes.nim index a7677edf9..dc9468def 100644 --- a/tests/stdlib/ttimes.nim +++ b/tests/stdlib/ttimes.nim @@ -116,7 +116,7 @@ template usingTimezone(tz: string, body: untyped) = body putEnv("TZ", oldZone) -suite "ttimes": +block: # ttimes # Generate tests for multiple timezone files where available # Set the TZ env var for each test @@ -143,7 +143,7 @@ suite "ttimes": test "parseTest": runTimezoneTests() - test "dst handling": + block: # dst handling usingTimezone("Europe/Stockholm"): # In case of an impossible time, the time is moved to after the # impossible time period @@ -163,7 +163,7 @@ suite "ttimes": check initDateTime(21, mOct, 2017, 01, 00, 00).format(f) == "2017-10-21 01:00 +02:00" - test "issue #6520": + block: # issue #6520 usingTimezone("Europe/Stockholm"): var local = fromUnix(1469275200).local var utc = fromUnix(1469275200).utc @@ -172,19 +172,19 @@ suite "ttimes": local.utcOffset = 0 check claimedOffset == utc.toTime - local.toTime - test "issue #5704": + block: # issue #5704 usingTimezone("Asia/Seoul"): let diff = parse("19700101-000000", "yyyyMMdd-hhmmss").toTime - parse("19000101-000000", "yyyyMMdd-hhmmss").toTime check diff == initDuration(seconds = 2208986872) - test "issue #6465": + block: # issue #6465 usingTimezone("Europe/Stockholm"): let dt = parse("2017-03-25 12:00", "yyyy-MM-dd hh:mm") check $(dt + initTimeInterval(days = 1)) == "2017-03-26T12:00:00+02:00" check $(dt + initDuration(days = 1)) == "2017-03-26T13:00:00+02:00" - test "adding/subtracting time across dst": + block: # adding/subtracting time across dst usingTimezone("Europe/Stockholm"): let dt1 = initDateTime(26, mMar, 2017, 03, 00, 00) check $(dt1 - 1.seconds) == "2017-03-26T01:59:59+01:00" @@ -192,55 +192,55 @@ suite "ttimes": var dt2 = initDateTime(29, mOct, 2017, 02, 59, 59) check $(dt2 + 1.seconds) == "2017-10-29T02:00:00+01:00" - test "datetime before epoch": + block: # datetime before epoch check $fromUnix(-2147483648).utc == "1901-12-13T20:45:52Z" - test "incorrect inputs: empty string": + block: # incorrect inputs: empty string parseTestExcp("", "yyyy-MM-dd") - test "incorrect inputs: year": + block: # incorrect inputs: year parseTestExcp("20-02-19", "yyyy-MM-dd") - test "incorrect inputs: month number": + block: # incorrect inputs: month number parseTestExcp("2018-2-19", "yyyy-MM-dd") - test "incorrect inputs: month name": + block: # incorrect inputs: month name parseTestExcp("2018-Fe", "yyyy-MMM-dd") - test "incorrect inputs: day": + block: # incorrect inputs: day parseTestExcp("2018-02-1", "yyyy-MM-dd") - test "incorrect inputs: day of week": + block: # incorrect inputs: day of week parseTestExcp("2018-Feb-Mo", "yyyy-MMM-ddd") - test "incorrect inputs: hour": + block: # incorrect inputs: hour parseTestExcp("2018-02-19 1:30", "yyyy-MM-dd hh:mm") - test "incorrect inputs: minute": + block: # incorrect inputs: minute parseTestExcp("2018-02-19 16:3", "yyyy-MM-dd hh:mm") - test "incorrect inputs: second": + block: # incorrect inputs: second parseTestExcp("2018-02-19 16:30:0", "yyyy-MM-dd hh:mm:ss") - test "incorrect inputs: timezone (z)": + block: # incorrect inputs: timezone (z) parseTestExcp("2018-02-19 16:30:00 ", "yyyy-MM-dd hh:mm:ss z") - test "incorrect inputs: timezone (zz) 1": + block: # incorrect inputs: timezone (zz) 1 parseTestExcp("2018-02-19 16:30:00 ", "yyyy-MM-dd hh:mm:ss zz") - test "incorrect inputs: timezone (zz) 2": + block: # incorrect inputs: timezone (zz) 2 parseTestExcp("2018-02-19 16:30:00 +1", "yyyy-MM-dd hh:mm:ss zz") - test "incorrect inputs: timezone (zzz) 1": + block: # incorrect inputs: timezone (zzz) 1 parseTestExcp("2018-02-19 16:30:00 ", "yyyy-MM-dd hh:mm:ss zzz") - test "incorrect inputs: timezone (zzz) 2": + block: # incorrect inputs: timezone (zzz) 2 parseTestExcp("2018-02-19 16:30:00 +01:", "yyyy-MM-dd hh:mm:ss zzz") - test "incorrect inputs: timezone (zzz) 3": + block: # incorrect inputs: timezone (zzz) 3 parseTestExcp("2018-02-19 16:30:00 +01:0", "yyyy-MM-dd hh:mm:ss zzz") - test "incorrect inputs: year (yyyy/uuuu)": + block: # incorrect inputs: year (yyyy/uuuu) parseTestExcp("-0001", "yyyy") parseTestExcp("-0001", "YYYY") parseTestExcp("1", "yyyy") @@ -249,7 +249,7 @@ suite "ttimes": parseTestExcp("12345", "uuuu") parseTestExcp("-1 BC", "UUUU g") - test "incorrect inputs: invalid sign": + block: # incorrect inputs: invalid sign parseTestExcp("+1", "YYYY") parseTestExcp("+1", "dd") parseTestExcp("+1", "MM") @@ -257,10 +257,10 @@ suite "ttimes": parseTestExcp("+1", "mm") parseTestExcp("+1", "ss") - test "_ as a separator": + block: # _ as a separator discard parse("2000_01_01", "YYYY'_'MM'_'dd") - test "dynamic timezone": + block: # dynamic timezone let tz = staticTz(seconds = -9000) let dt = initDateTime(1, mJan, 2000, 12, 00, 00, tz) check dt.utcOffset == -9000 @@ -269,13 +269,13 @@ suite "ttimes": check $dt.utc == "2000-01-01T09:30:00Z" check $dt.utc.inZone(tz) == $dt - test "isLeapYear": + block: # isLeapYear check isLeapYear(2016) check (not isLeapYear(2015)) check isLeapYear(2000) check (not isLeapYear(1900)) - test "TimeInterval": + block: # TimeInterval let t = fromUnix(876124714).utc # Mon 6 Oct 08:58:34 BST 1997 # Interval tests let t2 = t - 2.years @@ -287,7 +287,7 @@ suite "ttimes": check (t + 1.hours).toTime.toUnix == t.toTime.toUnix + 60 * 60 check (t - 1.hours).toTime.toUnix == t.toTime.toUnix - 60 * 60 - test "TimeInterval - months": + block: # TimeInterval - months var dt = initDateTime(1, mFeb, 2017, 00, 00, 00, utc()) check $(dt - initTimeInterval(months = 1)) == "2017-01-01T00:00:00Z" dt = initDateTime(15, mMar, 2017, 00, 00, 00, utc()) @@ -296,7 +296,7 @@ suite "ttimes": # This happens due to monthday overflow. It's consistent with Phobos. check $(dt - initTimeInterval(months = 1)) == "2017-03-03T00:00:00Z" - test "duration": + block: # duration let d = initDuration check d(hours = 48) + d(days = 5) == d(weeks = 1) let dt = initDateTime(01, mFeb, 2000, 00, 00, 00, 0, utc()) + d(milliseconds = 1) @@ -316,7 +316,7 @@ suite "ttimes": check (initDuration(seconds = 1, nanoseconds = 3) <= initDuration(seconds = 1, nanoseconds = 1)).not - test "large/small dates": + block: # large/small dates discard initDateTime(1, mJan, -35_000, 12, 00, 00, utc()) # with local tz discard initDateTime(1, mJan, -35_000, 12, 00, 00) @@ -328,7 +328,7 @@ suite "ttimes": let dt2 = dt + 35_001.years check $dt2 == "0001-01-01T12:00:01Z" - test "compare datetimes": + block: # compare datetimes var dt1 = now() var dt2 = dt1 check dt1 == dt2 @@ -336,7 +336,7 @@ suite "ttimes": dt2 = dt2 + 1.seconds check dt1 < dt2 - test "adding/subtracting TimeInterval": + block: # adding/subtracting TimeInterval # add/subtract TimeIntervals and Time/TimeInfo let now = getTime().utc let isSpecial = now.isLeapDay @@ -374,14 +374,14 @@ suite "ttimes": check initTime(0, 101).toWinTime.fromWinTime.nanosecond == 100 check initTime(0, 101).toWinTime.fromWinTime.nanosecond == 100 - test "issue 7620": + block: # issue 7620 let layout = "M/d/yyyy' 'h:mm:ss' 'tt' 'z" let t7620_am = parse("4/15/2017 12:01:02 AM +0", layout, utc()) check t7620_am.format(layout) == "4/15/2017 12:01:02 AM Z" let t7620_pm = parse("4/15/2017 12:01:02 PM +0", layout, utc()) check t7620_pm.format(layout) == "4/15/2017 12:01:02 PM Z" - test "format": + block: # format var dt = initDateTime(1, mJan, -0001, 17, 01, 02, 123_456_789, staticTz(hours = 1, minutes = 2, seconds = 3)) @@ -450,7 +450,7 @@ suite "ttimes": doAssert dt.format("zz") == tz[2] doAssert dt.format("zzz") == tz[3] - test "format locale": + block: # format locale let loc = DateTimeLocale( MMM: ["Fir","Sec","Thi","Fou","Fif","Six","Sev","Eig","Nin","Ten","Ele","Twe"], MMMM: ["Firsty", "Secondy", "Thirdy", "Fourthy", "Fifthy", "Sixthy", "Seventhy", "Eighthy", "Ninthy", "Tenthy", "Eleventhy", "Twelfthy"], @@ -467,7 +467,7 @@ suite "ttimes": check dt.format("MMM", loc) == "Fir" check dt.format("MMMM", loc) == "Firsty" - test "parse": + block: # parse check $parse("20180101", "yyyyMMdd", utc()) == "2018-01-01T00:00:00Z" parseTestExcp("+120180101", "yyyyMMdd") @@ -488,7 +488,7 @@ suite "ttimes": parseTestExcp("2000 A", "yyyy g") - test "parse locale": + block: # parse locale let loc = DateTimeLocale( MMM: ["Fir","Sec","Thi","Fou","Fif","Six","Sev","Eig","Nin","Ten","Ele","Twe"], MMMM: ["Firsty", "Secondy", "Thirdy", "Fourthy", "Fifthy", "Sixthy", "Seventhy", "Eighthy", "Ninthy", "Tenthy", "Eleventhy", "Twelfthy"], @@ -498,7 +498,7 @@ suite "ttimes": check $parse("02 Fir 2019", "dd MMM yyyy", utc(), loc) == "2019-01-02T00:00:00Z" check $parse("Fourthy 6, 2017", "MMMM d, yyyy", utc(), loc) == "2017-04-06T00:00:00Z" - test "timezoneConversion": + block: # timezoneConversion var l = now() let u = l.utc l = u.local @@ -506,7 +506,7 @@ suite "ttimes": check l.timezone == local() check u.timezone == utc() - test "getDayOfWeek": + block: # getDayOfWeek check getDayOfWeek(01, mJan, 0000) == dSat check getDayOfWeek(01, mJan, -0023) == dSat check getDayOfWeek(21, mSep, 1900) == dFri @@ -515,29 +515,29 @@ suite "ttimes": check getDayOfWeek(01, mJan, 2000) == dSat check getDayOfWeek(01, mJan, 2021) == dFri - test "between - simple": + block: # between - simple let x = initDateTime(10, mJan, 2018, 13, 00, 00) let y = initDateTime(11, mJan, 2018, 12, 00, 00) doAssert x + between(x, y) == y - test "between - dst start": + block: # between - dst start usingTimezone("Europe/Stockholm"): let x = initDateTime(25, mMar, 2018, 00, 00, 00) let y = initDateTime(25, mMar, 2018, 04, 00, 00) doAssert x + between(x, y) == y - test "between - empty interval": + block: # between - empty interval let x = now() let y = x doAssert x + between(x, y) == y - test "between - dst end": + block: # between - dst end usingTimezone("Europe/Stockholm"): let x = initDateTime(27, mOct, 2018, 02, 00, 00) let y = initDateTime(28, mOct, 2018, 01, 00, 00) doAssert x + between(x, y) == y - test "between - long day": + block: # between - long day usingTimezone("Europe/Stockholm"): # This day is 25 hours long in Europe/Stockholm let x = initDateTime(28, mOct, 2018, 00, 30, 00) @@ -545,7 +545,7 @@ suite "ttimes": doAssert between(x, y) == 24.hours + 30.minutes doAssert x + between(x, y) == y - test "between - offset change edge case": + block: # between - offset change edge case # This test case is important because in this case # `x + between(x.utc, y.utc) == y` is not true, which is very rare. usingTimezone("America/Belem"): @@ -554,19 +554,19 @@ suite "ttimes": doAssert x + between(x, y) == y doAssert y + between(y, x) == x - test "between - all units": + block: # between - all units let x = initDateTime(1, mJan, 2000, 00, 00, 00, utc()) let ti = initTimeInterval(1, 1, 1, 1, 1, 1, 1, 1, 1, 1) let y = x + ti doAssert between(x, y) == ti doAssert between(y, x) == -ti - test "between - monthday overflow": + block: # between - monthday overflow let x = initDateTime(31, mJan, 2001, 00, 00, 00, utc()) let y = initDateTime(1, mMar, 2001, 00, 00, 00, utc()) doAssert x + between(x, y) == y - test "between - misc": + block: # between - misc block: let x = initDateTime(31, mDec, 2000, 12, 00, 00, utc()) let y = initDateTime(01, mJan, 2001, 00, 00, 00, utc()) @@ -608,7 +608,7 @@ suite "ttimes": doAssert x + between(x, y) == y doAssert between(x, y) == 1.months + 1.weeks - test "default DateTime": # https://github.com/nim-lang/RFCs/issues/211 + block: # default DateTime https://github.com/nim-lang/RFCs/issues/211 var num = 0 for ai in Month: num.inc check num == 12 @@ -634,7 +634,7 @@ suite "ttimes": expect(AssertionDefect): discard a.format initTimeFormat("yyyy") expect(AssertionDefect): discard between(a, a) - test "inX procs": + block: # inX procs doAssert initDuration(seconds = 1).inSeconds == 1 doAssert initDuration(seconds = -1).inSeconds == -1 doAssert initDuration(seconds = -1, nanoseconds = 1).inSeconds == 0 diff --git a/tests/system/tio.nim b/tests/system/tio.nim index c4d041560..52a21837a 100644 --- a/tests/system/tio.nim +++ b/tests/system/tio.nim @@ -22,12 +22,12 @@ proc echoLoop(str: string): string = while not output.atEnd: result.add(output.readLine) -suite "io": - suite "readAll": - test "stdin": +block: # io + block: # readAll + block: # stdin check: echoLoop(STRING_DATA) == STRING_DATA - test "file": + block: # file check: readFile(TEST_FILE).strip == STRING_DATA diff --git a/tests/template/utemplates.nim b/tests/template/utemplates.nim index 017166250..7674ba7c0 100644 --- a/tests/template/utemplates.nim +++ b/tests/template/utemplates.nim @@ -3,11 +3,11 @@ import unittest template t(a: int): string = "int" template t(a: string): string = "string" -test "templates can be overloaded": +block: # templates can be overloaded check t(10) == "int" check t("test") == "string" -test "previous definitions can be further overloaded or hidden in local scopes": +block: # previous definitions can be further overloaded or hidden in local scopes template t(a: bool): string = "bool" check t(true) == "bool" @@ -17,7 +17,7 @@ test "previous definitions can be further overloaded or hidden in local scopes": check t(10) == "inner int" check t("test") == "string" -test "templates can be redefined multiple times": +block: # templates can be redefined multiple times template customAssert(cond: bool, msg: string): typed {.dirty.} = if not cond: fail(msg) From 792e4a0392519c7921a0afedb029d02b5485ca64 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sun, 27 Dec 2020 09:08:48 -0600 Subject: [PATCH 1219/3768] Revert #16478 (#16483) * minor * Revert "minor" This reverts commit ef1807cbb468bffdcfffb41f023644b57fb0fe11. --- compiler/llstream.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/llstream.nim b/compiler/llstream.nim index bd335c23d..6df927c60 100644 --- a/compiler/llstream.nim +++ b/compiler/llstream.nim @@ -110,7 +110,7 @@ proc llReadFromStdin(s: PLLStream, buf: pointer, bufLen: int): int = s.rd = 0 var line = newStringOfCap(120) var triples = 0 - while readLineFromStdin(if s.s.len == 0: "\n>>> " else: "... ", line): + while readLineFromStdin(if s.s.len == 0: ">>> " else: "... ", line): s.s.add(line) s.s.add("\n") inc triples, countTriples(line) From e718a4a058f9a9d0c9c8ce1d388040de7c14271d Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sun, 27 Dec 2020 12:46:21 -0600 Subject: [PATCH 1220/3768] follow #15860 clean cgi module (#16487) * follow #15860 clean cgi module * follow #15860 clean cgi module --- lib/pure/cgi.nim | 97 +++++++++++++++++++-------------------- tests/gc/growobjcrash.nim | 7 +-- tests/stdlib/tcgi.nim | 30 ++++-------- 3 files changed, 57 insertions(+), 77 deletions(-) diff --git a/lib/pure/cgi.nim b/lib/pure/cgi.nim index d3a762911..8d827f555 100644 --- a/lib/pure/cgi.nim +++ b/lib/pure/cgi.nim @@ -29,13 +29,10 @@ ## writeLine(stdout, "your password: " & myData["password"]) ## writeLine(stdout, "") -import strutils, os, strtabs, cookies, uri +import std/[strutils, os, strtabs, cookies, uri] export uri.encodeUrl, uri.decodeUrl -import std/private/decode_helpers - - proc addXmlChar(dest: var string, c: char) {.inline.} = case c of '&': add(dest, "&") @@ -46,23 +43,23 @@ proc addXmlChar(dest: var string, c: char) {.inline.} = proc xmlEncode*(s: string): string = ## Encodes a value to be XML safe: - ## * ``"`` is replaced by ``"`` - ## * ``<`` is replaced by ``<`` - ## * ``>`` is replaced by ``>`` - ## * ``&`` is replaced by ``&`` + ## * `"` is replaced by `"` + ## * `<` is replaced by `<` + ## * `>` is replaced by `>` + ## * `&` is replaced by `&` ## * every other character is carried over. result = newStringOfCap(s.len + s.len shr 2) for i in 0..len(s)-1: addXmlChar(result, s[i]) type - CgiError* = object of IOError ## Exception that is raised if a CGI error occurs - RequestMethod* = enum ## the used request method + CgiError* = object of IOError ## Exception that is raised if a CGI error occurs. + RequestMethod* = enum ## The used request method. methodNone, ## no REQUEST_METHOD environment variable methodPost, ## query uses the POST method methodGet ## query uses the GET method proc cgiError*(msg: string) {.noreturn.} = - ## Raises a ``CgiError`` exception with message `msg`. + ## Raises a `CgiError` exception with message `msg`. raise newException(CgiError, msg) proc getEncodedData(allowedMethods: set[RequestMethod]): string = @@ -97,7 +94,7 @@ iterator decodeData*(allowedMethods: set[RequestMethod] = {methodNone, methodPost, methodGet}): tuple[key, value: TaintedString] = ## Reads and decodes CGI data and yields the (name, value) pairs the ## data consists of. If the client does not use a method listed in the - ## `allowedMethods` set, a ``CgiError`` exception is raised. + ## `allowedMethods` set, a `CgiError` exception is raised. let data = getEncodedData(allowedMethods) try: for (key, value) in uri.decodeQuery(data): @@ -107,155 +104,155 @@ iterator decodeData*(allowedMethods: set[RequestMethod] = proc readData*(allowedMethods: set[RequestMethod] = {methodNone, methodPost, methodGet}): StringTableRef = - ## Read CGI data. If the client does not use a method listed in the - ## `allowedMethods` set, an `ECgi` exception is raised. + ## Reads CGI data. If the client does not use a method listed in the + ## `allowedMethods` set, a `CgiError` exception is raised. result = newStringTable() for name, value in decodeData(allowedMethods): result[name.string] = value.string proc readData*(data: string): StringTableRef = - ## Read CGI data from a string. + ## Reads CGI data from a string. result = newStringTable() for name, value in decodeData(data): result[name.string] = value.string proc validateData*(data: StringTableRef, validKeys: varargs[string]) = - ## validates data; raises `ECgi` if this fails. This checks that each variable + ## Validates data; raises `CgiError` if this fails. This checks that each variable ## name of the CGI `data` occurs in the `validKeys` array. for key, val in pairs(data): if find(validKeys, key) < 0: cgiError("unknown variable name: " & key) proc getContentLength*(): string = - ## returns contents of the ``CONTENT_LENGTH`` environment variable + ## Returns contents of the `CONTENT_LENGTH` environment variable. return getEnv("CONTENT_LENGTH").string proc getContentType*(): string = - ## returns contents of the ``CONTENT_TYPE`` environment variable + ## Returns contents of the `CONTENT_TYPE` environment variable. return getEnv("CONTENT_Type").string proc getDocumentRoot*(): string = - ## returns contents of the ``DOCUMENT_ROOT`` environment variable + ## Returns contents of the `DOCUMENT_ROOT` environment variable. return getEnv("DOCUMENT_ROOT").string proc getGatewayInterface*(): string = - ## returns contents of the ``GATEWAY_INTERFACE`` environment variable + ## Returns contents of the `GATEWAY_INTERFACE` environment variable. return getEnv("GATEWAY_INTERFACE").string proc getHttpAccept*(): string = - ## returns contents of the ``HTTP_ACCEPT`` environment variable + ## Returns contents of the `HTTP_ACCEPT` environment variable. return getEnv("HTTP_ACCEPT").string proc getHttpAcceptCharset*(): string = - ## returns contents of the ``HTTP_ACCEPT_CHARSET`` environment variable + ## Returns contents of the `HTTP_ACCEPT_CHARSET` environment variable. return getEnv("HTTP_ACCEPT_CHARSET").string proc getHttpAcceptEncoding*(): string = - ## returns contents of the ``HTTP_ACCEPT_ENCODING`` environment variable + ## Returns contents of the `HTTP_ACCEPT_ENCODING` environment variable. return getEnv("HTTP_ACCEPT_ENCODING").string proc getHttpAcceptLanguage*(): string = - ## returns contents of the ``HTTP_ACCEPT_LANGUAGE`` environment variable + ## Returns contents of the `HTTP_ACCEPT_LANGUAGE` environment variable. return getEnv("HTTP_ACCEPT_LANGUAGE").string proc getHttpConnection*(): string = - ## returns contents of the ``HTTP_CONNECTION`` environment variable + ## Returns contents of the `HTTP_CONNECTION` environment variable. return getEnv("HTTP_CONNECTION").string proc getHttpCookie*(): string = - ## returns contents of the ``HTTP_COOKIE`` environment variable + ## Returns contents of the `HTTP_COOKIE` environment variable. return getEnv("HTTP_COOKIE").string proc getHttpHost*(): string = - ## returns contents of the ``HTTP_HOST`` environment variable + ## Returns contents of the `HTTP_HOST` environment variable. return getEnv("HTTP_HOST").string proc getHttpReferer*(): string = - ## returns contents of the ``HTTP_REFERER`` environment variable + ## Returns contents of the `HTTP_REFERER` environment variable. return getEnv("HTTP_REFERER").string proc getHttpUserAgent*(): string = - ## returns contents of the ``HTTP_USER_AGENT`` environment variable + ## Returns contents of the `HTTP_USER_AGENT` environment variable. return getEnv("HTTP_USER_AGENT").string proc getPathInfo*(): string = - ## returns contents of the ``PATH_INFO`` environment variable + ## Returns contents of the `PATH_INFO` environment variable. return getEnv("PATH_INFO").string proc getPathTranslated*(): string = - ## returns contents of the ``PATH_TRANSLATED`` environment variable + ## Returns contents of the `PATH_TRANSLATED` environment variable. return getEnv("PATH_TRANSLATED").string proc getQueryString*(): string = - ## returns contents of the ``QUERY_STRING`` environment variable + ## Returns contents of the `QUERY_STRING` environment variable. return getEnv("QUERY_STRING").string proc getRemoteAddr*(): string = - ## returns contents of the ``REMOTE_ADDR`` environment variable + ## Returns contents of the `REMOTE_ADDR` environment variable. return getEnv("REMOTE_ADDR").string proc getRemoteHost*(): string = - ## returns contents of the ``REMOTE_HOST`` environment variable + ## Returns contents of the `REMOTE_HOST` environment variable. return getEnv("REMOTE_HOST").string proc getRemoteIdent*(): string = - ## returns contents of the ``REMOTE_IDENT`` environment variable + ## Returns contents of the `REMOTE_IDENT` environment variable. return getEnv("REMOTE_IDENT").string proc getRemotePort*(): string = - ## returns contents of the ``REMOTE_PORT`` environment variable + ## Returns contents of the `REMOTE_PORT` environment variable. return getEnv("REMOTE_PORT").string proc getRemoteUser*(): string = - ## returns contents of the ``REMOTE_USER`` environment variable + ## Returns contents of the `REMOTE_USER` environment variable. return getEnv("REMOTE_USER").string proc getRequestMethod*(): string = - ## returns contents of the ``REQUEST_METHOD`` environment variable + ## Returns contents of the `REQUEST_METHOD` environment variable. return getEnv("REQUEST_METHOD").string proc getRequestURI*(): string = - ## returns contents of the ``REQUEST_URI`` environment variable + ## Returns contents of the `REQUEST_URI` environment variable. return getEnv("REQUEST_URI").string proc getScriptFilename*(): string = - ## returns contents of the ``SCRIPT_FILENAME`` environment variable + ## Returns contents of the `SCRIPT_FILENAME` environment variable. return getEnv("SCRIPT_FILENAME").string proc getScriptName*(): string = - ## returns contents of the ``SCRIPT_NAME`` environment variable + ## Returns contents of the `SCRIPT_NAME` environment variable. return getEnv("SCRIPT_NAME").string proc getServerAddr*(): string = - ## returns contents of the ``SERVER_ADDR`` environment variable + ## Returns contents of the `SERVER_ADDR` environment variable. return getEnv("SERVER_ADDR").string proc getServerAdmin*(): string = - ## returns contents of the ``SERVER_ADMIN`` environment variable + ## Returns contents of the `SERVER_ADMIN` environment variable. return getEnv("SERVER_ADMIN").string proc getServerName*(): string = - ## returns contents of the ``SERVER_NAME`` environment variable + ## Returns contents of the `SERVER_NAME` environment variable. return getEnv("SERVER_NAME").string proc getServerPort*(): string = - ## returns contents of the ``SERVER_PORT`` environment variable + ## Returns contents of the `SERVER_PORT` environment variable. return getEnv("SERVER_PORT").string proc getServerProtocol*(): string = - ## returns contents of the ``SERVER_PROTOCOL`` environment variable + ## Returns contents of the `SERVER_PROTOCOL` environment variable. return getEnv("SERVER_PROTOCOL").string proc getServerSignature*(): string = - ## returns contents of the ``SERVER_SIGNATURE`` environment variable + ## Returns contents of the `SERVER_SIGNATURE` environment variable. return getEnv("SERVER_SIGNATURE").string proc getServerSoftware*(): string = - ## returns contents of the ``SERVER_SOFTWARE`` environment variable + ## Returns contents of the `SERVER_SOFTWARE` environment variable. return getEnv("SERVER_SOFTWARE").string proc setTestData*(keysvalues: varargs[string]) = - ## fills the appropriate environment variables to test your CGI application. + ## Fills the appropriate environment variables to test your CGI application. ## This can only simulate the 'GET' request method. `keysvalues` should ## provide embedded (name, value)-pairs. Example: ## @@ -273,7 +270,7 @@ proc setTestData*(keysvalues: varargs[string]) = putEnv("QUERY_STRING", query) proc writeContentType*() = - ## call this before starting to send your HTML data to `stdout`. This + ## Calls this before starting to send your HTML data to `stdout`. This ## implements this part of the CGI protocol: ## ## .. code-block:: Nim diff --git a/tests/gc/growobjcrash.nim b/tests/gc/growobjcrash.nim index 84fd30a4f..ff1aa7e98 100644 --- a/tests/gc/growobjcrash.nim +++ b/tests/gc/growobjcrash.nim @@ -1,8 +1,4 @@ -discard """ - output: "works" -""" - -import cgi, strtabs +import std/[cgi, strtabs] proc handleRequest(query: string): StringTableRef = iterator foo(): StringTableRef {.closure.} = @@ -26,4 +22,3 @@ proc main = quit "but now a leak" main() -echo "works" diff --git a/tests/stdlib/tcgi.nim b/tests/stdlib/tcgi.nim index cec188e35..993728712 100644 --- a/tests/stdlib/tcgi.nim +++ b/tests/stdlib/tcgi.nim @@ -1,25 +1,10 @@ -discard """ - output: ''' +import std/unittest +import std/[cgi, strtabs, sugar] -[Suite] Test cgi module -(key: "a", value: "1") -(key: "b", value: "0") -(key: "c", value: "3") -(key: "d", value: "") -(key: "e", value: "") -(key: "a", value: "5") -(key: "a", value: "t e x t") -(key: "e", value: "http://w3schools.com/my test.asp?name=ståle&car=saab") -''' -""" - -import unittest -import cgi, strtabs - -suite "Test cgi module": +block: # Test cgi module const queryString = "foo=bar&фу=бар&checked=✓&list=1,2,3&with_space=text%20with%20space" - test "test query parsing with readData": + block: # test query parsing with readData let parsedQuery = readData(queryString) check parsedQuery["foo"] == "bar" @@ -34,5 +19,8 @@ suite "Test cgi module": # bug #15369 let queryString = "a=1&b=0&c=3&d&e&a=5&a=t%20e%20x%20t&e=http%3A%2F%2Fw3schools.com%2Fmy%20test.asp%3Fname%3Dst%C3%A5le%26car%3Dsaab" -for pair in decodeData(queryString): - echo pair +doAssert collect(for pair in decodeData(queryString): pair) == + @[("a", "1"), ("b", "0"), ("c", "3"), + ("d", ""),("e", ""), ("a", "5"), ("a", "t e x t"), + ("e", "http://w3schools.com/my test.asp?name=ståle&car=saab") +] From fbc8a40c7a351ff7c0f2dc0608bc8926f89d8537 Mon Sep 17 00:00:00 2001 From: cooldome Date: Sun, 27 Dec 2020 21:05:33 +0200 Subject: [PATCH 1221/3768] fix #15043 (#16441) [backport:1.4] * fix #15043 * Trigger build --- compiler/lambdalifting.nim | 12 +++++++----- tests/arc/trepr.nim | 16 ++++++++++++++++ 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index 95e50d00f..4f3823f8a 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -238,10 +238,11 @@ proc liftingHarmful(conf: ConfigRef; owner: PSym): bool {.inline.} = result = conf.backend == backendJs and not isCompileTime proc createTypeBoundOpsLL(g: ModuleGraph; refType: PType; info: TLineInfo; idgen: IdGenerator; owner: PSym) = - createTypeBoundOps(g, nil, refType.lastSon, info, idgen) - createTypeBoundOps(g, nil, refType, info, idgen) - if tfHasAsgn in refType.flags or optSeqDestructors in g.config.globalOptions: - owner.flags.incl sfInjectDestructors + if owner.kind != skMacro: + createTypeBoundOps(g, nil, refType.lastSon, info, idgen) + createTypeBoundOps(g, nil, refType, info, idgen) + if tfHasAsgn in refType.flags or optSeqDestructors in g.config.globalOptions: + owner.flags.incl sfInjectDestructors proc liftIterSym*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode = # transforms (iter) to (let env = newClosure[iter](); (iter, env)) @@ -613,7 +614,8 @@ proc rawClosureCreation(owner: PSym; let fieldAccess = indirectAccess(env, local, env.info) # add ``env.param = param`` result.add(newAsgnStmt(fieldAccess, newSymNode(local), env.info)) - createTypeBoundOps(d.graph, nil, fieldAccess.typ, env.info, d.idgen) + if owner.kind != skMacro: + createTypeBoundOps(d.graph, nil, fieldAccess.typ, env.info, d.idgen) if tfHasAsgn in fieldAccess.typ.flags or optSeqDestructors in d.graph.config.globalOptions: owner.flags.incl sfInjectDestructors diff --git a/tests/arc/trepr.nim b/tests/arc/trepr.nim index 3c1e4129c..50d433208 100644 --- a/tests/arc/trepr.nim +++ b/tests/arc/trepr.nim @@ -71,3 +71,19 @@ proc p2 = discard repr p2 + +##################################################################### +# bug #15043 + +import macros + +macro extract(): untyped = + result = newStmtList() + var x: seq[tuple[node: NimNode]] + + proc test(n: NimNode) {.closure.} = + x.add (node: n) + + test(parseExpr("discard")) + +extract() From f9a15dbae909f4521cd506bedf7ec500c4f4d9f8 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 28 Dec 2020 03:19:47 -0800 Subject: [PATCH 1222/3768] fix `nim secret` dots interfering with prompt (#16491) * fix nim secret dots * cleanups --- compiler/llstream.nim | 6 +++++- compiler/main.nim | 3 ++- compiler/msgs.nim | 4 ++-- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/compiler/llstream.nim b/compiler/llstream.nim index 6df927c60..b768e6c83 100644 --- a/compiler/llstream.nim +++ b/compiler/llstream.nim @@ -20,6 +20,7 @@ when hasRstdin: import rdstdin type TLLRepl* = proc (s: PLLStream, buf: pointer, bufLen: int): int + OnPrompt* = proc() {.closure.} TLLStreamKind* = enum # enum of different stream implementations llsNone, # null stream: reading and writing has no effect llsString, # stream encapsulates a string @@ -32,6 +33,7 @@ type rd*, wr*: int # for string streams lineOffset*: int # for fake stdin line numbers repl*: TLLRepl # gives stdin control to clients + onPrompt*: OnPrompt PLLStream* = ref TLLStream @@ -55,12 +57,13 @@ proc llStreamOpen*(): PLLStream = result.kind = llsNone proc llReadFromStdin(s: PLLStream, buf: pointer, bufLen: int): int -proc llStreamOpenStdIn*(r: TLLRepl = llReadFromStdin): PLLStream = +proc llStreamOpenStdIn*(r: TLLRepl = llReadFromStdin, onPrompt: OnPrompt = nil): PLLStream = new(result) result.kind = llsStdIn result.s = "" result.lineOffset = -1 result.repl = r + result.onPrompt = onPrompt proc llStreamClose*(s: PLLStream) = case s.kind @@ -133,6 +136,7 @@ proc llStreamRead*(s: PLLStream, buf: pointer, bufLen: int): int = of llsFile: result = readBuffer(s.f, buf, bufLen) of llsStdIn: + if s.onPrompt!=nil: s.onPrompt() result = s.repl(s, buf, bufLen) proc llStreamReadLine*(s: PLLStream, line: var string): bool = diff --git a/compiler/main.nim b/compiler/main.nim index 73676ffd5..74c19bf10 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -138,7 +138,8 @@ proc commandInteractive(graph: ModuleGraph) = var m = graph.makeStdinModule() incl(m.flags, sfMainModule) var idgen = IdGenerator(module: m.itemId.module, item: m.itemId.item) - processModule(graph, m, idgen, llStreamOpenStdIn()) + let s = llStreamOpenStdIn(onPrompt = proc() = flushDot(graph.config, stderr)) + processModule(graph, m, idgen, s) proc commandScan(cache: IdentCache, config: ConfigRef) = var f = addFileExt(AbsoluteFile mainCommandArg(config), NimExt) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index d027ce960..6d6e21204 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -19,9 +19,9 @@ template instLoc(): InstantiationInfo = instantiationInfo(-2, fullPaths = true) template toStdOrrKind(stdOrr): untyped = if stdOrr == stdout: stdOrrStdout else: stdOrrStderr -template flushDot(conf, stdOrr) = +template flushDot*(conf, stdOrr) = ## safe to call multiple times - let stdOrrKind = stdOrr.toStdOrrKind() + let stdOrrKind = toStdOrrKind(stdOrr) if stdOrrKind in conf.lastMsgWasDot: conf.lastMsgWasDot.excl stdOrrKind write(stdOrr, "\n") From 6d442a40a6f89572052d61aeb73ec26d1f3451ce Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 28 Dec 2020 07:13:21 -0600 Subject: [PATCH 1223/3768] use doAssert in tests (#16486) --- tests/arc/tarcmisc.nim | 4 +- tests/arc/tasyncawait.nim | 2 +- tests/arc/tasyncleak2.nim | 2 +- tests/assign/tassign.nim | 4 +- tests/async/t12221.nim | 4 +- tests/async/tasync_gcsafe.nim | 2 +- tests/async/tasync_gcunsafe.nim | 2 +- tests/async/tasyncawait.nim | 2 +- tests/async/tasynceagain.nim | 4 +- tests/async/tasyncnetudp.nim | 4 +- tests/async/tasyncssl.nim | 4 +- tests/ccgbugs/t9286.nim | 4 +- tests/ccgbugs/twrong_tupleconv.nim | 2 +- tests/collections/tseq.nim | 2 +- tests/collections/ttables.nim | 94 ++++----- tests/collections/ttablesthreads.nim | 110 +++++----- tests/compiler/tbrees.nim | 14 +- tests/destructor/tcomplexobjconstr.nim | 16 +- tests/fields/tfielditerator.nim | 12 +- tests/generics/tgenerics_issues.nim | 8 +- tests/generics/tgenerics_various.nim | 38 ++-- tests/generics/tparser_generator.nim | 16 +- tests/lexer/tintegerliterals.nim | 14 +- tests/m14634.nim | 6 +- tests/metatype/twildtypedesc.nim | 10 +- tests/misc/tunsignedcomp.nim | 192 ++++++++--------- tests/newconfig/tfoo.nims | 44 ++-- tests/niminaction/Chapter3/various3.nim | 26 +-- tests/objvariant/tconstructionorder.nim | 20 +- tests/osproc/tclose.nim | 8 +- tests/parser/ttypeclasses.nim | 50 ++--- tests/pragmas/tbitsize.nim | 10 +- tests/pragmas/tcustom_pragma.nim | 60 +++--- tests/sets/tsets_various.nim | 122 +++++------ tests/statictypes/tstatictypes.nim | 18 +- tests/stdlib/t14139.nim | 2 +- tests/stdlib/talgorithm.nim | 30 +-- tests/stdlib/tcritbits.nim | 32 +-- tests/stdlib/tdeques.nim | 50 ++--- tests/stdlib/teditdistance.nim | 16 +- tests/stdlib/tenumerate.nim | 6 +- tests/stdlib/thtmlparser.nim | 2 +- tests/stdlib/thttpcore.nim | 30 +-- tests/stdlib/tlists.nim | 34 +-- tests/stdlib/tmath.nim | 38 ++-- tests/stdlib/tmd5.nim | 6 +- tests/stdlib/tos.nim | 16 +- tests/stdlib/tparsecfg.nim | 4 +- tests/stdlib/tparsopt.nim | 2 +- tests/stdlib/tpegs.nim | 2 +- tests/stdlib/tpunycode.nim | 6 +- tests/stdlib/trationals.nim | 126 ++++++------ tests/stdlib/trst.nim | 10 +- tests/stdlib/trstgen.nim | 152 +++++++------- tests/stdlib/tsequtils.nim | 166 +++++++-------- tests/stdlib/tsharedtable.nim | 6 +- tests/stdlib/tstrformat.nim | 4 +- tests/stdlib/tstrtabs.nim | 10 +- tests/stdlib/tstrutils.nim | 262 ++++++++++++------------ tests/stdlib/tsugar.nim | 24 +-- tests/stdlib/tsums.nim | 14 +- tests/stdlib/ttables.nim | 36 ++-- tests/stdlib/ttypeinfo.nim | 18 +- tests/stdlib/tunittest.nim | 2 +- tests/stdlib/txmltree.nim | 18 +- tests/types/tisop.nim | 10 +- tests/vm/tableinstatic.nim | 2 +- tests/vm/tissues.nim | 2 +- tests/vm/toverflowopcaddimmint.nim | 2 +- tests/vm/toverflowopcaddint.nim | 2 +- tests/vm/toverflowopcmulint.nim | 2 +- tests/vm/toverflowopcsubimmint.nim | 2 +- tests/vm/toverflowopcsubint.nim | 2 +- tests/vm/tstringnil.nim | 2 +- tests/vm/tvarsection.nim | 4 +- tests/vm/tvmmisc.nim | 24 +-- tests/vm/twrong_concat.nim | 2 +- tests/vm/twrongarray.nim | 2 +- 78 files changed, 1056 insertions(+), 1056 deletions(-) diff --git a/tests/arc/tarcmisc.nim b/tests/arc/tarcmisc.nim index 8d857921e..55803085f 100644 --- a/tests/arc/tarcmisc.nim +++ b/tests/arc/tarcmisc.nim @@ -135,8 +135,8 @@ let n = @["c", "b"] q = @[("c", "2"), ("b", "1")] -assert n.sortedByIt(it) == @["b", "c"], "fine" -assert q.sortedByIt(it[0]) == @[("b", "1"), ("c", "2")], "fails under arc" +doAssert n.sortedByIt(it) == @["b", "c"], "fine" +doAssert q.sortedByIt(it[0]) == @[("b", "1"), ("c", "2")], "fails under arc" #------------------------------------------------------------------------------ diff --git a/tests/arc/tasyncawait.nim b/tests/arc/tasyncawait.nim index f29b8d2b2..75d9bc9b5 100644 --- a/tests/arc/tasyncawait.nim +++ b/tests/arc/tasyncawait.nim @@ -62,7 +62,7 @@ proc main = let mem = getOccupiedMem() main() -assert msgCount == swarmSize * messagesToSend +doAssert msgCount == swarmSize * messagesToSend echo "result: ", msgCount GC_fullCollect() echo "memory: ", formatSize(getOccupiedMem() - mem) diff --git a/tests/arc/tasyncleak2.nim b/tests/arc/tasyncleak2.nim index 4d8486b3b..a8d71f1ee 100644 --- a/tests/arc/tasyncleak2.nim +++ b/tests/arc/tasyncleak2.nim @@ -84,5 +84,5 @@ proc main(): Future[void] = for i in 0..9: waitFor main() GC_fullCollect() - assert getOccupiedMem() < 1024 + doAssert getOccupiedMem() < 1024 echo "success" diff --git a/tests/assign/tassign.nim b/tests/assign/tassign.nim index 0589b0214..c95114015 100644 --- a/tests/assign/tassign.nim +++ b/tests/assign/tassign.nim @@ -93,9 +93,9 @@ block tgenericassign: var ret: seq[tuple[name: string, a: TAny]] = @[] for i in 0 .. 8000: var tup = ($name, newAny(nil, nil)) - assert(tup[0] == "example") + doAssert(tup[0] == "example") ret.add(tup) - assert(ret[ret.len()-1][0] == "example") + doAssert(ret[ret.len()-1][0] == "example") diff --git a/tests/async/t12221.nim b/tests/async/t12221.nim index 70e192356..e8bd9c11a 100644 --- a/tests/async/t12221.nim +++ b/tests/async/t12221.nim @@ -5,9 +5,9 @@ proc doubleSleep(hardSleep: int) {.async.} = sleep(hardSleep) template assertTime(target, timeTook: float): untyped {.dirty.} = - assert(timeTook*1000 > target - 1000, "Took too short, should've taken " & + doAssert(timeTook*1000 > target - 1000, "Took too short, should've taken " & $target & "ms, but took " & $(timeTook*1000) & "ms") - assert(timeTook*1000 < target + 1000, "Took too long, should've taken " & + doAssert(timeTook*1000 < target + 1000, "Took too long, should've taken " & $target & "ms, but took " & $(timeTook*1000) & "ms") var diff --git a/tests/async/tasync_gcsafe.nim b/tests/async/tasync_gcsafe.nim index 89df6456a..bc0eb4271 100644 --- a/tests/async/tasync_gcsafe.nim +++ b/tests/async/tasync_gcsafe.nim @@ -7,7 +7,7 @@ discard """ ''' """ -assert compileOption("threads"), "this test will not do anything useful without --threads:on" +doAssert compileOption("threads"), "this test will not do anything useful without --threads:on" import asyncdispatch diff --git a/tests/async/tasync_gcunsafe.nim b/tests/async/tasync_gcunsafe.nim index 55b66aaef..00c92b109 100644 --- a/tests/async/tasync_gcunsafe.nim +++ b/tests/async/tasync_gcunsafe.nim @@ -4,7 +4,7 @@ discard """ file: "asyncmacro.nim" """ -assert compileOption("threads"), "this test will not do anything useful without --threads:on" +doAssert compileOption("threads"), "this test will not do anything useful without --threads:on" import asyncdispatch diff --git a/tests/async/tasyncawait.nim b/tests/async/tasyncawait.nim index aec4ce523..f658a15ed 100644 --- a/tests/async/tasyncawait.nim +++ b/tests/async/tasyncawait.nim @@ -52,5 +52,5 @@ while true: poll() if clientCount == swarmSize: break -assert msgCount == swarmSize * messagesToSend +doAssert msgCount == swarmSize * messagesToSend doAssert msgCount == 2000 diff --git a/tests/async/tasynceagain.nim b/tests/async/tasynceagain.nim index aebd4ef16..94c3645dc 100644 --- a/tests/async/tasynceagain.nim +++ b/tests/async/tasynceagain.nim @@ -21,12 +21,12 @@ proc runServer() {.async.} = var lastN = 0 while true: let frame = await client.recv(FrameSize) - assert frame.len == FrameSize + doAssert frame.len == FrameSize let n = frame[0..<6].parseInt() echo "RCVD #", n, ": ", frame[0..80], "..." if n != lastN + 1: echo &"******** ERROR: Server received #{n}, but last was #{lastN}!" - assert n == lastN + 1 + doAssert n == lastN + 1 lastN = n await sleepAsync 100 diff --git a/tests/async/tasyncnetudp.nim b/tests/async/tasyncnetudp.nim index 3494def37..ef6dfc5e1 100644 --- a/tests/async/tasyncnetudp.nim +++ b/tests/async/tasyncnetudp.nim @@ -84,7 +84,7 @@ while true: if recvCount == swarmSize * messagesToSend: break -assert msgCount == swarmSize * messagesToSend -assert sendports == recvports +doAssert msgCount == swarmSize * messagesToSend +doAssert sendports == recvports echo msgCount \ No newline at end of file diff --git a/tests/async/tasyncssl.nim b/tests/async/tasyncssl.nim index c948ee9b7..a582818eb 100644 --- a/tests/async/tasyncssl.nim +++ b/tests/async/tasyncssl.nim @@ -69,5 +69,5 @@ when defined(ssl): elif defined(linux) and int.sizeof == 8: # currently: msgCount == 10 flakyAssert cond() - assert msgCount > 0 - else: assert cond(), $msgCount + doAssert msgCount > 0 + else: doAssert cond(), $msgCount diff --git a/tests/ccgbugs/t9286.nim b/tests/ccgbugs/t9286.nim index 8a45a7bf6..2fec23307 100644 --- a/tests/ccgbugs/t9286.nim +++ b/tests/ccgbugs/t9286.nim @@ -7,7 +7,7 @@ type Foo = ref object i: int proc next(foo: Foo): Option[Foo] = - try: assert(foo.i == 0) + try: doAssert(foo.i == 0) except: return # 2º: none return some(foo) # 1º: some @@ -17,6 +17,6 @@ proc test = while isSome(opt) and foo.i < 10: inc(foo.i) opt = next(foo) # 2º None - assert foo.i == 1, $foo.i + doAssert foo.i == 1, $foo.i test() diff --git a/tests/ccgbugs/twrong_tupleconv.nim b/tests/ccgbugs/twrong_tupleconv.nim index 7b1e58083..7a887d183 100644 --- a/tests/ccgbugs/twrong_tupleconv.nim +++ b/tests/ccgbugs/twrong_tupleconv.nim @@ -6,7 +6,7 @@ iterator myitems*[T](a: var seq[T]): var T {.inline.} = while i < L: yield a[i] inc(i) - assert(len(a) == L, "the length of the seq changed while iterating over it") + doAssert(len(a) == L, "the length of the seq changed while iterating over it") # Works fine var xs = @[1,2,3] diff --git a/tests/collections/tseq.nim b/tests/collections/tseq.nim index 263a571bf..88d6dc79b 100644 --- a/tests/collections/tseq.nim +++ b/tests/collections/tseq.nim @@ -24,7 +24,7 @@ block tseq2: # multiply two int sequences: for i in 0..len(a)-1: result[i] = a[i] * b[i] - assert(@[1, 2, 3] * @[1, 2, 3] == @[1, 4, 9]) + doAssert(@[1, 2, 3] * @[1, 2, 3] == @[1, 4, 9]) diff --git a/tests/collections/ttables.nim b/tests/collections/ttables.nim index 338a83fed..61197e9f0 100644 --- a/tests/collections/ttables.nim +++ b/tests/collections/ttables.nim @@ -50,20 +50,20 @@ block thashes: var t = initTable[int,int]() t[0] = 42 t[1] = t[0] + 1 - assert(t[0] == 42) - assert(t[1] == 43) + doAssert(t[0] == 42) + doAssert(t[1] == 43) let t2 = {1: 1, 2: 2}.toTable - assert(t2[2] == 2) + doAssert(t2[2] == 2) # Test with char block: var t = initTable[char,int]() t['0'] = 42 t['1'] = t['0'] + 1 - assert(t['0'] == 42) - assert(t['1'] == 43) + doAssert(t['0'] == 42) + doAssert(t['1'] == 43) let t2 = {'1': 1, '2': 2}.toTable - assert(t2['2'] == 2) + doAssert(t2['2'] == 2) # Test with enum block: @@ -72,10 +72,10 @@ block thashes: var t = initTable[E,int]() t[eA] = 42 t[eB] = t[eA] + 1 - assert(t[eA] == 42) - assert(t[eB] == 43) + doAssert(t[eA] == 42) + doAssert(t[eB] == 43) let t2 = {eA: 1, eB: 2}.toTable - assert(t2[eB] == 2) + doAssert(t2[eB] == 2) # Test with range block: @@ -84,10 +84,10 @@ block thashes: var t = initTable[R,int]() # causes warning, why? t[1] = 42 # causes warning, why? t[2] = t[1] + 1 - assert(t[1] == 42) - assert(t[2] == 43) + doAssert(t[1] == 42) + doAssert(t[2] == 43) let t2 = {1.R: 1, 2.R: 2}.toTable - assert(t2[2.R] == 2) + doAssert(t2[2.R] == 2) # Test which combines the generics for tuples + ordinals block: @@ -96,10 +96,10 @@ block thashes: var t = initTable[(string, E, int, char), int]() t[("a", eA, 0, '0')] = 42 t[("b", eB, 1, '1')] = t[("a", eA, 0, '0')] + 1 - assert(t[("a", eA, 0, '0')] == 42) - assert(t[("b", eB, 1, '1')] == 43) + doAssert(t[("a", eA, 0, '0')] == 42) + doAssert(t[("b", eB, 1, '1')] == 43) let t2 = {("a", eA, 0, '0'): 1, ("b", eB, 1, '1'): 2}.toTable - assert(t2[("b", eB, 1, '1')] == 2) + doAssert(t2[("b", eB, 1, '1')] == 2) # Test to check if overloading is possible # Unfortunately, this does not seem to work for int @@ -165,9 +165,9 @@ block tableconstr: ignoreExpr({2: 3, "key": "value"}) # NEW: - assert 56 in 50..100 + doAssert 56 in 50..100 - assert 56 in ..60 + doAssert 56 in ..60 block ttables2: @@ -239,8 +239,8 @@ block tablesref: t[(1,1)] = "11" for x in 0..1: for y in 0..1: - assert t[(x,y)] == $x & $y - assert t.sortedPairs == + doAssert t[(x,y)] == $x & $y + doAssert t.sortedPairs == @[((x: 0, y: 0), "00"), ((x: 0, y: 1), "01"), ((x: 1, y: 0), "10"), ((x: 1, y: 1), "11")] block tableTest2: @@ -253,31 +253,31 @@ block tablesref: t["012"] = 67.9 t["123"] = 1.5 # test overwriting - assert t["123"] == 1.5 + doAssert t["123"] == 1.5 try: echo t["111"] # deleted except KeyError: discard - assert(not hasKey(t, "111")) - assert "111" notin t + doAssert(not hasKey(t, "111")) + doAssert "111" notin t for key, val in items(data): t[key] = val.toFloat - for key, val in items(data): assert t[key] == val.toFloat + for key, val in items(data): doAssert t[key] == val.toFloat block orderedTableTest1: var t = newOrderedTable[string, int](2) for key, val in items(data): t[key] = val - for key, val in items(data): assert t[key] == val + for key, val in items(data): doAssert t[key] == val var i = 0 # `pairs` needs to yield in insertion order: for key, val in pairs(t): - assert key == data[i][0] - assert val == data[i][1] + doAssert key == data[i][0] + doAssert val == data[i][1] inc(i) for key, val in mpairs(t): val = 99 - for val in mvalues(t): assert val == 99 + for val in mvalues(t): doAssert val == 99 block countTableTest1: var s = data.toTable @@ -286,11 +286,11 @@ block tablesref: for x in [t, r]: for k in s.keys: x.inc(k) - assert x[k] == 1 + doAssert x[k] == 1 x.inc("90", 3) x.inc("12", 2) x.inc("34", 1) - assert t.largest()[0] == "90" + doAssert t.largest()[0] == "90" t.sort() r.sort(SortOrder.Ascending) @@ -301,9 +301,9 @@ block tablesref: var i = 0 for (k, v) in ps: case i - of 0: assert k == "90" and v == 4 - of 1: assert k == "12" and v == 3 - of 2: assert k == "34" and v == 2 + of 0: doAssert k == "90" and v == 4 + of 1: doAssert k == "12" and v == 3 + of 2: doAssert k == "34" and v == 2 else: break inc i @@ -327,17 +327,17 @@ block tablesref: block nilTest: var i, j: TableRef[int, int] = nil - assert i == j + doAssert i == j j = newTable[int, int]() - assert i != j - assert j != i + doAssert i != j + doAssert j != i i = newTable[int, int]() - assert i == j + doAssert i == j proc orderedTableSortTest() = var t = newOrderedTable[string, int](2) for key, val in items(data): t[key] = val - for key, val in items(data): assert t[key] == val + for key, val in items(data): doAssert t[key] == val proc cmper(x, y: tuple[key: string, val: int]): int = cmp(x.key, y.key) t.sort(cmper) var i = 0 @@ -369,25 +369,25 @@ block tablesref: t["test"] = 1.2345 t["111"] = 1.000043 t["123"] = 1.23 - assert t.len() != 0 + doAssert t.len() != 0 t.clear() - assert t.len() == 0 + doAssert t.len() == 0 block clearOrderedTableTest: var t = newOrderedTable[string, int](2) for key, val in items(data): t[key] = val - assert t.len() != 0 + doAssert t.len() != 0 t.clear() - assert t.len() == 0 + doAssert t.len() == 0 block clearCountTableTest: var t = newCountTable[string]() t.inc("90", 3) t.inc("12", 2) t.inc("34", 1) - assert t.len() != 0 + doAssert t.len() != 0 t.clear() - assert t.len() == 0 + doAssert t.len() == 0 orderedTableSortTest() echo "3" @@ -415,7 +415,7 @@ block: # https://github.com/nim-lang/Nim/issues/13496 doAssert sortedPairs(t) == @[(15, 1), (17, 3), (19, 2)] var s = newSeq[int]() for v in t.values: s.add(v) - assert s.len == 3 + doAssert s.len == 3 doAssert sortedItems(s) == @[1, 2, 3] when t is OrderedTable|OrderedTableRef: doAssert toSeq(t.keys) == @[15, 19, 17] @@ -433,14 +433,14 @@ block: # https://github.com/nim-lang/Nim/issues/13496 block testNonPowerOf2: var a = initTable[int, int](7) a[1] = 10 - assert a[1] == 10 + doAssert a[1] == 10 var b = initTable[int, int](9) b[1] = 10 - assert b[1] == 10 + doAssert b[1] == 10 block emptyOrdered: var t1: OrderedTable[int, string] var t2: OrderedTable[int, string] - assert t1 == t2 + doAssert t1 == t2 diff --git a/tests/collections/ttablesthreads.nim b/tests/collections/ttablesthreads.nim index 9f7d77719..2a4e1bf42 100644 --- a/tests/collections/ttablesthreads.nim +++ b/tests/collections/ttablesthreads.nim @@ -48,8 +48,8 @@ block tableTest1: t[(1,1)] = "11" for x in 0..1: for y in 0..1: - assert t[(x,y)] == $x & $y - assert t.sortedPairs == @[((x: 0, y: 0), "00"), ((x: 0, y: 1), "01"), ((x: 1, y: 0), "10"), ((x: 1, y: 1), "11")] + doAssert t[(x,y)] == $x & $y + doAssert t.sortedPairs == @[((x: 0, y: 0), "00"), ((x: 0, y: 1), "01"), ((x: 1, y: 0), "10"), ((x: 1, y: 1), "11")] block tableTest2: var t = initTable[string, float]() @@ -61,74 +61,74 @@ block tableTest2: t["012"] = 67.9 t["123"] = 1.5 # test overwriting - assert t["123"] == 1.5 + doAssert t["123"] == 1.5 try: echo t["111"] # deleted except KeyError: discard - assert(not hasKey(t, "111")) + doAssert(not hasKey(t, "111")) - assert "123" in t - assert("111" notin t) + doAssert "123" in t + doAssert("111" notin t) for key, val in items(data): t[key] = val.toFloat - for key, val in items(data): assert t[key] == val.toFloat + for key, val in items(data): doAssert t[key] == val.toFloat - assert(not t.hasKeyOrPut("456", 4.0)) # test absent key - assert t.hasKeyOrPut("012", 3.0) # test present key + doAssert(not t.hasKeyOrPut("456", 4.0)) # test absent key + doAssert t.hasKeyOrPut("012", 3.0) # test present key var x = t.mgetOrPut("111", 1.5) # test absent key x = x * 2 - assert x == 3.0 + doAssert x == 3.0 x = t.mgetOrPut("test", 1.5) # test present key x = x * 2 - assert x == 2 * 1.2345 + doAssert x == 2 * 1.2345 block orderedTableTest1: var t = initOrderedTable[string, int](2) for key, val in items(data): t[key] = val - for key, val in items(data): assert t[key] == val + for key, val in items(data): doAssert t[key] == val var i = 0 # `pairs` needs to yield in insertion order: for key, val in pairs(t): - assert key == data[i][0] - assert val == data[i][1] + doAssert key == data[i][0] + doAssert val == data[i][1] inc(i) for key, val in mpairs(t): val = 99 - for val in mvalues(t): assert val == 99 + for val in mvalues(t): doAssert val == 99 block orderedTableTest2: var s = initOrderedTable[string, int]() t = initOrderedTable[string, int]() - assert s == t + doAssert s == t for key, val in items(data): t[key] = val - assert s != t + doAssert s != t for key, val in items(sorteddata): s[key] = val - assert s != t + doAssert s != t t.clear() - assert s != t + doAssert s != t for key, val in items(sorteddata): t[key] = val - assert s == t + doAssert s == t block countTableTest1: var s = data.toTable var t = initCountTable[string]() for k in s.keys: t.inc(k) - for k in t.keys: assert t[k] == 1 + for k in t.keys: doAssert t[k] == 1 t.inc("90", 3) t.inc("12", 2) t.inc("34", 1) - assert t.largest()[0] == "90" + doAssert t.largest()[0] == "90" t.sort() var i = 0 for k, v in t.pairs: case i - of 0: assert k == "90" and v == 4 - of 1: assert k == "12" and v == 3 - of 2: assert k == "34" and v == 2 + of 0: doAssert k == "90" and v == 4 + of 1: doAssert k == "12" and v == 3 + of 2: doAssert k == "34" and v == 2 else: break inc i @@ -136,19 +136,19 @@ block countTableTest2: var s = initCountTable[int]() t = initCountTable[int]() - assert s == t + doAssert s == t s.inc(1) - assert s != t + doAssert s != t t.inc(2) - assert s != t + doAssert s != t t.inc(1) - assert s != t + doAssert s != t s.inc(2) - assert s == t + doAssert s == t s.inc(1) - assert s != t + doAssert s != t t.inc(1) - assert s == t + doAssert s == t block mpairsTableTest1: var t = initTable[string, int]() @@ -162,9 +162,9 @@ block mpairsTableTest1: for k, v in t.pairs: if k == "a" or k == "c": - assert v == 9 + doAssert v == 9 else: - assert v != 1 and v != 3 + doAssert v != 1 and v != 3 block SyntaxTest: var x = toTable[int, string]({:}) @@ -174,10 +174,10 @@ block zeroHashKeysTest: let initialLen = t.len var testTable = t testTable[nullHashKey] = value - assert testTable[nullHashKey] == value - assert testTable.len == initialLen + 1 + doAssert testTable[nullHashKey] == value + doAssert testTable.len == initialLen + 1 testTable.del(nullHashKey) - assert testTable.len == initialLen + doAssert testTable.len == initialLen # with empty table doZeroHashValueTest(toTable[int,int]({:}), 0, 42) @@ -194,46 +194,46 @@ block zeroHashKeysTest: block clearTableTest: var t = data.toTable - assert t.len() != 0 + doAssert t.len() != 0 t.clear() - assert t.len() == 0 + doAssert t.len() == 0 block clearOrderedTableTest: var t = data.toOrderedTable - assert t.len() != 0 + doAssert t.len() != 0 t.clear() - assert t.len() == 0 + doAssert t.len() == 0 block clearCountTableTest: var t = initCountTable[string]() t.inc("90", 3) t.inc("12", 2) t.inc("34", 1) - assert t.len() != 0 + doAssert t.len() != 0 t.clear() - assert t.len() == 0 + doAssert t.len() == 0 block withKeyTest: var t: SharedTable[int, int] t.init() t.withKey(1) do (k: int, v: var int, pairExists: var bool): - assert(v == 0) + doAssert(v == 0) pairExists = true v = 42 - assert(t.mget(1) == 42) + doAssert(t.mget(1) == 42) t.withKey(1) do (k: int, v: var int, pairExists: var bool): - assert(v == 42) + doAssert(v == 42) pairExists = false try: discard t.mget(1) - assert(false, "KeyError expected") + doAssert(false, "KeyError expected") except KeyError: discard t.withKey(2) do (k: int, v: var int, pairExists: var bool): pairExists = false try: discard t.mget(2) - assert(false, "KeyError expected") + doAssert(false, "KeyError expected") except KeyError: discard @@ -242,20 +242,20 @@ block takeTest: t["key"] = 123 var val = 0 - assert(t.take("key", val)) - assert(val == 123) + doAssert(t.take("key", val)) + doAssert(val == 123) val = -1 - assert(not t.take("key", val)) - assert(val == -1) + doAssert(not t.take("key", val)) + doAssert(val == -1) - assert(not t.take("otherkey", val)) - assert(val == -1) + doAssert(not t.take("otherkey", val)) + doAssert(val == -1) proc orderedTableSortTest() = var t = initOrderedTable[string, int](2) for key, val in items(data): t[key] = val - for key, val in items(data): assert t[key] == val + for key, val in items(data): doAssert t[key] == val t.sort(proc (x, y: tuple[key: string, val: int]): int = cmp(x.key, y.key)) var i = 0 # `pairs` needs to yield in sorted order: diff --git a/tests/compiler/tbrees.nim b/tests/compiler/tbrees.nim index 364b51b22..5f6482ed9 100644 --- a/tests/compiler/tbrees.nim +++ b/tests/compiler/tbrees.nim @@ -42,14 +42,14 @@ proc main = st.add("www.weather.com", "63.111.66.11") st.add("www.yahoo.com", "216.109.118.65") - assert st.getOrDefault("www.cs.princeton.edu") == "abc" - assert st.getOrDefault("www.harvardsucks.com") == "" + doAssert st.getOrDefault("www.cs.princeton.edu") == "abc" + doAssert st.getOrDefault("www.harvardsucks.com") == "" - assert st.getOrDefault("www.simpsons.com") == "209.052.165.60" - assert st.getOrDefault("www.apple.com") == "17.112.152.32" - assert st.getOrDefault("www.ebay.com") == "66.135.192.87" - assert st.getOrDefault("www.dell.com") == "143.166.224.230" - assert(st.len == 16) + doAssert st.getOrDefault("www.simpsons.com") == "209.052.165.60" + doAssert st.getOrDefault("www.apple.com") == "17.112.152.32" + doAssert st.getOrDefault("www.ebay.com") == "66.135.192.87" + doAssert st.getOrDefault("www.dell.com") == "143.166.224.230" + doAssert(st.len == 16) for k, v in st: echo k, ": ", v diff --git a/tests/destructor/tcomplexobjconstr.nim b/tests/destructor/tcomplexobjconstr.nim index fd112b6e2..aea0ad1fe 100644 --- a/tests/destructor/tcomplexobjconstr.nim +++ b/tests/destructor/tcomplexobjconstr.nim @@ -20,16 +20,16 @@ type of true: y*: float var x = new(MyObject2) -assert x of MyObject2 -assert x.subobj of MyObject1 -assert x.more[2] of MyObject1 -assert x.more[2] of RootObj +doAssert x of MyObject2 +doAssert x.subobj of MyObject1 +doAssert x.more[2] of MyObject1 +doAssert x.more[2] of RootObj var y: MyObject2 -assert y of MyObject2 -assert y.subobj of MyObject1 -assert y.more[2] of MyObject1 -assert y.more[2] of RootObj +doAssert y of MyObject2 +doAssert y.subobj of MyObject1 +doAssert y.more[2] of MyObject1 +doAssert y.more[2] of RootObj echo "true" diff --git a/tests/fields/tfielditerator.nim b/tests/fields/tfielditerator.nim index 877e4454c..d1fbf02f9 100644 --- a/tests/fields/tfielditerator.nim +++ b/tests/fields/tfielditerator.nim @@ -60,12 +60,12 @@ block titerator1: for key, val in fieldPairs(x): echo key, ": ", val - assert x != y - assert x == x - assert(not (x < x)) - assert x <= x - assert y < x - assert y <= x + doAssert x != y + doAssert x == x + doAssert(not (x < x)) + doAssert x <= x + doAssert y < x + doAssert y <= x block titerator2: diff --git a/tests/generics/tgenerics_issues.nim b/tests/generics/tgenerics_issues.nim index 812f339b9..365afd407 100644 --- a/tests/generics/tgenerics_issues.nim +++ b/tests/generics/tgenerics_issues.nim @@ -55,8 +55,8 @@ block t88: let c = ChildClass[string].new("Base", "Child") - assert c.baseMethod == "Base" - assert c.overriddenMethod == "Child" + doAssert c.baseMethod == "Base" + doAssert c.overriddenMethod == "Child" @@ -128,7 +128,7 @@ block t1789: bar: array[N, T] proc `[]`[N, T](f: Bar[N, T], n: range[0..(N - 1)]): T = - assert high(n) == N-1 + doAssert high(n) == N-1 result = f.bar[n] var b: Bar[3, int] @@ -734,7 +734,7 @@ block t1684: proc newDerived(idx: int): DerivedType {.inline.} = DerivedType(idx: idx) let d = newDerived(2) - assert(d.index == 2) + doAssert(d.index == 2) diff --git a/tests/generics/tgenerics_various.nim b/tests/generics/tgenerics_various.nim index 22d3cff7a..285108cd3 100644 --- a/tests/generics/tgenerics_various.nim +++ b/tests/generics/tgenerics_various.nim @@ -58,23 +58,23 @@ block tgenericdefaults: var x1: TFoo[int, float] static: - assert type(x1.x) is int - assert type(x1.y) is float - assert type(x1.z) is int + doAssert type(x1.x) is int + doAssert type(x1.y) is float + doAssert type(x1.z) is int var x2: TFoo[string, R = float, U = seq[int]] static: - assert type(x2.x) is string - assert type(x2.y) is seq[int] - assert type(x2.z) is float + doAssert type(x2.x) is string + doAssert type(x2.y) is seq[int] + doAssert type(x2.z) is float var x3: TBar[float] static: - assert type(x3.x) is float - assert type(x3.y) is array[4, float] - assert type(x3.z) is float + doAssert type(x3.x) is float + doAssert type(x3.y) is array[4, float] + doAssert type(x3.z) is float @@ -150,31 +150,31 @@ block tsharedcases: doAssert high(f2.data2) == 3 # int8.len - 1 == 3 static: - assert high(f1.data1) == ord(C) - assert high(f1.data2) == 5 # length of MyEnum minus one, because we used T.high + doAssert high(f1.data1) == ord(C) + doAssert high(f1.data2) == 5 # length of MyEnum minus one, because we used T.high - assert high(f2.data1) == 126 - assert high(f2.data2) == 3 + doAssert high(f2.data1) == 126 + doAssert high(f2.data2) == 3 - assert high(f1.data3) == 6 # length of MyEnum - assert high(f2.data3) == 4 # length of int8 + doAssert high(f1.data3) == 6 # length of MyEnum + doAssert high(f2.data3) == 4 # length of int8 - assert f2.data3[0] is float + doAssert f2.data3[0] is float block tmap_auto: let x = map(@[1, 2, 3], x => x+10) - assert x == @[11, 12, 13] + doAssert x == @[11, 12, 13] let y = map(@[(1,"a"), (2,"b"), (3,"c")], x => $x[0] & x[1]) - assert y == @["1a", "2b", "3c"] + doAssert y == @["1a", "2b", "3c"] proc eatsTwoArgProc[T,S,U](a: T, b: S, f: proc(t: T, s: S): U): U = f(a,b) let z = eatsTwoArgProc(1, "a", (t,s) => $t & s) - assert z == "1a" + doAssert z == "1a" diff --git a/tests/generics/tparser_generator.nim b/tests/generics/tparser_generator.nim index 8f8fea382..ac921c0e5 100644 --- a/tests/generics/tparser_generator.nim +++ b/tests/generics/tparser_generator.nim @@ -147,7 +147,7 @@ proc literal*[N, T, P](pattern: P, kind: N): Rule[N, T] = let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int = if start == len(text): return -1 - assert(len(text)>start, "Attempting to match at $#, string length is $# " % [$start, $len(text)]) + doAssert(len(text)>start, "Attempting to match at $#, string length is $# " % [$start, $len(text)]) when P is string or P is seq[N]: debug(debugLex, "Literal[" & $kind & "]: testing " & $pattern & " at " & $start & ": " & $text[start..start+len(pattern)-1]) if text.continuesWith(pattern, start): @@ -177,7 +177,7 @@ proc token[N, T](pattern: T, kind: N): Rule[N, T] = debug(debugLex, "Token[" & $kind & "]: testing " & pattern & " at " & $start) if start == len(text): return -1 - assert(len(text)>start, "Attempting to match at $#, string length is $# " % [$start, $len(text)]) + doAssert(len(text)>start, "Attempting to match at $#, string length is $# " % [$start, $len(text)]) let m = text.match(re(pattern), start) if m.isSome: let node = initNode(start, len(m.get.match), kind) @@ -192,7 +192,7 @@ proc chartest[N, T, S](testfunc: proc(s: S): bool, kind: N): Rule[N, T] = let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int = if start == len(text): return -1 - assert(len(text)>start, "Attempting to match at $#, string length is $# " % [$start, $len(text)]) + doAssert(len(text)>start, "Attempting to match at $#, string length is $# " % [$start, $len(text)]) if testfunc(text[start]): nodes.add(initNode(start, 1, kind)) result = 1 @@ -252,11 +252,11 @@ proc fail*[N, T](message: string, kind: N): Rule[N, T] = proc `+`*[N, T](left: Rule[N, T], right: Rule[N, T]): Rule[N, T] = let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int = var mynodes = newSeq[Node[N]]() - assert(not isNil(left.parser), "Left hand side parser is nil") + doAssert(not isNil(left.parser), "Left hand side parser is nil") let leftlength = left.parser(text, start, mynodes) if leftlength == -1: return leftlength - assert(not isNil(right.parser), "Right hand side parser is nil") + doAssert(not isNil(right.parser), "Right hand side parser is nil") let rightlength = right.parser(text, start+leftlength, mynodes) if rightlength == -1: return rightlength @@ -267,13 +267,13 @@ proc `+`*[N, T](left: Rule[N, T], right: Rule[N, T]): Rule[N, T] = proc `/`*[N, T](left: Rule[N, T], right: Rule[N, T]): Rule[N, T] = let parser = proc (text: T, start: int, nodes: var seq[Node[N]]): int = var mynodes = newSeq[Node[N]]() - assert(not isNil(left.parser), "Left hand side of / is not fully defined") + doAssert(not isNil(left.parser), "Left hand side of / is not fully defined") let leftlength = left.parser(text, start, mynodes) if leftlength != -1: nodes.add(mynodes) return leftlength mynodes = newSeq[Node[N]]() - assert(not isNil(right.parser), "Right hand side of / is not fully defined") + doAssert(not isNil(right.parser), "Right hand side of / is not fully defined") let rightlength = right.parser(text, start, mynodes) if rightlength == -1: return rightlength @@ -360,7 +360,7 @@ proc `/`*[N, T](rule: Rule[N, T]): Rule[N, T] = result = newRule[N, T](parser, rule.kind) proc `->`*(rule: Rule, production: Rule) = - assert(not isnil(production.parser), "Right hand side of -> is nil - has the rule been defined yet?") + doAssert(not isnil(production.parser), "Right hand side of -> is nil - has the rule been defined yet?") rule.parser = production.parser template grammar*[K](Kind, Text, Symbol: typedesc; default: K, code: untyped): typed {.hint[XDeclaredButNotUsed]: off.} = diff --git a/tests/lexer/tintegerliterals.nim b/tests/lexer/tintegerliterals.nim index 7420db144..fd401b71b 100644 --- a/tests/lexer/tintegerliterals.nim +++ b/tests/lexer/tintegerliterals.nim @@ -1,9 +1,9 @@ # test the valid literals -assert 0b10 == 2 -assert 0B10 == 2 -assert 0x10 == 16 -assert 0X10 == 16 -assert 0o10 == 8 +doAssert 0b10 == 2 +doAssert 0B10 == 2 +doAssert 0x10 == 16 +doAssert 0X10 == 16 +doAssert 0o10 == 8 # the following is deprecated: -assert 0c10 == 8 -assert 0C10 == 8 +doAssert 0c10 == 8 +doAssert 0C10 == 8 diff --git a/tests/m14634.nim b/tests/m14634.nim index 56a3d9034..f19f02f0c 100644 --- a/tests/m14634.nim +++ b/tests/m14634.nim @@ -20,7 +20,7 @@ when not defined(windows): # the test fails. var rc1 = selector.select(t) var rc2 = selector.select(t) - assert len(rc1) <= 1 and len(rc2) <= 1 + doAssert len(rc1) <= 1 and len(rc2) <= 1 data.s1 += ord(len(rc1) == 1) data.s2 += ord(len(rc2) == 1) selector.unregister(timer) @@ -32,9 +32,9 @@ when not defined(windows): # this can't be too large as it'll actually wait that long: # timer_notification_test.n * t2 var rc5 = selector.select(t2) - assert len(rc4) + len(rc5) <= 1 + doAssert len(rc4) + len(rc5) <= 1 data.s3 += ord(len(rc4) + len(rc5) == 1) - assert(selector.isEmpty()) + doAssert(selector.isEmpty()) selector.close() proc timerNotificationTest() = diff --git a/tests/metatype/twildtypedesc.nim b/tests/metatype/twildtypedesc.nim index 268bff0d8..d1c5ffba5 100644 --- a/tests/metatype/twildtypedesc.nim +++ b/tests/metatype/twildtypedesc.nim @@ -17,8 +17,8 @@ proc unpack[T](v: string): T = var s = "123" -assert(unpack[string](s) is string) -assert(unpack[int](s) is int) +doAssert(unpack[string](s) is string) +doAssert(unpack[int](s) is int) echo unpack[int](s) echo unpack[string](s) @@ -37,7 +37,7 @@ proc unit(t: typedesc[int]): t = 0 proc unit(t: typedesc[string]): t = "" proc unit(t: typedesc[float]): t = 0.0 -assert unit(int) == 0 -assert unit(string) == "" -assert unit(float) == 0.0 +doAssert unit(int) == 0 +doAssert unit(string) == "" +doAssert unit(float) == 0.0 diff --git a/tests/misc/tunsignedcomp.nim b/tests/misc/tunsignedcomp.nim index 19c8876b1..970c4ae9d 100644 --- a/tests/misc/tunsignedcomp.nim +++ b/tests/misc/tunsignedcomp.nim @@ -10,127 +10,127 @@ discard """ # unsigned < signed -assert 10'u8 < 20'i8 -assert 10'u8 < 20'i16 -assert 10'u8 < 20'i32 -assert 10'u8 < 20'i64 +doAssert 10'u8 < 20'i8 +doAssert 10'u8 < 20'i16 +doAssert 10'u8 < 20'i32 +doAssert 10'u8 < 20'i64 -assert 10'u16 < 20'i8 -assert 10'u16 < 20'i16 -assert 10'u16 < 20'i32 -assert 10'u16 < 20'i64 +doAssert 10'u16 < 20'i8 +doAssert 10'u16 < 20'i16 +doAssert 10'u16 < 20'i32 +doAssert 10'u16 < 20'i64 -assert 10'u32 < 20'i8 -assert 10'u32 < 20'i16 -assert 10'u32 < 20'i32 -assert 10'u32 < 20'i64 +doAssert 10'u32 < 20'i8 +doAssert 10'u32 < 20'i16 +doAssert 10'u32 < 20'i32 +doAssert 10'u32 < 20'i64 -# assert 10'u64 < 20'i8 -# assert 10'u64 < 20'i16 -# assert 10'u64 < 20'i32 -# assert 10'u64 < 20'i64 +# doAssert 10'u64 < 20'i8 +# doAssert 10'u64 < 20'i16 +# doAssert 10'u64 < 20'i32 +# doAssert 10'u64 < 20'i64 # signed < unsigned -assert 10'i8 < 20'u8 -assert 10'i8 < 20'u16 -assert 10'i8 < 20'u32 -# assert 10'i8 < 20'u64 +doAssert 10'i8 < 20'u8 +doAssert 10'i8 < 20'u16 +doAssert 10'i8 < 20'u32 +# doAssert 10'i8 < 20'u64 -assert 10'i16 < 20'u8 -assert 10'i16 < 20'u16 -assert 10'i16 < 20'u32 -# assert 10'i16 < 20'u64 +doAssert 10'i16 < 20'u8 +doAssert 10'i16 < 20'u16 +doAssert 10'i16 < 20'u32 +# doAssert 10'i16 < 20'u64 -assert 10'i32 < 20'u8 -assert 10'i32 < 20'u16 -assert 10'i32 < 20'u32 -# assert 10'i32 < 20'u64 +doAssert 10'i32 < 20'u8 +doAssert 10'i32 < 20'u16 +doAssert 10'i32 < 20'u32 +# doAssert 10'i32 < 20'u64 -assert 10'i64 < 20'u8 -assert 10'i64 < 20'u16 -assert 10'i64 < 20'u32 -# assert 10'i64 < 20'u64 +doAssert 10'i64 < 20'u8 +doAssert 10'i64 < 20'u16 +doAssert 10'i64 < 20'u32 +# doAssert 10'i64 < 20'u64 # unsigned <= signed -assert 10'u8 <= 20'i8 -assert 10'u8 <= 20'i16 -assert 10'u8 <= 20'i32 -assert 10'u8 <= 20'i64 +doAssert 10'u8 <= 20'i8 +doAssert 10'u8 <= 20'i16 +doAssert 10'u8 <= 20'i32 +doAssert 10'u8 <= 20'i64 -assert 10'u16 <= 20'i8 -assert 10'u16 <= 20'i16 -assert 10'u16 <= 20'i32 -assert 10'u16 <= 20'i64 +doAssert 10'u16 <= 20'i8 +doAssert 10'u16 <= 20'i16 +doAssert 10'u16 <= 20'i32 +doAssert 10'u16 <= 20'i64 -assert 10'u32 <= 20'i8 -assert 10'u32 <= 20'i16 -assert 10'u32 <= 20'i32 -assert 10'u32 <= 20'i64 +doAssert 10'u32 <= 20'i8 +doAssert 10'u32 <= 20'i16 +doAssert 10'u32 <= 20'i32 +doAssert 10'u32 <= 20'i64 -# assert 10'u64 <= 20'i8 -# assert 10'u64 <= 20'i16 -# assert 10'u64 <= 20'i32 -# assert 10'u64 <= 20'i64 +# doAssert 10'u64 <= 20'i8 +# doAssert 10'u64 <= 20'i16 +# doAssert 10'u64 <= 20'i32 +# doAssert 10'u64 <= 20'i64 # signed <= unsigned -assert 10'i8 <= 20'u8 -assert 10'i8 <= 20'u16 -assert 10'i8 <= 20'u32 -# assert 10'i8 <= 20'u64 +doAssert 10'i8 <= 20'u8 +doAssert 10'i8 <= 20'u16 +doAssert 10'i8 <= 20'u32 +# doAssert 10'i8 <= 20'u64 -assert 10'i16 <= 20'u8 -assert 10'i16 <= 20'u16 -assert 10'i16 <= 20'u32 -# assert 10'i16 <= 20'u64 +doAssert 10'i16 <= 20'u8 +doAssert 10'i16 <= 20'u16 +doAssert 10'i16 <= 20'u32 +# doAssert 10'i16 <= 20'u64 -assert 10'i32 <= 20'u8 -assert 10'i32 <= 20'u16 -assert 10'i32 <= 20'u32 -# assert 10'i32 <= 20'u64 +doAssert 10'i32 <= 20'u8 +doAssert 10'i32 <= 20'u16 +doAssert 10'i32 <= 20'u32 +# doAssert 10'i32 <= 20'u64 -assert 10'i64 <= 20'u8 -assert 10'i64 <= 20'u16 -assert 10'i64 <= 20'u32 -# assert 10'i64 <= 20'u64 +doAssert 10'i64 <= 20'u8 +doAssert 10'i64 <= 20'u16 +doAssert 10'i64 <= 20'u32 +# doAssert 10'i64 <= 20'u64 # signed == unsigned -assert 10'i8 == 10'u8 -assert 10'i8 == 10'u16 -assert 10'i8 == 10'u32 -# assert 10'i8 == 10'u64 +doAssert 10'i8 == 10'u8 +doAssert 10'i8 == 10'u16 +doAssert 10'i8 == 10'u32 +# doAssert 10'i8 == 10'u64 -assert 10'i16 == 10'u8 -assert 10'i16 == 10'u16 -assert 10'i16 == 10'u32 -# assert 10'i16 == 10'u64 +doAssert 10'i16 == 10'u8 +doAssert 10'i16 == 10'u16 +doAssert 10'i16 == 10'u32 +# doAssert 10'i16 == 10'u64 -assert 10'i32 == 10'u8 -assert 10'i32 == 10'u16 -assert 10'i32 == 10'u32 -# assert 10'i32 == 10'u64 +doAssert 10'i32 == 10'u8 +doAssert 10'i32 == 10'u16 +doAssert 10'i32 == 10'u32 +# doAssert 10'i32 == 10'u64 -assert 10'i64 == 10'u8 -assert 10'i64 == 10'u16 -assert 10'i64 == 10'u32 -# assert 10'i64 == 10'u64 +doAssert 10'i64 == 10'u8 +doAssert 10'i64 == 10'u16 +doAssert 10'i64 == 10'u32 +# doAssert 10'i64 == 10'u64 # unsigned == signed -assert 10'u8 == 10'i8 -assert 10'u8 == 10'i16 -assert 10'u8 == 10'i32 -# assert 10'u8 == 10'i64 +doAssert 10'u8 == 10'i8 +doAssert 10'u8 == 10'i16 +doAssert 10'u8 == 10'i32 +# doAssert 10'u8 == 10'i64 -assert 10'u16 == 10'i8 -assert 10'u16 == 10'i16 -assert 10'u16 == 10'i32 -# assert 10'u16 == 10'i64 +doAssert 10'u16 == 10'i8 +doAssert 10'u16 == 10'i16 +doAssert 10'u16 == 10'i32 +# doAssert 10'u16 == 10'i64 -assert 10'u32 == 10'i8 -assert 10'u32 == 10'i16 -assert 10'u32 == 10'i32 -# assert 10'u32 == 10'i64 +doAssert 10'u32 == 10'i8 +doAssert 10'u32 == 10'i16 +doAssert 10'u32 == 10'i32 +# doAssert 10'u32 == 10'i64 -# assert 10'u64 == 10'i8 -# assert 10'u64 == 10'i16 -# assert 10'u64 == 10'i32 -# assert 10'u64 == 10'i64 +# doAssert 10'u64 == 10'i8 +# doAssert 10'u64 == 10'i16 +# doAssert 10'u64 == 10'i32 +# doAssert 10'u64 == 10'i64 diff --git a/tests/newconfig/tfoo.nims b/tests/newconfig/tfoo.nims index a53e777d4..6f0048afb 100644 --- a/tests/newconfig/tfoo.nims +++ b/tests/newconfig/tfoo.nims @@ -51,56 +51,56 @@ doAssert(existsEnv("dummy") == false) # issue #7393 let wd = getCurrentDir() cd("..") -assert wd != getCurrentDir() +doAssert wd != getCurrentDir() cd(wd) -assert wd == getCurrentDir() +doAssert wd == getCurrentDir() when false: # this doesn't work in a 'koch testintall' environment - assert findExe("nim") != "" + doAssert findExe("nim") != "" # general tests mode = ScriptMode.Verbose -assert getCommand() == "c" +doAssert getCommand() == "c" setCommand("cpp") -assert getCommand() == "cpp" +doAssert getCommand() == "cpp" setCommand("c") -assert cmpic("HeLLO", "hello") == 0 +doAssert cmpic("HeLLO", "hello") == 0 -assert fileExists("tests/newconfig/tfoo.nims") == true -assert dirExists("tests") == true +doAssert fileExists("tests/newconfig/tfoo.nims") == true +doAssert dirExists("tests") == true -assert fileExists("tests/newconfig/tfoo.nims") == true -assert dirExists("tests") == true +doAssert fileExists("tests/newconfig/tfoo.nims") == true +doAssert dirExists("tests") == true discard selfExe() when defined(windows): - assert toExe("nim") == "nim.exe" - assert toDll("nim") == "nim.dll" + doAssert toExe("nim") == "nim.exe" + doAssert toDll("nim") == "nim.dll" else: - assert toExe("nim") == "nim" - assert toDll("nim") == "libnim.so" + doAssert toExe("nim") == "nim" + doAssert toDll("nim") == "libnim.so" rmDir("tempXYZ") doAssertRaises(OSError): rmDir("tempXYZ", checkDir = true) -assert dirExists("tempXYZ") == false +doAssert dirExists("tempXYZ") == false mkDir("tempXYZ") -assert dirExists("tempXYZ") == true -assert fileExists("tempXYZ/koch.nim") == false +doAssert dirExists("tempXYZ") == true +doAssert fileExists("tempXYZ/koch.nim") == false when false: # this doesn't work in a 'koch testintall' environment cpFile("koch.nim", "tempXYZ/koch.nim") - assert fileExists("tempXYZ/koch.nim") == true + doAssert fileExists("tempXYZ/koch.nim") == true cpDir("nimsuggest", "tempXYZ/.") - assert dirExists("tempXYZ/tests") == true - assert fileExists("tempXYZ/nimsuggest.nim") == true + doAssert dirExists("tempXYZ/tests") == true + doAssert fileExists("tempXYZ/nimsuggest.nim") == true rmFile("tempXYZ/koch.nim") - assert fileExists("tempXYZ/koch.nim") == false + doAssert fileExists("tempXYZ/koch.nim") == false rmDir("tempXYZ") -assert dirExists("tempXYZ") == false +doAssert dirExists("tempXYZ") == false diff --git a/tests/niminaction/Chapter3/various3.nim b/tests/niminaction/Chapter3/various3.nim index 849ea71d8..4e028a048 100644 --- a/tests/niminaction/Chapter3/various3.nim +++ b/tests/niminaction/Chapter3/various3.nim @@ -7,7 +7,7 @@ Future is no longer empty, 42 import threadpool proc foo: string = "Dog" var x: FlowVar[string] = spawn foo() -assert(^x == "Dog") +doAssert(^x == "Dog") block: type @@ -19,20 +19,20 @@ block: discard var obj = Box(empty: false, contents: "Hello") - assert obj.contents == "Hello" + doAssert obj.contents == "Hello" var obj2 = Box(empty: true) doAssertRaises(FieldDefect): echo(obj2.contents) import json -assert parseJson("null").kind == JNull -assert parseJson("true").kind == JBool -assert parseJson("42").kind == JInt -assert parseJson("3.14").kind == JFloat -assert parseJson("\"Hi\"").kind == JString -assert parseJson("""{ "key": "value" }""").kind == JObject -assert parseJson("[1, 2, 3, 4]").kind == JArray +doAssert parseJson("null").kind == JNull +doAssert parseJson("true").kind == JBool +doAssert parseJson("42").kind == JInt +doAssert parseJson("3.14").kind == JFloat +doAssert parseJson("\"Hi\"").kind == JString +doAssert parseJson("""{ "key": "value" }""").kind == JObject +doAssert parseJson("[1, 2, 3, 4]").kind == JArray import json let data = """ @@ -40,15 +40,15 @@ let data = """ """ let obj = parseJson(data) -assert obj.kind == JObject -assert obj["username"].kind == JString -assert obj["username"].str == "Dominik" +doAssert obj.kind == JObject +doAssert obj["username"].kind == JString +doAssert obj["username"].str == "Dominik" block: proc count10(): int = for i in 0 ..< 10: result.inc - assert count10() == 10 + doAssert count10() == 10 type Point = tuple[x, y: int] diff --git a/tests/objvariant/tconstructionorder.nim b/tests/objvariant/tconstructionorder.nim index 19ddea7a1..5ca484884 100644 --- a/tests/objvariant/tconstructionorder.nim +++ b/tests/objvariant/tconstructionorder.nim @@ -23,22 +23,22 @@ type # This will test that all the values are what we expect. proc assertTree(root: Node) = # check root of tree - assert root.kind == Operator - assert root.operator == '*' + doAssert root.kind == Operator + doAssert root.operator == '*' # check left subtree - assert root.left.value == 5 - assert root.left.kind == Literal + doAssert root.left.value == 5 + doAssert root.left.kind == Literal # check right subtree - assert root.right.kind == Operator - assert root.right.operator == '+' + doAssert root.right.kind == Operator + doAssert root.right.operator == '+' - assert root.right.left.value == 5 - assert root.right.left.kind == Literal + doAssert root.right.left.value == 5 + doAssert root.right.left.kind == Literal - assert root.right.right.value == 10 - assert root.right.right.kind == Literal + doAssert root.right.right.value == 10 + doAssert root.right.right.kind == Literal proc newLiteralNode(value: int): Node = result = Node( diff --git a/tests/osproc/tclose.nim b/tests/osproc/tclose.nim index d466b466a..1c99237c7 100644 --- a/tests/osproc/tclose.nim +++ b/tests/osproc/tclose.nim @@ -13,12 +13,12 @@ when defined(linux): let initCount = countFds() let p = osproc.startProcess("echo", options={poUsePath}) - assert countFds() == initCount + 3 + doAssert countFds() == initCount + 3 p.close - assert countFds() == initCount + doAssert countFds() == initCount let p1 = osproc.startProcess("echo", options={poUsePath}) discard p1.inputStream - assert countFds() == initCount + 3 + doAssert countFds() == initCount + 3 p.close - assert countFds() == initCount + doAssert countFds() == initCount diff --git a/tests/parser/ttypeclasses.nim b/tests/parser/ttypeclasses.nim index 06146dcb6..e6e7a48b8 100644 --- a/tests/parser/ttypeclasses.nim +++ b/tests/parser/ttypeclasses.nim @@ -16,31 +16,31 @@ var z: ptr int const C = @[1, 2, 3] static: - assert x is ref - assert y is distinct - assert z is ptr - assert C is static - assert C[1] is static[int] - assert C[0] is static[SomeInteger] - assert C isnot static[string] - assert C is SEQ|OBJ - assert C isnot OBJ|TPL - assert int is int - assert int is T - assert int is SomeInteger - assert seq[int] is type - assert seq[int] is type[seq] - assert seq[int] isnot type[seq[float]] - assert i isnot type[int] - assert type(i) is type[int] - assert x isnot T - assert y isnot S - assert z isnot enum - assert x isnot object - assert y isnot tuple - assert z isnot seq + doAssert x is ref + doAssert y is distinct + doAssert z is ptr + doAssert C is static + doAssert C[1] is static[int] + doAssert C[0] is static[SomeInteger] + doAssert C isnot static[string] + doAssert C is SEQ|OBJ + doAssert C isnot OBJ|TPL + doAssert int is int + doAssert int is T + doAssert int is SomeInteger + doAssert seq[int] is type + doAssert seq[int] is type[seq] + doAssert seq[int] isnot type[seq[float]] + doAssert i isnot type[int] + doAssert type(i) is type[int] + doAssert x isnot T + doAssert y isnot S + doAssert z isnot enum + doAssert x isnot object + doAssert y isnot tuple + doAssert z isnot seq # XXX: These cases don't work properly at the moment: - # assert type[int] isnot int - # assert type(int) isnot int + # doAssert type[int] isnot int + # doAssert type(int) isnot int diff --git a/tests/pragmas/tbitsize.nim b/tests/pragmas/tbitsize.nim index 7a44944d2..39aee445f 100644 --- a/tests/pragmas/tbitsize.nim +++ b/tests/pragmas/tbitsize.nim @@ -10,13 +10,13 @@ type var b: bits -assert b.flag == 0 +doAssert b.flag == 0 b.flag = 1 -assert b.flag == 1 +doAssert b.flag == 1 b.flag = 2 -assert b.flag == 0 +doAssert b.flag == 0 b.opts = 7 -assert b.opts == 7 +doAssert b.opts == 7 b.opts = 9 -assert b.opts == -7 +doAssert b.opts == -7 diff --git a/tests/pragmas/tcustom_pragma.nim b/tests/pragmas/tcustom_pragma.nim index a4a200c34..e9dac753d 100644 --- a/tests/pragmas/tcustom_pragma.nim +++ b/tests/pragmas/tcustom_pragma.nim @@ -8,7 +8,7 @@ block: proc myProc():int {.myAttr.} = 2 const hasMyAttr = myProc.hasCustomPragma(myAttr) static: - assert(hasMyAttr) + doAssert(hasMyAttr) block: template myAttr(a: string) {.pragma.} @@ -19,8 +19,8 @@ block: var o: MyObj static: - assert o.myField2.hasCustomPragma(myAttr) - assert(not o.myField1.hasCustomPragma(myAttr)) + doAssert o.myField2.hasCustomPragma(myAttr) + doAssert(not o.myField1.hasCustomPragma(myAttr)) import custom_pragma block: # A bit more advanced case @@ -42,31 +42,31 @@ block: # A bit more advanced case var s: MySerializable const aDefVal = s.a.getCustomPragmaVal(defaultValue) - static: assert(aDefVal == 5) + static: doAssert(aDefVal == 5) const aSerKey = s.a.getCustomPragmaVal(serializationKey) - static: assert(aSerKey == "asdf") + static: doAssert(aSerKey == "asdf") const cSerKey = getCustomPragmaVal(s.field.c, serializationKey) - static: assert(cSerKey == "cc") + static: doAssert(cSerKey == "cc") const procSerKey = getCustomPragmaVal(myproc, serializationKey) - static: assert(procSerKey == "myprocSS") + static: doAssert(procSerKey == "myprocSS") - static: assert(hasCustomPragma(myproc, alternativeKey)) + static: doAssert(hasCustomPragma(myproc, alternativeKey)) const hasFieldCustomPragma = s.field.hasCustomPragma(defaultValue) - static: assert(hasFieldCustomPragma == false) + static: doAssert(hasFieldCustomPragma == false) # pragma on an object static: - assert Subfield.hasCustomPragma(defaultValue) - assert(Subfield.getCustomPragmaVal(defaultValue) == "catman") + doAssert Subfield.hasCustomPragma(defaultValue) + doAssert(Subfield.getCustomPragmaVal(defaultValue) == "catman") - assert hasCustomPragma(type(s.field), defaultValue) + doAssert hasCustomPragma(type(s.field), defaultValue) proc foo(s: var MySerializable) = - static: assert(s.a.getCustomPragmaVal(defaultValue) == 5) + static: doAssert(s.a.getCustomPragmaVal(defaultValue) == 5) foo(s) @@ -91,8 +91,8 @@ block: # ref types leftSerKey = getCustomPragmaVal(s.left, serializationKey) rightSerKey = getCustomPragmaVal(s.right, serializationKey) static: - assert leftSerKey == "l" - assert rightSerKey == "r" + doAssert leftSerKey == "l" + doAssert rightSerKey == "r" var specS = SpecialNodeRef() @@ -100,25 +100,25 @@ block: # ref types dataDefVal = hasCustomPragma(specS.data, defaultValue) specLeftSerKey = hasCustomPragma(specS.left, serializationKey) static: - assert dataDefVal == true - assert specLeftSerKey == true + doAssert dataDefVal == true + doAssert specLeftSerKey == true var ptrS = NodePtr(nil) const ptrRightSerKey = getCustomPragmaVal(ptrS.right, serializationKey) static: - assert ptrRightSerKey == "r" + doAssert ptrRightSerKey == "r" var f = MyFile() const fileDefVal = f.getCustomPragmaVal(defaultValue) filePathDefVal = f.path.getCustomPragmaVal(defaultValue) static: - assert fileDefVal == "closed" - assert filePathDefVal == "invalid" + doAssert fileDefVal == "closed" + doAssert filePathDefVal == "invalid" static: - assert TypeWithoutPragma.hasCustomPragma(defaultValue) == false + doAssert TypeWithoutPragma.hasCustomPragma(defaultValue) == false block: type @@ -144,9 +144,9 @@ block: nestedItemDefVal = vari.nestedItem.getCustomPragmaVal(defaultValue) static: - assert hasIntSerKey - assert strSerKey == "string" - assert nestedItemDefVal == "Nimmers of the world, unite!" + doAssert hasIntSerKey + doAssert strSerKey == "string" + doAssert nestedItemDefVal == "Nimmers of the world, unite!" block: template simpleAttr {.pragma.} @@ -154,7 +154,7 @@ block: type Annotated {.simpleAttr.} = object proc generic_proc[T]() = - assert Annotated.hasCustomPragma(simpleAttr) + doAssert Annotated.hasCustomPragma(simpleAttr) #-------------------------------------------------------------------------- @@ -252,7 +252,7 @@ block: block: macro expectedAst(expectedRepr: static[string], input: untyped): untyped = - assert input.treeRepr & "\n" == expectedRepr + doAssert input.treeRepr & "\n" == expectedRepr return input const procTypeAst = """ @@ -270,7 +270,7 @@ ProcTy type Foo = proc (x: int) {.expectedAst(procTypeAst), async.} - static: assert Foo is proc(x: int): Future[void] + static: doAssert Foo is proc(x: int): Future[void] const asyncProcTypeAst = """ ProcTy @@ -288,7 +288,7 @@ ProcTy type Bar = proc (s: string) {.async, expectedAst(asyncProcTypeAst).} - static: assert Bar is proc(x: string): Future[void] + static: doAssert Bar is proc(x: string): Future[void] const typeAst = """ TypeDef @@ -310,7 +310,7 @@ TypeDef Baz {.expectedAst(typeAst).} = object x: string - static: assert Baz.x is string + static: doAssert Baz.x is string const procAst = """ ProcDef @@ -333,7 +333,7 @@ ProcDef proc bar(s: string): string {.expectedAst(procAst).} = return s - static: assert bar("x") == "x" + static: doAssert bar("x") == "x" #------------------------------------------------------ # bug #13909 diff --git a/tests/sets/tsets_various.nim b/tests/sets/tsets_various.nim index c27d8e124..3e468c8f9 100644 --- a/tests/sets/tsets_various.nim +++ b/tests/sets/tsets_various.nim @@ -58,13 +58,13 @@ block tsets2: var t = initHashSet[tuple[x, y: int]]() t.incl((0,0)) t.incl((1,0)) - assert(not t.containsOrIncl((0,1))) + doAssert(not t.containsOrIncl((0,1))) t.incl((1,1)) for x in 0..1: for y in 0..1: - assert((x,y) in t) - #assert($t == + doAssert((x,y) in t) + #doAssert($t == # "{(x: 0, y: 0), (x: 0, y: 1), (x: 1, y: 0), (x: 1, y: 1)}") block setTest2: @@ -76,31 +76,31 @@ block tsets2: t.incl("012") t.incl("123") # test duplicates - assert "123" in t - assert "111" notin t # deleted + doAssert "123" in t + doAssert "111" notin t # deleted - assert t.missingOrExcl("000") - assert "000" notin t - assert t.missingOrExcl("012") == false - assert "012" notin t + doAssert t.missingOrExcl("000") + doAssert "000" notin t + doAssert t.missingOrExcl("012") == false + doAssert "012" notin t - assert t.containsOrIncl("012") == false - assert t.containsOrIncl("012") - assert "012" in t # added back + doAssert t.containsOrIncl("012") == false + doAssert t.containsOrIncl("012") + doAssert "012" in t # added back for key in items(data): t.incl(key) - for key in items(data): assert key in t + for key in items(data): doAssert key in t for key in items(data): t.excl(key) - for key in items(data): assert key notin t + for key in items(data): doAssert key notin t block orderedSetTest1: var t = data.toOrderedSet - for key in items(data): assert key in t + for key in items(data): doAssert key in t var i = 0 # `items` needs to yield in insertion order: for key in items(t): - assert key == data[i] + doAssert key == data[i] inc(i) @@ -117,22 +117,22 @@ block tsets3: s1_s3 = s1 + s3 s2_s3 = s2 + s3 - assert s1_s2.len == 7 - assert s1_s3.len == 8 - assert s2_s3.len == 6 + doAssert s1_s2.len == 7 + doAssert s1_s3.len == 8 + doAssert s2_s3.len == 6 for i in s1: - assert i in s1_s2 - assert i in s1_s3 + doAssert i in s1_s2 + doAssert i in s1_s3 for i in s2: - assert i in s1_s2 - assert i in s2_s3 + doAssert i in s1_s2 + doAssert i in s2_s3 for i in s3: - assert i in s1_s3 - assert i in s2_s3 + doAssert i in s1_s3 + doAssert i in s2_s3 - assert((s1 + s1) == s1) - assert((s2 + s1) == s1_s2) + doAssert((s1 + s1) == s1) + doAssert((s2 + s1) == s1_s2) block intersection: let @@ -140,22 +140,22 @@ block tsets3: s1_s3 = intersection(s1, s3) s2_s3 = s2 * s3 - assert s1_s2.len == 3 - assert s1_s3.len == 0 - assert s2_s3.len == 2 + doAssert s1_s2.len == 3 + doAssert s1_s3.len == 0 + doAssert s2_s3.len == 2 for i in s1_s2: - assert i in s1 - assert i in s2 + doAssert i in s1 + doAssert i in s2 for i in s1_s3: - assert i in s1 - assert i in s3 + doAssert i in s1 + doAssert i in s3 for i in s2_s3: - assert i in s2 - assert i in s3 + doAssert i in s2 + doAssert i in s3 - assert((s2 * s2) == s2) - assert((s3 * s2) == s2_s3) + doAssert((s2 * s2) == s2) + doAssert((s3 * s2) == s2_s3) block symmetricDifference: let @@ -163,22 +163,22 @@ block tsets3: s1_s3 = s1 -+- s3 s2_s3 = s2 -+- s3 - assert s1_s2.len == 4 - assert s1_s3.len == 8 - assert s2_s3.len == 4 + doAssert s1_s2.len == 4 + doAssert s1_s3.len == 8 + doAssert s2_s3.len == 4 for i in s1: - assert i in s1_s2 xor i in s2 - assert i in s1_s3 xor i in s3 + doAssert i in s1_s2 xor i in s2 + doAssert i in s1_s3 xor i in s3 for i in s2: - assert i in s1_s2 xor i in s1 - assert i in s2_s3 xor i in s3 + doAssert i in s1_s2 xor i in s1 + doAssert i in s2_s3 xor i in s3 for i in s3: - assert i in s1_s3 xor i in s1 - assert i in s2_s3 xor i in s2 + doAssert i in s1_s3 xor i in s1 + doAssert i in s2_s3 xor i in s2 - assert((s3 -+- s3) == initHashSet[int]()) - assert((s3 -+- s1) == s1_s3) + doAssert((s3 -+- s3) == initHashSet[int]()) + doAssert((s3 -+- s1) == s1_s3) block difference: let @@ -186,23 +186,23 @@ block tsets3: s1_s3 = difference(s1, s3) s2_s3 = s2 - s3 - assert s1_s2.len == 2 - assert s1_s3.len == 5 - assert s2_s3.len == 3 + doAssert s1_s2.len == 2 + doAssert s1_s3.len == 5 + doAssert s2_s3.len == 3 for i in s1: - assert i in s1_s2 xor i in s2 - assert i in s1_s3 xor i in s3 + doAssert i in s1_s2 xor i in s2 + doAssert i in s1_s3 xor i in s3 for i in s2: - assert i in s2_s3 xor i in s3 + doAssert i in s2_s3 xor i in s3 - assert((s2 - s2) == initHashSet[int]()) + doAssert((s2 - s2) == initHashSet[int]()) block disjoint: - assert(not disjoint(s1, s2)) - assert disjoint(s1, s3) - assert(not disjoint(s2, s3)) - assert(not disjoint(s2, s2)) + doAssert(not disjoint(s1, s2)) + doAssert disjoint(s1, s3) + doAssert(not disjoint(s2, s3)) + doAssert(not disjoint(s2, s2)) block: # https://github.com/nim-lang/Nim/issues/13496 template testDel(body) = @@ -217,7 +217,7 @@ block: # https://github.com/nim-lang/Nim/issues/13496 doAssert sortedItems(t) == @[15, 17, 19] var s = newSeq[int]() for v in t: s.add(v) - assert s.len == 3 + doAssert s.len == 3 doAssert sortedItems(s) == @[15, 17, 19] when t is OrderedSet: doAssert sortedPairs(t) == @[(a: 0, b: 15), (a: 1, b: 19), (a: 2, b: 17)] diff --git a/tests/statictypes/tstatictypes.nim b/tests/statictypes/tstatictypes.nim index f3a0f0fcb..8817e07a0 100644 --- a/tests/statictypes/tstatictypes.nim +++ b/tests/statictypes/tstatictypes.nim @@ -17,14 +17,14 @@ Val1 import macros -template ok(x) = assert(x) -template no(x) = assert(not x) +template ok(x) = doAssert(x) +template no(x) = doAssert(not x) template accept(x) = - static: assert(compiles(x)) + static: doAssert(compiles(x)) template reject(x) = - static: assert(not compiles(x)) + static: doAssert(not compiles(x)) proc plus(a, b: int): int = a + b @@ -54,8 +54,8 @@ when true: b: static[int], c: static int) = static: - assert a.isStatic and b.isStatic and c.isStatic - assert isStatic(a + plus(b, c)) + doAssert a.isStatic and b.isStatic and c.isStatic + doAssert isStatic(a + plus(b, c)) echo "staticAlialProc instantiated with ", a, b, c when b mod a == 0: @@ -111,9 +111,9 @@ when true: var aw3: ArrayWrapper3[(10, "str")] static: - assert aw1.data.high == 5 - assert aw2.data.high == 6 - assert aw3.data.high == 9 + doAssert aw1.data.high == 5 + doAssert aw2.data.high == 6 + doAssert aw3.data.high == 9 # #6077 block: diff --git a/tests/stdlib/t14139.nim b/tests/stdlib/t14139.nim index 78e0f6645..07d2ff137 100644 --- a/tests/stdlib/t14139.nim +++ b/tests/stdlib/t14139.nim @@ -6,4 +6,4 @@ test_queue.push(7) test_queue.push(3) test_queue.push(9) let i = test_queue.pushpop(10) -assert i == 3 +doAssert i == 3 diff --git a/tests/stdlib/talgorithm.nim b/tests/stdlib/talgorithm.nim index 9dec68f03..148a65289 100644 --- a/tests/stdlib/talgorithm.nim +++ b/tests/stdlib/talgorithm.nim @@ -20,31 +20,31 @@ test() block: # Tests for lowerBound var arr = @[1, 2, 3, 5, 6, 7, 8, 9] - assert arr.lowerBound(0) == 0 - assert arr.lowerBound(4) == 3 - assert arr.lowerBound(5) == 3 - assert arr.lowerBound(10) == 8 + doAssert arr.lowerBound(0) == 0 + doAssert arr.lowerBound(4) == 3 + doAssert arr.lowerBound(5) == 3 + doAssert arr.lowerBound(10) == 8 arr = @[1, 5, 10] - assert arr.lowerBound(4) == 1 - assert arr.lowerBound(5) == 1 - assert arr.lowerBound(6) == 2 + doAssert arr.lowerBound(4) == 1 + doAssert arr.lowerBound(5) == 1 + doAssert arr.lowerBound(6) == 2 # Tests for isSorted var srt1 = [1, 2, 3, 4, 4, 4, 4, 5] var srt2 = ["iello", "hello"] var srt3 = [1.0, 1.0, 1.0] var srt4: seq[int] - assert srt1.isSorted(cmp) == true - assert srt2.isSorted(cmp) == false - assert srt3.isSorted(cmp) == true - assert srt4.isSorted(cmp) == true + doAssert srt1.isSorted(cmp) == true + doAssert srt2.isSorted(cmp) == false + doAssert srt3.isSorted(cmp) == true + doAssert srt4.isSorted(cmp) == true var srtseq = newSeq[int]() - assert srtseq.isSorted(cmp) == true + doAssert srtseq.isSorted(cmp) == true # Tests for reversed var arr1 = @[0, 1, 2, 3, 4] - assert arr1.reversed() == @[4, 3, 2, 1, 0] + doAssert arr1.reversed() == @[4, 3, 2, 1, 0] for i in 0 .. high(arr1): - assert arr1.reversed(0, i) == arr1.reversed()[high(arr1) - i .. high(arr1)] - assert arr1.reversed(i, high(arr1)) == arr1.reversed()[0 .. high(arr1) - i] + doAssert arr1.reversed(0, i) == arr1.reversed()[high(arr1) - i .. high(arr1)] + doAssert arr1.reversed(i, high(arr1)) == arr1.reversed()[0 .. high(arr1) - i] block: var list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10] diff --git a/tests/stdlib/tcritbits.nim b/tests/stdlib/tcritbits.nim index 7b2dde1c8..b350cb280 100644 --- a/tests/stdlib/tcritbits.nim +++ b/tests/stdlib/tcritbits.nim @@ -16,48 +16,48 @@ template main = doAssert r.contains"def" r.excl "def" - assert r.missingOrExcl("foo") == false - assert "foo" notin toSeq(r.items) + doAssert r.missingOrExcl("foo") == false + doAssert "foo" notin toSeq(r.items) - assert r.missingOrExcl("foo") == true + doAssert r.missingOrExcl("foo") == true - assert toSeq(r.items) == @["abc", "definition", "prefix", "xyz"] + doAssert toSeq(r.items) == @["abc", "definition", "prefix", "xyz"] - assert toSeq(r.itemsWithPrefix("de")) == @["definition"] + doAssert toSeq(r.itemsWithPrefix("de")) == @["definition"] var c = CritBitTree[int]() c.inc("a") - assert c["a"] == 1 + doAssert c["a"] == 1 c.inc("a", 4) - assert c["a"] == 5 + doAssert c["a"] == 5 c.inc("a", -5) - assert c["a"] == 0 + doAssert c["a"] == 0 c.inc("b", 2) - assert c["b"] == 2 + doAssert c["b"] == 2 c.inc("c", 3) - assert c["c"] == 3 + doAssert c["c"] == 3 c.inc("a", 1) - assert c["a"] == 1 + doAssert c["a"] == 1 var cf = CritBitTree[float]() cf.incl("a", 1.0) - assert cf["a"] == 1.0 + doAssert cf["a"] == 1.0 cf.incl("b", 2.0) - assert cf["b"] == 2.0 + doAssert cf["b"] == 2.0 cf.incl("c", 3.0) - assert cf["c"] == 3.0 + doAssert cf["c"] == 3.0 - assert cf.len == 3 + doAssert cf.len == 3 cf.excl("c") - assert cf.len == 2 + doAssert cf.len == 2 var cb: CritBitTree[string] cb.incl("help", "help") diff --git a/tests/stdlib/tdeques.nim b/tests/stdlib/tdeques.nim index db392c6cc..99208d4cf 100644 --- a/tests/stdlib/tdeques.nim +++ b/tests/stdlib/tdeques.nim @@ -11,7 +11,7 @@ block: proc main = var testDeque = initDeque[int]() testDeque.addFirst(1) - assert testDeque.index(0) == 1 + doAssert testDeque.index(0) == 1 main() @@ -35,26 +35,26 @@ block: deq.addFirst(123) var first = deq.popFirst() deq.addLast(56) - assert(deq.peekLast() == 56) + doAssert(deq.peekLast() == 56) deq.addLast(6) - assert(deq.peekLast() == 6) + doAssert(deq.peekLast() == 6) var second = deq.popFirst() deq.addLast(789) - assert(deq.peekLast() == 789) + doAssert(deq.peekLast() == 789) - assert first == 123 - assert second == 9 - assert($deq == "[4, 56, 6, 789]") - assert deq == [4, 56, 6, 789].toDeque + doAssert first == 123 + doAssert second == 9 + doAssert($deq == "[4, 56, 6, 789]") + doAssert deq == [4, 56, 6, 789].toDeque - assert deq[0] == deq.peekFirst and deq.peekFirst == 4 - #assert deq[^1] == deq.peekLast and deq.peekLast == 789 + doAssert deq[0] == deq.peekFirst and deq.peekFirst == 4 + #doAssert deq[^1] == deq.peekLast and deq.peekLast == 789 deq[0] = 42 deq[deq.len - 1] = 7 - assert 6 in deq and 789 notin deq - assert deq.find(6) >= 0 - assert deq.find(789) < 0 + doAssert 6 in deq and 789 notin deq + doAssert deq.find(6) >= 0 + doAssert deq.find(789) < 0 block: var d = initDeque[int](1) @@ -74,21 +74,21 @@ block: for i in -2 .. 10: if i in deq: - assert deq.contains(i) and deq.find(i) >= 0 + doAssert deq.contains(i) and deq.find(i) >= 0 else: - assert(not deq.contains(i) and deq.find(i) < 0) + doAssert(not deq.contains(i) and deq.find(i) < 0) when compileOption("boundChecks"): try: echo deq[99] - assert false + doAssert false except IndexDefect: discard try: - assert deq.len == 4 + doAssert deq.len == 4 for i in 0 ..< 5: deq.popFirst() - assert false + doAssert false except IndexDefect: discard @@ -98,24 +98,24 @@ block: deq.popFirst() deq.popLast() for i in 5 .. 8: deq.addFirst i - assert $deq == "[8, 7, 6, 5, 2, 3]" + doAssert $deq == "[8, 7, 6, 5, 2, 3]" # Similar to proc from the documentation example proc foo(a, b: Positive) = # assume random positive values for `a` and `b`. var deq = initDeque[int]() - assert deq.len == 0 + doAssert deq.len == 0 for i in 1 .. a: deq.addLast i if b < deq.len: # checking before indexed access. - assert deq[b] == b + 1 + doAssert deq[b] == b + 1 # The following two lines don't need any checking on access due to the logic # of the program, but that would not be the case if `a` could be 0. - assert deq.peekFirst == 1 - assert deq.peekLast == a + doAssert deq.peekFirst == 1 + doAssert deq.peekLast == a while deq.len > 0: # checking if the deque is empty - assert deq.popFirst() > 0 + doAssert deq.popFirst() > 0 #foo(0,0) foo(8, 5) @@ -133,7 +133,7 @@ block t13310: q.addFirst([1'i16].toHashSet) q.addFirst([2'i16].toHashSet) q.addFirst([3'i16].toHashSet) - assert $q == "[{3}, {2}, {1}]" + doAssert $q == "[{3}, {2}, {1}]" static: main() diff --git a/tests/stdlib/teditdistance.nim b/tests/stdlib/teditdistance.nim index 5a8acd513..433535635 100644 --- a/tests/stdlib/teditdistance.nim +++ b/tests/stdlib/teditdistance.nim @@ -30,11 +30,11 @@ doAssert editDistanceAscii("kitten", "sitting") == 3 # from Wikipedia doAssert editDistanceAscii("flaw", "lawn") == 2 # from Wikipedia -assert(editDistance("prefix__hallo_suffix", "prefix__hallo_suffix") == 0) -assert(editDistance("prefix__hallo_suffix", "prefix__hallo_suffi1") == 1) -assert(editDistance("prefix__hallo_suffix", "prefix__HALLO_suffix") == 5) -assert(editDistance("prefix__hallo_suffix", "prefix__ha_suffix") == 3) -assert(editDistance("prefix__hallo_suffix", "prefix") == 14) -assert(editDistance("prefix__hallo_suffix", "suffix") == 14) -assert(editDistance("prefix__hallo_suffix", "prefix__hao_suffix") == 2) -assert(editDistance("main", "malign") == 2) \ No newline at end of file +doAssert(editDistance("prefix__hallo_suffix", "prefix__hallo_suffix") == 0) +doAssert(editDistance("prefix__hallo_suffix", "prefix__hallo_suffi1") == 1) +doAssert(editDistance("prefix__hallo_suffix", "prefix__HALLO_suffix") == 5) +doAssert(editDistance("prefix__hallo_suffix", "prefix__ha_suffix") == 3) +doAssert(editDistance("prefix__hallo_suffix", "prefix") == 14) +doAssert(editDistance("prefix__hallo_suffix", "suffix") == 14) +doAssert(editDistance("prefix__hallo_suffix", "prefix__hao_suffix") == 2) +doAssert(editDistance("main", "malign") == 2) \ No newline at end of file diff --git a/tests/stdlib/tenumerate.nim b/tests/stdlib/tenumerate.nim index e5f21bc15..7a1c2d10a 100644 --- a/tests/stdlib/tenumerate.nim +++ b/tests/stdlib/tenumerate.nim @@ -6,14 +6,14 @@ block: var res: seq[(int, int)] for i, x in enumerate(a): res.add (i, x) - assert res == @[(0, 1), (1, 3), (2, 5), (3, 7)] + doAssert res == @[(0, 1), (1, 3), (2, 5), (3, 7)] block: var res: seq[(int, int)] for (i, x) in enumerate(a.items): res.add (i, x) - assert res == @[(0, 1), (1, 3), (2, 5), (3, 7)] + doAssert res == @[(0, 1), (1, 3), (2, 5), (3, 7)] block: var res: seq[(int, int)] for i, x in enumerate(3, a): res.add (i, x) - assert res == @[(3, 1), (4, 3), (5, 5), (6, 7)] + doAssert res == @[(3, 1), (4, 3), (5, 5), (6, 7)] diff --git a/tests/stdlib/thtmlparser.nim b/tests/stdlib/thtmlparser.nim index 2ac7b4004..f35785b25 100644 --- a/tests/stdlib/thtmlparser.nim +++ b/tests/stdlib/thtmlparser.nim @@ -47,7 +47,7 @@ block t2813: for n in tree.findAll("table"): n.findAll("tr", rows) # len = 2 break - assert tree.findAll("tr").len == rows.len + doAssert tree.findAll("tr").len == rows.len block t2814: diff --git a/tests/stdlib/thttpcore.nim b/tests/stdlib/thttpcore.nim index fd5e1d90c..6f88e9536 100644 --- a/tests/stdlib/thttpcore.nim +++ b/tests/stdlib/thttpcore.nim @@ -2,31 +2,31 @@ import httpcore, strutils block: block HttpCode: - assert $Http418 == "418 I'm a teapot" - assert Http418.is4xx() == true - assert Http418.is2xx() == false + doAssert $Http418 == "418 I'm a teapot" + doAssert Http418.is4xx() == true + doAssert Http418.is2xx() == false block headers: var h = newHttpHeaders() - assert h.len == 0 + doAssert h.len == 0 h.add("Cookie", "foo") - assert h.len == 1 - assert h.hasKey("cooKIE") - assert h["Cookie"] == "foo" - assert h["cookie"] == "foo" + doAssert h.len == 1 + doAssert h.hasKey("cooKIE") + doAssert h["Cookie"] == "foo" + doAssert h["cookie"] == "foo" h["cookie"] = @["bar", "x"] - assert h["Cookie"] == "bar" - assert h["Cookie", 1] == "x" - assert h["Cookie"].contains("BaR") == true - assert h["Cookie"].contains("X") == true - assert "baR" in h["cookiE"] + doAssert h["Cookie"] == "bar" + doAssert h["Cookie", 1] == "x" + doAssert h["Cookie"].contains("BaR") == true + doAssert h["Cookie"].contains("X") == true + doAssert "baR" in h["cookiE"] h.del("coOKie") - assert h.len == 0 + doAssert h.len == 0 # Test that header constructor works with repeated values let h1 = newHttpHeaders({"a": "1", "a": "2", "A": "3"}) - assert seq[string](h1["a"]).join(",") == "1,2,3" + doAssert seq[string](h1["a"]).join(",") == "1,2,3" block test_cookies_with_comma: doAssert parseHeader("cookie: foo, bar") == ("cookie", @["foo, bar"]) diff --git a/tests/stdlib/tlists.nim b/tests/stdlib/tlists.nim index 59ac8d7ee..b5a3d3f7e 100644 --- a/tests/stdlib/tlists.nim +++ b/tests/stdlib/tlists.nim @@ -11,16 +11,16 @@ block SinglyLinkedListTest1: var L: SinglyLinkedList[int] for d in items(data): L.prepend(d) for d in items(data): L.append(d) - assert($L == "[6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6]") + doAssert($L == "[6, 5, 4, 3, 2, 1, 1, 2, 3, 4, 5, 6]") - assert(4 in L) + doAssert(4 in L) block SinglyLinkedListTest2: var L: SinglyLinkedList[string] for d in items(data): L.prepend($d) - assert($L == """["6", "5", "4", "3", "2", "1"]""") + doAssert($L == """["6", "5", "4", "3", "2", "1"]""") - assert("4" in L) + doAssert("4" in L) block DoublyLinkedListTest1: @@ -28,39 +28,39 @@ block DoublyLinkedListTest1: for d in items(data): L.prepend(d) for d in items(data): L.append(d) L.remove(L.find(1)) - assert($L == "[6, 5, 4, 3, 2, 1, 2, 3, 4, 5, 6]") + doAssert($L == "[6, 5, 4, 3, 2, 1, 2, 3, 4, 5, 6]") - assert(4 in L) + doAssert(4 in L) block SinglyLinkedRingTest1: var L: SinglyLinkedRing[int] L.prepend(4) - assert($L == "[4]") + doAssert($L == "[4]") L.prepend(4) - assert($L == "[4, 4]") - assert(4 in L) + doAssert($L == "[4, 4]") + doAssert(4 in L) block DoublyLinkedRingTest1: var L: DoublyLinkedRing[int] L.prepend(4) - assert($L == "[4]") + doAssert($L == "[4]") L.prepend(4) - assert($L == "[4, 4]") - assert(4 in L) + doAssert($L == "[4, 4]") + doAssert(4 in L) L.append(3) L.append(5) - assert($L == "[4, 4, 3, 5]") + doAssert($L == "[4, 4, 3, 5]") L.remove(L.find(3)) L.remove(L.find(5)) L.remove(L.find(4)) L.remove(L.find(4)) - assert($L == "[]") - assert(4 notin L) + doAssert($L == "[]") + doAssert(4 notin L) block tlistsToString: block: @@ -105,8 +105,8 @@ template testCommon(initList, toList) = doAssert a.toSeq == [f0] doAssert b.toSeq == [f0, f1] f0.x = 42 - assert a.head.value.x == 42 - assert b.head.value.x == 42 + doAssert a.head.value.x == 42 + doAssert b.head.value.x == 42 block: # add, addMoved block: diff --git a/tests/stdlib/tmath.nim b/tests/stdlib/tmath.nim index 0f66a94d1..64a4ff0ca 100644 --- a/tests/stdlib/tmath.nim +++ b/tests/stdlib/tmath.nim @@ -152,10 +152,10 @@ block: return sqrt(num) # check gamma function - assert(gamma(5.0) == 24.0) # 4! - assert(lgamma(1.0) == 0.0) # ln(1.0) == 0.0 - assert(erf(6.0) > erf(5.0)) - assert(erfc(6.0) < erfc(5.0)) + doAssert(gamma(5.0) == 24.0) # 4! + doAssert(lgamma(1.0) == 0.0) # ln(1.0) == 0.0 + doAssert(erf(6.0) > erf(5.0)) + doAssert(erfc(6.0) < erfc(5.0)) # Function for approximate comparison of floats @@ -215,21 +215,21 @@ block: doAssert(classify(trunc(0.0'f32)) == fcZero) block: # sgn() tests - assert sgn(1'i8) == 1 - assert sgn(1'i16) == 1 - assert sgn(1'i32) == 1 - assert sgn(1'i64) == 1 - assert sgn(1'u8) == 1 - assert sgn(1'u16) == 1 - assert sgn(1'u32) == 1 - assert sgn(1'u64) == 1 - assert sgn(-12342.8844'f32) == -1 - assert sgn(123.9834'f64) == 1 - assert sgn(0'i32) == 0 - assert sgn(0'f32) == 0 - assert sgn(NegInf) == -1 - assert sgn(Inf) == 1 - assert sgn(NaN) == 0 + doAssert sgn(1'i8) == 1 + doAssert sgn(1'i16) == 1 + doAssert sgn(1'i32) == 1 + doAssert sgn(1'i64) == 1 + doAssert sgn(1'u8) == 1 + doAssert sgn(1'u16) == 1 + doAssert sgn(1'u32) == 1 + doAssert sgn(1'u64) == 1 + doAssert sgn(-12342.8844'f32) == -1 + doAssert sgn(123.9834'f64) == 1 + doAssert sgn(0'i32) == 0 + doAssert sgn(0'f32) == 0 + doAssert sgn(NegInf) == -1 + doAssert sgn(Inf) == 1 + doAssert sgn(NaN) == 0 block: # fac() tests try: diff --git a/tests/stdlib/tmd5.nim b/tests/stdlib/tmd5.nim index 736fa05a7..88a7b8d37 100644 --- a/tests/stdlib/tmd5.nim +++ b/tests/stdlib/tmd5.nim @@ -1,7 +1,7 @@ import md5 -assert(getMD5("Franz jagt im komplett verwahrlosten Taxi quer durch Bayern") == +doAssert(getMD5("Franz jagt im komplett verwahrlosten Taxi quer durch Bayern") == "a3cca2b2aa1e3b5b3b5aad99a8529074") -assert(getMD5("Frank jagt im komplett verwahrlosten Taxi quer durch Bayern") == +doAssert(getMD5("Frank jagt im komplett verwahrlosten Taxi quer durch Bayern") == "7e716d0e702df0505fc72e2b89467910") -assert($toMD5("") == "d41d8cd98f00b204e9800998ecf8427e") +doAssert($toMD5("") == "d41d8cd98f00b204e9800998ecf8427e") diff --git a/tests/stdlib/tos.nim b/tests/stdlib/tos.nim index 019303ebf..c053c16f2 100644 --- a/tests/stdlib/tos.nim +++ b/tests/stdlib/tos.nim @@ -468,19 +468,19 @@ block isRelativeTo: doAssert not isRelativeTo("/foo2", "/foo") block: # quoteShellWindows - assert quoteShellWindows("aaa") == "aaa" - assert quoteShellWindows("aaa\"") == "aaa\\\"" - assert quoteShellWindows("") == "\"\"" + doAssert quoteShellWindows("aaa") == "aaa" + doAssert quoteShellWindows("aaa\"") == "aaa\\\"" + doAssert quoteShellWindows("") == "\"\"" block: # quoteShellWindows - assert quoteShellPosix("aaa") == "aaa" - assert quoteShellPosix("aaa a") == "'aaa a'" - assert quoteShellPosix("") == "''" - assert quoteShellPosix("a'a") == "'a'\"'\"'a'" + doAssert quoteShellPosix("aaa") == "aaa" + doAssert quoteShellPosix("aaa a") == "'aaa a'" + doAssert quoteShellPosix("") == "''" + doAssert quoteShellPosix("a'a") == "'a'\"'\"'a'" block: # quoteShell when defined(posix): - assert quoteShell("") == "''" + doAssert quoteShell("") == "''" block: # normalizePathEnd # handle edge cases correctly: shouldn't affect whether path is diff --git a/tests/stdlib/tparsecfg.nim b/tests/stdlib/tparsecfg.nim index bbf88ee03..5c077bbda 100644 --- a/tests/stdlib/tparsecfg.nim +++ b/tests/stdlib/tparsecfg.nim @@ -24,8 +24,8 @@ when not defined(js): var config2 = loadConfig(file) let bar = config2.getSectionValue("foo", "bar") let foo = config2.getSectionValue("foo", "foo") - assert(bar == "-1") - assert(foo == "abc") + doAssert(bar == "-1") + doAssert(foo == "abc") ## Creating a configuration file. var dict1 = newConfig() diff --git a/tests/stdlib/tparsopt.nim b/tests/stdlib/tparsopt.nim index 948bc8d5f..54a470cb3 100644 --- a/tests/stdlib/tparsopt.nim +++ b/tests/stdlib/tparsopt.nim @@ -27,7 +27,7 @@ for kind, key, val in getopt(): of "version", "v": writeVersion() else: writeLine(stdout, "Unknown command line option: ", key, ": ", val) - of cmdEnd: assert(false) # cannot happen + of cmdEnd: doAssert(false) # cannot happen if filename == "": # no filename has been given, so we show the help: writeHelp() diff --git a/tests/stdlib/tpegs.nim b/tests/stdlib/tpegs.nim index 2990a44a5..99b7dc6f4 100644 --- a/tests/stdlib/tpegs.nim +++ b/tests/stdlib/tpegs.nim @@ -146,4 +146,4 @@ block: echo "Event parser output" echo "-------------------" let pLen = parseArithExpr(txt) - assert txt.len == pLen + doAssert txt.len == pLen diff --git a/tests/stdlib/tpunycode.nim b/tests/stdlib/tpunycode.nim index 998bd3bdf..596c5ef63 100644 --- a/tests/stdlib/tpunycode.nim +++ b/tests/stdlib/tpunycode.nim @@ -1,5 +1,5 @@ import punycode -assert(decode(encode("", "bücher")) == "bücher") -assert(decode(encode("münchen")) == "münchen") -assert encode("xn--", "münchen") == "xn--mnchen-3ya" +doAssert(decode(encode("", "bücher")) == "bücher") +doAssert(decode(encode("münchen")) == "münchen") +doAssert encode("xn--", "münchen") == "xn--mnchen-3ya" diff --git a/tests/stdlib/trationals.nim b/tests/stdlib/trationals.nim index 17238af07..23c15b884 100644 --- a/tests/stdlib/trationals.nim +++ b/tests/stdlib/trationals.nim @@ -9,90 +9,90 @@ var m1 = -1 // 1 tt = 10 // 2 -assert(a == a) -assert( (a-a) == z) -assert( (a+b) == o) -assert( (a/b) == o) -assert( (a*b) == 1 // 4) -assert( (3/a) == 6 // 1) -assert( (a/3) == 1 // 6) -assert(a*b == 1 // 4) -assert(tt*z == z) -assert(10*a == tt) -assert(a*10 == tt) -assert(tt/10 == a) -assert(a-m1 == 3 // 2) -assert(a+m1 == -1 // 2) -assert(m1+tt == 16 // 4) -assert(m1-tt == 6 // -1) +doAssert(a == a) +doAssert( (a-a) == z) +doAssert( (a+b) == o) +doAssert( (a/b) == o) +doAssert( (a*b) == 1 // 4) +doAssert( (3/a) == 6 // 1) +doAssert( (a/3) == 1 // 6) +doAssert(a*b == 1 // 4) +doAssert(tt*z == z) +doAssert(10*a == tt) +doAssert(a*10 == tt) +doAssert(tt/10 == a) +doAssert(a-m1 == 3 // 2) +doAssert(a+m1 == -1 // 2) +doAssert(m1+tt == 16 // 4) +doAssert(m1-tt == 6 // -1) -assert(z < o) -assert(z <= o) -assert(z == z) -assert(cmp(z, o) < 0) -assert(cmp(o, z) > 0) +doAssert(z < o) +doAssert(z <= o) +doAssert(z == z) +doAssert(cmp(z, o) < 0) +doAssert(cmp(o, z) > 0) -assert(o == o) -assert(o >= o) -assert(not(o > o)) -assert(cmp(o, o) == 0) -assert(cmp(z, z) == 0) -assert(hash(o) == hash(o)) +doAssert(o == o) +doAssert(o >= o) +doAssert(not(o > o)) +doAssert(cmp(o, o) == 0) +doAssert(cmp(z, z) == 0) +doAssert(hash(o) == hash(o)) -assert(a == b) -assert(a >= b) -assert(not(b > a)) -assert(cmp(a, b) == 0) -assert(hash(a) == hash(b)) +doAssert(a == b) +doAssert(a >= b) +doAssert(not(b > a)) +doAssert(cmp(a, b) == 0) +doAssert(hash(a) == hash(b)) var x = 1//3 x *= 5//1 -assert(x == 5//3) +doAssert(x == 5//3) x += 2 // 9 -assert(x == 17//9) +doAssert(x == 17//9) x -= 9//18 -assert(x == 25//18) +doAssert(x == 25//18) x /= 1//2 -assert(x == 50//18) +doAssert(x == 50//18) var y = 1//3 y *= 4 -assert(y == 4//3) +doAssert(y == 4//3) y += 5 -assert(y == 19//3) +doAssert(y == 19//3) y -= 2 -assert(y == 13//3) +doAssert(y == 13//3) y /= 9 -assert(y == 13//27) +doAssert(y == 13//27) -assert toRational(5) == 5//1 -assert abs(toFloat(y) - 0.4814814814814815) < 1.0e-7 -assert toInt(z) == 0 +doAssert toRational(5) == 5//1 +doAssert abs(toFloat(y) - 0.4814814814814815) < 1.0e-7 +doAssert toInt(z) == 0 when sizeof(int) == 8: - assert toRational(0.98765432) == 2111111029 // 2137499919 - assert toRational(PI) == 817696623 // 260280919 + doAssert toRational(0.98765432) == 2111111029 // 2137499919 + doAssert toRational(PI) == 817696623 // 260280919 when sizeof(int) == 4: - assert toRational(0.98765432) == 80 // 81 - assert toRational(PI) == 355 // 113 + doAssert toRational(0.98765432) == 80 // 81 + doAssert toRational(PI) == 355 // 113 -assert toRational(0.1) == 1 // 10 -assert toRational(0.9) == 9 // 10 +doAssert toRational(0.1) == 1 // 10 +doAssert toRational(0.9) == 9 // 10 -assert toRational(0.0) == 0 // 1 -assert toRational(-0.25) == 1 // -4 -assert toRational(3.2) == 16 // 5 -assert toRational(0.33) == 33 // 100 -assert toRational(0.22) == 11 // 50 -assert toRational(10.0) == 10 // 1 +doAssert toRational(0.0) == 0 // 1 +doAssert toRational(-0.25) == 1 // -4 +doAssert toRational(3.2) == 16 // 5 +doAssert toRational(0.33) == 33 // 100 +doAssert toRational(0.22) == 11 // 50 +doAssert toRational(10.0) == 10 // 1 -assert (1//1) div (3//10) == 3 -assert (-1//1) div (3//10) == -3 -assert (3//10) mod (1//1) == 3//10 -assert (-3//10) mod (1//1) == -3//10 -assert floorDiv(1//1, 3//10) == 3 -assert floorDiv(-1//1, 3//10) == -4 -assert floorMod(3//10, 1//1) == 3//10 -assert floorMod(-3//10, 1//1) == 7//10 +doAssert (1//1) div (3//10) == 3 +doAssert (-1//1) div (3//10) == -3 +doAssert (3//10) mod (1//1) == 3//10 +doAssert (-3//10) mod (1//1) == -3//10 +doAssert floorDiv(1//1, 3//10) == 3 +doAssert floorDiv(-1//1, 3//10) == -4 +doAssert floorMod(3//10, 1//1) == 3//10 +doAssert floorMod(-3//10, 1//1) == 7//10 diff --git a/tests/stdlib/trst.nim b/tests/stdlib/trst.nim index 6a6e6fdc0..0645e4150 100644 --- a/tests/stdlib/trst.nim +++ b/tests/stdlib/trst.nim @@ -16,7 +16,7 @@ suite "RST include directive": test "Include whole": "other.rst".writeFile("**test1**") let input = ".. include:: other.rst" - assert "test1" == rstTohtml(input, {}, defaultConfig()) + doAssert "test1" == rstTohtml(input, {}, defaultConfig()) removeFile("other.rst") test "Include starting from": @@ -30,7 +30,7 @@ OtherStart .. include:: other.rst :start-after: OtherStart """ - assert "Visible" == rstTohtml(input, {}, defaultConfig()) + doAssert "Visible" == rstTohtml(input, {}, defaultConfig()) removeFile("other.rst") test "Include everything before": @@ -44,7 +44,7 @@ And this should **NOT** be visible in `docs.html` .. include:: other.rst :end-before: OtherEnd """ - assert "Visible" == rstTohtml(input, {}, defaultConfig()) + doAssert "Visible" == rstTohtml(input, {}, defaultConfig()) removeFile("other.rst") @@ -62,7 +62,7 @@ And this should **NOT** be visible in `docs.html` :start-after: OtherStart :end-before: OtherEnd """ - assert "Visible" == rstTohtml(input, {}, defaultConfig()) + doAssert "Visible" == rstTohtml(input, {}, defaultConfig()) removeFile("other.rst") @@ -82,5 +82,5 @@ And this should **NOT** be visible in `docs.html` :start-after: OtherStart :end-before: OtherEnd """ - assert "Visible" == rstTohtml(input, {}, defaultConfig()) + doAssert "Visible" == rstTohtml(input, {}, defaultConfig()) removeFile("other.rst") diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim index 3283af8c6..c3388ab7d 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -22,7 +22,7 @@ suite "YAML syntax highlighting": : value ...""" let output = rstTohtml(input, {}, defaultConfig()) - assert output == """
    %YAML 1.2
    +    doAssert output == """
    %YAML 1.2
     ---
     a string: string
     a list:
    @@ -48,7 +48,7 @@ suite "YAML syntax highlighting":
           |+ # comment after header
          allowed, since more indented than parent"""
         let output = rstToHtml(input, {}, defaultConfig())
    -    assert output == """
    a literal block scalar: |
    +    doAssert output == """
    a literal block scalar: |
       some text
       # not a comment
      # a comment, since less indented
    @@ -74,7 +74,7 @@ suite "YAML syntax highlighting":
         ...
         %TAG ! !foo:"""
         let output = rstToHtml(input, {}, defaultConfig())
    -    assert output == """
    %YAML 1.2
    +    doAssert output == """
    %YAML 1.2
     ---
     %not a directive
     ...
    @@ -95,7 +95,7 @@ suite "YAML syntax highlighting":
           not numbers: [ 42e, 0023, +32.37, 8 ball]
         }"""
         let output = rstToHtml(input, {}, defaultConfig())
    -    assert output == """
    {
    +    doAssert output == """
    {
       "quoted string": 42,
       'single quoted string': false,
       [ list, "with", 'entries' ]: 73.32e-73,
    @@ -112,7 +112,7 @@ suite "YAML syntax highlighting":
         alias: *anchor
         """
         let output = rstToHtml(input, {}, defaultConfig())
    -    assert output == """
    --- !!map
    +    doAssert output == """
    --- !!map
     !!str string: !<tag:yaml.org,2002:int> 42
     ? &anchor !!seq []:
     : !localtag foo
    @@ -132,7 +132,7 @@ suite "YAML syntax highlighting":
           ?not a map key
         """
         let output = rstToHtml(input, {}, defaultConfig())
    -    assert output == """
    ...
    +    doAssert output == """
    ...
      %a string:
       a:string:not:a:map
     ...
    @@ -146,7 +146,7 @@ suite "YAML syntax highlighting":
     
     suite "RST/Markdown general":
       test "RST emphasis":
    -    assert rstToHtml("*Hello* **world**!", {},
    +    doAssert rstToHtml("*Hello* **world**!", {},
           newStringTable(modeStyleInsensitive)) ==
           "Hello world!"
     
    @@ -156,9 +156,9 @@ suite "RST/Markdown general":
           b = rstToHtml("(([Nim](https://nim-lang.org/)))", {roSupportMarkdown}, defaultConfig())
           c = rstToHtml("[[Nim](https://nim-lang.org/)]", {roSupportMarkdown}, defaultConfig())
     
    -    assert a == """(( Nim ))"""
    -    assert b == """((Nim))"""
    -    assert c == """[Nim]"""
    +    doAssert a == """(( Nim ))"""
    +    doAssert b == """((Nim))"""
    +    doAssert c == """[Nim]"""
     
       test "Markdown tables":
         let input1 = """
    @@ -170,7 +170,7 @@ suite "RST/Markdown general":
     |              | F2 without pipe
     not in table"""
         let output1 = rstToHtml(input1, {roSupportMarkdown}, defaultConfig())
    -    assert output1 == """
    +    doAssert output1 == """
    A1 headerA2 | not fooled
    @@ -181,7 +181,7 @@ not in table""" | A1 header | A2 | | --- | --- |""" let output2 = rstToHtml(input2, {roSupportMarkdown}, defaultConfig()) - assert output2 == """
    A1 headerA2 | not fooled
    C1C2 bold
    D1 code |D2
    E1 | text
    + doAssert output2 == """
    A1 headerA2
    A1 headerA2
    """ test "RST tables": @@ -197,10 +197,10 @@ A2 A3 A4 A5 ==== === """ let output1 = rstToLatex(input1, {}) - assert "{|X|X|}" in output1 # 2 columns - assert count(output1, "\\\\") == 4 # 4 rows + doAssert "{|X|X|}" in output1 # 2 columns + doAssert count(output1, "\\\\") == 4 # 4 rows for cell in ["H0", "H1", "A0", "A1", "A2", "A3", "A4", "A5"]: - assert cell in output1 + doAssert cell in output1 let input2 = """ Now test 3 columns / 2 rows, and also borders containing 4 =, 3 =, 1 = signs: @@ -212,10 +212,10 @@ A0 A1 X Ax Y ==== === = """ let output2 = rstToLatex(input2, {}) - assert "{|X|X|X|}" in output2 # 3 columns - assert count(output2, "\\\\") == 2 # 2 rows + doAssert "{|X|X|X|}" in output2 # 3 columns + doAssert count(output2, "\\\\") == 2 # 2 rows for cell in ["H0", "H1", "H", "A0", "A1", "X", "Ax", "Y"]: - assert cell in output2 + doAssert cell in output2 test "RST adornments": @@ -231,7 +231,7 @@ Long chapter name ''''''''''''''''''' """ let output1 = rstToHtml(input1, {roSupportMarkdown}, defaultConfig()) - assert "Long chapter name" in output1 and "" in output1 + doAssert "
    " in output1
     
       test "Markdown code block":
         let input1 = """
    @@ -317,7 +317,7 @@ Test literal block
     let x = 1
     ``` """
         let output1 = rstToHtml(input1, {roSupportMarkdown}, defaultConfig())
    -    assert "" in output1
    -    assert "other line
    " in output1 + doAssert "line block
    " in output1 + doAssert "other line
    " in output1 let output1l = rstToLatex(input1, {}) - assert "line block\\\\" in output1l - assert "other line\\\\" in output1l + doAssert "line block\\\\" in output1l + doAssert "other line\\\\" in output1l test "RST enumerated lists": let input1 = dedent """ @@ -382,8 +382,8 @@ Test1 """ let output1 = rstToHtml(input1, {roSupportMarkdown}, defaultConfig()) for i in 1..5: - assert ($i & ". line" & $i) notin output1 - assert ("
  • line" & $i & " " & $i & "
  • ") in output1 + doAssert ($i & ". line" & $i) notin output1 + doAssert ("
  • line" & $i & " " & $i & "
  • ") in output1 let input2 = dedent """ 3. line3 @@ -404,8 +404,8 @@ Test1 """ let output2 = rstToHtml(input2, {roSupportMarkdown}, defaultConfig()) for i in [3, 4, 5, 7, 8]: - assert ($i & ". line" & $i) notin output2 - assert ("
  • line" & $i & "
  • ") in output2 + doAssert ($i & ". line" & $i) notin output2 + doAssert ("
  • line" & $i & "
  • ") in output2 # check that nested enumerated lists work let input3 = dedent """ @@ -413,9 +413,9 @@ Test1 2. string2 """ let output3 = rstToHtml(input3, {roSupportMarkdown}, defaultConfig()) - assert count(output3, "
      ") == 2 - assert "
    1. string1
    2. " in output3 and "
    3. string2
    4. " in output3 + doAssert count(output3, "
        ") == 2 + doAssert "
      1. string1
      2. " in output3 and "
      3. string2
      4. " in output3 let input4 = dedent """ Check that enumeration specifiers are respected @@ -429,12 +429,12 @@ Test1 e) string6 """ let output4 = rstToHtml(input4, {roSupportMarkdown}, defaultConfig()) - assert count(output4, "
          ") == 4 + doAssert count(output4, "
            ") == 4 for enumerator in [9, 12]: - assert "start=\"$1\"" % [$enumerator] in output4 + doAssert "start=\"$1\"" % [$enumerator] in output4 for enumerator in [2, 5]: # 2=b, 5=e - assert "start=\"$1\"" % [$enumerator] in output4 + doAssert "start=\"$1\"" % [$enumerator] in output4 let input5 = dedent """ Check that auto-numbered enumeration lists work. @@ -449,9 +449,9 @@ Test1 #) string6 """ let output5 = rstToHtml(input5, {roSupportMarkdown}, defaultConfig()) - assert count(output5, "
              ") == 2 - assert count(output5, "
            1. ") == 5 + doAssert count(output5, "
                ") == 2 + doAssert count(output5, "
              1. ") == 5 let input5a = dedent """ Auto-numbered RST list can start with 1 even when Markdown support is on. @@ -461,9 +461,9 @@ Test1 #. string3 """ let output5a = rstToHtml(input5a, {roSupportMarkdown}, defaultConfig()) - assert count(output5a, "
                  ") == 1 - assert count(output5a, "
                1. ") == 3 + doAssert count(output5a, "
                    ") == 1 + doAssert count(output5a, "
                  1. ") == 3 let input6 = dedent """ ... And for alphabetic enumerators too! @@ -473,10 +473,10 @@ Test1 #. string3 """ let output6 = rstToHtml(input6, {roSupportMarkdown}, defaultConfig()) - assert count(output6, "
                      ") == 1 - assert count(output6, "
                    1. ") == 3 - assert "start=\"2\"" in output6 and "class=\"loweralpha simple\"" in output6 + doAssert count(output6, "
                        ") == 1 + doAssert count(output6, "
                      1. ") == 3 + doAssert "start=\"2\"" in output6 and "class=\"loweralpha simple\"" in output6 let input7 = dedent """ ... And for uppercase alphabetic enumerators. @@ -486,10 +486,10 @@ Test1 #. string3 """ let output7 = rstToHtml(input7, {roSupportMarkdown}, defaultConfig()) - assert count(output7, "
                          ") == 1 - assert count(output7, "
                        1. ") == 3 - assert "start=\"3\"" in output7 and "class=\"upperalpha simple\"" in output7 + doAssert count(output7, "
                            ") == 1 + doAssert count(output7, "
                          1. ") == 3 + doAssert "start=\"3\"" in output7 and "class=\"upperalpha simple\"" in output7 test "Markdown enumerated lists": let input1 = dedent """ @@ -505,10 +505,10 @@ Test1 """ let output1 = rstToHtml(input1, {roSupportMarkdown}, defaultConfig()) for i in 1..5: - assert ($i & ". line" & $i) notin output1 - assert ("
                          2. line" & $i & "
                          3. ") in output1 - assert count(output1, "
                              ") == 2 + doAssert ($i & ". line" & $i) notin output1 + doAssert ("
                            1. line" & $i & "
                            2. ") in output1 + doAssert count(output1, "
                                ") == 2 test "RST bullet lists": let input1 = dedent """ @@ -531,9 +531,9 @@ Test1 """ let output1 = rstToHtml(input1, {roSupportMarkdown}, defaultConfig()) for i in 1..5: - assert ("
                              1. line" & $i & " " & $i & "
                              2. ") in output1 - assert count(output1, "
                                  ") == 1 + doAssert ("
                                • line" & $i & " " & $i & "
                                • ") in output1 + doAssert count(output1, "
                                    ") == 1 test "RST admonitions": # check that all admonitions are implemented @@ -552,7 +552,7 @@ Test1 let output0 = rstToHtml(input0, {roSupportMarkdown}, defaultConfig()) for a in ["admonition", "attention", "caution", "danger", "error", "hint", "important", "note", "tip", "warning" ]: - assert "endOf " & a & "" in output0 + doAssert "endOf " & a & "" in output0 # Test that admonition does not swallow up the next paragraph. let input1 = dedent """ @@ -561,9 +561,9 @@ Test1 Test paragraph. """ let output1 = rstToHtml(input1, {roSupportMarkdown}, defaultConfig()) - assert "endOfError" in output1 - assert "

                                    Test paragraph.

                                    " in output1 - assert "class=\"admonition admonition-error\"" in output1 + doAssert "endOfError" in output1 + doAssert "

                                    Test paragraph.

                                    " in output1 + doAssert "class=\"admonition admonition-error\"" in output1 # Test that second line is parsed as continuation of the first line. let input2 = dedent """ @@ -573,16 +573,16 @@ Test1 Test paragraph. """ let output2 = rstToHtml(input2, {roSupportMarkdown}, defaultConfig()) - assert "endOfError Test2p." in output2 - assert "

                                    Test paragraph.

                                    " in output2 - assert "class=\"admonition admonition-error\"" in output2 + doAssert "endOfError Test2p." in output2 + doAssert "

                                    Test paragraph.

                                    " in output2 + doAssert "class=\"admonition admonition-error\"" in output2 let input3 = dedent """ .. note:: endOfNote """ let output3 = rstToHtml(input3, {roSupportMarkdown}, defaultConfig()) - assert "endOfNote" in output3 - assert "class=\"admonition admonition-info\"" in output3 + doAssert "endOfNote" in output3 + doAssert "class=\"admonition admonition-info\"" in output3 suite "RST/Code highlight": test "Basic Python code highlight": diff --git a/tests/stdlib/tsequtils.nim b/tests/stdlib/tsequtils.nim index efcc9f126..94f6c3b08 100644 --- a/tests/stdlib/tsequtils.nim +++ b/tests/stdlib/tsequtils.nim @@ -15,7 +15,7 @@ block: # concat test s2 = @[4, 5] s3 = @[6, 7] total = concat(s1, s2, s3) - assert total == @[1, 2, 3, 4, 5, 6, 7] + doAssert total == @[1, 2, 3, 4, 5, 6, 7] block: # count test let @@ -35,18 +35,18 @@ block: # count test ar3 = count(a2, 'y') ar4 = count(a2, 'x') ar5 = count(a2, 'a') - assert r0 == 0 - assert r1 == 1 - assert r2 == 2 - assert r3 == 0 - assert r4 == 1 - assert r5 == 2 - assert ar0 == 0 - assert ar1 == 1 - assert ar2 == 2 - assert ar3 == 0 - assert ar4 == 1 - assert ar5 == 2 + doAssert r0 == 0 + doAssert r1 == 1 + doAssert r2 == 2 + doAssert r3 == 0 + doAssert r4 == 1 + doAssert r5 == 2 + doAssert ar0 == 0 + doAssert ar1 == 1 + doAssert ar2 == 2 + doAssert ar3 == 0 + doAssert ar4 == 1 + doAssert ar5 == 2 block: # cycle tests let @@ -62,9 +62,9 @@ block: # cycle tests doAssert c.cycle(0) == @[] block: # repeat tests - assert repeat(10, 5) == @[10, 10, 10, 10, 10] - assert repeat(@[1, 2, 3], 2) == @[@[1, 2, 3], @[1, 2, 3]] - assert repeat([1, 2, 3], 2) == @[[1, 2, 3], [1, 2, 3]] + doAssert repeat(10, 5) == @[10, 10, 10, 10, 10] + doAssert repeat(@[1, 2, 3], 2) == @[@[1, 2, 3], @[1, 2, 3]] + doAssert repeat([1, 2, 3], 2) == @[[1, 2, 3], [1, 2, 3]] block: # deduplicates test let @@ -80,14 +80,14 @@ block: # deduplicates test unique6 = deduplicate(dup2, true) unique7 = deduplicate(dup3.sorted, true) unique8 = deduplicate(dup4, true) - assert unique1 == @[1, 3, 4, 2, 8] - assert unique2 == @["a", "c", "d"] - assert unique3 == @[1, 3, 4, 2, 8] - assert unique4 == @["a", "c", "d"] - assert unique5 == @[1, 2, 3, 4, 8] - assert unique6 == @["a", "c", "d"] - assert unique7 == @[1, 2, 3, 4, 8] - assert unique8 == @["a", "c", "d"] + doAssert unique1 == @[1, 3, 4, 2, 8] + doAssert unique2 == @["a", "c", "d"] + doAssert unique3 == @[1, 3, 4, 2, 8] + doAssert unique4 == @["a", "c", "d"] + doAssert unique5 == @[1, 2, 3, 4, 8] + doAssert unique6 == @["a", "c", "d"] + doAssert unique7 == @[1, 2, 3, 4, 8] + doAssert unique8 == @["a", "c", "d"] block: # zip test let @@ -100,29 +100,29 @@ block: # zip test zip1 = zip(short, long) zip2 = zip(short, words) zip3 = zip(ashort, along) - assert zip1 == @[(1, 6), (2, 5), (3, 4)] - assert zip2 == @[(1, "one"), (2, "two"), (3, "three")] - assert zip3 == @[(1, 6), (2, 5), (3, 4)] - assert zip1[2][1] == 4 - assert zip2[2][1] == "three" - assert zip3[2][1] == 4 + doAssert zip1 == @[(1, 6), (2, 5), (3, 4)] + doAssert zip2 == @[(1, "one"), (2, "two"), (3, "three")] + doAssert zip3 == @[(1, 6), (2, 5), (3, 4)] + doAssert zip1[2][1] == 4 + doAssert zip2[2][1] == "three" + doAssert zip3[2][1] == 4 when (NimMajor, NimMinor) <= (1, 0): let # In Nim 1.0.x and older, zip returned a seq of tuple strictly # with fields named "a" and "b". zipAb = zip(ashort, awords) - assert zipAb == @[(a: 1, b: "one"), (2, "two"), (3, "three")] - assert zipAb[2].b == "three" + doAssert zipAb == @[(a: 1, b: "one"), (2, "two"), (3, "three")] + doAssert zipAb[2].b == "three" else: let # As zip returns seq of anonymous tuples, they can be assigned # to any variable that's a sequence of named tuples too. zipXy: seq[tuple[x: int, y: string]] = zip(ashort, awords) zipMn: seq[tuple[m: int, n: string]] = zip(ashort, words) - assert zipXy == @[(x: 1, y: "one"), (2, "two"), (3, "three")] - assert zipMn == @[(m: 1, n: "one"), (2, "two"), (3, "three")] - assert zipXy[2].y == "three" - assert zipMn[2].n == "three" + doAssert zipXy == @[(x: 1, y: "one"), (2, "two"), (3, "three")] + doAssert zipMn == @[(m: 1, n: "one"), (2, "two"), (3, "three")] + doAssert zipXy[2].y == "three" + doAssert zipMn[2].n == "three" block: # distribute tests let numbers = @[1, 2, 3, 4, 5, 6, 7] @@ -156,13 +156,13 @@ block: # map test anumbers = [1, 4, 5, 8, 9, 7, 4] m1 = map(numbers, proc(x: int): int = 2*x) m2 = map(anumbers, proc(x: int): int = 2*x) - assert m1 == @[2, 8, 10, 16, 18, 14, 8] - assert m2 == @[2, 8, 10, 16, 18, 14, 8] + doAssert m1 == @[2, 8, 10, 16, 18, 14, 8] + doAssert m2 == @[2, 8, 10, 16, 18, 14, 8] block: # apply test var a = @["1", "2", "3", "4"] apply(a, proc(x: var string) = x &= "42") - assert a == @["142", "242", "342", "442"] + doAssert a == @["142", "242", "342", "442"] block: # filter proc test let @@ -172,34 +172,34 @@ block: # filter proc test f2 = filter(colors) do (x: string) -> bool: x.len > 5 f3 = filter(acolors, proc(x: string): bool = x.len < 6) f4 = filter(acolors) do (x: string) -> bool: x.len > 5 - assert f1 == @["red", "black"] - assert f2 == @["yellow"] - assert f3 == @["red", "black"] - assert f4 == @["yellow"] + doAssert f1 == @["red", "black"] + doAssert f2 == @["yellow"] + doAssert f3 == @["red", "black"] + doAssert f4 == @["yellow"] block: # filter iterator test let numbers = @[1, 4, 5, 8, 9, 7, 4] let anumbers = [1, 4, 5, 8, 9, 7, 4] - assert toSeq(filter(numbers, proc (x: int): bool = x mod 2 == 0)) == + doAssert toSeq(filter(numbers, proc (x: int): bool = x mod 2 == 0)) == @[4, 8, 4] - assert toSeq(filter(anumbers, proc (x: int): bool = x mod 2 == 0)) == + doAssert toSeq(filter(anumbers, proc (x: int): bool = x mod 2 == 0)) == @[4, 8, 4] block: # keepIf test var floats = @[13.0, 12.5, 5.8, 2.0, 6.1, 9.9, 10.1] keepIf(floats, proc(x: float): bool = x > 10) - assert floats == @[13.0, 12.5, 10.1] + doAssert floats == @[13.0, 12.5, 10.1] block: # delete tests let outcome = @[1, 1, 1, 1, 1, 1, 1, 1] var dest = @[1, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1] dest.delete(3, 8) - assert outcome == dest, """\ + doAssert outcome == dest, """\ Deleting range 3-9 from [1,1,1,2,2,2,2,2,2,1,1,1,1,1] is [1,1,1,1,1,1,1,1]""" var x = @[1, 2, 3] x.delete(100, 100) - assert x == @[1, 2, 3] + doAssert x == @[1, 2, 3] block: # insert tests var dest = @[1, 1, 1, 1, 1, 1, 1, 1] @@ -207,7 +207,7 @@ block: # insert tests src = @[2, 2, 2, 2, 2, 2] outcome = @[1, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1] dest.insert(src, 3) - assert dest == outcome, """\ + doAssert dest == outcome, """\ Inserting [2,2,2,2,2,2] into [1,1,1,1,1,1,1,1] at 3 is [1,1,1,2,2,2,2,2,2,1,1,1,1,1]""" @@ -216,57 +216,57 @@ block: # filterIt test temperatures = @[-272.15, -2.0, 24.5, 44.31, 99.9, -113.44] acceptable = filterIt(temperatures, it < 50 and it > -10) notAcceptable = filterIt(temperatures, it > 50 or it < -10) - assert acceptable == @[-2.0, 24.5, 44.31] - assert notAcceptable == @[-272.15, 99.9, -113.44] + doAssert acceptable == @[-2.0, 24.5, 44.31] + doAssert notAcceptable == @[-272.15, 99.9, -113.44] block: # keepItIf test var candidates = @["foo", "bar", "baz", "foobar"] keepItIf(candidates, it.len == 3 and it[0] == 'b') - assert candidates == @["bar", "baz"] + doAssert candidates == @["bar", "baz"] block: # all let numbers = @[1, 4, 5, 8, 9, 7, 4] anumbers = [1, 4, 5, 8, 9, 7, 4] len0seq: seq[int] = @[] - assert all(numbers, proc (x: int): bool = return x < 10) == true - assert all(numbers, proc (x: int): bool = return x < 9) == false - assert all(len0seq, proc (x: int): bool = return false) == true - assert all(anumbers, proc (x: int): bool = return x < 10) == true - assert all(anumbers, proc (x: int): bool = return x < 9) == false + doAssert all(numbers, proc (x: int): bool = return x < 10) == true + doAssert all(numbers, proc (x: int): bool = return x < 9) == false + doAssert all(len0seq, proc (x: int): bool = return false) == true + doAssert all(anumbers, proc (x: int): bool = return x < 10) == true + doAssert all(anumbers, proc (x: int): bool = return x < 9) == false block: # allIt let numbers = @[1, 4, 5, 8, 9, 7, 4] anumbers = [1, 4, 5, 8, 9, 7, 4] len0seq: seq[int] = @[] - assert allIt(numbers, it < 10) == true - assert allIt(numbers, it < 9) == false - assert allIt(len0seq, false) == true - assert allIt(anumbers, it < 10) == true - assert allIt(anumbers, it < 9) == false + doAssert allIt(numbers, it < 10) == true + doAssert allIt(numbers, it < 9) == false + doAssert allIt(len0seq, false) == true + doAssert allIt(anumbers, it < 10) == true + doAssert allIt(anumbers, it < 9) == false block: # any let numbers = @[1, 4, 5, 8, 9, 7, 4] anumbers = [1, 4, 5, 8, 9, 7, 4] len0seq: seq[int] = @[] - assert any(numbers, proc (x: int): bool = return x > 8) == true - assert any(numbers, proc (x: int): bool = return x > 9) == false - assert any(len0seq, proc (x: int): bool = return true) == false - assert any(anumbers, proc (x: int): bool = return x > 8) == true - assert any(anumbers, proc (x: int): bool = return x > 9) == false + doAssert any(numbers, proc (x: int): bool = return x > 8) == true + doAssert any(numbers, proc (x: int): bool = return x > 9) == false + doAssert any(len0seq, proc (x: int): bool = return true) == false + doAssert any(anumbers, proc (x: int): bool = return x > 8) == true + doAssert any(anumbers, proc (x: int): bool = return x > 9) == false block: # anyIt let numbers = @[1, 4, 5, 8, 9, 7, 4] anumbers = [1, 4, 5, 8, 9, 7, 4] len0seq: seq[int] = @[] - assert anyIt(numbers, it > 8) == true - assert anyIt(numbers, it > 9) == false - assert anyIt(len0seq, true) == false - assert anyIt(anumbers, it > 8) == true - assert anyIt(anumbers, it > 9) == false + doAssert anyIt(numbers, it > 8) == true + doAssert anyIt(numbers, it > 9) == false + doAssert anyIt(len0seq, true) == false + doAssert anyIt(anumbers, it > 8) == true + doAssert anyIt(anumbers, it > 9) == false block: # toSeq test block: @@ -275,7 +275,7 @@ block: # toSeq test oddNumbers = toSeq(filter(numeric) do (x: int) -> bool: if x mod 2 == 1: result = true) - assert oddNumbers == @[1, 3, 5, 7, 9] + doAssert oddNumbers == @[1, 3, 5, 7, 9] block: doAssert [1, 2].toSeq == @[1, 2] @@ -350,10 +350,10 @@ block: # foldl tests multiplication = foldl(numbers, a * b) words = @["nim", "is", "cool"] concatenation = foldl(words, a & b) - assert addition == 25, "Addition is (((5)+9)+11)" - assert subtraction == -15, "Subtraction is (((5)-9)-11)" - assert multiplication == 495, "Multiplication is (((5)*9)*11)" - assert concatenation == "nimiscool" + doAssert addition == 25, "Addition is (((5)+9)+11)" + doAssert subtraction == -15, "Subtraction is (((5)-9)-11)" + doAssert multiplication == 495, "Multiplication is (((5)*9)*11)" + doAssert concatenation == "nimiscool" block: # foldr tests let @@ -363,10 +363,10 @@ block: # foldr tests multiplication = foldr(numbers, a * b) words = @["nim", "is", "cool"] concatenation = foldr(words, a & b) - assert addition == 25, "Addition is (5+(9+(11)))" - assert subtraction == 7, "Subtraction is (5-(9-(11)))" - assert multiplication == 495, "Multiplication is (5*(9*(11)))" - assert concatenation == "nimiscool" + doAssert addition == 25, "Addition is (5+(9+(11)))" + doAssert subtraction == 7, "Subtraction is (5-(9-(11)))" + doAssert multiplication == 495, "Multiplication is (5*(9*(11)))" + doAssert concatenation == "nimiscool" doAssert toSeq(1..3).foldr(a + b) == 6 # issue #14404 block: # mapIt + applyIt test @@ -376,8 +376,8 @@ block: # mapIt + applyIt test strings = nums.identity.mapIt($(4 * it)) doAssert counter == 1 nums.applyIt(it * 3) - assert nums[0] + nums[3] == 15 - assert strings[2] == "12" + doAssert nums[0] + nums[3] == 15 + doAssert strings[2] == "12" block: # newSeqWith tests var seq2D = newSeqWith(4, newSeq[bool](2)) diff --git a/tests/stdlib/tsharedtable.nim b/tests/stdlib/tsharedtable.nim index ce6aa96df..0a8f7bcc0 100644 --- a/tests/stdlib/tsharedtable.nim +++ b/tests/stdlib/tsharedtable.nim @@ -11,9 +11,9 @@ block: init(table) table[1] = 10 - assert table.mget(1) == 10 - assert table.mgetOrPut(3, 7) == 7 - assert table.mgetOrPut(3, 99) == 7 + doAssert table.mget(1) == 10 + doAssert table.mgetOrPut(3, 7) == 7 + doAssert table.mgetOrPut(3, 99) == 7 deinitSharedTable(table) import sequtils, algorithm diff --git a/tests/stdlib/tstrformat.nim b/tests/stdlib/tstrformat.nim index d483093a7..be5f1f304 100644 --- a/tests/stdlib/tstrformat.nim +++ b/tests/stdlib/tstrformat.nim @@ -202,10 +202,10 @@ doAssert fmt"{nat=:3X}" == "nat= 40" proc my_proc = const value = "value" const a = &"{value}" - assert a == value + doAssert a == value const b = &"{value=}" - assert b == "value=" & value + doAssert b == "value=" & value my_proc() diff --git a/tests/stdlib/tstrtabs.nim b/tests/stdlib/tstrtabs.nim index d4344f95f..f629c183c 100644 --- a/tests/stdlib/tstrtabs.nim +++ b/tests/stdlib/tstrtabs.nim @@ -105,12 +105,12 @@ writeLine(stdout, "length of table ", $tab.len) block: var x = {"k": "v", "11": "22", "565": "67"}.newStringTable - assert x["k"] == "v" - assert x["11"] == "22" - assert x["565"] == "67" + doAssert x["k"] == "v" + doAssert x["11"] == "22" + doAssert x["565"] == "67" x["11"] = "23" - assert x["11"] == "23" + doAssert x["11"] == "23" x.clear(modeCaseInsensitive) x["11"] = "22" - assert x["11"] == "22" + doAssert x["11"] == "22" diff --git a/tests/stdlib/tstrutils.nim b/tests/stdlib/tstrutils.nim index 8d6fe75ae..a6248d1e3 100644 --- a/tests/stdlib/tstrutils.nim +++ b/tests/stdlib/tstrutils.nim @@ -58,9 +58,9 @@ template main() = block: # splitLines let fixture = "a\nb\rc\r\nd" - assert len(fixture.splitLines) == 4 - assert splitLines(fixture) == @["a", "b", "c", "d"] - assert splitLines(fixture, keepEol=true) == @["a\n", "b\r", "c\r\n", "d"] + doAssert len(fixture.splitLines) == 4 + doAssert splitLines(fixture) == @["a", "b", "c", "d"] + doAssert splitLines(fixture, keepEol=true) == @["a\n", "b\r", "c\r\n", "d"] block: # rsplit doAssert rsplit("foo bar", seps = Whitespace) == @["foo", "bar"] @@ -83,207 +83,207 @@ template main() = block: # removeSuffix var s = "hello\n\r" s.removeSuffix - assert s == "hello" + doAssert s == "hello" s.removeSuffix - assert s == "hello" + doAssert s == "hello" s = "hello\n\n" s.removeSuffix - assert s == "hello" + doAssert s == "hello" s = "hello\r" s.removeSuffix - assert s == "hello" + doAssert s == "hello" s = "hello \n there" s.removeSuffix - assert s == "hello \n there" + doAssert s == "hello \n there" s = "hello" s.removeSuffix("llo") - assert s == "he" + doAssert s == "he" s.removeSuffix('e') - assert s == "h" + doAssert s == "h" s = "hellos" s.removeSuffix({'s','z'}) - assert s == "hello" + doAssert s == "hello" s.removeSuffix({'l','o'}) - assert s == "he" + doAssert s == "he" s = "aeiou" s.removeSuffix("") - assert s == "aeiou" + doAssert s == "aeiou" s = "" s.removeSuffix("") - assert s == "" + doAssert s == "" s = " " s.removeSuffix - assert s == " " + doAssert s == " " s = " " s.removeSuffix("") - assert s == " " + doAssert s == " " s = " " s.removeSuffix(" ") - assert s == " " + doAssert s == " " s = " " s.removeSuffix(' ') - assert s == "" + doAssert s == "" # Contrary to Chomp in other languages # empty string does not change behaviour s = "hello\r\n\r\n" s.removeSuffix("") - assert s == "hello\r\n\r\n" + doAssert s == "hello\r\n\r\n" block: # removePrefix var s = "\n\rhello" s.removePrefix - assert s == "hello" + doAssert s == "hello" s.removePrefix - assert s == "hello" + doAssert s == "hello" s = "\n\nhello" s.removePrefix - assert s == "hello" + doAssert s == "hello" s = "\rhello" s.removePrefix - assert s == "hello" + doAssert s == "hello" s = "hello \n there" s.removePrefix - assert s == "hello \n there" + doAssert s == "hello \n there" s = "hello" s.removePrefix("hel") - assert s == "lo" + doAssert s == "lo" s.removePrefix('l') - assert s == "o" + doAssert s == "o" s = "hellos" s.removePrefix({'h','e'}) - assert s == "llos" + doAssert s == "llos" s.removePrefix({'l','o'}) - assert s == "s" + doAssert s == "s" s = "aeiou" s.removePrefix("") - assert s == "aeiou" + doAssert s == "aeiou" s = "" s.removePrefix("") - assert s == "" + doAssert s == "" s = " " s.removePrefix - assert s == " " + doAssert s == " " s = " " s.removePrefix("") - assert s == " " + doAssert s == " " s = " " s.removePrefix(" ") - assert s == " " + doAssert s == " " s = " " s.removePrefix(' ') - assert s == "" + doAssert s == "" # Contrary to Chomp in other languages # empty string does not change behaviour s = "\r\n\r\nhello" s.removePrefix("") - assert s == "\r\n\r\nhello" + doAssert s == "\r\n\r\nhello" block: # delete var s = "0123456789ABCDEFGH" delete(s, 4, 5) - assert s == "01236789ABCDEFGH" + doAssert s == "01236789ABCDEFGH" delete(s, s.len-1, s.len-1) - assert s == "01236789ABCDEFG" + doAssert s == "01236789ABCDEFG" delete(s, 0, 0) - assert s == "1236789ABCDEFG" + doAssert s == "1236789ABCDEFG" block: # find - assert "0123456789ABCDEFGH".find('A') == 10 - assert "0123456789ABCDEFGH".find('A', 5) == 10 - assert "0123456789ABCDEFGH".find('A', 5, 10) == 10 - assert "0123456789ABCDEFGH".find('A', 5, 9) == -1 - assert "0123456789ABCDEFGH".find("A") == 10 - assert "0123456789ABCDEFGH".find("A", 5) == 10 - assert "0123456789ABCDEFGH".find("A", 5, 10) == 10 - assert "0123456789ABCDEFGH".find("A", 5, 9) == -1 - assert "0123456789ABCDEFGH".find({'A'..'C'}) == 10 - assert "0123456789ABCDEFGH".find({'A'..'C'}, 5) == 10 - assert "0123456789ABCDEFGH".find({'A'..'C'}, 5, 10) == 10 - assert "0123456789ABCDEFGH".find({'A'..'C'}, 5, 9) == -1 + doAssert "0123456789ABCDEFGH".find('A') == 10 + doAssert "0123456789ABCDEFGH".find('A', 5) == 10 + doAssert "0123456789ABCDEFGH".find('A', 5, 10) == 10 + doAssert "0123456789ABCDEFGH".find('A', 5, 9) == -1 + doAssert "0123456789ABCDEFGH".find("A") == 10 + doAssert "0123456789ABCDEFGH".find("A", 5) == 10 + doAssert "0123456789ABCDEFGH".find("A", 5, 10) == 10 + doAssert "0123456789ABCDEFGH".find("A", 5, 9) == -1 + doAssert "0123456789ABCDEFGH".find({'A'..'C'}) == 10 + doAssert "0123456789ABCDEFGH".find({'A'..'C'}, 5) == 10 + doAssert "0123456789ABCDEFGH".find({'A'..'C'}, 5, 10) == 10 + doAssert "0123456789ABCDEFGH".find({'A'..'C'}, 5, 9) == -1 block: # rfind - assert "0123456789ABCDEFGAH".rfind('A') == 17 - assert "0123456789ABCDEFGAH".rfind('A', last=13) == 10 - assert "0123456789ABCDEFGAH".rfind('H', last=13) == -1 - assert "0123456789ABCDEFGAH".rfind("A") == 17 - assert "0123456789ABCDEFGAH".rfind("A", last=13) == 10 - assert "0123456789ABCDEFGAH".rfind("H", last=13) == -1 - assert "0123456789ABCDEFGAH".rfind({'A'..'C'}) == 17 - assert "0123456789ABCDEFGAH".rfind({'A'..'C'}, last=13) == 12 - assert "0123456789ABCDEFGAH".rfind({'G'..'H'}, last=13) == -1 - assert "0123456789ABCDEFGAH".rfind('A', start=18) == -1 - assert "0123456789ABCDEFGAH".rfind('A', start=11, last=17) == 17 - assert "0123456789ABCDEFGAH".rfind("0", start=0) == 0 - assert "0123456789ABCDEFGAH".rfind("0", start=1) == -1 - assert "0123456789ABCDEFGAH".rfind("H", start=11) == 18 - assert "0123456789ABCDEFGAH".rfind({'0'..'9'}, start=5) == 9 - assert "0123456789ABCDEFGAH".rfind({'0'..'9'}, start=10) == -1 + doAssert "0123456789ABCDEFGAH".rfind('A') == 17 + doAssert "0123456789ABCDEFGAH".rfind('A', last=13) == 10 + doAssert "0123456789ABCDEFGAH".rfind('H', last=13) == -1 + doAssert "0123456789ABCDEFGAH".rfind("A") == 17 + doAssert "0123456789ABCDEFGAH".rfind("A", last=13) == 10 + doAssert "0123456789ABCDEFGAH".rfind("H", last=13) == -1 + doAssert "0123456789ABCDEFGAH".rfind({'A'..'C'}) == 17 + doAssert "0123456789ABCDEFGAH".rfind({'A'..'C'}, last=13) == 12 + doAssert "0123456789ABCDEFGAH".rfind({'G'..'H'}, last=13) == -1 + doAssert "0123456789ABCDEFGAH".rfind('A', start=18) == -1 + doAssert "0123456789ABCDEFGAH".rfind('A', start=11, last=17) == 17 + doAssert "0123456789ABCDEFGAH".rfind("0", start=0) == 0 + doAssert "0123456789ABCDEFGAH".rfind("0", start=1) == -1 + doAssert "0123456789ABCDEFGAH".rfind("H", start=11) == 18 + doAssert "0123456789ABCDEFGAH".rfind({'0'..'9'}, start=5) == 9 + doAssert "0123456789ABCDEFGAH".rfind({'0'..'9'}, start=10) == -1 - assert "/1/2/3".rfind('/') == 4 - assert "/1/2/3".rfind('/', last=1) == 0 - assert "/1/2/3".rfind('0') == -1 + doAssert "/1/2/3".rfind('/') == 4 + doAssert "/1/2/3".rfind('/', last=1) == 0 + doAssert "/1/2/3".rfind('0') == -1 block: # trimZeros var x = "1200" x.trimZeros() - assert x == "1200" + doAssert x == "1200" x = "120.0" x.trimZeros() - assert x == "120" + doAssert x == "120" x = "0." x.trimZeros() - assert x == "0" + doAssert x == "0" x = "1.0e2" x.trimZeros() - assert x == "1e2" + doAssert x == "1e2" x = "78.90" x.trimZeros() - assert x == "78.9" + doAssert x == "78.9" x = "1.23e4" x.trimZeros() - assert x == "1.23e4" + doAssert x == "1.23e4" x = "1.01" x.trimZeros() - assert x == "1.01" + doAssert x == "1.01" x = "1.1001" x.trimZeros() - assert x == "1.1001" + doAssert x == "1.1001" x = "0.0" x.trimZeros() - assert x == "0" + doAssert x == "0" x = "0.01" x.trimZeros() - assert x == "0.01" + doAssert x == "0.01" x = "1e0" x.trimZeros() - assert x == "1e0" + doAssert x == "1e0" block: # countLines - proc assertCountLines(s: string) = assert s.countLines == s.splitLines.len + proc assertCountLines(s: string) = doAssert s.countLines == s.splitLines.len assertCountLines("") assertCountLines("\n") assertCountLines("\n\n") @@ -295,36 +295,36 @@ template main() = block: # parseBinInt, parseHexInt, parseOctInt # binary - assert "0b1111".parseBinInt == 15 - assert "0B1111".parseBinInt == 15 - assert "1111".parseBinInt == 15 - assert "1110".parseBinInt == 14 - assert "1_1_1_1".parseBinInt == 15 - assert "0b1_1_1_1".parseBinInt == 15 + doAssert "0b1111".parseBinInt == 15 + doAssert "0B1111".parseBinInt == 15 + doAssert "1111".parseBinInt == 15 + doAssert "1110".parseBinInt == 14 + doAssert "1_1_1_1".parseBinInt == 15 + doAssert "0b1_1_1_1".parseBinInt == 15 rejectParse "".parseBinInt rejectParse "_".parseBinInt rejectParse "0b".parseBinInt rejectParse "0b1234".parseBinInt # hex - assert "0x72".parseHexInt == 114 - assert "0X72".parseHexInt == 114 - assert "#72".parseHexInt == 114 - assert "72".parseHexInt == 114 - assert "FF".parseHexInt == 255 - assert "ff".parseHexInt == 255 - assert "fF".parseHexInt == 255 - assert "0x7_2".parseHexInt == 114 + doAssert "0x72".parseHexInt == 114 + doAssert "0X72".parseHexInt == 114 + doAssert "#72".parseHexInt == 114 + doAssert "72".parseHexInt == 114 + doAssert "FF".parseHexInt == 255 + doAssert "ff".parseHexInt == 255 + doAssert "fF".parseHexInt == 255 + doAssert "0x7_2".parseHexInt == 114 rejectParse "".parseHexInt rejectParse "_".parseHexInt rejectParse "0x".parseHexInt rejectParse "0xFFG".parseHexInt rejectParse "reject".parseHexInt # octal - assert "0o17".parseOctInt == 15 - assert "0O17".parseOctInt == 15 - assert "17".parseOctInt == 15 - assert "10".parseOctInt == 8 - assert "0o1_0_0".parseOctInt == 64 + doAssert "0o17".parseOctInt == 15 + doAssert "0O17".parseOctInt == 15 + doAssert "17".parseOctInt == 15 + doAssert "10".parseOctInt == 8 + doAssert "0o1_0_0".parseOctInt == 64 rejectParse "".parseOctInt rejectParse "_".parseOctInt rejectParse "0o".parseOctInt @@ -333,53 +333,53 @@ template main() = rejectParse "reject".parseOctInt block: # parseHexStr - assert "".parseHexStr == "" - assert "00Ff80".parseHexStr == "\0\xFF\x80" + doAssert "".parseHexStr == "" + doAssert "00Ff80".parseHexStr == "\0\xFF\x80" try: discard "00Ff8".parseHexStr - assert false, "Should raise ValueError" + doAssert false, "Should raise ValueError" except ValueError: discard try: discard "0k".parseHexStr - assert false, "Should raise ValueError" + doAssert false, "Should raise ValueError" except ValueError: discard - assert "".toHex == "" - assert "\x00\xFF\x80".toHex == "00FF80" - assert "0123456789abcdef".parseHexStr.toHex == "0123456789ABCDEF" + doAssert "".toHex == "" + doAssert "\x00\xFF\x80".toHex == "00FF80" + doAssert "0123456789abcdef".parseHexStr.toHex == "0123456789ABCDEF" block: # toHex - assert(toHex(100i16, 32) == "00000000000000000000000000000064") - assert(toHex(-100i16, 32) == "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9C") + doAssert(toHex(100i16, 32) == "00000000000000000000000000000064") + doAssert(toHex(-100i16, 32) == "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFF9C") when not defined js: - assert(toHex(high(uint64)) == "FFFFFFFFFFFFFFFF") - assert(toHex(high(uint64), 16) == "FFFFFFFFFFFFFFFF") - assert(toHex(high(uint64), 32) == "0000000000000000FFFFFFFFFFFFFFFF") + doAssert(toHex(high(uint64)) == "FFFFFFFFFFFFFFFF") + doAssert(toHex(high(uint64), 16) == "FFFFFFFFFFFFFFFF") + doAssert(toHex(high(uint64), 32) == "0000000000000000FFFFFFFFFFFFFFFF") block: # insertSep - assert(insertSep($1000_000) == "1_000_000") - assert(insertSep($232) == "232") - assert(insertSep($12345, ',') == "12,345") - assert(insertSep($0) == "0") + doAssert(insertSep($1000_000) == "1_000_000") + doAssert(insertSep($232) == "232") + doAssert(insertSep($12345, ',') == "12,345") + doAssert(insertSep($0) == "0") block: # repeat, spaces - assert(' '.repeat(8) == " ") - assert(" ".repeat(8) == " ") - assert(spaces(8) == " ") + doAssert(' '.repeat(8) == " ") + doAssert(" ".repeat(8) == " ") + doAssert(spaces(8) == " ") - assert(' '.repeat(0) == "") - assert(" ".repeat(0) == "") - assert(spaces(0) == "") + doAssert(' '.repeat(0) == "") + doAssert(" ".repeat(0) == "") + doAssert(spaces(0) == "") block: # toBin, toOct block:# bug #11369 var num: int64 = -1 when not defined js: - assert num.toBin(64) == "1111111111111111111111111111111111111111111111111111111111111111" - assert num.toOct(24) == "001777777777777777777777" + doAssert num.toBin(64) == "1111111111111111111111111111111111111111111111111111111111111111" + doAssert num.toOct(24) == "001777777777777777777777" block: # replace doAssert "oo".replace("", "abc") == "oo" @@ -499,26 +499,26 @@ template main() = doAssert parseEnum("invalid enum value", enC) == enC block: # indentation - assert 0 == indentation """ + doAssert 0 == indentation """ hey low there """ - assert 2 == indentation """ + doAssert 2 == indentation """ hey low there """ - assert 2 == indentation """ hey + doAssert 2 == indentation """ hey low there """ - assert 2 == indentation """ hey + doAssert 2 == indentation """ hey low there""" - assert 0 == indentation "" - assert 0 == indentation " \n \n" - assert 0 == indentation " " + doAssert 0 == indentation "" + doAssert 0 == indentation " \n \n" + doAssert 0 == indentation " " block: # indent doAssert " foo\n bar".indent(4, "Q") == "QQQQ foo\nQQQQ bar" diff --git a/tests/stdlib/tsugar.nim b/tests/stdlib/tsugar.nim index cca1fe75a..2f79a9174 100644 --- a/tests/stdlib/tsugar.nim +++ b/tests/stdlib/tsugar.nim @@ -47,15 +47,15 @@ doAssert b[1] == 1 import sets, tables let data = @["bird", "word"] # if this gets stuck in your head, its not my fault -assert collect(newSeq, for (i, d) in data.pairs: (if i mod 2 == 0: d)) == @["bird"] -assert collect(initTable(2), for (i, d) in data.pairs: {i: d}) == {0: "bird", +doAssert collect(newSeq, for (i, d) in data.pairs: (if i mod 2 == 0: d)) == @["bird"] +doAssert collect(initTable(2), for (i, d) in data.pairs: {i: d}) == {0: "bird", 1: "word"}.toTable -assert initHashSet.collect(for d in data.items: {d}) == data.toHashSet +doAssert initHashSet.collect(for d in data.items: {d}) == data.toHashSet let x = collect(newSeqOfCap(4)): for (i, d) in data.pairs: if i mod 2 == 0: d -assert x == @["bird"] +doAssert x == @["bird"] # bug #12874 @@ -69,20 +69,20 @@ let bug1 = collect( d & d ) ) -assert bug1 == @["bird", "wordword"] +doAssert bug1 == @["bird", "wordword"] import strutils let y = collect(newSeq): for (i, d) in data.pairs: try: parseInt(d) except: 0 -assert y == @[0, 0] +doAssert y == @[0, 0] let z = collect(newSeq): for (i, d) in data.pairs: case d of "bird": "word" else: d -assert z == @["word", "word"] +doAssert z == @["word", "word"] proc tforum = let ans = collect(newSeq): @@ -97,12 +97,12 @@ block: for d in data.items: when d is int: "word" else: d - assert x == @["bird", "word"] -assert collect(for (i, d) in pairs(data): (i, d)) == @[(0, "bird"), (1, "word")] -assert collect(for d in data.items: (try: parseInt(d) except: 0)) == @[0, 0] -assert collect(for (i, d) in pairs(data): {i: d}) == {1: "word", + doAssert x == @["bird", "word"] +doAssert collect(for (i, d) in pairs(data): (i, d)) == @[(0, "bird"), (1, "word")] +doAssert collect(for d in data.items: (try: parseInt(d) except: 0)) == @[0, 0] +doAssert collect(for (i, d) in pairs(data): {i: d}) == {1: "word", 0: "bird"}.toTable -assert collect(for d in data.items: {d}) == data.toHashSet +doAssert collect(for d in data.items: {d}) == data.toHashSet # bug #14332 template foo = diff --git a/tests/stdlib/tsums.nim b/tests/stdlib/tsums.nim index 979ffc391..4c29d3e10 100644 --- a/tests/stdlib/tsums.nim +++ b/tests/stdlib/tsums.nim @@ -5,18 +5,18 @@ var epsilon = 1.0 while 1.0 + epsilon != 1.0: epsilon /= 2.0 let data = @[1.0, epsilon, -epsilon] -assert sumKbn(data) == 1.0 -# assert sumPairs(data) != 1.0 # known to fail in 64 bits -assert (1.0 + epsilon) - epsilon != 1.0 +doAssert sumKbn(data) == 1.0 +# doAssert sumPairs(data) != 1.0 # known to fail in 64 bits +doAssert (1.0 + epsilon) - epsilon != 1.0 var tc1: seq[float] for n in 1 .. 1000: tc1.add 1.0 / n.float -assert sumKbn(tc1) == 7.485470860550345 -assert sumPairs(tc1) == 7.485470860550345 +doAssert sumKbn(tc1) == 7.485470860550345 +doAssert sumPairs(tc1) == 7.485470860550345 var tc2: seq[float] for n in 1 .. 1000: tc2.add pow(-1.0, n.float) / n.float -assert sumKbn(tc2) == -0.6926474305598203 -assert sumPairs(tc2) == -0.6926474305598204 +doAssert sumKbn(tc2) == -0.6926474305598203 +doAssert sumPairs(tc2) == -0.6926474305598204 diff --git a/tests/stdlib/ttables.nim b/tests/stdlib/ttables.nim index 656d7be6b..c1ae89b32 100644 --- a/tests/stdlib/ttables.nim +++ b/tests/stdlib/ttables.nim @@ -59,8 +59,8 @@ block: # Deletion from OrderedTable should account for collision groups. See iss }.toOrderedTable() t.del(key1) - assert(t.len == 1) - assert(key2 in t) + doAssert(t.len == 1) + doAssert(key2 in t) var t1 = initCountTable[string]() @@ -72,9 +72,9 @@ t2.inc("foo", 4) t2.inc("bar") t2.inc("baz", 11) merge(t1, t2) -assert(t1["foo"] == 5) -assert(t1["bar"] == 3) -assert(t1["baz"] == 14) +doAssert(t1["foo"] == 5) +doAssert(t1["bar"] == 3) +doAssert(t1["baz"] == 14) let t1r = newCountTable[string]() @@ -86,9 +86,9 @@ t2r.inc("foo", 4) t2r.inc("bar") t2r.inc("baz", 11) merge(t1r, t2r) -assert(t1r["foo"] == 5) -assert(t1r["bar"] == 3) -assert(t1r["baz"] == 14) +doAssert(t1r["foo"] == 5) +doAssert(t1r["bar"] == 3) +doAssert(t1r["baz"] == 14) var t1l = initCountTable[string]() @@ -127,28 +127,28 @@ block: #5482 var b = newOrderedTable[string, string](initialSize = 2) b["wrong?"] = "foo" b["wrong?"] = "foo2" - assert a == b + doAssert a == b block: #5482 var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable() var b = newOrderedTable[string, string](initialSize = 2) b["wrong?"] = "foo" b["wrong?"] = "foo2" - assert a == b + doAssert a == b block: #5487 var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable() var b = newOrderedTable[string, string]() # notice, default size! b["wrong?"] = "foo" b["wrong?"] = "foo2" - assert a == b + doAssert a == b block: #5487 var a = [("wrong?", "foo"), ("wrong?", "foo2")].newOrderedTable() var b = newOrderedTable[string, string]() # notice, default size! b["wrong?"] = "foo" b["wrong?"] = "foo2" - assert a == b + doAssert a == b block: var a = {"wrong?": "foo", "wrong?": "foo2"}.newOrderedTable() @@ -156,22 +156,22 @@ block: var c = newOrderedTable[string, string]() # notice, default size! c["wrong?"] = "foo" c["wrong?"] = "foo2" - assert a == b - assert a == c + doAssert a == b + doAssert a == c block: #6250 let a = {3: 1}.toOrderedTable b = {3: 2}.toOrderedTable - assert((a == b) == false) - assert((b == a) == false) + doAssert((a == b) == false) + doAssert((b == a) == false) block: #6250 let a = {3: 2}.toOrderedTable b = {3: 2}.toOrderedTable - assert((a == b) == true) - assert((b == a) == true) + doAssert((a == b) == true) + doAssert((b == a) == true) block: # CountTable.smallest let t = toCountTable([0, 0, 5, 5, 5]) diff --git a/tests/stdlib/ttypeinfo.nim b/tests/stdlib/ttypeinfo.nim index fc0bd3e53..61c661a58 100644 --- a/tests/stdlib/ttypeinfo.nim +++ b/tests/stdlib/ttypeinfo.nim @@ -17,16 +17,16 @@ var test = @[0,1,2,3,4] var x = toAny(test) var y = 78 x[4] = toAny(y) -assert x[2].getInt == 2 +doAssert x[2].getInt == 2 var test2: tuple[name: string, s: int] = ("test", 56) var x2 = toAny(test2) var i = 0 for n, a in fields(x2): case i - of 0: assert n == "Field0" and $a.kind == "akString" - of 1: assert n == "Field1" and $a.kind == "akInt" - else: assert false + of 0: doAssert n == "Field0" and $a.kind == "akString" + of 1: doAssert n == "Field1" and $a.kind == "akInt" + else: doAssert false inc i var test3: TestObj @@ -36,17 +36,17 @@ var x3 = toAny(test3) i = 0 for n, a in fields(x3): case i - of 0: assert n == "test" and $a.kind == "akInt" - of 1: assert n == "asd" and $a.kind == "akInt" - of 2: assert n == "test2" and $a.kind == "akEnum" - else: assert false + of 0: doAssert n == "test" and $a.kind == "akInt" + of 1: doAssert n == "asd" and $a.kind == "akInt" + of 2: doAssert n == "test2" and $a.kind == "akEnum" + else: doAssert false inc i var test4: ref string new(test4) test4[] = "test" var x4 = toAny(test4) -assert($x4[].kind() == "akString") +doAssert($x4[].kind() == "akString") block: # gimme a new scope dammit diff --git a/tests/stdlib/tunittest.nim b/tests/stdlib/tunittest.nim index 4b82df67b..505fb41a3 100644 --- a/tests/stdlib/tunittest.nim +++ b/tests/stdlib/tunittest.nim @@ -56,7 +56,7 @@ proc defectiveRobot() = of 1: raise newException(OSError, "CANNOT COMPUTE!") of 2: discard parseInt("Hello World!") of 3: raise newException(IOError, "I can't do that Dave.") - else: assert 2 + 2 == 5 + else: doAssert 2 + 2 == 5 test "unittest expect": expect IOError, OSError, ValueError, AssertionDefect: defectiveRobot() diff --git a/tests/stdlib/txmltree.nim b/tests/stdlib/txmltree.nim index 034435dd7..d2f713269 100644 --- a/tests/stdlib/txmltree.nim +++ b/tests/stdlib/txmltree.nim @@ -6,15 +6,15 @@ block: x: XmlNode x = <>a(href = "http://nim-lang.org", newText("Nim rules.")) - assert $x == """Nim rules.""" + doAssert $x == """Nim rules.""" x = <>outer(<>inner()) - assert $x == """ + doAssert $x == """ """ x = <>outer(<>middle(<>inner1(), <>inner2(), <>inner3(), <>inner4())) - assert $x == """ + doAssert $x == """ @@ -24,7 +24,7 @@ block: """ x = <>l0(<>l1(<>l2(<>l3(<>l4())))) - assert $x == """ + doAssert $x == """ @@ -35,14 +35,14 @@ block: """ x = <>l0(<>l1p1(), <>l1p2(), <>l1p3()) - assert $x == """ + doAssert $x == """ """ x = <>l0(<>l1(<>l2p1(), <>l2p2())) - assert $x == """ + doAssert $x == """ @@ -50,7 +50,7 @@ block: """ x = <>l0(<>l1(<>l2_1(), <>l2_2(<>l3_1(), <>l3_2(), <>l3_3(<>l4_1(), <>l4_2(), <>l4_3())), <>l2_3(), <>l2_4())) - assert $x == """ + doAssert $x == """ @@ -72,7 +72,7 @@ block: middle = newXmlTree("middle", [innermost]) innermost.add newText("innermost text") x = newXmlTree("outer", [middle]) - assert $x == """ + doAssert $x == """ innermost text @@ -82,4 +82,4 @@ block: x.add newText("my text") x.add newElement("sonTag") x.add newEntity("my entity") - assert $x == "my text&my entity;" + doAssert $x == "my text&my entity;" diff --git a/tests/types/tisop.nim b/tests/types/tisop.nim index ad5928016..5f9cba0d8 100644 --- a/tests/types/tisop.nim +++ b/tests/types/tisop.nim @@ -27,22 +27,22 @@ macro m(t: typedesc): typedesc = result = int var f: TFoo[int, int] -static: assert(f.y.type.name == "string") +static: doAssert(f.y.type.name == "string") when compiles(f.z): {.error: "Foo should not have a `z` field".} proc p(a, b: auto) = when a.type is int: - static: assert false + static: doAssert false var f: TFoo[m(a.type), b.type] static: - assert f.x.type.name == "int" + doAssert f.x.type.name == "int" echo f.y.type.name - assert f.y.type.name == "float" + doAssert f.y.type.name == "float" echo f.z.type.name - assert f.z.type.name == "float" + doAssert f.z.type.name == "float" p(A, f) diff --git a/tests/vm/tableinstatic.nim b/tests/vm/tableinstatic.nim index 4080a5286..934c3a8dd 100644 --- a/tests/vm/tableinstatic.nim +++ b/tests/vm/tableinstatic.nim @@ -35,4 +35,4 @@ static: otherTable["hallo"] = "123" otherTable["welt"] = "456" - assert otherTable == {"hallo": "123", "welt": "456"}.newTable + doAssert otherTable == {"hallo": "123", "welt": "456"}.newTable diff --git a/tests/vm/tissues.nim b/tests/vm/tissues.nim index 063559d2e..1cf3afc00 100644 --- a/tests/vm/tissues.nim +++ b/tests/vm/tissues.nim @@ -25,4 +25,4 @@ block t4952: static: let tree = newTree(nnkExprColonExpr) let t = (n: tree) - assert: t.n.kind == tree.kind + doAssert: t.n.kind == tree.kind diff --git a/tests/vm/toverflowopcaddimmint.nim b/tests/vm/toverflowopcaddimmint.nim index c36b9ed9b..4ff614e5b 100644 --- a/tests/vm/toverflowopcaddimmint.nim +++ b/tests/vm/toverflowopcaddimmint.nim @@ -7,5 +7,5 @@ static: var x = int64.high discard x + 1 - assert false + doAssert false p() diff --git a/tests/vm/toverflowopcaddint.nim b/tests/vm/toverflowopcaddint.nim index 6d96afc78..d494245b1 100644 --- a/tests/vm/toverflowopcaddint.nim +++ b/tests/vm/toverflowopcaddint.nim @@ -8,5 +8,5 @@ static: x = int64.high y = 1 discard x + y - assert false + doAssert false p() diff --git a/tests/vm/toverflowopcmulint.nim b/tests/vm/toverflowopcmulint.nim index 5607c59a7..936eea6c2 100644 --- a/tests/vm/toverflowopcmulint.nim +++ b/tests/vm/toverflowopcmulint.nim @@ -7,5 +7,5 @@ static: var x = 1'i64 shl 62 discard x * 2 - assert false + doAssert false p() diff --git a/tests/vm/toverflowopcsubimmint.nim b/tests/vm/toverflowopcsubimmint.nim index 09d6f745b..08356590c 100644 --- a/tests/vm/toverflowopcsubimmint.nim +++ b/tests/vm/toverflowopcsubimmint.nim @@ -6,5 +6,5 @@ static: proc p = var x = int64.low discard x - 1 - assert false + doAssert false p() diff --git a/tests/vm/toverflowopcsubint.nim b/tests/vm/toverflowopcsubint.nim index 8d114f200..74e34c6a4 100644 --- a/tests/vm/toverflowopcsubint.nim +++ b/tests/vm/toverflowopcsubint.nim @@ -8,5 +8,5 @@ static: x = int64.low y = 1 discard x - y - assert false + doAssert false p() diff --git a/tests/vm/tstringnil.nim b/tests/vm/tstringnil.nim index df408910e..d5dd4f4c9 100644 --- a/tests/vm/tstringnil.nim +++ b/tests/vm/tstringnil.nim @@ -47,4 +47,4 @@ macro suite(suiteName, suiteDesc, suiteBloc: untyped): typed = # Test above suite basics, "Description of such": test(t5, ""): - assert false + doAssert false diff --git a/tests/vm/tvarsection.nim b/tests/vm/tvarsection.nim index d1c4926a0..a45be6164 100644 --- a/tests/vm/tvarsection.nim +++ b/tests/vm/tvarsection.nim @@ -9,7 +9,7 @@ var d = "abc" static: - assert a == 2 - assert c == 3 + doAssert a == 2 + doAssert c == 3 echo b, d diff --git a/tests/vm/tvmmisc.nim b/tests/vm/tvmmisc.nim index e4d6c308f..005f7e255 100644 --- a/tests/vm/tvmmisc.nim +++ b/tests/vm/tvmmisc.nim @@ -22,11 +22,11 @@ import algorithm static: var numArray = [1, 2, 3, 4, -1] numArray.sort(cmp) - assert numArray == [-1, 1, 2, 3, 4] + doAssert numArray == [-1, 1, 2, 3, 4] var str = "cba" str.sort(cmp) - assert str == "abc" + doAssert str == "abc" # #6086 import math, sequtils, sugar @@ -42,7 +42,7 @@ block: var a = f() const b = f() - assert a == b + doAssert a == b block: proc f(): seq[char] = @@ -50,7 +50,7 @@ block: var runTime = f() const compTime = f() - assert runTime == compTime + doAssert runTime == compTime # #6083 block: @@ -64,24 +64,24 @@ block: result[i] = tmp const fact1000 = abc() - assert fact1000 == @[1, 2] + doAssert fact1000 == @[1, 2] # Tests for VM ops block: static: # for joint test, the project path is different, so I disabled it: when false: - assert "vm" in getProjectPath() + doAssert "vm" in getProjectPath() let b = getEnv("UNSETENVVAR") - assert b == "" - assert existsEnv("UNSERENVVAR") == false + doAssert b == "" + doAssert existsEnv("UNSERENVVAR") == false putEnv("UNSETENVVAR", "VALUE") - assert getEnv("UNSETENVVAR") == "VALUE" - assert existsEnv("UNSETENVVAR") == true + doAssert getEnv("UNSETENVVAR") == "VALUE" + doAssert existsEnv("UNSETENVVAR") == true - assert fileExists("MISSINGFILE") == false - assert dirExists("MISSINGDIR") == false + doAssert fileExists("MISSINGFILE") == false + doAssert dirExists("MISSINGDIR") == false # #7210 block: diff --git a/tests/vm/twrong_concat.nim b/tests/vm/twrong_concat.nim index 538ea2527..b9cca8341 100644 --- a/tests/vm/twrong_concat.nim +++ b/tests/vm/twrong_concat.nim @@ -23,6 +23,6 @@ static: sameBug(objs) # sameBug(objs) echo objs[0].field - assert(objs[0].field == "hello") # fails, because (objs[0].field == "hello bug") - mutated! + doAssert(objs[0].field == "hello") # fails, because (objs[0].field == "hello bug") - mutated! echo "success" diff --git a/tests/vm/twrongarray.nim b/tests/vm/twrongarray.nim index c1514d0e9..7f24290e2 100644 --- a/tests/vm/twrongarray.nim +++ b/tests/vm/twrongarray.nim @@ -14,4 +14,4 @@ when false: proc two(dummy: int, size: int) = var x: array[size * 1, int] # compiles, but shouldn't? - #assert(x.len == size) # just for fun + # doAssert(x.len == size) # just for fun From e70ac0f34c9f195f6e423b3ac7e54b912e62c71d Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Tue, 29 Dec 2020 05:32:11 +0300 Subject: [PATCH 1224/3768] RST: fix directive with fields (#16490) (#16493) * RST: fix directive with fields (#16490) * Update tests/stdlib/trstgen.nim Co-authored-by: Clyybber --- lib/packages/docutils/rst.nim | 3 ++- tests/stdlib/trstgen.nim | 11 +++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 7dbcaf482..698d76da1 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -1737,7 +1737,8 @@ proc parseDirective(p: var RstParser, flags: DirFlags, ## ## .. warning:: Any of the 3 children may be nil. result = parseDirective(p, flags) - if not isNil(contentParser): + if not isNil(contentParser) and + (currentTok(p).kind != tkIndent or indFollows(p)): var nextIndent = p.tok[tokenAfterNewline(p)-1].ival if nextIndent <= currInd(p): # parse only this line nextIndent = currentTok(p).col diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim index c3388ab7d..54a3db202 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -535,6 +535,17 @@ Test1 doAssert count(output1, "
                                      ") == 1 + test "Nim (RST extension) code-block": + # check that presence of fields doesn't consume the following text as + # its code (which is a literal block) + let input0 = dedent """ + .. code-block:: nim + :number-lines: 0 + + Paragraph1""" + let output0 = rstToHtml(input0, {roSupportMarkdown}, defaultConfig()) + doAssert "

                                      Paragraph1

                                      " in output0 + test "RST admonitions": # check that all admonitions are implemented let input0 = dedent """ From 672dc5cd87790dc7f44ad62ae66f07e96e665409 Mon Sep 17 00:00:00 2001 From: Alexander Ivanov Date: Tue, 29 Dec 2020 11:31:11 +0200 Subject: [PATCH 1225/3768] Nil type check implementation (#15287) * Nil checking * Enable current older not nil checking again, run new checking only under flag, skip our test * Enable tests, work on try/except and bugs, fix notnil tests * Enable strictNotNil tests (currently with lowercase category) and add some expected output * Work on try/except/finally: still some things unclear and a lot of code can raise out of try * Fix the notnil build by going back to the old version of a test which I shouldn't have changed * Fix test : use action compile * Work on mutation and aliasing: not finished * Render var parititions graph, try to understand it, fix a nilcheck if bug * Rebase, progress on working with partitions * Improve time logic * Fix some bugs, use graph indices instead of symbol in nil map * Fix bugs, test simpler ident aliasing for now, support two mutation levels * Support ContentMutation and ReAssignment: for now just detect possible re assignment for var parameters of calls * Enable several simple passing tests * Cleanup a bit, fix condition/branch infix-related bug * Remove some files, address some comments by Araq * Use internalError and no quit for now * Separate tests with expected warnings and with expected ok, fix a bug with if with a single branch related to copyMap * Fix new data structures, bugs: make tests pass, disable some for now * Work on fixing errors with non-sym nodes, aliasing: tests fail * Work on alias support: simple set-based logic, todo more tests and ref sets? * Use ref sets: TODO can we think of handle seq-s similar to varpartitions' Araq ones * Handle defers in one place, stop raising in reverse to make an async test compile with strictNotNil, add a commented out test * Dot expressions: call/reassignment. Other refactorings and distinct, SeqOfDistinct support. Checkout an older varpartitions * Work on field tracking * Backup : trying to fix bugs when running some stdlib stuff for running an async test * Start a section about strict not nil checking in experimental manual * Fix experimental strict not nil manual section and move it to another file based on Araq feedback * Fix unstructured flow and double warning problems, fix manual, cleanup * Fix if/elif/else : take in account structure according to Araq feedback * Refactor a bit * Work on bracket expr support, re-enable tests, clarify in manual/tests/implementation static index support for now * Work on compiling stdlib and compiler with strictNotNil * Small fixes to the manual for strictNotNil * Fix idgen for strict check nil rebase * Enable some simple tests, remove old stuff, comment out code/print * Copy the original varpartitions source instead of my changes * Remove some files --- compiler/ast.nim | 13 +- compiler/lineinfos.nim | 3 +- compiler/nilcheck.nim | 1381 ++++++++++++++++++ compiler/options.nim | 3 +- compiler/semobjconstr.nim | 1 + compiler/sempass2.nim | 5 +- compiler/semtypes.nim | 11 +- compiler/treetab.nim | 10 +- compiler/varpartitions.nim | 2 +- doc/manual_experimental.rst | 1 + doc/manual_experimental_strictnotnil.rst | 235 +++ tests/strictnotnil/tnilcheck.nim | 382 +++++ tests/strictnotnil/tnilcheck_no_warnings.nim | 182 +++ 13 files changed, 2215 insertions(+), 14 deletions(-) create mode 100644 compiler/nilcheck.nim create mode 100644 doc/manual_experimental_strictnotnil.rst create mode 100644 tests/strictnotnil/tnilcheck.nim create mode 100644 tests/strictnotnil/tnilcheck_no_warnings.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index 4ca5035ed..8acf08284 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1004,7 +1004,7 @@ const ConstantDataTypes*: TTypeKinds = {tyArray, tySet, tyTuple, tySequence} NilableTypes*: TTypeKinds = {tyPointer, tyCString, tyRef, tyPtr, - tyProc, tyError} + tyProc, tyError} # TODO PtrLikeKinds*: TTypeKinds = {tyPointer, tyPtr} # for VM ExportableSymKinds* = {skVar, skConst, skProc, skFunc, skMethod, skType, skIterator, @@ -1387,6 +1387,7 @@ proc newType*(kind: TTypeKind, id: ItemId; owner: PSym): PType = echo "KNID ", kind writeStackTrace() + proc mergeLoc(a: var TLoc, b: TLoc) = if a.k == low(typeof(a.k)): a.k = b.k if a.storage == low(typeof(a.storage)): a.storage = b.storage @@ -1952,9 +1953,13 @@ proc canRaise*(fn: PNode): bool = elif fn.kind == nkSym and fn.sym.magic == mEcho: result = true else: - result = fn.typ != nil and fn.typ.n != nil and ((fn.typ.n[0].len < effectListLen) or - (fn.typ.n[0][exceptionEffects] != nil and - fn.typ.n[0][exceptionEffects].safeLen > 0)) + # TODO check for n having sons? or just return false for now if not + if fn.typ != nil and fn.typ.n != nil and fn.typ.n[0].kind == nkSym: + result = false + else: + result = fn.typ != nil and fn.typ.n != nil and ((fn.typ.n[0].len < effectListLen) or + (fn.typ.n[0][exceptionEffects] != nil and + fn.typ.n[0][exceptionEffects].safeLen > 0)) proc toHumanStrImpl[T](kind: T, num: static int): string = result = $kind diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index f9ea90caf..9e0fe68fc 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -56,7 +56,7 @@ type warnLockLevel = "LockLevel", warnResultShadowed = "ResultShadowed", warnInconsistentSpacing = "Spacing", warnCaseTransition = "CaseTransition", warnCycleCreated = "CycleCreated", warnObservableStores = "ObservableStores", - warnUser = "User", + warnUser = "User", warnStrictNotNil = "StrictNotNil", hintSuccess = "Success", hintSuccessX = "SuccessX", hintCC = "CC", hintLineTooLong = "LineTooLong", hintXDeclaredButNotUsed = "XDeclaredButNotUsed", @@ -129,6 +129,7 @@ const warnCycleCreated: "$1", warnObservableStores: "observable stores to '$1'", warnUser: "$1", + warnStrictNotNil: "$1", hintSuccess: "operation successful: $#", # keep in sync with `testament.isSuccess` hintSuccessX: "${loc} lines; ${sec}s; $mem; $build build; proj: $project; out: $output", diff --git a/compiler/nilcheck.nim b/compiler/nilcheck.nim new file mode 100644 index 000000000..23f403589 --- /dev/null +++ b/compiler/nilcheck.nim @@ -0,0 +1,1381 @@ +# +# +# The Nim Compiler +# (c) Copyright 2017 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +import ast, renderer, intsets, tables, msgs, options, lineinfos, strformat, idents, treetab, hashes +import sequtils, strutils, std / sets + +# IMPORTANT: notes not up to date, i'll update this comment again +# +# notes: +# +# Env: int => nilability +# a = b +# nilability a <- nilability b +# deref a +# if Nil error is nil +# if MaybeNil error might be nil, hint add if isNil +# if Safe fine +# fun(arg: A) +# nilability arg <- for ref MaybeNil, for not nil or others Safe +# map is env? +# a or b +# each one forks a different env +# result = union(envL, envR) +# a and b +# b forks a's env +# if a: code +# result = union(previousEnv after not a, env after code) +# if a: b else: c +# result = union(env after b, env after c) +# result = b +# nilability result <- nilability b, if return type is not nil and result not safe, error +# return b +# as result = b +# try: a except: b finally: c +# in b and c env is union of all possible try first n lines, after union of a and b and c +# keep in mind canRaise and finally +# case a: of b: c +# similar to if +# call(arg) +# if it returns ref, assume it's MaybeNil: hint that one can add not nil to the return type +# call(var arg) # zahary comment +# if arg is ref, assume it's MaybeNil after call +# loop +# union of env for 0, 1, 2 iterations as Herb Sutter's paper +# why 2? +# return +# if something: stop (break return etc) +# is equivalent to if something: .. else: remain +# new(ref) +# ref becomes Safe +# objConstr(a: b) +# returns safe +# each check returns its nilability and map + +type + SeqOfDistinct[T, U] = distinct seq[U] + +# TODO use distinct base type instead of int? +func `[]`[T, U](a: SeqOfDistinct[T, U], index: T): U = + (seq[U])(a)[index.int] + +proc `[]=`[T, U](a: var SeqOfDistinct[T, U], index: T, value: U) = + ((seq[U])(a))[index.int] = value + +func `[]`[T, U](a: var SeqOfDistinct[T, U], index: T): var U = + (seq[U])(a)[index.int] + +func len[T, U](a: SeqOfDistinct[T, U]): T = + (seq[U])(a).len.T + +func low[T, U](a: SeqOfDistinct[T, U]): T = + (seq[U])(a).low.T + +func high[T, U](a: SeqOfDistinct[T, U]): T = + (seq[U])(a).high.T + +proc setLen[T, U](a: var SeqOfDistinct[T, U], length: T) = + ((seq[U])(a)).setLen(length.Natural) + + +proc newSeqOfDistinct[T, U](length: T = 0.T): SeqOfDistinct[T, U] = + (SeqOfDistinct[T, U])(newSeq[U](length.int)) + +func newSeqOfDistinct[T, U](length: int = 0): SeqOfDistinct[T, U] = + # newSeqOfDistinct(length.T) + # ? newSeqOfDistinct[T, U](length.T) + (SeqOfDistinct[T, U])(newSeq[U](length)) + +iterator items[T, U](a: SeqOfDistinct[T, U]): U = + for element in (seq[U])(a): + yield element + +iterator pairs[T, U](a: SeqOfDistinct[T, U]): (T, U) = + for i, element in (seq[U])(a): + yield (i.T, element) + +func `$`[T, U](a: SeqOfDistinct[T, U]): string = + $((seq[U])(a)) + +proc add*[T, U](a: var SeqOfDistinct[T, U], value: U) = + ((seq[U])(a)).add(value) + +type + ## a hashed representation of a node: should be equal for structurally equal nodes + Symbol = distinct int + + ## the index of an expression in the pre-indexed sequence of those + ExprIndex = distinct int16 + + ## the set index + SetIndex = distinct int + + ## transition kind: + ## what was the reason for changing the nilability of an expression + ## useful for error messages and showing why an expression is being detected as nil / maybe nil + TransitionKind = enum TArg, TAssign, TType, TNil, TVarArg, TResult, TSafe, TPotentialAlias, TDependant + + ## keep history for each transition + History = object + info: TLineInfo ## the location + nilability: Nilability ## the nilability + kind: TransitionKind ## what kind of transition was that + node: PNode ## the node of the expression + + ## the context for the checker: an instance for each procedure + NilCheckerContext = ref object + # abstractTime: AbstractTime + # partitions: Partitions + # symbolGraphs: Table[Symbol, ] + symbolIndices: Table[Symbol, ExprIndex] ## index for each symbol + expressions: SeqOfDistinct[ExprIndex, PNode] ## a sequence of pre-indexed expressions + dependants: SeqOfDistinct[ExprIndex, IntSet] ## expr indices for expressions which are compound and based on others + warningLocations: HashSet[TLineInfo] ## warning locations to check we don't warn twice for stuff like warnings in for loops + idgen: IdGenerator ## id generator + config: ConfigRef ## the config of the compiler + + ## a map that is containing the current nilability for usually a branch + ## and is pointing optionally to a parent map: they make a stack of maps + NilMap = ref object + expressions: SeqOfDistinct[ExprIndex, Nilability] ## the expressions with the same order as in NilCheckerContext + history: SeqOfDistinct[ExprIndex, seq[History]] ## history for each of them + # what about gc and refs? + setIndices: SeqOfDistinct[ExprIndex, SetIndex] ## set indices for each expression + sets: SeqOfDistinct[SetIndex, IntSet] ## disjoint sets with the aliased expressions + parent: NilMap ## the parent map + + ## Nilability : if a value is nilable. + ## we have maybe nil and nil, so we can differentiate between + ## cases where we know for sure a value is nil and not + ## otherwise we can have Safe, MaybeNil + ## Parent: is because we just use a sequence with the same length + ## instead of a table, and we need to check if something was initialized + ## at all: if Parent is set, then you need to check the parent nilability + ## if the parent is nil, then for now we return MaybeNil + ## unreachable is the result of add(Safe, Nil) and others + ## it is a result of no states left, so it's usually e.g. in unreachable else branches? + Nilability* = enum Parent, Safe, MaybeNil, Nil, Unreachable + + ## check + Check = object + nilability: Nilability + map: NilMap + elements: seq[(PNode, Nilability)] + + +# useful to have known resultId so we can set it in the beginning and on return +const resultId: Symbol = (-1).Symbol +const resultExprIndex: ExprIndex = 0.ExprIndex +const noSymbol = (-2).Symbol + +func `<`*(a: ExprIndex, b: ExprIndex): bool = + a.int16 < b.int16 + +func `<=`*(a: ExprIndex, b: ExprIndex): bool = + a.int16 <= b.int16 + +func `>`*(a: ExprIndex, b: ExprIndex): bool = + a.int16 > b.int16 + +func `>=`*(a: ExprIndex, b: ExprIndex): bool = + a.int16 >= b.int16 + +func `==`*(a: ExprIndex, b: ExprIndex): bool = + a.int16 == b.int16 + +func `$`*(a: ExprIndex): string = + $(a.int16) + +func `+`*(a: ExprIndex, b: ExprIndex): ExprIndex = + (a.int16 + b.int16).ExprIndex + +# TODO overflowing / < 0? +func `-`*(a: ExprIndex, b: ExprIndex): ExprIndex = + (a.int16 - b.int16).ExprIndex + +func `$`*(a: SetIndex): string = + $(a.int) + +func `==`*(a: SetIndex, b: SetIndex): bool = + a.int == b.int + +func `+`*(a: SetIndex, b: SetIndex): SetIndex = + (a.int + b.int).SetIndex + +# TODO over / under limit? +func `-`*(a: SetIndex, b: SetIndex): SetIndex = + (a.int - b.int).SetIndex + +proc check(n: PNode, ctx: NilCheckerContext, map: NilMap): Check +proc checkCondition(n: PNode, ctx: NilCheckerContext, map: NilMap, reverse: bool, base: bool): NilMap + +# the NilMap structure + +proc newNilMap(parent: NilMap = nil, count: int = -1): NilMap = + var expressionsCount = 0 + if count != -1: + expressionsCount = count + elif not parent.isNil: + expressionsCount = parent.expressions.len.int + result = NilMap( + expressions: newSeqOfDistinct[ExprIndex, Nilability](expressionsCount), + history: newSeqOfDistinct[ExprIndex, seq[History]](expressionsCount), + setIndices: newSeqOfDistinct[ExprIndex, SetIndex](expressionsCount), + parent: parent) + if parent.isNil: + for i, expr in result.expressions: + result.setIndices[i] = i.SetIndex + var newSet = initIntSet() + newSet.incl(i.int) + result.sets.add(newSet) + else: + for i, exprs in parent.sets: + result.sets.add(exprs) + for i, index in parent.setIndices: + result.setIndices[i] = index + # result.sets = parent.sets + # if not parent.isNil: + # # optimize []? + # result.expressions = parent.expressions + # result.history = parent.history + # result.sets = parent.sets + # result.base = if parent.isNil: result else: parent.base + +proc `[]`(map: NilMap, index: ExprIndex): Nilability = + if index < 0.ExprIndex or index >= map.expressions.len: + return MaybeNil + var now = map + while not now.isNil: + if now.expressions[index] != Parent: + return now.expressions[index] + now = now.parent + return MaybeNil + +proc history(map: NilMap, index: ExprIndex): seq[History] = + if index < map.expressions.len: + map.history[index] + else: + @[] + + +# helpers for debugging + +# import macros + +# echo-s only when nilDebugInfo is defined +# macro aecho*(a: varargs[untyped]): untyped = +# var e = nnkCall.newTree(ident"echo") +# for b in a: +# e.add(b) +# result = quote: +# when defined(nilDebugInfo): +# `e` + +# end of helpers for debugging + + +proc symbol(n: PNode): Symbol +func `$`(map: NilMap): string +proc reverseDirect(map: NilMap): NilMap +proc checkBranch(n: PNode, ctx: NilCheckerContext, map: NilMap): Check +proc hasUnstructuredControlFlowJump(n: PNode): bool + +proc symbol(n: PNode): Symbol = + ## returns a Symbol for each expression + ## the goal is to get an unique Symbol + ## but we have to ensure hashTree does it as we expect + case n.kind: + of nkIdent: + # TODO ensure no idents get passed to symbol + result = noSymbol + of nkSym: + if n.sym.kind == skResult: # credit to disruptek for showing me that + result = resultId + else: + result = n.sym.id.Symbol + of nkHiddenAddr, nkAddr: + result = symbol(n[0]) + else: + result = hashTree(n).Symbol + # echo "symbol ", n, " ", n.kind, " ", result.int + +func `$`(map: NilMap): string = + var now = map + var stack: seq[NilMap] = @[] + while not now.isNil: + stack.add(now) + now = now.parent + result.add("### start\n") + for i in 0 .. stack.len - 1: + now = stack[i] + result.add(" ###\n") + for index, value in now.expressions: + result.add(&" {index} {value}\n") + result.add "### end\n" + +proc namedMapDebugInfo(ctx: NilCheckerContext, map: NilMap): string = + result = "" + var now = map + var stack: seq[NilMap] = @[] + while not now.isNil: + stack.add(now) + now = now.parent + result.add("### start\n") + for i in 0 .. stack.len - 1: + now = stack[i] + result.add(" ###\n") + for index, value in now.expressions: + let name = ctx.expressions[index] + result.add(&" {name} {index} {value}\n") + result.add("### end\n") + +proc namedSetsDebugInfo(ctx: NilCheckerContext, map: NilMap): string = + result = "### sets " + for index, setIndex in map.setIndices: + var aliasSet = map.sets[setIndex] + result.add("{") + let expressions = aliasSet.mapIt($ctx.expressions[it.ExprIndex]) + result.add(join(expressions, ", ")) + result.add("} ") + result.add("\n") + +proc namedMapAndSetsDebugInfo(ctx: NilCheckerContext, map: NilMap): string = + result = namedMapDebugInfo(ctx, map) & namedSetsDebugInfo(ctx, map) + + + +const noExprIndex = (-1).ExprIndex +const noSetIndex = (-1).SetIndex + +proc `==`(a: Symbol, b: Symbol): bool = + a.int == b.int + +func `$`(a: Symbol): string = + $(a.int) + +template isConstBracket(n: PNode): bool = + n.kind == nkBracketExpr and n[1].kind in nkLiterals + +proc index(ctx: NilCheckerContext, n: PNode): ExprIndex = + # echo "n ", n, " ", n.kind + let a = symbol(n) + if ctx.symbolIndices.hasKey(a): + return ctx.symbolIndices[a] + else: + #for a, e in ctx.expressions: + # echo a, " ", e + #echo n.kind + # internalError(ctx.config, n.info, "expected " & $a & " " & $n & " to have a index") + return noExprIndex + # + #ctx.symbolIndices[symbol(n)] + + +proc aliasSet(ctx: NilCheckerContext, map: NilMap, n: PNode): IntSet = + result = map.sets[map.setIndices[ctx.index(n)]] + +proc aliasSet(ctx: NilCheckerContext, map: NilMap, index: ExprIndex): IntSet = + result = map.sets[map.setIndices[index]] + + + +proc store(map: NilMap, ctx: NilCheckerContext, index: ExprIndex, value: Nilability, kind: TransitionKind, info: TLineInfo, node: PNode = nil) = + if index == noExprIndex: + return + map.expressions[index] = value + map.history[index].add(History(info: info, kind: kind, node: node, nilability: value)) + #echo node, " ", index, " ", value + #echo ctx.namedMapAndSetsDebugInfo(map) + #for a, b in map.sets: + # echo a, " ", b + # echo map + + var exprAliases = aliasSet(ctx, map, index) + for a in exprAliases: + if a.ExprIndex != index: + #echo "alias ", a, " ", index + map.expressions[a.ExprIndex] = value + if value == Safe: + map.history[a.ExprIndex] = @[] + else: + map.history[a.ExprIndex].add(History(info: info, kind: TPotentialAlias, node: node, nilability: value)) + +proc moveOut(ctx: NilCheckerContext, map: NilMap, target: PNode) = + #echo "move out ", target + var targetIndex = ctx.index(target) + var targetSetIndex = map.setIndices[targetIndex] + if targetSetIndex != noSetIndex: + var targetSet = map.sets[targetSetIndex] + if targetSet.len > 1: + var other: ExprIndex + + for element in targetSet: + if element.ExprIndex != targetIndex: + other = element.ExprIndex + break + # map.sets[element].excl(targetIndex) + map.sets[map.setIndices[other]].excl(targetIndex.int) + var newSet = initIntSet() + newSet.incl(targetIndex.int) + map.sets.add(newSet) + map.setIndices[targetIndex] = map.sets.len - 1.SetIndex + +proc moveOutDependants(ctx: NilCheckerContext, map: NilMap, node: PNode) = + let index = ctx.index(node) + for dependant in ctx.dependants[index]: + moveOut(ctx, map, ctx.expressions[dependant.ExprIndex]) + +proc storeDependants(ctx: NilCheckerContext, map: NilMap, node: PNode, value: Nilability) = + let index = ctx.index(node) + for dependant in ctx.dependants[index]: + map.store(ctx, dependant.ExprIndex, value, TDependant, node.info, node) + +proc move(ctx: NilCheckerContext, map: NilMap, target: PNode, assigned: PNode) = + #echo "move ", target, " ", assigned + var targetIndex = ctx.index(target) + var assignedIndex: ExprIndex + var targetSetIndex = map.setIndices[targetIndex] + var assignedSetIndex: SetIndex + if assigned.kind == nkSym: + assignedIndex = ctx.index(assigned) + assignedSetIndex = map.setIndices[assignedIndex] + else: + assignedIndex = noExprIndex + assignedSetIndex = noSetIndex + if assignedIndex == noExprIndex: + moveOut(ctx, map, target) + elif targetSetIndex != assignedSetIndex: + map.sets[targetSetIndex].excl(targetIndex.int) + map.sets[assignedSetIndex].incl(targetIndex.int) + map.setIndices[targetIndex] = assignedSetIndex + +# proc hasKey(map: NilMap, ): bool = +# var now = map +# result = false +# while not now.isNil: +# if now.locals.hasKey(graphIndex): +# return true +# now = now.previous + +iterator pairs(map: NilMap): (ExprIndex, Nilability) = + for index, value in map.expressions: + yield (index, map[index]) + +proc copyMap(map: NilMap): NilMap = + if map.isNil: + return nil + result = newNilMap(map.parent) # no need for copy? if we change only this + result.expressions = map.expressions + result.history = map.history + result.sets = map.sets + result.setIndices = map.setIndices + +using + n: PNode + conf: ConfigRef + ctx: NilCheckerContext + map: NilMap + +proc typeNilability(typ: PType): Nilability + +# maybe: if canRaise, return MaybeNil ? +# no, because the target might be safe already +# with or without an exception +proc checkCall(n, ctx, map): Check = + # checks each call + # special case for new(T) -> result is always Safe + # for the others it depends on the return type of the call + # check args and handle possible mutations + + var isNew = false + result.map = map + for i, child in n: + discard check(child, ctx, map) + + if i > 0: + # var args make a new map with MaybeNil for our node + # as it might have been mutated + # TODO similar for normal refs and fields: find dependent exprs: brackets + + if child.kind == nkHiddenAddr and not child.typ.isNil and child.typ.kind == tyVar and child.typ[0].kind == tyRef: + if not isNew: + result.map = newNilMap(map) + isNew = true + # result.map[$child] = MaybeNil + var arg = child + while arg.kind == nkHiddenAddr: + arg = arg[0] + let a = ctx.index(arg) + if a != noExprIndex: + moveOut(ctx, result.map, arg) + moveOutDependants(ctx, result.map, arg) + result.map.store(ctx, a, MaybeNil, TVarArg, n.info, arg) + storeDependants(ctx, result.map, arg, MaybeNil) + elif not child.typ.isNil and child.typ.kind == tyRef: + if child.kind in {nkSym, nkDotExpr} or isConstBracket(child): + let a = ctx.index(child) + if ctx.dependants[a].len > 0: + if not isNew: + result.map = newNilMap(map) + isNew = true + moveOutDependants(ctx, result.map, child) + storeDependants(ctx, result.map, child, MaybeNil) + + if n[0].kind == nkSym and n[0].sym.magic == mNew: + # new hidden deref? + var value = if n[1].kind == nkHiddenDeref: n[1][0] else: n[1] + let b = ctx.index(value) + result.map.store(ctx, b, Safe, TAssign, value.info, value) + result.nilability = Safe + else: + # echo "n ", n, " ", n.typ.isNil + if not n.typ.isNil: + result.nilability = typeNilability(n.typ) + else: + result.nilability = Safe + # echo result.map + +template event(b: History): string = + case b.kind: + of TArg: "param with nilable type" + of TNil: "it returns true for isNil" + of TAssign: "assigns a value which might be nil" + of TVarArg: "passes it as a var arg which might change to nil" + of TResult: "it is nil by default" + of TType: "it has ref type" + of TSafe: "it is safe here as it returns false for isNil" + of TPotentialAlias: "it might be changed directly or through an alias" + of TDependant: "it might be changed because its base might be changed" + +proc derefWarning(n, ctx, map; kind: Nilability) = + ## a warning for potentially unsafe dereference + if n.info in ctx.warningLocations: + return + ctx.warningLocations.incl(n.info) + var a: seq[History] + if n.kind == nkSym: + a = history(map, ctx.index(n)) + var res = "" + var issue = case kind: + of Nil: "it is nil" + of MaybeNil: "it might be nil" + of Unreachable: "it is unreachable" + else: "" + res.add("can't deref " & $n & ", " & issue) + if a.len > 0: + res.add("\n") + for b in a: + res.add(" " & event(b) & " on line " & $b.info.line & ":" & $b.info.col) + message(ctx.config, n.info, warnStrictNotNil, res) + +proc handleNilability(check: Check; n, ctx, map) = + ## handle the check: + ## register a warning(error?) for Nil/MaybeNil + case check.nilability: + of Nil: + derefWarning(n, ctx, map, Nil) + of MaybeNil: + derefWarning(n, ctx, map, MaybeNil) + of Unreachable: + derefWarning(n, ctx, map, Unreachable) + else: + when defined(nilDebugInfo): + message(ctx.config, n.info, hintUser, "can deref " & $n) + +proc checkDeref(n, ctx, map): Check = + ## check dereference: deref n should be ok only if n is Safe + result = check(n[0], ctx, map) + + handleNilability(result, n[0], ctx, map) + + +proc checkRefExpr(n, ctx; check: Check): Check = + ## check ref expressions: TODO not sure when this happens + result = check + if n.typ.kind != tyRef: + result.nilability = typeNilability(n.typ) + elif tfNotNil notin n.typ.flags: + # echo "ref key ", n, " ", n.kind + if n.kind in {nkSym, nkDotExpr} or isConstBracket(n): + let key = ctx.index(n) + result.nilability = result.map[key] + elif n.kind == nkBracketExpr: + # sometimes false positive + result.nilability = MaybeNil + else: + # sometimes maybe false positive + result.nilability = MaybeNil + +proc checkDotExpr(n, ctx, map): Check = + ## check dot expressions: make sure we can dereference the base + result = check(n[0], ctx, map) + result = checkRefExpr(n, ctx, result) + +proc checkBracketExpr(n, ctx, map): Check = + ## check bracket expressions: make sure we can dereference the base + result = check(n[0], ctx, map) + # if might be deref: [] == *(a + index) for cstring + handleNilability(result, n[0], ctx, map) + result = check(n[1], ctx, result.map) + result = checkRefExpr(n, ctx, result) + # echo n, " ", result.nilability + + +template union(l: Nilability, r: Nilability): Nilability = + ## unify two states + if l == r: + l + else: + MaybeNil + +template add(l: Nilability, r: Nilability): Nilability = + if l == r: # Safe Safe -> Safe etc + l + elif l == Parent: # Parent Safe -> Safe etc + r + elif r == Parent: # Safe Parent -> Safe etc + l + elif l == Unreachable or r == Unreachable: # Safe Unreachable -> Unreachable etc + Unreachable + elif l == MaybeNil: # Safe MaybeNil -> Safe etc + r + elif r == MaybeNil: # MaybeNil Nil -> Nil etc + l + else: # Safe Nil -> Unreachable etc + Unreachable + +proc findCommonParent(l: NilMap, r: NilMap): NilMap = + result = l.parent + while not result.isNil: + var rparent = r.parent + while not rparent.isNil: + if result == rparent: + return result + rparent = rparent.parent + result = result.parent + +proc union(ctx: NilCheckerContext, l: NilMap, r: NilMap): NilMap = + ## unify two maps from different branches + ## combine their locals + ## what if they are from different parts of the same tree + ## e.g. + ## a -> b -> c + ## -> b1 + ## common then? + ## + if l.isNil: + return r + elif r.isNil: + return l + + let common = findCommonParent(l, r) + result = newNilMap(common, ctx.expressions.len.int) + + for index, value in l: + let h = history(r, index) + let info = if h.len > 0: h[^1].info else: TLineInfo(line: 0) # assert h.len > 0 + # echo "history", name, value, r[name], h[^1].info.line + result.store(ctx, index, union(value, r[index]), TAssign, info) + +proc add(ctx: NilCheckerContext, l: NilMap, r: NilMap): NilMap = + #echo "add " + #echo namedMapDebugInfo(ctx, l) + #echo " : " + #echo namedMapDebugInfo(ctx, r) + if l.isNil: + return r + elif r.isNil: + return l + + let common = findCommonParent(l, r) + result = newNilMap(common, ctx.expressions.len.int) + + for index, value in l: + let h = history(r, index) + let info = if h.len > 0: h[^1].info else: TLineInfo(line: 0) + # TODO: refactor and also think: is TAssign a good one + result.store(ctx, index, add(value, r[index]), TAssign, info) + + #echo "result" + #echo namedMapDebugInfo(ctx, result) + #echo "" + #echo "" + + +proc checkAsgn(target: PNode, assigned: PNode; ctx, map): Check = + ## check assignment + ## update map based on `assigned` + if assigned.kind != nkEmpty: + result = check(assigned, ctx, map) + else: + result = Check(nilability: typeNilability(target.typ), map: map) + + # we need to visit and check those, but we don't use the result for now + # is it possible to somehow have another event happen here? + discard check(target, ctx, map) + + if result.map.isNil: + result.map = map + if target.kind in {nkSym, nkDotExpr} or isConstBracket(target): + let t = ctx.index(target) + move(ctx, map, target, assigned) + case assigned.kind: + of nkNilLit: + result.map.store(ctx, t, Nil, TAssign, target.info, target) + else: + result.map.store(ctx, t, result.nilability, TAssign, target.info, target) + moveOutDependants(ctx, map, target) + storeDependants(ctx, map, target, MaybeNil) + if assigned.kind in {nkObjConstr, nkTupleConstr}: + for (element, value) in result.elements: + var elementNode = nkDotExpr.newTree(nkHiddenDeref.newTree(target), element) + if symbol(elementNode) in ctx.symbolIndices: + var elementIndex = ctx.index(elementNode) + result.map.store(ctx, elementIndex, value, TAssign, target.info, elementNode) + + +proc checkReturn(n, ctx, map): Check = + ## check return + # return n same as result = n; return ? + result = check(n[0], ctx, map) + result.map.store(ctx, resultExprIndex, result.nilability, TAssign, n.info) + + +proc checkIf(n, ctx, map): Check = + ## check branches based on condition + var mapIf: NilMap = map + + # first visit the condition + + # the structure is not If(Elif(Elif, Else), Else) + # it is + # If(Elif, Elif, Else) + + var mapCondition = checkCondition(n.sons[0].sons[0], ctx, mapIf, false, true) + + # the state of the conditions: negating conditions before the current one + var layerHistory = newNilMap(mapIf) + # the state after branch effects + var afterLayer: NilMap + # the result nilability for expressions + var nilability = Safe + + for branch in n.sons: + var branchConditionLayer = newNilMap(layerHistory) + var branchLayer: NilMap + var code: PNode + if branch.kind in {nkIfStmt, nkElifBranch}: + var mapCondition = checkCondition(branch[0], ctx, branchConditionLayer, false, true) + let reverseMapCondition = reverseDirect(mapCondition) + layerHistory = ctx.add(layerHistory, reverseMapCondition) + branchLayer = mapCondition + code = branch[1] + else: + branchLayer = layerHistory + code = branch + + let branchCheck = checkBranch(code, ctx, branchLayer) + # handles nil afterLayer -> returns branchCheck.map + afterLayer = ctx.union(afterLayer, branchCheck.map) + nilability = if n.kind == nkIfStmt: Safe else: union(nilability, branchCheck.nilability) + if n.sons.len > 1: + result.map = afterLayer + result.nilability = nilability + else: + if not hasUnstructuredControlFlowJump(n[0][1]): + # here it matters what happend inside, because + # we might continue in the parent branch after entering this one + # either we enter the branch, so we get mapIf and effect of branch -> afterLayer + # or we dont , so we get mapIf and (not condition) effect -> layerHistory + result.map = ctx.union(layerHistory, afterLayer) + result.nilability = Safe # no expr? + else: + # similar to else: because otherwise we are jumping out of + # the branch, so no union with the mapIf (we dont continue if the condition was true) + # here it also doesn't matter for the parent branch what happened in the branch, e.g. assigning to nil + # as if we continue there, we haven't entered the branch probably + # so we don't do an union with afterLayer + # layerHistory has the effect of mapIf and (not condition) + result.map = layerHistory + result.nilability = Safe + +proc checkFor(n, ctx, map): Check = + ## check for loops + ## try to repeat the unification of the code twice + ## to detect what can change after a several iterations + ## approach based on discussions with Zahary/Araq + ## similar approach used for other loops + var m = map.copyMap() + var map0 = map.copyMap() + #echo namedMapDebugInfo(ctx, map) + m = check(n.sons[2], ctx, map).map.copyMap() + if n[0].kind == nkSym: + m.store(ctx, ctx.index(n[0]), typeNilability(n[0].typ), TAssign, n[0].info) + # echo namedMapDebugInfo(ctx, map) + var check2 = check(n.sons[2], ctx, m) + var map2 = check2.map + + result.map = ctx.union(map0, m) + result.map = ctx.union(result.map, map2) + result.nilability = Safe + +# check: +# while code: +# code2 + +# if code: +# code2 +# if code: +# code2 + +# if code: +# code2 + +# check(code), check(code2 in code's map) + +proc checkWhile(n, ctx, map): Check = + ## check while loops + ## try to repeat the unification of the code twice + var m = checkCondition(n[0], ctx, map, false, false) + var map0 = map.copyMap() + m = check(n.sons[1], ctx, m).map + var map1 = m.copyMap() + var check2 = check(n.sons[1], ctx, m) + var map2 = check2.map + + result.map = ctx.union(map0, map1) + result.map = ctx.union(result.map, map2) + result.nilability = Safe + +proc checkInfix(n, ctx, map): Check = + ## check infix operators in condition + ## a and b : map is based on a; next b + ## a or b : map is an union of a and b's + ## a == b : use checkCondition + ## else: no change, just check args + if n[0].kind == nkSym: + var mapL: NilMap + var mapR: NilMap + if n[0].sym.magic notin {mAnd, mEqRef}: + mapL = checkCondition(n[1], ctx, map, false, false) + mapR = checkCondition(n[2], ctx, map, false, false) + case n[0].sym.magic: + of mOr: + result.map = ctx.union(mapL, mapR) + of mAnd: + result.map = checkCondition(n[1], ctx, map, false, false) + result.map = checkCondition(n[2], ctx, result.map, false, false) + of mEqRef: + if n[2].kind == nkIntLit: + if $n[2] == "true": + result.map = checkCondition(n[1], ctx, map, false, false) + elif $n[2] == "false": + result.map = checkCondition(n[1], ctx, map, true, false) + elif n[1].kind == nkIntLit: + if $n[1] == "true": + result.map = checkCondition(n[2], ctx, map, false, false) + elif $n[1] == "false": + result.map = checkCondition(n[2], ctx, map, true, false) + + if result.map.isNil: + result.map = map + else: + result.map = map + else: + result.map = map + result.nilability = Safe + +proc checkIsNil(n, ctx, map; isElse: bool = false): Check = + ## check isNil calls + ## update the map depending on if it is not isNil or isNil + result.map = newNilMap(map) + let value = n[1] + result.map.store(ctx, ctx.index(n[1]), if not isElse: Nil else: Safe, TArg, n.info, n) + +proc infix(ctx: NilCheckerContext, l: PNode, r: PNode, magic: TMagic): PNode = + var name = case magic: + of mEqRef: "==" + of mAnd: "and" + of mOr: "or" + else: "" + + var cache = newIdentCache() + var op = newSym(skVar, cache.getIdent(name), nextId ctx.idgen, nil, r.info) + + op.magic = magic + result = nkInfix.newTree( + newSymNode(op, r.info), + l, + r) + result.typ = newType(tyBool, nextId ctx.idgen, nil) + +proc prefixNot(ctx: NilCheckerContext, node: PNode): PNode = + var cache = newIdentCache() + var op = newSym(skVar, cache.getIdent("not"), nextId ctx.idgen, nil, node.info) + + op.magic = mNot + result = nkPrefix.newTree( + newSymNode(op, node.info), + node) + result.typ = newType(tyBool, nextId ctx.idgen, nil) + +proc infixEq(ctx: NilCheckerContext, l: PNode, r: PNode): PNode = + infix(ctx, l, r, mEqRef) + +proc infixOr(ctx: NilCheckerContext, l: PNode, r: PNode): PNode = + infix(ctx, l, r, mOr) + +proc checkCase(n, ctx, map): Check = + # case a: + # of b: c + # of b2: c2 + # is like + # if a == b: + # c + # elif a == b2: + # c2 + # also a == true is a , a == false is not a + let base = n[0] + result.map = map.copyMap() + result.nilability = Safe + var a: PNode + for child in n: + case child.kind: + of nkOfBranch: + if child.len < 2: + # echo "case with of with < 2 ", n + continue # TODO why does this happen + let branchBase = child[0] # TODO a, b or a, b..c etc + let code = child[^1] + let test = infixEq(ctx, base, branchBase) + if a.isNil: + a = test + else: + a = infixOr(ctx, a, test) + let conditionMap = checkCondition(test, ctx, map.copyMap(), false, false) + let newCheck = checkBranch(code, ctx, conditionMap) + result.map = ctx.union(result.map, newCheck.map) + result.nilability = union(result.nilability, newCheck.nilability) + of nkElifBranch: + discard "TODO: maybe adapt to be similar to checkIf" + of nkElse: + let mapElse = checkCondition(prefixNot(ctx, a), ctx, map.copyMap(), false, false) + let newCheck = checkBranch(child[0], ctx, mapElse) + result.map = ctx.union(result.map, newCheck.map) + result.nilability = union(result.nilability, newCheck.nilability) + else: + discard + +# notes +# try: +# a +# b +# except: +# c +# finally: +# d +# +# if a doesnt raise, this is not an exit point: +# so find what raises and update the map with that +# (a, b); c; d +# if nothing raises, except shouldn't happen +# .. might be a false positive tho, if canRaise is not conservative? +# so don't visit it +# +# nested nodes can raise as well: I hope nim returns canRaise for +# their parents +# +# a lot of stuff can raise +proc checkTry(n, ctx, map): Check = + var newMap = map.copyMap() + var currentMap = map + # we don't analyze except if nothing canRaise in try + var canRaise = false + var hasFinally = false + # var tryNodes: seq[PNode] + # if n[0].kind == nkStmtList: + # tryNodes = toSeq(n[0]) + # else: + # tryNodes = @[n[0]] + # for i, child in tryNodes: + # let (childNilability, childMap) = check(child, conf, currentMap) + # echo childMap + # currentMap = childMap + # # TODO what about nested + # if child.canRaise: + # newMap = union(newMap, childMap) + # canRaise = true + # else: + # newMap = childMap + let tryCheck = check(n[0], ctx, currentMap) + newMap = ctx.union(currentMap, tryCheck.map) + canRaise = n[0].canRaise + + var afterTryMap = newMap + for a, branch in n: + if a > 0: + case branch.kind: + of nkFinally: + newMap = ctx.union(afterTryMap, newMap) + let childCheck = check(branch[0], ctx, newMap) + newMap = ctx.union(newMap, childCheck.map) + hasFinally = true + of nkExceptBranch: + if canRaise: + let childCheck = check(branch[^1], ctx, newMap) + newMap = ctx.union(newMap, childCheck.map) + else: + discard + if not hasFinally: + # we might have not hit the except branches + newMap = ctx.union(afterTryMap, newMap) + result = Check(nilability: Safe, map: newMap) + +proc hasUnstructuredControlFlowJump(n: PNode): bool = + ## if the node contains a direct stop + ## as a continue/break/raise/return: then it means + ## we should reverse some of the map in the code after the condition + ## similar to else + # echo "n ", n, " ", n.kind + case n.kind: + of nkStmtList: + for child in n: + if hasUnstructuredControlFlowJump(child): + return true + of nkReturnStmt, nkBreakStmt, nkContinueStmt, nkRaiseStmt: + return true + of nkIfStmt, nkIfExpr, nkElifExpr, nkElse: + return false + else: + discard + return false + +proc reverse(value: Nilability): Nilability = + case value: + of Nil: Safe + of MaybeNil: MaybeNil + of Safe: Nil + of Parent: Parent + of Unreachable: Unreachable + +proc reverse(kind: TransitionKind): TransitionKind = + case kind: + of TNil: TSafe + of TSafe: TNil + of TPotentialAlias: TPotentialAlias + else: + kind + # raise newException(ValueError, "expected TNil or TSafe") + +proc reverseDirect(map: NilMap): NilMap = + # we create a new layer + # reverse the values only in this layer: + # because conditions should've stored their changes there + # b: Safe (not b.isNil) + # b: Parent Parent + # b: Nil (b.isNil) + + # layer block + # [ Parent ] [ Parent ] + # if -> if state + # layer -> reverse + # older older0 new + # older new + # [ b Nil ] [ Parent ] + # elif + # [ b Nil, c Nil] [ Parent ] + # + + # if b.isNil: + # # [ b Safe] + # c = A() # Safe + # elif not b.isNil: + # # [ b Safe ] + [b Nil] MaybeNil Unreachable + # # Unreachable defer can't deref b, it is unreachable + # discard + # else: + # b + + +# if + + + + # if: we just pass the map with a new layer for its block + # elif: we just pass the original map but with a new layer is the reverse of the previous popped layer (?) + # elif: + # else: we just pass the original map but with a new layer which is initialized as the reverse of the + # top layer of else + # else: + # + # [ b MaybeNil ] [b Parent] [b Parent] [b Safe] [b Nil] [] + # Safe + # c == 1 + # b Parent + # c == 2 + # b Parent + # not b.isNil + # b Safe + # c == 3 + # b Nil + # (else) + # b Nil + + result = map.copyMap() + for index, value in result.expressions: + result.expressions[index] = reverse(value) + if result.history[index].len > 0: + result.history[index][^1].kind = reverse(result.history[index][^1].kind) + result.history[index][^1].nilability = result.expressions[index] + +proc checkCondition(n, ctx, map; reverse: bool, base: bool): NilMap = + ## check conditions : used for if, some infix operators + ## isNil(a) + ## it returns a new map: you need to reverse all the direct elements for else + + # echo "condition ", n, " ", n.kind + if n.kind == nkCall: + result = newNilMap(map) + for element in n: + if element.kind == nkHiddenDeref and n[0].kind == nkSym and n[0].sym.magic == mIsNil: + result = check(element[0], ctx, result).map + else: + result = check(element, ctx, result).map + + if n[0].kind == nkSym and n[0].sym.magic == mIsNil: + # isNil(arg) + var arg = n[1] + while arg.kind == nkHiddenDeref: + arg = arg[0] + if arg.kind in {nkSym, nkDotExpr} or isConstBracket(arg): + let a = ctx.index(arg) + result.store(ctx, a, if not reverse: Nil else: Safe, if not reverse: TNil else: TSafe, n.info, arg) + else: + discard + else: + discard + elif n.kind == nkPrefix and n[0].kind == nkSym and n[0].sym.magic == mNot: + result = checkCondition(n[1], ctx, map, not reverse, false) + elif n.kind == nkInfix: + result = newNilMap(map) + result = checkInfix(n, ctx, result).map + else: + result = check(n, ctx, map).map + result = newNilMap(map) + assert not result.isNil + assert not result.parent.isNil + +proc checkResult(n, ctx, map) = + let resultNilability = map[resultExprIndex] + case resultNilability: + of Nil: + message(ctx.config, n.info, warnStrictNotNil, "return value is nil") + of MaybeNil: + message(ctx.config, n.info, warnStrictNotNil, "return value might be nil") + of Unreachable: + message(ctx.config, n.info, warnStrictNotNil, "return value is unreachable") + of Safe, Parent: + discard + +proc checkBranch(n: PNode, ctx: NilCheckerContext, map: NilMap): Check = + result = check(n, ctx, map) + + +# Faith! + +proc check(n: PNode, ctx: NilCheckerContext, map: NilMap): Check = + assert not map.isNil + + # echo "check n ", n, " ", n.kind + # echo "map ", namedMapDebugInfo(ctx, map) + case n.kind: + of nkSym: + result = Check(nilability: map[ctx.index(n)], map: map) + of nkCallKinds: + if n.sons[0].kind == nkSym: + let callSym = n.sons[0].sym + case callSym.magic: + of mAnd, mOr: + result = checkInfix(n, ctx, map) + of mIsNil: + result = checkIsNil(n, ctx, map) + else: + result = checkCall(n, ctx, map) + else: + result = checkCall(n, ctx, map) + of nkHiddenStdConv, nkHiddenSubConv, nkConv, nkExprColonExpr, nkExprEqExpr, + nkCast: + result = check(n.sons[1], ctx, map) + of nkStmtList, nkStmtListExpr, nkChckRangeF, nkChckRange64, nkChckRange, + nkBracket, nkCurly, nkPar, nkTupleConstr, nkClosure, nkObjConstr, nkElse: + result.map = map + if n.kind in {nkObjConstr, nkTupleConstr}: + # TODO deeper nested elements? + # A(field: B()) # + # field: Safe -> + var elements: seq[(PNode, Nilability)] + for i, child in n: + result = check(child, ctx, result.map) + if i > 0: + if child.kind == nkExprColonExpr: + elements.add((child[0], result.nilability)) + result.elements = elements + result.nilability = Safe + else: + for child in n: + result = check(child, ctx, result.map) + + of nkDotExpr: + result = checkDotExpr(n, ctx, map) + of nkDerefExpr, nkHiddenDeref: + result = checkDeref(n, ctx, map) + of nkAddr, nkHiddenAddr: + result = check(n.sons[0], ctx, map) + of nkIfStmt, nkIfExpr: + result = checkIf(n, ctx, map) + of nkAsgn: + result = checkAsgn(n[0], n[1], ctx, map) + of nkVarSection: + result.map = map + for child in n: + result = checkAsgn(child[0], child[2], ctx, result.map) + of nkForStmt: + result = checkFor(n, ctx, map) + of nkCaseStmt: + result = checkCase(n, ctx, map) + of nkReturnStmt: + result = checkReturn(n, ctx, map) + of nkBracketExpr: + result = checkBracketExpr(n, ctx, map) + of nkTryStmt: + result = checkTry(n, ctx, map) + of nkWhileStmt: + result = checkWhile(n, ctx, map) + of nkNone..pred(nkSym), succ(nkSym)..nkNilLit, nkTypeSection, nkProcDef, nkConverterDef, + nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo, + nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt, + nkExportStmt, nkPragma, nkCommentStmt, nkBreakState, nkTypeOfExpr: + + discard "don't follow this : same as varpartitions" + result = Check(nilability: Nil, map: map) + else: + + var elementMap = map.copyMap() + var elementCheck: Check + elementCheck.map = elementMap + for element in n: + elementCheck = check(element, ctx, elementCheck.map) + + result = Check(nilability: Nil, map: elementCheck.map) + + + + +proc typeNilability(typ: PType): Nilability = + assert not typ.isNil + # echo "typeNilability ", $typ.flags, " ", $typ.kind + result = if tfNotNil in typ.flags: + Safe + elif typ.kind in {tyRef, tyCString, tyPtr, tyPointer}: + # + # tyVar ? tyVarargs ? tySink ? tyLent ? + # TODO spec? tests? + MaybeNil + else: + Safe + # echo " result ", result + +proc preVisitNode(ctx: NilCheckerContext, node: PNode, conf: ConfigRef) = + # echo "visit node ", node + if node.kind in {nkSym, nkDotExpr} or isConstBracket(node): + let nodeSymbol = symbol(node) + if not ctx.symbolIndices.hasKey(nodeSymbol): + ctx.symbolIndices[nodeSymbol] = ctx.expressions.len + ctx.expressions.add(node) + if node.kind in {nkDotExpr, nkBracketExpr}: + if node.kind == nkDotExpr and (not node.typ.isNil and node.typ.kind == tyRef and tfNotNil notin node.typ.flags) or + node.kind == nkBracketExpr: + let index = ctx.symbolIndices[nodeSymbol] + var baseIndex = noExprIndex + # deref usually? + # ok, we hit another case + var base = if node[0].kind notin {nkSym, nkIdent}: node[0][0] else: node[0] + if base.kind != nkIdent: + let baseSymbol = symbol(base) + if not ctx.symbolIndices.hasKey(baseSymbol): + baseIndex = ctx.expressions.len # next visit should add it + else: + baseIndex = ctx.symbolIndices[baseSymbol] + if ctx.dependants.len <= baseIndex: + ctx.dependants.setLen(baseIndex + 1.ExprIndex) + ctx.dependants[baseIndex].incl(index.int) + case node.kind: + of nkSym, nkEmpty, nkNilLit, nkType, nkIdent, nkCharLit .. nkUInt64Lit, nkFloatLit .. nkFloat64Lit, nkStrLit .. nkTripleStrLit: + discard + of nkDotExpr: + # visit only the base + ctx.preVisitNode(node[0], conf) + else: + for element in node: + ctx.preVisitNode(element, conf) + +proc preVisit(ctx: NilCheckerContext, s: PSym, body: PNode, conf: ConfigRef) = + ctx.symbolIndices = {resultId: resultExprIndex}.toTable() + var cache = newIdentCache() + ctx.expressions = SeqOfDistinct[ExprIndex, PNode](@[newIdentNode(cache.getIdent("result"), s.ast.info)]) + var emptySet: IntSet # set[ExprIndex] + ctx.dependants = SeqOfDistinct[ExprIndex, IntSet](@[emptySet]) + for i, arg in s.typ.n.sons: + if i > 0: + if arg.kind != nkSym: + continue + let argSymbol = symbol(arg) + if not ctx.symbolIndices.hasKey(argSymbol): + ctx.symbolIndices[argSymbol] = ctx.expressions.len + ctx.expressions.add(arg) + ctx.preVisitNode(body, conf) + if ctx.dependants.len < ctx.expressions.len: + ctx.dependants.setLen(ctx.expressions.len) + # echo ctx.symbolIndices + # echo ctx.expressions + # echo ctx.dependants + +proc checkNil*(s: PSym; body: PNode; conf: ConfigRef, idgen: IdGenerator) = + let line = s.ast.info.line + let fileIndex = s.ast.info.fileIndex.int + var filename = conf.m.fileInfos[fileIndex].fullPath.string + + var context = NilCheckerContext(config: conf, idgen: idgen) + context.preVisit(s, body, conf) + var map = newNilMap(nil, context.symbolIndices.len) + + for i, child in s.typ.n.sons: + if i > 0: + if child.kind != nkSym: + continue + map.store(context, context.index(child), typeNilability(child.typ), TArg, child.info, child) + + map.store(context, resultExprIndex, if not s.typ[0].isNil and s.typ[0].kind == tyRef: Nil else: Safe, TResult, s.ast.info) + + # echo "checking ", s.name.s, " ", filename + + let res = check(body, context, map) + var canCheck = resultExprIndex in res.map.history.low .. res.map.history.high + if res.nilability == Safe and canCheck and res.map.history[resultExprIndex].len <= 1: + res.map.store(context, resultExprIndex, Safe, TAssign, s.ast.info) + else: + if res.nilability == Safe: + res.map.store(context, resultExprIndex, Safe, TAssign, s.ast.info) + + # TODO check for nilability result + # (ANotNil, BNotNil) : + # do we check on asgn nilability at all? + + if not s.typ[0].isNil and s.typ[0].kind == tyRef and tfNotNil in s.typ[0].flags: + checkResult(s.ast, context, res.map) diff --git a/compiler/options.nim b/compiler/options.nim index a5f262f01..87796539c 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -176,7 +176,8 @@ type ## Note: this feature can't be localized with {.push.} vmopsDanger, strictFuncs, - views + views, + strictNotNil LegacyFeature* = enum allowSemcheckedAstModification, diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim index 42529e1e5..792488c9f 100644 --- a/compiler/semobjconstr.nim +++ b/compiler/semobjconstr.nim @@ -153,6 +153,7 @@ proc collectMissingFields(c: PContext, fieldsRecList: PNode, if assignment == nil: constrCtx.missingFields.add r.sym + proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext, flags: TExprFlags): InitStatus = diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 35b1473c5..c0ae7471d 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -10,7 +10,7 @@ import intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees, wordrecg, strutils, options, guards, lineinfos, semfold, semdata, - modulegraphs, varpartitions, typeallowed + modulegraphs, varpartitions, typeallowed, nilcheck when defined(useDfa): import dfa @@ -1344,7 +1344,10 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) = when defined(useDfa): if s.name.s == "testp": dataflowAnalysis(s, body) + when false: trackWrites(s, body) + if strictNotNil in c.features and s.kind == skProc: + checkNil(s, body, g.config, c.idgen) proc trackStmt*(c: PContext; module: PSym; n: PNode, isTopLevel: bool) = if n.kind in {nkPragma, nkMacroDef, nkTemplateDef, nkProcDef, nkFuncDef, diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 827087c3d..d09b4c5bf 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -917,6 +917,7 @@ proc semAnyRef(c: PContext; n: PNode; kind: TTypeKind; prev: PType): PType = # check every except the last is an object: for i in isCall..= low(int)) and (n.intVal <= high(int)): result = result !& int(n.intVal) @@ -33,6 +34,9 @@ proc hashTree(n: PNode): Hash = else: for i in 0.. + +In the second case it would check builtin and imported modules as well. + +It checks the nilability of ref-like types and makes dereferencing safer based on flow typing and ``not nil`` annotations. + +Its implementation is different than the ``notnil`` one: defined under ``strictNotNil``. Keep in mind the difference in option names, be careful with distinguishing them. + +We check several kinds of types for nilability: + +- ref types +- pointer types +- proc types +- cstrings + +nil +------- + +The default kind of nilability types is the nilable kind: they can have the value ``nil``. +If you have a non-nilable type ``T``, you can use ``T nil`` to get a nilable type for it. + + +not nil +-------- + +You can annotate a type where nil isn't a valid value with ``not nil``. + +.. code-block:: nim + type + NilableObject = ref object + a: int + Object = NilableObject not nil + + Proc = (proc (x, y: int)) + + proc p(x: Object) = + echo x.a # ensured to dereference without an error + # compiler catches this: + p(nil) + # and also this: + var x: NilableObject + if x.isNil: + p(x) + else: + p(x) # ok + + + +If a type can include ``nil`` as a valid value, dereferencing values of the type +is checked by the compiler: if a value which might be nil is derefenced, this produces a warning by default, you can turn this into an error using the compiler options ``--warningAsError:strictNotNil`` + +If a type is nilable, you should dereference its values only after a ``isNil`` or equivalent check. + +local turn on/off +--------------------- + +You can still turn off nil checking on function/module level by using a ``{.strictNotNil: off}.`` pragma. +Note: test that/TODO for code/manual. + +nilability state +----------------- + +Currently a nilable value can be ``Safe``, ``MaybeNil`` or ``Nil`` : we use internally ``Parent`` and ``Unreachable`` but this is an implementation detail(a parent layer has the actual nilability). + +``Safe`` means it shouldn't be nil at that point: e.g. after assignment to a non-nil value or ``not a.isNil`` check +``MaybeNil`` means it might be nil, but it might not be nil: e.g. an argument, a call argument or a value after an ``if`` and ``else``. +``Nil`` means it should be nil at that point; e.g. after an assignment to ``nil`` or a ``.isNil`` check. + +``Unreachable`` means it shouldn't be possible to access this in this branch: so we do generate a warning as well. + +We show an error for each dereference (``[]``, ``.field``, ``[index]`` ``()`` etc) which is of a tracked expression which is +in ``MaybeNil`` or ``Nil`` state. + + +type nilability +---------------- + +Types are either nilable or non-nilable. +When you pass a param or a default value, we use the type : for nilable types we return ``MaybeNil`` +and for non-nilable ``Safe``. + +TODO: fix the manual here. (This is not great, as default values for non-nilables and nilables are usually actually ``nil`` , so we should think a bit more about this section.) + +params rules +------------ + +Param's nilability is detected based on type nilability. We use the type of the argument to detect the nilability. + + +assignment rules +----------------- + +Let's say we have ``left = right``. + +When we assign, we pass the right's nilability to the left's expression. There should be special handling of aliasing and compound expressions which we specify in their sections. (Assignment is a possible alias ``move`` or ``move out``). + +call args rules +----------------- + +When we call with arguments, we have two cases when we might change the nilability. + +.. code-block:: nim + callByVar(a) + +Here ``callByVar`` can re-assign ``a``, so this might change ``a``'s nilability, so we change it to ``MaybeNil``. +This is also a possible aliasing ``move out`` (moving out of a current alias set). + +.. code-block:: nim + call(a) + +Here ``call`` can change a field or element of ``a``, so if we have a dependant expression of ``a`` : e.g. ``a.field``. Dependats become ``MaybeNil``. + + +branches rules +--------------- + +Branches are the reason we do nil checking like this: with flow checking. +Sources of brancing are ``if``, ``while``, ``for``, ``and``, ``or``, ``case``, ``try`` and combinations with ``return``, ``break``, ``continue`` and ``raise`` + +We create a new layer/"scope" for each branch where we map expressions to nilability. This happens when we "fork": usually on the beginning of a construct. +When branches "join" we usually unify their expression maps or/and nilabilities. + +Merging usually merges maps and alias sets: nilabilities are merged like this: + +.. code-block:: nim + template union(l: Nilability, r: Nilability): Nilability = + ## unify two states + if l == r: + l + else: + MaybeNil + +Special handling is for ``.isNil`` and `` == nil``, also for ``not``, ``and`` and ``or``. + +``not`` reverses the nilability, ``and`` is similar to "forking" : the right expression is checked in the layer resulting from the left one and ``or`` is similar to "merging": the right and left expression should be both checked in the original layer. + +``isNil``, ``== nil`` make expressions ``Nil``. If there is a ``not`` or ``!= nil``, they make them ``Safe``. +We also reverse the nilability in the opposite branch: e.g. ``else``. + +compound expressions: field, index expressions +----------------------------------------------- + +We want to track also field(dot) and index(bracket) expressions. + +We track some of those compound expressions which might be nilable as dependants of their bases: ``a.field`` is changed if ``a`` is moved (re-assigned), +similarly ``a[index]`` is dependent on ``a`` and ``a.field.field`` on ``a.field``. + +When we move the base, we update dependants to ``MaybeNil``. Otherwise we usually start with type nilability. + +When we call args, we update the nilability of their dependants to ``MaybeNil`` as the calls usually can change them. +We might need to check for ``strictFuncs`` pure funcs and not do that then. + +For field expressions ``a.field``, we calculate an integer value based on a hash of the tree and just accept equivalent trees as equivalent expressions. + +For item expression ``a[index]``, we also calculate an integer value based on a hash of the tree and accept equivalent trees as equivalent expressions: for static values only. +For now we support only constant indices: we dont track expression with no-const indices. For those we just report a warning even if they are safe for now: one can use a local variable to workaround. For loops this might be annoying: so one should be able to turn off locally the warning using the ``{.warning[StrictCheckNotNil]:off}.``. + +For bracket expressions, in the future we might count ``a[]`` as the same general expression. +This means we should should the index but otherwise handle it the same for assign (maybe "aliasing" all the non-static elements) and differentiate only for static: e.g. ``a[0]`` and ``a[1]``. + +element tracking +----------------- + +When we assign an object construction, we should track the fields as well: + + +.. code-block:: nim + var a = Nilable(field: Nilable()) # a : Safe, a.field: Safe + +Usually we just track the result of an expression: probably this should apply for elements in other cases as well. +Also related to tracking initialization of expressions/fields. + +unstructured control flow rules +------------------------- + +Unstructured control flow keywords as ``return``, ``break``, ``continue``, ``raise`` mean that we jump from a branch out. +This means that if there is code after the finishing of the branch, it would be ran if one hasn't hit the direct parent branch of those: so it is similar to an ``else``. In those cases we should use the reverse nilabilities for the local to the condition expressions. E.g. + +.. code-block:: nim + for a in c: + if not a.isNil: + b() + break + code # here a: Nil , because if not, we would have breaked + + +aliasing +------------ + +We support alias detection for local expressions. + +We track sets of aliased expressions. We start with all nilable local expressions in separate sets. +Assignments and other changes to nilability can move / move out expressions of sets. + +``move``: Moving ``left`` to ``right`` means we remove ``left`` from its current set and unify it with the ``right``'s set. +This means it stops being aliased with its previous aliases. + +.. code-block:: nim + var left = b + left = right # moving left to right + +``move out``: Moving out ``left`` might remove it from the current set and ensure that it's in its own set as a single element. +e.g. + + +.. code-block:: nim + var left = b + left = nil # moving out + + +initialization of non nilable and nilable values +------------------------------------------------- + +TODO + +warnings and errors +--------------------- + +We show an error for each dereference (`[]`, `.field`, `[index]` `()` etc) which is of a tracked expression which is +in ``MaybeNil`` or ``Nil`` state. + +We might also show a history of the transitions and the reasons for them that might change the nilability of the expression. + diff --git a/tests/strictnotnil/tnilcheck.nim b/tests/strictnotnil/tnilcheck.nim new file mode 100644 index 000000000..5b9292522 --- /dev/null +++ b/tests/strictnotnil/tnilcheck.nim @@ -0,0 +1,382 @@ +discard """ +cmd: "nim check $file" +action: "reject" +""" + +import tables + +{.experimental: "strictNotNil".} + +type + Nilable* = ref object + a*: int + field*: Nilable + + NonNilable* = Nilable not nil + + Nilable2* = nil NonNilable + + +# proc `[]`(a: Nilable, b: int): Nilable = +# nil + + +# Nilable tests + + + +# test deref +proc testDeref(a: Nilable) = + echo a.a > 0 #[tt.Warning + ^ can't deref a, it might be nil + ]# + + + +# # # test if else +proc testIfElse(a: Nilable) = + if a.isNil: + echo a.a #[tt.Warning + ^ can't deref a, it is nil + ]# + else: + echo a.a # ok + +proc testIfNoElse(a: Nilable) = + if a.isNil: + echo a.a #[tt.Warning + ^ can't deref a, it is nil + ]# + echo a.a #[tt.Warning + ^ can't deref a, it might be nil + ]# + +proc testIfReturn(a: Nilable) = + if not a.isNil: + return + echo a.a #[tt.Warning + ^ can't deref a, it is nil + ]# + +proc testIfBreak(a: seq[Nilable]) = + for b in a: + if not b.isNil: + break + echo b.a #[tt.Warning + ^ can't deref b, it is nil + ]# + +proc testIfContinue(a: seq[Nilable]) = + for b in a: + if not b.isNil: + continue + echo b.a #[tt.Warning + ^ can't deref b, it is nil + ]# + +proc testIfRaise(a: Nilable) = + if not a.isNil: + raise newException(ValueError, "") + echo a.a #[tt.Warning + ^ can't deref a, it is nil + ]# + +proc testIfElif(a: Nilable) = + var c = 0 + if c == 0: + echo a.a #[tt.Warning + ^ can't deref a, it might be nil + ]# + elif c == 1: + echo a.a #[tt.Warning + ^ can't deref a, it might be nil + ]# + elif not a.isNil: + echo a.a # ok + elif c == 2: + echo 0 + else: + echo a.a #[tt.Warning + ^ can't deref a, it is nil + ]# + +proc testAssignUnify(a: Nilable, b: int) = + var a2 = a + if b == 0: + a2 = Nilable() + echo a2.a #[tt.Warning + ^ can't deref a2, it might be nil + ]# + + +# # test assign in branch and unifiying that with the main block after end of branch +proc testAssignUnifyNil(a: Nilable, b: int) = + var a2 = a + if b == 0: + a2 = nil + echo a2.a #[tt.Warning + ^ can't deref a2, it might be nil + ]# + +# test loop +proc testForLoop(a: Nilable) = + var b = Nilable() + for i in 0 .. 5: + echo b.a #[tt.Warning + ^ can't deref b, it might be nil + ]# + if i == 2: + b = a + echo b.a #[tt.Warning + ^ can't deref b, it might be nil + ]# + + + +# # TODO implement this after discussion +# # proc testResultCompoundNonNilableElement(a: Nilable): (NonNilable, NonNilable) = #[t t.Warning +# # ^ result might be not initialized, so it or an element might be nil +# # ]# +# # if not a.isNil: +# # result[0] = a #[t t.Warning +# # ^ can't assign nilable to non nilable: it might be nil +# # #] + +# # proc testNonNilDeref(a: NonNilable) = +# # echo a.a # ok + + + +# # # not only calls: we can use partitions for dependencies for field aliases +# # # so we can detect on change what does this affect or was this mutated between us and the original field + +# # proc testRootAliasField(a: Nilable) = +# # var aliasA = a +# # if not a.isNil and not a.field.isNil: +# # aliasA.field = nil +# # # a.field = nil +# # # aliasA = nil +# # echo a.field.a # [tt.Warning +# # ^ can't deref a.field, it might be nil +# # ]# + + +proc testAliasChanging(a: Nilable) = + var b = a + var aliasA = b + b = Nilable() + if not b.isNil: + echo aliasA.a #[tt.Warning + ^ can't deref aliasA, it might be nil + ]# + +# # TODO +# # proc testAliasUnion(a: Nilable) = +# # var a2 = a +# # var b = a2 +# # if a.isNil: +# # b = Nilable() +# # a2 = nil +# # else: +# # a2 = Nilable() +# # b = a2 +# # if not b.isNil: +# # echo a2.a #[ tt.Warning +# # ^ can't deref a2, it might be nil +# # ]# + +# # TODO after alias support +# #proc callVar(a: var Nilable) = +# # a.field = nil + + +# # TODO ptr support +# # proc testPtrAlias(a: Nilable) = +# # # pointer to a: hm. +# # # alias to a? +# # var ptrA = a.unsafeAddr # {0, 1} +# # if not a.isNil: # {0, 1} +# # ptrA[] = nil # {0, 1} 0: MaybeNil 1: MaybeNil +# # echo a.a #[ tt.Warning +# # ^ can't deref a, it might be nil +# # ]# + +# # TODO field stuff +# # currently it just doesnt support dot, so accidentally it shows a warning but because that +# # not alias i think +# # proc testFieldAlias(a: Nilable) = +# # var b = a # {0, 1} {2} +# # if not a.isNil and not a.field.isNil: # {0, 1} {2} +# # callVar(b) # {0, 1} {2} 0: Safe 1: Safe +# # echo a.field.a #[ tt.Warning +# # ^ can't deref a.field, it might be nil +# # ]# +# # +# # proc testUniqueHashTree(a: Nilable): Nilable = +# # # TODO what would be a clash +# # var field = 0 +# # if not a.isNil and not a.field.isNil: +# # # echo a.field.a +# # echo a[field].a +# # result = Nilable() + +# # proc testSeparateShadowingResult(a: Nilable): Nilable = +# # result = Nilable() +# # if not a.isNil: +# # var result: Nilable = nil +# # echo result.a + + +proc testCStringDeref(a: cstring) = + echo a[0] #[tt.Warning + ^ can't deref a, it might be nil + ]# + + +proc testNilablePtr(a: ptr int) = + if not a.isNil: + echo a[] # ok + echo a[] #[tt.Warning + ^ can't deref a, it might be nil + ]# + +# # proc testNonNilPtr(a: ptr int not nil) = +# # echo a[] # ok + +proc raiseCall: NonNilable = #[tt.Warning +^ return value is nil +]# + raise newException(ValueError, "raise for test") + +# proc testTryCatch(a: Nilable) = +# var other = a +# try: +# other = raiseCall() +# except: +# discard +# echo other.a #[ tt.Warning +# ^ can't deref other, it might be nil +# ]# + +# # proc testTryCatchDetectNoRaise(a: Nilable) = +# # var other = Nilable() +# # try: +# # other = nil +# # other = a +# # other = Nilable() +# # except: +# # other = nil +# # echo other.a # ok + +# # proc testTryCatchDetectFinally = +# # var other = Nilable() +# # try: +# # other = nil +# # other = Nilable() +# # except: +# # other = Nilable() +# # finally: +# # other = nil +# # echo other.a # can't deref other: it is nil + +# # proc testTryCatchDetectNilableWithRaise(b: bool) = +# # var other = Nilable() +# # try: +# # if b: +# # other = nil +# # else: +# # other = Nilable() +# # var other2 = raiseCall() +# # except: +# # echo other.a # ok + +# # echo other.a # can't deref a: it might be nil + +# # proc testRaise(a: Nilable) = +# # if a.isNil: +# # raise newException(ValueError, "a == nil") +# # echo a.a # ok + + +# # proc testBlockScope(a: Nilable) = +# # var other = a +# # block: +# # var other = Nilable() +# # echo other.a # ok +# # echo other.a # can't deref other: it might be nil + +# # ok we can't really get the nil value from here, so should be ok +# # proc testDirectRaiseCall: NonNilable = +# # var a = raiseCall() +# # result = NonNilable() + +# # proc testStmtList = +# # var a = Nilable() +# # block: +# # a = nil +# # a = Nilable() +# # echo a.a # ok + +proc callChange(a: Nilable) = + if not a.isNil: + a.field = nil + +proc testCallChangeField = + var a = Nilable() + a.field = Nilable() + callChange(a) + echo a.field.a #[ tt.Warning + ^ can't deref a.field, it might be nil + ]# + +proc testReassignVarWithField = + var a = Nilable() + a.field = Nilable() + echo a.field.a # ok + a = Nilable() + echo a.field.a #[ tt.Warning + ^ can't deref a.field, it might be nil + ]# + + +proc testItemDeref(a: var seq[Nilable]) = + echo a[0].a #[tt.Warning + ^ can't deref a[0], it might be nil + ]# + a[0] = Nilable() # good: now .. if we dont track, how do we know + echo a[0].a # ok + echo a[1].a #[tt.Warning + ^ can't deref a[1], it might be nil + ]# + var b = 1 + if a[b].isNil: + echo a[1].a #[tt.Warning + ^ can't deref a[1], it might be nil + ]# + var c = 0 + echo a[c].a #[tt.Warning + ^ can't deref a[c], it might be nil + ]# + + # known false positive + if not a[b].isNil: + echo a[b].a #[tt.Warning + ^ can't deref a[b], it might be nil + ]# + + const c = 0 + if a[c].isNil: + echo a[0].a #[tt.Warning + ^ can't deref a[0], it is nil + ]# + a[c] = Nilable() + echo a[0].a # ok + + + +# # # proc test10(a: Nilable) = +# # # if not a.isNil and not a.b.isNil: +# # # c_memset(globalA.addr, 0, globalA.sizeOf.csize_t) +# # # globalA = nil +# # # echo a.a # can't deref a: it might be nil + diff --git a/tests/strictnotnil/tnilcheck_no_warnings.nim b/tests/strictnotnil/tnilcheck_no_warnings.nim new file mode 100644 index 000000000..5ec9bc575 --- /dev/null +++ b/tests/strictnotnil/tnilcheck_no_warnings.nim @@ -0,0 +1,182 @@ +discard """ +cmd: "nim check --warningAsError:StrictNotNil $file" +action: "compile" +""" + +import tables + +{.experimental: "strictNotNil".} + +type + Nilable* = ref object + a*: int + field*: Nilable + + NonNilable* = Nilable not nil + + Nilable2* = nil NonNilable + + +# proc `[]`(a: Nilable, b: int): Nilable = +# nil + + +# Nilable tests + + + +# # test and +proc testAnd(a: Nilable) = + echo not a.isNil and a.a > 0 # ok + + +# test else branch and inferring not isNil +# proc testElse(a: Nilable, b: int) = +# if a.isNil: +# echo 0 +# else: +# echo a.a + +# test that here we can infer that n can't be nil anymore +proc testNotNilAfterAssign(a: Nilable, b: int) = + var n = a # a: MaybeNil n: MaybeNil + if n.isNil: # n: Safe a: MaybeNil + n = Nilable() # n: Safe a: MaybeNil + echo n.a # ok + +proc callVar(a: var Nilable) = + a = nil + +proc testVarAlias(a: Nilable) = # a: 0 aliasA: 1 {0} {1} + var aliasA = a # {0, 1} 0 MaybeNil 1 MaybeNil + if not a.isNil: # {0, 1} 0 Safe 1 Safe + callVar(aliasA) # {0, 1} 0 MaybeNil 1 MaybeNil + # if aliasA stops being in alias: it might be nil, but then a is still not nil + # if not: it cant be nil as it still points here + echo a.a # ok + +proc testAliasCheck(a: Nilable) = + var aliasA = a + if not a.isNil: + echo aliasA.a # ok + +proc testFieldCheck(a: Nilable) = + if not a.isNil and not a.field.isNil: + echo a.field.a # ok + +proc testTrackField = + var a = Nilable(field: Nilable()) + echo a.field.a # ok + +proc testNonNilDeref(a: NonNilable) = + echo a.a # ok + +# # not only calls: we can use partitions for dependencies for field aliases +# # so we can detect on change what does this affect or was this mutated between us and the original field + + +# proc testUniqueHashTree(a: Nilable): Nilable = +# # TODO what would be a clash +# var field = 0 +# if not a.isNil and not a.field.isNil: +# # echo a.field.a +# echo a[field].a +# result = Nilable() + +proc testSeparateShadowingResult(a: Nilable): Nilable = + result = Nilable() + if not a.isNil: + var result: Nilable = nil + echo result.a + + +proc testNonNilCString(a: cstring not nil) = + echo a[0] # ok + +proc testNonNilPtr(a: ptr int not nil) = + echo a[] # ok + + +# proc testTryCatchDetectNoRaise(a: Nilable) = +# var other = Nilable() +# try: +# other = nil +# other = a +# other = Nilable() +# except: +# other = nil +# echo other.a # ok + +# proc testTryCatchDetectFinally = +# var other = Nilable() +# try: +# other = nil +# other = Nilable() +# except: +# other = Nilable() +# finally: +# other = nil +# echo other.a # can't deref other: it is nil + +# proc testTryCatchDetectNilableWithRaise(b: bool) = +# var other = Nilable() +# try: +# if b: +# other = nil +# else: +# other = Nilable() +# var other2 = raiseCall() +# except: +# echo other.a # ok + +# echo other.a # can't deref a: it might be nil + +proc testRaise(a: Nilable) = + if a.isNil: + raise newException(ValueError, "a == nil") + echo a.a # ok + +# proc testBlockScope(a: Nilable) = +# var other = a +# block: +# var other = Nilable() +# echo other.a # ok +# echo other.a # can't deref other: it might be nil + +# # (ask Araq about this: not supported yet) ok we can't really get the nil value from here, so should be ok +# proc testDirectRaiseCall: NonNilable = +# var a = raiseCall() +# result = NonNilable() + +proc testStmtList = + var a = Nilable() + block: + a = nil + a = Nilable() + echo a.a # ok + +proc testItemDerefNoWarning(a: var seq[Nilable]) = + a[0] = Nilable() # good: now .. if we dont track, how do we know + echo a[0].a # ok + var b = 1 + + const c = 0 + a[c] = Nilable() + echo a[0].a # ok + +# proc callChange(a: Nilable) = +# a.field = nil + +# proc testCallAlias = +# var a = Nilable(field: Nilable()) +# callChange(a) +# echo a.field.a # can't deref a.field, it might be nil + +# # proc test10(a: Nilable) = +# # if not a.isNil and not a.b.isNil: +# # c_memset(globalA.addr, 0, globalA.sizeOf.csize_t) +# # globalA = nil +# # echo a.a # can't deref a: it might be nil + +var nilable: Nilable +var withField = Nilable(a: 0, field: Nilable()) From c9886a49528c08784b1f8e1f7b907024170973dc Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 29 Dec 2020 02:12:05 -0800 Subject: [PATCH 1226/3768] use -d:nimCompilerStackraceHints in more places (#16400) --- compiler/ccgexprs.nim | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index ef3ceae62..45044e0ff 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -9,6 +9,9 @@ # included from cgen.nim +when defined(nimCompilerStackraceHints): + import std/stackframes + proc getNullValueAuxT(p: BProc; orig, t: PType; obj, constOrNil: PNode, result: var Rope; count: var int; isConst: bool, info: TLineInfo) @@ -2643,6 +2646,8 @@ proc exprComplexConst(p: BProc, n: PNode, d: var TLoc) = d.storage = OnStatic proc expr(p: BProc, n: PNode, d: var TLoc) = + when defined(nimCompilerStackraceHints): + setFrameMsg p.config$n.info & " " & $n.kind p.currLineInfo = n.info case n.kind From fc1a4faf568424b221e6690257ee96c66e68408e Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 29 Dec 2020 04:26:10 -0800 Subject: [PATCH 1227/3768] improve turi.nim tests: js, vm; merge tdecodequery.nim (#16500) * improve turi tests: js, vm; merge tdecodequery.nim * improve test * add test in js, improve test * remove matrix: "--styleCheck:hint --panics:on" * fixup --- tests/stdlib/tdecodequery.nim | 7 - tests/stdlib/turi.nim | 344 +++++++++++++++++----------------- 2 files changed, 169 insertions(+), 182 deletions(-) delete mode 100644 tests/stdlib/tdecodequery.nim diff --git a/tests/stdlib/tdecodequery.nim b/tests/stdlib/tdecodequery.nim deleted file mode 100644 index ae180742f..000000000 --- a/tests/stdlib/tdecodequery.nim +++ /dev/null @@ -1,7 +0,0 @@ -import std/[uri, sequtils] - - -block: - doAssert toSeq(decodeQuery("a=1&b=0")) == @[("a", "1"), ("b", "0")] - doAssertRaises(UriParseError): - discard toSeq(decodeQuery("a=1&b=2c=6")) diff --git a/tests/stdlib/turi.nim b/tests/stdlib/turi.nim index 62fb17e4d..6354850fc 100644 --- a/tests/stdlib/turi.nim +++ b/tests/stdlib/turi.nim @@ -1,24 +1,14 @@ discard """ - cmd: "nim c -r --styleCheck:hint --panics:on $options $file" - targets: "c" - nimout: "" - action: "run" - exitcode: 0 - timeout: 60.0 + targets: "c js" + joinable: false # because of `include uri` """ -include uri +# import std/uri # pending https://github.com/nim-lang/Nim/pull/11865 +include uri # because of `removeDotSegments` +from std/sequtils import toSeq -block: - let org = "udp://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:8080" - let url = parseUri(org) - doAssert url.hostname == "2001:0db8:85a3:0000:0000:8a2e:0370:7334" # true - let newUrl = parseUri($url) - doAssert newUrl.hostname == "2001:0db8:85a3:0000:0000:8a2e:0370:7334" # true - - -block: - block: +template main() = + block: # encodeUrl, decodeUrl const test1 = "abc\L+def xyz" doAssert encodeUrl(test1) == "abc%0A%2Bdef+xyz" doAssert decodeUrl(encodeUrl(test1)) == test1 @@ -26,178 +16,177 @@ block: doAssert decodeUrl(encodeUrl(test1, false), false) == test1 doAssert decodeUrl(encodeUrl(test1)) == test1 - block: - let str = "http://localhost" - let test = parseUri(str) - doAssert test.path == "" + block: # parseUri + block: + let org = "udp://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:8080" + let url = parseUri(org) + doAssert url.hostname == "2001:0db8:85a3:0000:0000:8a2e:0370:7334" # true + let newUrl = parseUri($url) + doAssert newUrl.hostname == "2001:0db8:85a3:0000:0000:8a2e:0370:7334" # true - block: - let str = "http://localhost/" - let test = parseUri(str) - doAssert test.path == "/" + block: + let str = "http://localhost" + let test = parseUri(str) + doAssert test.path == "" - block: - let str = "http://localhost:8080/test" - let test = parseUri(str) - doAssert test.scheme == "http" - doAssert test.port == "8080" - doAssert test.path == "/test" - doAssert test.hostname == "localhost" - doAssert($test == str) + block: + let str = "http://localhost/" + let test = parseUri(str) + doAssert test.path == "/" - block: - let str = "foo://username:password@example.com:8042/over/there" & - "/index.dtb?type=animal&name=narwhal#nose" - let test = parseUri(str) - doAssert test.scheme == "foo" - doAssert test.username == "username" - doAssert test.password == "password" - doAssert test.hostname == "example.com" - doAssert test.port == "8042" - doAssert test.path == "/over/there/index.dtb" - doAssert test.query == "type=animal&name=narwhal" - doAssert test.anchor == "nose" - doAssert($test == str) + block: + let str = "http://localhost:8080/test" + let test = parseUri(str) + doAssert test.scheme == "http" + doAssert test.port == "8080" + doAssert test.path == "/test" + doAssert test.hostname == "localhost" + doAssert($test == str) - block: - # IPv6 address - let str = "foo://[::1]:1234/bar?baz=true&qux#quux" - let uri = parseUri(str) - doAssert uri.scheme == "foo" - doAssert uri.hostname == "::1" - doAssert uri.port == "1234" - doAssert uri.path == "/bar" - doAssert uri.query == "baz=true&qux" - doAssert uri.anchor == "quux" + block: + let str = "foo://username:password@example.com:8042/over/there" & + "/index.dtb?type=animal&name=narwhal#nose" + let test = parseUri(str) + doAssert test.scheme == "foo" + doAssert test.username == "username" + doAssert test.password == "password" + doAssert test.hostname == "example.com" + doAssert test.port == "8042" + doAssert test.path == "/over/there/index.dtb" + doAssert test.query == "type=animal&name=narwhal" + doAssert test.anchor == "nose" + doAssert($test == str) - block: - let str = "urn:example:animal:ferret:nose" - let test = parseUri(str) - doAssert test.scheme == "urn" - doAssert test.path == "example:animal:ferret:nose" - doAssert($test == str) + block: + # IPv6 address + let str = "foo://[::1]:1234/bar?baz=true&qux#quux" + let uri = parseUri(str) + doAssert uri.scheme == "foo" + doAssert uri.hostname == "::1" + doAssert uri.port == "1234" + doAssert uri.path == "/bar" + doAssert uri.query == "baz=true&qux" + doAssert uri.anchor == "quux" - block: - let str = "mailto:username@example.com?subject=Topic" - let test = parseUri(str) - doAssert test.scheme == "mailto" - doAssert test.username == "username" - doAssert test.hostname == "example.com" - doAssert test.query == "subject=Topic" - doAssert($test == str) + block: + let str = "urn:example:animal:ferret:nose" + let test = parseUri(str) + doAssert test.scheme == "urn" + doAssert test.path == "example:animal:ferret:nose" + doAssert($test == str) - block: - let str = "magnet:?xt=urn:sha1:72hsga62ba515sbd62&dn=foobar" - let test = parseUri(str) - doAssert test.scheme == "magnet" - doAssert test.query == "xt=urn:sha1:72hsga62ba515sbd62&dn=foobar" - doAssert($test == str) + block: + let str = "mailto:username@example.com?subject=Topic" + let test = parseUri(str) + doAssert test.scheme == "mailto" + doAssert test.username == "username" + doAssert test.hostname == "example.com" + doAssert test.query == "subject=Topic" + doAssert($test == str) - block: - let str = "/test/foo/bar?q=2#asdf" - let test = parseUri(str) - doAssert test.scheme == "" - doAssert test.path == "/test/foo/bar" - doAssert test.query == "q=2" - doAssert test.anchor == "asdf" - doAssert($test == str) + block: + let str = "magnet:?xt=urn:sha1:72hsga62ba515sbd62&dn=foobar" + let test = parseUri(str) + doAssert test.scheme == "magnet" + doAssert test.query == "xt=urn:sha1:72hsga62ba515sbd62&dn=foobar" + doAssert($test == str) - block: - let str = "test/no/slash" - let test = parseUri(str) - doAssert test.path == "test/no/slash" - doAssert($test == str) + block: + let str = "/test/foo/bar?q=2#asdf" + let test = parseUri(str) + doAssert test.scheme == "" + doAssert test.path == "/test/foo/bar" + doAssert test.query == "q=2" + doAssert test.anchor == "asdf" + doAssert($test == str) - block: - let str = "//git@github.com:dom96/packages" - let test = parseUri(str) - doAssert test.scheme == "" - doAssert test.username == "git" - doAssert test.hostname == "github.com" - doAssert test.port == "dom96" - doAssert test.path == "/packages" + block: + let str = "test/no/slash" + let test = parseUri(str) + doAssert test.path == "test/no/slash" + doAssert($test == str) - block: - let str = "file:///foo/bar/baz.txt" - let test = parseUri(str) - doAssert test.scheme == "file" - doAssert test.username == "" - doAssert test.hostname == "" - doAssert test.port == "" - doAssert test.path == "/foo/bar/baz.txt" + block: + let str = "//git@github.com:dom96/packages" + let test = parseUri(str) + doAssert test.scheme == "" + doAssert test.username == "git" + doAssert test.hostname == "github.com" + doAssert test.port == "dom96" + doAssert test.path == "/packages" - # Remove dot segments tests - block: + block: + let str = "file:///foo/bar/baz.txt" + let test = parseUri(str) + doAssert test.scheme == "file" + doAssert test.username == "" + doAssert test.hostname == "" + doAssert test.port == "" + doAssert test.path == "/foo/bar/baz.txt" + + block: # combine + block: + let concat = combine(parseUri("http://google.com/foo/bar/"), parseUri("baz")) + doAssert concat.path == "/foo/bar/baz" + doAssert concat.hostname == "google.com" + doAssert concat.scheme == "http" + + block: + let concat = combine(parseUri("http://google.com/foo"), parseUri("/baz")) + doAssert concat.path == "/baz" + doAssert concat.hostname == "google.com" + doAssert concat.scheme == "http" + + block: + let concat = combine(parseUri("http://google.com/foo/test"), parseUri("bar")) + doAssert concat.path == "/foo/bar" + + block: + let concat = combine(parseUri("http://google.com/foo/test"), parseUri("/bar")) + doAssert concat.path == "/bar" + + block: + let concat = combine(parseUri("http://google.com/foo/test"), parseUri("bar")) + doAssert concat.path == "/foo/bar" + + block: + let concat = combine(parseUri("http://google.com/foo/test/"), parseUri("bar")) + doAssert concat.path == "/foo/test/bar" + + block: + let concat = combine(parseUri("http://google.com/foo/test/"), parseUri("bar/")) + doAssert concat.path == "/foo/test/bar/" + + block: + let concat = combine(parseUri("http://google.com/foo/test/"), parseUri("bar/"), + parseUri("baz")) + doAssert concat.path == "/foo/test/bar/baz" + + block: # `/` + block: + let test = parseUri("http://example.com/foo") / "bar/asd" + doAssert test.path == "/foo/bar/asd" + + block: + let test = parseUri("http://example.com/foo/") / "/bar/asd" + doAssert test.path == "/foo/bar/asd" + + block: # removeDotSegments doAssert removeDotSegments("/foo/bar/baz") == "/foo/bar/baz" + doAssert removeDotSegments("") == "" # empty test - # Combine tests - block: - let concat = combine(parseUri("http://google.com/foo/bar/"), parseUri("baz")) - doAssert concat.path == "/foo/bar/baz" - doAssert concat.hostname == "google.com" - doAssert concat.scheme == "http" - - block: - let concat = combine(parseUri("http://google.com/foo"), parseUri("/baz")) - doAssert concat.path == "/baz" - doAssert concat.hostname == "google.com" - doAssert concat.scheme == "http" - - block: - let concat = combine(parseUri("http://google.com/foo/test"), parseUri("bar")) - doAssert concat.path == "/foo/bar" - - block: - let concat = combine(parseUri("http://google.com/foo/test"), parseUri("/bar")) - doAssert concat.path == "/bar" - - block: - let concat = combine(parseUri("http://google.com/foo/test"), parseUri("bar")) - doAssert concat.path == "/foo/bar" - - block: - let concat = combine(parseUri("http://google.com/foo/test/"), parseUri("bar")) - doAssert concat.path == "/foo/test/bar" - - block: - let concat = combine(parseUri("http://google.com/foo/test/"), parseUri("bar/")) - doAssert concat.path == "/foo/test/bar/" - - block: - let concat = combine(parseUri("http://google.com/foo/test/"), parseUri("bar/"), - parseUri("baz")) - doAssert concat.path == "/foo/test/bar/baz" - - # `/` tests - block: - let test = parseUri("http://example.com/foo") / "bar/asd" - doAssert test.path == "/foo/bar/asd" - - block: - let test = parseUri("http://example.com/foo/") / "/bar/asd" - doAssert test.path == "/foo/bar/asd" - - # removeDotSegments tests - block: - # empty test - doAssert removeDotSegments("") == "" - - # bug #3207 - block: + block: # bug #3207 doAssert parseUri("http://qq/1").combine(parseUri("https://qqq")).`$` == "https://qqq" - # bug #4959 - block: + block: # bug #4959 let foo = parseUri("http://example.com") / "/baz" doAssert foo.path == "/baz" - # bug found on stream 13/10/17 - block: + block: # bug found on stream 13/10/17 let foo = parseUri("http://localhost:9515") / "status" doAssert $foo == "http://localhost:9515/status" - # bug #6649 #6652 - block: + block: # bug #6649 #6652 var foo = parseUri("http://example.com") foo.hostname = "example.com" foo.path = "baz" @@ -224,8 +213,7 @@ block: foo.path = "relative" doAssert $foo == "file:relative" - # isAbsolute tests - block: + block: # isAbsolute tests doAssert "www.google.com".parseUri().isAbsolute() == false doAssert "http://www.google.com".parseUri().isAbsolute() == true doAssert "file:/dir/file".parseUri().isAbsolute() == true @@ -265,8 +253,7 @@ block: doAssert "https://example.com/about/staff.html?".parseUri().isAbsolute == true doAssert "https://example.com/about/staff.html?parameters".parseUri().isAbsolute == true - # encodeQuery tests - block: + block: # encodeQuery tests doAssert encodeQuery({:}) == "" doAssert encodeQuery({"foo": "bar"}) == "foo=bar" doAssert encodeQuery({"foo": "bar & baz"}) == "foo=bar+%26+baz" @@ -276,17 +263,17 @@ block: doAssert encodeQuery({"a": "1", "b": "", "c": "3"}) == "a=1&b&c=3" doAssert encodeQuery({"a": "1", "b": "", "c": "3"}, omitEq = false) == "a=1&b=&c=3" + block: # `?` block: var foo = parseUri("http://example.com") / "foo" ? {"bar": "1", "baz": "qux"} var foo1 = parseUri("http://example.com/foo?bar=1&baz=qux") doAssert foo == foo1 - block: var foo = parseUri("http://example.com") / "foo" ? {"do": "do", "bar": ""} var foo1 = parseUri("http://example.com/foo?do=do&bar") doAssert foo == foo1 - block dataUriBase64: + block: # getDataUri, dataUriBase64 doAssert getDataUri("", "text/plain") == "data:text/plain;charset=utf-8;base64," doAssert getDataUri(" ", "text/plain") == "data:text/plain;charset=utf-8;base64,IA==" doAssert getDataUri("c\xf7>", "text/plain") == "data:text/plain;charset=utf-8;base64,Y/c+" @@ -295,6 +282,13 @@ block: doAssert getDataUri("""!@#$%^&*()_+""", "text/plain") == "data:text/plain;charset=utf-8;base64,IUAjJCVeJiooKV8r" doAssert(getDataUri("the quick brown dog jumps over the lazy fox", "text/plain") == "data:text/plain;charset=utf-8;base64,dGhlIHF1aWNrIGJyb3duIGRvZyBqdW1wcyBvdmVyIHRoZSBsYXp5IGZveA==") - doAssert(getDataUri("""The present is theirs - The future, for which I really worked, is mine.""", "text/plain") == + doAssert(getDataUri("The present is theirs\n The future, for which I really worked, is mine.", "text/plain") == "data:text/plain;charset=utf-8;base64,VGhlIHByZXNlbnQgaXMgdGhlaXJzCiAgICAgIFRoZSBmdXR1cmUsIGZvciB3aGljaCBJIHJlYWxseSB3b3JrZWQsIGlzIG1pbmUu") + + block: # decodeQuery + doAssert toSeq(decodeQuery("a=1&b=0")) == @[("a", "1"), ("b", "0")] + doAssertRaises(UriParseError): + discard toSeq(decodeQuery("a=1&b=2c=6")) + +static: main() +main() From d5a3c2c2da2e3b2f6deeb2c1cd5430db90bc4fd5 Mon Sep 17 00:00:00 2001 From: Antonis Geralis <43617260+planetis-m@users.noreply.github.com> Date: Tue, 29 Dec 2020 14:27:08 +0200 Subject: [PATCH 1228/3768] Added cmpMem export (#16484) * added cmpMem export * updates * fix test * Tiny changelog change * Add a dot. Co-authored-by: Clyybber --- changelog.md | 6 ++- lib/system.nim | 2 + lib/system/memalloc.nim | 107 ++++++++++++++++++++++----------------- tests/stdlib/tmemory.nim | 15 ++++++ 4 files changed, 81 insertions(+), 49 deletions(-) create mode 100644 tests/stdlib/tmemory.nim diff --git a/changelog.md b/changelog.md index 6b12891c5..035ba46b8 100644 --- a/changelog.md +++ b/changelog.md @@ -36,6 +36,8 @@ - `nodejs` backend now supports osenv: `getEnv`, `putEnv`, `envPairs`, `delEnv`, `existsEnv`. +- Added `cmpMem` to `system`. + - `doAssertRaises` now correctly handles foreign exceptions. - Added `asyncdispatch.activeDescriptors` that returns the number of currently @@ -57,7 +59,7 @@ - Added `decodeQuery` to `std/uri`. - `strscans.scanf` now supports parsing single characters. -- `strscans.scanTuple` added which uses `strscans.scanf` internally, returning a tuple which can be unpacked for easier usage of `scanf`. +- `strscans.scanTuple` added which uses `strscans.scanf` internally, returning a tuple which can be unpacked for easier usage of `scanf`. - Added `setutils.toSet` that can take any iterable and convert it to a built-in set, if the iterable yields a built-in settable type. @@ -71,7 +73,7 @@ and `lists.toDoublyLinkedList` convert from `openArray`s; `lists.copy` implements shallow copying; `lists.add` concatenates two lists - an O(1) variation that consumes its argument, `addMoved`, is also supplied. - + - Added `sequtils` import to `prelude`. - Added `euclDiv` and `euclMod` to `math`. diff --git a/lib/system.nim b/lib/system.nim index fb008dc45..b6f7d655a 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2182,6 +2182,8 @@ when notJSnotNims: memTrackerOp("moveMem", dest, size) proc equalMem(a, b: pointer, size: Natural): bool = nimCmpMem(a, b, size) == 0 + proc cmpMem(a, b: pointer, size: Natural): int = + nimCmpMem(a, b, size) when not defined(js): proc cmp(x, y: string): int = diff --git a/lib/system/memalloc.nim b/lib/system/memalloc.nim index f5e8b2363..ffb5190ca 100644 --- a/lib/system/memalloc.nim +++ b/lib/system/memalloc.nim @@ -1,38 +1,51 @@ when notJSnotNims: proc zeroMem*(p: pointer, size: Natural) {.inline, noSideEffect, tags: [], locks: 0, raises: [].} - ## Overwrites the contents of the memory at ``p`` with the value 0. + ## Overwrites the contents of the memory at `p` with the value 0. ## - ## Exactly ``size`` bytes will be overwritten. Like any procedure + ## Exactly `size` bytes will be overwritten. Like any procedure ## dealing with raw memory this is **unsafe**. proc copyMem*(dest, source: pointer, size: Natural) {.inline, benign, tags: [], locks: 0, raises: [].} - ## Copies the contents from the memory at ``source`` to the memory - ## at ``dest``. - ## Exactly ``size`` bytes will be copied. The memory + ## Copies the contents from the memory at `source` to the memory + ## at `dest`. + ## Exactly `size` bytes will be copied. The memory ## regions may not overlap. Like any procedure dealing with raw ## memory this is **unsafe**. proc moveMem*(dest, source: pointer, size: Natural) {.inline, benign, tags: [], locks: 0, raises: [].} - ## Copies the contents from the memory at ``source`` to the memory - ## at ``dest``. + ## Copies the contents from the memory at `source` to the memory + ## at `dest`. ## - ## Exactly ``size`` bytes will be copied. The memory - ## regions may overlap, ``moveMem`` handles this case appropriately - ## and is thus somewhat more safe than ``copyMem``. Like any procedure + ## Exactly `size` bytes will be copied. The memory + ## regions may overlap, `moveMem` handles this case appropriately + ## and is thus somewhat more safe than `copyMem`. Like any procedure ## dealing with raw memory this is still **unsafe**, though. proc equalMem*(a, b: pointer, size: Natural): bool {.inline, noSideEffect, tags: [], locks: 0, raises: [].} - ## Compares the memory blocks ``a`` and ``b``. ``size`` bytes will + ## Compares the memory blocks `a` and `b`. `size` bytes will ## be compared. ## ## If the blocks are equal, `true` is returned, `false` ## otherwise. Like any procedure dealing with raw memory this is ## **unsafe**. + proc cmpMem*(a, b: pointer, size: Natural): int {.inline, noSideEffect, + tags: [], locks: 0, raises: [].} + ## Compares the memory blocks `a` and `b`. `size` bytes will + ## be compared. + ## + ## Returns: + ## * a value less than zero, if `a < b` + ## * a value greater than zero, if `a > b` + ## * zero, if `a == b` + ## + ## Like any procedure dealing with raw memory this is + ## **unsafe**. + when hasAlloc and not defined(js): proc allocImpl*(size: Natural): pointer {.noconv, rtl, tags: [], benign, raises: [].} @@ -75,7 +88,7 @@ when hasAlloc and not defined(js): proc getAllocStats*(): AllocStats = discard template alloc*(size: Natural): pointer = - ## Allocates a new memory block with at least ``size`` bytes. + ## Allocates a new memory block with at least `size` bytes. ## ## The block has to be freed with `realloc(block, 0) <#realloc.t,pointer,Natural>`_ ## or `dealloc(block) <#dealloc,pointer>`_. @@ -91,7 +104,7 @@ when hasAlloc and not defined(js): allocImpl(size) proc createU*(T: typedesc, size = 1.Positive): ptr T {.inline, benign, raises: [].} = - ## Allocates a new memory block with at least ``T.sizeof * size`` bytes. + ## Allocates a new memory block with at least `T.sizeof * size` bytes. ## ## The block has to be freed with `resize(block, 0) <#resize,ptr.T,Natural>`_ ## or `dealloc(block) <#dealloc,pointer>`_. @@ -106,7 +119,7 @@ when hasAlloc and not defined(js): cast[ptr T](alloc(T.sizeof * size)) template alloc0*(size: Natural): pointer = - ## Allocates a new memory block with at least ``size`` bytes. + ## Allocates a new memory block with at least `size` bytes. ## ## The block has to be freed with `realloc(block, 0) <#realloc.t,pointer,Natural>`_ ## or `dealloc(block) <#dealloc,pointer>`_. @@ -119,7 +132,7 @@ when hasAlloc and not defined(js): alloc0Impl(size) proc create*(T: typedesc, size = 1.Positive): ptr T {.inline, benign, raises: [].} = - ## Allocates a new memory block with at least ``T.sizeof * size`` bytes. + ## Allocates a new memory block with at least `T.sizeof * size` bytes. ## ## The block has to be freed with `resize(block, 0) <#resize,ptr.T,Natural>`_ ## or `dealloc(block) <#dealloc,pointer>`_. @@ -134,8 +147,8 @@ when hasAlloc and not defined(js): ## Grows or shrinks a given memory block. ## ## If `p` is **nil** then a new memory block is returned. - ## In either way the block has at least ``newSize`` bytes. - ## If ``newSize == 0`` and `p` is not **nil** ``realloc`` calls ``dealloc(p)``. + ## In either way the block has at least `newSize` bytes. + ## If `newSize == 0` and `p` is not **nil** `realloc` calls `dealloc(p)`. ## In other cases the block has to be freed with ## `dealloc(block) <#dealloc,pointer>`_. ## @@ -148,8 +161,8 @@ when hasAlloc and not defined(js): ## Grows or shrinks a given memory block. ## ## If `p` is **nil** then a new memory block is returned. - ## In either way the block has at least ``newSize`` bytes. - ## If ``newSize == 0`` and `p` is not **nil** ``realloc`` calls ``dealloc(p)``. + ## In either way the block has at least `newSize` bytes. + ## If `newSize == 0` and `p` is not **nil** `realloc` calls `dealloc(p)`. ## In other cases the block has to be freed with ## `dealloc(block) <#dealloc,pointer>`_. ## @@ -165,9 +178,9 @@ when hasAlloc and not defined(js): ## Grows or shrinks a given memory block. ## ## If `p` is **nil** then a new memory block is returned. - ## In either way the block has at least ``T.sizeof * newSize`` bytes. - ## If ``newSize == 0`` and `p` is not **nil** ``resize`` calls ``dealloc(p)``. - ## In other cases the block has to be freed with ``free``. + ## In either way the block has at least `T.sizeof * newSize` bytes. + ## If `newSize == 0` and `p` is not **nil** `resize` calls `dealloc(p)`. + ## In other cases the block has to be freed with `free`. ## ## The allocated memory belongs to its allocating thread! ## Use `resizeShared <#resizeShared,ptr.T,Natural>`_ to reallocate @@ -175,8 +188,8 @@ when hasAlloc and not defined(js): cast[ptr T](realloc(p, T.sizeof * newSize)) proc dealloc*(p: pointer) {.noconv, compilerproc, rtl, benign, raises: [], tags: [].} = - ## Frees the memory allocated with ``alloc``, ``alloc0`` or - ## ``realloc``. + ## Frees the memory allocated with `alloc`, `alloc0` or + ## `realloc`. ## ## **This procedure is dangerous!** ## If one forgets to free the memory a leak occurs; if one tries to @@ -190,7 +203,7 @@ when hasAlloc and not defined(js): template allocShared*(size: Natural): pointer = ## Allocates a new memory block on the shared heap with at - ## least ``size`` bytes. + ## least `size` bytes. ## ## The block has to be freed with ## `reallocShared(block, 0) <#reallocShared.t,pointer,Natural>`_ @@ -207,7 +220,7 @@ when hasAlloc and not defined(js): proc createSharedU*(T: typedesc, size = 1.Positive): ptr T {.inline, tags: [], benign, raises: [].} = ## Allocates a new memory block on the shared heap with at - ## least ``T.sizeof * size`` bytes. + ## least `T.sizeof * size` bytes. ## ## The block has to be freed with ## `resizeShared(block, 0) <#resizeShared,ptr.T,Natural>`_ or @@ -222,7 +235,7 @@ when hasAlloc and not defined(js): template allocShared0*(size: Natural): pointer = ## Allocates a new memory block on the shared heap with at - ## least ``size`` bytes. + ## least `size` bytes. ## ## The block has to be freed with ## `reallocShared(block, 0) <#reallocShared.t,pointer,Natural>`_ @@ -236,7 +249,7 @@ when hasAlloc and not defined(js): proc createShared*(T: typedesc, size = 1.Positive): ptr T {.inline.} = ## Allocates a new memory block on the shared heap with at - ## least ``T.sizeof * size`` bytes. + ## least `T.sizeof * size` bytes. ## ## The block has to be freed with ## `resizeShared(block, 0) <#resizeShared,ptr.T,Natural>`_ or @@ -251,9 +264,9 @@ when hasAlloc and not defined(js): ## Grows or shrinks a given memory block on the heap. ## ## If `p` is **nil** then a new memory block is returned. - ## In either way the block has at least ``newSize`` bytes. - ## If ``newSize == 0`` and `p` is not **nil** ``reallocShared`` calls - ## ``deallocShared(p)``. + ## In either way the block has at least `newSize` bytes. + ## If `newSize == 0` and `p` is not **nil** `reallocShared` calls + ## `deallocShared(p)`. ## In other cases the block has to be freed with ## `deallocShared <#deallocShared,pointer>`_. reallocSharedImpl(p, newSize) @@ -265,9 +278,9 @@ when hasAlloc and not defined(js): ## containing zero, so it is somewhat safer then reallocShared ## ## If `p` is **nil** then a new memory block is returned. - ## In either way the block has at least ``newSize`` bytes. - ## If ``newSize == 0`` and `p` is not **nil** ``reallocShared`` calls - ## ``deallocShared(p)``. + ## In either way the block has at least `newSize` bytes. + ## If `newSize == 0` and `p` is not **nil** `reallocShared` calls + ## `deallocShared(p)`. ## In other cases the block has to be freed with ## `deallocShared <#deallocShared,pointer>`_. reallocShared0Impl(p, oldSize, newSize) @@ -276,16 +289,16 @@ when hasAlloc and not defined(js): ## Grows or shrinks a given memory block on the heap. ## ## If `p` is **nil** then a new memory block is returned. - ## In either way the block has at least ``T.sizeof * newSize`` bytes. - ## If ``newSize == 0`` and `p` is not **nil** ``resizeShared`` calls - ## ``freeShared(p)``. + ## In either way the block has at least `T.sizeof * newSize` bytes. + ## If `newSize == 0` and `p` is not **nil** `resizeShared` calls + ## `freeShared(p)`. ## In other cases the block has to be freed with ## `freeShared <#freeShared,ptr.T>`_. cast[ptr T](reallocShared(p, T.sizeof * newSize)) proc deallocShared*(p: pointer) {.noconv, compilerproc, rtl, benign, raises: [], tags: [].} = - ## Frees the memory allocated with ``allocShared``, ``allocShared0`` or - ## ``reallocShared``. + ## Frees the memory allocated with `allocShared`, `allocShared0` or + ## `reallocShared`. ## ## **This procedure is dangerous!** ## If one forgets to free the memory a leak occurs; if one tries to @@ -295,8 +308,8 @@ when hasAlloc and not defined(js): deallocSharedImpl(p) proc freeShared*[T](p: ptr T) {.inline, benign, raises: [].} = - ## Frees the memory allocated with ``createShared``, ``createSharedU`` or - ## ``resizeShared``. + ## Frees the memory allocated with `createShared`, `createSharedU` or + ## `resizeShared`. ## ## **This procedure is dangerous!** ## If one forgets to free the memory a leak occurs; if one tries to @@ -304,7 +317,7 @@ when hasAlloc and not defined(js): ## or other memory may be corrupted. deallocShared(p) - include bitmasks + include bitmasks template `+!`(p: pointer, s: SomeInteger): pointer = cast[pointer](cast[int](p) +% int(s)) @@ -318,7 +331,7 @@ when hasAlloc and not defined(js): result = allocShared(size) else: result = alloc(size) - else: + else: # allocate (size + align - 1) necessary for alignment, # plus 2 bytes to store offset when compileOption("threads"): @@ -326,7 +339,7 @@ when hasAlloc and not defined(js): else: let base = alloc(size + align - 1 + sizeof(uint16)) # memory layout: padding + offset (2 bytes) + user_data - # in order to deallocate: read offset at user_data - 2 bytes, + # in order to deallocate: read offset at user_data - 2 bytes, # then deallocate user_data - offset let offset = align - (cast[int](base) and (align - 1)) cast[ptr uint16](base +! (offset - sizeof(uint16)))[] = uint16(offset) @@ -338,7 +351,7 @@ when hasAlloc and not defined(js): result = allocShared0(size) else: result = alloc0(size) - else: + else: # see comments for alignedAlloc when compileOption("threads"): let base = allocShared0(size + align - 1 + sizeof(uint16)) @@ -348,13 +361,13 @@ when hasAlloc and not defined(js): cast[ptr uint16](base +! (offset - sizeof(uint16)))[] = uint16(offset) result = base +! offset - proc alignedDealloc(p: pointer, align: int) {.compilerproc.} = + proc alignedDealloc(p: pointer, align: int) {.compilerproc.} = if align <= MemAlign: when compileOption("threads"): deallocShared(p) else: dealloc(p) - else: + else: # read offset at p - 2 bytes, then deallocate (p - offset) pointer let offset = cast[ptr uint16](p -! sizeof(uint16))[] when compileOption("threads"): diff --git a/tests/stdlib/tmemory.nim b/tests/stdlib/tmemory.nim new file mode 100644 index 000000000..25b5d526a --- /dev/null +++ b/tests/stdlib/tmemory.nim @@ -0,0 +1,15 @@ + +block: # cmpMem + type + SomeHash = array[15, byte] + + var + a: SomeHash + b: SomeHash + + a[^1] = byte(1) + let c = a + + doAssert cmpMem(a.addr, b.addr, sizeof(SomeHash)) > 0 + doAssert cmpMem(b.addr, a.addr, sizeof(SomeHash)) < 0 + doAssert cmpMem(a.addr, c.unsafeAddr, sizeof(SomeHash)) == 0 From 732419ae907208b0b484911b996893097402a6c7 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Tue, 29 Dec 2020 06:44:48 -0600 Subject: [PATCH 1229/3768] improve examples in manual (#16497) * improve examples in manual * Update doc/manual.rst Co-authored-by: Clyybber * Update tests/cpp/ttemplatetype.nim Co-authored-by: Clyybber Co-authored-by: Clyybber --- doc/manual.rst | 4 +++- tests/cpp/ttemplatetype.nim | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/manual.rst b/doc/manual.rst index fd0ac0529..9fabee1e8 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -7062,8 +7062,10 @@ one can import C++'s templates rather easily without the need for a pattern language for object types: .. code-block:: nim + :test: "nim cpp $1" + type - StdMap {.importcpp: "std::map", header: "".} [K, V] = object + StdMap[K, V] {.importcpp: "std::map", header: "".} = object proc `[]=`[K, V](this: var StdMap[K, V]; key: K; val: V) {. importcpp: "#[#] = #", header: "".} diff --git a/tests/cpp/ttemplatetype.nim b/tests/cpp/ttemplatetype.nim index ef24e4cdc..bf243ac43 100644 --- a/tests/cpp/ttemplatetype.nim +++ b/tests/cpp/ttemplatetype.nim @@ -3,7 +3,7 @@ discard """ """ type - Map {.importcpp: "std::map", header: "".} [T,U] = object + Map[T,U] {.importcpp: "std::map", header: "".} = object proc cInitMap(T: typedesc, U: typedesc): Map[T,U] {.importcpp: "std::map<'*1,'*2>()", nodecl.} From 89a2390f8bcf8615466aea9fd2653f57829404c4 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Tue, 29 Dec 2020 08:50:22 -0600 Subject: [PATCH 1230/3768] fix printing negative zero in JS backend (#16505) --- lib/system/jssys.nim | 4 +++- tests/misc/tnegativezero.nim | 30 ++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 tests/misc/tnegativezero.nim diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim index 5f18f01cb..64c766482 100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -495,11 +495,13 @@ proc negInt64(a: int64): int64 {.compilerproc.} = proc nimFloatToString(a: float): cstring {.compilerproc.} = ## ensures the result doesn't print like an integer, i.e. return 2.0, not 2 + # print `-0.0` properly asm """ function nimOnlyDigitsOrMinus(n) { return n.toString().match(/^-?\d+$/); } - if (Number.isSafeInteger(`a`)) `result` = `a`+".0" + if (Number.isSafeInteger(`a`)) + `result` = `a` === 0 && 1 / `a` < 0 ? "-0.0" : `a`+".0" else { `result` = `a`+"" if(nimOnlyDigitsOrMinus(`result`)){ diff --git a/tests/misc/tnegativezero.nim b/tests/misc/tnegativezero.nim new file mode 100644 index 000000000..a443e40cf --- /dev/null +++ b/tests/misc/tnegativezero.nim @@ -0,0 +1,30 @@ +discard """ + targets: "c cpp js" +""" + +proc main()= + block: + let a = -0.0 + doAssert $a == "-0.0" + doAssert $(-0.0) == "-0.0" + + block: + let a = 0.0 + when nimvm: discard ## TODO VM print wrong -0.0 + else: + doAssert $a == "0.0" + doAssert $(0.0) == "0.0" + + block: + let b = -0 + doAssert $b == "0" + doAssert $(-0) == "0" + + block: + let b = 0 + doAssert $b == "0" + doAssert $(0) == "0" + + +static: main() +main() From 95f599ca2d94e4d58055390c4b9c7761bbd60d01 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Tue, 29 Dec 2020 09:20:47 -0600 Subject: [PATCH 1231/3768] move asciitables to std/private/ (#16498) * move asciitables * minor --- compiler/dfa.nim | 3 ++- compiler/vmgen.nim | 2 +- {compiler => lib/std/private}/asciitables.nim | 8 ++++---- tests/compiler/tasciitables.nim | 2 +- 4 files changed, 8 insertions(+), 7 deletions(-) rename {compiler => lib/std/private}/asciitables.nim (89%) diff --git a/compiler/dfa.nim b/compiler/dfa.nim index d07122552..efd826594 100644 --- a/compiler/dfa.nim +++ b/compiler/dfa.nim @@ -29,7 +29,8 @@ ## "A Graph–Free Approach to Data–Flow Analysis" by Markus Mohnen. ## https://link.springer.com/content/pdf/10.1007/3-540-45937-5_6.pdf -import ast, types, intsets, lineinfos, renderer, asciitables +import ast, types, intsets, lineinfos, renderer +import std/private/asciitables from patterns import sameTrees diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 9f36fc736..efb657d17 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -35,7 +35,7 @@ const debugEchoCode* = defined(nimVMDebug) when debugEchoCode: - import asciitables + import std/private/asciitables when hasFFI: import evalffi diff --git a/compiler/asciitables.nim b/lib/std/private/asciitables.nim similarity index 89% rename from compiler/asciitables.nim rename to lib/std/private/asciitables.nim index 39bb26a5c..cbc595651 100644 --- a/compiler/asciitables.nim +++ b/lib/std/private/asciitables.nim @@ -1,6 +1,6 @@ #[ -move to std/asciitables.nim once stable, or to a nimble paackage -once compiler can depend on nimble +move to std/asciitables.nim once stable, or to a fusion package +once compiler can depend on fusion ]# type Cell* = object @@ -8,7 +8,7 @@ type Cell* = object width*, row*, col*, ncols*, nrows*: int iterator parseTableCells*(s: string, delim = '\t'): Cell = - ## iterates over all cells in a `delim`-delimited `s`, after a 1st + ## Iterates over all cells in a `delim`-delimited `s`, after a 1st ## pass that computes number of rows, columns, and width of each column. var widths: seq[int] var cell: Cell @@ -69,7 +69,7 @@ iterator parseTableCells*(s: string, delim = '\t'): Cell = finishRow() proc alignTable*(s: string, delim = '\t', fill = ' ', sep = " "): string = - ## formats a `delim`-delimited `s` representing a table; each cell is aligned + ## Formats a `delim`-delimited `s` representing a table; each cell is aligned ## to a width that's computed for each column; consecutive columns are ## delimited by `sep`, and alignment space is filled using `fill`. ## More customized formatting can be done by calling `parseTableCells` directly. diff --git a/tests/compiler/tasciitables.nim b/tests/compiler/tasciitables.nim index 80d648508..2f3b7bf2f 100644 --- a/tests/compiler/tasciitables.nim +++ b/tests/compiler/tasciitables.nim @@ -1,5 +1,5 @@ import stdtest/unittest_light -import compiler/asciitables +import std/private/asciitables import strformat From 1df0c04a1c273237bef01b520d3c7dd1017b1611 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Tue, 29 Dec 2020 22:30:20 -0600 Subject: [PATCH 1232/3768] disable grams (#16511) --- testament/important_packages.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 5a9775bad..dca8c5a4f 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -44,7 +44,8 @@ pkg1 "glob" pkg1 "ggplotnim", "nim c -d:noCairo -r tests/tests.nim" # pkg1 "gittyup", "nimble test", "https://github.com/disruptek/gittyup" pkg1 "gnuplot", "nim c gnuplot.nim" -pkg1 "gram", "nim c -r --gc:arc --define:danger tests/test.nim", "https://github.com/disruptek/gram" +# pkg1 "gram", "nim c -r --gc:arc --define:danger tests/test.nim", "https://github.com/disruptek/gram" + # pending https://github.com/nim-lang/Nim/issues/16509 pkg1 "hts", "nim c -o:htss src/hts.nim" # pkg1 "httpauth" pkg1 "illwill", "nimble examples" From 2f4d00fb98beaf6bb2e155e9da7dc7194038b5f3 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Wed, 30 Dec 2020 07:51:17 -0600 Subject: [PATCH 1233/3768] fix #16502 (#16512) --- lib/system/io.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/system/io.nim b/lib/system/io.nim index 22059c0a8..270779272 100644 --- a/lib/system/io.nim +++ b/lib/system/io.nim @@ -783,7 +783,7 @@ when declared(stdout): not defined(nintendoswitch) and not defined(freertos) and hostOS != "any" - const echoDoRaise = not defined(nimLegacyEchoNoRaise) # see PR #16366 + const echoDoRaise = not defined(nimLegacyEchoNoRaise) and not defined(guiapp) # see PR #16366 template checkErrMaybe(succeeded: bool): untyped = if not succeeded: From 8508c4e1c262567ecc093de8b645cec677ce5afd Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 30 Dec 2020 05:58:41 -0800 Subject: [PATCH 1234/3768] fix `hintProcessing` dots interference with `static:echo` and `hintCC`; add tests for `nim secret`, add tests for hintProcessing, misc other bug fixes (#16495) * fix dots interfering with static:echo * add tests * fix hintProcessing dots for hintCC * improve trunner tests * fix bug: readLineFromStdin now writes prompt to stdout, consistent with linenoise and rdstdin * disable a failing test for windows --- compiler/extccomp.nim | 5 +++- compiler/llstream.nim | 4 +-- compiler/main.nim | 2 +- compiler/msgs.nim | 12 ++++---- compiler/options.nim | 5 +++- compiler/vm.nim | 6 ++-- tests/misc/trunner.nim | 64 ++++++++++++++++++++++++++++++++++++------ 7 files changed, 77 insertions(+), 21 deletions(-) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index e37e867da..14b961ee1 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -848,7 +848,10 @@ proc callCCompiler*(conf: ConfigRef) = var cmds: TStringSeq var prettyCmds: TStringSeq let prettyCb = proc (idx: int) = - if prettyCmds[idx].len > 0: echo prettyCmds[idx] + if prettyCmds[idx].len > 0: + flushDot(conf) + # xxx should probably use stderr like other compiler messages, not stdout + echo prettyCmds[idx] for idx, it in conf.toCompile: # call the C compiler for the .c file: diff --git a/compiler/llstream.nim b/compiler/llstream.nim index b768e6c83..bfc377a16 100644 --- a/compiler/llstream.nim +++ b/compiler/llstream.nim @@ -75,10 +75,10 @@ proc llStreamClose*(s: PLLStream) = when not declared(readLineFromStdin): # fallback implementation: proc readLineFromStdin(prompt: string, line: var string): bool = - stderr.write(prompt) + stdout.write(prompt) result = readLine(stdin, line) if not result: - stderr.write("\n") + stdout.write("\n") quit(0) proc endsWith*(x: string, s: set[char]): bool = diff --git a/compiler/main.nim b/compiler/main.nim index 74c19bf10..c94c4323f 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -138,7 +138,7 @@ proc commandInteractive(graph: ModuleGraph) = var m = graph.makeStdinModule() incl(m.flags, sfMainModule) var idgen = IdGenerator(module: m.itemId.module, item: m.itemId.item) - let s = llStreamOpenStdIn(onPrompt = proc() = flushDot(graph.config, stderr)) + let s = llStreamOpenStdIn(onPrompt = proc() = flushDot(graph.config)) processModule(graph, m, idgen, s) proc commandScan(cache: IdentCache, config: ConfigRef) = diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 6d6e21204..afb51da09 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -19,8 +19,10 @@ template instLoc(): InstantiationInfo = instantiationInfo(-2, fullPaths = true) template toStdOrrKind(stdOrr): untyped = if stdOrr == stdout: stdOrrStdout else: stdOrrStderr -template flushDot*(conf, stdOrr) = +proc flushDot*(conf: ConfigRef) = ## safe to call multiple times + # xxx one edge case not yet handled is when `printf` is called at CT with `compiletimeFFI`. + let stdOrr = if optStdout in conf.globalOptions: stdout else: stderr let stdOrrKind = toStdOrrKind(stdOrr) if stdOrrKind in conf.lastMsgWasDot: conf.lastMsgWasDot.excl stdOrrKind @@ -311,12 +313,12 @@ proc msgWriteln*(conf: ConfigRef; s: string, flags: MsgFlags = {}) = conf.writelnHook(s) elif optStdout in conf.globalOptions or msgStdout in flags: if eStdOut in conf.m.errorOutputs: - flushDot(conf, stdout) + flushDot(conf) writeLine(stdout, s) flushFile(stdout) else: if eStdErr in conf.m.errorOutputs: - flushDot(conf, stderr) + flushDot(conf) writeLine(stderr, s) # On Windows stderr is fully-buffered when piped, regardless of C std. when defined(windows): @@ -368,11 +370,11 @@ template styledMsgWriteln*(args: varargs[typed]) = callIgnoringStyle(callWritelnHook, nil, args) elif optStdout in conf.globalOptions: if eStdOut in conf.m.errorOutputs: - flushDot(conf, stdout) + flushDot(conf) callIgnoringStyle(writeLine, stdout, args) flushFile(stdout) elif eStdErr in conf.m.errorOutputs: - flushDot(conf, stderr) + flushDot(conf) if optUseColors in conf.globalOptions: callStyledWriteLineStderr(args) else: diff --git a/compiler/options.nim b/compiler/options.nim index 87796539c..1de6d531a 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -368,8 +368,11 @@ proc setNote*(conf: ConfigRef, note: TNoteKind, enabled = true) = if enabled: incl(conf.notes, note) else: excl(conf.notes, note) proc hasHint*(conf: ConfigRef, note: TNoteKind): bool = + # ternary states instead of binary states would simplify logic if optHints notin conf.options: false - elif note in {hintConf}: # could add here other special notes like hintSource + elif note in {hintConf, hintProcessing}: + # could add here other special notes like hintSource + # these notes apply globally. note in conf.mainPackageNotes else: note in conf.notes diff --git a/compiler/vm.nim b/compiler/vm.nim index 34d76f21e..1004826ea 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1153,14 +1153,14 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = stackTrace(c, tos, pc, "node is not a proc symbol") of opcEcho: let rb = instr.regB - if rb == 1: - msgWriteln(c.config, regs[ra].node.strVal, {msgStdout}) + template fn(s) = msgWriteln(c.config, s, {msgStdout}) + if rb == 1: fn(regs[ra].node.strVal) else: var outp = "" for i in ra..ra+rb-1: #if regs[i].kind != rkNode: debug regs[i] outp.add(regs[i].node.strVal) - msgWriteln(c.config, outp, {msgStdout}) + fn(outp) of opcContainsSet: decodeBC(rkInt) regs[ra].intVal = ord(inSet(regs[rb].node, regs[rc].regToNode)) diff --git a/tests/misc/trunner.nim b/tests/misc/trunner.nim index e288a56c7..530561cd9 100644 --- a/tests/misc/trunner.nim +++ b/tests/misc/trunner.nim @@ -12,10 +12,18 @@ from std/sequtils import toSeq,mapIt from std/algorithm import sorted import stdtest/[specialpaths, unittest_light] from std/private/globs import nativeToUnixPath - +from strutils import startsWith, strip, removePrefix +from std/sugar import dup import "$lib/../compiler/nimpaths" +proc isDots(a: string): bool = + ## test for `hintProcessing` dots + a.startsWith(".") and a.strip(chars = {'.'}) == "" + const + defaultHintsOff = "--hint:successx:off --hint:exec:off --hint:link:off --hint:cc:off --hint:conf:off --hint:processing:off --hint:QuitCalled:off" + # useful when you want to turn only some hints on, and some common ones off. + # pending https://github.com/timotheecour/Nim/issues/453, simplify to: `--hints:off` nim = getCurrentCompilerExe() mode = when defined(c): "c" @@ -93,10 +101,9 @@ else: # don't run twice the same test check exitCode == 0 let ret = toSeq(walkDirRec(htmldocsDir, relative=true)).mapIt(it.nativeToUnixPath).sorted.join("\n") let context = $(i, ret, cmd) - var expected = "" case i of 0,5: - let htmlFile = htmldocsDir/"mmain.html" + let htmlFile = htmldocsDir/mainFname check htmlFile in outp # sanity check for `hintSuccessX` assertEquals ret, fmt""" {dotdotMangle}/imp.html @@ -106,7 +113,7 @@ imp.html imp.idx imp2.html imp2.idx -mmain.html +{mainFname} mmain.idx {nimdocOutCss} {theindexFname}""", context @@ -119,21 +126,21 @@ tests/nimdoc/sub/imp.html tests/nimdoc/sub/imp.idx tests/nimdoc/sub/imp2.html tests/nimdoc/sub/imp2.idx -tests/nimdoc/sub/mmain.html +tests/nimdoc/sub/{mainFname} tests/nimdoc/sub/mmain.idx {theindexFname}""" of 2, 3: assertEquals ret, fmt""" {docHackJsFname} -mmain.html +{mainFname} mmain.idx {nimdocOutCss}""", context of 4: assertEquals ret, fmt""" {docHackJsFname} {nimdocOutCss} -sub/mmain.html +sub/{mainFname} sub/mmain.idx""", context of 6: assertEquals ret, fmt""" -mmain.html +{mainFname} {nimdocOutCss}""", context else: doAssert false @@ -222,3 +229,44 @@ mmain.html check fmt"""{nim} {opt} --eval:"echo defined(nimscript)"""".execCmdEx == ("true\n", 0) check fmt"""{nim} r {opt} --eval:"echo defined(c)"""".execCmdEx == ("true\n", 0) check fmt"""{nim} r -b:js {opt} --eval:"echo defined(js)"""".execCmdEx == ("true\n", 0) + + block: # `hintProcessing` dots should not interfere with `static: echo` + friends + let cmd = fmt"""{nim} r {defaultHintsOff} --hint:processing -f --eval:"static: echo 1+1"""" + let (outp, exitCode) = execCmdEx(cmd, options = {poStdErrToStdOut}) + template check3(cond) = doAssert cond, $(outp,) + doAssert exitCode == 0 + let lines = outp.splitLines + check3 lines.len == 3 + when not defined(windows): # xxx: on windows, dots not properly handled, gives: `....2\n\n` + check3 lines[0].isDots + check3 lines[1] == "2" + check3 lines[2] == "" + else: + check3 "2" in outp + + block: # nim secret + let opt = fmt"{defaultHintsOff} --hint:processing" + template check3(cond) = doAssert cond, $(outp,) + for extra in ["", "--stdout"]: + let cmd = fmt"""{nim} secret {opt} {extra}""" + # xxx minor bug: `nim --hint:QuitCalled:off secret` ignores the hint cmdline flag + template run(input2): untyped = + execCmdEx(cmd, options = {poStdErrToStdOut}, input = input2) + block: + let (outp, exitCode) = run """echo 1+2; import strutils; echo strip(" ab "); quit()""" + let lines = outp.splitLines + when not defined(windows): + check3 lines.len == 5 + check3 lines[0].isDots + check3 lines[1].dup(removePrefix(">>> ")) == "3" # prompt depends on `nimUseLinenoise` + check3 lines[2].isDots + check3 lines[3] == "ab" + check3 lines[4] == "" + else: + check3 "3" in outp + check3 "ab" in outp + doAssert exitCode == 0 + block: + let (outp, exitCode) = run "echo 1+2; quit(2)" + check3 "3" in outp + doAssert exitCode == 2 From 84a7544988ffd8d26ff50c8a9417acbededf03fb Mon Sep 17 00:00:00 2001 From: Saem Ghani Date: Wed, 30 Dec 2020 06:02:51 -0800 Subject: [PATCH 1235/3768] nim-gdb.py fixes mostly for nimsuggest debugging (#16479) These fixes were primarily developed to assist in nimsuggest debugging. There is nothing intentionally specific done for nimsuggest, but beyond the automated tests all practical testing was done with nimsuggest. Undoubltedly these will also assist in other debugging scenarios. The current nim-dbg.py script was broken in a few ways: - failed to provide detailed value information for common types (see below) - was not passing existing tests - could not produce type summary information Broken types now working somewhat better: - sequences with ref types like strings - sequences with value types like ints - arrays with ref types like strings - tables with int or string keys Other improvements: - slightly more test coverage Future considerations: - this, data used by it, should be something the compiler can generates - account for different memory layouts ([arc/orc differ](https://github.com/nim-lang/Nim/pull/16479#issuecomment-751469536)) Attempts at improving nim-gdb.py More tests, few fixes for seq and type printing Tables debugging fixed added further tests Fixed type printing --- .../untestable/gdb/gdb_pretty_printer_test.py | 26 ++- .../gdb/gdb_pretty_printer_test_program.nim | 55 +++++-- tools/nim-gdb.py | 151 +++++++++++------- 3 files changed, 156 insertions(+), 76 deletions(-) diff --git a/tests/untestable/gdb/gdb_pretty_printer_test.py b/tests/untestable/gdb/gdb_pretty_printer_test.py index f002941ec..5b34bcb3d 100644 --- a/tests/untestable/gdb/gdb_pretty_printer_test.py +++ b/tests/untestable/gdb/gdb_pretty_printer_test.py @@ -6,31 +6,47 @@ import gdb # frontends might still be broken. gdb.execute("source ../../../tools/nim-gdb.py") -# debug all instances of the generic function `myDebug`, should be 8 +# debug all instances of the generic function `myDebug`, should be 14 gdb.execute("rbreak myDebug") gdb.execute("run") outputs = [ 'meTwo', + '""', '"meTwo"', '{meOne, meThree}', 'MyOtherEnum(1)', '5', 'array = {1, 2, 3, 4, 5}', + 'seq(0, 0)', + 'seq(0, 10)', + 'array = {"one", "two"}', + 'seq(3, 3) = {1, 2, 3}', 'seq(3, 3) = {"one", "two", "three"}', - 'Table(3, 64) = {["two"] = 2, ["three"] = 3, ["one"] = 1}', + 'Table(3, 64) = {[4] = "four", [5] = "five", [6] = "six"}', + 'Table(3, 8) = {["two"] = 2, ["three"] = 3, ["one"] = 1}', ] for i, expected in enumerate(outputs): + gdb.write(f"{i+1}) expecting: {expected}: ", gdb.STDLOG) + gdb.flush() + functionSymbol = gdb.selected_frame().block().function assert functionSymbol.line == 21 - if i == 5: + if i == 6: # myArray is passed as pointer to int to myDebug. I look up myArray up in the stack gdb.execute("up") - output = str(gdb.parse_and_eval("myArray")) + raw = gdb.parse_and_eval("myArray") + elif i == 9: + # myOtherArray is passed as pointer to int to myDebug. I look up myOtherArray up in the stack + gdb.execute("up") + raw = gdb.parse_and_eval("myOtherArray") else: - output = str(gdb.parse_and_eval("arg")) + raw = gdb.parse_and_eval("arg") + + output = str(raw) assert output == expected, output + " != " + expected + gdb.write(f"passed\n", gdb.STDLOG) gdb.execute("continue") diff --git a/tests/untestable/gdb/gdb_pretty_printer_test_program.nim b/tests/untestable/gdb/gdb_pretty_printer_test_program.nim index 458435c1a..c376ef89a 100644 --- a/tests/untestable/gdb/gdb_pretty_printer_test_program.nim +++ b/tests/untestable/gdb/gdb_pretty_printer_test_program.nim @@ -23,29 +23,56 @@ proc myDebug[T](arg: T): void = proc testProc(): void = var myEnum = meTwo - myDebug(myEnum) + myDebug(myEnum) #1 + + # create a string, but don't allocate it + var myString: string + myDebug(myString) #2 + # create a string object but also make the NTI for MyEnum is generated - var myString = $myEnum - myDebug(myString) + myString = $myEnum + myDebug(myString) #3 + var mySet = {meOne,meThree} - myDebug(mySet) + myDebug(mySet) #4 # for MyOtherEnum there is no NTI. This tests the fallback for the pretty printer. var moEnum = moTwo - myDebug(moEnum) + myDebug(moEnum) #5 + var moSet = {moOne,moThree} - myDebug(moSet) + myDebug(moSet) #6 let myArray = [1,2,3,4,5] - myDebug(myArray) - let mySeq = @["one","two","three"] - myDebug(mySeq) + myDebug(myArray) #7 - var myTable = initTable[string, int]() - myTable["one"] = 1 - myTable["two"] = 2 - myTable["three"] = 3 - myDebug(myTable) + # implicitly initialized seq test + var mySeq: seq[string] + myDebug(mySeq) #8 + + # len not equal to capacity + let myOtherSeq = newSeqOfCap[string](10) + myDebug(myOtherSeq) #9 + + let myOtherArray = ["one","two"] + myDebug(myOtherArray) #10 + + # numeric sec + var mySeq3 = @[1,2,3] + myDebug(mySeq3) #11 + + # seq had to grow + var mySeq4 = @["one","two","three"] + myDebug(mySeq4) #12 + + var myTable = initTable[int, string]() + myTable[4] = "four" + myTable[5] = "five" + myTable[6] = "six" + myDebug(myTable) #13 + + var myOtherTable = {"one": 1, "two": 2, "three": 3}.toTable + myDebug(myOtherTable) #14 echo(counter) diff --git a/tools/nim-gdb.py b/tools/nim-gdb.py index e994531b6..8143b94d5 100644 --- a/tools/nim-gdb.py +++ b/tools/nim-gdb.py @@ -34,6 +34,14 @@ def getNimRti(type_name): except: return None +def getNameFromNimRti(rti_val): + """ Return name (or None) given a Nim RTI ``gdb.Value`` """ + try: + # sometimes there isn't a name field -- example enums + return rti['name'].string(encoding="utf-8", errors="ignore") + except: + return None + class NimTypeRecognizer: # this type map maps from types that are generated in the C files to # how they are called in nim. To not mix up the name ``int`` from @@ -42,30 +50,25 @@ class NimTypeRecognizer: # ``int``. type_map_static = { - 'NI': 'system.int', 'NI8': 'int8', 'NI16': 'int16', 'NI32': 'int32', 'NI64': 'int64', - 'NU': 'uint', 'NU8': 'uint8','NU16': 'uint16', 'NU32': 'uint32', 'NU64': 'uint64', + 'NI': 'system.int', 'NI8': 'int8', 'NI16': 'int16', 'NI32': 'int32', + 'NI64': 'int64', + + 'NU': 'uint', 'NU8': 'uint8','NU16': 'uint16', 'NU32': 'uint32', + 'NU64': 'uint64', + 'NF': 'float', 'NF32': 'float32', 'NF64': 'float64', - 'NIM_BOOL': 'bool', 'NIM_CHAR': 'char', 'NCSTRING': 'cstring', - 'NimStringDesc': 'string' + + 'NIM_BOOL': 'bool', + + 'NIM_CHAR': 'char', 'NCSTRING': 'cstring', 'NimStringDesc': 'string' } - # Normally gdb distinguishes between the command `ptype` and - # `whatis`. `ptype` prints a very detailed view of the type, and - # `whatis` a very brief representation of the type. I haven't - # figured out a way to know from the type printer that is - # implemented here how to know if a type printer should print the - # short representation or the long representation. As a hacky - # workaround I just say I am not resposible for printing pointer - # types (seq and string are exception as they are semantically - # values). this way the default type printer will handle pointer - # types and dive into the members of that type. So I can still - # control with `ptype myval` and `ptype *myval` if I want to have - # detail or not. I this this method stinks but I could not figure - # out a better solution. - - object_type_pattern = re.compile("^(\w*):ObjectType$") + # object_type_pattern = re.compile("^(\w*):ObjectType$") def recognize(self, type_obj): + # skip things we can't handle like functions + if type_obj.code in [gdb.TYPE_CODE_FUNC, gdb.TYPE_CODE_VOID]: + return None tname = None if type_obj.tag is not None: @@ -75,44 +78,43 @@ class NimTypeRecognizer: # handle pointer types if not tname: - if type_obj.code == gdb.TYPE_CODE_PTR: + target_type = type_obj + if type_obj.code in [gdb.TYPE_CODE_PTR]: target_type = type_obj.target() - target_type_name = target_type.name - if target_type_name: - # visualize 'string' as non pointer type (unpack pointer type). - if target_type_name == "NimStringDesc": - tname = target_type_name # could also just return 'string' - # visualize 'seq[T]' as non pointer type. - if target_type_name.find('tySequence_') == 0: - tname = target_type_name - if not tname: - # We are not resposible for this type printing. - # Basically this means we don't print pointer types. - return None + if target_type.name: + # visualize 'string' as non pointer type (unpack pointer type). + if target_type.name == "NimStringDesc": + tname = target_type.name # could also just return 'string' + else: + rti = getNimRti(target_type.name) + if rti: + return getNameFromNimRti(rti) - result = self.type_map_static.get(tname, None) - if result: - return result + if tname: + result = self.type_map_static.get(tname, None) + if result: + return result - rti = getNimRti(tname) - if rti: - return rti['name'].string("utf-8", "ignore") - else: - return None + rti = getNimRti(tname) + if rti: + return getNameFromNimRti(rti) + + return None class NimTypePrinter: """Nim type printer. One printer for all Nim types.""" - # enabling and disabling of type printers can be done with the # following gdb commands: # # enable type-printer NimTypePrinter # disable type-printer NimTypePrinter + # relevant docs: https://sourceware.org/gdb/onlinedocs/gdb/Type-Printing-API.html name = "NimTypePrinter" - def __init__ (self): + + def __init__(self): self.enabled = True def instantiate(self): @@ -309,9 +311,25 @@ class NimStringPrinter: def to_string(self): if self.val: l = int(self.val['Sup']['len']) - return self.val['data'][0].address.string("utf-8", "ignore", l) + return self.val['data'].lazy_string(encoding="utf-8", length=l) else: - return "" + return "" + +# class NimStringPrinter: +# pattern = re.compile(r'^NimStringDesc$') + +# def __init__(self, val): +# self.val = val + +# def display_hint(self): +# return 'string' + +# def to_string(self): +# if self.val: +# l = int(self.val['Sup']['len']) +# return self.val['data'].lazy_string(encoding="utf-8", length=l) +# else: +# return "" class NimRopePrinter: pattern = re.compile(r'^tyObject_RopeObj__([A-Za-z0-9]*) \*$') @@ -372,14 +390,15 @@ class NimEnumPrinter: pattern = re.compile(r'^tyEnum_(\w*)__([A-Za-z0-9]*)$') def __init__(self, val): - self.val = val - match = self.pattern.match(self.val.type.name) + self.val = val + typeName = self.val.type.name + match = self.pattern.match(typeName) self.typeNimName = match.group(1) typeInfoName = "NTI__" + match.group(2) + "_" self.nti = gdb.lookup_global_symbol(typeInfoName) if self.nti is None: - printErrorOnce(typeInfoName, "NimEnumPrinter: lookup global symbol '" + typeInfoName + " failed for " + self.val.type.name + ".\n") + printErrorOnce(typeInfoName, f"NimEnumPrinter: lookup global symbol '{typeInfoName}' failed for {typeName}.\n") def to_string(self): if self.nti: @@ -476,11 +495,31 @@ class NimSeqPrinter: def children(self): if self.val: - length = int(self.val['Sup']['len']) - #align = len(str(length - 1)) - for i in range(length): - yield ("data[{0}]".format(i), self.val["data"][i]) + val = self.val + valType = val.type + length = int(val['Sup']['len']) + if length <= 0: + return + + dataType = valType['data'].type + data = val['data'] + + if self.val.type.name is None: + dataType = valType['data'].type.target().pointer() + data = val['data'].cast(dataType) + + inaccessible = False + for i in range(length): + if inaccessible: + return + try: + str(data[i]) + yield "data[{0}]".format(i), data[i] + except RuntimeError: + inaccessible = True + yield "data[{0}]".format(i), "inaccessible" + ################################################################################ class NimArrayPrinter: @@ -524,9 +563,9 @@ class NimStringTablePrinter: def children(self): if self.val: - data = NimSeqPrinter(self.val['data']) + data = NimSeqPrinter(self.val['data'].dereference()) for idxStr, entry in data.children(): - if int(entry['Field2']) > 0: + if int(entry['Field0']) != 0: yield (idxStr + ".Field0", entry['Field0']) yield (idxStr + ".Field1", entry['Field1']) @@ -537,7 +576,6 @@ class NimTablePrinter: def __init__(self, val): self.val = val - # match = self.pattern.match(self.val.type.name) def display_hint(self): return 'map' @@ -556,11 +594,10 @@ class NimTablePrinter: if self.val: data = NimSeqPrinter(self.val['data']) for idxStr, entry in data.children(): - if int(entry['Field0']) > 0: + if int(entry['Field0']) != 0: yield (idxStr + '.Field1', entry['Field1']) yield (idxStr + '.Field2', entry['Field2']) - ################################################################ # this is untested, therefore disabled @@ -651,7 +688,7 @@ def register_nim_pretty_printers_for_object(objfile): if nimMainSym and nimMainSym.symtab.objfile == objfile: print("set Nim pretty printers for ", objfile.filename) - objfile.type_printers = [NimTypePrinter()] + gdb.types.register_type_printer(objfile, NimTypePrinter()) objfile.pretty_printers = [makematcher(var) for var in list(globals().values()) if hasattr(var, 'pattern')] # Register pretty printers for all objfiles that are already loaded. From 73f778e441cd4229af4115f762bd597a8a20fb74 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Wed, 30 Dec 2020 08:06:50 -0600 Subject: [PATCH 1236/3768] follow #16505 move and active tests (#16508) * fix printing negative zero in JS backend * move tests --- tests/misc/tnegativezero.nim | 30 ------------------------------ tests/system/tdollars.nim | 26 ++++++++++++++++++++++++++ 2 files changed, 26 insertions(+), 30 deletions(-) delete mode 100644 tests/misc/tnegativezero.nim diff --git a/tests/misc/tnegativezero.nim b/tests/misc/tnegativezero.nim deleted file mode 100644 index a443e40cf..000000000 --- a/tests/misc/tnegativezero.nim +++ /dev/null @@ -1,30 +0,0 @@ -discard """ - targets: "c cpp js" -""" - -proc main()= - block: - let a = -0.0 - doAssert $a == "-0.0" - doAssert $(-0.0) == "-0.0" - - block: - let a = 0.0 - when nimvm: discard ## TODO VM print wrong -0.0 - else: - doAssert $a == "0.0" - doAssert $(0.0) == "0.0" - - block: - let b = -0 - doAssert $b == "0" - doAssert $(-0) == "0" - - block: - let b = 0 - doAssert $b == "0" - doAssert $(0) == "0" - - -static: main() -main() diff --git a/tests/system/tdollars.nim b/tests/system/tdollars.nim index 6ddec911f..1b2602ad0 100644 --- a/tests/system/tdollars.nim +++ b/tests/system/tdollars.nim @@ -77,3 +77,29 @@ block: # #14350 for JS doAssert cstr == nil doAssert cstr.isNil doAssert cstr != cstring("") + + +proc main()= + block: + let a = -0.0 + doAssert $a == "-0.0" + doAssert $(-0.0) == "-0.0" + + block: + let a = 0.0 + doAssert $a == "0.0" + doAssert $(0.0) == "0.0" + + block: + let b = -0 + doAssert $b == "0" + doAssert $(-0) == "0" + + block: + let b = 0 + doAssert $b == "0" + doAssert $(0) == "0" + + +static: main() +main() From 515cd454207ed9066c08ac8ef42407ae5ba62bc8 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Wed, 30 Dec 2020 08:09:30 -0600 Subject: [PATCH 1237/3768] Add math.copySign (#16406) * add math.copySign * fix + tests --- changelog.md | 1 + compiler/vmops.nim | 6 +++++ lib/pure/math.nim | 38 ++++++++++++++++++++++++++++++++ tests/stdlib/tmath.nim | 50 +++++++++++++++++++++++++++++++++++++++++- 4 files changed, 94 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 035ba46b8..bbc83ff25 100644 --- a/changelog.md +++ b/changelog.md @@ -69,6 +69,7 @@ - `echo` and `debugEcho` will now raise `IOError` if writing to stdout fails. Previous behavior silently ignored errors. See #16366. Use `-d:nimLegacyEchoNoRaise` for previous behavior. +- Added `math.copySign`. - Added new operations for singly- and doubly linked lists: `lists.toSinglyLinkedList` and `lists.toDoublyLinkedList` convert from `openArray`s; `lists.copy` implements shallow copying; `lists.add` concatenates two lists - an O(1) variation that consumes diff --git a/compiler/vmops.nim b/compiler/vmops.nim index 47df3d24f..3e859d3d7 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -13,6 +13,9 @@ from math import sqrt, ln, log10, log2, exp, round, arccos, arcsin, arctan, arctan2, cos, cosh, hypot, sinh, sin, tan, tanh, pow, trunc, floor, ceil, `mod` +when declared(math.copySign): + from math import copySign + from os import getEnv, existsEnv, dirExists, fileExists, putEnv, walkDir, getAppFilename from md5 import getMD5 from sighashes import symBodyDigest @@ -168,6 +171,9 @@ proc registerAdditionalOps*(c: PCtx) = wrap1f_math(floor) wrap1f_math(ceil) + when declared(copySign): + wrap2f_math(copySign) + wrap1s(getMD5, md5op) proc `mod Wrapper`(a: VmArgs) {.nimcall.} = diff --git a/lib/pure/math.nim b/lib/pure/math.nim index 5b19a8ec0..76052ec3b 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -62,6 +62,9 @@ when defined(c) or defined(cpp): proc c_isnan(x: float): bool {.importc: "isnan", header: "".} # a generic like `x: SomeFloat` might work too if this is implemented via a C macro. + proc c_copysign(x, y: cfloat): cfloat {.importc: "copysignf", header: "".} + proc c_copysign(x, y: cdouble): cdouble {.importc: "copysign", header: "".} + func binom*(n, k: int): int = ## Computes the `binomial coefficient `_. runnableExamples: @@ -153,6 +156,40 @@ func isNaN*(x: SomeFloat): bool {.inline, since: (1,5,1).} = when defined(js): fn() else: result = c_isnan(x) +func copySign*[T: SomeFloat](x, y: T): T {.inline, since: (1, 5, 1).} = + ## Returns a value with the magnitude of `x` and the sign of `y`; + ## this works even if x or y are NaN or zero, both of which can carry a sign. + runnableExamples: + doAssert copySign(1.0, -0.0) == -1.0 + doAssert copySign(0.0, -0.0) == -0.0 + doAssert copySign(-1.0, 0.0) == 1.0 + doAssert copySign(10.0, 0.0) == 10.0 + + doAssert copySign(Inf, -1.0) == -Inf + doAssert copySign(-Inf, 1.0) == Inf + doAssert copySign(-1.0, NaN) == 1.0 + doAssert copySign(10.0, NaN) == 10.0 + + doAssert copySign(NaN, 0.0).isNaN + doAssert copySign(NaN, -0.0).isNaN + + # fails in VM and JS backend + doAssert copySign(1.0, copySign(NaN, -1.0)) == -1.0 + + # TODO use signbit for examples + template impl() = + if y > 0.0 or (y == 0.0 and 1.0 / y > 0.0): + result = abs(x) + elif y <= 0.0: + result = -abs(x) + else: # must be NaN + result = abs(x) + + when defined(js): impl() + else: + when nimvm: impl() + else: result = c_copysign(x, y) + func classify*(x: float): FloatClass = ## Classifies a floating point value. ## @@ -1159,3 +1196,4 @@ func lcm*[T](x: openArray[T]): T {.since: (1, 1).} = while i < x.len: result = lcm(result, x[i]) inc(i) + diff --git a/tests/stdlib/tmath.nim b/tests/stdlib/tmath.nim index 64a4ff0ca..1b6fb4e9f 100644 --- a/tests/stdlib/tmath.nim +++ b/tests/stdlib/tmath.nim @@ -308,5 +308,53 @@ template main = doAssert not Inf.isNaN doAssert isNaN(Inf - Inf) -main() + block: # copySign + doAssert copySign(10.0, -1.0) == -10.0 + doAssert copySign(-10.0, -1.0) == -10.0 + doAssert copySign(-10.0, 1.0) == 10.0 + doAssert copySign(float(10), -1.0) == -10.0 + + doAssert copySign(10.0'f64, -1.0) == -10.0 + doAssert copySign(-10.0'f64, -1.0) == -10.0 + doAssert copySign(-10.0'f64, 1.0) == 10.0 + doAssert copySign(10'f64, -1.0) == -10.0 + + doAssert copySign(10.0'f32, -1.0) == -10.0 + doAssert copySign(-10.0'f32, -1.0) == -10.0 + doAssert copySign(-10.0'f32, 1.0) == 10.0 + doAssert copySign(10'f32, -1.0) == -10.0 + + doAssert copySign(Inf, -1.0) == -Inf + doAssert copySign(-Inf, 1.0) == Inf + doAssert copySign(Inf, 1.0) == Inf + doAssert copySign(-Inf, -1.0) == -Inf + doAssert copySign(Inf, 0.0) == Inf + doAssert copySign(Inf, -0.0) == -Inf + doAssert copySign(-Inf, 0.0) == Inf + doAssert copySign(-Inf, -0.0) == -Inf + doAssert copySign(1.0, -0.0) == -1.0 + doAssert copySign(0.0, -0.0) == -0.0 + doAssert copySign(-1.0, 0.0) == 1.0 + doAssert copySign(10.0, 0.0) == 10.0 + doAssert copySign(-1.0, NaN) == 1.0 + doAssert copySign(10.0, NaN) == 10.0 + + doAssert copySign(NaN, NaN).isNaN + doAssert copySign(-NaN, NaN).isNaN + doAssert copySign(NaN, -NaN).isNaN + doAssert copySign(-NaN, -NaN).isNaN + doAssert copySign(NaN, 0.0).isNaN + doAssert copySign(NaN, -0.0).isNaN + doAssert copySign(-NaN, 0.0).isNaN + doAssert copySign(-NaN, -0.0).isNaN + + when nimvm: + discard + else: + when not defined(js): + doAssert copySign(-1.0, -NaN) == 1.0 + doAssert copySign(10.0, -NaN) == 10.0 + doAssert copySign(1.0, copySign(NaN, -1.0)) == -1.0 # fails in VM + static: main() +main() From b8658a3e242f7304f7b0320089ef5aa0d5375c25 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Wed, 30 Dec 2020 11:10:50 -0300 Subject: [PATCH 1238/3768] Add assertions for jsconsole (#16460) --- changelog.md | 1 + lib/js/jsconsole.nim | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index bbc83ff25..c6a1d17d9 100644 --- a/changelog.md +++ b/changelog.md @@ -79,6 +79,7 @@ - Added `euclDiv` and `euclMod` to `math`. - Added `httpcore.is1xx` and missing HTTP codes. +- Added `jsconsole.jsAssert` for JavaScript target. ## Language changes diff --git a/lib/js/jsconsole.nim b/lib/js/jsconsole.nim index 35afd95ad..5b9893e75 100644 --- a/lib/js/jsconsole.nim +++ b/lib/js/jsconsole.nim @@ -10,10 +10,12 @@ ## Wrapper for the `console` object for the `JavaScript backend ## `_. +import std/private/since, std/private/miscdollars # toLocation + when not defined(js) and not defined(Nimdoc): {.error: "This module only works on the JavaScript platform".} -type Console* = ref object of RootObj +type Console* = ref object of JsRoot proc log*(console: Console) {.importcpp, varargs.} ## https://developer.mozilla.org/docs/Web/API/Console/log @@ -67,4 +69,34 @@ proc timeLog*(console: Console, label = "".cstring) {.importcpp.} proc table*(console: Console) {.importcpp, varargs.} ## https://developer.mozilla.org/docs/Web/API/Console/table +since (1, 5): + type InstantiationInfo = tuple[filename: string, line: int, column: int] + + func getMsg(info: InstantiationInfo; msg: string): string = + var temp = "" + temp.toLocation(info.filename, info.line, info.column + 1) + result.addQuoted(temp) + result.add ',' + result.addQuoted(msg) + + template jsAssert*(console: Console; assertion) = + ## JavaScript `console.assert`, for NodeJS this prints to stderr, + ## assert failure just prints to console and do not quit the program, + ## this is not meant to be better or even equal than normal assertions, + ## is just for when you need faster performance *and* assertions, + ## otherwise use the normal assertions for better user experience. + ## https://developer.mozilla.org/en-US/docs/Web/API/Console/assert + runnableExamples: + console.jsAssert(42 == 42) # OK + console.jsAssert(42 != 42) # Fail, prints "Assertion failed" and continues + console.jsAssert('`' == '\n' and '\t' == '\0') # Message correctly formatted + assert 42 == 42 # Normal assertions keep working + + const + loc = instantiationInfo(fullPaths = compileOption("excessiveStackTrace")) + msg = getMsg(loc, astToStr(assertion)).cstring + {.line: loc.}: + {.emit: ["console.assert(", assertion, ", ", msg, ");"].} + + var console* {.importc, nodecl.}: Console From 805917768dea1b223a3aab3131d5c76f862b4984 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Wed, 30 Dec 2020 09:26:49 -0600 Subject: [PATCH 1239/3768] use runnableExamples in options (#16503) --- lib/pure/options.nim | 43 +++++++++++++++++-------------------------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/lib/pure/options.nim b/lib/pure/options.nim index 8de4430c1..195da1008 100644 --- a/lib/pure/options.nim +++ b/lib/pure/options.nim @@ -23,38 +23,29 @@ ## Let's start with an example: a procedure that finds the index of a character ## in a string. ## -## .. code-block:: nim -## -## import options -## -## proc find(haystack: string, needle: char): Option[int] = -## for i, c in haystack: -## if c == needle: -## return some(i) -## return none(int) # This line is actually optional, -## # because the default is empty -## -## .. code-block:: nim -## -## let found = "abc".find('c') -## assert found.isSome and found.get() == 2 -## +runnableExamples: + proc find(haystack: string, needle: char): Option[int] = + for i, c in haystack: + if c == needle: + return some(i) + return none(int) # This line is actually optional, + # because the default is empty + + let found = "abc".find('c') + assert found.isSome and found.get() == 2 + ## The `get` operation demonstrated above returns the underlying value, or ## raises `UnpackDefect` if there is no value. Note that `UnpackDefect` ## inherits from `system.Defect`, and should therefore never be caught. ## Instead, rely on checking if the option contains a value with ## `isSome <#isSome,Option[T]>`_ and `isNone <#isNone,Option[T]>`_ procs. -## +## ## How to deal with an absence of a value: -## -## .. code-block:: nim -## -## let result = "team".find('i') -## -## # Nothing was found, so the result is `none`. -## assert(result == none(int)) -## # It has no value: -## assert(result.isNone) + +runnableExamples: + let result = none(int) + # It has no value: + assert(result.isNone) import typetraits From b42e7c0ef909a9712e4e8f87be304db5c105e721 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Wed, 30 Dec 2020 10:30:43 -0600 Subject: [PATCH 1240/3768] make the docs of arithmetics better (#16510) * fix * Update lib/system/arithmetics.nim Co-authored-by: Timothee Cour * Apply suggestions from code review Co-authored-by: Timothee Cour * Apply suggestions from code review Co-authored-by: Timothee Cour Co-authored-by: Timothee Cour --- lib/system/arithmetics.nim | 229 +++++++++++++++++-------------------- 1 file changed, 105 insertions(+), 124 deletions(-) diff --git a/lib/system/arithmetics.nim b/lib/system/arithmetics.nim index f6c1b69ff..4242cd1e3 100644 --- a/lib/system/arithmetics.nim +++ b/lib/system/arithmetics.nim @@ -1,48 +1,44 @@ -proc succ*[T: Ordinal](x: T, y = 1): T {.magic: "Succ", noSideEffect.} - ## Returns the ``y``-th successor (default: 1) of the value ``x``. - ## ``T`` has to be an `ordinal type <#Ordinal>`_. +proc succ*[T: Ordinal](x: T, y = 1): T {.magic: "Succ", noSideEffect.} = + ## Returns the `y`-th successor (default: 1) of the value `x`. ## - ## If such a value does not exist, ``OverflowDefect`` is raised + ## If such a value does not exist, `OverflowDefect` is raised ## or a compile time error occurs. - ## - ## .. code-block:: Nim - ## let x = 5 - ## echo succ(5) # => 6 - ## echo succ(5, 3) # => 8 + runnableExamples: + assert succ(5) == 6 + assert succ(5, 3) == 8 -proc pred*[T: Ordinal](x: T, y = 1): T {.magic: "Pred", noSideEffect.} - ## Returns the ``y``-th predecessor (default: 1) of the value ``x``. - ## ``T`` has to be an `ordinal type <#Ordinal>`_. +proc pred*[T: Ordinal](x: T, y = 1): T {.magic: "Pred", noSideEffect.} = + ## Returns the `y`-th predecessor (default: 1) of the value `x`. ## - ## If such a value does not exist, ``OverflowDefect`` is raised + ## If such a value does not exist, `OverflowDefect` is raised ## or a compile time error occurs. - ## - ## .. code-block:: Nim - ## let x = 5 - ## echo pred(5) # => 4 - ## echo pred(5, 3) # => 2 + runnableExamples: + assert pred(5) == 4 + assert pred(5, 3) == 2 -proc inc*[T: Ordinal](x: var T, y = 1) {.magic: "Inc", noSideEffect.} - ## Increments the ordinal ``x`` by ``y``. +proc inc*[T: Ordinal](x: var T, y = 1) {.magic: "Inc", noSideEffect.} = + ## Increments the ordinal `x` by `y`. ## - ## If such a value does not exist, ``OverflowDefect`` is raised or a compile - ## time error occurs. This is a short notation for: ``x = succ(x, y)``. - ## - ## .. code-block:: Nim - ## var i = 2 - ## inc(i) # i <- 3 - ## inc(i, 3) # i <- 6 + ## If such a value does not exist, `OverflowDefect` is raised or a compile + ## time error occurs. This is a short notation for: `x = succ(x, y)`. + runnableExamples: + var i = 2 + inc(i) + assert i == 3 + inc(i, 3) + assert i == 6 -proc dec*[T: Ordinal](x: var T, y = 1) {.magic: "Dec", noSideEffect.} - ## Decrements the ordinal ``x`` by ``y``. +proc dec*[T: Ordinal](x: var T, y = 1) {.magic: "Dec", noSideEffect.} = + ## Decrements the ordinal `x` by `y`. ## - ## If such a value does not exist, ``OverflowDefect`` is raised or a compile - ## time error occurs. This is a short notation for: ``x = pred(x, y)``. - ## - ## .. code-block:: Nim - ## var i = 2 - ## dec(i) # i <- 1 - ## dec(i, 3) # i <- -2 + ## If such a value does not exist, `OverflowDefect` is raised or a compile + ## time error occurs. This is a short notation for: `x = pred(x, y)`. + runnableExamples: + var i = 2 + dec(i) + assert i == 1 + dec(i, 3) + assert i == -2 @@ -51,38 +47,38 @@ proc dec*[T: Ordinal](x: var T, y = 1) {.magic: "Dec", noSideEffect.} when defined(nimNoZeroExtendMagic): proc ze*(x: int8): int {.deprecated.} = - ## zero extends a smaller integer type to ``int``. This treats `x` as + ## zero extends a smaller integer type to `int`. This treats `x` as ## unsigned. ## **Deprecated since version 0.19.9**: Use unsigned integers instead. cast[int](uint(cast[uint8](x))) proc ze*(x: int16): int {.deprecated.} = - ## zero extends a smaller integer type to ``int``. This treats `x` as + ## zero extends a smaller integer type to `int`. This treats `x` as ## unsigned. ## **Deprecated since version 0.19.9**: Use unsigned integers instead. cast[int](uint(cast[uint16](x))) proc ze64*(x: int8): int64 {.deprecated.} = - ## zero extends a smaller integer type to ``int64``. This treats `x` as + ## zero extends a smaller integer type to `int64`. This treats `x` as ## unsigned. ## **Deprecated since version 0.19.9**: Use unsigned integers instead. cast[int64](uint64(cast[uint8](x))) proc ze64*(x: int16): int64 {.deprecated.} = - ## zero extends a smaller integer type to ``int64``. This treats `x` as + ## zero extends a smaller integer type to `int64`. This treats `x` as ## unsigned. ## **Deprecated since version 0.19.9**: Use unsigned integers instead. cast[int64](uint64(cast[uint16](x))) proc ze64*(x: int32): int64 {.deprecated.} = - ## zero extends a smaller integer type to ``int64``. This treats `x` as + ## zero extends a smaller integer type to `int64`. This treats `x` as ## unsigned. ## **Deprecated since version 0.19.9**: Use unsigned integers instead. cast[int64](uint64(cast[uint32](x))) proc ze64*(x: int): int64 {.deprecated.} = - ## zero extends a smaller integer type to ``int64``. This treats `x` as - ## unsigned. Does nothing if the size of an ``int`` is the same as ``int64``. + ## zero extends a smaller integer type to `int64`. This treats `x` as + ## unsigned. Does nothing if the size of an `int` is the same as `int64`. ## (This is the case on 64 bit processors.) ## **Deprecated since version 0.19.9**: Use unsigned integers instead. cast[int64](uint64(cast[uint](x))) @@ -94,46 +90,46 @@ when defined(nimNoZeroExtendMagic): cast[int8](x) proc toU16*(x: int): int16 {.deprecated.} = - ## treats `x` as unsigned and converts it to an ``int16`` by taking the last + ## treats `x` as unsigned and converts it to an `int16` by taking the last ## 16 bits from `x`. ## **Deprecated since version 0.19.9**: Use unsigned integers instead. cast[int16](x) proc toU32*(x: int64): int32 {.deprecated.} = - ## treats `x` as unsigned and converts it to an ``int32`` by taking the + ## treats `x` as unsigned and converts it to an `int32` by taking the ## last 32 bits from `x`. ## **Deprecated since version 0.19.9**: Use unsigned integers instead. cast[int32](x) elif not defined(js): proc ze*(x: int8): int {.magic: "Ze8ToI", noSideEffect, deprecated.} - ## zero extends a smaller integer type to ``int``. This treats `x` as + ## zero extends a smaller integer type to `int`. This treats `x` as ## unsigned. ## **Deprecated since version 0.19.9**: Use unsigned integers instead. proc ze*(x: int16): int {.magic: "Ze16ToI", noSideEffect, deprecated.} - ## zero extends a smaller integer type to ``int``. This treats `x` as + ## zero extends a smaller integer type to `int`. This treats `x` as ## unsigned. ## **Deprecated since version 0.19.9**: Use unsigned integers instead. proc ze64*(x: int8): int64 {.magic: "Ze8ToI64", noSideEffect, deprecated.} - ## zero extends a smaller integer type to ``int64``. This treats `x` as + ## zero extends a smaller integer type to `int64`. This treats `x` as ## unsigned. ## **Deprecated since version 0.19.9**: Use unsigned integers instead. proc ze64*(x: int16): int64 {.magic: "Ze16ToI64", noSideEffect, deprecated.} - ## zero extends a smaller integer type to ``int64``. This treats `x` as + ## zero extends a smaller integer type to `int64`. This treats `x` as ## unsigned. ## **Deprecated since version 0.19.9**: Use unsigned integers instead. proc ze64*(x: int32): int64 {.magic: "Ze32ToI64", noSideEffect, deprecated.} - ## zero extends a smaller integer type to ``int64``. This treats `x` as + ## zero extends a smaller integer type to `int64`. This treats `x` as ## unsigned. ## **Deprecated since version 0.19.9**: Use unsigned integers instead. proc ze64*(x: int): int64 {.magic: "ZeIToI64", noSideEffect, deprecated.} - ## zero extends a smaller integer type to ``int64``. This treats `x` as - ## unsigned. Does nothing if the size of an ``int`` is the same as ``int64``. + ## zero extends a smaller integer type to `int64`. This treats `x` as + ## unsigned. Does nothing if the size of an `int` is the same as `int64`. ## (This is the case on 64 bit processors.) ## **Deprecated since version 0.19.9**: Use unsigned integers instead. @@ -143,12 +139,12 @@ elif not defined(js): ## **Deprecated since version 0.19.9**: Use unsigned integers instead. proc toU16*(x: int): int16 {.magic: "ToU16", noSideEffect, deprecated.} - ## treats `x` as unsigned and converts it to an ``int16`` by taking the last + ## treats `x` as unsigned and converts it to an `int16` by taking the last ## 16 bits from `x`. ## **Deprecated since version 0.19.9**: Use unsigned integers instead. proc toU32*(x: int64): int32 {.magic: "ToU32", noSideEffect, deprecated.} - ## treats `x` as unsigned and converts it to an ``int32`` by taking the + ## treats `x` as unsigned and converts it to an `int32` by taking the ## last 32 bits from `x`. ## **Deprecated since version 0.19.9**: Use unsigned integers instead. @@ -167,20 +163,13 @@ proc `-`*(x: int16): int16 {.magic: "UnaryMinusI", noSideEffect.} proc `-`*(x: int32): int32 {.magic: "UnaryMinusI", noSideEffect.} proc `-`*(x: int64): int64 {.magic: "UnaryMinusI64", noSideEffect.} -proc `not`*(x: int): int {.magic: "BitnotI", noSideEffect.} +proc `not`*(x: int): int {.magic: "BitnotI", noSideEffect.} = ## Computes the `bitwise complement` of the integer `x`. - ## - ## .. code-block:: Nim - ## var - ## a = 0'u8 - ## b = 0'i8 - ## c = 1000'u16 - ## d = 1000'i16 - ## - ## echo not a # => 255 - ## echo not b # => -1 - ## echo not c # => 64535 - ## echo not d # => -1001 + runnableExamples: + assert not 0'u8 == 255 + assert not 0'i8 == -1 + assert not 1000'u16 == 64535 + assert not 1000'i16 == -1001 proc `not`*(x: int8): int8 {.magic: "BitnotI", noSideEffect.} proc `not`*(x: int16): int16 {.magic: "BitnotI", noSideEffect.} proc `not`*(x: int32): int32 {.magic: "BitnotI", noSideEffect.} @@ -207,34 +196,32 @@ proc `*`*(x, y: int16): int16 {.magic: "MulI", noSideEffect.} proc `*`*(x, y: int32): int32 {.magic: "MulI", noSideEffect.} proc `*`*(x, y: int64): int64 {.magic: "MulI", noSideEffect.} -proc `div`*(x, y: int): int {.magic: "DivI", noSideEffect.} +proc `div`*(x, y: int): int {.magic: "DivI", noSideEffect.} = ## Computes the integer division. ## - ## This is roughly the same as ``trunc(x/y)``. - ## - ## .. code-block:: Nim - ## ( 1 div 2) == 0 - ## ( 2 div 2) == 1 - ## ( 3 div 2) == 1 - ## ( 7 div 3) == 2 - ## (-7 div 3) == -2 - ## ( 7 div -3) == -2 - ## (-7 div -3) == 2 + ## This is roughly the same as `math.trunc(x/y).int`. + runnableExamples: + assert (1 div 2) == 0 + assert (2 div 2) == 1 + assert (3 div 2) == 1 + assert (7 div 3) == 2 + assert (-7 div 3) == -2 + assert (7 div -3) == -2 + assert (-7 div -3) == 2 proc `div`*(x, y: int8): int8 {.magic: "DivI", noSideEffect.} proc `div`*(x, y: int16): int16 {.magic: "DivI", noSideEffect.} proc `div`*(x, y: int32): int32 {.magic: "DivI", noSideEffect.} proc `div`*(x, y: int64): int64 {.magic: "DivI", noSideEffect.} -proc `mod`*(x, y: int): int {.magic: "ModI", noSideEffect.} +proc `mod`*(x, y: int): int {.magic: "ModI", noSideEffect.} = ## Computes the integer modulo operation (remainder). ## - ## This is the same as ``x - (x div y) * y``. - ## - ## .. code-block:: Nim - ## ( 7 mod 5) == 2 - ## (-7 mod 5) == -2 - ## ( 7 mod -5) == 2 - ## (-7 mod -5) == -2 + ## This is the same as `x - (x div y) * y`. + runnableExamples: + assert (7 mod 5) == 2 + assert (-7 mod 5) == -2 + assert (7 mod -5) == 2 + assert (-7 mod -5) == -2 proc `mod`*(x, y: int8): int8 {.magic: "ModI", noSideEffect.} proc `mod`*(x, y: int16): int16 {.magic: "ModI", noSideEffect.} proc `mod`*(x, y: int32): int32 {.magic: "ModI", noSideEffect.} @@ -248,7 +235,7 @@ when defined(nimOldShiftRight) or not defined(nimAshr): proc `shr`*(x: int32, y: SomeInteger): int32 {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.} proc `shr`*(x: int64, y: SomeInteger): int64 {.magic: "ShrI", noSideEffect, deprecated: shrDepMessage.} else: - proc `shr`*(x: int, y: SomeInteger): int {.magic: "AshrI", noSideEffect.} + proc `shr`*(x: int, y: SomeInteger): int {.magic: "AshrI", noSideEffect.} = ## Computes the `shift right` operation of `x` and `y`, filling ## vacant bit positions with the sign bit. ## @@ -256,38 +243,36 @@ else: ## is different than in *C*. ## ## See also: - ## * `ashr proc <#ashr,int,SomeInteger>`_ for arithmetic shift right - ## - ## .. code-block:: Nim - ## 0b0001_0000'i8 shr 2 == 0b0000_0100'i8 - ## 0b0000_0001'i8 shr 1 == 0b0000_0000'i8 - ## 0b1000_0000'i8 shr 4 == 0b1111_1000'i8 - ## -1 shr 5 == -1 - ## 1 shr 5 == 0 - ## 16 shr 2 == 4 - ## -16 shr 2 == -4 + ## * `ashr func<#ashr,int,SomeInteger>`_ for arithmetic shift right + runnableExamples: + assert 0b0001_0000'i8 shr 2 == 0b0000_0100'i8 + assert 0b0000_0001'i8 shr 1 == 0b0000_0000'i8 + assert 0b1000_0000'i8 shr 4 == 0b1111_1000'i8 + assert -1 shr 5 == -1 + assert 1 shr 5 == 0 + assert 16 shr 2 == 4 + assert -16 shr 2 == -4 proc `shr`*(x: int8, y: SomeInteger): int8 {.magic: "AshrI", noSideEffect.} proc `shr`*(x: int16, y: SomeInteger): int16 {.magic: "AshrI", noSideEffect.} proc `shr`*(x: int32, y: SomeInteger): int32 {.magic: "AshrI", noSideEffect.} proc `shr`*(x: int64, y: SomeInteger): int64 {.magic: "AshrI", noSideEffect.} -proc `shl`*(x: int, y: SomeInteger): int {.magic: "ShlI", noSideEffect.} +proc `shl`*(x: int, y: SomeInteger): int {.magic: "ShlI", noSideEffect.} = ## Computes the `shift left` operation of `x` and `y`. ## ## **Note**: `Operator precedence `_ ## is different than in *C*. - ## - ## .. code-block:: Nim - ## 1'i32 shl 4 == 0x0000_0010 - ## 1'i64 shl 4 == 0x0000_0000_0000_0010 + runnableExamples: + assert 1'i32 shl 4 == 0x0000_0010 + assert 1'i64 shl 4 == 0x0000_0000_0000_0010 proc `shl`*(x: int8, y: SomeInteger): int8 {.magic: "ShlI", noSideEffect.} proc `shl`*(x: int16, y: SomeInteger): int16 {.magic: "ShlI", noSideEffect.} proc `shl`*(x: int32, y: SomeInteger): int32 {.magic: "ShlI", noSideEffect.} proc `shl`*(x: int64, y: SomeInteger): int64 {.magic: "ShlI", noSideEffect.} when defined(nimAshr): - proc ashr*(x: int, y: SomeInteger): int {.magic: "AshrI", noSideEffect.} + proc ashr*(x: int, y: SomeInteger): int {.magic: "AshrI", noSideEffect.} = ## Shifts right by pushing copies of the leftmost bit in from the left, ## and let the rightmost bits fall off. ## @@ -295,12 +280,11 @@ when defined(nimAshr): ## call syntax for it. ## ## See also: - ## * `shr proc <#shr,int,SomeInteger>`_ - ## - ## .. code-block:: Nim - ## ashr(0b0001_0000'i8, 2) == 0b0000_0100'i8 - ## ashr(0b1000_0000'i8, 8) == 0b1111_1111'i8 - ## ashr(0b1000_0000'i8, 1) == 0b1100_0000'i8 + ## * `shr func<#shr,int,SomeInteger>`_ + runnableExamples: + assert ashr(0b0001_0000'i8, 2) == 0b0000_0100'i8 + assert ashr(0b1000_0000'i8, 8) == 0b1111_1111'i8 + assert ashr(0b1000_0000'i8, 1) == 0b1100_0000'i8 proc ashr*(x: int8, y: SomeInteger): int8 {.magic: "AshrI", noSideEffect.} proc ashr*(x: int16, y: SomeInteger): int16 {.magic: "AshrI", noSideEffect.} proc ashr*(x: int32, y: SomeInteger): int32 {.magic: "AshrI", noSideEffect.} @@ -309,34 +293,31 @@ else: # used for bootstrapping the compiler proc ashr*[T](x: T, y: SomeInteger): T = discard -proc `and`*(x, y: int): int {.magic: "BitandI", noSideEffect.} +proc `and`*(x, y: int): int {.magic: "BitandI", noSideEffect.} = ## Computes the `bitwise and` of numbers `x` and `y`. - ## - ## .. code-block:: Nim - ## (0b0011 and 0b0101) == 0b0001 - ## (0b0111 and 0b1100) == 0b0100 + runnableExamples: + assert (0b0011 and 0b0101) == 0b0001 + assert (0b0111 and 0b1100) == 0b0100 proc `and`*(x, y: int8): int8 {.magic: "BitandI", noSideEffect.} proc `and`*(x, y: int16): int16 {.magic: "BitandI", noSideEffect.} proc `and`*(x, y: int32): int32 {.magic: "BitandI", noSideEffect.} proc `and`*(x, y: int64): int64 {.magic: "BitandI", noSideEffect.} -proc `or`*(x, y: int): int {.magic: "BitorI", noSideEffect.} +proc `or`*(x, y: int): int {.magic: "BitorI", noSideEffect.} = ## Computes the `bitwise or` of numbers `x` and `y`. - ## - ## .. code-block:: Nim - ## (0b0011 or 0b0101) == 0b0111 - ## (0b0111 or 0b1100) == 0b1111 + runnableExamples: + assert (0b0011 or 0b0101) == 0b0111 + assert (0b0111 or 0b1100) == 0b1111 proc `or`*(x, y: int8): int8 {.magic: "BitorI", noSideEffect.} proc `or`*(x, y: int16): int16 {.magic: "BitorI", noSideEffect.} proc `or`*(x, y: int32): int32 {.magic: "BitorI", noSideEffect.} proc `or`*(x, y: int64): int64 {.magic: "BitorI", noSideEffect.} -proc `xor`*(x, y: int): int {.magic: "BitxorI", noSideEffect.} +proc `xor`*(x, y: int): int {.magic: "BitxorI", noSideEffect.} = ## Computes the `bitwise xor` of numbers `x` and `y`. - ## - ## .. code-block:: Nim - ## (0b0011 xor 0b0101) == 0b0110 - ## (0b0111 xor 0b1100) == 0b1011 + runnableExamples: + assert (0b0011 xor 0b0101) == 0b0110 + assert (0b0111 xor 0b1100) == 0b1011 proc `xor`*(x, y: int8): int8 {.magic: "BitxorI", noSideEffect.} proc `xor`*(x, y: int16): int16 {.magic: "BitxorI", noSideEffect.} proc `xor`*(x, y: int32): int32 {.magic: "BitxorI", noSideEffect.} From 876fa3e62e41cd366b89137cc3c4f6b5b8b2bee8 Mon Sep 17 00:00:00 2001 From: rockcavera Date: Wed, 30 Dec 2020 21:41:25 -0300 Subject: [PATCH 1241/3768] adding missing commas in std/bitops (#16520) adding missing commas between the importc and header pragmas of some procs. --- lib/pure/bitops.nim | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/pure/bitops.nim b/lib/pure/bitops.nim index 4f22388f3..855289e84 100644 --- a/lib/pure/bitops.nim +++ b/lib/pure/bitops.nim @@ -477,11 +477,11 @@ when useGCC_builtins: elif useVCC_builtins: # Counts the number of one bits (population count) in a 16-, 32-, or 64-byte unsigned integer. proc builtin_popcnt16(a2: uint16): uint16 {. - importc: "__popcnt16"header: "", noSideEffect.} + importc: "__popcnt16", header: "", noSideEffect.} proc builtin_popcnt32(a2: uint32): uint32 {. - importc: "__popcnt"header: "", noSideEffect.} + importc: "__popcnt", header: "", noSideEffect.} proc builtin_popcnt64(a2: uint64): uint64 {. - importc: "__popcnt64"header: "", noSideEffect.} + importc: "__popcnt64", header: "", noSideEffect.} # Search the mask data from most significant bit (MSB) to least significant bit (LSB) for a set bit (1). proc bitScanReverse(index: ptr culong, mask: culong): cuchar {. @@ -506,9 +506,9 @@ elif useICC_builtins: # see also: https://software.intel.com/en-us/node/523362 # Count the number of bits set to 1 in an integer a, and return that count in dst. proc builtin_popcnt32(a: cint): cint {. - importc: "_popcnt"header: "", noSideEffect.} + importc: "_popcnt", header: "", noSideEffect.} proc builtin_popcnt64(a: uint64): cint {. - importc: "_popcnt64"header: "", noSideEffect.} + importc: "_popcnt64", header: "", noSideEffect.} # Returns the number of trailing 0-bits in x, starting at the least significant bit position. If x is 0, the result is undefined. proc bitScanForward(p: ptr uint32, b: uint32): cuchar {. From 17992fca1dc0b3674dce123296b277551bbca1db Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 30 Dec 2020 20:29:22 -0800 Subject: [PATCH 1242/3768] disable ggplotnim, refs #16523 (#16524) --- testament/important_packages.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index dca8c5a4f..65104e1fe 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -41,7 +41,7 @@ pkg1 "fidget" pkg1 "fragments", "nim c -r fragments/dsl.nim" pkg1 "gara" pkg1 "glob" -pkg1 "ggplotnim", "nim c -d:noCairo -r tests/tests.nim" +# pkg1 "ggplotnim", "nim c -d:noCairo -r tests/tests.nim" # pending bug #16523 # pkg1 "gittyup", "nimble test", "https://github.com/disruptek/gittyup" pkg1 "gnuplot", "nim c gnuplot.nim" # pkg1 "gram", "nim c -r --gc:arc --define:danger tests/test.nim", "https://github.com/disruptek/gram" From 5984f7a7dda5e6fb3119cd5705d5758e1b8f3fc7 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Thu, 31 Dec 2020 13:20:04 +0300 Subject: [PATCH 1243/3768] RST: improve line blocks (#16518) --- lib/packages/docutils/rst.nim | 24 +++++++++----- lib/packages/docutils/rstast.nim | 5 +-- lib/packages/docutils/rstgen.nim | 16 +++++++-- nimdoc/rst2html/expected/rst_examples.html | 2 +- tests/stdlib/trstgen.nim | 38 +++++++++++++++++++--- 5 files changed, 68 insertions(+), 17 deletions(-) diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 698d76da1..8d16edc61 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -1147,7 +1147,8 @@ proc isAdornmentHeadline(p: RstParser, adornmentIdx: int): bool = proc isLineBlock(p: RstParser): bool = var j = tokenAfterNewline(p) result = currentTok(p).col == p.tok[j].col and p.tok[j].symbol == "|" or - p.tok[j].col > currentTok(p).col + p.tok[j].col > currentTok(p).col or + p.tok[j].symbol == "\n" proc predNL(p: RstParser): bool = result = true @@ -1245,21 +1246,28 @@ proc whichSection(p: RstParser): RstNodeKind = proc parseLineBlock(p: var RstParser): PRstNode = result = nil - if nextTok(p).kind == tkWhite: + if nextTok(p).kind in {tkWhite, tkIndent}: var col = currentTok(p).col result = newRstNode(rnLineBlock) - pushInd(p, p.tok[p.idx + 2].col) - inc p.idx, 2 while true: var item = newRstNode(rnLineBlockItem) - parseSection(p, item) + if nextTok(p).kind == tkWhite: + if nextTok(p).symbol.len > 1: # pass additional indentation after '| ' + item.text = nextTok(p).symbol + inc p.idx, 2 + pushInd(p, p.tok[p.idx].col) + parseSection(p, item) + popInd(p) + else: # tkIndent => add an empty line + item.text = "\n" + inc p.idx, 1 result.add(item) if currentTok(p).kind == tkIndent and currentTok(p).ival == col and - nextTok(p).symbol == "|" and p.tok[p.idx + 2].kind == tkWhite: - inc p.idx, 3 + nextTok(p).symbol == "|" and + p.tok[p.idx + 2].kind in {tkWhite, tkIndent}: + inc p.idx, 1 else: break - popInd(p) proc parseParagraph(p: var RstParser, result: PRstNode) = while true: diff --git a/lib/packages/docutils/rstast.nim b/lib/packages/docutils/rstast.nim index f01bcada1..e4e192fa3 100644 --- a/lib/packages/docutils/rstast.nim +++ b/lib/packages/docutils/rstast.nim @@ -35,7 +35,8 @@ type rnOptionList, rnOptionListItem, rnOptionGroup, rnOption, rnOptionString, rnOptionArgument, rnDescription, rnLiteralBlock, rnQuotedLiteralBlock, rnLineBlock, # the | thingie - rnLineBlockItem, # sons of the | thing + rnLineBlockItem, # a son of rnLineBlock - one line inside it. + # When `RstNode` text="\n" the line's empty rnBlockQuote, # text just indented rnTable, rnGridTable, rnMarkdownTable, rnTableRow, rnTableHeaderCell, rnTableDataCell, rnLabel, # used for footnotes and other things @@ -73,7 +74,7 @@ type kind*: RstNodeKind ## the node's kind text*: string ## valid for leafs in the AST; and the title of ## the document or the section; and rnEnumList - ## and rnAdmonition + ## and rnAdmonition; and rnLineBlockItem level*: int ## valid for some node kinds sons*: RstNodeSeq ## the node's sons diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 5aa2b03d4..52125b52c 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -1160,9 +1160,21 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = of rnQuotedLiteralBlock: doAssert false, "renderRstToOut" of rnLineBlock: - renderAux(d, n, "

                                      $1

                                      ", "$1\n\n", result) + if n.sons.len == 1 and n.sons[0].text == "\n": + # whole line block is one empty line, no need to add extra spacing + renderAux(d, n, "

                                      $1

                                      ", "\n\n$1", result) + else: # add extra spacing around the line block for Latex + renderAux(d, n, "

                                      $1

                                      ", "\n\\vspace{0.5em}\n$1\\vspace{0.5em}\n", result) of rnLineBlockItem: - renderAux(d, n, "$1
                                      ", "$1\\\\\n", result) + if n.text.len == 0: # normal case - no additional indentation + renderAux(d, n, "$1
                                      ", "\\noindent $1\n\n", result) + elif n.text == "\n": # add one empty line + renderAux(d, n, "
                                      ", "\\vspace{1em}\n", result) + else: # additional indentation w.r.t. '| ' + let indent = $(0.5 * (n.text.len - 1).toFloat) & "em" + renderAux(d, n, + "$1
                                      ", + "\\noindent\\hspace{" & indent & "}$1\n\n", result) of rnBlockQuote: renderAux(d, n, "

                                      $1

                                      \n", "\\begin{quote}$1\\end{quote}\n", result) diff --git a/nimdoc/rst2html/expected/rst_examples.html b/nimdoc/rst2html/expected/rst_examples.html index a95fac0bd..23d192009 100644 --- a/nimdoc/rst2html/expected/rst_examples.html +++ b/nimdoc/rst2html/expected/rst_examples.html @@ -215,7 +215,7 @@ stmt = IND{>} stmt ^+ IND{=} DED # list of statements

                                      Apart from built-in operations like array indexing, memory allocation, etc. the raise statement is the only way to raise an exception.

                                      typedesc used as a parameter type also introduces an implicit generic. typedesc has its own set of rules:

                                      The !=, >, >=, in, notin, isnot operators are in fact templates:

                                      -

                                      a > b is transformed into b < a.
                                      a in b is transformed into contains(b, a).
                                      notin and isnot have the obvious meanings.

                                      A template where every parameter is untyped is called an immediate template. For historical reasons templates can be explicitly annotated with an immediate pragma and then these templates do not take part in overloading resolution and the parameters' types are ignored by the compiler. Explicit immediate templates are now deprecated.

                                      +

                                      a > b is transformed into b < a.
                                      a in b is transformed into contains(b, a).
                                      notin and isnot have the obvious meanings.

                                      A template where every parameter is untyped is called an immediate template. For historical reasons templates can be explicitly annotated with an immediate pragma and then these templates do not take part in overloading resolution and the parameters' types are ignored by the compiler. Explicit immediate templates are now deprecated.

                                      Symbol lookup in generics

                                      Open and Closed symbols

                                      The symbol binding rules in generics are slightly subtle: There are "open" and "closed" symbols. A "closed" symbol cannot be re-bound in the instantiation context, an "open" symbol can. Per default overloaded symbols are open and every other symbol is closed.

                                      diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim index 54a3db202..7acb23c5b 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -355,11 +355,41 @@ Test1 rstGenera.renderRstToOut(rstParse(input1, "", 1, 1, option, {}), output1) doAssert rstGenera.meta[metaTitle] == "Test1" # check that title was not overwritten to '|' - doAssert "line block
                                      " in output1 - doAssert "other line
                                      " in output1 + doAssert output1 == "



                                      line block
                                      other line

                                      " let output1l = rstToLatex(input1, {}) - doAssert "line block\\\\" in output1l - doAssert "other line\\\\" in output1l + doAssert "line block\n\n" in output1l + doAssert "other line\n\n" in output1l + doAssert output1l.count("\\vspace") == 2 + 2 # +2 surrounding paddings + + let input2 = dedent""" + Paragraph1 + + | + + Paragraph2""" + + let output2 = rstToHtml(input2, {roSupportMarkdown}, defaultConfig()) + doAssert "Paragraph1


                                      Paragraph2

                                      \n" == output2 + + let input3 = dedent""" + | xxx + | yyy + | zzz""" + + let output3 = rstToHtml(input3, {roSupportMarkdown}, defaultConfig()) + doAssert "xxx
                                      " in output3 + doAssert "yyy
                                      " in output3 + doAssert "zzz
                                      " in output3 + + # check that '| ' with a few spaces is still parsed as new line + let input4 = dedent""" + | xxx + | + | zzz""" + + let output4 = rstToHtml(input4, {roSupportMarkdown}, defaultConfig()) + doAssert "xxx

                                      " in output4 + doAssert "zzz
                                      " in output4 test "RST enumerated lists": let input1 = dedent """ From 5fb56a3b2c83b62d72c72a9d56ef1333671bc2b6 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Thu, 31 Dec 2020 04:54:40 -0600 Subject: [PATCH 1244/3768] refactor cmpIgnoreStyle and cmpIgnoreCase (#16399) * init * support strutils * more * better * Call len once per string/cstring * Change var to let * Compare ternary on first char * More appropriate param name * fix * better * one test * impl * more efficient * minor Co-authored-by: Clyybber --- lib/core/macros.nim | 19 +------ lib/core/typeinfo.nim | 26 +++------ lib/pure/cstrutils.nim | 90 +++++++++++++++----------------- lib/pure/strutils.nim | 30 ++--------- lib/std/private/strimpl.nim | 53 +++++++++++++++++++ tests/js/tstdlib_various.nim | 17 +----- tests/stdlib/tcstrutils.nim | 30 +++++++++++ tests/stdlib/tstdlib_various.nim | 23 +------- 8 files changed, 141 insertions(+), 147 deletions(-) create mode 100644 lib/std/private/strimpl.nim create mode 100644 tests/stdlib/tcstrutils.nim diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 97c3a46c5..748464061 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -1389,25 +1389,10 @@ when defined(nimVmEqIdent): ## these nodes will be unwrapped. else: + from std/private/strimpl import cmpIgnoreStyleImpl # this procedure is optimized for native code, it should not be compiled to nimVM bytecode. proc cmpIgnoreStyle(a, b: cstring): int {.noSideEffect.} = - proc toLower(c: char): char {.inline.} = - if c in {'A'..'Z'}: result = chr(ord(c) + (ord('a') - ord('A'))) - else: result = c - var i = 0 - var j = 0 - # first char is case sensitive - if a[0] != b[0]: return 1 - while true: - while a[i] == '_': inc(i) - while b[j] == '_': inc(j) # BUGFIX: typo - var aa = toLower(a[i]) - var bb = toLower(b[j]) - result = ord(aa) - ord(bb) - if result != 0 or aa == '\0': break - inc(i) - inc(j) - + cmpIgnoreStyleImpl(a, b, true) proc eqIdent*(a, b: string): bool = cmpIgnoreStyle(a, b) == 0 ## Check if two idents are equal. diff --git a/lib/core/typeinfo.nim b/lib/core/typeinfo.nim index c15f6dc1f..9b7e32466 100644 --- a/lib/core/typeinfo.nim +++ b/lib/core/typeinfo.nim @@ -91,6 +91,8 @@ when not defined(gcDestructors): else: include system/seqs_v2_reimpl +from std/private/strimpl import cmpIgnoreStyleImpl + when not defined(js): template rawType(x: Any): PNimType = cast[PNimType](x.rawTypePtr) @@ -366,36 +368,22 @@ iterator fields*(x: Any): tuple[name: string, any: Any] = for name, any in items(ret): yield ($name, any) -proc cmpIgnoreStyle(a, b: cstring): int {.noSideEffect.} = - proc toLower(c: char): char {.inline.} = - if c in {'A'..'Z'}: result = chr(ord(c) + (ord('a') - ord('A'))) - else: result = c - var i = 0 - var j = 0 - if a[0] != b[0]: return 1 - while true: - while a[i] == '_': inc(i) - while b[j] == '_': inc(j) # BUGFIX: typo - var aa = toLower(a[i]) - var bb = toLower(b[j]) - result = ord(aa) - ord(bb) - if result != 0 or aa == '\0': break - inc(i) - inc(j) +proc cmpNimIdentifier(a, b: cstring): int {.noSideEffect.} = + cmpIgnoreStyleImpl(a, b, true) proc getFieldNode(p: pointer, n: ptr TNimNode, name: cstring): ptr TNimNode = case n.kind of nkNone: assert(false) of nkSlot: - if cmpIgnoreStyle(n.name, name) == 0: + if cmpNimIdentifier(n.name, name) == 0: result = n of nkList: for i in 0..n.len-1: result = getFieldNode(p, n.sons[i], name) if result != nil: break of nkCase: - if cmpIgnoreStyle(n.name, name) == 0: + if cmpNimIdentifier(n.name, name) == 0: result = n else: var m = selectBranch(p, n) @@ -599,7 +587,7 @@ proc getEnumOrdinal*(x: Any, name: string): int = var n = typ.node var s = n.sons for i in 0 .. n.len-1: - if cmpIgnoreStyle($s[i].name, name) == 0: + if cmpNimIdentifier($s[i].name, name) == 0: if ntfEnumHole notin typ.flags: return i else: diff --git a/lib/pure/cstrutils.nim b/lib/pure/cstrutils.nim index 601508e2e..a95c13fb5 100644 --- a/lib/pure/cstrutils.nim +++ b/lib/pure/cstrutils.nim @@ -12,12 +12,8 @@ ## save allocations. include "system/inclrtl" +import std/private/strimpl -proc toLowerAscii(c: char): char {.inline.} = - if c in {'A'..'Z'}: - result = chr(ord(c) + (ord('a') - ord('A'))) - else: - result = c when defined(js): proc startsWith*(s, prefix: cstring): bool {.noSideEffect, @@ -25,7 +21,13 @@ when defined(js): proc endsWith*(s, suffix: cstring): bool {.noSideEffect, importjs: "#.endsWith(#)".} - + + proc cmpIgnoreStyle*(a, b: cstring): int {.noSideEffect.} = + cmpIgnoreStyleImpl(a, b) + + proc cmpIgnoreCase*(a, b: cstring): int {.noSideEffect.} = + cmpIgnoreCaseImpl(a, b) + # JS string has more operations that might warrant its own module: # https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String else: @@ -57,45 +59,39 @@ else: inc(i) if suffix[i] == '\0': return true -proc cmpIgnoreStyle*(a, b: cstring): int {.noSideEffect, - rtl, extern: "csuCmpIgnoreStyle".} = - ## Semantically the same as ``cmp(normalize($a), normalize($b))``. It - ## is just optimized to not allocate temporary strings. This should - ## NOT be used to compare Nim identifier names. use `macros.eqIdent` - ## for that. Returns: - ## - ## | 0 if a == b - ## | < 0 if a < b - ## | > 0 if a > b - ## - ## Not supported for JS backend, use `strutils.cmpIgnoreStyle - ## `_ instead. - var i = 0 - var j = 0 - while true: - while a[i] == '_': inc(i) - while b[j] == '_': inc(j) # BUGFIX: typo - var aa = toLowerAscii(a[i]) - var bb = toLowerAscii(b[j]) - result = ord(aa) - ord(bb) - if result != 0 or aa == '\0': break - inc(i) - inc(j) + proc cmpIgnoreStyle*(a, b: cstring): int {.noSideEffect, + rtl, extern: "csuCmpIgnoreStyle".} = + ## Semantically the same as ``cmp(normalize($a), normalize($b))``. It + ## is just optimized to not allocate temporary strings. This should + ## NOT be used to compare Nim identifier names. use `macros.eqIdent` + ## for that. Returns: + ## + ## | 0 if a == b + ## | < 0 if a < b + ## | > 0 if a > b + var i = 0 + var j = 0 + while true: + while a[i] == '_': inc(i) + while b[j] == '_': inc(j) # BUGFIX: typo + var aa = toLowerAscii(a[i]) + var bb = toLowerAscii(b[j]) + result = ord(aa) - ord(bb) + if result != 0 or aa == '\0': break + inc(i) + inc(j) -proc cmpIgnoreCase*(a, b: cstring): int {.noSideEffect, - rtl, extern: "csuCmpIgnoreCase".} = - ## Compares two strings in a case insensitive manner. Returns: - ## - ## | 0 if a == b - ## | < 0 if a < b - ## | > 0 if a > b - ## - ## Not supported for JS backend, use `strutils.cmpIgnoreCase - ## `_ instead. - var i = 0 - while true: - var aa = toLowerAscii(a[i]) - var bb = toLowerAscii(b[i]) - result = ord(aa) - ord(bb) - if result != 0 or aa == '\0': break - inc(i) + proc cmpIgnoreCase*(a, b: cstring): int {.noSideEffect, + rtl, extern: "csuCmpIgnoreCase".} = + ## Compares two strings in a case insensitive manner. Returns: + ## + ## | 0 if a == b + ## | < 0 if a < b + ## | > 0 if a > b + var i = 0 + while true: + var aa = toLowerAscii(a[i]) + var bb = toLowerAscii(b[i]) + result = ord(aa) - ord(bb) + if result != 0 or aa == '\0': break + inc(i) diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 0028ac4fb..b1418a3ec 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -81,6 +81,8 @@ when defined(nimVmExportFixed): include "system/inclrtl" import std/private/since +from std/private/strimpl import cmpIgnoreStyleImpl, cmpIgnoreCaseImpl + const Whitespace* = {' ', '\t', '\v', '\r', '\l', '\f'} @@ -319,13 +321,7 @@ func cmpIgnoreCase*(a, b: string): int {.rtl, extern: "nsuCmpIgnoreCase".} = doAssert cmpIgnoreCase("FooBar", "foobar") == 0 doAssert cmpIgnoreCase("bar", "Foo") < 0 doAssert cmpIgnoreCase("Foo5", "foo4") > 0 - var i = 0 - var m = min(a.len, b.len) - while i < m: - result = ord(toLowerAscii(a[i])) - ord(toLowerAscii(b[i])) - if result != 0: return - inc(i) - result = a.len - b.len + cmpIgnoreCaseImpl(a, b) {.push checks: off, line_trace: off.} # this is a hot-spot in the compiler! # thus we compile without checks here @@ -344,25 +340,7 @@ func cmpIgnoreStyle*(a, b: string): int {.rtl, extern: "nsuCmpIgnoreStyle".} = runnableExamples: doAssert cmpIgnoreStyle("foo_bar", "FooBar") == 0 doAssert cmpIgnoreStyle("foo_bar_5", "FooBar4") > 0 - var i = 0 - var j = 0 - while true: - while i < a.len and a[i] == '_': inc i - while j < b.len and b[j] == '_': inc j - var aa = if i < a.len: toLowerAscii(a[i]) else: '\0' - var bb = if j < b.len: toLowerAscii(b[j]) else: '\0' - result = ord(aa) - ord(bb) - if result != 0: return result - # the characters are identical: - if i >= a.len: - # both cursors at the end: - if j >= b.len: return 0 - # not yet at the end of 'b': - return -1 - elif j >= b.len: - return 1 - inc i - inc j + cmpIgnoreStyleImpl(a, b) {.pop.} # --------- Private templates for different split separators ----------- diff --git a/lib/std/private/strimpl.nim b/lib/std/private/strimpl.nim new file mode 100644 index 000000000..ae752165a --- /dev/null +++ b/lib/std/private/strimpl.nim @@ -0,0 +1,53 @@ +func toLowerAscii*(c: char): char {.inline.} = + if c in {'A'..'Z'}: + result = chr(ord(c) + (ord('a') - ord('A'))) + else: + result = c + +template firstCharCaseSensitiveImpl(a, b: typed, aLen, bLen: int) = + if aLen == 0 or bLen == 0: + return aLen - bLen + if a[0] != b[0]: return ord(a[0]) - ord(b[0]) + +template cmpIgnoreStyleImpl*(a, b: typed, firstCharCaseSensitive: static bool = false) = + # a, b are string or cstring + let aLen = a.len + let bLen = b.len + var i = 0 + var j = 0 + when firstCharCaseSensitive: + firstCharCaseSensitiveImpl(a, b, aLen, bLen) + inc i + inc j + while true: + while i < aLen and a[i] == '_': inc i + while j < bLen and b[j] == '_': inc j + let aa = if i < aLen: toLowerAscii(a[i]) else: '\0' + let bb = if j < bLen: toLowerAscii(b[j]) else: '\0' + result = ord(aa) - ord(bb) + if result != 0: return result + # the characters are identical: + if i >= aLen: + # both cursors at the end: + if j >= bLen: return 0 + # not yet at the end of 'b': + return -1 + elif j >= bLen: + return 1 + inc i + inc j + +template cmpIgnoreCaseImpl*(a, b: typed, firstCharCaseSensitive: static bool = false) = + # a, b are string or cstring + let aLen = a.len + let bLen = b.len + var i = 0 + when firstCharCaseSensitive: + firstCharCaseSensitiveImpl(a, b, aLen, bLen) + inc i + var m = min(aLen, bLen) + while i < m: + result = ord(toLowerAscii(a[i])) - ord(toLowerAscii(b[i])) + if result != 0: return + inc i + result = aLen - bLen diff --git a/tests/js/tstdlib_various.nim b/tests/js/tstdlib_various.nim index d19f40c39..a1bb63d46 100644 --- a/tests/js/tstdlib_various.nim +++ b/tests/js/tstdlib_various.nim @@ -29,7 +29,7 @@ Hi Andreas! How do you feel, Rumpf? """ import - critbits, cstrutils, sets, strutils, tables, random, algorithm, ropes, + critbits, sets, strutils, tables, random, algorithm, ropes, lists, htmlgen, xmltree, strtabs @@ -177,18 +177,3 @@ block txmltree: ]) ]) doAssert(y.innerText == "foobar") - - - -block tcstrutils: - let s = cstring "abcdef" - doAssert s.startsWith("a") - doAssert not s.startsWith("b") - doAssert s.endsWith("f") - doAssert not s.endsWith("a") - - let a = cstring "abracadabra" - doAssert a.startsWith("abra") - doAssert not a.startsWith("bra") - doAssert a.endsWith("abra") - doAssert not a.endsWith("dab") diff --git a/tests/stdlib/tcstrutils.nim b/tests/stdlib/tcstrutils.nim new file mode 100644 index 000000000..1daf32aa5 --- /dev/null +++ b/tests/stdlib/tcstrutils.nim @@ -0,0 +1,30 @@ +discard """ + targets: "c cpp js" +""" + +import cstrutils + + +block tcstrutils: + let s = cstring "abcdef" + doAssert s.startsWith("a") + doAssert not s.startsWith("b") + doAssert s.endsWith("f") + doAssert not s.endsWith("a") + + let a = cstring "abracadabra" + doAssert a.startsWith("abra") + doAssert not a.startsWith("bra") + doAssert a.endsWith("abra") + doAssert not a.endsWith("dab") + + doAssert cmpIgnoreCase(cstring "FooBar", "foobar") == 0 + doAssert cmpIgnoreCase(cstring "bar", "Foo") < 0 + doAssert cmpIgnoreCase(cstring "Foo5", "foo4") > 0 + + doAssert cmpIgnoreStyle(cstring "foo_bar", "FooBar") == 0 + doAssert cmpIgnoreStyle(cstring "foo_bar_5", "FooBar4") > 0 + + doAssert cmpIgnoreCase(cstring "", cstring "") == 0 + doAssert cmpIgnoreCase(cstring "", cstring "Hello") < 0 + doAssert cmpIgnoreCase(cstring "wind", cstring "") > 0 diff --git a/tests/stdlib/tstdlib_various.nim b/tests/stdlib/tstdlib_various.nim index cddd43f6e..b153fd2ba 100644 --- a/tests/stdlib/tstdlib_various.nim +++ b/tests/stdlib/tstdlib_various.nim @@ -38,7 +38,7 @@ true """ import - critbits, cstrutils, sets, strutils, tables, random, algorithm, re, ropes, + critbits, sets, strutils, tables, random, algorithm, re, ropes, segfaults, lists, parsesql, streams, os, htmlgen, xmltree, strtabs @@ -245,24 +245,3 @@ block txmltree: ]) ]) doAssert(y.innerText == "foobar") - - -block tcstrutils: - let s = cstring "abcdef" - doAssert s.startsWith("a") - doAssert not s.startsWith("b") - doAssert s.endsWith("f") - doAssert not s.endsWith("a") - - let a = cstring "abracadabra" - doAssert a.startsWith("abra") - doAssert not a.startsWith("bra") - doAssert a.endsWith("abra") - doAssert not a.endsWith("dab") - - doAssert cmpIgnoreCase(cstring "FooBar", "foobar") == 0 - doAssert cmpIgnoreCase(cstring "bar", "Foo") < 0 - doAssert cmpIgnoreCase(cstring "Foo5", "foo4") > 0 - - doAssert cmpIgnoreStyle(cstring "foo_bar", "FooBar") == 0 - doAssert cmpIgnoreStyle(cstring "foo_bar_5", "FooBar4") > 0 From 9d4a1f95544c6c7c58829384cf8045e3803faab0 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Fri, 1 Jan 2021 03:59:19 -0600 Subject: [PATCH 1245/3768] fix #16494 (#16513) * fix #16494 * fix * fix * fix * fix * fix * fix performance * add comments * improve performance * Update lib/system.nim Co-authored-by: Timothee Cour * Update lib/system.nim Co-authored-by: Timothee Cour * Update tests/stdlib/tmath_misc.nim Co-authored-by: Timothee Cour * Update tests/stdlib/tmath_misc.nim Co-authored-by: Timothee Cour Co-authored-by: Timothee Cour --- lib/system.nim | 24 ++++++++++++++++++++---- tests/stdlib/tmath_misc.nim | 24 ++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 4 deletions(-) create mode 100644 tests/stdlib/tmath_misc.nim diff --git a/lib/system.nim b/lib/system.nim index b6f7d655a..29c137b63 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1522,10 +1522,26 @@ include "system/iterators_1" {.push stackTrace: off.} -proc abs*(x: float64): float64 {.noSideEffect, inline.} = - if x < 0.0: -x else: x -proc abs*(x: float32): float32 {.noSideEffect, inline.} = - if x < 0.0: -x else: x + +when defined(js): + proc js_abs[T: SomeNumber](x: T): T {.importc: "Math.abs".} +else: + proc c_fabs(x: cdouble): cdouble {.importc: "fabs", header: "".} + proc c_fabsf(x: cfloat): cfloat {.importc: "fabsf", header: "".} + +proc abs*[T: float64 | float32](x: T): T {.noSideEffect, inline.} = + when nimvm: + if x < 0.0: result = -x + elif x == 0.0: result = 0.0 # handle 0.0, -0.0 + else: result = x # handle NaN, > 0 + else: + when defined(js): result = js_abs(x) + else: + when T is float64: + result = c_fabs(x) + else: + result = c_fabsf(x) + proc min*(x, y: float32): float32 {.noSideEffect, inline.} = if x <= y or y != y: x else: y proc min*(x, y: float64): float64 {.noSideEffect, inline.} = diff --git a/tests/stdlib/tmath_misc.nim b/tests/stdlib/tmath_misc.nim new file mode 100644 index 000000000..978e3e94d --- /dev/null +++ b/tests/stdlib/tmath_misc.nim @@ -0,0 +1,24 @@ +discard """ + targets: "c js" +""" + +# TODO merge this to tmath.nim once tmath.nim supports js target + +import math + +proc main() = + block: + doAssert 1.0 / abs(-0.0) == Inf + doAssert 1.0 / abs(0.0) == Inf + doAssert -1.0 / abs(-0.0) == -Inf + doAssert -1.0 / abs(0.0) == -Inf + doAssert abs(0.0) == 0.0 + doAssert abs(0.0'f32) == 0.0'f32 + + doAssert abs(Inf) == Inf + doAssert abs(-Inf) == Inf + doAssert abs(NaN).isNaN + doAssert abs(-NaN).isNaN + +static: main() +main() From eb25d7dd712bd339f11becbf5499d04930382a30 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Fri, 1 Jan 2021 11:32:41 -0600 Subject: [PATCH 1246/3768] enable ggplotnim (#16538) --- testament/important_packages.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 65104e1fe..dca8c5a4f 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -41,7 +41,7 @@ pkg1 "fidget" pkg1 "fragments", "nim c -r fragments/dsl.nim" pkg1 "gara" pkg1 "glob" -# pkg1 "ggplotnim", "nim c -d:noCairo -r tests/tests.nim" # pending bug #16523 +pkg1 "ggplotnim", "nim c -d:noCairo -r tests/tests.nim" # pkg1 "gittyup", "nimble test", "https://github.com/disruptek/gittyup" pkg1 "gnuplot", "nim c gnuplot.nim" # pkg1 "gram", "nim c -r --gc:arc --define:danger tests/test.nim", "https://github.com/disruptek/gram" From 4a479f4a6eb2f9c5a3665d95fa86d993fdb66281 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Fri, 1 Jan 2021 11:33:35 -0600 Subject: [PATCH 1247/3768] update contributing.rst (#16530) * update docs * Apply suggestions from code review * Update doc/contributing.rst --- doc/contributing.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/contributing.rst b/doc/contributing.rst index 34c963410..d1837ac14 100644 --- a/doc/contributing.rst +++ b/doc/contributing.rst @@ -239,7 +239,7 @@ not guaranteed to stay in sync, so ``runnableExamples`` is usually preferred: .. code-block:: nim proc someproc*(): string = - ## Return "something" + ## Returns "something" ## ## .. code-block:: ## echo someproc() # "something" @@ -262,12 +262,12 @@ first appearance of the proc. echo "hello" The preferred documentation style is to begin with a capital letter and use -the imperative (command) form. That is, between: +the third-person singular. That is, between: .. code-block:: nim proc hello*(): string = - ## Return "hello" + ## Returns "hello" result = "hello" or @@ -275,7 +275,7 @@ or .. code-block:: nim proc hello*(): string = - ## says hello + ## say hello result = "hello" the first is preferred. From e67059a03a02b53919fafd0f99eb59baa9ad9a53 Mon Sep 17 00:00:00 2001 From: Federico Ceratto Date: Fri, 1 Jan 2021 17:36:20 +0000 Subject: [PATCH 1248/3768] Add short description on GC (#16535) --- doc/gc.rst | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/doc/gc.rst b/doc/gc.rst index 804481cd9..a19ad0671 100644 --- a/doc/gc.rst +++ b/doc/gc.rst @@ -14,10 +14,15 @@ Nim's Memory Management Introduction ============ -This document describes how the multi-paradigm memory management strategies work. -How to tune the garbage collectors for your needs, like (soft) `realtime systems`:idx:, -and how the memory management strategies that are not garbage collectors work. +A memory-management algorithm optimal for every use-case cannot exist. +Nim provides multiple paradigms for needs ranging from large multi-threaded +applications, to games, hard-realtime systems and small microcontrollers. +This document describes how the management strategies work; +How to tune the garbage collectors for your needs, like (soft) `realtime systems`:idx:, +and how the memory management strategies other than garbage collectors work. + +.. note:: the default GC is incremental, thread-local and not "stop-the-world" Multi-paradigm Memory Management Strategies =========================================== From b5101b23b5291d12ebbee4b8de37ac128ce9b7f4 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Fri, 1 Jan 2021 12:01:07 -0600 Subject: [PATCH 1249/3768] refactor the tests of options (#16527) --- tests/stdlib/toptions.nim | 269 ++++++++++++++++++++++---------------- 1 file changed, 154 insertions(+), 115 deletions(-) diff --git a/tests/stdlib/toptions.nim b/tests/stdlib/toptions.nim index 04544ffb7..4764ce05f 100644 --- a/tests/stdlib/toptions.nim +++ b/tests/stdlib/toptions.nim @@ -1,19 +1,8 @@ discard """ - output: '''{"foo":{"test":"123"}}''' + targets: "c js" """ -import json, options - -type - Foo = ref object - test: string - Test = object - foo: Option[Foo] - -let js = """{"foo": {"test": "123"}}""" -let parsed = parseJson(js) -let a = parsed.to(Test) -echo $(%*a) +import std/[json, options] # RefPerson is used to test that overloaded `==` operator is not called by @@ -26,133 +15,183 @@ proc `==`(a, b: RefPerson): bool = assert(not a.isNil and not b.isNil) a.name == b.name -block options: - # work around a bug in unittest - let intNone = none(int) - let stringNone = none(string) - block example: - proc find(haystack: string, needle: char): Option[int] = - for i, c in haystack: - if c == needle: - return some i +template disableJsVm(body) = + # something doesn't work in JS VM + when defined(js): + when nimvm: discard + else: body + else: + body - doAssert("abc".find('c').get() == 2) +proc main() = + type + Foo = ref object + test: string + Test = object + foo: Option[Foo] - let result = "team".find('i') + let js = """{"foo": {"test": "123"}}""" + let parsed = parseJson(js) + let a = parsed.to(Test) + doAssert $(%*a) == """{"foo":{"test":"123"}}""" - doAssert result == intNone - doAssert result.isNone + block options: + # work around a bug in unittest + let intNone = none(int) + let stringNone = none(string) - block some: - doAssert some(6).get() == 6 - doAssert some("a").unsafeGet() == "a" - doAssert some(6).isSome - doAssert some("a").isSome + block example: + proc find(haystack: string, needle: char): Option[int] = + for i, c in haystack: + if c == needle: + return some i - block none: - doAssertRaises UnpackDefect: - discard none(int).get() - doAssert(none(int).isNone) - doAssert(not none(string).isSome) + doAssert("abc".find('c').get() == 2) - block equality: - doAssert some("a") == some("a") - doAssert some(7) != some(6) - doAssert some("a") != stringNone - doAssert intNone == intNone + let result = "team".find('i') - when compiles(some("a") == some(5)): - doAssert false - when compiles(none(string) == none(int)): - doAssert false + doAssert result == intNone + doAssert result.isNone - block get_with_a_default_value: - doAssert(some("Correct").get("Wrong") == "Correct") - doAssert(stringNone.get("Correct") == "Correct") + block some: + doAssert some(6).get() == 6 + doAssert some("a").unsafeGet() == "a" + doAssert some(6).isSome + doAssert some("a").isSome - block stringify: - doAssert($(some("Correct")) == "Some(\"Correct\")") - doAssert($(stringNone) == "None[string]") + block none: + doAssertRaises UnpackDefect: + discard none(int).get() + doAssert(none(int).isNone) + doAssert(not none(string).isSome) - block map_with_a_void_result: - var procRan = 0 - some(123).map(proc (v: int) = procRan = v) - doAssert procRan == 123 - intNone.map(proc (v: int) = doAssert false) + block equality: + doAssert some("a") == some("a") + doAssert some(7) != some(6) + doAssert some("a") != stringNone + doAssert intNone == intNone - block map: - doAssert(some(123).map(proc (v: int): int = v * 2) == some(246)) - doAssert(intNone.map(proc (v: int): int = v * 2).isNone) + when compiles(some("a") == some(5)): + doAssert false + when compiles(none(string) == none(int)): + doAssert false - block filter: - doAssert(some(123).filter(proc (v: int): bool = v == 123) == some(123)) - doAssert(some(456).filter(proc (v: int): bool = v == 123).isNone) - doAssert(intNone.filter(proc (v: int): bool = doAssert false).isNone) + block get_with_a_default_value: + doAssert(some("Correct").get("Wrong") == "Correct") + doAssert(stringNone.get("Correct") == "Correct") - block flatMap: - proc addOneIfNotZero(v: int): Option[int] = - if v != 0: - result = some(v + 1) - else: - result = none(int) + block stringify: + doAssert($(some("Correct")) == "Some(\"Correct\")") + doAssert($(stringNone) == "None[string]") - doAssert(some(1).flatMap(addOneIfNotZero) == some(2)) - doAssert(some(0).flatMap(addOneIfNotZero) == none(int)) - doAssert(some(1).flatMap(addOneIfNotZero).flatMap(addOneIfNotZero) == some(3)) + disableJsVm: + block map_with_a_void_result: + var procRan = 0 + # TODO closure anonymous functions doesn't work in VM with JS + # Error: cannot evaluate at compile time: procRan + some(123).map(proc (v: int) = procRan = v) + doAssert procRan == 123 + intNone.map(proc (v: int) = doAssert false) - proc maybeToString(v: int): Option[string] = - if v != 0: - result = some($v) - else: - result = none(string) + block map: + doAssert(some(123).map(proc (v: int): int = v * 2) == some(246)) + doAssert(intNone.map(proc (v: int): int = v * 2).isNone) - doAssert(some(1).flatMap(maybeToString) == some("1")) + block filter: + doAssert(some(123).filter(proc (v: int): bool = v == 123) == some(123)) + doAssert(some(456).filter(proc (v: int): bool = v == 123).isNone) + doAssert(intNone.filter(proc (v: int): bool = doAssert false).isNone) - proc maybeExclaim(v: string): Option[string] = - if v != "": - result = some v & "!" - else: - result = none(string) + block flatMap: + proc addOneIfNotZero(v: int): Option[int] = + if v != 0: + result = some(v + 1) + else: + result = none(int) - doAssert(some(1).flatMap(maybeToString).flatMap(maybeExclaim) == some("1!")) - doAssert(some(0).flatMap(maybeToString).flatMap(maybeExclaim) == none(string)) + doAssert(some(1).flatMap(addOneIfNotZero) == some(2)) + doAssert(some(0).flatMap(addOneIfNotZero) == none(int)) + doAssert(some(1).flatMap(addOneIfNotZero).flatMap(addOneIfNotZero) == some(3)) - block SomePointer: - var intref: ref int - doAssert(option(intref).isNone) - intref.new - doAssert(option(intref).isSome) + proc maybeToString(v: int): Option[string] = + if v != 0: + result = some($v) + else: + result = none(string) - let tmp = option(intref) - doAssert(sizeof(tmp) == sizeof(ptr int)) + doAssert(some(1).flatMap(maybeToString) == some("1")) - var prc = proc (x: int): int = x + 1 - doAssert(option(prc).isSome) - prc = nil - doAssert(option(prc).isNone) + proc maybeExclaim(v: string): Option[string] = + if v != "": + result = some v & "!" + else: + result = none(string) - block: - doAssert(none[int]().isNone) - doAssert(none(int) == none[int]()) + doAssert(some(1).flatMap(maybeToString).flatMap(maybeExclaim) == some("1!")) + doAssert(some(0).flatMap(maybeToString).flatMap(maybeExclaim) == none(string)) - # "$ on typed with .name" - block: - type Named = object - name: string + block SomePointer: + var intref: ref int + doAssert(option(intref).isNone) + intref.new + doAssert(option(intref).isSome) - let nobody = none(Named) - doAssert($nobody == "None[Named]") + let tmp = option(intref) + doAssert(sizeof(tmp) == sizeof(ptr int)) - # "$ on type with name()" - block: - type Person = object - myname: string + var prc = proc (x: int): int = x + 1 + doAssert(option(prc).isSome) + prc = nil + doAssert(option(prc).isNone) - let noperson = none(Person) - doAssert($noperson == "None[Person]") + block: + doAssert(none[int]().isNone) + doAssert(none(int) == none[int]()) - # "Ref type with overloaded `==`" - block: - let p = some(RefPerson.new()) - doAssert p.isSome + # "$ on typed with .name" + block: + type Named = object + name: string + + let nobody = none(Named) + doAssert($nobody == "None[Named]") + + # "$ on type with name()" + block: + type Person = object + myname: string + + let noperson = none(Person) + doAssert($noperson == "None[Person]") + + # "Ref type with overloaded `==`" + block: + let p = some(RefPerson.new()) + doAssert p.isSome + + block: # test cstring + block: + let x = some("".cstring) + doAssert x.isSome + doAssert x.get == "" + + block: + let x = some("12345".cstring) + doAssert x.isSome + doAssert x.get == "12345" + + block: + let x = "12345".cstring + let y = some(x) + doAssert y.isSome + doAssert y.get == "12345" + + block: + let x = none(cstring) + doAssert x.isNone + doAssert $x == "None[cstring]" + + +static: main() +main() From 5953fbd8347838f162d64e4acdc259fd372210a3 Mon Sep 17 00:00:00 2001 From: n5m <72841454+n5m@users.noreply.github.com> Date: Fri, 1 Jan 2021 18:01:45 +0000 Subject: [PATCH 1250/3768] link to POSIX sendSignal from osproc.kill docs (#16475) and from osproc.terminate docs --- lib/pure/osproc.nim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 9c198e6fa..9cac8e232 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -187,6 +187,7 @@ proc terminate*(p: Process) {.rtl, extern: "nosp$1", tags: [].} ## * `suspend proc <#suspend,Process>`_ ## * `resume proc <#resume,Process>`_ ## * `kill proc <#kill,Process>`_ + ## * `posix_utils.sendSignal(pid: Pid, signal: int) `_ proc kill*(p: Process) {.rtl, extern: "nosp$1", tags: [].} ## Kill the process `p`. @@ -198,6 +199,7 @@ proc kill*(p: Process) {.rtl, extern: "nosp$1", tags: [].} ## * `suspend proc <#suspend,Process>`_ ## * `resume proc <#resume,Process>`_ ## * `terminate proc <#terminate,Process>`_ + ## * `posix_utils.sendSignal(pid: Pid, signal: int) `_ proc running*(p: Process): bool {.rtl, extern: "nosp$1", tags: [].} ## Returns true if the process `p` is still running. Returns immediately. From bc0b4fbc9e422ab87104610d879f02fe08771b74 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Fri, 1 Jan 2021 12:28:59 -0600 Subject: [PATCH 1251/3768] happy new year 2021 (#16537) --- copying.txt | 2 +- readme.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/copying.txt b/copying.txt index a7ba3143f..a498a9525 100644 --- a/copying.txt +++ b/copying.txt @@ -1,7 +1,7 @@ ===================================================== Nim -- a Compiler for Nim. https://nim-lang.org/ -Copyright (C) 2006-2020 Andreas Rumpf. All rights reserved. +Copyright (C) 2006-2021 Andreas Rumpf. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/readme.md b/readme.md index bfd153143..fa7a2d86d 100644 --- a/readme.md +++ b/readme.md @@ -204,7 +204,7 @@ Nim. You are explicitly permitted to develop commercial applications using Nim. Please read the [copying.txt](copying.txt) file for more details. -Copyright © 2006-2020 Andreas Rumpf, all rights reserved. +Copyright © 2006-2021 Andreas Rumpf, all rights reserved. [nim-site]: https://nim-lang.org [nim-forum]: https://forum.nim-lang.org From d069c08d2b077abfb6e7e73e516448de4faa89ef Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Fri, 1 Jan 2021 12:39:05 -0600 Subject: [PATCH 1252/3768] follow up #16399 clean up docs (#16539) * follow up #16399 clean up docs * more --- lib/pure/cstrutils.nim | 71 ++++++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 30 deletions(-) diff --git a/lib/pure/cstrutils.nim b/lib/pure/cstrutils.nim index a95c13fb5..390aac00b 100644 --- a/lib/pure/cstrutils.nim +++ b/lib/pure/cstrutils.nim @@ -7,8 +7,8 @@ # distribution, for details about the copyright. # -## This module supports helper routines for working with ``cstring`` -## without having to convert ``cstring`` to ``string`` in order to +## This module supports helper routines for working with `cstring` +## without having to convert `cstring` to `string` in order to ## save allocations. include "system/inclrtl" @@ -16,41 +16,45 @@ import std/private/strimpl when defined(js): - proc startsWith*(s, prefix: cstring): bool {.noSideEffect, - importjs: "#.startsWith(#)".} + func startsWith*(s, prefix: cstring): bool {.importjs: "#.startsWith(#)".} - proc endsWith*(s, suffix: cstring): bool {.noSideEffect, - importjs: "#.endsWith(#)".} + func endsWith*(s, suffix: cstring): bool {.importjs: "#.endsWith(#)".} - proc cmpIgnoreStyle*(a, b: cstring): int {.noSideEffect.} = + func cmpIgnoreStyle*(a, b: cstring): int = cmpIgnoreStyleImpl(a, b) - proc cmpIgnoreCase*(a, b: cstring): int {.noSideEffect.} = + func cmpIgnoreCase*(a, b: cstring): int = cmpIgnoreCaseImpl(a, b) # JS string has more operations that might warrant its own module: # https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String else: - proc startsWith*(s, prefix: cstring): bool {.noSideEffect, - rtl, extern: "csuStartsWith".} = - ## Returns true if ``s`` starts with ``prefix``. + func startsWith*(s, prefix: cstring): bool {.rtl, extern: "csuStartsWith".} = + ## Returns true if `s` starts with `prefix`. ## - ## If ``prefix == ""`` true is returned. + ## If `prefix == ""` true is returned. ## - ## JS backend uses native ``String.prototype.startsWith``. + ## JS backend uses native `String.prototype.startsWith`. + runnableExamples: + assert startsWith(cstring"Hello, Nimion", cstring"Hello") + assert not startsWith(cstring"Hello, Nimion", cstring"Nimion") + var i = 0 while true: if prefix[i] == '\0': return true if s[i] != prefix[i]: return false inc(i) - proc endsWith*(s, suffix: cstring): bool {.noSideEffect, - rtl, extern: "csuEndsWith".} = - ## Returns true if ``s`` ends with ``suffix``. + func endsWith*(s, suffix: cstring): bool {.rtl, extern: "csuEndsWith".} = + ## Returns true if `s` ends with `suffix`. ## - ## If ``suffix == ""`` true is returned. - ## - ## JS backend uses native ``String.prototype.endsWith``. + ## If `suffix == ""` true is returned. + ## + ## JS backend uses native `String.prototype.endsWith`. + runnableExamples: + assert endsWith(cstring"Hello, Nimion", cstring"Nimion") + assert not endsWith(cstring"Hello, Nimion", cstring"Hello") + let slen = s.len var i = 0 var j = slen - len(suffix) @@ -59,16 +63,18 @@ else: inc(i) if suffix[i] == '\0': return true - proc cmpIgnoreStyle*(a, b: cstring): int {.noSideEffect, - rtl, extern: "csuCmpIgnoreStyle".} = - ## Semantically the same as ``cmp(normalize($a), normalize($b))``. It + func cmpIgnoreStyle*(a, b: cstring): int {.rtl, extern: "csuCmpIgnoreStyle".} = + ## Semantically the same as `cmp(normalize($a), normalize($b))`. It ## is just optimized to not allocate temporary strings. This should ## NOT be used to compare Nim identifier names. use `macros.eqIdent` ## for that. Returns: ## - ## | 0 if a == b - ## | < 0 if a < b - ## | > 0 if a > b + ## .. code-block:: + ## 0 if a == b + ## < 0 if a < b + ## > 0 if a > b + runnableExamples: + assert cmpIgnoreStyle(cstring"hello", cstring"H_e_L_Lo") == 0 var i = 0 var j = 0 while true: @@ -81,13 +87,18 @@ else: inc(i) inc(j) - proc cmpIgnoreCase*(a, b: cstring): int {.noSideEffect, - rtl, extern: "csuCmpIgnoreCase".} = + func cmpIgnoreCase*(a, b: cstring): int {.rtl, extern: "csuCmpIgnoreCase".} = ## Compares two strings in a case insensitive manner. Returns: ## - ## | 0 if a == b - ## | < 0 if a < b - ## | > 0 if a > b + ## .. code-block:: + ## 0 if a == b + ## < 0 if a < b + ## > 0 if a > b + runnableExamples: + assert cmpIgnoreCase(cstring"hello", cstring"HeLLo") == 0 + assert cmpIgnoreCase(cstring"echo", cstring"hello") < 0 + assert cmpIgnoreCase(cstring"yellow", cstring"hello") > 0 + var i = 0 while true: var aa = toLowerAscii(a[i]) From b254d91cd0099c1e0bc15126c52bd6485575ec5a Mon Sep 17 00:00:00 2001 From: n5m <72841454+n5m@users.noreply.github.com> Date: Fri, 1 Jan 2021 18:41:49 +0000 Subject: [PATCH 1253/3768] reuse const (#16422) --- lib/pure/osproc.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 9cac8e232..bc447eb67 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -981,7 +981,7 @@ elif not defined(useNimRtl): when not defined(android): "/bin/sh" else: "/system/bin/sh" data.sysCommand = useShPath - sysArgsRaw = @[data.sysCommand, "-c", command] + sysArgsRaw = @[useShPath, "-c", command] assert args.len == 0, "`args` has to be empty when using poEvalCommand." else: data.sysCommand = command From 505d04389ad5c83b50001e3108d89fcdec9c462f Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Fri, 1 Jan 2021 15:44:06 -0300 Subject: [PATCH 1254/3768] Documentation only Testament unittest (#16532) * Link Testament from unittest doc * Update lib/pure/unittest.nim Co-authored-by: flywind <43030857+xflywind@users.noreply.github.com> Co-authored-by: flywind <43030857+xflywind@users.noreply.github.com> --- lib/pure/unittest.nim | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim index 18b09e4c0..c387e14e1 100644 --- a/lib/pure/unittest.nim +++ b/lib/pure/unittest.nim @@ -9,11 +9,6 @@ ## :Author: Zahary Karadjov ## -## **Note**: Instead of ``unittest.nim``, please consider to use -## the ``testament`` tool which offers process isolation for your tests. -## Also ``when isMainModule: doAssert conditionHere`` is usually a -## much simpler solution for testing purposes. -## ## This module implements boilerplate to make unit testing easy. ## ## The test status and name is printed after any output or traceback. @@ -22,9 +17,18 @@ ## parent test as failed. Setup and teardown are inherited. Setup can be ## overridden locally. ## -## Compiled test files as well as ``nim c -r `` +## Compiled test files as well as `nim c -r ` ## exit with 0 for success (no failed tests) or 1 for failure. ## +## Testament +## ========= +## +## Instead of `unittest`, please consider using +## `the Testament tool `_ which offers process isolation for your tests. +## +## Alternatively using `when isMainModule: doAssert conditionHere` is usually a +## much simpler solution for testing purposes. +## ## Running a single test ## ===================== ## @@ -39,7 +43,7 @@ ## Running a single test suite ## =========================== ## -## Specify the suite name delimited by ``"::"``. +## Specify the suite name delimited by `"::"`. ## ## .. code:: ## @@ -50,7 +54,7 @@ ## ## A single ``"*"`` can be used for globbing. ## -## Delimit the end of a suite name with ``"::"``. +## Delimit the end of a suite name with `"::"`. ## ## Tests matching **any** of the arguments are executed. ## @@ -92,7 +96,7 @@ ## discard v[4] ## ## echo "suite teardown: run once after the tests" -## +## ## Limitations/Bugs ## ================ ## Since `check` will rewrite some expressions for supporting checkpoints From 0d0e43469f060818ec09d74de5b0bb7ded891898 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 1 Jan 2021 13:55:22 -0800 Subject: [PATCH 1255/3768] fix #14340 (#16386) --- compiler/ccgexprs.nim | 4 +++- tests/vm/tvmmisc.nim | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 45044e0ff..53b2832f0 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -3094,7 +3094,9 @@ proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType): Rope else: result = genConstSeq(p, n, typ, isConst) of tyProc: - if typ.callConv == ccClosure and n.len > 1 and n[1].kind == nkNilLit: + if typ.callConv == ccClosure and n.safeLen > 1 and n[1].kind == nkNilLit: + # n.kind could be: nkClosure, nkTupleConstr and maybe others; `n.safeLen` + # guards against the case of `nkSym`, refs bug #14340. # Conversion: nimcall -> closure. # this hack fixes issue that nkNilLit is expanded to {NIM_NIL,NIM_NIL} # this behaviour is needed since closure_var = nil must be diff --git a/tests/vm/tvmmisc.nim b/tests/vm/tvmmisc.nim index 005f7e255..c1e563806 100644 --- a/tests/vm/tvmmisc.nim +++ b/tests/vm/tvmmisc.nim @@ -306,6 +306,23 @@ block: # bug #8007 const d = @[Cost(kind: Fixed, cost: 999), Cost(kind: Dynamic, handler: foo)] doAssert $d == "@[(kind: Fixed, cost: 999), (kind: Dynamic, handler: ...)]" +block: # bug #14340 + block: + proc opl3EnvelopeCalcSin0() = discard + type EnvelopeSinfunc = proc() + # const EnvelopeCalcSin0 = opl3EnvelopeCalcSin0 # ok + const EnvelopeCalcSin0: EnvelopeSinfunc = opl3EnvelopeCalcSin0 # was bug + const envelopeSin = [EnvelopeCalcSin0] + var a = 0 + envelopeSin[a]() + + block: + type Mutator = proc() {.noSideEffect, gcsafe, locks: 0.} + proc mutator0() = discard + const mTable = [Mutator(mutator0)] + var i=0 + mTable[i]() + block: # VM wrong register free causes errors in unrelated code block: # bug #15597 #[ From 73a8b950cb6abf36a0d29c210bb7db302ae68325 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 2 Jan 2021 07:30:39 +0100 Subject: [PATCH 1256/3768] big steps torwards an efficient, simple IC implementation (#16543) * reworked ID handling * the packed AST now has its own ID mechanism * basic serialization code works * extract rodfiles to its own module * rodfiles: store and compare configs * rodfiles: store dependencies * store config at the end * precise dependency tracking * dependency tracking for rodfiles * completed loading of PSym, PType, etc * removed dead code * bugfix: do not realloc seqs when taking addr into an element * make IC opt-in for now * makes tcompilerapi green again * final cleanups Co-authored-by: Andy Davidoff --- compiler/ast.nim | 31 +- compiler/ccgexprs.nim | 2 +- compiler/ccgtypes.nim | 12 +- compiler/cgmeth.nim | 6 +- compiler/closureiters.nim | 12 +- compiler/commands.nim | 5 - compiler/enumtostr.nim | 16 +- compiler/evaltempl.nim | 2 +- compiler/ic/bitabs.nim | 49 +- compiler/ic/design.rst | 11 +- compiler/ic/from_packed_ast.nim | 12 - compiler/ic/packed_ast.nim | 330 ++++++---- compiler/ic/rodfiles.nim | 143 ++++ compiler/ic/to_packed_ast.nim | 692 +++++++++++++++++-- compiler/importer.nim | 3 +- compiler/incremental.nim | 198 ------ compiler/injectdestructors.nim | 6 +- compiler/lambdalifting.nim | 28 +- compiler/liftdestructors.nim | 20 +- compiler/lookups.nim | 2 +- compiler/lowerings.nim | 18 +- compiler/magicsys.nim | 16 +- compiler/main.nim | 5 +- compiler/modulegraphs.nim | 12 +- compiler/modules.nim | 8 +- compiler/nilcheck.nim | 128 ++-- compiler/options.nim | 4 + compiler/passes.nim | 139 ++-- compiler/plugins/itersgen.nim | 4 +- compiler/plugins/locals.nim | 2 +- compiler/pragmas.nim | 10 +- compiler/rod.nim | 31 - compiler/rodimpl.nim | 950 --------------------------- compiler/sem.nim | 20 +- compiler/semdata.nim | 45 +- compiler/semexprs.nim | 10 +- compiler/semfields.nim | 2 +- compiler/semfold.nim | 4 +- compiler/semgnrc.nim | 2 +- compiler/seminst.nim | 13 +- compiler/semmagic.nim | 24 +- compiler/semparallel.nim | 2 +- compiler/sempass2.nim | 2 +- compiler/semstmts.nim | 13 +- compiler/semtempl.nim | 2 +- compiler/semtypes.nim | 20 +- compiler/semtypinst.nim | 6 +- compiler/sigmatch.nim | 10 +- compiler/sinkparameter_inference.nim | 2 +- compiler/spawn.nim | 34 +- compiler/transf.nim | 6 +- compiler/types.nim | 4 +- compiler/vm.nim | 6 +- compiler/vmdeps.nim | 4 +- compiler/vmmarshal.nim | 2 +- lib/js/jsffi.nim | 4 +- tests/compilerapi/tcompilerapi.nim | 2 +- 57 files changed, 1429 insertions(+), 1717 deletions(-) delete mode 100644 compiler/ic/from_packed_ast.nim create mode 100644 compiler/ic/rodfiles.nim delete mode 100644 compiler/incremental.nim delete mode 100644 compiler/rod.nim delete mode 100644 compiler/rodimpl.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index 8acf08284..ccf4fe497 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1074,18 +1074,35 @@ template id*(a: PIdObj): int = (x.itemId.module.int shl moduleShift) + x.itemId.item.int type - IdGenerator* = ref ItemId # unfortunately, we really need the 'shared mutable' aspect here. + IdGenerator* = ref object # unfortunately, we really need the 'shared mutable' aspect here. + module*: int32 + symId*: int32 + typeId*: int32 + +proc hash*(x: ItemId): Hash = + var h: Hash = hash(x.module) + h = h !& hash(x.item) + result = !$h const PackageModuleId* = -3'i32 proc idGeneratorFromModule*(m: PSym): IdGenerator = assert m.kind == skModule - result = IdGenerator(module: m.itemId.module, item: m.itemId.item) + result = IdGenerator(module: m.itemId.module, symId: m.itemId.item, typeId: 0) -proc nextId*(x: IdGenerator): ItemId {.inline.} = - inc x.item - result = x[] +proc nextSymId*(x: IdGenerator): ItemId {.inline.} = + inc x.symId + result = ItemId(module: x.module, item: x.symId) + +proc nextTypeId*(x: IdGenerator): ItemId {.inline.} = + inc x.typeId + result = ItemId(module: x.module, item: x.typeId) + +when false: + proc nextId*(x: IdGenerator): ItemId {.inline.} = + inc x.item + result = x[] when false: proc storeBack*(dest: var IdGenerator; src: IdGenerator) {.inline.} = @@ -1831,7 +1848,7 @@ proc toVar*(typ: PType; kind: TTypeKind; idgen: IdGenerator): PType = ## returned. Otherwise ``typ`` is simply returned as-is. result = typ if typ.kind != kind: - result = newType(kind, nextId(idgen), typ.owner) + result = newType(kind, nextTypeId(idgen), typ.owner) rawAddSon(result, typ) proc toRef*(typ: PType; idgen: IdGenerator): PType = @@ -1839,7 +1856,7 @@ proc toRef*(typ: PType; idgen: IdGenerator): PType = ## returned. Otherwise ``typ`` is simply returned as-is. result = typ if typ.skipTypes({tyAlias, tyGenericInst}).kind == tyObject: - result = newType(tyRef, nextId(idgen), typ.owner) + result = newType(tyRef, nextTypeId(idgen), typ.owner) rawAddSon(result, typ) proc toObject*(typ: PType): PType = diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 53b2832f0..b8a8d21b0 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1772,7 +1772,7 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) = else: internalError(p.config, e.info, "genArrayLen()") proc makePtrType(baseType: PType; idgen: IdGenerator): PType = - result = newType(tyPtr, nextId idgen, baseType.owner) + result = newType(tyPtr, nextTypeId idgen, baseType.owner) addSonSkipIntLit(result, baseType, idgen) proc makeAddr(n: PNode; idgen: IdGenerator): PNode = diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 142fec056..ab12bad1e 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -1267,9 +1267,9 @@ proc genArrayInfo(m: BModule, typ: PType, name: Rope; info: TLineInfo) = proc fakeClosureType(m: BModule; owner: PSym): PType = # we generate the same RTTI as for a tuple[pointer, ref tuple[]] - result = newType(tyTuple, nextId m.idgen, owner) - result.rawAddSon(newType(tyPointer, nextId m.idgen, owner)) - var r = newType(tyRef, nextId m.idgen, owner) + result = newType(tyTuple, nextTypeId m.idgen, owner) + result.rawAddSon(newType(tyPointer, nextTypeId m.idgen, owner)) + var r = newType(tyRef, nextTypeId m.idgen, owner) let obj = createObj(m.g.graph, m.idgen, owner, owner.info, final=false) r.rawAddSon(obj) result.rawAddSon(r) @@ -1396,9 +1396,9 @@ proc genTypeInfoV2(m: BModule, t: PType; info: TLineInfo): Rope = result = prefixTI.rope & result & ")".rope proc openArrayToTuple(m: BModule; t: PType): PType = - result = newType(tyTuple, nextId m.idgen, t.owner) - let p = newType(tyPtr, nextId m.idgen, t.owner) - let a = newType(tyUncheckedArray, nextId m.idgen, t.owner) + result = newType(tyTuple, nextTypeId m.idgen, t.owner) + let p = newType(tyPtr, nextTypeId m.idgen, t.owner) + let a = newType(tyUncheckedArray, nextTypeId m.idgen, t.owner) a.add t.lastSon p.add a result.add p diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim index a0c16f2ed..5c5d35093 100644 --- a/compiler/cgmeth.nim +++ b/compiler/cgmeth.nim @@ -108,10 +108,10 @@ proc attachDispatcher(s: PSym, dispatcher: PNode) = s.ast[dispatcherPos] = dispatcher proc createDispatcher(s: PSym; idgen: IdGenerator): PSym = - var disp = copySym(s, nextId(idgen)) + var disp = copySym(s, nextSymId(idgen)) incl(disp.flags, sfDispatcher) excl(disp.flags, sfExported) - disp.typ = copyType(disp.typ, nextId(idgen), disp.typ.owner) + disp.typ = copyType(disp.typ, nextTypeId(idgen), disp.typ.owner) # we can't inline the dispatcher itself (for now): if disp.typ.callConv == ccInline: disp.typ.callConv = ccNimCall disp.ast = copyTree(s.ast) @@ -119,7 +119,7 @@ proc createDispatcher(s: PSym; idgen: IdGenerator): PSym = disp.loc.r = nil if s.typ[0] != nil: if disp.ast.len > resultPos: - disp.ast[resultPos].sym = copySym(s.ast[resultPos].sym, nextId(idgen)) + disp.ast[resultPos].sym = copySym(s.ast[resultPos].sym, nextSymId(idgen)) else: # We've encountered a method prototype without a filled-in # resultPos slot. We put a placeholder in there that will diff --git a/compiler/closureiters.nim b/compiler/closureiters.nim index 48088a609..43dfc69ae 100644 --- a/compiler/closureiters.nim +++ b/compiler/closureiters.nim @@ -177,7 +177,7 @@ proc newStateAssgn(ctx: var Ctx, stateNo: int = -2): PNode = ctx.newStateAssgn(newIntTypeNode(stateNo, ctx.g.getSysType(TLineInfo(), tyInt))) proc newEnvVar(ctx: var Ctx, name: string, typ: PType): PSym = - result = newSym(skVar, getIdent(ctx.g.cache, name), nextId(ctx.idgen), ctx.fn, ctx.fn.info) + result = newSym(skVar, getIdent(ctx.g.cache, name), nextSymId(ctx.idgen), ctx.fn, ctx.fn.info) result.typ = typ assert(not typ.isNil) @@ -1118,9 +1118,9 @@ proc skipThroughEmptyStates(ctx: var Ctx, n: PNode): PNode= n[i] = ctx.skipThroughEmptyStates(n[i]) proc newArrayType(g: ModuleGraph; n: int, t: PType; idgen: IdGenerator; owner: PSym): PType = - result = newType(tyArray, nextId(idgen), owner) + result = newType(tyArray, nextTypeId(idgen), owner) - let rng = newType(tyRange, nextId(idgen), owner) + let rng = newType(tyRange, nextTypeId(idgen), owner) rng.n = newTree(nkRange, g.newIntLit(owner.info, 0), g.newIntLit(owner.info, n)) rng.rawAddSon(t) @@ -1314,7 +1314,7 @@ proc freshVars(n: PNode; c: var FreshVarsContext): PNode = let idefs = copyNode(it) for v in 0..it.len-3: if it[v].kind == nkSym: - let x = copySym(it[v].sym, nextId(c.idgen)) + let x = copySym(it[v].sym, nextSymId(c.idgen)) c.tab[it[v].sym.id] = x idefs.add newSymNode(x) else: @@ -1393,9 +1393,9 @@ proc transformClosureIterator*(g: ModuleGraph; idgen: IdGenerator; fn: PSym, n: # Lambda lifting was not done yet. Use temporary :state sym, which will # be handled specially by lambda lifting. Local temp vars (if needed) # should follow the same logic. - ctx.stateVarSym = newSym(skVar, getIdent(ctx.g.cache, ":state"), nextId(idgen), fn, fn.info) + ctx.stateVarSym = newSym(skVar, getIdent(ctx.g.cache, ":state"), nextSymId(idgen), fn, fn.info) ctx.stateVarSym.typ = g.createClosureIterStateType(fn, idgen) - ctx.stateLoopLabel = newSym(skLabel, getIdent(ctx.g.cache, ":stateLoop"), nextId(idgen), fn, fn.info) + ctx.stateLoopLabel = newSym(skLabel, getIdent(ctx.g.cache, ":stateLoop"), nextSymId(idgen), fn, fn.info) var pc = PreprocessContext(finallys: @[], config: g.config, idgen: idgen) var n = preprocess(pc, n.toStmtList) #echo "transformed into ", n diff --git a/compiler/commands.nim b/compiler/commands.nim index 4b8755ab4..b52148668 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -31,7 +31,6 @@ import wordrecg, parseutils, nimblecmd, parseopt, sequtils, lineinfos, pathutils, strtabs -from incremental import nimIncremental from ast import eqTypeFlags, tfGcSafe, tfNoSideEffect # but some have deps to imported modules. Yay. @@ -799,10 +798,6 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; helpOnError(conf, pass) of "symbolfiles": discard "ignore for backwards compat" of "incremental": - when not nimIncremental: - localError(conf, info, "the compiler was not built with " & - "incremental compilation features; bootstrap with " & - "-d:nimIncremental to enable") case arg.normalize of "on": conf.symbolFiles = v2Sf of "off": conf.symbolFiles = disabledSf diff --git a/compiler/enumtostr.nim b/compiler/enumtostr.nim index 3274462d7..9bfa7001a 100644 --- a/compiler/enumtostr.nim +++ b/compiler/enumtostr.nim @@ -2,15 +2,15 @@ import ast, idents, lineinfos, modulegraphs, magicsys proc genEnumToStrProc*(t: PType; info: TLineInfo; g: ModuleGraph; idgen: IdGenerator): PSym = - result = newSym(skProc, getIdent(g.cache, "$"), nextId idgen, t.owner, info) + result = newSym(skProc, getIdent(g.cache, "$"), nextSymId idgen, t.owner, info) - let dest = newSym(skParam, getIdent(g.cache, "e"), nextId idgen, result, info) + let dest = newSym(skParam, getIdent(g.cache, "e"), nextSymId idgen, result, info) dest.typ = t - let res = newSym(skResult, getIdent(g.cache, "result"), nextId idgen, result, info) + let res = newSym(skResult, getIdent(g.cache, "result"), nextSymId idgen, result, info) res.typ = getSysType(g, info, tyString) - result.typ = newType(tyProc, nextId idgen, t.owner) + result.typ = newType(tyProc, nextTypeId idgen, t.owner) result.typ.n = newNodeI(nkFormalParams, info) rawAddSon(result.typ, res.typ) result.typ.n.add newNodeI(nkEffectList, info) @@ -63,15 +63,15 @@ proc searchObjCase(t: PType; field: PSym): PNode = doAssert result != nil proc genCaseObjDiscMapping*(t: PType; field: PSym; info: TLineInfo; g: ModuleGraph; idgen: IdGenerator): PSym = - result = newSym(skProc, getIdent(g.cache, "objDiscMapping"), nextId idgen, t.owner, info) + result = newSym(skProc, getIdent(g.cache, "objDiscMapping"), nextSymId idgen, t.owner, info) - let dest = newSym(skParam, getIdent(g.cache, "e"), nextId idgen, result, info) + let dest = newSym(skParam, getIdent(g.cache, "e"), nextSymId idgen, result, info) dest.typ = field.typ - let res = newSym(skResult, getIdent(g.cache, "result"), nextId idgen, result, info) + let res = newSym(skResult, getIdent(g.cache, "result"), nextSymId idgen, result, info) res.typ = getSysType(g, info, tyUInt8) - result.typ = newType(tyProc, nextId idgen, t.owner) + result.typ = newType(tyProc, nextTypeId idgen, t.owner) result.typ.n = newNodeI(nkFormalParams, info) rawAddSon(result.typ, res.typ) result.typ.n.add newNodeI(nkEffectList, info) diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim index 218a597d8..691d33a2c 100644 --- a/compiler/evaltempl.nim +++ b/compiler/evaltempl.nim @@ -49,7 +49,7 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) = internalAssert c.config, sfGenSym in s.flags or s.kind == skType var x = PSym(idTableGet(c.mapping, s)) if x == nil: - x = copySym(s, nextId(c.idgen)) + x = copySym(s, nextSymId(c.idgen)) # sem'check needs to set the owner properly later, see bug #9476 x.owner = nil # c.genSymOwner #if x.kind == skParam and x.owner.kind == skModule: diff --git a/compiler/ic/bitabs.nim b/compiler/ic/bitabs.nim index 91221ea49..1f75b7759 100644 --- a/compiler/ic/bitabs.nim +++ b/compiler/ic/bitabs.nim @@ -1,7 +1,7 @@ ## A BiTable is a table that can be seen as an optimized pair ## of (Table[LitId, Val], Table[Val, LitId]). -import hashes +import hashes, rodfiles type LitId* = distinct uint32 @@ -30,7 +30,9 @@ proc mustRehash(length, counter: int): bool {.inline.} = result = (length * 2 < counter * 3) or (length - counter < 4) const - idStart = 256 # Ids do not start with 0 but with this value. The IR needs it. + idStart = 256 ## + ## Ids do not start with 0 but with this value. The IR needs it. + ## TODO: explain why template idToIdx(x: LitId): int = x.int - idStart @@ -94,6 +96,21 @@ proc `[]`*[T](t: BiTable[T]; LitId: LitId): lent T {.inline.} = assert idx < t.vals.len result = t.vals[idx] +proc hash*[T](t: BiTable[T]): Hash = + ## as the keys are hashes of the values, we simply use them instead + var h: Hash = 0 + for i, n in pairs t.keys: + h = h !& hash((i, n)) + result = !$h + +proc store*[T](f: var RodFile; t: BiTable[T]) = + storeSeq(f, t.vals) + storeSeq(f, t.keys) + +proc load*[T](f: var RodFile; t: var BiTable[T]) = + loadSeq(f, t.vals) + loadSeq(f, t.keys) + when isMainModule: var t: BiTable[string] @@ -113,7 +130,35 @@ when isMainModule: for i in 0 ..< 100_000: assert t.getOrIncl($i & "___" & $i).idToIdx == i + 4 + echo "begin" echo t.vals.len echo t.vals[0] echo t.vals[1004] + + echo "middle" + + var tf: BiTable[float] + + discard tf.getOrIncl(0.4) + discard tf.getOrIncl(16.4) + discard tf.getOrIncl(32.4) + echo getKeyId(tf, 32.4) + + var f2 = open("testblah.bin", fmWrite) + echo store(f2, tf) + f2.close + + var f1 = open("testblah.bin", fmRead) + + var t2: BiTable[float] + + echo f1.load(t2) + echo t2.vals.len + + echo getKeyId(t2, 32.4) + + echo "end" + + + f1.close diff --git a/compiler/ic/design.rst b/compiler/ic/design.rst index 1a33f6a27..60434e4b8 100644 --- a/compiler/ic/design.rst +++ b/compiler/ic/design.rst @@ -29,14 +29,13 @@ mechanism needs to be implemented that we could get wrong. ModuleIds are rod-file specific too. -Configuration setup changes ---------------------------- - -For a MVP these are not detected. Later the configuration will be -stored in every `.rod` file. - Global state ------------ Global persistent state will be kept in a project specific `.rod` file. + +Rod File Format +--------------- + +It's a simple binary file format. `rodfiles.nim` contains some details. diff --git a/compiler/ic/from_packed_ast.nim b/compiler/ic/from_packed_ast.nim deleted file mode 100644 index cb2ecee79..000000000 --- a/compiler/ic/from_packed_ast.nim +++ /dev/null @@ -1,12 +0,0 @@ -# -# -# The Nim Compiler -# (c) Copyright 2020 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -import std / [hashes, tables] -import bitabs -import ".." / [ast, lineinfos, options, pathutils] diff --git a/compiler/ic/packed_ast.nim b/compiler/ic/packed_ast.nim index ef609e8c8..546e495c5 100644 --- a/compiler/ic/packed_ast.nim +++ b/compiler/ic/packed_ast.nim @@ -12,9 +12,9 @@ ## use this representation directly in all the transformations, ## it is superior. -import std / [hashes, tables] +import std / [hashes, tables, strtabs, md5] import bitabs -import ".." / [ast, lineinfos, options, pathutils] +import ".." / [ast, options] const localNamePos* = 0 @@ -39,16 +39,29 @@ const routineBodyPos* = 7 const - nkModuleRef = nkNone # pair of (ModuleId, SymId) + nkModuleRef* = nkNone # pair of (ModuleId, SymId) type SymId* = distinct int32 - TypeId* = distinct int32 ModuleId* = distinct int32 NodePos* = distinct int NodeId* = distinct int32 + PackedItemId* = object + module*: LitId # 0 if it's this module + item*: int32 # same as the in-memory representation + + TypeId* = PackedItemId + +const + nilTypeId* = PackedItemId(module: LitId(0), item: -1.int32) + nilItemId* = PackedItemId(module: LitId(0), item: -1.int32) + +const + emptyNodeId* = NodeId(-1) + +type PackedLineInfo* = object line*: uint16 col*: int16 @@ -64,13 +77,13 @@ type PackedSym* = object kind*: TSymKind name*: LitId - typeId*: TypeId + typ*: TypeId flags*: TSymFlags magic*: TMagic info*: PackedLineInfo - ast*: NodePos - owner*: ItemId - guard*: ItemId + ast*: NodeId + owner*: PackedItemId + guard*: PackedItemId bitsize*: int alignment*: int # for alignment options*: TOptions @@ -84,25 +97,25 @@ type PackedType* = object kind*: TTypeKind - nodekind*: TNodeKind + callConv*: TCallingConvention + #nodekind*: TNodeKind flags*: TTypeFlags - types*: int32 - nodes*: int32 - methods*: int32 - nodeflags*: TNodeFlags - info*: PackedLineInfo - sym*: ItemId - owner*: ItemId - attachedOps*: array[TTypeAttachedOp, ItemId] + types*: seq[TypeId] + n*: NodeId + methods*: seq[(int, PackedItemId)] + #nodeflags*: TNodeFlags + sym*: PackedItemId + owner*: PackedItemId + attachedOps*: array[TTypeAttachedOp, PackedItemId] size*: BiggestInt align*: int16 paddingAtEnd*: int16 lockLevel*: TLockLevel # lock level as required for deadlock checking # not serialized: loc*: TLoc because it is backend-specific typeInst*: TypeId - nonUniqueId*: ItemId + nonUniqueId*: int32 - Node* = object # 20 bytes + PackedNode* = object # 20 bytes kind*: TNodeKind flags*: TNodeFlags operand*: int32 # for kind in {nkSym, nkSymDef}: SymId @@ -115,72 +128,73 @@ type ModulePhase* = enum preLookup, lookedUpTopLevelStmts - Module* = object + GenericKey* = object + module*: int32 name*: string - file*: AbsoluteFile - ast*: PackedTree - phase*: ModulePhase - iface*: Table[string, seq[SymId]] # 'seq' because of overloading + types*: seq[MD5Digest] # is this a joke? - Program* = ref object - modules*: seq[Module] + PackedTree* = object ## usually represents a full Nim module + nodes*: seq[PackedNode] + #sh*: Shared Shared* = ref object # shared between different versions of 'Module'. # (though there is always exactly one valid # version of a module) syms*: seq[PackedSym] - types*: seq[seq[Node]] + types*: seq[PackedType] strings*: BiTable[string] # we could share these between modules. integers*: BiTable[BiggestInt] floats*: BiTable[BiggestFloat] - config*: ConfigRef - #thisModule*: ModuleId - #program*: Program + #config*: ConfigRef - PackedTree* = object ## usually represents a full Nim module - nodes*: seq[Node] - toPosition*: Table[SymId, NodePos] - sh*: Shared +proc hash*(key: GenericKey): Hash = + var h: Hash = 0 + h = h !& hash(key.module) + h = h !& hash(key.name) + h = h !& hash(key.types) + result = !$h proc `==`*(a, b: SymId): bool {.borrow.} proc hash*(a: SymId): Hash {.borrow.} proc `==`*(a, b: NodePos): bool {.borrow.} -proc `==`*(a, b: TypeId): bool {.borrow.} -proc `==`*(a, b: ModuleId): bool {.borrow.} - -proc declareSym*(tree: var PackedTree; kind: TSymKind; - name: LitId; info: PackedLineInfo): SymId = - result = SymId(tree.sh.syms.len) - tree.sh.syms.add PackedSym(kind: kind, name: name, flags: {}, magic: mNone, info: info) +#proc `==`*(a, b: TypeId): bool {.borrow.} +proc `==`*(a, b: NodeId): bool {.borrow.} proc newTreeFrom*(old: PackedTree): PackedTree = result.nodes = @[] - result.sh = old.sh + when false: result.sh = old.sh -proc litIdFromName*(tree: PackedTree; name: string): LitId = - result = tree.sh.strings.getOrIncl(name) +when false: + proc declareSym*(tree: var PackedTree; kind: TSymKind; + name: LitId; info: PackedLineInfo): SymId = + result = SymId(tree.sh.syms.len) + tree.sh.syms.add PackedSym(kind: kind, name: name, flags: {}, magic: mNone, info: info) -proc add*(tree: var PackedTree; kind: TNodeKind; token: string; info: PackedLineInfo) = - tree.nodes.add Node(kind: kind, operand: int32 getOrIncl(tree.sh.strings, token), info: info) + proc litIdFromName*(tree: PackedTree; name: string): LitId = + result = tree.sh.strings.getOrIncl(name) -proc add*(tree: var PackedTree; kind: TNodeKind; info: PackedLineInfo) = - tree.nodes.add Node(kind: kind, operand: 0, info: info) + proc add*(tree: var PackedTree; kind: TNodeKind; token: string; info: PackedLineInfo) = + tree.nodes.add PackedNode(kind: kind, info: info, + operand: int32 getOrIncl(tree.sh.strings, token)) + + proc add*(tree: var PackedTree; kind: TNodeKind; info: PackedLineInfo) = + tree.nodes.add PackedNode(kind: kind, operand: 0, info: info) proc throwAwayLastNode*(tree: var PackedTree) = tree.nodes.setLen(tree.nodes.len-1) proc addIdent*(tree: var PackedTree; s: LitId; info: PackedLineInfo) = - tree.nodes.add Node(kind: nkIdent, operand: int32(s), info: info) + tree.nodes.add PackedNode(kind: nkIdent, operand: int32(s), info: info) -proc addSym*(tree: var PackedTree; s: SymId; info: PackedLineInfo) = - tree.nodes.add Node(kind: nkSym, operand: int32(s), info: info) +proc addSym*(tree: var PackedTree; s: int32; info: PackedLineInfo) = + tree.nodes.add PackedNode(kind: nkSym, operand: s, info: info) proc addModuleId*(tree: var PackedTree; s: ModuleId; info: PackedLineInfo) = - tree.nodes.add Node(kind: nkInt32Lit, operand: int32(s), info: info) + tree.nodes.add PackedNode(kind: nkInt32Lit, operand: int32(s), info: info) proc addSymDef*(tree: var PackedTree; s: SymId; info: PackedLineInfo) = - tree.nodes.add Node(kind: nkSym, operand: int32(s), info: info) + tree.nodes.add PackedNode(kind: nkSym, operand: int32(s), info: info) proc isAtom*(tree: PackedTree; pos: int): bool {.inline.} = tree.nodes[pos].kind <= nkNilLit @@ -194,11 +208,12 @@ proc copyTree*(dest: var PackedTree; tree: PackedTree; n: NodePos) = for i in 0.. nkNilLit: @@ -247,7 +264,8 @@ iterator sons*(dest: var PackedTree; tree: PackedTree; n: NodePos): NodePos = for x in sonsReadonly(tree, n): yield x patch dest, patchPos -iterator isons*(dest: var PackedTree; tree: PackedTree; n: NodePos): (int, NodePos) = +iterator isons*(dest: var PackedTree; tree: PackedTree; + n: NodePos): (int, NodePos) = var i = 0 for ch0 in sons(dest, tree, n): yield (i, ch0) @@ -301,10 +319,19 @@ proc hasAtLeastXsons*(tree: PackedTree; n: NodePos; x: int): bool = if count >= x: return true return false -proc firstSon*(tree: PackedTree; n: NodePos): NodePos {.inline.} = NodePos(n.int+1) -proc kind*(tree: PackedTree; n: NodePos): TNodeKind {.inline.} = tree.nodes[n.int].kind -proc litId*(tree: PackedTree; n: NodePos): LitId {.inline.} = LitId tree.nodes[n.int].operand -proc info*(tree: PackedTree; n: NodePos): PackedLineInfo {.inline.} = tree.nodes[n.int].info +proc firstSon*(tree: PackedTree; n: NodePos): NodePos {.inline.} = + NodePos(n.int+1) +proc kind*(tree: PackedTree; n: NodePos): TNodeKind {.inline.} = + tree.nodes[n.int].kind +proc litId*(tree: PackedTree; n: NodePos): LitId {.inline.} = + LitId tree.nodes[n.int].operand +proc info*(tree: PackedTree; n: NodePos): PackedLineInfo {.inline.} = + tree.nodes[n.int].info + +template typ*(n: NodePos): PackedItemId = + tree.nodes[n.int].typeId +template flags*(n: NodePos): TNodeFlags = + tree.nodes[n.int].flags proc span(tree: PackedTree; pos: int): int {.inline.} = if isAtom(tree, pos): 1 else: tree.nodes[pos].operand @@ -330,7 +357,9 @@ proc ithSon*(tree: PackedTree; n: NodePos; i: int): NodePos = inc count assert false, "node has no i-th child" -proc `@`*(tree: PackedTree; lit: LitId): lent string {.inline.} = tree.sh.strings[lit] +when false: + proc `@`*(tree: PackedTree; lit: LitId): lent string {.inline.} = + tree.sh.strings[lit] template kind*(n: NodePos): TNodeKind = tree.nodes[n.int].kind template info*(n: NodePos): PackedLineInfo = tree.nodes[n.int].info @@ -340,29 +369,30 @@ template symId*(n: NodePos): SymId = SymId tree.nodes[n.int].operand proc firstSon*(n: NodePos): NodePos {.inline.} = NodePos(n.int+1) -proc strLit*(tree: PackedTree; n: NodePos): lent string = - assert n.kind == nkStrLit - result = tree.sh.strings[LitId tree.nodes[n.int].operand] +when false: + proc strLit*(tree: PackedTree; n: NodePos): lent string = + assert n.kind == nkStrLit + result = tree.sh.strings[LitId tree.nodes[n.int].operand] -proc strVal*(tree: PackedTree; n: NodePos): string = - assert n.kind == nkStrLit - result = tree.sh.strings[LitId tree.nodes[n.int].operand] - #result = cookedStrLit(raw) + proc strVal*(tree: PackedTree; n: NodePos): string = + assert n.kind == nkStrLit + result = tree.sh.strings[LitId tree.nodes[n.int].operand] + #result = cookedStrLit(raw) -proc filenameVal*(tree: PackedTree; n: NodePos): string = - case n.kind - of nkStrLit: - result = strVal(tree, n) - of nkIdent: - result = tree.sh.strings[n.litId] - of nkSym: - result = tree.sh.strings[tree.sh.syms[int n.symId].name] - else: - result = "" + proc filenameVal*(tree: PackedTree; n: NodePos): string = + case n.kind + of nkStrLit: + result = strVal(tree, n) + of nkIdent: + result = tree.sh.strings[n.litId] + of nkSym: + result = tree.sh.strings[tree.sh.syms[int n.symId].name] + else: + result = "" -proc identAsStr*(tree: PackedTree; n: NodePos): lent string = - assert n.kind == nkIdent - result = tree.sh.strings[LitId tree.nodes[n.int].operand] + proc identAsStr*(tree: PackedTree; n: NodePos): lent string = + assert n.kind == nkIdent + result = tree.sh.strings[LitId tree.nodes[n.int].operand] const externIntLit* = {nkCharLit, @@ -380,7 +410,8 @@ const externUIntLit* = {nkUIntLit, nkUInt8Lit, nkUInt16Lit, nkUInt32Lit, nkUInt64Lit} directIntLit* = nkInt32Lit -proc toString*(tree: PackedTree; n: NodePos; nesting: int; result: var string) = +proc toString*(tree: PackedTree; n: NodePos; sh: Shared; nesting: int; + result: var string) = let pos = n.int if result.len > 0 and result[^1] notin {' ', '\n'}: result.add ' ' @@ -390,46 +421,47 @@ proc toString*(tree: PackedTree; n: NodePos; nesting: int; result: var string) = of nkNone, nkEmpty, nkNilLit, nkType: discard of nkIdent, nkStrLit..nkTripleStrLit: result.add " " - result.add tree.sh.strings[LitId tree.nodes[pos].operand] + result.add sh.strings[LitId tree.nodes[pos].operand] of nkSym: result.add " " - result.add tree.sh.strings[tree.sh.syms[tree.nodes[pos].operand].name] + result.add sh.strings[sh.syms[tree.nodes[pos].operand].name] of directIntLit: result.add " " result.addInt tree.nodes[pos].operand of externSIntLit: result.add " " - result.addInt tree.sh.integers[LitId tree.nodes[pos].operand] + result.addInt sh.integers[LitId tree.nodes[pos].operand] of externUIntLit: result.add " " - result.add $cast[uint64](tree.sh.integers[LitId tree.nodes[pos].operand]) + result.add $cast[uint64](sh.integers[LitId tree.nodes[pos].operand]) else: result.add "(\n" for i in 1..(nesting+1)*2: result.add ' ' for child in sonsReadonly(tree, n): - toString(tree, child, nesting + 1, result) + toString(tree, child, sh, nesting + 1, result) result.add "\n" for i in 1..nesting*2: result.add ' ' result.add ")" #for i in 1..nesting*2: result.add ' ' -proc toString*(tree: PackedTree; n: NodePos): string = +proc toString*(tree: PackedTree; n: NodePos; sh: Shared): string = result = "" - toString(tree, n, 0, result) + toString(tree, n, sh, 0, result) -proc debug*(tree: PackedTree) = - stdout.write toString(tree, NodePos 0) +proc debug*(tree: PackedTree; sh: Shared) = + stdout.write toString(tree, NodePos 0, sh) -proc identIdImpl(tree: PackedTree; n: NodePos): LitId = - if n.kind == nkIdent: - result = n.litId - elif n.kind == nkSym: - result = tree.sh.syms[int n.symId].name - else: - result = LitId(0) +when false: + proc identIdImpl(tree: PackedTree; n: NodePos): LitId = + if n.kind == nkIdent: + result = n.litId + elif n.kind == nkSym: + result = tree.sh.syms[int n.symId].name + else: + result = LitId(0) -template identId*(n: NodePos): LitId = identIdImpl(tree, n) + template identId*(n: NodePos): LitId = identIdImpl(tree, n) template copyInto*(dest, n, body) = let patchPos = prepare(dest, tree, n) @@ -441,17 +473,20 @@ template copyIntoKind*(dest, kind, info, body) = body patch dest, patchPos -proc hasPragma*(tree: PackedTree; n: NodePos; pragma: string): bool = - let litId = tree.sh.strings.getKeyId(pragma) - if litId == LitId(0): - return false - assert n.kind == nkPragma - for ch0 in sonsReadonly(tree, n): - if ch0.kind == nkExprColonExpr: - if ch0.firstSon.identId == litId: +when false: + proc hasPragma*(tree: PackedTree; n: NodePos; pragma: string): bool = + let litId = tree.sh.strings.getKeyId(pragma) + if litId == LitId(0): + return false + assert n.kind == nkPragma + for ch0 in sonsReadonly(tree, n): + if ch0.kind == nkExprColonExpr: + if ch0.firstSon.identId == litId: + return true + elif ch0.identId == litId: return true - elif ch0.identId == litId: - return true + +proc getNodeId*(tree: PackedTree): NodeId {.inline.} = NodeId tree.nodes.len when false: proc produceError*(dest: var PackedTree; tree: PackedTree; n: NodePos; msg: string) = @@ -459,3 +494,68 @@ when false: dest.add nkStrLit, msg, n.info copyTree(dest, tree, n) patch dest, patchPos + + proc hash*(table: StringTableRef): Hash = + ## XXX: really should be introduced into strtabs... + var h: Hash = 0 + for pair in pairs table: + h = h !& hash(pair) + result = !$h + + proc hash*(config: ConfigRef): Hash = + ## XXX: vet and/or extend this + var h: Hash = 0 + h = h !& hash(config.selectedGC) + h = h !& hash(config.features) + h = h !& hash(config.legacyFeatures) + h = h !& hash(config.configVars) + h = h !& hash(config.symbols) + result = !$h + + # XXX: lazy hashes for now + type + LazyHashes = PackedSym or PackedType or PackedLib or + PackedLineInfo or PackedTree or PackedNode + + proc hash*(sh: Shared): Hash + proc hash*(s: LazyHashes): Hash + proc hash*(s: seq[LazyHashes]): Hash + + proc hash*(s: LazyHashes): Hash = + var h: Hash = 0 + for k, v in fieldPairs(s): + h = h !& hash((k, v)) + result = !$h + + proc hash*(s: seq[LazyHashes]): Hash = + ## critically, we need to hash the indices alongside their values + var h: Hash = 0 + for i, n in pairs s: + h = h !& hash((i, n)) + result = !$h + + proc hash*(sh: Shared): Hash = + ## might want to edit this... + # XXX: these have too many references + when false: + var h: Hash = 0 + h = h !& hash(sh.syms) + h = h !& hash(sh.types) + h = h !& hash(sh.strings) + h = h !& hash(sh.integers) + h = h !& hash(sh.floats) + h = h !& hash(sh.config) + result = !$h + + proc hash*(m: Module): Hash = + var h: Hash = 0 + h = h !& hash(m.name) + h = h !& hash(m.ast) + result = !$h + + template safeItemId*(x: typed; f: untyped): ItemId = + ## yield a valid ItemId value for the field of a nillable type + if x.isNil: + nilItemId + else: + x.`f` diff --git a/compiler/ic/rodfiles.nim b/compiler/ic/rodfiles.nim new file mode 100644 index 000000000..99ce183f2 --- /dev/null +++ b/compiler/ic/rodfiles.nim @@ -0,0 +1,143 @@ +# +# +# The Nim Compiler +# (c) Copyright 2020 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +from typetraits import supportsCopyMem + +type + RodSection* = enum + versionSection + configSection + stringsSection + checkSumsSection + depsSection + integersSection + floatsSection + topLevelSection + bodiesSection + symsSection + typesSection + + RodFileError* = enum + ok, tooBig, ioFailure, wrongHeader, wrongSection, configMismatch, + includeFileChanged + + RodFile* = object + f*: File + currentSection*: RodSection # for error checking + err*: RodFileError # little experiment to see if this works + # better than exceptions. + +const + RodVersion = 1 + cookie = [byte(0), byte('R'), byte('O'), byte('D'), + byte(0), byte(0), byte(0), byte(RodVersion)] + +proc storePrim*(f: var RodFile; s: string) = + if f.err != ok: return + if s.len >= high(int32): + f.err = tooBig + return + var lenPrefix = int32(s.len) + if writeBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix): + f.err = ioFailure + else: + if s.len != 0: + if writeBuffer(f.f, unsafeAddr(s[0]), s.len) != s.len: + f.err = ioFailure + +proc storePrim*[T](f: var RodFile; x: T) = + if f.err != ok: return + when supportsCopyMem(T): + if writeBuffer(f.f, unsafeAddr(x), sizeof(x)) != sizeof(x): + f.err = ioFailure + elif T is tuple: + for y in fields(x): + storePrim(f, y) + else: + {.error: "unsupported type for 'storePrim'".} + +proc storeSeq*[T](f: var RodFile; s: seq[T]) = + if f.err != ok: return + if s.len >= high(int32): + f.err = tooBig + return + var lenPrefix = int32(s.len) + if writeBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix): + f.err = ioFailure + else: + for i in 0.. 0: + if readBuffer(f.f, unsafeAddr(s[0]), s.len) != s.len: + f.err = ioFailure + +proc loadPrim*[T](f: var RodFile; x: var T) = + if f.err != ok: return + when supportsCopyMem(T): + if readBuffer(f.f, unsafeAddr(x), sizeof(x)) != sizeof(x): + f.err = ioFailure + elif T is tuple: + for y in fields(x): + loadPrim(f, y) + else: + {.error: "unsupported type for 'loadPrim'".} + +proc loadSeq*[T](f: var RodFile; s: var seq[T]) = + if f.err != ok: return + var lenPrefix = int32(0) + if readBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix): + f.err = ioFailure + else: + s = newSeq[T](lenPrefix) + for i in 0.. TypeId + symMarker*: IntSet #Table[ItemId, SymId] # ItemId.item -> SymId + config*: ConfigRef + +template primConfigFields(fn: untyped) {.dirty.} = + fn backend + fn selectedGC + fn cCompiler + fn options + fn globalOptions + +proc definedSymbolsAsString(config: ConfigRef): string = + result = newStringOfCap(200) + result.add "config" + for d in definedSymbolNames(config.symbols): + result.add ' ' + result.add d + +proc rememberConfig(c: var PackedEncoder; config: ConfigRef) = + c.m.definedSymbols = definedSymbolsAsString(config) + + template rem(x) = + c.m.cfg.x = config.x + primConfigFields rem + +proc configIdentical(m: PackedModule; config: ConfigRef): bool = + result = m.definedSymbols == definedSymbolsAsString(config) + template eq(x) = + result = result and m.cfg.x == config.x + primConfigFields eq + +proc hashFileCached(conf: ConfigRef; fileIdx: FileIndex): string = + result = msgs.getHash(conf, fileIdx) + if result.len == 0: + let fullpath = msgs.toFullPath(conf, fileIdx) + result = $secureHashFile(fullpath) + msgs.setHash(conf, fileIdx, result) + +proc toLitId(x: FileIndex; c: var PackedEncoder): LitId = + ## store a file index as a literal if x == c.lastFile: result = c.lastLit else: result = c.filenames.getOrDefault(x) if result == LitId(0): - let p = msgs.toFullPath(ir.sh.config, x) - result = getOrIncl(ir.sh.strings, p) + let p = msgs.toFullPath(c.config, x) + result = getOrIncl(c.m.sh.strings, p) c.filenames[x] = result c.lastFile = x c.lastLit = result + assert result != LitId(0) -proc toPackedInfo(x: TLineInfo; ir: var PackedTree; c: var Context): PackedLineInfo = - PackedLineInfo(line: x.line, col: x.col, file: toLitId(x.fileIndex, ir, c)) +proc toFileIndex(x: LitId; m: PackedModule; config: ConfigRef): FileIndex = + result = msgs.fileInfoIdx(config, AbsoluteFile m.sh.strings[x]) -proc toPackedType(t: PType; ir: var PackedTree; c: var Context): TypeId = - result = TypeId(0) +proc includesIdentical(m: var PackedModule; config: ConfigRef): bool = + for it in mitems(m.includes): + if hashFileCached(config, toFileIndex(it[0], m, config)) != it[1]: + return false + result = true -proc toPackedSym(s: PSym; ir: var PackedTree; c: var Context): SymId = - result = SymId(0) +proc initEncoder*(c: var PackedEncoder; m: PSym; config: ConfigRef) = + ## setup a context for serializing to packed ast + c.m.sh = Shared() + c.thisModule = m.itemId.module + c.config = config + c.m.bodies = newTreeFrom(c.m.topLevel) + c.m.hidden = newTreeFrom(c.m.topLevel) -proc toPackedSymNode(n: PNode; ir: var PackedTree; c: var Context) = + let thisNimFile = FileIndex c.thisModule + var h = msgs.getHash(config, thisNimFile) + if h.len == 0: + let fullpath = msgs.toFullPath(config, thisNimFile) + if isAbsolute(fullpath): + # For NimScript compiler API support the main Nim file might be from a stream. + h = $secureHashFile(fullpath) + msgs.setHash(config, thisNimFile, h) + c.m.includes.add((toLitId(thisNimFile, c), h)) # the module itself + +proc addIncludeFileDep*(c: var PackedEncoder; f: FileIndex) = + c.m.includes.add((toLitId(f, c), hashFileCached(c.config, f))) + +proc addImportFileDep*(c: var PackedEncoder; f: FileIndex) = + c.m.imports.add toLitId(f, c) + +proc toPackedNode*(n: PNode; ir: var PackedTree; c: var PackedEncoder) +proc toPackedSym*(s: PSym; c: var PackedEncoder): PackedItemId +proc toPackedType(t: PType; c: var PackedEncoder): PackedItemId + +proc flush(c: var PackedEncoder) = + ## serialize any pending types or symbols from the context + while true: + if c.pendingTypes.len > 0: + discard toPackedType(c.pendingTypes.pop, c) + elif c.pendingSyms.len > 0: + discard toPackedSym(c.pendingSyms.pop, c) + else: + break + +proc toLitId(x: string; c: var PackedEncoder): LitId = + ## store a string as a literal + result = getOrIncl(c.m.sh.strings, x) + +proc toLitId(x: BiggestInt; c: var PackedEncoder): LitId = + ## store an integer as a literal + result = getOrIncl(c.m.sh.integers, x) + +proc toPackedInfo(x: TLineInfo; c: var PackedEncoder): PackedLineInfo = + PackedLineInfo(line: x.line, col: x.col, file: toLitId(x.fileIndex, c)) + +proc safeItemId(s: PSym; c: var PackedEncoder): PackedItemId {.inline.} = + ## given a symbol, produce an ItemId with the correct properties + ## for local or remote symbols, packing the symbol as necessary + if s == nil: + result = nilItemId + elif s.itemId.module == c.thisModule: + result = PackedItemId(module: LitId(0), item: s.itemId.item) + else: + result = PackedItemId(module: toLitId(s.itemId.module.FileIndex, c), + item: s.itemId.item) + +proc addModuleRef(n: PNode; ir: var PackedTree; c: var PackedEncoder) = + ## add a remote symbol reference to the tree + let info = n.info.toPackedInfo(c) + ir.nodes.add PackedNode(kind: nkModuleRef, operand: 2.int32, # 2 kids... + typeId: toPackedType(n.typ, c), info: info) + ir.nodes.add PackedNode(kind: nkInt32Lit, info: info, + operand: toLitId(n.sym.itemId.module.FileIndex, c).int32) + ir.nodes.add PackedNode(kind: nkInt32Lit, info: info, + operand: n.sym.itemId.item) + +proc addMissing(c: var PackedEncoder; p: PSym) = + ## consider queuing a symbol for later addition to the packed tree + if p != nil and p.itemId.module == c.thisModule: + if p.itemId.item notin c.symMarker: + c.pendingSyms.add p + +proc addMissing(c: var PackedEncoder; p: PType) = + ## consider queuing a type for later addition to the packed tree + if p != nil and p.uniqueId.module == c.thisModule: + if p.uniqueId.item notin c.typeMarker: + c.pendingTypes.add p + +template storeNode(dest, src, field) = + var nodeId: NodeId + if src.field != nil: + nodeId = getNodeId(c.m.bodies) + toPackedNode(src.field, c.m.bodies, c) + else: + nodeId = emptyNodeId + dest.field = nodeId + +proc toPackedType(t: PType; c: var PackedEncoder): PackedItemId = + ## serialize a ptype + if t.isNil: return nilTypeId + + if t.uniqueId.module != c.thisModule: + # XXX Assert here that it already was serialized in the foreign module! + # it is a foreign type: + return PackedItemId(module: toLitId(t.uniqueId.module.FileIndex, c), item: t.uniqueId.item) + + if not c.typeMarker.containsOrIncl(t.uniqueId.item): + if t.uniqueId.item >= c.m.sh.types.len: + setLen c.m.sh.types, t.uniqueId.item+1 + + var p = PackedType(kind: t.kind, flags: t.flags, callConv: t.callConv, + size: t.size, align: t.align, nonUniqueId: t.itemId.item, + paddingAtEnd: t.paddingAtEnd, lockLevel: t.lockLevel) + storeNode(p, t, n) + + for op, s in pairs t.attachedOps: + c.addMissing s + p.attachedOps[op] = s.safeItemId(c) + + p.typeInst = t.typeInst.toPackedType(c) + for kid in items t.sons: + p.types.add kid.toPackedType(c) + for i, s in items t.methods: + c.addMissing s + p.methods.add (i, s.safeItemId(c)) + c.addMissing t.sym + p.sym = t.sym.safeItemId(c) + c.addMissing t.owner + p.owner = t.owner.safeItemId(c) + + # fill the reserved slot, nothing else: + c.m.sh.types[t.uniqueId.item] = p + + result = PackedItemId(module: LitId(0), item: t.uniqueId.item) + +proc toPackedLib(l: PLib; c: var PackedEncoder): PackedLib = + ## the plib hangs off the psym via the .annex field + if l.isNil: return + result.kind = l.kind + result.generated = l.generated + result.isOverriden = l.isOverriden + result.name = toLitId($l.name, c) + storeNode(result, l, path) + +proc toPackedSym*(s: PSym; c: var PackedEncoder): PackedItemId = + ## serialize a psym + if s.isNil: return nilItemId + + if s.itemId.module != c.thisModule: + # XXX Assert here that it already was serialized in the foreign module! + # it is a foreign symbol: + return PackedItemId(module: toLitId(s.itemId.module.FileIndex, c), item: s.itemId.item) + + if not c.symMarker.containsOrIncl(s.itemId.item): + if s.itemId.item >= c.m.sh.syms.len: + setLen c.m.sh.syms, s.itemId.item+1 + + var p = PackedSym(kind: s.kind, flags: s.flags, info: s.info.toPackedInfo(c), magic: s.magic, + position: s.position, offset: s.offset, options: s.options, + name: s.name.s.toLitId(c)) + + storeNode(p, s, ast) + storeNode(p, s, constraint) + + if s.kind in {skLet, skVar, skField, skForVar}: + c.addMissing s.guard + p.guard = s.guard.safeItemId(c) + p.bitsize = s.bitsize + p.alignment = s.alignment + + p.externalName = toLitId(if s.loc.r.isNil: "" else: $s.loc.r, c) + c.addMissing s.typ + p.typ = s.typ.toPackedType(c) + c.addMissing s.owner + p.owner = s.owner.safeItemId(c) + p.annex = toPackedLib(s.annex, c) + when hasFFI: + p.cname = toLitId(s.cname, c) + + # fill the reserved slot, nothing else: + c.m.sh.syms[s.itemId.item] = p + + result = PackedItemId(module: LitId(0), item: s.itemId.item) + +proc toSymNode(n: PNode; ir: var PackedTree; c: var PackedEncoder) = + ## store a local or remote psym reference in the tree assert n.kind == nkSym - let t = toPackedType(n.typ, ir, c) - - if n.sym.itemId.module == c.thisModule: + template s: PSym = n.sym + let id = s.toPackedSym(c).item + if s.itemId.module == c.thisModule: # it is a symbol that belongs to the module we're currently # packing: - let sid = toPackedSym(n.sym, ir, c) - ir.nodes.add Node(kind: n.kind, flags: n.flags, operand: int32(sid), - typeId: t, info: toPackedInfo(n.info, ir, c)) + ir.addSym(id, toPackedInfo(n.info, c)) else: # store it as an external module reference: - # nkModuleRef - discard - - -proc toPackedNode*(n: PNode; ir: var PackedTree; c: var Context) = - template toP(x: TLineInfo): PackedLineInfo = toPackedInfo(x, ir, c) + addModuleRef(n, ir, c) +proc toPackedNode*(n: PNode; ir: var PackedTree; c: var PackedEncoder) = + ## serialize a node into the tree + if n.isNil: return + let info = toPackedInfo(n.info, c) case n.kind - of nkNone, nkEmpty, nkNilLit: - ir.nodes.add Node(kind: n.kind, flags: n.flags, operand: 0, - typeId: toPackedType(n.typ, ir, c), info: toP n.info) + of nkNone, nkEmpty, nkNilLit, nkType: + ir.nodes.add PackedNode(kind: n.kind, flags: n.flags, operand: 0, + typeId: toPackedType(n.typ, c), info: info) of nkIdent: - ir.nodes.add Node(kind: n.kind, flags: n.flags, operand: int32 getOrIncl(ir.sh.strings, n.ident.s), - typeId: toPackedType(n.typ, ir, c), info: toP n.info) + ir.nodes.add PackedNode(kind: n.kind, flags: n.flags, + operand: int32 getOrIncl(c.m.sh.strings, n.ident.s), + typeId: toPackedType(n.typ, c), info: info) of nkSym: - toPackedSymNode(n, ir, c) + toSymNode(n, ir, c) of directIntLit: - ir.nodes.add Node(kind: n.kind, flags: n.flags, operand: int32(n.intVal), - typeId: toPackedType(n.typ, ir, c), info: toP n.info) + ir.nodes.add PackedNode(kind: n.kind, flags: n.flags, + operand: int32(n.intVal), + typeId: toPackedType(n.typ, c), info: info) of externIntLit: - ir.nodes.add Node(kind: n.kind, flags: n.flags, operand: int32 getOrIncl(ir.sh.integers, n.intVal), - typeId: toPackedType(n.typ, ir, c), info: toP n.info) + ir.nodes.add PackedNode(kind: n.kind, flags: n.flags, + operand: int32 getOrIncl(c.m.sh.integers, n.intVal), + typeId: toPackedType(n.typ, c), info: info) of nkStrLit..nkTripleStrLit: - ir.nodes.add Node(kind: n.kind, flags: n.flags, operand: int32 getOrIncl(ir.sh.strings, n.strVal), - typeId: toPackedType(n.typ, ir, c), info: toP n.info) + ir.nodes.add PackedNode(kind: n.kind, flags: n.flags, + operand: int32 getOrIncl(c.m.sh.strings, n.strVal), + typeId: toPackedType(n.typ, c), info: info) of nkFloatLit..nkFloat128Lit: - ir.nodes.add Node(kind: n.kind, flags: n.flags, operand: int32 getOrIncl(ir.sh.floats, n.floatVal), - typeId: toPackedType(n.typ, ir, c), info: toP n.info) + ir.nodes.add PackedNode(kind: n.kind, flags: n.flags, + operand: int32 getOrIncl(c.m.sh.floats, n.floatVal), + typeId: toPackedType(n.typ, c), info: info) else: - let patchPos = ir.prepare(n.kind, n.flags, toPackedType(n.typ, ir, c), toP n.info) + let patchPos = ir.prepare(n.kind, n.flags, + toPackedType(n.typ, c), info) for i in 0..= g.len: + g.setLen(m+1) + + case g[m].status + of undefined: + g[m].status = loading + let fullpath = msgs.toFullPath(conf, fileIdx) + let rod = toRodFile(conf, AbsoluteFile fullpath) + let err = loadRodFile(rod, g[m].fromDisk, conf) + if err == ok: + result = false + # check its dependencies: + for dep in g[m].fromDisk.imports: + let fid = toFileIndex(dep, g[m].fromDisk, conf) + # Warning: we need to traverse the full graph, so + # do **not use break here**! + if needsRecompile(g, conf, fid): + result = true + + g[m].status = if result: outdated else: loaded + else: + loadError(err, rod) + g[m].status = outdated + result = true + of loading, loaded: + result = false + of outdated: + result = true + +# ------------------------------------------------------------------------- + +proc storeError(err: RodFileError; filename: AbsoluteFile) = + echo "Error: ", $err, "; couldn't write to ", filename.string + removeFile(filename.string) + +proc saveRodFile*(filename: AbsoluteFile; encoder: var PackedEncoder) = + rememberConfig(encoder, encoder.config) + + var f = rodfiles.create(filename.string) + f.storeHeader() + f.storeSection configSection + f.storePrim encoder.m.definedSymbols + f.storePrim encoder.m.cfg + + f.storeSection stringsSection + f.store encoder.m.sh.strings + + f.storeSection checkSumsSection + f.storeSeq encoder.m.includes + + f.storeSection depsSection + f.storeSeq encoder.m.imports + + f.storeSection integersSection + f.store encoder.m.sh.integers + + f.storeSection floatsSection + f.store encoder.m.sh.floats + + f.storeSection topLevelSection + f.storeSeq encoder.m.topLevel.nodes + + f.storeSection bodiesSection + f.storeSeq encoder.m.bodies.nodes + + f.storeSection symsSection + f.storeSeq encoder.m.sh.syms + + f.storeSection typesSection + f.storeSeq encoder.m.sh.types + close(f) + if f.err != ok: + loadError(f.err, filename) + + when true: + # basic loader testing: + var m2: PackedModule + discard loadRodFile(filename, m2, encoder.config) + +# ---------------------------------------------------------------------------- + +type + PackedDecoder* = object + thisModule*: int32 + lastLit*: LitId + lastFile*: FileIndex # remember the last lookup entry. + config*: ConfigRef + ident: IdentCache + +proc loadType(c: var PackedDecoder; g: var PackedModuleGraph; t: PackedItemId): PType +proc loadSym(c: var PackedDecoder; g: var PackedModuleGraph; s: PackedItemId): PSym + +proc toFileIndexCached(c: var PackedDecoder; g: var PackedModuleGraph; f: LitId): FileIndex = + if c.lastLit == f: + result = c.lastFile + else: + result = toFileIndex(f, g[c.thisModule].fromDisk, c.config) + c.lastLit = f + c.lastFile = result + +proc translateLineInfo(c: var PackedDecoder; g: var PackedModuleGraph; + x: PackedLineInfo): TLineInfo = + assert g[c.thisModule].status == loaded + result = TLineInfo(line: x.line, col: x.col, + fileIndex: toFileIndexCached(c, g, x.file)) + +proc loadNodes(c: var PackedDecoder; g: var PackedModuleGraph; + tree: PackedTree; n: NodePos): PNode = + let k = n.kind + result = newNodeIT(k, translateLineInfo(c, g, n.info), + loadType(c, g, n.typ)) + result.flags = n.flags + + case k + of nkEmpty, nkNilLit, nkType: + discard + of nkIdent: + result.ident = getIdent(c.ident, g[c.thisModule].fromDisk.sh.strings[n.litId]) + of nkSym: + result.sym = loadSym(c, g, PackedItemId(module: LitId(0), item: tree.nodes[n.int].operand)) + of directIntLit: + result.intVal = tree.nodes[n.int].operand + of externIntLit: + result.intVal = g[c.thisModule].fromDisk.sh.integers[n.litId] + of nkStrLit..nkTripleStrLit: + result.strVal = g[c.thisModule].fromDisk.sh.strings[n.litId] + of nkFloatLit..nkFloat128Lit: + result.floatVal = g[c.thisModule].fromDisk.sh.floats[n.litId] + of nkModuleRef: + let (n1, n2) = sons2(tree, n) + assert n1.kind == nkInt32Lit + assert n2.kind == nkInt32Lit + transitionNoneToSym(result) + result.sym = loadSym(c, g, PackedItemId(module: n1.litId, item: tree.nodes[n2.int].operand)) + else: + for n0 in sonsReadonly(tree, n): + result.add loadNodes(c, g, tree, n0) + +proc moduleIndex*(c: var PackedDecoder; g: var PackedModuleGraph; + s: PackedItemId): int32 {.inline.} = + result = if s.module == LitId(0): c.thisModule + else: toFileIndexCached(c, g, s.module).int32 + +proc symHeaderFromPacked(c: var PackedDecoder; g: var PackedModuleGraph; + s: PackedSym; si, item: int32): PSym = + result = PSym(itemId: ItemId(module: si, item: item), + kind: s.kind, magic: s.magic, flags: s.flags, + info: translateLineInfo(c, g, s.info), + options: s.options, + position: s.position, + name: getIdent(c.ident, g[si].fromDisk.sh.strings[s.name]) + ) + +template loadAstBody(p, field) = + if p.field != emptyNodeId: + result.field = loadNodes(c, g, g[si].fromDisk.bodies, NodePos p.field) + +proc loadLib(c: var PackedDecoder; g: var PackedModuleGraph; + si, item: int32; l: PackedLib): PLib = + # XXX: hack; assume a zero LitId means the PackedLib is all zero (empty) + if l.name.int == 0: + result = nil + else: + result = PLib(generated: l.generated, isOverriden: l.isOverriden, + kind: l.kind, name: rope g[si].fromDisk.sh.strings[l.name]) + loadAstBody(l, path) + +proc symBodyFromPacked(c: var PackedDecoder; g: var PackedModuleGraph; + s: PackedSym; si, item: int32; result: PSym) = + result.typ = loadType(c, g, s.typ) + loadAstBody(s, constraint) + loadAstBody(s, ast) + result.annex = loadLib(c, g, si, item, s.annex) + when hasFFI: + result.cname = g[si].fromDisk.sh.strings[s.cname] + + if s.kind in {skLet, skVar, skField, skForVar}: + result.guard = loadSym(c, g, s.guard) + result.bitsize = s.bitsize + result.alignment = s.alignment + result.owner = loadSym(c, g, s.owner) + let externalName = g[si].fromDisk.sh.strings[s.externalName] + if externalName != "": + result.loc.r = rope externalName + +proc loadSym(c: var PackedDecoder; g: var PackedModuleGraph; s: PackedItemId): PSym = + if s == nilTypeId: + result = nil + else: + let si = moduleIndex(c, g, s) + assert g[si].status == loaded + if not g[si].symsInit: + g[si].symsInit = true + setLen g[si].syms, g[si].fromDisk.sh.syms.len + + if g[si].syms[s.item] == nil: + let packed = addr(g[si].fromDisk.sh.syms[s.item]) + result = symHeaderFromPacked(c, g, packed[], si, s.item) + # store it here early on, so that recursions work properly: + g[si].syms[s.item] = result + symBodyFromPacked(c, g, packed[], si, s.item, result) + else: + result = g[si].syms[s.item] + +proc typeHeaderFromPacked(c: var PackedDecoder; g: var PackedModuleGraph; + t: PackedType; si, item: int32): PType = + result = PType(itemId: ItemId(module: si, item: t.nonUniqueId), kind: t.kind, + flags: t.flags, size: t.size, align: t.align, + paddingAtEnd: t.paddingAtEnd, lockLevel: t.lockLevel, + uniqueId: ItemId(module: si, item: item)) + +proc typeBodyFromPacked(c: var PackedDecoder; g: var PackedModuleGraph; + t: PackedType; si, item: int32; result: PType) = + result.sym = loadSym(c, g, t.sym) + result.owner = loadSym(c, g, t.owner) + for op, item in pairs t.attachedOps: + result.attachedOps[op] = loadSym(c, g, item) + result.typeInst = loadType(c, g, t.typeInst) + for son in items t.types: + result.sons.add loadType(c, g, son) + loadAstBody(t, n) + for gen, id in items t.methods: + result.methods.add((gen, loadSym(c, g, id))) + +proc loadType(c: var PackedDecoder; g: var PackedModuleGraph; t: PackedItemId): PType = + if t == nilTypeId: + result = nil + else: + let si = moduleIndex(c, g, t) + assert g[si].status == loaded + if not g[si].typesInit: + g[si].typesInit = true + setLen g[si].types, g[si].fromDisk.sh.types.len + + if g[si].types[t.item] == nil: + let packed = addr(g[si].fromDisk.sh.types[t.item]) + result = typeHeaderFromPacked(c, g, packed[], si, t.item) + # store it here early on, so that recursions work properly: + g[si].types[t.item] = result + typeBodyFromPacked(c, g, packed[], si, t.item, result) + else: + result = g[si].types[t.item] + + +when false: + proc initGenericKey*(s: PSym; types: seq[PType]): GenericKey = + result.module = s.owner.itemId.module + result.name = s.name.s + result.types = mapIt types: hashType(it, {CoType, CoDistinct}).MD5Digest + + proc addGeneric*(m: var Module; c: var PackedEncoder; key: GenericKey; s: PSym) = + ## add a generic to the module + if key notin m.generics: + m.generics[key] = toPackedSym(s, m.ast, c) + toPackedNode(s.ast, m.ast, c) diff --git a/compiler/importer.nim b/compiler/importer.nim index a055e16b7..645c03b2b 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -220,12 +220,13 @@ proc importModuleAs(c: PContext; n: PNode, realModule: PSym): PSym = localError(c.config, n.info, "module alias must be an identifier") elif n[1].ident.id != realModule.name.id: # some misguided guy will write 'import abc.foo as foo' ... - result = createModuleAlias(realModule, nextId c.idgen, n[1].ident, realModule.info, + result = createModuleAlias(realModule, nextSymId c.idgen, n[1].ident, realModule.info, c.config.options) proc myImportModule(c: PContext, n: PNode; importStmtResult: PNode): PSym = let f = checkModuleName(c.config, n) if f != InvalidFileIdx: + addImportFileDep(c, f) let L = c.graph.importStack.len let recursion = c.graph.importStack.find(f) c.graph.importStack.add f diff --git a/compiler/incremental.nim b/compiler/incremental.nim deleted file mode 100644 index 8b3a9bf55..000000000 --- a/compiler/incremental.nim +++ /dev/null @@ -1,198 +0,0 @@ -# -# -# The Nim Compiler -# (c) Copyright 2018 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## Basic type definitions the module graph needs in order to support -## incremental compilations. - -const nimIncremental* = defined(nimIncremental) - -import options, lineinfos - -when nimIncremental: - import ast, msgs, intsets, btrees, db_sqlite, std / sha1, pathutils - from strutils import parseInt - from os import isAbsolute - - type - Writer* = object - sstack*: seq[PSym] # a stack of symbols to process - tstack*: seq[PType] # a stack of types to process - tmarks*, smarks*: IntSet - forwardedSyms*: seq[PSym] - - Reader* = object - syms*: BTree[int, PSym] - types*: BTree[int, PType] - - IncrementalCtx* = object - db*: DbConn - w*: Writer - r*: Reader - configChanged*: bool - - proc init*(incr: var IncrementalCtx) = - incr.w.sstack = @[] - incr.w.tstack = @[] - incr.w.tmarks = initIntSet() - incr.w.smarks = initIntSet() - incr.w.forwardedSyms = @[] - incr.r.syms = initBTree[int, PSym]() - incr.r.types = initBTree[int, PType]() - - - proc hashFileCached*(conf: ConfigRef; fileIdx: FileIndex; fullpath: AbsoluteFile): string = - result = msgs.getHash(conf, fileIdx) - if result.len == 0 and isAbsolute(string fullpath): - result = $secureHashFile(string fullpath) - msgs.setHash(conf, fileIdx, result) - - proc toDbFileId*(incr: var IncrementalCtx; conf: ConfigRef; fileIdx: FileIndex): int = - if fileIdx == FileIndex(-1): return -1 - let fullpath = toFullPath(conf, fileIdx) - let row = incr.db.getRow(sql"select id, fullhash from filenames where fullpath = ?", - fullpath) - let id = row[0] - let fullhash = hashFileCached(conf, fileIdx, AbsoluteFile fullpath) - if id.len == 0: - result = int incr.db.insertID(sql"insert into filenames(nimid, fullpath, fullhash) values (?, ?, ?)", - int(fileIdx), fullpath, fullhash) - else: - if row[1] != fullhash: - incr.db.exec(sql"update filenames set fullhash = ? where fullpath = ?", fullhash, fullpath) - result = parseInt(id) - - proc fromDbFileId*(incr: var IncrementalCtx; conf: ConfigRef; dbId: int): FileIndex = - if dbId == -1: return FileIndex(-1) - let fullpath = incr.db.getValue(sql"select fullpath from filenames where id = ?", dbId) - doAssert fullpath.len > 0, "cannot find file name for DB ID " & $dbId - result = fileInfoIdx(conf, AbsoluteFile fullpath) - - - proc addModuleDep*(incr: var IncrementalCtx; conf: ConfigRef; - module, fileIdx: FileIndex; - isIncludeFile: bool) = - if conf.symbolFiles != v2Sf: return - - let a = toDbFileId(incr, conf, module) - let b = toDbFileId(incr, conf, fileIdx) - - incr.db.exec(sql"insert into deps(module, dependency, isIncludeFile) values (?, ?, ?)", - a, b, ord(isIncludeFile)) - - # --------------- Database model --------------------------------------------- - - proc createDb*(db: DbConn) = - db.exec(sql""" - create table if not exists controlblock( - idgen integer not null - ); - """) - - db.exec(sql""" - create table if not exists config( - config varchar(8000) not null - ); - """) - - db.exec(sql""" - create table if not exists filenames( - id integer primary key, - nimid integer not null, - fullpath varchar(8000) not null, - fullHash varchar(256) not null - ); - """) - db.exec sql"create index if not exists FilenameIx on filenames(fullpath);" - - db.exec(sql""" - create table if not exists modules( - id integer primary key, - nimid integer not null, - fullpath varchar(8000) not null, - interfHash varchar(256) not null, - fullHash varchar(256) not null, - - created timestamp not null default (DATETIME('now')) - );""") - db.exec(sql"""create unique index if not exists SymNameIx on modules(fullpath);""") - - db.exec(sql""" - create table if not exists deps( - id integer primary key, - module integer not null, - dependency integer not null, - isIncludeFile integer not null, - foreign key (module) references filenames(id), - foreign key (dependency) references filenames(id) - );""") - db.exec(sql"""create index if not exists DepsIx on deps(module);""") - - db.exec(sql""" - create table if not exists types( - id integer primary key, - nimid integer not null, - module integer not null, - data blob not null, - foreign key (module) references module(id) - ); - """) - db.exec sql"create index TypeByModuleIdx on types(module);" - db.exec sql"create index TypeByNimIdIdx on types(nimid);" - - db.exec(sql""" - create table if not exists syms( - id integer primary key, - nimid integer not null, - module integer not null, - name varchar(256) not null, - data blob not null, - exported int not null, - foreign key (module) references module(id) - ); - """) - db.exec sql"create index if not exists SymNameIx on syms(name);" - db.exec sql"create index SymByNameAndModuleIdx on syms(name, module);" - db.exec sql"create index SymByModuleIdx on syms(module);" - db.exec sql"create index SymByNimIdIdx on syms(nimid);" - - - db.exec(sql""" - create table if not exists toplevelstmts( - id integer primary key, - position integer not null, - module integer not null, - data blob not null, - foreign key (module) references module(id) - ); - """) - db.exec sql"create index TopLevelStmtByModuleIdx on toplevelstmts(module);" - db.exec sql"create index TopLevelStmtByPositionIdx on toplevelstmts(position);" - - db.exec(sql""" - create table if not exists statics( - id integer primary key, - module integer not null, - data blob not null, - foreign key (module) references module(id) - ); - """) - db.exec sql"create index StaticsByModuleIdx on toplevelstmts(module);" - db.exec sql"insert into controlblock(idgen) values (0)" - - -else: - type - IncrementalCtx* = object - - template init*(incr: IncrementalCtx) = discard - - template addModuleDep*(incr: var IncrementalCtx; conf: ConfigRef; - module, fileIdx: FileIndex; - isIncludeFile: bool) = - discard diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 34c11e06c..76a063faa 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -62,7 +62,7 @@ template dbg(body) = body proc getTemp(c: var Con; s: var Scope; typ: PType; info: TLineInfo): PNode = - let sym = newSym(skTemp, getIdent(c.graph.cache, ":tmpD"), nextId c.idgen, c.owner, info) + let sym = newSym(skTemp, getIdent(c.graph.cache, ":tmpD"), nextSymId c.idgen, c.owner, info) sym.typ = typ s.vars.add(sym) result = newSymNode(sym) @@ -252,7 +252,7 @@ proc checkForErrorPragma(c: Con; t: PType; ri: PNode; opname: string) = localError(c.graph.config, ri.info, errGenerated, m) proc makePtrType(c: var Con, baseType: PType): PType = - result = newType(tyPtr, nextId c.idgen, c.owner) + result = newType(tyPtr, nextTypeId c.idgen, c.owner) addSonSkipIntLit(result, baseType, c.idgen) proc genOp(c: var Con; op: PSym; dest: PNode): PNode = @@ -415,7 +415,7 @@ proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): PNode = else: result = newNodeIT(nkStmtListExpr, n.info, n.typ) - var temp = newSym(skLet, getIdent(c.graph.cache, "blitTmp"), nextId c.idgen, c.owner, n.info) + var temp = newSym(skLet, getIdent(c.graph.cache, "blitTmp"), nextSymId c.idgen, c.owner, n.info) temp.typ = n.typ var v = newNodeI(nkLetSection, n.info) let tempAsNode = newSymNode(temp) diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index 4f3823f8a..8eaa9e1e2 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -130,14 +130,14 @@ proc createClosureIterStateType*(g: ModuleGraph; iter: PSym; idgen: IdGenerator) var n = newNodeI(nkRange, iter.info) n.add newIntNode(nkIntLit, -1) n.add newIntNode(nkIntLit, 0) - result = newType(tyRange, nextId(idgen), iter) + result = newType(tyRange, nextTypeId(idgen), iter) result.n = n var intType = nilOrSysInt(g) - if intType.isNil: intType = newType(tyInt, nextId(idgen), iter) + if intType.isNil: intType = newType(tyInt, nextTypeId(idgen), iter) rawAddSon(result, intType) proc createStateField(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PSym = - result = newSym(skField, getIdent(g.cache, ":state"), nextId(idgen), iter, iter.info) + result = newSym(skField, getIdent(g.cache, ":state"), nextSymId(idgen), iter, iter.info) result.typ = createClosureIterStateType(g, iter, idgen) proc createEnvObj(g: ModuleGraph; idgen: IdGenerator; owner: PSym; info: TLineInfo): PType = @@ -151,7 +151,7 @@ proc getClosureIterResult*(g: ModuleGraph; iter: PSym; idgen: IdGenerator): PSym result = iter.ast[resultPos].sym else: # XXX a bit hacky: - result = newSym(skResult, getIdent(g.cache, ":result"), nextId(idgen), iter, iter.info, {}) + result = newSym(skResult, getIdent(g.cache, ":result"), nextSymId(idgen), iter, iter.info, {}) result.typ = iter.typ[0] incl(result.flags, sfUsed) iter.ast.add newSymNode(result) @@ -259,7 +259,7 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PN addUniqueField(it.typ.skipTypes({tyOwned})[0], hp, g.cache, idgen) env = indirectAccess(newSymNode(it), hp, hp.info) else: - let e = newSym(skLet, iter.name, nextId(idgen), owner, n.info) + let e = newSym(skLet, iter.name, nextSymId(idgen), owner, n.info) e.typ = hp.typ e.flags = hp.flags env = newSymNode(e) @@ -330,7 +330,7 @@ proc getEnvTypeForOwner(c: var DetectionPass; owner: PSym; info: TLineInfo): PType = result = c.ownerToType.getOrDefault(owner.id) if result.isNil: - result = newType(tyRef, nextId(c.idgen), owner) + result = newType(tyRef, nextTypeId(c.idgen), owner) let obj = createEnvObj(c.graph, c.idgen, owner, info) rawAddSon(result, obj) c.ownerToType[owner.id] = result @@ -338,7 +338,7 @@ proc getEnvTypeForOwner(c: var DetectionPass; owner: PSym; proc asOwnedRef(c: var DetectionPass; t: PType): PType = if optOwnedRefs in c.graph.config.globalOptions: assert t.kind == tyRef - result = newType(tyOwned, nextId(c.idgen), t.owner) + result = newType(tyOwned, nextTypeId(c.idgen), t.owner) result.flags.incl tfHasOwned result.rawAddSon t else: @@ -347,7 +347,7 @@ proc asOwnedRef(c: var DetectionPass; t: PType): PType = proc getEnvTypeForOwnerUp(c: var DetectionPass; owner: PSym; info: TLineInfo): PType = var r = c.getEnvTypeForOwner(owner, info) - result = newType(tyPtr, nextId(c.idgen), owner) + result = newType(tyPtr, nextTypeId(c.idgen), owner) rawAddSon(result, r.skipTypes({tyOwned, tyRef, tyPtr})) proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) = @@ -375,7 +375,7 @@ proc createUpField(c: var DetectionPass; dest, dep: PSym; info: TLineInfo) = if c.graph.config.selectedGC == gcDestructors and sfCursor notin upField.flags: localError(c.graph.config, dep.info, "internal error: up reference is not a .cursor") else: - let result = newSym(skField, upIdent, nextId(c.idgen), obj.owner, obj.owner.info) + let result = newSym(skField, upIdent, nextSymId(c.idgen), obj.owner, obj.owner.info) result.typ = fieldType when false: if c.graph.config.selectedGC == gcDestructors: @@ -413,7 +413,7 @@ proc addClosureParam(c: var DetectionPass; fn: PSym; info: TLineInfo) = let owner = if fn.kind == skIterator: fn else: fn.skipGenericOwner let t = c.getEnvTypeForOwner(owner, info) if cp == nil: - cp = newSym(skParam, getIdent(c.graph.cache, paramName), nextId(c.idgen), fn, fn.info) + cp = newSym(skParam, getIdent(c.graph.cache, paramName), nextSymId(c.idgen), fn, fn.info) incl(cp.flags, sfFromGeneric) cp.typ = t addHiddenParam(fn, cp) @@ -539,7 +539,7 @@ proc accessViaEnvParam(g: ModuleGraph; n: PNode; owner: PSym): PNode = result = n proc newEnvVar(cache: IdentCache; owner: PSym; typ: PType; info: TLineInfo; idgen: IdGenerator): PNode = - var v = newSym(skVar, getIdent(cache, envName), nextId(idgen), owner, info) + var v = newSym(skVar, getIdent(cache, envName), nextSymId(idgen), owner, info) v.flags = {sfShadowed, sfGeneratedOp} v.typ = typ result = newSymNode(v) @@ -563,7 +563,7 @@ proc setupEnvVar(owner: PSym; d: var DetectionPass; result = newEnvVar(d.graph.cache, owner, asOwnedRef(d, envVarType), info, d.idgen) c.envVars[owner.id] = result if optOwnedRefs in d.graph.config.globalOptions: - var v = newSym(skVar, getIdent(d.graph.cache, envName & "Alt"), nextId d.idgen, owner, info) + var v = newSym(skVar, getIdent(d.graph.cache, envName & "Alt"), nextSymId d.idgen, owner, info) v.flags = {sfShadowed, sfGeneratedOp} v.typ = envVarType c.unownedEnvVars[owner.id] = newSymNode(v) @@ -647,7 +647,7 @@ proc closureCreationForIter(iter: PNode; d: var DetectionPass; c: var LiftingPass): PNode = result = newNodeIT(nkStmtListExpr, iter.info, iter.sym.typ) let owner = iter.sym.skipGenericOwner - var v = newSym(skVar, getIdent(d.graph.cache, envName), nextId(d.idgen), owner, iter.info) + var v = newSym(skVar, getIdent(d.graph.cache, envName), nextSymId(d.idgen), owner, iter.info) incl(v.flags, sfShadowed) v.typ = asOwnedRef(d, getHiddenParam(d.graph, iter.sym).typ) var vnode: PNode @@ -937,7 +937,7 @@ proc liftForLoop*(g: ModuleGraph; body: PNode; idgen: IdGenerator; owner: PSym): let iter = op.sym let hp = getHiddenParam(g, iter) - env = newSym(skLet, iter.name, nextId(idgen), owner, body.info) + env = newSym(skLet, iter.name, nextSymId(idgen), owner, body.info) env.typ = hp.typ env.flags = hp.flags diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 512fb6190..5ffa25e53 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -222,7 +222,7 @@ proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) = # for every field (dependent on dest.kind): # `=` dest.field, src.field # =destroy(blob) - var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), nextId c.idgen, c.fn, c.info) + var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), nextSymId c.idgen, c.fn, c.info) temp.typ = x.typ incl(temp.flags, sfFromGeneric) var v = newNodeI(nkVarSection, c.info) @@ -317,7 +317,7 @@ proc considerAsgnOrSink(c: var TLiftCtx; t: PType; body, x, y: PNode; if optSeqDestructors in c.g.config.globalOptions: var op = field let destructorOverriden = destructorOverriden(t) - if op != nil and op != c.fn and + if op != nil and op != c.fn and (sfOverriden in op.flags or destructorOverriden): if sfError in op.flags: incl c.fn.flags, sfError @@ -412,7 +412,7 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool = result = true proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode = - var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), nextId(c.idgen), c.fn, c.info) + var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), nextSymId(c.idgen), c.fn, c.info) temp.typ = getSysType(c.g, body.info, tyInt) incl(temp.flags, sfFromGeneric) @@ -422,7 +422,7 @@ proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode = body.add v proc declareTempOf(c: var TLiftCtx; body: PNode; value: PNode): PNode = - var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), nextId(c.idgen), c.fn, c.info) + var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), nextSymId(c.idgen), c.fn, c.info) temp.typ = value.typ incl(temp.flags, sfFromGeneric) @@ -899,17 +899,17 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp info: TLineInfo; idgen: IdGenerator): PSym = let procname = getIdent(g.cache, AttachedOpToStr[kind]) - result = newSym(skProc, procname, nextId(idgen), owner, info) - let dest = newSym(skParam, getIdent(g.cache, "dest"), nextId(idgen), result, info) + result = newSym(skProc, procname, nextSymId(idgen), owner, info) + let dest = newSym(skParam, getIdent(g.cache, "dest"), nextSymId(idgen), result, info) let src = newSym(skParam, getIdent(g.cache, if kind == attachedTrace: "env" else: "src"), - nextId(idgen), result, info) + nextSymId(idgen), result, info) dest.typ = makeVarType(typ.owner, typ, idgen) if kind == attachedTrace: src.typ = getSysType(g, info, tyPointer) else: src.typ = typ - result.typ = newProcType(info, nextId(idgen), owner) + result.typ = newProcType(info, nextTypeId(idgen), owner) result.typ.addParam dest if kind notin {attachedDestructor, attachedDispose}: result.typ.addParam src @@ -917,7 +917,7 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp if kind == attachedAsgn and g.config.selectedGC == gcOrc and cyclicType(typ.skipTypes(abstractInst)): let cycleParam = newSym(skParam, getIdent(g.cache, "cyclic"), - nextId(idgen), result, info) + nextSymId(idgen), result, info) cycleParam.typ = getSysType(g, info, tyBool) result.typ.addParam cycleParam @@ -983,7 +983,7 @@ proc produceDestructorForDiscriminator*(g: ModuleGraph; typ: PType; field: PSym, a.addMemReset = true let discrimantDest = result.typ.n[1].sym - let dst = newSym(skVar, getIdent(g.cache, "dest"), nextId(idgen), result, info) + let dst = newSym(skVar, getIdent(g.cache, "dest"), nextSymId(idgen), result, info) dst.typ = makePtrType(typ.owner, typ, idgen) let dstSym = newSymNode(dst) let d = newDeref(dstSym) diff --git a/compiler/lookups.nim b/compiler/lookups.nim index fba032c1e..3c0aabe9a 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -227,7 +227,7 @@ proc errorSym*(c: PContext, n: PNode): PSym = considerQuotedIdent(c, m) else: getIdent(c.cache, "err:" & renderTree(m)) - result = newSym(skError, ident, nextId(c.idgen), getCurrOwner(c), n.info, {}) + result = newSym(skError, ident, nextSymId(c.idgen), getCurrOwner(c), n.info, {}) result.typ = errorType(c) incl(result.flags, sfDiscardable) # pretend it's from the top level scope to prevent cascading errors: diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index e9e704075..37405d8d9 100644 --- a/compiler/lowerings.nim +++ b/compiler/lowerings.nim @@ -71,7 +71,7 @@ proc lowerTupleUnpacking*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: P let value = n.lastSon result = newNodeI(nkStmtList, n.info) - var temp = newSym(skTemp, getIdent(g.cache, genPrefix), nextId(idgen), + var temp = newSym(skTemp, getIdent(g.cache, genPrefix), nextSymId(idgen), owner, value.info, g.config.options) temp.typ = skipTypes(value.typ, abstractInst) incl(temp.flags, sfFromGeneric) @@ -91,7 +91,7 @@ proc evalOnce*(g: ModuleGraph; value: PNode; idgen: IdGenerator; owner: PSym): P ## freely, multiple times. This is frequently required and such a builtin would also be ## handy to have in macros.nim. The value that can be reused is 'result.lastSon'! result = newNodeIT(nkStmtListExpr, value.info, value.typ) - var temp = newSym(skTemp, getIdent(g.cache, genPrefix), nextId(idgen), + var temp = newSym(skTemp, getIdent(g.cache, genPrefix), nextSymId(idgen), owner, value.info, g.config.options) temp.typ = skipTypes(value.typ, abstractInst) incl(temp.flags, sfFromGeneric) @@ -117,7 +117,7 @@ proc lowerTupleUnpackingForAsgn*(g: ModuleGraph; n: PNode; idgen: IdGenerator; o let value = n.lastSon result = newNodeI(nkStmtList, n.info) - var temp = newSym(skTemp, getIdent(g.cache, "_"), nextId(idgen), owner, value.info, owner.options) + var temp = newSym(skTemp, getIdent(g.cache, "_"), nextSymId(idgen), owner, value.info, owner.options) var v = newNodeI(nkLetSection, value.info) let tempAsNode = newSymNode(temp) #newIdentNode(getIdent(genPrefix & $temp.id), value.info) @@ -135,7 +135,7 @@ proc lowerTupleUnpackingForAsgn*(g: ModuleGraph; n: PNode; idgen: IdGenerator; o proc lowerSwap*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNode = result = newNodeI(nkStmtList, n.info) # note: cannot use 'skTemp' here cause we really need the copy for the VM :-( - var temp = newSym(skVar, getIdent(g.cache, genPrefix), nextId(idgen), owner, n.info, owner.options) + var temp = newSym(skVar, getIdent(g.cache, genPrefix), nextSymId(idgen), owner, n.info, owner.options) temp.typ = n[1].typ incl(temp.flags, sfFromGeneric) incl(temp.flags, sfGenSym) @@ -154,7 +154,7 @@ proc lowerSwap*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PNod result.add newFastAsgnStmt(n[2], tempAsNode) proc createObj*(g: ModuleGraph; idgen: IdGenerator; owner: PSym, info: TLineInfo; final=true): PType = - result = newType(tyObject, nextId(idgen), owner) + result = newType(tyObject, nextTypeId(idgen), owner) if final: rawAddSon(result, nil) incl result.flags, tfFinal @@ -162,7 +162,7 @@ proc createObj*(g: ModuleGraph; idgen: IdGenerator; owner: PSym, info: TLineInfo rawAddSon(result, getCompilerProc(g, "RootObj").typ) result.n = newNodeI(nkRecList, info) let s = newSym(skType, getIdent(g.cache, "Env_" & toFilename(g.config, info) & "_" & $owner.name.s), - nextId(idgen), + nextSymId(idgen), owner, info, owner.options) incl s.flags, sfAnon s.typ = result @@ -225,7 +225,7 @@ proc addField*(obj: PType; s: PSym; cache: IdentCache; idgen: IdGenerator) = # because of 'gensym' support, we have to mangle the name with its ID. # This is hacky but the clean solution is much more complex than it looks. var field = newSym(skField, getIdent(cache, s.name.s & $obj.n.len), - nextId(idgen), s.owner, s.info, s.options) + nextSymId(idgen), s.owner, s.info, s.options) field.itemId = ItemId(module: s.itemId.module, item: -s.itemId.item) let t = skipIntLit(s.typ, idgen) field.typ = t @@ -239,7 +239,7 @@ proc addField*(obj: PType; s: PSym; cache: IdentCache; idgen: IdGenerator) = proc addUniqueField*(obj: PType; s: PSym; cache: IdentCache; idgen: IdGenerator): PSym {.discardable.} = result = lookupInRecord(obj.n, s.itemId) if result == nil: - var field = newSym(skField, getIdent(cache, s.name.s & $obj.n.len), nextId(idgen), + var field = newSym(skField, getIdent(cache, s.name.s & $obj.n.len), nextSymId(idgen), s.owner, s.info, s.options) field.itemId = ItemId(module: s.itemId.module, item: -s.itemId.item) let t = skipIntLit(s.typ, idgen) @@ -326,7 +326,7 @@ proc indirectAccess*(a, b: PSym, info: TLineInfo): PNode = proc genAddrOf*(n: PNode; idgen: IdGenerator; typeKind = tyPtr): PNode = result = newNodeI(nkAddr, n.info, 1) result[0] = n - result.typ = newType(typeKind, nextId(idgen), n.typ.owner) + result.typ = newType(typeKind, nextTypeId(idgen), n.typ.owner) result.typ.rawAddSon(n.typ) proc genDeref*(n: PNode; k = nkHiddenDeref): PNode = diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim index 71003371e..d700ab9a7 100644 --- a/compiler/magicsys.nim +++ b/compiler/magicsys.nim @@ -21,7 +21,7 @@ proc registerSysType*(g: ModuleGraph; t: PType) = if g.sysTypes[t.kind] == nil: g.sysTypes[t.kind] = t proc newSysType(g: ModuleGraph; kind: TTypeKind, size: int): PType = - result = newType(kind, nextId(g.idgen), g.systemModule) + result = newType(kind, nextTypeId(g.idgen), g.systemModule) result.size = size result.align = size.int16 @@ -29,8 +29,8 @@ proc getSysSym*(g: ModuleGraph; info: TLineInfo; name: string): PSym = result = strTableGet(g.systemModule.tab, getIdent(g.cache, name)) if result == nil: localError(g.config, info, "system module needs: " & name) - result = newSym(skError, getIdent(g.cache, name), nextId(g.idgen), g.systemModule, g.systemModule.info, {}) - result.typ = newType(tyError, nextId(g.idgen), g.systemModule) + result = newSym(skError, getIdent(g.cache, name), nextSymId(g.idgen), g.systemModule, g.systemModule.info, {}) + result.typ = newType(tyError, nextTypeId(g.idgen), g.systemModule) if result.kind == skAlias: result = result.owner proc getSysMagic*(g: ModuleGraph; info: TLineInfo; name: string, m: TMagic): PSym = @@ -45,8 +45,8 @@ proc getSysMagic*(g: ModuleGraph; info: TLineInfo; name: string, m: TMagic): PSy r = nextIdentIter(ti, g.systemModule.tab) if result != nil: return result localError(g.config, info, "system module needs: " & name) - result = newSym(skError, id, nextId(g.idgen), g.systemModule, g.systemModule.info, {}) - result.typ = newType(tyError, nextId(g.idgen), g.systemModule) + result = newSym(skError, id, nextSymId(g.idgen), g.systemModule, g.systemModule.info, {}) + result.typ = newType(tyError, nextTypeId(g.idgen), g.systemModule) proc sysTypeFromName*(g: ModuleGraph; info: TLineInfo; name: string): PType = result = getSysSym(g, info, name).typ @@ -101,12 +101,12 @@ proc getIntLitType*(g: ModuleGraph; literal: PNode): PType = result = g.intTypeCache[value.int] if result == nil: let ti = getSysType(g, literal.info, tyInt) - result = copyType(ti, nextId(g.idgen), ti.owner) + result = copyType(ti, nextTypeId(g.idgen), ti.owner) result.n = literal g.intTypeCache[value.int] = result else: let ti = getSysType(g, literal.info, tyInt) - result = copyType(ti, nextId(g.idgen), ti.owner) + result = copyType(ti, nextTypeId(g.idgen), ti.owner) result.n = literal proc getFloatLitType*(g: ModuleGraph; literal: PNode): PType = @@ -116,7 +116,7 @@ proc getFloatLitType*(g: ModuleGraph; literal: PNode): PType = proc skipIntLit*(t: PType; id: IdGenerator): PType {.inline.} = if t.n != nil and t.kind in {tyInt, tyFloat}: - result = copyType(t, nextId(id), t.owner) + result = copyType(t, nextTypeId(id), t.owner) result.n = nil else: result = t diff --git a/compiler/main.nim b/compiler/main.nim index c94c4323f..868198268 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -19,7 +19,7 @@ import cgen, json, nversion, platform, nimconf, passaux, depends, vm, modules, - modulegraphs, tables, rod, lineinfos, pathutils, vmprofiler + modulegraphs, tables, lineinfos, pathutils, vmprofiler when not defined(leanCompiler): import jsgen, docgen, docgen2 @@ -137,7 +137,7 @@ proc commandInteractive(graph: ModuleGraph) = else: var m = graph.makeStdinModule() incl(m.flags, sfMainModule) - var idgen = IdGenerator(module: m.itemId.module, item: m.itemId.item) + var idgen = IdGenerator(module: m.itemId.module, symId: m.itemId.item, typeId: 0) let s = llStreamOpenStdIn(onPrompt = proc() = flushDot(graph.config)) processModule(graph, m, idgen, s) @@ -165,7 +165,6 @@ proc mainCommand*(graph: ModuleGraph) = let conf = graph.config let cache = graph.cache - setupModuleCache(graph) # In "nim serve" scenario, each command must reset the registered passes clearPasses(graph) conf.lastCmdTime = epochTime() diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index e3c54eeae..5544668cc 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -9,7 +9,7 @@ ## This module implements the module graph data structure. The module graph ## represents a complete Nim project. Single modules can either be kept in RAM -## or stored in a Sqlite database. +## or stored in a rod-file. ## ## The caching of modules is critical for 'nimsuggest' and is tricky to get ## right. If module E is being edited, we need autocompletion (and type @@ -26,7 +26,7 @@ ## import ast, intsets, tables, options, lineinfos, hashes, idents, - incremental, btrees, md5 + btrees, md5 # import ic / packed_ast @@ -67,7 +67,6 @@ type intTypeCache*: array[-5..64, PType] opContains*, opNot*: PSym emptyNode*: PNode - incr*: IncrementalCtx canonTypes*: Table[SigHash, PType] symBodyHashes*: Table[int, SigHash] # symId to digest mapping importModuleCallback*: proc (graph: ModuleGraph; m: PSym, fileIdx: FileIndex): PSym {.nimcall.} @@ -176,7 +175,7 @@ proc stopCompile*(g: ModuleGraph): bool {.inline.} = result = g.doStopCompile != nil and g.doStopCompile() proc createMagic*(g: ModuleGraph; name: string, m: TMagic): PSym = - result = newSym(skProc, getIdent(g.cache, name), nextId(g.idgen), nil, unknownLineInfo, {}) + result = newSym(skProc, getIdent(g.cache, name), nextSymId(g.idgen), nil, unknownLineInfo, {}) result.magic = m result.flags = {sfNeverRaises} @@ -190,7 +189,7 @@ proc registerModule*(g: ModuleGraph; m: PSym) = proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph = result = ModuleGraph() - result.idgen = IdGenerator(module: -1'i32, item: 0'i32) + result.idgen = IdGenerator(module: -1'i32, symId: 0'i32, typeId: 0'i32) initStrTable(result.packageSyms) result.deps = initIntSet() result.importDeps = initTable[FileIndex, seq[FileIndex]]() @@ -206,7 +205,6 @@ proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph = result.opNot = createMagic(result, "not", mNot) result.opContains = createMagic(result, "contains", mInSet) result.emptyNode = newNode(nkEmpty) - init(result.incr) result.recordStmt = proc (graph: ModuleGraph; m: PSym; n: PNode) {.nimcall.} = discard result.cacheSeqs = initTable[string, PNode]() @@ -235,7 +233,6 @@ proc dependsOn(a, b: int): int {.inline.} = (a shl 15) + b proc addDep*(g: ModuleGraph; m: PSym, dep: FileIndex) = assert m.position == m.info.fileIndex.int32 - addModuleDep(g.incr, g.config, m.info.fileIndex, dep, isIncludeFile = false) if g.suggestMode: g.deps.incl m.position.dependsOn(dep.int) # we compute the transitive closure later when querying the graph lazily. @@ -243,7 +240,6 @@ proc addDep*(g: ModuleGraph; m: PSym, dep: FileIndex) = #invalidTransitiveClosure = true proc addIncludeDep*(g: ModuleGraph; module, includeFile: FileIndex) = - addModuleDep(g.incr, g.config, module, includeFile, isIncludeFile = true) discard hasKeyOrPut(g.inclToMod, includeFile, module) proc parentModule*(g: ModuleGraph; fileIdx: FileIndex): FileIndex = diff --git a/compiler/modules.nim b/compiler/modules.nim index 132f3788d..a8a9c4df8 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -11,7 +11,7 @@ import ast, astalgo, magicsys, msgs, options, - idents, lexer, passes, syntaxes, llstream, modulegraphs, rod, + idents, lexer, passes, syntaxes, llstream, modulegraphs, lineinfos, pathutils, tables proc resetSystemArtifacts*(g: ModuleGraph) = @@ -93,8 +93,10 @@ proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags): P discard processModule(graph, result, idGeneratorFromModule(result), s) if result == nil: let filename = AbsoluteFile toFullPath(graph.config, fileIdx) - result = loadModuleSym(graph, fileIdx, filename) - if result == nil: + when false: + # XXX entry point for module loading from the rod file + result = loadModuleSym(graph, fileIdx, filename) + when true: result = newModule(graph, fileIdx) result.flags.incl flags registerModule(graph, result) diff --git a/compiler/nilcheck.nim b/compiler/nilcheck.nim index 23f403589..d0ef45d04 100644 --- a/compiler/nilcheck.nim +++ b/compiler/nilcheck.nim @@ -11,9 +11,9 @@ import ast, renderer, intsets, tables, msgs, options, lineinfos, strformat, iden import sequtils, strutils, std / sets # IMPORTANT: notes not up to date, i'll update this comment again -# +# # notes: -# +# # Env: int => nilability # a = b # nilability a <- nilability b @@ -111,7 +111,7 @@ type Symbol = distinct int ## the index of an expression in the pre-indexed sequence of those - ExprIndex = distinct int16 + ExprIndex = distinct int16 ## the set index SetIndex = distinct int @@ -131,7 +131,7 @@ type ## the context for the checker: an instance for each procedure NilCheckerContext = ref object # abstractTime: AbstractTime - # partitions: Partitions + # partitions: Partitions # symbolGraphs: Table[Symbol, ] symbolIndices: Table[Symbol, ExprIndex] ## index for each symbol expressions: SeqOfDistinct[ExprIndex, PNode] ## a sequence of pre-indexed expressions @@ -360,7 +360,7 @@ func `$`(a: Symbol): string = $(a.int) template isConstBracket(n: PNode): bool = - n.kind == nkBracketExpr and n[1].kind in nkLiterals + n.kind == nkBracketExpr and n[1].kind in nkLiterals proc index(ctx: NilCheckerContext, n: PNode): ExprIndex = # echo "n ", n, " ", n.kind @@ -373,7 +373,7 @@ proc index(ctx: NilCheckerContext, n: PNode): ExprIndex = #echo n.kind # internalError(ctx.config, n.info, "expected " & $a & " " & $n & " to have a index") return noExprIndex - # + # #ctx.symbolIndices[symbol(n)] @@ -384,7 +384,7 @@ proc aliasSet(ctx: NilCheckerContext, map: NilMap, index: ExprIndex): IntSet = result = map.sets[map.setIndices[index]] - + proc store(map: NilMap, ctx: NilCheckerContext, index: ExprIndex, value: Nilability, kind: TransitionKind, info: TLineInfo, node: PNode = nil) = if index == noExprIndex: return @@ -414,7 +414,7 @@ proc moveOut(ctx: NilCheckerContext, map: NilMap, target: PNode) = var targetSet = map.sets[targetSetIndex] if targetSet.len > 1: var other: ExprIndex - + for element in targetSet: if element.ExprIndex != targetIndex: other = element.ExprIndex @@ -440,7 +440,7 @@ proc move(ctx: NilCheckerContext, map: NilMap, target: PNode, assigned: PNode) = #echo "move ", target, " ", assigned var targetIndex = ctx.index(target) var assignedIndex: ExprIndex - var targetSetIndex = map.setIndices[targetIndex] + var targetSetIndex = map.setIndices[targetIndex] var assignedSetIndex: SetIndex if assigned.kind == nkSym: assignedIndex = ctx.index(assigned) @@ -497,12 +497,12 @@ proc checkCall(n, ctx, map): Check = result.map = map for i, child in n: discard check(child, ctx, map) - + if i > 0: # var args make a new map with MaybeNil for our node # as it might have been mutated # TODO similar for normal refs and fields: find dependent exprs: brackets - + if child.kind == nkHiddenAddr and not child.typ.isNil and child.typ.kind == tyVar and child.typ[0].kind == tyRef: if not isNew: result.map = newNilMap(map) @@ -526,7 +526,7 @@ proc checkCall(n, ctx, map): Check = isNew = true moveOutDependants(ctx, result.map, child) storeDependants(ctx, result.map, child, MaybeNil) - + if n[0].kind == nkSym and n[0].sym.magic == mNew: # new hidden deref? var value = if n[1].kind == nkHiddenDeref: n[1][0] else: n[1] @@ -552,7 +552,7 @@ template event(b: History): string = of TSafe: "it is safe here as it returns false for isNil" of TPotentialAlias: "it might be changed directly or through an alias" of TDependant: "it might be changed because its base might be changed" - + proc derefWarning(n, ctx, map; kind: Nilability) = ## a warning for potentially unsafe dereference if n.info in ctx.warningLocations: @@ -587,14 +587,14 @@ proc handleNilability(check: Check; n, ctx, map) = else: when defined(nilDebugInfo): message(ctx.config, n.info, hintUser, "can deref " & $n) - + proc checkDeref(n, ctx, map): Check = ## check dereference: deref n should be ok only if n is Safe result = check(n[0], ctx, map) - + handleNilability(result, n[0], ctx, map) - + proc checkRefExpr(n, ctx; check: Check): Check = ## check ref expressions: TODO not sure when this happens result = check @@ -625,7 +625,7 @@ proc checkBracketExpr(n, ctx, map): Check = result = check(n[1], ctx, result.map) result = checkRefExpr(n, ctx, result) # echo n, " ", result.nilability - + template union(l: Nilability, r: Nilability): Nilability = ## unify two states @@ -654,7 +654,7 @@ proc findCommonParent(l: NilMap, r: NilMap): NilMap = result = l.parent while not result.isNil: var rparent = r.parent - while not rparent.isNil: + while not rparent.isNil: if result == rparent: return result rparent = rparent.parent @@ -666,17 +666,17 @@ proc union(ctx: NilCheckerContext, l: NilMap, r: NilMap): NilMap = ## what if they are from different parts of the same tree ## e.g. ## a -> b -> c - ## -> b1 + ## -> b1 ## common then? - ## + ## if l.isNil: return r elif r.isNil: return l - + let common = findCommonParent(l, r) result = newNilMap(common, ctx.expressions.len.int) - + for index, value in l: let h = history(r, index) let info = if h.len > 0: h[^1].info else: TLineInfo(line: 0) # assert h.len > 0 @@ -715,11 +715,11 @@ proc checkAsgn(target: PNode, assigned: PNode; ctx, map): Check = result = check(assigned, ctx, map) else: result = Check(nilability: typeNilability(target.typ), map: map) - + # we need to visit and check those, but we don't use the result for now # is it possible to somehow have another event happen here? discard check(target, ctx, map) - + if result.map.isNil: result.map = map if target.kind in {nkSym, nkDotExpr} or isConstBracket(target): @@ -738,8 +738,8 @@ proc checkAsgn(target: PNode, assigned: PNode; ctx, map): Check = if symbol(elementNode) in ctx.symbolIndices: var elementIndex = ctx.index(elementNode) result.map.store(ctx, elementIndex, value, TAssign, target.info, elementNode) - - + + proc checkReturn(n, ctx, map): Check = ## check return # return n same as result = n; return ? @@ -750,9 +750,9 @@ proc checkReturn(n, ctx, map): Check = proc checkIf(n, ctx, map): Check = ## check branches based on condition var mapIf: NilMap = map - + # first visit the condition - + # the structure is not If(Elif(Elif, Else), Else) # it is # If(Elif, Elif, Else) @@ -765,7 +765,7 @@ proc checkIf(n, ctx, map): Check = var afterLayer: NilMap # the result nilability for expressions var nilability = Safe - + for branch in n.sons: var branchConditionLayer = newNilMap(layerHistory) var branchLayer: NilMap @@ -779,7 +779,7 @@ proc checkIf(n, ctx, map): Check = else: branchLayer = layerHistory code = branch - + let branchCheck = checkBranch(code, ctx, branchLayer) # handles nil afterLayer -> returns branchCheck.map afterLayer = ctx.union(afterLayer, branchCheck.map) @@ -796,7 +796,7 @@ proc checkIf(n, ctx, map): Check = result.map = ctx.union(layerHistory, afterLayer) result.nilability = Safe # no expr? else: - # similar to else: because otherwise we are jumping out of + # similar to else: because otherwise we are jumping out of # the branch, so no union with the mapIf (we dont continue if the condition was true) # here it also doesn't matter for the parent branch what happened in the branch, e.g. assigning to nil # as if we continue there, we haven't entered the branch probably @@ -820,7 +820,7 @@ proc checkFor(n, ctx, map): Check = # echo namedMapDebugInfo(ctx, map) var check2 = check(n.sons[2], ctx, m) var map2 = check2.map - + result.map = ctx.union(map0, m) result.map = ctx.union(result.map, map2) result.nilability = Safe @@ -848,11 +848,11 @@ proc checkWhile(n, ctx, map): Check = var map1 = m.copyMap() var check2 = check(n.sons[1], ctx, m) var map2 = check2.map - + result.map = ctx.union(map0, map1) result.map = ctx.union(result.map, map2) result.nilability = Safe - + proc checkInfix(n, ctx, map): Check = ## check infix operators in condition ## a and b : map is based on a; next b @@ -882,7 +882,7 @@ proc checkInfix(n, ctx, map): Check = result.map = checkCondition(n[2], ctx, map, false, false) elif $n[1] == "false": result.map = checkCondition(n[2], ctx, map, true, false) - + if result.map.isNil: result.map = map else: @@ -906,24 +906,24 @@ proc infix(ctx: NilCheckerContext, l: PNode, r: PNode, magic: TMagic): PNode = else: "" var cache = newIdentCache() - var op = newSym(skVar, cache.getIdent(name), nextId ctx.idgen, nil, r.info) + var op = newSym(skVar, cache.getIdent(name), nextSymId ctx.idgen, nil, r.info) op.magic = magic result = nkInfix.newTree( newSymNode(op, r.info), l, r) - result.typ = newType(tyBool, nextId ctx.idgen, nil) + result.typ = newType(tyBool, nextTypeId ctx.idgen, nil) proc prefixNot(ctx: NilCheckerContext, node: PNode): PNode = var cache = newIdentCache() - var op = newSym(skVar, cache.getIdent("not"), nextId ctx.idgen, nil, node.info) + var op = newSym(skVar, cache.getIdent("not"), nextSymId ctx.idgen, nil, node.info) op.magic = mNot result = nkPrefix.newTree( newSymNode(op, node.info), node) - result.typ = newType(tyBool, nextId ctx.idgen, nil) + result.typ = newType(tyBool, nextTypeId ctx.idgen, nil) proc infixEq(ctx: NilCheckerContext, l: PNode, r: PNode): PNode = infix(ctx, l, r, mEqRef) @@ -1016,7 +1016,7 @@ proc checkTry(n, ctx, map): Check = let tryCheck = check(n[0], ctx, currentMap) newMap = ctx.union(currentMap, tryCheck.map) canRaise = n[0].canRaise - + var afterTryMap = newMap for a, branch in n: if a > 0: @@ -1026,7 +1026,7 @@ proc checkTry(n, ctx, map): Check = let childCheck = check(branch[0], ctx, newMap) newMap = ctx.union(newMap, childCheck.map) hasFinally = true - of nkExceptBranch: + of nkExceptBranch: if canRaise: let childCheck = check(branch[^1], ctx, newMap) newMap = ctx.union(newMap, childCheck.map) @@ -1069,7 +1069,7 @@ proc reverse(kind: TransitionKind): TransitionKind = of TNil: TSafe of TSafe: TNil of TPotentialAlias: TPotentialAlias - else: + else: kind # raise newException(ValueError, "expected TNil or TSafe") @@ -1079,41 +1079,41 @@ proc reverseDirect(map: NilMap): NilMap = # because conditions should've stored their changes there # b: Safe (not b.isNil) # b: Parent Parent - # b: Nil (b.isNil) + # b: Nil (b.isNil) # layer block # [ Parent ] [ Parent ] - # if -> if state + # if -> if state # layer -> reverse # older older0 new # older new # [ b Nil ] [ Parent ] # elif # [ b Nil, c Nil] [ Parent ] - # + # - # if b.isNil: + # if b.isNil: # # [ b Safe] # c = A() # Safe - # elif not b.isNil: + # elif not b.isNil: # # [ b Safe ] + [b Nil] MaybeNil Unreachable # # Unreachable defer can't deref b, it is unreachable # discard # else: - # b + # b - -# if + +# if # if: we just pass the map with a new layer for its block # elif: we just pass the original map but with a new layer is the reverse of the previous popped layer (?) - # elif: + # elif: # else: we just pass the original map but with a new layer which is initialized as the reverse of the # top layer of else # else: - # + # # [ b MaybeNil ] [b Parent] [b Parent] [b Safe] [b Nil] [] # Safe # c == 1 @@ -1181,7 +1181,7 @@ proc checkResult(n, ctx, map) = of Unreachable: message(ctx.config, n.info, warnStrictNotNil, "return value is unreachable") of Safe, Parent: - discard + discard proc checkBranch(n: PNode, ctx: NilCheckerContext, map: NilMap): Check = result = check(n, ctx, map) @@ -1191,7 +1191,7 @@ proc checkBranch(n: PNode, ctx: NilCheckerContext, map: NilMap): Check = proc check(n: PNode, ctx: NilCheckerContext, map: NilMap): Check = assert not map.isNil - + # echo "check n ", n, " ", n.kind # echo "map ", namedMapDebugInfo(ctx, map) case n.kind: @@ -1218,7 +1218,7 @@ proc check(n: PNode, ctx: NilCheckerContext, map: NilMap): Check = if n.kind in {nkObjConstr, nkTupleConstr}: # TODO deeper nested elements? # A(field: B()) # - # field: Safe -> + # field: Safe -> var elements: seq[(PNode, Nilability)] for i, child in n: result = check(child, ctx, result.map) @@ -1230,7 +1230,7 @@ proc check(n: PNode, ctx: NilCheckerContext, map: NilMap): Check = else: for child in n: result = check(child, ctx, result.map) - + of nkDotExpr: result = checkDotExpr(n, ctx, map) of nkDerefExpr, nkHiddenDeref: @@ -1261,7 +1261,7 @@ proc check(n: PNode, ctx: NilCheckerContext, map: NilMap): Check = nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt, nkExportStmt, nkPragma, nkCommentStmt, nkBreakState, nkTypeOfExpr: - + discard "don't follow this : same as varpartitions" result = Check(nilability: Nil, map: map) else: @@ -1275,17 +1275,17 @@ proc check(n: PNode, ctx: NilCheckerContext, map: NilMap): Check = result = Check(nilability: Nil, map: elementCheck.map) - - + + proc typeNilability(typ: PType): Nilability = assert not typ.isNil # echo "typeNilability ", $typ.flags, " ", $typ.kind result = if tfNotNil in typ.flags: Safe elif typ.kind in {tyRef, tyCString, tyPtr, tyPointer}: - # + # # tyVar ? tyVarargs ? tySink ? tyLent ? - # TODO spec? tests? + # TODO spec? tests? MaybeNil else: Safe @@ -1354,7 +1354,7 @@ proc checkNil*(s: PSym; body: PNode; conf: ConfigRef, idgen: IdGenerator) = var context = NilCheckerContext(config: conf, idgen: idgen) context.preVisit(s, body, conf) var map = newNilMap(nil, context.symbolIndices.len) - + for i, child in s.typ.n.sons: if i > 0: if child.kind != nkSym: @@ -1362,7 +1362,7 @@ proc checkNil*(s: PSym; body: PNode; conf: ConfigRef, idgen: IdGenerator) = map.store(context, context.index(child), typeNilability(child.typ), TArg, child.info, child) map.store(context, resultExprIndex, if not s.typ[0].isNil and s.typ[0].kind == tyRef: Nil else: Safe, TResult, s.ast.info) - + # echo "checking ", s.name.s, " ", filename let res = check(body, context, map) @@ -1374,7 +1374,7 @@ proc checkNil*(s: PSym; body: PNode; conf: ConfigRef, idgen: IdGenerator) = res.map.store(context, resultExprIndex, Safe, TAssign, s.ast.info) # TODO check for nilability result - # (ANotNil, BNotNil) : + # (ANotNil, BNotNil) : # do we check on asgn nilability at all? if not s.typ[0].isNil and s.typ[0].kind == tyRef and tfNotNil in s.typ[0].flags: diff --git a/compiler/options.nim b/compiler/options.nim index 1de6d531a..4a013f8fc 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -722,6 +722,10 @@ proc completeGeneratedFilePath*(conf: ConfigRef; f: AbsoluteFile, result = subdir / RelativeFile f.string.splitPath.tail #echo "completeGeneratedFilePath(", f, ") = ", result +proc toRodFile*(conf: ConfigRef; f: AbsoluteFile): AbsoluteFile = + result = changeFileExt(completeGeneratedFilePath(conf, + withPackageName(conf, f)), RodExt) + proc rawFindFile(conf: ConfigRef; f: RelativeFile; suppressStdlib: bool): AbsoluteFile = for it in conf.searchPaths: if suppressStdlib and it.string.startsWith(conf.libpath.string): diff --git a/compiler/passes.nim b/compiler/passes.nim index f266d2a6b..997a10cd8 100644 --- a/compiler/passes.nim +++ b/compiler/passes.nim @@ -13,7 +13,7 @@ import options, ast, llstream, msgs, idents, - syntaxes, modulegraphs, reorder, rod, + syntaxes, modulegraphs, reorder, lineinfos, pathutils type @@ -119,88 +119,65 @@ proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator; s: PLLStream fileIdx = module.fileIdx prepareConfigNotes(graph, module) - if module.id < 0: - # new module caching mechanism: - for i in 0..') - -proc encodeType(g: ModuleGraph, t: PType, result: var string) = - if t == nil: - # nil nodes have to be stored too: - result.add("[]") - return - # we need no surrounding [] here because the type is in a line of its own - if t.kind == tyForward: internalError(g.config, "encodeType: tyForward") - # for the new rodfile viewer we use a preceding [ so that the data section - # can easily be disambiguated: - result.add('[') - encodeVInt(ord(t.kind), result) - result.add('+') - encodeVInt(t.uniqueId, result) - if t.id != t.uniqueId: - result.add('+') - encodeVInt(t.id, result) - if t.n != nil: - encodeNode(g, unknownLineInfo, t.n, result) - if t.flags != {}: - result.add('$') - encodeVInt(cast[int32](t.flags), result) - if t.callConv != low(t.callConv): - result.add('?') - encodeVInt(ord(t.callConv), result) - if t.owner != nil: - result.add('*') - encodeVInt(t.owner.id, result) - pushSym(w, t.owner) - if t.sym != nil: - result.add('&') - encodeVInt(t.sym.id, result) - pushSym(w, t.sym) - if t.size != - 1: - result.add('/') - encodeVBiggestInt(t.size, result) - if t.align != 2: - result.add('=') - encodeVInt(t.align, result) - if t.lockLevel.ord != UnspecifiedLockLevel.ord: - result.add('\14') - encodeVInt(t.lockLevel.int16, result) - if t.paddingAtEnd != 0: - result.add('\15') - encodeVInt(t.paddingAtEnd, result) - for a in t.attachedOps: - result.add('\16') - if a == nil: - encodeVInt(-1, result) - else: - encodeVInt(a.id, result) - pushSym(w, a) - for i, s in items(t.methods): - result.add('\19') - encodeVInt(i, result) - result.add('\20') - encodeVInt(s.id, result) - pushSym(w, s) - encodeLoc(g, t.loc, result) - if t.typeInst != nil: - result.add('\21') - encodeVInt(t.typeInst.uniqueId, result) - pushType(w, t.typeInst) - for i in 0.. 100_000: - doAssert false, "loop never ends!" - if w.sstack.len > 0: - let s = w.sstack.pop() - when false: - echo "popped ", s.name.s, " ", s.id - storeSym(g, s) - elif w.tstack.len > 0: - let t = w.tstack.pop() - storeType(g, t) - when false: - echo "popped type ", typeToString(t), " ", t.uniqueId - else: - break - inc i - -proc storeNode*(g: ModuleGraph; module: PSym; n: PNode) = - if g.config.symbolFiles == disabledSf: return - var buf = newStringOfCap(160) - encodeNode(g, module.info, n, buf) - db.exec(sql"insert into toplevelstmts(module, position, data) values (?, ?, ?)", - abs(module.id), module.offset, buf) - inc module.offset - transitiveClosure(g) - -proc recordStmt*(g: ModuleGraph; module: PSym; n: PNode) = - storeNode(g, module, n) - -proc storeFilename(g: ModuleGraph; fullpath: AbsoluteFile; fileIdx: FileIndex) = - let id = db.getValue(sql"select id from filenames where fullpath = ?", fullpath.string) - if id.len == 0: - let fullhash = hashFileCached(g.config, fileIdx, fullpath) - db.exec(sql"insert into filenames(nimid, fullpath, fullhash) values (?, ?, ?)", - int(fileIdx), fullpath.string, fullhash) - -proc storeRemaining*(g: ModuleGraph; module: PSym) = - if g.config.symbolFiles == disabledSf: return - var stillForwarded: seq[PSym] = @[] - for s in w.forwardedSyms: - if sfForward notin s.flags: - storeSym(g, s) - else: - stillForwarded.add s - swap w.forwardedSyms, stillForwarded - transitiveClosure(g) - var nimid = 0 - for x in items(g.config.m.fileInfos): - storeFilename(g, x.fullPath, FileIndex(nimid)) - inc nimid - -# ---------------- decoder ----------------------------------- - -type - BlobReader = object - s: string - pos: int - -using - b: var BlobReader - g: ModuleGraph - -proc loadSym(g; id: int, info: TLineInfo): PSym -proc loadType(g; id: int, info: TLineInfo): PType - -proc decodeLineInfo(g; b; info: var TLineInfo) = - if b.s[b.pos] == '?': - inc(b.pos) - if b.s[b.pos] == ',': info.col = -1'i16 - else: info.col = int16(decodeVInt(b.s, b.pos)) - if b.s[b.pos] == ',': - inc(b.pos) - if b.s[b.pos] == ',': info.line = 0'u16 - else: info.line = uint16(decodeVInt(b.s, b.pos)) - if b.s[b.pos] == ',': - inc(b.pos) - #info.fileIndex = fromDbFileId(g.incr, g.config, decodeVInt(b.s, b.pos)) - info.fileIndex = FileIndex decodeVInt(b.s, b.pos) - -proc skipNode(b) = - # ')' itself cannot be part of a string literal so that this is correct. - assert b.s[b.pos] == '(' - var par = 0 - var pos = b.pos+1 - while true: - case b.s[pos] - of ')': - if par == 0: break - dec par - of '(': inc par - else: discard - inc pos - b.pos = pos+1 # skip ')' - -proc decodeNodeLazyBody(g; b; fInfo: TLineInfo, - belongsTo: PSym): PNode = - result = nil - if b.s[b.pos] == '(': - inc(b.pos) - if b.s[b.pos] == ')': - inc(b.pos) - return # nil node - result = newNodeI(TNodeKind(decodeVInt(b.s, b.pos)), fInfo) - decodeLineInfo(g, b, result.info) - if b.s[b.pos] == '$': - inc(b.pos) - result.flags = cast[TNodeFlags](int32(decodeVInt(b.s, b.pos))) - if b.s[b.pos] == '^': - inc(b.pos) - var id = decodeVInt(b.s, b.pos) - result.typ = loadType(g, id, result.info) - case result.kind - of nkCharLit..nkUInt64Lit: - if b.s[b.pos] == '!': - inc(b.pos) - result.intVal = decodeVBiggestInt(b.s, b.pos) - of nkFloatLit..nkFloat64Lit: - if b.s[b.pos] == '!': - inc(b.pos) - var fl = decodeStr(b.s, b.pos) - result.floatVal = parseFloat(fl) - of nkStrLit..nkTripleStrLit: - if b.s[b.pos] == '!': - inc(b.pos) - result.strVal = decodeStr(b.s, b.pos) - else: - result.strVal = "" - of nkIdent: - if b.s[b.pos] == '!': - inc(b.pos) - var fl = decodeStr(b.s, b.pos) - result.ident = g.cache.getIdent(fl) - else: - internalError(g.config, result.info, "decodeNode: nkIdent") - of nkSym: - if b.s[b.pos] == '!': - inc(b.pos) - var id = decodeVInt(b.s, b.pos) - result.sym = loadSym(g, id, result.info) - else: - internalError(g.config, result.info, "decodeNode: nkSym") - else: - var i = 0 - while b.s[b.pos] != ')': - when false: - if belongsTo != nil and i == bodyPos: - addSonNilAllowed(result, nil) - belongsTo.offset = b.pos - skipNode(b) - else: - discard - addSonNilAllowed(result, decodeNodeLazyBody(g, b, result.info, nil)) - inc i - if b.s[b.pos] == ')': inc(b.pos) - else: internalError(g.config, result.info, "decodeNode: ')' missing") - else: - internalError(g.config, fInfo, "decodeNode: '(' missing " & $b.pos) - -proc decodeNode(g; b; fInfo: TLineInfo): PNode = - result = decodeNodeLazyBody(g, b, fInfo, nil) - -proc decodeLoc(g; b; loc: var TLoc, info: TLineInfo) = - if b.s[b.pos] == '<': - inc(b.pos) - if b.s[b.pos] in {'0'..'9', 'a'..'z', 'A'..'Z'}: - loc.k = TLocKind(decodeVInt(b.s, b.pos)) - else: - loc.k = low(loc.k) - if b.s[b.pos] == '*': - inc(b.pos) - loc.storage = TStorageLoc(decodeVInt(b.s, b.pos)) - else: - loc.storage = low(loc.storage) - if b.s[b.pos] == '$': - inc(b.pos) - loc.flags = cast[TLocFlags](int32(decodeVInt(b.s, b.pos))) - else: - loc.flags = {} - if b.s[b.pos] == '^': - inc(b.pos) - loc.lode = decodeNode(g, b, info) - # rrGetType(b, decodeVInt(b.s, b.pos), info) - else: - loc.lode = nil - if b.s[b.pos] == '!': - inc(b.pos) - loc.r = rope(decodeStr(b.s, b.pos)) - else: - loc.r = nil - if b.s[b.pos] == '>': inc(b.pos) - else: internalError(g.config, info, "decodeLoc " & b.s[b.pos]) - -proc loadBlob(g; query: SqlQuery; id: int): BlobReader = - let blob = db.getValue(query, id) - if blob.len == 0: - internalError(g.config, "symbolfiles: cannot find ID " & $ id) - result = BlobReader(pos: 0) - shallowCopy(result.s, blob) - # ensure we can read without index checks: - result.s.add '\0' - -proc loadType(g; id: int; info: TLineInfo): PType = - result = g.incr.r.types.getOrDefault(id) - if result != nil: return result - var b = loadBlob(g, sql"select data from types where nimid = ?", id) - - if b.s[b.pos] == '[': - inc(b.pos) - if b.s[b.pos] == ']': - inc(b.pos) - return # nil type - new(result) - result.kind = TTypeKind(decodeVInt(b.s, b.pos)) - if b.s[b.pos] == '+': - inc(b.pos) - result.uniqueId = decodeVInt(b.s, b.pos) - setId(result.uniqueId) - #if debugIds: registerID(result) - else: - internalError(g.config, info, "decodeType: no id") - if b.s[b.pos] == '+': - inc(b.pos) - result.id = decodeVInt(b.s, b.pos) - else: - result.id = result.uniqueId - # here this also avoids endless recursion for recursive type - g.incr.r.types.add(result.uniqueId, result) - if b.s[b.pos] == '(': result.n = decodeNode(g, b, unknownLineInfo) - if b.s[b.pos] == '$': - inc(b.pos) - result.flags = cast[TTypeFlags](int32(decodeVInt(b.s, b.pos))) - if b.s[b.pos] == '?': - inc(b.pos) - result.callConv = TCallingConvention(decodeVInt(b.s, b.pos)) - if b.s[b.pos] == '*': - inc(b.pos) - result.owner = loadSym(g, decodeVInt(b.s, b.pos), info) - if b.s[b.pos] == '&': - inc(b.pos) - result.sym = loadSym(g, decodeVInt(b.s, b.pos), info) - if b.s[b.pos] == '/': - inc(b.pos) - result.size = decodeVInt(b.s, b.pos) - else: - result.size = -1 - if b.s[b.pos] == '=': - inc(b.pos) - result.align = decodeVInt(b.s, b.pos).int16 - else: - result.align = 2 - - if b.s[b.pos] == '\14': - inc(b.pos) - result.lockLevel = decodeVInt(b.s, b.pos).TLockLevel - else: - result.lockLevel = UnspecifiedLockLevel - - if b.s[b.pos] == '\15': - inc(b.pos) - result.paddingAtEnd = decodeVInt(b.s, b.pos).int16 - - for a in low(result.attachedOps)..high(result.attachedOps): - if b.s[b.pos] == '\16': - inc(b.pos) - let id = decodeVInt(b.s, b.pos) - if id >= 0: - result.attachedOps[a] = loadSym(g, id, info) - - while b.s[b.pos] == '\19': - inc(b.pos) - let x = decodeVInt(b.s, b.pos) - doAssert b.s[b.pos] == '\20' - inc(b.pos) - let y = loadSym(g, decodeVInt(b.s, b.pos), info) - result.methods.add((x, y)) - decodeLoc(g, b, result.loc, info) - if b.s[b.pos] == '\21': - inc(b.pos) - let d = decodeVInt(b.s, b.pos) - result.typeInst = loadType(g, d, info) - while b.s[b.pos] == '^': - inc(b.pos) - if b.s[b.pos] == '(': - inc(b.pos) - if b.s[b.pos] == ')': inc(b.pos) - else: internalError(g.config, info, "decodeType ^(" & b.s[b.pos]) - rawAddSon(result, nil) - else: - let d = decodeVInt(b.s, b.pos) - result.sons.add loadType(g, d, info) - -proc decodeLib(g; b; info: TLineInfo): PLib = - result = nil - if b.s[b.pos] == '|': - new(result) - inc(b.pos) - result.kind = TLibKind(decodeVInt(b.s, b.pos)) - if b.s[b.pos] != '|': internalError(g.config, "decodeLib: 1") - inc(b.pos) - result.name = rope(decodeStr(b.s, b.pos)) - if b.s[b.pos] != '|': internalError(g.config, "decodeLib: 2") - inc(b.pos) - result.path = decodeNode(g, b, info) - -proc decodeInstantiations(g; b; info: TLineInfo; - s: var seq[PInstantiation]) = - while b.s[b.pos] == '\15': - inc(b.pos) - var ii: PInstantiation - new ii - ii.sym = loadSym(g, decodeVInt(b.s, b.pos), info) - ii.concreteTypes = @[] - while b.s[b.pos] == '\17': - inc(b.pos) - ii.concreteTypes.add loadType(g, decodeVInt(b.s, b.pos), info) - if b.s[b.pos] == '\20': - inc(b.pos) - ii.compilesId = decodeVInt(b.s, b.pos) - s.add ii - -proc loadSymFromBlob(g; b; info: TLineInfo): PSym = - if b.s[b.pos] == '{': - inc(b.pos) - if b.s[b.pos] == '}': - inc(b.pos) - return # nil sym - var k = TSymKind(decodeVInt(b.s, b.pos)) - var id: int - if b.s[b.pos] == '+': - inc(b.pos) - id = decodeVInt(b.s, b.pos) - setId(id) - else: - internalError(g.config, info, "decodeSym: no id") - var ident: PIdent - if b.s[b.pos] == '&': - inc(b.pos) - ident = g.cache.getIdent(decodeStr(b.s, b.pos)) - else: - internalError(g.config, info, "decodeSym: no ident") - #echo "decoding: {", ident.s - result = PSym(id: id, kind: k, name: ident) - # read the rest of the symbol description: - g.incr.r.syms.add(result.id, result) - if b.s[b.pos] == '^': - inc(b.pos) - result.typ = loadType(g, decodeVInt(b.s, b.pos), info) - decodeLineInfo(g, b, result.info) - if b.s[b.pos] == '*': - inc(b.pos) - result.owner = loadSym(g, decodeVInt(b.s, b.pos), result.info) - if b.s[b.pos] == '$': - inc(b.pos) - result.flags = cast[TSymFlags](decodeVBiggestInt(b.s, b.pos)) - if b.s[b.pos] == '@': - inc(b.pos) - result.magic = TMagic(decodeVInt(b.s, b.pos)) - if b.s[b.pos] == '!': - inc(b.pos) - result.options = cast[TOptions](int32(decodeVInt(b.s, b.pos))) - if b.s[b.pos] == '%': - inc(b.pos) - result.position = decodeVInt(b.s, b.pos) - if b.s[b.pos] == '`': - inc(b.pos) - result.offset = decodeVInt(b.s, b.pos) - else: - result.offset = -1 - decodeLoc(g, b, result.loc, result.info) - result.annex = decodeLib(g, b, info) - if b.s[b.pos] == '#': - inc(b.pos) - result.constraint = decodeNode(g, b, unknownLineInfo) - case result.kind - of skType, skGenericParam: - while b.s[b.pos] == '\14': - inc(b.pos) - result.typeInstCache.add loadType(g, decodeVInt(b.s, b.pos), result.info) - of routineKinds: - decodeInstantiations(g, b, result.info, result.procInstCache) - if b.s[b.pos] == '\16': - inc(b.pos) - result.gcUnsafetyReason = loadSym(g, decodeVInt(b.s, b.pos), result.info) - if b.s[b.pos] == '\24': - inc b.pos - result.transformedBody = decodeNode(g, b, result.info) - #result.transformedBody = nil - of skModule, skPackage: - decodeInstantiations(g, b, result.info, result.usedGenerics) - of skLet, skVar, skField, skForVar: - if b.s[b.pos] == '\18': - inc(b.pos) - result.guard = loadSym(g, decodeVInt(b.s, b.pos), result.info) - if b.s[b.pos] == '\19': - inc(b.pos) - result.bitsize = decodeVInt(b.s, b.pos).int16 - else: discard - - if b.s[b.pos] == '(': - #if result.kind in routineKinds: - # result.ast = nil - #else: - result.ast = decodeNode(g, b, result.info) - if sfCompilerProc in result.flags: - registerCompilerProc(g, result) - #echo "loading ", result.name.s - -proc loadSym(g; id: int; info: TLineInfo): PSym = - result = g.incr.r.syms.getOrDefault(id) - if result != nil: return result - var b = loadBlob(g, sql"select data from syms where nimid = ?", id) - result = loadSymFromBlob(g, b, info) - doAssert id == result.id, "symbol ID is not consistent!" - -proc registerModule*(g; module: PSym) = - g.incr.r.syms.add(abs module.id, module) - -proc loadModuleSymTab(g; module: PSym) = - ## goal: fill module.tab - g.incr.r.syms.add(module.id, module) - for row in db.fastRows(sql"select nimid, data from syms where module = ? and exported = 1", abs(module.id)): - let id = parseInt(row[0]) - var s = g.incr.r.syms.getOrDefault(id) - if s == nil: - var b = BlobReader(pos: 0) - shallowCopy(b.s, row[1]) - # ensure we can read without index checks: - b.s.add '\0' - s = loadSymFromBlob(g, b, module.info) - assert s != nil - if s.kind != skField: - strTableAdd(module.tab, s) - if sfSystemModule in module.flags: - g.systemModule = module - -proc replay(g: ModuleGraph; module: PSym; n: PNode) = - # XXX check if we need to replay nkStaticStmt here. - case n.kind - #of nkStaticStmt: - #evalStaticStmt(module, g, n[0], module) - #of nkVarSection, nkLetSection: - # nkVarSections are already covered by the vmgen which produces nkStaticStmt - of nkMethodDef: - methodDef(g, n[namePos].sym, fromCache=true) - of nkCommentStmt: - # pragmas are complex and can be user-overriden via templates. So - # instead of using the original ``nkPragma`` nodes, we rely on the - # fact that pragmas.nim was patched to produce specialized recorded - # statements for us in the form of ``nkCommentStmt`` with (key, value) - # pairs. Ordinary nkCommentStmt nodes never have children so this is - # not ambiguous. - # Fortunately only a tiny subset of the available pragmas need to - # be replayed here. This is always a subset of ``pragmas.stmtPragmas``. - if n.len >= 2: - internalAssert g.config, n[0].kind == nkStrLit and n[1].kind == nkStrLit - case n[0].strVal - of "hint": message(g.config, n.info, hintUser, n[1].strVal) - of "warning": message(g.config, n.info, warnUser, n[1].strVal) - of "error": localError(g.config, n.info, errUser, n[1].strVal) - of "compile": - internalAssert g.config, n.len == 4 and n[2].kind == nkStrLit - let cname = AbsoluteFile n[1].strVal - var cf = Cfile(nimname: splitFile(cname).name, cname: cname, - obj: AbsoluteFile n[2].strVal, - flags: {CfileFlag.External}, - customArgs: n[3].strVal) - extccomp.addExternalFileToCompile(g.config, cf) - of "link": - extccomp.addExternalFileToLink(g.config, AbsoluteFile n[1].strVal) - of "passl": - extccomp.addLinkOption(g.config, n[1].strVal) - of "passc": - extccomp.addCompileOption(g.config, n[1].strVal) - of "localpassc": - extccomp.addLocalCompileOption(g.config, n[1].strVal, toFullPathConsiderDirty(g.config, module.info.fileIndex)) - of "cppdefine": - options.cppDefine(g.config, n[1].strVal) - of "inc": - let destKey = n[1].strVal - let by = n[2].intVal - let v = getOrDefault(g.cacheCounters, destKey) - g.cacheCounters[destKey] = v+by - of "put": - let destKey = n[1].strVal - let key = n[2].strVal - let val = n[3] - if not contains(g.cacheTables, destKey): - g.cacheTables[destKey] = initBTree[string, PNode]() - if not contains(g.cacheTables[destKey], key): - g.cacheTables[destKey].add(key, val) - else: - internalError(g.config, n.info, "key already exists: " & key) - of "incl": - let destKey = n[1].strVal - let val = n[2] - if not contains(g.cacheSeqs, destKey): - g.cacheSeqs[destKey] = newTree(nkStmtList, val) - else: - block search: - for existing in g.cacheSeqs[destKey]: - if exprStructuralEquivalent(existing, val, strictSymEquality=true): - break search - g.cacheSeqs[destKey].add val - of "add": - let destKey = n[1].strVal - let val = n[2] - if not contains(g.cacheSeqs, destKey): - g.cacheSeqs[destKey] = newTree(nkStmtList, val) - else: - g.cacheSeqs[destKey].add val - else: - internalAssert g.config, false - of nkImportStmt: - for x in n: - internalAssert g.config, x.kind == nkSym - let modpath = AbsoluteFile toFullPath(g.config, x.sym.info) - let imported = g.importModuleCallback(g, module, fileInfoIdx(g.config, modpath)) - internalAssert g.config, imported.id < 0 - of nkStmtList, nkStmtListExpr: - for x in n: replay(g, module, x) - of nkExportStmt: - for x in n: - doAssert x.kind == nkSym - strTableAdd(module.tab, x.sym) - else: discard "nothing to do for this node" - -proc loadNode*(g: ModuleGraph; module: PSym): PNode = - loadModuleSymTab(g, module) - result = newNodeI(nkStmtList, module.info) - for row in db.rows(sql"select data from toplevelstmts where module = ? order by position asc", - abs module.id): - var b = BlobReader(pos: 0) - # ensure we can read without index checks: - b.s = row[0] & '\0' - result.add decodeNode(g, b, module.info) - db.exec(sql"insert into controlblock(idgen) values (?)", gFrontEndId) - replay(g, module, result) - -proc setupModuleCache*(g: ModuleGraph) = - # historical note: there used to be a `rodfiles` dir with special tests - # for incremental compilation via symbol files. This was likely replaced by ic. - if g.config.symbolFiles == disabledSf: return - g.recordStmt = recordStmt - let dbfile = getNimcacheDir(g.config) / RelativeFile"rodfiles.db" - if g.config.symbolFiles == writeOnlySf: - removeFile(dbfile) - createDir getNimcacheDir(g.config) - let ec = encodeConfig(g) - if not fileExists(dbfile): - db = open(connection=string dbfile, user="nim", password="", - database="nim") - createDb(db) - db.exec(sql"insert into config(config) values (?)", ec) - else: - db = open(connection=string dbfile, user="nim", password="", - database="nim") - let oldConfig = db.getValue(sql"select config from config") - g.incr.configChanged = oldConfig != ec - # ensure the filename IDs stay consistent: - for row in db.rows(sql"select fullpath, nimid from filenames order by nimid"): - let id = fileInfoIdx(g.config, AbsoluteFile row[0]) - doAssert id.int == parseInt(row[1]) - db.exec(sql"update config set config = ?", ec) - db.exec(sql"pragma journal_mode=off") - # This MUST be turned off, otherwise it's way too slow even for testing purposes: - db.exec(sql"pragma SYNCHRONOUS=off") - db.exec(sql"pragma LOCKING_MODE=exclusive") - let lastId = db.getValue(sql"select max(idgen) from controlblock") - if lastId.len > 0: - idgen.setId(parseInt lastId) diff --git a/compiler/sem.nim b/compiler/sem.nim index e95f3799c..d5ae5a21d 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -16,7 +16,7 @@ import procfind, lookups, pragmas, passes, semdata, semtypinst, sigmatch, intsets, transf, vmdef, vm, aliases, cgmeth, lambdalifting, evaltempl, patterns, parampatterns, sempass2, linter, semmacrosanity, - lowerings, plugins/active, rod, lineinfos, strtabs, int128, + lowerings, plugins/active, lineinfos, strtabs, int128, isolation_check, typeallowed from modulegraphs import ModuleGraph, PPassContext, onUse, onDef, onDefResolveForward @@ -124,8 +124,8 @@ proc commonType*(c: PContext; x, y: PType): PType = # turn any concrete typedesc into the abstract typedesc type if a.len == 0: result = a else: - result = newType(tyTypeDesc, nextId(c.idgen), a.owner) - rawAddSon(result, newType(tyNone, nextId(c.idgen), a.owner)) + result = newType(tyTypeDesc, nextTypeId(c.idgen), a.owner) + rawAddSon(result, newType(tyNone, nextTypeId(c.idgen), a.owner)) elif b.kind in {tyArray, tySet, tySequence} and a.kind == b.kind: # check for seq[empty] vs. seq[int] @@ -137,7 +137,7 @@ proc commonType*(c: PContext; x, y: PType): PType = let aEmpty = isEmptyContainer(a[i]) let bEmpty = isEmptyContainer(b[i]) if aEmpty != bEmpty: - if nt.isNil: nt = copyType(a, nextId(c.idgen), a.owner) + if nt.isNil: nt = copyType(a, nextTypeId(c.idgen), a.owner) nt[i] = if aEmpty: b[i] else: a[i] if not nt.isNil: result = nt #elif b[idx].kind == tyEmpty: return x @@ -176,7 +176,7 @@ proc commonType*(c: PContext; x, y: PType): PType = # ill-formed AST, no need for additional tyRef/tyPtr if k != tyNone and x.kind != tyGenericInst: let r = result - result = newType(k, nextId(c.idgen), r.owner) + result = newType(k, nextTypeId(c.idgen), r.owner) result.addSonSkipIntLit(r, c.idgen) proc endsInNoReturn(n: PNode): bool = @@ -193,7 +193,7 @@ proc commonType*(c: PContext; x: PType, y: PNode): PType = commonType(c, x, y.typ) proc newSymS(kind: TSymKind, n: PNode, c: PContext): PSym = - result = newSym(kind, considerQuotedIdent(c, n), nextId c.idgen, getCurrOwner(c), n.info) + result = newSym(kind, considerQuotedIdent(c, n), nextSymId c.idgen, getCurrOwner(c), n.info) when defined(nimsuggest): suggestDecl(c, n, result) @@ -216,7 +216,7 @@ proc newSymG*(kind: TSymKind, n: PNode, c: PContext): PSym = # template; we must fix it here: see #909 result.owner = getCurrOwner(c) else: - result = newSym(kind, considerQuotedIdent(c, n), nextId c.idgen, getCurrOwner(c), n.info) + result = newSym(kind, considerQuotedIdent(c, n), nextSymId c.idgen, getCurrOwner(c), n.info) #if kind in {skForVar, skLet, skVar} and result.owner.kind == skModule: # incl(result.flags, sfGlobal) when defined(nimsuggest): @@ -255,7 +255,7 @@ proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, proc symFromType(c: PContext; t: PType, info: TLineInfo): PSym = if t.sym != nil: return t.sym - result = newSym(skType, getIdent(c.cache, "AnonType"), nextId c.idgen, t.owner, info) + result = newSym(skType, getIdent(c.cache, "AnonType"), nextSymId c.idgen, t.owner, info) result.flags.incl sfAnon result.typ = t @@ -616,7 +616,7 @@ proc myProcess(context: PPassContext, n: PNode): PNode {.nosinks.} = else: result = newNodeI(nkEmpty, n.info) #if c.config.cmd == cmdIdeTools: findSuggest(c, n) - rod.storeNode(c.graph, c.module, result) + storeRodNode(c, result) proc reportUnusedModules(c: PContext) = for i in 0..high(c.unusedImports): @@ -638,7 +638,7 @@ proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode = result.add(c.module.ast) popOwner(c) popProcCon(c) - storeRemaining(c.graph, c.module) + saveRodFile(c) const semPass* = makePass(myOpen, myProcess, myClose, isFrontend = true) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index a6660e14c..da38a6fc2 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -11,7 +11,9 @@ import intsets, options, ast, astalgo, msgs, idents, renderer, - magicsys, vmdef, modulegraphs, lineinfos, sets + magicsys, vmdef, modulegraphs, lineinfos, sets, pathutils + +import ic / to_packed_ast type TOptionEntry* = object # entries to put on a stack for pragma parsing @@ -77,9 +79,9 @@ type case mode*: ImportMode of importAll: discard of importSet: - imported*: IntSet + imported*: IntSet # of PIdent.id of importExcept: - exceptSet*: IntSet + exceptSet*: IntSet # of PIdent.id PContext* = ref TContext TContext* = object of TPassContext # a context represents the module @@ -140,6 +142,7 @@ type selfName*: PIdent cache*: IdentCache graph*: ModuleGraph + encoder*: PackedEncoder signatures*: TStrTable recursiveDep*: string suggestionsMade*: bool @@ -264,6 +267,16 @@ proc newContext*(graph: ModuleGraph; module: PSym): PContext = initStrTable(result.signatures) result.typesWithOps = @[] result.features = graph.config.features + if graph.config.symbolFiles != disabledSf: + initEncoder result.encoder, module, graph.config + +proc addIncludeFileDep*(c: PContext; f: FileIndex) = + if c.config.symbolFiles != disabledSf: + addIncludeFileDep(c.encoder, f) + +proc addImportFileDep*(c: PContext; f: FileIndex) = + if c.config.symbolFiles != disabledSf: + addImportFileDep(c.encoder, f) proc inclSym(sq: var seq[PSym], s: PSym) = for i in 0.. ord(high(TSymKind)): internalError(c.config, c.debug[pc], "request to create symbol of invalid kind") - var sym = newSym(k.TSymKind, getIdent(c.cache, name), nextId c.idgen, c.module.owner, c.debug[pc]) + var sym = newSym(k.TSymKind, getIdent(c.cache, name), nextSymId c.idgen, c.module.owner, c.debug[pc]) incl(sym.flags, sfGenSym) regs[ra].node = newSymNode(sym) regs[ra].node.flags.incl nfIsRef @@ -2257,7 +2257,7 @@ const evalMacroLimit = 1000 proc errorNode(idgen: IdGenerator; owner: PSym, n: PNode): PNode = result = newNodeI(nkEmpty, n.info) - result.typ = newType(tyError, nextId idgen, owner) + result.typ = newType(tyError, nextTypeId idgen, owner) result.typ.flags.incl tfCheckedForDestructor proc evalMacroCall*(module: PSym; idgen: IdGenerator; g: ModuleGraph; templInstCounter: ref int; diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim index 8083ae179..f8765c4dc 100644 --- a/compiler/vmdeps.nim +++ b/compiler/vmdeps.nim @@ -26,7 +26,7 @@ proc opSlurp*(file: string, info: TLineInfo, module: PSym; conf: ConfigRef): str proc atomicTypeX(cache: IdentCache; name: string; m: TMagic; t: PType; info: TLineInfo; idgen: IdGenerator): PNode = - let sym = newSym(skType, getIdent(cache, name), nextId(idgen), t.owner, info) + let sym = newSym(skType, getIdent(cache, name), nextSymId(idgen), t.owner, info) sym.magic = m sym.typ = t result = newSymNode(sym) @@ -47,7 +47,7 @@ proc mapTypeToBracketX(cache: IdentCache; name: string; m: TMagic; t: PType; inf for i in 0..= result.len: setLen(result.sons, pos + 1) let fieldNode = newNode(nkExprColonExpr) - fieldNode.add newSymNode(newSym(skField, ident, nextId(idgen), nil, unknownLineInfo)) + fieldNode.add newSymNode(newSym(skField, ident, nextSymId(idgen), nil, unknownLineInfo)) fieldNode.add loadAny(p, field.typ, tab, cache, conf, idgen) result[pos] = fieldNode if p.kind == jsonObjectEnd: next(p) diff --git a/lib/js/jsffi.nim b/lib/js/jsffi.nim index 9fe934f89..15727c9f7 100644 --- a/lib/js/jsffi.nim +++ b/lib/js/jsffi.nim @@ -455,9 +455,9 @@ macro `{}`*(typ: typedesc, xs: varargs[untyped]): auto = # from a proc, `this` being the first argument. proc replaceSyms(n: NimNode): NimNode = - if n.kind == nnkSym: + if n.kind == nnkSym: result = newIdentNode($n) - else: + else: result = n for i in 0.. Date: Sat, 2 Jan 2021 04:10:38 -0300 Subject: [PATCH 1257/3768] Add mimetypes.mimesLongest (#16480) * Allow single alloc mimetypes ops * Allow single alloc mimetypes ops * Update lib/pure/mimetypes.nim Co-authored-by: flywind <43030857+xflywind@users.noreply.github.com> * https://github.com/nim-lang/Nim/pull/16480#issuecomment-752630190 * https://github.com/nim-lang/Nim/pull/16480#issuecomment-752630190 * https://github.com/nim-lang/Nim/pull/16480#issuecomment-753349661 * update changelog Co-authored-by: flywind <43030857+xflywind@users.noreply.github.com> --- changelog.md | 3 +++ lib/pure/mimetypes.nim | 29 ++++++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index c6a1d17d9..b10b9607a 100644 --- a/changelog.md +++ b/changelog.md @@ -81,6 +81,9 @@ - Added `httpcore.is1xx` and missing HTTP codes. - Added `jsconsole.jsAssert` for JavaScript target. +- Added `mimetypes.mimesExtMaxLen` thats equal to the length of the longest "ext" from `mimes`. +- Added `mimetypes.mimesMaxLen` thats equal to the length of the longest "mime" from `mimes`. + ## Language changes diff --git a/lib/pure/mimetypes.nim b/lib/pure/mimetypes.nim index 206783929..880ccdff1 100644 --- a/lib/pure/mimetypes.nim +++ b/lib/pure/mimetypes.nim @@ -8,7 +8,7 @@ # ## This module implements a mimetypes database -import strtabs +import strtabs, std/private/since from strutils import startsWith, toLowerAscii, strip type @@ -1917,6 +1917,33 @@ func register*(mimedb: var MimeDB, ext: string, mimetype: string) = {.noSideEffect.}: mimedb.mimes[ext.toLowerAscii()] = mimetype.toLowerAscii() + +since (1, 5): + func mimesLongest(): array[2, int] {.compiletime.} = + runnableExamples: + static: + doAssert mimesLongest() >= (ext: 24, mime: 73) + var currentKeyLength, currentValLength: int + for item in mimes: + currentKeyLength = item[0].len + currentValLength = item[1].len + if currentKeyLength > result[0]: result[0] = currentKeyLength + if currentValLength > result[1]: result[1] = currentValLength + + const + ctValue = mimesLongest() # Use 2 const instead of func, save tuple unpack. + mimesExtMaxLen*: int = ctValue[0] ## \ + ## The length of the longest "ext" from `mimes`, + ## this is useful for optimizations with `newStringOfCap` and `newString`. + mimesMaxLen*: int = ctValue[1] ## \ + ## The length of the longest "mime" from `mimes`, + ## this is useful for optimizations with `newStringOfCap` and `newString`. + ## + ## See also: + ## * `newStringOfCap `_ + ## * `newString `_ + + runnableExamples: static: block: From 854ff26ac5a140b2f2cb509cef5a7aac551f6c34 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 2 Jan 2021 01:33:59 -0800 Subject: [PATCH 1258/3768] fix #16206, `nim r / nim -r` recompiles if cwd changes (#16349) --- compiler/extccomp.nim | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 14b961ee1..87b38092e 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -1026,6 +1026,8 @@ proc writeJsonBuildInstructions*(conf: ConfigRef) = lit $(%* conf.projectIsCmd) lit ",\L\"cmdInput\": " lit $(%* conf.cmdInput) + lit ",\L\"currentDir\": " + lit $(%* getCurrentDir()) if optRun in conf.globalOptions or isDefined(conf, "nimBetterRun"): lit ",\L\"cmdline\": " @@ -1046,14 +1048,23 @@ proc changeDetectedViaJsonBuildInstructions*(conf: ConfigRef; projectfile: Absol result = false try: let data = json.parseFile(jsonFile.string) - if not data.hasKey("depfiles") or not data.hasKey("cmdline"): + for key in "depfiles cmdline stdinInput currentDir".split: + if not data.hasKey(key): return true + if getCurrentDir() != data["currentDir"].getStr: + # fixes bug #16271 + # Note that simply comparing `expandFilename(projectFile)` would + # not be sufficient in case other flags depend implicitly on `getCurrentDir`, + # and would require much more care. Simply re-compiling is safer for now. + # A better strategy for future work would be to cache (with an LRU cache) + # the N most recent unique build instructions, as done with `rdmd`, + # which is both robust and avoids recompilation when switching back and forth + # between projects, see https://github.com/timotheecour/Nim/issues/199 return true let oldCmdLine = data["cmdline"].getStr if conf.commandLine != oldCmdLine: return true if hashNimExe() != data["nimexe"].getStr: return true - if not data.hasKey("stdinInput"): return true let stdinInput = data["stdinInput"].getBool let projectIsCmd = data["projectIsCmd"].getBool if conf.projectIsStdin or stdinInput: From d8b1ffc85733a2189a91deafe00d67af690028de Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sat, 2 Jan 2021 07:32:37 -0600 Subject: [PATCH 1259/3768] fix #16542 (#16549) * fix #16542 --- lib/pure/hashes.nim | 23 +++++++++++++++++++++-- tests/stdlib/thashes.nim | 31 ++++++++++++++++++++++++++++++- 2 files changed, 51 insertions(+), 3 deletions(-) diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim index 667f27e95..da72f2fab 100644 --- a/lib/pure/hashes.nim +++ b/lib/pure/hashes.nim @@ -195,10 +195,29 @@ else: ## Efficient hashing of integers. hashWangYi1(uint64(ord(x))) +when defined(js): + proc asBigInt(x: float): int64 = + # result is a `BigInt` type in js, but we cheat the type system + # and say it is a `int64` type. + # TODO refactor it using bigInt once jsBigInt is ready, pending pr #1640 + asm """ + const buffer = new ArrayBuffer(8); + const floatBuffer = new Float64Array(buffer); + const uintBuffer = new BigUint64Array(buffer); + floatBuffer[0] = `x`; + `result` = uintBuffer[0];""" + proc hash*(x: float): Hash {.inline.} = ## Efficient hashing of floats. - var y = x + 0.0 # for denormalization - result = hash(cast[ptr Hash](addr(y))[]) + let y = x + 0.0 # for denormalization + when nimvm: + # workaround a JS VM bug: bug #16547 + result = hashWangYi1(cast[int64](float64(y))) + else: + when not defined(js): + result = hashWangYi1(cast[Hash](y)) + else: + result = hashWangYi1(asBigInt(y)) # Forward declarations before methods that hash containers. This allows # containers to contain other containers diff --git a/tests/stdlib/thashes.nim b/tests/stdlib/thashes.nim index 520b27e26..9c9287784 100644 --- a/tests/stdlib/thashes.nim +++ b/tests/stdlib/thashes.nim @@ -2,7 +2,7 @@ discard """ targets: "c cpp js" """ -import hashes +import std/hashes block hashes: block hashing: @@ -75,3 +75,32 @@ block largeSize: # longer than 4 characters doAssert hash(xx) == hash(ssl, 0, 4) doAssert hash(xx, 0, 3) == hash(xxl, 0, 3) doAssert hash(xx, 0, 3) == hash(ssl, 0, 3) + +proc main() = + doAssert hash(0.0) == hash(0) + when sizeof(int) == 8: + block: + var s: seq[Hash] + for a in [0.0, 1.0, -1.0, 1000.0, -1000.0]: + let b = hash(a) + doAssert b notin s + s.add b + when defined(js): + doAssert hash(0.345602) == 2035867618 + doAssert hash(234567.45) == -20468103 + doAssert hash(-9999.283456) == -43247422 + doAssert hash(84375674.0) == 707542256 + else: + doAssert hash(0.345602) == 387936373221941218 + doAssert hash(234567.45) == -8179139172229468551 + doAssert hash(-9999.283456) == 5876943921626224834 + doAssert hash(84375674.0) == 1964453089107524848 + else: + doAssert hash(0.345602) != 0 + doAssert hash(234567.45) != 0 + doAssert hash(-9999.283456) != 0 + doAssert hash(84375674.0) != 0 + + +static: main() +main() From b8775bff575fb6860d43806b8070b904229927bf Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sat, 2 Jan 2021 10:11:46 -0600 Subject: [PATCH 1260/3768] fix `is "closure"` (#16552) --- compiler/semexprs.nim | 3 +-- tests/stdlib/thashes.nim | 9 +++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 9eccdef45..1e5772189 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -408,8 +408,7 @@ proc isOpImpl(c: PContext, n: PNode, flags: TExprFlags): PNode = of "closure": let t = skipTypes(t1, abstractRange) res = t.kind == tyProc and - t.callConv == ccClosure and - tfIterator notin t.flags + t.callConv == ccClosure of "iterator": let t = skipTypes(t1, abstractRange) res = t.kind == tyProc and diff --git a/tests/stdlib/thashes.nim b/tests/stdlib/thashes.nim index 9c9287784..b8a2c2c6a 100644 --- a/tests/stdlib/thashes.nim +++ b/tests/stdlib/thashes.nim @@ -4,6 +4,15 @@ discard """ import std/hashes + +when not defined(js) and not defined(cpp): + block: + var x = 12 + iterator hello(): int {.closure.} = + yield x + + discard hash(hello) + block hashes: block hashing: var dummy = 0.0 From e869767aa72a71e673c2e8fdc51925f28c13d432 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sat, 2 Jan 2021 10:13:01 -0600 Subject: [PATCH 1261/3768] fix #16061 (#16551) --- lib/pure/hashes.nim | 14 ++++++++++---- tests/stdlib/thashes.nim | 7 ++++++- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim index da72f2fab..dde4be128 100644 --- a/lib/pure/hashes.nim +++ b/lib/pure/hashes.nim @@ -297,6 +297,9 @@ proc murmurHash(x: openArray[byte]): Hash = h1 = h1 xor (h1 shr 16) return cast[Hash](h1) +proc hashVmImpl(x: cstring, sPos, ePos: int): Hash = + doAssert false, "implementation override in compiler/vmops.nim" + proc hashVmImpl(x: string, sPos, ePos: int): Hash = doAssert false, "implementation override in compiler/vmops.nim" @@ -341,11 +344,14 @@ proc hash*(x: cstring): Hash = inc i result = !$result else: - when not defined(js) and defined(nimToOpenArrayCString): - murmurHash(toOpenArrayByte(x, 0, x.high)) + when nimvm: + hashVmImpl(x, 0, high(x)) else: - let xx = $x - murmurHash(toOpenArrayByte(xx, 0, high(xx))) + when not defined(js) and defined(nimToOpenArrayCString): + murmurHash(toOpenArrayByte(x, 0, x.high)) + else: + let xx = $x + murmurHash(toOpenArrayByte(xx, 0, high(xx))) proc hash*(sBuf: string, sPos, ePos: int): Hash = ## Efficient hashing of a string buffer, from starting diff --git a/tests/stdlib/thashes.nim b/tests/stdlib/thashes.nim index b8a2c2c6a..17640387a 100644 --- a/tests/stdlib/thashes.nim +++ b/tests/stdlib/thashes.nim @@ -86,8 +86,13 @@ block largeSize: # longer than 4 characters doAssert hash(xx, 0, 3) == hash(ssl, 0, 3) proc main() = + + doAssert hash(0.0) == hash(0) - when sizeof(int) == 8: + doAssert hash(cstring"abracadabra") == 97309975 + doAssert hash(cstring"abracadabra") == hash("abracadabra") + + when sizeof(int) == 8 or defined(js): block: var s: seq[Hash] for a in [0.0, 1.0, -1.0, 1000.0, -1000.0]: From 2eccef7ad6dd2941bcc78692b499b4cb269e9a2a Mon Sep 17 00:00:00 2001 From: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Date: Sat, 2 Jan 2021 20:28:59 +0100 Subject: [PATCH 1262/3768] Algorithm improvements (#16529) * Improve documentation for algorithm Remove unused import in algorithm tests Improve formatting * Reapply fix for reverse on empty openArray * Use 3rd person singular Add more explanations. --- lib/pure/algorithm.nim | 276 +++++++++++++++++++----------------- tests/stdlib/talgorithm.nim | 4 +- 2 files changed, 144 insertions(+), 136 deletions(-) diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim index f2e4848df..029e9abf8 100644 --- a/lib/pure/algorithm.nim +++ b/lib/pure/algorithm.nim @@ -7,11 +7,11 @@ # distribution, for details about the copyright. # -## This module implements some common generic algorithms. +## This module implements some common generic algorithms on `openArray`s. ## ## Basic usage ## =========== -## +## runnableExamples: type People = tuple @@ -30,9 +30,7 @@ runnableExamples: (year: 2010, name: "Jane")] proc myCmp(x, y: People): int = - if x.name < y.name: -1 - elif x.name == y.name: 0 - else: 1 + cmp(x.name, y.name) # Sorting with custom proc a.sort(myCmp) @@ -49,18 +47,18 @@ type Descending, Ascending proc `*`*(x: int, order: SortOrder): int {.inline.} = - ## Flips ``x`` if ``order == Descending``. - ## If ``order == Ascending`` then ``x`` is returned. + ## Flips the sign of `x` if `order == Descending`. + ## If `order == Ascending` then `x` is returned. ## - ## ``x`` is supposed to be the result of a comparator, i.e. - ## | ``< 0`` for *less than*, - ## | ``== 0`` for *equal*, - ## | ``> 0`` for *greater than*. + ## `x` is supposed to be the result of a comparator, i.e. + ## | `< 0` for *less than*, + ## | `== 0` for *equal*, + ## | `> 0` for *greater than*. runnableExamples: - assert `*`(-123, Descending) == 123 - assert `*`(123, Descending) == -123 - assert `*`(-123, Ascending) == -123 - assert `*`(123, Ascending) == 123 + assert -123 * Descending == 123 + assert 123 * Descending == -123 + assert -123 * Ascending == -123 + assert 123 * Ascending == 123 var y = order.ord - 1 result = (x xor y) - y @@ -71,9 +69,9 @@ template fillImpl[T](a: var openArray[T], first, last: int, value: T) = inc(x) proc fill*[T](a: var openArray[T], first, last: Natural, value: T) = - ## Fills the slice ``a[first..last]`` with ``value``. + ## Assigns `value` to all elements of the slice `a[first..last]`. ## - ## If an invalid range is passed, it raises IndexDefect. + ## If an invalid range is passed, it raises `IndexDefect`. runnableExamples: var a: array[6, int] a.fill(1, 3, 9) @@ -84,7 +82,7 @@ proc fill*[T](a: var openArray[T], first, last: Natural, value: T) = fillImpl(a, first, last, value) proc fill*[T](a: var openArray[T], value: T) = - ## Fills the container ``a`` with ``value``. + ## Assigns `value` to all elements of the container `a`. runnableExamples: var a: array[6, int] a.fill(9) @@ -95,13 +93,13 @@ proc fill*[T](a: var openArray[T], value: T) = proc reverse*[T](a: var openArray[T], first, last: Natural) = - ## Reverses the slice ``a[first..last]``. + ## Reverses the slice `a[first..last]`. ## - ## If an invalid range is passed, it raises IndexDefect. + ## If an invalid range is passed, it raises `IndexDefect`. ## ## **See also:** - ## * `reversed proc<#reversed,openArray[T],Natural,int>`_ reverse a slice and returns a ``seq[T]`` - ## * `reversed proc<#reversed,openArray[T]>`_ reverse and returns a ``seq[T]`` + ## * `reversed proc<#reversed,openArray[T],Natural,int>`_ reverse a slice and returns a `seq[T]` + ## * `reversed proc<#reversed,openArray[T]>`_ reverse and returns a `seq[T]` runnableExamples: var a = [1, 2, 3, 4, 5, 6] a.reverse(1, 3) @@ -117,23 +115,24 @@ proc reverse*[T](a: var openArray[T], first, last: Natural) = inc(x) proc reverse*[T](a: var openArray[T]) = - ## Reverses the contents of the container ``a``. + ## Reverses the contents of the container `a`. ## ## **See also:** - ## * `reversed proc<#reversed,openArray[T],Natural,int>`_ reverse a slice and returns a ``seq[T]`` - ## * `reversed proc<#reversed,openArray[T]>`_ reverse and returns a ``seq[T]`` + ## * `reversed proc<#reversed,openArray[T],Natural,int>`_ reverse a slice and returns a `seq[T]` + ## * `reversed proc<#reversed,openArray[T]>`_ reverse and returns a `seq[T]` runnableExamples: var a = [1, 2, 3, 4, 5, 6] a.reverse() assert a == [6, 5, 4, 3, 2, 1] a.reverse() assert a == [1, 2, 3, 4, 5, 6] + # the max is needed, since a.high is -1 if a is empty reverse(a, 0, max(0, a.high)) proc reversed*[T](a: openArray[T], first: Natural, last: int): seq[T] = - ## Returns the reverse of the slice ``a[first..last]``. + ## Returns the reverse of the slice `a[first..last]`. ## - ## If an invalid range is passed, it raises IndexDefect. + ## If an invalid range is passed, it raises `IndexDefect`. ## ## **See also:** ## * `reverse proc<#reverse,openArray[T],Natural,Natural>`_ reverse a slice @@ -143,7 +142,7 @@ proc reversed*[T](a: openArray[T], first: Natural, last: int): seq[T] = a = [1, 2, 3, 4, 5, 6] b = a.reversed(1, 3) assert b == @[4, 3, 2] - assert last >= first-1 + assert last >= first - 1 var i = last - first var x = first.int result = newSeq[T](i + 1) @@ -153,7 +152,7 @@ proc reversed*[T](a: openArray[T], first: Natural, last: int): seq[T] = inc(x) proc reversed*[T](a: openArray[T]): seq[T] = - ## Returns the reverse of the container ``a``. + ## Returns the reverse of the container `a`. ## ## **See also:** ## * `reverse proc<#reverse,openArray[T],Natural,Natural>`_ reverse a slice @@ -166,19 +165,20 @@ proc reversed*[T](a: openArray[T]): seq[T] = reversed(a, 0, a.high) proc binarySearch*[T, K](a: openArray[T], key: K, - cmp: proc (x: T, y: K): int {.closure.}): int = - ## Binary search for ``key`` in ``a``. Returns -1 if not found. + cmp: proc (x: T, y: K): int {.closure.}): int = + ## Binary search for `key` in `a`. Return the index of `key` or -1 if not found. + ## Assumes that `a` is sorted according to `cmp`. ## - ## ``cmp`` is the comparator function to use, the expected return values are - ## the same as that of system.cmp. + ## `cmp` is the comparator function to use, the expected return values are + ## the same as those of system.cmp. runnableExamples: assert binarySearch(["a", "b", "c", "d"], "d", system.cmp[string]) == 3 - assert binarySearch(["a", "b", "d", "c"], "d", system.cmp[string]) == 2 - if a.len == 0: - return -1 - + assert binarySearch(["a", "b", "c", "d"], "c", system.cmp[string]) == 2 let len = a.len + if len == 0: + return -1 + if len == 1: if cmp(a[0], key) == 0: return 0 @@ -196,7 +196,7 @@ proc binarySearch*[T, K](a: openArray[T], key: K, if cmpRes == 0: return i - if cmpRes < 1: + if cmpRes < 0: result = i step = step shr 1 if cmp(a[result], key) != 0: result = -1 @@ -216,30 +216,32 @@ proc binarySearch*[T, K](a: openArray[T], key: K, if result >= len or cmp(a[result], key) != 0: result = -1 proc binarySearch*[T](a: openArray[T], key: T): int = - ## Binary search for ``key`` in ``a``. Returns -1 if not found. + ## Binary search for `key` in `a`. Return the index of `key` or -1 if not found. + ## Assumes that `a` is sorted. runnableExamples: assert binarySearch([0, 1, 2, 3, 4], 4) == 4 - assert binarySearch([0, 1, 4, 2, 3], 4) == 2 + assert binarySearch([0, 1, 2, 3, 4], 2) == 2 binarySearch(a, key, cmp[T]) const onlySafeCode = true -proc lowerBound*[T, K](a: openArray[T], key: K, cmp: proc(x: T, k: K): int {. - closure.}): int = - ## Returns a position to the first element in the ``a`` that is greater than - ## ``key``, or last if no such element is found. +proc lowerBound*[T, K](a: openArray[T], key: K, + cmp: proc(x: T, k: K): int {.closure.}): int = + ## Returns the index of the first element in `a` that is not less than + ## (i.e. greater or equal to) `key`, or last if no such element is found. ## In other words if you have a sorted sequence and you call - ## ``insert(thing, elm, lowerBound(thing, elm))`` + ## `insert(thing, elm, lowerBound(thing, elm))` ## the sequence will still be sorted. + ## Assumes that `a` is sorted according to `cmp`. ## - ## If an invalid range is passed, it raises IndexDefect. + ## If an invalid range is passed, it raises `IndexDefect`. ## - ## The version uses ``cmp`` to compare the elements. - ## The expected return values are the same as that of ``system.cmp``. + ## This version uses `cmp` to compare the elements. + ## The expected return values are the same as those of `system.cmp`. ## ## **See also:** - ## * `upperBound proc<#upperBound,openArray[T],K,proc(T,K)>`_ sorted by ``cmp`` in the specified order + ## * `upperBound proc<#upperBound,openArray[T],K,proc(T,K)>`_ sorted by `cmp` in the specified order ## * `upperBound proc<#upperBound,openArray[T],T>`_ runnableExamples: var arr = @[1, 2, 3, 5, 6, 7, 8, 9] @@ -261,33 +263,35 @@ proc lowerBound*[T, K](a: openArray[T], key: K, cmp: proc(x: T, k: K): int {. count = step proc lowerBound*[T](a: openArray[T], key: T): int = lowerBound(a, key, cmp[T]) - ## Returns a position to the first element in the ``a`` that is greater than - ## ``key``, or last if no such element is found. + ## Returns the index of the first element in `a` that is not less than + ## (i.e. greater or equal to) `key`, or last if no such element is found. ## In other words if you have a sorted sequence and you call - ## ``insert(thing, elm, lowerBound(thing, elm))`` + ## `insert(thing, elm, lowerBound(thing, elm))` ## the sequence will still be sorted. + ## Assumes that `a` is sorted. ## - ## The version uses the default comparison function ``cmp``. + ## This version uses the default comparison function `cmp`. ## ## **See also:** - ## * `upperBound proc<#upperBound,openArray[T],K,proc(T,K)>`_ sorted by ``cmp`` in the specified order + ## * `upperBound proc<#upperBound,openArray[T],K,proc(T,K)>`_ sorted by `cmp` in the specified order ## * `upperBound proc<#upperBound,openArray[T],T>`_ -proc upperBound*[T, K](a: openArray[T], key: K, cmp: proc(x: T, k: K): int {. - closure.}): int = - ## Returns a position to the first element in the ``a`` that is not less - ## (i.e. greater or equal to) than ``key``, or last if no such element is found. +proc upperBound*[T, K](a: openArray[T], key: K, + cmp: proc(x: T, k: K): int {.closure.}): int = + ## Returns the index of the first element in `a` that is greater than + ## `key`, or last if no such element is found. ## In other words if you have a sorted sequence and you call - ## ``insert(thing, elm, upperBound(thing, elm))`` + ## `insert(thing, elm, upperBound(thing, elm))` ## the sequence will still be sorted. + ## Assumes that `a` is sorted according to `cmp`. ## - ## If an invalid range is passed, it raises IndexDefect. + ## If an invalid range is passed, it raises `IndexDefect`. ## - ## The version uses ``cmp`` to compare the elements. The expected - ## return values are the same as that of ``system.cmp``. + ## This version uses `cmp` to compare the elements. The expected + ## return values are the same as those of `system.cmp`. ## ## **See also:** - ## * `lowerBound proc<#lowerBound,openArray[T],K,proc(T,K)>`_ sorted by ``cmp`` in the specified order + ## * `lowerBound proc<#lowerBound,openArray[T],K,proc(T,K)>`_ sorted by `cmp` in the specified order ## * `lowerBound proc<#lowerBound,openArray[T],T>`_ runnableExamples: var arr = @[1, 2, 3, 5, 6, 7, 8, 9] @@ -309,19 +313,20 @@ proc upperBound*[T, K](a: openArray[T], key: K, cmp: proc(x: T, k: K): int {. count = step proc upperBound*[T](a: openArray[T], key: T): int = upperBound(a, key, cmp[T]) - ## Returns a position to the first element in the ``a`` that is not less - ## (i.e. greater or equal to) than ``key``, or last if no such element is found. + ## Returns the index of the first element in `a` that is greater than + ## `key`, or last if no such element is found. ## In other words if you have a sorted sequence and you call - ## ``insert(thing, elm, upperBound(thing, elm))`` + ## `insert(thing, elm, upperBound(thing, elm))` ## the sequence will still be sorted. + ## Assumes that `a` is sorted. ## - ## The version uses the default comparison function ``cmp``. + ## This version uses the default comparison function `cmp`. ## ## **See also:** - ## * `lowerBound proc<#lowerBound,openArray[T],K,proc(T,K)>`_ sorted by ``cmp`` in the specified order + ## * `lowerBound proc<#lowerBound,openArray[T],K,proc(T,K)>`_ sorted by `cmp` in the specified order ## * `lowerBound proc<#lowerBound,openArray[T],T>`_ -template `<-` (a, b) = +template `<-`(a, b) = when defined(gcDestructors): a = move b elif onlySafeCode: @@ -331,10 +336,10 @@ template `<-` (a, b) = proc merge[T](a, b: var openArray[T], lo, m, hi: int, cmp: proc (x, y: T): int {.closure.}, order: SortOrder) = - # optimization: If max(left) <= min(right) there is nothing to do! - # 1 2 3 4 ## 5 6 7 8 + # Optimization: If max(left) <= min(right) there is nothing to do! + # 1 2 3 4 ## 5 6 7 8 # -> O(n) for sorted arrays. - # On random data this safes up to 40% of merge calls + # On random data this saves up to 40% of merge calls. if cmp(a[m], a[m+1]) * order <= 0: return var j = lo # copy a[j..m] into b: @@ -372,14 +377,15 @@ func sort*[T](a: var openArray[T], cmp: proc (x, y: T): int {.closure.}, order = SortOrder.Ascending) = ## Default Nim sort (an implementation of merge sort). The sorting - ## is guaranteed to be stable and the worst case is guaranteed to - ## be O(n log n). + ## is guaranteed to be stable (that is, equal elements stay in the same order) + ## and the worst case is guaranteed to be O(n log n). + ## Sorts by `cmp` in the specified `order`. ## ## The current implementation uses an iterative ## mergesort to achieve this. It uses a temporary sequence of - ## length ``a.len div 2``. If you do not wish to provide your own - ## ``cmp``, you may use ``system.cmp`` or instead call the overloaded - ## version of ``sort``, which uses ``system.cmp``. + ## length `a.len div 2`. If you do not wish to provide your own + ## `cmp`, you may use `system.cmp` or instead call the overloaded + ## version of `sort`, which uses `system.cmp`. ## ## .. code-block:: nim ## @@ -400,7 +406,7 @@ func sort*[T](a: var openArray[T], ## ## **See also:** ## * `sort proc<#sort,openArray[T]>`_ - ## * `sorted proc<#sorted,openArray[T],proc(T,T)>`_ sorted by ``cmp`` in the specified order + ## * `sorted proc<#sorted,openArray[T],proc(T,T)>`_ sorted by `cmp` in the specified order ## * `sorted proc<#sorted,openArray[T]>`_ ## * `sortedByIt template<#sortedByIt.t,untyped,untyped>`_ runnableExamples: @@ -411,8 +417,7 @@ func sort*[T](a: var openArray[T], sort(d, myCmp) assert d == ["fo", "qux", "boo", "barr"] var n = a.len - var b: seq[T] - newSeq(b, n div 2) + var b = newSeq[T](n div 2) var s = 1 while s < n: var m = n-1-s @@ -423,17 +428,17 @@ func sort*[T](a: var openArray[T], proc sort*[T](a: var openArray[T], order = SortOrder.Ascending) = sort[T](a, system.cmp[T], order) - ## Shortcut version of ``sort`` that uses ``system.cmp[T]`` as the comparison function. + ## Shortcut version of `sort` that uses `system.cmp[T]` as the comparison function. ## ## **See also:** ## * `sort func<#sort,openArray[T],proc(T,T)>`_ - ## * `sorted proc<#sorted,openArray[T],proc(T,T)>`_ sorted by ``cmp`` in the specified order + ## * `sorted proc<#sorted,openArray[T],proc(T,T)>`_ sorted by `cmp` in the specified order ## * `sorted proc<#sorted,openArray[T]>`_ ## * `sortedByIt template<#sortedByIt.t,untyped,untyped>`_ proc sorted*[T](a: openArray[T], cmp: proc(x, y: T): int {.closure.}, order = SortOrder.Ascending): seq[T] = - ## Returns ``a`` sorted by ``cmp`` in the specified ``order``. + ## Returns `a` sorted by `cmp` in the specified `order`. ## ## **See also:** ## * `sort func<#sort,openArray[T],proc(T,T)>`_ @@ -454,7 +459,7 @@ proc sorted*[T](a: openArray[T], cmp: proc(x, y: T): int {.closure.}, sort(result, cmp, order) proc sorted*[T](a: openArray[T], order = SortOrder.Ascending): seq[T] = - ## Shortcut version of ``sorted`` that uses ``system.cmp[T]`` as the comparison function. + ## Shortcut version of `sorted` that uses `system.cmp[T]` as the comparison function. ## ## **See also:** ## * `sort func<#sort,openArray[T],proc(T,T)>`_ @@ -472,18 +477,18 @@ proc sorted*[T](a: openArray[T], order = SortOrder.Ascending): seq[T] = sorted[T](a, system.cmp[T], order) template sortedByIt*(seq1, op: untyped): untyped = - ## Convenience template around the ``sorted`` proc to reduce typing. + ## Convenience template around the `sorted` proc to reduce typing. ## - ## The template injects the ``it`` variable which you can use directly in an + ## The template injects the `it` variable which you can use directly in an ## expression. ## - ## Because the underlying ``cmp()`` is defined for tuples you can do + ## Because the underlying `cmp()` is defined for tuples you can also do ## a nested sort. ## ## **See also:** ## * `sort func<#sort,openArray[T],proc(T,T)>`_ ## * `sort proc<#sort,openArray[T]>`_ - ## * `sorted proc<#sorted,openArray[T],proc(T,T)>`_ sorted by ``cmp`` in the specified order + ## * `sorted proc<#sorted,openArray[T],proc(T,T)>`_ sorted by `cmp` in the specified order ## * `sorted proc<#sorted,openArray[T]>`_ runnableExamples: type Person = tuple[name: string, age: int] @@ -510,9 +515,9 @@ template sortedByIt*(seq1, op: untyped): untyped = func isSorted*[T](a: openArray[T], cmp: proc(x, y: T): int {.closure.}, order = SortOrder.Ascending): bool = - ## Checks to see whether ``a`` is already sorted in ``order`` - ## using ``cmp`` for the comparison. Parameters identical - ## to ``sort``. Requires O(n) time. + ## Checks to see whether `a` is already sorted in `order` + ## using `cmp` for the comparison. The parameters are identical + ## to `sort`. Requires O(n) time. ## ## **See also:** ## * `isSorted proc<#isSorted,openArray[T]>`_ @@ -535,7 +540,7 @@ func isSorted*[T](a: openArray[T], return false proc isSorted*[T](a: openArray[T], order = SortOrder.Ascending): bool = - ## Shortcut version of ``isSorted`` that uses ``system.cmp[T]`` as the comparison function. + ## Shortcut version of `isSorted` that uses `system.cmp[T]` as the comparison function. ## ## **See also:** ## * `isSorted func<#isSorted,openArray[T],proc(T,T)>`_ @@ -555,8 +560,10 @@ proc isSorted*[T](a: openArray[T], order = SortOrder.Ascending): bool = isSorted(a, system.cmp[T], order) proc product*[T](x: openArray[seq[T]]): seq[seq[T]] = - ## Produces the Cartesian product of the array. Warning: complexity - ## may explode. + ## Produces the Cartesian product of the array. + ## Every element of the result is a combination of one element from each seq in `x`, + ## with the ith element coming from `x[i]`. + ## Warning: complexity may explode. runnableExamples: assert product(@[@[1], @[2]]) == @[@[1, 2]] assert product(@[@["A", "K"], @["Q"]]) == @[@["K", "Q"], @["A", "Q"]] @@ -567,34 +574,33 @@ proc product*[T](x: openArray[seq[T]]): seq[seq[T]] = result = @x return var - indexes = newSeq[int](x.len) + indices = newSeq[int](x.len) initial = newSeq[int](x.len) index = 0 - var next = newSeq[T]() - next.setLen(x.len) + var next = newSeq[T](x.len) for i in 0..(x.len-1): if len(x[i]) == 0: return - initial[i] = len(x[i])-1 - indexes = initial + initial[i] = len(x[i]) - 1 + indices = initial while true: - while indexes[index] == -1: - indexes[index] = initial[index] + while indices[index] == -1: + indices[index] = initial[index] index += 1 if index == x.len: return - indexes[index] -= 1 - for ni, i in indexes: + indices[index] -= 1 + for ni, i in indices: next[ni] = x[ni][i] result.add(next) index = 0 - indexes[index] -= 1 + indices[index] -= 1 proc nextPermutation*[T](x: var openArray[T]): bool {.discardable.} = - ## Calculates the next lexicographic permutation, directly modifying ``x``. + ## Calculates the next lexicographic permutation, directly modifying `x`. ## The result is whether a permutation happened, otherwise we have reached ## the last-ordered permutation. ## ## If you start with an unsorted array/seq, the repeated permutations - ## will **not** give you all permutations but stop with last. + ## will **not** give you all permutations but stop with the last. ## ## **See also:** ## * `prevPermutation proc<#prevPermutation,openArray[T]>`_ @@ -630,7 +636,7 @@ proc nextPermutation*[T](x: var openArray[T]): bool {.discardable.} = proc prevPermutation*[T](x: var openArray[T]): bool {.discardable.} = ## Calculates the previous lexicographic permutation, directly modifying - ## ``x``. The result is whether a permutation happened, otherwise we have + ## `x`. The result is whether a permutation happened, otherwise we have ## reached the first-ordered permutation. ## ## **See also:** @@ -664,7 +670,8 @@ proc prevPermutation*[T](x: var openArray[T]): bool {.discardable.} = result = true proc rotateInternal[T](arg: var openArray[T]; first, middle, last: int): int = - ## A port of std::rotate from c++. Ported from `this reference `_. + ## A port of std::rotate from C++. + ## Ported from [this reference](http://www.cplusplus.com/reference/algorithm/rotate/). result = first + last - middle if first == middle or middle == last: @@ -716,30 +723,30 @@ proc rotatedInternal[T](arg: openArray[T]; first, middle, last: int): seq[T] = result[i] = arg[i] proc rotateLeft*[T](arg: var openArray[T]; slice: HSlice[int, int]; - dist: int): int {.discardable.} = + dist: int): int {.discardable.} = ## Performs a left rotation on a range of elements. If you want to rotate - ## right, use a negative ``dist``. Specifically, ``rotateLeft`` rotates - ## the elements at ``slice`` by ``dist`` positions. + ## right, use a negative `dist`. Specifically, `rotateLeft` rotates + ## the elements at `slice` by `dist` positions. ## - ## | The element at index ``slice.a + dist`` will be at index ``slice.a``. - ## | The element at index ``slice.b`` will be at ``slice.a + dist -1``. - ## | The element at index ``slice.a`` will be at ``slice.b + 1 - dist``. - ## | The element at index ``slice.a + dist - 1`` will be at ``slice.b``. + ## | The element at index `slice.a + dist` will be at index `slice.a`. + ## | The element at index `slice.b` will be at `slice.a + dist - 1`. + ## | The element at index `slice.a` will be at `slice.b + 1 - dist`. + ## | The element at index `slice.a + dist - 1` will be at `slice.b`. ## - ## Elements outside of ``slice`` will be left unchanged. - ## The time complexity is linear to ``slice.b - slice.a + 1``. - ## If an invalid range (``HSlice``) is passed, it raises IndexDefect. + ## Elements outside of `slice` will be left unchanged. + ## The time complexity is linear to `slice.b - slice.a + 1`. + ## If an invalid range (`HSlice`) is passed, it raises `IndexDefect`. ## - ## ``slice`` + ## `slice` ## The indices of the element range that should be rotated. ## - ## ``dist`` + ## `dist` ## The distance in amount of elements that the data should be rotated. ## Can be negative, can be any number. ## ## **See also:** ## * `rotateLeft proc<#rotateLeft,openArray[T],int>`_ for a version which rotates the whole container - ## * `rotatedLeft proc<#rotatedLeft,openArray[T],HSlice[int,int],int>`_ for a version which returns a ``seq[T]`` + ## * `rotatedLeft proc<#rotatedLeft,openArray[T],HSlice[int,int],int>`_ for a version which returns a `seq[T]` runnableExamples: var a = [0, 1, 2, 3, 4, 5] a.rotateLeft(1 .. 4, 3) @@ -751,15 +758,16 @@ proc rotateLeft*[T](arg: var openArray[T]; slice: HSlice[int, int]; doAssertRaises(IndexDefect, a.rotateLeft(1 .. 7, 2)) let sliceLen = slice.b + 1 - slice.a let distLeft = ((dist mod sliceLen) + sliceLen) mod sliceLen - arg.rotateInternal(slice.a, slice.a+distLeft, slice.b + 1) + arg.rotateInternal(slice.a, slice.a + distLeft, slice.b + 1) proc rotateLeft*[T](arg: var openArray[T]; dist: int): int {.discardable.} = - ## Default arguments for slice, so that this procedure operates on the entire - ## ``arg``, and not just on a part of it. + ## Same as `rotateLeft`, but with default arguments for slice, + ## so that this procedure operates on the entire + ## `arg`, and not just on a part of it. ## ## **See also:** ## * `rotateLeft proc<#rotateLeft,openArray[T],HSlice[int,int],int>`_ for a version which rotates a range - ## * `rotatedLeft proc<#rotatedLeft,openArray[T],int>`_ for a version which returns a ``seq[T]`` + ## * `rotatedLeft proc<#rotatedLeft,openArray[T],int>`_ for a version which returns a `seq[T]` runnableExamples: var a = [1, 2, 3, 4, 5] a.rotateLeft(2) @@ -773,17 +781,17 @@ proc rotateLeft*[T](arg: var openArray[T]; dist: int): int {.discardable.} = arg.rotateInternal(0, distLeft, arglen) proc rotatedLeft*[T](arg: openArray[T]; slice: HSlice[int, int], - dist: int): seq[T] = - ## Same as ``rotateLeft``, just with the difference that it does - ## not modify the argument. It creates a new ``seq`` instead. + dist: int): seq[T] = + ## Same as `rotateLeft`, just with the difference that it does + ## not modify the argument. It creates a new `seq` instead. ## - ## Elements outside of ``slice`` will be left unchanged. - ## If an invalid range (``HSlice``) is passed, it raises IndexDefect. + ## Elements outside of `slice` will be left unchanged. + ## If an invalid range (`HSlice`) is passed, it raises `IndexDefect`. ## - ## ``slice`` + ## `slice` ## The indices of the element range that should be rotated. ## - ## ``dist`` + ## `dist` ## The distance in amount of elements that the data should be rotated. ## Can be negative, can be any number. ## @@ -803,8 +811,8 @@ proc rotatedLeft*[T](arg: openArray[T]; slice: HSlice[int, int], arg.rotatedInternal(slice.a, slice.a+distLeft, slice.b+1) proc rotatedLeft*[T](arg: openArray[T]; dist: int): seq[T] = - ## Same as ``rotateLeft``, just with the difference that it does - ## not modify the argument. It creates a new ``seq`` instead. + ## Same as `rotateLeft`, just with the difference that it does + ## not modify the argument. It creates a new `seq` instead. ## ## **See also:** ## * `rotateLeft proc<#rotateLeft,openArray[T],int>`_ for the in-place version of this proc diff --git a/tests/stdlib/talgorithm.nim b/tests/stdlib/talgorithm.nim index 148a65289..47a8d327b 100644 --- a/tests/stdlib/talgorithm.nim +++ b/tests/stdlib/talgorithm.nim @@ -3,7 +3,7 @@ discard """ ''' """ #12928,10456 -import sequtils, strutils, algorithm, json +import sequtils, algorithm, json proc test() = try: @@ -14,7 +14,7 @@ proc test() = echo prefixes except: discard - + test() block: From 471aab86a0b793afe34a36b41e4366a686b589b9 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 2 Jan 2021 12:41:44 -0800 Subject: [PATCH 1263/3768] fix #16526 run config.nims before foo.nim.cfg (#16557) * fix #16526 run config.nims before foo.nim.cfg * add test --- compiler/nimconf.nim | 4 ++-- tests/misc/trunner.nim | 19 +++++++++++++++++++ tests/newconfig/bar/config.nims | 0 tests/newconfig/bar/mfoo.nim | 0 tests/newconfig/bar/mfoo.nim.cfg | 0 tests/newconfig/bar/mfoo.nims | 0 tests/newconfig/bar/nim.cfg | 0 7 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 tests/newconfig/bar/config.nims create mode 100644 tests/newconfig/bar/mfoo.nim create mode 100644 tests/newconfig/bar/mfoo.nim.cfg create mode 100644 tests/newconfig/bar/mfoo.nims create mode 100644 tests/newconfig/bar/nim.cfg diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim index 01a79c1e3..1691e7ccf 100644 --- a/compiler/nimconf.nim +++ b/compiler/nimconf.nim @@ -281,6 +281,8 @@ proc loadConfigs*(cfg: RelativeFile; cache: IdentCache; conf: ConfigRef; idgen: if optSkipProjConfigFile notin conf.globalOptions: readConfigFile(pd / cfg) + if cfg == DefaultConfig: + runNimScriptIfExists(pd / DefaultConfigNims) if conf.projectName.len != 0: # new project wide config file: @@ -289,8 +291,6 @@ proc loadConfigs*(cfg: RelativeFile; cache: IdentCache; conf: ConfigRef; idgen: projectConfig = changeFileExt(conf.projectFull, "nim.cfg") readConfigFile(projectConfig) - if cfg == DefaultConfig: - runNimScriptIfExists(pd / DefaultConfigNims) let scriptFile = conf.projectFull.changeFileExt("nims") let scriptIsProj = scriptFile == conf.projectFull diff --git a/tests/misc/trunner.nim b/tests/misc/trunner.nim index 530561cd9..874ad66d6 100644 --- a/tests/misc/trunner.nim +++ b/tests/misc/trunner.nim @@ -224,6 +224,25 @@ sub/mmain.idx""", context let cmd = fmt"{nim} r -b:cpp --hints:off --nimcache:{nimcache} --warningAsError:ProveInit {file}" check execCmdEx(cmd) == ("witness\n", 0) + block: # config.nims, nim.cfg, hintConf, bug #16557 + let cmd = fmt"{nim} r {defaultHintsOff} --hint:conf tests/newconfig/bar/mfoo.nim" + let (outp, exitCode) = execCmdEx(cmd, options = {poStdErrToStdOut}) + doAssert exitCode == 0 + let dir = getCurrentDir() + let files = """ +config/nim.cfg +config/config.nims +tests/config.nims +tests/newconfig/bar/nim.cfg +tests/newconfig/bar/config.nims +tests/newconfig/bar/mfoo.nim.cfg +tests/newconfig/bar/mfoo.nims""".splitLines + var expected = "" + for a in files: + let b = dir / a + expected.add &"Hint: used config file '{b}' [Conf]\n" + doAssert outp == expected, outp & "\n" & expected + block: # nim --eval let opt = "--hints:off" check fmt"""{nim} {opt} --eval:"echo defined(nimscript)"""".execCmdEx == ("true\n", 0) diff --git a/tests/newconfig/bar/config.nims b/tests/newconfig/bar/config.nims new file mode 100644 index 000000000..e69de29bb diff --git a/tests/newconfig/bar/mfoo.nim b/tests/newconfig/bar/mfoo.nim new file mode 100644 index 000000000..e69de29bb diff --git a/tests/newconfig/bar/mfoo.nim.cfg b/tests/newconfig/bar/mfoo.nim.cfg new file mode 100644 index 000000000..e69de29bb diff --git a/tests/newconfig/bar/mfoo.nims b/tests/newconfig/bar/mfoo.nims new file mode 100644 index 000000000..e69de29bb diff --git a/tests/newconfig/bar/nim.cfg b/tests/newconfig/bar/nim.cfg new file mode 100644 index 000000000..e69de29bb From cf714c129f7dd598863d1cc588e685df2438c658 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Sat, 2 Jan 2021 21:47:26 +0100 Subject: [PATCH 1264/3768] Make config processing order test more robust --- tests/misc/trunner.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/misc/trunner.nim b/tests/misc/trunner.nim index 874ad66d6..3f5b10f97 100644 --- a/tests/misc/trunner.nim +++ b/tests/misc/trunner.nim @@ -225,7 +225,7 @@ sub/mmain.idx""", context check execCmdEx(cmd) == ("witness\n", 0) block: # config.nims, nim.cfg, hintConf, bug #16557 - let cmd = fmt"{nim} r {defaultHintsOff} --hint:conf tests/newconfig/bar/mfoo.nim" + let cmd = fmt"{nim} r {defaultHintsOff} --hint:conf --skipParentCfg tests/newconfig/bar/mfoo.nim" let (outp, exitCode) = execCmdEx(cmd, options = {poStdErrToStdOut}) doAssert exitCode == 0 let dir = getCurrentDir() From c71f5650c65026cf7c213b3a508fc16e89bc1ad2 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sat, 2 Jan 2021 22:55:08 -0600 Subject: [PATCH 1265/3768] Revert "Make config processing order test more robust" (#16561) This reverts commit cf714c129f7dd598863d1cc588e685df2438c658. --- tests/misc/trunner.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/misc/trunner.nim b/tests/misc/trunner.nim index 3f5b10f97..874ad66d6 100644 --- a/tests/misc/trunner.nim +++ b/tests/misc/trunner.nim @@ -225,7 +225,7 @@ sub/mmain.idx""", context check execCmdEx(cmd) == ("witness\n", 0) block: # config.nims, nim.cfg, hintConf, bug #16557 - let cmd = fmt"{nim} r {defaultHintsOff} --hint:conf --skipParentCfg tests/newconfig/bar/mfoo.nim" + let cmd = fmt"{nim} r {defaultHintsOff} --hint:conf tests/newconfig/bar/mfoo.nim" let (outp, exitCode) = execCmdEx(cmd, options = {poStdErrToStdOut}) doAssert exitCode == 0 let dir = getCurrentDir() From 2aed4186989e0c9192eadcc28f989d41f92237b0 Mon Sep 17 00:00:00 2001 From: hlaaftana <10591326+hlaaftana@users.noreply.github.com> Date: Sun, 3 Jan 2021 10:02:12 +0300 Subject: [PATCH 1266/3768] Fix #16554 (#16564) --- lib/pure/collections/critbits.nim | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/pure/collections/critbits.nim b/lib/pure/collections/critbits.nim index 695f44646..a4e727909 100644 --- a/lib/pure/collections/critbits.nim +++ b/lib/pure/collections/critbits.nim @@ -531,13 +531,14 @@ func commonPrefixLen*[T](c: CritBitTree[T]): int {.inline, since((1, 3)).} = else: c.root.byte else: 0 -func toCritBitTree*[A, B](pairs: openArray[(A, B)]): CritBitTree[A] {.since: (1, 3).} = +func toCritBitTree*[T](pairs: openArray[(string, T)]): CritBitTree[T] {.since: (1, 3).} = ## Creates a new `CritBitTree` that contains the given `pairs`. runnableExamples: doAssert {"a": "0", "b": "1", "c": "2"}.toCritBitTree is CritBitTree[string] + doAssert {"a": 0, "b": 1, "c": 2}.toCritBitTree is CritBitTree[int] for item in pairs: result.incl item[0], item[1] -func toCritBitTree*[T](items: openArray[T]): CritBitTree[void] {.since: (1, 3).} = +func toCritBitTree*(items: openArray[string]): CritBitTree[void] {.since: (1, 3).} = ## Creates a new `CritBitTree` that contains the given `items`. runnableExamples: doAssert ["a", "b", "c"].toCritBitTree is CritBitTree[void] From 76f92265d91ea7490dc434a82459ae93f5e8ff9e Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sun, 3 Jan 2021 04:05:59 -0600 Subject: [PATCH 1267/3768] fix #8412 (#16563) * Revert "Make config processing order test more robust" This reverts commit cf714c129f7dd598863d1cc588e685df2438c658. * enable tmath tests * fix #8412 * Revert "enable tmath tests" This reverts commit 293b63f57ef71e6c43b9faf24883c998c40a9484. * add tests * fix --- lib/system.nim | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/system.nim b/lib/system.nim index 29c137b63..618fd5dd7 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2394,16 +2394,22 @@ when notJSnotNims: proc rawProc*[T: proc](x: T): pointer {.noSideEffect, inline.} = ## Retrieves the raw proc pointer of the closure `x`. This is ## useful for interfacing closures with C. - {.emit: """ - `result` = `x`.ClP_0; - """.} + when T is "closure": + {.emit: """ + `result` = `x`.ClP_0; + """.} + else: + {.error: "Only closure function and iterator are allowed!".} proc rawEnv*[T: proc](x: T): pointer {.noSideEffect, inline.} = ## Retrieves the raw environment pointer of the closure `x`. This is ## useful for interfacing closures with C. - {.emit: """ - `result` = `x`.ClE_0; - """.} + when T is "closure": + {.emit: """ + `result` = `x`.ClE_0; + """.} + else: + {.error: "Only closure function and iterator are allowed!".} proc finished*[T: proc](x: T): bool {.noSideEffect, inline.} = ## can be used to determine if a first class iterator has finished. From c82c67dc69a32a18a94c7c4de8efed3eb609d8c1 Mon Sep 17 00:00:00 2001 From: Roman Inflianskas Date: Sun, 3 Jan 2021 15:00:22 +0200 Subject: [PATCH 1268/3768] fix #12958 (#16565) Sync between Linux kernel code (header: https://github.com/torvalds/linux/blob/master/tools/include/uapi/linux/sched.h) and the linux module in lib. `CLONE_STOPPED` was marked as deprecated, as it was removed in the Linux kernel upstream. Fixes #12958. --- lib/posix/linux.nim | 47 ++++++++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/lib/posix/linux.nim b/lib/posix/linux.nim index 680b61461..5ce9bf2fb 100644 --- a/lib/posix/linux.nim +++ b/lib/posix/linux.nim @@ -1,24 +1,35 @@ import posix +## Flags of `clone` syscall. +## See `clone syscall manual +## `_ for more information. const - CSIGNAL* = 0x000000FF - CLONE_VM* = 0x00000100 - CLONE_FS* = 0x00000200 - CLONE_FILES* = 0x00000400 - CLONE_SIGHAND* = 0x00000800 - CLONE_PTRACE* = 0x00002000 - CLONE_VFORK* = 0x00004000 - CLONE_PARENT* = 0x00008000 - CLONE_THREAD* = 0x00010000 - CLONE_NEWNS* = 0x00020000 - CLONE_SYSVSEM* = 0x00040000 - CLONE_SETTLS* = 0x00080000 - CLONE_PARENT_SETTID* = 0x00100000 - CLONE_CHILD_CLEARTID* = 0x00200000 - CLONE_DETACHED* = 0x00400000 - CLONE_UNTRACED* = 0x00800000 - CLONE_CHILD_SETTID* = 0x01000000 - CLONE_STOPPED* = 0x02000000 + CSIGNAL* = 0x000000FF'i32 + CLONE_VM* = 0x00000100'i32 + CLONE_FS* = 0x00000200'i32 + CLONE_FILES* = 0x00000400'i32 + CLONE_SIGHAND* = 0x00000800'i32 + CLONE_PIDFD* = 0x00001000'i32 + CLONE_PTRACE* = 0x00002000'i32 + CLONE_VFORK* = 0x00004000'i32 + CLONE_PARENT* = 0x00008000'i32 + CLONE_THREAD* = 0x00010000'i32 + CLONE_NEWNS* = 0x00020000'i32 + CLONE_SYSVSEM* = 0x00040000'i32 + CLONE_SETTLS* = 0x00080000'i32 + CLONE_PARENT_SETTID* = 0x00100000'i32 + CLONE_CHILD_CLEARTID* = 0x00200000'i32 + CLONE_DETACHED* = 0x00400000'i32 + CLONE_UNTRACED* = 0x00800000'i32 + CLONE_CHILD_SETTID* = 0x01000000'i32 + CLONE_NEWCGROUP* = 0x02000000'i32 + CLONE_NEWUTS* = 0x04000000'i32 + CLONE_NEWIPC* = 0x08000000'i32 + CLONE_NEWUSER* = 0x10000000'i32 + CLONE_NEWPID* = 0x20000000'i32 + CLONE_NEWNET* = 0x40000000'i32 + CLONE_IO* = 0x80000000'i32 + CLONE_STOPPED* {.deprecated.} = 0x02000000'i32 # fn should be of type proc (a2: pointer): void {.cdecl.} proc clone*(fn: pointer; child_stack: pointer; flags: cint; From a0134671eeed3cfac1e9035568cbdec41825da8d Mon Sep 17 00:00:00 2001 From: Clyybber Date: Sun, 3 Jan 2021 19:15:56 +0100 Subject: [PATCH 1269/3768] Make test independent of repo location (#16571) * Make test independent of repo location * Fix differently --- tests/misc/trunner.nim | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/misc/trunner.nim b/tests/misc/trunner.nim index 874ad66d6..63aac0ede 100644 --- a/tests/misc/trunner.nim +++ b/tests/misc/trunner.nim @@ -230,8 +230,6 @@ sub/mmain.idx""", context doAssert exitCode == 0 let dir = getCurrentDir() let files = """ -config/nim.cfg -config/config.nims tests/config.nims tests/newconfig/bar/nim.cfg tests/newconfig/bar/config.nims @@ -241,7 +239,7 @@ tests/newconfig/bar/mfoo.nims""".splitLines for a in files: let b = dir / a expected.add &"Hint: used config file '{b}' [Conf]\n" - doAssert outp == expected, outp & "\n" & expected + doAssert outp.endsWith expected, outp & "\n" & expected block: # nim --eval let opt = "--hints:off" From 763fef59fa9bf618f9b546dcb199872b5d8ee890 Mon Sep 17 00:00:00 2001 From: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Date: Mon, 4 Jan 2021 07:25:05 +0100 Subject: [PATCH 1270/3768] Improve documentation for critbits (#16568) --- lib/pure/collections/critbits.nim | 242 +++++++++++++----------------- 1 file changed, 107 insertions(+), 135 deletions(-) diff --git a/lib/pure/collections/critbits.nim b/lib/pure/collections/critbits.nim index a4e727909..113939567 100644 --- a/lib/pure/collections/critbits.nim +++ b/lib/pure/collections/critbits.nim @@ -8,10 +8,32 @@ # ## This module implements a `crit bit tree`:idx: which is an efficient -## container for a sorted set of strings, or for a sorted mapping of strings. Based on the excellent paper -## by Adam Langley. +## container for a sorted set of strings, or for a sorted mapping of strings. Based on the +## [excellent paper by Adam Langley](https://www.imperialviolet.org/binary/critbit.pdf). ## (A crit bit tree is a form of `radix tree`:idx: or `patricia trie`:idx:.) +runnableExamples: + from sequtils import toSeq + + var critbitAsSet: CritBitTree[void] = ["kitten", "puppy"].toCritBitTree + doAssert critbitAsSet.len == 2 + critbitAsSet.incl("") + doAssert "" in critbitAsSet + critbitAsSet.excl("") + doAssert "" notin critbitAsSet + doAssert toSeq(critbitAsSet.items) == @["kitten", "puppy"] + let same = ["puppy", "kitten", "puppy"].toCritBitTree + doAssert toSeq(same.keys) == toSeq(critbitAsSet.keys) + + var critbitAsDict: CritBitTree[int] = {"key1": 42}.toCritBitTree + doAssert critbitAsDict.len == 1 + critbitAsDict["key2"] = 0 + doAssert "key2" in critbitAsDict + doAssert critbitAsDict["key2"] == 0 + critbitAsDict.excl("key1") + doAssert "key1" notin critbitAsDict + doAssert toSeq(critbitAsDict.pairs) == @[("key2", 0)] + import std/private/since type @@ -28,17 +50,15 @@ type Node[T] = ref NodeObj[T] CritBitTree*[T] = object ## The crit bit tree can either be used ## as a mapping from strings to - ## some type ``T`` or as a set of - ## strings if ``T`` is void. + ## some type `T` or as a set of + ## strings if `T` is `void`. root: Node[T] count: int func len*[T](c: CritBitTree[T]): int {.inline.} = ## Returns the number of elements in `c` in O(1). runnableExamples: - var c: CritBitTree[void] - incl(c, "key1") - incl(c, "key2") + let c = ["key1", "key2"].toCritBitTree doAssert c.len == 2 result = c.count @@ -144,7 +164,7 @@ proc excl*[T](c: var CritBitTree[T], key: string) = ## Removes `key` (and its associated value) from the set `c`. ## If the `key` does not exist, nothing happens. ## - ## See also: + ## **See also:** ## * `incl proc <#incl,CritBitTree[void],string>`_ ## * `incl proc <#incl,CritBitTree[T],string,T>`_ runnableExamples: @@ -157,9 +177,9 @@ proc excl*[T](c: var CritBitTree[T], key: string) = proc missingOrExcl*[T](c: var CritBitTree[T], key: string): bool = ## Returns true if `c` does not contain the given `key`. If the key - ## does exist, c.excl(key) is performed. + ## does exist, `c.excl(key)` is performed. ## - ## See also: + ## **See also:** ## * `excl proc <#excl,CritBitTree[T],string>`_ ## * `containsOrIncl proc <#containsOrIncl,CritBitTree[T],string,T>`_ ## * `containsOrIncl proc <#containsOrIncl,CritBitTree[void],string>`_ @@ -178,10 +198,10 @@ proc missingOrExcl*[T](c: var CritBitTree[T], key: string): bool = result = c.count == oldCount proc containsOrIncl*[T](c: var CritBitTree[T], key: string, val: T): bool = - ## Returns true if `c` contains the given `key`. If the key does not exist - ## ``c[key] = val`` is performed. + ## Returns true if `c` contains the given `key`. If the key does not exist, + ## `c[key] = val` is performed. ## - ## See also: + ## **See also:** ## * `incl proc <#incl,CritBitTree[void],string>`_ ## * `incl proc <#incl,CritBitTree[T],string,T>`_ ## * `containsOrIncl proc <#containsOrIncl,CritBitTree[void],string>`_ @@ -204,10 +224,10 @@ proc containsOrIncl*[T](c: var CritBitTree[T], key: string, val: T): bool = if not result: n.val = val proc containsOrIncl*(c: var CritBitTree[void], key: string): bool = - ## Returns true if `c` contains the given `key`. If the key does not exist + ## Returns true if `c` contains the given `key`. If the key does not exist, ## it is inserted into `c`. ## - ## See also: + ## **See also:** ## * `incl proc <#incl,CritBitTree[void],string>`_ ## * `incl proc <#incl,CritBitTree[T],string,T>`_ ## * `containsOrIncl proc <#containsOrIncl,CritBitTree[T],string,T>`_ @@ -240,7 +260,7 @@ proc inc*(c: var CritBitTree[int]; key: string, val: int = 1) = proc incl*(c: var CritBitTree[void], key: string) = ## Includes `key` in `c`. ## - ## See also: + ## **See also:** ## * `excl proc <#excl,CritBitTree[T],string>`_ ## * `incl proc <#incl,CritBitTree[T],string,T>`_ runnableExamples: @@ -253,7 +273,7 @@ proc incl*(c: var CritBitTree[void], key: string) = proc incl*[T](c: var CritBitTree[T], key: string, val: T) = ## Inserts `key` with value `val` into `c`. ## - ## See also: + ## **See also:** ## * `excl proc <#excl,CritBitTree[T],string>`_ ## * `incl proc <#incl,CritBitTree[void],string>`_ runnableExamples: @@ -265,16 +285,11 @@ proc incl*[T](c: var CritBitTree[T], key: string, val: T) = n.val = val proc `[]=`*[T](c: var CritBitTree[T], key: string, val: T) = - ## Puts a (key, value)-pair into `t`. + ## Alias for `incl <#incl,CritBitTree[T],string,T>`_. ## - ## See also: + ## **See also:** ## * `[] proc <#[],CritBitTree[T],string>`_ ## * `[] proc <#[],CritBitTree[T],string_2>`_ - runnableExamples: - var c: CritBitTree[int] - c["key"] = 42 - doAssert c["key"] == 42 - var n = rawInsert(c, key) n.val = val @@ -286,20 +301,20 @@ template get[T](c: CritBitTree[T], key: string): T = n.val func `[]`*[T](c: CritBitTree[T], key: string): T {.inline.} = - ## Retrieves the value at ``c[key]``. If `key` is not in `t`, the - ## ``KeyError`` exception is raised. One can check with ``hasKey`` whether + ## Retrieves the value at `c[key]`. If `key` is not in `t`, the + ## `KeyError` exception is raised. One can check with `hasKey` whether ## the key exists. ## - ## See also: + ## **See also:** ## * `[] proc <#[],CritBitTree[T],string_2>`_ ## * `[]= proc <#[]=,CritBitTree[T],string,T>`_ get(c, key) func `[]`*[T](c: var CritBitTree[T], key: string): var T {.inline.} = - ## Retrieves the value at ``c[key]``. The value can be modified. - ## If `key` is not in `t`, the ``KeyError`` exception is raised. + ## Retrieves the value at `c[key]`. The value can be modified. + ## If `key` is not in `t`, the `KeyError` exception is raised. ## - ## See also: + ## **See also:** ## * `[] proc <#[],CritBitTree[T],string>`_ ## * `[]= proc <#[]=,CritBitTree[T],string,T>`_ get(c, key) @@ -320,27 +335,24 @@ iterator leaves[T](n: Node[T]): Node[T] = iterator keys*[T](c: CritBitTree[T]): string = ## Yields all keys in lexicographical order. runnableExamples: - var c: CritBitTree[int] - c["key1"] = 1 - c["key2"] = 2 - var keys: seq[string] - for key in c.keys: - keys.add(key) - doAssert keys == @["key1", "key2"] + from sequtils import toSeq + + let c = {"key1": 1, "key2": 2}.toCritBitTree + doAssert toSeq(c.keys) == @["key1", "key2"] for x in leaves(c.root): yield x.key iterator values*[T](c: CritBitTree[T]): T = ## Yields all values of `c` in the lexicographical order of the ## corresponding keys. + ## + ## **See also:** + ## * `mvalues iterator <#mvalues.i,CritBitTree[T]>`_ runnableExamples: - var c: CritBitTree[int] - c["key1"] = 1 - c["key2"] = 2 - var vals: seq[int] - for val in c.values: - vals.add(val) - doAssert vals == @[1, 2] + from sequtils import toSeq + + let c = {"key1": 1, "key2": 2}.toCritBitTree + doAssert toSeq(c.values) == @[1, 2] for x in leaves(c.root): yield x.val @@ -348,40 +360,33 @@ iterator mvalues*[T](c: var CritBitTree[T]): var T = ## Yields all values of `c` in the lexicographical order of the ## corresponding keys. The values can be modified. ## - ## See also: + ## **See also:** ## * `values iterator <#values.i,CritBitTree[T]>`_ for x in leaves(c.root): yield x.val iterator items*[T](c: CritBitTree[T]): string = - ## Yields all keys in lexicographical order. - runnableExamples: - var c: CritBitTree[int] - c["key1"] = 1 - c["key2"] = 2 - var keys: seq[string] - for key in c.items: - keys.add(key) - doAssert keys == @["key1", "key2"] - + ## Alias for `keys <#keys.i,CritBitTree[T]>`_. for x in leaves(c.root): yield x.key iterator pairs*[T](c: CritBitTree[T]): tuple[key: string, val: T] = - ## Yields all (key, value)-pairs of `c`. + ## Yields all `(key, value)`-pairs of `c` in the lexicographical order of the + ## corresponding keys. + ## + ## **See also:** + ## * `mpairs iterator <#mpairs.i,CritBitTree[T]>`_ runnableExamples: - var c: CritBitTree[int] - c["key1"] = 1 - c["key2"] = 2 - var ps: seq[tuple[key: string, val: int]] - for p in c.pairs: - ps.add(p) - doAssert ps == @[(key: "key1", val: 1), (key: "key2", val: 2)] + from sequtils import toSeq + + let c = {"key1": 1, "key2": 2}.toCritBitTree + doAssert toSeq(c.pairs) == @[(key: "key1", val: 1), (key: "key2", val: 2)] for x in leaves(c.root): yield (x.key, x.val) iterator mpairs*[T](c: var CritBitTree[T]): tuple[key: string, val: var T] = - ## Yields all (key, value)-pairs of `c`. The yielded values can be modified. + ## Yields all `(key, value)`-pairs of `c` in the lexicographical order of the + ## corresponding keys. The yielded values can be modified. ## - ## See also: + ## **See also:** ## * `pairs iterator <#pairs.i,CritBitTree[T]>`_ for x in leaves(c.root): yield (x.key, x.val) @@ -401,33 +406,14 @@ proc allprefixedAux[T](c: CritBitTree[T], key: string; if i >= p.key.len or p.key[i] != key[i]: return result = top -iterator itemsWithPrefix*[T](c: CritBitTree[T], prefix: string; - longestMatch = false): string = - ## Yields all keys starting with `prefix`. If `longestMatch` is true, - ## the longest match is returned, it doesn't have to be a complete match then. - runnableExamples: - var c: CritBitTree[int] - c["key1"] = 42 - c["key2"] = 43 - var keys: seq[string] - for key in c.itemsWithPrefix("key"): - keys.add(key) - doAssert keys == @["key1", "key2"] - - let top = allprefixedAux(c, prefix, longestMatch) - for x in leaves(top): yield x.key - iterator keysWithPrefix*[T](c: CritBitTree[T], prefix: string; longestMatch = false): string = ## Yields all keys starting with `prefix`. runnableExamples: - var c: CritBitTree[int] - c["key1"] = 42 - c["key2"] = 43 - var keys: seq[string] - for key in c.keysWithPrefix("key"): - keys.add(key) - doAssert keys == @["key1", "key2"] + from sequtils import toSeq + + let c = {"key1": 42, "key2": 43}.toCritBitTree + doAssert toSeq(c.keysWithPrefix("key")) == @["key1", "key2"] let top = allprefixedAux(c, prefix, longestMatch) for x in leaves(top): yield x.key @@ -436,14 +422,14 @@ iterator valuesWithPrefix*[T](c: CritBitTree[T], prefix: string; longestMatch = false): T = ## Yields all values of `c` starting with `prefix` of the ## corresponding keys. + ## + ## **See also:** + ## * `mvaluesWithPrefix iterator <#mvaluesWithPrefix.i,CritBitTree[T],string>`_ runnableExamples: - var c: CritBitTree[int] - c["key1"] = 42 - c["key2"] = 43 - var vals: seq[int] - for val in c.valuesWithPrefix("key"): - vals.add(val) - doAssert vals == @[42, 43] + from sequtils import toSeq + + let c = {"key1": 42, "key2": 43}.toCritBitTree + doAssert toSeq(c.valuesWithPrefix("key")) == @[42, 43] let top = allprefixedAux(c, prefix, longestMatch) for x in leaves(top): yield x.val @@ -453,43 +439,52 @@ iterator mvaluesWithPrefix*[T](c: var CritBitTree[T], prefix: string; ## Yields all values of `c` starting with `prefix` of the ## corresponding keys. The values can be modified. ## - ## See also: + ## **See also:** ## * `valuesWithPrefix iterator <#valuesWithPrefix.i,CritBitTree[T],string>`_ let top = allprefixedAux(c, prefix, longestMatch) for x in leaves(top): yield x.val +iterator itemsWithPrefix*[T](c: CritBitTree[T], prefix: string; + longestMatch = false): string = + ## Alias for `keysWithPrefix <#keysWithPrefix.i,CritBitTree[T],string>`_. + let top = allprefixedAux(c, prefix, longestMatch) + for x in leaves(top): yield x.key + iterator pairsWithPrefix*[T](c: CritBitTree[T], prefix: string; longestMatch = false): tuple[key: string, val: T] = ## Yields all (key, value)-pairs of `c` starting with `prefix`. + ## + ## **See also:** + ## * `mpairsWithPrefix iterator <#mpairsWithPrefix.i,CritBitTree[T],string>`_ runnableExamples: - var c: CritBitTree[int] - c["key1"] = 42 - c["key2"] = 43 - var ps: seq[tuple[key: string, val: int]] - for p in c.pairsWithPrefix("key"): - ps.add(p) - doAssert ps == @[(key: "key1", val: 42), (key: "key2", val: 43)] + from sequtils import toSeq + + let c = {"key1": 42, "key2": 43}.toCritBitTree + doAssert toSeq(c.pairsWithPrefix("key")) == @[(key: "key1", val: 42), (key: "key2", val: 43)] let top = allprefixedAux(c, prefix, longestMatch) for x in leaves(top): yield (x.key, x.val) iterator mpairsWithPrefix*[T](c: var CritBitTree[T], prefix: string; - longestMatch = false): tuple[key: string, val: var T] = + longestMatch = false): tuple[key: string, val: var T] = ## Yields all (key, value)-pairs of `c` starting with `prefix`. ## The yielded values can be modified. ## - ## See also: + ## **See also:** ## * `pairsWithPrefix iterator <#pairsWithPrefix.i,CritBitTree[T],string>`_ let top = allprefixedAux(c, prefix, longestMatch) for x in leaves(top): yield (x.key, x.val) func `$`*[T](c: CritBitTree[T]): string = - ## Turns `c` into a string representation. Example outputs: - ## ``{keyA: value, keyB: value}``, ``{:}`` - ## If `T` is void the outputs look like: - ## ``{keyA, keyB}``, ``{}``. + ## Turns `c` into a string representation. + runnableExamples: + doAssert $CritBitTree[int].default == "{:}" + doAssert $toCritBitTree({"key1": 1, "key2": 2}) == """{"key1": 1, "key2": 2}""" + doAssert $CritBitTree[void].default == "{}" + doAssert $toCritBitTree(["key1", "key2"]) == """{"key1", "key2"}""" + if c.len == 0: when T is void: result = "{}" @@ -516,7 +511,7 @@ func `$`*[T](c: CritBitTree[T]): string = result.add("}") func commonPrefixLen*[T](c: CritBitTree[T]): int {.inline, since((1, 3)).} = - ## Returns longest common prefix length of all keys of `c`. + ## Returns the length of the longest common prefix of all keys in `c`. ## If `c` is empty, returns 0. runnableExamples: var c: CritBitTree[void] @@ -536,35 +531,12 @@ func toCritBitTree*[T](pairs: openArray[(string, T)]): CritBitTree[T] {.since: ( runnableExamples: doAssert {"a": "0", "b": "1", "c": "2"}.toCritBitTree is CritBitTree[string] doAssert {"a": 0, "b": 1, "c": 2}.toCritBitTree is CritBitTree[int] + for item in pairs: result.incl item[0], item[1] func toCritBitTree*(items: openArray[string]): CritBitTree[void] {.since: (1, 3).} = ## Creates a new `CritBitTree` that contains the given `items`. runnableExamples: doAssert ["a", "b", "c"].toCritBitTree is CritBitTree[void] + for item in items: result.incl item - - -runnableExamples: - static: - block: - var critbitAsSet: CritBitTree[void] - doAssert critbitAsSet.len == 0 - incl critbitAsSet, "kitten" - doAssert critbitAsSet.len == 1 - incl critbitAsSet, "puppy" - doAssert critbitAsSet.len == 2 - incl critbitAsSet, "kitten" - doAssert critbitAsSet.len == 2 - incl critbitAsSet, "" - doAssert critbitAsSet.len == 3 - block: - var critbitAsDict: CritBitTree[int] - critbitAsDict["key"] = 42 - doAssert critbitAsDict["key"] == 42 - critbitAsDict["key"] = 0 - doAssert critbitAsDict["key"] == 0 - critbitAsDict["key"] = -int.high - doAssert critbitAsDict["key"] == -int.high - critbitAsDict["key"] = int.high - doAssert critbitAsDict["key"] == int.high From c80261bc00c2be59216f945e7915699f6adab690 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 4 Jan 2021 03:24:52 -0600 Subject: [PATCH 1271/3768] fix #12311 (#16578) --- lib/system.nim | 11 +++++++---- tests/system/tsystem_misc.nim | 7 +++++++ 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/lib/system.nim b/lib/system.nim index 618fd5dd7..e220ba7a3 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2412,10 +2412,13 @@ when notJSnotNims: {.error: "Only closure function and iterator are allowed!".} proc finished*[T: proc](x: T): bool {.noSideEffect, inline.} = - ## can be used to determine if a first class iterator has finished. - {.emit: """ - `result` = ((NI*) `x`.ClE_0)[1] < 0; - """.} + ## It can be used to determine if a first class iterator has finished. + when T is "iterator": + {.emit: """ + `result` = ((NI*) `x`.ClE_0)[1] < 0; + """.} + else: + {.error: "Only closure iterator is allowed!".} when defined(js): include "system/jssys" diff --git a/tests/system/tsystem_misc.nim b/tests/system/tsystem_misc.nim index 508715905..cb879d3b3 100644 --- a/tests/system/tsystem_misc.nim +++ b/tests/system/tsystem_misc.nim @@ -210,3 +210,10 @@ block: # Ordinal # doAssert enum is Ordinal # fails # doAssert Ordinal is SomeOrdinal # doAssert SomeOrdinal is Ordinal + +block: + proc p() = discard + + doAssert not compiles(echo p.rawProc.repr) + doAssert not compiles(echo p.rawEnv.repr) + doAssert not compiles(echo p.finished) From 435f829348e12642540277ebeebe88fa6f289f80 Mon Sep 17 00:00:00 2001 From: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Date: Mon, 4 Jan 2021 11:04:30 +0100 Subject: [PATCH 1272/3768] Improve sequtils documentation (#16559) * Improve sequtils documentation Uncomment assertions in tests * Use present tense --- lib/pure/collections/sequtils.nim | 157 +++++++++++++++--------------- tests/stdlib/tsequtils.nim | 6 +- 2 files changed, 81 insertions(+), 82 deletions(-) diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index 7aa885794..9d41a5389 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -8,13 +8,13 @@ # ## Although this module has `seq` in its name, it implements operations -## not only for `seq`:idx: type, but for three built-in container types under -## the `openArray` umbrella: +## not only for the `seq`:idx: type, but for three built-in container types +## under the `openArray` umbrella: ## * sequences ## * strings ## * array ## -## The system module defines several common functions, such as: +## The `system` module defines several common functions, such as: ## * `newSeq[T]` for creating new sequences of type `T` ## * `@` for converting arrays and strings to sequences ## * `add` for adding new elements to strings and sequences @@ -27,15 +27,15 @@ ## languages. ## ## For functional style programming you have different options at your disposal: -## * `sugar.collect macro`_ -## * pass `anonymous proc`_ -## * import `sugar module`_ and use -## `=> macro.m,untyped,untyped>`_ +## * the `sugar.collect macro`_ +## * pass an `anonymous proc`_ +## * import the `sugar module`_ and use +## the `=> macro.m,untyped,untyped>`_ ## * use `...It templates<#18>`_ ## (`mapIt<#mapIt.t,typed,untyped>`_, ## `filterIt<#filterIt.t,untyped,untyped>`_, etc.) ## -## The chaining of functions is possible thanks to the +## Chaining of functions is possible thanks to the ## `method call syntax`_. runnableExamples: @@ -44,11 +44,11 @@ runnableExamples: # Creating a sequence from 1 to 10, multiplying each member by 2, # keeping only the members which are not divisible by 6. let - foo = toSeq(1..10).map(x => x*2).filter(x => x mod 6 != 0) - bar = toSeq(1..10).mapIt(it*2).filterIt(it mod 6 != 0) + foo = toSeq(1..10).map(x => x * 2).filter(x => x mod 6 != 0) + bar = toSeq(1..10).mapIt(it * 2).filterIt(it mod 6 != 0) baz = collect: for i in 1..10: - let j = 2*i + let j = 2 * i if j mod 6 != 0: j @@ -71,7 +71,8 @@ runnableExamples: doAssert (vowels is seq[char]) and (vowels == @['a', 'e', 'i', 'o', 'u']) doAssert foo.filterIt(it notin vowels).join == "sqtls s n wsm mdl" -## **See also**: +## See also +## ======== ## * `strutils module`_ for common string functions ## * `sugar module`_ for syntactic sugar macros ## * `algorithm module`_ for common generic algorithms @@ -90,11 +91,11 @@ when not defined(nimhygiene): macro evalOnceAs(expAlias, exp: untyped, letAssigneable: static[bool]): untyped = ## Injects `expAlias` in caller scope, to avoid bugs involving multiple - ## substitution in macro arguments such as - ## https://github.com/nim-lang/Nim/issues/7187 + ## substitution in macro arguments such as + ## https://github.com/nim-lang/Nim/issues/7187. ## `evalOnceAs(myAlias, myExp)` will behave as `let myAlias = myExp` ## except when `letAssigneable` is false (e.g. to handle openArray) where - ## it just forwards `exp` unchanged + ## it just forwards `exp` unchanged. expectKind(expAlias, nnkIdent) var val = exp @@ -113,7 +114,7 @@ func concat*[T](seqs: varargs[seq[T]]): seq[T] = ## Takes several sequences' items and returns them inside a new sequence. ## All sequences must be of the same type. ## - ## See also: + ## **See also:** ## * `distribute func<#distribute,seq[T],Positive>`_ for a reverse ## operation ## @@ -183,7 +184,7 @@ func repeat*[T](x: T, n: Natural): seq[T] = func deduplicate*[T](s: openArray[T], isSorted: bool = false): seq[T] = ## Returns a new sequence without duplicates. ## - ## Setting the optional argument ``isSorted`` to ``true`` (default: false) + ## Setting the optional argument `isSorted` to true (default: false) ## uses a faster algorithm for deduplication. ## runnableExamples: @@ -210,7 +211,7 @@ func deduplicate*[T](s: openArray[T], isSorted: bool = false): seq[T] = func minIndex*[T](s: openArray[T]): int {.since: (1, 1).} = ## Returns the index of the minimum value of `s`. - ## ``T`` needs to have a ``<`` operator. + ## `T` needs to have a `<` operator. runnableExamples: let a = @[1, 2, 3, 4] @@ -227,7 +228,7 @@ func minIndex*[T](s: openArray[T]): int {.since: (1, 1).} = func maxIndex*[T](s: openArray[T]): int {.since: (1, 1).} = ## Returns the index of the maximum value of `s`. - ## ``T`` needs to have a ``<`` operator. + ## `T` needs to have a `<` operator. runnableExamples: let a = @[1, 2, 3, 4] @@ -251,9 +252,9 @@ template zipImpl(s1, s2, retType: untyped): untyped = ## If one container is shorter, the remaining items in the longer container ## are discarded. ## - ## **Note**: For Nim 1.0.x and older version, ``zip`` returned a seq of - ## named tuple with fields ``a`` and ``b``. For Nim versions 1.1.x and newer, - ## ``zip`` returns a seq of unnamed tuples. + ## **Note**: For Nim 1.0.x and older version, `zip` returned a seq of + ## named tuples with fields `a` and `b`. For Nim versions 1.1.x and newer, + ## `zip` returns a seq of unnamed tuples. runnableExamples: let short = @[1, 2, 3] @@ -311,7 +312,7 @@ func distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] = ## `num` empty sequences. ## ## If `spread` is false and the length of `s` is not a multiple of `num`, the - ## func will max out the first sub-sequence with ``1 + len(s) div num`` + ## func will max out the first sub-sequence with `1 + len(s) div num` ## entries, leaving the remainder of elements to the last sequence. ## ## On the other hand, if `spread` is true, the func will distribute evenly @@ -361,16 +362,16 @@ func distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] = proc map*[T, S](s: openArray[T], op: proc (x: T): S {.closure.}): seq[S]{.inline.} = - ## Returns a new sequence with the results of `op` proc applied to every + ## Returns a new sequence with the results of the `op` proc applied to every ## item in the container `s`. ## - ## Since the input is not modified you can use it to + ## Since the input is not modified, you can use it to ## transform the type of the elements in the input container. ## ## Instead of using `map` and `filter`, consider using the `collect` macro ## from the `sugar` module. ## - ## See also: + ## **See also:** ## * `sugar.collect macro`_ ## * `mapIt template<#mapIt.t,typed,untyped>`_ ## * `apply proc<#apply,openArray[T],proc(T)_2>`_ for the in-place version @@ -387,14 +388,13 @@ proc map*[T, S](s: openArray[T], op: proc (x: T): S {.closure.}): proc apply*[T](s: var openArray[T], op: proc (x: var T) {.closure.}) {.inline.} = - ## Applies `op` to every item in `s` modifying it directly. + ## Applies `op` to every item in `s`, modifying it directly. ## - ## Note that container `s` must be declared as a ``var`` - ## and it is required for your input and output types to - ## be the same, since `s` is modified in-place. - ## The parameter function takes a ``var T`` type parameter. + ## Note that the container `s` must be declared as a `var`, + ## since `s` is modified in-place. + ## The parameter function takes a `var T` type parameter. ## - ## See also: + ## **See also:** ## * `applyIt template<#applyIt.t,untyped,untyped>`_ ## * `map proc<#map,openArray[T],proc(T)>`_ ## @@ -409,12 +409,12 @@ proc apply*[T](s: var openArray[T], op: proc (x: T): T {.closure.}) {.inline.} = ## Applies `op` to every item in `s` modifying it directly. ## - ## Note that container `s` must be declared as a ``var`` + ## Note that the container `s` must be declared as a `var` ## and it is required for your input and output types to ## be the same, since `s` is modified in-place. - ## The parameter function takes and returns a ``T`` type variable. + ## The parameter function takes and returns a `T` type variable. ## - ## See also: + ## **See also:** ## * `applyIt template<#applyIt.t,untyped,untyped>`_ ## * `map proc<#map,openArray[T],proc(T)>`_ ## @@ -426,7 +426,8 @@ proc apply*[T](s: var openArray[T], op: proc (x: T): T {.closure.}) for i in 0 ..< s.len: s[i] = op(s[i]) proc apply*[T](s: openArray[T], op: proc (x: T) {.closure.}) {.inline, since: (1, 3).} = - ## Same as `apply` but for proc that do not return and do not mutate `s` directly. + ## Same as `apply` but for a proc that does not return anything + ## and does not mutate `s` directly. runnableExamples: var message: string apply([0, 1, 2, 3, 4], proc(item: int) = message.addInt item) @@ -435,12 +436,12 @@ proc apply*[T](s: openArray[T], op: proc (x: T) {.closure.}) {.inline, since: (1 iterator filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): T = ## Iterates through a container `s` and yields every item that fulfills the - ## predicate `pred` (function that returns a `bool`). + ## predicate `pred` (a function that returns a `bool`). ## ## Instead of using `map` and `filter`, consider using the `collect` macro ## from the `sugar` module. ## - ## See also: + ## **See also:** ## * `sugar.collect macro`_ ## * `fliter proc<#filter,openArray[T],proc(T)>`_ ## * `filterIt template<#filterIt.t,untyped,untyped>`_ @@ -458,13 +459,13 @@ iterator filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): T = proc filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): seq[T] {.inline.} = - ## Returns a new sequence with all the items of `s` that fulfilled the - ## predicate `pred` (function that returns a `bool`). + ## Returns a new sequence with all the items of `s` that fulfill the + ## predicate `pred` (a function that returns a `bool`). ## ## Instead of using `map` and `filter`, consider using the `collect` macro ## from the `sugar` module. ## - ## See also: + ## **See also:** ## * `sugar.collect macro`_ ## * `filterIt template<#filterIt.t,untyped,untyped>`_ ## * `filter iterator<#filter.i,openArray[T],proc(T)>`_ @@ -485,15 +486,15 @@ proc filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): seq[T] proc keepIf*[T](s: var seq[T], pred: proc(x: T): bool {.closure.}) {.inline.} = - ## Keeps the items in the passed sequence `s` if they fulfilled the - ## predicate `pred` (function that returns a `bool`). + ## Keeps the items in the passed sequence `s` if they fulfill the + ## predicate `pred` (a function that returns a `bool`). ## - ## Note that `s` must be declared as a ``var``. + ## Note that `s` must be declared as a `var`. ## ## Similar to the `filter proc<#filter,openArray[T],proc(T)>`_, ## but modifies the sequence directly. ## - ## See also: + ## **See also:** ## * `keepItIf template<#keepItIf.t,seq,untyped>`_ ## * `filter proc<#filter,openArray[T],proc(T)>`_ ## @@ -514,8 +515,8 @@ proc keepIf*[T](s: var seq[T], pred: proc(x: T): bool {.closure.}) setLen(s, pos) func delete*[T](s: var seq[T]; first, last: Natural) = - ## Deletes in the items of a sequence `s` at positions ``first..last`` - ## (including both ends of a range). + ## Deletes the items of a sequence `s` at positions `first..last` + ## (including both ends of the range). ## This modifies `s` itself, it does not return a copy. ## runnableExamples: @@ -527,8 +528,8 @@ func delete*[T](s: var seq[T]; first, last: Natural) = if first >= s.len: return var i = first - var j = min(len(s), last+1) - var newLen = len(s)-j+i + var j = min(len(s), last + 1) + var newLen = len(s) - j + i while i < newLen: when defined(gcDestructors): s[i] = move(s[j]) @@ -542,7 +543,7 @@ func insert*[T](dest: var seq[T], src: openArray[T], pos = 0) = ## Inserts items from `src` into `dest` at position `pos`. This modifies ## `dest` itself, it does not return a copy. ## - ## Notice that `src` and `dest` must be of the same type. + ## Note that the elements of `src` and `dest` must be of the same type. ## runnableExamples: var dest = @[1, 1, 1, 1, 1, 1, 1, 1] @@ -573,7 +574,7 @@ func insert*[T](dest: var seq[T], src: openArray[T], pos = 0) = template filterIt*(s, pred: untyped): untyped = - ## Returns a new sequence with all the items of `s` that fulfilled the + ## Returns a new sequence with all the items of `s` that fulfill the ## predicate `pred`. ## ## Unlike the `filter proc<#filter,openArray[T],proc(T)>`_ and @@ -584,7 +585,7 @@ template filterIt*(s, pred: untyped): untyped = ## Instead of using `mapIt` and `filterIt`, consider using the `collect` macro ## from the `sugar` module. ## - ## See also: + ## **See also:** ## * `sugar.collect macro`_ ## * `fliter proc<#filter,openArray[T],proc(T)>`_ ## * `filter iterator<#filter.i,openArray[T],proc(T)>`_ @@ -604,13 +605,13 @@ template filterIt*(s, pred: untyped): untyped = template keepItIf*(varSeq: seq, pred: untyped) = ## Keeps the items in the passed sequence (must be declared as a `var`) - ## if they fulfilled the predicate. + ## if they fulfill the predicate. ## ## Unlike the `keepIf proc<#keepIf,seq[T],proc(T)>`_, ## the predicate needs to be an expression using ## the `it` variable for testing, like: `keepItIf("abcxyz", it == 'x')`. ## - ## See also: + ## **See also:** ## * `keepIf proc<#keepIf,seq[T],proc(T)>`_ ## * `filterIt template<#filterIt.t,untyped,untyped>`_ ## @@ -633,7 +634,7 @@ template keepItIf*(varSeq: seq, pred: untyped) = since (1, 1): template countIt*(s, pred: untyped): int = - ## Returns a count of all the items that fulfilled the predicate. + ## Returns a count of all the items that fulfill the predicate. ## ## The predicate needs to be an expression using ## the `it` variable for testing, like: `countIt(@[1, 2, 3], it > 2)`. @@ -654,19 +655,19 @@ proc all*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): bool = ## Iterates through a container and checks if every item fulfills the ## predicate. ## - ## See also: + ## **See also:** ## * `allIt template<#allIt.t,untyped,untyped>`_ ## * `any proc<#any,openArray[T],proc(T)>`_ ## runnableExamples: let numbers = @[1, 4, 5, 8, 9, 7, 4] - assert all(numbers, proc (x: int): bool = return x < 10) == true - assert all(numbers, proc (x: int): bool = return x < 9) == false + assert all(numbers, proc (x: int): bool = x < 10) == true + assert all(numbers, proc (x: int): bool = x < 9) == false for i in s: if not pred(i): return false - return true + true template allIt*(s, pred: untyped): bool = ## Iterates through a container and checks if every item fulfills the @@ -676,7 +677,7 @@ template allIt*(s, pred: untyped): bool = ## the predicate needs to be an expression using ## the `it` variable for testing, like: `allIt("abba", it == 'a')`. ## - ## See also: + ## **See also:** ## * `all proc<#all,openArray[T],proc(T)>`_ ## * `anyIt template<#anyIt.t,untyped,untyped>`_ ## @@ -693,32 +694,32 @@ template allIt*(s, pred: untyped): bool = result proc any*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): bool = - ## Iterates through a container and checks if some item fulfills the - ## predicate. + ## Iterates through a container and checks if at least one item + ## fulfills the predicate. ## - ## See also: + ## **See also:** ## * `anyIt template<#anyIt.t,untyped,untyped>`_ ## * `all proc<#all,openArray[T],proc(T)>`_ ## runnableExamples: let numbers = @[1, 4, 5, 8, 9, 7, 4] - assert any(numbers, proc (x: int): bool = return x > 8) == true - assert any(numbers, proc (x: int): bool = return x > 9) == false + assert any(numbers, proc (x: int): bool = x > 8) == true + assert any(numbers, proc (x: int): bool = x > 9) == false for i in s: if pred(i): return true - return false + false template anyIt*(s, pred: untyped): bool = - ## Iterates through a container and checks if some item fulfills the - ## predicate. + ## Iterates through a container and checks if at least one item + ## fulfills the predicate. ## ## Unlike the `any proc<#any,openArray[T],proc(T)>`_, ## the predicate needs to be an expression using ## the `it` variable for testing, like: `anyIt("abba", it == 'a')`. ## - ## See also: + ## **See also:** ## * `any proc<#any,openArray[T],proc(T)>`_ ## * `allIt template<#allIt.t,untyped,untyped>`_ ## @@ -827,7 +828,7 @@ template foldl*(sequence, operation: untyped): untyped = ## the sequence of numbers 1, 2 and 3 will be parenthesized as (((1) - 2) - ## 3). ## - ## See also: + ## **See also:** ## * `foldl template<#foldl.t,,,>`_ with a starting parameter ## * `foldr template<#foldr.t,untyped,untyped>`_ ## @@ -872,7 +873,7 @@ template foldl*(sequence, operation, first): untyped = ## `a` and `b` for each step of the fold. The `first` parameter is the ## start value (the first `a`) and therefor defines the type of the result. ## - ## See also: + ## **See also:** ## * `foldr template<#foldr.t,untyped,untyped>`_ ## runnableExamples: @@ -903,7 +904,7 @@ template foldr*(sequence, operation: untyped): untyped = ## the sequence of numbers 1, 2 and 3 will be parenthesized as (1 - (2 - ## (3))). ## - ## See also: + ## **See also:** ## * `foldl template<#foldl.t,untyped,untyped>`_ ## * `foldl template<#foldl.t,,,>`_ with a starting parameter ## @@ -932,7 +933,7 @@ template foldr*(sequence, operation: untyped): untyped = result template mapIt*(s: typed, op: untyped): untyped = - ## Returns a new sequence with the results of `op` proc applied to every + ## Returns a new sequence with the results of the `op` proc applied to every ## item in the container `s`. ## ## Since the input is not modified you can use it to @@ -944,7 +945,7 @@ template mapIt*(s: typed, op: untyped): untyped = ## Instead of using `mapIt` and `filterIt`, consider using the `collect` macro ## from the `sugar` module. ## - ## See also: + ## **See also:** ## * `sugar.collect macro`_ ## * `map proc<#map,openArray[T],proc(T)>`_ ## * `applyIt template<#applyIt.t,untyped,untyped>`_ for the in-place version @@ -1010,10 +1011,10 @@ template applyIt*(varSeq, op: untyped) = ## Convenience template around the mutable `apply` proc to reduce typing. ## ## The template injects the `it` variable which you can use directly in an - ## expression. The expression has to return the same type as the sequence you - ## are mutating. + ## expression. The expression has to return the same type as the elements + ## of the sequence you are mutating. ## - ## See also: + ## **See also:** ## * `apply proc<#apply,openArray[T],proc(T)_2>`_ ## * `mapIt template<#mapIt.t,typed,untyped>`_ ## @@ -1081,7 +1082,7 @@ macro mapLiterals*(constructor, op: untyped; let b = mapLiterals((1.2, (2.3, 3.4), 4.8), int, nested=false) assert a == (1, (2, 3), 4) assert b == (1, (2.3, 3.4), 4) - + let c = mapLiterals((1, (2, 3), 4, (5, 6)), `$`) let d = mapLiterals((1, (2, 3), 4, (5, 6)), `$`, nested=false) assert c == ("1", ("2", "3"), "4", ("5", "6")) diff --git a/tests/stdlib/tsequtils.nim b/tests/stdlib/tsequtils.nim index 94f6c3b08..385e6e651 100644 --- a/tests/stdlib/tsequtils.nim +++ b/tests/stdlib/tsequtils.nim @@ -410,13 +410,11 @@ block: # mapIt with direct openArray template foo2(x: openArray[int]): seq[int] = x.mapIt(it * 10) counter = 0 doAssert foo2(openArray[int]([identity(1), identity(2)])) == @[10, 20] - # TODO: this fails; not sure how to fix this case - # doAssert counter == 2 + doAssert counter == 2 counter = 0 doAssert openArray[int]([identity(1), identity(2)]).mapIt(it) == @[1, 2] - # ditto - # doAssert counter == 2 + doAssert counter == 2 block: # mapIt empty test, see https://github.com/nim-lang/Nim/pull/8584#pullrequestreview-144723468 # NOTE: `[].mapIt(it)` is illegal, just as `let a = @[]` is (lacks type From acf3715ea808d6ca5cfd94a340205d588fd50969 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 4 Jan 2021 04:34:13 -0600 Subject: [PATCH 1273/3768] continue #15456 add #pragma directives compiler support (#16472) * continue #15456 * follow the advice from juan_carlos --- compiler/ccgstmts.nim | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 367e693e9..8c419caac 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -653,12 +653,19 @@ proc genParForStmt(p: BProc, t: PNode) = initLocExpr(p, call[2], rangeB) # $n at the beginning because of #9710 - if call.len == 4: # `||`(a, b, annotation) - lineF(p, cpsStmts, "$n#pragma omp $4$n" & - "for ($1 = $2; $1 <= $3; ++$1)", - [forLoopVar.loc.rdLoc, - rangeA.rdLoc, rangeB.rdLoc, - call[3].getStr.rope]) + if call.len == 4: # procName(a, b, annotation) + if call[0].sym.name.s == "||": # `||`(a, b, annotation) + lineF(p, cpsStmts, "$n#pragma omp $4$n" & + "for ($1 = $2; $1 <= $3; ++$1)", + [forLoopVar.loc.rdLoc, + rangeA.rdLoc, rangeB.rdLoc, + call[3].getStr.rope]) + else: + lineF(p, cpsStmts, "$n#pragma $4$n" & + "for ($1 = $2; $1 <= $3; ++$1)", + [forLoopVar.loc.rdLoc, + rangeA.rdLoc, rangeB.rdLoc, + call[3].getStr.rope]) else: # `||`(a, b, step, annotation) var step: TLoc initLocExpr(p, call[3], step) From 7c2c1ad0724c1a20063a7669912cdaf8767145e0 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 4 Jan 2021 04:40:56 -0600 Subject: [PATCH 1274/3768] enable tmath tests for JS backend (#16562) --- tests/stdlib/tmath.nim | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/tests/stdlib/tmath.nim b/tests/stdlib/tmath.nim index 1b6fb4e9f..4176f70e8 100644 --- a/tests/stdlib/tmath.nim +++ b/tests/stdlib/tmath.nim @@ -1,13 +1,12 @@ discard """ - action: run - matrix:"; -d:nimTmathCase2 -d:danger --passc:-ffast-math" + targets: "c cpp js" """ -# xxx: fix bugs for js then add: targets:"c js" +## xxx enable matrix:"; -d:nimTmathCase2 -d:danger --passc:-ffast-math" -import math, random, os -import unittest -import sets, tables +import std/[math, random, os] +import std/[unittest] +import std/[sets, tables] block: # random int block: # there might be some randomness From 349574d5745944ed2183b196113026f804393f29 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Mon, 4 Jan 2021 11:21:36 -0300 Subject: [PATCH 1275/3768] Add posix_utils.osReleaseFile (#16452) * Add posix_utils.osReleaseFile * Update lib/posix/posix_utils.nim Co-authored-by: flywind <43030857+xflywind@users.noreply.github.com> * Update lib/posix/posix_utils.nim Co-authored-by: flywind <43030857+xflywind@users.noreply.github.com> * Add a basic sanity test * Add a basic sanity test * Add a basic sanity test * Add a basic sanity test * https://github.com/nim-lang/Nim/pull/16452#issuecomment-753364096 * Update lib/posix/posix_utils.nim Co-authored-by: Andreas Rumpf * Update lib/posix/posix_utils.nim Co-authored-by: Andreas Rumpf * Update changelog.md Co-authored-by: Andreas Rumpf Co-authored-by: flywind <43030857+xflywind@users.noreply.github.com> Co-authored-by: Andreas Rumpf --- changelog.md | 5 +++++ lib/posix/posix_utils.nim | 22 +++++++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index b10b9607a..11ac33872 100644 --- a/changelog.md +++ b/changelog.md @@ -85,6 +85,11 @@ - Added `mimetypes.mimesMaxLen` thats equal to the length of the longest "mime" from `mimes`. + +- Added `posix_utils.osReleaseFile` to get system identification from `os-release` file on Linux and the BSDs. + https://www.freedesktop.org/software/systemd/man/os-release.html + + ## Language changes - `nimscript` now handles `except Exception as e`. diff --git a/lib/posix/posix_utils.nim b/lib/posix/posix_utils.nim index d083e20b9..7fd8b1fc9 100644 --- a/lib/posix/posix_utils.nim +++ b/lib/posix/posix_utils.nim @@ -11,7 +11,8 @@ # Where possible, contribute OS-independent procs in `os `_ instead. -import posix +import posix, parsecfg, os +import std/private/since type Uname* = object sysname*, nodename*, release*, version*, machine*: string @@ -107,3 +108,22 @@ proc mkdtemp*(prefix: string): string = if mkdtemp(tmpl) == nil: raise newException(OSError, $strerror(errno)) return $tmpl + +proc osReleaseFile*(): Config {.since: (1, 5).} = + ## Gets system identification from `os-release` file and returns it as a `parsecfg.Config`. + ## You also need to import the `parsecfg` module to gain access to this object. + ## The `os-release` file is an official Freedesktop.org open standard. + ## Available in Linux and BSD distributions, except Android and Android-based Linux. + ## `os-release` file is not available on Windows and OS X by design. + ## * https://www.freedesktop.org/software/systemd/man/os-release.html + runnableExamples: + import parsecfg + when defined(linux): + let data = osReleaseFile() + doAssert data.getSectionValue("", "NAME").len > 0 ## the data is up to each distro. + + # We do not use a {.strdefine.} because Standard says it *must* be that path. + for osReleaseFile in ["/etc/os-release", "/usr/lib/os-release"]: + if fileExists(osReleaseFile): + return loadConfig(osReleaseFile) + raise newException(IOError, "File not found: /etc/os-release, /usr/lib/os-release") From 0d67ad0bf38a69d110558a6eaa525e25cbf90648 Mon Sep 17 00:00:00 2001 From: Neelesh Chandola Date: Mon, 4 Jan 2021 22:16:39 +0530 Subject: [PATCH 1276/3768] Add backwards index overload for `[]` for JsonNode (#16501) * Add backwards index overload for `[]` for JsonNode * Add since Co-authored-by: flywind <43030857+xflywind@users.noreply.github.com> * Add docs, example, and changelog Co-authored-by: flywind <43030857+xflywind@users.noreply.github.com> --- changelog.md | 2 ++ lib/pure/json.nim | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/changelog.md b/changelog.md index 11ac33872..b97fcdd03 100644 --- a/changelog.md +++ b/changelog.md @@ -89,6 +89,8 @@ - Added `posix_utils.osReleaseFile` to get system identification from `os-release` file on Linux and the BSDs. https://www.freedesktop.org/software/systemd/man/os-release.html +- Added `BackwardsIndex` overload for `JsonNode`. + ## Language changes diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 063fad8b4..a9d0ed4cb 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -496,6 +496,19 @@ proc `[]`*(node: JsonNode, index: int): JsonNode {.inline.} = assert(node.kind == JArray) return node.elems[index] +proc `[]`*(node: JsonNode, index: BackwardsIndex): JsonNode {.inline, since: (1, 5, 1).} = + ## Gets the node at `array.len-i` in an array through the `^` operator. + ## + ## i.e. `j[^i]` is a shortcut for `j[j.len-i]`. + runnableExamples: + let + j = parseJson("[1,2,3,4,5]") + + doAssert j[^1].getInt == 5 + doAssert j[^2].getInt == 4 + + `[]`(node, node.len - int(index)) + proc hasKey*(node: JsonNode, key: string): bool = ## Checks if `key` exists in `node`. assert(node.kind == JObject) From 9531afac48aff3d5f0a6093742f60c95b043b08f Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 4 Jan 2021 11:27:58 -0600 Subject: [PATCH 1277/3768] fix #16499 (#16514) --- lib/system/fatal.nim | 24 ++++++++++++++---------- tests/assert/tassert_c.nim | 2 +- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/lib/system/fatal.nim b/lib/system/fatal.nim index 761e0dd69..64ec9cda3 100644 --- a/lib/system/fatal.nim +++ b/lib/system/fatal.nim @@ -30,16 +30,20 @@ elif (defined(nimQuirky) or defined(nimPanics)) and not defined(nimscript): proc name(t: typedesc): string {.magic: "TypeTrait".} proc sysFatal(exceptn: typedesc, message, arg: string) {.inline, noreturn.} = - writeStackTrace() - var buf = newStringOfCap(200) - add(buf, "Error: unhandled exception: ") - add(buf, message) - add(buf, arg) - add(buf, " [") - add(buf, name exceptn) - add(buf, "]\n") - cstderr.rawWrite buf - quit 1 + when nimvm: + # TODO when doAssertRaises works in CT, add a test for it + raise (ref exceptn)(msg: message & arg) + else: + writeStackTrace() + var buf = newStringOfCap(200) + add(buf, "Error: unhandled exception: ") + add(buf, message) + add(buf, arg) + add(buf, " [") + add(buf, name exceptn) + add(buf, "]\n") + cstderr.rawWrite buf + quit 1 proc sysFatal(exceptn: typedesc, message: string) {.inline, noreturn.} = sysFatal(exceptn, message, "") diff --git a/tests/assert/tassert_c.nim b/tests/assert/tassert_c.nim index c6a2eadb1..024175cbf 100644 --- a/tests/assert/tassert_c.nim +++ b/tests/assert/tassert_c.nim @@ -8,7 +8,7 @@ tassert_c.nim(35) tassert_c tassert_c.nim(34) foo assertions.nim(30) failedAssertImpl assertions.nim(23) raiseAssert -fatal.nim(49) sysFatal""" +fatal.nim(53) sysFatal""" proc tmatch(x, p: string): bool = var i = 0 From 80c8f06663658e12fcdcd2d36a0ee38683de552a Mon Sep 17 00:00:00 2001 From: Clyybber Date: Mon, 4 Jan 2021 18:41:02 +0100 Subject: [PATCH 1278/3768] Add test for static proc/lambda params (#16584) --- tests/statictypes/tstatictypes.nim | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/statictypes/tstatictypes.nim b/tests/statictypes/tstatictypes.nim index 8817e07a0..41c060138 100644 --- a/tests/statictypes/tstatictypes.nim +++ b/tests/statictypes/tstatictypes.nim @@ -366,3 +366,19 @@ block: block: type Foo[N: static int] = array[int32(0) .. int32(N), float] type T = Foo[3] + + +#------------------------------------------------------------------------------------------ +# static proc/lambda param +func isSorted2[T](a: openArray[T], cmp: static proc(x, y: T): bool {.inline.}): bool = + result = true + for i in 0.. Date: Mon, 4 Jan 2021 19:44:50 +0100 Subject: [PATCH 1279/3768] make --gc:arc --exceptions:quirky work again [backport:1.4] (#16583) * make --gc:arc --exceptions:quirky work again [backport:1.4] * fixes #16404 [backport:1.4] --- compiler/commands.nim | 4 ++++ compiler/main.nim | 2 -- lib/system/embedded.nim | 10 ++++++++++ lib/system/strmantle.nim | 20 ++++++++++---------- tests/manyloc/standalone/panicoverride.nim | 5 ----- tests/manyloc/standalone2/panicoverride.nim | 14 ++++++++++++++ tests/manyloc/standalone2/tavr.nim | 7 +++++++ tests/manyloc/standalone2/tavr.nim.cfg | 4 ++++ 8 files changed, 49 insertions(+), 17 deletions(-) create mode 100644 tests/manyloc/standalone2/panicoverride.nim create mode 100644 tests/manyloc/standalone2/tavr.nim create mode 100644 tests/manyloc/standalone2/tavr.nim.cfg diff --git a/compiler/commands.nim b/compiler/commands.nim index b52148668..9fb9b7e6e 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -572,6 +572,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; if pass in {passCmd2, passPP}: defineSymbol(conf.symbols, "nimSeqsV2") defineSymbol(conf.symbols, "nimV2") + if conf.exc == excNone and conf.backend != backendCpp: + conf.exc = excGoto of "orc": conf.selectedGC = gcOrc defineSymbol(conf.symbols, "gcdestructors") @@ -581,6 +583,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; if pass in {passCmd2, passPP}: defineSymbol(conf.symbols, "nimSeqsV2") defineSymbol(conf.symbols, "nimV2") + if conf.exc == excNone and conf.backend != backendCpp: + conf.exc = excGoto of "hooks": conf.selectedGC = gcHooks defineSymbol(conf.symbols, "gchooks") diff --git a/compiler/main.nim b/compiler/main.nim index 868198268..4c67aaea7 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -192,8 +192,6 @@ proc mainCommand*(graph: ModuleGraph) = # A better solution might be to fix system.nim undefSymbol(conf.symbols, "useNimRtl") of backendInvalid: doAssert false - if conf.selectedGC in {gcArc, gcOrc} and conf.backend != backendCpp: - conf.exc = excGoto proc compileToBackend() = customizeForBackend(conf.backend) diff --git a/lib/system/embedded.nim b/lib/system/embedded.nim index c4f15a336..258558c3f 100644 --- a/lib/system/embedded.nim +++ b/lib/system/embedded.nim @@ -44,3 +44,13 @@ proc setControlCHook(hook: proc () {.noconv.}) = discard proc closureIterSetupExc(e: ref Exception) {.compilerproc, inline.} = sysFatal(ReraiseDefect, "exception handling is not available") + +when gotoBasedExceptions: + var nimInErrorMode {.threadvar.}: bool + + proc nimErrorFlag(): ptr bool {.compilerRtl, inl.} = + result = addr(nimInErrorMode) + + proc nimTestErrorFlag() {.compilerRtl.} = + if nimInErrorMode: + sysFatal(ReraiseDefect, "exception handling is not available") diff --git a/lib/system/strmantle.nim b/lib/system/strmantle.nim index b5d275e25..7553f921b 100644 --- a/lib/system/strmantle.nim +++ b/lib/system/strmantle.nim @@ -24,26 +24,26 @@ const digitsTable = "0001020304050607080910111213141516171819" & # else: # res.add $i # doAssert res == digitsTable - + func digits10(num: uint64): int {.noinline.} = - if num < 10: + if num < 10'u64: result = 1 - elif num < 100: + elif num < 100'u64: result = 2 - elif num < 1_000: + elif num < 1_000'u64: result = 3 - elif num < 10_000: + elif num < 10_000'u64: result = 4 - elif num < 100_000: + elif num < 100_000'u64: result = 5 - elif num < 1_000_000: + elif num < 1_000_000'u64: result = 6 - elif num < 10_000_000: + elif num < 10_000_000'u64: result = 7 - elif num < 100_000_000: + elif num < 100_000_000'u64: result = 8 - elif num < 1_000_000_000: + elif num < 1_000_000_000'u64: result = 9 elif num < 10_000_000_000'u64: result = 10 diff --git a/tests/manyloc/standalone/panicoverride.nim b/tests/manyloc/standalone/panicoverride.nim index d9b3f4388..c0b8bb030 100644 --- a/tests/manyloc/standalone/panicoverride.nim +++ b/tests/manyloc/standalone/panicoverride.nim @@ -11,9 +11,4 @@ proc panic(s: string) {.noreturn.} = rawoutput(s) exit(1) -# Alternatively we also could implement these 2 here: -# -# proc sysFatal(exceptn: typeDesc, message: string) {.noReturn.} -# proc sysFatal(exceptn: typeDesc, message, arg: string) {.noReturn.} - {.pop.} diff --git a/tests/manyloc/standalone2/panicoverride.nim b/tests/manyloc/standalone2/panicoverride.nim new file mode 100644 index 000000000..c0b8bb030 --- /dev/null +++ b/tests/manyloc/standalone2/panicoverride.nim @@ -0,0 +1,14 @@ + +proc printf(frmt: cstring) {.varargs, importc, header: "", cdecl.} +proc exit(code: int) {.importc, header: "", cdecl.} + +{.push stack_trace: off, profiler:off.} + +proc rawoutput(s: string) = + printf("%s\n", s) + +proc panic(s: string) {.noreturn.} = + rawoutput(s) + exit(1) + +{.pop.} diff --git a/tests/manyloc/standalone2/tavr.nim b/tests/manyloc/standalone2/tavr.nim new file mode 100644 index 000000000..6cbc5c699 --- /dev/null +++ b/tests/manyloc/standalone2/tavr.nim @@ -0,0 +1,7 @@ +# bug #16404 + +proc printf(frmt: cstring) {.varargs, header: "", cdecl.} + +var x = 0 +inc x +printf("hi %ld\n", x+4777) diff --git a/tests/manyloc/standalone2/tavr.nim.cfg b/tests/manyloc/standalone2/tavr.nim.cfg new file mode 100644 index 000000000..e5291969d --- /dev/null +++ b/tests/manyloc/standalone2/tavr.nim.cfg @@ -0,0 +1,4 @@ +--gc:arc +--cpu:avr +--os:standalone +--compileOnly From 00144ee4e9857ad578597bed3f51176f252d6b13 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 4 Jan 2021 10:45:46 -0800 Subject: [PATCH 1280/3768] fix #16576: honor matrix in testament by making such tests non joinable (#16577) * refs #16576: honor matrix in testament by making such tests non joinable * add tests + misc fixes * fix test for i386 with -d:danger --- testament/categories.nim | 10 ++++++++-- testament/specs.nim | 1 + testament/testament.nim | 13 +++++++------ tests/stdlib/tmath.nim | 10 ++++++---- tests/testament/t16576.nim | 7 +++++++ tests/testament/tjoinable.nim | 8 ++++++++ 6 files changed, 37 insertions(+), 12 deletions(-) create mode 100644 tests/testament/t16576.nim create mode 100644 tests/testament/tjoinable.nim diff --git a/testament/categories.nim b/testament/categories.nim index c894bc9f9..39f0aaa27 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -49,6 +49,7 @@ proc isTestFile*(file: string): bool = # ---------------- IC tests --------------------------------------------- +# xxx deadcode proc icTests(r: var TResults; testsDir: string, cat: Category, options: string) = const tooltests = ["compiler/nim.nim", "tools/nimgrep.nim"] @@ -583,6 +584,8 @@ proc processSingleTest(r: var TResults, cat: Category, options, test: string, ta testSpec r, makeTest(test, options, cat), targets proc isJoinableSpec(spec: TSpec): bool = + # xxx simplify implementation using a whitelist of fields that are allowed to be + # set to non-default values (use `fieldPairs`), to avoid issues like bug #16576. result = not spec.sortoutput and spec.action == actionRun and not fileExists(spec.file.changeFileExt("cfg")) and @@ -595,6 +598,7 @@ proc isJoinableSpec(spec: TSpec): bool = spec.exitCode == 0 and spec.input.len == 0 and spec.nimout.len == 0 and + spec.matrix.len == 0 and spec.outputCheck != ocSubstr and spec.ccodeCheck.len == 0 and (spec.targets == {} or spec.targets == {targetC}) @@ -656,7 +660,7 @@ proc runJoinedTest(r: var TResults, cat: Category, testsDir: string) = writeFile(megatestFile, megatest) let root = getCurrentDir() - let args = ["c", "--nimCache:" & outDir, "-d:testing", "--listCmd", "--path:" & root, megatestFile] + let args = ["c", "--nimCache:" & outDir, "-d:testing", "-d:nimMegatest", "--listCmd", "--path:" & root, megatestFile] var (cmdLine, buf, exitCode) = execCmdEx2(command = compilerPrefix, args = args, input = "") if exitCode != 0: echo "$ " & cmdLine & "\n" & buf.string @@ -759,7 +763,9 @@ proc processCategory(r: var TResults, cat: Category, testSpec r, test inc testsRun if testsRun == 0: - const whiteListedDirs = ["deps"] + const whiteListedDirs = ["deps", "htmldocs", "pkgs"] + # `pkgs` because bug #16556 creates `pkgs` dirs and this can affect some users + # that try an old version of choosenim. doAssert cat.string in whiteListedDirs, "Invalid category specified: '$#' not in whilelist: $#" % [cat.string, $whiteListedDirs] diff --git a/testament/specs.nim b/testament/specs.nim index a7f0fd4bb..58fe7bf4f 100644 --- a/testament/specs.nim +++ b/testament/specs.nim @@ -71,6 +71,7 @@ type disabled, enabled, leaking TSpec* = object + # xxx make sure `isJoinableSpec` takes into account each field here. action*: TTestAction file*, cmd*: string input*: string diff --git a/testament/testament.nim b/testament/testament.nim index 7b1a25bf0..5686da81c 100644 --- a/testament/testament.nim +++ b/testament/testament.nim @@ -88,11 +88,12 @@ proc isSuccess(input: string): bool = # not clear how to do the equivalent of pkg/regex's: re"FOO(.*?)BAR" in pegs input.startsWith("Hint: ") and input.endsWith("[SuccessX]") -proc normalizeMsg(s: string): string = - result = newStringOfCap(s.len+1) - for x in splitLines(s): - if result.len > 0: result.add '\L' - result.add x.strip +when false: # deadcode + proc normalizeMsg(s: string): string = + result = newStringOfCap(s.len+1) + for x in splitLines(s): + if result.len > 0: result.add '\L' + result.add x.strip proc getFileDir(filename: string): string = result = filename.splitFile().dir @@ -794,7 +795,7 @@ proc main() = var subPath = p.key.string let nimRoot = currentSourcePath / "../.." # makes sure points to this regardless of cwd or which nim is used to compile this. - doAssert existsDir(nimRoot/testsDir) # sanity check + doAssert dirExists(nimRoot/testsDir) # sanity check if subPath.isAbsolute: subPath = subPath.relativePath(nimRoot) # at least one directory is required in the path, to use as a category name let pathParts = subPath.relativePath(testsDir).split({DirSep, AltSep}) diff --git a/tests/stdlib/tmath.nim b/tests/stdlib/tmath.nim index 4176f70e8..16c274032 100644 --- a/tests/stdlib/tmath.nim +++ b/tests/stdlib/tmath.nim @@ -1,8 +1,10 @@ discard """ targets: "c cpp js" + matrix:"; -d:danger" """ -## xxx enable matrix:"; -d:nimTmathCase2 -d:danger --passc:-ffast-math" +# xxx: there should be a test with `-d:nimTmathCase2 -d:danger --passc:-ffast-math`, +# but it requires disabling certain lines with `when not defined(nimTmathCase2)` import std/[math, random, os] import std/[unittest] @@ -156,13 +158,13 @@ block: doAssert(erf(6.0) > erf(5.0)) doAssert(erfc(6.0) < erfc(5.0)) - - # Function for approximate comparison of floats proc `==~`(x, y: float): bool = (abs(x-y) < 1e-9) + # Function for approximate comparison of floats + # xxx use `almostEqual` block: # prod doAssert prod([1, 2, 3, 4]) == 24 - doAssert prod([1.5, 3.4]) == 5.1 + doAssert prod([1.5, 3.4]).almostEqual 5.1 let x: seq[float] = @[] doAssert prod(x) == 1.0 diff --git a/tests/testament/t16576.nim b/tests/testament/t16576.nim new file mode 100644 index 000000000..8d0dd57e3 --- /dev/null +++ b/tests/testament/t16576.nim @@ -0,0 +1,7 @@ +discard """ + matrix:"-d:nimTest_t16576" +""" + +# bug #16576 +doAssert defined(nimTest_t16576) +doAssert not defined(nimMegatest) diff --git a/tests/testament/tjoinable.nim b/tests/testament/tjoinable.nim new file mode 100644 index 000000000..7a0ad7985 --- /dev/null +++ b/tests/testament/tjoinable.nim @@ -0,0 +1,8 @@ +discard """ + output: "ok" +""" + +# checks that this is joinable +doAssert defined(testing) +doAssert defined(nimMegatest) +echo "ok" # intentional to make sure this doesn't prevent `isJoinableSpec` From fe20492f0524aa640d468e31b7b44ebab4ae47ad Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 4 Jan 2021 12:54:33 -0600 Subject: [PATCH 1281/3768] clean up the docs of some modules under lib/js (#16579) --- lib/js/asyncjs.nim | 30 ++++++++--------- lib/js/dom.nim | 2 +- lib/js/dom_extensions.nim | 2 +- lib/js/jsconsole.nim | 2 +- lib/js/jscore.nim | 4 +-- lib/js/jsffi.nim | 68 +++++++++++++++++++-------------------- lib/js/jsre.nim | 2 +- tools/kochdocs.nim | 1 - 8 files changed, 55 insertions(+), 56 deletions(-) diff --git a/lib/js/asyncjs.nim b/lib/js/asyncjs.nim index 219b1bed5..76b948e6a 100644 --- a/lib/js/asyncjs.nim +++ b/lib/js/asyncjs.nim @@ -11,11 +11,11 @@ ## and libraries, writing async procedures in Nim and converting callback-based code ## to promises. ## -## A Nim procedure is asynchronous when it includes the ``{.async.}`` pragma. It -## should always have a ``Future[T]`` return type or not have a return type at all. -## A ``Future[void]`` return type is assumed by default. +## A Nim procedure is asynchronous when it includes the `{.async.}` pragma. It +## should always have a `Future[T]` return type or not have a return type at all. +## A `Future[void]` return type is assumed by default. ## -## This is roughly equivalent to the ``async`` keyword in JavaScript code. +## This is roughly equivalent to the `async` keyword in JavaScript code. ## ## .. code-block:: nim ## proc loadGame(name: string): Future[Game] {.async.} = @@ -28,14 +28,14 @@ ## // code ## } ## -## A call to an asynchronous procedure usually needs ``await`` to wait for -## the completion of the ``Future``. +## A call to an asynchronous procedure usually needs `await` to wait for +## the completion of the `Future`. ## ## .. code-block:: nim ## var game = await loadGame(name) ## ## Often, you might work with callback-based API-s. You can wrap them with -## asynchronous procedures using promises and ``newPromise``: +## asynchronous procedures using promises and `newPromise`: ## ## .. code-block:: nim ## proc loadGame(name: string): Future[Game] = @@ -44,7 +44,7 @@ ## resolve(game) ## return promise ## -## Forward definitions work properly, you just need to always add the ``{.async.}`` pragma: +## Forward definitions work properly, you just need to always add the `{.async.}` pragma: ## ## .. code-block:: nim ## proc loadGame(name: string): Future[Game] {.async.} @@ -57,10 +57,10 @@ ## If you need to use this module with older versions of JavaScript, you can ## use a tool that backports the resulting JavaScript code, as babel. -import jsffi -import macros +import std/jsffi +import std/macros -when not defined(js) and not defined(nimdoc) and not defined(nimsuggest): +when not defined(js) and not defined(nimsuggest): {.fatal: "Module asyncjs is designed to be used with the JavaScript backend.".} type @@ -69,7 +69,7 @@ type ## Wraps the return type of an asynchronous procedure. PromiseJs* {.importcpp: "Promise".} = ref object - ## A JavaScript Promise + ## A JavaScript Promise. proc replaceReturn(node: var NimNode) = @@ -139,7 +139,7 @@ proc generateJsasync(arg: NimNode): NimNode = macro async*(arg: untyped): untyped = ## Macro which converts normal procedures into - ## javascript-compatible async procedures + ## javascript-compatible async procedures. if arg.kind == nnkStmtList: result = newStmtList() for oneProc in arg: @@ -149,8 +149,8 @@ macro async*(arg: untyped): untyped = proc newPromise*[T](handler: proc(resolve: proc(response: T))): Future[T] {.importcpp: "(new Promise(#))".} ## A helper for wrapping callback-based functions - ## into promises and async procedures + ## into promises and async procedures. proc newPromise*(handler: proc(resolve: proc())): Future[void] {.importcpp: "(new Promise(#))".} ## A helper for wrapping callback-based functions - ## into promises and async procedures + ## into promises and async procedures. diff --git a/lib/js/dom.nim b/lib/js/dom.nim index d4e8ce86a..59d1381c7 100644 --- a/lib/js/dom.nim +++ b/lib/js/dom.nim @@ -10,7 +10,7 @@ ## Declaration of the Document Object Model for the `JavaScript backend ## `_. import std/private/since -when not defined(js) and not defined(Nimdoc): +when not defined(js): {.error: "This module only works on the JavaScript platform".} const diff --git a/lib/js/dom_extensions.nim b/lib/js/dom_extensions.nim index 851ec0c5f..f7d37f4bf 100644 --- a/lib/js/dom_extensions.nim +++ b/lib/js/dom_extensions.nim @@ -1,4 +1,4 @@ -import dom +import std/dom {.push importcpp.} proc elementsFromPoint*(n: DocumentOrShadowRoot; x, y: float): seq[Element] diff --git a/lib/js/jsconsole.nim b/lib/js/jsconsole.nim index 5b9893e75..87dd9eeec 100644 --- a/lib/js/jsconsole.nim +++ b/lib/js/jsconsole.nim @@ -12,7 +12,7 @@ import std/private/since, std/private/miscdollars # toLocation -when not defined(js) and not defined(Nimdoc): +when not defined(js): {.error: "This module only works on the JavaScript platform".} type Console* = ref object of JsRoot diff --git a/lib/js/jscore.nim b/lib/js/jscore.nim index 2e2bd2402..3af33b535 100644 --- a/lib/js/jscore.nim +++ b/lib/js/jscore.nim @@ -11,10 +11,10 @@ ## ## Unless your application has very ## specific requirements and solely targets JavaScript, you should be using -## the relevant functions in the ``math``, ``json``, and ``times`` stdlib +## the relevant functions in the `math`, `json`, and `times` stdlib ## modules instead. -when not defined(js) and not defined(Nimdoc): +when not defined(js): {.error: "This module only works on the JavaScript platform".} type diff --git a/lib/js/jsffi.nim b/lib/js/jsffi.nim index 15727c9f7..8220013c1 100644 --- a/lib/js/jsffi.nim +++ b/lib/js/jsffi.nim @@ -8,8 +8,8 @@ # ## This Module implements types and macros to facilitate the wrapping of, and -## interaction with JavaScript libraries. Using the provided types ``JsObject`` -## and ``JsAssoc`` together with the provided macros allows for smoother +## interaction with JavaScript libraries. Using the provided types `JsObject` +## and `JsAssoc` together with the provided macros allows for smoother ## interfacing with JavaScript, allowing for example quick and easy imports of ## JavaScript variables: @@ -24,18 +24,18 @@ runnableExamples: proc jq(selector: JsObject): JsObject {.importcpp: "$$(#)".} # Use jQuery to make the following code run, after the document is ready. - # This uses an experimental ``.()`` operator for ``JsObject``, to emit - # JavaScript calls, when no corresponding proc exists for ``JsObject``. + # This uses an experimental `.()` operator for `JsObject`, to emit + # JavaScript calls, when no corresponding proc exists for `JsObject`. proc main = jq(document).ready(proc() = console.log("Hello JavaScript!") ) -when not defined(js) and not defined(nimdoc) and not defined(nimsuggest): +when not defined(js) and not defined(nimsuggest): {.fatal: "Module jsFFI is designed to be used with the JavaScript backend.".} -import macros, tables +import std/[macros, tables] const setImpl = "#[#] = #" @@ -93,21 +93,21 @@ type var jsArguments* {.importc: "arguments", nodecl}: JsObject - ## JavaScript's arguments pseudo-variable + ## JavaScript's arguments pseudo-variable. jsNull* {.importc: "null", nodecl.}: JsObject - ## JavaScript's null literal + ## JavaScript's null literal. jsUndefined* {.importc: "undefined", nodecl.}: JsObject - ## JavaScript's undefined literal + ## JavaScript's undefined literal. jsDirname* {.importc: "__dirname", nodecl.}: cstring - ## JavaScript's __dirname pseudo-variable + ## JavaScript's __dirname pseudo-variable. jsFilename* {.importc: "__filename", nodecl.}: cstring - ## JavaScript's __filename pseudo-variable + ## JavaScript's __filename pseudo-variable. proc isNull*[T](x: T): bool {.noSideEffect, importcpp: "(# === null)".} - ## check if a value is exactly null + ## Checks if a value is exactly null. proc isUndefined*[T](x: T): bool {.noSideEffect, importcpp: "(# === undefined)".} - ## check if a value is exactly undefined + ## Checks if a value is exactly undefined. # Exceptions type @@ -122,7 +122,7 @@ type # New proc newJsObject*: JsObject {.importcpp: "{@}".} - ## Creates a new empty JsObject + ## Creates a new empty JsObject. proc newJsAssoc*[K: JsKey, V]: JsAssoc[K, V] {.importcpp: "{@}".} ## Creates a new empty JsAssoc with key type `K` and value type `V`. @@ -137,20 +137,20 @@ proc jsTypeOf*(x: JsObject): cstring {.importcpp: "typeof(#)".} proc jsNew*(x: auto): JsObject {.importcpp: "(new #)".} ## Turns a regular function call into an invocation of the - ## JavaScript's `new` operator + ## JavaScript's `new` operator. proc jsDelete*(x: auto): JsObject {.importcpp: "(delete #)".} - ## JavaScript's `delete` operator + ## JavaScript's `delete` operator. proc require*(module: cstring): JsObject {.importc.} - ## JavaScript's `require` function + ## JavaScript's `require` function. # Conversion to and from JsObject proc to*(x: JsObject, T: typedesc): T {.importcpp: "(#)".} ## Converts a JsObject `x` to type `T`. proc toJs*[T](val: T): JsObject {.importcpp: "(#)".} - ## Converts a value of any type to type JsObject + ## Converts a value of any type to type JsObject. template toJs*(s: string): JsObject = cstring(s).toJs @@ -161,7 +161,7 @@ macro jsFromAst*(n: untyped): untyped = return quote: toJs(`result`) proc `&`*(a, b: cstring): cstring {.importcpp: "(# + #)".} - ## Concatenation operator for JavaScript strings + ## Concatenation operator for JavaScript strings. proc `+` *(x, y: JsObject): JsObject {.importcpp: "(# + #)".} proc `-` *(x, y: JsObject): JsObject {.importcpp: "(# - #)".} @@ -186,24 +186,24 @@ proc `not`*(x: JsObject): JsObject {.importcpp: "(!#)".} proc `in` *(x, y: JsObject): JsObject {.importcpp: "(# in #)".} proc `[]`*(obj: JsObject, field: cstring): JsObject {.importcpp: getImpl.} - ## Return the value of a property of name `field` from a JsObject `obj`. + ## Returns the value of a property of name `field` from a JsObject `obj`. proc `[]`*(obj: JsObject, field: int): JsObject {.importcpp: getImpl.} - ## Return the value of a property of name `field` from a JsObject `obj`. + ## Returns the value of a property of name `field` from a JsObject `obj`. proc `[]=`*[T](obj: JsObject, field: cstring, val: T) {.importcpp: setImpl.} - ## Set the value of a property of name `field` in a JsObject `obj` to `v`. + ## Sets the value of a property of name `field` in a JsObject `obj` to `v`. proc `[]=`*[T](obj: JsObject, field: int, val: T) {.importcpp: setImpl.} - ## Set the value of a property of name `field` in a JsObject `obj` to `v`. + ## Sets the value of a property of name `field` in a JsObject `obj` to `v`. proc `[]`*[K: JsKey, V](obj: JsAssoc[K, V], field: K): V {.importcpp: getImpl.} - ## Return the value of a property of name `field` from a JsAssoc `obj`. + ## Returns the value of a property of name `field` from a JsAssoc `obj`. proc `[]=`*[K: JsKey, V](obj: JsAssoc[K, V], field: K, val: V) {.importcpp: setImpl.} - ## Set the value of a property of name `field` in a JsAssoc `obj` to `v`. + ## Sets the value of a property of name `field` in a JsAssoc `obj` to `v`. proc `[]`*[V](obj: JsAssoc[cstring, V], field: string): V = obj[cstring(field)] @@ -212,7 +212,7 @@ proc `[]=`*[V](obj: JsAssoc[cstring, V], field: string, val: V) = obj[cstring(field)] = val proc `==`*(x, y: JsRoot): bool {.importcpp: "(# === #)".} - ## Compare two JsObjects or JsAssocs. Be careful though, as this is comparison + ## Compares two JsObjects or JsAssocs. Be careful though, as this is comparison ## like in JavaScript, so if your JsObjects are in fact JavaScript Objects, ## and not strings or numbers, this is a *comparison of references*. @@ -341,7 +341,7 @@ macro `.()`*[K: cstring, V: proc](obj: JsAssoc[K, V], # Iterators: iterator pairs*(obj: JsObject): (cstring, JsObject) = - ## Yields tuples of type ``(cstring, JsObject)``, with the first entry + ## Yields tuples of type `(cstring, JsObject)`, with the first entry ## being the `name` of a fields in the JsObject and the second being its ## value wrapped into a JsObject. var k: cstring @@ -370,7 +370,7 @@ iterator keys*(obj: JsObject): cstring = {.emit: "}".} iterator pairs*[K: JsKey, V](assoc: JsAssoc[K, V]): (K,V) = - ## Yields tuples of type ``(K, V)``, with the first entry + ## Yields tuples of type `(K, V)`, with the first entry ## being a `key` in the JsAssoc and the second being its corresponding value. var k: cstring var v: V @@ -400,16 +400,16 @@ iterator keys*[K: JsKey, V](assoc: JsAssoc[K, V]): K = # Literal generation macro `{}`*(typ: typedesc, xs: varargs[untyped]): auto = - ## Takes a ``typedesc`` as its first argument, and a series of expressions of - ## type ``key: value``, and returns a value of the specified type with each - ## field ``key`` set to ``value``, as specified in the arguments of ``{}``. + ## Takes a `typedesc` as its first argument, and a series of expressions of + ## type `key: value`, and returns a value of the specified type with each + ## field `key` set to `value`, as specified in the arguments of `{}`. ## ## Example: ## ## .. code-block:: nim ## ## # Let's say we have a type with a ton of fields, where some fields do not - ## # need to be set, and we do not want those fields to be set to ``nil``: + ## # need to be set, and we do not want those fields to be set to `nil`: ## type ## ExtremelyHugeType = ref object ## a, b, c, d, e, f, g: int @@ -464,7 +464,7 @@ proc replaceSyms(n: NimNode): NimNode = macro bindMethod*(procedure: typed): auto = ## Takes the name of a procedure and wraps it into a lambda missing the first - ## argument, which passes the JavaScript builtin ``this`` as the first + ## argument, which passes the JavaScript builtin `this` as the first ## argument to the procedure. Returns the resulting lambda. ## ## Example: @@ -477,7 +477,7 @@ macro bindMethod*(procedure: typed): auto = ## return this.a + 42; ## }; ## - ## We can achieve this using the ``bindMethod`` macro: + ## We can achieve this using the `bindMethod` macro: ## ## .. code-block:: nim ## let obj = JsObject{ a: 10 } diff --git a/lib/js/jsre.nim b/lib/js/jsre.nim index f5c3cc1ac..7be7221bc 100644 --- a/lib/js/jsre.nim +++ b/lib/js/jsre.nim @@ -11,7 +11,7 @@ runnableExamples: doAssert jsregex.test(r"0123456789abcd") -when not defined(js) and not defined(Nimdoc): +when not defined(js): {.error: "This module only works on the JavaScript platform".} type RegExp* {.importjs.} = object ## Regular Expressions for JavaScript target. diff --git a/tools/kochdocs.nim b/tools/kochdocs.nim index 0cfedce47..81117dd52 100644 --- a/tools/kochdocs.nim +++ b/tools/kochdocs.nim @@ -136,7 +136,6 @@ lib/wrappers/openssl.nim lib/posix/posix.nim lib/posix/linux.nim lib/posix/termios.nim -lib/js/jscore.nim """.splitWhitespace() # some of these are include files so shouldn't be docgen'd From d2f4f25b5660454c2483ab1249195b3a3d037588 Mon Sep 17 00:00:00 2001 From: Miran Date: Mon, 4 Jan 2021 21:46:36 +0100 Subject: [PATCH 1282/3768] fix #16506 by changing the example (#16580) Co-authored-by: Andreas Rumpf --- lib/pure/asynchttpserver.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index f3f59baf8..4500d994e 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -35,7 +35,7 @@ ## server.listen Port(8080) ## while true: ## if server.shouldAcceptRequest(): -## asyncCheck server.acceptRequest(cb) +## await server.acceptRequest(cb) ## else: ## poll() ## @@ -382,7 +382,7 @@ when not defined(testing) and isMainModule: server.listen Port(5555) while true: if server.shouldAcceptRequest(): - asyncCheck server.acceptRequest(cb) + await server.acceptRequest(cb) else: poll() From b2a53795dcfa96445e27eaea2147d2596a8e4994 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 5 Jan 2021 05:29:21 -0800 Subject: [PATCH 1283/3768] merge tmath_misc.nim into tmath.nim (#16591) --- tests/stdlib/tmath.nim | 13 +++++++++++++ tests/stdlib/tmath_misc.nim | 24 ------------------------ 2 files changed, 13 insertions(+), 24 deletions(-) delete mode 100644 tests/stdlib/tmath_misc.nim diff --git a/tests/stdlib/tmath.nim b/tests/stdlib/tmath.nim index 16c274032..a4b493b93 100644 --- a/tests/stdlib/tmath.nim +++ b/tests/stdlib/tmath.nim @@ -357,5 +357,18 @@ template main = doAssert copySign(10.0, -NaN) == 10.0 doAssert copySign(1.0, copySign(NaN, -1.0)) == -1.0 # fails in VM + block: + doAssert 1.0 / abs(-0.0) == Inf + doAssert 1.0 / abs(0.0) == Inf + doAssert -1.0 / abs(-0.0) == -Inf + doAssert -1.0 / abs(0.0) == -Inf + doAssert abs(0.0) == 0.0 + doAssert abs(0.0'f32) == 0.0'f32 + + doAssert abs(Inf) == Inf + doAssert abs(-Inf) == Inf + doAssert abs(NaN).isNaN + doAssert abs(-NaN).isNaN + static: main() main() diff --git a/tests/stdlib/tmath_misc.nim b/tests/stdlib/tmath_misc.nim deleted file mode 100644 index 978e3e94d..000000000 --- a/tests/stdlib/tmath_misc.nim +++ /dev/null @@ -1,24 +0,0 @@ -discard """ - targets: "c js" -""" - -# TODO merge this to tmath.nim once tmath.nim supports js target - -import math - -proc main() = - block: - doAssert 1.0 / abs(-0.0) == Inf - doAssert 1.0 / abs(0.0) == Inf - doAssert -1.0 / abs(-0.0) == -Inf - doAssert -1.0 / abs(0.0) == -Inf - doAssert abs(0.0) == 0.0 - doAssert abs(0.0'f32) == 0.0'f32 - - doAssert abs(Inf) == Inf - doAssert abs(-Inf) == Inf - doAssert abs(NaN).isNaN - doAssert abs(-NaN).isNaN - -static: main() -main() From 9f1dd5c00f3ef5fdf221732bc2a70553b21426e5 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Tue, 5 Jan 2021 15:52:24 +0100 Subject: [PATCH 1284/3768] Make tmath test more strict (#16593) Don't use approximate equality when comparing numbers that are representable with perfect precision. --- tests/stdlib/tmath.nim | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/tests/stdlib/tmath.nim b/tests/stdlib/tmath.nim index a4b493b93..fbc6b80ad 100644 --- a/tests/stdlib/tmath.nim +++ b/tests/stdlib/tmath.nim @@ -170,19 +170,19 @@ block: block: # round() tests # Round to 0 decimal places - doAssert round(54.652) ==~ 55.0 - doAssert round(54.352) ==~ 54.0 - doAssert round(-54.652) ==~ -55.0 - doAssert round(-54.352) ==~ -54.0 - doAssert round(0.0) ==~ 0.0 + doAssert round(54.652) == 55.0 + doAssert round(54.352) == 54.0 + doAssert round(-54.652) == -55.0 + doAssert round(-54.352) == -54.0 + doAssert round(0.0) == 0.0 block: # splitDecimal() tests - doAssert splitDecimal(54.674).intpart ==~ 54.0 + doAssert splitDecimal(54.674).intpart == 54.0 doAssert splitDecimal(54.674).floatpart ==~ 0.674 - doAssert splitDecimal(-693.4356).intpart ==~ -693.0 + doAssert splitDecimal(-693.4356).intpart == -693.0 doAssert splitDecimal(-693.4356).floatpart ==~ -0.4356 - doAssert splitDecimal(0.0).intpart ==~ 0.0 - doAssert splitDecimal(0.0).floatpart ==~ 0.0 + doAssert splitDecimal(0.0).intpart == 0.0 + doAssert splitDecimal(0.0).floatpart == 0.0 block: # trunc tests for vcc doAssert(trunc(-1.1) == -1) @@ -257,8 +257,8 @@ block: doAssert floorDiv(-8, -3) == 2 doAssert floorMod(-8, -3) == -2 - doAssert floorMod(8.0, -3.0) ==~ -1.0 - doAssert floorMod(-8.5, 3.0) ==~ 0.5 + doAssert floorMod(8.0, -3.0) == -1.0 + doAssert floorMod(-8.5, 3.0) == 0.5 block: # euclDiv/euclMod doAssert euclDiv(8, 3) == 2 @@ -273,8 +273,8 @@ block: doAssert euclDiv(-8, -3) == 3 doAssert euclMod(-8, -3) == 1 - doAssert euclMod(8.0, -3.0) ==~ 2.0 - doAssert euclMod(-8.5, 3.0) ==~ 0.5 + doAssert euclMod(8.0, -3.0) == 2.0 + doAssert euclMod(-8.5, 3.0) == 0.5 doAssert euclDiv(9, 3) == 3 doAssert euclMod(9, 3) == 0 From 3b2f94810e4826c4eef4d517175a8b6a0a8a45a2 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 5 Jan 2021 08:21:06 -0800 Subject: [PATCH 1285/3768] remove duplication in asynchttpserver examples (#16586) * remove duplication in asynchttpserver examples * fixup * add comment showing how to run snippet locally --- lib/pure/asynchttpserver.nim | 76 +++++++++++++----------------------- 1 file changed, 28 insertions(+), 48 deletions(-) diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index 4500d994e..d9f5a3a0f 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -13,34 +13,34 @@ ## for testing applications locally. Because of this, when deploying your ## application in production you should use a reverse proxy (for example nginx) ## instead of allowing users to connect directly to this server. -## -## Example -## ======= -## -## This example will create an HTTP server on port 8080. The server will -## respond to all requests with a ``200 OK`` response code and "Hello World" -## as the response body. -## -## .. code-block:: Nim -## -## import asynchttpserver, asyncdispatch -## -## proc main {.async.} = -## var server = newAsyncHttpServer() -## proc cb(req: Request) {.async.} = -## let headers = {"Date": "Tue, 29 Apr 2014 23:40:08 GMT", -## "Content-type": "text/plain; charset=utf-8"} -## await req.respond(Http200, "Hello World", headers.newHttpHeaders()) -## -## server.listen Port(8080) -## while true: -## if server.shouldAcceptRequest(): -## await server.acceptRequest(cb) -## else: -## poll() -## -## asyncCheck main() -## runForever() + +runnableExamples: + # This example will create an HTTP server on port 8080. The server will + # respond to all requests with a `200 OK` response code and "Hello World" + # as the response body. Run locally with: + # `nim doc --doccmd:-d:nimAsynchttpserverEnableTest --lib:lib lib/pure/asynchttpserver.nim` + import asyncdispatch + if defined(nimAsynchttpserverEnableTest): + proc main {.async.} = + const port = 8080 + var server = newAsyncHttpServer() + proc cb(req: Request) {.async.} = + echo (req.reqMethod, req.url, req.headers) + let headers = {"Date": "Tue, 29 Apr 2014 23:40:08 GMT", + "Content-type": "text/plain; charset=utf-8"} + await req.respond(Http200, "Hello World", headers.newHttpHeaders()) + + echo "test this with: curl localhost:" & $port & "/" + server.listen Port(port) + while true: + if server.shouldAcceptRequest(): + await server.acceptRequest(cb) + else: + # too many concurrent connections, `maxFDs` exceeded + poll() + + asyncCheck main() + runForever() import asyncnet, asyncdispatch, parseutils, uri, strutils import httpcore @@ -368,23 +368,3 @@ proc serve*(server: AsyncHttpServer, port: Port, proc close*(server: AsyncHttpServer) = ## Terminates the async http server instance. server.socket.close() - -when not defined(testing) and isMainModule: - proc main {.async.} = - var server = newAsyncHttpServer() - proc cb(req: Request) {.async.} = - #echo(req.reqMethod, " ", req.url) - #echo(req.headers) - let headers = {"Date": "Tue, 29 Apr 2014 23:40:08 GMT", - "Content-type": "text/plain; charset=utf-8"} - await req.respond(Http200, "Hello World", headers.newHttpHeaders()) - - server.listen Port(5555) - while true: - if server.shouldAcceptRequest(): - await server.acceptRequest(cb) - else: - poll() - - asyncCheck main() - runForever() From b24d6d4b6a57a14aba0b22a9ea888a4adb19928c Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Tue, 5 Jan 2021 13:45:58 -0300 Subject: [PATCH 1286/3768] Remove old untested undocumented examples (#16595) --- examples/allany.nim | 24 ----------- examples/extract_keyval_pairs_pegs.nim | 7 --- examples/extract_keyval_pairs_re.nim | 8 ---- examples/hallo.nim | 3 -- examples/maximum.nim | 6 --- examples/myfile.txt | 11 ----- examples/readme.txt | 2 - examples/statcsv.nim | 60 -------------------------- examples/talk/dsl.nim | 33 -------------- examples/talk/formatoptimizer.nim | 55 ----------------------- examples/talk/hoisting.nim | 23 ---------- examples/talk/lazyeval.nim | 12 ------ examples/talk/quasiquote.nim | 11 ----- examples/talk/tags.nim | 9 ---- examples/tunit.nim | 47 -------------------- 15 files changed, 311 deletions(-) delete mode 100644 examples/allany.nim delete mode 100644 examples/extract_keyval_pairs_pegs.nim delete mode 100644 examples/extract_keyval_pairs_re.nim delete mode 100644 examples/hallo.nim delete mode 100644 examples/maximum.nim delete mode 100644 examples/myfile.txt delete mode 100644 examples/readme.txt delete mode 100644 examples/statcsv.nim delete mode 100644 examples/talk/dsl.nim delete mode 100644 examples/talk/formatoptimizer.nim delete mode 100644 examples/talk/hoisting.nim delete mode 100644 examples/talk/lazyeval.nim delete mode 100644 examples/talk/quasiquote.nim delete mode 100644 examples/talk/tags.nim delete mode 100644 examples/tunit.nim diff --git a/examples/allany.nim b/examples/allany.nim deleted file mode 100644 index 8a5ab81f0..000000000 --- a/examples/allany.nim +++ /dev/null @@ -1,24 +0,0 @@ -# All and any - -template all(container, cond: untyped): bool = - var result = true - for it in items(container): - if not cond(it): - result = false - break - result - -template any(container, cond: untyped): bool = - var result = false - for it in items(container): - if cond(it): - result = true - break - result - -if all("mystring", {'a'..'z'}.contains) and any("myohmy", 'y'.`==`): - echo "works" -else: - echo "does not work" - - diff --git a/examples/extract_keyval_pairs_pegs.nim b/examples/extract_keyval_pairs_pegs.nim deleted file mode 100644 index 2a5643276..000000000 --- a/examples/extract_keyval_pairs_pegs.nim +++ /dev/null @@ -1,7 +0,0 @@ -# Filter key=value pairs from "myfile.txt" -import pegs - -for x in lines("myfile.txt"): - if x =~ peg"{\ident} \s* '=' \s* {.*}": - echo "Key: ", matches[0], - " Value: ", matches[1] diff --git a/examples/extract_keyval_pairs_re.nim b/examples/extract_keyval_pairs_re.nim deleted file mode 100644 index a594c0fa8..000000000 --- a/examples/extract_keyval_pairs_re.nim +++ /dev/null @@ -1,8 +0,0 @@ -# Filter key=value pairs from "myfile.txt" -import re - -for x in lines("myfile.txt"): - if x =~ re"(\w+)=(.*)": - echo "Key: ", matches[0], " Value: ", matches[1] - - diff --git a/examples/hallo.nim b/examples/hallo.nim deleted file mode 100644 index d94da8c1f..000000000 --- a/examples/hallo.nim +++ /dev/null @@ -1,3 +0,0 @@ -# Hello world program - -echo "Hello World" diff --git a/examples/maximum.nim b/examples/maximum.nim deleted file mode 100644 index 3c43a48c9..000000000 --- a/examples/maximum.nim +++ /dev/null @@ -1,6 +0,0 @@ -# Shows how the method call syntax can be used to chain calls conveniently. - -import strutils, sequtils - -echo "Give a list of numbers (separated by spaces): " -stdin.readLine.split.map(parseInt).max.`$`.echo(" is the maximum!") diff --git a/examples/myfile.txt b/examples/myfile.txt deleted file mode 100644 index fb7cda984..000000000 --- a/examples/myfile.txt +++ /dev/null @@ -1,11 +0,0 @@ -kladsfa - -asdflksadlfasf - - -adsfljksadfl - - -key=/usr/bin/value -key2=/ha/ha - diff --git a/examples/readme.txt b/examples/readme.txt deleted file mode 100644 index 42446faea..000000000 --- a/examples/readme.txt +++ /dev/null @@ -1,2 +0,0 @@ -In this directory you can find several examples for how to use the Nim -library. diff --git a/examples/statcsv.nim b/examples/statcsv.nim deleted file mode 100644 index 983cd555f..000000000 --- a/examples/statcsv.nim +++ /dev/null @@ -1,60 +0,0 @@ -# Example program to show the parsecsv module -# This program reads a CSV file and computes sum, mean, minimum, maximum and -# the standard deviation of its columns. -# The CSV file can have a header which is then used for the output. - -import os, streams, parsecsv, strutils, math, stats - -if paramCount() < 1: - quit("Usage: statcsv filename[.csv]") - -var filename = addFileExt(paramStr(1), "csv") -var s = newFileStream(filename, fmRead) -if s == nil: quit("cannot open the file " & filename) - -var - x: CsvParser - header: seq[string] - res: seq[RunningStat] -open(x, s, filename, separator=';', skipInitialSpace = true) -while readRow(x): - if processedRows(x) == 1: - newSeq(res, x.row.len) # allocate space for the result - if validIdentifier(x.row[0]): - # header line: - header = x.row - else: - newSeq(header, x.row.len) - for i in 0..x.row.len-1: header[i] = "Col " & $(i+1) - else: - # data line: - for i in 0..x.row.len-1: - push(res[i], parseFloat(x.row[i])) -x.close() - -# Write results: -for i in 0..header.len-1: - stdout.write("\t") - stdout.write(header[i]) -stdout.write("\nSum") -for i in 0..header.len-1: - stdout.write("\t") - stdout.write(res[i].sum) -stdout.write("\nMean") -for i in 0..header.len-1: - stdout.write("\t") - stdout.write(res[i].mean) -stdout.write("\nMin") -for i in 0..header.len-1: - stdout.write("\t") - stdout.write(res[i].min) -stdout.write("\nMax") -for i in 0..header.len-1: - stdout.write("\t") - stdout.write(res[i].max) -stdout.write("\nStdDev") -for i in 0..header.len-1: - stdout.write("\t") - stdout.write(res[i].standardDeviation) -stdout.write("\n") - diff --git a/examples/talk/dsl.nim b/examples/talk/dsl.nim deleted file mode 100644 index 2dde51790..000000000 --- a/examples/talk/dsl.nim +++ /dev/null @@ -1,33 +0,0 @@ - -import strutils - -template html(name, matter: untyped) = - proc name(): string = - result = "" - matter - result.add("") - -template nestedTag(tag: untyped) = - template tag(matter: typed) = - result.add("<" & astToStr(tag) & ">") - matter - result.add("") - -template simpleTag(tag: untyped) = - template tag(matter: untyped) = - result.add("<$1>$2" % [astToStr(tag), matter]) - -nestedTag body -nestedTag head -nestedTag ul -simpleTag title -simpleTag li - -html mainPage: - head: - title "now look at this" - body: - ul: - li "Nim is quite capable" - -echo mainPage() diff --git a/examples/talk/formatoptimizer.nim b/examples/talk/formatoptimizer.nim deleted file mode 100644 index 6e3d0c2c3..000000000 --- a/examples/talk/formatoptimizer.nim +++ /dev/null @@ -1,55 +0,0 @@ -## This is the example that optimizes a modified "hello world" - -import macros - -proc invalidFormatString() = - echo "invalidFormatString" - -template formatImpl(handleChar: untyped) = - var i = 0 - while i < f.len: - if f[i] == '$': - case f[i+1] - of '1'..'9': - var j = 0 - i += 1 - while f[i] in {'0'..'9'}: - j = j * 10 + ord(f[i]) - ord('0') - i += 1 - result.add(a[j-1]) - else: - invalidFormatString() - else: - result.add(handleChar(f[i])) - i += 1 - -proc `%`*(f: string, a: openArray[string]): string = - template identity(x: untyped): untyped = x - result = "" - formatImpl(identity) - -macro optFormat{`%`(f, a)}(f: string{lit}, a: openArray[string]): untyped = - result = newNimNode(nnkBracket) - #newCall("&") - let f = f.strVal - formatImpl(newLit) - result = nestList(newIdentNode("&"), result) - -template optAdd1{x = y; add(x, z)}(x, y, z: string) = - x = y & z - -#template optAdd2{x.add(y); x.add(z)}(x, y, z: string) = -# x.add(y & z) - -proc `/&` [T: object](x: T): string = - result = "(" - for name, value in fieldPairs(x): - result.add("$1: $2\n" % [name, $value]) - result.add(")") - -type - MyObject = object - a, b: int - s: string -let obj = MyObject(a: 3, b: 4, s: "abc") -echo(/&obj) diff --git a/examples/talk/hoisting.nim b/examples/talk/hoisting.nim deleted file mode 100644 index 54e00884f..000000000 --- a/examples/talk/hoisting.nim +++ /dev/null @@ -1,23 +0,0 @@ -type - Regex = distinct string - -const maxSubpatterns = 10 - -proc re(x: string): Regex = - result = Regex(x) - -proc match(s: string, pattern: Regex, captures: var openArray[string]): bool = - true - -template optRe{re(x)}(x: string{lit}): Regex = - var g {.global.} = re(x) - g - -template `=~`(s: string, pattern: Regex): bool = - when not declaredInScope(matches): - var matches {.inject.}: array[maxSubPatterns, string] - match(s, pattern, matches) - -for line in lines("input.txt"): - if line =~ re"(\w+)=(\w+)": - echo "key-value pair; key: ", matches[0], " value: ", matches[1] diff --git a/examples/talk/lazyeval.nim b/examples/talk/lazyeval.nim deleted file mode 100644 index 77d963834..000000000 --- a/examples/talk/lazyeval.nim +++ /dev/null @@ -1,12 +0,0 @@ - -const - debug = true - -template log(msg: string) = - if debug: - echo msg -var - x = 1 - y = 2 - -log("x: " & $x & ", y: " & $y) diff --git a/examples/talk/quasiquote.nim b/examples/talk/quasiquote.nim deleted file mode 100644 index b3c7bb971..000000000 --- a/examples/talk/quasiquote.nim +++ /dev/null @@ -1,11 +0,0 @@ - -import macros - -macro check(ex: untyped): typed = - var info = ex.lineinfo - var expString = ex.toStrLit - result = quote do: - if not `ex`: - echo `info`, ": Check failed: ", `expString` - -check 1 < 2 diff --git a/examples/talk/tags.nim b/examples/talk/tags.nim deleted file mode 100644 index 8bf3450c9..000000000 --- a/examples/talk/tags.nim +++ /dev/null @@ -1,9 +0,0 @@ - -template htmlTag(tag: untyped) = - proc tag(): string = "<" & astToStr(tag) & ">" - -htmlTag(br) -htmlTag(html) - -echo br() -echo html() \ No newline at end of file diff --git a/examples/tunit.nim b/examples/tunit.nim deleted file mode 100644 index e8ff8a952..000000000 --- a/examples/tunit.nim +++ /dev/null @@ -1,47 +0,0 @@ - -import - unittest, macros - -var - a = 1 - b = 22 - c = 1 - d = 3 - -suite "my suite": - setup: - echo "suite setup" - var testVar = "from setup" - - teardown: - echo "suite teardown" - - test "first suite test": - testVar = "modified" - echo "test var: " & testVar - check a > b - - test "second suite test": - echo "test var: " & testVar - -proc foo: bool = - echo "running foo" - return true - -proc err = - raise newException(ArithmeticDefect, "some exception") - -test "final test": - echo "inside suite-less test" - - check: - a == c - foo() - d > 10 - -test "arithmetic failure": - expect(ArithmeticDefect): - err() - - expect(ArithmeticDefect, CatchableError): - discard foo() From df9e74b510a84b0050ffe022e77d42c6f949c2bc Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Tue, 5 Jan 2021 10:47:10 -0600 Subject: [PATCH 1287/3768] fix #9125 (#16582) * fix #9125 * Update tests/stdlib/tmath.nim Co-authored-by: Timothee Cour * back Co-authored-by: Andreas Rumpf Co-authored-by: Timothee Cour --- changelog.md | 5 +++-- lib/pure/math.nim | 12 +++++++++++- tests/stdlib/tmath.nim | 30 ++++++++++++++++++++++++++---- 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/changelog.md b/changelog.md index b97fcdd03..306cf54d3 100644 --- a/changelog.md +++ b/changelog.md @@ -84,13 +84,14 @@ - Added `mimetypes.mimesExtMaxLen` thats equal to the length of the longest "ext" from `mimes`. - Added `mimetypes.mimesMaxLen` thats equal to the length of the longest "mime" from `mimes`. - - - Added `posix_utils.osReleaseFile` to get system identification from `os-release` file on Linux and the BSDs. https://www.freedesktop.org/software/systemd/man/os-release.html - Added `BackwardsIndex` overload for `JsonNode`. +- `math.round` now is rounded "away from zero" in JS backend which is consistent +with other backends. see #9125. Use `-d:nimLegacyJsRound` for previous behavior. + ## Language changes diff --git a/lib/pure/math.nim b/lib/pure/math.nim index 76052ec3b..a6a3676b9 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -847,7 +847,17 @@ else: # JS func floor*(x: float64): float64 {.importc: "Math.floor", nodecl.} func ceil*(x: float32): float32 {.importc: "Math.ceil", nodecl.} func ceil*(x: float64): float64 {.importc: "Math.ceil", nodecl.} - func round*(x: float): float {.importc: "Math.round", nodecl.} + + when (NimMajor, NimMinor) < (1, 5) or defined(nimLegacyJsRound): + func round*(x: float): float {.importc: "Math.round", nodecl.} + else: + func jsRound(x: float): float {.importc: "Math.round", nodecl.} + func round*[T: float64 | float32](x: T): T = + if x >= 0: result = jsRound(x) + else: + result = ceil(x) + if result - x >= T(0.5): + result -= T(1.0) func trunc*(x: float32): float32 {.importc: "Math.trunc", nodecl.} func trunc*(x: float64): float64 {.importc: "Math.trunc", nodecl.} diff --git a/tests/stdlib/tmath.nim b/tests/stdlib/tmath.nim index fbc6b80ad..e5cb58eba 100644 --- a/tests/stdlib/tmath.nim +++ b/tests/stdlib/tmath.nim @@ -10,6 +10,10 @@ import std/[math, random, os] import std/[unittest] import std/[sets, tables] + +# Function for approximate comparison of floats +proc `==~`(x, y: float): bool = (abs(x-y) < 1e-9) + block: # random int block: # there might be some randomness var set = initHashSet[int](128) @@ -158,10 +162,6 @@ block: doAssert(erf(6.0) > erf(5.0)) doAssert(erfc(6.0) < erfc(5.0)) - proc `==~`(x, y: float): bool = (abs(x-y) < 1e-9) - # Function for approximate comparison of floats - # xxx use `almostEqual` - block: # prod doAssert prod([1, 2, 3, 4]) == 24 doAssert prod([1.5, 3.4]).almostEqual 5.1 @@ -349,6 +349,28 @@ template main = doAssert copySign(-NaN, 0.0).isNaN doAssert copySign(-NaN, -0.0).isNaN + block: # round() tests + # Round to 0 decimal places + doAssert round(54.652) == 55.0 + doAssert round(54.352) == 54.0 + doAssert round(-54.652) == -55.0 + doAssert round(-54.352) == -54.0 + doAssert round(0.0) == 0.0 + doAssert 1 / round(0.0) == Inf + doAssert 1 / round(-0.0) == -Inf + doAssert round(Inf) == Inf + doAssert round(-Inf) == -Inf + doAssert round(NaN).isNaN + doAssert round(-NaN).isNaN + doAssert round(-0.5) == -1.0 + doAssert round(0.5) == 1.0 + doAssert round(-1.5) == -2.0 + doAssert round(1.5) == 2.0 + doAssert round(-2.5) == -3.0 + doAssert round(2.5) == 3.0 + doAssert round(2.5'f32) == 3.0'f32 + doAssert round(2.5'f64) == 3.0'f64 + when nimvm: discard else: From 0c4bd65e8d2d81a5e52624215e864f7846eb320b Mon Sep 17 00:00:00 2001 From: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Date: Tue, 5 Jan 2021 17:50:15 +0100 Subject: [PATCH 1288/3768] Improve documentation for complex (#16588) * Improve documentation for complex Add missing doc comments * Add runnableExample Add links for principal values Optimize `-` Change var to let * Use std prefix for imports --- lib/pure/complex.nim | 272 +++++++++++++++++++++++--------------- tests/stdlib/tcomplex.nim | 12 +- 2 files changed, 171 insertions(+), 113 deletions(-) diff --git a/lib/pure/complex.nim b/lib/pure/complex.nim index 04e5e8e56..b9371c1e1 100644 --- a/lib/pure/complex.nim +++ b/lib/pure/complex.nim @@ -7,106 +7,155 @@ # distribution, for details about the copyright. # -## This module implements complex numbers. -## Complex numbers are currently implemented as generic on a 64-bit or 32-bit float. +## This module implements complex numbers +## and basic mathematical operations on them. +## +## Complex numbers are currently generic over 64-bit or 32-bit floats. + +runnableExamples: + from std/math import almostEqual, sqrt + + func almostEqual(a, b: Complex): bool = + almostEqual(a.re, b.re) and almostEqual(a.im, b.im) + + let + z1 = complex(1.0, 2.0) + z2 = complex(3.0, -4.0) + + assert almostEqual(z1 + z2, complex(4.0, -2.0)) + assert almostEqual(z1 - z2, complex(-2.0, 6.0)) + assert almostEqual(z1 * z2, complex(11.0, 2.0)) + assert almostEqual(z1 / z2, complex(-0.2, 0.4)) + + assert almostEqual(abs(z1), sqrt(5.0)) + assert almostEqual(conjugate(z1), complex(1.0, -2.0)) + + let (r, phi) = z1.polar + assert almostEqual(rect(r, phi), z1) {.push checks: off, line_dir: off, stack_trace: off, debugger: off.} # the user does not want to trace a part of the standard library! -import math +import std/math type Complex*[T: SomeFloat] = object - re*, im*: T ## A complex number, consisting of a real and an imaginary part. + re*, im*: T Complex64* = Complex[float64] - ## Alias for a pair of 64-bit floats. + ## Alias for a complex number using 64-bit floats. Complex32* = Complex[float32] - ## Alias for a pair of 32-bit floats. + ## Alias for a complex number using 32-bit floats. func complex*[T: SomeFloat](re: T; im: T = 0.0): Complex[T] = + ## Returns a `Complex[T]` with real part `re` and imaginary part `im`. result.re = re result.im = im -func complex32*(re: float32; im: float32 = 0.0): Complex[float32] = +func complex32*(re: float32; im: float32 = 0.0): Complex32 = + ## Returns a `Complex32` with real part `re` and imaginary part `im`. result.re = re result.im = im -func complex64*(re: float64; im: float64 = 0.0): Complex[float64] = +func complex64*(re: float64; im: float64 = 0.0): Complex64 = + ## Returns a `Complex64` with real part `re` and imaginary part `im`. result.re = re result.im = im -template im*(arg: typedesc[float32]): Complex32 = complex[float32](0, 1) -template im*(arg: typedesc[float64]): Complex64 = complex[float64](0, 1) -template im*(arg: float32): Complex32 = complex[float32](0, arg) -template im*(arg: float64): Complex64 = complex[float64](0, arg) +template im*(arg: typedesc[float32]): Complex32 = complex32(0, 1) + ## Returns the imaginary unit (`complex32(0, 1)`). +template im*(arg: typedesc[float64]): Complex64 = complex64(0, 1) + ## Returns the imaginary unit (`complex64(0, 1)`). +template im*(arg: float32): Complex32 = complex32(0, arg) + ## Returns `arg` as an imaginary number (`complex32(0, arg)`). +template im*(arg: float64): Complex64 = complex64(0, arg) + ## Returns `arg` as an imaginary number (`complex64(0, arg)`). func abs*[T](z: Complex[T]): T = - ## Returns the distance from (0,0) to ``z``. + ## Returns the absolute value of `z`, + ## that is the distance from (0, 0) to `z`. result = hypot(z.re, z.im) func abs2*[T](z: Complex[T]): T = - ## Returns the squared distance from (0,0) to ``z``. - result = z.re*z.re + z.im*z.im + ## Returns the squared absolute value of `z`, + ## that is the squared distance from (0, 0) to `z`. + ## This is more efficient than `abs(z) ^ 2`. + result = z.re * z.re + z.im * z.im func conjugate*[T](z: Complex[T]): Complex[T] = - ## Conjugates of complex number ``z``. + ## Returns the complex conjugate of `z` (`complex(z.re, -z.im)`). result.re = z.re result.im = -z.im func inv*[T](z: Complex[T]): Complex[T] = - ## Multiplicatives inverse of complex number ``z``. + ## Returns the multiplicative inverse of `z` (`1/z`). conjugate(z) / abs2(z) -func `==` *[T](x, y: Complex[T]): bool = - ## Compares two complex numbers ``x`` and ``y`` for equality. +func `==`*[T](x, y: Complex[T]): bool = + ## Compares two complex numbers for equality. result = x.re == y.re and x.im == y.im -func `+` *[T](x: T; y: Complex[T]): Complex[T] = +func `+`*[T](x: T; y: Complex[T]): Complex[T] = ## Adds a real number to a complex number. result.re = x + y.re result.im = y.im -func `+` *[T](x: Complex[T]; y: T): Complex[T] = +func `+`*[T](x: Complex[T]; y: T): Complex[T] = ## Adds a complex number to a real number. result.re = x.re + y result.im = x.im -func `+` *[T](x, y: Complex[T]): Complex[T] = +func `+`*[T](x, y: Complex[T]): Complex[T] = ## Adds two complex numbers. result.re = x.re + y.re result.im = x.im + y.im -func `-` *[T](z: Complex[T]): Complex[T] = +func `-`*[T](z: Complex[T]): Complex[T] = ## Unary minus for complex numbers. result.re = -z.re result.im = -z.im -func `-` *[T](x: T; y: Complex[T]): Complex[T] = +func `-`*[T](x: T; y: Complex[T]): Complex[T] = ## Subtracts a complex number from a real number. - x + (-y) + result.re = x - y.re + result.im = -y.im -func `-` *[T](x: Complex[T]; y: T): Complex[T] = +func `-`*[T](x: Complex[T]; y: T): Complex[T] = ## Subtracts a real number from a complex number. result.re = x.re - y result.im = x.im -func `-` *[T](x, y: Complex[T]): Complex[T] = +func `-`*[T](x, y: Complex[T]): Complex[T] = ## Subtracts two complex numbers. result.re = x.re - y.re result.im = x.im - y.im -func `/` *[T](x: Complex[T]; y: T): Complex[T] = - ## Divides complex number ``x`` by real number ``y``. +func `*`*[T](x: T; y: Complex[T]): Complex[T] = + ## Multiplies a real number with a complex number. + result.re = x * y.re + result.im = x * y.im + +func `*`*[T](x: Complex[T]; y: T): Complex[T] = + ## Multiplies a complex number with a real number. + result.re = x.re * y + result.im = x.im * y + +func `*`*[T](x, y: Complex[T]): Complex[T] = + ## Multiplies two complex numbers. + result.re = x.re * y.re - x.im * y.im + result.im = x.im * y.re + x.re * y.im + +func `/`*[T](x: Complex[T]; y: T): Complex[T] = + ## Divides a complex number by a real number. result.re = x.re / y result.im = x.im / y -func `/` *[T](x: T; y: Complex[T]): Complex[T] = - ## Divides real number ``x`` by complex number ``y``. +func `/`*[T](x: T; y: Complex[T]): Complex[T] = + ## Divides a real number by a complex number. result = x * inv(y) -func `/` *[T](x, y: Complex[T]): Complex[T] = - ## Divides ``x`` by ``y``. +func `/`*[T](x, y: Complex[T]): Complex[T] = + ## Divides two complex numbers. var r, den: T if abs(y.re) < abs(y.im): r = y.re / y.im @@ -119,45 +168,32 @@ func `/` *[T](x, y: Complex[T]): Complex[T] = result.re = (x.re + r * x.im) / den result.im = (x.im - r * x.re) / den -func `*` *[T](x: T; y: Complex[T]): Complex[T] = - ## Multiplies a real number and a complex number. - result.re = x * y.re - result.im = x * y.im -func `*` *[T](x: Complex[T]; y: T): Complex[T] = - ## Multiplies a complex number with a real number. - result.re = x.re * y - result.im = x.im * y - -func `*` *[T](x, y: Complex[T]): Complex[T] = - ## Multiplies ``x`` with ``y``. - result.re = x.re * y.re - x.im * y.im - result.im = x.im * y.re + x.re * y.im - - -func `+=` *[T](x: var Complex[T]; y: Complex[T]) = - ## Adds ``y`` to ``x``. +func `+=`*[T](x: var Complex[T]; y: Complex[T]) = + ## Adds `y` to `x`. x.re += y.re x.im += y.im -func `-=` *[T](x: var Complex[T]; y: Complex[T]) = - ## Subtracts ``y`` from ``x``. +func `-=`*[T](x: var Complex[T]; y: Complex[T]) = + ## Subtracts `y` from `x`. x.re -= y.re x.im -= y.im -func `*=` *[T](x: var Complex[T]; y: Complex[T]) = - ## Multiplies ``y`` to ``x``. +func `*=`*[T](x: var Complex[T]; y: Complex[T]) = + ## Multiplies `x` by `y`. let im = x.im * y.re + x.re * y.im x.re = x.re * y.re - x.im * y.im x.im = im -func `/=` *[T](x: var Complex[T]; y: Complex[T]) = - ## Divides ``x`` by ``y`` in place. +func `/=`*[T](x: var Complex[T]; y: Complex[T]) = + ## Divides `x` by `y` in place. x = x / y func sqrt*[T](z: Complex[T]): Complex[T] = - ## Square root for a complex number ``z``. + ## Computes the + ## ([principal](https://en.wikipedia.org/wiki/Square_root#Principal_square_root_of_a_complex_number)) + ## square root of a complex number `z`. var x, y, w, r: T if z.re == 0.0 and z.im == 0.0: @@ -180,28 +216,36 @@ func sqrt*[T](z: Complex[T]): Complex[T] = result.re = z.im / (result.im + result.im) func exp*[T](z: Complex[T]): Complex[T] = - ## ``e`` raised to the power ``z``. - var + ## Computes the exponential function (`e^z`). + let rho = exp(z.re) theta = z.im result.re = rho * cos(theta) result.im = rho * sin(theta) func ln*[T](z: Complex[T]): Complex[T] = - ## Returns the natural log of ``z``. + ## Returns the + ## ([principal value](https://en.wikipedia.org/wiki/Complex_logarithm#Principal_value) + ## of the) natural logarithm of `z`. result.re = ln(abs(z)) result.im = arctan2(z.im, z.re) func log10*[T](z: Complex[T]): Complex[T] = - ## Returns the log base 10 of ``z``. + ## Returns the logarithm base 10 of `z`. + ## + ## **See also:** + ## * `ln func<#ln,Complex[T]>`_ result = ln(z) / ln(10.0) func log2*[T](z: Complex[T]): Complex[T] = - ## Returns the log base 2 of ``z``. + ## Returns the logarithm base 2 of `z`. + ## + ## **See also:** + ## * `ln func<#ln,Complex[T]>`_ result = ln(z) / ln(2.0) func pow*[T](x, y: Complex[T]): Complex[T] = - ## ``x`` raised to the power ``y``. + ## `x` raised to the power of `y`. if x.re == 0.0 and x.im == 0.0: if y.re == 0.0 and y.im == 0.0: result.re = 1.0 @@ -214,7 +258,7 @@ func pow*[T](x, y: Complex[T]): Complex[T] = elif y.re == -1.0 and y.im == 0.0: result = T(1.0) / x else: - var + let rho = abs(x) theta = arctan2(x.im, x.re) s = pow(rho, y.re) * exp(-y.im * theta) @@ -223,126 +267,140 @@ func pow*[T](x, y: Complex[T]): Complex[T] = result.im = s * sin(r) func pow*[T](x: Complex[T]; y: T): Complex[T] = - ## Complex number ``x`` raised to the power ``y``. + ## The complex number `x` raised to the power of the real number `y`. pow(x, complex[T](y)) func sin*[T](z: Complex[T]): Complex[T] = - ## Returns the sine of ``z``. + ## Returns the sine of `z`. result.re = sin(z.re) * cosh(z.im) result.im = cos(z.re) * sinh(z.im) func arcsin*[T](z: Complex[T]): Complex[T] = - ## Returns the inverse sine of ``z``. + ## Returns the inverse sine of `z`. result = -im(T) * ln(im(T) * z + sqrt(T(1.0) - z*z)) func cos*[T](z: Complex[T]): Complex[T] = - ## Returns the cosine of ``z``. + ## Returns the cosine of `z`. result.re = cos(z.re) * cosh(z.im) result.im = -sin(z.re) * sinh(z.im) func arccos*[T](z: Complex[T]): Complex[T] = - ## Returns the inverse cosine of ``z``. + ## Returns the inverse cosine of `z`. result = -im(T) * ln(z + sqrt(z*z - T(1.0))) func tan*[T](z: Complex[T]): Complex[T] = - ## Returns the tangent of ``z``. + ## Returns the tangent of `z`. result = sin(z) / cos(z) func arctan*[T](z: Complex[T]): Complex[T] = - ## Returns the inverse tangent of ``z``. + ## Returns the inverse tangent of `z`. result = T(0.5)*im(T) * (ln(T(1.0) - im(T)*z) - ln(T(1.0) + im(T)*z)) func cot*[T](z: Complex[T]): Complex[T] = - ## Returns the cotangent of ``z``. + ## Returns the cotangent of `z`. result = cos(z)/sin(z) func arccot*[T](z: Complex[T]): Complex[T] = - ## Returns the inverse cotangent of ``z``. + ## Returns the inverse cotangent of `z`. result = T(0.5)*im(T) * (ln(T(1.0) - im(T)/z) - ln(T(1.0) + im(T)/z)) func sec*[T](z: Complex[T]): Complex[T] = - ## Returns the secant of ``z``. + ## Returns the secant of `z`. result = T(1.0) / cos(z) func arcsec*[T](z: Complex[T]): Complex[T] = - ## Returns the inverse secant of ``z``. + ## Returns the inverse secant of `z`. result = -im(T) * ln(im(T) * sqrt(1.0 - 1.0/(z*z)) + T(1.0)/z) func csc*[T](z: Complex[T]): Complex[T] = - ## Returns the cosecant of ``z``. + ## Returns the cosecant of `z`. result = T(1.0) / sin(z) func arccsc*[T](z: Complex[T]): Complex[T] = - ## Returns the inverse cosecant of ``z``. + ## Returns the inverse cosecant of `z`. result = -im(T) * ln(sqrt(T(1.0) - T(1.0)/(z*z)) + im(T)/z) func sinh*[T](z: Complex[T]): Complex[T] = - ## Returns the hyperbolic sine of ``z``. + ## Returns the hyperbolic sine of `z`. result = T(0.5) * (exp(z) - exp(-z)) func arcsinh*[T](z: Complex[T]): Complex[T] = - ## Returns the inverse hyperbolic sine of ``z``. + ## Returns the inverse hyperbolic sine of `z`. result = ln(z + sqrt(z*z + 1.0)) func cosh*[T](z: Complex[T]): Complex[T] = - ## Returns the hyperbolic cosine of ``z``. + ## Returns the hyperbolic cosine of `z`. result = T(0.5) * (exp(z) + exp(-z)) func arccosh*[T](z: Complex[T]): Complex[T] = - ## Returns the inverse hyperbolic cosine of ``z``. + ## Returns the inverse hyperbolic cosine of `z`. result = ln(z + sqrt(z*z - T(1.0))) func tanh*[T](z: Complex[T]): Complex[T] = - ## Returns the hyperbolic tangent of ``z``. + ## Returns the hyperbolic tangent of `z`. result = sinh(z) / cosh(z) func arctanh*[T](z: Complex[T]): Complex[T] = - ## Returns the inverse hyperbolic tangent of ``z``. + ## Returns the inverse hyperbolic tangent of `z`. result = T(0.5) * (ln((T(1.0)+z) / (T(1.0)-z))) -func sech*[T](z: Complex[T]): Complex[T] = - ## Returns the hyperbolic secant of ``z``. - result = T(2.0) / (exp(z) + exp(-z)) - -func arcsech*[T](z: Complex[T]): Complex[T] = - ## Returns the inverse hyperbolic secant of ``z``. - result = ln(1.0/z + sqrt(T(1.0)/z+T(1.0)) * sqrt(T(1.0)/z-T(1.0))) - -func csch*[T](z: Complex[T]): Complex[T] = - ## Returns the hyperbolic cosecant of ``z``. - result = T(2.0) / (exp(z) - exp(-z)) - -func arccsch*[T](z: Complex[T]): Complex[T] = - ## Returns the inverse hyperbolic cosecant of ``z``. - result = ln(T(1.0)/z + sqrt(T(1.0)/(z*z) + T(1.0))) - func coth*[T](z: Complex[T]): Complex[T] = - ## Returns the hyperbolic cotangent of ``z``. + ## Returns the hyperbolic cotangent of `z`. result = cosh(z) / sinh(z) func arccoth*[T](z: Complex[T]): Complex[T] = - ## Returns the inverse hyperbolic cotangent of ``z``. + ## Returns the inverse hyperbolic cotangent of `z`. result = T(0.5) * (ln(T(1.0) + T(1.0)/z) - ln(T(1.0) - T(1.0)/z)) +func sech*[T](z: Complex[T]): Complex[T] = + ## Returns the hyperbolic secant of `z`. + result = T(2.0) / (exp(z) + exp(-z)) + +func arcsech*[T](z: Complex[T]): Complex[T] = + ## Returns the inverse hyperbolic secant of `z`. + result = ln(1.0/z + sqrt(T(1.0)/z+T(1.0)) * sqrt(T(1.0)/z-T(1.0))) + +func csch*[T](z: Complex[T]): Complex[T] = + ## Returns the hyperbolic cosecant of `z`. + result = T(2.0) / (exp(z) - exp(-z)) + +func arccsch*[T](z: Complex[T]): Complex[T] = + ## Returns the inverse hyperbolic cosecant of `z`. + result = ln(T(1.0)/z + sqrt(T(1.0)/(z*z) + T(1.0))) + func phase*[T](z: Complex[T]): T = - ## Returns the phase of ``z``. + ## Returns the phase (or argument) of `z`, that is the angle in polar representation. + ## + ## | `result = arctan2(z.im, z.re)` arctan2(z.im, z.re) func polar*[T](z: Complex[T]): tuple[r, phi: T] = - ## Returns ``z`` in polar coordinates. + ## Returns `z` in polar coordinates. + ## + ## | `result.r = abs(z)` + ## | `result.phi = phase(z)` + ## + ## **See also:** + ## * `rect func<#rect,T,T>`_ for the inverse operation (r: abs(z), phi: phase(z)) func rect*[T](r, phi: T): Complex[T] = - ## Returns the complex number with polar coordinates ``r`` and ``phi``. + ## Returns the complex number with polar coordinates `r` and `phi`. ## - ## | ``result.re = r * cos(phi)`` - ## | ``result.im = r * sin(phi)`` + ## | `result.re = r * cos(phi)` + ## | `result.im = r * sin(phi)` + ## + ## **See also:** + ## * `polar func<#polar,Complex[T]>`_ for the inverse operation complex(r * cos(phi), r * sin(phi)) func `$`*(z: Complex): string = - ## Returns ``z``'s string representation as ``"(re, im)"``. + ## Returns `z`'s string representation as `"(re, im)"`. + runnableExamples: + doAssert $complex(1.0, 2.0) == "(1.0, 2.0)" + result = "(" & $z.re & ", " & $z.im & ")" {.pop.} diff --git a/tests/stdlib/tcomplex.nim b/tests/stdlib/tcomplex.nim index 8c9fa3055..15267b905 100644 --- a/tests/stdlib/tcomplex.nim +++ b/tests/stdlib/tcomplex.nim @@ -1,4 +1,4 @@ -import complex, math +import std/[complex, math] proc `=~`[T](x, y: Complex[T]): bool = @@ -7,7 +7,7 @@ proc `=~`[T](x, y: Complex[T]): bool = proc `=~`[T](x: Complex[T]; y: T): bool = result = abs(x.re-y) < 1e-6 and abs(x.im) < 1e-6 -var +let z: Complex64 = complex(0.0, 0.0) oo: Complex64 = complex(1.0, 1.0) a: Complex64 = complex(1.0, 2.0) @@ -76,12 +76,12 @@ doAssert(arccsch(a) =~ arcsinh(1.0/a)) doAssert(arccoth(a) =~ arctanh(1.0/a)) doAssert(phase(a) == 1.1071487177940904) -var t = polar(a) +let t = polar(a) doAssert(rect(t.r, t.phi) =~ a) doAssert(rect(1.0, 2.0) =~ complex(-0.4161468365471424, 0.9092974268256817)) -var +let i64: Complex32 = complex(0.0f, 1.0f) a64: Complex32 = 2.0f*i64 + 1.0.float32 b64: Complex32 = complex(-1.0'f32, -2.0'f32) @@ -96,7 +96,7 @@ doAssert(sin(arcsin(b64)) =~ b64) doAssert(cosh(arccosh(a64)) =~ a64) doAssert(phase(a64) - 1.107149f < 1e-6) -var t64 = polar(a64) +let t64 = polar(a64) doAssert(rect(t64.r, t64.phi) =~ a64) doAssert(rect(1.0f, 2.0f) =~ complex(-0.4161468f, 0.90929742f)) doAssert(sizeof(a64) == 8) @@ -104,5 +104,5 @@ doAssert(sizeof(a) == 16) doAssert 123.0.im + 456.0 == complex64(456, 123) -var localA = complex(0.1'f32) +let localA = complex(0.1'f32) doAssert localA.im is float32 From c04f305bf791ee5ecfd17f5a40d009d9c6b6f07a Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Tue, 5 Jan 2021 10:52:26 -0600 Subject: [PATCH 1289/3768] make cstrutils work in VM (#16590) * make cstrutils work in VM * more --- lib/pure/cstrutils.nim | 180 +++++++++++++++++++----------------- lib/pure/strutils.nim | 15 +-- lib/std/private/strimpl.nim | 29 +++++- tests/stdlib/tcstrutils.nim | 12 ++- 4 files changed, 132 insertions(+), 104 deletions(-) diff --git a/lib/pure/cstrutils.nim b/lib/pure/cstrutils.nim index 390aac00b..cdef7c804 100644 --- a/lib/pure/cstrutils.nim +++ b/lib/pure/cstrutils.nim @@ -16,93 +16,103 @@ import std/private/strimpl when defined(js): - func startsWith*(s, prefix: cstring): bool {.importjs: "#.startsWith(#)".} + func jsStartsWith(s, prefix: cstring): bool {.importjs: "#.startsWith(#)".} + func jsEndsWith(s, suffix: cstring): bool {.importjs: "#.endsWith(#)".} - func endsWith*(s, suffix: cstring): bool {.importjs: "#.endsWith(#)".} - func cmpIgnoreStyle*(a, b: cstring): int = +func startsWith*(s, prefix: cstring): bool {.rtl, extern: "csuStartsWith".} = + ## Returns true if `s` starts with `prefix`. + ## + ## JS backend uses native `String.prototype.startsWith`. + runnableExamples: + assert startsWith(cstring"Hello, Nimion", cstring"Hello") + assert not startsWith(cstring"Hello, Nimion", cstring"Nimion") + assert startsWith(cstring"Hello", cstring"") + when nimvm: + startsWithImpl(s, prefix) + else: + when defined(js): + result = jsStartsWith(s, prefix) + else: + var i = 0 + while true: + if prefix[i] == '\0': return true + if s[i] != prefix[i]: return false + inc(i) + +func endsWith*(s, suffix: cstring): bool {.rtl, extern: "csuEndsWith".} = + ## Returns true if `s` ends with `suffix`. + ## + ## JS backend uses native `String.prototype.endsWith`. + runnableExamples: + assert endsWith(cstring"Hello, Nimion", cstring"Nimion") + assert not endsWith(cstring"Hello, Nimion", cstring"Hello") + assert endsWith(cstring"Hello", cstring"") + when nimvm: + endsWithImpl(s, suffix) + else: + when defined(js): + result = jsEndsWith(s, suffix) + else: + let slen = s.len + var i = 0 + var j = slen - len(suffix) + while i+j <% slen: + if s[i+j] != suffix[i]: return false + inc(i) + if suffix[i] == '\0': return true + +func cmpIgnoreStyle*(a, b: cstring): int {.rtl, extern: "csuCmpIgnoreStyle".} = + ## Semantically the same as `cmp(normalize($a), normalize($b))`. It + ## is just optimized to not allocate temporary strings. This should + ## NOT be used to compare Nim identifier names. use `macros.eqIdent` + ## for that. Returns: + ## + ## .. code-block:: + ## 0 if a == b + ## < 0 if a < b + ## > 0 if a > b + runnableExamples: + assert cmpIgnoreStyle(cstring"hello", cstring"H_e_L_Lo") == 0 + when nimvm: cmpIgnoreStyleImpl(a, b) + else: + when defined(js): + cmpIgnoreStyleImpl(a, b) + else: + var i = 0 + var j = 0 + while true: + while a[i] == '_': inc(i) + while b[j] == '_': inc(j) # BUGFIX: typo + var aa = toLowerAscii(a[i]) + var bb = toLowerAscii(b[j]) + result = ord(aa) - ord(bb) + if result != 0 or aa == '\0': break + inc(i) + inc(j) - func cmpIgnoreCase*(a, b: cstring): int = +func cmpIgnoreCase*(a, b: cstring): int {.rtl, extern: "csuCmpIgnoreCase".} = + ## Compares two strings in a case insensitive manner. Returns: + ## + ## .. code-block:: + ## 0 if a == b + ## < 0 if a < b + ## > 0 if a > b + runnableExamples: + assert cmpIgnoreCase(cstring"hello", cstring"HeLLo") == 0 + assert cmpIgnoreCase(cstring"echo", cstring"hello") < 0 + assert cmpIgnoreCase(cstring"yellow", cstring"hello") > 0 + when nimvm: cmpIgnoreCaseImpl(a, b) - - # JS string has more operations that might warrant its own module: - # https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String -else: - func startsWith*(s, prefix: cstring): bool {.rtl, extern: "csuStartsWith".} = - ## Returns true if `s` starts with `prefix`. - ## - ## If `prefix == ""` true is returned. - ## - ## JS backend uses native `String.prototype.startsWith`. - runnableExamples: - assert startsWith(cstring"Hello, Nimion", cstring"Hello") - assert not startsWith(cstring"Hello, Nimion", cstring"Nimion") - - var i = 0 - while true: - if prefix[i] == '\0': return true - if s[i] != prefix[i]: return false - inc(i) - - func endsWith*(s, suffix: cstring): bool {.rtl, extern: "csuEndsWith".} = - ## Returns true if `s` ends with `suffix`. - ## - ## If `suffix == ""` true is returned. - ## - ## JS backend uses native `String.prototype.endsWith`. - runnableExamples: - assert endsWith(cstring"Hello, Nimion", cstring"Nimion") - assert not endsWith(cstring"Hello, Nimion", cstring"Hello") - - let slen = s.len - var i = 0 - var j = slen - len(suffix) - while i+j <% slen: - if s[i+j] != suffix[i]: return false - inc(i) - if suffix[i] == '\0': return true - - func cmpIgnoreStyle*(a, b: cstring): int {.rtl, extern: "csuCmpIgnoreStyle".} = - ## Semantically the same as `cmp(normalize($a), normalize($b))`. It - ## is just optimized to not allocate temporary strings. This should - ## NOT be used to compare Nim identifier names. use `macros.eqIdent` - ## for that. Returns: - ## - ## .. code-block:: - ## 0 if a == b - ## < 0 if a < b - ## > 0 if a > b - runnableExamples: - assert cmpIgnoreStyle(cstring"hello", cstring"H_e_L_Lo") == 0 - var i = 0 - var j = 0 - while true: - while a[i] == '_': inc(i) - while b[j] == '_': inc(j) # BUGFIX: typo - var aa = toLowerAscii(a[i]) - var bb = toLowerAscii(b[j]) - result = ord(aa) - ord(bb) - if result != 0 or aa == '\0': break - inc(i) - inc(j) - - func cmpIgnoreCase*(a, b: cstring): int {.rtl, extern: "csuCmpIgnoreCase".} = - ## Compares two strings in a case insensitive manner. Returns: - ## - ## .. code-block:: - ## 0 if a == b - ## < 0 if a < b - ## > 0 if a > b - runnableExamples: - assert cmpIgnoreCase(cstring"hello", cstring"HeLLo") == 0 - assert cmpIgnoreCase(cstring"echo", cstring"hello") < 0 - assert cmpIgnoreCase(cstring"yellow", cstring"hello") > 0 - - var i = 0 - while true: - var aa = toLowerAscii(a[i]) - var bb = toLowerAscii(b[i]) - result = ord(aa) - ord(bb) - if result != 0 or aa == '\0': break - inc(i) + else: + when defined(js): + cmpIgnoreCaseImpl(a, b) + else: + var i = 0 + while true: + var aa = toLowerAscii(a[i]) + var bb = toLowerAscii(b[i]) + result = ord(aa) - ord(bb) + if result != 0 or aa == '\0': break + inc(i) diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index b1418a3ec..a25df9e42 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -81,7 +81,7 @@ when defined(nimVmExportFixed): include "system/inclrtl" import std/private/since -from std/private/strimpl import cmpIgnoreStyleImpl, cmpIgnoreCaseImpl +from std/private/strimpl import cmpIgnoreStyleImpl, cmpIgnoreCaseImpl, startsWithImpl, endsWithImpl const @@ -1530,11 +1530,7 @@ func startsWith*(s, prefix: string): bool {.rtl, extern: "nsuStartsWith".} = let a = "abracadabra" doAssert a.startsWith("abra") == true doAssert a.startsWith("bra") == false - var i = 0 - while true: - if i >= prefix.len: return true - if i >= s.len or s[i] != prefix[i]: return false - inc(i) + startsWithImpl(s, prefix) func endsWith*(s: string, suffix: char): bool {.inline.} = ## Returns true if `s` ends with `suffix`. @@ -1562,12 +1558,7 @@ func endsWith*(s, suffix: string): bool {.rtl, extern: "nsuEndsWith".} = let a = "abracadabra" doAssert a.endsWith("abra") == true doAssert a.endsWith("dab") == false - var i = 0 - var j = len(s) - len(suffix) - while i+j >= 0 and i+j < s.len: - if s[i+j] != suffix[i]: return false - inc(i) - if i >= suffix.len: return true + endsWithImpl(s, suffix) func continuesWith*(s, substr: string, start: Natural): bool {.rtl, extern: "nsuContinuesWith".} = diff --git a/lib/std/private/strimpl.nim b/lib/std/private/strimpl.nim index ae752165a..3fa0dc1d3 100644 --- a/lib/std/private/strimpl.nim +++ b/lib/std/private/strimpl.nim @@ -4,13 +4,13 @@ func toLowerAscii*(c: char): char {.inline.} = else: result = c -template firstCharCaseSensitiveImpl(a, b: typed, aLen, bLen: int) = +template firstCharCaseSensitiveImpl[T: string | cstring](a, b: T, aLen, bLen: int) = if aLen == 0 or bLen == 0: return aLen - bLen if a[0] != b[0]: return ord(a[0]) - ord(b[0]) -template cmpIgnoreStyleImpl*(a, b: typed, firstCharCaseSensitive: static bool = false) = - # a, b are string or cstring +template cmpIgnoreStyleImpl*[T: string | cstring](a, b: T, + firstCharCaseSensitive: static bool = false) = let aLen = a.len let bLen = b.len var i = 0 @@ -37,8 +37,8 @@ template cmpIgnoreStyleImpl*(a, b: typed, firstCharCaseSensitive: static bool = inc i inc j -template cmpIgnoreCaseImpl*(a, b: typed, firstCharCaseSensitive: static bool = false) = - # a, b are string or cstring +template cmpIgnoreCaseImpl*[T: string | cstring](a, b: T, + firstCharCaseSensitive: static bool = false) = let aLen = a.len let bLen = b.len var i = 0 @@ -51,3 +51,22 @@ template cmpIgnoreCaseImpl*(a, b: typed, firstCharCaseSensitive: static bool = f if result != 0: return inc i result = aLen - bLen + +template startsWithImpl*[T: string | cstring](s, prefix: T) = + let prefixLen = prefix.len + let sLen = s.len + var i = 0 + while true: + if i >= prefixLen: return true + if i >= sLen or s[i] != prefix[i]: return false + inc(i) + +template endsWithImpl*[T: string | cstring](s, suffix: T) = + let suffixLen = suffix.len + let sLen = s.len + var i = 0 + var j = sLen - suffixLen + while i+j >= 0 and i+j < sLen: + if s[i+j] != suffix[i]: return false + inc(i) + if i >= suffixLen: return true diff --git a/tests/stdlib/tcstrutils.nim b/tests/stdlib/tcstrutils.nim index 1daf32aa5..ba3b1de68 100644 --- a/tests/stdlib/tcstrutils.nim +++ b/tests/stdlib/tcstrutils.nim @@ -2,21 +2,25 @@ discard """ targets: "c cpp js" """ -import cstrutils +import std/cstrutils -block tcstrutils: +proc main() = let s = cstring "abcdef" doAssert s.startsWith("a") doAssert not s.startsWith("b") doAssert s.endsWith("f") doAssert not s.endsWith("a") + doAssert s.startsWith("") + doAssert s.endsWith("") let a = cstring "abracadabra" doAssert a.startsWith("abra") doAssert not a.startsWith("bra") doAssert a.endsWith("abra") doAssert not a.endsWith("dab") + doAssert a.startsWith("") + doAssert a.endsWith("") doAssert cmpIgnoreCase(cstring "FooBar", "foobar") == 0 doAssert cmpIgnoreCase(cstring "bar", "Foo") < 0 @@ -28,3 +32,7 @@ block tcstrutils: doAssert cmpIgnoreCase(cstring "", cstring "") == 0 doAssert cmpIgnoreCase(cstring "", cstring "Hello") < 0 doAssert cmpIgnoreCase(cstring "wind", cstring "") > 0 + + +static: main() +main() From 2c2baa9fad1d144c42baf30ed004decdf8bb0483 Mon Sep 17 00:00:00 2001 From: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Date: Tue, 5 Jan 2021 20:51:51 +0100 Subject: [PATCH 1290/3768] Link the header (#16597) --- lib/pure/fenv.nim | 6 ++++-- tests/stdlib/tfenv.nim | 5 ++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/pure/fenv.nim b/lib/pure/fenv.nim index abddaf4cb..1a895bfa8 100644 --- a/lib/pure/fenv.nim +++ b/lib/pure/fenv.nim @@ -9,6 +9,8 @@ ## Floating-point environment. Handling of floating-point rounding and ## exceptions (overflow, division by zero, etc.). +## The types, vars and procs are bindings for the C standard library +## [](https://en.cppreference.com/w/c/numeric/fenv) header. when defined(Posix) and not defined(genode): {.passl: "-lm".} @@ -35,8 +37,8 @@ var FE_UPWARD* {.importc, header: "".}: cint ## round toward +Inf FE_DFL_ENV* {.importc, header: "".}: cint - ## macro of type pointer to fenv_t to be used as the argument - ## to functions taking an argument of type fenv_t; in this + ## macro of type pointer to `fenv_t` to be used as the argument + ## to functions taking an argument of type `fenv_t`; in this ## case the default environment will be used type diff --git a/tests/stdlib/tfenv.nim b/tests/stdlib/tfenv.nim index f58acf1c8..5bcd1ea7c 100644 --- a/tests/stdlib/tfenv.nim +++ b/tests/stdlib/tfenv.nim @@ -1,8 +1,7 @@ -import fenv +import std/fenv func is_significant(x: float): bool = - if x > minimumPositiveValue(float) and x < maximumPositiveValue(float): true - else: false + x > minimumPositiveValue(float) and x < maximumPositiveValue(float) doAssert is_significant(10.0) From d721f5cecad90a0aa7e2ea144607ffafdf647e31 Mon Sep 17 00:00:00 2001 From: n5m <72841454+n5m@users.noreply.github.com> Date: Wed, 6 Jan 2021 02:23:59 +0000 Subject: [PATCH 1291/3768] fix syntax in macros documentation (#16604) --- doc/astspec.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/astspec.txt b/doc/astspec.txt index 019b735f5..e7bbcecc2 100644 --- a/doc/astspec.txt +++ b/doc/astspec.txt @@ -1033,7 +1033,7 @@ AST: nnkEmpty(), # no pragmas here nnkOfInherit( nnkIdent("RootObj") # inherits from RootObj - ) + ), nnkEmpty() ) ) From 58b9191354aa99ac2d17f9a7db3bdf239c7bce6b Mon Sep 17 00:00:00 2001 From: cooldome Date: Wed, 6 Jan 2021 10:47:03 +0000 Subject: [PATCH 1292/3768] fix #16516 method dispatch for sink args (#16594) * fix #16516 * fix comment * Trigger build --- compiler/cgmeth.nim | 2 +- tests/method/tmethod_issues.nim | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim index 5c5d35093..a995804c7 100644 --- a/compiler/cgmeth.nim +++ b/compiler/cgmeth.nim @@ -67,7 +67,7 @@ proc sameMethodBucket(a, b: PSym; multiMethods: bool): MethodResult = while true: aa = skipTypes(aa, {tyGenericInst, tyAlias}) bb = skipTypes(bb, {tyGenericInst, tyAlias}) - if aa.kind == bb.kind and aa.kind in {tyVar, tyPtr, tyRef, tyLent}: + if aa.kind == bb.kind and aa.kind in {tyVar, tyPtr, tyRef, tyLent, tySink}: aa = aa.lastSon bb = bb.lastSon else: diff --git a/tests/method/tmethod_issues.nim b/tests/method/tmethod_issues.nim index 80f54caee..df4c3771a 100644 --- a/tests/method/tmethod_issues.nim +++ b/tests/method/tmethod_issues.nim @@ -2,6 +2,8 @@ discard """ output: ''' wof! wof! +type A +type B ''' """ @@ -126,3 +128,34 @@ var obj2 = Class2() obj1.test(obj2) obj2.test(obj1) + + +# ------------------------------------------------------- +# issue #16516 + +type + A = ref object of RootObj + x: int + + B = ref object of A + +method foo(v: sink A, lst: var seq[A]) {.base,locks:0.} = + echo "type A" + lst.add v + +method foo(v: sink B, lst: var seq[A]) = + echo "type B" + lst.add v + +proc main() = + let + a = A(x: 5) + b: A = B(x: 5) + + var lst: seq[A] + + foo(a, lst) + foo(b, lst) + +main() + From 0d5cab77f65df1431ed417a666ba49136af3b2c1 Mon Sep 17 00:00:00 2001 From: inv2004 Date: Wed, 6 Jan 2021 20:42:49 +0300 Subject: [PATCH 1293/3768] jsonutils: fromJson forward opt param fix (#16612) --- lib/std/jsonutils.nim | 8 ++++---- tests/stdlib/tjsonutils.nim | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/lib/std/jsonutils.nim b/lib/std/jsonutils.nim index 24935f511..4dca024c1 100644 --- a/lib/std/jsonutils.nim +++ b/lib/std/jsonutils.nim @@ -201,17 +201,17 @@ proc fromJson*[T](a: var T, b: JsonNode, opt = Joptions()) = if b.kind == JNull: a = nil else: a = T() - fromJson(a[], b) + fromJson(a[], b, opt) elif T is array: checkJson a.len == b.len, $(a.len, b.len, $T) var i = 0 for ai in mitems(a): - fromJson(ai, b[i]) + fromJson(ai, b[i], opt) i.inc elif T is seq: a.setLen b.len for i, val in b.getElems: - fromJson(a[i], val) + fromJson(a[i], val, opt) elif T is object: template fun(key, typ): untyped {.used.} = if b.hasKey key: @@ -237,7 +237,7 @@ proc fromJson*[T](a: var T, b: JsonNode, opt = Joptions()) = checkJson b.kind == JArray, $(b.kind) # we could customize whether to allow JNull var i = 0 for val in fields(a): - fromJson(val, b[i]) + fromJson(val, b[i], opt) i.inc checkJson b.len == i, $(b.len, i, $T, b) # could customize else: diff --git a/tests/stdlib/tjsonutils.nim b/tests/stdlib/tjsonutils.nim index 28f05ecbe..f56c32719 100644 --- a/tests/stdlib/tjsonutils.nim +++ b/tests/stdlib/tjsonutils.nim @@ -118,6 +118,20 @@ template fn() = testRoundtrip(Foo[int](t1: false, z2: 7)): """{"t1":false,"z2":7}""" # pending https://github.com/nim-lang/Nim/issues/14698, test with `type Foo[T] = ref object` + block: # bug: pass opt params in fromJson + type Foo = object + a: int + b: string + c: float + var f: seq[Foo] + try: + fromJson(f, parseJson """[{"b": "bbb"}]""") + doAssert false + except ValueError: + doAssert true + fromJson(f, parseJson """[{"b": "bbb"}]""", Joptions(allowExtraKeys: true, allowMissingKeys: true)) + doAssert f == @[Foo(a: 0, b: "bbb", c: 0.0)] + block testHashSet: testRoundtrip(HashSet[string]()): "[]" testRoundtrip([""].toHashSet): """[""]""" From c21360e67accbbad0c24c84432fdb4506f68c881 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 6 Jan 2021 10:28:17 -0800 Subject: [PATCH 1294/3768] macros.quote: document hard to use `op`; add more useful examples (#16489) * macros.quote: document hard to use `op`; add more useful examples * add back doc comment removed in a60305fbf3897cd90680e693dd4c0db2334d85d4 * address comment * fixup * clarify quoting rules * Update lib/core/macros.nim Co-authored-by: Clyybber Co-authored-by: Clyybber --- lib/core/macros.nim | 94 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 71 insertions(+), 23 deletions(-) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 748464061..204123f41 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -566,34 +566,82 @@ proc getAst*(macroOrTemplate: untyped): NimNode {.magic: "ExpandToAst", noSideEf ## macro FooMacro() = ## var ast = getAst(BarTemplate()) -proc quote*(bl: typed, op = "``"): NimNode {.magic: "QuoteAst", noSideEffect.} +proc quote*(bl: typed, op = "``"): NimNode {.magic: "QuoteAst", noSideEffect.} = ## Quasi-quoting operator. ## Accepts an expression or a block and returns the AST that represents it. ## Within the quoted AST, you are able to interpolate NimNode expressions ## from the surrounding scope. If no operator is given, quoting is done using ## backticks. Otherwise, the given operator must be used as a prefix operator - ## for any interpolated expression. - ## - ## Example: - ## - ## .. code-block:: nim - ## - ## macro check(ex: untyped) = - ## # this is a simplified version of the check macro from the - ## # unittest module. - ## - ## # If there is a failed check, we want to make it easy for - ## # the user to jump to the faulty line in the code, so we - ## # get the line info here: - ## var info = ex.lineinfo - ## - ## # We will also display the code string of the failed check: - ## var expString = ex.toStrLit - ## - ## # Finally we compose the code to implement the check: - ## result = quote do: - ## if not `ex`: - ## echo `info` & ": Check failed: " & `expString` + ## for any interpolated expression. The original meaning of the interpolation + ## operator may be obtained by escaping it (by prefixing it with itself) when used + ## as a unary operator: + ## e.g. `@` is escaped as `@@`, `&%` is escaped as `&%&%` and so on; see examples. + runnableExamples: + macro check(ex: untyped) = + # this is a simplified version of the check macro from the + # unittest module. + + # If there is a failed check, we want to make it easy for + # the user to jump to the faulty line in the code, so we + # get the line info here: + var info = ex.lineinfo + + # We will also display the code string of the failed check: + var expString = ex.toStrLit + + # Finally we compose the code to implement the check: + result = quote do: + if not `ex`: + echo `info` & ": Check failed: " & `expString` + check 1 + 1 == 2 + + runnableExamples: + # example showing how to define a symbol that requires backtick without + # quoting it. + var destroyCalled = false + macro bar() = + let s = newTree(nnkAccQuoted, ident"=destroy") + # let s = ident"`=destroy`" # this would not work + result = quote do: + type Foo = object + # proc `=destroy`(a: var Foo) = destroyCalled = true # this would not work + proc `s`(a: var Foo) = destroyCalled = true + block: + let a = Foo() + bar() + doAssert destroyCalled + + runnableExamples: + # custom `op` + var destroyCalled = false + macro bar() = + var x = 1.5 + result = quote("@") do: + type Foo = object + proc `=destroy`(a: var Foo) = + doAssert @x == 1.5 + doAssert compiles(@x == 1.5) + let b1 = @[1,2] + let b2 = @@[1,2] + doAssert $b1 == "[1, 2]" + doAssert $b2 == "@[1, 2]" + destroyCalled = true + block: + let a = Foo() + bar() + doAssert destroyCalled + + proc `&%`(x: int): int = 1 + proc `&%`(x, y: int): int = 2 + + macro bar2() = + var x = 3 + result = quote("&%") do: + var y = &%x # quoting operator + doAssert &%&%y == 1 # unary operator => need to escape + doAssert y &% y == 2 # binary operator => no need to escape + doAssert y == 3 + bar2() proc expectKind*(n: NimNode, k: NimNodeKind) {.compileTime.} = ## Checks that `n` is of kind `k`. If this is not the case, From 8a3b6190c3559061ca43cd73faba1a44170b1ee6 Mon Sep 17 00:00:00 2001 From: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Date: Wed, 6 Jan 2021 20:16:26 +0100 Subject: [PATCH 1295/3768] Improve documentation for deques (#16589) --- lib/pure/collections/deques.nim | 262 +++++++++++++------------------- 1 file changed, 106 insertions(+), 156 deletions(-) diff --git a/lib/pure/collections/deques.nim b/lib/pure/collections/deques.nim index f6d0f945e..7614b9d20 100644 --- a/lib/pure/collections/deques.nim +++ b/lib/pure/collections/deques.nim @@ -7,25 +7,24 @@ # distribution, for details about the copyright. # -## Implementation of a `deque`:idx: (double-ended queue). -## The underlying implementation uses a ``seq``. +## An implementation of a `deque`:idx: (double-ended queue). +## The underlying implementation uses a `seq`. ## -## None of the procs that get an individual value from the deque can be used +## Note that none of the procs that get an individual value from the deque should be used ## on an empty deque. -## If compiled with `boundChecks` option, those procs will raise an `IndexDefect` +## If compiled with the `boundChecks` option, those procs will raise an `IndexDefect` ## on such access. This should not be relied upon, as `-d:danger` or `--checks:off` will -## disable those checks and may return garbage or crash the program. +## disable those checks and then the procs may return garbage or crash the program. ## ## As such, a check to see if the deque is empty is needed before any ## access, unless your program logic guarantees it indirectly. runnableExamples: - var a = initDeque[int]() + var a = [10, 20, 30, 40].toDeque - doAssertRaises(IndexDefect, echo a[0]) + doAssertRaises(IndexDefect, echo a[4]) - for i in 1 .. 5: - a.addLast(10*i) + a.addLast(50) assert $a == "[10, 20, 30, 40, 50]" assert a.peekFirst == 10 @@ -44,7 +43,8 @@ runnableExamples: a.shrink(fromFirst = 1, fromLast = 2) assert $a == "[22, 11, 20]" -## **See also:** +## See also +## ======== ## * `lists module `_ for singly and doubly linked lists and rings ## * `channels module `_ for inter-thread communication @@ -54,9 +54,10 @@ import math type Deque*[T] = object - ## A double-ended queue backed with a ringed seq buffer. + ## A double-ended queue backed with a ringed `seq` buffer. ## - ## To initialize an empty deque use `initDeque proc <#initDeque,int>`_. + ## To initialize an empty deque, + ## use the `initDeque proc <#initDeque,int>`_. data: seq[T] head, tail, count, mask: int @@ -65,7 +66,7 @@ const template initImpl(result: typed, initialSize: int) = let correctSize = nextPowerOfTwo(initialSize) - result.mask = correctSize-1 + result.mask = correctSize - 1 newSeq(result.data, correctSize) template checkIfInitialized(deq: typed) = @@ -73,27 +74,27 @@ template checkIfInitialized(deq: typed) = if deq.mask == 0: initImpl(deq, defaultInitialSize) -proc initDeque*[T](initialSize: int = 4): Deque[T] = +proc initDeque*[T](initialSize: int = defaultInitialSize): Deque[T] = ## Creates a new empty deque. ## ## Optionally, the initial capacity can be reserved via `initialSize` - ## as a performance optimization. + ## as a performance optimization + ## (default: `defaultInitialSize <#defaultInitialSize>`_). ## The length of a newly created deque will still be 0. ## - ## See also: + ## **See also:** ## * `toDeque proc <#toDeque,openArray[T]>`_ result.initImpl(initialSize) proc toDeque*[T](x: openArray[T]): Deque[T] {.since: (1, 3).} = ## Creates a new deque that contains the elements of `x` (in the same order). ## - ## See also: + ## **See also:** ## * `initDeque proc <#initDeque,int>`_ runnableExamples: - var a = toDeque([7, 8, 9]) + let a = toDeque([7, 8, 9]) assert len(a) == 3 - assert a.popFirst == 7 - assert len(a) == 2 + assert $a == "[7, 8, 9]" result.initImpl(x.len) for item in items(x): @@ -120,11 +121,9 @@ template xBoundsCheck(deq, i) = "Out of bounds: " & $i & " < 0") proc `[]`*[T](deq: Deque[T], i: Natural): T {.inline.} = - ## Accesses the i-th element of `deq`. + ## Accesses the `i`-th element of `deq`. runnableExamples: - var a = initDeque[int]() - for i in 1 .. 5: - a.addLast(10*i) + let a = [10, 20, 30, 40, 50].toDeque assert a[0] == 10 assert a[3] == 40 doAssertRaises(IndexDefect, echo a[8]) @@ -133,25 +132,20 @@ proc `[]`*[T](deq: Deque[T], i: Natural): T {.inline.} = return deq.data[(deq.head + i) and deq.mask] proc `[]`*[T](deq: var Deque[T], i: Natural): var T {.inline.} = - ## Accesses the i-th element of `deq` and return a mutable + ## Accesses the `i`-th element of `deq` and returns a mutable ## reference to it. runnableExamples: - var a = initDeque[int]() - for i in 1 .. 5: - a.addLast(10*i) - assert a[0] == 10 - assert a[3] == 40 - doAssertRaises(IndexDefect, echo a[8]) + var a = [10, 20, 30, 40, 50].toDeque + inc(a[0]) + assert a[0] == 11 xBoundsCheck(deq, i) return deq.data[(deq.head + i) and deq.mask] proc `[]=`*[T](deq: var Deque[T], i: Natural, val: T) {.inline.} = - ## Changes the i-th element of `deq`. + ## Sets the `i`-th element of `deq` to `val`. runnableExamples: - var a = initDeque[int]() - for i in 1 .. 5: - a.addLast(10*i) + var a = [10, 20, 30, 40, 50].toDeque a[0] = 99 a[3] = 66 assert $a == "[99, 20, 30, 66, 50]" @@ -161,13 +155,11 @@ proc `[]=`*[T](deq: var Deque[T], i: Natural, val: T) {.inline.} = deq.data[(deq.head + i) and deq.mask] = val proc `[]`*[T](deq: Deque[T], i: BackwardsIndex): T {.inline.} = - ## Accesses the backwards indexed i-th element. + ## Accesses the backwards indexed `i`-th element. ## ## `deq[^1]` is the last element. runnableExamples: - var a = initDeque[int]() - for i in 1 .. 5: - a.addLast(10*i) + let a = [10, 20, 30, 40, 50].toDeque assert a[^1] == 50 assert a[^4] == 20 doAssertRaises(IndexDefect, echo a[^9]) @@ -176,28 +168,24 @@ proc `[]`*[T](deq: Deque[T], i: BackwardsIndex): T {.inline.} = return deq[deq.len - int(i)] proc `[]`*[T](deq: var Deque[T], i: BackwardsIndex): var T {.inline.} = - ## Accesses the backwards indexed i-th element. + ## Accesses the backwards indexed `i`-th element and returns a mutable + ## reference to it. ## ## `deq[^1]` is the last element. runnableExamples: - var a = initDeque[int]() - for i in 1 .. 5: - a.addLast(10*i) - assert a[^1] == 50 - assert a[^4] == 20 - doAssertRaises(IndexDefect, echo a[^9]) + var a = [10, 20, 30, 40, 50].toDeque + inc(a[^1]) + assert a[^1] == 51 xBoundsCheck(deq, deq.len - int(i)) return deq[deq.len - int(i)] proc `[]=`*[T](deq: var Deque[T], i: BackwardsIndex, x: T) {.inline.} = - ## Changes the backwards indexed i-th element. + ## Sets the backwards indexed `i`-th element of `deq` to `x`. ## ## `deq[^1]` is the last element. runnableExamples: - var a = initDeque[int]() - for i in 1 .. 5: - a.addLast(10*i) + var a = [10, 20, 30, 40, 50].toDeque a[^1] = 99 a[^3] = 77 assert $a == "[10, 20, 77, 40, 99]" @@ -208,14 +196,15 @@ proc `[]=`*[T](deq: var Deque[T], i: BackwardsIndex, x: T) {.inline.} = iterator items*[T](deq: Deque[T]): T = ## Yields every element of `deq`. + ## + ## **See also:** + ## * `mitems iterator <#mitems,Deque[T]>`_ runnableExamples: - var a = initDeque[int]() - for i in 1 .. 3: - a.addLast(10*i) - from sugar import collect - doAssert collect(for x in a: x) == [10, 20, 30] - # same as above: - doAssert collect(for x in items(a): x) == [10, 20, 30] + from sequtils import toSeq + + let a = [10, 20, 30, 40, 50].toDeque + assert toSeq(a.items) == @[10, 20, 30, 40, 50] + var i = deq.head for c in 0 ..< deq.count: yield deq.data[i] @@ -223,13 +212,14 @@ iterator items*[T](deq: Deque[T]): T = iterator mitems*[T](deq: var Deque[T]): var T = ## Yields every element of `deq`, which can be modified. + ## + ## **See also:** + ## * `items iterator <#items,Deque[T]>`_ runnableExamples: - var a = initDeque[int]() - for i in 1 .. 5: - a.addLast(10*i) + var a = [10, 20, 30, 40, 50].toDeque assert $a == "[10, 20, 30, 40, 50]" for x in mitems(a): - x = 5*x - 1 + x = 5 * x - 1 assert $a == "[49, 99, 149, 199, 249]" var i = deq.head @@ -238,13 +228,13 @@ iterator mitems*[T](deq: var Deque[T]): var T = i = (i + 1) and deq.mask iterator pairs*[T](deq: Deque[T]): tuple[key: int, val: T] = - ## Yields every (position, value) of `deq`. + ## Yields every `(position, value)`-pair of `deq`. runnableExamples: - var a = initDeque[int]() - for i in 1 .. 3: - a.addLast(10*i) - from sugar import collect - doAssert collect(for k, v in pairs(a): (k, v)) == @[(0, 10), (1, 20), (2, 30)] + from sequtils import toSeq + + let a = [10, 20, 30].toDeque + assert toSeq(a.pairs) == @[(0, 10), (1, 20), (2, 30)] + var i = deq.head for c in 0 ..< deq.count: yield (c, deq.data[i]) @@ -253,13 +243,14 @@ iterator pairs*[T](deq: Deque[T]): tuple[key: int, val: T] = proc contains*[T](deq: Deque[T], item: T): bool {.inline.} = ## Returns true if `item` is in `deq` or false if not found. ## - ## Usually used via the ``in`` operator. - ## It is the equivalent of ``deq.find(item) >= 0``. + ## Usually used via the `in` operator. + ## It is the equivalent of `deq.find(item) >= 0`. runnableExamples: - var q = [7, 9].toDeque + let q = [7, 9].toDeque assert 7 in q - assert q.contains 7 + assert q.contains(7) assert 8 notin q + for e in deq: if e == item: return true return false @@ -280,18 +271,14 @@ proc expandIfNeeded[T](deq: var Deque[T]) = deq.head = 0 proc addFirst*[T](deq: var Deque[T], item: T) = - ## Adds an `item` to the beginning of the `deq`. + ## Adds an `item` to the beginning of `deq`. ## - ## See also: + ## **See also:** ## * `addLast proc <#addLast,Deque[T],T>`_ - ## * `peekFirst proc <#peekFirst,Deque[T]>`_ - ## * `peekLast proc <#peekLast,Deque[T]>`_ - ## * `popFirst proc <#popFirst,Deque[T]>`_ - ## * `popLast proc <#popLast,Deque[T]>`_ runnableExamples: var a = initDeque[int]() for i in 1 .. 5: - a.addFirst(10*i) + a.addFirst(10 * i) assert $a == "[50, 40, 30, 20, 10]" expandIfNeeded(deq) @@ -300,18 +287,14 @@ proc addFirst*[T](deq: var Deque[T], item: T) = deq.data[deq.head] = item proc addLast*[T](deq: var Deque[T], item: T) = - ## Adds an `item` to the end of the `deq`. + ## Adds an `item` to the end of `deq`. ## - ## See also: + ## **See also:** ## * `addFirst proc <#addFirst,Deque[T],T>`_ - ## * `peekFirst proc <#peekFirst,Deque[T]>`_ - ## * `peekLast proc <#peekLast,Deque[T]>`_ - ## * `popFirst proc <#popFirst,Deque[T]>`_ - ## * `popLast proc <#popLast,Deque[T]>`_ runnableExamples: var a = initDeque[int]() for i in 1 .. 5: - a.addLast(10*i) + a.addLast(10 * i) assert $a == "[10, 20, 30, 40, 50]" expandIfNeeded(deq) @@ -322,16 +305,11 @@ proc addLast*[T](deq: var Deque[T], item: T) = proc peekFirst*[T](deq: Deque[T]): T {.inline.} = ## Returns the first element of `deq`, but does not remove it from the deque. ## - ## See also: - ## * `addFirst proc <#addFirst,Deque[T],T>`_ - ## * `addLast proc <#addLast,Deque[T],T>`_ + ## **See also:** + ## * `peekFirst proc <#peekFirst,Deque[T]_2>`_ which returns a mutable reference ## * `peekLast proc <#peekLast,Deque[T]>`_ - ## * `popFirst proc <#popFirst,Deque[T]>`_ - ## * `popLast proc <#popLast,Deque[T]>`_ runnableExamples: - var a = initDeque[int]() - for i in 1 .. 5: - a.addLast(10*i) + let a = [10, 20, 30, 40, 50].toDeque assert $a == "[10, 20, 30, 40, 50]" assert a.peekFirst == 10 assert len(a) == 5 @@ -342,16 +320,11 @@ proc peekFirst*[T](deq: Deque[T]): T {.inline.} = proc peekLast*[T](deq: Deque[T]): T {.inline.} = ## Returns the last element of `deq`, but does not remove it from the deque. ## - ## See also: - ## * `addFirst proc <#addFirst,Deque[T],T>`_ - ## * `addLast proc <#addLast,Deque[T],T>`_ + ## **See also:** + ## * `peekLast proc <#peekLast,Deque[T]_2>`_ which returns a mutable reference ## * `peekFirst proc <#peekFirst,Deque[T]>`_ - ## * `popFirst proc <#popFirst,Deque[T]>`_ - ## * `popLast proc <#popLast,Deque[T]>`_ runnableExamples: - var a = initDeque[int]() - for i in 1 .. 5: - a.addLast(10*i) + let a = [10, 20, 30, 40, 50].toDeque assert $a == "[10, 20, 30, 40, 50]" assert a.peekLast == 50 assert len(a) == 5 @@ -360,41 +333,31 @@ proc peekLast*[T](deq: Deque[T]): T {.inline.} = result = deq.data[(deq.tail - 1) and deq.mask] proc peekFirst*[T](deq: var Deque[T]): var T {.inline, since: (1, 3).} = - ## Returns the first element of `deq`, but does not remove it from the deque. + ## Returns a mutable reference to the first element of `deq`, + ## but does not remove it from the deque. ## - ## See also: - ## * `addFirst proc <#addFirst,Deque[T],T>`_ - ## * `addLast proc <#addLast,Deque[T],T>`_ - ## * `peekLast proc <#peekLast,Deque[T]>`_ - ## * `popFirst proc <#popFirst,Deque[T]>`_ - ## * `popLast proc <#popLast,Deque[T]>`_ + ## **See also:** + ## * `peekFirst proc <#peekFirst,Deque[T]>`_ + ## * `peekLast proc <#peekLast,Deque[T]_2>`_ runnableExamples: - var a = initDeque[int]() - for i in 1 .. 5: - a.addLast(10*i) - assert $a == "[10, 20, 30, 40, 50]" - assert a.peekFirst == 10 - assert len(a) == 5 + var a = [10, 20, 30, 40, 50].toDeque + a.peekFirst() = 99 + assert $a == "[99, 20, 30, 40, 50]" emptyCheck(deq) result = deq.data[deq.head] proc peekLast*[T](deq: var Deque[T]): var T {.inline, since: (1, 3).} = - ## Returns the last element of `deq`, but does not remove it from the deque. + ## Returns a mutable reference to the last element of `deq`, + ## but does not remove it from the deque. ## - ## See also: - ## * `addFirst proc <#addFirst,Deque[T],T>`_ - ## * `addLast proc <#addLast,Deque[T],T>`_ - ## * `peekFirst proc <#peekFirst,Deque[T]>`_ - ## * `popFirst proc <#popFirst,Deque[T]>`_ - ## * `popLast proc <#popLast,Deque[T]>`_ + ## **See also:** + ## * `peekFirst proc <#peekFirst,Deque[T]_2>`_ + ## * `peekLast proc <#peekLast,Deque[T]>`_ runnableExamples: - var a = initDeque[int]() - for i in 1 .. 5: - a.addLast(10*i) - assert $a == "[10, 20, 30, 40, 50]" - assert a.peekLast == 50 - assert len(a) == 5 + var a = [10, 20, 30, 40, 50].toDeque + a.peekLast() = 99 + assert $a == "[10, 20, 30, 40, 99]" emptyCheck(deq) result = deq.data[(deq.tail - 1) and deq.mask] @@ -406,17 +369,10 @@ proc popFirst*[T](deq: var Deque[T]): T {.inline, discardable.} = ## Removes and returns the first element of the `deq`. ## ## See also: - ## * `addFirst proc <#addFirst,Deque[T],T>`_ - ## * `addLast proc <#addLast,Deque[T],T>`_ - ## * `peekFirst proc <#peekFirst,Deque[T]>`_ - ## * `peekLast proc <#peekLast,Deque[T]>`_ ## * `popLast proc <#popLast,Deque[T]>`_ - ## * `clear proc <#clear,Deque[T]>`_ ## * `shrink proc <#shrink,Deque[T],int,int>`_ runnableExamples: - var a = initDeque[int]() - for i in 1 .. 5: - a.addLast(10*i) + var a = [10, 20, 30, 40, 50].toDeque assert $a == "[10, 20, 30, 40, 50]" assert a.popFirst == 10 assert $a == "[20, 30, 40, 50]" @@ -430,18 +386,11 @@ proc popFirst*[T](deq: var Deque[T]): T {.inline, discardable.} = proc popLast*[T](deq: var Deque[T]): T {.inline, discardable.} = ## Removes and returns the last element of the `deq`. ## - ## See also: - ## * `addFirst proc <#addFirst,Deque[T],T>`_ - ## * `addLast proc <#addLast,Deque[T],T>`_ - ## * `peekFirst proc <#peekFirst,Deque[T]>`_ - ## * `peekLast proc <#peekLast,Deque[T]>`_ + ## **See also:** ## * `popFirst proc <#popFirst,Deque[T]>`_ - ## * `clear proc <#clear,Deque[T]>`_ ## * `shrink proc <#shrink,Deque[T],int,int>`_ runnableExamples: - var a = initDeque[int]() - for i in 1 .. 5: - a.addLast(10*i) + var a = [10, 20, 30, 40, 50].toDeque assert $a == "[10, 20, 30, 40, 50]" assert a.popLast == 50 assert $a == "[10, 20, 30, 40]" @@ -455,14 +404,11 @@ proc popLast*[T](deq: var Deque[T]): T {.inline, discardable.} = proc clear*[T](deq: var Deque[T]) {.inline.} = ## Resets the deque so that it is empty. ## - ## See also: - ## * `clear proc <#clear,Deque[T]>`_ + ## **See also:** ## * `shrink proc <#shrink,Deque[T],int,int>`_ runnableExamples: - var a = initDeque[int]() - for i in 1 .. 5: - a.addFirst(10*i) - assert $a == "[50, 40, 30, 20, 10]" + var a = [10, 20, 30, 40, 50].toDeque + assert $a == "[10, 20, 30, 40, 50]" clear(a) assert len(a) == 0 @@ -477,15 +423,15 @@ proc shrink*[T](deq: var Deque[T], fromFirst = 0, fromLast = 0) = ## If the supplied number of elements exceeds the total number of elements ## in the deque, the deque will remain empty. ## - ## See also: + ## **See also:** ## * `clear proc <#clear,Deque[T]>`_ + ## * `popFirst proc <#popFirst,Deque[T]>`_ + ## * `popLast proc <#popLast,Deque[T]>`_ runnableExamples: - var a = initDeque[int]() - for i in 1 .. 5: - a.addFirst(10*i) - assert $a == "[50, 40, 30, 20, 10]" + var a = [10, 20, 30, 40, 50].toDeque + assert $a == "[10, 20, 30, 40, 50]" a.shrink(fromFirst = 2, fromLast = 1) - assert $a == "[30, 20]" + assert $a == "[30, 40]" if fromFirst + fromLast > deq.count: clear(deq) @@ -503,6 +449,10 @@ proc shrink*[T](deq: var Deque[T], fromFirst = 0, fromLast = 0) = proc `$`*[T](deq: Deque[T]): string = ## Turns a deque into its string representation. + runnableExamples: + let a = [10, 20, 30].toDeque + assert $a == "[10, 20, 30]" + result = "[" for x in deq: if result.len > 1: result.add(", ") From 21dfa04cbf638f4059244b4cecf1906b84889a1e Mon Sep 17 00:00:00 2001 From: Saem Ghani Date: Wed, 6 Jan 2021 11:26:16 -0800 Subject: [PATCH 1296/3768] fixes nim-lang/nimsuggest#119 outline includes (#16608) nimsuggest outline should account for includes, now it does: - the module prefix will be of the module doing the including - the filename will be of the module that was included - adds a test case for it --- compiler/suggest.nim | 16 +++++++++++++--- nimsuggest/tests/tinclude.nim | 10 ++++++++-- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/compiler/suggest.nim b/compiler/suggest.nim index 186b23cd9..73929f813 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -491,9 +491,19 @@ proc suggestSym*(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym; findUsages(conf, info, s, usageSym) elif conf.ideCmd == ideHighlight and info.fileIndex == conf.m.trackPos.fileIndex: suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideHighlight, info, 100, PrefixMatch.None, false, 0)) - elif conf.ideCmd == ideOutline and info.fileIndex == conf.m.trackPos.fileIndex and - isDecl: - suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideOutline, info, 100, PrefixMatch.None, false, 0)) + elif conf.ideCmd == ideOutline and isDecl: + # if a module is included then the info we have is inside the include and + # we need to walk up the owners until we find the outer most module, + # which will be the last skModule prior to an skPackage. + var + parentFileIndex = info.fileIndex # assume we're in the correct module + parentModule = s.owner + while parentModule != nil and parentModule.kind == skModule: + parentFileIndex = parentModule.info.fileIndex + parentModule = parentModule.owner + + if parentFileIndex == conf.m.trackPos.fileIndex: + suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideOutline, info, 100, PrefixMatch.None, false, 0)) proc extractPragma(s: PSym): PNode = if s.kind in routineKinds: diff --git a/nimsuggest/tests/tinclude.nim b/nimsuggest/tests/tinclude.nim index 23aa2d727..b67440b9e 100644 --- a/nimsuggest/tests/tinclude.nim +++ b/nimsuggest/tests/tinclude.nim @@ -1,4 +1,6 @@ -# import that has an include, def calls must work into and out of includes +# import that has an include: +# * def calls must work into and out of includes +# * outline calls on the import must show included members import fixtures/minclude_import proc go() = @@ -8,12 +10,16 @@ go() discard """ $nimsuggest --tester $file ->def $path/tinclude.nim:5:14 +>def $path/tinclude.nim:7:14 def;;skProc;;minclude_import.create;;proc (greeting: string, subject: string): Greet{.noSideEffect, gcsafe, locks: 0.};;*fixtures/minclude_include.nim;;3;;5;;"";;100 >def $path/fixtures/minclude_include.nim:3:71 def;;skType;;minclude_types.Greet;;Greet;;*fixtures/minclude_types.nim;;4;;2;;"";;100 >def $path/fixtures/minclude_include.nim:3:71 def;;skType;;minclude_types.Greet;;Greet;;*fixtures/minclude_types.nim;;4;;2;;"";;100 +>outline $path/fixtures/minclude_import.nim +outline;;skProc;;minclude_import.say;;*fixtures/minclude_import.nim;;7;;5;;"";;100 +outline;;skProc;;minclude_import.create;;*fixtures/minclude_include.nim;;3;;5;;"";;100 +outline;;skProc;;minclude_import.say;;*fixtures/minclude_import.nim;;13;;5;;"";;100 """ # TODO test/fix if the first `def` is not first or repeated we get no results From 025ca660f7fe396ec6794137c29845feaf7ab9a3 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 6 Jan 2021 11:28:24 -0800 Subject: [PATCH 1297/3768] [backport 1.0] add backend support for js bigint (#16606) * add backend support for js bigint * cleanup * add tests * add -d:nimHasJsBigIntBackend * cleanup * more tests --- compiler/condsyms.nim | 1 + compiler/jsgen.nim | 5 +++- tests/js/tbigint_backend.nim | 58 ++++++++++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 tests/js/tbigint_backend.nim diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 131f32f32..55f2b4a0f 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -123,3 +123,4 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasEffectTraitsModule") defineSymbol("nimHasCastPragmaBlocks") defineSymbol("nimHasDeclaredLocs") + defineSymbol("nimHasJsBigIntBackend") diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 675a24c92..4b369210d 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -1677,7 +1677,10 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope = var t = skipTypes(typ, abstractInst) case t.kind of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyChar: - result = putToSeq("0", indirect) + if $t.sym.loc.r == "bigint": + result = putToSeq("0n", indirect) + else: + result = putToSeq("0", indirect) of tyFloat..tyFloat128: result = putToSeq("0.0", indirect) of tyRange, tyGenericInst, tyAlias, tySink, tyOwned: diff --git a/tests/js/tbigint_backend.nim b/tests/js/tbigint_backend.nim new file mode 100644 index 000000000..db7ebb065 --- /dev/null +++ b/tests/js/tbigint_backend.nim @@ -0,0 +1,58 @@ +proc jsTypeOf*[T](x: T): cstring {.importjs: "typeof(#)".} + ## Returns the name of the JsObject's JavaScript type as a cstring. + # xxx replace jsffi.jsTypeOf with this definition and add tests + +type JsBigIntImpl {.importc: "bigint".} = int +type JsBigInt = distinct JsBigIntImpl + +doAssert JsBigInt isnot int +func big*(integer: SomeInteger): JsBigInt {.importjs: "BigInt(#)".} +func big*(integer: cstring): JsBigInt {.importjs: "BigInt(#)".} +func `<=`*(x, y: JsBigInt): bool {.importjs: "(# $1 #)".} +func `==`*(x, y: JsBigInt): bool {.importjs: "(# === #)".} +func inc*(x: var JsBigInt) {.importjs: "[#][0][0]++".} +func inc2*(x: var JsBigInt) {.importjs: "#++".} +func toCstring*(this: JsBigInt): cstring {.importjs: "#.toString()".} +func `$`*(this: JsBigInt): string = + $toCstring(this) + +block: + doAssert defined(nimHasJsBigIntBackend) + let z1 = big"10" + let z2 = big"15" + doAssert z1 == big"10" + doAssert z1 == z1 + doAssert z1 != z2 + var s: seq[cstring] + for i in z1 .. z2: + s.add $i + doAssert s == @["10".cstring, "11", "12", "13", "14", "15"] + block: + var a=big"3" + a.inc + doAssert a == big"4" + block: + var z: JsBigInt + doAssert $z == "0" + doAssert z.jsTypeOf == "bigint" # would fail without codegen change + doAssert z != big(1) + doAssert z == big"0" # ditto + + # ditto below + block: + let z: JsBigInt = big"1" + doAssert $z == "1" + doAssert z.jsTypeOf == "bigint" + doAssert z == big"1" + + block: + let z = JsBigInt.default + doAssert $z == "0" + doAssert z.jsTypeOf == "bigint" + doAssert z == big"0" + + block: + var a: seq[JsBigInt] + a.setLen 3 + doAssert a[^1].jsTypeOf == "bigint" + doAssert a[^1] == big"0" From d34d023da1d7af972366c3af58a144b395964b4c Mon Sep 17 00:00:00 2001 From: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Date: Wed, 6 Jan 2021 20:29:22 +0100 Subject: [PATCH 1298/3768] Minor docs/format changes (cpuinfo, volatile) (#16602) --- lib/pure/concurrency/cpuinfo.nim | 19 +++++++++---------- lib/pure/volatile.nim | 4 ++-- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/lib/pure/concurrency/cpuinfo.nim b/lib/pure/concurrency/cpuinfo.nim index 3e5695360..57d13fde3 100644 --- a/lib/pure/concurrency/cpuinfo.nim +++ b/lib/pure/concurrency/cpuinfo.nim @@ -7,7 +7,11 @@ # distribution, for details about the copyright. # -## This module implements procs to determine the number of CPUs / cores. +## This module implements a proc to determine the number of CPUs / cores. + +runnableExamples: + doAssert countProcessors() > 0 + include "system/inclrtl" @@ -15,15 +19,15 @@ when not defined(windows): import posix when defined(freebsd) or defined(macosx): - {.emit:"#include ".} + {.emit: "#include ".} when defined(openbsd) or defined(netbsd): - {.emit:"#include ".} + {.emit: "#include ".} when defined(macosx) or defined(bsd): # we HAVE to emit param.h before sysctl.h so we cannot use .header here # either. The amount of archaic bullshit in Poonix based OSes is just insane. - {.emit:"#include ".} + {.emit: "#include ".} const CTL_HW = 6 HW_AVAILCPU = 25 @@ -47,7 +51,7 @@ when defined(haiku): header: "".} proc countProcessors*(): int {.rtl, extern: "ncpi$1".} = - ## returns the number of the processors/cores the machine has. + ## Returns the number of the processors/cores the machine has. ## Returns 0 if it cannot be detected. when defined(windows): type @@ -95,8 +99,3 @@ proc countProcessors*(): int {.rtl, extern: "ncpi$1".} = else: result = sysconf(SC_NPROCESSORS_ONLN) if result <= 0: result = 0 - - -runnableExamples: - block: - doAssert countProcessors() > 0 diff --git a/lib/pure/volatile.nim b/lib/pure/volatile.nim index 208f0fcaa..7fdf40e4b 100644 --- a/lib/pure/volatile.nim +++ b/lib/pure/volatile.nim @@ -12,7 +12,7 @@ template volatileLoad*[T](src: ptr T): T = ## Generates a volatile load of the value stored in the container `src`. - ## Note that this only effects code generation on `C` like backends + ## Note that this only effects code generation on `C` like backends. when nimvm: src[] else: @@ -26,7 +26,7 @@ template volatileLoad*[T](src: ptr T): T = template volatileStore*[T](dest: ptr T, val: T) = ## Generates a volatile store into the container `dest` of the value ## `val`. Note that this only effects code generation on `C` like - ## backends + ## backends. when nimvm: dest[] = val else: From 04b765c16d4a53c03f8d41c9b9ad97ac4ac8189e Mon Sep 17 00:00:00 2001 From: inv2004 Date: Thu, 7 Jan 2021 05:09:02 +0300 Subject: [PATCH 1299/3768] Jsonutils: pass opt for object in object (#16615) * jsonutils: fromJson forward opt param fix * jsonutils: object in object test + fix: opt pass --- lib/std/jsonutils.nim | 2 +- tests/stdlib/tjsonutils.nim | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/std/jsonutils.nim b/lib/std/jsonutils.nim index 4dca024c1..b9e47bd70 100644 --- a/lib/std/jsonutils.nim +++ b/lib/std/jsonutils.nim @@ -119,7 +119,7 @@ template fromJsonFields(newObj, oldObj, json, discKeys, opt) = when key notin discKeys: if json.hasKey key: numMatched.inc - fromJson(val, json[key]) + fromJson(val, json[key], opt) elif opt.allowMissingKeys: # if there are no discriminant keys the `oldObj` must always have the # same keys as the new one. Otherwise we must check, because they could diff --git a/tests/stdlib/tjsonutils.nim b/tests/stdlib/tjsonutils.nim index f56c32719..06d01a9fb 100644 --- a/tests/stdlib/tjsonutils.nim +++ b/tests/stdlib/tjsonutils.nim @@ -123,6 +123,9 @@ template fn() = a: int b: string c: float + type Bar = object + foo: Foo + boo: string var f: seq[Foo] try: fromJson(f, parseJson """[{"b": "bbb"}]""") @@ -131,6 +134,9 @@ template fn() = doAssert true fromJson(f, parseJson """[{"b": "bbb"}]""", Joptions(allowExtraKeys: true, allowMissingKeys: true)) doAssert f == @[Foo(a: 0, b: "bbb", c: 0.0)] + var b: Bar + fromJson(b, parseJson """{"foo": {"b": "bbb"}}""", Joptions(allowExtraKeys: true, allowMissingKeys: true)) + doAssert b == Bar(foo: Foo(a: 0, b: "bbb", c: 0.0)) block testHashSet: testRoundtrip(HashSet[string]()): "[]" From 4754806fb5d6afb034dc2d27959353ec579b08e4 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Thu, 7 Jan 2021 07:39:56 +0000 Subject: [PATCH 1300/3768] Fixes the asynchttpserver example some more (#16599) I dislike this example a lot (busy looping for FDs to be closed is a very poor waste of resources) but at least with these changes it's a little bit better. --- lib/pure/asynchttpserver.nim | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index d9f5a3a0f..29a695379 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -18,29 +18,28 @@ runnableExamples: # This example will create an HTTP server on port 8080. The server will # respond to all requests with a `200 OK` response code and "Hello World" # as the response body. Run locally with: - # `nim doc --doccmd:-d:nimAsynchttpserverEnableTest --lib:lib lib/pure/asynchttpserver.nim` + # `nim doc --doccmd:-d:nimAsyncHttpServerEnableTest --lib:lib lib/pure/asynchttpserver.nim` import asyncdispatch - if defined(nimAsynchttpserverEnableTest): + if defined(nimAsyncHttpServerEnableTest): proc main {.async.} = const port = 8080 var server = newAsyncHttpServer() proc cb(req: Request) {.async.} = echo (req.reqMethod, req.url, req.headers) - let headers = {"Date": "Tue, 29 Apr 2014 23:40:08 GMT", - "Content-type": "text/plain; charset=utf-8"} + let headers = {"Content-type": "text/plain; charset=utf-8"} await req.respond(Http200, "Hello World", headers.newHttpHeaders()) echo "test this with: curl localhost:" & $port & "/" - server.listen Port(port) + server.listen(Port(port)) while true: if server.shouldAcceptRequest(): await server.acceptRequest(cb) else: # too many concurrent connections, `maxFDs` exceeded - poll() + # wait 500ms for FDs to be closed + await sleepAsync(500) - asyncCheck main() - runForever() + waitFor main() import asyncnet, asyncdispatch, parseutils, uri, strutils import httpcore From bab0aa6ecf8b91f9befaebefd0d4d5adeaac3ac3 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Thu, 7 Jan 2021 01:48:02 -0600 Subject: [PATCH 1301/3768] add math.signbit (#16592) --- changelog.md | 3 +++ compiler/vmops.nim | 6 ++++++ lib/pure/math.nim | 27 ++++++++++++++++++++++++++- tests/stdlib/tmath.nim | 10 ++++++++++ 4 files changed, 45 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 306cf54d3..4e9b8c162 100644 --- a/changelog.md +++ b/changelog.md @@ -93,6 +93,9 @@ with other backends. see #9125. Use `-d:nimLegacyJsRound` for previous behavior. + +- Added `math.signbit`. + ## Language changes - `nimscript` now handles `except Exception as e`. diff --git a/compiler/vmops.nim b/compiler/vmops.nim index 3e859d3d7..504a352b5 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -16,6 +16,9 @@ from math import sqrt, ln, log10, log2, exp, round, arccos, arcsin, when declared(math.copySign): from math import copySign +when declared(math.signbit): + from math import signbit + from os import getEnv, existsEnv, dirExists, fileExists, putEnv, walkDir, getAppFilename from md5 import getMD5 from sighashes import symBodyDigest @@ -174,6 +177,9 @@ proc registerAdditionalOps*(c: PCtx) = when declared(copySign): wrap2f_math(copySign) + when declared(signbit): + wrap1f_math(signbit) + wrap1s(getMD5, md5op) proc `mod Wrapper`(a: VmArgs) {.nimcall.} = diff --git a/lib/pure/math.nim b/lib/pure/math.nim index a6a3676b9..bcda68afe 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -56,7 +56,7 @@ import std/private/since {.push debugger: off.} # the user does not want to trace a part # of the standard library! -import bitops, fenv +import std/[bitops, fenv] when defined(c) or defined(cpp): proc c_isnan(x: float): bool {.importc: "isnan", header: "".} @@ -65,6 +65,8 @@ when defined(c) or defined(cpp): proc c_copysign(x, y: cfloat): cfloat {.importc: "copysignf", header: "".} proc c_copysign(x, y: cdouble): cdouble {.importc: "copysign", header: "".} + proc c_signbit(x: SomeFloat): cint {.importc: "signbit", header: "".} + func binom*(n, k: int): int = ## Computes the `binomial coefficient `_. runnableExamples: @@ -156,6 +158,29 @@ func isNaN*(x: SomeFloat): bool {.inline, since: (1,5,1).} = when defined(js): fn() else: result = c_isnan(x) +when defined(js): + proc toBitsImpl(x: float): array[2, uint32] = + asm """ + const buffer = new ArrayBuffer(8); + const floatBuffer = new Float64Array(buffer); + const uintBuffer = new Uint32Array(buffer); + floatBuffer[0] = `x`; + `result` = uintBuffer + """ + +proc signbit*(x: SomeFloat): bool {.inline, since: (1, 5, 1).} = + ## Returns true if `x` is negative, false otherwise. + runnableExamples: + doAssert not signbit(0.0) + doAssert signbit(-0.0) + doAssert signbit(-0.1) + doAssert not signbit(0.1) + when defined(js): + let uintBuffer = toBitsImpl(x) + result = (uintBuffer[1] shr 31) != 0 + else: + result = c_signbit(x) != 0 + func copySign*[T: SomeFloat](x, y: T): T {.inline, since: (1, 5, 1).} = ## Returns a value with the magnitude of `x` and the sign of `y`; ## this works even if x or y are NaN or zero, both of which can carry a sign. diff --git a/tests/stdlib/tmath.nim b/tests/stdlib/tmath.nim index e5cb58eba..62fdcd19f 100644 --- a/tests/stdlib/tmath.nim +++ b/tests/stdlib/tmath.nim @@ -304,6 +304,16 @@ block: template main = # xxx wrap all under `main` so it also gets tested in vm. + block: # signbit + doAssert not signbit(0.0) + doAssert signbit(-0.0) + doAssert signbit(-0.1) + doAssert not signbit(0.1) + + doAssert not signbit(Inf) + doAssert signbit(-Inf) + doAssert not signbit(NaN) + block: # isNaN doAssert NaN.isNaN doAssert not Inf.isNaN From 89a21e4ec71e705833d2aacd069e291cf41a19c6 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Thu, 7 Jan 2021 02:38:31 -0600 Subject: [PATCH 1302/3768] oids: switch from PRNG to random module (#16203) * switch from PRNG to random module * fix the regression * comments + tests * runnableExamples * make oids better --- lib/pure/oids.nim | 39 +++++++++++++++++++-------------------- tests/stdlib/toids.nim | 6 ++++++ 2 files changed, 25 insertions(+), 20 deletions(-) create mode 100644 tests/stdlib/toids.nim diff --git a/lib/pure/oids.nim b/lib/pure/oids.nim index d3a7c4fb6..957a8193a 100644 --- a/lib/pure/oids.nim +++ b/lib/pure/oids.nim @@ -12,24 +12,24 @@ ## produce a globally distributed unique ID. This implementation was extracted ## from the Mongodb interface and it thus binary compatible with a Mongo OID. ## -## This implementation calls ``math.randomize()`` for the first call of +## This implementation calls `initRand()` for the first call of ## ``genOid``. -import hashes, times, endians +import hashes, times, endians, random type - Oid* = object ## an OID + Oid* = object ## An OID. time: int32 ## fuzz: int32 ## count: int32 ## proc `==`*(oid1: Oid, oid2: Oid): bool = - ## Compare two Mongo Object IDs for equality + ## Compares two Mongo Object IDs for equality. return (oid1.time == oid2.time) and (oid1.fuzz == oid2.fuzz) and (oid1.count == oid2.count) proc hash*(oid: Oid): Hash = - ## Generate hash of Oid for use in hashtables + ## Generates hash of Oid for use in hashtables. var h: Hash = 0 h = h !& hash(oid.time) h = h !& hash(oid.fuzz) @@ -44,7 +44,7 @@ proc hexbyte*(hex: char): int = else: discard proc parseOid*(str: cstring): Oid = - ## parses an OID. + ## Parses an OID. var bytes = cast[cstring](addr(result.time)) var i = 0 while i < 12: @@ -52,6 +52,7 @@ proc parseOid*(str: cstring): Oid = inc(i) proc oidToString*(oid: Oid, str: cstring) = + ## Converts an oid to `str` which must have space allocated for 25 elements. const hex = "0123456789abcdef" # work around a compiler bug: var str = str @@ -66,35 +67,33 @@ proc oidToString*(oid: Oid, str: cstring) = str[24] = '\0' proc `$`*(oid: Oid): string = + ## Converts an oid to string. result = newString(24) oidToString(oid, result) -proc rand(): cint {.importc: "rand", header: "", nodecl.} -proc srand(seed: cint) {.importc: "srand", header: "", nodecl.} - -var t = getTime().toUnix.int32 -srand(t) var - incr: int = rand() - fuzz: int32 = rand() + t = getTime().toUnix.int32 + seed = initRand(t) + incr: int = seed.rand(int.high) + +let fuzz = cast[int32](seed.rand(high(int))) proc genOid*(): Oid = - ## generates a new OID. + ## Generates a new OID. + runnableExamples: + doAssert ($genOid()).len == 24 + if false: doAssert $genOid() == "5fc7f546ddbbc84800006aaf" t = getTime().toUnix.int32 - var i = int32(atomicInc(incr)) + var i = cast[int32](atomicInc(incr)) bigEndian32(addr result.time, addr(t)) result.fuzz = fuzz bigEndian32(addr result.count, addr(i)) proc generatedTime*(oid: Oid): Time = - ## returns the generated timestamp of the OID. + ## Returns the generated timestamp of the OID. var tmp: int32 var dummy = oid.time bigEndian32(addr(tmp), addr(dummy)) result = fromUnix(tmp) - -when not defined(testing) and isMainModule: - let xo = genOid() - echo xo.generatedTime diff --git a/tests/stdlib/toids.nim b/tests/stdlib/toids.nim new file mode 100644 index 000000000..f162dbe57 --- /dev/null +++ b/tests/stdlib/toids.nim @@ -0,0 +1,6 @@ +import std/oids + + +block: # genOid + let x = genOid() + doAssert ($x).len == 24 From 0e7902b976fbd0e566f0fb5f4915695aca406451 Mon Sep 17 00:00:00 2001 From: PMunch Date: Thu, 7 Jan 2021 16:09:57 +0100 Subject: [PATCH 1303/3768] Implements streams for sockets (#15729) --- changelog.md | 2 + lib/std/socketstreams.nim | 181 ++++++++++++++++++++++++++++++++ tests/stdlib/tsocketstreams.nim | 64 +++++++++++ 3 files changed, 247 insertions(+) create mode 100644 lib/std/socketstreams.nim create mode 100644 tests/stdlib/tsocketstreams.nim diff --git a/changelog.md b/changelog.md index 4e9b8c162..afe49f1f3 100644 --- a/changelog.md +++ b/changelog.md @@ -91,6 +91,8 @@ - `math.round` now is rounded "away from zero" in JS backend which is consistent with other backends. see #9125. Use `-d:nimLegacyJsRound` for previous behavior. +- Added `socketstream` module that wraps sockets in the stream interface + diff --git a/lib/std/socketstreams.nim b/lib/std/socketstreams.nim new file mode 100644 index 000000000..5c882858d --- /dev/null +++ b/lib/std/socketstreams.nim @@ -0,0 +1,181 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2021 Nim contributors +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module provides an implementation of the streams interface for sockets. +## It contains two separate implementations, a +## `ReadSocketStream <#ReadSocketStream>`_ and a +## `WriteSocketStream <#WriteSocketStream>`_. +## +## The `ReadSocketStream` only supports reading, peeking, and seeking. +## It reads into a buffer, so even by +## seeking backwards it will only read the same position a single time from the +## underlying socket. To clear the buffer and free the data read into it you +## can call `resetStream`, this will also reset the position back to 0 but +## won't do anything to the underlying socket. +## +## The `WriteSocketStream` allows both reading and writing, but it performs the +## reads on the internal buffer. So by writing to the buffer you can then read +## back what was written but without receiving anything from the socket. You +## can also set the position and overwrite parts of the buffer, and to send +## anything over the socket you need to call `flush` at which point you can't +## write anything to the buffer before the point of the flush (but it can still +## be read). Again to empty the underlying buffer you need to call +## `resetStream`. +## +## Examples +## ======== +## +## .. code-block:: Nim +## import std/socketstreams +## +## var +## socket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) +## stream = newReadSocketStream(socket) +## socket.sendTo("127.0.0.1", Port(12345), "SOME REQUEST") +## echo stream.readLine() # Will call `recv` +## stream.setPosition(0) +## echo stream.readLine() # Will return the read line from the buffer +## stream.resetStream() # Buffer is now empty, position is 0 +## echo stream.readLine() # Will call `recv` again +## stream.close() # Closes the socket +## +## .. code-block:: Nim +## +## import std/socketstreams +## +## var socket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) +## socket.connect("127.0.0.1", Port(12345)) +## var sendStream = newWriteSocketStream(socket) +## sendStream.write "NOM" +## sendStream.setPosition(1) +## echo sendStream.peekStr(2) # OM +## sendStream.write "I" +## sendStream.setPosition(0) +## echo sendStream.readStr(3) # NIM +## echo sendStream.getPosition() # 3 +## sendStream.flush() # This actually performs the writing to the socket +## sendStream.setPosition(1) +## sendStream.write "I" # Throws an error as we can't write into an already sent buffer + +import net, streams + +type + ReadSocketStream* = ref ReadSocketStreamObj + ReadSocketStreamObj* = object of StreamObj + data: Socket + pos: int + buf: seq[byte] + WriteSocketStream* = ref WriteSocketStreamObj + WriteSocketStreamObj* = object of ReadSocketStreamObj + lastFlush: int + +proc rsAtEnd(s: Stream): bool = + return false + +proc rsSetPosition(s: Stream, pos: int) = + var s = ReadSocketStream(s) + s.pos = pos + +proc rsGetPosition(s: Stream): int = + var s = ReadSocketStream(s) + return s.pos + +proc rsPeekData(s: Stream, buffer: pointer, bufLen: int): int = + let s = ReadSocketStream(s) + if bufLen > 0: + let oldLen = s.buf.len + s.buf.setLen(max(s.pos + bufLen, s.buf.len)) + if s.pos + bufLen > oldLen: + result = s.data.recv(s.buf[oldLen].addr, s.buf.len - oldLen) + if result > 0: + result += oldLen - s.pos + else: + result = bufLen + copyMem(buffer, s.buf[s.pos].addr, result) + +proc rsReadData(s: Stream, buffer: pointer, bufLen: int): int = + result = s.rsPeekData(buffer, bufLen) + var s = ReadSocketStream(s) + s.pos += bufLen + +proc rsReadDataStr(s: Stream, buffer: var string, slice: Slice[int]): int = + var s = ReadSocketStream(s) + result = slice.b + 1 - slice.a + if result > 0: + result = s.rsReadData(buffer[slice.a].addr, result) + inc(s.pos, result) + else: + result = 0 + +proc wsWriteData(s: Stream, buffer: pointer, bufLen: int) = + var s = WriteSocketStream(s) + if s.pos < s.lastFlush: + raise newException(IOError, "Unable to write into buffer that has already been sent") + if s.buf.len < s.pos + bufLen: + s.buf.setLen(s.pos + bufLen) + copyMem(s.buf[s.pos].addr, buffer, bufLen) + s.pos += bufLen + +proc wsPeekData(s: Stream, buffer: pointer, bufLen: int): int = + var s = WriteSocketStream(s) + result = bufLen + if result > 0: + if s.pos > s.buf.len or s.pos == s.buf.len or s.pos + bufLen > s.buf.len: + raise newException(IOError, "Unable to read past end of write buffer") + else: + copyMem(buffer, s.buf[s.pos].addr, bufLen) + +proc wsReadData(s: Stream, buffer: pointer, bufLen: int): int = + result = s.wsPeekData(buffer, bufLen) + var s = ReadSocketStream(s) + s.pos += bufLen + +proc wsAtEnd(s: Stream): bool = + var s = WriteSocketStream(s) + return s.pos == s.buf.len + +proc wsFlush(s: Stream) = + var s = WriteSocketStream(s) + discard s.data.send(s.buf[s.lastFlush].addr, s.buf.len - s.lastFlush) + s.lastFlush = s.buf.len + +proc rsClose(s: Stream) = + {.cast(tags: []).}: + var s = ReadSocketStream(s) + s.data.close() + +proc newReadSocketStream*(s: Socket): owned ReadSocketStream = + result = ReadSocketStream(data: s, pos: 0, + closeImpl: rsClose, + atEndImpl: rsAtEnd, + setPositionImpl: rsSetPosition, + getPositionImpl: rsGetPosition, + readDataImpl: rsReadData, + peekDataImpl: rsPeekData, + readDataStrImpl: rsReadDataStr) + +proc resetStream*(s: ReadSocketStream) = + s.buf = @[] + s.pos = 0 + +proc newWriteSocketStream*(s: Socket): owned WriteSocketStream = + result = WriteSocketStream(data: s, pos: 0, + closeImpl: rsClose, + atEndImpl: wsAtEnd, + setPositionImpl: rsSetPosition, + getPositionImpl: rsGetPosition, + writeDataImpl: wsWriteData, + readDataImpl: wsReadData, + peekDataImpl: wsPeekData, + flushImpl: wsFlush) + +proc resetStream*(s: WriteSocketStream) = + s.buf = @[] + s.pos = 0 + s.lastFlush = 0 diff --git a/tests/stdlib/tsocketstreams.nim b/tests/stdlib/tsocketstreams.nim new file mode 100644 index 000000000..0cf952810 --- /dev/null +++ b/tests/stdlib/tsocketstreams.nim @@ -0,0 +1,64 @@ +discard """ + output: ''' +OM +NIM +3 +NIM +NIM +Hello server! +Hi there client! +'''""" +import std/socketstreams, net, streams + +block UDP: + var recvSocket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) + var recvStream = newReadSocketStream(recvSocket) + recvSocket.bindAddr(Port(12345), "127.0.0.1") + + var sendSocket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) + sendSocket.connect("127.0.0.1", Port(12345)) + var sendStream = newWriteSocketStream(sendSocket) + sendStream.write "NOM\n" + sendStream.setPosition(1) + echo sendStream.peekStr(2) + sendStream.write "I" + sendStream.setPosition(0) + echo sendStream.readStr(3) + echo sendStream.getPosition() + sendStream.flush() + + echo recvStream.readLine() + recvStream.setPosition(0) + echo recvStream.readLine() + recvStream.close() + +block TCP: + var server = newSocket() + server.setSockOpt(OptReusePort, true) + server.bindAddr(Port(12345)) + server.listen() + + var + client = newSocket() + clientRequestStream = newWriteSocketStream(client) + clientResponseStream = newReadSocketStream(client) + client.connect("127.0.0.1", Port(12345)) + clientRequestStream.writeLine("Hello server!") + clientRequestStream.flush() + + var + incoming: Socket + address: string + server.acceptAddr(incoming, address) + var + serverRequestStream = newReadSocketStream(incoming) + serverResponseStream = newWriteSocketStream(incoming) + echo serverRequestStream.readLine() + serverResponseStream.writeLine("Hi there client!") + serverResponseStream.flush() + serverResponseStream.close() + serverRequestStream.close() + + echo clientResponseStream.readLine() + clientResponseStream.close() + clientRequestStream.close() From cbf227d9493be1e82f3abec1d019ce397163635c Mon Sep 17 00:00:00 2001 From: haxscramper Date: Thu, 7 Jan 2021 21:14:50 +0300 Subject: [PATCH 1304/3768] [FIX] Update fusion master commit hash (#16630) --- koch.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/koch.nim b/koch.nim index 5da262884..95c9c6161 100644 --- a/koch.nim +++ b/koch.nim @@ -11,7 +11,7 @@ const NimbleStableCommit = "8f7af860c5ce9634af880a7081c6435e1f2a5148" # master - FusionStableCommit = "319aac4d43b04113831b529f8003e82f4af6a4a5" + FusionStableCommit = "372ee4313827ef9f2ea388840f7d6b46c2b1b014" when not defined(windows): const From 0da4cb93d1924d99a91491c2ec6124d9ec2a413b Mon Sep 17 00:00:00 2001 From: rockcavera Date: Thu, 7 Jan 2021 15:16:26 -0300 Subject: [PATCH 1305/3768] minor fix (#16624) --- tests/stdlib/tbitops.nim.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/stdlib/tbitops.nim.cfg b/tests/stdlib/tbitops.nim.cfg index f0d7668a7..013e8d38e 100644 --- a/tests/stdlib/tbitops.nim.cfg +++ b/tests/stdlib/tbitops.nim.cfg @@ -1 +1 @@ --d:noUndefinedBitOps +-d:noUndefinedBitOpts From 796498525a3d1197cd6468e4eb45b1fb47a702af Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 7 Jan 2021 20:26:40 +0100 Subject: [PATCH 1306/3768] IC: next steps (#16550) * cleanups * ast.nim: cleanups * IC: no more sym.tab field, stored externally in the module graph * nimble compiles again * rodfiles: store bitwidth of integers and the endianness in the cookie because we serialize 'int' directly * rodfiles: added compilerproc and export sections * rodfiles: added all the missing sections * rodfiles: track the missing information * IC: architecture for lazy loading of proc bodies * make tests green again * completed the lazy loading of proc bodies * symbol lookup integration, part 1 * symbol lookup integration, part 2 * symbol lookup integration, part 3 * make tcompilerapi work again * rodfiles: fixed config change handling --- compiler/ast.nim | 19 - ...nicalizer.nim => canonicalizer_unused.nim} | 0 compiler/ccgexprs.nim | 4 +- compiler/ccgtypes.nim | 12 +- compiler/commands.nim | 17 +- compiler/evaltempl.nim | 2 +- compiler/ic/packed_ast.nim | 23 - compiler/ic/rodfiles.nim | 48 +- compiler/ic/to_packed_ast.nim | 433 +++++++++++++----- compiler/idents.nim | 5 + compiler/importer.nim | 27 +- compiler/jsgen.nim | 6 +- compiler/lookups.nim | 56 +-- compiler/magicsys.nim | 7 +- compiler/modulegraphs.nim | 102 ++++- compiler/modules.nim | 70 +-- compiler/nimeval.nim | 13 +- compiler/packagehandling.nim | 3 +- compiler/passes.nim | 8 +- compiler/plugins/itersgen.nim | 4 +- compiler/pragmas.nim | 4 + compiler/sem.nim | 3 +- compiler/semdata.nim | 22 +- compiler/semexprs.nim | 28 +- compiler/semfields.nim | 2 +- compiler/semgnrc.nim | 2 +- compiler/seminst.nim | 4 +- compiler/semstmts.nim | 12 +- compiler/semtempl.nim | 2 +- compiler/semtypes.nim | 4 +- compiler/sighashes.nim | 2 +- compiler/suggest.nim | 77 ++-- compiler/transf.nim | 15 +- compiler/vm.nim | 2 +- compiler/vmgen.nim | 13 +- nimsuggest/nimsuggest.nim | 2 +- 36 files changed, 653 insertions(+), 400 deletions(-) rename compiler/{canonicalizer.nim => canonicalizer_unused.nim} (100%) diff --git a/compiler/ast.nim b/compiler/ast.nim index ccf4fe497..de4ab5b77 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -833,18 +833,6 @@ type procInstCache*: seq[PInstantiation] gcUnsafetyReason*: PSym # for better error messages wrt gcsafe transformedBody*: PNode # cached body after transf pass - of skModule, skPackage: - # modules keep track of the generic symbols they use from other modules. - # this is because in incremental compilation, when a module is about to - # be replaced with a newer version, we must decrement the usage count - # of all previously used generics. - # For 'import as' we copy the module symbol but shallowCopy the 'tab' - # and set the 'usedGenerics' to ... XXX gah! Better set module.name - # instead? But this doesn't work either. --> We need an skModuleAlias? - # No need, just leave it as skModule but set the owner accordingly and - # check for the owner when touching 'usedGenerics'. - usedGenerics*: seq[PInstantiation] - tab*: TStrTable # interface table for modules of skLet, skVar, skField, skForVar: guard*: PSym bitsize*: int @@ -1455,8 +1443,6 @@ proc copySym*(s: PSym; id: ItemId): PSym = result.typ = s.typ result.flags = s.flags result.magic = s.magic - if s.kind == skModule: - copyStrTable(result.tab, s.tab) result.options = s.options result.position = s.position result.loc = s.loc @@ -1474,13 +1460,10 @@ proc createModuleAlias*(s: PSym, id: ItemId, newIdent: PIdent, info: TLineInfo; result.ast = s.ast #result.id = s.id # XXX figure out what to do with the ID. result.flags = s.flags - system.shallowCopy(result.tab, s.tab) result.options = s.options result.position = s.position result.loc = s.loc result.annex = s.annex - # XXX once usedGenerics is used, ensure module aliases keep working! - assert s.usedGenerics.len == 0 proc initStrTable*(x: var TStrTable) = x.counter = 0 @@ -1915,8 +1898,6 @@ template incompleteType*(t: PType): bool = template typeCompleted*(s: PSym) = incl s.flags, sfNoForward -template getBody*(s: PSym): PNode = s.ast[bodyPos] - template detailedInfo*(sym: PSym): string = sym.name.s diff --git a/compiler/canonicalizer.nim b/compiler/canonicalizer_unused.nim similarity index 100% rename from compiler/canonicalizer.nim rename to compiler/canonicalizer_unused.nim diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index b8a8d21b0..440ecdc7f 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1292,7 +1292,7 @@ proc rawGenNew(p: BProc, a: var TLoc, sizeExpr: Rope; needsInit: bool) = genAssignment(p, a, b, {}) else: let ti = genTypeInfoV1(p.module, typ, a.lode.info) - if bt.destructor != nil and not isTrivialProc(bt.destructor): + if bt.destructor != nil and not isTrivialProc(p.module.g.graph, bt.destructor): # the prototype of a destructor is ``=destroy(x: var T)`` and that of a # finalizer is: ``proc (x: ref T) {.nimcall.}``. We need to check the calling # convention at least: @@ -2204,7 +2204,7 @@ proc genDestroy(p: BProc; n: PNode) = else: discard "nothing to do" else: let t = n[1].typ.skipTypes(abstractVar) - if t.destructor != nil and t.destructor.ast[bodyPos].len != 0: + if t.destructor != nil and getBody(p.module.g.graph, t.destructor).len != 0: internalError(p.config, n.info, "destructor turned out to be not trivial") discard "ignore calls to the default destructor" diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index ab12bad1e..798eaf7f9 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -36,14 +36,6 @@ proc mangleField(m: BModule; name: PIdent): string = if isKeyword(name): result.add "_0" -when false: - proc hashOwner(s: PSym): SigHash = - var m = s - while m.kind != skModule: m = m.owner - let p = m.owner - assert p.kind == skPackage - result = gDebugInfo.register(p.name.s, m.name.s) - proc mangleName(m: BModule; s: PSym): Rope = result = s.loc.r if result == nil: @@ -1313,11 +1305,11 @@ proc genTypeInfo2Name(m: BModule; t: PType): Rope = it = it[0] result = makeCString(res) -proc isTrivialProc(s: PSym): bool {.inline.} = s.ast[bodyPos].len == 0 +proc isTrivialProc(g: ModuleGraph; s: PSym): bool {.inline.} = getBody(g, s).len == 0 proc genHook(m: BModule; t: PType; info: TLineInfo; op: TTypeAttachedOp): Rope = let theProc = t.attachedOps[op] - if theProc != nil and not isTrivialProc(theProc): + if theProc != nil and not isTrivialProc(m.g.graph, theProc): # the prototype of a destructor is ``=destroy(x: var T)`` and that of a # finalizer is: ``proc (x: ref T) {.nimcall.}``. We need to check the calling # convention at least: diff --git a/compiler/commands.nim b/compiler/commands.nim index 9fb9b7e6e..adb85bdd7 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -801,14 +801,15 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; expectNoArg(conf, switch, arg, pass, info) helpOnError(conf, pass) of "symbolfiles": discard "ignore for backwards compat" - of "incremental": - case arg.normalize - of "on": conf.symbolFiles = v2Sf - of "off": conf.symbolFiles = disabledSf - of "writeonly": conf.symbolFiles = writeOnlySf - of "readonly": conf.symbolFiles = readOnlySf - of "v2": conf.symbolFiles = v2Sf - else: localError(conf, info, "invalid option for --incremental: " & arg) + of "incremental", "ic": + if pass in {passCmd2, passPP}: + case arg.normalize + of "on": conf.symbolFiles = v2Sf + of "off": conf.symbolFiles = disabledSf + of "writeonly": conf.symbolFiles = writeOnlySf + of "readonly": conf.symbolFiles = readOnlySf + of "v2": conf.symbolFiles = v2Sf + else: localError(conf, info, "invalid option for --incremental: " & arg) of "skipcfg": processOnOffSwitchG(conf, {optSkipSystemConfigFile}, arg, pass, info) of "skipprojcfg": diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim index 691d33a2c..a85314ac2 100644 --- a/compiler/evaltempl.nim +++ b/compiler/evaltempl.nim @@ -187,7 +187,7 @@ proc evalTemplate*(n: PNode, tmpl, genSymOwner: PSym; ctx.instID = instID[] ctx.idgen = idgen - let body = tmpl.getBody + let body = tmpl.ast[bodyPos] #echo "instantion of ", renderTree(body, {renderIds}) if isAtom(body): result = newNodeI(nkPar, body.info) diff --git a/compiler/ic/packed_ast.nim b/compiler/ic/packed_ast.nim index 546e495c5..cbfb0c5d1 100644 --- a/compiler/ic/packed_ast.nim +++ b/compiler/ic/packed_ast.nim @@ -16,28 +16,6 @@ import std / [hashes, tables, strtabs, md5] import bitabs import ".." / [ast, options] -const - localNamePos* = 0 - localExportMarkerPos* = 1 - localPragmaPos* = 2 - localTypePos* = 3 - localValuePos* = 4 - - typeNamePos* = 0 - typeExportMarkerPos* = 1 - typeGenericParamsPos* = 2 - typePragmaPos* = 3 - typeBodyPos* = 4 - - routineNamePos* = 0 - routineExportMarkerPos* = 1 - routinePatternPos* = 2 - routineGenericParamsPos* = 3 - routineParamsPos* = 4 - routineResultPos* = 5 - routinePragmasPos* = 6 - routineBodyPos* = 7 - const nkModuleRef* = nkNone # pair of (ModuleId, SymId) @@ -55,7 +33,6 @@ type TypeId* = PackedItemId const - nilTypeId* = PackedItemId(module: LitId(0), item: -1.int32) nilItemId* = PackedItemId(module: LitId(0), item: -1.int32) const diff --git a/compiler/ic/rodfiles.nim b/compiler/ic/rodfiles.nim index 99ce183f2..725262120 100644 --- a/compiler/ic/rodfiles.nim +++ b/compiler/ic/rodfiles.nim @@ -18,6 +18,14 @@ type depsSection integersSection floatsSection + exportsSection + reexportsSection + compilerProcsSection + trmacrosSection + convertersSection + methodsSection + pureEnumsSection + macroUsagesSection topLevelSection bodiesSection symsSection @@ -36,26 +44,30 @@ type const RodVersion = 1 cookie = [byte(0), byte('R'), byte('O'), byte('D'), - byte(0), byte(0), byte(0), byte(RodVersion)] + byte(sizeof(int)*8), byte(system.cpuEndian), byte(0), byte(RodVersion)] + +proc setError(f: var RodFile; err: RodFileError) {.inline.} = + f.err = err + #raise newException(IOError, "IO error") proc storePrim*(f: var RodFile; s: string) = if f.err != ok: return if s.len >= high(int32): - f.err = tooBig + setError f, tooBig return var lenPrefix = int32(s.len) if writeBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix): - f.err = ioFailure + setError f, ioFailure else: if s.len != 0: if writeBuffer(f.f, unsafeAddr(s[0]), s.len) != s.len: - f.err = ioFailure + setError f, ioFailure proc storePrim*[T](f: var RodFile; x: T) = if f.err != ok: return when supportsCopyMem(T): if writeBuffer(f.f, unsafeAddr(x), sizeof(x)) != sizeof(x): - f.err = ioFailure + setError f, ioFailure elif T is tuple: for y in fields(x): storePrim(f, y) @@ -65,11 +77,11 @@ proc storePrim*[T](f: var RodFile; x: T) = proc storeSeq*[T](f: var RodFile; s: seq[T]) = if f.err != ok: return if s.len >= high(int32): - f.err = tooBig + setError f, tooBig return var lenPrefix = int32(s.len) if writeBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix): - f.err = ioFailure + setError f, ioFailure else: for i in 0.. 0: if readBuffer(f.f, unsafeAddr(s[0]), s.len) != s.len: - f.err = ioFailure + setError f, ioFailure proc loadPrim*[T](f: var RodFile; x: var T) = if f.err != ok: return when supportsCopyMem(T): if readBuffer(f.f, unsafeAddr(x), sizeof(x)) != sizeof(x): - f.err = ioFailure + setError f, ioFailure elif T is tuple: for y in fields(x): loadPrim(f, y) @@ -100,7 +112,7 @@ proc loadSeq*[T](f: var RodFile; s: var seq[T]) = if f.err != ok: return var lenPrefix = int32(0) if readBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix): - f.err = ioFailure + setError f, ioFailure else: s = newSeq[T](lenPrefix) for i in 0..= g.len: - g.setLen(m+1) - - case g[m].status - of undefined: - g[m].status = loading - let fullpath = msgs.toFullPath(conf, fileIdx) - let rod = toRodFile(conf, AbsoluteFile fullpath) - let err = loadRodFile(rod, g[m].fromDisk, conf) - if err == ok: - result = false - # check its dependencies: - for dep in g[m].fromDisk.imports: - let fid = toFileIndex(dep, g[m].fromDisk, conf) - # Warning: we need to traverse the full graph, so - # do **not use break here**! - if needsRecompile(g, conf, fid): - result = true - - g[m].status = if result: outdated else: loaded - else: - loadError(err, rod) - g[m].status = outdated - result = true - of loading, loaded: - result = false - of outdated: - result = true - # ------------------------------------------------------------------------- proc storeError(err: RodFileError; filename: AbsoluteFile) = @@ -465,7 +471,7 @@ proc storeError(err: RodFileError; filename: AbsoluteFile) = removeFile(filename.string) proc saveRodFile*(filename: AbsoluteFile; encoder: var PackedEncoder) = - rememberConfig(encoder, encoder.config) + #rememberConfig(encoder, encoder.config) var f = rodfiles.create(filename.string) f.storeHeader() @@ -473,40 +479,50 @@ proc saveRodFile*(filename: AbsoluteFile; encoder: var PackedEncoder) = f.storePrim encoder.m.definedSymbols f.storePrim encoder.m.cfg - f.storeSection stringsSection - f.store encoder.m.sh.strings + template storeSeqSection(section, data) {.dirty.} = + f.storeSection section + f.storeSeq data - f.storeSection checkSumsSection - f.storeSeq encoder.m.includes + template storeTabSection(section, data) {.dirty.} = + f.storeSection section + f.store data - f.storeSection depsSection - f.storeSeq encoder.m.imports + storeTabSection stringsSection, encoder.m.sh.strings - f.storeSection integersSection - f.store encoder.m.sh.integers + storeSeqSection checkSumsSection, encoder.m.includes - f.storeSection floatsSection - f.store encoder.m.sh.floats + storeSeqSection depsSection, encoder.m.imports - f.storeSection topLevelSection - f.storeSeq encoder.m.topLevel.nodes + storeTabSection integersSection, encoder.m.sh.integers + storeTabSection floatsSection, encoder.m.sh.floats - f.storeSection bodiesSection - f.storeSeq encoder.m.bodies.nodes + storeSeqSection exportsSection, encoder.m.exports - f.storeSection symsSection - f.storeSeq encoder.m.sh.syms + storeSeqSection reexportsSection, encoder.m.reexports - f.storeSection typesSection - f.storeSeq encoder.m.sh.types + storeSeqSection compilerProcsSection, encoder.m.compilerProcs + + storeSeqSection trmacrosSection, encoder.m.trmacros + storeSeqSection convertersSection, encoder.m.converters + storeSeqSection methodsSection, encoder.m.methods + storeSeqSection pureEnumsSection, encoder.m.pureEnums + storeSeqSection macroUsagesSection, encoder.m.macroUsages + + storeSeqSection topLevelSection, encoder.m.topLevel.nodes + + storeSeqSection bodiesSection, encoder.m.bodies.nodes + storeSeqSection symsSection, encoder.m.sh.syms + + storeSeqSection typesSection, encoder.m.sh.types close(f) if f.err != ok: - loadError(f.err, filename) + storeError(f.err, filename) - when true: + when false: # basic loader testing: var m2: PackedModule discard loadRodFile(filename, m2, encoder.config) + echo "loaded ", filename.string # ---------------------------------------------------------------------------- @@ -516,7 +532,25 @@ type lastLit*: LitId lastFile*: FileIndex # remember the last lookup entry. config*: ConfigRef - ident: IdentCache + cache: IdentCache + +type + ModuleStatus* = enum + undefined, + loading, + loaded, + outdated + + LoadedModule* = object + status*: ModuleStatus + symsInit, typesInit: bool + fromDisk: PackedModule + syms: seq[PSym] # indexed by itemId + types: seq[PType] + module*: PSym # the one true module symbol. + iface: Table[PIdent, seq[PackedItemId]] # PackedItemId so that it works with reexported symbols too + + PackedModuleGraph* = seq[LoadedModule] # indexed by FileIndex proc loadType(c: var PackedDecoder; g: var PackedModuleGraph; t: PackedItemId): PType proc loadSym(c: var PackedDecoder; g: var PackedModuleGraph; s: PackedItemId): PSym @@ -546,7 +580,7 @@ proc loadNodes(c: var PackedDecoder; g: var PackedModuleGraph; of nkEmpty, nkNilLit, nkType: discard of nkIdent: - result.ident = getIdent(c.ident, g[c.thisModule].fromDisk.sh.strings[n.litId]) + result.ident = getIdent(c.cache, g[c.thisModule].fromDisk.sh.strings[n.litId]) of nkSym: result.sym = loadSym(c, g, PackedItemId(module: LitId(0), item: tree.nodes[n.int].operand)) of directIntLit: @@ -567,6 +601,31 @@ proc loadNodes(c: var PackedDecoder; g: var PackedModuleGraph; for n0 in sonsReadonly(tree, n): result.add loadNodes(c, g, tree, n0) +proc loadProcHeader(c: var PackedDecoder; g: var PackedModuleGraph; + tree: PackedTree; n: NodePos): PNode = + # do not load the body of the proc. This will be done later in + # getProcBody, if required. + let k = n.kind + result = newNodeIT(k, translateLineInfo(c, g, n.info), + loadType(c, g, n.typ)) + result.flags = n.flags + assert k in {nkProcDef, nkMethodDef, nkIteratorDef, nkFuncDef, nkConverterDef} + var i = 0 + for n0 in sonsReadonly(tree, n): + if i != bodyPos: + result.add loadNodes(c, g, tree, n0) + else: + result.add nil + inc i + +proc loadProcBody(c: var PackedDecoder; g: var PackedModuleGraph; + tree: PackedTree; n: NodePos): PNode = + var i = 0 + for n0 in sonsReadonly(tree, n): + if i == bodyPos: + result = loadNodes(c, g, tree, n0) + inc i + proc moduleIndex*(c: var PackedDecoder; g: var PackedModuleGraph; s: PackedItemId): int32 {.inline.} = result = if s.module == LitId(0): c.thisModule @@ -579,13 +638,17 @@ proc symHeaderFromPacked(c: var PackedDecoder; g: var PackedModuleGraph; info: translateLineInfo(c, g, s.info), options: s.options, position: s.position, - name: getIdent(c.ident, g[si].fromDisk.sh.strings[s.name]) + name: getIdent(c.cache, g[si].fromDisk.sh.strings[s.name]) ) template loadAstBody(p, field) = if p.field != emptyNodeId: result.field = loadNodes(c, g, g[si].fromDisk.bodies, NodePos p.field) +template loadAstBodyLazy(p, field) = + if p.field != emptyNodeId: + result.field = loadProcHeader(c, g, g[si].fromDisk.bodies, NodePos p.field) + proc loadLib(c: var PackedDecoder; g: var PackedModuleGraph; si, item: int32; l: PackedLib): PLib = # XXX: hack; assume a zero LitId means the PackedLib is all zero (empty) @@ -600,7 +663,10 @@ proc symBodyFromPacked(c: var PackedDecoder; g: var PackedModuleGraph; s: PackedSym; si, item: int32; result: PSym) = result.typ = loadType(c, g, s.typ) loadAstBody(s, constraint) - loadAstBody(s, ast) + if result.kind in {skProc, skFunc, skIterator, skConverter, skMethod}: + loadAstBodyLazy(s, ast) + else: + loadAstBody(s, ast) result.annex = loadLib(c, g, si, item, s.annex) when hasFFI: result.cname = g[si].fromDisk.sh.strings[s.cname] @@ -615,7 +681,7 @@ proc symBodyFromPacked(c: var PackedDecoder; g: var PackedModuleGraph; result.loc.r = rope externalName proc loadSym(c: var PackedDecoder; g: var PackedModuleGraph; s: PackedItemId): PSym = - if s == nilTypeId: + if s == nilItemId: result = nil else: let si = moduleIndex(c, g, s) @@ -626,10 +692,16 @@ proc loadSym(c: var PackedDecoder; g: var PackedModuleGraph; s: PackedItemId): P if g[si].syms[s.item] == nil: let packed = addr(g[si].fromDisk.sh.syms[s.item]) - result = symHeaderFromPacked(c, g, packed[], si, s.item) - # store it here early on, so that recursions work properly: - g[si].syms[s.item] = result - symBodyFromPacked(c, g, packed[], si, s.item, result) + + if packed.kind != skModule: + result = symHeaderFromPacked(c, g, packed[], si, s.item) + # store it here early on, so that recursions work properly: + g[si].syms[s.item] = result + symBodyFromPacked(c, g, packed[], si, s.item, result) + else: + result = g[si].module + assert result != nil + else: result = g[si].syms[s.item] @@ -654,7 +726,7 @@ proc typeBodyFromPacked(c: var PackedDecoder; g: var PackedModuleGraph; result.methods.add((gen, loadSym(c, g, id))) proc loadType(c: var PackedDecoder; g: var PackedModuleGraph; t: PackedItemId): PType = - if t == nilTypeId: + if t == nilItemId: result = nil else: let si = moduleIndex(c, g, t) @@ -672,15 +744,142 @@ proc loadType(c: var PackedDecoder; g: var PackedModuleGraph; t: PackedItemId): else: result = g[si].types[t.item] +proc setupLookupTables(m: var LoadedModule; conf: ConfigRef; cache: IdentCache; fileIdx: FileIndex) = + m.iface = initTable[PIdent, seq[PackedItemId]]() + for e in m.fromDisk.exports: + let nameLit = e[0] + m.iface.mgetOrPut(cache.getIdent(m.fromDisk.sh.strings[nameLit]), @[]).add(PackedItemId(module: LitId(0), item: e[1])) + for re in m.fromDisk.reexports: + let nameLit = re[0] + m.iface.mgetOrPut(cache.getIdent(m.fromDisk.sh.strings[nameLit]), @[]).add(re[1]) -when false: - proc initGenericKey*(s: PSym; types: seq[PType]): GenericKey = - result.module = s.owner.itemId.module - result.name = s.name.s - result.types = mapIt types: hashType(it, {CoType, CoDistinct}).MD5Digest + let filename = AbsoluteFile toFullPath(conf, fileIdx) + # We cannot call ``newSym`` here, because we have to circumvent the ID + # mechanism, which we do in order to assign each module a persistent ID. + m.module = PSym(kind: skModule, itemId: ItemId(module: int32(fileIdx), item: 0'i32), + name: getIdent(cache, splitFile(filename).name), + info: newLineInfo(fileIdx, 1, 1)) + +proc needsRecompile(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache; + fileIdx: FileIndex): bool = + let m = int(fileIdx) + if m >= g.len: + g.setLen(m+1) + + case g[m].status + of undefined: + g[m].status = loading + let fullpath = msgs.toFullPath(conf, fileIdx) + let rod = toRodFile(conf, AbsoluteFile fullpath) + let err = loadRodFile(rod, g[m].fromDisk, conf) + if err == ok: + result = false + # check its dependencies: + for dep in g[m].fromDisk.imports: + let fid = toFileIndex(dep, g[m].fromDisk, conf) + # Warning: we need to traverse the full graph, so + # do **not use break here**! + if needsRecompile(g, conf, cache, fid): + result = true + + if not result: + setupLookupTables(g[m], conf, cache, fileIdx) + g[m].status = if result: outdated else: loaded + else: + loadError(err, rod) + g[m].status = outdated + result = true + of loading, loaded: + result = false + of outdated: + result = true + +proc moduleFromRodFile*(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache; + fileIdx: FileIndex): PSym = + ## Returns 'nil' if the module needs to be recompiled. + if needsRecompile(g, conf, cache, fileIdx): + result = nil + else: + result = g[int fileIdx].module + assert result != nil + +template setupDecoder() {.dirty.} = + var decoder = PackedDecoder( + thisModule: int32(module), + lastLit: LitId(0), + lastFile: FileIndex(-1), + config: config, + cache: cache) + +proc loadProcBody*(config: ConfigRef, cache: IdentCache; + g: var PackedModuleGraph; s: PSym): PNode = + let mId = s.itemId.module + var decoder = PackedDecoder( + thisModule: mId, + lastLit: LitId(0), + lastFile: FileIndex(-1), + config: config, + cache: cache) + let pos = g[mId].fromDisk.sh.syms[s.itemId.item].ast + assert pos != emptyNodeId + result = loadProcBody(decoder, g, g[mId].fromDisk.bodies, NodePos pos) + +type + RodIter* = object + decoder: PackedDecoder + values: seq[PackedItemId] + i: int + +proc initRodIter*(it: var RodIter; config: ConfigRef, cache: IdentCache; + g: var PackedModuleGraph; module: FileIndex; + name: PIdent): PSym = + it.decoder = PackedDecoder( + thisModule: int32(module), + lastLit: LitId(0), + lastFile: FileIndex(-1), + config: config, + cache: cache) + it.values = g[int module].iface.getOrDefault(name) + it.i = 0 + if it.i < it.values.len: + result = loadSym(it.decoder, g, it.values[it.i]) + inc it.i + +proc initRodIterAllSyms*(it: var RodIter; config: ConfigRef, cache: IdentCache; + g: var PackedModuleGraph; module: FileIndex): PSym = + it.decoder = PackedDecoder( + thisModule: int32(module), + lastLit: LitId(0), + lastFile: FileIndex(-1), + config: config, + cache: cache) + it.values = @[] + for v in g[int module].iface.values: + it.values.add v + it.i = 0 + if it.i < it.values.len: + result = loadSym(it.decoder, g, it.values[it.i]) + inc it.i + +proc nextRodIter*(it: var RodIter; g: var PackedModuleGraph): PSym = + if it.i < it.values.len: + result = loadSym(it.decoder, g, it.values[it.i]) + inc it.i + +iterator interfaceSymbols*(config: ConfigRef, cache: IdentCache; + g: var PackedModuleGraph; module: FileIndex; + name: PIdent): PSym = + setupDecoder() + let values = g[int module].iface.getOrDefault(name) + for pid in values: + let s = loadSym(decoder, g, pid) + assert s != nil + yield s + +proc interfaceSymbol*(config: ConfigRef, cache: IdentCache; + g: var PackedModuleGraph; module: FileIndex; + name: PIdent): PSym = + setupDecoder() + let values = g[int module].iface.getOrDefault(name) + result = loadSym(decoder, g, values[0]) - proc addGeneric*(m: var Module; c: var PackedEncoder; key: GenericKey; s: PSym) = - ## add a generic to the module - if key notin m.generics: - m.generics[key] = toPackedSym(s, m.ast, c) - toPackedNode(s.ast, m.ast, c) diff --git a/compiler/idents.nim b/compiler/idents.nim index 1e8c912d9..d2a84fd36 100644 --- a/compiler/idents.nim +++ b/compiler/idents.nim @@ -113,3 +113,8 @@ proc newIdentCache*(): IdentCache = proc whichKeyword*(id: PIdent): TSpecialWord = if id.id < 0: result = wInvalid else: result = TSpecialWord(id.id) + +proc hash*(x: PIdent): Hash {.inline.} = x.h +proc `==`*(a, b: PIdent): bool {.inline.} = + if a.isNil or b.isNil: result = system.`==`(a, b) + else: result = a.id == b.id diff --git a/compiler/importer.nim b/compiler/importer.nim index 645c03b2b..0fde13eb9 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -11,7 +11,8 @@ import intsets, ast, astalgo, msgs, options, idents, lookups, - semdata, modulepaths, sigmatch, lineinfos, sets + semdata, modulepaths, sigmatch, lineinfos, sets, + modulegraphs proc readExceptSet*(c: PContext, n: PNode): IntSet = assert n.kind in {nkImportExceptStmt, nkExportExceptStmt} @@ -108,7 +109,7 @@ proc rawImportSymbol(c: PContext, s, origin: PSym; importSet: var IntSet) = proc importSymbol(c: PContext, n: PNode, fromMod: PSym; importSet: var IntSet) = let ident = lookups.considerQuotedIdent(c, n) - let s = strTableGet(fromMod.tab, ident) + let s = someSym(c.graph, fromMod, ident) if s == nil: errorUndeclaredIdentifier(c, n.info, ident.s) else: @@ -118,16 +119,16 @@ proc importSymbol(c: PContext, n: PNode, fromMod: PSym; importSet: var IntSet) = # for an enumeration we have to add all identifiers if multiImport: # for a overloadable syms add all overloaded routines - var it: TIdentIter - var e = initIdentIter(it, fromMod.tab, s.name) + var it: ModuleIter + var e = initModuleIter(it, c.graph, fromMod, s.name) while e != nil: if e.name.id != s.name.id: internalError(c.config, n.info, "importSymbol: 3") if s.kind in ExportableSymKinds: rawImportSymbol(c, e, fromMod, importSet) - e = nextIdentIter(it, fromMod.tab) + e = nextModuleIter(it, c.graph) else: rawImportSymbol(c, s, fromMod, importSet) - suggestSym(c.config, n.info, s, c.graph.usageSym, false) + suggestSym(c.graph, n.info, s, c.graph.usageSym, false) proc addImport(c: PContext; im: sink ImportedModule) = for i in 0..high(c.imports): @@ -176,18 +177,6 @@ proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) = c.addImport ImportedModule(m: fromMod, mode: importExcept, exceptSet: exceptSet) addUnnamedIt(c, fromMod, it.id notin exceptSet) - when false: - var i: TTabIter - var s = initTabIter(i, fromMod.tab) - while s != nil: - if s.kind != skModule: - if s.kind != skEnumField: - if s.kind notin ExportableSymKinds: - internalError(c.config, s.info, "importAllSymbols: " & $s.kind & " " & s.name.s) - if exceptSet.isNil or s.name.id notin exceptSet: - rawImportSymbol(c, s, fromMod) - s = nextIter(i, fromMod.tab) - proc importAllSymbols*(c: PContext, fromMod: PSym) = c.addImport ImportedModule(m: fromMod, mode: importAll) addUnnamedIt(c, fromMod, true) @@ -256,7 +245,7 @@ proc myImportModule(c: PContext, n: PNode; importStmtResult: PNode): PSym = message(c.config, n.info, warnDeprecated, result.constraint.strVal & "; " & result.name.s & " is deprecated") else: message(c.config, n.info, warnDeprecated, result.name.s & " is deprecated") - suggestSym(c.config, n.info, result, c.graph.usageSym, false) + suggestSym(c.graph, n.info, result, c.graph.usageSym, false) importStmtResult.add newSymNode(result, n.info) #newStrNode(toFullPath(c.config, f), n.info) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 4b369210d..f588f9555 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -887,7 +887,7 @@ proc genCaseJS(p: PProc, n: PNode, r: var TCompRes) = var v = copyNode(e[0]) inc(totalRange, int(e[1].intVal - v.intVal)) if totalRange > 65535: - localError(p.config, n.info, + localError(p.config, n.info, "Your case statement contains too many branches, consider using if/else instead!") while v.intVal <= e[1].intVal: gen(p, v, cond) @@ -1076,7 +1076,7 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = useMagic(p, "nimCopy") # supports proc getF(): var T if x.kind in {nkHiddenDeref, nkDerefExpr} and x[0].kind in nkCallKinds: - lineF(p, "nimCopy($1, $2, $3);$n", + lineF(p, "nimCopy($1, $2, $3);$n", [a.res, b.res, genTypeInfo(p, y.typ)]) else: lineF(p, "$1 = nimCopy($1, $2, $3);$n", @@ -1426,7 +1426,7 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) = if lfNoDecl in s.loc.flags or s.magic != mNone or {sfImportc, sfInfixCall} * s.flags != {}: discard - elif s.kind == skMethod and s.getBody.kind == nkEmpty: + elif s.kind == skMethod and getBody(p.module.graph, s).kind == nkEmpty: # we cannot produce code for the dispatcher yet: discard elif sfForward in s.flags: diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 3c0aabe9a..cf6f07b7c 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -11,7 +11,8 @@ import intsets, ast, astalgo, idents, semdata, types, msgs, options, - renderer, nimfix/prettybase, lineinfos, strutils + renderer, nimfix/prettybase, lineinfos, strutils, + modulegraphs proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) @@ -107,8 +108,9 @@ proc localSearchInScope*(c: PContext, s: PIdent): PSym = scope = scope.parent result = strTableGet(scope.symbols, s) -proc initIdentIter(ti: var TIdentIter; marked: var IntSet; im: ImportedModule; name: PIdent): PSym = - result = initIdentIter(ti, im.m.tab, name) +proc initIdentIter(ti: var ModuleIter; marked: var IntSet; im: ImportedModule; name: PIdent; + g: ModuleGraph): PSym = + result = initModuleIter(ti, g, im.m, name) while result != nil: let b = case im.mode @@ -117,11 +119,12 @@ proc initIdentIter(ti: var TIdentIter; marked: var IntSet; im: ImportedModule; n of importExcept: name.id notin im.exceptSet if b and not containsOrIncl(marked, result.id): return result - result = nextIdentIter(ti, im.m.tab) + result = nextModuleIter(ti, g) -proc nextIdentIter(ti: var TIdentIter; marked: var IntSet; im: ImportedModule): PSym = +proc nextIdentIter(ti: var ModuleIter; marked: var IntSet; im: ImportedModule; + g: ModuleGraph): PSym = while true: - result = nextIdentIter(ti, im.m.tab) + result = nextModuleIter(ti, g) if result == nil: return nil case im.mode of importAll: @@ -134,17 +137,17 @@ proc nextIdentIter(ti: var TIdentIter; marked: var IntSet; im: ImportedModule): if result.name.id notin im.exceptSet and not containsOrIncl(marked, result.id): return result -iterator symbols(im: ImportedModule; marked: var IntSet; name: PIdent): PSym = - var ti: TIdentIter - var candidate = initIdentIter(ti, marked, im, name) +iterator symbols(im: ImportedModule; marked: var IntSet; name: PIdent; g: ModuleGraph): PSym = + var ti: ModuleIter + var candidate = initIdentIter(ti, marked, im, name, g) while candidate != nil: yield candidate - candidate = nextIdentIter(ti, marked, im) + candidate = nextIdentIter(ti, marked, im, g) iterator importedItems*(c: PContext; name: PIdent): PSym = var marked = initIntSet() for im in c.imports.mitems: - for s in symbols(im, marked, name): + for s in symbols(im, marked, name, c.graph): yield s proc allPureEnumFields(c: PContext; name: PIdent): seq[PSym] = @@ -169,15 +172,15 @@ iterator allSyms*(c: PContext): (PSym, int, bool) = dec scopeN isLocal = false for im in c.imports.mitems: - for s in im.m.tab.data: - if s != nil: - yield (s, scopeN, isLocal) + for s in modulegraphs.allSyms(c.graph, im.m): + assert s != nil + yield (s, scopeN, isLocal) proc someSymFromImportTable*(c: PContext; name: PIdent; ambiguous: var bool): PSym = var marked = initIntSet() result = nil for im in c.imports.mitems: - for s in symbols(im, marked, name): + for s in symbols(im, marked, name, c.graph): if result == nil: result = s else: @@ -214,7 +217,7 @@ proc searchInScopesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSy if result.len == 0: var marked = initIntSet() for im in c.imports.mitems: - for s in symbols(im, marked, s): + for s in symbols(im, marked, s, c.graph): if s.kind in filter: result.add s @@ -240,6 +243,7 @@ type oimSymChoiceLocalLookup TOverloadIter* = object it*: TIdentIter + mit*: ModuleIter m*: PSym mode*: TOverloadIterMode symChoiceIndex*: int @@ -306,7 +310,7 @@ proc addDeclAt*(c: PContext; scope: PScope, sym: PSym) = proc addInterfaceDeclAux(c: PContext, sym: PSym) = if sfExported in sym.flags: # add to interface: - if c.module != nil: strTableAdd(c.module.tab, sym) + if c.module != nil: exportSym(c, sym) else: internalError(c.config, sym.info, "addInterfaceDeclAux") proc addInterfaceDeclAt*(c: PContext, scope: PScope, sym: PSym) = @@ -475,7 +479,7 @@ proc qualifiedLookUp*(c: PContext, n: PNode, flags: set[TLookupFlag]): PSym = if m == c.module: result = strTableGet(c.topLevelScope.symbols, ident).skipAlias(n, c.config) else: - result = strTableGet(m.tab, ident).skipAlias(n, c.config) + result = someSym(c.graph, m, ident).skipAlias(n, c.config) if result == nil and checkUndeclared in flags: fixSpelling(n[1], ident, searchInScopes) errorUndeclaredIdentifier(c, n[1].info, ident.s) @@ -509,7 +513,7 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = scope = scope.parent if scope == nil: for i in 0..c.imports.high: - result = initIdentIter(o.it, o.marked, c.imports[i], ident).skipAlias(n, c.config) + result = initIdentIter(o.mit, o.marked, c.imports[i], ident, c.graph).skipAlias(n, c.config) if result != nil: o.currentScope = nil o.importIdx = i @@ -535,7 +539,7 @@ proc initOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = ident).skipAlias(n, c.config) o.mode = oimSelfModule else: - result = initIdentIter(o.it, o.m.tab, ident).skipAlias(n, c.config) + result = initModuleIter(o.mit, c.graph, o.m, ident).skipAlias(n, c.config) else: noidentError(c.config, n[1], n) result = errorSym(c, n[1]) @@ -568,7 +572,7 @@ proc nextOverloadIterImports(o: var TOverloadIter, c: PContext, n: PNode): PSym var idx = o.importIdx+1 o.importIdx = c.imports.len # assume the other imported modules lack this symbol too while idx < c.imports.len: - result = initIdentIter(o.it, o.marked, c.imports[idx], o.it.name).skipAlias(n, c.config) + result = initIdentIter(o.mit, o.marked, c.imports[idx], o.it.name, c.graph).skipAlias(n, c.config) if result != nil: # oh, we were wrong, some other module had the symbol, so remember that: o.importIdx = idx @@ -578,7 +582,7 @@ proc nextOverloadIterImports(o: var TOverloadIter, c: PContext, n: PNode): PSym proc symChoiceExtension(o: var TOverloadIter; c: PContext; n: PNode): PSym = assert o.currentScope == nil while o.importIdx < c.imports.len: - result = initIdentIter(o.it, o.marked, c.imports[o.importIdx], o.it.name).skipAlias(n, c.config) + result = initIdentIter(o.mit, o.marked, c.imports[o.importIdx], o.it.name, c.graph).skipAlias(n, c.config) #while result != nil and result.id in o.marked: # result = nextIdentIter(o.it, o.marked, c.imports[o.importIdx]) if result != nil: @@ -602,12 +606,12 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = else: o.importIdx = 0 if c.imports.len > 0: - result = initIdentIter(o.it, o.marked, c.imports[o.importIdx], o.it.name).skipAlias(n, c.config) + result = initIdentIter(o.mit, o.marked, c.imports[o.importIdx], o.it.name, c.graph).skipAlias(n, c.config) if result == nil: result = nextOverloadIterImports(o, c, n) break elif o.importIdx < c.imports.len: - result = nextIdentIter(o.it, o.marked, c.imports[o.importIdx]).skipAlias(n, c.config) + result = nextIdentIter(o.mit, o.marked, c.imports[o.importIdx], c.graph).skipAlias(n, c.config) if result == nil: result = nextOverloadIterImports(o, c, n) else: @@ -615,7 +619,7 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = of oimSelfModule: result = nextIdentIter(o.it, c.topLevelScope.symbols).skipAlias(n, c.config) of oimOtherModule: - result = nextIdentIter(o.it, o.m.tab).skipAlias(n, c.config) + result = nextModuleIter(o.mit, c.graph).skipAlias(n, c.config) of oimSymChoice: if o.symChoiceIndex < n.len: result = n[o.symChoiceIndex].sym @@ -654,7 +658,7 @@ proc nextOverloadIter*(o: var TOverloadIter, c: PContext, n: PNode): PSym = incl o.marked, result.id elif o.importIdx < c.imports.len: - result = nextIdentIter(o.it, o.marked, c.imports[o.importIdx]).skipAlias(n, c.config) + result = nextIdentIter(o.mit, o.marked, c.imports[o.importIdx], c.graph).skipAlias(n, c.config) #assert result.id notin o.marked #while result != nil and result.id in o.marked: # result = nextIdentIter(o.it, c.imports[o.importIdx]).skipAlias(n, c.config) diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim index d700ab9a7..e79be6776 100644 --- a/compiler/magicsys.nim +++ b/compiler/magicsys.nim @@ -26,7 +26,7 @@ proc newSysType(g: ModuleGraph; kind: TTypeKind, size: int): PType = result.align = size.int16 proc getSysSym*(g: ModuleGraph; info: TLineInfo; name: string): PSym = - result = strTableGet(g.systemModule.tab, getIdent(g.cache, name)) + result = systemModuleSym(g, getIdent(g.cache, name)) if result == nil: localError(g.config, info, "system module needs: " & name) result = newSym(skError, getIdent(g.cache, name), nextSymId(g.idgen), g.systemModule, g.systemModule.info, {}) @@ -34,15 +34,12 @@ proc getSysSym*(g: ModuleGraph; info: TLineInfo; name: string): PSym = if result.kind == skAlias: result = result.owner proc getSysMagic*(g: ModuleGraph; info: TLineInfo; name: string, m: TMagic): PSym = - var ti: TIdentIter let id = getIdent(g.cache, name) - var r = initIdentIter(ti, g.systemModule.tab, id) - while r != nil: + for r in systemModuleSyms(g, id): if r.magic == m: # prefer the tyInt variant: if r.typ[0] != nil and r.typ[0].kind == tyInt: return r result = r - r = nextIdentIter(ti, g.systemModule.tab) if result != nil: return result localError(g.config, info, "system module needs: " & name) result = newSym(skError, id, nextSymId(g.idgen), g.systemModule, g.systemModule.info, {}) diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index 5544668cc..ab3ef5ab8 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -10,25 +10,11 @@ ## This module implements the module graph data structure. The module graph ## represents a complete Nim project. Single modules can either be kept in RAM ## or stored in a rod-file. -## -## The caching of modules is critical for 'nimsuggest' and is tricky to get -## right. If module E is being edited, we need autocompletion (and type -## checking) for E but we don't want to recompile depending -## modules right away for faster turnaround times. Instead we mark the module's -## dependencies as 'dirty'. Let D be a dependency of E. If D is dirty, we -## need to recompile it and all of its dependencies that are marked as 'dirty'. -## 'nimsuggest sug' actually is invoked for the file being edited so we know -## its content changed and there is no need to compute any checksums. -## Instead of a recursive algorithm, we use an iterative algorithm: -## -## - If a module gets recompiled, its dependencies need to be updated. -## - Its dependent module stays the same. -## -import ast, intsets, tables, options, lineinfos, hashes, idents, +import ast, astalgo, intsets, tables, options, lineinfos, hashes, idents, btrees, md5 -# import ic / packed_ast +import ic / to_packed_ast type SigHash* = distinct MD5Digest @@ -39,9 +25,12 @@ type converters*: seq[PSym] patterns*: seq[PSym] pureEnums*: seq[PSym] + interf: TStrTable ModuleGraph* = ref object ifaces*: seq[Iface] ## indexed by int32 fileIdx + packed: PackedModuleGraph + startupPackedConfig*: PackedConfig packageSyms*: TStrTable deps*: IntSet # the dependency graph or potentially its transitive closure. importDeps*: Table[FileIndex, seq[FileIndex]] # explicit import module dependencies @@ -64,6 +53,7 @@ type sysTypes*: array[TTypeKind, PType] compilerprocs*: TStrTable exposed*: TStrTable + packageTypes*: TStrTable intTypeCache*: array[-5..64, PType] opContains*, opNot*: PSym emptyNode*: PNode @@ -132,6 +122,62 @@ proc toBase64a(s: cstring, len: int): string = result.add cb64[a shr 2] result.add cb64[(a and 3) shl 4] +template semtab*(m: PSym; g: ModuleGraph): TStrTable = + g.ifaces[m.position].interf + +proc cachedModule(g: ModuleGraph; m: PSym): bool {.inline.} = + m.position < g.packed.len and g.packed[m.position].status == loaded + +type + ModuleIter* = object + fromRod: bool + modIndex: int + ti: TIdentIter + rodIt: RodIter + +proc initModuleIter*(mi: var ModuleIter; g: ModuleGraph; m: PSym; name: PIdent): PSym = + assert m.kind == skModule + mi.modIndex = m.position + mi.fromRod = mi.modIndex < g.packed.len and g.packed[mi.modIndex].status == loaded + if mi.fromRod: + result = initRodIter(mi.rodIt, g.config, g.cache, g.packed, FileIndex mi.modIndex, name) + else: + result = initIdentIter(mi.ti, g.ifaces[mi.modIndex].interf, name) + +proc nextModuleIter*(mi: var ModuleIter; g: ModuleGraph): PSym = + if mi.fromRod: + result = nextRodIter(mi.rodIt, g.packed) + else: + result = nextIdentIter(mi.ti, g.ifaces[mi.modIndex].interf) + +iterator allSyms*(g: ModuleGraph; m: PSym): PSym = + if cachedModule(g, m): + var rodIt: RodIter + var r = initRodIterAllSyms(rodIt, g.config, g.cache, g.packed, FileIndex m.position) + while r != nil: + yield r + r = nextRodIter(rodIt, g.packed) + else: + for s in g.ifaces[m.position].interf.data: + if s != nil: + yield s + +proc someSym*(g: ModuleGraph; m: PSym; name: PIdent): PSym = + if cachedModule(g, m): + result = interfaceSymbol(g.config, g.cache, g.packed, FileIndex(m.position), name) + else: + result = strTableGet(g.ifaces[m.position].interf, name) + +proc systemModuleSym*(g: ModuleGraph; name: PIdent): PSym = + result = someSym(g, g.systemModule, name) + +iterator systemModuleSyms*(g: ModuleGraph; name: PIdent): PSym = + var mi: ModuleIter + var r = initModuleIter(mi, g, g.systemModule, name) + while r != nil: + yield r + r = nextModuleIter(mi, g) + proc `$`*(u: SigHash): string = toBase64a(cast[cstring](unsafeAddr u), sizeof(u)) @@ -186,6 +232,7 @@ proc registerModule*(g: ModuleGraph; m: PSym) = if m.position >= g.ifaces.len: setLen(g.ifaces, m.position + 1) g.ifaces[m.position] = Iface(module: m, converters: @[], patterns: @[]) + initStrTable(g.ifaces[m.position].interf) proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph = result = ModuleGraph() @@ -202,6 +249,7 @@ proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph = result.methods = @[] initStrTable(result.compilerprocs) initStrTable(result.exposed) + initStrTable(result.packageTypes) result.opNot = createMagic(result, "not", mNot) result.opContains = createMagic(result, "contains", mInSet) result.emptyNode = newNode(nkEmpty) @@ -226,8 +274,11 @@ proc resetAllModules*(g: ModuleGraph) = initStrTable(g.exposed) proc getModule*(g: ModuleGraph; fileIdx: FileIndex): PSym = - if fileIdx.int32 >= 0 and fileIdx.int32 < g.ifaces.len: - result = g.ifaces[fileIdx.int32].module + if fileIdx.int32 >= 0: + if fileIdx.int32 < g.packed.len and g.packed[fileIdx.int32].status == loaded: + result = g.packed[fileIdx.int32].module + elif fileIdx.int32 < g.ifaces.len: + result = g.ifaces[fileIdx.int32].module proc dependsOn(a, b: int): int {.inline.} = (a shl 15) + b @@ -280,3 +331,18 @@ proc markClientsDirty*(g: ModuleGraph; fileIdx: FileIndex) = proc isDirty*(g: ModuleGraph; m: PSym): bool = result = g.suggestMode and sfDirty in m.flags + +proc getBody*(g: ModuleGraph; s: PSym): PNode {.inline.} = + result = s.ast[bodyPos] + if result == nil and g.config.symbolFiles in {readOnlySf, v2Sf}: + result = loadProcBody(g.config, g.cache, g.packed, s) + s.ast[bodyPos] = result + assert result != nil + +proc moduleFromRodFile*(g: ModuleGraph; fileIdx: FileIndex): PSym = + ## Returns 'nil' if the module needs to be recompiled. + if g.config.symbolFiles in {readOnlySf, v2Sf}: + result = moduleFromRodFile(g.packed, g.config, g.cache, fileIdx) + +proc configComplete*(g: ModuleGraph) = + rememberStartupConfig(g.startupPackedConfig, g.config) diff --git a/compiler/modules.nim b/compiler/modules.nim index a8a9c4df8..deb0174b5 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -31,43 +31,49 @@ proc getPackage(graph: ModuleGraph; fileIdx: FileIndex): PSym = pck = getPackageName(graph.config, filename.string) pck2 = if pck.len > 0: pck else: "unknown" pack = getIdent(graph.cache, pck2) - var packSym = graph.packageSyms.strTableGet(pack) - if packSym == nil: - packSym = newSym(skPackage, getIdent(graph.cache, pck2), packageId(), nil, info) - initStrTable(packSym.tab) - graph.packageSyms.strTableAdd(packSym) + result = graph.packageSyms.strTableGet(pack) + if result == nil: + result = newSym(skPackage, getIdent(graph.cache, pck2), packageId(), nil, info) + #initStrTable(packSym.tab) + graph.packageSyms.strTableAdd(result) else: - let existing = strTableGet(packSym.tab, name) - if existing != nil and existing.info.fileIndex != info.fileIndex: - when false: - # we used to produce an error: - localError(graph.config, info, - "module names need to be unique per Nimble package; module clashes with " & - toFullPath(graph.config, existing.info.fileIndex)) - else: - # but starting with version 0.20 we now produce a fake Nimble package instead - # to resolve the conflicts: - let pck3 = fakePackageName(graph.config, filename) - # this makes the new `packSym`'s owner be the original `packSym` - packSym = newSym(skPackage, getIdent(graph.cache, pck3), packageId(), packSym, info) - initStrTable(packSym.tab) - graph.packageSyms.strTableAdd(packSym) - result = packSym + # we now produce a fake Nimble package instead + # to resolve the conflicts: + let pck3 = fakePackageName(graph.config, filename) + # this makes the new `packSym`'s owner be the original `packSym` + result = newSym(skPackage, getIdent(graph.cache, pck3), packageId(), result, info) + #initStrTable(packSym.tab) + graph.packageSyms.strTableAdd(result) + + when false: + let existing = strTableGet(packSym.tab, name) + if existing != nil and existing.info.fileIndex != info.fileIndex: + when false: + # we used to produce an error: + localError(graph.config, info, + "module names need to be unique per Nimble package; module clashes with " & + toFullPath(graph.config, existing.info.fileIndex)) + else: + # but starting with version 0.20 we now produce a fake Nimble package instead + # to resolve the conflicts: + let pck3 = fakePackageName(graph.config, filename) + # this makes the new `packSym`'s owner be the original `packSym` + packSym = newSym(skPackage, getIdent(graph.cache, pck3), packageId(), packSym, info) + #initStrTable(packSym.tab) + graph.packageSyms.strTableAdd(packSym) proc partialInitModule(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; filename: AbsoluteFile) = let packSym = getPackage(graph, fileIdx) result.owner = packSym result.position = int fileIdx - graph.registerModule(result) - - initStrTable(result.tab) + #initStrTable(result.tab(graph)) when false: strTableAdd(result.tab, result) # a module knows itself # This is now implemented via # c.moduleScope.addSym(module) # a module knows itself # in sem.nim, around line 527 - strTableAdd(packSym.tab, result) + #strTableAdd(packSym.tab, result) proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym = let filename = AbsoluteFile toFullPath(graph.config, fileIdx) @@ -79,6 +85,7 @@ proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym = if not isNimIdentifier(result.name.s): rawMessage(graph.config, errGenerated, "invalid module name: " & result.name.s) partialInitModule(result, graph, fileIdx, filename) + graph.registerModule(result) proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags): PSym = var flags = flags @@ -92,21 +99,20 @@ proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags): P elif graph.config.projectIsCmd: s = llStreamOpen(graph.config.cmdInput) discard processModule(graph, result, idGeneratorFromModule(result), s) if result == nil: + result = moduleFromRodFile(graph, fileIdx) let filename = AbsoluteFile toFullPath(graph.config, fileIdx) - when false: - # XXX entry point for module loading from the rod file - result = loadModuleSym(graph, fileIdx, filename) - when true: + if result == nil: result = newModule(graph, fileIdx) result.flags.incl flags registerModule(graph, result) + processModuleAux() else: partialInitModule(result, graph, fileIdx, filename) - processModuleAux() + # XXX replay the pragmas here! elif graph.isDirty(result): result.flags.excl sfDirty # reset module fields: - initStrTable(result.tab) + initStrTable(result.semtab(graph)) result.ast = nil processModuleAux() graph.markClientsDirty(fileIdx) @@ -152,6 +158,8 @@ proc compileProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIdx) = connectCallbacks(graph) let conf = graph.config wantMainModule(conf) + configComplete(graph) + let systemFileIdx = fileInfoIdx(conf, conf.libpath / RelativeFile"system.nim") let projectFile = if projectFileIdx == InvalidFileIdx: conf.projectMainIdx else: projectFileIdx conf.projectMainIdx2 = projectFile diff --git a/compiler/nimeval.nim b/compiler/nimeval.nim index b93129852..0170c9949 100644 --- a/compiler/nimeval.nim +++ b/compiler/nimeval.nim @@ -24,11 +24,8 @@ type iterator exportedSymbols*(i: Interpreter): PSym = assert i != nil assert i.mainModule != nil, "no main module selected" - var it: TTabIter - var s = initTabIter(it, i.mainModule.tab) - while s != nil: + for s in modulegraphs.allSyms(i.graph, i.mainModule): yield s - s = nextIter(it, i.mainModule.tab) proc selectUniqueSymbol*(i: Interpreter; name: string; symKinds: set[TSymKind] = {skLet, skVar}): PSym = @@ -37,14 +34,14 @@ proc selectUniqueSymbol*(i: Interpreter; name: string; assert i != nil assert i.mainModule != nil, "no main module selected" let n = getIdent(i.graph.cache, name) - var it: TIdentIter - var s = initIdentIter(it, i.mainModule.tab, n) + var it: ModuleIter + var s = initModuleIter(it, i.graph, i.mainModule, n) result = nil while s != nil: if s.kind in symKinds: if result == nil: result = s else: return nil # ambiguous - s = nextIdentIter(it, i.mainModule.tab) + s = nextModuleIter(it, i.graph) proc selectRoutine*(i: Interpreter; name: string): PSym = ## Selects a declared routine (proc/func/etc) from the main module. @@ -70,7 +67,7 @@ proc evalScript*(i: Interpreter; scriptStream: PLLStream = nil) = ## This can also be used to *reload* the script. assert i != nil assert i.mainModule != nil, "no main module selected" - initStrTable(i.mainModule.tab) + initStrTable(i.mainModule.semtab(i.graph)) i.mainModule.ast = nil let s = if scriptStream != nil: scriptStream diff --git a/compiler/packagehandling.nim b/compiler/packagehandling.nim index a781f1d51..4af0c28fa 100644 --- a/compiler/packagehandling.nim +++ b/compiler/packagehandling.nim @@ -44,7 +44,8 @@ proc fakePackageName*(conf: ConfigRef; path: AbsoluteFile): string = # in different directory get different name and they can be # placed in a directory. # foo-#head/../bar becomes @foo-@hhead@s..@sbar - result = "@m" & relativeTo(path, conf.projectPath).string.multiReplace({$os.DirSep: "@s", $os.AltSep: "@s", "#": "@h", "@": "@@", ":": "@c"}) + result = "@m" & relativeTo(path, conf.projectPath).string.multiReplace( + {$os.DirSep: "@s", $os.AltSep: "@s", "#": "@h", "@": "@@", ":": "@c"}) proc demanglePackageName*(path: string): string = result = path.multiReplace({"@@": "@", "@h": "#", "@s": "/", "@m": "", "@c": ":"}) diff --git a/compiler/passes.nim b/compiler/passes.nim index 997a10cd8..e3885540e 100644 --- a/compiler/passes.nim +++ b/compiler/passes.nim @@ -110,6 +110,12 @@ proc prepareConfigNotes(graph: ModuleGraph; module: PSym) = proc moduleHasChanged*(graph: ModuleGraph; module: PSym): bool {.inline.} = result = module.id >= 0 or isDefined(graph.config, "nimBackendAssumesChange") +proc partOfStdlib(x: PSym): bool = + var it = x.owner + while it != nil and it.kind == skPackage and it.owner != nil: + it = it.owner + result = it != nil and it.name.s == "stdlib" + proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator; stream: PLLStream): bool {.discardable.} = if graph.stopCompile(): return true @@ -131,7 +137,7 @@ proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator; while true: openParser(p, fileIdx, s, graph.cache, graph.config) - if module.owner == nil or module.owner.name.s != "stdlib" or module.name.s == "distros": + if not partOfStdlib(module) or module.name.s == "distros": # XXX what about caching? no processing then? what if I change the # modules to include between compilation runs? we'd need to track that # in ROD files. I think we should enable this feature only diff --git a/compiler/plugins/itersgen.nim b/compiler/plugins/itersgen.nim index 78c79bf59..24e26b2b7 100644 --- a/compiler/plugins/itersgen.nim +++ b/compiler/plugins/itersgen.nim @@ -9,7 +9,7 @@ ## Plugin to transform an inline iterator into a data structure. -import ".." / [ast, lookups, semdata, lambdalifting, msgs] +import ".." / [ast, modulegraphs, lookups, semdata, lambdalifting, msgs] proc iterToProcImpl*(c: PContext, n: PNode): PNode = result = newNodeI(nkStmtList, n.info) @@ -29,7 +29,7 @@ proc iterToProcImpl*(c: PContext, n: PNode): PNode = localError(c.config, n[2].info, "type must be a non-generic ref|ptr to object with state field") return - let body = liftIterToProc(c.graph, iter.sym, iter.sym.getBody, t, c.idgen) + let body = liftIterToProc(c.graph, iter.sym, getBody(c.graph, iter.sym), t, c.idgen) let prc = newSym(skProc, n[3].ident, nextSymId c.idgen, iter.sym.owner, iter.sym.info) prc.typ = copyType(iter.sym.typ, nextTypeId c.idgen, prc) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index a79a471de..c1627fe0c 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -14,6 +14,8 @@ import wordrecg, ropes, options, strutils, extccomp, math, magicsys, trees, types, lookups, lineinfos, pathutils, linter +from ic / to_packed_ast import addCompilerProc + const FirstCallConv* = wNimcall LastCallConv* = wNoconv @@ -702,6 +704,8 @@ proc markCompilerProc(c: PContext; s: PSym) = incl(s.flags, sfCompilerProc) incl(s.flags, sfUsed) registerCompilerProc(c.graph, s) + if c.config.symbolFiles != disabledSf: + addCompilerProc(c.encoder, s) proc deprecatedStmt(c: PContext; outerPragma: PNode) = let pragma = outerPragma[1] diff --git a/compiler/sem.nim b/compiler/sem.nim index d5ae5a21d..6f3b15867 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -19,7 +19,8 @@ import lowerings, plugins/active, lineinfos, strtabs, int128, isolation_check, typeallowed -from modulegraphs import ModuleGraph, PPassContext, onUse, onDef, onDefResolveForward +from modulegraphs import ModuleGraph, PPassContext, onUse, onDef, onDefResolveForward, + systemModuleSym, semtab, getBody, someSym, allSyms when defined(nimfix): import nimfix/prettybase diff --git a/compiler/semdata.nim b/compiler/semdata.nim index da38a6fc2..f2f074447 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -268,7 +268,7 @@ proc newContext*(graph: ModuleGraph; module: PSym): PContext = result.typesWithOps = @[] result.features = graph.config.features if graph.config.symbolFiles != disabledSf: - initEncoder result.encoder, module, graph.config + initEncoder result.encoder, module, graph.config, graph.startupPackedConfig proc addIncludeFileDep*(c: PContext; f: FileIndex) = if c.config.symbolFiles != disabledSf: @@ -286,15 +286,29 @@ proc inclSym(sq: var seq[PSym], s: PSym) = proc addConverter*(c: PContext, conv: PSym) = inclSym(c.converters, conv) inclSym(c.graph.ifaces[c.module.position].converters, conv) - #addConverter(c.graph, c.module, conv) # upcoming + if c.config.symbolFiles != disabledSf: + addConverter(c.encoder, conv) proc addPureEnum*(c: PContext, e: PSym) = inclSym(c.graph.ifaces[c.module.position].pureEnums, e) + if c.config.symbolFiles != disabledSf: + addPureEnum(c.encoder, e) proc addPattern*(c: PContext, p: PSym) = inclSym(c.patterns, p) inclSym(c.graph.ifaces[c.module.position].patterns, p) - #addPattern(c.graph, c.module, p) # upcoming + if c.config.symbolFiles != disabledSf: + addTrmacro(c.encoder, p) + +proc exportSym*(c: PContext; s: PSym) = + strTableAdd(c.module.semtab(c.graph), s) + if c.config.symbolFiles != disabledSf: + addExported(c.encoder, s) + +proc reexportSym*(c: PContext; s: PSym) = + strTableAdd(c.module.semtab(c.graph), s) + if c.config.symbolFiles != disabledSf: + addReexport(c.encoder, s) proc newLib*(kind: TLibKind): PLib = new(result) @@ -489,4 +503,4 @@ proc storeRodNode*(c: PContext, n: PNode) = proc saveRodFile*(c: PContext) = if c.config.symbolFiles != disabledSf: - saveRodFile(toRodFile(c.config, c.filename.AbsoluteFile), c.encoder) + saveRodFile(toRodFile(c.config, AbsoluteFile toFullPath(c.config, FileIndex c.module.position)), c.encoder) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 1e5772189..4a04cfb6d 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1014,7 +1014,7 @@ proc semDirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = proc buildEchoStmt(c: PContext, n: PNode): PNode = # we MUST not check 'n' for semantics again here! But for now we give up: result = newNodeI(nkCall, n.info) - var e = strTableGet(c.graph.systemModule.tab, getIdent(c.cache, "echo")) + let e = systemModuleSym(c.graph, getIdent(c.cache, "echo")) if e != nil: result.add(newSymNode(e)) else: @@ -1897,7 +1897,7 @@ proc lookUpForDeclared(c: PContext, n: PNode, onlyCurrentScope: bool): PSym = if m == c.module: result = strTableGet(c.topLevelScope.symbols, ident) else: - result = strTableGet(m.tab, ident) + result = someSym(c.graph, m, ident) of nkSym: result = n.sym of nkOpenSymChoice, nkClosedSymChoice: @@ -2504,7 +2504,7 @@ proc semBlock(c: PContext, n: PNode; flags: TExprFlags): PNode = elif labl.owner == nil: labl.owner = c.p.owner n[0] = newSymNode(labl, n[0].info) - suggestSym(c.config, n[0].info, labl, c.graph.usageSym) + suggestSym(c.graph, n[0].info, labl, c.graph.usageSym) styleCheckDef(c.config, labl) onDef(n[0].info, labl) n[1] = semExpr(c, n[1], flags) @@ -2522,15 +2522,12 @@ proc semExportExcept(c: PContext, n: PNode): PNode = let exceptSet = readExceptSet(c, n) let exported = moduleName.sym result = newNodeI(nkExportStmt, n.info) - strTableAdd(c.module.tab, exported) - var i: TTabIter - var s = initTabIter(i, exported.tab) - while s != nil: + reexportSym(c, exported) + for s in allSyms(c.graph, exported): if s.kind in ExportableSymKinds+{skModule} and s.name.id notin exceptSet and sfError notin s.flags: - strTableAdd(c.module.tab, s) + reexportSym(c, s) result.add newSymNode(s, n.info) - s = nextIter(i, exported.tab) markUsed(c, n.info, exported) proc semExport(c: PContext, n: PNode): PNode = @@ -2548,15 +2545,12 @@ proc semExport(c: PContext, n: PNode): PNode = localError(c.config, a.info, errGenerated, "cannot export: " & renderTree(a)) elif s.kind == skModule: # forward everything from that module: - strTableAdd(c.module.tab, s) - var ti: TTabIter - var it = initTabIter(ti, s.tab) - while it != nil: + reexportSym(c, s) + for it in allSyms(c.graph, s): if it.kind in ExportableSymKinds+{skModule}: - strTableAdd(c.module.tab, it) + reexportSym(c, it) result.add newSymNode(it, a.info) specialSyms(c, it) - it = nextIter(ti, s.tab) markUsed(c, n.info, s) else: while s != nil: @@ -2565,7 +2559,7 @@ proc semExport(c: PContext, n: PNode): PNode = "; enum field cannot be exported individually") if s.kind in ExportableSymKinds+{skModule} and sfError notin s.flags: result.add(newSymNode(s, a.info)) - strTableAdd(c.module.tab, s) + reexportSym(c, s) markUsed(c, n.info, s) specialSyms(c, s) if s.kind == skType and sfPure notin s.flags: @@ -2575,7 +2569,7 @@ proc semExport(c: PContext, n: PNode): PNode = var e = etyp.n[j].sym if e.kind != skEnumField: internalError(c.config, s.info, "rawImportSymbol") - strTableAdd(c.module.tab, e) + reexportSym(c, e) s = nextOverloadIter(o, c, a) diff --git a/compiler/semfields.nim b/compiler/semfields.nim index 7e8fffc01..93184c568 100644 --- a/compiler/semfields.nim +++ b/compiler/semfields.nim @@ -106,7 +106,7 @@ proc semForFields(c: PContext, n: PNode, m: TMagic): PNode = # so that 'break' etc. work as expected, we produce # a 'while true: stmt; break' loop ... result = newNodeI(nkWhileStmt, n.info, 2) - var trueSymbol = strTableGet(c.graph.systemModule.tab, getIdent(c.cache, "true")) + var trueSymbol = systemModuleSym(c.graph, getIdent(c.cache, "true")) if trueSymbol == nil: localError(c.config, n.info, "system needs: 'true'") trueSymbol = newSym(skUnknown, getIdent(c.cache, "true"), nextSymId c.idgen, getCurrOwner(c), n.info) diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index 1f633549a..dfbb022c8 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -481,7 +481,7 @@ proc semGenericStmt(c: PContext, n: PNode, if sfGenSym in s.flags and s.ast == nil: body = n[bodyPos] else: - body = s.getBody + body = getBody(c.graph, s) else: body = n[bodyPos] n[bodyPos] = semGenericStmtScope(c, body, flags, ctx) closeScope(c) diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 3f1cdace3..d273cac14 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -160,7 +160,7 @@ proc fixupInstantiatedSymbols(c: PContext, s: PSym) = pushInfoContext(c.config, oldPrc.info) openScope(c) var n = oldPrc.ast - n[bodyPos] = copyTree(s.getBody) + n[bodyPos] = copyTree(getBody(c.graph, s)) instantiateBody(c, n, oldPrc.typ.n, oldPrc, s) closeScope(c) popInfoContext(c.config) @@ -383,7 +383,7 @@ proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, if n[pragmasPos].kind != nkEmpty: pragma(c, result, n[pragmasPos], allRoutinePragmas) if isNil(n[bodyPos]): - n[bodyPos] = copyTree(fn.getBody) + n[bodyPos] = copyTree(getBody(c.graph, fn)) if c.inGenericContext == 0: instantiateBody(c, n, fn.typ.n, result, fn) sideEffectsCheck(c, result) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index c2c59bb31..a76116b1c 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -66,7 +66,7 @@ proc semBreakOrContinue(c: PContext, n: PNode): PNode = x.info = n.info incl(s.flags, sfUsed) n[0] = x - suggestSym(c.config, x.info, s, c.graph.usageSym) + suggestSym(c.graph, x.info, s, c.graph.usageSym) onUse(x.info, s) else: localError(c.config, n.info, errInvalidControlFlowX % s.name.s) @@ -332,7 +332,7 @@ proc semIdentDef(c: PContext, n: PNode, kind: TSymKind): PSym = discard result = n.info let info = getLineInfo(n) - suggestSym(c.config, info, result, c.graph.usageSym) + suggestSym(c.graph, info, result, c.graph.usageSym) proc checkNilable(c: PContext; v: PSym) = if {sfGlobal, sfImportc} * v.flags == {sfGlobal} and v.typ.requiresInit: @@ -1055,14 +1055,14 @@ proc typeDefLeftSidePass(c: PContext, typeSection: PNode, i: int) = if pkg.isNil or pkg.kind != skPackage: localError(c.config, name.info, "unknown package name: " & pkgName.s) else: - let typsym = pkg.tab.strTableGet(typName) + let typsym = c.graph.packageTypes.strTableGet(typName) if typsym.isNil: s = semIdentDef(c, name[1], skType) onDef(name[1].info, s) s.typ = newTypeS(tyObject, c) s.typ.sym = s s.flags.incl sfForward - pkg.tab.strTableAdd s + c.graph.packageTypes.strTableAdd s addInterfaceDecl(c, s) elif typsym.kind == skType and sfForward in typsym.flags: s = typsym @@ -1088,7 +1088,7 @@ proc typeDefLeftSidePass(c: PContext, typeSection: PNode, i: int) = if not isTopLevel(c) or pkg.isNil: localError(c.config, name.info, "only top level types in a package can be 'package'") else: - let typsym = pkg.tab.strTableGet(s.name) + let typsym = c.graph.packageTypes.strTableGet(s.name) if typsym != nil: if sfForward notin typsym.flags or sfNoForward notin typsym.flags: typeCompleted(typsym) @@ -1956,7 +1956,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, if not comesFromShadowScope: excl(proto.flags, sfForward) incl(proto.flags, sfWasForwarded) - suggestSym(c.config, s.info, proto, c.graph.usageSym) + suggestSym(c.graph, s.info, proto, c.graph.usageSym) closeScope(c) # close scope with wrong parameter symbols openScope(c) # open scope for old (correct) parameter symbols if proto.ast[genericParamsPos].kind != nkEmpty: diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index 14c3b9a11..65dc95916 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -250,7 +250,7 @@ proc semTemplSymbol(c: PContext, n: PNode, s: PSym; isField: bool): PNode = else: result = newSymNode(s, n.info) # Issue #12832 when defined(nimsuggest): - suggestSym(c.config, n.info, s, c.graph.usageSym, false) + suggestSym(c.graph, n.info, s, c.graph.usageSym, false) if {optStyleHint, optStyleError} * c.config.globalOptions != {}: styleCheckUse(c.config, n.info, s) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 99c588657..64113a4c6 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -140,7 +140,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = if result.sym != nil and sfExported in result.sym.flags: incl(e.flags, sfUsed) incl(e.flags, sfExported) - if not isPure: strTableAdd(c.module.tab, e) + if not isPure: exportSym(c, e) result.n.add symNode styleCheckDef(c.config, e) onDef(e.info, e) @@ -779,7 +779,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, n[i][1].info else: n[i].info - suggestSym(c.config, info, f, c.graph.usageSym) + suggestSym(c.graph, info, f, c.graph.usageSym) f.typ = typ f.position = pos f.options = c.config.options diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim index 9a16cc3e0..156bc66d7 100644 --- a/compiler/sighashes.nim +++ b/compiler/sighashes.nim @@ -371,7 +371,7 @@ proc symBodyDigest*(graph: ModuleGraph, sym: PSym): SigHash = if sym.ast != nil: md5Init(c) c.md5Update(cast[cstring](result.addr), sizeof(result)) - hashBodyTree(graph, c, sym.ast[bodyPos]) + hashBodyTree(graph, c, getBody(graph, sym)) c.md5Final(result.MD5Digest) graph.symBodyHashes[sym.id] = result diff --git a/compiler/suggest.nim b/compiler/suggest.nim index 73929f813..560d20b3f 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -56,10 +56,10 @@ proc findDocComment(n: PNode): PNode = elif n.kind in {nkAsgn, nkFastAsgn} and n.len == 2: result = findDocComment(n[1]) -proc extractDocComment(s: PSym): string = +proc extractDocComment(g: ModuleGraph; s: PSym): string = var n = findDocComment(s.ast) if n.isNil and s.kind in routineKinds and s.ast != nil: - n = findDocComment(s.ast[bodyPos]) + n = findDocComment(getBody(g, s)) if not n.isNil: result = n.comment.replace("\n##", "\n").strip else: @@ -117,7 +117,7 @@ proc getTokenLenFromSource(conf: ConfigRef; ident: string; info: TLineInfo): int elif sourceIdent != ident: result = 0 -proc symToSuggest(conf: ConfigRef; s: PSym, isLocal: bool, section: IdeCmd, info: TLineInfo; +proc symToSuggest(g: ModuleGraph; s: PSym, isLocal: bool, section: IdeCmd, info: TLineInfo; quality: range[0..100]; prefix: PrefixMatch; inTypeContext: bool; scope: int; useSuppliedInfo = false): Suggest = @@ -136,7 +136,7 @@ proc symToSuggest(conf: ConfigRef; s: PSym, isLocal: bool, section: IdeCmd, info if u.fileIndex == info.fileIndex: inc c result.localUsages = c result.symkind = byte s.kind - if optIdeTerse notin conf.globalOptions: + if optIdeTerse notin g.config.globalOptions: result.qualifiedPath = @[] if not isLocal and s.kind != skModule: let ow = s.owner @@ -156,20 +156,20 @@ proc symToSuggest(conf: ConfigRef; s: PSym, isLocal: bool, section: IdeCmd, info else: result.forth = "" when defined(nimsuggest) and not defined(noDocgen) and not defined(leanCompiler): - result.doc = s.extractDocComment + result.doc = extractDocComment(g, s) let infox = if useSuppliedInfo or section in {ideUse, ideHighlight, ideOutline}: info else: s.info - result.filePath = toFullPath(conf, infox) + result.filePath = toFullPath(g.config, infox) result.line = toLinenumber(infox) result.column = toColumn(infox) - result.version = conf.suggestVersion + result.version = g.config.suggestVersion result.tokenLen = if section != ideHighlight: s.name.s.len else: - getTokenLenFromSource(conf, s.name.s, infox) + getTokenLenFromSource(g.config, s.name.s, infox) proc `$`*(suggest: Suggest): string = result = $suggest.section @@ -261,7 +261,7 @@ proc fieldVisible*(c: PContext, f: PSym): bool {.inline.} = proc suggestField(c: PContext, s: PSym; f: PNode; info: TLineInfo; outputs: var Suggestions) = var pm: PrefixMatch if filterSym(s, f, pm) and fieldVisible(c, s): - outputs.add(symToSuggest(c.config, s, isLocal=true, ideSug, info, 100, pm, c.inTypeContext > 0, 0)) + outputs.add(symToSuggest(c.graph, s, isLocal=true, ideSug, info, 100, pm, c.inTypeContext > 0, 0)) proc getQuality(s: PSym): range[0..100] = if s.typ != nil and s.typ.len > 1: @@ -275,7 +275,7 @@ template wholeSymTab(cond, section: untyped) {.dirty.} = let it = item var pm: PrefixMatch if cond: - outputs.add(symToSuggest(c.config, it, isLocal = isLocal, section, info, getQuality(it), + outputs.add(symToSuggest(c.graph, it, isLocal = isLocal, section, info, getQuality(it), pm, c.inTypeContext > 0, scopeN)) proc suggestSymList(c: PContext, list, f: PNode; info: TLineInfo, outputs: var Suggestions) = @@ -346,7 +346,7 @@ proc suggestEverything(c: PContext, n, f: PNode, outputs: var Suggestions) = for (it, scopeN, isLocal) in allSyms(c): var pm: PrefixMatch if filterSym(it, f, pm): - outputs.add(symToSuggest(c.config, it, isLocal = isLocal, ideSug, n.info, 0, pm, + outputs.add(symToSuggest(c.graph, it, isLocal = isLocal, ideSug, n.info, 0, pm, c.inTypeContext > 0, scopeN)) proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions) = @@ -365,10 +365,10 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions) let m = c.graph.importModuleCallback(c.graph, c.module, fileInfoIdx(c.config, fullPath)) if m == nil: typ = nil else: - for it in items(n.sym.tab): + for it in allSyms(c.graph, n.sym): if filterSym(it, field, pm): - outputs.add(symToSuggest(c.config, it, isLocal=false, ideSug, n.info, 100, pm, c.inTypeContext > 0, -100)) - outputs.add(symToSuggest(c.config, m, isLocal=false, ideMod, n.info, 100, PrefixMatch.None, + outputs.add(symToSuggest(c.graph, it, isLocal=false, ideSug, n.info, 100, pm, c.inTypeContext > 0, -100)) + outputs.add(symToSuggest(c.graph, m, isLocal=false, ideMod, n.info, 100, PrefixMatch.None, c.inTypeContext > 0, -99)) if typ == nil: @@ -378,11 +378,11 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions) # all symbols accessible, because we are in the current module: for it in items(c.topLevelScope.symbols): if filterSym(it, field, pm): - outputs.add(symToSuggest(c.config, it, isLocal=false, ideSug, n.info, 100, pm, c.inTypeContext > 0, -99)) + outputs.add(symToSuggest(c.graph, it, isLocal=false, ideSug, n.info, 100, pm, c.inTypeContext > 0, -99)) else: - for it in items(n.sym.tab): + for it in allSyms(c.graph, n.sym): if filterSym(it, field, pm): - outputs.add(symToSuggest(c.config, it, isLocal=false, ideSug, n.info, 100, pm, c.inTypeContext > 0, -99)) + outputs.add(symToSuggest(c.graph, it, isLocal=false, ideSug, n.info, 100, pm, c.inTypeContext > 0, -99)) else: # fallback: suggestEverything(c, n, field, outputs) @@ -440,27 +440,27 @@ when defined(nimsuggest): if infoB.infoToInt == infoAsInt: return s.allUsages.add(info) -proc findUsages(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym) = - if conf.suggestVersion == 1: - if usageSym == nil and isTracked(info, conf.m.trackPos, s.name.s.len): +proc findUsages(g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym) = + if g.config.suggestVersion == 1: + if usageSym == nil and isTracked(info, g.config.m.trackPos, s.name.s.len): usageSym = s - suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideUse, info, 100, PrefixMatch.None, false, 0)) + suggestResult(g.config, symToSuggest(g, s, isLocal=false, ideUse, info, 100, PrefixMatch.None, false, 0)) elif s == usageSym: - if conf.lastLineInfo != info: - suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideUse, info, 100, PrefixMatch.None, false, 0)) - conf.lastLineInfo = info + if g.config.lastLineInfo != info: + suggestResult(g.config, symToSuggest(g, s, isLocal=false, ideUse, info, 100, PrefixMatch.None, false, 0)) + g.config.lastLineInfo = info when defined(nimsuggest): - proc listUsages*(conf: ConfigRef; s: PSym) = + proc listUsages*(g: ModuleGraph; s: PSym) = #echo "usages ", s.allUsages.len for info in s.allUsages: let x = if info == s.info and info.col == s.info.col: ideDef else: ideUse - suggestResult(conf, symToSuggest(conf, s, isLocal=false, x, info, 100, PrefixMatch.None, false, 0)) + suggestResult(g.config, symToSuggest(g, s, isLocal=false, x, info, 100, PrefixMatch.None, false, 0)) -proc findDefinition(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym) = +proc findDefinition(g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym) = if s.isNil: return - if isTracked(info, conf.m.trackPos, s.name.s.len) or (s == usageSym and sfForward notin s.flags): - suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0, useSuppliedInfo = s == usageSym)) + if isTracked(info, g.config.m.trackPos, s.name.s.len) or (s == usageSym and sfForward notin s.flags): + suggestResult(g.config, symToSuggest(g, s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0, useSuppliedInfo = s == usageSym)) if sfForward notin s.flags: suggestQuit() else: @@ -472,8 +472,9 @@ proc ensureIdx[T](x: var T, y: int) = proc ensureSeq[T](x: var seq[T]) = if x == nil: newSeq(x, 0) -proc suggestSym*(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym; isDecl=true) {.inline.} = +proc suggestSym*(g: ModuleGraph; info: TLineInfo; s: PSym; usageSym: var PSym; isDecl=true) {.inline.} = ## misnamed: should be 'symDeclared' + let conf = g.config when defined(nimsuggest): if conf.suggestVersion == 0: if s.allUsages.len == 0: @@ -482,15 +483,15 @@ proc suggestSym*(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym; s.addNoDup(info) if conf.ideCmd == ideUse: - findUsages(conf, info, s, usageSym) + findUsages(g, info, s, usageSym) elif conf.ideCmd == ideDef: - findDefinition(conf, info, s, usageSym) + findDefinition(g, info, s, usageSym) elif conf.ideCmd == ideDus and s != nil: if isTracked(info, conf.m.trackPos, s.name.s.len): - suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0)) - findUsages(conf, info, s, usageSym) + suggestResult(conf, symToSuggest(g, s, isLocal=false, ideDef, info, 100, PrefixMatch.None, false, 0)) + findUsages(g, info, s, usageSym) elif conf.ideCmd == ideHighlight and info.fileIndex == conf.m.trackPos.fileIndex: - suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideHighlight, info, 100, PrefixMatch.None, false, 0)) + suggestResult(conf, symToSuggest(g, s, isLocal=false, ideHighlight, info, 100, PrefixMatch.None, false, 0)) elif conf.ideCmd == ideOutline and isDecl: # if a module is included then the info we have is inside the include and # we need to walk up the owners until we find the outer most module, @@ -503,7 +504,7 @@ proc suggestSym*(conf: ConfigRef; info: TLineInfo; s: PSym; usageSym: var PSym; parentModule = parentModule.owner if parentFileIndex == conf.m.trackPos.fileIndex: - suggestResult(conf, symToSuggest(conf, s, isLocal=false, ideOutline, info, 100, PrefixMatch.None, false, 0)) + suggestResult(conf, symToSuggest(g, s, isLocal=false, ideOutline, info, 100, PrefixMatch.None, false, 0)) proc extractPragma(s: PSym): PNode = if s.kind in routineKinds: @@ -572,7 +573,7 @@ proc markUsed(c: PContext; info: TLineInfo; s: PSym) = if sfError in s.flags: userError(conf, info, s) when defined(nimsuggest): - suggestSym(conf, info, s, c.graph.usageSym, false) + suggestSym(c.graph, info, s, c.graph.usageSym, false) if {optStyleHint, optStyleError} * conf.globalOptions != {}: styleCheckUse(conf, info, s) markOwnerModuleAsUsed(c, s) @@ -658,7 +659,7 @@ proc suggestSentinel*(c: PContext) = for (it, scopeN, isLocal) in allSyms(c): var pm: PrefixMatch if filterSymNoOpr(it, nil, pm): - outputs.add(symToSuggest(c.config, it, isLocal = isLocal, ideSug, + outputs.add(symToSuggest(c.graph, it, isLocal = isLocal, ideSug, newLineInfo(c.config.m.trackPos.fileIndex, 0, -1), 0, PrefixMatch.None, false, scopeN)) diff --git a/compiler/transf.nim b/compiler/transf.nim index 461db9e89..e9ab6b47d 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -126,7 +126,7 @@ proc transformSymAux(c: PTransf, n: PNode): PNode = var tc = c.transCon if sfBorrow in s.flags and s.kind in routineKinds: # simply exchange the symbol: - b = s.getBody + b = getBody(c.graph, s) if b.kind != nkSym: internalError(c.graph.config, n.info, "wrong AST for borrowed symbol") b = newSymNode(b.sym, n.info) elif c.inlining > 0: @@ -594,7 +594,7 @@ proc findWrongOwners(c: PTransf, n: PNode) = else: for i in 0.. Date: Thu, 7 Jan 2021 20:57:11 +0100 Subject: [PATCH 1307/3768] Improve documentation for the md5 module (#16631) --- lib/pure/md5.nim | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/lib/pure/md5.nim b/lib/pure/md5.nim index ba944ba81..11c324548 100644 --- a/lib/pure/md5.nim +++ b/lib/pure/md5.nim @@ -7,11 +7,14 @@ # distribution, for details about the copyright. # -## Module for computing `MD5 checksums `_. +## Module for computing [MD5 checksums](https://en.wikipedia.org/wiki/MD5). ## -## **See also:** -## * `base64 module`_ implements a base64 encoder and decoder -## * `std/sha1 module `_ for a sha1 encoder and decoder +## **Note:** The procs in this module can be used at compile time. +## +## See also +## ======== +## * `base64 module`_ implements a Base64 encoder and decoder +## * `std/sha1 module `_ for a SHA-1 encoder and decoder ## * `hashes module`_ for efficient computations of hash values ## for diverse Nim types @@ -22,8 +25,8 @@ type MD5State = array[0..3, uint32] MD5Block = array[0..15, uint32] MD5CBits = array[0..7, uint8] - MD5Digest* = array[0..15, uint8] ## \ - ## MD5 checksum of a string, obtained with `toMD5 proc <#toMD5,string>`_. + MD5Digest* = array[0..15, uint8] + ## MD5 checksum of a string, obtained with the `toMD5 proc <#toMD5,string>`_. MD5Buffer = array[0..63, uint8] MD5Context* {.final.} = object state: MD5State @@ -180,7 +183,7 @@ proc md5Final*(c: var MD5Context, digest: var MD5Digest) {.raises: [], tags: [], proc toMD5*(s: string): MD5Digest = ## Computes the `MD5Digest` value for a string `s`. ## - ## See also: + ## **See also:** ## * `getMD5 proc <#getMD5,string>`_ which returns a string representation ## of the `MD5Digest` ## * `$ proc <#$,MD5Digest>`_ for converting MD5Digest to string @@ -202,10 +205,8 @@ proc `$`*(d: MD5Digest): string = proc getMD5*(s: string): string = ## Computes an MD5 value of `s` and returns its string representation. - ## .. note:: - ## available at compile time ## - ## See also: + ## **See also:** ## * `toMD5 proc <#toMD5,string>`_ which returns the `MD5Digest` of a string runnableExamples: assert getMD5("abc") == "900150983cd24fb0d6963f7d28e17f72" @@ -226,9 +227,9 @@ proc `==`*(D1, D2: MD5Digest): bool = proc md5Init*(c: var MD5Context) = - ## Initializes a `MD5Context`. + ## Initializes an `MD5Context`. ## - ## If you use `toMD5 proc <#toMD5,string>`_ there's no need to call this + ## If you use the `toMD5 proc <#toMD5,string>`_, there's no need to call this ## function explicitly. c.state[0] = 0x67452301'u32 c.state[1] = 0xEFCDAB89'u32 @@ -241,7 +242,7 @@ proc md5Init*(c: var MD5Context) = proc md5Update*(c: var MD5Context, input: cstring, len: int) = ## Updates the `MD5Context` with the `input` data of length `len`. ## - ## If you use `toMD5 proc <#toMD5,string>`_ there's no need to call this + ## If you use the `toMD5 proc <#toMD5,string>`_, there's no need to call this ## function explicitly. var input = input var Index = int((c.count[0] shr 3) and 0x3F) @@ -263,7 +264,7 @@ proc md5Update*(c: var MD5Context, input: cstring, len: int) = proc md5Final*(c: var MD5Context, digest: var MD5Digest) = ## Finishes the `MD5Context` and stores the result in `digest`. ## - ## If you use `toMD5 proc <#toMD5,string>`_ there's no need to call this + ## If you use the `toMD5 proc <#toMD5,string>`_, there's no need to call this ## function explicitly. var Bits: MD5CBits From 596da7f9a0d6303738d368e119e49461591bfe71 Mon Sep 17 00:00:00 2001 From: Andrew Date: Fri, 8 Jan 2021 11:29:32 +0300 Subject: [PATCH 1308/3768] happy new year 2021 again (#16638) --- compiler/options.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/options.nim b/compiler/options.nim index 4a013f8fc..6cdb5db63 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -19,7 +19,7 @@ const useEffectSystem* = true useWriteTracking* = false hasFFI* = defined(nimHasLibFFI) - copyrightYear* = "2020" + copyrightYear* = "2021" type # please make sure we have under 32 options # (improves code efficiency a lot!) From add1ccb6cb4ad4b444a5cfa3ef5573b0c1d47d09 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 8 Jan 2021 13:36:03 +0100 Subject: [PATCH 1309/3768] compiler: minor refactoring (#16633) --- compiler/main.nim | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/compiler/main.nim b/compiler/main.nim index 4c67aaea7..438f90ba6 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -211,18 +211,17 @@ proc mainCommand*(graph: ModuleGraph) = defineSymbol(conf.symbols, "nimdoc") body - block: ## command prepass - if conf.cmd == cmdCrun: conf.globalOptions.incl {optRun, optUseNimcache} - if conf.cmd notin cmdBackends + {cmdTcc}: customizeForBackend(backendC) - if conf.outDir.isEmpty: - # doc like commands can generate a lot of files (especially with --project) - # so by default should not end up in $PWD nor in $projectPath. - conf.outDir = block: - var ret = if optUseNimcache in conf.globalOptions: getNimcacheDir(conf) - else: conf.projectPath - doAssert ret.string.isAbsolute # `AbsoluteDir` is not a real guarantee - if conf.cmd in cmdDocLike + {cmdRst2html, cmdRst2tex}: ret = ret / htmldocsDir - ret + ## command prepass + if conf.cmd == cmdCrun: conf.globalOptions.incl {optRun, optUseNimcache} + if conf.cmd notin cmdBackends + {cmdTcc}: customizeForBackend(backendC) + if conf.outDir.isEmpty: + # doc like commands can generate a lot of files (especially with --project) + # so by default should not end up in $PWD nor in $projectPath. + var ret = if optUseNimcache in conf.globalOptions: getNimcacheDir(conf) + else: conf.projectPath + doAssert ret.string.isAbsolute # `AbsoluteDir` is not a real guarantee + if conf.cmd in cmdDocLike + {cmdRst2html, cmdRst2tex}: ret = ret / htmldocsDir + conf.outDir = ret ## process all commands case conf.cmd From 38b8d080f29021d2685e09d552c2a7753ef25484 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Fri, 8 Jan 2021 07:42:38 -0600 Subject: [PATCH 1310/3768] close #1550 add testcase (#16640) --- tests/iter/t1550.nim | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 tests/iter/t1550.nim diff --git a/tests/iter/t1550.nim b/tests/iter/t1550.nim new file mode 100644 index 000000000..8ad96f0da --- /dev/null +++ b/tests/iter/t1550.nim @@ -0,0 +1,20 @@ +type + A[T] = iterator(x: T): T {.gcsafe, closure.} + +iterator aimp[T](x: T): T {.gcsafe, closure.} = + var total = 0 + while (total < 100): + yield total + total += x + +iterator bimp(y: A[int], z:int): int {.gcsafe, closure.} = + for i in y(z): + yield i + +for x in aimp[int](3): + discard x + +var y = aimp[int] +var z = bimp +for x in z(y, 1): + discard x \ No newline at end of file From bfcb7c1621ba4cadb228125e924f1fe6bf91e20c Mon Sep 17 00:00:00 2001 From: Joey Date: Fri, 8 Jan 2021 11:48:23 -0700 Subject: [PATCH 1311/3768] DELETE requests should always have a content-length header (#16618) * DELETE requests should always have a content-length header Not having DELETE in this list is causing hanging when trying to close webdriver sessions in [halonium](https://github.com/halonium/halonium/issues/10) and likely any other implementation of the webdriver protocol. Both at least chromedriver and geckodriver are affected by this issue. * Change the content length calculation to match the http spec For reference: https://www.w3.org/Protocols/HTTP/1.0/draft-ietf-http-spec.html#Entity-Body --- lib/pure/httpclient.nim | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 3093f5564..70f3327b1 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -980,8 +980,11 @@ proc requestAux(client: HttpClient | AsyncHttpClient, url, httpMethod: string, var data: seq[string] if multipart != nil and multipart.content.len > 0: data = await client.format(multipart) - elif httpMethod in ["POST", "PATCH", "PUT"] or body.len != 0: - client.headers["Content-Length"] = $body.len + else: + if body.len != 0: + client.headers["Content-Length"] = $body.len + elif httpMethod notin ["GET", "HEAD"] and not client.headers.hasKey("Content-Length"): + client.headers["Content-Length"] = "0" when client is AsyncHttpClient: if not client.parseBodyFut.isNil: From 2a426ca8e2d64e4e415c20e97a6fbc8563ab1b2f Mon Sep 17 00:00:00 2001 From: alaviss Date: Fri, 8 Jan 2021 14:04:17 -0600 Subject: [PATCH 1312/3768] kochdocs: fusion needs the js backend too (#16644) --- tools/kochdocs.nim | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tools/kochdocs.nim b/tools/kochdocs.nim index 81117dd52..75bb4443f 100644 --- a/tools/kochdocs.nim +++ b/tools/kochdocs.nim @@ -15,7 +15,9 @@ const var nimExe*: string -template isJsOnly(file: string): bool = file.isRelativeTo("lib/js") +template isJsOnly(file: string): bool = + file.isRelativeTo("lib/js") or + file.isRelativeTo("lib/fusion/js") proc exe*(f: string): string = result = addFileExt(f, ExeExt) From ffb130b59c3f3d03e694b76af37563b070e28af0 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 8 Jan 2021 14:09:58 -0800 Subject: [PATCH 1313/3768] skip docs for `lib/fusion` (docs already run in fusion repo) (#16645) * run CI docs on koch.nim changes to avoid future regressions * kochdocs: skip lib/fusion --- .github/workflows/ci_docs.yml | 2 +- tools/kochdocs.nim | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci_docs.yml b/.github/workflows/ci_docs.yml index 74ca804f3..fcc89dfc1 100644 --- a/.github/workflows/ci_docs.yml +++ b/.github/workflows/ci_docs.yml @@ -26,7 +26,7 @@ on: - 'tools/dochack/dochack.nim' - 'tools/kochdocs.nim' - '.github/workflows/ci_docs.yml' - + - 'koch.nim' jobs: build: diff --git a/tools/kochdocs.nim b/tools/kochdocs.nim index 75bb4443f..f258087e7 100644 --- a/tools/kochdocs.nim +++ b/tools/kochdocs.nim @@ -15,9 +15,7 @@ const var nimExe*: string -template isJsOnly(file: string): bool = - file.isRelativeTo("lib/js") or - file.isRelativeTo("lib/fusion/js") +template isJsOnly(file: string): bool = file.isRelativeTo("lib/js") proc exe*(f: string): string = result = addFileExt(f, ExeExt) @@ -187,7 +185,8 @@ lib/system/widestrs.nim """.splitWhitespace() proc follow(a: PathEntry): bool = - a.path.lastPathPart notin ["nimcache", "htmldocs", "includes", "deprecated", "genode"] + result = a.path.lastPathPart notin ["nimcache", "htmldocs", "includes", "deprecated", "genode"] and + not a.path.isRelativeTo("lib/fusion") for entry in walkDirRecFilter("lib", follow = follow): let a = entry.path if entry.kind != pcFile or a.splitFile.ext != ".nim" or From b7ff0b2a1128dc976379102e180ae883641d6c82 Mon Sep 17 00:00:00 2001 From: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Date: Sat, 9 Jan 2021 00:24:41 +0100 Subject: [PATCH 1314/3768] Use func in lenientops (#16641) --- lib/pure/lenientops.nim | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/lib/pure/lenientops.nim b/lib/pure/lenientops.nim index f73df5e5f..a8fc78e39 100644 --- a/lib/pure/lenientops.nim +++ b/lib/pure/lenientops.nim @@ -8,49 +8,49 @@ # ## This module offers implementations of common binary operations -## like ``+``, ``-``, ``*``, ``/`` and comparison operations, +## like `+`, `-`, `*`, `/` and comparison operations, ## which work for mixed float/int operands. ## All operations convert the integer operand into the ## type of the float operand. For numerical expressions, the return ## type is always the type of the float involved in the expression, ## i.e., there is no auto conversion from float32 to float64. ## -## Note: In general, auto-converting from int to float loses +## **Note:** In general, auto-converting from int to float loses ## information, which is why these operators live in a separate ## module. Use with care. ## ## Regarding binary comparison, this module only provides unequal operators. -## The equality operator ``==`` is omitted, because depending on the use case +## The equality operator `==` is omitted, because depending on the use case ## either casting to float or rounding to int might be preferred, and users ## should make an explicit choice. -proc `+`*[I: SomeInteger, F: SomeFloat](i: I, f: F): F {.noSideEffect, inline.} = +func `+`*[I: SomeInteger, F: SomeFloat](i: I, f: F): F {.inline.} = F(i) + f -proc `+`*[I: SomeInteger, F: SomeFloat](f: F, i: I): F {.noSideEffect, inline.} = +func `+`*[I: SomeInteger, F: SomeFloat](f: F, i: I): F {.inline.} = f + F(i) -proc `-`*[I: SomeInteger, F: SomeFloat](i: I, f: F): F {.noSideEffect, inline.} = +func `-`*[I: SomeInteger, F: SomeFloat](i: I, f: F): F {.inline.} = F(i) - f -proc `-`*[I: SomeInteger, F: SomeFloat](f: F, i: I): F {.noSideEffect, inline.} = +func `-`*[I: SomeInteger, F: SomeFloat](f: F, i: I): F {.inline.} = f - F(i) -proc `*`*[I: SomeInteger, F: SomeFloat](i: I, f: F): F {.noSideEffect, inline.} = +func `*`*[I: SomeInteger, F: SomeFloat](i: I, f: F): F {.inline.} = F(i) * f -proc `*`*[I: SomeInteger, F: SomeFloat](f: F, i: I): F {.noSideEffect, inline.} = +func `*`*[I: SomeInteger, F: SomeFloat](f: F, i: I): F {.inline.} = f * F(i) -proc `/`*[I: SomeInteger, F: SomeFloat](i: I, f: F): F {.noSideEffect, inline.} = +func `/`*[I: SomeInteger, F: SomeFloat](i: I, f: F): F {.inline.} = F(i) / f -proc `/`*[I: SomeInteger, F: SomeFloat](f: F, i: I): F {.noSideEffect, inline.} = +func `/`*[I: SomeInteger, F: SomeFloat](f: F, i: I): F {.inline.} = f / F(i) -proc `<`*[I: SomeInteger, F: SomeFloat](i: I, f: F): bool {.noSideEffect, inline.} = +func `<`*[I: SomeInteger, F: SomeFloat](i: I, f: F): bool {.inline.} = F(i) < f -proc `<`*[I: SomeInteger, F: SomeFloat](f: F, i: I): bool {.noSideEffect, inline.} = +func `<`*[I: SomeInteger, F: SomeFloat](f: F, i: I): bool {.inline.} = f < F(i) -proc `<=`*[I: SomeInteger, F: SomeFloat](i: I, f: F): bool {.noSideEffect, inline.} = +func `<=`*[I: SomeInteger, F: SomeFloat](i: I, f: F): bool {.inline.} = F(i) <= f -proc `<=`*[I: SomeInteger, F: SomeFloat](f: F, i: I): bool {.noSideEffect, inline.} = +func `<=`*[I: SomeInteger, F: SomeFloat](f: F, i: I): bool {.inline.} = f <= F(i) # Note that we must not defined `>=` and `>`, because system.nim already has a From dbff2cd938b326279de0f3f97b2dd8c54a90468a Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sat, 9 Jan 2021 04:54:26 -0600 Subject: [PATCH 1315/3768] close #4834 add testcase (#16649) --- tests/cpp/t4834.nim | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 tests/cpp/t4834.nim diff --git a/tests/cpp/t4834.nim b/tests/cpp/t4834.nim new file mode 100644 index 000000000..0275b1b70 --- /dev/null +++ b/tests/cpp/t4834.nim @@ -0,0 +1,17 @@ +discard """ + targets: "cpp" +""" + +# issue #4834 +block: + defer: + let x = 0 + + +proc main() = + block: + defer: + raise newException(Exception, "foo") + +doAssertRaises(Exception): + main() From 65df5762a1f2011497350da0713a7bca05343326 Mon Sep 17 00:00:00 2001 From: vabresto <77133146+vabresto@users.noreply.github.com> Date: Sun, 10 Jan 2021 06:42:23 -0500 Subject: [PATCH 1316/3768] Add support for Transfer-Encoding: chunked (#16636) * Add support for Transfer-Encoding: chunked * Minor whitespace fixes * Use recv instead of recvLineInto * Undo changes to httpcore, inline changes --- lib/pure/asynchttpserver.nim | 44 ++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index 29a695379..86688d4b5 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -142,6 +142,17 @@ proc parseProtocol(protocol: string): tuple[orig: string, major, minor: int] = proc sendStatus(client: AsyncSocket, status: string): Future[void] = client.send("HTTP/1.1 " & status & "\c\L\c\L") +func hasChunkedEncoding(request: Request): bool = + ## Searches for a chunked transfer encoding + const transferEncoding = "Transfer-Encoding" + + if request.headers.hasKey(transferEncoding): + for encoding in seq[string](request.headers[transferEncoding]): + if "chunked" == encoding.strip: + # Returns true if it is both an HttpPost and has chunked encoding + return request.reqMethod == HttpPost + return false + proc processRequest( server: AsyncHttpServer, req: FutureVar[Request], @@ -261,6 +272,39 @@ proc processRequest( if request.body.len != contentLength: await request.respond(Http400, "Bad Request. Content-Length does not match actual.") return true + elif hasChunkedEncoding(request): + # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Transfer-Encoding + var sizeOrData = 0 + var bytesToRead = 0 + request.body = "" + + while true: + lineFut.mget.setLen(0) + lineFut.clean() + + # The encoding format alternates between specifying a number of bytes to read + # and the data to be read, of the previously specified size + if sizeOrData mod 2 == 0: + # Expect a number of chars to read + await client.recvLineInto(lineFut, maxLength = maxLine) + try: + bytesToRead = lineFut.mget.parseHexInt + except ValueError: + # Malformed request + await request.respond(Http411, ("Invalid chunked transfer encoding - " & + "chunk data size must be hex encoded")) + return true + else: + if bytesToRead == 0: + # Done reading chunked data + break + + # Read bytesToRead and add to body + # Note we add +2 because the line must be terminated by \r\n + let chunk = await client.recv(bytesToRead + 2) + request.body = request.body & chunk + + inc sizeOrData elif request.reqMethod == HttpPost: await request.respond(Http411, "Content-Length required.") return true From 7bde6aa37f52695736917a070bc25097f0cb0b34 Mon Sep 17 00:00:00 2001 From: Antonis Geralis <43617260+planetis-m@users.noreply.github.com> Date: Sun, 10 Jan 2021 15:40:53 +0200 Subject: [PATCH 1317/3768] Httpclient improvements (#15919) * Allow passing Uri instead of strings * Teach httpclient about 308 * Deprecate request proc where httpMethod is string * More use of HttpMethod enum Also fix handling of 308, I forgot to add the hunk to the previous commit. * Well behaved redirect handler * Also remove Transfer-Encoding * Removed unused proc * Secure redirection rules Strip sensitive headers for cross-domain redirects. * Allow httpMethod to be a string again This way unknown http verbs can be used without any problem. * Respect user-specified Host header * Missed multipart argument. * Try another method * add changelog * Fix hidden deprecation warning, parseEnum failing * This is wrong * Have to do it manually, parseEnum is not suitable * Review comments * update Co-authored-by: LemonBoy Co-authored-by: Dominik Picheta --- changelog.md | 3 + lib/pure/httpclient.nim | 189 ++++++++++++++++++++++------------- lib/pure/httpcore.nim | 57 ++++------- tests/stdlib/thttpclient.nim | 8 +- 4 files changed, 147 insertions(+), 110 deletions(-) diff --git a/changelog.md b/changelog.md index afe49f1f3..0705d0090 100644 --- a/changelog.md +++ b/changelog.md @@ -34,6 +34,9 @@ - Removed deprecated `iup` module from stdlib, it has already moved to [nimble](https://github.com/nim-lang/iup). +- various functions in `httpclient` now accept `url` of type `Uri`. Moreover `request` function's + `httpMethod` argument of type `string` was deprecated in favor of `HttpMethod` enum type. + - `nodejs` backend now supports osenv: `getEnv`, `putEnv`, `envPairs`, `delEnv`, `existsEnv`. - Added `cmpMem` to `system`. diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 70f3327b1..ea847ef8d 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -325,10 +325,14 @@ proc getDefaultSSL(): SslContext = result = defaultSslContext doAssert result != nil, "failure to initialize the SSL context" -proc newProxy*(url: string, auth = ""): Proxy = +proc newProxy*(url: string; auth = ""): Proxy = ## Constructs a new ``TProxy`` object. result = Proxy(url: parseUri(url), auth: auth) +proc newProxy*(url: Uri; auth = ""): Proxy = + ## Constructs a new ``TProxy`` object. + result = Proxy(url: url, auth: auth) + proc newMultipartData*: MultipartData {.inline.} = ## Constructs a new ``MultipartData`` object. MultipartData() @@ -457,29 +461,23 @@ proc sendFile(socket: Socket | AsyncSocket, await socket.send(buffer) file.close() -proc redirection(status: string): bool = - const redirectionNRs = ["301", "302", "303", "307", "308"] - for i in items(redirectionNRs): - if status.startsWith(i): - return true - -proc getNewLocation(lastURL: string, headers: HttpHeaders): string = - result = headers.getOrDefault"Location" - if result == "": httpError("location header expected") +proc getNewLocation(lastURL: Uri, headers: HttpHeaders): Uri = + let newLocation = headers.getOrDefault"Location" + if newLocation == "": httpError("location header expected") # Relative URLs. (Not part of the spec, but soon will be.) - let r = parseUri(result) - if r.hostname == "" and r.path != "": - var parsed = parseUri(lastURL) - parsed.path = r.path - parsed.query = r.query - parsed.anchor = r.anchor - result = $parsed + let parsedLocation = parseUri(newLocation) + if parsedLocation.hostname == "" and parsedLocation.path != "": + result = lastURL + result.path = parsedLocation.path + result.query = parsedLocation.query + result.anchor = parsedLocation.anchor + else: + result = parsedLocation -proc generateHeaders(requestUrl: Uri, httpMethod: string, headers: HttpHeaders, +proc generateHeaders(requestUrl: Uri, httpMethod: HttpMethod, headers: HttpHeaders, proxy: Proxy): string = # GET - let upperMethod = httpMethod.toUpperAscii() - result = upperMethod + result = $httpMethod result.add ' ' if proxy.isNil or requestUrl.scheme == "https": @@ -898,7 +896,7 @@ proc newConnection(client: HttpClient | AsyncHttpClient, connectUrl.hostname = url.hostname connectUrl.port = if url.port != "": url.port else: "443" - let proxyHeaderString = generateHeaders(connectUrl, $HttpConnect, + let proxyHeaderString = generateHeaders(connectUrl, HttpConnect, newHttpHeaders(), client.proxy) await client.socket.send(proxyHeaderString) let proxyResp = await parseResponse(client, false) @@ -967,14 +965,12 @@ proc override(fallback, override: HttpHeaders): HttpHeaders = for k, vs in override.table: result[k] = vs -proc requestAux(client: HttpClient | AsyncHttpClient, url, httpMethod: string, - body = "", headers: HttpHeaders = nil, +proc requestAux(client: HttpClient | AsyncHttpClient, url: Uri, + httpMethod: HttpMethod, body = "", headers: HttpHeaders = nil, multipart: MultipartData = nil): Future[Response | AsyncResponse] {.multisync.} = # Helper that actually makes the request. Does not handle redirects. - let requestUrl = parseUri(url) - - if requestUrl.scheme == "": + if url.scheme == "": raise newException(ValueError, "No uri scheme supplied.") var data: seq[string] @@ -992,13 +988,13 @@ proc requestAux(client: HttpClient | AsyncHttpClient, url, httpMethod: string, await client.parseBodyFut client.parseBodyFut = nil - await newConnection(client, requestUrl) + await newConnection(client, url) let newHeaders = client.headers.override(headers) if not newHeaders.hasKey("user-agent") and client.userAgent.len > 0: newHeaders["User-Agent"] = client.userAgent - let headerString = generateHeaders(requestUrl, httpMethod, newHeaders, + let headerString = generateHeaders(url, httpMethod, newHeaders, client.proxy) await client.socket.send(headerString) @@ -1020,12 +1016,13 @@ proc requestAux(client: HttpClient | AsyncHttpClient, url, httpMethod: string, elif body.len > 0: await client.socket.send(body) - let getBody = httpMethod.toLowerAscii() notin ["head", "connect"] and + let getBody = httpMethod notin {HttpHead, HttpConnect} and client.getBody result = await parseResponse(client, getBody) -proc request*(client: HttpClient | AsyncHttpClient, url: string, - httpMethod: string, body = "", headers: HttpHeaders = nil, +proc request*(client: HttpClient | AsyncHttpClient, url: Uri | string, + httpMethod: HttpMethod | string = HttpGet, body = "", + headers: HttpHeaders = nil, multipart: MultipartData = nil): Future[Response | AsyncResponse] {.multisync.} = ## Connects to the hostname specified by the URL and performs a request @@ -1040,33 +1037,87 @@ proc request*(client: HttpClient | AsyncHttpClient, url: string, ## ## You need to make sure that the ``url`` doesn't contain any newline ## characters. Failing to do so will raise ``AssertionDefect``. - doAssert(not url.contains({'\c', '\L'}), "url shouldn't contain any newline characters") + ## + ## **Deprecated since v1.5**: use HttpMethod enum instead; string parameter httpMethod is deprecated + when url is string: + doAssert(not url.contains({'\c', '\L'}), "url shouldn't contain any newline characters") + let url = parseUri(url) + + when httpMethod is string: + {.warning: + "Deprecated since v1.5; use HttpMethod enum instead; string parameter httpMethod is deprecated".} + let httpMethod = case httpMethod + of "HEAD": + HttpHead + of "GET": + HttpGet + of "POST": + HttpPost + of "PUT": + HttpPut + of "DELETE": + HttpDelete + of "TRACE": + HttpTrace + of "OPTIONS": + HttpOptions + of "CONNECT": + HttpConnect + of "PATCH": + HttpPatch + else: + raise newException(ValueError, "Invalid HTTP method name: " & httpMethod) result = await client.requestAux(url, httpMethod, body, headers, multipart) var lastURL = url for i in 1..client.maxRedirects: - if result.status.redirection(): - let redirectTo = getNewLocation(lastURL, result.headers) - # Guarantee method for HTTP 307: see https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/307 - var meth = if result.status == "307": httpMethod else: "GET" - result = await client.requestAux(redirectTo, meth, body, headers, multipart) - lastURL = redirectTo + let statusCode = result.code -proc request*(client: HttpClient | AsyncHttpClient, url: string, - httpMethod = HttpGet, body = "", headers: HttpHeaders = nil, - multipart: MultipartData = nil): Future[Response | AsyncResponse] - {.multisync.} = - ## Connects to the hostname specified by the URL and performs a request - ## using the method specified. - ## - ## Connection will be kept alive. Further requests on the same ``client`` to - ## the same hostname will not require a new connection to be made. The - ## connection can be closed by using the ``close`` procedure. - ## - ## When a request is made to a different hostname, the current connection will - ## be closed. - result = await request(client, url, $httpMethod, body, headers, multipart) + if statusCode notin {Http301, Http302, Http303, Http307, Http308}: + break + + let redirectTo = getNewLocation(lastURL, result.headers) + var redirectMethod: HttpMethod + var redirectBody: string + # For more informations about the redirect methods see: + # https://developer.mozilla.org/en-US/docs/Web/HTTP/Redirections + case statusCode + of Http301, Http302, Http303: + # The method is changed to GET unless it is GET or HEAD (RFC2616) + if httpMethod notin {HttpGet, HttpHead}: + redirectMethod = HttpGet + else: + redirectMethod = httpMethod + # The body is stripped away + redirectBody = "" + # Delete any header value associated with the body + if not headers.isNil(): + headers.del("Content-Length") + headers.del("Content-Type") + headers.del("Transfer-Encoding") + of Http307, Http308: + # The method and the body are unchanged + redirectMethod = httpMethod + redirectBody = body + else: + # Unreachable + doAssert(false) + + # Check if the redirection is to the same domain or a sub-domain (foo.com + # -> sub.foo.com) + if redirectTo.hostname != lastURL.hostname and + not redirectTo.hostname.endsWith("." & lastURL.hostname): + # Perform some cleanup of the header values + if headers != nil: + # Delete the Host header + headers.del("Host") + # Do not send any sensitive info to a unknown host + headers.del("Authorization") + + result = await client.requestAux(redirectTo, redirectMethod, redirectBody, + headers, multipart) + lastURL = redirectTo proc responseContent(resp: Response | AsyncResponse): Future[string] {.multisync.} = ## Returns the content of a response as a string. @@ -1079,79 +1130,79 @@ proc responseContent(resp: Response | AsyncResponse): Future[string] {.multisync return await resp.bodyStream.readAll() proc head*(client: HttpClient | AsyncHttpClient, - url: string): Future[Response | AsyncResponse] {.multisync.} = + url: Uri | string): Future[Response | AsyncResponse] {.multisync.} = ## Connects to the hostname specified by the URL and performs a HEAD request. ## ## This procedure uses httpClient values such as ``client.maxRedirects``. result = await client.request(url, HttpHead) proc get*(client: HttpClient | AsyncHttpClient, - url: string): Future[Response | AsyncResponse] {.multisync.} = + url: Uri | string): Future[Response | AsyncResponse] {.multisync.} = ## Connects to the hostname specified by the URL and performs a GET request. ## ## This procedure uses httpClient values such as ``client.maxRedirects``. result = await client.request(url, HttpGet) proc getContent*(client: HttpClient | AsyncHttpClient, - url: string): Future[string] {.multisync.} = + url: Uri | string): Future[string] {.multisync.} = ## Connects to the hostname specified by the URL and returns the content of a GET request. let resp = await get(client, url) return await responseContent(resp) proc delete*(client: HttpClient | AsyncHttpClient, - url: string): Future[Response | AsyncResponse] {.multisync.} = + url: Uri | string): Future[Response | AsyncResponse] {.multisync.} = ## Connects to the hostname specified by the URL and performs a DELETE request. ## This procedure uses httpClient values such as ``client.maxRedirects``. result = await client.request(url, HttpDelete) proc deleteContent*(client: HttpClient | AsyncHttpClient, - url: string): Future[string] {.multisync.} = + url: Uri | string): Future[string] {.multisync.} = ## Connects to the hostname specified by the URL and returns the content of a DELETE request. let resp = await delete(client, url) return await responseContent(resp) -proc post*(client: HttpClient | AsyncHttpClient, url: string, body = "", +proc post*(client: HttpClient | AsyncHttpClient, url: Uri | string, body = "", multipart: MultipartData = nil): Future[Response | AsyncResponse] {.multisync.} = ## Connects to the hostname specified by the URL and performs a POST request. ## This procedure uses httpClient values such as ``client.maxRedirects``. - result = await client.request(url, $HttpPost, body, multipart=multipart) + result = await client.request(url, HttpPost, body, multipart=multipart) -proc postContent*(client: HttpClient | AsyncHttpClient, url: string, body = "", +proc postContent*(client: HttpClient | AsyncHttpClient, url: Uri | string, body = "", multipart: MultipartData = nil): Future[string] {.multisync.} = ## Connects to the hostname specified by the URL and returns the content of a POST request. let resp = await post(client, url, body, multipart) return await responseContent(resp) -proc put*(client: HttpClient | AsyncHttpClient, url: string, body = "", +proc put*(client: HttpClient | AsyncHttpClient, url: Uri | string, body = "", multipart: MultipartData = nil): Future[Response | AsyncResponse] {.multisync.} = ## Connects to the hostname specified by the URL and performs a PUT request. ## This procedure uses httpClient values such as ``client.maxRedirects``. - result = await client.request(url, $HttpPut, body, multipart=multipart) + result = await client.request(url, HttpPut, body, multipart=multipart) -proc putContent*(client: HttpClient | AsyncHttpClient, url: string, body = "", +proc putContent*(client: HttpClient | AsyncHttpClient, url: Uri | string, body = "", multipart: MultipartData = nil): Future[string] {.multisync.} = ## Connects to the hostname specified by the URL andreturns the content of a PUT request. let resp = await put(client, url, body, multipart) return await responseContent(resp) -proc patch*(client: HttpClient | AsyncHttpClient, url: string, body = "", +proc patch*(client: HttpClient | AsyncHttpClient, url: Uri | string, body = "", multipart: MultipartData = nil): Future[Response | AsyncResponse] {.multisync.} = ## Connects to the hostname specified by the URL and performs a PATCH request. ## This procedure uses httpClient values such as ``client.maxRedirects``. - result = await client.request(url, $HttpPatch, body, multipart=multipart) + result = await client.request(url, HttpPatch, body, multipart=multipart) -proc patchContent*(client: HttpClient | AsyncHttpClient, url: string, body = "", +proc patchContent*(client: HttpClient | AsyncHttpClient, url: Uri | string, body = "", multipart: MultipartData = nil): Future[string] {.multisync.} = ## Connects to the hostname specified by the URL and returns the content of a PATCH request. let resp = await patch(client, url, body, multipart) return await responseContent(resp) -proc downloadFile*(client: HttpClient, url: string, filename: string) = +proc downloadFile*(client: HttpClient, url: Uri | string, filename: string) = ## Downloads ``url`` and saves it to ``filename``. client.getBody = false defer: @@ -1167,10 +1218,10 @@ proc downloadFile*(client: HttpClient, url: string, filename: string) = if resp.code.is4xx or resp.code.is5xx: raise newException(HttpRequestError, resp.status) -proc downloadFile*(client: AsyncHttpClient, url: string, +proc downloadFile*(client: AsyncHttpClient, url: Uri | string, filename: string): Future[void] = proc downloadFileEx(client: AsyncHttpClient, - url, filename: string): Future[void] {.async.} = + url: Uri | string, filename: string): Future[void] {.async.} = ## Downloads ``url`` and saves it to ``filename``. client.getBody = false let resp = await client.get(url) diff --git a/lib/pure/httpcore.nim b/lib/pure/httpcore.nim index 9287e9864..774de1260 100644 --- a/lib/pure/httpcore.nim +++ b/lib/pure/httpcore.nim @@ -29,24 +29,26 @@ type HttpVer11, HttpVer10 - HttpMethod* = enum ## the requested HttpMethod - HttpHead, ## Asks for the response identical to the one that would - ## correspond to a GET request, but without the response - ## body. - HttpGet, ## Retrieves the specified resource. - HttpPost, ## Submits data to be processed to the identified - ## resource. The data is included in the body of the - ## request. - HttpPut, ## Uploads a representation of the specified resource. - HttpDelete, ## Deletes the specified resource. - HttpTrace, ## Echoes back the received request, so that a client - ## can see what intermediate servers are adding or - ## changing in the request. - HttpOptions, ## Returns the HTTP methods that the server supports - ## for specified address. - HttpConnect, ## Converts the request connection to a transparent - ## TCP/IP tunnel, usually used for proxies. - HttpPatch ## Applies partial modifications to a resource. + HttpMethod* = enum ## the requested HttpMethod + HttpHead = "HEAD" ## Asks for the response identical to the one that + ## would correspond to a GET request, but without + ## the response body. + HttpGet = "GET" ## Retrieves the specified resource. + HttpPost = "POST" ## Submits data to be processed to the identified + ## resource. The data is included in the body of + ## the request. + HttpPut = "PUT" ## Uploads a representation of the specified + ## resource. + HttpDelete = "DELETE" ## Deletes the specified resource. + HttpTrace = "TRACE" ## Echoes back the received request, so that a + ## client + ## can see what intermediate servers are adding or + ## changing in the request. + HttpOptions = "OPTIONS" ## Returns the HTTP methods that the server + ## supports for specified address. + HttpConnect = "CONNECT" ## Converts the request connection to a transparent + ## TCP/IP tunnel, usually used for proxies. + HttpPatch = "PATCH" ## Applies partial modifications to a resource. const @@ -150,7 +152,6 @@ func newHttpHeaders*(keyValuePairs: else: result.table[key] = @[pair.val] - func `$`*(headers: HttpHeaders): string {.inline.} = $headers.table @@ -378,21 +379,3 @@ func is4xx*(code: HttpCode): bool {.inline.} = func is5xx*(code: HttpCode): bool {.inline.} = ## Determines whether ``code`` is a 5xx HTTP status code. code.int in {500 .. 599} - -func `$`*(httpMethod: HttpMethod): string {.inline.} = - runnableExamples: - doAssert $HttpHead == "HEAD" - doAssert $HttpPatch == "PATCH" - doAssert $HttpGet == "GET" - doAssert $HttpPost == "POST" - - result = case httpMethod - of HttpHead: "HEAD" - of HttpGet: "GET" - of HttpPost: "POST" - of HttpPut: "PUT" - of HttpDelete: "DELETE" - of HttpTrace: "TRACE" - of HttpOptions: "OPTIONS" - of HttpConnect: "CONNECT" - of HttpPatch: "PATCH" diff --git a/tests/stdlib/thttpclient.nim b/tests/stdlib/thttpclient.nim index 6a634d90f..4881370ee 100644 --- a/tests/stdlib/thttpclient.nim +++ b/tests/stdlib/thttpclient.nim @@ -39,7 +39,7 @@ proc makeIPv6HttpServer(hostname: string, port: Port, proc asyncTest() {.async.} = var client = newAsyncHttpClient() - var resp = await client.request("http://example.com/") + var resp = await client.request("http://example.com/", HttpGet) doAssert(resp.code.is2xx) var body = await resp.body body = await resp.body # Test caching @@ -48,7 +48,7 @@ proc asyncTest() {.async.} = resp = await client.request("http://example.com/404") doAssert(resp.code.is4xx) doAssert(resp.code == Http404) - doAssert(resp.status == Http404) + doAssert(resp.status == $Http404) resp = await client.request("https://google.com/") doAssert(resp.code.is2xx or resp.code.is3xx) @@ -102,14 +102,14 @@ proc asyncTest() {.async.} = proc syncTest() = var client = newHttpClient() - var resp = client.request("http://example.com/") + var resp = client.request("http://example.com/", HttpGet) doAssert(resp.code.is2xx) doAssert("Example Domain" in resp.body) resp = client.request("http://example.com/404") doAssert(resp.code.is4xx) doAssert(resp.code == Http404) - doAssert(resp.status == Http404) + doAssert(resp.status == $Http404) resp = client.request("https://google.com/") doAssert(resp.code.is2xx or resp.code.is3xx) From f82100ac93ddb8977d2a404d2cd85975114f50ab Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sun, 10 Jan 2021 10:19:40 -0600 Subject: [PATCH 1318/3768] fix broken CI (#16663) --- lib/pure/httpclient.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index ea847ef8d..6acf8d9cd 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -979,7 +979,7 @@ proc requestAux(client: HttpClient | AsyncHttpClient, url: Uri, else: if body.len != 0: client.headers["Content-Length"] = $body.len - elif httpMethod notin ["GET", "HEAD"] and not client.headers.hasKey("Content-Length"): + elif httpMethod notin [HttpGet, HttpHead] and not client.headers.hasKey("Content-Length"): client.headers["Content-Length"] = "0" when client is AsyncHttpClient: From 2c6f5ae6815367dcb44e441c8f3bda2e2513b061 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sun, 10 Jan 2021 15:51:29 -0600 Subject: [PATCH 1319/3768] fix #16650 (#16660) --- compiler/semfold.nim | 7 ++++++- tests/system/tostring.nim | 19 ++++++++++++------- 2 files changed, 18 insertions(+), 8 deletions(-) diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 3d0a9d0ae..4cc2dcba0 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -15,6 +15,8 @@ import platform, math, msgs, idents, renderer, types, commands, magicsys, modulegraphs, strtabs, lineinfos +from system/memory import nimCStrLen + proc errorType*(g: ModuleGraph): PType = ## creates a type representing an error state result = newType(tyError, nextTypeId(g.idgen), g.owners[^1]) @@ -133,7 +135,10 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; g: ModuleGraph): PNode = if a.kind == nkNilLit: result = newIntNodeT(Zero, n, g) elif a.kind in {nkStrLit..nkTripleStrLit}: - result = newIntNodeT(toInt128(a.strVal.len), n, g) + if a.typ.kind == tyString: + result = newIntNodeT(toInt128(a.strVal.len), n, g) + elif a.typ.kind == tyCString: + result = newIntNodeT(toInt128(nimCStrLen(a.strVal)), n, g) else: result = newIntNodeT(toInt128(a.len), n, g) of mUnaryPlusI, mUnaryPlusF64: result = a # throw `+` away diff --git a/tests/system/tostring.nim b/tests/system/tostring.nim index 4ff363075..fa82acc3b 100644 --- a/tests/system/tostring.nim +++ b/tests/system/tostring.nim @@ -1,7 +1,3 @@ -discard """ - output: "DONE: tostring.nim" -""" - doAssert "@[23, 45]" == $(@[23, 45]) doAssert "[32, 45]" == $([32, 45]) doAssert """@["", "foo", "bar"]""" == $(@["", "foo", "bar"]) @@ -108,7 +104,7 @@ bar(nilstring) static: stringCompare() -# bug 8847 +# issue #8847 var a2: cstring = "fo\"o2" block: @@ -116,5 +112,14 @@ block: s.addQuoted a2 doAssert s == "\"fo\\\"o2\"" - -echo "DONE: tostring.nim" +# issue #16650 +template fn() = + doAssert len(cstring"ab\0c") == 5 + doAssert len(cstring("ab\0c")) == 2 + when nimvm: + discard + else: + let c = cstring("ab\0c") + doAssert len(c) == 2 +fn() +static: fn() From 510e383d9233641a82ef25d3e3fe3e2eba3a4388 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 11 Jan 2021 00:26:52 -0600 Subject: [PATCH 1320/3768] add error messages (#16679) --- testament/testament.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testament/testament.nim b/testament/testament.nim index 5686da81c..314a83936 100644 --- a/testament/testament.nim +++ b/testament/testament.nim @@ -795,7 +795,7 @@ proc main() = var subPath = p.key.string let nimRoot = currentSourcePath / "../.." # makes sure points to this regardless of cwd or which nim is used to compile this. - doAssert dirExists(nimRoot/testsDir) # sanity check + doAssert(dirExists(nimRoot/testsDir), nimRoot/testsDir & " doesn't exist!") # sanity check if subPath.isAbsolute: subPath = subPath.relativePath(nimRoot) # at least one directory is required in the path, to use as a category name let pathParts = subPath.relativePath(testsDir).split({DirSep, AltSep}) From 0286a0879bc44e5267a5fd36e6f4aac8f78713ea Mon Sep 17 00:00:00 2001 From: cooldome Date: Mon, 11 Jan 2021 09:09:38 +0000 Subject: [PATCH 1321/3768] fix #16651 (#16658) * fix #16651 --- compiler/semtypes.nim | 7 +++++++ tests/converter/tgenericconverter.nim | 24 ++++++++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 64113a4c6..45f20da9b 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1059,6 +1059,13 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, # disable the bindOnce behavior for the type class result = recurse(paramType.base, true) + of tyTuple: + for i in 0.. Date: Mon, 11 Jan 2021 01:16:20 -0800 Subject: [PATCH 1322/3768] fix #16555, fixes #16405: len, high honors '\0' for cstring in vm (#16610) --- compiler/vm.nim | 4 ++++ compiler/vmdef.nim | 1 + compiler/vmgen.nim | 12 +++++++----- lib/system.nim | 20 +++++++++++++------- tests/stdlib/thashes.nim | 18 ++++++++++++++++-- tests/stdlib/tstring.nim | 34 +++++++++++++++++++++++++++++++++- 6 files changed, 74 insertions(+), 15 deletions(-) diff --git a/compiler/vm.nim b/compiler/vm.nim index 0f75faee3..b8df456e0 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -872,6 +872,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = decodeBImm(rkInt) assert regs[rb].kind == rkNode regs[ra].intVal = regs[rb].node.strVal.len - imm + of opcLenCstring: + decodeBImm(rkInt) + assert regs[rb].kind == rkNode + regs[ra].intVal = regs[rb].node.strVal.cstring.len - imm of opcIncl: decodeB(rkNode) let b = regs[rb].regToNode diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index 6d6c55250..eab18f417 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -86,6 +86,7 @@ type opcSubImmInt, opcLenSeq, opcLenStr, + opcLenCstring, opcIncl, opcInclRange, opcExcl, opcCard, opcMulInt, opcDivInt, opcModInt, opcAddFloat, opcSubFloat, opcMulFloat, opcDivFloat, diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 46931eb54..6790276a9 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1034,7 +1034,10 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of mLengthOpenArray, mLengthArray, mLengthSeq: genUnaryABI(c, n, dest, opcLenSeq) of mLengthStr: - genUnaryABI(c, n, dest, opcLenStr) + case n[1].typ.kind + of tyString: genUnaryABI(c, n, dest, opcLenStr) + of tyCString: genUnaryABI(c, n, dest, opcLenCstring) + else: doAssert false, $n[1].typ.kind of mIncl, mExcl: unused(c, n, dest) var d = c.genx(n[1]) @@ -1178,10 +1181,9 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = if dest < 0: dest = c.getTemp(n.typ) let tmp = c.genx(n[1]) case n[1].typ.skipTypes(abstractVar-{tyTypeDesc}).kind: - of tyString, tyCString: - c.gABI(n, opcLenStr, dest, tmp, 1) - else: - c.gABI(n, opcLenSeq, dest, tmp, 1) + of tyString: c.gABI(n, opcLenStr, dest, tmp, 1) + of tyCString: c.gABI(n, opcLenCstring, dest, tmp, 1) + else: c.gABI(n, opcLenSeq, dest, tmp, 1) c.freeTemp(tmp) of mEcho: unused(c, n, dest) diff --git a/lib/system.nim b/lib/system.nim index e220ba7a3..25184eb15 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -701,18 +701,24 @@ proc len*(x: string): int {.magic: "LengthStr", noSideEffect.} ## var str = "Hello world!" ## echo len(str) # => 12 -proc len*(x: cstring): int {.magic: "LengthStr", noSideEffect.} - ## Returns the length of a compatible string. This is sometimes - ## an O(n) operation. +proc len*(x: cstring): int {.magic: "LengthStr", noSideEffect.} = + ## Returns the length of a compatible string. This is an O(n) operation except + ## in js at runtime. ## ## **Note:** On the JS backend this currently counts UTF-16 code points ## instead of bytes at runtime (not at compile time). For now, if you ## need the byte length of the UTF-8 encoding, convert to string with ## `$` first then call `len`. - ## - ## .. code-block:: Nim - ## var str: cstring = "Hello world!" - ## len(str) # => 12 + runnableExamples: + doAssert len(cstring"abc") == 3 + doAssert len(cstring r"ab\0c") == 5 # \0 is escaped + doAssert len(cstring"ab\0c") == 5 # ditto + var a: cstring = "ab\0c" + when defined(js): doAssert a.len == 4 # len ignores \0 for js + else: doAssert a.len == 2 # \0 is a null terminator + static: + var a2: cstring = "ab\0c" + doAssert a2.len == 2 # \0 is a null terminator, even in js vm proc len*(x: (type array)|array): int {.magic: "LengthArray", noSideEffect.} ## Returns the length of an array or an array type. diff --git a/tests/stdlib/thashes.nim b/tests/stdlib/thashes.nim index 17640387a..ce7bb7d8c 100644 --- a/tests/stdlib/thashes.nim +++ b/tests/stdlib/thashes.nim @@ -86,8 +86,6 @@ block largeSize: # longer than 4 characters doAssert hash(xx, 0, 3) == hash(ssl, 0, 3) proc main() = - - doAssert hash(0.0) == hash(0) doAssert hash(cstring"abracadabra") == 97309975 doAssert hash(cstring"abracadabra") == hash("abracadabra") @@ -115,6 +113,22 @@ proc main() = doAssert hash(-9999.283456) != 0 doAssert hash(84375674.0) != 0 + block: # bug #16555 + proc fn(): auto = + # avoids hardcoding values + var a = "abc\0def" + var b = a.cstring + result = (hash(a), hash(b)) + doAssert result[0] != result[1] + when not defined(js): + doAssert fn() == static(fn()) + else: + # xxx this is a tricky case; consistency of hashes for cstring's containing + # '\0\' matters for c backend but less for js backend since such strings + # are much less common in js backend; we make vm for js backend consistent + # with c backend instead of js backend because FFI code (or other) could + # run at CT, expecting c semantics. + discard static: main() main() diff --git a/tests/stdlib/tstring.nim b/tests/stdlib/tstring.nim index 4d5a15940..fcbacc533 100644 --- a/tests/stdlib/tstring.nim +++ b/tests/stdlib/tstring.nim @@ -96,11 +96,43 @@ func reverse*(a: string): string = proc main() = + # xxx put all tests here to test in VM and RT test_string_slice() test_string_cmp() tester(1) - doAssert reverse("hello") == "olleh" + + block: # reverse + doAssert reverse("hello") == "olleh" + + block: # len, high + var a = "ab\0cd" + var b = a.cstring + doAssert a.len == 5 + block: # bug #16405 + when defined(js): + when nimvm: doAssert b.len == 2 + else: doAssert b.len == 5 + else: doAssert b.len == 2 + + doAssert a.high == a.len - 1 + doAssert b.high == b.len - 1 + + doAssert "".len == 0 + doAssert "".high == -1 + doAssert "".cstring.len == 0 + doAssert "".cstring.high == -1 + + var c: cstring = nil + template impl() = + doAssert c.len == 0 + doAssert c.high == -1 + when defined js: + when nimvm: impl() + else: + # xxx pending bug #16674 + discard + else: impl() static: main() main() From bbc96f974d643b5ab4e2b9d2855e94ef30ed3ee4 Mon Sep 17 00:00:00 2001 From: Saem Ghani Date: Mon, 11 Jan 2021 01:18:01 -0800 Subject: [PATCH 1323/3768] fixed nim-lang/nimsuggest#82 pure enum field sug (#16676) - previous code wasn't account for tyEnum being wrapped in tyTypeDesc - now pure enum fields are suggested --- compiler/suggest.nim | 21 +++++++++++---------- nimsuggest/tests/tsug_enum.nim | 18 ++++++++++++++++++ 2 files changed, 29 insertions(+), 10 deletions(-) create mode 100644 nimsuggest/tests/tsug_enum.nim diff --git a/compiler/suggest.nim b/compiler/suggest.nim index 560d20b3f..283690080 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -386,17 +386,17 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions) else: # fallback: suggestEverything(c, n, field, outputs) - elif typ.kind == tyEnum and n.kind == nkSym and n.sym.kind == skType: - # look up if the identifier belongs to the enum: - var t = typ - while t != nil: - suggestSymList(c, t.n, field, n.info, outputs) - t = t[0] - suggestOperations(c, n, field, typ, outputs) else: - let orig = typ # skipTypes(typ, {tyGenericInst, tyAlias, tySink}) - typ = skipTypes(typ, {tyGenericInst, tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink, tyOwned}) - if typ.kind == tyObject: + let orig = typ + typ = skipTypes(orig, {tyTypeDesc, tyGenericInst, tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink, tyOwned}) + + if typ.kind == tyEnum and n.kind == nkSym and n.sym.kind == skType: + # look up if the identifier belongs to the enum: + var t = typ + while t != nil: + suggestSymList(c, t.n, field, n.info, outputs) + t = t[0] + elif typ.kind == tyObject: var t = typ while true: suggestObject(c, t.n, field, n.info, outputs) @@ -404,6 +404,7 @@ proc suggestFieldAccess(c: PContext, n, field: PNode, outputs: var Suggestions) t = skipTypes(t[0], skipPtrs) elif typ.kind == tyTuple and typ.n != nil: suggestSymList(c, typ.n, field, n.info, outputs) + suggestOperations(c, n, field, orig, outputs) if typ != orig: suggestOperations(c, n, field, typ, outputs) diff --git a/nimsuggest/tests/tsug_enum.nim b/nimsuggest/tests/tsug_enum.nim new file mode 100644 index 000000000..97a225f16 --- /dev/null +++ b/nimsuggest/tests/tsug_enum.nim @@ -0,0 +1,18 @@ +## suggestions for enums + +type + LogLevel {.pure.} = enum + debug, log, warn, error + + FooBar = enum + fbFoo, fbBar + +echo fbFoo, fbBar + +echo LogLevel.deb#[!]# + +discard """ +$nimsuggest --tester $file +>sug $1 +sug;;skEnumField;;debug;;LogLevel;;*nimsuggest/tests/tsug_enum.nim;;5;;4;;"";;100;;Prefix +""" \ No newline at end of file From 5897ed9d3d5ca5f84423e87a70addc8c6764923e Mon Sep 17 00:00:00 2001 From: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Date: Mon, 11 Jan 2021 10:53:15 +0100 Subject: [PATCH 1324/3768] Improve documentation of strmisc (#16665) Simplify examples --- lib/pure/strmisc.nim | 60 ++++++++++++++++++++++---------------------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/lib/pure/strmisc.nim b/lib/pure/strmisc.nim index 5060deb78..c8cd839be 100644 --- a/lib/pure/strmisc.nim +++ b/lib/pure/strmisc.nim @@ -8,12 +8,12 @@ # ## This module contains various string utility routines that are uncommonly -## used in comparison to `strutils `_. +## used in comparison to the ones in `strutils `_. -import strutils +import std/strutils -proc expandTabs*(s: string, tabSize: int = 8): string {.noSideEffect.} = - ## Expand tab characters in `s` replacing them by spaces. +func expandTabs*(s: string, tabSize: int = 8): string = + ## Expands tab characters in `s`, replacing them by spaces. ## ## The amount of inserted spaces for each tab character is the difference ## between the current column number and the next tab position. Tab positions @@ -24,9 +24,7 @@ proc expandTabs*(s: string, tabSize: int = 8): string {.noSideEffect.} = runnableExamples: doAssert expandTabs("\t", 4) == " " doAssert expandTabs("\tfoo\t", 4) == " foo " - doAssert expandTabs("\tfoo\tbar", 4) == " foo bar" - doAssert expandTabs("\tfoo\tbar\t", 4) == " foo bar " - doAssert expandTabs("ab\tcd\n\txy\t", 3) == "ab cd\n xy " + doAssert expandTabs("a\tb\n\txy\t", 3) == "a b\n xy " result = newStringOfCap(s.len + s.len shr 2) var pos = 0 @@ -50,37 +48,39 @@ proc expandTabs*(s: string, tabSize: int = 8): string {.noSideEffect.} = if c == '\l': pos = 0 -proc partition*(s: string, sep: string, - right: bool = false): (string, string, string) - {.noSideEffect.} = - ## Split the string at the first or last occurrence of `sep` into a 3-tuple +func partition*(s: string, sep: string, + right: bool = false): (string, string, string) = + ## Splits the string at the first (if `right` is false) + ## or last (if `right` is true) occurrence of `sep` into a 3-tuple. ## - ## Returns a 3 string tuple of (beforeSep, `sep`, afterSep) or - ## (`s`, "", "") if `sep` is not found and `right` is false or - ## ("", "", `s`) if `sep` is not found and `right` is true + ## Returns a 3-tuple of strings, `(beforeSep, sep, afterSep)` or + ## `(s, "", "")` if `sep` is not found and `right` is false or + ## `("", "", s)` if `sep` is not found and `right` is true. + ## + ## **See also:** + ## * `rpartition proc <#rpartition,string,string>`_ runnableExamples: - doAssert partition("foo:bar", ":") == ("foo", ":", "bar") - doAssert partition("foobarbar", "bar") == ("foo", "bar", "bar") - doAssert partition("foobarbar", "bank") == ("foobarbar", "", "") - doAssert partition("foobarbar", "foo") == ("", "foo", "barbar") - doAssert partition("foofoobar", "bar") == ("foofoo", "bar", "") + doAssert partition("foo:bar:baz", ":") == ("foo", ":", "bar:baz") + doAssert partition("foo:bar:baz", ":", right = true) == ("foo:bar", ":", "baz") + doAssert partition("foobar", ":") == ("foobar", "", "") + doAssert partition("foobar", ":", right = true) == ("", "", "foobar") let position = if right: s.rfind(sep) else: s.find(sep) if position != -1: return (s[0 ..< position], sep, s[position + sep.len ..< s.len]) return if right: ("", "", s) else: (s, "", "") -proc rpartition*(s: string, sep: string): (string, string, string) - {.noSideEffect.} = - ## Split the string at the last occurrence of `sep` into a 3-tuple +func rpartition*(s: string, sep: string): (string, string, string) = + ## Splits the string at the last occurrence of `sep` into a 3-tuple. ## - ## Returns a 3 string tuple of (beforeSep, `sep`, afterSep) or - ## ("", "", `s`) if `sep` is not found + ## Returns a 3-tuple of strings, `(beforeSep, sep, afterSep)` or + ## `("", "", s)` if `sep` is not found. This is the same as + ## `partition(s, sep, right = true)`. + ## + ## **See also:** + ## * `partition proc <#partition,string,string,bool>`_ runnableExamples: - doAssert rpartition("foo:bar", ":") == ("foo", ":", "bar") - doAssert rpartition("foobarbar", "bar") == ("foobar", "bar", "") - doAssert rpartition("foobarbar", "bank") == ("", "", "foobarbar") - doAssert rpartition("foobarbar", "foo") == ("", "foo", "barbar") - doAssert rpartition("foofoobar", "bar") == ("foofoo", "bar", "") + doAssert rpartition("foo:bar:baz", ":") == ("foo:bar", ":", "baz") + doAssert rpartition("foobar", ":") == ("", "", "foobar") - return partition(s, sep, right = true) + partition(s, sep, right = true) From be6e8916faa51d227d51e1291d1f24751385b010 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 11 Jan 2021 04:39:38 -0600 Subject: [PATCH 1325/3768] fix negative nan (#16628) --- compiler/jsgen.nim | 5 ++++- compiler/rodutils.nim | 13 +++++++++++-- tests/stdlib/tmath.nim | 16 ++++++++++++++-- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index f588f9555..10a33423e 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -2485,7 +2485,10 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = let f = n.floatVal case classify(f) of fcNan: - r.res = rope"NaN" + if signbit(f): + r.res = rope"-NaN" + else: + r.res = rope"NaN" of fcNegZero: r.res = rope"-0.0" of fcZero: diff --git a/compiler/rodutils.nim b/compiler/rodutils.nim index 7070e6c3f..353992fca 100644 --- a/compiler/rodutils.nim +++ b/compiler/rodutils.nim @@ -8,7 +8,7 @@ # ## Serialization utilities for the compiler. -import strutils, math +import std/[strutils, math] # bcc on windows doesn't have C99 functions when defined(windows) and defined(bcc): @@ -33,10 +33,19 @@ when defined(windows) and defined(bcc): proc c_snprintf(s: cstring; n:uint; frmt: cstring): cint {.importc: "snprintf", header: "", nodecl, varargs.} + +when not declared(signbit): + proc c_signbit(x: SomeFloat): cint {.importc: "signbit", header: "".} + proc signbit*(x: SomeFloat): bool {.inline.} = + result = c_signbit(x) != 0 + proc toStrMaxPrecision*(f: BiggestFloat, literalPostfix = ""): string = case classify(f) of fcNan: - result = "NAN" + if signbit(f): + result = "-NAN" + else: + result = "NAN" of fcNegZero: result = "-0.0" & literalPostfix of fcZero: diff --git a/tests/stdlib/tmath.nim b/tests/stdlib/tmath.nim index 62fdcd19f..edab62a66 100644 --- a/tests/stdlib/tmath.nim +++ b/tests/stdlib/tmath.nim @@ -319,6 +319,18 @@ template main = doAssert not Inf.isNaN doAssert isNaN(Inf - Inf) + block: # signbit + let x1 = NaN + let x2 = -NaN + let x3 = -x1 + + doAssert isNaN(x1) + doAssert isNaN(x2) + doAssert isNaN(x3) + doAssert not signbit(x1) + doAssert signbit(x2) + doAssert signbit(x3) + block: # copySign doAssert copySign(10.0, -1.0) == -10.0 doAssert copySign(-10.0, -1.0) == -10.0 @@ -385,8 +397,8 @@ template main = discard else: when not defined(js): - doAssert copySign(-1.0, -NaN) == 1.0 - doAssert copySign(10.0, -NaN) == 10.0 + doAssert copySign(-1.0, -NaN) == -1.0 + doAssert copySign(10.0, -NaN) == -10.0 doAssert copySign(1.0, copySign(NaN, -1.0)) == -1.0 # fails in VM block: From aa185c0e9b4904b305496bd1f83cbbadf2fbfa36 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 11 Jan 2021 08:07:48 -0600 Subject: [PATCH 1326/3768] fix #13517 (#16681) --- compiler/vm.nim | 6 +++--- tests/distinct/tdistinct_issues.nim | 14 ++++++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/compiler/vm.nim b/compiler/vm.nim index b8df456e0..00f0aca7c 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -429,13 +429,13 @@ proc opConv(c: PCtx; dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): return true of tyUInt..tyUInt64: dest.ensureKind(rkInt) - case skipTypes(srctyp, abstractRange).kind + let styp = srctyp.skipTypes(abstractRange) # skip distinct types(dest type could do this too if needed) + case styp.kind of tyFloat..tyFloat64: dest.intVal = int(src.floatVal) else: - let srcDist = (sizeof(src.intVal) - srctyp.size) * 8 + let srcDist = (sizeof(src.intVal) - styp.size) * 8 let destDist = (sizeof(dest.intVal) - desttyp.size) * 8 - var value = cast[BiggestUInt](src.intVal) value = (value shl srcDist) shr srcDist value = (value shl destDist) shr destDist diff --git a/tests/distinct/tdistinct_issues.nim b/tests/distinct/tdistinct_issues.nim index ce71344d0..747cf0b8d 100644 --- a/tests/distinct/tdistinct_issues.nim +++ b/tests/distinct/tdistinct_issues.nim @@ -65,3 +65,17 @@ block t9322: proc mystr(s: string) = echo s mystr($Fix("apr")) + + +block: # bug #13517 + type MyUint64 = distinct uint64 + + proc `==`(a: MyUint64, b: uint64): bool = uint64(a) == b + + block: + doAssert MyUint64.high is MyUint64 + doAssert MyUint64.high == 18446744073709551615'u64 + + static: + doAssert MyUint64.high is MyUint64 + doAssert MyUint64.high == 18446744073709551615'u64 From 0c128259bb01cde8ed27430929b0a01c5ee7b88c Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 11 Jan 2021 08:13:13 -0600 Subject: [PATCH 1327/3768] close #7097 add testcase (#16682) --- tests/converter/t7097.nim | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 tests/converter/t7097.nim diff --git a/tests/converter/t7097.nim b/tests/converter/t7097.nim new file mode 100644 index 000000000..fdb573588 --- /dev/null +++ b/tests/converter/t7097.nim @@ -0,0 +1,38 @@ +type + Byte* = uint8 + Bytes* = seq[Byte] + + BytesRange* = object + bytes: Bytes + ibegin, iend: int + +proc initBytesRange*(s: var Bytes, ibegin = 0, iend = -1): BytesRange = + let e = if iend < 0: s.len + iend + 1 + else: iend + assert ibegin > 0 and e <= s.len + + shallow(s) + result.bytes = s + result.ibegin = ibegin + result.iend = e + +template `[]=`*(r: var BytesRange, i: int, v: Byte) = + r.bytes[r.ibegin + i] = v + +converter fromSeq*(s: Bytes): BytesRange = + var seqCopy = s + return initBytesRange(seqCopy) + +type + Reader* = object + data: BytesRange + position: int + +proc readerFromHex*(input: string): Reader = + let totalBytes = input.len div 2 + var backingStore = newSeq[Byte](totalBytes) + result.data = initBytesRange(backingStore) + + for i in 0 ..< totalBytes: + var nextByte = 0 + result.data[i] = Byte(nextByte) # <-------- instantiated from here From 5af13c5aceaedf211ff59113384bcb6a85c35c2b Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 11 Jan 2021 11:02:32 -0600 Subject: [PATCH 1328/3768] close #9655 add testcase (#16683) --- tests/ccgbugs/t9655.nim | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 tests/ccgbugs/t9655.nim diff --git a/tests/ccgbugs/t9655.nim b/tests/ccgbugs/t9655.nim new file mode 100644 index 000000000..29fb903a4 --- /dev/null +++ b/tests/ccgbugs/t9655.nim @@ -0,0 +1,30 @@ +discard """ + action: "compile" +""" + +import std/[asynchttpserver, asyncdispatch] +import std/[strformat] + +proc main() = + let local = "123" + + proc serveIndex(req: Request) {.async, gcsafe.} = + await req.respond(Http200, &"{local}") + + proc serve404(req: Request) {.async, gcsafe.} = + echo req.url.path + await req.respond(Http404, "not found") + + proc serve(req: Request) {.async, gcsafe.} = + let handler = case req.url.path: + of "/": + serveIndex + else: + serve404 + await handler(req) + + let server = newAsyncHttpServer() + waitFor server.serve(Port(8080), serve, address = "127.0.0.1") + +when isMainModule: + main() From da28df6113cdc552ad2ef3a029638efd9bfdadf2 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 11 Jan 2021 11:02:53 -0600 Subject: [PATCH 1329/3768] remove deprecated specs (#16684) --- testament/specs.nim | 4 ---- testament/tests/shouldfail/tmsg.nim | 6 ------ 2 files changed, 10 deletions(-) delete mode 100644 testament/tests/shouldfail/tmsg.nim diff --git a/testament/specs.nim b/testament/specs.nim index 58fe7bf4f..48fe51658 100644 --- a/testament/specs.nim +++ b/testament/specs.nim @@ -303,10 +303,6 @@ proc parseSpec*(filename: string): TSpec = of "exitcode": discard parseInt(e.value, result.exitCode) result.action = actionRun - of "msg": - result.msg = e.value - if result.action != actionRun: - result.action = actionCompile of "errormsg": result.msg = e.value result.action = actionReject diff --git a/testament/tests/shouldfail/tmsg.nim b/testament/tests/shouldfail/tmsg.nim deleted file mode 100644 index 4ad17fa95..000000000 --- a/testament/tests/shouldfail/tmsg.nim +++ /dev/null @@ -1,6 +0,0 @@ -discard """ -msg: "Hello World" -""" - -static: - echo "something else" From 335f849c36c4d7e618de277f6da64d8c1fdcc1c9 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 11 Jan 2021 12:00:57 -0600 Subject: [PATCH 1330/3768] close #9901 add testcase (#16662) * close #9901 add testcase * follow advice --- tests/misc/tproveinit.nim | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 tests/misc/tproveinit.nim diff --git a/tests/misc/tproveinit.nim b/tests/misc/tproveinit.nim new file mode 100644 index 000000000..c9f688309 --- /dev/null +++ b/tests/misc/tproveinit.nim @@ -0,0 +1,18 @@ +discard """ + joinable: false +""" + +{.warningAsError[ProveInit]:on.} +template main() = + proc fn(): var int = + discard + discard fn() +doAssert not compiles(main()) + +# bug #9901 +import std/[sequtils, times] +proc parseMyDates(line: string): DateTime = + result = parse(line, "yyyy-MM-dd") +var dateStrings = @["2018-12-01", "2018-12-02", "2018-12-03"] +var parsed = dateStrings.map(parseMyDates) +discard parsed From fd5c8ef20845a511ad4e0af8dd8ad4331bf46ffc Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Mon, 11 Jan 2021 21:51:04 +0300 Subject: [PATCH 1331/3768] RST: implement internal targets (#16614) --- compiler/docgen.nim | 2 +- config/nimdoc.cfg | 3 +- lib/packages/docutils/rst.nim | 168 +++++++++++++++++++++++-------- lib/packages/docutils/rstast.nim | 14 ++- lib/packages/docutils/rstgen.nim | 135 +++++++++++++++---------- tests/stdlib/trstgen.nim | 158 +++++++++++++++++++++++++++++ 6 files changed, 379 insertions(+), 101 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index d21db7634..f07adc428 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -560,7 +560,7 @@ proc getAllRunnableExamplesImpl(d: PDoc; n: PNode, dest: var Rope, state: Runnab "\n\\textbf{$1}\n", [msg.rope]) inc d.listingCounter let id = $d.listingCounter - dest.add(d.config.getOrDefault"doc.listing_start" % [id, "langNim"]) + dest.add(d.config.getOrDefault"doc.listing_start" % [id, "langNim", ""]) var dest2 = "" renderNimCode(dest2, code, isLatex = d.conf.cmd == cmdRst2tex) dest.add dest2 diff --git a/config/nimdoc.cfg b/config/nimdoc.cfg index 585b3cc49..ff4dac781 100644 --- a/config/nimdoc.cfg +++ b/config/nimdoc.cfg @@ -206,7 +206,8 @@ $moduledesc $content """ -doc.listing_start = "
                                      "
                                      +# $1 - number of listing in document, $2 - language (e.g. langNim), $3 - anchor
                                      +doc.listing_start = ""
                                       doc.listing_end = "
                                      " # * $analytics: Google analytics location, includes -Create a ``calculator.nim`` file with the following content (or reuse the one +Create a `calculator.nim` file with the following content (or reuse the one from the previous section): .. code-block:: nim @@ -209,9 +211,9 @@ from the previous section): when isMainModule: echo addTwoIntegers(3, 7) -Compile the Nim code to JavaScript with ``nim js -o:calculator.js -calculator.nim`` and open ``host.html`` in a browser. If the browser supports -javascript, you should see the value ``10`` in the browser's console. Use the +Compile the Nim code to JavaScript with `nim js -o:calculator.js +calculator.nim` and open `host.html` in a browser. If the browser supports +javascript, you should see the value `10` in the browser's console. Use the `dom module `_ for specific DOM querying and modification procs or take a look at `karax `_ for how to develop browser-based applications. @@ -222,29 +224,29 @@ Backend code calling Nim Backend code can interface with Nim code exposed through the `exportc pragma `_. The -``exportc`` pragma is the *generic* way of making Nim symbols available to +`exportc` pragma is the *generic* way of making Nim symbols available to the backends. By default, the Nim compiler will mangle all the Nim symbols to -avoid any name collision, so the most significant thing the ``exportc`` pragma +avoid any name collision, so the most significant thing the `exportc` pragma does is maintain the Nim symbol name, or if specified, use an alternative symbol for the backend in case the symbol rules don't match. The JavaScript target doesn't have any further interfacing considerations since it also has garbage collection, but the C targets require you to -initialize Nim's internals, which is done calling a ``NimMain`` function. +initialize Nim's internals, which is done calling a `NimMain` function. Also, C code requires you to specify a forward declaration for functions or the compiler will assume certain types for the return value and parameters which will likely make your program crash at runtime. -The Nim compiler can generate a C interface header through the ``--header`` +The Nim compiler can generate a C interface header through the `--header` command-line switch. The generated header will contain all the exported -symbols and the ``NimMain`` proc which you need to call before any other +symbols and the `NimMain` proc which you need to call before any other Nim code. Nim invocation example from C ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Create a ``fib.nim`` file with the following content: +Create a `fib.nim` file with the following content: .. code-block:: nim @@ -254,7 +256,7 @@ Create a ``fib.nim`` file with the following content: else: result = fib(a - 1) + fib(a - 2) -Create a ``maths.c`` file with the following content: +Create a `maths.c` file with the following content: .. code-block:: c @@ -277,30 +279,30 @@ program:: $ gcc -o m -I$HOME/.cache/nim/fib_d -Ipath/to/nim/lib $HOME/.cache/nim/fib_d/*.c maths.c The first command runs the Nim compiler with three special options to avoid -generating a ``main()`` function in the generated files, avoid linking the +generating a `main()` function in the generated files, avoid linking the object files into a final binary, and explicitly generate a header file for C -integration. All the generated files are placed into the ``nimcache`` -directory. That's why the next command compiles the ``maths.c`` source plus -all the ``.c`` files from ``nimcache``. In addition to this path, you also -have to tell the C compiler where to find Nim's ``nimbase.h`` header file. +integration. All the generated files are placed into the `nimcache` +directory. That's why the next command compiles the `maths.c` source plus +all the `.c` files from `nimcache`. In addition to this path, you also +have to tell the C compiler where to find Nim's `nimbase.h` header file. -Instead of depending on the generation of the individual ``.c`` files you can +Instead of depending on the generation of the individual `.c` files you can also ask the Nim compiler to generate a statically linked library:: $ nim c --app:staticLib --noMain --header fib.nim $ gcc -o m -Inimcache -Ipath/to/nim/lib libfib.nim.a maths.c The Nim compiler will handle linking the source files generated in the -``nimcache`` directory into the ``libfib.nim.a`` static library, which you can +`nimcache` directory into the `libfib.nim.a` static library, which you can then link into your C program. Note that these commands are generic and will vary for each system. For instance, on Linux systems you will likely need to -use ``-ldl`` too to link in required dlopen functionality. +use `-ldl` too to link in required dlopen functionality. Nim invocation example from JavaScript ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Create a ``mhost.html`` file with the following content: +Create a `mhost.html` file with the following content: .. code-block:: @@ -311,7 +313,7 @@ Create a ``mhost.html`` file with the following content: -Create a ``fib.nim`` file with the following content (or reuse the one +Create a `fib.nim` file with the following content (or reuse the one from the previous section): .. code-block:: nim @@ -322,10 +324,10 @@ from the previous section): else: result = fib(a - 1) + fib(a - 2) -Compile the Nim code to JavaScript with ``nim js -o:fib.js fib.nim`` and -open ``mhost.html`` in a browser. If the browser supports javascript, you -should see an alert box displaying the text ``Fib for 9 is 34``. As mentioned -earlier, JavaScript doesn't require an initialization call to ``NimMain`` or +Compile the Nim code to JavaScript with `nim js -o:fib.js fib.nim` and +open `mhost.html` in a browser. If the browser supports javascript, you +should see an alert box displaying the text `Fib for 9 is 34`. As mentioned +earlier, JavaScript doesn't require an initialization call to `NimMain` or a similar function and you can call the exported Nim proc directly. @@ -335,14 +337,14 @@ Nimcache naming logic The `nimcache`:idx: directory is generated during compilation and will hold either temporary or final files depending on your backend target. The default name for the directory depends on the used backend and on your OS but you can -use the ``--nimcache`` `compiler switch +use the `--nimcache` `compiler switch `_ to change it. Memory management ================= -In the previous sections, the ``NimMain()`` function reared its head. Since +In the previous sections, the `NimMain()` function reared its head. Since JavaScript already provides automatic memory management, you can freely pass objects between the two languages without problems. In C and derivate languages you need to be careful about what you do and how you share memory. The @@ -357,15 +359,15 @@ Strings and C strings The manual mentions that `Nim strings are implicitly convertible to cstrings `_ which makes interaction usually painless. Most C functions accepting a Nim string converted to a -``cstring`` will likely not need to keep this string around and by the time +`cstring` will likely not need to keep this string around and by the time they return the string won't be needed anymore. However, for the rare cases where a Nim string has to be preserved and made available to the C backend -as a ``cstring``, you will need to manually prevent the string data from being +as a `cstring`, you will need to manually prevent the string data from being freed with `GC_ref `_ and `GC_unref `_. A similar thing happens with C code invoking Nim code which returns a -``cstring``. Consider the following proc: +`cstring`. Consider the following proc: .. code-block:: nim @@ -373,8 +375,8 @@ A similar thing happens with C code invoking Nim code which returns a result = "Hey there C code! " & $rand(100) Since Nim's garbage collector is not aware of the C code, once the -``gimme`` proc has finished it can reclaim the memory of the ``cstring``. -However, from a practical standpoint, the C code invoking the ``gimme`` +`gimme` proc has finished it can reclaim the memory of the `cstring`. +However, from a practical standpoint, the C code invoking the `gimme` function directly will be able to use it since Nim's garbage collector has not had a chance to run *yet*. This gives you enough time to make a copy for the C side of the program, as calling any further Nim procs *might* trigger @@ -397,14 +399,14 @@ Again, if you are wrapping a library which *mallocs* and *frees* data structures, you need to expose the appropriate *free* function to Nim so you can clean it up. And of course, once cleaned you should avoid accessing it from Nim (or C for that matter). Typically C data structures have their own -``malloc_structure`` and ``free_structure`` specific functions, so wrapping +`malloc_structure` and `free_structure` specific functions, so wrapping these for the Nim side should be enough. Thread coordination ------------------- -When the ``NimMain()`` function is called Nim initializes the garbage +When the `NimMain()` function is called Nim initializes the garbage collector to the current thread, which is usually the main thread of your application. If your C code later spawns a different thread and calls Nim code, the garbage collector will fail to work properly and you will crash. diff --git a/doc/destructors.rst b/doc/destructors.rst index 4f06de455..01e2d2ee9 100644 --- a/doc/destructors.rst +++ b/doc/destructors.rst @@ -1,3 +1,5 @@ +.. default-role:: code + ================================== Nim Destructors and Move Semantics ================================== @@ -16,7 +18,7 @@ not use classical GC algorithms anymore but is based on destructors and move semantics. The new runtime's advantages are that Nim programs become oblivious to the involved heap sizes and programs are easier to write to make effective use of multi-core machines. As a nice bonus, files and sockets and -the like will not require manual ``close`` calls anymore. +the like will not require manual `close` calls anymore. This document aims to be a precise specification about how move semantics and destructors work in Nim. @@ -89,12 +91,12 @@ written as: Lifetime-tracking hooks ======================= -The memory management for Nim's standard ``string`` and ``seq`` types as +The memory management for Nim's standard `string` and `seq` types as well as other standard collections is performed via so-called "Lifetime-tracking hooks", which are particular `type bound operators `_. -There are 3 different hooks for each (generic or concrete) object type ``T`` (``T`` can also be a -``distinct`` type) that are called implicitly by the compiler. +There are 3 different hooks for each (generic or concrete) object type `T` (`T` can also be a +`distinct` type) that are called implicitly by the compiler. (Note: The word "hook" here does not imply any kind of dynamic binding or runtime indirections, the implicit calls are statically bound and @@ -109,14 +111,14 @@ other associated resources. Variables are destroyed via this hook when they go out of scope or when the routine they were declared in is about to return. -The prototype of this hook for a type ``T`` needs to be: +The prototype of this hook for a type `T` needs to be: .. code-block:: nim proc `=destroy`(x: var T) -The general pattern in ``=destroy`` looks like: +The general pattern in `=destroy` looks like: .. code-block:: nim @@ -133,20 +135,20 @@ The general pattern in ``=destroy`` looks like: A `=sink` hook moves an object around, the resources are stolen from the source and passed to the destination. It is ensured that the source's destructor does not free the resources afterward by setting the object to its default value -(the value the object's state started in). Setting an object ``x`` back to its -default value is written as ``wasMoved(x)``. When not provided the compiler +(the value the object's state started in). Setting an object `x` back to its +default value is written as `wasMoved(x)`. When not provided the compiler is using a combination of `=destroy` and `copyMem` instead. This is efficient hence users rarely need to implement their own `=sink` operator, it is enough to provide `=destroy` and `=copy`, compiler will take care of the rest. -The prototype of this hook for a type ``T`` needs to be: +The prototype of this hook for a type `T` needs to be: .. code-block:: nim proc `=sink`(dest: var T; source: T) -The general pattern in ``=sink`` looks like: +The general pattern in `=sink` looks like: .. code-block:: nim @@ -156,25 +158,25 @@ The general pattern in ``=sink`` looks like: dest.field = source.field -**Note**: ``=sink`` does not need to check for self-assignments. +**Note**: `=sink` does not need to check for self-assignments. How self-assignments are handled is explained later in this document. `=copy` hook --------------- -The ordinary assignment in Nim conceptually copies the values. The ``=copy`` hook -is called for assignments that couldn't be transformed into ``=sink`` +The ordinary assignment in Nim conceptually copies the values. The `=copy` hook +is called for assignments that couldn't be transformed into `=sink` operations. -The prototype of this hook for a type ``T`` needs to be: +The prototype of this hook for a type `T` needs to be: .. code-block:: nim proc `=copy`(dest: var T; source: T) -The general pattern in ``=copy`` looks like: +The general pattern in `=copy` looks like: .. code-block:: nim @@ -186,48 +188,48 @@ The general pattern in ``=copy`` looks like: dest.field = duplicateResource(source.field) -The ``=copy`` proc can be marked with the ``{.error.}`` pragma. Then any assignment +The `=copy` proc can be marked with the `{.error.}` pragma. Then any assignment that otherwise would lead to a copy is prevented at compile-time. This looks like: .. code-block:: nim proc `=copy`(dest: var T; source: T) {.error.} -but a custom error message (e.g., ``{.error: "custom error".}``) will not be emitted -by the compiler. Notice that there is no ``=`` before the ``{.error.}`` pragma. +but a custom error message (e.g., `{.error: "custom error".}`) will not be emitted +by the compiler. Notice that there is no `=` before the `{.error.}` pragma. Move semantics ============== A "move" can be regarded as an optimized copy operation. If the source of the copy operation is not used afterward, the copy can be replaced by a move. This -document uses the notation ``lastReadOf(x)`` to describe that ``x`` is not +document uses the notation `lastReadOf(x)` to describe that `x` is not used afterwards. This property is computed by a static control flow analysis -but can also be enforced by using ``system.move`` explicitly. +but can also be enforced by using `system.move` explicitly. Swap ==== The need to check for self-assignments and also the need to destroy previous -objects inside ``=copy`` and ``=sink`` is a strong indicator to treat -``system.swap`` as a builtin primitive of its own that simply swaps every -field in the involved objects via ``copyMem`` or a comparable mechanism. -In other words, ``swap(a, b)`` is **not** implemented -as ``let tmp = move(b); b = move(a); a = move(tmp)``. +objects inside `=copy` and `=sink` is a strong indicator to treat +`system.swap` as a builtin primitive of its own that simply swaps every +field in the involved objects via `copyMem` or a comparable mechanism. +In other words, `swap(a, b)` is **not** implemented +as `let tmp = move(b); b = move(a); a = move(tmp)`. This has further consequences: * Objects that contain pointers that point to the same object are not supported by Nim's model. Otherwise swapped objects would end up in an inconsistent state. -* Seqs can use ``realloc`` in the implementation. +* Seqs can use `realloc` in the implementation. Sink parameters =============== -To move a variable into a collection usually ``sink`` parameters are involved. -A location that is passed to a ``sink`` parameter should not be used afterward. +To move a variable into a collection usually `sink` parameters are involved. +A location that is passed to a `sink` parameter should not be used afterward. This is ensured by a static analysis over a control flow graph. If it cannot be proven to be the last usage of the location, a copy is done instead and this copy is then passed to the sink parameter. @@ -235,9 +237,9 @@ copy is then passed to the sink parameter. A sink parameter *may* be consumed once in the proc's body but doesn't have to be consumed at all. The reason for this is that signatures -like ``proc put(t: var Table; k: sink Key, v: sink Value)`` should be possible -without any further overloads and ``put`` might not take ownership of ``k`` if -``k`` already exists in the table. Sink parameters enable an affine type system, +like `proc put(t: var Table; k: sink Key, v: sink Value)` should be possible +without any further overloads and `put` might not take ownership of `k` if +`k` already exists in the table. Sink parameters enable an affine type system, not a linear type system. The employed static analysis is limited and only concerned with local variables; @@ -254,7 +256,7 @@ however, object and tuple fields are treated as separate entities: echo tup[1] -Sometimes it is required to explicitly ``move`` a value into its final position: +Sometimes it is required to explicitly `move` a value into its final position: .. code-block:: nim @@ -294,9 +296,9 @@ Rewrite rules **Note**: There are two different allowed implementation strategies: -1. The produced ``finally`` section can be a single section that is wrapped +1. The produced `finally` section can be a single section that is wrapped around the complete routine body. -2. The produced ``finally`` section is wrapped around the enclosing scope. +2. The produced `finally` section is wrapped around the enclosing scope. The current implementation follows strategy (2). This means that resources are destroyed at the scope exit. @@ -359,13 +361,13 @@ Object and array construction ============================= Object and array construction is treated as a function call where the -function has ``sink`` parameters. +function has `sink` parameters. Destructor removal ================== -``wasMoved(x);`` followed by a `=destroy(x)` operation cancel each other +`wasMoved(x);` followed by a `=destroy(x)` operation cancel each other out. An implementation is encouraged to exploit this in order to improve efficiency and code sizes. The current implementation does perform this optimization. @@ -374,22 +376,22 @@ optimization. Self assignments ================ -``=sink`` in combination with ``wasMoved`` can handle self-assignments but +`=sink` in combination with `wasMoved` can handle self-assignments but it's subtle. -The simple case of ``x = x`` cannot be turned -into ``=sink(x, x); wasMoved(x)`` because that would lose ``x``'s value. +The simple case of `x = x` cannot be turned +into `=sink(x, x); wasMoved(x)` because that would lose `x`'s value. The solution is that simple self-assignments that consist of -- Symbols: ``x = x`` -- Field access: ``x.f = x.f`` -- Array, sequence or string access with indices known at compile-time: ``x[0] = x[0]`` +- Symbols: `x = x` +- Field access: `x.f = x.f` +- Array, sequence or string access with indices known at compile-time: `x[0] = x[0]` are transformed into an empty statement that does nothing. The compiler is free to optimize further cases. -The complex case looks like a variant of ``x = f(x)``, we consider -``x = select(rand() < 0.5, x, y)`` here: +The complex case looks like a variant of `x = f(x)`, we consider +`x = select(rand() < 0.5, x, y)` here: .. code-block:: nim @@ -450,17 +452,17 @@ self-assignments. Lent type ========= -``proc p(x: sink T)`` means that the proc ``p`` takes ownership of ``x``. +`proc p(x: sink T)` means that the proc `p` takes ownership of `x`. To eliminate even more creation/copy <-> destruction pairs, a proc's return -type can be annotated as ``lent T``. This is useful for "getter" accessors +type can be annotated as `lent T`. This is useful for "getter" accessors that seek to allow an immutable view into a container. -The ``sink`` and ``lent`` annotations allow us to remove most (if not all) +The `sink` and `lent` annotations allow us to remove most (if not all) superfluous copies and destructions. -``lent T`` is like ``var T`` a hidden pointer. It is proven by the compiler +`lent T` is like `var T` a hidden pointer. It is proven by the compiler that the pointer does not outlive its origin. No destructor call is injected -for expressions of type ``lent T`` or of type ``var T``. +for expressions of type `lent T` or of type `var T`. .. code-block:: nim @@ -494,9 +496,9 @@ for expressions of type ``lent T`` or of type ``var T``. The .cursor annotation ====================== -Under the ``--gc:arc|orc`` modes Nim's `ref` type is implemented via the same runtime +Under the `--gc:arc|orc` modes Nim's `ref` type is implemented via the same runtime "hooks" and thus via reference counting. This means that cyclic structures cannot be freed -immediately (``--gc:orc`` ships with a cycle collector). With the ``.cursor`` annotation +immediately (`--gc:orc` ships with a cycle collector). With the `.cursor` annotation one can break up cycles declaratively: .. code-block:: nim @@ -510,7 +512,7 @@ But please notice that this is not C++'s weak_ptr, it means the right field is n involved in the reference counting, it is a raw pointer without runtime checks. Automatic reference counting also has the disadvantage that it introduces overhead -when iterating over linked structures. The ``.cursor`` annotation can also be used +when iterating over linked structures. The `.cursor` annotation can also be used to avoid this overhead: .. code-block:: nim @@ -521,11 +523,11 @@ to avoid this overhead: it = it.next -In fact, ``.cursor`` more generally prevents object construction/destruction pairs +In fact, `.cursor` more generally prevents object construction/destruction pairs and so can also be useful in other contexts. The alternative solution would be to -use raw pointers (``ptr``) instead which is more cumbersome and also more dangerous -for Nim's evolution: Later on, the compiler can try to prove ``.cursor`` annotations -to be safe, but for ``ptr`` the compiler has to remain silent about possible +use raw pointers (`ptr`) instead which is more cumbersome and also more dangerous +for Nim's evolution: Later on, the compiler can try to prove `.cursor` annotations +to be safe, but for `ptr` the compiler has to remain silent about possible problems. @@ -556,13 +558,13 @@ indirections: Hook lifting ============ -The hooks of a tuple type ``(A, B, ...)`` are generated by lifting the -hooks of the involved types ``A``, ``B``, ... to the tuple type. In -other words, a copy ``x = y`` is implemented -as ``x[0] = y[0]; x[1] = y[1]; ...``, likewise for ``=sink`` and ``=destroy``. +The hooks of a tuple type `(A, B, ...)` are generated by lifting the +hooks of the involved types `A`, `B`, ... to the tuple type. In +other words, a copy `x = y` is implemented +as `x[0] = y[0]; x[1] = y[1]; ...`, likewise for `=sink` and `=destroy`. -Other value-based compound types like ``object`` and ``array`` are handled -correspondingly. For ``object`` however, the compiler-generated hooks +Other value-based compound types like `object` and `array` are handled +correspondingly. For `object` however, the compiler-generated hooks can be overridden. This can also be important to use an alternative traversal of the involved data structure that is more efficient or in order to avoid deep recursions. @@ -588,18 +590,18 @@ The ability to override a hook leads to a phase ordering problem: discard -The solution is to define ``proc `=destroy`[T](f: var Foo[T])`` before +The solution is to define `proc `=destroy`[T](f: var Foo[T])` before it is used. The compiler generates implicit hooks for all types in *strategic places* so that an explicitly provided hook that comes too "late" can be detected reliably. These *strategic places* have been derived from the rewrite rules and are as follows: -- In the construct ``let/var x = ...`` (var/let binding) - hooks are generated for ``typeof(x)``. -- In ``x = ...`` (assignment) hooks are generated for ``typeof(x)``. -- In ``f(...)`` (function call) hooks are generated for ``typeof(f(...))``. -- For every sink parameter ``x: sink T`` the hooks are generated - for ``typeof(x)``. +- In the construct `let/var x = ...` (var/let binding) + hooks are generated for `typeof(x)`. +- In `x = ...` (assignment) hooks are generated for `typeof(x)`. +- In `f(...)` (function call) hooks are generated for `typeof(f(...))`. +- For every sink parameter `x: sink T` the hooks are generated + for `typeof(x)`. nodestroy pragma @@ -645,20 +647,18 @@ Instead the variable simply points to the literal. The literal is shared between different variables which are pointing to it. The copy operation is deferred until the first write. -```nim -var x = "abc" # no copy -var y = x # no copy -``` +.. code-block:: nim + var x = "abc" # no copy + var y = x # no copy The string literal "abc" is stored in static memory and not allocated on the heap. The variable `x` points to the literal and the variable `y` points to the literal too. There is no copy during assigning operations. -```nim -var x = "abc" # no copy -var y = x # no copy -y[0] = 'h' # copy -``` +.. code-block:: nim + var x = "abc" # no copy + var y = x # no copy + y[0] = 'h' # copy The program above shows when the copy operations happen. When mutating the variable `y`, the Nim compiler creates a fresh copy of `x`, @@ -670,37 +670,34 @@ and the variable `y` becomes a mutable string. Let's look at a silly example demonstrating this behaviour: -```nim -var x = "abc" -var y = x +.. code-block:: nim + var x = "abc" + var y = x -moveMem(addr y[0], addr x[0], 3) -``` + moveMem(addr y[0], addr x[0], 3) The program fails because we need to prepare a fresh copy for the variable `y`. `prepareMutation` should be called before the address operation. -```nim -var x = "abc" -var y = x +.. code-block:: nim + var x = "abc" + var y = x -prepareMutation(y) -moveMem(addr y[0], addr x[0], 3) -assert y == "abc" -``` + prepareMutation(y) + moveMem(addr y[0], addr x[0], 3) + assert y == "abc" Now `prepareMutation` solves the problem. It manually creates a fresh copy and makes the variable `y` mutable. -```nim -var x = "abc" -var y = x +.. code-block:: nim + var x = "abc" + var y = x -prepareMutation(y) -moveMem(addr y[0], addr x[0], 3) -moveMem(addr y[0], addr x[0], 3) -moveMem(addr y[0], addr x[0], 3) -assert y == "abc" -``` + prepareMutation(y) + moveMem(addr y[0], addr x[0], 3) + moveMem(addr y[0], addr x[0], 3) + moveMem(addr y[0], addr x[0], 3) + assert y == "abc" No matter how many times `moveMem` is called, the program compiles and runs. diff --git a/doc/docgen.rst b/doc/docgen.rst index e383bd8d0..c002ddb83 100644 --- a/doc/docgen.rst +++ b/doc/docgen.rst @@ -1,3 +1,5 @@ +.. default-role:: code + =================================== Nim DocGen Tools Guide =================================== @@ -15,7 +17,7 @@ This document describes the `documentation generation tools`:idx: built into the `Nim compiler `_, which can generate HTML and JSON output from input .nim files and projects, as well as HTML and LaTeX from input RST (reStructuredText) files. The output documentation will include the module -dependencies (``import``), any top-level documentation comments (##), and +dependencies (`import`), any top-level documentation comments (##), and exported symbols (*), including procedures, types, and variables. Quick start @@ -125,12 +127,12 @@ Document Types HTML ---- -The generation of HTML documents is done via the ``doc`` command. This command +The generation of HTML documents is done via the `doc` command. This command takes either a single .nim file, outputting a single .html file with the same base filename, or multiple .nim files, outputting multiple .html files and, optionally, an index file. -The ``doc`` command:: +The `doc` command:: nim doc sample Partial Output:: @@ -146,12 +148,12 @@ compiler. JSON ---- -The generation of JSON documents is done via the ``jsondoc`` command. This command +The generation of JSON documents is done via the `jsondoc` command. This command takes in a .nim file and outputs a .json file with the same base filename. Note -that this tool is built off of the ``doc`` command (previously ``doc2``), and +that this tool is built off of the `doc` command (previously `doc2`), and contains the same information. -The ``jsondoc`` command:: +The `jsondoc` command:: nim jsondoc sample Output:: @@ -171,10 +173,10 @@ Output:: ] } -Similarly to the old ``doc`` command, the old ``jsondoc`` command has been -renamed to ``jsondoc0``. +Similarly to the old `doc` command, the old `jsondoc` command has been +renamed to `jsondoc0`. -The ``jsondoc0`` command:: +The `jsondoc0` command:: nim jsondoc0 sample Output:: @@ -190,8 +192,8 @@ Output:: } ] -Note that the ``jsondoc`` command outputs it's JSON without pretty-printing it, -while ``jsondoc0`` outputs pretty-printed JSON. +Note that the `jsondoc` command outputs it's JSON without pretty-printing it, +while `jsondoc0` outputs pretty-printed JSON. Related Options =============== @@ -203,7 +205,7 @@ Project switch nim doc --project filename.nim This will recursively generate documentation of all nim modules imported -into the input module that belong to the Nimble package that ``filename.nim`` +into the input module that belong to the Nimble package that `filename.nim` belongs to. @@ -214,13 +216,13 @@ Index switch nim doc --index:on filename.nim This will generate an index of all the exported symbols in the input Nim -module, and put it into a neighboring file with the extension of ``.idx``. The +module, and put it into a neighboring file with the extension of `.idx`. The index file is line-oriented (newlines have to be escaped). Each line represents a tab-separated record of several columns, the first two mandatory, the rest optional. See the `Index (idx) file format`_ section for details. Once index files have been generated for one or more modules, the Nim -compiler command ``buildIndex directory`` can be run to go over all the index +compiler command `buildIndex directory` can be run to go over all the index files in the specified directory to generate a `theindex.html `_ file. @@ -230,28 +232,28 @@ See source switch :: nim doc --git.url: filename.nim -With the ``git.url`` switch the *See source* hyperlink will appear below each +With the `git.url` switch the *See source* hyperlink will appear below each documented item in your source code pointing to the implementation of that item on a GitHub repository. You can click the link to see the implementation of the item. -The ``git.commit`` switch overrides the hardcoded `devel` branch in config/nimdoc.cfg. +The `git.commit` switch overrides the hardcoded `devel` branch in config/nimdoc.cfg. This is useful to link to a different branch e.g. `--git.commit:master`, or to a tag e.g. `--git.commit:1.2.3` or a commit. Source URLs are generated as `href="${url}/tree/${commit}/${path}#L${line}"` by default and this compatible with GitHub but not with GitLab. -Similarly, ``git.devel`` switch overrides the hardcoded `devel` branch for the `Edit` link which is also useful if you have a different working branch than `devel` e.g. `--git.devel:master`. +Similarly, `git.devel` switch overrides the hardcoded `devel` branch for the `Edit` link which is also useful if you have a different working branch than `devel` e.g. `--git.devel:master`. Edit URLs are generated as `href="${url}/tree/${devel}/${path}#L${line}"` by default. -You can edit ``config/nimdoc.cfg`` and modify the ``doc.item.seesrc`` value with a hyperlink to your own code repository. +You can edit `config/nimdoc.cfg` and modify the `doc.item.seesrc` value with a hyperlink to your own code repository. -In the case of Nim's own documentation, the ``commit`` value is just a commit +In the case of Nim's own documentation, the `commit` value is just a commit hash to append to a formatted URL to https://github.com/nim-lang/Nim. The -``tools/nimweb.nim`` helper queries the current git commit hash during the doc +`tools/nimweb.nim` helper queries the current git commit hash during the doc generation, but since you might be working on an unpublished repository, it -also allows specifying a ``githash`` value in ``web/website.ini`` to force a +also allows specifying a `githash` value in `web/website.ini` to force a specific commit in the output. @@ -259,9 +261,9 @@ Other Input Formats =================== The *Nim compiler* also has support for RST (reStructuredText) files with -the ``rst2html`` and ``rst2tex`` commands. Documents like this one are +the `rst2html` and `rst2tex` commands. Documents like this one are initially written in a dialect of RST which adds support for nim source code -highlighting with the ``.. code-block:: nim`` prefix. ``code-block`` also +highlighting with the `.. code-block:: nim` prefix. `code-block` also supports highlighting of C++ and some other c-like languages. Usage:: @@ -270,17 +272,17 @@ Usage:: Output:: You're reading it! -The ``rst2tex`` command is invoked identically to ``rst2html``, but outputs +The `rst2tex` command is invoked identically to `rst2html`, but outputs a .tex file instead of .html. HTML anchor generation ====================== -When you run the ``rst2html`` command, all sections in the RST document will +When you run the `rst2html` command, all sections in the RST document will get an anchor you can hyperlink to. Usually, you can guess the anchor lower casing the section title and replacing spaces with dashes, and in any case, you -can get it from the table of contents. But when you run the ``doc`` +can get it from the table of contents. But when you run the `doc` command to generate API documentation, some symbol get one or two anchors at the same time: a numerical identifier, or a plain name plus a complex name. @@ -312,31 +314,31 @@ suffix may be added depending on the type of the callable: Callable type Suffix ------------- -------------- proc *empty string* -macro ``.m`` -method ``.e`` -iterator ``.i`` -template ``.t`` -converter ``.c`` +macro `.m` +method `.e` +iterator `.i` +template `.t` +converter `.c` ------------- -------------- -The relationship of type to suffix is made by the proc ``complexName`` in the -``compiler/docgen.nim`` file. Here are some examples of complex names for +The relationship of type to suffix is made by the proc `complexName` in the +`compiler/docgen.nim` file. Here are some examples of complex names for symbols in the `system module `_. -* ``type SomeSignedInt = int | int8 | int16 | int32 | int64`` **=>** +* `type SomeSignedInt = int | int8 | int16 | int32 | int64` **=>** `#SomeSignedInt `_ -* ``var globalRaiseHook: proc (e: ref E_Base): bool {.nimcall.}`` **=>** +* `var globalRaiseHook: proc (e: ref E_Base): bool {.nimcall.}` **=>** `#globalRaiseHook `_ -* ``const NimVersion = "0.0.0"`` **=>** +* `const NimVersion = "0.0.0"` **=>** `#NimVersion `_ -* ``proc getTotalMem(): int {.rtl, raises: [], tags: [].}`` **=>** +* `proc getTotalMem(): int {.rtl, raises: [], tags: [].}` **=>** `#getTotalMem, `_ -* ``proc len[T](x: seq[T]): int {.magic: "LengthSeq", noSideEffect.}`` **=>** +* `proc len[T](x: seq[T]): int {.magic: "LengthSeq", noSideEffect.}` **=>** `#len,seq[T] `_ -* ``iterator pairs[T](a: seq[T]): tuple[key: int, val: T] {.inline.}`` **=>** +* `iterator pairs[T](a: seq[T]): tuple[key: int, val: T] {.inline.}` **=>** `#pairs.i,seq[T] `_ -* ``template newException[](exceptn: typedesc; message: string; - parentException: ref Exception = nil): untyped`` **=>** +* `template newException[](exceptn: typedesc; message: string; + parentException: ref Exception = nil): untyped` **=>** `#newException.t,typedesc,string,ref.Exception `_ @@ -344,13 +346,13 @@ symbols in the `system module `_. Index (idx) file format ======================= -Files with the ``.idx`` extension are generated when you use the `Index +Files with the `.idx` extension are generated when you use the `Index switch <#related-options-index-switch>`_ along with commands to generate documentation from source or text files. You can programmatically generate indices with the `setIndexTerm() `_ and `writeIndexFile() `_ procs. -The purpose of ``idx`` files is to hold the interesting symbols and their HTML +The purpose of `idx` files is to hold the interesting symbols and their HTML references so they can be later concatenated into a big index file with `mergeIndexes() `_. This section documents the file format in detail. @@ -362,7 +364,7 @@ columns is: 1. Mandatory term being indexed. Terms can include quoting according to Nim's rules (e.g. \`^\`). -2. Base filename plus anchor hyperlink (e.g. ``algorithm.html#*,int,SortOrder``). +2. Base filename plus anchor hyperlink (e.g. `algorithm.html#*,int,SortOrder`). 3. Optional human-readable string to display as a hyperlink. If the value is not present or is the empty string, the hyperlink will be rendered using the term. Prefix whitespace indicates that this entry is @@ -371,8 +373,8 @@ columns is: this as a tooltip after hovering a moment over the hyperlink. The index generation tools try to differentiate between documentation -generated from ``.nim`` files and documentation generated from ``.txt`` or -``.rst`` files. The former are always closely related to source code and +generated from `.nim` files and documentation generated from `.txt` or +`.rst` files. The former are always closely related to source code and consist mainly of API entries. The latter are generic documents meant for human reading. @@ -391,7 +393,7 @@ the index file with their third column having as much prefix spaces as their level is in the TOC (at least 1 character). The prefix whitespace helps to filter TOC entries from API or text symbols. This is important because the amount of spaces is used to replicate the hierarchy for document TOCs in the -final index, and TOC entries found in ``.nim`` files are discarded. +final index, and TOC entries found in `.nim` files are discarded. Additional resources @@ -402,8 +404,8 @@ Additional resources `RST Quick Reference `_ -The output for HTML and LaTeX comes from the ``config/nimdoc.cfg`` and -``config/nimdoc.tex.cfg`` configuration files. You can add and modify these +The output for HTML and LaTeX comes from the `config/nimdoc.cfg` and +`config/nimdoc.tex.cfg` configuration files. You can add and modify these files to your project to change the look of the docgen output. You can import the `packages/docutils/rstgen module `_ in your diff --git a/doc/docstyle.rst b/doc/docstyle.rst index 090032b98..7f3fa8cf2 100644 --- a/doc/docstyle.rst +++ b/doc/docstyle.rst @@ -6,7 +6,7 @@ General Guidelines * See also `nep1`_ which should probably be merged here. * Authors should document anything that is exported; documentation for private - procs can be useful too (visible via ``nim doc --docInternal foo.nim``). + procs can be useful too (visible via `nim doc --docInternal foo.nim`). * Within documentation, a period (`.`) should follow each sentence (or sentence fragment) in a comment block. The documentation may be limited to one sentence fragment, but if multiple sentences are within the documentation, each sentence after the first should be complete and in present tense. @@ -15,7 +15,7 @@ General Guidelines and `nim doc` supports it. Likewise with rst files: `nim rst2html` will render those as monospace, and adding `.. default-role:: code` to an rst file will also make those render as monospace when rendered directly in tools such as github. -* In nim sources, for links, prefer `[link text](link.html)` to ``` `link text`_ ``` +* In nim sources, for links, prefer `[link text](link.html)` to `` `link text`_ `` since the syntax is simpler and markdown is more common (likewise, `nim rst2html` also supports it in rst files). .. code-block:: nim @@ -28,8 +28,8 @@ General Guidelines Module-level documentation -------------------------- -Documentation of a module is placed at the top of the module itself. Each line of documentation begins with double hashes (``##``). -Sometimes ``##[ multiline docs containing code ]##`` is preferable, see ``lib/pure/times.nim``. +Documentation of a module is placed at the top of the module itself. Each line of documentation begins with double hashes (`##`). +Sometimes `##[ multiline docs containing code ]##` is preferable, see `lib/pure/times.nim`. Code samples are encouraged, and should follow the general RST syntax: .. code-block:: Nim @@ -80,7 +80,7 @@ Whenever an example of usage would be helpful to the user, you should include on doAssert addThree(3, 125, 6) == -122 result = x +% y +% z -The command ``nim doc`` will then correctly syntax highlight the Nim code within the documentation. +The command `nim doc` will then correctly syntax highlight the Nim code within the documentation. Types ----- @@ -116,8 +116,8 @@ Make sure to place the documentation beside or within the object. .. code-block:: Nim type - ## Bad: this documentation disappears because it annotates the ``type`` keyword - ## above, not ``NamedQueue``. + ## Bad: this documentation disappears because it annotates the `type` keyword + ## above, not `NamedQueue`. NamedQueue*[T] = object name*: string ## This becomes the main documentation for the object, which ## is not what we want. @@ -127,7 +127,7 @@ Make sure to place the documentation beside or within the object. Var, Let, and Const ------------------- -When declaring module-wide constants and values, documentation is encouraged. The placement of doc comments is similar to the ``type`` sections. +When declaring module-wide constants and values, documentation is encouraged. The placement of doc comments is similar to the `type` sections. .. code-block:: Nim @@ -137,9 +137,9 @@ When declaring module-wide constants and values, documentation is encouraged. Th [1,2,3], [2,3,1], [3,1,2], - ] ## Doc comment for ``SpreadArray``. + ] ## Doc comment for `SpreadArray`. -Placement of comments in other areas is usually allowed, but will not become part of the documentation output and should therefore be prefaced by a single hash (``#``). +Placement of comments in other areas is usually allowed, but will not become part of the documentation output and should therefore be prefaced by a single hash (`#`). .. code-block:: Nim diff --git a/doc/drnim.rst b/doc/drnim.rst index ee6e0ea17..d33a6066e 100644 --- a/doc/drnim.rst +++ b/doc/drnim.rst @@ -1,3 +1,5 @@ +.. default-role:: code + =================================== DrNim User Guide =================================== @@ -18,7 +20,7 @@ DrNim's command-line options are the same as the Nim compiler's. DrNim currently only checks the sections of your code that are marked -via ``staticBoundChecks: on``: +via `staticBoundChecks: on`: .. code-block:: nim @@ -31,9 +33,9 @@ overflow errors are *not* prevented. Overflows will be checked for in the future. Later versions of the **Nim compiler** will **assume** that the checks inside -the ``staticBoundChecks: on`` environment have been proven correct and so +the `staticBoundChecks: on` environment have been proven correct and so it will **omit** the runtime checks. If you do not want this behavior, use -instead ``{.push staticBoundChecks: defined(nimDrNim).}``. This way the +instead `{.push staticBoundChecks: defined(nimDrNim).}`. This way the Nim compiler remains unaware of the performed proofs but DrNim will prove your code. @@ -41,7 +43,7 @@ your code. Installation ============ -Run ``koch drnim``, the executable will afterwards be in ``$nim/bin/drnim``. +Run `koch drnim`, the executable will afterwards be in `$nim/bin/drnim`. Motivating Example @@ -67,7 +69,7 @@ detects it and produces the following error message:: cannot prove: i <= len(a) + -1; counter example: i -> 0 a.len -> 0 [IndexCheck] -In other words for ``i == 0`` and ``a.len == 0`` (for example!) there would be +In other words for `i == 0` and `a.len == 0` (for example!) there would be an index out of bounds error. @@ -82,37 +84,37 @@ DrNim adds 4 additional annotations (pragmas) to Nim: - `assume`:idx: These pragmas are ignored by the Nim compiler so that they don't have to -be disabled via ``when defined(nimDrNim)``. +be disabled via `when defined(nimDrNim)`. Invariant --------- -An ``invariant`` is a proposition that must be true after every loop +An `invariant` is a proposition that must be true after every loop iteration, it's tied to the loop body it's part of. Requires -------- -A ``requires`` annotation describes what the function expects to be true -before it's called so that it can perform its operation. A ``requires`` +A `requires` annotation describes what the function expects to be true +before it's called so that it can perform its operation. A `requires` annotation is also called a `precondition`:idx:. Ensures ------- -An ``ensures`` annotation describes what will be true after the function -call. An ``ensures`` annotation is also called a `postcondition`:idx:. +An `ensures` annotation describes what will be true after the function +call. An `ensures` annotation is also called a `postcondition`:idx:. Assume ------ -An ``assume`` annotation describes what DrNim should **assume** to be true +An `assume` annotation describes what DrNim should **assume** to be true in this section of the program. It is an unsafe escape mechanism comparable -to Nim's ``cast`` statement. Use it only when you really know better +to Nim's `cast` statement. Use it only when you really know better than DrNim. You should add a comment to a paper that proves the proposition you assume. @@ -143,12 +145,12 @@ Example: insertionSort Unfortunately, the invariants required to prove that this code is correct take more code than the imperative instructions. However, this effort can be compensated by the fact that the result needs very little testing. Be aware though that -DrNim only proves that after ``insertionSort`` this condition holds:: +DrNim only proves that after `insertionSort` this condition holds:: forall(i in 1..``. -A ``prop`` is either a comparison or a compound:: +The basic syntax is `ensures|requires|invariant: `. +A `prop` is either a comparison or a compound:: prop = nim_bool_expression | prop 'and' prop @@ -186,17 +188,17 @@ A ``prop`` is either a comparison or a compound:: quantifier = 'in' nim_iteration_expression -``nim_iteration_expression`` here is an ordinary expression of Nim code -that describes an iteration space, for example ``1..4`` or ``1.. a.len``. +`nim_bool_expression` here is an ordinary expression of Nim code of +type `bool` like `a == 3` or `23 > a.len`. The supported subset of Nim code that can be used in these expressions -is currently underspecified but ``let`` variables, function parameters -and ``result`` (which represents the function's final result) are amenable +is currently underspecified but `let` variables, function parameters +and `result` (which represents the function's final result) are amenable for verification. The expressions must not have any side-effects and must terminate. -The operators ``forall``, ``exists``, ``->``, ``<->`` have to imported -from ``std / logic``. +The operators `forall`, `exists`, `->`, `<->` have to imported +from `std / logic`. diff --git a/doc/estp.rst b/doc/estp.rst index 805a84eb7..8146562b6 100644 --- a/doc/estp.rst +++ b/doc/estp.rst @@ -1,3 +1,5 @@ +.. default-role:: code + =================================================== Embedded Stack Trace Profiler (ESTP) User Guide =================================================== @@ -10,21 +12,21 @@ Nim comes with a platform independent profiler - the Embedded Stack Trace Profiler (ESTP). The profiler is *embedded* into your executable. To activate the profiler you need to do: -* compile your program with the ``--profiler:on --stackTrace:on`` command +* compile your program with the `--profiler:on --stackTrace:on` command line options -* import the ``nimprof`` module +* import the `nimprof` module * run your program as usual. -You can in fact look at ``nimprof``'s source code to see how to implement +You can in fact look at `nimprof`'s source code to see how to implement your own profiler. -The setting ``--profiler:on`` defines the conditional symbol ``profiler``. +The setting `--profiler:on` defines the conditional symbol `profiler`. After your program has finished the profiler will create a -file ``profile_results.txt`` containing the profiling results. +file `profile_results.txt` containing the profiling results. Since the profiler works by examining stack traces, it's essential that -the option ``--stackTrace:on`` is active! Unfortunately this means that a +the option `--stackTrace:on` is active! Unfortunately this means that a profiling build is much slower than a release build. @@ -35,12 +37,12 @@ You can also use ESTP as a memory profiler to see which stack traces allocate the most memory and thus create the most GC pressure. It may also help to find memory leaks. To activate the memory profiler you need to do: -* compile your program with the ``--profiler:off --stackTrace:on -d:memProfiler`` - command line options. Yes it's ``--profiler:off``. -* import the ``nimprof`` module +* compile your program with the `--profiler:off --stackTrace:on -d:memProfiler` + command line options. Yes it's `--profiler:off`. +* import the `nimprof` module * run your program as usual. -Define the symbol ``ignoreAllocationSize`` so that only the number of +Define the symbol `ignoreAllocationSize` so that only the number of allocations is counted and the sizes of the memory allocations do not matter. @@ -51,7 +53,7 @@ The results file lists stack traces ordered by significance. The following example file has been generated by profiling the Nim compiler itself: It shows that in total 5.4% of the runtime has been spent -in ``crcFromRope`` or its children. +in `crcFromRope` or its children. In general the stack traces show you immediately where the problem is because the trace acts like an explanation; in traditional profilers you can only find diff --git a/doc/filters.rst b/doc/filters.rst index 40346ecaf..52704411f 100644 --- a/doc/filters.rst +++ b/doc/filters.rst @@ -1,3 +1,5 @@ +.. default-role:: code + =================== Source Code Filters =================== @@ -8,7 +10,7 @@ A `Source Code Filter (SCF)` transforms the input character stream to an in-mem output stream before parsing. A filter can be used to provide templating systems or preprocessors. -To use a filter for a source file the ``#?`` notation is used:: +To use a filter for a source file the `#?` notation is used:: #? stdtmpl(subsChar = '$', metaChar = '#') #proc generateXML(name, age: string): string = @@ -21,9 +23,9 @@ To use a filter for a source file the ``#?`` notation is used:: As the example shows, passing arguments to a filter can be done just like an ordinary procedure call with named or positional arguments. The available parameters depend on the invoked filter. Before version 0.12.0 of -the language ``#!`` was used instead of ``#?``. +the language `#!` was used instead of `#?`. -**Hint:** With ``--hint[codeBegin]:on`` or ``--verbosity:2`` +**Hint:** With `--hint[codeBegin]:on` or `--verbosity:2` (or higher) while compiling or `nim check`, Nim lists the processed code after each filter application. @@ -32,8 +34,8 @@ Usage First, put your SCF code in a separate file with filters specified in the first line. **Note:** You can name your SCF file with any file extension you want, but the -conventional extension is ``.nimf`` -(it used to be ``.tmpl`` but that was too generic, for example preventing github to +conventional extension is `.nimf` +(it used to be `.tmpl` but that was too generic, for example preventing github to recognize it as Nim source file). If we use `generateXML` code shown above and call the SCF file `xmlGen.nimf` @@ -47,7 +49,7 @@ In your `main.nim`: Pipe operator ============= -Filters can be combined with the ``|`` pipe operator:: +Filters can be combined with the `|` pipe operator:: #? strip(startswith="<") | stdtmpl #proc generateXML(name, age: string): string = @@ -68,10 +70,10 @@ The replace filter replaces substrings in each line. Parameters and their defaults: - ``sub: string = ""`` + `sub: string = ""` the substring that is searched for - ``by: string = ""`` + `by: string = ""` the string the substring is replaced with @@ -83,14 +85,14 @@ each line. Parameters and their defaults: - ``startswith: string = ""`` + `startswith: string = ""` strip only the lines that start with *startswith* (ignoring leading whitespace). If empty every line is stripped. - ``leading: bool = true`` + `leading: bool = true` strip leading whitespace - ``trailing: bool = true`` + `trailing: bool = true` strip trailing whitespace @@ -99,25 +101,25 @@ StdTmpl filter The stdtmpl filter provides a simple templating engine for Nim. The filter uses a line based parser: Lines prefixed with a *meta character* -(default: ``#``) contain Nim code, other lines are verbatim. Because +(default: `#`) contain Nim code, other lines are verbatim. Because indentation-based parsing is not suited for a templating engine, control flow -statements need ``end X`` delimiters. +statements need `end X` delimiters. Parameters and their defaults: - ``metaChar: char = '#'`` + `metaChar: char = '#'` prefix for a line that contains Nim code - ``subsChar: char = '$'`` + `subsChar: char = '$'` prefix for a Nim expression within a template line - ``conc: string = " & "`` + `conc: string = " & "` the operation for concatenation - ``emit: string = "result.add"`` + `emit: string = "result.add"` the operation to emit a string literal - ``toString: string = "$"`` + `toString: string = "$"` the operation that is applied to each expression Example:: @@ -174,18 +176,18 @@ The filter transforms this into: Each line that does not start with the meta character (ignoring leading -whitespace) is converted to a string literal that is added to ``result``. +whitespace) is converted to a string literal that is added to `result`. The substitution character introduces a Nim expression *e* within the string literal. *e* is converted to a string with the *toString* operation -which defaults to ``$``. For strong type checking, set ``toString`` to the +which defaults to `$`. For strong type checking, set `toString` to the empty string. *e* must match this PEG pattern:: e <- [a-zA-Z\128-\255][a-zA-Z0-9\128-\255_.]* / '{' x '}' x <- '{' x+ '}' / [^}]* -To produce a single substitution character it has to be doubled: ``$$`` -produces ``$``. +To produce a single substitution character it has to be doubled: `$$` +produces `$`. The template engine is quite flexible. It is easy to produce a procedure that writes the template code directly to a file:: diff --git a/doc/gc.rst b/doc/gc.rst index 29b9a4131..4455afcbe 100644 --- a/doc/gc.rst +++ b/doc/gc.rst @@ -1,3 +1,5 @@ +.. default-role:: code + ======================= Nim's Memory Management ======================= @@ -27,37 +29,37 @@ and how the memory management strategies other than garbage collectors work. Multi-paradigm Memory Management Strategies =========================================== -To choose the memory management strategy use the ``--gc:`` switch. +To choose the memory management strategy use the `--gc:` switch. -- ``--gc:refc``. This is the default GC. It's a +- `--gc:refc`. This is the default GC. It's a deferred reference counting based garbage collector with a simple Mark&Sweep backup GC in order to collect cycles. Heaps are thread-local. -- ``--gc:markAndSweep``. Simple Mark-And-Sweep based garbage collector. Heaps are thread-local. -- ``--gc:boehm``. Boehm based garbage collector, it offers a shared heap. -- ``--gc:go``. Go's garbage collector, useful for interoperability with Go. Offers a shared heap. -- ``--gc:arc``. Plain reference counting with +- `--gc:markAndSweep`. Simple Mark-And-Sweep based garbage collector. Heaps are thread-local. +- `--gc:boehm`. Boehm based garbage collector, it offers a shared heap. +- `--gc:go`. Go's garbage collector, useful for interoperability with Go. Offers a shared heap. +- `--gc:arc`. Plain reference counting with `move semantic optimizations `_, offers a shared heap. It offers deterministic performance for `hard realtime`:idx: systems. Reference cycles cause memory leaks, beware. -- ``--gc:orc``. Same as ``--gc:arc`` but adds a cycle collector based on "trial deletion". +- `--gc:orc`. Same as `--gc:arc` but adds a cycle collector based on "trial deletion". Unfortunately, that makes its performance profile hard to reason about so it is less useful for hard real-time systems. -- ``--gc:none``. No memory management strategy nor a garbage collector. Allocated memory is - simply never freed. You should use ``--gc:arc`` instead. +- `--gc:none`. No memory management strategy nor a garbage collector. Allocated memory is + simply never freed. You should use `--gc:arc` instead. ================== ======== ================= ============== =================== Memory Management Heap Reference Cycles Stop-The-World Command line switch ================== ======== ================= ============== =================== -RefC Local Cycle Collector No ``--gc:refc`` -Mark & Sweep Local Cycle Collector No ``--gc:markAndSweep`` -ARC Shared Leak No ``--gc:arc`` -ORC Shared Cycle Collector No ``--gc:orc`` -Boehm Shared Cycle Collector Yes ``--gc:boehm`` -Go Shared Cycle Collector Yes ``--gc:go`` -None Manual Manual Manual ``--gc:none`` +RefC Local Cycle Collector No `--gc:refc` +Mark & Sweep Local Cycle Collector No `--gc:markAndSweep` +ARC Shared Leak No `--gc:arc` +ORC Shared Cycle Collector No `--gc:orc` +Boehm Shared Cycle Collector Yes `--gc:boehm` +Go Shared Cycle Collector Yes `--gc:go` +None Manual Manual Manual `--gc:none` ================== ======== ================= ============== =================== JavaScript's garbage collector is used for the `JavaScript and NodeJS @@ -73,14 +75,14 @@ Cycle collector --------------- The cycle collector can be en-/disabled independently from the other parts of -the garbage collector with ``GC_enableMarkAndSweep`` and ``GC_disableMarkAndSweep``. +the garbage collector with `GC_enableMarkAndSweep` and `GC_disableMarkAndSweep`. Soft real-time support ---------------------- To enable real-time support, the symbol `useRealtimeGC`:idx: needs to be -defined via ``--define:useRealtimeGC`` (you can put this into your config +defined via `--define:useRealtimeGC` (you can put this into your config file as well). With this switch the garbage collector supports the following operations: @@ -88,29 +90,29 @@ With this switch the garbage collector supports the following operations: proc GC_setMaxPause*(maxPauseInUs: int) proc GC_step*(us: int, strongAdvice = false, stackSize = -1) -The unit of the parameters ``maxPauseInUs`` and ``us`` is microseconds. +The unit of the parameters `maxPauseInUs` and `us` is microseconds. These two procs are the two modus operandi of the real-time garbage collector: (1) GC_SetMaxPause Mode - You can call ``GC_SetMaxPause`` at program startup and then each triggered - garbage collector run tries to not take longer than ``maxPause`` time. However, it is + You can call `GC_SetMaxPause` at program startup and then each triggered + garbage collector run tries to not take longer than `maxPause` time. However, it is possible (and common) that the work is nevertheless not evenly distributed - as each call to ``new`` can trigger the garbage collector and thus take ``maxPause`` + as each call to `new` can trigger the garbage collector and thus take `maxPause` time. (2) GC_step Mode - This allows the garbage collector to perform some work for up to ``us`` time. + This allows the garbage collector to perform some work for up to `us` time. This is useful to call in the main loop to ensure the garbage collector can do its work. - To bind all garbage collector activity to a ``GC_step`` call, - deactivate the garbage collector with ``GC_disable`` at program startup. - If ``strongAdvice`` is set to ``true``, + To bind all garbage collector activity to a `GC_step` call, + deactivate the garbage collector with `GC_disable` at program startup. + If `strongAdvice` is set to `true`, then the garbage collector will be forced to perform the collection cycle. Otherwise, the garbage collector may decide not to do anything, if there is not much garbage to collect. - You may also specify the current stack size via ``stackSize`` parameter. + You may also specify the current stack size via `stackSize` parameter. It can improve performance when you know that there are no unique Nim references below a certain point on the stack. Make sure the size you specify is greater than the potential worst-case size. @@ -130,16 +132,16 @@ Time measurement with garbage collectors ---------------------------------------- The garbage collectors' way of measuring time uses -(see ``lib/system/timers.nim`` for the implementation): +(see `lib/system/timers.nim` for the implementation): -1) ``QueryPerformanceCounter`` and ``QueryPerformanceFrequency`` on Windows. -2) ``mach_absolute_time`` on Mac OS X. -3) ``gettimeofday`` on Posix systems. +1) `QueryPerformanceCounter` and `QueryPerformanceFrequency` on Windows. +2) `mach_absolute_time` on Mac OS X. +3) `gettimeofday` on Posix systems. As such it supports a resolution of nanoseconds internally; however, the API uses microseconds for convenience. -Define the symbol ``reportMissedDeadlines`` to make the +Define the symbol `reportMissedDeadlines` to make the garbage collector output whenever it missed a deadline. The reporting will be enhanced and supported by the API in later versions of the collector. @@ -148,9 +150,9 @@ Tweaking the garbage collector ------------------------------ The collector checks whether there is still time left for its work after -every ``workPackage``'th iteration. This is currently set to 100 which means +every `workPackage`'th iteration. This is currently set to 100 which means that up to 100 objects are traversed and freed before it checks again. Thus -``workPackage`` affects the timing granularity and may need to be tweaked in +`workPackage` affects the timing granularity and may need to be tweaked in highly specialized environments or for older hardware. @@ -158,22 +160,22 @@ Keeping track of memory ======================= If you need to pass around memory allocated by Nim to C, you can use the -procs ``GC_ref`` and ``GC_unref`` to mark objects as referenced to avoid them +procs `GC_ref` and `GC_unref` to mark objects as referenced to avoid them being freed by the garbage collector. Other useful procs from `system `_ you can use to keep track of memory are: -* ``getTotalMem()`` Returns the amount of total memory managed by the garbage collector. -* ``getOccupiedMem()`` Bytes reserved by the garbage collector and used by objects. -* ``getFreeMem()`` Bytes reserved by the garbage collector and not in use. -* ``GC_getStatistics()`` Garbage collector statistics as a human-readable string. +* `getTotalMem()` Returns the amount of total memory managed by the garbage collector. +* `getOccupiedMem()` Bytes reserved by the garbage collector and used by objects. +* `getFreeMem()` Bytes reserved by the garbage collector and not in use. +* `GC_getStatistics()` Garbage collector statistics as a human-readable string. These numbers are usually only for the running thread, not for the whole heap, -with the exception of ``--gc:boehm`` and ``--gc:go``. +with the exception of `--gc:boehm` and `--gc:go`. -In addition to ``GC_ref`` and ``GC_unref`` you can avoid the garbage collector by manually -allocating memory with procs like ``alloc``, ``alloc0``, ``allocShared``, ``allocShared0`` or ``allocCStringArray``. +In addition to `GC_ref` and `GC_unref` you can avoid the garbage collector by manually +allocating memory with procs like `alloc`, `alloc0`, `allocShared`, `allocShared0` or `allocCStringArray`. The garbage collector won't try to free them, you need to call their respective *dealloc* pairs -(``dealloc``, ``deallocShared``, ``deallocCStringArray``, etc) +(`dealloc`, `deallocShared`, `deallocCStringArray`, etc) when you are done with them or they will leak. @@ -182,12 +184,12 @@ Heap dump The heap dump feature is still in its infancy, but it already proved useful for us, so it might be useful for you. To get a heap dump, compile -with ``-d:nimTypeNames`` and call ``dumpNumberOfInstances`` at a strategic place in your program. +with `-d:nimTypeNames` and call `dumpNumberOfInstances` at a strategic place in your program. This produces a list of the used types in your program and for every type the total amount of object instances for this type as well as the total amount of bytes these instances take up. The numbers count the number of objects in all garbage collector heaps, they refer to all running threads, not only to the current thread. (The current thread -would be the thread that calls ``dumpNumberOfInstances``.) This might +would be the thread that calls `dumpNumberOfInstances`.) This might change in later versions. diff --git a/doc/hcr.rst b/doc/hcr.rst index 6dc65b1cd..7e9d71da3 100644 --- a/doc/hcr.rst +++ b/doc/hcr.rst @@ -1,3 +1,5 @@ +.. default-role:: code + =================================== Hot code reloading =================================== @@ -19,8 +21,8 @@ so we have to use a helper module where the major logic we want to change during development resides. In this example, we use SDL2 to create a window and we reload the logic -code when ``F9`` is pressed. The important lines are marked with ``#***``. -To install SDL2 you can use ``nimble install sdl2``. +code when `F9` is pressed. The important lines are marked with `#***`. +To install SDL2 you can use `nimble install sdl2`. .. code-block:: nim @@ -125,7 +127,7 @@ Then recompile the project, but do not restart or quit the mymain.exe program! nim c --hotcodereloading:on mymain.nim -Now give the ``mymain`` SDL window the focus, press F9, and watch the +Now give the `mymain` SDL window the focus, press F9, and watch the updated version of the program. @@ -133,8 +135,8 @@ updated version of the program. Reloading API ============= -One can use the special event handlers ``beforeCodeReload`` and -``afterCodeReload`` to reset the state of a particular variable or to force +One can use the special event handlers `beforeCodeReload` and +`afterCodeReload` to reset the state of a particular variable or to force the execution of certain statements: .. code-block:: Nim @@ -178,8 +180,8 @@ It's expected that most projects will implement the reloading with a suitable build-system triggered IPC notification mechanism, but a polling solution is also possible through the provided `hasAnyModuleChanged()`:idx: API. -In order to access ``beforeCodeReload``, ``afterCodeReload``, ``hasModuleChanged`` -or ``hasAnyModuleChanged`` one must import the `hotcodereloading`:idx: module. +In order to access `beforeCodeReload`, `afterCodeReload`, `hasModuleChanged` +or `hasAnyModuleChanged` one must import the `hotcodereloading`:idx: module. Native code targets @@ -187,11 +189,11 @@ Native code targets Native projects using the hot code reloading option will be implicitly compiled with the `-d:useNimRtl` option and they will depend on both -the ``nimrtl`` library and the ``nimhcr`` library which implements the -hot code reloading run-time. Both libraries can be found in the ``lib`` +the `nimrtl` library and the `nimhcr` library which implements the +hot code reloading run-time. Both libraries can be found in the `lib` folder of Nim and can be compiled into dynamic libraries to satisfy runtime demands of the example code above. An example of compiling -``nimhcr.nim`` and ``nimrtl.nim`` when the source dir of Nim is installed +`nimhcr.nim` and `nimrtl.nim` when the source dir of Nim is installed with choosenim follows. :: @@ -208,16 +210,16 @@ with choosenim follows. # source directory (.dll for Windows, .so for Unix, .dylib for MacOS) All modules of the project will be compiled to separate dynamic link -libraries placed in the ``nimcache`` directory. Please note that during +libraries placed in the `nimcache` directory. Please note that during the execution of the program, the hot code reloading run-time will load only copies of these libraries in order to not interfere with any newly issued build commands. The main module of the program is considered non-reloadable. Please note that procs from reloadable modules should not appear in the call stack of -program while ``performCodeReload`` is being called. Thus, the main module +program while `performCodeReload` is being called. Thus, the main module is a suitable place for implementing a program loop capable of calling -``performCodeReload``. +`performCodeReload`. Please note that reloading won't be possible when any of the type definitions in the program has been changed. When closure iterators are used (directly or diff --git a/doc/idetools.rst b/doc/idetools.rst index 27926b14f..dcafaf45f 100644 --- a/doc/idetools.rst +++ b/doc/idetools.rst @@ -1,3 +1,5 @@ +.. default-role:: code + ================================ Nim IDE Integration Guide ================================ @@ -18,8 +20,8 @@ Note: this is mostly outdated, see instead `nimsuggest `_ Nim differs from many other compilers in that it is really fast, and being so fast makes it suited to provide external queries for text editors about the source code being written. Through the -``idetools`` command of `the compiler `_, any IDE -can query a ``.nim`` source file and obtain useful information like +`idetools` command of `the compiler `_, any IDE +can query a `.nim` source file and obtain useful information like definition of symbols or suggestions for completion. This document will guide you through the available options. If you @@ -36,7 +38,7 @@ Specifying the location of the query ------------------------------------ All of the available idetools commands require you to specify a -query location through the ``--track`` or ``--trackDirty`` switches. +query location through the `--track` or `--trackDirty` switches. The general idetools invocations are:: nim idetools --track:FILE,LINE,COL proj.nim @@ -45,35 +47,35 @@ Or:: nim idetools --trackDirty:DIRTY_FILE,FILE,LINE,COL proj.nim -``proj.nim`` +`proj.nim` This is the main *project* filename. Most of the time you will pass in the same as **FILE**, but for bigger projects this is the file which is used as main entry point for the program, the one which users compile to generate a final binary. -```` +`` This would be any of the other idetools available options, like - ``--def`` or ``--suggest`` explained in the following sections. + `--def` or `--suggest` explained in the following sections. -``COL`` +`COL` An integer with the column you are going to query. For the compiler columns start at zero, so the first column will be **0** and the last in an 80 column terminal will be **79**. -``LINE`` +`LINE` An integer with the line you are going to query. For the compiler lines start at **1**. -``FILE`` +`FILE` The file you want to perform the query on. Usually you will pass in the same value as **proj.nim**. -``DIRTY_FILE`` +`DIRTY_FILE` The **FILE** parameter is enough for static analysis, but IDEs tend to have *unsaved buffers* where the user may still be in the middle of typing a line. In such situations the IDE can save the current contents to a temporary file and then use the - ``--trackDirty`` switch. + `--trackDirty` switch. Dirty files are likely to contain errors and they are usually compiled partially only to the point needed to service the @@ -91,7 +93,7 @@ Or:: Definitions ----------- -The ``--def`` idetools switch performs a query about the definition +The `--def` idetools switch performs a query about the definition of a specific symbol. If available, idetools will answer with the type, source file, line/column information and other accessory data if available like a docstring. With this information an IDE can @@ -112,7 +114,7 @@ can't find any valid symbol matching the position of the query. Suggestions ----------- -The ``--suggest`` idetools switch performs a query about possible +The `--suggest` idetools switch performs a query about possible completion symbols at some point in the file. IDEs can easily provide an autocompletion feature where the IDE scans the current file (and related ones, if it knows about the language being edited and follows @@ -134,7 +136,7 @@ Idetools will try to return the suggestions sorted first by scope Invocation context ------------------ -The ``--context`` idetools switch is very similar to the suggestions +The `--context` idetools switch is very similar to the suggestions switch, but instead of being used after the user has typed a dot character, this one is meant to be used after the user has typed an opening brace to start typing parameters. @@ -143,7 +145,7 @@ an opening brace to start typing parameters. Symbol usages ------------- -The ``--usages`` idetools switch lists all usages of the symbol at +The `--usages` idetools switch lists all usages of the symbol at a position. IDEs can use this to find all the places in the file where the symbol is used and offer the user to rename it in all places at the same time. Again, a pure string based search and @@ -207,15 +209,15 @@ Idetools outputs is always returned on single lines separated by tab characters (``\t``). The values of each column are: 1. Three characters indicating the type of returned answer (e.g. - def for definition, ``sug`` for suggestion, etc). -2. Type of the symbol. This can be ``skProc``, ``skLet``, and just - about any of the enums defined in the module ``compiler/ast.nim``. + def for definition, `sug` for suggestion, etc). +2. Type of the symbol. This can be `skProc`, `skLet`, and just + about any of the enums defined in the module `compiler/ast.nim`. 3. Full qualified path of the symbol. If you are querying a symbol - defined in the ``proj.nim`` file, this would have the form - ``proj.symbolName``. + defined in the `proj.nim` file, this would have the form + `proj.symbolName`. 4. Type/signature. For variables and enums this will contain the type of the symbol, for procs, methods and templates this will - contain the full unique signature (e.g. ``proc (File)``). + contain the full unique signature (e.g. `proc (File)`). 5. Full path to the file containing the symbol. 6. Line where the symbol is located in the file. Lines start to count at **1**. @@ -374,8 +376,8 @@ While at the language level a method is differentiated from others by the parameters and return value, the signature of the method returned by idetools returns also the pragmas for the method. -Note that at the moment the word ``proc`` is returned for the -signature of the found method instead of the expected ``method``. +Note that at the moment the word `proc` is returned for the +signature of the found method instead of the expected `method`. This may change in the future. | **Third column**: module + [n scope nesting] + method name. @@ -517,7 +519,7 @@ Test suite ========== To verify that idetools is working properly there are files in the -``tests/caas/`` directory which provide unit testing. If you find +`tests/caas/` directory which provide unit testing. If you find odd idetools behaviour and are able to reproduce it, you are welcome to report it as a bug and add a test to the suite to avoid future regressions. @@ -533,27 +535,27 @@ run it manually. First you have to compile the tester:: $ cd my/nim/checkout/tests $ nim c testament/caasdriver.nim -Running the ``caasdriver`` without parameters will attempt to process +Running the `caasdriver` without parameters will attempt to process all the test cases in all three operation modes. If a test succeeds nothing will be printed and the process will exit with zero. If any test fails, the specific line of the test preceding the failure and the failure itself will be dumped to stdout, along with a final indicator of the success state and operation mode. You can pass the -parameter ``verbose`` to force all output even on successful tests. +parameter `verbose` to force all output even on successful tests. -The normal operation mode is called ``ProcRun`` and it involves +The normal operation mode is called `ProcRun` and it involves starting a process for each command or query, similar to running -manually the Nim compiler from the commandline. The ``CaasRun`` -mode starts a server process to answer all queries. The ``SymbolProcRun`` +manually the Nim compiler from the commandline. The `CaasRun` +mode starts a server process to answer all queries. The `SymbolProcRun` mode is used by compiler developers. This means that running all -tests involves processing all ``*.txt`` files three times, which +tests involves processing all `*.txt` files three times, which can be quite time consuming. If you don't want to run all the test case files you can pass any -substring as a parameter to ``caasdriver``. Only files matching the +substring as a parameter to `caasdriver`. Only files matching the passed substring will be run. The filtering doesn't use any globbing metacharacters, it's a plain match. For example, to run only -``*-compile*.txt`` tests in verbose mode:: +`*-compile*.txt` tests in verbose mode:: ./caasdriver verbose -compile @@ -561,18 +563,18 @@ metacharacters, it's a plain match. For example, to run only Test case file format --------------------- -All the ``tests/caas/*.txt`` files encode a session with the compiler: +All the `tests/caas/*.txt` files encode a session with the compiler: * The first line indicates the main project file. -* Lines starting with ``>`` indicate a command to be sent to the +* Lines starting with `>` indicate a command to be sent to the compiler and the lines following a command include checks for - expected or forbidden output (``!`` for forbidden). + expected or forbidden output (`!` for forbidden). -* If a line starts with ``#`` it will be ignored completely, so you +* If a line starts with `#` it will be ignored completely, so you can use that for comments. -* Since some cases are specific to either ``ProcRun`` or ``CaasRun`` +* Since some cases are specific to either `ProcRun` or `CaasRun` modes, you can prefix a line with the mode and the line will be processed only in that mode. diff --git a/doc/intern.rst b/doc/intern.rst index 0eb70e143..2456b25fd 100644 --- a/doc/intern.rst +++ b/doc/intern.rst @@ -1,3 +1,5 @@ +.. default-role:: code + ========================================= Internals of the Nim Compiler ========================================= @@ -19,19 +21,19 @@ The Nim project's directory structure is: ============ =================================================== Path Purpose ============ =================================================== -``bin`` generated binary files -``build`` generated C code for the installation -``compiler`` the Nim compiler itself; note that this +`bin` generated binary files +`build` generated C code for the installation +`compiler` the Nim compiler itself; note that this code has been translated from a bootstrapping version written in Pascal, so the code is **not** a poster child of good Nim code -``config`` configuration files for Nim -``dist`` additional packages for the distribution -``doc`` the documentation; it is a bunch of +`config` configuration files for Nim +`dist` additional packages for the distribution +`doc` the documentation; it is a bunch of reStructuredText files -``lib`` the Nim library -``web`` website of Nim; generated by ``nimweb`` - from the ``*.txt`` and ``*.nimf`` files +`lib` the Nim library +`web` website of Nim; generated by `nimweb` + from the `*.txt` and `*.nimf` files ============ =================================================== @@ -53,7 +55,7 @@ And for a debug version compatible with GDB:: nim c koch.nim ./koch boot --debuginfo --linedir:on -The ``koch`` program is Nim's maintenance script. It is a replacement for +The `koch` program is Nim's maintenance script. It is a replacement for make and shell scripting with the advantage that it is much more portable. More information about its options can be found in the `koch `_ documentation. @@ -67,8 +69,8 @@ Coding Guidelines * Max line length is 80 characters. * Provide spaces around binary operators if that enhances readability. * Use a space after a colon, but not before it. -* [deprecated] Start types with a capital ``T``, unless they are - pointers/references which start with ``P``. +* [deprecated] Start types with a capital `T`, unless they are + pointers/references which start with `P`. See also the `API naming design `_ document. @@ -81,12 +83,12 @@ portable programming language (within certain limits) and Nim generates C code, porting the code generator is not necessary. POSIX-compliant systems on conventional hardware are usually pretty easy to -port: Add the platform to ``platform`` (if it is not already listed there), +port: Add the platform to `platform` (if it is not already listed there), check that the OS, System modules work and recompile Nim. The only case where things aren't as easy is when the garbage collector needs some assembler tweaking to work. The standard -version of the GC uses C's ``setjmp`` function to store all registers +version of the GC uses C's `setjmp` function to store all registers on the hardware stack. It may be necessary that the new platform needs to replace this generic code by some assembler code. @@ -111,7 +113,7 @@ Complex assignments We already know the type information as a graph in the compiler. Thus we need to serialize this graph as RTTI for C code generation. -Look at the file ``lib/system/hti.nim`` for more information. +Look at the file `lib/system/hti.nim` for more information. Rebuilding the compiler ======================== @@ -139,7 +141,7 @@ Debugging the compiler ====================== You can of course use GDB or Visual Studio to debug the -compiler (via ``--debuginfo --lineDir:on``). However, there +compiler (via `--debuginfo --lineDir:on`). However, there are also lots of procs that aid in debugging: @@ -170,19 +172,19 @@ These procs may not be imported by a module. You can import them directly for de from renderer import renderTree from msgs import `??` -To create a new compiler for each run, use ``koch temp``:: +To create a new compiler for each run, use `koch temp`:: ./koch temp c /tmp/test.nim -``koch temp`` creates a debug build of the compiler, which is useful +`koch temp` creates a debug build of the compiler, which is useful to create stacktraces for compiler debugging. See also `Rebuilding the compiler`_ if you need more control. Bisecting for regressions ========================= -``koch temp`` returns 125 as the exit code in case the compiler -compilation fails. This exit code tells ``git bisect`` to skip the +`koch temp` returns 125 as the exit code in case the compiler +compilation fails. This exit code tells `git bisect` to skip the current commit.:: git bisect start bad-commit good-commit @@ -219,15 +221,15 @@ examples how the AST represents each syntactic structure. How the RTL is compiled ======================= -The ``system`` module contains the part of the RTL which needs support by +The `system` module contains the part of the RTL which needs support by compiler magic (and the stuff that needs to be in it because the spec says so). The C code generator generates the C code for it, just like any other -module. However, calls to some procedures like ``addInt`` are inserted by -the CCG. Therefore the module ``magicsys`` contains a table (``compilerprocs``) -with all symbols that are marked as ``compilerproc``. ``compilerprocs`` are -needed by the code generator. A ``magic`` proc is not the same as a -``compilerproc``: A ``magic`` is a proc that needs compiler magic for its -semantic checking, a ``compilerproc`` is a proc that is used by the code +module. However, calls to some procedures like `addInt` are inserted by +the CCG. Therefore the module `magicsys` contains a table (`compilerprocs`) +with all symbols that are marked as `compilerproc`. `compilerprocs` are +needed by the code generator. A `magic` proc is not the same as a +`compilerproc`: A `magic` is a proc that needs compiler magic for its +semantic checking, a `compilerproc` is a proc that is used by the code generator. @@ -254,7 +256,7 @@ This solves the problem without having to special case the logic that fills the internal seqs which are affected by the pragmas. In fact, this describes how the AST should be stored in the database, -as a "shallow" tree. Let's assume we compile module ``m`` with the +as a "shallow" tree. Let's assume we compile module `m` with the following contents: .. code-block:: nim @@ -279,21 +281,21 @@ Conceptually this is the AST we store for the module: static: echo "static" -The symbol's ``ast`` field is loaded lazily, on demand. This is where most +The symbol's `ast` field is loaded lazily, on demand. This is where most savings come from, only the shallow outer AST is reconstructed immediately. -It is also important that the replay involves the ``import`` statement so +It is also important that the replay involves the `import` statement so that dependencies are resolved properly. Shared global compiletime state ------------------------------- -Nim allows ``.global, compiletime`` variables that can be filled by macro +Nim allows `.global, compiletime` variables that can be filled by macro invocations across different modules. This feature breaks modularity in a severe way. Plenty of different solutions have been proposed: -- Restrict the types of global compiletime variables to ``Set[T]`` or +- Restrict the types of global compiletime variables to `Set[T]` or similar unordered, only-growable collections so that we can track the module's write effects to these variables and reapply the changes in a different order. @@ -306,7 +308,7 @@ severe way. Plenty of different solutions have been proposed: Since we adopt the "replay the top level statements" idea, the natural solution to this problem is to emit pseudo top level statements that reflect the mutations done to the global variable. However, this is -MUCH harder than it sounds, for example ``squeaknim`` uses this +MUCH harder than it sounds, for example `squeaknim` uses this snippet: .. code-block:: nim @@ -314,12 +316,12 @@ snippet: "\t^self externalCallFailed\C!\C\C") stCode.add(st & "\C\t\"Generated by NimSqueak\"\C\t" & apicall) -We can "replay" ``stCode.add`` only if the values of ``st`` -and ``apicall`` are known. And even then a hash table's ``add`` with its +We can "replay" `stCode.add` only if the values of `st` +and `apicall` are known. And even then a hash table's `add` with its hashing mechanism is too hard to replay. -In practice, things are worse still, consider ``someGlobal[i][j].add arg``. -We only know the root is ``someGlobal`` but the concrete path to the data +In practice, things are worse still, consider `someGlobal[i][j].add arg`. +We only know the root is `someGlobal` but the concrete path to the data is unknown as is the value that is added. We could compute a "diff" between the global states and use that to compute a symbol patchset, but this is quite some work, expensive to do at runtime (it would need to run after @@ -342,7 +344,7 @@ an alien API and works with some existing Nimble packages, at least. On the other hand, in Nim's future I would like to replace the VM by native code. A diff algorithm wouldn't work for that. -Instead the native code would work with an API like ``put``, ``get``: +Instead the native code would work with an API like `put`, `get`: .. code-block:: nim @@ -350,7 +352,7 @@ Instead the native code would work with an API like ``put``, ``get``: proc cacheGet*(key: string): NimNode The API should embrace the AST diffing notion: See the -module ``macrocache`` for the final details. +module `macrocache` for the final details. @@ -382,9 +384,9 @@ too. Type converters fall into this category: if 1: echo "ugly, but should work" -If in the above example module ``B`` is re-compiled, but ``A`` is not then -``B`` needs to be aware of ``toBool`` even though ``toBool`` is not referenced -in ``B`` *explicitly*. +If in the above example module `B` is re-compiled, but `A` is not then +`B` needs to be aware of `toBool` even though `toBool` is not referenced +in `B` *explicitly*. Both the multi method and the type converter problems are solved by the AST replay implementation. @@ -395,7 +397,7 @@ Generics We cache generic instantiations and need to ensure this caching works well with the incremental compilation feature. Since the cache is -attached to the ``PSym`` datastructure, it should work without any +attached to the `PSym` datastructure, it should work without any special logic. @@ -405,22 +407,22 @@ Backend issues - Init procs must not be "forgotten" to be called. - Files must not be "forgotten" to be linked. - Method dispatchers are global. -- DLL loading via ``dlsym`` is global. +- DLL loading via `dlsym` is global. - Emulated thread vars are global. However the biggest problem is that dead code elimination breaks modularity! -To see why, consider this scenario: The module ``G`` (for example the huge +To see why, consider this scenario: The module `G` (for example the huge Gtk2 module...) is compiled with dead code elimination turned on. So none -of ``G``'s procs is generated at all. +of `G`'s procs is generated at all. -Then module ``B`` is compiled that requires ``G.P1``. Ok, no problem, -``G.P1`` is loaded from the symbol file and ``G.c`` now contains ``G.P1``. +Then module `B` is compiled that requires `G.P1`. Ok, no problem, +`G.P1` is loaded from the symbol file and `G.c` now contains `G.P1`. -Then module ``A`` (that depends on ``B`` and ``G``) is compiled and ``B`` -and ``G`` are left unchanged. ``A`` requires ``G.P2``. +Then module `A` (that depends on `B` and `G`) is compiled and `B` +and `G` are left unchanged. `A` requires `G.P2`. -So now ``G.c`` MUST contain both ``P1`` and ``P2``, but we haven't even -loaded ``P1`` from the symbol file, nor do we want to because we then quickly +So now `G.c` MUST contain both `P1` and `P2`, but we haven't even +loaded `P1` from the symbol file, nor do we want to because we then quickly would restore large parts of the whole program. @@ -428,7 +430,7 @@ Solution ~~~~~~~~ The backend must have some logic so that if the currently processed module -is from the compilation cache, the ``ast`` field is not accessed. Instead +is from the compilation cache, the `ast` field is not accessed. Instead the generated C(++) for the symbol's body needs to be cached too and inserted back into the produced C file. This approach seems to deal with all the outlined problems above. @@ -444,8 +446,8 @@ in mind: keeps allocating memory! Thus a stack overflow may happen, hiding the real issue. * What seem to be C code generation problems is often a bug resulting from - not producing prototypes, so that some types default to ``cint``. Testing - without the ``-w`` option helps! + not producing prototypes, so that some types default to `cint`. Testing + without the `-w` option helps! The Garbage Collector @@ -464,9 +466,9 @@ code generation. Each cell has a header consisting of a RC and a pointer to its type descriptor. However the program does not know about these, so they are placed at -negative offsets. In the GC code the type ``PCell`` denotes a pointer +negative offsets. In the GC code the type `PCell` denotes a pointer decremented by the right offset, so that the header can be accessed easily. It -is extremely important that ``pointer`` is not confused with a ``PCell`` +is extremely important that `pointer` is not confused with a `PCell` as this would lead to a memory corruption. @@ -474,9 +476,9 @@ The CellSet data structure -------------------------- The GC depends on an extremely efficient datastructure for storing a -set of pointers - this is called a ``TCellSet`` in the source code. +set of pointers - this is called a `TCellSet` in the source code. Inserting, deleting and searching are done in constant time. However, -modifying a ``TCellSet`` during traversal leads to undefined behaviour. +modifying a `TCellSet` during traversal leads to undefined behaviour. .. code-block:: Nim type @@ -559,11 +561,11 @@ Code generation for closures is implemented by `lambda lifting`:idx:. Design ------ -A ``closure`` proc var can call ordinary procs of the default Nim calling +A `closure` proc var can call ordinary procs of the default Nim calling convention. But not the other way round! A closure is implemented as a -``tuple[prc, env]``. ``env`` can be nil implying a call without a closure. -This means that a call through a closure generates an ``if`` but the -interoperability is worth the cost of the ``if``. Thunk generation would be +`tuple[prc, env]`. `env` can be nil implying a call without a closure. +This means that a call through a closure generates an `if` but the +interoperability is worth the cost of the `if`. Thunk generation would be possible too, but it's slightly more effort to implement. Tests with GCC on Amd64 showed that it's really beneficial if the @@ -579,7 +581,7 @@ A thunk would need to call 'returnsDefaultCC[i]' somehow and that would require an *additional* closure generation... Ok, not really, but it requires to pass the function to call. So we'd end up with 2 indirect calls instead of one. Another much more severe problem which this solution is that it's not GC-safe -to pass a proc pointer around via a generic ``ref`` type. +to pass a proc pointer around via a generic `ref` type. Example code: @@ -695,15 +697,15 @@ Accumulator Internals --------- -Lambda lifting is implemented as part of the ``transf`` pass. The ``transf`` +Lambda lifting is implemented as part of the `transf` pass. The `transf` pass generates code to setup the environment and to pass it around. However, this pass does not change the types! So we have some kind of mismatch here; on the one hand the proc expression becomes an explicit tuple, on the other hand the tyProc(ccClosure) type is not changed. For C code generation it's also -important the hidden formal param is ``void*`` and not something more +important the hidden formal param is `void*` and not something more specialized. However the more specialized env type needs to passed to the -backend somehow. We deal with this by modifying ``s.ast[paramPos]`` to contain -the formal hidden parameter, but not ``s.typ``! +backend somehow. We deal with this by modifying `s.ast[paramPos]` to contain +the formal hidden parameter, but not `s.typ`! Integer literals: diff --git a/doc/koch.rst b/doc/koch.rst index c92e29812..1eb02d785 100644 --- a/doc/koch.rst +++ b/doc/koch.rst @@ -1,3 +1,5 @@ +.. default-role:: code + =============================== Nim maintenance script =============================== @@ -17,7 +19,7 @@ Introduction The `koch`:idx: program is Nim's maintenance script. It is a replacement for make and shell scripting with the advantage that it is much more portable. -The word *koch* means *cook* in German. ``koch`` is used mainly to build the +The word *koch* means *cook* in German. `koch` is used mainly to build the Nim compiler, but it can also be used for other tasks. This document describes the supported commands and their options. @@ -39,8 +41,8 @@ options: Use the linenoise library for interactive mode (not needed on Windows). After compilation is finished you will hopefully end up with the nim -compiler in the ``bin`` directory. You can add Nim's ``bin`` directory to -your ``$PATH`` or use the install command to place it where it will be +compiler in the `bin` directory. You can add Nim's `bin` directory to +your `$PATH` or use the install command to place it where it will be found. csource command @@ -54,33 +56,33 @@ temp command ------------ The temp command builds the Nim compiler but with a different final name -(``nim_temp``), so it doesn't overwrite your normal compiler. You can use +(`nim_temp`), so it doesn't overwrite your normal compiler. You can use this command to test different options, the same you would issue for the `boot command <#commands-boot-command>`_. test command ------------ -The `test`:idx: command can also be invoked with the alias ``tests``. This -command will compile and run ``testament/tester.nim``, which is the main -driver of Nim's test suite. You can pass options to the ``test`` command, +The `test`:idx: command can also be invoked with the alias `tests`. This +command will compile and run `testament/tester.nim`, which is the main +driver of Nim's test suite. You can pass options to the `test` command, they will be forwarded to the tester. See its source code for available options. web command ----------- -The `web`:idx: command converts the documentation in the ``doc`` directory +The `web`:idx: command converts the documentation in the `doc` directory from rst to HTML. It also repeats the same operation but places the result in -the ``web/upload`` which can be used to update the website at +the `web/upload` which can be used to update the website at https://nim-lang.org. By default, the documentation will be built in parallel using the number of available CPU cores. If any documentation build sub-commands fail, they will be rerun in serial fashion so that meaningful error output can be gathered for -inspection. The ``--parallelBuild:n`` switch or configuration option can be +inspection. The `--parallelBuild:n` switch or configuration option can be used to force a specific number of parallel jobs or run everything serially -from the start (``n == 1``). +from the start (`n == 1`). pdf command ----------- diff --git a/doc/lib.rst b/doc/lib.rst index 2a4c2c03c..3202c5a53 100644 --- a/doc/lib.rst +++ b/doc/lib.rst @@ -1,3 +1,5 @@ +.. default-role:: code + ==================== Nim Standard Library ==================== @@ -9,7 +11,7 @@ Nim Standard Library Nim's library is divided into *pure libraries*, *impure libraries*, and *wrappers*. -Pure libraries do not depend on any external ``*.dll`` or ``lib*.so`` binary +Pure libraries do not depend on any external `*.dll` or `lib*.so` binary while impure libraries do. A wrapper is an impure library that is a very low-level interface to a C library. @@ -37,11 +39,11 @@ Automatic imports * `threads `_ Basic Nim thread support. **Note:** This is part of the system module. Do not - import it explicitly. Enabled with ``--threads:on``. + import it explicitly. Enabled with `--threads:on`. * `channels `_ Nim message passing support for threads. **Note:** This is part of the - system module. Do not import it explicitly. Enabled with ``--threads:on``. + system module. Do not import it explicitly. Enabled with `--threads:on`. Core @@ -86,14 +88,14 @@ Algorithms This module implements some common generic algorithms like sort or binary search. * `std/enumutils `_ - This module adds functionality for the built-in ``enum`` type. + This module adds functionality for the built-in `enum` type. * `sequtils `_ - This module implements operations for the built-in ``seq`` type + This module implements operations for the built-in `seq` type which were inspired by functional programming languages. * `std/setutils `_ - This module adds functionality for the built-in ``set`` type. + This module adds functionality for the built-in `set` type. Collections @@ -105,7 +107,7 @@ Collections * `deques `_ Implementation of a double-ended queue. - The underlying implementation uses a ``seq``. + The underlying implementation uses a `seq`. * `heapqueue `_ Implementation of a heap data structure that can be used as a priority queue. @@ -140,7 +142,7 @@ String handling --------------- * `cstrutils `_ - Utilities for ``cstring`` handling. + Utilities for `cstring` handling. * `std/editdistance `_ This module contains an algorithm to compute the edit distance between two @@ -148,7 +150,7 @@ String handling * `encodings `_ Converts between different character encodings. On UNIX, this uses - the ``iconv`` library, on Windows the Windows API. + the `iconv` library, on Windows the Windows API. * `parseutils `_ This module contains helpers for parsing tokens, numbers, identifiers, etc. @@ -166,17 +168,17 @@ String handling * `strformat `_ Macro based standard string interpolation/formatting. Inspired by - Python's ``f``-strings. + Python's `f`-strings. * `strmisc `_ This module contains uncommon string handling operations that do not fit with the commonly used operations in strutils. * `strscans `_ - This module contains a ``scanf`` macro for convenient parsing of mini languages. + This module contains a `scanf` macro for convenient parsing of mini languages. * `strtabs `_ - The ``strtabs`` module implements an efficient hash table that is a mapping + The `strtabs` module implements an efficient hash table that is a mapping from strings to strings. Supports a case-sensitive, case-insensitive and style-insensitive modes. @@ -200,10 +202,10 @@ Time handling ------------- * `std/monotimes `_ - The ``monotimes`` module implements monotonic timestamps. + The `monotimes` module implements monotonic timestamps. * `times `_ - The ``times`` module contains support for working with time. + The `times` module contains support for working with time. Generic Operating System Services @@ -225,7 +227,7 @@ Generic Operating System Services data structures. * `memfiles `_ - This module provides support for memory-mapped files (Posix's ``mmap``) + This module provides support for memory-mapped files (Posix's `mmap`) on the different operating systems. * `os `_ @@ -234,12 +236,12 @@ Generic Operating System Services commands, etc. * `osproc `_ - Module for process communication beyond ``os.execShellCmd``. + Module for process communication beyond `os.execShellCmd`. * `streams `_ This module provides a stream interface and two implementations thereof: - the ``FileStream`` and the ``StringStream`` which implement the stream - interface for Nim file objects (``File``) and strings. Other modules + the `FileStream` and the `StringStream` which implement the stream + interface for Nim file objects (`File`) and strings. Other modules may provide other implementations for this standard stream interface. * `terminal `_ @@ -288,22 +290,22 @@ Internet Protocols and Support * `asyncfile `_ This module implements asynchronous file reading and writing using - ``asyncdispatch``. + `asyncdispatch`. * `asyncftpclient `_ - This module implements an asynchronous FTP client using the ``asyncnet`` + This module implements an asynchronous FTP client using the `asyncnet` module. * `asynchttpserver `_ - This module implements an asynchronous HTTP server using the ``asyncnet`` + This module implements an asynchronous HTTP server using the `asyncnet` module. * `asyncnet `_ - This module implements asynchronous sockets based on the ``asyncdispatch`` + This module implements asynchronous sockets based on the `asyncdispatch` module. * `asyncstreams `_ - This module provides ``FutureStream`` - a future that acts as a queue. + This module provides `FutureStream` - a future that acts as a queue. * `cgi `_ This module implements helpers for CGI applications. @@ -323,7 +325,7 @@ Internet Protocols and Support * `net `_ This module implements a high-level sockets API. It replaces the - ``sockets`` module. + `sockets` module. * `selectors `_ This module implements a selector API with backends specific to each OS. @@ -357,23 +359,23 @@ Parsers scheme for lexers and parsers. This is used by the diverse parsing modules. * `parsecfg `_ - The ``parsecfg`` module implements a high-performance configuration file - parser. The configuration file's syntax is similar to the Windows ``.ini`` + The `parsecfg` module implements a high-performance configuration file + parser. The configuration file's syntax is similar to the Windows `.ini` format, but much more powerful, as it is not a line based parser. String literals, raw string literals, and triple quote string literals are supported as in the Nim programming language. * `parsecsv `_ - The ``parsecsv`` module implements a simple high-performance CSV parser. + The `parsecsv` module implements a simple high-performance CSV parser. * `parseopt `_ - The ``parseopt`` module implements a command line option parser. + The `parseopt` module implements a command line option parser. * `parsesql `_ - The ``parsesql`` module implements a simple high-performance SQL parser. + The `parsesql` module implements a simple high-performance SQL parser. * `parsexml `_ - The ``parsexml`` module implements a simple high performance XML/HTML parser. + The `parsexml` module implements a simple high performance XML/HTML parser. The only encoding that is supported is UTF-8. The parser has been designed to be somewhat error-correcting, so that even some "wild HTML" found on the web can be parsed with it. @@ -458,7 +460,7 @@ Miscellaneous This module implements a simple logger. * `segfaults `_ - Turns access violations or segfaults into a ``NilAccessDefect`` exception. + Turns access violations or segfaults into a `NilAccessDefect` exception. * `sugar `_ This module implements nice syntactic sugar based on Nim's macro system. @@ -480,11 +482,11 @@ Modules for JS backend Declaration of the Document Object Model for the JS backend. * `jsconsole `_ - Wrapper for the ``console`` object. + Wrapper for the `console` object. * `jscore `_ The wrapper of core JavaScript functions. For most purposes, you should be using - the ``math``, ``json``, and ``times`` stdlib modules instead of this module. + the `math`, `json`, and `times` stdlib modules instead of this module. * `jsffi `_ Types and macros for easier interaction with JavaScript. diff --git a/doc/manual/var_t_return.rst b/doc/manual/var_t_return.rst index c6de8cf7c..e34993e3e 100644 --- a/doc/manual/var_t_return.rst +++ b/doc/manual/var_t_return.rst @@ -1,6 +1,8 @@ -Memory safety for returning by ``var T`` is ensured by a simple borrowing -rule: If ``result`` does not refer to a location pointing to the heap -(that is in ``result = X`` the ``X`` involves a ``ptr`` or ``ref`` access) +.. default-role:: code + +Memory safety for returning by `var T` is ensured by a simple borrowing +rule: If `result` does not refer to a location pointing to the heap +(that is in `result = X` the `X` involves a `ptr` or `ref` access) then it has to be derived from the routine's first parameter: .. code-block:: nim @@ -11,10 +13,10 @@ then it has to be derived from the routine's first parameter: var x: int # we know 'forward' provides a view into the location derived from # its first argument 'x'. - result = forward(x) # Error: location is derived from ``x`` + result = forward(x) # Error: location is derived from `x` # which is not p's first parameter and lives # on the stack. -In other words, the lifetime of what ``result`` points to is attached to the +In other words, the lifetime of what `result` points to is attached to the lifetime of the first parameter and that is enough knowledge to verify memory safety at the call site. diff --git a/doc/manual_experimental.rst b/doc/manual_experimental.rst index b76839842..cf2e0c247 100644 --- a/doc/manual_experimental.rst +++ b/doc/manual_experimental.rst @@ -1,3 +1,5 @@ +.. default-role:: code + ========================= Nim Experimental Features ========================= @@ -12,8 +14,8 @@ About this document =================== This document describes features of Nim that are to be considered experimental. -Some of these are not covered by the ``.experimental`` pragma or -``--experimental`` switch because they are already behind a special syntax and +Some of these are not covered by the `.experimental` pragma or +`--experimental` switch because they are already behind a special syntax and one may want to use Nim libraries using these features without using them oneself. @@ -28,13 +30,13 @@ Every Nim module resides in a (nimble) package. An object type can be attached to the package it resides in. If that is done, the type can be referenced from other modules as an `incomplete`:idx: object type. This feature allows to break up recursive type dependencies across module boundaries. Incomplete -object types are always passed ``byref`` and can only be used in pointer like -contexts (``var/ref/ptr IncompleteObject``) in general since the compiler does +object types are always passed `byref` and can only be used in pointer like +contexts (`var/ref/ptr IncompleteObject`) in general since the compiler does not yet know the size of the object. To complete an incomplete object -the ``package`` pragma has to be used. ``package`` implies ``byref``. +the `package` pragma has to be used. `package` implies `byref`. -As long as a type ``T`` is incomplete, neither ``sizeof(T)`` nor runtime -type information for ``T`` is available. +As long as a type `T` is incomplete, neither `sizeof(T)` nor runtime +type information for `T` is available. Example: @@ -63,8 +65,8 @@ Example: Void type ========= -The ``void`` type denotes the absence of any type. Parameters of -type ``void`` are treated as non-existent, ``void`` as a return type means that +The `void` type denotes the absence of any type. Parameters of +type `void` are treated as non-existent, `void` as a return type means that the procedure does not return a value: .. code-block:: nim @@ -73,7 +75,7 @@ the procedure does not return a value: nothing() # writes "ha" to stdout -The ``void`` type is particularly useful for generic code: +The `void` type is particularly useful for generic code: .. code-block:: nim proc callProc[T](p: proc (x: T), x: T) = @@ -88,7 +90,7 @@ The ``void`` type is particularly useful for generic code: callProc[int](intProc, 12) callProc[void](emptyProc) -However, a ``void`` type cannot be inferred in generic code: +However, a `void` type cannot be inferred in generic code: .. code-block:: nim callProc(emptyProc) @@ -96,8 +98,8 @@ However, a ``void`` type cannot be inferred in generic code: # but expected one of: # callProc(p: proc (T), x: T) -The ``void`` type is only valid for parameters and return types; other symbols -cannot have the type ``void``. +The `void` type is only valid for parameters and return types; other symbols +cannot have the type `void`. @@ -105,19 +107,19 @@ Covariance ========== Covariance in Nim can be introduced only through pointer-like types such -as ``ptr`` and ``ref``. Sequence, Array and OpenArray types, instantiated +as `ptr` and `ref`. Sequence, Array and OpenArray types, instantiated with pointer-like types will be considered covariant if and only if they -are also immutable. The introduction of a ``var`` modifier or additional -``ptr`` or ``ref`` indirections would result in invariant treatment of +are also immutable. The introduction of a `var` modifier or additional +`ptr` or `ref` indirections would result in invariant treatment of these types. -``proc`` types are currently always invariant, but future versions of Nim +`proc` types are currently always invariant, but future versions of Nim may relax this rule. User-defined generic types may also be covariant with respect to some of their parameters. By default, all generic params are considered invariant, -but you may choose the apply the prefix modifier ``in`` to a parameter to -make it contravariant or ``out`` to make it covariant: +but you may choose the apply the prefix modifier `in` to a parameter to +make it contravariant or `out` to make it covariant: .. code-block:: nim type @@ -166,10 +168,10 @@ values: # to point to a ComboBox On the other hand, in the `RingBuffer` example above, the designated generic -param is used to instantiate the non-pointer ``seq`` type, which means that +param is used to instantiate the non-pointer `seq` type, which means that the resulting generic type will have covariance that mimics an array or -sequence (i.e. it will be covariant only when instantiated with ``ptr`` and -``ref`` types): +sequence (i.e. it will be covariant only when instantiated with `ptr` and +`ref` types): .. code-block:: nim @@ -196,7 +198,7 @@ as `seq[AnnotatedPtr[T]]` or `RingBuffer[AnnotatedPtr[T]]` will also be considered covariant and you can create new pointer-like types by instantiating other user-defined pointer-like types. -The contravariant parameters introduced with the ``in`` modifier are currently +The contravariant parameters introduced with the `in` modifier are currently useful only when interfacing with imported types having such semantics. @@ -204,7 +206,7 @@ Automatic dereferencing ======================= Automatic dereferencing is performed for the first argument of a routine call. -This feature has to be enabled via ``{.experimental: "implicitDeref".}``: +This feature has to be enabled via `{.experimental: "implicitDeref".}`: .. code-block:: nim {.experimental: "implicitDeref".} @@ -257,7 +259,7 @@ preface definitions inside a module. Please note that if a callable symbol is never used in this scenario, its body will never be compiled. This is the default behavior leading to best compilation times, but if exhaustive compilation of all definitions is - required, using ``nim check`` provides this option as well. + required, using `nim check` provides this option as well. Example: @@ -290,10 +292,10 @@ what code is executed at the top level: .. TODO: Let's table this for now. This is an *experimental feature* and so the - specific manner in which ``declared`` operates with it can be decided in + specific manner in which `declared` operates with it can be decided in eventuality, because right now it works a bit weirdly. - The values of expressions involving ``declared`` are decided *before* the + The values of expressions involving `declared` are decided *before* the code reordering process, and not after. As an example, the output of this code is the same as it would be with code reordering disabled. @@ -325,7 +327,7 @@ Named argument overloading ========================== Routines with the same type signature can be called differently if a parameter -has different names. This does not need an ``experimental`` switch, but is an +has different names. This does not need an `experimental` switch, but is an unstable feature. .. code-block::nim @@ -344,7 +346,7 @@ Do notation =========== As a special more convenient notation, proc expressions involved in procedure -calls can use the ``do`` keyword: +calls can use the `do` keyword: .. code-block:: nim sort(cities) do (x,y: string) -> int: @@ -359,13 +361,13 @@ calls can use the ``do`` keyword: if not `ex`: echo `info`, ": Check failed: ", `expString` -``do`` is written after the parentheses enclosing the regular proc params. +`do` is written after the parentheses enclosing the regular proc params. The proc expression represented by the do block is appended to them. In calls using the command syntax, the do block will bind to the immediately preceding expression, transforming it in a call. -``do`` with parentheses is an anonymous ``proc``; however a ``do`` without -parentheses is just a block of code. The ``do`` notation can be used to +`do` with parentheses is an anonymous `proc`; however a `do` without +parentheses is just a block of code. The `do` notation can be used to pass multiple blocks to a macro: .. code-block:: nim @@ -385,7 +387,7 @@ dot operators ------------- **Note**: Dot operators are still experimental and so need to be enabled -via ``{.experimental: "dotOperators".}``. +via `{.experimental: "dotOperators".}`. Nim offers a special family of dot operators that can be used to intercept and rewrite proc call and field access attempts, referring @@ -398,7 +400,7 @@ When Nim encounters an expression that cannot be resolved by the standard overload resolution rules, the current scope will be searched for a dot operator that can be matched against a re-written form of the expression, where the unknown field or proc name is passed to -an ``untyped`` parameter: +an `untyped` parameter: .. code-block:: nim a.b # becomes `.`(a, b) @@ -471,10 +473,10 @@ Not nil annotation ================== **Note:** This is an experimental feature. It can be enabled with -``{.experimental: "notnil"}``. +`{.experimental: "notnil"}`. -All types for which ``nil`` is a valid value can be annotated with the ``not -nil`` annotation to exclude ``nil`` as a valid value: +All types for which `nil` is a valid value can be annotated with the `not +nil` annotation to exclude `nil` as a valid value: .. code-block:: nim {.experimental: "notnil"} @@ -526,9 +528,9 @@ The concept is a match if: a) all of the expressions within the body can be compiled for the tested type b) all statically evaluable boolean expressions in the body must be true -The identifiers following the ``concept`` keyword represent instances of the +The identifiers following the `concept` keyword represent instances of the currently matched type. You can apply any of the standard type modifiers such -as ``var``, ``ref``, ``ptr`` and ``static`` to denote a more specific type of +as `var`, `ref`, `ptr` and `static` to denote a more specific type of instance. You can also apply the `type` modifier to create a named instance of the type itself: @@ -546,9 +548,9 @@ the presence of callable symbols with specific signatures: OutputStream = concept var s s.write(string) -In order to check for symbols accepting ``type`` params, you must prefix -the type with the explicit ``type`` modifier. The named instance of the -type, following the ``concept`` keyword is also considered to have the +In order to check for symbols accepting `type` params, you must prefix +the type with the explicit `type` modifier. The named instance of the +type, following the `concept` keyword is also considered to have the explicit modifier and will be matched only as a type. .. code-block:: nim @@ -570,7 +572,7 @@ explicit modifier and will be matched only as a type. -x is T x - y is T -Please note that the ``is`` operator allows one to easily verify the precise +Please note that the `is` operator allows one to easily verify the precise type signatures of the required operations, but since type inference and default parameters are still applied in the concept body, it's also possible to describe usage protocols that do not reveal implementation details. @@ -586,7 +588,7 @@ By default, the compiler will report the matching errors in concepts only when no other overload can be selected and a normal compilation error is produced. When you need to understand why the compiler is not matching a particular concept and, as a result, a wrong overload is selected, you can apply the -``explain`` pragma to either the concept body or a particular call-site. +`explain` pragma to either the concept body or a particular call-site. .. code-block:: nim type @@ -670,7 +672,7 @@ resembles the way generic parameters of callable symbols are inferred on call sites. Unbound types can appear both as params to calls such as `s.push(T)` and -on the right-hand side of the ``is`` operator in cases such as `x.pop is T` +on the right-hand side of the `is` operator in cases such as `x.pop is T` and `x.data is seq[T]`. Unbound static params will be inferred from expressions involving the `==` @@ -684,8 +686,8 @@ operator and also when types dependent on them are being matched: The Nim compiler includes a simple linear equation solver, allowing it to infer static params in some situations where integer arithmetic is involved. -Just like in regular type classes, Nim discriminates between ``bind once`` -and ``bind many`` types when matching the concept. You can add the ``distinct`` +Just like in regular type classes, Nim discriminates between `bind once` +and `bind many` types when matching the concept. You can add the `distinct` modifier to any of the otherwise inferable types to get a type that will be matched without permanently inferring it. This may be useful when you need to match several procs accepting the same wide class of types: @@ -708,7 +710,7 @@ to match several procs accepting the same wide class of types: type Enum = distinct Enumerable o.baz is Enum -On the other hand, using ``bind once`` types allows you to test for equivalent +On the other hand, using `bind once` types allows you to test for equivalent types used in multiple signatures, without actually requiring any concrete types, thus allowing you to encode implementation-defined types: @@ -804,7 +806,7 @@ concept, we say that the outer concept is a refinement of the inner concept and thus it is more-specific. When both concepts are matched in a call during overload resolution, Nim will assign a higher precedence to the most specific one. As an alternative way of defining concept refinements, you can use the -object inheritance syntax involving the ``of`` keyword: +object inheritance syntax involving the `of` keyword: .. code-block:: nim type @@ -895,8 +897,8 @@ object inheritance syntax involving the ``of`` keyword: any type can implement an unlimited number of protocols or interfaces not originally envisioned by the type's author. - Any concept type can be turned into a VTable type by using the ``vtref`` - or the ``vtptr`` compiler magics. Under the hood, these magics generate + Any concept type can be turned into a VTable type by using the `vtref` + or the `vtptr` compiler magics. Under the hood, these magics generate a converter type class, which converts the regular instances of the matching types to the corresponding VTable type. @@ -928,8 +930,8 @@ object inheritance syntax involving the ``of`` keyword: but it will include a smaller number of captured procs. A completely empty vtable will be reported as an error. - The ``vtref`` magic produces types which can be bound to ``ref`` types and - the ``vtptr`` magic produced types bound to ``ptr`` types. + The `vtref` magic produces types which can be bound to `ref` types and + the `vtptr` magic produced types bound to `ptr` types. Type bound operations @@ -944,12 +946,12 @@ There are 4 operations that are bound to a type: These operations can be *overridden* instead of *overloaded*. This means the implementation is automatically lifted to structured types. For instance if type -``T`` has an overridden assignment operator ``=`` this operator is also used -for assignments of the type ``seq[T]``. Since these operations are bound to a +`T` has an overridden assignment operator `=` this operator is also used +for assignments of the type `seq[T]`. Since these operations are bound to a type they have to be bound to a nominal type for reasons of simplicity of -implementation: This means an overridden ``deepCopy`` for ``ref T`` is really -bound to ``T`` and not to ``ref T``. This also means that one cannot override -``deepCopy`` for both ``ptr T`` and ``ref T`` at the same time; instead a +implementation: This means an overridden `deepCopy` for `ref T` is really +bound to `T` and not to `ref T`. This also means that one cannot override +`deepCopy` for both `ptr T` and `ref T` at the same time; instead a helper distinct or object type has to be used for one pointer type. Assignments, moves and destruction are specified in @@ -959,9 +961,9 @@ the `destructors `_ document. deepCopy -------- -``=deepCopy`` is a builtin that is invoked whenever data is passed to -a ``spawn``'ed proc to ensure memory safety. The programmer can override its -behaviour for a specific ``ref`` or ``ptr`` type ``T``. (Later versions of the +`=deepCopy` is a builtin that is invoked whenever data is passed to +a `spawn`'ed proc to ensure memory safety. The programmer can override its +behaviour for a specific `ref` or `ptr` type `T`. (Later versions of the language may weaken this restriction.) The signature has to be: @@ -972,7 +974,7 @@ The signature has to be: This mechanism will be used by most data structures that support shared memory like channels to implement thread safe automatic memory management. -The builtin ``deepCopy`` can even clone closures and their environments. See +The builtin `deepCopy` can even clone closures and their environments. See the documentation of `spawn <#parallel-amp-spawn-spawn-statement>`_ for details. @@ -982,7 +984,7 @@ Case statement macros Macros named `case` can rewrite `case` statements for certain types in order to implement `pattern matching`:idx:. The following example implements a simplistic form of pattern matching for tuples, leveraging the existing -equality operator for tuples (as provided in ``system.==``): +equality operator for tuples (as provided in `system.==`): .. code-block:: nim :test: "nim c $1" @@ -1013,7 +1015,7 @@ equality operator for tuples (as provided in ``system.==``): Currently case statement macros must be enabled explicitly -via ``{.experimental: "caseStmtMacros".}``. +via `{.experimental: "caseStmtMacros".}`. `case` macros are subject to overload resolution. The type of the `case` statement's selector expression is matched against the type @@ -1039,10 +1041,10 @@ compilation pipeline with user defined optimizations: let x = 3 echo x * 2 -The compiler now rewrites ``x * 2`` as ``x + x``. The code inside the -curlies is the pattern to match against. The operators ``*``, ``**``, -``|``, ``~`` have a special meaning in patterns if they are written in infix -notation, so to match verbatim against ``*`` the ordinary function call syntax +The compiler now rewrites `x * 2` as `x + x`. The code inside the +curlies is the pattern to match against. The operators `*`, `**`, +`|`, `~` have a special meaning in patterns if they are written in infix +notation, so to match verbatim against `*` the ordinary function call syntax needs to be used. Term rewriting macro are applied recursively, up to a limit. This means that @@ -1080,7 +1082,7 @@ You can make one overload matching with a constraint and one without, and the one with a constraint will have precedence, and so you can handle both cases differently. -So what about ``2 * a``? We should tell the compiler ``*`` is commutative. We +So what about `2 * a`? We should tell the compiler `*` is commutative. We cannot really do that however as the following code only swaps arguments blindly: @@ -1092,59 +1094,59 @@ What optimizers really need to do is a *canonicalization*: .. code-block:: nim template canonMul{`*`(a, b)}(a: int{lit}, b: int): int = b*a -The ``int{lit}`` parameter pattern matches against an expression of -type ``int``, but only if it's a literal. +The `int{lit}` parameter pattern matches against an expression of +type `int`, but only if it's a literal. Parameter constraints --------------------- -The `parameter constraint`:idx: expression can use the operators ``|`` (or), -``&`` (and) and ``~`` (not) and the following predicates: +The `parameter constraint`:idx: expression can use the operators `|` (or), +`&` (and) and `~` (not) and the following predicates: =================== ===================================================== Predicate Meaning =================== ===================================================== -``atom`` The matching node has no children. -``lit`` The matching node is a literal like "abc", 12. -``sym`` The matching node must be a symbol (a bound +`atom` The matching node has no children. +`lit` The matching node is a literal like "abc", 12. +`sym` The matching node must be a symbol (a bound identifier). -``ident`` The matching node must be an identifier (an unbound +`ident` The matching node must be an identifier (an unbound identifier). -``call`` The matching AST must be a call/apply expression. -``lvalue`` The matching AST must be an lvalue. -``sideeffect`` The matching AST must have a side effect. -``nosideeffect`` The matching AST must have no side effect. -``param`` A symbol which is a parameter. -``genericparam`` A symbol which is a generic parameter. -``module`` A symbol which is a module. -``type`` A symbol which is a type. -``var`` A symbol which is a variable. -``let`` A symbol which is a ``let`` variable. -``const`` A symbol which is a constant. -``result`` The special ``result`` variable. -``proc`` A symbol which is a proc. -``method`` A symbol which is a method. -``iterator`` A symbol which is an iterator. -``converter`` A symbol which is a converter. -``macro`` A symbol which is a macro. -``template`` A symbol which is a template. -``field`` A symbol which is a field in a tuple or an object. -``enumfield`` A symbol which is a field in an enumeration. -``forvar`` A for loop variable. -``label`` A label (used in ``block`` statements). -``nk*`` The matching AST must have the specified kind. - (Example: ``nkIfStmt`` denotes an ``if`` statement.) -``alias`` States that the marked parameter needs to alias +`call` The matching AST must be a call/apply expression. +`lvalue` The matching AST must be an lvalue. +`sideeffect` The matching AST must have a side effect. +`nosideeffect` The matching AST must have no side effect. +`param` A symbol which is a parameter. +`genericparam` A symbol which is a generic parameter. +`module` A symbol which is a module. +`type` A symbol which is a type. +`var` A symbol which is a variable. +`let` A symbol which is a `let` variable. +`const` A symbol which is a constant. +`result` The special `result` variable. +`proc` A symbol which is a proc. +`method` A symbol which is a method. +`iterator` A symbol which is an iterator. +`converter` A symbol which is a converter. +`macro` A symbol which is a macro. +`template` A symbol which is a template. +`field` A symbol which is a field in a tuple or an object. +`enumfield` A symbol which is a field in an enumeration. +`forvar` A for loop variable. +`label` A label (used in `block` statements). +`nk*` The matching AST must have the specified kind. + (Example: `nkIfStmt` denotes an `if` statement.) +`alias` States that the marked parameter needs to alias with *some* other parameter. -``noalias`` States that *every* other parameter must not alias +`noalias` States that *every* other parameter must not alias with the marked parameter. =================== ===================================================== Predicates that share their name with a keyword have to be escaped with backticks. -The ``alias`` and ``noalias`` predicates refer not only to the matching AST, +The `alias` and `noalias` predicates refer not only to the matching AST, but also to every other bound parameter; syntactically they need to occur after the ordinary AST predicates: @@ -1158,14 +1160,14 @@ the ordinary AST predicates: Pattern operators ----------------- -The operators ``*``, ``**``, ``|``, ``~`` have a special meaning in patterns +The operators `*`, `**`, `|`, `~` have a special meaning in patterns if they are written in infix notation. -The ``|`` operator +The `|` operator ~~~~~~~~~~~~~~~~~~ -The ``|`` operator if used as infix operator creates an ordered choice: +The `|` operator if used as infix operator creates an ordered choice: .. code-block:: nim template t{0|1}(): untyped = 3 @@ -1182,15 +1184,15 @@ constant folding, so the following does not work: echo 1 The reason is that the compiler already transformed the 1 into "1" for -the ``echo`` statement. However, a term rewriting macro should not change the -semantics anyway. In fact they can be deactivated with the ``--patterns:off`` -command line option or temporarily with the ``patterns`` pragma. +the `echo` statement. However, a term rewriting macro should not change the +semantics anyway. In fact they can be deactivated with the `--patterns:off` +command line option or temporarily with the `patterns` pragma. -The ``{}`` operator +The `{}` operator ~~~~~~~~~~~~~~~~~~~ -A pattern expression can be bound to a pattern parameter via the ``expr{param}`` +A pattern expression can be bound to a pattern parameter via the `expr{param}` notation: .. code-block:: nim @@ -1200,10 +1202,10 @@ notation: echo a -The ``~`` operator +The `~` operator ~~~~~~~~~~~~~~~~~~ -The ``~`` operator is the **not** operator in patterns: +The `~` operator is the **not** operator in patterns: .. code-block:: nim template t{x = (~x){y} and (~x){z}}(x, y, z: bool) = @@ -1218,11 +1220,11 @@ The ``~`` operator is the **not** operator in patterns: echo a -The ``*`` operator +The `*` operator ~~~~~~~~~~~~~~~~~~ -The ``*`` operator can *flatten* a nested binary expression like ``a & b & c`` -to ``&(a, b, c)``: +The `*` operator can *flatten* a nested binary expression like `a & b & c` +to `&(a, b, c)`: .. code-block:: nim var @@ -1243,19 +1245,19 @@ to ``&(a, b, c)``: The second operator of `*` must be a parameter; it is used to gather all the -arguments. The expression ``"my" && (space & "awe" && "some " ) && "concat"`` -is passed to ``optConc`` in ``a`` as a special list (of kind ``nkArgList``) -which is flattened into a call expression; thus the invocation of ``optConc`` +arguments. The expression `"my" && (space & "awe" && "some " ) && "concat"` +is passed to `optConc` in `a` as a special list (of kind `nkArgList`) +which is flattened into a call expression; thus the invocation of `optConc` produces: .. code-block:: nim `&&`("my", space & "awe", "some ", "concat") -The ``**`` operator +The `**` operator ~~~~~~~~~~~~~~~~~~~ -The ``**`` is much like the ``*`` operator, except that it gathers not only +The `**` is much like the `*` operator, except that it gathers not only all the arguments, but also the matched operators in reverse polish notation: .. code-block:: nim @@ -1280,8 +1282,8 @@ all the arguments, but also the matched operators in reverse polish notation: echo x + y * z - x -This passes the expression ``x + y * z - x`` to the ``optM`` macro as -an ``nnkArglist`` node containing:: +This passes the expression `x + y * z - x` to the `optM` macro as +an `nnkArglist` node containing:: Arglist Sym "x" @@ -1292,14 +1294,14 @@ an ``nnkArglist`` node containing:: Sym "x" Sym "-" -(Which is the reverse polish notation of ``x + y * z - x``.) +(Which is the reverse polish notation of `x + y * z - x`.) Parameters ---------- Parameters in a pattern are type checked in the matching process. If a -parameter is of the type ``varargs`` it is treated specially and it can match +parameter is of the type `varargs` it is treated specially and it can match 0 or more arguments in the AST to be matched against: .. code-block:: nim @@ -1341,9 +1343,9 @@ The following example shows how some form of hoisting can be implemented: echo match("(a b c)", peg"'(' @ ')'") echo match("W_HI_Le", peg"\y 'while'") -The ``optPeg`` template optimizes the case of a peg constructor with a string +The `optPeg` template optimizes the case of a peg constructor with a string literal, so that the pattern will only be parsed once at program startup and -stored in a global ``gl`` which is then re-used. This optimization is called +stored in a global `gl` which is then re-used. This optimization is called hoisting because it is comparable to classical loop hoisting. @@ -1369,7 +1371,7 @@ constraints affect ordinary overloading resolution then: optLit(constant) optLit(variable) -However, the constraints ``alias`` and ``noalias`` are not available in +However, the constraints `alias` and `noalias` are not available in ordinary routines. @@ -1377,26 +1379,26 @@ Parallel & Spawn ================ Nim has two flavors of parallelism: -1) `Structured`:idx: parallelism via the ``parallel`` statement. -2) `Unstructured`:idx: parallelism via the standalone ``spawn`` statement. +1) `Structured`:idx: parallelism via the `parallel` statement. +2) `Unstructured`:idx: parallelism via the standalone `spawn` statement. Nim has a builtin thread pool that can be used for CPU intensive tasks. For -IO intensive tasks the ``async`` and ``await`` features should be +IO intensive tasks the `async` and `await` features should be used instead. Both parallel and spawn need the `threadpool `_ module to work. -Somewhat confusingly, ``spawn`` is also used in the ``parallel`` statement -with slightly different semantics. ``spawn`` always takes a call expression of -the form ``f(a, ...)``. Let ``T`` be ``f``'s return type. If ``T`` is ``void`` -then ``spawn``'s return type is also ``void`` otherwise it is ``FlowVar[T]``. +Somewhat confusingly, `spawn` is also used in the `parallel` statement +with slightly different semantics. `spawn` always takes a call expression of +the form `f(a, ...)`. Let `T` be `f`'s return type. If `T` is `void` +then `spawn`'s return type is also `void` otherwise it is `FlowVar[T]`. -Within a ``parallel`` section sometimes the ``FlowVar[T]`` is eliminated -to ``T``. This happens when ``T`` does not contain any GC'ed memory. -The compiler can ensure the location in ``location = spawn f(...)`` is not -read prematurely within a ``parallel`` section and so there is no need for -the overhead of an indirection via ``FlowVar[T]`` to ensure correctness. +Within a `parallel` section sometimes the `FlowVar[T]` is eliminated +to `T`. This happens when `T` does not contain any GC'ed memory. +The compiler can ensure the location in `location = spawn f(...)` is not +read prematurely within a `parallel` section and so there is no need for +the overhead of an indirection via `FlowVar[T]` to ensure correctness. -**Note**: Currently exceptions are not propagated between ``spawn``'ed tasks! +**Note**: Currently exceptions are not propagated between `spawn`'ed tasks! Spawn statement @@ -1415,25 +1417,25 @@ Spawn statement sync() For reasons of type safety and implementation simplicity the expression -that ``spawn`` takes is restricted: +that `spawn` takes is restricted: -* It must be a call expression ``f(a, ...)``. -* ``f`` must be ``gcsafe``. -* ``f`` must not have the calling convention ``closure``. -* ``f``'s parameters may not be of type ``var``. - This means one has to use raw ``ptr``'s for data passing reminding the +* It must be a call expression `f(a, ...)`. +* `f` must be `gcsafe`. +* `f` must not have the calling convention `closure`. +* `f`'s parameters may not be of type `var`. + This means one has to use raw `ptr`'s for data passing reminding the programmer to be careful. -* ``ref`` parameters are deeply copied which is a subtle semantic change and +* `ref` parameters are deeply copied which is a subtle semantic change and can cause performance problems but ensures memory safety. This deep copy - is performed via ``system.deepCopy`` and so can be overridden. -* For *safe* data exchange between ``f`` and the caller a global ``TChannel`` + is performed via `system.deepCopy` and so can be overridden. +* For *safe* data exchange between `f` and the caller a global `TChannel` needs to be used. However, since spawn can return a result, often no further communication is required. -``spawn`` executes the passed expression on the thread pool and returns -a `data flow variable`:idx: ``FlowVar[T]`` that can be read from. The reading -with the ``^`` operator is **blocking**. However, one can use ``blockUntilAny`` to +`spawn` executes the passed expression on the thread pool and returns +a `data flow variable`:idx: `FlowVar[T]` that can be read from. The reading +with the `^` operator is **blocking**. However, one can use `blockUntilAny` to wait on multiple flow variables at the same time: .. code-block:: nim @@ -1450,8 +1452,8 @@ wait on multiple flow variables at the same time: discard blockUntilAny(responses) Data flow variables ensure that no data races -are possible. Due to technical limitations not every type ``T`` is possible in -a data flow variable: ``T`` has to be of the type ``ref``, ``string``, ``seq`` +are possible. Due to technical limitations not every type `T` is possible in +a data flow variable: `T` has to be of the type `ref`, `string`, `seq` or of a type that doesn't contain a type that is garbage collected. This restriction is not hard to work-around in practice. @@ -1483,7 +1485,7 @@ Example: The parallel statement is the preferred mechanism to introduce parallelism in a -Nim program. A subset of the Nim language is valid within a ``parallel`` +Nim program. A subset of the Nim language is valid within a `parallel` section. This subset is checked during semantic analysis to be free of data races. A sophisticated `disjoint checker`:idx: ensures that no data races are possible even though shared memory is extensively supported! @@ -1491,25 +1493,25 @@ possible even though shared memory is extensively supported! The subset is in fact the full language with the following restrictions / changes: -* ``spawn`` within a ``parallel`` section has special semantics. -* Every location of the form ``a[i]`` and ``a[i..j]`` and ``dest`` where - ``dest`` is part of the pattern ``dest = spawn f(...)`` has to be +* `spawn` within a `parallel` section has special semantics. +* Every location of the form `a[i]` and `a[i..j]` and `dest` where + `dest` is part of the pattern `dest = spawn f(...)` has to be provably disjoint. This is called the *disjoint check*. -* Every other complex location ``loc`` that is used in a spawned - proc (``spawn f(loc)``) has to be immutable for the duration of - the ``parallel`` section. This is called the *immutability check*. Currently +* Every other complex location `loc` that is used in a spawned + proc (`spawn f(loc)`) has to be immutable for the duration of + the `parallel` section. This is called the *immutability check*. Currently it is not specified what exactly "complex location" means. We need to make this an optimization! * Every array access has to be provably within bounds. This is called the *bounds check*. * Slices are optimized so that no copy is performed. This optimization is not - yet performed for ordinary slices outside of a ``parallel`` section. + yet performed for ordinary slices outside of a `parallel` section. Guards and locks ================ -Apart from ``spawn`` and ``parallel`` Nim also provides all the common low level +Apart from `spawn` and `parallel` Nim also provides all the common low level concurrency mechanisms like locks, atomic intrinsics or condition variables. Nim significantly improves on the safety of these features via additional @@ -1528,13 +1530,13 @@ Guards and the locks section Protecting global variables ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Object fields and global variables can be annotated via a ``guard`` pragma: +Object fields and global variables can be annotated via a `guard` pragma: .. code-block:: nim var glock: TLock var gdata {.guard: glock.}: int -The compiler then ensures that every access of ``gdata`` is within a ``locks`` +The compiler then ensures that every access of `gdata` is within a `locks` section: .. code-block:: nim @@ -1547,11 +1549,11 @@ section: {.locks: [glock].}: echo gdata -Top level accesses to ``gdata`` are always allowed so that it can be initialized +Top level accesses to `gdata` are always allowed so that it can be initialized conveniently. It is *assumed* (but not enforced) that every top level statement is executed before any concurrent action happens. -The ``locks`` section deliberately looks ugly because it has no runtime +The `locks` section deliberately looks ugly because it has no runtime semantics and should not be used directly! It should only be used in templates that also implement some form of locking at runtime: @@ -1580,7 +1582,7 @@ model low level lockfree mechanisms: echo atomicRead(atomicCounter) -The ``locks`` pragma takes a list of lock expressions ``locks: [a, b, ...]`` +The `locks` pragma takes a list of lock expressions `locks: [a, b, ...]` in order to support *multi lock* statements. Why these are essential is explained in the `lock levels <#guards-and-locks-lock-levels>`_ section. @@ -1588,7 +1590,7 @@ explained in the `lock levels <#guards-and-locks-lock-levels>`_ section. Protecting general locations ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -The ``guard`` annotation can also be used to protect fields within an object. +The `guard` annotation can also be used to protect fields within an object. The guard then needs to be another field within the same object or a global variable. @@ -1606,7 +1608,7 @@ expressivity of the language: lock counters[i].L: inc counters[i].v -The access to field ``x.v`` is allowed since its guard ``x.L`` is active. +The access to field `x.v` is allowed since its guard `x.L` is active. After template expansion, this amounts to: .. code-block:: nim @@ -1619,10 +1621,10 @@ After template expansion, this amounts to: finally: pthread_mutex_unlock(counters[i].L) -There is an analysis that checks that ``counters[i].L`` is the lock that -corresponds to the protected location ``counters[i].v``. This analysis is called +There is an analysis that checks that `counters[i].L` is the lock that +corresponds to the protected location `counters[i].v`. This analysis is called `path analysis`:idx: because it deals with paths to locations -like ``obj.field[i].fieldB[j]``. +like `obj.field[i].fieldB[j]`. The path analysis is **currently unsound**, but that doesn't make it useless. Two paths are considered equivalent if they are syntactically the same. @@ -1644,10 +1646,10 @@ potential deadlocks during semantic analysis. A lock level is an constant integer in the range 0..1_000. Lock level 0 means that no lock is acquired at all. -If a section of code holds a lock of level ``M`` than it can also acquire any -lock of level ``N < M``. Another lock of level ``M`` cannot be acquired. Locks +If a section of code holds a lock of level `M` than it can also acquire any +lock of level `N < M`. Another lock of level `M` cannot be acquired. Locks of the same level can only be acquired *at the same time* within a -single ``locks`` section: +single `locks` section: .. code-block:: nim var a, b: TLock[2] @@ -1672,8 +1674,8 @@ single ``locks`` section: Here is how a typical multilock statement can be implemented in Nim. Note how -the runtime check is required to ensure a global ordering for two locks ``a`` -and ``b`` of the same lock level: +the runtime check is required to ensure a global ordering for two locks `a` +and `b` of the same lock level: .. code-block:: nim template multilock(a, b: ptr TLock; body: untyped) = @@ -1691,9 +1693,9 @@ and ``b`` of the same lock level: pthread_mutex_unlock(b) -Whole routines can also be annotated with a ``locks`` pragma that takes a lock +Whole routines can also be annotated with a `locks` pragma that takes a lock level. This then means that the routine may acquire locks of up to this level. -This is essential so that procs can be called within a ``locks`` section: +This is essential so that procs can be called within a `locks` section: .. code-block:: nim proc p() {.locks: 3.} = discard @@ -1704,17 +1706,17 @@ This is essential so that procs can be called within a ``locks`` section: p() -As usual ``locks`` is an inferred effect and there is a subtype -relation: ``proc () {.locks: N.}`` is a subtype of ``proc () {.locks: M.}`` +As usual `locks` is an inferred effect and there is a subtype +relation: `proc () {.locks: N.}` is a subtype of `proc () {.locks: M.}` iff (M <= N). -The ``locks`` pragma can also take the special value ``"unknown"``. This +The `locks` pragma can also take the special value `"unknown"`. This is useful in the context of dynamic method dispatching. In the following -example, the compiler can infer a lock level of 0 for the ``base`` case. +example, the compiler can infer a lock level of 0 for the `base` case. However, one of the overloaded methods calls a procvar which is -potentially locking. Thus, the lock level of calling ``g.testMethod`` +potentially locking. Thus, the lock level of calling `g.testMethod` cannot be inferred statically, leading to compiler warnings. By using -``{.locks: "unknown".}``, the base method can be marked explicitly as +`{.locks: "unknown".}`, the base method can be marked explicitly as having unknown lock level as well: .. code-block:: nim @@ -1736,8 +1738,8 @@ they will rewrite as long as there is a match. There was no way to ensure some rewrite happens only once, e.g. when rewriting term to same term plus extra content. -``noRewrite`` pragma can actually prevent further rewriting on marked code, -e.g. with given example ``echo("ab")`` will be rewritten just once: +`noRewrite` pragma can actually prevent further rewriting on marked code, +e.g. with given example `echo("ab")` will be rewritten just once: .. code-block:: nim template pwnEcho{echo(x)}(x: untyped) = @@ -1745,7 +1747,7 @@ e.g. with given example ``echo("ab")`` will be rewritten just once: echo "ab" -``noRewrite`` pragma can be useful to control term-rewriting macros recursion. +`noRewrite` pragma can be useful to control term-rewriting macros recursion. Aliasing restrictions in parameter passing @@ -1755,8 +1757,8 @@ Aliasing restrictions in parameter passing implementation and need to be fleshed out further. "Aliasing" here means that the underlying storage locations overlap in memory -at runtime. An "output parameter" is a parameter of type ``var T``, -an input parameter is any parameter that is not of type ``var``. +at runtime. An "output parameter" is a parameter of type `var T`, +an input parameter is any parameter that is not of type `var`. 1. Two output parameters should never be aliased. 2. An input and an output parameter should not be aliased. @@ -1767,25 +1769,25 @@ an input parameter is any parameter that is not of type ``var``. One problem with rules 3 and 4 is that they affect specific global or thread local variables, but Nim's effect tracking only tracks "uses no global variable" -via ``.noSideEffect``. The rules 3 and 4 can also be approximated by a different rule: +via `.noSideEffect`. The rules 3 and 4 can also be approximated by a different rule: 5. A global or thread local variable (or a location derived from such a location) - can only passed to a parameter of a ``.noSideEffect`` proc. + can only passed to a parameter of a `.noSideEffect` proc. Noalias annotation ================== -Since version 1.4 of the Nim compiler, there is a ``.noalias`` annotation for variables -and parameters. It is mapped directly to C/C++'s ``restrict`` keyword and means that +Since version 1.4 of the Nim compiler, there is a `.noalias` annotation for variables +and parameters. It is mapped directly to C/C++'s `restrict` keyword and means that the underlying pointer is pointing to a unique location in memory, no other aliases to this location exist. It is *unchecked* that this alias restriction is followed, if the restriction is violated, the backend optimizer is free to miscompile the code. This is an **unsafe** language feature. Ideally in later versions of the language, the restriction will be enforced at -compile time. (Which is also why the name ``noalias`` was choosen instead of a more -verbose name like ``unsafeAssumeNoAlias``.) +compile time. (Which is also why the name `noalias` was choosen instead of a more +verbose name like `unsafeAssumeNoAlias`.) Strict funcs @@ -1796,7 +1798,7 @@ to the existing rule that a side effect is calling a function with side effects the following rule is also enforced: Any mutation to an object does count as a side effect if that object is reachable -via a parameter that is not declared as a ``var`` parameter. +via a parameter that is not declared as a `var` parameter. For example: @@ -1830,14 +1832,14 @@ the `view types section <#view-types-algorithm>`_. View types ========== -**Note**: ``--experimental:views`` is more effective -with ``--experimental:strictFuncs``. +**Note**: `--experimental:views` is more effective +with `--experimental:strictFuncs`. A view type is a type that is or contains one of the following types: -- ``var T`` (mutable view into ``T``) -- ``lent T`` (immutable view into ``T``) -- ``openArray[T]`` (pair of (pointer to array of ``T``, size)) +- `var T` (mutable view into `T`) +- `lent T` (immutable view into `T`) +- `openArray[T]` (pair of (pointer to array of `T`, size)) For example: @@ -1850,7 +1852,7 @@ For example: View4 = Table[openArray[char], int] -Exceptions to this rule are types constructed via ``ptr`` or ``proc``. +Exceptions to this rule are types constructed via `ptr` or `proc`. For example, the following types are **not** view types: .. code-block:: nim @@ -1861,13 +1863,13 @@ For example, the following types are **not** view types: NotView3 = ptr array[4, var int] -A *mutable* view type is a type that is or contains a ``var T`` type. +A *mutable* view type is a type that is or contains a `var T` type. An *immutable* view type is a view type that is not a mutable view type. A *view* is a symbol (a let, var, const, etc.) that has a view type. Since version 1.4 Nim allows view types to be used as local variables. -This feature needs to be enabled via ``{.experimental: "views".}``. +This feature needs to be enabled via `{.experimental: "views".}`. A local variable of a view type *borrows* from the locations and it is statically enforced that the view does not outlive the location @@ -1901,47 +1903,47 @@ For example: A local variable of a view type can borrow from a location -derived from a parameter, another local variable, a global ``const`` or ``let`` -symbol or a thread-local ``var`` or ``let``. +derived from a parameter, another local variable, a global `const` or `let` +symbol or a thread-local `var` or `let`. -Let ``p`` the proc that is analysed for the correctness of the borrow operation. +Let `p` the proc that is analysed for the correctness of the borrow operation. -Let ``source`` be one of: +Let `source` be one of: -- A formal parameter of ``p``. Note that this does not cover parameters of +- A formal parameter of `p`. Note that this does not cover parameters of inner procs. -- The ``result`` symbol of ``p``. -- A local ``var`` or ``let`` or ``const`` of ``p``. Note that this does +- The `result` symbol of `p`. +- A local `var` or `let` or `const` of `p`. Note that this does not cover locals of inner procs. -- A thread-local ``var`` or ``let``. -- A global ``let`` or ``const``. +- A thread-local `var` or `let`. +- A global `let` or `const`. - A constant array/seq/object/tuple constructor. Path expressions ---------------- -A location derived from ``source`` is then defined as a path expression that -has ``source`` as the owner. A path expression ``e`` is defined recursively: +A location derived from `source` is then defined as a path expression that +has `source` as the owner. A path expression `e` is defined recursively: -- ``source`` itself is a path expression. -- Container access like ``e[i]`` is a path expression. -- Tuple access ``e[0]`` is a path expression. -- Object field access ``e.field`` is a path expression. -- ``system.toOpenArray(e, ...)`` is a path expression. -- Pointer dereference ``e[]`` is a path expression. -- An address ``addr e``, ``unsafeAddr e`` is a path expression. -- A type conversion ``T(e)`` is a path expression. -- A cast expression ``cast[T](e)`` is a path expression. -- ``f(e, ...)`` is a path expression if ``f``'s return type is a view type. - Because the view can only have been borrowed from ``e``, we then know - that owner of ``f(e, ...)`` is ``e``. +- `source` itself is a path expression. +- Container access like `e[i]` is a path expression. +- Tuple access `e[0]` is a path expression. +- Object field access `e.field` is a path expression. +- `system.toOpenArray(e, ...)` is a path expression. +- Pointer dereference `e[]` is a path expression. +- An address `addr e`, `unsafeAddr e` is a path expression. +- A type conversion `T(e)` is a path expression. +- A cast expression `cast[T](e)` is a path expression. +- `f(e, ...)` is a path expression if `f`'s return type is a view type. + Because the view can only have been borrowed from `e`, we then know + that owner of `f(e, ...)` is `e`. If a view type is used as a return type, the location must borrow from a location that is derived from the first parameter that is passed to the proc. See https://nim-lang.org/docs/manual.html#procedures-var-return-type for -details about how this is done for ``var T``. +details about how this is done for `var T`. A mutable view can borrow from a mutable location, an immutable view can borrow from both a mutable or an immutable location. @@ -1979,8 +1981,8 @@ The scope of the view does not matter: The analysis requires as much precision about mutations as is reasonably obtainable, so it is more effective with the experimental `strict funcs <#strict-funcs>`_ -feature. In other words ``--experimental:views`` works better -with ``--experimental:strictFuncs``. +feature. In other words `--experimental:views` works better +with `--experimental:strictFuncs`. The analysis is currently control flow insensitive: @@ -1992,8 +1994,8 @@ The analysis is currently control flow insensitive: s.setLen 0 echo v.field -In this example, the compiler assumes that ``s.setLen 0`` invalidates the -borrow operation of ``v`` even though a human being can easily see that it +In this example, the compiler assumes that `s.setLen 0` invalidates the +borrow operation of `v` even though a human being can easily see that it will never do that at runtime. @@ -2016,9 +2018,9 @@ A borrow operation ends with the last usage of the view variable. Reborrows --------- -A view ``v`` can borrow from multiple different locations. However, the borrow -is always the full span of ``v``'s lifetime and every location that is borrowed -from is sealed during ``v``'s lifetime. +A view `v` can borrow from multiple different locations. However, the borrow +is always the full span of `v`'s lifetime and every location that is borrowed +from is sealed during `v`'s lifetime. Algorithm @@ -2034,41 +2036,41 @@ a notion of an "abstract time", in the implementation it's a simple integer that incremented for every visited node. In the second pass information about the underlying object "graphs" is computed. -Let ``v`` be a parameter or a local variable. Let ``G(v)`` be the graph -that ``v`` belongs to. A graph is defined by the set of variables that belong -to the graph. Initially for all ``v``: ``G(v) = {v}``. Every variable can only +Let `v` be a parameter or a local variable. Let `G(v)` be the graph +that `v` belongs to. A graph is defined by the set of variables that belong +to the graph. Initially for all `v`: `G(v) = {v}`. Every variable can only be part of a single graph. -Assignments like ``a = b`` "connect" two variables, both variables end up in the -same graph ``{a, b} = G(a) = G(b)``. Unfortunately, the pattern to look for is +Assignments like `a = b` "connect" two variables, both variables end up in the +same graph `{a, b} = G(a) = G(b)`. Unfortunately, the pattern to look for is much more complex than that and can involve multiple assignment targets and sources:: f(x, y) = g(a, b) -connects ``x`` and ``y`` to ``a`` and ``b``: ``G(x) = G(y) = G(a) = G(b) = {x, y, a, b}``. +connects `x` and `y` to `a` and `b`: `G(x) = G(y) = G(a) = G(b) = {x, y, a, b}`. A type based alias analysis rules out some of these combinations, for example -a ``string`` value cannot possibly be connected to a ``seq[int]``. +a `string` value cannot possibly be connected to a `seq[int]`. -A pattern like ``v[] = value`` or ``v.field = value`` marks ``G(v)`` as mutated. +A pattern like `v[] = value` or `v.field = value` marks `G(v)` as mutated. After the second pass a set of disjoint graphs was computed. For strict functions it is then enforced that there is no graph that is both mutated and has an element that is an immutable parameter (that is a parameter that is not -of type ``var T``). +of type `var T`). -For borrow checking a different set of checks is performed. Let ``v`` be the view -and ``b`` the location that is borrowed from. +For borrow checking a different set of checks is performed. Let `v` be the view +and `b` the location that is borrowed from. -- The lifetime of ``v`` must not exceed ``b``'s lifetime. Note: The lifetime of +- The lifetime of `v` must not exceed `b`'s lifetime. Note: The lifetime of a parameter is the complete proc body. -- If ``v`` is a mutable view and ``v`` is used to actually mutate the - borrowed location, then ``b`` has to be a mutable location. +- If `v` is a mutable view and `v` is used to actually mutate the + borrowed location, then `b` has to be a mutable location. Note: If it is not actually used for mutation, borrowing a mutable view from an immutable location is allowed! This allows for many important idioms and will be justified in an upcoming RFC. -- During ``v``'s lifetime, ``G(b)`` can only be modified by ``v`` (and only if - ``v`` is a mutable view). -- If ``v`` is ``result`` then ``b`` has to be a location derived from the first +- During `v`'s lifetime, `G(b)` can only be modified by `v` (and only if + `v` is a mutable view). +- If `v` is `result` then `b` has to be a location derived from the first formal parameter or from a constant location. - A view cannot be used for a read or a write access before it was assigned to. diff --git a/doc/manual_experimental_strictnotnil.rst b/doc/manual_experimental_strictnotnil.rst index 10b59a74e..c7c045683 100644 --- a/doc/manual_experimental_strictnotnil.rst +++ b/doc/manual_experimental_strictnotnil.rst @@ -1,3 +1,4 @@ +.. default-role:: code Strict not nil checking ========================= @@ -14,9 +15,9 @@ or In the second case it would check builtin and imported modules as well. -It checks the nilability of ref-like types and makes dereferencing safer based on flow typing and ``not nil`` annotations. +It checks the nilability of ref-like types and makes dereferencing safer based on flow typing and `not nil` annotations. -Its implementation is different than the ``notnil`` one: defined under ``strictNotNil``. Keep in mind the difference in option names, be careful with distinguishing them. +Its implementation is different than the `notnil` one: defined under `strictNotNil`. Keep in mind the difference in option names, be careful with distinguishing them. We check several kinds of types for nilability: @@ -28,14 +29,14 @@ We check several kinds of types for nilability: nil ------- -The default kind of nilability types is the nilable kind: they can have the value ``nil``. -If you have a non-nilable type ``T``, you can use ``T nil`` to get a nilable type for it. +The default kind of nilability types is the nilable kind: they can have the value `nil`. +If you have a non-nilable type `T`, you can use `T nil` to get a nilable type for it. not nil -------- -You can annotate a type where nil isn't a valid value with ``not nil``. +You can annotate a type where nil isn't a valid value with `not nil`. .. code-block:: nim type @@ -58,40 +59,40 @@ You can annotate a type where nil isn't a valid value with ``not nil``. -If a type can include ``nil`` as a valid value, dereferencing values of the type -is checked by the compiler: if a value which might be nil is derefenced, this produces a warning by default, you can turn this into an error using the compiler options ``--warningAsError:strictNotNil`` +If a type can include `nil` as a valid value, dereferencing values of the type +is checked by the compiler: if a value which might be nil is derefenced, this produces a warning by default, you can turn this into an error using the compiler options `--warningAsError:strictNotNil` -If a type is nilable, you should dereference its values only after a ``isNil`` or equivalent check. +If a type is nilable, you should dereference its values only after a `isNil` or equivalent check. local turn on/off --------------------- -You can still turn off nil checking on function/module level by using a ``{.strictNotNil: off}.`` pragma. +You can still turn off nil checking on function/module level by using a `{.strictNotNil: off}.` pragma. Note: test that/TODO for code/manual. nilability state ----------------- -Currently a nilable value can be ``Safe``, ``MaybeNil`` or ``Nil`` : we use internally ``Parent`` and ``Unreachable`` but this is an implementation detail(a parent layer has the actual nilability). +Currently a nilable value can be `Safe`, `MaybeNil` or `Nil` : we use internally `Parent` and `Unreachable` but this is an implementation detail(a parent layer has the actual nilability). -``Safe`` means it shouldn't be nil at that point: e.g. after assignment to a non-nil value or ``not a.isNil`` check -``MaybeNil`` means it might be nil, but it might not be nil: e.g. an argument, a call argument or a value after an ``if`` and ``else``. -``Nil`` means it should be nil at that point; e.g. after an assignment to ``nil`` or a ``.isNil`` check. +`Safe` means it shouldn't be nil at that point: e.g. after assignment to a non-nil value or `not a.isNil` check +`MaybeNil` means it might be nil, but it might not be nil: e.g. an argument, a call argument or a value after an `if` and `else`. +`Nil` means it should be nil at that point; e.g. after an assignment to `nil` or a `.isNil` check. -``Unreachable`` means it shouldn't be possible to access this in this branch: so we do generate a warning as well. +`Unreachable` means it shouldn't be possible to access this in this branch: so we do generate a warning as well. -We show an error for each dereference (``[]``, ``.field``, ``[index]`` ``()`` etc) which is of a tracked expression which is -in ``MaybeNil`` or ``Nil`` state. +We show an error for each dereference (`[]`, `.field`, `[index]` `()` etc) which is of a tracked expression which is +in `MaybeNil` or `Nil` state. type nilability ---------------- Types are either nilable or non-nilable. -When you pass a param or a default value, we use the type : for nilable types we return ``MaybeNil`` -and for non-nilable ``Safe``. +When you pass a param or a default value, we use the type : for nilable types we return `MaybeNil` +and for non-nilable `Safe`. -TODO: fix the manual here. (This is not great, as default values for non-nilables and nilables are usually actually ``nil`` , so we should think a bit more about this section.) +TODO: fix the manual here. (This is not great, as default values for non-nilables and nilables are usually actually `nil` , so we should think a bit more about this section.) params rules ------------ @@ -102,9 +103,9 @@ Param's nilability is detected based on type nilability. We use the type of the assignment rules ----------------- -Let's say we have ``left = right``. +Let's say we have `left = right`. -When we assign, we pass the right's nilability to the left's expression. There should be special handling of aliasing and compound expressions which we specify in their sections. (Assignment is a possible alias ``move`` or ``move out``). +When we assign, we pass the right's nilability to the left's expression. There should be special handling of aliasing and compound expressions which we specify in their sections. (Assignment is a possible alias `move` or `move out`). call args rules ----------------- @@ -114,20 +115,20 @@ When we call with arguments, we have two cases when we might change the nilabili .. code-block:: nim callByVar(a) -Here ``callByVar`` can re-assign ``a``, so this might change ``a``'s nilability, so we change it to ``MaybeNil``. -This is also a possible aliasing ``move out`` (moving out of a current alias set). +Here `callByVar` can re-assign `a`, so this might change `a`'s nilability, so we change it to `MaybeNil`. +This is also a possible aliasing `move out` (moving out of a current alias set). .. code-block:: nim call(a) -Here ``call`` can change a field or element of ``a``, so if we have a dependant expression of ``a`` : e.g. ``a.field``. Dependats become ``MaybeNil``. +Here `call` can change a field or element of `a`, so if we have a dependant expression of `a` : e.g. `a.field`. Dependats become `MaybeNil`. branches rules --------------- Branches are the reason we do nil checking like this: with flow checking. -Sources of brancing are ``if``, ``while``, ``for``, ``and``, ``or``, ``case``, ``try`` and combinations with ``return``, ``break``, ``continue`` and ``raise`` +Sources of brancing are `if`, `while`, `for`, `and`, `or`, `case`, `try` and combinations with `return`, `break`, `continue` and `raise` We create a new layer/"scope" for each branch where we map expressions to nilability. This happens when we "fork": usually on the beginning of a construct. When branches "join" we usually unify their expression maps or/and nilabilities. @@ -142,33 +143,33 @@ Merging usually merges maps and alias sets: nilabilities are merged like this: else: MaybeNil -Special handling is for ``.isNil`` and `` == nil``, also for ``not``, ``and`` and ``or``. +Special handling is for `.isNil` and ` == nil`, also for `not`, `and` and `or`. -``not`` reverses the nilability, ``and`` is similar to "forking" : the right expression is checked in the layer resulting from the left one and ``or`` is similar to "merging": the right and left expression should be both checked in the original layer. +`not` reverses the nilability, `and` is similar to "forking" : the right expression is checked in the layer resulting from the left one and `or` is similar to "merging": the right and left expression should be both checked in the original layer. -``isNil``, ``== nil`` make expressions ``Nil``. If there is a ``not`` or ``!= nil``, they make them ``Safe``. -We also reverse the nilability in the opposite branch: e.g. ``else``. +`isNil`, `== nil` make expressions `Nil`. If there is a `not` or `!= nil`, they make them `Safe`. +We also reverse the nilability in the opposite branch: e.g. `else`. compound expressions: field, index expressions ----------------------------------------------- We want to track also field(dot) and index(bracket) expressions. -We track some of those compound expressions which might be nilable as dependants of their bases: ``a.field`` is changed if ``a`` is moved (re-assigned), -similarly ``a[index]`` is dependent on ``a`` and ``a.field.field`` on ``a.field``. +We track some of those compound expressions which might be nilable as dependants of their bases: `a.field` is changed if `a` is moved (re-assigned), +similarly `a[index]` is dependent on `a` and `a.field.field` on `a.field`. -When we move the base, we update dependants to ``MaybeNil``. Otherwise we usually start with type nilability. +When we move the base, we update dependants to `MaybeNil`. Otherwise we usually start with type nilability. -When we call args, we update the nilability of their dependants to ``MaybeNil`` as the calls usually can change them. -We might need to check for ``strictFuncs`` pure funcs and not do that then. +When we call args, we update the nilability of their dependants to `MaybeNil` as the calls usually can change them. +We might need to check for `strictFuncs` pure funcs and not do that then. -For field expressions ``a.field``, we calculate an integer value based on a hash of the tree and just accept equivalent trees as equivalent expressions. +For field expressions `a.field`, we calculate an integer value based on a hash of the tree and just accept equivalent trees as equivalent expressions. -For item expression ``a[index]``, we also calculate an integer value based on a hash of the tree and accept equivalent trees as equivalent expressions: for static values only. -For now we support only constant indices: we dont track expression with no-const indices. For those we just report a warning even if they are safe for now: one can use a local variable to workaround. For loops this might be annoying: so one should be able to turn off locally the warning using the ``{.warning[StrictCheckNotNil]:off}.``. +For item expression `a[index]`, we also calculate an integer value based on a hash of the tree and accept equivalent trees as equivalent expressions: for static values only. +For now we support only constant indices: we dont track expression with no-const indices. For those we just report a warning even if they are safe for now: one can use a local variable to workaround. For loops this might be annoying: so one should be able to turn off locally the warning using the `{.warning[StrictCheckNotNil]:off}.`. -For bracket expressions, in the future we might count ``a[]`` as the same general expression. -This means we should should the index but otherwise handle it the same for assign (maybe "aliasing" all the non-static elements) and differentiate only for static: e.g. ``a[0]`` and ``a[1]``. +For bracket expressions, in the future we might count `a[]` as the same general expression. +This means we should should the index but otherwise handle it the same for assign (maybe "aliasing" all the non-static elements) and differentiate only for static: e.g. `a[0]` and `a[1]`. element tracking ----------------- @@ -185,8 +186,8 @@ Also related to tracking initialization of expressions/fields. unstructured control flow rules ------------------------------- -Unstructured control flow keywords as ``return``, ``break``, ``continue``, ``raise`` mean that we jump from a branch out. -This means that if there is code after the finishing of the branch, it would be ran if one hasn't hit the direct parent branch of those: so it is similar to an ``else``. In those cases we should use the reverse nilabilities for the local to the condition expressions. E.g. +Unstructured control flow keywords as `return`, `break`, `continue`, `raise` mean that we jump from a branch out. +This means that if there is code after the finishing of the branch, it would be ran if one hasn't hit the direct parent branch of those: so it is similar to an `else`. In those cases we should use the reverse nilabilities for the local to the condition expressions. E.g. .. code-block:: nim for a in c: @@ -204,14 +205,14 @@ We support alias detection for local expressions. We track sets of aliased expressions. We start with all nilable local expressions in separate sets. Assignments and other changes to nilability can move / move out expressions of sets. -``move``: Moving ``left`` to ``right`` means we remove ``left`` from its current set and unify it with the ``right``'s set. +`move`: Moving `left` to `right` means we remove `left` from its current set and unify it with the `right`'s set. This means it stops being aliased with its previous aliases. .. code-block:: nim var left = b left = right # moving left to right -``move out``: Moving out ``left`` might remove it from the current set and ensure that it's in its own set as a single element. +`move out`: Moving out `left` might remove it from the current set and ensure that it's in its own set as a single element. e.g. @@ -229,7 +230,7 @@ warnings and errors --------------------- We show an error for each dereference (`[]`, `.field`, `[index]` `()` etc) which is of a tracked expression which is -in ``MaybeNil`` or ``Nil`` state. +in `MaybeNil` or `Nil` state. We might also show a history of the transitions and the reasons for them that might change the nilability of the expression. diff --git a/doc/nep1.rst b/doc/nep1.rst index 4a31524f6..3bae6a00b 100644 --- a/doc/nep1.rst +++ b/doc/nep1.rst @@ -1,3 +1,5 @@ +.. default-role:: code + ========================================================== Nim Enhancement Proposal #1 - Standard Library Style Guide ========================================================== @@ -124,11 +126,11 @@ Naming Conventions - In the age of HTTP, HTML, FTP, TCP, IP, UTF, WWW it is foolish to pretend these are somewhat special words requiring all uppercase. Instead treat them - as what they are: Real words. So it's ``parseUrl`` rather than - ``parseURL``, ``checkHttpHeader`` instead of ``checkHTTPHeader`` etc. + as what they are: Real words. So it's `parseUrl` rather than + `parseURL`, `checkHttpHeader` instead of `checkHTTPHeader` etc. -- Operations like ``mitems`` or ``mpairs`` (or the now deprecated ``mget``) - that allow a *mutating view* into some data structure should start with an ``m``. +- Operations like `mitems` or `mpairs` (or the now deprecated `mget`) + that allow a *mutating view* into some data structure should start with an `m`. - When both in-place mutation and 'returns transformed copy' are available the latter is a past participle of the former: @@ -136,8 +138,8 @@ Naming Conventions - sort and sorted - rotate and rotated -- When the 'returns transformed copy' version already exists like ``strutils.replace`` - an in-place version should get an ``-In`` suffix (``replaceIn`` for this example). +- When the 'returns transformed copy' version already exists like `strutils.replace` + an in-place version should get an `-In` suffix (`replaceIn` for this example). - Use `subjectVerb`, not `verbSubject`, e.g.: `fileExists`, not `existsFile`. @@ -153,25 +155,25 @@ to keep the names short but meaningful. ------------------- ------------ -------------------------------------- English word To use Notes ------------------- ------------ -------------------------------------- -initialize initFoo initializes a value type ``Foo`` -new newFoo initializes a reference type ``Foo`` - via ``new`` +initialize initFoo initializes a value type `Foo` +new newFoo initializes a reference type `Foo` + via `new` this or self self for method like procs, e.g.: `proc fun(self: Foo, a: int)` rationale: `self` is more unique in English than `this`, and `foo` would not be DRY. find find should return the position where something was found; for a bool result - use ``contains`` -contains contains often short for ``find() >= 0`` -append add use ``add`` instead of ``append`` + use `contains` +contains contains often short for `find() >= 0` +append add use `add` instead of `append` compare cmp should return an int with the - ``< 0`` ``== 0`` or ``> 0`` semantics; - for a bool result use ``sameXYZ`` -put put, ``[]=`` consider overloading ``[]=`` for put -get get, ``[]`` consider overloading ``[]`` for get; - consider to not use ``get`` as a - prefix: ``len`` instead of ``getLen`` + `< 0` `== 0` or `> 0` semantics; + for a bool result use `sameXYZ` +put put, `[]=` consider overloading `[]=` for put +get get, `[]` consider overloading `[]` for get; + consider to not use `get` as a + prefix: `len` instead of `getLen` length len also used for *number of elements* size size, len size should refer to a byte size capacity cap @@ -236,8 +238,8 @@ Coding Conventions - Use a proc when possible, only using the more powerful facilities of macros, templates, iterators, and converters when necessary. -- Use the ``let`` statement (not the ``var`` statement) when declaring variables that - do not change within their scope. Using the ``let`` statement ensures that +- Use the `let` statement (not the `var` statement) when declaring variables that + do not change within their scope. Using the `let` statement ensures that variables remain immutable, and gives those who read the code a better idea of the code's purpose. diff --git a/doc/nimc.rst b/doc/nimc.rst index ea173691f..aad889591 100644 --- a/doc/nimc.rst +++ b/doc/nimc.rst @@ -1,3 +1,5 @@ +.. default-role:: code + =================================== Nim Compiler User Guide =================================== @@ -45,8 +47,8 @@ Advanced command-line switches are: List of warnings ---------------- -Each warning can be activated individually with ``--warning[NAME]:on|off`` or -in a ``push`` pragma. +Each warning can be activated individually with `--warning[NAME]:on|off` or +in a `push` pragma. ========================== ============================================ Name Description @@ -60,7 +62,7 @@ ConfigDeprecated The project makes use of a deprecated config file. SmallLshouldNotBeUsed The letter 'l' should not be used as an identifier. -EachIdentIsTuple The code contains a confusing ``var`` +EachIdentIsTuple The code contains a confusing `var` declaration. User Some user-defined warning. ========================== ============================================ @@ -69,8 +71,8 @@ User Some user-defined warning. List of hints ------------- -Each hint can be activated individually with ``--hint[NAME]:on|off`` or in a -``push`` pragma. +Each hint can be activated individually with `--hint[NAME]:on|off` or in a +`push` pragma. ========================== ============================================ Name Description @@ -129,52 +131,52 @@ Level Description Compile-time symbols -------------------- -Through the ``-d:x`` or ``--define:x`` switch you can define compile-time +Through the `-d:x` or `--define:x` switch you can define compile-time symbols for conditional compilation. The defined switches can be checked in source code with the `when statement `_ and `defined proc `_. The typical use of this switch is -to enable builds in release mode (``-d:release``) where optimizations are -enabled for better performance. Another common use is the ``-d:ssl`` switch to +to enable builds in release mode (`-d:release`) where optimizations are +enabled for better performance. Another common use is the `-d:ssl` switch to activate SSL sockets. -Additionally, you may pass a value along with the symbol: ``-d:x=y`` +Additionally, you may pass a value along with the symbol: `-d:x=y` which may be used in conjunction with the `compile-time define pragmas`_ to override symbols during build time. Compile-time symbols are completely **case insensitive** and underscores are -ignored too. ``--define:FOO`` and ``--define:foo`` are identical. +ignored too. `--define:FOO` and `--define:foo` are identical. -Compile-time symbols starting with the ``nim`` prefix are reserved for the +Compile-time symbols starting with the `nim` prefix are reserved for the implementation and should not be used elsewhere. Configuration files ------------------- -**Note:** The *project file name* is the name of the ``.nim`` file that is +**Note:** The *project file name* is the name of the `.nim` file that is passed as a command-line argument to the compiler. -The ``nim`` executable processes configuration files in the following +The `nim` executable processes configuration files in the following directories (in this order; later files overwrite previous settings): -1) ``$nim/config/nim.cfg``, ``/etc/nim/nim.cfg`` (UNIX) or ``\config\nim.cfg`` (Windows). This file can be skipped with the ``--skipCfg`` command line option. -2) If environment variable ``XDG_CONFIG_HOME`` is defined, ``$XDG_CONFIG_HOME/nim/nim.cfg`` or ``~/.config/nim/nim.cfg`` (POSIX) or ``%APPDATA%/nim/nim.cfg`` (Windows). This file can be skipped with the ``--skipUserCfg`` command line option. -3) ``$parentDir/nim.cfg`` where ``$parentDir`` stands for any parent directory of the project file's path. These files can be skipped with the ``--skipParentCfg`` command-line option. -4) ``$projectDir/nim.cfg`` where ``$projectDir`` stands for the project file's path. This file can be skipped with the ``--skipProjCfg`` command-line option. -5) A project can also have a project-specific configuration file named ``$project.nim.cfg`` that resides in the same directory as ``$project.nim``. This file can be skipped with the ``--skipProjCfg`` command-line option. +1) `$nim/config/nim.cfg`, `/etc/nim/nim.cfg` (UNIX) or ``\config\nim.cfg`` (Windows). This file can be skipped with the `--skipCfg` command line option. +2) If environment variable `XDG_CONFIG_HOME` is defined, `$XDG_CONFIG_HOME/nim/nim.cfg` or `~/.config/nim/nim.cfg` (POSIX) or `%APPDATA%/nim/nim.cfg` (Windows). This file can be skipped with the `--skipUserCfg` command line option. +3) `$parentDir/nim.cfg` where `$parentDir` stands for any parent directory of the project file's path. These files can be skipped with the `--skipParentCfg` command-line option. +4) `$projectDir/nim.cfg` where `$projectDir` stands for the project file's path. This file can be skipped with the `--skipProjCfg` command-line option. +5) A project can also have a project-specific configuration file named `$project.nim.cfg` that resides in the same directory as `$project.nim`. This file can be skipped with the `--skipProjCfg` command-line option. Command-line settings have priority over configuration file settings. The default build of a project is a `debug build`:idx:. To compile a -`release build`:idx: define the ``release`` symbol:: +`release build`:idx: define the `release` symbol:: nim c -d:release myproject.nim - To compile a `dangerous release build`:idx: define the ``danger`` symbol:: + To compile a `dangerous release build`:idx: define the `danger` symbol:: nim c -d:danger myproject.nim @@ -186,10 +188,10 @@ Nim has the concept of a global search path (PATH) that is queried to determine where to find imported modules or include files. If multiple files are found an ambiguity error is produced. -``nim dump`` shows the contents of the PATH. +`nim dump` shows the contents of the PATH. However before the PATH is used the current directory is checked for the -file's existence. So if PATH contains ``$lib`` and ``$lib/bar`` and the +file's existence. So if PATH contains `$lib` and `$lib/bar` and the directory structure looks like this:: $lib/x.nim @@ -198,27 +200,27 @@ directory structure looks like this:: foo/main.nim other.nim -And ``main`` imports ``x``, ``foo/x`` is imported. If ``other`` imports ``x`` -then both ``$lib/x.nim`` and ``$lib/bar/x.nim`` match but ``$lib/x.nim`` is used +And `main` imports `x`, `foo/x` is imported. If `other` imports `x` +then both `$lib/x.nim` and `$lib/bar/x.nim` match but `$lib/x.nim` is used as it is the first match. Generated C code directory -------------------------- The generated files that Nim produces all go into a subdirectory called -``nimcache``. Its full path is +`nimcache`. Its full path is -- ``$XDG_CACHE_HOME/nim/$projectname(_r|_d)`` or ``~/.cache/nim/$projectname(_r|_d)`` +- `$XDG_CACHE_HOME/nim/$projectname(_r|_d)` or `~/.cache/nim/$projectname(_r|_d)` on Posix -- ``$HOME/nimcache/$projectname(_r|_d)`` on Windows. +- `$HOME/nimcache/$projectname(_r|_d)` on Windows. -The ``_r`` suffix is used for release builds, ``_d`` is for debug builds. +The `_r` suffix is used for release builds, `_d` is for debug builds. This makes it easy to delete all generated files. -The ``--nimcache`` +The `--nimcache` `compiler switch <#compiler-usage-commandminusline-switches>`_ can be used to -to change the ``nimcache`` directory. +to change the `nimcache` directory. However, the generated C code is not platform-independent. C code generated for Linux does not compile on Windows, for instance. The comment on top of the @@ -232,16 +234,16 @@ To change the compiler from the default compiler (at the command line):: nim c --cc:llvm_gcc --compile_only myfile.nim -This uses the configuration defined in ``config\nim.cfg`` for ``lvm_gcc``. +This uses the configuration defined in ``config\nim.cfg`` for `lvm_gcc`. If nimcache already contains compiled code from a different compiler for the same project, -add the ``-f`` flag to force all files to be recompiled. +add the `-f` flag to force all files to be recompiled. The default compiler is defined at the top of ``config\nim.cfg``. -Changing this setting affects the compiler used by ``koch`` to (re)build Nim. +Changing this setting affects the compiler used by `koch` to (re)build Nim. -To use the ``CC`` environment variable, use ``nim c --cc:env myfile.nim``. To use the -``CXX`` environment variable, use ``nim cpp --cc:env myfile.nim``. ``--cc:env`` is available +To use the `CC` environment variable, use `nim c --cc:env myfile.nim`. To use the +`CXX` environment variable, use `nim cpp --cc:env myfile.nim`. `--cc:env` is available since Nim version 1.4. @@ -252,7 +254,7 @@ To cross compile, use for example:: nim c --cpu:i386 --os:linux --compileOnly --genScript myproject.nim -Then move the C code and the compile script ``compile_myproject.sh`` to your +Then move the C code and the compile script `compile_myproject.sh` to your Linux i386 machine and run the script. Another way is to make Nim invoke a cross compiler toolchain:: @@ -260,8 +262,8 @@ Another way is to make Nim invoke a cross compiler toolchain:: nim c --cpu:arm --os:linux myproject.nim For cross compilation, the compiler invokes a C compiler named -like ``$cpu.$os.$cc`` (for example arm.linux.gcc) and the configuration -system is used to provide meaningful defaults. For example for ``ARM`` your +like `$cpu.$os.$cc` (for example arm.linux.gcc) and the configuration +system is used to provide meaningful defaults. For example for `ARM` your configuration file should contain something like:: arm.linux.gcc.path = "/usr/bin" @@ -275,7 +277,7 @@ To cross-compile for Windows from Linux or macOS using the MinGW-w64 toolchain:: nim c -d:mingw myproject.nim -Use ``--cpu:i386`` or ``--cpu:amd64`` to switch the CPU architecture. +Use `--cpu:i386` or `--cpu:amd64` to switch the CPU architecture. The MinGW-w64 toolchain can be installed as follows:: @@ -295,7 +297,7 @@ The first one is to treat Android as a simple Linux and use directly on android as if it was Linux. These programs are console-only programs that can't be distributed in the Play Store. -Use regular ``nim c`` inside termux to make Android terminal programs. +Use regular `nim c` inside termux to make Android terminal programs. Normal Android apps are written in Java, to use Nim inside an Android app you need a small Java stub that calls out to a native library written in @@ -303,16 +305,16 @@ Nim using the `NDK `_. You can also use `native-activity `_ to have the Java stub be auto-generated for you. -Use ``nim c -c --cpu:arm --os:android -d:androidNDK --noMain:on`` to +Use `nim c -c --cpu:arm --os:android -d:androidNDK --noMain:on` to generate the C source files you need to include in your Android Studio project. Add the generated C files to CMake build script in your Android project. Then do the final compile with Android Studio which uses Gradle to call CMake to compile the project. -Because Nim is part of a library it can't have its own c style ``main()`` -so you would need to define your own ``android_main`` and init the Java +Because Nim is part of a library it can't have its own c style `main()` +so you would need to define your own `android_main` and init the Java environment, or use a library like SDL2 or GLFM to do it. After the Android -stuff is done, it's very important to call ``NimMain()`` in order to +stuff is done, it's very important to call `NimMain()` in order to initialize Nim's garbage collector and to run the top level statements of your program. @@ -331,14 +333,14 @@ Normal languages for iOS development are Swift and Objective C. Both of these use LLVM and can be compiled into object files linked together with C, C++ or Objective C code produced by Nim. -Use ``nim c -c --os:ios --noMain:on`` to generate C files and include them in +Use `nim c -c --os:ios --noMain:on` to generate C files and include them in your XCode project. Then you can use XCode to compile, link, package and sign everything. -Because Nim is part of a library it can't have its own c style ``main()`` so you -would need to define `main` that calls ``autoreleasepool`` and -``UIApplicationMain`` to do it, or use a library like SDL2 or GLFM. After -the iOS setup is done, it's very important to call ``NimMain()`` to +Because Nim is part of a library it can't have its own c style `main()` so you +would need to define `main` that calls `autoreleasepool` and +`UIApplicationMain` to do it, or use a library like SDL2 or GLFM. After +the iOS setup is done, it's very important to call `NimMain()` to initialize Nim's garbage collector and to run the top-level statements of your program. @@ -356,8 +358,8 @@ Cross-compilation for Nintendo Switch ===================================== Simply add --os:nintendoswitch -to your usual ``nim c`` or ``nim cpp`` command and set the ``passC`` -and ``passL`` command line switches to something like: +to your usual `nim c` or `nim cpp` command and set the `passC` +and `passL` command line switches to something like: .. code-block:: console nim c ... --passC="-I$DEVKITPRO/libnx/include" ... @@ -378,8 +380,8 @@ For example, with the above-mentioned config:: nim c --os:nintendoswitch switchhomebrew.nim -This will generate a file called ``switchhomebrew.elf`` which can then be turned into -an nro file with the ``elf2nro`` tool in the DevkitPro release. Examples can be found at +This will generate a file called `switchhomebrew.elf` which can then be turned into +an nro file with the `elf2nro` tool in the DevkitPro release. Examples can be found at `the nim-libnx github repo `_. There are a few things that don't work because the DevkitPro libraries don't support them. @@ -399,62 +401,62 @@ DLL generation Nim supports the generation of DLLs. However, there must be only one instance of the GC per process/address space. This instance is contained in -``nimrtl.dll``. This means that every generated Nim DLL depends -on ``nimrtl.dll``. To generate the "nimrtl.dll" file, use the command:: +`nimrtl.dll`. This means that every generated Nim DLL depends +on `nimrtl.dll`. To generate the "nimrtl.dll" file, use the command:: nim c -d:release lib/nimrtl.nim -To link against ``nimrtl.dll`` use the command:: +To link against `nimrtl.dll` use the command:: nim c -d:useNimRtl myprog.nim -**Note**: Currently the creation of ``nimrtl.dll`` with thread support has +**Note**: Currently the creation of `nimrtl.dll` with thread support has never been tested and is unlikely to work! Additional compilation switches =============================== -The standard library supports a growing number of ``useX`` conditional defines +The standard library supports a growing number of `useX` conditional defines affecting how some features are implemented. This section tries to give a complete list. ====================== ========================================================= Define Effect ====================== ========================================================= -``release`` Turns on the optimizer. +`release` Turns on the optimizer. More aggressive optimizations are possible, e.g.: - ``--passC:-ffast-math`` (but see issue #10305) -``danger`` Turns off all runtime checks and turns on the optimizer. -``useFork`` Makes ``osproc`` use ``fork`` instead of ``posix_spawn``. -``useNimRtl`` Compile and link against ``nimrtl.dll``. -``useMalloc`` Makes Nim use C's `malloc`:idx: instead of Nim's + `--passC:-ffast-math` (but see issue #10305) +`danger` Turns off all runtime checks and turns on the optimizer. +`useFork` Makes `osproc` use `fork` instead of `posix_spawn`. +`useNimRtl` Compile and link against `nimrtl.dll`. +`useMalloc` Makes Nim use C's `malloc`:idx: instead of Nim's own memory manager, albeit prefixing each allocation with its size to support clearing memory on reallocation. - This only works with ``gc:none``, ``gc:arc`` and - ``--gc:orc``. -``useRealtimeGC`` Enables support of Nim's GC for *soft* realtime + This only works with `gc:none`, `gc:arc` and + `--gc:orc`. +`useRealtimeGC` Enables support of Nim's GC for *soft* realtime systems. See the documentation of the `gc `_ for further information. -``logGC`` Enable GC logging to stdout. -``nodejs`` The JS target is actually ``node.js``. -``ssl`` Enables OpenSSL support for the sockets module. -``memProfiler`` Enables memory profiling for the native GC. -``uClibc`` Use uClibc instead of libc. (Relevant for Unix-like OSes) -``checkAbi`` When using types from C headers, add checks that compare +`logGC` Enable GC logging to stdout. +`nodejs` The JS target is actually `node.js`. +`ssl` Enables OpenSSL support for the sockets module. +`memProfiler` Enables memory profiling for the native GC. +`uClibc` Use uClibc instead of libc. (Relevant for Unix-like OSes) +`checkAbi` When using types from C headers, add checks that compare what's in the Nim file with what's in the C header. This may become enabled by default in the future. -``tempDir`` This symbol takes a string as its value, like - ``--define:tempDir:/some/temp/path`` to override the - temporary directory returned by ``os.getTempDir()``. +`tempDir` This symbol takes a string as its value, like + `--define:tempDir:/some/temp/path` to override the + temporary directory returned by `os.getTempDir()`. The value **should** end with a directory separator character. (Relevant for the Android platform) -``useShPath`` This symbol takes a string as its value, like - ``--define:useShPath:/opt/sh/bin/sh`` to override the - path for the ``sh`` binary, in cases where it is not - located in the default location ``/bin/sh``. -``noSignalHandler`` Disable the crash handler from ``system.nim``. -``globalSymbols`` Load all ``{.dynlib.}`` libraries with the ``RTLD_GLOBAL`` +`useShPath` This symbol takes a string as its value, like + `--define:useShPath:/opt/sh/bin/sh` to override the + path for the `sh` binary, in cases where it is not + located in the default location `/bin/sh`. +`noSignalHandler` Disable the crash handler from `system.nim`. +`globalSymbols` Load all `{.dynlib.}` libraries with the `RTLD_GLOBAL` flag on Posix systems to resolve symbols in subsequently loaded libraries. ====================== ========================================================= @@ -471,20 +473,20 @@ generator and are subject to change. LineDir option -------------- -The ``lineDir`` option can be turned on or off. If turned on the -generated C code contains ``#line`` directives. This may be helpful for +The `lineDir` option can be turned on or off. If turned on the +generated C code contains `#line` directives. This may be helpful for debugging with GDB. StackTrace option ----------------- -If the ``stackTrace`` option is turned on, the generated C contains code to +If the `stackTrace` option is turned on, the generated C contains code to ensure that proper stack traces are given if the program crashes or some uncaught exception is raised. LineTrace option ---------------- -The ``lineTrace`` option implies the ``stackTrace`` option. If turned on, +The `lineTrace` option implies the `stackTrace` option. If turned on, the generated C contains code to ensure that proper stack traces with line number information are given if the program crashes or an uncaught exception is raised. @@ -493,10 +495,10 @@ is raised. DynlibOverride ============== -By default Nim's ``dynlib`` pragma causes the compiler to generate -``GetProcAddress`` (or their Unix counterparts) -calls to bind to a DLL. With the ``dynlibOverride`` command line switch this -can be prevented and then via ``--passL`` the static library can be linked +By default Nim's `dynlib` pragma causes the compiler to generate +`GetProcAddress` (or their Unix counterparts) +calls to bind to a DLL. With the `dynlibOverride` command line switch this +can be prevented and then via `--passL` the static library can be linked against. For instance, to link statically against Lua this command might work on Linux:: @@ -506,8 +508,8 @@ on Linux:: Backend language options ======================== -The typical compiler usage involves using the ``compile`` or ``c`` command to -transform a ``.nim`` file into one or more ``.c`` files which are then +The typical compiler usage involves using the `compile` or `c` command to +transform a `.nim` file into one or more `.c` files which are then compiled with the platform's C compiler into a static binary. However, there are other commands to compile to C++, Objective-C, or JavaScript. More details can be read in the `Nim Backend Integration document `_. @@ -517,7 +519,7 @@ Nim documentation tools ======================= Nim provides the `doc`:idx: command to generate HTML -documentation from ``.nim`` source files. Only exported symbols will appear in +documentation from `.nim` source files. Only exported symbols will appear in the output. For more details `see the docgen documentation `_. Nim idetools integration @@ -533,15 +535,15 @@ for further information. The Nim compiler supports an interactive mode. This is also known as a `REPL`:idx: (*read eval print loop*). If Nim has been built with the - ``-d:nimUseLinenoise`` switch, it uses the GNU readline library for terminal + `-d:nimUseLinenoise` switch, it uses the GNU readline library for terminal input management. To start Nim in interactive mode use the command - ``nim secret``. To quit use the ``quit()`` command. To determine whether an input + `nim secret`. To quit use the `quit()` command. To determine whether an input line is an incomplete statement to be continued these rules are used: 1. The line ends with ``[-+*/\\<>!\?\|%&$@~,;:=#^]\s*$`` (operator symbol followed by optional whitespace). 2. The line starts with a space (indentation). 3. The line is within a triple quoted string literal. However, the detection - does not work if the line contains more than one ``"""``. + does not work if the line contains more than one `"""`. Nim for embedded systems @@ -552,22 +554,22 @@ modern PC hardware and operating systems with ample memory, it is very well possible to run Nim code and a good part of the Nim standard libraries on small embedded microprocessors with only a few kilobytes of memory. -A good start is to use the ``any`` operating target together with the -``malloc`` memory allocator and the ``arc`` garbage collector. For example: +A good start is to use the `any` operating target together with the +`malloc` memory allocator and the `arc` garbage collector. For example: -``nim c --os:any --gc:arc -d:useMalloc [...] x.nim`` +`nim c --os:any --gc:arc -d:useMalloc [...] x.nim` -- ``--gc:arc`` will enable the reference counting memory management instead +- `--gc:arc` will enable the reference counting memory management instead of the default garbage collector. This enables Nim to use heap memory which is required for strings and seqs, for example. -- The ``--os:any`` target makes sure Nim does not depend on any specific +- The `--os:any` target makes sure Nim does not depend on any specific operating system primitives. Your platform should support only some basic - ANSI C library ``stdlib`` and ``stdio`` functions which should be available + ANSI C library `stdlib` and `stdio` functions which should be available on almost any platform. -- The ``-d:useMalloc`` option configures Nim to use only the standard C memory - manage primitives ``malloc()``, ``free()``, ``realloc()``. +- The `-d:useMalloc` option configures Nim to use only the standard C memory + manage primitives `malloc()`, `free()`, `realloc()`. If your platform does not provide these functions it should be trivial to provide an implementation for them and link these to your program. @@ -577,10 +579,10 @@ additional flags to both the Nim compiler and the C compiler and/or linker to optimize the build for size. For example, the following flags can be used when targeting a gcc compiler: -``--opt:size --passC:-flto --passL:-flto`` +`--opt:size --passC:-flto --passL:-flto` -The ``--opt:size`` flag instructs Nim to optimize code generation for small -size (with the help of the C compiler), the ``flto`` flags enable link-time +The `--opt:size` flag instructs Nim to optimize code generation for small +size (with the help of the C compiler), the `flto` flags enable link-time optimization in the compiler and linker. Check the `Cross-compilation` section for instructions on how to compile the @@ -600,7 +602,7 @@ The Nim programming language has no concept of Posix's signal handling mechanisms. However, the standard library offers some rudimentary support for signal handling, in particular, segmentation faults are turned into fatal errors that produce a stack trace. This can be disabled with the -``-d:noSignalHandler`` switch. +`-d:noSignalHandler` switch. Optimizing for Nim @@ -642,7 +644,7 @@ However, it is not efficient to do: .. code-block:: Nim var s = varA # assignment has to copy the whole string into a new buffer! -For ``let`` symbols a copy is not always necessary: +For `let` symbols a copy is not always necessary: .. code-block:: Nim let s = varA # may only copy a pointer if it safe to do so @@ -656,7 +658,7 @@ objects as `shallow`:idx:\: shallow(s) # mark 's' as a shallow string var x = s # now might not copy the string! -Usage of ``shallow`` is always safe once you know the string won't be modified +Usage of `shallow` is always safe once you know the string won't be modified anymore, similar to Ruby's `freeze`:idx:. diff --git a/doc/nimfix.rst b/doc/nimfix.rst index 62064fe69..d105346da 100644 --- a/doc/nimfix.rst +++ b/doc/nimfix.rst @@ -1,3 +1,5 @@ +.. default-role:: code + ===================== Nimfix User Guide ===================== @@ -14,12 +16,12 @@ It performs 3 different actions: 1. It makes your code case consistent. 2. It renames every symbol that has a deprecation rule. So if a module has a - rule ``{.deprecated: [TFoo: Foo].}`` then ``TFoo`` is replaced by ``Foo``. + rule `{.deprecated: [TFoo: Foo].}` then `TFoo` is replaced by `Foo`. 3. It can also check that your identifiers adhere to the official style guide - and optionally modify them to do so (via ``--styleCheck:auto``). + and optionally modify them to do so (via `--styleCheck:auto`). -Note that ``nimfix`` defaults to **overwrite** your code unless you -use ``--overwriteFiles:off``! But hey, if you do not use a version control +Note that `nimfix` defaults to **overwrite** your code unless you +use `--overwriteFiles:off`! But hey, if you do not use a version control system by this day and age, your project is already in big trouble. diff --git a/doc/nimgrep.rst b/doc/nimgrep.rst index 791ead162..5b0fe0dbb 100644 --- a/doc/nimgrep.rst +++ b/doc/nimgrep.rst @@ -1,3 +1,5 @@ +.. default-role:: code + ========================= nimgrep User's manual ========================= @@ -22,7 +24,7 @@ Compile nimgrep with the command:: nim c -d:release tools/nimgrep.nim -And copy the executable somewhere in your ``$PATH``. +And copy the executable somewhere in your `$PATH`. Command line switches diff --git a/doc/niminst.rst b/doc/niminst.rst index adb5e6f1f..3ccb47cc8 100644 --- a/doc/niminst.rst +++ b/doc/niminst.rst @@ -1,3 +1,5 @@ +.. default-role:: code + ========================= niminst User's manual ========================= @@ -31,8 +33,8 @@ configuration file. Here's an example of how the syntax looks like: :literal: The value of a key-value pair can reference user-defined variables via -the ``$variable`` notation: They can be defined in the command line with the -``--var:name=value`` switch. This is useful to not hard-coding the +the `$variable` notation: They can be defined in the command line with the +`--var:name=value` switch. This is useful to not hard-coding the program's version number into the configuration file, for instance. It follows a description of each possible section and how it affects the @@ -47,28 +49,28 @@ contain the following key-value pairs: ==================== ======================================================= Key description ==================== ======================================================= -``Name`` the project's name; this needs to be a single word -``DisplayName`` the project's long name; this can contain spaces. If - not specified, this is the same as ``Name``. -``Version`` the project's version -``OS`` the OSes to generate C code for; for example: - ``"windows;linux;macosx"`` -``CPU`` the CPUs to generate C code for; for example: - ``"i386;amd64;powerpc"`` -``Authors`` the project's authors -``Description`` the project's description -``App`` the application's type: "Console" or "GUI". If +`Name` the project's name; this needs to be a single word +`DisplayName` the project's long name; this can contain spaces. If + not specified, this is the same as `Name`. +`Version` the project's version +`OS` the OSes to generate C code for; for example: + `"windows;linux;macosx"` +`CPU` the CPUs to generate C code for; for example: + `"i386;amd64;powerpc"` +`Authors` the project's authors +`Description` the project's description +`App` the application's type: "Console" or "GUI". If "Console", niminst generates a special batch file for Windows to open up the command-line shell. -``License`` the filename of the application's license +`License` the filename of the application's license ==================== ======================================================= -``files`` key +`files` key ------------- -Many sections support the ``files`` key. Listed filenames -can be separated by semicolon or the ``files`` key can be repeated. Wildcards +Many sections support the `files` key. Listed filenames +can be separated by semicolon or the `files` key can be repeated. Wildcards in filenames are supported. If it is a directory name, all files in the directory are used:: @@ -80,63 +82,63 @@ directory are used:: Config section -------------- -The ``config`` section currently only supports the ``files`` key. Listed files +The `config` section currently only supports the `files` key. Listed files will be installed into the OS's configuration directory. Documentation section --------------------- -The ``documentation`` section supports the ``files`` key. +The `documentation` section supports the `files` key. Listed files will be installed into the OS's native documentation directory -(which might be ``$appdir/doc``). +(which might be `$appdir/doc`). -There is a ``start`` key which determines whether the Windows installer -generates a link to e.g. the ``index.html`` of your documentation. +There is a `start` key which determines whether the Windows installer +generates a link to e.g. the `index.html` of your documentation. Other section ------------- -The ``other`` section currently only supports the ``files`` key. +The `other` section currently only supports the `files` key. Listed files will be installed into the application installation directory -(``$appdir``). +(`$appdir`). Lib section ----------- -The ``lib`` section currently only supports the ``files`` key. +The `lib` section currently only supports the `files` key. Listed files will be installed into the OS's native library directory -(which might be ``$appdir/lib``). +(which might be `$appdir/lib`). Windows section --------------- -The ``windows`` section supports the ``files`` key for Windows-specific files. +The `windows` section supports the `files` key for Windows-specific files. Listed files will be installed into the application installation directory -(``$appdir``). +(`$appdir`). Other possible options are: ==================== ======================================================= Key description ==================== ======================================================= -``BinPath`` paths to add to the Windows ``%PATH%`` environment +`BinPath` paths to add to the Windows `%PATH%` environment variable. Example: ``BinPath: r"bin;dist\mingw\bin"`` -``InnoSetup`` boolean flag whether an Inno Setup installer should be - generated for Windows. Example: ``InnoSetup: "Yes"`` +`InnoSetup` boolean flag whether an Inno Setup installer should be + generated for Windows. Example: `InnoSetup: "Yes"` ==================== ======================================================= UnixBin section --------------- -The ``UnixBin`` section currently only supports the ``files`` key. +The `UnixBin` section currently only supports the `files` key. Listed files will be installed into the OS's native bin directory -(e.g. ``/usr/local/bin``). The exact location depends on the -installation path the user specifies when running the ``install.sh`` script. +(e.g. `/usr/local/bin`). The exact location depends on the +installation path the user specifies when running the `install.sh` script. Unix section @@ -147,11 +149,11 @@ Possible options are: ==================== ======================================================= Key description ==================== ======================================================= -``InstallScript`` boolean flag whether an installation shell script - should be generated. Example: ``InstallScript: "Yes"`` -``UninstallScript`` boolean flag whether a de-installation shell script +`InstallScript` boolean flag whether an installation shell script + should be generated. Example: `InstallScript: "Yes"` +`UninstallScript` boolean flag whether a de-installation shell script should be generated. - Example: ``UninstallScript: "Yes"`` + Example: `UninstallScript: "Yes"` ==================== ======================================================= @@ -163,10 +165,10 @@ Possible options are: ==================== ======================================================= Key description ==================== ======================================================= -``path`` Path to Inno Setup. +`path` Path to Inno Setup. Example: ``path = r"c:\inno setup 5\iscc.exe"`` -``flags`` Flags to pass to Inno Setup. - Example: ``flags = "/Q"`` +`flags` Flags to pass to Inno Setup. + Example: `flags = "/Q"` ==================== ======================================================= @@ -178,9 +180,9 @@ Possible options are: ==================== ======================================================= Key description ==================== ======================================================= -``path`` Path to the C compiler. -``flags`` Flags to pass to the C Compiler. - Example: ``flags = "-w"`` +`path` Path to the C compiler. +`flags` Flags to pass to the C Compiler. + Example: `flags = "-w"` ==================== ======================================================= diff --git a/doc/nims.rst b/doc/nims.rst index aa13d134a..f81637d73 100644 --- a/doc/nims.rst +++ b/doc/nims.rst @@ -1,30 +1,32 @@ +.. default-role:: code + ================================ NimScript ================================ -Strictly speaking, ``NimScript`` is the subset of Nim that can be evaluated +Strictly speaking, `NimScript` is the subset of Nim that can be evaluated by Nim's builtin virtual machine (VM). This VM is used for Nim's compiletime function evaluation features. -The ``nim`` executable processes the ``.nims`` configuration files in +The `nim` executable processes the `.nims` configuration files in the following directories (in this order; later files overwrite previous settings): -1) If environment variable ``XDG_CONFIG_HOME`` is defined, - ``$XDG_CONFIG_HOME/nim/config.nims`` or - ``~/.config/nim/config.nims`` (POSIX) or - ``%APPDATA%/nim/config.nims`` (Windows). This file can be skipped - with the ``--skipUserCfg`` command line option. -2) ``$parentDir/config.nims`` where ``$parentDir`` stands for any +1) If environment variable `XDG_CONFIG_HOME` is defined, + `$XDG_CONFIG_HOME/nim/config.nims` or + `~/.config/nim/config.nims` (POSIX) or + `%APPDATA%/nim/config.nims` (Windows). This file can be skipped + with the `--skipUserCfg` command line option. +2) `$parentDir/config.nims` where `$parentDir` stands for any parent directory of the project file's path. These files can be - skipped with the ``--skipParentCfg`` command line option. -3) ``$projectDir/config.nims`` where ``$projectDir`` stands for the - project's path. This file can be skipped with the ``--skipProjCfg`` + skipped with the `--skipParentCfg` command line option. +3) `$projectDir/config.nims` where `$projectDir` stands for the + project's path. This file can be skipped with the `--skipProjCfg` command line option. 4) A project can also have a project specific configuration file named - ``$project.nims`` that resides in the same directory as - ``$project.nim``. This file can be skipped with the same - ``--skipProjCfg`` command line option. + `$project.nims` that resides in the same directory as + `$project.nim`. This file can be skipped with the same + `--skipProjCfg` command line option. For available procs and implementation details see `nimscript `_. @@ -36,13 +38,13 @@ NimScript is subject to some limitations caused by the implementation of the VM (virtual machine): * Nim's FFI (foreign function interface) is not available in NimScript. This - means that any stdlib module which relies on ``importc`` can not be used in + means that any stdlib module which relies on `importc` can not be used in the VM. -* ``ptr`` operations are are hard to emulate with the symbolic representation +* `ptr` operations are are hard to emulate with the symbolic representation the VM uses. They are available and tested extensively but there are bugs left. -* ``var T`` function arguments rely on ``ptr`` operations internally and might +* `var T` function arguments rely on `ptr` operations internally and might also be problematic in some cases. * More than one level of `ref` is generally not supported (for example, the type @@ -50,7 +52,7 @@ NimScript is subject to some limitations caused by the implementation of the VM * Multimethods are not available. -* ``random.randomize()`` requires an ``int64`` explicitly passed as argument, you *must* pass a Seed integer. +* `random.randomize()` requires an `int64` explicitly passed as argument, you *must* pass a Seed integer. Standard library modules @@ -109,11 +111,11 @@ See also: NimScript as a configuration file ================================= -A command-line switch ``--FOO`` is written as ``switch("FOO")`` in -NimScript. Similarly, command-line ``--FOO:VAL`` translates to -``switch("FOO", "VAL")``. +A command-line switch `--FOO` is written as `switch("FOO")` in +NimScript. Similarly, command-line `--FOO:VAL` translates to +`switch("FOO", "VAL")`. -Here are few examples of using the ``switch`` proc: +Here are few examples of using the `switch` proc: .. code-block:: nim # command-line: --opt:size @@ -123,7 +125,7 @@ Here are few examples of using the ``switch`` proc: # command-line: --forceBuild switch("forceBuild") -NimScripts also support ``--`` templates for convenience, which look +NimScripts also support `--` templates for convenience, which look like command-line switches written as-is in the NimScript file. So the above example can be rewritten as: @@ -133,17 +135,17 @@ above example can be rewritten as: --forceBuild **Note**: In general, the *define* switches can also be set in -NimScripts using ``switch`` or ``--``, as shown in above -examples. Only the ``release`` define (``-d:release``) cannot be set +NimScripts using `switch` or `--`, as shown in above +examples. Only the `release` define (`-d:release`) cannot be set in NimScripts. NimScript as a build tool ========================= -The ``task`` template that the ``system`` module defines allows a NimScript +The `task` template that the `system` module defines allows a NimScript file to be used as a build tool. The following example defines a -task ``build`` that is an alias for the ``c`` command: +task `build` that is an alias for the `c` command: .. code-block:: nim task build, "builds an example": @@ -155,11 +157,11 @@ In fact, as a convention the following tasks should be available: ========= =================================================== Task Description ========= =================================================== -``help`` List all the available NimScript tasks along with their docstrings. -``build`` Build the project with the required - backend (``c``, ``cpp`` or ``js``). -``tests`` Runs the tests belonging to the project. -``bench`` Runs benchmarks belonging to the project. +`help` List all the available NimScript tasks along with their docstrings. +`build` Build the project with the required + backend (`c`, `cpp` or `js`). +`tests` Runs the tests belonging to the project. +`bench` Runs benchmarks belonging to the project. ========= =================================================== @@ -178,7 +180,7 @@ Standalone NimScript ==================== NimScript can also be used directly as a portable replacement for Bash and -Batch files. Use ``nim myscript.nims`` to run ``myscript.nims``. For example, +Batch files. Use `nim myscript.nims` to run `myscript.nims`. For example, installation of Nimble could be accomplished with this simple script: .. code-block:: nim @@ -196,8 +198,8 @@ installation of Nimble could be accomplished with this simple script: mvFile "nimble" & $id & "/src/nimble".toExe, "bin/nimble".toExe -On Unix, you can also use the shebang ``#!/usr/bin/env nim``, as long as your filename -ends with ``.nims``: +On Unix, you can also use the shebang `#!/usr/bin/env nim`, as long as your filename +ends with `.nims`: .. code-block:: nim @@ -206,7 +208,7 @@ ends with ``.nims``: echo "hello world" -Use ``#!/usr/bin/env -S nim --hints:off`` to disable hints. +Use `#!/usr/bin/env -S nim --hints:off` to disable hints. Benefits @@ -268,7 +270,7 @@ Powerful Metaprogramming NimScript can use Nim's templates, macros, types, concepts, effect tracking system, and more, you can create modules that work on compiled Nim and also on interpreted NimScript. -``func`` will still check for side effects, ``debugEcho`` also works as expected, +`func` will still check for side effects, `debugEcho` also works as expected, making it ideal for functional scripting metaprogramming. This is an example of a third party module that uses macros and templates to @@ -315,7 +317,7 @@ See the following NimScript: echo CompileDate -``likely()``, ``unlikely()``, ``static:`` and ``{.compiletime.}`` +`likely()`, `unlikely()`, `static:` and `{.compiletime.}` will produce no code at all when run on NimScript, but still no error nor warning is produced and the code just works. diff --git a/doc/nimsuggest.rst b/doc/nimsuggest.rst index 8db7c233b..509f72e8a 100644 --- a/doc/nimsuggest.rst +++ b/doc/nimsuggest.rst @@ -1,3 +1,5 @@ +.. default-role:: code + ================================ Nim IDE Integration Guide ================================ @@ -11,8 +13,8 @@ Nim differs from many other compilers in that it is really fast, and being so fast makes it suited to provide external queries for text editors about the source code being written. Through the -``nimsuggest`` tool, any IDE -can query a ``.nim`` source file and obtain useful information like +`nimsuggest` tool, any IDE +can query a `.nim` source file and obtain useful information like definition of symbols or suggestions for completion. This document will guide you through the available options. If you @@ -33,50 +35,50 @@ Nimsuggest is part of Nim's core. Build it via:: Nimsuggest invocation ===================== -Run it via ``nimsuggest --stdin --debug myproject.nim``. Nimsuggest is a -server that takes queries that are related to ``myproject``. There is some -support so that you can throw random ``.nim`` files which are not part -of ``myproject`` at Nimsuggest too, but usually the query refer to modules/files -that are part of ``myproject``. +Run it via `nimsuggest --stdin --debug myproject.nim`. Nimsuggest is a +server that takes queries that are related to `myproject`. There is some +support so that you can throw random `.nim` files which are not part +of `myproject` at Nimsuggest too, but usually the query refer to modules/files +that are part of `myproject`. -``--stdin`` means that Nimsuggest reads the query from ``stdin``. This is great +`--stdin` means that Nimsuggest reads the query from `stdin`. This is great for testing things out and playing with it but for an editor communication via sockets is more reasonable so that is the default. It listens to port 6000 by default. -Nimsuggest is basically a frontend for the nim compiler so ``--path`` flags and +Nimsuggest is basically a frontend for the nim compiler so `--path` flags and `config files `_ can be used to specify additional dependencies like -``nimsuggest --stdin --debug --path:"dependencies" myproject.nim``. +`nimsuggest --stdin --debug --path:"dependencies" myproject.nim`. Specifying the location of the query ------------------------------------ Nimsuggest then waits for queries to process. A query consists of a -cryptic 3 letter "command" ``def`` or ``con`` or ``sug`` or ``use`` followed by +cryptic 3 letter "command" `def` or `con` or `sug` or `use` followed by a location. A query location consists of: -``file.nim`` +`file.nim` This is the name of the module or include file the query refers to. -``dirtyfile.nim`` +`dirtyfile.nim` This is optional. - The ``file`` parameter is enough for static analysis, but IDEs + The `file` parameter is enough for static analysis, but IDEs tend to have *unsaved buffers* where the user may still be in the middle of typing a line. In such situations the IDE can save the current contents to a temporary file and then use the - ``dirtyfile.nim`` option to tell Nimsuggest that ``foobar.nim`` should - be taken from ``temporary/foobar.nim``. + `dirtyfile.nim` option to tell Nimsuggest that `foobar.nim` should + be taken from `temporary/foobar.nim`. -``line`` +`line` An integer with the line you are going to query. For the compiler lines start at **1**. -``col`` +`col` An integer with the column you are going to query. For the compiler columns start at **0**. @@ -84,7 +86,7 @@ a location. A query location consists of: Definitions ----------- -The ``def`` Nimsuggest command performs a query about the definition +The `def` Nimsuggest command performs a query about the definition of a specific symbol. If available, Nimsuggest will answer with the type, source file, line/column information and other accessory data if available like a docstring. With this information an IDE can @@ -105,7 +107,7 @@ can't find any valid symbol matching the position of the query. Suggestions ----------- -The ``sug`` Nimsuggest command performs a query about possible +The `sug` Nimsuggest command performs a query about possible completion symbols at some point in the file. The typical usage scenario for this option is to call it after the @@ -118,7 +120,7 @@ Nimsuggest will try to return the suggestions sorted first by scope Invocation context ------------------ -The ``con`` Nimsuggest command is very similar to the suggestions +The `con` Nimsuggest command is very similar to the suggestions command, but instead of being used after the user has typed a dot character, this one is meant to be used after the user has typed an opening brace to start typing parameters. @@ -127,7 +129,7 @@ an opening brace to start typing parameters. Symbol usages ------------- -The ``use`` Nimsuggest command lists all usages of the symbol at +The `use` Nimsuggest command lists all usages of the symbol at a position. IDEs can use this to find all the places in the file where the symbol is used and offer the user to rename it in all places at the same time. @@ -145,15 +147,15 @@ Nimsuggest output is always returned on single lines separated by tab characters (``\t``). The values of each column are: 1. Three characters indicating the type of returned answer (e.g. - ``def`` for definition, ``sug`` for suggestion, etc). -2. Type of the symbol. This can be ``skProc``, ``skLet``, and just - about any of the enums defined in the module ``compiler/ast.nim``. + `def` for definition, `sug` for suggestion, etc). +2. Type of the symbol. This can be `skProc`, `skLet`, and just + about any of the enums defined in the module `compiler/ast.nim`. 3. Fully qualified path of the symbol. If you are querying a symbol - defined in the ``proj.nim`` file, this would have the form - ``proj.symbolName``. + defined in the `proj.nim` file, this would have the form + `proj.symbolName`. 4. Type/signature. For variables and enums this will contain the type of the symbol, for procs, methods and templates this will - contain the full unique signature (e.g. ``proc (File)``). + contain the full unique signature (e.g. `proc (File)`). 5. Full path to the file containing the symbol. 6. Line where the symbol is located in the file. Lines start to count at **1**. diff --git a/doc/testament.rst b/doc/testament.rst index 5db68bfa7..04c966ffe 100644 --- a/doc/testament.rst +++ b/doc/testament.rst @@ -1,3 +1,5 @@ +.. default-role:: code + Testament is an advanced automatic unittests runner for Nim tests, is used for the development of Nim itself, offers process isolation for your tests, it can generate statistics about test cases, supports multiple targets (C, C++, ObjectiveC, JavaScript, etc), @@ -9,29 +11,29 @@ so can be useful to run your tests, even the most complex ones. Test files location =================== -By default Testament looks for test files on ``"./tests/*.nim"``. -You can overwrite this pattern glob using ``pattern ``. +By default Testament looks for test files on `"./tests/*.nim"`. +You can overwrite this pattern glob using `pattern `. The default working directory path can be changed using -``--directory:"folder/subfolder/"``. +`--directory:"folder/subfolder/"`. -Testament uses the ``nim`` compiler on ``PATH``. -You can change that using ``--nim:"folder/subfolder/nim"``. -Running JavaScript tests with ``--targets:"js"`` requires a working NodeJS on -``PATH``. +Testament uses the `nim` compiler on `PATH`. +You can change that using `--nim:"folder/subfolder/nim"`. +Running JavaScript tests with `--targets:"js"` requires a working NodeJS on +`PATH`. Options ======= -* ``--print`` Also print results to the console -* ``--simulate`` See what tests would be run but don't run them (for debugging) -* ``--failing`` Only show failing/ignored tests -* ``--targets:"c cpp js objc"`` Run tests for specified targets (default: all) -* ``--nim:path`` Use a particular nim executable (default: ``$PATH/nim``) -* ``--directory:dir`` Change to directory dir before reading the tests or doing anything else. -* ``--colors:on|off`` Turn messages coloring on|off. -* ``--backendLogging:on|off`` Disable or enable backend logging. By default turned on. -* ``--skipFrom:file`` Read tests to skip from ``file`` - one test per line, # comments ignored +* `--print` Also print results to the console +* `--simulate` See what tests would be run but don't run them (for debugging) +* `--failing` Only show failing/ignored tests +* `--targets:"c cpp js objc"` Run tests for specified targets (default: all) +* `--nim:path` Use a particular nim executable (default: `$PATH/nim`) +* `--directory:dir` Change to directory dir before reading the tests or doing anything else. +* `--colors:on|off` Turn messages coloring on|off. +* `--backendLogging:on|off` Disable or enable backend logging. By default turned on. +* `--skipFrom:file` Read tests to skip from `file` - one test per line, # comments ignored Running a single test @@ -68,7 +70,7 @@ To search for tests deeper in a directory, use HTML Reports ============ -Generate HTML Reports ``testresults.html`` from unittests, +Generate HTML Reports `testresults.html` from unittests, you have to run at least 1 test *before* generating a report: .. code:: @@ -172,7 +174,7 @@ Example "template" **to edit** and write a Testament unittest: assert 42 == 42, "Assert error message" -* As you can see the "Spec" is just a ``discard """ """``. +* As you can see the "Spec" is just a `discard """ """`. * Spec has sane defaults, so you don't need to provide them all, any simple assert will work just fine. * `This is not the full spec of Testament, check the Testament Spec on GitHub, see parseSpec(). `_ * `Nim itself uses Testament, so there are plenty of test examples. `_ diff --git a/doc/tools.rst b/doc/tools.rst index f231cdc7d..e0044e1ca 100644 --- a/doc/tools.rst +++ b/doc/tools.rst @@ -1,3 +1,5 @@ +.. default-role:: code + ======================== Tools available with Nim ======================== @@ -9,11 +11,11 @@ The standard distribution ships with the following tools: document explaining how it works. - | `Documentation generator `_ - | The builtin document generator ``nim doc`` generates HTML documentation - from ``.nim`` source files. + | The builtin document generator `nim doc` generates HTML documentation + from `.nim` source files. - | `Nimsuggest for IDE support `_ - | Through the ``nimsuggest`` tool, any IDE can query a ``.nim`` source file + | Through the `nimsuggest` tool, any IDE can query a `.nim` source file and obtain useful information like the definition of symbols or suggestions for completion. @@ -27,11 +29,11 @@ The standard distribution ships with the following tools: | Nim search and replace utility. - | nimpretty - | ``nimpretty`` is a Nim source code beautifier, + | `nimpretty` is a Nim source code beautifier, to format code according to the official style guide. - | `testament `_ - | ``testament`` is an advanced automatic *unittests runner* for Nim tests, + | `testament` is an advanced automatic *unittests runner* for Nim tests, is used for the development of Nim itself, offers process isolation for your tests, it can generate statistics about test cases, supports multiple targets (C, JS, etc), `simulated Dry-Runs `_, diff --git a/doc/tut1.rst b/doc/tut1.rst index 171b2f918..d2d6f8fe7 100644 --- a/doc/tut1.rst +++ b/doc/tut1.rst @@ -1,3 +1,5 @@ +.. default-role:: code + ===================== Nim Tutorial (Part I) ===================== @@ -46,7 +48,7 @@ Save this code to the file "greetings.nim". Now compile and run it:: nim compile --run greetings.nim -With the ``--run`` `switch `_ Nim +With the `--run` `switch `_ Nim executes the file automatically after compilation. You can give your program command-line arguments by appending them after the filename:: @@ -61,7 +63,7 @@ To compile a release version use:: nim c -d:release greetings.nim By default, the Nim compiler generates a large number of runtime checks -aiming for your debugging pleasure. With ``-d:release`` some checks are +aiming for your debugging pleasure. With `-d:release` some checks are `turned off and optimizations are turned on `_. @@ -70,8 +72,8 @@ syntax: statements which are not indented are executed when the program starts. Indentation is Nim's way of grouping statements. Indentation is done with spaces only, tabulators are not allowed. -String literals are enclosed in double-quotes. The ``var`` statement declares -a new variable named ``name`` of type ``string`` with the value that is +String literals are enclosed in double-quotes. The `var` statement declares +a new variable named `name` of type `string` with the value that is returned by the `readLine `_ procedure. Since the compiler knows that `readLine `_ returns a string, you can leave out the type in the declaration (this is called `local type @@ -85,7 +87,7 @@ Note that this is basically the only form of type inference that exists in Nim: it is a good compromise between brevity and readability. The "hello world" program contains several identifiers that are already known -to the compiler: ``echo``, `readLine `_, etc. +to the compiler: `echo`, `readLine `_, etc. These built-ins are declared in the system_ module which is implicitly imported by any other module. @@ -102,7 +104,7 @@ String and character literals ----------------------------- String literals are enclosed in double-quotes; character literals in single -quotes. Special characters are escaped with ``\``: ``\n`` means newline, ``\t`` +quotes. Special characters are escaped with ``\\``: ``\n`` means newline, ``\t`` means tabulator, etc. There are also *raw* string literals: .. code-block:: Nim @@ -111,8 +113,8 @@ means tabulator, etc. There are also *raw* string literals: In raw literals, the backslash is not an escape character. The third and last way to write string literals is *long-string literals*. -They are written with three quotes: ``""" ... """``; they can span over -multiple lines and the ``\`` is not an escape character either. They are very +They are written with three quotes: `""" ... """`; they can span over +multiple lines and the ``\\`` is not an escape character either. They are very useful for embedding HTML code templates for example. @@ -120,7 +122,7 @@ Comments -------- Comments start anywhere outside a string or character literal with the -hash character ``#``. Documentation comments start with ``##``: +hash character `#`. Documentation comments start with `##`: .. code-block:: nim :test: "nim c $1" @@ -133,7 +135,7 @@ Documentation comments are tokens; they are only allowed at certain places in the input file as they belong to the syntax tree! This feature enables simpler documentation generators. -Multiline comments are started with ``#[`` and terminated with ``]#``. Multiline +Multiline comments are started with `#[` and terminated with `]#`. Multiline comments can also be nested. .. code-block:: nim @@ -152,10 +154,10 @@ Numbers ------- Numerical literals are written as in most other languages. As a special twist, -underscores are allowed for better readability: ``1_000_000`` (one million). +underscores are allowed for better readability: `1_000_000` (one million). A number that contains a dot (or 'e' or 'E') is a floating-point literal: -``1.0e9`` (one billion). Hexadecimal literals are prefixed with ``0x``, -binary literals with ``0b`` and octal literals with ``0o``. A leading zero +`1.0e9` (one billion). Hexadecimal literals are prefixed with `0x`, +binary literals with `0b` and octal literals with `0o`. A leading zero alone does not produce an octal. @@ -164,9 +166,9 @@ The var statement The var statement declares a new local or global variable: .. code-block:: - var x, y: int # declares x and y to have the type ``int`` + var x, y: int # declares x and y to have the type `int` -Indentation can be used after the ``var`` keyword to list a whole section of +Indentation can be used after the `var` keyword to list a whole section of variables: .. code-block:: @@ -187,7 +189,7 @@ to a storage location: var x = "abc" # introduces a new variable `x` and assigns a value to it x = "xyz" # assigns a new value to `x` -``=`` is the *assignment operator*. The assignment operator can be +`=` is the *assignment operator*. The assignment operator can be overloaded. You can declare multiple variables with a single assignment statement and all the variables will have the same value: @@ -219,7 +221,7 @@ constant declaration at compile time: :test: "nim c $1" const x = "abc" # the constant x contains the string "abc" -Indentation can be used after the ``const`` keyword to list a whole section of +Indentation can be used after the `const` keyword to list a whole section of constants: .. code-block:: @@ -233,7 +235,7 @@ constants: The let statement ================= -The ``let`` statement works like the ``var`` statement but the declared +The `let` statement works like the `var` statement but the declared symbols are *single assignment* variables: After the initialization their value cannot change: @@ -241,8 +243,8 @@ value cannot change: let x = "abc" # introduces a new variable `x` and binds a value to it x = "xyz" # Illegal: assignment to `x` -The difference between ``let`` and ``const`` is: ``let`` introduces a variable -that can not be re-assigned, ``const`` means "enforce compile time evaluation +The difference between `let` and `const` is: `let` introduces a variable +that can not be re-assigned, `const` means "enforce compile time evaluation and put it into a data section": .. code-block:: @@ -276,9 +278,9 @@ The if statement is one way to branch the control flow: else: echo "Hi, ", name, "!" -There can be zero or more ``elif`` parts, and the ``else`` part is optional. -The keyword ``elif`` is short for ``else if``, and is useful to avoid -excessive indentation. (The ``""`` is the empty string. It contains no +There can be zero or more `elif` parts, and the `else` part is optional. +The keyword `elif` is short for `else if`, and is useful to avoid +excessive indentation. (The `""` is the empty string. It contains no characters.) @@ -301,7 +303,7 @@ a multi-branch: else: echo "Hi, ", name, "!" -As it can be seen, for an ``of`` branch a comma-separated list of values is also +As it can be seen, for an `of` branch a comma-separated list of values is also allowed. The case statement can deal with integers, other ordinal types, and strings. @@ -319,8 +321,8 @@ For integers or other ordinal types value ranges are also possible: of 3, 8: echo "The number is 3 or 8" However, the above code does not compile: the reason is that you have to cover -every value that ``n`` may contain, but the code only handles the values -``0..8``. Since it is not very practical to list every other possible integer +every value that `n` may contain, but the code only handles the values +`0..8`. Since it is not very practical to list every other possible integer (though it is possible thanks to the range notation), we fix this by telling the compiler that for every other value nothing should be done: @@ -334,7 +336,7 @@ the compiler that for every other value nothing should be done: The empty `discard statement <#procedures-discard-statement>`_ is a *do nothing* statement. The compiler knows that a case statement with an else part cannot fail and thus the error disappears. Note that it is impossible to cover -all possible string values: that is why string cases always need an ``else`` +all possible string values: that is why string cases always need an `else` branch. In general, the case statement is used for subrange types or enumerations where @@ -355,7 +357,7 @@ The while statement is a simple looping construct: while name == "": echo "Please tell me your name: " name = readLine(stdin) - # no ``var``, because we do not declare a new variable here + # no `var`, because we do not declare a new variable here The example uses a while loop to keep asking the users for their name, as long as the user types in nothing (only presses RETURN). @@ -364,7 +366,7 @@ as the user types in nothing (only presses RETURN). For statement ------------- -The ``for`` statement is a construct to loop over any element an *iterator* +The `for` statement is a construct to loop over any element an *iterator* provides. The example uses the built-in `countup `_ iterator: @@ -375,10 +377,10 @@ provides. The example uses the built-in `countup echo i # --> Outputs 1 2 3 4 5 6 7 8 9 10 on different lines -The variable ``i`` is implicitly declared by the -``for`` loop and has the type ``int``, because that is what `countup -`_ returns. ``i`` runs through the values -1, 2, .., 10. Each value is ``echo``-ed. This code does the same: +The variable `i` is implicitly declared by the +`for` loop and has the type `int`, because that is what `countup +`_ returns. `i` runs through the values +1, 2, .., 10. Each value is `echo`-ed. This code does the same: .. code-block:: nim echo "Counting to 10: " @@ -403,7 +405,7 @@ Since counting up occurs so often in programs, Nim also has a `.. for i in 1 .. 10: ... -Zero-indexed counting has two shortcuts ``..<`` and ``.. ^1`` +Zero-indexed counting has two shortcuts `..<` and `.. ^1` (`backward index operator `_) to simplify counting to one less than the higher index: @@ -426,8 +428,8 @@ or ... Other useful iterators for collections (like arrays and sequences) are -* ``items`` and ``mitems``, which provides immutable and mutable elements respectively, and -* ``pairs`` and ``mpairs`` which provides the element and an index number (immutable and mutable respectively) +* `items` and `mitems`, which provides immutable and mutable elements respectively, and +* `pairs` and `mpairs` which provides the element and an index number (immutable and mutable respectively) .. code-block:: nim :test: "nim c $1" @@ -439,7 +441,7 @@ Other useful iterators for collections (like arrays and sequences) are Scopes and the block statement ------------------------------ Control flow statements have a feature not covered yet: they open a -new scope. This means that in the following example, ``x`` is not accessible +new scope. This means that in the following example, `x` is not accessible outside the loop: .. code-block:: nim @@ -450,7 +452,7 @@ outside the loop: echo x # does not work A while (for) statement introduces an implicit block. Identifiers -are only visible within the block they have been declared. The ``block`` +are only visible within the block they have been declared. The `block` statement can be used to open a new block explicitly: .. code-block:: nim @@ -460,13 +462,13 @@ statement can be used to open a new block explicitly: var x = "hi" echo x # does not work either -The block's *label* (``myblock`` in the example) is optional. +The block's *label* (`myblock` in the example) is optional. Break statement --------------- -A block can be left prematurely with a ``break`` statement. The break statement -can leave a ``while``, ``for``, or a ``block`` statement. It leaves the +A block can be left prematurely with a `break` statement. The break statement +can leave a `while`, `for`, or a `block` statement. It leaves the innermost construct, unless a label of a block is given: .. code-block:: nim @@ -490,7 +492,7 @@ innermost construct, unless a label of a block is given: Continue statement ------------------ -Like in many other programming languages, a ``continue`` statement starts +Like in many other programming languages, a `continue` statement starts the next iteration immediately: .. code-block:: nim @@ -518,17 +520,17 @@ Example: else: echo "unknown operating system" -The ``when`` statement is almost identical to the ``if`` statement, but with these +The `when` statement is almost identical to the `if` statement, but with these differences: * Each condition must be a constant expression since it is evaluated by the compiler. * The statements within a branch do not open a new scope. * The compiler checks the semantics and produces code *only* for the statements - that belong to the first condition that evaluates to ``true``. + that belong to the first condition that evaluates to `true`. -The ``when`` statement is useful for writing platform-specific code, similar to -the ``#ifdef`` construct in the C programming language. +The `when` statement is useful for writing platform-specific code, similar to +the `#ifdef` construct in the C programming language. Statements and indentation @@ -539,8 +541,8 @@ indentation rules. In Nim, there is a distinction between *simple statements* and *complex statements*. *Simple statements* cannot contain other statements: -Assignment, procedure calls, or the ``return`` statement are all simple -statements. *Complex statements* like ``if``, ``when``, ``for``, ``while`` can +Assignment, procedure calls, or the `return` statement are all simple +statements. *Complex statements* like `if`, `when`, `for`, `while` can contain other statements. To avoid ambiguities, complex statements must always be indented, but single simple statements do not: @@ -575,7 +577,7 @@ contain indentation at certain places for better readability: As a rule of thumb, indentation within expressions is allowed after operators, an open parenthesis and after commas. -With parenthesis and semicolons ``(;)`` you can use statements where only +With parenthesis and semicolons `(;)` you can use statements where only an expression is allowed: .. code-block:: nim @@ -590,7 +592,7 @@ Procedures To define new commands like `echo `_ and `readLine `_ in the examples, the concept of a `procedure` is needed. (Some languages call them *methods* or *functions*.) -In Nim new procedures are defined with the ``proc`` keyword: +In Nim new procedures are defined with the `proc` keyword: .. code-block:: nim :test: "nim c $1" @@ -607,26 +609,26 @@ In Nim new procedures are defined with the ``proc`` keyword: else: echo "I think you know what the problem is just as well as I do." -This example shows a procedure named ``yes`` that asks the user a ``question`` +This example shows a procedure named `yes` that asks the user a `question` and returns true if they answered "yes" (or something similar) and returns -false if they answered "no" (or something similar). A ``return`` statement +false if they answered "no" (or something similar). A `return` statement leaves the procedure (and therefore the while loop) immediately. The -``(question: string): bool`` syntax describes that the procedure expects a -parameter named ``question`` of type ``string`` and returns a value of type -``bool``. The ``bool`` type is built-in: the only valid values for ``bool`` are -``true`` and ``false``. -The conditions in if or while statements must be of type ``bool``. +`(question: string): bool` syntax describes that the procedure expects a +parameter named `question` of type `string` and returns a value of type +`bool`. The `bool` type is built-in: the only valid values for `bool` are +`true` and `false`. +The conditions in if or while statements must be of type `bool`. -Some terminology: in the example ``question`` is called a (formal) *parameter*, -``"Should I..."`` is called an *argument* that is passed to this parameter. +Some terminology: in the example `question` is called a (formal) *parameter*, +`"Should I..."` is called an *argument* that is passed to this parameter. Result variable --------------- -A procedure that returns a value has an implicit ``result`` variable declared -that represents the return value. A ``return`` statement with no expression is -shorthand for ``return result``. The ``result`` value is always returned -automatically at the end of a procedure if there is no ``return`` statement at +A procedure that returns a value has an implicit `result` variable declared +that represents the return value. A `return` statement with no expression is +shorthand for `return result`. The `result` value is always returned +automatically at the end of a procedure if there is no `return` statement at the exit. .. code-block:: nim @@ -641,15 +643,15 @@ the exit. echo sumTillNegative(3, 4, 5) # echos 12 echo sumTillNegative(3, 4 , -1 , 6) # echos 7 -The ``result`` variable is already implicitly declared at the start of the +The `result` variable is already implicitly declared at the start of the function, so declaring it again with 'var result', for example, would shadow it with a normal variable of the same name. The result variable is also already initialized with the type's default value. Note that referential data types will -be ``nil`` at the start of the procedure, and thus may require manual +be `nil` at the start of the procedure, and thus may require manual initialization. -A procedure that does not have any ``return`` statement and does not use the -special ``result`` variable returns the value of its last expression. For example, +A procedure that does not have any `return` statement and does not use the +special `result` variable returns the value of its last expression. For example, this procedure .. code-block:: nim @@ -664,7 +666,7 @@ Parameters Parameters are immutable in the procedure body. By default, their value cannot be changed because this allows the compiler to implement parameter passing in the most efficient way. If a mutable variable is needed inside the procedure, it has -to be declared with ``var`` in the procedure body. Shadowing the parameter name +to be declared with `var` in the procedure body. Shadowing the parameter name is possible, and actually an idiom: .. code-block:: nim @@ -675,7 +677,7 @@ is possible, and actually an idiom: echo s[i] If the procedure needs to modify the argument for the -caller, a ``var`` parameter can be used: +caller, a `var` parameter can be used: .. code-block:: nim :test: "nim c $1" @@ -689,7 +691,7 @@ caller, a ``var`` parameter can be used: echo x echo y -In the example, ``res`` and ``remainder`` are `var parameters`. +In the example, `res` and `remainder` are `var parameters`. Var parameters can be modified by the procedure and the changes are visible to the caller. Note that the above example would better make use of a tuple as a return value instead of using var parameters. @@ -698,7 +700,7 @@ a tuple as a return value instead of using var parameters. Discard statement ----------------- To call a procedure that returns a value just for its side effects and ignoring -its return value, a ``discard`` statement **must** be used. Nim does not +its return value, a `discard` statement **must** be used. Nim does not allow silently throwing away a return value: .. code-block:: nim @@ -706,7 +708,7 @@ allow silently throwing away a return value: The return value can be ignored implicitly if the called proc/iterator has -been declared with the ``discardable`` pragma: +been declared with the `discardable` pragma: .. code-block:: nim :test: "nim c $1" @@ -732,7 +734,7 @@ that it is clear which argument belongs to which parameter: var w = createWindow(show = true, title = "My Application", x = 0, y = 0, height = 600, width = 800) -Now that we use named arguments to call ``createWindow`` the argument order +Now that we use named arguments to call `createWindow` the argument order does not matter anymore. Mixing named arguments with ordered arguments is also possible, but not very readable: @@ -745,7 +747,7 @@ The compiler checks that each parameter receives exactly one argument. Default values -------------- -To make the ``createWindow`` proc easier to use it should provide `default +To make the `createWindow` proc easier to use it should provide `default values`; these are values that are used as arguments if the caller does not specify them: @@ -757,11 +759,11 @@ specify them: var w = createWindow(title = "My Application", height = 600, width = 800) -Now the call to ``createWindow`` only needs to set the values that differ +Now the call to `createWindow` only needs to set the values that differ from the defaults. Note that type inference works for parameters with default values; there is -no need to write ``title: string = "unknown"``, for example. +no need to write `title: string = "unknown"`, for example. Overloaded procedures @@ -783,8 +785,8 @@ Nim provides the ability to overload procedures similar to C++: assert toString(13) == "positive" # calls the toString(x: int) proc assert toString(true) == "yep" # calls the toString(x: bool) proc -(Note that ``toString`` is usually the `$ `_ operator in -Nim.) The compiler chooses the most appropriate proc for the ``toString`` +(Note that `toString` is usually the `$ `_ operator in +Nim.) The compiler chooses the most appropriate proc for the `toString` calls. How this overloading resolution algorithm works exactly is not discussed here (it will be specified in the manual soon). However, it does not lead to nasty surprises and is based on a quite simple unification @@ -794,31 +796,31 @@ algorithm. Ambiguous calls are reported as errors. Operators --------- The Nim library makes heavy use of overloading - one reason for this is that -each operator like ``+`` is just an overloaded proc. The parser lets you -use operators in `infix notation` (``a + b``) or `prefix notation` (``+ a``). +each operator like `+` is just an overloaded proc. The parser lets you +use operators in `infix notation` (`a + b`) or `prefix notation` (`+ a`). An infix operator always receives two arguments, a prefix operator always one. (Postfix operators are not possible, because this would be ambiguous: does -``a @ @ b`` mean ``(a) @ (@b)`` or ``(a@) @ (b)``? It always means -``(a) @ (@b)``, because there are no postfix operators in Nim.) +`a @ @ b` mean `(a) @ (@b)` or `(a@) @ (b)`? It always means +`(a) @ (@b)`, because there are no postfix operators in Nim.) -Apart from a few built-in keyword operators such as ``and``, ``or``, ``not``, +Apart from a few built-in keyword operators such as `and`, `or`, `not`, operators always consist of these characters: ``+ - * \ / < > = @ $ ~ & % ! ? ^ . |`` User-defined operators are allowed. Nothing stops you from defining your own -``@!?+~`` operator, but doing so may reduce readability. +`@!?+~` operator, but doing so may reduce readability. The operator's precedence is determined by its first character. The details can be found in the manual. -To define a new operator enclose the operator in backticks "``": +To define a new operator enclose the operator in backticks "`": .. code-block:: nim proc `$` (x: myDataType): string = ... # now the $ operator also works with myDataType, overloading resolution # ensures that $ works for built-in types just like before -The "``" notation can also be used to call an operator just like any other +The "`" notation can also be used to call an operator just like any other procedure: .. code-block:: nim @@ -851,10 +853,10 @@ However, this cannot be done for mutually recursive procedures: else: n == 0 or odd(n-1) -Here ``odd`` depends on ``even`` and vice versa. Thus ``even`` needs to be +Here `odd` depends on `even` and vice versa. Thus `even` needs to be introduced to the compiler before it is completely defined. The syntax for -such a forward declaration is simple: just omit the ``=`` and the -procedure's body. The ``assert`` just adds border conditions, and will be +such a forward declaration is simple: just omit the `=` and the +procedure's body. The `assert` just adds border conditions, and will be covered later in `Modules`_ section. Later versions of the language will weaken the requirements for forward @@ -886,9 +888,9 @@ supports this loop? Lets try: inc(res) However, this does not work. The problem is that the procedure should not -only ``return``, but return and **continue** after an iteration has +only `return`, but return and **continue** after an iteration has finished. This *return and continue* is called a `yield` statement. Now -the only thing left to do is to replace the ``proc`` keyword by ``iterator`` +the only thing left to do is to replace the `proc` keyword by `iterator` and here it is - our first iterator: .. code-block:: nim @@ -903,19 +905,19 @@ Iterators look very similar to procedures, but there are several important differences: * Iterators can only be called from for loops. -* Iterators cannot contain a ``return`` statement (and procs cannot contain a - ``yield`` statement). -* Iterators have no implicit ``result`` variable. +* Iterators cannot contain a `return` statement (and procs cannot contain a + `yield` statement). +* Iterators have no implicit `result` variable. * Iterators do not support recursion. * Iterators cannot be forward declared, because the compiler must be able to inline an iterator. (This restriction will be gone in a future version of the compiler.) -However, you can also use a ``closure`` iterator to get a different set of +However, you can also use a `closure` iterator to get a different set of restrictions. See `first-class iterators `_ for details. Iterators can have the same name and parameters as a proc since essentially they have their own namespaces. Therefore it is common practice to wrap iterators in procs of the same name which accumulate the result of the -iterator and return it as a sequence, like ``split`` from the `strutils module +iterator and return it as a sequence, like `split` from the `strutils module `_. @@ -928,12 +930,12 @@ that are available for them in detail. Booleans -------- -Nim's boolean type is called ``bool`` and consists of the two -pre-defined values ``true`` and ``false``. Conditions in while, +Nim's boolean type is called `bool` and consists of the two +pre-defined values `true` and `false`. Conditions in while, if, elif, and when statements must be of type bool. -The operators ``not, and, or, xor, <, <=, >, >=, !=, ==`` are defined -for the bool type. The ``and`` and ``or`` operators perform short-circuit +The operators `not, and, or, xor, <, <=, >, >=, !=, ==` are defined +for the bool type. The `and` and `or` operators perform short-circuit evaluation. For example: .. code-block:: nim @@ -945,7 +947,7 @@ evaluation. For example: Characters ---------- -The `character type` is called ``char``. Its size is always one byte, so +The `character type` is called `char`. Its size is always one byte, so it cannot represent most UTF-8 characters, but it *can* represent one of the bytes that makes up a multi-byte UTF-8 character. The reason for this is efficiency: for the overwhelming majority of use-cases, @@ -953,57 +955,57 @@ the resulting programs will still handle UTF-8 properly as UTF-8 was especially designed for this. Character literals are enclosed in single quotes. -Chars can be compared with the ``==``, ``<``, ``<=``, ``>``, ``>=`` operators. -The ``$`` operator converts a ``char`` to a ``string``. Chars cannot be mixed -with integers; to get the ordinal value of a ``char`` use the ``ord`` proc. -Converting from an integer to a ``char`` is done with the ``chr`` proc. +Chars can be compared with the `==`, `<`, `<=`, `>`, `>=` operators. +The `$` operator converts a `char` to a `string`. Chars cannot be mixed +with integers; to get the ordinal value of a `char` use the `ord` proc. +Converting from an integer to a `char` is done with the `chr` proc. Strings ------- String variables are **mutable**, so appending to a string is possible, and quite efficient. Strings in Nim are both zero-terminated and have a -length field. A string's length can be retrieved with the builtin ``len`` +length field. A string's length can be retrieved with the builtin `len` procedure; the length never counts the terminating zero. Accessing the terminating zero is an error, it only exists so that a Nim string can be converted -to a ``cstring`` without doing a copy. +to a `cstring` without doing a copy. -The assignment operator for strings copies the string. You can use the ``&`` -operator to concatenate strings and ``add`` to append to a string. +The assignment operator for strings copies the string. You can use the `&` +operator to concatenate strings and `add` to append to a string. Strings are compared using their lexicographical order. All the comparison operators are supported. By convention, all strings are UTF-8 encoded, but this is not enforced. For example, when reading strings from binary files, they are merely -a sequence of bytes. The index operation ``s[i]`` means the i-th *char* of -``s``, not the i-th *unichar*. +a sequence of bytes. The index operation `s[i]` means the i-th *char* of +`s`, not the i-th *unichar*. -A string variable is initialized with the empty string ``""``. +A string variable is initialized with the empty string `""`. Integers -------- Nim has these integer types built-in: -``int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64``. +`int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64`. -The default integer type is ``int``. Integer literals can have a *type suffix* +The default integer type is `int`. Integer literals can have a *type suffix* to specify a non-default integer type: .. code-block:: nim :test: "nim c $1" let - x = 0 # x is of type ``int`` - y = 0'i8 # y is of type ``int8`` - z = 0'i64 # z is of type ``int64`` - u = 0'u # u is of type ``uint`` + x = 0 # x is of type `int` + y = 0'i8 # y is of type `int8` + z = 0'i64 # z is of type `int64` + u = 0'u # u is of type `uint` Most often integers are used for counting objects that reside in memory, so -``int`` has the same size as a pointer. +`int` has the same size as a pointer. -The common operators ``+ - * div mod < <= == != > >=`` are defined for -integers. The ``and or xor not`` operators are also defined for integers and -provide *bitwise* operations. Left bit shifting is done with the ``shl``, right -shifting with the ``shr`` operator. Bit shifting operators always treat their +The common operators `+ - * div mod < <= == != > >=` are defined for +integers. The `and or xor not` operators are also defined for integers and +provide *bitwise* operations. Left bit shifting is done with the `shl`, right +shifting with the `shr` operator. Bit shifting operators always treat their arguments as *unsigned*. For `arithmetic bit shifts`:idx: ordinary multiplication or division can be used. @@ -1018,10 +1020,10 @@ cannot be detected at compile time). Floats ------ -Nim has these floating-point types built-in: ``float float32 float64``. +Nim has these floating-point types built-in: `float float32 float64`. -The default float type is ``float``. In the current implementation, -``float`` is always 64-bits. +The default float type is `float`. In the current implementation, +`float` is always 64-bits. Float literals can have a *type suffix* to specify a non-default float type: @@ -1029,11 +1031,11 @@ type: .. code-block:: nim :test: "nim c $1" var - x = 0.0 # x is of type ``float`` - y = 0.0'f32 # y is of type ``float32`` - z = 0.0'f64 # z is of type ``float64`` + x = 0.0 # x is of type `float` + y = 0.0'f32 # y is of type `float32` + z = 0.0'f64 # z is of type `float64` -The common operators ``+ - * / < <= == != > >=`` are defined for +The common operators `+ - * / < <= == != > >=` are defined for floats and follow the IEEE-754 standard. Automatic type conversion in expressions with different kinds of floating-point types is performed: the smaller type is converted to the larger. Integer @@ -1061,13 +1063,13 @@ Internal type representation As mentioned earlier, the built-in `$ `_ (stringify) operator turns any basic type into a string, which you can then print to the console -using the ``echo`` proc. However, advanced types, and your own custom types, -won't work with the ``$`` operator until you define it for them. +using the `echo` proc. However, advanced types, and your own custom types, +won't work with the `$` operator until you define it for them. Sometimes you just want to debug the current value of a complex type without -having to write its ``$`` operator. You can use then the `repr +having to write its `$` operator. You can use then the `repr `_ proc which works with any type and even complex data graphs with cycles. The following example shows that even for basic types -there is a difference between the ``$`` and ``repr`` outputs: +there is a difference between the `$` and `repr` outputs: .. code-block:: nim :test: "nim c $1" @@ -1092,7 +1094,7 @@ there is a difference between the ``$`` and ``repr`` outputs: Advanced types ============== -In Nim new types can be defined within a ``type`` statement: +In Nim new types can be defined within a `type` statement: .. code-block:: nim :test: "nim c $1" @@ -1101,7 +1103,7 @@ In Nim new types can be defined within a ``type`` statement: biggestFloat = float64 # biggest float type that is available Enumeration and object types may only be defined within a -``type`` statement. +`type` statement. Enumerations @@ -1124,9 +1126,9 @@ at runtime by 0, the second by 1, and so on. For example: All the comparison operators can be used with enumeration types. An enumeration's symbol can be qualified to avoid ambiguities: -``Direction.south``. +`Direction.south`. -The ``$`` operator can convert any enumeration value to its name, and the ``ord`` +The `$` operator can convert any enumeration value to its name, and the `ord` proc can convert it to its underlying integer value. For better interfacing to other programming languages, the symbols of enum @@ -1136,7 +1138,7 @@ must be in ascending order. Ordinal types ------------- -Enumerations, integer types, ``char`` and ``bool`` (and +Enumerations, integer types, `char` and `bool` (and subranges) are called ordinal types. Ordinal types have quite a few special operations: @@ -1144,16 +1146,16 @@ a few special operations: ----------------- -------------------------------------------------------- Operation Comment ----------------- -------------------------------------------------------- -``ord(x)`` returns the integer value that is used to +`ord(x)` returns the integer value that is used to represent `x`'s value -``inc(x)`` increments `x` by one -``inc(x, n)`` increments `x` by `n`; `n` is an integer -``dec(x)`` decrements `x` by one -``dec(x, n)`` decrements `x` by `n`; `n` is an integer -``succ(x)`` returns the successor of `x` -``succ(x, n)`` returns the `n`'th successor of `x` -``pred(x)`` returns the predecessor of `x` -``pred(x, n)`` returns the `n`'th predecessor of `x` +`inc(x)` increments `x` by one +`inc(x, n)` increments `x` by `n`; `n` is an integer +`dec(x)` decrements `x` by one +`dec(x, n)` decrements `x` by `n`; `n` is an integer +`succ(x)` returns the successor of `x` +`succ(x, n)` returns the `n`'th successor of `x` +`pred(x)` returns the predecessor of `x` +`pred(x, n)` returns the `n`'th predecessor of `x` ----------------- -------------------------------------------------------- @@ -1174,17 +1176,17 @@ A subrange type is a range of values from an integer or enumeration type MySubrange = range[0..5] -``MySubrange`` is a subrange of ``int`` which can only hold the values 0 -to 5. Assigning any other value to a variable of type ``MySubrange`` is a +`MySubrange` is a subrange of `int` which can only hold the values 0 +to 5. Assigning any other value to a variable of type `MySubrange` is a compile-time or runtime error. Assignments from the base type to one of its subrange types (and vice versa) are allowed. -The ``system`` module defines the important `Natural `_ -type as ``range[0..high(int)]`` (`high `_ returns +The `system` module defines the important `Natural `_ +type as `range[0..high(int)]` (`high `_ returns the maximal value). Other programming languages may suggest the use of unsigned integers for natural numbers. This is often **unwise**: you don't want unsigned arithmetic (which wraps around) just because the numbers cannot be negative. -Nim's ``Natural`` type helps to avoid this common programming error. +Nim's `Natural` type helps to avoid this common programming error. Sets @@ -1197,7 +1199,7 @@ Arrays An array is a simple fixed-length container. Each element in an array has the same type. The array's index type can be any ordinal type. -Arrays can be constructed using ``[]``: +Arrays can be constructed using `[]`: .. code-block:: nim :test: "nim c $1" @@ -1210,10 +1212,10 @@ Arrays can be constructed using ``[]``: for i in low(x)..high(x): echo x[i] -The notation ``x[i]`` is used to access the i-th element of ``x``. +The notation `x[i]` is used to access the i-th element of `x`. Array access is always bounds checked (at compile-time or at runtime). These checks can be disabled via pragmas or invoking the compiler with the -``--bound_checks:off`` command line switch. +`--bound_checks:off` command line switch. Arrays are value types, like any other Nim type. The assignment operator copies the whole array contents. @@ -1263,9 +1265,9 @@ subdivided into height levels accessed through their integer index: #tower[north][east] = on #tower[0][1] = on -Note how the built-in ``len`` proc returns only the array's first dimension -length. Another way of defining the ``LightTower`` to better illustrate its -nested nature would be to omit the previous definition of the ``LevelSetting`` +Note how the built-in `len` proc returns only the array's first dimension +length. Another way of defining the `LightTower` to better illustrate its +nested nature would be to omit the previous definition of the `LevelSetting` type and instead write it embedded directly as the type of the first dimension: .. code-block:: nim @@ -1295,13 +1297,13 @@ Sequences are similar to arrays but of dynamic length which may change during runtime (like strings). Since sequences are resizable they are always allocated on the heap and garbage collected. -Sequences are always indexed with an ``int`` starting at position 0. The `len +Sequences are always indexed with an `int` starting at position 0. The `len `_, `low `_ and `high `_ operations are available for sequences too. -The notation ``x[i]`` can be used to access the i-th element of ``x``. +The notation `x[i]` can be used to access the i-th element of `x`. -Sequences can be constructed by the array constructor ``[]`` in conjunction -with the array to sequence operator ``@``. Another way to allocate space for +Sequences can be constructed by the array constructor `[]` in conjunction +with the array to sequence operator `@`. Another way to allocate space for a sequence is to call the built-in `newSeq `_ procedure. A sequence may be passed to an openarray parameter. @@ -1315,15 +1317,15 @@ Example: x: seq[int] # a reference to a sequence of integers x = @[1, 2, 3, 4, 5, 6] # the @ turns the array into a sequence allocated on the heap -Sequence variables are initialized with ``@[]``. +Sequence variables are initialized with `@[]`. -The ``for`` statement can be used with one or two variables when used with a +The `for` statement can be used with one or two variables when used with a sequence. When you use the one variable form, the variable will hold the value -provided by the sequence. The ``for`` statement is looping over the results +provided by the sequence. The `for` statement is looping over the results from the `items() `_ iterator from the `system `_ module. But if you use the two-variable form, the first variable will hold the index position and the second variable will hold the -value. Here the ``for`` statement is looping over the results from the +value. Here the `for` statement is looping over the results from the `pairs() `_ iterator from the `system `_ module. Examples: @@ -1348,7 +1350,7 @@ Open arrays Often fixed-size arrays turn out to be too inflexible; procedures should be able to deal with arrays of different sizes. The `openarray`:idx: type allows -this. Openarrays are always indexed with an ``int`` starting at position 0. +this. Openarrays are always indexed with an `int` starting at position 0. The `len `_, `low `_ and `high `_ operations are available for open arrays too. Any array with a compatible base type can be passed to an @@ -1377,7 +1379,7 @@ supported because this is seldom needed and cannot be done efficiently. Varargs ------- -A ``varargs`` parameter is like an openarray parameter. However, it is +A `varargs` parameter is like an openarray parameter. However, it is also a means to implement passing a variable number of arguments to a procedure. The compiler converts the list of arguments to an array automatically: @@ -1409,7 +1411,7 @@ type conversions in this context: myWriteln(stdout, [$123, $"abc", $4.0]) In this example `$ `_ is applied to any argument that is passed -to the parameter ``a``. Note that `$ `_ applied to strings is a +to the parameter `a`. Note that `$ `_ applied to strings is a nop. @@ -1441,7 +1443,7 @@ To understand some of the different ways of specifying the indices of strings, arrays, sequences, etc., it must be remembered that Nim uses zero-based indices. -So the string ``b`` is of length 19, and two different ways of specifying the +So the string `b` is of length 19, and two different ways of specifying the indices are .. code-block:: nim @@ -1451,22 +1453,22 @@ indices are 0 11 17 using indices ^19 ^8 ^2 using ^ syntax -where ``b[0 .. ^1]`` is equivalent to ``b[0 .. b.len-1]`` and ``b[0 ..< b.len]``, and it -can be seen that the ``^1`` provides a short-hand way of specifying the ``b.len-1``. See +where `b[0 .. ^1]` is equivalent to `b[0 .. b.len-1]` and `b[0 ..< b.len]`, and it +can be seen that the `^1` provides a short-hand way of specifying the `b.len-1`. See the `backwards index operator `_. In the above example, because the string ends in a period, to get the portion of the string that is "useless" and replace it with "useful". -``b[11 .. ^2]`` is the portion "useless", and ``b[11 .. ^2] = "useful"`` replaces the +`b[11 .. ^2]` is the portion "useless", and `b[11 .. ^2] = "useful"` replaces the "useless" portion with "useful", giving the result "Slices are useful." -Note 1: alternate ways of writing this are ``b[^8 .. ^2] = "useful"`` or -as ``b[11 .. b.len-2] = "useful"`` or as ``b[11 ..< b.len-1] = "useful"``. +Note 1: alternate ways of writing this are `b[^8 .. ^2] = "useful"` or +as `b[11 .. b.len-2] = "useful"` or as `b[11 ..< b.len-1] = "useful"`. -Note 2: As the ``^`` template returns a `distinct int `_ -of type ``BackwardsIndex``, we can have a ``lastIndex`` constant defined as ``const lastIndex = ^1``, -and later used as ``b[0 .. lastIndex]``. +Note 2: As the `^` template returns a `distinct int `_ +of type `BackwardsIndex`, we can have a `lastIndex` constant defined as `const lastIndex = ^1`, +and later used as `b[0 .. lastIndex]`. Objects ------- @@ -1476,7 +1478,7 @@ structure with a name is the object type. An object is a value type, which means that when an object is assigned to a new variable all its components are copied as well. -Each object type ``Foo`` has a constructor ``Foo(field: value, ...)`` +Each object type `Foo` has a constructor `Foo(field: value, ...)` where all of its fields can be initialized. Unspecified fields will get their default value. @@ -1510,7 +1512,7 @@ get their default value. Object fields that should be visible from outside the defining module have to -be marked with ``*``. +be marked with `*`. .. code-block:: nim :test: "nim c $1" @@ -1529,15 +1531,15 @@ Unlike object types though, tuple types are structurally typed, meaning different tuple-types are *equivalent* if they specify fields of the same type and of the same name in the same order. -The constructor ``()`` can be used to construct tuples. The order of the +The constructor `()` can be used to construct tuples. The order of the fields in the constructor must match the order in the tuple's definition. But unlike objects, a name for the tuple type may not be used here. -Like the object type the notation ``t.field`` is used to access a +Like the object type the notation `t.field` is used to access a tuple's field. Another notation that is not available for objects is -``t[i]`` to access the ``i``'th field. Here ``i`` must be a constant +`t[i]` to access the `i`'th field. Here `i` must be a constant integer. .. code-block:: nim @@ -1641,9 +1643,9 @@ untraced references are *unsafe*. However, for certain low-level operations Traced references are declared with the **ref** keyword; untraced references are declared with the **ptr** keyword. -The empty ``[]`` subscript notation can be used to *de-refer* a reference, -meaning to retrieve the item the reference points to. The ``.`` (access a -tuple/object field operator) and ``[]`` (array/string/sequence index operator) +The empty `[]` subscript notation can be used to *de-refer* a reference, +meaning to retrieve the item the reference points to. The `.` (access a +tuple/object field operator) and `[]` (array/string/sequence index operator) operators perform implicit dereferencing operations for reference types: .. code-block:: nim @@ -1659,18 +1661,18 @@ operators perform implicit dereferencing operations for reference types: n.data = 9 # no need to write n[].data; in fact n[].data is highly discouraged! -To allocate a new traced object, the built-in procedure ``new`` must be used. -To deal with untraced memory, the procedures ``alloc``, ``dealloc`` and -``realloc`` can be used. The `system `_ +To allocate a new traced object, the built-in procedure `new` must be used. +To deal with untraced memory, the procedures `alloc`, `dealloc` and +`realloc` can be used. The `system `_ module's documentation contains further details. -If a reference points to *nothing*, it has the value ``nil``. +If a reference points to *nothing*, it has the value `nil`. Procedural type --------------- A procedural type is a (somewhat abstract) pointer to a procedure. -``nil`` is an allowed value for a variable of a procedural type. +`nil` is an allowed value for a variable of a procedural type. Nim uses procedural types to achieve `functional`:idx: programming techniques. @@ -1708,7 +1710,7 @@ Nim supports splitting a program into pieces with a module concept. Each module is in its own file. Modules enable `information hiding`:idx: and `separate compilation`:idx:. A module may gain access to the symbols of another module by using the `import`:idx: statement. Only top-level symbols that are marked -with an asterisk (``*``) are exported: +with an asterisk (`*`) are exported: .. code-block:: nim # Module A @@ -1722,19 +1724,19 @@ with an asterisk (``*``) are exported: for i in 0..len(a)-1: result[i] = a[i] * b[i] when isMainModule: - # test the new ``*`` operator for sequences: + # test the new `*` operator for sequences: assert(@[1, 2, 3] * @[1, 2, 3] == @[1, 4, 9]) -The above module exports ``x`` and ``*``, but not ``y``. +The above module exports `x` and `*`, but not `y`. A module's top-level statements are executed at the start of the program. This can be used to initialize complex data structures for example. -Each module has a special magic constant ``isMainModule`` that is true if the +Each module has a special magic constant `isMainModule` that is true if the module is compiled as the main file. This is very useful to embed tests within the module as shown by the above example. -A symbol of a module *can* be *qualified* with the ``module.symbol`` syntax. And if +A symbol of a module *can* be *qualified* with the `module.symbol` syntax. And if a symbol is ambiguous, it *must* be qualified. A symbol is ambiguous if it is defined in two (or more) different modules and both modules are imported by a third one: @@ -1781,9 +1783,9 @@ rules apply: Excluding symbols ----------------- -The normal ``import`` statement will bring in all exported symbols. +The normal `import` statement will bring in all exported symbols. These can be limited by naming symbols that should be excluded using -the ``except`` qualifier. +the `except` qualifier. .. code-block:: nim import mymodule except y @@ -1792,14 +1794,14 @@ the ``except`` qualifier. From statement -------------- -We have already seen the simple ``import`` statement that just imports all +We have already seen the simple `import` statement that just imports all exported symbols. An alternative that only imports listed symbols is the -``from import`` statement: +`from import` statement: .. code-block:: nim from mymodule import x, y, z -The ``from`` statement can also force namespace qualification on +The `from` statement can also force namespace qualification on symbols, thereby making symbols available, but needing to be qualified in order to be used. @@ -1826,8 +1828,8 @@ define a shorter alias to use when qualifying symbols. Include statement ----------------- -The ``include`` statement does something fundamentally different than -importing a module: it merely includes the contents of a file. The ``include`` +The `include` statement does something fundamentally different than +importing a module: it merely includes the contents of a file. The `include` statement is useful to split up a large module into several files: .. code-block:: nim diff --git a/doc/tut2.rst b/doc/tut2.rst index e0d1bdb32..3aef6bb0f 100644 --- a/doc/tut2.rst +++ b/doc/tut2.rst @@ -1,3 +1,5 @@ +.. default-role:: code + ====================== Nim Tutorial (Part II) ====================== @@ -24,7 +26,7 @@ Pragmas Pragmas are Nim's method to give the compiler additional information/ commands without introducing a massive number of new keywords. Pragmas are -enclosed in the special ``{.`` and ``.}`` curly dot brackets. This tutorial +enclosed in the special `{.` and `.}` curly dot brackets. This tutorial does not cover pragmas. See the `manual `_ or `user guide `_ for a description of the available pragmas. @@ -45,11 +47,11 @@ Inheritance Inheritance in Nim is entirely optional. To enable inheritance with runtime type information the object needs to inherit from -``RootObj``. This can be done directly, or indirectly by -inheriting from an object that inherits from ``RootObj``. Usually -types with inheritance are also marked as ``ref`` types even though +`RootObj`. This can be done directly, or indirectly by +inheriting from an object that inherits from `RootObj`. Usually +types with inheritance are also marked as `ref` types even though this isn't strictly enforced. To check at runtime if an object is of a certain -type, the ``of`` operator can be used. +type, the `of` operator can be used. .. code-block:: nim :test: "nim c $1" @@ -69,16 +71,16 @@ type, the ``of`` operator can be used. student = Student(name: "Anton", age: 5, id: 2) echo student[] -Inheritance is done with the ``object of`` syntax. Multiple inheritance is -currently not supported. If an object type has no suitable ancestor, ``RootObj`` +Inheritance is done with the `object of` syntax. Multiple inheritance is +currently not supported. If an object type has no suitable ancestor, `RootObj` can be used as its ancestor, but this is only a convention. Objects that have -no ancestor are implicitly ``final``. You can use the ``inheritable`` pragma -to introduce new object roots apart from ``system.RootObj``. (This is used +no ancestor are implicitly `final`. You can use the `inheritable` pragma +to introduce new object roots apart from `system.RootObj`. (This is used in the GTK wrapper for instance.) Ref objects should be used whenever inheritance is used. It isn't strictly -necessary, but with non-ref objects assignments such as ``let person: Person = -Student(id: 123)`` will truncate subclass fields. +necessary, but with non-ref objects assignments such as `let person: Person = +Student(id: 123)` will truncate subclass fields. **Note**: Composition (*has-a* relation) is often preferable to inheritance (*is-a* relation) for simple code reuse. Since objects are value types in @@ -111,7 +113,7 @@ Example: Type conversions ---------------- Nim distinguishes between `type casts`:idx: and `type conversions`:idx:. -Casts are done with the ``cast`` operator and force the compiler to +Casts are done with the `cast` operator and force the compiler to interpret a bit pattern to be of another type. Type conversions are a much more polite way to convert a type into another: @@ -119,15 +121,15 @@ They preserve the abstract *value*, not necessarily the *bit-pattern*. If a type conversion is not possible, the compiler complains or an exception is raised. -The syntax for type conversions is ``destination_type(expression_to_convert)`` +The syntax for type conversions is `destination_type(expression_to_convert)` (like an ordinary call): .. code-block:: nim proc getID(x: Person): int = Student(x).id -The ``InvalidObjectConversionDefect`` exception is raised if ``x`` is not a -``Student``. +The `InvalidObjectConversionDefect` exception is raised if `x` is not a +`Student`. Object variants @@ -150,7 +152,7 @@ An example: nkSub, # a subtraction nkIf # an if statement Node = ref object - case kind: NodeKind # the ``kind`` field is the discriminator + case kind: NodeKind # the `kind` field is the discriminator of nkInt: intVal: int of nkFloat: floatVal: float of nkString: strVal: string @@ -173,9 +175,9 @@ Method call syntax ------------------ There is a syntactic sugar for calling routines: -The syntax ``obj.method(args)`` can be used instead of ``method(obj, args)``. +The syntax `obj.method(args)` can be used instead of `method(obj, args)`. If there are no remaining arguments, the parentheses can be omitted: -``obj.len`` (instead of ``len(obj)``). +`obj.len` (instead of `len(obj)`). This method call syntax is not restricted to objects, it can be used for any type: @@ -229,10 +231,10 @@ is needed: new s s.host = 34 # same as `host=`(s, 34) -(The example also shows ``inline`` procedures.) +(The example also shows `inline` procedures.) -The ``[]`` array access operator can be overloaded to provide +The `[]` array access operator can be overloaded to provide `array properties`:idx:\ : .. code-block:: nim @@ -258,14 +260,14 @@ The ``[]`` array access operator can be overloaded to provide else: assert(false) The example is silly, since a vector is better modelled by a tuple which -already provides ``v[]`` access. +already provides `v[]` access. Dynamic dispatch ---------------- Procedures always use static dispatch. For dynamic dispatch replace the -``proc`` keyword by ``method``: +`proc` keyword by `method`: .. code-block:: nim :test: "nim c $1" @@ -289,12 +291,12 @@ Procedures always use static dispatch. For dynamic dispatch replace the echo eval(newPlus(newPlus(newLit(1), newLit(2)), newLit(4))) -Note that in the example the constructors ``newLit`` and ``newPlus`` are procs -because it makes more sense for them to use static binding, but ``eval`` is a +Note that in the example the constructors `newLit` and `newPlus` are procs +because it makes more sense for them to use static binding, but `eval` is a method because it requires dynamic binding. **Note:** Starting from Nim 0.20, to use multi-methods one must explicitly pass -``--multimethods:on`` when compiling. +`--multimethods:on` when compiling. In a multi-method all parameters that have an object type are used for the dispatching: @@ -324,7 +326,7 @@ dispatching: As the example demonstrates, invocation of a multi-method cannot be ambiguous: Collide 2 is preferred over collide 1 because the resolution works from left to -right. Thus ``Unit, Thing`` is preferred over ``Thing, Unit``. +right. Thus `Unit, Thing` is preferred over `Thing, Unit`. **Performance note**: Nim does not produce a virtual method table, but generates dispatch trees. This avoids the expensive indirect branch for method @@ -338,19 +340,19 @@ Exceptions In Nim exceptions are objects. By convention, exception types are suffixed with 'Error'. The `system `_ module defines an exception hierarchy that you might want to stick to. Exceptions derive from -``system.Exception``, which provides the common interface. +`system.Exception`, which provides the common interface. Exceptions have to be allocated on the heap because their lifetime is unknown. The compiler will prevent you from raising an exception created on the stack. All raised exceptions should at least specify the reason for being raised in -the ``msg`` field. +the `msg` field. A convention is that exceptions should be raised in *exceptional* cases, they should not be used as an alternative method of control flow. Raise statement --------------- -Raising an exception is done with the ``raise`` statement: +Raising an exception is done with the `raise` statement: .. code-block:: nim :test: "nim c $1" @@ -360,9 +362,9 @@ Raising an exception is done with the ``raise`` statement: e.msg = "the request to the OS failed" raise e -If the ``raise`` keyword is not followed by an expression, the last exception +If the `raise` keyword is not followed by an expression, the last exception is *re-raised*. For the purpose of avoiding repeating this common code pattern, -the template ``newException`` in the ``system`` module can be used: +the template `newException` in the `system` module can be used: .. code-block:: nim raise newException(OSError, "the request to the OS failed") @@ -371,7 +373,7 @@ the template ``newException`` in the ``system`` module can be used: Try statement ------------- -The ``try`` statement handles exceptions: +The `try` statement handles exceptions: .. code-block:: nim :test: "nim c $1" @@ -399,23 +401,23 @@ The ``try`` statement handles exceptions: finally: close(f) -The statements after the ``try`` are executed unless an exception is -raised. Then the appropriate ``except`` part is executed. +The statements after the `try` are executed unless an exception is +raised. Then the appropriate `except` part is executed. -The empty ``except`` part is executed if there is an exception that is -not explicitly listed. It is similar to an ``else`` part in ``if`` +The empty `except` part is executed if there is an exception that is +not explicitly listed. It is similar to an `else` part in `if` statements. -If there is a ``finally`` part, it is always executed after the +If there is a `finally` part, it is always executed after the exception handlers. -The exception is *consumed* in an ``except`` part. If an exception is not +The exception is *consumed* in an `except` part. If an exception is not handled, it is propagated through the call stack. This means that often -the rest of the procedure - that is not within a ``finally`` clause - +the rest of the procedure - that is not within a `finally` clause - is not executed (if an exception occurs). If you need to *access* the actual exception object or message inside an -``except`` branch you can use the `getCurrentException() +`except` branch you can use the `getCurrentException() `_ and `getCurrentExceptionMsg() `_ procs from the `system `_ module. Example: @@ -433,10 +435,10 @@ module. Example: Annotating procs with raised exceptions --------------------------------------- -Through the use of the optional ``{.raises.}`` pragma you can specify that a +Through the use of the optional `{.raises.}` pragma you can specify that a proc is meant to raise a specific set of exceptions, or none at all. If the -``{.raises.}`` pragma is used, the compiler will verify that this is true. For -instance, if you specify that a proc raises ``IOError``, and at some point it +`{.raises.}` pragma is used, the compiler will verify that this is true. For +instance, if you specify that a proc raises `IOError`, and at some point it (or one of the procs it calls) starts raising a new exception the compiler will prevent that proc from compiling. Usage example: @@ -453,11 +455,11 @@ stopped validating the pragma and the raised exception not being caught, along with the file and line where the uncaught exception is being raised, which may help you locate the offending code which has changed. -If you want to add the ``{.raises.}`` pragma to existing code, the compiler can -also help you. You can add the ``{.effects.}`` pragma statement to your proc and +If you want to add the `{.raises.}` pragma to existing code, the compiler can +also help you. You can add the `{.effects.}` pragma statement to your proc and the compiler will output all inferred effects up to that point (exception tracking is part of Nim's effect system). Another more roundabout way to -find out the list of exceptions raised by a proc is to use the Nim ``doc`` +find out the list of exceptions raised by a proc is to use the Nim `doc` command which generates documentation for a whole module and decorates all procs with the list of raised exceptions. You can read more about Nim's `effect system and related pragmas in the manual `_. @@ -468,14 +470,14 @@ Generics Generics are Nim's means to parametrize procs, iterators or types with `type parameters`:idx:. Generic parameters are written within square -brackets, for example ``Foo[T]``. They are most useful for efficient type safe +brackets, for example `Foo[T]`. They are most useful for efficient type safe containers: .. code-block:: nim :test: "nim c $1" type BinaryTree*[T] = ref object # BinaryTree is a generic type with - # generic param ``T`` + # generic param `T` le, ri: BinaryTree[T] # left and right subtrees; may be nil data: T # the data stored in a node @@ -491,8 +493,8 @@ containers: else: var it = root while it != nil: - # compare the data items; uses the generic ``cmp`` proc - # that works for any type that has a ``==`` and ``<`` operator + # compare the data items; uses the generic `cmp` proc + # that works for any type that has a `==` and `<` operator var c = cmp(it.data, n.data) if c < 0: if it.le == nil: @@ -522,19 +524,19 @@ containers: n = n.le # and follow the left pointer var - root: BinaryTree[string] # instantiate a BinaryTree with ``string`` - add(root, newNode("hello")) # instantiates ``newNode`` and ``add`` - add(root, "world") # instantiates the second ``add`` proc + root: BinaryTree[string] # instantiate a BinaryTree with `string` + add(root, newNode("hello")) # instantiates `newNode` and `add` + add(root, "world") # instantiates the second `add` proc for str in preorder(root): stdout.writeLine(str) The example shows a generic binary tree. Depending on context, the brackets are used either to introduce type parameters or to instantiate a generic proc, iterator or type. As the example shows, generics work with overloading: the -best match of ``add`` is used. The built-in ``add`` procedure for sequences -is not hidden and is used in the ``preorder`` iterator. +best match of `add` is used. The built-in `add` procedure for sequences +is not hidden and is used in the `preorder` iterator. -There is a special ``[:T]`` syntax when using generics with the method call syntax: +There is a special `[:T]` syntax when using generics with the method call syntax: .. code-block:: nim :test: "nim c $1" @@ -567,14 +569,14 @@ Example: assert(5 != 6) # the compiler rewrites that to: assert(not (5 == 6)) -The ``!=``, ``>``, ``>=``, ``in``, ``notin``, ``isnot`` operators are in fact -templates: this has the benefit that if you overload the ``==`` operator, -the ``!=`` operator is available automatically and does the right thing. (Except +The `!=`, `>`, `>=`, `in`, `notin`, `isnot` operators are in fact +templates: this has the benefit that if you overload the `==` operator, +the `!=` operator is available automatically and does the right thing. (Except for IEEE floating point numbers - NaN breaks basic boolean logic.) -``a > b`` is transformed into ``b < a``. -``a in b`` is transformed into ``contains(b, a)``. -``notin`` and ``isnot`` have the obvious meanings. +`a > b` is transformed into `b < a`. +`a in b` is transformed into `contains(b, a)`. +`notin` and `isnot` have the obvious meanings. Templates are especially useful for lazy evaluation purposes. Consider a simple proc for logging: @@ -591,11 +593,11 @@ simple proc for logging: x = 4 log("x has the value: " & $x) -This code has a shortcoming: if ``debug`` is set to false someday, the quite -expensive ``$`` and ``&`` operations are still performed! (The argument +This code has a shortcoming: if `debug` is set to false someday, the quite +expensive `$` and `&` operations are still performed! (The argument evaluation for procedures is *eager*). -Turning the ``log`` proc into a template solves this problem: +Turning the `log` proc into a template solves this problem: .. code-block:: nim :test: "nim c $1" @@ -609,15 +611,15 @@ Turning the ``log`` proc into a template solves this problem: x = 4 log("x has the value: " & $x) -The parameters' types can be ordinary types or the meta types ``untyped``, -``typed``, or ``type``. ``type`` suggests that only a type symbol may be given -as an argument, and ``untyped`` means symbol lookups and type resolution is not +The parameters' types can be ordinary types or the meta types `untyped`, +`typed`, or `type`. `type` suggests that only a type symbol may be given +as an argument, and `untyped` means symbol lookups and type resolution is not performed before the expression is passed to the template. If the template has no explicit return type, -``void`` is used for consistency with procs and methods. +`void` is used for consistency with procs and methods. -To pass a block of statements to a template, use ``untyped`` for the last parameter: +To pass a block of statements to a template, use `untyped` for the last parameter: .. code-block:: nim :test: "nim c $1" @@ -638,10 +640,10 @@ To pass a block of statements to a template, use ``untyped`` for the last parame txt.writeLine("line 1") txt.writeLine("line 2") -In the example the two ``writeLine`` statements are bound to the ``body`` -parameter. The ``withFile`` template contains boilerplate code and helps to +In the example the two `writeLine` statements are bound to the `body` +parameter. The `withFile` template contains boilerplate code and helps to avoid a common bug: to forget to close the file. Note how the -``let fn = filename`` statement ensures that ``filename`` is evaluated only +`let fn = filename` statement ensures that `filename` is evaluated only once. Example: Lifting Procs @@ -653,7 +655,7 @@ Example: Lifting Procs template liftScalarProc(fname) = ## Lift a proc taking one scalar parameter and returning a - ## scalar value (eg ``proc sssss[T](x: T): float``), + ## scalar value (eg `proc sssss[T](x: T): float`), ## to provide templated procs that can handle a single ## parameter of seq[T] or nested seq[seq[]] or the same type ## @@ -675,15 +677,15 @@ Compilation to JavaScript Nim code can be compiled to JavaScript. However in order to write JavaScript-compatible code you should remember the following: -- ``addr`` and ``ptr`` have slightly different semantic meaning in JavaScript. +- `addr` and `ptr` have slightly different semantic meaning in JavaScript. It is recommended to avoid those if you're not sure how they are translated to JavaScript. -- ``cast[T](x)`` in JavaScript is translated to ``(x)``, except for casting +- `cast[T](x)` in JavaScript is translated to `(x)`, except for casting between signed/unsigned ints, in which case it behaves as static cast in C language. -- ``cstring`` in JavaScript means JavaScript string. It is a good practice to - use ``cstring`` only when it is semantically appropriate. E.g. don't use - ``cstring`` as a binary data buffer. +- `cstring` in JavaScript means JavaScript string. It is a good practice to + use `cstring` only when it is semantically appropriate. E.g. don't use + `cstring` as a binary data buffer. Part 3 diff --git a/doc/tut3.rst b/doc/tut3.rst index b0a3d8232..358a9b45e 100644 --- a/doc/tut3.rst +++ b/doc/tut3.rst @@ -1,3 +1,5 @@ +.. default-role:: code + ======================= Nim Tutorial (Part III) ======================= diff --git a/lib/system_overview.rst b/lib/system_overview.rst index 5e4b171fe..d6cbe1a35 100644 --- a/lib/system_overview.rst +++ b/lib/system_overview.rst @@ -1,3 +1,5 @@ +.. default-role:: code + The System module imports several separate modules, and their documentation is in separate files: @@ -36,7 +38,7 @@ Proc Usage * `strutils module `_ for common string functions * `strformat module `_ for string interpolation and formatting * `unicode module `_ for Unicode UTF-8 handling -* `strscans `_ for ``scanf`` and ``scanp`` macros, which offer +* `strscans `_ for `scanf` and `scanp` macros, which offer easier substring extraction than regular expressions * `strtabs module `_ for efficient hash tables (dictionaries, in some programming languages) mapping from strings to strings From 9e1a2e9b63fff3d3445a04600b431a42c800a0c8 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Fri, 19 Mar 2021 03:15:37 -0300 Subject: [PATCH 1740/3768] improve jsconsole (#17414) * Add jsconsole.dir * Update lib/js/jsconsole.nim Co-authored-by: Timothee Cour Co-authored-by: Timothee Cour --- changelog.md | 2 ++ lib/js/jsconsole.nim | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/changelog.md b/changelog.md index 4080f789c..219a66ae7 100644 --- a/changelog.md +++ b/changelog.md @@ -237,6 +237,8 @@ - In `std/os`, `getHomeDir`, `expandTilde`, `getTempDir`, `getConfigDir` now do not include trailing `DirSep`, unless `-d:nimLegacyHomeDir` is specified (for a transition period). +- Added `jsconsole.dir`, `jsconsole.dirxml`, `jsconsole.timeStamp`. + ## Language changes diff --git a/lib/js/jsconsole.nim b/lib/js/jsconsole.nim index b8a361af1..bf43adddd 100644 --- a/lib/js/jsconsole.nim +++ b/lib/js/jsconsole.nim @@ -110,5 +110,15 @@ since (1, 5): {.line: loc.}: {.emit: ["console.assert(", assertion, ", ", msg, ");"].} + func dir*(console: Console; obj: auto) {.importcpp.} + ## https://developer.mozilla.org/en-US/docs/Web/API/Console/dir + + func dirxml*(console: Console; obj: auto) {.importcpp.} + ## https://developer.mozilla.org/en-US/docs/Web/API/Console/dirxml + + func timeStamp*(console: Console; label: cstring) {.importcpp.} + ## https://developer.mozilla.org/en-US/docs/Web/API/Console/timeStamp + ## ..warning:: non-standard + var console* {.importc, nodecl.}: Console From 452366982d8ef7d8ebed5cd95c034a14ee2f56c0 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 18 Mar 2021 23:17:09 -0700 Subject: [PATCH 1741/3768] fix #16901: sidebar groups now works with all routines, not just proc,func (#17416) * fix #16901: sidebar groups now works with all routines, not just proc,func * fix tests --- compiler/docgen.nim | 2 +- .../expected/subdir/subdir_b/utils.html | 19 +++- nimdoc/testproject/expected/testproject.html | 104 +++++++++++++----- 3 files changed, 91 insertions(+), 34 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 37831c72c..57d5e7b01 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -1245,7 +1245,7 @@ proc genOutFile(d: PDoc, groupedToc = false): Rope = renderTocEntries(d[], j, 1, tmp) var toc = tmp.rope for i in TSymKind: - var shouldSort = i in {skProc, skFunc} and groupedToc + var shouldSort = i in routineKinds and groupedToc genSection(d, i, shouldSort) toc.add(d.toc[i]) if toc != nil: diff --git a/nimdoc/testproject/expected/subdir/subdir_b/utils.html b/nimdoc/testproject/expected/subdir/subdir_b/utils.html index 53088dfda..cb80f2873 100644 --- a/nimdoc/testproject/expected/subdir/subdir_b/utils.html +++ b/nimdoc/testproject/expected/subdir/subdir_b/utils.html @@ -121,12 +121,21 @@ window.addEventListener('DOMContentLoaded', main);
                                    • Templates
                                    • diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html index dbdcca79e..48b3094e7 100644 --- a/nimdoc/testproject/expected/testproject.html +++ b/nimdoc/testproject/expected/testproject.html @@ -309,56 +309,104 @@ window.addEventListener('DOMContentLoaded', main);
                                    • Methods
                                    • Iterators
                                    • Macros
                                    • Templates
                                    • From 83e002a318dee2370b266446d5ec26ad97a65f57 Mon Sep 17 00:00:00 2001 From: flywind Date: Fri, 19 Mar 2021 14:48:31 +0800 Subject: [PATCH 1742/3768] follow up #17391 add testcase (#17404) * Revert "make system random work in VM" * fix #17380 * attempt to fix bug * fix * better * fix * a bit * fix the leaks * revert * fix * better * follow up #17391 * fix * Update tchannels.nim * Update tests/stdlib/tchannels.nim * Update tchannels.nim --- tests/stdlib/tchannels.nim | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 tests/stdlib/tchannels.nim diff --git a/tests/stdlib/tchannels.nim b/tests/stdlib/tchannels.nim new file mode 100644 index 000000000..33108c50c --- /dev/null +++ b/tests/stdlib/tchannels.nim @@ -0,0 +1,33 @@ +discard """ + timeout: 5.0 # but typically < 1s + disabled: "freebsd" + matrix: "--gc:arc --threads:on; --gc:arc --threads:on -d:danger" +""" + +when true: + # bug #17380: this was either blocking (without -d:danger) or crashing with SIGSEGV (with -d:danger) + import std/[channels, isolation] + const + N1 = 10 + N2 = 100 + var + sender: array[N1, Thread[void]] + receiver: array[5, Thread[void]] + + var chan = newChannel[seq[string]](N1 * N2) # large enough to not block + proc sendHandler() = + chan.send(isolate(@["Hello, Nim"])) + proc recvHandler() = + template fn = + let x = chan.recv() + fn() + + template benchmark() = + for t in mitems(sender): + t.createThread(sendHandler) + joinThreads(sender) + for t in mitems(receiver): + t.createThread(recvHandler) + joinThreads(receiver) + for i in 0.. Date: Fri, 19 Mar 2021 21:44:13 +0800 Subject: [PATCH 1743/3768] fix a typo (#17417) * Revert "make system random work in VM" * fix #17380 * attempt to fix bug * fix * better * fix * a bit * fix the leaks * revert * fix * better * follow up #17391 * fix * Update tchannels.nim * Update tests/stdlib/tchannels.nim * Update tchannels.nim * fix a typo --- lib/std/channels.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/channels.nim b/lib/std/channels.nim index 69f6a6ffb..3bbc8a6b6 100644 --- a/lib/std/channels.nim +++ b/lib/std/channels.nim @@ -473,7 +473,7 @@ func send*[T](c: Channel[T], src: sink Isolated[T]) {.inline.} = discard sendMpmc(c.d, data.addr, sizeof(T), false) wasMoved(data) -template send*[T](c: var Channel[T]; src: T) = +template send*[T](c: Channel[T]; src: T) = ## Helper templates for `send`. send(c, isolate(src)) From 6c1c8f51b38c9bc570a70ec8d2836b823d3584cc Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 19 Mar 2021 16:53:38 +0100 Subject: [PATCH 1744/3768] IC: green tests (#17311) * IC: renamed to_packed_ast module to ic module * IC: don't store the --forceBuild flag, makes it easier to test * IC: enable hello world test * Codegen: refactorings for IC; changed the name mangling algorithm * fixed the HCR regressions * life is too short for HCR * tconvexhull is now allowed to use deepCopy * IC exposed a stdlib bug, required a refactoring * codegen: code cleanups * IC: even if a module is outdated, its dependencies might come from disk * IC: progress * IC: better name mangling, module IDs are not stable * IC: another refactoring helping with --ic:on --gc:arc * disable arraymancer on Windows for the time being * disable arraymancer altogether * IC: make basic test work with 'nim cpp' * IC: progress on --ic:on --gc:arc * wip; name mangling for type info --- compiler/ast.nim | 2 +- .../{ccgmerge.nim => ccgmerge_unused.nim} | 0 compiler/ccgtypes.nim | 60 ++++++++-- compiler/cgen.nim | 106 ++++++------------ compiler/cgendata.nim | 4 +- compiler/ic/cbackend.nim | 4 +- compiler/ic/dce.nim | 6 +- compiler/ic/{to_packed_ast.nim => ic.nim} | 21 ++-- compiler/ic/replayer.nim | 2 +- compiler/injectdestructors.nim | 8 +- compiler/liftdestructors.nim | 75 +++++++------ compiler/lineinfos.nim | 9 +- compiler/main.nim | 2 +- compiler/modulegraphs.nim | 29 ++++- compiler/modules.nim | 7 +- compiler/msgs.nim | 26 +++++ compiler/pragmas.nim | 2 +- compiler/semdata.nim | 2 +- compiler/semexprs.nim | 2 +- compiler/semparallel.nim | 14 +-- compiler/spawn.nim | 2 +- lib/pure/bitops.nim | 2 +- lib/system.nim | 1 + lib/system/countbits_impl.nim | 25 +++++ lib/system/excpt.nim | 2 +- lib/system/sets.nim | 15 --- testament/important_packages.nim | 3 +- tests/dll/nimhcr_integration.nim | 2 +- tests/ic/thallo.nim | 1 - tests/parallel/tconvexhull.nim | 2 - 30 files changed, 249 insertions(+), 187 deletions(-) rename compiler/{ccgmerge.nim => ccgmerge_unused.nim} (100%) rename compiler/ic/{to_packed_ast.nim => ic.nim} (98%) create mode 100644 lib/system/countbits_impl.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index d82420519..50a2fb58c 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1416,7 +1416,7 @@ proc newType*(kind: TTypeKind, id: ItemId; owner: PSym): PType = lockLevel: UnspecifiedLockLevel, uniqueId: id) when false: - if result.id == 76426: + if result.itemId.module == 55 and result.itemId.item == 2: echo "KNID ", kind writeStackTrace() diff --git a/compiler/ccgmerge.nim b/compiler/ccgmerge_unused.nim similarity index 100% rename from compiler/ccgmerge.nim rename to compiler/ccgmerge_unused.nim diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 9c751b1ca..73ee9cb8a 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -40,7 +40,13 @@ proc mangleName(m: BModule; s: PSym): Rope = result = s.loc.r if result == nil: result = s.name.s.mangle.rope - result.add(idOrSig(s, m.module.name.s.mangle, m.sigConflicts)) + result.add "_" + result.add m.g.graph.ifaces[s.itemId.module].uniqueName + result.add "_" + result.add rope s.itemId.item + if m.hcrOn: + result.add "_" + result.add(idOrSig(s, m.module.name.s.mangle, m.sigConflicts)) s.loc.r = result writeMangledName(m.ndi, s, m.config) @@ -1273,12 +1279,12 @@ proc genDeepCopyProc(m: BModule; s: PSym; result: Rope) = m.s[cfsTypeInit3].addf("$1.deepcopy =(void* (N_RAW_NIMCALL*)(void*))$2;$n", [result, s.loc.r]) -proc declareNimType(m: BModule, name: string; str: Rope, ownerModule: PSym) = +proc declareNimType(m: BModule, name: string; str: Rope, module: int) = let nr = rope(name) if m.hcrOn: m.s[cfsData].addf("static $2* $1;$n", [str, nr]) m.s[cfsTypeInit1].addf("\t$1 = ($3*)hcrGetGlobal($2, \"$1\");$n", - [str, getModuleDllPath(m, ownerModule), nr]) + [str, getModuleDllPath(m, module), nr]) else: m.s[cfsData].addf("extern $2 $1;$n", [str, nr]) @@ -1351,6 +1357,9 @@ proc genTypeInfoV2Impl(m: BModule, t, origType: PType, name: Rope; info: TLineIn if t.kind == tyObject and t.len > 0 and t[0] != nil and optEnableDeepCopy in m.config.globalOptions: discard genTypeInfoV1(m, t, info) +proc moduleOpenForCodegen(m: BModule; module: int32): bool {.inline.} = + result = module < m.g.modules.len and m.g.modules[module] != nil + proc genTypeInfoV2(m: BModule, t: PType; info: TLineInfo): Rope = let origType = t # distinct types can have their own destructors @@ -1374,11 +1383,11 @@ proc genTypeInfoV2(m: BModule, t: PType; info: TLineInfo): Rope = result = "NTIv2$1_" % [rope($sig)] m.typeInfoMarkerV2[sig] = result - let owner = t.skipTypes(typedescPtrs).owner.getModule - if owner != m.module: + let owner = t.skipTypes(typedescPtrs).itemId.module + if owner != m.module.position and moduleOpenForCodegen(m, owner): # make sure the type info is created in the owner module - assert m.g.modules[owner.position] != nil - discard genTypeInfoV2(m.g.modules[owner.position], origType, info) + assert m.g.modules[owner] != nil + discard genTypeInfoV2(m.g.modules[owner], origType, info) # reference the type info as extern here discard cgsym(m, "TNimTypeV2") declareNimType(m, "TNimTypeV2", result, owner) @@ -1397,6 +1406,33 @@ proc openArrayToTuple(m: BModule; t: PType): PType = result.add p result.add getSysType(m.g.graph, t.owner.info, tyInt) +proc typeToC(t: PType): string = + ## Just for more readable names, the result doesn't have + ## to be unique. + let s = typeToString(t) + result = newStringOfCap(s.len) + for i in 0.. 0: moduleInitRequired = true if addHcrGuards: prc.add("\tif (nim_hcr_do_init_) {\n\n") - prc.add(genSectionStart(section, m.config)) prc.add(m.thing.s(section)) - prc.add(genSectionEnd(section, m.config)) if addHcrGuards: prc.add("\n\t} // nim_hcr_do_init_\n") if m.preInitProc.s(cpsInit).len > 0 or m.preInitProc.s(cpsStmts).len > 0: @@ -1740,28 +1739,21 @@ proc genModule(m: BModule, cfile: Cfile): Rope = var moduleIsEmpty = true result = getFileHeader(m.config, cfile) - result.add(genMergeInfo(m)) generateThreadLocalStorage(m) generateHeaders(m) - result.add(genSectionStart(cfsHeaders, m.config)) result.add(m.s[cfsHeaders]) if m.config.cppCustomNamespace.len > 0: result.add openNamespaceNim(m.config.cppCustomNamespace) - result.add(genSectionEnd(cfsHeaders, m.config)) - result.add(genSectionStart(cfsFrameDefines, m.config)) if m.s[cfsFrameDefines].len > 0: result.add(m.s[cfsFrameDefines]) else: result.add("#define nimfr_(x, y)\n#define nimln_(x, y)\n") - result.add(genSectionEnd(cfsFrameDefines, m.config)) for i in cfsForwardTypes..cfsProcs: if m.s[i].len > 0: moduleIsEmpty = false - result.add(genSectionStart(i, m.config)) result.add(m.s[i]) - result.add(genSectionEnd(i, m.config)) if m.s[cfsInitProc].len > 0: moduleIsEmpty = false @@ -1851,9 +1843,7 @@ proc writeHeader(m: BModule) = generateThreadLocalStorage(m) for i in cfsHeaders..cfsProcs: - result.add(genSectionStart(i, m.config)) result.add(m.s[i]) - result.add(genSectionEnd(i, m.config)) if m.config.cppCustomNamespace.len > 0 and i == cfsHeaders: result.add openNamespaceNim(m.config.cppCustomNamespace) result.add(m.s[cfsInitProc]) @@ -1952,46 +1942,26 @@ proc shouldRecompile(m: BModule; code: Rope, cfile: Cfile): bool = proc writeModule(m: BModule, pending: bool) = template onExit() = close(m.ndi, m.config) let cfile = getCFile(m) - if true or optForceFullMake in m.config.globalOptions: - if moduleHasChanged(m.g.graph, m.module): - genInitCode(m) - finishTypeDescriptions(m) - if sfMainModule in m.module.flags: - # generate main file: - genMainProc(m) - m.s[cfsProcHeaders].add(m.g.mainModProcs) - generateThreadVarsSize(m) - - var cf = Cfile(nimname: m.module.name.s, cname: cfile, - obj: completeCfilePath(m.config, toObjFile(m.config, cfile)), flags: {}) - var code = genModule(m, cf) - if code != nil or m.config.symbolFiles != disabledSf: - when hasTinyCBackend: - if m.config.cmd == cmdTcc: - tccgen.compileCCode($code, m.config) - onExit() - return - - if not shouldRecompile(m, code, cf): cf.flags = {CfileFlag.Cached} - addFileToCompile(m.config, cf) - elif pending and mergeRequired(m) and sfMainModule notin m.module.flags: - let cf = Cfile(nimname: m.module.name.s, cname: cfile, - obj: completeCfilePath(m.config, toObjFile(m.config, cfile)), flags: {}) - mergeFiles(cfile, m) + if moduleHasChanged(m.g.graph, m.module): genInitCode(m) finishTypeDescriptions(m) - var code = genModule(m, cf) - if code != nil: - if not writeRope(code, cfile): - rawMessage(m.config, errCannotOpenFile, cfile.string) - addFileToCompile(m.config, cf) - else: - # Consider: first compilation compiles ``system.nim`` and produces - # ``system.c`` but then compilation fails due to an error. This means - # that ``system.o`` is missing, so we need to call the C compiler for it: - var cf = Cfile(nimname: m.module.name.s, cname: cfile, - obj: completeCfilePath(m.config, toObjFile(m.config, cfile)), flags: {}) - if fileExists(cf.obj): cf.flags = {CfileFlag.Cached} + if sfMainModule in m.module.flags: + # generate main file: + genMainProc(m) + m.s[cfsProcHeaders].add(m.g.mainModProcs) + generateThreadVarsSize(m) + + var cf = Cfile(nimname: m.module.name.s, cname: cfile, + obj: completeCfilePath(m.config, toObjFile(m.config, cfile)), flags: {}) + var code = genModule(m, cf) + if code != nil or m.config.symbolFiles != disabledSf: + when hasTinyCBackend: + if m.config.cmd == cmdTcc: + tccgen.compileCCode($code, m.config) + onExit() + return + + if not shouldRecompile(m, code, cf): cf.flags = {CfileFlag.Cached} addFileToCompile(m.config, cf) onExit() @@ -1999,21 +1969,10 @@ proc updateCachedModule(m: BModule) = let cfile = getCFile(m) var cf = Cfile(nimname: m.module.name.s, cname: cfile, obj: completeCfilePath(m.config, toObjFile(m.config, cfile)), flags: {}) - - if mergeRequired(m) and sfMainModule notin m.module.flags: - mergeFiles(cfile, m) - genInitCode(m) - finishTypeDescriptions(m) - var code = genModule(m, cf) - if code != nil: - if not writeRope(code, cfile): - rawMessage(m.config, errCannotOpenFile, cfile.string) - addFileToCompile(m.config, cf) - else: - if sfMainModule notin m.module.flags: - genMainProc(m) - cf.flags = {CfileFlag.Cached} - addFileToCompile(m.config, cf) + if sfMainModule notin m.module.flags: + genMainProc(m) + cf.flags = {CfileFlag.Cached} + addFileToCompile(m.config, cf) proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode) = ## Also called from IC. @@ -2080,8 +2039,7 @@ proc genForwardedProcs(g: BModuleList) = while g.forwardedProcs.len > 0: let prc = g.forwardedProcs.pop() - ms = getModule(prc) - m = g.modules[ms.position] + m = g.modules[prc.itemId.module] if sfForward in prc.flags: internalError(m.config, prc.info, "still forwarded: " & prc.name.s) diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index 6a128466a..3678adacf 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -102,7 +102,7 @@ type TTypeSeq* = seq[PType] TypeCache* = Table[SigHash, Rope] - TypeCacheWithOwner* = Table[SigHash, tuple[str: Rope, owner: PSym]] + TypeCacheWithOwner* = Table[SigHash, tuple[str: Rope, owner: int32]] CodegenFlag* = enum preventStackTrace, # true if stack traces need to be prevented @@ -202,7 +202,7 @@ proc newProc*(prc: PSym, module: BModule): BProc = result.sigConflicts = initCountTable[string]() proc newModuleList*(g: ModuleGraph): BModuleList = - BModuleList(typeInfoMarker: initTable[SigHash, tuple[str: Rope, owner: PSym]](), + BModuleList(typeInfoMarker: initTable[SigHash, tuple[str: Rope, owner: int32]](), config: g.config, graph: g, nimtvDeclared: initIntSet()) iterator cgenModules*(g: BModuleList): BModule = diff --git a/compiler/ic/cbackend.nim b/compiler/ic/cbackend.nim index 52a4a3339..88b2a9477 100644 --- a/compiler/ic/cbackend.nim +++ b/compiler/ic/cbackend.nim @@ -23,7 +23,7 @@ import std/[packedsets, algorithm] import ".."/[ast, options, lineinfos, modulegraphs, cgendata, cgen, pathutils, extccomp, msgs] -import packed_ast, to_packed_ast, dce, rodfiles +import packed_ast, ic, dce, rodfiles proc unpackTree(g: ModuleGraph; thisModule: int; tree: PackedTree; n: NodePos): PNode = @@ -83,7 +83,7 @@ proc aliveSymsChanged(config: ConfigRef; position: int; alive: AliveSyms): bool proc generateCode*(g: ModuleGraph) = ## The single entry point, generate C(++) code for the entire ## Nim program aka `ModuleGraph`. - initStrTable(g.compilerprocs) + resetForBackend(g) var alive = computeAliveSyms(g.packed, g.config) for i in 0..high(g.packed): diff --git a/compiler/ic/dce.nim b/compiler/ic/dce.nim index c7d66465d..0918fc379 100644 --- a/compiler/ic/dce.nim +++ b/compiler/ic/dce.nim @@ -12,7 +12,7 @@ import std / [intsets, tables] import ".." / [ast, options, lineinfos, types] -import packed_ast, to_packed_ast, bitabs +import packed_ast, ic, bitabs type AliveSyms* = seq[IntSet] @@ -111,7 +111,7 @@ proc aliveCode(c: var AliveContext; g: PackedModuleGraph; tree: PackedTree; n: N let otherModule = toFileIndexCached(c.decoder, g, c.thisModule, m).int followLater(c, g, otherModule, item) of nkMacroDef, nkTemplateDef, nkTypeSection, nkTypeOfExpr, - nkCommentStmt, nkIteratorDef, nkIncludeStmt, + nkCommentStmt, nkIncludeStmt, nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt, nkFromStmt, nkStaticStmt: discard @@ -121,7 +121,7 @@ proc aliveCode(c: var AliveContext; g: PackedModuleGraph; tree: PackedTree; n: N aliveCode(c, g, tree, son) of nkChckRangeF, nkChckRange64, nkChckRange: rangeCheckAnalysis(c, g, tree, n) - of nkProcDef, nkConverterDef, nkMethodDef, nkLambda, nkDo, nkFuncDef: + of nkProcDef, nkConverterDef, nkMethodDef, nkFuncDef, nkIteratorDef: if n.firstSon.kind == nkSym and isNotGeneric(n): let item = n.firstSon.operand if isExportedToC(c, g, item): diff --git a/compiler/ic/to_packed_ast.nim b/compiler/ic/ic.nim similarity index 98% rename from compiler/ic/to_packed_ast.nim rename to compiler/ic/ic.nim index 44902143d..99a68e0f0 100644 --- a/compiler/ic/to_packed_ast.nim +++ b/compiler/ic/ic.nim @@ -95,6 +95,7 @@ proc rememberStartupConfig*(dest: var PackedConfig, config: ConfigRef) = template rem(x) = dest.x = config.x primConfigFields rem + dest.globalOptions.excl optForceFullMake proc hashFileCached(conf: ConfigRef; fileIdx: FileIndex): string = result = msgs.getHash(conf, fileIdx) @@ -486,8 +487,14 @@ proc storeInstantiation*(c: var PackedEncoder; m: var PackedModule; s: PSym; i: concreteTypes: t) toPackedGeneratedProcDef(i.sym, c, m) -proc loadError(err: RodFileError; filename: AbsoluteFile) = - echo "Error: ", $err, " loading file: ", filename.string +proc loadError(err: RodFileError; filename: AbsoluteFile; config: ConfigRef;) = + case err + of cannotOpen: + rawMessage(config, warnCannotOpenFile, filename.string) + of includeFileChanged: + rawMessage(config, warnFileChanged, filename.string) + else: + echo "Error: ", $err, " loading file: ", filename.string proc loadRodFile*(filename: AbsoluteFile; m: var PackedModule; config: ConfigRef; ignoreConfig = false): RodFileError = @@ -718,7 +725,7 @@ proc loadProcHeader(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: result = newNodeIT(k, translateLineInfo(c, g, thisModule, n.info), loadType(c, g, thisModule, n.typ)) result.flags = n.flags - assert k in {nkProcDef, nkMethodDef, nkIteratorDef, nkFuncDef, nkConverterDef} + assert k in {nkProcDef, nkMethodDef, nkIteratorDef, nkFuncDef, nkConverterDef, nkLambda} var i = 0 for n0 in sonsReadonly(tree, n): if i != bodyPos: @@ -932,10 +939,10 @@ proc needsRecompile(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache else: g[m] = LoadedModule(status: outdated, module: g[m].module) else: - loadError(err, rod) + loadError(err, rod, conf) g[m].status = outdated result = true - when false: loadError(err, rod) + when false: loadError(err, rod, conf) of loading, loaded: # For loading: Assume no recompile is required. result = false @@ -951,8 +958,8 @@ proc moduleFromRodFile*(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentC result = g[int fileIdx].module assert result != nil assert result.position == int(fileIdx) - for m in cachedModules: - loadToReplayNodes(g, conf, cache, m, g[int m]) + for m in cachedModules: + loadToReplayNodes(g, conf, cache, m, g[int m]) template setupDecoder() {.dirty.} = var decoder = PackedDecoder( diff --git a/compiler/ic/replayer.nim b/compiler/ic/replayer.nim index 05c473090..61aa0e697 100644 --- a/compiler/ic/replayer.nim +++ b/compiler/ic/replayer.nim @@ -16,7 +16,7 @@ import ".." / [ast, modulegraphs, trees, extccomp, btrees, import tables -import packed_ast, to_packed_ast, bitabs +import packed_ast, ic, bitabs proc replayStateChanges*(module: PSym; g: ModuleGraph) = let list = module.ast diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index d20ed8e26..b65391252 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -350,7 +350,7 @@ proc genMarkCyclic(c: var Con; result, dest: PNode) = if t.kind == tyRef: result.add callCodegenProc(c.graph, "nimMarkCyclic", dest.info, dest) else: - let xenv = genBuiltin(c.graph, mAccessEnv, "accessEnv", dest) + let xenv = genBuiltin(c.graph, c.idgen, mAccessEnv, "accessEnv", dest) xenv.typ = getSysType(c.graph, dest.info, tyPointer) result.add callCodegenProc(c.graph, "nimMarkCyclic", dest.info, xenv) @@ -395,21 +395,21 @@ It is best to factor out piece of object that needs custom destructor into separ cond.add le cond.add tmp let notExpr = newNodeIT(nkPrefix, n.info, getSysType(c.graph, unknownLineInfo, tyBool)) - notExpr.add newSymNode(createMagic(c.graph, "not", mNot)) + notExpr.add newSymNode(createMagic(c.graph, c.idgen, "not", mNot)) notExpr.add cond result.add newTree(nkIfStmt, newTree(nkElifBranch, notExpr, c.genOp(branchDestructor, le))) result.add newTree(nkFastAsgn, le, tmp) proc genWasMoved(c: var Con, n: PNode): PNode = result = newNodeI(nkCall, n.info) - result.add(newSymNode(createMagic(c.graph, "wasMoved", mWasMoved))) + result.add(newSymNode(createMagic(c.graph, c.idgen, "wasMoved", mWasMoved))) result.add copyTree(n) #mWasMoved does not take the address #if n.kind != nkSym: # message(c.graph.config, n.info, warnUser, "wasMoved(" & $n & ")") proc genDefaultCall(t: PType; c: Con; info: TLineInfo): PNode = result = newNodeI(nkCall, info) - result.add(newSymNode(createMagic(c.graph, "default", mDefault))) + result.add(newSymNode(createMagic(c.graph, c.idgen, "default", mDefault))) result.typ = t proc destructiveMoveVar(n: PNode; c: var Con; s: var Scope): PNode = diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 889c65cc0..980e77e4b 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -70,16 +70,19 @@ proc newAsgnStmt(le, ri: PNode): PNode = result[0] = le result[1] = ri -proc genBuiltin*(g: ModuleGraph; magic: TMagic; name: string; i: PNode): PNode = +proc genBuiltin*(g: ModuleGraph; idgen: IdGenerator; magic: TMagic; name: string; i: PNode): PNode = result = newNodeI(nkCall, i.info) - result.add createMagic(g, name, magic).newSymNode + result.add createMagic(g, idgen, name, magic).newSymNode result.add i +proc genBuiltin(c: var TLiftCtx; magic: TMagic; name: string; i: PNode): PNode = + result = genBuiltin(c.g, c.idgen, magic, name, i) + proc defaultOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = if c.kind in {attachedAsgn, attachedDeepCopy, attachedSink}: body.add newAsgnStmt(x, y) elif c.kind == attachedDestructor and c.addMemReset: - let call = genBuiltin(c.g, mDefault, "default", x) + let call = genBuiltin(c, mDefault, "default", x) call.typ = t body.add newAsgnStmt(x, call) @@ -93,7 +96,7 @@ proc genAddr(c: var TLiftCtx; x: PNode): PNode = proc genWhileLoop(c: var TLiftCtx; i, dest: PNode): PNode = result = newNodeI(nkWhileStmt, c.info, 2) - let cmp = genBuiltin(c.g, mLtI, "<", i) + let cmp = genBuiltin(c, mLtI, "<", i) cmp.add genLen(c.g, dest) cmp.typ = getSysType(c.g, c.info, tyBool) result[0] = cmp @@ -116,10 +119,10 @@ proc genContainerOf(c: var TLiftCtx; objType: PType, field, x: PSym): PNode = dotExpr.add newNodeIT(nkType, c.info, objType) dotExpr.add newSymNode(field) - let offsetOf = genBuiltin(c.g, mOffsetOf, "offsetof", dotExpr) + let offsetOf = genBuiltin(c, mOffsetOf, "offsetof", dotExpr) offsetOf.typ = intType - let minusExpr = genBuiltin(c.g, mSubI, "-", castExpr1) + let minusExpr = genBuiltin(c, mSubI, "-", castExpr1) minusExpr.typ = intType minusExpr.add offsetOf @@ -135,7 +138,7 @@ proc destructorCall(c: var TLiftCtx; op: PSym; x: PNode): PNode = if sfNeverRaises notin op.flags: c.canRaise = true if c.addMemReset: - result = newTree(nkStmtList, destroy, genBuiltin(c.g, mWasMoved, "wasMoved", x)) + result = newTree(nkStmtList, destroy, genBuiltin(c, mWasMoved, "wasMoved", x)) else: result = destroy @@ -237,7 +240,7 @@ proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) = #body.add newAsgnStmt(blob, x) var wasMovedCall = newNodeI(nkCall, c.info) - wasMovedCall.add(newSymNode(createMagic(c.g, "wasMoved", mWasMoved))) + wasMovedCall.add(newSymNode(createMagic(c.g, c.idgen, "wasMoved", mWasMoved))) wasMovedCall.add x # mWasMoved does not take the address body.add wasMovedCall @@ -443,25 +446,25 @@ proc declareTempOf(c: var TLiftCtx; body: PNode; value: PNode): PNode = body.add v proc addIncStmt(c: var TLiftCtx; body, i: PNode) = - let incCall = genBuiltin(c.g, mInc, "inc", i) + let incCall = genBuiltin(c, mInc, "inc", i) incCall.add lowerings.newIntLit(c.g, c.info, 1) body.add incCall -proc newSeqCall(g: ModuleGraph; x, y: PNode): PNode = +proc newSeqCall(c: var TLiftCtx; x, y: PNode): PNode = # don't call genAddr(c, x) here: - result = genBuiltin(g, mNewSeq, "newSeq", x) - let lenCall = genBuiltin(g, mLengthSeq, "len", y) - lenCall.typ = getSysType(g, x.info, tyInt) + result = genBuiltin(c, mNewSeq, "newSeq", x) + let lenCall = genBuiltin(c, mLengthSeq, "len", y) + lenCall.typ = getSysType(c.g, x.info, tyInt) result.add lenCall -proc setLenStrCall(g: ModuleGraph; x, y: PNode): PNode = - let lenCall = genBuiltin(g, mLengthStr, "len", y) - lenCall.typ = getSysType(g, x.info, tyInt) - result = genBuiltin(g, mSetLengthStr, "setLen", x) # genAddr(g, x)) +proc setLenStrCall(c: var TLiftCtx; x, y: PNode): PNode = + let lenCall = genBuiltin(c, mLengthStr, "len", y) + lenCall.typ = getSysType(c.g, x.info, tyInt) + result = genBuiltin(c, mSetLengthStr, "setLen", x) # genAddr(g, x)) result.add lenCall proc setLenSeqCall(c: var TLiftCtx; t: PType; x, y: PNode): PNode = - let lenCall = genBuiltin(c.g, mLengthSeq, "len", y) + let lenCall = genBuiltin(c, mLengthSeq, "len", y) lenCall.typ = getSysType(c.g, x.info, tyInt) var op = getSysMagic(c.g, x.info, "setLen", mSetLengthSeq) op = instantiateGeneric(c, op, t, t) @@ -487,7 +490,7 @@ proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = body.add setLenSeqCall(c, t, x, y) forallElements(c, t, body, x, y) of attachedSink: - let moveCall = genBuiltin(c.g, mMove, "move", x) + let moveCall = genBuiltin(c, mMove, "move", x) moveCall.add y doAssert t.destructor != nil moveCall.add destructorCall(c, t.destructor, x) @@ -495,13 +498,13 @@ proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = of attachedDestructor: # destroy all elements: forallElements(c, t, body, x, y) - body.add genBuiltin(c.g, mDestroy, "destroy", x) + body.add genBuiltin(c, mDestroy, "destroy", x) of attachedTrace: # follow all elements: forallElements(c, t, body, x, y) of attachedDispose: forallElements(c, t, body, x, y) - body.add genBuiltin(c.g, mDestroy, "destroy", x) + body.add genBuiltin(c, mDestroy, "destroy", x) proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = createTypeBoundOps(c.g, c.c, t, body.info, c.idgen) @@ -521,7 +524,7 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = body.add newHookCall(c, t.assignment, x, y) of attachedSink: # we always inline the move for better performance: - let moveCall = genBuiltin(c.g, mMove, "move", x) + let moveCall = genBuiltin(c, mMove, "move", x) moveCall.add y doAssert t.destructor != nil moveCall.add destructorCall(c, t.destructor, x) @@ -549,13 +552,13 @@ proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = of attachedAsgn, attachedDeepCopy: body.add callCodegenProc(c.g, "nimAsgnStrV2", c.info, genAddr(c, x), y) of attachedSink: - let moveCall = genBuiltin(c.g, mMove, "move", x) + let moveCall = genBuiltin(c, mMove, "move", x) moveCall.add y doAssert t.destructor != nil moveCall.add destructorCall(c, t.destructor, x) body.add moveCall of attachedDestructor, attachedDispose: - body.add genBuiltin(c.g, mDestroy, "destroy", x) + body.add genBuiltin(c, mDestroy, "destroy", x) of attachedTrace: discard "strings are atomic and have no inner elements that are to trace" @@ -599,7 +602,7 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = if isFinal(elemType): addDestructorCall(c, elemType, actions, genDeref(tmp, nkDerefExpr)) - var alignOf = genBuiltin(c.g, mAlignOf, "alignof", newNodeIT(nkType, c.info, elemType)) + var alignOf = genBuiltin(c, mAlignOf, "alignof", newNodeIT(nkType, c.info, elemType)) alignOf.typ = getSysType(c.g, c.info, tyInt) actions.add callCodegenProc(c.g, "nimRawDispose", c.info, tmp, alignOf) else: @@ -609,7 +612,7 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = var cond: PNode if isCyclic: if isFinal(elemType): - let typInfo = genBuiltin(c.g, mGetTypeInfoV2, "getTypeInfoV2", newNodeIT(nkType, x.info, elemType)) + let typInfo = genBuiltin(c, mGetTypeInfoV2, "getTypeInfoV2", newNodeIT(nkType, x.info, elemType)) typInfo.typ = getSysType(c.g, c.info, tyPointer) cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicStatic", c.info, tmp, typInfo) else: @@ -641,7 +644,7 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = of attachedDeepCopy: assert(false, "cannot happen") of attachedTrace: if isFinal(elemType): - let typInfo = genBuiltin(c.g, mGetTypeInfoV2, "getTypeInfoV2", newNodeIT(nkType, x.info, elemType)) + let typInfo = genBuiltin(c, mGetTypeInfoV2, "getTypeInfoV2", newNodeIT(nkType, x.info, elemType)) typInfo.typ = getSysType(c.g, c.info, tyPointer) body.add callCodegenProc(c.g, "nimTraceRef", c.info, genAddrOf(x, c.idgen), typInfo, y) else: @@ -659,7 +662,7 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = ## Closures are really like refs except they always use a virtual destructor ## and we need to do the refcounting only on the ref field which we call 'xenv': - let xenv = genBuiltin(c.g, mAccessEnv, "accessEnv", x) + let xenv = genBuiltin(c, mAccessEnv, "accessEnv", x) xenv.typ = getSysType(c.g, c.info, tyPointer) let isCyclic = c.g.config.selectedGC == gcOrc @@ -687,7 +690,7 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = body.add genIf(c, cond, actions) body.add newAsgnStmt(x, y) of attachedAsgn: - let yenv = genBuiltin(c.g, mAccessEnv, "accessEnv", y) + let yenv = genBuiltin(c, mAccessEnv, "accessEnv", y) yenv.typ = getSysType(c.g, c.info, tyPointer) if isCyclic: body.add genIf(c, yenv, callCodegenProc(c.g, "nimIncRefCyclic", c.info, yenv, getCycleParam(c))) @@ -741,11 +744,11 @@ proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = let elemType = t.lastSon #fillBody(c, elemType, actions, genDeref(x), genDeref(y)) - #var disposeCall = genBuiltin(c.g, mDispose, "dispose", x) + #var disposeCall = genBuiltin(c, mDispose, "dispose", x) if isFinal(elemType): addDestructorCall(c, elemType, actions, genDeref(x, nkDerefExpr)) - var alignOf = genBuiltin(c.g, mAlignOf, "alignof", newNodeIT(nkType, c.info, elemType)) + var alignOf = genBuiltin(c, mAlignOf, "alignof", newNodeIT(nkType, c.info, elemType)) alignOf.typ = getSysType(c.g, c.info, tyInt) actions.add callCodegenProc(c.g, "nimRawDispose", c.info, x, alignOf) else: @@ -767,12 +770,12 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = # have to go through some indirection; we delegate this to the codegen: let call = newNodeI(nkCall, c.info, 2) call.typ = t - call[0] = newSymNode(createMagic(c.g, "deepCopy", mDeepCopy)) + call[0] = newSymNode(createMagic(c.g, c.idgen, "deepCopy", mDeepCopy)) call[1] = y body.add newAsgnStmt(x, call) elif (optOwnedRefs in c.g.config.globalOptions and optRefCheck in c.g.config.options) or c.g.config.selectedGC in {gcArc, gcOrc}: - let xx = genBuiltin(c.g, mAccessEnv, "accessEnv", x) + let xx = genBuiltin(c, mAccessEnv, "accessEnv", x) xx.typ = getSysType(c.g, c.info, tyPointer) case c.kind of attachedSink: @@ -781,7 +784,7 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = body.add genIf(c, xx, callCodegenProc(c.g, "nimDecWeakRef", c.info, xx)) body.add newAsgnStmt(x, y) of attachedAsgn: - let yy = genBuiltin(c.g, mAccessEnv, "accessEnv", y) + let yy = genBuiltin(c, mAccessEnv, "accessEnv", y) yy.typ = getSysType(c.g, c.info, tyPointer) body.add genIf(c, yy, callCodegenProc(c.g, "nimIncRef", c.info, yy)) body.add genIf(c, xx, callCodegenProc(c.g, "nimDecWeakRef", c.info, xx)) @@ -796,7 +799,7 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = of attachedTrace, attachedDispose: discard proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = - let xx = genBuiltin(c.g, mAccessEnv, "accessEnv", x) + let xx = genBuiltin(c, mAccessEnv, "accessEnv", x) xx.typ = getSysType(c.g, c.info, tyPointer) var actions = newNodeI(nkStmtList, c.info) #discard addDestructorCall(c, elemType, newNodeI(nkStmtList, c.info), genDeref(xx)) @@ -859,7 +862,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) = discard considerUserDefinedOp(c, t, body, x, y) elif tfHasAsgn in t.flags: if c.kind in {attachedAsgn, attachedSink, attachedDeepCopy}: - body.add newSeqCall(c.g, x, y) + body.add newSeqCall(c, x, y) forallElements(c, t, body, x, y) else: defaultOp(c, t, body, x, y) diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index 96a382453..d8f82aea0 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -59,7 +59,10 @@ type warnLockLevel = "LockLevel", warnResultShadowed = "ResultShadowed", warnInconsistentSpacing = "Spacing", warnCaseTransition = "CaseTransition", warnCycleCreated = "CycleCreated", warnObservableStores = "ObservableStores", - warnUser = "User", warnStrictNotNil = "StrictNotNil", + warnStrictNotNil = "StrictNotNil", + warnCannotOpen = "CannotOpen", + warnFileChanged = "FileChanged", + warnUser = "User", hintSuccess = "Success", hintSuccessX = "SuccessX", hintCC = "CC", hintLineTooLong = "LineTooLong", hintXDeclaredButNotUsed = "XDeclaredButNotUsed", @@ -133,8 +136,10 @@ const warnCaseTransition: "Potential object case transition, instantiate new object instead", warnCycleCreated: "$1", warnObservableStores: "observable stores to '$1'", - warnUser: "$1", warnStrictNotNil: "$1", + warnCannotOpen: "cannot open: $1", + warnFileChanged: "file changed: $1", + warnUser: "$1", hintSuccess: "operation successful: $#", # keep in sync with `testament.isSuccess` hintSuccessX: "${loc} lines; ${sec}s; $mem; $build build; proj: $project; out: $output", diff --git a/compiler/main.nim b/compiler/main.nim index 895923606..b61cdcadb 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -22,7 +22,7 @@ import modulegraphs, tables, lineinfos, pathutils, vmprofiler import ic / cbackend -from ic / to_packed_ast import rodViewer +from ic / ic import rodViewer when not defined(leanCompiler): import jsgen, docgen, docgen2 diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index b8a8b3e2c..de3773ca5 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -12,9 +12,9 @@ ## or stored in a rod-file. import ast, astalgo, intsets, tables, options, lineinfos, hashes, idents, - btrees, md5 + btrees, md5, ropes, msgs -import ic / [packed_ast, to_packed_ast] +import ic / [packed_ast, ic] type SigHash* = distinct MD5Digest @@ -30,6 +30,7 @@ type patterns*: seq[LazySym] pureEnums*: seq[LazySym] interf: TStrTable + uniqueName*: Rope Operators* = object opNot*, opContains*, opLe*, opLt*, opAnd*, opOr*, opIsNil*, opEq*: PSym @@ -117,6 +118,15 @@ type close: TPassClose, isFrontend: bool] +proc resetForBackend*(g: ModuleGraph) = + initStrTable(g.compilerprocs) + g.typeInstCache.clear() + g.procInstCache.clear() + for a in mitems(g.attachedOps): + a.clear() + g.methodsPerType.clear() + g.enumToStringProcs.clear() + const cb64 = [ "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", @@ -169,7 +179,7 @@ proc initEncoder*(g: ModuleGraph; module: PSym) = let id = module.position if id >= g.encoders.len: setLen g.encoders, id+1 - to_packed_ast.initEncoder(g.encoders[id], + ic.initEncoder(g.encoders[id], g.packed[id].fromDisk, module, g.config, g.startupPackedConfig) type @@ -359,11 +369,14 @@ else: proc stopCompile*(g: ModuleGraph): bool {.inline.} = result = g.doStopCompile != nil and g.doStopCompile() -proc createMagic*(g: ModuleGraph; name: string, m: TMagic): PSym = - result = newSym(skProc, getIdent(g.cache, name), nextSymId(g.idgen), nil, unknownLineInfo, {}) +proc createMagic*(g: ModuleGraph; idgen: IdGenerator; name: string, m: TMagic): PSym = + result = newSym(skProc, getIdent(g.cache, name), nextSymId(idgen), nil, unknownLineInfo, {}) result.magic = m result.flags = {sfNeverRaises} +proc createMagic(g: ModuleGraph; name: string, m: TMagic): PSym = + result = createMagic(g, g.idgen, name, m) + proc registerModule*(g: ModuleGraph; m: PSym) = assert m != nil assert m.kind == skModule @@ -374,9 +387,13 @@ proc registerModule*(g: ModuleGraph; m: PSym) = if m.position >= g.packed.len: setLen(g.packed, m.position + 1) - g.ifaces[m.position] = Iface(module: m, converters: @[], patterns: @[]) + g.ifaces[m.position] = Iface(module: m, converters: @[], patterns: @[], + uniqueName: rope(uniqueModuleName(g.config, FileIndex(m.position)))) initStrTable(g.ifaces[m.position].interf) +proc registerModuleById*(g: ModuleGraph; m: FileIndex) = + registerModule(g, g.packed[int m].module) + proc initOperators(g: ModuleGraph): Operators = # These are safe for IC. result.opLe = createMagic(g, "<=", mLeI) diff --git a/compiler/modules.nim b/compiler/modules.nim index 7503d4da2..7d7a2b6f7 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -108,9 +108,10 @@ proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags): P if sfSystemModule in flags: graph.systemModule = result partialInitModule(result, graph, fileIdx, filename) - for m in cachedModules: - replayStateChanges(graph.packed[m.int].module, graph) - replayGenericCacheInformation(graph, m.int) + for m in cachedModules: + registerModuleById(graph, m) + replayStateChanges(graph.packed[m.int].module, graph) + replayGenericCacheInformation(graph, m.int) elif graph.isDirty(result): result.flags.excl sfDirty # reset module fields: diff --git a/compiler/msgs.nim b/compiler/msgs.nim index b384dad24..bbe40507f 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -115,6 +115,7 @@ proc fileInfoIdx*(conf: ConfigRef; filename: AbsoluteFile; isKnownFile: var bool else: isKnownFile = false result = conf.m.fileInfos.len.FileIndex + #echo "ID ", result.int, " ", canon2 conf.m.fileInfos.add(newFileInfo(canon, if pseudoPath: RelativeFile filename else: relativeTo(canon, conf.projectPath))) conf.m.filenameToIndexTbl[canon2] = result @@ -630,3 +631,28 @@ template listMsg(title, r) = proc listWarnings*(conf: ConfigRef) = listMsg("Warnings:", warnMin..warnMax) proc listHints*(conf: ConfigRef) = listMsg("Hints:", hintMin..hintMax) + +proc uniqueModuleName*(conf: ConfigRef; fid: FileIndex): string = + ## The unique module name is guaranteed to only contain {'A'..'Z', 'a'..'z', '0'..'9', '_'} + ## so that it is useful as a C identifier snippet. + let path = AbsoluteFile toFullPath(conf, fid) + let rel = + if path.string.startsWith(conf.libpath.string): + relativeTo(path, conf.libpath).string + else: + relativeTo(path, conf.projectPath).string + let trunc = if rel.endsWith(".nim"): rel.len - len(".nim") else: rel.len + result = newStringOfCap(trunc) + for i in 0.. 0: result = shallowCopy(n) for i in 0.. 0: diff --git a/compiler/spawn.nim b/compiler/spawn.nim index 61bcc424b..54ed51dbc 100644 --- a/compiler/spawn.nim +++ b/compiler/spawn.nim @@ -245,7 +245,7 @@ proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType; # important special case: we always create a zero-copy slice: let slice = newNodeI(nkCall, n.info, 4) slice.typ = n.typ - slice[0] = newSymNode(createMagic(g, "slice", mSlice)) + slice[0] = newSymNode(createMagic(g, idgen, "slice", mSlice)) slice[0].typ = getSysType(g, n.info, tyInt) # fake type var fieldB = newSym(skField, tmpName, nextSymId idgen, objType.owner, n.info, g.config.options) fieldB.typ = getSysType(g, n.info, tyInt) diff --git a/lib/pure/bitops.nim b/lib/pure/bitops.nim index b75b0cf9a..57e3c7989 100644 --- a/lib/pure/bitops.nim +++ b/lib/pure/bitops.nim @@ -439,7 +439,7 @@ func fastlog2Nim(x: uint64): int {.inline.} = # sets.nim cannot import bitops, but bitops can use include # system/sets to eliminate code duplication. sets.nim defines # countBits32 and countBits64. -include system/sets +import system/countbits_impl template countSetBitsNim(n: uint32): int = countBits32(n) template countSetBitsNim(n: uint64): int = countBits64(n) diff --git a/lib/system.nim b/lib/system.nim index f0c3da517..574043125 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2339,6 +2339,7 @@ when notJSnotNims: when hostOS != "standalone" and hostOS != "any": include "system/dyncalls" + import system/countbits_impl include "system/sets" when defined(gogc): diff --git a/lib/system/countbits_impl.nim b/lib/system/countbits_impl.nim new file mode 100644 index 000000000..6c85612e2 --- /dev/null +++ b/lib/system/countbits_impl.nim @@ -0,0 +1,25 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2012 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Contains the used algorithms for counting bits. + +proc countBits32*(n: uint32): int {.compilerproc.} = + # generic formula is from: https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel + var v = uint32(n) + v = v - ((v shr 1'u32) and 0x55555555'u32) + v = (v and 0x33333333'u32) + ((v shr 2'u32) and 0x33333333'u32) + result = (((v + (v shr 4'u32) and 0xF0F0F0F'u32) * 0x1010101'u32) shr 24'u32).int + +proc countBits64*(n: uint64): int {.compilerproc, inline.} = + # generic formula is from: https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel + var v = uint64(n) + v = v - ((v shr 1'u64) and 0x5555555555555555'u64) + v = (v and 0x3333333333333333'u64) + ((v shr 2'u64) and 0x3333333333333333'u64) + v = (v + (v shr 4'u64) and 0x0F0F0F0F0F0F0F0F'u64) + result = ((v * 0x0101010101010101'u64) shr 56'u64).int diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index 8f7e2cbc7..06fa45097 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -567,7 +567,7 @@ when defined(cpp) and appType != "lib" and not gotoBasedExceptions and type StdException {.importcpp: "std::exception", header: "".} = object - proc what(ex: StdException): cstring {.importcpp: "((char *)#.what())".} + proc what(ex: StdException): cstring {.importcpp: "((char *)#.what())", nodecl.} proc setTerminate(handler: proc() {.noconv.}) {.importc: "std::set_terminate", header: "".} diff --git a/lib/system/sets.nim b/lib/system/sets.nim index 42c448848..04e10ba04 100644 --- a/lib/system/sets.nim +++ b/lib/system/sets.nim @@ -14,21 +14,6 @@ type # bitops can't be imported here, therefore the code duplication. -proc countBits32(n: uint32): int {.compilerproc.} = - # generic formula is from: https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel - var v = uint32(n) - v = v - ((v shr 1'u32) and 0x55555555'u32) - v = (v and 0x33333333'u32) + ((v shr 2'u32) and 0x33333333'u32) - result = (((v + (v shr 4'u32) and 0xF0F0F0F'u32) * 0x1010101'u32) shr 24'u32).int - -proc countBits64(n: uint64): int {.compilerproc, inline.} = - # generic formula is from: https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel - var v = uint64(n) - v = v - ((v shr 1'u64) and 0x5555555555555555'u64) - v = (v and 0x3333333333333333'u64) + ((v shr 2'u64) and 0x3333333333333333'u64) - v = (v + (v shr 4'u64) and 0x0F0F0F0F0F0F0F0F'u64) - result = ((v * 0x0101010101010101'u64) shr 56'u64).int - proc cardSet(s: NimSet, len: int): int {.compilerproc, inline.} = var i = 0 result = 0 diff --git a/testament/important_packages.nim b/testament/important_packages.nim index a3b6db57d..1dd7c69ca 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -30,7 +30,8 @@ proc pkg(name: string; cmd = "nimble test"; url = "", useHead = true) = # pkg "alea" pkg "argparse" -pkg "arraymancer", "nim c tests/tests_cpu.nim" +when false: + pkg "arraymancer", "nim c tests/tests_cpu.nim" # pkg "ast_pattern_matching", "nim c -r --oldgensym:on tests/test1.nim" pkg "awk" pkg "bigints", url = "https://github.com/Araq/nim-bigints" diff --git a/tests/dll/nimhcr_integration.nim b/tests/dll/nimhcr_integration.nim index 58851b5c4..ac34f1f85 100644 --- a/tests/dll/nimhcr_integration.nim +++ b/tests/dll/nimhcr_integration.nim @@ -1,5 +1,5 @@ discard """ - disabled: "openbsd" + disabled: "true" output: ''' main: HELLO! main: hasAnyModuleChanged? true diff --git a/tests/ic/thallo.nim b/tests/ic/thallo.nim index c29a0820c..7ead7c8ba 100644 --- a/tests/ic/thallo.nim +++ b/tests/ic/thallo.nim @@ -1,6 +1,5 @@ discard """ output: "Hello World" - disabled: "true" """ const str = "Hello World" diff --git a/tests/parallel/tconvexhull.nim b/tests/parallel/tconvexhull.nim index ebadb874d..0a07e6b76 100644 --- a/tests/parallel/tconvexhull.nim +++ b/tests/parallel/tconvexhull.nim @@ -1,8 +1,6 @@ discard """ output: ''' ''' - -ccodeCheck: "\\i ! @'deepCopy(' .*" """ # parallel convex hull for Nim bigbreak From e332c20ba7f0fffc0ad42751a99e11232973f27f Mon Sep 17 00:00:00 2001 From: flywind Date: Fri, 19 Mar 2021 23:54:10 +0800 Subject: [PATCH 1745/3768] follow up #17276 (#17355) * improve test coverage for isolation * a bit better * rename channels to channels_builtin * follow up #17276 * fix * Update lib/std/private/jsutils.nim --- lib/std/private/jsutils.nim | 12 ++++++++++-- tools/kochdocs.nim | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/std/private/jsutils.nim b/lib/std/private/jsutils.nim index cf58b3b81..32c717c56 100644 --- a/lib/std/private/jsutils.nim +++ b/lib/std/private/jsutils.nim @@ -1,4 +1,4 @@ -when defined(js): +when defined(js) or defined(nimdoc): import std/jsbigints type @@ -40,4 +40,12 @@ when defined(js): proc isInteger*[T](x: T): bool {.importjs: "Number.isInteger(#)".} - proc isSafeInteger*[T](x: T): bool {.importjs: "Number.isSafeInteger(#)".} + proc isSafeInteger*[T](x: T): bool {.importjs: "Number.isSafeInteger(#)".} = + runnableExamples: + import std/jsffi + assert not "123".toJs.isSafeInteger + assert 123.toJs.isSafeInteger + assert 9007199254740991.toJs.isSafeInteger + assert not 9007199254740992.toJs.isSafeInteger + + let maxSafeInteger* {.importjs: "Number.MAX_SAFE_INTEGER".} : int64 diff --git a/tools/kochdocs.nim b/tools/kochdocs.nim index 8f4dd87a9..621dc643f 100644 --- a/tools/kochdocs.nim +++ b/tools/kochdocs.nim @@ -14,7 +14,7 @@ const webUploadOutput = "web/upload" var nimExe*: string -const allowList = ["jsbigints.nim", "jsheaders.nim", "jsformdata.nim", "jsfetch.nim"] +const allowList = ["jsbigints.nim", "jsheaders.nim", "jsformdata.nim", "jsfetch.nim", "jsutils.nim"] template isJsOnly(file: string): bool = file.isRelativeTo("lib/js") or From 8e8bea9044f0ae1f0583cb6130f6fbac390bf26a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=C3=A9ctor=20M=2E=20Monacci?= Date: Fri, 19 Mar 2021 15:19:41 -0300 Subject: [PATCH 1746/3768] Clarify behaviour of char replace (#17339) Clarify behaviour of char replace by adding ```every ocurrence of character``` --- lib/pure/strutils.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 36a211545..510f0682d 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -2079,7 +2079,7 @@ func replace*(s, sub: string, by = ""): string {.rtl, func replace*(s: string, sub, by: char): string {.rtl, extern: "nsuReplaceChar".} = - ## Replaces `sub` in `s` by the character `by`. + ## Replaces every occurence of character `sub` in `s` by the character `by`. ## ## Optimized version of `replace <#replace,string,string,string>`_ for ## characters. From 430c30299f4c1cc5632d89c37487a78d4b4b3fde Mon Sep 17 00:00:00 2001 From: haxscramper Date: Fri, 19 Mar 2021 22:16:52 +0300 Subject: [PATCH 1747/3768] [FIX] use `mixin` for strscans.scanp (#17371) --- lib/pure/strscans.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/pure/strscans.nim b/lib/pure/strscans.nim index 6bedf2de2..73b53e3d6 100644 --- a/lib/pure/strscans.nim +++ b/lib/pure/strscans.nim @@ -577,6 +577,7 @@ macro scanp*(input, idx: typed; pattern: varargs[untyped]): bool = of nnkCallKinds: # *{'A'..'Z'} !! s.add(!_) template buildWhile(input, idx, init, cond, action): untyped = + mixin hasNxt while hasNxt(input, idx): init if not cond: break @@ -688,4 +689,4 @@ macro scanp*(input, idx: typed; pattern: varargs[untyped]): bool = result.add toIfChain(conds, idx, res, 0) result.add res when defined(debugScanp): - echo repr result \ No newline at end of file + echo repr result From 9997b42c3512d0422910704d6472c486efd4db19 Mon Sep 17 00:00:00 2001 From: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Date: Fri, 19 Mar 2021 22:22:48 +0100 Subject: [PATCH 1748/3768] Use importjs (#17422) --- lib/js/asyncjs.nim | 8 ++-- lib/js/dom_extensions.nim | 2 +- lib/js/jsffi.nim | 98 +++++++++++++++++++-------------------- lib/std/jsbigints.nim | 4 +- 4 files changed, 56 insertions(+), 56 deletions(-) diff --git a/lib/js/asyncjs.nim b/lib/js/asyncjs.nim index 73af232b4..c62ac633f 100644 --- a/lib/js/asyncjs.nim +++ b/lib/js/asyncjs.nim @@ -70,7 +70,7 @@ type future*: T ## Wraps the return type of an asynchronous procedure. - PromiseJs* {.importcpp: "Promise".} = ref object + PromiseJs* {.importjs: "Promise".} = ref object ## A JavaScript Promise. @@ -113,7 +113,7 @@ proc generateJsasync(arg: NimNode): NimNode = if len(code) > 0: var awaitFunction = quote: - proc await[T](f: Future[T]): T {.importcpp: "(await #)", used.} + proc await[T](f: Future[T]): T {.importjs: "(await #)", used.} result.body.add(awaitFunction) var resolve: NimNode @@ -150,11 +150,11 @@ macro async*(arg: untyped): untyped = else: result = generateJsasync(arg) -proc newPromise*[T](handler: proc(resolve: proc(response: T))): Future[T] {.importcpp: "(new Promise(#))".} +proc newPromise*[T](handler: proc(resolve: proc(response: T))): Future[T] {.importjs: "(new Promise(#))".} ## A helper for wrapping callback-based functions ## into promises and async procedures. -proc newPromise*(handler: proc(resolve: proc())): Future[void] {.importcpp: "(new Promise(#))".} +proc newPromise*(handler: proc(resolve: proc())): Future[void] {.importjs: "(new Promise(#))".} ## A helper for wrapping callback-based functions ## into promises and async procedures. diff --git a/lib/js/dom_extensions.nim b/lib/js/dom_extensions.nim index f7d37f4bf..a1ceff5b4 100644 --- a/lib/js/dom_extensions.nim +++ b/lib/js/dom_extensions.nim @@ -2,4 +2,4 @@ import std/dom {.push importcpp.} proc elementsFromPoint*(n: DocumentOrShadowRoot; x, y: float): seq[Element] -{.pop.} \ No newline at end of file +{.pop.} diff --git a/lib/js/jsffi.nim b/lib/js/jsffi.nim index 0734d891a..937e3727b 100644 --- a/lib/js/jsffi.nim +++ b/lib/js/jsffi.nim @@ -21,7 +21,7 @@ runnableExamples: var document {.importc, nodecl.}: JsObject var console {.importc, nodecl.}: JsObject # import the "$" function - proc jq(selector: JsObject): JsObject {.importcpp: "$$(#)".} + proc jq(selector: JsObject): JsObject {.importjs: "$$(#)".} # Use jQuery to make the following code run, after the document is ready. # This uses an experimental `.()` operator for `JsObject`, to emit @@ -70,7 +70,7 @@ template mangleJsName(name: cstring): cstring = # only values that can be mapped 1 to 1 with cstring should be keys: they have an injective function with cstring -proc toJsKey*[T: SomeInteger](text: cstring, t: type T): T {.importcpp: "parseInt(#)".} +proc toJsKey*[T: SomeInteger](text: cstring, t: type T): T {.importjs: "parseInt(#)".} proc toJsKey*[T: enum](text: cstring, t: type T): T = T(text.toJsKey(int)) @@ -78,7 +78,7 @@ proc toJsKey*[T: enum](text: cstring, t: type T): T = proc toJsKey*(text: cstring, t: type cstring): cstring = text -proc toJsKey*[T: SomeFloat](text: cstring, t: type T): T {.importcpp: "parseFloat(#)".} +proc toJsKey*[T: SomeFloat](text: cstring, t: type T): T {.importjs: "parseFloat(#)".} type JsKey* = concept a, type T @@ -103,10 +103,10 @@ var jsFilename* {.importc: "__filename", nodecl.}: cstring ## JavaScript's __filename pseudo-variable. -proc isNull*[T](x: T): bool {.noSideEffect, importcpp: "(# === null)".} +proc isNull*[T](x: T): bool {.noSideEffect, importjs: "(# === null)".} ## Checks if a value is exactly null. -proc isUndefined*[T](x: T): bool {.noSideEffect, importcpp: "(# === undefined)".} +proc isUndefined*[T](x: T): bool {.noSideEffect, importjs: "(# === undefined)".} ## Checks if a value is exactly undefined. # Exceptions @@ -121,35 +121,35 @@ type JsURIError* {.importc: "URIError".} = object of JsError # New -proc newJsObject*: JsObject {.importcpp: "{@}".} +proc newJsObject*: JsObject {.importjs: "{@}".} ## Creates a new empty JsObject. -proc newJsAssoc*[K: JsKey, V]: JsAssoc[K, V] {.importcpp: "{@}".} +proc newJsAssoc*[K: JsKey, V]: JsAssoc[K, V] {.importjs: "{@}".} ## Creates a new empty JsAssoc with key type `K` and value type `V`. # Checks proc hasOwnProperty*(x: JsObject, prop: cstring): bool - {.importcpp: "#.hasOwnProperty(#)".} + {.importjs: "#.hasOwnProperty(#)".} ## Checks, whether `x` has a property of name `prop`. -proc jsTypeOf*(x: JsObject): cstring {.importcpp: "typeof(#)".} +proc jsTypeOf*(x: JsObject): cstring {.importjs: "typeof(#)".} ## Returns the name of the JsObject's JavaScript type as a cstring. -proc jsNew*(x: auto): JsObject {.importcpp: "(new #)".} +proc jsNew*(x: auto): JsObject {.importjs: "(new #)".} ## Turns a regular function call into an invocation of the ## JavaScript's `new` operator. -proc jsDelete*(x: auto): JsObject {.importcpp: "(delete #)".} +proc jsDelete*(x: auto): JsObject {.importjs: "(delete #)".} ## JavaScript's `delete` operator. proc require*(module: cstring): JsObject {.importc.} ## JavaScript's `require` function. # Conversion to and from JsObject -proc to*(x: JsObject, T: typedesc): T {.importcpp: "(#)".} +proc to*(x: JsObject, T: typedesc): T {.importjs: "(#)".} ## Converts a JsObject `x` to type `T`. -proc toJs*[T](val: T): JsObject {.importcpp: "(#)".} +proc toJs*[T](val: T): JsObject {.importjs: "(#)".} ## Converts a value of any type to type JsObject. template toJs*(s: string): JsObject = cstring(s).toJs @@ -160,50 +160,50 @@ macro jsFromAst*(n: untyped): untyped = result = newProc(procType = nnkDo, body = result) return quote: toJs(`result`) -proc `&`*(a, b: cstring): cstring {.importcpp: "(# + #)".} +proc `&`*(a, b: cstring): cstring {.importjs: "(# + #)".} ## Concatenation operator for JavaScript strings. -proc `+` *(x, y: JsObject): JsObject {.importcpp: "(# + #)".} -proc `-` *(x, y: JsObject): JsObject {.importcpp: "(# - #)".} -proc `*` *(x, y: JsObject): JsObject {.importcpp: "(# * #)".} -proc `/` *(x, y: JsObject): JsObject {.importcpp: "(# / #)".} -proc `%` *(x, y: JsObject): JsObject {.importcpp: "(# % #)".} -proc `+=` *(x, y: JsObject): JsObject {.importcpp: "(# += #)", discardable.} -proc `-=` *(x, y: JsObject): JsObject {.importcpp: "(# -= #)", discardable.} -proc `*=` *(x, y: JsObject): JsObject {.importcpp: "(# *= #)", discardable.} -proc `/=` *(x, y: JsObject): JsObject {.importcpp: "(# /= #)", discardable.} -proc `%=` *(x, y: JsObject): JsObject {.importcpp: "(# %= #)", discardable.} -proc `++` *(x: JsObject): JsObject {.importcpp: "(++#)".} -proc `--` *(x: JsObject): JsObject {.importcpp: "(--#)".} -proc `>` *(x, y: JsObject): JsObject {.importcpp: "(# > #)".} -proc `<` *(x, y: JsObject): JsObject {.importcpp: "(# < #)".} -proc `>=` *(x, y: JsObject): JsObject {.importcpp: "(# >= #)".} -proc `<=` *(x, y: JsObject): JsObject {.importcpp: "(# <= #)".} -proc `**` *(x, y: JsObject): JsObject {.importcpp: "((#) ** #)".} +proc `+` *(x, y: JsObject): JsObject {.importjs: "(# + #)".} +proc `-` *(x, y: JsObject): JsObject {.importjs: "(# - #)".} +proc `*` *(x, y: JsObject): JsObject {.importjs: "(# * #)".} +proc `/` *(x, y: JsObject): JsObject {.importjs: "(# / #)".} +proc `%` *(x, y: JsObject): JsObject {.importjs: "(# % #)".} +proc `+=` *(x, y: JsObject): JsObject {.importjs: "(# += #)", discardable.} +proc `-=` *(x, y: JsObject): JsObject {.importjs: "(# -= #)", discardable.} +proc `*=` *(x, y: JsObject): JsObject {.importjs: "(# *= #)", discardable.} +proc `/=` *(x, y: JsObject): JsObject {.importjs: "(# /= #)", discardable.} +proc `%=` *(x, y: JsObject): JsObject {.importjs: "(# %= #)", discardable.} +proc `++` *(x: JsObject): JsObject {.importjs: "(++#)".} +proc `--` *(x: JsObject): JsObject {.importjs: "(--#)".} +proc `>` *(x, y: JsObject): JsObject {.importjs: "(# > #)".} +proc `<` *(x, y: JsObject): JsObject {.importjs: "(# < #)".} +proc `>=` *(x, y: JsObject): JsObject {.importjs: "(# >= #)".} +proc `<=` *(x, y: JsObject): JsObject {.importjs: "(# <= #)".} +proc `**` *(x, y: JsObject): JsObject {.importjs: "((#) ** #)".} # (#) needed, refs https://github.com/nim-lang/Nim/pull/16409#issuecomment-760550812 -proc `and`*(x, y: JsObject): JsObject {.importcpp: "(# && #)".} -proc `or` *(x, y: JsObject): JsObject {.importcpp: "(# || #)".} -proc `not`*(x: JsObject): JsObject {.importcpp: "(!#)".} -proc `in` *(x, y: JsObject): JsObject {.importcpp: "(# in #)".} +proc `and`*(x, y: JsObject): JsObject {.importjs: "(# && #)".} +proc `or` *(x, y: JsObject): JsObject {.importjs: "(# || #)".} +proc `not`*(x: JsObject): JsObject {.importjs: "(!#)".} +proc `in` *(x, y: JsObject): JsObject {.importjs: "(# in #)".} -proc `[]`*(obj: JsObject, field: cstring): JsObject {.importcpp: getImpl.} +proc `[]`*(obj: JsObject, field: cstring): JsObject {.importjs: getImpl.} ## Returns the value of a property of name `field` from a JsObject `obj`. -proc `[]`*(obj: JsObject, field: int): JsObject {.importcpp: getImpl.} +proc `[]`*(obj: JsObject, field: int): JsObject {.importjs: getImpl.} ## Returns the value of a property of name `field` from a JsObject `obj`. -proc `[]=`*[T](obj: JsObject, field: cstring, val: T) {.importcpp: setImpl.} +proc `[]=`*[T](obj: JsObject, field: cstring, val: T) {.importjs: setImpl.} ## Sets the value of a property of name `field` in a JsObject `obj` to `v`. -proc `[]=`*[T](obj: JsObject, field: int, val: T) {.importcpp: setImpl.} +proc `[]=`*[T](obj: JsObject, field: int, val: T) {.importjs: setImpl.} ## Sets the value of a property of name `field` in a JsObject `obj` to `v`. proc `[]`*[K: JsKey, V](obj: JsAssoc[K, V], field: K): V - {.importcpp: getImpl.} + {.importjs: getImpl.} ## Returns the value of a property of name `field` from a JsAssoc `obj`. proc `[]=`*[K: JsKey, V](obj: JsAssoc[K, V], field: K, val: V) - {.importcpp: setImpl.} + {.importjs: setImpl.} ## Sets the value of a property of name `field` in a JsAssoc `obj` to `v`. proc `[]`*[V](obj: JsAssoc[cstring, V], field: string): V = @@ -212,7 +212,7 @@ proc `[]`*[V](obj: JsAssoc[cstring, V], field: string): V = proc `[]=`*[V](obj: JsAssoc[cstring, V], field: string, val: V) = obj[cstring(field)] = val -proc `==`*(x, y: JsRoot): bool {.importcpp: "(# === #)".} +proc `==`*(x, y: JsRoot): bool {.importjs: "(# === #)".} ## Compares two JsObjects or JsAssocs. Be careful though, as this is comparison ## like in JavaScript, so if your JsObjects are in fact JavaScript Objects, ## and not strings or numbers, this is a *comparison of references*. @@ -229,7 +229,7 @@ macro `.`*(obj: JsObject, field: untyped): JsObject = let importString = "#." & $field result = quote do: proc helper(o: JsObject): JsObject - {.importcpp: `importString`, gensym.} + {.importjs: `importString`, gensym.} helper(`obj`) else: if not mangledNames.hasKey($field): @@ -237,7 +237,7 @@ macro `.`*(obj: JsObject, field: untyped): JsObject = let importString = "#." & mangledNames[$field] result = quote do: proc helper(o: JsObject): JsObject - {.importcpp: `importString`, gensym.} + {.importjs: `importString`, gensym.} helper(`obj`) macro `.=`*(obj: JsObject, field, value: untyped): untyped = @@ -247,7 +247,7 @@ macro `.=`*(obj: JsObject, field, value: untyped): untyped = let importString = "#." & $field & " = #" result = quote do: proc helper(o: JsObject, v: auto) - {.importcpp: `importString`, gensym.} + {.importjs: `importString`, gensym.} helper(`obj`, `value`) else: if not mangledNames.hasKey($field): @@ -255,7 +255,7 @@ macro `.=`*(obj: JsObject, field, value: untyped): untyped = let importString = "#." & mangledNames[$field] & " = #" result = quote do: proc helper(o: JsObject, v: auto) - {.importcpp: `importString`, gensym.} + {.importjs: `importString`, gensym.} helper(`obj`, `value`) macro `.()`*(obj: JsObject, @@ -286,7 +286,7 @@ macro `.()`*(obj: JsObject, importString = "#." & mangledNames[$field] & "(@)" result = quote: proc helper(o: JsObject): JsObject - {.importcpp: `importString`, gensym, discardable.} + {.importjs: `importString`, gensym, discardable.} helper(`obj`) for idx in 0 ..< args.len: let paramName = newIdentNode("param" & $idx) @@ -306,7 +306,7 @@ macro `.`*[K: cstring, V](obj: JsAssoc[K, V], importString = "#." & mangledNames[$field] result = quote do: proc helper(o: type(`obj`)): `obj`.V - {.importcpp: `importString`, gensym.} + {.importjs: `importString`, gensym.} helper(`obj`) macro `.=`*[K: cstring, V](obj: JsAssoc[K, V], @@ -323,7 +323,7 @@ macro `.=`*[K: cstring, V](obj: JsAssoc[K, V], importString = "#." & mangledNames[$field] & " = #" result = quote do: proc helper(o: type(`obj`), v: `obj`.V) - {.importcpp: `importString`, gensym.} + {.importjs: `importString`, gensym.} helper(`obj`, `value`) macro `.()`*[K: cstring, V: proc](obj: JsAssoc[K, V], diff --git a/lib/std/jsbigints.nim b/lib/std/jsbigints.nim index 21bb9e1a0..ccf14080b 100644 --- a/lib/std/jsbigints.nim +++ b/lib/std/jsbigints.nim @@ -3,8 +3,8 @@ when not defined(js): {.fatal: "Module jsbigints is designed to be used with the JavaScript backend.".} -type JsBigIntImpl {.importc: "bigint".} = int # https://github.com/nim-lang/Nim/pull/16606 -type JsBigInt* = distinct JsBigIntImpl ## Arbitrary precision integer for JavaScript target. +type JsBigIntImpl {.importjs: "bigint".} = int # https://github.com/nim-lang/Nim/pull/16606 +type JsBigInt* = distinct JsBigIntImpl ## Arbitrary precision integer for JavaScript target. func big*(integer: SomeInteger): JsBigInt {.importjs: "BigInt(#)".} = ## Constructor for `JsBigInt`. From b70e33f5bbb7b9c146c97d39aabae57504f4ee75 Mon Sep 17 00:00:00 2001 From: ee7 <45465154+ee7@users.noreply.github.com> Date: Sat, 20 Mar 2021 13:22:50 +0100 Subject: [PATCH 1749/3768] strutils: improve doc comments for `replace` funcs (#17427) This commit fixes mispellings of "occurrence" introduced by: - 76a3b350ce0f (#17337) - 8e8bea9044f0 (#17339) and adds the same "every occurrence of" in the `replaceWord` func. Other changes: - Prefer "replace with" to "replace by". - Be more consistent with "the" - prefer "of the character" given that we wrote "by the character". - Try to be more consistent with writing the types - add "the string `sub`" given that we wrote "the character `sub`". --- lib/pure/strutils.nim | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 510f0682d..4098749ce 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -2037,7 +2037,7 @@ func contains*(s: string, chars: set[char]): bool = func replace*(s, sub: string, by = ""): string {.rtl, extern: "nsuReplaceStr".} = - ## Replaces every occurence of `sub` in `s` by the string `by`. + ## Replaces every occurrence of the string `sub` in `s` with the string `by`. ## ## See also: ## * `find func<#find,string,string,Natural,int>`_ @@ -2079,7 +2079,8 @@ func replace*(s, sub: string, by = ""): string {.rtl, func replace*(s: string, sub, by: char): string {.rtl, extern: "nsuReplaceChar".} = - ## Replaces every occurence of character `sub` in `s` by the character `by`. + ## Replaces every occurrence of the character `sub` in `s` with the character + ## `by`. ## ## Optimized version of `replace <#replace,string,string,string>`_ for ## characters. @@ -2097,7 +2098,7 @@ func replace*(s: string, sub, by: char): string {.rtl, func replaceWord*(s, sub: string, by = ""): string {.rtl, extern: "nsuReplaceWord".} = - ## Replaces `sub` in `s` by the string `by`. + ## Replaces every occurrence of the string `sub` in `s` with the string `by`. ## ## Each occurrence of `sub` has to be surrounded by word boundaries ## (comparable to `\b` in regular expressions), otherwise it is not From eca0b8754458e6d57a0ebc248ae0d1d024e1723c Mon Sep 17 00:00:00 2001 From: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Date: Sun, 21 Mar 2021 02:30:57 +0100 Subject: [PATCH 1750/3768] Close #8545 by add a test case (#17432) Co-authored-by: Timothee Cour --- tests/misc/t8545.nim | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 tests/misc/t8545.nim diff --git a/tests/misc/t8545.nim b/tests/misc/t8545.nim new file mode 100644 index 000000000..89957e1d3 --- /dev/null +++ b/tests/misc/t8545.nim @@ -0,0 +1,23 @@ +discard """ + targets: "c cpp js" +""" + +# bug #8545 + +template bar(a: static[bool]): untyped = int + +proc main() = + proc foo1(a: static[bool]): auto = 1 + doAssert foo1(true) == 1 + + proc foo2(a: static[bool]): bar(a) = 1 + doAssert foo2(true) == 1 + + proc foo3(a: static[bool]): bar(cast[static[bool]](a)) = 1 + doAssert foo3(true) == 1 + + proc foo4(a: static[bool]): bar(static(a)) = 1 + doAssert foo4(true) == 1 + +static: main() +main() From c5b109233a1ffe283d460be40575bdaf5beb0104 Mon Sep 17 00:00:00 2001 From: Danil Yarantsev Date: Sun, 21 Mar 2021 12:34:10 +0300 Subject: [PATCH 1751/3768] Add documentation to the `macrocache` module (#17431) * Add docs to macrocache * use hint * Use incl in the incl example * add macrocache to lib * consistency * Update doc/lib.rst Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> * apply suggestions * clarify the warning Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> --- doc/lib.rst | 3 + lib/core/macrocache.nim | 191 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 179 insertions(+), 15 deletions(-) diff --git a/doc/lib.rst b/doc/lib.rst index 3202c5a53..9d715b1c7 100644 --- a/doc/lib.rst +++ b/doc/lib.rst @@ -64,6 +64,9 @@ Core * `locks `_ Locks and condition variables for Nim. +* `macrocache `_ + Provides an API for macros to collect compile-time information across modules. + * `macros `_ Contains the AST API and documentation of Nim for writing macros. diff --git a/lib/core/macrocache.nim b/lib/core/macrocache.nim index 8fe1fa603..e376ad87f 100644 --- a/lib/core/macrocache.nim +++ b/lib/core/macrocache.nim @@ -7,38 +7,199 @@ # distribution, for details about the copyright. # -## This module provides an API for macros that need to collect compile -## time information across module boundaries in global variables. -## Starting with version 0.19 of Nim this is not directly supported anymore -## as it breaks incremental compilations. -## Instead the API here needs to be used. +## This module provides an API for macros to collect compile-time information +## across module boundaries. It should be used instead of global `{.compileTime.}` +## variables as those break incremental compilation. +## +## The main feature of this module is that if you create `CacheTable`s or +## any other `Cache` types with the same name in different modules, their +## content will be shared, meaning that you can fill a `CacheTable` in +## one module, and iterate over its contents in another. + +runnableExamples: + import std/macros + + const mcTable = CacheTable"myTable" + const mcSeq = CacheSeq"mySeq" + const mcCounter = CacheCounter"myCounter" + + static: + # add new key "val" with the value `myval` + let myval = newLit("hello ic") + mcTable["val"] = myval + assert mcTable["val"].kind == nnkStrLit + + # Can access the same cache from different static contexts + # All the information is retained + static: + # get value from `mcTable` and add it to `mcSeq` + mcSeq.add(mcTable["val"]) + assert mcSeq.len == 1 + + static: + assert mcSeq[0].strVal == "hello ic" + + # increase `mcCounter` by 3 + mcCounter.inc(3) + assert mcCounter.value == 3 + type CacheSeq* = distinct string + ## Compile-time sequence of `NimNode`s. CacheTable* = distinct string + ## Compile-time table of key-value pairs. + ## + ## Keys are `string`s and values are `NimNode`s. CacheCounter* = distinct string + ## Compile-time counter, uses `int` for storing the count. -proc value*(c: CacheCounter): int {.magic: "NccValue".} -proc inc*(c: CacheCounter; by = 1) {.magic: "NccInc".} +proc value*(c: CacheCounter): int {.magic: "NccValue".} = + ## Returns the value of a counter `c`. + runnableExamples: + static: + let counter = CacheCounter"valTest" + # default value is 0 + assert counter.value == 0 -proc add*(s: CacheSeq; value: NimNode) {.magic: "NcsAdd".} -proc incl*(s: CacheSeq; value: NimNode) {.magic: "NcsIncl".} -proc len*(s: CacheSeq): int {.magic: "NcsLen".} -proc `[]`*(s: CacheSeq; i: int): NimNode {.magic: "NcsAt".} + inc counter + assert counter.value == 1 + +proc inc*(c: CacheCounter; by = 1) {.magic: "NccInc".} = + ## Increments the counter `c` with the value `by`. + runnableExamples: + static: + let counter = CacheCounter"incTest" + inc counter + inc counter, 5 + + assert counter.value == 6 + +proc add*(s: CacheSeq; value: NimNode) {.magic: "NcsAdd".} = + ## Adds `value` to `s`. + runnableExamples: + import std/macros + const mySeq = CacheSeq"addTest" + + static: + mySeq.add(newLit(5)) + mySeq.add(newLit("hello ic")) + + assert mySeq.len == 2 + assert mySeq[1].strVal == "hello ic" + +proc incl*(s: CacheSeq; value: NimNode) {.magic: "NcsIncl".} = + ## Adds `value` to `s`. + ## + ## .. hint:: This doesn't do anything if `value` is already in `s`. + runnableExamples: + import std/macros + const mySeq = CacheSeq"inclTest" + + static: + mySeq.incl(newLit(5)) + mySeq.incl(newLit(5)) + + # still one element + assert mySeq.len == 1 + +proc len*(s: CacheSeq): int {.magic: "NcsLen".} = + ## Returns the length of `s`. + runnableExamples: + import std/macros + + const mySeq = CacheSeq"lenTest" + static: + let val = newLit("helper") + mySeq.add(val) + assert mySeq.len == 1 + + mySeq.add(val) + assert mySeq.len == 2 + +proc `[]`*(s: CacheSeq; i: int): NimNode {.magic: "NcsAt".} = + ## Returns the `i`th value from `s`. + runnableExamples: + import std/macros + + const mySeq = CacheSeq"subTest" + static: + mySeq.add(newLit(42)) + assert mySeq[0].intVal == 42 iterator items*(s: CacheSeq): NimNode = + ## Iterates over each item in `s`. + runnableExamples: + import std/macros + const myseq = CacheSeq"itemsTest" + + static: + myseq.add(newLit(5)) + myseq.add(newLit(42)) + + for val in myseq: + # check that all values in `myseq` are int literals + assert val.kind == nnkIntLit + for i in 0 ..< len(s): yield s[i] -proc `[]=`*(t: CacheTable; key: string, value: NimNode) {.magic: "NctPut".} - ## 'key' has to be unique! +proc `[]=`*(t: CacheTable; key: string, value: NimNode) {.magic: "NctPut".} = + ## Inserts a `(key, value)` pair into `t`. + ## + ## .. warning:: `key` has to be unique! Assigning `value` to a `key` that is already + ## in the table will result in a compiler error. + runnableExamples: + import std/macros -proc len*(t: CacheTable): int {.magic: "NctLen".} -proc `[]`*(t: CacheTable; key: string): NimNode {.magic: "NctGet".} + const mcTable = CacheTable"subTest" + static: + # assign newLit(5) to the key "value" + mcTable["value"] = newLit(5) + + # check that we can get the value back + assert mcTable["value"].kind == nnkIntLit + +proc len*(t: CacheTable): int {.magic: "NctLen".} = + ## Returns the number of elements in `t`. + runnableExamples: + import std/macros + + const dataTable = CacheTable"lenTest" + static: + dataTable["key"] = newLit(5) + assert dataTable.len == 1 + +proc `[]`*(t: CacheTable; key: string): NimNode {.magic: "NctGet".} = + ## Retrieves the `NimNode` value at `t[key]`. + runnableExamples: + import std/macros + + const mcTable = CacheTable"subTest" + static: + mcTable["toAdd"] = newStmtList() + + # get the NimNode back + assert mcTable["toAdd"].kind == nnkStmtList proc hasNext(t: CacheTable; iter: int): bool {.magic: "NctHasNext".} proc next(t: CacheTable; iter: int): (string, NimNode, int) {.magic: "NctNext".} iterator pairs*(t: CacheTable): (string, NimNode) = + ## Iterates over all `(key, value)` pairs in `t`. + runnableExamples: + import std/macros + const mytabl = CacheTable"values" + + static: + mytabl["intVal"] = newLit(5) + mytabl["otherVal"] = newLit(6) + for key, val in mytabl: + # make sure that we actually get the same keys + assert key in ["intVal", "otherVal"] + + # all vals are int literals + assert val.kind == nnkIntLit + var h = 0 while hasNext(t, h): let (a, b, h2) = next(t, h) From 05743bc9f72f2e3cbf4b17f5974a811637f03241 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 21 Mar 2021 02:35:00 -0700 Subject: [PATCH 1752/3768] improve jsutils docs (#17421) * improve jsutils docs * address comments --- lib/std/private/jsutils.nim | 52 ++++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/lib/std/private/jsutils.nim b/lib/std/private/jsutils.nim index 32c717c56..836b3512a 100644 --- a/lib/std/private/jsutils.nim +++ b/lib/std/private/jsutils.nim @@ -1,4 +1,4 @@ -when defined(js) or defined(nimdoc): +when defined(js): import std/jsbigints type @@ -14,7 +14,6 @@ when defined(js) or defined(nimdoc): func newUint32Array*(buffer: ArrayBuffer): Uint32Array {.importjs: "new Uint32Array(#)".} func newBigUint64Array*(buffer: ArrayBuffer): BigUint64Array {.importjs: "new BigUint64Array(#)".} - func newUint8Array*(n: int): Uint8Array {.importjs: "new Uint8Array(#)".} func `[]`*(arr: Uint32Array, i: int): uint32 {.importjs: "#[#]".} @@ -22,12 +21,22 @@ when defined(js) or defined(nimdoc): func `[]`*(arr: BigUint64Array, i: int): JsBigInt {.importjs: "#[#]".} func `[]=`*(arr: Float64Array, i: int, v: float) {.importjs: "#[#] = #".} - - proc jsTypeOf*[T](x: T): cstring {.importjs: "typeof(#)".} - ## Returns the name of the JsObject's JavaScript type as a cstring. - # xxx replace jsffi.jsTypeOf with this definition and add tests + proc jsTypeOf*[T](x: T): cstring {.importjs: "typeof(#)".} = + ## Returns the name of the JsObject's JavaScript type as a cstring. + # xxx replace jsffi.jsTypeOf with this definition and add tests + runnableExamples: + import std/[jsffi, jsbigints] + assert jsTypeOf(1.toJs) == "number" + assert jsTypeOf(false.toJs) == "boolean" + assert [1].toJs.jsTypeOf == "object" # note the difference with `getProtoName` + assert big"1".toJs.jsTypeOf == "bigint" proc jsConstructorName*[T](a: T): cstring = + runnableExamples: + import std/jsffi + let a = array[2, float64].default + assert jsConstructorName(a) == "Float64Array" + assert jsConstructorName(a.toJs) == "Float64Array" asm """`result` = `a`.constructor.name""" proc hasJsBigInt*(): bool = @@ -36,16 +45,39 @@ when defined(js) or defined(nimdoc): proc hasBigUint64Array*(): bool = asm """`result` = typeof BigUint64Array != 'undefined'""" - proc getProtoName*[T](a: T): cstring {.importjs: "Object.prototype.toString.call(#)".} + proc getProtoName*[T](a: T): cstring {.importjs: "Object.prototype.toString.call(#)".} = + runnableExamples: + import std/[jsffi, jsbigints] + type A = ref object + assert 1.toJs.getProtoName == "[object Number]" + assert "a".toJs.getProtoName == "[object String]" + assert big"1".toJs.getProtoName == "[object BigInt]" + assert false.toJs.getProtoName == "[object Boolean]" + assert (a: 1).toJs.getProtoName == "[object Object]" + assert A.default.toJs.getProtoName == "[object Null]" + assert [1].toJs.getProtoName == "[object Int32Array]" # implementation defined + assert @[1].toJs.getProtoName == "[object Array]" # ditto - proc isInteger*[T](x: T): bool {.importjs: "Number.isInteger(#)".} + const maxSafeInteger* = 9007199254740991 + ## The same as `Number.MAX_SAFE_INTEGER` or `2^53 - 1`. + ## See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MAX_SAFE_INTEGER + runnableExamples: + let a {.importjs: "Number.MAX_SAFE_INTEGER".}: int64 + assert a == maxSafeInteger + + proc isInteger*[T](x: T): bool {.importjs: "Number.isInteger(#)".} = + runnableExamples: + import std/jsffi + assert 1.isInteger + assert not 1.5.isInteger + assert 1.toJs.isInteger + assert not 1.5.toJs.isInteger proc isSafeInteger*[T](x: T): bool {.importjs: "Number.isSafeInteger(#)".} = runnableExamples: import std/jsffi assert not "123".toJs.isSafeInteger + assert 123.isSafeInteger assert 123.toJs.isSafeInteger assert 9007199254740991.toJs.isSafeInteger assert not 9007199254740992.toJs.isSafeInteger - - let maxSafeInteger* {.importjs: "Number.MAX_SAFE_INTEGER".} : int64 From fb38d906a284a088f052e6fc842fb7f2df26a486 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Sun, 21 Mar 2021 06:35:55 -0300 Subject: [PATCH 1753/3768] Improve jsre (#17365) * Add dollar for regex * Add dollar for regex * Peer review feedbacks * Peer review feedbacks * Update lib/js/jsre.nim Co-authored-by: Timothee Cour * Update lib/js/jsre.nim Co-authored-by: Timothee Cour * Update lib/js/jsre.nim Co-authored-by: Timothee Cour * Pear review * Beer review * Beer review Co-authored-by: Timothee Cour --- changelog.md | 2 ++ lib/js/jsre.nim | 77 ++++++++++++++++++++++++++++++------------------- 2 files changed, 49 insertions(+), 30 deletions(-) diff --git a/changelog.md b/changelog.md index 219a66ae7..cc4f54fe0 100644 --- a/changelog.md +++ b/changelog.md @@ -239,6 +239,8 @@ - Added `jsconsole.dir`, `jsconsole.dirxml`, `jsconsole.timeStamp`. +- Added dollar `$` and `len` for `jsre.RegExp`. + ## Language changes diff --git a/lib/js/jsre.nim b/lib/js/jsre.nim index 7be7221bc..7d51db646 100644 --- a/lib/js/jsre.nim +++ b/lib/js/jsre.nim @@ -1,46 +1,63 @@ ## Regular Expressions for the JavaScript target. ## * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions - -runnableExamples: - let jsregex: RegExp = newRegExp(r"\s+", r"i") - jsregex.compile(r"\w+", r"i") - doAssert jsregex.test(r"nim javascript") - doAssert jsregex.exec(r"nim javascript") == @["nim".cstring] - doAssert jsregex.toString() == r"/\w+/i" - jsregex.compile(r"[0-9]", r"i") - doAssert jsregex.test(r"0123456789abcd") - - when not defined(js): {.error: "This module only works on the JavaScript platform".} -type RegExp* {.importjs.} = object ## Regular Expressions for JavaScript target. - flags* {.importjs.}: cstring ## cstring that contains the flags of the RegExp object. - dotAll* {.importjs.}: bool ## Whether `.` matches newlines or not. - global* {.importjs.}: bool ## Whether to test against all possible matches in a string, or only against the first. - ignoreCase* {.importjs.}: bool ## Whether to ignore case while attempting a match in a string. - multiline* {.importjs.}: bool ## Whether to search in strings across multiple lines. - source* {.importjs.}: cstring ## The text of the pattern. - sticky* {.importjs.}: bool ## Whether the search is sticky. - unicode* {.importjs.}: bool ## Whether Unicode features are enabled. - lastIndex* {.importjs.}: cint ## Index at which to start the next match (read/write property). - input* {.importjs.}: cstring ## Read-only and modified on successful match. - lastMatch* {.importjs.}: cstring ## Ditto. - lastParen* {.importjs.}: cstring ## Ditto. - leftContext* {.importjs.}: cstring ## Ditto. - rightContext* {.importjs.}: cstring ## Ditto. +type RegExp* = ref object of JsRoot + ## Regular Expressions for JavaScript target. + ## See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp + flags*: cstring ## cstring that contains the flags of the RegExp object. + dotAll*: bool ## Whether `.` matches newlines or not. + global*: bool ## Whether to test against all possible matches in a string, or only against the first. + ignoreCase*: bool ## Whether to ignore case while attempting a match in a string. + multiline*: bool ## Whether to search in strings across multiple lines. + source*: cstring ## The text of the pattern. + sticky*: bool ## Whether the search is sticky. + unicode*: bool ## Whether Unicode features are enabled. + lastIndex*: cint ## Index at which to start the next match (read/write property). + input*: cstring ## Read-only and modified on successful match. + lastMatch*: cstring ## Ditto. + lastParen*: cstring ## Ditto. + leftContext*: cstring ## Ditto. + rightContext*: cstring ## Ditto. + func newRegExp*(pattern: cstring; flags: cstring): RegExp {.importjs: "new RegExp(@)".} ## Creates a new RegExp object. +func newRegExp*(pattern: cstring): RegExp {.importjs: "new RegExp(@)".} + func compile*(self: RegExp; pattern: cstring; flags: cstring) {.importjs: "#.compile(@)".} ## Recompiles a regular expression during execution of a script. func exec*(self: RegExp; pattern: cstring): seq[cstring] {.importjs: "#.exec(#)".} ## Executes a search for a match in its string parameter. -func test*(self: RegExp; pattern: cstring): bool {.importjs: "#.test(#)".} - ## Tests for a match in its string parameter. - -func toString*(self: RegExp): cstring {.importjs: "#.toString()".} +func toCstring*(self: RegExp): cstring {.importjs: "#.toString()".} ## Returns a string representing the RegExp object. + +func `$`*(self: RegExp): string = $toCstring(self) + +func test*(self: RegExp; pattern: cstring): bool {.importjs: "#.test(#)", deprecated: "Use contains instead".} + +func toString*(self: RegExp): cstring {.importjs: "#.toString()", deprecated: "Use toCstring instead".} + +func contains*(pattern: cstring; self: RegExp): bool = + ## Tests for a substring match in its string parameter. + runnableExamples: + let jsregex: RegExp = newRegExp(r"bc$", r"i") + assert jsregex in r"abc" + assert jsregex notin r"abcd" + assert "xabc".contains jsregex + asm "`result` = `self`.test(`pattern`);" + + +runnableExamples: + let jsregex: RegExp = newRegExp(r"\s+", r"i") + jsregex.compile(r"\w+", r"i") + assert "nim javascript".contains jsregex + assert jsregex.exec(r"nim javascript") == @["nim".cstring] + assert jsregex.toCstring() == r"/\w+/i" + jsregex.compile(r"[0-9]", r"i") + assert "0123456789abcd".contains jsregex + assert $jsregex == "/[0-9]/i" From fd09ace55796ae6605f34dafecab24cc0d6ac9fa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Derek=20=E5=91=86?= <116649+derekdai@users.noreply.github.com> Date: Sun, 21 Mar 2021 21:29:39 +0800 Subject: [PATCH 1754/3768] prevent bitmasks double included in mmdist if -d:nimArcDebug added (#17436) --- lib/system/cellsets.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/system/cellsets.nim b/lib/system/cellsets.nim index ea00176b5..779f1a91f 100644 --- a/lib/system/cellsets.nim +++ b/lib/system/cellsets.nim @@ -13,7 +13,8 @@ when defined(gcOrc) or defined(gcArc): type PCell = Cell - include bitmasks + when not declaredInScope(PageShift): + include bitmasks else: type From 5bed7d282ad043d945225fe72adb654d49b4f2ee Mon Sep 17 00:00:00 2001 From: AFaurholt Date: Sun, 21 Mar 2021 18:35:22 +0100 Subject: [PATCH 1755/3768] added more modules to docs lib (#17430) * added more modules * Update doc/lib.rst Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> * Update doc/lib.rst Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> * Update doc/lib.rst Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> * Update doc/lib.rst Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> * Update doc/lib.rst Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> * listed alphabetically + link to json module * Added suggestion #17430 Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> --- doc/lib.rst | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/doc/lib.rst b/doc/lib.rst index 9d715b1c7..62e02815c 100644 --- a/doc/lib.rst +++ b/doc/lib.rst @@ -357,6 +357,9 @@ Parsers * `json `_ High-performance JSON parser. +* `std/jsonutils `_ + This module implements a hookable (de)serialization for arbitrary types. + * `lexbase `_ This is a low-level module that implements an extremely efficient buffering scheme for lexers and parsers. This is used by the diverse parsing modules. @@ -371,6 +374,9 @@ Parsers * `parsecsv `_ The `parsecsv` module implements a simple high-performance CSV parser. +* `parsejson `_ + This module implements a JSON parser. It is used and exported by the `json `_ module, but can also be used in its own right. + * `parseopt `_ The `parseopt` module implements a command line option parser. @@ -459,6 +465,9 @@ Miscellaneous * `coro `_ This module implements experimental coroutines in Nim. +* `std/enumerate `_ + This module implements `enumerate` syntactic sugar based on Nim's macro system. + * `logging `_ This module implements a simple logger. @@ -474,6 +483,10 @@ Miscellaneous * `std/varints `_ Decode variable-length integers that are compatible with SQLite. +* `std/with `_ + This module implements the `with` macro for easy function chaining. + + Modules for JS backend ---------------------- @@ -506,6 +519,7 @@ Regular expressions expressions. The current implementation uses PCRE. + Database support ---------------- @@ -522,6 +536,13 @@ Database support for other databases too. +Generic Operating System Services +--------------------------------- + +* `rdstdin `_ + This module contains code for reading from stdin. + + Wrappers ======== @@ -572,3 +593,11 @@ Network Programming and Internet Protocols * `openssl `_ Wrapper for OpenSSL. + + + +Unstable +======== + +* `atomics `_ + Types and operations for atomic operations and lockless algorithms. From 23fd0984283fe94108dea5d569450f5e0564c59d Mon Sep 17 00:00:00 2001 From: Saem Ghani Date: Sun, 21 Mar 2021 16:33:37 -0700 Subject: [PATCH 1756/3768] Fixes #17433; gensym callDef return in templ body (#17445) --- compiler/semtempl.nim | 2 +- tests/template/t17433.nim | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 tests/template/t17433.nim diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index 568873269..4bb9f3e6b 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -435,8 +435,8 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = of nkLetSection: semTemplSomeDecl(c, n, skLet) of nkFormalParams: checkMinSonsLen(n, 1, c.c.config) - n[0] = semTemplBody(c, n[0]) semTemplSomeDecl(c, n, skParam, 1) + n[0] = semTemplBody(c, n[0]) of nkConstSection: for i in 0.. Date: Mon, 22 Mar 2021 07:36:48 +0800 Subject: [PATCH 1757/3768] close #11330 sets uses optimized countSetBits (#17334) * Update lib/pure/bitops.nim * Update lib/system/sets.nim * Apply suggestions from code review Co-authored-by: Andreas Rumpf --- lib/pure/bitops.nim | 88 ++++------------------------------- lib/std/private/vmutils.nim | 17 +++++++ lib/system/countbits_impl.nim | 77 ++++++++++++++++++++++++++++-- lib/system/sets.nim | 1 - 4 files changed, 98 insertions(+), 85 deletions(-) create mode 100644 lib/std/private/vmutils.nim diff --git a/lib/pure/bitops.nim b/lib/pure/bitops.nim index 57e3c7989..377602e75 100644 --- a/lib/pure/bitops.nim +++ b/lib/pure/bitops.nim @@ -27,6 +27,9 @@ import macros import std/private/since +from std/private/vmutils import forwardImpl, toUnsigned + + func bitnot*[T: SomeInteger](x: T): T {.magic: "BitnotI".} ## Computes the `bitwise complement` of the integer `x`. @@ -58,34 +61,6 @@ macro bitxor*[T: SomeInteger](x, y: T; z: varargs[T]): T = for extra in z: result = newCall(fn, result, extra) -const useBuiltins = not defined(noIntrinsicsBitOpts) -const noUndefined = defined(noUndefinedBitOpts) -const useGCC_builtins = (defined(gcc) or defined(llvm_gcc) or - defined(clang)) and useBuiltins -const useICC_builtins = defined(icc) and useBuiltins -const useVCC_builtins = defined(vcc) and useBuiltins -const arch64 = sizeof(int) == 8 -const useBuiltinsRotate = (defined(amd64) or defined(i386)) and - (defined(gcc) or defined(clang) or defined(vcc) or - (defined(icl) and not defined(cpp))) and useBuiltins - -template toUnsigned(x: int8): uint8 = cast[uint8](x) -template toUnsigned(x: int16): uint16 = cast[uint16](x) -template toUnsigned(x: int32): uint32 = cast[uint32](x) -template toUnsigned(x: int64): uint64 = cast[uint64](x) -template toUnsigned(x: int): uint = cast[uint](x) - -template forwardImpl(impl, arg) {.dirty.} = - when sizeof(x) <= 4: - when x is SomeSignedInt: - impl(cast[uint32](x.int32)) - else: - impl(x.uint32) - else: - when x is SomeSignedInt: - impl(cast[uint64](x.int64)) - else: - impl(x.uint64) type BitsRange*[T] = range[0..sizeof(T)*8-1] ## A range with all bit positions for type `T`. @@ -436,13 +411,12 @@ func fastlog2Nim(x: uint64): int {.inline.} = v = v or v shr 32 result = lookup[(v * 0x03F6EAF2CD271461'u64) shr 58].int -# sets.nim cannot import bitops, but bitops can use include -# system/sets to eliminate code duplication. sets.nim defines -# countBits32 and countBits64. import system/countbits_impl -template countSetBitsNim(n: uint32): int = countBits32(n) -template countSetBitsNim(n: uint64): int = countBits64(n) +const arch64 = sizeof(int) == 8 +const useBuiltinsRotate = (defined(amd64) or defined(i386)) and + (defined(gcc) or defined(clang) or defined(vcc) or + (defined(icl) and not defined(cpp))) and useBuiltins template parityImpl[T](value: T): int = # formula id from: https://graphics.stanford.edu/%7Eseander/bithacks.html#ParityParallel @@ -459,11 +433,6 @@ template parityImpl[T](value: T): int = when useGCC_builtins: - # Returns the number of set 1-bits in value. - proc builtin_popcount(x: cuint): cint {.importc: "__builtin_popcount", cdecl.} - proc builtin_popcountll(x: culonglong): cint {. - importc: "__builtin_popcountll", cdecl.} - # Returns the bit parity in value proc builtin_parity(x: cuint): cint {.importc: "__builtin_parity", cdecl.} proc builtin_parityll(x: culonglong): cint {.importc: "__builtin_parityll", cdecl.} @@ -481,14 +450,6 @@ when useGCC_builtins: proc builtin_ctzll(x: culonglong): cint {.importc: "__builtin_ctzll", cdecl.} elif useVCC_builtins: - # Counts the number of one bits (population count) in a 16-, 32-, or 64-byte unsigned integer. - func builtin_popcnt16(a2: uint16): uint16 {. - importc: "__popcnt16", header: "".} - func builtin_popcnt32(a2: uint32): uint32 {. - importc: "__popcnt", header: "".} - func builtin_popcnt64(a2: uint64): uint64 {. - importc: "__popcnt64", header: "".} - # Search the mask data from most significant bit (MSB) to least significant bit (LSB) for a set bit (1). func bitScanReverse(index: ptr culong, mask: culong): cuchar {. importc: "_BitScanReverse", header: "".} @@ -507,15 +468,6 @@ elif useVCC_builtins: index.int elif useICC_builtins: - - # Intel compiler intrinsics: http://fulla.fnal.gov/intel/compiler_c/main_cls/intref_cls/common/intref_allia_misc.htm - # see also: https://software.intel.com/en-us/node/523362 - # Count the number of bits set to 1 in an integer a, and return that count in dst. - func builtin_popcnt32(a: cint): cint {. - importc: "_popcnt", header: "".} - func builtin_popcnt64(a: uint64): cint {. - importc: "_popcnt64", header: "".} - # Returns the number of trailing 0-bits in x, starting at the least significant bit position. If x is 0, the result is undefined. func bitScanForward(p: ptr uint32, b: uint32): cuchar {. importc: "_BitScanForward", header: "".} @@ -533,37 +485,13 @@ elif useICC_builtins: discard fnc(index.addr, v) index.int - func countSetBits*(x: SomeInteger): int {.inline.} = ## Counts the set bits in an integer (also called `Hamming weight`:idx:). runnableExamples: doAssert countSetBits(0b0000_0011'u8) == 2 doAssert countSetBits(0b1010_1010'u8) == 4 - # TODO: figure out if ICC support _popcnt32/_popcnt64 on platform without POPCNT. - # like GCC and MSVC - when x is SomeSignedInt: - let x = x.toUnsigned - when nimvm: - result = forwardImpl(countSetBitsNim, x) - else: - when useGCC_builtins: - when sizeof(x) <= 4: result = builtin_popcount(x.cuint).int - else: result = builtin_popcountll(x.culonglong).int - elif useVCC_builtins: - when sizeof(x) <= 2: result = builtin_popcnt16(x.uint16).int - elif sizeof(x) <= 4: result = builtin_popcnt32(x.uint32).int - elif arch64: result = builtin_popcnt64(x.uint64).int - else: result = builtin_popcnt32((x.uint64 and 0xFFFFFFFF'u64).uint32).int + - builtin_popcnt32((x.uint64 shr 32'u64).uint32).int - elif useICC_builtins: - when sizeof(x) <= 4: result = builtin_popcnt32(x.cint).int - elif arch64: result = builtin_popcnt64(x.uint64).int - else: result = builtin_popcnt32((x.uint64 and 0xFFFFFFFF'u64).cint).int + - builtin_popcnt32((x.uint64 shr 32'u64).cint).int - else: - when sizeof(x) <= 4: result = countSetBitsNim(x.uint32) - else: result = countSetBitsNim(x.uint64) + result = countSetBitsImpl(x) func popcount*(x: SomeInteger): int {.inline.} = ## Alias for `countSetBits <#countSetBits,SomeInteger>`_ (Hamming weight). diff --git a/lib/std/private/vmutils.nim b/lib/std/private/vmutils.nim new file mode 100644 index 000000000..d54977b4e --- /dev/null +++ b/lib/std/private/vmutils.nim @@ -0,0 +1,17 @@ +template forwardImpl*(impl, arg) {.dirty.} = + when sizeof(x) <= 4: + when x is SomeSignedInt: + impl(cast[uint32](x.int32)) + else: + impl(x.uint32) + else: + when x is SomeSignedInt: + impl(cast[uint64](x.int64)) + else: + impl(x.uint64) + +template toUnsigned*(x: int8): uint8 = cast[uint8](x) +template toUnsigned*(x: int16): uint16 = cast[uint16](x) +template toUnsigned*(x: int32): uint32 = cast[uint32](x) +template toUnsigned*(x: int64): uint64 = cast[uint64](x) +template toUnsigned*(x: int): uint = cast[uint](x) diff --git a/lib/system/countbits_impl.nim b/lib/system/countbits_impl.nim index 6c85612e2..d3c003ff7 100644 --- a/lib/system/countbits_impl.nim +++ b/lib/system/countbits_impl.nim @@ -9,17 +9,86 @@ ## Contains the used algorithms for counting bits. -proc countBits32*(n: uint32): int {.compilerproc.} = +from std/private/vmutils import forwardImpl, toUnsigned + + +const useBuiltins* = not defined(noIntrinsicsBitOpts) +const noUndefined* = defined(noUndefinedBitOpts) +const useGCC_builtins* = (defined(gcc) or defined(llvm_gcc) or + defined(clang)) and useBuiltins +const useICC_builtins* = defined(icc) and useBuiltins +const useVCC_builtins* = defined(vcc) and useBuiltins + +template countBitsImpl(n: uint32): int = # generic formula is from: https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel var v = uint32(n) v = v - ((v shr 1'u32) and 0x55555555'u32) v = (v and 0x33333333'u32) + ((v shr 2'u32) and 0x33333333'u32) - result = (((v + (v shr 4'u32) and 0xF0F0F0F'u32) * 0x1010101'u32) shr 24'u32).int + (((v + (v shr 4'u32) and 0xF0F0F0F'u32) * 0x1010101'u32) shr 24'u32).int -proc countBits64*(n: uint64): int {.compilerproc, inline.} = +template countBitsImpl(n: uint64): int = # generic formula is from: https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel var v = uint64(n) v = v - ((v shr 1'u64) and 0x5555555555555555'u64) v = (v and 0x3333333333333333'u64) + ((v shr 2'u64) and 0x3333333333333333'u64) v = (v + (v shr 4'u64) and 0x0F0F0F0F0F0F0F0F'u64) - result = ((v * 0x0101010101010101'u64) shr 56'u64).int + ((v * 0x0101010101010101'u64) shr 56'u64).int + + +when useGCC_builtins: + # Returns the number of set 1-bits in value. + proc builtin_popcount(x: cuint): cint {.importc: "__builtin_popcount", cdecl.} + proc builtin_popcountll(x: culonglong): cint {. + importc: "__builtin_popcountll", cdecl.} + +elif useVCC_builtins: + # Counts the number of one bits (population count) in a 16-, 32-, or 64-byte unsigned integer. + func builtin_popcnt16(a2: uint16): uint16 {. + importc: "__popcnt16", header: "".} + func builtin_popcnt32(a2: uint32): uint32 {. + importc: "__popcnt", header: "".} + func builtin_popcnt64(a2: uint64): uint64 {. + importc: "__popcnt64", header: "".} + +elif useICC_builtins: + # Intel compiler intrinsics: http://fulla.fnal.gov/intel/compiler_c/main_cls/intref_cls/common/intref_allia_misc.htm + # see also: https://software.intel.com/en-us/node/523362 + # Count the number of bits set to 1 in an integer a, and return that count in dst. + func builtin_popcnt32(a: cint): cint {. + importc: "_popcnt", header: "".} + func builtin_popcnt64(a: uint64): cint {. + importc: "_popcnt64", header: "".} + + +func countSetBitsImpl*(x: SomeInteger): int {.inline.} = + ## Counts the set bits in an integer (also called `Hamming weight`:idx:). + # TODO: figure out if ICC support _popcnt32/_popcnt64 on platform without POPCNT. + # like GCC and MSVC + when x is SomeSignedInt: + let x = x.toUnsigned + when nimvm: + result = forwardImpl(countBitsImpl, x) + else: + when useGCC_builtins: + when sizeof(x) <= 4: result = builtin_popcount(x.cuint).int + else: result = builtin_popcountll(x.culonglong).int + elif useVCC_builtins: + when sizeof(x) <= 2: result = builtin_popcnt16(x.uint16).int + elif sizeof(x) <= 4: result = builtin_popcnt32(x.uint32).int + elif arch64: result = builtin_popcnt64(x.uint64).int + else: result = builtin_popcnt32((x.uint64 and 0xFFFFFFFF'u64).uint32).int + + builtin_popcnt32((x.uint64 shr 32'u64).uint32).int + elif useICC_builtins: + when sizeof(x) <= 4: result = builtin_popcnt32(x.cint).int + elif arch64: result = builtin_popcnt64(x.uint64).int + else: result = builtin_popcnt32((x.uint64 and 0xFFFFFFFF'u64).cint).int + + builtin_popcnt32((x.uint64 shr 32'u64).cint).int + else: + when sizeof(x) <= 4: result = countBitsImpl(x.uint32) + else: result = countBitsImpl(x.uint64) + +proc countBits32*(n: uint32): int {.compilerproc, inline.} = + result = countSetBitsImpl(n) + +proc countBits64*(n: uint64): int {.compilerproc, inline.} = + result = countSetBitsImpl(n) diff --git a/lib/system/sets.nim b/lib/system/sets.nim index 04e10ba04..103c8d343 100644 --- a/lib/system/sets.nim +++ b/lib/system/sets.nim @@ -12,7 +12,6 @@ type NimSet = array[0..4*2048-1, uint8] -# bitops can't be imported here, therefore the code duplication. proc cardSet(s: NimSet, len: int): int {.compilerproc, inline.} = var i = 0 From e5873b3a9300897443f0f2e2db128e3463089003 Mon Sep 17 00:00:00 2001 From: Saem Ghani Date: Mon, 22 Mar 2021 04:46:34 -0700 Subject: [PATCH 1758/3768] semTemplateDef and t17433 clean-ups (#17448) - use `doAssert` in t17433 - use setGenericParamsMisc in semTemplateDef akin to semProcAux - pragma handling in semTemplateDef inline with semProcAux --- compiler/sem.nim | 24 ++++++++++++++++++++++-- compiler/semstmts.nim | 18 ------------------ compiler/semtempl.nim | 36 ++++++++++++++++++------------------ tests/template/t17433.nim | 6 +++--- 4 files changed, 43 insertions(+), 41 deletions(-) diff --git a/compiler/sem.nim b/compiler/sem.nim index 5593f8b02..bb42b5c1b 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -485,11 +485,31 @@ proc semConstBoolExpr(c: PContext, n: PNode): PNode = if result.kind != nkIntLit: localError(c.config, n.info, errConstExprExpected) - proc semGenericStmt(c: PContext, n: PNode): PNode proc semConceptBody(c: PContext, n: PNode): PNode -include semtypes, semtempl, semgnrc, semstmts, semexprs +include semtypes + +proc setGenericParamsMisc(c: PContext; n: PNode) = + ## used by call defs (procs, templates, macros, ...) to analyse their generic + ## params, and store the originals in miscPos for better error reporting. + let orig = n[genericParamsPos] + + doAssert orig.kind in {nkEmpty, nkGenericParams} + + if n[genericParamsPos].kind == nkEmpty: + n[genericParamsPos] = newNodeI(nkGenericParams, n.info) + else: + # we keep the original params around for better error messages, see + # issue https://github.com/nim-lang/Nim/issues/1713 + n[genericParamsPos] = semGenericParamList(c, orig) + + if n[miscPos].kind == nkEmpty: + n[miscPos] = newTree(nkBracket, c.graph.emptyNode, orig) + else: + n[miscPos][1] = orig + +include semtempl, semgnrc, semstmts, semexprs proc addCodeForGenerics(c: PContext, n: PNode) = for i in c.lastGenericIdx..=", ">", "incl", "excl", "in", "notin", "isnot"] @@ -608,26 +610,23 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = s.owner.name.s == "vm" and s.name.s == "stackTrace": incl(s.flags, sfCallsite) + s.ast = n + styleCheckDef(c.config, s) - onDef(n[0].info, s) + onDef(n[namePos].info, s) # check parameter list: #s.scope = c.currentScope pushOwner(c, s) openScope(c) - n[namePos] = newSymNode(s, n[namePos].info) - if n[pragmasPos].kind != nkEmpty: - pragma(c, s, n[pragmasPos], templatePragmas) + n[namePos] = newSymNode(s) + pragmaCallable(c, s, n, templatePragmas) + implicitPragmas(c, s, n.info, templatePragmas) - var gp: PNode - if n[genericParamsPos].kind != nkEmpty: - n[genericParamsPos] = semGenericParamList(c, n[genericParamsPos]) - gp = n[genericParamsPos] - else: - gp = newNodeI(nkGenericParams, n.info) + setGenericParamsMisc(c, n) # process parameters: var allUntyped = true if n[paramsPos].kind != nkEmpty: - semParamList(c, n[paramsPos], gp, s) + semParamList(c, n[paramsPos], n[genericParamsPos], s) # a template's parameters are not gensym'ed even if that was originally the # case as we determine whether it's a template parameter in the template # body by the absence of the sfGenSym flag: @@ -636,18 +635,21 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = param.flags.incl sfTemplateParam param.flags.excl sfGenSym if param.typ.kind != tyUntyped: allUntyped = false - if gp.len > 0 and n[genericParamsPos].kind == nkEmpty: - # we have a list of implicit type parameters: - n[genericParamsPos] = gp else: s.typ = newTypeS(tyProc, c) # XXX why do we need tyTyped as a return type again? s.typ.n = newNodeI(nkFormalParams, n.info) rawAddSon(s.typ, newTypeS(tyTyped, c)) s.typ.n.add newNodeIT(nkType, n.info, s.typ[0]) + if n[genericParamsPos].safeLen == 0: + # restore original generic type params as no explicit or implicit were found + n[genericParamsPos] = n[miscPos][1] + n[miscPos] = c.graph.emptyNode if allUntyped: incl(s.flags, sfAllUntyped) + if n[patternPos].kind != nkEmpty: n[patternPos] = semPattern(c, n[patternPos]) + var ctx: TemplCtx ctx.toBind = initIntSet() ctx.toMixin = initIntSet() @@ -662,8 +664,6 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = semIdeForTemplateOrGeneric(c, n[bodyPos], ctx.cursorInBody) closeScope(c) popOwner(c) - s.ast = n - result = n if sfCustomPragma in s.flags: if n[bodyPos].kind != nkEmpty: localError(c.config, n[bodyPos].info, errImplOfXNotAllowed % s.name.s) diff --git a/tests/template/t17433.nim b/tests/template/t17433.nim index 121f3c471..053d33ff0 100644 --- a/tests/template/t17433.nim +++ b/tests/template/t17433.nim @@ -7,10 +7,10 @@ from std/macros import expandMacros proc bar(a: typedesc): a = default(a) -assert bar(float) == 0.0 -assert bar(string) == "" +doAssert bar(float) == 0.0 +doAssert bar(string) == "" template main = proc baz(a: typedesc): a = default(a) - assert baz(float) == 0.0 + doAssert baz(float) == 0.0 main() From a75c4b70e86705c98a4006aa8475a10a0ebf94ec Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 22 Mar 2021 18:24:28 -0700 Subject: [PATCH 1759/3768] hint:cc goes to stderr (like all other hints) instead of stdout (#17465) Co-authored-by: ee7 <45465154+ee7@users.noreply.github.com> --- changelog.md | 1 + compiler/extccomp.nim | 15 +++++++-------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/changelog.md b/changelog.md index cc4f54fe0..00fc970bf 100644 --- a/changelog.md +++ b/changelog.md @@ -301,6 +301,7 @@ - Added `unsafeIsolate` and `extract` to `std/isolation`. +- `--hint:CC` now goes to stderr (like all other hints) instead of stdout. ## Tool changes diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index e95ed893b..e0fd5e206 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -281,6 +281,11 @@ const hExt* = ".h" +template writePrettyCmdsStderr(cmd) = + if cmd.len > 0: + flushDot(conf) + stderr.writeLine(cmd) + proc nameToCC*(name: string): TSystemCC = ## Returns the kind of compiler referred to by `name`, or ccNone ## if the name doesn't refer to any known compiler. @@ -848,11 +853,7 @@ proc callCCompiler*(conf: ConfigRef) = var script: Rope = nil var cmds: TStringSeq var prettyCmds: TStringSeq - let prettyCb = proc (idx: int) = - if prettyCmds[idx].len > 0: - flushDot(conf) - # xxx should probably use stderr like other compiler messages, not stdout - echo prettyCmds[idx] + let prettyCb = proc (idx: int) = writePrettyCmdsStderr(prettyCmds[idx]) for idx, it in conf.toCompile: # call the C compiler for the .c file: @@ -1108,9 +1109,7 @@ proc runJsonBuildInstructions*(conf: ConfigRef; projectfile: AbsoluteFile) = doAssert toCompile.kind == JArray var cmds: TStringSeq var prettyCmds: TStringSeq - let prettyCb = proc (idx: int) = - if prettyCmds[idx].len > 0: echo prettyCmds[idx] - + let prettyCb = proc (idx: int) = writePrettyCmdsStderr(prettyCmds[idx]) for c in toCompile: doAssert c.kind == JArray doAssert c.len >= 2 From 4f9aaee1d9a30976e4ca9bff6fb428960a6b92b0 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 23 Mar 2021 00:28:53 -0700 Subject: [PATCH 1760/3768] remove redundant void return in stdlib (#17464) --- lib/posix/linux.nim | 2 +- lib/posix/posix.nim | 2 +- lib/pure/concurrency/threadpool.nim | 4 ++-- lib/pure/coro.nim | 3 +-- lib/pure/json.nim | 2 +- lib/pure/net.nim | 2 +- lib/windows/winlean.nim | 8 ++++---- 7 files changed, 11 insertions(+), 12 deletions(-) diff --git a/lib/posix/linux.nim b/lib/posix/linux.nim index 5ce9bf2fb..2d6702e87 100644 --- a/lib/posix/linux.nim +++ b/lib/posix/linux.nim @@ -31,7 +31,7 @@ const CLONE_IO* = 0x80000000'i32 CLONE_STOPPED* {.deprecated.} = 0x02000000'i32 -# fn should be of type proc (a2: pointer): void {.cdecl.} +# fn should be of type proc (a2: pointer) {.cdecl.} proc clone*(fn: pointer; child_stack: pointer; flags: cint; arg: pointer; ptid: ptr Pid; tls: pointer; ctid: ptr Pid): cint {.importc, header: "".} diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index 520d0caa0..035b4e8fb 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -447,7 +447,7 @@ proc pthread_spin_unlock*(a1: ptr Pthread_spinlock): cint {. proc pthread_testcancel*() {.importc, header: "".} -proc exitnow*(code: int): void {.importc: "_exit", header: "".} +proc exitnow*(code: int) {.importc: "_exit", header: "".} proc access*(a1: cstring, a2: cint): cint {.importc, header: "".} proc alarm*(a1: cint): cint {.importc, header: "".} proc chdir*(a1: cstring): cint {.importc, header: "".} diff --git a/lib/pure/concurrency/threadpool.nim b/lib/pure/concurrency/threadpool.nim index 905e668ce..9beb39522 100644 --- a/lib/pure/concurrency/threadpool.nim +++ b/lib/pure/concurrency/threadpool.nim @@ -451,14 +451,14 @@ proc preferSpawn*(): bool = ## <#spawnX.t>`_ instead. result = gSomeReady.counter > 0 -proc spawn*(call: sink typed): void {.magic: "Spawn".} +proc spawn*(call: sink typed) {.magic: "Spawn".} ## Always spawns a new task, so that the `call` is never executed on ## the calling thread. ## ## `call` has to be a proc call `p(...)` where `p` is gcsafe and has a ## return type that is either `void` or compatible with `FlowVar[T]`. -proc pinnedSpawn*(id: ThreadId; call: sink typed): void {.magic: "Spawn".} +proc pinnedSpawn*(id: ThreadId; call: sink typed) {.magic: "Spawn".} ## Always spawns a new task on the worker thread with `id`, so that ## the `call` is **always** executed on the thread. ## diff --git a/lib/pure/coro.nim b/lib/pure/coro.nim index 21675928d..af8acf268 100644 --- a/lib/pure/coro.nim +++ b/lib/pure/coro.nim @@ -273,8 +273,7 @@ proc start*(c: proc(), stacksize: int = defaultStackSize): CoroutineRef {.discar coro = cast[CoroutinePtr](alloc0(sizeof(Coroutine))) coro.execContext = CreateFiberEx(stacksize, stacksize, FIBER_FLAG_FLOAT_SWITCH, - (proc(p: pointer): void {.stdcall.} = runCurrentTask()), - nil) + (proc(p: pointer) {.stdcall.} = runCurrentTask()), nil) coro.stack.size = stacksize else: coro = cast[CoroutinePtr](alloc0(sizeof(Coroutine) + stacksize)) diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 13cd3fb85..c09af32f3 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -1165,7 +1165,7 @@ when defined(nimFixedForwardGeneric): proc initFromJson[T: distinct](dst: var T; jsonNode: JsonNode; jsonPath: var string) = assignDistinctImpl(dst, jsonNode, jsonPath) - proc detectIncompatibleType(typeExpr, lineinfoNode: NimNode): void = + proc detectIncompatibleType(typeExpr, lineinfoNode: NimNode) = if typeExpr.kind == nnkTupleConstr: error("Use a named tuple instead of: " & typeExpr.repr, lineinfoNode) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 37e6e8f5a..b37782271 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -200,7 +200,7 @@ when defined(nimHasStyleChecks): proc socketError*(socket: Socket, err: int = -1, async = false, lastError = (-1).OSErrorCode, - flags: set[SocketFlag] = {}): void {.gcsafe.} + flags: set[SocketFlag] = {}) {.gcsafe.} proc isDisconnectionError*(flags: set[SocketFlag], lastError: OSErrorCode): bool = diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index 1cdb20fe7..6e4c438e6 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -1030,7 +1030,7 @@ const PROCESS_QUERY_LIMITED_INFORMATION* = 0x00001000'i32 PROCESS_SET_LIMITED_INFORMATION* = 0x00002000'i32 type - WAITORTIMERCALLBACK* = proc(para1: pointer, para2: int32): void {.stdcall.} + WAITORTIMERCALLBACK* = proc(para1: pointer, para2: int32) {.stdcall.} proc postQueuedCompletionStatus*(CompletionPort: Handle, dwNumberOfBytesTransferred: DWORD, @@ -1112,7 +1112,7 @@ else: {.stdcall, dynlib: "kernel32", importc: "ReadConsoleInputW".} type - LPFIBER_START_ROUTINE* = proc (param: pointer): void {.stdcall.} + LPFIBER_START_ROUTINE* = proc (param: pointer) {.stdcall.} const FIBER_FLAG_FLOAT_SWITCH* = 0x01 @@ -1121,8 +1121,8 @@ proc CreateFiber*(stackSize: int, fn: LPFIBER_START_ROUTINE, param: pointer): po proc CreateFiberEx*(stkCommit: int, stkReserve: int, flags: int32, fn: LPFIBER_START_ROUTINE, param: pointer): pointer {.stdcall, discardable, dynlib: "kernel32", importc.} proc ConvertThreadToFiber*(param: pointer): pointer {.stdcall, discardable, dynlib: "kernel32", importc.} proc ConvertThreadToFiberEx*(param: pointer, flags: int32): pointer {.stdcall, discardable, dynlib: "kernel32", importc.} -proc DeleteFiber*(fiber: pointer): void {.stdcall, discardable, dynlib: "kernel32", importc.} -proc SwitchToFiber*(fiber: pointer): void {.stdcall, discardable, dynlib: "kernel32", importc.} +proc DeleteFiber*(fiber: pointer) {.stdcall, discardable, dynlib: "kernel32", importc.} +proc SwitchToFiber*(fiber: pointer) {.stdcall, discardable, dynlib: "kernel32", importc.} proc GetCurrentFiber*(): pointer {.stdcall, importc, header: "windows.h".} proc toFILETIME*(t: int64): FILETIME = From b50776dd2facae166eb9d1bdbcff205c01adc25b Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 23 Mar 2021 00:30:41 -0700 Subject: [PATCH 1761/3768] refs #16338 make thttpclient_ssl_remotenetwork.nim less flaky (#17457) --- .../thttpclient_ssl_remotenetwork.nim | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/tests/untestable/thttpclient_ssl_remotenetwork.nim b/tests/untestable/thttpclient_ssl_remotenetwork.nim index eea65bf0b..f56fe274b 100644 --- a/tests/untestable/thttpclient_ssl_remotenetwork.nim +++ b/tests/untestable/thttpclient_ssl_remotenetwork.nim @@ -115,12 +115,23 @@ when enableRemoteNetworking and (defined(nimTestsEnableFlaky) or not defined(win else: # this is unexpected + var fatal = true + var msg = "" if raised: - echo " $# ($#) raised: $#" % [desc, $category, exception_msg] + msg = " $# ($#) raised: $#" % [desc, $category, exception_msg] + if "500 Internal Server Error" in exception_msg: + # refs https://github.com/nim-lang/Nim/issues/16338#issuecomment-804300278 + # we got: `good (good) raised: 500 Internal Server Error` + fatal = false + msg.add " (http 500 => assuming this is not our problem)" else: - echo " $# ($#) did not raise" % [desc, $category] - if category in {good, dubious, bad}: + msg = " $# ($#) did not raise" % [desc, $category] + + if category in {good, dubious, bad} and fatal: + echo "D20210322T121353: error: " & msg fail() + else: + echo "D20210322T121353: warning: " & msg suite "SSL certificate check - httpclient": From d78ebe4a0eadb889255c77f6e375ecec9c706f19 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 23 Mar 2021 00:33:09 -0700 Subject: [PATCH 1762/3768] fix #17454 (#17461) --- lib/core/macros.nim | 20 +++++++++----------- tests/stdlib/tmacros.nim | 5 ++++- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 491235d8b..8b45ed86b 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -1287,7 +1287,15 @@ proc `body=`*(someProc: NimNode, val: NimNode) {.compileTime.} = else: badNodeKind someProc, "body=" -proc basename*(a: NimNode): NimNode {.compileTime, benign.} +proc basename*(a: NimNode): NimNode {.raises: [].} = + ## Pull an identifier from prefix/postfix expressions. + case a.kind + of nnkIdent: result = a + of nnkPostfix, nnkPrefix: result = a[1] + of nnkPragmaExpr: result = basename(a[0]) + else: + error("Do not know how to get basename of (" & treeRepr(a) & ")\n" & + repr(a), a) proc `$`*(node: NimNode): string {.compileTime.} = ## Get the string of an identifier node. @@ -1347,16 +1355,6 @@ proc insert*(a: NimNode; pos: int; b: NimNode) {.compileTime.} = a[i + 1] = a[i] a[pos] = b -proc basename*(a: NimNode): NimNode = - ## Pull an identifier from prefix/postfix expressions. - case a.kind - of nnkIdent: result = a - of nnkPostfix, nnkPrefix: result = a[1] - of nnkPragmaExpr: result = basename(a[0]) - else: - error("Do not know how to get basename of (" & treeRepr(a) & ")\n" & - repr(a), a) - proc `basename=`*(a: NimNode; val: string) {.compileTime.}= case a.kind of nnkIdent: diff --git a/tests/stdlib/tmacros.nim b/tests/stdlib/tmacros.nim index 0cc1e340c..effe1032f 100644 --- a/tests/stdlib/tmacros.nim +++ b/tests/stdlib/tmacros.nim @@ -1,4 +1,4 @@ -import macros +import std/macros block: # hasArgOfName macro m(u: untyped): untyped = @@ -7,3 +7,6 @@ block: # hasArgOfName doAssert not hasArgOfName(params u,"nonexistent") proc p(s: string; i,j,k: int; b: bool; xs,ys: seq[int] = @[]) {.m.} = discard + +block: # bug #17454 + proc f(v: NimNode): string {.raises: [].} = $v From f3a642710989c3f47d5c2131efd69e3dbfedc336 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 23 Mar 2021 00:34:04 -0700 Subject: [PATCH 1763/3768] refs #17292 fix `repr` with `do:` (#17449) * refs #17292 fix `repr` with `do:` * address comment --- compiler/renderer.nim | 13 +++++-- tests/stdlib/trepr.nim | 80 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 3 deletions(-) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 13ff6941f..c36eaaf11 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -1007,12 +1007,19 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = of nkCall, nkConv, nkDotCall, nkPattern, nkObjConstr: if n.len > 1 and n.lastSon.kind in {nkStmtList, nkStmtListExpr}: accentedName(g, n[0]) - if n.len > 2: + var i = 1 + while i < n.len and n[i].kind notin {nkStmtList, nkStmtListExpr}: i.inc + if i > 1: put(g, tkParLe, "(") - gcomma(g, n, 1, -2) + gcomma(g, n, 1, i - 1 - n.len) put(g, tkParRi, ")") put(g, tkColon, ":") - gsub(g, n, n.len-1) + gsub(g, n, i) + for j in i+1 ..< n.len: + optNL(g) + put(g, tkDo, "do") + put(g, tkColon, ":") + gsub(g, n, j) elif n.len >= 1: case bracketKind(g, n[0]) of bkBracket: diff --git a/tests/stdlib/trepr.nim b/tests/stdlib/trepr.nim index 1d89d37b2..357854d67 100644 --- a/tests/stdlib/trepr.nim +++ b/tests/stdlib/trepr.nim @@ -6,6 +6,8 @@ discard """ # if excessive, could remove 'cpp' from targets from strutils import endsWith, contains +from std/macros import newLit +macro deb(a): string = newLit a.repr template main() = doAssert repr({3,5}) == "{3, 5}" @@ -65,5 +67,83 @@ template main() = else: doAssert reprOpenarray(arr) == "[1, 2, 3]" + block: # bug #17292 + template foo(a, b, c, d) = discard + block: + let a = deb: + foo(1, 2, 3, 4) + doAssert a == "\nfoo(1, 2, 3, 4)" + block: + let a = deb: + foo(1, 2, 3): 4 + doAssert a == """ + +foo(1, 2, 3): + 4""" + + block: + let a = deb: + foo(1, 2): 3 + do: 4 + doAssert a == """ + +foo(1, 2): + 3 +do: + 4""" + + block: + let a = deb: + foo(1): 3 + do: 3 + do: 4 + doAssert a == """ + +foo(1): + 3 +do: + 3 +do: + 4""" + + block: + let a = deb: + foo(1): + 3 + do: + discard + 3 + do: + discard + 4 + + doAssert a == """ + +foo(1): + 3 +do: + discard + 3 +do: + discard + 4""" + + block: + let a = deb: + foo: 1 + do: 2 + do: 3 + do: 4 + doAssert a == """ + +foo: + 1 +do: + 2 +do: + 3 +do: + 4""" + static: main() main() From 1d19cd660f39b9e76005b849ae1f7ca966278438 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 23 Mar 2021 00:37:15 -0700 Subject: [PATCH 1764/3768] fix #17458 tnetconnect.nim flaky (#17459) * fix tests * fix #17458 --- tests/stdlib/tnetconnect.nim | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/tests/stdlib/tnetconnect.nim b/tests/stdlib/tnetconnect.nim index e27499651..b74545710 100644 --- a/tests/stdlib/tnetconnect.nim +++ b/tests/stdlib/tnetconnect.nim @@ -1,22 +1,26 @@ discard """ - cmd: "nim c -r -d:ssl $file" - exitcode: 0 + matrix: "-d:ssl" """ import std/net +from std/strutils import `%` -# Issue 15215 - https://github.com/nim-lang/Nim/issues/15215 +# bug #15215 proc test() = - var - ctx = newContext() - socket = newSocket() + let ctx = newContext() - wrapSocket(ctx, socket) + proc fn(url: string) = + let socket = newSocket() + defer: close(socket) + connect(socket, url, Port(443), 5000) # typically 20 could be enough + send(socket, "GET / HTTP/1.0\nHost: $#\nConnection: close\n\n" % [url]) + wrapSocket(ctx, socket) - connect(socket, "www.nim-lang.org", Port(443), 5000) - - send(socket, "GET / HTTP/1.0\nHost: www.nim-lang.org\nConnection: close\n\n") - - close(socket) + # trying 2 sites makes it more resilent: refs #17458 this could give: + # Error: unhandled exception: Call to 'connect' timed out. [TimeoutError] + try: + fn("www.nim-lang.org") + except TimeoutError: + fn("www.google.com") test() From d6a16020fe98b467459544ee4cfafe61d681f918 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 23 Mar 2021 08:52:00 +0100 Subject: [PATCH 1765/3768] IC: backend: remember produced type information (#17440) --- compiler/ast.nim | 15 ++++---------- compiler/ccgtypes.nim | 14 ++++++++++--- compiler/ic/cbackend.nim | 43 ++++++++++++++++++++++++++++++++------- compiler/ic/ic.nim | 11 ++++++++-- compiler/ic/rodfiles.nim | 1 + compiler/modulegraphs.nim | 29 ++++++++++++++++++++++---- compiler/passes.nim | 5 +++++ compiler/sem.nim | 2 +- compiler/semdata.nim | 15 +------------- 9 files changed, 93 insertions(+), 42 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 50a2fb58c..1f3d5f129 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1229,17 +1229,10 @@ proc newSym*(symKind: TSymKind, name: PIdent, id: ItemId, owner: PSym, result = PSym(name: name, kind: symKind, flags: {}, info: info, itemId: id, options: options, owner: owner, offset: defaultOffset) when false: - if id.item > 2141: - let s = getStackTrace() - const words = ["createTypeBoundOps", - "initOperators", - "generateInstance", - "semIdentDef", "addLocalDecl"] - for w in words: - if w in s: - x.inc w - return - x.inc "" + if id.module == 48 and id.item == 39: + writeStackTrace() + echo "kind ", symKind, " ", name.s + if owner != nil: echo owner.name.s proc astdef*(s: PSym): PNode = # get only the definition (initializer) portion of the ast diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 73ee9cb8a..e17a55542 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -1386,7 +1386,6 @@ proc genTypeInfoV2(m: BModule, t: PType; info: TLineInfo): Rope = let owner = t.skipTypes(typedescPtrs).itemId.module if owner != m.module.position and moduleOpenForCodegen(m, owner): # make sure the type info is created in the owner module - assert m.g.modules[owner] != nil discard genTypeInfoV2(m.g.modules[owner], origType, info) # reference the type info as extern here discard cgsym(m, "TNimTypeV2") @@ -1456,18 +1455,27 @@ proc genTypeInfoV1(m: BModule, t: PType; info: TLineInfo): Rope = result = "NTI$1$2_" % [rope(typeToC(t)), rope($sig)] m.typeInfoMarker[sig] = result - let owner = t.skipTypes(typedescPtrs).itemId.module + let old = m.g.graph.emittedTypeInfo.getOrDefault($result) + if old != FileIndex(0): + discard cgsym(m, "TNimType") + discard cgsym(m, "TNimNode") + declareNimType(m, "TNimType", result, old.int) + return prefixTI.rope & result & ")".rope + + var owner = t.skipTypes(typedescPtrs).itemId.module if owner != m.module.position and moduleOpenForCodegen(m, owner): # make sure the type info is created in the owner module - assert m.g.modules[owner] != nil discard genTypeInfoV1(m.g.modules[owner], origType, info) # reference the type info as extern here discard cgsym(m, "TNimType") discard cgsym(m, "TNimNode") declareNimType(m, "TNimType", result, owner) return prefixTI.rope & result & ")".rope + else: + owner = m.module.position.int32 m.g.typeInfoMarker[sig] = (str: result, owner: owner) + rememberEmittedTypeInfo(m.g.graph, FileIndex(owner), $result) case t.kind of tyEmpty, tyVoid: result = rope"0" diff --git a/compiler/ic/cbackend.nim b/compiler/ic/cbackend.nim index 88b2a9477..34ee59d52 100644 --- a/compiler/ic/cbackend.nim +++ b/compiler/ic/cbackend.nim @@ -18,7 +18,7 @@ ## also doing cross-module dependency tracking and DCE that we don't need ## anymore. DCE is now done as prepass over the entire packed module graph. -import std/[packedsets, algorithm] +import std/[packedsets, algorithm, tables] # std/intsets would give `UnusedImport`, pending https://github.com/nim-lang/Nim/issues/14246 import ".."/[ast, options, lineinfos, modulegraphs, cgendata, cgen, pathutils, extccomp, msgs] @@ -45,6 +45,10 @@ proc generateCodeForModule(g: ModuleGraph; m: var LoadedModule; alive: var Alive finalCodegenActions(g, bmod, newNodeI(nkStmtList, m.module.info)) +proc replayTypeInfo(g: ModuleGraph; m: var LoadedModule; origin: FileIndex) = + for x in mitems(m.fromDisk.emittedTypeInfo): + g.emittedTypeInfo[x] = origin + proc addFileToLink(config: ConfigRef; m: PSym) = let filename = AbsoluteFile toFullPath(config, m.position.FileIndex) let ext = @@ -59,11 +63,28 @@ proc addFileToLink(config: ConfigRef; m: PSym) = flags: {CfileFlag.Cached}) addFileToCompile(config, cf) -proc aliveSymsChanged(config: ConfigRef; position: int; alive: AliveSyms): bool = +when defined(debugDce): + import std / [os, packedsets] + +proc storeAliveSymsImpl(asymFile: AbsoluteFile; s: seq[int32]) = + var f = rodfiles.create(asymFile.string) + f.storeHeader() + f.storeSection aliveSymsSection + f.storeSeq(s) + close f + +template prepare {.dirty.} = let asymFile = toRodFile(config, AbsoluteFile toFullPath(config, position.FileIndex), ".alivesyms") var s = newSeqOfCap[int32](alive[position].len) for a in items(alive[position]): s.add int32(a) sort(s) + +proc storeAliveSyms(config: ConfigRef; position: int; alive: AliveSyms) = + prepare() + storeAliveSymsImpl(asymFile, s) + +proc aliveSymsChanged(config: ConfigRef; position: int; alive: AliveSyms): bool = + prepare() var f2 = rodfiles.open(asymFile.string) f2.loadHeader() f2.loadSection aliveSymsSection @@ -73,12 +94,17 @@ proc aliveSymsChanged(config: ConfigRef; position: int; alive: AliveSyms): bool if f2.err == ok and oldData == s: result = false else: + when defined(debugDce): + let oldAsSet = toPackedSet[int32](oldData) + let newAsSet = toPackedSet[int32](s) + echo "set of live symbols changed ", asymFile.changeFileExt("rod"), " ", position, " ", f2.err + echo "in old but not in new ", oldAsSet.difference(newAsSet) + echo "in new but not in old ", newAsSet.difference(oldAsSet) + + if execShellCmd(getAppFilename() & " rod " & quoteShell(asymFile.changeFileExt("rod"))) != 0: + echo "command failed" result = true - var f = rodfiles.create(asymFile.string) - f.storeHeader() - f.storeSection aliveSymsSection - f.storeSeq(s) - close f + storeAliveSymsImpl(asymFile, s) proc generateCode*(g: ModuleGraph) = ## The single entry point, generate C(++) code for the entire @@ -95,6 +121,8 @@ proc generateCode*(g: ModuleGraph) = assert false of storing, outdated: generateCodeForModule(g, g.packed[i], alive) + closeRodFile(g, g.packed[i].module) + storeAliveSyms(g.config, g.packed[i].module.position, alive) of loaded: # Even though this module didn't change, DCE might trigger a change. # Consider this case: Module A uses symbol S from B and B does not use @@ -104,3 +132,4 @@ proc generateCode*(g: ModuleGraph) = generateCodeForModule(g, g.packed[i], alive) else: addFileToLink(g.config, g.packed[i].module) + replayTypeInfo(g, g.packed[i], FileIndex(i)) diff --git a/compiler/ic/ic.nim b/compiler/ic/ic.nim index 99a68e0f0..230b4d087 100644 --- a/compiler/ic/ic.nim +++ b/compiler/ic/ic.nim @@ -42,6 +42,8 @@ type methodsPerType*: seq[(PackedItemId, int, PackedItemId)] enumToStringProcs*: seq[(PackedItemId, PackedItemId)] + emittedTypeInfo*: seq[string] + sh*: Shared cfg: PackedConfig @@ -58,7 +60,7 @@ type config*: ConfigRef proc isActive*(e: PackedEncoder): bool = e.config != nil -proc disable*(e: var PackedEncoder) = e.config = nil +proc disable(e: var PackedEncoder) = e.config = nil template primConfigFields(fn: untyped) {.dirty.} = fn backend @@ -552,6 +554,7 @@ proc loadRodFile*(filename: AbsoluteFile; m: var PackedModule; config: ConfigRef loadSeqSection attachedOpsSection, m.attachedOps loadSeqSection methodsPerTypeSection, m.methodsPerType loadSeqSection enumToStringProcsSection, m.enumToStringProcs + loadSeqSection typeInfoSection, m.emittedTypeInfo close(f) result = f.err @@ -614,6 +617,7 @@ proc saveRodFile*(filename: AbsoluteFile; encoder: var PackedEncoder; m: var Pac storeSeqSection attachedOpsSection, m.attachedOps storeSeqSection methodsPerTypeSection, m.methodsPerType storeSeqSection enumToStringProcsSection, m.enumToStringProcs + storeSeqSection typeInfoSection, m.emittedTypeInfo close(f) encoder.disable() @@ -1139,7 +1143,10 @@ proc rodViewer*(rodfile: AbsoluteFile; config: ConfigRef, cache: IdentCache) = echo "all symbols" for i in 0..high(m.sh.syms): - echo " ", m.sh.strings[m.sh.syms[i].name], " local ID: ", i + if m.sh.syms[i].name != LitId(0): + echo " ", m.sh.strings[m.sh.syms[i].name], " local ID: ", i, " kind ", m.sh.syms[i].kind + else: + echo " local ID: ", i, " kind ", m.sh.syms[i].kind echo "symbols: ", m.sh.syms.len, " types: ", m.sh.types.len, " top level nodes: ", m.topLevel.nodes.len, " other nodes: ", m.bodies.nodes.len, diff --git a/compiler/ic/rodfiles.nim b/compiler/ic/rodfiles.nim index fa0a7c734..a518870f8 100644 --- a/compiler/ic/rodfiles.nim +++ b/compiler/ic/rodfiles.nim @@ -36,6 +36,7 @@ type attachedOpsSection methodsPerTypeSection enumToStringProcsSection + typeInfoSection # required by the backend aliveSymsSection # beware, this is stored in a `.alivesyms` file. RodFileError* = enum diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index de3773ca5..9ac76457c 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -11,9 +11,8 @@ ## represents a complete Nim project. Single modules can either be kept in RAM ## or stored in a rod-file. -import ast, astalgo, intsets, tables, options, lineinfos, hashes, idents, - btrees, md5, ropes, msgs - +import std / [intsets, tables, hashes, md5] +import ast, astalgo, options, lineinfos,idents, btrees, ropes, msgs, pathutils import ic / [packed_ast, ic] type @@ -60,6 +59,7 @@ type attachedOps*: array[TTypeAttachedOp, Table[ItemId, PSym]] # Type ID, destructors, etc. methodsPerType*: Table[ItemId, seq[(int, LazySym)]] # Type ID, attached methods enumToStringProcs*: Table[ItemId, LazySym] + emittedTypeInfo*: Table[string, FileIndex] startupPackedConfig*: PackedConfig packageSyms*: TStrTable @@ -68,7 +68,6 @@ type importDeps*: Table[FileIndex, seq[FileIndex]] # explicit import module dependencies suggestMode*: bool # whether we are in nimsuggest mode or not. invalidTransitiveClosure: bool - systemModuleComplete*: bool inclToMod*: Table[FileIndex, FileIndex] # mapping of include file to the # first module that included it importStack*: seq[FileIndex] # The current import stack. Used for detecting recursive @@ -435,6 +434,7 @@ proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph = result.canonTypes = initTable[SigHash, PType]() result.symBodyHashes = initTable[int, SigHash]() result.operators = initOperators(result) + result.emittedTypeInfo = initTable[string, FileIndex]() proc resetAllModules*(g: ModuleGraph) = initStrTable(g.packageSyms) @@ -455,6 +455,27 @@ proc getModule*(g: ModuleGraph; fileIdx: FileIndex): PSym = elif fileIdx.int32 < g.ifaces.len: result = g.ifaces[fileIdx.int32].module +proc rememberEmittedTypeInfo*(g: ModuleGraph; m: FileIndex; ti: string) = + #assert(not isCachedModule(g, m.int32)) + if g.config.symbolFiles != disabledSf: + #assert g.encoders[m.int32].isActive + g.packed[m.int32].fromDisk.emittedTypeInfo.add ti + +proc closeRodFile*(g: ModuleGraph; m: PSym) = + if g.config.symbolFiles in {readOnlySf, v2Sf}: + # For stress testing we seek to reload the symbols from memory. This + # way much of the logic is tested but the test is reproducible as it does + # not depend on the hard disk contents! + let mint = m.position + saveRodFile(toRodFile(g.config, AbsoluteFile toFullPath(g.config, FileIndex(mint))), + g.encoders[mint], g.packed[mint].fromDisk) + elif g.config.symbolFiles == stressTest: + # debug code, but maybe a good idea for production? Could reduce the compiler's + # memory consumption considerably at the cost of more loads from disk. + let mint = m.position + simulateCachedModule(g, m, g.packed[mint].fromDisk) + g.packed[mint].status = loaded + proc dependsOn(a, b: int): int {.inline.} = (a shl 15) + b proc addDep*(g: ModuleGraph; m: PSym, dep: FileIndex) = diff --git a/compiler/passes.nim b/compiler/passes.nim index 11799b122..3debce1f6 100644 --- a/compiler/passes.nim +++ b/compiler/passes.nim @@ -187,4 +187,9 @@ proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator; closeParser(p) if s.kind != llsStdIn: break closePasses(graph, a) + if graph.config.backend notin {backendC, backendCpp, backendObjc}: + # We only write rod files here if no C-like backend is active. + # The C-like backends have been patched to support the IC mechanism. + # They are responsible for closing the rod files. See `cbackend.nim`. + closeRodFile(graph, module) result = true diff --git a/compiler/sem.nim b/compiler/sem.nim index bb42b5c1b..6b3f0c80d 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -661,7 +661,7 @@ proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode = result.add(c.module.ast) popOwner(c) popProcCon(c) - saveRodFile(c) + sealRodFile(c) const semPass* = makePass(myOpen, myProcess, myClose, isFrontend = true) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 5cd440cc1..c655047e2 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -560,23 +560,10 @@ proc addToGenericCache*(c: PContext; s: PSym; inst: PType) = if c.config.symbolFiles != disabledSf: storeTypeInst(c.encoder, c.packedRepr, s, inst) -proc saveRodFile*(c: PContext) = +proc sealRodFile*(c: PContext) = if c.config.symbolFiles != disabledSf: if c.graph.vm != nil: for (m, n) in PCtx(c.graph.vm).vmstateDiff: if m == c.module: addPragmaComputation(c, n) - if sfSystemModule in c.module.flags: - c.graph.systemModuleComplete = true c.idgen.sealed = true # no further additions are allowed - if c.config.symbolFiles != stressTest: - # For stress testing we seek to reload the symbols from memory. This - # way much of the logic is tested but the test is reproducible as it does - # not depend on the hard disk contents! - saveRodFile(toRodFile(c.config, AbsoluteFile toFullPath(c.config, FileIndex c.module.position)), - c.encoder, c.packedRepr) - else: - # debug code, but maybe a good idea for production? Could reduce the compiler's - # memory consumption considerably at the cost of more loads from disk. - simulateCachedModule(c.graph, c.module, c.packedRepr) - c.graph.packed[c.module.position].status = loaded From 64e6670de45f8f5a470e5cc3ad9ed2cb85f0b57c Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 23 Mar 2021 01:31:23 -0700 Subject: [PATCH 1766/3768] fix #16973 ; nim doc now shows correct, canonical import name in title (#16999) * nim doc now shows correct import name in title --- compiler/docgen.nim | 25 +++++++++++----- compiler/options.nim | 29 ++++++++++++------- .../expected/index.html | 4 +-- 3 files changed, 38 insertions(+), 20 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 57d5e7b01..dded231d7 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -63,7 +63,22 @@ proc prettyString(a: object): string = for k, v in fieldPairs(a): result.add k & ": " & $v & "\n" -proc presentationPath*(conf: ConfigRef, file: AbsoluteFile, isTitle = false): RelativeFile = +proc canonicalImport*(conf: ConfigRef, file: AbsoluteFile): string = + ##[ + Shows the canonical module import, e.g.: + system, std/tables, fusion/pointers, system/assertions, std/private/asciitables + ]## + var ret = getRelativePathFromConfigPath(conf, file, isTitle = true) + let dir = getNimbleFile(conf, $file).parentDir.AbsoluteDir + if not dir.isEmpty: + let relPath = relativeTo(file, dir) + if not relPath.isEmpty and (ret.isEmpty or relPath.string.len < ret.string.len): + ret = relPath + if ret.isEmpty: + ret = relativeTo(file, conf.projectPath) + result = ret.string.nativeToUnixPath.changeFileExt("") + +proc presentationPath*(conf: ConfigRef, file: AbsoluteFile): RelativeFile = ## returns a relative file that will be appended to outDir let file2 = $file template bail() = @@ -97,10 +112,7 @@ proc presentationPath*(conf: ConfigRef, file: AbsoluteFile, isTitle = false): Re bail() if isAbsolute(result.string): result = file.string.splitPath()[1].RelativeFile - if isTitle: - result = result.string.nativeToUnixPath.RelativeFile - else: - result = result.string.replace("..", dotdotMangle).RelativeFile + result = result.string.replace("..", dotdotMangle).RelativeFile doAssert not result.isEmpty doAssert not isAbsolute(result.string) @@ -1259,8 +1271,7 @@ proc genOutFile(d: PDoc, groupedToc = false): Rope = setIndexTerm(d[], external, "", title) else: # Modules get an automatic title for the HTML, but no entry in the index. - # better than `extractFilename(changeFileExt(d.filename, ""))` as it disambiguates dups - title = $presentationPath(d.conf, AbsoluteFile d.filename, isTitle = true).changeFileExt("") + title = canonicalImport(d.conf, AbsoluteFile d.filename) var subtitle = "".rope if d.meta[metaSubtitle] != "": dispA(d.conf, subtitle, "

                                      $1

                                      ", diff --git a/compiler/options.nim b/compiler/options.nim index a2d6a51b3..2d63043df 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -761,8 +761,25 @@ when (NimMajor, NimMinor) < (1, 1) or not declared(isRelativeTo): let ret = relativePath(path, base) result = path.len > 0 and not ret.startsWith ".." -proc getRelativePathFromConfigPath*(conf: ConfigRef; f: AbsoluteFile): RelativeFile = +const stdlibDirs = [ + "pure", "core", "arch", + "pure/collections", + "pure/concurrency", + "pure/unidecode", "impure", + "wrappers", "wrappers/linenoise", + "windows", "posix", "js"] + +const + pkgPrefix = "pkg/" + stdPrefix = "std/" + +proc getRelativePathFromConfigPath*(conf: ConfigRef; f: AbsoluteFile, isTitle = false): RelativeFile = let f = $f + if isTitle: + for dir in stdlibDirs: + let path = conf.libpath.string / dir / f.lastPathPart + if path.cmpPaths(f) == 0: + return RelativeFile(stdPrefix & f.splitFile.name) template search(paths) = for it in paths: let it = $it @@ -784,18 +801,8 @@ proc findFile*(conf: ConfigRef; f: string; suppressStdlib = false): AbsoluteFile result = rawFindFile2(conf, RelativeFile f.toLowerAscii) patchModule(conf) -const stdlibDirs = [ - "pure", "core", "arch", - "pure/collections", - "pure/concurrency", - "pure/unidecode", "impure", - "wrappers", "wrappers/linenoise", - "windows", "posix", "js"] - proc findModule*(conf: ConfigRef; modulename, currentModule: string): AbsoluteFile = # returns path to module - const pkgPrefix = "pkg/" - const stdPrefix = "std/" var m = addFileExt(modulename, NimExt) if m.startsWith(pkgPrefix): result = findFile(conf, m.substr(pkgPrefix.len), suppressStdlib = true) diff --git a/nimdoc/test_out_index_dot_html/expected/index.html b/nimdoc/test_out_index_dot_html/expected/index.html index 4646732f1..db7470050 100644 --- a/nimdoc/test_out_index_dot_html/expected/index.html +++ b/nimdoc/test_out_index_dot_html/expected/index.html @@ -17,7 +17,7 @@ -foo +nimdoc/test_out_index_dot_html/foo @@ -64,7 +64,7 @@ window.addEventListener('DOMContentLoaded', main);
                                      -

                                      foo

                                      +

                                      nimdoc/test_out_index_dot_html/foo

                                      From c719d79d232c1dc8d878b0799e1011519652016f Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 23 Mar 2021 17:49:02 +0800 Subject: [PATCH 1767/3768] add algorithm.merge (#16182) * add merge to algorithm * Apply suggestions from code review * Update lib/pure/algorithm.nim * Apply suggestions from code review * Update changelog.md Co-authored-by: ee7 <45465154+ee7@users.noreply.github.com> --- changelog.md | 3 + lib/pure/algorithm.nim | 94 +++++++++++++++++++++ tests/stdlib/talgorithm.nim | 159 +++++++++++++++++++++++++++++++++++- 3 files changed, 255 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 00fc970bf..78817cf85 100644 --- a/changelog.md +++ b/changelog.md @@ -212,6 +212,9 @@ - `std/options` changed `$some(3)` to `"some(3)"` instead of `"Some(3)"` and `$none(int)` to `"none(int)"` instead of `"None[int]"`. + +- Added `algorithm.merge`. + - Added `std/jsfetch` module [Fetch](https://developer.mozilla.org/docs/Web/API/Fetch_API) wrapper for JavaScript target. diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim index a32df65bb..0410d65ee 100644 --- a/lib/pure/algorithm.nim +++ b/lib/pure/algorithm.nim @@ -42,6 +42,8 @@ runnableExamples: ## * `sequtils module`_ for working with the built-in seq type ## * `tables module`_ for sorting tables +import std/private/since + type SortOrder* = enum Descending, Ascending @@ -559,6 +561,98 @@ proc isSorted*[T](a: openArray[T], order = SortOrder.Ascending): bool = assert isSorted(e) == false isSorted(a, system.cmp[T], order) +proc merge*[T]( + result: var seq[T], + x, y: openArray[T], cmp: proc(x, y: T): int {.closure.} +) {.since: (1, 5, 1).} = + ## Merges two sorted `openArray`. `x` and `y` are assumed to be sorted. + ## If you do not wish to provide your own `cmp`, + ## you may use `system.cmp` or instead call the overloaded + ## version of `merge`, which uses `system.cmp`. + ## + ## .. note:: The original data of `result` is not cleared, + ## new data is appended to `result`. + ## + ## **See also:** + ## * `merge proc<#merge,seq[T],openArray[T],openArray[T]>`_ + runnableExamples: + let x = @[1, 3, 6] + let y = @[2, 3, 4] + + block: + var merged = @[7] # new data is appended to merged sequence + merged.merge(x, y, system.cmp[int]) + assert merged == @[7, 1, 2, 3, 3, 4, 6] + + block: + var merged = @[7] # if you only want new data, clear merged sequence first + merged.setLen(0) + merged.merge(x, y, system.cmp[int]) + assert merged.isSorted + assert merged == @[1, 2, 3, 3, 4, 6] + + import std/sugar + + var res: seq[(int, int)] + res.merge([(1, 1)], [(1, 2)], (a, b) => a[0] - b[0]) + assert res == @[(1, 1), (1, 2)] + + assert seq[int].default.dup(merge([1, 3], [2, 4])) == @[1, 2, 3, 4] + + let + sizeX = x.len + sizeY = y.len + oldLen = result.len + + result.setLen(oldLen + sizeX + sizeY) + + var + ix = 0 + iy = 0 + i = oldLen + + while true: + if ix == sizeX: + while iy < sizeY: + result[i] = y[iy] + inc i + inc iy + return + + if iy == sizeY: + while ix < sizeX: + result[i] = x[ix] + inc i + inc ix + return + + let itemX = x[ix] + let itemY = y[iy] + + if cmp(itemX, itemY) > 0: # to have a stable sort + result[i] = itemY + inc iy + else: + result[i] = itemX + inc ix + + inc i + +proc merge*[T](result: var seq[T], x, y: openArray[T]) {.inline, since: (1, 5, 1).} = + ## Shortcut version of `merge` that uses `system.cmp[T]` as the comparison function. + ## + ## **See also:** + ## * `merge proc<#merge,seq[T],openArray[T],openArray[T],proc(T,T)>`_ + runnableExamples: + let x = [5, 10, 15, 20, 25] + let y = [50, 40, 30, 20, 10].sorted + + var merged: seq[int] + merged.merge(x, y) + assert merged.isSorted + assert merged == @[5, 10, 10, 15, 20, 20, 25, 30, 40, 50] + merge(result, x, y, system.cmp) + proc product*[T](x: openArray[seq[T]]): seq[seq[T]] = ## Produces the Cartesian product of the array. ## Every element of the result is a combination of one element from each seq in `x`, diff --git a/tests/stdlib/talgorithm.nim b/tests/stdlib/talgorithm.nim index fecd85820..5bb5a6bdf 100644 --- a/tests/stdlib/talgorithm.nim +++ b/tests/stdlib/talgorithm.nim @@ -4,7 +4,8 @@ discard """ ''' """ #12928,10456 -import std/[sequtils, algorithm, json] + +import std/[sequtils, algorithm, json, sugar] proc test() = try: @@ -114,3 +115,159 @@ block: doAssert binarySearch(moreData, 6) == -1 doAssert binarySearch(moreData, 4711) == 4 doAssert binarySearch(moreData, 4712) == -1 + +# merge +proc main() = + block: + var x = @[1, 7, 8, 11, 21, 33, 45, 99] + var y = @[6, 7, 9, 12, 57, 66] + + var merged: seq[int] + merged.merge(x, y) + doAssert merged.isSorted + doAssert merged == sorted(x & y) + + block: + var x = @[111, 88, 76, 56, 45, 31, 22, 19, 11, 3] + var y = @[99, 85, 83, 82, 69, 64, 48, 42, 33, 31, 26, 13] + + var merged: seq[int] + merged.merge(x, y, (x, y) => -system.cmp(x, y)) + doAssert merged.isSorted((x, y) => -system.cmp(x, y)) + doAssert merged == sorted(x & y, SortOrder.Descending) + + block: + var x: seq[int] = @[] + var y = @[1] + + var merged: seq[int] + merged.merge(x, y) + doAssert merged.isSorted + doAssert merged.isSorted(SortOrder.Descending) + doAssert merged == @[1] + + block: + var x = [1, 3, 5, 5, 7] + var y: seq[int] = @[] + + var merged: seq[int] + merged.merge(x, y) + doAssert merged.isSorted + doAssert merged == @x + + block: + var x = [1, 3, 5, 5, 7] + var y: seq[int] = @[] + + var merged: seq[int] = @[1, 2, 3, 5, 6, 56, 99, 2, 34] + merged.merge(x, y) + doAssert merged == @[1, 2, 3, 5, 6, 56, 99, 2, 34, 1, 3, 5, 5, 7] + + + block: + var x: array[0, int] + var y = [1, 4, 6, 7, 9] + + var merged: seq[int] + merged.merge(x, y) + doAssert merged.isSorted + doAssert merged == @y + + block: + var x: array[0, int] + var y: array[0, int] + + var merged: seq[int] + merged.merge(x, y) + doAssert merged.isSorted + doAssert merged.len == 0 + + block: + var x: array[0, int] + var y: array[0, int] + + var merged: seq[int] = @[99, 99, 99] + merged.setLen(0) + merged.merge(x, y) + doAssert merged.isSorted + doAssert merged.len == 0 + + block: + var x: seq[int] + var y: seq[int] + + var merged: seq[int] + merged.merge(x, y) + doAssert merged.isSorted + doAssert merged.len == 0 + + block: + type + Record = object + id: int + + proc r(id: int): Record = + Record(id: id) + + proc cmp(x, y: Record): int = + if x.id == y.id: return 0 + if x.id < y.id: return -1 + result = 1 + + var x = @[r(-12), r(1), r(3), r(8), r(13), r(88)] + var y = @[r(4), r(7), r(12), r(13), r(77), r(99)] + + var merged: seq[Record] = @[] + merged.merge(x, y, cmp) + doAssert merged.isSorted(cmp) + doAssert merged.len == 12 + + block: + type + Record = object + id: int + + proc r(id: int): Record = + Record(id: id) + + proc ascendingCmp(x, y: Record): int = + if x.id == y.id: return 0 + if x.id < y.id: return -1 + result = 1 + + proc descendingCmp(x, y: Record): int = + if x.id == y.id: return 0 + if x.id < y.id: return 1 + result = -1 + + var x = @[r(-12), r(1), r(3), r(8), r(13), r(88)] + var y = @[r(4), r(7), r(12), r(13), r(77), r(99)] + + var merged: seq[Record] + merged.setLen(0) + merged.merge(x, y, ascendingCmp) + doAssert merged.isSorted(ascendingCmp) + doAssert merged == sorted(x & y, ascendingCmp) + + reverse(x) + reverse(y) + + merged.setLen(0) + merged.merge(x, y, descendingCmp) + doAssert merged.isSorted(descendingCmp) + doAssert merged == sorted(x & y, ascendingCmp, SortOrder.Descending) + + reverse(x) + reverse(y) + merged.setLen(0) + merged.merge(x, y, proc (x, y: Record): int = -descendingCmp(x, y)) + doAssert merged.isSorted(proc (x, y: Record): int = -descendingCmp(x, y)) + doAssert merged == sorted(x & y, ascendingCmp) + + + var x: seq[(int, int)] + x.merge([(1,1)], [(1,2)], (a,b) => a[0] - b[0]) + doAssert x == @[(1, 1), (1, 2)] + +static: main() +main() From c27cd83265558195316e0d3b6d13ccadcaecc728 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 23 Mar 2021 02:51:21 -0700 Subject: [PATCH 1768/3768] remove {.compileTime.} for procs with NimNode: redundant because of tfTriggersCompileTime (#17463) --- lib/core/macros.nim | 193 +++++++++++++++++++--------------------- lib/pure/asyncmacro.nim | 13 ++- lib/pure/htmlgen.nim | 4 +- lib/pure/json.nim | 4 +- lib/pure/sugar.nim | 2 +- lib/pure/xmltree.nim | 2 +- 6 files changed, 106 insertions(+), 112 deletions(-) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 8b45ed86b..5a556fc8d 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -404,29 +404,29 @@ proc warning*(msg: string, n: NimNode = nil) {.magic: "NWarning", benign.} proc hint*(msg: string, n: NimNode = nil) {.magic: "NHint", benign.} ## Writes a hint message at compile time. -proc newStrLitNode*(s: string): NimNode {.compileTime, noSideEffect.} = +proc newStrLitNode*(s: string): NimNode {.noSideEffect.} = ## Creates a string literal node from `s`. result = newNimNode(nnkStrLit) result.strVal = s -proc newCommentStmtNode*(s: string): NimNode {.compileTime, noSideEffect.} = +proc newCommentStmtNode*(s: string): NimNode {.noSideEffect.} = ## Creates a comment statement node. result = newNimNode(nnkCommentStmt) result.strVal = s -proc newIntLitNode*(i: BiggestInt): NimNode {.compileTime.} = +proc newIntLitNode*(i: BiggestInt): NimNode = ## Creates an int literal node from `i`. result = newNimNode(nnkIntLit) result.intVal = i -proc newFloatLitNode*(f: BiggestFloat): NimNode {.compileTime.} = +proc newFloatLitNode*(f: BiggestFloat): NimNode = ## Creates a float literal node from `f`. result = newNimNode(nnkFloatLit) result.floatVal = f {.push warnings: off.} -proc newIdentNode*(i: NimIdent): NimNode {.compileTime, deprecated.} = +proc newIdentNode*(i: NimIdent): NimNode {.deprecated: "use ident(string)".} = ## Creates an identifier node from `i`. result = newNimNode(nnkIdent) result.ident = i @@ -478,7 +478,7 @@ proc callsite*(): NimNode {.magic: "NCallSite", benign, deprecated: "Deprecated since v0.18.1; use varargs[untyped] in the macro prototype instead".} ## Returns the AST of the invocation expression that invoked this macro. -proc toStrLit*(n: NimNode): NimNode {.compileTime.} = +proc toStrLit*(n: NimNode): NimNode = ## Converts the AST `n` to the concrete Nim code and wraps that ## in a string literal node. return newStrLitNode(repr(n)) @@ -504,11 +504,11 @@ proc getFile(arg: NimNode): string {.magic: "NLineInfo", noSideEffect.} proc copyLineInfo*(arg: NimNode, info: NimNode) {.magic: "NLineInfo", noSideEffect.} ## Copy lineinfo from `info`. -proc lineInfoObj*(n: NimNode): LineInfo {.compileTime.} = +proc lineInfoObj*(n: NimNode): LineInfo = ## Returns `LineInfo` of `n`, using absolute path for `filename`. result = LineInfo(filename: n.getFile, line: n.getLine, column: n.getColumn) -proc lineInfo*(arg: NimNode): string {.compileTime.} = +proc lineInfo*(arg: NimNode): string = ## Return line info in the form `filepath(line, column)`. $arg.lineInfoObj @@ -522,14 +522,14 @@ proc internalErrorFlag*(): string {.magic: "NError", noSideEffect.} ## Some builtins set an error flag. This is then turned into a proper ## exception. **Note**: Ordinary application code should not call this. -proc parseExpr*(s: string): NimNode {.noSideEffect, compileTime.} = +proc parseExpr*(s: string): NimNode {.noSideEffect.} = ## Compiles the passed string to its AST representation. ## Expects a single expression. Raises `ValueError` for parsing errors. result = internalParseExpr(s) let x = internalErrorFlag() if x.len > 0: raise newException(ValueError, x) -proc parseStmt*(s: string): NimNode {.noSideEffect, compileTime.} = +proc parseStmt*(s: string): NimNode {.noSideEffect.} = ## Compiles the passed string to its AST representation. ## Expects one or more statements. Raises `ValueError` for parsing errors. result = internalParseStmt(s) @@ -626,25 +626,25 @@ proc quote*(bl: typed, op = "``"): NimNode {.magic: "QuoteAst", noSideEffect.} = doAssert y == 3 bar2() -proc expectKind*(n: NimNode, k: NimNodeKind) {.compileTime.} = +proc expectKind*(n: NimNode, k: NimNodeKind) = ## Checks that `n` is of kind `k`. If this is not the case, ## compilation aborts with an error message. This is useful for writing ## macros that check the AST that is passed to them. if n.kind != k: error("Expected a node of kind " & $k & ", got " & $n.kind, n) -proc expectMinLen*(n: NimNode, min: int) {.compileTime.} = +proc expectMinLen*(n: NimNode, min: int) = ## Checks that `n` has at least `min` children. If this is not the case, ## compilation aborts with an error message. This is useful for writing ## macros that check its number of arguments. if n.len < min: error("Expected a node with at least " & $min & " children, got " & $n.len, n) -proc expectLen*(n: NimNode, len: int) {.compileTime.} = +proc expectLen*(n: NimNode, len: int) = ## Checks that `n` has exactly `len` children. If this is not the case, ## compilation aborts with an error message. This is useful for writing ## macros that check its number of arguments. if n.len != len: error("Expected a node with " & $len & " children, got " & $n.len, n) -proc expectLen*(n: NimNode, min, max: int) {.compileTime.} = +proc expectLen*(n: NimNode, min, max: int) = ## Checks that `n` has a number of children in the range `min..max`. ## If this is not the case, compilation aborts with an error message. ## This is useful for writing macros that check its number of arguments. @@ -652,13 +652,12 @@ proc expectLen*(n: NimNode, min, max: int) {.compileTime.} = error("Expected a node with " & $min & ".." & $max & " children, got " & $n.len, n) proc newTree*(kind: NimNodeKind, - children: varargs[NimNode]): NimNode {.compileTime.} = + children: varargs[NimNode]): NimNode = ## Produces a new node with children. result = newNimNode(kind) result.add(children) -proc newCall*(theProc: NimNode, - args: varargs[NimNode]): NimNode {.compileTime.} = +proc newCall*(theProc: NimNode, args: varargs[NimNode]): NimNode = ## Produces a new call node. `theProc` is the proc that is called with ## the arguments `args[0..]`. result = newNimNode(nnkCall) @@ -667,7 +666,7 @@ proc newCall*(theProc: NimNode, {.push warnings: off.} -proc newCall*(theProc: NimIdent, args: varargs[NimNode]): NimNode {.compileTime, deprecated: +proc newCall*(theProc: NimIdent, args: varargs[NimNode]): NimNode {.deprecated: "Deprecated since v0.18.1; use 'newCall(string, ...)' or 'newCall(NimNode, ...)' instead".} = ## Produces a new call node. `theProc` is the proc that is called with ## the arguments `args[0..]`. @@ -678,128 +677,128 @@ proc newCall*(theProc: NimIdent, args: varargs[NimNode]): NimNode {.compileTime, {.pop.} proc newCall*(theProc: string, - args: varargs[NimNode]): NimNode {.compileTime.} = + args: varargs[NimNode]): NimNode = ## Produces a new call node. `theProc` is the proc that is called with ## the arguments `args[0..]`. result = newNimNode(nnkCall) result.add(newIdentNode(theProc)) result.add(args) -proc newLit*(c: char): NimNode {.compileTime.} = +proc newLit*(c: char): NimNode = ## Produces a new character literal node. result = newNimNode(nnkCharLit) result.intVal = ord(c) -proc newLit*(i: int): NimNode {.compileTime.} = +proc newLit*(i: int): NimNode = ## Produces a new integer literal node. result = newNimNode(nnkIntLit) result.intVal = i -proc newLit*(i: int8): NimNode {.compileTime.} = +proc newLit*(i: int8): NimNode = ## Produces a new integer literal node. result = newNimNode(nnkInt8Lit) result.intVal = i -proc newLit*(i: int16): NimNode {.compileTime.} = +proc newLit*(i: int16): NimNode = ## Produces a new integer literal node. result = newNimNode(nnkInt16Lit) result.intVal = i -proc newLit*(i: int32): NimNode {.compileTime.} = +proc newLit*(i: int32): NimNode = ## Produces a new integer literal node. result = newNimNode(nnkInt32Lit) result.intVal = i -proc newLit*(i: int64): NimNode {.compileTime.} = +proc newLit*(i: int64): NimNode = ## Produces a new integer literal node. result = newNimNode(nnkInt64Lit) result.intVal = i -proc newLit*(i: uint): NimNode {.compileTime.} = +proc newLit*(i: uint): NimNode = ## Produces a new unsigned integer literal node. result = newNimNode(nnkUIntLit) result.intVal = BiggestInt(i) -proc newLit*(i: uint8): NimNode {.compileTime.} = +proc newLit*(i: uint8): NimNode = ## Produces a new unsigned integer literal node. result = newNimNode(nnkUInt8Lit) result.intVal = BiggestInt(i) -proc newLit*(i: uint16): NimNode {.compileTime.} = +proc newLit*(i: uint16): NimNode = ## Produces a new unsigned integer literal node. result = newNimNode(nnkUInt16Lit) result.intVal = BiggestInt(i) -proc newLit*(i: uint32): NimNode {.compileTime.} = +proc newLit*(i: uint32): NimNode = ## Produces a new unsigned integer literal node. result = newNimNode(nnkUInt32Lit) result.intVal = BiggestInt(i) -proc newLit*(i: uint64): NimNode {.compileTime.} = +proc newLit*(i: uint64): NimNode = ## Produces a new unsigned integer literal node. result = newNimNode(nnkUInt64Lit) result.intVal = BiggestInt(i) -proc newLit*(b: bool): NimNode {.compileTime.} = +proc newLit*(b: bool): NimNode = ## Produces a new boolean literal node. result = if b: bindSym"true" else: bindSym"false" -proc newLit*(s: string): NimNode {.compileTime.} = +proc newLit*(s: string): NimNode = ## Produces a new string literal node. result = newNimNode(nnkStrLit) result.strVal = s when false: # the float type is not really a distinct type as described in https://github.com/nim-lang/Nim/issues/5875 - proc newLit*(f: float): NimNode {.compileTime.} = + proc newLit*(f: float): NimNode = ## Produces a new float literal node. result = newNimNode(nnkFloatLit) result.floatVal = f -proc newLit*(f: float32): NimNode {.compileTime.} = +proc newLit*(f: float32): NimNode = ## Produces a new float literal node. result = newNimNode(nnkFloat32Lit) result.floatVal = f -proc newLit*(f: float64): NimNode {.compileTime.} = +proc newLit*(f: float64): NimNode = ## Produces a new float literal node. result = newNimNode(nnkFloat64Lit) result.floatVal = f when declared(float128): - proc newLit*(f: float128): NimNode {.compileTime.} = + proc newLit*(f: float128): NimNode = ## Produces a new float literal node. result = newNimNode(nnkFloat128Lit) result.floatVal = f -proc newLit*(arg: enum): NimNode {.compileTime.} = +proc newLit*(arg: enum): NimNode = result = newCall( arg.typeof.getTypeInst[1], newLit(int(arg)) ) -proc newLit*[N,T](arg: array[N,T]): NimNode {.compileTime.} -proc newLit*[T](arg: seq[T]): NimNode {.compileTime.} -proc newLit*[T](s: set[T]): NimNode {.compileTime.} -proc newLit*[T: tuple](arg: T): NimNode {.compileTime.} +proc newLit*[N,T](arg: array[N,T]): NimNode +proc newLit*[T](arg: seq[T]): NimNode +proc newLit*[T](s: set[T]): NimNode +proc newLit*[T: tuple](arg: T): NimNode -proc newLit*(arg: object): NimNode {.compileTime.} = +proc newLit*(arg: object): NimNode = result = nnkObjConstr.newTree(arg.typeof.getTypeInst[1]) for a, b in arg.fieldPairs: result.add nnkExprColonExpr.newTree( newIdentNode(a), newLit(b) ) -proc newLit*(arg: ref object): NimNode {.compileTime.} = +proc newLit*(arg: ref object): NimNode = ## produces a new ref type literal node. result = nnkObjConstr.newTree(arg.typeof.getTypeInst[1]) for a, b in fieldPairs(arg[]): result.add nnkExprColonExpr.newTree(newIdentNode(a), newLit(b)) -proc newLit*[N,T](arg: array[N,T]): NimNode {.compileTime.} = +proc newLit*[N,T](arg: array[N,T]): NimNode = result = nnkBracket.newTree for x in arg: result.add newLit(x) -proc newLit*[T](arg: seq[T]): NimNode {.compileTime.} = +proc newLit*[T](arg: seq[T]): NimNode = let bracket = nnkBracket.newTree for x in arg: bracket.add newLit(x) @@ -812,7 +811,7 @@ proc newLit*[T](arg: seq[T]): NimNode {.compileTime.} = var typ = getTypeInst(typeof(arg))[1] result = newCall(typ,result) -proc newLit*[T](s: set[T]): NimNode {.compileTime.} = +proc newLit*[T](s: set[T]): NimNode = result = nnkCurly.newTree for x in s: result.add newLit(x) @@ -824,7 +823,7 @@ proc newLit*[T](s: set[T]): NimNode {.compileTime.} = proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".} ## See `typetraits.isNamedTuple` -proc newLit*[T: tuple](arg: T): NimNode {.compileTime.} = +proc newLit*[T: tuple](arg: T): NimNode = ## use -d:nimHasWorkaround14720 to restore behavior prior to PR, forcing ## a named tuple even when `arg` is unnamed. result = nnkTupleConstr.newTree @@ -835,7 +834,7 @@ proc newLit*[T: tuple](arg: T): NimNode {.compileTime.} = for b in arg.fields: result.add newLit(b) -proc nestList*(op: NimNode; pack: NimNode): NimNode {.compileTime.} = +proc nestList*(op: NimNode; pack: NimNode): NimNode = ## Nests the list `pack` into a tree of call expressions: ## `[a, b, c]` is transformed into `op(a, op(c, d))`. ## This is also known as fold expression. @@ -845,7 +844,7 @@ proc nestList*(op: NimNode; pack: NimNode): NimNode {.compileTime.} = for i in countdown(pack.len - 2, 0): result = newCall(op, pack[i], result) -proc nestList*(op: NimNode; pack: NimNode; init: NimNode): NimNode {.compileTime.} = +proc nestList*(op: NimNode; pack: NimNode; init: NimNode): NimNode = ## Nests the list `pack` into a tree of call expressions: ## `[a, b, c]` is transformed into `op(a, op(c, d))`. ## This is also known as fold expression. @@ -887,21 +886,21 @@ proc treeTraverse(n: NimNode; res: var string; level = 0; isLisp = false, indent if isLisp: res.add(")") -proc treeRepr*(n: NimNode): string {.compileTime, benign.} = +proc treeRepr*(n: NimNode): string {.benign.} = ## Convert the AST `n` to a human-readable tree-like string. ## ## See also `repr`, `lispRepr`, and `astGenRepr`. result = "" n.treeTraverse(result, isLisp = false, indented = true) -proc lispRepr*(n: NimNode; indented = false): string {.compileTime, benign.} = +proc lispRepr*(n: NimNode; indented = false): string {.benign.} = ## Convert the AST `n` to a human-readable lisp-like string. ## ## See also `repr`, `treeRepr`, and `astGenRepr`. result = "" n.treeTraverse(result, isLisp = true, indented = indented) -proc astGenRepr*(n: NimNode): string {.compileTime, benign.} = +proc astGenRepr*(n: NimNode): string {.benign.} = ## Convert the AST `n` to the code required to generate that AST. ## ## See also `repr`, `treeRepr`, and `lispRepr`. @@ -1020,56 +1019,56 @@ macro dumpAstGen*(s: untyped): untyped = echo s.astGenRepr ## ## Also see `dumpTree` and `dumpLisp`. -proc newEmptyNode*(): NimNode {.compileTime, noSideEffect.} = +proc newEmptyNode*(): NimNode {.noSideEffect.} = ## Create a new empty node. result = newNimNode(nnkEmpty) -proc newStmtList*(stmts: varargs[NimNode]): NimNode {.compileTime.}= +proc newStmtList*(stmts: varargs[NimNode]): NimNode= ## Create a new statement list. result = newNimNode(nnkStmtList).add(stmts) -proc newPar*(exprs: varargs[NimNode]): NimNode {.compileTime.}= +proc newPar*(exprs: varargs[NimNode]): NimNode= ## Create a new parentheses-enclosed expression. newNimNode(nnkPar).add(exprs) -proc newBlockStmt*(label, body: NimNode): NimNode {.compileTime.} = +proc newBlockStmt*(label, body: NimNode): NimNode = ## Create a new block statement with label. return newNimNode(nnkBlockStmt).add(label, body) -proc newBlockStmt*(body: NimNode): NimNode {.compileTime.} = +proc newBlockStmt*(body: NimNode): NimNode = ## Create a new block: stmt. return newNimNode(nnkBlockStmt).add(newEmptyNode(), body) -proc newVarStmt*(name, value: NimNode): NimNode {.compileTime.} = +proc newVarStmt*(name, value: NimNode): NimNode = ## Create a new var stmt. return newNimNode(nnkVarSection).add( newNimNode(nnkIdentDefs).add(name, newNimNode(nnkEmpty), value)) -proc newLetStmt*(name, value: NimNode): NimNode {.compileTime.} = +proc newLetStmt*(name, value: NimNode): NimNode = ## Create a new let stmt. return newNimNode(nnkLetSection).add( newNimNode(nnkIdentDefs).add(name, newNimNode(nnkEmpty), value)) -proc newConstStmt*(name, value: NimNode): NimNode {.compileTime.} = +proc newConstStmt*(name, value: NimNode): NimNode = ## Create a new const stmt. newNimNode(nnkConstSection).add( newNimNode(nnkConstDef).add(name, newNimNode(nnkEmpty), value)) -proc newAssignment*(lhs, rhs: NimNode): NimNode {.compileTime.} = +proc newAssignment*(lhs, rhs: NimNode): NimNode = return newNimNode(nnkAsgn).add(lhs, rhs) -proc newDotExpr*(a, b: NimNode): NimNode {.compileTime.} = +proc newDotExpr*(a, b: NimNode): NimNode = ## Create new dot expression. ## a.dot(b) -> `a.b` return newNimNode(nnkDotExpr).add(a, b) -proc newColonExpr*(a, b: NimNode): NimNode {.compileTime.} = +proc newColonExpr*(a, b: NimNode): NimNode = ## Create new colon expression. ## newColonExpr(a, b) -> `a: b` newNimNode(nnkExprColonExpr).add(a, b) proc newIdentDefs*(name, kind: NimNode; - default = newEmptyNode()): NimNode {.compileTime.} = + default = newEmptyNode()): NimNode = ## Creates a new `nnkIdentDefs` node of a specific kind and value. ## ## `nnkIdentDefs` need to have at least three children, but they can have @@ -1100,11 +1099,11 @@ proc newIdentDefs*(name, kind: NimNode; ## newStrLitNode("Hello")) newNimNode(nnkIdentDefs).add(name, kind, default) -proc newNilLit*(): NimNode {.compileTime.} = +proc newNilLit*(): NimNode = ## New nil literal shortcut. result = newNimNode(nnkNilLit) -proc last*(node: NimNode): NimNode {.compileTime.} = node[node.len-1] +proc last*(node: NimNode): NimNode = node[node.len-1] ## Return the last item in nodes children. Same as `node[^1]`. @@ -1115,7 +1114,7 @@ const CallNodes* = {nnkCall, nnkInfix, nnkPrefix, nnkPostfix, nnkCommand, nnkCallStrLit, nnkHiddenCallConv} -proc expectKind*(n: NimNode; k: set[NimNodeKind]) {.compileTime.} = +proc expectKind*(n: NimNode; k: set[NimNodeKind]) = ## Checks that `n` is of kind `k`. If this is not the case, ## compilation aborts with an error message. This is useful for writing ## macros that check the AST that is passed to them. @@ -1125,7 +1124,7 @@ proc newProc*(name = newEmptyNode(); params: openArray[NimNode] = [newEmptyNode()]; body: NimNode = newStmtList(); procType = nnkProcDef; - pragmas: NimNode = newEmptyNode()): NimNode {.compileTime.} = + pragmas: NimNode = newEmptyNode()): NimNode = ## Shortcut for creating a new proc. ## ## The `params` array must start with the return type of the proc, @@ -1142,8 +1141,7 @@ proc newProc*(name = newEmptyNode(); newEmptyNode(), body) -proc newIfStmt*(branches: varargs[tuple[cond, body: NimNode]]): - NimNode {.compileTime.} = +proc newIfStmt*(branches: varargs[tuple[cond, body: NimNode]]): NimNode = ## Constructor for `if` statements. ## ## .. code-block:: nim @@ -1160,7 +1158,7 @@ proc newIfStmt*(branches: varargs[tuple[cond, body: NimNode]]): result.add(newTree(nnkElifBranch, i.cond, i.body)) proc newEnum*(name: NimNode, fields: openArray[NimNode], - public, pure: bool): NimNode {.compileTime.} = + public, pure: bool): NimNode = ## Creates a new enum. `name` must be an ident. Fields are allowed to be ## either idents or EnumFieldDef @@ -1203,7 +1201,7 @@ proc newEnum*(name: NimNode, fields: openArray[NimNode], return typeSect -proc copyChildrenTo*(src, dest: NimNode) {.compileTime.}= +proc copyChildrenTo*(src, dest: NimNode) = ## Copy all children from `src` to `dest`. for i in 0 ..< src.len: dest.add src[i].copyNimTree @@ -1211,7 +1209,7 @@ proc copyChildrenTo*(src, dest: NimNode) {.compileTime.}= template expectRoutine(node: NimNode) = expectKind(node, RoutineNodes) -proc name*(someProc: NimNode): NimNode {.compileTime.} = +proc name*(someProc: NimNode): NimNode = someProc.expectRoutine result = someProc[0] if result.kind == nnkPostfix: @@ -1222,21 +1220,21 @@ proc name*(someProc: NimNode): NimNode {.compileTime.} = elif result.kind == nnkAccQuoted: result = result[0] -proc `name=`*(someProc: NimNode; val: NimNode) {.compileTime.} = +proc `name=`*(someProc: NimNode; val: NimNode) = someProc.expectRoutine if someProc[0].kind == nnkPostfix: someProc[0][1] = val else: someProc[0] = val -proc params*(someProc: NimNode): NimNode {.compileTime.} = +proc params*(someProc: NimNode): NimNode = someProc.expectRoutine result = someProc[3] -proc `params=`* (someProc: NimNode; params: NimNode) {.compileTime.}= +proc `params=`* (someProc: NimNode; params: NimNode) = someProc.expectRoutine expectKind(params, nnkFormalParams) someProc[3] = params -proc pragma*(someProc: NimNode): NimNode {.compileTime.} = +proc pragma*(someProc: NimNode): NimNode = ## Get the pragma of a proc type. ## These will be expanded. if someProc.kind == nnkProcTy: @@ -1244,7 +1242,7 @@ proc pragma*(someProc: NimNode): NimNode {.compileTime.} = else: someProc.expectRoutine result = someProc[4] -proc `pragma=`*(someProc: NimNode; val: NimNode) {.compileTime.}= +proc `pragma=`*(someProc: NimNode; val: NimNode) = ## Set the pragma of a proc type. expectKind(val, {nnkEmpty, nnkPragma}) if someProc.kind == nnkProcTy: @@ -1253,7 +1251,7 @@ proc `pragma=`*(someProc: NimNode; val: NimNode) {.compileTime.}= someProc.expectRoutine someProc[4] = val -proc addPragma*(someProc, pragma: NimNode) {.compileTime.} = +proc addPragma*(someProc, pragma: NimNode) = ## Adds pragma to routine definition. someProc.expectKind(RoutineNodes + {nnkProcTy}) var pragmaNode = someProc.pragma @@ -1265,7 +1263,7 @@ proc addPragma*(someProc, pragma: NimNode) {.compileTime.} = template badNodeKind(n, f) = error("Invalid node kind " & $n.kind & " for macros.`" & $f & "`", n) -proc body*(someProc: NimNode): NimNode {.compileTime.} = +proc body*(someProc: NimNode): NimNode = case someProc.kind: of RoutineNodes: return someProc[6] @@ -1276,7 +1274,7 @@ proc body*(someProc: NimNode): NimNode {.compileTime.} = else: badNodeKind someProc, "body" -proc `body=`*(someProc: NimNode, val: NimNode) {.compileTime.} = +proc `body=`*(someProc: NimNode, val: NimNode) = case someProc.kind of RoutineNodes: someProc[6] = val @@ -1287,7 +1285,7 @@ proc `body=`*(someProc: NimNode, val: NimNode) {.compileTime.} = else: badNodeKind someProc, "body=" -proc basename*(a: NimNode): NimNode {.raises: [].} = +proc basename*(a: NimNode): NimNode = ## Pull an identifier from prefix/postfix expressions. case a.kind of nnkIdent: result = a @@ -1297,7 +1295,7 @@ proc basename*(a: NimNode): NimNode {.raises: [].} = error("Do not know how to get basename of (" & treeRepr(a) & ")\n" & repr(a), a) -proc `$`*(node: NimNode): string {.compileTime.} = +proc `$`*(node: NimNode): string = ## Get the string of an identifier node. case node.kind of nnkPostfix: @@ -1340,7 +1338,7 @@ template findChild*(n: NimNode; cond: untyped): NimNode {.dirty.} = break res -proc insert*(a: NimNode; pos: int; b: NimNode) {.compileTime.} = +proc insert*(a: NimNode; pos: int; b: NimNode) = ## Insert node `b` into node `a` at `pos`. if len(a)-1 < pos: # add some empty nodes first @@ -1355,7 +1353,7 @@ proc insert*(a: NimNode; pos: int; b: NimNode) {.compileTime.} = a[i + 1] = a[i] a[pos] = b -proc `basename=`*(a: NimNode; val: string) {.compileTime.}= +proc `basename=`*(a: NimNode; val: string) = case a.kind of nnkIdent: a.strVal = val @@ -1366,32 +1364,29 @@ proc `basename=`*(a: NimNode; val: string) {.compileTime.}= error("Do not know how to get basename of (" & treeRepr(a) & ")\n" & repr(a), a) -proc postfix*(node: NimNode; op: string): NimNode {.compileTime.} = +proc postfix*(node: NimNode; op: string): NimNode = newNimNode(nnkPostfix).add(ident(op), node) -proc prefix*(node: NimNode; op: string): NimNode {.compileTime.} = +proc prefix*(node: NimNode; op: string): NimNode = newNimNode(nnkPrefix).add(ident(op), node) proc infix*(a: NimNode; op: string; - b: NimNode): NimNode {.compileTime.} = + b: NimNode): NimNode = newNimNode(nnkInfix).add(ident(op), a, b) -proc unpackPostfix*(node: NimNode): tuple[node: NimNode; op: string] {. - compileTime.} = +proc unpackPostfix*(node: NimNode): tuple[node: NimNode; op: string] = node.expectKind nnkPostfix result = (node[1], $node[0]) -proc unpackPrefix*(node: NimNode): tuple[node: NimNode; op: string] {. - compileTime.} = +proc unpackPrefix*(node: NimNode): tuple[node: NimNode; op: string] = node.expectKind nnkPrefix result = (node[1], $node[0]) -proc unpackInfix*(node: NimNode): tuple[left: NimNode; op: string; - right: NimNode] {.compileTime.} = +proc unpackInfix*(node: NimNode): tuple[left: NimNode; op: string; right: NimNode] = expectKind(node, nnkInfix) result = (node[1], $node[0], node[2]) -proc copy*(node: NimNode): NimNode {.compileTime.} = +proc copy*(node: NimNode): NimNode = ## An alias for `copyNimTree<#copyNimTree,NimNode>`_. return node.copyNimTree() @@ -1416,14 +1411,14 @@ proc eqIdent*(a: NimNode; b: NimNode): bool {.magic: "EqIdent", noSideEffect.} ## (`nnkPostfix`) or quoted with backticks (`nnkAccQuoted`), ## these nodes will be unwrapped. -proc expectIdent*(n: NimNode, name: string) {.compileTime, since: (1,1).} = +proc expectIdent*(n: NimNode, name: string) {.since: (1,1).} = ## Check that `eqIdent(n,name)` holds true. If this is not the ## case, compilation aborts with an error message. This is useful ## for writing macros that check the AST that is passed to them. if not eqIdent(n, name): error("Expected identifier to be `" & name & "` here", n) -proc hasArgOfName*(params: NimNode; name: string): bool {.compileTime.}= +proc hasArgOfName*(params: NimNode; name: string): bool= ## Search `nnkFormalParams` for an argument. expectKind(params, nnkFormalParams) for i in 1.. Date: Tue, 23 Mar 2021 15:40:30 +0100 Subject: [PATCH 1769/3768] Fixes #17450 (#17474) * Fixes #17450 * Add missing test output --- compiler/injectdestructors.nim | 1 + tests/arc/tmovebug.nim | 14 ++++++++++++++ tests/destructor/tnewruntime_strutils.nim | 13 ++++++++++++- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index b65391252..152efd8a1 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -573,6 +573,7 @@ template processScopeExpr(c: var Con; s: var Scope; ret: PNode, processCall: unt let tmp = c.getTemp(s.parent[], ret.typ, ret.info) tmp.sym.flags.incl sfSingleUsedTemp let cpy = if hasDestructor(c, ret.typ): + s.parent[].final.add c.genDestroy(tmp) moveOrCopy(tmp, ret, c, s, isDecl = true) else: newTree(nkFastAsgn, tmp, p(ret, c, s, normal)) diff --git a/tests/arc/tmovebug.nim b/tests/arc/tmovebug.nim index 888027186..3ff1c4a0c 100644 --- a/tests/arc/tmovebug.nim +++ b/tests/arc/tmovebug.nim @@ -107,6 +107,8 @@ sink destroy copy destroy +(f: 1) +destroy ''' """ @@ -770,3 +772,15 @@ proc pair(): tuple[a: C, b: C] = discard pair() + +# bug #17450 +proc noConsume(x: OO) {.nosinks.} = echo x + +proc main3 = + var i = 1 + noConsume: + block: + OO(f: i) + +main3() + diff --git a/tests/destructor/tnewruntime_strutils.nim b/tests/destructor/tnewruntime_strutils.nim index 8e5378f77..9c8d41973 100644 --- a/tests/destructor/tnewruntime_strutils.nim +++ b/tests/destructor/tnewruntime_strutils.nim @@ -5,7 +5,8 @@ discard """ @[(input: @["KXSC", "BGMC"]), (input: @["PXFX"]), (input: @["WXRQ", "ZSCZD"])] 14 First tasks completed. -Second tasks completed.''' +Second tasks completed. +test1''' """ import strutils, os, std / wordwrap @@ -241,3 +242,13 @@ when true: test_string_b.setLen new_len_b echo "Second tasks completed." + +# bug #17450 +proc main = + var i = 1 + echo: + block: + "test" & $i + +main() + From 97f51ed7c2fc67bfba9aef9c4b22a1fb450a5cae Mon Sep 17 00:00:00 2001 From: Clyybber Date: Tue, 23 Mar 2021 15:44:20 +0100 Subject: [PATCH 1770/3768] Revert "Fixes #17450 (#17474)" (#17476) This reverts commit 5f0c52048970cb7449937bd19191638c9e9f0c8f. --- compiler/injectdestructors.nim | 1 - tests/arc/tmovebug.nim | 14 -------------- tests/destructor/tnewruntime_strutils.nim | 13 +------------ 3 files changed, 1 insertion(+), 27 deletions(-) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 152efd8a1..b65391252 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -573,7 +573,6 @@ template processScopeExpr(c: var Con; s: var Scope; ret: PNode, processCall: unt let tmp = c.getTemp(s.parent[], ret.typ, ret.info) tmp.sym.flags.incl sfSingleUsedTemp let cpy = if hasDestructor(c, ret.typ): - s.parent[].final.add c.genDestroy(tmp) moveOrCopy(tmp, ret, c, s, isDecl = true) else: newTree(nkFastAsgn, tmp, p(ret, c, s, normal)) diff --git a/tests/arc/tmovebug.nim b/tests/arc/tmovebug.nim index 3ff1c4a0c..888027186 100644 --- a/tests/arc/tmovebug.nim +++ b/tests/arc/tmovebug.nim @@ -107,8 +107,6 @@ sink destroy copy destroy -(f: 1) -destroy ''' """ @@ -772,15 +770,3 @@ proc pair(): tuple[a: C, b: C] = discard pair() - -# bug #17450 -proc noConsume(x: OO) {.nosinks.} = echo x - -proc main3 = - var i = 1 - noConsume: - block: - OO(f: i) - -main3() - diff --git a/tests/destructor/tnewruntime_strutils.nim b/tests/destructor/tnewruntime_strutils.nim index 9c8d41973..8e5378f77 100644 --- a/tests/destructor/tnewruntime_strutils.nim +++ b/tests/destructor/tnewruntime_strutils.nim @@ -5,8 +5,7 @@ discard """ @[(input: @["KXSC", "BGMC"]), (input: @["PXFX"]), (input: @["WXRQ", "ZSCZD"])] 14 First tasks completed. -Second tasks completed. -test1''' +Second tasks completed.''' """ import strutils, os, std / wordwrap @@ -242,13 +241,3 @@ when true: test_string_b.setLen new_len_b echo "Second tasks completed." - -# bug #17450 -proc main = - var i = 1 - echo: - block: - "test" & $i - -main() - From 833084b671d1df21bd1a958810fc2a1bd13ae1d6 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Tue, 23 Mar 2021 16:30:49 +0100 Subject: [PATCH 1771/3768] Fixes #17450 (#17477) * Fixes #17450 * Add missing test output --- compiler/injectdestructors.nim | 1 + tests/arc/tmovebug.nim | 14 ++++++++++++++ tests/destructor/tnewruntime_strutils.nim | 13 ++++++++++++- 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index b65391252..152efd8a1 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -573,6 +573,7 @@ template processScopeExpr(c: var Con; s: var Scope; ret: PNode, processCall: unt let tmp = c.getTemp(s.parent[], ret.typ, ret.info) tmp.sym.flags.incl sfSingleUsedTemp let cpy = if hasDestructor(c, ret.typ): + s.parent[].final.add c.genDestroy(tmp) moveOrCopy(tmp, ret, c, s, isDecl = true) else: newTree(nkFastAsgn, tmp, p(ret, c, s, normal)) diff --git a/tests/arc/tmovebug.nim b/tests/arc/tmovebug.nim index 888027186..3ff1c4a0c 100644 --- a/tests/arc/tmovebug.nim +++ b/tests/arc/tmovebug.nim @@ -107,6 +107,8 @@ sink destroy copy destroy +(f: 1) +destroy ''' """ @@ -770,3 +772,15 @@ proc pair(): tuple[a: C, b: C] = discard pair() + +# bug #17450 +proc noConsume(x: OO) {.nosinks.} = echo x + +proc main3 = + var i = 1 + noConsume: + block: + OO(f: i) + +main3() + diff --git a/tests/destructor/tnewruntime_strutils.nim b/tests/destructor/tnewruntime_strutils.nim index 8e5378f77..9c8d41973 100644 --- a/tests/destructor/tnewruntime_strutils.nim +++ b/tests/destructor/tnewruntime_strutils.nim @@ -5,7 +5,8 @@ discard """ @[(input: @["KXSC", "BGMC"]), (input: @["PXFX"]), (input: @["WXRQ", "ZSCZD"])] 14 First tasks completed. -Second tasks completed.''' +Second tasks completed. +test1''' """ import strutils, os, std / wordwrap @@ -241,3 +242,13 @@ when true: test_string_b.setLen new_len_b echo "Second tasks completed." + +# bug #17450 +proc main = + var i = 1 + echo: + block: + "test" & $i + +main() + From 368422c219dd11018a8a3265921ddc283666e9e2 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Tue, 23 Mar 2021 20:12:27 +0300 Subject: [PATCH 1772/3768] update nimgrep documentation (#17415) * update nimgrep documentation * Update doc/nimgrep_cmdline.txt Co-authored-by: Andreas Rumpf --- doc/nimgrep.rst | 66 ++++++++++++++--------- doc/nimgrep_cmdline.txt | 114 ++++++++++++++++++++++++++++++++++++++++ tools/nimgrep.nim | 101 +---------------------------------- 3 files changed, 157 insertions(+), 124 deletions(-) create mode 100644 doc/nimgrep_cmdline.txt diff --git a/doc/nimgrep.rst b/doc/nimgrep.rst index 5b0fe0dbb..52fc4d62a 100644 --- a/doc/nimgrep.rst +++ b/doc/nimgrep.rst @@ -4,17 +4,20 @@ nimgrep User's manual ========================= +.. default-role:: literal + :Author: Andreas Rumpf -:Version: 0.9 +:Version: 1.6.0 +.. contents:: -Nimgrep is a command line tool for search&replace tasks. It can search for +Nimgrep is a command line tool for search and replace tasks. It can search for regex or peg patterns and can search whole directories at once. User confirmation for every single replace operation can be requested. Nimgrep has particularly good support for Nim's -eccentric *style insensitivity*. Apart from that it is a generic text -manipulation tool. +eccentric *style insensitivity* (see option `-y` below). +Apart from that it is a generic text manipulation tool. Installation @@ -30,23 +33,38 @@ And copy the executable somewhere in your `$PATH`. Command line switches ===================== -Usage: - nimgrep [options] [pattern] [replacement] (file/directory)* -Options: - --find, -f find the pattern (default) - --replace, -r replace the pattern - --peg pattern is a peg - --re pattern is a regular expression (default); extended - syntax for the regular expression is always turned on - --recursive process directories recursively - --confirm confirm each occurrence/replacement; there is a chance - to abort any time without touching the file - --stdin read pattern from stdin (to avoid the shell's confusing - quoting rules) - --word, -w the match should have word boundaries (buggy for pegs!) - --ignoreCase, -i be case insensitive - --ignoreStyle, -y be style insensitive - --ext:EX1|EX2|... only search the files with the given extension(s) - --verbose be verbose: list every processed file - --help, -h shows this help - --version, -v shows the version +.. include:: nimgrep_cmdline.txt + +Examples +======== + +All examples below use default PCRE Regex patterns: + ++ To search recursively in Nim files using style-insensitive identifiers:: + + --recursive --ext:'nim|nims' --ignoreStyle + # short: -r --ext:'nim|nims' -y + + .. Note:: we used `'` quotes to avoid special treatment of `|` symbol + for shells like Bash + ++ To exclude version control directories (Git, Mercurial=hg, Subversion=svn) + from the search:: + + --excludeDir:'^\.git$' --excludeDir:'^\.hg$' --excludeDir:'^\.svn$' + # short: --ed:'^\.git$' --ed:'^\.hg$' --ed:'^\.svn$' + ++ To search only in paths containing the `tests` sub-directory recursively:: + + --recursive --includeDir:'(^|/)tests($|/)' + # short: -r --id:'(^|/)tests($|/)' + + .. Attention:: note the subtle difference between `--excludeDir` and + `--includeDir`: the former is applied to relative directory entries + and the latter is applied to the whole paths + ++ Nimgrep can search multi-line, e.g. to find files containing `import` + and then `strutils` use:: + + 'import(.|\n)*?strutils' + diff --git a/doc/nimgrep_cmdline.txt b/doc/nimgrep_cmdline.txt new file mode 100644 index 000000000..bbcbcf530 --- /dev/null +++ b/doc/nimgrep_cmdline.txt @@ -0,0 +1,114 @@ + +Usage: + +* To search:: + nimgrep [options] PATTERN [(FILE/DIRECTORY)*/-] +* To replace:: + nimgrep [options] PATTERN --replace REPLACEMENT (FILE/DIRECTORY)*/- +* To list file names:: + nimgrep [options] --filenames [PATTERN] [(FILE/DIRECTORY)*] + +Positional arguments, from left to right: +1) PATTERN is either Regex (default) or Peg if `--peg` is specified. + PATTERN and REPLACEMENT should be skipped when `--stdin` is specified. +2) REPLACEMENT supports `$1`, `$#` notations for captured groups in PATTERN. + + .. DANGER:: `--replace` mode **DOES NOT** ask confirmation + unless `--confirm` is specified! + +3) Final arguments are a list of paths (FILE/DIRECTORY) or a standalone + minus `-` or not specified (empty): + + * empty, current directory `.` is assumed (not with `--replace`) + + .. Note:: so when no FILE/DIRECTORY/`-` is specified nimgrep + does **not** read the pipe, but searches files in the current + dir instead! + * `-`, read buffer once from stdin: pipe or terminal input; + in `--replace` mode the result is directed to stdout; + it's not compatible with `--stdin`, `--filenames`, or `--confirm` + + + For any given DIRECTORY nimgrep searches only its immediate files without + traversing sub-directories unless `--recursive` is specified. + +In replacement mode we require all 3 positional arguments to avoid damaging. + +Options: +* Mode of operation: + --find, -f find the PATTERN (default) + --replace, -! replace the PATTERN to REPLACEMENT, rewriting the files + --confirm confirm each occurrence/replacement; there is a chance + to abort any time without touching the file + --filenames just list filenames. Provide a PATTERN to find it in + the filenames (not in the contents of a file) or run + with empty pattern to just list all files:: + nimgrep --filenames # In current dir + nimgrep --filenames "" DIRECTORY + # Note empty pattern "", lists all files in DIRECTORY + +* Interprete patterns: + --peg PATTERN and PAT are Peg + --re PATTERN and PAT are regular expressions (default) + --rex, -x use the "extended" syntax for the regular expression + so that whitespace is not significant + --word, -w matches should have word boundaries (buggy for pegs!) + --ignoreCase, -i be case insensitive in PATTERN and PAT + --ignoreStyle, -y be style insensitive in PATTERN and PAT + .. Note:: PATERN and patterns PAT (see below in other options) are all either + Regex or Peg simultaneously and options `--rex`, `--word`, `--ignoreCase`, + and `--ignoreStyle` are applied to all of them. + +* File system walk: + --recursive, -r process directories recursively + --follow follow all symlinks when processing recursively + --ext:EX1|EX2|... only search the files with the given extension(s), + empty one ("--ext") means files with missing extension + --noExt:EX1|... exclude files having given extension(s), use empty one to + skip files with no extension (like some binary files are) + --includeFile:PAT search only files whose names contain pattern PAT + --excludeFile:PAT skip files whose names contain pattern PAT + --includeDir:PAT search only files with their whole directory path + containing PAT + --excludeDir:PAT skip directories whose name (not path) + contain pattern PAT + --if,--ef,--id,--ed abbreviations of the 4 options above + --sortTime, -s[:asc|desc] + order files by the last modification time (default: off): + ascending (recent files go last) or descending + +* Filter file content: + --match:PAT select files containing a (not displayed) match of PAT + --noMatch:PAT select files not containing any match of PAT + --bin:on|off|only process binary files? (detected by \0 in first 1K bytes) + (default: on - binary and text files treated the same way) + --text, -t process only text files, the same as `--bin:off` + +* Represent results: + --nocolor output will be given without any colors + --color[:on] force color even if output is redirected (default: auto) + --colorTheme:THEME select color THEME from `simple` (default), + `bnw` (black and white), `ack`, or `gnu` (GNU grep) + --count only print counts of matches for files that matched + --context:N, -c:N print N lines of leading context before every match and + N lines of trailing context after it (default N: 0) + --afterContext:N, -a:N + print N lines of trailing context after every match + --beforeContext:N, -b:N + print N lines of leading context before every match + --group, -g group matches by file + --newLine, -l display every matching line starting from a new line + --cols[:N] limit max displayed columns/width of output lines from + files by N characters, cropping overflows (default: off) + --cols:auto, -% calculate columns from terminal width for every line + --onlyAscii, -@ display only printable ASCII Latin characters 0x20-0x7E + substitutions: 0 -> ^@, 1 -> ^A, ... 0x1F -> ^_, + 0x7F -> '7F, ..., 0xFF -> 'FF + +* Miscellaneous: + --threads:N, -j:N speed up search by N additional workers (default: 0, off) + --stdin read PATTERN from stdin (to avoid the shell's confusing + quoting rules) and, if `--replace` given, REPLACEMENT + --verbose be verbose: list every processed file + --help, -h shows this help + --version, -v shows the version diff --git a/tools/nimgrep.nim b/tools/nimgrep.nim index cb46f30b8..a368c40ef 100644 --- a/tools/nimgrep.nim +++ b/tools/nimgrep.nim @@ -16,106 +16,7 @@ const Version & """ (c) 2012-2020 Andreas Rumpf - -Usage: -* To search: - nimgrep [options] PATTERN [(FILE/DIRECTORY)*/-] -* To replace: - nimgrep [options] PATTERN --replace REPLACEMENT (FILE/DIRECTORY)*/- -* To list file names: - nimgrep [options] --filenames [PATTERN] [(FILE/DIRECTORY)*] - -Positional arguments, from left to right: -* PATERN is either Regex (default) or Peg if --peg is specified. - PATTERN and REPLACEMENT should be skipped when --stdin is specified. -* REPLACEMENT supports $1, $# notations for captured groups in PATTERN. - Note: --replace mode DOES NOT ask confirmation unless --confirm is specified! -* Final arguments are a list of paths (FILE/DIRECTORY) or a standalone - minus '-' (pipe) or not specified (empty). Note for the empty case: when - no FILE/DIRECTORY/- is specified nimgrep DOES NOT read the pipe, but - searches files in the current dir instead! - - read buffer once from stdin: pipe or terminal input; - in --replace mode the result is directed to stdout; - it's not compatible with --stdin, --filenames, --confirm - (empty) current directory '.' is assumed (not with --replace) - For any given DIRECTORY nimgrep searches only its immediate files without - traversing sub-directories unless --recursive is specified. - In replacement mode all 3 positional arguments are required to avoid damaging. - -Options: -* Mode of operation: - --find, -f find the PATTERN (default) - --replace, -! replace the PATTERN to REPLACEMENT, rewriting the files - --confirm confirm each occurrence/replacement; there is a chance - to abort any time without touching the file - --filenames just list filenames. Provide a PATTERN to find it in - the filenames (not in the contents of a file) or run - with empty pattern to just list all files: - nimgrep --filenames # In current directory - nimgrep --filenames "" DIRECTORY # Note empty pattern "" - -* Interprete patterns: - --peg PATTERN and PAT are Peg - --re PATTERN and PAT are regular expressions (default) - --rex, -x use the "extended" syntax for the regular expression - so that whitespace is not significant - --word, -w matches should have word boundaries (buggy for pegs!) - --ignoreCase, -i be case insensitive in PATTERN and PAT - --ignoreStyle, -y be style insensitive in PATTERN and PAT - NOTE: PATERN and patterns PAT (see below in other options) are all either - Regex or Peg simultaneously and options --rex, --word, --ignoreCase, - --ignoreStyle are applied to all of them. - -* File system walk: - --recursive, -r process directories recursively - --follow follow all symlinks when processing recursively - --ext:EX1|EX2|... only search the files with the given extension(s), - empty one ("--ext") means files with missing extension - --noExt:EX1|... exclude files having given extension(s), use empty one to - skip files with no extension (like some binary files are) - --includeFile:PAT search only files whose names contain pattern PAT - --excludeFile:PAT skip files whose names contain pattern PAT - --includeDir:PAT search only files with whole directory path containing PAT - --excludeDir:PAT skip directories whose name (not path) contain pattern PAT - --if,--ef,--id,--ed abbreviations of 4 options above - --sortTime order files by the last modification time (default: off): - -s[:asc|desc] ascending (recent files go last) or descending - -* Filter file content: - --match:PAT select files containing a (not displayed) match of PAT - --noMatch:PAT select files not containing any match of PAT - --bin:on|off|only process binary files? (detected by \0 in first 1K bytes) - (default: on - binary and text files treated the same way) - --text, -t process only text files, the same as --bin:off - -* Represent results: - --nocolor output will be given without any colors - --color[:on] force color even if output is redirected (default: auto) - --colorTheme:THEME select color THEME from 'simple' (default), - 'bnw' (black and white) ,'ack', or 'gnu' (GNU grep) - --count only print counts of matches for files that matched - --context:N, -c:N print N lines of leading context before every match and - N lines of trailing context after it (default N: 0) - --afterContext:N, - -a:N print N lines of trailing context after every match - --beforeContext:N, - -b:N print N lines of leading context before every match - --group, -g group matches by file - --newLine, -l display every matching line starting from a new line - --cols[:N] limit max displayed columns/width of output lines from - files by N characters, cropping overflows (default: off) - --cols:auto, -% calculate columns from terminal width for every line - --onlyAscii, -@ display only printable ASCII Latin characters 0x20-0x7E - substitutions: 0 -> ^@, 1 -> ^A, ... 0x1F -> ^_, - 0x7F -> '7F, ..., 0xFF -> 'FF -* Miscellaneous: - --threads:N, -j:N speed up search by N additional workers (default: 0, off) - --stdin read PATTERN from stdin (to avoid the shell's confusing - quoting rules) and, if --replace given, REPLACEMENT - --verbose be verbose: list every processed file - --help, -h shows this help - --version, -v shows the version -""" +""" & slurp "../doc/nimgrep_cmdline.txt" # Limitations / ideas / TODO: # * No unicode support with --cols From 28221e8a33e111d8653c4ff21fad47a7ec6ef54d Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 24 Mar 2021 03:18:36 +0800 Subject: [PATCH 1773/3768] followup #16182 (#17472) * fix nim js cmp fails at CT * followup #16182 --- lib/pure/algorithm.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim index 0410d65ee..f4b3f46be 100644 --- a/lib/pure/algorithm.nim +++ b/lib/pure/algorithm.nim @@ -336,12 +336,12 @@ template `<-`(a, b) = else: copyMem(addr(a), addr(b), sizeof(T)) -proc merge[T](a, b: var openArray[T], lo, m, hi: int, +proc mergeAlt[T](a, b: var openArray[T], lo, m, hi: int, cmp: proc (x, y: T): int {.closure.}, order: SortOrder) = # Optimization: If max(left) <= min(right) there is nothing to do! # 1 2 3 4 ## 5 6 7 8 # -> O(n) for sorted arrays. - # On random data this saves up to 40% of merge calls. + # On random data this saves up to 40% of mergeAlt calls. if cmp(a[m], a[m+1]) * order <= 0: return var j = lo # copy a[j..m] into b: @@ -424,7 +424,7 @@ func sort*[T](a: var openArray[T], while s < n: var m = n-1-s while m >= 0: - merge(a, b, max(m-s+1, 0), m, m+s, cmp, order) + mergeAlt(a, b, max(m-s+1, 0), m, m+s, cmp, order) dec(m, s*2) s = s*2 From dbde97f649b0a4d947e15eddfacbde761cd49885 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 23 Mar 2021 23:46:19 +0100 Subject: [PATCH 1774/3768] fixes #16076 (#17486) --- compiler/semfold.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/semfold.nim b/compiler/semfold.nim index a3b9a8dde..0d4f6a0bd 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -685,7 +685,8 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode of nkDerefExpr, nkHiddenDeref: let a = getConstExpr(m, n[0], idgen, g) if a != nil and a.kind == nkNilLit: - localError(g.config, n.info, "nil dereference is not allowed") + result = nil + #localError(g.config, n.info, "nil dereference is not allowed") of nkCast: var a = getConstExpr(m, n[1], idgen, g) if a == nil: return From 8ccde68f132be4dba330eb6ec50f4679e564efac Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 24 Mar 2021 01:01:31 +0100 Subject: [PATCH 1775/3768] closes #17085; boehm has problems with large allocations, that is expected and we don't care (#17487) --- testament/categories.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testament/categories.nim b/testament/categories.nim index acb3ee0a8..86aa1f8af 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -173,7 +173,7 @@ proc gcTests(r: var TResults, cat: Category, options: string) = test "stackrefleak" test "cyclecollector" - test "trace_globals" + testWithoutBoehm "trace_globals" proc longGCTests(r: var TResults, cat: Category, options: string) = when defined(windows): From c015ecdc37221b374647b3b1503d80519e253d94 Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 24 Mar 2021 15:16:41 +0800 Subject: [PATCH 1776/3768] fix #17490 (#17491) --- lib/pure/json.nim | 14 ++++++++++---- tests/stdlib/tjson.nim | 11 +++++++++++ 2 files changed, 21 insertions(+), 4 deletions(-) diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 048ed2797..833403d68 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -927,7 +927,7 @@ when defined(js): proc parseNativeJson(x: cstring): JSObject {.importjs: "JSON.parse(#)".} - proc getVarType(x: JSObject): JsonNodeKind = + proc getVarType(x: JSObject, isRawNumber: var bool): JsonNodeKind = result = JNull case $getProtoName(x) # TODO: Implicit returns fail here. of "[object Array]": return JArray @@ -937,6 +937,7 @@ when defined(js): if isSafeInteger(x): return JInt else: + isRawNumber = true return JString else: return JFloat @@ -946,13 +947,13 @@ when defined(js): else: assert false proc len(x: JSObject): int = - assert x.getVarType == JArray asm """ `result` = `x`.length; """ proc convertObject(x: JSObject): JsonNode = - case getVarType(x) + var isRawNumber = false + case getVarType(x, isRawNumber) of JArray: result = newJArray() for i in 0 ..< x.len: @@ -973,7 +974,12 @@ when defined(js): result = newJFloat(x.to(float)) of JString: # Dunno what to do with isUnquoted here - result = newJString($x.to(cstring)) + if isRawNumber: + var value: cstring + {.emit: "`value` = `x`.toString();".} + result = newJRawNumber($value) + else: + result = newJString($x.to(cstring)) of JBool: result = newJBool(x.to(bool)) of JNull: diff --git a/tests/stdlib/tjson.nim b/tests/stdlib/tjson.nim index ceb9efb0e..e538baf4f 100644 --- a/tests/stdlib/tjson.nim +++ b/tests/stdlib/tjson.nim @@ -300,3 +300,14 @@ block: # bug #17383 when not defined(js): testRoundtrip(int64.high): "9223372036854775807" testRoundtrip(uint64.high): "18446744073709551615" + + +block: + let a = "18446744073709551615" + let b = a.parseJson + doAssert b.kind == JString + let c = $b + when defined(js): + doAssert c == "18446744073709552000" + else: + doAssert c == "18446744073709551615" From 13a203001434db8f6cc9e4deab10dbfc7f57bd94 Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 24 Mar 2021 15:49:05 +0800 Subject: [PATCH 1777/3768] follow up #17486 (#17492) * fix nim js cmp fails at CT * follow up #17486 * test more branches * better --- tests/iter/t16076.nim | 45 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 tests/iter/t16076.nim diff --git a/tests/iter/t16076.nim b/tests/iter/t16076.nim new file mode 100644 index 000000000..2eb409068 --- /dev/null +++ b/tests/iter/t16076.nim @@ -0,0 +1,45 @@ +discard """ + targets: "c js" +""" + +proc main() = + block: # bug #17485 + type + O = ref object + i: int + + iterator t(o: O): int = + if o != nil: + yield o.i + yield 0 + + proc m = + var data = "" + for i in t(nil): + data.addInt i + + doAssert data == "0" + + m() + + + block: # bug #16076 + type + R = ref object + z: int + + var data = "" + + iterator foo(x: int; y: R = nil): int {.inline.} = + if y == nil: + yield x + else: + yield y.z + + for b in foo(10): + data.addInt b + + doAssert data == "10" + +static: main() +main() From 465a41c3083668c37f4bbee1f00fd709d7f35298 Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 24 Mar 2021 16:44:24 +0800 Subject: [PATCH 1778/3768] [isolation]fix empty ref object bug (#17471) * fix nim js cmp fails at CT * [Minor]fix empty ref object for isolation * Update compiler/isolation_check.nim * Update compiler/isolation_check.nim Co-authored-by: Clyybber Co-authored-by: Clyybber --- compiler/isolation_check.nim | 8 ++++++-- tests/stdlib/tisolation.nim | 8 ++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/compiler/isolation_check.nim b/compiler/isolation_check.nim index 01f0a002a..9c8ca58b4 100644 --- a/compiler/isolation_check.nim +++ b/compiler/isolation_check.nim @@ -100,7 +100,12 @@ proc checkIsolate*(n: PNode): bool = for it in n: result = checkIsolate(it.lastSon) if not result: break - of nkCaseStmt, nkObjConstr: + of nkCaseStmt: + for i in 1.. Date: Wed, 24 Mar 2021 09:47:11 +0100 Subject: [PATCH 1779/3768] make unary minus part of number literals, refs #17020 (#17488) * make unary minus part of number literals, refs #17020 * fixes regression --- changelog.md | 4 +- compiler/lexer.nim | 87 +++++++++++++++++++++++------------- compiler/semtypes.nim | 2 +- doc/manual.rst | 43 +++++++++++++++--- tests/lexer/tunary_minus.nim | 76 +++++++++++++++++++++++++++++++ 5 files changed, 173 insertions(+), 39 deletions(-) create mode 100644 tests/lexer/tunary_minus.nim diff --git a/changelog.md b/changelog.md index 78817cf85..c0cd97024 100644 --- a/changelog.md +++ b/changelog.md @@ -212,7 +212,7 @@ - `std/options` changed `$some(3)` to `"some(3)"` instead of `"Some(3)"` and `$none(int)` to `"none(int)"` instead of `"None[int]"`. - + - Added `algorithm.merge`. @@ -263,6 +263,8 @@ - `typedesc[Foo]` now renders as such instead of `type Foo` in compiler messages. +- The unary minus in `-1` is now part of the integer literal, it is now parsed as a single token. + This implies that edge cases like `-128'i8` finally work correctly. ## Compiler changes diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 729ba3435..bcd3f0076 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -26,6 +26,7 @@ const SymStartChars*: set[char] = {'a'..'z', 'A'..'Z', '\x80'..'\xFF'} OpChars*: set[char] = {'+', '-', '*', '/', '\\', '<', '>', '!', '?', '^', '.', '|', '=', '%', '&', '$', '@', '~', ':'} + UnaryMinusWhitelist = {' ', '\t', '\n', '\r', ',', ';', '(', '[', '{'} # don't forget to update the 'highlite' module if these charsets should change @@ -51,22 +52,22 @@ type tkVar = "var", tkWhen = "when", tkWhile = "while", tkXor = "xor", tkYield = "yield", # end of keywords - tkIntLit = "tkIntLit", tkInt8Lit = "tkInt8Lit", tkInt16Lit = "tkInt16Lit", + tkIntLit = "tkIntLit", tkInt8Lit = "tkInt8Lit", tkInt16Lit = "tkInt16Lit", tkInt32Lit = "tkInt32Lit", tkInt64Lit = "tkInt64Lit", - tkUIntLit = "tkUIntLit", tkUInt8Lit = "tkUInt8Lit", tkUInt16Lit = "tkUInt16Lit", + tkUIntLit = "tkUIntLit", tkUInt8Lit = "tkUInt8Lit", tkUInt16Lit = "tkUInt16Lit", tkUInt32Lit = "tkUInt32Lit", tkUInt64Lit = "tkUInt64Lit", tkFloatLit = "tkFloatLit", tkFloat32Lit = "tkFloat32Lit", tkFloat64Lit = "tkFloat64Lit", tkFloat128Lit = "tkFloat128Lit", tkStrLit = "tkStrLit", tkRStrLit = "tkRStrLit", tkTripleStrLit = "tkTripleStrLit", - tkGStrLit = "tkGStrLit", tkGTripleStrLit = "tkGTripleStrLit", tkCharLit = "tkCharLit", - + tkGStrLit = "tkGStrLit", tkGTripleStrLit = "tkGTripleStrLit", tkCharLit = "tkCharLit", + tkParLe = "(", tkParRi = ")", tkBracketLe = "[", tkBracketRi = "]", tkCurlyLe = "{", tkCurlyRi = "}", tkBracketDotLe = "[.", tkBracketDotRi = ".]", tkCurlyDotLe = "{.", tkCurlyDotRi = ".}", tkParDotLe = "(.", tkParDotRi = ".)", tkComma = ",", tkSemiColon = ";", - tkColon = ":", tkColonColon = "::", tkEquals = "=", + tkColon = ":", tkColonColon = "::", tkEquals = "=", tkDot = ".", tkDotDot = "..", tkBracketLeColon = "[:", tkOpr, tkComment, tkAccent = "`", # these are fake tokens used by renderer.nim @@ -348,6 +349,14 @@ proc getNumber(L: var Lexer, result: var Token) = startpos = L.bufpos tokenBegin(result, startpos) + var isPositive = true + if L.buf[L.bufpos] == '-': + eatChar(L, result) + isPositive = true + + template setNumber(field, value) = + field = (if isPositive: value else: -value) + # First stage: find out base, make verifications, build token literal string # {'c', 'C'} is added for deprecation reasons to provide a clear error message if L.buf[L.bufpos] == '0' and L.buf[L.bufpos + 1] in baseCodeChars + {'c', 'C', 'O'}: @@ -459,7 +468,7 @@ proc getNumber(L: var Lexer, result: var Token) = # Third stage, extract actual number L.bufpos = startpos # restore position - var pos: int = startpos + var pos = startpos try: if (L.buf[pos] == '0') and (L.buf[pos + 1] in baseCodeChars): inc(pos, 2) @@ -500,20 +509,20 @@ proc getNumber(L: var Lexer, result: var Token) = internalError(L.config, getLineInfo(L), "getNumber") case result.tokType - of tkIntLit, tkInt64Lit: result.iNumber = xi - of tkInt8Lit: result.iNumber = ashr(xi shl 56, 56) - of tkInt16Lit: result.iNumber = ashr(xi shl 48, 48) - of tkInt32Lit: result.iNumber = ashr(xi shl 32, 32) - of tkUIntLit, tkUInt64Lit: result.iNumber = xi - of tkUInt8Lit: result.iNumber = xi and 0xff - of tkUInt16Lit: result.iNumber = xi and 0xffff - of tkUInt32Lit: result.iNumber = xi and 0xffffffff + of tkIntLit, tkInt64Lit: setNumber result.iNumber, xi + of tkInt8Lit: setNumber result.iNumber, ashr(xi shl 56, 56) + of tkInt16Lit: setNumber result.iNumber, ashr(xi shl 48, 48) + of tkInt32Lit: setNumber result.iNumber, ashr(xi shl 32, 32) + of tkUIntLit, tkUInt64Lit: setNumber result.iNumber, xi + of tkUInt8Lit: setNumber result.iNumber, xi and 0xff + of tkUInt16Lit: setNumber result.iNumber, xi and 0xffff + of tkUInt32Lit: setNumber result.iNumber, xi and 0xffffffff of tkFloat32Lit: - result.fNumber = (cast[PFloat32](addr(xi)))[] + setNumber result.fNumber, (cast[PFloat32](addr(xi)))[] # note: this code is endian neutral! # XXX: Test this on big endian machine! of tkFloat64Lit, tkFloatLit: - result.fNumber = (cast[PFloat64](addr(xi)))[] + setNumber result.fNumber, (cast[PFloat64](addr(xi)))[] else: internalError(L.config, getLineInfo(L), "getNumber") # Bounds checks. Non decimal literals are allowed to overflow the range of @@ -521,12 +530,13 @@ proc getNumber(L: var Lexer, result: var Token) = # below checks of signed sizes against uint*.high is deliberate: # (0x80'u8 = 128, 0x80'i8 = -128, etc == OK) if result.tokType notin floatTypes: - let outOfRange = case result.tokType: - of tkUInt8Lit, tkUInt16Lit, tkUInt32Lit: result.iNumber != xi - of tkInt8Lit: (xi > BiggestInt(uint8.high)) - of tkInt16Lit: (xi > BiggestInt(uint16.high)) - of tkInt32Lit: (xi > BiggestInt(uint32.high)) - else: false + let outOfRange = + case result.tokType + of tkUInt8Lit, tkUInt16Lit, tkUInt32Lit: result.iNumber != xi + of tkInt8Lit: (xi > BiggestInt(uint8.high)) + of tkInt16Lit: (xi > BiggestInt(uint16.high)) + of tkInt32Lit: (xi > BiggestInt(uint32.high)) + else: false if outOfRange: #echo "out of range num: ", result.iNumber, " vs ", xi @@ -557,23 +567,23 @@ proc getNumber(L: var Lexer, result: var Token) = raise newException(ValueError, "invalid integer: " & $result.literal) result.iNumber = iNumber - # Explicit bounds checks. Only T.high needs to be considered - # since result.iNumber can't be negative. + # Explicit bounds checks. let outOfRange = case result.tokType - of tkInt8Lit: result.iNumber > int8.high - of tkUInt8Lit: result.iNumber > BiggestInt(uint8.high) - of tkInt16Lit: result.iNumber > int16.high - of tkUInt16Lit: result.iNumber > BiggestInt(uint16.high) - of tkInt32Lit: result.iNumber > int32.high - of tkUInt32Lit: result.iNumber > BiggestInt(uint32.high) + of tkInt8Lit: result.iNumber > int8.high or result.iNumber < int8.low + of tkUInt8Lit: result.iNumber > BiggestInt(uint8.high) or result.iNumber < 0 + of tkInt16Lit: result.iNumber > int16.high or result.iNumber < int16.low + of tkUInt16Lit: result.iNumber > BiggestInt(uint16.high) or result.iNumber < 0 + of tkInt32Lit: result.iNumber > int32.high or result.iNumber < int32.low + of tkUInt32Lit: result.iNumber > BiggestInt(uint32.high) or result.iNumber < 0 else: false - if outOfRange: lexMessageLitNum(L, "number out of range: '$1'", startpos) + if outOfRange: + lexMessageLitNum(L, "number out of range: '$1'", startpos) # Promote int literal to int64? Not always necessary, but more consistent if result.tokType == tkIntLit: - if result.iNumber > high(int32): + if result.iNumber > high(int32) or result.iNumber < low(int32): result.tokType = tkInt64Lit except ValueError: @@ -1278,6 +1288,19 @@ proc rawGetTok*(L: var Lexer, tok: var Token) = let c = L.buf[L.bufpos] if c in SymChars+{'_'}: lexMessage(L, errGenerated, "invalid token: no whitespace between number and identifier") + of '-': + if L.buf[L.bufpos+1] in {'0'..'9'} and + (L.bufpos-1 == 0 or L.buf[L.bufpos-1] in UnaryMinusWhitelist): + # x)-23 # binary minus + # ,-23 # unary minus + # \n-78 # unary minus? Yes. + # =-3 # parsed as `=-` anyway + getNumber(L, tok) + let c = L.buf[L.bufpos] + if c in SymChars+{'_'}: + lexMessage(L, errGenerated, "invalid token: no whitespace between number and identifier") + else: + getOperator(L, tok) else: if c in OpChars: getOperator(L, tok) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index dcab9a884..0fce7b417 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -299,7 +299,7 @@ proc semArrayIndex(c: PContext, n: PNode): PType = result = makeRangeWithStaticExpr(c, e.typ.n) elif e.kind in {nkIntLit..nkUInt64Lit}: if e.intVal < 0: - localError(c.config, n[1].info, + localError(c.config, n.info, "Array length can't be negative, but was " & $e.intVal) result = makeRangeType(c, 0, e.intVal-1, n.info, e.typ) elif e.kind == nkSym and e.typ.kind == tyStatic: diff --git a/doc/manual.rst b/doc/manual.rst index 1bb47f28b..db1251630 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -499,10 +499,11 @@ Numerical constants are of a single type and have the form:: hexdigit = digit | 'A'..'F' | 'a'..'f' octdigit = '0'..'7' bindigit = '0'..'1' - HEX_LIT = '0' ('x' | 'X' ) hexdigit ( ['_'] hexdigit )* - DEC_LIT = digit ( ['_'] digit )* - OCT_LIT = '0' 'o' octdigit ( ['_'] octdigit )* - BIN_LIT = '0' ('b' | 'B' ) bindigit ( ['_'] bindigit )* + unary_minus = '-' # See the section about unary minus + HEX_LIT = unary_minus? '0' ('x' | 'X' ) hexdigit ( ['_'] hexdigit )* + DEC_LIT = unary_minus? digit ( ['_'] digit )* + OCT_LIT = unary_minus? '0' 'o' octdigit ( ['_'] octdigit )* + BIN_LIT = unary_minus? '0' ('b' | 'B' ) bindigit ( ['_'] bindigit )* INT_LIT = HEX_LIT | DEC_LIT @@ -521,7 +522,7 @@ Numerical constants are of a single type and have the form:: UINT64_LIT = INT_LIT ['\''] ('u' | 'U') '64' exponent = ('e' | 'E' ) ['+' | '-'] digit ( ['_'] digit )* - FLOAT_LIT = digit (['_'] digit)* (('.' digit (['_'] digit)* [exponent]) |exponent) + FLOAT_LIT = unary_minus? digit (['_'] digit)* (('.' digit (['_'] digit)* [exponent]) |exponent) FLOAT32_SUFFIX = ('f' | 'F') ['32'] FLOAT32_LIT = HEX_LIT '\'' FLOAT32_SUFFIX | (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] FLOAT32_SUFFIX @@ -535,6 +536,38 @@ for readability. Integer and floating-point literals may be given in decimal (no prefix), binary (prefix `0b`), octal (prefix `0o`), and hexadecimal (prefix `0x`) notation. +The fact that the unary minus `-` in a number literal like `-1` is considered +to be part of the literal is a late addition to the language. The rationale is that +an expression `-128'i8` should be valid and without this special case, this would +be impossible -- `128` is not a valid `int8` value, only `-128` is. + +For the `unary_minus` rule there are further restrictions that are not covered +in the formal grammar. For `-` to be part of the number literal its immediately +preceeding character has to be in the +set `{' ', '\t', '\n', '\r', ',', ';', '(', '[', '{'}`. This set was designed to +cover most cases in a natural manner. + +In the following examples, `-1` is a single token: + +.. code-block:: nim + + echo -1 + echo(-1) + echo [-1] + echo 3,-1 + + "abc";-1 + +In the following examples, `-1` is parsed as two separate tokens (as `- 1`): + +.. code-block:: nim + + echo x-1 + echo (int)-1 + echo [a]-1 + "abc"-1 + + There exists a literal for each numerical type that is defined. The suffix starting with an apostrophe ('\'') is called a `type suffix`:idx:. Literals without a type suffix are of an integer type diff --git a/tests/lexer/tunary_minus.nim b/tests/lexer/tunary_minus.nim new file mode 100644 index 000000000..89f1b79ef --- /dev/null +++ b/tests/lexer/tunary_minus.nim @@ -0,0 +1,76 @@ +discard """ + targets: "c cpp js" +""" + +# Test numeric literals and handling of minus symbol + +import std/[macros, strutils] + +macro lispReprStr*(a: untyped): untyped = newLit(a.lispRepr) + +macro assertAST*(expected: string, struct: untyped): untyped = + var ast = newLit(struct.treeRepr) + result = quote do: + if `ast` != `expected`: + doAssert false, "\nGot:\n" & `ast`.indent(2) & "\nExpected:\n" & `expected`.indent(2) + +const one = 1 +const minusOne = `-`(one) + +# border cases that *should* generate compiler errors: +assertAST dedent """ + StmtList + Asgn + Ident "x" + Command + IntLit 4 + IntLit -1""": + x = 4 -1 +assertAST dedent """ + StmtList + VarSection + IdentDefs + Ident "x" + Ident "uint" + IntLit -1""": + var x: uint = -1 +template bad() = + x = 4 -1 +doAssert not compiles(bad()) + +template main = + block: # check when a minus (-) is a negative sign for a literal + doAssert -1 == minusOne: + "unable to parse a spaced-prefixed negative int" + doAssert lispReprStr(-1) == """(IntLit -1)""" + doAssert -1.0'f64 == minusOne.float64 + doAssert lispReprStr(-1.000'f64) == """(Float64Lit -1.0)""" + doAssert lispReprStr( -1.000'f64) == """(Float64Lit -1.0)""" + doAssert [-1].contains(minusOne): + "unable to handle negatives after square bracket" + doAssert lispReprStr([-1]) == """(Bracket (IntLit -1))""" + doAssert (-1, 2)[0] == minusOne: + "unable to handle negatives after parenthesis" + doAssert lispReprStr((-1, 2)) == """(Par (IntLit -1) (IntLit 2))""" + proc x(): int = + var a = 1;-1 # the -1 should act as the return value + doAssert x() == minusOne: + "unable to handle negatives after semi-colon" + + block: # check when a minus (-) is an unary op + doAssert -one == minusOne: + "unable to a negative prior to identifier" + + block: # check when a minus (-) is a a subtraction op + doAssert 4-1 == 3: + "unable to handle subtraction sans surrounding spaces with a numeric literal" + doAssert 4-one == 3: + "unable to handle subtraction sans surrounding spaces with an identifier" + doAssert 4 - 1 == 3: + "unable to handle subtraction with surrounding spaces with a numeric literal" + doAssert 4 - one == 3: + "unable to handle subtraction with surrounding spaces with an identifier" + + +static: main() +main() From e94aec20da354d4d3bb6db6e4b840549aa0e7bf1 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 24 Mar 2021 02:36:15 -0700 Subject: [PATCH 1780/3768] fix #17325 linux 32bit CI; fix #17085 flaky test (#17469) --- azure-pipelines.yml | 10 +++++----- lib/pure/json.nim | 7 ++++--- tests/gc/trace_globals.nim | 13 ++++++++++++- tests/stdlib/tjsonutils.nim | 2 +- 4 files changed, 22 insertions(+), 10 deletions(-) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index f65f8ca11..9006cf6e5 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -17,11 +17,11 @@ jobs: Linux_amd64: vmImage: 'ubuntu-16.04' CPU: amd64 - # Linux_i386: - # # bug #17325: fails on 'ubuntu-16.04' because it now errors with: - # # g++-multilib : Depends: gcc-multilib (>= 4:5.3.1-1ubuntu1) but it is not going to be installed - # vmImage: 'ubuntu-18.04' - # CPU: i386 + Linux_i386: + # bug #17325: fails on 'ubuntu-16.04' because it now errors with: + # g++-multilib : Depends: gcc-multilib (>= 4:5.3.1-1ubuntu1) but it is not going to be installed + vmImage: 'ubuntu-18.04' + CPU: i386 OSX_amd64: vmImage: 'macOS-10.15' CPU: amd64 diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 833403d68..7ee5be9c5 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -1075,12 +1075,13 @@ when defined(nimFixedForwardGeneric): dst = jsonNode.copy proc initFromJson[T: SomeInteger](dst: var T; jsonNode: JsonNode, jsonPath: var string) = - when T is uint|uint64: + when T is uint|uint64 or (not defined(js) and int.sizeof == 4): + verifyJsonKind(jsonNode, {JInt, JString}, jsonPath) case jsonNode.kind of JString: - dst = T(parseBiggestUInt(jsonNode.str)) + let x = parseBiggestUInt(jsonNode.str) + dst = cast[T](x) else: - verifyJsonKind(jsonNode, {JInt}, jsonPath) dst = T(jsonNode.num) else: verifyJsonKind(jsonNode, {JInt}, jsonPath) diff --git a/tests/gc/trace_globals.nim b/tests/gc/trace_globals.nim index d91bc5f35..f62a15692 100644 --- a/tests/gc/trace_globals.nim +++ b/tests/gc/trace_globals.nim @@ -1,11 +1,20 @@ discard """ - output: '''10000000 + output: ''' +10000000 10000000 10000000''' """ # bug #17085 +#[ +refs https://github.com/nim-lang/Nim/issues/17085#issuecomment-786466595 +with --gc:boehm, this warning sometimes gets generated: +Warning: Repeated allocation of very large block (appr. size 14880768): +May lead to memory leak and poor performance. +nim CI now runs this test with `testWithoutBoehm` to avoid running it with --gc:boehm. +]# + proc init(): string = for a in 0..<10000000: result.add 'c' @@ -16,6 +25,8 @@ proc f() = var c {.global.} = init() echo a.len + # `echo` intentional according to + # https://github.com/nim-lang/Nim/pull/17469/files/0c9e94cb6b9ebca9da7cb19a063fba7aa409748e#r600016573 echo b.len echo c.len diff --git a/tests/stdlib/tjsonutils.nim b/tests/stdlib/tjsonutils.nim index 5cd975cab..63bf97704 100644 --- a/tests/stdlib/tjsonutils.nim +++ b/tests/stdlib/tjsonutils.nim @@ -71,7 +71,7 @@ template fn() = block: let a = (int32.high, uint32.high) testRoundtrip(a): "[2147483647,4294967295]" - when not defined(js): + when int.sizeof > 4: block: let a = (int64.high, uint64.high) testRoundtrip(a): "[9223372036854775807,18446744073709551615]" From 1590d145757416788e99023ab50c5afae6f8ea36 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 24 Mar 2021 02:58:29 -0700 Subject: [PATCH 1781/3768] fix #17260 render `\` properly in nim doc, rst2html (#17315) --- lib/packages/docutils/rst.nim | 21 ++++++++++++++++----- lib/system.nim | 4 ++-- nimdoc/rst2html/expected/rst_examples.html | 2 +- tests/stdlib/trstgen.nim | 22 ++++++++++++++++++++-- 4 files changed, 39 insertions(+), 10 deletions(-) diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index b5eef7610..11768c7da 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -852,6 +852,9 @@ proc isInlineMarkupEnd(p: RstParser, markup: string): bool = if not result: return # Rule 4: if p.idx > 0: + # see bug #17260; for now `\` must be written ``\``, likewise with sequences + # ending in an un-escaped `\`; `\\` is legal but not `\\\` for example; + # for this reason we can't use `["``", "`"]` here. if markup != "``" and prevTok(p).symbol == "\\": result = false @@ -1089,11 +1092,19 @@ proc parseUntil(p: var RstParser, father: PRstNode, postfix: string, if isInlineMarkupEnd(p, postfix): inc p.idx break - elif interpretBackslash: - parseBackslash(p, father) else: - father.add(newLeaf(p)) - inc p.idx + if postfix == "`": + if prevTok(p).symbol == "\\" and currentTok(p).symbol == "`": + father.sons[^1] = newLeaf(p) # instead, we should use lookahead + else: + father.add(newLeaf(p)) + inc p.idx + else: + if interpretBackslash: + parseBackslash(p, father) + else: + father.add(newLeaf(p)) + inc p.idx of tkAdornment, tkWord, tkOther: father.add(newLeaf(p)) inc p.idx @@ -1243,7 +1254,7 @@ proc parseInline(p: var RstParser, father: PRstNode) = father.add(n) elif isInlineMarkupStart(p, "`"): var n = newRstNode(rnInterpretedText) - parseUntil(p, n, "`", true) + parseUntil(p, n, "`", false) # bug #17260 n = parsePostfix(p, n) father.add(n) elif isInlineMarkupStart(p, "|"): diff --git a/lib/system.nim b/lib/system.nim index 574043125..4c31f20a2 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2827,7 +2827,7 @@ when declared(initDebugger): proc addEscapedChar*(s: var string, c: char) {.noSideEffect, inline.} = ## Adds a char to string `s` and applies the following escaping: ## - ## * replaces any `\` by `\\` + ## * replaces any ``\`` by `\\` ## * replaces any `'` by `\'` ## * replaces any `"` by `\"` ## * replaces any `\a` by `\\a` @@ -2838,7 +2838,7 @@ proc addEscapedChar*(s: var string, c: char) {.noSideEffect, inline.} = ## * replaces any `\f` by `\\f` ## * replaces any `\r` by `\\r` ## * replaces any `\e` by `\\e` - ## * replaces any other character not in the set `{'\21..'\126'} + ## * replaces any other character not in the set `{\21..\126}` ## by `\xHH` where `HH` is its hexadecimal value. ## ## The procedure has been designed so that its output is usable for many diff --git a/nimdoc/rst2html/expected/rst_examples.html b/nimdoc/rst2html/expected/rst_examples.html index 23d192009..8253289a4 100644 --- a/nimdoc/rst2html/expected/rst_examples.html +++ b/nimdoc/rst2html/expected/rst_examples.html @@ -300,7 +300,7 @@ stmt = IND{>} stmt ^+ IND{=} DED # list of statements

                                      However, this does not work. The problem is that the procedure should not only return, but return and continue after an iteration has finished. This return and continue is called a yield statement. Now the only thing left to do is to replace the proc keyword by iterator and here it is - our first iterator:

                                      - +
                                      A1 headerA2 | not fooled
                                      C1C2 bold
                                      D1 code |D2
                                      D1 code \|D2
                                      E1 | text
                                      F2 without pipe

                                      not in table

                                      diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim index cf82cdf91..d6055cc11 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -196,9 +196,15 @@ suite "RST/Markdown general": | | F2 without pipe not in table""" let output1 = input1.toHtml - doAssert output1 == """ + #[ + TODO: `\|` inside a table cell should render as `|` + `|` outside a table cell should render as `\|` + consistently with markdown, see https://stackoverflow.com/a/66557930/1426932 + ]# + doAssert output1 == """ +
                                      A1 headerA2 | not fooled
                                      - +
                                      A1 headerA2 | not fooled
                                      C1C2 bold
                                      D1 code |D2
                                      D1 code \|D2
                                      E1 | text
                                      F2 without pipe

                                      not in table

                                      @@ -549,6 +555,18 @@ let x = 1 let output2 = input2.toHtml doAssert "foo.bar""" + check """`foo\`\`bar`""".toHtml == """foo``bar""" + check """`foo\`bar`""".toHtml == """foo`bar""" + check """`\`bar`""".toHtml == """`bar""" + check """`a\b\x\\ar`""".toHtml == """a\b\x\\ar""" + + test "inline literal": + check """``foo.bar``""".toHtml == """foo.bar""" + check """``foo\bar``""".toHtml == """foo\bar""" + check """``f\`o\\o\b`ar``""".toHtml == """f\`o\\o\b`ar""" + test "RST comments": let input1 = """ Check that comment disappears: From 7366a3da37605b230823dd4b6db07abb70dbd40b Mon Sep 17 00:00:00 2001 From: Saem Ghani Date: Wed, 24 Mar 2021 03:55:58 -0700 Subject: [PATCH 1782/3768] potential fix for semgeneric formal params (#17494) marked locations where analysis of return formal param is done prior to args. This might fix some subtle bugs. --- compiler/isolation_check.nim | 3 +++ compiler/reorder.nim | 1 + compiler/semgnrc.nim | 6 ++++-- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/compiler/isolation_check.nim b/compiler/isolation_check.nim index 9c8ca58b4..43db5d59b 100644 --- a/compiler/isolation_check.nim +++ b/compiler/isolation_check.nim @@ -85,6 +85,9 @@ proc checkIsolate*(n: PNode): bool = of nkCharLit..nkNilLit: result = true of nkCallKinds: + # XXX: as long as we don't update the analysis while examining arguments + # we can do an early check of the return type, otherwise this is a + # bug and needs to be moved below if n[0].typ.flags * {tfGcSafe, tfNoSideEffect} == {}: return false for i in 1.. Date: Wed, 24 Mar 2021 14:46:19 +0100 Subject: [PATCH 1783/3768] custom integer literals (#17489) * user defined integer literals; refs #17020 * updated renderer.nim * use mlexerutils helper * imported all test cases from https://github.com/nim-lang/Nim/pull/17020 * final grammar updated --- changelog.md | 2 + compiler/docgen.nim | 2 +- compiler/lexer.nim | 342 +++++++++++------------ compiler/parser.nim | 18 +- compiler/renderer.nim | 18 +- compiler/semstmts.nim | 2 +- doc/grammar.txt | 3 +- doc/manual.rst | 54 +++- tests/lexer/mlexerutils.nim | 9 + tests/lexer/tcustom_numeric_literals.nim | 150 ++++++++++ tests/lexer/tstrlits.nim | 19 -- tests/lexer/tunary_minus.nim | 8 +- 12 files changed, 409 insertions(+), 218 deletions(-) create mode 100644 tests/lexer/mlexerutils.nim create mode 100644 tests/lexer/tcustom_numeric_literals.nim delete mode 100644 tests/lexer/tstrlits.nim diff --git a/changelog.md b/changelog.md index c0cd97024..922b8470a 100644 --- a/changelog.md +++ b/changelog.md @@ -266,6 +266,8 @@ - The unary minus in `-1` is now part of the integer literal, it is now parsed as a single token. This implies that edge cases like `-128'i8` finally work correctly. +- Custom numeric literals are now supported. + ## Compiler changes diff --git a/compiler/docgen.nim b/compiler/docgen.nim index dded231d7..b24a24a2b 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -416,7 +416,7 @@ proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var Rope; renderFlags: TRe of tkOpr: dispA(d.conf, result, "$1", "\\spanOperator{$1}", [escLit]) - of tkStrLit..tkTripleStrLit: + of tkStrLit..tkTripleStrLit, tkCustomLit: dispA(d.conf, result, "$1", "\\spanStringLit{$1}", [escLit]) of tkCharLit: diff --git a/compiler/lexer.nim b/compiler/lexer.nim index bcd3f0076..b34b010c2 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -60,6 +60,7 @@ type tkFloat64Lit = "tkFloat64Lit", tkFloat128Lit = "tkFloat128Lit", tkStrLit = "tkStrLit", tkRStrLit = "tkRStrLit", tkTripleStrLit = "tkTripleStrLit", tkGStrLit = "tkGStrLit", tkGTripleStrLit = "tkGTripleStrLit", tkCharLit = "tkCharLit", + tkCustomLit = "tkCustomLit", tkParLe = "(", tkParRi = ")", tkBracketLe = "[", tkBracketRi = "]", tkCurlyLe = "{", tkCurlyRi = "}", @@ -313,8 +314,7 @@ proc getNumber(L: var Lexer, result: var Token) = proc lexMessageLitNum(L: var Lexer, msg: string, startpos: int, msgKind = errGenerated) = # Used to get slightly human friendlier err messages. - const literalishChars = {'A'..'F', 'a'..'f', '0'..'9', 'X', 'x', 'o', 'O', - 'c', 'C', 'b', 'B', '_', '.', '\'', 'd', 'i', 'u'} + const literalishChars = {'A'..'Z', 'a'..'z', '0'..'9', '_', '.', '\''} var msgPos = L.bufpos var t: Token t.literal = "" @@ -326,15 +326,14 @@ proc getNumber(L: var Lexer, result: var Token) = t.literal.add(L.buf[L.bufpos]) inc(L.bufpos) matchChars(L, t, literalishChars) - if L.buf[L.bufpos] in {'\'', 'f', 'F', 'd', 'D', 'i', 'I', 'u', 'U'}: - inc(L.bufpos) + if L.buf[L.bufpos] in literalishChars: t.literal.add(L.buf[L.bufpos]) + inc(L.bufpos) matchChars(L, t, {'0'..'9'}) L.bufpos = msgPos lexMessage(L, msgKind, msg % t.literal) var - startpos, endpos: int xi: BiggestInt isBase10 = true numDigits = 0 @@ -346,7 +345,7 @@ proc getNumber(L: var Lexer, result: var Token) = result.tokType = tkIntLit # int literal until we know better result.literal = "" result.base = base10 - startpos = L.bufpos + let startpos = L.bufpos tokenBegin(result, startpos) var isPositive = true @@ -395,201 +394,187 @@ proc getNumber(L: var Lexer, result: var Token) = discard matchUnderscoreChars(L, result, {'0'..'9'}) if L.buf[L.bufpos] in {'e', 'E'}: result.tokType = tkFloatLit - eatChar(L, result, 'e') + eatChar(L, result) if L.buf[L.bufpos] in {'+', '-'}: eatChar(L, result) discard matchUnderscoreChars(L, result, {'0'..'9'}) - endpos = L.bufpos + let endpos = L.bufpos # Second stage, find out if there's a datatype suffix and handle it var postPos = endpos + if L.buf[postPos] in {'\'', 'f', 'F', 'd', 'D', 'i', 'I', 'u', 'U'}: + let errPos = postPos + var customLitPossible = false if L.buf[postPos] == '\'': inc(postPos) + customLitPossible = true - case L.buf[postPos] - of 'f', 'F': - inc(postPos) - if (L.buf[postPos] == '3') and (L.buf[postPos + 1] == '2'): - result.tokType = tkFloat32Lit - inc(postPos, 2) - elif (L.buf[postPos] == '6') and (L.buf[postPos + 1] == '4'): - result.tokType = tkFloat64Lit - inc(postPos, 2) - elif (L.buf[postPos] == '1') and - (L.buf[postPos + 1] == '2') and - (L.buf[postPos + 2] == '8'): - result.tokType = tkFloat128Lit - inc(postPos, 3) - else: # "f" alone defaults to float32 - result.tokType = tkFloat32Lit - of 'd', 'D': # ad hoc convenience shortcut for f64 - inc(postPos) - result.tokType = tkFloat64Lit - of 'i', 'I': - inc(postPos) - if (L.buf[postPos] == '6') and (L.buf[postPos + 1] == '4'): - result.tokType = tkInt64Lit - inc(postPos, 2) - elif (L.buf[postPos] == '3') and (L.buf[postPos + 1] == '2'): - result.tokType = tkInt32Lit - inc(postPos, 2) - elif (L.buf[postPos] == '1') and (L.buf[postPos + 1] == '6'): - result.tokType = tkInt16Lit - inc(postPos, 2) - elif (L.buf[postPos] == '8'): - result.tokType = tkInt8Lit - inc(postPos) + if L.buf[postPos] in SymChars: + var suffixAsLower = newStringOfCap(10) + var suffix = newStringOfCap(10) + while true: + let c = L.buf[postPos] + suffix.add c + suffixAsLower.add toLowerAscii(c) + inc postPos + if L.buf[postPos] notin SymChars+{'_'}: break + case suffix + of "f", "f32": result.tokType = tkFloat32Lit + of "d", "f64": result.tokType = tkFloat64Lit + of "f128": result.tokType = tkFloat128Lit + of "i8": result.tokType = tkInt8Lit + of "i16": result.tokType = tkInt16Lit + of "i32": result.tokType = tkInt32Lit + of "i64": result.tokType = tkInt64Lit + of "u": result.tokType = tkUIntLit + of "u8": result.tokType = tkUInt8Lit + of "u16": result.tokType = tkUInt16Lit + of "u32": result.tokType = tkUInt32Lit + of "u64": result.tokType = tkUInt64Lit else: - lexMessageLitNum(L, "invalid number: '$1'", startpos) - of 'u', 'U': - inc(postPos) - if (L.buf[postPos] == '6') and (L.buf[postPos + 1] == '4'): - result.tokType = tkUInt64Lit - inc(postPos, 2) - elif (L.buf[postPos] == '3') and (L.buf[postPos + 1] == '2'): - result.tokType = tkUInt32Lit - inc(postPos, 2) - elif (L.buf[postPos] == '1') and (L.buf[postPos + 1] == '6'): - result.tokType = tkUInt16Lit - inc(postPos, 2) - elif (L.buf[postPos] == '8'): - result.tokType = tkUInt8Lit - inc(postPos) - else: - result.tokType = tkUIntLit + if customLitPossible: + # remember the position of the ``'`` so that the parser doesn't + # have to reparse the custom literal: + result.iNumber = len(result.literal) + result.literal.add '\'' + result.literal.add suffix + result.tokType = tkCustomLit + else: + lexMessageLitNum(L, "invalid number suffix: '$1'", errPos) else: - lexMessageLitNum(L, "invalid number: '$1'", startpos) + lexMessageLitNum(L, "invalid number suffix: '$1'", errPos) # Is there still a literalish char awaiting? Then it's an error! if L.buf[postPos] in literalishChars or (L.buf[postPos] == '.' and L.buf[postPos + 1] in {'0'..'9'}): lexMessageLitNum(L, "invalid number: '$1'", startpos) - # Third stage, extract actual number - L.bufpos = startpos # restore position - var pos = startpos - try: - if (L.buf[pos] == '0') and (L.buf[pos + 1] in baseCodeChars): - inc(pos, 2) - xi = 0 # it is a base prefix + if result.tokType != tkCustomLit: + # Third stage, extract actual number + L.bufpos = startpos # restore position + var pos = startpos + try: + if (L.buf[pos] == '0') and (L.buf[pos + 1] in baseCodeChars): + inc(pos, 2) + xi = 0 # it is a base prefix - case L.buf[pos - 1] - of 'b', 'B': - result.base = base2 - while pos < endpos: - if L.buf[pos] != '_': - xi = `shl`(xi, 1) or (ord(L.buf[pos]) - ord('0')) - inc(pos) - # 'c', 'C' is deprecated - of 'o', 'c', 'C': - result.base = base8 - while pos < endpos: - if L.buf[pos] != '_': - xi = `shl`(xi, 3) or (ord(L.buf[pos]) - ord('0')) - inc(pos) - of 'x', 'X': - result.base = base16 - while pos < endpos: - case L.buf[pos] - of '_': + case L.buf[pos - 1] + of 'b', 'B': + result.base = base2 + while pos < endpos: + if L.buf[pos] != '_': + xi = `shl`(xi, 1) or (ord(L.buf[pos]) - ord('0')) inc(pos) - of '0'..'9': - xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('0')) + # 'c', 'C' is deprecated + of 'o', 'c', 'C': + result.base = base8 + while pos < endpos: + if L.buf[pos] != '_': + xi = `shl`(xi, 3) or (ord(L.buf[pos]) - ord('0')) inc(pos) - of 'a'..'f': - xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('a') + 10) - inc(pos) - of 'A'..'F': - xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('A') + 10) - inc(pos) - else: - break + of 'x', 'X': + result.base = base16 + while pos < endpos: + case L.buf[pos] + of '_': + inc(pos) + of '0'..'9': + xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('0')) + inc(pos) + of 'a'..'f': + xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('a') + 10) + inc(pos) + of 'A'..'F': + xi = `shl`(xi, 4) or (ord(L.buf[pos]) - ord('A') + 10) + inc(pos) + else: + break + else: + internalError(L.config, getLineInfo(L), "getNumber") + + case result.tokType + of tkIntLit, tkInt64Lit: setNumber result.iNumber, xi + of tkInt8Lit: setNumber result.iNumber, ashr(xi shl 56, 56) + of tkInt16Lit: setNumber result.iNumber, ashr(xi shl 48, 48) + of tkInt32Lit: setNumber result.iNumber, ashr(xi shl 32, 32) + of tkUIntLit, tkUInt64Lit: setNumber result.iNumber, xi + of tkUInt8Lit: setNumber result.iNumber, xi and 0xff + of tkUInt16Lit: setNumber result.iNumber, xi and 0xffff + of tkUInt32Lit: setNumber result.iNumber, xi and 0xffffffff + of tkFloat32Lit: + setNumber result.fNumber, (cast[PFloat32](addr(xi)))[] + # note: this code is endian neutral! + # XXX: Test this on big endian machine! + of tkFloat64Lit, tkFloatLit: + setNumber result.fNumber, (cast[PFloat64](addr(xi)))[] + else: internalError(L.config, getLineInfo(L), "getNumber") + + # Bounds checks. Non decimal literals are allowed to overflow the range of + # the datatype as long as their pattern don't overflow _bitwise_, hence + # below checks of signed sizes against uint*.high is deliberate: + # (0x80'u8 = 128, 0x80'i8 = -128, etc == OK) + if result.tokType notin floatTypes: + let outOfRange = + case result.tokType + of tkUInt8Lit, tkUInt16Lit, tkUInt32Lit: result.iNumber != xi + of tkInt8Lit: (xi > BiggestInt(uint8.high)) + of tkInt16Lit: (xi > BiggestInt(uint16.high)) + of tkInt32Lit: (xi > BiggestInt(uint32.high)) + else: false + + if outOfRange: + #echo "out of range num: ", result.iNumber, " vs ", xi + lexMessageLitNum(L, "number out of range: '$1'", startpos) + else: - internalError(L.config, getLineInfo(L), "getNumber") + case result.tokType + of floatTypes: + result.fNumber = parseFloat(result.literal) + of tkUInt64Lit, tkUIntLit: + var iNumber: uint64 + var len: int + try: + len = parseBiggestUInt(result.literal, iNumber) + except ValueError: + raise newException(OverflowDefect, "number out of range: " & $result.literal) + if len != result.literal.len: + raise newException(ValueError, "invalid integer: " & $result.literal) + result.iNumber = cast[int64](iNumber) + else: + var iNumber: int64 + var len: int + try: + len = parseBiggestInt(result.literal, iNumber) + except ValueError: + raise newException(OverflowDefect, "number out of range: " & $result.literal) + if len != result.literal.len: + raise newException(ValueError, "invalid integer: " & $result.literal) + result.iNumber = iNumber - case result.tokType - of tkIntLit, tkInt64Lit: setNumber result.iNumber, xi - of tkInt8Lit: setNumber result.iNumber, ashr(xi shl 56, 56) - of tkInt16Lit: setNumber result.iNumber, ashr(xi shl 48, 48) - of tkInt32Lit: setNumber result.iNumber, ashr(xi shl 32, 32) - of tkUIntLit, tkUInt64Lit: setNumber result.iNumber, xi - of tkUInt8Lit: setNumber result.iNumber, xi and 0xff - of tkUInt16Lit: setNumber result.iNumber, xi and 0xffff - of tkUInt32Lit: setNumber result.iNumber, xi and 0xffffffff - of tkFloat32Lit: - setNumber result.fNumber, (cast[PFloat32](addr(xi)))[] - # note: this code is endian neutral! - # XXX: Test this on big endian machine! - of tkFloat64Lit, tkFloatLit: - setNumber result.fNumber, (cast[PFloat64](addr(xi)))[] - else: internalError(L.config, getLineInfo(L), "getNumber") - - # Bounds checks. Non decimal literals are allowed to overflow the range of - # the datatype as long as their pattern don't overflow _bitwise_, hence - # below checks of signed sizes against uint*.high is deliberate: - # (0x80'u8 = 128, 0x80'i8 = -128, etc == OK) - if result.tokType notin floatTypes: + # Explicit bounds checks. let outOfRange = case result.tokType - of tkUInt8Lit, tkUInt16Lit, tkUInt32Lit: result.iNumber != xi - of tkInt8Lit: (xi > BiggestInt(uint8.high)) - of tkInt16Lit: (xi > BiggestInt(uint16.high)) - of tkInt32Lit: (xi > BiggestInt(uint32.high)) + of tkInt8Lit: result.iNumber > int8.high or result.iNumber < int8.low + of tkUInt8Lit: result.iNumber > BiggestInt(uint8.high) or result.iNumber < 0 + of tkInt16Lit: result.iNumber > int16.high or result.iNumber < int16.low + of tkUInt16Lit: result.iNumber > BiggestInt(uint16.high) or result.iNumber < 0 + of tkInt32Lit: result.iNumber > int32.high or result.iNumber < int32.low + of tkUInt32Lit: result.iNumber > BiggestInt(uint32.high) or result.iNumber < 0 else: false if outOfRange: - #echo "out of range num: ", result.iNumber, " vs ", xi lexMessageLitNum(L, "number out of range: '$1'", startpos) - else: - case result.tokType - of floatTypes: - result.fNumber = parseFloat(result.literal) - of tkUInt64Lit, tkUIntLit: - var iNumber: uint64 - var len: int - try: - len = parseBiggestUInt(result.literal, iNumber) - except ValueError: - raise newException(OverflowDefect, "number out of range: " & $result.literal) - if len != result.literal.len: - raise newException(ValueError, "invalid integer: " & $result.literal) - result.iNumber = cast[int64](iNumber) - else: - var iNumber: int64 - var len: int - try: - len = parseBiggestInt(result.literal, iNumber) - except ValueError: - raise newException(OverflowDefect, "number out of range: " & $result.literal) - if len != result.literal.len: - raise newException(ValueError, "invalid integer: " & $result.literal) - result.iNumber = iNumber + # Promote int literal to int64? Not always necessary, but more consistent + if result.tokType == tkIntLit: + if result.iNumber > high(int32) or result.iNumber < low(int32): + result.tokType = tkInt64Lit - # Explicit bounds checks. - let outOfRange = - case result.tokType - of tkInt8Lit: result.iNumber > int8.high or result.iNumber < int8.low - of tkUInt8Lit: result.iNumber > BiggestInt(uint8.high) or result.iNumber < 0 - of tkInt16Lit: result.iNumber > int16.high or result.iNumber < int16.low - of tkUInt16Lit: result.iNumber > BiggestInt(uint16.high) or result.iNumber < 0 - of tkInt32Lit: result.iNumber > int32.high or result.iNumber < int32.low - of tkUInt32Lit: result.iNumber > BiggestInt(uint32.high) or result.iNumber < 0 - else: false - - if outOfRange: - lexMessageLitNum(L, "number out of range: '$1'", startpos) - - # Promote int literal to int64? Not always necessary, but more consistent - if result.tokType == tkIntLit: - if result.iNumber > high(int32) or result.iNumber < low(int32): - result.tokType = tkInt64Lit - - except ValueError: - lexMessageLitNum(L, "invalid number: '$1'", startpos) - except OverflowDefect, RangeDefect: - lexMessageLitNum(L, "number out of range: '$1'", startpos) + except ValueError: + lexMessageLitNum(L, "invalid number: '$1'", startpos) + except OverflowDefect, RangeDefect: + lexMessageLitNum(L, "number out of range: '$1'", startpos) tokenEnd(result, postPos-1) L.bufpos = postPos @@ -830,8 +815,9 @@ proc getString(L: var Lexer, tok: var Token, mode: StringMode) = inc(pos) L.bufpos = pos -proc getCharacter(L: var Lexer, tok: var Token) = +proc getCharacter(L: var Lexer; tok: var Token) = tokenBegin(tok, L.bufpos) + let startPos = L.bufpos inc(L.bufpos) # skip ' var c = L.buf[L.bufpos] case c @@ -842,10 +828,16 @@ proc getCharacter(L: var Lexer, tok: var Token) = else: tok.literal = $c inc(L.bufpos) - if L.buf[L.bufpos] != '\'': - lexMessage(L, errGenerated, "missing closing ' for character literal") - tokenEndIgnore(tok, L.bufpos) - inc(L.bufpos) # skip ' + if L.buf[L.bufpos] == '\'': + tokenEndIgnore(tok, L.bufpos) + inc(L.bufpos) # skip ' + else: + if startPos > 0 and L.buf[startPos-1] == '`': + tok.literal = "'" + L.bufpos = startPos+1 + else: + lexMessage(L, errGenerated, "missing closing ' for character literal") + tokenEndIgnore(tok, L.bufpos) proc getSymbol(L: var Lexer, tok: var Token) = var h: Hash = 0 diff --git a/compiler/parser.nim b/compiler/parser.nim index fe857c81b..b9a6ffb8c 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -355,7 +355,7 @@ proc parseSymbol(p: var Parser, mode = smNormal): PNode = let node = newNodeI(nkIdent, lineinfo) node.ident = p.lex.cache.getIdent(accm) result.add(node) - of tokKeywordLow..tokKeywordHigh, tkSymbol, tkIntLit..tkCharLit: + of tokKeywordLow..tokKeywordHigh, tkSymbol, tkIntLit..tkCustomLit: result.add(newIdentNodeP(p.lex.cache.getIdent($p.tok), p)) getTok(p) else: @@ -627,7 +627,7 @@ proc identOrLiteral(p: var Parser, mode: PrimaryMode): PNode = #| | UINT_LIT | UINT8_LIT | UINT16_LIT | UINT32_LIT | UINT64_LIT #| | FLOAT_LIT | FLOAT32_LIT | FLOAT64_LIT #| | STR_LIT | RSTR_LIT | TRIPLESTR_LIT - #| | CHAR_LIT + #| | CHAR_LIT | CUSTOM_NUMERIC_LIT #| | NIL #| generalizedLit = GENERALIZED_STR_LIT | GENERALIZED_TRIPLESTR_LIT #| identOrLiteral = generalizedLit | symbol | literal @@ -710,6 +710,14 @@ proc identOrLiteral(p: var Parser, mode: PrimaryMode): PNode = of tkCharLit: result = newIntNodeP(nkCharLit, ord(p.tok.literal[0]), p) getTok(p) + of tkCustomLit: + let splitPos = p.tok.iNumber.int + let str = newStrNodeP(nkRStrLit, p.tok.literal.substr(0, splitPos-1), p) + let callee = newIdentNodeP(getIdent(p.lex.cache, p.tok.literal.substr(splitPos)), p) + result = newNodeP(nkDotExpr, p) + result.add str + result.add callee + getTok(p) of tkNil: result = newNodeP(nkNilLit, p) getTok(p) @@ -807,7 +815,7 @@ proc primarySuffix(p: var Parser, r: PNode, result = commandExpr(p, result, mode) break result = namedParams(p, result, nkCurlyExpr, tkCurlyRi) - of tkSymbol, tkAccent, tkIntLit..tkCharLit, tkNil, tkCast, + of tkSymbol, tkAccent, tkIntLit..tkCustomLit, tkNil, tkCast, tkOpr, tkDotDot, tkVar, tkOut, tkStatic, tkType, tkEnum, tkTuple, tkObject, tkProc: # XXX: In type sections we allow the free application of the @@ -1097,7 +1105,7 @@ proc isExprStart(p: Parser): bool = case p.tok.tokType of tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf, tkFor, tkProc, tkFunc, tkIterator, tkBind, tkBuiltInMagics, - tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCharLit, tkVar, tkRef, tkPtr, + tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCustomLit, tkVar, tkRef, tkPtr, tkTuple, tkObject, tkWhen, tkCase, tkOut: result = true else: result = false @@ -1498,7 +1506,7 @@ proc parseReturnOrRaise(p: var Parser, kind: TNodeKind): PNode = #| yieldStmt = 'yield' optInd expr? #| discardStmt = 'discard' optInd expr? #| breakStmt = 'break' optInd expr? - #| continueStmt = 'break' optInd expr? + #| continueStmt = 'continue' optInd expr? result = newNodeP(kind, p) getTok(p) if p.tok.tokType == tkComment: diff --git a/compiler/renderer.nim b/compiler/renderer.nim index c36eaaf11..9ca485f6e 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -942,7 +942,7 @@ proc skipHiddenNodes(n: PNode): PNode = else: break proc accentedName(g: var TSrcGen, n: PNode) = - const backticksNeeded = OpChars + {'[', '{'} + const backticksNeeded = OpChars + {'[', '{', '\''} if n == nil: return let isOperator = if n.kind == nkIdent and n.ident.s.len > 0 and n.ident.s[0] in backticksNeeded: true @@ -976,6 +976,11 @@ proc infixArgument(g: var TSrcGen, n: PNode, i: int) = if needsParenthesis: put(g, tkParRi, ")") +proc isCustomLit(n: PNode): bool = + n.len == 2 and n[0].kind == nkRStrLit and + (n[1].kind == nkIdent and n[1].ident.s.startsWith('\'')) or + (n[1].kind == nkSym and n[1].sym.name.s.startsWith('\'')) + proc gsub(g: var TSrcGen, n: PNode, c: TContext) = if isNil(n): return var @@ -1195,9 +1200,14 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = gcomma(g, n, c) put(g, tkBracketRi, "]") of nkDotExpr: - gsub(g, n, 0) - put(g, tkDot, ".") - gsub(g, n, 1) + if isCustomLit(n): + put(g, tkCustomLit, n[0].strVal) + gsub(g, n, 1) + else: + gsub(g, n, 0) + put(g, tkDot, ".") + if n.len > 1: + accentedName(g, n[1]) of nkBind: putWithSpace(g, tkBind, "bind") gsub(g, n, 0) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index ff8f68ed0..fee43162e 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1524,7 +1524,7 @@ proc semProcAnnotation(c: PContext, prc: PNode; return proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode {.nosinks.} = - ## used for resolving 'auto' in lambdas based on their callsite + ## used for resolving 'auto' in lambdas based on their callsite var n = n let original = n[namePos].sym let s = original #copySym(original, false) diff --git a/doc/grammar.txt b/doc/grammar.txt index 0d5eef179..d4f4a0515 100644 --- a/doc/grammar.txt +++ b/doc/grammar.txt @@ -46,7 +46,7 @@ literal = | INT_LIT | INT8_LIT | INT16_LIT | INT32_LIT | INT64_LIT | UINT_LIT | UINT8_LIT | UINT16_LIT | UINT32_LIT | UINT64_LIT | FLOAT_LIT | FLOAT32_LIT | FLOAT64_LIT | STR_LIT | RSTR_LIT | TRIPLESTR_LIT - | CHAR_LIT + | CHAR_LIT | CUSTOM_NUMERIC_LIT | NIL generalizedLit = GENERALIZED_STR_LIT | GENERALIZED_TRIPLESTR_LIT identOrLiteral = generalizedLit | symbol | literal @@ -100,6 +100,7 @@ postExprBlocks = ':' stmt? ( IND{=} doBlock | IND{=} 'of' exprList ':' stmt | IND{=} 'elif' expr ':' stmt | IND{=} 'except' exprList ':' stmt + | IND{=} 'finally' ':' stmt | IND{=} 'else' ':' stmt )* exprStmt = simpleExpr (( '=' optInd expr colonBody? ) diff --git a/doc/manual.rst b/doc/manual.rst index db1251630..a882eb945 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -490,11 +490,17 @@ this. Another reason is that Nim can thus support `array[char, int]` or type is used for Unicode characters, it can represent any Unicode character. `Rune` is declared in the `unicode module `_. +A character literal that does not end in ``'`` interpreted as ``'`` if there +is a preceeding backtick token. There must be no whitespace between the preceeding +backtick token and the character literal. This special rule ensures that a declaration +like ``proc `'customLiteral`(s: string)`` is valid. See also +`Custom Numeric Literals <#custom-numeric-literals>`_. -Numerical constants -------------------- -Numerical constants are of a single type and have the form:: +Numeric Literals +---------------- + +Numeric literals have the form:: hexdigit = digit | 'A'..'F' | 'a'..'f' octdigit = '0'..'7' @@ -530,8 +536,10 @@ Numerical constants are of a single type and have the form:: FLOAT64_LIT = HEX_LIT '\'' FLOAT64_SUFFIX | (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] FLOAT64_SUFFIX + CUSTOM_NUMERIC_LIT = (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) '\'' CUSTOM_NUMERIC_SUFFIX -As can be seen in the productions, numerical constants can contain underscores + +As can be seen in the productions, numeric literals can contain underscores for readability. Integer and floating-point literals may be given in decimal (no prefix), binary (prefix `0b`), octal (prefix `0o`), and hexadecimal (prefix `0x`) notation. @@ -579,7 +587,7 @@ is optional if it is not ambiguous (only hexadecimal floating-point literals with a type suffix can be ambiguous). -The type suffixes are: +The pre-defined type suffixes are: ================= ========================= Type Suffix Resulting type of literal @@ -611,6 +619,42 @@ the bit width of the datatype, it is accepted. Hence: 0b10000000'u8 == 0x80'u8 == 128, but, 0b10000000'i8 == 0x80'i8 == -1 instead of causing an overflow error. + +Custom Numeric Literals +~~~~~~~~~~~~~~~~~~~~~~~ + +If the suffix is not predefined, then the suffix is assumed to be a call +to a proc, template, macro or other callable identifier that is passed the +string containing the literal. The callable identifier needs to be declared +with a special ``'`` prefix: + +.. code-block:: nim + + import strutils + type u4 = distinct uint8 # a 4-bit unsigned integer aka "nibble" + proc `'u4`(n: string): u4 = + # The leading ' is required. + result = (parseInt(n) and 0x0F).u4 + + var x = 5'u4 + +More formally, a custom numeric literal `123'custom` is transformed +to r"123".`'custom` in the parsing step. There is no AST node kind that +corresponds to this transformation. The transformation naturally handles +the case that additional parameters are passed to the callee: + +.. code-block:: nim + + import strutils + type u4 = distinct uint8 # a 4-bit unsigned integer aka "nibble" + proc `'u4`(n: string; moreData: int): u4 = + result = (parseInt(n) and 0x0F).u4 + + var x = 5'u4(123) + +Custom numeric literals are covered by the grammar rule named `CUSTOM_NUMERIC_LIT`. + + Operators --------- diff --git a/tests/lexer/mlexerutils.nim b/tests/lexer/mlexerutils.nim new file mode 100644 index 000000000..eae7a0006 --- /dev/null +++ b/tests/lexer/mlexerutils.nim @@ -0,0 +1,9 @@ +import macros + +macro lispReprStr*(a: untyped): untyped = newLit(a.lispRepr) + +macro assertAST*(expected: string, struct: untyped): untyped = + var ast = newLit(struct.treeRepr) + result = quote do: + if `ast` != `expected`: + doAssert false, "\nGot:\n" & `ast`.indent(2) & "\nExpected:\n" & `expected`.indent(2) \ No newline at end of file diff --git a/tests/lexer/tcustom_numeric_literals.nim b/tests/lexer/tcustom_numeric_literals.nim new file mode 100644 index 000000000..a2f355b4d --- /dev/null +++ b/tests/lexer/tcustom_numeric_literals.nim @@ -0,0 +1,150 @@ +discard """ + targets: "c cpp js" +""" + +# Test tkStrNumLit + +import std/[macros, strutils] +import mlexerutils + +# AST checks + +assertAST dedent """ + StmtList + ProcDef + AccQuoted + Ident "\'" + Ident "wrap" + Empty + Empty + FormalParams + Ident "string" + IdentDefs + Ident "number" + Ident "string" + Empty + Empty + Empty + StmtList + Asgn + Ident "result" + Infix + Ident "&" + Infix + Ident "&" + StrLit "[[" + Ident "number" + StrLit "]]"""": + proc `'wrap`(number: string): string = + result = "[[" & number & "]]" + +assertAST dedent """ + StmtList + DotExpr + RStrLit "-38383839292839283928392839283928392839283.928493849385935898243e-50000" + Ident "\'wrap"""": + -38383839292839283928392839283928392839283.928493849385935898243e-50000'wrap + +proc `'wrap`(number: string): string = "[[" & number & "]]" +doAssert lispReprStr(-1'wrap) == """(DotExpr (RStrLit "-1") (Ident "\'wrap"))""" + +template main = + block: # basic suffix usage + template `'twrap`(number: string): untyped = + number.`'wrap` + proc extraContext(): string = + 22.40'wrap + proc `*`(left, right: string): string = + result = left & "times" & right + proc `+`(left, right: string): string = + result = left & "plus" & right + + doAssert 1'wrap == "[[1]]" + doAssert -1'wrap == "[[-1]]": + "unable to resolve a negative integer-suffix pattern" + doAssert 12345.67890'wrap == "[[12345.67890]]" + doAssert 1'wrap*1'wrap == "[[1]]times[[1]]": + "unable to resolve an operator between two suffixed numeric literals" + doAssert 1'wrap+ -1'wrap == "[[1]]plus[[-1]]": # will generate a compiler warning about inconsistent spacing + "unable to resolve a negative suffixed numeric literal following an operator" + doAssert 1'wrap + -1'wrap == "[[1]]plus[[-1]]" + doAssert 1'twrap == "[[1]]" + doAssert extraContext() == "[[22.40]]": + "unable to return a suffixed numeric literal by an implicit return" + doAssert 0x5a3a'wrap == "[[0x5a3a]]" + doAssert 0o5732'wrap == "[[0o5732]]" + doAssert 0b0101111010101'wrap == "[[0b0101111010101]]" + doAssert -38383839292839283928392839283928392839283.928493849385935898243e-50000'wrap == "[[-38383839292839283928392839283928392839283.928493849385935898243e-50000]]" + doAssert 1234.56'wrap == "[[1234.56]]": + "unable to properly account for context with suffixed numeric literals" + + block: # verify that the i64, f32, etc builtin suffixes still parse correctly + const expectedF32: float32 = 123.125 + proc `'f9`(number: string): string = # proc starts with 'f' just like 'f32' + "[[" & number & "]]" + proc `'f32a`(number: string): string = # looks even more like 'f32' + "[[" & number & "]]" + proc `'d9`(number: string): string = # proc starts with 'd' just like the d suffix + "[[" & number & "]]" + proc `'i9`(number: string): string = # proc starts with 'i' just like 'i64' + "[[" & number & "]]" + proc `'u9`(number: string): string = # proc starts with 'u' just like 'u8' + "[[" & number & "]]" + + doAssert 123.125f32 == expectedF32: + "failing to support non-quoted legacy f32 floating point suffix" + doAssert 123.125'f32 == expectedF32 + doAssert 123.125e0'f32 == expectedF32 + doAssert 1234.56'wrap == 1234.56'f9 + doAssert 1234.56'wrap == 1234.56'f32a + doAssert 1234.56'wrap == 1234.56'd9 + doAssert 1234.56'wrap == 1234.56'i9 + doAssert 1234.56'wrap == 1234.56'u9 + doAssert lispReprStr(1234.56'u9) == """(DotExpr (RStrLit "1234.56") (Ident "\'u9"))""": + "failed to properly build AST for suffix that starts with u" + doAssert -128'i8 == (-128).int8 + + block: # case checks + doAssert 1E2 == 100: + "lexer not handling upper-case exponent" + doAssert 1.0E2 == 100.0 + doAssert 1e2 == 100 + doAssert 0xdeadBEEF'wrap == "[[0xdeadBEEF]]": + "lexer not maintaining original case" + doAssert 0.1E12'wrap == "[[0.1E12]]" + doAssert 0.0e12'wrap == "[[0.0e12]]" + doAssert 0.0e+12'wrap == "[[0.0e+12]]" + doAssert 0.0e-12'wrap == "[[0.0e-12]]" + doAssert 0e-12'wrap == "[[0e-12]]" + + block: # macro and template usage + template `'foo`(a: string): untyped = (a, 2) + doAssert -12'foo == ("-12", 2) + template `'fooplus`(a: string, b: int): untyped = (a, b) + doAssert -12'fooplus(2) == ("-12", 2) + template `'fooplusopt`(a: string, b: int = 99): untyped = (a, b) + doAssert -12'fooplusopt(2) == ("-12", 2) + doAssert -12'fooplusopt() == ("-12", 99) + doAssert -12'fooplusopt == ("-12", 99) + macro `'bar`(a: static string): untyped = + var infix = newNimNode(nnkInfix) + infix.add newIdentNode("&") + infix.add newLit("got ") + infix.add newLit(a.repr) + result = newNimNode(nnkStmtList) + result.add infix + doAssert -12'bar == "got \"-12\"" + macro deb(a): untyped = newLit(a.repr) + doAssert deb(-12'bar) == "-12'bar" + # macro metawrap(): untyped = + # func wrap1(a: string): string = "{" & a & "}" + # func `'wrap2`(a: string): string = "{" & a & "}" + # result = quote do: + # let a1 = wrap1"-128" + # let a2 = -128'wrap2 + # metawrap() + # doAssert a1 == "{-128}" + # doAssert a2 == "{-128}" + +static: main() +main() diff --git a/tests/lexer/tstrlits.nim b/tests/lexer/tstrlits.nim deleted file mode 100644 index 8e8250a5b..000000000 --- a/tests/lexer/tstrlits.nim +++ /dev/null @@ -1,19 +0,0 @@ -discard """ - output: "a\"\"long string\"\"\"\"\"abc\"def_'2'●𝌆𝌆A" -""" -# Test the new different string literals - -const - tripleEmpty = """"long string"""""""" # "long string """"" - - rawQuote = r"a""" - - raw = r"abc""def" - - escaped = "\x5f'\50'\u25cf\u{1D306}\u{1d306}\u{41}" - - -stdout.write(rawQuote) -stdout.write(tripleEmpty) -stdout.write(raw) -stdout.writeLine(escaped) diff --git a/tests/lexer/tunary_minus.nim b/tests/lexer/tunary_minus.nim index 89f1b79ef..0aa861d53 100644 --- a/tests/lexer/tunary_minus.nim +++ b/tests/lexer/tunary_minus.nim @@ -6,13 +6,7 @@ discard """ import std/[macros, strutils] -macro lispReprStr*(a: untyped): untyped = newLit(a.lispRepr) - -macro assertAST*(expected: string, struct: untyped): untyped = - var ast = newLit(struct.treeRepr) - result = quote do: - if `ast` != `expected`: - doAssert false, "\nGot:\n" & `ast`.indent(2) & "\nExpected:\n" & `expected`.indent(2) +import mlexerutils const one = 1 const minusOne = `-`(one) From 355985ac89dd13d5b317744b30842155d9321565 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 24 Mar 2021 22:29:42 +0100 Subject: [PATCH 1784/3768] custom integer literals bugfixes (#17499) * custom integer literals bugfixes * make nimsuggest compile again --- changelog.md | 2 +- compiler/lexer.nim | 9 +++++---- doc/manual.rst | 6 +++++- tests/lexer/tunary_minus.nim | 3 +++ 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/changelog.md b/changelog.md index 922b8470a..f180e90f9 100644 --- a/changelog.md +++ b/changelog.md @@ -266,7 +266,7 @@ - The unary minus in `-1` is now part of the integer literal, it is now parsed as a single token. This implies that edge cases like `-128'i8` finally work correctly. -- Custom numeric literals are now supported. +- Custom numeric literals (e.g. `-128'bignum`) are now supported. ## Compiler changes diff --git a/compiler/lexer.nim b/compiler/lexer.nim index b34b010c2..ecf6cb0d9 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -345,13 +345,14 @@ proc getNumber(L: var Lexer, result: var Token) = result.tokType = tkIntLit # int literal until we know better result.literal = "" result.base = base10 - let startpos = L.bufpos - tokenBegin(result, startpos) + tokenBegin(result, L.bufpos) var isPositive = true if L.buf[L.bufpos] == '-': eatChar(L, result) - isPositive = true + isPositive = false + + let startpos = L.bufpos template setNumber(field, value) = field = (if isPositive: value else: -value) @@ -419,7 +420,7 @@ proc getNumber(L: var Lexer, result: var Token) = suffixAsLower.add toLowerAscii(c) inc postPos if L.buf[postPos] notin SymChars+{'_'}: break - case suffix + case suffixAsLower of "f", "f32": result.tokType = tkFloat32Lit of "d", "f64": result.tokType = tkFloat64Lit of "f128": result.tokType = tkFloat128Lit diff --git a/doc/manual.rst b/doc/manual.rst index a882eb945..51c434c7b 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -490,7 +490,7 @@ this. Another reason is that Nim can thus support `array[char, int]` or type is used for Unicode characters, it can represent any Unicode character. `Rune` is declared in the `unicode module `_. -A character literal that does not end in ``'`` interpreted as ``'`` if there +A character literal that does not end in ``'`` is interpreted as ``'`` if there is a preceeding backtick token. There must be no whitespace between the preceeding backtick token and the character literal. This special rule ensures that a declaration like ``proc `'customLiteral`(s: string)`` is valid. See also @@ -538,6 +538,9 @@ Numeric literals have the form:: CUSTOM_NUMERIC_LIT = (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) '\'' CUSTOM_NUMERIC_SUFFIX + # CUSTOM_NUMERIC_SUFFIX is any Nim identifier that is not + # a pre-defined type suffix. + As can be seen in the productions, numeric literals can contain underscores for readability. Integer and floating-point literals may be given in decimal (no @@ -653,6 +656,7 @@ the case that additional parameters are passed to the callee: var x = 5'u4(123) Custom numeric literals are covered by the grammar rule named `CUSTOM_NUMERIC_LIT`. +A custom numeric literal is a single token. Operators diff --git a/tests/lexer/tunary_minus.nim b/tests/lexer/tunary_minus.nim index 0aa861d53..639911fcd 100644 --- a/tests/lexer/tunary_minus.nim +++ b/tests/lexer/tunary_minus.nim @@ -51,6 +51,9 @@ template main = doAssert x() == minusOne: "unable to handle negatives after semi-colon" + doAssert -0b111 == -7 + doAssert -0xff == -255 + block: # check when a minus (-) is an unary op doAssert -one == minusOne: "unable to a negative prior to identifier" From 76d391a512b53b731941d6b355fe3431fc6b74c1 Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 25 Mar 2021 14:06:30 +0800 Subject: [PATCH 1785/3768] remove unnecessary check (#17502) --- compiler/spawn.nim | 2 -- 1 file changed, 2 deletions(-) diff --git a/compiler/spawn.nim b/compiler/spawn.nim index 54ed51dbc..1635f9f83 100644 --- a/compiler/spawn.nim +++ b/compiler/spawn.nim @@ -371,8 +371,6 @@ proc wrapProcForSpawn*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; spawnExp fn = indirectAccess(castExpr, field, n.info) elif fn.kind == nkSym and fn.sym.kind == skIterator: localError(g.config, n.info, "iterator in spawn environment is not allowed") - elif fn.typ.callConv == ccClosure: - localError(g.config, n.info, "closure in spawn environment is not allowed") call.add(fn) var varSection = newNodeI(nkVarSection, n.info) From 045400ad92f98363ef8931aea29165b48d07ac16 Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 25 Mar 2021 14:07:29 +0800 Subject: [PATCH 1786/3768] ref https://github.com/nim-lang/Nim/pull/17333 (#17495) --- doc/nep1.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/nep1.rst b/doc/nep1.rst index 3bae6a00b..bdf8e0eab 100644 --- a/doc/nep1.rst +++ b/doc/nep1.rst @@ -157,7 +157,8 @@ English word To use Notes ------------------- ------------ -------------------------------------- initialize initFoo initializes a value type `Foo` new newFoo initializes a reference type `Foo` - via `new` + via `new` or a value type `Foo` + with reference semantics. this or self self for method like procs, e.g.: `proc fun(self: Foo, a: int)` rationale: `self` is more unique in English From 46364e63cd0c292b3a803d1975d48db73f46e8e6 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Thu, 25 Mar 2021 10:15:05 +0300 Subject: [PATCH 1787/3768] fix RST parsing after option lists (#17442) --- lib/packages/docutils/rst.nim | 73 ++++++++++++++++++++++++++++++++++- tests/stdlib/trstgen.nim | 49 +++++++++++++++++++++++ 2 files changed, 121 insertions(+), 1 deletion(-) diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 11768c7da..adc83c9af 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -82,6 +82,14 @@ ## * ***triple emphasis*** (bold and italic) using \*\*\* ## * ``:idx:`` role for \`interpreted text\` to include the link to this ## text into an index (example: `Nim index`_). +## * double slash `//` in option lists serves as a prefix for any option that +## starts from a word (without any leading symbols like `-`, `--`, `/`):: +## +## //compile compile the project +## //doc generate documentation +## +## Here the dummy `//` will disappear, while options ``compile`` +## and ``doc`` will be left in the final document. ## ## .. [cmp:Sphinx] similar but different from the directives of ## Python `Sphinx directives`_ extensions @@ -548,6 +556,67 @@ proc pushInd(p: var RstParser, ind: int) = proc popInd(p: var RstParser) = if p.indentStack.len > 1: setLen(p.indentStack, p.indentStack.len - 1) +# Working with indentation in rst.nim +# ----------------------------------- +# +# Every line break has an associated tkIndent. +# The tokenizer writes back the first column of next non-blank line +# in all preceeding tkIndent tokens to the `ival` field of tkIndent. +# +# RST document is separated into body elements (B.E.), every of which +# has a dedicated handler proc (or block of logic when B.E. is a block quote) +# that should follow the next rule: +# Every B.E. handler proc should finish at tkIndent (newline) +# after its B.E. finishes. +# Then its callers (which is `parseSection` or another B.E. handler) +# check for tkIndent ival (without necessity to advance `p.idx`) +# and decide themselves whether they continue processing or also stop. +# +# An example:: +# +# L RST text fragment indentation +# +--------------------+ +# 1 | | <- (empty line at the start of file) no tokens +# 2 |First paragraph. | <- tkIndent has ival=0, and next tkWord has col=0 +# 3 | | <- tkIndent has ival=0 +# 4 |* bullet item and | <- tkIndent has ival=0, and next tkPunct has col=0 +# 5 | its continuation | <- tkIndent has ival=2, and next tkWord has col=2 +# 6 | | <- tkIndent has ival=4 +# 7 | Block quote | <- tkIndent has ival=4, and next tkWord has col=4 +# 8 | | <- tkIndent has ival=0 +# 9 | | <- tkIndent has ival=0 +# 10|Final paragraph | <- tkIndent has ival=0, and tkWord has col=0 +# +--------------------+ +# C:01234 +# +# Here parser starts with initial `indentStack=[0]` and then calls the +# 1st `parseSection`: +# +# - `parseSection` calls `parseParagraph` and "First paragraph" is parsed +# - bullet list handler is started at reaching ``*`` (L4 C0), it +# starts bullet item logic (L4 C2), which calls `pushInd(p, ind=2)`, +# then calls `parseSection` (2nd call, nested) which parses +# paragraph "bullet list and its continuation" and then starts +# a block quote logic (L7 C4). +# The block quote logic calls calls `pushInd(p, ind=4)` and +# calls `parseSection` again, so a (simplified) sequence of calls now is:: +# +# parseSection -> parseBulletList -> +# parseSection (+block quote logic) -> parseSection +# +# 3rd `parseSection` finishes, block quote logic calls `popInd(p)`, +# it returns to bullet item logic, which sees that next tkIndent has +# ival=0 and stops there since the required indentation for a bullet item +# is 2 and 0<2; the bullet item logic calls `popInd(p)`. +# Then bullet list handler checks that next tkWord (L10 C0) has the +# right indentation but does not have ``*`` so stops at tkIndent (L10). +# - 1st `parseSection` invocation calls `parseParagraph` and the +# "Final paragraph" is parsed. +# +# If a B.E. handler has advanced `p.idx` past tkIndent to check +# whether it should continue its processing or not, and decided not to, +# then this B.E. handler should step back (e.g. do `dec p.idx`). + proc initParser(p: var RstParser, sharedState: PSharedState) = p.indentStack = @[0] p.tok = @[] @@ -1912,8 +1981,9 @@ proc parseBulletList(p: var RstParser): PRstNode = proc parseOptionList(p: var RstParser): PRstNode = result = newRstNodeA(p, rnOptionList) + let col = currentTok(p).col while true: - if isOptionList(p): + if currentTok(p).col == col and isOptionList(p): var a = newRstNode(rnOptionGroup) var b = newRstNode(rnDescription) var c = newRstNode(rnOptionListItem) @@ -1936,6 +2006,7 @@ proc parseOptionList(p: var RstParser): PRstNode = c.add(b) result.add(c) else: + dec p.idx # back to tkIndent break proc parseDefinitionList(p: var RstParser): PRstNode = diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim index d6055cc11..0f3890faa 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -1277,6 +1277,55 @@ Test1 let refline = "Ref. " & ref1 & "! and " & ref2 & ";and " & ref3 & "." doAssert refline in output1 + test "Option lists 1": + # check that "* b" is not consumed by previous bullet item because of + # incorrect indentation handling in option lists + let input = dedent """ + * a + -m desc + -n very long + desc + * b""" + let output = input.toHtml + check(output.count("") == 2) + check(output.count("-mdesc""" in output) + check("""-nvery long desc""" in + output) + + test "Option lists 2": + # check that 2nd option list is not united with the 1st + let input = dedent """ + * a + -m desc + -n very long + desc + -d option""" + let output = input.toHtml + check(output.count("-mdesc""" in output) + check("""-nvery long desc""" in + output) + check("""-doption""" in + output) + + test "Option list 3 (double /)": + let input = dedent """ + * a + //compile compile1 + //doc doc1 + cont + -d option""" + let output = input.toHtml + check(output.count("compilecompile1""" in output) + check("""docdoc1 cont""" in + output) + check("""-doption""" in + output) suite "RST/Code highlight": test "Basic Python code highlight": let pythonCode = """ From 6e0a293dab195c973a45b38d22fdc97575c025fc Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 25 Mar 2021 15:28:06 +0800 Subject: [PATCH 1788/3768] re-enable fidget (#17229) --- testament/important_packages.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 1dd7c69ca..66a2fa09e 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -49,6 +49,7 @@ pkg "chronicles", "nim c -o:chr -r chronicles.nim" # when not defined(osx): # testdatagram.nim(560, 54): Check failed # pkg "chronos", "nim c -r -d:release tests/testall" # pending https://github.com/nim-lang/Nim/issues/17130 + pkg "cligen", "nim c --path:. -r cligen.nim" pkg "combparser", "nimble test --gc:orc" pkg "compactdict" @@ -59,7 +60,7 @@ pkg "delaunay" pkg "docopt" pkg "easygl", "nim c -o:egl -r src/easygl.nim", "https://github.com/jackmott/easygl" pkg "elvis" -# pkg "fidget" # pending https://github.com/treeform/fidget/issues/133 +pkg "fidget" pkg "fragments", "nim c -r fragments/dsl.nim" pkg "fusion" pkg "gara" From 8573160a44439ffe3fbbdd354188db9c976a2081 Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 25 Mar 2021 21:05:50 +0800 Subject: [PATCH 1789/3768] lent support for tables (#17505) --- lib/pure/collections/tables.nim | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index f77642349..9387807c0 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -702,7 +702,7 @@ iterator mpairs*[A, B](t: var Table[A, B]): (A, var B) = yield (t.data[h].key, t.data[h].val) assert(len(t) == L, "the length of the table changed while iterating over it") -iterator keys*[A, B](t: Table[A, B]): A = +iterator keys*[A, B](t: Table[A, B]): lent A = ## Iterates over any key in the table `t`. ## ## See also: @@ -723,7 +723,7 @@ iterator keys*[A, B](t: Table[A, B]): A = yield t.data[h].key assert(len(t) == L, "the length of the table changed while iterating over it") -iterator values*[A, B](t: Table[A, B]): B = +iterator values*[A, B](t: Table[A, B]): lent B = ## Iterates over any value in the table `t`. ## ## See also: @@ -1146,7 +1146,7 @@ iterator mpairs*[A, B](t: TableRef[A, B]): (A, var B) = yield (t.data[h].key, t.data[h].val) assert(len(t) == L, "the length of the table changed while iterating over it") -iterator keys*[A, B](t: TableRef[A, B]): A = +iterator keys*[A, B](t: TableRef[A, B]): lent A = ## Iterates over any key in the table `t`. ## ## See also: @@ -1167,7 +1167,7 @@ iterator keys*[A, B](t: TableRef[A, B]): A = yield t.data[h].key assert(len(t) == L, "the length of the table changed while iterating over it") -iterator values*[A, B](t: TableRef[A, B]): B = +iterator values*[A, B](t: TableRef[A, B]): lent B = ## Iterates over any value in the table `t`. ## ## See also: @@ -1722,7 +1722,7 @@ iterator mpairs*[A, B](t: var OrderedTable[A, B]): (A, var B) = yield (t.data[h].key, t.data[h].val) assert(len(t) == L, "the length of the table changed while iterating over it") -iterator keys*[A, B](t: OrderedTable[A, B]): A = +iterator keys*[A, B](t: OrderedTable[A, B]): lent A = ## Iterates over any key in the table `t` in insertion order. ## ## See also: @@ -1743,7 +1743,7 @@ iterator keys*[A, B](t: OrderedTable[A, B]): A = yield t.data[h].key assert(len(t) == L, "the length of the table changed while iterating over it") -iterator values*[A, B](t: OrderedTable[A, B]): B = +iterator values*[A, B](t: OrderedTable[A, B]): lent B = ## Iterates over any value in the table `t` in insertion order. ## ## See also: @@ -2130,7 +2130,7 @@ iterator mpairs*[A, B](t: OrderedTableRef[A, B]): (A, var B) = yield (t.data[h].key, t.data[h].val) assert(len(t) == L, "the length of the table changed while iterating over it") -iterator keys*[A, B](t: OrderedTableRef[A, B]): A = +iterator keys*[A, B](t: OrderedTableRef[A, B]): lent A = ## Iterates over any key in the table `t` in insertion order. ## ## See also: @@ -2151,7 +2151,7 @@ iterator keys*[A, B](t: OrderedTableRef[A, B]): A = yield t.data[h].key assert(len(t) == L, "the length of the table changed while iterating over it") -iterator values*[A, B](t: OrderedTableRef[A, B]): B = +iterator values*[A, B](t: OrderedTableRef[A, B]): lent B = ## Iterates over any value in the table `t` in insertion order. ## ## See also: @@ -2543,7 +2543,7 @@ iterator mpairs*[A](t: var CountTable[A]): (A, var int) = yield (t.data[h].key, t.data[h].val) assert(len(t) == L, "the length of the table changed while iterating over it") -iterator keys*[A](t: CountTable[A]): A = +iterator keys*[A](t: CountTable[A]): lent A = ## Iterates over any key in the table `t`. ## ## See also: From 42e895feb11515689aaca96f58fcffb366d34bb7 Mon Sep 17 00:00:00 2001 From: flywind Date: Fri, 26 Mar 2021 12:20:58 +0800 Subject: [PATCH 1790/3768] close #15696 (#17518) --- tests/vm/tvmmisc.nim | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/vm/tvmmisc.nim b/tests/vm/tvmmisc.nim index a485bac2e..f542fa560 100644 --- a/tests/vm/tvmmisc.nim +++ b/tests/vm/tvmmisc.nim @@ -255,6 +255,31 @@ block: doAssert e == @[] doAssert f == @[] + +block: # bug #10815 + type + Opcode = enum + iChar, iSet + + Inst = object + case code: Opcode + of iChar: + c: char + of iSet: + cs: set[char] + + Patt = seq[Inst] + + + proc `$`(p: Patt): string = + discard + + proc P(): Patt = + result.add Inst(code: iSet) + + const a = P() + doAssert $a == "" + import tables block: # bug #8007 From 8049d0befc8ac7689e98f266df69b5f268fb7683 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 25 Mar 2021 23:07:33 -0700 Subject: [PATCH 1791/3768] testament: remove unused tfile,tline,tcolumn (#17515) --- testament/specs.nim | 8 -------- testament/testament.nim | 30 ++---------------------------- 2 files changed, 2 insertions(+), 36 deletions(-) diff --git a/testament/specs.nim b/testament/specs.nim index 6b80fe41d..b5e6de7c2 100644 --- a/testament/specs.nim +++ b/testament/specs.nim @@ -79,8 +79,6 @@ type sortoutput*: bool output*: string line*, column*: int - tfile*: string - tline*, tcolumn*: int exitCode*: int msg*: string ccodeCheck*: seq[string] @@ -277,12 +275,6 @@ proc parseSpec*(filename: string): TSpec = if result.msg.len == 0 and result.nimout.len == 0: result.parseErrors.addLine "errormsg or msg needs to be specified before column" discard parseInt(e.value, result.column) - of "tfile": - result.tfile = e.value - of "tline": - discard parseInt(e.value, result.tline) - of "tcolumn": - discard parseInt(e.value, result.tcolumn) of "output": if result.outputCheck != ocSubstr: result.outputCheck = ocEqual diff --git a/testament/testament.nim b/testament/testament.nim index f45d4043a..6fc7e32ce 100644 --- a/testament/testament.nim +++ b/testament/testament.nim @@ -82,12 +82,6 @@ type let pegLineError = peg"{[^(]*} '(' {\d+} ', ' {\d+} ') ' ('Error') ':' \s* {.*}" - - pegLineTemplate = - peg""" - {[^(]*} '(' {\d+} ', ' {\d+} ') ' - 'template/generic instantiation' ( ' of `' [^`]+ '`' )? ' from here' .* - """ pegOtherError = peg"'Error:' \s* {.*}" pegOfInterest = pegLineError / pegOtherError @@ -165,7 +159,6 @@ proc callCompiler(cmdTemplate, filename, options, nimcache: string, let outp = p.outputStream var suc = "" var err = "" - var tmpl = "" var x = newStringOfCap(120) result.nimout = "" while true: @@ -174,9 +167,6 @@ proc callCompiler(cmdTemplate, filename, options, nimcache: string, if x =~ pegOfInterest: # `err` should contain the last error/warning message err = x - elif x =~ pegLineTemplate and err == "": - # `tmpl` contains the last template expansion before the error - tmpl = x elif x.isSuccess: suc = x elif not running(p): @@ -187,14 +177,7 @@ proc callCompiler(cmdTemplate, filename, options, nimcache: string, result.output = "" result.line = 0 result.column = 0 - result.tfile = "" - result.tline = 0 - result.tcolumn = 0 result.err = reNimcCrash - if tmpl =~ pegLineTemplate: - result.tfile = extractFilename(matches[0]) - result.tline = parseInt(matches[1]) - result.tcolumn = parseInt(matches[2]) if err =~ pegLineError: result.file = extractFilename(matches[0]) result.line = parseInt(matches[1]) @@ -374,22 +357,13 @@ proc cmpMsgs(r: var TResults, expected, given: TSpec, test: TTest, target: TTarg r.addResult(test, target, expected.msg, given.msg, reMsgsDiffer) elif expected.nimout.len > 0 and not greedyOrderedSubsetLines(expected.nimout, given.nimout): r.addResult(test, target, expected.nimout, given.nimout, reMsgsDiffer) - elif expected.tfile == "" and extractFilename(expected.file) != extractFilename(given.file) and + elif extractFilename(expected.file) != extractFilename(given.file) and "internal error:" notin expected.msg: r.addResult(test, target, expected.file, given.file, reFilesDiffer) elif expected.line != given.line and expected.line != 0 or expected.column != given.column and expected.column != 0: r.addResult(test, target, $expected.line & ':' & $expected.column, - $given.line & ':' & $given.column, - reLinesDiffer) - elif expected.tfile != "" and extractFilename(expected.tfile) != extractFilename(given.tfile) and - "internal error:" notin expected.msg: - r.addResult(test, target, expected.tfile, given.tfile, reFilesDiffer) - elif expected.tline != given.tline and expected.tline != 0 or - expected.tcolumn != given.tcolumn and expected.tcolumn != 0: - r.addResult(test, target, $expected.tline & ':' & $expected.tcolumn, - $given.tline & ':' & $given.tcolumn, - reLinesDiffer) + $given.line & ':' & $given.column, reLinesDiffer) else: r.addResult(test, target, expected.msg, given.msg, reSuccess) inc(r.passed) From e3e97421438da27267b159b922d7338fa92f1cd8 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 26 Mar 2021 08:52:26 +0100 Subject: [PATCH 1792/3768] added a 'koch ic' command for easier adhoc testing of IC (#17508) * added a 'koch ic' command for easier adhoc testing of IC * IC: progress * IC: enable generics test * make tests green --- compiler/ccgtypes.nim | 7 ++----- compiler/cgen.nim | 30 ++++++++++++++++++++++++++++++ compiler/ic/cbackend.nim | 12 +++++++++++- compiler/ic/ic.nim | 22 +++++++++++++++++----- compiler/ic/rodfiles.nim | 1 + compiler/modulegraphs.nim | 16 ++++++++++++++++ koch.nim | 21 +++++++++++++++++++-- tests/ic/tgenerics.nim | 1 - 8 files changed, 96 insertions(+), 14 deletions(-) diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index e17a55542..b0c3aa3b1 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -1357,9 +1357,6 @@ proc genTypeInfoV2Impl(m: BModule, t, origType: PType, name: Rope; info: TLineIn if t.kind == tyObject and t.len > 0 and t[0] != nil and optEnableDeepCopy in m.config.globalOptions: discard genTypeInfoV1(m, t, info) -proc moduleOpenForCodegen(m: BModule; module: int32): bool {.inline.} = - result = module < m.g.modules.len and m.g.modules[module] != nil - proc genTypeInfoV2(m: BModule, t: PType; info: TLineInfo): Rope = let origType = t # distinct types can have their own destructors @@ -1384,7 +1381,7 @@ proc genTypeInfoV2(m: BModule, t: PType; info: TLineInfo): Rope = m.typeInfoMarkerV2[sig] = result let owner = t.skipTypes(typedescPtrs).itemId.module - if owner != m.module.position and moduleOpenForCodegen(m, owner): + if owner != m.module.position and moduleOpenForCodegen(m.g.graph, FileIndex owner): # make sure the type info is created in the owner module discard genTypeInfoV2(m.g.modules[owner], origType, info) # reference the type info as extern here @@ -1463,7 +1460,7 @@ proc genTypeInfoV1(m: BModule, t: PType; info: TLineInfo): Rope = return prefixTI.rope & result & ")".rope var owner = t.skipTypes(typedescPtrs).itemId.module - if owner != m.module.position and moduleOpenForCodegen(m, owner): + if owner != m.module.position and moduleOpenForCodegen(m.g.graph, FileIndex owner): # make sure the type info is created in the owner module discard genTypeInfoV1(m.g.modules[owner], origType, info) # reference the type info as extern here diff --git a/compiler/cgen.nim b/compiler/cgen.nim index b88999088..7dfab6a42 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -22,6 +22,7 @@ when not defined(leanCompiler): import strutils except `%` # collides with ropes.`%` +from ic / ic import ModuleBackendFlag from modulegraphs import ModuleGraph, PPassContext from lineinfos import warnGcMem, errXMustBeCompileTime, hintDependency, errGenerated, errCannotOpenFile @@ -1501,6 +1502,33 @@ proc genMainProc(m: BModule) = if m.config.cppCustomNamespace.len > 0: m.s[cfsProcs].add openNamespaceNim(m.config.cppCustomNamespace) +proc registerInitProcs*(g: BModuleList; m: PSym; flags: set[ModuleBackendFlag]) = + ## Called from the IC backend. + if HasDatInitProc in flags: + let datInit = getSomeNameForModule(m) & "DatInit000" + g.mainModProcs.addf("N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [datInit]) + g.mainDatInit.addf("\t$1();$N", [datInit]) + if HasModuleInitProc in flags: + let init = getSomeNameForModule(m) & "Init000" + g.mainModProcs.addf("N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [init]) + let initCall = "\t$1();$N" % [init] + if sfMainModule in m.flags: + g.mainModInit.add(initCall) + elif sfSystemModule in m.flags: + g.mainDatInit.add(initCall) # systemInit must called right after systemDatInit if any + else: + g.otherModsInit.add(initCall) + +proc whichInitProcs*(m: BModule): set[ModuleBackendFlag] = + # called from IC. + result = {} + if m.hcrOn or m.preInitProc.s(cpsInit).len > 0 or m.preInitProc.s(cpsStmts).len > 0: + result.incl HasModuleInitProc + for i in cfsTypeInit1..cfsDynLibInit: + if m.s[i].len != 0: + result.incl HasDatInitProc + break + proc registerModuleToMain(g: BModuleList; m: BModule) = let init = m.getInitName @@ -1595,6 +1623,7 @@ proc genDatInitCode(m: BModule) = if moduleDatInitRequired: m.s[cfsDatInitProc].add(prc) + #rememberFlag(m.g.graph, m.module, HasDatInitProc) # Very similar to the contents of symInDynamicLib - basically only the # things needed for the hot code reloading runtime procs to be loaded @@ -1725,6 +1754,7 @@ proc genInitCode(m: BModule) = if moduleInitRequired or sfMainModule in m.module.flags: m.s[cfsInitProc].add(prc) + #rememberFlag(m.g.graph, m.module, HasModuleInitProc) genDatInitCode(m) diff --git a/compiler/ic/cbackend.nim b/compiler/ic/cbackend.nim index 34ee59d52..9282ac466 100644 --- a/compiler/ic/cbackend.nim +++ b/compiler/ic/cbackend.nim @@ -44,9 +44,11 @@ proc generateCodeForModule(g: ModuleGraph; m: var LoadedModule; alive: var Alive cgen.genTopLevelStmt(bmod, n) finalCodegenActions(g, bmod, newNodeI(nkStmtList, m.module.info)) + m.fromDisk.backendFlags = cgen.whichInitProcs(bmod) proc replayTypeInfo(g: ModuleGraph; m: var LoadedModule; origin: FileIndex) = for x in mitems(m.fromDisk.emittedTypeInfo): + #echo "found type ", x, " for file ", int(origin) g.emittedTypeInfo[x] = origin proc addFileToLink(config: ConfigRef; m: PSym) = @@ -112,12 +114,16 @@ proc generateCode*(g: ModuleGraph) = resetForBackend(g) var alive = computeAliveSyms(g.packed, g.config) + when false: + for i in 0..high(g.packed): + echo i, " is of status ", g.packed[i].status, " ", toFullPath(g.config, FileIndex(i)) + for i in 0..high(g.packed): # case statement here to enforce exhaustive checks. case g.packed[i].status of undefined: discard "nothing to do" - of loading: + of loading, stored: assert false of storing, outdated: generateCodeForModule(g, g.packed[i], alive) @@ -133,3 +139,7 @@ proc generateCode*(g: ModuleGraph) = else: addFileToLink(g.config, g.packed[i].module) replayTypeInfo(g, g.packed[i], FileIndex(i)) + + if g.backend == nil: + g.backend = cgendata.newModuleList(g) + registerInitProcs(BModuleList(g.backend), g.packed[i].module, g.packed[i].fromDisk.backendFlags) diff --git a/compiler/ic/ic.nim b/compiler/ic/ic.nim index 230b4d087..8017d8b40 100644 --- a/compiler/ic/ic.nim +++ b/compiler/ic/ic.nim @@ -22,6 +22,10 @@ type options: TOptions globalOptions: TGlobalOptions + ModuleBackendFlag* = enum + HasDatInitProc + HasModuleInitProc + PackedModule* = object ## the parts of a PackedEncoder that are part of the .rod file definedSymbols: string includes: seq[(LitId, string)] # first entry is the module filename itself @@ -43,6 +47,7 @@ type enumToStringProcs*: seq[(PackedItemId, PackedItemId)] emittedTypeInfo*: seq[string] + backendFlags*: set[ModuleBackendFlag] sh*: Shared cfg: PackedConfig @@ -556,6 +561,9 @@ proc loadRodFile*(filename: AbsoluteFile; m: var PackedModule; config: ConfigRef loadSeqSection enumToStringProcsSection, m.enumToStringProcs loadSeqSection typeInfoSection, m.emittedTypeInfo + f.loadSection backendFlagsSection + f.loadPrim m.backendFlags + close(f) result = f.err @@ -619,6 +627,9 @@ proc saveRodFile*(filename: AbsoluteFile; encoder: var PackedEncoder; m: var Pac storeSeqSection enumToStringProcsSection, m.enumToStringProcs storeSeqSection typeInfoSection, m.emittedTypeInfo + f.storeSection backendFlagsSection + f.storePrim m.backendFlags + close(f) encoder.disable() if f.err != ok: @@ -646,7 +657,8 @@ type storing, # state is strictly for stress-testing purposes loading, loaded, - outdated + outdated, + stored # store is complete, no further additions possible LoadedModule* = object status*: ModuleStatus @@ -673,7 +685,7 @@ proc toFileIndexCached*(c: var PackedDecoder; g: PackedModuleGraph; thisModule: proc translateLineInfo(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; x: PackedLineInfo): TLineInfo = - assert g[thisModule].status in {loaded, storing} + assert g[thisModule].status in {loaded, storing, stored} result = TLineInfo(line: x.line, col: x.col, fileIndex: toFileIndexCached(c, g, thisModule, x.file)) @@ -806,7 +818,7 @@ proc loadSym(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; s: result = nil else: let si = moduleIndex(c, g, thisModule, s) - assert g[si].status in {loaded, storing} + assert g[si].status in {loaded, storing, stored} if not g[si].symsInit: g[si].symsInit = true setLen g[si].syms, g[si].fromDisk.sh.syms.len @@ -852,7 +864,7 @@ proc loadType(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; t result = nil else: let si = moduleIndex(c, g, thisModule, t) - assert g[si].status in {loaded, storing} + assert g[si].status in {loaded, storing, stored} assert t.item > 0 if not g[si].typesInit: @@ -950,7 +962,7 @@ proc needsRecompile(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache of loading, loaded: # For loading: Assume no recompile is required. result = false - of outdated, storing: + of outdated, storing, stored: result = true proc moduleFromRodFile*(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache; diff --git a/compiler/ic/rodfiles.nim b/compiler/ic/rodfiles.nim index a518870f8..39a5b6b73 100644 --- a/compiler/ic/rodfiles.nim +++ b/compiler/ic/rodfiles.nim @@ -37,6 +37,7 @@ type methodsPerTypeSection enumToStringProcsSection typeInfoSection # required by the backend + backendFlagsSection aliveSymsSection # beware, this is stored in a `.alivesyms` file. RodFileError* = enum diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index 9ac76457c..e6f5267cd 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -455,11 +455,25 @@ proc getModule*(g: ModuleGraph; fileIdx: FileIndex): PSym = elif fileIdx.int32 < g.ifaces.len: result = g.ifaces[fileIdx.int32].module +proc moduleOpenForCodegen*(g: ModuleGraph; m: FileIndex): bool {.inline.} = + if g.config.symbolFiles == disabledSf: + result = true + else: + result = g.packed[m.int32].status notin {undefined, stored, loaded} + proc rememberEmittedTypeInfo*(g: ModuleGraph; m: FileIndex; ti: string) = #assert(not isCachedModule(g, m.int32)) if g.config.symbolFiles != disabledSf: #assert g.encoders[m.int32].isActive + assert g.packed[m.int32].status != stored g.packed[m.int32].fromDisk.emittedTypeInfo.add ti + #echo "added typeinfo ", m.int32, " ", ti, " suspicious ", not g.encoders[m.int32].isActive + +proc rememberFlag*(g: ModuleGraph; m: PSym; flag: ModuleBackendFlag) = + if g.config.symbolFiles != disabledSf: + #assert g.encoders[m.int32].isActive + assert g.packed[m.position].status != stored + g.packed[m.position].fromDisk.backendFlags.incl flag proc closeRodFile*(g: ModuleGraph; m: PSym) = if g.config.symbolFiles in {readOnlySf, v2Sf}: @@ -469,6 +483,8 @@ proc closeRodFile*(g: ModuleGraph; m: PSym) = let mint = m.position saveRodFile(toRodFile(g.config, AbsoluteFile toFullPath(g.config, FileIndex(mint))), g.encoders[mint], g.packed[mint].fromDisk) + g.packed[mint].status = stored + elif g.config.symbolFiles == stressTest: # debug code, but maybe a good idea for production? Could reduce the compiler's # memory consumption considerably at the cost of more loads from disk. diff --git a/koch.nim b/koch.nim index bf7fb1e62..6f1da8165 100644 --- a/koch.nim +++ b/koch.nim @@ -458,8 +458,8 @@ proc temp(args: string) = inc i let d = getAppDir() - var output = d / "compiler" / "nim".exe - var finalDest = d / "bin" / "nim_temp".exe + let output = d / "compiler" / "nim".exe + let finalDest = d / "bin" / "nim_temp".exe # 125 is the magic number to tell git bisect to skip the current commit. var (bootArgs, programArgs) = splitArgs(args) if "doc" notin programArgs and @@ -483,6 +483,22 @@ proc xtemp(cmd: string) = finally: copyExe(d / "bin" / "nim_backup".exe, d / "bin" / "nim".exe) +proc icTest(args: string) = + temp("") + let inp = os.parseCmdLine(args)[0] + let content = readFile(inp) + let nimExe = getAppDir() / "bin" / "nim_temp".exe + var i = 0 + for fragment in content.split("#!EDIT!#"): + let file = inp.replace(".nim", "_temp.nim") + writeFile(file, fragment) + var cmd = nimExe & " cpp --ic:on --listcmd " + if i == 0: + cmd.add "-f " + cmd.add quoteShell(file) + exec(cmd) + inc i + proc buildDrNim(args: string) = if not dirExists("dist/nimz3"): exec("git clone https://github.com/zevv/nimz3.git dist/nimz3") @@ -705,6 +721,7 @@ when isMainModule: of "fusion": let suffix = if latest: HeadHash else: FusionStableHash exec("nimble install -y fusion@$#" % suffix) + of "ic": icTest(op.cmdLineRest) else: showHelp() break of cmdEnd: break diff --git a/tests/ic/tgenerics.nim b/tests/ic/tgenerics.nim index bc5c05f4f..138799e85 100644 --- a/tests/ic/tgenerics.nim +++ b/tests/ic/tgenerics.nim @@ -1,6 +1,5 @@ discard """ output: "bar" - disabled: "true" """ import tables From bb3769975b7cbbe2f84c288d7f3363b6bad9ca0e Mon Sep 17 00:00:00 2001 From: Clyybber Date: Fri, 26 Mar 2021 13:05:51 +0100 Subject: [PATCH 1793/3768] Fix #17199 (#17348) * don't zero out in a move in the VM * Add testcases for #17199 * Update tests/arc/tarcmisc.nim Co-authored-by: Timothee Cour * Update tests/vm/tissues.nim Co-authored-by: Timothee Cour * Fix test failures * Fix test * Fix tests Co-authored-by: Andreas Rumpf Co-authored-by: Timothee Cour --- compiler/vmgen.nim | 7 ++++--- tests/arc/tarcmisc.nim | 32 ++++++++++++++++++++++++++++ tests/stdlib/tisolation.nim | 42 ++++++++++++++++++++++++------------- 3 files changed, 63 insertions(+), 18 deletions(-) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 7d7382d18..bd80df219 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1354,9 +1354,10 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = if dest < 0: dest = c.getTemp(arg.typ) gABC(c, arg, whichAsgnOpc(arg, requiresCopy=false), dest, a) # XXX use ldNullOpcode() here? - c.gABx(n, opcLdNull, a, c.genType(arg.typ)) - c.gABx(n, opcNodeToReg, a, a) - c.genAsgnPatch(arg, a) + # Don't zero out the arg for now #17199 + # c.gABx(n, opcLdNull, a, c.genType(arg.typ)) + # c.gABx(n, opcNodeToReg, a, a) + # c.genAsgnPatch(arg, a) c.freeTemp(a) of mNodeId: c.genUnaryABC(n, dest, opcNodeId) diff --git a/tests/arc/tarcmisc.nim b/tests/arc/tarcmisc.nim index 55803085f..82751f44c 100644 --- a/tests/arc/tarcmisc.nim +++ b/tests/arc/tarcmisc.nim @@ -390,3 +390,35 @@ proc newPixelBuffer(): PixelBuffer = discard newPixelBuffer() + +# bug #17199 + +proc passSeq(data: seq[string]) = + # used the system.& proc initially + let wat = data & "hello" + +proc test2 = + let name = @["hello", "world"] + passSeq(name) + doAssert name == @["hello", "world"] + +static: test2() # was buggy +test2() + +proc merge(x: sink seq[string], y: sink string): seq[string] = + newSeq(result, x.len + 1) + for i in 0..x.len-1: + result[i] = move(x[i]) + result[x.len] = move(y) + +proc passSeq2(data: seq[string]) = + # used the system.& proc initially + let wat = merge(data, "hello") + +proc test3 = + let name = @["hello", "world"] + passSeq2(name) + doAssert name == @["hello", "world"] + +static: test3() # was buggy +test3() diff --git a/tests/stdlib/tisolation.nim b/tests/stdlib/tisolation.nim index e43fd558a..c3857f483 100644 --- a/tests/stdlib/tisolation.nim +++ b/tests/stdlib/tisolation.nim @@ -7,7 +7,7 @@ import std/[isolation, json] -proc main() = +proc main(moveZeroesOut: static bool) = block: type Empty = ref object @@ -19,64 +19,76 @@ proc main() = block: # string literals var data = isolate("string") doAssert data.extract == "string" - doAssert data.extract == "" + if moveZeroesOut: + doAssert data.extract == "" block: # string literals var data = isolate("") doAssert data.extract == "" - doAssert data.extract == "" + if moveZeroesOut: + doAssert data.extract == "" block: var src = "string" var data = isolate(move src) doAssert data.extract == "string" - doAssert src.len == 0 + if moveZeroesOut: + doAssert src.len == 0 block: # int literals var data = isolate(1) doAssert data.extract == 1 - doAssert data.extract == 0 + if moveZeroesOut: + doAssert data.extract == 0 block: # float literals var data = isolate(1.6) doAssert data.extract == 1.6 - doAssert data.extract == 0.0 + if moveZeroesOut: + doAssert data.extract == 0.0 block: var data = isolate(@["1", "2"]) doAssert data.extract == @["1", "2"] - doAssert data.extract == @[] + if moveZeroesOut: + doAssert data.extract == @[] block: var data = isolate(@["1", "2", "3", "4", "5"]) doAssert data.extract == @["1", "2", "3", "4", "5"] - doAssert data.extract == @[] + if moveZeroesOut: + doAssert data.extract == @[] block: var data = isolate(@["", ""]) doAssert data.extract == @["", ""] - doAssert data.extract == @[] + if moveZeroesOut: + doAssert data.extract == @[] block: var src = @["1", "2"] var data = isolate(move src) doAssert data.extract == @["1", "2"] - doAssert src.len == 0 + if moveZeroesOut: + doAssert src.len == 0 block: var data = isolate(@[1, 2]) doAssert data.extract == @[1, 2] - doAssert data.extract == @[] + if moveZeroesOut: + doAssert data.extract == @[] block: var data = isolate(["1", "2"]) doAssert data.extract == ["1", "2"] - doAssert data.extract == ["", ""] + if moveZeroesOut: + doAssert data.extract == ["", ""] block: var data = isolate([1, 2]) doAssert data.extract == [1, 2] - doAssert data.extract == [0, 0] + if moveZeroesOut: + doAssert data.extract == [0, 0] block: type @@ -119,5 +131,5 @@ proc main() = doAssert $x == """@[(value: "1234")]""" -static: main() -main() +static: main(moveZeroesOut = false) +main(moveZeroesOut = true) From add771b051128b5eef09b4d77015fbeacf994700 Mon Sep 17 00:00:00 2001 From: narimiran Date: Fri, 26 Mar 2021 13:41:43 +0100 Subject: [PATCH 1794/3768] disable package 'chronicles' until 'chronos' is fixed --- testament/important_packages.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 66a2fa09e..eb0196f70 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -45,7 +45,7 @@ pkg "c2nim", "nim c testsuite/tester.nim" pkg "cascade" pkg "cello" pkg "chroma" -pkg "chronicles", "nim c -o:chr -r chronicles.nim" +# pkg "chronicles", "nim c -o:chr -r chronicles.nim" # when not defined(osx): # testdatagram.nim(560, 54): Check failed # pkg "chronos", "nim c -r -d:release tests/testall" # pending https://github.com/nim-lang/Nim/issues/17130 From 3e03f6733548fecb328214e0c8632fa4916992ed Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 26 Mar 2021 16:27:55 +0100 Subject: [PATCH 1795/3768] cleaned up the internal documentation (#17524) --- doc/intern.rst | 605 +++++++++------------------------------- lib/system/cellsets.nim | 35 ++- lib/system/gc.nim | 47 ++++ 3 files changed, 220 insertions(+), 467 deletions(-) diff --git a/doc/intern.rst b/doc/intern.rst index 2456b25fd..1e18d975c 100644 --- a/doc/intern.rst +++ b/doc/intern.rst @@ -32,28 +32,29 @@ Path Purpose `doc` the documentation; it is a bunch of reStructuredText files `lib` the Nim library -`web` website of Nim; generated by `nimweb` - from the `*.txt` and `*.nimf` files ============ =================================================== Bootstrapping the compiler ========================== +**Note**: Add ``.`` to your PATH so that `koch` can be used without the `./`. + Compiling the compiler is a simple matter of running:: nim c koch.nim - ./koch boot + koch boot -d:release -For a release version use:: +For a debug version use:: nim c koch.nim - ./koch boot -d:release + koch boot + And for a debug version compatible with GDB:: nim c koch.nim - ./koch boot --debuginfo --linedir:on + koch boot --debuginfo --linedir:on The `koch` program is Nim's maintenance script. It is a replacement for make and shell scripting with the advantage that it is much more portable. @@ -61,12 +62,105 @@ More information about its options can be found in the `koch `_ documentation. +Developing the compiler +======================= + +To create a new compiler for each run, use `koch temp`:: + + koch temp c test.nim + +`koch temp` creates a debug build of the compiler, which is useful +to create stacktraces for compiler debugging. + +You can of course use GDB or Visual Studio to debug the +compiler (via `--debuginfo --lineDir:on`). However, there +are also lots of procs that aid in debugging: + + +.. code-block:: nim + + # dealing with PNode: + echo renderTree(someNode) + debug(someNode) # some JSON representation + + # dealing with PType: + echo typeToString(someType) + debug(someType) + + # dealing with PSym: + echo symbol.name.s + debug(symbol) + + # pretty prints the Nim ast, but annotates symbol IDs: + echo renderTree(someNode, {renderIds}) + if `??`(conf, n.info, "temp.nim"): + # only output when it comes from "temp.nim" + echo renderTree(n) + if `??`(conf, n.info, "temp.nim"): + # why does it process temp.nim here? + writeStackTrace() + +These procs may not already be imported by the module you're editing. +You can import them directly for debugging: + +.. code-block:: nim + from astalgo import debug + from types import typeToString + from renderer import renderTree + from msgs import `??` + + +The compiler's architecture +=========================== + +Nim uses the classic compiler architecture: A lexer/scanner feds tokens to a +parser. The parser builds a syntax tree that is used by the code generators. +This syntax tree is the interface between the parser and the code generator. +It is essential to understand most of the compiler's code. + +Semantic analysis is separated from parsing. + +.. include:: filelist.txt + + +The syntax tree +--------------- +The syntax tree consists of nodes which may have an arbitrary number of +children. Types and symbols are represented by other nodes, because they +may contain cycles. The AST changes its shape after semantic checking. This +is needed to make life easier for the code generators. See the "ast" module +for the type definitions. The `macros `_ module contains many +examples how the AST represents each syntactic structure. + + +Bisecting for regressions +========================= + +`koch temp` returns 125 as the exit code in case the compiler +compilation fails. This exit code tells `git bisect` to skip the +current commit.:: + + git bisect start bad-commit good-commit + git bisect run ./koch temp -r c test-source.nim + +You can also bisect using custom options to build the compiler, for example if +you don't need a debug version of the compiler (which runs slower), you can replace +`./koch temp` by explicit compilation command, see `Rebuilding the compiler`_. + + +Runtimes +======== + +Nim has two different runtimes, the "old runtime" and the "new runtime". The old +runtime supports the old GCs (markAndSweep, refc, Boehm), the new runtime supports +ARC/ORC. The new runtime is active `when defined(nimV2)`. + + Coding Guidelines ================= -* Use CamelCase, not underscored_identifiers. -* Indent with two spaces. -* Max line length is 80 characters. +* We follow Nim's official style guide, see ``_. +* Max line length is 100 characters. * Provide spaces around binary operators if that enhances readability. * Use a space after a colon, but not before it. * [deprecated] Start types with a capital `T`, unless they are @@ -86,9 +180,9 @@ POSIX-compliant systems on conventional hardware are usually pretty easy to port: Add the platform to `platform` (if it is not already listed there), check that the OS, System modules work and recompile Nim. -The only case where things aren't as easy is when the garbage -collector needs some assembler tweaking to work. The standard -version of the GC uses C's `setjmp` function to store all registers +The only case where things aren't as easy is when old runtime's garbage +collectors need some assembler tweaking to work. The default +implementation uses C's `setjmp` function to store all registers on the hardware stack. It may be necessary that the new platform needs to replace this generic code by some assembler code. @@ -96,136 +190,33 @@ replace this generic code by some assembler code. Runtime type information ======================== +**Note**: This section describes the "old runtime". + *Runtime type information* (RTTI) is needed for several aspects of the Nim programming language: Garbage collection - The most important reason for RTTI. Generating - traversal procedures produces bigger code and is likely to be slower on - modern hardware as dynamic procedure binding is hard to predict. + The old GCs use the RTTI for traversing abitrary Nim types, but usually + only the `marker` field which contains a proc that does the traversal. Complex assignments Sequences and strings are implemented as pointers to resizeable buffers, but Nim requires copying for - assignments. Apart from RTTI the compiler could generate copy procedures - for any type that needs one. However, this would make the code bigger and - the RTTI is likely already there for the GC. + assignments. Apart from RTTI the compiler also generates copy procedures + as a specialization. We already know the type information as a graph in the compiler. Thus we need to serialize this graph as RTTI for C code generation. Look at the file `lib/system/hti.nim` for more information. -Rebuilding the compiler + +Magics and compilerProcs ======================== -After an initial build via `sh build_all.sh` on posix or `build_all.bat` on windows, -you can rebuild the compiler as follows: - -* `nim c koch` if you need to rebuild koch -* `./koch boot -d:release` this ensures the compiler can rebuild itself - (use `koch` instead of `./koch` on windows), which builds the compiler 3 times. - -A faster approach if you don't need to run the full bootstrapping implied by `koch boot`, -is the following: - -* `pathto/nim c --lib:lib -d:release -o:bin/nim_temp compiler/nim.nim` - -Where `pathto/nim` is any nim binary sufficiently recent (e.g. `bin/nim_cources` -built during bootstrap or `$HOME/.nimble/bin/nim` installed by `choosenim 1.2.0`) - -You can pass any additional options such as `-d:leanCompiler` if you don't need -certain features or `-d:debug --stacktrace:on --excessiveStackTrace --stackTraceMsgs` -for debugging the compiler. See also `Debugging the compiler`_. - -Debugging the compiler -====================== - -You can of course use GDB or Visual Studio to debug the -compiler (via `--debuginfo --lineDir:on`). However, there -are also lots of procs that aid in debugging: - - -.. code-block:: nim - # pretty prints the Nim AST - echo renderTree(someNode) - # outputs some JSON representation - debug(someNode) - # pretty prints some type - echo typeToString(someType) - debug(someType) - echo symbol.name.s - debug(symbol) - # pretty prints the Nim ast, but annotates symbol IDs: - echo renderTree(someNode, {renderIds}) - if `??`(conf, n.info, "temp.nim"): - # only output when it comes from "temp.nim" - echo renderTree(n) - if `??`(conf, n.info, "temp.nim"): - # why does it process temp.nim here? - writeStackTrace() - -These procs may not be imported by a module. You can import them directly for debugging: - -.. code-block:: nim - from astalgo import debug - from types import typeToString - from renderer import renderTree - from msgs import `??` - -To create a new compiler for each run, use `koch temp`:: - - ./koch temp c /tmp/test.nim - -`koch temp` creates a debug build of the compiler, which is useful -to create stacktraces for compiler debugging. See also -`Rebuilding the compiler`_ if you need more control. - -Bisecting for regressions -========================= - -`koch temp` returns 125 as the exit code in case the compiler -compilation fails. This exit code tells `git bisect` to skip the -current commit.:: - - git bisect start bad-commit good-commit - git bisect run ./koch temp -r c test-source.nim - -You can also bisect using custom options to build the compiler, for example if -you don't need a debug version of the compiler (which runs slower), you can replace -`./koch temp` by explicit compilation command, see `Rebuilding the compiler`_. - -The compiler's architecture -=========================== - -Nim uses the classic compiler architecture: A lexer/scanner feds tokens to a -parser. The parser builds a syntax tree that is used by the code generator. -This syntax tree is the interface between the parser and the code generator. -It is essential to understand most of the compiler's code. - -In order to compile Nim correctly, type-checking has to be separated from -parsing. Otherwise generics cannot work. - -.. include:: filelist.txt - - -The syntax tree ---------------- -The syntax tree consists of nodes which may have an arbitrary number of -children. Types and symbols are represented by other nodes, because they -may contain cycles. The AST changes its shape after semantic checking. This -is needed to make life easier for the code generators. See the "ast" module -for the type definitions. The `macros `_ module contains many -examples how the AST represents each syntactic structure. - - -How the RTL is compiled -======================= - The `system` module contains the part of the RTL which needs support by -compiler magic (and the stuff that needs to be in it because the spec -says so). The C code generator generates the C code for it, just like any other +compiler magic. The C code generator generates the C code for it, just like any other module. However, calls to some procedures like `addInt` are inserted by -the CCG. Therefore the module `magicsys` contains a table (`compilerprocs`) +the generator. Therefore there is a table (`compilerprocs`) with all symbols that are marked as `compilerproc`. `compilerprocs` are needed by the code generator. A `magic` proc is not the same as a `compilerproc`: A `magic` is a proc that needs compiler magic for its @@ -233,325 +224,6 @@ semantic checking, a `compilerproc` is a proc that is used by the code generator. -Compilation cache -================= - -The implementation of the compilation cache is tricky: There are lots -of issues to be solved for the front- and backend. - - -General approach: AST replay ----------------------------- - -We store a module's AST of a successful semantic check in a SQLite -database. There are plenty of features that require a sub sequence -to be re-applied, for example: - -.. code-block:: nim - {.compile: "foo.c".} # even if the module is loaded from the DB, - # "foo.c" needs to be compiled/linked. - -The solution is to **re-play** the module's top level statements. -This solves the problem without having to special case the logic -that fills the internal seqs which are affected by the pragmas. - -In fact, this describes how the AST should be stored in the database, -as a "shallow" tree. Let's assume we compile module `m` with the -following contents: - -.. code-block:: nim - import std/strutils - - var x*: int = 90 - {.compile: "foo.c".} - proc p = echo "p" - proc q = echo "q" - static: - echo "static" - -Conceptually this is the AST we store for the module: - -.. code-block:: nim - import std/strutils - - var x* - {.compile: "foo.c".} - proc p - proc q - static: - echo "static" - -The symbol's `ast` field is loaded lazily, on demand. This is where most -savings come from, only the shallow outer AST is reconstructed immediately. - -It is also important that the replay involves the `import` statement so -that dependencies are resolved properly. - - -Shared global compiletime state -------------------------------- - -Nim allows `.global, compiletime` variables that can be filled by macro -invocations across different modules. This feature breaks modularity in a -severe way. Plenty of different solutions have been proposed: - -- Restrict the types of global compiletime variables to `Set[T]` or - similar unordered, only-growable collections so that we can track - the module's write effects to these variables and reapply the changes - in a different order. -- In every module compilation, reset the variable to its default value. -- Provide a restrictive API that can load/save the compiletime state to - a file. - -(These solutions are not mutually exclusive.) - -Since we adopt the "replay the top level statements" idea, the natural -solution to this problem is to emit pseudo top level statements that -reflect the mutations done to the global variable. However, this is -MUCH harder than it sounds, for example `squeaknim` uses this -snippet: - -.. code-block:: nim - apicall.add(") module: '" & dllName & "'>\C" & - "\t^self externalCallFailed\C!\C\C") - stCode.add(st & "\C\t\"Generated by NimSqueak\"\C\t" & apicall) - -We can "replay" `stCode.add` only if the values of `st` -and `apicall` are known. And even then a hash table's `add` with its -hashing mechanism is too hard to replay. - -In practice, things are worse still, consider `someGlobal[i][j].add arg`. -We only know the root is `someGlobal` but the concrete path to the data -is unknown as is the value that is added. We could compute a "diff" between -the global states and use that to compute a symbol patchset, but this is -quite some work, expensive to do at runtime (it would need to run after -every module has been compiled) and would also break for hash tables. - -We need an API that hides the complex aliasing problems by not relying -on Nim's global variables. The obvious solution is to use string keys -instead of global variables: - -.. code-block:: nim - - proc cachePut*(key: string; value: string) - proc cacheGet*(key: string): string - -However, the values being strings/json is quite problematic: Many -lookup tables that are built at compiletime embed *proc vars* and -types which have no obvious string representation... Seems like -AST diffing is still the best idea as it will not require to use -an alien API and works with some existing Nimble packages, at least. - -On the other hand, in Nim's future I would like to replace the VM -by native code. A diff algorithm wouldn't work for that. -Instead the native code would work with an API like `put`, `get`: - -.. code-block:: nim - - proc cachePut*(key: string; value: NimNode) - proc cacheGet*(key: string): NimNode - -The API should embrace the AST diffing notion: See the -module `macrocache` for the final details. - - - -Methods and type converters ---------------------------- - -In the following -sections *global* means *shared between modules* or *property of the whole -program*. - -Nim contains language features that are *global*. The best example for that -are multi methods: Introducing a new method with the same name and some -compatible object parameter means that the method's dispatcher needs to take -the new method into account. So the dispatching logic is only completely known -after the whole program has been translated! - -Other features that are *implicitly* triggered cause problems for modularity -too. Type converters fall into this category: - -.. code-block:: nim - # module A - converter toBool(x: int): bool = - result = x != 0 - -.. code-block:: nim - # module B - import A - - if 1: - echo "ugly, but should work" - -If in the above example module `B` is re-compiled, but `A` is not then -`B` needs to be aware of `toBool` even though `toBool` is not referenced -in `B` *explicitly*. - -Both the multi method and the type converter problems are solved by the -AST replay implementation. - - -Generics -~~~~~~~~ - -We cache generic instantiations and need to ensure this caching works -well with the incremental compilation feature. Since the cache is -attached to the `PSym` datastructure, it should work without any -special logic. - - -Backend issues --------------- - -- Init procs must not be "forgotten" to be called. -- Files must not be "forgotten" to be linked. -- Method dispatchers are global. -- DLL loading via `dlsym` is global. -- Emulated thread vars are global. - -However the biggest problem is that dead code elimination breaks modularity! -To see why, consider this scenario: The module `G` (for example the huge -Gtk2 module...) is compiled with dead code elimination turned on. So none -of `G`'s procs is generated at all. - -Then module `B` is compiled that requires `G.P1`. Ok, no problem, -`G.P1` is loaded from the symbol file and `G.c` now contains `G.P1`. - -Then module `A` (that depends on `B` and `G`) is compiled and `B` -and `G` are left unchanged. `A` requires `G.P2`. - -So now `G.c` MUST contain both `P1` and `P2`, but we haven't even -loaded `P1` from the symbol file, nor do we want to because we then quickly -would restore large parts of the whole program. - - -Solution -~~~~~~~~ - -The backend must have some logic so that if the currently processed module -is from the compilation cache, the `ast` field is not accessed. Instead -the generated C(++) for the symbol's body needs to be cached too and -inserted back into the produced C file. This approach seems to deal with -all the outlined problems above. - - -Debugging Nim's memory management -================================= - -The following paragraphs are mostly a reminder for myself. Things to keep -in mind: - -* If an assertion in Nim's memory manager or GC fails, the stack trace - keeps allocating memory! Thus a stack overflow may happen, hiding the - real issue. -* What seem to be C code generation problems is often a bug resulting from - not producing prototypes, so that some types default to `cint`. Testing - without the `-w` option helps! - - -The Garbage Collector -===================== - -Introduction ------------- - -I use the term *cell* here to refer to everything that is traced -(sequences, refs, strings). -This section describes how the GC works. - -The basic algorithm is *Deferrent Reference Counting* with cycle detection. -References on the stack are not counted for better performance and easier C -code generation. - -Each cell has a header consisting of a RC and a pointer to its type -descriptor. However the program does not know about these, so they are placed at -negative offsets. In the GC code the type `PCell` denotes a pointer -decremented by the right offset, so that the header can be accessed easily. It -is extremely important that `pointer` is not confused with a `PCell` -as this would lead to a memory corruption. - - -The CellSet data structure --------------------------- - -The GC depends on an extremely efficient datastructure for storing a -set of pointers - this is called a `TCellSet` in the source code. -Inserting, deleting and searching are done in constant time. However, -modifying a `TCellSet` during traversal leads to undefined behaviour. - -.. code-block:: Nim - type - TCellSet # hidden - - proc cellSetInit(s: var TCellSet) # initialize a new set - proc cellSetDeinit(s: var TCellSet) # empty the set and free its memory - proc incl(s: var TCellSet, elem: PCell) # include an element - proc excl(s: var TCellSet, elem: PCell) # exclude an element - - proc `in`(elem: PCell, s: TCellSet): bool # tests membership - - iterator elements(s: TCellSet): (elem: PCell) - - -All the operations have to perform efficiently. Because a Cellset can -become huge a hash table alone is not suitable for this. - -We use a mixture of bitset and hash table for this. The hash table maps *pages* -to a page descriptor. The page descriptor contains a bit for any possible cell -address within this page. So including a cell is done as follows: - -- Find the page descriptor for the page the cell belongs to. -- Set the appropriate bit in the page descriptor indicating that the - cell points to the start of a memory block. - -Removing a cell is analogous - the bit has to be set to zero. -Single page descriptors are never deleted from the hash table. This is not -needed as the data structures needs to be rebuilt periodically anyway. - -Complete traversal is done in this way:: - - for each page descriptor d: - for each bit in d: - if bit == 1: - traverse the pointer belonging to this bit - - -Further complications ---------------------- - -In Nim the compiler cannot always know if a reference -is stored on the stack or not. This is caused by var parameters. -Consider this example: - -.. code-block:: Nim - proc setRef(r: var ref TNode) = - new(r) - - proc usage = - var - r: ref TNode - setRef(r) # here we should not update the reference counts, because - # r is on the stack - setRef(r.left) # here we should update the refcounts! - -We have to decide at runtime whether the reference is on the stack or not. -The generated code looks roughly like this: - -.. code-block:: C - void setref(TNode** ref) { - unsureAsgnRef(ref, newObj(TNode_TI, sizeof(TNode))) - } - void usage(void) { - setRef(&r) - setRef(&r->left) - } - -Note that for systems with a continuous stack (which most systems have) -the check whether the ref is on the stack is very cheap (only two -comparisons). - - Code generation for closures ============================ @@ -598,14 +270,14 @@ This should produce roughly this code: .. code-block:: nim type - PEnv = ref object + Env = ref object x: int # data - proc anon(y: int, c: PEnv): int = + proc anon(y: int, c: Env): int = return y + c.x proc add(x: int): tuple[prc, data] = - var env: PEnv + var env: Env new env env.x = x result = (anon, env) @@ -630,25 +302,25 @@ This should produce roughly this code: .. code-block:: nim type - PEnvX = ref object + EnvX = ref object x: int # data - PEnvY = ref object + EnvY = ref object y: int - ex: PEnvX + ex: EnvX - proc lambdaZ(z: int, ey: PEnvY): int = + proc lambdaZ(z: int, ey: EnvY): int = return ey.ex.x + ey.y + z - proc lambdaY(y: int, ex: PEnvX): tuple[prc, data: PEnvY] = - var ey: PEnvY + proc lambdaY(y: int, ex: EnvX): tuple[prc, data: EnvY] = + var ey: EnvY new ey ey.y = y ey.ex = ex result = (lambdaZ, ey) - proc add(x: int): tuple[prc, data: PEnvX] = - var ex: PEnvX + proc add(x: int): tuple[prc, data: EnvX] = + var ex: EnvX ex.x = x result = (labmdaY, ex) @@ -663,17 +335,11 @@ More useful is escape analysis and stack allocation of the environment, however. -Alternative ------------ - -Process the closure of all inner procs in one pass and accumulate the -environments. This is however not always possible. - - Accumulator ----------- .. code-block:: nim + proc getAccumulator(start: int): proc (): int {.closure} = var i = start return lambda: int = @@ -708,20 +374,26 @@ backend somehow. We deal with this by modifying `s.ast[paramPos]` to contain the formal hidden parameter, but not `s.typ`! -Integer literals: ------------------ +Notes on type and AST representation +==================================== + +To be expanded. + + +Integer literals +---------------- In Nim, there is a redundant way to specify the type of an integer literal. First of all, it should be unsurprising that every node has a node kind. The node of an integer literal can be any of the -following values: +following values:: nkIntLit, nkInt8Lit, nkInt16Lit, nkInt32Lit, nkInt64Lit, nkUIntLit, nkUInt8Lit, nkUInt16Lit, nkUInt32Lit, nkUInt64Lit On top of that, there is also the `typ` field for the type. It the kind of the `typ` field can be one of the following ones, and it -should be matching the literal kind: +should be matching the literal kind:: tyInt, tyInt8, tyInt16, tyInt32, tyInt64, tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64 @@ -777,6 +449,7 @@ pointing back to the integer literal node in the ast containing the integer value. These are the properties that hold true for integer literal types. +:: n.kind == nkIntLit n.typ.kind == tyInt n.typ.n == n diff --git a/lib/system/cellsets.nim b/lib/system/cellsets.nim index 779f1a91f..a0f1fabf9 100644 --- a/lib/system/cellsets.nim +++ b/lib/system/cellsets.nim @@ -7,7 +7,40 @@ # distribution, for details about the copyright. # -# Efficient set of pointers for the GC (and repr) + +#[ + +Efficient set of pointers for the GC (and repr) +----------------------------------------------- + +The GC depends on an extremely efficient datastructure for storing a +set of pointers - this is called a `CellSet` in the source code. +Inserting, deleting and searching are done in constant time. However, +modifying a `CellSet` during traversal leads to undefined behaviour. + +All operations on a CellSet have to perform efficiently. Because a Cellset can +become huge a hash table alone is not suitable for this. + +We use a mixture of bitset and hash table for this. The hash table maps *pages* +to a page descriptor. The page descriptor contains a bit for any possible cell +address within this page. So including a cell is done as follows: + +- Find the page descriptor for the page the cell belongs to. +- Set the appropriate bit in the page descriptor indicating that the + cell points to the start of a memory block. + +Removing a cell is analogous - the bit has to be set to zero. +Single page descriptors are never deleted from the hash table. This is not +needed as the data structures needs to be rebuilt periodically anyway. + +Complete traversal is done in this way:: + + for each page descriptor d: + for each bit in d: + if bit == 1: + traverse the pointer belonging to this bit + +]# when defined(gcOrc) or defined(gcArc): type diff --git a/lib/system/gc.nim b/lib/system/gc.nim index 1f7164266..ae29f3466 100644 --- a/lib/system/gc.nim +++ b/lib/system/gc.nim @@ -12,6 +12,53 @@ # Refcounting + Mark&Sweep. Complex algorithms avoided. # Been there, done that, didn't work. +#[ + +A *cell* is anything that is traced by the GC +(sequences, refs, strings, closures). + +The basic algorithm is *Deferrent Reference Counting* with cycle detection. +References on the stack are not counted for better performance and easier C +code generation. + +Each cell has a header consisting of a RC and a pointer to its type +descriptor. However the program does not know about these, so they are placed at +negative offsets. In the GC code the type `PCell` denotes a pointer +decremented by the right offset, so that the header can be accessed easily. It +is extremely important that `pointer` is not confused with a `PCell`. + +In Nim the compiler cannot always know if a reference +is stored on the stack or not. This is caused by var parameters. +Consider this example: + +.. code-block:: Nim + proc setRef(r: var ref TNode) = + new(r) + + proc usage = + var + r: ref TNode + setRef(r) # here we should not update the reference counts, because + # r is on the stack + setRef(r.left) # here we should update the refcounts! + +We have to decide at runtime whether the reference is on the stack or not. +The generated code looks roughly like this: + +.. code-block:: C + void setref(TNode** ref) { + unsureAsgnRef(ref, newObj(TNode_TI, sizeof(TNode))) + } + void usage(void) { + setRef(&r) + setRef(&r->left) + } + +Note that for systems with a continuous stack (which most systems have) +the check whether the ref is on the stack is very cheap (only two +comparisons). +]# + {.push profiler:off.} const From b1b767ed0db19b337ab41fab9241227d23aaae38 Mon Sep 17 00:00:00 2001 From: flywind Date: Sat, 27 Mar 2021 16:51:57 +0800 Subject: [PATCH 1796/3768] close #17157 add testcase (#17531) --- tests/proc/t17157.nim | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 tests/proc/t17157.nim diff --git a/tests/proc/t17157.nim b/tests/proc/t17157.nim new file mode 100644 index 000000000..020e93fce --- /dev/null +++ b/tests/proc/t17157.nim @@ -0,0 +1,6 @@ +discard """ + errormsg: "'untyped' is only allowed in templates and macros or magic procs" +""" + +template something(op: proc (v: untyped): void): void = + discard From 8ed6de4fe6517dc4db38ab5dea898c0016d1c08a Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 27 Mar 2021 02:22:42 -0700 Subject: [PATCH 1797/3768] fix flaky tnetconnect which sometimes failed with: No route to host [OSError] (#17526) --- tests/stdlib/tnetconnect.nim | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/stdlib/tnetconnect.nim b/tests/stdlib/tnetconnect.nim index b74545710..3a80df19f 100644 --- a/tests/stdlib/tnetconnect.nim +++ b/tests/stdlib/tnetconnect.nim @@ -17,10 +17,11 @@ proc test() = wrapSocket(ctx, socket) # trying 2 sites makes it more resilent: refs #17458 this could give: - # Error: unhandled exception: Call to 'connect' timed out. [TimeoutError] + # * Call to 'connect' timed out. [TimeoutError] + # * No route to host [OSError] try: fn("www.nim-lang.org") - except TimeoutError: + except TimeoutError, OSError: fn("www.google.com") test() From a65189a739c59fcc0d7b3f8fdcb2cf8bed432f68 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 27 Mar 2021 02:28:11 -0700 Subject: [PATCH 1798/3768] nnkArglist => nnkArgList + special case stylecheck:error (#17529) * nnkArglist => nnkArgList * special case stylecheck:error --- compiler/linter.nim | 8 +++++--- compiler/msgs.nim | 6 +++--- doc/astspec.txt | 2 +- doc/manual_experimental.rst | 2 +- lib/core/macros.nim | 2 +- lib/pure/htmlgen.nim | 2 +- lib/std/private/underscored_calls.nim | 2 +- tests/astspec/tastspec.nim | 2 +- tests/casestmt/tincompletecaseobject.nim | 4 ++-- tests/macros/tmacros_issues.nim | 2 +- 10 files changed, 17 insertions(+), 15 deletions(-) diff --git a/compiler/linter.nim b/compiler/linter.nim index 1ae1fb097..9af4f468e 100644 --- a/compiler/linter.nim +++ b/compiler/linter.nim @@ -125,9 +125,11 @@ proc styleCheckUse*(conf: ConfigRef; info: TLineInfo; s: PSym) = return let newName = s.name.s - let oldName = differs(conf, info, newName) - if oldName.len > 0: - lintReport(conf, info, newName, oldName) + let badName = differs(conf, info, newName) + if badName.len > 0: + # special rules for historical reasons + let forceHint = badName == "nnkArgList" and newName == "nnkArglist" or badName == "nnkArglist" and newName == "nnkArgList" + lintReport(conf, info, newName, badName, forceHint = forceHint) proc checkPragmaUse*(conf: ConfigRef; info: TLineInfo; w: TSpecialWord; pragmaName: string) = let wanted = $w diff --git a/compiler/msgs.nim b/compiler/msgs.nim index bbe40507f..24855ae18 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -612,9 +612,9 @@ template internalAssert*(conf: ConfigRef, e: bool) = let arg = info2.toFileLineCol internalErrorImpl(conf, unknownLineInfo, arg, info2) -template lintReport*(conf: ConfigRef; info: TLineInfo, beau, got: string) = - let m = "'$2' should be: '$1'" % [beau, got] - let msg = if optStyleError in conf.globalOptions: errGenerated else: hintName +template lintReport*(conf: ConfigRef; info: TLineInfo, beau, got: string, forceHint = false) = + let m = "'$1' should be: '$2'" % [got, beau] + let msg = if optStyleError in conf.globalOptions and not forceHint: errGenerated else: hintName liMessage(conf, info, msg, m, doNothing, instLoc()) proc quotedFilename*(conf: ConfigRef; i: TLineInfo): Rope = diff --git a/doc/astspec.txt b/doc/astspec.txt index c49f7bcc2..b4b8b34b5 100644 --- a/doc/astspec.txt +++ b/doc/astspec.txt @@ -1135,7 +1135,7 @@ AST: .. code-block:: nim # ... nnkTypeClassTy( # note this isn't nnkConceptTy! - nnkArglist( + nnkArgList( # ... idents for x, y, z ) # ... diff --git a/doc/manual_experimental.rst b/doc/manual_experimental.rst index cf2e0c247..fc46a2a14 100644 --- a/doc/manual_experimental.rst +++ b/doc/manual_experimental.rst @@ -1283,7 +1283,7 @@ all the arguments, but also the matched operators in reverse polish notation: echo x + y * z - x This passes the expression `x + y * z - x` to the `optM` macro as -an `nnkArglist` node containing:: +an `nnkArgList` node containing:: Arglist Sym "x" diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 5a556fc8d..8d6258e80 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -78,7 +78,7 @@ type nnkSharedTy, # 'shared T' nnkEnumTy, nnkEnumFieldDef, - nnkArglist, nnkPattern + nnkArgList, nnkPattern nnkHiddenTryStmt, nnkClosure, nnkGotoState, diff --git a/lib/pure/htmlgen.nim b/lib/pure/htmlgen.nim index 163f9303b..89eb24bb9 100644 --- a/lib/pure/htmlgen.nim +++ b/lib/pure/htmlgen.nim @@ -322,7 +322,7 @@ macro html*(e: varargs[untyped]): untyped = macro hr*(): untyped = ## Generates the HTML `hr` element. - result = xmlCheckedTag(newNimNode(nnkArglist), "hr", commonAttr, "", true) + result = xmlCheckedTag(newNimNode(nnkArgList), "hr", commonAttr, "", true) macro i*(e: varargs[untyped]): untyped = ## Generates the HTML `i` element. diff --git a/lib/std/private/underscored_calls.nim b/lib/std/private/underscored_calls.nim index 6d0a99ab5..f0bcbcc74 100644 --- a/lib/std/private/underscored_calls.nim +++ b/lib/std/private/underscored_calls.nim @@ -39,7 +39,7 @@ proc underscoredCall(n, arg0: NimNode): NimNode = result.add arg0 proc underscoredCalls*(result, calls, arg0: NimNode) = - expectKind calls, {nnkArglist, nnkStmtList, nnkStmtListExpr} + expectKind calls, {nnkArgList, nnkStmtList, nnkStmtListExpr} for call in calls: if call.kind in {nnkStmtList, nnkStmtListExpr}: diff --git a/tests/astspec/tastspec.nim b/tests/astspec/tastspec.nim index 3413d32f3..e2cfed277 100644 --- a/tests/astspec/tastspec.nim +++ b/tests/astspec/tastspec.nim @@ -929,7 +929,7 @@ static: (x & y & z) is string ast.peelOff({nnkStmtList, nnkTypeSection}).matchAst: - of nnkTypeDef(_, _, nnkTypeClassTy(nnkArglist, _, _, nnkStmtList)): + of nnkTypeDef(_, _, nnkTypeClassTy(nnkArgList, _, _, nnkStmtList)): # note this isn't nnkConceptTy! echo "ok" diff --git a/tests/casestmt/tincompletecaseobject.nim b/tests/casestmt/tincompletecaseobject.nim index 909ee4e1c..aa5deda7a 100644 --- a/tests/casestmt/tincompletecaseobject.nim +++ b/tests/casestmt/tincompletecaseobject.nim @@ -1,6 +1,6 @@ discard """ errormsg: ''' -not all cases are covered; missing: {nnkComesFrom, nnkDotCall, nnkHiddenCallConv, nnkVarTuple, nnkCurlyExpr, nnkRange, nnkCheckedFieldExpr, nnkDerefExpr, nnkElifExpr, nnkElseExpr, nnkLambda, nnkDo, nnkBind, nnkClosedSymChoice, nnkHiddenSubConv, nnkConv, nnkStaticExpr, nnkAddr, nnkHiddenAddr, nnkHiddenDeref, nnkObjDownConv, nnkObjUpConv, nnkChckRangeF, nnkChckRange64, nnkChckRange, nnkStringToCString, nnkCStringToString, nnkFastAsgn, nnkGenericParams, nnkFormalParams, nnkOfInherit, nnkImportAs, nnkConverterDef, nnkMacroDef, nnkTemplateDef, nnkIteratorDef, nnkOfBranch, nnkElifBranch, nnkExceptBranch, nnkElse, nnkAsmStmt, nnkTypeDef, nnkFinally, nnkContinueStmt, nnkImportStmt, nnkImportExceptStmt, nnkExportStmt, nnkExportExceptStmt, nnkFromStmt, nnkIncludeStmt, nnkUsingStmt, nnkBlockExpr, nnkStmtListType, nnkBlockType, nnkWith, nnkWithout, nnkTypeOfExpr, nnkObjectTy, nnkTupleTy, nnkTupleClassTy, nnkTypeClassTy, nnkStaticTy, nnkRecList, nnkRecCase, nnkRecWhen, nnkVarTy, nnkConstTy, nnkMutableTy, nnkDistinctTy, nnkProcTy, nnkIteratorTy, nnkSharedTy, nnkEnumTy, nnkEnumFieldDef, nnkArglist, nnkPattern, nnkReturnToken, nnkClosure, nnkGotoState, nnkState, nnkBreakState, nnkFuncDef, nnkTupleConstr} +not all cases are covered; missing: {nnkComesFrom, nnkDotCall, nnkHiddenCallConv, nnkVarTuple, nnkCurlyExpr, nnkRange, nnkCheckedFieldExpr, nnkDerefExpr, nnkElifExpr, nnkElseExpr, nnkLambda, nnkDo, nnkBind, nnkClosedSymChoice, nnkHiddenSubConv, nnkConv, nnkStaticExpr, nnkAddr, nnkHiddenAddr, nnkHiddenDeref, nnkObjDownConv, nnkObjUpConv, nnkChckRangeF, nnkChckRange64, nnkChckRange, nnkStringToCString, nnkCStringToString, nnkFastAsgn, nnkGenericParams, nnkFormalParams, nnkOfInherit, nnkImportAs, nnkConverterDef, nnkMacroDef, nnkTemplateDef, nnkIteratorDef, nnkOfBranch, nnkElifBranch, nnkExceptBranch, nnkElse, nnkAsmStmt, nnkTypeDef, nnkFinally, nnkContinueStmt, nnkImportStmt, nnkImportExceptStmt, nnkExportStmt, nnkExportExceptStmt, nnkFromStmt, nnkIncludeStmt, nnkUsingStmt, nnkBlockExpr, nnkStmtListType, nnkBlockType, nnkWith, nnkWithout, nnkTypeOfExpr, nnkObjectTy, nnkTupleTy, nnkTupleClassTy, nnkTypeClassTy, nnkStaticTy, nnkRecList, nnkRecCase, nnkRecWhen, nnkVarTy, nnkConstTy, nnkMutableTy, nnkDistinctTy, nnkProcTy, nnkIteratorTy, nnkSharedTy, nnkEnumTy, nnkEnumFieldDef, nnkArgList, nnkPattern, nnkReturnToken, nnkClosure, nnkGotoState, nnkState, nnkBreakState, nnkFuncDef, nnkTupleConstr} ''' """ @@ -62,7 +62,7 @@ type nnkSharedTy, # 'shared T' nnkEnumTy, nnkEnumFieldDef, - nnkArglist, nnkPattern + nnkArgList, nnkPattern nnkReturnToken, nnkClosure, nnkGotoState, diff --git a/tests/macros/tmacros_issues.nim b/tests/macros/tmacros_issues.nim index 19c706a82..a964f46ba 100644 --- a/tests/macros/tmacros_issues.nim +++ b/tests/macros/tmacros_issues.nim @@ -64,7 +64,7 @@ block t7723: block t8706: macro varargsLen(args:varargs[untyped]): untyped = - doAssert args.kind == nnkArglist + doAssert args.kind == nnkArgList doAssert args.len == 0 result = newLit(args.len) From 30959e24bd8840f16472d1f621e1a349d9ccae78 Mon Sep 17 00:00:00 2001 From: Federico Ceratto Date: Sat, 27 Mar 2021 10:36:39 +0100 Subject: [PATCH 1799/3768] Update security policy (#17522) * Update security policy * Update security.md Co-authored-by: Dominik Picheta Co-authored-by: Andreas Rumpf Co-authored-by: Dominik Picheta --- security.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/security.md b/security.md index e8d31b1b9..72a1a3e3d 100644 --- a/security.md +++ b/security.md @@ -2,7 +2,10 @@ ## Supported Versions -Security fixes are provided in new releases and bugfix releases. +Security advisories are published at: +https://github.com/nim-lang/security/security/advisories?state=published + +Security fixes are provided in new releases and in bugfix releases. We do not backport security fixes to older releases. @@ -10,8 +13,8 @@ We do not backport security fixes to older releases. ## Reporting a Vulnerability -Please do not report vulnerabilities via GitHub issues. - -If you have discovered a vulnerability, it is best to notify us about it via +If you have discovered a vulnerability, please notify us about it via security@nim-lang.org in order to set up a meeting where we can discuss the next steps. + +Please do not report vulnerabilities via GitHub issues. From cf5ce7616b3b2dcbe734dee7f48133c5dcf332f9 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 27 Mar 2021 12:21:57 +0100 Subject: [PATCH 1800/3768] IC: bugfix (#17533) * cleanups * IC: bugfix --- compiler/ic/ic.nim | 7 +++++-- compiler/transf.nim | 6 ++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/compiler/ic/ic.nim b/compiler/ic/ic.nim index 8017d8b40..b5fed7d4e 100644 --- a/compiler/ic/ic.nim +++ b/compiler/ic/ic.nim @@ -28,6 +28,7 @@ type PackedModule* = object ## the parts of a PackedEncoder that are part of the .rod file definedSymbols: string + moduleFlags: TSymFlags includes: seq[(LitId, string)] # first entry is the module filename itself imports: seq[LitId] # the modules this module depends on toReplay: PackedTree # pragmas and VM specific state to replay. @@ -139,6 +140,7 @@ proc initEncoder*(c: var PackedEncoder; m: var PackedModule; moduleSym: PSym; co m.sh = Shared() c.thisModule = moduleSym.itemId.module c.config = config + m.moduleFlags = moduleSym.flags m.bodies = newTreeFrom(m.topLevel) m.toReplay = newTreeFrom(m.topLevel) @@ -511,6 +513,7 @@ proc loadRodFile*(filename: AbsoluteFile; m: var PackedModule; config: ConfigRef f.loadSection configSection f.loadPrim m.definedSymbols + f.loadPrim m.moduleFlags f.loadPrim m.cfg if f.err == ok and not configIdentical(m, config) and not ignoreConfig: @@ -581,6 +584,7 @@ proc saveRodFile*(filename: AbsoluteFile; encoder: var PackedEncoder; m: var Pac f.storeHeader() f.storeSection configSection f.storePrim m.definedSymbols + f.storePrim m.moduleFlags f.storePrim m.cfg template storeSeqSection(section, data) {.dirty.} = @@ -909,8 +913,7 @@ proc setupLookupTables(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCa info: newLineInfo(fileIdx, 1, 1), position: int(fileIdx)) m.module.owner = newPackage(conf, cache, fileIdx) - if fileIdx == conf.projectMainIdx2: - m.module.flags.incl sfMainModule + m.module.flags = m.fromDisk.moduleFlags proc loadToReplayNodes(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache; fileIdx: FileIndex; m: var LoadedModule) = diff --git a/compiler/transf.nim b/compiler/transf.nim index d2d9156aa..48c737f46 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -29,8 +29,7 @@ proc transformBody*(g: ModuleGraph; idgen: IdGenerator, prc: PSym, cache: bool): import closureiters, lambdalifting type - PTransCon = ref TTransCon - TTransCon{.final.} = object # part of TContext; stackable + PTransCon = ref object # part of TContext; stackable mapping: TIdNodeTable # mapping from symbols to nodes owner: PSym # current owner forStmt: PNode # current for stmt @@ -40,7 +39,7 @@ type # if we encounter the 2nd yield statement next: PTransCon # for stacking - TTransfContext = object + PTransf = ref object module: PSym transCon: PTransCon # top of a TransCon stack inlining: int # > 0 if we are in inlining context (copy vars) @@ -49,7 +48,6 @@ type deferDetected, tooEarly: bool graph: ModuleGraph idgen: IdGenerator - PTransf = ref TTransfContext proc newTransNode(a: PNode): PNode {.inline.} = result = shallowCopy(a) From 9c52009de61a119dda2def870e5fb21581dafc9a Mon Sep 17 00:00:00 2001 From: flywind Date: Sun, 28 Mar 2021 01:12:11 +0800 Subject: [PATCH 1801/3768] close #7012 add testcase (#17537) --- tests/tuples/t7012.nim | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 tests/tuples/t7012.nim diff --git a/tests/tuples/t7012.nim b/tests/tuples/t7012.nim new file mode 100644 index 000000000..32d441ddd --- /dev/null +++ b/tests/tuples/t7012.nim @@ -0,0 +1,7 @@ +discard """ + errormsg: "illegal recursion in type 'Node'" +""" + +type Node[T] = tuple + next: ref Node[T] +var n: Node[int] \ No newline at end of file From d3705b253c155a6f129a7f26ea9435a0ab45143f Mon Sep 17 00:00:00 2001 From: flywind Date: Sun, 28 Mar 2021 01:18:35 +0800 Subject: [PATCH 1802/3768] close #5792 with testcase (#17536) --- tests/sets/t5792.nim | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 tests/sets/t5792.nim diff --git a/tests/sets/t5792.nim b/tests/sets/t5792.nim new file mode 100644 index 000000000..efc38b1bc --- /dev/null +++ b/tests/sets/t5792.nim @@ -0,0 +1,17 @@ +discard """ + matrix: "--gc:refc; --gc:arc" +""" + +type + T = enum + a + b + c + U = object + case k: T + of a: + x: int + of {b, c} - {a}: + y: int + +discard U(k: b, y: 1) From bbe4cf4703903081553fe066ece5a57a5f190493 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 27 Mar 2021 12:03:14 -0700 Subject: [PATCH 1803/3768] followup custom literals (#17500) --- compiler/condsyms.nim | 1 + compiler/ic/packed_ast.nim | 1 + compiler/lexer.nim | 25 ++++----- doc/manual.rst | 2 +- tests/lexer/tcustom_numeric_literals.nim | 69 ++++++++++++++++++------ tests/lexer/tstrlits.nim | 19 +++++++ tests/lexer/tunary_minus.nim | 10 +++- 7 files changed, 94 insertions(+), 33 deletions(-) create mode 100644 tests/lexer/tstrlits.nim diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index aa955e763..8d11dae7c 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -130,3 +130,4 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasWarningAsError") defineSymbol("nimHasHintAsError") defineSymbol("nimHasSpellSuggest") + defineSymbol("nimHasCustomLiterals") diff --git a/compiler/ic/packed_ast.nim b/compiler/ic/packed_ast.nim index 353cc3a42..1075664cb 100644 --- a/compiler/ic/packed_ast.nim +++ b/compiler/ic/packed_ast.nim @@ -333,6 +333,7 @@ template symId*(n: NodePos): SymId = SymId tree.nodes[n.int].operand proc firstSon*(n: NodePos): NodePos {.inline.} = NodePos(n.int+1) when false: + # xxx `nkStrLit` or `nkStrLit..nkTripleStrLit:` below? proc strLit*(tree: PackedTree; n: NodePos): lent string = assert n.kind == nkStrLit result = tree.sh.strings[LitId tree.nodes[n.int].operand] diff --git a/compiler/lexer.nim b/compiler/lexer.nim index ecf6cb0d9..cca7765e5 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -412,14 +412,12 @@ proc getNumber(L: var Lexer, result: var Token) = customLitPossible = true if L.buf[postPos] in SymChars: - var suffixAsLower = newStringOfCap(10) var suffix = newStringOfCap(10) while true: - let c = L.buf[postPos] - suffix.add c - suffixAsLower.add toLowerAscii(c) + suffix.add L.buf[postPos] inc postPos if L.buf[postPos] notin SymChars+{'_'}: break + let suffixAsLower = suffix.toLowerAscii case suffixAsLower of "f", "f32": result.tokType = tkFloat32Lit of "d", "f64": result.tokType = tkFloat64Lit @@ -433,16 +431,15 @@ proc getNumber(L: var Lexer, result: var Token) = of "u16": result.tokType = tkUInt16Lit of "u32": result.tokType = tkUInt32Lit of "u64": result.tokType = tkUInt64Lit + elif customLitPossible: + # remember the position of the `'` so that the parser doesn't + # have to reparse the custom literal: + result.iNumber = len(result.literal) + result.literal.add '\'' + result.literal.add suffix + result.tokType = tkCustomLit else: - if customLitPossible: - # remember the position of the ``'`` so that the parser doesn't - # have to reparse the custom literal: - result.iNumber = len(result.literal) - result.literal.add '\'' - result.literal.add suffix - result.tokType = tkCustomLit - else: - lexMessageLitNum(L, "invalid number suffix: '$1'", errPos) + lexMessageLitNum(L, "invalid number suffix: '$1'", errPos) else: lexMessageLitNum(L, "invalid number suffix: '$1'", errPos) @@ -467,7 +464,7 @@ proc getNumber(L: var Lexer, result: var Token) = if L.buf[pos] != '_': xi = `shl`(xi, 1) or (ord(L.buf[pos]) - ord('0')) inc(pos) - # 'c', 'C' is deprecated + # 'c', 'C' is deprecated (a warning is issued elsewhere) of 'o', 'c', 'C': result.base = base8 while pos < endpos: diff --git a/doc/manual.rst b/doc/manual.rst index 51c434c7b..fdc744bc7 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -536,7 +536,7 @@ Numeric literals have the form:: FLOAT64_LIT = HEX_LIT '\'' FLOAT64_SUFFIX | (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) ['\''] FLOAT64_SUFFIX - CUSTOM_NUMERIC_LIT = (FLOAT_LIT | DEC_LIT | OCT_LIT | BIN_LIT) '\'' CUSTOM_NUMERIC_SUFFIX + CUSTOM_NUMERIC_LIT = (FLOAT_LIT | INT_LIT) '\'' CUSTOM_NUMERIC_SUFFIX # CUSTOM_NUMERIC_SUFFIX is any Nim identifier that is not # a pre-defined type suffix. diff --git a/tests/lexer/tcustom_numeric_literals.nim b/tests/lexer/tcustom_numeric_literals.nim index a2f355b4d..17933950a 100644 --- a/tests/lexer/tcustom_numeric_literals.nim +++ b/tests/lexer/tcustom_numeric_literals.nim @@ -46,6 +46,7 @@ assertAST dedent """ -38383839292839283928392839283928392839283.928493849385935898243e-50000'wrap proc `'wrap`(number: string): string = "[[" & number & "]]" +proc wrap2(number: string): string = "[[" & number & "]]" doAssert lispReprStr(-1'wrap) == """(DotExpr (RStrLit "-1") (Ident "\'wrap"))""" template main = @@ -126,25 +127,59 @@ template main = doAssert -12'fooplusopt(2) == ("-12", 2) doAssert -12'fooplusopt() == ("-12", 99) doAssert -12'fooplusopt == ("-12", 99) - macro `'bar`(a: static string): untyped = - var infix = newNimNode(nnkInfix) - infix.add newIdentNode("&") - infix.add newLit("got ") - infix.add newLit(a.repr) - result = newNimNode(nnkStmtList) - result.add infix - doAssert -12'bar == "got \"-12\"" + macro `'bar`(a: static string): untyped = newLit(a.repr) + doAssert -12'bar == "\"-12\"" macro deb(a): untyped = newLit(a.repr) doAssert deb(-12'bar) == "-12'bar" - # macro metawrap(): untyped = - # func wrap1(a: string): string = "{" & a & "}" - # func `'wrap2`(a: string): string = "{" & a & "}" - # result = quote do: - # let a1 = wrap1"-128" - # let a2 = -128'wrap2 - # metawrap() - # doAssert a1 == "{-128}" - # doAssert a2 == "{-128}" + + block: # bug 1 from https://github.com/nim-lang/Nim/pull/17020#issuecomment-803193947 + macro deb1(a): untyped = newLit a.repr + macro deb2(a): untyped = newLit a.lispRepr + doAssert deb1(-12'wrap) == "-12'wrap" + doAssert deb1(-12'nonexistant) == "-12'nonexistant" + doAssert deb2(-12'nonexistant) == """(DotExpr (RStrLit "-12") (Ident "\'nonexistant"))""" + when false: # xxx bug: + # this holds: + doAssert deb2(-12.wrap2) == """(DotExpr (IntLit -12) (Sym "wrap2"))""" + doAssert deb2(-12'wrap) == """(DotExpr (RStrLit "-12") (Sym "\'wrap"))""" + # but instead this should hold: + doAssert deb2(-12.wrap2) == """(DotExpr (IntLit -12) (Ident "wrap2"))""" + doAssert deb2(-12'wrap) == """(DotExpr (RStrLit "-12") (Ident "\'wrap"))""" + + block: # bug 2 from https://github.com/nim-lang/Nim/pull/17020#issuecomment-803193947 + template toSuf(`'suf`): untyped = + let x = -12'suf + x + doAssert toSuf(`'wrap`) == "[[-12]]" + + block: # bug 10 from https://github.com/nim-lang/Nim/pull/17020#issuecomment-803193947 + proc `myecho`(a: auto): auto = a + template fn1(): untyped = + let a = "abc" + -12'wrap + template fn2(): untyped = + `myecho` -12'wrap + template fn3(): untyped = + -12'wrap + doAssert fn1() == "[[-12]]" + doAssert fn2() == "[[-12]]" + doAssert fn3() == "[[-12]]" + + when false: # xxx this fails; bug 9 from https://github.com/nim-lang/Nim/pull/17020#issuecomment-803193947 + #[ + possible workaround: use `genAst` (https://github.com/nim-lang/Nim/pull/17426) and this: + let a3 = `'wrap3`("-128") + ]# + block: + macro metawrap(): untyped = + func wrap1(a: string): string = "{" & a & "}" + func `'wrap3`(a: string): string = "{" & a & "}" + result = quote do: + let a1 = wrap1"-128" + let a2 = -128'wrap3 + metawrap() + doAssert a1 == "{-128}" + doAssert a2 == "{-128}" static: main() main() diff --git a/tests/lexer/tstrlits.nim b/tests/lexer/tstrlits.nim new file mode 100644 index 000000000..8e8250a5b --- /dev/null +++ b/tests/lexer/tstrlits.nim @@ -0,0 +1,19 @@ +discard """ + output: "a\"\"long string\"\"\"\"\"abc\"def_'2'●𝌆𝌆A" +""" +# Test the new different string literals + +const + tripleEmpty = """"long string"""""""" # "long string """"" + + rawQuote = r"a""" + + raw = r"abc""def" + + escaped = "\x5f'\50'\u25cf\u{1D306}\u{1d306}\u{41}" + + +stdout.write(rawQuote) +stdout.write(tripleEmpty) +stdout.write(raw) +stdout.writeLine(escaped) diff --git a/tests/lexer/tunary_minus.nim b/tests/lexer/tunary_minus.nim index 639911fcd..87b3cb52d 100644 --- a/tests/lexer/tunary_minus.nim +++ b/tests/lexer/tunary_minus.nim @@ -51,8 +51,17 @@ template main = doAssert x() == minusOne: "unable to handle negatives after semi-colon" + block: doAssert -0b111 == -7 doAssert -0xff == -255 + doAssert -128'i8 == (-128).int8 + doAssert $(-128'i8) == "-128" + doAssert -32768'i16 == int16.low + doAssert -2147483648'i32 == int32.low + when int.sizeof > 4: + doAssert -9223372036854775808 == int.low + when not defined(js): + doAssert -9223372036854775808 == int64.low block: # check when a minus (-) is an unary op doAssert -one == minusOne: @@ -68,6 +77,5 @@ template main = doAssert 4 - one == 3: "unable to handle subtraction with surrounding spaces with an identifier" - static: main() main() From d96469037ecc1ee7982b420cbe6ce34e91d18b01 Mon Sep 17 00:00:00 2001 From: Jason Beetham Date: Sat, 27 Mar 2021 18:43:24 -0600 Subject: [PATCH 1804/3768] Fixed embedded nimscript imports (#17425) --- compiler/options.nim | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/compiler/options.nim b/compiler/options.nim index 2d63043df..2c8d43733 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -812,10 +812,11 @@ proc findModule*(conf: ConfigRef; modulename, currentModule: string): AbsoluteFi for candidate in stdlibDirs: let path = (conf.libpath.string / candidate / stripped) if fileExists(path): - m = path + result = AbsoluteFile path break - let currentPath = currentModule.splitFile.dir - result = AbsoluteFile currentPath / m + else: # If prefixed with std/ why would we add the current module path! + let currentPath = currentModule.splitFile.dir + result = AbsoluteFile currentPath / m if not fileExists(result): result = findFile(conf, m) patchModule(conf) From b77a420d3eb118cc92443e925f48996fe419f662 Mon Sep 17 00:00:00 2001 From: rockcavera Date: Sun, 28 Mar 2021 02:11:51 -0300 Subject: [PATCH 1805/3768] set const arch64 to fix compiling with vcc/icc (#17539) --- lib/system/countbits_impl.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/system/countbits_impl.nim b/lib/system/countbits_impl.nim index d3c003ff7..e0338f8f9 100644 --- a/lib/system/countbits_impl.nim +++ b/lib/system/countbits_impl.nim @@ -18,6 +18,7 @@ const useGCC_builtins* = (defined(gcc) or defined(llvm_gcc) or defined(clang)) and useBuiltins const useICC_builtins* = defined(icc) and useBuiltins const useVCC_builtins* = defined(vcc) and useBuiltins +const arch64 = sizeof(int) == 8 template countBitsImpl(n: uint32): int = # generic formula is from: https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel From 6a355a4db05addb01fc02df0243c540f100bf107 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 28 Mar 2021 00:29:06 -0700 Subject: [PATCH 1806/3768] nim: unbreak CI; testament: add allowedFailure logic for tests that may fail but should still run (#17513) --- testament/categories.nim | 7 +++-- testament/important_packages.nim | 47 ++++++++++++++++---------------- testament/testament.nim | 16 +++++++---- 3 files changed, 40 insertions(+), 30 deletions(-) diff --git a/testament/categories.nim b/testament/categories.nim index 86aa1f8af..af6331dde 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -448,7 +448,10 @@ proc testNimblePackages(r: var TResults; cat: Category; packageFilter: string) = (outp, status) = execCmdEx(cmd, workingDir = workingDir2) status == QuitSuccess if not ok: - addResult(r, test, targetC, "", cmd & "\n" & outp, reFailed) + if pkg.allowFailure: + inc r.passed + inc r.failedButAllowed + addResult(r, test, targetC, "", cmd & "\n" & outp, reFailed, allowFailure = pkg.allowFailure) continue outp @@ -461,7 +464,7 @@ proc testNimblePackages(r: var TResults; cat: Category; packageFilter: string) = discard tryCommand("nimble install --depsOnly -y", maxRetries = 3) discard tryCommand(pkg.cmd, reFailed = reBuildFailed) inc r.passed - r.addResult(test, targetC, "", "", reSuccess) + r.addResult(test, targetC, "", "", reSuccess, allowFailure = pkg.allowFailure) errors = r.total - r.passed if errors == 0: diff --git a/testament/important_packages.nim b/testament/important_packages.nim index eb0196f70..8a8d6ffd0 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -22,22 +22,26 @@ When this is the case, a workaround is to test this package here by adding `--pa type NimblePackage* = object name*, cmd*, url*: string useHead*: bool + allowFailure*: bool + ## When true, we still run the test but the test is allowed to fail. + ## This is useful for packages that currently fail but that we still want to + ## run in CI, e.g. so that we can monitor when they start working again and + ## are reminded about those failures without making CI fail for unrelated PRs. var packages*: seq[NimblePackage] -proc pkg(name: string; cmd = "nimble test"; url = "", useHead = true) = - packages.add NimblePackage(name: name, cmd: cmd, url: url, useHead: useHead) +proc pkg(name: string; cmd = "nimble test"; url = "", useHead = true, allowFailure = false) = + packages.add NimblePackage(name: name, cmd: cmd, url: url, useHead: useHead, allowFailure: allowFailure) -# pkg "alea" +pkg "alea", allowFailure = true pkg "argparse" -when false: - pkg "arraymancer", "nim c tests/tests_cpu.nim" -# pkg "ast_pattern_matching", "nim c -r --oldgensym:on tests/test1.nim" +pkg "arraymancer", "nim c tests/tests_cpu.nim", allowFailure = true +pkg "ast_pattern_matching", "nim c -r --oldgensym:on tests/test1.nim", allowFailure = true pkg "awk" pkg "bigints", url = "https://github.com/Araq/nim-bigints" pkg "binaryheap", "nim c -r binaryheap.nim" pkg "BipBuffer" -# pkg "blscurve" # pending https://github.com/status-im/nim-blscurve/issues/39 +pkg "blscurve", allowFailure = true # pending https://github.com/status-im/nim-blscurve/issues/39 pkg "bncurve" pkg "brainfuck", "nim c -d:release -r tests/compile.nim" pkg "bump", "nim c --gc:arc --path:. -r tests/tbump.nim", "https://github.com/disruptek/bump" @@ -45,16 +49,13 @@ pkg "c2nim", "nim c testsuite/tester.nim" pkg "cascade" pkg "cello" pkg "chroma" -# pkg "chronicles", "nim c -o:chr -r chronicles.nim" -# when not defined(osx): # testdatagram.nim(560, 54): Check failed -# pkg "chronos", "nim c -r -d:release tests/testall" - # pending https://github.com/nim-lang/Nim/issues/17130 - +pkg "chronicles", "nim c -o:chr -r chronicles.nim", allowFailure = true # pending https://github.com/status-im/nim-chronos/issues/169 +pkg "chronos", "nim c -r -d:release tests/testall", allowFailure = true # pending https://github.com/nim-lang/Nim/issues/17130 pkg "cligen", "nim c --path:. -r cligen.nim" pkg "combparser", "nimble test --gc:orc" pkg "compactdict" pkg "comprehension", "nimble test", "https://github.com/alehander42/comprehension" -# pkg "criterion" # pending https://github.com/disruptek/criterion/issues/3 (wrongly closed) +pkg "criterion", allowFailure = true # pending https://github.com/disruptek/criterion/issues/3 (wrongly closed) pkg "dashing", "nim c tests/functional.nim" pkg "delaunay" pkg "docopt" @@ -66,12 +67,12 @@ pkg "fusion" pkg "gara" pkg "glob" pkg "ggplotnim", "nim c -d:noCairo -r tests/tests.nim" -# pkg "gittyup", "nimble test", "https://github.com/disruptek/gittyup" +pkg "gittyup", "nimble test", "https://github.com/disruptek/gittyup", allowFailure = true pkg "gnuplot", "nim c gnuplot.nim" # pkg "gram", "nim c -r --gc:arc --define:danger tests/test.nim", "https://github.com/disruptek/gram" # pending https://github.com/nim-lang/Nim/issues/16509 pkg "hts", "nim c -o:htss src/hts.nim" -# pkg "httpauth" +pkg "httpauth", allowFailure = true pkg "illwill", "nimble examples" pkg "inim" pkg "itertools", "nim doc src/itertools.nim" @@ -87,28 +88,28 @@ pkg "memo" pkg "msgpack4nim", "nim c -r tests/test_spec.nim" pkg "nake", "nim c nakefile.nim" pkg "neo", "nim c -d:blas=openblas tests/all.nim" -# pkg "nesm", "nimble tests" # notice plural 'tests' -# pkg "nico" +pkg "nesm", "nimble tests", allowFailure = true # notice plural 'tests' +pkg "nico", allowFailure = true pkg "nicy", "nim c -r src/nicy.nim" pkg "nigui", "nim c -o:niguii -r src/nigui.nim" pkg "nimcrypto", "nim r --path:. tests/testall.nim" # `--path:.` workaround needed, see D20210308T165435 pkg "NimData", "nim c -o:nimdataa src/nimdata.nim" pkg "nimes", "nim c src/nimes.nim" pkg "nimfp", "nim c -o:nfp -r src/fp.nim" -# pkg "nimgame2", "nim c nimgame2/nimgame.nim" # XXX Doesn't work with deprecated 'randomize', will create a PR. +pkg "nimgame2", "nim c nimgame2/nimgame.nim", allowFailure = true # XXX Doesn't work with deprecated 'randomize', will create a PR. pkg "nimgen", "nim c -o:nimgenn -r src/nimgen/runcfg.nim" pkg "nimlsp" pkg "nimly", "nim c -r tests/test_readme_example.nim" -# pkg "nimongo", "nimble test_ci" -# pkg "nimph", "nimble test", "https://github.com/disruptek/nimph" +pkg "nimongo", "nimble test_ci", allowFailure = true +pkg "nimph", "nimble test", "https://github.com/disruptek/nimph", allowFailure = true pkg "nimpy", "nim c -r tests/nimfrompy.nim" pkg "nimquery" pkg "nimsl" pkg "nimsvg" pkg "nimterop", "nimble minitest" pkg "nimwc", "nim c nimwc.nim" -# pkg "nimx", "nim c --threads:on test/main.nim" -# pkg "nitter", "nim c src/nitter.nim", "https://github.com/zedeus/nitter" +pkg "nimx", "nim c --threads:on test/main.nim", allowFailure = true +pkg "nitter", "nim c src/nitter.nim", "https://github.com/zedeus/nitter", allowFailure = true pkg "norm", "nim c -r tests/sqlite/trows.nim" pkg "npeg", "nimble testarc" pkg "numericalnim", "nim c -r tests/test_integrate.nim" @@ -150,7 +151,7 @@ pkg "unicodedb", "nim c -d:release -r tests/tests.nim" pkg "unicodeplus", "nim c -d:release -r tests/tests.nim" pkg "unpack" pkg "websocket", "nim c websocket.nim" -# pkg "winim" +pkg "winim", allowFailure = true pkg "with" pkg "ws" pkg "yaml", "nim build" diff --git a/testament/testament.nim b/testament/testament.nim index 6fc7e32ce..462c0a57c 100644 --- a/testament/testament.nim +++ b/testament/testament.nim @@ -66,7 +66,8 @@ proc isNimRepoTests(): bool = type Category = distinct string TResults = object - total, passed, skipped: int + total, passed, failedButAllowed, skipped: int + ## xxx rename passed to passedOrAllowedFailure data: string TTest = object name: string @@ -212,6 +213,7 @@ proc callCCompiler(cmdTemplate, filename, options: string, proc initResults: TResults = result.total = 0 result.passed = 0 + result.failedButAllowed = 0 result.skipped = 0 result.data = "" @@ -239,16 +241,20 @@ template maybeStyledEcho(args: varargs[untyped]): untyped = proc `$`(x: TResults): string = - result = ("Tests passed: $1 / $3
                                      \n" & - "Tests skipped: $2 / $3
                                      \n") % - [$x.passed, $x.skipped, $x.total] + result = """ +Tests passed or allowed to fail: $2 / $1
                                      +Tests failed and allowed to fail: $3 / $1
                                      +Tests skipped: $4 / $1
                                      +""" % [$x.total, $x.passed, $x.failedButAllowed, $x.skipped] proc addResult(r: var TResults, test: TTest, target: TTarget, - expected, given: string, successOrig: TResultEnum) = + expected, given: string, successOrig: TResultEnum, allowFailure = false) = # test.name is easier to find than test.name.extractFilename # A bit hacky but simple and works with tests/testament/tshould_not_work.nim var name = test.name.replace(DirSep, '/') name.add ' ' & $target + if allowFailure: + name.add " (allowed to fail) " if test.options.len > 0: name.add ' ' & test.options let duration = epochTime() - test.startTime From c86fdfa1ee2cbba201b0a6182212ced5e218dc65 Mon Sep 17 00:00:00 2001 From: flywind Date: Sun, 28 Mar 2021 15:57:22 +0800 Subject: [PATCH 1807/3768] add testcase for #9466 (#17538) --- tests/notnil/tnotnil5.nim | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 tests/notnil/tnotnil5.nim diff --git a/tests/notnil/tnotnil5.nim b/tests/notnil/tnotnil5.nim new file mode 100644 index 000000000..2dcb7f7c3 --- /dev/null +++ b/tests/notnil/tnotnil5.nim @@ -0,0 +1,28 @@ +discard """ + matrix: "--threads:on" +""" + +{.experimental: "parallel".} +{.experimental: "notnil".} +import threadpool + +type + AO = object + x: int + + A = ref AO not nil + +proc process(a: A): A = + return A(x: a.x+1) + +proc processMany(ayys: openArray[A]): seq[A] = + var newAs: seq[FlowVar[A]] + + parallel: + for a in ayys: + newAs.add(spawn process(a)) + for newAflow in newAs: + let newA = ^newAflow + if isNil(newA): + return @[] + result.add(newA) From eb3ed44009fdfea3e67608b68a049c265124d643 Mon Sep 17 00:00:00 2001 From: ee7 <45465154+ee7@users.noreply.github.com> Date: Sun, 28 Mar 2021 12:44:16 +0200 Subject: [PATCH 1808/3768] system: fix link in `delete` proc (#17550) The `delete` proc had a "See also" link that linked to itself. --- lib/system.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/system.nim b/lib/system.nim index 4c31f20a2..27f976119 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1321,7 +1321,7 @@ proc delete*[T](x: var seq[T], i: Natural) {.noSideEffect.} = ## This is an `O(n)` operation. ## ## See also: - ## * `del <#delete,seq[T],Natural>`_ for O(1) operation + ## * `del <#del,seq[T],Natural>`_ for O(1) operation ## ## .. code-block:: Nim ## var i = @[1, 2, 3, 4, 5] From 207bcabdf29dbaa954a3db778cbd9e4d59ca37ae Mon Sep 17 00:00:00 2001 From: Ardek Romak Date: Sun, 28 Mar 2021 19:57:40 +0300 Subject: [PATCH 1809/3768] Add a getter for all defined Sections in parsecfg (#15450) --- changelog.md | 2 ++ lib/pure/parsecfg.nim | 6 ++++++ tests/stdlib/tparsecfg.nim | 23 +++++++++-------------- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/changelog.md b/changelog.md index f180e90f9..d3335c2a3 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,8 @@ ## Standard library additions and changes +- Added `sections` iterator in `parsecfg`. + - Make custom op in macros.quote work for all statements. - On Windows the SSL library now checks for valid certificates. diff --git a/lib/pure/parsecfg.nim b/lib/pure/parsecfg.nim index 23bb09ac4..7f22a9857 100644 --- a/lib/pure/parsecfg.nim +++ b/lib/pure/parsecfg.nim @@ -174,6 +174,7 @@ runnableExamples: import strutils, lexbase, streams, tables import std/private/decode_helpers +import std/private/since include "system/inclrtl" @@ -649,3 +650,8 @@ proc delSectionKey*(dict: var Config, section, key: string) = dict.del(section) else: dict[section].del(key) + +iterator sections*(dict: Config): lent string {.since: (1, 5).} = + ## Iterates through the sections in the `dict`. + for section in dict.keys: + yield section diff --git a/tests/stdlib/tparsecfg.nim b/tests/stdlib/tparsecfg.nim index 5c077bbda..b2e57ac3d 100644 --- a/tests/stdlib/tparsecfg.nim +++ b/tests/stdlib/tparsecfg.nim @@ -2,7 +2,7 @@ discard """ targets: "c js" """ -import parsecfg, streams +import parsecfg, streams, sequtils when not defined(js): from stdtest/specialpaths import buildDir @@ -39,19 +39,14 @@ var ss = newStringStream() dict1.writeConfig(ss) ## Reading a configuration file. -var dict2 = loadConfig(newStringStream(ss.data)) -var charset = dict2.getSectionValue("", "charset") -var threads = dict2.getSectionValue("Package", "--threads") -var pname = dict2.getSectionValue("Package", "name") -var name = dict2.getSectionValue("Author", "name") -var qq = dict2.getSectionValue("Author", "qq") -var email = dict2.getSectionValue("Author", "email") -doAssert charset == "utf-8" -doAssert threads == "on" -doAssert pname == "hello" -doAssert name == "lihf8515" -doAssert qq == "10214028" -doAssert email == "lihaifeng@wxm.com" +let dict2 = loadConfig(newStringStream(ss.data)) +doAssert dict2.getSectionValue("", "charset") == "utf-8" +doAssert dict2.getSectionValue("Package", "--threads") == "on" +doAssert dict2.getSectionValue("Package", "name") == "hello" +doAssert dict2.getSectionValue("Author", "name") == "lihf8515" +doAssert dict2.getSectionValue("Author", "qq") == "10214028" +doAssert dict2.getSectionValue("Author", "email") == "lihaifeng@wxm.com" +doAssert toSeq(dict2.sections) == @["", "Package", "Author"] ## Modifying a configuration file. var dict3 = loadConfig(newStringStream(ss.data)) From a5600e49df71463cda6bd0a062e02c8bdd48da0c Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 29 Mar 2021 07:40:37 +0800 Subject: [PATCH 1810/3768] close #9622 add testcase (#17557) * fix nim js cmp fails at CT * close #9622 add testcase --- tests/vm/t9622.nim | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 tests/vm/t9622.nim diff --git a/tests/vm/t9622.nim b/tests/vm/t9622.nim new file mode 100644 index 000000000..214ab0f19 --- /dev/null +++ b/tests/vm/t9622.nim @@ -0,0 +1,30 @@ +discard """ + targets: "c cpp" + matrix: "--gc:refc; --gc:arc" +""" + +type + GlobNodeKind = enum + LiteralIdent, + Group + + GlobNode = object + case kind: GlobNodeKind + of LiteralIdent: + value: string + of Group: + values: seq[string] + + PathSegment = object + children: seq[GlobNode] + + GlobPattern = seq[PathSegment] + +proc parseImpl(): GlobPattern = + if result.len == 0: + result.add PathSegment() + result[^1].children.add GlobNode(kind: LiteralIdent) + +block: + const pattern = parseImpl() + doAssert $pattern == """@[(children: @[(kind: LiteralIdent, value: "")])]""" From a36816fc047d4ab2c4f80056c207f117a635a5d4 Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 29 Mar 2021 09:11:07 +0800 Subject: [PATCH 1811/3768] follow up #17539 (#17548) * fix nim js cmp fails at CT * follow up #17539 --- lib/pure/bitops.nim | 1 - lib/system/countbits_impl.nim | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/pure/bitops.nim b/lib/pure/bitops.nim index 377602e75..4ef190c1b 100644 --- a/lib/pure/bitops.nim +++ b/lib/pure/bitops.nim @@ -413,7 +413,6 @@ func fastlog2Nim(x: uint64): int {.inline.} = import system/countbits_impl -const arch64 = sizeof(int) == 8 const useBuiltinsRotate = (defined(amd64) or defined(i386)) and (defined(gcc) or defined(clang) or defined(vcc) or (defined(icl) and not defined(cpp))) and useBuiltins diff --git a/lib/system/countbits_impl.nim b/lib/system/countbits_impl.nim index e0338f8f9..184f97bd4 100644 --- a/lib/system/countbits_impl.nim +++ b/lib/system/countbits_impl.nim @@ -18,7 +18,7 @@ const useGCC_builtins* = (defined(gcc) or defined(llvm_gcc) or defined(clang)) and useBuiltins const useICC_builtins* = defined(icc) and useBuiltins const useVCC_builtins* = defined(vcc) and useBuiltins -const arch64 = sizeof(int) == 8 +const arch64* = sizeof(int) == 8 template countBitsImpl(n: uint32): int = # generic formula is from: https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel From e5be216ccbf1e3453e0076f2f260ddda85a5f27a Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 29 Mar 2021 11:21:27 +0800 Subject: [PATCH 1812/3768] [docs]close #12580 (#17549) --- lib/system/memalloc.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/system/memalloc.nim b/lib/system/memalloc.nim index fca1db91d..f8ebc8c5f 100644 --- a/lib/system/memalloc.nim +++ b/lib/system/memalloc.nim @@ -188,8 +188,8 @@ when hasAlloc and not defined(js): cast[ptr T](realloc(p, T.sizeof * newSize)) proc dealloc*(p: pointer) {.noconv, compilerproc, rtl, benign, raises: [], tags: [].} = - ## Frees the memory allocated with `alloc`, `alloc0` or - ## `realloc`. + ## Frees the memory allocated with `alloc`, `alloc0`, + ## `realloc`, `create` or `createU`. ## ## **This procedure is dangerous!** ## If one forgets to free the memory a leak occurs; if one tries to From b36182b0a4ca13d90c1a19de5b9945289c4f25fd Mon Sep 17 00:00:00 2001 From: Danil Yarantsev Date: Mon, 29 Mar 2021 11:49:19 +0300 Subject: [PATCH 1813/3768] Free the certificate after checking in `checkCertName` (#17558) [backport:1.2] * Fix small leak in checkCertName * Size is not needed either * Free the certificate after checking --- lib/pure/net.nim | 7 ++++--- lib/wrappers/openssl.nim | 2 ++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index b37782271..bb1a14cfd 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -770,10 +770,11 @@ when defineSsl: raiseSSLError("No SSL certificate found.") const X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT = 0x1.cuint - const size = 1024 - var peername: string = newString(size) + # https://www.openssl.org/docs/man1.1.1/man3/X509_check_host.html let match = certificate.X509_check_host(hostname.cstring, hostname.len.cint, - X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT, peername) + X509_CHECK_FLAG_ALWAYS_CHECK_SUBJECT, nil) + # https://www.openssl.org/docs/man1.1.1/man3/SSL_get_peer_certificate.html + X509_free(certificate) if match != 1: raiseSSLError("SSL Certificate check failed.") diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim index 313ce7d19..ec4740bab 100644 --- a/lib/wrappers/openssl.nim +++ b/lib/wrappers/openssl.nim @@ -809,6 +809,8 @@ when not defined(nimDisableCertificateValidation) and not defined(windows): proc X509_check_host*(cert: PX509, name: cstring, namelen: cint, flags:cuint, peername: cstring): cint {.cdecl, dynlib: DLLSSLName, importc.} + proc X509_free*(cert: PX509) {.cdecl, dynlib: DLLSSLName, importc.} + # Certificates store type PX509_STORE* = SslPtr From 085580a2c8f303acf1654731ebc7dba7e6e501a7 Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 29 Mar 2021 16:50:58 +0800 Subject: [PATCH 1814/3768] fix #14010(fix std/registry + arc) (#17551) * fix nim js cmp fails at CT * fix #14010 * Update tests/stdlib/tregistry.nim Co-authored-by: Timothee Cour * Update tregistry.nim Co-authored-by: Timothee Cour --- lib/windows/registry.nim | 18 ++++++++++-------- tests/stdlib/tregistry.nim | 15 +++++++++++++++ 2 files changed, 25 insertions(+), 8 deletions(-) create mode 100644 tests/stdlib/tregistry.nim diff --git a/lib/windows/registry.nim b/lib/windows/registry.nim index c17f2f455..ab7d0b1fe 100644 --- a/lib/windows/registry.nim +++ b/lib/windows/registry.nim @@ -54,16 +54,18 @@ proc getUnicodeValue*(path, key: string; handle: HKEY): string = var newHandle: HKEY call regOpenKeyEx(handle, hh, 0, KEY_READ or KEY_WOW64_64KEY, newHandle) call regGetValue(newHandle, nil, kk, flags, nil, nil, addr bufsize) - var res = newWideCString("", bufsize) - call regGetValue(newHandle, nil, kk, flags, nil, cast[pointer](res), - addr bufsize) - result = res $ bufsize + if bufSize > 0: + var res = newWideCString(bufsize) + call regGetValue(newHandle, nil, kk, flags, nil, addr res[0], + addr bufsize) + result = res $ bufsize call regCloseKey(newHandle) else: - var res = newWideCString("", bufsize) - call regGetValue(handle, hh, kk, flags, nil, cast[pointer](res), - addr bufsize) - result = res $ bufsize + if bufSize > 0: + var res = newWideCString(bufsize) + call regGetValue(handle, hh, kk, flags, nil, addr res[0], + addr bufsize) + result = res $ bufsize proc regSetValue(key: HKEY, lpSubKey, lpValueName: WideCString, dwType: int32; lpData: WideCString; cbData: int32): int32 {. diff --git a/tests/stdlib/tregistry.nim b/tests/stdlib/tregistry.nim new file mode 100644 index 000000000..8bf084f6d --- /dev/null +++ b/tests/stdlib/tregistry.nim @@ -0,0 +1,15 @@ +discard """ + disabled: "unix" + matrix: "--gc:refc; --gc:arc" +""" + +when defined(windows): + import std/registry + + block: # bug #14010 + let path = "Environment" + let key = "D20210328T202842_key" + let val = "D20210328T202842_val" + let handle = HKEY_CURRENT_USER + setUnicodeValue("Environment", key, val, handle) + doAssert getUnicodeValue(path, key, handle) == val From e45b1e3f36f2c4778967410137bb87b8691e6c8c Mon Sep 17 00:00:00 2001 From: ben Date: Mon, 29 Mar 2021 10:24:15 +0100 Subject: [PATCH 1815/3768] Fix net.readLine infine loop #17534 (#17535) * Fix net.readLine infine loop #17534 * fix #17534 add return to template --- lib/pure/net.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index bb1a14cfd..ca109dc22 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -1535,6 +1535,7 @@ proc readLine*(socket: Socket, line: var string, timeout = -1, if flags.isDisconnectionError(lastError): setLen(line, 0) socket.socketError(n, lastError = lastError, flags = flags) + return var waited: Duration From ab57e7f244fb44fbd283de1fcb16c9c9a6fece42 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 29 Mar 2021 12:42:34 +0200 Subject: [PATCH 1816/3768] added a simple 'koch branchdone' command to speed up my workflow (#17563) --- koch.nim | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/koch.nim b/koch.nim index 6f1da8165..3b00d3564 100644 --- a/koch.nim +++ b/koch.nim @@ -664,6 +664,13 @@ proc showHelp() = quit(HelpText % [VersionAsString & spaces(44-len(VersionAsString)), CompileDate, CompileTime], QuitSuccess) +proc branchDone() = + let thisBranch = execProcess("git symbolic-ref --short HEAD").strip() + if thisBranch != "devel" and thisBranch != "": + exec("git checkout devel") + exec("git branch -D " & thisBranch) + exec("git pull --rebase") + when isMainModule: var op = initOptParser() var @@ -722,6 +729,7 @@ when isMainModule: let suffix = if latest: HeadHash else: FusionStableHash exec("nimble install -y fusion@$#" % suffix) of "ic": icTest(op.cmdLineRest) + of "branchdone": branchDone() else: showHelp() break of cmdEnd: break From 9e3960e83dea1b00fe670fc78e74cfd6fafd53b2 Mon Sep 17 00:00:00 2001 From: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Date: Mon, 29 Mar 2021 12:44:31 +0200 Subject: [PATCH 1817/3768] Small doc improvements for `std/with` (#17556) --- lib/std/with.nim | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/std/with.nim b/lib/std/with.nim index 79afd61a4..c7338b4e4 100644 --- a/lib/std/with.nim +++ b/lib/std/with.nim @@ -12,15 +12,16 @@ ## and https://github.com/nim-lang/RFCs/issues/192 for details leading to this ## particular design. ## -## **Since** version 1.2. +## **Since:** version 1.2. import macros, private / underscored_calls macro with*(arg: typed; calls: varargs[untyped]): untyped = - ## This macro provides the `chaining`:idx: of function calls. + ## This macro provides `chaining`:idx: of function calls. ## It does so by patching every call in `calls` to ## use `arg` as the first argument. - ## **This evaluates `arg` multiple times!** + ## + ## .. caution:: This evaluates `arg` multiple times! runnableExamples: var x = "yay" with x: From 8b26b3ad0d6c9458ac31170a95d956347a55627e Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 29 Mar 2021 03:46:53 -0700 Subject: [PATCH 1818/3768] remove docutils.nimble (not a real nimble package, and affected canonical imports) (#17544) * remove docutils.nimble (not a real nimble package, and affected canonical imports) * cleanup redundant references --- lib/packages/docutils/docutils.nimble | 4 ---- lib/packages/docutils/docutils.nimble.old | 7 +++++++ lib/packages/docutils/highlite.nim | 2 -- lib/packages/docutils/rst.nim | 3 --- lib/packages/docutils/rstast.nim | 2 -- lib/packages/docutils/rstgen.nim | 2 -- 6 files changed, 7 insertions(+), 13 deletions(-) delete mode 100644 lib/packages/docutils/docutils.nimble create mode 100644 lib/packages/docutils/docutils.nimble.old diff --git a/lib/packages/docutils/docutils.nimble b/lib/packages/docutils/docutils.nimble deleted file mode 100644 index f1683c515..000000000 --- a/lib/packages/docutils/docutils.nimble +++ /dev/null @@ -1,4 +0,0 @@ -version = "0.10.0" -author = "Andreas Rumpf" -description = "Nim's reStructuredText processor." -license = "MIT" diff --git a/lib/packages/docutils/docutils.nimble.old b/lib/packages/docutils/docutils.nimble.old new file mode 100644 index 000000000..f97c3bdde --- /dev/null +++ b/lib/packages/docutils/docutils.nimble.old @@ -0,0 +1,7 @@ +# xxx disabled this as this isn't really a nimble package and it affects logic +# used to compute canonical imports, refs https://github.com/nim-lang/Nim/pull/16999#issuecomment-805442914 + +version = "0.10.0" +author = "Andreas Rumpf" +description = "Nim's reStructuredText processor." +license = "MIT" diff --git a/lib/packages/docutils/highlite.nim b/lib/packages/docutils/highlite.nim index 8c91e0a8e..2655a7ea3 100644 --- a/lib/packages/docutils/highlite.nim +++ b/lib/packages/docutils/highlite.nim @@ -11,8 +11,6 @@ ## Currently only few languages are supported, other languages may be added. ## The interface supports one language nested in another. ## -## **Note:** Import `packages/docutils/highlite` to use this module -## ## You can use this to build your own syntax highlighting, check this example: ## ## .. code::nim diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index adc83c9af..6845f61b9 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -142,9 +142,6 @@ ## See `packages/docutils/rstgen module `_ to know how to ## generate HTML or Latex strings to embed them into your documents. ## -## .. Tip:: Import ``packages/docutils/rst`` to use this module -## programmatically. -## ## .. _quick introduction: https://docutils.sourceforge.io/docs/user/rst/quickstart.html ## .. _RST reference: https://docutils.sourceforge.io/docs/user/rst/quickref.html ## .. _RST specification: https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html diff --git a/lib/packages/docutils/rstast.nim b/lib/packages/docutils/rstast.nim index c68df7daa..ff34da2d1 100644 --- a/lib/packages/docutils/rstast.nim +++ b/lib/packages/docutils/rstast.nim @@ -8,8 +8,6 @@ # ## This module implements an AST for the `reStructuredText`:idx: parser. -## -## **Note:** Import ``packages/docutils/rstast`` to use this module import strutils, json diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index c52a0fdcc..30c7f3080 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -38,8 +38,6 @@ ## * The same goes for footnotes/citations links: they point to themselves. ## No backreferences are generated since finding all references of a footnote ## can be done by simply searching for [footnoteName]. -## -## .. Tip: Import ``packages/docutils/rstgen`` to use this module import strutils, os, hashes, strtabs, rstast, rst, highlite, tables, sequtils, algorithm, parseutils From c6dc9c02551e93fd4c53e48fd7fa8a0df7843267 Mon Sep 17 00:00:00 2001 From: Saem Ghani Date: Mon, 29 Mar 2021 03:48:00 -0700 Subject: [PATCH 1819/3768] fixes #17437 - crash where error reporting > 1 (#17547) * fixes #17437 * Fix bug reference comment Co-authored-by: Timothee Cour * [skip ci] describe why we have hasError Co-authored-by: Timothee Cour --- compiler/semobjconstr.nim | 16 ++++++++++++---- tests/objects/t17437.nim | 22 ++++++++++++++++++++++ 2 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 tests/objects/t17437.nim diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim index 792488c9f..dea187ea7 100644 --- a/compiler/semobjconstr.nim +++ b/compiler/semobjconstr.nim @@ -380,7 +380,7 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = if t == nil: localError(c.config, n.info, errGenerated, "object constructor needs an object type") - return + return errorNode(c, result) t = skipTypes(t, {tyGenericInst, tyAlias, tySink, tyOwned}) if t.kind == tyRef: @@ -392,17 +392,19 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = result.typ.flags.incl tfHasOwned if t.kind != tyObject: localError(c.config, n.info, errGenerated, "object constructor needs an object type") - return + return errorNode(c, result) # Check if the object is fully initialized by recursively testing each # field (if this is a case object, initialized fields in two different # branches will be reported as an error): var constrCtx = initConstrContext(t, result) let initResult = semConstructTypeAux(c, constrCtx, flags) + var hasError = false # needed to split error detect/report for better msgs # It's possible that the object was not fully initialized while # specifying a .requiresInit. pragma: if constrCtx.missingFields.len > 0: + hasError = true localError(c.config, result.info, "The $1 type requires the following fields to be initialized: $2.", [t.sym.name.s, listSymbolNames(constrCtx.missingFields)]) @@ -415,6 +417,7 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = if nfSem notin field.flags: if field.kind != nkExprColonExpr: invalidObjConstr(c, field) + hasError = true continue let id = considerQuotedIdent(c, field[0]) # This node was not processed. There are two possible reasons: @@ -423,10 +426,15 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = let prevId = considerQuotedIdent(c, result[j][0]) if prevId.id == id.id: localError(c.config, field.info, errFieldInitTwice % id.s) - return + hasError = true + break # 2) No such field exists in the constructed type localError(c.config, field.info, errUndeclaredFieldX % id.s) - return + hasError = true + break if initResult == initFull: incl result.flags, nfAllFieldsSet + + # wrap in an error see #17437 + if hasError: result = errorNode(c, result) diff --git a/tests/objects/t17437.nim b/tests/objects/t17437.nim new file mode 100644 index 000000000..b5c0e0525 --- /dev/null +++ b/tests/objects/t17437.nim @@ -0,0 +1,22 @@ +discard """ + cmd: "nim check $file" + errormsg: "" + nimout: ''' +t17437.nim(20, 16) Error: undeclared identifier: 'x' +t17437.nim(20, 16) Error: expression 'x' has no type (or is ambiguous) +t17437.nim(20, 19) Error: incorrect object construction syntax +t17437.nim(20, 19) Error: incorrect object construction syntax +t17437.nim(20, 12) Error: expression '' has no type (or is ambiguous) +''' +""" + +# bug #17437 invalid object construction should result in error + +type + V = ref object + x, y: int + +proc m = + var v = V(x: x, y) + +m() From 89e0e0f27fe937b0eb7386f6f49f4b66f1d27edb Mon Sep 17 00:00:00 2001 From: Danil Yarantsev Date: Mon, 29 Mar 2021 14:06:23 +0300 Subject: [PATCH 1820/3768] Small doc fixes in net (#17566) --- lib/pure/net.nim | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index ca109dc22..9be9c6acb 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -1427,7 +1427,7 @@ proc recv*(socket: Socket, data: var string, size: int, timeout = -1, flags = {SocketFlag.SafeDisconn}): int = ## Higher-level version of `recv`. ## - ## Reads **up to** `size` bytes from `socket` into `buf`. + ## Reads **up to** `size` bytes from `socket` into `data`. ## ## For buffered sockets this function will attempt to read all the requested ## data. It will read this data in `BufferSize` chunks. @@ -1444,8 +1444,6 @@ proc recv*(socket: Socket, data: var string, size: int, timeout = -1, ## A timeout may be specified in milliseconds, if enough data is not received ## within the time specified a TimeoutError exception will be raised. ## - ## **Note**: `data` must be initialised. - ## ## .. warning:: Only the `SafeDisconn` flag is currently supported. data.setLen(size) result = @@ -1464,7 +1462,7 @@ proc recv*(socket: Socket, size: int, timeout = -1, flags = {SocketFlag.SafeDisconn}): string {.inline.} = ## Higher-level version of `recv` which returns a string. ## - ## Reads **up to** `size` bytes from `socket` into `buf`. + ## Reads **up to** `size` bytes from `socket` into the result. ## ## For buffered sockets this function will attempt to read all the requested ## data. It will read this data in `BufferSize` chunks. From 04520c0ce4ed38a3fbae37731a4979480144c77b Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 29 Mar 2021 04:07:14 -0700 Subject: [PATCH 1821/3768] remove std/ prefix from doc/lib.rst now that canonical import is shown in module docs (#17543) * remove std/ prefix from doc/lib.rst now that canonical import is shown in module docs * cleanup top docs for std/sha1 --- doc/lib.rst | 26 +++++++++++++------------- lib/std/sha1.nim | 27 ++++++++++----------------- 2 files changed, 23 insertions(+), 30 deletions(-) diff --git a/doc/lib.rst b/doc/lib.rst index 62e02815c..15b4f70d6 100644 --- a/doc/lib.rst +++ b/doc/lib.rst @@ -90,14 +90,14 @@ Algorithms * `algorithm `_ This module implements some common generic algorithms like sort or binary search. -* `std/enumutils `_ +* `enumutils `_ This module adds functionality for the built-in `enum` type. * `sequtils `_ This module implements operations for the built-in `seq` type which were inspired by functional programming languages. -* `std/setutils `_ +* `setutils `_ This module adds functionality for the built-in `set` type. @@ -125,7 +125,7 @@ Collections * `options `_ The option type encapsulates an optional value. -* `std/packedsets `_ +* `packedsets `_ Efficient implementation of a set of ordinals as a sparse bit set. * `sets `_ @@ -147,7 +147,7 @@ String handling * `cstrutils `_ Utilities for `cstring` handling. -* `std/editdistance `_ +* `editdistance `_ This module contains an algorithm to compute the edit distance between two Unicode strings. @@ -197,14 +197,14 @@ String handling It provides a single proc that does Unicode to ASCII transliterations. Based on Python's Unidecode module. -* `std/wordwrap `_ +* `wordwrap `_ This module contains an algorithm to wordwrap a Unicode string. Time handling ------------- -* `std/monotimes `_ +* `monotimes `_ The `monotimes` module implements monotonic timestamps. * `times `_ @@ -272,7 +272,7 @@ Math libraries * `random `_ Fast and tiny random number generator. -* `std/sysrand `_ +* `sysrand `_ Cryptographically secure pseudorandom number generator. * `rationals `_ @@ -281,7 +281,7 @@ Math libraries * `stats `_ Statistical analysis -* `std/sums `_ +* `sums `_ Accurate summation functions. @@ -357,7 +357,7 @@ Parsers * `json `_ High-performance JSON parser. -* `std/jsonutils `_ +* `jsonutils `_ This module implements a hookable (de)serialization for arbitrary types. * `lexbase `_ @@ -448,7 +448,7 @@ Hashing produce a globally distributed unique ID. This implementation was extracted from the Mongodb interface and it thus binary compatible with a Mongo OID. -* `std/sha1 `_ +* `sha1 `_ This module implements a sha1 encoder and decoder. @@ -465,7 +465,7 @@ Miscellaneous * `coro `_ This module implements experimental coroutines in Nim. -* `std/enumerate `_ +* `enumerate `_ This module implements `enumerate` syntactic sugar based on Nim's macro system. * `logging `_ @@ -480,10 +480,10 @@ Miscellaneous * `unittest `_ Implements a Unit testing DSL. -* `std/varints `_ +* `varints `_ Decode variable-length integers that are compatible with SQLite. -* `std/with `_ +* `with `_ This module implements the `with` macro for easy function chaining. diff --git a/lib/std/sha1.nim b/lib/std/sha1.nim index b74b285f8..9c5efed1f 100644 --- a/lib/std/sha1.nim +++ b/lib/std/sha1.nim @@ -6,33 +6,26 @@ # See the file "copying.txt", included in this # distribution, for details about the copyright. # -## **Note:** Import `std/sha1` to use this module. -## ## [SHA-1 (Secure Hash Algorithm 1)](https://en.wikipedia.org/wiki/SHA-1) ## is a cryptographic hash function which takes an input and produces ## a 160-bit (20-byte) hash value known as a message digest. ## -## Basic usage -## =========== -## -runnableExamples: - let accessName = secureHash("John Doe") - assert $accessName == "AE6E4D1209F17B460503904FAD297B31E9CF6362" - -## .. code-block:: -## let -## a = secureHashFile("myFile.nim") -## b = parseSecureHash("10DFAEBF6BFDBC7939957068E2EFACEC4972933C") -## -## if a == b: -## echo "Files match" -## ## See also ## ======== ## * `base64 module`_ implements a Base64 encoder and decoder ## * `hashes module`_ for efficient computations of hash values for diverse Nim types ## * `md5 module`_ implements the MD5 checksum algorithm +runnableExamples: + let accessName = secureHash("John Doe") + assert $accessName == "AE6E4D1209F17B460503904FAD297B31E9CF6362" + +runnableExamples("-r:off"): + let + a = secureHashFile("myFile.nim") + b = parseSecureHash("10DFAEBF6BFDBC7939957068E2EFACEC4972933C") + assert a == b, "files don't match" + import std/strutils from std/endians import bigEndian32, bigEndian64 From 81e54c1d30b7651851b9de56d76b9af81e192504 Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 29 Mar 2021 20:22:29 +0800 Subject: [PATCH 1822/3768] Add `hasClosure` to `std/typetraits` (#17501) * fix nim js cmp fails at CT * Add `hasClosure` to `std/effecttraits` * type * Update changelog.md Co-authored-by: Timothee Cour * Update lib/std/effecttraits.nim Co-authored-by: Timothee Cour Co-authored-by: Andreas Rumpf --- changelog.md | 2 + compiler/vmops.nim | 4 ++ lib/pure/typetraits.nim | 11 +++++ tests/stdlib/ttypetraits.nim | 92 ++++++++++++++++++++++++++++++++++++ 4 files changed, 109 insertions(+) create mode 100644 tests/stdlib/ttypetraits.nim diff --git a/changelog.md b/changelog.md index d3335c2a3..be130f780 100644 --- a/changelog.md +++ b/changelog.md @@ -246,6 +246,8 @@ - Added dollar `$` and `len` for `jsre.RegExp`. +- Added `hasClosure` to `std/typetraits`. + ## Language changes diff --git a/compiler/vmops.nim b/compiler/vmops.nim index 5748b41b3..04356fc76 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -316,3 +316,7 @@ proc registerAdditionalOps*(c: PCtx) = let fn = getNode(a, 0) setResult(a, (fn.typ != nil and tfNoSideEffect in fn.typ.flags) or (fn.kind == nkSym and fn.sym.kind == skFunc)) + + registerCallback c, "stdlib.typetraits.hasClosureImpl", proc (a: VmArgs) = + let fn = getNode(a, 0) + setResult(a, fn.kind == nkClosure or (fn.typ != nil and fn.typ.callConv == ccClosure)) diff --git a/lib/pure/typetraits.nim b/lib/pure/typetraits.nim index 6827e23b1..2527dc26e 100644 --- a/lib/pure/typetraits.nim +++ b/lib/pure/typetraits.nim @@ -263,3 +263,14 @@ since (1, 1): type T2 = T genericParamsImpl(T2) + + +proc hasClosureImpl(n: NimNode): bool = discard "see compiler/vmops.nim" + +proc hasClosure*(fn: NimNode): bool {.since: (1, 5, 1).} = + ## Return true if the func/proc/etc `fn` has `closure`. + ## `fn` has to be a resolved symbol of kind `nnkSym`. This + ## implies that the macro that calls this proc should accept `typed` + ## arguments and not `untyped` arguments. + expectKind fn, nnkSym + result = hasClosureImpl(fn) diff --git a/tests/stdlib/ttypetraits.nim b/tests/stdlib/ttypetraits.nim new file mode 100644 index 000000000..de8259ab0 --- /dev/null +++ b/tests/stdlib/ttypetraits.nim @@ -0,0 +1,92 @@ +discard """ + targets: "c cpp js" +""" + +import std/typetraits + + + +macro testClosure(fn: typed, flag: static bool) = + if flag: + doAssert hasClosure(fn) + else: + doAssert not hasClosure(fn) + +block: + proc h1() = + echo 1 + + testClosure(h1, false) + + proc h2() {.nimcall.} = + echo 2 + + testClosure(h2, false) + + +block: + proc fn(): auto = + proc hello() {.nimcall.} = + echo 3 + hello + + let name = fn() + testClosure(name, false) + +block: + proc fn(): auto = + proc hello() = + echo 3 + hello + + let name = fn() + testClosure(name, false) + +block: + proc fn(): auto = + var x = 0 + proc hello() = + echo 3 + inc x + hello + + let name = fn() + testClosure(name, true) + +block: + proc fn(): auto = + var x = 0 + proc hello() {.closure.} = + echo 3 + inc x + hello + + let name = fn() + testClosure(name, true) + +block: + proc fn(): auto = + var x = 0 + proc hello() {.closure.} = + echo 3 + inc x + hello + + let name = fn() + testClosure(name, true) + + let name2 = name + testClosure(name2, true) + +block: + iterator hello(): int = + yield 1 + + testClosure(hello, false) + +when not defined(js): + block: + iterator hello(): int {.closure.}= + yield 1 + + testClosure(hello, true) From e2269f9216e7d46bd446448c7bbeb5fd8ca38d1f Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 29 Mar 2021 20:39:49 +0800 Subject: [PATCH 1823/3768] [docs]fix #17473 (#17565) * fix nim js cmp fails at CT * Add `hasClosure` to `std/effecttraits` * type * Update changelog.md Co-authored-by: Timothee Cour * fix #14011 * Delete ttypetraits.nim * Apply suggestions from code review * fix #17473 * Revert "fix #14011" This reverts commit 0eed97a84b172b198bf4e6de69c04b84ef9d9f93. * Update lib/system.nim Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Co-authored-by: Timothee Cour Co-authored-by: Andreas Rumpf Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> --- lib/pure/strutils.nim | 12 ++++++++++-- lib/system.nim | 5 +++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 4098749ce..3680b11de 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -2198,13 +2198,21 @@ func insertSep*(s: string, sep = '_', digits = 3): string {.rtl, func escape*(s: string, prefix = "\"", suffix = "\""): string {.rtl, extern: "nsuEscape".} = - ## Escapes a string `s`. See `system.addEscapedChar - ## `_ for the escaping scheme. + ## Escapes a string `s`. + ## + ## .. note:: The escaping scheme is different from + ## `system.addEscapedChar`. + ## + ## * replaces `'\0'..'\31'` and `'\127'..'\255'` by `\xHH` where `HH` is its hexadecimal value + ## * replaces ``\`` by `\\` + ## * replaces `'` by `\'` + ## * replaces `"` by `\"` ## ## The resulting string is prefixed with `prefix` and suffixed with `suffix`. ## Both may be empty strings. ## ## See also: + ## * `addEscapedChar proc`_ ## * `unescape func<#unescape,string,string,string>`_ for the opposite ## operation result = newStringOfCap(s.len + s.len shr 2) diff --git a/lib/system.nim b/lib/system.nim index 27f976119..ba27da0c7 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2839,12 +2839,13 @@ proc addEscapedChar*(s: var string, c: char) {.noSideEffect, inline.} = ## * replaces any `\r` by `\\r` ## * replaces any `\e` by `\\e` ## * replaces any other character not in the set `{\21..\126}` - ## by `\xHH` where `HH` is its hexadecimal value. + ## by `\xHH` where `HH` is its hexadecimal value ## ## The procedure has been designed so that its output is usable for many ## different common syntaxes. ## - ## **Note**: This is **not correct** for producing Ansi C code! + ## .. warning:: This is **not correct** for producing ANSI C code! + ## case c of '\a': s.add "\\a" # \x07 of '\b': s.add "\\b" # \x08 From 1a407402a4614fc9100617f71c2d3c9b8a8367a5 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Mon, 29 Mar 2021 16:03:54 +0200 Subject: [PATCH 1824/3768] Typo in vm.nim --- compiler/vm.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/vm.nim b/compiler/vm.nim index c2ff798a7..d97d2d6e6 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -2221,7 +2221,7 @@ proc setupCompileTimeVar*(module: PSym; idgen: IdGenerator; g: ModuleGraph; n: P discard evalConstExprAux(module, idgen, g, nil, n, emStaticStmt) proc prepareVMValue(arg: PNode): PNode = - ## strip nkExprColonExpr from tuple values recurively. That is how + ## strip nkExprColonExpr from tuple values recursively. That is how ## they are expected to be stored in the VM. # Early abort without copy. No transformation takes place. From cfff27529e4ec129daad602d945a2b222145e922 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 29 Mar 2021 16:23:19 +0200 Subject: [PATCH 1825/3768] added nkError to the AST (#17567) * added nkError to the AST * Update lib/core/macros.nim Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> * Update compiler/ast.nim Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> --- compiler/ast.nim | 1 + compiler/errorhandling.nim | 79 +++++++++++++++++++++++++++++++++++ compiler/renderer.nim | 4 ++ compiler/sempass2.nim | 4 +- lib/core/macros.nim | 3 +- tests/misc/tinvalidnewseq.nim | 2 +- 6 files changed, 90 insertions(+), 3 deletions(-) create mode 100644 compiler/errorhandling.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index 1f3d5f129..7d13956e9 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -221,6 +221,7 @@ type nkBreakState, # special break statement for easier code generation nkFuncDef, # a func nkTupleConstr # a tuple constructor + nkError # erroneous AST node nkModuleRef # for .rod file support: A (moduleId, itemId) pair nkReplayAction # for .rod file support: A replay action nkNilRodNode # for .rod file support: a 'nil' PNode diff --git a/compiler/errorhandling.nim b/compiler/errorhandling.nim new file mode 100644 index 000000000..d7092d477 --- /dev/null +++ b/compiler/errorhandling.nim @@ -0,0 +1,79 @@ +# +# +# The Nim Compiler +# (c) Copyright 2021 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module contains support code for new-styled error +## handling via an `nkError` node kind. + +import ast, renderer, options, lineinfos, strutils, types + +type + ErrorKind* = enum ## expand as you need. + RawTypeMismatchError + ExpressionCannotBeCalled + CustomError + WrongNumberOfArguments + AmbiguousCall + +proc errorSubNode*(n: PNode): PNode = + case n.kind + of nkEmpty..nkNilLit: + result = nil + of nkError: + result = n + else: + result = nil + for i in 0.. 1 + let wrongNode = n[0] + case ErrorKind(n[1].intVal) + of RawTypeMismatchError: + result = "type mismatch" + of ExpressionCannotBeCalled: + result = "expression '$1' cannot be called" % wrongNode[0].renderTree + of CustomError: + result = n[2].strVal + of WrongNumberOfArguments: + result = "wrong number of arguments" + of AmbiguousCall: + let a = n[2].sym + let b = n[3].sym + var args = "(" + for i in 1.. 1: args.add(", ") + args.add(typeToString(wrongNode[i].typ)) + args.add(")") + result = "ambiguous call; both $1 and $2 match for: $3" % [ + getProcHeader(config, a), + getProcHeader(config, b), + args] diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 9ca485f6e..9a599f6fc 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -1645,6 +1645,10 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = gsons(g, n, c, 0) of nkTypeClassTy: gTypeClassTy(g, n) + of nkError: + putWithSpace(g, tkSymbol, "error") + #gcomma(g, n, c) + gsub(g, n[0], c) else: #nkNone, nkExplicitTypeListCall: internalError(g.config, n.info, "rnimsyn.gsub(" & $n.kind & ')') diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index f269afe4c..a1572c85e 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -10,7 +10,7 @@ import intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees, wordrecg, strutils, options, guards, lineinfos, semfold, semdata, - modulegraphs, varpartitions, typeallowed, nilcheck + modulegraphs, varpartitions, typeallowed, nilcheck, errorhandling when defined(useDfa): import dfa @@ -1136,6 +1136,8 @@ proc track(tracked: PEffects, n: PNode) = dec tracked.leftPartOfAsgn for i in 1 ..< n.len: track(tracked, n[i]) inc tracked.leftPartOfAsgn + of nkError: + localError(tracked.config, n.info, errorToString(tracked.config, n)) else: for i in 0.. Date: Mon, 29 Mar 2021 07:25:27 -0700 Subject: [PATCH 1826/3768] fix https://github.com/nim-lang/RFCs/issues/352: show top-level import for top-level runnableExamples in generated docs (#17542) * fix https://github.com/nim-lang/RFCs/issues/352: show top-level import for top-level runnableExamples in generated docs * use canonical imoprt * fix test --- compiler/docgen.nim | 25 +++++++++++++------- nimdoc/testproject/expected/testproject.html | 12 ++++++---- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index b24a24a2b..f1a9750b7 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -505,7 +505,7 @@ proc runAllExamples(d: PDoc) = proc quoted(a: string): string = result.addQuoted(a) -proc prepareExample(d: PDoc; n: PNode): tuple[rdoccmd: string, code: string] = +proc prepareExample(d: PDoc; n: PNode, topLevel: bool): tuple[rdoccmd: string, code: string] = ## returns `rdoccmd` and source code for this runnableExamples var rdoccmd = "" if n.len < 2 or n.len > 3: globalError(d.conf, n.info, "runnableExamples invalid") @@ -556,7 +556,14 @@ $3 if rdoccmd notin d.exampleGroups: d.exampleGroups[rdoccmd] = ExampleGroup(rdoccmd: rdoccmd, docCmd: d.conf.docCmd, index: d.exampleGroups.len) d.exampleGroups[rdoccmd].code.add "import $1\n" % outp.string.quoted - result = (rdoccmd, code) + + var codeShown: string + if topLevel: # refs https://github.com/nim-lang/RFCs/issues/352 + let title = canonicalImport(d.conf, AbsoluteFile d.filename) + codeShown = "import $#\n$#" % [title, code] + else: + codeShown = code + result = (rdoccmd, codeShown) when false: proc extractImports(n: PNode; result: PNode) = if n.kind in {nkImportStmt, nkImportExceptStmt, nkFromStmt}: @@ -576,7 +583,7 @@ type RunnableState = enum rsRunnable rsDone -proc getAllRunnableExamplesImpl(d: PDoc; n: PNode, dest: var Rope, state: RunnableState): RunnableState = +proc getAllRunnableExamplesImpl(d: PDoc; n: PNode, dest: var Rope, state: RunnableState, topLevel: bool): RunnableState = ##[ Simple state machine to tell whether we render runnableExamples and doc comments. This is to ensure that we can interleave runnableExamples and doc comments freely; @@ -601,7 +608,7 @@ proc getAllRunnableExamplesImpl(d: PDoc; n: PNode, dest: var Rope, state: Runnab of nkCallKinds: if isRunnableExamples(n[0]) and n.len >= 2 and n.lastSon.kind == nkStmtList and state in {rsStart, rsComment, rsRunnable}: - let (rdoccmd, code) = prepareExample(d, n) + let (rdoccmd, code) = prepareExample(d, n, topLevel) var msg = "Example:" if rdoccmd.len > 0: msg.add " cmd: " & rdoccmd dispA(d.conf, dest, "\n

                                      $1

                                      \n", @@ -652,19 +659,19 @@ proc getRoutineBody(n: PNode): PNode = proc getAllRunnableExamples(d: PDoc, n: PNode, dest: var Rope) = var n = n var state = rsStart - template fn(n2) = - state = getAllRunnableExamplesImpl(d, n2, dest, state) + template fn(n2, topLevel) = + state = getAllRunnableExamplesImpl(d, n2, dest, state, topLevel) dest.add genComment(d, n).rope case n.kind of routineDefs: n = n.getRoutineBody case n.kind - of nkCommentStmt, nkCallKinds: fn(n) + of nkCommentStmt, nkCallKinds: fn(n, topLevel = false) else: for i in 0..This is the top level module.

                                      Example:

                                      -
                                      import subdir / subdir_b / utils
                                      +
                                      import testproject
                                      +import subdir / subdir_b / utils
                                       doAssert bar(3, 4) == 7
                                       foo(enumValueA, enumValueB)
                                       # bug #11078
                                       for x in "xx": discard
                                      top2

                                      Example:

                                      -
                                      discard "in top2"
                                      top2 after +
                                      import testproject
                                      +discard "in top2"
                                      top2 after

                                      Example:

                                      -
                                      discard "in top3"
                                      top3 after +
                                      import testproject
                                      +discard "in top3"
                                      top3 after

                                      Example:

                                      -
                                      assert 3*2 == 6

                                      +
                                      import testproject
                                      +assert 3*2 == 6

                                      Imports

                                      From 7ad49950bd5ec05f145dc43ae35f51127ea76366 Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 29 Mar 2021 23:54:28 +0800 Subject: [PATCH 1827/3768] [os:standalone]fix #14011 (#17564) --- lib/system/chcks.nim | 10 ++++++++-- tests/gc/panicoverride.nim | 14 ++++++++++++++ tests/gc/tstandalone.nim | 14 ++++++++++++++ 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 tests/gc/panicoverride.nim create mode 100644 tests/gc/tstandalone.nim diff --git a/lib/system/chcks.nim b/lib/system/chcks.nim index e0a8bd78e..255d97cb2 100644 --- a/lib/system/chcks.nim +++ b/lib/system/chcks.nim @@ -29,10 +29,16 @@ proc raiseFieldError(f: string) {.compilerproc, noinline.} = sysFatal(FieldDefect, f) proc raiseRangeErrorI(i, a, b: BiggestInt) {.compilerproc, noinline.} = - sysFatal(RangeDefect, "value out of range: " & $i & " notin " & $a & " .. " & $b) + when defined(standalone): + sysFatal(RangeDefect, "value out of range") + else: + sysFatal(RangeDefect, "value out of range: " & $i & " notin " & $a & " .. " & $b) proc raiseRangeErrorF(i, a, b: float) {.compilerproc, noinline.} = - sysFatal(RangeDefect, "value out of range: " & $i & " notin " & $a & " .. " & $b) + when defined(standalone): + sysFatal(RangeDefect, "value out of range") + else: + sysFatal(RangeDefect, "value out of range: " & $i & " notin " & $a & " .. " & $b) proc raiseRangeErrorU(i, a, b: uint64) {.compilerproc, noinline.} = # todo: better error reporting diff --git a/tests/gc/panicoverride.nim b/tests/gc/panicoverride.nim new file mode 100644 index 000000000..0f28b0b72 --- /dev/null +++ b/tests/gc/panicoverride.nim @@ -0,0 +1,14 @@ + +proc printf(frmt: cstring) {.varargs, importc, header: "", cdecl.} +proc exit(code: int) {.importc, header: "", cdecl.} + +{.push stack_trace: off, profiler:off.} + +proc rawoutput(s: string) = + printf("%s\n", s) + +proc panic(s: string) {.noreturn.} = + rawoutput(s) + exit(1) + +{.pop.} \ No newline at end of file diff --git a/tests/gc/tstandalone.nim b/tests/gc/tstandalone.nim new file mode 100644 index 000000000..41dad9ba4 --- /dev/null +++ b/tests/gc/tstandalone.nim @@ -0,0 +1,14 @@ +discard """ + matrix: "--os:standalone --gc:none" + exitcode: 1 + output: "value out of range" +""" + +type + rangeType = range[0..1] + +var + r: rangeType = 0 + i = 2 + +r = rangeType(i) From 3f9c51a332dc798e1f4d61480ca8b6048ab281cd Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 30 Mar 2021 00:28:55 +0800 Subject: [PATCH 1828/3768] [nim check]fix #17460 (#17569) --- compiler/semstmts.nim | 2 ++ tests/errmsgs/t17460.nim | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 tests/errmsgs/t17460.nim diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index fee43162e..bbfd49e2a 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -726,6 +726,8 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode = if n[0].kind == nkVarTuple: if n[0].len-1 != iterAfterVarLent.len: localError(c.config, n[0].info, errWrongNumberOfVariables) + return errorNode(c, n) + for i in 0.. Date: Mon, 29 Mar 2021 19:32:49 +0300 Subject: [PATCH 1829/3768] RST: enable parsing of prefix roles (ref #17340) (#17514) --- doc/contributing.rst | 9 ++++++++ lib/packages/docutils/rst.nim | 38 +++++++++++++++++--------------- tests/stdlib/trstgen.nim | 41 +++++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 18 deletions(-) diff --git a/doc/contributing.rst b/doc/contributing.rst index b7188a436..279a4ee94 100644 --- a/doc/contributing.rst +++ b/doc/contributing.rst @@ -285,6 +285,15 @@ or the first is preferred. +When you specify an *RST role* (highlighting/interpretation marker) do it +in the postfix form for uniformity, that is after \`text in backticks\`. +For example an ``:idx:`` role for referencing a topic ("SQLite" in the +example below) from `Nim Index`_ can be used in doc comment this way: + +.. code-block:: nim + ## A higher level `SQLite`:idx: database wrapper. + +.. _`Nim Index`: https://nim-lang.org/docs/theindex.html Best practices ============== diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 6845f61b9..d331c2c12 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -68,7 +68,7 @@ ## substitution references, standalone hyperlinks, ## internal links (inline and outline) ## + \`interpreted text\` with roles ``:literal:``, ``:strong:``, -## ``emphasis``, ``:sub:``/``:subscript:``, ``:sup:``/``:supscript:`` +## ``emphasis``, ``:sub:``/``:subscript:``, ``:sup:``/``:superscript:`` ## (see `RST roles list`_ for description). ## + inline internal targets ## @@ -1018,6 +1018,16 @@ proc fixupEmbeddedRef(n, a, b: PRstNode) = for i in countup(0, sep - incr): a.add(n.sons[i]) for i in countup(sep + 1, n.len - 2): b.add(n.sons[i]) +proc whichRole(sym: string): RstNodeKind = + case sym + of "idx": result = rnIdx + of "literal": result = rnInlineLiteral + of "strong": result = rnStrongEmphasis + of "emphasis": result = rnEmphasis + of "sub", "subscript": result = rnSub + of "sup", "superscript": result = rnSup + else: result = rnGeneralRole + proc parsePostfix(p: var RstParser, n: PRstNode): PRstNode = var newKind = n.kind var newSons = n.sons @@ -1042,22 +1052,8 @@ proc parsePostfix(p: var RstParser, n: PRstNode): PRstNode = result = newRstNode(newKind, newSons) elif match(p, p.idx, ":w:"): # a role: - if nextTok(p).symbol == "idx": - newKind = rnIdx - elif nextTok(p).symbol == "literal": - newKind = rnInlineLiteral - elif nextTok(p).symbol == "strong": - newKind = rnStrongEmphasis - elif nextTok(p).symbol == "emphasis": - newKind = rnEmphasis - elif nextTok(p).symbol == "sub" or - nextTok(p).symbol == "subscript": - newKind = rnSub - elif nextTok(p).symbol == "sup" or - nextTok(p).symbol == "supscript": - newKind = rnSup - else: - newKind = rnGeneralRole + newKind = whichRole(nextTok(p).symbol) + if newKind == rnGeneralRole: let newN = newRstNode(rnInner, n.sons) newSons = @[newN, newLeaf(nextTok(p).symbol)] inc p.idx, 3 @@ -1318,6 +1314,12 @@ proc parseInline(p: var RstParser, father: PRstNode) = var n = newRstNode(rnInlineLiteral) parseUntil(p, n, "``", false) father.add(n) + elif match(p, p.idx, ":w:") and p.tok[p.idx+3].symbol == "`": + let k = whichRole(nextTok(p).symbol) + let n = newRstNode(k) + inc p.idx, 3 + parseUntil(p, n, "`", false) # bug #17260 + father.add(n) elif isInlineMarkupStart(p, "`"): var n = newRstNode(rnInterpretedText) parseUntil(p, n, "`", false) # bug #17260 @@ -1677,7 +1679,7 @@ proc whichSection(p: RstParser): RstNodeKind = elif predNL(p) and currentTok(p).symbol in ["+", "*", "-"] and nextTok(p).kind == tkWhite: result = rnBulletList - elif match(p, p.idx, ":w:") and predNL(p): + elif match(p, p.idx, ":w:E") and predNL(p): # (currentTok(p).symbol == ":") result = rnFieldList elif match(p, p.idx, "(e) ") or match(p, p.idx, "e) ") or diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim index 0f3890faa..da01c30d2 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -1326,6 +1326,47 @@ Test1 output) check("""-doption""" in output) + + test "Roles: subscript prefix/postfix": + let expected = "See some text." + check "See :subscript:`some text`.".toHtml == expected + check "See `some text`:subscript:.".toHtml == expected + + test "Roles: correct parsing from beginning of line": + let expected = "3He is an isotope of helium." + check """:superscript:`3`\ He is an isotope of helium.""".toHtml == expected + check """:sup:`3`\ He is an isotope of helium.""".toHtml == expected + check """`3`:sup:\ He is an isotope of helium.""".toHtml == expected + check """`3`:superscript:\ He is an isotope of helium.""".toHtml == expected + + test "(not) Roles: check escaping 1": + let expected = """See :subscript:""" & + """some text.""" + check """See \:subscript:`some text`.""".toHtml == expected + check """See :subscript\:`some text`.""".toHtml == expected + + test "(not) Roles: check escaping 2": + check("""See :subscript:\`some text\`.""".toHtml == + "See :subscript:`some text`.") + + test "Field list": + check(":field: text".toHtml == + """""" & + """""" & + """""" & + """""" & "\n
                                      field: text
                                      ") + + test "Field list: body after newline": + let output = dedent """ + :field: + text1""".toHtml + check "field:" in output + check "" in output + + test "Field list (incorrect)": + check ":field:text".toHtml == ":field:text" + suite "RST/Code highlight": test "Basic Python code highlight": let pythonCode = """ From 1f1ef85eb0980f2e1077638a502c0754c3e2b80d Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 30 Mar 2021 02:48:06 +0800 Subject: [PATCH 1830/3768] [std/uri]fix #17481 (#17568) --- lib/pure/uri.nim | 2 +- tests/stdlib/turi.nim | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim index 920529ecf..4ac04d02e 100644 --- a/lib/pure/uri.nim +++ b/lib/pure/uri.nim @@ -294,7 +294,7 @@ func parseUri*(uri: string, result: var Uri) = var i = 0 # Check if this is a reference URI (relative URI) - let doubleSlash = uri.len > 1 and uri[1] == '/' + let doubleSlash = uri.len > 1 and uri[0] == '/' and uri[1] == '/' if i < uri.len and uri[i] == '/': # Make sure `uri` doesn't begin with '//'. if not doubleSlash: diff --git a/tests/stdlib/turi.nim b/tests/stdlib/turi.nim index 1a6f37520..d1a30bbfc 100644 --- a/tests/stdlib/turi.nim +++ b/tests/stdlib/turi.nim @@ -283,5 +283,16 @@ template main() = doAssert toSeq(decodeQuery("a=1&b=0")) == @[("a", "1"), ("b", "0")] doAssert toSeq(decodeQuery("a=1&b=2c=6")) == @[("a", "1"), ("b", "2c=6")] + block: # bug #17481 + let u1 = parseUri("./") + let u2 = parseUri("./path") + let u3 = parseUri("a/path") + doAssert u1.scheme.len == 0 + doAssert u1.path == "./" + doAssert u2.scheme.len == 0 + doAssert u2.path == "./path" + doAssert u3.scheme.len == 0 + doAssert u3.path == "a/path" + static: main() main() From ed126794b6af8b39960a7acc603cee1abc73da4a Mon Sep 17 00:00:00 2001 From: Clyybber Date: Mon, 29 Mar 2021 21:16:11 +0200 Subject: [PATCH 1831/3768] Fix #17412 (#17560) * Fix #17412 * Address review --- compiler/ccgtypes.nim | 28 ++----------------------- compiler/ccgutils.nim | 40 ++++++++++++++++++++++++++++++++++-- compiler/liftdestructors.nim | 11 +++++++++- 3 files changed, 50 insertions(+), 29 deletions(-) diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index b0c3aa3b1..761fd5bf8 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -256,32 +256,8 @@ proc addAbiCheck(m: BModule, t: PType, name: Rope) = m.s[cfsTypeInfo].addf("NIM_STATIC_ASSERT(sizeof($1) == $2, $3);$n", [name, rope(size), msg2.rope]) # see `testCodegenABICheck` for example error message it generates -proc ccgIntroducedPtr(conf: ConfigRef; s: PSym, retType: PType): bool = - var pt = skipTypes(s.typ, typedescInst) - assert skResult != s.kind - - if tfByRef in pt.flags: return true - elif tfByCopy in pt.flags: return false - case pt.kind - of tyObject: - if s.typ.sym != nil and sfForward in s.typ.sym.flags: - # forwarded objects are *always* passed by pointers for consistency! - result = true - elif (optByRef in s.options) or (getSize(conf, pt) > conf.target.floatSize * 3): - result = true # requested anyway - elif (tfFinal in pt.flags) and (pt[0] == nil): - result = false # no need, because no subtyping possible - else: - result = true # ordinary objects are always passed by reference, - # otherwise casting doesn't work - of tyTuple: - result = (getSize(conf, pt) > conf.target.floatSize*3) or (optByRef in s.options) - else: - result = false - # first parameter and return type is 'lent T'? --> use pass by pointer - if s.position == 0 and retType != nil and retType.kind == tyLent: - result = not (pt.kind in {tyVar, tyArray, tyOpenArray, tyVarargs, tyRef, tyPtr, tyPointer} or - pt.kind == tySet and mapSetType(conf, pt) == ctArray) +from ccgutils import ccgIntroducedPtr +export ccgIntroducedPtr proc fillResult(conf: ConfigRef; param: PNode) = fillLoc(param.sym.loc, locParam, param, ~"Result", diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim index f2a8c1e36..06b75a20f 100644 --- a/compiler/ccgutils.nim +++ b/compiler/ccgutils.nim @@ -10,8 +10,8 @@ # This module declares some helpers for the C code generator. import - ast, hashes, strutils, msgs, wordrecg, - platform, trees, options + ast, types, hashes, strutils, msgs, wordrecg, + platform, trees, options, cgendata proc getPragmaStmt*(n: PNode, w: TSpecialWord): PNode = case n.kind @@ -106,3 +106,39 @@ proc mangle*(name: string): string = requiresUnderscore = true if requiresUnderscore: result.add "_" + +proc mapSetType(conf: ConfigRef; typ: PType): TCTypeKind = + case int(getSize(conf, typ)) + of 1: result = ctInt8 + of 2: result = ctInt16 + of 4: result = ctInt32 + of 8: result = ctInt64 + else: result = ctArray + +proc ccgIntroducedPtr*(conf: ConfigRef; s: PSym, retType: PType): bool = + var pt = skipTypes(s.typ, typedescInst) + assert skResult != s.kind + + if tfByRef in pt.flags: return true + elif tfByCopy in pt.flags: return false + case pt.kind + of tyObject: + if s.typ.sym != nil and sfForward in s.typ.sym.flags: + # forwarded objects are *always* passed by pointers for consistency! + result = true + elif (optByRef in s.options) or (getSize(conf, pt) > conf.target.floatSize * 3): + result = true # requested anyway + elif (tfFinal in pt.flags) and (pt[0] == nil): + result = false # no need, because no subtyping possible + else: + result = true # ordinary objects are always passed by reference, + # otherwise casting doesn't work + of tyTuple: + result = (getSize(conf, pt) > conf.target.floatSize*3) or (optByRef in s.options) + else: + result = false + # first parameter and return type is 'lent T'? --> use pass by pointer + if s.position == 0 and retType != nil and retType.kind == tyLent: + result = not (pt.kind in {tyVar, tyArray, tyOpenArray, tyVarargs, tyRef, tyPtr, tyPointer} or + pt.kind == tySet and mapSetType(conf, pt) == ctArray) + diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 980e77e4b..bb24fed34 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -11,7 +11,7 @@ ## (``=sink``, ``=``, ``=destroy``, ``=deepCopy``). import modulegraphs, lineinfos, idents, ast, renderer, semdata, - sighashes, lowerings, options, types, msgs, magicsys, tables + sighashes, lowerings, options, types, msgs, magicsys, tables, ccgutils from trees import isCaseObj @@ -230,6 +230,15 @@ proc fillBodyObjT(c: var TLiftCtx; t: PType, body, x, y: PNode) = # for every field (dependent on dest.kind): # `=` dest.field, src.field # =destroy(blob) + var dummy = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), nextSymId c.idgen, c.fn, c.info) + dummy.typ = y.typ + if ccgIntroducedPtr(c.g.config, dummy, y.typ): + # Because of potential aliasing when the src param is passed by ref, we need to check for equality here, + # because the wasMoved(dest) call would zero out src, if dest aliases src. + var cond = newTree(nkCall, newSymNode(c.g.getSysMagic(c.info, "==", mEqRef)), + newTreeIT(nkAddr, c.info, makePtrType(c.fn, x.typ, c.idgen), x), newTreeIT(nkAddr, c.info, makePtrType(c.fn, y.typ, c.idgen), y)) + cond.typ = getSysType(c.g, x.info, tyBool) + body.add genIf(c, cond, newTreeI(nkReturnStmt, c.info, newNodeI(nkEmpty, c.info))) var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), nextSymId c.idgen, c.fn, c.info) temp.typ = x.typ incl(temp.flags, sfFromGeneric) From 35655cd1899ad2d85ca93ad7482572f0f345d8fb Mon Sep 17 00:00:00 2001 From: Dylan Modesitt Date: Mon, 29 Mar 2021 19:12:58 -0400 Subject: [PATCH 1832/3768] Add hasDataBuffered to asyncnet (#16000) Co-authored-by: flywind Co-authored-by: Timothee Cour --- changelog.md | 2 ++ lib/pure/asyncnet.nim | 5 +++++ 2 files changed, 7 insertions(+) diff --git a/changelog.md b/changelog.md index be130f780..2fbff8b41 100644 --- a/changelog.md +++ b/changelog.md @@ -246,6 +246,8 @@ - Added dollar `$` and `len` for `jsre.RegExp`. +- Added `hasDataBuffered` to `asyncnet`. + - Added `hasClosure` to `std/typetraits`. diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index f36c427de..7aeb99777 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -650,6 +650,11 @@ proc bindAddr*(socket: AsyncSocket, port = Port(0), address = "") {. raiseOSError(osLastError()) freeaddrinfo(aiList) +proc hasDataBuffered*(s: AsyncSocket): bool {.since: (1, 5).} = + ## Determines whether an AsyncSocket has data buffered. + # xxx dedup with std/net + s.isBuffered and s.bufLen > 0 and s.currPos != s.bufLen + when defined(posix): proc connectUnix*(socket: AsyncSocket, path: string): owned(Future[void]) = From 159c06e0451f85842b2168886565fa57496a2c68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arne=20D=C3=B6ring?= Date: Tue, 30 Mar 2021 02:06:51 +0200 Subject: [PATCH 1833/3768] unify tuple expressions (#13793) * unify tuple expressions * fix test * fix test * apply feedback * Handle empty tuples * Fix rendering named unary tuple * Protect static NimNode against stripping * Slightly less hacky * Revert "Slightly less hacky" This reverts commit 170c5aec0addc029f637afbc948700ca006b7942. * Slightly less hacky * Cleanup * Fix test * Fix another test * Add condsym * Rebase fallout * changelog: Move from compiler changes to language changes * Add stricter tests * Add empty tuple example to doc/astspec * Fix test Co-authored-by: Clyybber --- changelog.md | 3 +++ compiler/condsyms.nim | 1 + compiler/parser.nim | 15 ++++++----- compiler/renderer.nim | 2 +- compiler/vm.nim | 6 +++++ doc/astspec.txt | 45 ++++++++++++++++++++++++++++--- tests/ast_pattern_matching.nim | 4 +-- tests/astspec/tastspec.nim | 36 ++++++++++++++++++++----- tests/compiler/tprefixmatches.nim | 2 +- tests/lexer/tunary_minus.nim | 2 +- tests/macros/tdumpast.nim | 31 +++++++++++++++++++++ tests/parser/tpostexprblocks.nim | 6 ++--- tests/parser/ttypemodifiers.nim | 15 +++++------ 13 files changed, 136 insertions(+), 32 deletions(-) diff --git a/changelog.md b/changelog.md index 2fbff8b41..7452e3182 100644 --- a/changelog.md +++ b/changelog.md @@ -274,6 +274,9 @@ - Custom numeric literals (e.g. `-128'bignum`) are now supported. +- Tuple expressions are now parsed consistently as + `nnkTupleConstr` node. Will affect macros expecting nodes to be of `nnkPar`. + ## Compiler changes diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 8d11dae7c..9ba97a8ed 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -131,3 +131,4 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasHintAsError") defineSymbol("nimHasSpellSuggest") defineSymbol("nimHasCustomLiterals") + defineSymbol("nimHasUnifiedTuple") diff --git a/compiler/parser.nim b/compiler/parser.nim index b9a6ffb8c..83ea4cbdc 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -421,10 +421,9 @@ proc exprColonEqExprListAux(p: var Parser, endTok: TokType, result: PNode) = var a = exprColonEqExpr(p) result.add(a) if p.tok.tokType != tkComma: break - getTok(p) - # (1,) produces a tuple expression - if endTok == tkParRi and p.tok.tokType == tkParRi and result.kind == nkPar: + elif result.kind == nkPar: result.transitionSonsKind(nkTupleConstr) + getTok(p) skipComment(p, a) optPar(p) eat(p, endTok) @@ -584,7 +583,10 @@ proc parsePar(p: var Parser): PNode = semiStmtList(p, result) elif p.tok.tokType == tkCurlyDotLe: result.add(parseStmtPragma(p)) - elif p.tok.tokType != tkParRi: + elif p.tok.tokType == tkParRi: + # Empty tuple '()' + result.transitionSonsKind(nkTupleConstr) + else: var a = simpleExpr(p) if p.tok.tokType == tkDo: result = postExprBlocks(p, a) @@ -605,13 +607,14 @@ proc parsePar(p: var Parser): PNode = semiStmtList(p, result) else: a = colonOrEquals(p, a) + if a.kind == nkExprColonExpr: + result.transitionSonsKind(nkTupleConstr) result.add(a) if p.tok.tokType == tkComma: getTok(p) skipComment(p, a) # (1,) produces a tuple expression: - if p.tok.tokType == tkParRi: - result.transitionSonsKind(nkTupleConstr) + result.transitionSonsKind(nkTupleConstr) # progress guaranteed while p.tok.tokType != tkParRi and p.tok.tokType != tkEof: var a = exprColonEqExpr(p) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 9a599f6fc..b9ec48185 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -1182,7 +1182,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = of nkTupleConstr: put(g, tkParLe, "(") gcomma(g, n, c) - if n.len == 1: put(g, tkComma, ",") + if n.len == 1 and n[0].kind != nkExprColonExpr: put(g, tkComma, ",") put(g, tkParRi, ")") of nkCurly: put(g, tkCurlyLe, "{") diff --git a/compiler/vm.nim b/compiler/vm.nim index d97d2d6e6..baaf0f14e 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -2228,6 +2228,12 @@ proc prepareVMValue(arg: PNode): PNode = if arg.kind in nkLiterals: return arg + if arg.kind == nkExprColonExpr and arg[0].typ != nil and + arg[0].typ.sym != nil and arg[0].typ.sym.magic == mPNimrodNode: + # Poor mans way of protecting static NimNodes + # XXX: Maybe we need a nkNimNode? + return arg + result = copyNode(arg) if arg.kind == nkTupleConstr: for child in arg: diff --git a/doc/astspec.txt b/doc/astspec.txt index b4b8b34b5..92212bf8c 100644 --- a/doc/astspec.txt +++ b/doc/astspec.txt @@ -329,19 +329,56 @@ AST: Parentheses ----------- -Parentheses for affecting operator precedence or tuple construction -are built with the ``nnkPar`` node. +Parentheses for affecting operator precedence use the ``nnkPar`` node. Concrete syntax: .. code-block:: nim - (1, 2, (3)) + (a + b) * c AST: .. code-block:: nim - nnkPar(nnkIntLit(1), nnkIntLit(2), nnkPar(nnkIntLit(3))) + nnkInfix(nnkIdent("*"), + nnkPar( + nnkInfix(nnkIdent("+"), nnkIdent("a"), nnkIdent("b"))), + nnkIdent("c")) +Tuple Constructors +------------------ + +Nodes for tuple construction are built with the ``nnkTupleConstr`` node. + +Concrete syntax: + +.. code-block:: nim + (1, 2, 3) + (a: 1, b: 2, c: 3) + () + +AST: + +.. code-block:: nim + nnkTupleConstr(nnkIntLit(1), nnkIntLit(2), nnkIntLit(3)) + nnkTupleConstr( + nnkExprColonExpr(nnkIdent("a"), nnkIntLit(1)), + nnkExprColonExpr(nnkIdent("b"), nnkIntLit(2)), + nnkExprColonExpr(nnkIdent("c"), nnkIntLit(3))) + +Since the one tuple would be syntactically identical to parentheses +with an expression in them, the parser expects a trailing comma for +them. For tuple constructors with field names, this is not necessary. + +.. code-block:: nim + (1,) + (a: 1) + +AST: + +.. code-block:: nim + nnkTupleConstr(nnkIntLit(1)) + nnkTupleConstr( + nnkExprColonExpr(nnkIdent("a"), nnkIntLit(1))) Curly braces ------------ diff --git a/tests/ast_pattern_matching.nim b/tests/ast_pattern_matching.nim index c08234b9e..d209493b6 100644 --- a/tests/ast_pattern_matching.nim +++ b/tests/ast_pattern_matching.nim @@ -133,8 +133,8 @@ proc matchLengthKind*(arg: NimNode; kind: NimNodeKind; length: int): MatchingErr matchLengthKind(arg, {kind}, length) proc matchValue(arg: NimNode; kind: set[NimNodeKind]; value: SomeInteger): MatchingError {.compileTime.} = - let kindFail = not(kind.card == 0 or arg.kind in kind) - let valueFail = arg.intVal != int(value) + template kindFail: bool = not(kind.card == 0 or arg.kind in kind) + template valueFail: bool = arg.intVal != int(value) if kindFail or valueFail: result.node = arg result.kind = WrongKindValue diff --git a/tests/astspec/tastspec.nim b/tests/astspec/tastspec.nim index e2cfed277..33a245b1b 100644 --- a/tests/astspec/tastspec.nim +++ b/tests/astspec/tastspec.nim @@ -327,19 +327,43 @@ static: testArrayAccessOperator(x[y]) - - ## Parentheses scope: - let ast = myquote: - (1, 2, (3)) + (a + b) * c ast.matchAst: - of nnkPar(nnkIntLit(intVal = 1), nnkIntLit(intVal = 2), nnkPar(nnkIntLit(intVal = 3))): - echo "ok" + of nnkInfix(ident"*", nnkPar(nnkInfix(ident"+", ident"a", ident"b")), ident"c"): + echo "parentheses ok" + ## Tuple Constructors + + scope: + let ast = myquote: + (1, 2, 3) + (a: 1, b: 2, c: 3) + (1,) + (a: 1) + () + + for it in ast: + echo it.lispRepr + it.matchAst: + of nnkTupleConstr(nnkIntLit(intVal = 1), nnkIntLit(intVal = 2), nnkIntLit(intVal = 3)): + echo "simple tuple ok" + of nnkTupleConstr( + nnkExprColonExpr(ident"a", nnkIntLit(intVal = 1)), + nnkExprColonExpr(ident"b", nnkIntLit(intVal = 2)), + nnkExprColonExpr(ident"c", nnkIntLit(intVal = 3)) + ): + echo "named tuple ok" + of nnkTupleConstr(nnkIntLit(intVal = 1)): + echo "one tuple ok" + of nnkTupleConstr(nnkExprColonExpr(ident"a", nnkIntLit(intVal = 1))): + echo "named one tuple ok" + of nnkTupleConstr(): + echo "empty tuple ok" ## Curly braces diff --git a/tests/compiler/tprefixmatches.nim b/tests/compiler/tprefixmatches.nim index a89a6f613..6a3186729 100644 --- a/tests/compiler/tprefixmatches.nim +++ b/tests/compiler/tprefixmatches.nim @@ -5,7 +5,7 @@ macro check(val, body: untyped): untyped = result = newStmtList() expectKind body, nnkStmtList for b in body: - expectKind b, nnkPar + expectKind b, nnkTupleConstr expectLen b, 2 let p = b[0] let s = b[1] diff --git a/tests/lexer/tunary_minus.nim b/tests/lexer/tunary_minus.nim index 87b3cb52d..1641e918c 100644 --- a/tests/lexer/tunary_minus.nim +++ b/tests/lexer/tunary_minus.nim @@ -45,7 +45,7 @@ template main = doAssert lispReprStr([-1]) == """(Bracket (IntLit -1))""" doAssert (-1, 2)[0] == minusOne: "unable to handle negatives after parenthesis" - doAssert lispReprStr((-1, 2)) == """(Par (IntLit -1) (IntLit 2))""" + doAssert lispReprStr((-1, 2)) == """(TupleConstr (IntLit -1) (IntLit 2))""" proc x(): int = var a = 1;-1 # the -1 should act as the return value doAssert x() == minusOne: diff --git a/tests/macros/tdumpast.nim b/tests/macros/tdumpast.nim index b9d224ab8..b6bcbe8f0 100644 --- a/tests/macros/tdumpast.nim +++ b/tests/macros/tdumpast.nim @@ -48,3 +48,34 @@ macro fun3(): untyped = int | float | array | seq | object | ptr | pointer | float32 doAssert n.repr == "int | float | array | seq | object | ptr | pointer | float32", n.repr fun3() + +macro fun4() = + let n = quote do: + (a: 1) + doAssert n.repr == "(a: 1)", n.repr +fun4() + +# nkTupleConstr vs nkPar tests: +block: # lispRepr + macro lispRepr2(a: untyped): string = newLit a.lispRepr + + doAssert lispRepr2(()) == """(TupleConstr)""" + doAssert lispRepr2((a: 1)) == """(TupleConstr (ExprColonExpr (Ident "a") (IntLit 1)))""" + doAssert lispRepr2((a: 1, b: 2)) == """(TupleConstr (ExprColonExpr (Ident "a") (IntLit 1)) (ExprColonExpr (Ident "b") (IntLit 2)))""" + doAssert lispRepr2((1,)) == """(TupleConstr (IntLit 1))""" + doAssert lispRepr2((1, 2)) == """(TupleConstr (IntLit 1) (IntLit 2))""" + doAssert lispRepr2((1, 2, 3.0)) == """(TupleConstr (IntLit 1) (IntLit 2) (FloatLit 3.0))""" + doAssert lispRepr2((1)) == """(Par (IntLit 1))""" + doAssert lispRepr2((1+2)) == """(Par (Infix (Ident "+") (IntLit 1) (IntLit 2)))""" + +block: # repr + macro repr2(a: untyped): string = newLit a.repr + + doAssert repr2(()) == "()" + doAssert repr2((a: 1)) == "(a: 1)" + doAssert repr2((a: 1, b: 2)) == "(a: 1, b: 2)" + doAssert repr2((1,)) == "(1,)" + doAssert repr2((1, 2)) == "(1, 2)" + doAssert repr2((1, 2, 3.0)) == "(1, 2, 3.0)" + doAssert repr2((1)) == "(1)" + doAssert repr2((1+2)) == "(1 + 2)" diff --git a/tests/parser/tpostexprblocks.nim b/tests/parser/tpostexprblocks.nim index bc10a72e2..d272c712f 100644 --- a/tests/parser/tpostexprblocks.nim +++ b/tests/parser/tpostexprblocks.nim @@ -464,7 +464,7 @@ StmtList DiscardStmt Empty OfBranch - Par + TupleConstr Ident "a" Ident "b" StmtList @@ -476,7 +476,7 @@ StmtList DiscardStmt Empty ElifBranch - Par + TupleConstr Ident "a" Ident "b" StmtList @@ -488,7 +488,7 @@ StmtList DiscardStmt Empty ExceptBranch - Par + TupleConstr Ident "a" Ident "b" StmtList diff --git a/tests/parser/ttypemodifiers.nim b/tests/parser/ttypemodifiers.nim index 2c322b44b..9a1ccb1a5 100644 --- a/tests/parser/ttypemodifiers.nim +++ b/tests/parser/ttypemodifiers.nim @@ -21,7 +21,7 @@ StmtList Ident "PtrTuple" Empty PtrTy - Par + TupleConstr Ident "int" Ident "string" TypeDef @@ -43,14 +43,14 @@ StmtList Ident "RefTupleType" Empty RefTy - Par + TupleConstr Ident "int" Ident "string" TypeDef Ident "RefTupleVars" Empty RefTy - Par + TupleConstr Ident "a" Ident "b" TypeDef @@ -80,7 +80,7 @@ StmtList Empty Command Ident "static" - Par + TupleConstr Ident "int" Ident "string" TypeDef @@ -155,7 +155,7 @@ StmtList Empty Command Ident "type" - Par + TupleConstr Ident "a" Ident "b" TypeDef @@ -163,7 +163,7 @@ StmtList Empty Command Ident "type" - Par + TupleConstr Ident "int" Ident "string" TypeDef @@ -287,7 +287,7 @@ StmtList IdentDefs Ident "refTuple2" RefTy - Par + TupleConstr Ident "int" Ident "string" Empty @@ -524,4 +524,3 @@ dumpTree: static: staticStmtList1 staticStmtList2 - From 8db93fd0a29dbb950cc14327b31d683c3b0d8926 Mon Sep 17 00:00:00 2001 From: Danil Yarantsev Date: Tue, 30 Mar 2021 09:04:17 +0300 Subject: [PATCH 1834/3768] Fix entries in nimc advopt (#17576) --- doc/advopt.txt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/doc/advopt.txt b/doc/advopt.txt index 108c07222..763ac80a6 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -43,8 +43,9 @@ Advanced options: --warning[X]:on|off turn specific warning X on|off --hints:on|off|list turn all hints on|off or list all available --hint[X]:on|off turn specific hint X on|off - --warningAsError[X]:on|off turn specific warning X into an error on|off - --hintAsError[X]:on|off turn specific hint X into an error on|off + --warningAsError[X]:on|off + turn specific warning X into an error on|off + --hintAsError[X]:on|off turn specific hint X into an error on|off --styleCheck:off|hint|error produce hints or errors for Nim identifiers that do not adhere to Nim's official style guide @@ -83,7 +84,8 @@ Advanced options: if path == @default (the default and most useful), will use best match among @pkg,@path. if these are nonexistent, will use project path - -b, --backend:c|cpp|js|objc sets backend to use with commands like `nim doc` or `nim r` + -b, --backend:c|cpp|js|objc + sets backend to use with commands like `nim doc` or `nim r` --docCmd:cmd if `cmd == skip`, skips runnableExamples else, runs runnableExamples with given options, e.g.: `--docCmd:"-d:foo --threads:on"` From a672ec3c9e25159d3482aebfe1d0bb4271910869 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Tue, 30 Mar 2021 08:05:37 +0200 Subject: [PATCH 1835/3768] Fix #17299, fix setAffinity for android (#17574) * Fix #17299 * Comment * Fix typo --- lib/system/threadlocalstorage.nim | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/lib/system/threadlocalstorage.nim b/lib/system/threadlocalstorage.nim index 922150fff..cbb74c7df 100644 --- a/lib/system/threadlocalstorage.nim +++ b/lib/system/threadlocalstorage.nim @@ -194,8 +194,23 @@ else: proc cpusetIncl(cpu: cint; s: var CpuSet) {. importc: "CPU_SET", header: schedh.} - proc setAffinity(thread: SysThread; setsize: csize_t; s: var CpuSet) {. - importc: "pthread_setaffinity_np", header: pthreadh.} + when defined(android): + # libc of android doesn't implement pthread_setaffinity_np, + # it exposes pthread_gettid_np though, so we can use that in combination + # with sched_setaffinity to set the thread affinity. + type Pid {.importc: "pid_t", header: "".} = int32 # From posix_other.nim + + proc setAffinityTID(tid: Pid; setsize: csize_t; s: var CpuSet) {. + importc: "sched_setaffinity", header: schedh.} + + proc pthread_gettid_np(thread: SysThread): Pid {. + importc: "pthread_gettid_np", header: pthreadh.} + + proc setAffinity(thread: SysThread; setsize: csize_t; s: var CpuSet) = + setAffinityTID(pthread_gettid_np(thread), setsize, s) + else: + proc setAffinity(thread: SysThread; setsize: csize_t; s: var CpuSet) {. + importc: "pthread_setaffinity_np", header: pthreadh.} const From 9e88425d7c5e3edfcbddedbe062c5f028789a002 Mon Sep 17 00:00:00 2001 From: Ardek Romak Date: Tue, 30 Mar 2021 09:07:45 +0300 Subject: [PATCH 1836/3768] Fix void async in templates (#17562) * Fix https://github.com/nim-lang/Nim/issues/16159 * Add test for async in template * Shorten the expression * Even shorter Co-authored-by: Clyybber --- lib/pure/asyncmacro.nim | 5 +++-- tests/async/tasyncintemplate.nim | 12 ++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 tests/async/tasyncintemplate.nim diff --git a/lib/pure/asyncmacro.nim b/lib/pure/asyncmacro.nim index eecec4278..336d10aad 100644 --- a/lib/pure/asyncmacro.nim +++ b/lib/pure/asyncmacro.nim @@ -184,8 +184,9 @@ proc asyncSingleProc(prc: NimNode): NimNode = verifyReturnType(repr(returnType), returnType) let subtypeIsVoid = returnType.kind == nnkEmpty or - (baseType.kind == nnkIdent and returnType[1].eqIdent("void")) - + (baseType.kind in {nnkIdent, nnkSym} and + baseType.eqIdent("void")) + let futureVarIdents = getFutureVarIdents(prc.params) var outerProcBody = newNimNode(nnkStmtList, prc.body) diff --git a/tests/async/tasyncintemplate.nim b/tests/async/tasyncintemplate.nim new file mode 100644 index 000000000..31f4cd95c --- /dev/null +++ b/tests/async/tasyncintemplate.nim @@ -0,0 +1,12 @@ +discard """ + output: 42 +""" + +import asyncdispatch + +template foo() = + proc temp(): Future[int] {.async.} = return 42 + proc tempVoid(): Future[void] {.async.} = echo await temp() + +foo() +waitFor tempVoid() From 40093b4a937d751ef43b5c2ca447677bb858dff4 Mon Sep 17 00:00:00 2001 From: Miran Date: Tue, 30 Mar 2021 10:20:58 +0200 Subject: [PATCH 1837/3768] [backport:1.2] Avoid inlining of newObj and newObjRC1 calls (#17582) This is taken from: https://github.com/status-im/Nim/commit/af69b3ceae16281efd45cbee4ce1bedd14282304 Full original comment: This is to avoid heavy inlining happening when two allocation calls would occur shortly after each other. This inlining would sometimes be accompanied with an optimisation as the compiler is able to see that cellToUsr ending the first allocation call is shortly followed by an usrToCell call. The pointer arithmetic is redundant and the compiler can eliminate it, leaving only the cell address in a register (and later the stack) instead of the actual pointer to the user data, as one would expect. This combined with a GC collect cycle will cause the stack scan to only notice the cell address, which is of no good due to a usrToCell in the gcMark call which shifts that address to an adjacent cell. This means that the actual cell of importance will not get marked and thus cause a premature collection of that cell. BOOM. --- lib/system/gc.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/system/gc.nim b/lib/system/gc.nim index ae29f3466..229b26f3c 100644 --- a/lib/system/gc.nim +++ b/lib/system/gc.nim @@ -482,7 +482,7 @@ proc newObjNoInit(typ: PNimType, size: int): pointer {.compilerRtl.} = result = rawNewObj(typ, size, gch) when defined(memProfiler): nimProfile(size) -proc newObj(typ: PNimType, size: int): pointer {.compilerRtl.} = +proc newObj(typ: PNimType, size: int): pointer {.compilerRtl, noinline.} = result = rawNewObj(typ, size, gch) zeroMem(result, size) when defined(memProfiler): nimProfile(size) @@ -497,7 +497,7 @@ proc newSeq(typ: PNimType, len: int): pointer {.compilerRtl.} = when defined(memProfiler): nimProfile(size) {.pop.} -proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl.} = +proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl, noinline.} = # generates a new object and sets its reference counter to 1 incTypeSize typ, size sysAssert(allocInv(gch.region), "newObjRC1 begin") From 3715fc41d54ae5c139fba79f1f61419e320e8ba5 Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 30 Mar 2021 16:27:24 +0800 Subject: [PATCH 1838/3768] fix #17512 (#17520) --- compiler/ccgcalls.nim | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 64b883087..2a93cb5af 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -259,6 +259,10 @@ proc withTmpIfNeeded(p: BProc, a: TLoc, needsTmp: bool): TLoc = else: result = a +proc literalsNeedsTmp(p: BProc, a: TLoc): TLoc = + getTemp(p, a.lode.typ, result, needsInit=false) + genAssignment(p, result, a, {}) + proc genArgStringToCString(p: BProc, n: PNode, needsTmp: bool): Rope {.inline.} = var a: TLoc initLocExpr(p, n[0], a) @@ -273,7 +277,10 @@ proc genArg(p: BProc, n: PNode, param: PSym; call: PNode, needsTmp = false): Rop result = openArrayLoc(p, param.typ, n) elif ccgIntroducedPtr(p.config, param, call[0].typ[0]): initLocExpr(p, n, a) - result = addrLoc(p.config, withTmpIfNeeded(p, a, needsTmp)) + if n.kind in {nkCharLit..nkNilLit}: + result = addrLoc(p.config, literalsNeedsTmp(p, a)) + else: + result = addrLoc(p.config, withTmpIfNeeded(p, a, needsTmp)) elif p.module.compileToCpp and param.typ.kind in {tyVar} and n.kind == nkHiddenAddr: initLocExprSingleUse(p, n[0], a) From f5b1e384a411e1418180ea81bb8fc71bf2feaa26 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 30 Mar 2021 09:17:21 +0200 Subject: [PATCH 1839/3768] parser.nim: simply layout change --- compiler/parser.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/parser.nim b/compiler/parser.nim index 83ea4cbdc..d103d161a 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -1990,7 +1990,8 @@ proc parseObject(p: var Parser): PNode = result.add(parseObjectPart(p)) proc parseTypeClassParam(p: var Parser): PNode = - let modifier = case p.tok.tokType + let modifier = + case p.tok.tokType of tkOut, tkVar: nkVarTy of tkPtr: nkPtrTy of tkRef: nkRefTy From af2a7e8cbc3729ac62be64b5683e4a2187160b2f Mon Sep 17 00:00:00 2001 From: rockcavera Date: Tue, 30 Mar 2021 07:24:21 -0300 Subject: [PATCH 1840/3768] Rotation right and left refactored to generics. Continuation of #16622 (#17578) * Rotation right and left refactored to generics Continuation of [16622](https://github.com/nim-lang/Nim/pull/16622) * add runnableExamples --- lib/pure/bitops.nim | 121 ++++++++++++-------------------------------- 1 file changed, 32 insertions(+), 89 deletions(-) diff --git a/lib/pure/bitops.nim b/lib/pure/bitops.nim index 4ef190c1b..b5b08985e 100644 --- a/lib/pure/bitops.nim +++ b/lib/pure/bitops.nim @@ -771,7 +771,9 @@ func rotr[T: SomeUnsignedInt](value: T, rot: int32): T {.inline.} = let rot = rot and mask (value shr rot) or (value shl ((-rot) and mask)) -func shiftTypeToImpl(size: static int, shift: int): auto {.inline.} = +func shiftTypeTo(size: static int, shift: int): auto {.inline.} = + ## Returns the `shift` for the rotation according to the compiler and the + ## `size`. when (defined(vcc) and (size in [4, 8])) or defined(gcc) or defined(icl): cint(shift) elif (defined(vcc) and (size in [1, 2])) or (defined(clang) and size == 1): @@ -784,118 +786,59 @@ func shiftTypeToImpl(size: static int, shift: int): auto {.inline.} = elif size == 8: culonglong(shift) -template shiftTypeTo[T](value: T, shift: int): auto = - ## Returns the `shift` for the rotation according to the compiler and the size - ## of the` value`. - shiftTypeToImpl(sizeof(value), shift) - -func rotateLeftBits*(value: uint8, shift: range[0..8]): uint8 {.inline.} = - ## Left-rotate bits in a 8-bits value. +func rotateLeftBits*[T: SomeUnsignedInt](value: T, shift: range[0..(sizeof(T) * 8)]): T {.inline.} = + ## Left-rotate bits in a `value`. runnableExamples: doAssert rotateLeftBits(0b0110_1001'u8, 4) == 0b1001_0110'u8 - - when nimvm: - rotl(value, shift.int32) - else: - when useBuiltinsRotate: - builtin_rotl8(value.cuchar, shiftTypeTo(value, shift)).uint8 - else: - rotl(value, shift.int32) - -func rotateLeftBits*(value: uint16, shift: range[0..16]): uint16 {.inline.} = - ## Left-rotate bits in a 16-bits value. - runnableExamples: doAssert rotateLeftBits(0b00111100_11000011'u16, 8) == 0b11000011_00111100'u16 - - when nimvm: - rotl(value, shift.int32) - else: - when useBuiltinsRotate: - builtin_rotl16(value.cushort, shiftTypeTo(value, shift)).uint16 - else: - rotl(value, shift.int32) - -func rotateLeftBits*(value: uint32, shift: range[0..32]): uint32 {.inline.} = - ## Left-rotate bits in a 32-bits value. - runnableExamples: doAssert rotateLeftBits(0b0000111111110000_1111000000001111'u32, 16) == 0b1111000000001111_0000111111110000'u32 - - when nimvm: - rotl(value, shift.int32) - else: - when useBuiltinsRotate: - builtin_rotl32(value.cuint, shiftTypeTo(value, shift)).uint32 - else: - rotl(value, shift.int32) - -func rotateLeftBits*(value: uint64, shift: range[0..64]): uint64 {.inline.} = - ## Left-rotate bits in a 64-bits value. - runnableExamples: doAssert rotateLeftBits(0b00000000111111111111111100000000_11111111000000000000000011111111'u64, 32) == 0b11111111000000000000000011111111_00000000111111111111111100000000'u64 - when nimvm: rotl(value, shift.int32) else: - when useBuiltinsRotate and defined(amd64): - builtin_rotl64(value.culonglong, shiftTypeTo(value, shift)).uint64 + when useBuiltinsRotate: + const size = sizeof(T) + when size == 1: + builtin_rotl8(value.cuchar, shiftTypeTo(size, shift)).T + elif size == 2: + builtin_rotl16(value.cushort, shiftTypeTo(size, shift)).T + elif size == 4: + builtin_rotl32(value.cuint, shiftTypeTo(size, shift)).T + elif size == 8 and arch64: + builtin_rotl64(value.culonglong, shiftTypeTo(size, shift)).T + else: + rotl(value, shift.int32) else: rotl(value, shift.int32) -func rotateRightBits*(value: uint8, shift: range[0..8]): uint8 {.inline.} = - ## Right-rotate bits in a 8-bits value. +func rotateRightBits*[T: SomeUnsignedInt](value: T, shift: range[0..(sizeof(T) * 8)]): T {.inline.} = + ## Right-rotate bits in a `value`. runnableExamples: doAssert rotateRightBits(0b0110_1001'u8, 4) == 0b1001_0110'u8 - - when nimvm: - rotr(value, shift.int32) - else: - when useBuiltinsRotate: - builtin_rotr8(value.cuchar, shiftTypeTo(value, shift)).uint8 - else: - rotr(value, shift.int32) - -func rotateRightBits*(value: uint16, shift: range[0..16]): uint16 {.inline.} = - ## Right-rotate bits in a 16-bits value. - runnableExamples: doAssert rotateRightBits(0b00111100_11000011'u16, 8) == 0b11000011_00111100'u16 - - when nimvm: - rotr(value, shift.int32) - else: - when useBuiltinsRotate: - builtin_rotr16(value.cushort, shiftTypeTo(value, shift)).uint16 - else: - rotr(value, shift.int32) - -func rotateRightBits*(value: uint32, shift: range[0..32]): uint32 {.inline.} = - ## Right-rotate bits in a 32-bits value. - runnableExamples: doAssert rotateRightBits(0b0000111111110000_1111000000001111'u32, 16) == 0b1111000000001111_0000111111110000'u32 - + doAssert rotateRightBits(0b00000000111111111111111100000000_11111111000000000000000011111111'u64, 32) == + 0b11111111000000000000000011111111_00000000111111111111111100000000'u64 when nimvm: rotr(value, shift.int32) else: when useBuiltinsRotate: - builtin_rotr32(value.cuint, shiftTypeTo(value, shift)).uint32 - else: - rotr(value, shift.int32) - -func rotateRightBits*(value: uint64, shift: range[0..64]): uint64 {.inline.} = - ## Right-rotate bits in a 64-bits value. - runnableExamples: - doAssert rotateRightBits(0b00000000111111111111111100000000_11111111000000000000000011111111'u64, 32) == - 0b11111111000000000000000011111111_00000000111111111111111100000000'u64 - - when nimvm: - rotr(value, shift.int32) - else: - when useBuiltinsRotate and defined(amd64): - builtin_rotr64(value.culonglong, shiftTypeTo(value, shift)).uint64 + const size = sizeof(T) + when size == 1: + builtin_rotr8(value.cuchar, shiftTypeTo(size, shift)).T + elif size == 2: + builtin_rotr16(value.cushort, shiftTypeTo(size, shift)).T + elif size == 4: + builtin_rotr32(value.cuint, shiftTypeTo(size, shift)).T + elif size == 8 and arch64: + builtin_rotr64(value.culonglong, shiftTypeTo(size, shift)).T + else: + rotr(value, shift.int32) else: rotr(value, shift.int32) From 5ecbe6737110bd3fb9a85c2582409483fb5494b0 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 30 Mar 2021 20:13:18 +0200 Subject: [PATCH 1841/3768] makes DrNim compile again (#17584) --- compiler/modulegraphs.nim | 3 ++- drnim/drnim.nim | 29 +++++++++++++++++------------ 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index e6f5267cd..4430e3baa 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -393,8 +393,9 @@ proc registerModule*(g: ModuleGraph; m: PSym) = proc registerModuleById*(g: ModuleGraph; m: FileIndex) = registerModule(g, g.packed[int m].module) -proc initOperators(g: ModuleGraph): Operators = +proc initOperators*(g: ModuleGraph): Operators = # These are safe for IC. + # Public because it's used by DrNim. result.opLe = createMagic(g, "<=", mLeI) result.opLt = createMagic(g, "<", mLtI) result.opAnd = createMagic(g, "and", mAnd) diff --git a/drnim/drnim.nim b/drnim/drnim.nim index 44eab8625..d549e1d5b 100644 --- a/drnim/drnim.nim +++ b/drnim/drnim.nim @@ -68,6 +68,7 @@ type DrnimContext = ref object z3: Z3_context graph: ModuleGraph + idgen: IdGenerator facts: seq[(PNode, VersionScope)] varVersions: seq[int] # this maps variable IDs to their current version. varSyms: seq[PSym] # mirrors 'varVersions' @@ -80,6 +81,7 @@ type DrCon = object graph: ModuleGraph + idgen: IdGenerator mapping: Table[string, Z3_ast] canonParameterNames: bool assumeUniqueness: bool @@ -301,12 +303,12 @@ proc nodeToZ3(c: var DrCon; n: PNode; scope: VersionScope; vars: var seq[PNode]) proc nodeToDomain(c: var DrCon; n, q: PNode; opAnd: PSym): PNode = assert n.kind == nkInfix - let opLe = createMagic(c.graph, "<=", mLeI) + let opLe = createMagic(c.graph, c.idgen, "<=", mLeI) case $n[0] of "..": result = buildCall(opAnd, buildCall(opLe, n[1], q), buildCall(opLe, q, n[2])) of "..<": - let opLt = createMagic(c.graph, "<", mLtI) + let opLt = createMagic(c.graph, c.idgen, "<", mLtI) result = buildCall(opAnd, buildCall(opLe, n[1], q), buildCall(opLt, q, n[2])) else: notImplemented(n) @@ -315,7 +317,7 @@ template quantorToZ3(fn) {.dirty.} = template ctx: untyped = c.up.z3 var bound = newSeq[Z3_app](n.len-2) - let opAnd = createMagic(c.graph, "and", mAnd) + let opAnd = createMagic(c.graph, c.idgen, "and", mAnd) var known: PNode for i in 1..n.len-2: let it = n[i] @@ -333,7 +335,7 @@ template quantorToZ3(fn) {.dirty.} = var dummy: seq[PNode] assert known != nil - let x = nodeToZ3(c, buildCall(createMagic(c.graph, "->", mImplies), + let x = nodeToZ3(c, buildCall(createMagic(c.graph, c.idgen, "->", mImplies), known, n[^1]), scope, dummy) result = fn(ctx, 0, bound.len.cuint, addr(bound[0]), 0, nil, x) @@ -395,8 +397,8 @@ proc nodeToZ3(c: var DrCon; n: PNode; scope: VersionScope; vars: var seq[PNode]) else: notImplemented(n) of mHigh: - let addOpr = createMagic(c.graph, "+", mAddI) - let lenOpr = createMagic(c.graph, "len", mLengthOpenArray) + let addOpr = createMagic(c.graph, c.idgen, "+", mAddI) + let lenOpr = createMagic(c.graph, c.idgen, "len", mLengthOpenArray) let asLenExpr = addOpr.buildCall(lenOpr.buildCall(n[1]), nkIntLit.newIntNode(-1)) result = rec asLenExpr of mLow: @@ -577,8 +579,8 @@ proc addRangeInfo(c: var DrCon, n: PNode; scope: VersionScope, res: var seq[Z3_a res.add nodeToZ3(c, translateEnsures(ensures, n), scope, dummy) return - let x = newTree(nkInfix, newSymNode createMagic(c.graph, "<=", cmpOp), lowBound, n) - let y = newTree(nkInfix, newSymNode createMagic(c.graph, "<=", cmpOp), n, highBound) + let x = newTree(nkInfix, newSymNode createMagic(c.graph, c.idgen, "<=", cmpOp), lowBound, n) + let y = newTree(nkInfix, newSymNode createMagic(c.graph, c.idgen, "<=", cmpOp), n, highBound) var dummy: seq[PNode] res.add nodeToZ3(c, x, scope, dummy) @@ -677,6 +679,7 @@ proc proofEngine(ctx: DrnimContext; assumptions: seq[(PNode, VersionScope)]; toProve: (PNode, VersionScope)): (bool, string) = var c: DrCon c.graph = ctx.graph + c.idgen = ctx.idgen c.assumeUniqueness = assumeUniqueness c.up = ctx result = proofEngineAux(c, assumptions, toProve) @@ -738,10 +741,11 @@ proc compatibleProps(graph: ModuleGraph; formal, actual: PType): bool {.nimcall. var c: DrCon c.graph = graph + c.idgen = graph.idgen c.canonParameterNames = true try: c.up = DrnimContext(z3: setupZ3(), o: initOperators(graph), graph: graph, owner: nil, - opImplies: createMagic(graph, "->", mImplies)) + opImplies: createMagic(graph, c.idgen, "->", mImplies)) template zero: untyped = VersionScope(0) if not frequires.isEmpty: result = not arequires.isEmpty and proofEngineAux(c, @[(frequires, zero)], (arequires, zero))[0] @@ -847,7 +851,7 @@ proc checkLe(c: DrnimContext, a, b: PNode) = of tyChar, tyUInt..tyUInt64: cmpOp = mLeU else: discard - let cmp = newTree(nkInfix, newSymNode createMagic(c.graph, "<=", cmpOp), a, b) + let cmp = newTree(nkInfix, newSymNode createMagic(c.graph, c.idgen, "<=", cmpOp), a, b) cmp.info = a.info discard prove(c, cmp) @@ -1026,7 +1030,7 @@ proc traverseAsgn(c: DrnimContext; n: PNode) = proc replaceByOldParams(fact, le: PNode): PNode = if guards.sameTree(fact, le): result = newNodeIT(nkCall, fact.info, fact.typ) - result.add newSymNode createMagic(c.graph, "old", mOld) + result.add newSymNode createMagic(c.graph, c.idgen, "old", mOld) result.add fact else: result = shallowCopy(fact) @@ -1176,8 +1180,9 @@ proc strongSemCheck(graph: ModuleGraph; owner: PSym; n: PNode) = c.z3 = setupZ3() c.o = initOperators(graph) c.graph = graph + c.idgen = graph.idgen c.owner = owner - c.opImplies = createMagic(c.graph, "->", mImplies) + c.opImplies = createMagic(c.graph, c.idgen, "->", mImplies) try: traverse(c, n) ensuresCheck(c, owner) From 72988509ba0859a5bdfb43a78273c2de26c3d6dc Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 30 Mar 2021 11:14:39 -0700 Subject: [PATCH 1842/3768] add getPort to resolve Port(0) (#17559) * add getPort to resolve Port(0) * fixup * use getPort in examples + tests * address comments: do not re-export Port --- changelog.md | 2 + lib/pure/asynchttpserver.nim | 42 ++++++++++++------- testament/lib/stdtest/netutils.nim | 3 +- .../tasynchttpserver_transferencoding.nim | 3 +- 4 files changed, 32 insertions(+), 18 deletions(-) diff --git a/changelog.md b/changelog.md index 7452e3182..c234bf174 100644 --- a/changelog.md +++ b/changelog.md @@ -71,6 +71,8 @@ - Added `asyncdispatch.activeDescriptors` that returns the number of currently active async event handles/file descriptors. +- Added to `asynchttpserver` `getPort` and `getSocket`. + - `--gc:orc` is now 10% faster than previously for common workloads. If you have trouble with its changed behavior, compile with `-d:nimOldOrc`. diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index 38be4ceac..ec464682e 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -15,20 +15,20 @@ ## instead of allowing users to connect directly to this server. runnableExamples("-r:off"): - # This example will create an HTTP server on port 8080. The server will - # respond to all requests with a `200 OK` response code and "Hello World" + # This example will create an HTTP server on an automatically chosen port. + # It will respond to all requests with a `200 OK` response code and "Hello World" # as the response body. import std/asyncdispatch proc main {.async.} = - const port = 8080 var server = newAsyncHttpServer() proc cb(req: Request) {.async.} = echo (req.reqMethod, req.url, req.headers) let headers = {"Content-type": "text/plain; charset=utf-8"} await req.respond(Http200, "Hello World", headers.newHttpHeaders()) - echo "test this with: curl localhost:" & $port & "/" - server.listen(Port(port)) + server.listen(Port(0)) # or Port(8080) to hardcode the standard HTTP port. + let port = server.getPort + echo "test this with: curl localhost:" & $port.uint16 & "/" while true: if server.shouldAcceptRequest(): await server.acceptRequest(cb) @@ -41,6 +41,7 @@ runnableExamples("-r:off"): import asyncnet, asyncdispatch, parseutils, uri, strutils import httpcore +from nativesockets import getLocalAddr, AF_INET import std/private/since export httpcore except parseHeader @@ -70,21 +71,32 @@ type maxBody: int ## The maximum content-length that will be read for the body. maxFDs: int -func getSocket*(a: AsyncHttpServer): AsyncSocket {.since: (1, 5, 1).} = - ## Returns the `AsyncHttpServer`s internal `AsyncSocket` instance. - ## - ## Useful for identifying what port the AsyncHttpServer is bound to, if it - ## was chosen automatically. +proc getPort*(self: AsyncHttpServer): Port {.since: (1, 5, 1).} = + ## Returns the port `self` was bound to. + ## + ## Useful for identifying what port `self` is bound to, if it + ## was chosen automatically, for example via `listen(Port(0))`. + runnableExamples: + from std/nativesockets import Port + let server = newAsyncHttpServer() + server.listen(Port(0)) + assert server.getPort.uint16 > 0 + server.close() + result = getLocalAddr(self.socket.getFd, AF_INET)[1] + +func getSocket*(self: AsyncHttpServer): AsyncSocket {.since: (1, 5, 1).} = + ## Field accessor. runnableExamples: - from std/asyncdispatch import Port from std/asyncnet import getFd - from std/nativesockets import getLocalAddr, AF_INET + from std/nativesockets import getLocalAddr, AF_INET, Port let server = newAsyncHttpServer() server.listen(Port(0)) # Socket is not bound until this point - let port = getLocalAddr(server.getSocket.getFd, AF_INET)[1] - doAssert uint16(port) > 0 + # note: a more direct way to get the port is `getPort`. + let (laddr, port) = getLocalAddr(server.getSocket.getFd, AF_INET) + assert uint16(port) > 0 + assert laddr == "0.0.0.0" server.close() - a.socket + self.socket proc newAsyncHttpServer*(reuseAddr = true, reusePort = false, maxBody = 8388608): AsyncHttpServer = diff --git a/testament/lib/stdtest/netutils.nim b/testament/lib/stdtest/netutils.nim index eb913a56a..5115390e0 100644 --- a/testament/lib/stdtest/netutils.nim +++ b/testament/lib/stdtest/netutils.nim @@ -1,6 +1,7 @@ import std/[nativesockets, asyncdispatch, os] proc bindAvailablePort*(handle: SocketHandle, port = Port(0)): Port = + ## See also `asynchttpserver.getPort`. block: var name: Sockaddr_in name.sin_family = typeof(name.sin_family)(toInt(AF_INET)) @@ -8,5 +9,5 @@ proc bindAvailablePort*(handle: SocketHandle, port = Port(0)): Port = name.sin_addr.s_addr = htonl(INADDR_ANY) if bindAddr(handle, cast[ptr SockAddr](addr(name)), sizeof(name).Socklen) < 0'i32: - raiseOSError(osLastError()) + raiseOSError(osLastError(), $port) result = getLocalAddr(handle, AF_INET)[1] diff --git a/tests/stdlib/tasynchttpserver_transferencoding.nim b/tests/stdlib/tasynchttpserver_transferencoding.nim index 34f3cef11..bfa0e2f69 100644 --- a/tests/stdlib/tasynchttpserver_transferencoding.nim +++ b/tests/stdlib/tasynchttpserver_transferencoding.nim @@ -27,10 +27,9 @@ template genTest(input, expected) = let server = newAsyncHttpServer() waitFor runSleepLoop(server) - let port = getLocalAddr(server.getSocket.getFd, AF_INET)[1] let data = postBegin & input var socket = newSocket() - socket.connect("127.0.0.1", port) + socket.connect("127.0.0.1", server.getPort) socket.send(data) waitFor sleepAsync(10) socket.close() From 7c09e0c75773a2df6469a2acd94f3090aef83255 Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 31 Mar 2021 02:15:14 +0800 Subject: [PATCH 1843/3768] fix #15617(fix compilation failure on -d:useMalloc --gc:none) (#17555) Co-authored-by: Andreas Rumpf Co-authored-by: Timothee Cour --- lib/system/mmdisp.nim | 10 ++++++++++ tests/system/tgcnone.nim | 6 ++++++ 2 files changed, 16 insertions(+) create mode 100644 tests/system/tgcnone.nim diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim index 96dc86bfd..3317ba627 100644 --- a/lib/system/mmdisp.nim +++ b/lib/system/mmdisp.nim @@ -57,6 +57,16 @@ elif defined(gogc): elif (defined(nogc) or defined(gcDestructors)) and defined(useMalloc): include system / mm / malloc + when defined(nogc): + proc GC_getStatistics(): string = "" + proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} = + result = alloc0(size) + + proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} = + result = newObj(typ, align(GenericSeqSize, typ.align) + len * typ.base.size) + cast[PGenericSeq](result).len = len + cast[PGenericSeq](result).reserved = len + elif defined(nogc): include system / mm / none diff --git a/tests/system/tgcnone.nim b/tests/system/tgcnone.nim new file mode 100644 index 000000000..700176d5f --- /dev/null +++ b/tests/system/tgcnone.nim @@ -0,0 +1,6 @@ +discard """ + matrix: "--gc:none -d:useMalloc" +""" +# bug #15617 +let x = 4 +doAssert x == 4 From 8ee0eda84156c8d9074b7a42cdedc209cffdc4a4 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 31 Mar 2021 01:15:08 -0700 Subject: [PATCH 1844/3768] fix #12282 distinct now does not create erroneous copy in VM (#17594) --- compiler/vmgen.nim | 14 ++++++++++++-- tests/distinct/tdistinct.nim | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index bd80df219..f6e385976 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -833,10 +833,20 @@ proc genAddSubInt(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = c.genNarrow(n, dest) proc genConv(c: PCtx; n, arg: PNode; dest: var TDest; opc=opcConv) = - if n.typ.kind == arg.typ.kind and arg.typ.kind == tyProc: - # don't do anything for lambda lifting conversions: + let t2 = n.typ.skipTypes({tyDistinct}) + let targ2 = arg.typ.skipTypes({tyDistinct}) + + proc implicitConv(): bool = + if sameType(t2, targ2): return true + # xxx consider whether to use t2 and targ2 here + if n.typ.kind == arg.typ.kind and arg.typ.kind == tyProc: + # don't do anything for lambda lifting conversions: + return true + + if implicitConv(): gen(c, arg, dest) return + let tmp = c.genx(arg) if dest < 0: dest = c.getTemp(n.typ) c.gABC(n, opc, dest, tmp) diff --git a/tests/distinct/tdistinct.nim b/tests/distinct/tdistinct.nim index d64f33443..876975a7c 100644 --- a/tests/distinct/tdistinct.nim +++ b/tests/distinct/tdistinct.nim @@ -1,4 +1,5 @@ discard """ + targets: "c js" output: ''' tdistinct 25 @@ -138,3 +139,34 @@ block tRequiresInit: accept: let s = "test" doAssert s == "test" + +type Foo = distinct string + +template main() = + # xxx put everything here to test under RT + VM + block: # bug #12282 + block: + proc test() = + var s: Foo + s.string.add('c') + doAssert s.string == "c" # was failing + test() + + block: + proc add(a: var Foo, b: char) {.borrow.} + proc test() = + var s: Foo + s.add('c') + doAssert s.string == "c" # was ok + test() + + block: + proc add(a: var Foo, b: char) {.borrow.} + proc test() = + var s: string + s.Foo.add('c') + doAssert s.string == "c" # was failing + test() + +static: main() +main() From 6d7d1e60fea201109bdafce550622b5b49e23323 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 31 Mar 2021 02:25:26 -0700 Subject: [PATCH 1845/3768] fix #14585, fix #17589: access to static param now works (#17590) --- compiler/types.nim | 2 +- compiler/vmgen.nim | 2 +- tests/statictypes/tstatic.nim | 56 +++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 2 deletions(-) create mode 100644 tests/statictypes/tstatic.nim diff --git a/compiler/types.nim b/compiler/types.nim index a0d43ec09..1dbbc3261 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -67,7 +67,7 @@ const tyAlias, tyInferred, tySink, tyOwned} # see also ast.abstractVarRange abstractInst* = {tyGenericInst, tyDistinct, tyOrdinal, tyTypeDesc, tyAlias, - tyInferred, tySink, tyOwned} + tyInferred, tySink, tyOwned} # xxx what about tyStatic? abstractInstOwned* = abstractInst + {tyOwned} skipPtrs* = {tyVar, tyPtr, tyRef, tyGenericInst, tyTypeDesc, tyAlias, tyInferred, tySink, tyLent, tyOwned} diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index f6e385976..be26f98b3 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -980,7 +980,7 @@ proc genBindSym(c: PCtx; n: PNode; dest: var TDest) = proc fitsRegister*(t: PType): bool = assert t != nil - t.skipTypes(abstractInst-{tyTypeDesc}).kind in { + t.skipTypes(abstractInst + {tyStatic} - {tyTypeDesc}).kind in { tyRange, tyEnum, tyBool, tyInt..tyUInt64, tyChar} proc ldNullOpcode(t: PType): TOpcode = diff --git a/tests/statictypes/tstatic.nim b/tests/statictypes/tstatic.nim new file mode 100644 index 000000000..aaa63534b --- /dev/null +++ b/tests/statictypes/tstatic.nim @@ -0,0 +1,56 @@ +discard """ + targets: "c cpp js" +""" + +template main() = + block: # bug #17589 + #[ + # all those gave some variation of the same bug: + 'intVal' is not accessible using discriminant 'kind' of type 'TFullReg' + 'floatVal' is not accessible using discriminant 'kind' of type 'TFullReg' + ]# + block: + proc f(a: static uint64): uint64 = + a + const x = 3'u64 + static: doAssert f(x) == 3'u64 + doAssert f(x) == 3'u64 + + block: + proc f(a: static uint64): uint64 = + a + const x = 3'u64 + static: doAssert f(x) == 3'u64 + doAssert f(x) == 3'u64 + + block: + proc foo(x: uint8): uint8 = x + proc f(a: static uint8): auto = foo(a) + const x = 3'u8 + static: doAssert f(x) == 3'u8 + doAssert f(x) == 3'u8 + + block: + template foo2(x: float) = + let b = x == 0 + proc foo(x: float) = foo2(x) + proc f(a: static float) = foo(a) + const x = 1.0 + static: f(x) + + block: + proc foo(x: int32) = + let b = x == 0 + proc f(a: static int32) = foo(a) + static: f(32767) # was working + static: f(32768) # was failing because >= int16.high (see isInt16Lit) + + block: # bug #14585 + const foo_m0ninv = 0x1234'u64 + proc foo(m0ninv: static uint64) = + let b = $m0ninv + static: + foo(foo_m0ninv) + +static: main() +main() From b18307f94060d2f672a83d72c557f3856368b73a Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 31 Mar 2021 02:27:02 -0700 Subject: [PATCH 1846/3768] fix #17572 (#17586) --- compiler/vm.nim | 3 ++- tests/misc/tunsignedconv.nim | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/compiler/vm.nim b/compiler/vm.nim index baaf0f14e..e3ba2994f 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -415,7 +415,8 @@ proc opConv(c: PCtx; dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): else: internalError(c.config, "cannot convert to string " & desttyp.typeToString) else: - case skipTypes(desttyp, abstractVarRange).kind + let desttyp = skipTypes(desttyp, abstractVarRange) + case desttyp.kind of tyInt..tyInt64: dest.ensureKind(rkInt) case skipTypes(srctyp, abstractRange).kind diff --git a/tests/misc/tunsignedconv.nim b/tests/misc/tunsignedconv.nim index b9463b5f0..0acb39106 100644 --- a/tests/misc/tunsignedconv.nim +++ b/tests/misc/tunsignedconv.nim @@ -79,3 +79,19 @@ except RangeDefect: success = true doAssert success, "conversion should fail at runtime" + +template main() = + # xxx move all tests under here so it gets tested in CT and RT + block: # bug #17572 + type T = distinct uint64 + func f(x: uint64): auto = + let a = T(x) + (x, a.uint64) + const x = 1'u64 shl 63 or 7 + const b = T(x) + doAssert b.uint64 == 9223372036854775815'u64 + doAssert $b.uint64 == "9223372036854775815" + doAssert f(x) == (9223372036854775815'u64, 9223372036854775815'u64) + +static: main() +main() From ece714773491de1757d9afbda6d499de9b610b31 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 31 Mar 2021 13:26:44 +0200 Subject: [PATCH 1847/3768] better error messages for .raise effect analysis (#17595) --- compiler/sempass2.nim | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index a1572c85e..87d196fad 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -304,8 +304,8 @@ proc addToIntersection(inter: var TIntersection, s: int) = proc throws(tracked, n, orig: PNode) = if n.typ == nil or n.typ.kind != tyError: if orig != nil: - let x = copyNode(n) - x.info = orig.info + let x = copyTree(orig) + x.typ = n.typ tracked.add x else: tracked.add n @@ -329,7 +329,7 @@ proc createTag(g: ModuleGraph; n: PNode): PNode = if not n.isNil: result.info = n.info proc addRaiseEffect(a: PEffects, e, comesFrom: PNode) = - assert e.kind != nkRaiseStmt + #assert e.kind != nkRaiseStmt var aa = a.exc for i in a.bottom.. 0: rr = rr.lastSon + localError(g.config, r.info, errGenerated, renderTree(rr) & " " & msg & typeToString(r.typ)) popInfoContext(g.config) # hint about unnecessarily listed exception types: if hints: From d3529d0d5afb4d30bd7bdcbc49ff77fd5e67ed39 Mon Sep 17 00:00:00 2001 From: narimiran Date: Wed, 31 Mar 2021 13:34:32 +0200 Subject: [PATCH 1848/3768] [ci skip] minor: fix indentation in manual.rst --- doc/manual.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/manual.rst b/doc/manual.rst index fdc744bc7..1fe554437 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -6156,12 +6156,12 @@ avoid ambiguity when there are multiple modules with the same path. There are two pseudo directories: 1. `std`: The `std` pseudo directory is the abstract location of Nim's standard -library. For example, the syntax `import std / strutils` is used to unambiguously -refer to the standard library's `strutils` module. + library. For example, the syntax `import std / strutils` is used to unambiguously + refer to the standard library's `strutils` module. 2. `pkg`: The `pkg` pseudo directory is used to unambiguously refer to a Nimble -package. However, for technical details that lie outside the scope of this document, -its semantics are: *Use the search path to look for module name but ignore the standard -library locations*. In other words, it is the opposite of `std`. + package. However, for technical details that lie outside the scope of this document, + its semantics are: *Use the search path to look for module name but ignore the standard + library locations*. In other words, it is the opposite of `std`. From import statement From 0be7f854ed0327c3c2c75e682eddab2b502538bb Mon Sep 17 00:00:00 2001 From: narimiran Date: Wed, 31 Mar 2021 13:45:14 +0200 Subject: [PATCH 1849/3768] [ci skip] correct indentation this time --- doc/manual.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/manual.rst b/doc/manual.rst index 1fe554437..b1aae2669 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -6156,12 +6156,12 @@ avoid ambiguity when there are multiple modules with the same path. There are two pseudo directories: 1. `std`: The `std` pseudo directory is the abstract location of Nim's standard - library. For example, the syntax `import std / strutils` is used to unambiguously - refer to the standard library's `strutils` module. + library. For example, the syntax `import std / strutils` is used to unambiguously + refer to the standard library's `strutils` module. 2. `pkg`: The `pkg` pseudo directory is used to unambiguously refer to a Nimble - package. However, for technical details that lie outside the scope of this document, - its semantics are: *Use the search path to look for module name but ignore the standard - library locations*. In other words, it is the opposite of `std`. + package. However, for technical details that lie outside the scope of this document, + its semantics are: *Use the search path to look for module name but ignore the standard + library locations*. In other words, it is the opposite of `std`. From import statement From 65efa727ffec223fd9e9fa8efcff38246f7b9ad4 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Wed, 31 Mar 2021 17:20:42 +0100 Subject: [PATCH 1850/3768] Removes asynchttpserver.getSocket. (#17587) --- lib/pure/asynchttpserver.nim | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index ec464682e..3982edc54 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -84,20 +84,6 @@ proc getPort*(self: AsyncHttpServer): Port {.since: (1, 5, 1).} = server.close() result = getLocalAddr(self.socket.getFd, AF_INET)[1] -func getSocket*(self: AsyncHttpServer): AsyncSocket {.since: (1, 5, 1).} = - ## Field accessor. - runnableExamples: - from std/asyncnet import getFd - from std/nativesockets import getLocalAddr, AF_INET, Port - let server = newAsyncHttpServer() - server.listen(Port(0)) # Socket is not bound until this point - # note: a more direct way to get the port is `getPort`. - let (laddr, port) = getLocalAddr(server.getSocket.getFd, AF_INET) - assert uint16(port) > 0 - assert laddr == "0.0.0.0" - server.close() - self.socket - proc newAsyncHttpServer*(reuseAddr = true, reusePort = false, maxBody = 8388608): AsyncHttpServer = ## Creates a new `AsyncHttpServer` instance. From 9b67e5c61b50424e66b2a9bc8cc566058c71a223 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 31 Mar 2021 09:24:39 -0700 Subject: [PATCH 1851/3768] jsonutils: support cstring (including as Table key); improve docs (#16062) * jsonutils: support cstring (including as Table key); improve docs * changelog * un-disable a test now that #16061 was fixed --- changelog.md | 2 ++ lib/std/jsonutils.nim | 22 +++++++++++++++++----- tests/stdlib/thashes.nim | 1 + tests/stdlib/tjsonutils.nim | 13 ++++++++----- 4 files changed, 28 insertions(+), 10 deletions(-) diff --git a/changelog.md b/changelog.md index c234bf174..f6dcee833 100644 --- a/changelog.md +++ b/changelog.md @@ -47,6 +47,8 @@ - Added an overload for the `collect` macro that inferes the container type based on the syntax of the last expression. Works with std seqs, tables and sets. +- `jsonutils` now handles `cstring` (including as Table key). + - Added `randState` template that exposes the default random number generator. Useful for library authors. diff --git a/lib/std/jsonutils.nim b/lib/std/jsonutils.nim index fa61d79db..a5daa9fb4 100644 --- a/lib/std/jsonutils.nim +++ b/lib/std/jsonutils.nim @@ -11,7 +11,7 @@ runnableExamples: z1: int8 let a = (1.5'f32, (b: "b2", a: "a2"), 'x', @[Foo(t: true, z1: -3), nil], [{"name": "John"}.newStringTable]) let j = a.toJson - doAssert j.jsonTo(typeof(a)).toJson == j + assert j.jsonTo(typeof(a)).toJson == j import std/[json,strutils,tables,sets,strtabs,options] @@ -197,6 +197,11 @@ proc fromJson*[T](a: var T, b: JsonNode, opt = Joptions()) = else: a.distinctBase.fromJson(b) elif T is string|SomeNumber: a = to(b,T) + elif T is cstring: + case b.kind + of JNull: a = nil + of JString: a = b.str + else: checkJson false, $($T, " ", b) elif T is JsonNode: a = b elif T is ref | ptr: if b.kind == JNull: a = nil @@ -273,9 +278,10 @@ proc toJson*[T](a: T): JsonNode = elif T is bool: result = %(a) elif T is SomeInteger: result = %a elif T is Ordinal: result = %(a.ord) + elif T is cstring: (if a == nil: result = newJNull() else: result = % $a) else: result = %a -proc fromJsonHook*[K, V](t: var (Table[K, V] | OrderedTable[K, V]), +proc fromJsonHook*[K: string|cstring, V](t: var (Table[K, V] | OrderedTable[K, V]), jsonNode: JsonNode) = ## Enables `fromJson` for `Table` and `OrderedTable` types. ## @@ -296,21 +302,27 @@ proc fromJsonHook*[K, V](t: var (Table[K, V] | OrderedTable[K, V]), for k, v in jsonNode: t[k] = jsonTo(v, V) -proc toJsonHook*[K, V](t: (Table[K, V] | OrderedTable[K, V])): JsonNode = +proc toJsonHook*[K: string|cstring, V](t: (Table[K, V] | OrderedTable[K, V])): JsonNode = ## Enables `toJson` for `Table` and `OrderedTable` types. ## ## See also: ## * `fromJsonHook proc<#fromJsonHook,,JsonNode>`_ + # pending PR #9217 use: toSeq(a) instead of `collect` in `runnableExamples`. runnableExamples: - import std/[tables, json] + import std/[tables, json, sugar] let foo = ( t: [("two", 2)].toTable, ot: [("one", 1), ("three", 3)].toOrderedTable) assert $toJson(foo) == """{"t":{"two":2},"ot":{"one":1,"three":3}}""" + # if keys are not string|cstring, you can use this: + let a = {10: "foo", 11: "bar"}.newOrderedTable + let a2 = collect: (for k,v in a: (k,v)) + assert $toJson(a2) == """[[10,"foo"],[11,"bar"]]""" result = newJObject() for k, v in pairs(t): - result[k] = toJson(v) + # not sure if $k has overhead for string + result[(when K is string: k else: $k)] = toJson(v) proc fromJsonHook*[A](s: var SomeSet[A], jsonNode: JsonNode) = ## Enables `fromJson` for `HashSet` and `OrderedSet` types. diff --git a/tests/stdlib/thashes.nim b/tests/stdlib/thashes.nim index a4487c8c0..044259f88 100644 --- a/tests/stdlib/thashes.nim +++ b/tests/stdlib/thashes.nim @@ -87,6 +87,7 @@ block largeSize: # longer than 4 characters proc main() = doAssert hash(0.0) == hash(0) + # bug #16061 doAssert hash(cstring"abracadabra") == 97309975 doAssert hash(cstring"abracadabra") == hash("abracadabra") diff --git a/tests/stdlib/tjsonutils.nim b/tests/stdlib/tjsonutils.nim index 63bf97704..eaf5d68f9 100644 --- a/tests/stdlib/tjsonutils.nim +++ b/tests/stdlib/tjsonutils.nim @@ -14,6 +14,7 @@ proc testRoundtrip[T](t: T, expected: string) = doAssert t2.toJson == j import tables, sets, algorithm, sequtils, options, strtabs +from strutils import contains type Foo = ref object id: int @@ -40,27 +41,29 @@ template fn() = # https://github.com/nim-lang/Nim/issues/12282 testRoundtrip(Foo(1.5)): """1.5""" - block: + block: # OrderedTable testRoundtrip({"z": "Z", "y": "Y"}.toOrderedTable): """{"z":"Z","y":"Y"}""" + doAssert toJson({"z": 10, "": 11}.newTable).`$`.contains """"":11""" # allows hash to change + testRoundtrip({"z".cstring: 1, "".cstring: 2}.toOrderedTable): """{"z":1,"":2}""" testRoundtrip({"z": (f1: 'f'), }.toTable): """{"z":{"f1":102}}""" - block: + block: # StringTable testRoundtrip({"name": "John", "city": "Monaco"}.newStringTable): """{"mode":"modeCaseSensitive","table":{"city":"Monaco","name":"John"}}""" block: # complex example let t = {"z": "Z", "y": "Y"}.newStringTable type A = ref object a1: string - let a = (1.1, "fo", 'x', @[10,11], [true, false], [t,newStringTable()], [0'i8,3'i8], -4'i16, (foo: 0.5'f32, bar: A(a1: "abc"), bar2: A.default)) + let a = (1.1, "fo", 'x', @[10,11], [true, false], [t,newStringTable()], [0'i8,3'i8], -4'i16, (foo: 0.5'f32, bar: A(a1: "abc"), bar2: A.default, cstring1: "foo", cstring2: "", cstring3: cstring(nil))) testRoundtrip(a): - """[1.1,"fo",120,[10,11],[true,false],[{"mode":"modeCaseSensitive","table":{"y":"Y","z":"Z"}},{"mode":"modeCaseSensitive","table":{}}],[0,3],-4,{"foo":0.5,"bar":{"a1":"abc"},"bar2":null}]""" + """[1.1,"fo",120,[10,11],[true,false],[{"mode":"modeCaseSensitive","table":{"y":"Y","z":"Z"}},{"mode":"modeCaseSensitive","table":{}}],[0,3],-4,{"foo":0.5,"bar":{"a1":"abc"},"bar2":null,"cstring1":"foo","cstring2":"","cstring3":null}]""" block: # edge case when user defined `==` doesn't handle `nil` well, e.g.: # https://github.com/nim-lang/nimble/blob/63695f490728e3935692c29f3d71944d83bb1e83/src/nimblepkg/version.nim#L105 testRoundtrip(@[Foo(id: 10), nil]): """[{"id":10},null]""" - block: + block: # enum type Foo = enum f1, f2, f3, f4, f5 type Bar = enum b1, b2, b3, b4 let a = [f2: b2, f3: b3, f4: b4] From 3f9c26539de010eaa12a879154f60e4bf656e6e3 Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 1 Apr 2021 02:54:36 +0800 Subject: [PATCH 1852/3768] close #16786 (#17598) --- tests/async/tasyncintemplate.nim | 36 +++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/tests/async/tasyncintemplate.nim b/tests/async/tasyncintemplate.nim index 31f4cd95c..0f810b5be 100644 --- a/tests/async/tasyncintemplate.nim +++ b/tests/async/tasyncintemplate.nim @@ -1,12 +1,46 @@ discard """ - output: 42 + output: ''' +42 +1 +2 +3 +4 +''' """ import asyncdispatch +# bug #16159 template foo() = proc temp(): Future[int] {.async.} = return 42 proc tempVoid(): Future[void] {.async.} = echo await temp() foo() waitFor tempVoid() + + +block: # bug #16786 + block: + proc main(a: int|string)= + proc bar(b: int|string) = echo b + bar(a) + main(1) + + block: + proc main(a: int) : Future[void] {.async.} = + proc bar(b: int): Future[void] {.async.} = echo b + await bar(a) + waitFor main(2) + + block: + proc main(a: int) : Future[void] {.async.} = + proc bar(b: int | string): Future[void] {.async.} = echo b + await bar(a) + waitFor main(3) + + block: + # bug + proc main(a: int|string) = + proc bar(b: int): Future[void] {.async.} = echo b + waitFor bar(a) + main(4) From fe9a37f2a57d0e76984b25c3dfe0fbdc94a30d9e Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 1 Apr 2021 02:55:27 +0800 Subject: [PATCH 1853/3768] close #12684 add testcase (#17599) --- tests/compiler/t12684.nim | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 tests/compiler/t12684.nim diff --git a/tests/compiler/t12684.nim b/tests/compiler/t12684.nim new file mode 100644 index 000000000..a5f33d2c9 --- /dev/null +++ b/tests/compiler/t12684.nim @@ -0,0 +1,7 @@ +discard """ + cmd: "nim check --hints:off --warnings:off $file" + errormsg: "undeclared identifier: 'Undeclared'" +""" + +var x: Undeclared +import compiler/nimeval From 924ab3adea30b8ffe590d1dd08cc3ff23f4881de Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 1 Apr 2021 14:05:04 +0800 Subject: [PATCH 1854/3768] fix #17190 `nimscript` now accepts arbitrary file extensions for `nim e main.customext` (#17596) * fix #17190 * cah * merge * Update tnimscriptwithnimext.nim * Update tnimscriptwithmacro.nims * Apply suggestions from code review * Delete tnimscriptwithnimext.nim * Update tests/tools/tnimscriptwithmacro.nims * fix * fix * add a test * Apply suggestions from code review Co-authored-by: Timothee Cour * Apply suggestions from code review * Update changelog.md Co-authored-by: Timothee Cour Co-authored-by: Timothee Cour Co-authored-by: Andreas Rumpf --- changelog.md | 2 ++ compiler/main.nim | 2 -- compiler/nimconf.nim | 7 +++++-- tests/misc/trunner.nim | 9 +++++++++ tests/newconfig/foo2/mfoo2.customext | 2 ++ 5 files changed, 18 insertions(+), 4 deletions(-) create mode 100644 tests/newconfig/foo2/mfoo2.customext diff --git a/changelog.md b/changelog.md index f6dcee833..900693644 100644 --- a/changelog.md +++ b/changelog.md @@ -281,6 +281,8 @@ - Tuple expressions are now parsed consistently as `nnkTupleConstr` node. Will affect macros expecting nodes to be of `nnkPar`. +- `nim e` now accepts arbitrary file extensions for the nimscript file, + although `.nims` is still the preferred extension in general. ## Compiler changes diff --git a/compiler/main.nim b/compiler/main.nim index b61cdcadb..26a19063f 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -342,8 +342,6 @@ proc mainCommand*(graph: ModuleGraph) = if conf.projectIsCmd or conf.projectIsStdin: discard elif not fileExists(conf.projectFull): rawMessage(conf, errGenerated, "NimScript file does not exist: " & conf.projectFull.string) - elif not conf.projectFull.string.endsWith(".nims"): - rawMessage(conf, errGenerated, "not a NimScript file: " & conf.projectFull.string) # main NimScript logic handled in `loadConfigs`. of cmdNop: discard of cmdJsonscript: diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim index 1691e7ccf..b63e5a0ad 100644 --- a/compiler/nimconf.nim +++ b/compiler/nimconf.nim @@ -298,11 +298,14 @@ proc loadConfigs*(cfg: RelativeFile; cache: IdentCache; conf: ConfigRef; idgen: for filename in configFiles: # delayed to here so that `hintConf` is honored rawMessage(conf, hintConf, filename.string) - if scriptIsProj: + if conf.cmd == cmdNimscript: showHintConf() configFiles.setLen 0 if conf.cmd != cmdIdeTools: - runNimScriptIfExists(scriptFile, isMain = true) + if conf.cmd == cmdNimscript: + runNimScriptIfExists(conf.projectFull, isMain = true) + else: + runNimScriptIfExists(scriptFile, isMain = true) else: if not scriptIsProj: runNimScriptIfExists(scriptFile, isMain = true) diff --git a/tests/misc/trunner.nim b/tests/misc/trunner.nim index a66177f28..1895eeb97 100644 --- a/tests/misc/trunner.nim +++ b/tests/misc/trunner.nim @@ -238,6 +238,15 @@ tests/newconfig/bar/mfoo.nims""".splitLines expected.add &"Hint: used config file '{b}' [Conf]\n" doAssert outp.endsWith expected, outp & "\n" & expected + block: # mfoo2.customext + let filename = testsDir / "newconfig/foo2/mfoo2.customext" + let cmd = fmt"{nim} e --hint:conf {filename}" + let (outp, exitCode) = execCmdEx(cmd, options = {poStdErrToStdOut}) + doAssert exitCode == 0 + var expected = &"Hint: used config file '{filename}' [Conf]\n" + doAssert outp.endsWith "123" & "\n" & expected + + block: # nim --eval let opt = "--hints:off" check fmt"""{nim} {opt} --eval:"echo defined(nimscript)"""".execCmdEx == ("true\n", 0) diff --git a/tests/newconfig/foo2/mfoo2.customext b/tests/newconfig/foo2/mfoo2.customext new file mode 100644 index 000000000..66c8b1d15 --- /dev/null +++ b/tests/newconfig/foo2/mfoo2.customext @@ -0,0 +1,2 @@ +doAssert defined(nimscript) +echo "123" From a7bb973a24fb73d378230216a6bfcfd2f62e4e78 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 31 Mar 2021 23:45:55 -0700 Subject: [PATCH 1855/3768] docgen: render pragmas by default except for a select list (and fix #9074) (#17054) --- compiler/docgen.nim | 22 ++--- compiler/lexer.nim | 2 +- compiler/renderer.nim | 22 ++++- .../expected/index.html | 2 +- .../expected/subdir/subdir_b/utils.html | 2 +- nimdoc/testproject/expected/testproject.html | 82 +++++++++---------- 6 files changed, 76 insertions(+), 56 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index f1a9750b7..67f21e1c4 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -446,22 +446,22 @@ proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var Rope; renderFlags: TRe "\\spanIdentifier{$1}", [escLit]) of tkSpaces, tkInvalid: result.add(literal) - of tkCurlyDotLe: + of tkHideableStart: template fun(s) = dispA(d.conf, result, s, "\\spanOther{$1}", [escLit]) if renderRunnableExamples in renderFlags: fun "$1" - else: fun: "" & # This span is required for the JS to work properly - """{...} + else: + # 1st span is required for the JS to work properly + fun """ + +... - -$1 -""".replace("\n", "") # Must remove newlines because wrapped in a
                                      -    of tkCurlyDotRi:
                                      +""".replace("\n", "")  # Must remove newlines because wrapped in a 
                                      +    of tkHideableEnd:
                                             template fun(s) = dispA(d.conf, result, s, "\\spanOther{$1}", [escLit])
                                             if renderRunnableExamples in renderFlags: fun "$1"
                                      -      else: fun """
                                      -
                                      -$1
                                      -""".replace("\n", "")
                                      +      else: fun ""
                                      +    of tkCurlyDotLe: dispA(d.conf, result, "$1", "\\spanOther{$1}", [escLit])
                                      +    of tkCurlyDotRi: dispA(d.conf, result, "$1", "\\spanOther{$1}", [escLit])
                                           of tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, tkCurlyRi,
                                              tkBracketDotLe, tkBracketDotRi, tkParDotLe,
                                              tkParDotRi, tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot,
                                      diff --git a/compiler/lexer.nim b/compiler/lexer.nim
                                      index cca7765e5..7c7ac4177 100644
                                      --- a/compiler/lexer.nim
                                      +++ b/compiler/lexer.nim
                                      @@ -72,7 +72,7 @@ type
                                           tkDot = ".", tkDotDot = "..", tkBracketLeColon = "[:",
                                           tkOpr, tkComment, tkAccent = "`",
                                           # these are fake tokens used by renderer.nim
                                      -    tkSpaces, tkInfixOpr, tkPrefixOpr, tkPostfixOpr
                                      +    tkSpaces, tkInfixOpr, tkPrefixOpr, tkPostfixOpr, tkHideableStart, tkHideableEnd
                                       
                                         TokTypes* = set[TokType]
                                       
                                      diff --git a/compiler/renderer.nim b/compiler/renderer.nim
                                      index b9ec48185..3d260f350 100644
                                      --- a/compiler/renderer.nim
                                      +++ b/compiler/renderer.nim
                                      @@ -185,7 +185,7 @@ proc dedent(g: var TSrcGen) =
                                       proc put(g: var TSrcGen, kind: TokType, s: string; sym: PSym = nil) =
                                         if kind != tkSpaces:
                                           addPendingNL(g)
                                      -    if s.len > 0:
                                      +    if s.len > 0 or kind in {tkHideableStart, tkHideableEnd}:
                                             addTok(g, kind, s, sym)
                                         else:
                                           g.pendingWhitespace = s.len
                                      @@ -587,13 +587,30 @@ proc putWithSpace(g: var TSrcGen, kind: TokType, s: string) =
                                         put(g, kind, s)
                                         put(g, tkSpaces, Space)
                                       
                                      +proc isHideable(config: ConfigRef, n: PNode): bool =
                                      +  # xxx compare `ident` directly with `getIdent(cache, wRaises)`, but
                                      +  # this requires a `cache`.
                                      +  case n.kind
                                      +  of nkExprColonExpr: result = n[0].kind == nkIdent and n[0].ident.s in ["raises", "tags", "extern"]
                                      +  of nkIdent: result = n.ident.s in ["gcsafe"]
                                      +  else: result = false
                                      +
                                       proc gcommaAux(g: var TSrcGen, n: PNode, ind: int, start: int = 0,
                                                      theEnd: int = - 1, separator = tkComma) =
                                      +  let inPragma = g.inPragma == 1 # just the top-level
                                      +  var inHideable = false
                                         for i in start..n.len + theEnd:
                                           var c = i < n.len + theEnd
                                           var sublen = lsub(g, n[i]) + ord(c)
                                           if not fits(g, g.lineLen + sublen) and (ind + sublen < MaxLineLen): optNL(g, ind)
                                           let oldLen = g.tokens.len
                                      +    if inPragma:
                                      +      if not inHideable and isHideable(g.config, n[i]):
                                      +        inHideable = true
                                      +        put(g, tkHideableStart, "")
                                      +      elif inHideable and not isHideable(g.config, n[i]):
                                      +        inHideable = false
                                      +        put(g, tkHideableEnd, "")
                                           gsub(g, n[i])
                                           if c:
                                             if g.tokens.len > oldLen:
                                      @@ -601,6 +618,9 @@ proc gcommaAux(g: var TSrcGen, n: PNode, ind: int, start: int = 0,
                                             if shouldRenderComment(g) and hasCom(n[i]):
                                               gcoms(g)
                                               optNL(g, ind)
                                      +  if inHideable:
                                      +    put(g, tkHideableEnd, "")
                                      +    inHideable = false
                                       
                                       proc gcomma(g: var TSrcGen, n: PNode, c: TContext, start: int = 0,
                                                   theEnd: int = - 1) =
                                      diff --git a/nimdoc/test_out_index_dot_html/expected/index.html b/nimdoc/test_out_index_dot_html/expected/index.html
                                      index db7470050..bac39fd33 100644
                                      --- a/nimdoc/test_out_index_dot_html/expected/index.html
                                      +++ b/nimdoc/test_out_index_dot_html/expected/index.html
                                      @@ -117,7 +117,7 @@ window.addEventListener('DOMContentLoaded', main);
                                       

                                      Procs

                                      -
                                      proc foo() {...}{.raises: [], tags: [].}
                                      +
                                      proc foo() {....raises: [], tags: [].}
                                      I do foo diff --git a/nimdoc/testproject/expected/subdir/subdir_b/utils.html b/nimdoc/testproject/expected/subdir/subdir_b/utils.html index cb80f2873..c1ee9c49d 100644 --- a/nimdoc/testproject/expected/subdir/subdir_b/utils.html +++ b/nimdoc/testproject/expected/subdir/subdir_b/utils.html @@ -177,7 +177,7 @@ window.addEventListener('DOMContentLoaded', main);

                                      Procs

                                      -
                                      proc someType(): SomeType {...}{.raises: [], tags: [].}
                                      +
                                      proc someType(): SomeType {....raises: [], tags: [].}
                                      constructor. diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html index 599a904a2..8525048ea 100644 --- a/nimdoc/testproject/expected/testproject.html +++ b/nimdoc/testproject/expected/testproject.html @@ -444,7 +444,7 @@ window.addEventListener('DOMContentLoaded', main);

                                      Types

                                      -
                                      A {...}{.inject.} = enum
                                      +
                                      A {.inject.} = enum
                                         aA
                                      @@ -452,7 +452,7 @@ The enum A.
                                      -
                                      B {...}{.inject.} = enum
                                      +
                                      B {.inject.} = enum
                                         bB
                                      @@ -542,7 +542,7 @@ This should be visible.
                                      -
                                      proc baz[T](a, b: T): T {...}{.deprecated.}
                                      +
                                      proc baz[T](a, b: T): T {.deprecated.}
                                      Deprecated @@ -552,7 +552,7 @@ This is deprecated without message.
                                      -
                                      proc buzz[T](a, b: T): T {...}{.deprecated: "since v0.20".}
                                      +
                                      proc buzz[T](a, b: T): T {.deprecated: "since v0.20".}
                                      Deprecated: since v0.20 @@ -562,14 +562,14 @@ This is deprecated with a message.
                                      -
                                      func someFunc() {...}{.raises: [], tags: [].}
                                      +
                                      func someFunc() {....raises: [], tags: [].}
                                      My someFunc. Stuff in quotes here. Some link
                                      -
                                      proc fromUtils3() {...}{.raises: [], tags: [].}
                                      +
                                      proc fromUtils3() {....raises: [], tags: [].}
                                      came form utils but should be shown where fromUtilsGen is called @@ -585,14 +585,14 @@ came form utils but should be shown where -
                                      proc z1(): Foo {...}{.raises: [], tags: [].}
                                      +
                                      proc z1(): Foo {....raises: [], tags: [].}
                                      cz1
                                      -
                                      proc z2() {...}{.raises: [], tags: [].}
                                      +
                                      proc z2() {....raises: [], tags: [].}
                                      cz2 @@ -601,49 +601,49 @@ cz2
                                      -
                                      proc z3() {...}{.raises: [], tags: [].}
                                      +
                                      proc z3() {....raises: [], tags: [].}
                                      cz3
                                      -
                                      proc z4() {...}{.raises: [], tags: [].}
                                      +
                                      proc z4() {....raises: [], tags: [].}
                                      cz4
                                      -
                                      proc z5(): int {...}{.raises: [], tags: [].}
                                      +
                                      proc z5(): int {....raises: [], tags: [].}
                                      cz5
                                      -
                                      proc z6(): int {...}{.raises: [], tags: [].}
                                      +
                                      proc z6(): int {....raises: [], tags: [].}
                                      cz6
                                      -
                                      proc z7(): int {...}{.raises: [], tags: [].}
                                      +
                                      proc z7(): int {....raises: [], tags: [].}
                                      cz7
                                      -
                                      proc z8(): int {...}{.raises: [], tags: [].}
                                      +
                                      proc z8(): int {....raises: [], tags: [].}
                                      cz8
                                      -
                                      proc z9() {...}{.raises: [], tags: [].}
                                      +
                                      proc z9() {....raises: [], tags: [].}
                                      @@ -652,7 +652,7 @@ cz8
                                      -
                                      proc z10() {...}{.raises: [], tags: [].}
                                      +
                                      proc z10() {....raises: [], tags: [].}
                                      @@ -661,7 +661,7 @@ cz8
                                      -
                                      proc z11() {...}{.raises: [], tags: [].}
                                      +
                                      proc z11() {....raises: [], tags: [].}
                                      @@ -670,7 +670,7 @@ cz8
                                      -
                                      proc z12(): int {...}{.raises: [], tags: [].}
                                      +
                                      proc z12(): int {....raises: [], tags: [].}
                                      @@ -679,7 +679,7 @@ cz8
                                      -
                                      proc z13() {...}{.raises: [], tags: [].}
                                      +
                                      proc z13() {....raises: [], tags: [].}
                                      cz13 @@ -688,14 +688,14 @@ cz13
                                      -
                                      proc baz() {...}{.raises: [], tags: [].}
                                      +
                                      proc baz() {....raises: [], tags: [].}
                                      -
                                      proc z17() {...}{.raises: [], tags: [].}
                                      +
                                      proc z17() {....raises: [], tags: [].}
                                      cz17 rest @@ -704,7 +704,7 @@ cz17 rest
                                      -
                                      proc p1() {...}{.raises: [], tags: [].}
                                      +
                                      proc p1() {....raises: [], tags: [].}
                                      cp1 @@ -727,7 +727,7 @@ this is a nested doc comment
                                      -
                                      proc addfBug14485() {...}{.raises: [], tags: [].}
                                      +
                                      proc addfBug14485() {....raises: [], tags: [].}
                                      Some proc @@ -746,23 +746,23 @@ Some proc
                                      -
                                      proc c_printf(frmt: cstring): cint {...}{.importc: "printf", header: "<stdio.h>",
                                      -                                     varargs, discardable.}
                                      +
                                      proc c_printf(frmt: cstring): cint {.importc: "printf", header: "<stdio.h>",
                                      +                                     varargs, discardable.}
                                      the c printf. etc.
                                      -
                                      proc c_nonexistant(frmt: cstring): cint {...}{.importc: "nonexistant",
                                      -    header: "<stdio.h>", varargs, discardable.}
                                      +
                                      proc c_nonexistant(frmt: cstring): cint {.importc: "nonexistant",
                                      +    header: "<stdio.h>", varargs, discardable.}
                                      -
                                      proc low[T: Ordinal | enum | range](x: T): T {...}{.magic: "Low", noSideEffect.}
                                      +
                                      proc low[T: Ordinal | enum | range](x: T): T {.magic: "Low", noSideEffect.}

                                      Returns the lowest possible value of an ordinal value x. As a special semantic rule, x may also be a type identifier.

                                      @@ -773,7 +773,7 @@ the c printf. etc.
                                      -
                                      proc low2[T: Ordinal | enum | range](x: T): T {...}{.magic: "Low", noSideEffect.}
                                      +
                                      proc low2[T: Ordinal | enum | range](x: T): T {.magic: "Low", noSideEffect.}

                                      Returns the lowest possible value of an ordinal value x. As a special semantic rule, x may also be a type identifier.

                                      @@ -786,7 +786,7 @@ the c printf. etc.
                                      -
                                      proc tripleStrLitTest() {...}{.raises: [], tags: [].}
                                      +
                                      proc tripleStrLitTest() {....raises: [], tags: [].}
                                      @@ -829,22 +829,22 @@ at indent 0
                                      -
                                      proc asyncFun1(): Future[int] {...}{.raises: [Exception, ValueError],
                                      -                                tags: [RootEffect].}
                                      +
                                      proc asyncFun1(): Future[int] {....raises: [Exception, ValueError],
                                      +                                tags: [RootEffect].}
                                      ok1
                                      -
                                      proc asyncFun2(): owned(Future[void]) {...}{.raises: [Exception], tags: [RootEffect].}
                                      +
                                      proc asyncFun2(): owned(Future[void]) {....raises: [Exception], tags: [RootEffect].}
                                      -
                                      proc asyncFun3(): owned(Future[void]) {...}{.raises: [Exception], tags: [RootEffect].}
                                      +
                                      proc asyncFun3(): owned(Future[void]) {....raises: [Exception], tags: [RootEffect].}
                                      @@ -853,7 +853,7 @@ ok1
                                      -
                                      proc anything() {...}{.raises: [], tags: [].}
                                      +
                                      proc anything() {....raises: [], tags: [].}
                                      There is no block quote after blank lines at the beginning. @@ -865,21 +865,21 @@ There is no block quote after blank lines at the beginning.

                                      Methods

                                      -
                                      method method1(self: Moo) {...}{.raises: [], tags: [].}
                                      +
                                      method method1(self: Moo) {....raises: [], tags: [].}
                                      foo1
                                      -
                                      method method2(self: Moo): int {...}{.raises: [], tags: [].}
                                      +
                                      method method2(self: Moo): int {....raises: [], tags: [].}
                                      foo2
                                      -
                                      method method3(self: Moo): int {...}{.raises: [], tags: [].}
                                      +
                                      method method3(self: Moo): int {....raises: [], tags: [].}
                                      foo3 @@ -891,7 +891,7 @@ foo3

                                      Iterators

                                      -
                                      iterator fromUtils1(): int {...}{.raises: [], tags: [].}
                                      +
                                      iterator fromUtils1(): int {....raises: [], tags: [].}
                                      @@ -902,14 +902,14 @@ foo3
                                      -
                                      iterator iter1(n: int): int {...}{.raises: [], tags: [].}
                                      +
                                      iterator iter1(n: int): int {....raises: [], tags: [].}
                                      foo1
                                      -
                                      iterator iter2(n: int): int {...}{.raises: [], tags: [].}
                                      +
                                      iterator iter2(n: int): int {....raises: [], tags: [].}
                                      foo2 From 6f53579a8ceb7576fb1b31f72c9d584916b4b5d9 Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 1 Apr 2021 14:47:59 +0800 Subject: [PATCH 1856/3768] close #9534 add testcase (#17607) --- tests/template/t9534.nim | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 tests/template/t9534.nim diff --git a/tests/template/t9534.nim b/tests/template/t9534.nim new file mode 100644 index 000000000..8d66f42a0 --- /dev/null +++ b/tests/template/t9534.nim @@ -0,0 +1,21 @@ +discard """ + targets: "c cpp" +""" + +# bug #9534 +type + Object = object + data: int + +template test() = + proc methodName(o: Object): int = + var p: pointer + doAssert o.data == 521 + let f {.used.} = cast[proc (o: int): int {.nimcall.}](p) + doAssert o.data == 521 + result = 1314 + + var a = Object(data: 521) + doAssert methodName(a) == 1314 + +test() From a31e601550cc6f9fd9e09b385bfbd869a408cd4b Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 31 Mar 2021 23:48:44 -0700 Subject: [PATCH 1857/3768] misc fixes: build_all.sh, changelog (#17601) * fix a bug in build_all.sh: $@ => "$@" * remove getSocket from changelog following #17587 * remove unused import --- build_all.sh | 2 +- changelog.md | 2 +- compiler/errorhandling.nim | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build_all.sh b/build_all.sh index e66980e56..c3c6e68b7 100755 --- a/build_all.sh +++ b/build_all.sh @@ -42,7 +42,7 @@ build_nim_csources(){ echo_run cp bin/nim $nim_csources } -[ -f $nim_csources ] || echo_run build_nim_csources $@ +[ -f $nim_csources ] || echo_run build_nim_csources "$@" # Note: if fails, may need to `cd csources && git pull` echo_run bin/nim c --skipUserCfg --skipParentCfg koch diff --git a/changelog.md b/changelog.md index 900693644..53d30bf36 100644 --- a/changelog.md +++ b/changelog.md @@ -73,7 +73,7 @@ - Added `asyncdispatch.activeDescriptors` that returns the number of currently active async event handles/file descriptors. -- Added to `asynchttpserver` `getPort` and `getSocket`. +- Added `getPort` to `asynchttpserver`. - `--gc:orc` is now 10% faster than previously for common workloads. If you have trouble with its changed behavior, compile with `-d:nimOldOrc`. diff --git a/compiler/errorhandling.nim b/compiler/errorhandling.nim index d7092d477..cda7ab3f4 100644 --- a/compiler/errorhandling.nim +++ b/compiler/errorhandling.nim @@ -10,7 +10,7 @@ ## This module contains support code for new-styled error ## handling via an `nkError` node kind. -import ast, renderer, options, lineinfos, strutils, types +import ast, renderer, options, strutils, types type ErrorKind* = enum ## expand as you need. From f3c504e49a4e24b7fbdcd5c8eec786fa86616233 Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 1 Apr 2021 14:49:10 +0800 Subject: [PATCH 1858/3768] a bit better message (#17606) --- compiler/ccgtypes.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 761fd5bf8..9d95e3f67 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -194,8 +194,8 @@ proc mapType(conf: ConfigRef; typ: PType; kind: TSymKind): TCTypeKind = result = TCTypeKind(ord(typ.kind) - ord(tyInt) + ord(ctInt)) of tyStatic: if typ.n != nil: result = mapType(conf, lastSon typ, kind) - else: doAssert(false, "mapType") - else: doAssert(false, "mapType") + else: doAssert(false, "mapType: " & $typ.kind) + else: doAssert(false, "mapType: " & $typ.kind) proc mapReturnType(conf: ConfigRef; typ: PType): TCTypeKind = #if skipTypes(typ, typedescInst).kind == tyArray: result = ctPtr From e25a487ed6237d30a461137d0a206e1e1a2f62d9 Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 1 Apr 2021 18:46:47 +0800 Subject: [PATCH 1859/3768] close #6133 add testcase (#17605) * close #6133 add testcase * b --- tests/system/tstatic.nim | 50 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 tests/system/tstatic.nim diff --git a/tests/system/tstatic.nim b/tests/system/tstatic.nim new file mode 100644 index 000000000..1f1b9dbe3 --- /dev/null +++ b/tests/system/tstatic.nim @@ -0,0 +1,50 @@ +discard """ + targets: "c cpp js" +""" + +import std/strutils + +# bug #6133 +template main() = + block: + block: + proc foo(q: string, a: int): int = + result = q.len + + proc foo(q: static[string]): int = + result = foo(q, 5) + + doAssert foo("123") == 3 + + block: + type E = enum A + + if false: + var e = A + discard $e + + proc foo(a: string): int = + len(a) # 16640 + + proc foo(a: static[bool]): int {.used.} = + discard + + doAssert foo("") == 0 + + block: + proc foo(a: string): int = + len(a) + + proc foo(a: static[bool]): int {.used.} = + discard + + doAssert foo("abc") == 3 + + block: + proc parseInt(f: static[bool]): int {.used.} = discard + + doAssert "123".parseInt == 123 + + +static: main() +main() From f0587e02e255b1fc1a0d0b21aebcacf37ec43c8f Mon Sep 17 00:00:00 2001 From: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Date: Thu, 1 Apr 2021 12:47:56 +0200 Subject: [PATCH 1860/3768] Fix #17017 (math edge cases) (#17588) * Fix #17017 Add more test cases * USe signbit in example --- lib/pure/math.nim | 18 ++++++++++++----- tests/stdlib/tmath.nim | 45 +++++++++++++++++++++++++++++++++++------- 2 files changed, 51 insertions(+), 12 deletions(-) diff --git a/lib/pure/math.nim b/lib/pure/math.nim index 74a98e655..2ef7ee102 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -949,18 +949,25 @@ func frexp*[T: float32|float64](x: T): tuple[frac: T, exp: int] {.inline.} = doAssert frexp(8.0) == (0.5, 4) doAssert frexp(-8.0) == (-0.5, 4) doAssert frexp(0.0) == (0.0, 0) + # special cases: - when not defined(windows): - doAssert frexp(-0.0) == (-0.0, 0) # signbit preserved for +-0 - doAssert frexp(Inf).frac == Inf # +- Inf preserved - doAssert frexp(NaN).frac.isNaN + doAssert frexp(-0.0).frac.signbit # signbit preserved for +-0 + doAssert frexp(Inf).frac == Inf # +- Inf preserved + doAssert frexp(NaN).frac.isNaN + when not defined(js): var exp: cint result.frac = c_frexp2(x, exp) result.exp = exp else: if x == 0.0: - result = (0.0, 0) + # reuse signbit implementation + let uintBuffer = toBitsImpl(x) + if (uintBuffer[1] shr 31) != 0: + # x is -0.0 + result = (-0.0, 0) + else: + result = (0.0, 0) elif x < 0.0: result = frexp(-x) result.frac = -result.frac @@ -980,6 +987,7 @@ func frexp*[T: float32|float64](x: T, exponent: var int): T {.inline.} = var x: int doAssert frexp(5.0, x) == 0.625 doAssert x == 3 + (result, exponent) = frexp(x) diff --git a/tests/stdlib/tmath.nim b/tests/stdlib/tmath.nim index 49b4c82f1..2cf544ddb 100644 --- a/tests/stdlib/tmath.nim +++ b/tests/stdlib/tmath.nim @@ -29,10 +29,6 @@ template main() = doAssert erf(6.0) > erf(5.0) doAssert erfc(6.0) < erfc(5.0) - when not defined(js) and not defined(windows): # xxx pending bug #17017 - doAssert gamma(-1.0).isNaN - - block: # sgn() tests doAssert sgn(1'i8) == 1 doAssert sgn(1'i16) == 1 @@ -46,6 +42,7 @@ template main() = doAssert sgn(123.9834'f64) == 1 doAssert sgn(0'i32) == 0 doAssert sgn(0'f32) == 0 + doAssert sgn(-0.0'f64) == 0 doAssert sgn(NegInf) == -1 doAssert sgn(Inf) == 1 doAssert sgn(NaN) == 0 @@ -358,7 +355,7 @@ template main() = doAssert almostEqual(prod([1.5, 3.4]), 5.1) let x: seq[float] = @[] doAssert prod(x) == 1.0 - + block: # clamp range doAssert clamp(10, 1..5) == 5 doAssert clamp(3, 1..5) == 3 @@ -370,8 +367,42 @@ template main() = doAssert a1.clamp(a2..a4) == a2 doAssert clamp((3, 0), (1, 0) .. (2, 9)) == (2, 9) - when not defined(windows): # xxx pending bug #17017 - doAssert sqrt(-1.0).isNaN + block: # edge cases + doAssert sqrt(-4.0).isNaN + + doAssert ln(0.0) == -Inf + doAssert ln(-0.0) == -Inf + doAssert ln(-12.0).isNaN + + doAssert log10(0.0) == -Inf + doAssert log10(-0.0) == -Inf + doAssert log10(-12.0).isNaN + + doAssert log2(0.0) == -Inf + doAssert log2(-0.0) == -Inf + doAssert log2(-12.0).isNaN + + when nimvm: discard + else: + doAssert frexp(0.0) == (0.0, 0) + doAssert frexp(-0.0) == (-0.0, 0) + doAssert classify(frexp(-0.0)[0]) == fcNegZero + + when not defined(js): + doAssert gamma(0.0) == Inf + doAssert gamma(-0.0) == -Inf + doAssert gamma(-1.0).isNaN + + doAssert lgamma(0.0) == Inf + doAssert lgamma(-0.0) == Inf + doAssert lgamma(-1.0) == Inf + + when nimvm: discard + else: + var exponent: cint + doAssert c_frexp(0.0, exponent) == 0.0 + doAssert c_frexp(-0.0, exponent) == -0.0 + doAssert classify(c_frexp(-0.0, exponent)) == fcNegZero static: main() main() From 2365b5250ec8a8893a376e75ad841d01c327893d Mon Sep 17 00:00:00 2001 From: flywind Date: Fri, 2 Apr 2021 12:06:50 +0800 Subject: [PATCH 1861/3768] docs: note regarding emit with backticks (#17608) --- doc/manual.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/manual.rst b/doc/manual.rst index b1aae2669..3c71ece95 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -7039,9 +7039,9 @@ Example: void fun(){} """.} -For backward compatibility, if the argument to the `emit` statement -is a single string literal, Nim symbols can be referred to via backticks. -This usage is however deprecated. +.. note:: For backward compatibility, if the argument to the `emit` statement + is a single string literal, Nim symbols can be referred to via backticks. + This usage is however deprecated. For a top-level emit statement, the section where in the generated C/C++ file the code should be emitted can be influenced via the From 792a03b7561195b27f5ad2b9654781cc17b86c75 Mon Sep 17 00:00:00 2001 From: flywind Date: Fri, 2 Apr 2021 15:18:40 +0800 Subject: [PATCH 1862/3768] [docs minor] close #16553 (#17612) Co-authored-by: Timothee Cour --- lib/pure/os.nim | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index d0b3aef1a..0710b8333 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -2266,16 +2266,12 @@ iterator walkDir*(dir: string; relative = false, checkDir = false): ## / fileA2.txt ## ## and this code: - ## - ## .. code-block:: Nim - ## for kind, path in walkDir("dirA"): - ## echo(path) - ## - ## produce this output (but not necessarily in this order!):: - ## dirA/dirB - ## dirA/dirC - ## dirA/fileA1.txt - ## dirA/fileA2.txt + runnableExamples("-r:off"): + import std/[strutils, sugar] + # note: order is not guaranteed + # this also works at compile time + assert collect(for k in walkDir("dirA"): k.path).join(" ") == + "dirA/dirB dirA/dirC dirA/fileA2.txt dirA/fileA1.txt" ## ## See also: ## * `walkPattern iterator <#walkPattern.i,string>`_ From 774e66f3d123520c44016ca23fd103538fc3c87d Mon Sep 17 00:00:00 2001 From: flywind Date: Fri, 2 Apr 2021 17:56:30 +0800 Subject: [PATCH 1863/3768] close #7875 add testcase (#17611) * close #7875 add testcase * fix --- tests/macros/t7875.nim | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 tests/macros/t7875.nim diff --git a/tests/macros/t7875.nim b/tests/macros/t7875.nim new file mode 100644 index 000000000..7b6e47b86 --- /dev/null +++ b/tests/macros/t7875.nim @@ -0,0 +1,22 @@ +discard """ + nimout: "var mysym`gensym0: MyType[float32]" + joinable: false +""" + +import macros + +type + MyType[T] = object + +# this is totally fine +var mysym: MyType[float32] + +macro foobar(): untyped = + let floatSym = bindSym"float32" + + result = quote do: + var mysym: MyType[`floatSym`] + + echo result.repr + +foobar() From 9abd383a2ae36e50ee23b78654e9d8154db46c84 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 2 Apr 2021 04:11:03 -0700 Subject: [PATCH 1864/3768] new `genAst` as replacement for `quote do` (#17426) * new `macros.genAst`: fixes all issues with `quote do` * add changelog entry * add workaround for https://github.com/nim-lang/Nim/issues/2465#issuecomment-511076669 * add test for #9607 * add kNoExposeLocalInjects option * add test case for nested application of genAst * genAst: automatically call newLit when needed * allow skipping `{}`: genAst: foo * add test that shows this fixes #11986 * add examples showing mixin; add examples showing passing types, macros, templates * move to std/genasts * improve docs --- changelog.md | 3 + lib/core/macros.nim | 5 +- lib/std/genasts.nim | 87 +++++++++++++ tests/stdlib/mgenast.nim | 53 ++++++++ tests/stdlib/tgenast.nim | 269 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 416 insertions(+), 1 deletion(-) create mode 100644 lib/std/genasts.nim create mode 100644 tests/stdlib/mgenast.nim create mode 100644 tests/stdlib/tgenast.nim diff --git a/changelog.md b/changelog.md index 53d30bf36..020d8a350 100644 --- a/changelog.md +++ b/changelog.md @@ -255,6 +255,9 @@ - Added `hasClosure` to `std/typetraits`. +- Added `genasts.genAst` that avoids the problems inherent with `quote do` and can + be used as a replacement. + ## Language changes - `nimscript` now handles `except Exception as e`. diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 795909c6b..49c9a999c 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -539,6 +539,7 @@ proc parseStmt*(s: string): NimNode {.noSideEffect.} = proc getAst*(macroOrTemplate: untyped): NimNode {.magic: "ExpandToAst", noSideEffect.} ## Obtains the AST nodes returned from a macro or template invocation. + ## See also `genasts.genAst`. ## Example: ## ## .. code-block:: nim @@ -559,6 +560,8 @@ proc quote*(bl: typed, op = "``"): NimNode {.magic: "QuoteAst", noSideEffect.} = ## ## A custom operator interpolation needs accent quoted (``) whenever it resolves ## to a symbol. + ## + ## See also `genasts `_ which avoids some issues with `quote`. runnableExamples: macro check(ex: untyped) = # this is a simplified version of the check macro from the @@ -1419,7 +1422,7 @@ proc expectIdent*(n: NimNode, name: string) {.since: (1,1).} = if not eqIdent(n, name): error("Expected identifier to be `" & name & "` here", n) -proc hasArgOfName*(params: NimNode; name: string): bool= +proc hasArgOfName*(params: NimNode; name: string): bool = ## Search `nnkFormalParams` for an argument. expectKind(params, nnkFormalParams) for i in 1..`_ for details. + # Default is unset, to avoid hijacking of uncaptured local symbols by + # symbols in caller scope. + kNoNewLit, + # don't call call newLit automatically in `genAst` capture parameters + +macro genAstOpt*(options: static set[GenAstOpt], args: varargs[untyped]): untyped = + ## Accepts a list of captured variables `a=b` or `a` and a block and returns the + ## AST that represents it. Local `{.inject.}` symbols (e.g. procs) are captured + ## unless `kDirtyTemplate in options`. + runnableExamples: + # This example shows how one could write a simplified version of `unittest.check`. + import std/[macros, strutils] + macro check2(cond: bool): untyped = + assert cond.kind == nnkInfix, "$# not implemented" % $cond.kind + result = genAst(cond, s = repr(cond), lhs = cond[1], rhs = cond[2]): + # each local symbol we access must be explicitly captured + if not cond: + doAssert false, "'$#'' failed: lhs: '$#', rhs: '$#'" % [s, $lhs, $rhs] + let a = 3 + check2 a*2 == a+3 + if false: check2 a*2 < a+1 # would error with: 'a * 2 < a + 1'' failed: lhs: '6', rhs: '4' + + runnableExamples: + # This example goes in more details about the capture semantics. + macro fun(a: string, b: static bool): untyped = + let c = 'z' + var d = 11 # implicitly {.gensym.} and needs to be captured for use in `genAst`. + proc localFun(): auto = 12 # implicitly {.inject.}, doesn't need to be captured. + genAst(a, b, c = true): + # `a`, `b` are captured explicitly, `c` is a local definition masking `c = 'z'`. + const b2 = b # macro static param `b` is forwarded here as a static param. + # `echo d` would give: `var not init` because `d` is not captured. + (a & a, b, c, localFun()) # localFun can be called without capture. + assert fun("ab", false) == ("abab", false, true, 12) + + let params = newTree(nnkFormalParams, newEmptyNode()) + let pragmas = + if kDirtyTemplate in options: + nnkPragma.newTree(ident"dirty") + else: + newEmptyNode() + + template newLitMaybe(a): untyped = + when (a is type) or (typeof(a) is (proc | iterator | func | NimNode)): + a # `proc` actually also covers template, macro + else: newLit(a) + + # using `_` as workaround, see https://github.com/nim-lang/Nim/issues/2465#issuecomment-511076669 + let name = genSym(nskTemplate, "_fun") + let call = newCall(name) + for a in args[0..^2]: + var varName: NimNode + var varVal: NimNode + case a.kind + of nnkExprEqExpr: + varName = a[0] + varVal = a[1] + of nnkIdent: + varName = a + varVal = a + else: error("invalid argument kind: " & $a.kind, a) + if kNoNewLit notin options: varVal = newCall(bindSym"newLitMaybe", varVal) + + params.add newTree(nnkIdentDefs, varName, newEmptyNode(), newEmptyNode()) + call.add varVal + + result = newStmtList() + result.add nnkTemplateDef.newTree( + name, + newEmptyNode(), + newEmptyNode(), + params, + pragmas, + newEmptyNode(), + args[^1]) + result.add newCall(bindSym"getAst", call) + +template genAst*(args: varargs[untyped]): untyped = + ## Convenience wrapper around `genAstOpt`. + genAstOpt({}, args) diff --git a/tests/stdlib/mgenast.nim b/tests/stdlib/mgenast.nim new file mode 100644 index 000000000..2b5381891 --- /dev/null +++ b/tests/stdlib/mgenast.nim @@ -0,0 +1,53 @@ +import std/genasts +import std/macros + +# Using a enum instead of, say, int, to make apparent potential bugs related to +# forgetting converting to NimNode via newLit, see bug #9607 + +type Foo* = enum kfoo0, kfoo1, kfoo2, kfoo3, kfoo4 + +proc myLocalPriv(): auto = kfoo1 +proc myLocalPriv2(): auto = kfoo1 +macro bindme2*(): untyped = + genAst: myLocalPriv() +macro bindme3*(): untyped = + ## myLocalPriv must be captured explicitly + genAstOpt({kDirtyTemplate}, myLocalPriv): myLocalPriv() + +macro bindme4*(): untyped = + ## calling this won't compile because `myLocalPriv` isn't captured + genAstOpt({kDirtyTemplate}): myLocalPriv() + +macro bindme5UseExpose*(): untyped = + genAst: myLocalPriv2() + +macro bindme5UseExposeFalse*(): untyped = + genAstOpt({kDirtyTemplate}): myLocalPriv2() + +# example from bug #7889 +from std/streams import newStringStream, readData, writeData + +macro bindme6UseExpose*(): untyped = + genAst: + var tst = "sometext" + var ss = newStringStream("anothertext") + writeData(ss, tst[0].addr, 2) + discard readData(ss, tst[0].addr, 2) + +macro bindme6UseExposeFalse*(): untyped = + ## with `kDirtyTemplate`, requires passing all referenced symbols + ## which can be tedious + genAstOpt({kDirtyTemplate}, newStringStream, writeData, readData): + var tst = "sometext" + var ss = newStringStream("anothertext") + writeData(ss, tst[0].addr, 2) + discard readData(ss, tst[0].addr, 2) + + +proc locafun1(): auto = "in locafun1" +proc locafun2(): auto = "in locafun2" +# locafun3 in caller scope only +macro mixinExample*(): untyped = + genAst: + mixin locafun1 + (locafun1(), locafun2(), locafun3()) diff --git a/tests/stdlib/tgenast.nim b/tests/stdlib/tgenast.nim new file mode 100644 index 000000000..0904b83dd --- /dev/null +++ b/tests/stdlib/tgenast.nim @@ -0,0 +1,269 @@ +# xxx also test on js + +import std/genasts +import std/macros +from std/strformat import `&` +import ./mgenast + +proc main = + block: + macro bar(x0: static Foo, x1: Foo, x2: Foo, xignored: Foo): untyped = + let s0 = "not captured!" + let s1 = "not captured!" + let xignoredLocal = kfoo4 + + # newLit optional: + let x3 = newLit kfoo4 + let x3b = kfoo4 + + result = genAstOpt({kDirtyTemplate}, s1=true, s2="asdf", x0, x1=x1, x2, x3, x3b): + doAssert not declared(xignored) + doAssert not declared(xignoredLocal) + (s1, s2, s0, x0, x1, x2, x3, x3b) + + let s0 = "caller scope!" + + doAssert bar(kfoo1, kfoo2, kfoo3, kfoo4) == + (true, "asdf", "caller scope!", kfoo1, kfoo2, kfoo3, kfoo4, kfoo4) + + block: + # doesn't have limitation mentioned in https://github.com/nim-lang/RFCs/issues/122#issue-401636535 + macro abc(name: untyped): untyped = + result = genAst(name): + type name = object + + abc(Bar) + doAssert Bar.default == Bar() + + block: + # backticks parser limitations / ambiguities not are an issue with `genAst`: + # (#10326 #9745 are fixed but `quote do` still has underlying ambiguity issue + # with backticks) + type Foo = object + a: int + + macro m1(): untyped = + # result = quote do: # Error: undeclared identifier: 'a1' + result = genAst: + template `a1=`(x: var Foo, val: int) = + x.a = val + + m1() + var x0: Foo + x0.a1 = 10 + doAssert x0 == Foo(a: 10) + + block: + # avoids bug #7375 + macro fun(b: static[bool], b2: bool): untyped = + result = newStmtList() + macro foo(c: bool): untyped = + var b = false + result = genAst(b, c): + fun(b, c) + + foo(true) + + block: + # avoids bug #7589 + # since `==` works with genAst, the problem goes away + macro foo2(): untyped = + # result = quote do: # Error: '==' cannot be passed to a procvar + result = genAst: + `==`(3,4) + doAssert not foo2() + + block: + # avoids bug #7726 + # expressions such as `a.len` are just passed as arguments to `genAst`, and + # caller scope is not polluted with definitions such as `let b = newLit a.len` + macro foo(): untyped = + let a = @[1, 2, 3, 4, 5] + result = genAst(a, b = a.len): # shows 2 ways to get a.len + (a.len, b) + doAssert foo() == (5, 5) + + block: + # avoids bug #9607 + proc fun1(info:LineInfo): string = "bar1" + proc fun2(info:int): string = "bar2" + + macro bar2(args: varargs[untyped]): untyped = + let info = args.lineInfoObj + let fun1 = bindSym"fun1" # optional; we can remove this and also the + # capture of fun1, as show in next example + result = genAst(info, fun1): + (fun1(info), fun2(info.line)) + doAssert bar2() == ("bar1", "bar2") + + macro bar3(args: varargs[untyped]): untyped = + let info = args.lineInfoObj + result = genAst(info): + (fun1(info), fun2(info.line)) + doAssert bar3() == ("bar1", "bar2") + + macro bar(args: varargs[untyped]): untyped = + let info = args.lineInfoObj + let fun1 = bindSym"fun1" + let fun2 = bindSym"fun2" + result = genAstOpt({kDirtyTemplate}, info): + (fun1(info), fun2(info.line)) + doAssert bar() == ("bar1", "bar2") + + block: + # example from bug #7889 works + # after changing method call syntax to regular call syntax; this is a + # limitation described in bug #7085 + # note that `quote do` would also work after that change in this example. + doAssert bindme2() == kfoo1 + doAssert bindme3() == kfoo1 + doAssert not compiles(bindme4()) # correctly gives Error: undeclared identifier: 'myLocalPriv' + proc myLocalPriv2(): auto = kfoo2 + doAssert bindme5UseExpose() == kfoo1 + + # example showing hijacking behavior when using `kDirtyTemplate` + doAssert bindme5UseExposeFalse() == kfoo2 + # local `myLocalPriv2` hijacks symbol `mgenast.myLocalPriv2`. In most + # use cases this is probably not what macro writer intends as it's + # surprising; hence `kDirtyTemplate` is not the default. + + when nimvm: # disabled because `newStringStream` is used + discard + else: + bindme6UseExpose() + bindme6UseExposeFalse() + + block: + macro mbar(x3: Foo, x3b: static Foo): untyped = + var x1=kfoo3 + var x2=newLit kfoo3 + var x4=kfoo3 + var xLocal=kfoo3 + + proc funLocal(): auto = kfoo4 + + result = genAst(x1, x2, x3, x4): + # local x1 overrides remote x1 + when false: + # one advantage of using `kDirtyTemplate` is that these would hold: + doAssert not declared xLocal + doAssert not compiles(echo xLocal) + # however, even without it, we at least correctly generate CT error + # if trying to use un-captured symbol; this correctly gives: + # Error: internal error: environment misses: xLocal + echo xLocal + + proc foo1(): auto = + # note that `funLocal` is captured implicitly, according to hygienic + # template rules; with `kDirtyTemplate` it would not unless + # captured in `genAst` capture list explicitly + (a0: xRemote, a1: x1, a2: x2, a3: x3, a4: x4, a5: funLocal()) + + return result + + proc main()= + var xRemote=kfoo1 + var x1=kfoo2 + mbar(kfoo4, kfoo4) + doAssert foo1() == (a0: kfoo1, a1: kfoo3, a2: kfoo3, a3: kfoo4, a4: kfoo3, a5: kfoo4) + + main() + + block: + # With `kDirtyTemplate`, the example from #8220 works. + # See https://nim-lang.github.io/Nim/strformat.html#limitations for + # an explanation of why {.dirty.} is needed. + macro foo(): untyped = + result = genAstOpt({kDirtyTemplate}): + let bar = "Hello, World" + &"Let's interpolate {bar} in the string" + doAssert foo() == "Let's interpolate Hello, World in the string" + + + block: # nested application of genAst + macro createMacro(name, obj, field: untyped): untyped = + result = genAst(obj = newDotExpr(obj, field), lit = 10, name, field): + # can't reuse `result` here, would clash + macro name(arg: untyped): untyped = + genAst(arg2=arg): # somehow `arg2` rename is needed + (obj, astToStr(field), lit, arg2) + + var x = @[1, 2, 3] + createMacro foo, x, len + doAssert (foo 20) == (3, "len", 10, 20) + + block: # test with kNoNewLit + macro bar(): untyped = + let s1 = true + template boo(x): untyped = + fun(x) + result = genAstOpt({kNoNewLit}, s1=newLit(s1), s1b=s1): (s1, s1b) + doAssert bar() == (true, 1) + + block: # sanity check: check passing `{}` also works + macro bar(): untyped = + result = genAstOpt({}, s1=true): s1 + doAssert bar() == true + + block: # test passing function and type symbols + proc z1(): auto = 41 + type Z4 = type(1'i8) + macro bar(Z1: typedesc): untyped = + proc z2(): auto = 42 + proc z3[T](a: T): auto = 43 + let Z2 = genAst(): + type(true) + let z4 = genAst(): + proc myfun(): auto = 44 + myfun + type Z3 = type(1'u8) + result = genAst(z4, Z1, Z2): + # z1, z2, z3, Z3, Z4 are captured automatically + # z1, z2, z3 can optionally be specified in capture list + (z1(), z2(), z3('a'), z4(), $Z1, $Z2, $Z3, $Z4) + type Z1 = type('c') + doAssert bar(Z1) == (41, 42, 43, 44, "char", "bool", "uint8", "int8") + + block: # fix bug #11986 + proc foo(): auto = + var s = { 'a', 'b' } + # var n = quote do: `s` # would print {97, 98} + var n = genAst(s): s + n.repr + static: doAssert foo() == "{'a', 'b'}" + + block: # also from #11986 + macro foo(): untyped = + var s = { 'a', 'b' } + # quote do: + # let t = `s` + # $typeof(t) # set[range 0..65535(int)] + genAst(s): + let t = s + $typeof(t) + doAssert foo() == "set[char]" + + block: + macro foo(): untyped = + type Foo = object + template baz2(a: int): untyped = a*10 + macro baz3(a: int): untyped = newLit 13 + result = newStmtList() + + result.add genAst(Foo, baz2, baz3) do: # shows you can pass types, templates etc + var x: Foo + $($typeof(x), baz2(3), baz3(4)) + + let ret = genAst() do: # shows you don't have to, since they're inject'd + var x: Foo + $($typeof(x), baz2(3), baz3(4)) + doAssert foo() == """("Foo", 30, 13)""" + + block: # illustrates how symbol visiblity can be controlled precisely using `mixin` + proc locafun1(): auto = "in locafun1 (caller scope)" # this will be used because of `mixin locafun1` => explicit hijacking is ok + proc locafun2(): auto = "in locafun2 (caller scope)" # this won't be used => no hijacking + proc locafun3(): auto = "in locafun3 (caller scope)" + doAssert mixinExample() == ("in locafun1 (caller scope)", "in locafun2", "in locafun3 (caller scope)") + +static: main() +main() From 07991d902e6630593ce2db0f34c06bbb2abb336e Mon Sep 17 00:00:00 2001 From: flywind Date: Sat, 3 Apr 2021 04:06:29 +0800 Subject: [PATCH 1865/3768] [docs minor] close #17618 (#17620) * [docs minor] close #17618 * Update lib/pure/times.nim Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> --- lib/pure/times.nim | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index e763b17bb..a92eccc8c 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -313,8 +313,11 @@ type Duration* = object ## Represents a fixed duration of time, meaning a duration ## that has constant length independent of the context. ## - ## To create a new `Duration`, use `initDuration proc + ## To create a new `Duration`, use `initDuration ## <#initDuration,int64,int64,int64,int64,int64,int64,int64,int64>`_. + ## Instead of trying to access the private attributes, use + ## `inSeconds <#inSeconds,Duration>`_ for converting to seconds and + ## `inNanoseconds <#inNanoseconds,Duration>`_ for converting to nanoseconds. seconds: int64 nanosecond: NanosecondRange @@ -622,56 +625,56 @@ template convert(dur: Duration, unit: static[FixedTimeUnit]): int64 = convert(Nanoseconds, unit, dur.nanosecond) proc inWeeks*(dur: Duration): int64 = - ## Convert the duration to the number of whole weeks. + ## Converts the duration to the number of whole weeks. runnableExamples: let dur = initDuration(days = 8) doAssert dur.inWeeks == 1 dur.convert(Weeks) proc inDays*(dur: Duration): int64 = - ## Convert the duration to the number of whole days. + ## Converts the duration to the number of whole days. runnableExamples: let dur = initDuration(hours = -50) doAssert dur.inDays == -2 dur.convert(Days) proc inHours*(dur: Duration): int64 = - ## Convert the duration to the number of whole hours. + ## Converts the duration to the number of whole hours. runnableExamples: let dur = initDuration(minutes = 60, days = 2) doAssert dur.inHours == 49 dur.convert(Hours) proc inMinutes*(dur: Duration): int64 = - ## Convert the duration to the number of whole minutes. + ## Converts the duration to the number of whole minutes. runnableExamples: let dur = initDuration(hours = 2, seconds = 10) doAssert dur.inMinutes == 120 dur.convert(Minutes) proc inSeconds*(dur: Duration): int64 = - ## Convert the duration to the number of whole seconds. + ## Converts the duration to the number of whole seconds. runnableExamples: let dur = initDuration(hours = 2, milliseconds = 10) doAssert dur.inSeconds == 2 * 60 * 60 dur.convert(Seconds) proc inMilliseconds*(dur: Duration): int64 = - ## Convert the duration to the number of whole milliseconds. + ## Converts the duration to the number of whole milliseconds. runnableExamples: let dur = initDuration(seconds = -2) doAssert dur.inMilliseconds == -2000 dur.convert(Milliseconds) proc inMicroseconds*(dur: Duration): int64 = - ## Convert the duration to the number of whole microseconds. + ## Converts the duration to the number of whole microseconds. runnableExamples: let dur = initDuration(seconds = -2) doAssert dur.inMicroseconds == -2000000 dur.convert(Microseconds) proc inNanoseconds*(dur: Duration): int64 = - ## Convert the duration to the number of whole nanoseconds. + ## Converts the duration to the number of whole nanoseconds. runnableExamples: let dur = initDuration(seconds = -2) doAssert dur.inNanoseconds == -2000000000 From e35946f306c31d02869dfc4f191b76893b792700 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Fri, 2 Apr 2021 23:11:44 +0300 Subject: [PATCH 1866/3768] enable syntax highlighting for inline code (#17585) * enable syntax highlighting for inline code * finish '.. default-role' and preliminary '.. role' implementation * more compact check in dirRole * set :literal: as default role for *.rst * Update lib/packages/docutils/rst.nim Co-authored-by: Timothee Cour * use whichRole for setting currRoleKind * Update lib/packages/docutils/rst.nim Co-authored-by: Timothee Cour * rename rnGeneralRole -> rnUnknownRole Co-authored-by: Timothee Cour --- compiler/docgen.nim | 3 +- doc/contributing.rst | 38 ++++++ lib/packages/docutils/rst.nim | 116 +++++++++++++++---- lib/packages/docutils/rstast.nim | 9 +- lib/packages/docutils/rstgen.nim | 33 ++++-- nimdoc/rst2html/expected/rst_examples.html | 6 +- nimdoc/rst2html/source/rst_examples.rst | 4 + nimdoc/testproject/expected/testproject.html | 8 +- tests/stdlib/trstgen.nim | 73 ++++++++++-- 9 files changed, 239 insertions(+), 51 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 67f21e1c4..37d8955f6 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -192,7 +192,8 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef, result.cache = cache result.outDir = conf.outDir.string initRstGenerator(result[], (if conf.cmd != cmdRst2tex: outHtml else: outLatex), - conf.configVars, filename.string, {roSupportRawDirective, roSupportMarkdown}, + conf.configVars, filename.string, + {roSupportRawDirective, roSupportMarkdown, roNimFile}, docgenFindFile, compilerMsgHandler) if conf.configVars.hasKey("doc.googleAnalytics"): diff --git a/doc/contributing.rst b/doc/contributing.rst index 279a4ee94..7ee3aa444 100644 --- a/doc/contributing.rst +++ b/doc/contributing.rst @@ -295,6 +295,44 @@ example below) from `Nim Index`_ can be used in doc comment this way: .. _`Nim Index`: https://nim-lang.org/docs/theindex.html +Inline monospaced text can be input using \`single backticks\` or +\`\`double backticks\`\`. The former are syntactically highlighted, +the latter are not. +To avoid accidental highlighting follow this rule in `*.nim` files: + +* use single backticks for fragments of code in Nim and other + programming languages, including identifiers, in `*.nim` files. + + For languages other than Nim add a role after final backtick, + e.g. for C++ inline highlighting:: + + `#include `:cpp: + + For a currently unsupported language add the `:code:` role, + like for SQL in this example:: + + `SELECT * FROM ;`:code: + +* prefer double backticks otherwise: + + * for file names: \`\`os.nim\`\` + * for fragments of strings **not** enclosed by `"` and `"` and not + related to code, e.g. text of compiler messages + * for command line options: \`\`--docInternal\`\` + * also when code ends with a standalone ``\`` (otherwise a combination of + ``\`` and a final \` would get escaped) + +.. Note:: `*.rst` files have `:literal:` as their default role. + So for them the rule above is only applicable if the `:nim:` role + is set up manually as the default:: + + .. role:: nim(code) + :language: nim + .. default-role:: nim + + The first 2 lines are for other RST implementations, + including Github one. + Best practices ============== diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index d331c2c12..66efa3a3c 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -78,6 +78,13 @@ ## ## * directives: ``code-block`` [cmp:Sphinx]_, ``title``, ## ``index`` [cmp:Sphinx]_ +## * predefined roles ``:nim:`` (default), ``:c:`` (C programming language), +## ``:python:``, ``:yaml:``, ``:java:``, ``:cpp:`` (C++), ``:csharp`` (C#). +## That is every language that `highlite `_ supports. +## They turn on appropriate syntax highlighting in inline code. +## +## .. Note:: default role for Nim files is ``:nim:``, +## for ``*.rst`` it's currently ``:literal:``. ## ## * ***triple emphasis*** (bold and italic) using \*\*\* ## * ``:idx:`` role for \`interpreted text\` to include the link to this @@ -161,7 +168,9 @@ type roSupportSmilies, ## make the RST parser support smilies like ``:)`` roSupportRawDirective, ## support the ``raw`` directive (don't support ## it for sandboxing) - roSupportMarkdown ## support additional features of Markdown + roSupportMarkdown, ## support additional features of Markdown + roNimFile ## set for Nim files where default interpreted + ## text role should be :nim: RstParseOptions* = set[RstParseOption] @@ -454,6 +463,8 @@ type hTitleCnt: int # =0 if no title, =1 if only main title, # =2 if both title and subtitle are present hCurLevel: int # current section level + currRole: string # current interpreted text role + currRoleKind: RstNodeKind # ... and its node kind subs: seq[Substitution] # substitutions refs: seq[Substitution] # references anchors: seq[AnchorSubst] # internal target substitutions @@ -514,10 +525,36 @@ proc defaultFindFile*(filename: string): string = if fileExists(filename): result = filename else: result = "" +proc defaultRole(options: RstParseOptions): string = + if roNimFile in options: "nim" else: "literal" + +# mirror highlite.nim sourceLanguageToStr with substitutions c++ cpp, c# csharp +const supportedLanguages = ["nim", "yaml", "python", "java", "c", + "cpp", "csharp"] + +proc whichRoleAux(sym: string): RstNodeKind = + let r = sym.toLowerAscii + case r + of "idx": result = rnIdx + of "literal": result = rnInlineLiteral + of "strong": result = rnStrongEmphasis + of "emphasis": result = rnEmphasis + of "sub", "subscript": result = rnSub + of "sup", "superscript": result = rnSup + # literal and code are the same in our implementation + of "code": result = rnInlineLiteral + # c++ currently can be spelled only as cpp, c# only as csharp + elif r in supportedLanguages: + result = rnInlineCode + else: # unknown role + result = rnUnknownRole + proc newSharedState(options: RstParseOptions, findFile: FindFileHandler, msgHandler: MsgHandler): PSharedState = new(result) + result.currRole = defaultRole(options) + result.currRoleKind = whichRoleAux(result.currRole) result.subs = @[] result.refs = @[] result.options = options @@ -1018,15 +1055,28 @@ proc fixupEmbeddedRef(n, a, b: PRstNode) = for i in countup(0, sep - incr): a.add(n.sons[i]) for i in countup(sep + 1, n.len - 2): b.add(n.sons[i]) -proc whichRole(sym: string): RstNodeKind = - case sym - of "idx": result = rnIdx - of "literal": result = rnInlineLiteral - of "strong": result = rnStrongEmphasis - of "emphasis": result = rnEmphasis - of "sub", "subscript": result = rnSub - of "sup", "superscript": result = rnSup - else: result = rnGeneralRole +proc whichRole(p: RstParser, sym: string): RstNodeKind = + result = whichRoleAux(sym) + if result == rnUnknownRole: + rstMessage(p, mwUnsupportedLanguage, p.s.currRole) + +proc toInlineCode(n: PRstNode, language: string): PRstNode = + ## Creates rnInlineCode and attaches `n` contents as code (in 3rd son). + result = newRstNode(rnInlineCode) + let args = newRstNode(rnDirArg) + var lang = language + if language == "cpp": lang = "c++" + elif language == "csharp": lang = "c#" + args.add newLeaf(lang) + result.add args + result.add PRstNode(nil) + var lb = newRstNode(rnLiteralBlock) + var s: string + for i in n.sons: + assert i.kind == rnLeaf + s.add i.text + lb.add newLeaf(s) + result.add lb proc parsePostfix(p: var RstParser, n: PRstNode): PRstNode = var newKind = n.kind @@ -1052,14 +1102,23 @@ proc parsePostfix(p: var RstParser, n: PRstNode): PRstNode = result = newRstNode(newKind, newSons) elif match(p, p.idx, ":w:"): # a role: - newKind = whichRole(nextTok(p).symbol) - if newKind == rnGeneralRole: + let roleName = nextTok(p).symbol + newKind = whichRole(p, roleName) + if newKind == rnUnknownRole: let newN = newRstNode(rnInner, n.sons) - newSons = @[newN, newLeaf(nextTok(p).symbol)] + newSons = @[newN, newLeaf(roleName)] + result = newRstNode(newKind, newSons) + elif newKind == rnInlineCode: + result = n.toInlineCode(language=roleName) + else: + result = newRstNode(newKind, newSons) inc p.idx, 3 - result = newRstNode(newKind, newSons) - else: # no change - result = n + else: + if p.s.currRoleKind == rnInlineCode: + result = n.toInlineCode(language=p.s.currRole) + else: + newKind = p.s.currRoleKind + result = newRstNode(newKind, newSons) proc matchVerbatim(p: RstParser, start: int, expr: string): int = result = start @@ -1315,9 +1374,12 @@ proc parseInline(p: var RstParser, father: PRstNode) = parseUntil(p, n, "``", false) father.add(n) elif match(p, p.idx, ":w:") and p.tok[p.idx+3].symbol == "`": - let k = whichRole(nextTok(p).symbol) - let n = newRstNode(k) + let roleName = nextTok(p).symbol + let k = whichRole(p, roleName) + var n = newRstNode(k) inc p.idx, 3 + if k == rnInlineCode: + n = n.toInlineCode(language=roleName) parseUntil(p, n, "`", false) # bug #17260 father.add(n) elif isInlineMarkupStart(p, "`"): @@ -2421,6 +2483,18 @@ proc dirAdmonition(p: var RstParser, d: string): PRstNode = proc dirDefaultRole(p: var RstParser): PRstNode = result = parseDirective(p, rnDefaultRole, {hasArg}, nil) + if result.sons[0].len == 0: p.s.currRole = defaultRole(p.s.options) + else: + assert result.sons[0].sons[0].kind == rnLeaf + p.s.currRole = result.sons[0].sons[0].text + p.s.currRoleKind = whichRole(p, p.s.currRole) + +proc dirRole(p: var RstParser): PRstNode = + result = parseDirective(p, rnDirective, {hasArg, hasOptions}, nil) + # just check that language is supported, TODO: real role association + let lang = getFieldValue(result, "language").strip + if lang != "" and lang notin supportedLanguages: + rstMessage(p, mwUnsupportedLanguage, lang) proc dirRawAux(p: var RstParser, result: var PRstNode, kind: RstNodeKind, contentParser: SectionParser) = @@ -2465,7 +2539,9 @@ proc selectDir(p: var RstParser, d: string): PRstNode = of "code-block": result = dirCodeBlock(p, nimExtension = true) of "container": result = dirContainer(p) of "contents": result = dirContents(p) - of "danger", "error": result = dirAdmonition(p, d) + of "danger": result = dirAdmonition(p, d) + of "default-role": result = dirDefaultRole(p) + of "error": result = dirAdmonition(p, d) of "figure": result = dirFigure(p) of "hint": result = dirAdmonition(p, d) of "image": result = dirImage(p) @@ -2478,10 +2554,10 @@ proc selectDir(p: var RstParser, d: string): PRstNode = result = dirRaw(p) else: rstMessage(p, meInvalidDirective, d) + of "role": result = dirRole(p) of "tip": result = dirAdmonition(p, d) of "title": result = dirTitle(p) of "warning": result = dirAdmonition(p, d) - of "default-role": result = dirDefaultRole(p) else: let tok = p.tok[p.idx-2] # report on directive in ".. directive::" rstMessage(p, meInvalidDirective, d, tok.line, tok.col) diff --git a/lib/packages/docutils/rstast.nim b/lib/packages/docutils/rstast.nim index ff34da2d1..dd456b577 100644 --- a/lib/packages/docutils/rstast.nim +++ b/lib/packages/docutils/rstast.nim @@ -55,12 +55,15 @@ type # * `file#id `_ # * `file#id '_ rnSubstitutionDef, # a definition of a substitution - rnGeneralRole, # Inline markup: + # Inline markup: + rnInlineCode, + rnUnknownRole, # interpreted text with an unknown role rnSub, rnSup, rnIdx, rnEmphasis, # "*" rnStrongEmphasis, # "**" rnTripleEmphasis, # "***" - rnInterpretedText, # "`" + rnInterpretedText, # "`" an auxiliary role for parsing that will + # be converted into other kinds like rnInlineCode rnInlineLiteral, # "``" rnInlineTarget, # "_`target`" rnSubstitutionReferences, # "|" @@ -252,7 +255,7 @@ proc renderRstToRst(d: var RenderContext, n: PRstNode, result: var string) = result.add(" <") renderRstToRst(d, n.sons[1], result) result.add(">`_") - of rnGeneralRole: + of rnUnknownRole: result.add('`') renderRstToRst(d, n.sons[0],result) result.add("`:") diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 30c7f3080..1a16f590e 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -942,7 +942,7 @@ proc parseCodeBlockParams(d: PDoc, n: PRstNode): CodeBlockParams = result.init if n.isNil: return - assert n.kind == rnCodeBlock + assert n.kind in {rnCodeBlock, rnInlineCode} assert(not n.sons[2].isNil) # Parse the field list for rendering parameters if there are any. @@ -987,8 +987,8 @@ proc buildLinesHtmlTable(d: PDoc; params: CodeBlockParams, code: string, "
                                      text1
                                      " & ( d.config.getOrDefault"doc.listing_button" % id) -proc renderCodeBlock(d: PDoc, n: PRstNode, result: var string) = - ## Renders a code block, appending it to `result`. +proc renderCode(d: PDoc, n: PRstNode, result: var string) = + ## Renders a code (code block or inline code), appending it to `result`. ## ## If the code block uses the ``number-lines`` option, a table will be ## generated with two columns, the first being a list of numbers and the @@ -997,7 +997,7 @@ proc renderCodeBlock(d: PDoc, n: PRstNode, result: var string) = ## may also come from the parser through the internal ``default-language`` ## option to differentiate between a plain code block and Nim's code block ## extension. - assert n.kind == rnCodeBlock + assert n.kind in {rnCodeBlock, rnInlineCode} if n.sons[2] == nil: return var params = d.parseCodeBlockParams(n) var m = n.sons[2].sons[0] @@ -1006,10 +1006,23 @@ proc renderCodeBlock(d: PDoc, n: PRstNode, result: var string) = if params.testCmd.len > 0 and d.onTestSnippet != nil: d.onTestSnippet(d, params.filename, params.testCmd, params.status, m.text) - let (blockStart, blockEnd) = buildLinesHtmlTable(d, params, m.text, + var blockStart, blockEnd: string + case d.target + of outHtml: + if n.kind == rnCodeBlock: + (blockStart, blockEnd) = buildLinesHtmlTable(d, params, m.text, n.anchor.idS) - dispA(d.target, result, blockStart, - "\\begin{rstpre}\n" & n.anchor.idS & "\n", []) + else: # rnInlineCode + blockStart = "" + blockEnd = "" + of outLatex: + if n.kind == rnCodeBlock: + blockStart = "\n\n\\begin{rstpre}" & n.anchor.idS & "\n" + blockEnd = "\n\\end{rstpre}\n" + else: # rnInlineCode + blockStart = "\\texttt{" + blockEnd = "}" + dispA(d.target, result, blockStart, blockStart, []) if params.lang == langNone: if len(params.langStr) > 0: d.msgHandler(d.filename, 1, 0, mwUnsupportedLanguage, params.langStr) @@ -1028,7 +1041,7 @@ proc renderCodeBlock(d: PDoc, n: PRstNode, result: var string) = esc(d.target, substr(m.text, g.start, g.length+g.start-1)), tokenClassToStr[g.kind]]) deinitGeneralTokenizer(g) - dispA(d.target, result, blockEnd, "\n\\end{rstpre}\n") + dispA(d.target, result, blockEnd, blockEnd) proc renderContainer(d: PDoc, n: PRstNode, result: var string) = var tmp = "" @@ -1294,13 +1307,13 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = result.add addNodes(lastSon(n)) of rnImage, rnFigure: renderImage(d, n, result) - of rnCodeBlock: renderCodeBlock(d, n, result) + of rnCodeBlock, rnInlineCode: renderCode(d, n, result) of rnContainer: renderContainer(d, n, result) of rnSubstitutionReferences, rnSubstitutionDef: renderAux(d, n, "|$1|", "|$1|", result) of rnDirective: renderAux(d, n, "", "", result) - of rnGeneralRole: + of rnUnknownRole: var tmp0 = "" var tmp1 = "" renderRstToOut(d, n.sons[0], tmp0) diff --git a/nimdoc/rst2html/expected/rst_examples.html b/nimdoc/rst2html/expected/rst_examples.html index 8253289a4..32abc0f80 100644 --- a/nimdoc/rst2html/expected/rst_examples.html +++ b/nimdoc/rst2html/expected/rst_examples.html @@ -213,7 +213,7 @@ stmt = IND{>} stmt ^+ IND{=} DED # list of statements

                                      Let T's be p's return type. NRVO applies for T if sizeof(T) >= N (where N is implementation dependent), in other words, it applies for "big" structures.

                                      Apart from built-in operations like array indexing, memory allocation, etc. the raise statement is the only way to raise an exception.

                                      -

                                      typedesc used as a parameter type also introduces an implicit generic. typedesc has its own set of rules:

                                      +

                                      typedesc used as a parameter type also introduces an implicit generic. typedesc has its own set of rules:

                                      The !=, >, >=, in, notin, isnot operators are in fact templates:

                                      a > b is transformed into b < a.
                                      a in b is transformed into contains(b, a).
                                      notin and isnot have the obvious meanings.

                                      A template where every parameter is untyped is called an immediate template. For historical reasons templates can be explicitly annotated with an immediate pragma and then these templates do not take part in overloading resolution and the parameters' types are ignored by the compiler. Explicit immediate templates are now deprecated.

                                      @@ -297,10 +297,10 @@ stmt = IND{>} stmt ^+ IND{=} DED # list of statements

                                      Introduction

                                      "Der Mensch ist doch ein Augentier -- schöne Dinge wünsch ich mir."

                                      This document is a tutorial for the programming language Nim. This tutorial assumes that you are familiar with basic programming concepts like variables, types, or statements but is kept very basic. The manual contains many more examples of the advanced language features. All code examples in this tutorial, as well as the ones found in the rest of Nim's documentation, follow the Nim style guide.

                                      -

                                      However, this does not work. The problem is that the procedure should not only return, but return and continue after an iteration has finished. This return and continue is called a yield statement. Now the only thing left to do is to replace the proc keyword by iterator and here it is - our first iterator:

                                      +

                                      However, this does not work. The problem is that the procedure should not only return, but return and continue after an iteration has finished. This return and continue is called a yield statement. Now the only thing left to do is to replace the proc keyword by iterator and here it is - our first iterator:

                                      - +
                                      A1 headerA2 | not fooled
                                      C1C2 bold
                                      D1 code \|D2
                                      D1 code \|D2
                                      E1 | text
                                      F2 without pipe

                                      not in table

                                      diff --git a/nimdoc/rst2html/source/rst_examples.rst b/nimdoc/rst2html/source/rst_examples.rst index 54f0124c8..7fa20de6c 100644 --- a/nimdoc/rst2html/source/rst_examples.rst +++ b/nimdoc/rst2html/source/rst_examples.rst @@ -5,6 +5,10 @@ Not a Nim Manual :Authors: Andreas Rumpf, Zahary Karadjov :Version: |nimversion| +.. role:: nim(code) + :language: nim +.. default-role:: nim + .. contents:: diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html index 8525048ea..ba1791d81 100644 --- a/nimdoc/testproject/expected/testproject.html +++ b/nimdoc/testproject/expected/testproject.html @@ -565,14 +565,14 @@ This is deprecated with a message.
                                      func someFunc() {....raises: [], tags: [].}
                                      -My someFunc. Stuff in quotes here. Some link +My someFunc. Stuff in quotes here. Some link
                                      proc fromUtils3() {....raises: [], tags: [].}
                                      -came form utils but should be shown where fromUtilsGen is called +came form utils but should be shown where fromUtilsGen is called

                                      Example:

                                      discard 1
                                      @@ -765,7 +765,7 @@ the c printf. etc.
                                      proc low[T: Ordinal | enum | range](x: T): T {.magic: "Low", noSideEffect.}
                                      -

                                      Returns the lowest possible value of an ordinal value x. As a special semantic rule, x may also be a type identifier.

                                      +

                                      Returns the lowest possible value of an ordinal value x. As a special semantic rule, x may also be a type identifier.

                                      See also:

                                      @@ -776,7 +776,7 @@ the c printf. etc.
                                      proc low2[T: Ordinal | enum | range](x: T): T {.magic: "Low", noSideEffect.}
                                      -

                                      Returns the lowest possible value of an ordinal value x. As a special semantic rule, x may also be a type identifier.

                                      +

                                      Returns the lowest possible value of an ordinal value x. As a special semantic rule, x may also be a type identifier.

                                      See also:

                                      diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim index da01c30d2..ad0c27f05 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -10,7 +10,7 @@ import unittest, strutils, strtabs import std/private/miscdollars proc toHtml(input: string, - rstOptions: RstParseOptions = {roSupportMarkdown}, + rstOptions: RstParseOptions = {roSupportMarkdown, roNimFile}, error: ref string = nil, warnings: ref seq[string] = nil): string = ## If `error` is nil then no errors should be generated. @@ -36,6 +36,11 @@ proc toHtml(input: string, except EParseError: discard +# inline code tags (for parsing originated from highlite.nim) +proc id(str: string): string = """""" & str & "" +proc op(str: string): string = """""" & str & "" +proc pu(str: string): string = """""" & str & "" + suite "YAML syntax highlighting": test "Basics": let input = """.. code-block:: yaml @@ -201,14 +206,14 @@ not in table""" `|` outside a table cell should render as `\|` consistently with markdown, see https://stackoverflow.com/a/66557930/1426932 ]# - doAssert output1 == """ + check(output1 == """ - +
                                      A1 headerA2 | not fooled
                                      C1C2 bold
                                      D1 code \|D2
                                      D1 """ & id"code" & " " & op"\|" & """D2
                                      E1 | text
                                      F2 without pipe

                                      not in table

                                      -""" +""") let input2 = """ | A1 header | A2 | | --- | --- |""" @@ -556,19 +561,66 @@ let x = 1 doAssert "foo.bar
                                      """ - check """`foo\`\`bar`""".toHtml == """foo``bar""" - check """`foo\`bar`""".toHtml == """foo`bar""" - check """`\`bar`""".toHtml == """`bar""" - check """`a\b\x\\ar`""".toHtml == """a\b\x\\ar""" + check("""`foo.bar`""".toHtml == + """""" & + id"foo" & op"." & id"bar" & "") + check("""`foo\`\`bar`""".toHtml == + """""" & + id"foo" & pu"`" & pu"`" & id"bar" & "") + check("""`foo\`bar`""".toHtml == + """""" & + id"foo" & pu"`" & id"bar" & "") + check("""`\`bar`""".toHtml == + """""" & + pu"`" & id"bar" & "") + check("""`a\b\x\\ar`""".toHtml == + """""" & + id"a" & op"""\""" & id"b" & op"""\""" & id"x" & op"""\\""" & id"ar" & + "") test "inline literal": check """``foo.bar``""".toHtml == """foo.bar""" check """``foo\bar``""".toHtml == """foo\bar""" check """``f\`o\\o\b`ar``""".toHtml == """f\`o\\o\b`ar""" + test "default-role": + # nim(default) -> literal -> nim -> code(=literal) + let input = dedent""" + Par1 `value1`. + + .. default-role:: literal + + Par2 `value2`. + + .. default-role:: nim + + Par3 `value3`. + + .. default-role:: code + + Par4 `value4`.""" + let p1 = """Par1 """ & id"value1" & "." + let p2 = """

                                      Par2 value2.

                                      """ + let p3 = """

                                      Par3 """ & id"value3" & ".

                                      " + let p4 = """

                                      Par4 value4.

                                      """ + let expected = p1 & p2 & "\n" & p3 & "\n" & p4 & "\n" + check(input.toHtml == expected) + + test "role directive": + let input = dedent""" + .. role:: y(code) + :language: yaml + + .. role:: brainhelp(code) + :language: brainhelp + """ + var warnings = new seq[string] + let output = input.toHtml(warnings=warnings) + check(warnings[].len == 1 and "language 'brainhelp' not supported" in warnings[0]) + test "RST comments": let input1 = """ + Check that comment disappears: .. @@ -1341,7 +1393,8 @@ Test1 test "(not) Roles: check escaping 1": let expected = """See :subscript:""" & - """some text.""" + """""" & id"some" & " " & id"text" & + "." check """See \:subscript:`some text`.""".toHtml == expected check """See :subscript\:`some text`.""".toHtml == expected From a807233aebcd3759bc3e21b450ed89e1eb6ddace Mon Sep 17 00:00:00 2001 From: flywind Date: Sat, 3 Apr 2021 09:26:30 +0800 Subject: [PATCH 1867/3768] fix #17615(runnableExamples silently ignored if placed after some code) (#17619) Co-authored-by: Timothee Cour --- compiler/docgen.nim | 33 +++++++++++--------- lib/experimental/diff.nim | 8 ++--- lib/pure/coro.nim | 2 +- lib/std/jsbigints.nim | 4 +-- lib/wrappers/openssl.nim | 3 +- nimdoc/testproject/expected/testproject.html | 4 ++- nimdoc/testproject/testproject.nim | 4 +-- tests/nimdoc/t17615.nim | 11 +++++++ 8 files changed, 41 insertions(+), 28 deletions(-) create mode 100644 tests/nimdoc/t17615.nim diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 37d8955f6..2f7415241 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -608,20 +608,23 @@ proc getAllRunnableExamplesImpl(d: PDoc; n: PNode, dest: var Rope, state: Runnab return rsComment of nkCallKinds: if isRunnableExamples(n[0]) and - n.len >= 2 and n.lastSon.kind == nkStmtList and state in {rsStart, rsComment, rsRunnable}: - let (rdoccmd, code) = prepareExample(d, n, topLevel) - var msg = "Example:" - if rdoccmd.len > 0: msg.add " cmd: " & rdoccmd - dispA(d.conf, dest, "\n

                                      $1

                                      \n", - "\n\\textbf{$1}\n", [msg.rope]) - inc d.listingCounter - let id = $d.listingCounter - dest.add(d.config.getOrDefault"doc.listing_start" % [id, "langNim", ""]) - var dest2 = "" - renderNimCode(dest2, code, isLatex = d.conf.cmd == cmdRst2tex) - dest.add dest2 - dest.add(d.config.getOrDefault"doc.listing_end" % id) - return rsRunnable + n.len >= 2 and n.lastSon.kind == nkStmtList: + if state in {rsStart, rsComment, rsRunnable}: + let (rdoccmd, code) = prepareExample(d, n, topLevel) + var msg = "Example:" + if rdoccmd.len > 0: msg.add " cmd: " & rdoccmd + dispA(d.conf, dest, "\n

                                      $1

                                      \n", + "\n\\textbf{$1}\n", [msg.rope]) + inc d.listingCounter + let id = $d.listingCounter + dest.add(d.config.getOrDefault"doc.listing_start" % [id, "langNim", ""]) + var dest2 = "" + renderNimCode(dest2, code, isLatex = d.conf.cmd == cmdRst2tex) + dest.add dest2 + dest.add(d.config.getOrDefault"doc.listing_end" % id) + return rsRunnable + else: + localError(d.conf, n.info, errUser, "runnableExamples must appear before the first non-comment statement") else: discard return rsDone # change this to `rsStart` if you want to keep generating doc comments @@ -671,7 +674,7 @@ proc getAllRunnableExamples(d: PDoc, n: PNode, dest: var Rope) = else: for i in 0..Example:

                                      discard 4
                                      ok5 ok5b

                                      Example:

                                      -
                                      assert true
                                      in or out? +
                                      assert true
                                      +

                                      Example:

                                      +
                                      discard 1
                                      in or out?
                                      diff --git a/nimdoc/testproject/testproject.nim b/nimdoc/testproject/testproject.nim index 69edb0d23..6b82acb9b 100644 --- a/nimdoc/testproject/testproject.nim +++ b/nimdoc/testproject/testproject.nim @@ -330,13 +330,11 @@ when true: # (most) templates ## ok5 ## ok5b runnableExamples: assert true + runnableExamples: discard 1 ## in or out? - # this is an edge case; a newline separate last runnableExamples from - # next doc comment but AST isnt' aware of it; this could change in future discard 8 ## out - runnableExamples: discard 1 when true: # issue #14473 import std/[sequtils] diff --git a/tests/nimdoc/t17615.nim b/tests/nimdoc/t17615.nim new file mode 100644 index 000000000..77ae35a15 --- /dev/null +++ b/tests/nimdoc/t17615.nim @@ -0,0 +1,11 @@ +discard """ + cmd: "nim doc -r $file" + errormsg: "runnableExamples must appear before the first non-comment statement" + line: 10 +""" + +func fn*() = + ## foo + discard + runnableExamples: + assert true From 270964c487e5347c61dade25bec903580483dda5 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 2 Apr 2021 22:15:21 -0700 Subject: [PATCH 1868/3768] implement RFCs/294 ; disallow enum <=> enum conversion (#16351) * fix https://github.com/nim-lang/RFCs/issues/294 ; disallow enum <=> enum conversion * fix the runnableExamples that was the instigator of this RFC * legacy -d:nimLegacyConvEnumEnum * use -d:nimLegacyConvEnumEnum in important_package nimgame2 * add test for enum cast * improve changelog * add changelog: Changes affecting backward compatibility * cleanup changelog * fix changelog --- changelog.md | 58 +++++++++++++++++++------------- changelogs/changelog_X_XX_X.md | 4 +++ compiler/semexprs.nim | 5 +++ compiler/sempass2.nim | 6 ++-- compiler/vmgen.nim | 4 +-- lib/system/comparisons.nim | 2 +- testament/important_packages.nim | 5 +-- tests/misc/tcast.nim | 36 +++++++++++++++++--- tests/misc/tconv.nim | 33 +++++++++++++++++- 9 files changed, 115 insertions(+), 38 deletions(-) diff --git a/changelog.md b/changelog.md index 020d8a350..43a15e398 100644 --- a/changelog.md +++ b/changelog.md @@ -2,6 +2,40 @@ +## Changes affecting backward compatibility + +- `repr` now doesn't insert trailing newline; previous behavior was very inconsistent, + see #16034. Use `-d:nimLegacyReprWithNewline` for previous behavior. + +- An enum now can't be converted to another enum directly, you must use `ord` (or `cast`, but + compiler won't help if you misuse it). + ``` + type A = enum a1, a2 + type B = enum b1, b2 + doAssert not compiles(a1.B) + doAssert compiles(a1.ord.B) + ``` + for a transition period, use `-d:nimLegacyConvEnumEnum`. + +- Type mismatch errors now show more context, use `-d:nimLegacyTypeMismatch` for previous + behavior. + +- `echo` and `debugEcho` will now raise `IOError` if writing to stdout fails. Previous behavior + silently ignored errors. See #16366. Use `-d:nimLegacyEchoNoRaise` for previous behavior. + +- `math.round` now is rounded "away from zero" in JS backend which is consistent + with other backends. See #9125. Use `-d:nimLegacyJsRound` for previous behavior. + +- Changed the behavior of `uri.decodeQuery` when there are unencoded `=` + characters in the decoded values. Prior versions would raise an error. This is + no longer the case to comply with the HTML spec and other languages + implementations. Old behavior can be obtained with + `-d:nimLegacyParseQueryStrict`. `cgi.decodeData` which uses the same + underlying code is also updated the same way. + +- In `std/os`, `getHomeDir`, `expandTilde`, `getTempDir`, `getConfigDir` now do not include trailing `DirSep`, + unless `-d:nimLegacyHomeDir` is specified (for a transition period). + ## Standard library additions and changes - Added `sections` iterator in `parsecfg`. @@ -84,9 +118,6 @@ - Added a simpler to use `io.readChars` overload. -- `repr` now doesn't insert trailing newline; previous behavior was very inconsistent, - see #16034. Use `-d:nimLegacyReprWithNewline` for previous behavior. - - Added `**` to jsffi. - `writeStackTrace` is available in JS backend now. @@ -109,9 +140,6 @@ - Added `math.isNaN`. -- `echo` and `debugEcho` will now raise `IOError` if writing to stdout fails. Previous behavior - silently ignored errors. See #16366. Use `-d:nimLegacyEchoNoRaise` for previous behavior. - - Added `jsbigints` module, arbitrary precision integers for JavaScript target. - Added `math.copySign`. @@ -130,18 +158,8 @@ - Added `posix_utils.osReleaseFile` to get system identification from `os-release` file on Linux and the BSDs. https://www.freedesktop.org/software/systemd/man/os-release.html -- `math.round` now is rounded "away from zero" in JS backend which is consistent - with other backends. See #9125. Use `-d:nimLegacyJsRound` for previous behavior. - - Added `socketstream` module that wraps sockets in the stream interface -- Changed the behavior of `uri.decodeQuery` when there are unencoded `=` - characters in the decoded values. Prior versions would raise an error. This is - no longer the case to comply with the HTML spec and other languages - implementations. Old behavior can be obtained with - `-d:nimLegacyParseQueryStrict`. `cgi.decodeData` which uses the same - underlying code is also updated the same way. - - Added `sugar.dumpToString` which improves on `sugar.dump`. - Added `math.signbit`. @@ -235,7 +253,6 @@ - Added `jscore.debugger` to [call any available debugging functionality, such as breakpoints.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/debugger). - - Added `std/channels`. - Added `htmlgen.portal` for [making "SPA style" pages using HTML only](https://web.dev/hands-on-portals). @@ -243,9 +260,6 @@ - Added `ZZZ` and `ZZZZ` patterns to `times.nim` `DateTime` parsing, to match time zone offsets without colons, e.g. `UTC+7 -> +0700`. -- In `std/os`, `getHomeDir`, `expandTilde`, `getTempDir`, `getConfigDir` now do not include trailing `DirSep`, - unless `-d:nimLegacyHomeDir` is specified (for a transition period). - - Added `jsconsole.dir`, `jsconsole.dirxml`, `jsconsole.timeStamp`. - Added dollar `$` and `len` for `jsre.RegExp`. @@ -254,7 +268,6 @@ - Added `hasClosure` to `std/typetraits`. - - Added `genasts.genAst` that avoids the problems inherent with `quote do` and can be used as a replacement. @@ -304,9 +317,6 @@ - VM now supports `addr(mystring[ind])` (index + index assignment) -- Type mismatch errors now show more context, use `-d:nimLegacyTypeMismatch` for previous - behavior. - - Added `--hintAsError` with similar semantics as `--warningAsError`. - TLS: OSX now uses native TLS (`--tlsEmulation:off`), TLS now works with importcpp non-POD types, diff --git a/changelogs/changelog_X_XX_X.md b/changelogs/changelog_X_XX_X.md index 48520fd44..77b421f33 100644 --- a/changelogs/changelog_X_XX_X.md +++ b/changelogs/changelog_X_XX_X.md @@ -3,10 +3,14 @@ This is an example file. The changes should go to changelog.md! +## Changes affecting backward compatibility + +- `foo` now behaves differently, use `-d:nimLegacyFoo` for previous behavior. ## Standard library additions and changes - Added `example.exampleProc`. + - Changed `example.foo` to take additional `bar` parameter. diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 27b78aa6f..71c9e0295 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -163,6 +163,11 @@ proc checkConvertible(c: PContext, targetTyp: PType, src: PNode): TConvStatus = result = checkConversionBetweenObjects(d.skipTypes(abstractInst), s.skipTypes(abstractInst), pointers) elif (targetBaseTyp.kind in IntegralTypes) and (srcBaseTyp.kind in IntegralTypes): + if targetTyp.kind == tyEnum and srcBaseTyp.kind == tyEnum: + if c.config.isDefined("nimLegacyConvEnumEnum"): + message(c.config, src.info, warnUser, "enum to enum conversion is now deprecated") + else: result = convNotLegal + # `elif` would be incorrect here if targetTyp.kind == tyBool: discard "convOk" elif targetTyp.isOrdinalType: diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 87d196fad..52998c214 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -137,7 +137,7 @@ proc guardGlobal(a: PEffects; n: PNode; guard: PSym) = # we allow accesses nevertheless in top level statements for # easier initialization: #if a.isTopLevel: - # message(n.info, warnUnguardedAccess, renderTree(n)) + # message(a.config, n.info, warnUnguardedAccess, renderTree(n)) #else: if not a.isTopLevel: localError(a.config, n.info, "unguarded access: " & renderTree(n)) @@ -478,7 +478,7 @@ proc getLockLevel(s: PSym): TLockLevel = result = 0.TLockLevel else: result = UnknownLockLevel - #message(s.info, warnUser, "FOR THIS " & s.name.s) + #message(??.config, s.info, warnUser, "FOR THIS " & s.name.s) proc mergeLockLevels(tracked: PEffects, n: PNode, lockLevel: TLockLevel) = if lockLevel >= tracked.currLockLevel: @@ -544,7 +544,7 @@ proc assumeTheWorst(tracked: PEffects; n: PNode; op: PType) = let lockLevel = if op.lockLevel == UnspecifiedLockLevel: UnknownLockLevel else: op.lockLevel #if lockLevel == UnknownLockLevel: - # message(n.info, warnUser, "had to assume the worst here") + # message(??.config, n.info, warnUser, "had to assume the worst here") mergeLockLevels(tracked, n, lockLevel) proc isOwnedProcVar(n: PNode; owner: PSym): bool = diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index be26f98b3..69d7ec4b3 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1435,7 +1435,7 @@ proc genAddr(c: PCtx, n: PNode, dest: var TDest, flags: TGenFlags) = # permanent, so that it's not used for anything else: c.prc.slots[tmp].kind = slotTempPerm # XXX this is still a hack - #message(n.info, warnUser, "suspicious opcode used") + #message(c.congig, n.info, warnUser, "suspicious opcode used") else: gABC(c, n, opcAddrReg, dest, tmp) c.freeTemp(tmp) @@ -1687,7 +1687,7 @@ proc genArrAccessOpcode(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode; c.gABC(n, opcNodeToReg, dest, cc) c.freeTemp(cc) else: - #message(n.info, warnUser, "argh") + #message(c.config, n.info, warnUser, "argh") #echo "FLAGS ", flags, " ", fitsRegister(n.typ), " ", typeToString(n.typ) c.gABC(n, opc, dest, a, b) c.freeTemp(a) diff --git a/lib/system/comparisons.nim b/lib/system/comparisons.nim index 2e5664a95..7122daa20 100644 --- a/lib/system/comparisons.nim +++ b/lib/system/comparisons.nim @@ -9,7 +9,7 @@ proc `==`*[Enum: enum](x, y: Enum): bool {.magic: "EqEnum", noSideEffect.} = place1, place2 = 3 var e1 = field1 - e2 = Enum1(place2) + e2 = place2.ord.Enum1 assert e1 == e2 assert not compiles(e1 == place2) # raises error proc `==`*(x, y: pointer): bool {.magic: "EqRef", noSideEffect.} = diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 8a8d6ffd0..8f096b71e 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -96,14 +96,15 @@ pkg "nimcrypto", "nim r --path:. tests/testall.nim" # `--path:.` workaround need pkg "NimData", "nim c -o:nimdataa src/nimdata.nim" pkg "nimes", "nim c src/nimes.nim" pkg "nimfp", "nim c -o:nfp -r src/fp.nim" -pkg "nimgame2", "nim c nimgame2/nimgame.nim", allowFailure = true # XXX Doesn't work with deprecated 'randomize', will create a PR. +pkg "nimgame2", "nim c -d:nimLegacyConvEnumEnum nimgame2/nimgame.nim", allowFailure = true + # XXX Doesn't work with deprecated 'randomize', will create a PR. pkg "nimgen", "nim c -o:nimgenn -r src/nimgen/runcfg.nim" pkg "nimlsp" pkg "nimly", "nim c -r tests/test_readme_example.nim" pkg "nimongo", "nimble test_ci", allowFailure = true pkg "nimph", "nimble test", "https://github.com/disruptek/nimph", allowFailure = true pkg "nimpy", "nim c -r tests/nimfrompy.nim" -pkg "nimquery" +pkg "nimquery", allowFailure = true # pending https://github.com/GULPF/nimquery/pull/10 pkg "nimsl" pkg "nimsvg" pkg "nimterop", "nimble minitest" diff --git a/tests/misc/tcast.nim b/tests/misc/tcast.nim index 454801a2d..6d67b1c52 100644 --- a/tests/misc/tcast.nim +++ b/tests/misc/tcast.nim @@ -48,7 +48,7 @@ block: var x: ref int = nil doAssert cast[int](cast[ptr int](x)) == 0 -block: +block: # cast of nil block: static: let a = cast[pointer](nil) @@ -71,7 +71,33 @@ block: static: doAssert cast[RootRef](nil).repr == "nil" - # Issue #15730, not fixed yet - # block: - # static: - # doAssert cast[cstring](nil).repr == "nil" + when false: # xxx bug #15730, not fixed yet + block: + static: + doAssert cast[cstring](nil).repr == "nil" + +template main() = + # xxx move all under here to get tested in VM + block: # cast of enum + type Koo = enum k1, k2 + type Goo = enum g1, g2 + type Boo = enum b1 = -1, b2, b3, b4 + type Coo = enum c1 = -1i8, c2, c3, c4 + when nimvm: + # xxx: Error: VM does not support 'cast' from tyEnum to tyEnum + discard + else: + doAssert cast[Koo](k2) == k2 + doAssert cast[Goo](k2) == g2 + doAssert cast[Goo](k2.ord) == g2 + + doAssert b3.ord == 1 + doAssert cast[Koo](b3) == k2 + doAssert cast[Boo](k2) == b3 + + doAssert c3.ord == 1 + doAssert cast[Koo](c3) == k2 + doAssert cast[Coo](k2) == c3 + +static: main() +main() diff --git a/tests/misc/tconv.nim b/tests/misc/tconv.nim index f7d15b0b5..c93fc57f8 100644 --- a/tests/misc/tconv.nim +++ b/tests/misc/tconv.nim @@ -1,5 +1,13 @@ +discard """ + nimout:''' +tconv.nim(81, 15) Warning: enum to enum conversion is now deprecated [User] +''' +""" + template reject(x) = - static: assert(not compiles(x)) + static: doAssert(not compiles(x)) +template accept(x) = + static: doAssert(compiles(x)) reject: const x = int8(300) @@ -55,3 +63,26 @@ block: # issue 3766 proc r(x: static[R]) = echo x r 3.R + + +block: # https://github.com/nim-lang/RFCs/issues/294 + type Koo = enum k1, k2 + type Goo = enum g1, g2 + + accept: Koo(k2) + accept: k2.Koo + accept: k2.int.Goo + + reject: Goo(k2) + reject: k2.Goo + reject: k2.string + + {.define(nimLegacyConvEnumEnum).} + discard Goo(k2) + accept: Goo(k2) + accept: k2.Goo + reject: k2.string + {.undef(nimLegacyConvEnumEnum).} + + reject: Goo(k2) + reject: k2.Goo From 61c1e35181206b4305b6f57662bd9b2524c0fef8 Mon Sep 17 00:00:00 2001 From: flywind Date: Sat, 3 Apr 2021 14:17:55 +0800 Subject: [PATCH 1869/3768] close #14806 (#17626) * close #14806 * tiny --- tests/tools/compile/config.nims | 1 + tests/tools/compile/readme.md | 1 + tests/tools/compile/tdeps.nim | 5 +++++ tests/tools/compile/tdetect.nim | 5 +++++ 4 files changed, 12 insertions(+) create mode 100644 tests/tools/compile/config.nims create mode 100644 tests/tools/compile/readme.md create mode 100644 tests/tools/compile/tdeps.nim create mode 100644 tests/tools/compile/tdetect.nim diff --git a/tests/tools/compile/config.nims b/tests/tools/compile/config.nims new file mode 100644 index 000000000..a19545668 --- /dev/null +++ b/tests/tools/compile/config.nims @@ -0,0 +1 @@ +switch("path", "$lib/../") \ No newline at end of file diff --git a/tests/tools/compile/readme.md b/tests/tools/compile/readme.md new file mode 100644 index 000000000..cb5058e02 --- /dev/null +++ b/tests/tools/compile/readme.md @@ -0,0 +1 @@ +Test whether the tools compile. \ No newline at end of file diff --git a/tests/tools/compile/tdeps.nim b/tests/tools/compile/tdeps.nim new file mode 100644 index 000000000..971ca1b8e --- /dev/null +++ b/tests/tools/compile/tdeps.nim @@ -0,0 +1,5 @@ +discard """ + action: compile +""" + +include tools/deps diff --git a/tests/tools/compile/tdetect.nim b/tests/tools/compile/tdetect.nim new file mode 100644 index 000000000..253fc0d39 --- /dev/null +++ b/tests/tools/compile/tdetect.nim @@ -0,0 +1,5 @@ +discard """ + action: compile +""" + +include tools/detect/detect From fe7a76f62fa1563a76e1dcce18e3ee98bcd5d36a Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 2 Apr 2021 23:19:17 -0700 Subject: [PATCH 1870/3768] repr: fix rendering of `'big`, `=destroy` etc (#17624) --- compiler/renderer.nim | 22 +++++++++++++++++++--- tests/stdlib/trepr.nim | 18 ++++++++++++++++++ 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 3d260f350..1ca5a41e3 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -51,6 +51,8 @@ type config*: ConfigRef mangler: seq[PSym] +proc renderTree*(n: PNode, renderFlags: TRenderFlags = {}): string + # We render the source code in a two phases: The first # determines how long the subtree will likely be, the second # phase appends to a buffer that will be the output. @@ -1314,9 +1316,23 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = put(g, tkOpr, "[]") of nkAccQuoted: put(g, tkAccent, "`") - if n.len > 0: gsub(g, n[0]) - for i in 1.. 0 and tmp[0] in {'a'..'z', 'A'..'Z'}: + # handle `=destroy`, `'big' + discard + else: + useSpace = true + elif i > 0: useSpace = true + if useSpace: put(g, tkSpaces, Space) gsub(g, n[i]) put(g, tkAccent, "`") of nkIfExpr: diff --git a/tests/stdlib/trepr.nim b/tests/stdlib/trepr.nim index 357854d67..bca9b9f91 100644 --- a/tests/stdlib/trepr.nim +++ b/tests/stdlib/trepr.nim @@ -145,5 +145,23 @@ do: do: 4""" + block: # bug #17292 (bug 4) + let a = deb: + proc `=destroy`() = discard + proc `'foo`(): int = discard + proc `foo bar baz`(): int = discard + let a2 = """ + +proc `=destroy`() = + discard + +proc `'foo`(): int = + discard + +proc `foo bar baz`(): int = + discard +""" + doAssert a2 == a + static: main() main() From 4a11a04fba99e868c371be09216dc93ae2fe3cd6 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 3 Apr 2021 07:05:37 -0700 Subject: [PATCH 1871/3768] fix #14850: `repr` now correctly renders `do` (#17623) * fix #14850: `repr` now correctly renders `do` * add tests * fix test --- compiler/renderer.nim | 20 +++++++++----- tests/errmsgs/twrongcolon.nim | 4 +-- tests/stdlib/trepr.nim | 49 +++++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+), 9 deletions(-) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 1ca5a41e3..9a867b0a1 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -569,11 +569,11 @@ proc initContext(c: var TContext) = c.spacing = 0 c.flags = {} -proc gsub(g: var TSrcGen, n: PNode, c: TContext) -proc gsub(g: var TSrcGen, n: PNode) = +proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) +proc gsub(g: var TSrcGen, n: PNode, fromStmtList = false) = var c: TContext initContext(c) - gsub(g, n, c) + gsub(g, n, c, fromStmtList = fromStmtList) proc hasCom(n: PNode): bool = result = false @@ -681,7 +681,7 @@ proc gstmts(g: var TSrcGen, n: PNode, c: TContext, doIndent=true) = if n[i].kind in {nkStmtList, nkStmtListExpr, nkStmtListType}: gstmts(g, n[i], c, doIndent=false) else: - gsub(g, n[i]) + gsub(g, n[i], fromStmtList = true) gcoms(g) if doIndent: dedent(g) else: @@ -1003,7 +1003,7 @@ proc isCustomLit(n: PNode): bool = (n[1].kind == nkIdent and n[1].ident.s.startsWith('\'')) or (n[1].kind == nkSym and n[1].sym.name.s.startsWith('\'')) -proc gsub(g: var TSrcGen, n: PNode, c: TContext) = +proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = if isNil(n): return var a: TContext @@ -1040,9 +1040,15 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext) = put(g, tkParLe, "(") gcomma(g, n, 1, i - 1 - n.len) put(g, tkParRi, ")") - put(g, tkColon, ":") + if fromStmtList: + put(g, tkColon, ":") + else: + put(g, tkSpaces, Space) + put(g, tkDo, "do") + put(g, tkColon, ":") gsub(g, n, i) - for j in i+1 ..< n.len: + i.inc + for j in i ..< n.len: optNL(g) put(g, tkDo, "do") put(g, tkColon, ":") diff --git a/tests/errmsgs/twrongcolon.nim b/tests/errmsgs/twrongcolon.nim index e59e37660..f4f996c3e 100644 --- a/tests/errmsgs/twrongcolon.nim +++ b/tests/errmsgs/twrongcolon.nim @@ -1,7 +1,7 @@ discard """ -errormsg: "in expression ':" +errormsg: "in expression ' do:" nimout: ''' -Error: in expression ': +Error: in expression ' do: 890': identifier expected, but found '' ''' diff --git a/tests/stdlib/trepr.nim b/tests/stdlib/trepr.nim index bca9b9f91..3dcbe9b69 100644 --- a/tests/stdlib/trepr.nim +++ b/tests/stdlib/trepr.nim @@ -163,5 +163,54 @@ proc `foo bar baz`(): int = """ doAssert a2 == a + block: # bug #14850 + block: + let a = deb: + template bar(): untyped = + foo1: + discard + 4 + foo2(1): + discard + 4 + foo3(1): + discard + 4 + do: 1 + do: 2 + x.add foo4 + x.add: foo5: 3 + x.add foo6 do: 4 + a.add(foo7 do: + echo "baz" + 4) + + doAssert a == """ + +template bar(): untyped = + foo1: + discard + 4 + foo2(1): + discard + 4 + foo3(1): + discard + 4 + do: + 1 + do: + 2 + x.add foo4 + x.add: + foo5: + 3 + x.add foo6 do: + 4 + a.add(foo7 do: + echo "baz" + 4) +""" + static: main() main() From 66890c3ad476edc54358d5ab9758df218c5c4229 Mon Sep 17 00:00:00 2001 From: flywind Date: Sun, 4 Apr 2021 01:31:33 +0800 Subject: [PATCH 1872/3768] enable some packages (#17629) --- testament/important_packages.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 8f096b71e..620ad3a00 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -49,7 +49,7 @@ pkg "c2nim", "nim c testsuite/tester.nim" pkg "cascade" pkg "cello" pkg "chroma" -pkg "chronicles", "nim c -o:chr -r chronicles.nim", allowFailure = true # pending https://github.com/status-im/nim-chronos/issues/169 +pkg "chronicles", "nim c -o:chr -r chronicles.nim" pkg "chronos", "nim c -r -d:release tests/testall", allowFailure = true # pending https://github.com/nim-lang/Nim/issues/17130 pkg "cligen", "nim c --path:. -r cligen.nim" pkg "combparser", "nimble test --gc:orc" @@ -72,7 +72,7 @@ pkg "gnuplot", "nim c gnuplot.nim" # pkg "gram", "nim c -r --gc:arc --define:danger tests/test.nim", "https://github.com/disruptek/gram" # pending https://github.com/nim-lang/Nim/issues/16509 pkg "hts", "nim c -o:htss src/hts.nim" -pkg "httpauth", allowFailure = true +pkg "httpauth" pkg "illwill", "nimble examples" pkg "inim" pkg "itertools", "nim doc src/itertools.nim" @@ -110,7 +110,7 @@ pkg "nimsvg" pkg "nimterop", "nimble minitest" pkg "nimwc", "nim c nimwc.nim" pkg "nimx", "nim c --threads:on test/main.nim", allowFailure = true -pkg "nitter", "nim c src/nitter.nim", "https://github.com/zedeus/nitter", allowFailure = true +pkg "nitter", "nim c src/nitter.nim", "https://github.com/zedeus/nitter" pkg "norm", "nim c -r tests/sqlite/trows.nim" pkg "npeg", "nimble testarc" pkg "numericalnim", "nim c -r tests/test_integrate.nim" From 97764c8dc91081875f57029ae3f72d3a503f5e45 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 3 Apr 2021 15:24:01 -0700 Subject: [PATCH 1873/3768] remove un-needed {.push hint[ConvFromXtoItselfNotNeeded]: off.} in asyncfutures, asyncmacro (#17631) --- lib/pure/asyncfutures.nim | 2 -- lib/pure/asyncmacro.nim | 5 +---- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/lib/pure/asyncfutures.nim b/lib/pure/asyncfutures.nim index c0c5c3f07..3c36c3453 100644 --- a/lib/pure/asyncfutures.nim +++ b/lib/pure/asyncfutures.nim @@ -363,12 +363,10 @@ proc read*[T](future: Future[T] | FutureVar[T]): T = ## this function will fail with a `ValueError` exception. ## ## If the result of the future is an error then that error will be raised. - {.push hint[ConvFromXtoItselfNotNeeded]: off.} when future is Future[T]: let fut = future else: let fut = Future[T](future) - {.pop.} if fut.finished: if fut.error != nil: injectStacktrace(fut) diff --git a/lib/pure/asyncmacro.nim b/lib/pure/asyncmacro.nim index 336d10aad..aaa5d9acd 100644 --- a/lib/pure/asyncmacro.nim +++ b/lib/pure/asyncmacro.nim @@ -35,14 +35,11 @@ template createCb(retFutureSym, iteratorNameSym, if next == nil: if not retFutureSym.finished: - let msg = "Async procedure ($1) yielded `nil`, are you await'ing a " & - "`nil` Future?" + let msg = "Async procedure ($1) yielded `nil`, are you await'ing a `nil` Future?" raise newException(AssertionDefect, msg % strName) else: {.gcsafe.}: - {.push hint[ConvFromXtoItselfNotNeeded]: off.} next.addCallback cast[proc() {.closure, gcsafe.}](identName) - {.pop.} except: futureVarCompletions if retFutureSym.finished: From f02e159b56aaa63713991c0a7f8e7125e91c832e Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Sun, 4 Apr 2021 21:54:26 +0300 Subject: [PATCH 1874/3768] fix rst option list at EOF (follow-up #17442) (#17638) --- lib/packages/docutils/rst.nim | 2 +- tests/stdlib/trstgen.nim | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 66efa3a3c..dfa2f12be 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -2067,7 +2067,7 @@ proc parseOptionList(p: var RstParser): PRstNode = c.add(b) result.add(c) else: - dec p.idx # back to tkIndent + if currentTok(p).kind != tkEof: dec p.idx # back to tkIndent break proc parseDefinitionList(p: var RstParser): PRstNode = diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim index ad0c27f05..57dacdf11 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -1362,6 +1362,7 @@ Test1 output) check("""-doption""" in output) + check "

                                      option

                                      " notin output test "Option list 3 (double /)": let input = dedent """ @@ -1378,6 +1379,7 @@ Test1 output) check("""-doption""" in output) + check "

                                      option

                                      " notin output test "Roles: subscript prefix/postfix": let expected = "See some text." From 70a30317f7b0a5711e248e0653b50d1c057bd16b Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 5 Apr 2021 04:47:28 +0800 Subject: [PATCH 1875/3768] fix #16693: testament spec nimout too lax (#16698) Co-authored-by: Timothee Cour --- testament/lib/stdtest/testutils.nim | 33 +++++++++++++------ testament/tests/shouldfail/tccodecheck.nim | 3 +- testament/tests/shouldfail/tcolumn.nim | 7 ++-- testament/tests/shouldfail/terrormsg.nim | 7 ++-- testament/tests/shouldfail/texitcode1.nim | 3 +- testament/tests/shouldfail/tfile.nim | 5 +-- testament/tests/shouldfail/tline.nim | 7 ++-- testament/tests/shouldfail/tmaxcodesize.nim | 3 +- testament/tests/shouldfail/tnimout.nim | 5 +-- testament/tests/shouldfail/toutput.nim | 7 ++-- testament/tests/shouldfail/toutputsub.nim | 3 +- testament/tests/shouldfail/treject.nim | 3 +- testament/tests/shouldfail/tsortoutput.nim | 11 ++++--- testament/tests/shouldfail/ttimeout.nim | 1 + testament/tests/shouldfail/tvalgrind.nim | 5 +-- tests/compilerfeatures/texpandmacro.nim | 2 +- tests/errmsgs/tgcsafety.nim | 2 +- tests/errmsgs/twrongcolon.nim | 2 +- tests/exprs/tresultwarning.nim | 2 +- tests/init/tuninit1.nim | 2 +- tests/objvariant/tcheckedfield1.nim | 2 +- tests/pragmas/thintprocessing.nim | 18 ++++++++++ tests/pragmas/twarning_off.nim | 2 -- tests/stdlib/tcstring.nim | 2 +- tests/stdlib/ttestutils.nim | 5 +++ .../varres/tprevent_forloopvar_mutations.nim | 2 +- tests/vm/tcompiletimetable.nim | 8 +++-- tests/vm/tmisc_vm.nim | 3 +- 28 files changed, 103 insertions(+), 52 deletions(-) create mode 100644 tests/pragmas/thintprocessing.nim diff --git a/testament/lib/stdtest/testutils.nim b/testament/lib/stdtest/testutils.nim index 36f951272..58d136696 100644 --- a/testament/lib/stdtest/testutils.nim +++ b/testament/lib/stdtest/testutils.nim @@ -1,5 +1,4 @@ import std/private/miscdollars -import std/strutils from std/os import getEnv template flakyAssert*(cond: untyped, msg = "", notifySuccess = true) = @@ -26,15 +25,29 @@ template flakyAssert*(cond: untyped, msg = "", notifySuccess = true) = msg2.add $expr & " " & msg echo msg2 -proc greedyOrderedSubsetLines*(lhs, rhs: string): bool = - ## returns true if each stripped line in `lhs` appears in rhs, using a greedy matching. - let rhs = rhs.strip - var currentPos = 0 - for line in lhs.strip.splitLines: - currentPos = rhs.find(line.strip, currentPos) - if currentPos < 0: - return false - return true +when not defined(js): + import std/strutils + + proc greedyOrderedSubsetLines*(lhs, rhs: string): bool = + ## Returns true if each stripped line in `lhs` appears in rhs, using a greedy matching. + iterator splitLinesClosure(): string {.closure.} = + for line in splitLines(rhs.strip): + yield line + + var rhsIter = splitLinesClosure + var currentLine = strip(rhsIter()) + + for line in lhs.strip.splitLines: + let line = line.strip + if line.len != 0: + while line != currentLine: + currentLine = strip(rhsIter()) + if rhsIter.finished: + return false + + if rhsIter.finished: + return false + return true template enableRemoteNetworking*: bool = ## Allows contolling whether to run some test at a statement-level granularity. diff --git a/testament/tests/shouldfail/tccodecheck.nim b/testament/tests/shouldfail/tccodecheck.nim index a8d216a5b..7b5f0cce6 100644 --- a/testament/tests/shouldfail/tccodecheck.nim +++ b/testament/tests/shouldfail/tccodecheck.nim @@ -1,5 +1,6 @@ discard """ -ccodecheck: "baz" + targets: "c" + ccodecheck: "baz" """ proc foo(): void {.exportc: "bar".}= diff --git a/testament/tests/shouldfail/tcolumn.nim b/testament/tests/shouldfail/tcolumn.nim index 89482e673..b79ec52a4 100644 --- a/testament/tests/shouldfail/tcolumn.nim +++ b/testament/tests/shouldfail/tcolumn.nim @@ -1,7 +1,8 @@ discard """ -errormsg: "undeclared identifier: 'undeclared'" -line: 8 -column: 7 + errormsg: "undeclared identifier: 'undeclared'" + targets: "c" + line: 9 + column: 7 """ # test should fail because the line directive is wrong diff --git a/testament/tests/shouldfail/terrormsg.nim b/testament/tests/shouldfail/terrormsg.nim index dbbdf5021..e69035235 100644 --- a/testament/tests/shouldfail/terrormsg.nim +++ b/testament/tests/shouldfail/terrormsg.nim @@ -1,7 +1,8 @@ discard """ -errormsg: "wrong error message" -line: 8 -column: 6 + errormsg: "wrong error message" + targets: "c" + line: 9 + column: 6 """ # test should fail because the line directive is wrong diff --git a/testament/tests/shouldfail/texitcode1.nim b/testament/tests/shouldfail/texitcode1.nim index 1b38b4f2e..e5e061578 100644 --- a/testament/tests/shouldfail/texitcode1.nim +++ b/testament/tests/shouldfail/texitcode1.nim @@ -1,3 +1,4 @@ discard """ -exitcode: 1 + targets: "c" + exitcode: 1 """ diff --git a/testament/tests/shouldfail/tfile.nim b/testament/tests/shouldfail/tfile.nim index 20d4bd1f3..9463882f9 100644 --- a/testament/tests/shouldfail/tfile.nim +++ b/testament/tests/shouldfail/tfile.nim @@ -1,6 +1,7 @@ discard """ -errormsg: "undeclared identifier: 'undefined'" -file: "notthisfile.nim" + targets: "c" + errormsg: "undeclared identifier: 'undefined'" + file: "notthisfile.nim" """ echo undefined diff --git a/testament/tests/shouldfail/tline.nim b/testament/tests/shouldfail/tline.nim index f7a09875c..7f7e90896 100644 --- a/testament/tests/shouldfail/tline.nim +++ b/testament/tests/shouldfail/tline.nim @@ -1,7 +1,8 @@ discard """ -errormsg: "undeclared identifier: 'undeclared'" -line: 9 -column: 6 + targets: "c" + errormsg: "undeclared identifier: 'undeclared'" + line: 10 + column: 6 """ # test should fail because the line directive is wrong diff --git a/testament/tests/shouldfail/tmaxcodesize.nim b/testament/tests/shouldfail/tmaxcodesize.nim index 9879e4181..9e2bd9cfb 100644 --- a/testament/tests/shouldfail/tmaxcodesize.nim +++ b/testament/tests/shouldfail/tmaxcodesize.nim @@ -1,5 +1,6 @@ discard """ -maxcodesize: 1 + targets: "c" + maxcodesize: 1 """ echo "Hello World" diff --git a/testament/tests/shouldfail/tnimout.nim b/testament/tests/shouldfail/tnimout.nim index c0e332053..832f134b0 100644 --- a/testament/tests/shouldfail/tnimout.nim +++ b/testament/tests/shouldfail/tnimout.nim @@ -1,6 +1,7 @@ discard """ -nimout: "Hello World!" -action: compile + targets: "c" + nimout: "Hello World!" + action: compile """ static: diff --git a/testament/tests/shouldfail/toutput.nim b/testament/tests/shouldfail/toutput.nim index ac0bc7a46..0fa4d7278 100644 --- a/testament/tests/shouldfail/toutput.nim +++ b/testament/tests/shouldfail/toutput.nim @@ -1,7 +1,8 @@ discard """ -output: ''' -done -''' + targets: "c" + output: ''' + done + ''' """ echo "broken" diff --git a/testament/tests/shouldfail/toutputsub.nim b/testament/tests/shouldfail/toutputsub.nim index 7cc51ee8d..b34f3a8f2 100644 --- a/testament/tests/shouldfail/toutputsub.nim +++ b/testament/tests/shouldfail/toutputsub.nim @@ -1,5 +1,6 @@ discard """ -outputsub: "something else" + outputsub: "something else" + targets: "c" """ echo "Hello World!" diff --git a/testament/tests/shouldfail/treject.nim b/testament/tests/shouldfail/treject.nim index aaf2b4a63..395dc4251 100644 --- a/testament/tests/shouldfail/treject.nim +++ b/testament/tests/shouldfail/treject.nim @@ -1,5 +1,6 @@ discard """ -action: "reject" + action: "reject" + targets: "c" """ # Because we set action="reject", we expect this line not to compile. But the diff --git a/testament/tests/shouldfail/tsortoutput.nim b/testament/tests/shouldfail/tsortoutput.nim index 4ce9ce26d..0c165d21b 100644 --- a/testament/tests/shouldfail/tsortoutput.nim +++ b/testament/tests/shouldfail/tsortoutput.nim @@ -1,9 +1,10 @@ discard """ -sortoutput: true -output: ''' -2 -1 -''' + sortoutput: true + targets: "c" + output: ''' + 2 + 1 + ''' """ # this test should ensure that the output is actually sorted diff --git a/testament/tests/shouldfail/ttimeout.nim b/testament/tests/shouldfail/ttimeout.nim index fd3e1a598..8ffd71aaa 100644 --- a/testament/tests/shouldfail/ttimeout.nim +++ b/testament/tests/shouldfail/ttimeout.nim @@ -1,5 +1,6 @@ discard """ timeout: "0.1" + targets: "c" """ import os diff --git a/testament/tests/shouldfail/tvalgrind.nim b/testament/tests/shouldfail/tvalgrind.nim index 4f699fd3b..5502705b3 100644 --- a/testament/tests/shouldfail/tvalgrind.nim +++ b/testament/tests/shouldfail/tvalgrind.nim @@ -1,6 +1,7 @@ discard """ -valgrind: true -cmd: "nim $target --gc:arc -d:useMalloc $options $file" + valgrind: true + targets: "c" + cmd: "nim $target --gc:arc -d:useMalloc $options $file" """ # this is the same check used by testament/specs.nim whether or not valgrind diff --git a/tests/compilerfeatures/texpandmacro.nim b/tests/compilerfeatures/texpandmacro.nim index 76b0263ae..fea8b571f 100644 --- a/tests/compilerfeatures/texpandmacro.nim +++ b/tests/compilerfeatures/texpandmacro.nim @@ -1,6 +1,6 @@ discard """ cmd: "nim c --expandMacro:foo $file" - nimout: '''Hint: expanded macro: + nimout: '''texpandmacro.nim(17, 1) Hint: expanded macro: echo ["injected echo"] var x = 4 [ExpandMacro] ''' diff --git a/tests/errmsgs/tgcsafety.nim b/tests/errmsgs/tgcsafety.nim index 77515b74f..09ef92e75 100644 --- a/tests/errmsgs/tgcsafety.nim +++ b/tests/errmsgs/tgcsafety.nim @@ -2,7 +2,7 @@ discard """ cmd: "nim check $file" errormsg: "type mismatch: got .}>" nimout: ''' -type mismatch: got .}> +tgcsafety.nim(30, 18) Error: type mismatch: got .}> but expected one of: proc serve(server: AsyncHttpServer; port: Port; callback: proc (request: Request): Future[void] {.closure, gcsafe.}; diff --git a/tests/errmsgs/twrongcolon.nim b/tests/errmsgs/twrongcolon.nim index f4f996c3e..20063cbc4 100644 --- a/tests/errmsgs/twrongcolon.nim +++ b/tests/errmsgs/twrongcolon.nim @@ -1,7 +1,7 @@ discard """ errormsg: "in expression ' do:" nimout: ''' -Error: in expression ' do: +twrongcolon.nim(11, 12) Error: in expression ' do: 890': identifier expected, but found '' ''' diff --git a/tests/exprs/tresultwarning.nim b/tests/exprs/tresultwarning.nim index 32934408e..28dabfdb1 100644 --- a/tests/exprs/tresultwarning.nim +++ b/tests/exprs/tresultwarning.nim @@ -1,5 +1,5 @@ discard """ - nimout: "Special variable 'result' is shadowed. [ResultShadowed]" + nimout: "tresultwarning.nim(6, 7) Warning: Special variable 'result' is shadowed. [ResultShadowed]" """ proc test(): string = diff --git a/tests/init/tuninit1.nim b/tests/init/tuninit1.nim index fe91733ff..b281bcf89 100644 --- a/tests/init/tuninit1.nim +++ b/tests/init/tuninit1.nim @@ -1,5 +1,5 @@ discard """ - nimout: "Warning: use explicit initialization of 'y' for clarity [Uninit]" + nimout: "tuninit1.nim(35, 11) Warning: use explicit initialization of 'y' for clarity [Uninit]" line:34 action: compile """ diff --git a/tests/objvariant/tcheckedfield1.nim b/tests/objvariant/tcheckedfield1.nim index 69b099f24..e1a2e60e6 100644 --- a/tests/objvariant/tcheckedfield1.nim +++ b/tests/objvariant/tcheckedfield1.nim @@ -1,5 +1,5 @@ discard """ - nimout: "Warning: cannot prove that field 'x.s' is accessible [ProveField]" + nimout: "tcheckedfield1.nim(40, 6) Warning: cannot prove that field 'x.s' is accessible [ProveField]" line:51 action: run output: "abc abc" diff --git a/tests/pragmas/thintprocessing.nim b/tests/pragmas/thintprocessing.nim new file mode 100644 index 000000000..c608bc6e4 --- /dev/null +++ b/tests/pragmas/thintprocessing.nim @@ -0,0 +1,18 @@ +discard """ + disabled: windows + matrix: "--hint:processing" + nimout: ''' +compile start +.. +warn_module.nim(6, 6) Hint: 'test' is declared but not used [XDeclaredButNotUsed] +compile end +''' +""" + +static: + echo "compile start" + +import warn_module + +static: + echo "compile end" diff --git a/tests/pragmas/twarning_off.nim b/tests/pragmas/twarning_off.nim index bada2999b..ccf07b9c4 100644 --- a/tests/pragmas/twarning_off.nim +++ b/tests/pragmas/twarning_off.nim @@ -1,8 +1,6 @@ discard """ - matrix: "--hint:processing" nimout: ''' compile start -.. warn_module.nim(6, 6) Hint: 'test' is declared but not used [XDeclaredButNotUsed] compile end ''' diff --git a/tests/stdlib/tcstring.nim b/tests/stdlib/tcstring.nim index 98da5d5c4..04a26b53c 100644 --- a/tests/stdlib/tcstring.nim +++ b/tests/stdlib/tcstring.nim @@ -1,6 +1,6 @@ discard """ targets: "c cpp js" - matrix: "; --gc:arc" + matrix: "--gc:refc; --gc:arc" """ from std/sugar import collect diff --git a/tests/stdlib/ttestutils.nim b/tests/stdlib/ttestutils.nim index 1a50d311b..7e39c9ae3 100644 --- a/tests/stdlib/ttestutils.nim +++ b/tests/stdlib/ttestutils.nim @@ -4,3 +4,8 @@ block: # greedyOrderedSubsetLines doAssert greedyOrderedSubsetLines("a1\na3", "a0\na1\na2\na3\na4") doAssert not greedyOrderedSubsetLines("a3\na1", "a0\na1\na2\na3\na4") # out of order doAssert not greedyOrderedSubsetLines("a1\na5", "a0\na1\na2\na3\na4") # a5 not in lhs + + doAssert not greedyOrderedSubsetLines("a1\na5", "a0\na1\na2\na3\na4\nprefix:a5") + doAssert not greedyOrderedSubsetLines("a1\na5", "a0\na1\na2\na3\na4\na5:suffix") + doAssert not greedyOrderedSubsetLines("a5", "a0\na1\na2\na3\na4\nprefix:a5") + doAssert not greedyOrderedSubsetLines("a5", "a0\na1\na2\na3\na4\na5:suffix") diff --git a/tests/varres/tprevent_forloopvar_mutations.nim b/tests/varres/tprevent_forloopvar_mutations.nim index 15f31d8a3..045dc7cbb 100644 --- a/tests/varres/tprevent_forloopvar_mutations.nim +++ b/tests/varres/tprevent_forloopvar_mutations.nim @@ -1,7 +1,7 @@ discard """ errormsg: "type mismatch: got " line: 17 - nimout: '''type mismatch: got + nimout: '''tprevent_forloopvar_mutations.nim(17, 7) Error: type mismatch: got but expected one of: proc inc[T: Ordinal](x: var T; y = 1) first type mismatch at position: 1 diff --git a/tests/vm/tcompiletimetable.nim b/tests/vm/tcompiletimetable.nim index ece2ddfe9..1db490f1a 100644 --- a/tests/vm/tcompiletimetable.nim +++ b/tests/vm/tcompiletimetable.nim @@ -1,12 +1,16 @@ discard """ - nimout: '''2 + nimout: ''' +2 3 4:2 Got Hi Got Hey +''' + output:''' a b -c''' +c +''' """ # bug #404 diff --git a/tests/vm/tmisc_vm.nim b/tests/vm/tmisc_vm.nim index 2d3e30c5e..bbf618622 100644 --- a/tests/vm/tmisc_vm.nim +++ b/tests/vm/tmisc_vm.nim @@ -19,6 +19,7 @@ foo4 (a: 0, b: 0) ''' """ +import std/sets #bug #1009 type @@ -95,8 +96,6 @@ static: simpleTryFinally() # bug #10981 -import sets - proc main = for i in 0..<15: var someSets = @[initHashSet[int]()] From ca405167ed79a6591104de62de6bed077f0698f5 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Mon, 5 Apr 2021 09:33:06 +0300 Subject: [PATCH 1876/3768] fix :number-lines: regression (#17639) --- lib/packages/docutils/rstgen.nim | 2 +- tests/stdlib/trstgen.nim | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 1a16f590e..f0a2604ff 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -980,7 +980,7 @@ proc buildLinesHtmlTable(d: PDoc; params: CodeBlockParams, code: string, result.beginTable.add($line & "\n") line.inc codeLines.dec - result.beginTable.add("" & ( + result.beginTable.add("
                                      " & ( d.config.getOrDefault"doc.listing_start" % [id, sourceLanguageToStr[params.lang], idStr])) result.endTable = (d.config.getOrDefault"doc.listing_end" % id) & diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim index 57dacdf11..99b8d8db5 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -1089,6 +1089,16 @@ Test1 let output0 = input0.toHtml doAssert "

                                      Paragraph1

                                      " in output0 + test "Nim code-block :number-lines:": + let input = dedent """ + .. code-block:: nim + :number-lines: 55 + + x + y + """ + check "
                                      55\n56\n
                                      " in input.toHtml + test "RST admonitions": # check that all admonitions are implemented let input0 = dedent """ From b9c94f22aa6f3b43962f1a1f2a9b27e9cba42069 Mon Sep 17 00:00:00 2001 From: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Date: Mon, 5 Apr 2021 09:42:18 +0200 Subject: [PATCH 1877/3768] Improve the typeinfo module (#17625) Co-authored-by: Timothee Cour --- lib/core/typeinfo.nim | 251 ++++++++++++++++++++++-------------------- 1 file changed, 129 insertions(+), 122 deletions(-) diff --git a/lib/core/typeinfo.nim b/lib/core/typeinfo.nim index 1c21e10bf..196ac8fe1 100644 --- a/lib/core/typeinfo.nim +++ b/lib/core/typeinfo.nim @@ -9,16 +9,29 @@ ## This module implements an interface to Nim's `runtime type information`:idx: ## (`RTTI`:idx:). See the `marshal `_ module for an example of -## what this module allows you to do. +## what this allows you to do. ## -## Note that even though `Any` and its operations hide the nasty low level -## details from its clients, it remains inherently unsafe! Also, Nim's -## runtime type information will evolve and may eventually be deprecated. -## As an alternative approach to programmatically understanding and -## manipulating types, consider using the `macros `_ package to -## work with the types' AST representation at compile time. See, for example, -## the `getTypeImpl proc`_. As an alternative -## approach to storing arbitrary types at runtime, consider using generics. +## ..note:: Even though `Any` and its operations hide the nasty low level +## details from its users, it remains inherently unsafe! Also, Nim's +## runtime type information will evolve and may eventually be deprecated. +## As an alternative approach to programmatically understanding and +## manipulating types, consider using the `macros `_ module to +## work with the types' AST representation at compile time. See for example +## the `getTypeImpl proc `_. As an alternative +## approach to storing arbitrary types at runtime, consider using generics. + +runnableExamples: + var x: Any + + var i = 42 + x = i.toAny + assert x.kind == akInt + assert x.getInt == 42 + + var s = @[1, 2, 3] + x = s.toAny + assert x.kind == akSequence + assert x.len == 3 {.push hints: off.} @@ -28,44 +41,45 @@ include "system/hti.nim" {.pop.} type - AnyKind* = enum ## what kind of `any` it is - akNone = 0, ## invalid any - akBool = 1, ## any represents a `bool` - akChar = 2, ## any represents a `char` - akEnum = 14, ## any represents an enum - akArray = 16, ## any represents an array - akObject = 17, ## any represents an object - akTuple = 18, ## any represents a tuple - akSet = 19, ## any represents a set - akRange = 20, ## any represents a range - akPtr = 21, ## any represents a ptr - akRef = 22, ## any represents a ref - akSequence = 24, ## any represents a sequence - akProc = 25, ## any represents a proc - akPointer = 26, ## any represents a pointer - akString = 28, ## any represents a string - akCString = 29, ## any represents a cstring - akInt = 31, ## any represents an int - akInt8 = 32, ## any represents an int8 - akInt16 = 33, ## any represents an int16 - akInt32 = 34, ## any represents an int32 - akInt64 = 35, ## any represents an int64 - akFloat = 36, ## any represents a float - akFloat32 = 37, ## any represents a float32 - akFloat64 = 38, ## any represents a float64 - akFloat128 = 39, ## any represents a float128 - akUInt = 40, ## any represents an unsigned int - akUInt8 = 41, ## any represents an unsigned int8 - akUInt16 = 42, ## any represents an unsigned in16 - akUInt32 = 43, ## any represents an unsigned int32 - akUInt64 = 44, ## any represents an unsigned int64 + AnyKind* = enum ## The kind of `Any`. + akNone = 0, ## invalid + akBool = 1, ## bool + akChar = 2, ## char + akEnum = 14, ## enum + akArray = 16, ## array + akObject = 17, ## object + akTuple = 18, ## tuple + akSet = 19, ## set + akRange = 20, ## range + akPtr = 21, ## ptr + akRef = 22, ## ref + akSequence = 24, ## sequence + akProc = 25, ## proc + akPointer = 26, ## pointer + akString = 28, ## string + akCString = 29, ## cstring + akInt = 31, ## int + akInt8 = 32, ## int8 + akInt16 = 33, ## int16 + akInt32 = 34, ## int32 + akInt64 = 35, ## int64 + akFloat = 36, ## float + akFloat32 = 37, ## float32 + akFloat64 = 38, ## float64 + akFloat128 = 39, ## float128 + akUInt = 40, ## uint + akUInt8 = 41, ## uint8 + akUInt16 = 42, ## uin16 + akUInt32 = 43, ## uint32 + akUInt64 = 44, ## uint64 # akOpt = 44+18 ## the builtin 'opt' type. - Any* = object ## can represent any nim value; NOTE: the wrapped - ## value can be modified with its wrapper! This means - ## that `Any` keeps a non-traced pointer to its - ## wrapped value and **must not** live longer than - ## its wrapped value. + Any* = object + ## A type that can represent any nim value. + ## + ## .. danger:: The wrapped value can be modified with its wrapper! This means + ## that `Any` keeps a non-traced pointer to its wrapped value and + ## **must not** live longer than its wrapped value. value: pointer when defined(js): rawType: PNimType @@ -84,9 +98,9 @@ when not defined(gcDestructors): PGenSeq = ptr TGenericSeq when defined(gogc): - const GenericSeqSize = (3 * sizeof(int)) + const GenericSeqSize = 3 * sizeof(int) else: - const GenericSeqSize = (2 * sizeof(int)) + const GenericSeqSize = 2 * sizeof(int) else: include system/seqs_v2_reimpl @@ -103,8 +117,7 @@ when not defined(js): proc genericAssign(dest, src: pointer, mt: PNimType) {.importCompilerProc.} when not defined(gcDestructors): - proc genericShallowAssign(dest, src: pointer, mt: PNimType) {. - importCompilerProc.} + proc genericShallowAssign(dest, src: pointer, mt: PNimType) {.importCompilerProc.} proc incrSeq(seq: PGenSeq, elemSize, elemAlign: int): PGenSeq {.importCompilerProc.} proc newObj(typ: PNimType, size: int): pointer {.importCompilerProc.} proc newSeq(typ: PNimType, len: int): pointer {.importCompilerProc.} @@ -120,7 +133,7 @@ template `+!!`(a, b): untyped = cast[pointer](cast[ByteAddress](a) + b) proc getDiscriminant(aa: pointer, n: ptr TNimNode): int = assert(n.kind == nkCase) var d: int - var a = cast[ByteAddress](aa) + let a = cast[ByteAddress](aa) case n.typ.size of 1: d = ze(cast[ptr int8](a +% n.offset)[]) of 2: d = ze(cast[ptr int16](a +% n.offset)[]) @@ -130,7 +143,7 @@ proc getDiscriminant(aa: pointer, n: ptr TNimNode): int = return d proc selectBranch(aa: pointer, n: ptr TNimNode): ptr TNimNode = - var discr = getDiscriminant(aa, n) + let discr = getDiscriminant(aa, n) if discr <% n.len: result = n.sons[discr] if result == nil: result = n.sons[n.len] @@ -144,17 +157,17 @@ proc newAny(value: pointer, rawType: PNimType): Any {.inline.} = when declared(system.VarSlot): proc toAny*(x: VarSlot): Any {.inline.} = - ## Constructs a `Any` object from a variable slot `x`. + ## Constructs an `Any` object from a variable slot `x`. ## This captures `x`'s address, so `x` can be modified with its - ## `Any` wrapper! The client needs to ensure that the wrapper + ## `Any` wrapper! The caller needs to ensure that the wrapper ## **does not** live longer than `x`! ## This is provided for easier reflection capabilities of a debugger. result.value = x.address result.rawType = x.typ proc toAny*[T](x: var T): Any {.inline.} = - ## Constructs a `Any` object from `x`. This captures `x`'s address, so - ## `x` can be modified with its `Any` wrapper! The client needs to ensure + ## Constructs an `Any` object from `x`. This captures `x`'s address, so + ## `x` can be modified with its `Any` wrapper! The caller needs to ensure ## that the wrapper **does not** live longer than `x`! newAny(addr(x), cast[PNimType](getTypeInfo(x))) @@ -167,12 +180,12 @@ proc size*(x: Any): int {.inline.} = result = x.rawType.size proc baseTypeKind*(x: Any): AnyKind {.inline.} = - ## Gets the base type's kind; `akNone` is returned if `x` has no base type. + ## Gets the base type's kind. If `x` has no base type, `akNone` is returned. if x.rawType.base != nil: result = AnyKind(ord(x.rawType.base.kind)) proc baseTypeSize*(x: Any): int {.inline.} = - ## Returns the size of `x`'s basetype. + ## Returns the size of `x`'s base type. If `x` has no base type, 0 is returned. if x.rawType.base != nil: result = x.rawType.base.size @@ -301,33 +314,32 @@ proc len*(x: Any): int = proc base*(x: Any): Any = - ## Returns base Any (useful for inherited object types). + ## Returns the base type of `x` (useful for inherited object types). result.rawType = x.rawType.base result.value = x.value proc isNil*(x: Any): bool = - ## `isNil` for an any `x` that represents a cstring, proc or + ## `isNil` for an `x` that represents a cstring, proc or ## some pointer type. assert x.rawType.kind in {tyCString, tyRef, tyPtr, tyPointer, tyProc} result = isNil(cast[ppointer](x.value)[]) -const - pointerLike = when defined(gcDestructors): {tyCString, tyRef, tyPtr, tyPointer, tyProc} - else: {tyString, tyCString, tyRef, tyPtr, tyPointer, - tySequence, tyProc} +const pointerLike = + when defined(gcDestructors): {tyCString, tyRef, tyPtr, tyPointer, tyProc} + else: {tyString, tyCString, tyRef, tyPtr, tyPointer, tySequence, tyProc} proc getPointer*(x: Any): pointer = ## Retrieves the pointer value out of `x`. `x` needs to be of kind ## `akString`, `akCString`, `akProc`, `akRef`, `akPtr`, - ## `akPointer`, `akSequence`. + ## `akPointer` or `akSequence`. assert x.rawType.kind in pointerLike result = cast[ppointer](x.value)[] proc setPointer*(x: Any, y: pointer) = ## Sets the pointer value of `x`. `x` needs to be of kind ## `akString`, `akCString`, `akProc`, `akRef`, `akPtr`, - ## `akPointer`, `akSequence`. + ## `akPointer` or `akSequence`. assert x.rawType.kind in pointerLike if y != nil and x.rawType.kind != tyPointer: genericAssign(x.value, y, x.rawType) @@ -349,15 +361,15 @@ proc fieldsAux(p: pointer, n: ptr TNimNode, if m != nil: fieldsAux(p, m, ret) iterator fields*(x: Any): tuple[name: string, any: Any] = - ## Iterates over every active field of the any `x` that represents an object + ## Iterates over every active field of `x`. `x` needs to represent an object ## or a tuple. assert x.rawType.kind in {tyTuple, tyObject} - var p = x.value + let p = x.value var t = x.rawType # XXX BUG: does not work yet, however is questionable anyway when false: if x.rawType.kind == tyObject: t = cast[ptr PNimType](x.value)[] - var ret: seq[tuple[name: cstring, any: Any]] = @[] + var ret: seq[tuple[name: cstring, any: Any]] if t.kind == tyObject: while true: fieldsAux(p, t.node, ret) @@ -368,8 +380,7 @@ iterator fields*(x: Any): tuple[name: string, any: Any] = for name, any in items(ret): yield ($name, any) -proc getFieldNode(p: pointer, n: ptr TNimNode, - name: cstring): ptr TNimNode = +proc getFieldNode(p: pointer, n: ptr TNimNode, name: cstring): ptr TNimNode = case n.kind of nkNone: assert(false) of nkSlot: @@ -383,17 +394,17 @@ proc getFieldNode(p: pointer, n: ptr TNimNode, if cmpNimIdentifier(n.name, name) == 0: result = n else: - var m = selectBranch(p, n) + let m = selectBranch(p, n) if m != nil: result = getFieldNode(p, m, name) proc `[]=`*(x: Any, fieldName: string, value: Any) = - ## Sets a field of `x`; `x` represents an object or a tuple. + ## Sets a field of `x`. `x` needs to represent an object or a tuple. var t = x.rawType # XXX BUG: does not work yet, however is questionable anyway when false: if x.rawType.kind == tyObject: t = cast[ptr PNimType](x.value)[] assert x.rawType.kind in {tyTuple, tyObject} - var n = getFieldNode(x.value, t.node, fieldName) + let n = getFieldNode(x.value, t.node, fieldName) if n != nil: assert n.typ == value.rawType genericAssign(x.value +!! n.offset, value.value, value.rawType) @@ -401,13 +412,13 @@ proc `[]=`*(x: Any, fieldName: string, value: Any) = raise newException(ValueError, "invalid field name: " & fieldName) proc `[]`*(x: Any, fieldName: string): Any = - ## Gets a field of `x`; `x` represents an object or a tuple. + ## Gets a field of `x`. `x` needs to represent an object or a tuple. var t = x.rawType # XXX BUG: does not work yet, however is questionable anyway when false: if x.rawType.kind == tyObject: t = cast[ptr PNimType](x.value)[] assert x.rawType.kind in {tyTuple, tyObject} - var n = getFieldNode(x.value, t.node, fieldName) + let n = getFieldNode(x.value, t.node, fieldName) if n != nil: result.value = x.value +!! n.offset result.rawType = n.typ @@ -417,39 +428,39 @@ proc `[]`*(x: Any, fieldName: string): Any = raise newException(ValueError, "invalid field name: " & fieldName) proc `[]`*(x: Any): Any = - ## Dereference operation for the any `x` that represents a ptr or a ref. + ## Dereference operator for `Any`. `x` needs to represent a ptr or a ref. assert x.rawType.kind in {tyRef, tyPtr} result.value = cast[ppointer](x.value)[] result.rawType = x.rawType.base proc `[]=`*(x, y: Any) = - ## Dereference operation for the any `x` that represents a ptr or a ref. + ## Dereference operator for `Any`. `x` needs to represent a ptr or a ref. assert x.rawType.kind in {tyRef, tyPtr} assert y.rawType == x.rawType.base genericAssign(cast[ppointer](x.value)[], y.value, y.rawType) proc getInt*(x: Any): int = - ## Retrieves the int value out of `x`. `x` needs to represent an int. + ## Retrieves the `int` value out of `x`. `x` needs to represent an `int`. assert skipRange(x.rawType).kind == tyInt result = cast[ptr int](x.value)[] proc getInt8*(x: Any): int8 = - ## Retrieves the int8 value out of `x`. `x` needs to represent an int8. + ## Retrieves the `int8` value out of `x`. `x` needs to represent an `int8`. assert skipRange(x.rawType).kind == tyInt8 result = cast[ptr int8](x.value)[] proc getInt16*(x: Any): int16 = - ## Retrieves the int16 value out of `x`. `x` needs to represent an int16. + ## Retrieves the `int16` value out of `x`. `x` needs to represent an `int16`. assert skipRange(x.rawType).kind == tyInt16 result = cast[ptr int16](x.value)[] proc getInt32*(x: Any): int32 = - ## Retrieves the int32 value out of `x`. `x` needs to represent an int32. + ## Retrieves the `int32` value out of `x`. `x` needs to represent an `int32`. assert skipRange(x.rawType).kind == tyInt32 result = cast[ptr int32](x.value)[] proc getInt64*(x: Any): int64 = - ## Retrieves the int64 value out of `x`. `x` needs to represent an int64. + ## Retrieves the `int64` value out of `x`. `x` needs to represent an `int64`. assert skipRange(x.rawType).kind == tyInt64 result = cast[ptr int64](x.value)[] @@ -457,7 +468,7 @@ proc getBiggestInt*(x: Any): BiggestInt = ## Retrieves the integer value out of `x`. `x` needs to represent ## some integer, a bool, a char, an enum or a small enough bit set. ## The value might be sign-extended to `BiggestInt`. - var t = skipRange(x.rawType) + let t = skipRange(x.rawType) case t.kind of tyInt: result = BiggestInt(cast[ptr int](x.value)[]) of tyInt8: result = BiggestInt(cast[ptr int8](x.value)[]) @@ -482,7 +493,7 @@ proc getBiggestInt*(x: Any): BiggestInt = proc setBiggestInt*(x: Any, y: BiggestInt) = ## Sets the integer value of `x`. `x` needs to represent ## some integer, a bool, a char, an enum or a small enough bit set. - var t = skipRange(x.rawType) + let t = skipRange(x.rawType) case t.kind of tyInt: cast[ptr int](x.value)[] = int(y) of tyInt8: cast[ptr int8](x.value)[] = int8(y) @@ -505,38 +516,34 @@ proc setBiggestInt*(x: Any, y: BiggestInt) = else: assert false proc getUInt*(x: Any): uint = - ## Retrieves the uint value out of `x`, `x` needs to represent an uint. + ## Retrieves the `uint` value out of `x`. `x` needs to represent a `uint`. assert skipRange(x.rawType).kind == tyUInt result = cast[ptr uint](x.value)[] proc getUInt8*(x: Any): uint8 = - ## Retrieves the uint8 value out of `x`, `x` needs to represent an - ## uint8. + ## Retrieves the `uint8` value out of `x`. `x` needs to represent a `uint8`. assert skipRange(x.rawType).kind == tyUInt8 result = cast[ptr uint8](x.value)[] proc getUInt16*(x: Any): uint16 = - ## Retrieves the uint16 value out of `x`, `x` needs to represent an - ## uint16. + ## Retrieves the `uint16` value out of `x`. `x` needs to represent a `uint16`. assert skipRange(x.rawType).kind == tyUInt16 result = cast[ptr uint16](x.value)[] proc getUInt32*(x: Any): uint32 = - ## Retrieves the uint32 value out of `x`, `x` needs to represent an - ## uint32. + ## Retrieves the `uint32` value out of `x`. `x` needs to represent a `uint32`. assert skipRange(x.rawType).kind == tyUInt32 result = cast[ptr uint32](x.value)[] proc getUInt64*(x: Any): uint64 = - ## Retrieves the uint64 value out of `x`, `x` needs to represent an - ## uint64. + ## Retrieves the `uint64` value out of `x`. `x` needs to represent a `uint64`. assert skipRange(x.rawType).kind == tyUInt64 result = cast[ptr uint64](x.value)[] proc getBiggestUint*(x: Any): uint64 = ## Retrieves the unsigned integer value out of `x`. `x` needs to ## represent an unsigned integer. - var t = skipRange(x.rawType) + let t = skipRange(x.rawType) case t.kind of tyUInt: result = uint64(cast[ptr uint](x.value)[]) of tyUInt8: result = uint64(cast[ptr uint8](x.value)[]) @@ -546,9 +553,9 @@ proc getBiggestUint*(x: Any): uint64 = else: assert false proc setBiggestUint*(x: Any; y: uint64) = - ## Sets the unsigned integer value of `c`. `c` needs to represent an + ## Sets the unsigned integer value of `x`. `x` needs to represent an ## unsigned integer. - var t = skipRange(x.rawType) + let t = skipRange(x.rawType) case t.kind: of tyUInt: cast[ptr uint](x.value)[] = uint(y) of tyUInt8: cast[ptr uint8](x.value)[] = uint8(y) @@ -558,14 +565,14 @@ proc setBiggestUint*(x: Any; y: uint64) = else: assert false proc getChar*(x: Any): char = - ## Retrieves the char value out of `x`. `x` needs to represent a char. - var t = skipRange(x.rawType) + ## Retrieves the `char` value out of `x`. `x` needs to represent a `char`. + let t = skipRange(x.rawType) assert t.kind == tyChar result = cast[ptr char](x.value)[] proc getBool*(x: Any): bool = - ## Retrieves the bool value out of `x`. `x` needs to represent a bool. - var t = skipRange(x.rawType) + ## Retrieves the `bool` value out of `x`. `x` needs to represent a `bool`. + let t = skipRange(x.rawType) assert t.kind == tyBool result = cast[ptr bool](x.value)[] @@ -579,10 +586,10 @@ proc getEnumOrdinal*(x: Any, name: string): int = ## Gets the enum field ordinal from `name`. `x` needs to represent an enum ## but is only used to access the type information. In case of an error ## `low(int)` is returned. - var typ = skipRange(x.rawType) + let typ = skipRange(x.rawType) assert typ.kind == tyEnum - var n = typ.node - var s = n.sons + let n = typ.node + let s = n.sons for i in 0 .. n.len-1: if cmpNimIdentifier($s[i].name, name) == 0: if ntfEnumHole notin typ.flags: @@ -595,16 +602,16 @@ proc getEnumField*(x: Any, ordinalValue: int): string = ## Gets the enum field name as a string. `x` needs to represent an enum ## but is only used to access the type information. The field name of ## `ordinalValue` is returned. - var typ = skipRange(x.rawType) + let typ = skipRange(x.rawType) assert typ.kind == tyEnum - var e = ordinalValue + let e = ordinalValue if ntfEnumHole notin typ.flags: if e <% typ.node.len: return $typ.node.sons[e].name else: # ugh we need a slow linear search: - var n = typ.node - var s = n.sons + let n = typ.node + let s = n.sons for i in 0 .. n.len-1: if s[i].offset == e: return $s[i].name result = $e @@ -614,17 +621,17 @@ proc getEnumField*(x: Any): string = result = getEnumField(x, getBiggestInt(x).int) proc getFloat*(x: Any): float = - ## Retrieves the float value out of `x`. `x` needs to represent an float. + ## Retrieves the `float` value out of `x`. `x` needs to represent a `float`. assert skipRange(x.rawType).kind == tyFloat result = cast[ptr float](x.value)[] proc getFloat32*(x: Any): float32 = - ## Retrieves the float32 value out of `x`. `x` needs to represent an float32. + ## Retrieves the `float32` value out of `x`. `x` needs to represent a `float32`. assert skipRange(x.rawType).kind == tyFloat32 result = cast[ptr float32](x.value)[] proc getFloat64*(x: Any): float64 = - ## Retrieves the float64 value out of `x`. `x` needs to represent an float64. + ## Retrieves the `float64` value out of `x`. `x` needs to represent a `float64`. assert skipRange(x.rawType).kind == tyFloat64 result = cast[ptr float64](x.value)[] @@ -647,7 +654,7 @@ proc setBiggestFloat*(x: Any, y: BiggestFloat) = else: assert false proc getString*(x: Any): string = - ## Retrieves the string value out of `x`. `x` needs to represent a string. + ## Retrieves the `string` value out of `x`. `x` needs to represent a `string`. assert x.rawType.kind == tyString when defined(gcDestructors): result = cast[ptr string](x.value)[] @@ -656,12 +663,12 @@ proc getString*(x: Any): string = result = cast[ptr string](x.value)[] proc setString*(x: Any, y: string) = - ## Sets the string value of `x`. `x` needs to represent a string. + ## Sets the `string` value of `x`. `x` needs to represent a `string`. assert x.rawType.kind == tyString cast[ptr string](x.value)[] = y # also correct for gcDestructors proc getCString*(x: Any): cstring = - ## Retrieves the cstring value out of `x`. `x` needs to represent a cstring. + ## Retrieves the `cstring` value out of `x`. `x` needs to represent a `cstring`. assert x.rawType.kind == tyCString result = cast[ptr cstring](x.value)[] @@ -672,10 +679,10 @@ proc assign*(x, y: Any) = genericAssign(x.value, y.value, y.rawType) iterator elements*(x: Any): int = - ## Iterates over every element of `x` that represents a Nim bitset. + ## Iterates over every element of `x`. `x` needs to represent a `set`. assert x.rawType.kind == tySet - var typ = x.rawType - var p = x.value + let typ = x.rawType + let p = x.value # "typ.slots.len" field is for sets the "first" field var u: int64 case typ.size @@ -684,22 +691,22 @@ iterator elements*(x: Any): int = of 4: u = ze64(cast[ptr int32](p)[]) of 8: u = cast[ptr int64](p)[] else: - var a = cast[pbyteArray](p) + let a = cast[pbyteArray](p) for i in 0 .. typ.size*8-1: if (ze(a[i div 8]) and (1 shl (i mod 8))) != 0: - yield i+typ.node.len + yield i + typ.node.len if typ.size <= 8: for i in 0..sizeof(int64)*8-1: if (u and (1'i64 shl int64(i))) != 0'i64: - yield i+typ.node.len + yield i + typ.node.len proc inclSetElement*(x: Any, elem: int) = ## Includes an element `elem` in `x`. `x` needs to represent a Nim bitset. assert x.rawType.kind == tySet - var typ = x.rawType - var p = x.value + let typ = x.rawType + let p = x.value # "typ.slots.len" field is for sets the "first" field - var e = elem - typ.node.len + let e = elem - typ.node.len case typ.size of 1: var a = cast[ptr int8](p) From 526157917598bc603f5e2296f86946b1ba7f2e65 Mon Sep 17 00:00:00 2001 From: Saem Ghani Date: Mon, 5 Apr 2021 02:37:28 -0700 Subject: [PATCH 1878/3768] [nim-gdb] Fixed enums and flag output [ci skip] (#17634) Debugger works for enums again. Additionally, flags work better than before. Reworked object printer as well, but the approach needs much more work or has to be replaced all together. This is mostly to save the work and myself or someone else can revisit it. --- .../untestable/gdb/gdb_pretty_printer_test.py | 5 +- .../gdb/gdb_pretty_printer_test_program.nim | 32 +++ tools/nim-gdb.py | 238 +++++++++++++----- 3 files changed, 204 insertions(+), 71 deletions(-) diff --git a/tests/untestable/gdb/gdb_pretty_printer_test.py b/tests/untestable/gdb/gdb_pretty_printer_test.py index 5b34bcb3d..8f0f88e85 100644 --- a/tests/untestable/gdb/gdb_pretty_printer_test.py +++ b/tests/untestable/gdb/gdb_pretty_printer_test.py @@ -25,6 +25,7 @@ outputs = [ 'seq(3, 3) = {"one", "two", "three"}', 'Table(3, 64) = {[4] = "four", [5] = "five", [6] = "six"}', 'Table(3, 8) = {["two"] = 2, ["three"] = 3, ["one"] = 1}', + '{a = 1, b = "some string"}' ] for i, expected in enumerate(outputs): @@ -32,7 +33,7 @@ for i, expected in enumerate(outputs): gdb.flush() functionSymbol = gdb.selected_frame().block().function - assert functionSymbol.line == 21 + assert functionSymbol.line == 41, str(functionSymbol.line) if i == 6: # myArray is passed as pointer to int to myDebug. I look up myArray up in the stack @@ -47,6 +48,6 @@ for i, expected in enumerate(outputs): output = str(raw) - assert output == expected, output + " != " + expected + assert output == expected, "{0} : output: ({1}) != expected: ({2})".format(i, output, expected) gdb.write(f"passed\n", gdb.STDLOG) gdb.execute("continue") diff --git a/tests/untestable/gdb/gdb_pretty_printer_test_program.nim b/tests/untestable/gdb/gdb_pretty_printer_test_program.nim index c376ef89a..d2acdd282 100644 --- a/tests/untestable/gdb/gdb_pretty_printer_test_program.nim +++ b/tests/untestable/gdb/gdb_pretty_printer_test_program.nim @@ -14,7 +14,27 @@ type moTwo, moThree, moFoure, + + MyObj = object + a*: int + b*: string + + # MyVariant = ref object + # id*: int + # case kind*: MyEnum + # of meOne: mInt*: int + # of meTwo, meThree: discard + # of meFour: + # moInt*: int + # babies*: seq[MyVariant] + # after: float + # MyIntVariant = ref object + # stuff*: int + # case myKind*: range[0..32766] + # of 0: mFloat*: float + # of 2: mString*: string + # else: mBabies*: seq[MyIntVariant] var counter = 0 @@ -74,6 +94,18 @@ proc testProc(): void = var myOtherTable = {"one": 1, "two": 2, "three": 3}.toTable myDebug(myOtherTable) #14 + var obj = MyObj(a: 1, b: "some string") + myDebug(obj) #15 + + # var varObj = MyVariant(id: 13, kind: meFour, moInt: 94, + # babies: @[MyVariant(id: 18, kind: meOne, mInt: 7, after: 1.0), + # MyVariant(id: 21, kind: meThree, after: 2.0)], + # after: 3.0) + # myDebug(varObj) #16 + + # var varObjInt = MyIntVariant(stuff: 5, myKind: 2, mString: "this is my sweet string") + # myDebug(varObjInt) #17 + echo(counter) diff --git a/tools/nim-gdb.py b/tools/nim-gdb.py index 8143b94d5..f35b9a033 100644 --- a/tools/nim-gdb.py +++ b/tools/nim-gdb.py @@ -1,6 +1,7 @@ import gdb import re import sys +import traceback # some feedback that the nim runtime support is loading, isn't a bad # thing at all. @@ -13,14 +14,14 @@ def printErrorOnce(id, message): global errorSet if id not in errorSet: errorSet.add(id) - gdb.write(message, gdb.STDERR) + gdb.write("printErrorOnce: " + message, gdb.STDERR) ################################################################################ ##### Type pretty printers ################################################################################ -type_hash_regex = re.compile("^\w*_([A-Za-z0-9]*)$") +type_hash_regex = re.compile("^([A-Za-z0-9]*)_([A-Za-z0-9]*)_+([A-Za-z0-9]*)$") def getNimRti(type_name): """ Return a ``gdb.Value`` object for the Nim Runtime Information of ``type_name``. """ @@ -28,13 +29,20 @@ def getNimRti(type_name): # Get static const TNimType variable. This should be available for # every non trivial Nim type. m = type_hash_regex.match(type_name) + lookups = [ + "NTI" + m.group(2).lower() + "__" + m.group(3) + "_", + "NTI" + "__" + m.group(3) + "_", + "NTI" + m.group(2).replace("colon", "58").lower() + "__" + m.group(3) + "_" + ] if m: - try: - return gdb.parse_and_eval("NTI__" + m.group(1) + "_") - except: - return None + for l in lookups: + try: + return gdb.parse_and_eval(l) + except: + pass + None -def getNameFromNimRti(rti_val): +def getNameFromNimRti(rti): """ Return name (or None) given a Nim RTI ``gdb.Value`` """ try: # sometimes there isn't a name field -- example enums @@ -192,6 +200,7 @@ class NimStringEqFunction (gdb.Function): NimStringEqFunction() + ################################################################################ ##### GDB Command, equivalent of Nim's $ operator ################################################################################ @@ -315,22 +324,6 @@ class NimStringPrinter: else: return "" -# class NimStringPrinter: -# pattern = re.compile(r'^NimStringDesc$') - -# def __init__(self, val): -# self.val = val - -# def display_hint(self): -# return 'string' - -# def to_string(self): -# if self.val: -# l = int(self.val['Sup']['len']) -# return self.val['data'].lazy_string(encoding="utf-8", length=l) -# else: -# return "" - class NimRopePrinter: pattern = re.compile(r'^tyObject_RopeObj__([A-Za-z0-9]*) \*$') @@ -372,8 +365,8 @@ def reprEnum(e, typ): e = int(e) n = typ["node"] flags = int(typ["flags"]) - # 1 << 2 is {ntfEnumHole} - if ((1 << 2) & flags) == 0: + # 1 << 6 is {ntfEnumHole} + if ((1 << 6) & flags) == 0: o = e - int(n["sons"][0]["offset"]) if o >= 0 and 0 < int(n["len"]): return n["sons"][o]["name"].string("utf-8", "ignore") @@ -386,19 +379,26 @@ def reprEnum(e, typ): return str(e) + " (invalid data!)" +def enumNti(typeNimName, idString): + typeInfoName = "NTI" + typeNimName.lower() + "__" + idString + "_" + nti = gdb.lookup_global_symbol(typeInfoName) + if nti is None: + typeInfoName = "NTI" + "__" + idString + "_" + nti = gdb.lookup_global_symbol(typeInfoName) + return (typeInfoName, nti) + class NimEnumPrinter: - pattern = re.compile(r'^tyEnum_(\w*)__([A-Za-z0-9]*)$') + pattern = re.compile(r'^tyEnum_([A-Za-z0-9]+)__([A-Za-z0-9]*)$') def __init__(self, val): self.val = val typeName = self.val.type.name match = self.pattern.match(typeName) self.typeNimName = match.group(1) - typeInfoName = "NTI__" + match.group(2) + "_" - self.nti = gdb.lookup_global_symbol(typeInfoName) + typeInfoName, self.nti = enumNti(self.typeNimName, match.group(2)) if self.nti is None: - printErrorOnce(typeInfoName, f"NimEnumPrinter: lookup global symbol '{typeInfoName}' failed for {typeName}.\n") + printErrorOnce(typeInfoName, f"NimEnumPrinter: lookup global symbol: '{typeInfoName}' failed for {typeName}.\n") def to_string(self): if self.nti: @@ -414,18 +414,17 @@ class NimSetPrinter: ## the set printer is limited to sets that fit in an integer. Other ## sets are compiled to `NU8 *` (ptr uint8) and are invisible to ## gdb (currently). - pattern = re.compile(r'^tySet_tyEnum_(\w*)_([A-Za-z0-9]*)$') + pattern = re.compile(r'^tySet_tyEnum_([A-Za-z0-9]+)__([A-Za-z0-9]*)$') def __init__(self, val): self.val = val - match = self.pattern.match(self.val.type.name) - self.typeNimName = match.group(1) - - typeInfoName = "NTI__" + match.group(2) + "_" - self.nti = gdb.lookup_global_symbol(typeInfoName) + typeName = self.val.type.name + match = self.pattern.match(typeName) + self.typeNimName = match.group(1) + typeInfoName, self.nti = enumNti(self.typeNimName, match.group(2)) if self.nti is None: - printErrorOnce(typeInfoName, "NimSetPrinter: lookup global symbol '"+ typeInfoName +" failed for " + self.val.type.name + ".\n") + printErrorOnce(typeInfoName, f"NimSetPrinter: lookup global symbol: '{typeInfoName}' failed for {typeName}.\n") def to_string(self): if self.nti: @@ -563,7 +562,7 @@ class NimStringTablePrinter: def children(self): if self.val: - data = NimSeqPrinter(self.val['data'].dereference()) + data = NimSeqPrinter(self.val['data'].referenced_value()) for idxStr, entry in data.children(): if int(entry['Field0']) != 0: yield (idxStr + ".Field0", entry['Field0']) @@ -603,55 +602,156 @@ class NimTablePrinter: # this is untested, therefore disabled # class NimObjectPrinter: -# pattern = re.compile(r'^tyObject_.*$') +# pattern = re.compile(r'^tyObject_([A-Za-z0-9]+)__(_?[A-Za-z0-9]*)(:? \*)?$') # def __init__(self, val): # self.val = val +# self.valType = None +# self.valTypeNimName = None # def display_hint(self): # return 'object' +# def _determineValType(self): +# if self.valType is None: +# vt = self.val.type +# if vt.name is None: +# target = vt.target() +# self.valType = target.pointer() +# self.fields = target.fields() +# self.valTypeName = target.name +# self.isPointer = True +# else: +# self.valType = vt +# self.fields = vt.fields() +# self.valTypeName = vt.name +# self.isPointer = False + # def to_string(self): -# return str(self.val.type) +# if self.valTypeNimName is None: +# self._determineValType() +# match = self.pattern.match(self.valTypeName) +# self.valTypeNimName = match.group(1) + +# return self.valTypeNimName # def children(self): -# if not self.val: -# yield "object", "" -# raise StopIteration +# self._determineValType() +# if self.isPointer and int(self.val) == 0: +# return +# self.baseVal = self.val.referenced_value() if self.isPointer else self.val -# for (i, field) in enumerate(self.val.type.fields()): -# if field.type.code == gdb.TYPE_CODE_UNION: -# yield _union_field -# else: -# yield (field.name, self.val[field]) +# for c in self.handleFields(self.baseVal, getNimRti(self.valTypeName)): +# yield c + +# def handleFields(self, currVal, rti, fields = None): +# rtiSons = None +# discField = (0, None) +# seenSup = False +# if fields is None: +# fields = self.fields +# try: # XXX: remove try after finished debugging this method +# for (i, field) in enumerate(fields): +# if field.name == "Sup": # inherited data +# seenSup = True +# baseRef = rti['base'] +# if baseRef: +# baseRti = baseRef.referenced_value() +# baseVal = currVal['Sup'] +# baseValType = baseVal.type +# if baseValType.name is None: +# baseValType = baseValType.target().pointer() +# baseValFields = baseValType.target().fields() +# else: +# baseValFields = baseValType.fields() + +# for c in self.handleFields(baseVal, baseRti, baseValFields): +# yield c +# else: +# if field.type.code == gdb.TYPE_CODE_UNION: +# # if not rtiSons: +# rtiNode = rti['node'].referenced_value() +# rtiSons = rtiNode['sons'] -# def _union_field(self, i, field): -# rti = getNimRti(self.val.type.name) -# if rti is None: -# return (field.name, "UNION field can't be displayed without RTI") +# if not rtiSons and int(rtiNode['len']) == 0 and str(rtiNode['name']) != "0x0": +# rtiSons = [rti['node']] # sons are dereferenced by the consumer + +# if not rtiSons: +# printErrorOnce(self.valTypeName, f"NimObjectPrinter: UNION field can't be displayed without RTI {self.valTypeName}, using fallback.\n") +# # yield (field.name, self.baseVal[field]) # XXX: this fallback seems wrong +# return # XXX: this should probably continue instead? -# node_sons = rti['node'].dereference()['sons'] -# prev_field = self.val.type.fields()[i - 1] +# if int(rtiNode['len']) != 0 and str(rtiNode['name']) != "0x0": +# gdb.write(f"wtf IT HAPPENED {self.valTypeName}\n", gdb.STDERR) -# descriminant_node = None -# for i in range(int(node['len'])): -# son = node_sons[i].dereference() -# if son['name'].string("utf-8", "ignore") == str(prev_field.name): -# descriminant_node = son -# break -# if descriminant_node is None: -# raise ValueError("Can't find union descriminant field in object RTI") +# discNode = rtiSons[discField[0]].referenced_value() +# if not discNode: +# raise ValueError("Can't find union discriminant field in object RTI") + +# discNodeLen = int(discNode['len']) +# discFieldVal = int(currVal[discField[1].name]) -# if descriminant_node is None: raise ValueError("Can't find union field in object RTI") -# union_node = descriminant_node['sons'][int(self.val[prev_field])].dereference() -# union_val = self.val[field] +# unionNodeRef = None +# if discFieldVal < discNodeLen: +# unionNodeRef = discNode['sons'][discFieldVal] +# if not unionNodeRef: +# unionNodeRef = discNode['sons'][discNodeLen] -# for f1 in union_val.type.fields(): -# for f2 in union_val[f1].type.fields(): -# if str(f2.name) == union_node['name'].string("utf-8", "ignore"): -# return (str(f2.name), union_val[f1][f2]) +# if not unionNodeRef: +# printErrorOnce(self.valTypeName + "no union node", f"wtf is up with sons {self.valTypeName} {unionNodeRef} {rtiNode['offset']} {discNode} {discFieldVal} {discNodeLen} {discField[1].name} {field.name} {field.type}\n") +# continue -# raise ValueError("RTI is absent or incomplete, can't find union definition in RTI") +# unionNode = unionNodeRef.referenced_value() + +# fieldName = "" if field.name == None else field.name.lower() +# unionNodeName = "" if not unionNode['name'] else unionNode['name'].string("utf-8", "ignore") +# if not unionNodeName or unionNodeName.lower() != fieldName: +# unionFieldName = f"_{discField[1].name.lower()}_{int(rti['node'].referenced_value()['len'])}" +# gdb.write(f"wtf i: {i} union: {unionFieldName} field: {fieldName} type: {field.type.name} tag: {field.type.tag}\n", gdb.STDERR) +# else: +# unionFieldName = unionNodeName + +# if discNodeLen == 0: +# yield (unionFieldName, currVal[unionFieldName]) +# else: +# unionNodeLen = int(unionNode['len']) +# if unionNodeLen > 0: +# for u in range(unionNodeLen): +# un = unionNode['sons'][u].referenced_value()['name'].string("utf-8", "ignore") +# yield (un, currVal[unionFieldName][un]) +# else: +# yield(unionNodeName, currVal[unionFieldName]) +# else: +# discIndex = i - 1 if seenSup else i +# discField = (discIndex, field) # discriminant field is the last normal field +# yield (field.name, currVal[field.name]) +# except GeneratorExit: +# raise +# except: +# gdb.write(f"wtf {self.valTypeName} {i} fn: {field.name} df: {discField} rti: {rti} rtiNode: {rti['node'].referenced_value()} rtiSons: {rtiSons} {sys.exc_info()} {traceback.format_tb(sys.exc_info()[2], limit = 10)}\n", gdb.STDERR) +# gdb.write(f"wtf {self.valTypeName} {i} {field.name}\n", gdb.STDERR) + +# # seenSup = False +# # for (i, field) in enumerate(fields): +# # # if field.name: +# # # val = currVal[field.name] +# # # else: +# # # val = None +# # rtiNode = rti['node'].referenced_value() +# # rtiLen = int(rtiNode['len']) +# # if int(rtiNode['len']) > 0: +# # sons = rtiNode['sons'] +# # elif int(rti['len']) == 0 and str(rti['name']) != "0x0": +# # sons = [rti['node']] # sons are dereferenced by the consumer +# # sonsIdx = i - 1 if seenSup else i +# # s = sons[sonsIdx].referenced_value() +# # addr = int(currVal.address) +# # off = addr + int(rtiNode['offset']) +# # seenSup = seenSup or field.name == "Sup" + +# # gdb.write(f"wtf: i: {i} sonsIdx: {sonsIdx} field: {field.name} rtiLen: {rtiLen} rti: {rti} rtiNode: {rtiNode} isUnion: {field.type.code == gdb.TYPE_CODE_UNION} s: {s}\n", gdb.STDERR) + +# raise ################################################################################ From 28de32c8b274d5eb55dd4a777abbcfe1945861d5 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 5 Apr 2021 12:19:17 -0700 Subject: [PATCH 1879/3768] document caveats of quit (#17648) --- lib/system.nim | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/lib/system.nim b/lib/system.nim index ba27da0c7..bd9ae2762 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1185,10 +1185,6 @@ when defined(nimdoc): ## ## Before stopping the program the "exit procedures" are called in the ## opposite order they were added with `addExitProc `_. - ## `quit` never returns and ignores any exception that may have been raised - ## by the quit procedures. It does *not* call the garbage collector to free - ## all the memory, unless a quit procedure calls `GC_fullCollect - ## <#GC_fullCollect>`_. ## ## The proc `quit(QuitSuccess)` is called implicitly when your nim ## program finishes without incident for platforms where this is the @@ -1199,6 +1195,14 @@ when defined(nimdoc): ## have any compile time effect. If you need to stop the compiler inside a ## macro, use the `error `_ or `fatal ## `_ pragmas. + ## + ## .. danger:: In almost all cases, in particular in library code, prefer + ## alternatives, e.g. `doAssert false` or raise a `Defect`. + ## `quit` bypasses regular control flow in particular `defer`, + ## `try`, `catch`, `finally` and `destructors`, and exceptions that may have been + ## raised by an `addExitProc` proc, as well as cleanup code in other threads. + ## It does *not* call the garbage collector to free all the memory, + ## unless an `addExitProc` proc calls `GC_fullCollect <#GC_fullCollect>`_. elif defined(genode): include genode/env From aa4f18eff0344c4de828c1e22e440c0734113086 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 6 Apr 2021 02:11:30 -0700 Subject: [PATCH 1880/3768] items(array)+friends: remove a RT comparison (#17650) --- lib/system/iterators.nim | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/system/iterators.nim b/lib/system/iterators.nim index d8af4eed5..86d64d0c7 100644 --- a/lib/system/iterators.nim +++ b/lib/system/iterators.nim @@ -29,8 +29,8 @@ iterator mitems*[T](a: var openArray[T]): var T {.inline.} = iterator items*[IX, T](a: array[IX, T]): T {.inline.} = ## Iterates over each item of `a`. - var i = low(IX) - if i <= high(IX): + when a.len > 0: + var i = low(IX) while true: yield a[i] if i >= high(IX): break @@ -38,8 +38,8 @@ iterator items*[IX, T](a: array[IX, T]): T {.inline.} = iterator mitems*[IX, T](a: var array[IX, T]): var T {.inline.} = ## Iterates over each item of `a` so that you can modify the yielded value. - var i = low(IX) - if i <= high(IX): + when a.len > 0: + var i = low(IX) while true: yield a[i] if i >= high(IX): break @@ -146,8 +146,8 @@ iterator mpairs*[T](a: var openArray[T]): tuple[key: int, val: var T]{.inline.} iterator pairs*[IX, T](a: array[IX, T]): tuple[key: IX, val: T] {.inline.} = ## Iterates over each item of `a`. Yields `(index, a[index])` pairs. - var i = low(IX) - if i <= high(IX): + when a.len > 0: + var i = low(IX) while true: yield (i, a[i]) if i >= high(IX): break @@ -156,8 +156,8 @@ iterator pairs*[IX, T](a: array[IX, T]): tuple[key: IX, val: T] {.inline.} = iterator mpairs*[IX, T](a: var array[IX, T]): tuple[key: IX, val: var T] {.inline.} = ## Iterates over each item of `a`. Yields `(index, a[index])` pairs. ## `a[index]` can be modified. - var i = low(IX) - if i <= high(IX): + when a.len > 0: + var i = low(IX) while true: yield (i, a[i]) if i >= high(IX): break From 7bfb9f00022513c2d0f4f93799c07be7e9ffa1e6 Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 6 Apr 2021 22:20:01 +0800 Subject: [PATCH 1881/3768] close #17636 (#17643) --- tests/concepts/texplain.nim | 86 +++++++++---------- tests/concepts/twrapconcept.nim | 3 +- .../tdont_return_unowned_from_owned.nim | 4 +- tests/effects/tfuncs_cannot_mutate.nim | 4 +- tests/effects/tfuncs_cannot_mutate_simple.nim | 3 +- tests/errmsgs/t6608.nim | 3 +- tests/errmsgs/tconceptconstraint.nim | 1 - tests/errmsgs/tgenericconstraint.nim | 1 - tests/errmsgs/twrong_at_operator.nim | 3 +- tests/errmsgs/twrongcolon.nim | 3 +- tests/init/tuninit1.nim | 3 +- tests/misc/tnoop.nim | 3 +- tests/objvariant/tcheckedfield1.nim | 3 +- tests/parallel/tgc_unsafe2.nim | 7 +- .../varres/tprevent_forloopvar_mutations.nim | 3 +- tests/views/tcannot_borrow.nim | 5 +- 16 files changed, 60 insertions(+), 75 deletions(-) diff --git a/tests/concepts/texplain.nim b/tests/concepts/texplain.nim index de3d540cd..49eb8eb6b 100644 --- a/tests/concepts/texplain.nim +++ b/tests/concepts/texplain.nim @@ -1,45 +1,45 @@ discard """ cmd: "nim c --verbosity:0 --colors:off $file" nimout: ''' -texplain.nim(164, 10) Hint: Non-matching candidates for e(y) +texplain.nim(162, 10) Hint: Non-matching candidates for e(y) proc e(i: int): int first type mismatch at position: 1 required type for i: int but expression 'y' is of type: MatchingType -texplain.nim(167, 7) Hint: Non-matching candidates for e(10) +texplain.nim(165, 7) Hint: Non-matching candidates for e(10) proc e(o: ExplainedConcept): int first type mismatch at position: 1 required type for o: ExplainedConcept but expression '10' is of type: int literal(10) -texplain.nim(130, 6) ExplainedConcept: undeclared field: 'foo' -texplain.nim(130, 6) ExplainedConcept: undeclared field: '.' -texplain.nim(130, 6) ExplainedConcept: expression '.' cannot be called -texplain.nim(130, 6) ExplainedConcept: expression '' has no type (or is ambiguous) -texplain.nim(130, 5) ExplainedConcept: concept predicate failed -texplain.nim(131, 6) ExplainedConcept: undeclared field: 'bar' -texplain.nim(131, 6) ExplainedConcept: undeclared field: '.' -texplain.nim(131, 6) ExplainedConcept: expression '.' cannot be called -texplain.nim(131, 6) ExplainedConcept: expression '' has no type (or is ambiguous) -texplain.nim(130, 5) ExplainedConcept: concept predicate failed +texplain.nim(128, 6) ExplainedConcept: undeclared field: 'foo' +texplain.nim(128, 6) ExplainedConcept: undeclared field: '.' +texplain.nim(128, 6) ExplainedConcept: expression '.' cannot be called +texplain.nim(128, 6) ExplainedConcept: expression '' has no type (or is ambiguous) +texplain.nim(128, 5) ExplainedConcept: concept predicate failed +texplain.nim(129, 6) ExplainedConcept: undeclared field: 'bar' +texplain.nim(129, 6) ExplainedConcept: undeclared field: '.' +texplain.nim(129, 6) ExplainedConcept: expression '.' cannot be called +texplain.nim(129, 6) ExplainedConcept: expression '' has no type (or is ambiguous) +texplain.nim(128, 5) ExplainedConcept: concept predicate failed -texplain.nim(170, 10) Hint: Non-matching candidates for e(10) +texplain.nim(168, 10) Hint: Non-matching candidates for e(10) proc e(o: ExplainedConcept): int first type mismatch at position: 1 required type for o: ExplainedConcept but expression '10' is of type: int literal(10) -texplain.nim(130, 6) ExplainedConcept: undeclared field: 'foo' -texplain.nim(130, 6) ExplainedConcept: undeclared field: '.' -texplain.nim(130, 6) ExplainedConcept: expression '.' cannot be called -texplain.nim(130, 6) ExplainedConcept: expression '' has no type (or is ambiguous) -texplain.nim(130, 5) ExplainedConcept: concept predicate failed -texplain.nim(131, 6) ExplainedConcept: undeclared field: 'bar' -texplain.nim(131, 6) ExplainedConcept: undeclared field: '.' -texplain.nim(131, 6) ExplainedConcept: expression '.' cannot be called -texplain.nim(131, 6) ExplainedConcept: expression '' has no type (or is ambiguous) -texplain.nim(130, 5) ExplainedConcept: concept predicate failed +texplain.nim(128, 6) ExplainedConcept: undeclared field: 'foo' +texplain.nim(128, 6) ExplainedConcept: undeclared field: '.' +texplain.nim(128, 6) ExplainedConcept: expression '.' cannot be called +texplain.nim(128, 6) ExplainedConcept: expression '' has no type (or is ambiguous) +texplain.nim(128, 5) ExplainedConcept: concept predicate failed +texplain.nim(129, 6) ExplainedConcept: undeclared field: 'bar' +texplain.nim(129, 6) ExplainedConcept: undeclared field: '.' +texplain.nim(129, 6) ExplainedConcept: expression '.' cannot be called +texplain.nim(129, 6) ExplainedConcept: expression '' has no type (or is ambiguous) +texplain.nim(128, 5) ExplainedConcept: concept predicate failed -texplain.nim(174, 20) Error: type mismatch: got +texplain.nim(172, 20) Error: type mismatch: got but expected one of: proc e(i: int): int first type mismatch at position: 1 @@ -49,11 +49,11 @@ proc e(o: ExplainedConcept): int first type mismatch at position: 1 required type for o: ExplainedConcept but expression 'n' is of type: NonMatchingType -texplain.nim(174, 9) template/generic instantiation of `assert` from here -texplain.nim(130, 5) ExplainedConcept: concept predicate failed +texplain.nim(172, 9) template/generic instantiation of `assert` from here +texplain.nim(128, 5) ExplainedConcept: concept predicate failed expression: e(n) -texplain.nim(175, 20) Error: type mismatch: got +texplain.nim(173, 20) Error: type mismatch: got but expected one of: proc r(i: string): int first type mismatch at position: 1 @@ -63,15 +63,15 @@ proc r(o: RegularConcept): int first type mismatch at position: 1 required type for o: RegularConcept but expression 'n' is of type: NonMatchingType -texplain.nim(175, 9) template/generic instantiation of `assert` from here -texplain.nim(134, 5) RegularConcept: concept predicate failed +texplain.nim(173, 9) template/generic instantiation of `assert` from here +texplain.nim(132, 5) RegularConcept: concept predicate failed proc r[T](a: SomeNumber; b: T; c: auto) first type mismatch at position: 1 required type for a: SomeNumber but expression 'n' is of type: NonMatchingType expression: r(n) -texplain.nim(176, 20) Hint: Non-matching candidates for r(y) +texplain.nim(174, 20) Hint: Non-matching candidates for r(y) proc r(i: string): int first type mismatch at position: 1 required type for i: string @@ -81,28 +81,26 @@ proc r[T](a: SomeNumber; b: T; c: auto) required type for a: SomeNumber but expression 'y' is of type: MatchingType -texplain.nim(184, 2) Error: type mismatch: got +texplain.nim(182, 2) Error: type mismatch: got but expected one of: proc f(o: NestedConcept) first type mismatch at position: 1 required type for o: NestedConcept but expression 'y' is of type: MatchingType -texplain.nim(134, 6) RegularConcept: undeclared field: 'foo' -texplain.nim(134, 6) RegularConcept: undeclared field: '.' -texplain.nim(134, 6) RegularConcept: expression '.' cannot be called -texplain.nim(134, 6) RegularConcept: expression '' has no type (or is ambiguous) -texplain.nim(134, 5) RegularConcept: concept predicate failed -texplain.nim(135, 6) RegularConcept: undeclared field: 'bar' -texplain.nim(135, 6) RegularConcept: undeclared field: '.' -texplain.nim(135, 6) RegularConcept: expression '.' cannot be called -texplain.nim(135, 6) RegularConcept: expression '' has no type (or is ambiguous) -texplain.nim(134, 5) RegularConcept: concept predicate failed -texplain.nim(138, 5) NestedConcept: concept predicate failed +texplain.nim(132, 6) RegularConcept: undeclared field: 'foo' +texplain.nim(132, 6) RegularConcept: undeclared field: '.' +texplain.nim(132, 6) RegularConcept: expression '.' cannot be called +texplain.nim(132, 6) RegularConcept: expression '' has no type (or is ambiguous) +texplain.nim(132, 5) RegularConcept: concept predicate failed +texplain.nim(133, 6) RegularConcept: undeclared field: 'bar' +texplain.nim(133, 6) RegularConcept: undeclared field: '.' +texplain.nim(133, 6) RegularConcept: expression '.' cannot be called +texplain.nim(133, 6) RegularConcept: expression '' has no type (or is ambiguous) +texplain.nim(132, 5) RegularConcept: concept predicate failed +texplain.nim(136, 5) NestedConcept: concept predicate failed expression: f(y)''' errormsg: "type mismatch: got " - line: 184 - """ diff --git a/tests/concepts/twrapconcept.nim b/tests/concepts/twrapconcept.nim index 377b63afe..c3dea2ff9 100644 --- a/tests/concepts/twrapconcept.nim +++ b/tests/concepts/twrapconcept.nim @@ -1,7 +1,6 @@ discard """ errormsg: "type mismatch: got " - line: 21 - nimout: "twrapconcept.nim(11, 5) Foo: concept predicate failed" + nimout: "twrapconcept.nim(10, 5) Foo: concept predicate failed" """ # https://github.com/nim-lang/Nim/issues/5127 diff --git a/tests/destructor/tdont_return_unowned_from_owned.nim b/tests/destructor/tdont_return_unowned_from_owned.nim index a726960c6..d27626dea 100644 --- a/tests/destructor/tdont_return_unowned_from_owned.nim +++ b/tests/destructor/tdont_return_unowned_from_owned.nim @@ -1,6 +1,7 @@ discard """ cmd: "nim check --newruntime --hints:off $file" - nimout: '''tdont_return_unowned_from_owned.nim(36, 10) Error: cannot return an owned pointer as an unowned pointer; use 'owned(Obj)' as the return type + nimout: ''' +tdont_return_unowned_from_owned.nim(36, 10) Error: cannot return an owned pointer as an unowned pointer; use 'owned(Obj)' as the return type tdont_return_unowned_from_owned.nim(39, 10) Error: cannot return an owned pointer as an unowned pointer; use 'owned(Obj)' as the return type tdont_return_unowned_from_owned.nim(42, 6) Error: type mismatch: got but expected one of: @@ -16,7 +17,6 @@ tdont_return_unowned_from_owned.nim(51, 13) Error: assignment produces a danglin tdont_return_unowned_from_owned.nim(55, 10) Error: cannot return an owned pointer as an unowned pointer; use 'owned(RootRef)' as the return type ''' errormsg: "cannot return an owned pointer as an unowned pointer; use 'owned(RootRef)' as the return type" - line: 55 """ diff --git a/tests/effects/tfuncs_cannot_mutate.nim b/tests/effects/tfuncs_cannot_mutate.nim index 4768af2b3..8784cbbb1 100644 --- a/tests/effects/tfuncs_cannot_mutate.nim +++ b/tests/effects/tfuncs_cannot_mutate.nim @@ -2,8 +2,8 @@ discard """ errormsg: "'mutate' can have side effects" nimout: '''an object reachable from 'n' is potentially mutated tfuncs_cannot_mutate.nim(39, 15) the mutation is here -tfuncs_cannot_mutate.nim(37, 7) is the statement that connected the mutation to the parameter''' - line: 33 +tfuncs_cannot_mutate.nim(37, 7) is the statement that connected the mutation to the parameter +''' """ {.experimental: "strictFuncs".} diff --git a/tests/effects/tfuncs_cannot_mutate_simple.nim b/tests/effects/tfuncs_cannot_mutate_simple.nim index 9de20d1ec..a94a8d746 100644 --- a/tests/effects/tfuncs_cannot_mutate_simple.nim +++ b/tests/effects/tfuncs_cannot_mutate_simple.nim @@ -1,8 +1,7 @@ discard """ errormsg: "'edit' can have side effects" nimout: '''an object reachable from 'x' is potentially mutated -tfuncs_cannot_mutate_simple.nim(17, 4) the mutation is here''' - line: 16 +tfuncs_cannot_mutate_simple.nim(16, 4) the mutation is here''' """ {.experimental: "strictFuncs".} diff --git a/tests/errmsgs/t6608.nim b/tests/errmsgs/t6608.nim index 3d82f3cd8..88cbf42fd 100644 --- a/tests/errmsgs/t6608.nim +++ b/tests/errmsgs/t6608.nim @@ -1,10 +1,9 @@ discard """ cmd: "nim c --hints:off $file" errormsg: "type mismatch: got <>" - nimout: '''t6608.nim(14, 4) Error: type mismatch: got <> + nimout: '''t6608.nim(13, 4) Error: type mismatch: got <> but expected one of: AcceptCB = proc (s: string){.closure.}''' - line: 14 """ type diff --git a/tests/errmsgs/tconceptconstraint.nim b/tests/errmsgs/tconceptconstraint.nim index b1977acbc..066ec5bdb 100644 --- a/tests/errmsgs/tconceptconstraint.nim +++ b/tests/errmsgs/tconceptconstraint.nim @@ -1,6 +1,5 @@ discard """ errormsg: "cannot instantiate B" - line: 20 nimout: ''' got: but expected: diff --git a/tests/errmsgs/tgenericconstraint.nim b/tests/errmsgs/tgenericconstraint.nim index b7272e787..b51fb3488 100644 --- a/tests/errmsgs/tgenericconstraint.nim +++ b/tests/errmsgs/tgenericconstraint.nim @@ -1,6 +1,5 @@ discard """ errormsg: "cannot instantiate B" - line: 14 nimout: ''' got: but expected: diff --git a/tests/errmsgs/twrong_at_operator.nim b/tests/errmsgs/twrong_at_operator.nim index d7bfca415..438186f01 100644 --- a/tests/errmsgs/twrong_at_operator.nim +++ b/tests/errmsgs/twrong_at_operator.nim @@ -1,8 +1,7 @@ discard """ errormsg: "type mismatch: got " -line: 22 nimout: ''' -twrong_at_operator.nim(22, 30) Error: type mismatch: got +twrong_at_operator.nim(21, 30) Error: type mismatch: got but expected one of: proc `@`[IDX, T](a: sink array[IDX, T]): seq[T] first type mismatch at position: 1 diff --git a/tests/errmsgs/twrongcolon.nim b/tests/errmsgs/twrongcolon.nim index 20063cbc4..06e802eb7 100644 --- a/tests/errmsgs/twrongcolon.nim +++ b/tests/errmsgs/twrongcolon.nim @@ -1,11 +1,10 @@ discard """ errormsg: "in expression ' do:" nimout: ''' -twrongcolon.nim(11, 12) Error: in expression ' do: +twrongcolon.nim(10, 12) Error: in expression ' do: 890': identifier expected, but found '' ''' -line: 11 """ var n: int : 890 diff --git a/tests/init/tuninit1.nim b/tests/init/tuninit1.nim index b281bcf89..9a4161a30 100644 --- a/tests/init/tuninit1.nim +++ b/tests/init/tuninit1.nim @@ -1,6 +1,5 @@ discard """ - nimout: "tuninit1.nim(35, 11) Warning: use explicit initialization of 'y' for clarity [Uninit]" - line:34 + nimout: "tuninit1.nim(34, 11) Warning: use explicit initialization of 'y' for clarity [Uninit]" action: compile """ diff --git a/tests/misc/tnoop.nim b/tests/misc/tnoop.nim index 7f5bb1045..f55f2441a 100644 --- a/tests/misc/tnoop.nim +++ b/tests/misc/tnoop.nim @@ -1,9 +1,8 @@ discard """ nimout: ''' - found 'a' [var declared in tnoop.nim(11, 3)] + found 'a' [var declared in tnoop.nim(10, 3)] ''' file: "tnoop.nim" - line: 13 errormsg: "attempting to call routine: 'a'" """ diff --git a/tests/objvariant/tcheckedfield1.nim b/tests/objvariant/tcheckedfield1.nim index e1a2e60e6..4a6c49f66 100644 --- a/tests/objvariant/tcheckedfield1.nim +++ b/tests/objvariant/tcheckedfield1.nim @@ -1,6 +1,5 @@ discard """ - nimout: "tcheckedfield1.nim(40, 6) Warning: cannot prove that field 'x.s' is accessible [ProveField]" - line:51 + nimout: "tcheckedfield1.nim(39, 6) Warning: cannot prove that field 'x.s' is accessible [ProveField]" action: run output: "abc abc" """ diff --git a/tests/parallel/tgc_unsafe2.nim b/tests/parallel/tgc_unsafe2.nim index 40af728fb..7d98dafcb 100644 --- a/tests/parallel/tgc_unsafe2.nim +++ b/tests/parallel/tgc_unsafe2.nim @@ -1,9 +1,8 @@ discard """ errormsg: "'consumer' is not GC-safe as it calls 'track'" - line: 28 - nimout: '''tgc_unsafe2.nim(22, 6) Warning: 'trick' is not GC-safe as it accesses 'global' which is a global using GC'ed memory [GcUnsafe2] -tgc_unsafe2.nim(26, 6) Warning: 'track' is not GC-safe as it calls 'trick' [GcUnsafe2] -tgc_unsafe2.nim(28, 6) Error: 'consumer' is not GC-safe as it calls 'track' + nimout: '''tgc_unsafe2.nim(21, 6) Warning: 'trick' is not GC-safe as it accesses 'global' which is a global using GC'ed memory [GcUnsafe2] +tgc_unsafe2.nim(25, 6) Warning: 'track' is not GC-safe as it calls 'trick' [GcUnsafe2] +tgc_unsafe2.nim(27, 6) Error: 'consumer' is not GC-safe as it calls 'track' ''' """ diff --git a/tests/varres/tprevent_forloopvar_mutations.nim b/tests/varres/tprevent_forloopvar_mutations.nim index 045dc7cbb..96bb208c2 100644 --- a/tests/varres/tprevent_forloopvar_mutations.nim +++ b/tests/varres/tprevent_forloopvar_mutations.nim @@ -1,7 +1,6 @@ discard """ errormsg: "type mismatch: got " - line: 17 - nimout: '''tprevent_forloopvar_mutations.nim(17, 7) Error: type mismatch: got + nimout: '''tprevent_forloopvar_mutations.nim(16, 7) Error: type mismatch: got but expected one of: proc inc[T: Ordinal](x: var T; y = 1) first type mismatch at position: 1 diff --git a/tests/views/tcannot_borrow.nim b/tests/views/tcannot_borrow.nim index d1c194b25..0b8793159 100644 --- a/tests/views/tcannot_borrow.nim +++ b/tests/views/tcannot_borrow.nim @@ -1,8 +1,7 @@ discard """ errormsg: "cannot borrow" - nimout: '''tcannot_borrow.nim(21, 7) Error: cannot borrow meh; what it borrows from is potentially mutated -tcannot_borrow.nim(22, 3) the mutation is here''' - line: 21 + nimout: '''tcannot_borrow.nim(20, 7) Error: cannot borrow meh; what it borrows from is potentially mutated +tcannot_borrow.nim(21, 3) the mutation is here''' """ From c7b4639460dd377ee937e3468df9b7041744302f Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 6 Apr 2021 22:21:58 +0800 Subject: [PATCH 1882/3768] ref #14873 (#17644) * ref #14873 * comment * Update lib/core/locks.nim --- lib/core/locks.nim | 5 +++++ tests/stdlib/tlocks.nim | 6 +++--- tests/stdlib/uselocks.nim | 4 ++++ 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/lib/core/locks.nim b/lib/core/locks.nim index bddd6d864..1540e2bcd 100644 --- a/lib/core/locks.nim +++ b/lib/core/locks.nim @@ -28,6 +28,11 @@ type {.push stackTrace: off.} + +proc `$`*(lock: Lock): string = + # workaround bug #14873 + result = "()" + proc initLock*(lock: var Lock) {.inline.} = ## Initializes the given lock. when not defined(js): diff --git a/tests/stdlib/tlocks.nim b/tests/stdlib/tlocks.nim index e567cfde8..0815c5d01 100644 --- a/tests/stdlib/tlocks.nim +++ b/tests/stdlib/tlocks.nim @@ -1,10 +1,10 @@ discard """ - output: '''3''' - cmd: "nim $target --threads:on $options $file" + targets: "c cpp js" + matrix: "--threads:on" """ #bug #6049 import uselocks var m = createMyType[int]() -echo $m.use() +doAssert m.use() == 3 diff --git a/tests/stdlib/uselocks.nim b/tests/stdlib/uselocks.nim index cde9641b2..e9d23f9d9 100644 --- a/tests/stdlib/uselocks.nim +++ b/tests/stdlib/uselocks.nim @@ -9,3 +9,7 @@ proc createMyType*[T]: MyType[T] = proc use* (m: var MyType): int = withLock m.lock: result = 3 + +block: + var l: Lock + doAssert $l == "()" From e406e28738cfaebd6e0ff4642daa55c9114bd68a Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 7 Apr 2021 00:01:54 +0800 Subject: [PATCH 1883/3768] fix #16898 #17621 (#17628) * fix #16898 * fix #17621 * Update compiler/semtypes.nim --- compiler/semtypes.nim | 6 ++++-- compiler/typeallowed.nim | 3 ++- tests/lent/t16898.nim | 31 +++++++++++++++++++++++++++++++ tests/lent/t17621.nim | 15 +++++++++++++++ 4 files changed, 52 insertions(+), 3 deletions(-) create mode 100644 tests/lent/t16898.nim create mode 100644 tests/lent/t17621.nim diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 0fce7b417..b34dc36e3 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -10,7 +10,7 @@ # this module does the semantic checking of type declarations # included from sem.nim -import math +import std/math const errStringOrIdentNodeExpected = "string or ident node expected" @@ -1171,6 +1171,7 @@ proc liftParamType(c: PContext, procKind: TSymKind, genericParams: PNode, else: discard proc semParamType(c: PContext, n: PNode, constraint: var PNode): PType = + ## Semchecks the type of parameters. if n.kind == nkCurlyExpr: result = semTypeNode(c, n[0], nil) constraint = semNodeKindConstraints(n, c.config, 1) @@ -1226,10 +1227,11 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, if hasType: typ = semParamType(c, a[^2], constraint) # TODO: Disallow typed/untyped in procs in the compiler/stdlib - if kind == skProc and (typ.kind == tyTyped or typ.kind == tyUntyped): + if kind in {skProc, skFunc} and (typ.kind == tyTyped or typ.kind == tyUntyped): if not isMagic(getCurrOwner(c)): localError(c.config, a[^2].info, "'" & typ.sym.name.s & "' is only allowed in templates and macros or magic procs") + if hasDefault: def = a[^1] block determineType: diff --git a/compiler/typeallowed.nim b/compiler/typeallowed.nim index db3735359..9075d9e38 100644 --- a/compiler/typeallowed.nim +++ b/compiler/typeallowed.nim @@ -57,7 +57,8 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, of tyVar, tyLent: if kind in {skProc, skFunc, skConst} and (views notin c.features): result = t - elif t.kind == tyLent and kind != skResult and (views notin c.features): + elif t.kind == tyLent and ((kind != skResult and views notin c.features) or + kind == skParam): # lent can't be used as parameters. result = t else: var t2 = skipTypes(t[0], abstractInst-{tyTypeDesc, tySink}) diff --git a/tests/lent/t16898.nim b/tests/lent/t16898.nim new file mode 100644 index 000000000..ea7c934ef --- /dev/null +++ b/tests/lent/t16898.nim @@ -0,0 +1,31 @@ +discard """ + errormsg: "invalid type: 'lent QuadraticExt' in this context: 'proc (r: var QuadraticExt, a: lent QuadraticExt, b: lent QuadraticExt){.noSideEffect, gcsafe, locks: 0.}' for proc" +""" + +# bug #16898 +type + Fp[N: static int, T] = object + big: array[N, T] + +type + QuadraticExt* = concept x + ## Quadratic Extension concept (like complex) + type BaseField = auto + x.c0 is BaseField + x.c1 is BaseField + +{.experimental:"views".} + +func prod(r: var QuadraticExt, a, b: lent QuadraticExt) = + discard + +type + Fp2[N: static int, T] = object + c0, c1: Fp[N, T] + +# This should be passed by reference, +# but concepts do not respect the 24 bytes rule +# or `byref` pragma. +var r, a, b: Fp2[6, uint64] + +prod(r, a, b) diff --git a/tests/lent/t17621.nim b/tests/lent/t17621.nim new file mode 100644 index 000000000..e324a963e --- /dev/null +++ b/tests/lent/t17621.nim @@ -0,0 +1,15 @@ +discard """ + errormsg: "invalid type: 'lent Test' in this context: 'proc (self: lent Test)' for proc" +""" + +# bug #17621 +{.experimental: "views".} + +type Test = ref object + foo: int + +proc modify(self: lent Test) = + self.foo += 1 + +let test = Test(foo: 12) +modify(test) From 020c0a33447287c4d42e67754bae75d01b6160a0 Mon Sep 17 00:00:00 2001 From: Miran Date: Tue, 6 Apr 2021 18:52:12 +0200 Subject: [PATCH 1884/3768] don't run one example on 32-bit machines (#17655) This example seems to break our 32-bit nightlies builds. This is just a temporary solution (TM) until we figure out a better one. --- lib/pure/math.nim | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/pure/math.nim b/lib/pure/math.nim index 2ef7ee102..03e5040ae 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -951,9 +951,10 @@ func frexp*[T: float32|float64](x: T): tuple[frac: T, exp: int] {.inline.} = doAssert frexp(0.0) == (0.0, 0) # special cases: - doAssert frexp(-0.0).frac.signbit # signbit preserved for +-0 - doAssert frexp(Inf).frac == Inf # +- Inf preserved - doAssert frexp(NaN).frac.isNaN + when sizeof(int) == 8: + doAssert frexp(-0.0).frac.signbit # signbit preserved for +-0 + doAssert frexp(Inf).frac == Inf # +- Inf preserved + doAssert frexp(NaN).frac.isNaN when not defined(js): var exp: cint From 6ab58168661b27778dd6bd64bedf70a7889f5aa0 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 6 Apr 2021 13:01:34 -0700 Subject: [PATCH 1885/3768] add compiler/debugutils.nim to help debugging compiler (#17652) * add compiler/debugutils.nim * address comments --- compiler/debugutils.nim | 19 +++++++++++++++++++ compiler/options.nim | 23 ++++++++++++++++------- 2 files changed, 35 insertions(+), 7 deletions(-) create mode 100644 compiler/debugutils.nim diff --git a/compiler/debugutils.nim b/compiler/debugutils.nim new file mode 100644 index 000000000..235c100b3 --- /dev/null +++ b/compiler/debugutils.nim @@ -0,0 +1,19 @@ +##[ +Utilities to help with debugging nim compiler. + +Experimental API, subject to change. +]## + +import options + +var conf0: ConfigRef + +proc onNewConfigRef*(conf: ConfigRef) {.inline.} = + ## Caches `conf`, which can be retrieved with `getConfigRef`. + ## This avoids having to forward `conf` all the way down the call chain to + ## procs that need it during a debugging session. + conf0 = conf + +proc getConfigRef*(): ConfigRef = + ## nil, if -d:nimDebugUtils wasn't specified + result = conf0 diff --git a/compiler/options.nim b/compiler/options.nim index 2c8d43733..0bdb51159 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -446,6 +446,10 @@ proc newProfileData(): ProfileData = const foreignPackageNotesDefault* = { hintProcessing, warnUnknownMagic, hintQuitCalled, hintExecuting, hintUser, warnUser} +proc isDefined*(conf: ConfigRef; symbol: string): bool + +import debugutils + proc newConfigRef*(): ConfigRef = result = ConfigRef( selectedGC: gcRefc, @@ -504,16 +508,21 @@ proc newConfigRef*(): ConfigRef = # enable colors by default on terminals if terminal.isatty(stderr): incl(result.globalOptions, optUseColors) + when defined(nimDebugUtils): + onNewConfigRef(result) proc newPartialConfigRef*(): ConfigRef = ## create a new ConfigRef that is only good enough for error reporting. - result = ConfigRef( - selectedGC: gcRefc, - verbosity: 1, - options: DefaultOptions, - globalOptions: DefaultGlobalOptions, - foreignPackageNotes: foreignPackageNotesDefault, - notes: NotesVerbosity[1], mainPackageNotes: NotesVerbosity[1]) + when defined(nimDebugUtils): + result = getConfigRef() + else: + result = ConfigRef( + selectedGC: gcRefc, + verbosity: 1, + options: DefaultOptions, + globalOptions: DefaultGlobalOptions, + foreignPackageNotes: foreignPackageNotesDefault, + notes: NotesVerbosity[1], mainPackageNotes: NotesVerbosity[1]) proc cppDefine*(c: ConfigRef; define: string) = c.cppDefines.incl define From 34c1c631bb61e8a7dde923c73a2eecbd9d96345a Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 7 Apr 2021 04:08:59 +0800 Subject: [PATCH 1886/3768] close #4451 (#17627) --- doc/estp.rst | 8 ++++++++ lib/pure/nimprof.nim | 3 ++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/doc/estp.rst b/doc/estp.rst index 8146562b6..fa9977d44 100644 --- a/doc/estp.rst +++ b/doc/estp.rst @@ -21,6 +21,14 @@ You can in fact look at `nimprof`'s source code to see how to implement your own profiler. The setting `--profiler:on` defines the conditional symbol `profiler`. +You can use `when compileOption("profiler")` to make the switch seamless. +If `profiler` is `off`, your program runs normally. Otherwise your program +is profiled. + +```nim +when compileOption("profiler"): + import nimprof +``` After your program has finished the profiler will create a file `profile_results.txt` containing the profiling results. diff --git a/lib/pure/nimprof.nim b/lib/pure/nimprof.nim index 721ae35c3..3b1f703e3 100644 --- a/lib/pure/nimprof.nim +++ b/lib/pure/nimprof.nim @@ -9,7 +9,8 @@ ## Profiling support for Nim. This is an embedded profiler that requires ## `--profiler:on`. You only need to import this module to get a profiling -## report at program exit. +## report at program exit. See `Embedded Stack Trace Profiler `_ +## for usage. when not defined(profiler) and not defined(memProfiler): {.error: "Profiling support is turned off! Enable profiling by passing `--profiler:on --stackTrace:on` to the compiler (see the Nim Compiler User Guide for more options).".} From 444658fe5817808d71c06f1b5c272c1132d89bfb Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 7 Apr 2021 07:46:45 +0200 Subject: [PATCH 1887/3768] fixes #17656 (#17657) --- compiler/semexprs.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 71c9e0295..c0aa168ef 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2962,7 +2962,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = of nkMixinStmt: discard of nkBindStmt: if c.p != nil: - c.p.localBindStmts.add n + if n.len > 0 and n[0].kind == nkSym: + c.p.localBindStmts.add n else: localError(c.config, n.info, "invalid context for 'bind' statement: " & renderTree(n, {renderNoComments})) From d3702a4dc3f4226ae601f503bc3f2244f9e1bdce Mon Sep 17 00:00:00 2001 From: rockcavera Date: Wed, 7 Apr 2021 02:52:14 -0300 Subject: [PATCH 1888/3768] removing warning imported and not used debugutils (#17660) --- compiler/options.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/options.nim b/compiler/options.nim index 0bdb51159..7408ce675 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -448,7 +448,8 @@ const foreignPackageNotesDefault* = { proc isDefined*(conf: ConfigRef; symbol: string): bool -import debugutils +when defined(nimDebugUtils): + import debugutils proc newConfigRef*(): ConfigRef = result = ConfigRef( From 939df8cbdaf8b65966c60b4e368ba317cf4d194f Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 7 Apr 2021 13:40:57 +0200 Subject: [PATCH 1889/3768] ensure the avr example keeps compiling (#17663) * ensure the avr example keeps compiling * Update tests/avr/thello.nim * now compiles properly --- lib/system/embedded.nim | 4 ++++ tests/avr/nim.cfg | 12 ++++++++++++ tests/avr/panicoverride.nim | 13 +++++++++++++ tests/avr/thello.nim | 6 ++++++ 4 files changed, 35 insertions(+) create mode 100644 tests/avr/nim.cfg create mode 100644 tests/avr/panicoverride.nim create mode 100644 tests/avr/thello.nim diff --git a/lib/system/embedded.nim b/lib/system/embedded.nim index 258558c3f..e0b053c7b 100644 --- a/lib/system/embedded.nim +++ b/lib/system/embedded.nim @@ -34,6 +34,10 @@ proc quitOrDebug() {.noreturn, importc: "abort", header: "", nodecl.} proc raiseException(e: ref Exception, ename: cstring) {.compilerRtl.} = sysFatal(ReraiseDefect, "exception handling is not available") +proc raiseExceptionEx(e: sink(ref Exception), ename, procname, filename: cstring, + line: int) {.compilerRtl.} = + sysFatal(ReraiseDefect, "exception handling is not available") + proc reraiseException() {.compilerRtl.} = sysFatal(ReraiseDefect, "no exception to reraise") diff --git a/tests/avr/nim.cfg b/tests/avr/nim.cfg new file mode 100644 index 000000000..d6eba8eda --- /dev/null +++ b/tests/avr/nim.cfg @@ -0,0 +1,12 @@ +avr.standalone.gcc.path = "/usr/bin" +avr.standalone.gcc.exe = "avr-gcc" +avr.standalone.gcc.linkerexe = "avr-gcc" +passC = "-Os" +passC = "-DF_CPU=16000000UL" +passC = "-mmcu=atmega328p" +passL = "-mmcu=atmega328p" +passC = "-flto" +passL = "-flto" +cpu = "avr" +deadCodeElim = "on" +gc = "arc" diff --git a/tests/avr/panicoverride.nim b/tests/avr/panicoverride.nim new file mode 100644 index 000000000..770933ddd --- /dev/null +++ b/tests/avr/panicoverride.nim @@ -0,0 +1,13 @@ +proc printf(frmt: cstring) {.varargs, importc, header: "", cdecl.} +proc exit(code: int) {.importc, header: "", cdecl.} + +{.push stack_trace: off, profiler:off.} + +proc rawoutput(s: string) = + printf("%s\n", s) + +proc panic(s: string) = + rawoutput(s) + exit(1) + +{.pop.} diff --git a/tests/avr/thello.nim b/tests/avr/thello.nim new file mode 100644 index 000000000..a0191815c --- /dev/null +++ b/tests/avr/thello.nim @@ -0,0 +1,6 @@ +discard """ + cmd: "nim c --compileOnly --os:standalone --exceptions:quirky -d:noSignalHandler -d:danger $file" + action: "compile" +""" + +echo "hi" From 834d5641e88b144c1cf94fa13f567b0a2acda977 Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 7 Apr 2021 20:17:48 +0800 Subject: [PATCH 1890/3768] use sink and lent in deques (#17661) * use sink and lent in deques * Update lib/pure/collections/deques.nim Co-authored-by: Andreas Rumpf Co-authored-by: Andreas Rumpf --- lib/pure/collections/deques.nim | 20 ++++++------- tests/stdlib/tdeques.nim | 53 +++++++++++++++++++++++++++++++-- 2 files changed, 61 insertions(+), 12 deletions(-) diff --git a/lib/pure/collections/deques.nim b/lib/pure/collections/deques.nim index c8bbb1cc6..d2d53d993 100644 --- a/lib/pure/collections/deques.nim +++ b/lib/pure/collections/deques.nim @@ -50,7 +50,7 @@ runnableExamples: import std/private/since -import math +import std/math type Deque*[T] = object @@ -120,7 +120,7 @@ template xBoundsCheck(deq, i) = raise newException(IndexDefect, "Out of bounds: " & $i & " < 0") -proc `[]`*[T](deq: Deque[T], i: Natural): T {.inline.} = +proc `[]`*[T](deq: Deque[T], i: Natural): lent T {.inline.} = ## Accesses the `i`-th element of `deq`. runnableExamples: let a = [10, 20, 30, 40, 50].toDeque @@ -142,7 +142,7 @@ proc `[]`*[T](deq: var Deque[T], i: Natural): var T {.inline.} = xBoundsCheck(deq, i) return deq.data[(deq.head + i) and deq.mask] -proc `[]=`*[T](deq: var Deque[T], i: Natural, val: T) {.inline.} = +proc `[]=`*[T](deq: var Deque[T], i: Natural, val: sink T) {.inline.} = ## Sets the `i`-th element of `deq` to `val`. runnableExamples: var a = [10, 20, 30, 40, 50].toDeque @@ -154,7 +154,7 @@ proc `[]=`*[T](deq: var Deque[T], i: Natural, val: T) {.inline.} = xBoundsCheck(deq, i) deq.data[(deq.head + i) and deq.mask] = val -proc `[]`*[T](deq: Deque[T], i: BackwardsIndex): T {.inline.} = +proc `[]`*[T](deq: Deque[T], i: BackwardsIndex): lent T {.inline.} = ## Accesses the backwards indexed `i`-th element. ## ## `deq[^1]` is the last element. @@ -180,7 +180,7 @@ proc `[]`*[T](deq: var Deque[T], i: BackwardsIndex): var T {.inline.} = xBoundsCheck(deq, deq.len - int(i)) return deq[deq.len - int(i)] -proc `[]=`*[T](deq: var Deque[T], i: BackwardsIndex, x: T) {.inline.} = +proc `[]=`*[T](deq: var Deque[T], i: BackwardsIndex, x: sink T) {.inline.} = ## Sets the backwards indexed `i`-th element of `deq` to `x`. ## ## `deq[^1]` is the last element. @@ -194,7 +194,7 @@ proc `[]=`*[T](deq: var Deque[T], i: BackwardsIndex, x: T) {.inline.} = xBoundsCheck(deq, deq.len - int(i)) deq[deq.len - int(i)] = x -iterator items*[T](deq: Deque[T]): T = +iterator items*[T](deq: Deque[T]): lent T = ## Yields every element of `deq`. ## ## **See also:** @@ -270,7 +270,7 @@ proc expandIfNeeded[T](deq: var Deque[T]) = deq.tail = deq.count deq.head = 0 -proc addFirst*[T](deq: var Deque[T], item: T) = +proc addFirst*[T](deq: var Deque[T], item: sink T) = ## Adds an `item` to the beginning of `deq`. ## ## **See also:** @@ -286,7 +286,7 @@ proc addFirst*[T](deq: var Deque[T], item: T) = deq.head = (deq.head - 1) and deq.mask deq.data[deq.head] = item -proc addLast*[T](deq: var Deque[T], item: T) = +proc addLast*[T](deq: var Deque[T], item: sink T) = ## Adds an `item` to the end of `deq`. ## ## **See also:** @@ -302,7 +302,7 @@ proc addLast*[T](deq: var Deque[T], item: T) = deq.data[deq.tail] = item deq.tail = (deq.tail + 1) and deq.mask -proc peekFirst*[T](deq: Deque[T]): T {.inline.} = +proc peekFirst*[T](deq: Deque[T]): lent T {.inline.} = ## Returns the first element of `deq`, but does not remove it from the deque. ## ## **See also:** @@ -317,7 +317,7 @@ proc peekFirst*[T](deq: Deque[T]): T {.inline.} = emptyCheck(deq) result = deq.data[deq.head] -proc peekLast*[T](deq: Deque[T]): T {.inline.} = +proc peekLast*[T](deq: Deque[T]): lent T {.inline.} = ## Returns the last element of `deq`, but does not remove it from the deque. ## ## **See also:** diff --git a/tests/stdlib/tdeques.nim b/tests/stdlib/tdeques.nim index 99208d4cf..03e951fce 100644 --- a/tests/stdlib/tdeques.nim +++ b/tests/stdlib/tdeques.nim @@ -1,8 +1,11 @@ discard """ - targets: "c js" + matrix: "--gc:refc; --gc:orc" + targets: "c cpp js" """ import std/deques +from std/sequtils import toSeq + block: proc index(self: Deque[int], idx: Natural): int = @@ -125,7 +128,7 @@ block: foo(1, 5) foo(3, 2) -import sets +import std/sets block t13310: proc main() = @@ -137,3 +140,49 @@ block t13310: static: main() + + +proc main() = + block: + let a = [10, 20, 30].toDeque + doAssert toSeq(a.pairs) == @[(0, 10), (1, 20), (2, 30)] + + block: + let q = [7, 9].toDeque + doAssert 7 in q + doAssert q.contains(7) + doAssert 8 notin q + + block: + let a = [10, 20, 30, 40, 50].toDeque + doAssert $a == "[10, 20, 30, 40, 50]" + doAssert a.peekFirst == 10 + doAssert len(a) == 5 + + block: + let a = [10, 20, 30, 40, 50].toDeque + doAssert $a == "[10, 20, 30, 40, 50]" + doAssert a.peekLast == 50 + doAssert len(a) == 5 + + block: + var a = [10, 20, 30, 40, 50].toDeque + doAssert $a == "[10, 20, 30, 40, 50]" + doAssert a.popFirst == 10 + doAssert $a == "[20, 30, 40, 50]" + + block: + var a = [10, 20, 30, 40, 50].toDeque + doAssert $a == "[10, 20, 30, 40, 50]" + doAssert a.popLast == 50 + doAssert $a == "[10, 20, 30, 40]" + + block: + var a = [10, 20, 30, 40, 50].toDeque + doAssert $a == "[10, 20, 30, 40, 50]" + clear(a) + doAssert len(a) == 0 + + +static: main() +main() From a7d6ba45260d9fee3bdc2761fa67bcd114edac7f Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 8 Apr 2021 01:54:28 +0800 Subject: [PATCH 1891/3768] minor typos (#17668) --- compiler/semparallel.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/semparallel.nim b/compiler/semparallel.nim index b5b0be91b..f696f6a0a 100644 --- a/compiler/semparallel.nim +++ b/compiler/semparallel.nim @@ -460,10 +460,10 @@ proc transformSpawn(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n, barrier: result = n proc checkArgs(a: var AnalysisCtx; n: PNode) = - discard "too implement" + discard "to implement" proc generateAliasChecks(a: AnalysisCtx; result: PNode) = - discard "too implement" + discard "to implement" proc liftParallel*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n: PNode): PNode = # this needs to be called after the 'for' loop elimination From bd7807de4c7f8474b029f36b4d3527f7a03de6e4 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 7 Apr 2021 23:26:53 +0200 Subject: [PATCH 1892/3768] fixes #17647 (#17667) --- doc/hcr.rst | 2 +- lib/nimhcr.nim | 8 ++++---- lib/system/gc.nim | 20 ++++++++++---------- lib/system/gc_common.nim | 4 ++-- lib/system/gc_ms.nim | 10 +++++----- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/doc/hcr.rst b/doc/hcr.rst index 7e9d71da3..fd4ba34d4 100644 --- a/doc/hcr.rst +++ b/doc/hcr.rst @@ -192,7 +192,7 @@ compiled with the `-d:useNimRtl` option and they will depend on both the `nimrtl` library and the `nimhcr` library which implements the hot code reloading run-time. Both libraries can be found in the `lib` folder of Nim and can be compiled into dynamic libraries to satisfy -runtime demands of the example code above. An example of compiling +runtime demands of the example code above. An example of compiling `nimhcr.nim` and `nimrtl.nim` when the source dir of Nim is installed with choosenim follows. diff --git a/lib/nimhcr.nim b/lib/nimhcr.nim index 42e3266ae..2846f931b 100644 --- a/lib/nimhcr.nim +++ b/lib/nimhcr.nim @@ -209,7 +209,7 @@ when not defined(js) and (defined(hotcodereloading) or else: "so" type HcrProcGetter* = proc (libHandle: pointer, procName: cstring): pointer {.nimcall.} - HcrGcMarkerProc = proc () {.nimcall.} + HcrGcMarkerProc = proc () {.nimcall, raises: [].} HcrModuleInitializer* = proc () {.nimcall.} when defined(createNimHcr): @@ -606,7 +606,7 @@ when defined(createNimHcr): proc hcrGeneration*(): int {.nimhcr.} = generation - proc hcrMarkGlobals*() {.nimhcr, nimcall, gcsafe.} = + proc hcrMarkGlobals*() {.compilerproc, exportc, dynlib, nimcall, gcsafe.} = # This is gcsafe, because it will be registered # only in the GC of the main thread. {.gcsafe.}: @@ -648,10 +648,10 @@ elif defined(hotcodereloading) or defined(testNimHcr): proc hcrAddEventHandler*(isBefore: bool, cb: proc ()) {.nimhcr.} - proc hcrMarkGlobals*() {.nimhcr, nimcall, gcsafe.} + proc hcrMarkGlobals*() {.raises: [], nimhcr, nimcall, gcsafe.} when declared(nimRegisterGlobalMarker): - nimRegisterGlobalMarker(hcrMarkGlobals) + nimRegisterGlobalMarker(cast[GlobalMarkerProc](hcrMarkGlobals)) else: proc hcrHasModuleChanged*(moduleHash: string): bool = diff --git a/lib/system/gc.nim b/lib/system/gc.nim index 229b26f3c..039a627ba 100644 --- a/lib/system/gc.nim +++ b/lib/system/gc.nim @@ -94,7 +94,7 @@ type waZctDecRef, waPush #, waDebug - Finalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign.} + Finalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign, raises: [].} # A ref type can have a finalizer that is called before the object's # storage is freed. @@ -219,11 +219,11 @@ template gcTrace(cell, state: untyped) = when traceGC: traceCell(cell, state) # forward declarations: -proc collectCT(gch: var GcHeap) {.benign.} -proc isOnStack(p: pointer): bool {.noinline, benign.} -proc forAllChildren(cell: PCell, op: WalkOp) {.benign.} -proc doOperation(p: pointer, op: WalkOp) {.benign.} -proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) {.benign.} +proc collectCT(gch: var GcHeap) {.benign, raises: [].} +proc isOnStack(p: pointer): bool {.noinline, benign, raises: [].} +proc forAllChildren(cell: PCell, op: WalkOp) {.benign, raises: [].} +proc doOperation(p: pointer, op: WalkOp) {.benign, raises: [].} +proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) {.benign, raises: [].} # we need the prototype here for debugging purposes proc incRef(c: PCell) {.inline.} = @@ -636,7 +636,7 @@ proc markS(gch: var GcHeap, c: PCell) = if not containsOrIncl(gch.marked, d): forAllChildren(d, waMarkPrecise) -proc markGlobals(gch: var GcHeap) = +proc markGlobals(gch: var GcHeap) {.raises: [].} = if gch.gcThreadId == 0: for i in 0 .. globalMarkersLen-1: globalMarkers[i]() for i in 0 .. threadLocalMarkersLen-1: threadLocalMarkers[i]() @@ -691,9 +691,9 @@ proc doOperation(p: pointer, op: WalkOp) = proc nimGCvisit(d: pointer, op: int) {.compilerRtl.} = doOperation(d, WalkOp(op)) -proc collectZCT(gch: var GcHeap): bool {.benign.} +proc collectZCT(gch: var GcHeap): bool {.benign, raises: [].} -proc collectCycles(gch: var GcHeap) = +proc collectCycles(gch: var GcHeap) {.raises: [].} = when hasThreadSupport: for c in gch.toDispose: nimGCunref(c) @@ -800,7 +800,7 @@ proc unmarkStackAndRegisters(gch: var GcHeap) = decRef(d[i]) gch.decStack.len = 0 -proc collectCTBody(gch: var GcHeap) = +proc collectCTBody(gch: var GcHeap) {.raises: [].} = when withRealTime: let t0 = getticks() sysAssert(allocInv(gch.region), "collectCT: begin") diff --git a/lib/system/gc_common.nim b/lib/system/gc_common.nim index 7596de6fb..ea8857ece 100644 --- a/lib/system/gc_common.nim +++ b/lib/system/gc_common.nim @@ -415,7 +415,7 @@ else: # end of non-portable code # ---------------------------------------------------------------------------- -proc prepareDealloc(cell: PCell) = +proc prepareDealloc(cell: PCell) {.raises: [].} = when declared(useMarkForDebug): when useMarkForDebug: gcAssert(cell notin gch.marked, "Cell still alive!") @@ -458,7 +458,7 @@ proc deallocHeap*(runFinalizers = true; allowGcAfterwards = true) = initGC() type - GlobalMarkerProc = proc () {.nimcall, benign.} + GlobalMarkerProc = proc () {.nimcall, benign, raises: [].} var globalMarkersLen {.exportc.}: int globalMarkers {.exportc.}: array[0..3499, GlobalMarkerProc] diff --git a/lib/system/gc_ms.nim b/lib/system/gc_ms.nim index 852f5d7aa..b4221f7fd 100644 --- a/lib/system/gc_ms.nim +++ b/lib/system/gc_ms.nim @@ -36,7 +36,7 @@ type # local waMarkPrecise # fast precise marking - Finalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign.} + Finalizer {.compilerproc.} = proc (self: pointer) {.nimcall, benign, raises: [].} # A ref type can have a finalizer that is called before the object's # storage is freed. @@ -115,10 +115,10 @@ when BitsPerPage mod (sizeof(int)*8) != 0: {.error: "(BitsPerPage mod BitsPerUnit) should be zero!".} # forward declarations: -proc collectCT(gch: var GcHeap; size: int) {.benign.} -proc forAllChildren(cell: PCell, op: WalkOp) {.benign.} -proc doOperation(p: pointer, op: WalkOp) {.benign.} -proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) {.benign.} +proc collectCT(gch: var GcHeap; size: int) {.benign, raises: [].} +proc forAllChildren(cell: PCell, op: WalkOp) {.benign, raises: [].} +proc doOperation(p: pointer, op: WalkOp) {.benign, raises: [].} +proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) {.benign, raises: [].} # we need the prototype here for debugging purposes when defined(nimGcRefLeak): From 643dbc743bf6436cdc659ae57e2841e125aaa9de Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 8 Apr 2021 00:54:47 +0200 Subject: [PATCH 1893/3768] use strstr for a faster find implementation (#17672) * use strstr for a faster find implementation * stress the -d:release and -d:danger switches --- doc/tut1.rst | 7 ++++++- lib/pure/strutils.nim | 29 ++++++++++++++++++++++++++--- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/doc/tut1.rst b/doc/tut1.rst index d2d6f8fe7..cca4e8c0e 100644 --- a/doc/tut1.rst +++ b/doc/tut1.rst @@ -67,6 +67,11 @@ aiming for your debugging pleasure. With `-d:release` some checks are `turned off and optimizations are turned on `_. +For benchmarking or production code, use the `-d:release` switch. +For comparing the performance with unsafe languages like C, use the `-d:danger` switch +in order to get meaningful, comparable results. Otherwise Nim might be handicapped +by checks that are **not even available** for C. + Though it should be pretty obvious what the program does, I will explain the syntax: statements which are not indented are executed when the program starts. Indentation is Nim's way of grouping statements. Indentation is @@ -626,7 +631,7 @@ Some terminology: in the example `question` is called a (formal) *parameter*, Result variable --------------- A procedure that returns a value has an implicit `result` variable declared -that represents the return value. A `return` statement with no expression is +that represents the return value. A `return` statement with no expression is shorthand for `return result`. The `result` value is always returned automatically at the end of a procedure if there is no `return` statement at the exit. diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 3680b11de..bfd53a3a0 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -1826,6 +1826,9 @@ func find*(a: SkipTable, s, sub: string, start: Natural = 0, last = 0): int {. when not (defined(js) or defined(nimdoc) or defined(nimscript)): func c_memchr(cstr: pointer, c: char, n: csize_t): pointer {. importc: "memchr", header: "".} + func c_strstr(haystack, needle: cstring): cstring {. + importc: "strstr", header: "".} + const hasCStringBuiltin = true else: const hasCStringBuiltin = false @@ -1889,9 +1892,29 @@ func find*(s, sub: string, start: Natural = 0, last = 0): int {.rtl, ## * `replace func<#replace,string,string,string>`_ if sub.len > s.len: return -1 if sub.len == 1: return find(s, sub[0], start, last) - var a {.noinit.}: SkipTable - initSkipTable(a, sub) - result = find(a, s, sub, start, last) + + template useSkipTable {.dirty.} = + var a {.noinit.}: SkipTable + initSkipTable(a, sub) + result = find(a, s, sub, start, last) + + when not hasCStringBuiltin: + useSkipTable() + else: + when nimvm: + useSkipTable() + else: + when hasCStringBuiltin: + if last == 0 and s.len > start: + let found = c_strstr(s[start].unsafeAddr, sub) + if not found.isNil: + result = cast[ByteAddress](found) -% cast[ByteAddress](s.cstring) + else: + result = -1 + else: + useSkipTable() + else: + useSkipTable() func rfind*(s: string, sub: char, start: Natural = 0, last = -1): int {.rtl, extern: "nsuRFindChar".} = From 1b03accac78632897065ecfef40a6e40d8dfe328 Mon Sep 17 00:00:00 2001 From: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Date: Thu, 8 Apr 2021 14:08:58 +0200 Subject: [PATCH 1894/3768] Improve endians module (#17674) Extend documentation Add runnableExamples --- lib/pure/endians.nim | 49 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 42 insertions(+), 7 deletions(-) diff --git a/lib/pure/endians.nim b/lib/pure/endians.nim index 29fde1c80..a0dc97c1d 100644 --- a/lib/pure/endians.nim +++ b/lib/pure/endians.nim @@ -10,6 +10,11 @@ ## This module contains helpers that deal with different byte orders ## (`endian`:idx:). ## +## Endianess is the order of bytes of a value in memory. Big-endian means that +## the most significant byte is stored at the smallest memory address, +## while little endian means that the least-significant byte is stored +## at the smallest address. See also https://en.wikipedia.org/wiki/Endianness. +## ## Unstable API. when defined(gcc) or defined(llvm_gcc) or defined(clang): @@ -47,7 +52,7 @@ else: when useBuiltinSwap: template swapOpImpl(T: typedesc, op: untyped) = - ## We have to use `copyMem` here instead of a simple deference because they + ## We have to use `copyMem` here instead of a simple dereference because they ## may point to a unaligned address. A sufficiently smart compiler _should_ ## be able to elide them when they're not necessary. var tmp: T @@ -56,18 +61,40 @@ when useBuiltinSwap: copyMem(outp, addr tmp, sizeof(T)) proc swapEndian64*(outp, inp: pointer) {.inline, noSideEffect.} = + ## Copies `inp` to `outp`, reversing the byte order. + ## Both buffers are supposed to contain at least 8 bytes. + runnableExamples: + var a = [1'u8, 2, 3, 4, 5, 6, 7, 8] + var b: array[8, uint8] + swapEndian64(addr b, addr a) + assert b == [8'u8, 7, 6, 5, 4, 3, 2, 1] + swapOpImpl(uint64, builtin_bswap64) proc swapEndian32*(outp, inp: pointer) {.inline, noSideEffect.} = + ## Copies `inp` to `outp`, reversing the byte order. + ## Both buffers are supposed to contain at least 4 bytes. + runnableExamples: + var a = [1'u8, 2, 3, 4] + var b: array[4, uint8] + swapEndian32(addr b, addr a) + assert b == [4'u8, 3, 2, 1] + swapOpImpl(uint32, builtin_bswap32) proc swapEndian16*(outp, inp: pointer) {.inline, noSideEffect.} = + ## Copies `inp` to `outp`, reversing the byte order. + ## Both buffers are supposed to contain at least 2 bytes. + runnableExamples: + var a = [1'u8, 2] + var b: array[2, uint8] + swapEndian16(addr b, addr a) + assert b == [2'u8, 1] + swapOpImpl(uint16, builtin_bswap16) else: proc swapEndian64*(outp, inp: pointer) = - ## copies `inp` to `outp` swapping bytes. Both buffers are supposed to - ## contain at least 8 bytes. var i = cast[cstring](inp) var o = cast[cstring](outp) o[0] = i[7] @@ -80,8 +107,6 @@ else: o[7] = i[0] proc swapEndian32*(outp, inp: pointer) = - ## copies `inp` to `outp` swapping bytes. Both buffers are supposed to - ## contain at least 4 bytes. var i = cast[cstring](inp) var o = cast[cstring](outp) o[0] = i[3] @@ -90,8 +115,6 @@ else: o[3] = i[0] proc swapEndian16*(outp, inp: pointer) = - ## copies `inp` to `outp` swapping bytes. Both buffers are supposed to - ## contain at least 2 bytes. var i = cast[cstring](inp) var o = cast[cstring](outp) o[0] = i[1] @@ -106,8 +129,20 @@ when system.cpuEndian == bigEndian: proc bigEndian16*(outp, inp: pointer) {.inline.} = copyMem(outp, inp, 2) else: proc littleEndian64*(outp, inp: pointer) {.inline.} = copyMem(outp, inp, 8) + ## Copies `inp` to `outp`, storing it in 64-bit little-endian order. + ## Both buffers are supposed to contain at least 8 bytes. proc littleEndian32*(outp, inp: pointer) {.inline.} = copyMem(outp, inp, 4) + ## Copies `inp` to `outp`, storing it in 32-bit little-endian order. + ## Both buffers are supposed to contain at least 4 bytes. proc littleEndian16*(outp, inp: pointer){.inline.} = copyMem(outp, inp, 2) + ## Copies `inp` to `outp`, storing it in 16-bit little-endian order. + ## Both buffers are supposed to contain at least 2 bytes. proc bigEndian64*(outp, inp: pointer) {.inline.} = swapEndian64(outp, inp) + ## Copies `inp` to `outp`, storing it in 64-bit big-endian order. + ## Both buffers are supposed to contain at least 8 bytes. proc bigEndian32*(outp, inp: pointer) {.inline.} = swapEndian32(outp, inp) + ## Copies `inp` to `outp`, storing it in 32-bit big-endian order. + ## Both buffers are supposed to contain at least 4 bytes. proc bigEndian16*(outp, inp: pointer) {.inline.} = swapEndian16(outp, inp) + ## Copies `inp` to `outp`, storing it in 16-bit big-endian order. + ## Both buffers are supposed to contain at least 2 bytes. From 4d3f9d3536e84d756f15b708177f289417dca3d2 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 8 Apr 2021 17:46:51 +0200 Subject: [PATCH 1895/3768] manual: minor improvements (#17677) * manual: minor improvements * Update doc/manual.rst Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> --- doc/manual.rst | 94 +++++++++++++++++++++++++------------------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/doc/manual.rst b/doc/manual.rst index 3c71ece95..aad8034c8 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -222,7 +222,7 @@ comment: `Documentation comments`:idx: are comments that start with two `##`. Documentation comments are tokens; they are only allowed at certain places in -the input file as they belong to the syntax tree! +the input file as they belong to the syntax tree. Multiline comments @@ -315,8 +315,8 @@ it was not case-sensitive and underscores were ignored and there was not even a distinction between `foo` and `Foo`. -Stropping ---------- +Keywords as identifiers +----------------------- If a keyword is enclosed in backticks it loses its keyword property and becomes an ordinary identifier. @@ -326,12 +326,12 @@ Examples var `var` = "Hello Stropping" .. code-block:: nim - type Type = object - `int`: int + type Obj = object + `type`: int - let `object` = Type(`int`: 9) - assert `object` is Type - assert `object`.`int` == 9 + let `object` = Obj(`type`: 9) + assert `object` is Obj + assert `object`.`type` == 9 var `var` = 42 let `let` = 8 @@ -442,7 +442,7 @@ Terminal symbols in the grammar: `GENERALIZED_STR_LIT`, The construct `identifier"string literal"` (without whitespace between the identifier and the opening quotation mark) is a generalized raw string literal. It is a shortcut for the construct -`identifier(r"string literal")`, so it denotes a procedure call with a +`identifier(r"string literal")`, so it denotes a routine call with a raw string literal as its only argument. Generalized raw string literals are especially convenient for embedding mini languages directly into Nim (for example regular expressions). @@ -457,8 +457,8 @@ Character literals Character literals are enclosed in single quotes `''` and can contain the same escape sequences as strings - with one exception: the platform dependent `newline`:idx: (``\p``) -is not allowed as it may be wider than one character (often it is the pair -CR/LF for example). Here are the valid `escape sequences`:idx: for character +is not allowed as it may be wider than one character (it can be the pair +CR/LF). Here are the valid `escape sequences`:idx: for character literals: ================== =================================================== @@ -482,19 +482,21 @@ literals: exactly two hex digits are allowed ================== =================================================== -A character is not a Unicode character but a single byte. The reason for this -is efficiency: for the overwhelming majority of use-cases, the resulting -programs will still handle UTF-8 properly as UTF-8 was specially designed for -this. Another reason is that Nim can thus support `array[char, int]` or -`set[char]` efficiently as many algorithms rely on this feature. The `Rune` -type is used for Unicode characters, it can represent any Unicode character. +A character is not a Unicode character but a single byte. + +Rationale: It enables the efficient support of `array[char, int]` or +`set[char]`. + +The `Rune` type can represent any Unicode character. `Rune` is declared in the `unicode module `_. A character literal that does not end in ``'`` is interpreted as ``'`` if there is a preceeding backtick token. There must be no whitespace between the preceeding -backtick token and the character literal. This special rule ensures that a declaration -like ``proc `'customLiteral`(s: string)`` is valid. See also -`Custom Numeric Literals <#custom-numeric-literals>`_. +backtick token and the character literal. This special case ensures that a declaration +like ``proc `'customLiteral`(s: string)`` is valid. ``proc `'customLiteral`(s: string)`` +is the same as ``proc `'\''customLiteral`(s: string)``. + +See also `Custom Numeric Literals <#custom-numeric-literals>`_. Numeric Literals @@ -908,37 +910,37 @@ constant expressions that may be surprising to those coming from other statically typed languages. For example, the following code echoes the beginning of the Fibonacci series **at compile-time**. (This is a demonstration of flexibility in defining constants, not a recommended style for solving this -problem!) +problem.) .. code-block:: nim :test: "nim c $1" import std/strformat - var fib_n {.compileTime.}: int - var fib_prev {.compileTime.}: int - var fib_prev_prev {.compileTime.}: int + var fibN {.compileTime.}: int + var fibPrev {.compileTime.}: int + var fibPrevPrev {.compileTime.}: int - proc next_fib(): int = - result = if fib_n < 2: - fib_n + proc nextFib(): int = + result = if fibN < 2: + fibN else: - fib_prev_prev + fib_prev - inc(fib_n) - fib_prev_prev = fib_prev - fib_prev = result + fibPrevPrev + fibPrev + inc(fibN) + fibPrevPrev = fibPrev + fibPrev = result - const f0 = next_fib() - const f1 = next_fib() + const f0 = nextFib() + const f1 = nextFib() - const display_fib = block: - const f2 = next_fib() + const displayFib = block: + const f2 = nextFib() var result = fmt"Fibonacci sequence: {f0}, {f1}, {f2}" for i in 3..12: - add(result, fmt", {next_fib()}") + add(result, fmt", {nextFib()}") result static: - echo display_fib + echo displayFib Restrictions on Compile-Time Execution @@ -1013,7 +1015,9 @@ intXX Literals of these types have the suffix 'iXX. `uint` - the generic `unsigned integer`:idx: type; its size is platform-dependent and has the same size as a pointer. An integer literal with the type suffix `'u` is of this type. + the generic `unsigned integer`:idx: type; its size is platform-dependent and + has the same size as a pointer. An integer literal with the type + suffix `'u` is of this type. uintXX additional unsigned integer types of XX bits use this naming scheme @@ -1199,12 +1203,8 @@ Character type -------------- The character type is named `char` in Nim. Its size is one byte. Thus it cannot represent a UTF-8 character, but a part of it. -The reason for this is efficiency: for the overwhelming majority of use-cases, -the resulting programs will still handle UTF-8 properly as UTF-8 was especially -designed for this. -Another reason is that Nim can support `array[char, int]` or -`set[char]` efficiently as many algorithms rely on this feature. The -`Rune` type is used for Unicode characters, it can represent any Unicode + +The `Rune` type is used for Unicode characters, it can represent any Unicode character. `Rune` is declared in the `unicode module `_. @@ -1232,7 +1232,7 @@ Now the following holds:: # Also allowed: ord(Direction.west) == 3 -Thus, north < east < south < west. The comparison operators can be used +The implied order is: north < east < south < west. The comparison operators can be used with enumeration types. Instead of `north` etc, the enum value can also be qualified with the enum type that it resides in, `Direction.north`. @@ -1248,7 +1248,7 @@ An explicit ordered enum can have *holes*: TokenType = enum a = 2, b = 4, c = 89 # holes are valid -However, it is then not ordinal anymore, so it is not possible to use these +However, it is then not ordinal anymore, so it is impossible to use these enums as an index type for arrays. The procedures `inc`, `dec`, `succ` and `pred` are not available for them either. @@ -7885,4 +7885,4 @@ Threads and exceptions The interaction between threads and exceptions is simple: A *handled* exception in one thread cannot affect any other thread. However, an *unhandled* exception -in one thread terminates the whole *process*! +in one thread terminates the whole *process*. From 42687457b079c16f69b22ff2354590780bd4cf45 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Thu, 8 Apr 2021 20:00:14 +0300 Subject: [PATCH 1896/3768] further progress on rst roles & directives (fix #17646) (#17659) * further progress on rst roles & dir-s (fix #17646) * fix documents according to the messages * fix bug 17 from #17340 --- doc/manual_experimental.rst | 2 +- lib/experimental/diff.nim | 4 +- lib/packages/docutils/highlite.nim | 4 +- lib/packages/docutils/rst.nim | 110 ++++++++++++++++++++--------- lib/pure/asyncdispatch.nim | 2 +- lib/pure/asyncftpclient.nim | 6 +- lib/pure/asynchttpserver.nim | 2 +- lib/pure/asyncnet.nim | 2 +- lib/pure/base64.nim | 8 +-- lib/pure/dynlib.nim | 2 +- lib/system/iterators.nim | 2 +- tests/stdlib/trstgen.nim | 36 ++++++++++ 12 files changed, 129 insertions(+), 51 deletions(-) diff --git a/doc/manual_experimental.rst b/doc/manual_experimental.rst index fc46a2a14..51037b509 100644 --- a/doc/manual_experimental.rst +++ b/doc/manual_experimental.rst @@ -330,7 +330,7 @@ Routines with the same type signature can be called differently if a parameter has different names. This does not need an `experimental` switch, but is an unstable feature. -.. code-block::nim +.. code-block:: Nim proc foo(x: int) = echo "Using x: ", x proc foo(y: int) = diff --git a/lib/experimental/diff.nim b/lib/experimental/diff.nim index 3462a0fa4..ba4e8ad64 100644 --- a/lib/experimental/diff.nim +++ b/lib/experimental/diff.nim @@ -12,14 +12,14 @@ ## ## A basic example of `diffInt` on 2 arrays of integers: ## -## .. code::nim +## .. code:: Nim ## ## import experimental/diff ## echo diffInt([0, 1, 2, 3, 4, 5, 6, 7, 8], [-1, 1, 2, 3, 4, 5, 666, 7, 42]) ## ## Another short example of `diffText` to diff strings: ## -## .. code::nim +## .. code:: Nim ## ## import experimental/diff ## # 2 samples of text for testing (from "The Call of Cthulhu" by Lovecraft) diff --git a/lib/packages/docutils/highlite.nim b/lib/packages/docutils/highlite.nim index 2655a7ea3..94cb2ebde 100644 --- a/lib/packages/docutils/highlite.nim +++ b/lib/packages/docutils/highlite.nim @@ -13,7 +13,7 @@ ## ## You can use this to build your own syntax highlighting, check this example: ## -## .. code::nim +## .. code:: Nim ## let code = """for x in $int.high: echo x.ord mod 2 == 0""" ## var toknizr: GeneralTokenizer ## initGeneralTokenizer(toknizr, code) @@ -34,7 +34,7 @@ ## ## The proc `getSourceLanguage` can get the language `enum` from a string: ## -## .. code::nim +## .. code:: Nim ## for l in ["C", "c++", "jAvA", "Nim", "c#"]: echo getSourceLanguage(l) ## diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index dfa2f12be..a8bc04a1a 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -912,6 +912,38 @@ template newLeaf(s: string): PRstNode = newRstLeaf(s) proc newLeaf(p: var RstParser): PRstNode = result = newLeaf(currentTok(p).symbol) +proc validRefnamePunct(x: string): bool = + ## https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#reference-names + x.len == 1 and x[0] in {'-', '_', '.', ':', '+'} + +func getRefnameIdx(p: RstParser, startIdx: int): int = + ## Gets last token index of a refname ("word" in RST terminology): + ## + ## reference names are single words consisting of alphanumerics plus + ## isolated (no two adjacent) internal hyphens, underscores, periods, + ## colons and plus signs; no whitespace or other characters are allowed. + ## + ## Refnames are used for: + ## - reference names + ## - role names + ## - directive names + ## - footnote labels + ## + # TODO: use this func in all other relevant places + var j = startIdx + if p.tok[j].kind == tkWord: + inc j + while p.tok[j].kind == tkPunct and validRefnamePunct(p.tok[j].symbol) and + p.tok[j+1].kind == tkWord: + inc j, 2 + result = j - 1 + +func getRefname(p: RstParser, startIdx: int): (string, int) = + let lastIdx = getRefnameIdx(p, startIdx) + result[1] = lastIdx + for j in startIdx..lastIdx: + result[0].add p.tok[j].symbol + proc getReferenceName(p: var RstParser, endStr: string): PRstNode = var res = newRstNode(rnInner) while true: @@ -1011,7 +1043,10 @@ proc match(p: RstParser, start: int, expr: string): bool = var last = expr.len - 1 while i <= last: case expr[i] - of 'w': result = p.tok[j].kind == tkWord + of 'w': + let lastIdx = getRefnameIdx(p, j) + result = lastIdx >= j + if result: j = lastIdx of ' ': result = p.tok[j].kind == tkWhite of 'i': result = p.tok[j].kind == tkIndent of 'I': result = p.tok[j].kind in {tkIndent, tkEof} @@ -1058,7 +1093,7 @@ proc fixupEmbeddedRef(n, a, b: PRstNode) = proc whichRole(p: RstParser, sym: string): RstNodeKind = result = whichRoleAux(sym) if result == rnUnknownRole: - rstMessage(p, mwUnsupportedLanguage, p.s.currRole) + rstMessage(p, mwUnsupportedLanguage, sym) proc toInlineCode(n: PRstNode, language: string): PRstNode = ## Creates rnInlineCode and attaches `n` contents as code (in 3rd son). @@ -1078,6 +1113,11 @@ proc toInlineCode(n: PRstNode, language: string): PRstNode = lb.add newLeaf(s) result.add lb +proc toUnknownRole(n: PRstNode, roleName: string): PRstNode = + let newN = newRstNode(rnInner, n.sons) + let newSons = @[newN, newLeaf(roleName)] + result = newRstNode(rnUnknownRole, newSons) + proc parsePostfix(p: var RstParser, n: PRstNode): PRstNode = var newKind = n.kind var newSons = n.sons @@ -1102,17 +1142,15 @@ proc parsePostfix(p: var RstParser, n: PRstNode): PRstNode = result = newRstNode(newKind, newSons) elif match(p, p.idx, ":w:"): # a role: - let roleName = nextTok(p).symbol + let (roleName, lastIdx) = getRefname(p, p.idx+1) newKind = whichRole(p, roleName) if newKind == rnUnknownRole: - let newN = newRstNode(rnInner, n.sons) - newSons = @[newN, newLeaf(roleName)] - result = newRstNode(newKind, newSons) + result = n.toUnknownRole(roleName) elif newKind == rnInlineCode: result = n.toInlineCode(language=roleName) else: result = newRstNode(newKind, newSons) - inc p.idx, 3 + p.idx = lastIdx + 2 else: if p.s.currRoleKind == rnInlineCode: result = n.toInlineCode(language=p.s.currRole) @@ -1139,10 +1177,6 @@ proc parseSmiley(p: var RstParser): PRstNode = result.text = val return -proc validRefnamePunct(x: string): bool = - ## https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#reference-names - x.len == 1 and x[0] in {'-', '_', '.', ':', '+'} - proc isUrl(p: RstParser, i: int): bool = result = p.tok[i+1].symbol == ":" and p.tok[i+2].symbol == "//" and p.tok[i+3].kind == tkWord and @@ -1373,14 +1407,18 @@ proc parseInline(p: var RstParser, father: PRstNode) = var n = newRstNode(rnInlineLiteral) parseUntil(p, n, "``", false) father.add(n) - elif match(p, p.idx, ":w:") and p.tok[p.idx+3].symbol == "`": - let roleName = nextTok(p).symbol + elif match(p, p.idx, ":w:") and + (var lastIdx = getRefnameIdx(p, p.idx + 1); + p.tok[lastIdx+2].symbol == "`"): + let (roleName, _) = getRefname(p, p.idx+1) let k = whichRole(p, roleName) var n = newRstNode(k) - inc p.idx, 3 + p.idx = lastIdx + 2 if k == rnInlineCode: n = n.toInlineCode(language=roleName) parseUntil(p, n, "`", false) # bug #17260 + if k == rnUnknownRole: + n = n.toUnknownRole(roleName) father.add(n) elif isInlineMarkupStart(p, "`"): var n = newRstNode(rnInterpretedText) @@ -1438,25 +1476,28 @@ proc parseInline(p: var RstParser, father: PRstNode) = else: discard proc getDirective(p: var RstParser): string = - if currentTok(p).kind == tkWhite and nextTok(p).kind == tkWord: - var j = p.idx - inc p.idx - result = currentTok(p).symbol - inc p.idx - while currentTok(p).kind in {tkWord, tkPunct, tkAdornment, tkOther}: - if currentTok(p).symbol == "::": break - result.add(currentTok(p).symbol) - inc p.idx - if currentTok(p).kind == tkWhite: inc p.idx - if currentTok(p).symbol == "::": - inc p.idx - if currentTok(p).kind == tkWhite: inc p.idx - else: - p.idx = j # set back - result = "" # error - else: - result = "" - result = result.toLowerAscii() + result = "" + if currentTok(p).kind == tkWhite: + let (name, lastIdx) = getRefname(p, p.idx + 1) + let afterIdx = lastIdx + 1 + if name.len > 0: + if p.tok[afterIdx].symbol == "::": + result = name + p.idx = afterIdx + 1 + if currentTok(p).kind == tkWhite: + inc p.idx + elif currentTok(p).kind != tkIndent: + rstMessage(p, mwRstStyle, + "whitespace or newline expected after directive " & name) + result = result.toLowerAscii() + elif p.tok[afterIdx].symbol == ":": + rstMessage(p, mwRstStyle, + "double colon :: may be missing at end of '" & name & "'", + p.tok[afterIdx].line, p.tok[afterIdx].col) + elif p.tok[afterIdx].kind == tkPunct and p.tok[afterIdx].symbol[0] == ':': + rstMessage(p, mwRstStyle, + "too many colons for a directive (should be ::)", + p.tok[afterIdx].line, p.tok[afterIdx].col) proc parseComment(p: var RstParser): PRstNode = case currentTok(p).kind @@ -1711,7 +1752,8 @@ proc whichSection(p: RstParser): RstNodeKind = return rnCodeBlock elif currentTok(p).symbol == "::": return rnLiteralBlock - elif currentTok(p).symbol == ".." and predNL(p): + elif currentTok(p).symbol == ".." and predNL(p) and + nextTok(p).kind in {tkWhite, tkIndent}: return rnDirective case currentTok(p).kind of tkAdornment: diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 55a20270d..3866ebd24 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -42,7 +42,7 @@ ## ## Code to read some data from a socket may look something like this: ## -## .. code-block::nim +## .. code-block:: Nim ## var future = socket.recv(100) ## future.addCallback( ## proc () = diff --git a/lib/pure/asyncftpclient.nim b/lib/pure/asyncftpclient.nim index 0ee45785d..056d65564 100644 --- a/lib/pure/asyncftpclient.nim +++ b/lib/pure/asyncftpclient.nim @@ -21,7 +21,7 @@ ## In order to begin any sort of transfer of files you must first ## connect to an FTP server. You can do so with the `connect` procedure. ## -## .. code-block::nim +## .. code-block:: Nim ## import std/[asyncdispatch, asyncftpclient] ## proc main() {.async.} = ## var ftp = newAsyncFtpClient("example.com", user = "test", pass = "test") @@ -41,7 +41,7 @@ ## working directory before you do so with the `pwd` procedure, you can also ## instead specify an absolute path. ## -## .. code-block::nim +## .. code-block:: Nim ## import std/[asyncdispatch, asyncftpclient] ## proc main() {.async.} = ## var ftp = newAsyncFtpClient("example.com", user = "test", pass = "test") @@ -62,7 +62,7 @@ ## Procs that take an `onProgressChanged` callback will call this every ## `progressInterval` milliseconds. ## -## .. code-block::nim +## .. code-block:: Nim ## import std/[asyncdispatch, asyncftpclient] ## ## proc onProgressChanged(total, progress: BiggestInt, diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index 3982edc54..fd0cad5ca 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -108,7 +108,7 @@ proc respond*(req: Request, code: HttpCode, content: string, ## ## Example: ## - ## .. code-block::nim + ## .. code-block:: Nim ## import std/json ## proc handler(req: Request) {.async.} = ## if req.url.path == "/hello-world": diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index 7aeb99777..7ef40a128 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -65,7 +65,7 @@ ## ## The following example demonstrates a simple chat server. ## -## .. code-block::nim +## .. code-block:: Nim ## ## import std/[asyncnet, asyncdispatch] ## diff --git a/lib/pure/base64.nim b/lib/pure/base64.nim index bf196b54d..d5c1fda7a 100644 --- a/lib/pure/base64.nim +++ b/lib/pure/base64.nim @@ -23,14 +23,14 @@ ## Encoding data ## ------------- ## -## .. code-block::nim +## .. code-block:: Nim ## import std/base64 ## let encoded = encode("Hello World") ## assert encoded == "SGVsbG8gV29ybGQ=" ## ## Apart from strings you can also encode lists of integers or characters: ## -## .. code-block::nim +## .. code-block:: Nim ## import std/base64 ## let encodedInts = encode([1,2,3]) ## assert encodedInts == "AQID" @@ -41,7 +41,7 @@ ## Decoding data ## ------------- ## -## .. code-block::nim +## .. code-block:: Nim ## import std/base64 ## let decoded = decode("SGVsbG8gV29ybGQ=") ## assert decoded == "Hello World" @@ -49,7 +49,7 @@ ## URL Safe Base64 ## --------------- ## -## .. code-block::nim +## .. code-block:: Nim ## import std/base64 ## doAssert encode("c\xf7>", safe = true) == "Y_c-" ## doAssert encode("c\xf7>", safe = false) == "Y/c+" diff --git a/lib/pure/dynlib.nim b/lib/pure/dynlib.nim index 3e1d84729..a1a94072c 100644 --- a/lib/pure/dynlib.nim +++ b/lib/pure/dynlib.nim @@ -22,7 +22,7 @@ ## If the library fails to load or the function 'greet' is not found, ## it quits with a failure error code. ## -## .. code-block::nim +## .. code-block:: Nim ## ## import std/dynlib ## diff --git a/lib/system/iterators.nim b/lib/system/iterators.nim index 86d64d0c7..0f79970b8 100644 --- a/lib/system/iterators.nim +++ b/lib/system/iterators.nim @@ -304,7 +304,7 @@ iterator fieldPairs*[T: tuple|object](x: T): tuple[key: string, val: RootObj] {. ## picking the appropriate code to a secondary proc which you overload for ## each field type and pass the `value` to. ## - ## .. warning::: This really transforms the 'for' and unrolls the loop. The + ## .. warning:: This really transforms the 'for' and unrolls the loop. The ## current implementation also has a bug that affects symbol binding in the ## loop body. runnableExamples: diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim index 99b8d8db5..b403d96c6 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -136,6 +136,25 @@ suite "YAML syntax highlighting": not numbers: [ 42e, 0023, +32.37, 8 ball] }
    """ + test "Directives: warnings": + let input = dedent""" + .. non-existant-warning: Paragraph. + + .. another.wrong:warning::: Paragraph. + """ + var warnings = new seq[string] + let output = input.toHtml(warnings=warnings) + check output == "" + doAssert warnings[].len == 2 + check "(1, 24) Warning: RST style:" in warnings[0] + check "double colon :: may be missing at end of 'non-existant-warning'" in warnings[0] + check "(3, 25) Warning: RST style:" in warnings[1] + check "RST style: too many colons for a directive (should be ::)" in warnings[1] + + test "not a directive": + let input = "..warning:: I am not a warning." + check input.toHtml == input + test "Anchors, Aliases, Tags": let input = """.. code-block:: yaml --- !!map @@ -1403,6 +1422,23 @@ Test1 check """`3`:sup:\ He is an isotope of helium.""".toHtml == expected check """`3`:superscript:\ He is an isotope of helium.""".toHtml == expected + test "Roles: warnings": + let input = dedent""" + See function :py:func:`spam`. + + See also `egg`:py:class:. + """ + var warnings = new seq[string] + let output = input.toHtml(warnings=warnings) + doAssert warnings[].len == 2 + check "(1, 14) Warning: " in warnings[0] + check "language 'py:func' not supported" in warnings[0] + check "(3, 15) Warning: " in warnings[1] + check "language 'py:class' not supported" in warnings[1] + check("""

    See function spam.

    """ & "\n" & + """

    See also egg.

    """ & "\n" == + output) + test "(not) Roles: check escaping 1": let expected = """See :subscript:""" & """""" & id"some" & " " & id"text" & From 9f0f4775524d6167655e089a864cd9fdbd10c806 Mon Sep 17 00:00:00 2001 From: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Date: Thu, 8 Apr 2021 19:41:43 +0200 Subject: [PATCH 1897/3768] Update `sysrand` documentation (#17676) Co-authored-by: Andreas Rumpf --- doc/lib.rst | 8 ++++---- lib/std/sysrand.nim | 32 ++++++++++++++++---------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/doc/lib.rst b/doc/lib.rst index 15b4f70d6..f09fe16b2 100644 --- a/doc/lib.rst +++ b/doc/lib.rst @@ -272,18 +272,18 @@ Math libraries * `random `_ Fast and tiny random number generator. -* `sysrand `_ - Cryptographically secure pseudorandom number generator. - * `rationals `_ This module implements rational numbers and relevant mathematical operations. * `stats `_ - Statistical analysis + Statistical analysis. * `sums `_ Accurate summation functions. +* `sysrand `_ + Cryptographically secure pseudorandom number generator. + Internet Protocols and Support ------------------------------ diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index c9908143c..4a2cdd10b 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -12,21 +12,21 @@ ## therefore may not be secure. ## ## `std/sysrand` generates random numbers from a secure source provided by the operating system. -## It is also called Cryptographically secure pseudorandom number generator. -## It should be unpredictable enough for cryptographic applications, +## It is a cryptographically secure pseudorandom number generator +## and should be unpredictable enough for cryptographic applications, ## though its exact quality depends on the OS implementation. ## -## | Targets | Implementation| -## | :--- | ----: | -## | Windows | `BCryptGenRandom`_ | -## | Linux | `getrandom`_ | -## | MacOSX | `getentropy`_ | -## | IOS | `SecRandomCopyBytes`_ | -## | OpenBSD | `getentropy openbsd`_ | -## | FreeBSD | `getrandom freebsd`_ | -## | JS(Web Browser) | `getRandomValues`_ | -## | Nodejs | `randomFillSync`_ | -## | Other Unix platforms | `/dev/urandom`_ | +## | Targets | Implementation | +## | :--- | ----: | +## | Windows | `BCryptGenRandom`_ | +## | Linux | `getrandom`_ | +## | MacOSX | `getentropy`_ | +## | iOS | `SecRandomCopyBytes`_ | +## | OpenBSD | `getentropy openbsd`_ | +## | FreeBSD | `getrandom freebsd`_ | +## | JS (Web Browser) | `getRandomValues`_ | +## | Node.js | `randomFillSync`_ | +## | Other Unix platforms | `/dev/urandom`_ | ## ## .. _BCryptGenRandom: https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom ## .. _getrandom: https://man7.org/linux/man-pages/man2/getrandom.2.html @@ -195,7 +195,7 @@ elif defined(linux): elif defined(openbsd): proc getentropy(p: pointer, size: cint): cint {.importc: "getentropy", header: "".} - # fills a buffer with high-quality entropy, + # Fills a buffer with high-quality entropy, # which can be used as input for process-context pseudorandom generators like `arc4random`. # The maximum buffer size permitted is 256 bytes. @@ -227,7 +227,7 @@ elif defined(ios): proc secRandomCopyBytes( rnd: SecRandomRef, count: csize_t, bytes: pointer - ): cint {.importc: "SecRandomCopyBytes", header: "".} + ): cint {.importc: "SecRandomCopyBytes", header: "".} ## https://developer.apple.com/documentation/security/1399291-secrandomcopybytes template urandomImpl(result: var int, dest: var openArray[byte]) = @@ -292,7 +292,7 @@ proc urandom*(dest: var openArray[byte]): bool = ## If the call succeeds, returns `true`. ## ## If `dest` is empty, `urandom` immediately returns success, - ## without calling underlying operating system api. + ## without calling the underlying operating system API. ## ## .. warning:: The code hasn't been audited by cryptography experts and ## is provided as-is without guarantees. Use at your own risks. For production From 6ec55ebf48d2b3889cd1391fdd630e65a01c0c91 Mon Sep 17 00:00:00 2001 From: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Date: Thu, 8 Apr 2021 20:29:11 +0200 Subject: [PATCH 1898/3768] Fix rst typo (#17671) --- lib/core/typeinfo.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/typeinfo.nim b/lib/core/typeinfo.nim index 196ac8fe1..08d567351 100644 --- a/lib/core/typeinfo.nim +++ b/lib/core/typeinfo.nim @@ -11,7 +11,7 @@ ## (`RTTI`:idx:). See the `marshal `_ module for an example of ## what this allows you to do. ## -## ..note:: Even though `Any` and its operations hide the nasty low level +## .. note:: Even though `Any` and its operations hide the nasty low level ## details from its users, it remains inherently unsafe! Also, Nim's ## runtime type information will evolve and may eventually be deprecated. ## As an alternative approach to programmatically understanding and From 2ac45a4794c9d4cc99ef3b154102fd185d35714e Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 9 Apr 2021 00:18:07 +0200 Subject: [PATCH 1899/3768] undocument unsupported advanced options (#17678) --- doc/advopt.txt | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/doc/advopt.txt b/doc/advopt.txt index 763ac80a6..4842d090e 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -43,7 +43,7 @@ Advanced options: --warning[X]:on|off turn specific warning X on|off --hints:on|off|list turn all hints on|off or list all available --hint[X]:on|off turn specific hint X on|off - --warningAsError[X]:on|off + --warningAsError[X]:on|off turn specific warning X into an error on|off --hintAsError[X]:on|off turn specific hint X into an error on|off --styleCheck:off|hint|error @@ -84,7 +84,7 @@ Advanced options: if path == @default (the default and most useful), will use best match among @pkg,@path. if these are nonexistent, will use project path - -b, --backend:c|cpp|js|objc + -b, --backend:c|cpp|js|objc sets backend to use with commands like `nim doc` or `nim r` --docCmd:cmd if `cmd == skip`, skips runnableExamples else, runs runnableExamples with given options, e.g.: @@ -95,26 +95,22 @@ Advanced options: --lineDir:on|off generation of #line directive on|off --embedsrc:on|off embeds the original source code as comments in the generated output - --threadanalysis:on|off turn thread analysis on|off --tlsEmulation:on|off turn thread local storage emulation on|off --implicitStatic:on|off turn implicit compile time evaluation on|off --trmacros:on|off turn term rewriting macros on|off --multimethods:on|off turn multi-methods on|off - --memTracker:on|off turn memory tracker on|off --hotCodeReloading:on|off turn support for hot code reloading on|off --excessiveStackTrace:on|off stack traces use full file paths --stackTraceMsgs:on|off enable user defined stack frame msgs via `setFrameMsg` - --seqsv2:on|off use the new string/seq implementation based on - destructors --skipCfg:on|off do not read the nim installation's configuration file --skipUserCfg:on|off do not read the user's configuration file --skipParentCfg:on|off do not read the parent dirs' configuration files --skipProjCfg:on|off do not read the project's configuration file --gc:refc|arc|orc|markAndSweep|boehm|go|none|regions select the GC to use; default is 'refc' - --exceptions:setjmp|cpp|goto + --exceptions:setjmp|cpp|goto|quirky select the exception handling implementation --index:on|off turn index file generation on|off --putenv:key=value set an environment variable @@ -149,11 +145,8 @@ Advanced options: --legacy:$2 enable obsolete/legacy language feature --useVersion:1.0|1.2 emulate Nim version X of the Nim compiler, for testing - --profiler:on|off enable profiling; requires `import nimprof`, and - works better with `--stackTrace:on` - see also https://nim-lang.github.io/Nim/estp.html - --benchmarkVM:on|off enable benchmarking of VM code with cpuTime() - --profileVM:on|off enable compile time VM profiler - --sinkInference:on|off en-/disable sink parameter inference (default: on) + --benchmarkVM:on|off turn benchmarking of VM code with cpuTime() on|off + --profileVM:on|off turn compile time VM profiler on|off + --sinkInference:on|off turn sink parameter inference on|off (default: on) --panics:on|off turn panics into process terminations (default: off) --deepcopy:on|off enable 'system.deepCopy' for ``--gc:arc|orc`` From e4b64eee894ddbf41935d3b8aeed1448cd2b5e46 Mon Sep 17 00:00:00 2001 From: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Date: Fri, 9 Apr 2021 00:40:19 +0200 Subject: [PATCH 1900/3768] Fix small typos (#17680) --- doc/intern.rst | 2 +- lib/js/jsconsole.nim | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/intern.rst b/doc/intern.rst index 1e18d975c..3b1a48b41 100644 --- a/doc/intern.rst +++ b/doc/intern.rst @@ -113,7 +113,7 @@ You can import them directly for debugging: The compiler's architecture =========================== -Nim uses the classic compiler architecture: A lexer/scanner feds tokens to a +Nim uses the classic compiler architecture: A lexer/scanner feeds tokens to a parser. The parser builds a syntax tree that is used by the code generators. This syntax tree is the interface between the parser and the code generator. It is essential to understand most of the compiler's code. diff --git a/lib/js/jsconsole.nim b/lib/js/jsconsole.nim index bf43adddd..e74127334 100644 --- a/lib/js/jsconsole.nim +++ b/lib/js/jsconsole.nim @@ -118,6 +118,7 @@ since (1, 5): func timeStamp*(console: Console; label: cstring) {.importcpp.} ## https://developer.mozilla.org/en-US/docs/Web/API/Console/timeStamp + ## ## ..warning:: non-standard From 13b958eb45a3569acaa18f9381d28b8f37d42057 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 9 Apr 2021 06:59:25 +0200 Subject: [PATCH 1901/3768] IC: added basic test case for methods (#17679) * IC: added basic test case for methods * IC: better methods test --- compiler/ic/cbackend.nim | 35 ++++++++++++++++++++++++++++------- compiler/ic/ic.nim | 2 +- tests/ic/mbaseobj.nim | 7 +++++++ tests/ic/tmethods.nim | 28 ++++++++++++++++++++++++++++ 4 files changed, 64 insertions(+), 8 deletions(-) create mode 100644 tests/ic/mbaseobj.nim create mode 100644 tests/ic/tmethods.nim diff --git a/compiler/ic/cbackend.nim b/compiler/ic/cbackend.nim index 9282ac466..2f3910b32 100644 --- a/compiler/ic/cbackend.nim +++ b/compiler/ic/cbackend.nim @@ -30,12 +30,16 @@ proc unpackTree(g: ModuleGraph; thisModule: int; var decoder = initPackedDecoder(g.config, g.cache) result = loadNodes(decoder, g.packed, thisModule, tree, n) -proc generateCodeForModule(g: ModuleGraph; m: var LoadedModule; alive: var AliveSyms) = +proc setupBackendModule(g: ModuleGraph; m: var LoadedModule) = if g.backend == nil: g.backend = cgendata.newModuleList(g) - + assert g.backend != nil var bmod = cgen.newModule(BModuleList(g.backend), m.module, g.config) bmod.idgen = idgenFromLoadedModule(m) + +proc generateCodeForModule(g: ModuleGraph; m: var LoadedModule; alive: var AliveSyms) = + var bmod = BModuleList(g.backend).modules[m.module.position] + assert bmod != nil bmod.flags.incl useAliveDataFromDce bmod.alive = move alive[m.module.position] @@ -118,6 +122,27 @@ proc generateCode*(g: ModuleGraph) = for i in 0..high(g.packed): echo i, " is of status ", g.packed[i].status, " ", toFullPath(g.config, FileIndex(i)) + # First pass: Setup all the backend modules for all the modules that have + # changed: + for i in 0..high(g.packed): + # case statement here to enforce exhaustive checks. + case g.packed[i].status + of undefined: + discard "nothing to do" + of loading, stored: + assert false + of storing, outdated: + setupBackendModule(g, g.packed[i]) + of loaded: + # Even though this module didn't change, DCE might trigger a change. + # Consider this case: Module A uses symbol S from B and B does not use + # S itself. A is then edited not to use S either. Thus we have to + # recompile B in order to remove S from the final result. + if aliveSymsChanged(g.config, g.packed[i].module.position, alive): + g.packed[i].loadedButAliveSetChanged = true + setupBackendModule(g, g.packed[i]) + + # Second pass: Code generation. for i in 0..high(g.packed): # case statement here to enforce exhaustive checks. case g.packed[i].status @@ -130,11 +155,7 @@ proc generateCode*(g: ModuleGraph) = closeRodFile(g, g.packed[i].module) storeAliveSyms(g.config, g.packed[i].module.position, alive) of loaded: - # Even though this module didn't change, DCE might trigger a change. - # Consider this case: Module A uses symbol S from B and B does not use - # S itself. A is then edited not to use S either. Thus we have to - # recompile B in order to remove S from the final result. - if aliveSymsChanged(g.config, g.packed[i].module.position, alive): + if g.packed[i].loadedButAliveSetChanged: generateCodeForModule(g, g.packed[i], alive) else: addFileToLink(g.config, g.packed[i].module) diff --git a/compiler/ic/ic.nim b/compiler/ic/ic.nim index b5fed7d4e..46bb63e07 100644 --- a/compiler/ic/ic.nim +++ b/compiler/ic/ic.nim @@ -666,7 +666,7 @@ type LoadedModule* = object status*: ModuleStatus - symsInit, typesInit: bool + symsInit, typesInit, loadedButAliveSetChanged*: bool fromDisk*: PackedModule syms: seq[PSym] # indexed by itemId types: seq[PType] diff --git a/tests/ic/mbaseobj.nim b/tests/ic/mbaseobj.nim new file mode 100644 index 000000000..0f4e4a90d --- /dev/null +++ b/tests/ic/mbaseobj.nim @@ -0,0 +1,7 @@ + +type + Base* = ref object of RootObj + s*: string + +method m*(b: Base) {.base.} = + echo "Base ", b.s diff --git a/tests/ic/tmethods.nim b/tests/ic/tmethods.nim new file mode 100644 index 000000000..251f26889 --- /dev/null +++ b/tests/ic/tmethods.nim @@ -0,0 +1,28 @@ +discard """ + output: '''Base abc''' +""" + +import mbaseobj + +var c = Base(s: "abc") +m c + +#!EDIT!# + +discard """ + output: '''Base abc +Inherited abc''' +""" + +import mbaseobj + +type + Inherited = ref object of Base + +method m(i: Inherited) = + procCall m(Base i) + echo "Inherited ", i.s + +var c = Inherited(s: "abc") +m c + From 826ad2f36f34694277f624933049de2be40e0703 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 9 Apr 2021 00:09:04 -0500 Subject: [PATCH 1902/3768] nep1: guidelines for getters and setters (#17645) * nep1: guidelines for getters and setters * address a comment --- doc/nep1.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/doc/nep1.rst b/doc/nep1.rst index bdf8e0eab..8687725bb 100644 --- a/doc/nep1.rst +++ b/doc/nep1.rst @@ -301,3 +301,12 @@ Miscellaneous let a = """foo bar """ + +- A getter API for a private field `foo` should preferably be named `foo`, not `getFoo`. + A getter-like API should preferably be named `getFoo`, not `foo` if: + * the API has side effects + * or the cost is not `O(1)` + For in between cases, there is no clear guideline. + +- Likewise with a setter API, replacing `foo` with `foo=` and `getFoo` with `setFoo` + in the above text. From cce1b24b1cc3860ff503668761d2798b1b1d1ef0 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 9 Apr 2021 01:00:13 -0500 Subject: [PATCH 1903/3768] ast: add getPIdent (#17684) * ast: add getPIdent * fixup --- compiler/ast.nim | 8 ++++++++ compiler/renderer.nim | 20 +++++++++----------- compiler/reorder.nim | 7 ++----- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 7d13956e9..fffe08cb7 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1054,6 +1054,14 @@ const defaultAlignment = -1 defaultOffset = -1 +proc getPIdent*(a: PNode): PIdent {.inline.} = + ## Returns underlying `PIdent` for `{nkSym, nkIdent}`, or `nil`. + # xxx consider whether also returning the 1st ident for {nkOpenSymChoice, nkClosedSymChoice} + # which may simplify code. + case a.kind + of nkSym: a.sym.name + of nkIdent: a.ident + else: nil proc getnimblePkg*(a: PSym): PSym = result = a diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 9a867b0a1..78e2fe20e 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -964,14 +964,12 @@ proc skipHiddenNodes(n: PNode): PNode = else: break proc accentedName(g: var TSrcGen, n: PNode) = + # This is for cases where ident should've really been a `nkAccQuoted`, e.g. `:tmp` + # or if user writes a macro with `ident":foo"`. It's unclear whether these should be legal. const backticksNeeded = OpChars + {'[', '{', '\''} if n == nil: return - let isOperator = - if n.kind == nkIdent and n.ident.s.len > 0 and n.ident.s[0] in backticksNeeded: true - elif n.kind == nkSym and n.sym.name.s.len > 0 and n.sym.name.s[0] in backticksNeeded: true - else: false - - if isOperator: + let ident = n.getPIdent + if ident != nil and ident.s[0] in backticksNeeded: put(g, tkAccent, "`") gident(g, n) put(g, tkAccent, "`") @@ -999,9 +997,9 @@ proc infixArgument(g: var TSrcGen, n: PNode, i: int) = put(g, tkParRi, ")") proc isCustomLit(n: PNode): bool = - n.len == 2 and n[0].kind == nkRStrLit and - (n[1].kind == nkIdent and n[1].ident.s.startsWith('\'')) or - (n[1].kind == nkSym and n[1].sym.name.s.startsWith('\'')) + if n.len == 2 and n[0].kind == nkRStrLit: + let ident = n[1].getPIdent + result = ident != nil and ident.s.startsWith('\'') proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = if isNil(n): return @@ -1234,8 +1232,8 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = else: gsub(g, n, 0) put(g, tkDot, ".") - if n.len > 1: - accentedName(g, n[1]) + assert n.len == 2, $n.len + accentedName(g, n[1]) of nkBind: putWithSpace(g, tkBind, "bind") gsub(g, n, 0) diff --git a/compiler/reorder.nim b/compiler/reorder.nim index 4ffe4ccf8..65c1fb9ee 100644 --- a/compiler/reorder.nim +++ b/compiler/reorder.nim @@ -36,11 +36,8 @@ proc newDepN(id: int, pnode: PNode): DepN = proc accQuoted(cache: IdentCache; n: PNode): PIdent = var id = "" for i in 0.. Date: Fri, 9 Apr 2021 02:37:10 -0500 Subject: [PATCH 1904/3768] make repr handle setters `foo=` (#17683) --- compiler/renderer.nim | 13 ++++++++----- tests/stdlib/trepr.nim | 9 +++++++++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 78e2fe20e..b3b0adc01 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -1327,13 +1327,16 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = of nkIdent: n.ident.s of nkSym: n.sym.name.s else: "" + proc isAlpha(n: PNode): bool = + if n.kind in {nkIdent, nkSym}: + let tmp = n.getStrVal + result = tmp.len > 0 and tmp[0] in {'a'..'z', 'A'..'Z'} var useSpace = false if i == 1 and n[0].kind == nkIdent and n[0].ident.s in ["=", "'"]: - let tmp = n[1].getStrVal - if tmp.len > 0 and tmp[0] in {'a'..'z', 'A'..'Z'}: - # handle `=destroy`, `'big' - discard - else: + if not n[1].isAlpha: # handle `=destroy`, `'big' + useSpace = true + elif i == 1 and n[1].kind == nkIdent and n[1].ident.s == "=": + if not n[0].isAlpha: # handle setters, e.g. `foo=` useSpace = true elif i > 0: useSpace = true if useSpace: put(g, tkSpaces, Space) diff --git a/tests/stdlib/trepr.nim b/tests/stdlib/trepr.nim index 3dcbe9b69..f72bbb34e 100644 --- a/tests/stdlib/trepr.nim +++ b/tests/stdlib/trepr.nim @@ -163,6 +163,15 @@ proc `foo bar baz`(): int = """ doAssert a2 == a + block: # setters: `foo=` + let a = deb: + proc `foo=`() = discard + doAssert a == """ + +proc `foo=`() = + discard +""" + block: # bug #14850 block: let a = deb: From f25243140baa00db4a663d453a262e41f8d9073f Mon Sep 17 00:00:00 2001 From: flywind Date: Fri, 9 Apr 2021 17:51:51 +0800 Subject: [PATCH 1905/3768] add std/tasks (#17447) --- changelog.md | 2 + lib/std/tasks.nim | 272 +++++++++++++++++++++ tests/stdlib/ttasks.nim | 506 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 780 insertions(+) create mode 100644 lib/std/tasks.nim create mode 100644 tests/stdlib/ttasks.nim diff --git a/changelog.md b/changelog.md index 43a15e398..a2879833f 100644 --- a/changelog.md +++ b/changelog.md @@ -264,6 +264,8 @@ - Added dollar `$` and `len` for `jsre.RegExp`. +- Added `std/tasks`. + - Added `hasDataBuffered` to `asyncnet`. - Added `hasClosure` to `std/typetraits`. diff --git a/lib/std/tasks.nim b/lib/std/tasks.nim new file mode 100644 index 000000000..6b7f86ce4 --- /dev/null +++ b/lib/std/tasks.nim @@ -0,0 +1,272 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2021 Nim contributors +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module provides basic primitives for creating parallel programs. +## A `Task` should be only owned by a single Thread, it cannot be shared by threads. + +import std/[macros, isolation, typetraits] +import system/ansi_c + +export isolation + + +when compileOption("threads"): + from std/effecttraits import isGcSafe + + +# +# proc hello(a: int, b: string) = +# echo $a & b +# +# let literal = "Nim" +# let t = toTask(hello(521, literal)) +# +# +# is roughly converted to +# +# type +# ScratchObj_369098780 = object +# a: int +# b: string +# +# let scratch_369098762 = cast[ptr ScratchObj_369098780](c_calloc(csize_t 1, +# csize_t sizeof(ScratchObj_369098780))) +# if scratch_369098762.isNil: +# raise newException(OutOfMemDefect, "Could not allocate memory") +# block: +# var isolate_369098776 = isolate(521) +# scratch_369098762.a = extract(isolate_369098776) +# var isolate_369098778 = isolate(literal) +# scratch_369098762.b = extract(isolate_369098778) +# proc hello_369098781(args`gensym3: pointer) {.nimcall.} = +# let objTemp_369098775 = cast[ptr ScratchObj_369098780](args`gensym3) +# let :tmp_369098777 = objTemp_369098775.a +# let :tmp_369098779 = objTemp_369098775.b +# hello(a = :tmp_369098777, b = :tmp_369098779) +# +# proc destroyScratch_369098782(args`gensym3: pointer) {.nimcall.} = +# let obj_369098783 = cast[ptr ScratchObj_369098780](args`gensym3) +# =destroy(obj_369098783[]) +# let t = Task(callback: hello_369098781, args: scratch_369098762, destroy: destroyScratch_369098782) +# + + +type + Task* = object ## `Task` contains the callback and its arguments. + callback: proc (args: pointer) {.nimcall.} + args: pointer + destroy: proc (args: pointer) {.nimcall.} + + +proc `=copy`*(x: var Task, y: Task) {.error.} + +proc `=destroy`*(t: var Task) {.inline.} = + ## Frees the resources allocated for a `Task`. + if t.args != nil: + if t.destroy != nil: + t.destroy(t.args) + c_free(t.args) + +proc invoke*(task: Task) {.inline.} = + ## Invokes the `task`. + task.callback(task.args) + +template checkIsolate(scratchAssignList: seq[NimNode], procParam, scratchDotExpr: NimNode) = + # block: + # var isoTempA = isolate(521) + # scratch.a = extract(isolateA) + # var isoTempB = isolate(literal) + # scratch.b = extract(isolateB) + let isolatedTemp = genSym(nskTemp, "isoTemp") + scratchAssignList.add newVarStmt(isolatedTemp, newCall(newidentNode("isolate"), procParam)) + scratchAssignList.add newAssignment(scratchDotExpr, + newcall(newIdentNode("extract"), isolatedTemp)) + +template addAllNode(assignParam: NimNode, procParam: NimNode) = + let scratchDotExpr = newDotExpr(scratchIdent, formalParams[i][0]) + + checkIsolate(scratchAssignList, procParam, scratchDotExpr) + + let tempNode = genSym(kind = nskTemp, ident = formalParams[i][0].strVal) + callNode.add nnkExprEqExpr.newTree(formalParams[i][0], tempNode) + tempAssignList.add newLetStmt(tempNode, newDotExpr(objTemp, formalParams[i][0])) + scratchRecList.add newIdentDefs(newIdentNode(formalParams[i][0].strVal), assignParam) + +macro toTask*(e: typed{nkCall | nkInfix | nkPrefix | nkPostfix | nkCommand | nkCallStrLit}): Task = + ## Converts the call and its arguments to `Task`. + runnableExamples("--gc:orc"): + proc hello(a: int) = echo a + + let b = toTask hello(13) + assert b is Task + + doAssert getTypeInst(e).typeKind == ntyVoid + + when compileOption("threads"): + if not isGcSafe(e[0]): + error("'toTask' takes a GC safe call expression") + + if hasClosure(e[0]): + error("closure call is not allowed") + + if e.len > 1: + let scratchIdent = genSym(kind = nskTemp, ident = "scratch") + let impl = e[0].getTypeInst + + when defined(nimTasksDebug): + echo impl.treeRepr + echo e.treeRepr + let formalParams = impl[0] + + var + scratchRecList = newNimNode(nnkRecList) + scratchAssignList: seq[NimNode] + tempAssignList: seq[NimNode] + callNode: seq[NimNode] + + let + objTemp = genSym(nskTemp, ident = "objTemp") + + for i in 1 ..< formalParams.len: + var param = formalParams[i][1] + + if param.kind == nnkBracketExpr and param[0].eqIdent("sink"): + param = param[0] + + if param.typeKind in {ntyExpr, ntyStmt}: + error("'toTask'ed function cannot have a 'typed' or 'untyped' parameter") + + case param.kind + of nnkVarTy: + error("'toTask'ed function cannot have a 'var' parameter") + of nnkBracketExpr: + if param[0].typeKind == ntyTypeDesc: + callNode.add nnkExprEqExpr.newTree(formalParams[i][0], e[i]) + elif param[0].typeKind in {ntyVarargs, ntyOpenArray}: + if param[1].typeKind in {ntyExpr, ntyStmt}: + error("'toTask'ed function cannot have a 'typed' or 'untyped' parameter") + let + seqType = nnkBracketExpr.newTree(newIdentNode("seq"), param[1]) + seqCallNode = newcall("@", e[i]) + addAllNode(seqType, seqCallNode) + else: + addAllNode(param, e[i]) + of nnkBracket, nnkObjConstr: + # passing by static parameters + # so we pass them directly instead of passing by scratchObj + callNode.add nnkExprEqExpr.newTree(formalParams[i][0], e[i]) + of nnkSym, nnkPtrTy: + addAllNode(param, e[i]) + of nnkCharLit..nnkNilLit: + callNode.add nnkExprEqExpr.newTree(formalParams[i][0], e[i]) + else: + error("not supported type kinds") + + let scratchObjType = genSym(kind = nskType, ident = "ScratchObj") + let scratchObj = nnkTypeSection.newTree( + nnkTypeDef.newTree( + scratchObjType, + newEmptyNode(), + nnkObjectTy.newTree( + newEmptyNode(), + newEmptyNode(), + scratchRecList + ) + ) + ) + + + let scratchObjPtrType = quote do: + cast[ptr `scratchObjType`](c_calloc(csize_t 1, csize_t sizeof(`scratchObjType`))) + + let scratchLetSection = newLetStmt( + scratchIdent, + scratchObjPtrType + ) + + let scratchCheck = quote do: + if `scratchIdent`.isNil: + raise newException(OutOfMemDefect, "Could not allocate memory") + + var stmtList = newStmtList() + stmtList.add(scratchObj) + stmtList.add(scratchLetSection) + stmtList.add(scratchCheck) + stmtList.add(nnkBlockStmt.newTree(newEmptyNode(), newStmtList(scratchAssignList))) + + var functionStmtList = newStmtList() + let funcCall = newCall(e[0], callNode) + functionStmtList.add tempAssignList + functionStmtList.add funcCall + + let funcName = genSym(nskProc, e[0].strVal) + let destroyName = genSym(nskProc, "destroyScratch") + let objTemp2 = genSym(ident = "obj") + let tempNode = quote("@") do: + `=destroy`(@objTemp2[]) + + result = quote do: + `stmtList` + + proc `funcName`(args: pointer) {.nimcall.} = + let `objTemp` = cast[ptr `scratchObjType`](args) + `functionStmtList` + + proc `destroyName`(args: pointer) {.nimcall.} = + let `objTemp2` = cast[ptr `scratchObjType`](args) + `tempNode` + + Task(callback: `funcName`, args: `scratchIdent`, destroy: `destroyName`) + else: + let funcCall = newCall(e[0]) + let funcName = genSym(nskProc, e[0].strVal) + + result = quote do: + proc `funcName`(args: pointer) {.nimcall.} = + `funcCall` + + Task(callback: `funcName`, args: nil) + + when defined(nimTasksDebug): + echo result.repr + +runnableExamples("--gc:orc"): + block: + var num = 0 + proc hello(a: int) = inc num, a + + let b = toTask hello(13) + b.invoke() + assert num == 13 + # A task can be invoked multiple times + b.invoke() + assert num == 26 + + block: + type + Runnable = ref object + data: int + + var data: int + proc hello(a: Runnable) {.nimcall.} = + a.data += 2 + data = a.data + + + when false: + # the parameters of call must be isolated. + let x = Runnable(data: 12) + let b = toTask hello(x) # error ----> expression cannot be isolated: x + b.invoke() + + let b = toTask(hello(Runnable(data: 12))) + b.invoke() + assert data == 14 + b.invoke() + assert data == 16 diff --git a/tests/stdlib/ttasks.nim b/tests/stdlib/ttasks.nim new file mode 100644 index 000000000..75fed9f9b --- /dev/null +++ b/tests/stdlib/ttasks.nim @@ -0,0 +1,506 @@ +discard """ + targets: "c cpp" + matrix: "--gc:orc" +""" + +import std/[tasks, strformat] + +block: + var s = "" + proc `+`(x: int, y: string) = + s.add $x & y + + let literal = "Nim" + let t = toTask(521 + literal) + t.invoke() + + doAssert s == "521Nim" + +block: + var s = "" + proc `!`(x: int) = + s.add $x + + let t = toTask !12 + t.invoke() + + doAssert s == "12" + + +block: + block: + var called = 0 + proc hello(x: static range[1 .. 5]) = + called += x + + let b = toTask hello(3) + b.invoke() + doAssert called == 3 + b.invoke() + doAssert called == 6 + + block: + var called = 0 + proc hello(x: range[1 .. 5]) = + called += x + + let b = toTask hello(3) + b.invoke() + doAssert called == 3 + b.invoke() + doAssert called == 6 + + block: + var called = 0 + proc hello(x: 1 .. 5) = + called += x + + let b = toTask hello(3) + b.invoke() + doAssert called == 3 + b.invoke() + doAssert called == 6 + + block: + var temp = "" + proc hello(a: int or seq[string]) = + when a is seq[string]: + for s in a: + temp.add s + else: + temp.addInt a + + let x = @["1", "2", "3", "4"] + let b = toTask hello(x) + b.invoke() + doAssert temp == "1234" + b.invoke() + doAssert temp == "12341234" + + + block: + var temp = "" + + proc hello(a: int or string) = + when a is string: + temp.add a + + let x = "!2" + + let b = toTask hello(x) + b.invoke() + doAssert temp == x + + block: + var temp = "" + proc hello(a: int or string) = + when a is string: + temp.add a + + let x = "!2" + let b = toTask hello(x) + b.invoke() + doAssert temp == x + + block: + var x = 0 + proc hello(typ: typedesc) = + x += typ(12) + + let b = toTask hello(int) + b.invoke() + doAssert x == 12 + + block: + var temp = "" + proc hello(a: int or seq[string]) = + when a is seq[string]: + for s in a: + temp.add s + + let x = @["1", "2", "3", "4"] + let b = toTask hello(x) + b.invoke() + doAssert temp == "1234" + + block: + var temp = "" + proc hello(a: int | string) = + when a is string: + temp.add a + + let x = "!2" + let b = toTask hello(x) + b.invoke() + doAssert temp == x + + block: + var x = 0 + proc hello(a: int | string) = + when a is int: + x = a + + let b = toTask hello(12) + b.invoke() + doAssert x == 12 + + block: + var a1: seq[int] + var a2 = 0 + proc hello(c: seq[int], a: int) = + a1 = c + a2 = a + + let x = 12 + var y = @[1, 3, 1, 4, 5, x, 1] + let b = toTask hello(y, 12) + b.invoke() + + doAssert a1 == y + doAssert a2 == x + + block: + var a1: seq[int] + var a2 = 0 + proc hello(c: seq[int], a: int) = + a1 = c + a2 = a + var x = 2 + let b = toTask hello(@[1, 3, 1, 4, 5, x, 1], 12) + b.invoke() + + doAssert a1 == @[1, 3, 1, 4, 5, x, 1] + doAssert a2 == 12 + + block: + var a1: array[7, int] + var a2 = 0 + proc hello(c: array[7, int], a: int) = + a1 = c + a2 = a + + let b = toTask hello([1, 3, 1, 4, 5, 2, 1], 12) + b.invoke() + + doAssert a1 == [1, 3, 1, 4, 5, 2, 1] + doAssert a2 == 12 + + block: + var a1: seq[int] + var a2 = 0 + proc hello(c: seq[int], a: int) = + a1 = c + a2 = a + + let b = toTask hello(@[1, 3, 1, 4, 5, 2, 1], 12) + b.invoke() + + doAssert a1 == @[1, 3, 1, 4, 5, 2, 1] + doAssert a2 == 12 + + block: + var a1: seq[int] + var a2 = 0 + proc hello(a: int, c: seq[int]) = + a1 = c + a2 = a + + let b = toTask hello(8, @[1, 3, 1, 4, 5, 2, 1]) + b.invoke() + + doAssert a1 == @[1, 3, 1, 4, 5, 2, 1] + doAssert a2 == 8 + + let c = toTask 8.hello(@[1, 3, 1, 4, 5, 2, 1]) + c.invoke() + + doAssert a1 == @[1, 3, 1, 4, 5, 2, 1] + doAssert a2 == 8 + + block: + var a1: seq[seq[int]] + var a2: int + proc hello(a: int, c: openArray[seq[int]]) = + a1 = @c + a2 = a + + let b = toTask hello(8, @[@[3], @[4], @[5], @[6], @[12], @[7]]) + b.invoke() + + doAssert a1 == @[@[3], @[4], @[5], @[6], @[12], @[7]] + doAssert a2 == 8 + + block: + var a1: seq[int] + var a2: int + proc hello(a: int, c: openArray[int]) = + a1 = @c + a2 = a + + let b = toTask hello(8, @[3, 4, 5, 6, 12, 7]) + b.invoke() + + doAssert a1 == @[3, 4, 5, 6, 12, 7] + doAssert a2 == 8 + + block: + var a1: seq[int] + var a2: int + proc hello(a: int, c: static varargs[int]) = + a1 = @c + a2 = a + + let b = toTask hello(8, @[3, 4, 5, 6, 12, 7]) + b.invoke() + + doAssert a1 == @[3, 4, 5, 6, 12, 7] + doAssert a2 == 8 + + block: + var a1: seq[int] + var a2: int + proc hello(a: int, c: static varargs[int]) = + a1 = @c + a2 = a + + let b = toTask hello(8, [3, 4, 5, 6, 12, 7]) + b.invoke() + + doAssert a1 == @[3, 4, 5, 6, 12, 7] + doAssert a2 == 8 + + block: + var a1: seq[int] + var a2: int + proc hello(a: int, c: varargs[int]) = + a1 = @c + a2 = a + + let x = 12 + let b = toTask hello(8, 3, 4, 5, 6, x, 7) + b.invoke() + + doAssert a1 == @[3, 4, 5, 6, 12, 7] + doAssert a2 == 8 + + block: + var x = 12 + + proc hello(x: ptr int) = + x[] += 12 + + let b = toTask hello(addr x) + b.invoke() + + doAssert x == 24 + + let c = toTask x.addr.hello + invoke(c) + + doAssert x == 36 + block: + type + Test = ref object + id: int + + var x = 0 + proc hello(a: int, c: static Test) = + x += a + x += c.id + + let b = toTask hello(8, Test(id: 12)) + b.invoke() + + doAssert x == 20 + + block: + type + Test = object + id: int + + var x = 0 + proc hello(a: int, c: static Test) = + x += a + x += c.id + + let b = toTask hello(8, Test(id: 12)) + b.invoke() + doAssert x == 20 + + block: + var x = 0 + proc hello(a: int, c: static seq[int]) = + x += a + for i in c: + x += i + + let b = toTask hello(8, @[3, 4, 5, 6, 12, 7]) + b.invoke() + doAssert x == 45 + + block: + var x = 0 + proc hello(a: int, c: static array[5, int]) = + x += a + for i in c: + x += i + + let b = toTask hello(8, [3, 4, 5, 6, 12]) + b.invoke() + doAssert x == 38 + + block: + var aVal = 0 + var cVal = "" + + proc hello(a: int, c: static string) = + aVal += a + cVal.add c + + var x = 1314 + let b = toTask hello(x, "hello") + b.invoke() + + doAssert aVal == x + doAssert cVal == "hello" + + block: + var aVal = "" + + proc hello(a: static string) = + aVal.add a + let b = toTask hello("hello") + b.invoke() + + doAssert aVal == "hello" + + block: + var aVal = 0 + var cVal = "" + + proc hello(a: static int, c: static string) = + aVal += a + cVal.add c + let b = toTask hello(8, "hello") + b.invoke() + + doAssert aVal == 8 + doAssert cVal == "hello" + + block: + var aVal = 0 + var cVal = 0 + + proc hello(a: static int, c: int) = + aVal += a + cVal += c + + let b = toTask hello(c = 0, a = 8) + b.invoke() + + doAssert aVal == 8 + doAssert cVal == 0 + + block: + var aVal = 0 + var cVal = 0 + + proc hello(a: int, c: static int) = + aVal += a + cVal += c + + let b = toTask hello(c = 0, a = 8) + b.invoke() + + doAssert aVal == 8 + doAssert cVal == 0 + + block: + var aVal = 0 + var cVal = 0 + + proc hello(a: static int, c: static int) = + aVal += a + cVal += c + + let b = toTask hello(0, 8) + b.invoke() + + doAssert aVal == 0 + doAssert cVal == 8 + + block: + var temp = "" + proc hello(x: int, y: seq[string], d = 134) = + temp = fmt"{x=} {y=} {d=}" + + + proc main() = + var x = @["23456"] + let t = toTask hello(2233, x) + t.invoke() + + doAssert temp == """x=2233 y=@["23456"] d=134""" + + main() + + + block: + var temp = "" + proc hello(x: int, y: seq[string], d = 134) = + temp.add fmt"{x=} {y=} {d=}" + + proc ok() = + temp = "ok" + + proc main() = + var x = @["23456"] + let t = toTask hello(2233, x) + t.invoke() + t.invoke() + + doAssert temp == """x=2233 y=@["23456"] d=134x=2233 y=@["23456"] d=134""" + + main() + + var x = @["4"] + let m = toTask hello(2233, x, 7) + m.invoke() + + doAssert temp == """x=2233 y=@["23456"] d=134x=2233 y=@["23456"] d=134x=2233 y=@["4"] d=7""" + + let n = toTask ok() + n.invoke() + + doAssert temp == "ok" + + block: + var called = 0 + block: + proc hello() = + inc called + + let a = toTask hello() + invoke(a) + + doAssert called == 1 + + block: + proc hello(a: int) = + inc called, a + + let b = toTask hello(13) + let c = toTask hello(a = 14) + b.invoke() + c.invoke() + + doAssert called == 28 + + block: + proc hello(a: int, c: int) = + inc called, a + + let b = toTask hello(c = 0, a = 8) + b.invoke() + + doAssert called == 36 From 86a1dcf92864f50fb1ab076cf58e19a3da81d301 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 9 Apr 2021 15:36:06 +0200 Subject: [PATCH 1906/3768] hashes: Made the runnableExample easier to understand (#17689) --- lib/pure/hashes.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim index c526c976f..3339adba2 100644 --- a/lib/pure/hashes.nim +++ b/lib/pure/hashes.nim @@ -21,7 +21,7 @@ runnableExamples: foo: int bar: string - iterator items(x: Something): int = + iterator items(x: Something): Hash = yield hash(x.foo) yield hash(x.bar) From 8aa5991beaa01f25a90d4115ed2495c92221f603 Mon Sep 17 00:00:00 2001 From: Emery Hemingway Date: Fri, 9 Apr 2021 16:29:10 +0200 Subject: [PATCH 1907/3768] Genode platform fixes (#17521) * Genode: move dyncall failures to runtime Do not use the "error" pragma to warn that dynamic library loading is not implemented, print a message at runtime and exit. * Genode: use stricter dataspace type in page allocator * Genode: remove compiler configuration from nim.cfg Self-hosting Nim is not supported on Genode and defining the cross-compilation environment can be done externally. * Genode: use new mutex API * Genode: call nim_component_construct as a C procedure * Genode: implement echo for NimStringV2 --- compiler/ccgexprs.nim | 14 ++++++++++---- compiler/cgen.nim | 2 +- config/nim.cfg | 25 +------------------------ lib/genode/alloc.nim | 14 +++++++------- lib/genode_cpp/syslocks.h | 20 +++++++++----------- lib/pure/dynlib.nim | 26 ++++++++++++++++++++++++++ lib/system/dyncalls.nim | 12 ++++++------ 7 files changed, 60 insertions(+), 53 deletions(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index ea09b3400..3872ed360 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1103,15 +1103,21 @@ proc genEcho(p: BProc, n: PNode) = # echo directly to the Genode LOG session var args: Rope = nil var a: TLoc - for it in n.sons: + for i, it in n.sons: if it.skipConv.kind == nkNilLit: args.add(", \"\"") - else: + elif n.len != 0: initLocExpr(p, it, a) - args.add(ropecg(p.module, ", Genode::Cstring($1->data, $1->len)", [rdLoc(a)])) + if i > 0: + args.add(", ") + case detectStrVersion(p.module) + of 2: + args.add(ropecg(p.module, "Genode::Cstring($1.p->data, $1.len)", [a.rdLoc])) + else: + args.add(ropecg(p.module, "Genode::Cstring($1->data, $1->len)", [a.rdLoc])) p.module.includeHeader("") p.module.includeHeader("") - linefmt(p, cpsStmts, """Genode::log(""$1);$n""", [args]) + linefmt(p, cpsStmts, """Genode::log($1);$n""", [args]) else: if n.len == 0: linefmt(p, cpsStmts, "#echoBinSafe(NIM_NIL, $1);$n", [n.len]) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 7dfab6a42..5d6de2a03 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -1418,7 +1418,7 @@ proc genMainProc(m: BModule) = GenodeNimMain = "extern Genode::Env *nim_runtime_env;$N" & - "extern void nim_component_construct(Genode::Env*);$N$N" & + "extern \"C\" void nim_component_construct(Genode::Env*);$N$N" & NimMainBody ComponentConstruct = diff --git a/config/nim.cfg b/config/nim.cfg index 39e6c002b..d3ab75825 100644 --- a/config/nim.cfg +++ b/config/nim.cfg @@ -114,7 +114,7 @@ nimblepath="$home/.nimble/pkgs/" clang.options.linker = "-Wl,--as-needed -lnetwork" clang.cpp.options.linker = "-Wl,--as-needed -lnetwork" tcc.options.linker = "-Wl,--as-needed -lnetwork" - @else: + @elif not genode: # -fopenmp gcc.options.linker = "-ldl" gcc.cpp.options.linker = "-ldl" @@ -289,29 +289,6 @@ vcc.cpp.options.size = "/O1" # Configuration for the Tiny C Compiler: tcc.options.always = "-w" -# Configuration for the Genode toolchain -@if genode: - noCppExceptions # avoid std C++ - tlsEmulation:on # no TLS segment register magic - @if i386 or amd64: - gcc.exe = "genode-x86-gcc" - gcc.cpp.exe = "genode-x86-g++" - gcc.cpp.linkerexe = "genode-x86-ld" - @elif arm: - gcc.exe = "genode-arm-gcc" - gcc.cpp.exe = "genode-arm-g++" - gcc.cpp.linkerexe = "genode-arm-ld" - @elif arm64: - gcc.exe = "genode-aarch64-gcc" - gcc.cpp.exe = "genode-aarch64-g++" - gcc.cpp.linkerexe = "genode-aarch64-ld" - @elif riscv64: - gcc.exe = "genode-riscv-gcc" - gcc.cpp.exe = "genode-riscv-g++" - gcc.cpp.linkerexe = "genode-riscv-ld" - @end -@end - @if arm or arm64: --define:nimEmulateOverflowChecks @end diff --git a/lib/genode/alloc.nim b/lib/genode/alloc.nim index a21a3ad7b..3ddd3074b 100644 --- a/lib/genode/alloc.nim +++ b/lib/genode/alloc.nim @@ -17,18 +17,18 @@ when not defined(genode): when not declared(GenodeEnv): include genode/env -type DataspaceCapability {. - importcpp: "Genode::Dataspace_capability", pure.} = object +type RamDataspaceCapability {. + importcpp: "Genode::Ram_dataspace_capability", pure.} = object type Map = object attachment: pointer size: int - ds: DataspaceCapability + ds: RamDataspaceCapability SlabMeta = object next: ptr MapSlab - ds: DataspaceCapability + ds: RamDataspaceCapability MapSlab = object meta: SlabMeta @@ -45,11 +45,11 @@ proc capsAvail(env: GenodeEnv): int {. ## Return the number of available capabilities. ## Each dataspace allocation consumes a capability. -proc allocDataspace(env: GenodeEnv; size: int): DataspaceCapability {. +proc allocDataspace(env: GenodeEnv; size: int): RamDataspaceCapability {. importcpp: "#->pd().alloc(@)".} ## Allocate a dataspace and its capability. -proc attachDataspace(env: GenodeEnv; ds: DataspaceCapability): pointer {. +proc attachDataspace(env: GenodeEnv; ds: RamDataspaceCapability): pointer {. importcpp: "#->rm().attach(@)".} ## Attach a dataspace into the component address-space. @@ -57,7 +57,7 @@ proc detachAddress(env: GenodeEnv; p: pointer) {. importcpp: "#->rm().detach(@)".} ## Detach a dataspace from the component address-space. -proc freeDataspace(env: GenodeEnv; ds: DataspaceCapability) {. +proc freeDataspace(env: GenodeEnv; ds: RamDataspaceCapability) {. importcpp: "#->pd().free(@)".} ## Free a dataspace. diff --git a/lib/genode_cpp/syslocks.h b/lib/genode_cpp/syslocks.h index 8ba39abc2..c50180903 100644 --- a/lib/genode_cpp/syslocks.h +++ b/lib/genode_cpp/syslocks.h @@ -13,7 +13,7 @@ /* Genode includes */ #include -#include +#include namespace Nim { struct SysLock; @@ -22,15 +22,14 @@ namespace Nim { struct Nim::SysLock { - Genode::Lock _lock_a, _lock_b; + Genode::Mutex _mutex_a, _mutex_b; bool _locked; void acquireSys() { - _lock_a.lock(); + Genode::Mutex::Guard guard(_mutex_a); _locked = true; - _lock_a.unlock(); - _lock_b.lock(); + _mutex_b.acquire(); } bool tryAcquireSys() @@ -38,23 +37,22 @@ struct Nim::SysLock if (_locked) return false; - _lock_a.lock(); + Genode::Mutex::Guard guard(_mutex_a); + if (_locked) { - _lock_a.unlock(); return false; } else { _locked = true; - _lock_b.lock(); - _lock_a.unlock(); + _mutex_b.acquire(); return true; } } void releaseSys() { + Genode::Mutex::Guard guard(_mutex_a); _locked = false; - _lock_a.unlock(); - _lock_b.unlock(); + _mutex_b.release(); } }; diff --git a/lib/pure/dynlib.nim b/lib/pure/dynlib.nim index a1a94072c..3c1466322 100644 --- a/lib/pure/dynlib.nim +++ b/lib/pure/dynlib.nim @@ -150,6 +150,32 @@ elif defined(nintendoswitch): proc symAddr(lib: LibHandle, name: cstring): pointer = raise newException(OSError, "symAddr not implemented on Nintendo Switch!") +elif defined(genode): + # + # ========================================================================= + # Not implemented for Genode without POSIX. Raise an error if called. + # ========================================================================= + # + + template raiseErr(prc: string) = + raise newException(OSError, prc & " not implemented, compile with POSIX suport") + + proc dlclose(lib: LibHandle) = + raiseErr(OSError, "dlclose") + proc dlopen(path: cstring, mode: int): LibHandle = + raiseErr(OSError, "dlopen") + proc dlsym(lib: LibHandle, name: cstring): pointer = + raiseErr(OSError, "dlsym") + proc loadLib(path: string, global_symbols = false): LibHandle = + raiseErr(OSError, "loadLib") + proc loadLib(): LibHandle = + raiseErr(OSError, "loadLib") + proc unloadLib(lib: LibHandle) = + raiseErr(OSError, "unloadLib") + proc symAddr(lib: LibHandle, name: cstring): pointer = + raiseErr(OSError, "symAddr") + + elif defined(windows) or defined(dos): # # ======================================================================= diff --git a/lib/system/dyncalls.nim b/lib/system/dyncalls.nim index b0f326bb5..c14fcf31e 100644 --- a/lib/system/dyncalls.nim +++ b/lib/system/dyncalls.nim @@ -167,14 +167,14 @@ elif defined(windows) or defined(dos): elif defined(genode): - proc nimUnloadLibrary(lib: LibHandle) {. - error: "nimUnloadLibrary not implemented".} + proc nimUnloadLibrary(lib: LibHandle) = + raiseAssert("nimUnloadLibrary not implemented") - proc nimLoadLibrary(path: string): LibHandle {. - error: "nimLoadLibrary not implemented".} + proc nimLoadLibrary(path: string): LibHandle = + raiseAssert("nimLoadLibrary not implemented") - proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr {. - error: "nimGetProcAddr not implemented".} + proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr = + raiseAssert("nimGetProcAddr not implemented") elif defined(nintendoswitch) or defined(freertos): proc nimUnloadLibrary(lib: LibHandle) = From 1822ed384abdbbb5d385b1ad24e23e66936dbf74 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 9 Apr 2021 16:41:49 +0200 Subject: [PATCH 1908/3768] IC: added converter test case (#17688) * IC: added converter test case * IC: make converter example work --- compiler/importer.nim | 9 ++++++--- compiler/modulegraphs.nim | 4 ++++ tests/ic/mdefconverter.nim | 2 ++ tests/ic/tconverter.nim | 18 ++++++++++++++++++ 4 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 tests/ic/mdefconverter.nim create mode 100644 tests/ic/tconverter.nim diff --git a/compiler/importer.nim b/compiler/importer.nim index cb529795a..f8ee0d483 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -163,14 +163,17 @@ proc addImport(c: PContext; im: sink ImportedModule) = c.imports.add im template addUnnamedIt(c: PContext, fromMod: PSym; filter: untyped) {.dirty.} = - for it in c.graph.ifaces[fromMod.position].converters: + for it in mitems c.graph.ifaces[fromMod.position].converters: if filter: + loadPackedSym(c.graph, it) addConverter(c, it) - for it in c.graph.ifaces[fromMod.position].patterns: + for it in mitems c.graph.ifaces[fromMod.position].patterns: if filter: + loadPackedSym(c.graph, it) addPattern(c, it) - for it in c.graph.ifaces[fromMod.position].pureEnums: + for it in mitems c.graph.ifaces[fromMod.position].pureEnums: if filter: + loadPackedSym(c.graph, it) importPureEnumFields(c, it.sym, it.sym.typ) proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) = diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index 4430e3baa..02c745018 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -326,6 +326,10 @@ proc loadCompilerProc*(g: ModuleGraph; name: string): PSym = strTableAdd(g.compilerprocs, result) return result +proc loadPackedSym*(g: ModuleGraph; s: var LazySym) = + if s.sym == nil: + s.sym = loadSymFromId(g.config, g.cache, g.packed, s.id.module, s.id.packed) + proc `$`*(u: SigHash): string = toBase64a(cast[cstring](unsafeAddr u), sizeof(u)) diff --git a/tests/ic/mdefconverter.nim b/tests/ic/mdefconverter.nim new file mode 100644 index 000000000..d0a23f801 --- /dev/null +++ b/tests/ic/mdefconverter.nim @@ -0,0 +1,2 @@ + +converter toBool*(x: int): bool = x != 0 diff --git a/tests/ic/tconverter.nim b/tests/ic/tconverter.nim new file mode 100644 index 000000000..aecdf4b48 --- /dev/null +++ b/tests/ic/tconverter.nim @@ -0,0 +1,18 @@ +discard """ + output: "yes" +""" + +import mdefconverter + +echo "yes" + +#!EDIT!# + +discard """ + output: "converted int to bool" +""" + +import mdefconverter + +if 4: + echo "converted int to bool" From 08262206d35a2f171f09681a1abe47485bd79e88 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 10 Apr 2021 01:55:39 -0500 Subject: [PATCH 1909/3768] refs #15667 improve invalid indentation errors, report when & where `=` could be missing (#16397) * refs #15667 improve invalid indentation errors * also show line info where = is missing * add test * add more tests --- compiler/lexer.nim | 1 + compiler/parser.nim | 19 +++++++++++--- tests/parser/t15667.nim | 58 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 4 deletions(-) create mode 100644 tests/parser/t15667.nim diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 7c7ac4177..59b983060 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -163,6 +163,7 @@ proc prettyTok*(tok: Token): string = else: $tok proc printTok*(conf: ConfigRef; tok: Token) = + # xxx factor with toLocation msgWriteln(conf, $tok.line & ":" & $tok.col & "\t" & $tok.tokType & " " & $tok) proc initToken*(L: var Token) = diff --git a/compiler/parser.nim b/compiler/parser.nim index d103d161a..fd9d06de5 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -44,6 +44,9 @@ type hasProgress: bool # some while loop requires progress ensurance lex*: Lexer # The lexer that is used for parsing tok*: Token # The current token + lineStartPrevious*: int + lineNumberPrevious*: int + bufposPrevious*: int inPragma*: int # Pragma level inSemiStmtList*: int emptyNode: PNode @@ -77,7 +80,7 @@ proc eat*(p: var Parser, tokType: TokType) proc skipInd*(p: var Parser) proc optPar*(p: var Parser) proc optInd*(p: var Parser, n: PNode) -proc indAndComment*(p: var Parser, n: PNode) +proc indAndComment*(p: var Parser, n: PNode, maybeMissEquals = false) proc setBaseFlags*(n: PNode, base: NumericalBase) proc parseSymbol*(p: var Parser, mode = smNormal): PNode proc parseTry(p: var Parser; isExpr: bool): PNode @@ -100,6 +103,9 @@ template prettySection(body) = proc getTok(p: var Parser) = ## Get the next token from the parser's lexer, and store it in the parser's ## `tok` member. + p.lineNumberPrevious = p.lex.lineNumber + p.lineStartPrevious = p.lex.lineStart + p.bufposPrevious = p.lex.bufpos rawGetTok(p.lex, p.tok) p.hasProgress = true when defined(nimpretty): @@ -223,9 +229,13 @@ proc parLineInfo(p: Parser): TLineInfo = ## Retrieve the line information associated with the parser's current state. result = getLineInfo(p.lex, p.tok) -proc indAndComment(p: var Parser, n: PNode) = +proc indAndComment(p: var Parser, n: PNode, maybeMissEquals = false) = if p.tok.indent > p.currInd: if p.tok.tokType == tkComment: rawSkipComment(p, n) + elif maybeMissEquals: + let col = p.bufposPrevious - p.lineStartPrevious + var info = newLineInfo(p.lex.fileIdx, p.lineNumberPrevious, col) + parMessage(p, "invalid indentation, maybe you forgot a '=' at $1 ?" % [p.lex.config$info]) else: parMessage(p, errInvalidIndentation) else: skipComment(p, n) @@ -1773,13 +1783,14 @@ proc parseRoutine(p: var Parser, kind: TNodeKind): PNode = else: result.add(p.emptyNode) # empty exception tracking: result.add(p.emptyNode) - if p.tok.tokType == tkEquals and p.validInd: + let maybeMissEquals = p.tok.tokType != tkEquals + if (not maybeMissEquals) and p.validInd: getTok(p) skipComment(p, result) result.add(parseStmt(p)) else: result.add(p.emptyNode) - indAndComment(p, result) + indAndComment(p, result, maybeMissEquals) proc newCommentStmt(p: var Parser): PNode = #| commentStmt = COMMENT diff --git a/tests/parser/t15667.nim b/tests/parser/t15667.nim new file mode 100644 index 000000000..d15178789 --- /dev/null +++ b/tests/parser/t15667.nim @@ -0,0 +1,58 @@ +discard """ + cmd: "nim check $options $file" + action: "reject" + nimout: ''' +t15667.nim(23, 5) Error: invalid indentation, maybe you forgot a '=' at t15667.nim(22, 13) ? +t15667.nim(28, 5) Error: invalid indentation, maybe you forgot a '=' at t15667.nim(26, 13) ? +t15667.nim(33, 5) Error: invalid indentation, maybe you forgot a '=' at t15667.nim(31, 25) ? +t15667.nim(42, 5) Error: invalid indentation, maybe you forgot a '=' at t15667.nim(38, 12) ? +t15667.nim(56, 5) Error: invalid indentation, maybe you forgot a '=' at t15667.nim(55, 13) ? +''' +""" + + + + + + + + +# line 20 +block: + proc fn1() + discard + +block: + proc fn2() + # + discard + +block: + proc fn3() {.exportc.} + # + discard + +block: # complex example + proc asdfasdfsd() {. exportc, + inline + .} # foo + #[ + bar + ]# + discard + +block: # xxx this doesn't work yet (only a bare `invalid indentation` error) + proc fn5() + ## + discard + +block: # ditto + proc fn6*() + ## foo bar + runnableExamples: discard + +block: + proc fn8() + runnableExamples: + discard + discard From 2150cd1826f11c74ce780dc0aaecedbed094230d Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Sat, 10 Apr 2021 10:49:01 +0300 Subject: [PATCH 1910/3768] restyle RST option lists (#17637) * WIP: restyle RST option lists * apply similar style to Latex * fix tests * minor visual tweaks * update tests * remove leftover comments --- config/nimdoc.tex.cfg | 11 ++++++- doc/nimdoc.css | 27 +++++++++++++++++ lib/packages/docutils/rst.nim | 2 ++ lib/packages/docutils/rstast.nim | 4 +-- lib/packages/docutils/rstgen.nim | 18 +++++++---- nimdoc/testproject/expected/nimdoc.out.css | 27 +++++++++++++++++ tests/stdlib/trstgen.nim | 35 ++++++++++++++-------- 7 files changed, 103 insertions(+), 21 deletions(-) diff --git a/config/nimdoc.tex.cfg b/config/nimdoc.tex.cfg index 307b280cc..69266f85d 100644 --- a/config/nimdoc.tex.cfg +++ b/config/nimdoc.tex.cfg @@ -50,7 +50,7 @@ doc.file = """ \usepackage{fancyvrb, courier} \usepackage{tabularx} \usepackage{hyperref} -\usepackage{enumitem} % for enumList and rstfootnote +\usepackage{enumitem} % for option list, enumList, and rstfootnote \usepackage{xcolor} \usepackage[tikz]{mdframed} @@ -77,6 +77,15 @@ bottomline=false} \newenvironment{rstpre}{\VerbatimEnvironment\begingroup\begin{Verbatim}[fontsize=\footnotesize , commandchars=\\\{\}]}{\end{Verbatim}\endgroup} \newenvironment{rstfootnote}{\begin{description}[labelindent=1em,leftmargin=1em,labelwidth=2.6em]}{\end{description}} +\ifdim\linewidth<30em + \def\rstoptleftmargin{0.4\linewidth} + \def\rstoptlabelwidth{0.35\linewidth} +\else + \def\rstoptleftmargin{12em} + \def\rstoptlabelwidth{10.5em} +\fi +\newenvironment{rstoptlist}{% +\begin{description}[font=\sffamily\bfseries,style=nextline,leftmargin=\rstoptleftmargin,labelwidth=\rstoptlabelwidth]}{\end{description}} % to pack tabularx into a new environment, special syntax is needed :-( \newenvironment{rsttab}[1]{\tabularx{\linewidth}{#1}}{\endtabularx} diff --git a/doc/nimdoc.css b/doc/nimdoc.css index db9a7ce97..ced791d16 100644 --- a/doc/nimdoc.css +++ b/doc/nimdoc.css @@ -511,6 +511,33 @@ div.footnote-label { min-width: 1.7em; } +div.option-list { + border: 0.1em solid var(--border); +} +div.option-list-item { + padding-left: 12em; + padding-right: 0; + padding-bottom: 0.3em; + padding-top: 0.3em; +} +div.odd { + background-color: var(--secondary-background); +} +div.option-list-label { + margin-left: -11.5em; + margin-right: 0em; + min-width: 11.5em; + font-weight: bolder; + display: inline-block; + vertical-align: top; +} +div.option-list-description { + width: calc(100% - 1em); + padding-left: 1em; + padding-right: 0; + display: inline-block; +} + blockquote { font-size: 0.9em; font-style: italic; diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index a8bc04a1a..c2385d517 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -2085,6 +2085,7 @@ proc parseBulletList(p: var RstParser): PRstNode = proc parseOptionList(p: var RstParser): PRstNode = result = newRstNodeA(p, rnOptionList) let col = currentTok(p).col + var order = 1 while true: if currentTok(p).col == col and isOptionList(p): var a = newRstNode(rnOptionGroup) @@ -2107,6 +2108,7 @@ proc parseOptionList(p: var RstParser): PRstNode = if currentTok(p).kind == tkIndent: inc p.idx c.add(a) c.add(b) + c.order = order; inc order result.add(c) else: if currentTok(p).kind != tkEof: dec p.idx # back to tkIndent diff --git a/lib/packages/docutils/rstast.nim b/lib/packages/docutils/rstast.nim index dd456b577..394cc2698 100644 --- a/lib/packages/docutils/rstast.nim +++ b/lib/packages/docutils/rstast.nim @@ -90,7 +90,7 @@ type level*: int ## level of headings starting from 1 (main ## chapter) to larger ones (minor sub-sections) ## level=0 means it's document title or subtitle - of rnFootnote, rnCitation, rnFootnoteRef: + of rnFootnote, rnCitation, rnFootnoteRef, rnOptionListItem: order*: int ## footnote order (for auto-symbol footnotes and ## auto-numbered ones without a label) else: @@ -368,7 +368,7 @@ proc renderRstToStr*(node: PRstNode, indent=0): string = result.add txt of rnHeadline, rnOverline, rnMarkdownHeadline: result.add "\tlevel=" & $node.level - of rnFootnote, rnCitation, rnFootnoteRef: + of rnFootnote, rnCitation, rnFootnoteRef, rnOptionListItem: result.add (if node.order == 0: "" else: "\torder=" & $node.order) else: discard diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index f0a2604ff..1b9334a77 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -1165,7 +1165,7 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = renderAux(d, n, "$1\n", "\\begin{description}\n$2\n$1\\end{description}\n", result) of rnDefItem: renderAux(d, n, result) - of rnDefName: renderAux(d, n, "$1\n", "$2\\item[$1] ", result) + of rnDefName: renderAux(d, n, "$1\n", "$2\\item[$1]\\ ", result) of rnDefBody: renderAux(d, n, "$1\n", "$2\n$1\n", result) of rnFieldList: var tmp = "" @@ -1189,14 +1189,20 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = of rnIndex: renderRstToOut(d, n.sons[2], result) of rnOptionList: - renderAux(d, n, "$1", - "\\begin{description}\n$2\n$1\\end{description}\n", result) + renderAux(d, n, "$1", + "\\begin{rstoptlist}$2\n$1\\end{rstoptlist}", result) of rnOptionListItem: - renderAux(d, n, "$1\n", "$1", result) + var addclass = if n.order mod 2 == 1: " odd" else: "" + renderAux(d, n, + "
    $1
    \n", + "$1", result) of rnOptionGroup: - renderAux(d, n, "$1", "\\item[$1]", result) + renderAux(d, n, + "
    $1
    ", + "\\item[$1]", result) of rnDescription: - renderAux(d, n, "$1\n", " $1\n", result) + renderAux(d, n, "
    $1
    ", + " $1\n", result) of rnOption, rnOptionString, rnOptionArgument: doAssert false, "renderRstToOut" of rnLiteralBlock: diff --git a/nimdoc/testproject/expected/nimdoc.out.css b/nimdoc/testproject/expected/nimdoc.out.css index db9a7ce97..ced791d16 100644 --- a/nimdoc/testproject/expected/nimdoc.out.css +++ b/nimdoc/testproject/expected/nimdoc.out.css @@ -511,6 +511,33 @@ div.footnote-label { min-width: 1.7em; } +div.option-list { + border: 0.1em solid var(--border); +} +div.option-list-item { + padding-left: 12em; + padding-right: 0; + padding-bottom: 0.3em; + padding-top: 0.3em; +} +div.odd { + background-color: var(--secondary-background); +} +div.option-list-label { + margin-left: -11.5em; + margin-right: 0em; + min-width: 11.5em; + font-weight: bolder; + display: inline-block; + vertical-align: top; +} +div.option-list-description { + width: calc(100% - 1em); + padding-left: 1em; + padding-right: 0; + display: inline-block; +} + blockquote { font-size: 0.9em; font-style: italic; diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim index b403d96c6..ed5d72226 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -1255,7 +1255,7 @@ Test1 doAssert "
    ") == 2) - check(output.count("-mdesc""" in output) - check("""-nvery long desc""" in + check(output.count("
    -m
    """ & + """
    desc
    """ in + output) + check("""
    -n
    """ & + """
    very long desc
    """ in output) test "Option lists 2": @@ -1385,11 +1388,15 @@ Test1 -d option""" let output = input.toHtml check(output.count("-mdesc""" in output) - check("""-nvery long desc""" in + check output.count("
    -m
    """ & + """
    desc
    """ in output) - check("""-doption""" in + check("""
    -n
    """ & + """
    very long desc
    """ in + output) + check("""
    -d
    """ & + """
    option
    """ in output) check "

    option

    " notin output @@ -1402,11 +1409,15 @@ Test1 -d option""" let output = input.toHtml check(output.count("compilecompile1""" in output) - check("""docdoc1 cont""" in + check output.count("
    compile
    """ & + """
    compile1
    """ in output) - check("""-doption""" in + check("""
    doc
    """ & + """
    doc1 cont
    """ in + output) + check("""
    -d
    """ & + """
    option
    """ in output) check "

    option

    " notin output From 3aaec0647b6c924ea1da18e8427f13289ebab980 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Sun, 11 Apr 2021 11:23:08 +0300 Subject: [PATCH 1911/3768] turn on syntax highlighting in Manual & Tutorial (#17692) * turn on syntax highlighting in Manual & Tutorial * avoid highlighting of "method" * use relative path * 2 more changes --- doc/manual.rst | 192 ++++++++++++++++++------------------ doc/manual/var_t_return.rst | 1 + doc/rstcommon.rst | 6 ++ doc/sets_fragment.txt | 38 +++---- doc/tut1.rst | 34 +++---- doc/tut2.rst | 11 ++- doc/tut3.rst | 4 +- 7 files changed, 149 insertions(+), 137 deletions(-) create mode 100644 doc/rstcommon.rst diff --git a/doc/manual.rst b/doc/manual.rst index aad8034c8..1325d1c5f 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -1,5 +1,3 @@ -.. default-role:: code - ========== Nim Manual ========== @@ -7,6 +5,8 @@ Nim Manual :Authors: Andreas Rumpf, Zahary Karadjov :Version: |nimversion| +.. default-role:: code +.. include:: rstcommon.rst .. contents:: @@ -127,9 +127,9 @@ compiler may instead choose to allow the program to die with a fatal error. echo "invalid index" The current implementation allows to switch between these different behaviors -via `--panics:on|off`. When panics are turned on, the program dies with a +via ``--panics:on|off``. When panics are turned on, the program dies with a panic, if they are turned off the runtime errors are turned into -exceptions. The benefit of `--panics:on` is that it produces smaller binary +exceptions. The benefit of ``--panics:on`` is that it produces smaller binary code and the compiler has more freedom to optimize the code. An `unchecked runtime error`:idx: is an error that is not guaranteed to be @@ -490,7 +490,7 @@ Rationale: It enables the efficient support of `array[char, int]` or The `Rune` type can represent any Unicode character. `Rune` is declared in the `unicode module `_. -A character literal that does not end in ``'`` is interpreted as ``'`` if there +A character literal that does not end in `'` is interpreted as `'` if there is a preceeding backtick token. There must be no whitespace between the preceeding backtick token and the character literal. This special case ensures that a declaration like ``proc `'customLiteral`(s: string)`` is valid. ``proc `'customLiteral`(s: string)`` @@ -752,7 +752,7 @@ Otherwise, precedence is determined by the first character. Precedence level Operators First character Terminal symbol ================ ======================================================= ================== =============== 10 (highest) `$ ^` OP10 - 9 `* / div mod shl shr %` ``* % \ /`` OP9 + 9 `* / div mod shl shr %` `* % \ /` OP9 8 `+ -` `+ - ~ |` OP8 7 `&` `&` OP7 6 `..` `.` OP6 @@ -1008,7 +1008,7 @@ These integer types are pre-defined: literal that has no type suffix is of this type if it is in the range `low(int32)..high(int32)` otherwise the literal's type is `int64`. -intXX +`int`\ XX additional signed integer types of XX bits use this naming scheme (example: int16 is a 16-bit wide integer). The current implementation supports `int8`, `int16`, `int32`, `int64`. @@ -1019,7 +1019,7 @@ intXX has the same size as a pointer. An integer literal with the type suffix `'u` is of this type. -uintXX +`uint`\ XX additional unsigned integer types of XX bits use this naming scheme (example: uint16 is a 16-bit wide unsigned integer). The current implementation supports `uint8`, `uint16`, `uint32`, @@ -1039,22 +1039,22 @@ the `%` suffix as convention: ====================== ====================================================== operation meaning ====================== ====================================================== -`a +% b` unsigned integer addition -`a -% b` unsigned integer subtraction -`a *% b` unsigned integer multiplication -`a /% b` unsigned integer division -`a %% b` unsigned integer modulo operation -`a <% b` treat `a` and `b` as unsigned and compare -`a <=% b` treat `a` and `b` as unsigned and compare -`ze(a)` extends the bits of `a` with zeros until it has the +`a +% b` unsigned integer addition +`a -% b` unsigned integer subtraction +`a *% b` unsigned integer multiplication +`a /% b` unsigned integer division +`a %% b` unsigned integer modulo operation +`a <% b` treat `a` and `b` as unsigned and compare +`a <=% b` treat `a` and `b` as unsigned and compare +`ze(a)` extends the bits of `a` with zeros until it has the width of the `int` type -`toU8(a)` treats `a` as unsigned and converts it to an +`toU8(a)` treats `a` as unsigned and converts it to an unsigned integer of 8 bits (but still the `int8` type) -`toU16(a)` treats `a` as unsigned and converts it to an +`toU16(a)` treats `a` as unsigned and converts it to an unsigned integer of 16 bits (but still the `int16` type) -`toU32(a)` treats `a` as unsigned and converts it to an +`toU32(a)` treats `a` as unsigned and converts it to an unsigned integer of 32 bits (but still the `int32` type) ====================== ====================================================== @@ -1117,9 +1117,9 @@ The following floating-point types are pre-defined: but now it is always mapped to `float64`. This type should be used in general. -floatXX +`float`\ XX an implementation may define additional floating-point types of XX bits using - this naming scheme (example: float64 is a 64-bit wide float). The current + this naming scheme (example: `float64` is a 64-bit wide float). The current implementation supports `float32` and `float64`. Literals of these types have the suffix 'fXX. @@ -1459,7 +1459,7 @@ The notation `x[i]` can be used to access the i-th element of `x`. Arrays are always bounds checked (statically or at runtime). These checks can be disabled via pragmas or invoking the compiler with the -`--boundChecks:off` command-line switch. +``--boundChecks:off`` command-line switch. An array constructor can have explicit indexes for readability: @@ -1946,7 +1946,8 @@ memory manually: dealloc(d) Without the `reset` call the memory allocated for the `d.s` string would -never be freed. The example also demonstrates two important features for low-level programming: the `sizeof` proc returns the size of a type or value +never be freed. The example also demonstrates two important features for +low-level programming: the `sizeof` proc returns the size of a type or value in bytes. The `cast` operator can circumvent the type system: the compiler is forced to treat the result of the `alloc0` call (which returns an untyped pointer) as if it would have the type `ptr Data`. Casting should only be @@ -2045,7 +2046,7 @@ Nim supports these `calling conventions`:idx:\: C++ class member functions on the x86 architecture. `syscall`:idx: - The syscall convention is the same as `__syscall` in C. It is used for + The syscall convention is the same as `__syscall`:c: in C. It is used for interrupts. `noconv`:idx: @@ -3217,7 +3218,7 @@ Assembler statement The direct embedding of assembler code into Nim code is supported by the unsafe `asm` statement. Identifiers in the assembler code that refer to Nim identifiers shall be enclosed in a special character which can be -specified in the statement's pragmas. The default special character is ``'`'``: +specified in the statement's pragmas. The default special character is `'\`'`: .. code-block:: nim {.push stackTrace:off.} @@ -3261,7 +3262,7 @@ Instead of: Using statement --------------- -The using statement provides syntactic convenience in modules where +The `using` statement provides syntactic convenience in modules where the same parameter names and types are used over and over. Instead of: .. code-block:: nim @@ -3301,8 +3302,8 @@ that are explicitly typed is possible and requires a semicolon between them. If expression ------------- -An `if expression` is almost like an if statement, but it is an expression. -This feature is similar to `ternary operators` in other languages. +An `if` expression is almost like an if statement, but it is an expression. +This feature is similar to *ternary operators* in other languages. Example: .. code-block:: nim @@ -3314,12 +3315,12 @@ required. `Elif` parts are also allowed. When expression --------------- -Just like an `if expression`, but corresponding to the when statement. +Just like an `if` expression, but corresponding to the `when` statement. Case expression --------------- -The `case expression` is again very similar to the case statement: +The `case` expression is again very similar to the case statement: .. code-block:: nim var favoriteFood = case animal @@ -3337,7 +3338,7 @@ the last expression as the result value. Block expression ---------------- -A `block expression` is almost like a block statement, but it is an expression +A `block` expression is almost like a block statement, but it is an expression that uses the last expression under the block as the value. It is similar to the statement list expression, but the statement list expression does not open a new block scope. @@ -3434,7 +3435,7 @@ Type casts should not be confused with *type conversions,* as mentioned in the prior section. Unlike type conversions, a type cast cannot change the underlying bit pattern of the data being casted (aside from that the size of the target type may differ from the source type). Casting resembles *type punning* in other -languages or C++'s `reinterpret_cast` and `bit_cast` features. +languages or C++'s `reinterpret_cast`:cpp: and `bit_cast`:cpp: features. The addr operator ----------------- @@ -3562,7 +3563,7 @@ the operator's position within an expression.) There is no way to declare postfix operators: all postfix operators are built-in and handled by the grammar explicitly. -Any operator can be called like an ordinary proc with the '`opr`' +Any operator can be called like an ordinary proc with the \`opr\` notation. (Thus an operator can have more than two parameters): .. code-block:: nim @@ -3596,9 +3597,9 @@ current module: Method call syntax ------------------ -For object-oriented programming, the syntax `obj.method(args)` can be used -instead of `method(obj, args)`. The parentheses can be omitted if there are no -remaining arguments: `obj.len` (instead of `len(obj)`). +For object-oriented programming, the syntax `obj.methodName(args)` can be used +instead of `methodName(obj, args)`. The parentheses can be omitted if +there are no remaining arguments: `obj.len` (instead of `len(obj)`). This method call syntax is not restricted to objects, it can be used to supply any type of first argument for procedures: @@ -3813,7 +3814,7 @@ simplicity (they require specialized semantic checking):: Thus they act more like keywords than like ordinary identifiers; unlike a keyword however, a redefinition may `shadow`:idx: the definition in -the `system` module. From this list the following should not be written in dot +the system_ module. From this list the following should not be written in dot notation `x.f` since `x` cannot be type-checked before it gets passed to `f`:: @@ -4059,7 +4060,7 @@ Multi-methods -------------- **Note:** Starting from Nim 0.20, to use multi-methods one must explicitly pass -`--multimethods:on` when compiling. +``--multimethods:on`` when compiling. In a multi-method, all parameters that have an object type are used for the dispatching: @@ -4768,7 +4769,7 @@ And so is: The reason for this is that `DivByZeroDefect` inherits from `Defect` and -with `--panics:on` Defects become unrecoverable errors. +with ``--panics:on`` Defects become unrecoverable errors. (Since version 1.4 of the language.) @@ -5567,7 +5568,7 @@ However, this means that the method call syntax is not available for **Note**: The Nim compiler prior to version 1 was more lenient about this -requirement. Use the `--useVersion:0.19` switch for a transition period. +requirement. Use the ``--useVersion:0.19`` switch for a transition period. @@ -6022,7 +6023,7 @@ A module may gain access to symbols of another module by the `import`:idx: statement. `Recursive module dependencies`:idx: are allowed, but are slightly subtle. Only top-level symbols that are marked with an asterisk (`*`) are exported. A valid module name can only be a valid Nim identifier (and thus its -filename is `identifier.nim`). +filename is ``identifier.nim``). The algorithm for compiling modules is: @@ -6484,7 +6485,7 @@ asmNoStackFrame pragma A proc can be marked with the `asmNoStackFrame` pragma to tell the compiler it should not generate a stack frame for the proc. There are also no exit statements like `return result;` generated and the generated C function is -declared as `__declspec(naked)` or `__attribute__((naked))` (depending on +declared as `__declspec(naked)`:c: or `__attribute__((naked))`:c: (depending on the used C compiler). **Note**: This pragma should only be used by procs which consist solely of @@ -6879,7 +6880,7 @@ This pragma has no effect on the JS backend. Volatile pragma --------------- The `volatile` pragma is for variables only. It declares the variable as -`volatile`, whatever that means in C/C++ (its semantics are not well defined +`volatile`:c:, whatever that means in C/C++ (its semantics are not well defined in C/C++). **Note**: This pragma will not exist for the LLVM backend. @@ -6906,7 +6907,7 @@ Header pragma ------------- The `header` pragma is very similar to the `nodecl` pragma: It can be applied to almost any symbol and specifies that it should not be declared -and instead, the generated code should contain an `#include`: +and instead, the generated code should contain an `#include`:c:\: .. code-block:: Nim type @@ -6915,8 +6916,8 @@ and instead, the generated code should contain an `#include`: The `header` pragma always expects a string constant. The string constant contains the header file: As usual for C, a system header file is enclosed -in angle brackets: `<>`. If no angle brackets are given, Nim -encloses the header file in `""` in the generated C code. +in angle brackets: `<>`:c:. If no angle brackets are given, Nim +encloses the header file in `""`:c: in the generated C code. **Note**: This will not work for the LLVM backend. @@ -6924,7 +6925,7 @@ encloses the header file in `""` in the generated C code. IncompleteStruct pragma ----------------------- The `incompleteStruct` pragma tells the compiler to not use the -underlying C `struct` in a `sizeof` expression: +underlying C `struct`:c: in a `sizeof` expression: .. code-block:: Nim type @@ -6941,8 +6942,8 @@ with the project: {.compile: "myfile.cpp".} **Note**: Nim computes a SHA1 checksum and only recompiles the file if it -has changed. One can use the `-f` command-line option to force the recompilation -of the file. +has changed. One can use the ``-f`` command-line option to force +the recompilation of the file. Since 1.4 the `compile` pragma is also available with this syntax: @@ -6964,7 +6965,7 @@ The `link` pragma can be used to link an additional file with the project: PassC pragma ------------ The `passc` pragma can be used to pass additional parameters to the C -compiler like one would using the command-line switch `--passc`: +compiler like one would using the command-line switch ``--passc``: .. code-block:: Nim {.passc: "-Wall -Werror".} @@ -6992,7 +6993,7 @@ the pragma resides in: PassL pragma ------------ The `passL` pragma can be used to pass additional parameters to the linker -like one would be using the command-line switch `--passL`: +like one would be using the command-line switch ``--passL``: .. code-block:: Nim {.passL: "-lSDLmain -lSDL".} @@ -7029,7 +7030,7 @@ Example: embedsC() `nimbase.h` defines `NIM_EXTERNC` C macro that can be used for -`extern "C"` code to work with both `nim c` and `nim cpp`, e.g.: +`extern "C"`:cpp: code to work with both `nim c` and `nim cpp`, e.g.: .. code-block:: Nim proc foobar() {.importc:"$1".} @@ -7044,8 +7045,8 @@ Example: This usage is however deprecated. For a top-level emit statement, the section where in the generated C/C++ file -the code should be emitted can be influenced via the -prefixes `/*TYPESECTION*/` or `/*VARSECTION*/` or `/*INCLUDESECTION*/`: +the code should be emitted can be influenced via the prefixes +`/*TYPESECTION*/`:c: or `/*VARSECTION*/`:c: or `/*INCLUDESECTION*/`:c:\: .. code-block:: Nim {.emit: """/*TYPESECTION*/ @@ -7075,7 +7076,7 @@ Similar to the `importc pragma for C <#foreign-function-interface-importc-pragma>`_, the `importcpp` pragma can be used to import `C++`:idx: methods or C++ symbols in general. The generated code then uses the C++ method calling -syntax: `obj->method(arg)`. In combination with the `header` and `emit` +syntax: `obj->method(arg)`:cpp:. In combination with the `header` and `emit` pragmas this allows *sloppy* interfacing with libraries written in C++: .. code-block:: Nim @@ -7105,16 +7106,16 @@ pragmas this allows *sloppy* interfacing with libraries written in C++: proc run(device: IrrlichtDevice): bool {. header: irr, importcpp: "#.run(@)".} -The compiler needs to be told to generate C++ (command `cpp`) for +The compiler needs to be told to generate C++ (command ``cpp``) for this to work. The conditional symbol `cpp` is defined when the compiler emits C++ code. Namespaces ~~~~~~~~~~ -The *sloppy interfacing* example uses `.emit` to produce `using namespace` +The *sloppy interfacing* example uses `.emit` to produce `using namespace`:cpp: declarations. It is usually much better to instead refer to the imported name -via the `namespace::identifier` notation: +via the `namespace::identifier`:cpp: notation: .. code-block:: nim type @@ -7126,7 +7127,8 @@ Importcpp for enums ~~~~~~~~~~~~~~~~~~~ When `importcpp` is applied to an enum type the numerical enum values are -annotated with the C++ enum type, like in this example: `((TheCppEnum)(3))`. +annotated with the C++ enum type, like in this example: +`((TheCppEnum)(3))`:cpp:. (This turned out to be the simplest way to implement it.) @@ -7136,10 +7138,11 @@ Importcpp for procs Note that the `importcpp` variant for procs uses a somewhat cryptic pattern language for maximum flexibility: -- A hash `#` symbol is replaced by the first or next argument. -- A dot following the hash `#.` indicates that the call should use C++'s dot +- A hash ``#`` symbol is replaced by the first or next argument. +- A dot following the hash ``#.`` indicates that the call should use C++'s dot or arrow notation. -- An at symbol `@` is replaced by the remaining arguments, separated by commas. +- An at symbol ``@`` is replaced by the remaining arguments, + separated by commas. For example: @@ -7155,7 +7158,7 @@ Produces: As a special rule to keep backward compatibility with older versions of the `importcpp` pragma, if there is no special pattern -character (any of `# ' @`) at all, C++'s +character (any of ``# ' @``) at all, C++'s dot or arrow notation is assumed, so the above example can also be written as: .. code-block:: nim @@ -7169,11 +7172,11 @@ capabilities: proc dictLookup(a: Dict, k: Key): Value {.importcpp: "#[#]".} -- An apostrophe `'` followed by an integer `i` in the range 0..9 +- An apostrophe ``'`` followed by an integer ``i`` in the range 0..9 is replaced by the i'th parameter *type*. The 0th position is the result type. This can be used to pass types to C++ function templates. Between - the `'` and the digit, an asterisk can be used to get to the base type - of the type. (So it "takes away a star" from the type; `T*` becomes `T`.) + the ``'`` and the digit, an asterisk can be used to get to the base type + of the type. (So it "takes away a star" from the type; `T*`:c: becomes `T`.) Two stars can be used to get to the element type of the element type etc. For example: @@ -7191,12 +7194,12 @@ Produces: x = SystemManager::getSubsystem() -- `#@` is a special case to support a `cnew` operation. It is required so +- ``#@`` is a special case to support a `cnew` operation. It is required so that the call expression is inlined directly, without going through a temporary location. This is only required to circumvent a limitation of the current code generator. -For example C++'s `new` operator can be "imported" like this: +For example C++'s `new`:cpp: operator can be "imported" like this: .. code-block:: nim proc cnew*[T](x: T): ptr T {.importcpp: "(new '*0#@)", nodecl.} @@ -7211,7 +7214,7 @@ Produces: .. code-block:: C x = new Foo(3, 4) -However, depending on the use case `new Foo` can also be wrapped like this +However, depending on the use case `new Foo`:cpp: can also be wrapped like this instead: .. code-block:: nim @@ -7224,7 +7227,8 @@ Wrapping constructors ~~~~~~~~~~~~~~~~~~~~~ Sometimes a C++ class has a private copy constructor and so code like -`Class c = Class(1,2);` must not be generated but instead `Class c(1,2);`. +`Class c = Class(1,2);`:cpp: must not be generated but instead +`Class c(1,2);`:cpp:. For this purpose the Nim proc that wraps a C++ constructor needs to be annotated with the `constructor`:idx: pragma. This pragma also helps to generate faster C++ code since construction then doesn't invoke the copy constructor: @@ -7298,7 +7302,7 @@ ImportJs pragma Similar to the `importcpp pragma for C++ <#implementation-specific-pragmas-importcpp-pragma>`_, the `importjs` pragma can be used to import Javascript methods or symbols in general. The generated code then uses the Javascript method -calling syntax: `obj.method(arg)`. +calling syntax: ``obj.method(arg)``. ImportObjC pragma @@ -7306,7 +7310,7 @@ ImportObjC pragma Similar to the `importc pragma for C <#foreign-function-interface-importc-pragma>`_, the `importobjc` pragma can be used to import `Objective C`:idx: methods. The generated code then uses the -Objective C method calling syntax: `[obj method param1: arg]`. +Objective C method calling syntax: ``[obj method param1: arg]``. In addition with the `header` and `emit` pragmas this allows *sloppy* interfacing with libraries written in Objective C: @@ -7346,8 +7350,8 @@ allows *sloppy* interfacing with libraries written in Objective C: g.greet(12, 34) g.free() -The compiler needs to be told to generate Objective C (command `objc`) for -this to work. The conditional symbol `objc` is defined when the compiler +The compiler needs to be told to generate Objective C (command ``objc``) for +this to work. The conditional symbol ``objc`` is defined when the compiler emits Objective C code. @@ -7392,7 +7396,7 @@ will generate this code: The `.cppNonPod` pragma should be used for non-POD `importcpp` types so that they work properly (in particular regarding constructor and destructor) for -`.threadvar` variables. This requires `--tlsEmulation:off`. +`.threadvar` variables. This requires ``--tlsEmulation:off``. .. code-block:: nim type Foo {.cppNonPod, importcpp, header: "funs.h".} = object @@ -7436,12 +7440,12 @@ pragma description :: nim c -d:FooBar=42 foobar.nim -In the above example, providing the -d flag causes the symbol +In the above example, providing the ``-d`` flag causes the symbol `FooBar` to be overwritten at compile-time, printing out 42. If the -`-d:FooBar=42` were to be omitted, the default value of 5 would be +``-d:FooBar=42`` were to be omitted, the default value of 5 would be used. To see if a value was provided, `defined(FooBar)` can be used. -The syntax `-d:flag` is actually just a shortcut for `-d:flag=true`. +The syntax ``-d:flag`` is actually just a shortcut for ``-d:flag=true``. User-defined pragmas ==================== @@ -7604,7 +7608,7 @@ spelled*: proc printf(formatstr: cstring) {.header: "", importc: "printf", varargs.} When `importc` is applied to a `let` statement it can omit its value which -will then be expected to come from C. This can be used to import a C `const`: +will then be expected to come from C. This can be used to import a C `const`:c:\: .. code-block:: {.emit: "const int cconst = 42;".} @@ -7625,8 +7629,8 @@ is not set to C, other pragmas are available: .. code-block:: Nim proc p(s: cstring) {.importc: "prefix$1".} -In the example, the external name of `p` is set to `prefixp`. Only `$1` -is available and a literal dollar sign must be written as `$$`. +In the example, the external name of `p` is set to `prefixp`. Only ``$1`` +is available and a literal dollar sign must be written as ``$$``. Exportc pragma @@ -7648,8 +7652,8 @@ The string literal passed to `exportc` can be a format string: proc p(s: string) {.exportc: "prefix$1".} = echo s -In the example, the external name of `p` is set to `prefixp`. Only `$1` -is available and a literal dollar sign must be written as `$$`. +In the example, the external name of `p` is set to `prefixp`. Only ``$1`` +is available and a literal dollar sign must be written as ``$$``. If the symbol should also be exported to a dynamic library, the `dynlib` pragma should be used in addition to the `exportc` pragma. See @@ -7665,8 +7669,8 @@ mangling. The string literal passed to `extern` can be a format string: proc p(s: string) {.extern: "prefix$1".} = echo s -In the example, the external name of `p` is set to `prefixp`. Only `$1` -is available and a literal dollar sign must be written as `$$`. +In the example, the external name of `p` is set to `prefixp`. Only ``$1`` +is available and a literal dollar sign must be written as ``$$``. Bycopy pragma @@ -7704,8 +7708,8 @@ strings automatically: Union pragma ------------ The `union` pragma can be applied to any `object` type. It means all -of the object's fields are overlaid in memory. This produces a `union` -instead of a `struct` in the generated C/C++ code. The object declaration +of the object's fields are overlaid in memory. This produces a `union`:c: +instead of a `struct`:c: in the generated C/C++ code. The object declaration then must not use inheritance or any GC'ed memory but this is currently not checked. @@ -7728,7 +7732,7 @@ a static error. Usage with inheritance should be defined and documented. Dynlib pragma for import ------------------------ With the `dynlib` pragma, a procedure or a variable can be imported from -a dynamic library (`.dll` files for Windows, `lib*.so` files for UNIX). +a dynamic library (``.dll`` files for Windows, ``lib*.so`` files for UNIX). The non-optional argument has to be the name of the dynamic library: .. code-block:: Nim @@ -7771,14 +7775,14 @@ string expressions in general: proc myImport(s: cstring) {.cdecl, importc, dynlib: getDllName().} -**Note**: Patterns like `libtcl(|8.5|8.4).so` are only supported in constant +**Note**: Patterns like ``libtcl(|8.5|8.4).so`` are only supported in constant strings, because they are precompiled. **Note**: Passing variables to the `dynlib` pragma will fail at runtime because of order of initialization problems. **Note**: A `dynlib` import can be overridden with -the `--dynlibOverride:name` command-line option. The +the ``--dynlibOverride:name`` command-line option. The `Compiler User Guide `_ contains further information. @@ -7793,15 +7797,15 @@ conjunction with the `exportc` pragma: proc exportme(): int {.cdecl, exportc, dynlib.} This is only useful if the program is compiled as a dynamic library via the -`--app:lib` command-line option. +``--app:lib`` command-line option. Threads ======= -To enable thread support the `--threads:on` command-line switch needs to -be used. The `system` module then contains several threading primitives. +To enable thread support the ``--threads:on`` command-line switch needs to +be used. The system_ module then contains several threading primitives. See the `threads `_ and `channels `_ modules for the low-level thread API. There are also high-level parallelism constructs available. See `spawn `_ for @@ -7842,7 +7846,7 @@ any of its parameters contain a `ref` or `closure` type. This enforces the *no heap sharing restriction*. Routines that are imported from C are always assumed to be `gcsafe`. -To disable the GC-safety checking the `--threadAnalysis:off` command-line +To disable the GC-safety checking the ``--threadAnalysis:off`` command-line switch can be used. This is a temporary workaround to ease the porting effort from old code to the new threading model. diff --git a/doc/manual/var_t_return.rst b/doc/manual/var_t_return.rst index e34993e3e..f5c5bc4c0 100644 --- a/doc/manual/var_t_return.rst +++ b/doc/manual/var_t_return.rst @@ -1,4 +1,5 @@ .. default-role:: code +.. include:: ../rstcommon.rst Memory safety for returning by `var T` is ensured by a simple borrowing rule: If `result` does not refer to a location pointing to the heap diff --git a/doc/rstcommon.rst b/doc/rstcommon.rst new file mode 100644 index 000000000..c1503fa96 --- /dev/null +++ b/doc/rstcommon.rst @@ -0,0 +1,6 @@ + +.. role:: nim(code) + :language: nim + +.. default-role:: nim + diff --git a/doc/sets_fragment.txt b/doc/sets_fragment.txt index 8436190a0..84e2fe8c1 100644 --- a/doc/sets_fragment.txt +++ b/doc/sets_fragment.txt @@ -1,13 +1,13 @@ The set type models the mathematical notion of a set. The set's basetype can only be an ordinal type of a certain size, namely: -* ``int8``-``int16`` -* ``uint8``/``byte``-``uint16`` -* ``char`` -* ``enum`` +* `int8`-`int16` +* `uint8`/`byte`-`uint16` +* `char` +* `enum` or equivalent. For signed integers the set's base type is defined to be in the -range ``0 .. MaxSetElements-1`` where ``MaxSetElements`` is currently always +range `0 .. MaxSetElements-1` where `MaxSetElements` is currently always 2^16. The reason is that sets are implemented as high performance bit vectors. @@ -17,7 +17,7 @@ Attempting to declare a set with a larger type will result in an error: var s: set[int64] # Error: set is too large -Sets can be constructed via the set constructor: ``{}`` is the empty set. The +Sets can be constructed via the set constructor: `{}` is the empty set. The empty set is type compatible with any concrete set type. The constructor can also be used to include elements (and ranges of elements): @@ -35,18 +35,18 @@ These operations are supported by sets: ================== ======================================================== operation meaning ================== ======================================================== -``A + B`` union of two sets -``A * B`` intersection of two sets -``A - B`` difference of two sets (A without B's elements) -``A == B`` set equality -``A <= B`` subset relation (A is subset of B or equal to B) -``A < B`` strict subset relation (A is a proper subset of B) -``e in A`` set membership (A contains element e) -``e notin A`` A does not contain element e -``contains(A, e)`` A contains element e -``card(A)`` the cardinality of A (number of elements in A) -``incl(A, elem)`` same as ``A = A + {elem}`` -``excl(A, elem)`` same as ``A = A - {elem}`` +`A + B` union of two sets +`A * B` intersection of two sets +`A - B` difference of two sets (A without B's elements) +`A == B` set equality +`A <= B` subset relation (A is subset of B or equal to B) +`A < B` strict subset relation (A is a proper subset of B) +`e in A` set membership (A contains element e) +`e notin A` A does not contain element e +`contains(A, e)` A contains element e +`card(A)` the cardinality of A (number of elements in A) +`incl(A, elem)` same as `A = A + {elem}` +`excl(A, elem)` same as `A = A - {elem}` ================== ======================================================== Bit fields @@ -54,7 +54,7 @@ Bit fields Sets are often used to define a type for the *flags* of a procedure. This is a cleaner (and type safe) solution than defining integer -constants that have to be ``or``'ed together. +constants that have to be `or`'ed together. Enum, sets and casting can be used together as in: diff --git a/doc/tut1.rst b/doc/tut1.rst index cca4e8c0e..8d80a0520 100644 --- a/doc/tut1.rst +++ b/doc/tut1.rst @@ -1,5 +1,3 @@ -.. default-role:: code - ===================== Nim Tutorial (Part I) ===================== @@ -7,6 +5,8 @@ Nim Tutorial (Part I) :Author: Andreas Rumpf :Version: |nimversion| +.. default-role:: code +.. include:: rstcommon.rst .. contents:: Introduction @@ -48,7 +48,7 @@ Save this code to the file "greetings.nim". Now compile and run it:: nim compile --run greetings.nim -With the `--run` `switch `_ Nim +With the ``--run`` `switch `_ Nim executes the file automatically after compilation. You can give your program command-line arguments by appending them after the filename:: @@ -63,12 +63,12 @@ To compile a release version use:: nim c -d:release greetings.nim By default, the Nim compiler generates a large number of runtime checks -aiming for your debugging pleasure. With `-d:release` some checks are +aiming for your debugging pleasure. With ``-d:release`` some checks are `turned off and optimizations are turned on `_. -For benchmarking or production code, use the `-d:release` switch. -For comparing the performance with unsafe languages like C, use the `-d:danger` switch +For benchmarking or production code, use the ``-d:release`` switch. +For comparing the performance with unsafe languages like C, use the ``-d:danger`` switch in order to get meaningful, comparable results. Otherwise Nim might be handicapped by checks that are **not even available** for C. @@ -535,7 +535,7 @@ differences: that belong to the first condition that evaluates to `true`. The `when` statement is useful for writing platform-specific code, similar to -the `#ifdef` construct in the C programming language. +the `#ifdef`:c: construct in the C programming language. Statements and indentation @@ -596,7 +596,7 @@ Procedures To define new commands like `echo `_ and `readLine `_ in the examples, the concept of a -`procedure` is needed. (Some languages call them *methods* or *functions*.) +*procedure* is needed. (Some languages call them *methods* or *functions*.) In Nim new procedures are defined with the `proc` keyword: .. code-block:: nim @@ -802,7 +802,7 @@ Operators --------- The Nim library makes heavy use of overloading - one reason for this is that each operator like `+` is just an overloaded proc. The parser lets you -use operators in `infix notation` (`a + b`) or `prefix notation` (`+ a`). +use operators in *infix notation* (`a + b`) or *prefix notation* (`+ a`). An infix operator always receives two arguments, a prefix operator always one. (Postfix operators are not possible, because this would be ambiguous: does `a @ @ b` mean `(a) @ (@b)` or `(a@) @ (b)`? It always means @@ -810,7 +810,7 @@ An infix operator always receives two arguments, a prefix operator always one. Apart from a few built-in keyword operators such as `and`, `or`, `not`, operators always consist of these characters: -``+ - * \ / < > = @ $ ~ & % ! ? ^ . |`` +`+ - * \ / < > = @ $ ~ & % ! ? ^ . |` User-defined operators are allowed. Nothing stops you from defining your own `@!?+~` operator, but doing so may reduce readability. @@ -952,7 +952,7 @@ evaluation. For example: Characters ---------- -The `character type` is called `char`. Its size is always one byte, so +The *character type* is called `char`. Its size is always one byte, so it cannot represent most UTF-8 characters, but it *can* represent one of the bytes that makes up a multi-byte UTF-8 character. The reason for this is efficiency: for the overwhelming majority of use-cases, @@ -1220,7 +1220,7 @@ Arrays can be constructed using `[]`: The notation `x[i]` is used to access the i-th element of `x`. Array access is always bounds checked (at compile-time or at runtime). These checks can be disabled via pragmas or invoking the compiler with the -`--bound_checks:off` command line switch. +``--bound_checks:off`` command line switch. Arrays are value types, like any other Nim type. The assignment operator copies the whole array contents. @@ -1620,11 +1620,11 @@ variables! For example: path = "usr/local/nimc.html" (dir, name, ext) = splitFile(path) baddir, badname, badext = splitFile(path) - echo dir # outputs `usr/local` - echo name # outputs `nimc` - echo ext # outputs `.html` + echo dir # outputs "usr/local" + echo name # outputs "nimc" + echo ext # outputs ".html" # All the following output the same line: - # `(dir: usr/local, name: nimc, ext: .html)` + # "(dir: usr/local, name: nimc, ext: .html)" echo baddir echo badname echo badext @@ -1711,7 +1711,7 @@ Examples are provided in the `manual `_. Modules ======= -Nim supports splitting a program into pieces with a module concept. +Nim supports splitting a program into pieces with a *module* concept. Each module is in its own file. Modules enable `information hiding`:idx: and `separate compilation`:idx:. A module may gain access to the symbols of another module by using the `import`:idx: statement. Only top-level symbols that are marked diff --git a/doc/tut2.rst b/doc/tut2.rst index 3aef6bb0f..2624b2690 100644 --- a/doc/tut2.rst +++ b/doc/tut2.rst @@ -1,5 +1,3 @@ -.. default-role:: code - ====================== Nim Tutorial (Part II) ====================== @@ -7,6 +5,8 @@ Nim Tutorial (Part II) :Author: Andreas Rumpf :Version: |nimversion| +.. default-role:: code +.. include:: rstcommon.rst .. contents:: @@ -175,7 +175,8 @@ Method call syntax ------------------ There is a syntactic sugar for calling routines: -The syntax `obj.method(args)` can be used instead of `method(obj, args)`. +The syntax `obj.methodName(args)` can be used +instead of `methodName(obj, args)`. If there are no remaining arguments, the parentheses can be omitted: `obj.len` (instead of `len(obj)`). @@ -296,7 +297,7 @@ because it makes more sense for them to use static binding, but `eval` is a method because it requires dynamic binding. **Note:** Starting from Nim 0.20, to use multi-methods one must explicitly pass -`--multimethods:on` when compiling. +``--multimethods:on`` when compiling. In a multi-method all parameters that have an object type are used for the dispatching: @@ -459,7 +460,7 @@ If you want to add the `{.raises.}` pragma to existing code, the compiler can also help you. You can add the `{.effects.}` pragma statement to your proc and the compiler will output all inferred effects up to that point (exception tracking is part of Nim's effect system). Another more roundabout way to -find out the list of exceptions raised by a proc is to use the Nim `doc` +find out the list of exceptions raised by a proc is to use the Nim ``doc`` command which generates documentation for a whole module and decorates all procs with the list of raised exceptions. You can read more about Nim's `effect system and related pragmas in the manual `_. diff --git a/doc/tut3.rst b/doc/tut3.rst index 358a9b45e..6b5a3b5ca 100644 --- a/doc/tut3.rst +++ b/doc/tut3.rst @@ -1,5 +1,3 @@ -.. default-role:: code - ======================= Nim Tutorial (Part III) ======================= @@ -7,6 +5,8 @@ Nim Tutorial (Part III) :Author: Arne Döring :Version: |nimversion| +.. default-role:: code +.. include:: rstcommon.rst .. contents:: From a5b30c94c2dcd0f17b37685d9ce96eee959ba554 Mon Sep 17 00:00:00 2001 From: shirleyquirk <31934565+shirleyquirk@users.noreply.github.com> Date: Sun, 11 Apr 2021 12:07:23 +0100 Subject: [PATCH 1912/3768] [feature] add arbitrary code execution to strformat (#17694) * changed parser to ignore ':' within parens * Update strformat.nim * Update lib/pure/strformat.nim Co-authored-by: flywind Co-authored-by: Andreas Rumpf --- lib/pure/strformat.nim | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/lib/pure/strformat.nim b/lib/pure/strformat.nim index 7ab359038..c232c8a46 100644 --- a/lib/pure/strformat.nim +++ b/lib/pure/strformat.nim @@ -74,6 +74,20 @@ runnableExamples: assert fmt"{123.456:13e}" == " 1.234560e+02" ##[ +# Expressions +]## +runnableExamples: + let x = 3.14 + assert fmt"{(if x!=0: 1.0/x else: 0):.5}" == "0.31847" + assert fmt"""{(block: + var res: string + for i in 1..15: + res.add (if i mod 15 == 0: "FizzBuzz" + elif i mod 5 == 0: "Buzz" + elif i mod 3 == 0: "Fizz" + else: $i) & " " + res)}""" == "1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz " +##[ # Debugging strings `fmt"{expr=}"` expands to `fmt"expr={expr}"` namely the text of the expression, @@ -557,7 +571,7 @@ proc strformatImpl(pattern: NimNode; openChar, closeChar: char): NimNode = # XXX: https://github.com/nim-lang/Nim/issues/8405 # When compiling with -d:useNimRtl, certain procs such as `count` from the strutils # module are not accessible at compile-time: - let expectedGrowth = when defined(useNimRtl): 0 else: count(f, '{') * 10 + let expectedGrowth = when defined(useNimRtl): 0 else: count(f, openChar) * 10 result.add newVarStmt(res, newCall(bindSym"newStringOfCap", newLit(f.len + expectedGrowth))) var strlit = "" @@ -573,8 +587,12 @@ proc strformatImpl(pattern: NimNode; openChar, closeChar: char): NimNode = strlit = "" var subexpr = "" - while i < f.len and f[i] != closeChar and f[i] != ':': - if f[i] == '=': + var inParens = 0 + while i < f.len and f[i] != closeChar and (f[i] != ':' or inParens!=0): + case f[i] + of '(': inc inParens + of ')': dec inParens + of '=': let start = i inc i i += f.skipWhitespace(i) @@ -582,10 +600,11 @@ proc strformatImpl(pattern: NimNode; openChar, closeChar: char): NimNode = result.add newCall(bindSym"add", res, newLit(subexpr & f[start ..< i])) else: subexpr.add f[start ..< i] - else: - subexpr.add f[i] - inc i - + continue + else: discard + subexpr.add f[i] + inc i + var x: NimNode try: x = parseExpr(subexpr) @@ -608,11 +627,11 @@ proc strformatImpl(pattern: NimNode; openChar, closeChar: char): NimNode = doAssert false, "invalid format string: missing '}'" result.add newCall(formatSym, res, x, newLit(options)) elif f[i] == closeChar: - if f[i+1] == closeChar: + if i Date: Sun, 11 Apr 2021 07:25:41 -0500 Subject: [PATCH 1913/3768] iterable[T] (#17196) * fix failing test toSeq in manual which now works * changelog * reject proc fn(a: iterable) * add iterable to spec * remove MCS/UFCS limitation that now works --- changelog.md | 5 + compiler/ast.nim | 6 +- compiler/condsyms.nim | 1 + compiler/jsgen.nim | 2 +- compiler/liftdestructors.nim | 2 +- compiler/linter.nim | 2 +- compiler/semcall.nim | 10 +- compiler/semdata.nim | 2 +- compiler/semexprs.nim | 9 +- compiler/semtypes.nim | 13 +++ compiler/sigmatch.nim | 31 ++++-- compiler/typeallowed.nim | 3 + compiler/types.nim | 11 ++- compiler/vmdeps.nim | 1 + doc/manual.rst | 43 +++++---- lib/system.nim | 4 + testament/lib/stdtest/testutils.nim | 6 ++ tests/types/titerable.nim | 143 ++++++++++++++++++++++++++++ 18 files changed, 254 insertions(+), 40 deletions(-) create mode 100644 tests/types/titerable.nim diff --git a/changelog.md b/changelog.md index a2879833f..2f1d2d816 100644 --- a/changelog.md +++ b/changelog.md @@ -302,6 +302,10 @@ - `nim e` now accepts arbitrary file extensions for the nimscript file, although `.nims` is still the preferred extension in general. +- Added `iterable[T]` type class to match called iterators, which enables writing: + `template fn(a: iterable)` instead of `template fn(a: untyped)` + + ## Compiler changes - Added `--declaredlocs` to show symbol declaration location in messages. @@ -341,6 +345,7 @@ - `--hint:CC` now goes to stderr (like all other hints) instead of stdout. + ## Tool changes - The rst parser now supports markdown table syntax. diff --git a/compiler/ast.nim b/compiler/ast.nim index fffe08cb7..155af6375 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -440,10 +440,12 @@ type tyVoid # now different from tyEmpty, hurray! + tyIterable static: # remind us when TTypeKind stops to fit in a single 64-bit word - assert TTypeKind.high.ord <= 63 + # assert TTypeKind.high.ord <= 63 + discard const tyPureObject* = tyTuple @@ -664,7 +666,7 @@ type mDefault, mUnown, mIsolate, mAccessEnv, mReset, mArray, mOpenArray, mRange, mSet, mSeq, mVarargs, mRef, mPtr, mVar, mDistinct, mVoid, mTuple, - mOrdinal, + mOrdinal, mIterableType, mInt, mInt8, mInt16, mInt32, mInt64, mUInt, mUInt8, mUInt16, mUInt32, mUInt64, mFloat, mFloat32, mFloat64, mFloat128, diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 9ba97a8ed..bdecd7e53 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -132,3 +132,4 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasSpellSuggest") defineSymbol("nimHasCustomLiterals") defineSymbol("nimHasUnifiedTuple") + defineSymbol("nimHasIterable") diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 3fc7708bf..ca716b1c0 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -211,7 +211,7 @@ proc mapType(typ: PType): TJSTypeKind = else: result = etyNone of tyProc: result = etyProc of tyCString: result = etyString - of tyConcept: doAssert false + of tyConcept, tyIterable: doAssert false proc mapType(p: PProc; typ: PType): TJSTypeKind = result = mapType(typ) diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index bb24fed34..436665827 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -908,7 +908,7 @@ proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) = of tyOrdinal, tyRange, tyInferred, tyGenericInst, tyAlias, tySink: fillBody(c, lastSon(t), body, x, y) - of tyConcept: doAssert false + of tyConcept, tyIterable: doAssert false proc produceSymDistinctType(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp; info: TLineInfo; diff --git a/compiler/linter.nim b/compiler/linter.nim index 9af4f468e..83dd84699 100644 --- a/compiler/linter.nim +++ b/compiler/linter.nim @@ -40,7 +40,7 @@ proc beautifyName(s: string, k: TSymKind): string = "pointer", "float", "csize", "csize_t", "cdouble", "cchar", "cschar", "cshort", "cu", "nil", "typedesc", "auto", "any", "range", "openarray", "varargs", "set", "cfloat", "ref", "ptr", - "untyped", "typed", "static", "sink", "lent", "type", "owned"]: + "untyped", "typed", "static", "sink", "lent", "type", "owned", "iterable"]: result.add s[i] else: result.add toUpperAscii(s[i]) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 99979c382..8f29bdf32 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -62,7 +62,7 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode, best, alt: var TCandidate, errors: var CandidateErrors, diagnosticsFlag: bool, - errorsEnabled: bool) = + errorsEnabled: bool, flags: TExprFlags) = var o: TOverloadIter var sym = initOverloadIter(o, c, headSymbol) var scope = o.lastOverloadScope @@ -95,7 +95,11 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode, matches(c, n, orig, z) if z.state == csMatch: # little hack so that iterators are preferred over everything else: - if sym.kind == skIterator: inc(z.exactMatches, 200) + if sym.kind == skIterator: + if not (efWantIterator notin flags and efWantIterable in flags): + inc(z.exactMatches, 200) + else: + dec(z.exactMatches, 200) case best.state of csEmpty, csNoMatch: best = z of csMatch: @@ -356,7 +360,7 @@ proc resolveOverloads(c: PContext, n, orig: PNode, template pickBest(headSymbol) = pickBestCandidate(c, headSymbol, n, orig, initialBinding, filter, result, alt, errors, efExplain in flags, - errorsEnabled) + errorsEnabled, flags) pickBest(f) let overloadsState = result.state diff --git a/compiler/semdata.nim b/compiler/semdata.nim index c655047e2..51b2cdea1 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -54,7 +54,7 @@ type inst*: PInstantiation TExprFlag* = enum - efLValue, efWantIterator, efInTypeof, + efLValue, efWantIterator, efWantIterable, efInTypeof, efNeedStatic, # Use this in contexts where a static value is mandatory efPreferStatic, diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index c0aa168ef..256c008c9 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -853,7 +853,7 @@ proc semStaticExpr(c: PContext, n: PNode): PNode = proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode, flags: TExprFlags): PNode = - if flags*{efInTypeof, efWantIterator} != {}: + if flags*{efInTypeof, efWantIterator, efWantIterable} != {}: # consider: 'for x in pReturningArray()' --> we don't want the restriction # to 'skIterator' anymore; skIterator is preferred in sigmatch already # for typeof support. @@ -877,6 +877,11 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode, # error correction, prevents endless for loop elimination in transf. # See bug #2051: result[0] = newSymNode(errorSym(c, n)) + elif callee.kind == skIterator: + if efWantIterable in flags: + let typ = newTypeS(tyIterable, c) + rawAddSon(typ, result.typ) + result.typ = typ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode @@ -1364,7 +1369,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = onUse(n[1].info, s) return - n[0] = semExprWithType(c, n[0], flags+{efDetermineType}) + n[0] = semExprWithType(c, n[0], flags+{efDetermineType, efWantIterable}) #restoreOldStyleType(n[0]) var i = considerQuotedIdent(c, n[1], n) var ty = n[0].typ diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index b34dc36e3..34a90a3f2 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -356,6 +356,15 @@ proc semArray(c: PContext, n: PNode, prev: PType): PType = localError(c.config, n.info, errArrayExpectsTwoTypeParams) result = newOrPrevType(tyError, prev, c) +proc semIterableType(c: PContext, n: PNode, prev: PType): PType = + result = newOrPrevType(tyIterable, prev, c) + if n.len == 2: + let base = semTypeNode(c, n[1], nil) + addSonSkipIntLit(result, base, c.idgen) + else: + localError(c.config, n.info, errXExpectsOneTypeParam % "iterable") + result = newOrPrevType(tyError, prev, c) + proc semOrdinal(c: PContext, n: PNode, prev: PType): PType = result = newOrPrevType(tyOrdinal, prev, c) if n.len == 2: @@ -1844,6 +1853,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = of mRange: result = semRange(c, n, prev) of mSet: result = semSet(c, n, prev) of mOrdinal: result = semOrdinal(c, n, prev) + of mIterableType: result = semIterableType(c, n, prev) of mSeq: result = semContainer(c, n, tySequence, "seq", prev) if optSeqDestructors in c.config.globalOptions: @@ -2067,6 +2077,9 @@ proc processMagicType(c: PContext, m: PSym) = of mOrdinal: setMagicIntegral(c.config, m, tyOrdinal, szUncomputedSize) rawAddSon(m.typ, newTypeS(tyNone, c)) + of mIterableType: + setMagicIntegral(c.config, m, tyIterable, 0) + rawAddSon(m.typ, newTypeS(tyNone, c)) of mPNimrodNode: incl m.typ.flags, tfTriggersCompileTime incl m.typ.flags, tfCheckedForDestructor diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 87f7c273b..767f34dda 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1119,6 +1119,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, return if x >= isGeneric: isGeneric else: x return isNone + of tyIterable: + if f.kind != tyIterable: return isNone of tyNot: case f.kind of tyNot: @@ -1421,6 +1423,15 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, of tyAlias, tySink: result = typeRel(c, lastSon(f), a, flags) + of tyIterable: + if a.kind == tyIterable: + if f.len == 1: + result = typeRel(c, lastSon(f), lastSon(a), flags) + else: + # f.len = 3, for some reason + result = isGeneric + else: + result = isNone of tyGenericInst: var prev = PType(idTableGet(c.bindings, f)) let origF = f @@ -2270,14 +2281,18 @@ proc prepareOperand(c: PContext; formal: PType; a: PNode): PNode = # a.typ == nil is valid result = a elif a.typ.isNil: - # XXX This is unsound! 'formal' can differ from overloaded routine to - # overloaded routine! - let flags = {efDetermineType, efAllowStmt} - #if formal.kind == tyIter: {efDetermineType, efWantIterator} - #else: {efDetermineType, efAllowStmt} - #elif formal.kind == tyTyped: {efDetermineType, efWantStmt} - #else: {efDetermineType} - result = c.semOperand(c, a, flags) + if formal.kind == tyIterable: + let flags = {efDetermineType, efAllowStmt, efWantIterator, efWantIterable} + result = c.semOperand(c, a, flags) + else: + # XXX This is unsound! 'formal' can differ from overloaded routine to + # overloaded routine! + let flags = {efDetermineType, efAllowStmt} + #if formal.kind == tyIterable: {efDetermineType, efWantIterator} + #else: {efDetermineType, efAllowStmt} + #elif formal.kind == tyTyped: {efDetermineType, efWantStmt} + #else: {efDetermineType} + result = c.semOperand(c, a, flags) else: result = a considerGenSyms(c, result) diff --git a/compiler/typeallowed.nim b/compiler/typeallowed.nim index 9075d9e38..270e7c028 100644 --- a/compiler/typeallowed.nim +++ b/compiler/typeallowed.nim @@ -97,6 +97,9 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, result = nil of tyUntyped, tyTyped: if kind notin {skParam, skResult} or taNoUntyped in flags: result = t + of tyIterable: + if kind notin {skParam} or taNoUntyped in flags: result = t + # tyIterable is only for templates and macros. of tyStatic: if kind notin {skParam}: result = t of tyVoid: diff --git a/compiler/types.nim b/compiler/types.nim index 1dbbc3261..ce4812506 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -460,8 +460,8 @@ const "lent ", "varargs[$1]", "UncheckedArray[$1]", "Error Type", "BuiltInTypeClass", "UserTypeClass", "UserTypeClassInst", "CompositeTypeClass", "inferred", - "and", "or", "not", "any", "static", "TypeFromExpr", "out ", - "void"] + "and", "or", "not", "any", "static", "TypeFromExpr", "concept", # xxx bugfix + "void", "iterable"] const preferToResolveSymbols = {preferName, preferTypeName, preferModuleInfo, preferGenericArg, preferResolved, preferMixed} @@ -645,6 +645,11 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = of tyDistinct: result = "distinct " & typeToString(t[0], if prefer == preferModuleInfo: preferModuleInfo else: preferTypeName) + of tyIterable: + # xxx factor this pattern + result = "iterable" + if t.len > 0: + result &= "[" & typeToString(t[0]) & ']' of tyTuple: # we iterate over t.sons here, because t.n may be nil if t.n != nil: @@ -1191,7 +1196,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = result = sameTypeOrNilAux(a[0], b[0], c) and sameValue(a.n[0], b.n[0]) and sameValue(a.n[1], b.n[1]) - of tyGenericInst, tyAlias, tyInferred: + of tyGenericInst, tyAlias, tyInferred, tyIterable: cycleCheck() result = sameTypeAux(a.lastSon, b.lastSon, c) of tyNone: result = false diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim index a9157bc03..6191f9459 100644 --- a/compiler/vmdeps.nim +++ b/compiler/vmdeps.nim @@ -287,6 +287,7 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo; of tyAnd: result = mapTypeToBracket("and", mAnd, t, info) of tyOr: result = mapTypeToBracket("or", mOr, t, info) of tyNot: result = mapTypeToBracket("not", mNot, t, info) + of tyIterable: result = mapTypeToBracket("iterable", mIterableType, t, info) of tyAnything: result = atomicType("anything", mNone) of tyInferred: assert false of tyStatic, tyFromExpr: diff --git a/doc/manual.rst b/doc/manual.rst index 1325d1c5f..ea444a0ac 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -2645,6 +2645,28 @@ Varargs matching See `Varargs <#types-varargs>`_. +iterable +-------- + +A called `iterator` yielding type `T` can be passed to a template or macro via +a parameter typed as `untyped` (for unresolved expressions) or the type class +`iterable` or `iterable[T]` (after type checking and overload resolution). + +.. code-block:: nim + iterator iota(n: int): int = + for i in 0..`_ for passing iterators to templates and macros. + Converters ========== @@ -5591,24 +5616,6 @@ is used to invoke templates/macros: unknownIdentifier.declareVar -Another common example is this: - -.. code-block:: nim - :test: "nim c $1" - :status: 1 - - from std/sequtils import toSeq - - iterator something: string = - yield "Hello" - yield "World" - - var info = something().toSeq - -The problem here is that the compiler already decided that `something()` as -an iterator is not callable in this context before `toSeq` gets its -chance to convert it into a sequence. - It is also not possible to use fully qualified identifiers with module symbol in method call syntax. The order in which the dot operator binds to symbols prohibits this. diff --git a/lib/system.nim b/lib/system.nim index bd9ae2762..6e84aca66 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -123,6 +123,10 @@ proc defined*(x: untyped): bool {.magic: "Defined", noSideEffect, compileTime.} ## # Do here programmer friendly expensive sanity checks. ## # Put here the normal code +when defined(nimHasIterable): + type + iterable*[T] {.magic: IterableType.} ## Represents an expression that yields `T` + when defined(nimHashOrdinalFixed): type Ordinal*[T] {.magic: Ordinal.} ## Generic ordinal type. Includes integer, diff --git a/testament/lib/stdtest/testutils.nim b/testament/lib/stdtest/testutils.nim index 58d136696..abffff24c 100644 --- a/testament/lib/stdtest/testutils.nim +++ b/testament/lib/stdtest/testutils.nim @@ -79,3 +79,9 @@ template whenVMorJs*(bodyIf, bodyElse) = else: when defined(js): bodyIf else: bodyElse + +template accept*(a) = + doAssert compiles(a) + +template reject*(a) = + doAssert not compiles(a) diff --git a/tests/types/titerable.nim b/tests/types/titerable.nim new file mode 100644 index 000000000..b0713961d --- /dev/null +++ b/tests/types/titerable.nim @@ -0,0 +1,143 @@ +discard """ + targets: "c js" +""" + +from stdtest/testutils import accept, reject, whenVMorJs + +# toSeq-like templates + +template toSeq2(a: iterable): auto = + var ret: seq[typeof(a)] + for ai in a: ret.add ai + ret + +template toSeq3(a: iterable[string]): auto = + var ret: seq[typeof(a)] + for ai in a: ret.add ai + ret + +template toSeq4[T](a: iterable[T]): auto = + var ret: seq[typeof(a)] + for ai in a: ret.add ai + ret + +template toSeq5[T: SomeInteger](a: iterable[T]): auto = + var ret: seq[typeof(a)] + for ai in a: ret.add ai + ret + +template toSeq6(a: iterable[int | float]): auto = + var ret: seq[typeof(a)] + for ai in a: ret.add ai + ret + +template toSeq7(a: iterable[seq]): auto = + var ret: seq[typeof(a)] + for ai in a: ret.add ai + ret + +template fn7b(a: untyped) = discard +template fn7c(a: typed) = discard +template fn7d(a: auto) = discard +template fn7e[T](a: T) = discard + +template fn8a(a: iterable) = discard +template fn8b[T](a: iterable[T]) = discard +template fn8c(a: iterable[int]) = discard + +template bad1 = + template fn4(a: int, b: iterable[float, int]) = + discard + +template bad2 = + proc fn4(a: iterable) = discard + +template bad3 = + proc fn4(a: iterable[int]) = discard + +template good4 = + template fn1(a: iterable) = discard + template fn2(a: iterable[int]) = discard + +# iterators +iterator iota(n: int): auto = + for i in 0.. Date: Sun, 11 Apr 2021 17:37:32 +0200 Subject: [PATCH 1914/3768] IC: integrity checking (#17695) * IC: integrity checking: the plumbing code * progress * progress + bugfix (yes, the code already found a bug) * implemented integrity checking --- compiler/ic/bitabs.nim | 4 + compiler/ic/ic.nim | 3 +- compiler/ic/integrity.nim | 151 ++++++++++++++++++++++++++++++++++++++ compiler/main.nim | 4 +- compiler/semdata.nim | 3 + compiler/semstmts.nim | 2 +- koch.nim | 2 +- testament/categories.nim | 2 +- 8 files changed, 166 insertions(+), 5 deletions(-) create mode 100644 compiler/ic/integrity.nim diff --git a/compiler/ic/bitabs.nim b/compiler/ic/bitabs.nim index 1f75b7759..0bce30b5d 100644 --- a/compiler/ic/bitabs.nim +++ b/compiler/ic/bitabs.nim @@ -36,6 +36,10 @@ const template idToIdx(x: LitId): int = x.int - idStart +proc hasLitId*[T](t: BiTable[T]; x: LitId): bool = + let idx = idToIdx(x) + result = idx >= 0 and idx < t.vals.len + proc enlarge[T](t: var BiTable[T]) = var n: seq[LitId] newSeq(n, len(t.keys) * 2) diff --git a/compiler/ic/ic.nim b/compiler/ic/ic.nim index 46bb63e07..6769c6ffe 100644 --- a/compiler/ic/ic.nim +++ b/compiler/ic/ic.nim @@ -31,7 +31,7 @@ type moduleFlags: TSymFlags includes: seq[(LitId, string)] # first entry is the module filename itself imports: seq[LitId] # the modules this module depends on - toReplay: PackedTree # pragmas and VM specific state to replay. + toReplay*: PackedTree # pragmas and VM specific state to replay. topLevel*: PackedTree # top level statements bodies*: PackedTree # other trees. Referenced from typ.n and sym.ast by their position. #producedGenerics*: Table[GenericKey, SymId] @@ -167,6 +167,7 @@ proc addExported*(c: var PackedEncoder; m: var PackedModule; s: PSym) = m.exports.add((nameId, s.itemId.item)) proc addConverter*(c: var PackedEncoder; m: var PackedModule; s: PSym) = + assert c.thisModule == s.itemId.module m.converters.add(s.itemId.item) proc addTrmacro*(c: var PackedEncoder; m: var PackedModule; s: PSym) = diff --git a/compiler/ic/integrity.nim b/compiler/ic/integrity.nim new file mode 100644 index 000000000..b4545de94 --- /dev/null +++ b/compiler/ic/integrity.nim @@ -0,0 +1,151 @@ +# +# +# The Nim Compiler +# (c) Copyright 2021 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Integrity checking for a set of .rod files. +## The set must cover a complete Nim project. + +import std / sets +import ".." / [ast, modulegraphs] +import packed_ast, bitabs, ic + +type + CheckedContext = object + g: ModuleGraph + thisModule: int32 + checkedSyms: HashSet[ItemId] + checkedTypes: HashSet[ItemId] + +proc checkType(c: var CheckedContext; typeId: PackedItemId) +proc checkForeignSym(c: var CheckedContext; symId: PackedItemId) +proc checkNode(c: var CheckedContext; tree: PackedTree; n: NodePos) + +proc checkTypeObj(c: var CheckedContext; typ: PackedType) = + for child in typ.types: + checkType(c, child) + if typ.n != emptyNodeId: + checkNode(c, c.g.packed[c.thisModule].fromDisk.bodies, NodePos typ.n) + if typ.sym != nilItemId: + checkForeignSym(c, typ.sym) + if typ.owner != nilItemId: + checkForeignSym(c, typ.owner) + checkType(c, typ.typeInst) + +proc checkType(c: var CheckedContext; typeId: PackedItemId) = + if typeId == nilItemId: return + let itemId = translateId(typeId, c.g.packed, c.thisModule, c.g.config) + if not c.checkedTypes.containsOrIncl(itemId): + let oldThisModule = c.thisModule + c.thisModule = itemId.module + checkTypeObj c, c.g.packed[itemId.module].fromDisk.sh.types[itemId.item] + c.thisModule = oldThisModule + +proc checkSym(c: var CheckedContext; s: PackedSym) = + if s.name != LitId(0): + assert c.g.packed[c.thisModule].fromDisk.sh.strings.hasLitId s.name + checkType c, s.typ + if s.ast != emptyNodeId: + checkNode(c, c.g.packed[c.thisModule].fromDisk.bodies, NodePos s.ast) + if s.owner != nilItemId: + checkForeignSym(c, s.owner) + +proc checkLocalSym(c: var CheckedContext; item: int32) = + let itemId = ItemId(module: c.thisModule, item: item) + if not c.checkedSyms.containsOrIncl(itemId): + checkSym c, c.g.packed[c.thisModule].fromDisk.sh.syms[item] + +proc checkForeignSym(c: var CheckedContext; symId: PackedItemId) = + let itemId = translateId(symId, c.g.packed, c.thisModule, c.g.config) + if not c.checkedSyms.containsOrIncl(itemId): + let oldThisModule = c.thisModule + c.thisModule = itemId.module + checkSym c, c.g.packed[itemId.module].fromDisk.sh.syms[itemId.item] + c.thisModule = oldThisModule + +proc checkNode(c: var CheckedContext; tree: PackedTree; n: NodePos) = + if tree[n.int].typeId != nilItemId: + checkType(c, tree[n.int].typeId) + case n.kind + of nkEmpty, nkNilLit, nkType, nkNilRodNode: + discard + of nkIdent: + assert c.g.packed[c.thisModule].fromDisk.sh.strings.hasLitId n.litId + of nkSym: + checkLocalSym(c, tree.nodes[n.int].operand) + of directIntLit: + discard + of externIntLit: + assert c.g.packed[c.thisModule].fromDisk.sh.integers.hasLitId n.litId + of nkStrLit..nkTripleStrLit: + assert c.g.packed[c.thisModule].fromDisk.sh.strings.hasLitId n.litId + of nkFloatLit..nkFloat128Lit: + assert c.g.packed[c.thisModule].fromDisk.sh.floats.hasLitId n.litId + of nkModuleRef: + let (n1, n2) = sons2(tree, n) + assert n1.kind == nkInt32Lit + assert n2.kind == nkInt32Lit + checkForeignSym(c, PackedItemId(module: n1.litId, item: tree.nodes[n2.int].operand)) + else: + for n0 in sonsReadonly(tree, n): + checkNode(c, tree, n0) + +proc checkTree(c: var CheckedContext; t: PackedTree) = + for p in allNodes(t): checkNode(c, t, p) + +proc checkLocalSymIds(c: var CheckedContext; m: PackedModule; symIds: seq[int32]) = + for symId in symIds: + assert symId >= 0 and symId < m.sh.syms.len, $symId & " " & $m.sh.syms.len + +proc checkModule(c: var CheckedContext; m: PackedModule) = + # We check that: + # - Every symbol references existing types and symbols. + # - Every tree node references existing types and symbols. + for i in 0..high(m.sh.syms): + checkLocalSym c, int32(i) + + checkTree c, m.toReplay + checkTree c, m.topLevel + + for e in m.exports: + assert e[1] >= 0 and e[1] < m.sh.syms.len + assert e[0] == m.sh.syms[e[1]].name + + for e in m.compilerProcs: + assert e[1] >= 0 and e[1] < m.sh.syms.len + assert e[0] == m.sh.syms[e[1]].name + + checkLocalSymIds c, m, m.converters + checkLocalSymIds c, m, m.methods + checkLocalSymIds c, m, m.trmacros + checkLocalSymIds c, m, m.pureEnums + #[ + To do: Check all these fields: + + reexports*: seq[(LitId, PackedItemId)] + macroUsages*: seq[(PackedItemId, PackedLineInfo)] + + typeInstCache*: seq[(PackedItemId, PackedItemId)] + procInstCache*: seq[PackedInstantiation] + attachedOps*: seq[(TTypeAttachedOp, PackedItemId, PackedItemId)] + methodsPerType*: seq[(PackedItemId, int, PackedItemId)] + enumToStringProcs*: seq[(PackedItemId, PackedItemId)] + ]# + +proc checkIntegrity*(g: ModuleGraph) = + var c = CheckedContext(g: g) + for i in 0..high(g.packed): + # case statement here to enforce exhaustive checks. + case g.packed[i].status + of undefined: + discard "nothing to do" + of loading: + assert false, "cannot check integrity: Module still loading" + of stored, storing, outdated, loaded: + c.thisModule = int32 i + checkModule(c, g.packed[i].fromDisk) + diff --git a/compiler/main.nim b/compiler/main.nim index 26a19063f..3c5018546 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -21,7 +21,7 @@ import modules, modulegraphs, tables, lineinfos, pathutils, vmprofiler -import ic / cbackend +import ic / [cbackend, integrity] from ic / ic import rodViewer when not defined(leanCompiler): @@ -93,6 +93,8 @@ proc commandCompileToC(graph: ModuleGraph) = if conf.symbolFiles == disabledSf: cgenWriteModules(graph.backend, conf) else: + if isDefined(conf, "nimIcIntegrityChecks"): + checkIntegrity(graph) generateCode(graph) # graph.backend can be nil under IC when nothing changed at all: if graph.backend != nil: diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 51b2cdea1..daf1265e8 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -341,6 +341,9 @@ proc addConverter*(c: PContext, conv: LazySym) = assert conv.sym != nil if inclSym(c.converters, conv.sym): add(c.graph.ifaces[c.module.position].converters, conv) + +proc addConverterDef*(c: PContext, conv: LazySym) = + addConverter(c, conv) if c.config.symbolFiles != disabledSf: addConverter(c.encoder, c.packedRepr, conv.sym) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index bbfd49e2a..4c6a745f6 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -2093,7 +2093,7 @@ proc semConverterDef(c: PContext, n: PNode): PNode = var t = s.typ if t[0] == nil: localError(c.config, n.info, errXNeedsReturnType % "converter") if t.len != 2: localError(c.config, n.info, "a converter takes exactly one argument") - addConverter(c, LazySym(sym: s)) + addConverterDef(c, LazySym(sym: s)) proc semMacroDef(c: PContext, n: PNode): PNode = checkSonsLen(n, bodyPos + 1, c.config) diff --git a/koch.nim b/koch.nim index 3b00d3564..44d38589b 100644 --- a/koch.nim +++ b/koch.nim @@ -492,7 +492,7 @@ proc icTest(args: string) = for fragment in content.split("#!EDIT!#"): let file = inp.replace(".nim", "_temp.nim") writeFile(file, fragment) - var cmd = nimExe & " cpp --ic:on --listcmd " + var cmd = nimExe & " cpp --ic:on -d:nimIcIntegrityChecks --listcmd " if i == 0: cmd.add "-f " cmd.add quoteShell(file) diff --git a/testament/categories.nim b/testament/categories.nim index af6331dde..618046287 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -488,7 +488,7 @@ proc icTests(r: var TResults; testsDir: string, cat: Category, options: string) tooltests = ["compiler/nim.nim", "tools/nimgrep.nim"] writeOnly = " --incremental:writeonly " readOnly = " --incremental:readonly " - incrementalOn = " --incremental:on " + incrementalOn = " --incremental:on -d:nimIcIntegrityChecks " template test(x: untyped) = testSpecWithNimcache(r, makeRawTest(file, x & options, cat), nimcache) From e710b9cf33939fa20230ab3029ceb735e0a669ad Mon Sep 17 00:00:00 2001 From: Araq Date: Sun, 11 Apr 2021 20:14:44 +0200 Subject: [PATCH 1915/3768] macrocache.nim: removed trailing whitespace --- lib/core/macrocache.nim | 48 ++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/lib/core/macrocache.nim b/lib/core/macrocache.nim index e376ad87f..d4b103793 100644 --- a/lib/core/macrocache.nim +++ b/lib/core/macrocache.nim @@ -10,10 +10,10 @@ ## This module provides an API for macros to collect compile-time information ## across module boundaries. It should be used instead of global `{.compileTime.}` ## variables as those break incremental compilation. -## +## ## The main feature of this module is that if you create `CacheTable`s or ## any other `Cache` types with the same name in different modules, their -## content will be shared, meaning that you can fill a `CacheTable` in +## content will be shared, meaning that you can fill a `CacheTable` in ## one module, and iterate over its contents in another. runnableExamples: @@ -22,20 +22,20 @@ runnableExamples: const mcTable = CacheTable"myTable" const mcSeq = CacheSeq"mySeq" const mcCounter = CacheCounter"myCounter" - + static: # add new key "val" with the value `myval` let myval = newLit("hello ic") mcTable["val"] = myval assert mcTable["val"].kind == nnkStrLit - + # Can access the same cache from different static contexts # All the information is retained static: # get value from `mcTable` and add it to `mcSeq` mcSeq.add(mcTable["val"]) assert mcSeq.len == 1 - + static: assert mcSeq[0].strVal == "hello ic" @@ -49,7 +49,7 @@ type ## Compile-time sequence of `NimNode`s. CacheTable* = distinct string ## Compile-time table of key-value pairs. - ## + ## ## Keys are `string`s and values are `NimNode`s. CacheCounter* = distinct string ## Compile-time counter, uses `int` for storing the count. @@ -80,22 +80,22 @@ proc add*(s: CacheSeq; value: NimNode) {.magic: "NcsAdd".} = runnableExamples: import std/macros const mySeq = CacheSeq"addTest" - + static: mySeq.add(newLit(5)) mySeq.add(newLit("hello ic")) - + assert mySeq.len == 2 assert mySeq[1].strVal == "hello ic" -proc incl*(s: CacheSeq; value: NimNode) {.magic: "NcsIncl".} = +proc incl*(s: CacheSeq; value: NimNode) {.magic: "NcsIncl".} = ## Adds `value` to `s`. - ## + ## ## .. hint:: This doesn't do anything if `value` is already in `s`. runnableExamples: import std/macros const mySeq = CacheSeq"inclTest" - + static: mySeq.incl(newLit(5)) mySeq.incl(newLit(5)) @@ -113,7 +113,7 @@ proc len*(s: CacheSeq): int {.magic: "NcsLen".} = let val = newLit("helper") mySeq.add(val) assert mySeq.len == 1 - + mySeq.add(val) assert mySeq.len == 2 @@ -132,20 +132,20 @@ iterator items*(s: CacheSeq): NimNode = runnableExamples: import std/macros const myseq = CacheSeq"itemsTest" - + static: myseq.add(newLit(5)) myseq.add(newLit(42)) - + for val in myseq: # check that all values in `myseq` are int literals assert val.kind == nnkIntLit - + for i in 0 ..< len(s): yield s[i] proc `[]=`*(t: CacheTable; key: string, value: NimNode) {.magic: "NctPut".} = - ## Inserts a `(key, value)` pair into `t`. - ## + ## Inserts a `(key, value)` pair into `t`. + ## ## .. warning:: `key` has to be unique! Assigning `value` to a `key` that is already ## in the table will result in a compiler error. runnableExamples: @@ -155,11 +155,11 @@ proc `[]=`*(t: CacheTable; key: string, value: NimNode) {.magic: "NctPut".} = static: # assign newLit(5) to the key "value" mcTable["value"] = newLit(5) - - # check that we can get the value back - assert mcTable["value"].kind == nnkIntLit -proc len*(t: CacheTable): int {.magic: "NctLen".} = + # check that we can get the value back + assert mcTable["value"].kind == nnkIntLit + +proc len*(t: CacheTable): int {.magic: "NctLen".} = ## Returns the number of elements in `t`. runnableExamples: import std/macros @@ -177,7 +177,7 @@ proc `[]`*(t: CacheTable; key: string): NimNode {.magic: "NctGet".} = const mcTable = CacheTable"subTest" static: mcTable["toAdd"] = newStmtList() - + # get the NimNode back assert mcTable["toAdd"].kind == nnkStmtList @@ -189,8 +189,8 @@ iterator pairs*(t: CacheTable): (string, NimNode) = runnableExamples: import std/macros const mytabl = CacheTable"values" - - static: + + static: mytabl["intVal"] = newLit(5) mytabl["otherVal"] = newLit(6) for key, val in mytabl: From cae183915490846a0ec7dbcd52b29a74c7c59f05 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 11 Apr 2021 21:41:24 +0200 Subject: [PATCH 1916/3768] IC: added tcompiletime_counter test case (#17698) --- tests/ic/mcompiletime_counter.nim | 15 +++++++++++++++ tests/ic/tcompiletime_counter.nim | 24 ++++++++++++++++++++++++ 2 files changed, 39 insertions(+) create mode 100644 tests/ic/mcompiletime_counter.nim create mode 100644 tests/ic/tcompiletime_counter.nim diff --git a/tests/ic/mcompiletime_counter.nim b/tests/ic/mcompiletime_counter.nim new file mode 100644 index 000000000..6fc7b3f2a --- /dev/null +++ b/tests/ic/mcompiletime_counter.nim @@ -0,0 +1,15 @@ + +import std/macros +import std/macrocache + +const myCounter = CacheCounter"myCounter" + +proc getUniqueId*(): int {.compileTime.} = + inc myCounter + result = myCounter.value + +static: + myCounter.inc(3) + assert myCounter.value == 3 + + diff --git a/tests/ic/tcompiletime_counter.nim b/tests/ic/tcompiletime_counter.nim new file mode 100644 index 000000000..695a6ec27 --- /dev/null +++ b/tests/ic/tcompiletime_counter.nim @@ -0,0 +1,24 @@ +discard """ + output: '''id 4''' +""" + +import mcompiletime_counter + +const intId = getUniqueId() + +echo "id ", intId + +#!EDIT!# + +discard """ + output: '''id 4 5''' +""" + +import mcompiletime_counter + +const + intId = getUniqueId() + floatId = getUniqueId() + +echo "id ", intId, " ", floatId + From 0bc943ad54d707f93d47a6d52bf7d445c45388d8 Mon Sep 17 00:00:00 2001 From: shirleyquirk <31934565+shirleyquirk@users.noreply.github.com> Date: Mon, 12 Apr 2021 06:32:37 +0100 Subject: [PATCH 1917/3768] followup strformat PR. backslash escapes, tests, docs (#17700) * Allow use of colons inside fmt allowing colons inside fmt by replacing the format specifier delimiter lets arbitrary nim code be run within fmt expressions. Co-authored-by: flywind * formatting,documentation,backslash escapes Adding support for evaluating expressions by special-casing parentheses causes this regression: `&"""{ "(hello)" }"""` no longer parses. In addition, code such as &"""{(if open: '(' else: ')')}""" wouldn't work. To enable that, as well as the use of, e.g. Table constructors inside curlies, I've added backslash escapes. This also means that if/for/etc statements, unparenthesized, will work, if the colons are escaped, but i've left that under-documented. It's not exactly elegant having two types of escape, but I believe it's the least bad option. * changelog * added json strformat test * pulled my thumb out and wrote a parser Co-authored-by: Andreas Rumpf Co-authored-by: flywind --- changelogs/changelog_X_XX_X.md | 1 + lib/pure/strformat.nim | 33 ++++++++++++++++--- tests/stdlib/tstrformat.nim | 60 +++++++++++++++++++++++++++++++++- 3 files changed, 89 insertions(+), 5 deletions(-) diff --git a/changelogs/changelog_X_XX_X.md b/changelogs/changelog_X_XX_X.md index 77b421f33..d68ef4c20 100644 --- a/changelogs/changelog_X_XX_X.md +++ b/changelogs/changelog_X_XX_X.md @@ -13,6 +13,7 @@ The changes should go to changelog.md! - Changed `example.foo` to take additional `bar` parameter. +- Added support for evaluating parenthesised expressions in strformat ## Language changes diff --git a/lib/pure/strformat.nim b/lib/pure/strformat.nim index c232c8a46..736b4d501 100644 --- a/lib/pure/strformat.nim +++ b/lib/pure/strformat.nim @@ -144,6 +144,19 @@ An expression like `&"{key} is {value:arg} {{z}}"` is transformed into: Parts of the string that are enclosed in the curly braces are interpreted as Nim code, to escape a `{` or `}`, double it. +Within a curly expression,however, '{','}', must be escaped with a backslash. + +To enable evaluating Nim expressions within curlies, inside parentheses +colons do not need to be escaped. +]## + +runnableExamples: + let x = "hello" + assert fmt"""{ "\{(" & x & ")\}" }""" == "{(hello)}" + assert fmt"""{{({ x })}}""" == "{(hello)}" + assert fmt"""{ $(\{x:1,"world":2\}) }""" == """[("hello", 1), ("world", 2)]""" + +##[ `&` delegates most of the work to an open overloaded set of `formatValue` procs. The required signature for a type `T` that supports formatting is usually `proc formatValue(result: var string; x: T; specifier: string)`. @@ -289,6 +302,7 @@ keep the hygiene of `myTemplate`, and we do not want `arg1` to be injected into the context where `myTemplate` is expanded, everything is wrapped in a `block`. + # Future directions A curly expression with commas in it like `{x, argA, argB}` could be @@ -588,10 +602,21 @@ proc strformatImpl(pattern: NimNode; openChar, closeChar: char): NimNode = var subexpr = "" var inParens = 0 - while i < f.len and f[i] != closeChar and (f[i] != ':' or inParens!=0): + var inSingleQuotes = false + var inDoubleQuotes = false + template notEscaped:bool = f[i-1]!='\\' + while i < f.len and f[i] != closeChar and (f[i] != ':' or inParens != 0): case f[i] - of '(': inc inParens - of ')': dec inParens + of '\\': + if i < f.len-1 and f[i+1] in {openChar,closeChar,':'}: inc i + of '\'': + if not inDoubleQuotes and notEscaped: inSingleQuotes = not inSingleQuotes + of '\"': + if notEscaped: inDoubleQuotes = not inDoubleQuotes + of '(': + if not (inSingleQuotes or inDoubleQuotes): inc inParens + of ')': + if not (inSingleQuotes or inDoubleQuotes): dec inParens of '=': let start = i inc i @@ -604,7 +629,7 @@ proc strformatImpl(pattern: NimNode; openChar, closeChar: char): NimNode = else: discard subexpr.add f[i] inc i - + var x: NimNode try: x = parseExpr(subexpr) diff --git a/tests/stdlib/tstrformat.nim b/tests/stdlib/tstrformat.nim index 86100e421..1863d3138 100644 --- a/tests/stdlib/tstrformat.nim +++ b/tests/stdlib/tstrformat.nim @@ -1,7 +1,7 @@ # xxx: test js target import genericstrformat -import std/[strformat, strutils, times] +import std/[strformat, strutils, times, tables, json] proc main() = block: # issue #7632 @@ -284,6 +284,20 @@ proc main() = doAssert fmt"{123.456=:>13e}" == "123.456= 1.234560e+02" doAssert fmt"{123.456=:13e}" == "123.456= 1.234560e+02" + let x = 3.14 + doAssert fmt"{(if x!=0: 1.0/x else: 0):.5}" == "0.31847" + doAssert fmt"""{(block: + var res: string + for i in 1..15: + res.add (if i mod 15 == 0: "FizzBuzz" + elif i mod 5 == 0: "Buzz" + elif i mod 3 == 0: "Fizz" + else: $i) & " " + res)}""" == "1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz " + + doAssert fmt"""{ "\{(" & msg & ")\}" }""" == "{(hello)}" + doAssert fmt"""{{({ msg })}}""" == "{(hello)}" + doAssert fmt"""{ $(\{msg:1,"world":2\}) }""" == """[("hello", 1), ("world", 2)]""" block: # tests for debug format string var name = "hello" let age = 21 @@ -496,6 +510,50 @@ proc main() = block: # test low(int64) doAssert &"{low(int64):-}" == "-9223372036854775808" + block: #expressions plus formatting + doAssert fmt"{if true\: 123.456 else\: 0=:>9.3f}" == "if true: 123.456 else: 0= 123.456" + doAssert fmt"{(if true: 123.456 else: 0)=}" == "(if true: 123.456 else: 0)=123.456" + doAssert fmt"{if true\: 123.456 else\: 0=:9.3f}" == "if true: 123.456 else: 0= 123.456" + doAssert fmt"{(if true: 123.456 else: 0)=:9.4f}" == "(if true: 123.456 else: 0)= 123.4560" + doAssert fmt"{(if true: 123.456 else: 0)=:>9.0f}" == "(if true: 123.456 else: 0)= 123." + doAssert fmt"{if true\: 123.456 else\: 0=:<9.4f}" == "if true: 123.456 else: 0=123.4560 " + doAssert fmt"""{(case true + of false: 0.0 + of true: 123.456)=:e}""" == """(case true + of false: 0.0 + of true: 123.456)=1.234560e+02""" + + doAssert fmt"""{block\: + var res = 0.000123456 + for _ in 0..5\: + res *= 10 + res=:>13e}""" == """block: + var res = 0.000123456 + for _ in 0..5: + res *= 10 + res= 1.234560e+02""" + #side effects + var x = 5 + doAssert fmt"{(x=7;123.456)=:13e}" == "(x=7;123.456)= 1.234560e+02" + doAssert x==7 + block: #curly bracket expressions and tuples + proc formatValue(result: var string; value:Table|bool|JsonNode; specifier:string) = result.add $value + + doAssert fmt"""{\{"a"\:1,"b"\:2\}.toTable() = }""" == """{"a":1,"b":2}.toTable() = {"a": 1, "b": 2}""" + doAssert fmt"""{(\{3: (1,"hi",0.9),4: (4,"lo",1.1)\}).toTable()}""" == """{3: (1, "hi", 0.9), 4: (4, "lo", 1.1)}""" + doAssert fmt"""{ (%* \{"name": "Isaac", "books": ["Robot Dreams"]\}) }""" == """{"name":"Isaac","books":["Robot Dreams"]}""" + doAssert """%( \%\* {"name": "Isaac"})*""".fmt('%','*') == """{"name":"Isaac"}""" + block: #parens in quotes that fool my syntax highlighter + doAssert fmt"{(if true: ')' else: '(')}" == ")" + doAssert fmt"{(if true: ']' else: ')')}" == "]" + doAssert fmt"""{(if true: "\")\"" else: "\"(")}""" == """")"""" + doAssert &"""{(if true: "\")" else: "")}""" == "\")" + doAssert &"{(if true: \"\\\")\" else: \"\")}" == "\")" + doAssert fmt"""{(if true: "')" else: "")}""" == "')" + doAssert fmt"""{(if true: "'" & "'" & ')' else: "")}""" == "'')" + doAssert &"""{(if true: "'" & "'" & ')' else: "")}""" == "'')" + doAssert &"{(if true: \"\'\" & \"'\" & ')' else: \"\")}" == "'')" + doAssert fmt"""{(if true: "'" & ')' else: "")}""" == "')" # xxx static: main() main() From 8b5769133cf30dae2a6ed15aa3ba9d2f2be713ec Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 12 Apr 2021 14:58:46 +0800 Subject: [PATCH 1918/3768] remove unnecessary assignment (#17702) --- lib/pure/coro.nim | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/pure/coro.nim b/lib/pure/coro.nim index ccd9fba4d..e3ed6aadd 100644 --- a/lib/pure/coro.nim +++ b/lib/pure/coro.nim @@ -274,7 +274,6 @@ proc start*(c: proc(), stacksize: int = defaultStackSize): CoroutineRef {.discar coro.execContext = CreateFiberEx(stacksize, stacksize, FIBER_FLAG_FLOAT_SWITCH, (proc(p: pointer) {.stdcall.} = runCurrentTask()), nil) - coro.stack.size = stacksize else: coro = cast[CoroutinePtr](alloc0(sizeof(Coroutine) + stacksize)) coro.stack.top = cast[pointer](cast[ByteAddress](coro) + sizeof(Coroutine)) From 4f5709e32648435ff253b056ac7066240c39d2b7 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 13 Apr 2021 07:23:29 +0200 Subject: [PATCH 1919/3768] IC: serialization bugfix (#17701) --- compiler/ic/ic.nim | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/ic/ic.nim b/compiler/ic/ic.nim index 6769c6ffe..32bacba70 100644 --- a/compiler/ic/ic.nim +++ b/compiler/ic/ic.nim @@ -260,9 +260,9 @@ proc storeTypeLater(t: PType; c: var PackedEncoder; m: var PackedModule): Packed assert t.uniqueId.module >= 0 assert t.uniqueId.item > 0 return PackedItemId(module: toLitId(t.uniqueId.module.FileIndex, c, m), item: t.uniqueId.item) - assert t.itemId.module >= 0 + assert t.uniqueId.module >= 0 assert t.uniqueId.item > 0 - result = PackedItemId(module: toLitId(t.itemId.module.FileIndex, c, m), item: t.uniqueId.item) + result = PackedItemId(module: toLitId(t.uniqueId.module.FileIndex, c, m), item: t.uniqueId.item) addMissing(c, t) proc storeSymLater(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId = @@ -318,9 +318,9 @@ proc storeType(t: PType; c: var PackedEncoder; m: var PackedModule): PackedItemI # fill the reserved slot, nothing else: m.sh.types[t.uniqueId.item] = p - assert t.itemId.module >= 0 + assert t.uniqueId.module >= 0 assert t.uniqueId.item > 0 - result = PackedItemId(module: toLitId(t.itemId.module.FileIndex, c, m), item: t.uniqueId.item) + result = PackedItemId(module: toLitId(t.uniqueId.module.FileIndex, c, m), item: t.uniqueId.item) proc toPackedLib(l: PLib; c: var PackedEncoder; m: var PackedModule): PackedLib = ## the plib hangs off the psym via the .annex field From e92c78a9ac0ce0f7a9b2a0fada0c2c2557c40ae2 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 13 Apr 2021 00:33:28 -0700 Subject: [PATCH 1920/3768] add number literal jsbigints.big (#17707) --- lib/std/jsbigints.nim | 20 +++++++++++++------- tests/stdlib/tjsbigints.nim | 6 +++--- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/lib/std/jsbigints.nim b/lib/std/jsbigints.nim index 96ba90649..27df8fdad 100644 --- a/lib/std/jsbigints.nim +++ b/lib/std/jsbigints.nim @@ -13,16 +13,22 @@ func big*(integer: SomeInteger): JsBigInt {.importjs: "BigInt(#)".} = doAssert 0b1111100111.big == 0o1747.big and 0o1747.big == 999.big when nimvm: doAssert false, "JsBigInt can not be used at compile-time nor static context" else: discard -func big*(integer: cstring): JsBigInt {.importjs: "BigInt(#)".} = +func `'big`*(num: cstring): JsBigInt {.importjs: "BigInt(#)".} = ## Constructor for `JsBigInt`. runnableExamples: - doAssert big"-1" == big"1" - big"2" + doAssert -1'big == 1'big - 2'big # supports decimal, binary, octal, hex: - doAssert big"12" == 12.big - doAssert big"0b101" == 0b101.big - doAssert big"0o701" == 0o701.big - doAssert big"0xdeadbeaf" == 0xdeadbeaf.big - doAssert big"0xffffffffffffffff" == (1.big shl 64.big) - 1.big + doAssert -12'big == big"-12" + doAssert 12'big == 12.big + doAssert 0b101'big == 0b101.big + doAssert 0o701'big == 0o701.big + doAssert 0xdeadbeaf'big == 0xdeadbeaf.big + doAssert 0xffffffffffffffff'big == (1'big shl 64'big) - 1'big + doAssert not compiles(static(12'big)) + when nimvm: doAssert false, "JsBigInt can not be used at compile-time nor static context" else: discard + +func big*(integer: cstring): JsBigInt {.importjs: "BigInt(#)".} = + ## Alias for `'big` when nimvm: doAssert false, "JsBigInt can not be used at compile-time nor static context" else: discard func toCstring*(this: JsBigInt; radix: 2..36): cstring {.importjs: "#.toString(#)".} = diff --git a/tests/stdlib/tjsbigints.nim b/tests/stdlib/tjsbigints.nim index 34c5ddfbf..fcf699c67 100644 --- a/tests/stdlib/tjsbigints.nim +++ b/tests/stdlib/tjsbigints.nim @@ -26,7 +26,7 @@ doAssert big1.toCstring(10) == "2147483647".cstring doAssert big2 ** big3 == big(443556) var huge = big"999999999999999999999999999999999999999999999999999999999999999999999999999999999999999" huge.inc -huge = huge + big"-999999999999999999999999999999999999999999999999999999999999999999999999999999999999999" +huge = huge + -999999999999999999999999999999999999999999999999999999999999999999999999999999999999999'big doAssert huge == big"1" var list: seq[JsBigInt] for i in big"0" .. big"5": @@ -40,7 +40,7 @@ for i in big"0" ..< big"5": doAssert list == @[big"0", big"1", big"2", big"3", big"4"] block: - let b = big"2" - doAssert -b ** big"3" == big"-8" + let b = 2'big + doAssert -b ** 3'big == -8'big doAssert -b ** big"2" == big"4" # not -4 because of precedence doAssert -big"3" == big"-3" From 8e6b87a917a5d09a70d1e29f0b06915c29fcac6b Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 13 Apr 2021 10:43:16 +0200 Subject: [PATCH 1921/3768] IC: code cleanup (#17708) --- compiler/ic/ic.nim | 57 ++++++++++------------------------------------ 1 file changed, 12 insertions(+), 45 deletions(-) diff --git a/compiler/ic/ic.nim b/compiler/ic/ic.nim index 32bacba70..e882b7a85 100644 --- a/compiler/ic/ic.nim +++ b/compiler/ic/ic.nim @@ -254,41 +254,31 @@ proc storeTypeLater(t: PType; c: var PackedEncoder; m: var PackedModule): Packed # we only write one tree into m.bodies after the other. if t.isNil: return nilItemId - if t.uniqueId.module != c.thisModule: - # XXX Assert here that it already was serialized in the foreign module! - # it is a foreign type: - assert t.uniqueId.module >= 0 - assert t.uniqueId.item > 0 - return PackedItemId(module: toLitId(t.uniqueId.module.FileIndex, c, m), item: t.uniqueId.item) assert t.uniqueId.module >= 0 assert t.uniqueId.item > 0 result = PackedItemId(module: toLitId(t.uniqueId.module.FileIndex, c, m), item: t.uniqueId.item) - addMissing(c, t) + if t.uniqueId.module == c.thisModule: + # the type belongs to this module, so serialize it here, eventually. + addMissing(c, t) proc storeSymLater(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId = if s.isNil: return nilItemId assert s.itemId.module >= 0 - if s.itemId.module != c.thisModule: - # XXX Assert here that it already was serialized in the foreign module! - # it is a foreign symbol: - assert s.itemId.module >= 0 - return PackedItemId(module: toLitId(s.itemId.module.FileIndex, c, m), item: s.itemId.item) assert s.itemId.module >= 0 result = PackedItemId(module: toLitId(s.itemId.module.FileIndex, c, m), item: s.itemId.item) - addMissing(c, s) + if s.itemId.module == c.thisModule: + # the sym belongs to this module, so serialize it here, eventually. + addMissing(c, s) proc storeType(t: PType; c: var PackedEncoder; m: var PackedModule): PackedItemId = ## serialize a ptype if t.isNil: return nilItemId - if t.uniqueId.module != c.thisModule: - # XXX Assert here that it already was serialized in the foreign module! - # it is a foreign type: - assert t.uniqueId.module >= 0 - assert t.uniqueId.item > 0 - return PackedItemId(module: toLitId(t.uniqueId.module.FileIndex, c, m), item: t.uniqueId.item) + assert t.uniqueId.module >= 0 + assert t.uniqueId.item > 0 + result = PackedItemId(module: toLitId(t.uniqueId.module.FileIndex, c, m), item: t.uniqueId.item) - if not c.typeMarker.containsOrIncl(t.uniqueId.item): + if t.uniqueId.module == c.thisModule and not c.typeMarker.containsOrIncl(t.uniqueId.item): if t.uniqueId.item >= m.sh.types.len: setLen m.sh.types, t.uniqueId.item+1 @@ -296,20 +286,9 @@ proc storeType(t: PType; c: var PackedEncoder; m: var PackedModule): PackedItemI size: t.size, align: t.align, nonUniqueId: t.itemId.item, paddingAtEnd: t.paddingAtEnd, lockLevel: t.lockLevel) storeNode(p, t, n) - - when false: - for op, s in pairs t.attachedOps: - c.addMissing s - p.attachedOps[op] = s.safeItemId(c, m) - p.typeInst = t.typeInst.storeType(c, m) for kid in items t.sons: p.types.add kid.storeType(c, m) - - when false: - for i, s in items t.methods: - c.addMissing s - p.methods.add (i, s.safeItemId(c, m)) c.addMissing t.sym p.sym = t.sym.safeItemId(c, m) c.addMissing t.owner @@ -318,10 +297,6 @@ proc storeType(t: PType; c: var PackedEncoder; m: var PackedModule): PackedItemI # fill the reserved slot, nothing else: m.sh.types[t.uniqueId.item] = p - assert t.uniqueId.module >= 0 - assert t.uniqueId.item > 0 - result = PackedItemId(module: toLitId(t.uniqueId.module.FileIndex, c, m), item: t.uniqueId.item) - proc toPackedLib(l: PLib; c: var PackedEncoder; m: var PackedModule): PackedLib = ## the plib hangs off the psym via the .annex field if l.isNil: return @@ -336,14 +311,9 @@ proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId if s.isNil: return nilItemId assert s.itemId.module >= 0 + result = PackedItemId(module: toLitId(s.itemId.module.FileIndex, c, m), item: s.itemId.item) - if s.itemId.module != c.thisModule: - # XXX Assert here that it already was serialized in the foreign module! - # it is a foreign symbol: - assert s.itemId.module >= 0 - return PackedItemId(module: toLitId(s.itemId.module.FileIndex, c, m), item: s.itemId.item) - - if not c.symMarker.containsOrIncl(s.itemId.item): + if s.itemId.module == c.thisModule and not c.symMarker.containsOrIncl(s.itemId.item): if s.itemId.item >= m.sh.syms.len: setLen m.sh.syms, s.itemId.item+1 @@ -375,9 +345,6 @@ proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId # fill the reserved slot, nothing else: m.sh.syms[s.itemId.item] = p - assert s.itemId.module >= 0 - result = PackedItemId(module: toLitId(s.itemId.module.FileIndex, c, m), item: s.itemId.item) - proc addModuleRef(n: PNode; ir: var PackedTree; c: var PackedEncoder; m: var PackedModule) = ## add a remote symbol reference to the tree let info = n.info.toPackedInfo(c, m) From fe77c3c72fe1fde4f41286158ba612c02083dfe2 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 13 Apr 2021 14:16:19 -0700 Subject: [PATCH 1922/3768] megatest: allow duplicate file names, show compilation progress (#17713) * megatest: allow duplicate file names * megatest: show compilation progress --- testament/categories.nim | 17 +++++++++++------ tests/misc/tjoinable.nim | 3 +++ tests/testament/tjoinable.nim | 1 + 3 files changed, 15 insertions(+), 6 deletions(-) create mode 100644 tests/misc/tjoinable.nim diff --git a/testament/categories.nim b/testament/categories.nim index 618046287..ee5c46519 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -571,6 +571,11 @@ proc quoted(a: string): string = proc runJoinedTest(r: var TResults, cat: Category, testsDir: string) = ## returns a list of tests that have problems + #[ + xxx create a reusable megatest API after abstracting out testament specific code, + refs https://github.com/timotheecour/Nim/issues/655 + and https://github.com/nim-lang/gtk2/pull/28; it's useful in other contexts. + ]# var specs: seq[TSpec] = @[] for kind, dir in walkDir(testsDir): assert testsDir.startsWith(testsDir) @@ -595,16 +600,16 @@ proc runJoinedTest(r: var TResults, cat: Category, testsDir: string) = var megatest: string # xxx (minor) put outputExceptedFile, outputGottenFile, megatestFile under here or `buildDir` var outDir = nimcacheDir(testsDir / "megatest", "", targetC) - const marker = "megatest:processing: " - + template toMarker(file, i): string = + "megatest:processing: [$1] $2" % [$i, file] for i, runSpec in specs: let file = runSpec.file - let file2 = outDir / ("megatest_$1.nim" % $i) + let file2 = outDir / ("megatest_a_$1.nim" % $i) # `include` didn't work with `trecmod2.nim`, so using `import` - let code = "echo \"$1\", $2\n" % [marker, quoted(file)] + let code = "echo $1\nstatic: echo \"CT:\", $1\n" % [toMarker(file, i).quoted] createDir(file2.parentDir) writeFile(file2, code) - megatest.add "import $1\nimport $2\n" % [quoted(file2), quoted(file)] + megatest.add "import $1\nimport $2 as megatest_b_$3\n" % [file2.quoted, file.quoted, $i] let megatestFile = testsDir / "megatest.nim" # so it uses testsDir / "config.nims" writeFile(megatestFile, megatest) @@ -626,7 +631,7 @@ proc runJoinedTest(r: var TResults, cat: Category, testsDir: string) = writeFile(outputGottenFile, buf) var outputExpected = "" for i, runSpec in specs: - outputExpected.add marker & runSpec.file & "\n" + outputExpected.add toMarker(runSpec.file, i) & "\n" if runSpec.output.len > 0: outputExpected.add runSpec.output if not runSpec.output.endsWith "\n": diff --git a/tests/misc/tjoinable.nim b/tests/misc/tjoinable.nim new file mode 100644 index 000000000..f23fca0d4 --- /dev/null +++ b/tests/misc/tjoinable.nim @@ -0,0 +1,3 @@ +# checks that megatest allows duplicate names, see also `tests/testament/tjoinable.nim` +doAssert defined(testing) +doAssert defined(nimMegatest) diff --git a/tests/testament/tjoinable.nim b/tests/testament/tjoinable.nim index 7a0ad7985..c651780de 100644 --- a/tests/testament/tjoinable.nim +++ b/tests/testament/tjoinable.nim @@ -3,6 +3,7 @@ discard """ """ # checks that this is joinable +# checks that megatest allows duplicate names, see also `tests/misc/tjoinable.nim` doAssert defined(testing) doAssert defined(nimMegatest) echo "ok" # intentional to make sure this doesn't prevent `isJoinableSpec` From 840e13deb79db7b072595658e6aa8328d55d7889 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 13 Apr 2021 23:50:15 -0700 Subject: [PATCH 1923/3768] type with same name in different scope now works (#17710) * type with same name in different scope now works * fix tests/enum/tenum.nim which was wrong because it was affected by this bug --- compiler/semstmts.nim | 7 ++- compiler/sighashes.nim | 19 ++++-- tests/ccgbugs/tsamename3.nim | 111 +++++++++++++++++++++++++++++++++++ tests/enum/tenum.nim | 5 +- 4 files changed, 133 insertions(+), 9 deletions(-) create mode 100644 tests/ccgbugs/tsamename3.nim diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 4c6a745f6..2c257bd3d 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1354,8 +1354,11 @@ proc typeSectionFinalPass(c: PContext, n: PNode) = checkConstructedType(c.config, s.info, s.typ) if s.typ.kind in {tyObject, tyTuple} and not s.typ.n.isNil: checkForMetaFields(c, s.typ.n) - # fix bug #5170: ensure locally scoped object types get a unique name: - if s.typ.kind == tyObject and not isTopLevel(c): incl(s.flags, sfGenSym) + + # fix bug #5170, bug #17162, bug #15526: ensure locally scoped types get a unique name: + if s.typ.kind in {tyEnum, tyRef, tyObject} and not isTopLevel(c): + incl(s.flags, sfGenSym) + #instAllTypeBoundOp(c, n.info) diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim index 156bc66d7..f6df422e6 100644 --- a/compiler/sighashes.nim +++ b/compiler/sighashes.nim @@ -151,15 +151,24 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) = c.hashTypeSym(t.sym) else: c.hashSym(t.sym) - if {sfAnon, sfGenSym} * t.sym.flags != {}: + + var symWithFlags: PSym + template hasFlag(sym): bool = + let ret = {sfAnon, sfGenSym} * sym.flags != {} + if ret: symWithFlags = sym + ret + if hasFlag(t.sym) or (t.kind == tyObject and t.owner.kind == skType and t.owner.typ.kind == tyRef and hasFlag(t.owner)): + # for `PFoo:ObjectType`, arising from `type PFoo = ref object` # Generated object names can be identical, so we need to # disambiguate furthermore by hashing the field types and names. if t.n.len > 0: - let oldFlags = t.sym.flags - # Mild hack to prevent endless recursion. - t.sym.flags.excl {sfAnon, sfGenSym} + let oldFlags = symWithFlags.flags + # Hack to prevent endless recursion + # xxx intead, use a hash table to indicate we've already visited a type, which + # would also be more efficient. + symWithFlags.flags.excl {sfAnon, sfGenSym} hashTree(c, t.n, flags + {CoHashTypeInsideNode}) - t.sym.flags = oldFlags + symWithFlags.flags = oldFlags else: # The object has no fields: we _must_ add something here in order to # make the hash different from the one we produce by hashing only the diff --git a/tests/ccgbugs/tsamename3.nim b/tests/ccgbugs/tsamename3.nim new file mode 100644 index 000000000..a69391e5c --- /dev/null +++ b/tests/ccgbugs/tsamename3.nim @@ -0,0 +1,111 @@ +block: # bug #15526 + block: + type Foo = ref object + x1: int + let f1 = Foo(x1: 1) + block: + type Foo = ref object + x2: int + let f2 = Foo(x2: 2) + +block: # ditto + template fn() = + block: + type Foo = ref object + x1: int + let f1 = Foo(x1: 1) + doAssert f1.x1 == 1 + block: + type Foo = ref object + x2: int + let f2 = Foo(x2: 2) + doAssert f2.x2 == 2 + static: fn() + fn() + +block: # bug #17162 + template fn = + var ret: string + block: + type A = enum a0, a1, a2 + for ai in A: + ret.add $ai + block: + type A = enum b0, b1, b2, b3 + for ai in A: + ret.add $ai + doAssert ret == "a0a1a2b0b1b2b3" + + static: fn() # ok + fn() # was bug + +block: # ditto + proc fn = + var ret: string + block: + type A = enum a0, a1, a2 + for ai in A: + ret.add $ai + block: + type A = enum b0, b1, b2, b3 + for ai in A: + ret.add $ai + doAssert ret == "a0a1a2b0b1b2b3" + + static: fn() # ok + fn() # was bug + +block: # bug #5170 + block: + type Foo = object + x1: int + let f1 = Foo(x1: 1) + block: + type Foo = object + x2: int + let f2 = Foo(x2: 2) + +block: # ditto + block: + type Foo = object + bar: bool + var f1: Foo + + block: + type Foo = object + baz: int + var f2: Foo + doAssert f2.baz == 0 + + block: + template fn() = + block: + type Foo = object + x1: int + let f1 = Foo(x1: 1) + doAssert f1.x1 == 1 + block: + type Foo = object + x2: int + let f2 = Foo(x2: 2) + doAssert f2.x2 == 2 + static: fn() + fn() + +when true: # ditto, refs https://github.com/nim-lang/Nim/issues/5170#issuecomment-582712132 + type Foo1 = object # at top level + bar: bool + var f1: Foo1 + + block: + type Foo1 = object + baz: int + var f2: Foo1 + doAssert f2.baz == 0 + +block: # make sure `hashType` doesn't recurse infinitely + type + PFoo = ref object + a, b: PFoo + c: int + var a: PFoo diff --git a/tests/enum/tenum.nim b/tests/enum/tenum.nim index aa338ee2c..97fd4c68c 100644 --- a/tests/enum/tenum.nim +++ b/tests/enum/tenum.nim @@ -6,7 +6,7 @@ ABCDC foo first0second32third64 my value A1my value Bconc2valueCabc4abc -my value A0my value Bconc1valueCabc3valueC +my value A0my value Bconc1valueCabc3abc ''' """ @@ -124,7 +124,8 @@ block tnamedfields: # trick the optimizer with a variable: var x = valueD echo valueA, ord(valueA), valueB, ord(valueB), valueC, valueD, ord(valueD), x - + doAssert $x == $valueD, $x + doAssert $x == "abc", $x block toptions: From 25b4a0ab0a03f35c58bc0dbfff51a344e108bfe3 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 14 Apr 2021 00:44:07 -0700 Subject: [PATCH 1924/3768] testament --verbose: prints commands; gitignore ic artifacts (#17716) --- .gitignore | 7 +++++-- testament/testament.nim | 14 +++++++++++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index e71fdf352..a22a4cd03 100644 --- a/.gitignore +++ b/.gitignore @@ -72,14 +72,17 @@ testament.db .*/ ~* -# testament cruft; TODO: generate these in a gitignore'd dir in the first place. +# testament cruft; TODO: generate these in a gitignore'd dir (./build) in the first place. testresults/ test.txt /test.ini tweeter.db tweeter_test.db -megatest.nim + +/tests/megatest.nim +/tests/ic/*_temp.nim + /outputExpected.txt /outputGotten.txt diff --git a/testament/testament.nim b/testament/testament.nim index 462c0a57c..ee7f67484 100644 --- a/testament/testament.nim +++ b/testament/testament.nim @@ -21,6 +21,11 @@ from lib/stdtest/specialpaths import splitTestFile var useColors = true var backendLogging = true var simulate = false +var optVerbose = false + +proc verboseCmd(cmd: string) = + if optVerbose: + echo "executing: ", cmd const failString* = "FAIL: " # ensures all failures can be searched with 1 keyword in CI logs @@ -38,7 +43,8 @@ Command: Arguments: arguments are passed to the compiler Options: - --print also print results to the console + --print print results to the console + --verbose print commands (compiling and running tests) --simulate see what tests would be run but don't run them (for debugging) --failing only show failing/ignored tests --targets:"c cpp js objc" run tests for specified targets (default: all) @@ -113,6 +119,7 @@ proc execCmdEx2(command: string, args: openArray[string]; workingDir, input: str for arg in args: result.cmdLine.add ' ' result.cmdLine.add quoteShell(arg) + verboseCmd(result.cmdLine) var p = startProcess(command, workingDir = workingDir, args = args, options = {poStdErrToStdOut, poUsePath}) var outp = outputStream(p) @@ -155,6 +162,7 @@ proc callCompiler(cmdTemplate, filename, options, nimcache: string, let c = prepareTestArgs(cmdTemplate, filename, options, nimcache, target, extraOptions) result.cmd = quoteShellCommand(c) + verboseCmd(c.quoteShellCommand) var p = startProcess(command = c[0], args = c[1 .. ^1], options = {poStdErrToStdOut, poUsePath}) let outp = p.outputStream @@ -491,7 +499,6 @@ proc testSpecHelper(r: var TResults, test: var TTest, expected: TSpec, valgrindOptions.add "--leak-check=yes" args = valgrindOptions & exeCmd & args exeCmd = "valgrind" - # xxx honor `testament --verbose` here var (_, buf, exitCode) = execCmdEx2(exeCmd, args, input = expected.input) # Treat all failure codes from nodejs as 1. Older versions of nodejs used # to return other codes, but for us it is sufficient to know that it's not 0. @@ -663,7 +670,8 @@ proc main() = p.next() while p.kind in {cmdLongOption, cmdShortOption}: case p.key.normalize - of "print", "verbose": optPrintResults = true + of "print": optPrintResults = true + of "verbose": optVerbose = true of "failing": optFailing = true of "pedantic": discard # deadcode refs https://github.com/nim-lang/Nim/issues/16731 of "targets": From 3b47a689cfa56d9b54cd1b92dc1b57d3e4094937 Mon Sep 17 00:00:00 2001 From: zah Date: Wed, 14 Apr 2021 13:10:01 +0300 Subject: [PATCH 1925/3768] Remove the use of usrToCell in gcMark [backport:1.2] (#17709) * Remove the use of usrToCell in gcMark [backport:1.2] Recently, we've discovered a GC crash resulting from inlining of the memory allocation procs that allowed the compiler to avoid maintaining any references to the "user pointer" on the stack. Instead, a "cell pointer" appeared there and all field accesses were performed with adjusted offsets. This interfered with the ability of the GC to mark the correct cell in the conservative stack scans which lead to premature collection of objects. More details here: https://github.com/status-im/Nim/commit/af69b3ceae16281efd45cbee4ce1bedd14282304 This commit closes another theoretical loophole that may lead to the same problem. If a short proc is accessing both the object and its reference count in a short sequence of instructions, the compiler may be enticed to reduce the number of registers being used by storing only a single pointer to the object and using offsets when reading and writing fields. A perfectly good strategy would be to store only the cell pointer, so the reference count updates can be performed without applying offsets. Accessing the fields of the object requires offsets anyway, but these can be adjusted at compile-time without any loss. Following this strategy will lead to the same problem of marking a wrong cell during the conservative stack scan, leading to premature collection. The problem is avoided by not using `usrToCell` in `gcMark`. Since the cell discovery logic can already handle interior pointers, the user pointers don't need to be adjusted for the GC to function correctly. --- lib/system/gc.nim | 6 +++--- lib/system/gc_ms.nim | 5 ++--- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/lib/system/gc.nim b/lib/system/gc.nim index 039a627ba..e50e80f11 100644 --- a/lib/system/gc.nim +++ b/lib/system/gc.nim @@ -710,16 +710,16 @@ proc collectCycles(gch: var GcHeap) {.raises: [].} = proc gcMark(gch: var GcHeap, p: pointer) {.inline.} = # the addresses are not as cells on the stack, so turn them to cells: sysAssert(allocInv(gch.region), "gcMark begin") - var cell = usrToCell(p) - var c = cast[ByteAddress](cell) + var c = cast[ByteAddress](p) if c >% PageSize: # fast check: does it look like a cell? - var objStart = cast[PCell](interiorAllocatedPtr(gch.region, cell)) + var objStart = cast[PCell](interiorAllocatedPtr(gch.region, p)) if objStart != nil: # mark the cell: incRef(objStart) add(gch.decStack, objStart) when false: + let cell = usrToCell(p) if isAllocatedPtr(gch.region, cell): sysAssert false, "allocated pointer but not interior?" # mark the cell: diff --git a/lib/system/gc_ms.nim b/lib/system/gc_ms.nim index b4221f7fd..c20e93699 100644 --- a/lib/system/gc_ms.nim +++ b/lib/system/gc_ms.nim @@ -455,11 +455,10 @@ proc markGlobals(gch: var GcHeap) = proc gcMark(gch: var GcHeap, p: pointer) {.inline.} = # the addresses are not as cells on the stack, so turn them to cells: - var cell = usrToCell(p) - var c = cast[ByteAddress](cell) + var c = cast[ByteAddress](p) if c >% PageSize: # fast check: does it look like a cell? - var objStart = cast[PCell](interiorAllocatedPtr(gch.region, cell)) + var objStart = cast[PCell](interiorAllocatedPtr(gch.region, p)) if objStart != nil: mark(gch, objStart) From 67e28c07f9cdb22a423b0ab08d4868252acc8ea4 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 14 Apr 2021 16:44:37 +0200 Subject: [PATCH 1926/3768] IC: first steps towards 'nim check --def --ic:on' (#17714) * IC: first steps towards 'nim check --def --ic:on' * IC navigator: deduplicate output lines * IC navigator: progress * IC navigator: use a different nimcache entry * IC navigator: special logic for templates/macros * IC navigator: proper error messages * IC navigator: prepare for testing code; document only what currently works somewhat --- compiler/commands.nim | 3 + compiler/ic/ic.nim | 28 +++++-- compiler/ic/navigator.nim | 147 +++++++++++++++++++++++++++++++++++++ compiler/ic/packed_ast.nim | 4 +- compiler/main.nim | 15 +++- compiler/msgs.nim | 4 +- compiler/options.nim | 7 +- compiler/sem.nim | 1 + compiler/semdata.nim | 10 +++ compiler/semexprs.nim | 1 + doc/advopt.txt | 3 + 11 files changed, 211 insertions(+), 12 deletions(-) create mode 100644 compiler/ic/navigator.nim diff --git a/compiler/commands.nim b/compiler/commands.nim index f36a4f515..e918979fb 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -859,6 +859,9 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "usages": expectNoArg(conf, switch, arg, pass, info) conf.ideCmd = ideUse + of "defusages": + expectNoArg(conf, switch, arg, pass, info) + conf.ideCmd = ideDus of "stdout": processOnOffSwitchG(conf, {optStdout}, arg, pass, info) of "listfullpaths": diff --git a/compiler/ic/ic.nim b/compiler/ic/ic.nim index e882b7a85..4613b9cb8 100644 --- a/compiler/ic/ic.nim +++ b/compiler/ic/ic.nim @@ -89,14 +89,27 @@ proc rememberConfig(c: var PackedEncoder; m: var PackedModule; config: ConfigRef #primConfigFields rem m.cfg = pc +const + debugConfigDiff = defined(debugConfigDiff) + +when debugConfigDiff: + import std / [hashes, tables, intsets, sha1, strutils, sets] + proc configIdentical(m: PackedModule; config: ConfigRef): bool = result = m.definedSymbols == definedSymbolsAsString(config) - #if not result: - # echo "A ", m.definedSymbols, " ", definedSymbolsAsString(config) + when debugConfigDiff: + if not result: + var wordsA = m.definedSymbols.split(Whitespace).toHashSet() + var wordsB = definedSymbolsAsString(config).split(Whitespace).toHashSet() + for c in wordsA - wordsB: + echo "in A but not in B ", c + for c in wordsB - wordsA: + echo "in B but not in A ", c template eq(x) = result = result and m.cfg.x == config.x - #if not result: - # echo "B ", m.cfg.x, " ", config.x + when debugConfigDiff: + if m.cfg.x != config.x: + echo "B ", m.cfg.x, " ", config.x primConfigFields eq proc rememberStartupConfig*(dest: var PackedConfig, config: ConfigRef) = @@ -124,7 +137,7 @@ proc toLitId(x: FileIndex; c: var PackedEncoder; m: var PackedModule): LitId = c.filenames[x] = result c.lastFile = x c.lastLit = result - assert result != LitId(0) + assert result != LitId(0) proc toFileIndex*(x: LitId; m: PackedModule; config: ConfigRef): FileIndex = result = msgs.fileInfoIdx(config, AbsoluteFile m.sh.strings[x]) @@ -144,6 +157,8 @@ proc initEncoder*(c: var PackedEncoder; m: var PackedModule; moduleSym: PSym; co m.bodies = newTreeFrom(m.topLevel) m.toReplay = newTreeFrom(m.topLevel) + c.lastFile = FileIndex(-10) + let thisNimFile = FileIndex c.thisModule var h = msgs.getHash(config, thisNimFile) if h.len == 0: @@ -464,6 +479,9 @@ proc storeInstantiation*(c: var PackedEncoder; m: var PackedModule; s: PSym; i: concreteTypes: t) toPackedGeneratedProcDef(i.sym, c, m) +proc storeExpansion*(c: var PackedEncoder; m: var PackedModule; info: TLineInfo; s: PSym) = + toPackedNode(newSymNode(s, info), m.bodies, c, m) + proc loadError(err: RodFileError; filename: AbsoluteFile; config: ConfigRef;) = case err of cannotOpen: diff --git a/compiler/ic/navigator.nim b/compiler/ic/navigator.nim new file mode 100644 index 000000000..ad2b29f42 --- /dev/null +++ b/compiler/ic/navigator.nim @@ -0,0 +1,147 @@ +# +# +# The Nim Compiler +# (c) Copyright 2021 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Supports the "nim check --ic:on --def --track:FILE,LINE,COL" +## IDE-like features. It uses the set of .rod files to accomplish +## its task. The set must cover a complete Nim project. + +import std / sets + +from os import nil +from std/private/miscdollars import toLocation + +import ".." / [ast, modulegraphs, msgs, options] +import packed_ast, bitabs, ic + +type + NavContext = object + g: ModuleGraph + thisModule: int32 + trackPos: PackedLineInfo + alreadyEmitted: HashSet[string] + outputSep: char # for easier testing, use short filenames and spaces instead of tabs. + +proc isTracked(current, trackPos: PackedLineInfo, tokenLen: int): bool = + if current.file == trackPos.file and current.line == trackPos.line: + let col = trackPos.col + if col >= current.col and col < current.col+tokenLen: + return true + +proc searchLocalSym(c: var NavContext; s: PackedSym; info: PackedLineInfo): bool = + result = s.name != LitId(0) and + isTracked(info, c.trackPos, c.g.packed[c.thisModule].fromDisk.sh.strings[s.name].len) + +proc searchForeignSym(c: var NavContext; s: ItemId; info: PackedLineInfo): bool = + let name = c.g.packed[s.module].fromDisk.sh.syms[s.item].name + result = name != LitId(0) and + isTracked(info, c.trackPos, c.g.packed[s.module].fromDisk.sh.strings[name].len) + +const + EmptyItemId = ItemId(module: -1'i32, item: -1'i32) + +proc search(c: var NavContext; tree: PackedTree): ItemId = + # We use the linear representation here directly: + for i in 0..high(tree.nodes): + case tree.nodes[i].kind + of nkSym: + let item = tree.nodes[i].operand + if searchLocalSym(c, c.g.packed[c.thisModule].fromDisk.sh.syms[item], tree.nodes[i].info): + return ItemId(module: c.thisModule, item: item) + of nkModuleRef: + if tree.nodes[i].info.line == c.trackPos.line and tree.nodes[i].info.file == c.trackPos.file: + let (n1, n2) = sons2(tree, NodePos i) + assert n1.kind == nkInt32Lit + assert n2.kind == nkInt32Lit + let pId = PackedItemId(module: n1.litId, item: tree.nodes[n2.int].operand) + let itemId = translateId(pId, c.g.packed, c.thisModule, c.g.config) + if searchForeignSym(c, itemId, tree.nodes[i].info): + return itemId + else: discard + return EmptyItemId + +proc isDecl(tree: PackedTree; n: NodePos): bool = + # XXX This is not correct yet. + const declarativeNodes = procDefs + {nkMacroDef, nkTemplateDef, + nkLetSection, nkVarSection, nkUsingStmt, nkConstSection, nkTypeSection, + nkIdentDefs, nkEnumTy, nkVarTuple} + result = n.int >= 0 and tree[n.int].kind in declarativeNodes + +proc usage(c: var NavContext; info: PackedLineInfo; isDecl: bool) = + var m = "" + var file = c.g.packed[c.thisModule].fromDisk.sh.strings[info.file] + if c.outputSep == ' ': + file = os.extractFilename file + toLocation(m, file, info.line.int, info.col.int + ColOffset) + if not c.alreadyEmitted.containsOrIncl(m): + echo (if isDecl: "def" else: "usage"), c.outputSep, m + +proc list(c: var NavContext; tree: PackedTree; sym: ItemId) = + for i in 0..high(tree.nodes): + case tree.nodes[i].kind + of nkSym: + let item = tree.nodes[i].operand + if sym.item == item and sym.module == c.thisModule: + usage(c, tree.nodes[i].info, isDecl(tree, parent(NodePos i))) + of nkModuleRef: + let (n1, n2) = sons2(tree, NodePos i) + assert n1.kind == nkInt32Lit + assert n2.kind == nkInt32Lit + let pId = PackedItemId(module: n1.litId, item: tree.nodes[n2.int].operand) + let itemId = translateId(pId, c.g.packed, c.thisModule, c.g.config) + if itemId.item == sym.item and sym.module == itemId.module: + usage(c, tree.nodes[i].info, isDecl(tree, parent(NodePos i))) + else: discard + +proc nav(g: ModuleGraph) = + # translate the track position to a packed position: + let unpacked = g.config.m.trackPos + let mid = unpacked.fileIndex + let fileId = g.packed[int32 mid].fromDisk.sh.strings.getKeyId(toFullPath(g.config, mid)) + + if fileId == LitId(0): + internalError(g.config, unpacked, "cannot find a valid file ID") + return + + var c = NavContext( + g: g, + thisModule: int32 mid, + trackPos: PackedLineInfo(line: unpacked.line, col: unpacked.col, file: fileId), + outputSep: if isDefined(g.config, "nimIcNavigatorTests"): ' ' else: '\t' + ) + var symId = search(c, g.packed[int32 mid].fromDisk.topLevel) + if symId == EmptyItemId: + symId = search(c, g.packed[int32 mid].fromDisk.bodies) + + if symId == EmptyItemId: + localError(g.config, unpacked, "no symbol at this position") + return + + for i in 0..high(g.packed): + # case statement here to enforce exhaustive checks. + case g.packed[i].status + of undefined: + discard "nothing to do" + of loading: + assert false, "cannot check integrity: Module still loading" + of stored, storing, outdated, loaded: + c.thisModule = int32 i + list(c, g.packed[i].fromDisk.topLevel, symId) + list(c, g.packed[i].fromDisk.bodies, symId) + +proc navDefinition*(g: ModuleGraph) = nav(g) +proc navUsages*(g: ModuleGraph) = nav(g) +proc navDefusages*(g: ModuleGraph) = nav(g) + +proc writeRodFiles*(g: ModuleGraph) = + for i in 0..high(g.packed): + case g.packed[i].status + of undefined, loading, stored, loaded: + discard "nothing to do" + of storing, outdated: + closeRodFile(g, g.packed[i].module) diff --git a/compiler/ic/packed_ast.nim b/compiler/ic/packed_ast.nim index 1075664cb..5cc3b1476 100644 --- a/compiler/ic/packed_ast.nim +++ b/compiler/ic/packed_ast.nim @@ -258,9 +258,9 @@ iterator sonsWithoutLast2*(tree: PackedTree; n: NodePos): NodePos = proc parentImpl(tree: PackedTree; n: NodePos): NodePos = # finding the parent of a node is rather easy: var pos = n.int - 1 - while pos >= 0 and isAtom(tree, pos) or (pos + tree.nodes[pos].operand - 1 < n.int): + while pos >= 0 and (isAtom(tree, pos) or (pos + tree.nodes[pos].operand - 1 < n.int)): dec pos - assert pos >= 0, "node has no parent" + #assert pos >= 0, "node has no parent" result = NodePos(pos) template parent*(n: NodePos): NodePos = parentImpl(tree, n) diff --git a/compiler/main.nim b/compiler/main.nim index 3c5018546..475ba7773 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -21,7 +21,7 @@ import modules, modulegraphs, tables, lineinfos, pathutils, vmprofiler -import ic / [cbackend, integrity] +import ic / [cbackend, integrity, navigator] from ic / ic import rodViewer when not defined(leanCompiler): @@ -54,11 +54,20 @@ proc commandGenDepend(graph: ModuleGraph) = ' ' & changeFileExt(project, "dot").string) proc commandCheck(graph: ModuleGraph) = - graph.config.setErrorMaxHighMaybe - defineSymbol(graph.config.symbols, "nimcheck") + let conf = graph.config + conf.setErrorMaxHighMaybe + defineSymbol(conf.symbols, "nimcheck") semanticPasses(graph) # use an empty backend for semantic checking only compileProject(graph) + if conf.symbolFiles != disabledSf: + case conf.ideCmd + of ideDef: navDefinition(graph) + of ideUse: navUsages(graph) + of ideDus: navDefusages(graph) + else: discard + writeRodFiles(graph) + when not defined(leanCompiler): proc commandDoc2(graph: ModuleGraph; json: bool) = handleDocOutputOptions graph.config diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 24855ae18..452b1cd89 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -418,7 +418,9 @@ proc handleError(conf: ConfigRef; msg: TMsgKind, eh: TErrorHandling, s: string) inc(conf.errorCounter) conf.exitcode = 1'i8 if conf.errorCounter >= conf.errorMax: - quit(conf, msg) + # only really quit when we're not in the new 'nim check --def' mode: + if conf.ideCmd == ideNone: + quit(conf, msg) elif eh == doAbort and conf.cmd != cmdIdeTools: quit(conf, msg) elif eh == doRaise: diff --git a/compiler/options.nim b/compiler/options.nim index 7408ce675..9eeffb4fb 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -685,6 +685,11 @@ proc getOsCacheDir(): string = result = getHomeDir() / genSubDir.string proc getNimcacheDir*(conf: ConfigRef): AbsoluteDir = + proc nimcacheSuffix(conf: ConfigRef): string = + if conf.cmd == cmdCheck: "_check" + elif isDefined(conf, "release") or isDefined(conf, "danger"): "_r" + else: "_d" + # XXX projectName should always be without a file extension! result = if not conf.nimcacheDir.isEmpty: conf.nimcacheDir @@ -692,7 +697,7 @@ proc getNimcacheDir*(conf: ConfigRef): AbsoluteDir = conf.projectPath / genSubDir else: AbsoluteDir(getOsCacheDir() / splitFile(conf.projectName).name & - (if isDefined(conf, "release") or isDefined(conf, "danger"): "_r" else: "_d")) + nimcacheSuffix(conf)) proc pathSubs*(conf: ConfigRef; p, config: string): string = let home = removeTrailingDirSep(os.getHomeDir()) diff --git a/compiler/sem.nim b/compiler/sem.nim index 6b3f0c80d..ccc634b4d 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -452,6 +452,7 @@ const proc semMacroExpr(c: PContext, n, nOrig: PNode, sym: PSym, flags: TExprFlags = {}): PNode = + rememberExpansion(c, nOrig.info, sym) pushInfoContext(c.config, nOrig.info, sym.detailedInfo) let info = getCallLineInfo(n) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index daf1265e8..6b0dcb0e2 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -570,3 +570,13 @@ proc sealRodFile*(c: PContext) = if m == c.module: addPragmaComputation(c, n) c.idgen.sealed = true # no further additions are allowed + +proc rememberExpansion*(c: PContext; info: TLineInfo; expandedSym: PSym) = + ## Templates and macros are very special in Nim; these have + ## inlining semantics so after semantic checking they leave no trace + ## in the sem'checked AST. This is very bad for IDE-like tooling + ## ("find all usages of this template" would not work). We need special + ## logic to remember macro/template expansions. This is done here and + ## delegated to the "rod" file mechanism. + if c.config.symbolFiles != disabledSf: + storeExpansion(c.encoder, c.packedRepr, info, expandedSym) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 256c008c9..cbd4318f7 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -27,6 +27,7 @@ const proc semTemplateExpr(c: PContext, n: PNode, s: PSym, flags: TExprFlags = {}): PNode = + rememberExpansion(c, n.info, s) let info = getCallLineInfo(n) markUsed(c, info, s) onUse(info, s) diff --git a/doc/advopt.txt b/doc/advopt.txt index 4842d090e..c7b63d50f 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -15,6 +15,7 @@ Advanced commands: //dump dump all defined conditionals and search paths see also: --dump.format:json (useful with: `| jq`) //check checks the project for syntax and semantic + (can be combined with --track and --defusages) Runtime checks (see -x): --objChecks:on|off turn obj conversion checks on|off @@ -27,6 +28,8 @@ Runtime checks (see -x): --infChecks:on|off turn Inf checks on|off Advanced options: + --track:FILE,LINE,COL set the tracking position for (--defusages) + --defusages find the definition and all usages of a symbol -o:FILE, --out:FILE set the output filename --outdir:DIR set the path where the output file will be written --usenimcache will use `outdir=$$nimcache`, whichever it resolves From 58f44c8b4e6486789a08555dccee6492d32575d4 Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 14 Apr 2021 22:45:22 +0800 Subject: [PATCH 1927/3768] Update channels.nim (#17717) --- lib/std/channels.nim | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/std/channels.nim b/lib/std/channels.nim index 3bbc8a6b6..49def0913 100644 --- a/lib/std/channels.nim +++ b/lib/std/channels.nim @@ -307,8 +307,8 @@ proc sendUnbufferedMpmc(chan: ChannelRaw, data: sink pointer, size: int, nonBloc chan.head = 1 - release(chan.headLock) signal(chan.notEmptyCond) + release(chan.headLock) result = true proc sendMpmc(chan: ChannelRaw, data: sink pointer, size: int, nonBlocking: bool): bool = @@ -343,8 +343,8 @@ proc sendMpmc(chan: ChannelRaw, data: sink pointer, size: int, nonBlocking: bool if chan.tail == 2 * chan.size: chan.tail = 0 - release(chan.tailLock) signal(chan.notEmptyCond) + release(chan.tailLock) result = true proc recvUnbufferedMpmc(chan: ChannelRaw, data: pointer, size: int, nonBlocking: bool): bool = @@ -368,8 +368,8 @@ proc recvUnbufferedMpmc(chan: ChannelRaw, data: pointer, size: int, nonBlocking: chan.head = 0 - release(chan.headLock) signal(chan.notFullCond) + release(chan.headLock) result = true proc recvMpmc(chan: ChannelRaw, data: pointer, size: int, nonBlocking: bool): bool = @@ -404,8 +404,8 @@ proc recvMpmc(chan: ChannelRaw, data: pointer, size: int, nonBlocking: bool): bo if chan.head == 2 * chan.size: chan.head = 0 - release(chan.headLock) signal(chan.notFullCond) + release(chan.headLock) result = true proc channelCloseMpmc(chan: ChannelRaw): bool = From 44657b78c40475e60187b48157bae41ea5371252 Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 15 Apr 2021 01:14:14 +0800 Subject: [PATCH 1928/3768] callback cannot be nil (#17718) `Task.callback` cannot be nil, we need to raise it at debug and release mode Situations: - if users create a Task object without using `toTask` and invoke the Task - if users already move the Task and invoke the Task --- lib/std/tasks.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/std/tasks.nim b/lib/std/tasks.nim index 6b7f86ce4..aed36f985 100644 --- a/lib/std/tasks.nim +++ b/lib/std/tasks.nim @@ -75,6 +75,7 @@ proc `=destroy`*(t: var Task) {.inline.} = proc invoke*(task: Task) {.inline.} = ## Invokes the `task`. + assert task.callback != nil task.callback(task.args) template checkIsolate(scratchAssignList: seq[NimNode], procParam, scratchDotExpr: NimNode) = From 56c37759d6183aa32d474e669de618ee9c4f7633 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arne=20D=C3=B6ring?= Date: Wed, 14 Apr 2021 20:42:09 +0200 Subject: [PATCH 1929/3768] getCustomPragma is split up in more usable chunks (#11526) * getCustomPragma is split up in more usable chunks * changelog entry * fix for style checks * shitty typedesc special casing * Add since annotation and remove typedesc comments * Fix typo * Revert since annotation because it breaks bootstrapping * Export getCustomPragmaNode conditionally * Reduce code duplication * Update since * Update lib/core/macros.nim * Apply suggestions from code review Co-authored-by: Clyybber Co-authored-by: Andreas Rumpf --- changelog.md | 1 + lib/core/macros.nim | 254 ++++++++++++++++++++----------- tests/pragmas/tcustom_pragma.nim | 30 +++- 3 files changed, 191 insertions(+), 94 deletions(-) diff --git a/changelog.md b/changelog.md index 2f1d2d816..ec1690e06 100644 --- a/changelog.md +++ b/changelog.md @@ -32,6 +32,7 @@ implementations. Old behavior can be obtained with `-d:nimLegacyParseQueryStrict`. `cgi.decodeData` which uses the same underlying code is also updated the same way. +- Custom pragma values have now an API for use in macros. - In `std/os`, `getHomeDir`, `expandTilde`, `getTempDir`, `getConfigDir` now do not include trailing `DirSep`, unless `-d:nimLegacyHomeDir` is specified (for a transition period). diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 49c9a999c..ebe5a7187 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -1474,80 +1474,153 @@ macro expandMacros*(body: typed): untyped = echo body.toStrLit result = body -proc customPragmaNode(n: NimNode): NimNode = - expectKind(n, {nnkSym, nnkDotExpr, nnkBracketExpr, nnkTypeOfExpr, nnkCheckedFieldExpr}) - let - typ = n.getTypeInst() +proc findPragmaExprForFieldSym(arg, fieldSym: NimNode): NimNode = + case arg.kind + of nnkRecList, nnkRecCase: + for it in arg.children: + result = findPragmaExprForFieldSym(it, fieldSym) + if result != nil: + return + of nnkOfBranch: + return findPragmaExprForFieldSym(arg[1], fieldSym) + of nnkElse: + return findPragmaExprForFieldSym(arg[0], fieldSym) + of nnkIdentDefs: + for i in 0 ..< arg.len-2: + let child = arg[i] + result = findPragmaExprForFieldSym(child, fieldSym) + if result != nil: + return + of nnkAccQuoted, nnkIdent, nnkSym, nnkPostfix: + return nil + of nnkPragmaExpr: + var ident = arg[0] + if ident.kind == nnkPostfix: ident = ident[1] + if ident.kind == nnkAccQuoted: ident = ident[0] + if eqIdent(ident, fieldSym): + return arg[1] + else: + error("illegal arg: ", arg) - if typ.kind == nnkBracketExpr and typ.len > 1 and typ[1].kind == nnkProcTy: - return typ[1][1] - elif typ.typeKind == ntyTypeDesc: - let impl = typ[1].getImpl() - if impl[0].kind == nnkPragmaExpr: - return impl[0][1] +proc getPragmaByName(pragmaExpr: NimNode, name: string): NimNode = + if pragmaExpr.kind == nnkPragma: + for it in pragmaExpr: + if it.kind in nnkPragmaCallKinds: + if eqIdent(it[0], name): + return it + elif it.kind == nnkSym: + if eqIdent(it, name): + return it + +proc getCustomPragmaNodeFromProcSym(sym: NimNode, name: string): NimNode = + sym.expectKind nnkSym + if sym.symKind != nskProc: error("expected proc sym", sym) + + let impl = sym.getImpl + expectKind(impl, nnkProcDef) + result = getPragmaByName(impl[4], name) + +proc getCustomPragmaNodeFromObjFieldSym(sym: NimNode, name: string): NimNode = + sym.expectKind nnkSym + if sym.symKind != nskField: error("expected field sym", sym) + + # note this is not ``getTypeImpl``, because the result of + # ``getTypeImpl`` is cleaned up of any pragma expressions. + let impl = sym.owner.getImpl + impl.expectKind nnkTypeDef + + let objectTy = if impl[2].kind == nnkRefTy: impl[2][0] + else: impl[2] + + # only works on object types + objectTy.expectKind nnkObjectTy + + let recList = objectTy[2] + recList.expectKind nnkRecList + result = getPragmaByName(findPragmaExprForFieldSym(recList, sym), name) + +proc getCustomPragmaNodeFromTypeSym(sym: NimNode, name: string): NimNode = + sym.expectKind nnkSym + if sym.symKind != nskType: error("expected type sym", sym) + let impl = sym.getImpl + if impl.len > 0: + impl.expectKind nnkTypeDef + let pragmaExpr = impl[0] + if pragmaExpr.kind == nnkPragmaExpr: + result = getPragmaByName(pragmaExpr[1], name) + +proc getCustomPragmaNodeFromVarLetSym(sym: NimNode, name: string): NimNode = + sym.expectKind nnkSym + if sym.symKind notin {nskVar, nskLet}: error("expected var/let sym", sym) + let impl = sym.getImpl + impl.expectKind nnkIdentDefs + impl.expectLen 3 + let pragmaExpr = impl[0] + if pragmaExpr.kind == nnkPragmaExpr: + result = getPragmaByName(pragmaExpr[1], name) + +proc getCustomPragmaNode(sym: NimNode, name: string): NimNode = + sym.expectKind nnkSym + case sym.symKind + of nskField: + result = getCustomPragmaNodeFromObjFieldSym(sym, name) + of nskProc: + result = getCustomPragmaNodeFromProcSym(sym, name) + of nskType: + result = getCustomPragmaNodeFromTypeSym(sym, name) + of nskParam: + # When a typedesc parameter is passed to the macro, it will be of nskParam. + let typeInst = getTypeInst(sym) + if typeInst.kind == nnkBracketExpr and eqIdent(typeInst[0], "typeDesc"): + result = getCustomPragmaNodeFromTypeSym(typeInst[1], name) else: - return impl[0] # handle types which don't have macro at all + error("illegal sym kind for argument: " & $sym.symKind, sym) + of nskVar, nskLet: + # I think it is a bad idea to fall back to the typeSym. The API + # explicity requests a var/let symbol, not a type symbol. + result = + getCustomPragmaNodeFromVarLetSym(sym, name) or + getCustomPragmaNodeFromTypeSym(sym.getTypeInst, name) + else: + error("illegal sym kind for argument: " & $sym.symKind, sym) - if n.kind == nnkSym: # either an variable or a proc - let impl = n.getImpl() - if impl.kind in RoutineNodes: - return impl.pragma - elif impl.kind == nnkIdentDefs and impl[0].kind == nnkPragmaExpr: - return impl[0][1] +since (1, 5): + export getCustomPragmaNode + +proc hasCustomPragma*(n: NimNode, name: string): bool = + n.expectKind nnkSym + let pragmaNode = getCustomPragmaNode(n, name) + result = pragmaNode != nil + +proc getCustomPragmaNodeSmart(n: NimNode, name: string): NimNode = + case n.kind + of nnkDotExpr: + result = getCustomPragmaNode(n[1], name) + of nnkCheckedFieldExpr: + expectKind n[0], nnkDotExpr + result = getCustomPragmaNode(n[0][1], name) + of nnkSym: + result = getCustomPragmaNode(n, name) + of nnkTypeOfExpr: + var typeSym = n.getTypeInst + while typeSym.kind == nnkBracketExpr and typeSym[0].eqIdent "typeDesc": + typeSym = typeSym[1] + case typeSym.kind: + of nnkSym: + result = getCustomPragmaNode(typeSym, name) + of nnkProcTy: + # It is a bad idea to support this. The annotation can't be part + # of a symbol. + let pragmaExpr = typeSym[1] + result = getPragmaByName(pragmaExpr, name) else: - let timpl = typ.getImpl() - if timpl.len>0 and timpl[0].len>1: - return timpl[0][1] - else: - return timpl + typeSym.expectKind nnkSym + of nnkBracketExpr: + result = nil #false + else: + n.expectKind({nnkDotExpr, nnkCheckedFieldExpr, nnkSym, nnkTypeOfExpr}) - if n.kind in {nnkDotExpr, nnkCheckedFieldExpr}: - let name = $(if n.kind == nnkCheckedFieldExpr: n[0][1] else: n[1]) - let typInst = getTypeInst(if n.kind == nnkCheckedFieldExpr or n[0].kind == nnkHiddenDeref: n[0][0] else: n[0]) - var typDef = getImpl(if typInst.kind == nnkVarTy: typInst[0] else: typInst) - while typDef != nil: - typDef.expectKind(nnkTypeDef) - let typ = typDef[2] - typ.expectKind({nnkRefTy, nnkPtrTy, nnkObjectTy}) - let isRef = typ.kind in {nnkRefTy, nnkPtrTy} - if isRef and typ[0].kind in {nnkSym, nnkBracketExpr}: # defines ref type for another object(e.g. X = ref X) - typDef = getImpl(typ[0]) - else: # object definition, maybe an object directly defined as a ref type - let - obj = (if isRef: typ[0] else: typ) - var identDefsStack = newSeq[NimNode](obj[2].len) - for i in 0.. 0: - var identDefs = identDefsStack.pop() - if identDefs.kind == nnkRecCase: - identDefsStack.add(identDefs[0]) - for i in 1.. 0 and p[0].kind == nnkSym and p[0] == cp): - return newLit(true) - return newLit(false) + result = newLit(getCustomPragmaNodeSmart(n, $cp) != nil) macro getCustomPragmaVal*(n: typed, cp: typed{nkSym}): untyped = ## Expands to value of custom pragma `cp` of expression `n` which is expected @@ -1586,22 +1654,26 @@ macro getCustomPragmaVal*(n: typed, cp: typed{nkSym}): untyped = ## assert(o.myField.getCustomPragmaVal(serializationKey) == "mf") ## assert(o.getCustomPragmaVal(serializationKey) == "mo") ## assert(MyObj.getCustomPragmaVal(serializationKey) == "mo") - result = nil - let pragmaNode = customPragmaNode(n) - for p in pragmaNode: - if p.kind in nnkPragmaCallKinds and p.len > 0 and p[0].kind == nnkSym and p[0] == cp: - if p.len == 2: - result = p[1] - else: - let def = p[0].getImpl[3] - result = newTree(nnkPar) - for i in 1 ..< def.len: - let key = def[i][0] - let val = p[i] - result.add newTree(nnkExprColonExpr, key, val) - break - if result.kind == nnkEmpty: - error(n.repr & " doesn't have a pragma named " & cp.repr()) # returning an empty node results in most cases in a cryptic error, + n.expectKind({nnkDotExpr, nnkCheckedFieldExpr, nnkSym, nnkTypeOfExpr}) + let pragmaNode = getCustomPragmaNodeSmart(n, $cp) + + case pragmaNode.kind + of nnkPragmaCallKinds: + assert pragmaNode[0] == cp + if pragmaNode.len == 2: + result = pragmaNode[1] + else: + # create a named tuple expression for pragmas with multiple arguments + let def = pragmaNode[0].getImpl[3] + result = newTree(nnkPar) + for i in 1 ..< def.len: + let key = def[i][0] + let val = pragmaNode[i] + result.add nnkExprColonExpr.newTree(key, val) + of nnkSym: + error("The named pragma " & cp.repr & " in " & n.repr & " has no arguments and therefore no value.") + else: + error(n.repr & " doesn't have a pragma named " & cp.repr, n) macro unpackVarargs*(callee: untyped; args: varargs[untyped]): untyped = result = newCall(callee) diff --git a/tests/pragmas/tcustom_pragma.nim b/tests/pragmas/tcustom_pragma.nim index e9dac753d..5342b78e6 100644 --- a/tests/pragmas/tcustom_pragma.nim +++ b/tests/pragmas/tcustom_pragma.nim @@ -156,13 +156,20 @@ block: proc generic_proc[T]() = doAssert Annotated.hasCustomPragma(simpleAttr) - #-------------------------------------------------------------------------- # Pragma on proc type -let a: proc(x: int) {.defaultValue(5).} = nil +type + MyAnnotatedProcType {.defaultValue(4).} = proc(x: int) + +let a {.defaultValue(4).}: proc(x: int) = nil +var b: MyAnnotatedProcType = nil +var c: proc(x: int): void {.defaultValue(5).} = nil static: - doAssert hasCustomPragma(a.type, defaultValue) + doAssert hasCustomPragma(a, defaultValue) + doAssert hasCustomPragma(MyAnnotatedProcType, defaultValue) + doAssert hasCustomPragma(b, defaultValue) + doAssert hasCustomPragma(typeof(c), defaultValue) # bug #8371 template thingy {.pragma.} @@ -378,3 +385,20 @@ block: b {.world.}: int discard Hello(a: 1.0, b: 12) + +# issue #11511 +block: + template myAttr {.pragma.} + + type TObj = object + a {.myAttr.}: int + + macro hasMyAttr(t: typedesc): untyped = + let objTy = t.getType[1].getType + let recList = objTy[2] + let sym = recList[0] + assert sym.kind == nnkSym and sym.eqIdent("a") + let hasAttr = sym.hasCustomPragma("myAttr") + newLit(hasAttr) + + doAssert hasMyAttr(TObj) From d6242d7fe19849d6cc7079f0b006065fb2a8a19e Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 14 Apr 2021 12:22:22 -0700 Subject: [PATCH 1930/3768] simplify asyncfutures, asyncmacro (#17633) --- lib/pure/asyncfutures.nim | 20 ++++---- lib/pure/asyncmacro.nim | 83 +++++++++----------------------- tests/async/tasyncintemplate.nim | 28 ++++++++--- tests/errmsgs/tgcsafety.nim | 3 +- tests/pragmas/tcustom_pragma.nim | 20 +++----- 5 files changed, 62 insertions(+), 92 deletions(-) diff --git a/lib/pure/asyncfutures.nim b/lib/pure/asyncfutures.nim index 3c36c3453..fd62b2703 100644 --- a/lib/pure/asyncfutures.nim +++ b/lib/pure/asyncfutures.nim @@ -189,24 +189,22 @@ proc add(callbacks: var CallbackList, function: CallbackFunc) = last = last.next last.next = newCallback -proc complete*[T](future: Future[T], val: T) = - ## Completes `future` with value `val`. +proc completeImpl[T, U](future: Future[T], val: U, isVoid: static bool) = #assert(not future.finished, "Future already finished, cannot finish twice.") checkFinished(future) assert(future.error == nil) - future.value = val + when not isVoid: + future.value = val future.finished = true future.callbacks.call() when isFutureLoggingEnabled: logFutureFinish(future) -proc complete*(future: Future[void]) = - ## Completes a void `future`. - #assert(not future.finished, "Future already finished, cannot finish twice.") - checkFinished(future) - assert(future.error == nil) - future.finished = true - future.callbacks.call() - when isFutureLoggingEnabled: logFutureFinish(future) +proc complete*[T](future: Future[T], val: T) = + ## Completes `future` with value `val`. + completeImpl(future, val, false) + +proc complete*(future: Future[void], val = Future[void].default) = + completeImpl(future, (), true) proc complete*[T](future: FutureVar[T]) = ## Completes a `FutureVar`. diff --git a/lib/pure/asyncmacro.nim b/lib/pure/asyncmacro.nim index aaa5d9acd..ce538913f 100644 --- a/lib/pure/asyncmacro.nim +++ b/lib/pure/asyncmacro.nim @@ -11,7 +11,6 @@ import macros, strutils, asyncfutures - # TODO: Ref https://github.com/nim-lang/Nim/issues/5617 # TODO: Add more line infos proc newCallWithLineInfo(fromNode: NimNode; theProc: NimNode, args: varargs[NimNode]): NimNode = @@ -65,10 +64,7 @@ proc createFutureVarCompletions(futureVarIdents: seq[NimNode], fromNode: NimNode ) ) -proc processBody(node, retFutureSym: NimNode, - subTypeIsVoid: bool, - futureVarIdents: seq[NimNode]): NimNode = - #echo(node.treeRepr) +proc processBody(node, retFutureSym: NimNode, futureVarIdents: seq[NimNode]): NimNode = result = node case node.kind of nnkReturnStmt: @@ -78,14 +74,9 @@ proc processBody(node, retFutureSym: NimNode, result.add createFutureVarCompletions(futureVarIdents, node) if node[0].kind == nnkEmpty: - if not subTypeIsVoid: - result.add newCall(newIdentNode("complete"), retFutureSym, - newIdentNode("result")) - else: - result.add newCall(newIdentNode("complete"), retFutureSym) + result.add newCall(newIdentNode("complete"), retFutureSym, newIdentNode("result")) else: - let x = node[0].processBody(retFutureSym, subTypeIsVoid, - futureVarIdents) + let x = node[0].processBody(retFutureSym, futureVarIdents) if x.kind == nnkYieldStmt: result.add x else: result.add newCall(newIdentNode("complete"), retFutureSym, x) @@ -98,8 +89,7 @@ proc processBody(node, retFutureSym: NimNode, else: discard for i in 0 ..< result.len: - result[i] = processBody(result[i], retFutureSym, subTypeIsVoid, - futureVarIdents) + result[i] = processBody(result[i], retFutureSym, futureVarIdents) # echo result.repr @@ -146,7 +136,7 @@ proc asyncSingleProc(prc: NimNode): NimNode = if prc.kind == nnkProcTy: result = prc if prc[0][0].kind == nnkEmpty: - result[0][0] = parseExpr("Future[void]") + result[0][0] = quote do: Future[void] return result if prc.kind notin {nnkProcDef, nnkLambda, nnkMethodDef, nnkDo}: @@ -180,12 +170,7 @@ proc asyncSingleProc(prc: NimNode): NimNode = else: verifyReturnType(repr(returnType), returnType) - let subtypeIsVoid = returnType.kind == nnkEmpty or - (baseType.kind in {nnkIdent, nnkSym} and - baseType.eqIdent("void")) - let futureVarIdents = getFutureVarIdents(prc.params) - var outerProcBody = newNimNode(nnkStmtList, prc.body) # Extract the documentation comment from the original procedure declaration. @@ -213,42 +198,30 @@ proc asyncSingleProc(prc: NimNode): NimNode = # -> # -> complete(retFuture, result) var iteratorNameSym = genSym(nskIterator, $prcName & "Iter") - var procBody = prc.body.processBody(retFutureSym, subtypeIsVoid, - futureVarIdents) + var procBody = prc.body.processBody(retFutureSym, futureVarIdents) # don't do anything with forward bodies (empty) if procBody.kind != nnkEmpty: # fix #13899, defer should not escape its original scope procBody = newStmtList(newTree(nnkBlockStmt, newEmptyNode(), procBody)) - procBody.add(createFutureVarCompletions(futureVarIdents, nil)) + let resultIdent = ident"result" + procBody.insert(0): quote do: + {.push warning[resultshadowed]: off.} + when `subRetType` isnot void: + var `resultIdent`: `subRetType` + else: + var `resultIdent`: Future[void] + {.pop.} + procBody.add quote do: + complete(`retFutureSym`, `resultIdent`) - if not subtypeIsVoid: - procBody.insert(0, newNimNode(nnkPragma).add(newIdentNode("push"), - newNimNode(nnkExprColonExpr).add(newNimNode(nnkBracketExpr).add( - newIdentNode("warning"), newIdentNode("resultshadowed")), - newIdentNode("off")))) # -> {.push warning[resultshadowed]: off.} - - procBody.insert(1, newNimNode(nnkVarSection, prc.body).add( - newIdentDefs(newIdentNode("result"), baseType))) # -> var result: T - - procBody.insert(2, newNimNode(nnkPragma).add( - newIdentNode("pop"))) # -> {.pop.}) - - procBody.add( - newCall(newIdentNode("complete"), - retFutureSym, newIdentNode("result"))) # -> complete(retFuture, result) - else: - # -> complete(retFuture) - procBody.add(newCall(newIdentNode("complete"), retFutureSym)) - - var closureIterator = newProc(iteratorNameSym, [parseExpr("owned(FutureBase)")], + var closureIterator = newProc(iteratorNameSym, [quote do: owned(FutureBase)], procBody, nnkIteratorDef) closureIterator.pragma = newNimNode(nnkPragma, lineInfoFrom = prc.body) closureIterator.addPragma(newIdentNode("closure")) # If proc has an explicit gcsafe pragma, we add it to iterator as well. - if prc.pragma.findChild(it.kind in {nnkSym, nnkIdent} and $it == - "gcsafe") != nil: + if prc.pragma.findChild(it.kind in {nnkSym, nnkIdent} and $it == "gcsafe") != nil: closureIterator.addPragma(newIdentNode("gcsafe")) outerProcBody.add(closureIterator) @@ -266,12 +239,10 @@ proc asyncSingleProc(prc: NimNode): NimNode = outerProcBody.add newNimNode(nnkReturnStmt, prc.body[^1]).add(retFutureSym) result = prc - - if subtypeIsVoid: - # Add discardable pragma. - if returnType.kind == nnkEmpty: - # Add Future[void] - result.params[0] = parseExpr("owned(Future[void])") + # Add discardable pragma. + if returnType.kind == nnkEmpty: + # xxx consider removing `owned`? it's inconsistent with non-void case + result.params[0] = quote do: owned(Future[void]) # based on the yglukhov's patch to chronos: https://github.com/status-im/nim-chronos/pull/47 if procBody.kind != nnkEmpty: @@ -279,10 +250,6 @@ proc asyncSingleProc(prc: NimNode): NimNode = `outerProcBody` result.body = body2 - #echo(treeRepr(result)) - #if prcName == "recvLineInto": - # echo(toStrLit(result)) - macro async*(prc: untyped): untyped = ## Macro which processes async procedures into the appropriate ## iterators and yield statements. @@ -352,9 +319,3 @@ macro multisync*(prc: untyped): untyped = result = newStmtList() result.add(asyncSingleProc(asyncPrc)) result.add(sync) - # echo result.repr - -# overload for await as a fallback handler, based on the yglukhov's patch to chronos: https://github.com/status-im/nim-chronos/pull/47 -# template await*(f: typed): untyped = - # static: - # error "await only available within {.async.}" diff --git a/tests/async/tasyncintemplate.nim b/tests/async/tasyncintemplate.nim index 0f810b5be..4bddb1d18 100644 --- a/tests/async/tasyncintemplate.nim +++ b/tests/async/tasyncintemplate.nim @@ -1,6 +1,8 @@ discard """ output: ''' 42 +43 +43 1 2 3 @@ -8,16 +10,30 @@ discard """ ''' """ +# xxx move to tests/async/tasyncintemplate.nim import asyncdispatch -# bug #16159 -template foo() = - proc temp(): Future[int] {.async.} = return 42 - proc tempVoid(): Future[void] {.async.} = echo await temp() +block: # bug #16159 + template foo() = + proc temp(): Future[int] {.async.} = return 42 + proc tempVoid(): Future[void] {.async.} = echo await temp() + foo() + waitFor tempVoid() -foo() -waitFor tempVoid() +block: # aliasing `void` + template foo() = + type Foo = void + proc temp(): Future[int] {.async.} = return 43 + proc tempVoid(): Future[Foo] {.async.} = echo await temp() + proc tempVoid2() {.async.} = echo await temp() + foo() + waitFor tempVoid() + waitFor tempVoid2() +block: # sanity check + template foo() = + proc bad(): int {.async.} = discard + doAssert not compiles(bad()) block: # bug #16786 block: diff --git a/tests/errmsgs/tgcsafety.nim b/tests/errmsgs/tgcsafety.nim index 09ef92e75..563ead2e0 100644 --- a/tests/errmsgs/tgcsafety.nim +++ b/tests/errmsgs/tgcsafety.nim @@ -6,7 +6,8 @@ tgcsafety.nim(30, 18) Error: type mismatch: got .} diff --git a/tests/pragmas/tcustom_pragma.nim b/tests/pragmas/tcustom_pragma.nim index 5342b78e6..55761d107 100644 --- a/tests/pragmas/tcustom_pragma.nim +++ b/tests/pragmas/tcustom_pragma.nim @@ -262,6 +262,10 @@ block: doAssert input.treeRepr & "\n" == expectedRepr return input + macro expectedAstRepr(expectedRepr: static[string], input: untyped): untyped = + doAssert input.repr == expectedRepr + return input + const procTypeAst = """ ProcTy FormalParams @@ -280,20 +284,10 @@ ProcTy static: doAssert Foo is proc(x: int): Future[void] const asyncProcTypeAst = """ -ProcTy - FormalParams - BracketExpr - Ident "Future" - Ident "void" - IdentDefs - Ident "s" - Ident "string" - Empty - Pragma -""" - +proc (s: string): Future[void] {..}""" + # using expectedAst would show `OpenSymChoice` for Future[void], which is fragile. type - Bar = proc (s: string) {.async, expectedAst(asyncProcTypeAst).} + Bar = proc (s: string) {.async, expectedAstRepr(asyncProcTypeAst).} static: doAssert Bar is proc(x: string): Future[void] From eff9d75379d6f9f4a52c2040425dad1f408ede37 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 14 Apr 2021 22:35:18 +0200 Subject: [PATCH 1931/3768] IC: rodfiles: no floats (#17720) --- compiler/ic/ic.nim | 19 ++++++++----------- compiler/ic/integrity.nim | 6 ++---- compiler/ic/packed_ast.nim | 11 +++++++---- compiler/ic/rodfiles.nim | 3 +-- 4 files changed, 18 insertions(+), 21 deletions(-) diff --git a/compiler/ic/ic.nim b/compiler/ic/ic.nim index 4613b9cb8..4f63ce03a 100644 --- a/compiler/ic/ic.nim +++ b/compiler/ic/ic.nim @@ -224,7 +224,7 @@ proc toLitId(x: string; m: var PackedModule): LitId = proc toLitId(x: BiggestInt; m: var PackedModule): LitId = ## store an integer as a literal - result = getOrIncl(m.sh.integers, x) + result = getOrIncl(m.sh.numbers, x) proc toPackedInfo(x: TLineInfo; c: var PackedEncoder; m: var PackedModule): PackedLineInfo = PackedLineInfo(line: x.line, col: x.col, file: toLitId(x.fileIndex, c, m)) @@ -400,7 +400,7 @@ proc toPackedNode*(n: PNode; ir: var PackedTree; c: var PackedEncoder; m: var Pa typeId: storeTypeLater(n.typ, c, m), info: info) of externIntLit: ir.nodes.add PackedNode(kind: n.kind, flags: n.flags, - operand: int32 getOrIncl(m.sh.integers, n.intVal), + operand: int32 getOrIncl(m.sh.numbers, n.intVal), typeId: storeTypeLater(n.typ, c, m), info: info) of nkStrLit..nkTripleStrLit: ir.nodes.add PackedNode(kind: n.kind, flags: n.flags, @@ -408,7 +408,7 @@ proc toPackedNode*(n: PNode; ir: var PackedTree; c: var PackedEncoder; m: var Pa typeId: storeTypeLater(n.typ, c, m), info: info) of nkFloatLit..nkFloat128Lit: ir.nodes.add PackedNode(kind: n.kind, flags: n.flags, - operand: int32 getOrIncl(m.sh.floats, n.floatVal), + operand: int32 getOrIncl(m.sh.numbers, cast[BiggestInt](n.floatVal)), typeId: storeTypeLater(n.typ, c, m), info: info) else: let patchPos = ir.prepare(n.kind, n.flags, @@ -521,8 +521,7 @@ proc loadRodFile*(filename: AbsoluteFile; m: var PackedModule; config: ConfigRef loadSeqSection depsSection, m.imports - loadTabSection integersSection, m.sh.integers - loadTabSection floatsSection, m.sh.floats + loadTabSection numbersSection, m.sh.numbers loadSeqSection exportsSection, m.exports @@ -587,8 +586,7 @@ proc saveRodFile*(filename: AbsoluteFile; encoder: var PackedEncoder; m: var Pac storeSeqSection depsSection, m.imports - storeTabSection integersSection, m.sh.integers - storeTabSection floatsSection, m.sh.floats + storeTabSection numbersSection, m.sh.numbers storeSeqSection exportsSection, m.exports @@ -700,11 +698,11 @@ proc loadNodes*(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; of directIntLit: result.intVal = tree.nodes[n.int].operand of externIntLit: - result.intVal = g[thisModule].fromDisk.sh.integers[n.litId] + result.intVal = g[thisModule].fromDisk.sh.numbers[n.litId] of nkStrLit..nkTripleStrLit: result.strVal = g[thisModule].fromDisk.sh.strings[n.litId] of nkFloatLit..nkFloat128Lit: - result.floatVal = g[thisModule].fromDisk.sh.floats[n.litId] + result.floatVal = cast[BiggestFloat](g[thisModule].fromDisk.sh.numbers[n.litId]) of nkModuleRef: let (n1, n2) = sons2(tree, n) assert n1.kind == nkInt32Lit @@ -1151,5 +1149,4 @@ proc rodViewer*(rodfile: AbsoluteFile; config: ConfigRef, cache: IdentCache) = echo "symbols: ", m.sh.syms.len, " types: ", m.sh.types.len, " top level nodes: ", m.topLevel.nodes.len, " other nodes: ", m.bodies.nodes.len, - " strings: ", m.sh.strings.len, " integers: ", m.sh.integers.len, - " floats: ", m.sh.floats.len + " strings: ", m.sh.strings.len, " numbers: ", m.sh.numbers.len diff --git a/compiler/ic/integrity.nim b/compiler/ic/integrity.nim index b4545de94..a28f2066a 100644 --- a/compiler/ic/integrity.nim +++ b/compiler/ic/integrity.nim @@ -79,12 +79,10 @@ proc checkNode(c: var CheckedContext; tree: PackedTree; n: NodePos) = checkLocalSym(c, tree.nodes[n.int].operand) of directIntLit: discard - of externIntLit: - assert c.g.packed[c.thisModule].fromDisk.sh.integers.hasLitId n.litId + of externIntLit, nkFloatLit..nkFloat128Lit: + assert c.g.packed[c.thisModule].fromDisk.sh.numbers.hasLitId n.litId of nkStrLit..nkTripleStrLit: assert c.g.packed[c.thisModule].fromDisk.sh.strings.hasLitId n.litId - of nkFloatLit..nkFloat128Lit: - assert c.g.packed[c.thisModule].fromDisk.sh.floats.hasLitId n.litId of nkModuleRef: let (n1, n2) = sons2(tree, n) assert n1.kind == nkInt32Lit diff --git a/compiler/ic/packed_ast.nim b/compiler/ic/packed_ast.nim index 5cc3b1476..0649b56ce 100644 --- a/compiler/ic/packed_ast.nim +++ b/compiler/ic/packed_ast.nim @@ -106,8 +106,8 @@ type syms*: seq[PackedSym] types*: seq[PackedType] strings*: BiTable[string] # we could share these between modules. - integers*: BiTable[BiggestInt] - floats*: BiTable[BiggestFloat] + numbers*: BiTable[BiggestInt] # we also store floats in here so + # that we can assure that every bit is kept #config*: ConfigRef PackedInstantiation* = object @@ -394,10 +394,13 @@ proc toString*(tree: PackedTree; n: NodePos; sh: Shared; nesting: int; result.addInt tree.nodes[pos].operand of externSIntLit: result.add " " - result.addInt sh.integers[LitId tree.nodes[pos].operand] + result.addInt sh.numbers[LitId tree.nodes[pos].operand] of externUIntLit: result.add " " - result.add $cast[uint64](sh.integers[LitId tree.nodes[pos].operand]) + result.add $cast[uint64](sh.numbers[LitId tree.nodes[pos].operand]) + of nkFloatLit..nkFloat128Lit: + result.add " " + result.add $cast[BiggestFloat](sh.numbers[LitId tree.nodes[pos].operand]) else: result.add "(\n" for i in 1..(nesting+1)*2: result.add ' ' diff --git a/compiler/ic/rodfiles.nim b/compiler/ic/rodfiles.nim index 39a5b6b73..fbe1cad33 100644 --- a/compiler/ic/rodfiles.nim +++ b/compiler/ic/rodfiles.nim @@ -16,8 +16,7 @@ type stringsSection checkSumsSection depsSection - integersSection - floatsSection + numbersSection exportsSection reexportsSection compilerProcsSection From ae9231cfebf800886c4febcf0fc7ccc380066108 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 14 Apr 2021 13:45:25 -0700 Subject: [PATCH 1932/3768] fix CI tests/errmsgs/tgcsafety.nim (#17721) --- tests/errmsgs/tgcsafety.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/errmsgs/tgcsafety.nim b/tests/errmsgs/tgcsafety.nim index 563ead2e0..6021d19e1 100644 --- a/tests/errmsgs/tgcsafety.nim +++ b/tests/errmsgs/tgcsafety.nim @@ -2,7 +2,7 @@ discard """ cmd: "nim check $file" errormsg: "type mismatch: got .}>" nimout: ''' -tgcsafety.nim(30, 18) Error: type mismatch: got .}> +tgcsafety.nim(31, 18) Error: type mismatch: got .}> but expected one of: proc serve(server: AsyncHttpServer; port: Port; callback: proc (request: Request): Future[void] {.closure, gcsafe.}; From f8dce493d36c10bfdfb3bd4ac87eae7b96b97f1a Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Thu, 15 Apr 2021 09:12:44 +0300 Subject: [PATCH 1933/3768] rst indentation fixes (ref #17340) (#17715) --- lib/packages/docutils/rst.nim | 95 +++++++---- lib/packages/docutils/rstast.nim | 18 +- nimdoc/rst2html/expected/rst_examples.html | 4 +- tests/stdlib/trst.nim | 185 ++++++++++++++++++++- tests/stdlib/trstgen.nim | 13 +- 5 files changed, 272 insertions(+), 43 deletions(-) diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index c2385d517..7c4b92f88 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -1532,6 +1532,55 @@ proc parseUntilNewline(p: var RstParser, father: PRstNode) = of tkEof, tkIndent: break proc parseSection(p: var RstParser, result: PRstNode) {.gcsafe.} + +proc tokenAfterNewline(p: RstParser, start: int): int = + result = start + while true: + case p.tok[result].kind + of tkEof: + break + of tkIndent: + inc result + break + else: inc result + +proc tokenAfterNewline(p: RstParser): int {.inline.} = + result = tokenAfterNewline(p, p.idx) + +proc getWrappableIndent(p: RstParser): int = + ## Gets baseline indentation for bodies of field lists and directives. + ## Handles situations like this (with possible de-indent in [case.3]):: + ## + ## :field: definition [case.1] + ## + ## currInd currentTok(p).col + ## | | + ## v v + ## + ## .. Note:: defItem: [case.2] + ## definition + ## + ## ^ + ## | + ## nextIndent + ## + ## .. Note:: - point1 [case.3] + ## - point 2 + ## + ## ^ + ## | + ## nextIndent + if currentTok(p).kind == tkIndent: + result = currentTok(p).ival + else: + var nextIndent = p.tok[tokenAfterNewline(p)-1].ival + if nextIndent <= currInd(p): # parse only this line [case.1] + result = currentTok(p).col + elif nextIndent >= currentTok(p).col: # may be a definition list [case.2] + result = currentTok(p).col + else: + result = nextIndent # [case.3] + proc parseField(p: var RstParser): PRstNode = ## Returns a parsed rnField node. ## @@ -1541,13 +1590,12 @@ proc parseField(p: var RstParser): PRstNode = var fieldname = newRstNode(rnFieldName) parseUntil(p, fieldname, ":", false) var fieldbody = newRstNode(rnFieldBody) - if currentTok(p).kind != tkIndent: parseLine(p, fieldbody) - if currentTok(p).kind == tkIndent: - var indent = currentTok(p).ival - if indent > col: - pushInd(p, indent) - parseSection(p, fieldbody) - popInd(p) + if currentTok(p).kind == tkWhite: inc p.idx + let indent = getWrappableIndent(p) + if indent > col: + pushInd(p, indent) + parseSection(p, fieldbody) + popInd(p) result.add(fieldname) result.add(fieldbody) @@ -1652,20 +1700,6 @@ proc countTitles(p: var RstParser, n: PRstNode) = if p.s.hTitleCnt >= 2: break -proc tokenAfterNewline(p: RstParser, start: int): int = - result = start - while true: - case p.tok[result].kind - of tkEof: - break - of tkIndent: - inc result - break - else: inc result - -proc tokenAfterNewline(p: RstParser): int {.inline.} = - result = tokenAfterNewline(p, p.idx) - proc isAdornmentHeadline(p: RstParser, adornmentIdx: int): bool = ## check that underline/overline length is enough for the heading. ## No support for Unicode. @@ -1752,7 +1786,7 @@ proc whichSection(p: RstParser): RstNodeKind = return rnCodeBlock elif currentTok(p).symbol == "::": return rnLiteralBlock - elif currentTok(p).symbol == ".." and predNL(p) and + elif currentTok(p).symbol == ".." and nextTok(p).kind in {tkWhite, tkIndent}: return rnDirective case currentTok(p).kind @@ -1780,10 +1814,9 @@ proc whichSection(p: RstParser): RstNodeKind = elif match(p, tokenAfterNewline(p), "aI") and isAdornmentHeadline(p, tokenAfterNewline(p)): result = rnHeadline - elif predNL(p) and - currentTok(p).symbol in ["+", "*", "-"] and nextTok(p).kind == tkWhite: + elif currentTok(p).symbol in ["+", "*", "-"] and nextTok(p).kind == tkWhite: result = rnBulletList - elif match(p, p.idx, ":w:E") and predNL(p): + elif match(p, p.idx, ":w:E"): # (currentTok(p).symbol == ":") result = rnFieldList elif match(p, p.idx, "(e) ") or match(p, p.idx, "e) ") or @@ -2350,9 +2383,11 @@ proc parseDirective(p: var RstParser, k: RstNodeKind, flags: DirFlags): PRstNode parseLine(p, args) result.add(args) if hasOptions in flags: - if currentTok(p).kind == tkIndent and currentTok(p).ival >= 3 and + if currentTok(p).kind == tkIndent and currentTok(p).ival > currInd(p) and nextTok(p).symbol == ":": + pushInd(p, currentTok(p).ival) options = parseFields(p) + popInd(p) result.add(options) proc indFollows(p: RstParser): bool = @@ -2363,11 +2398,9 @@ proc parseBlockContent(p: var RstParser, father: var PRstNode, ## parse the final content part of explicit markup blocks (directives, ## footnotes, etc). Returns true if succeeded. if currentTok(p).kind != tkIndent or indFollows(p): - var nextIndent = p.tok[tokenAfterNewline(p)-1].ival - if nextIndent <= currInd(p): # parse only this line - nextIndent = currentTok(p).col - pushInd(p, nextIndent) - var content = contentParser(p) + let blockIndent = getWrappableIndent(p) + pushInd(p, blockIndent) + let content = contentParser(p) popInd(p) father.add content result = true diff --git a/lib/packages/docutils/rstast.nim b/lib/packages/docutils/rstast.nim index 394cc2698..00f3f2b35 100644 --- a/lib/packages/docutils/rstast.nim +++ b/lib/packages/docutils/rstast.nim @@ -350,7 +350,7 @@ proc renderRstToJson*(node: PRstNode): string = proc renderRstToStr*(node: PRstNode, indent=0): string = ## Writes the parsed RST `node` into a compact string ## representation in the format (one line per every sub-node): - ## ``indent - kind - text - level - order - anchor (if non-zero)`` + ## ``indent - kind - [text|level|order|adType] - anchor (if non-zero)`` ## (suitable for debugging of RST parsing). if node == nil: result.add " ".repeat(indent) & "[nil]\n" @@ -358,21 +358,23 @@ proc renderRstToStr*(node: PRstNode, indent=0): string = result.add " ".repeat(indent) & $node.kind case node.kind of rnLeaf, rnSmiley: - result.add (if node.text == "": "" else: "\t'" & node.text & "'") + result.add (if node.text == "": "" else: " '" & node.text & "'") of rnEnumList: - result.add "\tlabelFmt=" & node.labelFmt + result.add " labelFmt=" & node.labelFmt of rnLineBlockItem: var txt: string - if node.lineIndent == "\n": txt = "\t(blank line)" - else: txt = "\tlineIndent=" & $node.lineIndent.len + if node.lineIndent == "\n": txt = " (blank line)" + else: txt = " lineIndent=" & $node.lineIndent.len result.add txt + of rnAdmonition: + result.add " adType=" & node.adType of rnHeadline, rnOverline, rnMarkdownHeadline: - result.add "\tlevel=" & $node.level + result.add " level=" & $node.level of rnFootnote, rnCitation, rnFootnoteRef, rnOptionListItem: - result.add (if node.order == 0: "" else: "\torder=" & $node.order) + result.add (if node.order == 0: "" else: " order=" & $node.order) else: discard - result.add (if node.anchor == "": "" else: "\tanchor='" & node.anchor & "'") + result.add (if node.anchor == "": "" else: " anchor='" & node.anchor & "'") result.add "\n" for son in node.sons: result.add renderRstToStr(son, indent=indent+2) diff --git a/nimdoc/rst2html/expected/rst_examples.html b/nimdoc/rst2html/expected/rst_examples.html index 32abc0f80..5c434193e 100644 --- a/nimdoc/rst2html/expected/rst_examples.html +++ b/nimdoc/rst2html/expected/rst_examples.html @@ -119,8 +119,8 @@ window.addEventListener('DOMContentLoaded', main);
    -

    - +

    Authors: Andreas Rumpf, Zahary Karadjov
    Version: |nimversion|
    +
    Authors:Andreas Rumpf, Zahary Karadjov
    Version:|nimversion|

    "Complexity" seems to be a lot like "energy": you can transfer it from the end-user to one/some of the other players, but the total amount seems to remain pretty much constant for a given task. -- Ran

    About this document

    Note: This document is a draft! Several of Nim's features may need more precise wording. This manual is constantly evolving into a proper specification.

    diff --git a/tests/stdlib/trst.nim b/tests/stdlib/trst.nim index 0645e4150..2398b92a8 100644 --- a/tests/stdlib/trst.nim +++ b/tests/stdlib/trst.nim @@ -1,6 +1,8 @@ discard """ output: ''' +[Suite] RST indentation + [Suite] RST include directive ''' """ @@ -9,9 +11,190 @@ discard """ import ../../lib/packages/docutils/rstgen import ../../lib/packages/docutils/rst -import unittest +import ../../lib/packages/docutils/rstast +import unittest, strutils +import std/private/miscdollars import os +proc toAst(input: string, + rstOptions: RstParseOptions = {roSupportMarkdown, roNimFile}, + error: ref string = nil, + warnings: ref seq[string] = nil): string = + ## If `error` is nil then no errors should be generated. + ## The same goes for `warnings`. + proc testMsgHandler(filename: string, line, col: int, msgkind: MsgKind, + arg: string) = + let mc = msgkind.whichMsgClass + let a = $msgkind % arg + var message: string + toLocation(message, filename, line, col + ColRstOffset) + message.add " $1: $2" % [$mc, a] + if mc == mcError: + doAssert error != nil, "unexpected RST error '" & message & "'" + error[] = message + # we check only first error because subsequent ones may be meaningless + raise newException(EParseError, message) + else: + doAssert warnings != nil, "unexpected RST warning '" & message & "'" + warnings[].add message + try: + const filen = "input" + + proc myFindFile(filename: string): string = + # we don't find any files in online mode: + result = "" + + var dummyHasToc = false + var rst = rstParse(input, filen, line=LineRstInit, column=ColRstInit, + dummyHasToc, rstOptions, myFindFile, testMsgHandler) + result = renderRstToStr(rst) + except EParseError: + discard + +suite "RST indentation": + test "nested bullet lists": + let input = dedent """ + * - bullet1 + - bullet2 + * - bullet3 + - bullet4 + """ + let output = input.toAst + check(output == dedent""" + rnBulletList + rnBulletItem + rnBulletList + rnBulletItem + rnInner + rnLeaf 'bullet1' + rnBulletItem + rnInner + rnLeaf 'bullet2' + rnBulletItem + rnBulletList + rnBulletItem + rnInner + rnLeaf 'bullet3' + rnBulletItem + rnInner + rnLeaf 'bullet4' + """) + + test "nested markup blocks": + let input = dedent""" + #) .. Hint:: .. Error:: none + #) .. Warning:: term0 + Definition0 + #) some + paragraph1 + #) term1 + Definition1 + term2 + Definition2 + """ + check(input.toAst == dedent""" + rnEnumList labelFmt=1) + rnEnumItem + rnAdmonition adType=hint + [nil] + [nil] + rnAdmonition adType=error + [nil] + [nil] + rnLeaf 'none' + rnEnumItem + rnAdmonition adType=warning + [nil] + [nil] + rnDefList + rnDefItem + rnDefName + rnLeaf 'term0' + rnDefBody + rnInner + rnLeaf 'Definition0' + rnEnumItem + rnInner + rnLeaf 'some' + rnLeaf ' ' + rnLeaf 'paragraph1' + rnEnumItem + rnDefList + rnDefItem + rnDefName + rnLeaf 'term1' + rnDefBody + rnInner + rnLeaf 'Definition1' + rnDefItem + rnDefName + rnLeaf 'term2' + rnDefBody + rnInner + rnLeaf 'Definition2' + """) + + test "code-block parsing": + let input1 = dedent""" + .. code-block:: nim + :test: "nim c $1" + + template additive(typ: typedesc) = + discard + """ + let input2 = dedent""" + .. code-block:: nim + :test: "nim c $1" + + template additive(typ: typedesc) = + discard + """ + let input3 = dedent""" + .. code-block:: nim + :test: "nim c $1" + template additive(typ: typedesc) = + discard + """ + let inputWrong = dedent""" + .. code-block:: nim + :test: "nim c $1" + + template additive(typ: typedesc) = + discard + """ + let ast = dedent""" + rnCodeBlock + rnDirArg + rnLeaf 'nim' + rnFieldList + rnField + rnFieldName + rnLeaf 'test' + rnFieldBody + rnInner + rnLeaf '"' + rnLeaf 'nim' + rnLeaf ' ' + rnLeaf 'c' + rnLeaf ' ' + rnLeaf '$' + rnLeaf '1' + rnLeaf '"' + rnField + rnFieldName + rnLeaf 'default-language' + rnFieldBody + rnLeaf 'Nim' + rnLiteralBlock + rnLeaf 'template additive(typ: typedesc) = + discard' + """ + check input1.toAst == ast + check input2.toAst == ast + check input3.toAst == ast + # "template..." should be parsed as a definition list attached to ":test:": + check inputWrong.toAst != ast + suite "RST include directive": test "Include whole": "other.rst".writeFile("**test1**") diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim index ed5d72226..29747f4e8 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -1118,6 +1118,17 @@ Test1 """ check "
    55\n56\n
    " in input.toHtml + test "Nim code-block indentation": + let input = dedent """ + .. code-block:: nim + :number-lines: 55 + + x + """ + let output = input.toHtml + check "
    55\n
    " in output + check "x" in output + test "RST admonitions": # check that all admonitions are implemented let input0 = dedent """ @@ -1466,7 +1477,7 @@ Test1 """""" & """""" & """""" & - """""" & "\n
    field: text
    ") + """text""" & "\n") test "Field list: body after newline": let output = dedent """ From 51a40c1f38a8a03f745f1982cf74091a1c6f7315 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Thu, 15 Apr 2021 13:55:50 +0200 Subject: [PATCH 1934/3768] Fix getCustomPragmaVal for some multi arg pragmas (#17723) * Fix getCustomPragmaVal for some multi arg pragmas * Bootstrap fix --- lib/core/macros.nim | 86 +++++++++++++++++--------------- tests/pragmas/tcustom_pragma.nim | 9 ++++ 2 files changed, 54 insertions(+), 41 deletions(-) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index ebe5a7187..fc68781da 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -1474,23 +1474,20 @@ macro expandMacros*(body: typed): untyped = echo body.toStrLit result = body -proc findPragmaExprForFieldSym(arg, fieldSym: NimNode): NimNode = +proc findPragmaNodeInRecList(arg, fieldSym: NimNode): NimNode = case arg.kind of nnkRecList, nnkRecCase: for it in arg.children: - result = findPragmaExprForFieldSym(it, fieldSym) - if result != nil: - return + result = findPragmaNodeInRecList(it, fieldSym) + if result != nil: return of nnkOfBranch: - return findPragmaExprForFieldSym(arg[1], fieldSym) + return findPragmaNodeInRecList(arg[1], fieldSym) of nnkElse: - return findPragmaExprForFieldSym(arg[0], fieldSym) + return findPragmaNodeInRecList(arg[0], fieldSym) of nnkIdentDefs: - for i in 0 ..< arg.len-2: - let child = arg[i] - result = findPragmaExprForFieldSym(child, fieldSym) - if result != nil: - return + for i in 0.. Date: Thu, 15 Apr 2021 15:45:38 +0200 Subject: [PATCH 1935/3768] Fix array's high & low return type for empty arrays (#17705) * fix array.high/low return type * Add test for empty array low return type Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> --- compiler/semexprs.nim | 2 ++ lib/system.nim | 8 ++++++++ tests/array/tarray.nim | 7 +++++++ 3 files changed, 17 insertions(+) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index cbd4318f7..13b53ef32 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -379,6 +379,8 @@ proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode = n.typ = getSysType(c.graph, n.info, tyInt) of tyArray: n.typ = typ[0] # indextype + if n.typ.kind == tyRange and emptyRange(n.typ.n[0], n.typ.n[1]): #Invalid range + n.typ = getSysType(c.graph, n.info, tyInt) of tyInt..tyInt64, tyChar, tyBool, tyEnum, tyUInt..tyUInt64, tyFloat..tyFloat64: n.typ = n[1].typ.skipTypes({tyTypeDesc}) of tyGenericParam: diff --git a/lib/system.nim b/lib/system.nim index 6e84aca66..38f6d64d3 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -348,6 +348,8 @@ proc high*[T](x: openArray[T]): int {.magic: "High", noSideEffect.} proc high*[I, T](x: array[I, T]): I {.magic: "High", noSideEffect.} ## Returns the highest possible index of an array `x`. ## + ## For empty arrays, the return type is `int`. + ## ## See also: ## * `low(array) <#low,array[I,T]>`_ ## @@ -360,6 +362,8 @@ proc high*[I, T](x: array[I, T]): I {.magic: "High", noSideEffect.} proc high*[I, T](x: typedesc[array[I, T]]): I {.magic: "High", noSideEffect.} ## Returns the highest possible index of an array type. ## + ## For empty arrays, the return type is `int`. + ## ## See also: ## * `low(typedesc[array]) <#low,typedesc[array[I,T]]>`_ ## @@ -420,6 +424,8 @@ proc low*[T](x: openArray[T]): int {.magic: "Low", noSideEffect.} proc low*[I, T](x: array[I, T]): I {.magic: "Low", noSideEffect.} ## Returns the lowest possible index of an array `x`. ## + ## For empty arrays, the return type is `int`. + ## ## See also: ## * `high(array) <#high,array[I,T]>`_ ## @@ -432,6 +438,8 @@ proc low*[I, T](x: array[I, T]): I {.magic: "Low", noSideEffect.} proc low*[I, T](x: typedesc[array[I, T]]): I {.magic: "Low", noSideEffect.} ## Returns the lowest possible index of an array type. ## + ## For empty arrays, the return type is `int`. + ## ## See also: ## * `high(typedesc[array]) <#high,typedesc[array[I,T]]>`_ ## diff --git a/tests/array/tarray.nim b/tests/array/tarray.nim index 81a43f203..2765ad06d 100644 --- a/tests/array/tarray.nim +++ b/tests/array/tarray.nim @@ -587,3 +587,10 @@ block t12466: a[0'u16 + i] = i for i in 0'u16 ..< 8'u16: a[0'u16 + i] = i + +block t17705: + # https://github.com/nim-lang/Nim/pull/17705 + var a = array[0, int].low + a = int(a) + var b = array[0, int].high + b = int(b) From 3f56b9944e5e81338630060bdc89454853340792 Mon Sep 17 00:00:00 2001 From: flywind Date: Fri, 16 Apr 2021 01:25:03 +0800 Subject: [PATCH 1936/3768] follow up #17536 (#17727) --- tests/sets/t5792.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/sets/t5792.nim b/tests/sets/t5792.nim index efc38b1bc..297a1fc15 100644 --- a/tests/sets/t5792.nim +++ b/tests/sets/t5792.nim @@ -14,4 +14,4 @@ type of {b, c} - {a}: y: int -discard U(k: b, y: 1) +doAssert U(k: b, y: 1).y == 1 From 5c12c711f7076d0b095d6ca392de4e11834ceb0f Mon Sep 17 00:00:00 2001 From: flywind Date: Fri, 16 Apr 2021 01:25:35 +0800 Subject: [PATCH 1937/3768] follow up #17518 (#17726) --- tests/vm/tvmmisc.nim | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/vm/tvmmisc.nim b/tests/vm/tvmmisc.nim index f542fa560..f0d2c2dbf 100644 --- a/tests/vm/tvmmisc.nim +++ b/tests/vm/tvmmisc.nim @@ -279,6 +279,14 @@ block: # bug #10815 const a = P() doAssert $a == "" + +when defined osx: # xxx bug https://github.com/nim-lang/Nim/issues/10815#issuecomment-476380734 + block: + type CharSet {.union.} = object + cs: set[char] + vs: array[4, uint64] + const a = Charset(cs: {'a'..'z'}) + doAssert a.repr.len > 0 import tables From 95e8ddabb2bcc37e5f1bdd62bcc891b96f80083a Mon Sep 17 00:00:00 2001 From: shirleyquirk <31934565+shirleyquirk@users.noreply.github.com> Date: Thu, 15 Apr 2021 21:45:51 +0100 Subject: [PATCH 1938/3768] followup #17700 put changelog in wrong file (#17729) * Allow use of colons inside fmt allowing colons inside fmt by replacing the format specifier delimiter lets arbitrary nim code be run within fmt expressions. * oops * Update strformat.nim * one space. * Update lib/pure/strformat.nim Co-authored-by: Andreas Rumpf * Update lib/pure/strformat.nim Co-authored-by: Andreas Rumpf * changed parser to ignore ':' within parens * Update strformat.nim * Update lib/pure/strformat.nim Co-authored-by: flywind * formatting,documentation,backslash escapes Adding support for evaluating expressions by special-casing parentheses causes this regression: `&"""{ "(hello)" }"""` no longer parses. In addition, code such as &"""{(if open: '(' else: ')')}""" wouldn't work. To enable that, as well as the use of, e.g. Table constructors inside curlies, I've added backslash escapes. This also means that if/for/etc statements, unparenthesized, will work, if the colons are escaped, but i've left that under-documented. It's not exactly elegant having two types of escape, but I believe it's the least bad option. * not sure about how this works * changelog * changelog * added json strformat test * pulled my thumb out and wrote a parser * more escapes and string tests * ok, now i'm sure it's broken but cant get a failing test * found it * ok, that'll do * did i delete this? * spcng * spacing * messed up where changelogs are supposed to go * ocd Co-authored-by: Andreas Rumpf Co-authored-by: flywind --- changelog.md | 1 + changelogs/changelog_X_XX_X.md | 2 -- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index ec1690e06..aeaeb598d 100644 --- a/changelog.md +++ b/changelog.md @@ -38,6 +38,7 @@ unless `-d:nimLegacyHomeDir` is specified (for a transition period). ## Standard library additions and changes +- Added support for parenthesized expressions in `strformat` - Added `sections` iterator in `parsecfg`. diff --git a/changelogs/changelog_X_XX_X.md b/changelogs/changelog_X_XX_X.md index d68ef4c20..524599b11 100644 --- a/changelogs/changelog_X_XX_X.md +++ b/changelogs/changelog_X_XX_X.md @@ -13,8 +13,6 @@ The changes should go to changelog.md! - Changed `example.foo` to take additional `bar` parameter. -- Added support for evaluating parenthesised expressions in strformat - ## Language changes From 12783dbcf0075492a3d090e4dc3ead3628c18a3a Mon Sep 17 00:00:00 2001 From: flywind Date: Fri, 16 Apr 2021 14:50:44 +0800 Subject: [PATCH 1939/3768] make the copy operation of Thread an error (#17734) --- lib/system/threads.nim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/system/threads.nim b/lib/system/threads.nim index def35c238..7fbfe54ab 100644 --- a/lib/system/threads.nim +++ b/lib/system/threads.nim @@ -90,6 +90,8 @@ type dataFn: proc (m: TArg) {.nimcall, gcsafe.} data: TArg +proc `=copy`*[TArg](x: var Thread[TArg], y: Thread[TArg]) {.error.} + var threadDestructionHandlers {.rtlThreadVar.}: seq[proc () {.closure, gcsafe, raises: [].}] From 8161b02897a75c4b30593dbcc189cbd49d3832ea Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 16 Apr 2021 00:16:39 -0700 Subject: [PATCH 1940/3768] `import foo {.all.}` reboot (#17706) --- changelog.md | 9 ++++ compiler/ast.nim | 8 +-- compiler/ic/ic.nim | 58 ++++++++++++++------ compiler/ic/rodfiles.nim | 1 + compiler/importer.nim | 67 +++++++++++++++++------ compiler/lookups.nim | 12 +++-- compiler/magicsys.nim | 1 + compiler/modulegraphs.nim | 64 ++++++++++++++-------- compiler/modules.nim | 2 +- compiler/nimeval.nim | 2 +- compiler/options.nim | 2 +- compiler/semdata.nim | 8 ++- compiler/semmagic.nim | 5 ++ compiler/semstmts.nim | 1 + compiler/semtypes.nim | 7 +-- compiler/suggest.nim | 13 +++-- compiler/wordrecg.nim | 2 +- lib/std/importutils.nim | 34 ++++++++++++ testament/categories.nim | 1 + tests/ic/mimports.nim | 9 ++++ tests/ic/mimportsb.nim | 4 ++ tests/ic/timports.nim | 29 ++++++++++ tests/importalls/m1.nim | 70 ++++++++++++++++++++++++ tests/importalls/m2.nim | 3 ++ tests/importalls/m3.nim | 5 ++ tests/importalls/m4.nim | 10 ++++ tests/importalls/mt0.nim | 9 ++++ tests/importalls/mt1.nim | 23 ++++++++ tests/importalls/mt2.nim | 104 ++++++++++++++++++++++++++++++++++++ tests/importalls/mt3.nim | 12 +++++ tests/importalls/mt4.nim | 4 ++ tests/importalls/mt4b.nim | 3 ++ tests/importalls/mt5.nim | 9 ++++ tests/importalls/mt6.nim | 13 +++++ tests/importalls/mt7.nim | 14 +++++ tests/importalls/mt8.nim | 23 ++++++++ tests/importalls/mt9.nim | 12 +++++ tests/importalls/tmain2.nim | 7 +++ 38 files changed, 584 insertions(+), 76 deletions(-) create mode 100644 lib/std/importutils.nim create mode 100644 tests/ic/mimports.nim create mode 100644 tests/ic/mimportsb.nim create mode 100644 tests/ic/timports.nim create mode 100644 tests/importalls/m1.nim create mode 100644 tests/importalls/m2.nim create mode 100644 tests/importalls/m3.nim create mode 100644 tests/importalls/m4.nim create mode 100644 tests/importalls/mt0.nim create mode 100644 tests/importalls/mt1.nim create mode 100644 tests/importalls/mt2.nim create mode 100644 tests/importalls/mt3.nim create mode 100644 tests/importalls/mt4.nim create mode 100644 tests/importalls/mt4b.nim create mode 100644 tests/importalls/mt5.nim create mode 100644 tests/importalls/mt6.nim create mode 100644 tests/importalls/mt7.nim create mode 100644 tests/importalls/mt8.nim create mode 100644 tests/importalls/mt9.nim create mode 100644 tests/importalls/tmain2.nim diff --git a/changelog.md b/changelog.md index aeaeb598d..6dcfab9c6 100644 --- a/changelog.md +++ b/changelog.md @@ -307,6 +307,15 @@ - Added `iterable[T]` type class to match called iterators, which enables writing: `template fn(a: iterable)` instead of `template fn(a: untyped)` +- A new import syntax `import foo {.all.}` now allows to import all symbols (public or private) + from `foo`. It works in combination with all pre-existing import features. + This reduces or eliminates the need for workarounds such as using `include` (which has known issues) + when you need a private symbol for testing or making some internal APIs public just because + another internal module needs those. + It also helps mitigate the lack of cyclic imports in some cases. + +- Added a new module `std/importutils`, and an API `privateAccess`, which allows access to private fields + for an object type in the current scope. ## Compiler changes diff --git a/compiler/ast.nim b/compiler/ast.nim index 155af6375..4c835871f 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -605,6 +605,8 @@ type const routineKinds* = {skProc, skFunc, skMethod, skIterator, skConverter, skMacro, skTemplate} + ExportableSymKinds* = {skVar, skLet, skConst, skType, skEnumField, skStub, skAlias} + routineKinds + tfUnion* = tfNoSideEffect tfGcSafe* = tfThread tfObjHasKids* = tfEnumHasHoles @@ -691,7 +693,7 @@ type mInstantiationInfo, mGetTypeInfo, mGetTypeInfoV2, mNimvm, mIntDefine, mStrDefine, mBoolDefine, mRunnableExamples, mException, mBuiltinType, mSymOwner, mUncheckedArray, mGetImplTransf, - mSymIsInstantiationOf, mNodeId + mSymIsInstantiationOf, mNodeId, mPrivateAccess # things that we can evaluate safely at compile time, even if not asked for it: @@ -841,6 +843,7 @@ type depthLevel*: int symbols*: TStrTable parent*: PScope + allowPrivateAccess*: seq[PSym] # # enable access to private fields PScope* = ref TScope @@ -1011,9 +1014,6 @@ const NilableTypes*: TTypeKinds = {tyPointer, tyCString, tyRef, tyPtr, tyProc, tyError} # TODO PtrLikeKinds*: TTypeKinds = {tyPointer, tyPtr} # for VM - ExportableSymKinds* = {skVar, skConst, skProc, skFunc, skMethod, skType, - skIterator, - skMacro, skTemplate, skConverter, skEnumField, skLet, skStub, skAlias} PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16, nfDotSetter, nfDotField, nfIsRef, nfIsPtr, nfPreventCg, nfLL, diff --git a/compiler/ic/ic.nim b/compiler/ic/ic.nim index 4f63ce03a..1cd03ecfa 100644 --- a/compiler/ic/ic.nim +++ b/compiler/ic/ic.nim @@ -36,6 +36,7 @@ type bodies*: PackedTree # other trees. Referenced from typ.n and sym.ast by their position. #producedGenerics*: Table[GenericKey, SymId] exports*: seq[(LitId, int32)] + hidden*: seq[(LitId, int32)] reexports*: seq[(LitId, PackedItemId)] compilerProcs*: seq[(LitId, int32)] converters*, methods*, trmacros*, pureEnums*: seq[int32] @@ -177,6 +178,10 @@ proc addIncludeFileDep*(c: var PackedEncoder; m: var PackedModule; f: FileIndex) proc addImportFileDep*(c: var PackedEncoder; m: var PackedModule; f: FileIndex) = m.imports.add toLitId(f, c, m) +proc addHidden*(c: var PackedEncoder; m: var PackedModule; s: PSym) = + let nameId = getOrIncl(m.sh.strings, s.name.s) + m.hidden.add((nameId, s.itemId.item)) + proc addExported*(c: var PackedEncoder; m: var PackedModule; s: PSym) = let nameId = getOrIncl(m.sh.strings, s.name.s) m.exports.add((nameId, s.itemId.item)) @@ -524,7 +529,7 @@ proc loadRodFile*(filename: AbsoluteFile; m: var PackedModule; config: ConfigRef loadTabSection numbersSection, m.sh.numbers loadSeqSection exportsSection, m.exports - + loadSeqSection hiddenSection, m.hidden loadSeqSection reexportsSection, m.reexports loadSeqSection compilerProcsSection, m.compilerProcs @@ -589,7 +594,7 @@ proc saveRodFile*(filename: AbsoluteFile; encoder: var PackedEncoder; m: var Pac storeTabSection numbersSection, m.sh.numbers storeSeqSection exportsSection, m.exports - + storeSeqSection hiddenSection, m.hidden storeSeqSection reexportsSection, m.reexports storeSeqSection compilerProcsSection, m.compilerProcs @@ -655,7 +660,9 @@ type syms: seq[PSym] # indexed by itemId types: seq[PType] module*: PSym # the one true module symbol. - iface: Table[PIdent, seq[PackedItemId]] # PackedItemId so that it works with reexported symbols too + iface, ifaceHidden: Table[PIdent, seq[PackedItemId]] + # PackedItemId so that it works with reexported symbols too + # ifaceHidden includes private symbols PackedModuleGraph* = seq[LoadedModule] # indexed by FileIndex @@ -882,12 +889,22 @@ proc newPackage(config: ConfigRef; cache: IdentCache; fileIdx: FileIndex): PSym proc setupLookupTables(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache; fileIdx: FileIndex; m: var LoadedModule) = m.iface = initTable[PIdent, seq[PackedItemId]]() - for e in m.fromDisk.exports: + m.ifaceHidden = initTable[PIdent, seq[PackedItemId]]() + template impl(iface, e) = let nameLit = e[0] - m.iface.mgetOrPut(cache.getIdent(m.fromDisk.sh.strings[nameLit]), @[]).add(PackedItemId(module: LitId(0), item: e[1])) - for re in m.fromDisk.reexports: - let nameLit = re[0] - m.iface.mgetOrPut(cache.getIdent(m.fromDisk.sh.strings[nameLit]), @[]).add(re[1]) + let e2 = + when e[1] is PackedItemId: e[1] + else: PackedItemId(module: LitId(0), item: e[1]) + iface.mgetOrPut(cache.getIdent(m.fromDisk.sh.strings[nameLit]), @[]).add(e2) + + for e in m.fromDisk.exports: + m.iface.impl(e) + m.ifaceHidden.impl(e) + for e in m.fromDisk.reexports: + m.iface.impl(e) + m.ifaceHidden.impl(e) + for e in m.fromDisk.hidden: + m.ifaceHidden.impl(e) let filename = AbsoluteFile toFullPath(conf, fileIdx) # We cannot call ``newSym`` here, because we have to circumvent the ID @@ -1053,16 +1070,21 @@ type values: seq[PackedItemId] i, module: int +template interfSelect(a: LoadedModule, importHidden: bool): auto = + var ret = a.iface.addr + if importHidden: ret = a.ifaceHidden.addr + ret[] + proc initRodIter*(it: var RodIter; config: ConfigRef, cache: IdentCache; g: var PackedModuleGraph; module: FileIndex; - name: PIdent): PSym = + name: PIdent, importHidden: bool): PSym = it.decoder = PackedDecoder( lastModule: int32(-1), lastLit: LitId(0), lastFile: FileIndex(-1), config: config, cache: cache) - it.values = g[int module].iface.getOrDefault(name) + it.values = g[int module].interfSelect(importHidden).getOrDefault(name) it.i = 0 it.module = int(module) if it.i < it.values.len: @@ -1070,7 +1092,7 @@ proc initRodIter*(it: var RodIter; config: ConfigRef, cache: IdentCache; inc it.i proc initRodIterAllSyms*(it: var RodIter; config: ConfigRef, cache: IdentCache; - g: var PackedModuleGraph; module: FileIndex): PSym = + g: var PackedModuleGraph; module: FileIndex, importHidden: bool): PSym = it.decoder = PackedDecoder( lastModule: int32(-1), lastLit: LitId(0), @@ -1079,7 +1101,7 @@ proc initRodIterAllSyms*(it: var RodIter; config: ConfigRef, cache: IdentCache; cache: cache) it.values = @[] it.module = int(module) - for v in g[int module].iface.values: + for v in g[int module].interfSelect(importHidden).values: it.values.add v it.i = 0 if it.i < it.values.len: @@ -1093,9 +1115,9 @@ proc nextRodIter*(it: var RodIter; g: var PackedModuleGraph): PSym = iterator interfaceSymbols*(config: ConfigRef, cache: IdentCache; g: var PackedModuleGraph; module: FileIndex; - name: PIdent): PSym = + name: PIdent, importHidden: bool): PSym = setupDecoder() - let values = g[int module].iface.getOrDefault(name) + let values = g[int module].interfSelect(importHidden).getOrDefault(name) for pid in values: let s = loadSym(decoder, g, int(module), pid) assert s != nil @@ -1103,9 +1125,9 @@ iterator interfaceSymbols*(config: ConfigRef, cache: IdentCache; proc interfaceSymbol*(config: ConfigRef, cache: IdentCache; g: var PackedModuleGraph; module: FileIndex; - name: PIdent): PSym = + name: PIdent, importHidden: bool): PSym = setupDecoder() - let values = g[int module].iface.getOrDefault(name) + let values = g[int module].interfSelect(importHidden).getOrDefault(name) result = loadSym(decoder, g, int(module), values[0]) proc idgenFromLoadedModule*(m: LoadedModule): IdGenerator = @@ -1140,6 +1162,10 @@ proc rodViewer*(rodfile: AbsoluteFile; config: ConfigRef, cache: IdentCache) = echo " ", m.sh.strings[ex[0]] # reexports*: seq[(LitId, PackedItemId)] + echo "hidden: " & $m.hidden.len + for ex in m.hidden: + echo " ", m.sh.strings[ex[0]], " local ID: ", ex[1] + echo "all symbols" for i in 0..high(m.sh.syms): if m.sh.syms[i].name != LitId(0): diff --git a/compiler/ic/rodfiles.nim b/compiler/ic/rodfiles.nim index fbe1cad33..47362da0b 100644 --- a/compiler/ic/rodfiles.nim +++ b/compiler/ic/rodfiles.nim @@ -18,6 +18,7 @@ type depsSection numbersSection exportsSection + hiddenSection reexportsSection compilerProcsSection trmacrosSection diff --git a/compiler/importer.nim b/compiler/importer.nim index f8ee0d483..692cdb604 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -12,7 +12,7 @@ import intsets, ast, astalgo, msgs, options, idents, lookups, semdata, modulepaths, sigmatch, lineinfos, sets, - modulegraphs + modulegraphs, wordrecg proc readExceptSet*(c: PContext, n: PNode): IntSet = assert n.kind in {nkImportExceptStmt, nkExportExceptStmt} @@ -107,7 +107,25 @@ proc rawImportSymbol(c: PContext, s, origin: PSym; importSet: var IntSet) = if s.owner != origin: c.exportIndirections.incl((origin.id, s.id)) +proc splitPragmas(c: PContext, n: PNode): (PNode, seq[TSpecialWord]) = + template bail = globalError(c.config, n.info, "invalid pragma") + if n.kind == nkPragmaExpr: + if n.len == 2 and n[1].kind == nkPragma: + result[0] = n[0] + for ni in n[1]: + if ni.kind == nkIdent: result[1].add whichKeyword(ni.ident) + else: bail() + else: bail() + else: + result[0] = n + if result[0].safeLen > 0: + (result[0][^1], result[1]) = splitPragmas(c, result[0][^1]) + proc importSymbol(c: PContext, n: PNode, fromMod: PSym; importSet: var IntSet) = + let (n, kws) = splitPragmas(c, n) + if kws.len > 0: + globalError(c.config, n.info, "unexpected pragma") + let ident = lookups.considerQuotedIdent(c, n) let s = someSym(c.graph, fromMod, ident) if s == nil: @@ -204,18 +222,43 @@ proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet; fromMod: PSym; im for i in 0..n.safeLen-1: importForwarded(c, n[i], exceptSet, fromMod, importSet) -proc importModuleAs(c: PContext; n: PNode, realModule: PSym): PSym = +proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden: bool): PSym = result = realModule c.unusedImports.add((realModule, n.info)) + template createModuleAliasImpl(ident): untyped = + createModuleAlias(realModule, nextSymId c.idgen, ident, realModule.info, c.config.options) if n.kind != nkImportAs: discard elif n.len != 2 or n[1].kind != nkIdent: localError(c.config, n.info, "module alias must be an identifier") elif n[1].ident.id != realModule.name.id: # some misguided guy will write 'import abc.foo as foo' ... - result = createModuleAlias(realModule, nextSymId c.idgen, n[1].ident, realModule.info, - c.config.options) + result = createModuleAliasImpl(n[1].ident) + if importHidden: + if result == realModule: # avoids modifying `realModule`, see D20201209T194412. + result = createModuleAliasImpl(realModule.name) + result.options.incl optImportHidden -proc myImportModule(c: PContext, n: PNode; importStmtResult: PNode): PSym = +proc transformImportAs(c: PContext; n: PNode): tuple[node: PNode, importHidden: bool] = + var ret: typeof(result) + proc processPragma(n2: PNode): PNode = + let (result2, kws) = splitPragmas(c, n2) + result = result2 + for ai in kws: + case ai + of wImportHidden: ret.importHidden = true + else: globalError(c.config, n.info, "invalid pragma, expected: " & ${wImportHidden}) + + if n.kind == nkInfix and considerQuotedIdent(c, n[0]).s == "as": + ret.node = newNodeI(nkImportAs, n.info) + ret.node.add n[1].processPragma + ret.node.add n[2] + else: + ret.node = n.processPragma + return ret + +proc myImportModule(c: PContext, n: var PNode, importStmtResult: PNode): PSym = + let transf = transformImportAs(c, n) + n = transf.node let f = checkModuleName(c.config, n) if f != InvalidFileIdx: addImportFileDep(c, f) @@ -232,7 +275,7 @@ proc myImportModule(c: PContext, n: PNode; importStmtResult: PNode): PSym = c.recursiveDep = err discard pushOptionEntry(c) - result = importModuleAs(c, n, c.graph.importModuleCallback(c.graph, c.module, f)) + result = importModuleAs(c, n, c.graph.importModuleCallback(c.graph, c.module, f), transf.importHidden) popOptionEntry(c) #echo "set back to ", L @@ -252,16 +295,8 @@ proc myImportModule(c: PContext, n: PNode; importStmtResult: PNode): PSym = importStmtResult.add newSymNode(result, n.info) #newStrNode(toFullPath(c.config, f), n.info) -proc transformImportAs(c: PContext; n: PNode): PNode = - if n.kind == nkInfix and considerQuotedIdent(c, n[0]).s == "as": - result = newNodeI(nkImportAs, n.info) - result.add n[1] - result.add n[2] - else: - result = n - proc impMod(c: PContext; it: PNode; importStmtResult: PNode) = - let it = transformImportAs(c, it) + var it = it let m = myImportModule(c, it, importStmtResult) if m != nil: # ``addDecl`` needs to be done before ``importAllSymbols``! @@ -296,7 +331,6 @@ proc evalImport*(c: PContext, n: PNode): PNode = proc evalFrom*(c: PContext, n: PNode): PNode = result = newNodeI(nkImportStmt, n.info) checkMinSonsLen(n, 2, c.config) - n[0] = transformImportAs(c, n[0]) var m = myImportModule(c, n[0], result) if m != nil: n[0] = newSymNode(m) @@ -311,7 +345,6 @@ proc evalFrom*(c: PContext, n: PNode): PNode = proc evalImportExcept*(c: PContext, n: PNode): PNode = result = newNodeI(nkImportStmt, n.info) checkMinSonsLen(n, 2, c.config) - n[0] = transformImportAs(c, n[0]) var m = myImportModule(c, n[0], result) if m != nil: n[0] = newSymNode(m) diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 15a22c778..7ceadfb96 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -75,7 +75,7 @@ proc closeScope*(c: PContext) = ensureNoMissingOrUnusedSymbols(c, c.currentScope) rawCloseScope(c) -iterator allScopes(scope: PScope): PScope = +iterator allScopes*(scope: PScope): PScope = var current = scope while current != nil: yield current @@ -311,11 +311,17 @@ proc addDeclAt*(c: PContext; scope: PScope, sym: PSym) = if conflict != nil: wrongRedefinition(c, sym.info, sym.name.s, conflict.info) -proc addInterfaceDeclAux(c: PContext, sym: PSym) = - if sfExported in sym.flags: +from ic / ic import addHidden + +proc addInterfaceDeclAux*(c: PContext, sym: PSym, forceExport = false) = + if sfExported in sym.flags or forceExport: # add to interface: if c.module != nil: exportSym(c, sym) else: internalError(c.config, sym.info, "addInterfaceDeclAux") + elif sym.kind in ExportableSymKinds and c.module != nil and isTopLevelInsideDeclaration(c, sym): + strTableAdd(semtabAll(c.graph, c.module), sym) + if c.config.symbolFiles != disabledSf: + addHidden(c.encoder, c.packedRepr, sym) proc addInterfaceDeclAt*(c: PContext, scope: PScope, sym: PSym) = addDeclAt(c, scope, sym) diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim index e91bdf272..38d2345c8 100644 --- a/compiler/magicsys.nim +++ b/compiler/magicsys.nim @@ -50,6 +50,7 @@ proc getSysType*(g: ModuleGraph; info: TLineInfo; kind: TTypeKind): PType = result = g.sysTypes[kind] if result == nil: case kind + of tyVoid: result = sysTypeFromName("void") of tyInt: result = sysTypeFromName("int") of tyInt8: result = sysTypeFromName("int8") of tyInt16: result = sysTypeFromName("int16") diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index 02c745018..9df66269b 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -29,6 +29,7 @@ type patterns*: seq[LazySym] pureEnums*: seq[LazySym] interf: TStrTable + interfHidden: TStrTable uniqueName*: Rope Operators* = object @@ -160,9 +161,25 @@ proc toBase64a(s: cstring, len: int): string = result.add cb64[a shr 2] result.add cb64[(a and 3) shl 4] -template semtab*(m: PSym; g: ModuleGraph): TStrTable = +template interfSelect(iface: Iface, importHidden: bool): TStrTable = + var ret = iface.interf.addr # without intermediate ptr, it creates a copy and compiler becomes 15x slower! + if importHidden: ret = iface.interfHidden.addr + ret[] + +template semtab(g: ModuleGraph, m: PSym): TStrTable = g.ifaces[m.position].interf +template semtabAll*(g: ModuleGraph, m: PSym): TStrTable = + g.ifaces[m.position].interfHidden + +proc initStrTables*(g: ModuleGraph, m: PSym) = + initStrTable(semtab(g, m)) + initStrTable(semtabAll(g, m)) + +proc strTableAdds*(g: ModuleGraph, m: PSym, s: PSym) = + strTableAdd(semtab(g, m), s) + strTableAdd(semtabAll(g, m), s) + proc isCachedModule(g: ModuleGraph; module: int): bool {.inline.} = result = module < g.packed.len and g.packed[module].status == loaded @@ -187,39 +204,43 @@ type modIndex: int ti: TIdentIter rodIt: RodIter + importHidden: bool proc initModuleIter*(mi: var ModuleIter; g: ModuleGraph; m: PSym; name: PIdent): PSym = assert m.kind == skModule mi.modIndex = m.position mi.fromRod = isCachedModule(g, mi.modIndex) + mi.importHidden = optImportHidden in m.options if mi.fromRod: - result = initRodIter(mi.rodIt, g.config, g.cache, g.packed, FileIndex mi.modIndex, name) + result = initRodIter(mi.rodIt, g.config, g.cache, g.packed, FileIndex mi.modIndex, name, mi.importHidden) else: - result = initIdentIter(mi.ti, g.ifaces[mi.modIndex].interf, name) + result = initIdentIter(mi.ti, g.ifaces[mi.modIndex].interfSelect(mi.importHidden), name) proc nextModuleIter*(mi: var ModuleIter; g: ModuleGraph): PSym = if mi.fromRod: result = nextRodIter(mi.rodIt, g.packed) else: - result = nextIdentIter(mi.ti, g.ifaces[mi.modIndex].interf) + result = nextIdentIter(mi.ti, g.ifaces[mi.modIndex].interfSelect(mi.importHidden)) iterator allSyms*(g: ModuleGraph; m: PSym): PSym = + let importHidden = optImportHidden in m.options if isCachedModule(g, m): var rodIt: RodIter - var r = initRodIterAllSyms(rodIt, g.config, g.cache, g.packed, FileIndex m.position) + var r = initRodIterAllSyms(rodIt, g.config, g.cache, g.packed, FileIndex m.position, importHidden) while r != nil: yield r r = nextRodIter(rodIt, g.packed) else: - for s in g.ifaces[m.position].interf.data: + for s in g.ifaces[m.position].interfSelect(importHidden).data: if s != nil: yield s proc someSym*(g: ModuleGraph; m: PSym; name: PIdent): PSym = + let importHidden = optImportHidden in m.options if isCachedModule(g, m): - result = interfaceSymbol(g.config, g.cache, g.packed, FileIndex(m.position), name) + result = interfaceSymbol(g.config, g.cache, g.packed, FileIndex(m.position), name, importHidden) else: - result = strTableGet(g.ifaces[m.position].interf, name) + result = strTableGet(g.ifaces[m.position].interfSelect(importHidden), name) proc systemModuleSym*(g: ModuleGraph; name: PIdent): PSym = result = someSym(g, g.systemModule, name) @@ -343,26 +364,23 @@ proc hash*(u: SigHash): Hash = proc hash*(x: FileIndex): Hash {.borrow.} +template getPContext(): untyped = + when c is PContext: c + else: c.c + when defined(nimfind): template onUse*(info: TLineInfo; s: PSym) = - when compiles(c.c.graph): - if c.c.graph.onUsage != nil: c.c.graph.onUsage(c.c.graph, s, info) - else: - if c.graph.onUsage != nil: c.graph.onUsage(c.graph, s, info) + let c = getPContext() + if c.graph.onUsage != nil: c.graph.onUsage(c.graph, s, info) template onDef*(info: TLineInfo; s: PSym) = - when compiles(c.c.graph): - if c.c.graph.onDefinition != nil: c.c.graph.onDefinition(c.c.graph, s, info) - else: - if c.graph.onDefinition != nil: c.graph.onDefinition(c.graph, s, info) + let c = getPContext() + if c.graph.onDefinition != nil: c.graph.onDefinition(c.graph, s, info) template onDefResolveForward*(info: TLineInfo; s: PSym) = - when compiles(c.c.graph): - if c.c.graph.onDefinitionResolveForward != nil: - c.c.graph.onDefinitionResolveForward(c.c.graph, s, info) - else: - if c.graph.onDefinitionResolveForward != nil: - c.graph.onDefinitionResolveForward(c.graph, s, info) + let c = getPContext() + if c.graph.onDefinitionResolveForward != nil: + c.graph.onDefinitionResolveForward(c.graph, s, info) else: template onUse*(info: TLineInfo; s: PSym) = discard @@ -392,7 +410,7 @@ proc registerModule*(g: ModuleGraph; m: PSym) = g.ifaces[m.position] = Iface(module: m, converters: @[], patterns: @[], uniqueName: rope(uniqueModuleName(g.config, FileIndex(m.position)))) - initStrTable(g.ifaces[m.position].interf) + initStrTables(g, m) proc registerModuleById*(g: ModuleGraph; m: FileIndex) = registerModule(g, g.packed[int m].module) diff --git a/compiler/modules.nim b/compiler/modules.nim index 7d7a2b6f7..27b93e8df 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -115,7 +115,7 @@ proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags): P elif graph.isDirty(result): result.flags.excl sfDirty # reset module fields: - initStrTable(result.semtab(graph)) + initStrTables(graph, result) result.ast = nil processModuleAux() graph.markClientsDirty(fileIdx) diff --git a/compiler/nimeval.nim b/compiler/nimeval.nim index 9be595cce..577c7b586 100644 --- a/compiler/nimeval.nim +++ b/compiler/nimeval.nim @@ -67,7 +67,7 @@ proc evalScript*(i: Interpreter; scriptStream: PLLStream = nil) = ## This can also be used to *reload* the script. assert i != nil assert i.mainModule != nil, "no main module selected" - initStrTable(i.mainModule.semtab(i.graph)) + initStrTables(i.graph, i.mainModule) i.mainModule.ast = nil let s = if scriptStream != nil: scriptStream diff --git a/compiler/options.nim b/compiler/options.nim index 9eeffb4fb..9ba4d62e4 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -41,7 +41,7 @@ type # please make sure we have under 32 options optMemTracker, optSinkInference # 'sink T' inference optCursorInference - + optImportHidden TOptions* = set[TOption] TGlobalOption* = enum # **keep binary compatible** diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 6b0dcb0e2..9e7258a69 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -361,12 +361,12 @@ proc addPattern*(c: PContext, p: LazySym) = addTrmacro(c.encoder, c.packedRepr, p.sym) proc exportSym*(c: PContext; s: PSym) = - strTableAdd(c.module.semtab(c.graph), s) + strTableAdds(c.graph, c.module, s) if c.config.symbolFiles != disabledSf: addExported(c.encoder, c.packedRepr, s) proc reexportSym*(c: PContext; s: PSym) = - strTableAdd(c.module.semtab(c.graph), s) + strTableAdds(c.graph, c.module, s) if c.config.symbolFiles != disabledSf: addReexport(c.encoder, c.packedRepr, s) @@ -536,6 +536,10 @@ proc checkMinSonsLen*(n: PNode, length: int; conf: ConfigRef) = proc isTopLevel*(c: PContext): bool {.inline.} = result = c.currentScope.depthLevel <= 2 +proc isTopLevelInsideDeclaration*(c: PContext, sym: PSym): bool {.inline.} = + # for routeKinds the scope isn't closed yet: + c.currentScope.depthLevel <= 2 + ord(sym.kind in routineKinds) + proc pushCaseContext*(c: PContext, caseNode: PNode) = c.p.caseContext.add((caseNode, 0)) diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index c80e689a5..d8be85273 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -577,5 +577,10 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, if n[1].typ.skipTypes(abstractInst).kind in {tyUInt..tyUInt64}: n[0].sym.magic = mSubU result = n + of mPrivateAccess: + let sym = n[1].typ[0].sym + assert sym != nil + c.currentScope.allowPrivateAccess.add sym + result = newNodeIT(nkEmpty, n.info, getSysType(c.graph, n.info, tyVoid)) else: result = n diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 2c257bd3d..bedc5a7e5 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1067,6 +1067,7 @@ proc typeDefLeftSidePass(c: PContext, typeSection: PNode, i: int) = elif typsym.kind == skType and sfForward in typsym.flags: s = typsym addInterfaceDecl(c, s) + # PRTEMP no onDef here? else: localError(c.config, name.info, typsym.name.s & " is not a type that can be forwarded") s = typsym diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 34a90a3f2..4763f0fd3 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -138,9 +138,10 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = identToReplace[] = symNode if e.position == 0: hasNull = true if result.sym != nil and sfExported in result.sym.flags: - incl(e.flags, sfUsed) - incl(e.flags, sfExported) - if not isPure: exportSym(c, e) + incl(e.flags, {sfUsed, sfExported}) + if result.sym != nil and not isPure: + addInterfaceDeclAux(c, e, forceExport = sfExported in result.sym.flags) + result.n.add symNode styleCheckDef(c.config, e) onDef(e.info, e) diff --git a/compiler/suggest.nim b/compiler/suggest.nim index cad776015..7a2c0d964 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -253,10 +253,15 @@ proc filterSymNoOpr(s: PSym; prefix: PNode; res: var PrefixMatch): bool {.inline proc fieldVisible*(c: PContext, f: PSym): bool {.inline.} = let fmoduleId = getModule(f).id result = sfExported in f.flags or fmoduleId == c.module.id - for module in c.friendModules: - if fmoduleId == module.id: - result = true - break + + if not result: + for module in c.friendModules: + if fmoduleId == module.id: return true + if f.kind == skField: + let symObj = f.owner + for scope in allScopes(c.currentScope): + for sym in scope.allowPrivateAccess: + if symObj.id == sym.id: return true proc getQuality(s: PSym): range[0..100] = result = 100 diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index 6a68e2c70..827efcfa0 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -109,7 +109,7 @@ type wStdIn = "stdin", wStdOut = "stdout", wStdErr = "stderr", wInOut = "inout", wByCopy = "bycopy", wByRef = "byref", wOneWay = "oneway", - wBitsize = "bitsize" + wBitsize = "bitsize", wImportHidden = "all", TSpecialWords* = set[TSpecialWord] diff --git a/lib/std/importutils.nim b/lib/std/importutils.nim new file mode 100644 index 000000000..4efe0e140 --- /dev/null +++ b/lib/std/importutils.nim @@ -0,0 +1,34 @@ +##[ +Utilities related to import and symbol resolution. + +Experimental API, subject to change. +]## + +#[ +Possible future APIs: +* module symbols (https://github.com/nim-lang/Nim/pull/9560) +* whichModule (subsumes canImport / moduleExists) (https://github.com/timotheecour/Nim/issues/376) +* getCurrentPkgDir (https://github.com/nim-lang/Nim/pull/10530) +* import from a computed string + related APIs (https://github.com/nim-lang/Nim/pull/10527) +]# + +when defined(nimImportutilsExample): + type Foo = object + x1: int # private + proc initFoo*(): auto = Foo() + +proc privateAccess*(t: typedesc) {.magic: "PrivateAccess".} = + ## Enables access to private fields of `t` in current scope. + runnableExamples("-d:nimImportutilsExample"): + # here we're importing a module containing: + # type Foo = object + # x1: int # private + # proc initFoo*(): auto = Foo() + var a = initFoo() + block: + assert not compiles(a.x1) + privateAccess(a.type) + a.x1 = 1 # accessible in this scope + block: + assert a.x1 == 1 # still in scope + assert not compiles(a.x1) diff --git a/testament/categories.nim b/testament/categories.nim index ee5c46519..18508d70c 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -500,6 +500,7 @@ proc icTests(r: var TResults; testsDir: string, cat: Category, options: string) const tempExt = "_temp.nim" for it in walkDirRec(testsDir / "ic"): + # for it in ["tests/ic/timports.nim"]: # debugging: to try a specific test if isTestFile(it) and not it.endsWith(tempExt): let nimcache = nimcacheDir(it, options, getTestSpecTarget()) removeDir(nimcache) diff --git a/tests/ic/mimports.nim b/tests/ic/mimports.nim new file mode 100644 index 000000000..50773e001 --- /dev/null +++ b/tests/ic/mimports.nim @@ -0,0 +1,9 @@ +from mimportsb {.all.} import fnb1, hfnb3 + +proc fn1*(): int = 1 +proc fn2*(): int = 2 +proc hfn3(): int = 3 +proc hfn4(): int = 4 +proc hfn5(): int = 5 + +export mimportsb.fnb2, hfnb3 diff --git a/tests/ic/mimportsb.nim b/tests/ic/mimportsb.nim new file mode 100644 index 000000000..3cae925c5 --- /dev/null +++ b/tests/ic/mimportsb.nim @@ -0,0 +1,4 @@ +proc fnb1*(): int = 1 +proc fnb2*(): int = 2 +proc hfnb3(): int = 3 +proc hfnb4(): int = 4 diff --git a/tests/ic/timports.nim b/tests/ic/timports.nim new file mode 100644 index 000000000..518a689f5 --- /dev/null +++ b/tests/ic/timports.nim @@ -0,0 +1,29 @@ +import mimports +doAssert fn1() == 1 +doAssert not declared(hfn3) + +#!EDIT!# + +import mimports {.all.} +doAssert fn1() == 1 +doAssert declared(hfn3) +doAssert hfn3() == 3 +doAssert mimports.hfn4() == 4 + +# reexports +doAssert not declared(fnb1) +doAssert not declared(hfnb4) +doAssert fnb2() == 2 +doAssert hfnb3() == 3 + +#!EDIT!# + +from mimports {.all.} import hfn3 +doAssert not declared(fn1) +from mimports {.all.} as bar import fn1 +doAssert fn1() == 1 +doAssert hfn3() == 3 +doAssert not declared(hfn4) +doAssert declared(mimports.hfn4) +doAssert mimports.hfn4() == 4 +doAssert bar.hfn4() == 4 diff --git a/tests/importalls/m1.nim b/tests/importalls/m1.nim new file mode 100644 index 000000000..b6ccbf5da --- /dev/null +++ b/tests/importalls/m1.nim @@ -0,0 +1,70 @@ +import ./m2 +import ./m3 {.all.} as m3 +from ./m3 as m3Bis import nil + +doAssert m3h2 == 2 +export m3h2 + +export m3Bis.m3p1 + +const foo0* = 2 +const foo1 = bar1 + +const foo1Aux = 2 +export foo1Aux + +doAssert not declared(bar2) +doAssert not compiles(bar2) + +var foo2 = 2 +let foo3 = 2 + +type Foo4 = enum + kg1, kg2 + +type Foo4b {.pure.} = enum + foo4b1, foo4b2 + +type Foo5 = object + z1: string + z2: Foo4 + z3: int + z4*: int + +proc `z3`*(a: Foo5): auto = + a.z3 * 10 + +proc foo6(): auto = 2 +proc foo6b*(): auto = 2 +template foo7: untyped = 2 +macro foo8(): untyped = discard +template foo9(a: int) = discard + +block: + template foo10: untyped = 2 + type Foo11 = enum + kg1b, kg2b + proc foo12(): auto = 2 + +proc initFoo5*(z3: int): Foo5 = Foo5(z3: z3) + +func foo13(): auto = 2 +iterator foo14a(): int = discard +iterator foo14b*(): int = discard +iterator foo14c(): int {.closure.} = discard +iterator foo14d(): int {.inline.} = discard + +# fwd declare +proc foo15(): int +proc foo15(): int = 2 + +proc foo16*(): int +proc foo16(): int = 2 + +proc foo17*(): int +proc foo17*(): int = 2 + +# other +type A1 = distinct int +type A2 = distinct int +converter foo18(x: A1): A2 = discard diff --git a/tests/importalls/m2.nim b/tests/importalls/m2.nim new file mode 100644 index 000000000..186650f68 --- /dev/null +++ b/tests/importalls/m2.nim @@ -0,0 +1,3 @@ +const bar1* = 2 +const bar2 = 2 +const bar3 = 3 diff --git a/tests/importalls/m3.nim b/tests/importalls/m3.nim new file mode 100644 index 000000000..caeae2a01 --- /dev/null +++ b/tests/importalls/m3.nim @@ -0,0 +1,5 @@ +# xxx use below naming convention in other test files: p: public, h: hidden +const m3p1* = 2 +const m3h2 = 2 +const m3h3 = 3 +const m3h4 = 4 diff --git a/tests/importalls/m4.nim b/tests/importalls/m4.nim new file mode 100644 index 000000000..b682b766a --- /dev/null +++ b/tests/importalls/m4.nim @@ -0,0 +1,10 @@ +{.warning[UnusedImport]: off.} # xxx bug: this shouldn't be needed since we have `export m3` +import ./m3 {.all.} +import ./m3 as m3b +export m3b +export m3h3 +export m3.m3h4 + +import ./m2 {.all.} as m2b +export m2b except bar3 + diff --git a/tests/importalls/mt0.nim b/tests/importalls/mt0.nim new file mode 100644 index 000000000..154d30ef9 --- /dev/null +++ b/tests/importalls/mt0.nim @@ -0,0 +1,9 @@ +import ./m1 as m +doAssert compiles(foo0) +doAssert not compiles(foo1) +doAssert foo6b() == 2 +doAssert m3h2 == 2 + +var f = initFoo5(z3=3) +doAssert f.z3 == 30 +doAssert z3(f) == 30 diff --git a/tests/importalls/mt1.nim b/tests/importalls/mt1.nim new file mode 100644 index 000000000..87c4eff16 --- /dev/null +++ b/tests/importalls/mt1.nim @@ -0,0 +1,23 @@ +import ./m1 {.all.} as m +doAssert foo1 == 2 +doAssert m.foo1 == 2 + +doAssert m.m3h2 == 2 +doAssert m3h2 == 2 +doAssert m.foo1Aux == 2 +doAssert m.m3p1 == 2 + +## field access +import std/importutils +privateAccess(Foo5) +# var x = Foo5(z1: "foo", z2: m.kg1) +# doAssert x.z1 == "foo" + +var f0: Foo5 +f0.z3 = 3 +doAssert f0.z3 == 3 +var f = initFoo5(z3=3) +doAssert f.z3 == 3 +doAssert z3(f) == 30 +doAssert m.z3(f) == 30 +doAssert not compiles(mt1.`z3`(f)) # z3 is an imported symbol diff --git a/tests/importalls/mt2.nim b/tests/importalls/mt2.nim new file mode 100644 index 000000000..52edbb69e --- /dev/null +++ b/tests/importalls/mt2.nim @@ -0,0 +1,104 @@ +from ./m1 {.all.} as r1 import foo1 +from ./m1 {.all.} as r2 import foo7 + +block: # different symbol kinds + doAssert foo1 == 2 + doAssert r1.foo1 == 2 + doAssert r1.foo2 == 2 + doAssert compiles(foo1) + doAssert compiles(r1.foo2) + doAssert not compiles(foo2) + doAssert not compiles(m3h2) + doAssert r1.foo3 == 2 + + block: # enum + var a: r1.Foo4 + let a1 = r1.kg1 + doAssert a1 == r1.Foo4.kg1 + type A = r1.Foo4 + doAssert a1 == A.kg1 + doAssert not compiles(kg1) + doAssert compiles(A.kg1) + var witness = false + for ai in r1.Foo4: + doAssert ai == a + doAssert ai == a1 + witness = true + break + doAssert witness + + block: # {.pure.} enum + var a: r1.Foo4b + doAssert not compiles(r1.foo4b1) # because pure + doAssert not compiles(foo4b1) + let a1 = r1.Foo4b.foo4b1 + doAssert a1 == a + type A = r1.Foo4b + doAssert a1 == A.foo4b1 + var witness = false + for ai in A: + doAssert ai == a + doAssert ai == a1 + witness = true + break + doAssert witness + + block: # object + doAssert compiles(r1.Foo5) + var a: r1.Foo5 + doAssert compiles(a.z4) + doAssert not compiles(a.z3) + + block: # remaining symbol kinds + doAssert r1.foo6() == 2 + doAssert r1.foo6b() == 2 + doAssert foo7() == 2 + doAssert r2.foo6b() == 2 + + r1.foo8() + r1.foo9(1) + doAssert r1.foo13() == 2 + for a in r1.foo14a(): discard + for a in r1.foo14b(): discard + for a in r1.foo14c(): discard + for a in r1.foo14d(): discard + doAssert r1.foo15() == 2 + doAssert r1.foo16() == 2 + doAssert r1.foo17() == 2 + doAssert compiles(r1.foo18) + doAssert declared(r1.foo18) + + block: # declarations at block scope should not be visible + doAssert declared(foo7) + doAssert declared(r1.foo6) + doAssert not declared(foo10) + doAssert not declared(foo6) + doAssert not declared(r1.Foo11) + doAssert not declared(r1.kg1b) + doAssert not declared(r1.foo12) + doAssert not compiles(r1.foo12()) + +## field access +import std/importutils +privateAccess(r1.Foo5) +var x = r1.Foo5(z1: "foo", z2: r1.kg1) +doAssert x.z1 == "foo" + +var f0: r1.Foo5 +f0.z3 = 3 +doAssert f0.z3 == 3 +var f = r1.initFoo5(z3=3) +doAssert f.z3 == 3 +doAssert r1.z3(f) == 30 + +import ./m1 as r3 +doAssert not declared(foo2) +doAssert not declared(r3.foo2) + +from ./m1 {.all.} as r4 import nil +doAssert not declared(foo2) +doAssert declared(r4.foo2) + +from ./m1 {.all.} import nil +doAssert not declared(foo2) +doAssert declared(m1.foo2) diff --git a/tests/importalls/mt3.nim b/tests/importalls/mt3.nim new file mode 100644 index 000000000..2bd7fd79d --- /dev/null +++ b/tests/importalls/mt3.nim @@ -0,0 +1,12 @@ +import ./m1 {.all.} + # D20201209T194412:here keep this as is, without `as`, so that mt8.nim test keeps + # checking that the original module symbol for `m1` isn't modified and that + # only the alias in `createModuleAlias` is affected. +doAssert declared(m1.foo1) +doAssert foo1 == 2 + + +doAssert m1.foo1 == 2 + +doAssert not compiles(mt3.foo0) # foo0 is an imported symbol +doAssert not compiles(mt3.foo1) # ditto diff --git a/tests/importalls/mt4.nim b/tests/importalls/mt4.nim new file mode 100644 index 000000000..cd7d32aad --- /dev/null +++ b/tests/importalls/mt4.nim @@ -0,0 +1,4 @@ +import ./m1 {.all.} except foo1 +doAssert foo2 == 2 +doAssert declared(foo2) +doAssert not compiles(foo1) diff --git a/tests/importalls/mt4b.nim b/tests/importalls/mt4b.nim new file mode 100644 index 000000000..4578bc54c --- /dev/null +++ b/tests/importalls/mt4b.nim @@ -0,0 +1,3 @@ +from ./m1 {.all.} as m2 import nil +doAssert not compiles(foo1) +doAssert m2.foo1 == 2 diff --git a/tests/importalls/mt5.nim b/tests/importalls/mt5.nim new file mode 100644 index 000000000..5c5785af6 --- /dev/null +++ b/tests/importalls/mt5.nim @@ -0,0 +1,9 @@ +import ./m1 {.all.} as m2 except foo1 +doAssert foo2 == 2 +doAssert not compiles(foo1) +doAssert m2.foo1 == 2 +doAssert compiles(m2.foo1) + +from system {.all.} as s import ThisIsSystem +doAssert ThisIsSystem +doAssert s.ThisIsSystem diff --git a/tests/importalls/mt6.nim b/tests/importalls/mt6.nim new file mode 100644 index 000000000..c6d8b2193 --- /dev/null +++ b/tests/importalls/mt6.nim @@ -0,0 +1,13 @@ +import ./m1 {.all.} as m2 +doAssert compiles(foo1) +doAssert compiles(m2.foo1) +doAssert declared(foo1) +doAssert declared(m2.foo0) # public: works fine + +doAssert m2.foo1 == 2 +doAssert declared(m2.foo1) +doAssert not declared(m2.nonexistant) + +# also tests the quoted `""` import +import "."/"m1" {.all.} as m1b +doAssert compiles(m1b.foo1) diff --git a/tests/importalls/mt7.nim b/tests/importalls/mt7.nim new file mode 100644 index 000000000..45a664610 --- /dev/null +++ b/tests/importalls/mt7.nim @@ -0,0 +1,14 @@ +include ./m1 +doAssert compiles(foo1) +doAssert compiles(mt7.foo1) +doAssert declared(foo1) +doAssert declared(mt7.foo1) +doAssert declared(mt7.foo0) + +var f0: Foo5 +f0.z3 = 3 +doAssert f0.z3 == 3 +var f = initFoo5(z3=3) +doAssert f.z3 == 3 +doAssert mt7.z3(f) == 30 +doAssert z3(f) == 30 diff --git a/tests/importalls/mt8.nim b/tests/importalls/mt8.nim new file mode 100644 index 000000000..f484e7b01 --- /dev/null +++ b/tests/importalls/mt8.nim @@ -0,0 +1,23 @@ +#[ +test multiple imports +]# + +{.warning[UnusedImport]: off.} +import ./m1, m2 {.all.}, ./m3 {.all.} + # make sure this keeps using `import ./m1` without as. + +# m1 is regularly imported +doAssert declared(m1.foo0) +doAssert declared(foo0) + +doAssert not declared(m1.foo1) + # if we didn't call `createModuleAlias` even for `import f1 {.all.}`, + # this would fail, see D20201209T194412. + +# m2 +doAssert declared(m2.bar2) +doAssert declared(bar2) + +# m3 +doAssert declared(m3.m3h2) +doAssert declared(m3h2) diff --git a/tests/importalls/mt9.nim b/tests/importalls/mt9.nim new file mode 100644 index 000000000..42d48ecbd --- /dev/null +++ b/tests/importalls/mt9.nim @@ -0,0 +1,12 @@ +# tests re-export of a module with import {.all.} + +import ./m4 + +doAssert m3p1 == 2 +doAssert not declared(m3h2) +doAssert m3h3 == 3 +doAssert m3h4 == 4 + +doAssert bar1 == 2 +doAssert bar2 == 2 +doAssert not declared(bar3) diff --git a/tests/importalls/tmain2.nim b/tests/importalls/tmain2.nim new file mode 100644 index 000000000..596f0f135 --- /dev/null +++ b/tests/importalls/tmain2.nim @@ -0,0 +1,7 @@ +discard """ +joinable: false # for clarity, but not necessary +""" + +{.warning[UnusedImport]: off.} +# only import `mt*.nim` here; these depend on `m*.nim` +import "."/[mt0,mt1,mt2,mt3,mt4,mt4b,mt5,mt6,mt7,mt8,mt9] From 606288974f5a424d4c5063bc6ce9c1b5cdc0a7bc Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 16 Apr 2021 12:20:43 +0200 Subject: [PATCH 1941/3768] ic navigator tests (#17735) * IC navigator: first basic test --- .gitignore | 1 + compiler/ic/ic.nim | 3 ++- compiler/ic/navigator.nim | 2 +- testament/categories.nim | 19 ++++++++++++------- testament/testament.nim | 10 +++++----- tests/navigator/tnav1.nim | 33 +++++++++++++++++++++++++++++++++ 6 files changed, 54 insertions(+), 14 deletions(-) create mode 100644 tests/navigator/tnav1.nim diff --git a/.gitignore b/.gitignore index a22a4cd03..e9e742404 100644 --- a/.gitignore +++ b/.gitignore @@ -82,6 +82,7 @@ tweeter_test.db /tests/megatest.nim /tests/ic/*_temp.nim +/tests/navigator/*_temp.nim /outputExpected.txt diff --git a/compiler/ic/ic.nim b/compiler/ic/ic.nim index 1cd03ecfa..1f2d502ae 100644 --- a/compiler/ic/ic.nim +++ b/compiler/ic/ic.nim @@ -494,7 +494,8 @@ proc loadError(err: RodFileError; filename: AbsoluteFile; config: ConfigRef;) = of includeFileChanged: rawMessage(config, warnFileChanged, filename.string) else: - echo "Error: ", $err, " loading file: ", filename.string + rawMessage(config, warnCannotOpenFile, filename.string & " reason: " & $err) + #echo "Error: ", $err, " loading file: ", filename.string proc loadRodFile*(filename: AbsoluteFile; m: var PackedModule; config: ConfigRef; ignoreConfig = false): RodFileError = diff --git a/compiler/ic/navigator.nim b/compiler/ic/navigator.nim index ad2b29f42..b09275220 100644 --- a/compiler/ic/navigator.nim +++ b/compiler/ic/navigator.nim @@ -79,7 +79,7 @@ proc usage(c: var NavContext; info: PackedLineInfo; isDecl: bool) = file = os.extractFilename file toLocation(m, file, info.line.int, info.col.int + ColOffset) if not c.alreadyEmitted.containsOrIncl(m): - echo (if isDecl: "def" else: "usage"), c.outputSep, m + msgWriteln c.g.config, (if isDecl: "def" else: "usage") & c.outputSep & m proc list(c: var NavContext; tree: PackedTree; sym: ItemId) = for i in 0..high(tree.nodes): diff --git a/testament/categories.nim b/testament/categories.nim index 18508d70c..7b57adb8a 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -483,24 +483,27 @@ proc testNimblePackages(r: var TResults; cat: Category; packageFilter: string) = # ---------------- IC tests --------------------------------------------- -proc icTests(r: var TResults; testsDir: string, cat: Category, options: string) = +proc icTests(r: var TResults; testsDir: string, cat: Category, options: string; + isNavigatorTest: bool) = const tooltests = ["compiler/nim.nim", "tools/nimgrep.nim"] writeOnly = " --incremental:writeonly " readOnly = " --incremental:readonly " incrementalOn = " --incremental:on -d:nimIcIntegrityChecks " + navTestConfig = " --ic:on --defusages -d:nimIcNavigatorTests --hint[Conf]:off --warnings:off " template test(x: untyped) = testSpecWithNimcache(r, makeRawTest(file, x & options, cat), nimcache) template editedTest(x: untyped) = var test = makeTest(file, x & options, cat) + if isNavigatorTest: + test.spec.action = actionCompile test.spec.targets = {getTestSpecTarget()} testSpecWithNimcache(r, test, nimcache) const tempExt = "_temp.nim" - for it in walkDirRec(testsDir / "ic"): - # for it in ["tests/ic/timports.nim"]: # debugging: to try a specific test + for it in walkDirRec(testsDir): if isTestFile(it) and not it.endsWith(tempExt): let nimcache = nimcacheDir(it, options, getTestSpecTarget()) removeDir(nimcache) @@ -510,10 +513,10 @@ proc icTests(r: var TResults; testsDir: string, cat: Category, options: string) let file = it.replace(".nim", tempExt) writeFile(file, fragment) let oldPassed = r.passed - editedTest incrementalOn + editedTest(if isNavigatorTest: navTestConfig else: incrementalOn) if r.passed != oldPassed+1: break - when false: + if not isNavigatorTest and false: for file in tooltests: let nimcache = nimcacheDir(file, options, getTestSpecTarget()) removeDir(nimcache) @@ -528,7 +531,7 @@ proc icTests(r: var TResults; testsDir: string, cat: Category, options: string) # ---------------------------------------------------------------------------- -const AdditionalCategories = ["debugger", "examples", "lib", "ic"] +const AdditionalCategories = ["debugger", "examples", "lib", "ic", "navigator"] const MegaTestCat = "megatest" proc `&.?`(a, b: string): string = @@ -695,7 +698,9 @@ proc processCategory(r: var TResults, cat: Category, of "niminaction": testNimInAction(r, cat, options) of "ic": - icTests(r, testsDir, cat, options) + icTests(r, testsDir / cat2, cat, options, isNavigatorTest=false) + of "navigator": + icTests(r, testsDir / cat2, cat, options, isNavigatorTest=true) of "untestable": # These require special treatment e.g. because they depend on a third party # dependency; see `trunner_special` which runs some of those. diff --git a/testament/testament.nim b/testament/testament.nim index ee7f67484..b9e8f3b95 100644 --- a/testament/testament.nim +++ b/testament/testament.nim @@ -157,8 +157,8 @@ proc prepareTestArgs(cmdTemplate, filename, options, nimcache: string, "options", options, "file", filename.quoteShell, "filedir", filename.getFileDir(), "nim", compilerPrefix]) -proc callCompiler(cmdTemplate, filename, options, nimcache: string, - target: TTarget, extraOptions = ""): TSpec = +proc callNimCompiler(cmdTemplate, filename, options, nimcache: string, + target: TTarget, extraOptions = ""): TSpec = let c = prepareTestArgs(cmdTemplate, filename, options, nimcache, target, extraOptions) result.cmd = quoteShellCommand(c) @@ -465,11 +465,11 @@ proc testSpecHelper(r: var TResults, test: var TTest, expected: TSpec, test.startTime = epochTime() case expected.action of actionCompile: - var given = callCompiler(expected.getCmd, test.name, test.options, nimcache, target, + var given = callNimCompiler(expected.getCmd, test.name, test.options, nimcache, target, extraOptions = " --stdout --hint[Path]:off --hint[Processing]:off") compilerOutputTests(test, target, given, expected, r) of actionRun: - var given = callCompiler(expected.getCmd, test.name, test.options, + var given = callNimCompiler(expected.getCmd, test.name, test.options, nimcache, target, extraOptions) if given.err != reSuccess: r.addResult(test, target, "", "$ " & given.cmd & '\n' & given.nimout, given.err) @@ -523,7 +523,7 @@ proc testSpecHelper(r: var TResults, test: var TTest, expected: TSpec, else: compilerOutputTests(test, target, given, expected, r) of actionReject: - var given = callCompiler(expected.getCmd, test.name, test.options, + var given = callNimCompiler(expected.getCmd, test.name, test.options, nimcache, target) cmpMsgs(r, expected, given, test, target) diff --git a/tests/navigator/tnav1.nim b/tests/navigator/tnav1.nim new file mode 100644 index 000000000..d7c6f63e2 --- /dev/null +++ b/tests/navigator/tnav1.nim @@ -0,0 +1,33 @@ +discard """ + cmd: "nim check $options --track:$file,12,7 $file" + nimout: '''def tnav1_temp.nim(11, 10) +usage tnav1_temp.nim(12, 8) + ''' +""" + + +import std / [times] + +proc foo(x: int) = + echo x + +foo(3) +echo "yes", 1 != 3 + +#!EDIT!# +discard """ + cmd: "nim check $options --track:$file,15,2 $file" + nimout: '''def tnav1_temp.nim(12, 6) +usage tnav1_temp.nim(15, 1) + ''' +""" + + +import std / [times] + +proc foo(x: int) = + echo x + +foo(3) +echo "yes", 1 != 3 + From 611b88763f8ec88889b14da31ff220cb47789846 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 16 Apr 2021 03:54:49 -0700 Subject: [PATCH 1942/3768] start using import {.all.} (#17736) --- lib/pure/uri.nim | 22 ---------------------- tests/stdlib/turi.nim | 19 +++++++++++++++++++ 2 files changed, 19 insertions(+), 22 deletions(-) diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim index 4ac04d02e..d2a9f3848 100644 --- a/lib/pure/uri.nim +++ b/lib/pure/uri.nim @@ -555,25 +555,3 @@ proc getDataUri*(data, mime: string, encoding = "utf-8"): string {.since: (1, 3) runnableExamples: static: doAssert getDataUri("Nim", "text/plain") == "data:text/plain;charset=utf-8;base64,Tmlt" assert encoding.len > 0 and mime.len > 0 # Must *not* be URL-Safe, see RFC-2397 result = "data:" & mime & ";charset=" & encoding & ";base64," & base64.encode(data) - -when isMainModule and defined(testing): - # needed (pending https://github.com/nim-lang/Nim/pull/11865) because - # `removeDotSegments` is private, the other tests are in `turi`. - block: # removeDotSegments - # `removeDotSegments` is exported for -d:testing only - doAssert removeDotSegments("/foo/bar/baz") == "/foo/bar/baz" - doAssert removeDotSegments("") == "" # empty test - doAssert removeDotSegments(".") == "." # trailing period - doAssert removeDotSegments("a1/a2/../a3/a4/a5/./a6/a7/././") == "a1/a3/a4/a5/a6/a7/" - doAssert removeDotSegments("https://a1/a2/../a3/a4/a5/./a6/a7/././") == "https://a1/a3/a4/a5/a6/a7/" - doAssert removeDotSegments("http://a1/a2") == "http://a1/a2" - doAssert removeDotSegments("http://www.ai.") == "http://www.ai." - when false: # xxx these cases are buggy - # this should work, refs https://webmasters.stackexchange.com/questions/73934/how-can-urls-have-a-dot-at-the-end-e-g-www-bla-de - doAssert removeDotSegments("http://www.ai./") == "http://www.ai./" # fails - echo removeDotSegments("http://www.ai./") # http://www.ai/ - echo removeDotSegments("a/b.../c") # b.c - echo removeDotSegments("a/b../c") # bc - echo removeDotSegments("a/.../c") # .c - echo removeDotSegments("a//../b") # a/b - echo removeDotSegments("a/b/c//") # a/b/c// diff --git a/tests/stdlib/turi.nim b/tests/stdlib/turi.nim index d1a30bbfc..a3b6afe2c 100644 --- a/tests/stdlib/turi.nim +++ b/tests/stdlib/turi.nim @@ -3,6 +3,7 @@ discard """ """ import std/uri +from std/uri {.all.} as uri2 import removeDotSegments from std/sequtils import toSeq template main() = @@ -14,6 +15,24 @@ template main() = doAssert decodeUrl(encodeUrl(test1, false), false) == test1 doAssert decodeUrl(encodeUrl(test1)) == test1 + block: # removeDotSegments + doAssert removeDotSegments("/foo/bar/baz") == "/foo/bar/baz" + doAssert removeDotSegments("") == "" # empty test + doAssert removeDotSegments(".") == "." # trailing period + doAssert removeDotSegments("a1/a2/../a3/a4/a5/./a6/a7/././") == "a1/a3/a4/a5/a6/a7/" + doAssert removeDotSegments("https://a1/a2/../a3/a4/a5/./a6/a7/././") == "https://a1/a3/a4/a5/a6/a7/" + doAssert removeDotSegments("http://a1/a2") == "http://a1/a2" + doAssert removeDotSegments("http://www.ai.") == "http://www.ai." + when false: # xxx these cases are buggy + # this should work, refs https://webmasters.stackexchange.com/questions/73934/how-can-urls-have-a-dot-at-the-end-e-g-www-bla-de + doAssert removeDotSegments("http://www.ai./") == "http://www.ai./" # fails + echo removeDotSegments("http://www.ai./") # http://www.ai/ + echo removeDotSegments("a/b.../c") # b.c + echo removeDotSegments("a/b../c") # bc + echo removeDotSegments("a/.../c") # .c + echo removeDotSegments("a//../b") # a/b + echo removeDotSegments("a/b/c//") # a/b/c// + block: # parseUri block: let org = "udp://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:8080" From d19e4310dc16cae2329c55dfa8feb94e0981dc0c Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 16 Apr 2021 05:21:26 -0700 Subject: [PATCH 1943/3768] std/hashes: hash(ref|ptr|pointer) + other improvements (#17731) --- lib/pure/hashes.nim | 86 +++++++++++++++++++---------- testament/lib/stdtest/testutils.nim | 4 ++ tests/collections/ttables.nim | 14 +++++ tests/stdlib/thashes.nim | 22 +++++++- tests/stdlib/tstrutils.nim | 6 +- 5 files changed, 96 insertions(+), 36 deletions(-) diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim index 3339adba2..2ef0e2454 100644 --- a/lib/pure/hashes.nim +++ b/lib/pure/hashes.nim @@ -182,7 +182,7 @@ proc hashData*(data: pointer, size: int): Hash = var h: Hash = 0 when defined(js): var p: cstring - asm """`p` = `Data`;""" + asm """`p` = `Data`""" else: var p = cast[cstring](data) var i = 0 @@ -193,32 +193,6 @@ proc hashData*(data: pointer, size: int): Hash = dec(s) result = !$h -when defined(js): - var objectID = 0 - -proc hash*(x: pointer): Hash {.inline.} = - ## Efficient hashing of pointers. - when defined(js): - asm """ - if (typeof `x` == "object") { - if ("_NimID" in `x`) - `result` = `x`["_NimID"]; - else { - `result` = ++`objectID`; - `x`["_NimID"] = `result`; - } - } - """ - else: - result = cast[Hash](cast[uint](x) shr 3) # skip the alignment - -proc hash*[T: proc](x: T): Hash {.inline.} = - ## Efficient hashing of proc vars. Closures are supported too. - when T is "closure": - result = hash(rawProc(x)) !& hash(rawEnv(x)) - else: - result = hash(pointer(x)) - proc hashIdentity*[T: Ordinal|enum](x: T): Hash {.inline, since: (1, 3).} = ## The identity hash, i.e. `hashIdentity(x) = x`. cast[Hash](ord(x)) @@ -232,6 +206,59 @@ else: ## Efficient hashing of integers. hashWangYi1(uint64(ord(x))) +when defined(js): + var objectID = 0 + proc getObjectId(x: pointer): int = + asm """ + if (typeof `x` == "object") { + if ("_NimID" in `x`) + `result` = `x`["_NimID"]; + else { + `result` = ++`objectID`; + `x`["_NimID"] = `result`; + } + } + """ + +proc hash*(x: pointer): Hash {.inline.} = + ## Efficient `hash` overload. + when defined(js): + let y = getObjectId(x) + else: + let y = cast[int](x) + hash(y) # consistent with code expecting scrambled hashes depending on `nimIntHash1`. + +proc hash*[T](x: ref[T] | ptr[T]): Hash {.inline.} = + ## Efficient `hash` overload. + runnableExamples: + var a: array[10, uint8] + assert a[0].addr.hash != a[1].addr.hash + assert cast[pointer](a[0].addr).hash == a[0].addr.hash + runnableExamples: + type A = ref object + x: int + let a = A(x: 3) + let ha = a.hash + assert ha != A(x: 3).hash # A(x: 3) is a different ref object from `a`. + a.x = 4 + assert ha == a.hash # the hash only depends on the address + runnableExamples: + # you can overload `hash` if you want to customize semantics + type A[T] = ref object + x, y: T + proc hash(a: A): Hash = hash(a.x) + assert A[int](x: 3, y: 4).hash == A[int](x: 3, y: 5).hash + # xxx pending bug #17733, merge as `proc hash*(pointer | ref | ptr): Hash` + # or `proc hash*[T: ref | ptr](x: T): Hash` + hash(cast[pointer](x)) + +proc hash*[T: proc](x: T): Hash {.inline.} = + ## Efficient hashing of proc vars. Closures are supported too. + when T is "closure": + result = hash((rawProc(x), rawEnv(x))) + else: + result = hash(pointer(x)) + proc hash*(x: float): Hash {.inline.} = ## Efficient hashing of floats. let y = x + 0.0 # for denormalization @@ -484,10 +511,9 @@ proc hashIgnoreCase*(sBuf: string, sPos, ePos: int): Hash = h = h !& ord(c) result = !$h - proc hash*[T: tuple | object](x: T): Hash = - ## Efficient hashing of tuples and objects. - ## There must be a `hash` proc defined for each of the field types. + ## Efficient `hash` overload. + ## `hash` must be defined for each component of `x`. runnableExamples: type Obj = object x: int diff --git a/testament/lib/stdtest/testutils.nim b/testament/lib/stdtest/testutils.nim index abffff24c..35423de17 100644 --- a/testament/lib/stdtest/testutils.nim +++ b/testament/lib/stdtest/testutils.nim @@ -85,3 +85,7 @@ template accept*(a) = template reject*(a) = doAssert not compiles(a) + +template disableVm*(body) = + when nimvm: discard + else: body diff --git a/tests/collections/ttables.nim b/tests/collections/ttables.nim index 61197e9f0..c2864b75f 100644 --- a/tests/collections/ttables.nim +++ b/tests/collections/ttables.nim @@ -7,7 +7,11 @@ And we get here 3 ''' joinable: false +targets: "c cpp js" """ + +# xxx wrap in a template to test in VM, see https://github.com/timotheecour/Nim/issues/534#issuecomment-769565033 + import hashes, sequtils, tables, algorithm proc sortedPairs[T](t: T): auto = toSeq(t.pairs).sorted @@ -444,3 +448,13 @@ block emptyOrdered: var t2: OrderedTable[int, string] doAssert t1 == t2 +block: # Table[ref, int] + type A = ref object + x: int + var t: OrderedTable[A, int] + let a1 = A(x: 3) + let a2 = A(x: 3) + t[a1] = 10 + t[a2] = 11 + doAssert t[a1] == 10 + doAssert t[a2] == 11 diff --git a/tests/stdlib/thashes.nim b/tests/stdlib/thashes.nim index 044259f88..66857d3ca 100644 --- a/tests/stdlib/thashes.nim +++ b/tests/stdlib/thashes.nim @@ -3,7 +3,7 @@ discard """ """ import std/hashes - +from stdtest/testutils import disableVm, whenVMorJs when not defined(js) and not defined(cpp): block: @@ -177,5 +177,25 @@ proc main() = doAssert hash(Obj5(t: false, x: 1)) == hash(Obj5(t: true, y: 1)) doAssert hash(Obj5(t: false, x: 1)) != hash(Obj5(t: true, y: 2)) + block: # hash(ref|ptr|pointer) + var a: array[10, uint8] + # disableVm: + whenVMorJs: + # pending fix proposed in https://github.com/nim-lang/Nim/issues/15952#issuecomment-786312417 + discard + do: + assert a[0].addr.hash != a[1].addr.hash + assert cast[pointer](a[0].addr).hash == a[0].addr.hash + + block: # hash(ref) + type A = ref object + x: int + let a = A(x: 3) + disableVm: # xxx Error: VM does not support 'cast' from tyRef to tyPointer + let ha = a.hash + assert ha != A(x: 3).hash # A(x: 3) is a different ref object from `a`. + a.x = 4 + assert ha == a.hash # the hash only depends on the address + static: main() main() diff --git a/tests/stdlib/tstrutils.nim b/tests/stdlib/tstrutils.nim index a6248d1e3..771dddcaf 100644 --- a/tests/stdlib/tstrutils.nim +++ b/tests/stdlib/tstrutils.nim @@ -3,7 +3,7 @@ discard """ """ import std/strutils - +from stdtest/testutils import disableVm # xxx each instance of `disableVm` and `when not defined js:` should eventually be fixed template rejectParse(e) = @@ -12,10 +12,6 @@ template rejectParse(e) = raise newException(AssertionDefect, "This was supposed to fail: $#!" % astToStr(e)) except ValueError: discard -template disableVm(body) = - when nimvm: discard - else: body - template main() = block: # strip doAssert strip(" ha ") == "ha" From fdd4391534578d6a5a655eef99ef96e53ff2b4f1 Mon Sep 17 00:00:00 2001 From: shirleyquirk <31934565+shirleyquirk@users.noreply.github.com> Date: Fri, 16 Apr 2021 13:22:51 +0100 Subject: [PATCH 1944/3768] Fix buffer-overrun bug in net (#17728) [backport:1.0] --- changelog.md | 2 ++ lib/pure/net.nim | 7 +++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/changelog.md b/changelog.md index 6dcfab9c6..cfdcd3c62 100644 --- a/changelog.md +++ b/changelog.md @@ -40,6 +40,8 @@ ## Standard library additions and changes - Added support for parenthesized expressions in `strformat` +- Fixed buffer overflow bugs in `net` + - Added `sections` iterator in `parsecfg`. - Make custom op in macros.quote work for all statements. diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 9be9c6acb..343cdc9b1 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -690,12 +690,11 @@ when defineSsl: let ctx = SslContext(context: ssl.SSL_get_SSL_CTX) let hintString = if hint == nil: "" else: $hint let (identityString, pskString) = (ctx.clientGetPskFunc)(hintString) - if psk.len.cuint > max_psk_len: + if pskString.len.cuint > max_psk_len: return 0 if identityString.len.cuint >= max_identity_len: return 0 - - copyMem(identity, identityString.cstring, pskString.len + 1) # with the last zero byte + copyMem(identity, identityString.cstring, identityString.len + 1) # with the last zero byte copyMem(psk, pskString.cstring, pskString.len) return pskString.len.cuint @@ -716,7 +715,7 @@ when defineSsl: max_psk_len: cint): cuint {.cdecl.} = let ctx = SslContext(context: ssl.SSL_get_SSL_CTX) let pskString = (ctx.serverGetPskFunc)($identity) - if psk.len.cint > max_psk_len: + if pskString.len.cint > max_psk_len: return 0 copyMem(psk, pskString.cstring, pskString.len) From 1b65b9cc193791285f63fcf9d52f19da72fffd4f Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 16 Apr 2021 05:55:51 -0700 Subject: [PATCH 1945/3768] refs #17292 fix `repr`: `(discard)` now does't render as `discard` which gave illegal code (#17455) * refs #17292 fix `repr` with (discard) * add tests * add more tests --- compiler/renderer.nim | 8 ++++++- tests/stdlib/trepr.nim | 50 ++++++++++++++++++++++++++++++++---------- 2 files changed, 45 insertions(+), 13 deletions(-) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index b3b0adc01..4c0de9ed5 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -1461,7 +1461,13 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = put(g, tkSpaces, Space) putWithSpace(g, tkEquals, "=") gsub(g, n, 1) - of nkStmtList, nkStmtListExpr, nkStmtListType: gstmts(g, n, emptyContext) + of nkStmtList, nkStmtListExpr, nkStmtListType: + if n.len == 1 and n[0].kind == nkDiscardStmt: + put(g, tkParLe, "(") + gsub(g, n[0]) + put(g, tkParRi, ")") + else: + gstmts(g, n, emptyContext) of nkIfStmt: putWithSpace(g, tkIf, "if") gif(g, n) diff --git a/tests/stdlib/trepr.nim b/tests/stdlib/trepr.nim index f72bbb34e..86f3dee30 100644 --- a/tests/stdlib/trepr.nim +++ b/tests/stdlib/trepr.nim @@ -5,9 +5,10 @@ discard """ # if excessive, could remove 'cpp' from targets -from strutils import endsWith, contains +from strutils import endsWith, contains, strip from std/macros import newLit -macro deb(a): string = newLit a.repr + +macro deb(a): string = newLit a.repr.strip template main() = doAssert repr({3,5}) == "{3, 5}" @@ -67,17 +68,16 @@ template main() = else: doAssert reprOpenarray(arr) == "[1, 2, 3]" - block: # bug #17292 + block: # bug #17292 repr with `do` template foo(a, b, c, d) = discard block: let a = deb: foo(1, 2, 3, 4) - doAssert a == "\nfoo(1, 2, 3, 4)" + doAssert a == "foo(1, 2, 3, 4)" block: let a = deb: foo(1, 2, 3): 4 doAssert a == """ - foo(1, 2, 3): 4""" @@ -86,7 +86,6 @@ foo(1, 2, 3): foo(1, 2): 3 do: 4 doAssert a == """ - foo(1, 2): 3 do: @@ -98,7 +97,6 @@ do: do: 3 do: 4 doAssert a == """ - foo(1): 3 do: @@ -118,7 +116,6 @@ do: 4 doAssert a == """ - foo(1): 3 do: @@ -135,7 +132,6 @@ do: do: 3 do: 4 doAssert a == """ - foo: 1 do: @@ -145,13 +141,44 @@ do: do: 4""" + block: # bug #17292 repr with `(discard)` (`discard` would result in illegal code) + let a = deb: + let f {.inject.} = () => (discard) + doAssert a == """ +let f {.inject.} = () => + (discard )""" + + let a2 = deb: + block: + discard + discard + + block: + when true: discard + + # let a = b => discard # illegal + discard b => (discard) # legal + + block: + return + doAssert a2 == """ +block: + discard +discard +block: + when true: + discard +discard b => + (discard ) +block: + return""" + block: # bug #17292 (bug 4) let a = deb: proc `=destroy`() = discard proc `'foo`(): int = discard proc `foo bar baz`(): int = discard let a2 = """ - proc `=destroy`() = discard @@ -159,8 +186,7 @@ proc `'foo`(): int = discard proc `foo bar baz`(): int = - discard -""" + discard""" doAssert a2 == a block: # setters: `foo=` From 957478ce264d0496f9a0c33de4af77ad0846b42d Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 16 Apr 2021 15:23:17 +0200 Subject: [PATCH 1946/3768] removed nimfind tool, succeeded by 'nim check --defusages' (#17737) --- koch.nim | 2 - tools/nimfind.nim | 234 --------------------------------------------- tools/nimfind.nims | 2 - 3 files changed, 238 deletions(-) delete mode 100644 tools/nimfind.nim delete mode 100644 tools/nimfind.nims diff --git a/koch.nim b/koch.nim index 44d38589b..3e41310d7 100644 --- a/koch.nim +++ b/koch.nim @@ -216,8 +216,6 @@ proc buildTools(args: string = "") = options = "-d:release " & args) when defined(windows): buildVccTool(args) bundleNimpretty(args) - nimCompileFold("Compile nimfind", "tools/nimfind.nim", - options = "-d:release " & args) nimCompileFold("Compile testament", "testament/testament.nim", options = "-d:release " & args) diff --git a/tools/nimfind.nim b/tools/nimfind.nim deleted file mode 100644 index 9c8247606..000000000 --- a/tools/nimfind.nim +++ /dev/null @@ -1,234 +0,0 @@ -# -# -# The Nim Compiler -# (c) Copyright 2018 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## Nimfind is a tool that helps to give editors IDE like capabilities. - -when not defined(nimcore): - {.error: "nimcore MUST be defined for Nim's core tooling".} -when not defined(nimfind): - {.error: "nimfind MUST be defined for Nim's nimfind tool".} - -const Usage = """ -Nimfind - Tool to find declarations or usages for Nim symbols -Usage: - nimfind [options] file.nim:line:col - -Options: - --help, -h show this help - --rebuild rebuild the index - --project:file.nim use file.nim as the entry point - -In addition, all command line options of Nim that do not affect code generation -are supported. -""" - -import strutils, os, parseopt - -import "../compiler" / [options, commands, modules, sem, - passes, passaux, msgs, ast, - idents, modulegraphs, lineinfos, cmdlinehelper, - pathutils] - -import db_sqlite - -proc createDb(db: DbConn) = - db.exec(sql""" - create table if not exists filenames( - id integer primary key, - fullpath varchar(8000) not null - ); - """) - db.exec sql"create index if not exists FilenameIx on filenames(fullpath);" - - # every sym can have potentially 2 different definitions due to forward - # declarations. - db.exec(sql""" - create table if not exists syms( - id integer primary key, - nimid integer not null, - name varchar(256) not null, - defline integer not null, - defcol integer not null, - deffile integer not null, - deflineB integer not null default 0, - defcolB integer not null default 0, - deffileB integer not null default 0, - foreign key (deffile) references filenames(id), - foreign key (deffileB) references filenames(id) - ); - """) - - db.exec(sql""" - create table if not exists usages( - id integer primary key, - nimid integer not null, - line integer not null, - col integer not null, - colB integer not null, - file integer not null, - foreign key (file) references filenames(id), - foreign key (nimid) references syms(nimid) - ); - """) - db.exec sql"create index if not exists UsagesIx on usages(file, line);" - -proc toDbFileId*(db: DbConn; conf: ConfigRef; fileIdx: FileIndex): int = - if fileIdx == FileIndex(-1): return -1 - let fullpath = toFullPath(conf, fileIdx) - let row = db.getRow(sql"select id from filenames where fullpath = ?", fullpath) - let id = row[0] - if id.len == 0: - result = int db.insertID(sql"insert into filenames(fullpath) values (?)", - fullpath) - else: - result = parseInt(id) - -type - FinderRef = ref object of RootObj - db: DbConn - -proc writeDef(graph: ModuleGraph; s: PSym; info: TLineInfo) = - let f = FinderRef(graph.backend) - f.db.exec(sql"""insert into syms(nimid, name, defline, defcol, deffile) values (?, ?, ?, ?, ?)""", - s.id, s.name.s, info.line, info.col, - toDbFileId(f.db, graph.config, info.fileIndex)) - -proc writeDefResolveForward(graph: ModuleGraph; s: PSym; info: TLineInfo) = - let f = FinderRef(graph.backend) - f.db.exec(sql"""update syms set deflineB = ?, defcolB = ?, deffileB = ? - where nimid = ?""", info.line, info.col, - toDbFileId(f.db, graph.config, info.fileIndex), s.id) - -proc writeUsage(graph: ModuleGraph; s: PSym; info: TLineInfo) = - let f = FinderRef(graph.backend) - f.db.exec(sql"""insert into usages(nimid, line, col, colB, file) values (?, ?, ?, ?, ?)""", - s.id, info.line, info.col, info.col + s.name.s.len - 1, - toDbFileId(f.db, graph.config, info.fileIndex)) - -proc performSearch(conf: ConfigRef; dbfile: AbsoluteFile) = - var db = open(connection=string dbfile, user="nim", password="", - database="nim") - let pos = conf.m.trackPos - let fid = toDbFileId(db, conf, pos.fileIndex) - let known = toFullPath(conf, pos.fileIndex) - let nimids = db.getRow(sql"""select distinct nimid from usages where line = ? and file = ? and ? between col and colB""", - pos.line, fid, pos.col) - if nimids.len > 0: - var idSet = "" - for id in nimids: - if idSet.len > 0: idSet.add ", " - idSet.add id - var outputLater = "" - for r in db.rows(sql"""select line, col, filenames.fullpath from usages - inner join filenames on filenames.id = file - where nimid in (?)""", idSet): - let line = parseInt(r[0]) - let col = parseInt(r[1]) - let file = r[2] - if file == known and line == pos.line.int: - # output the line we already know last: - outputLater.add file & ":" & $line & ":" & $(col+1) & "\n" - else: - echo file, ":", line, ":", col+1 - if outputLater.len > 0: stdout.write outputLater - close(db) - -proc setupDb(g: ModuleGraph; dbfile: AbsoluteFile) = - var f = FinderRef() - removeFile(dbfile) - f.db = open(connection=string dbfile, user="nim", password="", - database="nim") - createDb(f.db) - f.db.exec(sql"pragma journal_mode=off") - # This MUST be turned off, otherwise it's way too slow even for testing purposes: - f.db.exec(sql"pragma SYNCHRONOUS=off") - f.db.exec(sql"pragma LOCKING_MODE=exclusive") - g.backend = f - -proc mainCommand(graph: ModuleGraph) = - let conf = graph.config - let dbfile = getNimcacheDir(conf) / RelativeFile"nimfind.db" - if not fileExists(dbfile) or optForceFullMake in conf.globalOptions: - clearPasses(graph) - registerPass graph, verbosePass - registerPass graph, semPass - conf.setCmd cmdIdeTools - wantMainModule(conf) - setupDb(graph, dbfile) - - graph.onDefinition = writeUsage # writeDef - graph.onDefinitionResolveForward = writeUsage # writeDefResolveForward - graph.onUsage = writeUsage - - if not fileExists(conf.projectFull): - quit "cannot find file: " & conf.projectFull.string - add(conf.searchPaths, conf.libpath) - conf.setErrorMaxHighMaybe - try: - compileProject(graph) - finally: - close(FinderRef(graph.backend).db) - performSearch(conf, dbfile) - -proc processCmdLine*(pass: TCmdLinePass, cmd: string; conf: ConfigRef) = - var p = parseopt.initOptParser(cmd) - while true: - parseopt.next(p) - case p.kind - of cmdEnd: break - of cmdLongOption, cmdShortOption: - case p.key.normalize - of "help", "h": - stdout.writeLine(Usage) - quit() - of "project": - conf.projectName = p.val - of "rebuild": - incl conf.globalOptions, optForceFullMake - else: processSwitch(pass, p, conf) - of cmdArgument: - let info = p.key.split(':') - if info.len == 3: - conf.projectName = findProjectNimFile(conf, info[0].splitFile.dir) - if conf.projectName.len == 0: conf.projectName = info[0] - try: - conf.m.trackPos = newLineInfo(conf, AbsoluteFile info[0], - parseInt(info[1]), parseInt(info[2])-1) - except ValueError: - quit "invalid command line" - else: - quit "invalid command line" - -proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = - let self = NimProg( - suggestMode: true, - processCmdLine: processCmdLine - ) - self.initDefinesProg(conf, "nimfind") - - if paramCount() == 0: - stdout.writeLine(Usage) - return - - self.processCmdLineAndProjectPath(conf) - - # Find Nim's prefix dir. - let binaryPath = findExe("nim") - if binaryPath == "": - raise newException(IOError, - "Cannot find Nim standard library: Nim compiler not in PATH") - conf.prefixDir = AbsoluteDir binaryPath.splitPath().head.parentDir() - if not dirExists(conf.prefixDir / RelativeDir"lib"): - conf.prefixDir = AbsoluteDir"" - - var graph = newModuleGraph(cache, conf) - if self.loadConfigsAndProcessCmdLine(cache, conf, graph): - mainCommand(graph) - -handleCmdLine(newIdentCache(), newConfigRef()) diff --git a/tools/nimfind.nims b/tools/nimfind.nims deleted file mode 100644 index 3013c360d..000000000 --- a/tools/nimfind.nims +++ /dev/null @@ -1,2 +0,0 @@ ---define:nimfind ---define:nimcore From 201ac2b9c93a8c3677b4910a1986c91741aee0e3 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 16 Apr 2021 11:26:05 -0700 Subject: [PATCH 1947/3768] fix nim CI following #17455 (same postmortem as #17721) (#17740) --- tests/stdlib/trepr.nim | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/stdlib/trepr.nim b/tests/stdlib/trepr.nim index 86f3dee30..356764903 100644 --- a/tests/stdlib/trepr.nim +++ b/tests/stdlib/trepr.nim @@ -193,10 +193,8 @@ proc `foo bar baz`(): int = let a = deb: proc `foo=`() = discard doAssert a == """ - proc `foo=`() = - discard -""" + discard""" block: # bug #14850 block: @@ -221,7 +219,6 @@ proc `foo=`() = 4) doAssert a == """ - template bar(): untyped = foo1: discard @@ -244,8 +241,7 @@ template bar(): untyped = 4 a.add(foo7 do: echo "baz" - 4) -""" + 4)""" static: main() main() From 8e474fbb57c2f7b58a840b5b30230c2267633c8e Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 17 Apr 2021 01:12:12 +0200 Subject: [PATCH 1948/3768] IC: yet another embarrassing omission (#17743) * IC: yet another embarrassing omission * VM: fewer hacks that kept IC from working --- compiler/ast.nim | 2 +- compiler/ic/ic.nim | 3 ++- compiler/vmdef.nim | 3 +++ compiler/vmgen.nim | 14 ++++++-------- 4 files changed, 12 insertions(+), 10 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 4c835871f..bf0c9669a 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1054,7 +1054,7 @@ const defaultSize = -1 defaultAlignment = -1 - defaultOffset = -1 + defaultOffset* = -1 proc getPIdent*(a: PNode): PIdent {.inline.} = ## Returns underlying `PIdent` for `{nkSym, nkIdent}`, or `nil`. diff --git a/compiler/ic/ic.nim b/compiler/ic/ic.nim index 1f2d502ae..da0af2edf 100644 --- a/compiler/ic/ic.nim +++ b/compiler/ic/ic.nim @@ -765,7 +765,8 @@ proc symHeaderFromPacked(c: var PackedDecoder; g: var PackedModuleGraph; kind: s.kind, magic: s.magic, flags: s.flags, info: translateLineInfo(c, g, si, s.info), options: s.options, - position: s.position, + position: if s.kind in {skForVar, skVar, skLet, skTemp}: 0 else: s.position, + offset: if s.kind in routineKinds: defaultOffset else: s.offset, name: getIdent(c.cache, g[si].fromDisk.sh.strings[s.name]) ) diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index e8b5cdda9..068ade4b0 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -10,6 +10,8 @@ ## This module contains the type definitions for the new evaluation engine. ## An instruction is 1-3 int32s in memory, it is a register based VM. +import std / tables + import ast, idents, options, modulegraphs, lineinfos type TInstrType* = uint64 @@ -266,6 +268,7 @@ type profiler*: Profiler templInstCounter*: ref int # gives every template instantiation a unique ID, needed here for getAst vmstateDiff*: seq[(PSym, PNode)] # we remember the "diff" to global state here (feature for IC) + procToCodePos*: Table[int, int] PStackFrame* = ref TStackFrame TStackFrame* = object diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 69d7ec4b3..951d47d2f 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -27,6 +27,8 @@ # solves the opcLdConst vs opcAsgnConst issue. Of course whether we need # this copy depends on the involved types. +import std / tables + import strutils, ast, types, msgs, renderer, vmdef, intsets, magicsys, options, lowerings, lineinfos, transf @@ -2248,8 +2250,8 @@ proc optimizeJumps(c: PCtx; start: int) = else: discard proc genProc(c: PCtx; s: PSym): int = - var x = s.ast[miscPos] - if x.kind == nkEmpty or x[0].kind == nkEmpty: + let pos = c.procToCodePos.getOrDefault(s.id) + if pos == 0: #if s.name.s == "outterMacro" or s.name.s == "innerProc": # echo "GENERATING CODE FOR ", s.name.s let last = c.code.len-1 @@ -2260,11 +2262,7 @@ proc genProc(c: PCtx; s: PSym): int = c.debug.setLen(last) #c.removeLastEof result = c.code.len+1 # skip the jump instruction - if x.kind == nkEmpty: - x = newTree(nkBracket, newIntNode(nkIntLit, result), x) - else: - x[0] = newIntNode(nkIntLit, result) - s.ast[miscPos] = x + c.procToCodePos[s.id] = result # thanks to the jmp we can add top level statements easily and also nest # procs easily: let body = transformBody(c.graph, c.idgen, s, cache = not isCompileTimeProc(s)) @@ -2297,4 +2295,4 @@ proc genProc(c: PCtx; s: PSym): int = c.prc = oldPrc else: c.prc.maxSlots = s.offset - result = x[0].intVal.int + result = pos From 7e94420847d2d18347410099e97195ddebe8b85f Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 17 Apr 2021 02:14:09 -0700 Subject: [PATCH 1949/3768] cString => cSourceString; tyCString => tyCstring so that error msgs show cstring, not cString (#17744) --- compiler/ast.nim | 6 +++--- compiler/ccgcalls.nim | 2 +- compiler/ccgexprs.nim | 14 +++++++------- compiler/ccgreset.nim | 2 +- compiler/ccgtypes.nim | 6 +++--- compiler/concepts.nim | 4 ++-- compiler/evalffi.nim | 8 ++++---- compiler/jsgen.nim | 20 ++++++++++---------- compiler/jstypes.nim | 2 +- compiler/liftdestructors.nim | 2 +- compiler/magicsys.nim | 2 +- compiler/nilcheck.nim | 2 +- compiler/semexprs.nim | 4 ++-- compiler/semfold.nim | 4 ++-- compiler/semmacrosanity.nim | 2 +- compiler/sempass2.nim | 2 +- compiler/semtypes.nim | 6 +++--- compiler/sigmatch.nim | 10 +++++----- compiler/sizealignoffsetimpl.nim | 2 +- compiler/sourcemap.nim | 10 +++++----- compiler/transf.nim | 4 ++-- compiler/typeallowed.nim | 2 +- compiler/types.nim | 6 +++--- compiler/vm.nim | 2 +- compiler/vmdeps.nim | 2 +- compiler/vmgen.nim | 12 ++++++------ compiler/vmmarshal.nim | 4 ++-- lib/core/typeinfo.nim | 8 ++++---- lib/system/assign.nim | 2 +- lib/system/hti.nim | 2 +- lib/system/repr.nim | 2 +- lib/system/reprjs.nim | 2 +- 32 files changed, 79 insertions(+), 79 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index bf0c9669a..02a2ae7a4 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -379,7 +379,7 @@ type tySequence, tyProc, tyPointer, tyOpenArray, - tyString, tyCString, tyForward, + tyString, tyCstring, tyForward, tyInt, tyInt8, tyInt16, tyInt32, tyInt64, # signed integers tyFloat, tyFloat32, tyFloat64, tyFloat128, tyUInt, tyUInt8, tyUInt16, tyUInt32, tyUInt64, @@ -1005,13 +1005,13 @@ const tyBool, tyChar, tyEnum, tyArray, tyObject, tySet, tyTuple, tyRange, tyPtr, tyRef, tyVar, tyLent, tySequence, tyProc, tyPointer, - tyOpenArray, tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128, + tyOpenArray, tyString, tyCstring, tyInt..tyInt64, tyFloat..tyFloat128, tyUInt..tyUInt64} IntegralTypes* = {tyBool, tyChar, tyEnum, tyInt..tyInt64, tyFloat..tyFloat128, tyUInt..tyUInt64} # weird name because it contains tyFloat ConstantDataTypes*: TTypeKinds = {tyArray, tySet, tyTuple, tySequence} - NilableTypes*: TTypeKinds = {tyPointer, tyCString, tyRef, tyPtr, + NilableTypes*: TTypeKinds = {tyPointer, tyCstring, tyRef, tyPtr, tyProc, tyError} # TODO PtrLikeKinds*: TTypeKinds = {tyPointer, tyPtr} # for VM PersistentNodeFlags*: TNodeFlags = {nfBase2, nfBase8, nfBase16, diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 2a93cb5af..616028ac6 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -178,7 +178,7 @@ proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType): (Rope, else: result = ("($3*)($1)+($2)" % [rdLoc(a), rdLoc(b), dest], lengthExpr) - of tyUncheckedArray, tyCString: + of tyUncheckedArray, tyCstring: result = ("($3*)($1)+($2)" % [rdLoc(a), rdLoc(b), dest], lengthExpr) of tyString, tySequence: diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 3872ed360..0e54adf49 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -399,7 +399,7 @@ proc genAssignment(p: BProc, dest, src: TLoc, flags: TAssignmentFlags) = [rdLoc(dest), rdLoc(src), getSize(p.config, dest.t)]) else: linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)]) - of tyPtr, tyPointer, tyChar, tyBool, tyEnum, tyCString, + of tyPtr, tyPointer, tyChar, tyBool, tyEnum, tyCstring, tyInt..tyUInt64, tyRange, tyVar, tyLent, tyNil: linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)]) else: internalError(p.config, "genAssignment: " & $ty.kind) @@ -449,7 +449,7 @@ proc genDeepCopy(p: BProc; dest, src: TLoc) = [rdLoc(dest), rdLoc(src), getSize(p.config, dest.t)]) else: linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)]) - of tyPointer, tyChar, tyBool, tyEnum, tyCString, + of tyPointer, tyChar, tyBool, tyEnum, tyCstring, tyInt..tyUInt64, tyRange, tyVar, tyLent: linefmt(p, cpsStmts, "$1 = $2;$n", [rdLoc(dest), rdLoc(src)]) else: internalError(p.config, "genDeepCopy: " & $ty.kind) @@ -1016,7 +1016,7 @@ proc genBracketExpr(p: BProc; n: PNode; d: var TLoc) = of tyArray: genArrayElem(p, n, n[0], n[1], d) of tyOpenArray, tyVarargs: genOpenArrayElem(p, n, n[0], n[1], d) of tySequence, tyString: genSeqElem(p, n, n[0], n[1], d) - of tyCString: genCStringElem(p, n, n[0], n[1], d) + of tyCstring: genCStringElem(p, n, n[0], n[1], d) of tyTuple: genTupleElem(p, n, d) else: internalError(p.config, n.info, "expr(nkBracketExpr, " & $ty.kind & ')') discard getTypeDesc(p.module, n.typ) @@ -1666,7 +1666,7 @@ proc genRepr(p: BProc, e: PNode, d: var TLoc) = putIntoDest(p, d, e, ropecg(p.module, "#reprOpenArray($1, $2)", [rdLoc(b), genTypeInfoV1(p.module, elemType(t), e.info)]), a.storage) - of tyCString, tyArray, tyRef, tyPtr, tyPointer, tyNil, tySequence: + of tyCstring, tyArray, tyRef, tyPtr, tyPointer, tyNil, tySequence: putIntoDest(p, d, e, ropecg(p.module, "#reprAny($1, $2)", [ rdLoc(a), genTypeInfoV1(p.module, t, e.info)]), a.storage) @@ -1745,7 +1745,7 @@ proc genArrayLen(p: BProc, e: PNode, d: var TLoc, op: TMagic) = else: if op == mHigh: unaryExpr(p, e, d, "($1.Field1-1)") else: unaryExpr(p, e, d, "$1.Field1") - of tyCString: + of tyCstring: if op == mHigh: unaryExpr(p, e, d, "($1 ? (#nimCStrLen($1)-1) : -1)") else: unaryExpr(p, e, d, "($1 ? #nimCStrLen($1) : 0)") of tyString: @@ -2920,7 +2920,7 @@ proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo): Rope = of tyBool: result = rope"NIM_FALSE" of tyEnum, tyChar, tyInt..tyInt64, tyUInt..tyUInt64: result = rope"0" of tyFloat..tyFloat128: result = rope"0.0" - of tyCString, tyVar, tyLent, tyPointer, tyPtr, tyUntyped, + of tyCstring, tyVar, tyLent, tyPointer, tyPtr, tyUntyped, tyTyped, tyTypeDesc, tyStatic, tyRef, tyNil: result = rope"NIM_NIL" of tyString, tySequence: @@ -3187,7 +3187,7 @@ proc genBracedInit(p: BProc, n: PNode; isConst: bool; optionalType: PType): Rope of tyObject: result = genConstObjConstr(p, n, isConst) - of tyString, tyCString: + of tyString, tyCstring: if optSeqDestructors in p.config.globalOptions and n.kind != nkNilLit and ty == tyString: result = genStringLiteralV2Const(p.module, n, isConst) else: diff --git a/compiler/ccgreset.nim b/compiler/ccgreset.nim index ef1505f57..fc7370d81 100644 --- a/compiler/ccgreset.nim +++ b/compiler/ccgreset.nim @@ -85,7 +85,7 @@ proc specializeResetT(p: BProc, accessor: Rope, typ: PType) = lineCg(p, cpsStmts, "$1 = NIM_NIL;$n", [accessor]) of tyChar, tyBool, tyEnum, tyInt..tyUInt64: lineCg(p, cpsStmts, "$1 = 0;$n", [accessor]) - of tyCString, tyPointer, tyPtr, tyVar, tyLent: + of tyCstring, tyPointer, tyPtr, tyVar, tyLent: lineCg(p, cpsStmts, "$1 = NIM_NIL;$n", [accessor]) else: discard diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 9d95e3f67..3d4619764 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -189,7 +189,7 @@ proc mapType(conf: ConfigRef; typ: PType; kind: TSymKind): TCTypeKind = of tySequence: result = ctNimSeq of tyProc: result = if typ.callConv != ccClosure: ctProc else: ctStruct of tyString: result = ctNimStr - of tyCString: result = ctCString + of tyCstring: result = ctCString of tyInt..tyUInt64: result = TCTypeKind(ord(typ.kind) - ord(tyInt) + ord(ctInt)) of tyStatic: @@ -292,7 +292,7 @@ proc getSimpleTypeDesc(m: BModule, typ: PType): Rope = else: discard cgsym(m, "NimStringDesc") result = typeNameOrLiteral(m, typ, "NimStringDesc*") - of tyCString: result = typeNameOrLiteral(m, typ, "NCSTRING") + of tyCstring: result = typeNameOrLiteral(m, typ, "NCSTRING") of tyBool: result = typeNameOrLiteral(m, typ, "NIM_BOOL") of tyChar: result = typeNameOrLiteral(m, typ, "NIM_CHAR") of tyNil: result = typeNameOrLiteral(m, typ, "void*") @@ -1452,7 +1452,7 @@ proc genTypeInfoV1(m: BModule, t: PType; info: TLineInfo): Rope = case t.kind of tyEmpty, tyVoid: result = rope"0" - of tyPointer, tyBool, tyChar, tyCString, tyString, tyInt..tyUInt64, tyVar, tyLent: + of tyPointer, tyBool, tyChar, tyCstring, tyString, tyInt..tyUInt64, tyVar, tyLent: genTypeInfoAuxBase(m, t, t, result, rope"0", info) of tyStatic: if t.n != nil: result = genTypeInfoV1(m, lastSon t, info) diff --git a/compiler/concepts.nim b/compiler/concepts.nim index 54579f73f..de994f1b8 100644 --- a/compiler/concepts.nim +++ b/compiler/concepts.nim @@ -139,7 +139,7 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool = if result: when logBindings: echo "A adding ", f, " ", ak m.inferred.add((f, ak)) - elif m.magic == mArrGet and ak.kind in {tyArray, tyOpenArray, tySequence, tyVarargs, tyCString, tyString}: + elif m.magic == mArrGet and ak.kind in {tyArray, tyOpenArray, tySequence, tyVarargs, tyCstring, tyString}: when logBindings: echo "B adding ", f, " ", lastSon ak m.inferred.add((f, lastSon ak)) result = true @@ -165,7 +165,7 @@ proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool = result = false of tyEnum, tyObject, tyDistinct: result = sameType(f, a) - of tyEmpty, tyString, tyCString, tyPointer, tyNil, tyUntyped, tyTyped, tyVoid: + of tyEmpty, tyString, tyCstring, tyPointer, tyNil, tyUntyped, tyTyped, tyVoid: result = a.skipTypes(ignorableForArgType).kind == f.kind of tyBool, tyChar, tyInt..tyUInt64: let ak = a.skipTypes(ignorableForArgType) diff --git a/compiler/evalffi.nim b/compiler/evalffi.nim index 0d471cf98..5004011ed 100644 --- a/compiler/evalffi.nim +++ b/compiler/evalffi.nim @@ -92,7 +92,7 @@ proc mapType(conf: ConfigRef, t: ast.PType): ptr libffi.Type = else: result = nil of tyFloat, tyFloat64: result = addr libffi.type_double of tyFloat32: result = addr libffi.type_float - of tyVar, tyLent, tyPointer, tyPtr, tyRef, tyCString, tySequence, tyString, tyUntyped, + of tyVar, tyLent, tyPointer, tyPtr, tyRef, tyCstring, tySequence, tyString, tyUntyped, tyTyped, tyTypeDesc, tyProc, tyArray, tyStatic, tyNil: result = addr libffi.type_pointer of tyDistinct, tyAlias, tySink: @@ -205,7 +205,7 @@ proc pack(conf: ConfigRef, v: PNode, typ: PType, res: pointer) = of tyFloat32: awr(float32, v.floatVal) of tyFloat64: awr(float64, v.floatVal) - of tyPointer, tyProc, tyCString, tyString: + of tyPointer, tyProc, tyCstring, tyString: if v.kind == nkNilLit: # nothing to do since the memory is 0 initialized anyway discard @@ -386,7 +386,7 @@ proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode = result = unpackObject(conf, x, typ, n) of tyArray: result = unpackArray(conf, x, typ, n) - of tyCString, tyString: + of tyCstring, tyString: let p = rd(cstring, x) if p.isNil: setNil() @@ -402,7 +402,7 @@ proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode = proc fficast*(conf: ConfigRef, x: PNode, destTyp: PType): PNode = if x.kind == nkPtrLit and x.typ.kind in {tyPtr, tyRef, tyVar, tyLent, tyPointer, - tyProc, tyCString, tyString, + tyProc, tyCstring, tyString, tySequence}: result = newNodeIT(x.kind, x.info, destTyp) result.intVal = x.intVal diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index ca716b1c0..350563d6b 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -210,7 +210,7 @@ proc mapType(typ: PType): TJSTypeKind = if t.n != nil: result = mapType(lastSon t) else: result = etyNone of tyProc: result = etyProc - of tyCString: result = etyString + of tyCstring: result = etyString of tyConcept, tyIterable: doAssert false proc mapType(p: PProc; typ: PType): TJSTypeKind = @@ -1047,14 +1047,14 @@ proc needsNoCopy(p: PProc; y: PNode): bool = return y.kind in nodeKindsNeedNoCopy or ((mapType(y.typ) != etyBaseIndex or (y.kind == nkSym and y.sym.kind == skParam)) and (skipTypes(y.typ, abstractInst).kind in - {tyRef, tyPtr, tyLent, tyVar, tyCString, tyProc, tyOwned} + IntegralTypes)) + {tyRef, tyPtr, tyLent, tyVar, tyCstring, tyProc, tyOwned} + IntegralTypes)) proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = var a, b: TCompRes var xtyp = mapType(p, x.typ) # disable `[]=` for cstring - if x.kind == nkBracketExpr and x.len >= 2 and x[0].typ.skipTypes(abstractInst).kind == tyCString: + if x.kind == nkBracketExpr and x.len >= 2 and x[0].typ.skipTypes(abstractInst).kind == tyCstring: localError(p.config, x.info, "cstring doesn't support `[]=` operator") gen(p, x, a) @@ -1263,14 +1263,14 @@ proc genArrayAccess(p: PProc, n: PNode, r: var TCompRes) = var ty = skipTypes(n[0].typ, abstractVarRange) if ty.kind in {tyRef, tyPtr, tyLent, tyOwned}: ty = skipTypes(ty.lastSon, abstractVarRange) case ty.kind - of tyArray, tyOpenArray, tySequence, tyString, tyCString, tyVarargs: + of tyArray, tyOpenArray, tySequence, tyString, tyCstring, tyVarargs: genArrayAddr(p, n, r) of tyTuple: genFieldAddr(p, n, r) else: internalError(p.config, n.info, "expr(nkBracketExpr, " & $ty.kind & ')') r.typ = mapType(n.typ) if r.res == nil: internalError(p.config, n.info, "genArrayAccess") - if ty.kind == tyCString: + if ty.kind == tyCstring: r.res = "$1.charCodeAt($2)" % [r.address, r.res] elif r.typ == etyBaseIndex: if needsTemp(p, n[0]): @@ -1340,7 +1340,7 @@ proc genAddr(p: PProc, n: PNode, r: var TCompRes) = else: let kindOfIndexedExpr = skipTypes(n[0][0].typ, abstractVarRange).kind case kindOfIndexedExpr - of tyArray, tyOpenArray, tySequence, tyString, tyCString, tyVarargs: + of tyArray, tyOpenArray, tySequence, tyString, tyCstring, tyVarargs: genArrayAddr(p, n[0], r) of tyTuple: genFieldAddr(p, n[0], r) @@ -1744,7 +1744,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope = result = putToSeq("null", indirect) of tySequence, tyString: result = putToSeq("[]", indirect) - of tyCString, tyProc: + of tyCstring, tyProc: result = putToSeq("null", indirect) of tyStatic: if t.n != nil: @@ -2030,7 +2030,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = gen(p, n[1], lhs) gen(p, n[2], rhs) - if skipTypes(n[1].typ, abstractVarRange).kind == tyCString: + if skipTypes(n[1].typ, abstractVarRange).kind == tyCstring: let (b, tmp) = maybeMakeTemp(p, n[2], rhs) r.res = "if (null != $1) { if (null == $2) $2 = $3; else $2 += $3; }" % [b, lhs.rdLoc, tmp] @@ -2087,7 +2087,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = of mLengthStr, mLengthSeq, mLengthOpenArray, mLengthArray: var x: TCompRes gen(p, n[1], x) - if skipTypes(n[1].typ, abstractInst).kind == tyCString: + if skipTypes(n[1].typ, abstractInst).kind == tyCstring: let (a, tmp) = maybeMakeTemp(p, n[1], x) r.res = "(($1) == null ? 0 : ($2).length)" % [a, tmp] else: @@ -2096,7 +2096,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = of mHigh: var x: TCompRes gen(p, n[1], x) - if skipTypes(n[1].typ, abstractInst).kind == tyCString: + if skipTypes(n[1].typ, abstractInst).kind == tyCstring: let (a, tmp) = maybeMakeTemp(p, n[1], x) r.res = "(($1) == null ? -1 : ($2).length - 1)" % [a, tmp] else: diff --git a/compiler/jstypes.nim b/compiler/jstypes.nim index 2073c252e..81f03dc12 100644 --- a/compiler/jstypes.nim +++ b/compiler/jstypes.nim @@ -128,7 +128,7 @@ proc genTypeInfo(p: PProc, typ: PType): Rope = case t.kind of tyDistinct: result = genTypeInfo(p, t[0]) - of tyPointer, tyProc, tyBool, tyChar, tyCString, tyString, tyInt..tyUInt64: + of tyPointer, tyProc, tyBool, tyChar, tyCstring, tyString, tyInt..tyUInt64: var s = "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" % [result, rope(ord(t.kind))] diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 436665827..ec0435f95 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -825,7 +825,7 @@ proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) = case t.kind of tyNone, tyEmpty, tyVoid: discard - of tyPointer, tySet, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString, + of tyPointer, tySet, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCstring, tyPtr, tyUncheckedArray, tyVar, tyLent: defaultOp(c, t, body, x, y) of tyRef: diff --git a/compiler/magicsys.nim b/compiler/magicsys.nim index 38d2345c8..ab234a2a8 100644 --- a/compiler/magicsys.nim +++ b/compiler/magicsys.nim @@ -68,7 +68,7 @@ proc getSysType*(g: ModuleGraph; info: TLineInfo; kind: TTypeKind): PType = of tyBool: result = sysTypeFromName("bool") of tyChar: result = sysTypeFromName("char") of tyString: result = sysTypeFromName("string") - of tyCString: result = sysTypeFromName("cstring") + of tyCstring: result = sysTypeFromName("cstring") of tyPointer: result = sysTypeFromName("pointer") of tyNil: result = newSysType(g, tyNil, g.config.target.ptrSize) else: internalError(g.config, "request for typekind: " & $kind) diff --git a/compiler/nilcheck.nim b/compiler/nilcheck.nim index b779830d6..f3ec893f7 100644 --- a/compiler/nilcheck.nim +++ b/compiler/nilcheck.nim @@ -1283,7 +1283,7 @@ proc typeNilability(typ: PType): Nilability = # echo "typeNilability ", $typ.flags, " ", $typ.kind result = if tfNotNil in typ.flags: Safe - elif typ.kind in {tyRef, tyCString, tyPtr, tyPointer}: + elif typ.kind in {tyRef, tyCstring, tyPtr, tyPointer}: # # tyVar ? tyVarargs ? tySink ? tyLent ? # TODO spec? tests? diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 13b53ef32..1582cc0fd 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -375,7 +375,7 @@ proc semLowHigh(c: PContext, n: PNode, m: TMagic): PNode = n[1] = semExprWithType(c, n[1], {efDetermineType}) var typ = skipTypes(n[1].typ, abstractVarRange + {tyTypeDesc, tyUserTypeClassInst}) case typ.kind - of tySequence, tyString, tyCString, tyOpenArray, tyVarargs: + of tySequence, tyString, tyCstring, tyOpenArray, tyVarargs: n.typ = getSysType(c.graph, n.info, tyInt) of tyArray: n.typ = typ[0] # indextype @@ -1508,7 +1508,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = arr = arr.base case arr.kind - of tyArray, tyOpenArray, tyVarargs, tySequence, tyString, tyCString, + of tyArray, tyOpenArray, tyVarargs, tySequence, tyString, tyCstring, tyUncheckedArray: if n.len != 2: return nil n[0] = makeDeref(n[0]) diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 0d4f6a0bd..04ed73209 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -143,7 +143,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; idgen: IdGenerator; g: ModuleGraph): P elif a.kind in {nkStrLit..nkTripleStrLit}: if a.typ.kind == tyString: result = newIntNodeT(toInt128(a.strVal.len), n, idgen, g) - elif a.typ.kind == tyCString: + elif a.typ.kind == tyCstring: result = newIntNodeT(toInt128(nimCStrLen(a.strVal)), n, idgen, g) else: result = newIntNodeT(toInt128(a.len), n, idgen, g) @@ -578,7 +578,7 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode result = newIntNodeT(firstOrd(g.config, n[1].typ), n, idgen, g) of mHigh: if skipTypes(n[1].typ, abstractVar+{tyUserTypeClassInst}).kind notin - {tySequence, tyString, tyCString, tyOpenArray, tyVarargs}: + {tySequence, tyString, tyCstring, tyOpenArray, tyVarargs}: if skipTypes(n[1].typ, abstractVarRange).kind in tyFloat..tyFloat64: result = newFloatNodeT(lastFloat(n[1].typ), n, g) else: diff --git a/compiler/semmacrosanity.nim b/compiler/semmacrosanity.nim index a8eb62067..aebee8998 100644 --- a/compiler/semmacrosanity.nim +++ b/compiler/semmacrosanity.nim @@ -92,7 +92,7 @@ proc annotateType*(n: PNode, t: PType; conf: ConfigRef) = else: globalError(conf, n.info, "integer literal must have some int type") of nkStrLit..nkTripleStrLit: - if x.kind in {tyString, tyCString}: + if x.kind in {tyString, tyCstring}: n.typ = t else: globalError(conf, n.info, "string literal must be of some string type") diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 52998c214..e66c9596a 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -696,7 +696,7 @@ proc paramType(op: PType, i: int): PType = if op != nil and i < op.len: result = op[i] proc cstringCheck(tracked: PEffects; n: PNode) = - if n[0].typ.kind == tyCString and (let a = skipConv(n[1]); + if n[0].typ.kind == tyCstring and (let a = skipConv(n[1]); a.typ.kind == tyString and a.kind notin {nkStrLit..nkTripleStrLit}): message(tracked.config, n.info, warnUnsafeCode, renderTree(n)) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 4763f0fd3..f7c42e638 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -95,7 +95,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = of tyTuple: if v.len == 2: strVal = v[1] # second tuple part is the string value - if skipTypes(strVal.typ, abstractInst).kind in {tyString, tyCString}: + if skipTypes(strVal.typ, abstractInst).kind in {tyString, tyCstring}: if not isOrdinalType(v[0].typ, allowEnumWithHoles=true): localError(c.config, v[0].info, errOrdinalTypeExpected & "; given: " & typeToString(v[0].typ, preferDesc)) x = toInt64(getOrdValue(v[0])) # first tuple part is the ordinal @@ -104,7 +104,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = localError(c.config, strVal.info, errStringLiteralExpected) else: localError(c.config, v.info, errWrongNumberOfVariables) - of tyString, tyCString: + of tyString, tyCstring: strVal = v x = counter else: @@ -2037,7 +2037,7 @@ proc processMagicType(c: PContext, m: PSym) = if optSeqDestructors in c.config.globalOptions: incl m.typ.flags, tfHasAsgn of mCstring: - setMagicIntegral(c.config, m, tyCString, c.config.target.ptrSize) + setMagicIntegral(c.config, m, tyCstring, c.config.target.ptrSize) rawAddSon(m.typ, getSysType(c.graph, m.info, tyChar)) of mPointer: setMagicIntegral(c.config, m, tyPointer, c.config.target.ptrSize) of mNil: setMagicType(c.config, m, tyNil, c.config.target.ptrSize) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 767f34dda..2563c38e9 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -260,7 +260,7 @@ proc sumGeneric(t: PType): int = of tyGenericParam, tyUntyped, tyTyped: break of tyAlias, tySink: t = t.lastSon of tyBool, tyChar, tyEnum, tyObject, tyPointer, - tyString, tyCString, tyInt..tyInt64, tyFloat..tyFloat128, + tyString, tyCstring, tyInt..tyInt64, tyFloat..tyFloat128, tyUInt..tyUInt64, tyCompositeTypeClass: return isvar else: @@ -1383,7 +1383,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, # 'pointer' is NOT compatible to regionized pointers # so 'dealloc(regionPtr)' fails: if a.len == 1: result = isConvertible - of tyCString: result = isConvertible + of tyCstring: result = isConvertible else: discard of tyString: case a.kind @@ -1394,10 +1394,10 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, result = isEqual of tyNil: result = isNone else: discard - of tyCString: + of tyCstring: # conversion from string to cstring is automatic: case a.kind - of tyCString: + of tyCstring: if tfNotNil in f.flags and tfNotNil notin a.flags: result = isNilConversion else: @@ -2436,7 +2436,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var Int n[a] = prepareOperand(c, n[a]) if skipTypes(n[a].typ, abstractVar-{tyTypeDesc}).kind==tyString: m.call.add implicitConv(nkHiddenStdConv, - getSysType(c.graph, n[a].info, tyCString), + getSysType(c.graph, n[a].info, tyCstring), copyTree(n[a]), m, c) else: m.call.add copyTree(n[a]) diff --git a/compiler/sizealignoffsetimpl.nim b/compiler/sizealignoffsetimpl.nim index b50777c9e..44ba32579 100644 --- a/compiler/sizealignoffsetimpl.nim +++ b/compiler/sizealignoffsetimpl.nim @@ -242,7 +242,7 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) = else: typ.size = conf.target.ptrSize typ.align = int16(conf.target.ptrSize) - of tyCString, tySequence, tyPtr, tyRef, tyVar, tyLent: + of tyCstring, tySequence, tyPtr, tyRef, tyVar, tyLent: let base = typ.lastSon if base == typ: # this is not the correct location to detect ``type A = ptr A`` diff --git a/compiler/sourcemap.nim b/compiler/sourcemap.nim index 2ec40227b..b87de75f3 100644 --- a/compiler/sourcemap.nim +++ b/compiler/sourcemap.nim @@ -8,13 +8,13 @@ type name*: string children*: seq[Child] - C = enum cSourceNode, cString + C = enum cSourceNode, cSourceString Child* = ref object case kind*: C: of cSourceNode: node*: SourceNode - of cString: + of cSourceString: s*: string SourceMap* = ref object @@ -44,7 +44,7 @@ type proc child*(s: string): Child = - Child(kind: cString, s: s) + Child(kind: cSourceString, s: s) proc child*(node: SourceNode): Child = @@ -72,7 +72,7 @@ proc text*(sourceNode: SourceNode, depth: int): string = let empty = " " result = &"{repeat(empty, depth)}SourceNode({sourceNode.source}:{sourceNode.line}:{sourceNode.column}):\n" for child in sourceNode.children: - if child.kind == cString: + if child.kind == cSourceString: result.add(&"{repeat(empty, depth + 1)}{child.s}\n") else: result.add(child.node.text(depth + 1)) @@ -308,7 +308,7 @@ proc addMapping*(map: SourceMapGenerator, mapping: Mapping) = proc walk*(node: SourceNode, fn: proc(line: string, original: SourceNode)) = for child in node.children: - if child.kind == cString and child.s.len > 0: + if child.kind == cSourceString and child.s.len > 0: fn(child.s, node) else: child.node.walk(fn) diff --git a/compiler/transf.nim b/compiler/transf.nim index 48c737f46..fb59887b0 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -497,14 +497,14 @@ proc transformConv(c: PTransf, n: PNode): PNode = result.typ = takeType(n.typ, n[1].typ, c.graph, c.idgen) #echo n.info, " came here and produced ", typeToString(result.typ), # " from ", typeToString(n.typ), " and ", typeToString(n[1].typ) - of tyCString: + of tyCstring: if source.kind == tyString: result = newTransNode(nkStringToCString, n, 1) result[0] = transform(c, n[1]) else: result = transformSons(c, n) of tyString: - if source.kind == tyCString: + if source.kind == tyCstring: result = newTransNode(nkCStringToString, n, 1) result[0] = transform(c, n[1]) else: diff --git a/compiler/typeallowed.nim b/compiler/typeallowed.nim index 270e7c028..2f0c039a4 100644 --- a/compiler/typeallowed.nim +++ b/compiler/typeallowed.nim @@ -116,7 +116,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, result = t of tyNil: if kind != skConst and kind != skParam: result = t - of tyString, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCString, tyPointer: + of tyString, tyBool, tyChar, tyEnum, tyInt..tyUInt64, tyCstring, tyPointer: result = nil of tyOrdinal: if kind != skParam: result = t diff --git a/compiler/types.nim b/compiler/types.nim index ce4812506..e56fcffe4 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -503,7 +503,7 @@ proc typeToString(typ: PType, prefer: TPreferedDesc = preferName): string = result = typeToString(t[0]) elif prefer in {preferResolved, preferMixed}: case t.kind - of IntegralTypes + {tyFloat..tyFloat128} + {tyString, tyCString}: + of IntegralTypes + {tyFloat..tyFloat128} + {tyString, tyCstring}: result = typeToStr[t.kind] of tyGenericBody: result = typeToString(t.lastSon) @@ -762,7 +762,7 @@ proc firstOrd*(conf: ConfigRef; t: PType): Int128 = of tyOrdinal: if t.len > 0: result = firstOrd(conf, lastSon(t)) else: internalError(conf, "invalid kind for firstOrd(" & $t.kind & ')') - of tyUncheckedArray, tyCString: + of tyUncheckedArray, tyCstring: result = Zero else: internalError(conf, "invalid kind for firstOrd(" & $t.kind & ')') @@ -1129,7 +1129,7 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = return true case a.kind - of tyEmpty, tyChar, tyBool, tyNil, tyPointer, tyString, tyCString, + of tyEmpty, tyChar, tyBool, tyNil, tyPointer, tyString, tyCstring, tyInt..tyUInt64, tyTyped, tyUntyped, tyVoid: result = sameFlags(a, b) of tyStatic, tyFromExpr: diff --git a/compiler/vm.nim b/compiler/vm.nim index e3ba2994f..5c993d77c 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -399,7 +399,7 @@ proc opConv(c: PCtx; dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): dest.node.strVal = $src.floatVal of tyString: dest.node.strVal = src.node.strVal - of tyCString: + of tyCstring: if src.node.kind == nkBracket: # Array of chars var strVal = "" diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim index 6191f9459..b67e2e48a 100644 --- a/compiler/vmdeps.nim +++ b/compiler/vmdeps.nim @@ -257,7 +257,7 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo; result.add t.n[1].copyTree of tyPointer: result = atomicType("pointer", mPointer) of tyString: result = atomicType("string", mString) - of tyCString: result = atomicType("cstring", mCstring) + of tyCstring: result = atomicType("cstring", mCstring) of tyInt: result = atomicType("int", mInt) of tyInt8: result = atomicType("int8", mInt8) of tyInt16: result = atomicType("int16", mInt16) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 951d47d2f..c60e81020 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -190,7 +190,7 @@ proc getSlotKind(t: PType): TSlotKind = case t.skipTypes(abstractRange-{tyTypeDesc}).kind of tyBool, tyChar, tyEnum, tyOrdinal, tyInt..tyInt64, tyUInt..tyUInt64: slotTempInt - of tyString, tyCString: + of tyString, tyCstring: slotTempStr of tyFloat..tyFloat128: slotTempFloat @@ -1053,7 +1053,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of mLengthStr: case n[1].typ.kind of tyString: genUnaryABI(c, n, dest, opcLenStr) - of tyCString: genUnaryABI(c, n, dest, opcLenCstring) + of tyCstring: genUnaryABI(c, n, dest, opcLenCstring) else: doAssert false, $n[1].typ.kind of mIncl, mExcl: unused(c, n, dest) @@ -1199,7 +1199,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = let tmp = c.genx(n[1]) case n[1].typ.skipTypes(abstractVar-{tyTypeDesc}).kind: of tyString: c.gABI(n, opcLenStr, dest, tmp, 1) - of tyCString: c.gABI(n, opcLenCstring, dest, tmp, 1) + of tyCstring: c.gABI(n, opcLenCstring, dest, tmp, 1) else: c.gABI(n, opcLenSeq, dest, tmp, 1) c.freeTemp(tmp) of mEcho: @@ -1530,7 +1530,7 @@ proc genAsgn(c: PCtx; le, ri: PNode; requiresCopy: bool) = let idx = c.genIndex(le[1], le[0].typ) let tmp = c.genx(ri) if le[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind in { - tyString, tyCString}: + tyString, tyCstring}: c.preventFalseAlias(le, opcWrStrIdx, dest, idx, tmp) else: c.preventFalseAlias(le, opcWrArr, dest, idx, tmp) @@ -1775,7 +1775,7 @@ proc genCheckedObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = proc genArrAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = let arrayType = n[0].typ.skipTypes(abstractVarRange-{tyTypeDesc}).kind - if arrayType in {tyString, tyCString}: + if arrayType in {tyString, tyCstring}: let opc = if gfNodeAddr in flags: opcLdStrIdxAddr else: opcLdStrIdx genArrAccessOpcode(c, n, dest, opc, flags) elif arrayType == tyTypeDesc: @@ -1813,7 +1813,7 @@ proc getNullValue(typ: PType, info: TLineInfo; conf: ConfigRef): PNode = result = newNodeIT(nkUIntLit, info, t) of tyFloat..tyFloat128: result = newNodeIT(nkFloatLit, info, t) - of tyCString, tyString: + of tyCstring, tyString: result = newNodeIT(nkStrLit, info, t) result.strVal = "" of tyVar, tyLent, tyPointer, tyPtr, tyUntyped, diff --git a/compiler/vmmarshal.nim b/compiler/vmmarshal.nim index ffd8e16d7..d48d2408c 100644 --- a/compiler/vmmarshal.nim +++ b/compiler/vmmarshal.nim @@ -121,7 +121,7 @@ proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet; s.add(", ") storeAny(s, t.lastSon, a, stored, conf) s.add("]") - of tyString, tyCString: + of tyString, tyCstring: if a.kind == nkNilLit: s.add("null") else: s.add(escapeJson(a.strVal)) of tyInt..tyInt64, tyUInt..tyUInt64: s.add($a.intVal) @@ -255,7 +255,7 @@ proc loadAny(p: var JsonParser, t: PType, if p.kind == jsonArrayEnd: next(p) else: raiseParseErr(p, "']' end of ref-address pair expected") else: raiseParseErr(p, "int for pointer type expected") - of tyString, tyCString: + of tyString, tyCstring: case p.kind of jsonNull: result = newNode(nkNilLit) diff --git a/lib/core/typeinfo.nim b/lib/core/typeinfo.nim index 08d567351..d66dc1898 100644 --- a/lib/core/typeinfo.nim +++ b/lib/core/typeinfo.nim @@ -322,12 +322,12 @@ proc base*(x: Any): Any = proc isNil*(x: Any): bool = ## `isNil` for an `x` that represents a cstring, proc or ## some pointer type. - assert x.rawType.kind in {tyCString, tyRef, tyPtr, tyPointer, tyProc} + assert x.rawType.kind in {tyCstring, tyRef, tyPtr, tyPointer, tyProc} result = isNil(cast[ppointer](x.value)[]) const pointerLike = - when defined(gcDestructors): {tyCString, tyRef, tyPtr, tyPointer, tyProc} - else: {tyString, tyCString, tyRef, tyPtr, tyPointer, tySequence, tyProc} + when defined(gcDestructors): {tyCstring, tyRef, tyPtr, tyPointer, tyProc} + else: {tyString, tyCstring, tyRef, tyPtr, tyPointer, tySequence, tyProc} proc getPointer*(x: Any): pointer = ## Retrieves the pointer value out of `x`. `x` needs to be of kind @@ -669,7 +669,7 @@ proc setString*(x: Any, y: string) = proc getCString*(x: Any): cstring = ## Retrieves the `cstring` value out of `x`. `x` needs to represent a `cstring`. - assert x.rawType.kind == tyCString + assert x.rawType.kind == tyCstring result = cast[ptr cstring](x.value)[] proc assign*(x, y: Any) = diff --git a/lib/system/assign.nim b/lib/system/assign.nim index faf16fc90..20b854107 100644 --- a/lib/system/assign.nim +++ b/lib/system/assign.nim @@ -167,7 +167,7 @@ when false: of tyPointer: k = "range" of tyOpenArray: k = "openarray" of tyString: k = "string" - of tyCString: k = "cstring" + of tyCstring: k = "cstring" of tyInt: k = "int" of tyInt32: k = "int32" else: k = "other" diff --git a/lib/system/hti.nim b/lib/system/hti.nim index 3dbcd7615..9acaae88b 100644 --- a/lib/system/hti.nim +++ b/lib/system/hti.nim @@ -40,7 +40,7 @@ type tyPointer, tyOpenArray, tyString, - tyCString, + tyCstring, tyForward, tyInt, tyInt8, diff --git a/lib/system/repr.nim b/lib/system/repr.nim index 7424500eb..020c2281f 100644 --- a/lib/system/repr.nim +++ b/lib/system/repr.nim @@ -281,7 +281,7 @@ when not defined(useNimRtl): of tyString: let sp = cast[ptr string](p) reprStrAux(result, sp[].cstring, sp[].len) - of tyCString: + of tyCstring: let cs = cast[ptr cstring](p)[] if cs.isNil: add result, "nil" else: reprStrAux(result, cs, cs.len) diff --git a/lib/system/reprjs.nim b/lib/system/reprjs.nim index 36972024a..cdc3403f8 100644 --- a/lib/system/reprjs.nim +++ b/lib/system/reprjs.nim @@ -200,7 +200,7 @@ proc reprAux(result: var string, p: pointer, typ: PNimType, var fp: int {. emit: "`fp` = `p`;\n" .} add(result, reprStr(cast[string](p))) - of tyCString: + of tyCstring: var fp: cstring {. emit: "`fp` = `p`;\n" .} if fp.isNil: From 5c5f356347b15a82491c7f23e9bf2dd30a0c80f2 Mon Sep 17 00:00:00 2001 From: flywind Date: Sat, 17 Apr 2021 17:48:22 +0800 Subject: [PATCH 1950/3768] replace defer with try ... finally (#17753) --- lib/core/rlocks.nim | 10 +++++----- tests/stdlib/tuserlocks.nim | 11 +++++++++-- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/lib/core/rlocks.nim b/lib/core/rlocks.nim index 27a9c5e60..f9224adb8 100644 --- a/lib/core/rlocks.nim +++ b/lib/core/rlocks.nim @@ -47,9 +47,9 @@ proc release*(lock: var RLock) = template withRLock*(lock: var RLock, code: untyped): untyped = ## Acquires the given lock and then executes the code. - block: - acquire(lock) - defer: - release(lock) - {.locks: [lock].}: + acquire(lock) + {.locks: [lock].}: + try: code + finally: + release(lock) diff --git a/tests/stdlib/tuserlocks.nim b/tests/stdlib/tuserlocks.nim index f6eafa05d..9251fa9e2 100644 --- a/tests/stdlib/tuserlocks.nim +++ b/tests/stdlib/tuserlocks.nim @@ -1,8 +1,8 @@ discard """ - cmd: "nim $target --threads:on $options $file" + matrix: "--threads:on" """ -import rlocks +import std/rlocks var r: RLock r.initRLock() @@ -10,4 +10,11 @@ doAssert r.tryAcquire() doAssert r.tryAcquire() r.release() r.release() + +block: + var x = 12 + withRLock r: + inc x + doAssert x == 13 + r.deinitRLock() From 5c9c1988f6bb92e8cfdc639fc0f83e78cb5efbd2 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 17 Apr 2021 02:49:54 -0700 Subject: [PATCH 1951/3768] -d:nimDebug: calls doAssert false instead of quit (#17739) --- compiler/ccgexprs.nim | 4 ++-- compiler/docgen.nim | 2 +- compiler/extccomp.nim | 2 +- compiler/ic/ic.nim | 3 +-- compiler/main.nim | 6 +++--- compiler/msgs.nim | 3 ++- compiler/options.nim | 10 ++++++++-- compiler/sigmatch.nim | 2 +- compiler/syntaxes.nim | 2 +- 9 files changed, 20 insertions(+), 14 deletions(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 0e54adf49..a42c30aae 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2387,13 +2387,13 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = localError(p.config, e.info, strutils.`%`(errXMustBeCompileTime, e[0].sym.name.s)) of mSpawn: when defined(leanCompiler): - quit "compiler built without support for the 'spawn' statement" + p.config.quitOrRaise "compiler built without support for the 'spawn' statement" else: let n = spawn.wrapProcForSpawn(p.module.g.graph, p.module.idgen, p.module.module, e, e.typ, nil, nil) expr(p, n, d) of mParallel: when defined(leanCompiler): - quit "compiler built without support for the 'parallel' statement" + p.config.quitOrRaise "compiler built without support for the 'parallel' statement" else: let n = semparallel.liftParallel(p.module.g.graph, p.module.idgen, p.module.module, e) expr(p, n, d) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 2f7415241..4d9f09ca6 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -498,7 +498,7 @@ proc runAllExamples(d: PDoc) = "docCmd", group.docCmd, ] if os.execShellCmd(cmd) != 0: - quit "[runnableExamples] failed: generated file: '$1' group: '$2' cmd: $3" % [outp.string, group[].prettyString, cmd] + d.conf.quitOrRaise "[runnableExamples] failed: generated file: '$1' group: '$2' cmd: $3" % [outp.string, group[].prettyString, cmd] else: # keep generated source file `outp` to allow inspection. rawMessage(d.conf, hintSuccess, ["runnableExamples: " & outp.string]) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index e0fd5e206..8d1432aa8 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -1132,7 +1132,7 @@ proc runJsonBuildInstructions*(conf: ConfigRef; projectfile: AbsoluteFile) = except: let e = getCurrentException() - quit "\ncaught exception:\n" & e.msg & "\nstacktrace:\n" & e.getStackTrace() & + conf.quitOrRaise "\ncaught exception:\n" & e.msg & "\nstacktrace:\n" & e.getStackTrace() & "error evaluating JSON file: " & jsonFile.string proc genMappingFiles(conf: ConfigRef; list: CfileList): Rope = diff --git a/compiler/ic/ic.nim b/compiler/ic/ic.nim index da0af2edf..6f208a2a4 100644 --- a/compiler/ic/ic.nim +++ b/compiler/ic/ic.nim @@ -1149,8 +1149,7 @@ proc rodViewer*(rodfile: AbsoluteFile; config: ConfigRef, cache: IdentCache) = var m: PackedModule let err = loadRodFile(rodfile, m, config, ignoreConfig=true) if err != ok: - echo "Error: could not load: ", rodfile.string, " reason: ", err - quit 1 + config.quitOrRaise "Error: could not load: " & $rodfile.string & " reason: " & $err when true: echo "exports:" diff --git a/compiler/main.nim b/compiler/main.nim index 475ba7773..6eb830164 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -229,7 +229,7 @@ proc mainCommand*(graph: ModuleGraph) = template docLikeCmd(body) = when defined(leanCompiler): - quit "compiler wasn't built with documentation generator" + conf.quitOrRaise "compiler wasn't built with documentation generator" else: wantMainModule(conf) loadConfigs(DocConfig, cache, conf, graph.idgen) @@ -278,7 +278,7 @@ proc mainCommand*(graph: ModuleGraph) = conf.setNoteDefaults(warn, true) conf.setNoteDefaults(warnRedefinitionOfLabel, false) # similar to issue #13218 when defined(leanCompiler): - quit "compiler wasn't built with documentation generator" + conf.quitOrRaise "compiler wasn't built with documentation generator" else: loadConfigs(DocConfig, cache, conf, graph.idgen) commandRst2Html(cache, conf) @@ -288,7 +288,7 @@ proc mainCommand*(graph: ModuleGraph) = warnFieldXNotSupported, warnRstStyle]: conf.setNoteDefaults(warn, true) when defined(leanCompiler): - quit "compiler wasn't built with documentation generator" + conf.quitOrRaise "compiler wasn't built with documentation generator" else: loadConfigs(DocTexConfig, cache, conf, graph.idgen) commandRst2TeX(cache, conf) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 452b1cd89..c1e1f9e39 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -398,7 +398,8 @@ proc log*(s: string) = close(f) proc quit(conf: ConfigRef; msg: TMsgKind) {.gcsafe.} = - if defined(debug) or msg == errInternal or conf.hasHint(hintStackTrace): + if conf.isDefined("nimDebug"): quitOrRaise(conf, $msg) + elif defined(debug) or msg == errInternal or conf.hasHint(hintStackTrace): {.gcsafe.}: if stackTraceAvailable() and isNil(conf.writelnHook): writeStackTrace() diff --git a/compiler/options.nim b/compiler/options.nim index 9ba4d62e4..5475410a4 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -576,6 +576,13 @@ proc isDefined*(conf: ConfigRef; symbol: string): bool = osDragonfly, osMacosx} else: discard +template quitOrRaise*(conf: ConfigRef, msg = "") = + # xxx in future work, consider whether to also intercept `msgQuit` calls + if conf.isDefined("nimDebug"): + doAssert false, msg + else: + quit(msg) # quits with QuitFailure + proc importantComments*(conf: ConfigRef): bool {.inline.} = conf.cmd in cmdDocLike + {cmdIdeTools} proc usesWriteBarrier*(conf: ConfigRef): bool {.inline.} = conf.selectedGC >= gcRefc @@ -732,8 +739,7 @@ proc completeGeneratedFilePath*(conf: ConfigRef; f: AbsoluteFile, try: createDir(subdir.string) except OSError: - writeLine(stdout, "cannot create directory: " & subdir.string) - quit(1) + conf.quitOrRaise "cannot create directory: " & subdir.string result = subdir / RelativeFile f.string.splitPath.tail #echo "completeGeneratedFilePath(", f, ") = ", result diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 2563c38e9..6a36c1381 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1011,7 +1011,7 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, when declared(deallocatedRefId): let corrupt = deallocatedRefId(cast[pointer](f)) if corrupt != 0: - quit "it's corrupt " & $corrupt + c.c.config.quitOrRaise "it's corrupt " & $corrupt if f.kind == tyUntyped: if aOrig != nil: put(c, f, aOrig) diff --git a/compiler/syntaxes.nim b/compiler/syntaxes.nim index e745e28ba..03a9702a3 100644 --- a/compiler/syntaxes.nim +++ b/compiler/syntaxes.nim @@ -50,7 +50,7 @@ proc parsePipe(filename: AbsoluteFile, inputStream: PLLStream; cache: IdentCache if i+1 < line.len and line[i] == '#' and line[i+1] == '?': when defined(nimpretty): # XXX this is a bit hacky, but oh well... - quit "can't nimpretty a source code filter" + config.quitOrRaise "can't nimpretty a source code filter: " & $filename else: inc(i, 2) while i < line.len and line[i] in Whitespace: inc(i) From 8e124da75d7b3cafe27c197a027d34f1aca012e3 Mon Sep 17 00:00:00 2001 From: Tanguy Cizain Date: Sat, 17 Apr 2021 15:34:00 +0200 Subject: [PATCH 1952/3768] fix range to unsigned conversion (#17754) --- compiler/sigmatch.nim | 2 +- tests/range/trange.nim | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 6a36c1381..b2003c0a8 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -409,7 +409,7 @@ proc handleRange(f, a: PType, min, max: TTypeKind): TTypeRelation = elif a.kind == tyRange and # Make sure the conversion happens between types w/ same signedness (f.kind in {tyInt..tyInt64} and a[0].kind in {tyInt..tyInt64} or - f.kind in {tyUInt8..tyUInt32} and a[0].kind in {tyUInt8..tyInt32}) and + f.kind in {tyUInt8..tyUInt32} and a[0].kind in {tyUInt8..tyUInt32}) and a.n[0].intVal >= firstOrd(nil, f) and a.n[1].intVal <= lastOrd(nil, f): # passing 'nil' to firstOrd/lastOrd here as type checking rules should # not depend on the target integer size configurations! diff --git a/tests/range/trange.nim b/tests/range/trange.nim index c864387f6..92ab5ea86 100644 --- a/tests/range/trange.nim +++ b/tests/range/trange.nim @@ -142,3 +142,15 @@ var a: array[4'u, string] for i in 0.. Date: Sat, 17 Apr 2021 16:27:10 +0100 Subject: [PATCH 1953/3768] make cuchar alias uint8 instead of char (#17738) --- changelog.md | 2 ++ lib/system.nim | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index cfdcd3c62..5efde5659 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,8 @@ ## Changes affecting backward compatibility +- `cuchar` now aliases `uint8` instead of `char` + - `repr` now doesn't insert trailing newline; previous behavior was very inconsistent, see #16034. Use `-d:nimLegacyReprWithNewline` for previous behavior. diff --git a/lib/system.nim b/lib/system.nim index 38f6d64d3..b7a4b45cf 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1452,7 +1452,7 @@ type # these work for most platforms: ## This is the same as the type `long double` in *C*. ## This C type is not supported by Nim's code generator. - cuchar* {.importc: "unsigned char", nodecl.} = char + cuchar* {.importc: "unsigned char", nodecl.} = uint8 ## This is the same as the type `unsigned char` in *C*. cushort* {.importc: "unsigned short", nodecl.} = uint16 ## This is the same as the type `unsigned short` in *C*. From 7b6ab5109fe76afefbf5744b5f9f534e86d94629 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 18 Apr 2021 07:27:03 +0200 Subject: [PATCH 1954/3768] IC exposes typedesc implementation shenanigans (#17759) * IC exposes typedesc implementation shenanigans; so I change system.default's definition to what it should have been to begin with * Update lib/system.nim Co-authored-by: Timothee Cour --- lib/system.nim | 2 +- tests/ic/tstdlib_import_changed.nim | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 tests/ic/tstdlib_import_changed.nim diff --git a/lib/system.nim b/lib/system.nim index b7a4b45cf..532883853 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -980,7 +980,7 @@ proc `@`* [IDX, T](a: sink array[IDX, T]): seq[T] {.magic: "ArrToSeq", noSideEff ## echo @a # => @[1, 3, 5] ## echo @b # => @['f', 'o', 'o'] -proc default*(T: typedesc): T {.magic: "Default", noSideEffect.} = +proc default*[T](_: typedesc[T]): T {.magic: "Default", noSideEffect.} = ## returns the default value of the type `T`. runnableExamples: assert (int, float).default == (0, 0.0) diff --git a/tests/ic/tstdlib_import_changed.nim b/tests/ic/tstdlib_import_changed.nim new file mode 100644 index 000000000..da69a53b5 --- /dev/null +++ b/tests/ic/tstdlib_import_changed.nim @@ -0,0 +1,15 @@ +discard """ + output: '''yes''' +""" + +echo "yes" + +#!EDIT!# + +discard """ + output: '''yes2''' +""" + +import std / [monotimes] +#discard getMonoTime() +echo "yes2" From ca3fe63bab54779e6dc2df3c9a72b9c4280c0eaf Mon Sep 17 00:00:00 2001 From: flywind Date: Sun, 18 Apr 2021 21:30:30 +0800 Subject: [PATCH 1955/3768] remove jstin from important packages (#17765) Ref https://github.com/nim-lang/Nim/pull/17759 https://github.com/LemonBoy/jstin/blob/master/src/jstin.nim is already archived and cannot pass CI anymore. --- testament/important_packages.nim | 1 - 1 file changed, 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 620ad3a00..28d7dc367 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -77,7 +77,6 @@ pkg "illwill", "nimble examples" pkg "inim" pkg "itertools", "nim doc src/itertools.nim" pkg "iterutils" -pkg "jstin" pkg "karax", "nim c -r tests/tester.nim" pkg "kdtree", "nimble test", "https://github.com/jblindsay/kdtree" pkg "loopfusion" From 42c6eec4ef7752c4f48ace2899a44840df95da9c Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 18 Apr 2021 06:34:29 -0700 Subject: [PATCH 1956/3768] fix #17749 ignore SIGPIPE signals, fix nim CI #17748 (#17752) * fix #17749 SIGPIPE * fix for windows --- changelog.md | 20 ++++++++++++-------- koch.nim | 4 ++++ lib/system/excpt.nim | 16 +++++++++++++--- tests/stdlib/tosproc.nim | 23 +++++++++++++++++++++-- tests/system/tsigexitcode.nim | 11 +++++++---- 5 files changed, 57 insertions(+), 17 deletions(-) diff --git a/changelog.md b/changelog.md index 5efde5659..8fab425f8 100644 --- a/changelog.md +++ b/changelog.md @@ -39,6 +39,18 @@ - In `std/os`, `getHomeDir`, `expandTilde`, `getTempDir`, `getConfigDir` now do not include trailing `DirSep`, unless `-d:nimLegacyHomeDir` is specified (for a transition period). +- On POSIX systems, the default signal handlers used for Nim programs (it's + used for printing the stacktrace on fatal signals) will now re-raise the + signal for the OS default handlers to handle. + + This lets the OS perform its default actions, which might include core + dumping (on select signals) and notifying the parent process about the cause + of termination. + +- On POSIX systems, we now ignore `SIGPIPE` signals, use `-d:nimLegacySigpipeHandler` + for previous behavior. + + ## Standard library additions and changes - Added support for parenthesized expressions in `strformat` @@ -219,14 +231,6 @@ `ValueError` when the real command line is not available. `parseopt` was previously excluded from `prelude` for JS, as it could not be imported. -- On POSIX systems, the default signal handlers used for Nim programs (it's - used for printing the stacktrace on fatal signals) will now re-raise the - signal for the OS default handlers to handle. - - This lets the OS perform its default actions, which might include core - dumping (on select signals) and notifying the parent process about the cause - of termination. - - Added `system.prepareStrMutation` for better support of low level `moveMem`, `copyMem` operations for Orc's copy-on-write string implementation. diff --git a/koch.nim b/koch.nim index 3e41310d7..49540eb6a 100644 --- a/koch.nim +++ b/koch.nim @@ -542,6 +542,10 @@ proc runCI(cmd: string) = # boot without -d:nimHasLibFFI to make sure this still works kochExecFold("Boot in release mode", "boot -d:release -d:nimStrictMode") + when false: # debugging: when you need to run only 1 test in CI, use something like this: + execFold("debugging test", "nim r tests/stdlib/tosproc.nim") + doAssert false, "debugging only" + ## build nimble early on to enable remainder to depend on it if needed kochExecFold("Build Nimble", "nimble") diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index 06fa45097..da034e57e 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -602,6 +602,9 @@ when defined(cpp) and appType != "lib" and not gotoBasedExceptions and quit 1 when not defined(noSignalHandler) and not defined(useNimRtl): + type Sighandler = proc (a: cint) {.noconv, benign.} + # xxx factor with ansi_c.CSighandlerT, posix.Sighandler + proc signalHandler(sign: cint) {.exportc: "signalHandler", noconv.} = template processSignal(s, action: untyped) {.dirty.} = if s == SIGINT: action("SIGINT: Interrupted by Ctrl-C.\n") @@ -652,7 +655,11 @@ when not defined(noSignalHandler) and not defined(useNimRtl): else: quit(1) + var SIG_IGN {.importc: "SIG_IGN", header: "".}: Sighandler + proc registerSignalHandler() = + # xxx `signal` is deprecated and has many caveats, we should use `sigaction` instead, e.g. + # https://stackoverflow.com/questions/231912/what-is-the-difference-between-sigaction-and-signal c_signal(SIGINT, signalHandler) c_signal(SIGSEGV, signalHandler) c_signal(SIGABRT, signalHandler) @@ -661,14 +668,17 @@ when not defined(noSignalHandler) and not defined(useNimRtl): when declared(SIGBUS): c_signal(SIGBUS, signalHandler) when declared(SIGPIPE): - c_signal(SIGPIPE, signalHandler) + when defined(nimLegacySigpipeHandler): + c_signal(SIGPIPE, signalHandler) + else: + c_signal(SIGPIPE, SIG_IGN) registerSignalHandler() # call it in initialization section proc setControlCHook(hook: proc () {.noconv.}) = # ugly cast, but should work on all architectures: - type SignalHandler = proc (sign: cint) {.noconv, benign.} - c_signal(SIGINT, cast[SignalHandler](hook)) + when declared(Sighandler): + c_signal(SIGINT, cast[Sighandler](hook)) when not defined(noSignalHandler) and not defined(useNimRtl): proc unsetControlCHook() = diff --git a/tests/stdlib/tosproc.nim b/tests/stdlib/tosproc.nim index 96cff1468..67731322a 100644 --- a/tests/stdlib/tosproc.nim +++ b/tests/stdlib/tosproc.nim @@ -206,8 +206,8 @@ else: # main driver var line = newStringOfCap(120) while true: if outp.readLine(line): - result[0].string.add(line.string) - result[0].string.add("\n") + result[0].add(line) + result[0].add("\n") else: result[1] = peekExitCode(p) if result[1] != -1: break @@ -279,3 +279,22 @@ else: # main driver when defined(posix): doAssert execCmdEx("echo $FO", env = newStringTable({"FO": "B"})) == ("B\n", 0) doAssert execCmdEx("echo $PWD", workingDir = "/") == ("/\n", 0) + + block: # bug #17749 + let output = compileNimProg("-d:case_testfile4", "D20210417T011153") + var p = startProcess(output, dir) + let inp = p.inputStream + var count = 0 + when defined(windows): + # xxx we should make osproc.hsWriteData raise IOError on windows, consistent + # with posix; we could also (in addition) make IOError a subclass of OSError. + type SIGPIPEError = OSError + else: + type SIGPIPEError = IOError + doAssertRaises(SIGPIPEError): + for i in 0..<100000: + count.inc + inp.writeLine "ok" # was giving SIGPIPE and crashing + doAssert count >= 100 + doAssert waitForExit(p) == QuitFailure + close(p) # xxx isn't that missing in other places? diff --git a/tests/system/tsigexitcode.nim b/tests/system/tsigexitcode.nim index 6922cb8eb..249256b40 100644 --- a/tests/system/tsigexitcode.nim +++ b/tests/system/tsigexitcode.nim @@ -11,10 +11,13 @@ proc main() = discard posix.raise(signal) else: # synchronize this list with lib/system/except.nim:registerSignalHandler() - let fatalSigs = [SIGINT, SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS, - SIGPIPE] - for s in fatalSigs: + let sigs = [SIGINT, SIGSEGV, SIGABRT, SIGFPE, SIGILL, SIGBUS, SIGPIPE] + for s in sigs: let (_, exitCode) = execCmdEx(quoteShellCommand [getAppFilename(), $s]) - doAssert exitCode == 128 + s, "mismatched exit code for signal " & $s + if s == SIGPIPE: + # SIGPIPE should be ignored + doAssert exitCode == 0, $(exitCode, s) + else: + doAssert exitCode == 128+s, $(exitCode, s) main() From c7b77829fe26d9053df2181d96ffc358409674a6 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 18 Apr 2021 23:38:07 +0200 Subject: [PATCH 1957/3768] IC: fixes a long standing bug about DCE set computations (#17763) --- compiler/ic/cbackend.nim | 58 +++++++++++++++++++++++----------------- 1 file changed, 33 insertions(+), 25 deletions(-) diff --git a/compiler/ic/cbackend.nim b/compiler/ic/cbackend.nim index 2f3910b32..c7e5c7266 100644 --- a/compiler/ic/cbackend.nim +++ b/compiler/ic/cbackend.nim @@ -104,14 +104,35 @@ proc aliveSymsChanged(config: ConfigRef; position: int; alive: AliveSyms): bool let oldAsSet = toPackedSet[int32](oldData) let newAsSet = toPackedSet[int32](s) echo "set of live symbols changed ", asymFile.changeFileExt("rod"), " ", position, " ", f2.err - echo "in old but not in new ", oldAsSet.difference(newAsSet) - echo "in new but not in old ", newAsSet.difference(oldAsSet) - - if execShellCmd(getAppFilename() & " rod " & quoteShell(asymFile.changeFileExt("rod"))) != 0: - echo "command failed" + echo "in old but not in new ", oldAsSet.difference(newAsSet), " number of entries in old ", oldAsSet.len + echo "in new but not in old ", newAsSet.difference(oldAsSet), " number of entries in new ", newAsSet.len + #if execShellCmd(getAppFilename() & " rod " & quoteShell(asymFile.changeFileExt("rod"))) != 0: + # echo "command failed" result = true storeAliveSymsImpl(asymFile, s) +proc genPackedModule(g: ModuleGraph, i: int; alive: var AliveSyms) = + # case statement here to enforce exhaustive checks. + case g.packed[i].status + of undefined: + discard "nothing to do" + of loading, stored: + assert false + of storing, outdated: + storeAliveSyms(g.config, g.packed[i].module.position, alive) + generateCodeForModule(g, g.packed[i], alive) + closeRodFile(g, g.packed[i].module) + of loaded: + if g.packed[i].loadedButAliveSetChanged: + generateCodeForModule(g, g.packed[i], alive) + else: + addFileToLink(g.config, g.packed[i].module) + replayTypeInfo(g, g.packed[i], FileIndex(i)) + + if g.backend == nil: + g.backend = cgendata.newModuleList(g) + registerInitProcs(BModuleList(g.backend), g.packed[i].module, g.packed[i].fromDisk.backendFlags) + proc generateCode*(g: ModuleGraph) = ## The single entry point, generate C(++) code for the entire ## Nim program aka `ModuleGraph`. @@ -143,24 +164,11 @@ proc generateCode*(g: ModuleGraph) = setupBackendModule(g, g.packed[i]) # Second pass: Code generation. + let mainModuleIdx = g.config.projectMainIdx2.int + # We need to generate the main module last, because only then + # all init procs have been registered: for i in 0..high(g.packed): - # case statement here to enforce exhaustive checks. - case g.packed[i].status - of undefined: - discard "nothing to do" - of loading, stored: - assert false - of storing, outdated: - generateCodeForModule(g, g.packed[i], alive) - closeRodFile(g, g.packed[i].module) - storeAliveSyms(g.config, g.packed[i].module.position, alive) - of loaded: - if g.packed[i].loadedButAliveSetChanged: - generateCodeForModule(g, g.packed[i], alive) - else: - addFileToLink(g.config, g.packed[i].module) - replayTypeInfo(g, g.packed[i], FileIndex(i)) - - if g.backend == nil: - g.backend = cgendata.newModuleList(g) - registerInitProcs(BModuleList(g.backend), g.packed[i].module, g.packed[i].fromDisk.backendFlags) + if i != mainModuleIdx: + genPackedModule(g, i, alive) + if mainModuleIdx >= 0: + genPackedModule(g, mainModuleIdx, alive) From 6916faf83c8af609304bde9d2ea2ed62c514cbf1 Mon Sep 17 00:00:00 2001 From: Dankr4d Date: Sun, 18 Apr 2021 23:39:05 +0200 Subject: [PATCH 1958/3768] Fix #17755 (#17766) Signed-off-by: Dankr4d --- lib/wrappers/openssl.nim | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim index 9c33386c5..1a8893957 100644 --- a/lib/wrappers/openssl.nim +++ b/lib/wrappers/openssl.nim @@ -52,14 +52,23 @@ when sslVersion != "": from posix import SocketHandle elif useWinVersion: - when not defined(nimOldDlls) and defined(cpu64): + when defined(openssl10) or defined(nimOldDlls): + when defined(cpu64): + const + DLLSSLName* = "(ssleay32|ssleay64).dll" + DLLUtilName* = "(libeay32|libeay64).dll" + else: + const + DLLSSLName* = "ssleay32.dll" + DLLUtilName* = "libeay32.dll" + elif defined(cpu64): const DLLSSLName* = "(libssl-1_1-x64|ssleay64|libssl64).dll" DLLUtilName* = "(libcrypto-1_1-x64|libeay64).dll" else: const DLLSSLName* = "(libssl-1_1|ssleay32|libssl32).dll" - DLLUtilName* = "(libcrypto-1_1|libeay32).dll" + DLLUtilName* = "(libcrypto-1_1|libeay32).dll" from winlean import SocketHandle else: From d6c8efa5d444f6849102dca192a199f12c8d55eb Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 18 Apr 2021 15:03:24 -0700 Subject: [PATCH 1959/3768] declaredlocs followup: handle `cannot instantiate` errors (#17745) --- compiler/semtypes.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index f7c42e638..8be210067 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1463,9 +1463,9 @@ proc semGeneric(c: PContext, n: PNode, s: PSym, prev: PType): PType = matches(c, n, copyTree(n), m) if m.state != csMatch: - let err = "cannot instantiate " & typeToString(t) & "\n" & - "got: <" & describeArgs(c, n) & ">\n" & - "but expected: <" & describeArgs(c, t.n, 0) & ">" + var err = "cannot instantiate " + err.addTypeHeader(c.config, t) + err.add "\ngot: <$1>\nbut expected: <$2>" % [describeArgs(c, n), describeArgs(c, t.n, 0)] localError(c.config, n.info, errGenerated, err) return newOrPrevType(tyError, prev, c) From 0a10af5a2ce27638c8298e96866a779928122269 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 18 Apr 2021 15:15:58 -0700 Subject: [PATCH 1960/3768] privateAccess now works with ref | ptr (#17760) --- compiler/semmagic.nim | 6 +-- compiler/suggest.nim | 4 +- lib/std/importutils.nim | 2 +- testament/lib/stdtest/testutils.nim | 20 +++++++ tests/importalls/mt1.nim | 4 +- tests/stdlib/mimportutils.nim | 17 ++++++ tests/stdlib/timportutils.nim | 82 +++++++++++++++++++++++++++++ tests/stdlib/ttestutils.nim | 29 +++++++--- 8 files changed, 149 insertions(+), 15 deletions(-) create mode 100644 tests/stdlib/mimportutils.nim create mode 100644 tests/stdlib/timportutils.nim diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index d8be85273..c6cf63767 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -578,9 +578,9 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, n[0].sym.magic = mSubU result = n of mPrivateAccess: - let sym = n[1].typ[0].sym - assert sym != nil - c.currentScope.allowPrivateAccess.add sym + var t = n[1].typ[0] + if t.kind in {tyRef, tyPtr}: t = t[0] + c.currentScope.allowPrivateAccess.add t.sym result = newNodeIT(nkEmpty, n.info, getSysType(c.graph, n.info, tyVoid)) else: result = n diff --git a/compiler/suggest.nim b/compiler/suggest.nim index 7a2c0d964..f3add0849 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -258,7 +258,9 @@ proc fieldVisible*(c: PContext, f: PSym): bool {.inline.} = for module in c.friendModules: if fmoduleId == module.id: return true if f.kind == skField: - let symObj = f.owner + var symObj = f.owner + if symObj.typ.kind in {tyRef, tyPtr}: + symObj = symObj.typ[0].sym for scope in allScopes(c.currentScope): for sym in scope.allowPrivateAccess: if symObj.id == sym.id: return true diff --git a/lib/std/importutils.nim b/lib/std/importutils.nim index 4efe0e140..f7a3cbd03 100644 --- a/lib/std/importutils.nim +++ b/lib/std/importutils.nim @@ -17,7 +17,7 @@ when defined(nimImportutilsExample): x1: int # private proc initFoo*(): auto = Foo() -proc privateAccess*(t: typedesc) {.magic: "PrivateAccess".} = +proc privateAccess*(t: typedesc[object|ref|ptr]) {.magic: "PrivateAccess".} = ## Enables access to private fields of `t` in current scope. runnableExamples("-d:nimImportutilsExample"): # here we're importing a module containing: diff --git a/testament/lib/stdtest/testutils.nim b/testament/lib/stdtest/testutils.nim index 35423de17..44048adbd 100644 --- a/testament/lib/stdtest/testutils.nim +++ b/testament/lib/stdtest/testutils.nim @@ -1,5 +1,6 @@ import std/private/miscdollars from std/os import getEnv +import std/[macros, genasts] template flakyAssert*(cond: untyped, msg = "", notifySuccess = true) = ## API to deal with flaky or failing tests. This avoids disabling entire tests @@ -89,3 +90,22 @@ template reject*(a) = template disableVm*(body) = when nimvm: discard else: body + +template typeOrVoid[T](a: T): type = + # FACTOR with asyncjs.typeOrVoid + T + +macro assertAll*(body) = + ## works in VM, unlike `check`, `require` + runnableExamples: + assertAll: + 1+1 == 2 + var a = @[1, 2] # statements work + a.len == 2 + # remove this once these support VM, pending #10129 (closed but not yet fixed) + result = newStmtList() + for a in body: + result.add genAst(a) do: + # better than: `when not compiles(typeof(a)):` + when typeOrVoid(a) is void: a + else: doAssert a diff --git a/tests/importalls/mt1.nim b/tests/importalls/mt1.nim index 87c4eff16..b7983945c 100644 --- a/tests/importalls/mt1.nim +++ b/tests/importalls/mt1.nim @@ -10,8 +10,8 @@ doAssert m.m3p1 == 2 ## field access import std/importutils privateAccess(Foo5) -# var x = Foo5(z1: "foo", z2: m.kg1) -# doAssert x.z1 == "foo" +var x = Foo5(z1: "foo", z2: m.kg1) +doAssert x.z1 == "foo" var f0: Foo5 f0.z3 = 3 diff --git a/tests/stdlib/mimportutils.nim b/tests/stdlib/mimportutils.nim new file mode 100644 index 000000000..d2b185cd3 --- /dev/null +++ b/tests/stdlib/mimportutils.nim @@ -0,0 +1,17 @@ +type + A* = object + a0*: int + ha1: float + B = object + b0*: int + hb1: float + C* = ref object + c0: int + hc1: float + D* = ptr object + d0: int + hd1: float + PA* = ref A + PtA* = ptr A + +proc initB*(): B = B() diff --git a/tests/stdlib/timportutils.nim b/tests/stdlib/timportutils.nim new file mode 100644 index 000000000..37e2b7102 --- /dev/null +++ b/tests/stdlib/timportutils.nim @@ -0,0 +1,82 @@ +import std/importutils +import stdtest/testutils +import mimportutils + +template main = + block: # privateAccess + assertAll: + var a: A + var b = initB() # B is private + compiles(a.a0) + compiles(b.b0) + not compiles(a.ha1) + not compiles(b.hb1) + + block: + assertAll: + privateAccess A + compiles(a.ha1) + a.ha1 == 0.0 + not compiles(a.hb1) + privateAccess b.typeof + b.hb1 = 3 + type B2 = b.typeof + let b2 = B2(b0: 4, hb1: 5) + b.hb1 == 3 + b2 == B2(b0: 4, hb1: 5) + + assertAll: + not compiles(a.ha1) + not compiles(b.hb1) + + block: + assertAll: + not compiles(C(c0: 1, hc1: 2)) + privateAccess C + let c = C(c0: 1, hc1: 2) + c.hc1 == 2 + + block: + assertAll: + privateAccess PA + var pa = PA(a0: 1, ha1: 2) + pa.ha1 == 2 + pa.ha1 = 3 + pa.ha1 == 3 + + block: + assertAll: + var a = A(a0: 1) + var a2 = a.addr + not compiles(a2.ha1) + privateAccess PtA + a2.type is PtA + a2.ha1 = 2 + a2.ha1 == 2 + a.ha1 = 3 + a2.ha1 == 3 + + block: + disableVm: + assertAll: + var a = A.create() + defer: dealloc(a) + a is PtA + a.typeof is PtA + not compiles(a.ha1) + privateAccess a.typeof + a.ha1 = 2 + a.ha1 == 2 + a[].ha1 = 3 + a.ha1 == 3 + + block: + disableVm: + assertAll: + var a = A.create() + defer: dealloc(a) + privateAccess PtA + a.ha1 == 0 + +static: main() +main() diff --git a/tests/stdlib/ttestutils.nim b/tests/stdlib/ttestutils.nim index 7e39c9ae3..0d789cd76 100644 --- a/tests/stdlib/ttestutils.nim +++ b/tests/stdlib/ttestutils.nim @@ -1,11 +1,24 @@ import stdtest/testutils -block: # greedyOrderedSubsetLines - doAssert greedyOrderedSubsetLines("a1\na3", "a0\na1\na2\na3\na4") - doAssert not greedyOrderedSubsetLines("a3\na1", "a0\na1\na2\na3\na4") # out of order - doAssert not greedyOrderedSubsetLines("a1\na5", "a0\na1\na2\na3\na4") # a5 not in lhs +block: # assertAll + assertAll: + 1+1 == 2 + var a = 3 + a == 3 - doAssert not greedyOrderedSubsetLines("a1\na5", "a0\na1\na2\na3\na4\nprefix:a5") - doAssert not greedyOrderedSubsetLines("a1\na5", "a0\na1\na2\na3\na4\na5:suffix") - doAssert not greedyOrderedSubsetLines("a5", "a0\na1\na2\na3\na4\nprefix:a5") - doAssert not greedyOrderedSubsetLines("a5", "a0\na1\na2\na3\na4\na5:suffix") + doAssertRaises(AssertionDefect): + assertAll: + 1+1 == 2 + var a = 3 + a == 4 + +block: # greedyOrderedSubsetLines + assertAll: + greedyOrderedSubsetLines("a1\na3", "a0\na1\na2\na3\na4") + not greedyOrderedSubsetLines("a3\na1", "a0\na1\na2\na3\na4") # out of order + not greedyOrderedSubsetLines("a1\na5", "a0\na1\na2\na3\na4") # a5 not in lhs + + not greedyOrderedSubsetLines("a1\na5", "a0\na1\na2\na3\na4\nprefix:a5") + not greedyOrderedSubsetLines("a1\na5", "a0\na1\na2\na3\na4\na5:suffix") + not greedyOrderedSubsetLines("a5", "a0\na1\na2\na3\na4\nprefix:a5") + not greedyOrderedSubsetLines("a5", "a0\na1\na2\na3\na4\na5:suffix") From 5042cb956be1cfc1dfb106d3dbc9d897e10f72d8 Mon Sep 17 00:00:00 2001 From: Saem Ghani Date: Sun, 18 Apr 2021 22:40:08 -0700 Subject: [PATCH 1961/3768] [ci skip] document compiler/ic/rodfiles.nim (#17771) * [ci skip] document compiler/ic/rodfiles.nim Why? * understand how rodfile module works and a bit of the format * leave notes behind for others * rather than Araq guess what others need, he can fix what other glean * possible model for making the compiler more aproachable Bonus: * might have found a minor bug in `loadSection` * Update compiler/ic/rodfiles.nim Co-authored-by: Andreas Rumpf --- compiler/ic/rodfiles.nim | 72 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/compiler/ic/rodfiles.nim b/compiler/ic/rodfiles.nim index 47362da0b..86d8ba203 100644 --- a/compiler/ic/rodfiles.nim +++ b/compiler/ic/rodfiles.nim @@ -7,8 +7,65 @@ # distribution, for details about the copyright. # +## Low level binary format used by the compiler to store and load various AST +## and related data. +## +## NB: this is incredibly low level and if you're interested in how the +## compiler works and less a storage format, you're probably looking for +## the `ic` or `packed_ast` modules to understand the logical format. + from typetraits import supportsCopyMem +## Overview +## ======== +## `RodFile` represents a Rod File (versioned binary format), and the +## associated data for common interactions such as IO and error tracking +## (`RodFileError`). The file format broken up into sections (`RodSection`) +## and preceeded by a header (see: `cookie`). The precise layout, section +## ordering and data following the section are determined by the user. See +## `ic.loadRoadFile`. +## +## A basic but "wrong" example of the lifecycle: +## --------------------------------------------- +## 1. `create` or `open` - create a new one or open an existing +## 2. `storeHeader` - header info +## 3. `storePrim` or `storeSeq` - save your stuff +## 4. `close` - and we're done +## +## Now read the bits below to understand what's missing. +## +## Issues with the Example +## ``````````````````````` +## Missing Sections: +## This is a low level API, so headers and sections need to be stored and +## loaded by the user, see `storeHeader` & `loadHeader` and `storeSection` & +## `loadSection`, respectively. +## +## No Error Handling: +## The API is centered around IO and prone to error, each operation checks or +## sets the `RodFile.err` field. A user of this API needs to handle these +## appropriately. +## +## API Notes +## ========= +## +## Valid inputs for Rod files +## -------------------------- +## ASTs, hopes, dreams, and anything as long as it and any children it may have +## support `copyMem`. This means anything that is not a pointer and that does not contain a pointer. At a glance these are: +## * string +## * objects & tuples (fields are recursed) +## * sequences AKA `seq[T]` +## +## Note on error handling style +## ---------------------------- +## A flag based approach is used where operations no-op in case of a +## preexisting error and set the flag if they encounter one. +## +## Misc +## ---- +## * 'Prim' is short for 'primitive', as in a non-sequence type + type RodSection* = enum versionSection @@ -60,6 +117,8 @@ proc setError(f: var RodFile; err: RodFileError) {.inline.} = #raise newException(IOError, "IO error") proc storePrim*(f: var RodFile; s: string) = + ## Stores a string. + ## The len is prefixed to allow for later retreival. if f.err != ok: return if s.len >= high(int32): setError f, tooBig @@ -73,6 +132,9 @@ proc storePrim*(f: var RodFile; s: string) = setError f, ioFailure proc storePrim*[T](f: var RodFile; x: T) = + ## Stores a non-sequence/string `T`. + ## If `T` doesn't support `copyMem` and is an object or tuple then the fields + ## are written -- the user from context will need to know which `T` to load. if f.err != ok: return when supportsCopyMem(T): if writeBuffer(f.f, unsafeAddr(x), sizeof(x)) != sizeof(x): @@ -90,6 +152,7 @@ proc storePrim*[T](f: var RodFile; x: T) = {.error: "unsupported type for 'storePrim'".} proc storeSeq*[T](f: var RodFile; s: seq[T]) = + ## Stores a sequence of `T`s, with the len as a prefix for later retrieval. if f.err != ok: return if s.len >= high(int32): setError f, tooBig @@ -102,6 +165,7 @@ proc storeSeq*[T](f: var RodFile; s: seq[T]) = storePrim(f, s[i]) proc loadPrim*(f: var RodFile; s: var string) = + ## Read a string, the length was stored as a prefix if f.err != ok: return var lenPrefix = int32(0) if readBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix): @@ -113,6 +177,7 @@ proc loadPrim*(f: var RodFile; s: var string) = setError f, ioFailure proc loadPrim*[T](f: var RodFile; x: var T) = + ## Load a non-sequence/string `T`. if f.err != ok: return when supportsCopyMem(T): if readBuffer(f.f, unsafeAddr(x), sizeof(x)) != sizeof(x): @@ -130,6 +195,7 @@ proc loadPrim*[T](f: var RodFile; x: var T) = {.error: "unsupported type for 'loadPrim'".} proc loadSeq*[T](f: var RodFile; s: var seq[T]) = + ## `T` must be compatible with `copyMem`, see `loadPrim` if f.err != ok: return var lenPrefix = int32(0) if readBuffer(f.f, addr lenPrefix, sizeof(lenPrefix)) != sizeof(lenPrefix): @@ -140,11 +206,13 @@ proc loadSeq*[T](f: var RodFile; s: var seq[T]) = loadPrim(f, s[i]) proc storeHeader*(f: var RodFile) = + ## stores the header which is described by `cookie`. if f.err != ok: return if f.f.writeBytes(cookie, 0, cookie.len) != cookie.len: setError f, ioFailure proc loadHeader*(f: var RodFile) = + ## Loads the header which is described by `cookie`. if f.err != ok: return var thisCookie: array[cookie.len, byte] if f.f.readBytes(thisCookie, 0, thisCookie.len) != thisCookie.len: @@ -153,12 +221,14 @@ proc loadHeader*(f: var RodFile) = setError f, wrongHeader proc storeSection*(f: var RodFile; s: RodSection) = + ## update `currentSection` and writes the bytes value of s. if f.err != ok: return assert f.currentSection < s f.currentSection = s storePrim(f, s) proc loadSection*(f: var RodFile; expected: RodSection) = + ## read the bytes value of s, sets and error if the section is incorrect. if f.err != ok: return var s: RodSection loadPrim(f, s) @@ -166,11 +236,13 @@ proc loadSection*(f: var RodFile; expected: RodSection) = setError f, wrongSection proc create*(filename: string): RodFile = + ## create the file and open it for writing if not open(result.f, filename, fmWrite): setError result, cannotOpen proc close*(f: var RodFile) = close(f.f) proc open*(filename: string): RodFile = + ## open the file for reading if not open(result.f, filename, fmRead): setError result, cannotOpen From 172a5ab7f82df503c1123a3bfa2f702416733858 Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 19 Apr 2021 13:41:50 +0800 Subject: [PATCH 1962/3768] [std/rlocks]add inline pragma (#17773) so that it can be used as the base of new locks module with destructors. --- lib/core/rlocks.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/core/rlocks.nim b/lib/core/rlocks.nim index f9224adb8..5f155262a 100644 --- a/lib/core/rlocks.nim +++ b/lib/core/rlocks.nim @@ -33,15 +33,15 @@ proc deinitRLock*(lock: var RLock) {.inline.} = ## Frees the resources associated with the lock. deinitSys(lock) -proc tryAcquire*(lock: var RLock): bool = +proc tryAcquire*(lock: var RLock): bool {.inline.} = ## Tries to acquire the given lock. Returns `true` on success. result = tryAcquireSys(lock) -proc acquire*(lock: var RLock) = +proc acquire*(lock: var RLock) {.inline.} = ## Acquires the given lock. acquireSys(lock) -proc release*(lock: var RLock) = +proc release*(lock: var RLock) {.inline.} = ## Releases the given lock. releaseSys(lock) From f8038af5ecfa20deaa0b9b1e0d8a5f5915dd9780 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Mon, 19 Apr 2021 02:42:38 -0300 Subject: [PATCH 1963/3768] Documentation only, dom (#17770) * ReSync with Devel * ReSync * https://github.com/timotheecour/Nim/issues/674 * Update lib/js/dom.nim Co-authored-by: flywind Co-authored-by: Andreas Rumpf Co-authored-by: flywind --- lib/js/dom.nim | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/js/dom.nim b/lib/js/dom.nim index a133f1f69..303cc178f 100644 --- a/lib/js/dom.nim +++ b/lib/js/dom.nim @@ -1496,7 +1496,9 @@ proc find*(w: Window, text: cstring, caseSensitive = false, backwards = false) proc focus*(w: Window) proc forward*(w: Window) -proc getComputedStyle*(w: Window, e: Node, pe:Node = nil): Style +proc getComputedStyle*(w: Window, e: Node, pe: Node = nil): Style + ## .. warning:: The returned Style may or may not be read-only at run-time in the browser. getComputedStyle is performance costly. + proc handleEvent*(w: Window, e: Event) proc home*(w: Window) proc moveBy*(w: Window, x, y: int) From cedbc7035d171d1535365c8acb889911b839ce99 Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 19 Apr 2021 16:33:56 +0800 Subject: [PATCH 1964/3768] [std/locks]remove workaround for withLock (#17772) Ref #6113 and #6049 The workaround for generics instantiation is unnecessary. It seems to be fixed by another PR I guess. The test still works. So the changes should be harmless. https://github.com/nim-lang/Nim/blob/devel/tests/stdlib/tlocks.nim I also add some inline pragmas. --- lib/core/locks.nim | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/core/locks.nim b/lib/core/locks.nim index 1540e2bcd..674817674 100644 --- a/lib/core/locks.nim +++ b/lib/core/locks.nim @@ -42,16 +42,16 @@ proc deinitLock*(lock: var Lock) {.inline.} = ## Frees the resources associated with the lock. deinitSys(lock) -proc tryAcquire*(lock: var Lock): bool = +proc tryAcquire*(lock: var Lock): bool {.inline.} = ## Tries to acquire the given lock. Returns `true` on success. result = tryAcquireSys(lock) -proc acquire*(lock: var Lock) = +proc acquire*(lock: var Lock) {.inline.} = ## Acquires the given lock. when not defined(js): acquireSys(lock) -proc release*(lock: var Lock) = +proc release*(lock: var Lock) {.inline.} = ## Releases the given lock. when not defined(js): releaseSys(lock) @@ -76,7 +76,6 @@ proc signal*(cond: var Cond) {.inline.} = template withLock*(a: Lock, body: untyped) = ## Acquires the given lock, executes the statements in body and ## releases the lock after the statements finish executing. - mixin acquire, release acquire(a) {.locks: [a].}: try: From dc89b212572fecd449dd469ffb2424c78941c12f Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 19 Apr 2021 16:51:13 +0800 Subject: [PATCH 1965/3768] [std/locks]close #7998(complete condition variables) (#17711) * close #7998 * workaround genode * Update lib/system/syslocks.nim --- lib/core/locks.nim | 9 +++-- lib/genode_cpp/syslocks.h | 5 +++ lib/system/syslocks.nim | 55 ++++++++++++++++++------------- lib/system/sysspawn.nim | 33 ++++++++----------- lib/system/threadlocalstorage.nim | 3 ++ 5 files changed, 60 insertions(+), 45 deletions(-) diff --git a/lib/core/locks.nim b/lib/core/locks.nim index 674817674..92967b9db 100644 --- a/lib/core/locks.nim +++ b/lib/core/locks.nim @@ -66,13 +66,18 @@ proc deinitCond*(cond: var Cond) {.inline.} = deinitSysCond(cond) proc wait*(cond: var Cond, lock: var Lock) {.inline.} = - ## waits on the condition variable `cond`. + ## Waits on the condition variable `cond`. waitSysCond(cond, lock) proc signal*(cond: var Cond) {.inline.} = - ## sends a signal to the condition variable `cond`. + ## Sends a signal to the condition variable `cond`. signalSysCond(cond) +proc broadcast*(cond: var Cond) {.inline.} = + ## Unblocks all threads currently blocked on the + ## specified condition variable `cond`. + broadcastSysCond(cond) + template withLock*(a: Lock, body: untyped) = ## Acquires the given lock, executes the statements in body and ## releases the lock after the statements finish executing. diff --git a/lib/genode_cpp/syslocks.h b/lib/genode_cpp/syslocks.h index c50180903..b5d5ae694 100644 --- a/lib/genode_cpp/syslocks.h +++ b/lib/genode_cpp/syslocks.h @@ -71,6 +71,11 @@ struct Nim::SysCond { _semaphore.up(); } + + void broadcastSysCond() + { + _semaphore.up(); + } }; #endif diff --git a/lib/system/syslocks.nim b/lib/system/syslocks.nim index 51dbfd3a2..ac52d9a8c 100644 --- a/lib/system/syslocks.nim +++ b/lib/system/syslocks.nim @@ -24,7 +24,9 @@ when defined(windows): LockSemaphore: int SpinCount: int - SysCond = Handle + RTL_CONDITION_VARIABLE {.importc: "RTL_CONDITION_VARIABLE", header: "synchapi.h".} = object + thePtr {.importc: "ptr".} : Handle + SysCond = RTL_CONDITION_VARIABLE proc initSysLock(L: var SysLock) {.importc: "InitializeCriticalSection", header: "".} @@ -48,30 +50,29 @@ when defined(windows): proc deinitSys(L: var SysLock) {.importc: "DeleteCriticalSection", header: "".} - proc createEvent(lpEventAttributes: pointer, - bManualReset, bInitialState: int32, - lpName: cstring): SysCond {.stdcall, noSideEffect, - dynlib: "kernel32", importc: "CreateEventA".} + proc initializeConditionVariable( + conditionVariable: var SysCond + ) {.stdcall, noSideEffect, dynlib: "kernel32", importc: "InitializeConditionVariable".} - proc closeHandle(hObject: Handle) {.stdcall, noSideEffect, - dynlib: "kernel32", importc: "CloseHandle".} - proc waitForSingleObject(hHandle: Handle, dwMilliseconds: int32): int32 {. - stdcall, dynlib: "kernel32", importc: "WaitForSingleObject", noSideEffect.} + proc sleepConditionVariableCS( + conditionVariable: var SysCond, + PCRITICAL_SECTION: var SysLock, + dwMilliseconds: int + ): int32 {.stdcall, noSideEffect, dynlib: "kernel32", importc: "SleepConditionVariableCS".} - proc signalSysCond(hEvent: SysCond) {.stdcall, noSideEffect, - dynlib: "kernel32", importc: "SetEvent".} + + proc signalSysCond(hEvent: var SysCond) {.stdcall, noSideEffect, + dynlib: "kernel32", importc: "WakeConditionVariable".} + + proc broadcastSysCond(hEvent: var SysCond) {.stdcall, noSideEffect, + dynlib: "kernel32", importc: "WakeAllConditionVariable".} proc initSysCond(cond: var SysCond) {.inline.} = - cond = createEvent(nil, 0'i32, 0'i32, nil) + initializeConditionVariable(cond) proc deinitSysCond(cond: var SysCond) {.inline.} = - closeHandle(cond) + discard proc waitSysCond(cond: var SysCond, lock: var SysLock) = - releaseSys(lock) - discard waitForSingleObject(cond, -1'i32) - acquireSys(lock) - - proc waitSysCondWindows(cond: var SysCond) = - discard waitForSingleObject(cond, -1'i32) + discard sleepConditionVariableCS(cond, lock, -1'i32) elif defined(genode): const @@ -94,6 +95,8 @@ elif defined(genode): noSideEffect, importcpp.} proc signalSysCond(cond: var SysCond) {. noSideEffect, importcpp.} + proc broadcastSysCond(cond: var SysCond) {. + noSideEffect, importcpp.} else: type @@ -181,7 +184,7 @@ else: releaseSysAux(L) when insideRLocksModule: - let SysLockType_Reentrant{.importc: "PTHREAD_MUTEX_RECURSIVE", + let SysLockType_Reentrant {.importc: "PTHREAD_MUTEX_RECURSIVE", header: "".}: SysLockType proc initSysLockAttr(a: var SysLockAttr) {. importc: "pthread_mutexattr_init", header: "", noSideEffect.} @@ -194,10 +197,12 @@ else: proc deinitSysCondAux(cond: var SysCondObj) {.noSideEffect, importc: "pthread_cond_destroy", header: "".} - proc waitSysCondAux(cond: var SysCondObj, lock: var SysLockObj) {. + proc waitSysCondAux(cond: var SysCondObj, lock: var SysLockObj): cint {. importc: "pthread_cond_wait", header: "", noSideEffect.} proc signalSysCondAux(cond: var SysCondObj) {. importc: "pthread_cond_signal", header: "", noSideEffect.} + proc broadcastSysCondAux(cond: var SysCondObj) {. + importc: "pthread_cond_broadcast", header: "", noSideEffect.} when defined(ios): proc initSysCond(cond: var SysCond, cond_attr: ptr SysCondAttr = nil) = @@ -209,9 +214,11 @@ else: c_free(cond) template waitSysCond(cond: var SysCond, lock: var SysLock) = - waitSysCondAux(cond[], lock[]) + discard waitSysCondAux(cond[], lock[]) template signalSysCond(cond: var SysCond) = signalSysCondAux(cond[]) + template broadcastSysCond(cond: var SysCond) = + broadcastSysCondAux(cond[]) else: template initSysCond(cond: var SysCond, cond_attr: ptr SysCondAttr = nil) = initSysCondAux(cond, cond_attr) @@ -219,8 +226,10 @@ else: deinitSysCondAux(cond) template waitSysCond(cond: var SysCond, lock: var SysLock) = - waitSysCondAux(cond, lock) + discard waitSysCondAux(cond, lock) template signalSysCond(cond: var SysCond) = signalSysCondAux(cond) + template broadcastSysCond(cond: var SysCond) = + broadcastSysCondAux(cond) {.pop.} diff --git a/lib/system/sysspawn.nim b/lib/system/sysspawn.nim index d1f5803f4..c9b9a68f0 100644 --- a/lib/system/sysspawn.nim +++ b/lib/system/sysspawn.nim @@ -20,34 +20,28 @@ when not declared(NimString): type CondVar = object c: SysCond - when defined(posix): - stupidLock: SysLock - counter: int + stupidLock: SysLock + counter: int proc createCondVar(): CondVar = initSysCond(result.c) - when defined(posix): - initSysLock(result.stupidLock) - #acquireSys(result.stupidLock) + initSysLock(result.stupidLock) + #acquireSys(result.stupidLock) proc destroyCondVar(c: var CondVar) {.inline.} = deinitSysCond(c.c) proc await(cv: var CondVar) = - when defined(posix): - acquireSys(cv.stupidLock) - while cv.counter <= 0: - waitSysCond(cv.c, cv.stupidLock) - dec cv.counter - releaseSys(cv.stupidLock) - else: - waitSysCondWindows(cv.c) + acquireSys(cv.stupidLock) + while cv.counter <= 0: + waitSysCond(cv.c, cv.stupidLock) + dec cv.counter + releaseSys(cv.stupidLock) proc signal(cv: var CondVar) = - when defined(posix): - acquireSys(cv.stupidLock) - inc cv.counter - releaseSys(cv.stupidLock) + acquireSys(cv.stupidLock) + inc cv.counter + releaseSys(cv.stupidLock) signalSysCond(cv.c) type @@ -57,8 +51,7 @@ type proc createFastCondVar(): FastCondVar = initSysCond(result.slow.c) - when defined(posix): - initSysLock(result.slow.stupidLock) + initSysLock(result.slow.stupidLock) #acquireSys(result.slow.stupidLock) result.event = false result.slowPath = false diff --git a/lib/system/threadlocalstorage.nim b/lib/system/threadlocalstorage.nim index cbb74c7df..30962ef77 100644 --- a/lib/system/threadlocalstorage.nim +++ b/lib/system/threadlocalstorage.nim @@ -17,6 +17,9 @@ when defined(windows): proc winResumeThread(hThread: SysThread): int32 {. stdcall, dynlib: "kernel32", importc: "ResumeThread".} + proc waitForSingleObject(hHandle: SysThread, dwMilliseconds: int32): int32 {. + stdcall, dynlib: "kernel32", importc: "WaitForSingleObject".} + proc waitForMultipleObjects(nCount: int32, lpHandles: ptr SysThread, bWaitAll: int32, From 83fa0fc843a5757c6fa1af9ac89b85b71c0c7581 Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 19 Apr 2021 17:21:35 +0800 Subject: [PATCH 1966/3768] fix #12521(type alias for openArray) (#17593) * fix nim js cmp fails at CT * fix #12521 * address comments --- compiler/ccgtypes.nim | 2 +- tests/openarray/topenarray.nim | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 3d4619764..fa58b5651 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -453,7 +453,7 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope, params.add(~"NIM_NOALIAS ") params.add(param.loc.r) # declare the len field for open arrays: - var arr = param.typ + var arr = param.typ.skipTypes({tyGenericInst}) if arr.kind in {tyVar, tyLent, tySink}: arr = arr.lastSon var j = 0 while arr.kind in {tyOpenArray, tyVarargs}: diff --git a/tests/openarray/topenarray.nim b/tests/openarray/topenarray.nim index 0b6e3afc4..aec5a4cbf 100644 --- a/tests/openarray/topenarray.nim +++ b/tests/openarray/topenarray.nim @@ -23,5 +23,31 @@ proc main = when defined(js): discard # xxx bug #15952: `a` left unchanged else: doAssert a == [1, 0, 10, 20, 5] + block: # bug #12521 + block: + type slice[T] = openArray[T] + + # Proc using that alias + proc testing(sl: slice[int]): seq[int] = + for item in sl: + result.add item + + let mySeq = @[1, 2, 3, 4, 5, 6, 7, 8, 9] + doAssert testing(mySeq) == mySeq + doAssert testing(mySeq[2..^2]) == mySeq[2..^2] + + block: + type slice = openArray[int] + + # Proc using that alias + proc testing(sl: slice): seq[int] = + for item in sl: + result.add item + + let mySeq = @[1, 2, 3, 4, 5, 6, 7, 8, 9] + doAssert testing(mySeq) == mySeq + doAssert testing(mySeq[2..^2]) == mySeq[2..^2] + + main() # static: main() # xxx bug #15952: Error: cannot generate code for: mSlice From 4b0b536419a500dc2e5a987ed9c2a374e0848008 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 19 Apr 2021 11:48:06 +0200 Subject: [PATCH 1967/3768] ic refactoring (#17778) * minor IC documentation update * IC: refactoring: removed the 'shared' type and fields, these were a leftover from an earlier design --- compiler/ic/dce.nim | 18 ++-- compiler/ic/design.rst | 8 +- compiler/ic/ic.nim | 175 +++++++++++++++++++++++-------------- compiler/ic/integrity.nim | 26 +++--- compiler/ic/navigator.nim | 12 +-- compiler/ic/packed_ast.nim | 56 ------------ compiler/ic/rodfiles.nim | 10 +-- 7 files changed, 146 insertions(+), 159 deletions(-) diff --git a/compiler/ic/dce.nim b/compiler/ic/dce.nim index 0918fc379..48ed414e2 100644 --- a/compiler/ic/dce.nim +++ b/compiler/ic/dce.nim @@ -27,7 +27,7 @@ type proc isExportedToC(c: var AliveContext; g: PackedModuleGraph; symId: int32): bool = ## "Exported to C" procs are special (these are marked with '.exportc') because these ## must not be optimized away! - let symPtr = addr g[c.thisModule].fromDisk.sh.syms[symId] + let symPtr = unsafeAddr g[c.thisModule].fromDisk.syms[symId] let flags = symPtr.flags # due to a bug/limitation in the lambda lifting, unused inner procs # are not transformed correctly; issue (#411). However, the whole purpose here @@ -39,7 +39,7 @@ proc isExportedToC(c: var AliveContext; g: PackedModuleGraph; symId: int32): boo # XXX: This used to be a condition to: # (sfExportc in prc.flags and lfExportLib in prc.loc.flags) or if sfCompilerProc in flags: - c.compilerProcs[g[c.thisModule].fromDisk.sh.strings[symPtr.name]] = (c.thisModule, symId) + c.compilerProcs[g[c.thisModule].fromDisk.strings[symPtr.name]] = (c.thisModule, symId) template isNotGeneric(n: NodePos): bool = ithSon(tree, n, genericParamsPos).kind == nkEmpty @@ -47,17 +47,17 @@ proc followLater(c: var AliveContext; g: PackedModuleGraph; module: int; item: i ## Marks a symbol 'item' as used and later in 'followNow' the symbol's body will ## be analysed. if not c.alive[module].containsOrIncl(item): - var body = g[module].fromDisk.sh.syms[item].ast + var body = g[module].fromDisk.syms[item].ast if body != emptyNodeId: - let opt = g[module].fromDisk.sh.syms[item].options - if g[module].fromDisk.sh.syms[item].kind in routineKinds: + let opt = g[module].fromDisk.syms[item].options + if g[module].fromDisk.syms[item].kind in routineKinds: body = NodeId ithSon(g[module].fromDisk.bodies, NodePos body, bodyPos) c.stack.add((module, opt, NodePos(body))) when false: - let nid = g[module].fromDisk.sh.syms[item].name + let nid = g[module].fromDisk.syms[item].name if nid != LitId(0): - let name = g[module].fromDisk.sh.strings[nid] + let name = g[module].fromDisk.strings[nid] if name in ["nimFrame", "callDepthLimitReached"]: echo "I was called! ", name, " body exists: ", body != emptyNodeId, " ", module, " ", item @@ -66,12 +66,12 @@ proc requestCompilerProc(c: var AliveContext; g: PackedModuleGraph; name: string followLater(c, g, module, item) proc loadTypeKind(t: PackedItemId; c: AliveContext; g: PackedModuleGraph; toSkip: set[TTypeKind]): TTypeKind = - template kind(t: ItemId): TTypeKind = g[t.module].fromDisk.sh.types[t.item].kind + template kind(t: ItemId): TTypeKind = g[t.module].fromDisk.types[t.item].kind var t2 = translateId(t, g, c.thisModule, c.decoder.config) result = t2.kind while result in toSkip: - t2 = translateId(g[t2.module].fromDisk.sh.types[t2.item].types[^1], g, t2.module, c.decoder.config) + t2 = translateId(g[t2.module].fromDisk.types[t2.item].types[^1], g, t2.module, c.decoder.config) result = t2.kind proc rangeCheckAnalysis(c: var AliveContext; g: PackedModuleGraph; tree: PackedTree; n: NodePos) = diff --git a/compiler/ic/design.rst b/compiler/ic/design.rst index d8e1315b1..b096e3103 100644 --- a/compiler/ic/design.rst +++ b/compiler/ic/design.rst @@ -7,12 +7,8 @@ The frontend produces a set of `.rod` files. Every `.nim` module produces its own `.rod` file. - The IR must be a faithful representation of the AST in memory. -- The backend can do its own caching but doesn't have to. -- We know by comparing 'nim check compiler/nim' against 'nim c compiler/nim' - that 2/3 of the compiler's runtime is spent in the frontend. Hence we - implement IC for the frontend first and only later for the backend. The - backend will recompile everything until we implement its own caching - mechanisms. +- The backend can do its own caching but doesn't have to. In the + current implementation the backend also caches its results. Advantage of the "set of files" vs the previous global database: - By construction, we either read from the `.rod` file or from the diff --git a/compiler/ic/ic.nim b/compiler/ic/ic.nim index 6f208a2a4..a23685be7 100644 --- a/compiler/ic/ic.nim +++ b/compiler/ic/ic.nim @@ -51,7 +51,12 @@ type emittedTypeInfo*: seq[string] backendFlags*: set[ModuleBackendFlag] - sh*: Shared + syms*: seq[PackedSym] + types*: seq[PackedType] + strings*: BiTable[string] # we could share these between modules. + numbers*: BiTable[BiggestInt] # we also store floats in here so + # that we can assure that every bit is kept + cfg: PackedConfig PackedEncoder* = object @@ -66,6 +71,50 @@ type symMarker*: IntSet #Table[ItemId, SymId] # ItemId.item -> SymId config*: ConfigRef +proc toString*(tree: PackedTree; n: NodePos; m: PackedModule; nesting: int; + result: var string) = + let pos = n.int + if result.len > 0 and result[^1] notin {' ', '\n'}: + result.add ' ' + + result.add $tree[pos].kind + case tree.nodes[pos].kind + of nkNone, nkEmpty, nkNilLit, nkType: discard + of nkIdent, nkStrLit..nkTripleStrLit: + result.add " " + result.add m.strings[LitId tree.nodes[pos].operand] + of nkSym: + result.add " " + result.add m.strings[m.syms[tree.nodes[pos].operand].name] + of directIntLit: + result.add " " + result.addInt tree.nodes[pos].operand + of externSIntLit: + result.add " " + result.addInt m.numbers[LitId tree.nodes[pos].operand] + of externUIntLit: + result.add " " + result.add $cast[uint64](m.numbers[LitId tree.nodes[pos].operand]) + of nkFloatLit..nkFloat128Lit: + result.add " " + result.add $cast[BiggestFloat](m.numbers[LitId tree.nodes[pos].operand]) + else: + result.add "(\n" + for i in 1..(nesting+1)*2: result.add ' ' + for child in sonsReadonly(tree, n): + toString(tree, child, m, nesting + 1, result) + result.add "\n" + for i in 1..nesting*2: result.add ' ' + result.add ")" + #for i in 1..nesting*2: result.add ' ' + +proc toString*(tree: PackedTree; n: NodePos; m: PackedModule): string = + result = "" + toString(tree, n, m, 0, result) + +proc debug*(tree: PackedTree; m: PackedModule) = + stdout.write toString(tree, NodePos 0, m) + proc isActive*(e: PackedEncoder): bool = e.config != nil proc disable(e: var PackedEncoder) = e.config = nil @@ -134,14 +183,14 @@ proc toLitId(x: FileIndex; c: var PackedEncoder; m: var PackedModule): LitId = result = c.filenames.getOrDefault(x) if result == LitId(0): let p = msgs.toFullPath(c.config, x) - result = getOrIncl(m.sh.strings, p) + result = getOrIncl(m.strings, p) c.filenames[x] = result c.lastFile = x c.lastLit = result assert result != LitId(0) proc toFileIndex*(x: LitId; m: PackedModule; config: ConfigRef): FileIndex = - result = msgs.fileInfoIdx(config, AbsoluteFile m.sh.strings[x]) + result = msgs.fileInfoIdx(config, AbsoluteFile m.strings[x]) proc includesIdentical(m: var PackedModule; config: ConfigRef): bool = for it in mitems(m.includes): @@ -151,7 +200,6 @@ proc includesIdentical(m: var PackedModule; config: ConfigRef): bool = proc initEncoder*(c: var PackedEncoder; m: var PackedModule; moduleSym: PSym; config: ConfigRef; pc: PackedConfig) = ## setup a context for serializing to packed ast - m.sh = Shared() c.thisModule = moduleSym.itemId.module c.config = config m.moduleFlags = moduleSym.flags @@ -179,11 +227,11 @@ proc addImportFileDep*(c: var PackedEncoder; m: var PackedModule; f: FileIndex) m.imports.add toLitId(f, c, m) proc addHidden*(c: var PackedEncoder; m: var PackedModule; s: PSym) = - let nameId = getOrIncl(m.sh.strings, s.name.s) + let nameId = getOrIncl(m.strings, s.name.s) m.hidden.add((nameId, s.itemId.item)) proc addExported*(c: var PackedEncoder; m: var PackedModule; s: PSym) = - let nameId = getOrIncl(m.sh.strings, s.name.s) + let nameId = getOrIncl(m.strings, s.name.s) m.exports.add((nameId, s.itemId.item)) proc addConverter*(c: var PackedEncoder; m: var PackedModule; s: PSym) = @@ -201,12 +249,12 @@ proc addMethod*(c: var PackedEncoder; m: var PackedModule; s: PSym) = m.methods.add s.itemId.item proc addReexport*(c: var PackedEncoder; m: var PackedModule; s: PSym) = - let nameId = getOrIncl(m.sh.strings, s.name.s) + let nameId = getOrIncl(m.strings, s.name.s) m.reexports.add((nameId, PackedItemId(module: toLitId(s.itemId.module.FileIndex, c, m), item: s.itemId.item))) proc addCompilerProc*(c: var PackedEncoder; m: var PackedModule; s: PSym) = - let nameId = getOrIncl(m.sh.strings, s.name.s) + let nameId = getOrIncl(m.strings, s.name.s) m.compilerProcs.add((nameId, s.itemId.item)) proc toPackedNode*(n: PNode; ir: var PackedTree; c: var PackedEncoder; m: var PackedModule) @@ -225,11 +273,11 @@ proc flush(c: var PackedEncoder; m: var PackedModule) = proc toLitId(x: string; m: var PackedModule): LitId = ## store a string as a literal - result = getOrIncl(m.sh.strings, x) + result = getOrIncl(m.strings, x) proc toLitId(x: BiggestInt; m: var PackedModule): LitId = ## store an integer as a literal - result = getOrIncl(m.sh.numbers, x) + result = getOrIncl(m.numbers, x) proc toPackedInfo(x: TLineInfo; c: var PackedEncoder; m: var PackedModule): PackedLineInfo = PackedLineInfo(line: x.line, col: x.col, file: toLitId(x.fileIndex, c, m)) @@ -299,8 +347,8 @@ proc storeType(t: PType; c: var PackedEncoder; m: var PackedModule): PackedItemI result = PackedItemId(module: toLitId(t.uniqueId.module.FileIndex, c, m), item: t.uniqueId.item) if t.uniqueId.module == c.thisModule and not c.typeMarker.containsOrIncl(t.uniqueId.item): - if t.uniqueId.item >= m.sh.types.len: - setLen m.sh.types, t.uniqueId.item+1 + if t.uniqueId.item >= m.types.len: + setLen m.types, t.uniqueId.item+1 var p = PackedType(kind: t.kind, flags: t.flags, callConv: t.callConv, size: t.size, align: t.align, nonUniqueId: t.itemId.item, @@ -315,7 +363,7 @@ proc storeType(t: PType; c: var PackedEncoder; m: var PackedModule): PackedItemI p.owner = t.owner.safeItemId(c, m) # fill the reserved slot, nothing else: - m.sh.types[t.uniqueId.item] = p + m.types[t.uniqueId.item] = p proc toPackedLib(l: PLib; c: var PackedEncoder; m: var PackedModule): PackedLib = ## the plib hangs off the psym via the .annex field @@ -334,8 +382,8 @@ proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId result = PackedItemId(module: toLitId(s.itemId.module.FileIndex, c, m), item: s.itemId.item) if s.itemId.module == c.thisModule and not c.symMarker.containsOrIncl(s.itemId.item): - if s.itemId.item >= m.sh.syms.len: - setLen m.sh.syms, s.itemId.item+1 + if s.itemId.item >= m.syms.len: + setLen m.syms, s.itemId.item+1 assert sfForward notin s.flags @@ -363,7 +411,7 @@ proc storeSym*(s: PSym; c: var PackedEncoder; m: var PackedModule): PackedItemId p.cname = toLitId(s.cname, m) # fill the reserved slot, nothing else: - m.sh.syms[s.itemId.item] = p + m.syms[s.itemId.item] = p proc addModuleRef(n: PNode; ir: var PackedTree; c: var PackedEncoder; m: var PackedModule) = ## add a remote symbol reference to the tree @@ -387,7 +435,7 @@ proc toPackedNode*(n: PNode; ir: var PackedTree; c: var PackedEncoder; m: var Pa typeId: storeTypeLater(n.typ, c, m), info: info) of nkIdent: ir.nodes.add PackedNode(kind: n.kind, flags: n.flags, - operand: int32 getOrIncl(m.sh.strings, n.ident.s), + operand: int32 getOrIncl(m.strings, n.ident.s), typeId: storeTypeLater(n.typ, c, m), info: info) of nkSym: if n.sym.itemId.module == c.thisModule: @@ -405,15 +453,15 @@ proc toPackedNode*(n: PNode; ir: var PackedTree; c: var PackedEncoder; m: var Pa typeId: storeTypeLater(n.typ, c, m), info: info) of externIntLit: ir.nodes.add PackedNode(kind: n.kind, flags: n.flags, - operand: int32 getOrIncl(m.sh.numbers, n.intVal), + operand: int32 getOrIncl(m.numbers, n.intVal), typeId: storeTypeLater(n.typ, c, m), info: info) of nkStrLit..nkTripleStrLit: ir.nodes.add PackedNode(kind: n.kind, flags: n.flags, - operand: int32 getOrIncl(m.sh.strings, n.strVal), + operand: int32 getOrIncl(m.strings, n.strVal), typeId: storeTypeLater(n.typ, c, m), info: info) of nkFloatLit..nkFloat128Lit: ir.nodes.add PackedNode(kind: n.kind, flags: n.flags, - operand: int32 getOrIncl(m.sh.numbers, cast[BiggestInt](n.floatVal)), + operand: int32 getOrIncl(m.numbers, cast[BiggestInt](n.floatVal)), typeId: storeTypeLater(n.typ, c, m), info: info) else: let patchPos = ir.prepare(n.kind, n.flags, @@ -499,7 +547,6 @@ proc loadError(err: RodFileError; filename: AbsoluteFile; config: ConfigRef;) = proc loadRodFile*(filename: AbsoluteFile; m: var PackedModule; config: ConfigRef; ignoreConfig = false): RodFileError = - m.sh = Shared() var f = rodfiles.open(filename.string) f.loadHeader() f.loadSection configSection @@ -519,7 +566,7 @@ proc loadRodFile*(filename: AbsoluteFile; m: var PackedModule; config: ConfigRef f.loadSection section f.load data - loadTabSection stringsSection, m.sh.strings + loadTabSection stringsSection, m.strings loadSeqSection checkSumsSection, m.includes if not includesIdentical(m, config): @@ -527,7 +574,7 @@ proc loadRodFile*(filename: AbsoluteFile; m: var PackedModule; config: ConfigRef loadSeqSection depsSection, m.imports - loadTabSection numbersSection, m.sh.numbers + loadTabSection numbersSection, m.numbers loadSeqSection exportsSection, m.exports loadSeqSection hiddenSection, m.hidden @@ -545,8 +592,8 @@ proc loadRodFile*(filename: AbsoluteFile; m: var PackedModule; config: ConfigRef loadSeqSection toReplaySection, m.toReplay.nodes loadSeqSection topLevelSection, m.topLevel.nodes loadSeqSection bodiesSection, m.bodies.nodes - loadSeqSection symsSection, m.sh.syms - loadSeqSection typesSection, m.sh.types + loadSeqSection symsSection, m.syms + loadSeqSection typesSection, m.types loadSeqSection typeInstCacheSection, m.typeInstCache loadSeqSection procInstCacheSection, m.procInstCache @@ -586,13 +633,13 @@ proc saveRodFile*(filename: AbsoluteFile; encoder: var PackedEncoder; m: var Pac f.storeSection section f.store data - storeTabSection stringsSection, m.sh.strings + storeTabSection stringsSection, m.strings storeSeqSection checkSumsSection, m.includes storeSeqSection depsSection, m.imports - storeTabSection numbersSection, m.sh.numbers + storeTabSection numbersSection, m.numbers storeSeqSection exportsSection, m.exports storeSeqSection hiddenSection, m.hidden @@ -610,9 +657,9 @@ proc saveRodFile*(filename: AbsoluteFile; encoder: var PackedEncoder; m: var Pac storeSeqSection topLevelSection, m.topLevel.nodes storeSeqSection bodiesSection, m.bodies.nodes - storeSeqSection symsSection, m.sh.syms + storeSeqSection symsSection, m.syms - storeSeqSection typesSection, m.sh.types + storeSeqSection typesSection, m.types storeSeqSection typeInstCacheSection, m.typeInstCache storeSeqSection procInstCacheSection, m.procInstCache @@ -700,17 +747,17 @@ proc loadNodes*(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; of nkEmpty, nkNilLit, nkType: discard of nkIdent: - result.ident = getIdent(c.cache, g[thisModule].fromDisk.sh.strings[n.litId]) + result.ident = getIdent(c.cache, g[thisModule].fromDisk.strings[n.litId]) of nkSym: result.sym = loadSym(c, g, thisModule, PackedItemId(module: LitId(0), item: tree.nodes[n.int].operand)) of directIntLit: result.intVal = tree.nodes[n.int].operand of externIntLit: - result.intVal = g[thisModule].fromDisk.sh.numbers[n.litId] + result.intVal = g[thisModule].fromDisk.numbers[n.litId] of nkStrLit..nkTripleStrLit: - result.strVal = g[thisModule].fromDisk.sh.strings[n.litId] + result.strVal = g[thisModule].fromDisk.strings[n.litId] of nkFloatLit..nkFloat128Lit: - result.floatVal = cast[BiggestFloat](g[thisModule].fromDisk.sh.numbers[n.litId]) + result.floatVal = cast[BiggestFloat](g[thisModule].fromDisk.numbers[n.litId]) of nkModuleRef: let (n1, n2) = sons2(tree, n) assert n1.kind == nkInt32Lit @@ -767,7 +814,7 @@ proc symHeaderFromPacked(c: var PackedDecoder; g: var PackedModuleGraph; options: s.options, position: if s.kind in {skForVar, skVar, skLet, skTemp}: 0 else: s.position, offset: if s.kind in routineKinds: defaultOffset else: s.offset, - name: getIdent(c.cache, g[si].fromDisk.sh.strings[s.name]) + name: getIdent(c.cache, g[si].fromDisk.strings[s.name]) ) template loadAstBody(p, field) = @@ -785,7 +832,7 @@ proc loadLib(c: var PackedDecoder; g: var PackedModuleGraph; result = nil else: result = PLib(generated: l.generated, isOverriden: l.isOverriden, - kind: l.kind, name: rope g[si].fromDisk.sh.strings[l.name]) + kind: l.kind, name: rope g[si].fromDisk.strings[l.name]) loadAstBody(l, path) proc symBodyFromPacked(c: var PackedDecoder; g: var PackedModuleGraph; @@ -798,14 +845,14 @@ proc symBodyFromPacked(c: var PackedDecoder; g: var PackedModuleGraph; loadAstBody(s, ast) result.annex = loadLib(c, g, si, item, s.annex) when hasFFI: - result.cname = g[si].fromDisk.sh.strings[s.cname] + result.cname = g[si].fromDisk.strings[s.cname] if s.kind in {skLet, skVar, skField, skForVar}: result.guard = loadSym(c, g, si, s.guard) result.bitsize = s.bitsize result.alignment = s.alignment result.owner = loadSym(c, g, si, s.owner) - let externalName = g[si].fromDisk.sh.strings[s.externalName] + let externalName = g[si].fromDisk.strings[s.externalName] if externalName != "": result.loc.r = rope externalName result.loc.flags = s.locFlags @@ -818,14 +865,14 @@ proc loadSym(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; s: assert g[si].status in {loaded, storing, stored} if not g[si].symsInit: g[si].symsInit = true - setLen g[si].syms, g[si].fromDisk.sh.syms.len + setLen g[si].syms, g[si].fromDisk.syms.len if g[si].syms[s.item] == nil: - if g[si].fromDisk.sh.syms[s.item].kind != skModule: - result = symHeaderFromPacked(c, g, g[si].fromDisk.sh.syms[s.item], si, s.item) + if g[si].fromDisk.syms[s.item].kind != skModule: + result = symHeaderFromPacked(c, g, g[si].fromDisk.syms[s.item], si, s.item) # store it here early on, so that recursions work properly: g[si].syms[s.item] = result - symBodyFromPacked(c, g, g[si].fromDisk.sh.syms[s.item], si, s.item, result) + symBodyFromPacked(c, g, g[si].fromDisk.syms[s.item], si, s.item, result) else: result = g[si].module assert result != nil @@ -866,13 +913,13 @@ proc loadType(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; t if not g[si].typesInit: g[si].typesInit = true - setLen g[si].types, g[si].fromDisk.sh.types.len + setLen g[si].types, g[si].fromDisk.types.len if g[si].types[t.item] == nil: - result = typeHeaderFromPacked(c, g, g[si].fromDisk.sh.types[t.item], si, t.item) + result = typeHeaderFromPacked(c, g, g[si].fromDisk.types[t.item], si, t.item) # store it here early on, so that recursions work properly: g[si].types[t.item] = result - typeBodyFromPacked(c, g, g[si].fromDisk.sh.types[t.item], si, t.item, result) + typeBodyFromPacked(c, g, g[si].fromDisk.types[t.item], si, t.item, result) else: result = g[si].types[t.item] assert result.itemId.item > 0 @@ -897,7 +944,7 @@ proc setupLookupTables(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCa let e2 = when e[1] is PackedItemId: e[1] else: PackedItemId(module: LitId(0), item: e[1]) - iface.mgetOrPut(cache.getIdent(m.fromDisk.sh.strings[nameLit]), @[]).add(e2) + iface.mgetOrPut(cache.getIdent(m.fromDisk.strings[nameLit]), @[]).add(e2) for e in m.fromDisk.exports: m.iface.impl(e) @@ -1000,7 +1047,7 @@ proc loadProcBody*(config: ConfigRef, cache: IdentCache; lastFile: FileIndex(-1), config: config, cache: cache) - let pos = g[mId].fromDisk.sh.syms[s.itemId.item].ast + let pos = g[mId].fromDisk.syms[s.itemId.item].ast assert pos != emptyNodeId result = loadProcBody(decoder, g, mId, g[mId].fromDisk.bodies, NodePos pos) @@ -1042,15 +1089,15 @@ proc translateId*(id: PackedItemId; g: PackedModuleGraph; thisModule: int; confi proc checkForHoles(m: PackedModule; config: ConfigRef; moduleId: int) = var bugs = 0 - for i in 1 .. high(m.sh.syms): - if m.sh.syms[i].kind == skUnknown: + for i in 1 .. high(m.syms): + if m.syms[i].kind == skUnknown: echo "EMPTY ID ", i, " module ", moduleId, " ", toFullPath(config, FileIndex(moduleId)) inc bugs assert bugs == 0 when false: var nones = 0 - for i in 1 .. high(m.sh.types): - inc nones, m.sh.types[i].kind == tyNone + for i in 1 .. high(m.types): + inc nones, m.types[i].kind == tyNone assert nones < 1 proc simulateLoadedModule*(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache; @@ -1133,13 +1180,13 @@ proc interfaceSymbol*(config: ConfigRef, cache: IdentCache; result = loadSym(decoder, g, int(module), values[0]) proc idgenFromLoadedModule*(m: LoadedModule): IdGenerator = - IdGenerator(module: m.module.itemId.module, symId: int32 m.fromDisk.sh.syms.len, - typeId: int32 m.fromDisk.sh.types.len) + IdGenerator(module: m.module.itemId.module, symId: int32 m.fromDisk.syms.len, + typeId: int32 m.fromDisk.types.len) proc searchForCompilerproc*(m: LoadedModule; name: string): int32 = # slow, linear search, but the results are cached: for it in items(m.fromDisk.compilerProcs): - if m.fromDisk.sh.strings[it[0]] == name: + if m.fromDisk.strings[it[0]] == name: return it[1] return -1 @@ -1149,31 +1196,31 @@ proc rodViewer*(rodfile: AbsoluteFile; config: ConfigRef, cache: IdentCache) = var m: PackedModule let err = loadRodFile(rodfile, m, config, ignoreConfig=true) if err != ok: - config.quitOrRaise "Error: could not load: " & $rodfile.string & " reason: " & $err + config.quitOrRaise "Error: could not load: " & $rodfile.string & " reason: " & $err when true: echo "exports:" for ex in m.exports: - echo " ", m.sh.strings[ex[0]], " local ID: ", ex[1] - assert ex[0] == m.sh.syms[ex[1]].name + echo " ", m.strings[ex[0]], " local ID: ", ex[1] + assert ex[0] == m.syms[ex[1]].name # ex[1] int32 echo "reexports:" for ex in m.reexports: - echo " ", m.sh.strings[ex[0]] + echo " ", m.strings[ex[0]] # reexports*: seq[(LitId, PackedItemId)] echo "hidden: " & $m.hidden.len for ex in m.hidden: - echo " ", m.sh.strings[ex[0]], " local ID: ", ex[1] + echo " ", m.strings[ex[0]], " local ID: ", ex[1] echo "all symbols" - for i in 0..high(m.sh.syms): - if m.sh.syms[i].name != LitId(0): - echo " ", m.sh.strings[m.sh.syms[i].name], " local ID: ", i, " kind ", m.sh.syms[i].kind + for i in 0..high(m.syms): + if m.syms[i].name != LitId(0): + echo " ", m.strings[m.syms[i].name], " local ID: ", i, " kind ", m.syms[i].kind else: - echo " local ID: ", i, " kind ", m.sh.syms[i].kind + echo " local ID: ", i, " kind ", m.syms[i].kind - echo "symbols: ", m.sh.syms.len, " types: ", m.sh.types.len, + echo "symbols: ", m.syms.len, " types: ", m.types.len, " top level nodes: ", m.topLevel.nodes.len, " other nodes: ", m.bodies.nodes.len, - " strings: ", m.sh.strings.len, " numbers: ", m.sh.numbers.len + " strings: ", m.strings.len, " numbers: ", m.numbers.len diff --git a/compiler/ic/integrity.nim b/compiler/ic/integrity.nim index a28f2066a..428c344a3 100644 --- a/compiler/ic/integrity.nim +++ b/compiler/ic/integrity.nim @@ -42,12 +42,12 @@ proc checkType(c: var CheckedContext; typeId: PackedItemId) = if not c.checkedTypes.containsOrIncl(itemId): let oldThisModule = c.thisModule c.thisModule = itemId.module - checkTypeObj c, c.g.packed[itemId.module].fromDisk.sh.types[itemId.item] + checkTypeObj c, c.g.packed[itemId.module].fromDisk.types[itemId.item] c.thisModule = oldThisModule proc checkSym(c: var CheckedContext; s: PackedSym) = if s.name != LitId(0): - assert c.g.packed[c.thisModule].fromDisk.sh.strings.hasLitId s.name + assert c.g.packed[c.thisModule].fromDisk.strings.hasLitId s.name checkType c, s.typ if s.ast != emptyNodeId: checkNode(c, c.g.packed[c.thisModule].fromDisk.bodies, NodePos s.ast) @@ -57,14 +57,14 @@ proc checkSym(c: var CheckedContext; s: PackedSym) = proc checkLocalSym(c: var CheckedContext; item: int32) = let itemId = ItemId(module: c.thisModule, item: item) if not c.checkedSyms.containsOrIncl(itemId): - checkSym c, c.g.packed[c.thisModule].fromDisk.sh.syms[item] + checkSym c, c.g.packed[c.thisModule].fromDisk.syms[item] proc checkForeignSym(c: var CheckedContext; symId: PackedItemId) = let itemId = translateId(symId, c.g.packed, c.thisModule, c.g.config) if not c.checkedSyms.containsOrIncl(itemId): let oldThisModule = c.thisModule c.thisModule = itemId.module - checkSym c, c.g.packed[itemId.module].fromDisk.sh.syms[itemId.item] + checkSym c, c.g.packed[itemId.module].fromDisk.syms[itemId.item] c.thisModule = oldThisModule proc checkNode(c: var CheckedContext; tree: PackedTree; n: NodePos) = @@ -74,15 +74,15 @@ proc checkNode(c: var CheckedContext; tree: PackedTree; n: NodePos) = of nkEmpty, nkNilLit, nkType, nkNilRodNode: discard of nkIdent: - assert c.g.packed[c.thisModule].fromDisk.sh.strings.hasLitId n.litId + assert c.g.packed[c.thisModule].fromDisk.strings.hasLitId n.litId of nkSym: checkLocalSym(c, tree.nodes[n.int].operand) of directIntLit: discard of externIntLit, nkFloatLit..nkFloat128Lit: - assert c.g.packed[c.thisModule].fromDisk.sh.numbers.hasLitId n.litId + assert c.g.packed[c.thisModule].fromDisk.numbers.hasLitId n.litId of nkStrLit..nkTripleStrLit: - assert c.g.packed[c.thisModule].fromDisk.sh.strings.hasLitId n.litId + assert c.g.packed[c.thisModule].fromDisk.strings.hasLitId n.litId of nkModuleRef: let (n1, n2) = sons2(tree, n) assert n1.kind == nkInt32Lit @@ -97,25 +97,25 @@ proc checkTree(c: var CheckedContext; t: PackedTree) = proc checkLocalSymIds(c: var CheckedContext; m: PackedModule; symIds: seq[int32]) = for symId in symIds: - assert symId >= 0 and symId < m.sh.syms.len, $symId & " " & $m.sh.syms.len + assert symId >= 0 and symId < m.syms.len, $symId & " " & $m.syms.len proc checkModule(c: var CheckedContext; m: PackedModule) = # We check that: # - Every symbol references existing types and symbols. # - Every tree node references existing types and symbols. - for i in 0..high(m.sh.syms): + for i in 0..high(m.syms): checkLocalSym c, int32(i) checkTree c, m.toReplay checkTree c, m.topLevel for e in m.exports: - assert e[1] >= 0 and e[1] < m.sh.syms.len - assert e[0] == m.sh.syms[e[1]].name + assert e[1] >= 0 and e[1] < m.syms.len + assert e[0] == m.syms[e[1]].name for e in m.compilerProcs: - assert e[1] >= 0 and e[1] < m.sh.syms.len - assert e[0] == m.sh.syms[e[1]].name + assert e[1] >= 0 and e[1] < m.syms.len + assert e[0] == m.syms[e[1]].name checkLocalSymIds c, m, m.converters checkLocalSymIds c, m, m.methods diff --git a/compiler/ic/navigator.nim b/compiler/ic/navigator.nim index b09275220..b1a237cf7 100644 --- a/compiler/ic/navigator.nim +++ b/compiler/ic/navigator.nim @@ -35,12 +35,12 @@ proc isTracked(current, trackPos: PackedLineInfo, tokenLen: int): bool = proc searchLocalSym(c: var NavContext; s: PackedSym; info: PackedLineInfo): bool = result = s.name != LitId(0) and - isTracked(info, c.trackPos, c.g.packed[c.thisModule].fromDisk.sh.strings[s.name].len) + isTracked(info, c.trackPos, c.g.packed[c.thisModule].fromDisk.strings[s.name].len) proc searchForeignSym(c: var NavContext; s: ItemId; info: PackedLineInfo): bool = - let name = c.g.packed[s.module].fromDisk.sh.syms[s.item].name + let name = c.g.packed[s.module].fromDisk.syms[s.item].name result = name != LitId(0) and - isTracked(info, c.trackPos, c.g.packed[s.module].fromDisk.sh.strings[name].len) + isTracked(info, c.trackPos, c.g.packed[s.module].fromDisk.strings[name].len) const EmptyItemId = ItemId(module: -1'i32, item: -1'i32) @@ -51,7 +51,7 @@ proc search(c: var NavContext; tree: PackedTree): ItemId = case tree.nodes[i].kind of nkSym: let item = tree.nodes[i].operand - if searchLocalSym(c, c.g.packed[c.thisModule].fromDisk.sh.syms[item], tree.nodes[i].info): + if searchLocalSym(c, c.g.packed[c.thisModule].fromDisk.syms[item], tree.nodes[i].info): return ItemId(module: c.thisModule, item: item) of nkModuleRef: if tree.nodes[i].info.line == c.trackPos.line and tree.nodes[i].info.file == c.trackPos.file: @@ -74,7 +74,7 @@ proc isDecl(tree: PackedTree; n: NodePos): bool = proc usage(c: var NavContext; info: PackedLineInfo; isDecl: bool) = var m = "" - var file = c.g.packed[c.thisModule].fromDisk.sh.strings[info.file] + var file = c.g.packed[c.thisModule].fromDisk.strings[info.file] if c.outputSep == ' ': file = os.extractFilename file toLocation(m, file, info.line.int, info.col.int + ColOffset) @@ -102,7 +102,7 @@ proc nav(g: ModuleGraph) = # translate the track position to a packed position: let unpacked = g.config.m.trackPos let mid = unpacked.fileIndex - let fileId = g.packed[int32 mid].fromDisk.sh.strings.getKeyId(toFullPath(g.config, mid)) + let fileId = g.packed[int32 mid].fromDisk.strings.getKeyId(toFullPath(g.config, mid)) if fileId == LitId(0): internalError(g.config, unpacked, "cannot find a valid file ID") diff --git a/compiler/ic/packed_ast.nim b/compiler/ic/packed_ast.nim index 0649b56ce..2700c8253 100644 --- a/compiler/ic/packed_ast.nim +++ b/compiler/ic/packed_ast.nim @@ -98,17 +98,6 @@ type PackedTree* = object ## usually represents a full Nim module nodes*: seq[PackedNode] - #sh*: Shared - - Shared* = ref object # shared between different versions of 'Module'. - # (though there is always exactly one valid - # version of a module) - syms*: seq[PackedSym] - types*: seq[PackedType] - strings*: BiTable[string] # we could share these between modules. - numbers*: BiTable[BiggestInt] # we also store floats in here so - # that we can assure that every bit is kept - #config*: ConfigRef PackedInstantiation* = object key*, sym*: PackedItemId @@ -374,51 +363,6 @@ const externUIntLit* = {nkUIntLit, nkUInt8Lit, nkUInt16Lit, nkUInt32Lit, nkUInt64Lit} directIntLit* = nkInt32Lit -proc toString*(tree: PackedTree; n: NodePos; sh: Shared; nesting: int; - result: var string) = - let pos = n.int - if result.len > 0 and result[^1] notin {' ', '\n'}: - result.add ' ' - - result.add $tree[pos].kind - case tree.nodes[pos].kind - of nkNone, nkEmpty, nkNilLit, nkType: discard - of nkIdent, nkStrLit..nkTripleStrLit: - result.add " " - result.add sh.strings[LitId tree.nodes[pos].operand] - of nkSym: - result.add " " - result.add sh.strings[sh.syms[tree.nodes[pos].operand].name] - of directIntLit: - result.add " " - result.addInt tree.nodes[pos].operand - of externSIntLit: - result.add " " - result.addInt sh.numbers[LitId tree.nodes[pos].operand] - of externUIntLit: - result.add " " - result.add $cast[uint64](sh.numbers[LitId tree.nodes[pos].operand]) - of nkFloatLit..nkFloat128Lit: - result.add " " - result.add $cast[BiggestFloat](sh.numbers[LitId tree.nodes[pos].operand]) - else: - result.add "(\n" - for i in 1..(nesting+1)*2: result.add ' ' - for child in sonsReadonly(tree, n): - toString(tree, child, sh, nesting + 1, result) - result.add "\n" - for i in 1..nesting*2: result.add ' ' - result.add ")" - #for i in 1..nesting*2: result.add ' ' - - -proc toString*(tree: PackedTree; n: NodePos; sh: Shared): string = - result = "" - toString(tree, n, sh, 0, result) - -proc debug*(tree: PackedTree; sh: Shared) = - stdout.write toString(tree, NodePos 0, sh) - when false: proc identIdImpl(tree: PackedTree; n: NodePos): LitId = if n.kind == nkIdent: diff --git a/compiler/ic/rodfiles.nim b/compiler/ic/rodfiles.nim index 86d8ba203..a52bd18b3 100644 --- a/compiler/ic/rodfiles.nim +++ b/compiler/ic/rodfiles.nim @@ -23,8 +23,8 @@ from typetraits import supportsCopyMem ## (`RodFileError`). The file format broken up into sections (`RodSection`) ## and preceeded by a header (see: `cookie`). The precise layout, section ## ordering and data following the section are determined by the user. See -## `ic.loadRoadFile`. -## +## `ic.loadRodFile`. +## ## A basic but "wrong" example of the lifecycle: ## --------------------------------------------- ## 1. `create` or `open` - create a new one or open an existing @@ -45,10 +45,10 @@ from typetraits import supportsCopyMem ## The API is centered around IO and prone to error, each operation checks or ## sets the `RodFile.err` field. A user of this API needs to handle these ## appropriately. -## +## ## API Notes ## ========= -## +## ## Valid inputs for Rod files ## -------------------------- ## ASTs, hopes, dreams, and anything as long as it and any children it may have @@ -61,7 +61,7 @@ from typetraits import supportsCopyMem ## ---------------------------- ## A flag based approach is used where operations no-op in case of a ## preexisting error and set the flag if they encounter one. -## +## ## Misc ## ---- ## * 'Prim' is short for 'primitive', as in a non-sequence type From bae7b5b7795ae8b429e50deba61430ae43a1750f Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 19 Apr 2021 17:48:26 +0800 Subject: [PATCH 1968/3768] fix #17732(forward args... to megatest) (#17780) --- testament/categories.nim | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/testament/categories.nim b/testament/categories.nim index 7b57adb8a..4c3a0b1f5 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -573,7 +573,7 @@ proc quoted(a: string): string = # todo: consider moving to system.nim result.addQuoted(a) -proc runJoinedTest(r: var TResults, cat: Category, testsDir: string) = +proc runJoinedTest(r: var TResults, cat: Category, testsDir: string, options: string) = ## returns a list of tests that have problems #[ xxx create a reusable megatest API after abstracting out testament specific code, @@ -619,7 +619,11 @@ proc runJoinedTest(r: var TResults, cat: Category, testsDir: string) = writeFile(megatestFile, megatest) let root = getCurrentDir() - let args = ["c", "--nimCache:" & outDir, "-d:testing", "-d:nimMegatest", "--listCmd", "--path:" & root, megatestFile] + + var args = @["c", "--nimCache:" & outDir, "-d:testing", "-d:nimMegatest", "--listCmd", + "--path:" & root] + args.add options.parseCmdLine + args.add megatestFile var (cmdLine, buf, exitCode) = execCmdEx2(command = compilerPrefix, args = args, input = "") if exitCode != 0: echo "$ " & cmdLine & "\n" & buf @@ -710,7 +714,7 @@ proc processCategory(r: var TResults, cat: Category, if not handled: case cat2 of "megatest": - runJoinedTest(r, cat, testsDir) + runJoinedTest(r, cat, testsDir, options) else: var testsRun = 0 var files: seq[string] From 6de5aa1971b17b0869470d7cdc886ef230103832 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Mon, 19 Apr 2021 13:16:10 +0200 Subject: [PATCH 1969/3768] Rename `=` to `=copy` in stdlib (#17781) --- lib/deprecated/pure/sharedstrings.nim | 2 +- lib/std/channels.nim | 2 +- lib/std/isolation.nim | 2 +- lib/system.nim | 1 + lib/system/widestrs.nim | 2 +- 5 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/deprecated/pure/sharedstrings.nim b/lib/deprecated/pure/sharedstrings.nim index 29cb0fcc2..4d4ae151b 100644 --- a/lib/deprecated/pure/sharedstrings.nim +++ b/lib/deprecated/pure/sharedstrings.nim @@ -37,7 +37,7 @@ proc `=destroy`*(s: SharedString) = decRef(s.buffer) when false: - proc `=`*(dest: var SharedString; src: SharedString) = + proc `=copy`*(dest: var SharedString; src: SharedString) = incRef(src.buffer) if not dest.buffer.isNil: decRef(dest.buffer) diff --git a/lib/std/channels.nim b/lib/std/channels.nim index 49def0913..5ff141384 100644 --- a/lib/std/channels.nim +++ b/lib/std/channels.nim @@ -443,7 +443,7 @@ proc `=destroy`*[T](c: var Channel[T]) = else: atomicDec(c.d.atomicCounter) -proc `=`*[T](dest: var Channel[T], src: Channel[T]) = +proc `=copy`*[T](dest: var Channel[T], src: Channel[T]) = ## Shares `Channel` by reference counting. if src.d != nil: atomicInc(src.d.atomicCounter) diff --git a/lib/std/isolation.nim b/lib/std/isolation.nim index 8daca233b..7d6ac6092 100644 --- a/lib/std/isolation.nim +++ b/lib/std/isolation.nim @@ -18,7 +18,7 @@ type Isolated*[T] = object ## Isolated data can only be moved, not copied. value: T -proc `=`*[T](dest: var Isolated[T]; src: Isolated[T]) {.error.} +proc `=copy`*[T](dest: var Isolated[T]; src: Isolated[T]) {.error.} proc `=sink`*[T](dest: var Isolated[T]; src: Isolated[T]) {.inline.} = # delegate to value's sink operation diff --git a/lib/system.nim b/lib/system.nim index 532883853..7fd35781b 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -478,6 +478,7 @@ proc `[]`*[I: Ordinal;T](a: T; i: I): T {. proc `[]=`*[I: Ordinal;T,S](a: T; i: I; x: sink S) {.noSideEffect, magic: "ArrPut".} proc `=`*[T](dest: var T; src: T) {.noSideEffect, magic: "Asgn".} +proc `=copy`*[T](dest: var T; src: T) {.noSideEffect, magic: "Asgn".} proc arrGet[I: Ordinal;T](a: T; i: I): T {. noSideEffect, magic: "ArrGet".} diff --git a/lib/system/widestrs.nim b/lib/system/widestrs.nim index 16bc3f3ca..a999c9167 100644 --- a/lib/system/widestrs.nim +++ b/lib/system/widestrs.nim @@ -32,7 +32,7 @@ when defined(nimv2): else: dealloc(a.data) - proc `=`(a: var WideCStringObj; b: WideCStringObj) {.error.} + proc `=copy`(a: var WideCStringObj; b: WideCStringObj) {.error.} proc `=sink`(a: var WideCStringObj; b: WideCStringObj) = a.bytes = b.bytes From 438afb4db93dd9fcc6e94995a56f616a9e3d7826 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Mon, 19 Apr 2021 17:19:21 +0200 Subject: [PATCH 1970/3768] Introduce localErrorNode (#17785) --- compiler/sem.nim | 3 +-- compiler/semdata.nim | 13 +++++++++++++ compiler/semexprs.nim | 6 ++---- compiler/semmagic.nim | 12 ++++-------- compiler/semobjconstr.nim | 6 ++---- compiler/semstmts.nim | 3 +-- compiler/vm.nim | 8 ++++---- 7 files changed, 27 insertions(+), 24 deletions(-) diff --git a/compiler/sem.nim b/compiler/sem.nim index ccc634b4d..4020fc4d6 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -296,8 +296,7 @@ proc fixupTypeAfterEval(c: PContext, evaluated, eOrig: PNode): PNode = result = evaluated let expectedType = eOrig.typ.skipTypes({tyStatic}) if hasCycle(result): - globalError(c.config, eOrig.info, "the resulting AST is cyclic and cannot be processed further") - result = errorNode(c, eOrig) + result = localErrorNode(c, eOrig, "the resulting AST is cyclic and cannot be processed further") else: semmacrosanity.annotateType(result, expectedType, c.config) else: diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 9e7258a69..354d4587d 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -501,6 +501,19 @@ proc errorNode*(c: PContext, n: PNode): PNode = result = newNodeI(nkEmpty, n.info) result.typ = errorType(c) +proc localErrorNode*(c: PContext, n: PNode, info: TLineInfo, msg: TMsgKind, arg: string): PNode = + localError(c.config, info, msg, arg) + result = errorNode(c, n) + +proc localErrorNode*(c: PContext, n: PNode, info: TLineInfo, arg: string): PNode = + localErrorNode(c, n, info, errGenerated, arg) + +proc localErrorNode*(c: PContext, n: PNode, msg: TMsgKind, arg: string): PNode = + localErrorNode(c, n, n.info, msg, arg) + +proc localErrorNode*(c: PContext, n: PNode, arg: string): PNode = + localErrorNode(c, n, n.info, errGenerated, arg) + proc fillTypeS*(dest: PType, kind: TTypeKind, c: PContext) = dest.kind = kind dest.owner = getCurrOwner(c) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 1582cc0fd..353831736 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1044,8 +1044,7 @@ proc buildEchoStmt(c: PContext, n: PNode): PNode = if e != nil: result.add(newSymNode(e)) else: - localError(c.config, n.info, "system needs: echo") - result.add(errorNode(c, n)) + result.add localErrorNode(c, n, "system needs: echo") result.add(n) result = semExpr(c, result) @@ -2621,8 +2620,7 @@ proc semTupleConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = # check if either everything or nothing is tyTypeDesc for i in 1.. high(TSymChoiceRule).int: - localError(c.config, n[2].info, errConstExprExpected) - return errorNode(c, n) + return localErrorNode(c, n, n[2].info, errConstExprExpected) let id = newIdentNode(getIdent(c.cache, sl.strVal), n.info) let s = qualifiedLookUp(c, id, {checkUndeclared}) @@ -253,12 +251,10 @@ proc semBindSym(c: PContext, n: PNode): PNode = proc opBindSym(c: PContext, scope: PScope, n: PNode, isMixin: int, info: PNode): PNode = if n.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit, nkIdent}: - localError(c.config, info.info, errStringOrIdentNodeExpected) - return errorNode(c, n) + return localErrorNode(c, n, info.info, errStringOrIdentNodeExpected) if isMixin < 0 or isMixin > high(TSymChoiceRule).int: - localError(c.config, info.info, errConstExprExpected) - return errorNode(c, n) + return localErrorNode(c, n, info.info, errConstExprExpected) let id = if n.kind == nkIdent: n else: newIdentNode(getIdent(c.cache, n.strVal), info.info) diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim index dea187ea7..63b4b9a75 100644 --- a/compiler/semobjconstr.nim +++ b/compiler/semobjconstr.nim @@ -379,8 +379,7 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = for child in n: result.add child if t == nil: - localError(c.config, n.info, errGenerated, "object constructor needs an object type") - return errorNode(c, result) + return localErrorNode(c, result, errGenerated, "object constructor needs an object type") t = skipTypes(t, {tyGenericInst, tyAlias, tySink, tyOwned}) if t.kind == tyRef: @@ -391,8 +390,7 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = # multiple times as long as they don't have closures. result.typ.flags.incl tfHasOwned if t.kind != tyObject: - localError(c.config, n.info, errGenerated, "object constructor needs an object type") - return errorNode(c, result) + return localErrorNode(c, result, errGenerated, "object constructor needs an object type") # Check if the object is fully initialized by recursively testing each # field (if this is a case object, initialized fields in two different diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index bedc5a7e5..a10047187 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -725,8 +725,7 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode = if n.len == 3: if n[0].kind == nkVarTuple: if n[0].len-1 != iterAfterVarLent.len: - localError(c.config, n[0].info, errWrongNumberOfVariables) - return errorNode(c, n) + return localErrorNode(c, n, n[0].info, errWrongNumberOfVariables) for i in 0.. Date: Mon, 19 Apr 2021 17:51:00 +0200 Subject: [PATCH 1971/3768] Small privateAccess improvement (#17786) --- lib/std/importutils.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/importutils.nim b/lib/std/importutils.nim index f7a3cbd03..0c0f546b9 100644 --- a/lib/std/importutils.nim +++ b/lib/std/importutils.nim @@ -17,7 +17,7 @@ when defined(nimImportutilsExample): x1: int # private proc initFoo*(): auto = Foo() -proc privateAccess*(t: typedesc[object|ref|ptr]) {.magic: "PrivateAccess".} = +proc privateAccess*(t: typedesc[object|(ref object)|(ptr object)]) {.magic: "PrivateAccess".} = ## Enables access to private fields of `t` in current scope. runnableExamples("-d:nimImportutilsExample"): # here we're importing a module containing: From 3b80f0dc8ef2bc9d2a981a7c92a6355dfc72b7ef Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 19 Apr 2021 22:37:09 +0200 Subject: [PATCH 1972/3768] IC navigator: added support for include files (#17784) * ic fixed navigator crash when track wrong/missed Also fixed an issue with getNimcacheDir not observing the outDir. * closer, but not sure how to test[skip ci][ci skip] * IC navigator: added support for include files * update * make posix happy via expandFilename * update Co-authored-by: Saem Ghani --- compiler/commands.nim | 69 +++++++++++++++++++++----------- compiler/ic/ic.nim | 2 +- compiler/ic/navigator.nim | 28 +++++++++++-- compiler/msgs.nim | 3 ++ compiler/options.nim | 5 ++- testament/categories.nim | 2 +- tests/navigator/minclude.nim | 2 + tests/navigator/tincludefile.nim | 29 ++++++++++++++ tests/navigator/tnav1.nim | 4 +- 9 files changed, 111 insertions(+), 33 deletions(-) create mode 100644 tests/navigator/minclude.nim create mode 100644 tests/navigator/tincludefile.nim diff --git a/compiler/commands.nim b/compiler/commands.nim index e918979fb..5d6e45fb5 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -28,7 +28,7 @@ bootSwitch(usedNoGC, defined(nogc), "--gc:none") import os, msgs, options, nversion, condsyms, strutils, extccomp, platform, wordrecg, parseutils, nimblecmd, parseopt, sequtils, lineinfos, - pathutils, strtabs + pathutils, strtabs, pathnorm from ast import eqTypeFlags, tfGcSafe, tfNoSideEffect @@ -359,31 +359,52 @@ proc processCfgPath(conf: ConfigRef; path: string, info: TLineInfo): AbsoluteDir const errInvalidNumber = "$1 is not a valid number" +proc makeAbsolute(s: string): AbsoluteFile = + if isAbsolute(s): + AbsoluteFile pathnorm.normalizePath(s) + else: + AbsoluteFile pathnorm.normalizePath(os.getCurrentDir() / s) + +proc setTrackingInfo(conf: ConfigRef; dirty, file, line, column: string, + info: TLineInfo) = + ## set tracking info, common code for track, trackDirty, & ideTrack + var ln, col: int + if parseUtils.parseInt(line, ln) <= 0: + localError(conf, info, errInvalidNumber % line) + if parseUtils.parseInt(column, col) <= 0: + localError(conf, info, errInvalidNumber % column) + + let a = makeAbsolute(file) + if dirty == "": + conf.m.trackPos = newLineInfo(conf, a, ln, col) + else: + let dirtyOriginalIdx = fileInfoIdx(conf, a) + if dirtyOriginalIdx.int32 >= 0: + msgs.setDirtyFile(conf, dirtyOriginalIdx, makeAbsolute(dirty)) + conf.m.trackPos = newLineInfo(dirtyOriginalIdx, ln, col) + proc trackDirty(conf: ConfigRef; arg: string, info: TLineInfo) = var a = arg.split(',') if a.len != 4: localError(conf, info, "DIRTY_BUFFER,ORIGINAL_FILE,LINE,COLUMN expected") - var line, column: int - if parseUtils.parseInt(a[2], line) <= 0: - localError(conf, info, errInvalidNumber % a[1]) - if parseUtils.parseInt(a[3], column) <= 0: - localError(conf, info, errInvalidNumber % a[2]) - - let dirtyOriginalIdx = fileInfoIdx(conf, AbsoluteFile a[1]) - if dirtyOriginalIdx.int32 >= 0: - msgs.setDirtyFile(conf, dirtyOriginalIdx, AbsoluteFile a[0]) - - conf.m.trackPos = newLineInfo(dirtyOriginalIdx, line, column) + setTrackingInfo(conf, a[0], a[1], a[2], a[3], info) proc track(conf: ConfigRef; arg: string, info: TLineInfo) = var a = arg.split(',') if a.len != 3: localError(conf, info, "FILE,LINE,COLUMN expected") - var line, column: int - if parseUtils.parseInt(a[1], line) <= 0: - localError(conf, info, errInvalidNumber % a[1]) - if parseUtils.parseInt(a[2], column) <= 0: - localError(conf, info, errInvalidNumber % a[2]) - conf.m.trackPos = newLineInfo(conf, AbsoluteFile a[0], line, column) + setTrackingInfo(conf, "", a[0], a[1], a[2], info) + +proc trackIde(conf: ConfigRef; cmd: IdeCmd, arg: string, info: TLineInfo) = + ## set the tracking info related to an ide cmd, supports optional dirty file + var a = arg.split(',') + case a.len + of 4: + setTrackingInfo(conf, a[0], a[1], a[2], a[3], info) + of 3: + setTrackingInfo(conf, "", a[0], a[1], a[2], info) + else: + localError(conf, info, "[DIRTY_BUFFER,]ORIGINAL_FILE,LINE,COLUMN expected") + conf.ideCmd = cmd proc dynlibOverride(conf: ConfigRef; switch, arg: string, pass: TCmdLinePass, info: TLineInfo) = if pass in {passCmd2, passPP}: @@ -851,17 +872,17 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; expectNoArg(conf, switch, arg, pass, info) conf.ideCmd = ideSug of "def": - expectNoArg(conf, switch, arg, pass, info) - conf.ideCmd = ideDef + expectArg(conf, switch, arg, pass, info) + trackIde(conf, ideDef, arg, info) of "context": expectNoArg(conf, switch, arg, pass, info) conf.ideCmd = ideCon of "usages": - expectNoArg(conf, switch, arg, pass, info) - conf.ideCmd = ideUse + expectArg(conf, switch, arg, pass, info) + trackIde(conf, ideUse, arg, info) of "defusages": - expectNoArg(conf, switch, arg, pass, info) - conf.ideCmd = ideDus + expectArg(conf, switch, arg, pass, info) + trackIde(conf, ideDus, arg, info) of "stdout": processOnOffSwitchG(conf, {optStdout}, arg, pass, info) of "listfullpaths": diff --git a/compiler/ic/ic.nim b/compiler/ic/ic.nim index a23685be7..d9a8756f1 100644 --- a/compiler/ic/ic.nim +++ b/compiler/ic/ic.nim @@ -29,7 +29,7 @@ type PackedModule* = object ## the parts of a PackedEncoder that are part of the .rod file definedSymbols: string moduleFlags: TSymFlags - includes: seq[(LitId, string)] # first entry is the module filename itself + includes*: seq[(LitId, string)] # first entry is the module filename itself imports: seq[LitId] # the modules this module depends on toReplay*: PackedTree # pragmas and VM specific state to replay. topLevel*: PackedTree # top level statements diff --git a/compiler/ic/navigator.nim b/compiler/ic/navigator.nim index b1a237cf7..cedf41579 100644 --- a/compiler/ic/navigator.nim +++ b/compiler/ic/navigator.nim @@ -98,11 +98,31 @@ proc list(c: var NavContext; tree: PackedTree; sym: ItemId) = usage(c, tree.nodes[i].info, isDecl(tree, parent(NodePos i))) else: discard +proc searchForIncludeFile(g: ModuleGraph; fullPath: string): int = + for i in 0..high(g.packed): + for k in 1..high(g.packed[i].fromDisk.includes): + # we start from 1 because the first "include" file is + # the module's filename. + if os.cmpPaths(g.packed[i].fromDisk.strings[g.packed[i].fromDisk.includes[k][0]], fullPath) == 0: + return i + return -1 + proc nav(g: ModuleGraph) = # translate the track position to a packed position: let unpacked = g.config.m.trackPos - let mid = unpacked.fileIndex - let fileId = g.packed[int32 mid].fromDisk.strings.getKeyId(toFullPath(g.config, mid)) + var mid = unpacked.fileIndex.int + + let fullPath = toFullPath(g.config, unpacked.fileIndex) + + if g.packed[mid].status == undefined: + # check if 'mid' is an include file of some other module: + mid = searchForIncludeFile(g, fullPath) + + if mid < 0: + localError(g.config, unpacked, "unknown file name: " & fullPath) + return + + let fileId = g.packed[mid].fromDisk.strings.getKeyId(fullPath) if fileId == LitId(0): internalError(g.config, unpacked, "cannot find a valid file ID") @@ -114,9 +134,9 @@ proc nav(g: ModuleGraph) = trackPos: PackedLineInfo(line: unpacked.line, col: unpacked.col, file: fileId), outputSep: if isDefined(g.config, "nimIcNavigatorTests"): ' ' else: '\t' ) - var symId = search(c, g.packed[int32 mid].fromDisk.topLevel) + var symId = search(c, g.packed[mid].fromDisk.topLevel) if symId == EmptyItemId: - symId = search(c, g.packed[int32 mid].fromDisk.bodies) + symId = search(c, g.packed[mid].fromDisk.bodies) if symId == EmptyItemId: localError(g.config, unpacked, "no symbol at this position") diff --git a/compiler/msgs.nim b/compiler/msgs.nim index c1e1f9e39..39b10d7df 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -582,6 +582,9 @@ template globalError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") template globalError*(conf: ConfigRef; info: TLineInfo, arg: string) = liMessage(conf, info, errGenerated, arg, doRaise, instLoc()) +template globalError*(conf: ConfigRef; format: string, params: openArray[string]) = + liMessage(conf, unknownLineInfo, errGenerated, format % params, doRaise, instLoc()) + template localError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") = liMessage(conf, info, msg, arg, doNothing, instLoc()) diff --git a/compiler/options.nim b/compiler/options.nim index 5475410a4..c1ac6b1e0 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -701,7 +701,10 @@ proc getNimcacheDir*(conf: ConfigRef): AbsoluteDir = result = if not conf.nimcacheDir.isEmpty: conf.nimcacheDir elif conf.backend == backendJs: - conf.projectPath / genSubDir + if conf.outDir.isEmpty: + conf.projectPath / genSubDir + else: + conf.outDir / genSubDir else: AbsoluteDir(getOsCacheDir() / splitFile(conf.projectName).name & nimcacheSuffix(conf)) diff --git a/testament/categories.nim b/testament/categories.nim index 4c3a0b1f5..d05112a25 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -490,7 +490,7 @@ proc icTests(r: var TResults; testsDir: string, cat: Category, options: string; writeOnly = " --incremental:writeonly " readOnly = " --incremental:readonly " incrementalOn = " --incremental:on -d:nimIcIntegrityChecks " - navTestConfig = " --ic:on --defusages -d:nimIcNavigatorTests --hint[Conf]:off --warnings:off " + navTestConfig = " --ic:on -d:nimIcNavigatorTests --hint[Conf]:off --warnings:off " template test(x: untyped) = testSpecWithNimcache(r, makeRawTest(file, x & options, cat), nimcache) diff --git a/tests/navigator/minclude.nim b/tests/navigator/minclude.nim new file mode 100644 index 000000000..f65ebfab9 --- /dev/null +++ b/tests/navigator/minclude.nim @@ -0,0 +1,2 @@ +# An include file. +foo(3) diff --git a/tests/navigator/tincludefile.nim b/tests/navigator/tincludefile.nim new file mode 100644 index 000000000..f35ab2ec9 --- /dev/null +++ b/tests/navigator/tincludefile.nim @@ -0,0 +1,29 @@ +discard """ + cmd: "nim check $options --defusages:$file,12,7 $file" + nimout: '''def tincludefile_temp.nim(11, 10) +usage tincludefile_temp.nim(12, 8) + ''' +""" + + + + +proc foo(x: int) = + echo x + +foo(3) +echo "yes", 1 != 3 + +#!EDIT!# +discard """ + cmd: "nim check $options --defusages:$file/../minclude.nim,2,2 $file" + nimout: '''def tincludefile_temp.nim(10, 6) +usage minclude.nim(2, 1) + ''' +""" + + +proc foo(x: int) = + echo x + +include minclude diff --git a/tests/navigator/tnav1.nim b/tests/navigator/tnav1.nim index d7c6f63e2..b6bbdbf19 100644 --- a/tests/navigator/tnav1.nim +++ b/tests/navigator/tnav1.nim @@ -1,5 +1,5 @@ discard """ - cmd: "nim check $options --track:$file,12,7 $file" + cmd: "nim check $options --defusages:$file,12,7 $file" nimout: '''def tnav1_temp.nim(11, 10) usage tnav1_temp.nim(12, 8) ''' @@ -16,7 +16,7 @@ echo "yes", 1 != 3 #!EDIT!# discard """ - cmd: "nim check $options --track:$file,15,2 $file" + cmd: "nim check $options --defusages:$file,15,2 $file" nimout: '''def tnav1_temp.nim(12, 6) usage tnav1_temp.nim(15, 1) ''' From ad67bcf379dbe5b7b17d85e28620749cd8bcf5e9 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 19 Apr 2021 19:02:52 -0700 Subject: [PATCH 1973/3768] fix https://github.com/nim-lang/RFCs/issues/311 remove unary slice (#16714) --- compiler/suggest.nim | 2 +- lib/system.nim | 8 -------- testament/categories.nim | 2 +- tests/collections/ttables.nim | 2 +- tests/misc/tslices.nim | 11 ++++++----- 5 files changed, 9 insertions(+), 16 deletions(-) diff --git a/compiler/suggest.nim b/compiler/suggest.nim index f3add0849..a5b4ac87d 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -112,7 +112,7 @@ proc getTokenLenFromSource(conf: ConfigRef; ident: string; info: TLineInfo): int if ident[^1] == '=' and ident[0] in linter.Letters: if sourceIdent != "=": result = 0 - elif sourceIdent.len > ident.len and sourceIdent[..ident.high] == ident: + elif sourceIdent.len > ident.len and sourceIdent[0..ident.high] == ident: result = ident.len elif sourceIdent != ident: result = 0 diff --git a/lib/system.nim b/lib/system.nim index 7fd35781b..d73ef6dbe 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -510,14 +510,6 @@ proc `..`*[T, U](a: sink T, b: sink U): HSlice[T, U] {.noSideEffect, inline, mag ## echo a[2 .. 3] # @[30, 40] result = HSlice[T, U](a: a, b: b) -proc `..`*[T](b: sink T): HSlice[int, T] {.noSideEffect, inline, magic: "DotDot".} = - ## Unary `slice`:idx: operator that constructs an interval `[default(int), b]`. - ## - ## .. code-block:: Nim - ## let a = [10, 20, 30, 40, 50] - ## echo a[.. 2] # @[10, 20, 30] - result = HSlice[int, T](a: 0, b: b) - when defined(hotCodeReloading): {.pragma: hcrInline, inline.} else: diff --git a/testament/categories.nim b/testament/categories.nim index d05112a25..d7848d51d 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -339,7 +339,7 @@ proc findMainFile(dir: string): string = var nimFiles = 0 for kind, file in os.walkDir(dir): if kind == pcFile: - if file.endsWith(cfgExt): return file[ .. ^(cfgExt.len+1)] & ".nim" + if file.endsWith(cfgExt): return file[0..^(cfgExt.len+1)] & ".nim" elif file.endsWith(".nim"): if result.len == 0: result = file inc nimFiles diff --git a/tests/collections/ttables.nim b/tests/collections/ttables.nim index c2864b75f..638f4241b 100644 --- a/tests/collections/ttables.nim +++ b/tests/collections/ttables.nim @@ -171,7 +171,7 @@ block tableconstr: # NEW: doAssert 56 in 50..100 - doAssert 56 in ..60 + doAssert 56 in 0..60 block ttables2: diff --git a/tests/misc/tslices.nim b/tests/misc/tslices.nim index d063c5ebf..987b50de1 100644 --- a/tests/misc/tslices.nim +++ b/tests/misc/tslices.nim @@ -11,10 +11,9 @@ verichtetd # Test the new slices. -import strutils - var mystr = "Abgrund" -mystr[..1] = "Zu" +# mystr[..1] = "Zu" # deprecated +mystr[0..1] = "Zu" mystr[4..4] = "5" @@ -23,7 +22,8 @@ type var myarr: array[TEnum, int] = [1, 2, 3, 4, 5, 6] myarr[e1..e3] = myarr[e4..e6] -myarr[..e3] = myarr[e4..e6] +# myarr[..e3] = myarr[e4..e6] # deprecated +myarr[0..e3] = myarr[e4..e6] for x in items(myarr): stdout.write(x) echo() @@ -46,7 +46,8 @@ echo mystr mystr[4..4] = "u" # test full replacement -mystr[.. ^2] = "egerichtet" +# mystr[.. ^2] = "egerichtet" # deprecated +mystr[0 .. ^2] = "egerichtet" echo mystr From 6852d091b33757ab34d1f0c4f9b22d10dbe48049 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 19 Apr 2021 21:54:25 -0700 Subject: [PATCH 1974/3768] changelog: document hash changes (#17792) * changelog: document hash changes * fixup --- changelog.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index 8fab425f8..3caf5e92d 100644 --- a/changelog.md +++ b/changelog.md @@ -50,6 +50,9 @@ - On POSIX systems, we now ignore `SIGPIPE` signals, use `-d:nimLegacySigpipeHandler` for previous behavior. +- `hashes.hash` now supports `object` and `ref` (can be overloaded in user code). +- `hashes.hash(proc|ptr|ref|pointer)` now calls `hash(int)` and honors `-d:nimIntHash1`, + `hashes.hash(closure)` has also been improved. ## Standard library additions and changes - Added support for parenthesized expressions in `strformat` @@ -235,12 +238,9 @@ level `moveMem`, `copyMem` operations for Orc's copy-on-write string implementation. -- `hashes.hash` now supports `object`, but can be overloaded. - - Added `std/strbasics` for high performance string operations. Added `strip`, `setSlice`, `add(a: var string, b: openArray[char])`. -- `hashes.hash` now supports `object`, but can be overloaded. - Added to `wrapnils` an option-like API via `??.`, `isSome`, `get`. From fb02b569578e4538852cb92b350036548aa5da15 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 20 Apr 2021 00:01:53 -0700 Subject: [PATCH 1975/3768] support skip ci in azure pipelines, and support it properly in github actions pipelines (#17561) * support skip ci in azure pipelines * [skip ci] example of a commit that skips CI * example of a commit that does not skip CI * fix github actions logic for ci skip handling * [skip ci] example of a commit that skips CI for all pipelines * example of a commit that does not skip CI for any pipeline --- .github/workflows/ci_docs.yml | 13 ++++++++++++- .github/workflows/ci_packages.yml | 14 +++++++++++++- azure-pipelines.yml | 22 ++++++++++++++++------ doc/contributing.rst | 7 ++++--- 4 files changed, 45 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci_docs.yml b/.github/workflows/ci_docs.yml index 22d425b10..982efe610 100644 --- a/.github/workflows/ci_docs.yml +++ b/.github/workflows/ci_docs.yml @@ -30,8 +30,9 @@ on: jobs: build: + # see D20210329T004830 if: | - !contains(format('{0} {1}', github.event.head_commit.message, github.event.pull_request.title), '[skip ci]') + !contains(format('{0}', github.event.pull_request.title), '[skip ci]') strategy: fail-fast: false matrix: @@ -50,6 +51,16 @@ jobs: steps: - name: 'Checkout' uses: actions/checkout@v2 + with: + fetch-depth: 2 + + - name: 'Check whether to skip CI' + shell: bash + run: | + # see D20210329T004830 + commitMsg=$(git log --no-merges -1 --pretty=format:"%s") + echo commitMsg: $commitMsg + echo $commitMsg | grep -v '\[skip ci\]' - name: 'Install build dependencies (macOS)' if: runner.os == 'macOS' diff --git a/.github/workflows/ci_packages.yml b/.github/workflows/ci_packages.yml index dd42db0c4..b7af1210c 100644 --- a/.github/workflows/ci_packages.yml +++ b/.github/workflows/ci_packages.yml @@ -3,8 +3,9 @@ on: [push, pull_request] jobs: build: + # see D20210329T004830 if: | - !contains(format('{0} {1}', github.event.head_commit.message, github.event.pull_request.title), '[skip ci]') + !contains(format('{0}', github.event.pull_request.title), '[skip ci]') strategy: fail-fast: false matrix: @@ -19,6 +20,17 @@ jobs: steps: - name: 'Checkout' uses: actions/checkout@v2 + with: + fetch-depth: 2 + + - name: 'Check whether to skip CI' + shell: bash + run: | + # see D20210329T004830 + commitMsg=$(git log --no-merges -1 --pretty=format:"%s") + echo commitMsg: $commitMsg + echo $commitMsg | grep -v '\[skip ci\]' + - name: 'Checkout csources' uses: actions/checkout@v2 with: diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 9006cf6e5..55bad609e 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -52,10 +52,20 @@ jobs: steps: - bash: git config --global core.autocrlf false displayName: 'Disable auto conversion to CRLF by git (Windows-only)' - condition: eq(variables['Agent.OS'], 'Windows_NT') + condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) - checkout: self - fetchDepth: 1 + fetchDepth: 2 # see D20210329T004830 + + - bash: | + # D20210329T004830:here refs https://github.com/microsoft/azure-pipelines-agent/issues/2944 + # `--no-merges` is needed to avoid merge commits which occur for PR's. + # $(Build.SourceVersionMessage) is not helpful + # nor is `github.event.head_commit.message` for github actions. + commitMsg=$(git log --no-merges -1 --pretty=format:"%s") + echo commitMsg: $commitMsg + echo $commitMsg | grep -v '\[skip ci\]' # fails if [skip ci] not in commit msg + displayName: 'Check whether to skip CI' - bash: git clone --depth 1 https://github.com/nim-lang/csources displayName: 'Checkout Nim csources' @@ -73,7 +83,7 @@ jobs: echo_run sudo apt-fast install --no-install-recommends -yq \ libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev valgrind libc6-dbg displayName: 'Install dependencies (amd64 Linux)' - condition: and(eq(variables['Agent.OS'], 'Linux'), eq(variables['CPU'], 'amd64')) + condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'), eq(variables['CPU'], 'amd64')) - bash: | set -e @@ -113,11 +123,11 @@ jobs: echo_run chmod 755 bin/g++ displayName: 'Install dependencies (i386 Linux)' - condition: and(eq(variables['Agent.OS'], 'Linux'), eq(variables['CPU'], 'i386')) + condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'), eq(variables['CPU'], 'i386')) - bash: brew install boehmgc make sfml displayName: 'Install dependencies (OSX)' - condition: eq(variables['Agent.OS'], 'Darwin') + condition: and(succeeded(), eq(variables['Agent.OS'], 'Darwin')) - bash: | set -e @@ -130,7 +140,7 @@ jobs: echo_run echo '##vso[task.prependpath]$(System.DefaultWorkingDirectory)/dist/mingw64/bin' displayName: 'Install dependencies (Windows)' - condition: eq(variables['Agent.OS'], 'Windows_NT') + condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) - bash: echo '##vso[task.prependpath]$(System.DefaultWorkingDirectory)/bin' displayName: 'Add build binaries to PATH' diff --git a/doc/contributing.rst b/doc/contributing.rst index 7ee3aa444..3d3da2ce0 100644 --- a/doc/contributing.rst +++ b/doc/contributing.rst @@ -508,9 +508,10 @@ Continuous Integration (CI) 1. Continuous Integration is by default run on every push in a PR; this clogs the CI pipeline and affects other PR's; if you don't need it (e.g. for WIP or - documentation only changes), add `[ci skip]` to your commit message title. - This convention is supported by `Appveyor - `_ + documentation only changes), add `[skip ci]` to your commit message title. + This convention is supported by our github actions pipelines and our azure pipeline + as well as our former other pipelines: + `Appveyor `_ and `Travis `_. 2. Consider enabling CI (azure, GitHub actions and builds.sr.ht) in your own Nim fork, and From 68e7ed9c57605bc98d3ab7625374113386e62ee7 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 20 Apr 2021 00:02:35 -0700 Subject: [PATCH 1976/3768] important_packages: `allowed_failures` batch (#17757) * important_packages: reserve batch 0 for allowed failures * custom batch name: allowed_failures --- .github/workflows/ci_packages.yml | 2 +- testament/categories.nim | 35 ++++++++++++++++++++++--------- testament/testament.nim | 2 +- 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci_packages.yml b/.github/workflows/ci_packages.yml index b7af1210c..e7073ea23 100644 --- a/.github/workflows/ci_packages.yml +++ b/.github/workflows/ci_packages.yml @@ -11,7 +11,7 @@ jobs: matrix: os: [ubuntu-18.04, macos-10.15] cpu: [amd64] - batch: ["0_3", "1_3", "2_3"] # list of `index_num` + batch: ["allowed_failures", "0_3", "1_3", "2_3"] # list of `index_num` name: '${{ matrix.os }} (batch: ${{ matrix.batch }})' runs-on: ${{ matrix.os }} env: diff --git a/testament/categories.nim b/testament/categories.nim index d7848d51d..19d9e4507 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -14,6 +14,7 @@ import important_packages import std/strformat +from std/sequtils import filterIt const specialCategories = [ @@ -399,8 +400,7 @@ proc testStdlib(r: var TResults, pattern, options: string, cat: Category) = testSpec r, testObj # ----------------------------- nimble ---------------------------------------- -proc listPackages(packageFilter: string): seq[NimblePackage] = - # xxx document `packageFilter`, seems like a bad API (at least should be a regex; a substring match makes no sense) +proc listPackagesAll(): seq[NimblePackage] = var nimbleDir = getEnv("NIMBLE_DIR") if nimbleDir.len == 0: nimbleDir = getHomeDir() / ".nimble" let packageIndex = nimbleDir / "packages_official.json" @@ -409,14 +409,29 @@ proc listPackages(packageFilter: string): seq[NimblePackage] = for a in packageList: if a["name"].str == name: return a for pkg in important_packages.packages.items: - if isCurrentBatch(testamentData0, pkg.name) and packageFilter in pkg.name: - var pkg = pkg - if pkg.url.len == 0: - let pkg2 = findPackage(pkg.name) - if pkg2 == nil: - raise newException(ValueError, "Cannot find package '$#'." % pkg.name) - pkg.url = pkg2["url"].str - result.add pkg + var pkg = pkg + if pkg.url.len == 0: + let pkg2 = findPackage(pkg.name) + if pkg2 == nil: + raise newException(ValueError, "Cannot find package '$#'." % pkg.name) + pkg.url = pkg2["url"].str + result.add pkg + +proc listPackages(packageFilter: string): seq[NimblePackage] = + let pkgs = listPackagesAll() + if packageFilter.len != 0: + # xxx document `packageFilter`, seems like a bad API, + # at least should be a regex; a substring match makes no sense. + result = pkgs.filterIt(packageFilter in it.name) + else: + let pkgs1 = pkgs.filterIt(it.allowFailure) + let pkgs2 = pkgs.filterIt(not it.allowFailure) + if testamentData0.batchArg == "allowed_failures": + result = pkgs1 + else: + for i in 0.. 0 and p.val[0] in {'0'..'9'}: let s = p.val.split("_") doAssert s.len == 2, $(p.val, s) testamentData0.testamentBatch = s[0].parseInt From 4742e6e1fdc6cebd3052875748fa10ad44c8a42e Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 20 Apr 2021 00:25:28 -0700 Subject: [PATCH 1977/3768] followup #16714: add -d:nimLegacyUnarySlice + changelog (#17794) --- changelog.md | 3 +++ lib/system.nim | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/changelog.md b/changelog.md index 3caf5e92d..0537b3d6a 100644 --- a/changelog.md +++ b/changelog.md @@ -54,6 +54,9 @@ - `hashes.hash(proc|ptr|ref|pointer)` now calls `hash(int)` and honors `-d:nimIntHash1`, `hashes.hash(closure)` has also been improved. +- The unary slice `..b` was removed, use `0..b` instead or use `-d:nimLegacyUnarySlice` + for a deprecation period. + ## Standard library additions and changes - Added support for parenthesized expressions in `strformat` diff --git a/lib/system.nim b/lib/system.nim index d73ef6dbe..41749c27a 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -510,6 +510,16 @@ proc `..`*[T, U](a: sink T, b: sink U): HSlice[T, U] {.noSideEffect, inline, mag ## echo a[2 .. 3] # @[30, 40] result = HSlice[T, U](a: a, b: b) +when defined(nimLegacyUnarySlice): + proc `..`*[T](b: sink T): HSlice[int, T] + {.noSideEffect, inline, magic: "DotDot", deprecated: "replace `..b` with `0..b`".} = + ## Unary `slice`:idx: operator that constructs an interval `[default(int), b]`. + ## + ## .. code-block:: Nim + ## let a = [10, 20, 30, 40, 50] + ## echo a[.. 2] # @[10, 20, 30] + result = HSlice[int, T](a: 0, b: b) + when defined(hotCodeReloading): {.pragma: hcrInline, inline.} else: From 54fe44135ee447c167b4a3a2d6ec5ff7855688ea Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 20 Apr 2021 15:24:20 +0200 Subject: [PATCH 1978/3768] updated the documentation (#17796) --- compiler/ic/navigator.nim | 2 +- doc/advopt.txt | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/ic/navigator.nim b/compiler/ic/navigator.nim index cedf41579..6af2c2ac0 100644 --- a/compiler/ic/navigator.nim +++ b/compiler/ic/navigator.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -## Supports the "nim check --ic:on --def --track:FILE,LINE,COL" +## Supports the "nim check --ic:on --defusages:FILE,LINE,COL" ## IDE-like features. It uses the set of .rod files to accomplish ## its task. The set must cover a complete Nim project. diff --git a/doc/advopt.txt b/doc/advopt.txt index c7b63d50f..bfc381cff 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -14,8 +14,8 @@ Advanced commands: module dependency graph //dump dump all defined conditionals and search paths see also: --dump.format:json (useful with: `| jq`) - //check checks the project for syntax and semantic - (can be combined with --track and --defusages) + //check checks the project for syntax and semantics + (can be combined with --defusages) Runtime checks (see -x): --objChecks:on|off turn obj conversion checks on|off @@ -28,8 +28,8 @@ Runtime checks (see -x): --infChecks:on|off turn Inf checks on|off Advanced options: - --track:FILE,LINE,COL set the tracking position for (--defusages) - --defusages find the definition and all usages of a symbol + --defusages:FILE,LINE,COL + find the definition and all usages of a symbol -o:FILE, --out:FILE set the output filename --outdir:DIR set the path where the output file will be written --usenimcache will use `outdir=$$nimcache`, whichever it resolves From c776498170a2a08a67b1317d8965482cf266a733 Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 20 Apr 2021 21:39:58 +0800 Subject: [PATCH 1979/3768] close #9691 and close #10913('spawn'ed function cannot have a 'typed' or 'untyped' parameter) (#17775) --- compiler/spawn.nim | 12 +++++++++++- tests/parallel/t10913.nim | 20 ++++++++++++++++++++ tests/parallel/t9691.nim | 9 +++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 tests/parallel/t10913.nim create mode 100644 tests/parallel/t9691.nim diff --git a/compiler/spawn.nim b/compiler/spawn.nim index 1635f9f83..205197eaf 100644 --- a/compiler/spawn.nim +++ b/compiler/spawn.nim @@ -192,6 +192,11 @@ proc createCastExpr(argsParam: PSym; objType: PType; idgen: IdGenerator): PNode result.typ = newType(tyPtr, nextTypeId idgen, objType.owner) result.typ.rawAddSon(objType) +template checkMagicProcs(g: ModuleGraph, n: PNode, formal: PNode) = + if (formal.typ.kind == tyVarargs and formal.typ[0].kind in {tyTyped, tyUntyped}) or + formal.typ.kind in {tyTyped, tyUntyped}: + localError(g.config, n.info, "'spawn'ed function cannot have a 'typed' or 'untyped' parameter") + proc setupArgsForConcurrency(g: ModuleGraph; n: PNode; objType: PType; idgen: IdGenerator; owner: PSym; scratchObj: PSym, castExpr, call, @@ -205,6 +210,9 @@ proc setupArgsForConcurrency(g: ModuleGraph; n: PNode; objType: PType; if i < formals.len: if formals[i].typ.kind in {tyVar, tyLent}: localError(g.config, n[i].info, "'spawn'ed function cannot have a 'var' parameter") + + checkMagicProcs(g, n[i], formals[i]) + if formals[i].typ.kind in {tyTypeDesc, tyStatic}: continue #elif containsTyRef(argType): @@ -233,6 +241,9 @@ proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType; let n = n[i] if i < formals.len and formals[i].typ.kind in {tyStatic, tyTypeDesc}: continue + + checkMagicProcs(g, n, formals[i]) + let argType = skipTypes(if i < formals.len: formals[i].typ else: n.typ, abstractInst) #if containsTyRef(argType): @@ -420,4 +431,3 @@ proc wrapProcForSpawn*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; spawnExp wrapperProc.newSymNode, genAddrOf(scratchObj.newSymNode, idgen), nil, spawnExpr) if spawnKind == srFlowVar: result.add fvField - diff --git a/tests/parallel/t10913.nim b/tests/parallel/t10913.nim new file mode 100644 index 000000000..d8459ecd0 --- /dev/null +++ b/tests/parallel/t10913.nim @@ -0,0 +1,20 @@ +discard """ + matrix: "--threads:on" + errormsg: "'spawn'ed function cannot have a 'typed' or 'untyped' parameter" +""" + +# bug #10913 + +import threadpool + +proc useParallel*[T](unused: T) = + # use a generic T here to show the problem. + {.push experimental: "parallel".} + parallel: + for i in 0..4: + spawn echo "echo in parallel" + sync() + + {.pop.} + +useParallel(1) diff --git a/tests/parallel/t9691.nim b/tests/parallel/t9691.nim new file mode 100644 index 000000000..bbf2b1bc7 --- /dev/null +++ b/tests/parallel/t9691.nim @@ -0,0 +1,9 @@ +discard """ + matrix: "--threads:on" + errormsg: "'spawn'ed function cannot have a 'typed' or 'untyped' parameter" +""" + +# bug #9691 + +import threadpool +spawn echo() From 0b116310bfb691755481293e8f9af11190fe0999 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 20 Apr 2021 16:30:17 +0200 Subject: [PATCH 1980/3768] unit separator (#17730) * use the ASCII Unit Separator so that error messages can be handled precisely by the tooling * updated testament --- compiler/main.nim | 4 +-- compiler/msgs.nim | 61 ++++++++++++++++++-------------- compiler/vm.nim | 10 +++--- testament/important_packages.nim | 2 +- testament/specs.nim | 2 +- testament/testament.nim | 7 ++++ tests/misc/trunner.nim | 4 +-- tests/osproc/treadlines.nim | 4 +-- 8 files changed, 55 insertions(+), 39 deletions(-) diff --git a/compiler/main.nim b/compiler/main.nim index 6eb830164..d66a5f329 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -335,8 +335,8 @@ proc mainCommand*(graph: ModuleGraph) = msgWriteln(conf, $dumpdata, {msgStdout, msgSkipHook}) else: msgWriteln(conf, "-- list of currently defined symbols --", - {msgStdout, msgSkipHook}) - for s in definedSymbolNames(conf.symbols): msgWriteln(conf, s, {msgStdout, msgSkipHook}) + {msgStdout, msgSkipHook, msgNoUnitSep}) + for s in definedSymbolNames(conf.symbols): msgWriteln(conf, s, {msgStdout, msgSkipHook, msgNoUnitSep}) msgWriteln(conf, "-- end of list --", {msgStdout, msgSkipHook}) for it in conf.searchPaths: msgWriteln(conf, it.string) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 39b10d7df..30421fe6a 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -241,12 +241,13 @@ template toFullPath*(conf: ConfigRef; info: TLineInfo): string = template toFullPathConsiderDirty*(conf: ConfigRef; info: TLineInfo): string = string toFullPathConsiderDirty(conf, info.fileIndex) -type FilenameOption* = enum - foAbs # absolute path, e.g.: /pathto/bar/foo.nim - foRelProject # relative to project path, e.g.: ../foo.nim - foMagicSauce # magic sauce, shortest of (foAbs, foRelProject) - foName # lastPathPart, e.g.: foo.nim - foStacktrace # if optExcessiveStackTrace: foAbs else: foName +type + FilenameOption* = enum + foAbs # absolute path, e.g.: /pathto/bar/foo.nim + foRelProject # relative to project path, e.g.: ../foo.nim + foMagicSauce # magic sauce, shortest of (foAbs, foRelProject) + foName # lastPathPart, e.g.: foo.nim + foStacktrace # if optExcessiveStackTrace: foAbs else: foName proc toFilenameOption*(conf: ConfigRef, fileIdx: FileIndex, opt: FilenameOption): string = case opt @@ -295,10 +296,14 @@ proc `??`* (conf: ConfigRef; info: TLineInfo, filename: string): bool = # only for debugging purposes result = filename in toFilename(conf, info) +const + UnitSep = "\31" + type MsgFlag* = enum ## flags altering msgWriteln behavior msgStdout, ## force writing to stdout, even stderr is default msgSkipHook ## skip message hook even if it is present + msgNoUnitSep ## the message is a complete "paragraph". MsgFlags* = set[MsgFlag] proc msgWriteln*(conf: ConfigRef; s: string, flags: MsgFlags = {}) = @@ -310,17 +315,20 @@ proc msgWriteln*(conf: ConfigRef; s: string, flags: MsgFlags = {}) = ## This is used for 'nim dump' etc. where we don't have nimsuggest ## support. #if conf.cmd == cmdIdeTools and optCDebug notin gGlobalOptions: return + let sep = if msgNoUnitSep notin flags: UnitSep else: "" if not isNil(conf.writelnHook) and msgSkipHook notin flags: - conf.writelnHook(s) + conf.writelnHook(s & sep) elif optStdout in conf.globalOptions or msgStdout in flags: if eStdOut in conf.m.errorOutputs: flushDot(conf) - writeLine(stdout, s) + write stdout, s + writeLine(stdout, sep) flushFile(stdout) else: if eStdErr in conf.m.errorOutputs: flushDot(conf) - writeLine(stderr, s) + write stderr, s + writeLine(stderr, sep) # On Windows stderr is fully-buffered when piped, regardless of C std. when defined(windows): flushFile(stderr) @@ -366,7 +374,7 @@ proc msgWrite(conf: ConfigRef; s: string) = flushFile(stdOrr) conf.lastMsgWasDot.incl stdOrr.toStdOrrKind() # subsequent writes need `flushDot` -template styledMsgWriteln*(args: varargs[typed]) = +template styledMsgWriteln(args: varargs[typed]) = if not isNil(conf.writelnHook): callIgnoringStyle(callWritelnHook, nil, args) elif optStdout in conf.globalOptions: @@ -407,7 +415,7 @@ proc quit(conf: ConfigRef; msg: TMsgKind) {.gcsafe.} = styledMsgWriteln(fgRed, """ No stack traceback available To create a stacktrace, rerun compilation with './koch temp $1 ', see $2 for details""" % - [conf.command, "intern.html#debugging-the-compiler".createDocLink]) + [conf.command, "intern.html#debugging-the-compiler".createDocLink], UnitSep) quit 1 proc handleError(conf: ConfigRef; msg: TMsgKind, eh: TErrorHandling, s: string) = @@ -444,10 +452,11 @@ proc writeContext(conf: ConfigRef; lastinfo: TLineInfo) = conf.structuredErrorHook(conf, context.info, instantiationFrom, Severity.Hint) else: - let message = if context.detail == "": - instantiationFrom - else: - instantiationOfFrom.format(context.detail) + let message = + if context.detail == "": + instantiationFrom + else: + instantiationOfFrom.format(context.detail) styledMsgWriteln(styleBright, conf.toFileLineCol(context.info), " ", resetStyle, message) info = context.info @@ -479,11 +488,12 @@ proc sourceLine*(conf: ConfigRef; i: TLineInfo): string = result = conf.m.fileInfos[i.fileIndex.int32].lines[i.line.int-1] -proc writeSurroundingSrc(conf: ConfigRef; info: TLineInfo) = - const indent = " " - msgWriteln(conf, indent & $sourceLine(conf, info)) - if info.col >= 0: - msgWriteln(conf, indent & spaces(info.col) & '^') +proc getSurroundingSrc(conf: ConfigRef; info: TLineInfo): string = + if conf.hasHint(hintSource) and info != unknownLineInfo: + const indent = " " + result = "\n" & indent & $sourceLine(conf, info) + if info.col >= 0: + result.add "\n" & indent & spaces(info.col) & '^' proc formatMsg*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string): string = let title = case msg @@ -545,14 +555,13 @@ proc liMessage*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string, if msg == hintProcessing: msgWrite(conf, ".") else: - styledMsgWriteln(styleBright, loc, resetStyle, color, title, resetStyle, s, KindColor, kindmsg) - if conf.hasHint(hintSource) and info != unknownLineInfo: - conf.writeSurroundingSrc(info) + styledMsgWriteln(styleBright, loc, resetStyle, color, title, resetStyle, s, KindColor, kindmsg, + resetStyle, conf.getSurroundingSrc(info), UnitSep) if hintMsgOrigin in conf.mainPackageNotes: styledMsgWriteln(styleBright, toFileLineCol(info2), resetStyle, " compiler msg initiated here", KindColor, KindFormat % $hintMsgOrigin, - resetStyle) + resetStyle, UnitSep) handleError(conf, msg, eh, s) template rawMessage*(conf: ConfigRef; msg: TMsgKind, args: openArray[string]) = @@ -632,8 +641,8 @@ proc quotedFilename*(conf: ConfigRef; i: TLineInfo): Rope = result = conf.m.fileInfos[i.fileIndex.int32].quotedName template listMsg(title, r) = - msgWriteln(conf, title) - for a in r: msgWriteln(conf, " [$1] $2" % [if a in conf.notes: "x" else: " ", $a]) + msgWriteln(conf, title, {msgNoUnitSep}) + for a in r: msgWriteln(conf, " [$1] $2" % [if a in conf.notes: "x" else: " ", $a], {msgNoUnitSep}) proc listWarnings*(conf: ConfigRef) = listMsg("Warnings:", warnMin..warnMax) proc listHints*(conf: ConfigRef) = listMsg("Hints:", hintMin..hintMax) diff --git a/compiler/vm.nim b/compiler/vm.nim index d588067c7..7a231a482 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -37,7 +37,7 @@ proc stackTraceAux(c: PCtx; x: PStackFrame; pc: int; recursionLimit=100) = while x != nil: inc calls x = x.next - msgWriteln(c.config, $calls & " calls omitted\n") + msgWriteln(c.config, $calls & " calls omitted\n", {msgNoUnitSep}) return stackTraceAux(c, x.next, x.comesFrom, recursionLimit-1) var info = c.debug[pc] @@ -59,12 +59,12 @@ proc stackTraceAux(c: PCtx; x: PStackFrame; pc: int; recursionLimit=100) = if x.prc != nil: for k in 1..max(1, 25-s.len): s.add(' ') s.add(x.prc.name.s) - msgWriteln(c.config, s) + msgWriteln(c.config, s, {msgNoUnitSep}) proc stackTraceImpl(c: PCtx, tos: PStackFrame, pc: int, msg: string, lineInfo: TLineInfo, infoOrigin: InstantiationInfo) {.noinline.} = # noinline to avoid code bloat - msgWriteln(c.config, "stack trace: (most recent call last)") + msgWriteln(c.config, "stack trace: (most recent call last)", {msgNoUnitSep}) stackTraceAux(c, tos, pc) let action = if c.mode == emRepl: doRaise else: doNothing # XXX test if we want 'globalError' for every mode @@ -470,7 +470,7 @@ template handleJmpBack() {.dirty.} = if allowInfiniteLoops in c.features: c.loopIterations = c.config.maxLoopIterationsVM else: - msgWriteln(c.config, "stack trace: (most recent call last)") + msgWriteln(c.config, "stack trace: (most recent call last)", {msgNoUnitSep}) stackTraceAux(c, tos, pc) globalError(c.config, c.debug[pc], errTooManyIterations % $c.config.maxLoopIterationsVM) dec(c.loopIterations) @@ -1163,7 +1163,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = stackTrace(c, tos, pc, "node is not a proc symbol") of opcEcho: let rb = instr.regB - template fn(s) = msgWriteln(c.config, s, {msgStdout}) + template fn(s) = msgWriteln(c.config, s, {msgStdout, msgNoUnitSep}) if rb == 1: fn(regs[ra].node.strVal) else: var outp = "" diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 28d7dc367..ecae42811 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -74,7 +74,7 @@ pkg "gnuplot", "nim c gnuplot.nim" pkg "hts", "nim c -o:htss src/hts.nim" pkg "httpauth" pkg "illwill", "nimble examples" -pkg "inim" +pkg "inim", allowFailure=true pkg "itertools", "nim doc src/itertools.nim" pkg "iterutils" pkg "karax", "nim c -r tests/tester.nim" diff --git a/testament/specs.nim b/testament/specs.nim index b5e6de7c2..043f14ed3 100644 --- a/testament/specs.nim +++ b/testament/specs.nim @@ -202,7 +202,7 @@ proc extractSpec(filename: string; spec: var TSpec): string = # look for """ only in the first section if a >= 0 and b > a and a < 40: - result = s.substr(a+3, b-1).replace("'''", tripleQuote) + result = s.substr(a+3, b-1).multiReplace({"'''": tripleQuote, "\\31": "\31"}) else: #echo "warning: file does not contain spec: " & filename result = "" diff --git a/testament/testament.nim b/testament/testament.nim index f2a8e9055..b79b86e51 100644 --- a/testament/testament.nim +++ b/testament/testament.nim @@ -18,6 +18,11 @@ import compiler/nodejs import lib/stdtest/testutils from lib/stdtest/specialpaths import splitTestFile +proc trimUnitSep(x: var string) = + let L = x.len + if L > 0 and x[^1] == '\31': + setLen x, L-1 + var useColors = true var backendLogging = true var simulate = false @@ -172,6 +177,7 @@ proc callNimCompiler(cmdTemplate, filename, options, nimcache: string, result.nimout = "" while true: if outp.readLine(x): + trimUnitSep x result.nimout.add(x & '\n') if x =~ pegOfInterest: # `err` should contain the last error/warning message @@ -196,6 +202,7 @@ proc callNimCompiler(cmdTemplate, filename, options, nimcache: string, result.msg = matches[0] elif suc.isSuccess: result.err = reSuccess + trimUnitSep result.msg proc callCCompiler(cmdTemplate, filename, options: string, target: TTarget): TSpec = diff --git a/tests/misc/trunner.nim b/tests/misc/trunner.nim index 1895eeb97..df9ee9a48 100644 --- a/tests/misc/trunner.nim +++ b/tests/misc/trunner.nim @@ -235,7 +235,7 @@ tests/newconfig/bar/mfoo.nims""".splitLines var expected = "" for a in files: let b = dir / a - expected.add &"Hint: used config file '{b}' [Conf]\n" + expected.add &"Hint: used config file '{b}' [Conf]\31\n" doAssert outp.endsWith expected, outp & "\n" & expected block: # mfoo2.customext @@ -243,7 +243,7 @@ tests/newconfig/bar/mfoo.nims""".splitLines let cmd = fmt"{nim} e --hint:conf {filename}" let (outp, exitCode) = execCmdEx(cmd, options = {poStdErrToStdOut}) doAssert exitCode == 0 - var expected = &"Hint: used config file '{filename}' [Conf]\n" + var expected = &"Hint: used config file '{filename}' [Conf]\31\n" doAssert outp.endsWith "123" & "\n" & expected diff --git a/tests/osproc/treadlines.nim b/tests/osproc/treadlines.nim index 3a8303321..436dd7a10 100644 --- a/tests/osproc/treadlines.nim +++ b/tests/osproc/treadlines.nim @@ -1,6 +1,6 @@ discard """ - output: '''Error: cannot open 'a.nim' -Error: cannot open 'b.nim' + output: '''Error: cannot open 'a.nim'\31 +Error: cannot open 'b.nim'\31 ''' targets: "c" """ From eb221dcc27b7f8f11ec34046e3165508acdf8beb Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 21 Apr 2021 01:00:44 +0800 Subject: [PATCH 1981/3768] [std/deques] move data instead of copy + destroy (#17800) --- lib/pure/collections/deques.nim | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/lib/pure/collections/deques.nim b/lib/pure/collections/deques.nim index d2d53d993..582bb02a7 100644 --- a/lib/pure/collections/deques.nim +++ b/lib/pure/collections/deques.nim @@ -379,8 +379,7 @@ proc popFirst*[T](deq: var Deque[T]): T {.inline, discardable.} = emptyCheck(deq) dec deq.count - result = deq.data[deq.head] - destroy(deq.data[deq.head]) + result = move deq.data[deq.head] deq.head = (deq.head + 1) and deq.mask proc popLast*[T](deq: var Deque[T]): T {.inline, discardable.} = @@ -398,8 +397,7 @@ proc popLast*[T](deq: var Deque[T]): T {.inline, discardable.} = emptyCheck(deq) dec deq.count deq.tail = (deq.tail - 1) and deq.mask - result = deq.data[deq.tail] - destroy(deq.data[deq.tail]) + result = move deq.data[deq.tail] proc clear*[T](deq: var Deque[T]) {.inline.} = ## Resets the deque so that it is empty. From 603af22b7ca46ac566f8c7c15402028f3f976a4e Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 21 Apr 2021 11:05:16 +0800 Subject: [PATCH 1982/3768] [std/tasks]add gcsafe pragmas (#17799) * [std/tasks]add gcsafe pragmas * Update tasks.nim --- lib/std/tasks.nim | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/std/tasks.nim b/lib/std/tasks.nim index aed36f985..97c4df3f7 100644 --- a/lib/std/tasks.nim +++ b/lib/std/tasks.nim @@ -59,7 +59,7 @@ when compileOption("threads"): type Task* = object ## `Task` contains the callback and its arguments. - callback: proc (args: pointer) {.nimcall.} + callback: proc (args: pointer) {.nimcall, gcsafe.} args: pointer destroy: proc (args: pointer) {.nimcall.} @@ -73,7 +73,7 @@ proc `=destroy`*(t: var Task) {.inline.} = t.destroy(t.args) c_free(t.args) -proc invoke*(task: Task) {.inline.} = +proc invoke*(task: Task) {.inline, gcsafe.} = ## Invokes the `task`. assert task.callback != nil task.callback(task.args) @@ -215,7 +215,7 @@ macro toTask*(e: typed{nkCall | nkInfix | nkPrefix | nkPostfix | nkCommand | nkC result = quote do: `stmtList` - proc `funcName`(args: pointer) {.nimcall.} = + proc `funcName`(args: pointer) {.gcsafe, nimcall.} = let `objTemp` = cast[ptr `scratchObjType`](args) `functionStmtList` @@ -229,7 +229,7 @@ macro toTask*(e: typed{nkCall | nkInfix | nkPrefix | nkPostfix | nkCommand | nkC let funcName = genSym(nskProc, e[0].strVal) result = quote do: - proc `funcName`(args: pointer) {.nimcall.} = + proc `funcName`(args: pointer) {.gcsafe, nimcall.} = `funcCall` Task(callback: `funcName`, args: nil) From 8de053d8708d3547edeee8c4846b355a557f1d18 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 20 Apr 2021 22:00:45 -0700 Subject: [PATCH 1983/3768] fix `hintMsgOrigin` regression + simplify msgs code (#17805) --- compiler/msgs.nim | 5 +---- compiler/semdata.nim | 18 +++++++----------- compiler/semexprs.nim | 2 +- compiler/semmagic.nim | 8 ++++---- compiler/semobjconstr.nim | 13 ++++++------- compiler/semstmts.nim | 8 +++----- 6 files changed, 22 insertions(+), 32 deletions(-) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 30421fe6a..89949ef17 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -14,7 +14,7 @@ import std/private/miscdollars import strutils2 type InstantiationInfo* = typeof(instantiationInfo()) -template instLoc(): InstantiationInfo = instantiationInfo(-2, fullPaths = true) +template instLoc*(): InstantiationInfo = instantiationInfo(-2, fullPaths = true) template toStdOrrKind(stdOrr): untyped = if stdOrr == stdout: stdOrrStdout else: stdOrrStderr @@ -600,9 +600,6 @@ template localError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") template localError*(conf: ConfigRef; info: TLineInfo, arg: string) = liMessage(conf, info, errGenerated, arg, doNothing, instLoc()) -template localError*(conf: ConfigRef; info: TLineInfo, format: string, params: openArray[string]) = - liMessage(conf, info, errGenerated, format % params, doNothing, instLoc()) - template message*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") = liMessage(conf, info, msg, arg, doNothing, instLoc()) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 354d4587d..95278893a 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -501,18 +501,14 @@ proc errorNode*(c: PContext, n: PNode): PNode = result = newNodeI(nkEmpty, n.info) result.typ = errorType(c) -proc localErrorNode*(c: PContext, n: PNode, info: TLineInfo, msg: TMsgKind, arg: string): PNode = - localError(c.config, info, msg, arg) - result = errorNode(c, n) +template localErrorNode*(c: PContext, n: PNode, arg: string, info: TLineInfo, kind: TMsgKind = errGenerated): PNode = + liMessage(c.config, info, kind, arg, doNothing, instLoc()) + errorNode(c, n) -proc localErrorNode*(c: PContext, n: PNode, info: TLineInfo, arg: string): PNode = - localErrorNode(c, n, info, errGenerated, arg) - -proc localErrorNode*(c: PContext, n: PNode, msg: TMsgKind, arg: string): PNode = - localErrorNode(c, n, n.info, msg, arg) - -proc localErrorNode*(c: PContext, n: PNode, arg: string): PNode = - localErrorNode(c, n, n.info, errGenerated, arg) +template localErrorNode*(c: PContext, n: PNode, arg: string, kind: TMsgKind = errGenerated): PNode = + let n2 = n + liMessage(c.config, n2.info, kind, arg, doNothing, instLoc()) + errorNode(c, n2) proc fillTypeS*(dest: PType, kind: TTypeKind, c: PContext) = dest.kind = kind diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 353831736..defceb291 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2620,7 +2620,7 @@ proc semTupleConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = # check if either everything or nothing is tyTypeDesc for i in 1.. high(TSymChoiceRule).int: - return localErrorNode(c, n, n[2].info, errConstExprExpected) + return localErrorNode(c, n, errConstExprExpected, n[2].info) let id = newIdentNode(getIdent(c.cache, sl.strVal), n.info) let s = qualifiedLookUp(c, id, {checkUndeclared}) @@ -251,10 +251,10 @@ proc semBindSym(c: PContext, n: PNode): PNode = proc opBindSym(c: PContext, scope: PScope, n: PNode, isMixin: int, info: PNode): PNode = if n.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit, nkIdent}: - return localErrorNode(c, n, info.info, errStringOrIdentNodeExpected) + return localErrorNode(c, n, errStringOrIdentNodeExpected, info.info) if isMixin < 0 or isMixin > high(TSymChoiceRule).int: - return localErrorNode(c, n, info.info, errConstExprExpected) + return localErrorNode(c, n, errConstExprExpected, info.info) let id = if n.kind == nkIdent: n else: newIdentNode(getIdent(c.cache, n.strVal), info.info) diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim index 63b4b9a75..01d5a8b28 100644 --- a/compiler/semobjconstr.nim +++ b/compiler/semobjconstr.nim @@ -210,7 +210,7 @@ proc semConstructFields(c: PContext, n: PNode, localError(c.config, constrCtx.initExpr.info, "a case selecting discriminator '$1' with value '$2' " & "appears in the object construction, but the field(s) $3 " & - "are in conflict with this value.", + "are in conflict with this value." % [discriminator.sym.name.s, discriminatorVal.renderTree, fields]) template valuesInConflictError(valsDiff) = @@ -365,11 +365,10 @@ proc defaultConstructionError(c: PContext, t: PType, info: TLineInfo) = assert constrCtx.missingFields.len > 0 localError(c.config, info, "The $1 type doesn't have a default value. The following fields must " & - "be initialized: $2.", - [typeToString(t), listSymbolNames(constrCtx.missingFields)]) + "be initialized: $2." % [typeToString(t), listSymbolNames(constrCtx.missingFields)]) elif objType.kind == tyDistinct: localError(c.config, info, - "The $1 distinct type doesn't have a default value.", [typeToString(t)]) + "The $1 distinct type doesn't have a default value." % typeToString(t)) else: assert false, "Must not enter here." @@ -379,7 +378,7 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = for child in n: result.add child if t == nil: - return localErrorNode(c, result, errGenerated, "object constructor needs an object type") + return localErrorNode(c, result, "object constructor needs an object type") t = skipTypes(t, {tyGenericInst, tyAlias, tySink, tyOwned}) if t.kind == tyRef: @@ -390,7 +389,7 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = # multiple times as long as they don't have closures. result.typ.flags.incl tfHasOwned if t.kind != tyObject: - return localErrorNode(c, result, errGenerated, "object constructor needs an object type") + return localErrorNode(c, result, "object constructor needs an object type") # Check if the object is fully initialized by recursively testing each # field (if this is a case object, initialized fields in two different @@ -404,7 +403,7 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = if constrCtx.missingFields.len > 0: hasError = true localError(c.config, result.info, - "The $1 type requires the following fields to be initialized: $2.", + "The $1 type requires the following fields to be initialized: $2." % [t.sym.name.s, listSymbolNames(constrCtx.missingFields)]) # Since we were traversing the object fields, it's possible that diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index a10047187..22e4295fa 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -725,7 +725,7 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode = if n.len == 3: if n[0].kind == nkVarTuple: if n[0].len-1 != iterAfterVarLent.len: - return localErrorNode(c, n, n[0].info, errWrongNumberOfVariables) + return localErrorNode(c, n, errWrongNumberOfVariables, n[0].info) for i in 0.. 0 and not isException(typ.lastSon): - localError(c.config, n.info, "raised object of type $1 does not inherit from Exception", - [typeToString(typ)]) + localError(c.config, n.info, "raised object of type $1 does not inherit from Exception" % typeToString(typ)) proc addGenericParamListToScope(c: PContext, n: PNode) = if n.kind != nkGenericParams: illFormedAst(n, c.config) @@ -2201,8 +2200,7 @@ proc inferConceptStaticParam(c: PContext, inferred, n: PNode) = if not sameType(res.typ, typ.base): localError(c.config, n.info, "cannot infer the concept parameter '%s', due to a type mismatch. " & - "attempt to equate '%s' and '%s'.", - [inferred.renderTree, $res.typ, $typ.base]) + "attempt to equate '%s' and '%s'." % [inferred.renderTree, $res.typ, $typ.base]) typ.n = res proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode = From 7bce1f8578eafffacd599401e709de279528e68b Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 20 Apr 2021 22:33:56 -0700 Subject: [PATCH 1984/3768] close #13373 document `(ref Foo)(a: 1)` (#17804) --- doc/manual.rst | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/doc/manual.rst b/doc/manual.rst index ea444a0ac..15242cf7c 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -1702,7 +1702,17 @@ has the syntax `T(fieldA: valueA, fieldB: valueB, ...)` where `T` is an `object` type or a `ref object` type: .. code-block:: nim - var student = Student(name: "Anton", age: 5, id: 3) + type + Student = object + name: string + age: int + PStudent = ref Student + var a1 = Student(name: "Anton", age: 5) + var a2 = PStudent(name: "Anton", age: 5) + # this also works directly: + var a3 = (ref Student)(name: "Anton", age: 5) + # not all fields need to be mentioned, and they can be mentioned out of order: + var a4 = Student(age: 5) Note that, unlike tuples, objects require the field names along with their values. For a `ref object` type `system.new` is invoked implicitly. From a9b62de8956af50424767408bbe30631c63b331b Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 21 Apr 2021 07:41:33 +0200 Subject: [PATCH 1985/3768] CIs: attempt to use csources_v1 (#16282) * CIs: attempt to use csources_v1 * also updated the BSDs * also updated azure pipelines * std modules should not itself use the 'std/' import dir... * compiler has to be careful with std/ for v1 booting --- .builds/freebsd.yml | 2 +- .builds/openbsd_0.yml | 2 +- .builds/openbsd_1.yml | 2 +- .github/workflows/ci_docs.yml | 4 +- azure-pipelines.yml | 2 +- ci/build.bat | 2 +- ci/build.sh | 2 +- ci/nsis_build.bat | 2 +- compiler/extccomp.nim | 2 +- compiler/ic/cbackend.nim | 6 +-- compiler/ic/dce.nim | 2 +- compiler/ic/ic.nim | 6 +-- compiler/ic/integrity.nim | 2 +- compiler/ic/navigator.nim | 2 +- compiler/ic/packed_ast.nim | 2 +- compiler/int128.nim | 4 +- compiler/jsgen.nim | 2 +- compiler/lookups.nim | 2 +- compiler/modulegraphs.nim | 2 +- compiler/nilcheck.nim | 2 +- compiler/nim.nim | 2 +- compiler/nimpaths.nim | 2 +- compiler/rodutils.nim | 2 +- compiler/semdata.nim | 2 +- compiler/semtypes.nim | 2 +- compiler/vmdef.nim | 2 +- compiler/vmgen.nim | 2 +- compiler/vmops.nim | 16 +++---- compiler/wordrecg.nim | 80 ++++++++++++++++----------------- lib/pure/cgi.nim | 2 +- lib/pure/collections/deques.nim | 2 +- lib/pure/collections/tables.nim | 2 +- lib/pure/complex.nim | 2 +- lib/pure/cookies.nim | 6 +-- lib/pure/distros.nim | 6 +-- lib/pure/json.nim | 7 ++- lib/pure/marshal.nim | 2 +- lib/pure/math.nim | 2 +- lib/pure/oids.nim | 2 +- lib/pure/options.nim | 2 +- lib/pure/os.nim | 8 ++-- lib/pure/random.nim | 4 +- lib/pure/rationals.nim | 2 +- lib/pure/ropes.nim | 2 +- lib/pure/stats.nim | 2 +- lib/pure/strformat.nim | 4 +- lib/pure/sugar.nim | 2 +- lib/pure/typetraits.nim | 2 +- lib/pure/unittest.nim | 8 ++-- lib/pure/uri.nim | 2 +- lib/std/enumutils.nim | 8 ++-- lib/std/jsonutils.nim | 18 ++++---- lib/std/monotimes.nim | 4 +- lib/std/private/globs.nim | 2 +- lib/std/setutils.nim | 2 +- lib/std/sha1.nim | 4 +- lib/std/sysrand.nim | 10 ++--- lib/std/wrapnils.nim | 4 +- 58 files changed, 142 insertions(+), 143 deletions(-) diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml index 6898a80fd..443be798a 100644 --- a/.builds/freebsd.yml +++ b/.builds/freebsd.yml @@ -15,7 +15,7 @@ environment: tasks: - setup: | cd Nim - git clone --depth 1 -q https://github.com/nim-lang/csources.git + git clone --depth 1 -q https://github.com/nim-lang/csources_v1.git csources gmake -C csources -j $(sysctl -n hw.ncpu) bin/nim c --skipUserCfg --skipParentCfg koch echo 'export PATH=$HOME/Nim/bin:$PATH' >> $HOME/.buildenv diff --git a/.builds/openbsd_0.yml b/.builds/openbsd_0.yml index 146ad8165..c09f0f08d 100644 --- a/.builds/openbsd_0.yml +++ b/.builds/openbsd_0.yml @@ -18,7 +18,7 @@ environment: tasks: - setup: | cd Nim - git clone --depth 1 -q https://github.com/nim-lang/csources.git + git clone --depth 1 -q https://github.com/nim-lang/csources_v1.git csources gmake -C csources -j $(sysctl -n hw.ncpuonline) bin/nim c koch echo 'export PATH=$HOME/Nim/bin:$PATH' >> $HOME/.buildenv diff --git a/.builds/openbsd_1.yml b/.builds/openbsd_1.yml index ad3734008..ffe449733 100644 --- a/.builds/openbsd_1.yml +++ b/.builds/openbsd_1.yml @@ -18,7 +18,7 @@ environment: tasks: - setup: | cd Nim - git clone --depth 1 -q https://github.com/nim-lang/csources.git + git clone --depth 1 -q https://github.com/nim-lang/csources_v1.git csources gmake -C csources -j $(sysctl -n hw.ncpuonline) bin/nim c koch echo 'export PATH=$HOME/Nim/bin:$PATH' >> $HOME/.buildenv diff --git a/.github/workflows/ci_docs.yml b/.github/workflows/ci_docs.yml index 982efe610..c9036d1d4 100644 --- a/.github/workflows/ci_docs.yml +++ b/.github/workflows/ci_docs.yml @@ -85,7 +85,7 @@ jobs: id: csources-version shell: bash run: | - sha=$(git ls-remote https://github.com/nim-lang/csources master | cut -f 1) + sha=$(git ls-remote https://github.com/nim-lang/csources_v1 master | cut -f 1) echo "::set-output name=sha::$sha" - name: 'Get prebuilt csources from cache' @@ -99,7 +99,7 @@ jobs: if: steps.csources-cache.outputs.cache-hit != 'true' uses: actions/checkout@v2 with: - repository: nim-lang/csources + repository: nim-lang/csources_v1 path: csources - name: 'Build 1-stage compiler from csources' diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 55bad609e..5c244648d 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -67,7 +67,7 @@ jobs: echo $commitMsg | grep -v '\[skip ci\]' # fails if [skip ci] not in commit msg displayName: 'Check whether to skip CI' - - bash: git clone --depth 1 https://github.com/nim-lang/csources + - bash: git clone --depth 1 https://github.com/nim-lang/csources_v1 csources displayName: 'Checkout Nim csources' - task: NodeTool@0 diff --git a/ci/build.bat b/ci/build.bat index cdce8d3d2..2227168e5 100644 --- a/ci/build.bat +++ b/ci/build.bat @@ -2,7 +2,7 @@ REM Some debug info echo "Running on %CI_RUNNER_ID% (%CI_RUNNER_DESCRIPTION%) with tags %CI_RUNNER_TAGS%." gcc -v -git clone --depth 1 https://github.com/nim-lang/csources.git +git clone --depth 1 https://github.com/nim-lang/csources_v1.git csources cd csources call build64.bat cd .. diff --git a/ci/build.sh b/ci/build.sh index 6321fffba..a89d011ed 100644 --- a/ci/build.sh +++ b/ci/build.sh @@ -1,7 +1,7 @@ sh ci/deps.sh # Build from C sources. -git clone --depth 1 https://github.com/nim-lang/csources.git +git clone --depth 1 https://github.com/nim-lang/csources_v1.git csources cd csources sh build.sh cd .. diff --git a/ci/nsis_build.bat b/ci/nsis_build.bat index 4806810d7..12aff1b72 100644 --- a/ci/nsis_build.bat +++ b/ci/nsis_build.bat @@ -25,7 +25,7 @@ Rem Build csources koch csources -d:release || exit /b rem Grab C sources and nimsuggest -git clone --depth 1 https://github.com/nim-lang/csources.git +git clone --depth 1 https://github.com/nim-lang/csources_v1.git csources set PATH=%CD%\bin;%PATH% diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 8d1432aa8..71a20fc47 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -14,7 +14,7 @@ import ropes, platform, condsyms, options, msgs, lineinfos, pathutils -import std/[os, strutils, osproc, sha1, streams, sequtils, times, strtabs, json] +import os, strutils, osproc, std/sha1, streams, sequtils, times, strtabs, json type TInfoCCProp* = enum # properties of the C compiler: diff --git a/compiler/ic/cbackend.nim b/compiler/ic/cbackend.nim index c7e5c7266..f5811cb3b 100644 --- a/compiler/ic/cbackend.nim +++ b/compiler/ic/cbackend.nim @@ -18,8 +18,8 @@ ## also doing cross-module dependency tracking and DCE that we don't need ## anymore. DCE is now done as prepass over the entire packed module graph. -import std/[packedsets, algorithm, tables] - # std/intsets would give `UnusedImport`, pending https://github.com/nim-lang/Nim/issues/14246 +import std/packedsets, algorithm, tables + import ".."/[ast, options, lineinfos, modulegraphs, cgendata, cgen, pathutils, extccomp, msgs] @@ -70,7 +70,7 @@ proc addFileToLink(config: ConfigRef; m: PSym) = addFileToCompile(config, cf) when defined(debugDce): - import std / [os, packedsets] + import os, std/packedsets proc storeAliveSymsImpl(asymFile: AbsoluteFile; s: seq[int32]) = var f = rodfiles.create(asymFile.string) diff --git a/compiler/ic/dce.nim b/compiler/ic/dce.nim index 48ed414e2..350b17d1b 100644 --- a/compiler/ic/dce.nim +++ b/compiler/ic/dce.nim @@ -9,7 +9,7 @@ ## Dead code elimination (=DCE) for IC. -import std / [intsets, tables] +import intsets, tables import ".." / [ast, options, lineinfos, types] import packed_ast, ic, bitabs diff --git a/compiler/ic/ic.nim b/compiler/ic/ic.nim index d9a8756f1..6530cb6c2 100644 --- a/compiler/ic/ic.nim +++ b/compiler/ic/ic.nim @@ -7,12 +7,12 @@ # distribution, for details about the copyright. # -import std / [hashes, tables, intsets, sha1] +import hashes, tables, intsets, std/sha1 import packed_ast, bitabs, rodfiles import ".." / [ast, idents, lineinfos, msgs, ropes, options, pathutils, condsyms] #import ".." / [renderer, astalgo] -from std / os import removeFile, isAbsolute +from os import removeFile, isAbsolute type PackedConfig* = object @@ -143,7 +143,7 @@ const debugConfigDiff = defined(debugConfigDiff) when debugConfigDiff: - import std / [hashes, tables, intsets, sha1, strutils, sets] + import hashes, tables, intsets, sha1, strutils, sets proc configIdentical(m: PackedModule; config: ConfigRef): bool = result = m.definedSymbols == definedSymbolsAsString(config) diff --git a/compiler/ic/integrity.nim b/compiler/ic/integrity.nim index 428c344a3..ed367ef61 100644 --- a/compiler/ic/integrity.nim +++ b/compiler/ic/integrity.nim @@ -10,7 +10,7 @@ ## Integrity checking for a set of .rod files. ## The set must cover a complete Nim project. -import std / sets +import sets import ".." / [ast, modulegraphs] import packed_ast, bitabs, ic diff --git a/compiler/ic/navigator.nim b/compiler/ic/navigator.nim index 6af2c2ac0..a1a14885d 100644 --- a/compiler/ic/navigator.nim +++ b/compiler/ic/navigator.nim @@ -11,7 +11,7 @@ ## IDE-like features. It uses the set of .rod files to accomplish ## its task. The set must cover a complete Nim project. -import std / sets +import sets from os import nil from std/private/miscdollars import toLocation diff --git a/compiler/ic/packed_ast.nim b/compiler/ic/packed_ast.nim index 2700c8253..6fadde17c 100644 --- a/compiler/ic/packed_ast.nim +++ b/compiler/ic/packed_ast.nim @@ -12,7 +12,7 @@ ## use this representation directly in all the transformations, ## it is superior. -import std / [hashes, tables, strtabs] +import hashes, tables, strtabs import bitabs import ".." / [ast, options] diff --git a/compiler/int128.nim b/compiler/int128.nim index 8d3cd7113..6ba7c1961 100644 --- a/compiler/int128.nim +++ b/compiler/int128.nim @@ -3,7 +3,7 @@ ## hold all from `low(BiggestInt)` to `high(BiggestUInt)`, This ## type is for that purpose. -from std/math import trunc +from math import trunc type Int128* = object @@ -378,7 +378,7 @@ proc `*`*(lhs, rhs: Int128): Int128 = proc `*=`*(a: var Int128, b: Int128) = a = a * b -import std/bitops +import bitops proc fastLog2*(a: Int128): int = if a.udata[3] != 0: diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 350563d6b..e75ff182f 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -35,7 +35,7 @@ import cgmeth, lowerings, sighashes, modulegraphs, lineinfos, rodutils, transf, injectdestructors, sourcemap -import std/[json, sets, math, tables, intsets, strutils] +import json, sets, math, tables, intsets, strutils from modulegraphs import ModuleGraph, PPassContext diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 7ceadfb96..c4f506a68 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -372,7 +372,7 @@ when false: of 'a'..'z': result = getIdent(c.cache, toLowerAscii(x.s[0]) & x.s.substr(1)) else: result = x -import std/[editdistance, heapqueue] +import std/editdistance, heapqueue type SpellCandidate = object dist: int diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index 9df66269b..c268308d2 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -11,7 +11,7 @@ ## represents a complete Nim project. Single modules can either be kept in RAM ## or stored in a rod-file. -import std / [intsets, tables, hashes, md5] +import intsets, tables, hashes, md5 import ast, astalgo, options, lineinfos,idents, btrees, ropes, msgs, pathutils import ic / [packed_ast, ic] diff --git a/compiler/nilcheck.nim b/compiler/nilcheck.nim index f3ec893f7..9c2d091f7 100644 --- a/compiler/nilcheck.nim +++ b/compiler/nilcheck.nim @@ -8,7 +8,7 @@ # import ast, renderer, intsets, tables, msgs, options, lineinfos, strformat, idents, treetab, hashes -import sequtils, strutils, std / sets +import sequtils, strutils, sets # IMPORTANT: notes not up to date, i'll update this comment again # diff --git a/compiler/nim.nim b/compiler/nim.nim index df06a83a9..f18238f60 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -24,7 +24,7 @@ import idents, lineinfos, cmdlinehelper, pathutils, modulegraphs -from std/browsers import openDefaultBrowser +from browsers import openDefaultBrowser from nodejs import findNodeJs when hasTinyCBackend: diff --git a/compiler/nimpaths.nim b/compiler/nimpaths.nim index 71bb9a7d7..a93b488fd 100644 --- a/compiler/nimpaths.nim +++ b/compiler/nimpaths.nim @@ -17,7 +17,7 @@ interpolation variables: Unstable API ]## -import std/[os,strutils] +import os, strutils const docCss* = "$nimr/doc/nimdoc.css" diff --git a/compiler/rodutils.nim b/compiler/rodutils.nim index 353992fca..e13b08e05 100644 --- a/compiler/rodutils.nim +++ b/compiler/rodutils.nim @@ -8,7 +8,7 @@ # ## Serialization utilities for the compiler. -import std/[strutils, math] +import strutils, math # bcc on windows doesn't have C99 functions when defined(windows) and defined(bcc): diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 95278893a..9a17efb1c 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -9,7 +9,7 @@ ## This module contains the data structures for the semantic checking phase. -import std / tables +import tables import intsets, options, ast, astalgo, msgs, idents, renderer, diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 8be210067..c921703b0 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -10,7 +10,7 @@ # this module does the semantic checking of type declarations # included from sem.nim -import std/math +import math const errStringOrIdentNodeExpected = "string or ident node expected" diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index 068ade4b0..0afb2ed6a 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -10,7 +10,7 @@ ## This module contains the type definitions for the new evaluation engine. ## An instruction is 1-3 int32s in memory, it is a register based VM. -import std / tables +import tables import ast, idents, options, modulegraphs, lineinfos diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index c60e81020..3a6004e8b 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -27,7 +27,7 @@ # solves the opcLdConst vs opcAsgnConst issue. Of course whether we need # this copy depends on the involved types. -import std / tables +import tables import strutils, ast, types, msgs, renderer, vmdef, diff --git a/compiler/vmops.nim b/compiler/vmops.nim index 04356fc76..1d22750e7 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -9,24 +9,24 @@ # Unfortunately this cannot be a module yet: #import vmdeps, vm -from std/math import sqrt, ln, log10, log2, exp, round, arccos, arcsin, +from math import sqrt, ln, log10, log2, exp, round, arccos, arcsin, arctan, arctan2, cos, cosh, hypot, sinh, sin, tan, tanh, pow, trunc, floor, ceil, `mod`, cbrt, arcsinh, arccosh, arctanh, erf, erfc, gamma, lgamma when declared(math.copySign): - from std/math import copySign + from math import copySign when declared(math.signbit): - from std/math import signbit + from math import signbit -from std/os import getEnv, existsEnv, dirExists, fileExists, putEnv, walkDir, +from os import getEnv, existsEnv, dirExists, fileExists, putEnv, walkDir, getAppFilename, raiseOSError, osLastError -from std/md5 import getMD5 -from std/times import cpuTime -from std/hashes import hash -from std/osproc import nil +from md5 import getMD5 +from times import cpuTime +from hashes import hash +from osproc import nil from sighashes import symBodyDigest diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index 827efcfa0..c3625c5f3 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -17,58 +17,58 @@ type TSpecialWord* = enum wInvalid = "", wAddr = "addr", wAnd = "and", wAs = "as", wAsm = "asm", - wBind = "bind", wBlock = "block", wBreak = "break", wCase = "case", wCast = "cast", - wConcept = "concept", wConst = "const", wContinue = "continue", wConverter = "converter", + wBind = "bind", wBlock = "block", wBreak = "break", wCase = "case", wCast = "cast", + wConcept = "concept", wConst = "const", wContinue = "continue", wConverter = "converter", wDefer = "defer", wDiscard = "discard", wDistinct = "distinct", wDiv = "div", wDo = "do", - wElif = "elif", wElse = "else", wEnd = "end", wEnum = "enum", wExcept = "except", - wExport = "export", wFinally = "finally", wFor = "for", wFrom = "from", wFunc = "func", - wIf = "if", wImport = "import", wIn = "in", wInclude = "include", wInterface = "interface", + wElif = "elif", wElse = "else", wEnd = "end", wEnum = "enum", wExcept = "except", + wExport = "export", wFinally = "finally", wFor = "for", wFrom = "from", wFunc = "func", + wIf = "if", wImport = "import", wIn = "in", wInclude = "include", wInterface = "interface", wIs = "is", wIsnot = "isnot", wIterator = "iterator", wLet = "let", wMacro = "macro", - wMethod = "method", wMixin = "mixin", wMod = "mod", wNil = "nil", wNot = "not", wNotin = "notin", - wObject = "object", wOf = "of", wOr = "or", wOut = "out", wProc = "proc", wPtr = "ptr", - wRaise = "raise", wRef = "ref", wReturn = "return", wShl = "shl", wShr = "shr", wStatic = "static", - wTemplate = "template", wTry = "try", wTuple = "tuple", wType = "type", wUsing = "using", + wMethod = "method", wMixin = "mixin", wMod = "mod", wNil = "nil", wNot = "not", wNotin = "notin", + wObject = "object", wOf = "of", wOr = "or", wOut = "out", wProc = "proc", wPtr = "ptr", + wRaise = "raise", wRef = "ref", wReturn = "return", wShl = "shl", wShr = "shr", wStatic = "static", + wTemplate = "template", wTry = "try", wTuple = "tuple", wType = "type", wUsing = "using", wVar = "var", wWhen = "when", wWhile = "while", wXor = "xor", wYield = "yield", wColon = ":", wColonColon = "::", wEquals = "=", wDot = ".", wDotDot = "..", wStar = "*", wMinus = "-", - wMagic = "magic", wThread = "thread", wFinal = "final", wProfiler = "profiler", + wMagic = "magic", wThread = "thread", wFinal = "final", wProfiler = "profiler", wMemTracker = "memtracker", wObjChecks = "objchecks", - wIntDefine = "intdefine", wStrDefine = "strdefine", wBoolDefine = "booldefine", + wIntDefine = "intdefine", wStrDefine = "strdefine", wBoolDefine = "booldefine", wCursor = "cursor", wNoalias = "noalias", - wImmediate = "immediate", wConstructor = "constructor", wDestructor = "destructor", + wImmediate = "immediate", wConstructor = "constructor", wDestructor = "destructor", wDelegator = "delegator", wOverride = "override", wImportCpp = "importcpp", - wCppNonPod = "cppNonPod", + wCppNonPod = "cppNonPod", wImportObjC = "importobjc", wImportCompilerProc = "importcompilerproc", - wImportc = "importc", wImportJs = "importjs", wExportc = "exportc", wExportCpp = "exportcpp", + wImportc = "importc", wImportJs = "importjs", wExportc = "exportc", wExportCpp = "exportcpp", wExportNims = "exportnims", wIncompleteStruct = "incompleteStruct", # deprecated wCompleteStruct = "completeStruct", wRequiresInit = "requiresInit", wAlign = "align", wNodecl = "nodecl", wPure = "pure", wSideEffect = "sideEffect", wHeader = "header", wNoSideEffect = "noSideEffect", wGcSafe = "gcsafe", wNoreturn = "noreturn", wNosinks = "nosinks", wMerge = "merge", wLib = "lib", wDynlib = "dynlib", - wCompilerProc = "compilerproc", wCore = "core", wProcVar = "procvar", - wBase = "base", wUsed = "used", wFatal = "fatal", wError = "error", wWarning = "warning", + wCompilerProc = "compilerproc", wCore = "core", wProcVar = "procvar", + wBase = "base", wUsed = "used", wFatal = "fatal", wError = "error", wWarning = "warning", wHint = "hint", wWarningAsError = "warningAsError", wHintAsError = "hintAsError", wLine = "line", wPush = "push", - wPop = "pop", wDefine = "define", wUndef = "undef", wLineDir = "lineDir", + wPop = "pop", wDefine = "define", wUndef = "undef", wLineDir = "lineDir", wStackTrace = "stackTrace", wLineTrace = "lineTrace", wLink = "link", wCompile = "compile", - wLinksys = "linksys", wDeprecated = "deprecated", wVarargs = "varargs", wCallconv = "callconv", - wDebugger = "debugger", wNimcall = "nimcall", wStdcall = "stdcall", wCdecl = "cdecl", + wLinksys = "linksys", wDeprecated = "deprecated", wVarargs = "varargs", wCallconv = "callconv", + wDebugger = "debugger", wNimcall = "nimcall", wStdcall = "stdcall", wCdecl = "cdecl", wSafecall = "safecall", wSyscall = "syscall", wInline = "inline", wNoInline = "noinline", wFastcall = "fastcall", wThiscall = "thiscall", wClosure = "closure", wNoconv = "noconv", - wOn = "on", wOff = "off", wChecks = "checks", wRangeChecks = "rangeChecks", + wOn = "on", wOff = "off", wChecks = "checks", wRangeChecks = "rangeChecks", wBoundChecks = "boundChecks", wOverflowChecks = "overflowChecks", wNilChecks = "nilChecks", - wFloatChecks = "floatChecks", wNanChecks = "nanChecks", wInfChecks = "infChecks", + wFloatChecks = "floatChecks", wNanChecks = "nanChecks", wInfChecks = "infChecks", wStyleChecks = "styleChecks", wStaticBoundchecks = "staticBoundChecks", wNonReloadable = "nonReloadable", wExecuteOnReload = "executeOnReload", - wAssertions = "assertions", wPatterns = "patterns", wTrMacros = "trmacros", + wAssertions = "assertions", wPatterns = "patterns", wTrMacros = "trmacros", wSinkInference = "sinkInference", wWarnings = "warnings", - wHints = "hints", wOptimization = "optimization", wRaises = "raises", + wHints = "hints", wOptimization = "optimization", wRaises = "raises", wWrites = "writes", wReads = "reads", wSize = "size", wEffects = "effects", wTags = "tags", wRequires = "requires", wEnsures = "ensures", wInvariant = "invariant", wAssume = "assume", wAssert = "assert", @@ -76,34 +76,34 @@ type wSafecode = "safecode", wPackage = "package", wNoForward = "noforward", wReorder = "reorder", wNoRewrite = "norewrite", wNoDestroy = "nodestroy", wPragma = "pragma", wCompileTime = "compileTime", wNoInit = "noinit", wPassc = "passc", wPassl = "passl", - wLocalPassc = "localPassC", wBorrow = "borrow", wDiscardable = "discardable", + wLocalPassc = "localPassC", wBorrow = "borrow", wDiscardable = "discardable", wFieldChecks = "fieldChecks", wSubsChar = "subschar", wAcyclic = "acyclic", wShallow = "shallow", wUnroll = "unroll", wLinearScanEnd = "linearScanEnd", wComputedGoto = "computedGoto", wInjectStmt = "injectStmt", wExperimental = "experimental", - wWrite = "write", wGensym = "gensym", wInject = "inject", wDirty = "dirty", + wWrite = "write", wGensym = "gensym", wInject = "inject", wDirty = "dirty", wInheritable = "inheritable", wThreadVar = "threadvar", wEmit = "emit", wAsmNoStackFrame = "asmNoStackFrame", wImplicitStatic = "implicitStatic", wGlobal = "global", wCodegenDecl = "codegenDecl", wUnchecked = "unchecked", wGuard = "guard", wLocks = "locks", wPartial = "partial", wExplain = "explain", wLiftLocals = "liftlocals", - wAuto = "auto", wBool = "bool", wCatch = "catch", wChar = "char", - wClass = "class", wCompl = "compl", wConst_cast = "const_cast", wDefault = "default", - wDelete = "delete", wDouble = "double", wDynamic_cast = "dynamic_cast", + wAuto = "auto", wBool = "bool", wCatch = "catch", wChar = "char", + wClass = "class", wCompl = "compl", wConst_cast = "const_cast", wDefault = "default", + wDelete = "delete", wDouble = "double", wDynamic_cast = "dynamic_cast", wExplicit = "explicit", wExtern = "extern", wFalse = "false", wFloat = "float", - wFriend = "friend", wGoto = "goto", wInt = "int", wLong = "long", wMutable = "mutable", - wNamespace = "namespace", wNew = "new", wOperator = "operator", wPrivate = "private", - wProtected = "protected", wPublic = "public", wRegister = "register", - wReinterpret_cast = "reinterpret_cast", wRestrict = "restrict", wShort = "short", - wSigned = "signed", wSizeof = "sizeof", wStatic_cast = "static_cast", wStruct = "struct", - wSwitch = "switch", wThis = "this", wThrow = "throw", wTrue = "true", wTypedef = "typedef", + wFriend = "friend", wGoto = "goto", wInt = "int", wLong = "long", wMutable = "mutable", + wNamespace = "namespace", wNew = "new", wOperator = "operator", wPrivate = "private", + wProtected = "protected", wPublic = "public", wRegister = "register", + wReinterpret_cast = "reinterpret_cast", wRestrict = "restrict", wShort = "short", + wSigned = "signed", wSizeof = "sizeof", wStatic_cast = "static_cast", wStruct = "struct", + wSwitch = "switch", wThis = "this", wThrow = "throw", wTrue = "true", wTypedef = "typedef", wTypeid = "typeid", wTypeof = "typeof", wTypename = "typename", - wUnion = "union", wPacked = "packed", wUnsigned = "unsigned", wVirtual = "virtual", + wUnion = "union", wPacked = "packed", wUnsigned = "unsigned", wVirtual = "virtual", wVoid = "void", wVolatile = "volatile", wWchar_t = "wchar_t", - wAlignas = "alignas", wAlignof = "alignof", wConstexpr = "constexpr", wDecltype = "decltype", + wAlignas = "alignas", wAlignof = "alignof", wConstexpr = "constexpr", wDecltype = "decltype", wNullptr = "nullptr", wNoexcept = "noexcept", - wThread_local = "thread_local", wStatic_assert = "static_assert", + wThread_local = "thread_local", wStatic_assert = "static_assert", wChar16_t = "char16_t", wChar32_t = "char32_t", wStdIn = "stdin", wStdOut = "stdout", wStdErr = "stderr", @@ -134,14 +134,14 @@ const enumUtilsExist = compiles: when enumUtilsExist: from std/enumutils import genEnumCaseStmt from strutils import normalize - proc findStr*[T: enum](a, b: static[T], s: string, default: T): T = + proc findStr*[T: enum](a, b: static[T], s: string, default: T): T = genEnumCaseStmt(T, s, default, ord(a), ord(b), normalize) else: from strutils import cmpIgnoreStyle - proc findStr*[T: enum](a, b: static[T], s: string, default: T): T {.deprecated.} = + proc findStr*[T: enum](a, b: static[T], s: string, default: T): T {.deprecated.} = # used for compiler bootstrapping only for i in a..b: if cmpIgnoreStyle($i, s) == 0: return i - result = default \ No newline at end of file + result = default \ No newline at end of file diff --git a/lib/pure/cgi.nim b/lib/pure/cgi.nim index fd5089dbb..566482b21 100644 --- a/lib/pure/cgi.nim +++ b/lib/pure/cgi.nim @@ -29,7 +29,7 @@ ## writeLine(stdout, "your password: " & myData["password"]) ## writeLine(stdout, "") -import std/[strutils, os, strtabs, cookies, uri] +import strutils, os, strtabs, cookies, uri export uri.encodeUrl, uri.decodeUrl diff --git a/lib/pure/collections/deques.nim b/lib/pure/collections/deques.nim index 582bb02a7..c378dff1d 100644 --- a/lib/pure/collections/deques.nim +++ b/lib/pure/collections/deques.nim @@ -50,7 +50,7 @@ runnableExamples: import std/private/since -import std/math +import math type Deque*[T] = object diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index 9387807c0..8739b60a3 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -196,7 +196,7 @@ runnableExamples: import std/private/since -import std/[hashes, math, algorithm] +import hashes, math, algorithm type KeyValuePair[A, B] = tuple[hcode: Hash, key: A, val: B] diff --git a/lib/pure/complex.nim b/lib/pure/complex.nim index b9371c1e1..5492028a8 100644 --- a/lib/pure/complex.nim +++ b/lib/pure/complex.nim @@ -36,7 +36,7 @@ runnableExamples: {.push checks: off, line_dir: off, stack_trace: off, debugger: off.} # the user does not want to trace a part of the standard library! -import std/math +import math type Complex*[T: SomeFloat] = object diff --git a/lib/pure/cookies.nim b/lib/pure/cookies.nim index 8d9cc0c95..25d701eb4 100644 --- a/lib/pure/cookies.nim +++ b/lib/pure/cookies.nim @@ -9,7 +9,7 @@ ## This module implements helper procs for parsing Cookies. -import std/[strtabs, times, options] +import strtabs, times, options type @@ -25,7 +25,7 @@ proc parseCookies*(s: string): StringTableRef = ## "Set-Cookie" header set by servers. runnableExamples: import std/strtabs - let cookieJar = parseCookies("a=1; foo=bar") + let cookieJar = parseCookies("a=1; foo=bar") assert cookieJar["a"] == "1" assert cookieJar["foo"] == "bar" @@ -46,7 +46,7 @@ proc parseCookies*(s: string): StringTableRef = proc setCookie*(key, value: string, domain = "", path = "", expires = "", noName = false, - secure = false, httpOnly = false, + secure = false, httpOnly = false, maxAge = none(int), sameSite = SameSite.Default): string = ## Creates a command in the format of ## `Set-Cookie: key=value; Domain=...; ...` diff --git a/lib/pure/distros.nim b/lib/pure/distros.nim index 2b119b92c..c1bc44eeb 100644 --- a/lib/pure/distros.nim +++ b/lib/pure/distros.nim @@ -27,11 +27,11 @@ ## ## See `packaging `_ for hints on distributing Nim using OS packages. -from std/strutils import contains, toLowerAscii +from strutils import contains, toLowerAscii when not defined(nimscript): - from std/osproc import execProcess - from std/os import existsEnv + from osproc import execProcess + from os import existsEnv type Distribution* {.pure.} = enum ## the list of known distributions diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 7ee5be9c5..96ee61fd5 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -159,10 +159,9 @@ runnableExamples: a1, a2, a0, a3, a4: int doAssert $(%* Foo()) == """{"a1":0,"a2":0,"a0":0,"a3":0,"a4":0}""" -import - std/[hashes, tables, strutils, lexbase, streams, macros, parsejson] +import hashes, tables, strutils, lexbase, streams, macros, parsejson -import std/options # xxx remove this dependency using same approach as https://github.com/nim-lang/Nim/pull/14563 +import options # xxx remove this dependency using same approach as https://github.com/nim-lang/Nim/pull/14563 import std/private/since export @@ -921,7 +920,7 @@ proc parseJson*(s: Stream, filename: string = ""; rawIntegers = false, rawFloats p.close() when defined(js): - from std/math import `mod` + from math import `mod` from std/jsffi import JSObject, `[]`, to from std/private/jsutils import getProtoName, isInteger, isSafeInteger diff --git a/lib/pure/marshal.nim b/lib/pure/marshal.nim index 936b8fe94..993e0f510 100644 --- a/lib/pure/marshal.nim +++ b/lib/pure/marshal.nim @@ -54,7 +54,7 @@ Please use alternative packages for serialization. It is possible to reimplement this module using generics and type traits. Please contribute a new implementation.""".} -import std/[streams, typeinfo, json, intsets, tables, unicode] +import streams, typeinfo, json, intsets, tables, unicode proc ptrToInt(x: pointer): int {.inline.} = result = cast[int](x) # don't skip alignment diff --git a/lib/pure/math.nim b/lib/pure/math.nim index 03e5040ae..cff946581 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -58,7 +58,7 @@ import std/private/since {.push debugger: off.} # the user does not want to trace a part # of the standard library! -import std/[bitops, fenv] +import bitops, fenv when defined(c) or defined(cpp): proc c_isnan(x: float): bool {.importc: "isnan", header: "".} diff --git a/lib/pure/oids.nim b/lib/pure/oids.nim index fb70047b6..da285a187 100644 --- a/lib/pure/oids.nim +++ b/lib/pure/oids.nim @@ -15,7 +15,7 @@ ## This implementation calls `initRand()` for the first call of ## `genOid`. -import std/[hashes, times, endians, random] +import hashes, times, endians, random from std/private/decode_helpers import handleHexChar type diff --git a/lib/pure/options.nim b/lib/pure/options.nim index 63e3598f0..bd5aa389e 100644 --- a/lib/pure/options.nim +++ b/lib/pure/options.nim @@ -69,7 +69,7 @@ supports pattern matching on `Option`s, with the `Some()` and # xxx pending https://github.com/timotheecour/Nim/issues/376 use `runnableExamples` and `whichModule` -import std/typetraits +import typetraits when (NimMajor, NimMinor) >= (1, 1): type diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 0710b8333..50a88b43b 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -44,7 +44,7 @@ include system/inclrtl import std/private/since -import std/[strutils, pathnorm] +import strutils, pathnorm const weirdTarget = defined(nimscript) or defined(js) @@ -65,9 +65,9 @@ since (1, 1): when weirdTarget: discard elif defined(windows): - import std/[winlean, times] + import winlean, times elif defined(posix): - import std/[posix, times] + import posix, times proc toTime(ts: Timespec): times.Time {.inline.} = result = initTime(ts.tv_sec.int64, ts.tv_nsec.int) @@ -1008,7 +1008,7 @@ proc expandTilde*(path: string): string {. ## ## Windows: this is still supported despite Windows platform not having this ## convention; also, both ``~/`` and ``~\`` are handled. - ## + ## ## .. warning:: `~bob` and `~bob/` are not yet handled correctly. ## ## See also: diff --git a/lib/pure/random.nim b/lib/pure/random.nim index f91d92731..e58623baa 100644 --- a/lib/pure/random.nim +++ b/lib/pure/random.nim @@ -72,7 +72,7 @@ runnableExamples: ## * `list of cryptographic and hashing modules `_ ## in the standard library -import std/[algorithm, math] +import algorithm, math import std/private/since include system/inclrtl @@ -625,7 +625,7 @@ proc shuffle*[T](x: var openArray[T]) = shuffle(state, x) when not defined(nimscript) and not defined(standalone): - import std/times + import times proc initRand(): Rand = ## Initializes a new Rand state with a seed based on the current time. diff --git a/lib/pure/rationals.nim b/lib/pure/rationals.nim index a059651bb..800979cda 100644 --- a/lib/pure/rationals.nim +++ b/lib/pure/rationals.nim @@ -21,7 +21,7 @@ runnableExamples: doAssert r1 * r2 == -3 // 8 doAssert r1 / r2 == -2 // 3 -import std/[math, hashes] +import math, hashes type Rational*[T] = object ## A rational number, consisting of a numerator `num` and a denominator `den`. diff --git a/lib/pure/ropes.nim b/lib/pure/ropes.nim index 42550af1d..1300b4479 100644 --- a/lib/pure/ropes.nim +++ b/lib/pure/ropes.nim @@ -17,7 +17,7 @@ ## runtime efficiency. include system/inclrtl -import std/streams +import streams {.push debugger: off.} # the user does not want to trace a part # of the standard library! diff --git a/lib/pure/stats.nim b/lib/pure/stats.nim index 6e2d5fecd..e46dff212 100644 --- a/lib/pure/stats.nim +++ b/lib/pure/stats.nim @@ -53,7 +53,7 @@ runnableExamples: doAssert statistics.kurtosis() ~= -1.0 doAssert statistics.kurtosisS() ~= -0.7000000000000008 -from std/math import FloatClass, sqrt, pow, round +from math import FloatClass, sqrt, pow, round {.push debugger: off.} # the user does not want to trace a part # of the standard library! diff --git a/lib/pure/strformat.nim b/lib/pure/strformat.nim index 736b4d501..52731e970 100644 --- a/lib/pure/strformat.nim +++ b/lib/pure/strformat.nim @@ -313,8 +313,8 @@ help with readability, since there is only so much you can cram into single letter DSLs. ]## -import std/[macros, parseutils, unicode] -import std/strutils except format +import macros, parseutils, unicode +import strutils except format proc mkDigit(v: int, typ: char): string {.inline.} = assert(v < 26) diff --git a/lib/pure/sugar.nim b/lib/pure/sugar.nim index 239ed9cf1..9abef55a2 100644 --- a/lib/pure/sugar.nim +++ b/lib/pure/sugar.nim @@ -11,7 +11,7 @@ ## macro system. import std/private/since -import std/macros +import macros proc checkPragma(ex, prag: var NimNode) = since (1, 3): diff --git a/lib/pure/typetraits.nim b/lib/pure/typetraits.nim index 2527dc26e..3e215a0ea 100644 --- a/lib/pure/typetraits.nim +++ b/lib/pure/typetraits.nim @@ -167,7 +167,7 @@ since (1, 3, 5): typeof(block: (for ai in a: ai)) -import std/macros +import macros macro enumLen*(T: typedesc[enum]): int = ## Returns the number of items in the enum `T`. diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim index 3cff833e4..80a05db36 100644 --- a/lib/pure/unittest.nim +++ b/lib/pure/unittest.nim @@ -108,15 +108,15 @@ import std/private/since import std/exitprocs -import std/[macros, strutils, streams, times, sets, sequtils] +import macros, strutils, streams, times, sets, sequtils when declared(stdout): - import std/os + import os const useTerminal = not defined(js) when useTerminal: - import std/terminal + import terminal type TestStatus* = enum ## The status of a test when it is done. @@ -747,7 +747,7 @@ macro expect*(exceptions: varargs[typed], body: untyped): untyped = of 2: discard parseInt("Hello World!") of 3: raise newException(IOError, "I can't do that Dave.") else: assert 2 + 2 == 5 - + expect IOError, OSError, ValueError, AssertionDefect: defectiveRobot() diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim index d2a9f3848..cf712fa89 100644 --- a/lib/pure/uri.nim +++ b/lib/pure/uri.nim @@ -39,7 +39,7 @@ runnableExamples: doAssert getDataUri("Nim", "text/plain") == "data:text/plain;charset=utf-8;base64,Tmlt" -import std/[strutils, parseutils, base64] +import strutils, parseutils, base64 import std/private/[since, decode_helpers] diff --git a/lib/std/enumutils.nim b/lib/std/enumutils.nim index 16dab9d1a..6195ae07d 100644 --- a/lib/std/enumutils.nim +++ b/lib/std/enumutils.nim @@ -7,16 +7,16 @@ # distribution, for details about the copyright. # -import std/macros -from std/typetraits import OrdinalEnum, HoleyEnum +import macros +from typetraits import OrdinalEnum, HoleyEnum # xxx `genEnumCaseStmt` needs tests and runnableExamples -macro genEnumCaseStmt*(typ: typedesc, argSym: typed, default: typed, +macro genEnumCaseStmt*(typ: typedesc, argSym: typed, default: typed, userMin, userMax: static[int], normalizer: static[proc(s :string): string]): untyped = # generates a case stmt, which assigns the correct enum field given # a normalized string comparison to the `argSym` input. - # string normalization is done using passed normalizer. + # string normalization is done using passed normalizer. # NOTE: for an enum with fields Foo, Bar, ... we cannot generate # `of "Foo".nimIdentNormalize: Foo`. # This will fail, if the enum is not defined at top level (e.g. in a block). diff --git a/lib/std/jsonutils.nim b/lib/std/jsonutils.nim index a5daa9fb4..4c27bc9f2 100644 --- a/lib/std/jsonutils.nim +++ b/lib/std/jsonutils.nim @@ -13,7 +13,7 @@ runnableExamples: let j = a.toJson assert j.jsonTo(typeof(a)).toJson == j -import std/[json,strutils,tables,sets,strtabs,options] +import json, strutils, tables, sets, strtabs, options #[ Future directions: @@ -27,7 +27,7 @@ add a way to customize serialization, for e.g.: objects. ]# -import std/macros +import macros type Joptions* = object @@ -106,7 +106,7 @@ proc hasField[T](obj: T, field: string): bool = return true return false -macro accessField(obj: typed, name: static string): untyped = +macro accessField(obj: typed, name: static string): untyped = newDotExpr(obj, ident(name)) template fromJsonFields(newObj, oldObj, json, discKeys, opt) = @@ -146,7 +146,7 @@ template fromJsonFields(newObj, oldObj, json, discKeys, opt) = json.len == numMatched else: json.len == num and num == numMatched - + checkJson ok, $(json.len, num, numMatched, $T, json) proc fromJson*[T](a: var T, b: JsonNode, opt = Joptions()) @@ -284,7 +284,7 @@ proc toJson*[T](a: T): JsonNode = proc fromJsonHook*[K: string|cstring, V](t: var (Table[K, V] | OrderedTable[K, V]), jsonNode: JsonNode) = ## Enables `fromJson` for `Table` and `OrderedTable` types. - ## + ## ## See also: ## * `toJsonHook proc<#toJsonHook>`_ runnableExamples: @@ -326,7 +326,7 @@ proc toJsonHook*[K: string|cstring, V](t: (Table[K, V] | OrderedTable[K, V])): J proc fromJsonHook*[A](s: var SomeSet[A], jsonNode: JsonNode) = ## Enables `fromJson` for `HashSet` and `OrderedSet` types. - ## + ## ## See also: ## * `toJsonHook proc<#toJsonHook,SomeSet[A]>`_ runnableExamples: @@ -360,7 +360,7 @@ proc toJsonHook*[A](s: SomeSet[A]): JsonNode = proc fromJsonHook*[T](self: var Option[T], jsonNode: JsonNode) = ## Enables `fromJson` for `Option` types. - ## + ## ## See also: ## * `toJsonHook proc<#toJsonHook,Option[T]>`_ runnableExamples: @@ -395,7 +395,7 @@ proc toJsonHook*[T](self: Option[T]): JsonNode = proc fromJsonHook*(a: var StringTableRef, b: JsonNode) = ## Enables `fromJson` for `StringTableRef` type. - ## + ## ## See also: ## * `toJsonHook proc<#toJsonHook,StringTableRef>`_ runnableExamples: @@ -413,7 +413,7 @@ proc fromJsonHook*(a: var StringTableRef, b: JsonNode) = proc toJsonHook*(a: StringTableRef): JsonNode = ## Enables `toJson` for `StringTableRef` type. - ## + ## ## See also: ## * `fromJsonHook proc<#fromJsonHook,StringTableRef,JsonNode>`_ runnableExamples: diff --git a/lib/std/monotimes.nim b/lib/std/monotimes.nim index 78736d719..8f6aa5b66 100644 --- a/lib/std/monotimes.nim +++ b/lib/std/monotimes.nim @@ -39,7 +39,7 @@ See also * `times module `_ ]## -import std/times +import times type MonoTime* = object ## Represents a monotonic timestamp. @@ -77,7 +77,7 @@ when defined(js): {.pop.} elif defined(posix) and not defined(osx): - import std/posix + import posix elif defined(windows): proc QueryPerformanceCounter(res: var uint64) {. diff --git a/lib/std/private/globs.nim b/lib/std/private/globs.nim index b98a7808b..a32f1d1b9 100644 --- a/lib/std/private/globs.nim +++ b/lib/std/private/globs.nim @@ -4,7 +4,7 @@ this can eventually be moved to std/os and `walkDirRec` can be implemented in te to avoid duplication ]## -import std/[os] +import os when defined(windows): from strutils import replace diff --git a/lib/std/setutils.nim b/lib/std/setutils.nim index c7fac0a54..aba5f348a 100644 --- a/lib/std/setutils.nim +++ b/lib/std/setutils.nim @@ -14,7 +14,7 @@ ## * `std/packedsets `_ ## * `std/sets `_ -import std/[typetraits, macros] +import typetraits, macros #[ type SetElement* = char|byte|bool|int16|uint16|enum|uint8|int8 diff --git a/lib/std/sha1.nim b/lib/std/sha1.nim index 9c5efed1f..bda0c6de7 100644 --- a/lib/std/sha1.nim +++ b/lib/std/sha1.nim @@ -26,8 +26,8 @@ runnableExamples("-r:off"): b = parseSecureHash("10DFAEBF6BFDBC7939957068E2EFACEC4972933C") assert a == b, "files don't match" -import std/strutils -from std/endians import bigEndian32, bigEndian64 +import strutils +from endians import bigEndian32, bigEndian64 const Sha1DigestSize = 20 diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index 4a2cdd10b..4c51577ac 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -10,7 +10,7 @@ ## .. warning:: This module was added in Nim 1.6. If you are using it for cryptographic purposes, ## keep in mind that so far this has not been audited by any security professionals, ## therefore may not be secure. -## +## ## `std/sysrand` generates random numbers from a secure source provided by the operating system. ## It is a cryptographically secure pseudorandom number generator ## and should be unpredictable enough for cryptographic applications, @@ -52,10 +52,10 @@ runnableExamples: when not defined(js): - import std/os + import os when defined(posix): - import std/posix + import posix const batchImplOS = defined(freebsd) or defined(openbsd) or (defined(macosx) and not defined(ios)) @@ -243,7 +243,7 @@ elif defined(macosx): """ proc getentropy(p: pointer, size: csize_t): cint {.importc: "getentropy", header: sysrandomHeader.} - # getentropy() fills a buffer with random data, which can be used as input + # getentropy() fills a buffer with random data, which can be used as input # for process-context pseudorandom generators like arc4random(3). # The maximum buffer size permitted is 256 bytes. @@ -310,7 +310,7 @@ proc urandom*(dest: var openArray[byte]): bool = proc urandom*(size: Natural): seq[byte] {.inline.} = ## Returns random bytes suitable for cryptographic use. - ## + ## ## .. warning:: The code hasn't been audited by cryptography experts and ## is provided as-is without guarantees. Use at your own risks. For production ## systems we advise you to request an external audit. diff --git a/lib/std/wrapnils.nim b/lib/std/wrapnils.nim index 3ff48fbfe..708faf4cf 100644 --- a/lib/std/wrapnils.nim +++ b/lib/std/wrapnils.nim @@ -24,7 +24,7 @@ runnableExamples: assert (?.f2.x2.x2).x3 == nil # this terminates ?. early -from std/options import Option, isSome, get, option, unsafeGet, UnpackDefect +from options import Option, isSome, get, option, unsafeGet, UnpackDefect export options.get, options.isSome, options.isNone template fakeDot*(a: Option, b): untyped = @@ -59,7 +59,7 @@ func `[]`*[U](a: Option[U]): auto {.inline.} = if a2 != nil: result = option(a2[]) -import std/macros +import macros func replace(n: NimNode): NimNode = if n.kind == nnkDotExpr: From f1ce173283a06197cfde80e9117ba1aa0ed0dd02 Mon Sep 17 00:00:00 2001 From: Joey Date: Tue, 20 Apr 2021 23:42:04 -0600 Subject: [PATCH 1986/3768] Fix Httpclient headers from being modified accidentally (#17808) --- lib/pure/httpclient.nim | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 14bcfd2fb..b60894103 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -962,12 +962,15 @@ proc format(client: HttpClient | AsyncHttpClient, proc override(fallback, override: HttpHeaders): HttpHeaders = # Right-biased map union for `HttpHeaders` - if override.isNil: - return fallback result = newHttpHeaders() # Copy by value result.table[] = fallback.table[] + + if override.isNil: + # Return the copy of fallback so it does not get modified + return result + for k, vs in override.table: result[k] = vs From 2951f89374e9797fa5567b5afd4effb3149c1fd5 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 21 Apr 2021 01:52:17 -0700 Subject: [PATCH 1987/3768] improve errmsg refs #17793 object ctor with generics (#17806) --- compiler/semobjconstr.nim | 5 ++++- compiler/types.nim | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim index 01d5a8b28..6f3cabcbf 100644 --- a/compiler/semobjconstr.nim +++ b/compiler/semobjconstr.nim @@ -11,6 +11,8 @@ # included from sem.nim +from sugar import dup + type ObjConstrContext = object typ: PType # The constructed type @@ -389,7 +391,8 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = # multiple times as long as they don't have closures. result.typ.flags.incl tfHasOwned if t.kind != tyObject: - return localErrorNode(c, result, "object constructor needs an object type") + return localErrorNode(c, result, + "object constructor needs an object type".dup(addDeclaredLoc(c.config, t))) # Check if the object is fully initialized by recursively testing each # field (if this is a case object, initialized fields in two different diff --git a/compiler/types.nim b/compiler/types.nim index e56fcffe4..ba2cc76ce 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -130,7 +130,7 @@ proc addDeclaredLocMaybe*(result: var string, conf: ConfigRef; sym: PSym) = if optDeclaredLocs in conf.globalOptions and sym != nil: addDeclaredLoc(result, conf, sym) -proc addDeclaredLoc(result: var string, conf: ConfigRef; typ: PType) = +proc addDeclaredLoc*(result: var string, conf: ConfigRef; typ: PType) = let typ = typ.skipTypes(abstractInst - {tyRange}) result.add " [$1" % typ.kind.toHumanStr if typ.sym != nil: From c631648cb31b11d61d249e0745181acb0fcc30cc Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 21 Apr 2021 21:07:36 +0800 Subject: [PATCH 1988/3768] close #9372 add std/tempfiles (#17361) * close #9372 add std/tempfile --- changelog.md | 3 + lib/std/tempfiles.nim | 139 ++++++++++++++++++++++++++++++++++++ tests/stdlib/ttempfiles.nim | 17 +++++ 3 files changed, 159 insertions(+) create mode 100644 lib/std/tempfiles.nim create mode 100644 tests/stdlib/ttempfiles.nim diff --git a/changelog.md b/changelog.md index 0537b3d6a..abd2f8733 100644 --- a/changelog.md +++ b/changelog.md @@ -283,9 +283,12 @@ - Added `hasClosure` to `std/typetraits`. +- Added `std/tempfiles`. + - Added `genasts.genAst` that avoids the problems inherent with `quote do` and can be used as a replacement. + ## Language changes - `nimscript` now handles `except Exception as e`. diff --git a/lib/std/tempfiles.nim b/lib/std/tempfiles.nim new file mode 100644 index 000000000..1e1bbd403 --- /dev/null +++ b/lib/std/tempfiles.nim @@ -0,0 +1,139 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2021 Nim contributors +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module creates temporary files and directories. + +import os, random + + +const + maxRetry = 10000 + letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" + nimTempPathLength {.intdefine.} = 8 + + +when defined(windows): + import winlean + + var O_RDWR {.importc: "_O_RDWR", header: "".}: cint + + proc c_fdopen( + filehandle: cint, + mode: cstring + ): File {.importc: "_fdopen",header: "".} + + proc open_osfhandle(osh: Handle, mode: cint): cint {. + importc: "_open_osfhandle", header: "".} + + proc close_osfandle(fd: cint): cint {. + importc: "_close", header: "".} +else: + import posix + + proc c_fdopen( + filehandle: cint, + mode: cstring + ): File {.importc: "fdopen",header: "".} + + +proc safeOpen(filename: string): File = + ## Open files exclusively. + when defined(windows): + let dwShareMode = FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE + let dwCreation = CREATE_NEW + let dwFlags = FILE_FLAG_BACKUP_SEMANTICS or FILE_ATTRIBUTE_NORMAL + let handle = createFileW(newWideCString(filename), GENERIC_READ or GENERIC_WRITE, dwShareMode, + nil, dwCreation, dwFlags, Handle(0)) + + if handle == INVALID_HANDLE_VALUE: + raiseOSError(osLastError(), filename) + + let fileHandle = open_osfhandle(handle, O_RDWR) + if fileHandle == -1: + discard closeHandle(handle) + raiseOSError(osLastError(), filename) + + result = c_fdopen(fileHandle, "w+") + if result == nil: + discard close_osfandle(fileHandle) + raiseOSError(osLastError(), filename) + else: + let flags = posix.O_RDWR or posix.O_CREAT or posix.O_EXCL + + let fileHandle = posix.open(filename, flags) + if fileHandle == -1: + raiseOSError(osLastError(), filename) + + result = c_fdopen(fileHandle, "w+") + if result == nil: + discard posix.close(fileHandle) # TODO handles failure when closing file + raiseOSError(osLastError(), filename) + +template randomPathName(length: Natural): string = + var res = newString(length) + var state = initRand() + for i in 0 ..< length: + res[i] = state.sample(letters) + res + +proc createTempFile*(prefix, suffix: string, dir = ""): tuple[fd: File, path: string] = + ## `createTempFile` creates a new temporary file in the directory `dir`. + ## + ## If `dir` is the empty string, the default directory for temporary files + ## (`getTempDir `_) will be used. + ## The temporary file name begins with `prefix` and ends with `suffix`. + ## `createTempFile` returns a file handle to an open file and the path of that file. + ## + ## If failing to create a temporary file, `IOError` will be raised. + ## + ## .. note:: It is the caller's responsibility to remove the file when no longer needed. + ## + var dir = dir + if dir.len == 0: + dir = getTempDir() + + createDir(dir) + + for i in 0 ..< maxRetry: + result.path = dir / (prefix & randomPathName(nimTempPathLength) & suffix) + try: + result.fd = safeOpen(result.path) + except OSError: + continue + return + + raise newException(IOError, "Failed to create a temporary file under directory " & dir) + +proc createTempDir*(prefix, suffix: string, dir = ""): string = + ## `createTempDir` creates a new temporary directory in the directory `dir`. + ## + ## If `dir` is the empty string, the default directory for temporary files + ## (`getTempDir `_) will be used. + ## The temporary directory name begins with `prefix` and ends with `suffix`. + ## `createTempDir` returns the path of that temporary firectory. + ## + ## If failing to create a temporary directory, `IOError` will be raised. + ## + ## .. note:: It is the caller's responsibility to remove the directory when no longer needed. + ## + var dir = dir + if dir.len == 0: + dir = getTempDir() + + createDir(dir) + + for i in 0 ..< maxRetry: + result = dir / (prefix & randomPathName(nimTempPathLength) & suffix) + try: + if not existsOrCreateDir(result): + return + except OSError: + continue + + raise newException(IOError, "Failed to create a temporary directory under directory " & dir) diff --git a/tests/stdlib/ttempfiles.nim b/tests/stdlib/ttempfiles.nim new file mode 100644 index 000000000..29ef2fb2b --- /dev/null +++ b/tests/stdlib/ttempfiles.nim @@ -0,0 +1,17 @@ +import std/[os, tempfiles] + + +doAssert createTempDir("nim", "tmp") != createTempDir("nim", "tmp") + +block: + var t1 = createTempFile("nim", ".tmp") + var t2 = createTempFile("nim", ".tmp") + doAssert t1.path != t2.path + + write(t1.fd, "1234") + doAssert readAll(t2.fd) == "" + + close(t1.fd) + close(t2.fd) + removeFile(t1.path) + removeFile(t2.path) From da1c1a711780e21a372ef70f9080aebe5b9ef987 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 21 Apr 2021 06:26:27 -0700 Subject: [PATCH 1989/3768] `--filenames:abs|canonical|legacyRelProj` for filenames in compiler msgs (replaces `--listfullpaths:on|off`) (#17746) * use canonicalImport for filename_magicSauce * --filenames:abs|canonical|magic * rename: magic => legacyRelProj --- changelog.md | 2 ++ compiler/commands.nim | 18 +++++++++++++++++- compiler/docgen.nim | 15 --------------- compiler/main.nim | 8 +++++--- compiler/msgs.nim | 24 +++++++++--------------- compiler/options.nim | 37 +++++++++++++++++++++++++++++++++---- doc/advopt.txt | 4 +++- drnim/drnim.nim | 3 ++- tests/config.nims | 2 +- 9 files changed, 72 insertions(+), 41 deletions(-) diff --git a/changelog.md b/changelog.md index abd2f8733..30fe237be 100644 --- a/changelog.md +++ b/changelog.md @@ -341,6 +341,8 @@ - Added `--spellSuggest` to show spelling suggestions on typos. +- Added `--filenames:abs|canonical|magic` which replaces --listFullPaths:on|off + - Source+Edit links now appear on top of every docgen'd page when `nim doc --git.url:url ...` is given. diff --git a/compiler/commands.nim b/compiler/commands.nim index 5d6e45fb5..0733e500a 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -141,6 +141,15 @@ proc splitSwitch(conf: ConfigRef; switch: string, cmd, arg: var string, pass: TC elif switch[i] == '[': arg = substr(switch, i) else: invalidCmdLineOption(conf, pass, switch, info) +template switchOn(arg: string): bool = + # xxx use `switchOn` wherever appropriate + case arg.normalize + of "", "on": true + of "off": false + else: + localError(conf, info, errOnOrOffExpectedButXFound % arg) + false + proc processOnOffSwitch(conf: ConfigRef; op: TOptions, arg: string, pass: TCmdLinePass, info: TLineInfo) = case arg.normalize @@ -885,8 +894,15 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; trackIde(conf, ideDus, arg, info) of "stdout": processOnOffSwitchG(conf, {optStdout}, arg, pass, info) + of "filenames": + case arg.normalize + of "abs": conf.filenameOption = foAbs + of "canonical": conf.filenameOption = foCanonical + of "legacyrelproj": conf.filenameOption = foLegacyRelProj + else: localError(conf, info, "expected: abs|canonical|legacyRelProj, got: $1" % arg) of "listfullpaths": - processOnOffSwitchG(conf, {optListFullPaths}, arg, pass, info) + # xxx in future work, use `warningDeprecated` + conf.filenameOption = if switchOn(arg): foAbs else: foCanonical of "spellsuggest": if arg.len == 0: conf.spellSuggestMax = spellSuggestSecretSauce elif arg == "auto": conf.spellSuggestMax = spellSuggestSecretSauce diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 4d9f09ca6..977fcf8ef 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -63,21 +63,6 @@ proc prettyString(a: object): string = for k, v in fieldPairs(a): result.add k & ": " & $v & "\n" -proc canonicalImport*(conf: ConfigRef, file: AbsoluteFile): string = - ##[ - Shows the canonical module import, e.g.: - system, std/tables, fusion/pointers, system/assertions, std/private/asciitables - ]## - var ret = getRelativePathFromConfigPath(conf, file, isTitle = true) - let dir = getNimbleFile(conf, $file).parentDir.AbsoluteDir - if not dir.isEmpty: - let relPath = relativeTo(file, dir) - if not relPath.isEmpty and (ret.isEmpty or relPath.string.len < ret.string.len): - ret = relPath - if ret.isEmpty: - ret = relativeTo(file, conf.projectPath) - result = ret.string.nativeToUnixPath.changeFileExt("") - proc presentationPath*(conf: ConfigRef, file: AbsoluteFile): RelativeFile = ## returns a relative file that will be appended to outDir let file2 = $file diff --git a/compiler/main.nim b/compiler/main.nim index d66a5f329..9c9a789cb 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -362,6 +362,7 @@ proc mainCommand*(graph: ModuleGraph) = rawMessage(conf, errGenerated, "invalid command: " & conf.command) if conf.errorCounter == 0 and conf.cmd notin {cmdTcc, cmdDump, cmdNop}: + # D20210419T170230:here let mem = when declared(system.getMaxMem): formatSize(getMaxMem()) & " peakmem" else: formatSize(getTotalMem()) & " totmem" @@ -370,8 +371,8 @@ proc mainCommand*(graph: ModuleGraph) = elif isDefined(conf, "release"): "Release" else: "Debug" let sec = formatFloat(epochTime() - conf.lastCmdTime, ffDecimal, 3) - let project = if optListFullPaths in conf.globalOptions: $conf.projectFull else: $conf.projectName - + let project = if conf.filenameOption == foAbs: $conf.projectFull else: $conf.projectName + # xxx honor conf.filenameOption more accurately var output: string if optCompileOnly in conf.globalOptions and conf.cmd != cmdJsonscript: output = $conf.jsonBuildFile @@ -380,7 +381,8 @@ proc mainCommand*(graph: ModuleGraph) = output = "unknownOutput" else: output = $conf.absOutFile - if optListFullPaths notin conf.globalOptions: output = output.AbsoluteFile.extractFilename + if conf.filenameOption != foAbs: output = output.AbsoluteFile.extractFilename + # xxx honor filenameOption more accurately if optProfileVM in conf.globalOptions: echo conf.dump(conf.vmProfileData) rawMessage(conf, hintSuccessX, [ diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 89949ef17..b7b6a582e 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -241,37 +241,30 @@ template toFullPath*(conf: ConfigRef; info: TLineInfo): string = template toFullPathConsiderDirty*(conf: ConfigRef; info: TLineInfo): string = string toFullPathConsiderDirty(conf, info.fileIndex) -type - FilenameOption* = enum - foAbs # absolute path, e.g.: /pathto/bar/foo.nim - foRelProject # relative to project path, e.g.: ../foo.nim - foMagicSauce # magic sauce, shortest of (foAbs, foRelProject) - foName # lastPathPart, e.g.: foo.nim - foStacktrace # if optExcessiveStackTrace: foAbs else: foName - proc toFilenameOption*(conf: ConfigRef, fileIdx: FileIndex, opt: FilenameOption): string = case opt of foAbs: result = toFullPath(conf, fileIdx) of foRelProject: result = toProjPath(conf, fileIdx) - of foMagicSauce: + of foCanonical: + let absPath = toFullPath(conf, fileIdx) + result = canonicalImportAux(conf, absPath.AbsoluteFile) + of foName: result = toProjPath(conf, fileIdx).lastPathPart + of foLegacyRelProj: let absPath = toFullPath(conf, fileIdx) relPath = toProjPath(conf, fileIdx) - result = if (optListFullPaths in conf.globalOptions) or - (relPath.len > absPath.len) or - (relPath.count("..") > 2): + result = if (relPath.len > absPath.len) or (relPath.count("..") > 2): absPath else: relPath - of foName: result = toProjPath(conf, fileIdx).lastPathPart of foStacktrace: if optExcessiveStackTrace in conf.globalOptions: result = toFilenameOption(conf, fileIdx, foAbs) else: result = toFilenameOption(conf, fileIdx, foName) -proc toMsgFilename*(conf: ConfigRef; info: FileIndex): string = - toFilenameOption(conf, info, foMagicSauce) +proc toMsgFilename*(conf: ConfigRef; fileIdx: FileIndex): string = + toFilenameOption(conf, fileIdx, conf.filenameOption) template toMsgFilename*(conf: ConfigRef; info: TLineInfo): string = toMsgFilename(conf, info.fileIndex) @@ -558,6 +551,7 @@ proc liMessage*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string, styledMsgWriteln(styleBright, loc, resetStyle, color, title, resetStyle, s, KindColor, kindmsg, resetStyle, conf.getSurroundingSrc(info), UnitSep) if hintMsgOrigin in conf.mainPackageNotes: + # xxx needs a bit of refactoring to honor `conf.filenameOption` styledMsgWriteln(styleBright, toFileLineCol(info2), resetStyle, " compiler msg initiated here", KindColor, KindFormat % $hintMsgOrigin, diff --git a/compiler/options.nim b/compiler/options.nim index c1ac6b1e0..2aaaf5844 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -13,7 +13,7 @@ import from terminal import isatty from times import utc, fromUnix, local, getTime, format, DateTime - +from std/private/globs import nativeToUnixPath const hasTinyCBackend* = defined(tinyc) useEffectSystem* = true @@ -78,7 +78,6 @@ type # please make sure we have under 32 options optWholeProject # for 'doc': output any dependency optDocInternal # generate documentation for non-exported symbols optMixedMode # true if some module triggered C++ codegen - optListFullPaths # use full paths in toMsgFilename optDeclaredLocs # show declaration locations in messages optNoNimblePath optHotCodeReloading @@ -258,6 +257,14 @@ type stdOrrStdout stdOrrStderr + FilenameOption* = enum + foAbs # absolute path, e.g.: /pathto/bar/foo.nim + foRelProject # relative to project path, e.g.: ../foo.nim + foCanonical # canonical module name + foLegacyRelProj # legacy, shortest of (foAbs, foRelProject) + foName # lastPathPart, e.g.: foo.nim + foStacktrace # if optExcessiveStackTrace: foAbs else: foName + ConfigRef* = ref object ## every global configuration ## fields marked with '*' are subject to ## the incremental compilation mechanisms @@ -270,6 +277,7 @@ type macrosToExpand*: StringTableRef arcToExpand*: StringTableRef m*: MsgConfig + filenameOption*: FilenameOption # how to render paths in compiler messages evalTemplateCounter*: int evalMacroCounter*: int exitcode*: int8 @@ -413,8 +421,7 @@ const optBoundsCheck, optOverflowCheck, optAssert, optWarns, optRefCheck, optHints, optStackTrace, optLineTrace, # consider adding `optStackTraceMsgs` optTrMacros, optStyleCheck, optCursorInference} - DefaultGlobalOptions* = {optThreadAnalysis, - optExcessiveStackTrace, optListFullPaths} + DefaultGlobalOptions* = {optThreadAnalysis, optExcessiveStackTrace} proc getSrcTimestamp(): DateTime = try: @@ -461,6 +468,7 @@ proc newConfigRef*(): ConfigRef = macrosToExpand: newStringTable(modeStyleInsensitive), arcToExpand: newStringTable(modeStyleInsensitive), m: initMsgConfig(), + filenameOption: foAbs, cppDefines: initHashSet[string](), headerFile: "", features: {}, legacyFeatures: {}, foreignPackageNotes: foreignPackageNotesDefault, notes: NotesVerbosity[1], mainPackageNotes: NotesVerbosity[1], @@ -514,6 +522,7 @@ proc newConfigRef*(): ConfigRef = proc newPartialConfigRef*(): ConfigRef = ## create a new ConfigRef that is only good enough for error reporting. + # xxx FACTOR with `newConfigRef` when defined(nimDebugUtils): result = getConfigRef() else: @@ -522,6 +531,7 @@ proc newPartialConfigRef*(): ConfigRef = verbosity: 1, options: DefaultOptions, globalOptions: DefaultGlobalOptions, + filenameOption: foAbs, foreignPackageNotes: foreignPackageNotesDefault, notes: NotesVerbosity[1], mainPackageNotes: NotesVerbosity[1]) @@ -885,6 +895,25 @@ proc findProjectNimFile*(conf: ConfigRef; pkg: string): string = if dir == "": break return "" +proc canonicalImportAux*(conf: ConfigRef, file: AbsoluteFile): string = + ##[ + Shows the canonical module import, e.g.: + system, std/tables, fusion/pointers, system/assertions, std/private/asciitables + ]## + var ret = getRelativePathFromConfigPath(conf, file, isTitle = true) + let dir = getNimbleFile(conf, $file).parentDir.AbsoluteDir + if not dir.isEmpty: + let relPath = relativeTo(file, dir) + if not relPath.isEmpty and (ret.isEmpty or relPath.string.len < ret.string.len): + ret = relPath + if ret.isEmpty: + ret = relativeTo(file, conf.projectPath) + result = ret.string + +proc canonicalImport*(conf: ConfigRef, file: AbsoluteFile): string = + let ret = canonicalImportAux(conf, file) + result = ret.nativeToUnixPath.changeFileExt("") + proc canonDynlibName(s: string): string = let start = if s.startsWith("lib"): 3 else: 0 let ende = strutils.find(s, {'(', ')', '.'}) diff --git a/doc/advopt.txt b/doc/advopt.txt index bfc381cff..539cc620c 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -36,7 +36,9 @@ Advanced options: to after all options have been processed --stdout:on|off output to stdout --colors:on|off turn compiler messages coloring on|off - --listFullPaths:on|off list full paths in messages + --filenames:abs|canonical|legacyRelProj + customize how filenames are rendered in compiler messages, + defaults to `abs` (absolute) --declaredLocs:on|off show declaration locations in messages --spellSuggest|:num show at most `num >= 0` spelling suggestions on typos. if `num` is not specified (or `auto`), return diff --git a/drnim/drnim.nim b/drnim/drnim.nim index d549e1d5b..a591a8ef3 100644 --- a/drnim/drnim.nim +++ b/drnim/drnim.nim @@ -1205,6 +1205,7 @@ proc mainCommand(graph: ModuleGraph) = registerPass graph, semPass compileProject(graph) if conf.errorCounter == 0: + # xxx deduplicate with D20210419T170230 let mem = when declared(system.getMaxMem): formatSize(getMaxMem()) & " peakmem" else: formatSize(getTotalMem()) & " totmem" @@ -1213,7 +1214,7 @@ proc mainCommand(graph: ModuleGraph) = elif isDefined(conf, "release"): "Release" else: "Debug" let sec = formatFloat(epochTime() - conf.lastCmdTime, ffDecimal, 3) - let project = if optListFullPaths in conf.globalOptions: $conf.projectFull else: $conf.projectName + let project = if conf.filenameOption == foAbs: $conf.projectFull else: $conf.projectName rawMessage(conf, hintSuccessX, [ "loc", loc, "sec", sec, diff --git a/tests/config.nims b/tests/config.nims index 41edf0005..e5d6545f4 100644 --- a/tests/config.nims +++ b/tests/config.nims @@ -6,7 +6,7 @@ switch("path", "$lib/../testament/lib") ## prevent common user config settings to interfere with testament expectations ## Indifidual tests can override this if needed to test for these options. switch("colors", "off") -switch("listFullPaths", "off") +switch("filenames", "legacyRelProj") switch("excessiveStackTrace", "off") switch("spellSuggest", "0") From 7c64e49d452607fa246b54e931c057ed172b264a Mon Sep 17 00:00:00 2001 From: Clyybber Date: Wed, 21 Apr 2021 15:28:42 +0200 Subject: [PATCH 1990/3768] getCustomPragmaVal priority/override fixes (#17725) * Adhere left-to-right rule for custom pragma priority * Improve error message for no custom pragmas * custom pragmas on var/let sym take priority over its type ones * Workaround & bug --- lib/core/macros.nim | 78 ++++++++++++++++---------------- tests/pragmas/tcustom_pragma.nim | 10 ++++ 2 files changed, 50 insertions(+), 38 deletions(-) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index fc68781da..79c3ba28b 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -1536,6 +1536,14 @@ proc getPragmaNodeFromTypeSym(sym: NimNode): NimNode = if pragmaExpr.kind == nnkPragmaExpr: result = pragmaExpr[1] +proc getPragmaNodeFromType(typ: NimNode): NimNode = + case typ.kind + of nnkSym: + result = getPragmaNodeFromTypeSym(typ) + of nnkProcTy: + result = typ[1] + else: error("illegal typ kind for argument: " & $typ.kind, typ) + proc getPragmaNodeFromVarLetSym(sym: NimNode): NimNode = sym.expectKind nnkSym if sym.symKind notin {nskVar, nskLet}: error("expected var/let sym", sym) @@ -1546,73 +1554,64 @@ proc getPragmaNodeFromVarLetSym(sym: NimNode): NimNode = if pragmaExpr.kind == nnkPragmaExpr: result = pragmaExpr[1] -proc getPragmaByName(pragmaExpr: NimNode, name: string): NimNode = +proc getPragmasByName(pragmaExpr: NimNode, name: string): seq[NimNode] = if pragmaExpr.kind == nnkPragma: for it in pragmaExpr: if it.kind in nnkPragmaCallKinds: if eqIdent(it[0], name): - return it + result.add it elif it.kind == nnkSym: if eqIdent(it, name): - return it + result.add it -proc getCustomPragmaNode(sym: NimNode, name: string): NimNode = +proc getCustomPragmaNodes(sym: NimNode, name: string): seq[NimNode] = sym.expectKind nnkSym case sym.symKind of nskField: - result = getPragmaNodeFromObjFieldSym(sym).getPragmaByName(name) + result = getPragmaNodeFromObjFieldSym(sym).getPragmasByName(name) of nskProc: - result = getPragmaNodeFromProcSym(sym).getPragmaByName(name) + result = getPragmaNodeFromProcSym(sym).getPragmasByName(name) of nskType: - result = getPragmaNodeFromTypeSym(sym).getPragmaByName(name) + result = getPragmaNodeFromTypeSym(sym).getPragmasByName(name) of nskParam: # When a typedesc parameter is passed to the macro, it will be of nskParam. let typeInst = getTypeInst(sym) if typeInst.kind == nnkBracketExpr and eqIdent(typeInst[0], "typeDesc"): - result = getPragmaNodeFromTypeSym(typeInst[1]).getPragmaByName(name) + result = getPragmaNodeFromTypeSym(typeInst[1]).getPragmasByName(name) else: error("illegal sym kind for argument: " & $sym.symKind, sym) of nskVar, nskLet: - # I think it is a bad idea to fall back to the typeSym. The API - # explicity requests a var/let symbol, not a type symbol. - result = getPragmaNodeFromVarLetSym(sym).getPragmaByName(name) or - getPragmaNodeFromTypeSym(sym.getTypeInst).getPragmaByName(name) + # This checks the type of the sym too, this is consistent with how + # field expressions are handled too. If this is changed, make sure to + # change it for fields expressions too. + result = getPragmaNodeFromType(sym.getTypeInst).getPragmasByName(name) + result.add getPragmaNodeFromVarLetSym(sym).getPragmasByName(name) else: error("illegal sym kind for argument: " & $sym.symKind, sym) since (1, 5): - export getCustomPragmaNode + export getCustomPragmaNodes proc hasCustomPragma*(n: NimNode, name: string): bool = n.expectKind nnkSym - let pragmaNode = getCustomPragmaNode(n, name) - result = pragmaNode != nil + result = getCustomPragmaNodes(n, name).len > 0 -proc getCustomPragmaNodeSmart(n: NimNode, name: string): NimNode = +proc getCustomPragmaNodesSmart(n: NimNode, name: string): seq[NimNode] = case n.kind of nnkDotExpr: - result = getCustomPragmaNode(n[1], name) + result = getCustomPragmaNodes(n[1], name) of nnkCheckedFieldExpr: expectKind n[0], nnkDotExpr - result = getCustomPragmaNode(n[0][1], name) + result = getCustomPragmaNodes(n[0][1], name) of nnkSym: - result = getCustomPragmaNode(n, name) + result = getCustomPragmaNodes(n, name) of nnkTypeOfExpr: - var typeSym = n.getTypeInst - while typeSym.kind == nnkBracketExpr and typeSym[0].eqIdent "typeDesc": - typeSym = typeSym[1] - case typeSym.kind: - of nnkSym: - result = getCustomPragmaNode(typeSym, name) - of nnkProcTy: - # It is a bad idea to support this. The annotation can't be part - # of a symbol. - let pragmaExpr = typeSym[1] - result = getPragmaByName(pragmaExpr, name) - else: - typeSym.expectKind nnkSym + var typ = n.getTypeInst + while typ.kind == nnkBracketExpr and typ[0].eqIdent "typeDesc": + typ = typ[1] + result = getPragmaNodeFromType(typ).getPragmasByName(name) of nnkBracketExpr: - result = nil #false + discard else: n.expectKind({nnkDotExpr, nnkCheckedFieldExpr, nnkSym, nnkTypeOfExpr}) @@ -1633,7 +1632,7 @@ macro hasCustomPragma*(n: typed, cp: typed{nkSym}): bool = ## var o: MyObj ## assert(o.myField.hasCustomPragma(myAttr)) ## assert(myProc.hasCustomPragma(myAttr)) - result = newLit(getCustomPragmaNodeSmart(n, $cp) != nil) + result = newLit(getCustomPragmaNodesSmart(n, $cp).len > 0) iterator iterOverFormalArgs(f: NimNode): tuple[name, typ, val: NimNode] = f.expectKind nnkFormalParams @@ -1644,7 +1643,7 @@ iterator iterOverFormalArgs(f: NimNode): tuple[name, typ, val: NimNode] = for j in 0.. Date: Wed, 21 Apr 2021 16:03:30 +0200 Subject: [PATCH 1991/3768] Revert localErrorNode param order changes (#17809) * Revert localErrorNode param order changes * Remove unused globalError overload * heh --- compiler/msgs.nim | 3 --- compiler/semdata.nim | 18 ++++++++++++++---- compiler/semexprs.nim | 2 +- compiler/semmagic.nim | 8 ++++---- compiler/semstmts.nim | 2 +- 5 files changed, 20 insertions(+), 13 deletions(-) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index b7b6a582e..f08722cc7 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -585,9 +585,6 @@ template globalError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") template globalError*(conf: ConfigRef; info: TLineInfo, arg: string) = liMessage(conf, info, errGenerated, arg, doRaise, instLoc()) -template globalError*(conf: ConfigRef; format: string, params: openArray[string]) = - liMessage(conf, unknownLineInfo, errGenerated, format % params, doRaise, instLoc()) - template localError*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") = liMessage(conf, info, msg, arg, doNothing, instLoc()) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 9a17efb1c..1c1a5159c 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -501,13 +501,23 @@ proc errorNode*(c: PContext, n: PNode): PNode = result = newNodeI(nkEmpty, n.info) result.typ = errorType(c) -template localErrorNode*(c: PContext, n: PNode, arg: string, info: TLineInfo, kind: TMsgKind = errGenerated): PNode = - liMessage(c.config, info, kind, arg, doNothing, instLoc()) +# These mimic localError +template localErrorNode*(c: PContext, n: PNode, info: TLineInfo, msg: TMsgKind, arg: string): PNode = + liMessage(c.config, info, msg, arg, doNothing, instLoc()) errorNode(c, n) -template localErrorNode*(c: PContext, n: PNode, arg: string, kind: TMsgKind = errGenerated): PNode = +template localErrorNode*(c: PContext, n: PNode, info: TLineInfo, arg: string): PNode = + liMessage(c.config, info, errGenerated, arg, doNothing, instLoc()) + errorNode(c, n) + +template localErrorNode*(c: PContext, n: PNode, msg: TMsgKind, arg: string): PNode = let n2 = n - liMessage(c.config, n2.info, kind, arg, doNothing, instLoc()) + liMessage(c.config, n2.info, msg, arg, doNothing, instLoc()) + errorNode(c, n2) + +template localErrorNode*(c: PContext, n: PNode, arg: string): PNode = + let n2 = n + liMessage(c.config, n2.info, errGenerated, arg, doNothing, instLoc()) errorNode(c, n2) proc fillTypeS*(dest: PType, kind: TTypeKind, c: PContext) = diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index defceb291..353831736 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2620,7 +2620,7 @@ proc semTupleConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = # check if either everything or nothing is tyTypeDesc for i in 1.. high(TSymChoiceRule).int: - return localErrorNode(c, n, errConstExprExpected, n[2].info) + return localErrorNode(c, n, n[2].info, errConstExprExpected) let id = newIdentNode(getIdent(c.cache, sl.strVal), n.info) let s = qualifiedLookUp(c, id, {checkUndeclared}) @@ -251,10 +251,10 @@ proc semBindSym(c: PContext, n: PNode): PNode = proc opBindSym(c: PContext, scope: PScope, n: PNode, isMixin: int, info: PNode): PNode = if n.kind notin {nkStrLit, nkRStrLit, nkTripleStrLit, nkIdent}: - return localErrorNode(c, n, errStringOrIdentNodeExpected, info.info) + return localErrorNode(c, n, info.info, errStringOrIdentNodeExpected) if isMixin < 0 or isMixin > high(TSymChoiceRule).int: - return localErrorNode(c, n, errConstExprExpected, info.info) + return localErrorNode(c, n, info.info, errConstExprExpected) let id = if n.kind == nkIdent: n else: newIdentNode(getIdent(c.cache, n.strVal), info.info) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 22e4295fa..d56c1c1bd 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -725,7 +725,7 @@ proc semForVars(c: PContext, n: PNode; flags: TExprFlags): PNode = if n.len == 3: if n[0].kind == nkVarTuple: if n[0].len-1 != iterAfterVarLent.len: - return localErrorNode(c, n, errWrongNumberOfVariables, n[0].info) + return localErrorNode(c, n, n[0].info, errWrongNumberOfVariables) for i in 0.. Date: Wed, 21 Apr 2021 17:57:54 +0300 Subject: [PATCH 1992/3768] add RST highlighting for command line / shells (also fixes #16858) (#17789) --- config/nimdoc.tex.cfg | 2 + doc/contributing.rst | 177 +++++++++++---------- doc/docstyle.rst | 16 +- doc/nimdoc.css | 31 +++- lib/packages/docutils/highlite.nim | 83 +++++++++- lib/packages/docutils/rst.nim | 49 +++--- lib/packages/docutils/rstast.nim | 4 +- lib/packages/docutils/rstgen.nim | 18 ++- nimdoc/testproject/expected/nimdoc.out.css | 31 +++- tests/stdlib/trstgen.nim | 20 ++- 10 files changed, 291 insertions(+), 140 deletions(-) diff --git a/config/nimdoc.tex.cfg b/config/nimdoc.tex.cfg index 69266f85d..471f20dc1 100644 --- a/config/nimdoc.tex.cfg +++ b/config/nimdoc.tex.cfg @@ -137,6 +137,8 @@ bottomline=false} \newcommand{\spanReference}[1]{#1} \newcommand{\spanOther}[1]{#1} \newcommand{\spantok}[1]{\frame{#1}} +\newcommand{\spanprogram}[1]{\textbf{\underline{#1}}} +\newcommand{\spanoption}[1]{\textbf{#1}} $content \end{document} diff --git a/doc/contributing.rst b/doc/contributing.rst index 3d3da2ce0..81fa9f8eb 100644 --- a/doc/contributing.rst +++ b/doc/contributing.rst @@ -1,9 +1,10 @@ -.. default-role:: code - ============ Contributing ============ +.. default-role:: code +.. include:: rstcommon.rst + .. contents:: @@ -19,21 +20,23 @@ Writing tests There are 4 types of tests: -1. `runnableExamples` documentation comment tests, ran by `nim doc mymod.nim` +1. `runnableExamples` documentation comment tests, ran by `nim doc mymod.nim`:cmd: These end up in documentation and ensure documentation stays in sync with code. -2. separate test files, e.g.: `tests/stdlib/tos.nim`. - In nim repo, `testament` (see below) runs all `$nim/tests/*/t*.nim` test files; +2. separate test files, e.g.: ``tests/stdlib/tos.nim``. + In nim repo, `testament`:cmd: (see below) runs all + ``$nim/tests/*/t*.nim`` test files; for nimble packages, see https://github.com/nim-lang/nimble#tests. -3. (deprecated) tests in `when isMainModule:` block, ran by `nim r mymod.nim`. - `nimble test` can run those in nimble packages when specified in a +3. (deprecated) tests in `when isMainModule:` block, ran by `nim r mymod.nim`:cmd:. + `nimble test`:cmd: can run those in nimble packages when specified in a `task "test"`. -4. (not preferred) `.. code-block:: nim` RST snippets; these should only be used in rst sources, +4. (not preferred) ``.. code-block:: nim`` RST snippets; + these should only be used in rst sources, in nim sources `runnableExamples` should now always be preferred to those for several reasons (cleaner syntax, syntax highlights, batched testing, and - `rdoccmd` allows customization). + parameter `rdoccmd` allows customization). Not all the tests follow the convention here, feel free to change the ones that don't. Always leave the code cleaner than you found it. @@ -41,8 +44,8 @@ that don't. Always leave the code cleaner than you found it. Stdlib ------ -Each stdlib module (anything under `lib/`, e.g. `lib/pure/os.nim`) should -preferably have a corresponding separate test file, e.g. `tests/stdlib/tos.nim`. +Each stdlib module (anything under ``lib/``, e.g. ``lib/pure/os.nim``) should +preferably have a corresponding separate test file, e.g. ``tests/stdlib/tos.nim``. The old convention was to add a `when isMainModule:` block in the source file, which only gets executed when the tester is building the file. @@ -71,36 +74,36 @@ Sample test: # doAssert with `not` can now be done as follows: doAssert not (1 == 2) -Always refer to a GitHub issue using the following exact syntax: `bug #1234` as shown +Always refer to a GitHub issue using the following exact syntax: ``bug #1234`` as shown above, so that it's consistent and easier to search or for tooling. Some browser extensions (e.g. https://github.com/sindresorhus/refined-github) will even turn those in clickable links when it works. Rationale for using a separate test file instead of `when isMainModule:` block: * allows custom compiler flags or testing options (see details below) -* faster CI since they can be joined in `megatest` (combined into a single test) +* faster CI since they can be joined in ``megatest`` (combined into a single test) * avoids making the parser do un-necessary work when a source file is merely imported * avoids mixing source and test code when reporting line of code statistics or code coverage Compiler -------- -The tests for the compiler use a testing tool called `testament`. They are all -located in `tests/` (e.g.: `tests/destructor/tdestructor3.nim`). +The tests for the compiler use a testing tool called `testament`:cmd:. They are all +located in ``tests/`` (e.g.: ``tests/destructor/tdestructor3.nim``). Each test has its own file. All test files are prefixed with `t`. If you want to create a file for import into another test only, use the prefix `m`. At the beginning of every test is the expected behavior of the test. Possible keys are: -- `cmd`: A compilation command template e.g. `nim $target --threads:on $options $file` +- `cmd`: A compilation command template e.g. `nim $target --threads:on $options $file`:cmd: - `output`: The expected output (stdout + stderr), most likely via `echo` - `exitcode`: Exit code of the test (via `exit(number)`) - `errormsg`: The expected compiler error message - `file`: The file the errormsg was produced at - `line`: The line the errormsg was produced at -For a full spec, see here: `testament/specs.nim` +For a full spec, see here: ``testament/specs.nim`` An example of a test: @@ -124,51 +127,51 @@ Running tests You can run the tests with -:: +.. code-block:: cmd ./koch tests which will run a good subset of tests. Some tests may fail. If you only want to see the output of failing tests, go for -:: - +```cmd ./koch tests --failing all +``` You can also run only a single category of tests. A category is a subdirectory -in the `tests` directory. There are a couple of special categories; for a -list of these, see `testament/categories.nim`, at the bottom. +in the ``tests/`` directory. There are a couple of special categories; for a +list of these, see ``testament/categories.nim``, at the bottom. -:: +.. code:: cmd - ./koch tests c lib # compiles/runs stdlib modules, including `isMainModule` tests + ./koch tests c lib # compiles / runs stdlib modules, including `isMainModule` tests ./koch tests c megatest # runs a set of tests that can be combined into 1 To run a single test: -:: +.. code:: cmd ./koch test run / # e.g.: tuples/ttuples_issues ./koch test run tests/stdlib/tos.nim # can also provide relative path For reproducible tests (to reproduce an environment more similar to the one run by Continuous Integration on travis/appveyor), you may want to disable your -local configuration (e.g. in `~/.config/nim/nim.cfg`) which may affect some +local configuration (e.g. in ``~/.config/nim/nim.cfg``) which may affect some tests; this can also be achieved by using -`export XDG_CONFIG_HOME=pathtoAlternateConfig` before running `./koch` +`export XDG_CONFIG_HOME=pathtoAlternateConfig`:cmd: before running `./koch`:cmd: commands. Comparing tests =============== -Test failures can be grepped using `Failure:`. +Test failures can be grepped using ``Failure:``. The tester can compare two test runs. First, you need to create a reference test. You'll also need to the commit id, because that's what the tester needs to know in order to compare the two. -:: +.. code:: cmd git checkout devel DEVEL_COMMIT=$(git rev-parse HEAD) @@ -176,15 +179,15 @@ the tester needs to know in order to compare the two. Then switch over to your changes and run the tester again. -:: +.. code:: cmd git checkout your-changes ./koch tests -Then you can ask the tester to create a `testresults.html` which will +Then you can ask the tester to create a ``testresults.html`` which will tell you if any new tests passed/failed. -:: +.. code:: cmd ./koch tests --print html $DEVEL_COMMIT @@ -219,14 +222,14 @@ Documentation When contributing new procs, be sure to add documentation, especially if the proc is public. Even private procs benefit from documentation and can be -viewed using `nim doc --docInternal foo.nim`. +viewed using `nim doc --docInternal foo.nim`:cmd:. Documentation begins on the line following the `proc` definition, and is prefixed by `##` on each line. Runnable code examples are also encouraged, to show typical behavior with a few test cases (typically 1 to 3 `assert` statements, depending on complexity). -These `runnableExamples` are automatically run by `nim doc mymodule.nim` -as well as `testament` and guarantee they stay in sync. +These `runnableExamples` are automatically run by `nim doc mymodule.nim`:cmd: +as well as `testament`:cmd: and guarantee they stay in sync. .. code-block:: nim proc addBar*(a: string): string = @@ -238,7 +241,7 @@ as well as `testament` and guarantee they stay in sync. See `parentDir `_ example. The RestructuredText Nim uses has a special syntax for including code snippets -embedded in documentation; these are not run by `nim doc` and therefore are +embedded in documentation; these are not run by `nim doc`:cmd: and therefore are not guaranteed to stay in sync, so `runnableExamples` is almost always preferred: .. code-block:: nim @@ -250,9 +253,9 @@ not guaranteed to stay in sync, so `runnableExamples` is almost always preferred ## echo someProc() # "something" result = "something" # single-hash comments do not produce documentation -The `.. code-block:: nim` followed by a newline and an indentation instructs the -`nim doc` command to produce syntax-highlighted example code with the -documentation (`.. code-block::` is sufficient from inside a nim module). +The ``.. code-block:: nim`` followed by a newline and an indentation instructs the +`nim doc`:cmd: command to produce syntax-highlighted example code with the +documentation (``.. code-block::`` is sufficient from inside a nim module). When forward declaration is used, the documentation should be included with the first appearance of the proc. @@ -298,10 +301,10 @@ example below) from `Nim Index`_ can be used in doc comment this way: Inline monospaced text can be input using \`single backticks\` or \`\`double backticks\`\`. The former are syntactically highlighted, the latter are not. -To avoid accidental highlighting follow this rule in `*.nim` files: +To avoid accidental highlighting follow this rule in ``*.nim`` files: * use single backticks for fragments of code in Nim and other - programming languages, including identifiers, in `*.nim` files. + programming languages, including identifiers, in ``*.nim`` files. For languages other than Nim add a role after final backtick, e.g. for C++ inline highlighting:: @@ -313,18 +316,20 @@ To avoid accidental highlighting follow this rule in `*.nim` files: `SELECT * FROM ;`:code: + Highlight shell commands by ``:cmd:`` role; for command line options use + ``:option:`` role, e.g.: \`--docInternal\`:option:. + * prefer double backticks otherwise: * for file names: \`\`os.nim\`\` * for fragments of strings **not** enclosed by `"` and `"` and not related to code, e.g. text of compiler messages - * for command line options: \`\`--docInternal\`\` * also when code ends with a standalone ``\`` (otherwise a combination of ``\`` and a final \` would get escaped) -.. Note:: `*.rst` files have `:literal:` as their default role. - So for them the rule above is only applicable if the `:nim:` role - is set up manually as the default:: +.. Note:: ``*.rst`` files have ``:literal:`` as their default role. + So for them the rule above is only applicable if the ``:nim:`` role + is set up manually as the default [*]_:: .. role:: nim(code) :language: nim @@ -333,6 +338,8 @@ To avoid accidental highlighting follow this rule in `*.nim` files: The first 2 lines are for other RST implementations, including Github one. + .. [*] this is fulfilled when ``doc/rstcommon.rst`` is included. + Best practices ============== @@ -350,7 +357,7 @@ to avoid name conflicts across packages. # if in nim sources when defined(allocStats): discard # bad, can cause conflicts when defined(nimAllocStats): discard # preferred - # if in a pacakge `cligen`: + # if in a package `cligen`: when defined(debug): discard # bad, can cause conflicts when defined(cligenDebug): discard # preferred @@ -372,7 +379,7 @@ Design with method call syntax chaining in mind # can be called as: `getLines().foo(false)` .. _avoid_quit: -Use exceptions (including assert / doAssert) instead of `quit` +Use exceptions (including `assert` / `doAssert`) instead of `quit` rationale: https://forum.nim-lang.org/t/4089 .. code-block:: nim @@ -382,7 +389,7 @@ rationale: https://forum.nim-lang.org/t/4089 .. _tests_use_doAssert: Use `doAssert` (or `unittest.check`, `unittest.require`), not `assert` in all -tests so they'll be enabled even with `--assertions:off`. +tests so they'll be enabled even with `--assertions:off`:option:. .. code-block:: nim @@ -391,10 +398,10 @@ tests so they'll be enabled even with `--assertions:off`. doAssert foo() # preferred .. _runnableExamples_use_assert: -An exception to the above rule is `runnableExamples` and `code-block` rst blocks +An exception to the above rule is `runnableExamples` and ``code-block`` rst blocks intended to be used as `runnableExamples`, which for brevity use `assert` -instead of `doAssert`. Note that `nim doc -d:danger main` won't pass `-d:danger` to the -`runnableExamples`, but `nim doc --doccmd:-d:danger main` would, and so would the +instead of `doAssert`. Note that `nim doc -d:danger main`:cmd: won't pass `-d:danger`:option: to the +`runnableExamples`, but `nim doc --doccmd:-d:danger main`:cmd: would, and so would the second example below: .. code-block:: nim @@ -437,8 +444,8 @@ https://github.com/nim-lang/Nim/pull/9335 and https://forum.nim-lang.org/t/4089 doAssert foo() == [1, 2] # preferred, except when not possible to do so. -The Git stuff -============= +The `git`:cmd: stuff +==================== General commit rules -------------------- @@ -446,12 +453,12 @@ General commit rules 1. Important, critical bugfixes that have a tiny chance of breaking somebody's code should be backported to the latest stable release branch (currently 1.4.x) and maybe also all the way back to the 1.0.x branch. - The commit message should contain the tag `[backport]` for "backport to all - stable releases" and the tag `[backport:$VERSION]` for backporting to the + The commit message should contain the tag ``[backport]`` for "backport to all + stable releases" and the tag ``[backport:$VERSION]`` for backporting to the given $VERSION. 2. If you introduce changes which affect backward compatibility, - make breaking changes, or have PR which is tagged as `[feature]`, + make breaking changes, or have PR which is tagged as ``[feature]``, the changes should be mentioned in `the changelog `_. @@ -462,29 +469,29 @@ General commit rules your editor reformatted automatically the code or whatever different reason, this should be excluded from the commit. - *Tip:* Never commit everything as is using `git commit -a`, but review - carefully your changes with `git add -p`. + *Tip:* Never commit everything as is using `git commit -a`:cmd:, but review + carefully your changes with `git add -p`:cmd:. 4. Changes should not introduce any trailing whitespace. - Always check your changes for whitespace errors using `git diff --check` - or add the following `pre-commit` hook: + Always check your changes for whitespace errors using `git diff --check`:cmd: + or add the following ``pre-commit`` hook: - .. code-block:: sh + .. code:: cmd #!/bin/sh git diff --check --cached || exit $? 5. Describe your commit and use your common sense. - Example commit message: + Example commit message:: - `Fixes #123; refs #124` + Fixes #123; refs #124 - indicates that issue `#123` is completely fixed (GitHub may automatically - close it when the PR is committed), wheres issue `#124` is referenced + indicates that issue ``#123`` is completely fixed (GitHub may automatically + close it when the PR is committed), wheres issue ``#124`` is referenced (e.g.: partially fixed) and won't close the issue when committed. 6. PR body (not just PR title) should contain references to fixed/referenced github - issues, e.g.: `fix #123` or `refs #123`. This is so that you get proper cross + issues, e.g.: ``fix #123`` or ``refs #123``. This is so that you get proper cross referencing from linked issue to the PR (github won't make those links with just PR title, and commit messages aren't always sufficient to ensure that, e.g. can't be changed after a PR is merged). @@ -492,7 +499,7 @@ General commit rules 7. Commits should be always be rebased against devel (so a fast forward merge can happen) - e.g.: use `git pull --rebase origin devel`. This is to avoid messing up + e.g.: use `git pull --rebase origin devel`:cmd:. This is to avoid messing up git history. Exceptions should be very rare: when rebase gives too many conflicts, simply squash all commits using the script shown in @@ -508,7 +515,7 @@ Continuous Integration (CI) 1. Continuous Integration is by default run on every push in a PR; this clogs the CI pipeline and affects other PR's; if you don't need it (e.g. for WIP or - documentation only changes), add `[skip ci]` to your commit message title. + documentation only changes), add ``[skip ci]`` to your commit message title. This convention is supported by our github actions pipelines and our azure pipeline as well as our former other pipelines: `Appveyor `_ @@ -531,16 +538,16 @@ Debugging CI failures, flaky tests, etc will re-trigger all CI jobs (even successful ones, which can be wasteful). Instead, follow these instructions to only restart the jobs that failed: - * Azure: if on your own fork, it's possible from inside azure console - (e.g. `dev.azure.com/username/username/_build/results?buildId=1430&view=results`) via `rerun failed jobs` on top. - If either on you own fork or in Nim repo, it's possible from inside GitHub UI - under checks tab, see https://github.com/timotheecour/Nim/issues/211#issuecomment-629751569 - * GitHub actions: under "Checks" tab, click "Re-run jobs" in the right. - * builds.sr.ht: create a sourcehut account so you can restart a PR job as illustrated. - builds.sr.ht also allows you to ssh to a CI machine which can help a lot for debugging - issues, see docs in https://man.sr.ht/builds.sr.ht/build-ssh.md and - https://drewdevault.com/2019/08/19/Introducing-shell-access-for-builds.html; see - https://man.sr.ht/tutorials/set-up-account-and-git.md to generate and upload ssh keys. + * Azure: if on your own fork, it's possible from inside azure console + (e.g. ``dev.azure.com/username/username/_build/results?buildId=1430&view=results``) via ``rerun failed jobs`` on top. + If either on you own fork or in Nim repo, it's possible from inside GitHub UI + under checks tab, see https://github.com/timotheecour/Nim/issues/211#issuecomment-629751569 + * GitHub actions: under "Checks" tab, click "Re-run jobs" in the right. + * builds.sr.ht: create a sourcehut account so you can restart a PR job as illustrated. + builds.sr.ht also allows you to ssh to a CI machine which can help a lot for debugging + issues, see docs in https://man.sr.ht/builds.sr.ht/build-ssh.md and + https://drewdevault.com/2019/08/19/Introducing-shell-access-for-builds.html; see + https://man.sr.ht/tutorials/set-up-account-and-git.md to generate and upload ssh keys. Code reviews @@ -554,15 +561,15 @@ Code reviews doesn't help much as it doesn't highlight moves. Instead, you can use something like this, see visual results `here `_: - .. code-block:: sh + .. code:: cmd git fetch origin pull/10431/head && git checkout FETCH_HEAD git diff --color-moved-ws=allow-indentation-change --color-moved=blocks HEAD^ 3. In addition, you can view GitHub-like diffs locally to identify what was changed - within a code block using `diff-highlight` or `diff-so-fancy`, e.g.: + within a code block using `diff-highlight`:cmd: or `diff-so-fancy`:cmd:, e.g.: - .. code-block:: sh + :: # put this in ~/.gitconfig: [core] @@ -651,15 +658,15 @@ to existing modules is acceptable. For two reasons: Conventions ----------- -1. New stdlib modules should go under `Nim/lib/std/`. The rationale is to +1. New stdlib modules should go under ``Nim/lib/std/``. The rationale is to require users to import via `import std/foo` instead of `import foo`, which would cause potential conflicts with nimble packages. Note that this still applies for new modules in existing logical - directories, e.g.: use `lib/std/collections/foo.nim`, - not `lib/pure/collections/foo.nim`. + directories, e.g.: use ``lib/std/collections/foo.nim``, + not ``lib/pure/collections/foo.nim``. 2. New module names should prefer plural form whenever possible, e.g.: - `std/sums.nim` instead of `std/sum.nim`. In particular, this reduces + ``std/sums.nim`` instead of ``std/sum.nim``. In particular, this reduces chances of conflicts between module name and the symbols it defines. Furthermore, module names should use `snake_case` and not use capital letters, which cause issues when going from an OS without case diff --git a/doc/docstyle.rst b/doc/docstyle.rst index 7f3fa8cf2..df1f36dad 100644 --- a/doc/docstyle.rst +++ b/doc/docstyle.rst @@ -6,17 +6,17 @@ General Guidelines * See also `nep1`_ which should probably be merged here. * Authors should document anything that is exported; documentation for private - procs can be useful too (visible via `nim doc --docInternal foo.nim`). + procs can be useful too (visible via `nim doc --docInternal foo.nim`:cmd:). * Within documentation, a period (`.`) should follow each sentence (or sentence fragment) in a comment block. The documentation may be limited to one sentence fragment, but if multiple sentences are within the documentation, each sentence after the first should be complete and in present tense. * Documentation is parsed as a custom ReStructuredText (RST) with partial markdown support. * In nim sources, prefer single backticks to double backticks since it's simpler - and `nim doc` supports it. Likewise with rst files: `nim rst2html` will render those as monospace, and - adding `.. default-role:: code` to an rst file will also make those render as monospace when rendered directly + and `nim doc`:cmd: supports it. Likewise with ``rst`` files: `nim rst2html`:cmd: will render those as monospace, and + adding ``.. default-role:: code`` to an ``rst`` file will also make those render as monospace when rendered directly in tools such as github. -* In nim sources, for links, prefer `[link text](link.html)` to `` `link text`_ `` - since the syntax is simpler and markdown is more common (likewise, `nim rst2html` also supports it in rst files). +* (debatable) In nim sources, for links, prefer ``[link text](link.html)`` to `\`link text\`_`:code: + since the syntax is simpler and markdown is more common (likewise, `nim rst2html`:cmd: also supports it in ``rst`` files). .. code-block:: nim @@ -29,7 +29,7 @@ Module-level documentation -------------------------- Documentation of a module is placed at the top of the module itself. Each line of documentation begins with double hashes (`##`). -Sometimes `##[ multiline docs containing code ]##` is preferable, see `lib/pure/times.nim`. +Sometimes `##[ multiline docs containing code ]##` is preferable, see ``lib/pure/times.nim``. Code samples are encouraged, and should follow the general RST syntax: .. code-block:: Nim @@ -76,11 +76,11 @@ Whenever an example of usage would be helpful to the user, you should include on ## echo execCmdEx("git pull") ## drawOnScreen() runnableExamples: - # `runnableExamples` is usually preferred to `code-block`, when possible. + # `runnableExamples` is usually preferred to ``code-block``, when possible. doAssert addThree(3, 125, 6) == -122 result = x +% y +% z -The command `nim doc` will then correctly syntax highlight the Nim code within the documentation. +The command `nim doc`:cmd: will then correctly syntax highlight the Nim code within the documentation. Types ----- diff --git a/doc/nimdoc.css b/doc/nimdoc.css index ced791d16..6366d33dc 100644 --- a/doc/nimdoc.css +++ b/doc/nimdoc.css @@ -35,6 +35,8 @@ Modified by Boyd Greenfield and narimiran --escapeSequence: #c4891b; --number: #252dbe; --literal: #a4255b; + --program: #6060c0; + --option: #508000; --raw-data: #a4255b; } @@ -63,6 +65,8 @@ Modified by Boyd Greenfield and narimiran --escapeSequence: #bd93f9; --number: #bd93f9; --literal: #f1fa8c; + --program: #9090c0; + --option: #90b010; --raw-data: #8be9fd; } @@ -527,7 +531,6 @@ div.option-list-label { margin-left: -11.5em; margin-right: 0em; min-width: 11.5em; - font-weight: bolder; display: inline-block; vertical-align: top; } @@ -546,7 +549,7 @@ blockquote { border-left: 5px solid #bbc; } -.pre { +.pre, span.tok { font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace; font-weight: 500; font-size: 0.85em; @@ -557,6 +560,12 @@ blockquote { border-radius: 4px; } +span.tok { + border: 1px solid #808080; + padding-bottom: 0.1em; + margin-right: 0.2em; +} + pre { font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace; color: var(--text); @@ -844,9 +853,6 @@ span.classifier { span.classifier-delimiter { font-weight: bold; } -span.option { - white-space: nowrap; } - span.problematic { color: #b30000; } @@ -926,6 +932,21 @@ span.Preprocessor { span.Directive { color: #252dbe; } +span.option { + font-weight: bold; + font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace; + color: var(--option); +} + +span.program { + font-weight: bold; + color: var(--program); + text-decoration: underline; + text-decoration-color: var(--hint); + text-decoration-thickness: 0.05em; + text-underline-offset: 0.15em; +} + span.Command, span.Rule, span.Hyperlink, span.Label, span.Reference, span.Other { color: var(--other); } diff --git a/lib/packages/docutils/highlite.nim b/lib/packages/docutils/highlite.nim index 94cb2ebde..c0f4c9760 100644 --- a/lib/packages/docutils/highlite.nim +++ b/lib/packages/docutils/highlite.nim @@ -37,6 +37,18 @@ ## .. code:: Nim ## for l in ["C", "c++", "jAvA", "Nim", "c#"]: echo getSourceLanguage(l) ## +## There is also a `Cmd` pseudo-language supported, which is a simple generic +## shell/cmdline tokenizer (UNIX shell/Powershell/Windows Command): +## no escaping, no programming language constructs besides variable definition +## at the beginning of line. It supports these operators: +## +## .. code:: Cmd +## & && | || ( ) '' "" ; # for comments +## +## Instead of escaping always use quotes like here +## `nimgrep --ext:'nim|nims' file.name`:cmd: shows how to input ``|``. +## Any argument that contains ``.`` or ``/`` or ``\`` will be treated +## as a file or directory. import strutils @@ -45,7 +57,7 @@ from algorithm import binarySearch type SourceLanguage* = enum langNone, langNim, langCpp, langCsharp, langC, langJava, - langYaml, langPython + langYaml, langPython, langCmd TokenClass* = enum gtEof, gtNone, gtWhitespace, gtDecNumber, gtBinNumber, gtHexNumber, gtOctNumber, gtFloatNumber, gtIdentifier, gtKeyword, gtStringLit, @@ -53,7 +65,7 @@ type gtOperator, gtPunctuation, gtComment, gtLongComment, gtRegularExpression, gtTagStart, gtTagEnd, gtKey, gtValue, gtRawData, gtAssembler, gtPreprocessor, gtDirective, gtCommand, gtRule, gtHyperlink, gtLabel, - gtReference, gtOther + gtReference, gtProgram, gtOption, gtOther GeneralTokenizer* = object of RootObj kind*: TokenClass start*, length*: int @@ -64,14 +76,17 @@ type const sourceLanguageToStr*: array[SourceLanguage, string] = ["none", - "Nim", "C++", "C#", "C", "Java", "Yaml", "Python"] + "Nim", "C++", "C#", "C", "Java", "Yaml", "Python", "Cmd"] tokenClassToStr*: array[TokenClass, string] = ["Eof", "None", "Whitespace", "DecNumber", "BinNumber", "HexNumber", "OctNumber", "FloatNumber", "Identifier", "Keyword", "StringLit", "LongStringLit", "CharLit", "EscapeSequence", "Operator", "Punctuation", "Comment", "LongComment", "RegularExpression", "TagStart", "TagEnd", "Key", "Value", "RawData", "Assembler", "Preprocessor", "Directive", "Command", "Rule", "Hyperlink", - "Label", "Reference", "Other"] + "Label", "Reference", + # start from lower-case if there is a corresponding RST role (see rst.nim) + "program", "option", + "Other"] # The following list comes from doc/keywords.txt, make sure it is # synchronized with this array by running the module itself as a test case. @@ -898,6 +913,65 @@ proc pythonNextToken(g: var GeneralTokenizer) = "with", "yield"] nimNextToken(g, keywords) +proc cmdNextToken(g: var GeneralTokenizer) = + var pos = g.pos + g.start = g.pos + if g.state == low(TokenClass): + g.state = gtProgram + case g.buf[pos] + of ' ', '\t'..'\r': + g.kind = gtWhitespace + while g.buf[pos] in {' ', '\t'..'\r'}: + if g.buf[pos] == '\n': + g.state = gtProgram + inc(pos) + of '\'', '"': + g.kind = gtOption + let q = g.buf[pos] + inc(pos) + while g.buf[pos] notin {q, '\0'}: + inc(pos) + if g.buf[pos] == q: inc(pos) + of '#': + g.kind = gtComment + while g.buf[pos] notin {'\n', '\0'}: + inc(pos) + of '&', '|': + g.kind = gtOperator + inc(pos) + if g.buf[pos] == g.buf[pos-1]: inc(pos) + g.state = gtProgram + of '(': + g.kind = gtOperator + g.state = gtProgram + inc(pos) + of ')': + g.kind = gtOperator + inc(pos) + of ';': + g.state = gtProgram + g.kind = gtOperator + inc(pos) + of '\0': g.kind = gtEof + else: + if g.state == gtProgram: + g.kind = gtProgram + g.state = gtOption + else: + g.kind = gtOption + while g.buf[pos] notin {' ', '\t'..'\r', '&', '|', '(', ')', '\'', '"', '\0'}: + if g.buf[pos] == ';' and g.buf[pos+1] == ' ': + # (check space because ';' can be used inside arguments in Win bat) + break + if g.kind == gtOption and g.buf[pos] in {'/', '\\', '.'}: + g.kind = gtIdentifier # for file/dir name + elif g.kind == gtProgram and g.buf[pos] == '=': + g.kind = gtIdentifier # for env variable setting at beginning of line + g.state = gtProgram + inc(pos) + g.length = pos - g.pos + g.pos = pos + proc getNextToken*(g: var GeneralTokenizer, lang: SourceLanguage) = g.lang = lang case lang @@ -909,6 +983,7 @@ proc getNextToken*(g: var GeneralTokenizer, lang: SourceLanguage) = of langJava: javaNextToken(g) of langYaml: yamlNextToken(g) of langPython: pythonNextToken(g) + of langCmd: cmdNextToken(g) when isMainModule: var keywords: seq[string] diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 7c4b92f88..cb65791ff 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -23,10 +23,10 @@ ## ## Nim can output the result to HTML [#html]_ or Latex [#latex]_. ## -## .. [#html] commands ``nim doc`` for ``*.nim`` files and -## ``nim rst2html`` for ``*.rst`` files +## .. [#html] commands `nim doc`:cmd: for ``*.nim`` files and +## `nim rst2html`:cmd: for ``*.rst`` files ## -## .. [#latex] command ``nim rst2tex`` for ``*.rst``. +## .. [#latex] command `nim rst2tex`:cmd: for ``*.rst``. ## ## If you are new to RST please consider reading the following: ## @@ -78,14 +78,21 @@ ## ## * directives: ``code-block`` [cmp:Sphinx]_, ``title``, ## ``index`` [cmp:Sphinx]_ -## * predefined roles ``:nim:`` (default), ``:c:`` (C programming language), -## ``:python:``, ``:yaml:``, ``:java:``, ``:cpp:`` (C++), ``:csharp`` (C#). -## That is every language that `highlite `_ supports. -## They turn on appropriate syntax highlighting in inline code. +## * predefined roles +## - ``:nim:`` (default), ``:c:`` (C programming language), +## ``:python:``, ``:yaml:``, ``:java:``, ``:cpp:`` (C++), ``:csharp`` (C#). +## That is every language that `highlite `_ supports. +## They turn on appropriate syntax highlighting in inline code. ## -## .. Note:: default role for Nim files is ``:nim:``, -## for ``*.rst`` it's currently ``:literal:``. +## .. Note:: default role for Nim files is ``:nim:``, +## for ``*.rst`` it's currently ``:literal:``. ## +## - generic command line highlighting roles: +## - ``:cmd:`` for commands and common shells syntax +## - ``:program:`` for executable names [cmp:Sphinx]_ +## (one can just use ``:cmd:`` on single word) +## - ``:option:`` for command line options [cmp:Sphinx]_ +## - ``:tok:``, a role for highlighting of programming language tokens ## * ***triple emphasis*** (bold and italic) using \*\*\* ## * ``:idx:`` role for \`interpreted text\` to include the link to this ## text into an index (example: `Nim index`_). @@ -95,11 +102,11 @@ ## //compile compile the project ## //doc generate documentation ## -## Here the dummy `//` will disappear, while options ``compile`` -## and ``doc`` will be left in the final document. +## Here the dummy `//` will disappear, while options `compile`:option: +## and `doc`:option: will be left in the final document. ## ## .. [cmp:Sphinx] similar but different from the directives of -## Python `Sphinx directives`_ extensions +## Python `Sphinx directives`_ and `Sphinx roles`_ extensions ## ## .. _`extra features`: ## @@ -144,7 +151,7 @@ ## ----- ## ## See `Nim DocGen Tools Guide `_ for the details about -## ``nim doc``, ``nim rst2html`` and ``nim rst2tex`` commands. +## `nim doc`:cmd:, `nim rst2html`:cmd: and `nim rst2tex`:cmd: commands. ## ## See `packages/docutils/rstgen module `_ to know how to ## generate HTML or Latex strings to embed them into your documents. @@ -156,6 +163,7 @@ ## .. _RST roles list: https://docutils.sourceforge.io/docs/ref/rst/roles.html ## .. _Nim index: https://nim-lang.org/docs/theindex.html ## .. _Sphinx directives: https://www.sphinx-doc.org/en/master/usage/restructuredtext/directives.html +## .. _Sphinx roles: https://www.sphinx-doc.org/en/master/usage/restructuredtext/roles.html import os, strutils, rstast, std/enumutils, algorithm, lists, sequtils, @@ -530,7 +538,7 @@ proc defaultRole(options: RstParseOptions): string = # mirror highlite.nim sourceLanguageToStr with substitutions c++ cpp, c# csharp const supportedLanguages = ["nim", "yaml", "python", "java", "c", - "cpp", "csharp"] + "cpp", "csharp", "cmd"] proc whichRoleAux(sym: string): RstNodeKind = let r = sym.toLowerAscii @@ -543,6 +551,7 @@ proc whichRoleAux(sym: string): RstNodeKind = of "sup", "superscript": result = rnSup # literal and code are the same in our implementation of "code": result = rnInlineLiteral + of "program", "option", "tok": result = rnCodeFragment # c++ currently can be spelled only as cpp, c# only as csharp elif r in supportedLanguages: result = rnInlineCode @@ -1113,10 +1122,10 @@ proc toInlineCode(n: PRstNode, language: string): PRstNode = lb.add newLeaf(s) result.add lb -proc toUnknownRole(n: PRstNode, roleName: string): PRstNode = +proc toOtherRole(n: PRstNode, kind: RstNodeKind, roleName: string): PRstNode = let newN = newRstNode(rnInner, n.sons) let newSons = @[newN, newLeaf(roleName)] - result = newRstNode(rnUnknownRole, newSons) + result = newRstNode(kind, newSons) proc parsePostfix(p: var RstParser, n: PRstNode): PRstNode = var newKind = n.kind @@ -1144,8 +1153,8 @@ proc parsePostfix(p: var RstParser, n: PRstNode): PRstNode = # a role: let (roleName, lastIdx) = getRefname(p, p.idx+1) newKind = whichRole(p, roleName) - if newKind == rnUnknownRole: - result = n.toUnknownRole(roleName) + if newKind in {rnUnknownRole, rnCodeFragment}: + result = n.toOtherRole(newKind, roleName) elif newKind == rnInlineCode: result = n.toInlineCode(language=roleName) else: @@ -1417,8 +1426,8 @@ proc parseInline(p: var RstParser, father: PRstNode) = if k == rnInlineCode: n = n.toInlineCode(language=roleName) parseUntil(p, n, "`", false) # bug #17260 - if k == rnUnknownRole: - n = n.toUnknownRole(roleName) + if k in {rnUnknownRole, rnCodeFragment}: + n = n.toOtherRole(k, roleName) father.add(n) elif isInlineMarkupStart(p, "`"): var n = newRstNode(rnInterpretedText) diff --git a/lib/packages/docutils/rstast.nim b/lib/packages/docutils/rstast.nim index 00f3f2b35..81e3ba6d9 100644 --- a/lib/packages/docutils/rstast.nim +++ b/lib/packages/docutils/rstast.nim @@ -56,7 +56,9 @@ type # * `file#id '_ rnSubstitutionDef, # a definition of a substitution # Inline markup: - rnInlineCode, + rnInlineCode, # interpreted text with code in a known language + rnCodeFragment, # inline code for highlighting with the specified + # class (which cannot be inferred from context) rnUnknownRole, # interpreted text with an unknown role rnSub, rnSup, rnIdx, rnEmphasis, # "*" diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 1b9334a77..40ed88954 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -1198,7 +1198,8 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = "$1", result) of rnOptionGroup: renderAux(d, n, - "
    $1
    ", + "
    " & + "$1
    ", "\\item[$1]", result) of rnDescription: renderAux(d, n, "
    $1
    ", @@ -1319,13 +1320,22 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = renderAux(d, n, "|$1|", "|$1|", result) of rnDirective: renderAux(d, n, "", "", result) - of rnUnknownRole: + of rnUnknownRole, rnCodeFragment: var tmp0 = "" var tmp1 = "" renderRstToOut(d, n.sons[0], tmp0) renderRstToOut(d, n.sons[1], tmp1) - dispA(d.target, result, "$1", "\\span$2{$1}", - [tmp0, tmp1]) + var class = tmp1 + # don't allow missing role break latex compilation: + if d.target == outLatex and n.kind == rnUnknownRole: class = "Other" + if n.kind == rnCodeFragment: + dispA(d.target, result, + "" & + "$1", + "\\texttt{\\span$2{$1}}", [tmp0, class]) + else: # rnUnknownRole, not necessarily code/monospace font + dispA(d.target, result, "$1", "\\span$2{$1}", + [tmp0, class]) of rnSub: renderAux(d, n, "$1", "\\rstsub{$1}", result) of rnSup: renderAux(d, n, "$1", "\\rstsup{$1}", result) of rnEmphasis: renderAux(d, n, "$1", "\\emph{$1}", result) diff --git a/nimdoc/testproject/expected/nimdoc.out.css b/nimdoc/testproject/expected/nimdoc.out.css index ced791d16..6366d33dc 100644 --- a/nimdoc/testproject/expected/nimdoc.out.css +++ b/nimdoc/testproject/expected/nimdoc.out.css @@ -35,6 +35,8 @@ Modified by Boyd Greenfield and narimiran --escapeSequence: #c4891b; --number: #252dbe; --literal: #a4255b; + --program: #6060c0; + --option: #508000; --raw-data: #a4255b; } @@ -63,6 +65,8 @@ Modified by Boyd Greenfield and narimiran --escapeSequence: #bd93f9; --number: #bd93f9; --literal: #f1fa8c; + --program: #9090c0; + --option: #90b010; --raw-data: #8be9fd; } @@ -527,7 +531,6 @@ div.option-list-label { margin-left: -11.5em; margin-right: 0em; min-width: 11.5em; - font-weight: bolder; display: inline-block; vertical-align: top; } @@ -546,7 +549,7 @@ blockquote { border-left: 5px solid #bbc; } -.pre { +.pre, span.tok { font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace; font-weight: 500; font-size: 0.85em; @@ -557,6 +560,12 @@ blockquote { border-radius: 4px; } +span.tok { + border: 1px solid #808080; + padding-bottom: 0.1em; + margin-right: 0.2em; +} + pre { font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace; color: var(--text); @@ -844,9 +853,6 @@ span.classifier { span.classifier-delimiter { font-weight: bold; } -span.option { - white-space: nowrap; } - span.problematic { color: #b30000; } @@ -926,6 +932,21 @@ span.Preprocessor { span.Directive { color: #252dbe; } +span.option { + font-weight: bold; + font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace; + color: var(--option); +} + +span.program { + font-weight: bold; + color: var(--program); + text-decoration: underline; + text-decoration-color: var(--hint); + text-decoration-thickness: 0.05em; + text-underline-offset: 0.15em; +} + span.Command, span.Rule, span.Hyperlink, span.Label, span.Reference, span.Other { color: var(--other); } diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim index 29747f4e8..0af6ba566 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -40,6 +40,10 @@ proc toHtml(input: string, proc id(str: string): string = """""" & str & "" proc op(str: string): string = """""" & str & "" proc pu(str: string): string = """""" & str & "" +proc optionListLabel(opt: string): string = + """
    """ & + opt & + "
    " suite "YAML syntax highlighting": test "Basics": @@ -1382,10 +1386,10 @@ Test1 check(output.count("") == 2) check(output.count("
    -m
    """ & + check(optionListLabel("-m") & """
    desc
    """ in output) - check("""
    -n
    """ & + check(optionListLabel("-n") & """
    very long desc
    """ in output) @@ -1400,13 +1404,13 @@ Test1 let output = input.toHtml check(output.count("-m""" & + check(optionListLabel("-m") & """
    desc
    """ in output) - check("""
    -n
    """ & + check(optionListLabel("-n") & """
    very long desc
    """ in output) - check("""
    -d
    """ & + check(optionListLabel("-d") & """
    option
    """ in output) check "

    option

    " notin output @@ -1421,13 +1425,13 @@ Test1 let output = input.toHtml check(output.count("compile""" & + check(optionListLabel("compile") & """
    compile1
    """ in output) - check("""
    doc
    """ & + check(optionListLabel("doc") & """
    doc1 cont
    """ in output) - check("""
    -d
    """ & + check(optionListLabel("-d") & """
    option
    """ in output) check "

    option

    " notin output From 4471141a1d68158f3f23c6584d1f0434807c0203 Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 21 Apr 2021 23:53:31 +0800 Subject: [PATCH 1993/3768] fix #7535(Poor error message for spawn when a procedure (without calling it)) (#17774) --- compiler/semexprs.nim | 7 +++++-- tests/parallel/t7535.nim | 11 +++++++++++ 2 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 tests/parallel/t7535.nim diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 353831736..97f88869e 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2282,12 +2282,15 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = of mSpawn: markUsed(c, n.info, s) when defined(leanCompiler): - localError(c.config, n.info, "compiler was built without 'spawn' support") - result = n + result = localErrorNode(c, n, "compiler was built without 'spawn' support") else: result = setMs(n, s) for i in 1.. 1 and n[1].kind notin nkCallKinds: + return localErrorNode(c, n, n[1].info, "'spawn' takes a call expression; got " & $n[1]) + let typ = result[^1].typ if not typ.isEmptyType: if spawnResult(typ, c.inParallelStmt > 0) == srFlowVar: diff --git a/tests/parallel/t7535.nim b/tests/parallel/t7535.nim new file mode 100644 index 000000000..a6bc0dabe --- /dev/null +++ b/tests/parallel/t7535.nim @@ -0,0 +1,11 @@ +discard """ + matrix: "--threads:on" + errormsg: "'spawn' takes a call expression; got proc (x: uint32) = echo [x]" +""" + +import threadpool + +# bug #7535 +proc print_parallel_nok(r: uint32) = + for x in 0..r: + spawn (proc (x: uint32) = echo x) From fb32fff8dcf2b15a469e5cce07b10d88aa6352ee Mon Sep 17 00:00:00 2001 From: Sivchari <55221074+sivchari@users.noreply.github.com> Date: Thu, 22 Apr 2021 02:47:01 +0900 Subject: [PATCH 1994/3768] js generates spurious >>> on shr (#17767) * js generates spurious >>> on shr * Add shr arithmetic test * fix variables from const to let during testing --- compiler/jsgen.nim | 3 +-- tests/stdlib/tarithmetics.nim | 49 +++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 tests/stdlib/tarithmetics.nim diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index e75ff182f..53ce45e96 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -683,8 +683,7 @@ proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = var x, y: TCompRes gen(p, n[1], x) gen(p, n[2], y) - let trimmer = unsignedTrimmer(n[1].typ.skipTypes(abstractRange).size) - r.res = "(($1 $2) >>> $3)" % [x.rdLoc, trimmer, y.rdLoc] + r.res = "($1 >>> $2)" % [x.rdLoc, y.rdLoc] of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr: arithAux(p, n, r, op) diff --git a/tests/stdlib/tarithmetics.nim b/tests/stdlib/tarithmetics.nim new file mode 100644 index 000000000..296ccd56e --- /dev/null +++ b/tests/stdlib/tarithmetics.nim @@ -0,0 +1,49 @@ +discard """ + targets: "c cpp js" +""" + +# TODO: in future work move existing arithmetic tests (tests/arithm/*) into this file +# FYI https://github.com/nim-lang/Nim/pull/17767 + +template main = + # put all arithmetic tests + + block tshr: + block: # Signed types + let + a1 = -3 + a2 = -2 + b1 = -4'i8 + b2 = 1'i8 + c1 = -5'i16 + c2 = 1'i16 + d1 = -7i32 + d2 = 1'i32 + e1 = -9'i64 + e2 = 1'i64 + doAssert a1 shr a2 == -1 + doAssert b1 shr b2 == -2 + doAssert c1 shr c2 == -3 + doAssert d1 shr d2 == -4 + doAssert e1 shr e2 == -5 + + block: # Unsigned types + let + a1 = 3'u + a2 = 2'u + b1 = 2'u8 + b2 = 1'u8 + c1 = 5'u16 + c2 = 1'u16 + d1 = 6'u32 + d2 = 1'u32 + e1 = 8'u64 + e2 = 1'u64 + doAssert a1 shr a2 == 0 + doAssert b1 shr b2 == 1 + doAssert c1 shr c2 == 2 + doAssert d1 shr d2 == 3 + doAssert e1 shr e2 == 4 + +static: main() +main() From 53c898de4105049c767804d0d2c520f31a86b874 Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 22 Apr 2021 14:08:56 +0800 Subject: [PATCH 1995/3768] fix #17812 (repr fails to compile with ARC/ORC) (#17816) --- lib/system/repr_v2.nim | 2 +- tests/arc/t17812.nim | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 tests/arc/t17812.nim diff --git a/lib/system/repr_v2.nim b/lib/system/repr_v2.nim index d456f4454..618cc2b40 100644 --- a/lib/system/repr_v2.nim +++ b/lib/system/repr_v2.nim @@ -84,7 +84,7 @@ proc repr*(p: pointer): string = proc repr*(p: proc): string = ## repr of a proc as its address - repr(cast[pointer](p)) + repr(cast[ptr pointer](unsafeAddr p)[]) template repr*(x: distinct): string = repr(distinctBase(typeof(x))(x)) diff --git a/tests/arc/t17812.nim b/tests/arc/t17812.nim new file mode 100644 index 000000000..bcd5f3a93 --- /dev/null +++ b/tests/arc/t17812.nim @@ -0,0 +1,29 @@ +discard """ + targets: "c js" + matrix: "--gc:refc; --gc:arc" +""" + +import std/times + +block: # bug #17812 + block: + type + Task = object + cb: proc () + + proc hello() = discard + + + let t = Task(cb: hello) + + doAssert t.repr.len > 0 + + + block: + type MyObj = object + field: DateTime + + + proc `$`(o: MyObj): string = o.repr + + doAssert ($MyObj()).len > 0 From 56859fe8516cd3e8191f9162c6409aeed8493941 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 22 Apr 2021 12:04:13 +0200 Subject: [PATCH 1996/3768] Windows: separate DLLs are not required anymore --- readme.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/readme.md b/readme.md index fa7a2d86d..9a07ae56b 100644 --- a/readme.md +++ b/readme.md @@ -84,10 +84,6 @@ Next, run the appropriate build shell script for your platform: * `build_all.sh` (Linux, Mac) * `build_all.bat` (Windows) -Windows requires a number of other dependencies that you may need to install including -PCRE and OpenSSL. Nim hosts a zip package containing known working versions of the -required DLLs [here](https://nim-lang.org/download/dlls.zip). - Finally, once you have finished the build steps (on Windows, Mac, or Linux) you should add the ``bin`` directory to your PATH. From e4a3feeb92940a9b3c4ce88fc808a0cdff2e4e9c Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 22 Apr 2021 23:07:22 -0700 Subject: [PATCH 1997/3768] add `--processing:dots|filenames|off` to customize `hintProcessing` (#17817) --- changelog.md | 2 ++ compiler/ccgtypes.nim | 2 +- compiler/commands.nim | 10 ++++++++++ compiler/msgs.nim | 2 +- compiler/options.nim | 32 +++++++++++++++--------------- compiler/passaux.nim | 7 ++++--- doc/advopt.txt | 2 ++ drnim/tests/config.nims | 2 +- nimsuggest/config.nims | 2 ++ nimsuggest/tester.nim | 9 ++++++--- nimsuggest/tests/tchk1.nim | 2 +- nimsuggest/tests/tchk_compiles.nim | 2 +- nimsuggest/tests/ttempl_inst.nim | 2 +- testament/testament.nim | 2 +- tests/misc/trunner.nim | 2 +- 15 files changed, 50 insertions(+), 30 deletions(-) create mode 100644 nimsuggest/config.nims diff --git a/changelog.md b/changelog.md index 30fe237be..7b4a2438a 100644 --- a/changelog.md +++ b/changelog.md @@ -343,6 +343,8 @@ - Added `--filenames:abs|canonical|magic` which replaces --listFullPaths:on|off +- Added `--processing:dots|filenames|off` which customizes `hintProcessing` + - Source+Edit links now appear on top of every docgen'd page when `nim doc --git.url:url ...` is given. diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index fa58b5651..767525fd5 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -1018,7 +1018,7 @@ proc genTypeInfoAuxBase(m: BModule; typ, origType: PType; var flags = 0 if not containsGarbageCollectedRef(typ): flags = flags or 1 if not canFormAcycle(typ): flags = flags or 2 - #else MessageOut("can contain a cycle: " & typeToString(typ)) + #else echo("can contain a cycle: " & typeToString(typ)) if flags != 0: m.s[cfsTypeInit3].addf("$1.flags = $2;$n", [nameHcr, rope(flags)]) discard cgsym(m, "TNimType") diff --git a/compiler/commands.nim b/compiler/commands.nim index 0733e500a..278199bc0 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -900,6 +900,16 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "canonical": conf.filenameOption = foCanonical of "legacyrelproj": conf.filenameOption = foLegacyRelProj else: localError(conf, info, "expected: abs|canonical|legacyRelProj, got: $1" % arg) + of "processing": + incl(conf.notes, hintProcessing) + incl(conf.mainPackageNotes, hintProcessing) + case arg.normalize + of "dots": conf.hintProcessingDots = true + of "filenames": conf.hintProcessingDots = false + of "off": + excl(conf.notes, hintProcessing) + excl(conf.mainPackageNotes, hintProcessing) + else: localError(conf, info, "expected: dots|filenames|off, got: $1" % arg) of "listfullpaths": # xxx in future work, use `warningDeprecated` conf.filenameOption = if switchOn(arg): foAbs else: foCanonical diff --git a/compiler/msgs.nim b/compiler/msgs.nim index f08722cc7..93ca8d7cc 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -545,7 +545,7 @@ proc liMessage*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string, if conf.structuredErrorHook != nil: conf.structuredErrorHook(conf, info, s & kindmsg, sev) if not ignoreMsgBecauseOfIdeTools(conf, msg): - if msg == hintProcessing: + if msg == hintProcessing and conf.hintProcessingDots: msgWrite(conf, ".") else: styledMsgWriteln(styleBright, loc, resetStyle, color, title, resetStyle, s, KindColor, kindmsg, diff --git a/compiler/options.nim b/compiler/options.nim index 2aaaf5844..7044e64bd 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -287,6 +287,7 @@ type implicitCmd*: bool # whether some flag triggered an implicit `command` selectedGC*: TGCMode # the selected GC (+) exc*: ExceptionSystem + hintProcessingDots*: bool # true for dots, false for filenames verbosity*: int # how verbose the compiler is numberOfProcessors*: int # number of processors lastCmdTime*: float # when caas is enabled, we measure each command @@ -458,20 +459,25 @@ proc isDefined*(conf: ConfigRef; symbol: string): bool when defined(nimDebugUtils): import debugutils +proc initConfigRefCommon(conf: ConfigRef) = + conf.selectedGC = gcRefc + conf.verbosity = 1 + conf.hintProcessingDots = true + conf.options = DefaultOptions + conf.globalOptions = DefaultGlobalOptions + conf.filenameOption = foAbs + conf.foreignPackageNotes = foreignPackageNotesDefault + conf.notes = NotesVerbosity[1] + conf.mainPackageNotes = NotesVerbosity[1] + proc newConfigRef*(): ConfigRef = result = ConfigRef( - selectedGC: gcRefc, cCompiler: ccGcc, - verbosity: 1, - options: DefaultOptions, - globalOptions: DefaultGlobalOptions, macrosToExpand: newStringTable(modeStyleInsensitive), arcToExpand: newStringTable(modeStyleInsensitive), m: initMsgConfig(), - filenameOption: foAbs, cppDefines: initHashSet[string](), - headerFile: "", features: {}, legacyFeatures: {}, foreignPackageNotes: foreignPackageNotesDefault, - notes: NotesVerbosity[1], mainPackageNotes: NotesVerbosity[1], + headerFile: "", features: {}, legacyFeatures: {}, configVars: newStringTable(modeStyleInsensitive), symbols: newStringTable(modeStyleInsensitive), packageCache: newPackageCache(), @@ -513,6 +519,7 @@ proc newConfigRef*(): ConfigRef = vmProfileData: newProfileData(), spellSuggestMax: spellSuggestSecretSauce, ) + initConfigRefCommon(result) setTargetFromSystem(result.target) # enable colors by default on terminals if terminal.isatty(stderr): @@ -522,18 +529,11 @@ proc newConfigRef*(): ConfigRef = proc newPartialConfigRef*(): ConfigRef = ## create a new ConfigRef that is only good enough for error reporting. - # xxx FACTOR with `newConfigRef` when defined(nimDebugUtils): result = getConfigRef() else: - result = ConfigRef( - selectedGC: gcRefc, - verbosity: 1, - options: DefaultOptions, - globalOptions: DefaultGlobalOptions, - filenameOption: foAbs, - foreignPackageNotes: foreignPackageNotesDefault, - notes: NotesVerbosity[1], mainPackageNotes: NotesVerbosity[1]) + result = ConfigRef() + initConfigRefCommon(result) proc cppDefine*(c: ConfigRef; define: string) = c.cppDefines.incl define diff --git a/compiler/passaux.nim b/compiler/passaux.nim index 715b7d676..9abc97be4 100644 --- a/compiler/passaux.nim +++ b/compiler/passaux.nim @@ -19,9 +19,10 @@ type config: ConfigRef proc verboseOpen(graph: ModuleGraph; s: PSym; idgen: IdGenerator): PPassContext = - #MessageOut('compiling ' + s.name.s); - result = VerboseRef(config: graph.config, idgen: idgen) - rawMessage(graph.config, hintProcessing, s.name.s) + let conf = graph.config + result = VerboseRef(config: conf, idgen: idgen) + let path = toFilenameOption(conf, s.position.FileIndex, conf.filenameOption) + rawMessage(conf, hintProcessing, path) proc verboseProcess(context: PPassContext, n: PNode): PNode = result = n diff --git a/doc/advopt.txt b/doc/advopt.txt index 539cc620c..a783ebec5 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -39,6 +39,8 @@ Advanced options: --filenames:abs|canonical|legacyRelProj customize how filenames are rendered in compiler messages, defaults to `abs` (absolute) + --processing:dots|filenames|off + show files as they're being processed by nim compiler --declaredLocs:on|off show declaration locations in messages --spellSuggest|:num show at most `num >= 0` spelling suggestions on typos. if `num` is not specified (or `auto`), return diff --git a/drnim/tests/config.nims b/drnim/tests/config.nims index a6b0e7d88..346b0b4e6 100644 --- a/drnim/tests/config.nims +++ b/drnim/tests/config.nims @@ -3,7 +3,7 @@ switch("path", "$nim/testament/lib") # so we can `import stdtest/foo` in this di ## prevent common user config settings to interfere with testament expectations ## Indifidual tests can override this if needed to test for these options. switch("colors", "off") -switch("listFullPaths", "off") +switch("filenames", "canonical") switch("excessiveStackTrace", "off") # we only want to check the marked parts in the tests: diff --git a/nimsuggest/config.nims b/nimsuggest/config.nims new file mode 100644 index 000000000..ee19f9893 --- /dev/null +++ b/nimsuggest/config.nims @@ -0,0 +1,2 @@ +# xxx not sure why this flag isn't needed: switch("processing", "filenames") +switch("filenames", "canonical") diff --git a/nimsuggest/tester.nim b/nimsuggest/tester.nim index 425430ede..9fcf7eacc 100644 --- a/nimsuggest/tester.nim +++ b/nimsuggest/tester.nim @@ -255,7 +255,6 @@ proc runEpcTest(filename: string): int = options={poStdErrToStdOut, poUsePath, poInteractive, poDaemon}) let outp = p.outputStream - let inp = p.inputStream var report = "" var socket = newSocket() try: @@ -315,8 +314,12 @@ proc runTest(filename: string): int = answer.add '\L' doReport(filename, answer, resp, report) finally: - inp.writeLine("quit") - inp.flush() + try: + inp.writeLine("quit") + inp.flush() + except: + # assume it's SIGPIPE, ie, the child already died + discard close(p) if report.len > 0: echo "==== STDIN ======================================" diff --git a/nimsuggest/tests/tchk1.nim b/nimsuggest/tests/tchk1.nim index c28b88b9b..794392c1f 100644 --- a/nimsuggest/tests/tchk1.nim +++ b/nimsuggest/tests/tchk1.nim @@ -17,7 +17,7 @@ proc main = discard """ $nimsuggest --tester $file >chk $1 -chk;;skUnknown;;;;Hint;;???;;0;;-1;;"tchk1 [Processing]";;0 +chk;;skUnknown;;;;Hint;;???;;0;;-1;;"tests/tchk1.nim [Processing]";;0 chk;;skUnknown;;;;Error;;$file;;12;;0;;"identifier expected, but got \'keyword template\'";;0 chk;;skUnknown;;;;Error;;$file;;14;;0;;"nestable statement requires indentation";;0 chk;;skUnknown;;;;Error;;$file;;12;;0;;"implementation of \'foo\' expected";;0 diff --git a/nimsuggest/tests/tchk_compiles.nim b/nimsuggest/tests/tchk_compiles.nim index 887a947b5..2afa06899 100644 --- a/nimsuggest/tests/tchk_compiles.nim +++ b/nimsuggest/tests/tchk_compiles.nim @@ -4,5 +4,5 @@ discard compiles(2 + "hello") discard """ $nimsuggest --tester $file >chk $1 -chk;;skUnknown;;;;Hint;;???;;0;;-1;;"tchk_compiles [Processing]";;0 +chk;;skUnknown;;;;Hint;;???;;0;;-1;;"tests/tchk_compiles.nim [Processing]";;0 """ diff --git a/nimsuggest/tests/ttempl_inst.nim b/nimsuggest/tests/ttempl_inst.nim index ed04a67ce..da413d99a 100644 --- a/nimsuggest/tests/ttempl_inst.nim +++ b/nimsuggest/tests/ttempl_inst.nim @@ -7,7 +7,7 @@ foo() discard """ $nimsuggest --tester $file >chk $1 -chk;;skUnknown;;;;Hint;;???;;0;;-1;;"ttempl_inst [Processing]";;0 +chk;;skUnknown;;;;Hint;;???;;0;;-1;;"tests/ttempl_inst.nim [Processing]";;0 chk;;skUnknown;;;;Hint;;$file;;4;;3;;"template/generic instantiation from here";;0 chk;;skUnknown;;;;Warning;;$file;;2;;11;;"foo [User]";;0 """ diff --git a/testament/testament.nim b/testament/testament.nim index b79b86e51..f259c7b96 100644 --- a/testament/testament.nim +++ b/testament/testament.nim @@ -104,7 +104,7 @@ proc isSuccess(input: string): bool = # not clear how to do the equivalent of pkg/regex's: re"FOO(.*?)BAR" in pegs # note: this doesn't handle colors, eg: `\e[1m\e[0m\e[32mHint:`; while we # could handle colors, there would be other issues such as handling other flags - # that may appear in user config (eg: `--listFullPaths`). + # that may appear in user config (eg: `--filenames`). # Passing `XDG_CONFIG_HOME= testament args...` can be used to ignore user config # stored in XDG_CONFIG_HOME, refs https://wiki.archlinux.org/index.php/XDG_Base_Directory input.startsWith("Hint: ") and input.endsWith("[SuccessX]") diff --git a/tests/misc/trunner.nim b/tests/misc/trunner.nim index df9ee9a48..505a06cf8 100644 --- a/tests/misc/trunner.nim +++ b/tests/misc/trunner.nim @@ -92,7 +92,7 @@ else: # don't run twice the same test of 5: nimcache / htmldocsDirname else: file.parentDir / htmldocsDirname - var cmd = fmt"{nim} doc --index:on --listFullPaths --hint:successX:on --nimcache:{nimcache} {options[i]} {file}" + var cmd = fmt"{nim} doc --index:on --filenames:abs --hint:successX:on --nimcache:{nimcache} {options[i]} {file}" removeDir(htmldocsDir) let (outp, exitCode) = execCmdEx(cmd) check exitCode == 0 From dbb053492a3c64236f7e8f9358b9c7f297ba241a Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 23 Apr 2021 01:05:23 -0700 Subject: [PATCH 1998/3768] followup #17561, skipping ci now implies green (#17813) * followup #17561, skip ci now implies green; [skip ci] * fixup [skip ci] * fixup test without skip ci * fixup [skip ci] * fixup2 [skip ci] * github actions now automatically supports [skip ci] --- .github/workflows/ci_docs.yml | 11 ----------- .github/workflows/ci_packages.yml | 11 ----------- azure-pipelines.yml | 33 ++++++++++++++++++++----------- ci/funs.sh | 22 +++++++++++++++++++++ 4 files changed, 44 insertions(+), 33 deletions(-) diff --git a/.github/workflows/ci_docs.yml b/.github/workflows/ci_docs.yml index c9036d1d4..750613574 100644 --- a/.github/workflows/ci_docs.yml +++ b/.github/workflows/ci_docs.yml @@ -30,9 +30,6 @@ on: jobs: build: - # see D20210329T004830 - if: | - !contains(format('{0}', github.event.pull_request.title), '[skip ci]') strategy: fail-fast: false matrix: @@ -54,14 +51,6 @@ jobs: with: fetch-depth: 2 - - name: 'Check whether to skip CI' - shell: bash - run: | - # see D20210329T004830 - commitMsg=$(git log --no-merges -1 --pretty=format:"%s") - echo commitMsg: $commitMsg - echo $commitMsg | grep -v '\[skip ci\]' - - name: 'Install build dependencies (macOS)' if: runner.os == 'macOS' run: brew install make diff --git a/.github/workflows/ci_packages.yml b/.github/workflows/ci_packages.yml index e7073ea23..7ebdc5144 100644 --- a/.github/workflows/ci_packages.yml +++ b/.github/workflows/ci_packages.yml @@ -3,9 +3,6 @@ on: [push, pull_request] jobs: build: - # see D20210329T004830 - if: | - !contains(format('{0}', github.event.pull_request.title), '[skip ci]') strategy: fail-fast: false matrix: @@ -23,14 +20,6 @@ jobs: with: fetch-depth: 2 - - name: 'Check whether to skip CI' - shell: bash - run: | - # see D20210329T004830 - commitMsg=$(git log --no-merges -1 --pretty=format:"%s") - echo commitMsg: $commitMsg - echo $commitMsg | grep -v '\[skip ci\]' - - name: 'Checkout csources' uses: actions/checkout@v2 with: diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 5c244648d..7e5b5c27d 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -7,6 +7,10 @@ pr: include: - '*' +variables: +- name: skipci + value: false + jobs: - job: packages @@ -58,22 +62,22 @@ jobs: fetchDepth: 2 # see D20210329T004830 - bash: | - # D20210329T004830:here refs https://github.com/microsoft/azure-pipelines-agent/issues/2944 - # `--no-merges` is needed to avoid merge commits which occur for PR's. - # $(Build.SourceVersionMessage) is not helpful - # nor is `github.event.head_commit.message` for github actions. - commitMsg=$(git log --no-merges -1 --pretty=format:"%s") - echo commitMsg: $commitMsg - echo $commitMsg | grep -v '\[skip ci\]' # fails if [skip ci] not in commit msg + set -e + . ci/funs.sh + if nimIsCiSkip; then + echo '##vso[task.setvariable variable=skipci]true' + fi displayName: 'Check whether to skip CI' - bash: git clone --depth 1 https://github.com/nim-lang/csources_v1 csources displayName: 'Checkout Nim csources' + condition: and(succeeded(), eq(variables['skipci'], 'false')) - task: NodeTool@0 inputs: versionSpec: '12.x' displayName: 'Install node.js 12.x' + condition: and(succeeded(), eq(variables['skipci'], 'false')) - bash: | set -e @@ -83,7 +87,7 @@ jobs: echo_run sudo apt-fast install --no-install-recommends -yq \ libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev valgrind libc6-dbg displayName: 'Install dependencies (amd64 Linux)' - condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'), eq(variables['CPU'], 'amd64')) + condition: and(succeeded(), eq(variables['skipci'], 'false'), eq(variables['Agent.OS'], 'Linux'), eq(variables['CPU'], 'amd64')) - bash: | set -e @@ -123,11 +127,11 @@ jobs: echo_run chmod 755 bin/g++ displayName: 'Install dependencies (i386 Linux)' - condition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'), eq(variables['CPU'], 'i386')) + condition: and(succeeded(), eq(variables['skipci'], 'false'), eq(variables['Agent.OS'], 'Linux'), eq(variables['CPU'], 'i386')) - bash: brew install boehmgc make sfml displayName: 'Install dependencies (OSX)' - condition: and(succeeded(), eq(variables['Agent.OS'], 'Darwin')) + condition: and(succeeded(), eq(variables['skipci'], 'false'), eq(variables['Agent.OS'], 'Darwin')) - bash: | set -e @@ -140,9 +144,10 @@ jobs: echo_run echo '##vso[task.prependpath]$(System.DefaultWorkingDirectory)/dist/mingw64/bin' displayName: 'Install dependencies (Windows)' - condition: and(succeeded(), eq(variables['Agent.OS'], 'Windows_NT')) + condition: and(succeeded(), eq(variables['skipci'], 'false'), eq(variables['Agent.OS'], 'Windows_NT')) - bash: echo '##vso[task.prependpath]$(System.DefaultWorkingDirectory)/bin' + condition: and(succeeded(), eq(variables['skipci'], 'false')) displayName: 'Add build binaries to PATH' - bash: | @@ -155,15 +160,18 @@ jobs: echo_run node -v echo_run echo '##[section]make version' echo_run make -v + condition: and(succeeded(), eq(variables['skipci'], 'false')) displayName: 'System information' - bash: echo '##vso[task.setvariable variable=csources_version]'"$(git -C csources rev-parse HEAD)" + condition: and(succeeded(), eq(variables['skipci'], 'false')) displayName: 'Get csources version' - task: Cache@2 inputs: key: 'csources | "$(Agent.OS)" | $(CPU) | $(csources_version)' path: csources/bin + condition: and(succeeded(), eq(variables['skipci'], 'false')) displayName: 'Restore built csources' - bash: | @@ -192,13 +200,16 @@ jobs: fi echo_run cp csources/bin/nim$ext bin + condition: and(succeeded(), eq(variables['skipci'], 'false')) displayName: 'Build 1-stage compiler from csources' - bash: nim c koch + condition: and(succeeded(), eq(variables['skipci'], 'false')) displayName: 'Build koch' # set result to omit the "bash exited with error code '1'" message - bash: ./koch runCI || echo '##vso[task.complete result=Failed]' + condition: and(succeeded(), eq(variables['skipci'], 'false')) displayName: 'Run CI' env: SYSTEM_ACCESSTOKEN: $(System.AccessToken) diff --git a/ci/funs.sh b/ci/funs.sh index de56ef152..7f82e1ca3 100644 --- a/ci/funs.sh +++ b/ci/funs.sh @@ -1,4 +1,5 @@ # utilities used in CI pipelines to avoid duplication. +# Avoid top-level statements. echo_run () { # echo's a command before running it, which helps understanding logs @@ -6,3 +7,24 @@ echo_run () { echo "$@" "$@" } + +nimGetLastCommit() { + git log --no-merges -1 --pretty=format:"%s" +} + +nimIsCiSkip(){ + # D20210329T004830:here refs https://github.com/microsoft/azure-pipelines-agent/issues/2944 + # `--no-merges` is needed to avoid merge commits which occur for PR's. + # $(Build.SourceVersionMessage) is not helpful + # nor is `github.event.head_commit.message` for github actions. + # Note: `[skip ci]` is now handled automatically for github actions, see https://github.blog/changelog/2021-02-08-github-actions-skip-pull-request-and-push-workflows-with-skip-ci/ + commitMsg=$(nimGetLastCommit) + echo commitMsg: "$commitMsg" + if [[ $commitMsg == *"[skip ci]"* ]]; then + echo "skipci: true" + return 0 + else + echo "skipci: false" + return 1 + fi +} From dce0b3b002fcbac716bada039bed4a02eb501744 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 23 Apr 2021 02:28:42 -0700 Subject: [PATCH 1999/3768] refactor all code that builds csources (#17815) * refactor all code that builds csources * fixup * nim_csourcesDir_v0 + nim_csourcesDir * remove deprecated, unused scripts from ci/ * reuse nimCsourcesHash in ci * simplify CI pipelines by reusing nimBuildCsourcesIfNeeded * simplify ci_docs.yml by reusing nimBuildCsourcesIfNeeded * cleanup * use csources_v1 as destination dir * fixup * remove pushCsources * address comment: remove build.sh support for now * fixup --- .builds/freebsd.yml | 6 +-- .builds/openbsd_0.yml | 8 ++-- .builds/openbsd_1.yml | 8 ++-- .github/workflows/ci.yml.disabled | 25 ++--------- .github/workflows/ci_docs.yml | 51 +++------------------ .github/workflows/ci_packages.yml | 25 ++--------- .gitignore | 3 ++ .travis.yml | 4 +- appveyor.yml.disabled | 8 ++-- azure-pipelines.yml | 50 ++++----------------- build_all.bat | 9 ++-- build_all.sh | 46 +++---------------- ci/build.bat | 14 ------ ci/build.sh | 15 ------- ci/deps.bat | 4 -- ci/deps.sh | 16 ------- ci/funs.sh | 73 ++++++++++++++++++++++++++++++- ci/nsis_build.bat | 59 ------------------------- koch.nim | 36 ++------------- lib/std/private/globs.nim | 2 +- readme.md | 5 ++- tools/ci_generate.nim | 8 ++-- 22 files changed, 136 insertions(+), 339 deletions(-) delete mode 100644 ci/build.bat delete mode 100644 ci/build.sh delete mode 100644 ci/deps.bat delete mode 100644 ci/deps.sh delete mode 100644 ci/nsis_build.bat diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml index 443be798a..c1fb80253 100644 --- a/.builds/freebsd.yml +++ b/.builds/freebsd.yml @@ -14,10 +14,10 @@ environment: CC: /usr/bin/clang tasks: - setup: | + set -e cd Nim - git clone --depth 1 -q https://github.com/nim-lang/csources_v1.git csources - gmake -C csources -j $(sysctl -n hw.ncpu) - bin/nim c --skipUserCfg --skipParentCfg koch + . ci/funs.sh && nimBuildCsourcesIfNeeded + $nim_csources c --skipUserCfg --skipParentCfg koch echo 'export PATH=$HOME/Nim/bin:$PATH' >> $HOME/.buildenv - test: | cd Nim diff --git a/.builds/openbsd_0.yml b/.builds/openbsd_0.yml index c09f0f08d..e1d85d2a7 100644 --- a/.builds/openbsd_0.yml +++ b/.builds/openbsd_0.yml @@ -17,15 +17,15 @@ environment: CC: /usr/bin/clang tasks: - setup: | + set -e cd Nim - git clone --depth 1 -q https://github.com/nim-lang/csources_v1.git csources - gmake -C csources -j $(sysctl -n hw.ncpuonline) - bin/nim c koch + . ci/funs.sh && nimBuildCsourcesIfNeeded + $nim_csources c koch echo 'export PATH=$HOME/Nim/bin:$PATH' >> $HOME/.buildenv - test: | cd Nim if ! ./koch runCI; then - nim c -r tools/ci_testresults.nim + nim r tools/ci_testresults.nim exit 1 fi triggers: diff --git a/.builds/openbsd_1.yml b/.builds/openbsd_1.yml index ffe449733..ed2ded056 100644 --- a/.builds/openbsd_1.yml +++ b/.builds/openbsd_1.yml @@ -17,15 +17,15 @@ environment: CC: /usr/bin/clang tasks: - setup: | + set -e cd Nim - git clone --depth 1 -q https://github.com/nim-lang/csources_v1.git csources - gmake -C csources -j $(sysctl -n hw.ncpuonline) - bin/nim c koch + . ci/funs.sh && nimBuildCsourcesIfNeeded + $nim_csources c koch echo 'export PATH=$HOME/Nim/bin:$PATH' >> $HOME/.buildenv - test: | cd Nim if ! ./koch runCI; then - nim c -r tools/ci_testresults.nim + nim r tools/ci_testresults.nim exit 1 fi triggers: diff --git a/.github/workflows/ci.yml.disabled b/.github/workflows/ci.yml.disabled index 183576456..62daf875a 100644 --- a/.github/workflows/ci.yml.disabled +++ b/.github/workflows/ci.yml.disabled @@ -1,3 +1,5 @@ +# out of date and unused but revivable + name: Continous Integration on: [push, pull_request] @@ -30,11 +32,6 @@ jobs: steps: - name: 'Checkout' uses: actions/checkout@v2 - - name: 'Checkout csources' - uses: actions/checkout@v2 - with: - repository: nim-lang/csources - path: csources - name: 'Install node.js 8.x' uses: actions/setup-node@v1 @@ -91,24 +88,10 @@ jobs: shell: bash run: echo "${{ github.workspace }}/bin" >> "${GITHUB_PATH}" - - name: 'Build csources' + - name: 'Build csourcesAny' shell: bash - run: | - ncpu= - case '${{ runner.os }}' in - 'Linux') - ncpu=$(nproc) - ;; - 'macOS') - ncpu=$(sysctl -n hw.ncpu) - ;; - 'Windows') - ncpu=$NUMBER_OF_PROCESSORS - ;; - esac - [[ -z "$ncpu" || $ncpu -le 0 ]] && ncpu=1 + run: . ci/funs.sh && nimBuildCsourcesIfNeeded CC=gcc ucpu='${{ matrix.cpu }}' - make -C csources -j $ncpu CC=gcc ucpu='${{ matrix.cpu }}' - name: 'Build koch' shell: bash run: nim c koch diff --git a/.github/workflows/ci_docs.yml b/.github/workflows/ci_docs.yml index 750613574..1459d6a31 100644 --- a/.github/workflows/ci_docs.yml +++ b/.github/workflows/ci_docs.yml @@ -70,53 +70,12 @@ jobs: shell: bash run: echo "${{ github.workspace }}/bin" >> "${GITHUB_PATH}" - - name: 'Get current csources version' - id: csources-version + - name: 'Build csourcesAny' shell: bash - run: | - sha=$(git ls-remote https://github.com/nim-lang/csources_v1 master | cut -f 1) - echo "::set-output name=sha::$sha" - - - name: 'Get prebuilt csources from cache' - id: csources-cache - uses: actions/cache@v1 - with: - path: bin - key: '${{ matrix.os }}-${{ steps.csources-version.outputs.sha }}' - - - name: 'Checkout csources' - if: steps.csources-cache.outputs.cache-hit != 'true' - uses: actions/checkout@v2 - with: - repository: nim-lang/csources_v1 - path: csources - - - name: 'Build 1-stage compiler from csources' - shell: bash - run: | - ext= - [[ '${{ runner.os }}' == 'Windows' ]] && ext=.exe - if [[ ! -x bin/nim-csources$ext ]]; then - ncpu= - case '${{ runner.os }}' in - 'Linux') - ncpu=$(nproc) - ;; - 'macOS') - ncpu=$(sysctl -n hw.ncpu) - ;; - 'Windows') - ncpu=$NUMBER_OF_PROCESSORS - ;; - esac - [[ -z "$ncpu" || $ncpu -le 0 ]] && ncpu=1 - - make -C csources -j $ncpu CC=gcc - cp bin/nim{,-csources}$ext - else - echo 'Cache hit, using prebuilt csources' - cp bin/nim{-csources,}$ext - fi + run: . ci/funs.sh && nimBuildCsourcesIfNeeded CC=gcc + # was previously using caching via `actions/cache@v1` but this wasn't + # used in other CI pipelines and it's unclear the added complexity + # was worth the saving; can be revisited if needed. - name: 'Build koch' shell: bash diff --git a/.github/workflows/ci_packages.yml b/.github/workflows/ci_packages.yml index 7ebdc5144..c6355bbcb 100644 --- a/.github/workflows/ci_packages.yml +++ b/.github/workflows/ci_packages.yml @@ -20,16 +20,11 @@ jobs: with: fetch-depth: 2 - - name: 'Checkout csources' - uses: actions/checkout@v2 - with: - repository: nim-lang/csources - path: csources - - name: 'Install node.js 12.x' uses: actions/setup-node@v1 with: node-version: '12.x' + - name: 'Install dependencies (Linux amd64)' if: runner.os == 'Linux' && matrix.cpu == 'amd64' run: | @@ -56,24 +51,10 @@ jobs: shell: bash run: echo "${{ github.workspace }}/bin" >> "${GITHUB_PATH}" - - name: 'Build csources' + - name: 'Build csourcesAny' shell: bash - run: | - ncpu= - case '${{ runner.os }}' in - 'Linux') - ncpu=$(nproc) - ;; - 'macOS') - ncpu=$(sysctl -n hw.ncpu) - ;; - 'Windows') - ncpu=$NUMBER_OF_PROCESSORS - ;; - esac - [[ -z "$ncpu" || $ncpu -le 0 ]] && ncpu=1 + run: . ci/funs.sh && nimBuildCsourcesIfNeeded CC=gcc ucpu='${{ matrix.cpu }}' - make -C csources -j $ncpu CC=gcc ucpu='${{ matrix.cpu }}' - name: 'Build koch' shell: bash run: nim c koch diff --git a/.gitignore b/.gitignore index e9e742404..d22b8316a 100644 --- a/.gitignore +++ b/.gitignore @@ -64,7 +64,10 @@ lib/**/*.html testament.db /tests/**/*.json /tests/**/*.js + /csources +/csources_v1 + /dist/ # /lib/fusion # fusion is now unbundled; `git status` should reveal if it's there so users can act on it diff --git a/.travis.yml b/.travis.yml index 4c4c35334..13e5d151a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,3 +1,4 @@ +# deprecated, out of date and unused sudo: false language: c @@ -22,9 +23,8 @@ addons: - valgrind before_script: - - git clone --depth 1 https://github.com/nim-lang/csources.git + - . ci/funs.sh && nimBuildCsourcesIfNeeded LD=$CC ucpu=$CPU - export PATH="$PWD/bin${PATH:+:$PATH}" - - make -C csources -j 2 LD=$CC ucpu=$CPU script: - echo "travis_fold:start:nim_c_koch" diff --git a/appveyor.yml.disabled b/appveyor.yml.disabled index 5468ac88a..b13c5d909 100644 --- a/appveyor.yml.disabled +++ b/appveyor.yml.disabled @@ -1,3 +1,4 @@ +# deprecated, out of date and unused version: '{build}' environment: @@ -23,10 +24,9 @@ install: - IF not exist "%MINGW_ARCHIVE%" appveyor DownloadFile "%MINGW_URL%" -FileName "%MINGW_ARCHIVE%" - 7z x -y "%MINGW_ARCHIVE%" -o"%CD%\DIST"> nul - SET PATH=%CD%\DIST\%MINGW_DIR%\BIN;%CD%\BIN;%PATH% - - git clone --depth 1 https://github.com/nim-lang/csources - - cd csources - - build64.bat - - cd .. + # may need adjustments, untested, see similar examples here: + # https://chromium.googlesource.com/external/github.com/swig/swig/+/1e36f51346d95f8b9848e682c2eb986e9cb9b4f4/appveyor.yml + - bash -c ". ci/funs.sh && nimBuildCsourcesIfNeeded" build_script: - openssl version diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 7e5b5c27d..8cf69f528 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -69,10 +69,6 @@ jobs: fi displayName: 'Check whether to skip CI' - - bash: git clone --depth 1 https://github.com/nim-lang/csources_v1 csources - displayName: 'Checkout Nim csources' - condition: and(succeeded(), eq(variables['skipci'], 'false')) - - task: NodeTool@0 inputs: versionSpec: '12.x' @@ -163,45 +159,17 @@ jobs: condition: and(succeeded(), eq(variables['skipci'], 'false')) displayName: 'System information' - - bash: echo '##vso[task.setvariable variable=csources_version]'"$(git -C csources rev-parse HEAD)" + - bash: . ci/funs.sh && nimBuildCsourcesIfNeeded CC=gcc ucpu=$(CPU) condition: and(succeeded(), eq(variables['skipci'], 'false')) - displayName: 'Get csources version' + displayName: 'Build csourcesAny' - - task: Cache@2 - inputs: - key: 'csources | "$(Agent.OS)" | $(CPU) | $(csources_version)' - path: csources/bin - condition: and(succeeded(), eq(variables['skipci'], 'false')) - displayName: 'Restore built csources' - - - bash: | - set -e - . ci/funs.sh - ncpu= - ext= - case '$(Agent.OS)' in - 'Linux') - ncpu=$(nproc) - ;; - 'Darwin') - ncpu=$(sysctl -n hw.ncpu) - ;; - 'Windows_NT') - ncpu=$NUMBER_OF_PROCESSORS - ext=.exe - ;; - esac - [[ -z "$ncpu" || $ncpu -le 0 ]] && ncpu=1 - - if [[ -x csources/bin/nim$ext ]]; then - echo_run echo "Found cached compiler, skipping build" - else - echo_run make -C csources -j $ncpu CC=gcc ucpu=$(CPU) koch=no - fi - - echo_run cp csources/bin/nim$ext bin - condition: and(succeeded(), eq(variables['skipci'], 'false')) - displayName: 'Build 1-stage compiler from csources' + # this could be revived if performance justifies it (needs a few updates) + # - task: Cache@2 + # inputs: + # key: 'csourcesAny | "$(Agent.OS)" | $(CPU) | $(csources_version)' + # path: $(nim_csources) + # condition: and(succeeded(), eq(variables['skipci'], 'false')) + # displayName: 'Restore built csourcesAny' - bash: nim c koch condition: and(succeeded(), eq(variables['skipci'], 'false')) diff --git a/build_all.bat b/build_all.bat index 7967e2584..78a7a354a 100644 --- a/build_all.bat +++ b/build_all.bat @@ -1,10 +1,13 @@ @echo off rem build development version of the compiler; can be rerun safely -if not exist csources ( - git clone --depth 1 https://github.com/nim-lang/csources.git +rem TODO: call nimBuildCsourcesIfNeeded or auto-generate this file (from a nim script) +rem to avoid duplication. +if not exist csources_v1 ( + git clone --depth 1 https://github.com/nim-lang/csources_v1.git ) if not exist bin\nim.exe ( - cd csources + cd csources_v1 + git checkout a8a5241f9475099c823cfe1a5e0ca4022ac201ff if PROCESSOR_ARCHITECTURE == AMD64 ( SET ARCH=64 ) diff --git a/build_all.sh b/build_all.sh index c3c6e68b7..49504c8fe 100755 --- a/build_all.sh +++ b/build_all.sh @@ -1,52 +1,16 @@ #! /bin/sh # build development version of the compiler; can be rerun safely. -# arguments can be passed, e.g. `--os freebsd` +# arguments can be passed, e.g.: +# CC=gcc ucpu=amd64 uos=darwin set -u # error on undefined variables set -e # exit on first error -echo_run(){ - echo "$*" - "$@" -} +. ci/funs.sh +nimBuildCsourcesIfNeeded "$@" -[ -d csources ] || echo_run git clone -q --depth 1 https://github.com/nim-lang/csources.git - -nim_csources=bin/nim_csources - -build_nim_csources_via_script(){ - echo_run cd csources - echo_run sh build.sh "$@" -} - -build_nim_csources(){ - # avoid changing dir in case of failure - ( - if [ $# -ne 0 ]; then - # some args were passed (e.g.: `--cpu i386`), need to call build.sh - build_nim_csources_via_script "$@" - else - # no args, use multiple Make jobs (5X faster on 16 cores: 10s instead of 50s) - makeX=make - unamestr=$(uname) - if [ "$unamestr" = 'FreeBSD' ]; then - makeX=gmake - fi - nCPU=$(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || getconf _NPROCESSORS_ONLN 2>/dev/null || 1) - which $makeX && echo_run $makeX -C csources -j $((nCPU + 2)) -l $nCPU || build_nim_csources_via_script - fi - ) - # keep $nim_csources in case needed to investigate bootstrap issues - # without having to rebuild from csources - echo_run cp bin/nim $nim_csources -} - -[ -f $nim_csources ] || echo_run build_nim_csources "$@" - -# Note: if fails, may need to `cd csources && git pull` +# Note: if fails, may need to update csourcesAny manually echo_run bin/nim c --skipUserCfg --skipParentCfg koch - echo_run ./koch boot -d:release --skipUserCfg --skipParentCfg echo_run ./koch tools --skipUserCfg --skipParentCfg # Compile Nimble and other tools. - diff --git a/ci/build.bat b/ci/build.bat deleted file mode 100644 index 2227168e5..000000000 --- a/ci/build.bat +++ /dev/null @@ -1,14 +0,0 @@ -REM Some debug info -echo "Running on %CI_RUNNER_ID% (%CI_RUNNER_DESCRIPTION%) with tags %CI_RUNNER_TAGS%." -gcc -v - -git clone --depth 1 https://github.com/nim-lang/csources_v1.git csources -cd csources -call build64.bat -cd .. -set PATH=%CD%\bin;%PATH% -nim -v -nim c koch -koch.exe boot -copy bin/nim bin/nimd -koch.exe boot -d:release diff --git a/ci/build.sh b/ci/build.sh deleted file mode 100644 index a89d011ed..000000000 --- a/ci/build.sh +++ /dev/null @@ -1,15 +0,0 @@ -sh ci/deps.sh - -# Build from C sources. -git clone --depth 1 https://github.com/nim-lang/csources_v1.git csources -cd csources -sh build.sh -cd .. -# Add Nim to the PATH -export PATH=$(pwd)/bin${PATH:+:$PATH} -# Bootstrap. -nim -v -nim c koch -./koch boot -cp bin/nim bin/nimd -./koch boot -d:release diff --git a/ci/deps.bat b/ci/deps.bat deleted file mode 100644 index bda1fe14f..000000000 --- a/ci/deps.bat +++ /dev/null @@ -1,4 +0,0 @@ -nim e install_nimble.nims -nim e tests/test_nimscript.nims -nimble update -nimble install -y zip opengl sdl1 jester@#head niminst diff --git a/ci/deps.sh b/ci/deps.sh deleted file mode 100644 index f0f831a2a..000000000 --- a/ci/deps.sh +++ /dev/null @@ -1,16 +0,0 @@ -# Some debug info -echo "Running on $CI_RUNNER_ID ($CI_RUNNER_DESCRIPTION) with tags $CI_RUNNER_TAGS." - -# Packages -apt-get update -qq -apt-get install -y -qq build-essential git libcurl4-openssl-dev libsdl1.2-dev libgc-dev nodejs - -gcc -v - -export PATH=$(pwd)/bin${PATH:+:$PATH} - -# Nimble deps -nim e install_nimble.nims -nim e tests/test_nimscript.nims -nimble update -nimble install zip opengl sdl1 jester@#head niminst diff --git a/ci/funs.sh b/ci/funs.sh index 7f82e1ca3..7a6c66722 100644 --- a/ci/funs.sh +++ b/ci/funs.sh @@ -1,5 +1,7 @@ -# utilities used in CI pipelines to avoid duplication. +# Utilities used in CI pipelines and tooling to avoid duplication. # Avoid top-level statements. +# Prefer nim scripts whenever possible. +# functions starting with `_` are considered internal, less stable. echo_run () { # echo's a command before running it, which helps understanding logs @@ -28,3 +30,72 @@ nimIsCiSkip(){ return 1 fi } + +nimDefineVars(){ + nim_csourcesDir=csources_v1 # where we clone + nim_csourcesUrl=https://github.com/nim-lang/csources_v1.git + nim_csourcesHash=a8a5241f9475099c823cfe1a5e0ca4022ac201ff + nim_csources=bin/nim_csources_$nim_csourcesHash +} + +_nimNumCpu(){ + # linux: $(nproc) + # FreeBSD | macOS: $(sysctl -n hw.ncpu) + # OpenBSD: $(sysctl -n hw.ncpuonline) + # windows: $NUMBER_OF_PROCESSORS ? + echo $(nproc 2>/dev/null || sysctl -n hw.logicalcpu 2>/dev/null || getconf _NPROCESSORS_ONLN 2>/dev/null || 1) +} + +_nimBuildCsourcesIfNeeded(){ + # if some systems cannot use make or gmake, we could add support for calling `build.sh` + # but this is slower (not parallel jobs) and would require making build.sh + # understand the arguments passed to the makefile (e.g. `CC=gcc ucpu=amd64 uos=darwin`), + # instead of `--cpu amd64 --os darwin`. + unamestr=$(uname) + # uname values: https://en.wikipedia.org/wiki/Uname + if [ "$unamestr" = 'FreeBSD' ]; then + makeX=gmake + elif [ "$unamestr" = 'OpenBSD' ]; then + makeX=gmake + else + makeX=make + fi + nCPU=$(_nimNumCpu) + echo_run which $makeX + # parallel jobs (5X faster on 16 cores: 10s instead of 50s) + echo_run $makeX -C $nim_csourcesDir -j $((nCPU + 2)) -l $nCPU "$@" + # keep $nim_csources in case needed to investigate bootstrap issues + # without having to rebuild + echo_run cp bin/nim $nim_csources +} + +nimCsourcesHash(){ + nimDefineVars + echo $nim_csourcesHash +} + +nimBuildCsourcesIfNeeded(){ + # goal: allow cachine each tagged version independently + # to avoid rebuilding, so that tools like `git bisect` + # can grab a cached past version without rebuilding. + nimDefineVars + ( + set -e + # avoid polluting caller scope with internal variable definitions. + if test -f "$nim_csources"; then + echo "$nim_csources exists." + else + if test -d "$nim_csourcesDir"; then + echo "$nim_csourcesDir exists." + else + # depth 1: adjust as needed in case useful for `git bisect` + echo_run git clone -q --depth 1 $nim_csourcesUrl "$nim_csourcesDir" + echo_run git -C "$nim_csourcesDir" checkout $nim_csourcesHash + fi + _nimBuildCsourcesIfNeeded "$@" + fi + + echo_run cp $nim_csources bin/nim + echo_run $nim_csources -v + ) +} diff --git a/ci/nsis_build.bat b/ci/nsis_build.bat deleted file mode 100644 index 12aff1b72..000000000 --- a/ci/nsis_build.bat +++ /dev/null @@ -1,59 +0,0 @@ -REM - Run the full testsuite; testament\tester all - -REM - Uncomment the list of changes in news.txt -REM - write a news ticker entry -REM - Update the version - -REM - Generate the full docs; koch web0 -REM - Generate the installers; -REM - Update the version in system.nim -REM - Test the installers -REM - Tag the release -REM - Merge devel into master -REM - Update csources - -set NIMVER=%1 - -Rem Build -docs file: -koch web0 -cd web\upload -7z a -tzip docs-%NIMVER%.zip *.html -move /y docs-%NIMVER%.zip download -cd ..\.. - -Rem Build csources -koch csources -d:release || exit /b - -rem Grab C sources and nimsuggest -git clone --depth 1 https://github.com/nim-lang/csources_v1.git csources - -set PATH=%CD%\bin;%PATH% - -ReM Build Win32 version: - -set PATH=C:\Users\araq\projects\mingw32\bin;%PATH% -cd csources -call build.bat -cd .. -ReM Rebuilding koch is necessary because it uses its pointer size to determine -ReM which mingw link to put in the NSIS installer. -nim c --out:koch_temp koch || exit /b -koch_temp boot -d:release || exit /b -koch_temp nsis -d:release || exit /b -koch_temp zip -d:release || exit /b -dir build -move /y build\nim_%NIMVER%.exe build\nim-%NIMVER%_x32.exe || exit /b -move /y build\nim-%NIMVER%.zip build\nim-%NIMVER%_x32.zip || exit /b - - -ReM Build Win64 version: -set PATH=C:\Users\araq\projects\mingw64\bin;%PATH% -cd csources -call build64.bat -cd .. -nim c --out:koch_temp koch || exit /b -koch_temp boot -d:release || exit /b -koch_temp nsis -d:release || exit /b -koch_temp zip -d:release || exit /b -move /y build\nim_%NIMVER%.exe build\nim-%NIMVER%_x64.exe || exit /b -move /y build\nim-%NIMVER%.zip build\nim-%NIMVER%_x64.zip || exit /b diff --git a/koch.nim b/koch.nim index 49540eb6a..c78356b58 100644 --- a/koch.nim +++ b/koch.nim @@ -89,7 +89,6 @@ Commands for core developers: tests [options] run the testsuite (run a subset of tests by specifying a category, e.g. `tests cat async`) temp options creates a temporary compiler for testing - pushcsource push generated C sources to its repo Web options: --googleAnalytics:UA-... add the given google analytics code to the docs. To build the official docs, use UA-48159761-1 @@ -283,7 +282,7 @@ template doUseCpp(): bool = getEnv("NIM_COMPILE_TO_CPP", "false") == "true" proc boot(args: string) = ## bootstrapping is a process that involves 3 steps: - ## 1. use csources to produce nim1.exe. This nim1.exe is buggy but + ## 1. use csourcesAny to produce nim1.exe. This nim1.exe is buggy but ## rock solid for building a Nim compiler. It shouldn't be used for anything else. ## 2. use nim1.exe to produce nim2.exe. nim2.exe is the one you really need. ## 3. We use nim2.exe to build nim3.exe. nim3.exe is equal to nim2.exe except for timestamps. @@ -315,13 +314,8 @@ proc boot(args: string) = let ret = execCmdEx(nimStart & " --version") doAssert ret.exitCode == 0 let version = ret.output.splitLines[0] - # remove these when csources get updated - template addLib() = + if version.startsWith "Nim Compiler Version 0.20.0": extraOption.add " --lib:lib" # see https://github.com/nim-lang/Nim/pull/14291 - if version.startsWith "Nim Compiler Version 0.19.0": - extraOption.add " -d:nimBoostrapCsources0_19_0" - addLib() - elif version.startsWith "Nim Compiler Version 0.20.0": addLib() # in order to use less memory, we split the build into two steps: # --compileOnly produces a $project.json file and does not run GCC/Clang. @@ -587,29 +581,6 @@ proc runCI(cmd: string) = when defined(posix): execFold("Run nimsuggest tests", "nim c -r nimsuggest/tester") -proc pushCsources() = - if not dirExists("../csources/.git"): - quit "[Error] no csources git repository found" - csource("-d:danger") - let cwd = getCurrentDir() - try: - copyDir("build/c_code", "../csources/c_code") - copyFile("build/build.sh", "../csources/build.sh") - copyFile("build/build.bat", "../csources/build.bat") - copyFile("build/build64.bat", "../csources/build64.bat") - copyFile("build/makefile", "../csources/makefile") - - setCurrentDir("../csources") - for kind, path in walkDir("c_code"): - if kind == pcDir: - exec("git add " & path / "*.c") - exec("git commit -am \"updated csources to version " & NimVersion & "\"") - exec("git push origin master") - exec("git tag -am \"Version $1\" v$1" % NimVersion) - exec("git push origin v$1" % NimVersion) - finally: - setCurrentDir(cwd) - proc testUnixInstall(cmdLineRest: string) = csource("-d:danger" & cmdLineRest) xz(false, cmdLineRest) @@ -723,7 +694,8 @@ when isMainModule: of "tools": buildTools(op.cmdLineRest) bundleNimbleExe(latest, op.cmdLineRest) - of "pushcsource", "pushcsources": pushCsources() + of "pushcsource": + quit "use this instead: https://github.com/nim-lang/csources_v1/blob/master/push_c_code.nim" of "valgrind": valgrind(op.cmdLineRest) of "c2nim": bundleC2nim(op.cmdLineRest) of "drnim": buildDrNim(op.cmdLineRest) diff --git a/lib/std/private/globs.nim b/lib/std/private/globs.nim index a32f1d1b9..55f6a40bd 100644 --- a/lib/std/private/globs.nim +++ b/lib/std/private/globs.nim @@ -52,5 +52,5 @@ proc nativeToUnixPath*(path: string): string = when isMainModule: import sugar - for a in walkDirRecFilter(".", follow = a=>a.path.lastPathPart notin ["nimcache", ".git", ".csources", "bin"]): + for a in walkDirRecFilter(".", follow = a=>a.path.lastPathPart notin ["nimcache", ".git", "csources_v1", "csources", "bin"]): echo a diff --git a/readme.md b/readme.md index 9a07ae56b..37babf711 100644 --- a/readme.md +++ b/readme.md @@ -45,7 +45,7 @@ Compiling the Nim compiler is quite straightforward if you follow these steps: First, the C source of an older version of the Nim compiler is needed to bootstrap the latest version because the Nim compiler itself is written in the Nim programming language. Those C sources are available within the -[``nim-lang/csources``][csources-repo] repository. +[``nim-lang/csources_v1``][csources-v1-repo] repository. Next, to build from source you will need: @@ -217,7 +217,8 @@ Copyright © 2006-2021 Andreas Rumpf, all rights reserved. [nim-bitcoin]: https://blockchain.info/address/1BXfuKM2uvoD6mbx4g5xM3eQhLzkCK77tJ [nimble-repo]: https://github.com/nim-lang/nimble [nimsuggest-repo]: https://github.com/nim-lang/nimsuggest -[csources-repo]: https://github.com/nim-lang/csources +[csources-repo-deprecated]: https://github.com/nim-lang/csources +[csources-v1-repo]: https://github.com/nim-lang/csources_v1 [badge-nim-travisci]: https://img.shields.io/travis/nim-lang/Nim/devel.svg?style=flat-square [badge-nim-irc]: https://img.shields.io/badge/chat-on_irc-blue.svg?style=flat-square [badge-nim-discord]: https://img.shields.io/discord/371759389889003530?color=blue&label=discord&logo=discord&logoColor=gold&style=flat-square diff --git a/tools/ci_generate.nim b/tools/ci_generate.nim index d13f28c33..e2580ff6b 100644 --- a/tools/ci_generate.nim +++ b/tools/ci_generate.nim @@ -33,15 +33,15 @@ environment: CC: /usr/bin/clang tasks: - setup: | + set -e cd Nim - git clone --depth 1 -q https://github.com/nim-lang/csources.git - gmake -C csources -j $(sysctl -n hw.ncpuonline) - bin/nim c koch + . ci/funs.sh && nimBuildCsourcesIfNeeded + $nim_csources c koch echo 'export PATH=$HOME/Nim/bin:$PATH' >> $HOME/.buildenv - test: | cd Nim if ! ./koch runCI; then - nim c -r tools/ci_testresults.nim + nim r tools/ci_testresults.nim exit 1 fi triggers: From 511b6d2449c65cecc68eccbee1385e02758f0fd4 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 23 Apr 2021 13:50:27 +0200 Subject: [PATCH 2000/3768] typos (#17824) --- compiler/ic/packed_ast.nim | 2 +- doc/destructors.rst | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/ic/packed_ast.nim b/compiler/ic/packed_ast.nim index 6fadde17c..17beda2c1 100644 --- a/compiler/ic/packed_ast.nim +++ b/compiler/ic/packed_ast.nim @@ -86,7 +86,7 @@ type typeInst*: PackedItemId nonUniqueId*: int32 - PackedNode* = object # 20 bytes + PackedNode* = object # 28 bytes kind*: TNodeKind flags*: TNodeFlags operand*: int32 # for kind in {nkSym, nkSymDef}: SymId diff --git a/doc/destructors.rst b/doc/destructors.rst index 01e2d2ee9..c93fcabe1 100644 --- a/doc/destructors.rst +++ b/doc/destructors.rst @@ -277,7 +277,7 @@ inference. But it has to be enabled via `--sinkInference:on`, either on the command line or via a `push` pragma. To enable it for a section of code, one can -use `{.push sinkInference: on.}`...`{.pop.}`. +use `{.push sinkInference: on.}` ... `{.pop.}`. The `.nosinks`:idx: pragma can be used to disable this inference for a single routine: @@ -661,12 +661,12 @@ There is no copy during assigning operations. y[0] = 'h' # copy The program above shows when the copy operations happen. -When mutating the variable `y`, the Nim compiler creates a fresh copy of `x`, -the variable `y` won't point to the string literal anymore. -Instead it points to the copy of `x` of which the memory can be mutated +When mutating the variable `y`, the Nim compiler creates a fresh copy of `x`, +the variable `y` won't point to the string literal anymore. +Instead it points to the copy of `x` of which the memory can be mutated and the variable `y` becomes a mutable string. -.. Note:: The abstraction fails for `addr x` because whether the address is going to be used for mutations is unknown. +.. Note:: The abstraction fails for `addr x` because whether the address is going to be used for mutations is unknown. Let's look at a silly example demonstrating this behaviour: From 3516f57e172cef22f583f43c7b16fd1f63921c6e Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 23 Apr 2021 04:51:24 -0700 Subject: [PATCH 2001/3768] manual: fix rendering of `Identifiers & Keywords` [skip ci] (#17811) --- doc/manual.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/manual.rst b/doc/manual.rst index 15242cf7c..e98a0cc09 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -261,8 +261,9 @@ and underscores, with the following restrictions: * begins with a letter * does not end with an underscore `_` -* two immediate following underscores `__` are not allowed:: +* two immediate following underscores `__` are not allowed: +.. code-block:: letter ::= 'A'..'Z' | 'a'..'z' | '\x80'..'\xff' digit ::= '0'..'9' IDENTIFIER ::= letter ( ['_'] (letter | digit) )* From 2abc936d511342265d2ef27c4b079dd49332d65a Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 23 Apr 2021 05:36:38 -0700 Subject: [PATCH 2002/3768] `typeof(voidStmt)` now works (#17807) * `typeof(voidStmt)` now works * remove typeOrVoid * add condsyms, and reference cligen https://github.com/c-blake/cligen/pull/193 * fixup * changelog [skip ci] * fixup --- changelog.md | 2 + compiler/condsyms.nim | 1 + compiler/sem.nim | 1 + compiler/semdata.nim | 3 ++ compiler/semexprs.nim | 4 +- lib/js/asyncjs.nim | 7 +--- testament/lib/stdtest/testutils.nim | 11 +++--- tests/typerel/tvoid.nim | 61 +++++++++++++++++++++++++++++ 8 files changed, 78 insertions(+), 12 deletions(-) diff --git a/changelog.md b/changelog.md index 7b4a2438a..d7b568aa0 100644 --- a/changelog.md +++ b/changelog.md @@ -331,6 +331,8 @@ - Added a new module `std/importutils`, and an API `privateAccess`, which allows access to private fields for an object type in the current scope. +- `typeof(voidStmt)` now works and returns `void`. + ## Compiler changes - Added `--declaredlocs` to show symbol declaration location in messages. diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index bdecd7e53..6a49584c8 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -133,3 +133,4 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasCustomLiterals") defineSymbol("nimHasUnifiedTuple") defineSymbol("nimHasIterable") + defineSymbol("nimHasTypeofVoid") diff --git a/compiler/sem.nim b/compiler/sem.nim index 4020fc4d6..b80795354 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -525,6 +525,7 @@ proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext var c = newContext(graph, module) c.idgen = idgen c.enforceVoidContext = newType(tyTyped, nextTypeId(idgen), nil) + c.voidType = newType(tyVoid, nextTypeId(idgen), nil) if c.p != nil: internalError(graph.config, module.info, "sem.myOpen") c.semConstExpr = semConstExpr diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 1c1a5159c..082a4813e 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -90,6 +90,9 @@ type TContext* = object of TPassContext # a context represents the module # that is currently being compiled enforceVoidContext*: PType + # for `if cond: stmt else: foo`, `foo` will be evaluated under + # enforceVoidContext != nil + voidType*: PType # for typeof(stmt) module*: PSym # the module sym belonging to the context currentScope*: PScope # current scope moduleScope*: PScope # scope for modules diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 97f88869e..2e229d861 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -83,7 +83,9 @@ proc semExprCheck(c: PContext, n: PNode, flags: TExprFlags): PNode = proc semExprWithType(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = result = semExprCheck(c, n, flags) - if result.typ == nil or result.typ == c.enforceVoidContext: + if result.typ == nil and efInTypeof in flags: + result.typ = c.voidType + elif result.typ == nil or result.typ == c.enforceVoidContext: localError(c.config, n.info, errExprXHasNoType % renderTree(result, {renderNoComments})) result.typ = errorType(c) diff --git a/lib/js/asyncjs.nim b/lib/js/asyncjs.nim index c62ac633f..861fe3fe2 100644 --- a/lib/js/asyncjs.nim +++ b/lib/js/asyncjs.nim @@ -158,10 +158,6 @@ proc newPromise*(handler: proc(resolve: proc())): Future[void] {.importjs: "(new ## A helper for wrapping callback-based functions ## into promises and async procedures. -template typeOrVoid[T](a: T): type = - # xxx this is useful, make it public in std/typetraits in future work - T - template maybeFuture(T): untyped = # avoids `Future[Future[T]]` when T is Future: T @@ -221,7 +217,8 @@ when defined(nimExperimentalAsyncjsThen): assert witness == 3 template impl(call): untyped = - when typeOrVoid(call) is void: + # see D20210421T014713 + when typeof(block: call) is void: var ret: Future[void] else: var ret = default(maybeFuture(typeof(call))) diff --git a/testament/lib/stdtest/testutils.nim b/testament/lib/stdtest/testutils.nim index 44048adbd..241ab1770 100644 --- a/testament/lib/stdtest/testutils.nim +++ b/testament/lib/stdtest/testutils.nim @@ -91,10 +91,6 @@ template disableVm*(body) = when nimvm: discard else: body -template typeOrVoid[T](a: T): type = - # FACTOR with asyncjs.typeOrVoid - T - macro assertAll*(body) = ## works in VM, unlike `check`, `require` runnableExamples: @@ -106,6 +102,9 @@ macro assertAll*(body) = result = newStmtList() for a in body: result.add genAst(a) do: - # better than: `when not compiles(typeof(a)):` - when typeOrVoid(a) is void: a + # D20210421T014713:here + # xxx pending https://github.com/nim-lang/Nim/issues/12030, + # `typeof` should introduce its own scope, so that this + # is sufficient: `typeof(a)` instead of `typeof(block: a)` + when typeof(block: a) is void: a else: doAssert a diff --git a/tests/typerel/tvoid.nim b/tests/typerel/tvoid.nim index 1e46d016e..0b7b525cc 100644 --- a/tests/typerel/tvoid.nim +++ b/tests/typerel/tvoid.nim @@ -35,3 +35,64 @@ ReturnT[void]() echo ReturnT[string]("abc") nothing() +block: # typeof(stmt) + proc fn1(): auto = + discard + proc fn2(): auto = + 1 + doAssert type(fn1()) is void + doAssert typeof(fn1()) is void + doAssert typeof(fn1()) isnot int + + doAssert type(fn2()) isnot void + doAssert typeof(fn2()) isnot void + when typeof(fn1()) is void: discard + else: doAssert false + + doAssert typeof(1+1) is int + doAssert typeof((discard)) is void + + type A1 = typeof(fn1()) + doAssert A1 is void + type A2 = type(fn1()) + doAssert A2 is void + doAssert A2 is A1 + + when false: + # xxx: MCS/UFCS doesn't work here: Error: expression 'fn1()' has no type (or is ambiguous) + type A3 = fn1().type + proc bar[T](a: T): string = $T + doAssert bar(1) == "int" + doAssert bar(fn1()) == "void" + + proc bar2[T](a: T): bool = T is void + doAssert not bar2(1) + doAssert bar2(fn1()) + + block: + proc bar3[T](a: T): T = a + let a1 = bar3(1) + doAssert compiles(block: + let a1 = bar3(fn2())) + doAssert not compiles(block: + let a2 = bar3(fn1())) + doAssert compiles(block: bar3(fn1())) + doAssert compiles(bar3(fn1())) + doAssert typeof(bar3(fn1())) is void + doAssert not compiles(sizeof(bar3(fn1()))) + + block: + var a = 1 + doAssert typeof((a = 2)) is void + doAssert typeof((a = 2; a = 3)) is void + doAssert typeof(block: + a = 2; a = 3) is void + + block: + var a = 1 + template bad1 = echo (a; a = 2) + doAssert not compiles(bad1()) + + block: + template bad2 = echo (nonexistant; discard) + doAssert not compiles(bad2()) From 80df21d9a368b0ad40f6c36b898861e75926b732 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 23 Apr 2021 14:22:04 -0700 Subject: [PATCH 2003/3768] `--declaredlocs` now shows location for `T` instead of `static` in `proc fn(a: static T)` (#17795) --- compiler/types.nim | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/types.nim b/compiler/types.nim index ba2cc76ce..b57e46444 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -131,7 +131,10 @@ proc addDeclaredLocMaybe*(result: var string, conf: ConfigRef; sym: PSym) = addDeclaredLoc(result, conf, sym) proc addDeclaredLoc*(result: var string, conf: ConfigRef; typ: PType) = - let typ = typ.skipTypes(abstractInst - {tyRange}) + # xxx figure out how to resolve `tyGenericParam`, e.g. for + # proc fn[T](a: T, b: T) = discard + # fn(1.1, "a") + let typ = typ.skipTypes(abstractInst + {tyStatic} - {tyRange}) result.add " [$1" % typ.kind.toHumanStr if typ.sym != nil: result.add " declared in " & toFileLineCol(conf, typ.sym.info) From b667e288e27a09ce4f65166fc66a6b713350a9b6 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 23 Apr 2021 22:54:31 -0700 Subject: [PATCH 2004/3768] move travis, appveyor, ci.yml.disabled to `unmaintained/` (#17828) * move travis, appveyor, ci.yml.disabled to unmaintained * update some mentions of travis, appevyor * fix test --- changelog.md | 2 ++ doc/contributing.rst | 4 ++-- testament/specs.nim | 4 ++-- tests/async/tasyncdial.nim | 1 - tests/stdlib/tmemlinesBuf.nim | 8 +------- tests/stdlib/tnetdial.nim | 1 - tests/system/talloc.nim | 6 +----- tests/system/talloc2.nim | 2 +- .travis.yml => unmaintained/.travis.yml | 0 appveyor.yml.disabled => unmaintained/appveyor.yml | 0 .../workflows/ci.yml.disabled => unmaintained/ci.yml | 0 unmaintained/readme.md | 11 +++++++++++ 12 files changed, 20 insertions(+), 19 deletions(-) rename .travis.yml => unmaintained/.travis.yml (100%) rename appveyor.yml.disabled => unmaintained/appveyor.yml (100%) rename .github/workflows/ci.yml.disabled => unmaintained/ci.yml (100%) create mode 100644 unmaintained/readme.md diff --git a/changelog.md b/changelog.md index d7b568aa0..943b5d6df 100644 --- a/changelog.md +++ b/changelog.md @@ -57,6 +57,8 @@ - The unary slice `..b` was removed, use `0..b` instead or use `-d:nimLegacyUnarySlice` for a deprecation period. +- Moved `.travis.yml`, `appveyor.yml.disabled`, `.github/workflows/ci.yml.disabled` to `unmaintained/`. + ## Standard library additions and changes - Added support for parenthesized expressions in `strformat` diff --git a/doc/contributing.rst b/doc/contributing.rst index 81fa9f8eb..abc81dae6 100644 --- a/doc/contributing.rst +++ b/doc/contributing.rst @@ -155,7 +155,7 @@ To run a single test: ./koch test run tests/stdlib/tos.nim # can also provide relative path For reproducible tests (to reproduce an environment more similar to the one -run by Continuous Integration on travis/appveyor), you may want to disable your +run by Continuous Integration on github actions/azure pipelines), you may want to disable your local configuration (e.g. in ``~/.config/nim/nim.cfg``) which may affect some tests; this can also be achieved by using `export XDG_CONFIG_HOME=pathtoAlternateConfig`:cmd: before running `./koch`:cmd: @@ -517,7 +517,7 @@ Continuous Integration (CI) the CI pipeline and affects other PR's; if you don't need it (e.g. for WIP or documentation only changes), add ``[skip ci]`` to your commit message title. This convention is supported by our github actions pipelines and our azure pipeline - as well as our former other pipelines: + (using custom logic, which should complete in < 1mn) as well as our former other pipelines: `Appveyor `_ and `Travis `_. diff --git a/testament/specs.nim b/testament/specs.nim index 043f14ed3..38e4ed0db 100644 --- a/testament/specs.nim +++ b/testament/specs.nim @@ -328,9 +328,9 @@ proc parseSpec*(filename: string): TSpec = when defined(unix): result.err = reDisabled of "posix": when defined(posix): result.err = reDisabled - of "travis": + of "travis": # deprecated if isTravis: result.err = reDisabled - of "appveyor": + of "appveyor": # deprecated if isAppVeyor: result.err = reDisabled of "azure": if isAzure: result.err = reDisabled diff --git a/tests/async/tasyncdial.nim b/tests/async/tasyncdial.nim index a755de74e..f0377dfd5 100644 --- a/tests/async/tasyncdial.nim +++ b/tests/async/tasyncdial.nim @@ -3,7 +3,6 @@ discard """ OK AF_INET OK AF_INET6 ''' - disabled: "travis" """ import diff --git a/tests/stdlib/tmemlinesBuf.nim b/tests/stdlib/tmemlinesBuf.nim index ea607525d..3f0bd5182 100644 --- a/tests/stdlib/tmemlinesBuf.nim +++ b/tests/stdlib/tmemlinesBuf.nim @@ -1,8 +1,3 @@ -discard """ -output: "15" -disabled: "appveyor" -""" - import memfiles var inp = memfiles.open("tests/stdlib/tmemlinesBuf.nim") var buffer: string = "" @@ -11,5 +6,4 @@ for line in lines(inp, buffer): lineCount += 1 close(inp) - -echo lineCount +doAssert lineCount == 9, $lineCount # this file's number of lines diff --git a/tests/stdlib/tnetdial.nim b/tests/stdlib/tnetdial.nim index f940bd630..3de00ff99 100644 --- a/tests/stdlib/tnetdial.nim +++ b/tests/stdlib/tnetdial.nim @@ -2,7 +2,6 @@ discard """ cmd: "nim c --threads:on $file" exitcode: 0 output: "OK" - disabled: "travis" """ import os, net, nativesockets, asyncdispatch diff --git a/tests/system/talloc.nim b/tests/system/talloc.nim index 9b970fda0..a81fef481 100644 --- a/tests/system/talloc.nim +++ b/tests/system/talloc.nim @@ -1,8 +1,4 @@ -discard """ -disabled: "appveyor" -""" - -# appveyor is "out of memory" +# was: appveyor is "out of memory" var x: ptr int diff --git a/tests/system/talloc2.nim b/tests/system/talloc2.nim index c498a3f4b..9d1687f34 100644 --- a/tests/system/talloc2.nim +++ b/tests/system/talloc2.nim @@ -5,7 +5,7 @@ joinable: false disabled: 32bit """ # no point to test this on system with smaller address space -# appveyor is "out of memory" +# was: appveyor is "out of memory" const nmax = 2*1024*1024*1024 diff --git a/.travis.yml b/unmaintained/.travis.yml similarity index 100% rename from .travis.yml rename to unmaintained/.travis.yml diff --git a/appveyor.yml.disabled b/unmaintained/appveyor.yml similarity index 100% rename from appveyor.yml.disabled rename to unmaintained/appveyor.yml diff --git a/.github/workflows/ci.yml.disabled b/unmaintained/ci.yml similarity index 100% rename from .github/workflows/ci.yml.disabled rename to unmaintained/ci.yml diff --git a/unmaintained/readme.md b/unmaintained/readme.md new file mode 100644 index 000000000..cddb15cd2 --- /dev/null +++ b/unmaintained/readme.md @@ -0,0 +1,11 @@ +Deprecated, unused and unmaintained files can be moved here when they have some value, +e.g. if someone want to revive them in their project or in this repo. If a deprecated and unused file +is redundant, it should just be removed instead. + +See also https://github.com/nim-lang/graveyard, which is maintained on a best effort basis +and has different goals. + +## table of contents +.travis.yml -> unmaintained/.travis.yml +appveyor.yml.disabled -> unmaintained/appveyor.yml +.github/workflows/ci.yml.disabled -> unmaintained/ci.yml From 6e8f0f8cfcd6d617074be9868027e39daaeba103 Mon Sep 17 00:00:00 2001 From: flywind Date: Sun, 25 Apr 2021 01:33:29 +0800 Subject: [PATCH 2005/3768] add colon (#17834) --- compiler/pragmas.nim | 2 +- compiler/semexprs.nim | 2 +- compiler/spawn.nim | 2 +- lib/pure/httpclient.nim | 2 +- lib/pure/selectors.nim | 2 +- tests/parallel/t7535.nim | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 35e75bc7b..3a2d9cede 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -824,7 +824,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, makeExternExport(c, sym, getOptionalStr(c, it, "$1"), it.info) if k == wExportCpp: if c.config.backend != backendCpp: - localError(c.config, it.info, "exportcpp requires `cpp` backend, got " & $c.config.backend) + localError(c.config, it.info, "exportcpp requires `cpp` backend, got: " & $c.config.backend) else: incl(sym.flags, sfMangleCpp) incl(sym.flags, sfUsed) # avoid wrong hints diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 2e229d861..cf8d4777f 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2291,7 +2291,7 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = result[i] = semExpr(c, n[i]) if n.len > 1 and n[1].kind notin nkCallKinds: - return localErrorNode(c, n, n[1].info, "'spawn' takes a call expression; got " & $n[1]) + return localErrorNode(c, n, n[1].info, "'spawn' takes a call expression; got: " & $n[1]) let typ = result[^1].typ if not typ.isEmptyType: diff --git a/compiler/spawn.nim b/compiler/spawn.nim index 205197eaf..e57686cb4 100644 --- a/compiler/spawn.nim +++ b/compiler/spawn.nim @@ -334,7 +334,7 @@ proc wrapProcForSpawn*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; spawnExp result = newNodeI(nkStmtList, n.info) if n.kind notin nkCallKinds: - localError(g.config, n.info, "'spawn' takes a call expression; got " & $n) + localError(g.config, n.info, "'spawn' takes a call expression; got: " & $n) return if optThreadAnalysis in g.config.globalOptions: if {tfThread, tfNoSideEffect} * n[0].typ.flags == {}: diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index b60894103..86708dc40 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -751,7 +751,7 @@ proc parseBody(client: HttpClient | AsyncHttpClient, headers: HttpHeaders, httpError("Got disconnected while trying to read body.") if recvLen != length: httpError("Received length doesn't match expected length. Wanted " & - $length & " got " & $recvLen) + $length & " got: " & $recvLen) else: # (http://tools.ietf.org/html/rfc2616#section-4.4) NR.4 TODO diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim index e78219aec..82550e09b 100644 --- a/lib/pure/selectors.nim +++ b/lib/pure/selectors.nim @@ -321,7 +321,7 @@ else: proc verifySelectParams(timeout: int) = # Timeout of -1 means: wait forever # Anything higher is the time to wait in milliseconds. - doAssert(timeout >= -1, "Cannot select with a negative value, got " & $timeout) + doAssert(timeout >= -1, "Cannot select with a negative value, got: " & $timeout) when defined(linux) and not defined(emscripten): include ioselects/ioselectors_epoll diff --git a/tests/parallel/t7535.nim b/tests/parallel/t7535.nim index a6bc0dabe..052dcdc3a 100644 --- a/tests/parallel/t7535.nim +++ b/tests/parallel/t7535.nim @@ -1,6 +1,6 @@ discard """ matrix: "--threads:on" - errormsg: "'spawn' takes a call expression; got proc (x: uint32) = echo [x]" + errormsg: "'spawn' takes a call expression; got: proc (x: uint32) = echo [x]" """ import threadpool From e67593d31797cd6fa5e360c0f0a30fd02e596308 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Sat, 24 Apr 2021 19:34:23 +0200 Subject: [PATCH 2006/3768] Remove unmaintained dir (#17833) --- unmaintained/.travis.yml | 52 ------------------- unmaintained/appveyor.yml | 37 -------------- unmaintained/ci.yml | 105 -------------------------------------- unmaintained/readme.md | 11 ---- 4 files changed, 205 deletions(-) delete mode 100644 unmaintained/.travis.yml delete mode 100644 unmaintained/appveyor.yml delete mode 100644 unmaintained/ci.yml delete mode 100644 unmaintained/readme.md diff --git a/unmaintained/.travis.yml b/unmaintained/.travis.yml deleted file mode 100644 index 13e5d151a..000000000 --- a/unmaintained/.travis.yml +++ /dev/null @@ -1,52 +0,0 @@ -# deprecated, out of date and unused -sudo: false -language: c - -dist: xenial - -matrix: - include: - - os: linux - env: - - NIM_COMPILE_TO_CPP=false - - CPU=amd64 - -addons: - apt: - # update the list above if more deps are introduced - packages: - - libcurl4-openssl-dev - - libsdl1.2-dev - - libgc-dev - - libsfml-dev - - libc6-dbg - - valgrind - -before_script: - - . ci/funs.sh && nimBuildCsourcesIfNeeded LD=$CC ucpu=$CPU - - export PATH="$PWD/bin${PATH:+:$PATH}" - -script: - - echo "travis_fold:start:nim_c_koch" - - nim c koch - - echo "travis_fold:end:nim_c_koch" - - echo "travis_fold:start:koch_boot" - - ./koch boot - - echo "travis_fold:end:koch_boot" - - echo "travis_fold:start:koch_doc" - - ./koch doc - - echo "travis_fold:end:koch_doc" - -before_deploy: - # Make https://nim-lang.github.io/Nim work the same as https://nim-lang.github.io/Nim/overview.html - - cp -f ./doc/html/overview.html ./doc/html/index.html - -deploy: # https://nim-lang.github.io/Nim - provider: pages - # local-dir *has* to be a relative path from the repo root. - local-dir: "doc/html" - skip-cleanup: true - github-token: "$GITHUB_OAUTH_TOKEN" - keep-history: false - on: - branch: devel diff --git a/unmaintained/appveyor.yml b/unmaintained/appveyor.yml deleted file mode 100644 index b13c5d909..000000000 --- a/unmaintained/appveyor.yml +++ /dev/null @@ -1,37 +0,0 @@ -# deprecated, out of date and unused -version: '{build}' - -environment: - DLLS_URL: https://nim-lang.org/download/dlls.zip - DLLS_ARCHIVE: dlls.zip - MINGW_DIR: mingw64 - MINGW_URL: https://nim-lang.org/download/mingw64.7z - MINGW_ARCHIVE: mingw64.7z - - matrix: - - NIM_TEST_PACKAGES: false - - NIM_TEST_PACKAGES: true - -cache: - - '%MINGW_ARCHIVE%' - - '%DLLS_ARCHIVE%' - - -install: - - ps: Install-Product node 8 # node 8 or later is required to test js async stuff - - IF not exist "%DLLS_ARCHIVE%" appveyor DownloadFile "%DLLS_URL%" -FileName "%DLLS_ARCHIVE%" - - 7z x -y "%DLLS_ARCHIVE%" -o"%CD%\BIN"> nul - - IF not exist "%MINGW_ARCHIVE%" appveyor DownloadFile "%MINGW_URL%" -FileName "%MINGW_ARCHIVE%" - - 7z x -y "%MINGW_ARCHIVE%" -o"%CD%\DIST"> nul - - SET PATH=%CD%\DIST\%MINGW_DIR%\BIN;%CD%\BIN;%PATH% - # may need adjustments, untested, see similar examples here: - # https://chromium.googlesource.com/external/github.com/swig/swig/+/1e36f51346d95f8b9848e682c2eb986e9cb9b4f4/appveyor.yml - - bash -c ". ci/funs.sh && nimBuildCsourcesIfNeeded" - -build_script: - - openssl version - - openssl version -d - - bin\nim c koch - - koch runCI - -deploy: off diff --git a/unmaintained/ci.yml b/unmaintained/ci.yml deleted file mode 100644 index 62daf875a..000000000 --- a/unmaintained/ci.yml +++ /dev/null @@ -1,105 +0,0 @@ -# out of date and unused but revivable - -name: Continous Integration -on: [push, pull_request] - -jobs: - build: - strategy: - matrix: - os: [ubuntu-18.04, macos-10.15, windows-2019] - cpu: [amd64, i386] - cpp: ['false', 'true'] - pkg: ['false', 'true'] - exclude: - - os: ubuntu-18.04 - cpp: 'true' - - os: ubuntu-18.04 - pkg: 'true' - - os: macos-10.15 - cpu: i386 - - os: macos-10.15 - pkg: 'true' - - os: windows-2019 - cpu: i386 - - os: windows-2019 - cpp: 'true' - name: '${{ matrix.os }} (${{ matrix.cpu }}, cpp: ${{ matrix.cpp }}, pkg: ${{ matrix.pkg }})' - runs-on: ${{ matrix.os }} - env: - NIM_COMPILE_TO_CPP: ${{ matrix.cpp }} - NIM_TEST_PACKAGES: ${{ matrix.pkg }} - steps: - - name: 'Checkout' - uses: actions/checkout@v2 - - - name: 'Install node.js 8.x' - uses: actions/setup-node@v1 - with: - node-version: '8.x' - - name: 'Install dependencies (Linux amd64)' - if: runner.os == 'Linux' && matrix.cpu == 'amd64' - run: | - sudo apt-fast update -qq - DEBIAN_FRONTEND='noninteractive' \ - sudo apt-fast install --no-install-recommends -yq \ - libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev \ - valgrind libc6-dbg - - name: 'Install dependencies (Linux i386)' - if: runner.os == 'Linux' && matrix.cpu == 'i386' - run: | - sudo dpkg --add-architecture i386 - - sudo apt-fast update -qq - DEBIAN_FRONTEND='noninteractive' \ - sudo apt-fast install --no-install-recommends --allow-downgrades -yq \ - g++-multilib gcc-multilib libcurl4-openssl-dev:i386 libgc-dev:i386 \ - libsdl1.2-dev:i386 libsfml-dev:i386 libglib2.0-dev:i386 \ - libffi-dev:i386 - - cat << EOF > bin/gcc - #!/bin/bash - - exec $(which gcc) -m32 "\$@" - EOF - cat << EOF > bin/g++ - #!/bin/bash - - exec $(which g++) -m32 "\$@" - EOF - - chmod 755 bin/gcc - chmod 755 bin/g++ - - name: 'Install dependencies (macOS)' - if: runner.os == 'macOS' - run: brew install boehmgc make sfml - - name: 'Install dependencies (Windows)' - if: runner.os == 'Windows' - shell: bash - run: | - mkdir dist - curl -L https://nim-lang.org/download/mingw64.7z -o dist/mingw64.7z - curl -L https://nim-lang.org/download/dlls.zip -o dist/dlls.zip - 7z x dist/mingw64.7z -odist - 7z x dist/dlls.zip -obin - echo "${{ github.workspace }}/dist/mingw64/bin" >> "${GITHUB_PATH}" - - - name: 'Add build binaries to PATH' - shell: bash - run: echo "${{ github.workspace }}/bin" >> "${GITHUB_PATH}" - - - name: 'Build csourcesAny' - shell: bash - run: . ci/funs.sh && nimBuildCsourcesIfNeeded CC=gcc ucpu='${{ matrix.cpu }}' - - - name: 'Build koch' - shell: bash - run: nim c koch - - name: 'Run CI' - shell: bash - run: ./koch runCI - - - name: 'Show failed tests' - if: failure() - shell: bash - run: nim c -r tools/ci_testresults.nim diff --git a/unmaintained/readme.md b/unmaintained/readme.md deleted file mode 100644 index cddb15cd2..000000000 --- a/unmaintained/readme.md +++ /dev/null @@ -1,11 +0,0 @@ -Deprecated, unused and unmaintained files can be moved here when they have some value, -e.g. if someone want to revive them in their project or in this repo. If a deprecated and unused file -is redundant, it should just be removed instead. - -See also https://github.com/nim-lang/graveyard, which is maintained on a best effort basis -and has different goals. - -## table of contents -.travis.yml -> unmaintained/.travis.yml -appveyor.yml.disabled -> unmaintained/appveyor.yml -.github/workflows/ci.yml.disabled -> unmaintained/ci.yml From ffe4328b3513617be0d27c773d59bea441ec1c65 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 25 Apr 2021 01:25:31 -0700 Subject: [PATCH 2007/3768] `--usenimcache` (implied by `nim r main`) now caches some compile options to avoid recompiling when project was previously compiled with such options. (#17829) * `--usenimcache` (implied by `nim r main`) now caches some compile options to avoid recompiling when project was previously compiled with such options. * works * add test * changelog * use std/with --- changelog.md | 9 +++++++ compiler/extccomp.nim | 17 +++++++------ compiler/main.nim | 51 ++++++++++++++++++++++++++------------- compiler/options.nim | 15 ------------ tests/misc/mbetterrun.nim | 3 +++ tests/misc/trunner.nim | 48 +++++++++++++++++++++++++++--------- 6 files changed, 91 insertions(+), 52 deletions(-) create mode 100644 tests/misc/mbetterrun.nim diff --git a/changelog.md b/changelog.md index 943b5d6df..43ce43352 100644 --- a/changelog.md +++ b/changelog.md @@ -377,7 +377,16 @@ - `--hint:CC` now goes to stderr (like all other hints) instead of stdout. +- json build instructions are now generated in `$nimcache/outFileBasename.json` + instead of `$nimcache/projectName.json`. This allows avoiding recompiling a given project + compiled with different options if the output file differs. +- `--usenimcache` (implied by `nim r main`) now generates an output file that includes a hash of + some of the compilation options, which allows caching generated binaries: + nim r main # recompiles + nim r -d:foo main # recompiles + nim r main # uses cached binary + nim r main arg1 arg2 # ditto (runtime arguments are irrelevant) ## Tool changes diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 71a20fc47..3b415c499 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -934,9 +934,14 @@ proc callCCompiler*(conf: ConfigRef) = script.add("\n") generateScript(conf, script) - template hashNimExe(): string = $secureHashFile(os.getAppFilename()) +proc jsonBuildInstructionsFile*(conf: ConfigRef): AbsoluteFile = + # `outFile` is better than `projectName`, as it allows having different json + # files for a given source file compiled with different options; it also + # works out of the box with `hashMainCompilationParams`. + result = getNimcacheDir(conf) / conf.outFile.changeFileExt("json") + proc writeJsonBuildInstructions*(conf: ConfigRef) = template lit(x: string) = f.write x template str(x: string) = @@ -993,8 +998,7 @@ proc writeJsonBuildInstructions*(conf: ConfigRef) = var buf = newStringOfCap(50) - - let jsonFile = conf.getNimcacheDir / RelativeFile(conf.projectName & ".json") + let jsonFile = conf.jsonBuildInstructionsFile conf.jsonBuildFile = jsonFile let output = conf.absOutFile @@ -1038,8 +1042,7 @@ proc writeJsonBuildInstructions*(conf: ConfigRef) = lit "\L}\L" close(f) -proc changeDetectedViaJsonBuildInstructions*(conf: ConfigRef; projectfile: AbsoluteFile): bool = - let jsonFile = toGeneratedFile(conf, projectfile, "json") +proc changeDetectedViaJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile): bool = if not fileExists(jsonFile): return true if not fileExists(conf.absOutFile): return true result = false @@ -1090,11 +1093,9 @@ proc changeDetectedViaJsonBuildInstructions*(conf: ConfigRef; projectfile: Absol echo "Warning: JSON processing failed: ", getCurrentExceptionMsg() result = true -proc runJsonBuildInstructions*(conf: ConfigRef; projectfile: AbsoluteFile) = - let jsonFile = toGeneratedFile(conf, projectfile, "json") +proc runJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile) = try: let data = json.parseFile(jsonFile.string) - let output = data["outputFile"].getStr createDir output.parentDir let outputCurrent = $conf.absOutFile diff --git a/compiler/main.nim b/compiler/main.nim index 9c9a789cb..48fbd185c 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -19,7 +19,7 @@ import cgen, json, nversion, platform, nimconf, passaux, depends, vm, modules, - modulegraphs, tables, lineinfos, pathutils, vmprofiler + modulegraphs, tables, lineinfos, pathutils, vmprofiler, std/[sha1, with] import ic / [cbackend, integrity, navigator] from ic / ic import rodViewer @@ -80,15 +80,13 @@ when not defined(leanCompiler): proc commandCompileToC(graph: ModuleGraph) = let conf = graph.config - setOutFile(conf) extccomp.initVars(conf) semanticPasses(graph) if conf.symbolFiles == disabledSf: registerPass(graph, cgenPass) if {optRun, optForceFullMake} * conf.globalOptions == {optRun} or isDefined(conf, "nimBetterRun"): - let proj = changeFileExt(conf.projectFull, "") - if not changeDetectedViaJsonBuildInstructions(conf, proj): + if not changeDetectedViaJsonBuildInstructions(conf, conf.jsonBuildInstructionsFile): # nothing changed graph.config.notes = graph.config.mainPackageNotes return @@ -117,27 +115,20 @@ proc commandCompileToC(graph: ModuleGraph) = writeDepsFile(graph) proc commandJsonScript(graph: ModuleGraph) = - let proj = changeFileExt(graph.config.projectFull, "") - extccomp.runJsonBuildInstructions(graph.config, proj) + extccomp.runJsonBuildInstructions(graph.config, graph.config.jsonBuildInstructionsFile) proc commandCompileToJS(graph: ModuleGraph) = + let conf = graph.config when defined(leanCompiler): - globalError(graph.config, unknownLineInfo, "compiler wasn't built with JS code generator") + globalError(conf, unknownLineInfo, "compiler wasn't built with JS code generator") else: - let conf = graph.config conf.exc = excCpp - - if conf.outFile.isEmpty: - conf.outFile = RelativeFile(conf.projectName & ".js") - - #incl(gGlobalOptions, optSafeCode) - setTarget(graph.config.target, osJS, cpuJS) - #initDefines() - defineSymbol(graph.config.symbols, "ecmascript") # For backward compatibility + setTarget(conf.target, osJS, cpuJS) + defineSymbol(conf.symbols, "ecmascript") # For backward compatibility semanticPasses(graph) registerPass(graph, JSgenPass) compileProject(graph) - if optGenScript in graph.config.globalOptions: + if optGenScript in conf.globalOptions: writeDepsFile(graph) proc interactivePasses(graph: ModuleGraph) = @@ -186,6 +177,31 @@ proc commandView(graph: ModuleGraph) = const PrintRopeCacheStats = false +proc hashMainCompilationParams*(conf: ConfigRef): string = + ## doesn't have to be complete; worst case is a cache hit and recompilation. + var state = newSha1State() + with state: + update os.getAppFilename() # nim compiler + update conf.commandLine # excludes `arguments`, as it should + update $conf.projectFull # so that running `nim r main` from 2 directories caches differently + result = $SecureHash(state.finalize()) + +proc setOutFile*(conf: ConfigRef) = + proc libNameTmpl(conf: ConfigRef): string {.inline.} = + result = if conf.target.targetOS == osWindows: "$1.lib" else: "lib$1.a" + + if conf.outFile.isEmpty: + var base = conf.projectName + if optUseNimcache in conf.globalOptions: + base.add "_" & hashMainCompilationParams(conf) + let targetName = + if conf.backend == backendJs: base & ".js" + elif optGenDynLib in conf.globalOptions: + platform.OS[conf.target.targetOS].dllFrmt % base + elif optGenStaticLib in conf.globalOptions: libNameTmpl(conf) % base + else: base & platform.OS[conf.target.targetOS].exeExt + conf.outFile = RelativeFile targetName + proc mainCommand*(graph: ModuleGraph) = let conf = graph.config let cache = graph.cache @@ -220,6 +236,7 @@ proc mainCommand*(graph: ModuleGraph) = proc compileToBackend() = customizeForBackend(conf.backend) + setOutFile(conf) case conf.backend of backendC: commandCompileToC(graph) of backendCpp: commandCompileToC(graph) diff --git a/compiler/options.nim b/compiler/options.nim index 7044e64bd..209564d0a 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -970,18 +970,3 @@ proc floatInt64Align*(conf: ConfigRef): int16 = # to 4bytes (except with -malign-double) return 4 return 8 - -proc setOutFile*(conf: ConfigRef) = - proc libNameTmpl(conf: ConfigRef): string {.inline.} = - result = if conf.target.targetOS == osWindows: "$1.lib" else: "lib$1.a" - - if conf.outFile.isEmpty: - let base = conf.projectName - let targetName = - if optGenDynLib in conf.globalOptions: - platform.OS[conf.target.targetOS].dllFrmt % base - elif optGenStaticLib in conf.globalOptions: - libNameTmpl(conf) % base - else: - base & platform.OS[conf.target.targetOS].exeExt - conf.outFile = RelativeFile targetName diff --git a/tests/misc/mbetterrun.nim b/tests/misc/mbetterrun.nim new file mode 100644 index 000000000..d4f427af0 --- /dev/null +++ b/tests/misc/mbetterrun.nim @@ -0,0 +1,3 @@ +const mbetterrunVal {.strdefine.} = "" +static: echo "compiling: " & mbetterrunVal +echo "running: " & mbetterrunVal diff --git a/tests/misc/trunner.nim b/tests/misc/trunner.nim index 505a06cf8..2a82ca9ee 100644 --- a/tests/misc/trunner.nim +++ b/tests/misc/trunner.nim @@ -29,13 +29,18 @@ const nimcache = buildDir / "nimcacheTrunner" # instead of `querySetting(nimcacheDir)`, avoids stomping on other parallel tests -proc runCmd(file, options = ""): auto = +proc runNimCmd(file, options = "", rtarg = ""): auto = let fileabs = testsDir / file.unixToNativePath doAssert fileabs.fileExists, fileabs - let cmd = fmt"{nim} {mode} {options} --hints:off {fileabs}" + let cmd = fmt"{nim} {mode} {options} --hints:off {fileabs} {rtarg}" result = execCmdEx(cmd) when false: echo result[0] & "\n" & result[1] # for debugging +proc runNimCmdChk(file, options = "", rtarg = ""): string = + let (ret, status) = runNimCmd(file, options, rtarg = rtarg) + doAssert status == 0, $(file, options) & "\n" & ret + ret + when defined(nimTrunnerFfi): block: # mevalffi when defined(openbsd): @@ -53,8 +58,8 @@ when defined(nimTrunnerFfi): hello world stderr hi stderr """ - let (output, exitCode) = runCmd("vm/mevalffi.nim", fmt"{opt} --experimental:compiletimeFFI") - let expected = fmt""" + let output = runNimCmdChk("vm/mevalffi.nim", fmt"{opt} --experimental:compiletimeFFI") + doAssert output == fmt""" {prefix}foo foo:100 foo:101 @@ -62,12 +67,10 @@ foo:102:103 foo:102:103:104 foo:0.03:asdf:103:105 ret=[s1:foobar s2:foobar age:25 pi:3.14] -""" - doAssert output == expected, output - doAssert exitCode == 0 +""", output else: # don't run twice the same test - import std/[strutils] + import std/strutils template check2(msg) = doAssert msg in output, output block: # tests with various options `nim doc --project --index --docroot` @@ -142,17 +145,16 @@ sub/mmain.idx""", context else: doAssert false block: # mstatic_assert - let (output, exitCode) = runCmd("ccgbugs/mstatic_assert.nim", "-d:caseBad") + let (output, exitCode) = runNimCmd("ccgbugs/mstatic_assert.nim", "-d:caseBad") check2 "sizeof(bool) == 2" check exitCode != 0 block: # ABI checks let file = "misc/msizeof5.nim" block: - let (output, exitCode) = runCmd(file, "-d:checkAbi") - doAssert exitCode == 0, output + discard runNimCmdChk(file, "-d:checkAbi") block: - let (output, exitCode) = runCmd(file, "-d:checkAbi -d:caseBad") + let (output, exitCode) = runNimCmd(file, "-d:checkAbi -d:caseBad") # on platforms that support _StaticAssert natively, errors will show full context, e.g.: # error: static_assert failed due to requirement 'sizeof(unsigned char) == 8' # "backend & Nim disagree on size for: BadImportcType{int64} [declared in mabi_check.nim(1, 6)]" @@ -293,3 +295,25 @@ tests/newconfig/bar/mfoo.nims""".splitLines let (outp, exitCode) = run "echo 1+2; quit(2)" check3 "3" in outp doAssert exitCode == 2 + + block: # nimBetterRun + let file = "misc/mbetterrun.nim" + const nimcache2 = buildDir / "D20210423T185116" + removeDir nimcache2 + # related to `-d:nimBetterRun` + let opt = fmt"-r --usenimcache --nimcache:{nimcache2}" + var ret = "" + for a in @["v1", "v2", "v1", "v3"]: + ret.add runNimCmdChk(file, fmt"{opt} -d:mbetterrunVal:{a}") + ret.add runNimCmdChk(file, fmt"{opt} -d:mbetterrunVal:v2", rtarg = "arg1 arg2") + # rt arguments should not cause a recompilation + doAssert ret == """ +compiling: v1 +running: v1 +compiling: v2 +running: v2 +running: v1 +compiling: v3 +running: v3 +running: v2 +""", ret From 17db15f9b1a03ac93cb6c5c0264f2b534ab8b2ab Mon Sep 17 00:00:00 2001 From: Saem Ghani Date: Sun, 25 Apr 2021 01:30:52 -0700 Subject: [PATCH 2008/3768] fix #17836 (typed macro isNil for proc params) (#17841) thanks @alaviss for the test --- compiler/ast.nim | 4 ++++ compiler/vm.nim | 4 ++-- tests/macros/t17836.nim | 15 +++++++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 tests/macros/t17836.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index 02a2ae7a4..7090bc7ee 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1171,6 +1171,7 @@ when defined(useNodeIds): var gNodeId: int proc newNode*(kind: TNodeKind): PNode = + ## new node with unknown line info, no type, and no children result = PNode(kind: kind, info: unknownLineInfo) when defined(useNodeIds): result.id = gNodeId @@ -1180,6 +1181,7 @@ proc newNode*(kind: TNodeKind): PNode = inc gNodeId proc newNodeI*(kind: TNodeKind, info: TLineInfo): PNode = + ## new node with line info, no type, and no children result = PNode(kind: kind, info: info) when defined(useNodeIds): result.id = gNodeId @@ -1189,6 +1191,7 @@ proc newNodeI*(kind: TNodeKind, info: TLineInfo): PNode = inc gNodeId proc newNodeI*(kind: TNodeKind, info: TLineInfo, children: int): PNode = + ## new node with line info, type, and children result = PNode(kind: kind, info: info) if children > 0: newSeq(result.sons, children) @@ -1200,6 +1203,7 @@ proc newNodeI*(kind: TNodeKind, info: TLineInfo, children: int): PNode = inc gNodeId proc newNodeIT*(kind: TNodeKind, info: TLineInfo, typ: PType): PNode = + ## new node with line info, type, and no children result = newNode(kind) result.info = info result.typ = typ diff --git a/compiler/vm.nim b/compiler/vm.nim index 7a231a482..19ccea55b 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1513,8 +1513,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = # reference with the value `nil`, so `isNil` should be false! (node.kind == nkNilLit and nfIsRef notin node.flags) or (not node.typ.isNil and node.typ.kind == tyProc and - node.typ.callConv == ccClosure and node[0].kind == nkNilLit and - node[1].kind == nkNilLit)) + node.typ.callConv == ccClosure and node.safeLen > 0 and + node[0].kind == nkNilLit and node[1].kind == nkNilLit)) of opcNBindSym: # cannot use this simple check # if dynamicBindSym notin c.config.features: diff --git a/tests/macros/t17836.nim b/tests/macros/t17836.nim new file mode 100644 index 000000000..2453637f5 --- /dev/null +++ b/tests/macros/t17836.nim @@ -0,0 +1,15 @@ +import macros + +# Ensure that `isNil` works in the typed macro context when pass procs. + +type + O = object + fn: proc(i: int): int + +var o: O + +macro typedBug(expr: typed) = + doAssert expr[1] != nil + doAssert not expr[1].isNil + +typedBug(o.fn) \ No newline at end of file From 22e06ee95aeb32929a5b912096dfaa662165c162 Mon Sep 17 00:00:00 2001 From: flywind Date: Sun, 25 Apr 2021 16:34:53 +0800 Subject: [PATCH 2009/3768] Ref #17831(synchapi.h: No such file or directory) (#17832) * Ref #17831 use windows.h * use header: "windows.h" * Update syslocks.nim * Update lib/system/syslocks.nim --- lib/system/syslocks.nim | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/system/syslocks.nim b/lib/system/syslocks.nim index ac52d9a8c..2f0c8b0ba 100644 --- a/lib/system/syslocks.nim +++ b/lib/system/syslocks.nim @@ -24,9 +24,8 @@ when defined(windows): LockSemaphore: int SpinCount: int - RTL_CONDITION_VARIABLE {.importc: "RTL_CONDITION_VARIABLE", header: "synchapi.h".} = object + SysCond {.importc: "RTL_CONDITION_VARIABLE", header: "".} = object thePtr {.importc: "ptr".} : Handle - SysCond = RTL_CONDITION_VARIABLE proc initSysLock(L: var SysLock) {.importc: "InitializeCriticalSection", header: "".} From 9e6f2d7d186f760d8eb7dc26c863f23bd044b9c9 Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 26 Apr 2021 01:16:20 +0800 Subject: [PATCH 2010/3768] bring back jstin after forking a new one (#17844) ref https://github.com/nim-lang/Nim/pull/17765#issuecomment-822019094 --- testament/important_packages.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index ecae42811..64000dbb2 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -77,6 +77,7 @@ pkg "illwill", "nimble examples" pkg "inim", allowFailure=true pkg "itertools", "nim doc src/itertools.nim" pkg "iterutils" +pkg "jstin" pkg "karax", "nim c -r tests/tester.nim" pkg "kdtree", "nimble test", "https://github.com/jblindsay/kdtree" pkg "loopfusion" From 68e522ececdb9f79371f4b60d1fbddcf01736050 Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 26 Apr 2021 15:04:52 +0800 Subject: [PATCH 2011/3768] Remove confusing (#17830) --- compiler/renderer.nim | 5 +---- lib/pure/collections/lists.nim | 14 +++++++------- lib/pure/collections/tables.nim | 14 +++++++------- lib/pure/parsecfg.nim | 10 +++++----- lib/pure/unittest.nim | 6 +++--- lib/system.nim | 4 ---- tests/destructor/tnewruntime_misc.nim | 7 ++++++- 7 files changed, 29 insertions(+), 31 deletions(-) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 4c0de9ed5..8bc3c3306 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -1290,10 +1290,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = put(g, tkSpaces, Space) infixArgument(g, n, 2) of nkPrefix: - if n.len > 0 and n[0].kind == nkIdent and n[0].ident.s == "": - discard "XXX Remove this hack after 0.20 has been released!" - else: - gsub(g, n, 0) + gsub(g, n, 0) if n.len > 1: let opr = if n[0].kind == nkIdent: n[0].ident elif n[0].kind == nkSym: n[0].sym.name diff --git a/lib/pure/collections/lists.nim b/lib/pure/collections/lists.nim index c1fcb0743..c83be7aa3 100644 --- a/lib/pure/collections/lists.nim +++ b/lib/pure/collections/lists.nim @@ -68,7 +68,7 @@ type ## A node of a doubly linked list. ## ## It consists of a `value` field, and pointers to `next` and `prev`. - next*: (DoublyLinkedNode[T]) + next*: DoublyLinkedNode[T] prev* {.cursor.}: DoublyLinkedNode[T] value*: T DoublyLinkedNode*[T] = ref DoublyLinkedNodeObj[T] @@ -77,23 +77,23 @@ type ## A node of a singly linked list. ## ## It consists of a `value` field, and a pointer to `next`. - next*: (SinglyLinkedNode[T]) + next*: SinglyLinkedNode[T] value*: T SinglyLinkedNode*[T] = ref SinglyLinkedNodeObj[T] SinglyLinkedList*[T] = object ## A singly linked list. - head*: (SinglyLinkedNode[T]) + head*: SinglyLinkedNode[T] tail* {.cursor.}: SinglyLinkedNode[T] DoublyLinkedList*[T] = object ## A doubly linked list. - head*: (DoublyLinkedNode[T]) + head*: DoublyLinkedNode[T] tail* {.cursor.}: DoublyLinkedNode[T] SinglyLinkedRing*[T] = object ## A singly linked ring. - head*: (SinglyLinkedNode[T]) + head*: SinglyLinkedNode[T] tail* {.cursor.}: SinglyLinkedNode[T] DoublyLinkedRing*[T] = object @@ -148,7 +148,7 @@ proc initDoublyLinkedRing*[T](): DoublyLinkedRing[T] = discard -proc newDoublyLinkedNode*[T](value: T): (DoublyLinkedNode[T]) = +proc newDoublyLinkedNode*[T](value: T): DoublyLinkedNode[T] = ## Creates a new doubly linked node with the given `value`. runnableExamples: let n = newDoublyLinkedNode[int](5) @@ -157,7 +157,7 @@ proc newDoublyLinkedNode*[T](value: T): (DoublyLinkedNode[T]) = new(result) result.value = value -proc newSinglyLinkedNode*[T](value: T): (SinglyLinkedNode[T]) = +proc newSinglyLinkedNode*[T](value: T): SinglyLinkedNode[T] = ## Creates a new singly linked node with the given `value`. runnableExamples: let n = newSinglyLinkedNode[int](5) diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index 8739b60a3..b71bb0845 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -795,7 +795,7 @@ iterator allValues*[A, B](t: Table[A, B]; key: A): B {.deprecated: # ------------------------------------------------------------------- -proc newTable*[A, B](initialSize = defaultInitialSize): TableRef[A, B] = +proc newTable*[A, B](initialSize = defaultInitialSize): TableRef[A, B] = ## Creates a new ref hash table that is empty. ## ## See also: @@ -810,7 +810,7 @@ proc newTable*[A, B](initialSize = defaultInitialSize): TableRef[A, B] = new(result) result[] = initTable[A, B](initialSize) -proc newTable*[A, B](pairs: openArray[(A, B)]): TableRef[A, B] = +proc newTable*[A, B](pairs: openArray[(A, B)]): TableRef[A, B] = ## Creates a new ref hash table that contains the given `pairs`. ## ## `pairs` is a container consisting of `(key, value)` tuples. @@ -826,7 +826,7 @@ proc newTable*[A, B](pairs: openArray[(A, B)]): TableRef[A, B] = new(result) result[] = toTable[A, B](pairs) -proc newTableFrom*[A, B, C](collection: A, index: proc(x: B): C): TableRef[C, B] = +proc newTableFrom*[A, B, C](collection: A, index: proc(x: B): C): TableRef[C, B] = ## Index the collection with the proc provided. # TODO: As soon as supported, change collection: A to collection: A[B] result = newTable[C, B]() @@ -1790,7 +1790,7 @@ iterator mvalues*[A, B](t: var OrderedTable[A, B]): var B = # --------------------------- OrderedTableRef ------------------------------- # --------------------------------------------------------------------------- -proc newOrderedTable*[A, B](initialSize = defaultInitialSize): OrderedTableRef[A, B] = +proc newOrderedTable*[A, B](initialSize = defaultInitialSize): OrderedTableRef[A, B] = ## Creates a new ordered ref hash table that is empty. ## ## See also: @@ -1805,7 +1805,7 @@ proc newOrderedTable*[A, B](initialSize = defaultInitialSize): OrderedTableR new(result) result[] = initOrderedTable[A, B](initialSize) -proc newOrderedTable*[A, B](pairs: openArray[(A, B)]): OrderedTableRef[A, B] = +proc newOrderedTable*[A, B](pairs: openArray[(A, B)]): OrderedTableRef[A, B] = ## Creates a new ordered ref hash table that contains the given `pairs`. ## ## `pairs` is a container consisting of `(key, value)` tuples. @@ -2610,7 +2610,7 @@ iterator mvalues*[A](t: var CountTable[A]): var int = proc inc*[A](t: CountTableRef[A], key: A, val = 1) -proc newCountTable*[A](initialSize = defaultInitialSize): CountTableRef[A] = +proc newCountTable*[A](initialSize = defaultInitialSize): CountTableRef[A] = ## Creates a new ref count table that is empty. ## ## See also: @@ -2621,7 +2621,7 @@ proc newCountTable*[A](initialSize = defaultInitialSize): CountTableRef[A] = new(result) result[] = initCountTable[A](initialSize) -proc newCountTable*[A](keys: openArray[A]): CountTableRef[A] = +proc newCountTable*[A](keys: openArray[A]): CountTableRef[A] = ## Creates a new ref count table with every member of a container `keys` ## having a count of how many times it occurs in that container. result = newCountTable[A](keys.len) diff --git a/lib/pure/parsecfg.nim b/lib/pure/parsecfg.nim index 7f22a9857..2350cea4a 100644 --- a/lib/pure/parsecfg.nim +++ b/lib/pure/parsecfg.nim @@ -496,17 +496,17 @@ proc next*(c: var CfgParser): CfgEvent {.rtl, extern: "npc$1".} = # ---------------- Configuration file related operations ---------------- type - Config* = OrderedTableRef[string, OrderedTableRef[string, string]] + Config* = OrderedTableRef[string, OrderedTableRef[string, string]] proc newConfig*(): Config = ## Creates a new configuration table. ## Useful when wanting to create a configuration file. - result = newOrderedTable[string, OrderedTableRef[string, string]]() + result = newOrderedTable[string, OrderedTableRef[string, string]]() -proc loadConfig*(stream: Stream, filename: string = "[stream]"): Config = +proc loadConfig*(stream: Stream, filename: string = "[stream]"): Config = ## Loads the specified configuration from stream into a new Config instance. ## `filename` parameter is only used for nicer error messages. - var dict = newOrderedTable[string, OrderedTableRef[string, string]]() + var dict = newOrderedTable[string, OrderedTableRef[string, string]]() var curSection = "" ## Current section, ## the default value of the current section is "", ## which means that the current section is a common @@ -536,7 +536,7 @@ proc loadConfig*(stream: Stream, filename: string = "[stream]"): Config = close(p) result = dict -proc loadConfig*(filename: string): Config = +proc loadConfig*(filename: string): Config = ## Loads the specified configuration file into a new Config instance. let file = open(filename, fmRead) let fileStream = newFileStream(file) diff --git a/lib/pure/unittest.nim b/lib/pure/unittest.nim index 80a05db36..b1c01f8db 100644 --- a/lib/pure/unittest.nim +++ b/lib/pure/unittest.nim @@ -218,7 +218,7 @@ proc resetOutputFormatters* {.since: (1, 1).} = formatters = @[] proc newConsoleOutputFormatter*(outputLevel: OutputLevel = outputLevelDefault, - colorOutput = true): ConsoleOutputFormatter = + colorOutput = true): ConsoleOutputFormatter = ConsoleOutputFormatter( outputLevel: outputLevel, colorOutput: colorOutput @@ -246,7 +246,7 @@ proc colorOutput(): bool = deprecateEnvVarHere() result = false -proc defaultConsoleFormatter*(): ConsoleOutputFormatter = +proc defaultConsoleFormatter*(): ConsoleOutputFormatter = var colorOutput = colorOutput() var outputLevel = nimUnittestOutputLevel.parseEnum[:OutputLevel] when declared(stdout): @@ -315,7 +315,7 @@ proc xmlEscape(s: string): string = else: result.add(c) -proc newJUnitOutputFormatter*(stream: Stream): JUnitOutputFormatter = +proc newJUnitOutputFormatter*(stream: Stream): JUnitOutputFormatter = ## Creates a formatter that writes report to the specified stream in ## JUnit format. ## The ``stream`` is NOT closed automatically when the test are finished, diff --git a/lib/system.nim b/lib/system.nim index 41749c27a..9e9bacf39 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -885,8 +885,6 @@ when defined(nimOwnedEnabled) and not defined(nimscript): proc unown*[T](x: T): T {.magic: "Unown", noSideEffect.} ## Use the expression `x` ignoring its ownership attribute. - # This is only required to make 0.20 compile with the 0.19 line. - template ``*(t: untyped): untyped = owned(t) else: template unown*(x: typed): untyped = x @@ -908,8 +906,6 @@ else: new(r) return r - # This is only required to make 0.20 compile with the 0.19 line. - template ``*(t: untyped): untyped = t template disarm*(x: typed) = ## Useful for `disarming` dangling pointers explicitly for `--newruntime`. diff --git a/tests/destructor/tnewruntime_misc.nim b/tests/destructor/tnewruntime_misc.nim index 1a5b0e3b0..48ea36b7c 100644 --- a/tests/destructor/tnewruntime_misc.nim +++ b/tests/destructor/tnewruntime_misc.nim @@ -25,8 +25,13 @@ putEnv("HEAPTRASHING", "Indeed") let s1 = getAllocStats() + +proc newTableOwned[A, B](initialSize = defaultInitialSize): owned(TableRef[A, B]) = + new(result) + result[] = initTable[A, B](initialSize) + proc main = - var w = newTable[string, owned Node]() + var w = newTableOwned[string, owned Node]() w["key"] = Node(field: "value") echo w["key"][] echo getEnv("HEAPTRASHING") From 01bca8cd4fad857124794144bff09ae0326895bd Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 26 Apr 2021 20:48:25 +0800 Subject: [PATCH 2012/3768] fix gbk encodings on windows (#17843) --- lib/pure/encodings.nim | 1 + tests/stdlib/tencodings.nim | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 tests/stdlib/tencodings.nim diff --git a/lib/pure/encodings.nim b/lib/pure/encodings.nim index 1d8512018..bdc41c6ff 100644 --- a/lib/pure/encodings.nim +++ b/lib/pure/encodings.nim @@ -72,6 +72,7 @@ when defined(windows): (875, "cp875"), # IBM EBCDIC Greek Modern (932, "shift_jis"), # ANSI/OEM Japanese; Japanese (Shift-JIS) (936, "gb2312"), # ANSI/OEM Simplified Chinese (PRC, Singapore); Chinese Simplified (GB2312) + (936, "gbk"), # Alias for GB2312 encoding (949, "ks_c_5601-1987"), # ANSI/OEM Korean (Unified Hangul Code) (950, "big5"), # ANSI/OEM Traditional Chinese (Taiwan; Hong Kong SAR, PRC); Chinese Traditional (Big5) (1026, "IBM1026"), # IBM EBCDIC Turkish (Latin 5) diff --git a/tests/stdlib/tencodings.nim b/tests/stdlib/tencodings.nim new file mode 100644 index 000000000..1da52ba59 --- /dev/null +++ b/tests/stdlib/tencodings.nim @@ -0,0 +1,25 @@ +import std/encodings + +var fromGBK = open("utf-8", "gbk") +var toGBK = open("gbk", "utf-8") + +var fromGB2312 = open("utf-8", "gb2312") +var toGB2312 = open("gb2312", "utf-8") + + +block: + let data = "\215\237\186\243\178\187\214\170\204\236\212\218\203\174\163\172\194\250\180\178\208\199\195\206\209\185\208\199\186\211" + doAssert fromGBK.convert(data) == "醉后不知天在水,满床星梦压星河" + +block: + let data = "万两黄金容易得,知心一个也难求" + doAssert toGBK.convert(data) == "\205\242\193\189\187\198\189\240\200\221\210\215\181\195\163\172\214\170\208\196\210\187\184\246\210\178\196\209\199\243" + + +block: + let data = "\215\212\208\197\200\203\201\250\182\254\176\217\196\234\163\172\187\225\181\177\203\174\187\247\200\253\199\167\192\239" + doAssert fromGB2312.convert(data) == "自信人生二百年,会当水击三千里" + +block: + let data = "谁怕?一蓑烟雨任平生" + doAssert toGB2312.convert(data) == "\203\173\197\194\163\191\210\187\203\242\209\204\211\234\200\206\198\189\201\250" From 01267292d8335b21c69e0e4d840d1fa1078cf1b8 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 26 Apr 2021 11:17:51 -0700 Subject: [PATCH 2013/3768] disable telebot, refs https://github.com/ba0f3/telebot.nim/issues/59 (#17860) --- testament/important_packages.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 64000dbb2..35c0cf0ce 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -139,7 +139,7 @@ pkg "stint", "nim r stint.nim" pkg "strslice" pkg "strunicode", "nim c -r src/strunicode.nim" pkg "synthesis" -pkg "telebot", "nim c -o:tbot -r src/telebot.nim" +pkg "telebot", "nim c -o:tbot -r src/telebot.nim", allowFailure = true # pending https://github.com/ba0f3/telebot.nim/issues/59 pkg "tempdir" pkg "templates" pkg "tensordsl", "nim c -r tests/tests.nim", "https://krux02@bitbucket.org/krux02/tensordslnim.git" From f52aa6bde8ba7b36d0589ee8e5d8319e5152237e Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 26 Apr 2021 11:32:41 -0700 Subject: [PATCH 2014/3768] fix typo in test name undeclared_routime.nim => undeclared_routine.nim (#17861) --- tests/errmsgs/undeclared_routime.nim | 13 ------------- tests/errmsgs/undeclared_routine.nim | 13 +++++++++++++ ...eclared_routime2.nim => undeclared_routine2.nim} | 0 ...eclared_routime3.nim => undeclared_routine3.nim} | 4 ++-- ...eclared_routime4.nim => undeclared_routine4.nim} | 2 +- ...eclared_routime5.nim => undeclared_routine5.nim} | 2 +- ...compiles.nim => undeclared_routine_compiles.nim} | 0 7 files changed, 17 insertions(+), 17 deletions(-) delete mode 100644 tests/errmsgs/undeclared_routime.nim create mode 100644 tests/errmsgs/undeclared_routine.nim rename tests/errmsgs/{undeclared_routime2.nim => undeclared_routine2.nim} (100%) rename tests/errmsgs/{undeclared_routime3.nim => undeclared_routine3.nim} (62%) rename tests/errmsgs/{undeclared_routime4.nim => undeclared_routine4.nim} (69%) rename tests/errmsgs/{undeclared_routime5.nim => undeclared_routine5.nim} (67%) rename tests/errmsgs/{undeclared_routime_compiles.nim => undeclared_routine_compiles.nim} (100%) diff --git a/tests/errmsgs/undeclared_routime.nim b/tests/errmsgs/undeclared_routime.nim deleted file mode 100644 index 7ef12cc0e..000000000 --- a/tests/errmsgs/undeclared_routime.nim +++ /dev/null @@ -1,13 +0,0 @@ -discard """ -cmd: '''nim c --hints:off $file''' -errormsg: "attempting to call routine: 'myiter'" -nimout: '''undeclared_routime.nim(13, 15) Error: attempting to call routine: 'myiter' - found 'undeclared_routime.myiter(a: string)[iterator declared in undeclared_routime.nim(10, 9)]' - found 'undeclared_routime.myiter()[iterator declared in undeclared_routime.nim(11, 9)]' -''' -""" - -iterator myiter(a:string): int = discard -iterator myiter(): int = discard - -let a = myiter(1) diff --git a/tests/errmsgs/undeclared_routine.nim b/tests/errmsgs/undeclared_routine.nim new file mode 100644 index 000000000..126ade52a --- /dev/null +++ b/tests/errmsgs/undeclared_routine.nim @@ -0,0 +1,13 @@ +discard """ +cmd: '''nim c --hints:off $file''' +errormsg: "attempting to call routine: 'myiter'" +nimout: '''undeclared_routine.nim(13, 15) Error: attempting to call routine: 'myiter' + found 'undeclared_routine.myiter(a: string)[iterator declared in undeclared_routine.nim(10, 9)]' + found 'undeclared_routine.myiter()[iterator declared in undeclared_routine.nim(11, 9)]' +''' +""" + +iterator myiter(a:string): int = discard +iterator myiter(): int = discard + +let a = myiter(1) diff --git a/tests/errmsgs/undeclared_routime2.nim b/tests/errmsgs/undeclared_routine2.nim similarity index 100% rename from tests/errmsgs/undeclared_routime2.nim rename to tests/errmsgs/undeclared_routine2.nim diff --git a/tests/errmsgs/undeclared_routime3.nim b/tests/errmsgs/undeclared_routine3.nim similarity index 62% rename from tests/errmsgs/undeclared_routime3.nim rename to tests/errmsgs/undeclared_routine3.nim index 052adfc08..537871900 100644 --- a/tests/errmsgs/undeclared_routime3.nim +++ b/tests/errmsgs/undeclared_routine3.nim @@ -1,8 +1,8 @@ discard """ cmd: '''nim c --hints:off $file''' errormsg: "undeclared field: 'bar'" -nimout: '''undeclared_routime3.nim(13, 10) Error: undeclared field: 'bar' - found 'undeclared_routime3.bar()[declared in undeclared_routime3.nim(12, 9)]' of kind 'iterator' +nimout: '''undeclared_routine3.nim(13, 10) Error: undeclared field: 'bar' + found 'undeclared_routine3.bar()[declared in undeclared_routine3.nim(12, 9)]' of kind 'iterator' ''' """ diff --git a/tests/errmsgs/undeclared_routime4.nim b/tests/errmsgs/undeclared_routine4.nim similarity index 69% rename from tests/errmsgs/undeclared_routime4.nim rename to tests/errmsgs/undeclared_routine4.nim index 674caa421..9012a983f 100644 --- a/tests/errmsgs/undeclared_routime4.nim +++ b/tests/errmsgs/undeclared_routine4.nim @@ -1,7 +1,7 @@ discard """ cmd: '''nim c --hints:off $file''' errormsg: "undeclared field: 'bar'" -nimout: '''undeclared_routime4.nim(10, 10) Error: undeclared field: 'bar' +nimout: '''undeclared_routine4.nim(10, 10) Error: undeclared field: 'bar' ''' """ diff --git a/tests/errmsgs/undeclared_routime5.nim b/tests/errmsgs/undeclared_routine5.nim similarity index 67% rename from tests/errmsgs/undeclared_routime5.nim rename to tests/errmsgs/undeclared_routine5.nim index 4394134ab..0387448a4 100644 --- a/tests/errmsgs/undeclared_routime5.nim +++ b/tests/errmsgs/undeclared_routine5.nim @@ -1,7 +1,7 @@ discard """ cmd: '''nim c --hints:off $file''' errormsg: "undeclared identifier: 'myfun'" -nimout: '''undeclared_routime5.nim(9, 9) Error: undeclared identifier: 'myfun' +nimout: '''undeclared_routine5.nim(9, 9) Error: undeclared identifier: 'myfun' ''' """ diff --git a/tests/errmsgs/undeclared_routime_compiles.nim b/tests/errmsgs/undeclared_routine_compiles.nim similarity index 100% rename from tests/errmsgs/undeclared_routime_compiles.nim rename to tests/errmsgs/undeclared_routine_compiles.nim From 4c9fa9b75cfd6b62ab4d3a8d19fddf7f60c656fd Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 26 Apr 2021 19:14:00 -0700 Subject: [PATCH 2015/3768] fix typo in changelog refs #17746 [skip ci] (#17869) --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 43ce43352..94dbbbeb3 100644 --- a/changelog.md +++ b/changelog.md @@ -345,7 +345,7 @@ - Added `--spellSuggest` to show spelling suggestions on typos. -- Added `--filenames:abs|canonical|magic` which replaces --listFullPaths:on|off +- Added `--filenames:abs|canonical|legacyRelProj` which replaces --listFullPaths:on|off - Added `--processing:dots|filenames|off` which customizes `hintProcessing` From fdfd0f35a1c293a813f743694a5bdd199c2a8433 Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 27 Apr 2021 10:31:52 +0800 Subject: [PATCH 2016/3768] correct changelog[skip CI] (#17870) --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 94dbbbeb3..81a387359 100644 --- a/changelog.md +++ b/changelog.md @@ -57,7 +57,7 @@ - The unary slice `..b` was removed, use `0..b` instead or use `-d:nimLegacyUnarySlice` for a deprecation period. -- Moved `.travis.yml`, `appveyor.yml.disabled`, `.github/workflows/ci.yml.disabled` to `unmaintained/`. +- Removed `.travis.yml`, `appveyor.yml.disabled`, `.github/workflows/ci.yml.disabled`. ## Standard library additions and changes - Added support for parenthesized expressions in `strformat` From 3f58b7face3313a5fd51ab3227eabbcd53da51e2 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 26 Apr 2021 23:23:01 -0700 Subject: [PATCH 2017/3768] add -d:nimLegacyNoHashRef for a transition period which avoids defining hash(ref) (#17858) --- changelog.md | 1 + lib/pure/hashes.nim | 39 ++++++++++++++++++++++----------------- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/changelog.md b/changelog.md index 81a387359..e4b5c39f1 100644 --- a/changelog.md +++ b/changelog.md @@ -51,6 +51,7 @@ for previous behavior. - `hashes.hash` now supports `object` and `ref` (can be overloaded in user code). + For a transition period, use `-d:nimLegacyNoHashRef` to avoid defining `hash(ref)`. - `hashes.hash(proc|ptr|ref|pointer)` now calls `hash(int)` and honors `-d:nimIntHash1`, `hashes.hash(closure)` has also been improved. diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim index 2ef0e2454..83b7a8d75 100644 --- a/lib/pure/hashes.nim +++ b/lib/pure/hashes.nim @@ -228,30 +228,35 @@ proc hash*(x: pointer): Hash {.inline.} = let y = cast[int](x) hash(y) # consistent with code expecting scrambled hashes depending on `nimIntHash1`. -proc hash*[T](x: ref[T] | ptr[T]): Hash {.inline.} = +proc hash*[T](x: ptr[T]): Hash {.inline.} = ## Efficient `hash` overload. runnableExamples: var a: array[10, uint8] assert a[0].addr.hash != a[1].addr.hash assert cast[pointer](a[0].addr).hash == a[0].addr.hash - runnableExamples: - type A = ref object - x: int - let a = A(x: 3) - let ha = a.hash - assert ha != A(x: 3).hash # A(x: 3) is a different ref object from `a`. - a.x = 4 - assert ha == a.hash # the hash only depends on the address - runnableExamples: - # you can overload `hash` if you want to customize semantics - type A[T] = ref object - x, y: T - proc hash(a: A): Hash = hash(a.x) - assert A[int](x: 3, y: 4).hash == A[int](x: 3, y: 5).hash - # xxx pending bug #17733, merge as `proc hash*(pointer | ref | ptr): Hash` - # or `proc hash*[T: ref | ptr](x: T): Hash` hash(cast[pointer](x)) +when not defined(nimLegacyNoHashRef): + proc hash*[T](x: ref[T]): Hash {.inline.} = + ## Efficient `hash` overload. + runnableExamples: + type A = ref object + x: int + let a = A(x: 3) + let ha = a.hash + assert ha != A(x: 3).hash # A(x: 3) is a different ref object from `a`. + a.x = 4 + assert ha == a.hash # the hash only depends on the address + runnableExamples: + # you can overload `hash` if you want to customize semantics + type A[T] = ref object + x, y: T + proc hash(a: A): Hash = hash(a.x) + assert A[int](x: 3, y: 4).hash == A[int](x: 3, y: 5).hash + # xxx pending bug #17733, merge as `proc hash*(pointer | ref | ptr): Hash` + # or `proc hash*[T: ref | ptr](x: T): Hash` + hash(cast[pointer](x)) + proc hash*[T: proc](x: T): Hash {.inline.} = ## Efficient hashing of proc vars. Closures are supported too. when T is "closure": From b1c7c994b0bcd39776fce16bdda24358f1fbb0d4 Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 27 Apr 2021 16:24:59 +0800 Subject: [PATCH 2018/3768] [std/parsecfg]use runnableExamples (#17868) * [std/parsecfg]use runnableExamples * Apply suggestions from code review Co-authored-by: Timothee Cour --- lib/pure/parsecfg.nim | 191 +++++++++++++++++++++--------------------- 1 file changed, 95 insertions(+), 96 deletions(-) diff --git a/lib/pure/parsecfg.nim b/lib/pure/parsecfg.nim index 2350cea4a..0ee19912c 100644 --- a/lib/pure/parsecfg.nim +++ b/lib/pure/parsecfg.nim @@ -19,37 +19,32 @@ ## :literal: ## ## Here is an example of how to use the configuration file parser: -## -## .. code-block:: nim -## -## import std/[os, parsecfg, strutils, streams] -## -## var f = newFileStream(paramStr(1), fmRead) -## if f != nil: -## var p: CfgParser -## open(p, f, paramStr(1)) -## while true: -## var e = next(p) -## case e.kind -## of cfgEof: break -## of cfgSectionStart: ## a `[section]` has been parsed -## echo("new section: " & e.section) -## of cfgKeyValuePair: -## echo("key-value-pair: " & e.key & ": " & e.value) -## of cfgOption: -## echo("command: " & e.key & ": " & e.value) -## of cfgError: -## echo(e.msg) -## close(p) -## else: -## echo("cannot open: " & paramStr(1)) -## -## -## Examples -## ======== -## +runnableExamples("-r:off"): + import std/[strutils, streams] + + let configFile = "example.ini" + var f = newFileStream(configFile, fmRead) + assert f != nil, "cannot open " & configFile + var p: CfgParser + open(p, f, configFile) + while true: + var e = next(p) + case e.kind + of cfgEof: break + of cfgSectionStart: ## a `[section]` has been parsed + echo "new section: " & e.section + of cfgKeyValuePair: + echo "key-value-pair: " & e.key & ": " & e.value + of cfgOption: + echo "command: " & e.key & ": " & e.value + of cfgError: + echo e.msg + close(p) + +##[ ## Configuration file example -## -------------------------- +]## + ## ## .. code-block:: nim ## @@ -58,60 +53,64 @@ ## name = "hello" ## --threads:on ## [Author] -## name = "lihf8515" -## qq = "10214028" -## email = "lihaifeng@wxm.com" -## +## name = "nim-lang" +## website = "nim-lang.org" + +##[ ## Creating a configuration file -## ----------------------------- -## .. code-block:: nim -## -## import std/parsecfg -## var dict=newConfig() -## dict.setSectionKey("","charset","utf-8") -## dict.setSectionKey("Package","name","hello") -## dict.setSectionKey("Package","--threads","on") -## dict.setSectionKey("Author","name","lihf8515") -## dict.setSectionKey("Author","qq","10214028") -## dict.setSectionKey("Author","email","lihaifeng@wxm.com") -## dict.writeConfig("config.ini") -## +]## + +runnableExamples: + var dict = newConfig() + dict.setSectionKey("","charset", "utf-8") + dict.setSectionKey("Package", "name", "hello") + dict.setSectionKey("Package", "--threads", "on") + dict.setSectionKey("Author", "name", "nim-lang") + dict.setSectionKey("Author", "website", "nim-lang.org") + assert $dict == """ +charset=utf-8 +[Package] +name=hello +--threads:on +[Author] +name=nim-lang +website=nim-lang.org +""" + +##[ ## Reading a configuration file -## ---------------------------- -## .. code-block:: nim -## -## import std/parsecfg -## var dict = loadConfig("config.ini") -## var charset = dict.getSectionValue("","charset") -## var threads = dict.getSectionValue("Package","--threads") -## var pname = dict.getSectionValue("Package","name") -## var name = dict.getSectionValue("Author","name") -## var qq = dict.getSectionValue("Author","qq") -## var email = dict.getSectionValue("Author","email") -## echo pname & "\n" & name & "\n" & qq & "\n" & email -## +]## + +runnableExamples("-r:off"): + let dict = loadConfig("config.ini") + let charset = dict.getSectionValue("","charset") + let threads = dict.getSectionValue("Package","--threads") + let pname = dict.getSectionValue("Package","name") + let name = dict.getSectionValue("Author","name") + let website = dict.getSectionValue("Author","website") + echo pname & "\n" & name & "\n" & website + +##[ ## Modifying a configuration file -## ------------------------------ -## .. code-block:: nim -## -## import std/parsecfg -## var dict = loadConfig("config.ini") -## dict.setSectionKey("Author","name","lhf") -## dict.writeConfig("config.ini") -## +]## + +runnableExamples("-r:off"): + var dict = loadConfig("config.ini") + dict.setSectionKey("Author", "name", "nim-lang") + dict.writeConfig("config.ini") + +##[ ## Deleting a section key in a configuration file -## ---------------------------------------------- -## .. code-block:: nim -## -## import std/parsecfg -## var dict = loadConfig("config.ini") -## dict.delSectionKey("Author","email") -## dict.writeConfig("config.ini") -## +]## + +runnableExamples("-r:off"): + var dict = loadConfig("config.ini") + dict.delSectionKey("Author", "website") + dict.writeConfig("config.ini") + +##[ ## Supported INI File structure -## ---------------------------- -## The examples below are supported: -## +]## # taken from https://docs.python.org/3/library/configparser.html#supported-ini-file-structure runnableExamples: @@ -150,27 +149,27 @@ runnableExamples: ) let section1 = "Simple Values" - doAssert dict.getSectionValue(section1, "key") == "value" - doAssert dict.getSectionValue(section1, "spaces in keys") == "allowed" - doAssert dict.getSectionValue(section1, "spaces in values") == "allowed as well" - doAssert dict.getSectionValue(section1, "spaces around the delimiter") == "obviously" - doAssert dict.getSectionValue(section1, "you can also use") == "to delimit keys from values" + assert dict.getSectionValue(section1, "key") == "value" + assert dict.getSectionValue(section1, "spaces in keys") == "allowed" + assert dict.getSectionValue(section1, "spaces in values") == "allowed as well" + assert dict.getSectionValue(section1, "spaces around the delimiter") == "obviously" + assert dict.getSectionValue(section1, "you can also use") == "to delimit keys from values" let section2 = "All Values Are Strings" - doAssert dict.getSectionValue(section2, "values like this") == "19990429" - doAssert dict.getSectionValue(section2, "or this") == "3.14159265359" - doAssert dict.getSectionValue(section2, "are they treated as numbers") == "no" - doAssert dict.getSectionValue(section2, "integers floats and booleans are held as") == "strings" - doAssert dict.getSectionValue(section2, "can use the API to get converted values directly") == "true" + assert dict.getSectionValue(section2, "values like this") == "19990429" + assert dict.getSectionValue(section2, "or this") == "3.14159265359" + assert dict.getSectionValue(section2, "are they treated as numbers") == "no" + assert dict.getSectionValue(section2, "integers floats and booleans are held as") == "strings" + assert dict.getSectionValue(section2, "can use the API to get converted values directly") == "true" let section3 = "Seletion A" - doAssert dict.getSectionValue(section3, + assert dict.getSectionValue(section3, "space around section name will be ignored", "not an empty value") == "" let section4 = "Sections Can Be Indented" - doAssert dict.getSectionValue(section4, "can_values_be_as_well") == "True" - doAssert dict.getSectionValue(section4, "does_that_mean_anything_special") == "False" - doAssert dict.getSectionValue(section4, "purpose") == "formatting for readability" + assert dict.getSectionValue(section4, "can_values_be_as_well") == "True" + assert dict.getSectionValue(section4, "does_that_mean_anything_special") == "False" + assert dict.getSectionValue(section4, "purpose") == "formatting for readability" import strutils, lexbase, streams, tables import std/private/decode_helpers @@ -564,7 +563,7 @@ proc replace(s: string): string = proc writeConfig*(dict: Config, stream: Stream) = ## Writes the contents of the table to the specified stream. ## - ## **Note:** Comment statement will be ignored. + ## .. note:: Comment statement will be ignored. for section, sectionData in dict.pairs(): if section != "": ## Not general section if not allCharsInSet(section, SymChars): ## Non system character @@ -604,7 +603,7 @@ proc writeConfig*(dict: Config, stream: Stream) = proc `$`*(dict: Config): string = ## Writes the contents of the table to string. ## - ## **Note:** Comment statement will be ignored. + ## .. note:: Comment statement will be ignored. let stream = newStringStream() defer: stream.close() dict.writeConfig(stream) @@ -613,7 +612,7 @@ proc `$`*(dict: Config): string = proc writeConfig*(dict: Config, filename: string) = ## Writes the contents of the table to the specified configuration file. ## - ## **Note:** Comment statement will be ignored. + ## .. note:: Comment statement will be ignored. let file = open(filename, fmWrite) defer: file.close() let fileStream = newFileStream(file) From 93c26041a5484373512069512471bbde943a7242 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 27 Apr 2021 01:42:24 -0700 Subject: [PATCH 2019/3768] fix #17859; rename tests so they run in CI; merge several tests with nim check (#17862) * rename a test so it runs in CI; merge several tests with nim check * continue * continue * continue * rename tests/errmsgs/undeclared_routine_compiles.nim -> tests/errmsgs/tundeclared_routine_compiles.nim --- tests/errmsgs/tundeclared_routine.nim | 44 +++++++++++++++++++ ...s.nim => tundeclared_routine_compiles.nim} | 0 tests/errmsgs/undeclared_routine.nim | 13 ------ tests/errmsgs/undeclared_routine2.nim | 9 ---- tests/errmsgs/undeclared_routine3.nim | 13 ------ tests/errmsgs/undeclared_routine4.nim | 10 ----- tests/errmsgs/undeclared_routine5.nim | 9 ---- 7 files changed, 44 insertions(+), 54 deletions(-) create mode 100644 tests/errmsgs/tundeclared_routine.nim rename tests/errmsgs/{undeclared_routine_compiles.nim => tundeclared_routine_compiles.nim} (100%) delete mode 100644 tests/errmsgs/undeclared_routine.nim delete mode 100644 tests/errmsgs/undeclared_routine2.nim delete mode 100644 tests/errmsgs/undeclared_routine3.nim delete mode 100644 tests/errmsgs/undeclared_routine4.nim delete mode 100644 tests/errmsgs/undeclared_routine5.nim diff --git a/tests/errmsgs/tundeclared_routine.nim b/tests/errmsgs/tundeclared_routine.nim new file mode 100644 index 000000000..2f1320fff --- /dev/null +++ b/tests/errmsgs/tundeclared_routine.nim @@ -0,0 +1,44 @@ +discard """ +cmd: '''nim check --hints:off $file''' +action: reject +nimout: ''' +tundeclared_routine.nim(24, 17) Error: attempting to call routine: 'myiter' + found tundeclared_routine.myiter(a: string) [iterator declared in tundeclared_routine.nim(22, 12)] + found tundeclared_routine.myiter() [iterator declared in tundeclared_routine.nim(23, 12)] +tundeclared_routine.nim(29, 28) Error: invalid pragma: myPragma +tundeclared_routine.nim(36, 13) Error: undeclared field: 'bar3' for type tundeclared_routine.Foo [type declared in tundeclared_routine.nim(33, 8)] + found tundeclared_routine.bar3() [iterator declared in tundeclared_routine.nim(35, 12)] +tundeclared_routine.nim(41, 13) Error: undeclared field: 'bar4' for type tundeclared_routine.Foo [type declared in tundeclared_routine.nim(39, 8)] +tundeclared_routine.nim(44, 15) Error: attempting to call routine: 'bad5' +''' +""" + + + + + +# line 20 +block: + iterator myiter(a:string): int = discard + iterator myiter(): int = discard + let a = myiter(1) + +block: + proc myPragma():int=discard + iterator myPragma():int=discard + proc myfun(a:int): int {.myPragma.} = 1 + let a = myfun(1) + +block: + type Foo = object + var a = Foo() + iterator bar3():int=discard + let a2 = a.bar3 + +block: + type Foo = object + var a = Foo() + let a2 = a.bar4 + +block: + let a = bad5(1) diff --git a/tests/errmsgs/undeclared_routine_compiles.nim b/tests/errmsgs/tundeclared_routine_compiles.nim similarity index 100% rename from tests/errmsgs/undeclared_routine_compiles.nim rename to tests/errmsgs/tundeclared_routine_compiles.nim diff --git a/tests/errmsgs/undeclared_routine.nim b/tests/errmsgs/undeclared_routine.nim deleted file mode 100644 index 126ade52a..000000000 --- a/tests/errmsgs/undeclared_routine.nim +++ /dev/null @@ -1,13 +0,0 @@ -discard """ -cmd: '''nim c --hints:off $file''' -errormsg: "attempting to call routine: 'myiter'" -nimout: '''undeclared_routine.nim(13, 15) Error: attempting to call routine: 'myiter' - found 'undeclared_routine.myiter(a: string)[iterator declared in undeclared_routine.nim(10, 9)]' - found 'undeclared_routine.myiter()[iterator declared in undeclared_routine.nim(11, 9)]' -''' -""" - -iterator myiter(a:string): int = discard -iterator myiter(): int = discard - -let a = myiter(1) diff --git a/tests/errmsgs/undeclared_routine2.nim b/tests/errmsgs/undeclared_routine2.nim deleted file mode 100644 index 3e48b48f4..000000000 --- a/tests/errmsgs/undeclared_routine2.nim +++ /dev/null @@ -1,9 +0,0 @@ -discard """ -cmd: '''nim c --hints:off $file''' -errormsg: "invalid pragma: myPragma" -""" - -proc myPragma():int=discard -iterator myPragma():int=discard -proc myfun(a:int): int {.myPragma.} = 1 -let a = myfun(1) diff --git a/tests/errmsgs/undeclared_routine3.nim b/tests/errmsgs/undeclared_routine3.nim deleted file mode 100644 index 537871900..000000000 --- a/tests/errmsgs/undeclared_routine3.nim +++ /dev/null @@ -1,13 +0,0 @@ -discard """ -cmd: '''nim c --hints:off $file''' -errormsg: "undeclared field: 'bar'" -nimout: '''undeclared_routine3.nim(13, 10) Error: undeclared field: 'bar' - found 'undeclared_routine3.bar()[declared in undeclared_routine3.nim(12, 9)]' of kind 'iterator' -''' -""" - - -type Foo = object -var a = Foo() -iterator bar():int=discard -let a = a.bar diff --git a/tests/errmsgs/undeclared_routine4.nim b/tests/errmsgs/undeclared_routine4.nim deleted file mode 100644 index 9012a983f..000000000 --- a/tests/errmsgs/undeclared_routine4.nim +++ /dev/null @@ -1,10 +0,0 @@ -discard """ -cmd: '''nim c --hints:off $file''' -errormsg: "undeclared field: 'bar'" -nimout: '''undeclared_routine4.nim(10, 10) Error: undeclared field: 'bar' -''' -""" - -type Foo = object -var a = Foo() -let a = a.bar diff --git a/tests/errmsgs/undeclared_routine5.nim b/tests/errmsgs/undeclared_routine5.nim deleted file mode 100644 index 0387448a4..000000000 --- a/tests/errmsgs/undeclared_routine5.nim +++ /dev/null @@ -1,9 +0,0 @@ -discard """ -cmd: '''nim c --hints:off $file''' -errormsg: "undeclared identifier: 'myfun'" -nimout: '''undeclared_routine5.nim(9, 9) Error: undeclared identifier: 'myfun' -''' -""" - - -let a = myfun(1) From 66022423aa21710f57f36e4f3c8c9633567cdb5e Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 27 Apr 2021 17:09:58 +0800 Subject: [PATCH 2020/3768] [std/pegs] increase test coverage (#17871) --- lib/pure/pegs.nim | 144 ------------------------------------- tests/stdlib/tpegs.nim | 158 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 154 insertions(+), 148 deletions(-) diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim index 06a77af57..b5d9b1d07 100644 --- a/lib/pure/pegs.nim +++ b/lib/pure/pegs.nim @@ -2041,147 +2041,3 @@ proc escapePeg*(s: string): string = inQuote = true result.add(c) if inQuote: result.add('\'') - -when isMainModule: - proc pegsTest() = - assert escapePeg("abc''def'") == r"'abc'\x27\x27'def'\x27" - assert match("(a b c)", peg"'(' @ ')'") - assert match("W_HI_Le", peg"\y 'while'") - assert(not match("W_HI_L", peg"\y 'while'")) - assert(not match("W_HI_Le", peg"\y v'while'")) - assert match("W_HI_Le", peg"y'while'") - - assert($ +digits == $peg"\d+") - assert "0158787".match(peg"\d+") - assert "ABC 0232".match(peg"\w+\s+\d+") - assert "ABC".match(peg"\d+ / \w+") - - var accum: seq[string] = @[] - for word in split("00232this02939is39an22example111", peg"\d+"): - accum.add(word) - assert(accum == @["this", "is", "an", "example"]) - - assert matchLen("key", ident) == 3 - - var pattern = sequence(ident, *whitespace, term('='), *whitespace, ident) - assert matchLen("key1= cal9", pattern) == 11 - - var ws = newNonTerminal("ws", 1, 1) - ws.rule = *whitespace - - var expr = newNonTerminal("expr", 1, 1) - expr.rule = sequence(capture(ident), *sequence( - nonterminal(ws), term('+'), nonterminal(ws), nonterminal(expr))) - - var c: Captures - var s = "a+b + c +d+e+f" - assert rawMatch(s, expr.rule, 0, c) == len(s) - var a = "" - for i in 0..c.ml-1: - a.add(substr(s, c.matches[i][0], c.matches[i][1])) - assert a == "abcdef" - #echo expr.rule - - #const filename = "lib/devel/peg/grammar.txt" - #var grammar = parsePeg(newFileStream(filename, fmRead), filename) - #echo "a <- [abc]*?".match(grammar) - assert find("_____abc_______", term("abc"), 2) == 5 - assert match("_______ana", peg"A <- 'ana' / . A") - assert match("abcs%%%", peg"A <- ..A / .A / '%'") - - var matches: array[0..MaxSubpatterns-1, string] - if "abc" =~ peg"{'a'}'bc' 'xyz' / {\ident}": - assert matches[0] == "abc" - else: - assert false - - var g2 = peg"""S <- A B / C D - A <- 'a'+ - B <- 'b'+ - C <- 'c'+ - D <- 'd'+ - """ - assert($g2 == "((A B) / (C D))") - assert match("cccccdddddd", g2) - assert("var1=key; var2=key2".replacef(peg"{\ident}'='{\ident}", "$1<-$2$2") == - "var1<-keykey; var2<-key2key2") - assert("var1=key; var2=key2".replace(peg"{\ident}'='{\ident}", "$1<-$2$2") == - "$1<-$2$2; $1<-$2$2") - assert "var1=key; var2=key2".endsWith(peg"{\ident}'='{\ident}") - - if "aaaaaa" =~ peg"'aa' !. / ({'a'})+": - assert matches[0] == "a" - else: - assert false - - if match("abcdefg", peg"c {d} ef {g}", matches, 2): - assert matches[0] == "d" - assert matches[1] == "g" - else: - assert false - - accum = @[] - for x in findAll("abcdef", peg".", 3): - accum.add(x) - assert(accum == @["d", "e", "f"]) - - for x in findAll("abcdef", peg"^{.}", 3): - assert x == "d" - - if "f(a, b)" =~ peg"{[0-9]+} / ({\ident} '(' {@} ')')": - assert matches[0] == "f" - assert matches[1] == "a, b" - else: - assert false - - assert match("eine übersicht und außerdem", peg"(\letter \white*)+") - # ß is not a lower cased letter?! - assert match("eine übersicht und auerdem", peg"(\lower \white*)+") - assert match("EINE ÜBERSICHT UND AUSSERDEM", peg"(\upper \white*)+") - assert(not match("456678", peg"(\letter)+")) - - assert("var1 = key; var2 = key2".replacef( - peg"\skip(\s*) {\ident}'='{\ident}", "$1<-$2$2") == - "var1<-keykey;var2<-key2key2") - - assert match("prefix/start", peg"^start$", 7) - - if "foo" =~ peg"{'a'}?.*": - assert matches[0].len == 0 - else: assert false - - if "foo" =~ peg"{''}.*": - assert matches[0] == "" - else: assert false - - if "foo" =~ peg"{'foo'}": - assert matches[0] == "foo" - else: assert false - - let empty_test = peg"^\d*" - let str = "XYZ" - - assert(str.find(empty_test) == 0) - assert(str.match(empty_test)) - - proc handleMatches(m: int, n: int, c: openArray[string]): string = - result = "" - - if m > 0: - result.add ", " - - result.add case n: - of 2: toLowerAscii(c[0]) & ": '" & c[1] & "'" - of 1: toLowerAscii(c[0]) & ": ''" - else: "" - - assert("Var1=key1;var2=Key2; VAR3". - replace(peg"{\ident}('='{\ident})* ';'* \s*", - handleMatches) == "var1: 'key1', var2: 'Key2', var3: ''") - - - doAssert "test1".match(peg"""{@}$""") - doAssert "test2".match(peg"""{(!$ .)*} $""") - pegsTest() - static: - pegsTest() diff --git a/tests/stdlib/tpegs.nim b/tests/stdlib/tpegs.nim index 99b7dc6f4..f2c93c2e5 100644 --- a/tests/stdlib/tpegs.nim +++ b/tests/stdlib/tpegs.nim @@ -1,5 +1,5 @@ discard """ - targets: "c js" + targets: "c cpp js" output: ''' PEG AST traversal output ------------------------ @@ -51,8 +51,7 @@ Event parser output ''' """ -import strutils, streams -import pegs +import std/[strutils, streams, pegs] const indent = " " @@ -125,7 +124,7 @@ block: discard of "Sum", "Product": try: - let val = matchStr.parseFloat + let val {.used.} = matchStr.parseFloat except ValueError: if valStack.len > 1 and opStack.len > 0: valStack[^2] = case opStack[^1] @@ -147,3 +146,154 @@ block: echo "-------------------" let pLen = parseArithExpr(txt) doAssert txt.len == pLen + + +import std/importutils + +block: + proc pegsTest() = + privateAccess(NonTerminal) + privateAccess(Captures) + + doAssert escapePeg("abc''def'") == r"'abc'\x27\x27'def'\x27" + doAssert match("(a b c)", peg"'(' @ ')'") + doAssert match("W_HI_Le", peg"\y 'while'") + doAssert(not match("W_HI_L", peg"\y 'while'")) + doAssert(not match("W_HI_Le", peg"\y v'while'")) + doAssert match("W_HI_Le", peg"y'while'") + + doAssert($ +digits == $peg"\d+") + doAssert "0158787".match(peg"\d+") + doAssert "ABC 0232".match(peg"\w+\s+\d+") + doAssert "ABC".match(peg"\d+ / \w+") + + var accum: seq[string] = @[] + for word in split("00232this02939is39an22example111", peg"\d+"): + accum.add(word) + doAssert(accum == @["this", "is", "an", "example"]) + + doAssert matchLen("key", ident) == 3 + + var pattern = sequence(ident, *whitespace, term('='), *whitespace, ident) + doAssert matchLen("key1= cal9", pattern) == 11 + + var ws = newNonTerminal("ws", 1, 1) + ws.rule = *whitespace + + var expr = newNonTerminal("expr", 1, 1) + expr.rule = sequence(capture(ident), *sequence( + nonterminal(ws), term('+'), nonterminal(ws), nonterminal(expr))) + + var c: Captures + var s = "a+b + c +d+e+f" + doAssert rawMatch(s, expr.rule, 0, c) == len(s) + var a = "" + for i in 0..c.ml-1: + a.add(substr(s, c.matches[i][0], c.matches[i][1])) + doAssert a == "abcdef" + #echo expr.rule + + #const filename = "lib/devel/peg/grammar.txt" + #var grammar = parsePeg(newFileStream(filename, fmRead), filename) + #echo "a <- [abc]*?".match(grammar) + doAssert find("_____abc_______", term("abc"), 2) == 5 + doAssert match("_______ana", peg"A <- 'ana' / . A") + doAssert match("abcs%%%", peg"A <- ..A / .A / '%'") + + var matches: array[0..MaxSubpatterns-1, string] + if "abc" =~ peg"{'a'}'bc' 'xyz' / {\ident}": + doAssert matches[0] == "abc" + else: + doAssert false + + var g2 = peg"""S <- A B / C D + A <- 'a'+ + B <- 'b'+ + C <- 'c'+ + D <- 'd'+ + """ + doAssert($g2 == "((A B) / (C D))") + doAssert match("cccccdddddd", g2) + doAssert("var1=key; var2=key2".replacef(peg"{\ident}'='{\ident}", "$1<-$2$2") == + "var1<-keykey; var2<-key2key2") + doAssert("var1=key; var2=key2".replace(peg"{\ident}'='{\ident}", "$1<-$2$2") == + "$1<-$2$2; $1<-$2$2") + doAssert "var1=key; var2=key2".endsWith(peg"{\ident}'='{\ident}") + + if "aaaaaa" =~ peg"'aa' !. / ({'a'})+": + doAssert matches[0] == "a" + else: + doAssert false + + if match("abcdefg", peg"c {d} ef {g}", matches, 2): + doAssert matches[0] == "d" + doAssert matches[1] == "g" + else: + doAssert false + + accum = @[] + for x in findAll("abcdef", peg".", 3): + accum.add(x) + doAssert(accum == @["d", "e", "f"]) + + for x in findAll("abcdef", peg"^{.}", 3): + doAssert x == "d" + + if "f(a, b)" =~ peg"{[0-9]+} / ({\ident} '(' {@} ')')": + doAssert matches[0] == "f" + doAssert matches[1] == "a, b" + else: + doAssert false + + doAssert match("eine übersicht und außerdem", peg"(\letter \white*)+") + # ß is not a lower cased letter?! + doAssert match("eine übersicht und auerdem", peg"(\lower \white*)+") + doAssert match("EINE ÜBERSICHT UND AUSSERDEM", peg"(\upper \white*)+") + doAssert(not match("456678", peg"(\letter)+")) + + doAssert("var1 = key; var2 = key2".replacef( + peg"\skip(\s*) {\ident}'='{\ident}", "$1<-$2$2") == + "var1<-keykey;var2<-key2key2") + + doAssert match("prefix/start", peg"^start$", 7) + + if "foo" =~ peg"{'a'}?.*": + doAssert matches[0].len == 0 + else: doAssert false + + if "foo" =~ peg"{''}.*": + doAssert matches[0] == "" + else: doAssert false + + if "foo" =~ peg"{'foo'}": + doAssert matches[0] == "foo" + else: doAssert false + + let empty_test = peg"^\d*" + let str = "XYZ" + + doAssert(str.find(empty_test) == 0) + doAssert(str.match(empty_test)) + + proc handleMatches(m: int, n: int, c: openArray[string]): string = + result = "" + + if m > 0: + result.add ", " + + result.add case n: + of 2: toLowerAscii(c[0]) & ": '" & c[1] & "'" + of 1: toLowerAscii(c[0]) & ": ''" + else: "" + + doAssert("Var1=key1;var2=Key2; VAR3". + replace(peg"{\ident}('='{\ident})* ';'* \s*", + handleMatches) == "var1: 'key1', var2: 'Key2', var3: ''") + + + doAssert "test1".match(peg"""{@}$""") + doAssert "test2".match(peg"""{(!$ .)*} $""") + pegsTest() + static: + pegsTest() + From a236002e54d68a672c720bd9b6d27ea0ba44edb8 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 27 Apr 2021 02:11:28 -0700 Subject: [PATCH 2021/3768] testament: add `nimoutFull: bool` spec (#17867) * testament: add `nimoutFull: bool` spec * PRTEMP * works * cleanup * add test for #12741 * PRTEMP failing test * remove unrelated changes * changelog --- changelog.md | 3 +++ testament/categories.nim | 3 +++ testament/specs.nim | 3 +++ testament/testament.nim | 18 +++++++++++------- testament/tests/shouldfail/tnimoutfull.nim | 15 +++++++++++++++ tests/config.nims | 8 ++++++-- tests/testament/tshould_not_work.nim | 2 ++ 7 files changed, 43 insertions(+), 9 deletions(-) create mode 100644 testament/tests/shouldfail/tnimoutfull.nim diff --git a/changelog.md b/changelog.md index e4b5c39f1..75d086450 100644 --- a/changelog.md +++ b/changelog.md @@ -399,3 +399,6 @@ - `fusion` is now un-bundled from nim, `./koch fusion` will install it via nimble at a fixed hash. + +- testament: added `nimoutFull: bool` spec to compare full output of compiler + instead of a subset. diff --git a/testament/categories.nim b/testament/categories.nim index 19d9e4507..ffee5eeb3 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -576,6 +576,9 @@ proc isJoinableSpec(spec: TSpec): bool = spec.exitCode == 0 and spec.input.len == 0 and spec.nimout.len == 0 and + spec.nimoutFull == false and + # so that tests can have `nimoutFull: true` with `nimout.len == 0` with + # the meaning that they expect empty output. spec.matrix.len == 0 and spec.outputCheck != ocSubstr and spec.ccodeCheck.len == 0 and diff --git a/testament/specs.nim b/testament/specs.nim index 38e4ed0db..3774778b3 100644 --- a/testament/specs.nim +++ b/testament/specs.nim @@ -88,6 +88,7 @@ type targets*: set[TTarget] matrix*: seq[string] nimout*: string + nimoutFull*: bool # whether nimout is all compiler output or a subset parseErrors*: string # when the spec definition is invalid, this is not empty. unjoinable*: bool unbatchable*: bool @@ -297,6 +298,8 @@ proc parseSpec*(filename: string): TSpec = result.action = actionReject of "nimout": result.nimout = e.value + of "nimoutfull": + result.nimoutFull = parseCfgBool(e.value) of "batchable": result.unbatchable = not parseCfgBool(e.value) of "joinable": diff --git a/testament/testament.nim b/testament/testament.nim index f259c7b96..97f1f01b8 100644 --- a/testament/testament.nim +++ b/testament/testament.nim @@ -371,12 +371,20 @@ proc checkForInlineErrors(r: var TResults, expected, given: TSpec, test: TTest, r.addResult(test, target, "", given.msg, reSuccess) inc(r.passed) +proc nimoutCheck(expected, given: TSpec): bool = + result = true + if expected.nimoutFull: + if expected.nimout != given.nimout: + result = false + elif expected.nimout.len > 0 and not greedyOrderedSubsetLines(expected.nimout, given.nimout): + result = false + proc cmpMsgs(r: var TResults, expected, given: TSpec, test: TTest, target: TTarget) = if expected.inlineErrors.len > 0: checkForInlineErrors(r, expected, given, test, target) elif strip(expected.msg) notin strip(given.msg): r.addResult(test, target, expected.msg, given.msg, reMsgsDiffer) - elif expected.nimout.len > 0 and not greedyOrderedSubsetLines(expected.nimout, given.nimout): + elif not nimoutCheck(expected, given): r.addResult(test, target, expected.nimout, given.nimout, reMsgsDiffer) elif extractFilename(expected.file) != extractFilename(given.file) and "internal error:" notin expected.msg: @@ -424,10 +432,6 @@ proc codegenCheck(test: TTest, target: TTarget, spec: TSpec, expectedMsg: var st given.err = reCodeNotFound echo getCurrentExceptionMsg() -proc nimoutCheck(test: TTest; expectedNimout: string; given: var TSpec) = - if not greedyOrderedSubsetLines(expectedNimout, given.nimout): - given.err = reMsgsDiffer - proc compilerOutputTests(test: TTest, target: TTarget, given: var TSpec, expected: TSpec; r: var TResults) = var expectedmsg: string = "" @@ -436,10 +440,10 @@ proc compilerOutputTests(test: TTest, target: TTarget, given: var TSpec, if expected.needsCodegenCheck: codegenCheck(test, target, expected, expectedmsg, given) givenmsg = given.msg - if expected.nimout.len > 0: + if not nimoutCheck(expected, given): + given.err = reMsgsDiffer expectedmsg = expected.nimout givenmsg = given.nimout.strip - nimoutCheck(test, expectedmsg, given) else: givenmsg = "$ " & given.cmd & '\n' & given.nimout if given.err == reSuccess: inc(r.passed) diff --git a/testament/tests/shouldfail/tnimoutfull.nim b/testament/tests/shouldfail/tnimoutfull.nim new file mode 100644 index 000000000..3349ceedf --- /dev/null +++ b/testament/tests/shouldfail/tnimoutfull.nim @@ -0,0 +1,15 @@ +discard """ + targets: "c" + nimout: ''' +msg1 +msg2 +''' + action: compile + nimoutFull: true +""" + +# should fail because `msg3` is not in nimout and `nimoutFill: true` was given +static: + echo "msg1" + echo "msg2" + echo "msg3" diff --git a/tests/config.nims b/tests/config.nims index e5d6545f4..578b69e76 100644 --- a/tests/config.nims +++ b/tests/config.nims @@ -6,9 +6,13 @@ switch("path", "$lib/../testament/lib") ## prevent common user config settings to interfere with testament expectations ## Indifidual tests can override this if needed to test for these options. switch("colors", "off") -switch("filenames", "legacyRelProj") + switch("excessiveStackTrace", "off") -switch("spellSuggest", "0") + +when (NimMajor, NimMinor, NimPatch) >= (1,5,1): + # to make it easier to test against older nim versions, (best effort only) + switch("filenames", "legacyRelProj") + switch("spellSuggest", "0") # for std/unittest switch("define", "nimUnittestOutputLevel:PRINT_FAILURES") diff --git a/tests/testament/tshould_not_work.nim b/tests/testament/tshould_not_work.nim index a0b4d6a36..2777bfe95 100644 --- a/tests/testament/tshould_not_work.nim +++ b/tests/testament/tshould_not_work.nim @@ -21,6 +21,8 @@ Failure: reCodegenFailure max allowed size: 1 FAIL: tests/shouldfail/tnimout.nim c Failure: reMsgsDiffer +FAIL: tests/shouldfail/tnimoutfull.nim c +Failure: reMsgsDiffer FAIL: tests/shouldfail/toutput.nim c Failure: reOutputsDiffer FAIL: tests/shouldfail/toutputsub.nim c From b03d6c9b2f9d117ed85d410313b7b39e49a16964 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Tue, 27 Apr 2021 13:03:26 +0200 Subject: [PATCH 2022/3768] Fix #17712 (#17873) --- compiler/dfa.nim | 7 ++++--- tests/arc/tarcmisc.nim | 11 +++++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/compiler/dfa.nim b/compiler/dfa.nim index c7a9d4694..501d39f45 100644 --- a/compiler/dfa.nim +++ b/compiler/dfa.nim @@ -696,9 +696,10 @@ proc skipTrivials(c: var Con, n: PNode): PNode = proc genUse(c: var Con; orig: PNode) = let n = c.skipTrivials(orig) - if n.kind == nkSym and n.sym.kind in InterestingSyms: - c.code.add Instr(n: orig, kind: use) - elif n.kind in nkCallKinds: + if n.kind == nkSym: + if n.sym.kind in InterestingSyms: + c.code.add Instr(n: orig, kind: use) + else: gen(c, n) proc genDef(c: var Con; orig: PNode) = diff --git a/tests/arc/tarcmisc.nim b/tests/arc/tarcmisc.nim index 82751f44c..a52538085 100644 --- a/tests/arc/tarcmisc.nim +++ b/tests/arc/tarcmisc.nim @@ -27,6 +27,7 @@ finalizer aaaaa hello ok +true closed destroying variable: 20 destroying variable: 10 @@ -422,3 +423,13 @@ proc test3 = static: test3() # was buggy test3() + +# bug #17712 +proc t17712 = + var ppv = new int + discard @[ppv] + var el: ref int + el = [ppv][0] + echo el != nil + +t17712() From d881a05bf6b75d9abc48b07b2a093cb96fea0b4c Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 28 Apr 2021 01:14:26 +0800 Subject: [PATCH 2023/3768] [std/encodings] fix iconv headers on OpenBSD (#17872) --- lib/pure/encodings.nim | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/pure/encodings.nim b/lib/pure/encodings.nim index bdc41c6ff..893660d87 100644 --- a/lib/pure/encodings.nim +++ b/lib/pure/encodings.nim @@ -282,8 +282,10 @@ else: var errno {.importc, header: "".}: cint - when defined(freebsd) or defined(netbsd): + when defined(bsd): {.pragma: importIconv, cdecl, header: "".} + when defined(openbsd): + {.passL: "-liconv".} else: {.pragma: importIconv, cdecl, dynlib: iconvDll.} From 7637cff9cf677ad7e5952aed7670fe408eded418 Mon Sep 17 00:00:00 2001 From: Michael Krieger Date: Wed, 28 Apr 2021 20:33:08 +0200 Subject: [PATCH 2024/3768] Fix documentation of strutils.stripLineEnd (#17883) It does not return anything. --- lib/pure/strutils.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index bfd53a3a0..5bbd850cc 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -2812,7 +2812,7 @@ func strip*(s: string, leading = true, trailing = true, result = substr(s, first, last) func stripLineEnd*(s: var string) = - ## Returns `s` stripped from one of these suffixes: + ## Strips one of these suffixes from `s` in-place: ## `\r, \n, \r\n, \f, \v` (at most once instance). ## For example, can be useful in conjunction with `osproc.execCmdEx`. ## aka: `chomp`:idx: From 601c050fcb4cc3101265018eb1cc381c6cb9dab9 Mon Sep 17 00:00:00 2001 From: c-blake Date: Thu, 29 Apr 2021 02:55:43 -0400 Subject: [PATCH 2025/3768] Implement https://forum.nim-lang.org/t/7848#50018 (#17874) * Implement https://forum.nim-lang.org/t/7848#50018 with just the same `SuccessX` hint category, build mode on a separate, final line, and no change to how the mode is spelled for -d:release/-d:danger. * Change to add a new BuildMode hint category and keep testament in sync as per comment. * Add "--hint:buildmode:off" to `defaultHintsOff`. * Remove as requested. * As requested for tests clean up. * Address code review. * Address code review. * Mirror db456423116a9b19666f551f4d38aded3964c2e2 --- compiler/lineinfos.nim | 8 +++++--- compiler/main.nim | 8 ++++---- doc/nimc.rst | 1 + drnim/drnim.nim | 8 ++++---- testament/testament.nim | 3 ++- tests/misc/trunner.nim | 2 +- 6 files changed, 17 insertions(+), 13 deletions(-) diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index d8f82aea0..c7057e7db 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -64,7 +64,8 @@ type warnFileChanged = "FileChanged", warnUser = "User", - hintSuccess = "Success", hintSuccessX = "SuccessX", hintCC = "CC", + hintSuccess = "Success", hintSuccessX = "SuccessX", hintBuildMode = "BuildMode", + hintCC = "CC", hintLineTooLong = "LineTooLong", hintXDeclaredButNotUsed = "XDeclaredButNotUsed", hintXCannotRaiseY = "XCannotRaiseY", hintConvToBaseNotNeeded = "ConvToBaseNotNeeded", hintConvFromXtoItselfNotNeeded = "ConvFromXtoItselfNotNeeded", hintExprAlwaysX = "ExprAlwaysX", @@ -142,7 +143,8 @@ const warnUser: "$1", hintSuccess: "operation successful: $#", # keep in sync with `testament.isSuccess` - hintSuccessX: "${loc} lines; ${sec}s; $mem; $build build; proj: $project; out: $output", + hintSuccessX: "$loc lines; ${sec}s; $mem; proj: $project; out: $output", + hintBuildMode: "$1", hintCC: "CC: $1", hintLineTooLong: "line too long", hintXDeclaredButNotUsed: "'$1' is declared but not used", @@ -196,7 +198,7 @@ proc computeNotesVerbosity(): array[0..3, TNoteKinds] = result[1] = result[2] - {warnProveField, warnProveIndex, warnGcUnsafe, hintPath, hintDependency, hintCodeBegin, hintCodeEnd, hintSource, hintGlobalVar, hintGCStats, hintMsgOrigin} - result[0] = result[1] - {hintSuccessX, hintSuccess, hintConf, + result[0] = result[1] - {hintSuccessX, hintBuildMode, hintSuccess, hintConf, hintProcessing, hintPattern, hintExecuting, hintLinking, hintCC} const diff --git a/compiler/main.nim b/compiler/main.nim index 48fbd185c..e7ee021bc 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -384,9 +384,9 @@ proc mainCommand*(graph: ModuleGraph) = when declared(system.getMaxMem): formatSize(getMaxMem()) & " peakmem" else: formatSize(getTotalMem()) & " totmem" let loc = $conf.linesCompiled - let build = if isDefined(conf, "danger"): "Dangerous Release" - elif isDefined(conf, "release"): "Release" - else: "Debug" + let build = if isDefined(conf, "danger"): "Dangerous Release build" + elif isDefined(conf, "release"): "Release build" + else: "***SLOW, DEBUG BUILD***; -d:release makes code run faster." let sec = formatFloat(epochTime() - conf.lastCmdTime, ffDecimal, 3) let project = if conf.filenameOption == foAbs: $conf.projectFull else: $conf.projectName # xxx honor conf.filenameOption more accurately @@ -406,10 +406,10 @@ proc mainCommand*(graph: ModuleGraph) = "loc", loc, "sec", sec, "mem", mem, - "build", build, "project", project, "output", output, ]) + rawMessage(conf, hintBuildMode, build) when PrintRopeCacheStats: echo "rope cache stats: " diff --git a/doc/nimc.rst b/doc/nimc.rst index aad889591..9db95e81b 100644 --- a/doc/nimc.rst +++ b/doc/nimc.rst @@ -102,6 +102,7 @@ Source The source line that triggered a diagnostic message. StackTrace Success, SuccessX Successful compilation of a library or a binary. +BuildMode Kind of build: debug, release, danger User UserRaw XDeclaredButNotUsed Unused symbols in the code. diff --git a/drnim/drnim.nim b/drnim/drnim.nim index a591a8ef3..1eded533d 100644 --- a/drnim/drnim.nim +++ b/drnim/drnim.nim @@ -1210,19 +1210,19 @@ proc mainCommand(graph: ModuleGraph) = when declared(system.getMaxMem): formatSize(getMaxMem()) & " peakmem" else: formatSize(getTotalMem()) & " totmem" let loc = $conf.linesCompiled - let build = if isDefined(conf, "danger"): "Dangerous Release" - elif isDefined(conf, "release"): "Release" - else: "Debug" + let build = if isDefined(conf, "danger"): "Dangerous Release build" + elif isDefined(conf, "release"): "Release build" + else: "***SLOW, DEBUG BUILD***; -d:release makes code run faster." let sec = formatFloat(epochTime() - conf.lastCmdTime, ffDecimal, 3) let project = if conf.filenameOption == foAbs: $conf.projectFull else: $conf.projectName rawMessage(conf, hintSuccessX, [ "loc", loc, "sec", sec, "mem", mem, - "build", build, "project", project, "output", "" ]) + rawMessage(conf, hintBuildMode, build) proc processCmdLine(pass: TCmdLinePass, cmd: string; config: ConfigRef) = var p = parseopt.initOptParser(cmd) diff --git a/testament/testament.nim b/testament/testament.nim index 97f1f01b8..9caa3f6b9 100644 --- a/testament/testament.nim +++ b/testament/testament.nim @@ -107,7 +107,8 @@ proc isSuccess(input: string): bool = # that may appear in user config (eg: `--filenames`). # Passing `XDG_CONFIG_HOME= testament args...` can be used to ignore user config # stored in XDG_CONFIG_HOME, refs https://wiki.archlinux.org/index.php/XDG_Base_Directory - input.startsWith("Hint: ") and input.endsWith("[SuccessX]") + input.startsWith("Hint: ") and + (input.endsWith("[SuccessX]") or input.endsWith("[BuildMode]")) proc getFileDir(filename: string): string = result = filename.splitFile().dir diff --git a/tests/misc/trunner.nim b/tests/misc/trunner.nim index 2a82ca9ee..06c828eaa 100644 --- a/tests/misc/trunner.nim +++ b/tests/misc/trunner.nim @@ -21,7 +21,7 @@ proc isDots(a: string): bool = a.startsWith(".") and a.strip(chars = {'.'}) == "" const - defaultHintsOff = "--hint:successx:off --hint:exec:off --hint:link:off --hint:cc:off --hint:conf:off --hint:processing:off --hint:QuitCalled:off" + defaultHintsOff = "--hint:successx:off --hint:buildmode:off --hint:exec:off --hint:link:off --hint:cc:off --hint:conf:off --hint:processing:off --hint:QuitCalled:off" # useful when you want to turn only some hints on, and some common ones off. # pending https://github.com/timotheecour/Nim/issues/453, simplify to: `--hints:off` nim = getCurrentCompilerExe() From 927ae26fad47289ae84de0cb23f0395ca7a79610 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 29 Apr 2021 00:01:00 -0700 Subject: [PATCH 2026/3768] externalToLink: use quoteShell to avoid issues with spaces in paths for {.link.} pragmas (#17875) --- compiler/extccomp.nim | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 3b415c499..1cea9edeb 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -943,6 +943,7 @@ proc jsonBuildInstructionsFile*(conf: ConfigRef): AbsoluteFile = result = getNimcacheDir(conf) / conf.outFile.changeFileExt("json") proc writeJsonBuildInstructions*(conf: ConfigRef) = + # xxx use std/json instead, will result in simpler, more maintainable code. template lit(x: string) = f.write x template str(x: string) = buf.setLen 0 @@ -964,23 +965,18 @@ proc writeJsonBuildInstructions*(conf: ConfigRef) = proc linkfiles(conf: ConfigRef; f: File; buf, objfiles: var string; clist: CfileList; llist: seq[string]) = var pastStart = false + template impl(path) = + let path2 = quoteShell(path) + objfiles.add(' ') + objfiles.add(path2) + if pastStart: lit ",\L" + str path2 + pastStart = true for it in llist: - let objfile = if noAbsolutePaths(conf): it.extractFilename - else: it - let objstr = addFileExt(objfile, CC[conf.cCompiler].objExt) - objfiles.add(' ') - objfiles.add(objstr) - if pastStart: lit ",\L" - str objstr - pastStart = true - + let objfile = if noAbsolutePaths(conf): it.extractFilename else: it + impl(addFileExt(objfile, CC[conf.cCompiler].objExt)) for it in clist: - let objstr = quoteShell(it.obj) - objfiles.add(' ') - objfiles.add(objstr) - if pastStart: lit ",\L" - str objstr - pastStart = true + impl(it.obj) lit "\L" proc depfiles(conf: ConfigRef; f: File; buf: var string) = From 016a8ccd7a8a018b28e8f424085c178d96860c6c Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 29 Apr 2021 15:51:54 +0800 Subject: [PATCH 2027/3768] [std/encodings]move to tests (#17866) --- lib/pure/encodings.nim | 72 ------------------------------------ tests/stdlib/tencodings.nim | 73 +++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 72 deletions(-) diff --git a/lib/pure/encodings.nim b/lib/pure/encodings.nim index 893660d87..89db0a278 100644 --- a/lib/pure/encodings.nim +++ b/lib/pure/encodings.nim @@ -476,75 +476,3 @@ proc convert*(s: string, destEncoding = "UTF-8", result = convert(c, s) finally: close(c) - -when not defined(testing) and isMainModule: - let - orig = "öäüß" - cp1252 = convert(orig, "CP1252", "UTF-8") - ibm850 = convert(cp1252, "ibm850", "CP1252") - current = getCurrentEncoding() - echo "Original string from source code: ", orig - echo "Forced ibm850 encoding: ", ibm850 - echo "Current encoding: ", current - echo "From ibm850 to current: ", convert(ibm850, current, "ibm850") - -when not defined(testing) and isMainModule and defined(windows): - block should_throw_on_unsupported_conversions: - let original = "some string" - - doAssertRaises(EncodingError): - discard convert(original, "utf-8", "utf-32") - - doAssertRaises(EncodingError): - discard convert(original, "utf-8", "unicodeFFFE") - - doAssertRaises(EncodingError): - discard convert(original, "utf-8", "utf-32BE") - - doAssertRaises(EncodingError): - discard convert(original, "unicodeFFFE", "utf-8") - - doAssertRaises(EncodingError): - discard convert(original, "utf-32", "utf-8") - - doAssertRaises(EncodingError): - discard convert(original, "utf-32BE", "utf-8") - - block should_convert_from_utf16_to_utf8: - let original = "\x42\x04\x35\x04\x41\x04\x42\x04" # utf-16 little endian test string "тест" - let result = convert(original, "utf-8", "utf-16") - doAssert(result == "\xd1\x82\xd0\xb5\xd1\x81\xd1\x82") - - block should_convert_from_utf16_to_win1251: - let original = "\x42\x04\x35\x04\x41\x04\x42\x04" # utf-16 little endian test string "тест" - let result = convert(original, "windows-1251", "utf-16") - doAssert(result == "\xf2\xe5\xf1\xf2") - - block should_convert_from_win1251_to_koi8r: - let original = "\xf2\xe5\xf1\xf2" # win1251 test string "тест" - let result = convert(original, "koi8-r", "windows-1251") - doAssert(result == "\xd4\xc5\xd3\xd4") - - block should_convert_from_koi8r_to_win1251: - let original = "\xd4\xc5\xd3\xd4" # koi8r test string "тест" - let result = convert(original, "windows-1251", "koi8-r") - doAssert(result == "\xf2\xe5\xf1\xf2") - - block should_convert_from_utf8_to_win1251: - let original = "\xd1\x82\xd0\xb5\xd1\x81\xd1\x82" # utf-8 test string "тест" - let result = convert(original, "windows-1251", "utf-8") - doAssert(result == "\xf2\xe5\xf1\xf2") - - block should_convert_from_utf8_to_utf16: - let original = "\xd1\x82\xd0\xb5\xd1\x81\xd1\x82" # utf-8 test string "тест" - let result = convert(original, "utf-16", "utf-8") - doAssert(result == "\x42\x04\x35\x04\x41\x04\x42\x04") - - block should_handle_empty_string_for_any_conversion: - let original = "" - var result = convert(original, "utf-16", "utf-8") - doAssert(result == "") - result = convert(original, "utf-8", "utf-16") - doAssert(result == "") - result = convert(original, "windows-1251", "koi8-r") - doAssert(result == "") diff --git a/tests/stdlib/tencodings.nim b/tests/stdlib/tencodings.nim index 1da52ba59..8ca55dbd9 100644 --- a/tests/stdlib/tencodings.nim +++ b/tests/stdlib/tencodings.nim @@ -23,3 +23,76 @@ block: block: let data = "谁怕?一蓑烟雨任平生" doAssert toGB2312.convert(data) == "\203\173\197\194\163\191\210\187\203\242\209\204\211\234\200\206\198\189\201\250" + + +when defined(windows): + block should_throw_on_unsupported_conversions: + let original = "some string" + + doAssertRaises(EncodingError): + discard convert(original, "utf-8", "utf-32") + + doAssertRaises(EncodingError): + discard convert(original, "utf-8", "unicodeFFFE") + + doAssertRaises(EncodingError): + discard convert(original, "utf-8", "utf-32BE") + + doAssertRaises(EncodingError): + discard convert(original, "unicodeFFFE", "utf-8") + + doAssertRaises(EncodingError): + discard convert(original, "utf-32", "utf-8") + + doAssertRaises(EncodingError): + discard convert(original, "utf-32BE", "utf-8") + + block should_convert_from_utf16_to_utf8: + let original = "\x42\x04\x35\x04\x41\x04\x42\x04" # utf-16 little endian test string "тест" + let result = convert(original, "utf-8", "utf-16") + doAssert(result == "\xd1\x82\xd0\xb5\xd1\x81\xd1\x82") + + block should_convert_from_utf16_to_win1251: + let original = "\x42\x04\x35\x04\x41\x04\x42\x04" # utf-16 little endian test string "тест" + let result = convert(original, "windows-1251", "utf-16") + doAssert(result == "\xf2\xe5\xf1\xf2") + + block should_convert_from_win1251_to_koi8r: + let original = "\xf2\xe5\xf1\xf2" # win1251 test string "тест" + let result = convert(original, "koi8-r", "windows-1251") + doAssert(result == "\xd4\xc5\xd3\xd4") + + block should_convert_from_koi8r_to_win1251: + let original = "\xd4\xc5\xd3\xd4" # koi8r test string "тест" + let result = convert(original, "windows-1251", "koi8-r") + doAssert(result == "\xf2\xe5\xf1\xf2") + + block should_convert_from_utf8_to_win1251: + let original = "\xd1\x82\xd0\xb5\xd1\x81\xd1\x82" # utf-8 test string "тест" + let result = convert(original, "windows-1251", "utf-8") + doAssert(result == "\xf2\xe5\xf1\xf2") + + block should_convert_from_utf8_to_utf16: + let original = "\xd1\x82\xd0\xb5\xd1\x81\xd1\x82" # utf-8 test string "тест" + let result = convert(original, "utf-16", "utf-8") + doAssert(result == "\x42\x04\x35\x04\x41\x04\x42\x04") + + block should_handle_empty_string_for_any_conversion: + let original = "" + var result = convert(original, "utf-16", "utf-8") + doAssert(result == "") + result = convert(original, "utf-8", "utf-16") + doAssert(result == "") + result = convert(original, "windows-1251", "koi8-r") + doAssert(result == "") + + +block: + let + orig = "öäüß" + cp1252 = convert(orig, "CP1252", "UTF-8") + ibm850 = convert(cp1252, "ibm850", "CP1252") + current = getCurrentEncoding() + doAssert orig == "\195\182\195\164\195\188\195\159" + doAssert ibm850 == "\148\132\129\225" + doAssert convert(ibm850, current, "ibm850") == orig From 87229e272ecd4012b750215d35e914580cd8475c Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 29 Apr 2021 02:25:08 -0700 Subject: [PATCH 2028/3768] fix #17853 (ascii message separator broke json nim dump) (#17887) --- changelog.md | 4 ++++ compiler/main.nim | 3 ++- compiler/msgs.nim | 1 + compiler/options.nim | 2 +- tests/misc/trunner.nim | 11 +++++++++++ tests/osproc/treadlines.nim | 7 +++++-- 6 files changed, 24 insertions(+), 4 deletions(-) diff --git a/changelog.md b/changelog.md index 75d086450..21f3cb9b5 100644 --- a/changelog.md +++ b/changelog.md @@ -60,6 +60,10 @@ - Removed `.travis.yml`, `appveyor.yml.disabled`, `.github/workflows/ci.yml.disabled`. +- Nim compiler now adds ASCII unit separator `\31` before a newline for every generated + message (potentially multiline), so tooling can tell when messages start and end. + + ## Standard library additions and changes - Added support for parenthesized expressions in `strformat` diff --git a/compiler/main.nim b/compiler/main.nim index e7ee021bc..db5dd439e 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -349,7 +349,8 @@ proc mainCommand*(graph: ModuleGraph) = (key: "warnings", val: warnings), ] - msgWriteln(conf, $dumpdata, {msgStdout, msgSkipHook}) + msgWriteln(conf, $dumpdata, {msgStdout, msgSkipHook, msgNoUnitSep}) + # `msgNoUnitSep` to avoid generating invalid json, refs bug #17853 else: msgWriteln(conf, "-- list of currently defined symbols --", {msgStdout, msgSkipHook, msgNoUnitSep}) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 93ca8d7cc..3abbf9e69 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -291,6 +291,7 @@ proc `??`* (conf: ConfigRef; info: TLineInfo, filename: string): bool = const UnitSep = "\31" + # this needs care to avoid issues similar to https://github.com/nim-lang/Nim/issues/17853 type MsgFlag* = enum ## flags altering msgWriteln behavior diff --git a/compiler/options.nim b/compiler/options.nim index 209564d0a..4773ba284 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -44,7 +44,7 @@ type # please make sure we have under 32 options optImportHidden TOptions* = set[TOption] - TGlobalOption* = enum # **keep binary compatible** + TGlobalOption* = enum gloptNone, optForceFullMake, optWasNimscript, # redundant with `cmdNimscript`, could be removed optListCmd, optCompileOnly, optNoLinking, diff --git a/tests/misc/trunner.nim b/tests/misc/trunner.nim index 06c828eaa..014373dfb 100644 --- a/tests/misc/trunner.nim +++ b/tests/misc/trunner.nim @@ -71,6 +71,7 @@ ret=[s1:foobar s2:foobar age:25 pi:3.14] else: # don't run twice the same test import std/strutils + import std/json template check2(msg) = doAssert msg in output, output block: # tests with various options `nim doc --project --index --docroot` @@ -317,3 +318,13 @@ compiling: v3 running: v3 running: v2 """, ret + + block: # nim dump + let cmd = fmt"{nim} dump --dump.format:json -d:D20210428T161003 --hints:off ." + let (ret, status) = execCmdEx(cmd) + doAssert status == 0 + let j = ret.parseJson + # sanity checks + doAssert "D20210428T161003" in j["defined_symbols"].to(seq[string]) + doAssert j["version"].to(string) == NimVersion + doAssert j["nimExe"].to(string) == getCurrentCompilerExe() diff --git a/tests/osproc/treadlines.nim b/tests/osproc/treadlines.nim index 436dd7a10..bcde19d7f 100644 --- a/tests/osproc/treadlines.nim +++ b/tests/osproc/treadlines.nim @@ -1,15 +1,18 @@ discard """ - output: '''Error: cannot open 'a.nim'\31 + output: ''' +Error: cannot open 'a.nim'\31 Error: cannot open 'b.nim'\31 ''' targets: "c" """ import osproc +from std/os import getCurrentCompilerExe var ps: seq[Process] # compile & run 2 progs in parallel +const nim = getCurrentCompilerExe() for prog in ["a", "b"]: - ps.add startProcess("nim", "", + ps.add startProcess(nim, "", ["r", "--hint[Conf]=off", "--hint[Processing]=off", prog], options = {poUsePath, poDaemon, poStdErrToStdOut}) From 5edddd68d04648553bc02d52cd53d3a1a14102f3 Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 29 Apr 2021 19:39:36 +0800 Subject: [PATCH 2029/3768] improve cache line size (#17885) * improve cache line size - 64bit system tend to use cache line of 64 bytes - add align https://trishagee.com/2011/07/22/dissecting_the_disruptor_why_its_so_fast_part_two__magic_cache_line_padding Though I'm not sure, what do you think? @timotheecour * Update lib/pure/concurrency/threadpool.nim --- lib/pure/concurrency/threadpool.nim | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/lib/pure/concurrency/threadpool.nim b/lib/pure/concurrency/threadpool.nim index 9beb39522..f8a5f9ba8 100644 --- a/lib/pure/concurrency/threadpool.nim +++ b/lib/pure/concurrency/threadpool.nim @@ -52,17 +52,14 @@ proc signal(cv: var Semaphore) = release(cv.L) signal(cv.c) -const CacheLineSize = 32 # true for most archs +const CacheLineSize = 64 # true for most archs type Barrier {.compilerproc.} = object entered: int cv: Semaphore # Semaphore takes 3 words at least - when sizeof(int) < 8: - cacheAlign: array[CacheLineSize-4*sizeof(int), byte] - left: int - cacheAlign2: array[CacheLineSize-sizeof(int), byte] - interest: bool # whether the master is interested in the "all done" event + left {.align(CacheLineSize).}: int + interest {.align(CacheLineSize).} : bool # whether the master is interested in the "all done" event proc barrierEnter(b: ptr Barrier) {.compilerproc, inline.} = # due to the signaling between threads, it is ensured we are the only From e4db733d80d0b70192ac7eb4094a46269fe7cd5b Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 29 Apr 2021 04:42:56 -0700 Subject: [PATCH 2030/3768] fix #17888: remove undefined behavior for posix.open; fix tempfiles.createTempFile (#17889) * fix #17888: remove undefined behavior for posix.open; fix tempfiles.createTempFile * fix for tests/async/tasyncfile.nim * hide mode for now * add notice regarding stability --- lib/posix/posix.nim | 6 +++++- lib/pure/streams.nim | 1 + lib/std/tempfiles.nim | 13 ++++++++++--- 3 files changed, 16 insertions(+), 4 deletions(-) diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index 035b4e8fb..45fe00d5d 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -183,7 +183,11 @@ proc dlsym*(a1: pointer, a2: cstring): pointer {.importc, header: "", s proc creat*(a1: cstring, a2: Mode): cint {.importc, header: "", sideEffect.} proc fcntl*(a1: cint | SocketHandle, a2: cint): cint {.varargs, importc, header: "", sideEffect.} -proc open*(a1: cstring, a2: cint): cint {.varargs, importc, header: "", sideEffect.} +proc openImpl(a1: cstring, a2: cint): cint {.varargs, importc: "open", header: "", sideEffect.} +proc open*(a1: cstring, a2: cint, mode: Mode | cint = 0.Mode): cint {.inline.} = + # prevents bug #17888 + openImpl(a1, a2, mode) + proc posix_fadvise*(a1: cint, a2, a3: Off, a4: cint): cint {. importc, header: "".} proc posix_fallocate*(a1: cint, a2, a3: Off): cint {. diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim index eb1c9cc14..3cc5ea038 100644 --- a/lib/pure/streams.nim +++ b/lib/pure/streams.nim @@ -1503,6 +1503,7 @@ when false: of fmReadWrite: flags = O_RDWR or int(O_CREAT) of fmReadWriteExisting: flags = O_RDWR of fmAppend: flags = O_WRONLY or int(O_CREAT) or O_APPEND + static: doAssert false # handle bug #17888 var handle = open(filename, flags) if handle < 0: raise newEOS("posix.open() call failed") result = newFileHandleStream(handle) diff --git a/lib/std/tempfiles.nim b/lib/std/tempfiles.nim index 1e1bbd403..2a6fe7d83 100644 --- a/lib/std/tempfiles.nim +++ b/lib/std/tempfiles.nim @@ -8,6 +8,8 @@ # ## This module creates temporary files and directories. +## +## Experimental API, subject to change. import os, random @@ -44,6 +46,8 @@ else: proc safeOpen(filename: string): File = ## Open files exclusively. + # xxx this should be clarified; it doesn't in particular prevent other processes + # from opening the file, at least currently. when defined(windows): let dwShareMode = FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE let dwCreation = CREATE_NEW @@ -64,9 +68,13 @@ proc safeOpen(filename: string): File = discard close_osfandle(fileHandle) raiseOSError(osLastError(), filename) else: + # xxx we need a `proc toMode(a: FilePermission): Mode`, possibly by + # exposing fusion/filepermissions.fromFilePermissions to stdlib; then we need + # to expose a `perm` param so users can customize this (e.g. the temp file may + # need execute permissions), and figure out how to make the API cross platform. + let mode = Mode(S_IRUSR or S_IWUSR) let flags = posix.O_RDWR or posix.O_CREAT or posix.O_EXCL - - let fileHandle = posix.open(filename, flags) + let fileHandle = posix.open(filename, flags, mode) if fileHandle == -1: raiseOSError(osLastError(), filename) @@ -93,7 +101,6 @@ proc createTempFile*(prefix, suffix: string, dir = ""): tuple[fd: File, path: st ## If failing to create a temporary file, `IOError` will be raised. ## ## .. note:: It is the caller's responsibility to remove the file when no longer needed. - ## var dir = dir if dir.len == 0: dir = getTempDir() From a424075b5eb50681b448dcf73f7559549945ad58 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 29 Apr 2021 04:44:53 -0700 Subject: [PATCH 2031/3768] improve nimsuggest/tester, minor improvements to koch.nim (#17879) * improve nimsuggest/tester * koch improvements --- koch.nim | 21 ++++++++++----------- nimdoc/testproject/testproject.nim | 2 +- nimpretty/tester.nim | 2 +- nimsuggest/tester.nim | 22 ++++++++++++---------- 4 files changed, 24 insertions(+), 23 deletions(-) diff --git a/koch.nim b/koch.nim index c78356b58..12f9e1c14 100644 --- a/koch.nim +++ b/koch.nim @@ -154,7 +154,7 @@ proc bundleNimbleExe(latest: bool, args: string) = proc bundleNimsuggest(args: string) = nimCompileFold("Compile nimsuggest", "nimsuggest/nimsuggest.nim", - options = "-d:release -d:danger " & args) + options = "-d:danger " & args) proc buildVccTool(args: string) = let input = "tools/vccexe/vccexe.nim" @@ -558,28 +558,27 @@ proc runCI(cmd: string) = ## run tests execFold("Test nimscript", "nim e tests/test_nimscript.nims") when defined(windows): - # note: will be over-written below - execFold("Compile tester", "nim c -d:nimCoroutines --os:genode -d:posix --compileOnly testament/testament") + execFold("Compile tester", "nim c --usenimcache -d:nimCoroutines --os:genode -d:posix --compileOnly testament/testament") # main bottleneck here # xxx: even though this is the main bottleneck, we could speedup the rest via batching with `--batch`. # BUG: with initOptParser, `--batch:'' all` interprets `all` as the argument of --batch, pending bug #14343 - execFold("Run tester", "nim c -r -d:nimCoroutines --putenv:NIM_TESTAMENT_REMOTE_NETWORKING:1 -d:nimStrictMode testament/testament $# all -d:nimCoroutines" % batchParam) + execFold("Run tester", "nim c -r --putenv:NIM_TESTAMENT_REMOTE_NETWORKING:1 -d:nimStrictMode testament/testament $# all -d:nimCoroutines" % batchParam) - block CT_FFI: + block: # nimHasLibFFI: when defined(posix): # windows can be handled in future PR's execFold("nimble install -y libffi", "nimble install -y libffi") - const nimFFI = "./bin/nim.ctffi" + const nimFFI = "bin/nim.ctffi" # no need to bootstrap with koch boot (would be slower) let backend = if doUseCpp(): "cpp" else: "c" execFold("build with -d:nimHasLibFFI", "nim $1 -d:release -d:nimHasLibFFI -o:$2 compiler/nim.nim" % [backend, nimFFI]) execFold("test with -d:nimHasLibFFI", "$1 $2 -r testament/testament --nim:$1 r tests/misc/trunner.nim -d:nimTrunnerFfi" % [nimFFI, backend]) - execFold("Run nimdoc tests", "nim c -r nimdoc/tester") - execFold("Run rst2html tests", "nim c -r nimdoc/rsttester") - execFold("Run nimpretty tests", "nim c -r nimpretty/tester.nim") + execFold("Run nimdoc tests", "nim r nimdoc/tester") + execFold("Run rst2html tests", "nim r nimdoc/rsttester") + execFold("Run nimpretty tests", "nim r nimpretty/tester.nim") when defined(posix): - execFold("Run nimsuggest tests", "nim c -r nimsuggest/tester") + execFold("Run nimsuggest tests", "nim r nimsuggest/tester") proc testUnixInstall(cmdLineRest: string) = csource("-d:danger" & cmdLineRest) @@ -605,7 +604,7 @@ proc testUnixInstall(cmdLineRest: string) = execCleanPath("./koch tools") # check the tests work: putEnv("NIM_EXE_NOT_IN_PATH", "NOT_IN_PATH") - execCleanPath("./koch tests --nim:./bin/nim cat megatest", destDir / "bin") + execCleanPath("./koch tests --nim:bin/nim cat megatest", destDir / "bin") else: echo "Version check: failure" finally: diff --git a/nimdoc/testproject/testproject.nim b/nimdoc/testproject/testproject.nim index 6b82acb9b..c7a1cd0fc 100644 --- a/nimdoc/testproject/testproject.nim +++ b/nimdoc/testproject/testproject.nim @@ -341,7 +341,7 @@ when true: # issue #14473 template doit(): untyped = ## doit ## return output only - toSeq([1,2]) + toSeq(["D20210427T172228"]) # make it searcheable at least until we figure out a way to avoid echo echo doit() # using doAssert or similar to avoid echo would "hide" the original bug when true: # issue #14846 diff --git a/nimpretty/tester.nim b/nimpretty/tester.nim index d646b25ce..0a60ce693 100644 --- a/nimpretty/tester.nim +++ b/nimpretty/tester.nim @@ -3,7 +3,7 @@ import strutils, os, sequtils const - dir = "nimpretty/tests/" + dir = "nimpretty/tests" outputdir = dir / "outputdir" var diff --git a/nimsuggest/tester.nim b/nimsuggest/tester.nim index 9fcf7eacc..f19cf5538 100644 --- a/nimsuggest/tester.nim +++ b/nimsuggest/tester.nim @@ -2,6 +2,8 @@ # Every test file can have a #[!]# comment that is deleted from the input # before 'nimsuggest' is invoked to ensure this token doesn't make a # crucial difference for Nim's parser. +# When debugging, to run a single test, use for e.g.: +# `nim r nimsuggest/tester.nim nimsuggest/tests/tsug_accquote.nim` import os, osproc, strutils, streams, re, sexp, net @@ -13,16 +15,16 @@ type disabled: bool const - curDir = when defined(windows): "" else: "" DummyEof = "!EOF!" - -template tpath(): untyped = getAppDir() / "tests" + tpath = "nimsuggest/tests" + # we could also use `stdtest/specialpaths` import std/compilesettings proc parseTest(filename: string; epcMode=false): Test = const cursorMarker = "#[!]#" - let nimsug = curDir & addFileExt("nimsuggest", ExeExt) + let nimsug = "bin" / addFileExt("nimsuggest", ExeExt) + doAssert nimsug.fileExists, nimsug const libpath = querySetting(libPath) result.filename = filename result.dest = getTempDir() / extractFilename(filename) @@ -63,7 +65,7 @@ proc parseTest(filename: string; epcMode=false): Test = elif x.startsWith(">"): # since 'markers' here are not complete yet, we do the $substitutions # afterwards - result.script.add((x.substr(1).replaceWord("$path", tpath()), "")) + result.script.add((x.substr(1).replaceWord("$path", tpath), "")) elif x.len > 0: # expected output line: let x = x % ["file", filename, "lib", libpath] @@ -104,7 +106,7 @@ proc parseCmd(c: string): seq[string] = proc edit(tmpfile: string; x: seq[string]) = if x.len != 3 and x.len != 4: quit "!edit takes two or three arguments" - let f = if x.len >= 4: tpath() / x[3] else: tmpfile + let f = if x.len >= 4: tpath / x[3] else: tmpfile try: let content = readFile(f) let newcontent = content.replace(x[1], x[2]) @@ -121,12 +123,12 @@ proc exec(x: seq[string]) = proc copy(x: seq[string]) = if x.len != 3: quit "!copy takes two arguments" - let rel = tpath() + let rel = tpath copyFile(rel / x[1], rel / x[2]) proc del(x: seq[string]) = if x.len != 2: quit "!del takes one argument" - removeFile(tpath() / x[1]) + removeFile(tpath / x[1]) proc runCmd(cmd, dest: string): bool = result = cmd[0] == '!' @@ -317,7 +319,7 @@ proc runTest(filename: string): int = try: inp.writeLine("quit") inp.flush() - except: + except IOError, OSError: # assume it's SIGPIPE, ie, the child already died discard close(p) @@ -334,7 +336,7 @@ proc main() = failures += runTest(xx) failures += runEpcTest(xx) else: - for x in walkFiles(tpath() / "t*.nim"): + for x in walkFiles(tpath / "t*.nim"): echo "Test ", x let xx = expandFilename x when not defined(windows): From 198b5e3a7a629ea58b1e2a71a3e70f35484f289b Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 29 Apr 2021 04:53:58 -0700 Subject: [PATCH 2032/3768] improve tsets.nim, twrong_setconstr.nim and avoid name clashes with compiler sets (#17876) * improve tests/sets/tsets.nim and avoid name clashes with compiler sets * avoid name clashes in twrong_setconstr.nim and merge into tsets --- tests/ccgbugs/twrong_setconstr.nim | 146 ----------------- tests/sets/tsets.nim | 247 +++++++---------------------- 2 files changed, 59 insertions(+), 334 deletions(-) delete mode 100644 tests/ccgbugs/twrong_setconstr.nim diff --git a/tests/ccgbugs/twrong_setconstr.nim b/tests/ccgbugs/twrong_setconstr.nim deleted file mode 100644 index 8be0b82b5..000000000 --- a/tests/ccgbugs/twrong_setconstr.nim +++ /dev/null @@ -1,146 +0,0 @@ -discard """ - output: "" -""" - -# bug #2880 - -type - TMsgKind* = enum - errUnknown, errIllFormedAstX, errInternal, errCannotOpenFile, errGenerated, - errXCompilerDoesNotSupportCpp, errStringLiteralExpected, - errIntLiteralExpected, errInvalidCharacterConstant, - errClosingTripleQuoteExpected, errClosingQuoteExpected, - errTabulatorsAreNotAllowed, errInvalidToken, errLineTooLong, - errInvalidNumber, errInvalidNumberOctalCode, errNumberOutOfRange, - errNnotAllowedInCharacter, errClosingBracketExpected, errMissingFinalQuote, - errIdentifierExpected, errNewlineExpected, errInvalidModuleName, - errOperatorExpected, errTokenExpected, errStringAfterIncludeExpected, - errRecursiveDependencyX, errOnOrOffExpected, errNoneSpeedOrSizeExpected, - errInvalidPragma, errUnknownPragma, errInvalidDirectiveX, - errAtPopWithoutPush, errEmptyAsm, errInvalidIndentation, - errExceptionExpected, errExceptionAlreadyHandled, - errYieldNotAllowedHere, errYieldNotAllowedInTryStmt, - errInvalidNumberOfYieldExpr, errCannotReturnExpr, errAttemptToRedefine, - errStmtInvalidAfterReturn, errStmtExpected, errInvalidLabel, - errInvalidCmdLineOption, errCmdLineArgExpected, errCmdLineNoArgExpected, - errInvalidVarSubstitution, errUnknownVar, errUnknownCcompiler, - errOnOrOffExpectedButXFound, errOnOffOrListExpectedButXFound, - errNoneBoehmRefcExpectedButXFound, - errNoneSpeedOrSizeExpectedButXFound, errGuiConsoleOrLibExpectedButXFound, - errUnknownOS, errUnknownCPU, errGenOutExpectedButXFound, - errArgsNeedRunOption, errInvalidMultipleAsgn, errColonOrEqualsExpected, - errExprExpected, errUndeclaredIdentifier, errUseQualifier, errTypeExpected, - errSystemNeeds, errExecutionOfProgramFailed, errNotOverloadable, - errInvalidArgForX, errStmtHasNoEffect, errXExpectsTypeOrValue, - errXExpectsArrayType, errIteratorCannotBeInstantiated, errExprXAmbiguous, - errConstantDivisionByZero, errOrdinalTypeExpected, - errOrdinalOrFloatTypeExpected, errOverOrUnderflow, - errCannotEvalXBecauseIncompletelyDefined, errChrExpectsRange0_255, - errDynlibRequiresExportc, errUndeclaredFieldX, errNilAccess, - errIndexOutOfBounds, errIndexTypesDoNotMatch, errBracketsInvalidForType, - errValueOutOfSetBounds, errFieldInitTwice, errFieldNotInit, - errExprXCannotBeCalled, errExprHasNoType, errExprXHasNoType, - errCastNotInSafeMode, errExprCannotBeCastedToX, errCommaOrParRiExpected, - errCurlyLeOrParLeExpected, errSectionExpected, errRangeExpected, - errMagicOnlyInSystem, errPowerOfTwoExpected, - errStringMayNotBeEmpty, errCallConvExpected, errProcOnlyOneCallConv, - errSymbolMustBeImported, errExprMustBeBool, errConstExprExpected, - errDuplicateCaseLabel, errRangeIsEmpty, errSelectorMustBeOfCertainTypes, - errSelectorMustBeOrdinal, errOrdXMustNotBeNegative, errLenXinvalid, - errWrongNumberOfVariables, errExprCannotBeRaised, errBreakOnlyInLoop, - errTypeXhasUnknownSize, errConstNeedsConstExpr, errConstNeedsValue, - errResultCannotBeOpenArray, errSizeTooBig, errSetTooBig, - errBaseTypeMustBeOrdinal, errInheritanceOnlyWithNonFinalObjects, - errInheritanceOnlyWithEnums, errIllegalRecursionInTypeX, - errCannotInstantiateX, errExprHasNoAddress, errXStackEscape, - errVarForOutParamNeeded, - errPureTypeMismatch, errTypeMismatch, errButExpected, errButExpectedX, - errAmbiguousCallXYZ, errWrongNumberOfArguments, - errXCannotBePassedToProcVar, - errXCannotBeInParamDecl, errPragmaOnlyInHeaderOfProc, errImplOfXNotAllowed, - errImplOfXexpected, errNoSymbolToBorrowFromFound, errDiscardValueX, - errInvalidDiscard, errIllegalConvFromXtoY, errCannotBindXTwice, - errInvalidOrderInArrayConstructor, - errInvalidOrderInEnumX, errEnumXHasHoles, errExceptExpected, errInvalidTry, - errOptionExpected, errXisNoLabel, errNotAllCasesCovered, - errUnknownSubstitionVar, errComplexStmtRequiresInd, errXisNotCallable, - errNoPragmasAllowedForX, errNoGenericParamsAllowedForX, - errInvalidParamKindX, errDefaultArgumentInvalid, errNamedParamHasToBeIdent, - errNoReturnTypeForX, errConvNeedsOneArg, errInvalidPragmaX, - errXNotAllowedHere, errInvalidControlFlowX, - errXisNoType, errCircumNeedsPointer, errInvalidExpression, - errInvalidExpressionX, errEnumHasNoValueX, errNamedExprExpected, - errNamedExprNotAllowed, errXExpectsOneTypeParam, - errArrayExpectsTwoTypeParams, errInvalidVisibilityX, errInitHereNotAllowed, - errXCannotBeAssignedTo, errIteratorNotAllowed, errXNeedsReturnType, - errNoReturnTypeDeclared, - errInvalidCommandX, errXOnlyAtModuleScope, - errXNeedsParamObjectType, - errTemplateInstantiationTooNested, errInstantiationFrom, - errInvalidIndexValueForTuple, errCommandExpectsFilename, - errMainModuleMustBeSpecified, - errXExpected, - errTIsNotAConcreteType, - errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError, - errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile, - errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitly, - errOnlyACallOpCanBeDelegator, errUsingNoSymbol, - errMacroBodyDependsOnGenericTypes, - errDestructorNotGenericEnough, - errInlineIteratorsAsProcParams, - errXExpectsTwoArguments, - errXExpectsObjectTypes, errXcanNeverBeOfThisSubtype, errTooManyIterations, - errCannotInterpretNodeX, errFieldXNotFound, errInvalidConversionFromTypeX, - errAssertionFailed, errCannotGenerateCodeForX, errXRequiresOneArgument, - errUnhandledExceptionX, errCyclicTree, errXisNoMacroOrTemplate, - errXhasSideEffects, errIteratorExpected, errLetNeedsInit, - errThreadvarCannotInit, errWrongSymbolX, errIllegalCaptureX, - errXCannotBeClosure, errXMustBeCompileTime, - errCannotInferTypeOfTheLiteral, - errCannotInferReturnType, - errGenericLambdaNotAllowed, - errCompilerDoesntSupportTarget, - errUser, - warnCannotOpenFile, - warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, - warnDeprecated, warnConfigDeprecated, - warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel, - warnUnknownSubstitutionX, warnLanguageXNotSupported, - warnFieldXNotSupported, warnCommentXIgnored, - warnNilStatement, warnTypelessParam, - warnDifferentHeaps, warnWriteToForeignHeap, warnUnsafeCode, - warnEachIdentIsTuple - warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2, - warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed, - warnUser, - hintSuccess, hintSuccessX, - hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded, - hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled, - hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath, - hintConditionAlwaysTrue, hintName, hintPattern, - hintExecuting, hintLinking, hintDependency, - hintSource, hintStackTrace, hintGCStats, - hintUser - -const - warnMin = warnCannotOpenFile - hintMax = high(TMsgKind) - -type - TNoteKind = range[warnMin..hintMax] # "notes" are warnings or hints - TNoteKinds = set[TNoteKind] - -const - NotesVerbosityConst: array[0..0, TNoteKinds] = [ - {low(TNoteKind)..high(TNoteKind)} - {hintGCStats}] - fuckyou = NotesVerbosityConst[0] - -var - gNotesFromConst: TNoteKinds = NotesVerbosityConst[0] - gNotesFromConst2: TNoteKinds = fuckyou - -if hintGCStats in gNotesFromConst: - echo "hintGCStats in gNotesFromConst A" - -if hintGCStats in gNotesFromConst2: - echo "hintGCStats in gNotesFromConst B" diff --git a/tests/sets/tsets.nim b/tests/sets/tsets.nim index dbbeed543..9ccf7a0c2 100644 --- a/tests/sets/tsets.nim +++ b/tests/sets/tsets.nim @@ -1,207 +1,57 @@ -discard """ -output: ''' -Ha ein F ist in s! -false -''' -""" -# Test the handling of sets +# Test builtin sets -import - strutils +# xxx these tests are not very good, this should be revisited. + +when defined nimTestsTsetsGenerate: + # to generate enums for this test + var ret: string + for i in 0..<276: + ret.add "k" & $i & ", " + echo ret proc testSets(s: var set[char]) = s = {'A', 'B', 'C', 'E'..'G'} + {'Z'} + s # test sets if the first element is different from 0: -type - TAZ = range['a'..'z'] - TAZset = set[TAZ] +block: + type + TAZ = range['a'..'z'] + TAZset = set[TAZ] + FakeTokType = enum + k0, k1, k2, k3, k4, k5, k6, k7, k8, k9, k10, k11, k12, k13, k14, k15, k16, k17, k18, k19, k20, k21, k22, k23, k24, k25, k26, k27, k28, k29, k30, k31, k32, k33, k34, k35, k36, k37, k38, k39, k40, k41, k42, k43, k44, k45, k46, k47, k48, k49, k50, k51, k52, k53, k54, k55, k56, k57, k58, k59, k60, k61, k62, k63, k64, k65, k66, k67, k68, k69, k70, k71, k72, k73, k74, k75, k76, k77, k78, k79, k80, k81, k82, k83, k84, k85, k86, k87, k88, k89, k90, k91, k92, k93, k94, k95, k96, k97, k98, k99, k100, k101, k102, k103, k104, k105, k106, k107, k108, k109, k110, k111, k112, k113, k114, k115, k116, k117, k118, k119, k120, k121, k122, k123, k124, k125, k126, k127, k128, k129, k130, k131, k132, k133, k134, k135, k136, k137, k138, k139, k140, k141, k142, k143, k144, k145, k146, k147, k148, k149, k150, k151, k152, k153, k154, k155, k156, k157, k158, k159, k160, k161, k162, k163, k164, k165, k166, k167, k168, k169, k170, k171, k172, k173, k174, k175, k176, k177, k178, k179, k180, k181, k182, k183, k184, k185, k186, k187, k188, k189, k190, k191, k192, k193, k194, k195, k196, k197, k198, k199, k200, k201, k202, k203, k204, k205, k206, k207, k208, k209, k210, k211, k212, k213, k214, k215, k216, k217, k218, k219, k220, k221, k222, k223, k224, k225, k226, k227, k228, k229, k230, k231, k232, k233, k234, k235, k236, k237, k238, k239, k240, k241, k242, k243, k244, k245, k246, k247, k248, k249 + FakeTokTypeRange = range[k2..k101] + FakeTokTypes = set[FakeTokTypeRange] - TTokType* = enum - tkInvalid, tkEof, - tkSymbol, - tkAddr, tkAnd, tkAs, tkAsm, tkBlock, tkBreak, tkCase, tkCast, tkConst, - tkContinue, tkConverter, tkDiscard, tkDiv, tkElif, tkElse, tkEnd, tkEnum, - tkExcept, tkException, tkFinally, tkFor, tkFrom, tkGeneric, tkIf, tkImplies, - tkImport, tkIn, tkInclude, tkIs, tkIsnot, tkIterator, tkLambda, tkMacro, - tkMethod, tkMod, tkNil, tkNot, tkNotin, tkObject, tkOf, tkOr, tkOut, tkProc, - tkPtr, tkRaise, tkRecord, tkRef, tkReturn, tkShl, tkShr, tkTemplate, tkTry, - tkType, tkVar, tkWhen, tkWhere, tkWhile, tkWith, tkWithout, tkXor, tkYield, - tkIntLit, tkInt8Lit, tkInt16Lit, tkInt32Lit, tkInt64Lit, tkFloatLit, - tkFloat32Lit, tkFloat64Lit, tkStrLit, tkRStrLit, tkTripleStrLit, tkCharLit, - tkRCharLit, tkParLe, tkParRi, tkBracketLe, tkBracketRi, tkCurlyLe, - tkCurlyRi, tkBracketDotLe, tkBracketDotRi, - tkCurlyDotLe, tkCurlyDotRi, - tkParDotLe, tkParDotRi, - tkComma, tkSemiColon, tkColon, tkEquals, tkDot, tkDotDot, tkHat, tkOpr, - tkComment, tkAccent, tkInd, tkSad, tkDed, - tkSpaces, tkInfixOpr, tkPrefixOpr, tkPostfixOpr - TTokTypeRange = range[tkSymbol..tkDed] - TTokTypes* = set[TTokTypeRange] + const toktypes: FakeTokTypes = {FakeTokTypeRange(k2)..pred(k64), k72..k74} -const - toktypes: TTokTypes = {TTokTypeRange(tkSymbol)..pred(tkIntLit), - tkStrLit..tkTripleStrLit} - -var - s: set[char] - a: TAZset -s = {'0'..'9'} -testSets(s) -if 'F' in s: write(stdout, "Ha ein F ist in s!\n") -else: write(stdout, "BUG: F ist nicht in s!\n") -a = {} #{'a'..'z'} -for x in low(TAZ) .. high(TAZ): - incl(a, x) - if x in a: discard - else: write(stdout, "BUG: something not in a!\n") - -for x in low(TTokTypeRange) .. high(TTokTypeRange): - if x in tokTypes: - discard - #writeLine(stdout, "the token '$1' is in the set" % repr(x)) - -#OUT Ha ein F ist in s! + var + s: set[char] + a: TAZset + s = {'0'..'9'} + testSets(s) + doAssert 'F' in s + a = {} #{'a'..'z'} + for x in low(TAZ) .. high(TAZ): + incl(a, x) + doAssert x in a + for x in low(FakeTokTypeRange) .. high(FakeTokTypeRange): + if x in tokTypes: + discard type - TMsgKind* = enum - errUnknown, errIllFormedAstX, errInternal, errCannotOpenFile, errGenerated, - errXCompilerDoesNotSupportCpp, errStringLiteralExpected, - errIntLiteralExpected, errInvalidCharacterConstant, - errClosingTripleQuoteExpected, errClosingQuoteExpected, - errTabulatorsAreNotAllowed, errInvalidToken, errLineTooLong, - errInvalidNumber, errNumberOutOfRange, errNnotAllowedInCharacter, - errClosingBracketExpected, errMissingFinalQuote, errIdentifierExpected, - errNewlineExpected, - errInvalidModuleName, - errOperatorExpected, errTokenExpected, errStringAfterIncludeExpected, - errRecursiveDependencyX, errOnOrOffExpected, errNoneSpeedOrSizeExpected, - errInvalidPragma, errUnknownPragma, errInvalidDirectiveX, - errAtPopWithoutPush, errEmptyAsm, errInvalidIndentation, - errExceptionExpected, errExceptionAlreadyHandled, - errYieldNotAllowedHere, errYieldNotAllowedInTryStmt, - errInvalidNumberOfYieldExpr, errCannotReturnExpr, errAttemptToRedefine, - errStmtInvalidAfterReturn, errStmtExpected, errInvalidLabel, - errInvalidCmdLineOption, errCmdLineArgExpected, errCmdLineNoArgExpected, - errInvalidVarSubstitution, errUnknownVar, errUnknownCcompiler, - errOnOrOffExpectedButXFound, errNoneBoehmRefcExpectedButXFound, - errNoneSpeedOrSizeExpectedButXFound, errGuiConsoleOrLibExpectedButXFound, - errUnknownOS, errUnknownCPU, errGenOutExpectedButXFound, - errArgsNeedRunOption, errInvalidMultipleAsgn, errColonOrEqualsExpected, - errExprExpected, errUndeclaredIdentifier, errUseQualifier, errTypeExpected, - errSystemNeeds, errExecutionOfProgramFailed, errNotOverloadable, - errInvalidArgForX, errStmtHasNoEffect, errXExpectsTypeOrValue, - errXExpectsArrayType, errIteratorCannotBeInstantiated, errExprXAmbiguous, - errConstantDivisionByZero, errOrdinalTypeExpected, - errOrdinalOrFloatTypeExpected, errOverOrUnderflow, - errCannotEvalXBecauseIncompletelyDefined, errChrExpectsRange0_255, - errDynlibRequiresExportc, errUndeclaredFieldX, errNilAccess, - errIndexOutOfBounds, errIndexTypesDoNotMatch, errBracketsInvalidForType, - errValueOutOfSetBounds, errFieldInitTwice, errFieldNotInit, - errExprXCannotBeCalled, errExprHasNoType, errExprXHasNoType, - errCastNotInSafeMode, errExprCannotBeCastedToX, errCommaOrParRiExpected, - errCurlyLeOrParLeExpected, errSectionExpected, errRangeExpected, - errMagicOnlyInSystem, errPowerOfTwoExpected, - errStringMayNotBeEmpty, errCallConvExpected, errProcOnlyOneCallConv, - errSymbolMustBeImported, errExprMustBeBool, errConstExprExpected, - errDuplicateCaseLabel, errRangeIsEmpty, errSelectorMustBeOfCertainTypes, - errSelectorMustBeOrdinal, errOrdXMustNotBeNegative, errLenXinvalid, - errWrongNumberOfVariables, errExprCannotBeRaised, errBreakOnlyInLoop, - errTypeXhasUnknownSize, errConstNeedsConstExpr, errConstNeedsValue, - errResultCannotBeOpenArray, errSizeTooBig, errSetTooBig, - errBaseTypeMustBeOrdinal, errInheritanceOnlyWithNonFinalObjects, - errInheritanceOnlyWithEnums, errIllegalRecursionInTypeX, - errCannotInstantiateX, errExprHasNoAddress, errXStackEscape, - errVarForOutParamNeeded, - errPureTypeMismatch, errTypeMismatch, errButExpected, errButExpectedX, - errAmbiguousCallXYZ, errWrongNumberOfArguments, - errXCannotBePassedToProcVar, - errXCannotBeInParamDecl, errPragmaOnlyInHeaderOfProc, errImplOfXNotAllowed, - errImplOfXexpected, errNoSymbolToBorrowFromFound, errDiscardValueX, - errInvalidDiscard, errIllegalConvFromXtoY, errCannotBindXTwice, - errInvalidOrderInArrayConstructor, - errInvalidOrderInEnumX, errEnumXHasHoles, errExceptExpected, errInvalidTry, - errOptionExpected, errXisNoLabel, errNotAllCasesCovered, - errUnknownSubstitionVar, errComplexStmtRequiresInd, errXisNotCallable, - errNoPragmasAllowedForX, errNoGenericParamsAllowedForX, - errInvalidParamKindX, errDefaultArgumentInvalid, errNamedParamHasToBeIdent, - errNoReturnTypeForX, errConvNeedsOneArg, errInvalidPragmaX, - errXNotAllowedHere, errInvalidControlFlowX, - errXisNoType, errCircumNeedsPointer, errInvalidExpression, - errInvalidExpressionX, errEnumHasNoValueX, errNamedExprExpected, - errNamedExprNotAllowed, errXExpectsOneTypeParam, - errArrayExpectsTwoTypeParams, errInvalidVisibilityX, errInitHereNotAllowed, - errXCannotBeAssignedTo, errIteratorNotAllowed, errXNeedsReturnType, - errNoReturnTypeDeclared, - errInvalidCommandX, errXOnlyAtModuleScope, - errXNeedsParamObjectType, - errTemplateInstantiationTooNested, errInstantiationFrom, - errInvalidIndexValueForTuple, errCommandExpectsFilename, - errMainModuleMustBeSpecified, - errXExpected, - errTIsNotAConcreteType, - errInvalidSectionStart, errGridTableNotImplemented, errGeneralParseError, - errNewSectionExpected, errWhitespaceExpected, errXisNoValidIndexFile, - errCannotRenderX, errVarVarTypeNotAllowed, errInstantiateXExplicitly, - errOnlyACallOpCanBeDelegator, errUsingNoSymbol, - errMacroBodyDependsOnGenericTypes, - errDestructorNotGenericEnough, - errInlineIteratorsAsProcParams, - errXExpectsTwoArguments, - errXExpectsObjectTypes, errXcanNeverBeOfThisSubtype, errTooManyIterations, - errCannotInterpretNodeX, errFieldXNotFound, errInvalidConversionFromTypeX, - errAssertionFailed, errCannotGenerateCodeForX, errXRequiresOneArgument, - errUnhandledExceptionX, errCyclicTree, errXisNoMacroOrTemplate, - errXhasSideEffects, errIteratorExpected, errLetNeedsInit, - errThreadvarCannotInit, errWrongSymbolX, errIllegalCaptureX, - errXCannotBeClosure, errXMustBeCompileTime, - errCannotInferTypeOfTheLiteral, - errCannotInferReturnType, - errGenericLambdaNotAllowed, - errCompilerDoesntSupportTarget, - errUser, - warnCannotOpenFile, - warnOctalEscape, warnXIsNeverRead, warnXmightNotBeenInit, - warnDeprecated, warnConfigDeprecated, - warnSmallLshouldNotBeUsed, warnUnknownMagic, warnRedefinitionOfLabel, - warnUnknownSubstitutionX, warnLanguageXNotSupported, - warnFieldXNotSupported, warnCommentXIgnored, - warnNilStatement, warnTypelessParam, - warnDifferentHeaps, warnWriteToForeignHeap, warnUnsafeCode, - warnEachIdentIsTuple, - warnProveInit, warnProveField, warnProveIndex, warnGcUnsafe, warnGcUnsafe2, - warnUninit, warnGcMem, warnDestructor, warnLockLevel, warnResultShadowed, - warnUser, - hintSuccess, hintSuccessX, - hintLineTooLong, hintXDeclaredButNotUsed, hintConvToBaseNotNeeded, - hintConvFromXtoItselfNotNeeded, hintExprAlwaysX, hintQuitCalled, - hintProcessing, hintCodeBegin, hintCodeEnd, hintConf, hintPath, - hintConditionAlwaysTrue, hintName, hintPattern, - hintUser + FakeMsgKind* = enum + k0, k1, k2, k3, k4, k5, k6, k7, k8, k9, k10, k11, k12, k13, k14, k15, k16, k17, k18, k19, k20, k21, k22, k23, k24, k25, k26, k27, k28, k29, k30, k31, k32, k33, k34, k35, k36, k37, k38, k39, k40, k41, k42, k43, k44, k45, k46, k47, k48, k49, k50, k51, k52, k53, k54, k55, k56, k57, k58, k59, k60, k61, k62, k63, k64, k65, k66, k67, k68, k69, k70, k71, k72, k73, k74, k75, k76, k77, k78, k79, k80, k81, k82, k83, k84, k85, k86, k87, k88, k89, k90, k91, k92, k93, k94, k95, k96, k97, k98, k99, k100, k101, k102, k103, k104, k105, k106, k107, k108, k109, k110, k111, k112, k113, k114, k115, k116, k117, k118, k119, k120, k121, k122, k123, k124, k125, k126, k127, k128, k129, k130, k131, k132, k133, k134, k135, k136, k137, k138, k139, k140, k141, k142, k143, k144, k145, k146, k147, k148, k149, k150, k151, k152, k153, k154, k155, k156, k157, k158, k159, k160, k161, k162, k163, k164, k165, k166, k167, k168, k169, k170, k171, k172, k173, k174, k175, k176, k177, k178, k179, k180, k181, k182, k183, k184, k185, k186, k187, k188, k189, k190, k191, k192, k193, k194, k195, k196, k197, k198, k199, k200, k201, k202, k203, k204, k205, k206, k207, k208, k209, k210, k211, k212, k213, k214, k215, k216, k217, k218, k219, k220, k221, k222, k223, k224, k225, k226, k227, k228, k229, k230, k231, k232, k233, k234, k235, k236, k237, k238, k239, k240, k241, k242, k243, k244, k245, k246, k247, k248, k249, k250, k251, k252, k253, k254, k255, k256, k257, k258, k259, k260, k261, k262, k263, k264, k265, k266, k267, k268, k269, k270, k271, k272, k273, k274, k275, -const - fatalMin* = errUnknown - fatalMax* = errInternal - errMin* = errUnknown - errMax* = errUser - warnMin* = warnCannotOpenFile - warnMax* = pred(hintSuccess) - hintMin* = hintSuccess - hintMax* = high(TMsgKind) +doAssert pred(k260) == k259 type - TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints - TNoteKinds* = set[TNoteKind] + FakeMsgKind2 = range[k230..high(FakeMsgKind)] + FakeMsgKind3 = set[FakeMsgKind2] -var - gNotes*: TNoteKinds = {low(TNoteKind)..high(TNoteKind)} - - {warnUninit, warnProveField, warnProveIndex, warnGcUnsafe} +var gNotes: FakeMsgKind3 = {low(FakeMsgKind2)..high(FakeMsgKind2)} - {k233, k235} - -#import compiler.msgs - -echo warnUninit in gNotes +doAssert k233 notin gNotes # 7555 doAssert {-1.int8, -2, -2}.card == 2 @@ -218,3 +68,24 @@ type Foo = enum let x = { Foo1, Foo2 } # bug #8425 + +block: + # bug #2880 + type + FakeMsgKind = enum + k0, k1, k2, k3, k4, k5, k6, k7, k8, k9, k10, k11, k12, k13, k14, k15, k16, k17, k18, k19, k20, k21, k22, k23, k24, k25, k26, k27, k28, k29, k30, k31, k32, k33, k34, k35, k36, k37, k38, k39, k40, k41, k42, k43, k44, k45, k46, k47, k48, k49, k50, k51, k52, k53, k54, k55, k56, k57, k58, k59, k60, k61, k62, k63, k64, k65, k66, k67, k68, k69, k70, k71, k72, k73, k74, k75, k76, k77, k78, k79, k80, k81, k82, k83, k84, k85, k86, k87, k88, k89, k90, k91, k92, k93, k94, k95, k96, k97, k98, k99, k100 + + type + FakeMsgKind2 = range[k50..high(FakeMsgKind)] + FakeMsgKind2s = set[FakeMsgKind2] + + const + a1: array[0..0, FakeMsgKind2s] = [{low(FakeMsgKind2)..high(FakeMsgKind2)} - {k99}] + a2 = a1[0] + + var + s1: FakeMsgKind2s = a1[0] + s2: FakeMsgKind2s = a2 + + doAssert k99 notin s1 + doAssert k99 notin s2 From 5439cfc317cd678438bd6c154b84c2bf625504ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=B6hlich=20A?= Date: Thu, 29 Apr 2021 14:11:05 +0200 Subject: [PATCH 2033/3768] Fix `insert` calling wrong function (#17856) The `insert` method is calling `tryInsertID`, which ignores the `pkName` parameter. Calling `tryInsert` instead should be correct. --- lib/impure/db_postgres.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/impure/db_postgres.nim b/lib/impure/db_postgres.nim index 034a94852..cb9e5414a 100644 --- a/lib/impure/db_postgres.nim +++ b/lib/impure/db_postgres.nim @@ -524,7 +524,7 @@ proc insert*(db: DbConn, query: SqlQuery, pkName: string, {.tags: [WriteDbEffect], since: (1, 3).} = ## executes the query (typically "INSERT") and returns the ## generated ID - result = tryInsertID(db, query, args) + result = tryInsert(db, query, pkName, args) if result < 0: dbError(db) proc execAffectedRows*(db: DbConn, query: SqlQuery, From e61381a293ab4986427c5ce8968c6c031c83f34f Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Thu, 29 Apr 2021 18:16:14 +0300 Subject: [PATCH 2034/3768] follow-up #17692: more inline syntax highlighting (#17837) --- doc/apis.rst | 5 +- doc/backends.rst | 131 +++++++++++++------------ doc/manual.rst | 47 ++++----- doc/nimc.rst | 240 +++++++++++++++++++++++++++------------------- doc/rstcommon.rst | 45 +++++++++ 5 files changed, 287 insertions(+), 181 deletions(-) diff --git a/doc/apis.rst b/doc/apis.rst index d01e75d78..e8313749d 100644 --- a/doc/apis.rst +++ b/doc/apis.rst @@ -1,9 +1,10 @@ -.. default-role:: code - ================= API naming design ================= +.. default-role:: code +.. include:: rstcommon.rst + The API is designed to be **easy to use** and consistent. Ease of use is measured by the number of calls to achieve a concrete high-level action. diff --git a/doc/backends.rst b/doc/backends.rst index 896b0f834..377e899b0 100644 --- a/doc/backends.rst +++ b/doc/backends.rst @@ -1,5 +1,3 @@ -.. default-role:: code - ================================ Nim Backend Integration ================================ @@ -7,6 +5,10 @@ :Author: Puppet Master :Version: |nimversion| +.. default-role:: code +.. include:: rstcommon.rst +.. no syntax highlighting here by default: + .. contents:: "Heresy grows from idleness." -- Unknown. @@ -15,8 +17,9 @@ Introduction ============ The `Nim Compiler User Guide `_ documents the typical -compiler invocation, using the `compile` or `c` command to transform a -`.nim` file into one or more `.c` files which are then compiled with the +compiler invocation, using the `compile`:option: +or `c`:option: command to transform a +``.nim`` file into one or more ``.c`` files which are then compiled with the platform's C compiler into a static binary. However, there are other commands to compile to C++, Objective-C, or JavaScript. This document tries to concentrate in a single place all the backend and interfacing options. @@ -25,7 +28,7 @@ The Nim compiler supports mainly two backend families: the C, C++ and Objective-C targets and the JavaScript target. `The C like targets <#backends-the-c-like-targets>`_ creates source files that can be compiled into a library or a final executable. `The JavaScript target -<#backends-the-javascript-target>`_ can generate a `.js` file which you +<#backends-the-javascript-target>`_ can generate a ``.js`` file which you reference from an HTML file or create a `standalone Node.js program `_. @@ -42,20 +45,22 @@ The C like targets The commands to compile to either C, C++ or Objective-C are: - //compileToC, cc compile project with C code generator - //compileToCpp, cpp compile project to C++ code - //compileToOC, objc compile project to Objective C code +//compileToC, cc compile project with C code generator +//compileToCpp, cpp compile project to C++ code +//compileToOC, objc compile project to Objective C code The most significant difference between these commands is that if you look -into the `nimcache` directory you will find `.c`, `.cpp` or `.m` +into the ``nimcache`` directory you will find ``.c``, ``.cpp`` or ``.m`` files, other than that all of them will produce a native binary for your project. This allows you to take the generated code and place it directly into a project using any of these languages. Here are some typical command- -line invocations:: +line invocations: - $ nim c hallo.nim - $ nim cpp hallo.nim - $ nim objc hallo.nim +.. code:: cmd + + nim c hallo.nim + nim cpp hallo.nim + nim objc hallo.nim The compiler commands select the target backend, but if needed you can `specify additional switches for cross-compilation @@ -66,11 +71,11 @@ or compiler/linker commands. The JavaScript target --------------------- -Nim can also generate `JavaScript`:idx: code through the `js` command. +Nim can also generate `JavaScript`:idx: code through the `js`:option: command. Nim targets JavaScript 1.5 which is supported by any widely used browser. Since JavaScript does not have a portable means to include another module, -Nim just generates a long `.js` file. +Nim just generates a long ``.js`` file. Features or modules that the JavaScript platform does not support are not available. This includes: @@ -88,10 +93,12 @@ To compensate, the standard library has modules `catered to the JS backend and more support will come in the future (for instance, Node.js bindings to get OS info). -To compile a Nim module into a `.js` file use the `js` command; the -default is a `.js` file that is supposed to be referenced in an `.html` +To compile a Nim module into a ``.js`` file use the `js`:option: command; the +default is a ``.js`` file that is supposed to be referenced in an ``.html`` file. However, you can also run the code with `nodejs`:idx: -(``_):: +(``_): + +.. code:: cmd nim js -d:nodejs -r examples/hallo.nim @@ -150,7 +157,7 @@ interface. C invocation example ~~~~~~~~~~~~~~~~~~~~ -Create a `logic.c` file with the following content: +Create a ``logic.c`` file with the following content: .. code-block:: c int addTwoIntegers(int a, int b) @@ -158,7 +165,7 @@ Create a `logic.c` file with the following content: return a + b; } -Create a `calculator.nim` file with the following content: +Create a ``calculator.nim`` file with the following content: .. code-block:: nim @@ -168,26 +175,28 @@ Create a `calculator.nim` file with the following content: when isMainModule: echo addTwoIntegers(3, 7) -With these two files in place, you can run `nim c -r calculator.nim` and -the Nim compiler will compile the `logic.c` file in addition to -`calculator.nim` and link both into an executable, which outputs `10` when +With these two files in place, you can run `nim c -r calculator.nim`:cmd: and +the Nim compiler will compile the ``logic.c`` file in addition to +``calculator.nim`` and link both into an executable, which outputs `10` when run. Another way to link the C file statically and get the same effect would -be to remove the line with the `compile` pragma and run the following typical -Unix commands:: +be to remove the line with the `compile` pragma and run the following +typical Unix commands: - $ gcc -c logic.c - $ ar rvs mylib.a logic.o - $ nim c --passL:mylib.a -r calculator.nim +.. code:: cmd -Just like in this example we pass the path to the `mylib.a` library (and we -could as well pass `logic.o`) we could be passing switches to link any other + gcc -c logic.c + ar rvs mylib.a logic.o + nim c --passL:mylib.a -r calculator.nim + +Just like in this example we pass the path to the ``mylib.a`` library (and we +could as well pass ``logic.o``) we could be passing switches to link any other static C library. JavaScript invocation example ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Create a `host.html` file with the following content: +Create a ``host.html`` file with the following content: .. code-block:: @@ -201,7 +210,7 @@ Create a `host.html` file with the following content: -Create a `calculator.nim` file with the following content (or reuse the one +Create a ``calculator.nim`` file with the following content (or reuse the one from the previous section): .. code-block:: nim @@ -212,7 +221,7 @@ from the previous section): echo addTwoIntegers(3, 7) Compile the Nim code to JavaScript with `nim js -o:calculator.js -calculator.nim` and open `host.html` in a browser. If the browser supports +calculator.nim`:cmd: and open ``host.html`` in a browser. If the browser supports javascript, you should see the value `10` in the browser's console. Use the `dom module `_ for specific DOM querying and modification procs or take a look at `karax `_ for how to @@ -237,7 +246,7 @@ Also, C code requires you to specify a forward declaration for functions or the compiler will assume certain types for the return value and parameters which will likely make your program crash at runtime. -The Nim compiler can generate a C interface header through the `--header` +The Nim compiler can generate a C interface header through the `--header`:option: command-line switch. The generated header will contain all the exported symbols and the `NimMain` proc which you need to call before any other Nim code. @@ -246,7 +255,7 @@ Nim code. Nim invocation example from C ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Create a `fib.nim` file with the following content: +Create a ``fib.nim`` file with the following content: .. code-block:: nim @@ -256,7 +265,7 @@ Create a `fib.nim` file with the following content: else: result = fib(a - 1) + fib(a - 2) -Create a `maths.c` file with the following content: +Create a ``maths.c`` file with the following content: .. code-block:: c @@ -273,36 +282,40 @@ Create a `maths.c` file with the following content: Now you can run the following Unix like commands to first generate C sources from the Nim code, then link them into a static binary along your main C -program:: +program: - $ nim c --noMain --noLinking --header:fib.h fib.nim - $ gcc -o m -I$HOME/.cache/nim/fib_d -Ipath/to/nim/lib $HOME/.cache/nim/fib_d/*.c maths.c +.. code:: cmd + + nim c --noMain --noLinking --header:fib.h fib.nim + gcc -o m -I$HOME/.cache/nim/fib_d -Ipath/to/nim/lib $HOME/.cache/nim/fib_d/*.c maths.c The first command runs the Nim compiler with three special options to avoid -generating a `main()` function in the generated files, avoid linking the +generating a `main()`:c: function in the generated files, avoid linking the object files into a final binary, and explicitly generate a header file for C -integration. All the generated files are placed into the `nimcache` -directory. That's why the next command compiles the `maths.c` source plus -all the `.c` files from `nimcache`. In addition to this path, you also -have to tell the C compiler where to find Nim's `nimbase.h` header file. +integration. All the generated files are placed into the ``nimcache`` +directory. That's why the next command compiles the ``maths.c`` source plus +all the ``.c`` files from ``nimcache``. In addition to this path, you also +have to tell the C compiler where to find Nim's ``nimbase.h`` header file. -Instead of depending on the generation of the individual `.c` files you can -also ask the Nim compiler to generate a statically linked library:: +Instead of depending on the generation of the individual ``.c`` files you can +also ask the Nim compiler to generate a statically linked library: - $ nim c --app:staticLib --noMain --header fib.nim - $ gcc -o m -Inimcache -Ipath/to/nim/lib libfib.nim.a maths.c +.. code:: cmd + + nim c --app:staticLib --noMain --header fib.nim + gcc -o m -Inimcache -Ipath/to/nim/lib libfib.nim.a maths.c The Nim compiler will handle linking the source files generated in the -`nimcache` directory into the `libfib.nim.a` static library, which you can +``nimcache`` directory into the ``libfib.nim.a`` static library, which you can then link into your C program. Note that these commands are generic and will vary for each system. For instance, on Linux systems you will likely need to -use `-ldl` too to link in required dlopen functionality. +use `-ldl`:option: too to link in required dlopen functionality. Nim invocation example from JavaScript ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Create a `mhost.html` file with the following content: +Create a ``mhost.html`` file with the following content: .. code-block:: @@ -313,7 +326,7 @@ Create a `mhost.html` file with the following content: -Create a `fib.nim` file with the following content (or reuse the one +Create a ``fib.nim`` file with the following content (or reuse the one from the previous section): .. code-block:: nim @@ -324,9 +337,9 @@ from the previous section): else: result = fib(a - 1) + fib(a - 2) -Compile the Nim code to JavaScript with `nim js -o:fib.js fib.nim` and -open `mhost.html` in a browser. If the browser supports javascript, you -should see an alert box displaying the text `Fib for 9 is 34`. As mentioned +Compile the Nim code to JavaScript with `nim js -o:fib.js fib.nim`:cmd: and +open ``mhost.html`` in a browser. If the browser supports javascript, you +should see an alert box displaying the text ``Fib for 9 is 34``. As mentioned earlier, JavaScript doesn't require an initialization call to `NimMain` or a similar function and you can call the exported Nim proc directly. @@ -337,7 +350,7 @@ Nimcache naming logic The `nimcache`:idx: directory is generated during compilation and will hold either temporary or final files depending on your backend target. The default name for the directory depends on the used backend and on your OS but you can -use the `--nimcache` `compiler switch +use the `--nimcache`:option: `compiler switch `_ to change it. @@ -362,8 +375,8 @@ painless. Most C functions accepting a Nim string converted to a `cstring` will likely not need to keep this string around and by the time they return the string won't be needed anymore. However, for the rare cases where a Nim string has to be preserved and made available to the C backend -as a `cstring`, you will need to manually prevent the string data from being -freed with `GC_ref `_ and `GC_unref +as a `cstring`, you will need to manually prevent the string data +from being freed with `GC_ref `_ and `GC_unref `_. A similar thing happens with C code invoking Nim code which returns a @@ -399,7 +412,7 @@ Again, if you are wrapping a library which *mallocs* and *frees* data structures, you need to expose the appropriate *free* function to Nim so you can clean it up. And of course, once cleaned you should avoid accessing it from Nim (or C for that matter). Typically C data structures have their own -`malloc_structure` and `free_structure` specific functions, so wrapping +`malloc_structure`:c: and `free_structure`:c: specific functions, so wrapping these for the Nim side should be enough. diff --git a/doc/manual.rst b/doc/manual.rst index e98a0cc09..50e9b5be5 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -127,9 +127,9 @@ compiler may instead choose to allow the program to die with a fatal error. echo "invalid index" The current implementation allows to switch between these different behaviors -via ``--panics:on|off``. When panics are turned on, the program dies with a +via `--panics:on|off`:option:. When panics are turned on, the program dies with a panic, if they are turned off the runtime errors are turned into -exceptions. The benefit of ``--panics:on`` is that it produces smaller binary +exceptions. The benefit of `--panics:on`:option: is that it produces smaller binary code and the compiler has more freedom to optimize the code. An `unchecked runtime error`:idx: is an error that is not guaranteed to be @@ -678,7 +678,7 @@ defined here.) These keywords are also operators: `and or not xor shl shr div mod in notin is isnot of as from`. -`.`:tok: `=`:tok:, `:`:tok:, `::`:tok: are not available as general operators; they +`.`:tok:, `=`:tok:, `:`:tok:, `::`:tok: are not available as general operators; they are used for other notational purposes. `*:` is as a special case treated as the two tokens `*`:tok: and `:`:tok: @@ -697,7 +697,7 @@ The following strings denote other tokens:: The `slice`:idx: operator `..`:tok: takes precedence over other tokens that -contain a dot: `{..}`:tok: are the three tokens `{`:tok:, `..`:tok:, `}`:tok: +contain a dot: `{..}` are the three tokens `{`:tok:, `..`:tok:, `}`:tok: and not the two tokens `{.`:tok:, `.}`:tok:. @@ -1460,7 +1460,7 @@ The notation `x[i]` can be used to access the i-th element of `x`. Arrays are always bounds checked (statically or at runtime). These checks can be disabled via pragmas or invoking the compiler with the -``--boundChecks:off`` command-line switch. +`--boundChecks:off`:option: command-line switch. An array constructor can have explicit indexes for readability: @@ -4093,7 +4093,7 @@ Multi-methods -------------- **Note:** Starting from Nim 0.20, to use multi-methods one must explicitly pass -``--multimethods:on`` when compiling. +`--multimethods:on`:option: when compiling. In a multi-method, all parameters that have an object type are used for the dispatching: @@ -4805,7 +4805,7 @@ And so is: The reason for this is that `DivByZeroDefect` inherits from `Defect` and -with ``--panics:on`` Defects become unrecoverable errors. +with `--panics:on`:option: Defects become unrecoverable errors. (Since version 1.4 of the language.) @@ -5604,7 +5604,7 @@ However, this means that the method call syntax is not available for **Note**: The Nim compiler prior to version 1 was more lenient about this -requirement. Use the ``--useVersion:0.19`` switch for a transition period. +requirement. Use the `--useVersion:0.19`:option: switch for a transition period. @@ -6960,7 +6960,7 @@ with the project: {.compile: "myfile.cpp".} **Note**: Nim computes a SHA1 checksum and only recompiles the file if it -has changed. One can use the ``-f`` command-line option to force +has changed. One can use the `-f`:option: command-line option to force the recompilation of the file. Since 1.4 the `compile` pragma is also available with this syntax: @@ -6983,7 +6983,7 @@ The `link` pragma can be used to link an additional file with the project: PassC pragma ------------ The `passc` pragma can be used to pass additional parameters to the C -compiler like one would using the command-line switch ``--passc``: +compiler like one would using the command-line switch `--passc`:option:\: .. code-block:: Nim {.passc: "-Wall -Werror".} @@ -7011,7 +7011,7 @@ the pragma resides in: PassL pragma ------------ The `passL` pragma can be used to pass additional parameters to the linker -like one would be using the command-line switch ``--passL``: +like one would be using the command-line switch `--passL`:option:\: .. code-block:: Nim {.passL: "-lSDLmain -lSDL".} @@ -7047,8 +7047,8 @@ Example: embedsC() -`nimbase.h` defines `NIM_EXTERNC` C macro that can be used for -`extern "C"`:cpp: code to work with both `nim c` and `nim cpp`, e.g.: +``nimbase.h`` defines `NIM_EXTERNC`:c: C macro that can be used for +`extern "C"`:cpp: code to work with both `nim c`:cmd: and `nim cpp`:cmd:, e.g.: .. code-block:: Nim proc foobar() {.importc:"$1".} @@ -7124,7 +7124,7 @@ pragmas this allows *sloppy* interfacing with libraries written in C++: proc run(device: IrrlichtDevice): bool {. header: irr, importcpp: "#.run(@)".} -The compiler needs to be told to generate C++ (command ``cpp``) for +The compiler needs to be told to generate C++ (command `cpp`:option:) for this to work. The conditional symbol `cpp` is defined when the compiler emits C++ code. @@ -7368,7 +7368,7 @@ allows *sloppy* interfacing with libraries written in Objective C: g.greet(12, 34) g.free() -The compiler needs to be told to generate Objective C (command ``objc``) for +The compiler needs to be told to generate Objective C (command `objc`:option:) for this to work. The conditional symbol ``objc`` is defined when the compiler emits Objective C code. @@ -7414,7 +7414,7 @@ will generate this code: The `.cppNonPod` pragma should be used for non-POD `importcpp` types so that they work properly (in particular regarding constructor and destructor) for -`.threadvar` variables. This requires ``--tlsEmulation:off``. +`.threadvar` variables. This requires `--tlsEmulation:off`:option:. .. code-block:: nim type Foo {.cppNonPod, importcpp, header: "funs.h".} = object @@ -7458,12 +7458,13 @@ pragma description :: nim c -d:FooBar=42 foobar.nim -In the above example, providing the ``-d`` flag causes the symbol +In the above example, providing the `-d`:option: flag causes the symbol `FooBar` to be overwritten at compile-time, printing out 42. If the -``-d:FooBar=42`` were to be omitted, the default value of 5 would be +`-d:FooBar=42`:option: were to be omitted, the default value of 5 would be used. To see if a value was provided, `defined(FooBar)` can be used. -The syntax ``-d:flag`` is actually just a shortcut for ``-d:flag=true``. +The syntax `-d:flag`:option: is actually just a shortcut for +`-d:flag=true`:option:. User-defined pragmas ==================== @@ -7800,7 +7801,7 @@ strings, because they are precompiled. because of order of initialization problems. **Note**: A `dynlib` import can be overridden with -the ``--dynlibOverride:name`` command-line option. The +the `--dynlibOverride:name`:option: command-line option. The `Compiler User Guide `_ contains further information. @@ -7815,14 +7816,14 @@ conjunction with the `exportc` pragma: proc exportme(): int {.cdecl, exportc, dynlib.} This is only useful if the program is compiled as a dynamic library via the -``--app:lib`` command-line option. +`--app:lib`:option: command-line option. Threads ======= -To enable thread support the ``--threads:on`` command-line switch needs to +To enable thread support the `--threads:on`:option: command-line switch needs to be used. The system_ module then contains several threading primitives. See the `threads `_ and `channels `_ modules for the low-level thread API. There are also high-level parallelism constructs @@ -7864,7 +7865,7 @@ any of its parameters contain a `ref` or `closure` type. This enforces the *no heap sharing restriction*. Routines that are imported from C are always assumed to be `gcsafe`. -To disable the GC-safety checking the ``--threadAnalysis:off`` command-line +To disable the GC-safety checking the `--threadAnalysis:off`:option: command-line switch can be used. This is a temporary workaround to ease the porting effort from old code to the new threading model. diff --git a/doc/nimc.rst b/doc/nimc.rst index 9db95e81b..47f7243cc 100644 --- a/doc/nimc.rst +++ b/doc/nimc.rst @@ -1,5 +1,3 @@ -.. default-role:: code - =================================== Nim Compiler User Guide =================================== @@ -7,8 +5,12 @@ :Author: Andreas Rumpf :Version: |nimversion| +.. default-role:: code +.. include:: rstcommon.rst .. contents:: +.. + "Look at you, hacker. A pathetic creature of meat and bone, panting and sweating as you run through my corridors. How can you challenge a perfect, immortal machine?" @@ -32,6 +34,9 @@ Command-line switches --------------------- Basic command-line switches are: +.. no syntax highlighting in the below included files at the moment +.. default-role:: code + Usage: .. include:: basicopt.txt @@ -43,11 +48,12 @@ Advanced command-line switches are: .. include:: advopt.txt +.. include:: rstcommon.rst List of warnings ---------------- -Each warning can be activated individually with `--warning[NAME]:on|off` or +Each warning can be activated individually with `--warning[NAME]:on|off`:option: or in a `push` pragma. ========================== ============================================ @@ -71,7 +77,7 @@ User Some user-defined warning. List of hints ------------- -Each hint can be activated individually with `--hint[NAME]:on|off` or in a +Each hint can be activated individually with `--hint[NAME]:on|off`:option: or in a `push` pragma. ========================== ============================================ @@ -132,22 +138,22 @@ Level Description Compile-time symbols -------------------- -Through the `-d:x` or `--define:x` switch you can define compile-time +Through the `-d:x`:option: or `--define:x`:option: switch you can define compile-time symbols for conditional compilation. The defined switches can be checked in source code with the `when statement `_ and `defined proc `_. The typical use of this switch is -to enable builds in release mode (`-d:release`) where optimizations are -enabled for better performance. Another common use is the `-d:ssl` switch to +to enable builds in release mode (`-d:release`:option:) where optimizations are +enabled for better performance. Another common use is the `-d:ssl`:option: switch to activate SSL sockets. -Additionally, you may pass a value along with the symbol: `-d:x=y` +Additionally, you may pass a value along with the symbol: `-d:x=y`:option: which may be used in conjunction with the `compile-time define pragmas`_ to override symbols during build time. Compile-time symbols are completely **case insensitive** and underscores are -ignored too. `--define:FOO` and `--define:foo` are identical. +ignored too. `--define:FOO`:option: and `--define:foo`:option: are identical. Compile-time symbols starting with the `nim` prefix are reserved for the implementation and should not be used elsewhere. @@ -156,28 +162,47 @@ implementation and should not be used elsewhere. Configuration files ------------------- -**Note:** The *project file name* is the name of the `.nim` file that is +**Note:** The *project file name* is the name of the ``.nim`` file that is passed as a command-line argument to the compiler. -The `nim` executable processes configuration files in the following +The `nim`:cmd: executable processes configuration files in the following directories (in this order; later files overwrite previous settings): -1) `$nim/config/nim.cfg`, `/etc/nim/nim.cfg` (UNIX) or ``\config\nim.cfg`` (Windows). This file can be skipped with the `--skipCfg` command line option. -2) If environment variable `XDG_CONFIG_HOME` is defined, `$XDG_CONFIG_HOME/nim/nim.cfg` or `~/.config/nim/nim.cfg` (POSIX) or `%APPDATA%/nim/nim.cfg` (Windows). This file can be skipped with the `--skipUserCfg` command line option. -3) `$parentDir/nim.cfg` where `$parentDir` stands for any parent directory of the project file's path. These files can be skipped with the `--skipParentCfg` command-line option. -4) `$projectDir/nim.cfg` where `$projectDir` stands for the project file's path. This file can be skipped with the `--skipProjCfg` command-line option. -5) A project can also have a project-specific configuration file named `$project.nim.cfg` that resides in the same directory as `$project.nim`. This file can be skipped with the `--skipProjCfg` command-line option. +1) ``$nim/config/nim.cfg``, ``/etc/nim/nim.cfg`` (UNIX) or + ``\config\nim.cfg`` (Windows). + This file can be skipped with the `--skipCfg`:option: command line option. +2) If environment variable `XDG_CONFIG_HOME` is defined, + ``$XDG_CONFIG_HOME/nim/nim.cfg`` or ``~/.config/nim/nim.cfg`` (POSIX) or + ``%APPDATA%/nim/nim.cfg`` (Windows). + This file can be skipped with the `--skipUserCfg`:option: command line + option. +3) ``$parentDir/nim.cfg`` where ``$parentDir`` stands for any parent + directory of the project file's path. + These files can be skipped with the `--skipParentCfg`:option: + command-line option. +4) ``$projectDir/nim.cfg`` where ``$projectDir`` stands for the project + file's path. + This file can be skipped with the `--skipProjCfg`:option: + command-line option. +5) A project can also have a project-specific configuration file named + ``$project.nim.cfg`` that resides in the same directory as ``$project.nim``. + This file can be skipped with the `--skipProjCfg`:option: + command-line option. Command-line settings have priority over configuration file settings. The default build of a project is a `debug build`:idx:. To compile a -`release build`:idx: define the `release` symbol:: +`release build`:idx: define the `release` symbol: + +.. code:: cmd nim c -d:release myproject.nim - To compile a `dangerous release build`:idx: define the `danger` symbol:: +To compile a `dangerous release build`:idx: define the `danger` symbol: + +.. code:: cmd nim c -d:danger myproject.nim @@ -189,10 +214,10 @@ Nim has the concept of a global search path (PATH) that is queried to determine where to find imported modules or include files. If multiple files are found an ambiguity error is produced. -`nim dump` shows the contents of the PATH. +`nim dump`:cmd: shows the contents of the PATH. However before the PATH is used the current directory is checked for the -file's existence. So if PATH contains `$lib` and `$lib/bar` and the +file's existence. So if PATH contains ``$lib`` and ``$lib/bar`` and the directory structure looks like this:: $lib/x.nim @@ -202,26 +227,26 @@ directory structure looks like this:: other.nim And `main` imports `x`, `foo/x` is imported. If `other` imports `x` -then both `$lib/x.nim` and `$lib/bar/x.nim` match but `$lib/x.nim` is used +then both ``$lib/x.nim`` and ``$lib/bar/x.nim`` match but ``$lib/x.nim`` is used as it is the first match. Generated C code directory -------------------------- The generated files that Nim produces all go into a subdirectory called -`nimcache`. Its full path is +``nimcache``. Its full path is -- `$XDG_CACHE_HOME/nim/$projectname(_r|_d)` or `~/.cache/nim/$projectname(_r|_d)` +- ``$XDG_CACHE_HOME/nim/$projectname(_r|_d)`` or ``~/.cache/nim/$projectname(_r|_d)`` on Posix -- `$HOME/nimcache/$projectname(_r|_d)` on Windows. +- ``$HOME\nimcache\$projectname(_r|_d)`` on Windows. The `_r` suffix is used for release builds, `_d` is for debug builds. This makes it easy to delete all generated files. -The `--nimcache` +The `--nimcache`:option: `compiler switch <#compiler-usage-commandminusline-switches>`_ can be used to -to change the `nimcache` directory. +to change the ``nimcache`` directory. However, the generated C code is not platform-independent. C code generated for Linux does not compile on Windows, for instance. The comment on top of the @@ -231,34 +256,40 @@ C file lists the OS, CPU, and CC the file has been compiled for. Compiler Selection ================== -To change the compiler from the default compiler (at the command line):: +To change the compiler from the default compiler (at the command line): + +.. code:: cmd nim c --cc:llvm_gcc --compile_only myfile.nim -This uses the configuration defined in ``config\nim.cfg`` for `lvm_gcc`. +This uses the configuration defined in ``config\nim.cfg`` for `llvm_gcc`:cmd:. If nimcache already contains compiled code from a different compiler for the same project, -add the `-f` flag to force all files to be recompiled. +add the `-f`:option: flag to force all files to be recompiled. The default compiler is defined at the top of ``config\nim.cfg``. -Changing this setting affects the compiler used by `koch` to (re)build Nim. +Changing this setting affects the compiler used by `koch`:cmd: to (re)build Nim. -To use the `CC` environment variable, use `nim c --cc:env myfile.nim`. To use the -`CXX` environment variable, use `nim cpp --cc:env myfile.nim`. `--cc:env` is available -since Nim version 1.4. +To use the `CC` environment variable, use `nim c --cc:env myfile.nim`:cmd:. +To use the `CXX` environment variable, use `nim cpp --cc:env myfile.nim`:cmd:. +`--cc:env`:option: is available since Nim version 1.4. Cross-compilation ================= -To cross compile, use for example:: +To cross compile, use for example: + +.. code:: cmd nim c --cpu:i386 --os:linux --compileOnly --genScript myproject.nim -Then move the C code and the compile script `compile_myproject.sh` to your +Then move the C code and the compile script `compile_myproject.sh`:cmd: to your Linux i386 machine and run the script. -Another way is to make Nim invoke a cross compiler toolchain:: +Another way is to make Nim invoke a cross compiler toolchain: + +.. code:: cmd nim c --cpu:arm --os:linux myproject.nim @@ -274,17 +305,22 @@ configuration file should contain something like:: Cross-compilation for Windows ============================= -To cross-compile for Windows from Linux or macOS using the MinGW-w64 toolchain:: +To cross-compile for Windows from Linux or macOS using the MinGW-w64 toolchain: + +.. code:: cmd nim c -d:mingw myproject.nim -Use `--cpu:i386` or `--cpu:amd64` to switch the CPU architecture. +Use `--cpu:i386`:option: or `--cpu:amd64`:option: to switch the CPU architecture. -The MinGW-w64 toolchain can be installed as follows:: +The MinGW-w64 toolchain can be installed as follows: - Ubuntu: apt install mingw-w64 - CentOS: yum install mingw32-gcc | mingw64-gcc - requires EPEL - OSX: brew install mingw-w64 +.. code:: cmd + + apt install mingw-w64 # Ubuntu + yum install mingw32-gcc + yum install mingw64-gcc # CentOS - requires EPEL + brew install mingw-w64 # OSX Cross-compilation for Android @@ -298,7 +334,7 @@ The first one is to treat Android as a simple Linux and use directly on android as if it was Linux. These programs are console-only programs that can't be distributed in the Play Store. -Use regular `nim c` inside termux to make Android terminal programs. +Use regular `nim c`:cmd: inside termux to make Android terminal programs. Normal Android apps are written in Java, to use Nim inside an Android app you need a small Java stub that calls out to a native library written in @@ -306,16 +342,16 @@ Nim using the `NDK `_. You can also use `native-activity `_ to have the Java stub be auto-generated for you. -Use `nim c -c --cpu:arm --os:android -d:androidNDK --noMain:on` to +Use `nim c -c --cpu:arm --os:android -d:androidNDK --noMain:on`:cmd: to generate the C source files you need to include in your Android Studio project. Add the generated C files to CMake build script in your Android project. Then do the final compile with Android Studio which uses Gradle to call CMake to compile the project. -Because Nim is part of a library it can't have its own c style `main()` -so you would need to define your own `android_main` and init the Java +Because Nim is part of a library it can't have its own C-style `main()`:c: +so you would need to define your own `android_main`:c: and init the Java environment, or use a library like SDL2 or GLFM to do it. After the Android -stuff is done, it's very important to call `NimMain()` in order to +stuff is done, it's very important to call `NimMain()`:c: in order to initialize Nim's garbage collector and to run the top level statements of your program. @@ -334,14 +370,14 @@ Normal languages for iOS development are Swift and Objective C. Both of these use LLVM and can be compiled into object files linked together with C, C++ or Objective C code produced by Nim. -Use `nim c -c --os:ios --noMain:on` to generate C files and include them in +Use `nim c -c --os:ios --noMain:on`:cmd: to generate C files and include them in your XCode project. Then you can use XCode to compile, link, package and sign everything. -Because Nim is part of a library it can't have its own c style `main()` so you +Because Nim is part of a library it can't have its own C-style `main()`:c: so you would need to define `main` that calls `autoreleasepool` and `UIApplicationMain` to do it, or use a library like SDL2 or GLFM. After -the iOS setup is done, it's very important to call `NimMain()` to +the iOS setup is done, it's very important to call `NimMain()`:c: to initialize Nim's garbage collector and to run the top-level statements of your program. @@ -358,17 +394,16 @@ so you need to clean those files manually to do a clean build. Cross-compilation for Nintendo Switch ===================================== -Simply add --os:nintendoswitch -to your usual `nim c` or `nim cpp` command and set the `passC` -and `passL` command line switches to something like: +Simply add `--os:nintendoswitch`:option: +to your usual `nim c`:cmd: or `nim cpp`:cmd: command and set the `passC`:option: +and `passL`:option: command line switches to something like: -.. code-block:: console +.. code-block:: cmd nim c ... --passC="-I$DEVKITPRO/libnx/include" ... --passL="-specs=$DEVKITPRO/libnx/switch.specs -L$DEVKITPRO/libnx/lib -lnx" -or setup a nim.cfg file like so: +or setup a ``nim.cfg`` file like so:: -.. code-block:: Nim #nim.cfg --passC="-I$DEVKITPRO/libnx/include" --passL="-specs=$DEVKITPRO/libnx/switch.specs -L$DEVKITPRO/libnx/lib -lnx" @@ -377,12 +412,14 @@ The DevkitPro setup must be the same as the default with their new installer `here for Mac/Linux `_ or `here for Windows `_. -For example, with the above-mentioned config:: +For example, with the above-mentioned config: + +.. code:: cmd nim c --os:nintendoswitch switchhomebrew.nim -This will generate a file called `switchhomebrew.elf` which can then be turned into -an nro file with the `elf2nro` tool in the DevkitPro release. Examples can be found at +This will generate a file called ``switchhomebrew.elf`` which can then be turned into +an nro file with the `elf2nro`:cmd: tool in the DevkitPro release. Examples can be found at `the nim-libnx github repo `_. There are a few things that don't work because the DevkitPro libraries don't support them. @@ -402,16 +439,20 @@ DLL generation Nim supports the generation of DLLs. However, there must be only one instance of the GC per process/address space. This instance is contained in -`nimrtl.dll`. This means that every generated Nim DLL depends -on `nimrtl.dll`. To generate the "nimrtl.dll" file, use the command:: +``nimrtl.dll``. This means that every generated Nim DLL depends +on ``nimrtl.dll``. To generate the "nimrtl.dll" file, use the command: + +.. code:: cmd nim c -d:release lib/nimrtl.nim -To link against `nimrtl.dll` use the command:: +To link against ``nimrtl.dll`` use the command: + +.. code:: cmd nim c -d:useNimRtl myprog.nim -**Note**: Currently the creation of `nimrtl.dll` with thread support has +**Note**: Currently the creation of ``nimrtl.dll`` with thread support has never been tested and is unlikely to work! @@ -427,20 +468,20 @@ Define Effect ====================== ========================================================= `release` Turns on the optimizer. More aggressive optimizations are possible, e.g.: - `--passC:-ffast-math` (but see issue #10305) + `--passC:-ffast-math`:option: (but see issue #10305) `danger` Turns off all runtime checks and turns on the optimizer. -`useFork` Makes `osproc` use `fork` instead of `posix_spawn`. -`useNimRtl` Compile and link against `nimrtl.dll`. +`useFork` Makes `osproc` use `fork`:c: instead of `posix_spawn`:c:. +`useNimRtl` Compile and link against ``nimrtl.dll``. `useMalloc` Makes Nim use C's `malloc`:idx: instead of Nim's own memory manager, albeit prefixing each allocation with its size to support clearing memory on reallocation. - This only works with `gc:none`, `gc:arc` and - `--gc:orc`. + This only works with `--gc:none`:option:, + `--gc:arc`:option: and `--gc:orc`:option:. `useRealtimeGC` Enables support of Nim's GC for *soft* realtime systems. See the documentation of the `gc `_ for further information. `logGC` Enable GC logging to stdout. -`nodejs` The JS target is actually `node.js`. +`nodejs` The JS target is actually ``node.js``. `ssl` Enables OpenSSL support for the sockets module. `memProfiler` Enables memory profiling for the native GC. `uClibc` Use uClibc instead of libc. (Relevant for Unix-like OSes) @@ -448,16 +489,16 @@ Define Effect what's in the Nim file with what's in the C header. This may become enabled by default in the future. `tempDir` This symbol takes a string as its value, like - `--define:tempDir:/some/temp/path` to override the - temporary directory returned by `os.getTempDir()`. + `--define:tempDir:/some/temp/path`:option: to override + the temporary directory returned by `os.getTempDir()`. The value **should** end with a directory separator character. (Relevant for the Android platform) `useShPath` This symbol takes a string as its value, like - `--define:useShPath:/opt/sh/bin/sh` to override the - path for the `sh` binary, in cases where it is not - located in the default location `/bin/sh`. -`noSignalHandler` Disable the crash handler from `system.nim`. -`globalSymbols` Load all `{.dynlib.}` libraries with the `RTLD_GLOBAL` + `--define:useShPath:/opt/sh/bin/sh`:option: to override + the path for the `sh`:cmd: binary, in cases where it is + not located in the default location ``/bin/sh``. +`noSignalHandler` Disable the crash handler from ``system.nim``. +`globalSymbols` Load all `{.dynlib.}` libraries with the `RTLD_GLOBAL`:c: flag on Posix systems to resolve symbols in subsequently loaded libraries. ====================== ========================================================= @@ -474,20 +515,21 @@ generator and are subject to change. LineDir option -------------- -The `lineDir` option can be turned on or off. If turned on the -generated C code contains `#line` directives. This may be helpful for +The `--lineDir`:option: option can be turned on or off. If turned on the +generated C code contains `#line`:c: directives. This may be helpful for debugging with GDB. StackTrace option ----------------- -If the `stackTrace` option is turned on, the generated C contains code to +If the `--stackTrace`:option: option is turned on, the generated C contains code to ensure that proper stack traces are given if the program crashes or some uncaught exception is raised. LineTrace option ---------------- -The `lineTrace` option implies the `stackTrace` option. If turned on, +The `--lineTrace`:option: option implies the `stackTrace`:option: option. +If turned on, the generated C contains code to ensure that proper stack traces with line number information are given if the program crashes or an uncaught exception is raised. @@ -497,11 +539,13 @@ DynlibOverride ============== By default Nim's `dynlib` pragma causes the compiler to generate -`GetProcAddress` (or their Unix counterparts) -calls to bind to a DLL. With the `dynlibOverride` command line switch this -can be prevented and then via `--passL` the static library can be linked +`GetProcAddress`:cpp: (or their Unix counterparts) +calls to bind to a DLL. With the `dynlibOverride`:option: command line switch this +can be prevented and then via `--passL`:option: the static library can be linked against. For instance, to link statically against Lua this command might work -on Linux:: +on Linux: + +.. code:: cmd nim c --dynlibOverride:lua --passL:liblua.lib program.nim @@ -509,8 +553,8 @@ on Linux:: Backend language options ======================== -The typical compiler usage involves using the `compile` or `c` command to -transform a `.nim` file into one or more `.c` files which are then +The typical compiler usage involves using the `compile`:option: or `c`:option: +command to transform a ``.nim`` file into one or more ``.c`` files which are then compiled with the platform's C compiler into a static binary. However, there are other commands to compile to C++, Objective-C, or JavaScript. More details can be read in the `Nim Backend Integration document `_. @@ -520,7 +564,7 @@ Nim documentation tools ======================= Nim provides the `doc`:idx: command to generate HTML -documentation from `.nim` source files. Only exported symbols will appear in +documentation from ``.nim`` source files. Only exported symbols will appear in the output. For more details `see the docgen documentation `_. Nim idetools integration @@ -558,19 +602,21 @@ embedded microprocessors with only a few kilobytes of memory. A good start is to use the `any` operating target together with the `malloc` memory allocator and the `arc` garbage collector. For example: -`nim c --os:any --gc:arc -d:useMalloc [...] x.nim` +.. code:: cmd -- `--gc:arc` will enable the reference counting memory management instead + nim c --os:any --gc:arc -d:useMalloc [...] x.nim + +- `--gc:arc`:option: will enable the reference counting memory management instead of the default garbage collector. This enables Nim to use heap memory which is required for strings and seqs, for example. -- The `--os:any` target makes sure Nim does not depend on any specific +- The `--os:any`:option: target makes sure Nim does not depend on any specific operating system primitives. Your platform should support only some basic ANSI C library `stdlib` and `stdio` functions which should be available on almost any platform. -- The `-d:useMalloc` option configures Nim to use only the standard C memory - manage primitives `malloc()`, `free()`, `realloc()`. +- The `-d:useMalloc`:option: option configures Nim to use only the standard C memory + manage primitives `malloc()`:c:, `free()`:c:, `realloc()`:c:. If your platform does not provide these functions it should be trivial to provide an implementation for them and link these to your program. @@ -580,13 +626,13 @@ additional flags to both the Nim compiler and the C compiler and/or linker to optimize the build for size. For example, the following flags can be used when targeting a gcc compiler: -`--opt:size --passC:-flto --passL:-flto` +`--opt:size --passC:-flto --passL:-flto`:option: -The `--opt:size` flag instructs Nim to optimize code generation for small -size (with the help of the C compiler), the `flto` flags enable link-time +The `--opt:size`:option: flag instructs Nim to optimize code generation for small +size (with the help of the C compiler), the `-flto`:option: flags enable link-time optimization in the compiler and linker. -Check the `Cross-compilation` section for instructions on how to compile the +Check the `Cross-compilation`_ section for instructions on how to compile the program for your target. Nim for realtime systems @@ -603,7 +649,7 @@ The Nim programming language has no concept of Posix's signal handling mechanisms. However, the standard library offers some rudimentary support for signal handling, in particular, segmentation faults are turned into fatal errors that produce a stack trace. This can be disabled with the -`-d:noSignalHandler` switch. +`-d:noSignalHandler`:option: switch. Optimizing for Nim diff --git a/doc/rstcommon.rst b/doc/rstcommon.rst index c1503fa96..7ecc7b8cb 100644 --- a/doc/rstcommon.rst +++ b/doc/rstcommon.rst @@ -1,6 +1,51 @@ +.. + Usage of this file: + Add this in the beginning of *.rst file:: + + .. default-role:: code + .. include:: rstcommon.rst + + It's the current trick for brevity and compatibility with both Github and + rst2html.py, considering that Github cannot highlight Nim in + RST files anyway and it does not include files. + This way interpreted text is displayed with monospaced font in Github + and it's displayed an Nim code in both rst2html.py + (note ".. default-role:: Nim" above) and `nim rst2html`. + + For files that are user manual and consist of stuff like cmdline + option description, use 'code' as a **real** default role: + + .. include:: rstcommon.rst + .. default-role:: code + +.. define language roles explicitly (for compatibility with rst2html.py): .. role:: nim(code) :language: nim .. default-role:: nim +.. role:: c(code) + :language: c + +.. role:: cpp(code) + :language: cpp + +.. role:: yaml(code) + :language: yaml + +.. role:: python(code) + :language: python + +.. role:: java(code) + :language: java + +.. role:: csharp(code) + :language: csharp + +.. role:: cmd(code) + +.. role:: program(code) + +.. role:: option(code) + From 16405083485967a395b8d677833bc26040881b21 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Thu, 29 Apr 2021 18:31:15 +0300 Subject: [PATCH 2035/3768] more strict RST inline markup parsing (#17827) * more strict RST inline markup parsing * add test for unexpected bonus * introduce `roPreferMarkdown` --- compiler/docgen.nim | 7 +- lib/packages/docutils/rst.nim | 52 ++++++++--- tests/stdlib/trst.nim | 161 ++++++++++++++++++++++++++++++++++ 3 files changed, 207 insertions(+), 13 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 977fcf8ef..78fb88f9b 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -178,7 +178,8 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef, result.outDir = conf.outDir.string initRstGenerator(result[], (if conf.cmd != cmdRst2tex: outHtml else: outLatex), conf.configVars, filename.string, - {roSupportRawDirective, roSupportMarkdown, roNimFile}, + {roSupportRawDirective, roSupportMarkdown, + roPreferMarkdown, roNimFile}, docgenFindFile, compilerMsgHandler) if conf.configVars.hasKey("doc.googleAnalytics"): @@ -1380,7 +1381,9 @@ proc commandRstAux(cache: IdentCache, conf: ConfigRef; d.isPureRst = true var rst = parseRst(readFile(filen.string), filen.string, line=LineRstInit, column=ColRstInit, - d.hasToc, {roSupportRawDirective, roSupportMarkdown}, conf) + d.hasToc, + {roSupportRawDirective, roSupportMarkdown, roPreferMarkdown}, + conf) var modDesc = newStringOfCap(30_000) renderRstToOut(d[], rst, modDesc) d.modDesc = rope(modDesc) diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index cb65791ff..6ad466716 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -11,9 +11,9 @@ ## rst ## ================================== ## -## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -## Nim-flavored reStructuredText -## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +## Nim-flavored reStructuredText and Markdown +## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ## ## This module implements a `reStructuredText`:idx: (RST) parser. ## A large subset is implemented with some limitations_ and @@ -177,6 +177,8 @@ type roSupportRawDirective, ## support the ``raw`` directive (don't support ## it for sandboxing) roSupportMarkdown, ## support additional features of Markdown + roPreferMarkdown, ## parse as Markdown (keeping RST as "extension" + ## to Markdown) -- implies `roSupportMarkdown` roNimFile ## set for Nim files where default interpreted ## text role should be :nim: @@ -277,6 +279,7 @@ type line*, col*, baseIndent*: int skipPounds*: bool adornmentLine*: bool + escapeNext*: bool proc getThing(L: var Lexer, tok: var Token, s: set[char]) = tok.kind = tkWord @@ -314,10 +317,18 @@ proc getPunctAdornment(L: var Lexer, tok: var Token) = tok.col = L.col var pos = L.bufpos let c = L.buf[pos] - while true: + if not L.escapeNext and (c != '\\' or L.adornmentLine): + while true: + tok.symbol.add(L.buf[pos]) + inc pos + if L.buf[pos] != c: break + elif L.escapeNext: tok.symbol.add(L.buf[pos]) inc pos - if L.buf[pos] != c: break + else: # not L.escapeNext and c == '\\' and not L.adornmentLine + tok.symbol.add '\\' + inc pos + L.escapeNext = true inc L.col, pos - L.bufpos L.bufpos = pos if tok.symbol == "\\": tok.kind = tkPunct @@ -429,7 +440,9 @@ proc getTokens(buffer: string, skipPounds: bool, tokens: var TokenSeq): int = while true: inc length setLen(tokens, length) + let toEscape = L.escapeNext rawGetTok(L, tokens[length - 1]) + if toEscape: L.escapeNext = false if tokens[length - 1].kind == tkEof: break if tokens[0].kind == tkWhite: # BUGFIX @@ -981,16 +994,24 @@ proc expect(p: var RstParser, tok: string) = if currentTok(p).symbol == tok: inc p.idx else: rstMessage(p, meExpected, tok) -proc isInlineMarkupEnd(p: RstParser, markup: string): bool = +proc isInlineMarkupEnd(p: RstParser, markup: string, exact: bool): bool = # rst rules: https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#inline-markup-recognition-rules - result = currentTok(p).symbol == markup + if exact: + result = currentTok(p).symbol == markup + else: + result = currentTok(p).symbol.endsWith markup + if (not result) and markup == "``": + # check that escaping may have splitted `` to 2 tokens ` and ` + result = currentTok(p).symbol == "`" and prevTok(p).symbol == "`" if not result: return # Rule 2: result = prevTok(p).kind notin {tkIndent, tkWhite} if not result: return # Rule 7: result = nextTok(p).kind in {tkIndent, tkWhite, tkEof} or - markup in ["``", "`"] and nextTok(p).kind in {tkIndent, tkWhite, tkWord, tkEof} or + (roPreferMarkdown in p.s.options and + markup in ["``", "`"] and + nextTok(p).kind in {tkIndent, tkWhite, tkWord, tkEof}) or nextTok(p).symbol[0] in {'\'', '\"', ')', ']', '}', '>', '-', '/', '\\', ':', '.', ',', ';', '!', '?', '_'} if not result: return @@ -1130,7 +1151,8 @@ proc toOtherRole(n: PRstNode, kind: RstNodeKind, roleName: string): PRstNode = proc parsePostfix(p: var RstParser, n: PRstNode): PRstNode = var newKind = n.kind var newSons = n.sons - if isInlineMarkupEnd(p, "_") or isInlineMarkupEnd(p, "__"): + if isInlineMarkupEnd(p, "_", exact=true) or + isInlineMarkupEnd(p, "__", exact=true): inc p.idx if p.tok[p.idx-2].symbol == "`" and p.tok[p.idx-3].symbol == ">": var a = newRstNode(rnInner) @@ -1215,7 +1237,7 @@ proc parseWordOrRef(p: var RstParser, father: PRstNode) = inc p.idx while currentTok(p).kind in {tkWord, tkPunct}: if currentTok(p).kind == tkPunct: - if isInlineMarkupEnd(p, "_"): + if isInlineMarkupEnd(p, "_", exact=true): isRef = true break if not validRefnamePunct(currentTok(p).symbol): @@ -1253,7 +1275,15 @@ proc parseUntil(p: var RstParser, father: PRstNode, postfix: string, while true: case currentTok(p).kind of tkPunct: - if isInlineMarkupEnd(p, postfix): + if isInlineMarkupEnd(p, postfix, exact=false): + let l = currentTok(p).symbol.len + if l > postfix.len: + # handle cases like *emphasis with stars****. (It's valid RST!) + father.add newLeaf(currentTok(p).symbol[0 ..< l - postfix.len]) + elif postfix == "``" and currentTok(p).symbol == "`" and + prevTok(p).symbol == "`": + # handle cases like ``literal\`` - delete ` already added after \ + father.sons.setLen(father.sons.len - 1) inc p.idx break else: diff --git a/tests/stdlib/trst.nim b/tests/stdlib/trst.nim index 2398b92a8..fef80dfc7 100644 --- a/tests/stdlib/trst.nim +++ b/tests/stdlib/trst.nim @@ -4,6 +4,10 @@ discard """ [Suite] RST indentation [Suite] RST include directive + +[Suite] RST escaping + +[Suite] RST inline markup ''' """ @@ -267,3 +271,160 @@ And this should **NOT** be visible in `docs.html` """ doAssert "Visible" == rstTohtml(input, {}, defaultConfig()) removeFile("other.rst") + +suite "RST escaping": + test "backspaces": + check("""\ this""".toAst == dedent""" + rnLeaf 'this' + """) + + check("""\\ this""".toAst == dedent""" + rnInner + rnLeaf '\' + rnLeaf ' ' + rnLeaf 'this' + """) + + check("""\\\ this""".toAst == dedent""" + rnInner + rnLeaf '\' + rnLeaf 'this' + """) + + check("""\\\\ this""".toAst == dedent""" + rnInner + rnLeaf '\' + rnLeaf '\' + rnLeaf ' ' + rnLeaf 'this' + """) + +suite "RST inline markup": + test "end-string has repeating symbols": + check("*emphasis content****".toAst == dedent""" + rnEmphasis + rnLeaf 'emphasis' + rnLeaf ' ' + rnLeaf 'content' + rnLeaf '***' + """) + + check("""*emphasis content\****""".toAst == dedent""" + rnEmphasis + rnLeaf 'emphasis' + rnLeaf ' ' + rnLeaf 'content' + rnLeaf '*' + rnLeaf '**' + """) # exact configuration of leafs with * is not really essential, + # only total number of * is essential + + check("**strong content****".toAst == dedent""" + rnStrongEmphasis + rnLeaf 'strong' + rnLeaf ' ' + rnLeaf 'content' + rnLeaf '**' + """) + + check("""**strong content*\****""".toAst == dedent""" + rnStrongEmphasis + rnLeaf 'strong' + rnLeaf ' ' + rnLeaf 'content' + rnLeaf '*' + rnLeaf '*' + rnLeaf '*' + """) + + check("``lit content`````".toAst == dedent""" + rnInlineLiteral + rnLeaf 'lit' + rnLeaf ' ' + rnLeaf 'content' + rnLeaf '```' + """) + + + test """interpreted text can be ended with \` """: + let output = (".. default-role:: literal\n" & """`\``""").toAst + check(output.endsWith """ + rnParagraph + rnInlineLiteral + rnLeaf '`'""" & "\n") + + let output2 = """`\``""".toAst + check(output2 == dedent""" + rnInlineCode + rnDirArg + rnLeaf 'nim' + [nil] + rnLiteralBlock + rnLeaf '`' + """) + + let output3 = """`proc \`+\``""".toAst + check(output3 == dedent""" + rnInlineCode + rnDirArg + rnLeaf 'nim' + [nil] + rnLiteralBlock + rnLeaf 'proc `+`' + """) + + test """inline literals can contain \ anywhere""": + check("""``\``""".toAst == dedent""" + rnInlineLiteral + rnLeaf '\' + """) + + check("""``\\``""".toAst == dedent""" + rnInlineLiteral + rnLeaf '\' + rnLeaf '\' + """) + + check("""``\```""".toAst == dedent""" + rnInlineLiteral + rnLeaf '\' + rnLeaf '`' + """) + + check("""``\\```""".toAst == dedent""" + rnInlineLiteral + rnLeaf '\' + rnLeaf '\' + rnLeaf '`' + """) + + check("""``\````""".toAst == dedent""" + rnInlineLiteral + rnLeaf '\' + rnLeaf '`' + rnLeaf '`' + """) + + test "references with _ at the end": + check(dedent""" + .. _lnk: https + + lnk_""".toAst == + dedent""" + rnHyperlink + rnInner + rnLeaf 'lnk' + rnInner + rnLeaf 'https' + """) + + test "not a hyper link": + check(dedent""" + .. _lnk: https + + lnk___""".toAst == + dedent""" + rnInner + rnLeaf 'lnk' + rnLeaf '___' + """) From 20248a68fd867ce64822698b316f1a8b6300d7ca Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 30 Apr 2021 02:00:33 -0700 Subject: [PATCH 2036/3768] gitutils: add diffStrings, diffFiles, and use it in testament to compare expected vs gotten (#17892) * gitutils: add diffStrings, diffFiles, and use it in testament to compare expected vs gotten * refactor with createTempDir * cleanup * refacotr * PRTEMP fake test spec changes to show effect of diffStrings * add runnableExamples for experimental/diff + cross-reference with gitutils * Revert "PRTEMP fake test spec changes to show effect of diffStrings" This reverts commit 57dc8d642dce6c1127c98b7cbc9edbfe747d4047. --- lib/experimental/diff.nim | 45 +++++++++++++++++---------------- lib/std/private/gitutils.nim | 40 +++++++++++++++++++++++++++++- lib/std/tempfiles.nim | 48 +++++++++++++++++++----------------- nimdoc/rsttester.nim | 3 ++- nimdoc/tester.nim | 3 ++- nimpretty/tester.nim | 5 ++-- testament/categories.nim | 4 +-- testament/testament.nim | 3 ++- 8 files changed, 99 insertions(+), 52 deletions(-) diff --git a/lib/experimental/diff.nim b/lib/experimental/diff.nim index ba4e8ad64..ea0a5cfb8 100644 --- a/lib/experimental/diff.nim +++ b/lib/experimental/diff.nim @@ -10,28 +10,31 @@ ## This module implements an algorithm to compute the ## `diff`:idx: between two sequences of lines. ## -## A basic example of `diffInt` on 2 arrays of integers: -## -## .. code:: Nim -## -## import experimental/diff -## echo diffInt([0, 1, 2, 3, 4, 5, 6, 7, 8], [-1, 1, 2, 3, 4, 5, 666, 7, 42]) -## -## Another short example of `diffText` to diff strings: -## -## .. code:: Nim -## -## import experimental/diff -## # 2 samples of text for testing (from "The Call of Cthulhu" by Lovecraft) -## let txt0 = """I have looked upon all the universe has to hold of horror, -## even skies of spring and flowers of summer must ever be poison to me.""" -## let txt1 = """I have looked upon all your code has to hold of bugs, -## even skies of spring and flowers of summer must ever be poison to me.""" -## -## echo diffText(txt0, txt1) -## ## - To learn more see `Diff on Wikipedia. `_ +runnableExamples: + assert diffInt( + [0, 1, 2, 3, 4, 5, 6, 7, 8], + [-1, 1, 2, 3, 4, 5, 666, 7, 42]) == + @[Item(startA: 0, startB: 0, deletedA: 1, insertedB: 1), + Item(startA: 6, startB: 6, deletedA: 1, insertedB: 1), + Item(startA: 8, startB: 8, deletedA: 1, insertedB: 1)] + +runnableExamples: + # 2 samples of text (from "The Call of Cthulhu" by Lovecraft) + let txt0 = """ +abc +def ghi +jkl2""" + let txt1 = """ +bacx +abc +def ghi +jkl""" + assert diffText(txt0, txt1) == + @[Item(startA: 0, startB: 0, deletedA: 0, insertedB: 1), + Item(startA: 2, startB: 3, deletedA: 1, insertedB: 1)] + # code owner: Arne Döring # # This is based on C# code written by Matthias Hertel, http://www.mathertel.de @@ -309,7 +312,7 @@ proc diffText*(textA, textB: string): seq[Item] = ## `textB` B-version of the text (usually the new one) ## ## Returns a seq of Items that describe the differences. - + # See also `gitutils.diffStrings`. # prepare the input-text and convert to comparable numbers. var h = initTable[string, int]() # TextA.len + TextB.len <- probably wrong initial size # The A-Version of the data (original data) to be compared. diff --git a/lib/std/private/gitutils.nim b/lib/std/private/gitutils.nim index bf5e7cb1f..5bcd9e377 100644 --- a/lib/std/private/gitutils.nim +++ b/lib/std/private/gitutils.nim @@ -4,7 +4,7 @@ internal API for now, API subject to change # xxx move other git utilities here; candidate for stdlib. -import std/[os, osproc, strutils] +import std/[os, osproc, strutils, tempfiles] const commitHead* = "HEAD" @@ -38,3 +38,41 @@ proc isGitRepo*(dir: string): bool = # usually a series of ../), so we know that it's safe to unconditionally # remove trailing whitespaces from the result. result = status == 0 and output.strip() == "" + +proc diffFiles*(path1, path2: string): tuple[output: string, same: bool] = + ## Returns a human readable diff of files `path1`, `path2`, the exact form of + ## which is implementation defined. + # This could be customized, e.g. non-git diff with `diff -uNdr`, or with + # git diff options (e.g. --color-moved, --word-diff). + # in general, `git diff` has more options than `diff`. + var status = 0 + (result.output, status) = execCmdEx("git diff --no-index $1 $2" % [path1.quoteShell, path2.quoteShell]) + doAssert (status == 0) or (status == 1) + result.same = status == 0 + +proc diffStrings*(a, b: string): tuple[output: string, same: bool] = + ## Returns a human readable diff of `a`, `b`, the exact form of which is + ## implementation defined. + ## See also `experimental.diff`. + runnableExamples: + let a = "ok1\nok2\nok3\n" + let b = "ok1\nok2 alt\nok3\nok4\n" + let (c, same) = diffStrings(a, b) + doAssert not same + let (c2, same2) = diffStrings(a, a) + doAssert same2 + runnableExamples("-r:off"): + let a = "ok1\nok2\nok3\n" + let b = "ok1\nok2 alt\nok3\nok4\n" + echo diffStrings(a, b).output + + template tmpFileImpl(prefix, str): auto = + let path = genTempPath(prefix, "") + writeFile(path, str) + path + let patha = tmpFileImpl("diffStrings_a_", a) + let pathb = tmpFileImpl("diffStrings_b_", b) + defer: + removeFile(patha) + removeFile(pathb) + result = diffFiles(patha, pathb) diff --git a/lib/std/tempfiles.nim b/lib/std/tempfiles.nim index 2a6fe7d83..91a3ce7f3 100644 --- a/lib/std/tempfiles.nim +++ b/lib/std/tempfiles.nim @@ -90,25 +90,33 @@ template randomPathName(length: Natural): string = res[i] = state.sample(letters) res +proc getTempDirImpl(dir: string): string {.inline.} = + result = dir + if result.len == 0: + result = getTempDir() + +proc genTempPath*(prefix, suffix: string, dir = ""): string = + ## Generates a path name in `dir`. + ## + ## If `dir` is empty, (`getTempDir `_) will be used. + ## The path begins with `prefix` and ends with `suffix`. + let dir = getTempDirImpl(dir) + result = dir / (prefix & randomPathName(nimTempPathLength) & suffix) + proc createTempFile*(prefix, suffix: string, dir = ""): tuple[fd: File, path: string] = - ## `createTempFile` creates a new temporary file in the directory `dir`. + ## Creates a new temporary file in the directory `dir`. ## - ## If `dir` is the empty string, the default directory for temporary files - ## (`getTempDir `_) will be used. - ## The temporary file name begins with `prefix` and ends with `suffix`. - ## `createTempFile` returns a file handle to an open file and the path of that file. + ## This generates a path name using `genTempPath(prefix, suffix, dir)` and + ## returns a file handle to an open file and the path of that file, possibly after + ## retrying to ensure it doesn't already exist. ## ## If failing to create a temporary file, `IOError` will be raised. ## ## .. note:: It is the caller's responsibility to remove the file when no longer needed. - var dir = dir - if dir.len == 0: - dir = getTempDir() - + let dir = getTempDirImpl(dir) createDir(dir) - for i in 0 ..< maxRetry: - result.path = dir / (prefix & randomPathName(nimTempPathLength) & suffix) + result.path = genTempPath(prefix, suffix, dir) try: result.fd = safeOpen(result.path) except OSError: @@ -118,25 +126,19 @@ proc createTempFile*(prefix, suffix: string, dir = ""): tuple[fd: File, path: st raise newException(IOError, "Failed to create a temporary file under directory " & dir) proc createTempDir*(prefix, suffix: string, dir = ""): string = - ## `createTempDir` creates a new temporary directory in the directory `dir`. + ## Creates a new temporary directory in the directory `dir`. ## - ## If `dir` is the empty string, the default directory for temporary files - ## (`getTempDir `_) will be used. - ## The temporary directory name begins with `prefix` and ends with `suffix`. - ## `createTempDir` returns the path of that temporary firectory. + ## This generates a dir name using `genTempPath(prefix, suffix, dir)`, creates + ## the directory and returns it, possibly after retrying to ensure it doesn't + ## already exist. ## ## If failing to create a temporary directory, `IOError` will be raised. ## ## .. note:: It is the caller's responsibility to remove the directory when no longer needed. - ## - var dir = dir - if dir.len == 0: - dir = getTempDir() - + let dir = getTempDirImpl(dir) createDir(dir) - for i in 0 ..< maxRetry: - result = dir / (prefix & randomPathName(nimTempPathLength) & suffix) + result = genTempPath(prefix, suffix, dir) try: if not existsOrCreateDir(result): return diff --git a/nimdoc/rsttester.nim b/nimdoc/rsttester.nim index 6d41ffb86..daca3dfc7 100644 --- a/nimdoc/rsttester.nim +++ b/nimdoc/rsttester.nim @@ -1,4 +1,5 @@ import os, strutils +from std/private/gitutils import diffFiles const baseDir = "nimdoc/rst2html" @@ -19,7 +20,7 @@ proc testRst2Html(fixup = false) = exec("$1 rst2html $2" % [nimExe, sourceFile]) let producedHtml = expectedHtml.replace('\\', '/').replace("/expected/", "/source/htmldocs/") if readFile(expectedHtml) != readFile(producedHtml): - discard execShellCmd("diff -uNdr " & expectedHtml & " " & producedHtml) + echo diffFiles(expectedHtml, producedHtml).output inc failures if fixup: copyFile(producedHtml, expectedHtml) diff --git a/nimdoc/tester.nim b/nimdoc/tester.nim index 526295222..9daa0bb51 100644 --- a/nimdoc/tester.nim +++ b/nimdoc/tester.nim @@ -3,6 +3,7 @@ # to change expected results (after carefully verifying everything), use -d:fixup import strutils, os +from std/private/gitutils import diffFiles var failures = 0 @@ -40,7 +41,7 @@ proc testNimDoc(prjDir, docsDir: string; switches: NimSwitches; fixup = false) = inc failures elif readFile(expected) != readFile(produced): echo "FAILURE: files differ: ", produced - discard execShellCmd("diff -uNdr " & expected & " " & produced) + echo diffFiles(expected, produced).output inc failures if fixup: copyFile(produced, expected) diff --git a/nimpretty/tester.nim b/nimpretty/tester.nim index 0a60ce693..b1f15aee6 100644 --- a/nimpretty/tester.nim +++ b/nimpretty/tester.nim @@ -1,6 +1,7 @@ # Small program that runs the test cases import strutils, os, sequtils +from std/private/gitutils import diffFiles const dir = "nimpretty/tests" @@ -26,7 +27,7 @@ proc test(infile, ext: string) = let produced = dir / nimFile.changeFileExt(ext) if readFile(expected) != readFile(produced): echo "FAILURE: files differ: ", nimFile - discard execShellCmd("diff -uNdr " & expected & " " & produced) + echo diffFiles(expected, produced).output failures += 1 else: echo "SUCCESS: files identical: ", nimFile @@ -43,7 +44,7 @@ proc testTogether(infiles: seq[string]) = let produced = dir / "outputdir" / infile if readFile(expected) != readFile(produced): echo "FAILURE: files differ: ", nimFile - discard execShellCmd("diff -uNdr " & expected & " " & produced) + echo diffFiles(expected, produced).output failures += 1 else: echo "SUCCESS: files identical: ", nimFile diff --git a/testament/categories.nim b/testament/categories.nim index ffee5eeb3..f1dee3570 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -665,8 +665,8 @@ proc runJoinedTest(r: var TResults, cat: Category, testsDir: string, options: st if buf != outputExpected: writeFile(outputExceptedFile, outputExpected) - discard execShellCmd("diff -uNdr $1 $2" % [outputExceptedFile, outputGottenFile]) - echo failString & "megatest output different!" + echo diffFiles(outputGottenFile, outputExceptedFile).output + echo failString & "megatest output different, see $1 vs $2" % [outputGottenFile, outputExceptedFile] # outputGottenFile, outputExceptedFile not removed on purpose for debugging. quit 1 else: diff --git a/testament/testament.nim b/testament/testament.nim index 9caa3f6b9..0607ac41d 100644 --- a/testament/testament.nim +++ b/testament/testament.nim @@ -17,6 +17,7 @@ from std/sugar import dup import compiler/nodejs import lib/stdtest/testutils from lib/stdtest/specialpaths import splitTestFile +from std/private/gitutils import diffStrings proc trimUnitSep(x: var string) = let L = x.len @@ -307,7 +308,7 @@ proc addResult(r: var TResults, test: TTest, target: TTarget, maybeStyledEcho styleBright, expected, "\n" maybeStyledEcho fgYellow, "Gotten:" maybeStyledEcho styleBright, given, "\n" - + echo diffStrings(expected, given).output if backendLogging and (isAppVeyor or isAzure): let (outcome, msg) = From 76f93877cdb2a2baf28eed6edda5bcc96f4ef404 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Fri, 30 Apr 2021 17:17:29 +0200 Subject: [PATCH 2037/3768] Small parser loop fix (#17904) * Fix loop on error with nim check in semiStmtList * Add test --- compiler/parser.nim | 1 + tests/parser/t15667.nim | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/compiler/parser.nim b/compiler/parser.nim index fd9d06de5..9cddf8e4f 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -556,6 +556,7 @@ proc semiStmtList(p: var Parser, result: PNode) = let a = complexOrSimpleStmt(p) if a.kind == nkEmpty: parMessage(p, errExprExpected, p.tok) + getTok(p) else: result.add a dec p.inSemiStmtList diff --git a/tests/parser/t15667.nim b/tests/parser/t15667.nim index d15178789..fcb862825 100644 --- a/tests/parser/t15667.nim +++ b/tests/parser/t15667.nim @@ -7,6 +7,7 @@ t15667.nim(28, 5) Error: invalid indentation, maybe you forgot a '=' at t15667.n t15667.nim(33, 5) Error: invalid indentation, maybe you forgot a '=' at t15667.nim(31, 25) ? t15667.nim(42, 5) Error: invalid indentation, maybe you forgot a '=' at t15667.nim(38, 12) ? t15667.nim(56, 5) Error: invalid indentation, maybe you forgot a '=' at t15667.nim(55, 13) ? +t15667.nim(61, 48) Error: expression expected, but found ',' ''' """ @@ -16,7 +17,6 @@ t15667.nim(56, 5) Error: invalid indentation, maybe you forgot a '=' at t15667.n - # line 20 block: proc fn1() @@ -56,3 +56,6 @@ block: runnableExamples: discard discard + +# semiStmtList loop issue +proc bar(k:static bool):SomeNumber = (when k: 3, else: 3.0) From 3192995ac97ad0648b6d1e5a5a1a42d403b37e72 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 30 Apr 2021 09:54:47 -0700 Subject: [PATCH 2038/3768] close #16646; `since` now works with bootstrap nim post csources_v1 (#17895) * revive #16627 now that csources_v1 was merged * use dedent in rst.nim, refs https://github.com/nim-lang/Nim/pull/17257#discussion_r589025683 * fix test and improve rendering of a rst warning --- compiler/semtypes.nim | 4 ++-- lib/packages/docutils/rst.nim | 5 ++--- tests/stdlib/trstgen.nim | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index c921703b0..6a59624cd 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -256,8 +256,8 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType = else: result.n.add semConstExpr(c, range[i]) - if (result.n[0].kind in {nkFloatLit..nkFloat64Lit} and classify(result.n[0].floatVal) == fcNan) or - (result.n[1].kind in {nkFloatLit..nkFloat64Lit} and classify(result.n[1].floatVal) == fcNan): + if (result.n[0].kind in {nkFloatLit..nkFloat64Lit} and result.n[0].floatVal.isNaN) or + (result.n[1].kind in {nkFloatLit..nkFloat64Lit} and result.n[1].floatVal.isNaN): localError(c.config, n.info, "NaN is not a valid start or end for a range") if weakLeValue(result.n[0], result.n[1]) == impNo: diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 6ad466716..223e57bc4 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -2250,11 +2250,10 @@ proc parseEnumList(p: var RstParser): PRstNode = let n = p.line + p.tok[j].line let msg = "\n" & """ not enough indentation on line $2 - (should be at column $3 if it's a continuation of enum. list), + (should be at column $3 if it's a continuation of enum. list), or no blank line after line $1 (if it should be the next paragraph), or no escaping \ at the beginning of line $1 - (if lines $1..$2 are a normal paragraph, not enum. list)""". - unindent(8) + (if lines $1..$2 are a normal paragraph, not enum. list)""".dedent let c = p.col + requiredIndent + ColRstOffset rstMessage(p, mwRstStyle, msg % [$(n-1), $n, $c], p.tok[j].line, p.tok[j].col) diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim index 0af6ba566..cbdb23cd6 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -849,7 +849,7 @@ Test1 let output8 = input8.toHtml(warnings = warnings8) check(warnings8[].len == 1) check("input(6, 1) Warning: RST style: \n" & - " not enough indentation on line 6" in warnings8[0]) + "not enough indentation on line 6" in warnings8[0]) doAssert output8 == "Paragraph.
      " & "
    1. stringA
    2. \n
    3. stringB
    4. \n
    \n

    C. string1 string2

    \n" From d32ab61e61a9c8875cda926fb76e12dc8e8b9a23 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 30 Apr 2021 12:40:39 -0700 Subject: [PATCH 2039/3768] fix #17905: hash(closure) was not being tested (#17906) --- lib/pure/hashes.nim | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim index 83b7a8d75..2dab498d7 100644 --- a/lib/pure/hashes.nim +++ b/lib/pure/hashes.nim @@ -257,13 +257,6 @@ when not defined(nimLegacyNoHashRef): # or `proc hash*[T: ref | ptr](x: T): Hash` hash(cast[pointer](x)) -proc hash*[T: proc](x: T): Hash {.inline.} = - ## Efficient hashing of proc vars. Closures are supported too. - when T is "closure": - result = hash((rawProc(x), rawEnv(x))) - else: - result = hash(pointer(x)) - proc hash*(x: float): Hash {.inline.} = ## Efficient hashing of floats. let y = x + 0.0 # for denormalization @@ -516,10 +509,10 @@ proc hashIgnoreCase*(sBuf: string, sPos, ePos: int): Hash = h = h !& ord(c) result = !$h -proc hash*[T: tuple | object](x: T): Hash = +proc hash*[T: tuple | object | proc](x: T): Hash {.inline.} = ## Efficient `hash` overload. - ## `hash` must be defined for each component of `x`. runnableExamples: + # for `tuple|object`, `hash` must be defined for each component of `x`. type Obj = object x: int y: string @@ -530,9 +523,24 @@ proc hash*[T: tuple | object](x: T): Hash = # you can define custom hashes for objects (even if they're generic): proc hash(a: Obj2): Hash = hash((a.x)) assert hash(Obj2[float](x: 520, y: "Nim")) == hash(Obj2[float](x: 520, y: "Nim2")) - for f in fields(x): - result = result !& hash(f) - result = !$result + runnableExamples: + # proc and closure examples + proc fn1() = discard + var a = 0 + proc fn2() = a.inc + assert hash(fn1) != hash(fn2) + const fn1b = fn1 + assert hash(fn1b) == hash(fn1) + let fn2b = fn2 + assert hash(fn2b) == hash(fn2) + when T is "closure": + result = hash((rawProc(x), rawEnv(x))) + elif T is (proc): + result = hash(pointer(x)) + else: + for f in fields(x): + result = result !& hash(f) + result = !$result proc hash*[A](x: openArray[A]): Hash = ## Efficient hashing of arrays and sequences. From d0485e326ab56af367246227dedc088839678b6b Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 30 Apr 2021 13:27:53 -0700 Subject: [PATCH 2040/3768] bring back std/ prefix within compiler and ensure it works in bootstrap + bsd (#17902) * [WIP] bring back std/ prefix within compiler and ensure it works in bootstrap + bsd * refs https://github.com/nim-lang/Nim/pull/16282#discussion_r616846863 * sounds very similar to https://github.com/nim-lang/Nim/pull/14291 * more: vmops * update tools/ci_generate.nim * auto-generate freebsd.yml as well, to avoid duplication with openbsd.yml * cleanup * undo temporary CI removal --- .builds/freebsd.yml | 8 ++++++- .builds/openbsd_0.yml | 7 ++++-- .builds/openbsd_1.yml | 7 ++++-- compiler/ic/dce.nim | 2 +- compiler/vmops.nim | 16 ++++++------- koch.nim | 4 +++- tools/ci_generate.nim | 54 ++++++++++++++++++++++++++++--------------- 7 files changed, 65 insertions(+), 33 deletions(-) diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml index c1fb80253..8f336771a 100644 --- a/.builds/freebsd.yml +++ b/.builds/freebsd.yml @@ -1,3 +1,5 @@ +## DO NO EDIT DIRECTLY! auto-generated by `nim r tools/ci_generate.nim` + # see https://man.sr.ht/builds.sr.ht/compatibility.md#freebsd image: freebsd/latest packages: @@ -8,9 +10,12 @@ packages: - devel/sfml - www/node - devel/gmake + + sources: - https://github.com/nim-lang/Nim environment: + NIM_TESTAMENT_BATCH: "0_1" CC: /usr/bin/clang tasks: - setup: | @@ -20,9 +25,10 @@ tasks: $nim_csources c --skipUserCfg --skipParentCfg koch echo 'export PATH=$HOME/Nim/bin:$PATH' >> $HOME/.buildenv - test: | + set -e cd Nim if ! ./koch runCI; then - nim c -r tools/ci_testresults.nim + nim r tools/ci_testresults.nim exit 1 fi triggers: diff --git a/.builds/openbsd_0.yml b/.builds/openbsd_0.yml index e1d85d2a7..c19cd63c6 100644 --- a/.builds/openbsd_0.yml +++ b/.builds/openbsd_0.yml @@ -1,4 +1,4 @@ -## do not edit directly; auto-generated by `nim r tools/ci_generate.nim` +## DO NO EDIT DIRECTLY! auto-generated by `nim r tools/ci_generate.nim` image: openbsd/latest packages: @@ -10,6 +10,8 @@ packages: - sfml - sdl2 - libffi + + sources: - https://github.com/nim-lang/Nim environment: @@ -20,9 +22,10 @@ tasks: set -e cd Nim . ci/funs.sh && nimBuildCsourcesIfNeeded - $nim_csources c koch + $nim_csources c --skipUserCfg --skipParentCfg koch echo 'export PATH=$HOME/Nim/bin:$PATH' >> $HOME/.buildenv - test: | + set -e cd Nim if ! ./koch runCI; then nim r tools/ci_testresults.nim diff --git a/.builds/openbsd_1.yml b/.builds/openbsd_1.yml index ed2ded056..06f5f2ae5 100644 --- a/.builds/openbsd_1.yml +++ b/.builds/openbsd_1.yml @@ -1,4 +1,4 @@ -## do not edit directly; auto-generated by `nim r tools/ci_generate.nim` +## DO NO EDIT DIRECTLY! auto-generated by `nim r tools/ci_generate.nim` image: openbsd/latest packages: @@ -10,6 +10,8 @@ packages: - sfml - sdl2 - libffi + + sources: - https://github.com/nim-lang/Nim environment: @@ -20,9 +22,10 @@ tasks: set -e cd Nim . ci/funs.sh && nimBuildCsourcesIfNeeded - $nim_csources c koch + $nim_csources c --skipUserCfg --skipParentCfg koch echo 'export PATH=$HOME/Nim/bin:$PATH' >> $HOME/.buildenv - test: | + set -e cd Nim if ! ./koch runCI; then nim r tools/ci_testresults.nim diff --git a/compiler/ic/dce.nim b/compiler/ic/dce.nim index 350b17d1b..0a436a5d1 100644 --- a/compiler/ic/dce.nim +++ b/compiler/ic/dce.nim @@ -9,7 +9,7 @@ ## Dead code elimination (=DCE) for IC. -import intsets, tables +import std/[intsets, tables] import ".." / [ast, options, lineinfos, types] import packed_ast, ic, bitabs diff --git a/compiler/vmops.nim b/compiler/vmops.nim index 1d22750e7..04356fc76 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -9,24 +9,24 @@ # Unfortunately this cannot be a module yet: #import vmdeps, vm -from math import sqrt, ln, log10, log2, exp, round, arccos, arcsin, +from std/math import sqrt, ln, log10, log2, exp, round, arccos, arcsin, arctan, arctan2, cos, cosh, hypot, sinh, sin, tan, tanh, pow, trunc, floor, ceil, `mod`, cbrt, arcsinh, arccosh, arctanh, erf, erfc, gamma, lgamma when declared(math.copySign): - from math import copySign + from std/math import copySign when declared(math.signbit): - from math import signbit + from std/math import signbit -from os import getEnv, existsEnv, dirExists, fileExists, putEnv, walkDir, +from std/os import getEnv, existsEnv, dirExists, fileExists, putEnv, walkDir, getAppFilename, raiseOSError, osLastError -from md5 import getMD5 -from times import cpuTime -from hashes import hash -from osproc import nil +from std/md5 import getMD5 +from std/times import cpuTime +from std/hashes import hash +from std/osproc import nil from sighashes import symBodyDigest diff --git a/koch.nim b/koch.nim index 12f9e1c14..15355ec36 100644 --- a/koch.nim +++ b/koch.nim @@ -534,7 +534,9 @@ proc runCI(cmd: string) = echo "runCI: ", cmd echo hostInfo() # boot without -d:nimHasLibFFI to make sure this still works - kochExecFold("Boot in release mode", "boot -d:release -d:nimStrictMode") + # `--lib:lib` is needed for bootstrap on openbsd, for reasons described in + # https://github.com/nim-lang/Nim/pull/14291 (`getAppFilename` bugsfor older nim on openbsd). + kochExecFold("Boot in release mode", "boot -d:release -d:nimStrictMode --lib:lib") when false: # debugging: when you need to run only 1 test in CI, use something like this: execFold("debugging test", "nim r tests/stdlib/tosproc.nim") diff --git a/tools/ci_generate.nim b/tools/ci_generate.nim index e2580ff6b..6517df34a 100644 --- a/tools/ci_generate.nim +++ b/tools/ci_generate.nim @@ -1,6 +1,6 @@ ##[ avoid code duplication in CI pipelines. -For now, this is only used for openbsd, but there is a lot of other code +For now, this is only used for openbsd + freebsd, but there is a lot of other code duplication that could be removed. ## usage @@ -10,22 +10,14 @@ nim r tools/ci_generate.nim ``` ]## -import std/strformat +import std/[strformat, os] -proc genCIopenbsd(batch: int, num: int): string = +proc genCiBsd(header: string, batch: int, num: int): string = result = fmt""" -## do not edit directly; auto-generated by `nim r tools/ci_generate.nim` +## DO NO EDIT DIRECTLY! auto-generated by `nim r tools/ci_generate.nim` + +{header} -image: openbsd/latest -packages: -- gmake -- sqlite3 -- node -- boehm-gc -- pcre -- sfml -- sdl2 -- libffi sources: - https://github.com/nim-lang/Nim environment: @@ -36,9 +28,10 @@ tasks: set -e cd Nim . ci/funs.sh && nimBuildCsourcesIfNeeded - $nim_csources c koch + $nim_csources c --skipUserCfg --skipParentCfg koch echo 'export PATH=$HOME/Nim/bin:$PATH' >> $HOME/.buildenv - test: | + set -e cd Nim if ! ./koch runCI; then nim r tools/ci_testresults.nim @@ -51,14 +44,39 @@ triggers: """ proc main()= + let dir = ".builds" # not too large to be resource friendly, refs bug #17107 let num = 2 # if you reduce this, make sure to remove files that shouldn't be generated, # or better, do the cleanup logic here e.g.: `rm .builds/openbsd_*` + let headerFreebsd = """ +# see https://man.sr.ht/builds.sr.ht/compatibility.md#freebsd +image: freebsd/latest +packages: +- databases/sqlite3 +- devel/boehm-gc-threaded +- devel/pcre +- devel/sdl20 +- devel/sfml +- www/node +- devel/gmake +""" + + let headerOpenbsd = """ +image: openbsd/latest +packages: +- gmake +- sqlite3 +- node +- boehm-gc +- pcre +- sfml +- sdl2 +- libffi +""" for i in 0.. Date: Fri, 30 Apr 2021 22:39:25 +0200 Subject: [PATCH 2041/3768] Document the difference between toFloat/toInt and type conversion (#17894) --- lib/system.nim | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/system.nim b/lib/system.nim index 9e9bacf39..174630317 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1472,7 +1472,7 @@ type # these work for most platforms: PInt32* = ptr int32 ## An alias for `ptr int32`. proc toFloat*(i: int): float {.noSideEffect, inline.} = - ## Converts an integer `i` into a `float`. + ## Converts an integer `i` into a `float`. Same as `float(i)`. ## ## If the conversion fails, `ValueError` is raised. ## However, on most platforms the conversion cannot fail. @@ -1494,7 +1494,8 @@ proc toInt*(f: float): int {.noSideEffect.} = ## ## Conversion rounds `f` half away from 0, see ## `Round half away from zero - ## `_. + ## `_, + ## as opposed to a type conversion which rounds towards zero. ## ## Note that some floating point numbers (e.g. infinity or even 1e19) ## cannot be accurately converted. From abb8a73134597297b2c14567f7f8d72f6b723d24 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Sat, 1 May 2021 08:20:33 +0300 Subject: [PATCH 2042/3768] Fix nim-lang/nimforum#285 - punctuation after URL (#17908) * Fix nim-lang/nimforum#285 - punctuation after URL * keep only one leaf in a rnStandaloneHyperlink * add more complex URL --- lib/packages/docutils/rst.nim | 28 ++++++++++++-------- tests/stdlib/trst.nim | 50 +++++++++++++++++++++++++++++++++++ 2 files changed, 67 insertions(+), 11 deletions(-) diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 223e57bc4..249c310b9 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -1213,23 +1213,29 @@ proc isUrl(p: RstParser, i: int): bool = p.tok[i+3].kind == tkWord and p.tok[i].symbol in ["http", "https", "ftp", "telnet", "file"] +proc parseUrl(p: var RstParser): PRstNode = + ## https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#standalone-hyperlinks + result = newRstNode(rnStandaloneHyperlink) + var lastIdx = p.idx + while p.tok[lastIdx].kind in {tkWord, tkPunct, tkOther}: + inc lastIdx + dec lastIdx + # standalone URL can not end with punctuation in RST + while lastIdx >= p.idx and p.tok[lastIdx].kind == tkPunct and + p.tok[lastIdx].symbol != "/": + dec lastIdx + var s = "" + for i in p.idx .. lastIdx: s.add p.tok[i].symbol + result.add s + p.idx = lastIdx + 1 + proc parseWordOrRef(p: var RstParser, father: PRstNode) = ## Parses a normal word or may be a reference or URL. if nextTok(p).kind != tkPunct: # <- main path, a normal word father.add newLeaf(p) inc p.idx elif isUrl(p, p.idx): # URL http://something - var n = newRstNode(rnStandaloneHyperlink) - while true: - case currentTok(p).kind - of tkWord, tkAdornment, tkOther: discard - of tkPunct: - if nextTok(p).kind notin {tkWord, tkAdornment, tkOther, tkPunct}: - break - else: break - n.add(newLeaf(p)) - inc p.idx - father.add(n) + father.add parseUrl(p) else: # check for reference (probably, long one like some.ref.with.dots_ ) var saveIdx = p.idx diff --git a/tests/stdlib/trst.nim b/tests/stdlib/trst.nim index fef80dfc7..905e5143a 100644 --- a/tests/stdlib/trst.nim +++ b/tests/stdlib/trst.nim @@ -428,3 +428,53 @@ suite "RST inline markup": rnLeaf 'lnk' rnLeaf '___' """) + + test "no punctuation in the end of a standalone URI is allowed": + check(dedent""" + [see (http://no.org)], end""".toAst == + dedent""" + rnInner + rnLeaf '[' + rnLeaf 'see' + rnLeaf ' ' + rnLeaf '(' + rnStandaloneHyperlink + rnLeaf 'http://no.org' + rnLeaf ')' + rnLeaf ']' + rnLeaf ',' + rnLeaf ' ' + rnLeaf 'end' + """) + + # but `/` at the end is OK + check( + dedent""" + See http://no.org/ end""".toAst == + dedent""" + rnInner + rnLeaf 'See' + rnLeaf ' ' + rnStandaloneHyperlink + rnLeaf 'http://no.org/' + rnLeaf ' ' + rnLeaf 'end' + """) + + # a more complex URL with some made-up ending '&='. + # Github Markdown would include final &= and + # so would rst2html.py in contradiction with RST spec. + check( + dedent""" + See https://www.google.com/url?sa=t&source=web&cd=&cad=rja&url=https%3A%2F%2Fnim-lang.github.io%2FNim%2Frst.html%23features&usg=AO&= end""".toAst == + dedent""" + rnInner + rnLeaf 'See' + rnLeaf ' ' + rnStandaloneHyperlink + rnLeaf 'https://www.google.com/url?sa=t&source=web&cd=&cad=rja&url=https%3A%2F%2Fnim-lang.github.io%2FNim%2Frst.html%23features&usg=AO' + rnLeaf '&' + rnLeaf '=' + rnLeaf ' ' + rnLeaf 'end' + """) From 1f1d85bb9c614d93ce38becdcc421dda00264a75 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 30 Apr 2021 22:24:41 -0700 Subject: [PATCH 2043/3768] reuse config/build_config.txt for all bootstrap scripts (posix + windows + ci); use build_all.bat in 1 CI, fix bug in build_all.bat (#17899) * reuse config/build_config.txt for all bootstrap scripts (posix + windows + ci) * ci_docs: use build_all.bat in CI (just in that pipeline) to ensure it keeps working * fixup * fix pre-existing bug in build_all.bat * fixup * cp => copy /y * auto-generate build_all.bat, build_all.sh * fixup --- .github/workflows/ci_docs.yml | 10 +++++- build_all.bat | 35 ++++++++++++------- build_all.sh | 9 ++--- ci/build_autogen.bat | 26 ++++++++++++++ ci/funs.sh | 8 ++--- config/build_config.txt | 4 +++ tools/ci_generate.nim | 65 ++++++++++++++++++++++++++++++++++- 7 files changed, 134 insertions(+), 23 deletions(-) create mode 100644 ci/build_autogen.bat create mode 100644 config/build_config.txt diff --git a/.github/workflows/ci_docs.yml b/.github/workflows/ci_docs.yml index 1459d6a31..dfcff66e0 100644 --- a/.github/workflows/ci_docs.yml +++ b/.github/workflows/ci_docs.yml @@ -70,13 +70,21 @@ jobs: shell: bash run: echo "${{ github.workspace }}/bin" >> "${GITHUB_PATH}" - - name: 'Build csourcesAny' + - name: 'Build csourcesAny (posix)' + # this would work on windows and other CI use this on windows, + # but we ensure here that `ci/build_autogen.bat` keeps working on windows. + if: runner.os != 'Windows' shell: bash run: . ci/funs.sh && nimBuildCsourcesIfNeeded CC=gcc # was previously using caching via `actions/cache@v1` but this wasn't # used in other CI pipelines and it's unclear the added complexity # was worth the saving; can be revisited if needed. + - name: 'Build csourcesAny (windows)' + if: runner.os == 'Windows' + shell: cmd + run: ci/build_autogen.bat + - name: 'Build koch' shell: bash run: nim c koch diff --git a/build_all.bat b/build_all.bat index 78a7a354a..1bff761b3 100644 --- a/build_all.bat +++ b/build_all.bat @@ -1,20 +1,29 @@ @echo off -rem build development version of the compiler; can be rerun safely -rem TODO: call nimBuildCsourcesIfNeeded or auto-generate this file (from a nim script) -rem to avoid duplication. -if not exist csources_v1 ( - git clone --depth 1 https://github.com/nim-lang/csources_v1.git +rem DO NO EDIT DIRECTLY! auto-generated by `nim r tools/ci_generate.nim` +rem Build development version of the compiler; can be rerun safely +rem bare bones version of ci/funs.sh adapted for windows. + +rem Read in some common shared variables (shared with other tools), +rem see https://stackoverflow.com/questions/3068929/how-to-read-file-contents-into-a-variable-in-a-batch-file +for /f "delims== tokens=1,2" %%G in (config/build_config.txt) do set %%G=%%H +SET nim_csources=bin\nim_csources_%nim_csourcesHash%.exe +echo "building from csources: %nim_csources%" + +if not exist %nim_csourcesDir% ( + git clone -q --depth 1 %nim_csourcesUrl% %nim_csourcesDir% ) -if not exist bin\nim.exe ( - cd csources_v1 - git checkout a8a5241f9475099c823cfe1a5e0ca4022ac201ff - if PROCESSOR_ARCHITECTURE == AMD64 ( + +if not exist %nim_csources% ( + cd %nim_csourcesDir% + git checkout %nim_csourcesHash% + echo "%PROCESSOR_ARCHITECTURE%" + if "%PROCESSOR_ARCHITECTURE%"=="AMD64" ( SET ARCH=64 ) CALL build.bat cd .. + copy /y bin\nim.exe %nim_csources% ) -bin\nim.exe c --skipUserCfg --skipParentCfg koch -koch.exe boot -d:release --skipUserCfg --skipParentCfg -koch.exe tools --skipUserCfg --skipParentCfg - + bin\nim.exe c --skipUserCfg --skipParentCfg --hints:off koch + koch boot -d:release --skipUserCfg --skipParentCfg --hints:off + koch tools --skipUserCfg --skipParentCfg --hints:off diff --git a/build_all.sh b/build_all.sh index 49504c8fe..90665672f 100755 --- a/build_all.sh +++ b/build_all.sh @@ -1,4 +1,5 @@ #! /bin/sh +rem DO NO EDIT DIRECTLY! auto-generated by `nim r tools/ci_generate.nim` # build development version of the compiler; can be rerun safely. # arguments can be passed, e.g.: @@ -10,7 +11,7 @@ set -e # exit on first error . ci/funs.sh nimBuildCsourcesIfNeeded "$@" -# Note: if fails, may need to update csourcesAny manually -echo_run bin/nim c --skipUserCfg --skipParentCfg koch -echo_run ./koch boot -d:release --skipUserCfg --skipParentCfg -echo_run ./koch tools --skipUserCfg --skipParentCfg # Compile Nimble and other tools. +echo_run bin/nim c --skipUserCfg --skipParentCfg --hints:off koch +echo_run ./koch boot -d:release --skipUserCfg --skipParentCfg --hints:off +echo_run ./koch tools --skipUserCfg --skipParentCfg --hints:off + diff --git a/ci/build_autogen.bat b/ci/build_autogen.bat new file mode 100644 index 000000000..8b1113175 --- /dev/null +++ b/ci/build_autogen.bat @@ -0,0 +1,26 @@ +@echo off +rem DO NO EDIT DIRECTLY! auto-generated by `nim r tools/ci_generate.nim` +rem Build development version of the compiler; can be rerun safely +rem bare bones version of ci/funs.sh adapted for windows. + +rem Read in some common shared variables (shared with other tools), +rem see https://stackoverflow.com/questions/3068929/how-to-read-file-contents-into-a-variable-in-a-batch-file +for /f "delims== tokens=1,2" %%G in (config/build_config.txt) do set %%G=%%H +SET nim_csources=bin\nim_csources_%nim_csourcesHash%.exe +echo "building from csources: %nim_csources%" + +if not exist %nim_csourcesDir% ( + git clone -q --depth 1 %nim_csourcesUrl% %nim_csourcesDir% +) + +if not exist %nim_csources% ( + cd %nim_csourcesDir% + git checkout %nim_csourcesHash% + echo "%PROCESSOR_ARCHITECTURE%" + if "%PROCESSOR_ARCHITECTURE%"=="AMD64" ( + SET ARCH=64 + ) + CALL build.bat + cd .. + copy /y bin\nim.exe %nim_csources% +) diff --git a/ci/funs.sh b/ci/funs.sh index 7a6c66722..5158f38d5 100644 --- a/ci/funs.sh +++ b/ci/funs.sh @@ -32,9 +32,7 @@ nimIsCiSkip(){ } nimDefineVars(){ - nim_csourcesDir=csources_v1 # where we clone - nim_csourcesUrl=https://github.com/nim-lang/csources_v1.git - nim_csourcesHash=a8a5241f9475099c823cfe1a5e0ca4022ac201ff + . config/build_config.txt nim_csources=bin/nim_csources_$nim_csourcesHash } @@ -88,9 +86,11 @@ nimBuildCsourcesIfNeeded(){ if test -d "$nim_csourcesDir"; then echo "$nim_csourcesDir exists." else - # depth 1: adjust as needed in case useful for `git bisect` + # Note: using git tags would allow fetching just what's needed, unlike git hashes, e.g. + # via `git clone -q --depth 1 --branch $tag $nim_csourcesUrl`. echo_run git clone -q --depth 1 $nim_csourcesUrl "$nim_csourcesDir" echo_run git -C "$nim_csourcesDir" checkout $nim_csourcesHash + # if needed we could also add: `git reset --hard $nim_csourcesHash` fi _nimBuildCsourcesIfNeeded "$@" fi diff --git a/config/build_config.txt b/config/build_config.txt new file mode 100644 index 000000000..2484d7a52 --- /dev/null +++ b/config/build_config.txt @@ -0,0 +1,4 @@ +nim_comment="key-value pairs for windows/posix bootstrapping build scripts" +nim_csourcesDir=csources_v1 +nim_csourcesUrl=https://github.com/nim-lang/csources_v1.git +nim_csourcesHash=a8a5241f9475099c823cfe1a5e0ca4022ac201ff diff --git a/tools/ci_generate.nim b/tools/ci_generate.nim index 6517df34a..2e6b4b3de 100644 --- a/tools/ci_generate.nim +++ b/tools/ci_generate.nim @@ -12,9 +12,10 @@ nim r tools/ci_generate.nim import std/[strformat, os] +const doNotEdit = "DO NO EDIT DIRECTLY! auto-generated by `nim r tools/ci_generate.nim`" proc genCiBsd(header: string, batch: int, num: int): string = result = fmt""" -## DO NO EDIT DIRECTLY! auto-generated by `nim r tools/ci_generate.nim` +## {doNotEdit} {header} @@ -43,6 +44,64 @@ triggers: to: Andreas Rumpf """ +proc genBuildExtras(echoRun, koch, nim: string): string = + result = fmt""" +{echoRun} {nim} c --skipUserCfg --skipParentCfg --hints:off koch +{echoRun} {koch} boot -d:release --skipUserCfg --skipParentCfg --hints:off +{echoRun} {koch} tools --skipUserCfg --skipParentCfg --hints:off +""" + +proc genWindowsScript(buildAll: bool): string = + result = fmt""" +@echo off +rem {doNotEdit} +rem Build development version of the compiler; can be rerun safely +rem bare bones version of ci/funs.sh adapted for windows. + +rem Read in some common shared variables (shared with other tools), +rem see https://stackoverflow.com/questions/3068929/how-to-read-file-contents-into-a-variable-in-a-batch-file +for /f "delims== tokens=1,2" %%G in (config/build_config.txt) do set %%G=%%H +SET nim_csources=bin\nim_csources_%nim_csourcesHash%.exe +echo "building from csources: %nim_csources%" + +if not exist %nim_csourcesDir% ( + git clone -q --depth 1 %nim_csourcesUrl% %nim_csourcesDir% +) + +if not exist %nim_csources% ( + cd %nim_csourcesDir% + git checkout %nim_csourcesHash% + echo "%PROCESSOR_ARCHITECTURE%" + if "%PROCESSOR_ARCHITECTURE%"=="AMD64" ( + SET ARCH=64 + ) + CALL build.bat + cd .. + copy /y bin\nim.exe %nim_csources% +) +""" + + if buildAll: + result.add genBuildExtras("", "koch", r"bin\nim.exe") + +proc genPosixScript(): string = + result = fmt""" +#! /bin/sh +rem {doNotEdit} + +# build development version of the compiler; can be rerun safely. +# arguments can be passed, e.g.: +# CC=gcc ucpu=amd64 uos=darwin + +set -u # error on undefined variables +set -e # exit on first error + +. ci/funs.sh +nimBuildCsourcesIfNeeded "$@" + +{genBuildExtras("echo_run", "./koch", "bin/nim")} +""" + proc main()= let dir = ".builds" # not too large to be resource friendly, refs bug #17107 @@ -74,9 +133,13 @@ packages: - sdl2 - libffi """ + for i in 0.. Date: Fri, 30 Apr 2021 22:26:52 -0700 Subject: [PATCH 2044/3768] misc fixes: remove `forceConst` (obsolete by static), add more runnableExamples to system (#17896) * misc fixes * add runnableExamples for compileOption * add runnableExamples for runnableExamples * move tconsteval => tconst * cleanup --- lib/std/compilesettings.nim | 2 +- lib/system.nim | 84 ++++++++++++++++++------------------- tests/vm/tconst.nim | 35 ++++++++++++++++ tests/vm/tconsteval.nim | 31 -------------- tests/vm/tvmops.nim | 14 +------ 5 files changed, 78 insertions(+), 88 deletions(-) create mode 100644 tests/vm/tconst.nim delete mode 100644 tests/vm/tconsteval.nim diff --git a/lib/std/compilesettings.nim b/lib/std/compilesettings.nim index 725b68acd..273cfdb57 100644 --- a/lib/std/compilesettings.nim +++ b/lib/std/compilesettings.nim @@ -8,7 +8,7 @@ # ## This module allows querying the compiler about -## diverse configuration settings. +## diverse configuration settings. See also `compileOption`. # Note: Only add new enum values at the end to ensure binary compatibility with # other Nim compiler versions! diff --git a/lib/system.nim b/lib/system.nim index 174630317..c2c3306d4 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -55,35 +55,61 @@ type include "system/basic_types" +proc runnableExamples*(rdoccmd = "", body: untyped) {.magic: "RunnableExamples".} = + ## A section you should use to mark `runnable example`:idx: code with. + ## + ## - In normal debug and release builds code within + ## a `runnableExamples` section is ignored. + ## - The documentation generator is aware of these examples and considers them + ## part of the `##` doc comment. As the last step of documentation + ## generation each runnableExample is put in its own file `$file_examples$i.nim`, + ## compiled and tested. The collected examples are + ## put into their own module to ensure the examples do not refer to + ## non-exported symbols. + runnableExamples: + proc timesTwo*(x: int): int = + ## This proc doubles a number. + runnableExamples: + # at module scope + const exported* = 123 + assert timesTwo(5) == 10 + block: # at block scope + defer: echo "done" + runnableExamples "-d:foo -b:cpp": + import std/compilesettings + assert querySetting(backend) == "cpp" + assert defined(foo) + runnableExamples "-r:off": ## this one is only compiled + import std/browsers + openDefaultBrowser "https://forum.nim-lang.org/" + 2 * x + proc compileOption*(option: string): bool {. - magic: "CompileOption", noSideEffect.} + magic: "CompileOption", noSideEffect.} = ## Can be used to determine an `on|off` compile-time option. ## ## See also: ## * `compileOption <#compileOption,string,string>`_ for enum options ## * `defined <#defined,untyped>`_ ## * `std/compilesettings module `_ - ## - ## Example: - ## - ## .. code-block:: Nim - ## when compileOption("floatchecks"): - ## echo "compiled with floating point NaN and Inf checks" + runnableExamples("--floatChecks:off"): + static: doAssert not compileOption("floatchecks") + {.push floatChecks: on.} + static: doAssert compileOption("floatchecks") + # floating point NaN and Inf checks enabled in this scope + {.pop.} proc compileOption*(option, arg: string): bool {. - magic: "CompileOptionArg", noSideEffect.} + magic: "CompileOptionArg", noSideEffect.} = ## Can be used to determine an enum compile-time option. ## ## See also: ## * `compileOption <#compileOption,string>`_ for `on|off` options ## * `defined <#defined,untyped>`_ ## * `std/compilesettings module `_ - ## - ## Example: - ## - ## .. code-block:: Nim - ## when compileOption("opt", "size") and compileOption("gc", "boehm"): - ## echo "compiled with optimization for size and uses Boehm's GC" + runnableExamples: + when compileOption("opt", "size") and compileOption("gc", "boehm"): + discard "compiled with optimization for size and uses Boehm's GC" {.push warning[GcMem]: off, warning[Uninit]: off.} {.push hints: off.} @@ -139,36 +165,6 @@ else: OrdinalImpl[T] {.magic: Ordinal.} Ordinal* = OrdinalImpl | uint | uint64 -proc runnableExamples*(rdoccmd = "", body: untyped) {.magic: "RunnableExamples".} - ## A section you should use to mark `runnable example`:idx: code with. - ## - ## - In normal debug and release builds code within - ## a `runnableExamples` section is ignored. - ## - The documentation generator is aware of these examples and considers them - ## part of the `##` doc comment. As the last step of documentation - ## generation each runnableExample is put in its own file `$file_examples$i.nim`, - ## compiled and tested. The collected examples are - ## put into their own module to ensure the examples do not refer to - ## non-exported symbols. - ## - ## Usage: - ## - ## .. code-block:: Nim - ## proc double*(x: int): int = - ## ## This proc doubles a number. - ## runnableExamples: - ## ## at module scope - ## assert double(5) == 10 - ## block: ## at block scope - ## defer: echo "done" - ## result = 2 * x - ## runnableExamples "-d:foo -b:cpp": - ## import std/compilesettings - ## doAssert querySetting(backend) == "cpp" - ## runnableExamples "-r:off": ## this one is only compiled - ## import std/browsers - ## openDefaultBrowser "https://forum.nim-lang.org/" - when defined(nimHasDeclaredMagic): proc declared*(x: untyped): bool {.magic: "Declared", noSideEffect, compileTime.} ## Special compile-time procedure that checks whether `x` is diff --git a/tests/vm/tconst.nim b/tests/vm/tconst.nim new file mode 100644 index 000000000..3ebbec043 --- /dev/null +++ b/tests/vm/tconst.nim @@ -0,0 +1,35 @@ +discard """ + targets: "c cpp js" +""" + +import std/strutils + +template forceConst(a: untyped): untyped = + ## Force evaluation at CT, but `static(a)` is simpler + const ret = a + ret + +proc isNimVm(): bool = + when nimvm: result = true + else: result = false + +block: + doAssert forceConst(isNimVm()) + doAssert not isNimVm() + doAssert forceConst(isNimVm()) == static(isNimVm()) + doAssert forceConst(isNimVm()) == isNimVm().static + +template main() = + # xxx merge more const related tests here + block: + const + a = """ + Version $1| + Compiled at: $2, $3 + """ % [NimVersion & spaces(44-len(NimVersion)), CompileDate, CompileTime] + let b = $a + doAssert CompileTime in b + doAssert NimVersion in b + +static: main() +main() diff --git a/tests/vm/tconsteval.nim b/tests/vm/tconsteval.nim deleted file mode 100644 index 2e0fcb888..000000000 --- a/tests/vm/tconsteval.nim +++ /dev/null @@ -1,31 +0,0 @@ -discard """ -action: compile -""" - -import strutils - -const - HelpText = """ -+-----------------------------------------------------------------+ -| Maintenance program for Nim | -| Version $1| -| (c) 2012 Andreas Rumpf | -+-----------------------------------------------------------------+ -Compiled at: $2, $3 - -Usage: - koch [options] command [options for command] -Options: - --force, -f, -B, -b forces rebuild - --help, -h shows this help and quits -Possible Commands: - boot [options] bootstraps with given command line options - clean cleans Nim project; removes generated files - web generates the website - csource [options] builds the C sources for installation - zip builds the installation ZIP package - inno builds the Inno Setup installer -""" % [NimVersion & spaces(44-len(NimVersion)), - CompileDate, CompileTime] - -echo HelpText diff --git a/tests/vm/tvmops.nim b/tests/vm/tvmops.nim index c8febd982..632dcbee7 100644 --- a/tests/vm/tvmops.nim +++ b/tests/vm/tvmops.nim @@ -9,16 +9,6 @@ import os import math import strutils -template forceConst(a: untyped): untyped = - ## Force evaluation at CT, useful for example here: - ## `callFoo(forceConst(getBar1()), getBar2())` - ## instead of: - ## block: - ## const a = getBar1() - ## `callFoo(a, getBar2())` - const ret = a - ret - static: # TODO: add more tests block: #getAppFilename, gorgeEx, gorge @@ -51,6 +41,6 @@ static: block: # Check against bugs like #9176 - doAssert getCurrentCompilerExe() == forceConst(getCurrentCompilerExe()) + doAssert getCurrentCompilerExe() == getCurrentCompilerExe().static if false: #pending #9176 - doAssert gorgeEx("unexistant") == forceConst(gorgeEx("unexistant")) + doAssert gorgeEx("unexistant") == gorgeEx("unexistant").static From 9f75e8abc153bafdc5d3a34f001d20a92d36e220 Mon Sep 17 00:00:00 2001 From: flywind Date: Sat, 1 May 2021 13:27:50 +0800 Subject: [PATCH 2045/3768] [std/base64] uses runnableExamples (#17882) * [std/base64] uses runnableExamples * Update lib/pure/base64.nim Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> --- lib/pure/base64.nim | 62 ++++++++++++++++++++++----------------------- 1 file changed, 30 insertions(+), 32 deletions(-) diff --git a/lib/pure/base64.nim b/lib/pure/base64.nim index d5c1fda7a..daba786f2 100644 --- a/lib/pure/base64.nim +++ b/lib/pure/base64.nim @@ -16,44 +16,42 @@ ## Each Base64 digit represents exactly 6 bits of data. Three 8-bit ## bytes (i.e., a total of 24 bits) can therefore be represented by ## four 6-bit Base64 digits. -## -## Basic usage -## =========== -## + +##[ +# Basic usage ## Encoding data -## ------------- -## -## .. code-block:: Nim -## import std/base64 -## let encoded = encode("Hello World") -## assert encoded == "SGVsbG8gV29ybGQ=" +]## + +runnableExamples: + let encoded = encode("Hello World") + assert encoded == "SGVsbG8gV29ybGQ=" + ## ## Apart from strings you can also encode lists of integers or characters: ## -## .. code-block:: Nim -## import std/base64 -## let encodedInts = encode([1,2,3]) -## assert encodedInts == "AQID" -## let encodedChars = encode(['h','e','y']) -## assert encodedChars == "aGV5" -## -## + +runnableExamples: + let encodedInts = encode([1,2,3]) + assert encodedInts == "AQID" + let encodedChars = encode(['h','e','y']) + assert encodedChars == "aGV5" + +##[ ## Decoding data -## ------------- -## -## .. code-block:: Nim -## import std/base64 -## let decoded = decode("SGVsbG8gV29ybGQ=") -## assert decoded == "Hello World" -## +]## + +runnableExamples: + let decoded = decode("SGVsbG8gV29ybGQ=") + assert decoded == "Hello World" + +##[ ## URL Safe Base64 -## --------------- -## -## .. code-block:: Nim -## import std/base64 -## doAssert encode("c\xf7>", safe = true) == "Y_c-" -## doAssert encode("c\xf7>", safe = false) == "Y/c+" -## +]## + +runnableExamples: + assert encode("c\xf7>", safe = true) == "Y_c-" + assert encode("c\xf7>", safe = false) == "Y/c+" + ## See also ## ======== ## From a55c7e9679e8df0ffc7eaff1e2d5626ec5a9129a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Yanis=20Zafir=C3=B3pulos?= <1265028+drkameleon@users.noreply.github.com> Date: Sat, 1 May 2021 09:37:02 +0200 Subject: [PATCH 2046/3768] WIP: Added missing functions to jsre module (#17881) * added missing functions: `replace`, `replaceAll`, `split`, `match` * added `startsWith` & `endsWith` * Update lib/js/jsre.nim Co-authored-by: Juan Carlos --- lib/js/jsre.nim | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/lib/js/jsre.nim b/lib/js/jsre.nim index 7d51db646..a23fccaa8 100644 --- a/lib/js/jsre.nim +++ b/lib/js/jsre.nim @@ -30,6 +30,15 @@ func newRegExp*(pattern: cstring): RegExp {.importjs: "new RegExp(@)".} func compile*(self: RegExp; pattern: cstring; flags: cstring) {.importjs: "#.compile(@)".} ## Recompiles a regular expression during execution of a script. +func replace*(pattern: cstring; self: RegExp; replacement: cstring): cstring {.importjs: "#.replace(#, #)".} + ## Returns a new string with some or all matches of a pattern replaced by given replacement + +func split*(pattern: cstring; self: RegExp): seq[cstring] {.importjs: "#.split(#)".} + ## Divides a string into an ordered list of substrings and returns the array + +func match*(pattern: cstring; self: RegExp): seq[cstring] {.importjs: "#.match(#)".} + ## Returns an array of matches of a RegExp against given string + func exec*(self: RegExp; pattern: cstring): seq[cstring] {.importjs: "#.exec(#)".} ## Executes a search for a match in its string parameter. @@ -51,6 +60,20 @@ func contains*(pattern: cstring; self: RegExp): bool = assert "xabc".contains jsregex asm "`result` = `self`.test(`pattern`);" +func startsWith*(pattern: cstring; self: RegExp): bool = + ## Tests if string starts with given RegExp + runnableExamples: + let jsregex: RegExp = newRegExp(r"abc", r"i") + assert "abcd".startsWith jsregex + pattern.contains(newRegExp(("^" & $(self.source)).cstring, self.flags)) + +func endsWith*(pattern: cstring; self: RegExp): bool = + ## Tests if string ends with given RegExp + runnableExamples: + let jsregex: RegExp = newRegExp(r"bcd", r"i") + assert "abcd".endsWith jsregex + pattern.contains(newRegExp(($(self.source) & "$").cstring, self.flags)) + runnableExamples: let jsregex: RegExp = newRegExp(r"\s+", r"i") @@ -61,3 +84,10 @@ runnableExamples: jsregex.compile(r"[0-9]", r"i") assert "0123456789abcd".contains jsregex assert $jsregex == "/[0-9]/i" + jsregex.compile(r"abc", r"i") + assert "abcd".startsWith jsregex + assert "dabc".endsWith jsregex + jsregex.compile(r"\d", r"i") + assert "do1ne".split(jsregex) == @["do".cstring, "ne".cstring] + jsregex.compile(r"[lw]", r"i") + assert "hello world".replace(jsregex,"X") == "heXlo world" From fb86271556b4ea4485195febaa02b972149ca088 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 1 May 2021 11:10:40 +0200 Subject: [PATCH 2047/3768] =?UTF-8?q?system.nim=20cleanup=20some=20exporte?= =?UTF-8?q?d=20constants=20which=20should=20never=20have=20be=E2=80=A6=20(?= =?UTF-8?q?#17909)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * system.nim cleanup some exported constants which should never have been exported --- compiler/commands.nim | 5 +---- compiler/options.nim | 2 ++ lib/pure/coro.nim | 2 ++ lib/pure/os.nim | 2 +- lib/system.nim | 22 ++-------------------- lib/system/ansi_c.nim | 2 +- lib/system/coro_detection.nim | 20 ++++++++++++++++++++ lib/system/excpt.nim | 2 +- lib/system/io.nim | 2 +- tests/cpp/tcovariancerules.nim | 2 +- 10 files changed, 32 insertions(+), 29 deletions(-) create mode 100644 lib/system/coro_detection.nim diff --git a/compiler/commands.nim b/compiler/commands.nim index 278199bc0..32f56ad0e 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -34,9 +34,6 @@ from ast import eqTypeFlags, tfGcSafe, tfNoSideEffect # but some have deps to imported modules. Yay. bootSwitch(usedTinyC, hasTinyCBackend, "-d:tinyc") -bootSwitch(usedNativeStacktrace, - defined(nativeStackTrace) and nativeStackTraceSupported, - "-d:nativeStackTrace") bootSwitch(usedFFI, hasFFI, "-d:nimHasLibFFI") type @@ -101,7 +98,7 @@ proc writeVersionInfo(conf: ConfigRef; pass: TCmdLinePass) = msgWriteln(conf, "git hash: " & gitHash, {msgStdout}) msgWriteln(conf, "active boot switches:" & usedRelease & usedDanger & - usedTinyC & useLinenoise & usedNativeStacktrace & + usedTinyC & useLinenoise & usedFFI & usedBoehm & usedMarkAndSweep & usedGoGC & usedNoGC, {msgStdout}) msgQuit(0) diff --git a/compiler/options.nim b/compiler/options.nim index 4773ba284..36e0e64da 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -21,6 +21,8 @@ const hasFFI* = defined(nimHasLibFFI) copyrightYear* = "2021" + nimEnableCovariance* = defined(nimEnableCovariance) + type # please make sure we have under 32 options # (improves code efficiency a lot!) TOption* = enum # **keep binary compatible** diff --git a/lib/pure/coro.nim b/lib/pure/coro.nim index e3ed6aadd..f4495a536 100644 --- a/lib/pure/coro.nim +++ b/lib/pure/coro.nim @@ -21,6 +21,8 @@ ## ## Unstable API. +import system/coro_detection + when not nimCoroutines and not defined(nimdoc): when defined(noNimCoroutines): {.error: "Coroutines can not be used with -d:noNimCoroutines".} diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 50a88b43b..287fbe125 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -1910,7 +1910,7 @@ proc copyFileToDir*(source, dir: string, options = {cfSymlinkFollow}) copyFile(source, dir / source.lastPathPart, options) when not declared(ENOENT) and not defined(windows): - when NoFakeVars: + when defined(nimscript): when not defined(haiku): const ENOENT = cint(2) # 2 on most systems including Solaris else: diff --git a/lib/system.nim b/lib/system.nim index c2c3306d4..e9c036b8a 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -537,7 +537,7 @@ const include "system/inclrtl" -const NoFakeVars* = defined(nimscript) ## `true` if the backend doesn't support \ +const NoFakeVars = defined(nimscript) ## `true` if the backend doesn't support \ ## "fake variables" like `var EBADF {.importc.}: cint`. const notJSnotNims = not defined(js) and not defined(nimscript) @@ -1132,7 +1132,6 @@ const const hasThreadSupport = compileOption("threads") and not defined(nimscript) hasSharedHeap = defined(boehmgc) or defined(gogc) # don't share heaps; every thread has its own - nimEnableCovariance* = defined(nimEnableCovariance) # or true when hasThreadSupport and defined(tcc) and not compileOption("tlsEmulation"): # tcc doesn't support TLS @@ -1920,24 +1919,7 @@ include "system/gc_interface" # we have to compute this here before turning it off in except.nim anyway ... const NimStackTrace = compileOption("stacktrace") -template coroutinesSupportedPlatform(): bool = - when defined(sparc) or defined(ELATE) or defined(boehmgc) or defined(gogc) or - defined(nogc) or defined(gcRegions) or defined(gcMarkAndSweep): - false - else: - true - -when defined(nimCoroutines): - # Explicit opt-in. - when not coroutinesSupportedPlatform(): - {.error: "Coroutines are not supported on this architecture and/or garbage collector.".} - const nimCoroutines* = true -elif defined(noNimCoroutines): - # Explicit opt-out. - const nimCoroutines* = false -else: - # Autodetect coroutine support. - const nimCoroutines* = false +import system/coro_detection {.push checks: off.} # obviously we cannot generate checking operations here :-) diff --git a/lib/system/ansi_c.nim b/lib/system/ansi_c.nim index 7e156eaab..e5f26e070 100644 --- a/lib/system/ansi_c.nim +++ b/lib/system/ansi_c.nim @@ -74,7 +74,7 @@ elif defined(haiku): SIGPIPE* = cint(7) SIG_DFL* = cast[CSighandlerT](0) else: - when NoFakeVars: + when defined(nimscript): {.error: "SIGABRT not ported to your platform".} else: var diff --git a/lib/system/coro_detection.nim b/lib/system/coro_detection.nim new file mode 100644 index 000000000..f6c1b5c15 --- /dev/null +++ b/lib/system/coro_detection.nim @@ -0,0 +1,20 @@ +## Coroutine detection logic + +template coroutinesSupportedPlatform(): bool = + when defined(sparc) or defined(ELATE) or defined(boehmgc) or defined(gogc) or + defined(nogc) or defined(gcRegions) or defined(gcMarkAndSweep): + false + else: + true + +when defined(nimCoroutines): + # Explicit opt-in. + when not coroutinesSupportedPlatform(): + {.error: "Coroutines are not supported on this architecture and/or garbage collector.".} + const nimCoroutines* = true +elif defined(noNimCoroutines): + # Explicit opt-out. + const nimCoroutines* = false +else: + # Autodetect coroutine support. + const nimCoroutines* = false diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index da034e57e..8ba3a1601 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -146,7 +146,7 @@ proc closureIterSetupExc(e: ref Exception) {.compilerproc, inline.} = # some platforms have native support for stack traces: const - nativeStackTraceSupported* = (defined(macosx) or defined(linux)) and + nativeStackTraceSupported = (defined(macosx) or defined(linux)) and not NimStackTrace hasSomeStackTrace = NimStackTrace or defined(nimStackTraceOverride) or (defined(nativeStackTrace) and nativeStackTraceSupported) diff --git a/lib/system/io.nim b/lib/system/io.nim index 300e7ea3f..594c78209 100644 --- a/lib/system/io.nim +++ b/lib/system/io.nim @@ -252,7 +252,7 @@ proc write*(f: File, s: string) {.tags: [WriteIOEffect], benign.} = raiseEIO("cannot write string to file") {.pop.} -when NoFakeVars: +when defined(nimscript): when defined(windows): const IOFBF = cint(0) diff --git a/tests/cpp/tcovariancerules.nim b/tests/cpp/tcovariancerules.nim index 49fe8015b..5ac7d8bac 100644 --- a/tests/cpp/tcovariancerules.nim +++ b/tests/cpp/tcovariancerules.nim @@ -31,7 +31,7 @@ import macros macro skipElse(n: untyped): untyped = n[0] template acceptWithCovariance(x, otherwise): untyped = - when nimEnableCovariance: + when defined nimEnableCovariance: x else: reject(x) From ee6d56141c11cf9273d3671eb9dd54097361cc1b Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 1 May 2021 02:12:13 -0700 Subject: [PATCH 2048/3768] fix #17911 rawProc for cpp (#17912) --- lib/pure/hashes.nim | 18 ++++++++++------ lib/system.nim | 13 ++++++++---- tests/stdlib/thashes.nim | 30 ++++++++++++++++++++++---- tests/stdlib/tsystem.nim | 46 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 93 insertions(+), 14 deletions(-) create mode 100644 tests/stdlib/tsystem.nim diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim index 2dab498d7..f61fbdf2b 100644 --- a/lib/pure/hashes.nim +++ b/lib/pure/hashes.nim @@ -524,15 +524,21 @@ proc hash*[T: tuple | object | proc](x: T): Hash {.inline.} = proc hash(a: Obj2): Hash = hash((a.x)) assert hash(Obj2[float](x: 520, y: "Nim")) == hash(Obj2[float](x: 520, y: "Nim2")) runnableExamples: - # proc and closure examples + # proc proc fn1() = discard - var a = 0 - proc fn2() = a.inc - assert hash(fn1) != hash(fn2) const fn1b = fn1 assert hash(fn1b) == hash(fn1) - let fn2b = fn2 - assert hash(fn2b) == hash(fn2) + + # closure + proc outer = + var a = 0 + proc fn2() = a.inc + assert fn2 is "closure" + let fn2b = fn2 + assert hash(fn2b) == hash(fn2) + assert hash(fn2) != hash(fn1) + outer() + when T is "closure": result = hash((rawProc(x), rawEnv(x))) elif T is (proc): diff --git a/lib/system.nim b/lib/system.nim index e9c036b8a..aecda4a70 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2414,17 +2414,22 @@ when notJSnotNims: proc rawProc*[T: proc](x: T): pointer {.noSideEffect, inline.} = ## Retrieves the raw proc pointer of the closure `x`. This is - ## useful for interfacing closures with C. + ## useful for interfacing closures with C/C++, hash compuations, etc. when T is "closure": + #[ + The conversion from function pointer to `void*` is a tricky topic, but this + should work at least for c++ >= c++11, e.g. for `dlsym` support. + refs: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57869, + https://stackoverflow.com/questions/14125474/casts-between-pointer-to-function-and-pointer-to-object-in-c-and-c + ]# {.emit: """ - `result` = `x`.ClP_0; + `result` = (void*)`x`.ClP_0; """.} else: {.error: "Only closure function and iterator are allowed!".} proc rawEnv*[T: proc](x: T): pointer {.noSideEffect, inline.} = - ## Retrieves the raw environment pointer of the closure `x`. This is - ## useful for interfacing closures with C. + ## Retrieves the raw environment pointer of the closure `x`. See also `rawProc`. when T is "closure": {.emit: """ `result` = `x`.ClE_0; diff --git a/tests/stdlib/thashes.nim b/tests/stdlib/thashes.nim index 66857d3ca..46576ef12 100644 --- a/tests/stdlib/thashes.nim +++ b/tests/stdlib/thashes.nim @@ -184,8 +184,8 @@ proc main() = # pending fix proposed in https://github.com/nim-lang/Nim/issues/15952#issuecomment-786312417 discard do: - assert a[0].addr.hash != a[1].addr.hash - assert cast[pointer](a[0].addr).hash == a[0].addr.hash + doAssert a[0].addr.hash != a[1].addr.hash + doAssert cast[pointer](a[0].addr).hash == a[0].addr.hash block: # hash(ref) type A = ref object @@ -193,9 +193,31 @@ proc main() = let a = A(x: 3) disableVm: # xxx Error: VM does not support 'cast' from tyRef to tyPointer let ha = a.hash - assert ha != A(x: 3).hash # A(x: 3) is a different ref object from `a`. + doAssert ha != A(x: 3).hash # A(x: 3) is a different ref object from `a`. a.x = 4 - assert ha == a.hash # the hash only depends on the address + doAssert ha == a.hash # the hash only depends on the address + + block: # hash(proc) + proc fn(a: int): auto = a*2 + doAssert fn isnot "closure" + doAssert fn is (proc) + const fn2 = fn + let fn3 = fn + whenVMorJs: discard + do: + doAssert hash(fn2) == hash(fn) + doAssert hash(fn3) == hash(fn) + + block: # hash(closure) + proc outer() = + var a = 0 + proc inner() = a.inc + doAssert inner is "closure" + let inner2 = inner + whenVMorJs: discard + do: + doAssert hash(inner2) == hash(inner) + outer() static: main() main() diff --git a/tests/stdlib/tsystem.nim b/tests/stdlib/tsystem.nim new file mode 100644 index 000000000..f8b172b44 --- /dev/null +++ b/tests/stdlib/tsystem.nim @@ -0,0 +1,46 @@ +discard """ + targets: "c cpp js" +""" + +# TODO: in future work move existing `system` tests here, where they belong + +import stdtest/testutils + +template main = + block: # closure + proc outer() = + var a = 0 + proc inner1 = a.inc + proc inner2 = discard + doAssert inner1 is "closure" + doAssert inner2 isnot "closure" + doAssert inner1 is (proc) + doAssert inner2 is (proc) + let inner1b = inner1 + doAssert inner1b is "closure" + doAssert inner1b == inner1 + outer() + + block: # rawProc, rawProc, bug #17911 + proc outer() = + var a = 0 + var b = 0 + proc inner1() = a.inc + proc inner2() = a += 2 + proc inner3() = b.inc + let inner1b = inner1 + doAssert inner2 != inner1 + doAssert inner3 != inner1 + whenVMorJs: discard + do: + doAssert rawProc(inner1b) == rawProc(inner1) + doAssert rawProc(inner2) != rawProc(inner1) + doAssert rawProc(inner3) != rawProc(inner1) + + doAssert rawEnv(inner1b) == rawEnv(inner1) + doAssert rawEnv(inner2) == rawEnv(inner1) # because both use `a` + # doAssert rawEnv(inner3) != rawEnv(inner1) # because `a` vs `b` # this doesn't hold + outer() + +static: main() +main() From 13b57524d35a18df57bec2a7d6a2faa7ce712e9f Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 1 May 2021 10:43:20 -0700 Subject: [PATCH 2049/3768] ref #17913; fix typo in tools/ci_generate.nim generating build_all.sh (#17915) --- build_all.sh | 2 +- tools/ci_generate.nim | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build_all.sh b/build_all.sh index 90665672f..37374aa7c 100755 --- a/build_all.sh +++ b/build_all.sh @@ -1,5 +1,5 @@ #! /bin/sh -rem DO NO EDIT DIRECTLY! auto-generated by `nim r tools/ci_generate.nim` +# DO NO EDIT DIRECTLY! auto-generated by `nim r tools/ci_generate.nim` # build development version of the compiler; can be rerun safely. # arguments can be passed, e.g.: diff --git a/tools/ci_generate.nim b/tools/ci_generate.nim index 2e6b4b3de..f08cd5409 100644 --- a/tools/ci_generate.nim +++ b/tools/ci_generate.nim @@ -87,7 +87,7 @@ if not exist %nim_csources% ( proc genPosixScript(): string = result = fmt""" #! /bin/sh -rem {doNotEdit} +# {doNotEdit} # build development version of the compiler; can be rerun safely. # arguments can be passed, e.g.: From 78e2d299dffc9508430778fcff1dcaee2c8d39fd Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 1 May 2021 15:26:41 -0700 Subject: [PATCH 2050/3768] typo: nonexistant => nonexistent (#17918) * typo: nonexistant => nonexistent * fix test (ordering differs because of https://github.com/nim-lang/Nim/issues/17910) --- lib/pure/osproc.nim | 2 +- lib/pure/sugar.nim | 2 +- nimdoc/testproject/expected/testproject.html | 14 +++++++------- nimdoc/testproject/expected/testproject.idx | 2 +- nimdoc/testproject/expected/theindex.html | 4 ++-- nimdoc/testproject/testproject.nim | 2 +- tests/generics/t13525.nim | 8 ++++---- tests/importalls/mt6.nim | 2 +- tests/lexer/tcustom_numeric_literals.nim | 4 ++-- tests/nimdoc/trunnableexamples.nim | 4 ++-- tests/stdlib/tos.nim | 8 ++++---- tests/stdlib/tosproc.nim | 4 ++-- tests/stdlib/trstgen.nim | 4 ++-- tests/typerel/tvoid.nim | 2 +- tests/types/titerable.nim | 10 +++++----- tests/vm/tvmops.nim | 4 ++-- 16 files changed, 38 insertions(+), 38 deletions(-) diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 6aefb8d6c..1ac37661b 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -1595,7 +1595,7 @@ proc execCmdEx*(command: string, options: set[ProcessOption] = { ## import std/[strutils, strtabs] ## stripLineEnd(result[0]) ## portable way to remove trailing newline, if any ## doAssert result == ("12", 0) - ## doAssert execCmdEx("ls --nonexistant").exitCode != 0 + ## doAssert execCmdEx("ls --nonexistent").exitCode != 0 ## when defined(posix): ## assert execCmdEx("echo $FO", env = newStringTable({"FO": "B"})) == ("B\n", 0) ## assert execCmdEx("echo $PWD", workingDir = "/") == ("/\n", 0) diff --git a/lib/pure/sugar.nim b/lib/pure/sugar.nim index 9abef55a2..7cfa932e6 100644 --- a/lib/pure/sugar.nim +++ b/lib/pure/sugar.nim @@ -186,7 +186,7 @@ macro dumpToString*(x: untyped): string = assert dumpToString(a + x) == "a + x: 1 + x = 11" template square(x): untyped = x * x assert dumpToString(square(x)) == "square(x): x * x = 100" - assert not compiles dumpToString(1 + nonexistant) + assert not compiles dumpToString(1 + nonexistent) import std/strutils assert "failedAssertImpl" in dumpToString(assert true) # example with a statement result = newCall(bindSym"dumpToStringImpl") diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html index 1ea9e172a..70c5b695a 100644 --- a/nimdoc/testproject/expected/testproject.html +++ b/nimdoc/testproject/expected/testproject.html @@ -210,6 +210,11 @@ window.addEventListener('DOMContentLoaded', main);
  • z11()
  • + + -
      asyncFun1
    • - -
      proc c_nonexistant(frmt: cstring): cint {.importc: "nonexistant",
      +
      +
      proc c_nonexistent(frmt: cstring): cint {.importc: "nonexistent",
           header: "<stdio.h>", varargs, discardable.}
      diff --git a/nimdoc/testproject/expected/testproject.idx b/nimdoc/testproject/expected/testproject.idx index 1d8be99da..a8cdc3be2 100644 --- a/nimdoc/testproject/expected/testproject.idx +++ b/nimdoc/testproject/expected/testproject.idx @@ -35,7 +35,7 @@ z17 testproject.html#z17 testproject: z17() p1 testproject.html#p1 testproject: p1() addfBug14485 testproject.html#addfBug14485 testproject: addfBug14485() c_printf testproject.html#c_printf,cstring testproject: c_printf(frmt: cstring): cint -c_nonexistant testproject.html#c_nonexistant,cstring testproject: c_nonexistant(frmt: cstring): cint +c_nonexistent testproject.html#c_nonexistent,cstring testproject: c_nonexistent(frmt: cstring): cint low testproject.html#low,T testproject: low[T: Ordinal | enum | range](x: T): T low2 testproject.html#low2,T testproject: low2[T: Ordinal | enum | range](x: T): T tripleStrLitTest testproject.html#tripleStrLitTest testproject: tripleStrLitTest() diff --git a/nimdoc/testproject/expected/theindex.html b/nimdoc/testproject/expected/theindex.html index 81370d115..e6efee986 100644 --- a/nimdoc/testproject/expected/theindex.html +++ b/nimdoc/testproject/expected/theindex.html @@ -142,9 +142,9 @@ window.addEventListener('DOMContentLoaded', main);
    • Shapes.Circle
    -
    c_nonexistant:
    @@ -597,7 +597,7 @@ cz1 cz2

    Example:

    -
    discard "in cz2"
    +
    discard "in cz2"
    @@ -648,7 +648,7 @@ cz8

    Example:

    -
    doAssert 1 + 1 == 2
    +
    doAssert 1 + 1 == 2
    @@ -657,7 +657,7 @@ cz8

    Example: cmd: -d:foobar

    -
    discard 1
    cz10 +
    discard 1
    cz10 @@ -666,7 +666,7 @@ cz8

    Example:

    -
    discard 1
    +
    discard 1
    @@ -675,7 +675,7 @@ cz8

    Example:

    -
    discard 1
    +
    discard 1
    @@ -700,7 +700,7 @@ cz13 cz17 rest

    Example:

    -
    discard 1
    rest +
    discard 1
    rest @@ -709,21 +709,21 @@ cz17 rest cp1

    Example:

    -
    doAssert 1 == 1 # regular comments work here
    c4 +
    doAssert 1 == 1 # regular comments work here
    c4

    Example:

    -
    # c5 regular comments before 1st token work
    -# regular comment
    -#[
    +
    # c5 regular comments before 1st token work
    +# regular comment
    +#[
     nested regular comment
    -]#
    -doAssert 2 == 2 # c8
    -## this is a non-nested doc comment
    +]#
    +doAssert 2 == 2 # c8
    +## this is a non-nested doc comment
     
    -##[
    +##[
     this is a nested doc comment
    -]##
    -discard "c9"
    -# also work after
    +]##
    +discard "c9" +# also work after
    @@ -732,9 +732,9 @@ this is a nested doc comment Some proc

    Example:

    -
    discard "foo() = " & $[1]
    -#[
    -0: let's also add some broken html to make sure this won't break in future
    +
    discard "foo() = " & $[1]
    +#[
    +0: let's also add some broken html to make sure this won't break in future
     1: </span>
     2: </span>
     3: </span
    @@ -782,7 +782,7 @@ the c printf. etc.
     
     
    low2(2) # => -9223372036854775808

    Example:

    -
    discard "in low2"
    +
    discard "in low2"
    @@ -791,41 +791,41 @@ the c printf. etc.

    Example:

    -
    ## mullitline string litterals are tricky as their indentation can span
    -## below that of the runnableExamples
    -let s1a = """
    +
    ## mullitline string litterals are tricky as their indentation can span
    +## below that of the runnableExamples
    +let s1a = """
     should appear at indent 0
       at indent 2
     at indent 0
    -"""
    -# make sure this works too
    -let s1b = """start at same line
    +"""
    +# make sure this works too
    +let s1b = """start at same line
       at indent 2
     at indent 0
    -""" # comment after
    -let s2 = """sandwich """
    -let s3 = """"""
    -when false:
    -  let s5 = """
    -        in s5 """
    +""" # comment after
    +let s2 = """sandwich """
    +let s3 = """"""
    +when false:
    +  let s5 = """
    +        in s5 """
     
    -let s3b = ["""
    +let s3b = ["""
     %!? #[...] # inside a multiline ...
    -""", "foo"]
    +""", "foo"]
     
    -## make sure handles trailing spaces
    -let s4 = """ 
    -"""
    +## make sure handles trailing spaces
    +let s4 = """ 
    +"""
     
    -let s5 = """ x
    -"""
    -let s6 = """ ""
    -"""
    -let s7 = """"""""""
    -let s8 = ["""""""""", """
    -  """ ]
    -discard
    -# should be in
    +let s5 = """ x +""" +let s6 = """ "" +""" +let s7 = """""""""" +let s8 = ["""""""""", """ + """ ] +discard +# should be in
    @@ -896,9 +896,9 @@ foo3

    Example:

    -
    # ok1
    -assert 1 == 1
    -# ok2
    +
    # ok1
    +assert 1 == 1
    +# ok2
    @@ -914,7 +914,7 @@ foo1 foo2

    Example:

    -
    discard # bar
    +
    discard # bar
    @@ -935,9 +935,9 @@ foo2

    Example:

    -
    discard 1
    cz16 after +
    discard 1
    cz16 after

    Example:

    -
    doAssert 2 == 1 + 1
    +
    doAssert 2 == 1 + 1
    @@ -979,20 +979,20 @@ This does nothing

    Example:

    -
    import std/strutils
    -## issue #8871 preserve formatting
    -## line doc comment
    -# bar
    -doAssert "'foo" == "'foo"
    -##[
    +
    import std/strutils
    +## issue #8871 preserve formatting
    +## line doc comment
    +# bar
    +doAssert "'foo" == "'foo"
    +##[
     foo
     bar
    -]##
    +]##
     
    -doAssert: not "foo".startsWith "ba"
    -block:
    -  discard 0xff # elu par cette crapule
    -# should be in
    should be still in +doAssert: not "foo".startsWith "ba" +block: + discard 0xff # elu par cette crapule +# should be in
    should be still in @@ -1012,13 +1012,13 @@ cz15

    Example:

    discard

    Example:

    -
    discard 3
    +
    discard 3

    Example:

    -
    discard 4
    ok5 ok5b +
    discard 4
    ok5 ok5b

    Example:

    -
    assert true
    +
    assert true

    Example:

    -
    discard 1
    in or out? +
    discard 1
    in or out? @@ -1027,7 +1027,7 @@ cz15

    Example:

    -
    discard 2
    +
    discard 2
    From 2ce592a209f4cd7f59c009a01e4ac4037e63a7bd Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 14 May 2021 15:33:17 -0700 Subject: [PATCH 2104/3768] refs #18011 disable some newly failing tests on cpp windows; refs #17946 increase timeout for tchannels (#18012) --- lib/system.nim | 8 ++++---- tests/cpp/temitlist.nim | 4 +++- tests/cpp/tempty_generic_obj.nim | 4 +++- tests/exception/tcpp_imported_exc.nim | 4 +++- tests/stdlib/tchannels.nim | 2 +- 5 files changed, 14 insertions(+), 8 deletions(-) diff --git a/lib/system.nim b/lib/system.nim index 9317c3440..be30665dd 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1317,10 +1317,10 @@ proc del*[T](x: var seq[T], i: Natural) {.noSideEffect.} = ## ## See also: ## * `delete <#delete,seq[T],Natural>`_ for preserving the order - ## - ## .. code-block:: Nim - ## var i = @[1, 2, 3, 4, 5] - ## i.del(2) # => @[1, 2, 5, 4] + runnableExamples: + var a = @[10, 11, 12, 13, 14] + a.del(2) + assert a == @[10, 11, 14, 13] let xl = x.len - 1 movingCopy(x[i], x[xl]) setLen(x, xl) diff --git a/tests/cpp/temitlist.nim b/tests/cpp/temitlist.nim index 852537518..d446176fa 100644 --- a/tests/cpp/temitlist.nim +++ b/tests/cpp/temitlist.nim @@ -1,7 +1,9 @@ discard """ targets: "cpp" - output: '''6.0 + output: ''' +6.0 0''' +disabled: "windows" # pending bug #18011 """ # bug #4730 diff --git a/tests/cpp/tempty_generic_obj.nim b/tests/cpp/tempty_generic_obj.nim index 913c1ec3c..6125190b4 100644 --- a/tests/cpp/tempty_generic_obj.nim +++ b/tests/cpp/tempty_generic_obj.nim @@ -1,7 +1,9 @@ discard """ targets: "cpp" - output: '''int + output: ''' +int float''' +disabled: "windows" # pending bug #18011 """ import typetraits diff --git a/tests/exception/tcpp_imported_exc.nim b/tests/exception/tcpp_imported_exc.nim index 8b96ca635..5195b48e7 100644 --- a/tests/exception/tcpp_imported_exc.nim +++ b/tests/exception/tcpp_imported_exc.nim @@ -1,6 +1,7 @@ discard """ targets: "cpp" -output: '''caught as std::exception +output: ''' +caught as std::exception expected finally1 finally2 @@ -12,6 +13,7 @@ finally 2 expected cpp exception caught ''' +disabled: "windows" # pending bug #18011 """ type diff --git a/tests/stdlib/tchannels.nim b/tests/stdlib/tchannels.nim index d609eec78..492faf500 100644 --- a/tests/stdlib/tchannels.nim +++ b/tests/stdlib/tchannels.nim @@ -1,5 +1,5 @@ discard """ - timeout: 20.0 # but typically < 1s (in isolation but other tests running in parallel can affect this since based on epochTime) + timeout: 60.0 # but typically < 1s (in isolation but other tests running in parallel can affect this since based on epochTime) disabled: "freebsd" matrix: "--gc:arc --threads:on; --gc:arc --threads:on -d:danger" """ From 4857c462d5a9dc68519ab3c6996523bdcccb883f Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 15 May 2021 06:50:39 +0200 Subject: [PATCH 2105/3768] Revert "fix #14873 properly by skipping `abi` field in importc type (#17944)" (#17992) This reverts commit 98c29c01eb91a0c6ce7da09380a272eebe6bca6f. --- lib/core/locks.nim | 5 +++++ lib/system/syslocks.nim | 8 ++++++++ tests/stdlib/uselocks.nim | 18 +----------------- 3 files changed, 14 insertions(+), 17 deletions(-) diff --git a/lib/core/locks.nim b/lib/core/locks.nim index baec45829..92967b9db 100644 --- a/lib/core/locks.nim +++ b/lib/core/locks.nim @@ -28,6 +28,11 @@ type {.push stackTrace: off.} + +proc `$`*(lock: Lock): string = + # workaround bug #14873 + result = "()" + proc initLock*(lock: var Lock) {.inline.} = ## Initializes the given lock. when not defined(js): diff --git a/lib/system/syslocks.nim b/lib/system/syslocks.nim index 9e8da610d..2f0c8b0ba 100644 --- a/lib/system/syslocks.nim +++ b/lib/system/syslocks.nim @@ -102,18 +102,26 @@ else: SysLockObj {.importc: "pthread_mutex_t", pure, final, header: """#include #include """.} = object + when defined(linux) and defined(amd64): + abi: array[40 div sizeof(clong), clong] SysLockAttr {.importc: "pthread_mutexattr_t", pure, final header: """#include #include """.} = object + when defined(linux) and defined(amd64): + abi: array[4 div sizeof(cint), cint] # actually a cint SysCondObj {.importc: "pthread_cond_t", pure, final, header: """#include #include """.} = object + when defined(linux) and defined(amd64): + abi: array[48 div sizeof(clonglong), clonglong] SysCondAttr {.importc: "pthread_condattr_t", pure, final header: """#include #include """.} = object + when defined(linux) and defined(amd64): + abi: array[4 div sizeof(cint), cint] # actually a cint SysLockType = distinct cint diff --git a/tests/stdlib/uselocks.nim b/tests/stdlib/uselocks.nim index e82de8adf..e9d23f9d9 100644 --- a/tests/stdlib/uselocks.nim +++ b/tests/stdlib/uselocks.nim @@ -11,21 +11,5 @@ proc use* (m: var MyType): int = result = 3 block: - # bug #14873 var l: Lock - doAssert ($l).len > 0 - # on posix, "()", on windows, something else, but that shouldn't be part of the spec - # what matters is that `$` doesn't cause the codegen bug mentioned - -when true: # intentional - # bug https://github.com/nim-lang/Nim/issues/14873#issuecomment-784241605 - type - Test = object - path: string # Removing this makes both cases work. - lock: Lock - # A: This is not fine. - var a = Test() - proc main(): void = - # B: This is fine. - var b = Test() - main() + doAssert $l == "()" From 1568ae23c66c572891207f2c1d330d246b3144ac Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Sat, 15 May 2021 07:53:38 +0300 Subject: [PATCH 2106/3768] docgen: escape special characters in titles (#18014) --- compiler/docgen.nim | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 23e6ba10d..e07db9188 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -1217,10 +1217,11 @@ proc genOutFile(d: PDoc, groupedToc = false): string = else: # Modules get an automatic title for the HTML, but no entry in the index. title = canonicalImport(d.conf, AbsoluteFile d.filename) + title = esc(d.target, title) var subtitle = "" if d.meta[metaSubtitle] != "": dispA(d.conf, subtitle, "

    $1

    ", - "\\\\\\vspace{0.5em}\\large $1", [d.meta[metaSubtitle]]) + "\\\\\\vspace{0.5em}\\large $1", [esc(d.target, d.meta[metaSubtitle])]) var groupsection = getConfigVar(d.conf, "doc.body_toc_groupsection") let bodyname = if d.hasToc and not d.isPureRst and not d.conf.isLatexCmd: @@ -1247,7 +1248,7 @@ proc genOutFile(d: PDoc, groupedToc = false): string = "title", title, "subtitle", subtitle, "tableofcontents", toc, "moduledesc", d.modDesc, "date", getDateStr(), "time", getClockStr(), "content", content, "author", d.meta[metaAuthor], - "version", d.meta[metaVersion], "analytics", d.analytics, + "version", esc(d.target, d.meta[metaVersion]), "analytics", d.analytics, "deprecationMsg", d.modDeprecationMsg] else: code = content From 3824fd3f9a303bc885a6f1f2eb7aa28fb5460cb7 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Sat, 15 May 2021 09:12:26 +0300 Subject: [PATCH 2107/3768] RST opt.list to have priority over def.list (#17845) --- lib/packages/docutils/rst.nim | 4 ++-- tests/stdlib/trst.nim | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 2 deletions(-) diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 181929c09..9cbc4efb7 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -1867,10 +1867,10 @@ proc whichSection(p: RstParser): RstNodeKind = elif match(p, p.idx, "(e) ") or match(p, p.idx, "e) ") or match(p, p.idx, "e. "): result = rnEnumList - elif isDefList(p): - result = rnDefList elif isOptionList(p): result = rnOptionList + elif isDefList(p): + result = rnDefList else: result = rnParagraph of tkWord, tkOther, tkWhite: diff --git a/tests/stdlib/trst.nim b/tests/stdlib/trst.nim index 905e5143a..e6c5d9700 100644 --- a/tests/stdlib/trst.nim +++ b/tests/stdlib/trst.nim @@ -1,6 +1,8 @@ discard """ output: ''' +[Suite] RST parsing + [Suite] RST indentation [Suite] RST include directive @@ -55,6 +57,30 @@ proc toAst(input: string, except EParseError: discard +suite "RST parsing": + test "option list has priority over definition list": + check(dedent""" + --defusages + file + -o set + """.toAst == + dedent""" + rnOptionList + rnOptionListItem order=1 + rnOptionGroup + rnLeaf '--' + rnLeaf 'defusages' + rnDescription + rnInner + rnLeaf 'file' + rnOptionListItem order=2 + rnOptionGroup + rnLeaf '-' + rnLeaf 'o' + rnDescription + rnLeaf 'set' + """) + suite "RST indentation": test "nested bullet lists": let input = dedent """ From 99788ee50426946d954e7311f567414b39f7b8d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20T=2E=20Jarl=C3=B8v?= Date: Sat, 15 May 2021 21:26:15 +0200 Subject: [PATCH 2108/3768] Escape `%00` / `\0` in `dbQuote` (#18015) [backport:1.4] Fix https://github.com/nim-lang/Nim/issues/17925 --- lib/impure/db_postgres.nim | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/impure/db_postgres.nim b/lib/impure/db_postgres.nim index cb9e5414a..4edeac2fc 100644 --- a/lib/impure/db_postgres.nim +++ b/lib/impure/db_postgres.nim @@ -110,7 +110,9 @@ proc dbQuote*(s: string): string = ## DB quotes the string. result = "'" for c in items(s): - if c == '\'': add(result, "''") + case c + of '\'': add(result, "''") + of '\0': add(result, "\\0") else: add(result, c) add(result, '\'') From 36145236644085c636c76500a189e3afda40c13f Mon Sep 17 00:00:00 2001 From: Clyybber Date: Sun, 16 May 2021 00:15:53 +0200 Subject: [PATCH 2109/3768] Rework DFA traversal (#18016) * enable using dbg: without a context * Optimally joining first write/last read analysis * Add test for #18002 * potLastReads -> potentialLastReads --- compiler/injectdestructors.nim | 208 ++++++++++++++++----------------- tests/arc/tmovebug.nim | 34 ++++++ 2 files changed, 133 insertions(+), 109 deletions(-) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 152efd8a1..48a852d26 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -46,6 +46,13 @@ type sinkArg const toDebug {.strdefine.} = "" +when toDebug.len > 0: + var shouldDebug = false + +template dbg(body) = + when toDebug.len > 0: + if shouldDebug: + body proc hasDestructor(c: Con; t: PType): bool {.inline.} = result = ast.hasDestructor(t) @@ -54,11 +61,6 @@ proc hasDestructor(c: Con; t: PType): bool {.inline.} = if not result and c.graph.config.selectedGC in {gcArc, gcOrc}: assert(not containsGarbageCollectedRef(t)) -template dbg(body) = - when toDebug.len > 0: - if c.owner.name.s == toDebug or toDebug == "always": - body - proc getTemp(c: var Con; s: var Scope; typ: PType; info: TLineInfo): PNode = let sym = newSym(skTemp, getIdent(c.graph.cache, ":tmpD"), nextSymId c.idgen, c.owner, info) sym.typ = typ @@ -75,106 +77,111 @@ import sets, hashes, tables proc hash(n: PNode): Hash = hash(cast[pointer](n)) -type AliasCache = Table[(PNode, PNode), AliasKind] -proc aliasesCached(cache: var AliasCache, obj, field: PNode): AliasKind = +proc aliasesCached(cache: var Table[(PNode, PNode), AliasKind], obj, field: PNode): AliasKind = let key = (obj, field) if not cache.hasKey(key): cache[key] = aliases(obj, field) cache[key] -proc collectLastReads(cfg: ControlFlowGraph; cache: var AliasCache, lastReads, potLastReads: var IntSet; pc: var int, until: int) = - template aliasesCached(obj, field: PNode): untyped = +type + State = ref object + lastReads: IntSet + potentialLastReads: IntSet + notLastReads: IntSet + alreadySeen: HashSet[PNode] + +proc preprocessCfg(cfg: var ControlFlowGraph) = + for i in 0.. cfg.len: + cfg[i].dest = cfg.len - i + +proc mergeStates(a: var State, b: sink State) = + # Inplace for performance: + # lastReads = a.lastReads + b.lastReads + # potentialLastReads = (a.potentialLastReads + b.potentialLastReads) - (a.notLastReads + b.notLastReads) + # notLastReads = a.notLastReads + b.notLastReads + # alreadySeen = a.alreadySeen + b.alreadySeen + # b is never nil + if a == nil: + a = b + else: + a.lastReads.incl b.lastReads + a.potentialLastReads.incl b.potentialLastReads + a.potentialLastReads.excl a.notLastReads + a.potentialLastReads.excl b.notLastReads + a.notLastReads.incl b.notLastReads + a.alreadySeen.incl b.alreadySeen + +proc computeLastReadsAndFirstWrites(cfg: ControlFlowGraph) = + var cache = initTable[(PNode, PNode), AliasKind]() + template aliasesCached(obj, field: PNode): AliasKind = aliasesCached(cache, obj, field) - while pc < until: - case cfg[pc].kind - of def: - let potLastReadsCopy = potLastReads - for r in potLastReadsCopy: - if cfg[pc].n.aliasesCached(cfg[r].n) == yes: - # the path leads to a redefinition of 's' --> sink 's'. - lastReads.incl r - potLastReads.excl r - elif cfg[r].n.aliasesCached(cfg[pc].n) != no: - # only partially writes to 's' --> can't sink 's', so this def reads 's' - # or maybe writes to 's' --> can't sink 's' - cfg[r].n.comment = '\n' & $pc - potLastReads.excl r - inc pc - of use: - let potLastReadsCopy = potLastReads - for r in potLastReadsCopy: - if cfg[pc].n.aliasesCached(cfg[r].n) != no or cfg[r].n.aliasesCached(cfg[pc].n) != no: - cfg[r].n.comment = '\n' & $pc - potLastReads.excl r + var cfg = cfg + preprocessCfg(cfg) - potLastReads.incl pc + var states = newSeq[State](cfg.len + 1) + states[0] = State() - inc pc - of goto: - pc += cfg[pc].dest - of fork: - var variantA = pc + 1 - var variantB = pc + cfg[pc].dest - var potLastReadsA, potLastReadsB = potLastReads - var lastReadsA, lastReadsB: IntSet - while variantA != variantB and max(variantA, variantB) < cfg.len and min(variantA, variantB) < until: - if variantA < variantB: - collectLastReads(cfg, cache, lastReadsA, potLastReadsA, variantA, min(variantB, until)) - else: - collectLastReads(cfg, cache, lastReadsB, potLastReadsB, variantB, min(variantA, until)) + for pc in 0.. sink 's'. + state.lastReads.incl r + state.potentialLastReads.excl r + elif cfg[r].n.aliasesCached(cfg[pc].n) != no: + # only partially writes to 's' --> can't sink 's', so this def reads 's' + # or maybe writes to 's' --> can't sink 's' + cfg[r].n.comment = '\n' & $pc + state.potentialLastReads.excl r + state.notLastReads.incl r - # Add those last reads that were turned into last reads on both branches - lastReads.incl lastReadsA * lastReadsB - # Add those last reads that were turned into last reads on only one branch, - # but where the read operation itself also belongs to only that branch - lastReads.incl (lastReadsA + lastReadsB) - potLastReads + var alreadySeenThisNode = false + for s in state.alreadySeen: + if cfg[pc].n.aliasesCached(s) != no or s.aliasesCached(cfg[pc].n) != no: + alreadySeenThisNode = true; break + if alreadySeenThisNode: cfg[pc].n.flags.excl nfFirstWrite + else: cfg[pc].n.flags.incl nfFirstWrite - let oldPotLastReads = potLastReads - potLastReads = initIntSet() + state.alreadySeen.incl cfg[pc].n - potLastReads.incl potLastReadsA + potLastReadsB + mergeStates(states[pc + 1], move(states[pc])) + of use: + var potentialLastReadsCopy = state.potentialLastReads + for r in potentialLastReadsCopy: + if cfg[pc].n.aliasesCached(cfg[r].n) != no or cfg[r].n.aliasesCached(cfg[pc].n) != no: + cfg[r].n.comment = '\n' & $pc + state.potentialLastReads.excl r + state.notLastReads.incl r - # Remove potential last reads that were invalidated in a branch, - # but don't remove those which were turned into last reads on that branch - potLastReads.excl ((oldPotLastReads - potLastReadsA) - lastReadsA) - potLastReads.excl ((oldPotLastReads - potLastReadsB) - lastReadsB) + state.potentialLastReads.incl pc - pc = min(variantA, variantB) + state.alreadySeen.incl cfg[pc].n -proc collectFirstWrites(cfg: ControlFlowGraph; alreadySeen: var HashSet[PNode]; pc: var int, until: int) = - while pc < until: - case cfg[pc].kind - of def: - var alreadySeenThisNode = false - for s in alreadySeen: - if cfg[pc].n.aliases(s) != no or s.aliases(cfg[pc].n) != no: - alreadySeenThisNode = true; break - if alreadySeenThisNode: cfg[pc].n.flags.excl nfFirstWrite - else: cfg[pc].n.flags.incl nfFirstWrite + mergeStates(states[pc + 1], move(states[pc])) + of goto: + mergeStates(states[pc + cfg[pc].dest], move(states[pc])) + of fork: + var copy = State() + copy[] = states[pc][] + mergeStates(states[pc + cfg[pc].dest], copy) + mergeStates(states[pc + 1], move(states[pc])) - alreadySeen.incl cfg[pc].n - - inc pc - of use: - alreadySeen.incl cfg[pc].n - - inc pc - of goto: - pc += cfg[pc].dest - of fork: - var variantA = pc + 1 - var variantB = pc + cfg[pc].dest - var alreadySeenA, alreadySeenB = alreadySeen - while variantA != variantB and max(variantA, variantB) < cfg.len and min(variantA, variantB) < until: - if variantA < variantB: - collectFirstWrites(cfg, alreadySeenA, variantA, min(variantB, until)) - else: - collectFirstWrites(cfg, alreadySeenB, variantB, min(variantA, until)) - - alreadySeen.incl alreadySeenA + alreadySeenB - - pc = min(variantA, variantB) + let lastReads = (states[^1].lastReads + states[^1].potentialLastReads) - states[^1].notLastReads + var lastReadTable: Table[PNode, seq[int]] + for position, node in cfg: + if node.kind == use: + lastReadTable.mgetOrPut(node.n, @[]).add position + for node, positions in lastReadTable: + block checkIfAllPosLastRead: + for p in positions: + if p notin lastReads: break checkIfAllPosLastRead + node.flags.incl nfLastRead proc isLastRead(n: PNode; c: var Con): bool = let m = dfa.skipConvDfa(n) @@ -1096,6 +1103,8 @@ proc injectDefaultCalls(n: PNode, c: var Con) = injectDefaultCalls(n[i], c) proc injectDestructorCalls*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n: PNode): PNode = + when toDebug.len > 0: + shouldDebug = toDebug == owner.name.s or toDebug == "always" if sfGeneratedOp in owner.flags or (owner.kind == skIterator and isInlineIterator(owner.typ)): return n var c = Con(owner: owner, graph: g, g: constructCfg(owner, n), idgen: idgen) @@ -1107,26 +1116,7 @@ proc injectDestructorCalls*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; n: if optCursorInference in g.config.options: computeCursors(owner, n, g) - block: - var cache = initTable[(PNode, PNode), AliasKind]() - var lastReads, potLastReads: IntSet - var pc = 0 - collectLastReads(c.g, cache, lastReads, potLastReads, pc, c.g.len) - lastReads.incl potLastReads - var lastReadTable: Table[PNode, seq[int]] - for position, node in c.g: - if node.kind == use: - lastReadTable.mgetOrPut(node.n, @[]).add position - for node, positions in lastReadTable: - var allPositionsLastRead = true - for p in positions: - if p notin lastReads: allPositionsLastRead = false; break - if allPositionsLastRead: - node.flags.incl nfLastRead - - var alreadySeen: HashSet[PNode] - pc = 0 - collectFirstWrites(c.g, alreadySeen, pc, c.g.len) + computeLastReadsAndFirstWrites(c.g) var scope: Scope let body = p(n, c, scope, normal) diff --git a/tests/arc/tmovebug.nim b/tests/arc/tmovebug.nim index 3ff1c4a0c..7977d330a 100644 --- a/tests/arc/tmovebug.nim +++ b/tests/arc/tmovebug.nim @@ -784,3 +784,37 @@ proc main3 = main3() +# misc +proc smoltest(x: bool): bool = + while true: + if true: return x + +discard smoltest(true) + +# bug #18002 +type + TTypeAttachedOp = enum + attachedAsgn + attachedSink + attachedTrace + + PNode = ref object + discard + +proc genAddrOf(n: PNode) = + assert n != nil, "moved?!" + +proc atomicClosureOp = + let x = PNode() + + genAddrOf: + block: + x + + case attachedTrace + of attachedSink: discard + of attachedAsgn: discard + of attachedTrace: genAddrOf(x) + +atomicClosureOp() + From c218f2ba0b8e27110087ea754c11cff123806a94 Mon Sep 17 00:00:00 2001 From: flywind Date: Sun, 16 May 2021 10:39:52 +0800 Subject: [PATCH 2110/3768] [std/re]fix terrible and strange interface --- lib/impure/re.nim | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/impure/re.nim b/lib/impure/re.nim index 0c96876b9..95d77033b 100644 --- a/lib/impure/re.nim +++ b/lib/impure/re.nim @@ -142,7 +142,7 @@ proc matchOrFind(buf: cstring, pattern: Regex, matches: var openArray[string], return rawMatches[1] - rawMatches[0] proc findBounds*(buf: cstring, pattern: Regex, matches: var openArray[string], - start = 0, bufSize: int): tuple[first, last: int] = + start = 0, bufSize = 0): tuple[first, last: int] = ## returns the starting position and end position of `pattern` in `buf` ## (where `buf` has length `bufSize` and is not necessarily `'\0'` terminated), ## and the captured @@ -200,7 +200,7 @@ proc findBounds*(s: string, pattern: Regex, result = findBounds(cstring(s), pattern, matches, start, s.len) proc findBounds*(buf: cstring, pattern: Regex, - start = 0, bufSize: int): tuple[first, last: int] = + start = 0, bufSize = 0): tuple[first, last: int] = ## returns the `first` and `last` position of `pattern` in `buf`, ## where `buf` has length `bufSize` (not necessarily `'\0'` terminated). ## If it does not match, `(-1,0)` is returned. @@ -239,7 +239,7 @@ proc matchLen*(s: string, pattern: Regex, matches: var openArray[string], result = matchOrFind(cstring(s), pattern, matches, start.cint, s.len.cint, pcre.ANCHORED) proc matchLen*(buf: cstring, pattern: Regex, matches: var openArray[string], - start = 0, bufSize: int): int {.inline.} = + start = 0, bufSize = 0): int {.inline.} = ## the same as `match`, but it returns the length of the match, ## if there is no match, `-1` is returned. Note that a match length ## of zero can happen. @@ -281,7 +281,7 @@ proc match*(s: string, pattern: Regex, matches: var openArray[string], result = matchLen(cstring(s), pattern, matches, start, s.len) != -1 proc match*(buf: cstring, pattern: Regex, matches: var openArray[string], - start = 0, bufSize: int): bool {.inline.} = + start = 0, bufSize = 0): bool {.inline.} = ## returns `true` if `buf[start.. Date: Sun, 16 May 2021 11:10:41 +0800 Subject: [PATCH 2111/3768] Revert "[std/re]fix terrible and strange interface" (#18027) This reverts commit c218f2ba0b8e27110087ea754c11cff123806a94. --- lib/impure/re.nim | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/impure/re.nim b/lib/impure/re.nim index 95d77033b..0c96876b9 100644 --- a/lib/impure/re.nim +++ b/lib/impure/re.nim @@ -142,7 +142,7 @@ proc matchOrFind(buf: cstring, pattern: Regex, matches: var openArray[string], return rawMatches[1] - rawMatches[0] proc findBounds*(buf: cstring, pattern: Regex, matches: var openArray[string], - start = 0, bufSize = 0): tuple[first, last: int] = + start = 0, bufSize: int): tuple[first, last: int] = ## returns the starting position and end position of `pattern` in `buf` ## (where `buf` has length `bufSize` and is not necessarily `'\0'` terminated), ## and the captured @@ -200,7 +200,7 @@ proc findBounds*(s: string, pattern: Regex, result = findBounds(cstring(s), pattern, matches, start, s.len) proc findBounds*(buf: cstring, pattern: Regex, - start = 0, bufSize = 0): tuple[first, last: int] = + start = 0, bufSize: int): tuple[first, last: int] = ## returns the `first` and `last` position of `pattern` in `buf`, ## where `buf` has length `bufSize` (not necessarily `'\0'` terminated). ## If it does not match, `(-1,0)` is returned. @@ -239,7 +239,7 @@ proc matchLen*(s: string, pattern: Regex, matches: var openArray[string], result = matchOrFind(cstring(s), pattern, matches, start.cint, s.len.cint, pcre.ANCHORED) proc matchLen*(buf: cstring, pattern: Regex, matches: var openArray[string], - start = 0, bufSize = 0): int {.inline.} = + start = 0, bufSize: int): int {.inline.} = ## the same as `match`, but it returns the length of the match, ## if there is no match, `-1` is returned. Note that a match length ## of zero can happen. @@ -281,7 +281,7 @@ proc match*(s: string, pattern: Regex, matches: var openArray[string], result = matchLen(cstring(s), pattern, matches, start, s.len) != -1 proc match*(buf: cstring, pattern: Regex, matches: var openArray[string], - start = 0, bufSize = 0): bool {.inline.} = + start = 0, bufSize: int): bool {.inline.} = ## returns `true` if `buf[start.. Date: Sun, 16 May 2021 10:03:22 -0700 Subject: [PATCH 2112/3768] more informative error msg for undeclared field (`A(badfield: 1)` and `a.badfield = expr`) (#17777) --- compiler/semcall.nim | 15 +++++++++-- compiler/semobjconstr.nim | 3 ++- tests/errmsgs/tundeclared_field.nim | 40 +++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 3 deletions(-) create mode 100644 tests/errmsgs/tundeclared_field.nim diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 8f29bdf32..facaa89f7 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -411,8 +411,19 @@ proc resolveOverloads(c: PContext, n, orig: PNode, if overloadsState == csEmpty and result.state == csEmpty: if efNoUndeclared notin flags: # for tests/pragmas/tcustom_pragma.nim - # xxx adapt/use errorUndeclaredIdentifierHint(c, n, f.ident) - localError(c.config, n.info, getMsgDiagnostic(c, flags, n, f)) + template impl() = + # xxx adapt/use errorUndeclaredIdentifierHint(c, n, f.ident) + localError(c.config, n.info, getMsgDiagnostic(c, flags, n, f)) + if n[0].kind == nkIdent and n[0].ident.s == ".=" and n[2].kind == nkIdent: + let sym = n[1].typ.sym + if sym == nil: + impl() + else: + let field = n[2].ident.s + let msg = errUndeclaredField % field & " for type " & getProcHeader(c.config, sym) + localError(c.config, orig[2].info, msg) + else: + impl() return elif result.state != csMatch: if nfExprCall in n.flags: diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim index 6f3cabcbf..16e3c6cee 100644 --- a/compiler/semobjconstr.nim +++ b/compiler/semobjconstr.nim @@ -429,7 +429,8 @@ proc semObjConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = hasError = true break # 2) No such field exists in the constructed type - localError(c.config, field.info, errUndeclaredFieldX % id.s) + let msg = errUndeclaredField % id.s & " for type " & getProcHeader(c.config, t.sym) + localError(c.config, field.info, msg) hasError = true break diff --git a/tests/errmsgs/tundeclared_field.nim b/tests/errmsgs/tundeclared_field.nim new file mode 100644 index 000000000..2b274ae58 --- /dev/null +++ b/tests/errmsgs/tundeclared_field.nim @@ -0,0 +1,40 @@ +discard """ +cmd: '''nim check --hints:off $file''' +action: reject +nimout: ''' +tundeclared_field.nim(25, 12) Error: undeclared field: 'bad' for type tundeclared_field.A [type declared in tundeclared_field.nim(22, 8)] +tundeclared_field.nim(30, 16) Error: undeclared field: 'bad' for type tundeclared_field.A [type declared in tundeclared_field.nim(28, 8)] +tundeclared_field.nim(36, 4) Error: undeclared field: 'bad' for type tundeclared_field.A [type declared in tundeclared_field.nim(33, 8)] +tundeclared_field.nim(40, 13) Error: cannot instantiate Foo [type declared in tundeclared_field.nim(39, 8)] +''' +""" + + + + + + + + + +# line 20 +block: + type A = object + a0: int + var a: A + discard a.bad + +block: + type A = object + a0: int + var a = A(bad: 0) + +block: + type A = object + a0: int + var a: A + a.bad = 0 + +block: + type Foo[T: SomeInteger] = object + var a: Foo[float] From 63fcb9e5f54fa6d3a65499abbed26a923f0dd337 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Sun, 16 May 2021 19:43:52 +0200 Subject: [PATCH 2113/3768] Disable performance hints by default (verbosity 1) (#18024) --- compiler/lineinfos.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index 27d7b32e0..47cddb888 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -199,7 +199,7 @@ proc computeNotesVerbosity(): array[0..3, TNoteKinds] = result[2] = result[3] - {hintStackTrace, warnUninit, hintExtendedContext, hintDeclaredLoc} result[1] = result[2] - {warnProveField, warnProveIndex, warnGcUnsafe, hintPath, hintDependency, hintCodeBegin, hintCodeEnd, - hintSource, hintGlobalVar, hintGCStats, hintMsgOrigin} + hintSource, hintGlobalVar, hintGCStats, hintMsgOrigin, hintPerformance} result[0] = result[1] - {hintSuccessX, hintBuildMode, hintSuccess, hintConf, hintProcessing, hintPattern, hintExecuting, hintLinking, hintCC} From d83b25db1ebe7025d95dbce3978219948f5c49d3 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 16 May 2021 14:54:10 -0700 Subject: [PATCH 2114/3768] fix #18007: std/json now serializes nan,inf,-inf as strings instead of invalid json (#18026) * fix #18007: std/json now serializes nan,inf,-inf as raw strings instead of invalid json * fix roundtrip * fix tests * fix changelog * simplify * add runnableExamples * fix typo [skip ci] --- changelog.md | 2 + lib/pure/json.nim | 121 +++++++++++++++++++++--------------- lib/std/jsonutils.nim | 3 + tests/stdlib/tjson.nim | 23 +++++++ tests/stdlib/tjsonutils.nim | 21 ++++++- 5 files changed, 119 insertions(+), 51 deletions(-) diff --git a/changelog.md b/changelog.md index 98a7a8c44..369b11917 100644 --- a/changelog.md +++ b/changelog.md @@ -70,6 +70,8 @@ - `jsonutils` now serializes/deserializes holey enums as regular enums (via `ord`) instead of as strings. Use `-d:nimLegacyJsonutilsHoleyEnum` for a transition period. +- `json` and `jsonutils` now serialize NaN, Inf, -Inf as strings, so that + `%[NaN, -Inf]` is the string `["nan","-inf"]` instead of `[nan,-inf]` which was invalid json. ## Standard library additions and changes - Added support for parenthesized expressions in `strformat` diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 96ee61fd5..3a9fa5898 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -333,7 +333,16 @@ proc `%`*(n: BiggestInt): JsonNode = proc `%`*(n: float): JsonNode = ## Generic constructor for JSON data. Creates a new `JFloat JsonNode`. - result = JsonNode(kind: JFloat, fnum: n) + runnableExamples: + assert $(%[NaN, Inf, -Inf, 0.0, -0.0, 1.0, 1e-2]) == """["nan","inf","-inf",0.0,-0.0,1.0,0.01]""" + assert (%NaN).kind == JString + assert (%0.0).kind == JFloat + # for those special cases, we could also have used `newJRawNumber` but then + # it would've been inconsisten with the case of `parseJson` vs `%` for representing them. + if n != n: newJString("nan") + elif n == Inf: newJString("inf") + elif n == -Inf: newJString("-inf") + else: JsonNode(kind: JFloat, fnum: n) proc `%`*(b: bool): JsonNode = ## Generic constructor for JSON data. Creates a new `JBool JsonNode`. @@ -662,6 +671,47 @@ proc escapeJson*(s: string): string = result = newStringOfCap(s.len + s.len shr 3) escapeJson(s, result) +proc toUgly*(result: var string, node: JsonNode) = + ## Converts `node` to its JSON Representation, without + ## regard for human readability. Meant to improve `$` string + ## conversion performance. + ## + ## JSON representation is stored in the passed `result` + ## + ## This provides higher efficiency than the `pretty` procedure as it + ## does **not** attempt to format the resulting JSON to make it human readable. + var comma = false + case node.kind: + of JArray: + result.add "[" + for child in node.elems: + if comma: result.add "," + else: comma = true + result.toUgly child + result.add "]" + of JObject: + result.add "{" + for key, value in pairs(node.fields): + if comma: result.add "," + else: comma = true + key.escapeJson(result) + result.add ":" + result.toUgly value + result.add "}" + of JString: + if node.isUnquoted: + result.add node.str + else: + escapeJson(node.str, result) + of JInt: + result.addInt(node.num) + of JFloat: + result.addFloat(node.fnum) + of JBool: + result.add(if node.bval: "true" else: "false") + of JNull: + result.add "null" + proc toPretty(result: var string, node: JsonNode, indent = 2, ml = true, lstArr = false, currIndent = 0) = case node.kind @@ -689,10 +739,7 @@ proc toPretty(result: var string, node: JsonNode, indent = 2, ml = true, result.add("{}") of JString: if lstArr: result.indent(currIndent) - if node.isUnquoted: - result.add node.str - else: - escapeJson(node.str, result) + toUgly(result, node) of JInt: if lstArr: result.indent(currIndent) result.addInt(node.num) @@ -743,47 +790,6 @@ proc pretty*(node: JsonNode, indent = 2): string = result = "" toPretty(result, node, indent) -proc toUgly*(result: var string, node: JsonNode) = - ## Converts `node` to its JSON Representation, without - ## regard for human readability. Meant to improve `$` string - ## conversion performance. - ## - ## JSON representation is stored in the passed `result` - ## - ## This provides higher efficiency than the `pretty` procedure as it - ## does **not** attempt to format the resulting JSON to make it human readable. - var comma = false - case node.kind: - of JArray: - result.add "[" - for child in node.elems: - if comma: result.add "," - else: comma = true - result.toUgly child - result.add "]" - of JObject: - result.add "{" - for key, value in pairs(node.fields): - if comma: result.add "," - else: comma = true - key.escapeJson(result) - result.add ":" - result.toUgly value - result.add "}" - of JString: - if node.isUnquoted: - result.add node.str - else: - node.str.escapeJson(result) - of JInt: - result.addInt(node.num) - of JFloat: - result.addFloat(node.fnum) - of JBool: - result.add(if node.bval: "true" else: "false") - of JNull: - result.add "null" - proc `$`*(node: JsonNode): string = ## Converts `node` to its JSON Representation on one line. result = newStringOfCap(node.len shl 1) @@ -1087,11 +1093,26 @@ when defined(nimFixedForwardGeneric): dst = cast[T](jsonNode.num) proc initFromJson[T: SomeFloat](dst: var T; jsonNode: JsonNode; jsonPath: var string) = - verifyJsonKind(jsonNode, {JInt, JFloat}, jsonPath) - if jsonNode.kind == JFloat: - dst = T(jsonNode.fnum) + if jsonNode.kind == JString: + case jsonNode.str + of "nan": + let b = NaN + dst = T(b) + # dst = NaN # would fail some tests because range conversions would cause CT error + # in some cases; but this is not a hot-spot inside this branch and backend can optimize this. + of "inf": + let b = Inf + dst = T(b) + of "-inf": + let b = -Inf + dst = T(b) + else: raise newException(JsonKindError, "expected 'nan|inf|-inf', got " & jsonNode.str) else: - dst = T(jsonNode.num) + verifyJsonKind(jsonNode, {JInt, JFloat}, jsonPath) + if jsonNode.kind == JFloat: + dst = T(jsonNode.fnum) + else: + dst = T(jsonNode.num) proc initFromJson[T: enum](dst: var T; jsonNode: JsonNode; jsonPath: var string) = verifyJsonKind(jsonNode, {JString}, jsonPath) diff --git a/lib/std/jsonutils.nim b/lib/std/jsonutils.nim index 99f5ce9c6..1f49f60ed 100644 --- a/lib/std/jsonutils.nim +++ b/lib/std/jsonutils.nim @@ -12,6 +12,9 @@ runnableExamples: let a = (1.5'f32, (b: "b2", a: "a2"), 'x', @[Foo(t: true, z1: -3), nil], [{"name": "John"}.newStringTable]) let j = a.toJson assert j.jsonTo(typeof(a)).toJson == j + assert $[NaN, Inf, -Inf, 0.0, -0.0, 1.0, 1e-2].toJson == """["nan","inf","-inf",0.0,-0.0,1.0,0.01]""" + assert 0.0.toJson.kind == JFloat + assert Inf.toJson.kind == JString import json, strutils, tables, sets, strtabs, options diff --git a/tests/stdlib/tjson.nim b/tests/stdlib/tjson.nim index e538baf4f..e757e6c7e 100644 --- a/tests/stdlib/tjson.nim +++ b/tests/stdlib/tjson.nim @@ -8,14 +8,27 @@ Note: Macro tests are in tests/stdlib/tjsonmacro.nim ]# import std/[json,parsejson,strutils] +from std/math import isNaN when not defined(js): import std/streams proc testRoundtrip[T](t: T, expected: string) = + # checks that `T => json => T2 => json2` is such that json2 = json let j = %t doAssert $j == expected, $j doAssert %(j.to(T)) == j +proc testRoundtripVal[T](t: T, expected: string) = + # similar to testRoundtrip, but also checks that the `T => json => T2` is such that `T2 == T` + # note that this isn't always possible, e.g. for pointer-like types or nans + let j = %t + doAssert $j == expected, $j + let j2 = ($j).parseJson + doAssert $j2 == expected, $(j2, t) + let t2 = j2.to(T) + doAssert t2 == t + doAssert $(%* t2) == expected # sanity check, because -0.0 = 0.0 but their json representation differs + let testJson = parseJson"""{ "a": [1, 2, 3, 4], "b": "asd", "c": "\ud83c\udf83", "d": "\u00E6"}""" # nil passthrough doAssert(testJson{"doesnt_exist"}{"anything"}.isNil) @@ -301,6 +314,16 @@ block: # bug #17383 testRoundtrip(int64.high): "9223372036854775807" testRoundtrip(uint64.high): "18446744073709551615" +block: # bug #18007 + testRoundtrip([NaN, Inf, -Inf, 0.0, -0.0, 1.0]): """["nan","inf","-inf",0.0,-0.0,1.0]""" + # pending https://github.com/nim-lang/Nim/issues/18025 use: + # testRoundtrip([float32(NaN), Inf, -Inf, 0.0, -0.0, 1.0]) + let inf = float32(Inf) + testRoundtrip([float32(NaN), inf, -inf, 0.0, -0.0, 1.0]): """["nan","inf","-inf",0.0,-0.0,1.0]""" + when not defined(js): # because of Infinity vs inf + testRoundtripVal([inf, -inf, 0.0, -0.0, 1.0]): """["inf","-inf",0.0,-0.0,1.0]""" + let a = parseJson($(%NaN)).to(float) + doAssert a.isNaN block: let a = "18446744073709551615" diff --git a/tests/stdlib/tjsonutils.nim b/tests/stdlib/tjsonutils.nim index 18d0aa325..c826a79b0 100644 --- a/tests/stdlib/tjsonutils.nim +++ b/tests/stdlib/tjsonutils.nim @@ -4,15 +4,28 @@ discard """ import std/jsonutils import std/json +from std/math import isNaN proc testRoundtrip[T](t: T, expected: string) = + # checks that `T => json => T2 => json2` is such that json2 = json let j = t.toJson - doAssert $j == expected, $j + doAssert $j == expected, "\n" & $j & "\n" & expected doAssert j.jsonTo(T).toJson == j var t2: T t2.fromJson(j) doAssert t2.toJson == j +proc testRoundtripVal[T](t: T, expected: string) = + # similar to testRoundtrip, but also checks that the `T => json => T2` is such that `T2 == T` + # note that this isn't always possible, e.g. for pointer-like types. + let j = t.toJson + let j2 = $j + doAssert j2 == expected, j2 + let j3 = j2.parseJson + let t2 = j3.jsonTo(T) + doAssert t2 == t + doAssert $t2.toJson == j2 # still needed, because -0.0 = 0.0 but their json representation differs + import tables, sets, algorithm, sequtils, options, strtabs from strutils import contains @@ -91,6 +104,12 @@ template fn() = else: testRoundtrip(a): "[9223372036854775807,18446744073709551615]" + block: # bug #18007 + testRoundtrip((NaN, Inf, -Inf, 0.0, -0.0, 1.0)): """["nan","inf","-inf",0.0,-0.0,1.0]""" + testRoundtrip((float32(NaN), Inf, -Inf, 0.0, -0.0, 1.0)): """["nan","inf","-inf",0.0,-0.0,1.0]""" + testRoundtripVal((Inf, -Inf, 0.0, -0.0, 1.0)): """["inf","-inf",0.0,-0.0,1.0]""" + doAssert ($NaN.toJson).parseJson.jsonTo(float).isNaN + block: # case object type Foo = object x0: float From 2096490b5974cfb11d0f5d37ff6d3a963ef43713 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Mon, 17 May 2021 00:55:14 +0300 Subject: [PATCH 2115/3768] follow-up #17930 - inline syntax highlighting (#18013) * follow-up #17930 - inline syntax highlighting * make closure->nimcall --- doc/gc.rst | 35 ++++++++++++++++++++++------------- lib/packages/docutils/rst.nim | 25 ++++++++++++++----------- tests/stdlib/trst.nim | 21 +++++++++++++++++++++ 3 files changed, 57 insertions(+), 24 deletions(-) diff --git a/doc/gc.rst b/doc/gc.rst index 4455afcbe..5de02f73d 100644 --- a/doc/gc.rst +++ b/doc/gc.rst @@ -1,9 +1,10 @@ -.. default-role:: code - ======================= Nim's Memory Management ======================= +.. default-role:: code +.. include:: rstcommon.rst + :Author: Andreas Rumpf :Version: |nimversion| @@ -29,24 +30,28 @@ and how the memory management strategies other than garbage collectors work. Multi-paradigm Memory Management Strategies =========================================== +.. default-role:: option + To choose the memory management strategy use the `--gc:` switch. -- `--gc:refc`. This is the default GC. It's a +--gc:refc This is the default GC. It's a deferred reference counting based garbage collector with a simple Mark&Sweep backup GC in order to collect cycles. Heaps are thread-local. -- `--gc:markAndSweep`. Simple Mark-And-Sweep based garbage collector. Heaps are thread-local. -- `--gc:boehm`. Boehm based garbage collector, it offers a shared heap. -- `--gc:go`. Go's garbage collector, useful for interoperability with Go. Offers a shared heap. -- `--gc:arc`. Plain reference counting with +--gc:markAndSweep Simple Mark-And-Sweep based garbage collector. + Heaps are thread-local. +--gc:boehm Boehm based garbage collector, it offers a shared heap. +--gc:go Go's garbage collector, useful for interoperability with Go. + Offers a shared heap. +--gc:arc Plain reference counting with `move semantic optimizations `_, offers a shared heap. It offers deterministic performance for `hard realtime`:idx: systems. Reference cycles cause memory leaks, beware. -- `--gc:orc`. Same as `--gc:arc` but adds a cycle collector based on "trial deletion". +--gc:orc Same as `--gc:arc` but adds a cycle collector based on "trial deletion". Unfortunately, that makes its performance profile hard to reason about so it is less useful for hard real-time systems. -- `--gc:none`. No memory management strategy nor a garbage collector. Allocated memory is +--gc:none No memory management strategy nor a garbage collector. Allocated memory is simply never freed. You should use `--gc:arc` instead. @@ -62,6 +67,9 @@ Go Shared Cycle Collector Yes `--gc:go` None Manual Manual Manual `--gc:none` ================== ======== ================= ============== =================== +.. default-role:: code +.. include:: rstcommon.rst + JavaScript's garbage collector is used for the `JavaScript and NodeJS `_ compilation targets. The `NimScript `_ target uses the memory management strategy built into @@ -82,7 +90,7 @@ Soft real-time support ---------------------- To enable real-time support, the symbol `useRealtimeGC`:idx: needs to be -defined via `--define:useRealtimeGC` (you can put this into your config +defined via `--define:useRealtimeGC`:option: (you can put this into your config file as well). With this switch the garbage collector supports the following operations: @@ -132,7 +140,7 @@ Time measurement with garbage collectors ---------------------------------------- The garbage collectors' way of measuring time uses -(see `lib/system/timers.nim` for the implementation): +(see ``lib/system/timers.nim`` for the implementation): 1) `QueryPerformanceCounter` and `QueryPerformanceFrequency` on Windows. 2) `mach_absolute_time` on Mac OS X. @@ -170,7 +178,7 @@ Other useful procs from `system `_ you can use to keep track of mem * `GC_getStatistics()` Garbage collector statistics as a human-readable string. These numbers are usually only for the running thread, not for the whole heap, -with the exception of `--gc:boehm` and `--gc:go`. +with the exception of `--gc:boehm`:option: and `--gc:go`:option:. In addition to `GC_ref` and `GC_unref` you can avoid the garbage collector by manually allocating memory with procs like `alloc`, `alloc0`, `allocShared`, `allocShared0` or `allocCStringArray`. @@ -184,7 +192,8 @@ Heap dump The heap dump feature is still in its infancy, but it already proved useful for us, so it might be useful for you. To get a heap dump, compile -with `-d:nimTypeNames` and call `dumpNumberOfInstances` at a strategic place in your program. +with `-d:nimTypeNames`:option: and call `dumpNumberOfInstances` +at a strategic place in your program. This produces a list of the used types in your program and for every type the total amount of object instances for this type as well as the total amount of bytes these instances take up. diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 9cbc4efb7..da04e9b54 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -1151,6 +1151,18 @@ proc toOtherRole(n: PRstNode, kind: RstNodeKind, roleName: string): PRstNode = proc parsePostfix(p: var RstParser, n: PRstNode): PRstNode = var newKind = n.kind var newSons = n.sons + + proc finalizeInterpreted(node: PRstNode, newKind: RstNodeKind, + newSons: seq[PRstNode], roleName: string): + PRstNode {.nimcall.} = + # fixes interpreted text (`x` or `y`:role:) to proper internal AST format + if newKind in {rnUnknownRole, rnCodeFragment}: + result = node.toOtherRole(newKind, roleName) + elif newKind == rnInlineCode: + result = node.toInlineCode(language=roleName) + else: + result = newRstNode(newKind, newSons) + if isInlineMarkupEnd(p, "_", exact=true) or isInlineMarkupEnd(p, "__", exact=true): inc p.idx @@ -1175,19 +1187,10 @@ proc parsePostfix(p: var RstParser, n: PRstNode): PRstNode = # a role: let (roleName, lastIdx) = getRefname(p, p.idx+1) newKind = whichRole(p, roleName) - if newKind in {rnUnknownRole, rnCodeFragment}: - result = n.toOtherRole(newKind, roleName) - elif newKind == rnInlineCode: - result = n.toInlineCode(language=roleName) - else: - result = newRstNode(newKind, newSons) + result = n.finalizeInterpreted(newKind, newSons, roleName) p.idx = lastIdx + 2 else: - if p.s.currRoleKind == rnInlineCode: - result = n.toInlineCode(language=p.s.currRole) - else: - newKind = p.s.currRoleKind - result = newRstNode(newKind, newSons) + result = n.finalizeInterpreted(p.s.currRoleKind, newSons, p.s.currRole) proc matchVerbatim(p: RstParser, start: int, expr: string): int = result = start diff --git a/tests/stdlib/trst.nim b/tests/stdlib/trst.nim index e6c5d9700..71f5a858b 100644 --- a/tests/stdlib/trst.nim +++ b/tests/stdlib/trst.nim @@ -371,6 +371,27 @@ suite "RST inline markup": rnLeaf '```' """) + test "interpreted text parsing: code fragments": + check(dedent""" + .. default-role:: option + + `--gc:refc`""".toAst == + dedent""" + rnInner + rnDefaultRole + rnDirArg + rnLeaf 'option' + [nil] + [nil] + rnParagraph + rnCodeFragment + rnInner + rnLeaf '--' + rnLeaf 'gc' + rnLeaf ':' + rnLeaf 'refc' + rnLeaf 'option' + """) test """interpreted text can be ended with \` """: let output = (".. default-role:: literal\n" & """`\``""").toAst From e904c6d87ccbb453f6f1d649b2c715bd3cebc815 Mon Sep 17 00:00:00 2001 From: Joey Date: Mon, 17 May 2021 00:04:39 -0600 Subject: [PATCH 2116/3768] Add `checked` to dom (#18033) This allows the ability to set a checkbox as checked programmatically. It's different from `setAttribute` because once an input has been clicked on by the user, `setAttribute` no longer works programmatically. --- lib/js/dom.nim | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/js/dom.nim b/lib/js/dom.nim index 303cc178f..16fc96b68 100644 --- a/lib/js/dom.nim +++ b/lib/js/dom.nim @@ -1379,6 +1379,9 @@ proc `class=`*(n: Node; v: cstring) {.importcpp: "#.className = #", nodecl.} proc value*(n: Node): cstring {.importcpp: "#.value", nodecl.} proc `value=`*(n: Node; v: cstring) {.importcpp: "#.value = #", nodecl.} +proc checked*(n: Node): bool {.importcpp: "#.checked", nodecl.} +proc `checked=`*(n: Node; v: bool) {.importcpp: "#.checked = #", nodecl.} + proc `disabled=`*(n: Node; v: bool) {.importcpp: "#.disabled = #", nodecl.} when defined(nodejs): From 8be5344b3bb74d62cff5aa6a18f4223680612ad5 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 17 May 2021 01:28:52 -0700 Subject: [PATCH 2117/3768] ./koch --nonexistant now fails (#18036) --- koch.nim | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/koch.nim b/koch.nim index 15355ec36..3d7276aee 100644 --- a/koch.nim +++ b/koch.nim @@ -634,9 +634,9 @@ proc valgrind(cmd: string) = let supp = getAppDir() / "tools" / "nimgrind.supp" exec("valgrind --suppressions=" & supp & valcmd) -proc showHelp() = +proc showHelp(success: bool) = quit(HelpText % [VersionAsString & spaces(44-len(VersionAsString)), - CompileDate, CompileTime], QuitSuccess) + CompileDate, CompileTime], if success: QuitSuccess else: QuitFailure) proc branchDone() = let thisBranch = execProcess("git symbolic-ref --short HEAD").strip() @@ -656,6 +656,7 @@ when isMainModule: case op.kind of cmdLongOption, cmdShortOption: case normalize(op.key) + of "help", "h": showHelp(success = true) of "latest": latest = true of "stable": latest = false of "nim": nimExe = op.val.absolutePath # absolute so still works with changeDir @@ -663,7 +664,7 @@ when isMainModule: localDocsOnly = true if op.val.len > 0: localDocsOut = op.val.absolutePath - else: showHelp() + else: showHelp(success = false) of cmdArgument: case normalize(op.key) of "boot": boot(op.cmdLineRest) @@ -705,6 +706,6 @@ when isMainModule: exec("nimble install -y fusion@$#" % suffix) of "ic": icTest(op.cmdLineRest) of "branchdone": branchDone() - else: showHelp() + else: showHelp(success = false) break of cmdEnd: break From fac5bae7b7d87aeec48c7252029c2852ee157ac9 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Mon, 17 May 2021 13:48:10 +0200 Subject: [PATCH 2118/3768] Fix running testament c nimble-packages without batch arg (#18023) * Fix running testament c nimble-packages without batch arg * Fix --- testament/categories.nim | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/testament/categories.nim b/testament/categories.nim index 1253a2b8c..1f0bd6acf 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -424,11 +424,12 @@ proc listPackages(packageFilter: string): seq[NimblePackage] = # at least should be a regex; a substring match makes no sense. result = pkgs.filterIt(packageFilter in it.name) else: - let pkgs1 = pkgs.filterIt(it.allowFailure) - let pkgs2 = pkgs.filterIt(not it.allowFailure) if testamentData0.batchArg == "allowed_failures": - result = pkgs1 + result = pkgs.filterIt(it.allowFailure) + elif testamentData0.testamentNumBatch == 0: + result = pkgs else: + let pkgs2 = pkgs.filterIt(not it.allowFailure) for i in 0.. Date: Tue, 18 May 2021 09:16:07 +0300 Subject: [PATCH 2119/3768] add sink and lent annotations for xmltree and streams (#18037) --- lib/pure/streams.nim | 2 +- lib/pure/xmltree.nim | 30 +++++++++++++++--------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim index 3cc5ea038..7dc81148f 100644 --- a/lib/pure/streams.nim +++ b/lib/pure/streams.nim @@ -1252,7 +1252,7 @@ else: # after 1.3 or JS not defined var s = StringStream(s) s.data = "" - proc newStringStream*(s: string = ""): owned StringStream = + proc newStringStream*(s: sink string = ""): owned StringStream = ## Creates a new stream from the string `s`. ## ## See also: diff --git a/lib/pure/xmltree.nim b/lib/pure/xmltree.nim index af7e4d508..fdb2c3fc5 100644 --- a/lib/pure/xmltree.nim +++ b/lib/pure/xmltree.nim @@ -70,7 +70,7 @@ proc newXmlNode(kind: XmlNodeKind): XmlNode = ## Creates a new ``XmlNode``. result = XmlNode(k: kind) -proc newElement*(tag: string): XmlNode = +proc newElement*(tag: sink string): XmlNode = ## Creates a new ``XmlNode`` of kind ``xnElement`` with the given `tag`. ## ## See also: @@ -89,7 +89,7 @@ proc newElement*(tag: string): XmlNode = result.s = @[] # init attributes lazily to save memory -proc newText*(text: string): XmlNode = +proc newText*(text: sink string): XmlNode = ## Creates a new ``XmlNode`` of kind ``xnText`` with the text `text`. runnableExamples: var b = newText("my text") @@ -99,13 +99,13 @@ proc newText*(text: string): XmlNode = result = newXmlNode(xnText) result.fText = text -proc newVerbatimText*(text: string): XmlNode {.since: (1, 3).} = +proc newVerbatimText*(text: sink string): XmlNode {.since: (1, 3).} = ## Creates a new ``XmlNode`` of kind ``xnVerbatimText`` with the text `text`. ## **Since**: Version 1.3. result = newXmlNode(xnVerbatimText) result.fText = text -proc newComment*(comment: string): XmlNode = +proc newComment*(comment: sink string): XmlNode = ## Creates a new ``XmlNode`` of kind ``xnComment`` with the text `comment`. runnableExamples: var c = newComment("my comment") @@ -115,7 +115,7 @@ proc newComment*(comment: string): XmlNode = result = newXmlNode(xnComment) result.fText = comment -proc newCData*(cdata: string): XmlNode = +proc newCData*(cdata: sink string): XmlNode = ## Creates a new ``XmlNode`` of kind ``xnCData`` with the text `cdata`. runnableExamples: var d = newCData("my cdata") @@ -135,7 +135,7 @@ proc newEntity*(entity: string): XmlNode = result = newXmlNode(xnEntity) result.fText = entity -proc newXmlTree*(tag: string, children: openArray[XmlNode], +proc newXmlTree*(tag: sink string, children: openArray[XmlNode], attributes: XmlAttributes = nil): XmlNode = ## Creates a new XML tree with `tag`, `children` and `attributes`. ## @@ -151,7 +151,7 @@ proc newXmlTree*(tag: string, children: openArray[XmlNode], h.add newEntity("some entity") let att = {"key1": "first value", "key2": "second value"}.toXmlAttributes let k = newXmlTree("treeTag", [g, h], att) - + doAssert $k == """ some text &some entity; @@ -163,7 +163,7 @@ proc newXmlTree*(tag: string, children: openArray[XmlNode], for i in 0..children.len-1: result.s[i] = children[i] result.fAttr = attributes -proc text*(n: XmlNode): string {.inline.} = +proc text*(n: XmlNode): lent string {.inline.} = ## Gets the associated text with the node `n`. ## ## `n` can be a CDATA, Text, comment, or entity node. @@ -181,7 +181,7 @@ proc text*(n: XmlNode): string {.inline.} = assert n.k in {xnText, xnComment, xnCData, xnEntity} result = n.fText -proc `text=`*(n: XmlNode, text: string){.inline.} = +proc `text=`*(n: XmlNode, text: sink string) {.inline.} = ## Sets the associated text with the node `n`. ## ## `n` can be a CDATA, Text, comment, or entity node. @@ -199,7 +199,7 @@ proc `text=`*(n: XmlNode, text: string){.inline.} = assert n.k in {xnText, xnComment, xnCData, xnEntity} n.fText = text -proc tag*(n: XmlNode): string {.inline.} = +proc tag*(n: XmlNode): lent string {.inline.} = ## Gets the tag name of `n`. ## ## `n` has to be an ``xnElement`` node. @@ -220,7 +220,7 @@ proc tag*(n: XmlNode): string {.inline.} = assert n.k == xnElement result = n.fTag -proc `tag=`*(n: XmlNode, tag: string) {.inline.} = +proc `tag=`*(n: XmlNode, tag: sink string) {.inline.} = ## Sets the tag name of `n`. ## ## `n` has to be an ``xnElement`` node. @@ -389,13 +389,13 @@ proc clear*(n: var XmlNode) = var g = newElement("myTag") g.add newText("some text") g.add newComment("this is comment") - + var h = newElement("secondTag") h.add newEntity("some entity") - + let att = {"key1": "first value", "key2": "second value"}.toXmlAttributes var k = newXmlTree("treeTag", [g, h], att) - + doAssert $k == """ some text &some entity; @@ -447,7 +447,7 @@ proc toXmlAttributes*(keyValuePairs: varargs[tuple[key, let att = {"key1": "first value", "key2": "second value"}.toXmlAttributes var j = newElement("myTag") j.attrs = att - + doAssert $j == """""" newStringTable(keyValuePairs) From 7f077a76fea389ce4f55e18857c499fadb3958fa Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 18 May 2021 06:10:19 -0700 Subject: [PATCH 2120/3768] jsonutils: add customization for toJson via `ToJsonOptions`; generalize symbolName; add symbolRank (#18029) * jsonutils: add customization for toJson via `ToJsonOptions` * add enumutils.symbolRank * lookup table implementation for HoleyEnum * cleanup * changelog * fixup * Update lib/std/jsonutils.nim Co-authored-by: Andreas Rumpf --- changelog.md | 5 ++- lib/std/enumutils.nim | 65 +++++++++++++++++++++++++++++++++++-- lib/std/jsonutils.nim | 43 +++++++++++++++++------- tests/stdlib/tjsonutils.nim | 13 ++++++++ 4 files changed, 112 insertions(+), 14 deletions(-) diff --git a/changelog.md b/changelog.md index 369b11917..97e749337 100644 --- a/changelog.md +++ b/changelog.md @@ -118,9 +118,11 @@ - `json.%`,`json.to`, `jsonutils.formJson`,`jsonutils.toJson` now work with `uint|uint64` instead of raising (as in 1.4) or giving wrong results (as in 1.2). +- `jsonutils` now handles `cstring` (including as Table key), and `set`. + - added `jsonutils.jsonTo` overload with `opt = Joptions()` param. -- `jsonutils` now handles `cstring` (including as Table key), and `set`. +- `jsonutils.toJson` now supports customization via `ToJsonOptions`. - Added an overload for the `collect` macro that inferes the container type based on the syntax of the last expression. Works with std seqs, tables and sets. @@ -138,6 +140,7 @@ - Added `std/enumutils` module. Added `genEnumCaseStmt` macro that generates case statement to parse string to enum. Added `items` for enums with holes. Added `symbolName` to return the enum symbol name ignoring the human readable name. + Added `symbolRank` to return the index in which an enum member is listed in an enum. - Added `typetraits.HoleyEnum` for enums with holes, `OrdinalEnum` for enums without holes. diff --git a/lib/std/enumutils.nim b/lib/std/enumutils.nim index 6195ae07d..09cf24f51 100644 --- a/lib/std/enumutils.nim +++ b/lib/std/enumutils.nim @@ -86,8 +86,67 @@ iterator items*[T: HoleyEnum](E: typedesc[T]): T = assert B[float].toSeq == [B[float].b0, B[float].b1] for a in enumFullRange(E): yield a -func symbolName*[T: OrdinalEnum](a: T): string = +func span(T: typedesc[HoleyEnum]): int = + (T.high.ord - T.low.ord) + 1 + +const invalidSlot = uint8.high + +proc genLookup[T: typedesc[HoleyEnum]](_: T): auto = + const n = span(T) + var ret: array[n, uint8] + var i = 0 + assert n <= invalidSlot.int + for ai in mitems(ret): ai = invalidSlot + for ai in items(T): + ret[ai.ord - T.low.ord] = uint8(i) + inc(i) + return ret + +func symbolRankImpl[T](a: T): int {.inline.} = + const n = T.span + const thres = 255 # must be <= `invalidSlot`, but this should be tuned. + when n <= thres: + const lookup = genLookup(T) + let lookup2 {.global.} = lookup # xxx improve pending https://github.com/timotheecour/Nim/issues/553 + #[ + This could be optimized using a hash adapted to `T` (possible since it's known at CT) + to get better key distribution before indexing into the lookup table table. + ]# + {.noSideEffect.}: # because it's immutable + let ret = lookup2[ord(a) - T.low.ord] + if ret != invalidSlot: return ret.int + else: + var i = 0 + # we could also generate a case statement as optimization + for ai in items(T): + if ai == a: return i + inc(i) + raise newException(IndexDefect, $ord(a) & " invalid for " & $T) + +template symbolRank*[T: enum](a: T): int = + ## Returns the index in which `a` is listed in `T`. + ## + ## The cost for a `HoleyEnum` is implementation defined, currently optimized + ## for small enums, otherwise is `O(T.enumLen)`. + runnableExamples: + type + A = enum a0 = -3, a1 = 10, a2, a3 = (20, "f3Alt") # HoleyEnum + B = enum b0, b1, b2 # OrdinalEnum + C = enum c0 = 10, c1, c2 # OrdinalEnum + assert a2.symbolRank == 2 + assert b2.symbolRank == 2 + assert c2.symbolRank == 2 + assert c2.ord == 12 + assert a2.ord == 11 + var invalid = 7.A + doAssertRaises(IndexDefect): discard invalid.symbolRank + when T is Ordinal: ord(a) - T.low.ord.static + else: symbolRankImpl(a) + +func symbolName*[T: enum](a: T): string = ## Returns the symbol name of an enum. + ## + ## This uses `symbolRank`. runnableExamples: type B = enum b0 = (10, "kb0") @@ -97,5 +156,7 @@ func symbolName*[T: OrdinalEnum](a: T): string = assert b.symbolName == "b0" assert $b == "kb0" static: assert B.high.symbolName == "b2" + type C = enum c0 = -3, c1 = 4, c2 = 20 # HoleyEnum + assert c1.symbolName == "c1" const names = enumNames(T) - names[a.ord - T.low.ord] + names[a.symbolRank] diff --git a/lib/std/jsonutils.nim b/lib/std/jsonutils.nim index 1f49f60ed..1e222e3a2 100644 --- a/lib/std/jsonutils.nim +++ b/lib/std/jsonutils.nim @@ -31,9 +31,11 @@ add a way to customize serialization, for e.g.: ]# import macros +from enumutils import symbolName +from typetraits import OrdinalEnum type - Joptions* = object + Joptions* = object # xxx rename FromJsonOptions ## Options controlling the behavior of `fromJson`. allowExtraKeys*: bool ## If `true` Nim's object to which the JSON is parsed is not required to @@ -42,6 +44,17 @@ type ## If `true` Nim's object to which JSON is parsed is allowed to have ## fields without corresponding JSON keys. # in future work: a key rename could be added + EnumMode* = enum + joptEnumOrd + joptEnumSymbol + joptEnumString + ToJsonOptions* = object + enumMode*: EnumMode + # xxx charMode + +proc initToJsonOptions*(): ToJsonOptions = + ## initializes `ToJsonOptions` with sane options. + ToJsonOptions(enumMode: joptEnumOrd) proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".} proc distinctBase(T: typedesc): typedesc {.magic: "TypeTrait".} @@ -261,33 +274,41 @@ proc jsonTo*(b: JsonNode, T: typedesc, opt = Joptions()): T = ## reverse of `toJson` fromJson(result, b, opt) -proc toJson*[T](a: T): JsonNode = +proc toJson*[T](a: T, opt = initToJsonOptions()): JsonNode = ## serializes `a` to json; uses `toJsonHook(a: T)` if it's in scope to ## customize serialization, see strtabs.toJsonHook for an example. when compiles(toJsonHook(a)): result = toJsonHook(a) elif T is object | tuple: when T is object or isNamedTuple(T): result = newJObject() - for k, v in a.fieldPairs: result[k] = toJson(v) + for k, v in a.fieldPairs: result[k] = toJson(v, opt) else: result = newJArray() - for v in a.fields: result.add toJson(v) + for v in a.fields: result.add toJson(v, opt) elif T is ref | ptr: if system.`==`(a, nil): result = newJNull() - else: result = toJson(a[]) + else: result = toJson(a[], opt) elif T is array | seq | set: result = newJArray() - for ai in a: result.add toJson(ai) - elif T is pointer: result = toJson(cast[int](a)) + for ai in a: result.add toJson(ai, opt) + elif T is pointer: result = toJson(cast[int](a), opt) # edge case: `a == nil` could've also led to `newJNull()`, but this results # in simpler code for `toJson` and `fromJson`. - elif T is distinct: result = toJson(a.distinctBase) + elif T is distinct: result = toJson(a.distinctBase, opt) elif T is bool: result = %(a) elif T is SomeInteger: result = %a - elif T is Ordinal: result = %(a.ord) elif T is enum: - when defined(nimLegacyJsonutilsHoleyEnum): result = %a - else: result = %(a.ord) + case opt.enumMode + of joptEnumOrd: + when T is Ordinal or not defined(nimLegacyJsonutilsHoleyEnum): %(a.ord) + else: toJson($a, opt) + of joptEnumSymbol: + when T is OrdinalEnum: + toJson(symbolName(a), opt) + else: + toJson($a, opt) + of joptEnumString: toJson($a, opt) + elif T is Ordinal: result = %(a.ord) elif T is cstring: (if a == nil: result = newJNull() else: result = % $a) else: result = %a diff --git a/tests/stdlib/tjsonutils.nim b/tests/stdlib/tjsonutils.nim index c826a79b0..a55b0ca1d 100644 --- a/tests/stdlib/tjsonutils.nim +++ b/tests/stdlib/tjsonutils.nim @@ -35,6 +35,13 @@ type Foo = ref object proc `==`(a, b: Foo): bool = a.id == b.id +type MyEnum = enum me0, me1 = "me1Alt", me2, me3, me4 + +proc `$`(a: MyEnum): string = + # putting this here pending https://github.com/nim-lang/Nim/issues/13747 + if a == me2: "me2Modif" + else: system.`$`(a) + template fn() = block: # toJson, jsonTo type Foo = distinct float @@ -83,6 +90,12 @@ template fn() = doAssert b2.ord == 1 # explains the `1` testRoundtrip(a): """[1,2,3]""" + block: # ToJsonOptions + let a = (me1, me2) + doAssert $a.toJson() == "[1,2]" + doAssert $a.toJson(ToJsonOptions(enumMode: joptEnumSymbol)) == """["me1","me2"]""" + doAssert $a.toJson(ToJsonOptions(enumMode: joptEnumString)) == """["me1Alt","me2Modif"]""" + block: # set type Foo = enum f1, f2, f3, f4, f5 type Goo = enum g1 = 10, g2 = 15, g3 = 17, g4 From 31143c68fc7438406df39207b30594e775b4f97e Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 18 May 2021 10:48:13 -0700 Subject: [PATCH 2121/3768] disable pkg/fidget refs https://github.com/treeform/fidget/issues/155 (#18043) --- testament/important_packages.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 1a61d3bbf..b84155eee 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -61,7 +61,7 @@ pkg "delaunay" pkg "docopt" pkg "easygl", "nim c -o:egl -r src/easygl.nim", "https://github.com/jackmott/easygl" pkg "elvis" -pkg "fidget" +pkg "fidget", allowFailure = true # pending https://github.com/treeform/fidget/issues/155 pkg "fragments", "nim c -r fragments/dsl.nim" pkg "fusion" pkg "gara" From 53935b8b27643215b677cc152f563a7fb7cb84fb Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 18 May 2021 21:45:37 +0200 Subject: [PATCH 2122/3768] ARC: fixes memory leaks with newSeq used in a loop [backport:1.4] (#18040) * ARC: fixes memory leaks with newSeq used in a loop [backport:1.4] * Update tests/arc/tnewseq_legacy.nim --- compiler/ccgexprs.nim | 7 ++++++- lib/system/seqs_v2.nim | 4 ++++ tests/arc/tnewseq_legacy.nim | 13 +++++++++++++ 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 tests/arc/tnewseq_legacy.nim diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index b5ad24cc1..7cb403a94 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2319,7 +2319,12 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = gcUsage(p.config, e) else: genNewFinalize(p, e) - of mNewSeq: genNewSeq(p, e) + of mNewSeq: + if optSeqDestructors in p.config.globalOptions: + e[1] = makeAddr(e[1], p.module.idgen) + genCall(p, e, d) + else: + genNewSeq(p, e) of mNewSeqOfCap: genNewSeqOfCap(p, e, d) of mSizeOf: let t = e[1].typ.skipTypes({tyTypeDesc}) diff --git a/lib/system/seqs_v2.nim b/lib/system/seqs_v2.nim index b7f24ecd5..375eef340 100644 --- a/lib/system/seqs_v2.nim +++ b/lib/system/seqs_v2.nim @@ -124,3 +124,7 @@ proc setLen[T](s: var seq[T], newlen: Natural) = if xu.p == nil or xu.p.cap < newlen: xu.p = cast[typeof(xu.p)](prepareSeqAdd(oldLen, xu.p, newlen - oldLen, sizeof(T), alignof(T))) xu.len = newlen + +proc newSeq[T](s: var seq[T], len: Natural) = + shrink(s, 0) + setLen(s, len) diff --git a/tests/arc/tnewseq_legacy.nim b/tests/arc/tnewseq_legacy.nim new file mode 100644 index 000000000..4730d2c2b --- /dev/null +++ b/tests/arc/tnewseq_legacy.nim @@ -0,0 +1,13 @@ +discard """ + output: "(allocCount: 201, deallocCount: 201)" + cmd: "nim c --gc:orc -d:nimAllocStats $file" +""" + +proc main(prefix: string) = + var c: seq[string] + for i in 0..<100: + newSeq(c, 100) + c[i] = prefix & $i + +main("abc") +echo getAllocStats() From 558644725ddcb72ae4e0b34ad5cfb1d38e2c5385 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 19 May 2021 16:00:14 +0200 Subject: [PATCH 2123/3768] fixes #17943 (#18045) * fixes #17943 * IC: test 'nim check --ic:on' for the full Nim compiler * Update testament/testament.nim Co-authored-by: Clyybber Co-authored-by: Clyybber --- compiler/ic/ic.nim | 3 +++ testament/categories.nim | 33 +++++++++++++++++++-------------- testament/testament.nim | 4 ++-- 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/compiler/ic/ic.nim b/compiler/ic/ic.nim index 6530cb6c2..bd3be27f0 100644 --- a/compiler/ic/ic.nim +++ b/compiler/ic/ic.nim @@ -507,6 +507,9 @@ proc toPackedNodeIgnoreProcDefs(n: PNode, encoder: var PackedEncoder; m: var Pac of nkStmtList, nkStmtListExpr: for it in n: toPackedNodeIgnoreProcDefs(it, encoder, m) + of nkImportStmt, nkImportExceptStmt, nkExportStmt, nkExportExceptStmt, + nkFromStmt, nkIncludeStmt: + discard "nothing to do" else: toPackedNode(n, m.topLevel, encoder, m) diff --git a/testament/categories.nim b/testament/categories.nim index 1f0bd6acf..28ede1ba0 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -502,7 +502,7 @@ proc testNimblePackages(r: var TResults; cat: Category; packageFilter: string) = proc icTests(r: var TResults; testsDir: string, cat: Category, options: string; isNavigatorTest: bool) = const - tooltests = ["compiler/nim.nim", "tools/nimgrep.nim"] + tooltests = ["compiler/nim.nim"] writeOnly = " --incremental:writeonly " readOnly = " --incremental:readonly " incrementalOn = " --incremental:on -d:nimIcIntegrityChecks " @@ -518,6 +518,24 @@ proc icTests(r: var TResults; testsDir: string, cat: Category, options: string; test.spec.targets = {getTestSpecTarget()} testSpecWithNimcache(r, test, nimcache) + template checkTest() = + var test = makeRawTest(file, options, cat) + test.spec.cmd = compilerPrefix & " check --hint[Conf]:off --warnings:off --ic:on $options " & file + testSpecWithNimcache(r, test, nimcache) + + if not isNavigatorTest: + for file in tooltests: + let nimcache = nimcacheDir(file, options, getTestSpecTarget()) + removeDir(nimcache) + + let oldPassed = r.passed + checkTest() + + if r.passed == oldPassed+1: + checkTest() + if r.passed == oldPassed+2: + checkTest() + const tempExt = "_temp.nim" for it in walkDirRec(testsDir): if isTestFile(it) and not it.endsWith(tempExt): @@ -532,19 +550,6 @@ proc icTests(r: var TResults; testsDir: string, cat: Category, options: string; editedTest(if isNavigatorTest: navTestConfig else: incrementalOn) if r.passed != oldPassed+1: break - if not isNavigatorTest and false: - for file in tooltests: - let nimcache = nimcacheDir(file, options, getTestSpecTarget()) - removeDir(nimcache) - - let oldPassed = r.passed - test writeOnly - - if r.passed == oldPassed+1: - test readOnly - if r.passed == oldPassed+2: - test readOnly - # ---------------------------------------------------------------------------- const AdditionalCategories = ["debugger", "examples", "lib", "ic", "navigator"] diff --git a/testament/testament.nim b/testament/testament.nim index 076123882..a8dc0557c 100644 --- a/testament/testament.nim +++ b/testament/testament.nim @@ -500,7 +500,7 @@ proc equalModuloLastNewline(a, b: string): bool = proc testSpecHelper(r: var TResults, test: var TTest, expected: TSpec, target: TTarget, nimcache: string, extraOptions = "") = test.startTime = epochTime() - template callNimCompilerImpl(): untyped = + template callNimCompilerImpl(): untyped = # xxx this used to also pass: `--stdout --hint:Path:off`, but was done inconsistently # with other branches callNimCompiler(expected.getCmd, test.name, test.options, nimcache, target, extraOptions) @@ -649,9 +649,9 @@ proc makeRawTest(test, options: string, cat: Category): TTest {.used.} = result.name = test result.options = options result.spec = initSpec(addFileExt(test, ".nim")) - result.startTime = epochTime() result.spec.action = actionCompile result.spec.targets = {getTestSpecTarget()} + result.startTime = epochTime() # TODO: fix these files const disabledFilesDefault = @[ From 7052503ca8d360f11fabf476f7450dc3f794a602 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 19 May 2021 19:18:16 +0200 Subject: [PATCH 2124/3768] make strformat part of the prelude (#18046) --- changelog.md | 3 +++ lib/pure/prelude.nim | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 97e749337..f9c48e0bf 100644 --- a/changelog.md +++ b/changelog.md @@ -73,6 +73,9 @@ - `json` and `jsonutils` now serialize NaN, Inf, -Inf as strings, so that `%[NaN, -Inf]` is the string `["nan","-inf"]` instead of `[nan,-inf]` which was invalid json. +- `strformat` is now part of `include std/prelude`. + + ## Standard library additions and changes - Added support for parenthesized expressions in `strformat` diff --git a/lib/pure/prelude.nim b/lib/pure/prelude.nim index f1728b5f7..3c98d9d57 100644 --- a/lib/pure/prelude.nim +++ b/lib/pure/prelude.nim @@ -25,4 +25,4 @@ when defined(nimdoc) and isMainModule: # xxx `nim doc -b:js -d:nodejs --doccmd:-d:nodejs lib/pure/prelude.nim` fails for some reason # specific to `nim doc`, but the code otherwise works with nodejs. -import std/[os, strutils, times, parseutils, hashes, tables, sets, sequtils, parseopt] +import std/[os, strutils, times, parseutils, hashes, tables, sets, sequtils, parseopt, strformat] From a1c82c39af812b54cd3dd1e472c9088457fb7dc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C8=98tefan=20Talpalaru?= Date: Wed, 19 May 2021 19:19:11 +0200 Subject: [PATCH 2125/3768] asyncdispatch+stackTraceOverride: fix premature collection (#18039) [backport:1.2] Copying StackTraceEntry instances when nimStackTraceOverride is defined breaks the link between a cstring field that's supposed to point at another string field in the same object. Sometimes, the original object is garbage collected, that memory region reused for storing other strings, so when the StackTraceEntry copy tries to use its cstring pointer to construct a traceback message, it accesses unrelated strings. This only happens for async tracebacks and this patch prevents that by making sure we only use the string fields when nimStackTraceOverride is defined. Async tracebacks also beautified slightly by getting rid of an extra line that was supposed to be commented out, along with the corresponding debugging output. There's also a micro-optimisation to avoid concatenating two strings just to get their combined length. --- changelog.md | 1 + lib/pure/asyncfutures.nim | 46 ++++++++++++++++++++++---------- lib/system/exceptions.nim | 2 +- tests/async/tasync_traceback.nim | 4 +-- 4 files changed, 36 insertions(+), 17 deletions(-) diff --git a/changelog.md b/changelog.md index f9c48e0bf..992cd89c0 100644 --- a/changelog.md +++ b/changelog.md @@ -316,6 +316,7 @@ - Added `copyWithin` [for `seq` and `array` for JavaScript targets](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/copyWithin). +- Fixed premature garbage collection in asyncdispatch, when a stack trace override is in place. ## Language changes diff --git a/lib/pure/asyncfutures.nim b/lib/pure/asyncfutures.nim index 0178ee4d6..af3fbb3ec 100644 --- a/lib/pure/asyncfutures.nim +++ b/lib/pure/asyncfutures.nim @@ -278,19 +278,33 @@ proc `callback=`*[T](future: Future[T], ## If future has already completed then `cb` will be called immediately. future.callback = proc () = cb(future) +template getFilenameProcname(entry: StackTraceEntry): (string, string) = + when compiles(entry.filenameStr) and compiles(entry.procnameStr): + # We can't rely on "entry.filename" and "entry.procname" still being valid + # cstring pointers, because the "string.data" buffers they pointed to might + # be already garbage collected (this entry being a non-shallow copy, + # "entry.filename" no longer points to "entry.filenameStr.data", but to the + # buffer of the original object). + (entry.filenameStr, entry.procnameStr) + else: + ($entry.filename, $entry.procname) + proc getHint(entry: StackTraceEntry): string = ## We try to provide some hints about stack trace entries that the user ## may not be familiar with, in particular calls inside the stdlib. + + let (filename, procname) = getFilenameProcname(entry) + result = "" - if entry.procname == cstring"processPendingCallbacks": - if cmpIgnoreStyle(entry.filename, "asyncdispatch.nim") == 0: + if procname == "processPendingCallbacks": + if cmpIgnoreStyle(filename, "asyncdispatch.nim") == 0: return "Executes pending callbacks" - elif entry.procname == cstring"poll": - if cmpIgnoreStyle(entry.filename, "asyncdispatch.nim") == 0: + elif procname == "poll": + if cmpIgnoreStyle(filename, "asyncdispatch.nim") == 0: return "Processes asynchronous completion events" - if entry.procname.endsWith(NimAsyncContinueSuffix): - if cmpIgnoreStyle(entry.filename, "asyncmacro.nim") == 0: + if procname.endsWith(NimAsyncContinueSuffix): + if cmpIgnoreStyle(filename, "asyncmacro.nim") == 0: return "Resumes an async procedure" proc `$`*(stackTraceEntries: seq[StackTraceEntry]): string = @@ -303,16 +317,20 @@ proc `$`*(stackTraceEntries: seq[StackTraceEntry]): string = # Find longest filename & line number combo for alignment purposes. var longestLeft = 0 for entry in entries: - if entry.procname.isNil: continue + let (filename, procname) = getFilenameProcname(entry) - let left = $entry.filename & $entry.line - if left.len > longestLeft: - longestLeft = left.len + if procname == "": continue + + let leftLen = filename.len + len($entry.line) + if leftLen > longestLeft: + longestLeft = leftLen var indent = 2 # Format the entries. for entry in entries: - if entry.procname.isNil: + let (filename, procname) = getFilenameProcname(entry) + + if procname == "": if entry.line == reraisedFromBegin: result.add(spaces(indent) & "#[\n") indent.inc(2) @@ -321,11 +339,11 @@ proc `$`*(stackTraceEntries: seq[StackTraceEntry]): string = result.add(spaces(indent) & "]#\n") continue - let left = "$#($#)" % [$entry.filename, $entry.line] + let left = "$#($#)" % [filename, $entry.line] result.add((spaces(indent) & "$#$# $#\n") % [ left, spaces(longestLeft - left.len + 2), - $entry.procname + procname ]) let hint = getHint(entry) if hint.len > 0: @@ -349,9 +367,9 @@ proc injectStacktrace[T](future: Future[T]) = newMsg.add($entries) newMsg.add("Exception message: " & exceptionMsg & "\n") - newMsg.add("Exception type:") # # For debugging purposes + # newMsg.add("Exception type:") # for entry in getStackTraceEntries(future.error): # newMsg.add "\n" & $entry future.error.msg = newMsg diff --git a/lib/system/exceptions.nim b/lib/system/exceptions.nim index b5f4fc325..5dcd77bd0 100644 --- a/lib/system/exceptions.nim +++ b/lib/system/exceptions.nim @@ -29,7 +29,7 @@ type programCounter*: uint ## Program counter - will be used to get the rest of the info, ## when `$` is called on this type. We can't use ## "cuintptr_t" in here. - procnameStr*, filenameStr*: string ## GC-ed objects holding the cstrings in "procname" and "filename" + procnameStr*, filenameStr*: string ## GC-ed alternatives to "procname" and "filename" Exception* {.compilerproc, magic: "Exception".} = object of RootObj ## \ ## Base exception class. diff --git a/tests/async/tasync_traceback.nim b/tests/async/tasync_traceback.nim index 9f787929b..cd16b2257 100644 --- a/tests/async/tasync_traceback.nim +++ b/tests/async/tasync_traceback.nim @@ -86,7 +86,7 @@ Async traceback: asyncfutures\.nim\(\d+?\)\s+?read \]# Exception message: b failure -Exception type: + bar failure Async traceback: @@ -114,7 +114,7 @@ Async traceback: asyncfutures\.nim\(\d+?\)\s+?read \]# Exception message: bar failure -Exception type: + """ # TODO: is asyncmacro good enough location for fooIter traceback/debugging? just put the callsite info for all? From df429fa28772e077faa30dd6e3a701abf48c7669 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 20 May 2021 11:52:46 +0200 Subject: [PATCH 2126/3768] config system: special case -d:release and -d:danger [backport:1.4] (#18051) --- changelog.md | 4 ++++ compiler/commands.nim | 19 +++++++++++++++---- compiler/main.nim | 3 ++- config/nim.cfg | 2 ++ tests/newconfig/tfoo.nim | 4 ++-- tests/newconfig/tfoo.nims | 2 ++ 6 files changed, 27 insertions(+), 7 deletions(-) diff --git a/changelog.md b/changelog.md index 992cd89c0..4c30d67a7 100644 --- a/changelog.md +++ b/changelog.md @@ -75,6 +75,10 @@ - `strformat` is now part of `include std/prelude`. +- The configuration subsystem now allows for `-d:release` and `-d:danger` to work as expected. + The downside is that these defines now have custom logic that doesn't apply for + other defines. + ## Standard library additions and changes - Added support for parenthesized expressions in `strformat` diff --git a/compiler/commands.nim b/compiler/commands.nim index b9c8e9eb3..605e2930c 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -482,6 +482,19 @@ proc setCommandEarly*(conf: ConfigRef, command: string) = else: conf.foreignPackageNotes = foreignPackageNotesDefault +proc specialDefine(conf: ConfigRef, key: string) = + # Keep this syncronized with the default config/nim.cfg! + if cmpIgnoreStyle(key, "nimQuirky") == 0: + conf.exc = excQuirky + elif cmpIgnoreStyle(key, "release") == 0 or cmpIgnoreStyle(key, "danger") == 0: + conf.options.excl {optStackTrace, optLineTrace, optLineDir, optOptimizeSize} + conf.globalOptions.excl {optExcessiveStackTrace, optCDebug} + conf.options.incl optOptimizeSpeed + if cmpIgnoreStyle(key, "danger") == 0 or cmpIgnoreStyle(key, "quick") == 0: + conf.options.excl {optObjCheck, optFieldCheck, optRangeCheck, optBoundsCheck, + optOverflowCheck, optAssert, optStackTrace, optLineTrace, optLineDir} + conf.globalOptions.excl {optCDebug} + proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; conf: ConfigRef) = var @@ -548,12 +561,10 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; expectArg(conf, switch, arg, pass, info) if {':', '='} in arg: splitSwitch(conf, arg, key, val, pass, info) - if cmpIgnoreStyle(key, "nimQuirky") == 0: - conf.exc = excQuirky + specialDefine(conf, key) defineSymbol(conf.symbols, key, val) else: - if cmpIgnoreStyle(arg, "nimQuirky") == 0: - conf.exc = excQuirky + specialDefine(conf, arg) defineSymbol(conf.symbols, arg) of "undef", "u": expectArg(conf, switch, arg, pass, info) diff --git a/compiler/main.nim b/compiler/main.nim index e2b5f4b67..71e549ab8 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -417,7 +417,8 @@ proc mainCommand*(graph: ModuleGraph) = "project", project, "output", output, ]) - rawMessage(conf, hintBuildMode, build) + if conf.cmd in cmdBackends: + rawMessage(conf, hintBuildMode, build) when PrintRopeCacheStats: echo "rope cache stats: " diff --git a/config/nim.cfg b/config/nim.cfg index d3ab75825..3b964d124 100644 --- a/config/nim.cfg +++ b/config/nim.cfg @@ -50,6 +50,7 @@ path="$lib/pure" @end nimblepath="$home/.nimble/pkgs/" +# Syncronize with compiler/commands.specialDefine @if danger or quick: obj_checks:off field_checks:off @@ -63,6 +64,7 @@ nimblepath="$home/.nimble/pkgs/" line_dir:off @end +# Syncronize with compiler/commands.specialDefine @if release or danger: stacktrace:off excessiveStackTrace:off diff --git a/tests/newconfig/tfoo.nim b/tests/newconfig/tfoo.nim index 6654202c5..0c6ded470 100644 --- a/tests/newconfig/tfoo.nim +++ b/tests/newconfig/tfoo.nim @@ -1,6 +1,6 @@ discard """ cmd: "nim default --hint:cc:off --hint:cc $file" - output: '''hello world! 0.5''' + output: '''hello world! 0.5 true''' nimout: '''[NimScript] exec: gcc -v''' """ @@ -10,4 +10,4 @@ when not defined(definedefine): import math, mfriends discard gen[int]() -echo "hello world! ", ln 2.0 +echo "hello world! ", ln 2.0, " ", compileOption("opt", "speed") diff --git a/tests/newconfig/tfoo.nims b/tests/newconfig/tfoo.nims index 6f0048afb..0cb950227 100644 --- a/tests/newconfig/tfoo.nims +++ b/tests/newconfig/tfoo.nims @@ -3,6 +3,8 @@ mode = ScriptMode.Whatif exec "gcc -v" +--define:release + --forceBuild --path: "../friends" From 6a5973882bb0c4134a0e454ea4ae69dc54815f27 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Thu, 20 May 2021 10:48:46 -0300 Subject: [PATCH 2127/3768] jsgen improve spacing (#18048) * Fix inconsistent spacings in generated JS of jsgen --- compiler/jsgen.nim | 60 ++++++++++++++++++++++---------------------- compiler/jstypes.nim | 4 +-- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 53ce45e96..3b183b9d6 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -322,7 +322,7 @@ proc isSimpleExpr(p: PProc; n: PNode): bool = proc getTemp(p: PProc, defineInLocals: bool = true): Rope = inc(p.unique) - result = "Tmp$1" % [rope(p.unique)] + result = "Temporary$1" % [rope(p.unique)] if defineInLocals: p.locals.add(p.indentLine("var $1;$n" % [result])) @@ -489,12 +489,12 @@ proc maybeMakeTempAssignable(p: PProc, n: PNode; x: TCompRes): tuple[a, tmp: Rop if optBoundsCheck in p.options: useMagic(p, "chckIndx") if first == 0: # save a couple chars - index.res = "chckIndx($1, 0, ($2).length-1)" % [index.res, tmp1] + index.res = "chckIndx($1, 0, ($2).length - 1)" % [index.res, tmp1] else: - index.res = "chckIndx($1, $2, ($3).length+($2)-1)-($2)" % [ + index.res = "chckIndx($1, $2, ($3).length + ($2) - 1) - ($2)" % [ index.res, rope(first), tmp1] elif first != 0: - index.res = "($1)-($2)" % [index.res, rope(first)] + index.res = "($1) - ($2)" % [index.res, rope(first)] else: discard # index.res = index.res let (n1, tmp2) = maybeMakeTemp(p, n[1], index) @@ -607,12 +607,12 @@ proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = if n[1].typ.size <= 4: applyFormat("($1 << $2)", "($1 << $2)") else: - applyFormat("($1 * Math.pow(2,$2))", "($1 * Math.pow(2,$2))") + applyFormat("($1 * Math.pow(2, $2))", "($1 * Math.pow(2, $2))") of mAshrI: if n[1].typ.size <= 4: applyFormat("($1 >> $2)", "($1 >> $2)") else: - applyFormat("Math.floor($1 / Math.pow(2,$2))", "Math.floor($1 / Math.pow(2,$2))") + applyFormat("Math.floor($1 / Math.pow(2, $2))", "Math.floor($1 / Math.pow(2, $2))") of mBitandI: applyFormat("($1 & $2)", "($1 & $2)") of mBitorI: applyFormat("($1 | $2)", "($1 | $2)") of mBitxorI: applyFormat("($1 ^ $2)", "($1 ^ $2)") @@ -656,8 +656,8 @@ proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = of mUnaryMinusF64: applyFormat("-($1)", "-($1)") of mCharToStr: applyFormat("nimCharToStr($1)", "nimCharToStr($1)") of mBoolToStr: applyFormat("nimBoolToStr($1)", "nimBoolToStr($1)") - of mIntToStr: applyFormat("cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")") - of mInt64ToStr: applyFormat("cstrToNimstr(($1)+\"\")", "cstrToNimstr(($1)+\"\")") + of mIntToStr: applyFormat("cstrToNimstr(($1) + \"\")", "cstrToNimstr(($1) + \"\")") + of mInt64ToStr: applyFormat("cstrToNimstr(($1) + \"\")", "cstrToNimstr(($1) + \"\")") of mFloatToStr: useMagic(p, "nimFloatToString") applyFormat "cstrToNimstr(nimFloatToString($1))" @@ -704,7 +704,7 @@ proc hasFrameInfo(p: PProc): bool = ((p.prc == nil) or not (sfPure in p.prc.flags)) proc lineDir(config: ConfigRef, info: TLineInfo, line: int): Rope = - ropes.`%`("// line $2 \"$1\"$n", + ropes.`%`("/* line $2 \"$1\" */$n", [rope(toFullPath(config, info)), rope(line)]) proc genLineDir(p: PProc, n: PNode) = @@ -725,9 +725,9 @@ proc genWhileStmt(p: PProc, n: PNode) = p.blocks[^1].id = -p.unique p.blocks[^1].isLoop = true let labl = p.unique.rope - lineF(p, "L$1: while (true) {$n", [labl]) + lineF(p, "Label$1: while (true) {$n", [labl]) p.nested: gen(p, n[0], cond) - lineF(p, "if (!$1) break L$2;$n", + lineF(p, "if (!$1) break Label$2;$n", [cond.res, labl]) p.nested: genStmt(p, n[1]) lineF(p, "}$n", [labl]) @@ -750,8 +750,8 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = # try { # stmts; # --excHandler; - # } catch (EXC) { - # var prevJSError = lastJSError; lastJSError = EXC; + # } catch (EXCEPTION) { + # var prevJSError = lastJSError; lastJSError = EXCEPTION; # framePtr = tmpFramePtr; # --excHandler; # if (e.typ && e.typ == NTI433 || e.typ == NTI2321) { @@ -785,8 +785,8 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = moveInto(p, a, r) var generalCatchBranchExists = false if catchBranchesExist: - p.body.addf("--excHandler;$n} catch (EXC) {$n var prevJSError = lastJSError;$n" & - " lastJSError = EXC;$n --excHandler;$n", []) + p.body.addf("--excHandler;$n} catch (EXCEPTION) {$n var prevJSError = lastJSError;$n" & + " lastJSError = EXCEPTION;$n --excHandler;$n", []) line(p, "framePtr = $1;$n" % [tmpFramePtr]) while i < n.len and n[i].kind == nkExceptBranch: if n[i].len == 1: @@ -928,12 +928,12 @@ proc genBlock(p: PProc, n: PNode, r: var TCompRes) = sym.loc.k = locOther sym.position = idx+1 let labl = p.unique - lineF(p, "L$1: do {$n", [labl.rope]) + lineF(p, "Label$1: do {$n", [labl.rope]) setLen(p.blocks, idx + 1) p.blocks[idx].id = - p.unique # negative because it isn't used yet gen(p, n[1], r) setLen(p.blocks, idx) - lineF(p, "} while(false);$n", [labl.rope]) + lineF(p, "} while (false);$n", [labl.rope]) proc genBreakStmt(p: PProc, n: PNode) = var idx: int @@ -951,7 +951,7 @@ proc genBreakStmt(p: PProc, n: PNode) = if idx < 0 or not p.blocks[idx].isLoop: internalError(p.config, n.info, "no loop to break") p.blocks[idx].id = abs(p.blocks[idx].id) # label is used - lineF(p, "break L$1;$n", [rope(p.blocks[idx].id)]) + lineF(p, "break Label$1;$n", [rope(p.blocks[idx].id)]) proc genAsmOrEmitStmt(p: PProc, n: PNode) = genLineDir(p, n) @@ -1248,12 +1248,12 @@ proc genArrayAddr(p: PProc, n: PNode, r: var TCompRes) = if optBoundsCheck in p.options: useMagic(p, "chckIndx") if first == 0: # save a couple chars - r.res = "chckIndx($1, 0, ($2).length-1)" % [b.res, tmp] + r.res = "chckIndx($1, 0, ($2).length - 1)" % [b.res, tmp] else: - r.res = "chckIndx($1, $2, ($3).length+($2)-1)-($2)" % [ + r.res = "chckIndx($1, $2, ($3).length + ($2) - 1) - ($2)" % [ b.res, rope(first), tmp] elif first != 0: - r.res = "($1)-($2)" % [b.res, rope(first)] + r.res = "($1) - ($2)" % [b.res, rope(first)] else: r.res = b.res r.kind = resExpr @@ -1876,13 +1876,13 @@ proc genNewSeq(p: PProc, n: PNode) = gen(p, n[1], x) gen(p, n[2], y) let t = skipTypes(n[1].typ, abstractVar)[0] - lineF(p, "$1 = new Array($2); for (var i=0;i<$2;++i) {$1[i]=$3;}", [ + lineF(p, "$1 = new Array($2); for (var i = 0 ; i < $2 ; ++i) { $1[i] = $3; }", [ x.rdLoc, y.rdLoc, createVar(p, t, false)]) proc genOrd(p: PProc, n: PNode, r: var TCompRes) = case skipTypes(n[1].typ, abstractVar + abstractRange).kind of tyEnum, tyInt..tyUInt64, tyChar: gen(p, n[1], r) - of tyBool: unaryExpr(p, n, r, "", "($1 ? 1:0)") + of tyBool: unaryExpr(p, n, r, "", "($1 ? 1 : 0)") else: internalError(p.config, n.info, "genOrd") proc genConStrStr(p: PProc, n: PNode, r: var TCompRes) = @@ -2122,7 +2122,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = let t = skipTypes(n[1].typ, abstractVar)[0] let (a, tmp) = maybeMakeTemp(p, n[1], x) let (b, tmp2) = maybeMakeTemp(p, n[2], y) - r.res = """if ($1.length < $2) { for (var i=$4.length;i<$5;++i) $4.push($3); } + r.res = """if ($1.length < $2) { for (var i = $4.length ; i < $5 ; ++i) $4.push($3); } else { $4.length = $5; }""" % [a, b, createVar(p, t, false), tmp, tmp2] r.kind = resExpr of mCard: unaryExpr(p, n, r, "SetCard", "SetCard($1)") @@ -2159,7 +2159,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = gen(p, n[1], x) gen(p, n[2], y) gen(p, n[3], z) - r.res = "($1.slice($2, $3+1))" % [x.rdLoc, y.rdLoc, z.rdLoc] + r.res = "($1.slice($2, $3 + 1))" % [x.rdLoc, y.rdLoc, z.rdLoc] r.kind = resExpr of mMove: genMove(p, n, r) @@ -2281,7 +2281,7 @@ proc genConv(p: PProc, n: PNode, r: var TCompRes) = r.res = "(!!($1))" % [r.res] r.kind = resExpr elif toInt: - r.res = "(($1)|0)" % [r.res] + r.res = "(($1) | 0)" % [r.res] else: # TODO: What types must we handle here? discard @@ -2337,7 +2337,7 @@ proc genReturnStmt(p: PProc, n: PNode) = proc frameCreate(p: PProc; procname, filename: Rope): Rope = const frameFmt = - "var F={procname:$1,prev:framePtr,filename:$2,line:0};$n" + "var F = {procname: $1, prev: framePtr, filename: $2, line: 0};$n" result = p.indentLine(frameFmt % [procname, filename]) result.add p.indentLine(ropes.`%`("framePtr = F;$n", [])) @@ -2431,10 +2431,10 @@ proc genProc(oldProc: PProc, prc: PSym): Rope = # references will end up calling the reloaded code. var thunkName = name name = name & "IMLP" - result.add("function $#() { return $#.apply(this, arguments); }$n" % + result.add("\Lfunction $#() { return $#.apply(this, arguments); }$n" % [thunkName, name]) - def = "function $#($#) {$n$#$#$#$#$#" % + def = "\Lfunction $#($#) {$n$#$#$#$#$#" % [ name, header, optionalLine(p.globals), @@ -2482,7 +2482,7 @@ proc genCast(p: PProc, n: PNode, r: var TCompRes) = elif fromUint: if src.size == 4 and dest.size == 4: # XXX prevent multi evaluations - r.res = "($1|0)" % [r.res] + r.res = "($1 | 0)" % [r.res] else: let trimmer = unsignedTrimmer(dest.size) let minuend = case dest.size diff --git a/compiler/jstypes.nim b/compiler/jstypes.nim index 81f03dc12..5b684b60c 100644 --- a/compiler/jstypes.nim +++ b/compiler/jstypes.nim @@ -135,14 +135,14 @@ proc genTypeInfo(p: PProc, typ: PType): Rope = prepend(p.g.typeInfo, s) of tyVar, tyLent, tyRef, tyPtr, tySequence, tyRange, tySet: var s = - "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" % + "var $1 = {size: 0, kind: $2, base: null, node: null, finalizer: null};$n" % [result, rope(ord(t.kind))] prepend(p.g.typeInfo, s) p.g.typeInfo.addf("$1.base = $2;$n", [result, genTypeInfo(p, t.lastSon)]) of tyArray: var s = - "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" % + "var $1 = {size: 0, kind: $2, base: null, node: null, finalizer: null};$n" % [result, rope(ord(t.kind))] prepend(p.g.typeInfo, s) p.g.typeInfo.addf("$1.base = $2;$n", From 9f7e2e30573a377770fa630f12a47bac09751282 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Fri, 21 May 2021 07:54:20 +0300 Subject: [PATCH 2128/3768] docs: make inline markup more compatible with Markdown (#18053) fixes https://github.com/timotheecour/Nim/issues/739 --- doc/regexprs.txt | 2 +- lib/impure/db_sqlite.nim | 2 +- lib/packages/docutils/rst.nim | 147 ++++++++++++++++++++++------------ lib/posix/posix_utils.nim | 2 +- tests/stdlib/trst.nim | 65 +++++++++++++-- tests/stdlib/trstgen.nim | 12 +-- 6 files changed, 168 insertions(+), 62 deletions(-) diff --git a/doc/regexprs.txt b/doc/regexprs.txt index b7370d858..9ec08b810 100644 --- a/doc/regexprs.txt +++ b/doc/regexprs.txt @@ -146,7 +146,7 @@ character meaning After ``\x``, from zero to two hexadecimal digits are read (letters can be in upper or lower case). In UTF-8 mode, any number of hexadecimal digits may appear between ``\x{`` and ``}``, but the value of the character code must be -less than 2**31 (that is, the maximum hexadecimal value is 7FFFFFFF). If +less than 2^31 (that is, the maximum hexadecimal value is 7FFFFFFF). If characters other than hexadecimal digits appear between ``\x{`` and ``}``, or if there is no terminating ``}``, this form of escape is not recognized. Instead, the initial ``\x`` will be interpreted as a basic hexadecimal escape, diff --git a/lib/impure/db_sqlite.nim b/lib/impure/db_sqlite.nim index 832407960..7bd807a12 100644 --- a/lib/impure/db_sqlite.nim +++ b/lib/impure/db_sqlite.nim @@ -152,7 +152,7 @@ ## Instead, a `seq[string]` is returned for each row. ## ## The reasoning is as follows: -## 1. it's close to what many DBs offer natively (char**) +## 1. it's close to what many DBs offer natively (`char**`:c:) ## 2. it hides the number of types that the DB supports ## (int? int64? decimal up to 10 places? geo coords?) ## 3. it's convenient when all you do is to forward the data to somewhere else (echo, log, put the data into a new query) diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index da04e9b54..dae692fb7 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -130,6 +130,32 @@ ## .. warning:: Using Nim-specific features can cause other RST implementations ## to fail on your document. ## +## Idiosyncrasies +## -------------- +## +## Currently we do **not** aim at 100% Markdown or RST compatibility in inline +## markup recognition rules because that would provide very little user value. +## This parser has 2 modes for inline markup: +## +## 1) Markdown-like mode which is enabled by `roPreferMarkdown` option +## (turned **on** by default). +## +## .. Note:: RST features like directives are still turned **on** +## +## 2) Compatibility mode which is RST rules. +## +## .. Note:: in both modes the parser interpretes text between single +## backticks (code) identically: +## backslash does not escape; the only exception: ``\`` folowed by ` +## does escape so that we can always input a single backtick ` in +## inline code. However that makes impossible to input code with +## ``\`` at the end in *single* backticks, one must use *double* +## backticks:: +## +## `\` -- WRONG +## ``\`` -- GOOD +## So single backticks can always be input: `\`` will turn to ` code +## ## Limitations ## ----------- ## @@ -994,8 +1020,22 @@ proc expect(p: var RstParser, tok: string) = if currentTok(p).symbol == tok: inc p.idx else: rstMessage(p, meExpected, tok) -proc isInlineMarkupEnd(p: RstParser, markup: string, exact: bool): bool = +proc inlineMarkdownEnd(p: RstParser): bool = + result = prevTok(p).kind notin {tkIndent, tkWhite} + ## (For a special case of ` we don't allow spaces surrounding it + ## unlike original Markdown because this behavior confusing/useless) + +proc inlineRstEnd(p: RstParser): bool = # rst rules: https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#inline-markup-recognition-rules + # Rule 2: + result = prevTok(p).kind notin {tkIndent, tkWhite} + if not result: return + # Rule 7: + result = nextTok(p).kind in {tkIndent, tkWhite, tkEof} or + nextTok(p).symbol[0] in + {'\'', '\"', ')', ']', '}', '>', '-', '/', '\\', ':', '.', ',', ';', '!', '?', '_'} + +proc isInlineMarkupEnd(p: RstParser, markup: string, exact: bool): bool = if exact: result = currentTok(p).symbol == markup else: @@ -1004,55 +1044,58 @@ proc isInlineMarkupEnd(p: RstParser, markup: string, exact: bool): bool = # check that escaping may have splitted `` to 2 tokens ` and ` result = currentTok(p).symbol == "`" and prevTok(p).symbol == "`" if not result: return - # Rule 2: - result = prevTok(p).kind notin {tkIndent, tkWhite} - if not result: return - # Rule 7: - result = nextTok(p).kind in {tkIndent, tkWhite, tkEof} or - (roPreferMarkdown in p.s.options and - markup in ["``", "`"] and - nextTok(p).kind in {tkIndent, tkWhite, tkWord, tkEof}) or - nextTok(p).symbol[0] in - {'\'', '\"', ')', ']', '}', '>', '-', '/', '\\', ':', '.', ',', ';', '!', '?', '_'} - if not result: return - # Rule 4: - if p.idx > 0: - # see bug #17260; for now `\` must be written ``\``, likewise with sequences - # ending in an un-escaped `\`; `\\` is legal but not `\\\` for example; - # for this reason we can't use `["``", "`"]` here. - if markup != "``" and prevTok(p).symbol == "\\": - result = false + # surroundings check + if markup in ["_", "__"]: + result = inlineRstEnd(p) + else: + if roPreferMarkdown in p.s.options: result = inlineMarkdownEnd(p) + else: result = inlineRstEnd(p) -proc isInlineMarkupStart(p: RstParser, markup: string): bool = - # rst rules: https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#inline-markup-recognition-rules - var d: char - if markup != "_`": - result = currentTok(p).symbol == markup - else: # _` is a 2 token case - result = currentTok(p).symbol == "_" and nextTok(p).symbol == "`" +proc rstRuleSurround(p: RstParser): bool = + result = true + # Rules 4 & 5: + if p.idx > 0: + var d: char + var c = prevTok(p).symbol[0] + case c + of '\'', '\"': d = c + of '(': d = ')' + of '[': d = ']' + of '{': d = '}' + of '<': d = '>' + else: d = '\0' + if d != '\0': result = nextTok(p).symbol[0] != d + +proc inlineMarkdownStart(p: RstParser): bool = + result = nextTok(p).kind notin {tkIndent, tkWhite, tkEof} if not result: return - # Rule 6: + # this rst rule is really nice, let us use it in Markdown mode too. + result = rstRuleSurround(p) + +proc inlineRstStart(p: RstParser): bool = + ## rst rules: https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#inline-markup-recognition-rules + # Rule 6 result = p.idx == 0 or prevTok(p).kind in {tkIndent, tkWhite} or - (markup in ["``", "`"] and prevTok(p).kind in {tkIndent, tkWhite, tkWord}) or prevTok(p).symbol[0] in {'\'', '\"', '(', '[', '{', '<', '-', '/', ':', '_'} if not result: return # Rule 1: result = nextTok(p).kind notin {tkIndent, tkWhite, tkEof} if not result: return - # Rules 4 & 5: - if p.idx > 0: - if prevTok(p).symbol == "\\": - result = false - else: - var c = prevTok(p).symbol[0] - case c - of '\'', '\"': d = c - of '(': d = ')' - of '[': d = ']' - of '{': d = '}' - of '<': d = '>' - else: d = '\0' - if d != '\0': result = nextTok(p).symbol[0] != d + result = rstRuleSurround(p) + +proc isInlineMarkupStart(p: RstParser, markup: string): bool = + if markup != "_`": + result = currentTok(p).symbol == markup + else: # _` is a 2 token case + result = currentTok(p).symbol == "_" and nextTok(p).symbol == "`" + if not result: return + # surroundings check + if markup in ["_", "__", "[", "|"]: + # Note: we require space/punctuation even before [markdown link](...) + result = inlineRstStart(p) + else: + if roPreferMarkdown in p.s.options: result = inlineMarkdownStart(p) + else: result = inlineRstStart(p) proc match(p: RstParser, start: int, expr: string): bool = # regular expressions are: @@ -1263,10 +1306,7 @@ proc parseWordOrRef(p: var RstParser, father: PRstNode) = proc parseBackslash(p: var RstParser, father: PRstNode) = assert(currentTok(p).kind == tkPunct) - if currentTok(p).symbol == "\\\\": - father.add newLeaf("\\") - inc p.idx - elif currentTok(p).symbol == "\\": + if currentTok(p).symbol == "\\": # XXX: Unicode? inc p.idx if currentTok(p).kind != tkWhite: father.add(newLeaf(p)) @@ -1297,11 +1337,20 @@ proc parseUntil(p: var RstParser, father: PRstNode, postfix: string, break else: if postfix == "`": - if prevTok(p).symbol == "\\" and currentTok(p).symbol == "`": - father.sons[^1] = newLeaf(p) # instead, we should use lookahead + if currentTok(p).symbol == "\\": + if nextTok(p).symbol == "\\": + father.add newLeaf("\\") + father.add newLeaf("\\") + inc p.idx, 2 + elif nextTok(p).symbol == "`": # escape ` + father.add newLeaf("`") + inc p.idx, 2 + else: + father.add newLeaf("\\") + inc p.idx else: father.add(newLeaf(p)) - inc p.idx + inc p.idx else: if interpretBackslash: parseBackslash(p, father) diff --git a/lib/posix/posix_utils.nim b/lib/posix/posix_utils.nim index aeec73a45..c2d5aab56 100644 --- a/lib/posix/posix_utils.nim +++ b/lib/posix/posix_utils.nim @@ -7,7 +7,7 @@ # ## A set of helpers for the POSIX module. -## Raw interfaces are in the other posix*.nim files. +## Raw interfaces are in the other ``posix*.nim`` files. # Where possible, contribute OS-independent procs in `os `_ instead. diff --git a/tests/stdlib/trst.nim b/tests/stdlib/trst.nim index 71f5a858b..ec34edc91 100644 --- a/tests/stdlib/trst.nim +++ b/tests/stdlib/trst.nim @@ -23,7 +23,7 @@ import std/private/miscdollars import os proc toAst(input: string, - rstOptions: RstParseOptions = {roSupportMarkdown, roNimFile}, + rstOptions: RstParseOptions = {roPreferMarkdown, roSupportMarkdown, roNimFile}, error: ref string = nil, warnings: ref seq[string] = nil): string = ## If `error` is nil then no errors should be generated. @@ -36,10 +36,11 @@ proc toAst(input: string, toLocation(message, filename, line, col + ColRstOffset) message.add " $1: $2" % [$mc, a] if mc == mcError: - doAssert error != nil, "unexpected RST error '" & message & "'" + if error == nil: + raise newException(EParseError, "[unexpected error] " & message) error[] = message # we check only first error because subsequent ones may be meaningless - raise newException(EParseError, message) + raise newException(EParseError, "") else: doAssert warnings != nil, "unexpected RST warning '" & message & "'" warnings[].add message @@ -54,8 +55,9 @@ proc toAst(input: string, var rst = rstParse(input, filen, line=LineRstInit, column=ColRstInit, dummyHasToc, rstOptions, myFindFile, testMsgHandler) result = renderRstToStr(rst) - except EParseError: - discard + except EParseError as e: + if e.msg != "": + result = e.msg suite "RST parsing": test "option list has priority over definition list": @@ -326,6 +328,28 @@ suite "RST escaping": """) suite "RST inline markup": + test "* and ** surrounded by spaces are not inline markup": + check("a * b * c ** d ** e".toAst == dedent""" + rnInner + rnLeaf 'a' + rnLeaf ' ' + rnLeaf '*' + rnLeaf ' ' + rnLeaf 'b' + rnLeaf ' ' + rnLeaf '*' + rnLeaf ' ' + rnLeaf 'c' + rnLeaf ' ' + rnLeaf '**' + rnLeaf ' ' + rnLeaf 'd' + rnLeaf ' ' + rnLeaf '**' + rnLeaf ' ' + rnLeaf 'e' + """) + test "end-string has repeating symbols": check("*emphasis content****".toAst == dedent""" rnEmphasis @@ -420,6 +444,37 @@ suite "RST inline markup": rnLeaf 'proc `+`' """) + check("""`\\`""".toAst == + dedent""" + rnInlineCode + rnDirArg + rnLeaf 'nim' + [nil] + rnLiteralBlock + rnLeaf '\\' + """) + + test "Markdown-style code/backtick": + # no whitespace is required before ` + check("`try`...`except`".toAst == + dedent""" + rnInner + rnInlineCode + rnDirArg + rnLeaf 'nim' + [nil] + rnLiteralBlock + rnLeaf 'try' + rnLeaf '...' + rnInlineCode + rnDirArg + rnLeaf 'nim' + [nil] + rnLiteralBlock + rnLeaf 'except' + """) + + test """inline literals can contain \ anywhere""": check("""``\``""".toAst == dedent""" rnInlineLiteral diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim index 667fec780..864728686 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -10,7 +10,7 @@ import unittest, strutils, strtabs import std/private/miscdollars proc toHtml(input: string, - rstOptions: RstParseOptions = {roSupportMarkdown, roNimFile}, + rstOptions: RstParseOptions = {roPreferMarkdown, roSupportMarkdown, roNimFile}, error: ref string = nil, warnings: ref seq[string] = nil): string = ## If `error` is nil then no errors should be generated. @@ -23,18 +23,20 @@ proc toHtml(input: string, toLocation(message, filename, line, col + ColRstOffset) message.add " $1: $2" % [$mc, a] if mc == mcError: - doAssert error != nil, "unexpected RST error '" & message & "'" + if error == nil: + raise newException(EParseError, "[unexpected error] " & message) error[] = message # we check only first error because subsequent ones may be meaningless - raise newException(EParseError, message) + raise newException(EParseError, "") else: doAssert warnings != nil, "unexpected RST warning '" & message & "'" warnings[].add message try: result = rstToHtml(input, rstOptions, defaultConfig(), msgHandler=testMsgHandler) - except EParseError: - discard + except EParseError as e: + if e.msg != "": + result = e.msg # inline code tags (for parsing originated from highlite.nim) proc id(str: string): string = """""" & str & "" From e12597589f2a8cdaabe09e8b2b92ff0fbe3727ef Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Sat, 22 May 2021 08:12:30 -0300 Subject: [PATCH 2129/3768] Error message minor (#18021) * Update compiler/semtypes.nim Co-authored-by: Timothee Cour Co-authored-by: flywind Co-authored-by: Andreas Rumpf --- compiler/lineinfos.nim | 2 +- compiler/semtypes.nim | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index 47cddb888..bf81aed02 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -110,7 +110,7 @@ const warnFieldXNotSupported: "field '$1' not supported", warnRstStyle: "RST style: $1", warnCommentXIgnored: "comment '$1' ignored", - warnTypelessParam: "'$1' has no type. Typeless parameters are deprecated; only allowed for 'template'", + warnTypelessParam: "", # deadcode warnUseBase: "use {.base.} for base methods; baseless methods are deprecated", warnWriteToForeignHeap: "write to foreign heap", warnUnsafeCode: "unsafe code: '$1'", diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 97e37f8b7..84556ca1a 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1297,7 +1297,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, let param = strTableGet(c.signatures, arg.name) if param != nil: typ = param.typ else: - localError(c.config, a.info, "typeless parameters are obsolete") + localError(c.config, a.info, "parameter '$1' requires a type" % param.name.s) typ = errorType(c) let lifted = liftParamType(c, kind, genericParams, typ, arg.name.s, arg.info) From 1636c05d138a7a52c5b6cb3cafe6ab84f59f086b Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 23 May 2021 01:02:04 -0700 Subject: [PATCH 2130/3768] close #5540 generic object with generic field evaluated too early (#18062) --- tests/misc/t5540.nim | 45 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 tests/misc/t5540.nim diff --git a/tests/misc/t5540.nim b/tests/misc/t5540.nim new file mode 100644 index 000000000..6a19e70e1 --- /dev/null +++ b/tests/misc/t5540.nim @@ -0,0 +1,45 @@ +# bug #5540; works in 1.2.0 +# fails in 1.0 (Error: cannot generate VM code for) +# fails in 0.18.0 (Error: type mismatch: got ) + +block: + type + Fruit = object + Yellow = object + a: int + template getColor(x: typedesc[Fruit]): typedesc = Yellow + type + Banana[T] = object + b: T + a: getColor(Fruit) + Apple[T] = object + a: T + b: getColor(T) + block: + var x: Banana[int] + doAssert x.b == 0 + doAssert x.a is Yellow + block: + var x: Apple[Fruit] + doAssert x.b is Yellow + +block: + type + Fruit = object + Yellow = object + a: int + + template getColor(x: typedesc[Fruit]): typedesc = Yellow + + type + Banana[T] = object + b: T + a: getColor(Fruit) + + Apple[T] = object + a: T + b: getColor(T) + + var x: Banana[int] + x.b = 13 + x.a.a = 17 From 61630c6aee56acf688c48c00f5a6711a59e79616 Mon Sep 17 00:00:00 2001 From: Euan Date: Sun, 23 May 2021 17:23:20 +0100 Subject: [PATCH 2131/3768] Use gmake for NetBSD as well as FreeBSD/OpenBSD (#18064) --- ci/funs.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ci/funs.sh b/ci/funs.sh index cad005246..0e4f0eb3b 100644 --- a/ci/funs.sh +++ b/ci/funs.sh @@ -55,6 +55,8 @@ _nimBuildCsourcesIfNeeded(){ makeX=gmake elif [ "$unamestr" = 'OpenBSD' ]; then makeX=gmake + elif [ "$unamestr" = 'NetBSD' ]; then + makeX=gmake else makeX=make fi From 1421a3bf265e45cea7c26dcaf3be148ea02db5f0 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 23 May 2021 12:28:26 -0700 Subject: [PATCH 2132/3768] pkg/pixie: use latest (#18069) --- testament/important_packages.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index b84155eee..975dd63ad 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -118,7 +118,7 @@ pkg "optionsutils" pkg "ormin", "nim c -o:orminn ormin.nim" pkg "parsetoml" pkg "patty" -pkg "pixie", useHead = false +pkg "pixie" pkg "plotly", "nim c examples/all.nim" pkg "pnm" pkg "polypbren" From d217888e5679aff063668930bd00892c4f5cb2b3 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 23 May 2021 14:06:14 -0700 Subject: [PATCH 2133/3768] close #18009 parseJson JInt vs JFloat; preserve -0.0 as JFloat to distinguish from 0.0 (#18067) --- lib/pure/json.nim | 2 +- tests/stdlib/tjsonutils.nim | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 3a9fa5898..d5bbb86b8 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -938,7 +938,7 @@ when defined(js): of "[object Array]": return JArray of "[object Object]": return JObject of "[object Number]": - if isInteger(x): + if isInteger(x) and 1.0 / cast[float](x) != -Inf: # preserve -0.0 as float if isSafeInteger(x): return JInt else: diff --git a/tests/stdlib/tjsonutils.nim b/tests/stdlib/tjsonutils.nim index a55b0ca1d..62486b896 100644 --- a/tests/stdlib/tjsonutils.nim +++ b/tests/stdlib/tjsonutils.nim @@ -4,7 +4,8 @@ discard """ import std/jsonutils import std/json -from std/math import isNaN +from std/math import isNaN, signbit +from stdtest/testutils import whenRuntimeJs proc testRoundtrip[T](t: T, expected: string) = # checks that `T => json => T2 => json2` is such that json2 = json @@ -123,6 +124,20 @@ template fn() = testRoundtripVal((Inf, -Inf, 0.0, -0.0, 1.0)): """["inf","-inf",0.0,-0.0,1.0]""" doAssert ($NaN.toJson).parseJson.jsonTo(float).isNaN + block: # bug #18009; unfixable unless we change parseJson (which would have overhead), + # but at least we can guarantee that the distinction between 0.0 and -0.0 is preserved. + let a = (0, 0.0, -0.0, 0.5, 1, 1.0) + testRoundtripVal(a): "[0,0.0,-0.0,0.5,1,1.0]" + let a2 = $($a.toJson).parseJson + whenRuntimeJs: + doAssert a2 == "[0,0,-0.0,0.5,1,1]" + do: + doAssert a2 == "[0,0.0,-0.0,0.5,1,1.0]" + let b = a2.parseJson.jsonTo(type(a)) + doAssert not b[1].signbit + doAssert b[2].signbit + doAssert not b[3].signbit + block: # case object type Foo = object x0: float From 478f717377c4cd60cfce112b8b21d58031b118b4 Mon Sep 17 00:00:00 2001 From: xioren <40043405+xioren@users.noreply.github.com> Date: Mon, 24 May 2021 20:56:31 -0700 Subject: [PATCH 2134/3768] Move async example to asynchronous version of proc (#18078) * improve runnableExamples in std/httpclient * Add synchronous example. * Update lib/pure/httpclient.nim Co-authored-by: Timothee Cour --- lib/pure/httpclient.nim | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 86708dc40..0dbf8a045 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -574,15 +574,11 @@ proc newHttpClient*(userAgent = defUserAgent, maxRedirects = 5, ## ## `headers` specifies the HTTP Headers. runnableExamples: - import std/[asyncdispatch, httpclient, strutils] + import std/strutils - proc asyncProc(): Future[string] {.async.} = - var client = newAsyncHttpClient() - return await client.getContent("http://example.com") - - let exampleHtml = waitFor asyncProc() + let exampleHtml = newHttpClient().getContent("http://example.com") assert "Example Domain" in exampleHtml - assert not ("Pizza" in exampleHtml) + assert "Pizza" notin exampleHtml new result result.headers = headers @@ -616,6 +612,17 @@ proc newAsyncHttpClient*(userAgent = defUserAgent, maxRedirects = 5, ## connections. ## ## `headers` specifies the HTTP Headers. + runnableExamples: + import std/[asyncdispatch, strutils] + + proc asyncProc(): Future[string] {.async.} = + let client = newAsyncHttpClient() + result = await client.getContent("http://example.com") + + let exampleHtml = waitFor asyncProc() + assert "Example Domain" in exampleHtml + assert "Pizza" notin exampleHtml + new result result.headers = headers result.userAgent = userAgent From b59dc3b255d778cefdbef2c0c3ff55d43892c11c Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 26 May 2021 00:41:50 -0700 Subject: [PATCH 2135/3768] remove some custom logic in testament around flags, testExec (#18090) * remove some custom logic in testament around flags, testExec * remove testExec, custom logic around flags from testament * fixup --- testament/categories.nim | 23 ----------------------- testament/testament.nim | 16 ---------------- tests/flags/tgenscript.nim | 6 ------ tests/misc/trunner.nim | 33 ++++++++++++++++++++++++++++++--- 4 files changed, 30 insertions(+), 48 deletions(-) delete mode 100644 tests/flags/tgenscript.nim diff --git a/testament/categories.nim b/testament/categories.nim index 28ede1ba0..510e90c53 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -23,7 +23,6 @@ const "debugger", "dll", "examples", - "flags", "gc", "io", "js", @@ -48,26 +47,6 @@ proc isTestFile*(file: string): bool = let (_, name, ext) = splitFile(file) result = ext == ".nim" and name.startsWith("t") -# --------------------- flags tests ------------------------------------------- - -proc flagTests(r: var TResults, cat: Category, options: string) = - # --genscript - const filename = testsDir/"flags"/"tgenscript" - const genopts = " --genscript" - let nimcache = nimcacheDir(filename, genopts, targetC) - testSpec r, makeTest(filename, genopts, cat) - - when defined(windows): - testExec r, makeTest(filename, " cmd /c cd " & nimcache & - " && compile_tgenscript.bat", cat) - - elif defined(posix): - testExec r, makeTest(filename, " sh -c \"cd " & nimcache & - " && sh compile_tgenscript.sh\"", cat) - - # Run - testExec r, makeTest(filename, " " & nimcache / "tgenscript", cat) - # --------------------- DLL generation tests ---------------------------------- proc runBasicDLLTest(c, r: var TResults, cat: Category, options: string) = @@ -705,8 +684,6 @@ proc processCategory(r: var TResults, cat: Category, jsTests(r, cat, options) of "dll": dllTests(r, cat, options) - of "flags": - flagTests(r, cat, options) of "gc": gcTests(r, cat, options) of "longgc": diff --git a/testament/testament.nim b/testament/testament.nim index a8dc0557c..2edefdb01 100644 --- a/testament/testament.nim +++ b/testament/testament.nim @@ -621,22 +621,6 @@ proc testC(r: var TResults, test: TTest, action: TTestAction) = if exitCode != 0: given.err = reExitcodesDiffer if given.err == reSuccess: inc(r.passed) -proc testExec(r: var TResults, test: TTest) = - # runs executable or script, just goes by exit code - if not checkDisabled(r, test): return - - inc(r.total) - let (outp, errC) = execCmdEx(test.options.strip()) - var given: TSpec - if errC == 0: - given.err = reSuccess - else: - given.err = reExitcodesDiffer - given.msg = outp - - if given.err == reSuccess: inc(r.passed) - r.addResult(test, targetC, "", given.msg, given.err) - proc makeTest(test, options: string, cat: Category): TTest = result.cat = cat result.name = test diff --git a/tests/flags/tgenscript.nim b/tests/flags/tgenscript.nim deleted file mode 100644 index d58395a40..000000000 --- a/tests/flags/tgenscript.nim +++ /dev/null @@ -1,6 +0,0 @@ -discard """ - targets: "c" - action: compile -""" - -echo "--genscript" diff --git a/tests/misc/trunner.nim b/tests/misc/trunner.nim index 1b679d92c..3297b3a24 100644 --- a/tests/misc/trunner.nim +++ b/tests/misc/trunner.nim @@ -33,16 +33,23 @@ const proc runNimCmd(file, options = "", rtarg = ""): auto = let fileabs = testsDir / file.unixToNativePath - doAssert fileabs.fileExists, fileabs + # doAssert fileabs.fileExists, fileabs # disabled because this allows passing `nim r --eval:code fakefile` let cmd = fmt"{nim} {mode} {options} --hints:off {fileabs} {rtarg}" result = execCmdEx(cmd) - when false: echo result[0] & "\n" & result[1] # for debugging + when false: # for debugging + echo cmd + echo result[0] & "\n" & $result[1] proc runNimCmdChk(file, options = "", rtarg = ""): string = let (ret, status) = runNimCmd(file, options, rtarg = rtarg) doAssert status == 0, $(file, options) & "\n" & ret ret +proc genShellCmd(filename: string): string = + let filename = filename.quoteShell + when defined(windows): "cmd /c " & filename # or "cmd /c " ? + else: "sh " & filename + when defined(nimTrunnerFfi): block: # mevalffi when defined(openbsd): @@ -71,7 +78,9 @@ foo:0.03:asdf:103:105 ret=[s1:foobar s2:foobar age:25 pi:3.14] """, output -else: # don't run twice the same test +elif not defined(nimTestsTrunnerDebugging): + # don't run twice the same test with `nimTrunnerFfi` + # use `-d:nimTestsTrunnerDebugging` for debugging convenience when you want to just run 1 test import std/strutils import std/json template check2(msg) = doAssert msg in output, output @@ -330,3 +339,21 @@ running: v2 doAssert "D20210428T161003" in j["defined_symbols"].to(seq[string]) doAssert j["version"].to(string) == NimVersion doAssert j["nimExe"].to(string) == getCurrentCompilerExe() + + block: # genscript + const nimcache2 = buildDir / "D20210524T212851" + removeDir(nimcache2) + let input = "tgenscript_fakefile" # no need for a real file, --eval is good enough + let output = runNimCmdChk(input, fmt"""--genscript --nimcache:{nimcache2.quoteShell} --eval:"echo(12345)" """) + doAssert output.len == 0, output + let ext = when defined(windows): ".bat" else: ".sh" + let filename = fmt"compile_{input}{ext}" # synchronize with `generateScript` + doAssert fileExists(nimcache2/filename), nimcache2/filename + let (outp, status) = execCmdEx(genShellCmd(filename), options = {poStdErrToStdOut}, workingDir = nimcache2) + doAssert status == 0, outp + let (outp2, status2) = execCmdEx(nimcache2 / input, options = {poStdErrToStdOut}) + doAssert outp2 == "12345\n", outp2 + doAssert status2 == 0 + +else: + discard # only during debugging, tests added here will run with `-d:nimTestsTrunnerDebugging` enabled From c495628255330a559e3398663cc8872cca18b04d Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 26 May 2021 00:43:30 -0700 Subject: [PATCH 2136/3768] refactor common code in CI pipelines (#18035) * refactor CI with nimInternalInstallDepsWindows * refactor CI with nimInternalBuildKochAndRunCI * fixup --- .builds/freebsd.yml | 6 +----- .builds/openbsd_0.yml | 6 +----- .builds/openbsd_1.yml | 6 +----- .github/workflows/ci_docs.yml | 8 +++----- .github/workflows/ci_packages.yml | 22 ++++++---------------- azure-pipelines.yml | 16 ++++------------ ci/funs.sh | 17 +++++++++++++++++ tools/ci_generate.nim | 6 +----- 8 files changed, 34 insertions(+), 53 deletions(-) diff --git a/.builds/freebsd.yml b/.builds/freebsd.yml index 8f336771a..15d09dda0 100644 --- a/.builds/freebsd.yml +++ b/.builds/freebsd.yml @@ -22,15 +22,11 @@ tasks: set -e cd Nim . ci/funs.sh && nimBuildCsourcesIfNeeded - $nim_csources c --skipUserCfg --skipParentCfg koch echo 'export PATH=$HOME/Nim/bin:$PATH' >> $HOME/.buildenv - test: | set -e cd Nim - if ! ./koch runCI; then - nim r tools/ci_testresults.nim - exit 1 - fi + . ci/funs.sh && nimInternalBuildKochAndRunCI triggers: - action: email condition: failure diff --git a/.builds/openbsd_0.yml b/.builds/openbsd_0.yml index c19cd63c6..c3b2fd43e 100644 --- a/.builds/openbsd_0.yml +++ b/.builds/openbsd_0.yml @@ -22,15 +22,11 @@ tasks: set -e cd Nim . ci/funs.sh && nimBuildCsourcesIfNeeded - $nim_csources c --skipUserCfg --skipParentCfg koch echo 'export PATH=$HOME/Nim/bin:$PATH' >> $HOME/.buildenv - test: | set -e cd Nim - if ! ./koch runCI; then - nim r tools/ci_testresults.nim - exit 1 - fi + . ci/funs.sh && nimInternalBuildKochAndRunCI triggers: - action: email condition: failure diff --git a/.builds/openbsd_1.yml b/.builds/openbsd_1.yml index 06f5f2ae5..e98ec4640 100644 --- a/.builds/openbsd_1.yml +++ b/.builds/openbsd_1.yml @@ -22,15 +22,11 @@ tasks: set -e cd Nim . ci/funs.sh && nimBuildCsourcesIfNeeded - $nim_csources c --skipUserCfg --skipParentCfg koch echo 'export PATH=$HOME/Nim/bin:$PATH' >> $HOME/.buildenv - test: | set -e cd Nim - if ! ./koch runCI; then - nim r tools/ci_testresults.nim - exit 1 - fi + . ci/funs.sh && nimInternalBuildKochAndRunCI triggers: - action: email condition: failure diff --git a/.github/workflows/ci_docs.yml b/.github/workflows/ci_docs.yml index 364c05051..ddef94fbe 100644 --- a/.github/workflows/ci_docs.yml +++ b/.github/workflows/ci_docs.yml @@ -59,11 +59,9 @@ jobs: if: runner.os == 'Windows' shell: bash run: | - mkdir dist - curl -L https://nim-lang.org/download/mingw64.7z -o dist/mingw64.7z - curl -L https://nim-lang.org/download/dlls.zip -o dist/dlls.zip - 7z x dist/mingw64.7z -odist - 7z x dist/dlls.zip -obin + set -e + . ci/funs.sh + nimInternalInstallDepsWindows echo "${{ github.workspace }}/dist/mingw64/bin" >> "${GITHUB_PATH}" - name: 'Add build binaries to PATH' diff --git a/.github/workflows/ci_packages.yml b/.github/workflows/ci_packages.yml index d033aba10..aeb29e147 100644 --- a/.github/workflows/ci_packages.yml +++ b/.github/workflows/ci_packages.yml @@ -40,12 +40,10 @@ jobs: if: runner.os == 'Windows' shell: bash run: | - mkdir dist - curl -L https://nim-lang.org/download/mingw64.7z -o dist/mingw64.7z - curl -L https://nim-lang.org/download/dlls.zip -o dist/dlls.zip - 7z x dist/mingw64.7z -odist - 7z x dist/dlls.zip -obin - echo "${{ github.workspace }}/dist/mingw64/bin" >> "${GITHUB_PATH}" + set -e + . ci/funs.sh + nimInternalInstallDepsWindows + echo_run echo "${{ github.workspace }}/dist/mingw64/bin" >> "${GITHUB_PATH}" - name: 'Add build binaries to PATH' shell: bash @@ -59,14 +57,6 @@ jobs: shell: bash run: . ci/funs.sh && nimBuildCsourcesIfNeeded CC=gcc ucpu='${{ matrix.cpu }}' - - name: 'Build koch' + - name: 'koch, Run CI' shell: bash - run: nim c koch - - name: 'Run CI' - shell: bash - run: ./koch runCI - - - name: 'Show failed tests' - if: failure() - shell: bash - run: nim c -r tools/ci_testresults.nim + run: . ci/funs.sh && nimInternalBuildKochAndRunCI diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 112fb0a62..f0c3b2d0a 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -133,11 +133,7 @@ jobs: - bash: | set -e . ci/funs.sh - echo_run mkdir dist - echo_run curl -L https://nim-lang.org/download/mingw64.7z -o dist/mingw64.7z - echo_run curl -L https://nim-lang.org/download/dlls.zip -o dist/dlls.zip - echo_run 7z x dist/mingw64.7z -odist - echo_run 7z x dist/dlls.zip -obin + nimInternalInstallDepsWindows echo_run echo '##vso[task.prependpath]$(System.DefaultWorkingDirectory)/dist/mingw64/bin' displayName: 'Install dependencies (Windows)' @@ -163,13 +159,9 @@ jobs: # condition: and(succeeded(), eq(variables['skipci'], 'false')) # displayName: 'Restore built csourcesAny' - - bash: nim c koch + - bash: . ci/funs.sh && nimInternalBuildKochAndRunCI + # would could also show on failure: echo '##vso[task.complete result=Failed]' condition: and(succeeded(), eq(variables['skipci'], 'false')) - displayName: 'Build koch' - - # set result to omit the "bash exited with error code '1'" message - - bash: ./koch runCI || echo '##vso[task.complete result=Failed]' - condition: and(succeeded(), eq(variables['skipci'], 'false')) - displayName: 'Run CI' + displayName: 'koch, Run CI' env: SYSTEM_ACCESSTOKEN: $(System.AccessToken) diff --git a/ci/funs.sh b/ci/funs.sh index 0e4f0eb3b..9772535be 100644 --- a/ci/funs.sh +++ b/ci/funs.sh @@ -31,6 +31,23 @@ nimIsCiSkip(){ fi } +nimInternalInstallDepsWindows(){ + echo_run mkdir dist + echo_run curl -L https://nim-lang.org/download/mingw64.7z -o dist/mingw64.7z + echo_run curl -L https://nim-lang.org/download/dlls.zip -o dist/dlls.zip + echo_run 7z x dist/mingw64.7z -odist + echo_run 7z x dist/dlls.zip -obin +} + +nimInternalBuildKochAndRunCI(){ + echo_run nim c koch + if ! echo_run ./koch runCI; then + echo_run echo "runCI failed" + echo_run nim r -tools/ci_testresults.nim + return 1 + fi +} + nimDefineVars(){ . config/build_config.txt nim_csources=bin/nim_csources_$nim_csourcesHash diff --git a/tools/ci_generate.nim b/tools/ci_generate.nim index f08cd5409..52b84f0d8 100644 --- a/tools/ci_generate.nim +++ b/tools/ci_generate.nim @@ -29,15 +29,11 @@ tasks: set -e cd Nim . ci/funs.sh && nimBuildCsourcesIfNeeded - $nim_csources c --skipUserCfg --skipParentCfg koch echo 'export PATH=$HOME/Nim/bin:$PATH' >> $HOME/.buildenv - test: | set -e cd Nim - if ! ./koch runCI; then - nim r tools/ci_testresults.nim - exit 1 - fi + . ci/funs.sh && nimInternalBuildKochAndRunCI triggers: - action: email condition: failure From 8df55d0ad735277683631bb8e5966ee75545de2b Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 26 May 2021 00:44:00 -0700 Subject: [PATCH 2137/3768] close #3482 no more cgen error with typed templates (#18094) --- tests/misc/t3482.nim | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 tests/misc/t3482.nim diff --git a/tests/misc/t3482.nim b/tests/misc/t3482.nim new file mode 100644 index 000000000..33b3b8f40 --- /dev/null +++ b/tests/misc/t3482.nim @@ -0,0 +1,15 @@ +discard """ + action: reject + nimout: "t3482.nim(13, 8) Error: undeclared identifier: 'output'" +""" +# bug #3482 (correct behavior since 1.4.0, cgen error in 1.2.0) +template foo*(body: typed) = + if true: + body + +proc test = + foo: + var output = "" + echo output.len + +test() From 0b2bbcaa23f91b06bc06dd2cf35d4d26a6d24aa8 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 26 May 2021 00:46:53 -0700 Subject: [PATCH 2138/3768] fix #18077 testament now parses `cmd` properly (#18086) --- testament/categories.nim | 3 ++- testament/testament.nim | 53 ++++++++++++++++++++++++++++------------ tests/misc/t18077.nim | 21 ++++++++++++++++ 3 files changed, 61 insertions(+), 16 deletions(-) create mode 100644 tests/misc/t18077.nim diff --git a/testament/categories.nim b/testament/categories.nim index 510e90c53..8cf90666c 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -87,8 +87,9 @@ proc runBasicDLLTest(c, r: var TResults, cat: Category, options: string) = var hcri = makeTest("tests/dll/nimhcr_integration.nim", options & " --forceBuild --hotCodeReloading:on" & rpath, cat) let nimcache = nimcacheDir(hcri.name, hcri.options, getTestSpecTarget()) - hcri.args = prepareTestArgs(hcri.spec.getCmd, hcri.name, + let cmd = prepareTestCmd(hcri.spec.getCmd, hcri.name, hcri.options, nimcache, getTestSpecTarget()) + hcri.testArgs = cmd.parseCmdLine testSpec r, hcri proc dllTests(r: var TResults, cat: Category, options: string) = diff --git a/testament/testament.nim b/testament/testament.nim index 2edefdb01..1eb925122 100644 --- a/testament/testament.nim +++ b/testament/testament.nim @@ -85,7 +85,7 @@ type name: string cat: Category options: string - args: seq[string] + testArgs: seq[string] spec: TSpec startTime: float debugInfo: string @@ -154,24 +154,23 @@ proc nimcacheDir(filename, options: string, target: TTarget): string = let hashInput = options & $target result = "nimcache" / (filename & '_' & hashInput.getMD5) -proc prepareTestArgs(cmdTemplate, filename, options, nimcache: string, - target: TTarget, extraOptions = ""): seq[string] = +proc prepareTestCmd(cmdTemplate, filename, options, nimcache: string, + target: TTarget, extraOptions = ""): string = var options = target.defaultOptions & ' ' & options - # improve pending https://github.com/nim-lang/Nim/issues/14343 - if nimcache.len > 0: options.add ' ' & ("--nimCache:" & nimcache).quoteShell + if nimcache.len > 0: options.add(" --nimCache:$#" % nimcache.quoteShell) options.add ' ' & extraOptions - result = parseCmdLine(cmdTemplate % ["target", targetToCmd[target], + # we avoid using `parseCmdLine` which is buggy, refs bug #14343 + result = cmdTemplate % ["target", targetToCmd[target], "options", options, "file", filename.quoteShell, - "filedir", filename.getFileDir(), "nim", compilerPrefix]) + "filedir", filename.getFileDir(), "nim", compilerPrefix] proc callNimCompiler(cmdTemplate, filename, options, nimcache: string, target: TTarget, extraOptions = ""): TSpec = - let c = prepareTestArgs(cmdTemplate, filename, options, nimcache, target, + result.cmd = prepareTestCmd(cmdTemplate, filename, options, nimcache, target, extraOptions) - result.cmd = quoteShellCommand(c) - verboseCmd(c.quoteShellCommand) - var p = startProcess(command = c[0], args = c[1 .. ^1], - options = {poStdErrToStdOut, poUsePath}) + verboseCmd(result.cmd) + var p = startProcess(command = result.cmd, + options = {poStdErrToStdOut, poUsePath, poEvalCommand}) let outp = p.outputStream var foundSuccessMsg = false var foundErrorMsg = false @@ -224,9 +223,33 @@ proc callNimCompiler(cmdTemplate, filename, options, nimcache: string, proc callCCompiler(cmdTemplate, filename, options: string, target: TTarget): TSpec = - let c = prepareTestArgs(cmdTemplate, filename, options, nimcache = "", target) + let cmd = prepareTestCmd(cmdTemplate, filename, options, nimcache = "", target) + doAssert false + #[ + this code hasn't been run in a while, and should be removed which simplifies code + there are better ways to do this anyways (e.g. running c code from a nim file) + + the only place where this is called is: + `testC r, makeTest("tests/realtimeGC/cmain", cOptions, cat), actionRun` + which isn't run unless you call: + XDG_CONFIG_HOME= nim r --lib:lib --stacktrace:on testament/testament.nim r longgc + + and this fails since at least nim 1.0 with: + testament/testament.nim(851) testament + testament/testament.nim(822) main + testament/categories.nim(713) processCategory + testament/categories.nim(189) longGCTests + testament/testament.nim(644) makeTest + testament/specs.nim(251) parseSpec + testament/specs.nim(184) extractSpec + lib/system/io.nim(861) readFile + Error: unhandled exception: cannot open: tests/realtimeGC/cmain.nim [IOError] + + Also, `c[5 .. ^1]` is too magical. + ]# + let c = cmd.parseCmdLine var p = startProcess(command = "gcc", args = c[5 .. ^1], - options = {poStdErrToStdOut, poUsePath}) + options = {poStdErrToStdOut, poUsePath, poEvalCommand}) let outp = p.outputStream var x = newStringOfCap(120) result.nimout = "" @@ -525,7 +548,7 @@ proc testSpecHelper(r: var TResults, test: var TTest, expected: TSpec, reExeNotFound) else: var exeCmd: string - var args = test.args + var args = test.testArgs if isJsTarget: exeCmd = nodejs # see D20210217T215950 diff --git a/tests/misc/t18077.nim b/tests/misc/t18077.nim new file mode 100644 index 000000000..6cd05d575 --- /dev/null +++ b/tests/misc/t18077.nim @@ -0,0 +1,21 @@ +discard """ + cmd: '''nim doc -d:nimTestsT18077b:4 --doccmd:"-d:nimTestsT18077 -d:nimTestsT18077b:3 --hints:off" $file''' + action: compile +""" + +# bug #18077 + +const nimTestsT18077b {.intdefine.} = 1 + +static: + when defined(nimdoc): + doAssert nimTestsT18077b == 4 + doAssert not defined(nimTestsT18077) + else: + doAssert defined(nimTestsT18077) + doAssert nimTestsT18077b == 3 + +runnableExamples: + const nimTestsT18077b {.intdefine.} = 2 + doAssert nimTestsT18077b == 3 + doAssert defined(nimTestsT18077) From 3b1aabdcffafbbf37ad02f013f754f67dfe2461c Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 26 May 2021 00:51:48 -0700 Subject: [PATCH 2139/3768] change `--hint[X] => --hint:X` in nim repo (avoids shell quoting issues) (#18085) --- compiler/nim.cfg | 4 ++-- compiler/nimfix/nimfix.nim.cfg | 2 +- config/nim.cfg | 4 ++-- doc/advopt.txt | 10 +++++----- doc/filters.rst | 2 +- doc/nimc.rst | 8 ++++---- drnim/nim.cfg | 2 +- nimsuggest/nimsuggest.nim.cfg | 2 +- testament/categories.nim | 4 ++-- tests/effects/toutparam.nim | 2 +- tests/osproc/treadlines.nim | 2 +- tests/pragmas/t8741.nim | 2 +- tests/range/tcompiletime_range_checks.nim | 2 +- tests/tools/tunused_imports.nim | 2 +- tools/nimweb.nim | 2 +- 15 files changed, 25 insertions(+), 25 deletions(-) diff --git a/compiler/nim.cfg b/compiler/nim.cfg index 40e93d523..9ecd00b0b 100644 --- a/compiler/nim.cfg +++ b/compiler/nim.cfg @@ -1,6 +1,6 @@ # Special configuration file for the Nim project -hint[XDeclaredButNotUsed]:off +hint:XDeclaredButNotUsed:off define:booting define:nimcore @@ -22,5 +22,5 @@ define:useStdoutAsStdmsg #gc:markAndSweep @if nimHasWarningObservableStores: - warning[ObservableStores]: off + warning:ObservableStores: off @end diff --git a/compiler/nimfix/nimfix.nim.cfg b/compiler/nimfix/nimfix.nim.cfg index 0d9dbfa4b..27ebf10b9 100644 --- a/compiler/nimfix/nimfix.nim.cfg +++ b/compiler/nimfix/nimfix.nim.cfg @@ -1,7 +1,7 @@ # Special configuration file for the Nim project # gc:markAndSweep -hint[XDeclaredButNotUsed]:off +hint:XDeclaredButNotUsed:off path:"$projectPath/.." path:"$lib/packages/docutils" diff --git a/config/nim.cfg b/config/nim.cfg index 3b964d124..c0a844e03 100644 --- a/config/nim.cfg +++ b/config/nim.cfg @@ -13,8 +13,8 @@ cc = gcc # additional options always passed to the compiler: --parallel_build: "0" # 0 to auto-detect number of processors -hint[LineTooLong]=off -#hint[XDeclaredButNotUsed]=off +hint:LineTooLong:off +#hint:XDeclaredButNotUsed:off # Examples of how to setup a cross-compiler: diff --git a/doc/advopt.txt b/doc/advopt.txt index e50fb243b..ea3370880 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -47,12 +47,12 @@ Advanced options: an implementation defined set of suggestions. -w:on|off|list, --warnings:on|off|list turn all warnings on|off or list all available - --warning[X]:on|off turn specific warning X on|off + --warning:X:on|off turn specific warning X on|off. + `warning:X` means `warning:X:on`, as with similar flags. --hints:on|off|list turn all hints on|off or list all available - --hint[X]:on|off turn specific hint X on|off - --warningAsError[X]:on|off - turn specific warning X into an error on|off - --hintAsError[X]:on|off turn specific hint X into an error on|off + --hint:X:on|off turn specific hint X on|off. + --warningAsError:X:on|off turn specific warning X into an error on|off + --hintAsError:X:on|off turn specific hint X into an error on|off --styleCheck:off|hint|error produce hints or errors for Nim identifiers that do not adhere to Nim's official style guide diff --git a/doc/filters.rst b/doc/filters.rst index c6d436194..e237744cb 100644 --- a/doc/filters.rst +++ b/doc/filters.rst @@ -25,7 +25,7 @@ just like an ordinary procedure call with named or positional arguments. The available parameters depend on the invoked filter. Before version 0.12.0 of the language `#!` was used instead of `#?`. -**Hint:** With `--hint[codeBegin]:on`:option: or `--verbosity:2`:option: +**Hint:** With `--hint:codeBegin:on`:option: or `--verbosity:2`:option: (or higher) while compiling or `nim check`:cmd:, Nim lists the processed code after each filter application. diff --git a/doc/nimc.rst b/doc/nimc.rst index 47f7243cc..e467a33af 100644 --- a/doc/nimc.rst +++ b/doc/nimc.rst @@ -53,8 +53,8 @@ Advanced command-line switches are: List of warnings ---------------- -Each warning can be activated individually with `--warning[NAME]:on|off`:option: or -in a `push` pragma. +Each warning can be activated individually with `--warning:NAME:on|off`:option: or +in a `push` pragma with `{.warning[NAME]:on|off.}`. ========================== ============================================ Name Description @@ -77,8 +77,8 @@ User Some user-defined warning. List of hints ------------- -Each hint can be activated individually with `--hint[NAME]:on|off`:option: or in a -`push` pragma. +Each hint can be activated individually with `--hint:NAME:on|off`:option: or in a +`push` pragma with `{.hint[NAME]:on|off.}`. ========================== ============================================ Name Description diff --git a/drnim/nim.cfg b/drnim/nim.cfg index 58c1725d9..57dca974c 100644 --- a/drnim/nim.cfg +++ b/drnim/nim.cfg @@ -1,6 +1,6 @@ # Special configuration file for the Nim project -hint[XDeclaredButNotUsed]:off +hint:XDeclaredButNotUsed:off define:booting define:nimcore diff --git a/nimsuggest/nimsuggest.nim.cfg b/nimsuggest/nimsuggest.nim.cfg index 394449740..6e5ee8c71 100644 --- a/nimsuggest/nimsuggest.nim.cfg +++ b/nimsuggest/nimsuggest.nim.cfg @@ -2,7 +2,7 @@ gc:markAndSweep -hint[XDeclaredButNotUsed]:off +hint:XDeclaredButNotUsed:off path:"$lib/packages/docutils" diff --git a/testament/categories.nim b/testament/categories.nim index 8cf90666c..0a3595ff4 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -486,7 +486,7 @@ proc icTests(r: var TResults; testsDir: string, cat: Category, options: string; writeOnly = " --incremental:writeonly " readOnly = " --incremental:readonly " incrementalOn = " --incremental:on -d:nimIcIntegrityChecks " - navTestConfig = " --ic:on -d:nimIcNavigatorTests --hint[Conf]:off --warnings:off " + navTestConfig = " --ic:on -d:nimIcNavigatorTests --hint:Conf:off --warnings:off " template test(x: untyped) = testSpecWithNimcache(r, makeRawTest(file, x & options, cat), nimcache) @@ -500,7 +500,7 @@ proc icTests(r: var TResults; testsDir: string, cat: Category, options: string; template checkTest() = var test = makeRawTest(file, options, cat) - test.spec.cmd = compilerPrefix & " check --hint[Conf]:off --warnings:off --ic:on $options " & file + test.spec.cmd = compilerPrefix & " check --hint:Conf:off --warnings:off --ic:on $options " & file testSpecWithNimcache(r, test, nimcache) if not isNavigatorTest: diff --git a/tests/effects/toutparam.nim b/tests/effects/toutparam.nim index 00572aa6b..1126aa77e 100644 --- a/tests/effects/toutparam.nim +++ b/tests/effects/toutparam.nim @@ -1,5 +1,5 @@ discard """ - cmd: '''nim c --warningAsError[Uninit]:on --skipCfg --skipParentCfg $file''' + cmd: '''nim c --warningAsError:Uninit:on --skipCfg --skipParentCfg $file''' errormsg: "use explicit initialization of 'x' for clarity [Uninit]" line: 24 disabled: "true" diff --git a/tests/osproc/treadlines.nim b/tests/osproc/treadlines.nim index bcde19d7f..200a7c299 100644 --- a/tests/osproc/treadlines.nim +++ b/tests/osproc/treadlines.nim @@ -13,7 +13,7 @@ var ps: seq[Process] # compile & run 2 progs in parallel const nim = getCurrentCompilerExe() for prog in ["a", "b"]: ps.add startProcess(nim, "", - ["r", "--hint[Conf]=off", "--hint[Processing]=off", prog], + ["r", "--hint:Conf:off", "--hint:Processing:off", prog], options = {poUsePath, poDaemon, poStdErrToStdOut}) for p in ps: diff --git a/tests/pragmas/t8741.nim b/tests/pragmas/t8741.nim index 61a449c01..221c732b0 100644 --- a/tests/pragmas/t8741.nim +++ b/tests/pragmas/t8741.nim @@ -1,5 +1,5 @@ discard """ - cmd: "nim check --hint[processing]:off $file" + cmd: "nim check --hint:processing:off $file" errormsg: "3 is not two" nimout: '''t8741.nim(13, 9) Error: cannot attach a custom pragma to 'a' t8741.nim(29, 15) template/generic instantiation of `onlyTwo` from here diff --git a/tests/range/tcompiletime_range_checks.nim b/tests/range/tcompiletime_range_checks.nim index 37095e0b7..29e2c48a8 100644 --- a/tests/range/tcompiletime_range_checks.nim +++ b/tests/range/tcompiletime_range_checks.nim @@ -1,5 +1,5 @@ discard """ - cmd: "nim check --hint[Processing]:off --hint[Conf]:off $file" + cmd: "nim check --hint:Processing:off --hint:Conf:off $file" errormsg: "18446744073709551615 can't be converted to int8" nimout: '''tcompiletime_range_checks.nim(36, 21) Error: 2147483648 can't be converted to int32 tcompiletime_range_checks.nim(37, 23) Error: -1 can't be converted to uint64 diff --git a/tests/tools/tunused_imports.nim b/tests/tools/tunused_imports.nim index bce906634..31d6cf7d7 100644 --- a/tests/tools/tunused_imports.nim +++ b/tests/tools/tunused_imports.nim @@ -1,5 +1,5 @@ discard """ - cmd: '''nim c --hint[Processing]:off $file''' + cmd: '''nim c --hint:Processing:off $file''' nimout: ''' tunused_imports.nim(11, 10) Warning: BEGIN [User] tunused_imports.nim(36, 10) Warning: END [User] diff --git a/tools/nimweb.nim b/tools/nimweb.nim index f71b5f3be..ccc80dfcf 100644 --- a/tools/nimweb.nim +++ b/tools/nimweb.nim @@ -53,7 +53,7 @@ proc initConfigData(c: var TConfigData) = c.webdoc = @[] c.pdf = @[] c.infile = "" - c.nimArgs = "--hint[Conf]:off --hint[Path]:off --hint[Processing]:off -d:boot " + c.nimArgs = "--hint:Conf:off --hint:Path:off --hint:Processing:off -d:boot " c.gitURL = "https://github.com/nim-lang/Nim" c.docHTMLOutput = "doc/html" c.webUploadOutput = "web/upload" From 1e0165186bb8539cfd8aca1a7af8d01dc278bd46 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 26 May 2021 11:49:01 -0700 Subject: [PATCH 2140/3768] testament: remove deadcode related to realtimeGC, testC, callCCompiler (#18087) --- testament/categories.nim | 22 +++--------- testament/testament.nim | 63 --------------------------------- tests/readme.md | 3 +- tests/realtimeGC/cmain.c | 69 ------------------------------------- tests/realtimeGC/readme.txt | 10 ------ tests/realtimeGC/tmain.nim | 10 +++++- 6 files changed, 15 insertions(+), 162 deletions(-) delete mode 100644 tests/realtimeGC/cmain.c delete mode 100644 tests/realtimeGC/readme.txt diff --git a/testament/categories.nim b/testament/categories.nim index 0a3595ff4..7f05797ff 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -28,7 +28,6 @@ const "js", "ic", "lib", - "longgc", "manyloc", "nimble-packages", "niminaction", @@ -39,7 +38,6 @@ const "coroutines", "osproc", "shouldfail", - "dir with space", "destructor" ] @@ -156,19 +154,6 @@ proc gcTests(r: var TResults, cat: Category, options: string) = test "cyclecollector" testWithoutBoehm "trace_globals" -proc longGCTests(r: var TResults, cat: Category, options: string) = - when defined(windows): - let cOptions = "-ldl -DWIN" - else: - let cOptions = "-ldl" - - var c = initResults() - # According to ioTests, this should compile the file - testSpec c, makeTest("tests/realtimeGC/shared", options, cat) - # ^- why is this not appended to r? Should this be discarded? - testC r, makeTest("tests/realtimeGC/cmain", cOptions, cat), actionRun - testSpec r, makeTest("tests/realtimeGC/nmain", options & "--threads: on", cat) - # ------------------------- threading tests ----------------------------------- proc threadTests(r: var TResults, cat: Category, options: string) = @@ -186,6 +171,11 @@ proc ioTests(r: var TResults, cat: Category, options: string) = # dummy compile result: var c = initResults() testSpec c, makeTest("tests/system/helpers/readall_echo", options, cat) + # ^- why is this not appended to r? Should this be discarded? + # EDIT: this should be replaced by something like in D20210524T180826, + # likewise in similar instances where `testSpec c` is used, or more generally + # when a test depends on another test, as it makes tests non-independent, + # creating complications for batching and megatest logic. testSpec r, makeTest("tests/system/tio", options, cat) # ------------------------- async tests --------------------------------------- @@ -687,8 +677,6 @@ proc processCategory(r: var TResults, cat: Category, dllTests(r, cat, options) of "gc": gcTests(r, cat, options) - of "longgc": - longGCTests(r, cat, options) of "debugger": debuggerTests(r, cat, options) of "manyloc": diff --git a/testament/testament.nim b/testament/testament.nim index 1eb925122..789c987c0 100644 --- a/testament/testament.nim +++ b/testament/testament.nim @@ -221,53 +221,6 @@ proc callNimCompiler(cmdTemplate, filename, options, nimcache: string, result.msg = matches[0] trimUnitSep result.msg -proc callCCompiler(cmdTemplate, filename, options: string, - target: TTarget): TSpec = - let cmd = prepareTestCmd(cmdTemplate, filename, options, nimcache = "", target) - doAssert false - #[ - this code hasn't been run in a while, and should be removed which simplifies code - there are better ways to do this anyways (e.g. running c code from a nim file) - - the only place where this is called is: - `testC r, makeTest("tests/realtimeGC/cmain", cOptions, cat), actionRun` - which isn't run unless you call: - XDG_CONFIG_HOME= nim r --lib:lib --stacktrace:on testament/testament.nim r longgc - - and this fails since at least nim 1.0 with: - testament/testament.nim(851) testament - testament/testament.nim(822) main - testament/categories.nim(713) processCategory - testament/categories.nim(189) longGCTests - testament/testament.nim(644) makeTest - testament/specs.nim(251) parseSpec - testament/specs.nim(184) extractSpec - lib/system/io.nim(861) readFile - Error: unhandled exception: cannot open: tests/realtimeGC/cmain.nim [IOError] - - Also, `c[5 .. ^1]` is too magical. - ]# - let c = cmd.parseCmdLine - var p = startProcess(command = "gcc", args = c[5 .. ^1], - options = {poStdErrToStdOut, poUsePath, poEvalCommand}) - let outp = p.outputStream - var x = newStringOfCap(120) - result.nimout = "" - result.msg = "" - result.file = "" - result.output = "" - result.line = -1 - while true: - if outp.readLine(x): - result.nimout.add(x & '\n') - elif not running(p): - break - close(p) - if p.peekExitCode == 0: - result.err = reSuccess - else: - result.err = reNimcCrash - proc initResults: TResults = result.total = 0 result.passed = 0 @@ -628,22 +581,6 @@ proc testSpecWithNimcache(r: var TResults, test: TTest; nimcache: string) {.used var testClone = test testSpecHelper(r, testClone, test.spec, target, nimcache) -proc testC(r: var TResults, test: TTest, action: TTestAction) = - # runs C code. Doesn't support any specs, just goes by exit code. - if not checkDisabled(r, test): return - - let tname = test.name.addFileExt(".c") - inc(r.total) - maybeStyledEcho "Processing ", fgCyan, extractFilename(tname) - var given = callCCompiler(getCmd(TSpec()), test.name & ".c", test.options, targetC) - if given.err != reSuccess: - r.addResult(test, targetC, "", given.msg, given.err) - elif action == actionRun: - let exeFile = changeFileExt(test.name, ExeExt) - var (_, exitCode) = execCmdEx(exeFile, options = {poStdErrToStdOut, poUsePath}) - if exitCode != 0: given.err = reExitcodesDiffer - if given.err == reSuccess: inc(r.passed) - proc makeTest(test, options: string, cat: Category): TTest = result.cat = cat result.name = test diff --git a/tests/readme.md b/tests/readme.md index 69be92051..dca7ec152 100644 --- a/tests/readme.md +++ b/tests/readme.md @@ -55,5 +55,4 @@ testing, which is slower). The folder ``dll`` contains simple DLL tests. The folder ``realtimeGC`` contains a test for validating that the realtime GC -can run properly without linking against the nimrtl.dll/so. It includes a C -client and platform specific build files for manual compilation. +can run properly without linking against the nimrtl.dll/so. diff --git a/tests/realtimeGC/cmain.c b/tests/realtimeGC/cmain.c deleted file mode 100644 index 4bc5bd026..000000000 --- a/tests/realtimeGC/cmain.c +++ /dev/null @@ -1,69 +0,0 @@ -/* xxx consider removing, this seems redundant with tmain.nim */ - -#ifdef WIN -#include -#else -#include -#include /* for sleep(3) */ -#endif -#include -#include -#include -#include -#include - -#define RUNTIME (15*60) - - -typedef void (*pFunc)(void); - -int main(int argc, char* argv[]) -{ - int i; - void* hndl; - pFunc status; - pFunc count; - pFunc checkOccupiedMem; - -#ifdef WIN - hndl = (void*) LoadLibrary((char const*)"./tests/realtimeGC/shared.dll"); - status = (pFunc)GetProcAddress((HMODULE) hndl, (char const*)"status"); - count = (pFunc)GetProcAddress((HMODULE) hndl, (char const*)"count"); - checkOccupiedMem = (pFunc)GetProcAddress((HMODULE) hndl, (char const*)"checkOccupiedMem"); -#else /* OSX || NIX xxx: OSX assumes dylib*/ - hndl = (void*) dlopen((char const*)"./tests/realtimeGC/libshared.so", RTLD_LAZY); - status = (pFunc) dlsym(hndl, (char const*)"status"); - count = (pFunc) dlsym(hndl, (char const*)"count"); - checkOccupiedMem = (pFunc) dlsym(hndl, (char const*)"checkOccupiedMem"); -#endif - - assert(hndl); - assert(status); - assert(count); - assert(checkOccupiedMem); - - time_t startTime = time((time_t*)0); - time_t runTime = (time_t)(RUNTIME); - time_t accumTime = 0; - while (accumTime < runTime) { - for (i = 0; i < 10; i++) - count(); - /* printf("1. sleeping...\n"); */ - sleep(1); - for (i = 0; i < 10; i++) - status(); - /* printf("2. sleeping...\n"); */ - sleep(1); - checkOccupiedMem(); - accumTime = time((time_t*)0) - startTime; - /* printf("--- Minutes left to run: %d\n", (int)(runTime-accumTime)/60); */ - } - printf("Cleaning up the shared object pointer...\n"); -#ifdef WIN - FreeLibrary((HMODULE)hndl); -#else /* OSX || NIX */ - dlclose(hndl); -#endif - printf("Done\n"); - return 0; -} diff --git a/tests/realtimeGC/readme.txt b/tests/realtimeGC/readme.txt deleted file mode 100644 index c5837ee14..000000000 --- a/tests/realtimeGC/readme.txt +++ /dev/null @@ -1,10 +0,0 @@ -Test the realtime GC without linking nimrtl.dll/so. - -To build by hand and run the test for 35 minutes: - $ nim r --threads:on -d:runtimeSecs:2100 tests/realtimeGC/nmain.nim - -``` -xxx do we still need tests/realtimeGC/cmain.c? -if so, tests/realtimeGC/cmain.c needs to updated and factorized with nmain.nim to avoid duplication (even if it's a C file) -``` - $ gcc -o tests/realtimeGC/cmain tests/realtimeGC/cmain.c -ldl \ No newline at end of file diff --git a/tests/realtimeGC/tmain.nim b/tests/realtimeGC/tmain.nim index f1515a549..ca0177e3d 100644 --- a/tests/realtimeGC/tmain.nim +++ b/tests/realtimeGC/tmain.nim @@ -9,6 +9,13 @@ these dont' seem needed --debuginfo nor these from the previous main.nim.cfg: --app:console ]# +#[ +Test the realtime GC without linking nimrtl.dll/so. + +To build by hand and run the test for 35 minutes: +`nim r --threads:on -d:runtimeSecs:2100 tests/realtimeGC/tmain.nim` +]# + import times, os, strformat, strutils from stdtest/specialpaths import buildDir # import threadpool @@ -16,9 +23,10 @@ from stdtest/specialpaths import buildDir const runtimeSecs {.intdefine.} = 5 const file = "shared.nim" -const dllname = buildDir / (DynlibFormat % file) +const dllname = buildDir / (DynlibFormat % "shared_D20210524T180506") static: + # D20210524T180826:here we compile the dependency on the fly let nim = getCurrentCompilerExe() let (output, exitCode) = gorgeEx(fmt"{nim} c -o:{dllname} --debuginfo --app:lib --threads:on -d:release -d:useRealtimeGC {file}") doAssert exitCode == 0, output From d62a298a2834b9a35aac4d922a810a772cd3bc1d Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 27 May 2021 09:04:10 -0700 Subject: [PATCH 2141/3768] fix typo in ci/funs.sh (#18115) --- ci/funs.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ci/funs.sh b/ci/funs.sh index 9772535be..4a8887658 100644 --- a/ci/funs.sh +++ b/ci/funs.sh @@ -43,7 +43,7 @@ nimInternalBuildKochAndRunCI(){ echo_run nim c koch if ! echo_run ./koch runCI; then echo_run echo "runCI failed" - echo_run nim r -tools/ci_testresults.nim + echo_run nim r tools/ci_testresults.nim return 1 fi } From 9eae6b4fe312035daadd8dcb0ca488a3e5897c11 Mon Sep 17 00:00:00 2001 From: vabresto <77133146+vabresto@users.noreply.github.com> Date: Fri, 28 May 2021 18:17:37 -0400 Subject: [PATCH 2142/3768] fix #17456 flaky test tasynchttpserver_transferencoding (#18052) --- .../tasynchttpserver_transferencoding.nim | 55 +++++++++++-------- 1 file changed, 32 insertions(+), 23 deletions(-) diff --git a/tests/stdlib/tasynchttpserver_transferencoding.nim b/tests/stdlib/tasynchttpserver_transferencoding.nim index bfa0e2f69..fd79ce3a0 100644 --- a/tests/stdlib/tasynchttpserver_transferencoding.nim +++ b/tests/stdlib/tasynchttpserver_transferencoding.nim @@ -1,3 +1,7 @@ +discard """ + matrix: "--gc:arc --threads:on; --gc:arc --threads:on -d:danger; --threads:on" +""" + import httpclient, asynchttpserver, asyncdispatch, asyncfutures import net @@ -10,33 +14,38 @@ Transfer-Encoding:chunked """ -template genTest(input, expected) = - var sanity = false - proc handler(request: Request) {.async.} = - doAssert(request.body == expected) - doAssert(request.headers.hasKey("Transfer-Encoding")) - doAssert(not request.headers.hasKey("Content-Length")) - sanity = true - await request.respond(Http200, "Good") +template genTest(input, expected: string) = + proc handler(request: Request, future: Future[bool]) {.async, gcsafe.} = + doAssert(request.body == expected) + doAssert(request.headers.hasKey("Transfer-Encoding")) + doAssert(not request.headers.hasKey("Content-Length")) + future.complete(true) + await request.respond(Http200, "Good") - proc runSleepLoop(server: AsyncHttpServer) {.async.} = + proc sendData(data: string, port: Port) {.async.} = + var socket = newSocket() + defer: socket.close() + + socket.connect("127.0.0.1", port) + socket.send(data) + + proc runTest(): Future[bool] {.async.} = + var handlerFuture = newFuture[bool]("runTest") + let data = postBegin & input + let server = newAsyncHttpServer() server.listen(Port(0)) - proc wrapper() = - waitFor server.acceptRequest(handler) - asyncdispatch.callSoon wrapper - let server = newAsyncHttpServer() - waitFor runSleepLoop(server) - let data = postBegin & input - var socket = newSocket() - socket.connect("127.0.0.1", server.getPort) - socket.send(data) - waitFor sleepAsync(10) - socket.close() - server.close() + proc wrapper(request: Request): Future[void] {.gcsafe, closure.} = + handler(request, handlerFuture) + + asyncCheck sendData(data, server.getPort) + asyncCheck server.acceptRequest(wrapper) + doAssert await handlerFuture + + server.close() + return true - # Verify we ran the handler and its asserts - doAssert(sanity) + doAssert waitFor runTest() block: const expected = "hello=world" From c792154b030c1bfec9ed0020ddaf985d5f034ca3 Mon Sep 17 00:00:00 2001 From: quantimnot <54247259+quantimnot@users.noreply.github.com> Date: Sat, 29 May 2021 15:39:46 -0400 Subject: [PATCH 2143/3768] Minor doc fixes; follow up to 17258 and 17259 (#18123) Co-authored-by: quantimnot --- doc/manual.rst | 6 +++--- doc/manual_experimental_strictnotnil.rst | 2 +- doc/tut1.rst | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/manual.rst b/doc/manual.rst index 2b85f5c34..6a0dec120 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -363,7 +363,7 @@ contain the following `escape sequences`:idx:\ : ``\\`` `backslash`:idx: ``\"`` `quotation mark`:idx: ``\'`` `apostrophe`:idx: - ``\\`` '0'..'9'+ `character with decimal value d`:idx:; + ``\`` '0'..'9'+ `character with decimal value d`:idx:; all decimal digits directly following are used for the character ``\a`` `alert`:idx: @@ -473,7 +473,7 @@ literals: ``\\`` `backslash`:idx: ``\"`` `quotation mark`:idx: ``\'`` `apostrophe`:idx: - ``\\`` '0'..'9'+ `character with decimal value d`:idx:; + ``\`` '0'..'9'+ `character with decimal value d`:idx:; all decimal digits directly following are used for the character ``\a`` `alert`:idx: @@ -7825,7 +7825,7 @@ Threads To enable thread support the `--threads:on`:option: command-line switch needs to be used. The system_ module then contains several threading primitives. -See the `threads `_ and `channels `_ modules +See the `threads `_ and `channels `_ modules for the low-level thread API. There are also high-level parallelism constructs available. See `spawn `_ for further details. diff --git a/doc/manual_experimental_strictnotnil.rst b/doc/manual_experimental_strictnotnil.rst index c7c045683..cb3bfeca9 100644 --- a/doc/manual_experimental_strictnotnil.rst +++ b/doc/manual_experimental_strictnotnil.rst @@ -143,7 +143,7 @@ Merging usually merges maps and alias sets: nilabilities are merged like this: else: MaybeNil -Special handling is for `.isNil` and ` == nil`, also for `not`, `and` and `or`. +Special handling is for `.isNil` and `== nil`, also for `not`, `and` and `or`. `not` reverses the nilability, `and` is similar to "forking" : the right expression is checked in the layer resulting from the left one and `or` is similar to "merging": the right and left expression should be both checked in the original layer. diff --git a/doc/tut1.rst b/doc/tut1.rst index 2f19ae173..210ea324e 100644 --- a/doc/tut1.rst +++ b/doc/tut1.rst @@ -114,7 +114,7 @@ String and character literals ----------------------------- String literals are enclosed in double-quotes; character literals in single -quotes. Special characters are escaped with ``\\``: ``\n`` means newline, ``\t`` +quotes. Special characters are escaped with ``\``: ``\n`` means newline, ``\t`` means tabulator, etc. There are also *raw* string literals: .. code-block:: Nim From cfe19247e856950f7facaf7adbc50bca8dec3990 Mon Sep 17 00:00:00 2001 From: vabresto <77133146+vabresto@users.noreply.github.com> Date: Sat, 29 May 2021 15:44:39 -0400 Subject: [PATCH 2144/3768] Disable tasynchttpserver_transferencoding on FreeBSD (#18122) --- tests/stdlib/tasynchttpserver_transferencoding.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/stdlib/tasynchttpserver_transferencoding.nim b/tests/stdlib/tasynchttpserver_transferencoding.nim index fd79ce3a0..cdd30d04b 100644 --- a/tests/stdlib/tasynchttpserver_transferencoding.nim +++ b/tests/stdlib/tasynchttpserver_transferencoding.nim @@ -1,5 +1,6 @@ discard """ matrix: "--gc:arc --threads:on; --gc:arc --threads:on -d:danger; --threads:on" + disabled: "freebsd" """ import httpclient, asynchttpserver, asyncdispatch, asyncfutures From 4a7f2c386cde91b784106905f6e70d3954d8fae9 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 30 May 2021 13:37:21 -0700 Subject: [PATCH 2145/3768] close #16569: deprecated `reversed(a, start, last)` overload, use toOpenArray instead (#18047) * close #16569: deprecated reversed overload, use toOpenArray instead * [skip ci] change wording in changelog per review * fixup --- changelog.md | 2 ++ lib/pure/algorithm.nim | 39 ++++++++++----------------------------- 2 files changed, 12 insertions(+), 29 deletions(-) diff --git a/changelog.md b/changelog.md index 4c30d67a7..809728b16 100644 --- a/changelog.md +++ b/changelog.md @@ -75,6 +75,8 @@ - `strformat` is now part of `include std/prelude`. +- Deprecated `proc reversed*[T](a: openArray[T], first: Natural, last: int): seq[T]` in `std/algorithm`. + - The configuration subsystem now allows for `-d:release` and `-d:danger` to work as expected. The downside is that these defines now have custom logic that doesn't apply for other defines. diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim index f4b3f46be..c96f599e8 100644 --- a/lib/pure/algorithm.nim +++ b/lib/pure/algorithm.nim @@ -131,40 +131,21 @@ proc reverse*[T](a: var openArray[T]) = # the max is needed, since a.high is -1 if a is empty reverse(a, 0, max(0, a.high)) -proc reversed*[T](a: openArray[T], first: Natural, last: int): seq[T] = - ## Returns the reverse of the slice `a[first..last]`. - ## - ## If an invalid range is passed, it raises `IndexDefect`. +proc reversed*[T](a: openArray[T]): seq[T] {.inline.} = + ## Returns the elements of `a` in reverse order. ## ## **See also:** - ## * `reverse proc<#reverse,openArray[T],Natural,Natural>`_ reverse a slice ## * `reverse proc<#reverse,openArray[T]>`_ runnableExamples: - let - a = [1, 2, 3, 4, 5, 6] - b = a.reversed(1, 3) - assert b == @[4, 3, 2] - assert last >= first - 1 - var i = last - first - var x = first.int - result = newSeq[T](i + 1) - while i >= 0: - result[i] = a[x] - dec(i) - inc(x) + assert [10, 11, 12].reversed == @[12, 11, 10] + assert seq[string].default.reversed == @[] + let n = a.len + result.setLen(n) + for i in 0..`_ reverse a slice - ## * `reverse proc<#reverse,openArray[T]>`_ - runnableExamples: - let - a = [1, 2, 3, 4, 5, 6] - b = reversed(a) - assert b == @[6, 5, 4, 3, 2, 1] - reversed(a, 0, a.high) +proc reversed*[T](a: openArray[T], first: Natural, last: int): seq[T] + {.inline, deprecated: "use: `reversed(toOpenArray(a, first, last))`".} = + reversed(toOpenArray(a, first, last)) proc binarySearch*[T, K](a: openArray[T], key: K, cmp: proc (x: T, y: K): int {.closure.}): int = From a6bd6c7ed803dcef41b49343b5cd60607b984ca8 Mon Sep 17 00:00:00 2001 From: Yuriy Glukhov Date: Sun, 30 May 2021 23:38:33 +0300 Subject: [PATCH 2146/3768] Fixes #17849 (#18055) [backport:1.2] * Fixes #17849 * Update compiler/closureiters.nim Co-authored-by: Andreas Rumpf --- compiler/closureiters.nim | 6 ++++++ tests/iter/tyieldintry.nim | 16 +++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/compiler/closureiters.nim b/compiler/closureiters.nim index e474e5ba2..adc5a4873 100644 --- a/compiler/closureiters.nim +++ b/compiler/closureiters.nim @@ -606,6 +606,12 @@ proc lowerStmtListExprs(ctx: var Ctx, n: PNode, needsSplit: var bool): PNode = internalError(ctx.g.config, "lowerStmtListExpr(nkCaseStmt): " & $branch.kind) result.add(n) result.add(ctx.newEnvVarAccess(tmp)) + elif n[0].kind == nkStmtListExpr: + result = newNodeI(nkStmtList, n.info) + let (st, ex) = exprToStmtList(n[0]) + result.add(st) + n[0] = ex + result.add(n) of nkCallKinds, nkChckRange, nkChckRangeF, nkChckRange64: var ns = false diff --git a/tests/iter/tyieldintry.nim b/tests/iter/tyieldintry.nim index 35df55c24..3d5260800 100644 --- a/tests/iter/tyieldintry.nim +++ b/tests/iter/tyieldintry.nim @@ -483,5 +483,19 @@ block: # nnkChckRange test(it, 1, 2, 3) -echo "ok" +block: #17849 - yield in case subject + template yieldInCase: int = + yield 2 + 3 + iterator it(): int {.closure.} = + yield 1 + case yieldInCase() + of 1: checkpoint(11) + of 3: checkpoint(13) + else: checkpoint(14) + yield 5 + + test(it, 1, 2, 13, 5) + +echo "ok" From 4274e6e4d700ef72275344afed44d0b658729761 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 30 May 2021 13:39:17 -0700 Subject: [PATCH 2147/3768] upgrade ubuntu 16.04 (not supported starting dec 2021) => 18.04; revive Linux_i386 (#18107) --- .gitlab-ci.yml | 4 +++- azure-pipelines.yml | 14 +++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ad9e565a6..31908d223 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,6 @@ -image: ubuntu:16.04 +# xxx unused, out of date + +image: ubuntu:18.04 stages: - pre-build diff --git a/azure-pipelines.yml b/azure-pipelines.yml index f0c3b2d0a..a70a86fae 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -19,14 +19,14 @@ jobs: strategy: matrix: Linux_amd64: - vmImage: 'ubuntu-16.04' + vmImage: 'ubuntu-18.04' CPU: amd64 - # pending bug #17325 (broken again) - # Linux_i386: - # # bug #17325: fails on 'ubuntu-16.04' because it now errors with: - # # g++-multilib : Depends: gcc-multilib (>= 4:5.3.1-1ubuntu1) but it is not going to be installed - # vmImage: 'ubuntu-18.04' - # CPU: i386 + # regularly breaks, refs bug #17325 + Linux_i386: + # on 'ubuntu-16.04' (not supported anymore anyways) it errored with: + # g++-multilib : Depends: gcc-multilib (>= 4:5.3.1-1ubuntu1) but it is not going to be installed + vmImage: 'ubuntu-18.04' + CPU: i386 OSX_amd64: vmImage: 'macOS-10.15' CPU: amd64 From e2ab08603db6499063b3ff0f16776b4ed20c36f0 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 30 May 2021 13:40:42 -0700 Subject: [PATCH 2148/3768] fix warnings/hints in nimdoc/tester.nim (#18083) * fix warnings/hints in nimdoc/tester.nim * improve err msg for nimdoc/tester.nim and change flag from fixup to nimTestsNimdocFixup * address comment: put back quit instead of doAssert --- nimdoc/tester.nim | 12 +++++++----- nimdoc/testproject/expected/testproject.html | 8 ++++---- nimdoc/testproject/testproject.nim | 9 +++++---- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/nimdoc/tester.nim b/nimdoc/tester.nim index 9daa0bb51..1e438b97b 100644 --- a/nimdoc/tester.nim +++ b/nimdoc/tester.nim @@ -1,10 +1,12 @@ # Small program that runs the test cases for 'nim doc'. # To run this, cd to the git repo root, and run "nim r nimdoc/tester.nim". -# to change expected results (after carefully verifying everything), use -d:fixup +# to change expected results (after carefully verifying everything), use -d:nimTestsNimdocFixup import strutils, os from std/private/gitutils import diffFiles +const fixup = defined(nimTestsNimdocFixup) + var failures = 0 @@ -62,7 +64,7 @@ let "$1/$2.nim" % [test1Dir, test1PrjName]], buildIndex: @["--out:$1/$2/theindex.html" % [test1Dir, test1DocsDir], "$1/$2" % [test1Dir, test1DocsDir]]) -testNimDoc(test1Dir, test1DocsDir, test1Switches, defined(fixup)) +testNimDoc(test1Dir, test1DocsDir, test1Switches, fixup) # Test "nim doc --out:.. --index:on .." let @@ -75,7 +77,7 @@ let "$1/$2.nim" % [test2Dir, test2PrjName]], buildIndex: @["--out:$1/$2/theindex.html" % [test2Dir, test2DocsDir], "$1/$2" % [test2Dir, test2DocsDir]]) -testNimDoc(test2Dir, test2DocsDir, test2Switches, defined(fixup)) +testNimDoc(test2Dir, test2DocsDir, test2Switches, fixup) -# Check for failures -if failures > 0: quit($failures & " failures occurred.") +if failures > 0: + quit "$# failures occurred; see note in tester.nim regarding -d:nimTestsNimdocFixup" % $failures diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html index f23f6bb42..d2fe38021 100644 --- a/nimdoc/testproject/expected/testproject.html +++ b/nimdoc/testproject/expected/testproject.html @@ -790,7 +790,7 @@ the c printf. etc.
    -

    Example:

    +

    Example: cmd: --hint:XDeclaredButNotUsed:off

    ## mullitline string litterals are tricky as their indentation can span
     ## below that of the runnableExamples
     let s1a = """
    @@ -865,21 +865,21 @@ There is no block quote after blank lines at the beginning.
     

    Methods

    -
    method method1(self: Moo) {....raises: [], tags: [].}
    +
    method method1(self: Moo) {.base, ...raises: [], tags: [].}
    foo1
    -
    method method2(self: Moo): int {....raises: [], tags: [].}
    +
    method method2(self: Moo): int {.base, ...raises: [], tags: [].}
    foo2
    -
    method method3(self: Moo): int {....raises: [], tags: [].}
    +
    method method3(self: Moo): int {.base, ...raises: [], tags: [].}
    foo3 diff --git a/nimdoc/testproject/testproject.nim b/nimdoc/testproject/testproject.nim index d435efa66..b88b41098 100644 --- a/nimdoc/testproject/testproject.nim +++ b/nimdoc/testproject/testproject.nim @@ -131,6 +131,7 @@ when true: # BUG: this currently this won't be run since not exported # but probably should doAssert false + if false: bazNonExported() # silence XDeclaredButNotUsed proc z17*() = # BUG: a comment before 1st doc comment currently doesn't prevent @@ -212,7 +213,7 @@ when true: # tests RST inside comments when true: # multiline string litterals proc tripleStrLitTest*() = - runnableExamples: + runnableExamples("--hint:XDeclaredButNotUsed:off"): ## mullitline string litterals are tricky as their indentation can span ## below that of the runnableExamples let s1a = """ @@ -252,12 +253,12 @@ at indent 0 when true: # methods; issue #14691 type Moo = object - method method1*(self: Moo) = + method method1*(self: Moo) {.base.} = ## foo1 - method method2*(self: Moo): int = + method method2*(self: Moo): int {.base.} = ## foo2 result = 1 - method method3*(self: Moo): int = + method method3*(self: Moo): int {.base.} = ## foo3 1 From 50e98e6efaca05b60b33db0c878f04951a43b3b8 Mon Sep 17 00:00:00 2001 From: Miran Date: Sun, 30 May 2021 23:55:51 +0200 Subject: [PATCH 2149/3768] hashes for refs should be an opt-in feature (#18098) --- changelog.md | 17 +++++++++++++---- lib/pure/hashes.nim | 2 +- tests/collections/ttables.nim | 1 + tests/stdlib/thashes.nim | 1 + 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/changelog.md b/changelog.md index 809728b16..279ba38dd 100644 --- a/changelog.md +++ b/changelog.md @@ -50,8 +50,9 @@ - On POSIX systems, we now ignore `SIGPIPE` signals, use `-d:nimLegacySigpipeHandler` for previous behavior. -- `hashes.hash` now supports `object` and `ref` (can be overloaded in user code). - For a transition period, use `-d:nimLegacyNoHashRef` to avoid defining `hash(ref)`. +- `hashes.hash` can now support `object` and `ref` (can be overloaded in user code), + if `-d:nimEnableHashRef` is used. + - `hashes.hash(proc|ptr|ref|pointer)` now calls `hash(int)` and honors `-d:nimIntHash1`, `hashes.hash(closure)` has also been improved. @@ -82,7 +83,9 @@ other defines. + ## Standard library additions and changes + - Added support for parenthesized expressions in `strformat` - Fixed buffer overflow bugs in `net` @@ -99,7 +102,6 @@ the OpenSSL DLLs (e.g. libssl-1_1-x64.dll, libcrypto-1_1-x64.dll) you now also need to ship `cacert.pem` with your `.exe` file. - - Make `{.requiresInit.}` pragma to work for `distinct` types. - Added a macros `enumLen` for returning the number of items in an enum to the @@ -324,6 +326,8 @@ - Fixed premature garbage collection in asyncdispatch, when a stack trace override is in place. + + ## Language changes - `nimscript` now handles `except Exception as e`. @@ -368,6 +372,8 @@ - `typeof(voidStmt)` now works and returns `void`. + + ## Compiler changes - Added `--declaredlocs` to show symbol declaration location in messages. @@ -394,7 +400,8 @@ - TLS: OSX now uses native TLS (`--tlsEmulation:off`), TLS now works with importcpp non-POD types, such types must use `.cppNonPod` and `--tlsEmulation:off`should be used. -- Now array literals(JS backend) uses JS typed arrays when the corresponding js typed array exists, for example `[byte(1), 2, 3]` generates `new Uint8Array([1, 2, 3])`. +- Now array literals(JS backend) uses JS typed arrays when the corresponding js typed array exists, + for example `[byte(1), 2, 3]` generates `new Uint8Array([1, 2, 3])`. - docgen: rst files can now use single backticks instead of double backticks and correctly render in both rst2html (as before) as well as common tools rendering rst directly (e.g. github), by @@ -426,6 +433,8 @@ the official Nim style guide. To be enabled, this has to be combined either with `--styleCheck:error` or `--styleCheck:hint`. + + ## Tool changes - The rst parser now supports markdown table syntax. diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim index f61fbdf2b..46b4fc948 100644 --- a/lib/pure/hashes.nim +++ b/lib/pure/hashes.nim @@ -236,7 +236,7 @@ proc hash*[T](x: ptr[T]): Hash {.inline.} = assert cast[pointer](a[0].addr).hash == a[0].addr.hash hash(cast[pointer](x)) -when not defined(nimLegacyNoHashRef): +when defined(nimEnableHashRef): proc hash*[T](x: ref[T]): Hash {.inline.} = ## Efficient `hash` overload. runnableExamples: diff --git a/tests/collections/ttables.nim b/tests/collections/ttables.nim index 638f4241b..751509062 100644 --- a/tests/collections/ttables.nim +++ b/tests/collections/ttables.nim @@ -8,6 +8,7 @@ And we get here ''' joinable: false targets: "c cpp js" +matrix: "-d:nimEnableHashRef" """ # xxx wrap in a template to test in VM, see https://github.com/timotheecour/Nim/issues/534#issuecomment-769565033 diff --git a/tests/stdlib/thashes.nim b/tests/stdlib/thashes.nim index 46576ef12..1e9b02d0c 100644 --- a/tests/stdlib/thashes.nim +++ b/tests/stdlib/thashes.nim @@ -1,5 +1,6 @@ discard """ targets: "c cpp js" + matrix: "-d:nimEnableHashRef" """ import std/hashes From 98ea61f09b4bfdc13c911b1ff3fb404dfc0d1338 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 30 May 2021 14:57:25 -0700 Subject: [PATCH 2150/3768] dont silence hints in system.nim (#18034) --- lib/system.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/system.nim b/lib/system.nim index be30665dd..942d29fe8 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -112,7 +112,7 @@ proc compileOption*(option, arg: string): bool {. discard "compiled with optimization for size and uses Boehm's GC" {.push warning[GcMem]: off, warning[Uninit]: off.} -{.push hints: off.} +# {.push hints: off.} proc `or`*(a, b: typedesc): typedesc {.magic: "TypeTrait", noSideEffect.} ## Constructs an `or` meta class. @@ -2471,7 +2471,7 @@ proc quit*(errormsg: string, errorcode = QuitFailure) {.noreturn.} = quit(errorcode) {.pop.} # checks: off -{.pop.} # hints: off +# {.pop.} # hints: off proc `/`*(x, y: int): float {.inline, noSideEffect.} = ## Division of integers that results in a float. From 18b477431138d944b90e7a6e6e0412496634b518 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 31 May 2021 01:51:20 -0700 Subject: [PATCH 2151/3768] document macros.unpackVarargs (#18106) * deprecate macros.unpackVarargs * un-deprecate unpackVarargs and add docs+runnableExamples * update examples + tests with varargs[typed] --- lib/core/macros.nim | 15 ++++++++++ tests/destructor/tatomicptrs.nim | 1 + tests/stdlib/tmacros.nim | 50 ++++++++++++++++++++++++++++++++ 3 files changed, 66 insertions(+) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 79c3ba28b..89f90a23b 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -1682,6 +1682,21 @@ macro getCustomPragmaVal*(n: typed, cp: typed): untyped = error(n.repr & " doesn't have a pragma named " & cp.repr, n) macro unpackVarargs*(callee: untyped; args: varargs[untyped]): untyped = + ## Calls `callee` with `args` unpacked as individual arguments. + ## This is useful in 2 cases: + ## * when forwarding `varargs[T]` for some typed `T` + ## * when forwarding `varargs[untyped]` when `args` can potentially be empty, + ## due to a compiler limitation + runnableExamples: + template call1(fun: typed; args: varargs[untyped]): untyped = + unpackVarargs(fun, args) + # when varargsLen(args) > 0: fun(args) else: fun() # this would also work + template call2(fun: typed; args: varargs[typed]): untyped = + unpackVarargs(fun, args) + proc fn1(a = 0, b = 1) = discard (a, b) + call1(fn1, 10, 11) + call1(fn1) # `args` is empty in this case + if false: call2(echo, 10, 11) # would print 1011 result = newCall(callee) for i in 0 ..< args.len: result.add args[i] diff --git a/tests/destructor/tatomicptrs.nim b/tests/destructor/tatomicptrs.nim index 7313afbf5..36f0cab8a 100644 --- a/tests/destructor/tatomicptrs.nim +++ b/tests/destructor/tatomicptrs.nim @@ -72,6 +72,7 @@ template `.=`*[T](s: SharedPtr[T]; field, value: untyped) = from macros import unpackVarargs template `.()`*[T](s: SharedPtr[T]; field: untyped, args: varargs[untyped]): untyped = + # xxx this isn't used, the test should be improved unpackVarargs(s.x.field, args) diff --git a/tests/stdlib/tmacros.nim b/tests/stdlib/tmacros.nim index effe1032f..52ea1e898 100644 --- a/tests/stdlib/tmacros.nim +++ b/tests/stdlib/tmacros.nim @@ -10,3 +10,53 @@ block: # hasArgOfName block: # bug #17454 proc f(v: NimNode): string {.raises: [].} = $v + +block: # unpackVarargs + block: + proc bar1(a: varargs[int]): string = + for ai in a: result.add " " & $ai + proc bar2(a: varargs[int]) = + let s1 = bar1(a) + let s2 = unpackVarargs(bar1, a) # `unpackVarargs` makes no difference here + doAssert s1 == s2 + bar2(1, 2, 3) + bar2(1) + bar2() + + block: + template call1(fun: typed; args: varargs[untyped]): untyped = + unpackVarargs(fun, args) + template call2(fun: typed; args: varargs[untyped]): untyped = + # fun(args) # works except for last case with empty `args`, pending bug #9996 + when varargsLen(args) > 0: fun(args) + else: fun() + + proc fn1(a = 0, b = 1) = discard (a, b) + + call1(fn1) + call1(fn1, 10) + call1(fn1, 10, 11) + + call2(fn1) + call2(fn1, 10) + call2(fn1, 10, 11) + + block: + template call1(fun: typed; args: varargs[typed]): untyped = + unpackVarargs(fun, args) + template call2(fun: typed; args: varargs[typed]): untyped = + # xxx this would give a confusing error message: + # required type for a: varargs[typed] [varargs] but expression '[10]' is of type: varargs[typed] [varargs] + when varargsLen(args) > 0: fun(args) + else: fun() + macro toString(a: varargs[typed, `$`]): string = + var msg = genSym(nskVar, "msg") + result = newStmtList() + result.add quote do: + var `msg` = "" + for ai in a: + result.add quote do: `msg`.add $`ai` + result.add quote do: `msg` + doAssert call1(toString) == "" + doAssert call1(toString, 10) == "10" + doAssert call1(toString, 10, 11) == "1011" From 064fe18de61c2aae5c215077ba1dd0352affa271 Mon Sep 17 00:00:00 2001 From: n5m <72841454+n5m@users.noreply.github.com> Date: Mon, 31 May 2021 09:20:15 +0000 Subject: [PATCH 2152/3768] improve fast returns of find and rfind (#18127) --- lib/pure/strutils.nim | 4 +++- tests/stdlib/tstrutils.nim | 28 ++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 5bbd850cc..3fb67ead1 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -1890,7 +1890,7 @@ func find*(s, sub: string, start: Natural = 0, last = 0): int {.rtl, ## See also: ## * `rfind func<#rfind,string,string,Natural>`_ ## * `replace func<#replace,string,string,string>`_ - if sub.len > s.len: return -1 + if sub.len > s.len - start: return -1 if sub.len == 1: return find(s, sub[0], start, last) template useSkipTable {.dirty.} = @@ -1967,6 +1967,8 @@ func rfind*(s, sub: string, start: Natural = 0, last = -1): int {.rtl, ## * `find func<#find,string,string,Natural,int>`_ if sub.len == 0: return -1 + if sub.len > s.len - start: + return -1 let last = if last == -1: s.high else: last result = 0 for i in countdown(last - sub.len + 1, start): diff --git a/tests/stdlib/tstrutils.nim b/tests/stdlib/tstrutils.nim index 771dddcaf..637661ba3 100644 --- a/tests/stdlib/tstrutils.nim +++ b/tests/stdlib/tstrutils.nim @@ -243,6 +243,34 @@ template main() = doAssert "/1/2/3".rfind('/', last=1) == 0 doAssert "/1/2/3".rfind('0') == -1 + block: + const haystack: string = "ABCABABABABCAB" + doAssert haystack.len == 14 + doAssert haystack.rfind("ABC") == 9 + doAssert haystack.rfind("ABC", last=13) == 9 + doAssert haystack.rfind("ABC", last=12) == 9 + doAssert haystack.rfind("ABC", last=11) == 9 + doAssert haystack.rfind("ABC", last=10) == 0 + + doAssert haystack.rfind("ABC", start=0) == 9 + doAssert haystack.rfind("ABC", start=1) == 9 + doAssert haystack.rfind("ABC", start=2) == 9 + doAssert haystack.rfind("ABC", start=9) == 9 + doAssert haystack.rfind("ABC", start=10) == -1 + doAssert haystack.rfind("ABC", start=11) == -1 + doAssert haystack.rfind("ABC", start=12) == -1 + + doAssert haystack.rfind("ABC", start=0, last=13) == 9 + doAssert haystack.rfind("ABC", start=0, last=12) == 9 + doAssert haystack.rfind("ABC", start=0, last=11) == 9 + doAssert haystack.rfind("ABC", start=0, last=10) == 0 + doAssert haystack.rfind("ABC", start=1, last=10) == -1 + + doAssert "".rfind("/") == -1 + doAssert "/".rfind("/") == 0 + doAssert "/".rfind("//") == -1 + doAssert "///".rfind("//", start=3) == -1 + block: # trimZeros var x = "1200" x.trimZeros() From b7ad29e692ffd9d12fdf4d42cd8ef2a63b9340d1 Mon Sep 17 00:00:00 2001 From: Saem Ghani Date: Mon, 31 May 2021 04:27:44 -0700 Subject: [PATCH 2153/3768] fix #18113 (#18124) --- compiler/semtempl.nim | 8 ++++++-- tests/proc/t17157.nim | 1 + tests/template/t18113.nim | 14 ++++++++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 tests/template/t18113.nim diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index 40502daf4..181cc01db 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -610,8 +610,6 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = s.owner.name.s == "vm" and s.name.s == "stackTrace": incl(s.flags, sfCallsite) - s.ast = n - styleCheckDef(c.config, s) onDef(n[namePos].info, s) # check parameter list: @@ -664,6 +662,12 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = semIdeForTemplateOrGeneric(c, n[bodyPos], ctx.cursorInBody) closeScope(c) popOwner(c) + + # set the symbol AST after pragmas, at least. This stops pragma that have + # been pushed (implicit) to be explicitly added to the template definition + # and misapplied to the body. see #18113 + s.ast = n + if sfCustomPragma in s.flags: if n[bodyPos].kind != nkEmpty: localError(c.config, n[bodyPos].info, errImplOfXNotAllowed % s.name.s) diff --git a/tests/proc/t17157.nim b/tests/proc/t17157.nim index 020e93fce..2927eeee8 100644 --- a/tests/proc/t17157.nim +++ b/tests/proc/t17157.nim @@ -1,5 +1,6 @@ discard """ errormsg: "'untyped' is only allowed in templates and macros or magic procs" + disabled: true """ template something(op: proc (v: untyped): void): void = diff --git a/tests/template/t18113.nim b/tests/template/t18113.nim new file mode 100644 index 000000000..a84b3fd0e --- /dev/null +++ b/tests/template/t18113.nim @@ -0,0 +1,14 @@ +# ensure template pragma handling doesn't eagerly attempt to add an implicit +# 'pushed' pragma to the evaluation of any intermediate AST prior to +# substitution. + +# bug #18113 + +import sequtils + +{.push raises: [Defect].} + +var a = toSeq([1, 2, 3, 5, 10]).filterIt(it > 5) + +doAssert a.len == 1 +doAssert a[0] == 10 From a36efb59b5a74db6c5bbe6c0997c0221d0f55491 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 31 May 2021 04:39:19 -0700 Subject: [PATCH 2154/3768] fix #16256: nimout: should give error (vacuously true); improve a few tests (#18089) * fix #16256: nimout: should give error (vacuously true); improve some tests * renamed: tests/stdlib/t9710.nim -> tests/misc/t9710.nim * improve tests * fix non-DRY tests * improve $nim_prs_D/tests/stdlib/t9091.nim * renamed: tests/stdlib/t9091.nim -> tests/misc/t9091.nim * fixup * address comment: doAssert => result.parseErrors --- testament/specs.nim | 5 ++ tests/{stdlib => misc}/t9091.nim | 15 +++--- tests/misc/t9710.nim | 6 +++ tests/stdlib/t9710.nim | 11 ---- tests/stdlib/tgetprotobyname.nim | 10 ---- tests/stdlib/tnativesockets.nim | 46 ++++++++--------- tests/stdlib/tvarints.nim | 86 +++++++++++++------------------- 7 files changed, 73 insertions(+), 106 deletions(-) rename tests/{stdlib => misc}/t9091.nim (70%) create mode 100644 tests/misc/t9710.nim delete mode 100644 tests/stdlib/t9710.nim diff --git a/testament/specs.nim b/testament/specs.nim index a3271aeaa..6fd0ab22d 100644 --- a/testament/specs.nim +++ b/testament/specs.nim @@ -253,6 +253,7 @@ proc parseSpec*(filename: string): TSpec = var p: CfgParser open(p, ss, filename, 1) var flags: HashSet[string] + var nimoutFound = false while true: var e = next(p) case e.kind @@ -309,6 +310,7 @@ proc parseSpec*(filename: string): TSpec = result.action = actionReject of "nimout": result.nimout = e.value + nimoutFound = true of "nimoutfull": result.nimoutFull = parseCfgBool(e.value) of "batchable": @@ -401,6 +403,9 @@ proc parseSpec*(filename: string): TSpec = if skips.anyIt(it in result.file): result.err = reDisabled + if nimoutFound and result.nimout.len == 0 and not result.nimoutFull: + result.parseErrors.addLine "empty `nimout` is vacuously true, use `nimoutFull:true` if intentional" + result.inCurrentBatch = isCurrentBatch(testamentData0, filename) or result.unbatchable if not result.inCurrentBatch: result.err = reDisabled diff --git a/tests/stdlib/t9091.nim b/tests/misc/t9091.nim similarity index 70% rename from tests/stdlib/t9091.nim rename to tests/misc/t9091.nim index 8419479a7..6e7a98ca5 100644 --- a/tests/stdlib/t9091.nim +++ b/tests/misc/t9091.nim @@ -1,10 +1,5 @@ -discard """ - targets: "c" - output: "test AObj" - action: "compile" - exitcode: 0 - timeout: 60.0 -""" +# bug #9091 + import streams block: @@ -18,6 +13,8 @@ block: let mi = new Mine str.write(mi) + str.setPosition 0 + doAssert str.readAll == "sure" block: type @@ -27,10 +24,10 @@ block: proc foo(a: int): string = "" proc test(args: varargs[string, foo]) = - echo "varargs" + doAssert false proc test(a: AObj) = - echo "test AObj" + discard let x = AObj() test(x) diff --git a/tests/misc/t9710.nim b/tests/misc/t9710.nim new file mode 100644 index 000000000..c65cb7bf4 --- /dev/null +++ b/tests/misc/t9710.nim @@ -0,0 +1,6 @@ +discard """ + matrix: "--debugger:native" +""" +# bug #9710 +for i in 1 || 200: + discard i diff --git a/tests/stdlib/t9710.nim b/tests/stdlib/t9710.nim deleted file mode 100644 index f3ed860df..000000000 --- a/tests/stdlib/t9710.nim +++ /dev/null @@ -1,11 +0,0 @@ -discard """ - cmd: "nim c -r --debugger:native --panics:on $options $file" - targets: "c" - nimout: "" - action: "run" - exitcode: 0 - timeout: 60.0 -""" - -for i in 1 || 200: - discard i diff --git a/tests/stdlib/tgetprotobyname.nim b/tests/stdlib/tgetprotobyname.nim index b4df05102..0b60d059f 100644 --- a/tests/stdlib/tgetprotobyname.nim +++ b/tests/stdlib/tgetprotobyname.nim @@ -1,15 +1,5 @@ -discard """ - cmd: "nim c -r --styleCheck:hint --panics:on $options $file" - targets: "c" - nimout: "" - action: "run" - exitcode: 0 - timeout: 60.0 -""" - import nativesockets - when not defined(netbsd): # Ref: https://github.com/nim-lang/Nim/issues/15452 - NetBSD doesn't define an `ip` protocol doAssert getProtoByName("ip") == 0 diff --git a/tests/stdlib/tnativesockets.nim b/tests/stdlib/tnativesockets.nim index b0cfd09cf..6a1a00881 100644 --- a/tests/stdlib/tnativesockets.nim +++ b/tests/stdlib/tnativesockets.nim @@ -1,29 +1,25 @@ -discard """ - cmd: "nim c -r --styleCheck:hint --panics:on $options $file" - targets: "c" - nimout: "" - action: "run" - exitcode: 0 - timeout: 60.0 -""" - -import nativesockets +import std/nativesockets +import stdtest/testutils +block: + let hostname = getHostname() + doAssert hostname.len > 0 when defined(windows): - doAssert toInt(IPPROTO_IP) == 0.cint - doAssert toInt(IPPROTO_ICMP) == 1.cint - doAssert toInt(IPPROTO_TCP) == 6.cint - doAssert toInt(IPPROTO_UDP) == 17.cint - doAssert toInt(IPPROTO_IPV6) == 41.cint - doAssert toInt(IPPROTO_ICMPV6) == 58.cint - doAssert toInt(IPPROTO_RAW) == 20.cint + assertAll: + toInt(IPPROTO_IP) == 0 + toInt(IPPROTO_ICMP) == 1 + toInt(IPPROTO_TCP) == 6 + toInt(IPPROTO_UDP) == 17 + toInt(IPPROTO_IPV6) == 41 + toInt(IPPROTO_ICMPV6) == 58 + toInt(IPPROTO_RAW) == 20 - # no changes to enum value - doAssert ord(IPPROTO_TCP) == 6 - doAssert ord(IPPROTO_UDP) == 17 - doAssert ord(IPPROTO_IP) == 18 - doAssert ord(IPPROTO_IPV6) == 19 - doAssert ord(IPPROTO_RAW) == 20 - doAssert ord(IPPROTO_ICMP) == 21 - doAssert ord(IPPROTO_ICMPV6) == 22 + # no changes to enum value + ord(IPPROTO_TCP) == 6 + ord(IPPROTO_UDP) == 17 + ord(IPPROTO_IP) == 18 + ord(IPPROTO_IPV6) == 19 + ord(IPPROTO_RAW) == 20 + ord(IPPROTO_ICMP) == 21 + ord(IPPROTO_ICMPV6) == 22 diff --git a/tests/stdlib/tvarints.nim b/tests/stdlib/tvarints.nim index dcdb756ce..772121f9f 100644 --- a/tests/stdlib/tvarints.nim +++ b/tests/stdlib/tvarints.nim @@ -1,15 +1,6 @@ -discard """ - cmd: "nim c -r --styleCheck:hint --panics:on $options $file" - matrix: "-d:danger; -d:release" - targets: "c cpp" - nimout: "" - action: "run" - exitcode: 0 - timeout: 60.0 -""" - import std/varints +# xxx doesn't work with js: tvarints.nim(18, 14) `wrLen == rdLen` [AssertionDefect] block: var dest: array[50, byte] @@ -39,46 +30,39 @@ block: block: var hugeIntArray: array[50, byte] var readedInt: uint64 - doAssert writeVu64(hugeIntArray, 0.uint64) == readVu64(hugeIntArray, readedInt) - doAssert readedInt == 0.uint64 - doAssert writeVu64(hugeIntArray, uint64.high) == readVu64(hugeIntArray, readedInt) - doAssert readedInt == uint64.high - doAssert writeVu64(hugeIntArray, uint64(int64.high)) == readVu64(hugeIntArray, readedInt) - doAssert readedInt == uint64(int64.high) - doAssert writeVu64(hugeIntArray, uint64(int32.high)) == readVu64(hugeIntArray, readedInt) - doAssert readedInt == uint64(int32.high) - doAssert writeVu64(hugeIntArray, uint64(int16.high)) == readVu64(hugeIntArray, readedInt) - doAssert readedInt == uint64(int16.high) - doAssert writeVu64(hugeIntArray, uint64(int8.high)) == readVu64(hugeIntArray, readedInt) - doAssert readedInt == uint64(int8.high) - doAssert writeVu64(hugeIntArray, cast[uint64](0.0)) == readVu64(hugeIntArray, readedInt) - doAssert readedInt == cast[uint64](0.0) - doAssert writeVu64(hugeIntArray, cast[uint64](-0.0)) == readVu64(hugeIntArray, readedInt) - doAssert readedInt == cast[uint64](-0.0) - doAssert writeVu64(hugeIntArray, cast[uint64](0.1)) == readVu64(hugeIntArray, readedInt) - doAssert readedInt == cast[uint64](0.1) - doAssert writeVu64(hugeIntArray, cast[uint64](0.9555555555555555555555501)) == readVu64(hugeIntArray, readedInt) - doAssert readedInt == cast[uint64](0.9555555555555555555555501) - doAssert writeVu64(hugeIntArray, cast[uint64](+Inf)) == readVu64(hugeIntArray, readedInt) - doAssert readedInt == cast[uint64](+Inf) - doAssert writeVu64(hugeIntArray, cast[uint64](NegInf)) == readVu64(hugeIntArray, readedInt) - doAssert readedInt == cast[uint64](NegInf) - doAssert writeVu64(hugeIntArray, cast[uint64](Nan)) == readVu64(hugeIntArray, readedInt) - doAssert readedInt == cast[uint64](Nan) - doAssert writeVu64(hugeIntArray, cast[uint64](3.1415926535897932384626433)) == readVu64(hugeIntArray, readedInt) - doAssert readedInt == cast[uint64](3.1415926535897932384626433) - doAssert writeVu64(hugeIntArray, cast[uint64](2.71828182845904523536028747)) == readVu64(hugeIntArray, readedInt) - doAssert readedInt == cast[uint64](2.71828182845904523536028747) + + template chk(a) = + let b = cast[uint64](a) + doAssert writeVu64(hugeIntArray, b) == readVu64(hugeIntArray, readedInt) + doAssert readedInt == b + + chk 0 + chk uint64.high + chk int64.high + chk int32.high + chk int16.high + chk int16.high + chk int8.high + chk 0.0 + chk -0.0 + chk 0.1 + chk Inf + chk NegInf + chk Nan + chk 3.1415926535897932384626433 block: - doAssert encodeZigzag(decodeZigzag(0.uint64)) == 0.uint64 - doAssert encodeZigzag(decodeZigzag(uint64(uint32.high))) == uint64(uint32.high) - doAssert encodeZigzag(decodeZigzag(uint64(int32.high))) == uint64(int32.high) - doAssert encodeZigzag(decodeZigzag(uint64(int16.high))) == uint64(int16.high) - doAssert encodeZigzag(decodeZigzag(uint64(int8.high))) == uint64(int8.high) - doAssert encodeZigzag(decodeZigzag(cast[uint64](0.0))) == cast[uint64](0.0) - doAssert encodeZigzag(decodeZigzag(cast[uint64](0.1))) == cast[uint64](0.1) - doAssert encodeZigzag(decodeZigzag(cast[uint64](0.9555555555555555555555501))) == cast[uint64](0.9555555555555555555555501) - doAssert encodeZigzag(decodeZigzag(cast[uint64](+Inf))) == cast[uint64](+Inf) - doAssert encodeZigzag(decodeZigzag(cast[uint64](3.1415926535897932384626433))) == cast[uint64](3.1415926535897932384626433) - doAssert encodeZigzag(decodeZigzag(cast[uint64](2.71828182845904523536028747))) == cast[uint64](2.71828182845904523536028747) + template chk(a) = + let b = cast[uint64](a) + doAssert encodeZigzag(decodeZigzag(b)) == b + chk 0 + chk uint32.high + chk int32.high + chk int16.high + chk int8.high + chk 0.0 + chk 0.1 + chk 0.9555555555555555555555501 + chk Inf + chk 3.1415926535897932384626433 + chk 2.71828182845904523536028747 From 60cbdbf37ac66624f78edd5b3d575c80c6603679 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 31 May 2021 13:14:50 -0700 Subject: [PATCH 2155/3768] close #18092 document elif in case statements (#18105) * close #18092 [skip ci] document elif in case statements * fixup * clarify spec; mention special rule for string in case statements * address comments --- doc/manual.rst | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/doc/manual.rst b/doc/manual.rst index 6a0dec120..ff34b62ca 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -2981,11 +2981,13 @@ Example: .. code-block:: nim - case readline(stdin) + let line = readline(stdin) + case line of "delete-everything", "restart-computer": echo "permission denied" of "go-for-a-walk": echo "please yourself" - else: echo "unknown command" + elif line.len == 0: echo "empty" # optional, must come after `of` branches + else: echo "unknown command" # ditto # indentation of the branches is also allowed; and so is an optional colon # after the selecting expression: @@ -2996,19 +2998,23 @@ Example: else: echo "unknown command" -The `case` statement is similar to the if statement, but it represents +The `case` statement is similar to the `if` statement, but it represents a multi-branch selection. The expression after the keyword `case` is evaluated and if its value is in a *slicelist* the corresponding statements (after the `of` keyword) are executed. If the value is not in any -given *slicelist* the `else` part is executed. If there is no `else` -part and not all possible values that `expr` can hold occur in a -*slicelist*, a static error occurs. This holds only for expressions of -ordinal types. "All possible values" of `expr` are determined by `expr`'s -type. To suppress the static error an `else` part with an -empty `discard` statement should be used. +given *slicelist*, trailing `elif` and `else` parts are executed using same +semantics as for `if` statement, and `elif` is handled just like `else: if`. +If there are no `else` or `elif` parts and not +all possible values that `expr` can hold occur in a *slicelist*, a static error occurs. +This holds only for expressions of ordinal types. +"All possible values" of `expr` are determined by `expr`'s type. +To suppress the static error an `else: discard` should be used. For non-ordinal types, it is not possible to list every possible value and so these always require an `else` part. +An exception to this rule is for the `string` type, which currently doesn't +require a trailing `else` or `elif` branch; it's unspecified whether this will +keep working in future versions. Because case statements are checked for exhaustiveness during semantic analysis, the value in every `of` branch must be a constant expression. @@ -3054,15 +3060,15 @@ won't work: var foo = Foo(x: @[]) foo.get_x().add("asd") -This can be fixed by explicitly using `return`: +This can be fixed by explicitly using `result` or `return`: .. code-block:: nim proc get_x(x: Foo): var seq[string] = case true of true: - return x.x + result = x.x else: - return x.x + result = x.x When statement From 9559350e34a62614d1f7de0945095a4c4f4f5ece Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 31 May 2021 13:16:33 -0700 Subject: [PATCH 2156/3768] add `os.getCacheDir` (#18126) * add `os.getCacheDir` * fixup * address comments --- changelog.md | 3 ++- lib/pure/os.nim | 44 ++++++++++++++++++++++++++++++++++++++------ tests/stdlib/tos.nim | 3 +++ 3 files changed, 43 insertions(+), 7 deletions(-) diff --git a/changelog.md b/changelog.md index 279ba38dd..d404b1965 100644 --- a/changelog.md +++ b/changelog.md @@ -175,10 +175,11 @@ - `--gc:orc` is now 10% faster than previously for common workloads. If you have trouble with its changed behavior, compile with `-d:nimOldOrc`. - - `os.FileInfo` (returned by `getFileInfo`) now contains `blockSize`, determining preferred I/O block size for this file object. +- Added `os.getCacheDir()` to return platform specific cache directory. + - Added a simpler to use `io.readChars` overload. - Added `**` to jsffi. diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 287fbe125..c48d0d84f 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -922,9 +922,7 @@ proc getConfigDir*(): string {.rtl, extern: "nos$1", ## See also: ## * `getHomeDir proc <#getHomeDir>`_ ## * `getTempDir proc <#getTempDir>`_ - ## * `expandTilde proc <#expandTilde,string>`_ - ## * `getCurrentDir proc <#getCurrentDir>`_ - ## * `setCurrentDir proc <#setCurrentDir,string>`_ + ## * `getCacheDir proc <#getCacheDir>`_ runnableExamples: from std/strutils import endsWith # See `getHomeDir` for behavior regarding trailing DirSep. @@ -935,6 +933,42 @@ proc getConfigDir*(): string {.rtl, extern: "nos$1", result = getEnv("XDG_CONFIG_HOME", getEnv("HOME") / ".config") result.normalizePathEnd(trailingSep = defined(nimLegacyHomeDir)) +proc getCacheDir*(): string = + ## Returns the cache directory of the current user for applications. + ## + ## This makes use of the following environment variables: + ## + ## * On Windows: `getEnv("LOCALAPPDATA")` + ## + ## * On macOS: `getEnv("XDG_CACHE_HOME", getEnv("HOME") / "Library/Caches")` + ## + ## * On other platforms: `getEnv("XDG_CACHE_HOME", getEnv("HOME") / ".cache")` + ## + ## **See also:** + ## * `getHomeDir proc <#getHomeDir>`_ + ## * `getTempDir proc <#getTempDir>`_ + ## * `getConfigDir proc <#getConfigDir>`_ + # follows https://crates.io/crates/platform-dirs + when defined(windows): + result = getEnv("LOCALAPPDATA") + elif defined(osx): + result = getEnv("XDG_CACHE_HOME", getEnv("HOME") / "Library/Caches") + else: + result = getEnv("XDG_CACHE_HOME", getEnv("HOME") / ".cache") + result.normalizePathEnd(false) + +proc getCacheDir*(app: string): string = + ## Returns the cache directory for an application `app`. + ## + ## * On windows, this uses: `getCacheDir() / app / "cache"` + ## + ## * On other platforms, this uses: `getCacheDir() / app` + when defined(windows): + getCacheDir() / app / "cache" + else: + getCacheDir() / app + + when defined(windows): type DWORD = uint32 @@ -972,9 +1006,7 @@ proc getTempDir*(): string {.rtl, extern: "nos$1", ## See also: ## * `getHomeDir proc <#getHomeDir>`_ ## * `getConfigDir proc <#getConfigDir>`_ - ## * `expandTilde proc <#expandTilde,string>`_ - ## * `getCurrentDir proc <#getCurrentDir>`_ - ## * `setCurrentDir proc <#setCurrentDir,string>`_ + ## * `getCacheDir proc <#getCacheDir>`_ runnableExamples: from std/strutils import endsWith # See `getHomeDir` for behavior regarding trailing DirSep. diff --git a/tests/stdlib/tos.nim b/tests/stdlib/tos.nim index 1fcee5a2e..b9769d05c 100644 --- a/tests/stdlib/tos.nim +++ b/tests/stdlib/tos.nim @@ -556,6 +556,9 @@ block getTempDir: else: doAssert getTempDir() == "/tmp" +block: # getCacheDir + doAssert getCacheDir().dirExists + block osenv: block delEnv: const dummyEnvVar = "DUMMY_ENV_VAR" # This env var wouldn't be likely to exist to begin with From 369a7d12467831295c12f5f51aa8c69ec3b3596a Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 31 May 2021 13:17:52 -0700 Subject: [PATCH 2157/3768] jsonutils.toJson now serializes JsonNode as is by default (#18097) * jsonutils.toJson now serializes JsonNode as is (without deep copy nor treating it as a regular ref object) * JsonNodeMode --- changelog.md | 4 +++- lib/std/jsonutils.nim | 20 ++++++++++++++++---- tests/stdlib/tjsonutils.nim | 22 ++++++++++++++++++++++ 3 files changed, 41 insertions(+), 5 deletions(-) diff --git a/changelog.md b/changelog.md index d404b1965..9806a6011 100644 --- a/changelog.md +++ b/changelog.md @@ -69,7 +69,9 @@ previous behavior for a transition time, see PR #17467. - `jsonutils` now serializes/deserializes holey enums as regular enums (via `ord`) instead of as strings. - Use `-d:nimLegacyJsonutilsHoleyEnum` for a transition period. + Use `-d:nimLegacyJsonutilsHoleyEnum` for a transition period. `toJson` now serializes `JsonNode` + as is via reference (without a deep copy) instead of treating `JsonNode` as a regular ref object, + this can be customized via `jsonNodeMode`. - `json` and `jsonutils` now serialize NaN, Inf, -Inf as strings, so that `%[NaN, -Inf]` is the string `["nan","-inf"]` instead of `[nan,-inf]` which was invalid json. diff --git a/lib/std/jsonutils.nim b/lib/std/jsonutils.nim index 1e222e3a2..e33ae4401 100644 --- a/lib/std/jsonutils.nim +++ b/lib/std/jsonutils.nim @@ -48,13 +48,18 @@ type joptEnumOrd joptEnumSymbol joptEnumString + JsonNodeMode* = enum ## controls `toJson` for JsonNode types + joptJsonNodeAsRef ## returns the ref as is + joptJsonNodeAsCopy ## returns a deep copy of the JsonNode + joptJsonNodeAsObject ## treats JsonNode as a regular ref object ToJsonOptions* = object enumMode*: EnumMode - # xxx charMode + jsonNodeMode*: JsonNodeMode + # xxx charMode, etc proc initToJsonOptions*(): ToJsonOptions = ## initializes `ToJsonOptions` with sane options. - ToJsonOptions(enumMode: joptEnumOrd) + ToJsonOptions(enumMode: joptEnumOrd, jsonNodeMode: joptJsonNodeAsRef) proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".} proc distinctBase(T: typedesc): typedesc {.magic: "TypeTrait".} @@ -286,8 +291,15 @@ proc toJson*[T](a: T, opt = initToJsonOptions()): JsonNode = result = newJArray() for v in a.fields: result.add toJson(v, opt) elif T is ref | ptr: - if system.`==`(a, nil): result = newJNull() - else: result = toJson(a[], opt) + template impl = + if system.`==`(a, nil): result = newJNull() + else: result = toJson(a[], opt) + when T is JsonNode: + case opt.jsonNodeMode + of joptJsonNodeAsRef: result = a + of joptJsonNodeAsCopy: result = copy(a) + of joptJsonNodeAsObject: impl() + else: impl() elif T is array | seq | set: result = newJArray() for ai in a: result.add toJson(ai, opt) diff --git a/tests/stdlib/tjsonutils.nim b/tests/stdlib/tjsonutils.nim index 62486b896..88c05facf 100644 --- a/tests/stdlib/tjsonutils.nim +++ b/tests/stdlib/tjsonutils.nim @@ -91,6 +91,28 @@ template fn() = doAssert b2.ord == 1 # explains the `1` testRoundtrip(a): """[1,2,3]""" + block: # JsonNode + let a = ((1, 2.5, "abc").toJson, (3, 4.5, "foo")) + testRoundtripVal(a): """[[1,2.5,"abc"],[3,4.5,"foo"]]""" + + block: + template toInt(a): untyped = cast[int](a) + + let a = 3.toJson + let b = (a, a) + + let c1 = b.toJson + doAssert c1[0].toInt == a.toInt + doAssert c1[1].toInt == a.toInt + + let c2 = b.toJson(ToJsonOptions(jsonNodeMode: joptJsonNodeAsCopy)) + doAssert c2[0].toInt != a.toInt + doAssert c2[1].toInt != c2[0].toInt + doAssert c2[1] == c2[0] + + let c3 = b.toJson(ToJsonOptions(jsonNodeMode: joptJsonNodeAsObject)) + doAssert $c3 == """[{"isUnquoted":false,"kind":2,"num":3},{"isUnquoted":false,"kind":2,"num":3}]""" + block: # ToJsonOptions let a = (me1, me2) doAssert $a.toJson() == "[1,2]" From 06d50bfd4cac6611c5fc775c8130cad87e4dcee1 Mon Sep 17 00:00:00 2001 From: Dylan Modesitt Date: Mon, 31 May 2021 16:51:32 -0400 Subject: [PATCH 2158/3768] Fixes #5034 illformed AST from getImpl with proc returning value (#17976) * Fixes 5034 * address comments --- compiler/semstmts.nim | 25 +++++++++++++++++++++---- tests/macros/tmacrogetimpl.nim | 31 +++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+), 4 deletions(-) create mode 100644 tests/macros/tmacrogetimpl.nim diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index b4630294b..085146f7f 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1439,16 +1439,33 @@ proc semBorrow(c: PContext, n: PNode, s: PSym) = else: localError(c.config, n.info, errNoSymbolToBorrowFromFound) +proc swapResult(n: PNode, sRes: PSym, dNode: PNode) = + ## Swap nodes that are (skResult) symbols to d(estination)Node. + for i in 0.. resultPos and n[resultPos] != nil: - if n[resultPos].sym.kind != skResult or n[resultPos].sym.owner != getCurrOwner(c): + if n[resultPos].sym.kind != skResult: localError(c.config, n.info, "incorrect result proc symbol") + if n[resultPos].sym.owner != getCurrOwner(c): + # re-write result with new ownership, and re-write the proc accordingly + let sResSym = n[resultPos].sym + genResSym(s) + n[resultPos] = newSymNode(s) + swapResult(n, sResSym, n[resultPos]) c.p.resultSym = n[resultPos].sym else: - var s = newSym(skResult, getIdent(c.cache, "result"), nextSymId c.idgen, getCurrOwner(c), n.info) - s.typ = t - incl(s.flags, sfUsed) + genResSym(s) c.p.resultSym = s n.add newSymNode(c.p.resultSym) addParamOrResult(c, c.p.resultSym, owner) diff --git a/tests/macros/tmacrogetimpl.nim b/tests/macros/tmacrogetimpl.nim new file mode 100644 index 000000000..1d996ff29 --- /dev/null +++ b/tests/macros/tmacrogetimpl.nim @@ -0,0 +1,31 @@ +import macros + +# bug #5034 + +macro copyImpl(srsProc: typed, toSym: untyped) = + result = copyNimTree(getImplTransformed(srsProc)) + result[0] = ident $toSym.toStrLit() + +proc foo1(x: float, one: bool = true): float = + if one: + return 1'f + result = x + +proc bar1(what: string): string = + ## be a little more adversarial with `skResult` + proc buzz: string = + result = "lightyear" + if what == "buzz": + result = "buzz " & buzz() + else: + result = what + return result + +copyImpl(foo1, foo2) +doAssert foo1(1'f) == 1.0 +doAssert foo2(10.0, false) == 10.0 +doAssert foo2(10.0) == 1.0 + +copyImpl(bar1, bar2) +doAssert bar1("buzz") == "buzz lightyear" +doAssert bar1("macros") == "macros" From c0e8199acc9f49caa36bae34d103600d112b66f3 Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 1 Jun 2021 05:28:22 +0800 Subject: [PATCH 2159/3768] [std/re] fix findBounds and find procs (#18028) * [std/re] make interface consistent * tiny * revert --- lib/impure/re.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/impure/re.nim b/lib/impure/re.nim index 0c96876b9..40860cc88 100644 --- a/lib/impure/re.nim +++ b/lib/impure/re.nim @@ -171,7 +171,7 @@ proc findBounds*(s: string, pattern: Regex, matches: var openArray[string], proc findBounds*(buf: cstring, pattern: Regex, matches: var openArray[tuple[first, last: int]], - start = 0, bufSize = 0): tuple[first, last: int] = + start = 0, bufSize: int): tuple[first, last: int] = ## returns the starting position and end position of `pattern` in `buf` ## (where `buf` has length `bufSize` and is not necessarily `'\0'` terminated), ## and the captured substrings in the array `matches`. @@ -290,7 +290,7 @@ proc match*(buf: cstring, pattern: Regex, matches: var openArray[string], result = matchLen(buf, pattern, matches, start, bufSize) != -1 proc find*(buf: cstring, pattern: Regex, matches: var openArray[string], - start = 0, bufSize = 0): int = + start = 0, bufSize: int): int = ## returns the starting position of `pattern` in `buf` and the captured ## substrings in the array `matches`. If it does not match, nothing ## is written into `matches` and `-1` is returned. From c2e3dc0ed16a854981769b426e9e327663447df7 Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 2 Jun 2021 00:16:25 +0800 Subject: [PATCH 2160/3768] close #18129 Add setCurrentException for JS backend (#18145) * [std/re] make interface consistent * tiny * revert * close #18129 add setCurrentException * changelog entry --- changelog.md | 2 +- lib/system/jssys.nim | 3 +++ tests/exception/tsetexceptions.nim | 8 ++++++++ 3 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 tests/exception/tsetexceptions.nim diff --git a/changelog.md b/changelog.md index 9806a6011..900b23ad8 100644 --- a/changelog.md +++ b/changelog.md @@ -329,7 +329,7 @@ - Fixed premature garbage collection in asyncdispatch, when a stack trace override is in place. - +- Added setCurrentException for JS backend. ## Language changes diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim index ef06437e5..371cb7962 100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -69,6 +69,9 @@ proc getCurrentExceptionMsg*(): string = return $msg return "" +proc setCurrentException*(exc: ref Exception) = + lastJSError = cast[PJSError](exc) + proc auxWriteStackTrace(f: PCallFrame): string = type TempFrame = tuple[procname: cstring, line: int, filename: cstring] diff --git a/tests/exception/tsetexceptions.nim b/tests/exception/tsetexceptions.nim new file mode 100644 index 000000000..557fc1898 --- /dev/null +++ b/tests/exception/tsetexceptions.nim @@ -0,0 +1,8 @@ +discard """ + targets: "c cpp js" +""" + +let ex = newException(CatchableError, "test") +setCurrentException(ex) +doAssert getCurrentException().msg == ex.msg +doAssert getCurrentExceptionMsg() == ex.msg From ba3ec7b04954a9a60adb6e1a64405f7644f799ee Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Tue, 1 Jun 2021 21:47:23 +0300 Subject: [PATCH 2161/3768] docs: Latex generation improvements (#18141) * docs: improve Latex generation * make it work on Windows + fix ] escaping * minor fixes with escapes and style --- compiler/docgen.nim | 2 +- config/nimdoc.tex.cfg | 202 ++++++++++++++++++++++--------- lib/packages/docutils/rstast.nim | 18 ++- lib/packages/docutils/rstgen.nim | 140 +++++++++++---------- tests/stdlib/trstgen.nim | 4 +- tools/kochdocs.nim | 14 +-- 6 files changed, 252 insertions(+), 128 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index e07db9188..18c568ba0 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -549,7 +549,7 @@ proc getAllRunnableExamplesImpl(d: PDoc; n: PNode, dest: var string, var msg = "Example:" if rdoccmd.len > 0: msg.add " cmd: " & rdoccmd dispA(d.conf, dest, "\n

    $1

    \n", - "\n\\textbf{$1}\n", [msg]) + "\n\n\\textbf{$1}\n", [msg]) inc d.listingCounter let id = $d.listingCounter dest.add(d.config.getOrDefault"doc.listing_start" % [id, "langNim", ""]) diff --git a/config/nimdoc.tex.cfg b/config/nimdoc.tex.cfg index b19b25678..75382ce26 100644 --- a/config/nimdoc.tex.cfg +++ b/config/nimdoc.tex.cfg @@ -8,7 +8,8 @@ split.item.toc = "20" # after this number of characters doc.section = """ -\rsthA{$sectionTitle}\label{$sectionID} +\rsthA[$sectionTitle]{$sectionTitle}\label{$sectionID} + $content """ @@ -17,12 +18,16 @@ doc.section.toc = "" doc.item = """ +\vspace{1em} \phantomsection\addcontentsline{toc}{subsection}{$uniqueName} -\begin{rstpre} +\begin{rstdocitem} $header -\end{rstpre} +\end{rstdocitem} + +\begin{addmargin}[0.05\linewidth]{0pt} $desc +\end{addmargin} """ doc.item.toc = "" @@ -42,46 +47,118 @@ $content # $1 - number of listing in document, $2 - language (e.g. langNim), $3 - anchor doc.listing_start = "\\begin{rstpre}\n" -doc.listing_end = "\n\\end{rstpre}\n" +doc.listing_end = "\n\\end{rstpre}\n\n" doc.file = """ % This file was generated by Nim. % Generated: $date $time UTC -\documentclass[a4paper]{article} -\usepackage[left=2cm,right=3cm,top=3cm,bottom=3cm]{geometry} -\usepackage[utf8]{inputenc} -\usepackage[T1]{fontenc} +% +% Compile it by: xelatex (up to 3 times to get labels generated) +% ------- +% +\documentclass[a4paper,11pt]{article} +\usepackage[a4paper,xetex,left=3cm,right=3cm,top=1.5cm,bottom=2cm]{geometry} + +% for 2-sided printing with larger inner "binding" margin +%\usepackage[a4paper,xetex,twoside,left=4cm,right=2cm,top=1.5cm,bottom=2cm]{geometry} +% for e-readers with 1.77:1 aspect ratio (e.g. 1920x1080) +%\usepackage[xetex,paperheight=27.6cm,paperwidth=15.5cm,left=3mm,right=3mm,top=3mm,bottom=3mm]{geometry} +% for e-readers with 1.45:1 aspect ratio (e.g. 1200x825) +%\usepackage[xetex,paperheight=22.5cm,paperwidth=15.5cm,left=3mm,right=3mm,top=3mm,bottom=3mm]{geometry} +% for e-readers with 1.33:1 aspect ratio (e.g. 1872x1404) +%\usepackage[xetex,paperheight=20.7cm,paperwidth=15.5cm,left=3mm,right=3mm,top=3mm,bottom=3mm]{geometry} + +\usepackage{fontspec} +% logic to select default font with some fall-back fonts. +\IfFontExistsTF{Times New Roman}{% + \setmainfont{Times New Roman} % the default font + \typeout{========================================= nim: using Times New Roman} +}{ + \IfFontExistsTF{FreeSerif}{% + \setmainfont{FreeSerif} % fallback #1 - official GNU font, resembles Times + \typeout{========================================= nim: using FreeSerif} + }{ + \IfFontExistsTF{DejaVuSerif}{% + \setmainfont{DejaVuSerif} % fallback #2 - very widespread free font + \typeout{========================================= nim: using DejaVuSerif} + }{ + \typeout{!!!!!!!!!!!!!!!!!!! Fonts not found !!!!!!!!!!!!!!!!!!!!!!!} + } + } +} + +% default monospace font for code: +\usepackage{GoMono} +\usepackage{relsize} +% make this monospace font 2 steps smaller to hold 80-character line +\newcommand{\rstverbblockfont}{\smaller[2]} +\newcommand{\rstverbinlinefont}{\smaller} + +\usepackage{parskip} % paragraphs delimited by vertical space, no indent \usepackage{graphicx} -\usepackage{lmodern} -\usepackage{fancyvrb, courier} -\usepackage{tabularx} -\usepackage{hyperref} -\usepackage{enumitem} % for option list, enumList, and rstfootnote + +\usepackage{dingbat} % for \carriagereturn, etc +\usepackage{fvextra} % for code blocks (works better than original fancyvrb) +\fvset{ + breaklines, + breakafter={=}:|\_\{\}[](){,}.;+-*/'", + breaksymbolleft=\color{red}{\ensuremath{\hookrightarrow}}, + breaksymbolright=\color{red}{\small\carriagereturn} +} +\fvinlineset{% + breaklines, + breakafter={=}:|\_\{\}[](){,}.;+-*/'", + % that does not work at all when we underline inline code by ulem :-( + commandchars=\\\{\} +} + +\usepackage{scrextend} % for the `addmargin` environment \usepackage{xcolor} -\usepackage[tikz]{mdframed} -\usetikzlibrary{shadows} -\mdfsetup{% -linewidth=3, -topline=false, -rightline=false, -bottomline=false} +\usepackage[urlbordercolor=blue,linkbordercolor=cyan, + pdfborderstyle={/S/U/W 1}]{hyperref} +\usepackage{enumitem} % for option list, enumList, and rstfootnote -\begin{document} -\title{$title $version $subtitle} -\author{$author} +\usepackage[most]{tcolorbox} % boxes around admonitions, code blocks, doc.item -\tolerance 1414 -\hbadness 1414 -\emergencystretch 1.5em -\hfuzz 0.3pt -\widowpenalty=10000 -\vfuzz \hfuzz -\raggedbottom +\newtcolorbox{rstadmonition}[1][]{blanker, breakable, + left=3mm, right=3mm, top=1mm, bottom=1mm, + before upper=\indent, parbox=false, #1} + +\definecolor{rstframecolor}{rgb}{0.85, 0.8, 0.6} + +\newtcolorbox{rstprebox}[1][]{blanker, breakable, + left=3mm, right=3mm, top=1mm, bottom=1mm, + borderline ={0.1em}{0pt}{rstframecolor}, + before upper=\indent, parbox=false, #1} + +\newenvironment{rstpre}{% +\VerbatimEnvironment\begingroup\begin{rstprebox}% +\begin{Verbatim}[fontsize=\rstverbblockfont , commandchars=\\\{\}]}% +{\end{Verbatim}\end{rstprebox}\endgroup} + +\newtcolorbox{rstdocitembox}[1][]{blanker, breakable, + left=3mm, right=3mm, top=1mm, bottom=1mm, + borderline ={1pt}{0pt}{cyan}, + before upper=\indent, parbox=false, #1} + +% Inline code formatting: grey underline, +% use \Verb from fvextras e.g. to display -- correctly as double - +\usepackage[normalem]{ulem} +\newcommand\rstuline{\bgroup\markoverwith{\textcolor{rstframecolor}{\rule[-0.8ex]{2pt}{1.0pt}}}\ULon} + +\newcommand{\rstcode}[1]{% +{\rstverbinlinefont\Verb{\rstuline{#1}}}% +} + +\newcommand{\rstcodeitem}[1]{\Verb{#1}} + +\newenvironment{rstdocitem}{% +\VerbatimEnvironment\begingroup\begin{rstdocitembox}% +\begin{Verbatim}[fontsize=\rstverbblockfont , commandchars=\\\{\}]}% +{\end{Verbatim}\end{rstdocitembox}\endgroup} -\maketitle -\newenvironment{rstpre}{\VerbatimEnvironment\begingroup\begin{Verbatim}[fontsize=\footnotesize , commandchars=\\\{\}]}{\end{Verbatim}\endgroup} \newenvironment{rstfootnote}{\begin{description}[labelindent=1em,leftmargin=1em,labelwidth=2.6em]}{\end{description}} \ifdim\linewidth<30em \def\rstoptleftmargin{0.4\linewidth} @@ -93,37 +170,41 @@ bottomline=false} \newenvironment{rstoptlist}{% \begin{description}[font=\sffamily\bfseries,style=nextline,leftmargin=\rstoptleftmargin,labelwidth=\rstoptlabelwidth]}{\end{description}} -% to pack tabularx into a new environment, special syntax is needed :-( -\newenvironment{rsttab}[1]{\tabularx{\linewidth}{#1}}{\endtabularx} +\usepackage{tabulary} % tables with adjustable cell width and no overflow +% make tabulary prevent overflows (https://tex.stackexchange.com/a/195088) +\tymin=60pt +\tymax=\maxdimen +% to pack tabulary into a new environment, special syntax is needed :-( +\newenvironment{rsttab}[1]{\tabulary{\linewidth}{#1}}{\endtabulary} \newcommand{\rstsub}[1]{\raisebox{-0.5ex}{\scriptsize{#1}}} \newcommand{\rstsup}[1]{\raisebox{0.5ex}{\scriptsize{#1}}} -\newcommand{\rsthA}[1]{\section{#1}} -\newcommand{\rsthB}[1]{\subsection{#1}} -\newcommand{\rsthC}[1]{\subsubsection{#1}} -\newcommand{\rsthD}[1]{\paragraph{#1}} -\newcommand{\rsthE}[1]{\paragraph{#1}} +\newcommand{\rsthA}[2][]{\section[#1]{#2}} +\newcommand{\rsthB}[2][]{\subsection[#1]{#2}} +\newcommand{\rsthC}[2][]{\subsubsection[#1]{#2}} +\newcommand{\rsthD}[2][]{\paragraph[#1]{#2}} +\newcommand{\rsthE}[2][]{\paragraph[#1]{#2}} -\newcommand{\rstovA}[1]{\section*{#1}} -\newcommand{\rstovB}[1]{\subsection*{#1}} -\newcommand{\rstovC}[1]{\subsubsection*{#1}} -\newcommand{\rstovD}[1]{\paragraph*{#1}} -\newcommand{\rstovE}[1]{\paragraph*{#1}} +\newcommand{\rstovA}[2][]{\section*[#1]{#2}} +\newcommand{\rstovB}[2][]{\subsection*[#1]{#2}} +\newcommand{\rstovC}[2][]{\subsubsection*[#1]{#2}} +\newcommand{\rstovD}[2][]{\paragraph*[#1]{#2}} +\newcommand{\rstovE}[2][]{\paragraph*[#1]{#2}} % Syntax highlighting: -\newcommand{\spanDecNumber}[1]{#1} -\newcommand{\spanBinNumber}[1]{#1} -\newcommand{\spanHexNumber}[1]{#1} -\newcommand{\spanOctNumber}[1]{#1} -\newcommand{\spanFloatNumber}[1]{#1} +\newcommand{\spanDecNumber}[1]{\textbf{\textcolor{darkgray}{#1}}} +\newcommand{\spanBinNumber}[1]{\textbf{\textcolor{darkgray}{#1}}} +\newcommand{\spanHexNumber}[1]{\textbf{\textcolor{darkgray}{#1}}} +\newcommand{\spanOctNumber}[1]{\textbf{\textcolor{darkgray}{#1}}} +\newcommand{\spanFloatNumber}[1]{\textbf{\textcolor{darkgray}{#1}}} \newcommand{\spanIdentifier}[1]{#1} \newcommand{\spanKeyword}[1]{\textbf{#1}} -\newcommand{\spanStringLit}[1]{#1} -\newcommand{\spanLongStringLit}[1]{#1} +\newcommand{\spanStringLit}[1]{\textbf{\textcolor{darkgray}{#1}}} +\newcommand{\spanLongStringLit}[1]{\textbf{\textcolor{darkgray}{#1}}} \newcommand{\spanCharLit}[1]{#1} \newcommand{\spanEscapeSequence}[1]{#1} -\newcommand{\spanOperator}[1]{#1} +\newcommand{\spanOperator}[1]{\textbf{#1}} \newcommand{\spanPunctuation}[1]{#1} \newcommand{\spanComment}[1]{\emph{#1}} \newcommand{\spanLongComment}[1]{\emph{#1}} @@ -132,7 +213,7 @@ bottomline=false} \newcommand{\spanTagEnd}[1]{#1} \newcommand{\spanKey}[1]{#1} \newcommand{\spanValue}[1]{#1} -\newcommand{\spanRawData}[1]{#1} +\newcommand{\spanRawData}[1]{\textbf{\textcolor{darkgray}{#1}}} \newcommand{\spanAssembler}[1]{#1} \newcommand{\spanPreprocessor}[1]{#1} \newcommand{\spanDirective}[1]{#1} @@ -142,11 +223,20 @@ bottomline=false} \newcommand{\spanLabel}[1]{#1} \newcommand{\spanReference}[1]{#1} \newcommand{\spanOther}[1]{#1} -\newcommand{\spantok}[1]{\frame{#1}} +\newcommand{\spantok}[1]{\fbox{#1}} \newcommand{\spanPrompt}[1]{\textcolor{red}{\textbf{#1}}} -\newcommand{\spanProgramOutput}[1]{\textcolor{gray}{\textbf{#1}}} +\newcommand{\spanProgramOutput}[1]{\textcolor{darkgray}{\textbf{#1}}} \newcommand{\spanprogram}[1]{\textbf{\underline{#1}}} -\newcommand{\spanoption}[1]{\textbf{#1}} +\newcommand{\spanoption}[1]{\textbf{\textcolor{darkgray}{#1}}} + +\begin{document} +\title{$title $version $subtitle} +\author{$author} + +% Never allow text overflow to margin: +\setlength\emergencystretch{\hsize}\hbadness=10000 + +\maketitle $content \end{document} diff --git a/lib/packages/docutils/rstast.nim b/lib/packages/docutils/rstast.nim index 81e3ba6d9..2489ce40c 100644 --- a/lib/packages/docutils/rstast.nim +++ b/lib/packages/docutils/rstast.nim @@ -349,8 +349,24 @@ proc renderRstToJson*(node: PRstNode): string = ## } renderRstToJsonNode(node).pretty +proc renderRstToText*(node: PRstNode): string = + ## minimal text representation of markup node + const code = {rnCodeFragment, rnInterpretedText, rnInlineLiteral, rnInlineCode} + if node == nil: + return "" + case node.kind + of rnLeaf, rnSmiley: + result.add node.text + else: + if node.kind in code: result.add "`" + for i in 0 ..< node.sons.len: + if node.kind in {rnInlineCode, rnCodeBlock} and i == 0: + continue # omit language specifier + result.add renderRstToText(node.sons[i]) + if node.kind in code: result.add "`" + proc renderRstToStr*(node: PRstNode, indent=0): string = - ## Writes the parsed RST `node` into a compact string + ## Writes the parsed RST `node` into an AST tree with compact string ## representation in the format (one line per every sub-node): ## ``indent - kind - [text|level|order|adType] - anchor (if non-zero)`` ## (suitable for debugging of RST parsing). diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 5ab6f28ee..e3f889283 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -60,6 +60,10 @@ type MetaEnum* = enum metaNone, metaTitle, metaSubtitle, metaAuthor, metaVersion + EscapeMode = enum # in Latex text inside options [] and URLs is + # escaped slightly differently than in normal text + emText, emOption, emUrl # emText is currently used for code also + RstGenerator* = object of RootObj target*: OutputTarget config*: StringTableRef @@ -84,6 +88,7 @@ type id*: int ## A counter useful for generating IDs. onTestSnippet*: proc (d: var RstGenerator; filename, cmd: string; status: int; content: string) + escMode*: EscapeMode PDoc = var RstGenerator ## Alias to type less. @@ -161,6 +166,7 @@ proc initRstGenerator*(g: var RstGenerator, target: OutputTarget, g.findFile = findFile g.currentSection = "" g.id = 0 + g.escMode = emText let fileParts = filename.splitFile if fileParts.ext == ".nim": g.currentSection = "Module " & fileParts.name @@ -189,28 +195,34 @@ proc addHtmlChar(dest: var string, c: char) = of '\"': add(dest, """) else: add(dest, c) -proc addRtfChar(dest: var string, c: char) = +proc addTexChar(dest: var string, c: char, escMode: EscapeMode) = + ## Escapes 10 special Latex characters and sometimes ` and [, ]. + ## TODO: @ is always a normal symbol (besides the header), am I wrong? + ## All escapes that need to work in text and code blocks (`emText` mode) + ## should start from \ (to be compatible with fancyvrb/fvextra). case c - of '{': add(dest, "\\{") - of '}': add(dest, "\\}") - of '\\': add(dest, "\\\\") - else: add(dest, c) - -proc addTexChar(dest: var string, c: char) = - # Escapes 10 special Latex characters. Note that [, ], and ` are not - # considered as such. TODO: neither is @, am I wrong? - case c - of '_', '{', '}', '$', '&', '#', '%': add(dest, "\\" & c) + of '_', '$', '&', '#', '%': add(dest, "\\" & c) # \~ and \^ have a special meaning unless they are followed by {} of '~', '^': add(dest, "\\" & c & "{}") + # Latex loves to substitute ` to opening quote, even in texttt mode! + of '`': add(dest, "\\textasciigrave{}") # add {} to avoid gobbling up space by \textbackslash of '\\': add(dest, "\\textbackslash{}") + # Using { and } in URL in Latex: https://tex.stackexchange.com/a/469175 + of '{': + add(dest, if escMode == emUrl: "\\%7B" else: "\\{") + of '}': + add(dest, if escMode == emUrl: "\\%7D" else: "\\}") + of ']': + # escape ] inside an optional argument in e.g. \section[static[T]]{.. + add(dest, if escMode == emOption: "\\text{]}" else: "]") else: add(dest, c) -proc escChar*(target: OutputTarget, dest: var string, c: char) {.inline.} = +proc escChar*(target: OutputTarget, dest: var string, + c: char, escMode: EscapeMode) {.inline.} = case target of outHtml: addHtmlChar(dest, c) - of outLatex: addTexChar(dest, c) + of outLatex: addTexChar(dest, c, escMode) proc addSplitter(target: OutputTarget; dest: var string) {.inline.} = case target @@ -229,7 +241,7 @@ proc nextSplitPoint*(s: string, start: int): int = inc(result) dec(result) # last valid index -proc esc*(target: OutputTarget, s: string, splitAfter = -1): string = +proc esc*(target: OutputTarget, s: string, splitAfter = -1, escMode = emText): string = ## Escapes the HTML. result = "" if splitAfter >= 0: @@ -240,11 +252,11 @@ proc esc*(target: OutputTarget, s: string, splitAfter = -1): string = #if (splitter != " ") or (partLen + k - j + 1 > splitAfter): partLen = 0 addSplitter(target, result) - for i in countup(j, k): escChar(target, result, s[i]) + for i in countup(j, k): escChar(target, result, s[i], escMode) inc(partLen, k - j + 1) j = k + 1 else: - for i in countup(0, len(s) - 1): escChar(target, result, s[i]) + for i in countup(0, len(s) - 1): escChar(target, result, s[i], escMode) proc disp(target: OutputTarget, xml, tex: string): string = @@ -760,6 +772,8 @@ proc renderHeadline(d: PDoc, n: PRstNode, result: var string) = sectionPrefix = rstnodeToRefname(n2) & "-" break var refname = sectionPrefix & rstnodeToRefname(n) + var tocName = esc(d.target, renderRstToText(n), escMode = emOption) + # for Latex: simple text without commands that may break TOC/hyperref if d.hasToc: var length = len(d.tocPart) setLen(d.tocPart, length + 1) @@ -768,13 +782,14 @@ proc renderHeadline(d: PDoc, n: PRstNode, result: var string) = d.tocPart[length].header = tmp dispA(d.target, result, "\n$3", "\\rsth$4{$3}$2\n", - [$n.level, refname.idS, tmp, $chr(n.level - 1 + ord('A')), refname]) + "$2 href=\"#$5\">$3", "\\rsth$4[$6]{$3}$2\n", + [$n.level, refname.idS, tmp, + $chr(n.level - 1 + ord('A')), refname, tocName]) else: dispA(d.target, result, "\n$3", - "\\rsth$4{$3}$2\n", [ + "\\rsth$4[$5]{$3}$2\n", [ $n.level, refname.idS, tmp, - $chr(n.level - 1 + ord('A'))]) + $chr(n.level - 1 + ord('A')), tocName]) # Generate index entry using spaces to indicate TOC level for the output HTML. assert n.level >= 0 @@ -802,9 +817,10 @@ proc renderOverline(d: PDoc, n: PRstNode, result: var string) = var tmp = "" for i in countup(0, len(n) - 1): renderRstToOut(d, n.sons[i], tmp) d.currentSection = tmp + var tocName = esc(d.target, renderRstToText(n), escMode=emOption) dispA(d.target, result, "
    $3
    ", - "\\rstov$4{$3}$2\n", [$n.level, - rstnodeToRefname(n).idS, tmp, $chr(n.level - 1 + ord('A'))]) + "\\rstov$4[$5]{$3}$2\n", [$n.level, + rstnodeToRefname(n).idS, tmp, $chr(n.level - 1 + ord('A')), tocName]) proc renderTocEntry(d: PDoc, e: TocEntry, result: var string) = @@ -870,7 +886,7 @@ proc renderImage(d: PDoc, n: PRstNode, result: var string) = htmlOut = "" # support for `:target:` links for images: - var target = esc(d.target, getFieldValue(n, "target").strip()) + var target = esc(d.target, getFieldValue(n, "target").strip(), escMode=emUrl) if target.len > 0: # `htmlOut` needs to be of the following format for link to work for images: # @@ -1031,16 +1047,16 @@ proc renderCode(d: PDoc, n: PRstNode, result: var string) = blockEnd = "
    " of outLatex: if n.kind == rnCodeBlock: - blockStart = "\n\n\\begin{rstpre}" & n.anchor.idS & "\n" - blockEnd = "\n\\end{rstpre}\n" + blockStart = "\n\n" & n.anchor.idS & "\\begin{rstpre}\n" + blockEnd = "\n\\end{rstpre}\n\n" else: # rnInlineCode - blockStart = "\\texttt{" + blockStart = "\\rstcode{" blockEnd = "}" dispA(d.target, result, blockStart, blockStart, []) if params.lang == langNone: if len(params.langStr) > 0: d.msgHandler(d.filename, 1, 0, mwUnsupportedLanguage, params.langStr) - for letter in m.text: escChar(d.target, result, letter) + for letter in m.text: escChar(d.target, result, letter, emText) else: renderCodeLang(result, params.lang, m.text, d.target) dispA(d.target, result, blockEnd, blockEnd) @@ -1055,9 +1071,8 @@ proc renderContainer(d: PDoc, n: PRstNode, result: var string) = dispA(d.target, result, "
    $2
    ", "$2", [arg, tmp]) proc texColumns(n: PRstNode): string = - result = "" let nColumns = if n.sons.len > 0: len(n.sons[0]) else: 1 - for i in countup(1, nColumns): add(result, "|X") + result = "L".repeat(nColumns) proc renderField(d: PDoc, n: PRstNode, result: var string) = var b = false @@ -1144,19 +1159,38 @@ proc renderAdmonition(d: PDoc, n: PRstNode, result: var string) = renderAux(d, n, htmlHead & "" & txt & ":\n" & "$1\n", - "\n\n\\begin{mdframed}[linecolor=" & texColor & "]$2\n" & + "\n\n\\begin{rstadmonition}[borderline west={0.2em}{0pt}{" & + texColor & "}]$2\n" & "{" & texSz & "\\color{" & texColor & "}{\\textbf{" & txt & ":}}} " & - "$1\n\\end{mdframed}\n", + "$1\n\\end{rstadmonition}\n", result) +proc renderHyperlink(d: PDoc, text, link: PRstNode, result: var string, external: bool) = + var linkStr = "" + block: + let mode = d.escMode + d.escMode = emUrl + renderRstToOut(d, link, linkStr) + d.escMode = mode + var textStr = "" + renderRstToOut(d, text, textStr) + if external: + dispA(d.target, result, + "$1", + "\\href{$2}{$1}", [textStr, linkStr]) + else: + dispA(d.target, result, + "$1", + "\\hyperlink{$2}{$1} (p.~\\pageref{$2})", [textStr, linkStr]) + proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = if n == nil: return case n.kind of rnInner: renderAux(d, n, result) of rnHeadline, rnMarkdownHeadline: renderHeadline(d, n, result) of rnOverline: renderOverline(d, n, result) - of rnTransition: renderAux(d, n, "\n", "\\hrule$2\n", result) - of rnParagraph: renderAux(d, n, "$1

    \n", "$2\n$1\n\n", result) + of rnTransition: renderAux(d, n, "\n", "\n\n\\vspace{0.6em}\\hrule$2\n", result) + of rnParagraph: renderAux(d, n, "$1

    \n", "\n\n$2\n$1\n\n", result) of rnBulletList: renderAux(d, n, "$1\n", "\\begin{itemize}\n$2\n$1\\end{itemize}\n", result) @@ -1202,7 +1236,7 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = renderAux(d, n, "
    " & "$1
    ", - "\\item[$1]", result) + "\\item[\\rstcodeitem{\\spanoption{$1}}]", result) of rnDescription: renderAux(d, n, "
    $1
    ", " $1\n", result) @@ -1210,7 +1244,7 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = doAssert false, "renderRstToOut" of rnLiteralBlock: renderAux(d, n, "$1
    \n", - "\\begin{rstpre}\n$2\n$1\n\\end{rstpre}\n", result) + "\n\n\\begin{rstpre}\n$2\n$1\n\\end{rstpre}\n\n", result) of rnQuotedLiteralBlock: doAssert false, "renderRstToOut" of rnLineBlock: @@ -1237,8 +1271,8 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = of rnTable, rnGridTable, rnMarkdownTable: renderAux(d, n, "$1", - "\\begin{table}\n$2\n\\begin{rsttab}{" & - texColumns(n) & "|}\n\\hline\n$1\\end{rsttab}\\end{table}", result) + "\n$2\n\\begin{rsttab}{" & + texColumns(n) & "}\n\\hline\n$1\\end{rsttab}", result) of rnTableRow: if len(n) >= 1: if d.target == outLatex: @@ -1275,21 +1309,13 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = "\\item[\\textsuperscript{[$3]}]$2 $1\n", [body, n.anchor.idS, mark, n.anchor]) of rnRef: - var tmp = "" - renderAux(d, n, tmp) - dispA(d.target, result, - "$1", - "$1\\ref{$2}", [tmp, rstnodeToRefname(n)]) + renderHyperlink(d, text=n.sons[0], link=n.sons[0], result, external=false) of rnStandaloneHyperlink: - renderAux(d, n, - "$1", - "\\href{$1}{$1}", result) + renderHyperlink(d, text=n.sons[0], link=n.sons[0], result, external=true) of rnInternalRef: - var tmp = "" - renderAux(d, n.sons[0], tmp) - dispA(d.target, result, - "$1", - "\\hyperlink{$2}{$1} (p.~\\pageref{$2})", [tmp, n.sons[1].text]) + renderHyperlink(d, text=n.sons[0], link=n.sons[1], result, external=false) + of rnHyperlink: + renderHyperlink(d, text=n.sons[0], link=n.sons[1], result, external=true) of rnFootnoteRef: var tmp = "[" renderAux(d, n.sons[0], tmp) @@ -1299,14 +1325,6 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = "$1", "\\textsuperscript{\\hyperlink{$2}{\\textbf{$1}}}", [tmp, n.sons[1].text]) - of rnHyperlink: - var tmp0 = "" - var tmp1 = "" - renderRstToOut(d, n.sons[0], tmp0) - renderRstToOut(d, n.sons[1], tmp1) - dispA(d.target, result, - "$1", - "\\href{$2}{$1}", [tmp0, tmp1]) of rnDirArg, rnRaw: renderAux(d, n, result) of rnRawHtml: if d.target != outLatex and not lastSon(n).isNil: @@ -1334,7 +1352,7 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = dispA(d.target, result, "" & "$1", - "\\texttt{\\span$2{$1}}", [tmp0, class]) + "\\rstcode{\\span$2{$1}}", [tmp0, class]) else: # rnUnknownRole, not necessarily code/monospace font dispA(d.target, result, "$1", "\\span$2{$1}", [tmp0, class]) @@ -1351,7 +1369,7 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = of rnInlineLiteral, rnInterpretedText: renderAux(d, n, "$1", - "\\texttt{$1}", result) + "\\rstcode{$1}", result) of rnInlineTarget: var tmp = "" renderAux(d, n, tmp) @@ -1360,7 +1378,7 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = "\\label{$2}\\hypertarget{$2}{$1}", [tmp, rstnodeToRefname(n)]) of rnSmiley: renderSmiley(d, n, result) - of rnLeaf: result.add(esc(d.target, n.text)) + of rnLeaf: result.add(esc(d.target, n.text, escMode=d.escMode)) of rnContents: d.hasToc = true of rnDefaultRole: discard of rnTitle: diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim index 864728686..34b172935 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -259,7 +259,7 @@ A2 A3 A4 A5 ==== === """ let output1 = rstToLatex(input1, {}) - doAssert "{|X|X|}" in output1 # 2 columns + doAssert "{LL}" in output1 # 2 columns doAssert count(output1, "\\\\") == 4 # 4 rows for cell in ["H0", "H1", "A0", "A1", "A2", "A3", "A4", "A5"]: doAssert cell in output1 @@ -274,7 +274,7 @@ A0 A1 X Ax Y ==== === = """ let output2 = rstToLatex(input2, {}) - doAssert "{|X|X|X|}" in output2 # 3 columns + doAssert "{LLL}" in output2 # 3 columns doAssert count(output2, "\\\\") == 2 # 2 rows for cell in ["H0", "H1", "H", "A0", "A1", "X", "Ax", "Y"]: doAssert cell in output2 diff --git a/tools/kochdocs.nim b/tools/kochdocs.nim index 621dc643f..f25564fad 100644 --- a/tools/kochdocs.nim +++ b/tools/kochdocs.nim @@ -284,22 +284,22 @@ proc buildDoc(nimArgs, destPath: string) = proc nim2pdf(src: string, dst: string, nimArgs: string) = # xxx expose as a `nim` command or in some other reusable way. - let outDir = "build" / "pdflatextmp" # xxx factor pending https://github.com/timotheecour/Nim/issues/616 + let outDir = "build" / "xelatextmp" # xxx factor pending https://github.com/timotheecour/Nim/issues/616 # note: this will generate temporary files in gitignored `outDir`: aux toc log out tex exec("$# rst2tex $# --outdir:$# $#" % [findNim().quoteShell(), nimArgs, outDir.quoteShell, src.quoteShell]) let texFile = outDir / src.lastPathPart.changeFileExt("tex") - for i in 0..<2: # call LaTeX twice to get cross references right: - let pdflatexLog = outDir / "pdflatex.log" + for i in 0..<3: # call LaTeX three times to get cross references right: + let xelatexLog = outDir / "xelatex.log" # `>` should work on windows, if not, we can use `execCmdEx` - let cmd = "pdflatex -interaction=nonstopmode -output-directory=$# $# > $#" % [outDir.quoteShell, texFile.quoteShell, pdflatexLog.quoteShell] - exec(cmd) # on error, user can inspect `pdflatexLog` + let cmd = "xelatex -interaction=nonstopmode -output-directory=$# $# > $#" % [outDir.quoteShell, texFile.quoteShell, xelatexLog.quoteShell] + exec(cmd) # on error, user can inspect `xelatexLog` moveFile(texFile.changeFileExt("pdf"), dst) proc buildPdfDoc*(nimArgs, destPath: string) = var pdfList: seq[string] createDir(destPath) - if os.execShellCmd("pdflatex -version") != 0: - doAssert false, "pdflatex not found" # or, raise an exception + if os.execShellCmd("xelatex -version") != 0: + doAssert false, "xelatex not found" # or, raise an exception else: for src in items(rstPdfList): let dst = destPath / src.lastPathPart.changeFileExt("pdf") From 63db2b19bf0787db89ce89c8861e976d44e33cbd Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 1 Jun 2021 22:29:53 +0200 Subject: [PATCH 2162/3768] use dragonbox algorithm; alternative to #18008 (#18139) * use dragonbox algorithm; alternative to #18008 * removed unsafe code --- changelog.md | 4 + compiler/condsyms.nim | 2 + lib/system/dragonbox.nim | 1347 ++++++++++++++++++++++++++++++++++++ lib/system/formatfloat.nim | 97 +-- tests/float/tfloat6.nim | 8 +- 5 files changed, 1408 insertions(+), 50 deletions(-) create mode 100644 lib/system/dragonbox.nim diff --git a/changelog.md b/changelog.md index 900b23ad8..a23aedbdf 100644 --- a/changelog.md +++ b/changelog.md @@ -76,6 +76,10 @@ - `json` and `jsonutils` now serialize NaN, Inf, -Inf as strings, so that `%[NaN, -Inf]` is the string `["nan","-inf"]` instead of `[nan,-inf]` which was invalid json. +- `system.addFloat` now uses the "Dragonbox" algorithm, which ensures correct roundtrips of floating point + numbers, that the minimum length representation of a floating point number is used and correct rounding. + Use `-d:nimLegacyAddFloat` for a transition period. + - `strformat` is now part of `include std/prelude`. - Deprecated `proc reversed*[T](a: openArray[T], first: Natural, last: int): seq[T]` in `std/algorithm`. diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 6a49584c8..ff0c603cb 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -134,3 +134,5 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasUnifiedTuple") defineSymbol("nimHasIterable") defineSymbol("nimHasTypeofVoid") + + defineSymbol("nimHasDragonBox") diff --git a/lib/system/dragonbox.nim b/lib/system/dragonbox.nim new file mode 100644 index 000000000..db608c630 --- /dev/null +++ b/lib/system/dragonbox.nim @@ -0,0 +1,1347 @@ +## Copyright 2020 Junekey Jeon +## Copyright 2020 Alexander Bolz +## +## Distributed under the Boost Software License, Version 1.0. +## (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt) + +## char* output_end = Dtoa(buffer, value); +## +## Converts the given double-precision number into decimal form and stores the result in the given +## buffer. +## +## The buffer must be large enough, i.e. >= DtoaMinBufferLength. +## The output format is similar to printf("%g"). +## The output is _not_ null-terminted. +## +## The output is optimal, i.e. the output string +## 1. rounds back to the input number when read in (using round-to-nearest-even) +## 2. is as short as possible, +## 3. is as close to the input number as possible. +## +## Note: +## This function may temporarily write up to DtoaMinBufferLength characters into the buffer. + +const + dtoaMinBufferLength*: cint = 64 + +## -------------------------------------------------------------------------------------------------- +## This file contains an implementation of Junekey Jeon's Dragonbox algorithm. +## +## It is a simplified version of the reference implementation found here: +## https://github.com/jk-jeon/dragonbox +## +## The reference implementation also works with single-precision floating-point numbers and +## has options to configure the rounding mode. +## -------------------------------------------------------------------------------------------------- + +template dragonbox_Assert*(x: untyped): untyped = + assert(x) + +## ================================================================================================== +## +## ================================================================================================== + +type + ValueType* = float + BitsType* = uint64 + +type + Double* = object + bits*: BitsType + +const ## = p (includes the hidden bit) + significandSize*: int32 = 53 + +const ## static constexpr int32_t MaxExponent = 1024 - 1 - (SignificandSize - 1); + ## static constexpr int32_t MinExponent = std::numeric_limits::min_exponent - 1 - (SignificandSize - 1); + exponentBias*: int32 = 1024 - 1 + (significandSize - 1) + +const + maxIeeeExponent*: BitsType = BitsType(2 * 1024 - 1) + +const ## = 2^(p-1) + hiddenBit*: BitsType = BitsType(1) shl (significandSize - 1) + +const ## = 2^(p-1) - 1 + significandMask*: BitsType = hiddenBit - 1 + +const + exponentMask*: BitsType = maxIeeeExponent shl (significandSize - 1) + +const + signMask*: BitsType = not (not BitsType(0) shr 1) + +proc constructDouble*(bits: BitsType): Double {.constructor.} = + result.bits = bits + +proc constructDouble*(value: ValueType): Double {.constructor.} = + result.bits = cast[typeof(result.bits)](value) + +proc physicalSignificand*(this: Double): BitsType {.noSideEffect.} = + return this.bits and significandMask + +proc physicalExponent*(this: Double): BitsType {.noSideEffect.} = + return (this.bits and exponentMask) shr (significandSize - 1) + +proc isFinite*(this: Double): bool {.noSideEffect.} = + return (this.bits and exponentMask) != exponentMask + +proc isInf*(this: Double): bool {.noSideEffect.} = + return (this.bits and exponentMask) == exponentMask and + (this.bits and significandMask) == 0 + +proc isNaN*(this: Double): bool {.noSideEffect.} = + return (this.bits and exponentMask) == exponentMask and + (this.bits and significandMask) != 0 + +proc isZero*(this: Double): bool {.noSideEffect.} = + return (this.bits and not signMask) == 0 + +proc signBit*(this: Double): int {.noSideEffect.} = + return ord((this.bits and signMask) != 0) + +## namespace +## ================================================================================================== +## +## ================================================================================================== +## Returns floor(x / 2^n). +## +## Technically, right-shift of negative integers is implementation defined... +## Should easily be optimized into SAR (or equivalent) instruction. + +proc floorDivPow2*(x: int32; n: int32): int32 {.inline.} = + return x shr n + +proc floorLog2Pow10*(e: int32): int32 {.inline.} = + dragonbox_Assert(e >= -1233) + dragonbox_Assert(e <= 1233) + return floorDivPow2(e * 1741647, 19) + +proc floorLog10Pow2*(e: int32): int32 {.inline.} = + dragonbox_Assert(e >= -1500) + dragonbox_Assert(e <= 1500) + return floorDivPow2(e * 1262611, 22) + +proc floorLog10ThreeQuartersPow2*(e: int32): int32 {.inline.} = + dragonbox_Assert(e >= -1500) + dragonbox_Assert(e <= 1500) + return floorDivPow2(e * 1262611 - 524031, 22) + +## ================================================================================================== +## +## ================================================================================================== + +type + uint64x2* {.bycopy.} = object + hi*: uint64 + lo*: uint64 + + +proc computePow10*(k: int32): uint64x2 {.inline.} = + const + kMin: int32 = -292 + const + kMax: int32 = 326 + const + pow10: array[kMax - kMin + 1, uint64x2] = [ + uint64x2(hi: 0xFF77B1FCBEBCDC4F'u, lo: 0x25E8E89C13BB0F7B'u), + uint64x2(hi: 0x9FAACF3DF73609B1'u, lo: 0x77B191618C54E9AD'u), + uint64x2(hi: 0xC795830D75038C1D'u, lo: 0xD59DF5B9EF6A2418'u), + uint64x2(hi: 0xF97AE3D0D2446F25'u, lo: 0x4B0573286B44AD1E'u), + uint64x2(hi: 0x9BECCE62836AC577'u, lo: 0x4EE367F9430AEC33'u), + uint64x2(hi: 0xC2E801FB244576D5'u, lo: 0x229C41F793CDA740'u), + uint64x2(hi: 0xF3A20279ED56D48A'u, lo: 0x6B43527578C11110'u), + uint64x2(hi: 0x9845418C345644D6'u, lo: 0x830A13896B78AAAA'u), + uint64x2(hi: 0xBE5691EF416BD60C'u, lo: 0x23CC986BC656D554'u), + uint64x2(hi: 0xEDEC366B11C6CB8F'u, lo: 0x2CBFBE86B7EC8AA9'u), + uint64x2(hi: 0x94B3A202EB1C3F39'u, lo: 0x7BF7D71432F3D6AA'u), + uint64x2(hi: 0xB9E08A83A5E34F07'u, lo: 0xDAF5CCD93FB0CC54'u), + uint64x2(hi: 0xE858AD248F5C22C9'u, lo: 0xD1B3400F8F9CFF69'u), + uint64x2(hi: 0x91376C36D99995BE'u, lo: 0x23100809B9C21FA2'u), + uint64x2(hi: 0xB58547448FFFFB2D'u, lo: 0xABD40A0C2832A78B'u), + uint64x2(hi: 0xE2E69915B3FFF9F9'u, lo: 0x16C90C8F323F516D'u), + uint64x2(hi: 0x8DD01FAD907FFC3B'u, lo: 0xAE3DA7D97F6792E4'u), + uint64x2(hi: 0xB1442798F49FFB4A'u, lo: 0x99CD11CFDF41779D'u), + uint64x2(hi: 0xDD95317F31C7FA1D'u, lo: 0x40405643D711D584'u), + uint64x2(hi: 0x8A7D3EEF7F1CFC52'u, lo: 0x482835EA666B2573'u), + uint64x2(hi: 0xAD1C8EAB5EE43B66'u, lo: 0xDA3243650005EED0'u), + uint64x2(hi: 0xD863B256369D4A40'u, lo: 0x90BED43E40076A83'u), + uint64x2(hi: 0x873E4F75E2224E68'u, lo: 0x5A7744A6E804A292'u), + uint64x2(hi: 0xA90DE3535AAAE202'u, lo: 0x711515D0A205CB37'u), + uint64x2(hi: 0xD3515C2831559A83'u, lo: 0x0D5A5B44CA873E04'u), + uint64x2(hi: 0x8412D9991ED58091'u, lo: 0xE858790AFE9486C3'u), + uint64x2(hi: 0xA5178FFF668AE0B6'u, lo: 0x626E974DBE39A873'u), + uint64x2(hi: 0xCE5D73FF402D98E3'u, lo: 0xFB0A3D212DC81290'u), + uint64x2(hi: 0x80FA687F881C7F8E'u, lo: 0x7CE66634BC9D0B9A'u), + uint64x2(hi: 0xA139029F6A239F72'u, lo: 0x1C1FFFC1EBC44E81'u), + uint64x2(hi: 0xC987434744AC874E'u, lo: 0xA327FFB266B56221'u), + uint64x2(hi: 0xFBE9141915D7A922'u, lo: 0x4BF1FF9F0062BAA9'u), + uint64x2(hi: 0x9D71AC8FADA6C9B5'u, lo: 0x6F773FC3603DB4AA'u), + uint64x2(hi: 0xC4CE17B399107C22'u, lo: 0xCB550FB4384D21D4'u), + uint64x2(hi: 0xF6019DA07F549B2B'u, lo: 0x7E2A53A146606A49'u), + uint64x2(hi: 0x99C102844F94E0FB'u, lo: 0x2EDA7444CBFC426E'u), + uint64x2(hi: 0xC0314325637A1939'u, lo: 0xFA911155FEFB5309'u), + uint64x2(hi: 0xF03D93EEBC589F88'u, lo: 0x793555AB7EBA27CB'u), + uint64x2(hi: 0x96267C7535B763B5'u, lo: 0x4BC1558B2F3458DF'u), + uint64x2(hi: 0xBBB01B9283253CA2'u, lo: 0x9EB1AAEDFB016F17'u), + uint64x2(hi: 0xEA9C227723EE8BCB'u, lo: 0x465E15A979C1CADD'u), + uint64x2(hi: 0x92A1958A7675175F'u, lo: 0x0BFACD89EC191ECA'u), + uint64x2(hi: 0xB749FAED14125D36'u, lo: 0xCEF980EC671F667C'u), + uint64x2(hi: 0xE51C79A85916F484'u, lo: 0x82B7E12780E7401B'u), + uint64x2(hi: 0x8F31CC0937AE58D2'u, lo: 0xD1B2ECB8B0908811'u), + uint64x2(hi: 0xB2FE3F0B8599EF07'u, lo: 0x861FA7E6DCB4AA16'u), + uint64x2(hi: 0xDFBDCECE67006AC9'u, lo: 0x67A791E093E1D49B'u), + uint64x2(hi: 0x8BD6A141006042BD'u, lo: 0xE0C8BB2C5C6D24E1'u), + uint64x2(hi: 0xAECC49914078536D'u, lo: 0x58FAE9F773886E19'u), + uint64x2(hi: 0xDA7F5BF590966848'u, lo: 0xAF39A475506A899F'u), + uint64x2(hi: 0x888F99797A5E012D'u, lo: 0x6D8406C952429604'u), + uint64x2(hi: 0xAAB37FD7D8F58178'u, lo: 0xC8E5087BA6D33B84'u), + uint64x2(hi: 0xD5605FCDCF32E1D6'u, lo: 0xFB1E4A9A90880A65'u), + uint64x2(hi: 0x855C3BE0A17FCD26'u, lo: 0x5CF2EEA09A550680'u), + uint64x2(hi: 0xA6B34AD8C9DFC06F'u, lo: 0xF42FAA48C0EA481F'u), + uint64x2(hi: 0xD0601D8EFC57B08B'u, lo: 0xF13B94DAF124DA27'u), + uint64x2(hi: 0x823C12795DB6CE57'u, lo: 0x76C53D08D6B70859'u), + uint64x2(hi: 0xA2CB1717B52481ED'u, lo: 0x54768C4B0C64CA6F'u), + uint64x2(hi: 0xCB7DDCDDA26DA268'u, lo: 0xA9942F5DCF7DFD0A'u), + uint64x2(hi: 0xFE5D54150B090B02'u, lo: 0xD3F93B35435D7C4D'u), + uint64x2(hi: 0x9EFA548D26E5A6E1'u, lo: 0xC47BC5014A1A6DB0'u), + uint64x2(hi: 0xC6B8E9B0709F109A'u, lo: 0x359AB6419CA1091C'u), + uint64x2(hi: 0xF867241C8CC6D4C0'u, lo: 0xC30163D203C94B63'u), + uint64x2(hi: 0x9B407691D7FC44F8'u, lo: 0x79E0DE63425DCF1E'u), + uint64x2(hi: 0xC21094364DFB5636'u, lo: 0x985915FC12F542E5'u), + uint64x2(hi: 0xF294B943E17A2BC4'u, lo: 0x3E6F5B7B17B2939E'u), + uint64x2(hi: 0x979CF3CA6CEC5B5A'u, lo: 0xA705992CEECF9C43'u), + uint64x2(hi: 0xBD8430BD08277231'u, lo: 0x50C6FF782A838354'u), + uint64x2(hi: 0xECE53CEC4A314EBD'u, lo: 0xA4F8BF5635246429'u), + uint64x2(hi: 0x940F4613AE5ED136'u, lo: 0x871B7795E136BE9A'u), + uint64x2(hi: 0xB913179899F68584'u, lo: 0x28E2557B59846E40'u), + uint64x2(hi: 0xE757DD7EC07426E5'u, lo: 0x331AEADA2FE589D0'u), + uint64x2(hi: 0x9096EA6F3848984F'u, lo: 0x3FF0D2C85DEF7622'u), + uint64x2(hi: 0xB4BCA50B065ABE63'u, lo: 0x0FED077A756B53AA'u), + uint64x2(hi: 0xE1EBCE4DC7F16DFB'u, lo: 0xD3E8495912C62895'u), + uint64x2(hi: 0x8D3360F09CF6E4BD'u, lo: 0x64712DD7ABBBD95D'u), + uint64x2(hi: 0xB080392CC4349DEC'u, lo: 0xBD8D794D96AACFB4'u), + uint64x2(hi: 0xDCA04777F541C567'u, lo: 0xECF0D7A0FC5583A1'u), + uint64x2(hi: 0x89E42CAAF9491B60'u, lo: 0xF41686C49DB57245'u), + uint64x2(hi: 0xAC5D37D5B79B6239'u, lo: 0x311C2875C522CED6'u), + uint64x2(hi: 0xD77485CB25823AC7'u, lo: 0x7D633293366B828C'u), + uint64x2(hi: 0x86A8D39EF77164BC'u, lo: 0xAE5DFF9C02033198'u), + uint64x2(hi: 0xA8530886B54DBDEB'u, lo: 0xD9F57F830283FDFD'u), + uint64x2(hi: 0xD267CAA862A12D66'u, lo: 0xD072DF63C324FD7C'u), + uint64x2(hi: 0x8380DEA93DA4BC60'u, lo: 0x4247CB9E59F71E6E'u), + uint64x2(hi: 0xA46116538D0DEB78'u, lo: 0x52D9BE85F074E609'u), + uint64x2(hi: 0xCD795BE870516656'u, lo: 0x67902E276C921F8C'u), + uint64x2(hi: 0x806BD9714632DFF6'u, lo: 0x00BA1CD8A3DB53B7'u), + uint64x2(hi: 0xA086CFCD97BF97F3'u, lo: 0x80E8A40ECCD228A5'u), + uint64x2(hi: 0xC8A883C0FDAF7DF0'u, lo: 0x6122CD128006B2CE'u), + uint64x2(hi: 0xFAD2A4B13D1B5D6C'u, lo: 0x796B805720085F82'u), + uint64x2(hi: 0x9CC3A6EEC6311A63'u, lo: 0xCBE3303674053BB1'u), + uint64x2(hi: 0xC3F490AA77BD60FC'u, lo: 0xBEDBFC4411068A9D'u), + uint64x2(hi: 0xF4F1B4D515ACB93B'u, lo: 0xEE92FB5515482D45'u), + uint64x2(hi: 0x991711052D8BF3C5'u, lo: 0x751BDD152D4D1C4B'u), + uint64x2(hi: 0xBF5CD54678EEF0B6'u, lo: 0xD262D45A78A0635E'u), + uint64x2(hi: 0xEF340A98172AACE4'u, lo: 0x86FB897116C87C35'u), + uint64x2(hi: 0x9580869F0E7AAC0E'u, lo: 0xD45D35E6AE3D4DA1'u), + uint64x2(hi: 0xBAE0A846D2195712'u, lo: 0x8974836059CCA10A'u), + uint64x2(hi: 0xE998D258869FACD7'u, lo: 0x2BD1A438703FC94C'u), + uint64x2(hi: 0x91FF83775423CC06'u, lo: 0x7B6306A34627DDD0'u), + uint64x2(hi: 0xB67F6455292CBF08'u, lo: 0x1A3BC84C17B1D543'u), + uint64x2(hi: 0xE41F3D6A7377EECA'u, lo: 0x20CABA5F1D9E4A94'u), + uint64x2(hi: 0x8E938662882AF53E'u, lo: 0x547EB47B7282EE9D'u), + uint64x2(hi: 0xB23867FB2A35B28D'u, lo: 0xE99E619A4F23AA44'u), + uint64x2(hi: 0xDEC681F9F4C31F31'u, lo: 0x6405FA00E2EC94D5'u), + uint64x2(hi: 0x8B3C113C38F9F37E'u, lo: 0xDE83BC408DD3DD05'u), + uint64x2(hi: 0xAE0B158B4738705E'u, lo: 0x9624AB50B148D446'u), + uint64x2(hi: 0xD98DDAEE19068C76'u, lo: 0x3BADD624DD9B0958'u), + uint64x2(hi: 0x87F8A8D4CFA417C9'u, lo: 0xE54CA5D70A80E5D7'u), + uint64x2(hi: 0xA9F6D30A038D1DBC'u, lo: 0x5E9FCF4CCD211F4D'u), + uint64x2(hi: 0xD47487CC8470652B'u, lo: 0x7647C32000696720'u), + uint64x2(hi: 0x84C8D4DFD2C63F3B'u, lo: 0x29ECD9F40041E074'u), + uint64x2(hi: 0xA5FB0A17C777CF09'u, lo: 0xF468107100525891'u), + uint64x2(hi: 0xCF79CC9DB955C2CC'u, lo: 0x7182148D4066EEB5'u), + uint64x2(hi: 0x81AC1FE293D599BF'u, lo: 0xC6F14CD848405531'u), + uint64x2(hi: 0xA21727DB38CB002F'u, lo: 0xB8ADA00E5A506A7D'u), + uint64x2(hi: 0xCA9CF1D206FDC03B'u, lo: 0xA6D90811F0E4851D'u), + uint64x2(hi: 0xFD442E4688BD304A'u, lo: 0x908F4A166D1DA664'u), + uint64x2(hi: 0x9E4A9CEC15763E2E'u, lo: 0x9A598E4E043287FF'u), + uint64x2(hi: 0xC5DD44271AD3CDBA'u, lo: 0x40EFF1E1853F29FE'u), + uint64x2(hi: 0xF7549530E188C128'u, lo: 0xD12BEE59E68EF47D'u), + uint64x2(hi: 0x9A94DD3E8CF578B9'u, lo: 0x82BB74F8301958CF'u), + uint64x2(hi: 0xC13A148E3032D6E7'u, lo: 0xE36A52363C1FAF02'u), + uint64x2(hi: 0xF18899B1BC3F8CA1'u, lo: 0xDC44E6C3CB279AC2'u), + uint64x2(hi: 0x96F5600F15A7B7E5'u, lo: 0x29AB103A5EF8C0BA'u), + uint64x2(hi: 0xBCB2B812DB11A5DE'u, lo: 0x7415D448F6B6F0E8'u), + uint64x2(hi: 0xEBDF661791D60F56'u, lo: 0x111B495B3464AD22'u), + uint64x2(hi: 0x936B9FCEBB25C995'u, lo: 0xCAB10DD900BEEC35'u), + uint64x2(hi: 0xB84687C269EF3BFB'u, lo: 0x3D5D514F40EEA743'u), + uint64x2(hi: 0xE65829B3046B0AFA'u, lo: 0x0CB4A5A3112A5113'u), + uint64x2(hi: 0x8FF71A0FE2C2E6DC'u, lo: 0x47F0E785EABA72AC'u), + uint64x2(hi: 0xB3F4E093DB73A093'u, lo: 0x59ED216765690F57'u), + uint64x2(hi: 0xE0F218B8D25088B8'u, lo: 0x306869C13EC3532D'u), + uint64x2(hi: 0x8C974F7383725573'u, lo: 0x1E414218C73A13FC'u), + uint64x2(hi: 0xAFBD2350644EEACF'u, lo: 0xE5D1929EF90898FB'u), + uint64x2(hi: 0xDBAC6C247D62A583'u, lo: 0xDF45F746B74ABF3A'u), + uint64x2(hi: 0x894BC396CE5DA772'u, lo: 0x6B8BBA8C328EB784'u), + uint64x2(hi: 0xAB9EB47C81F5114F'u, lo: 0x066EA92F3F326565'u), + uint64x2(hi: 0xD686619BA27255A2'u, lo: 0xC80A537B0EFEFEBE'u), + uint64x2(hi: 0x8613FD0145877585'u, lo: 0xBD06742CE95F5F37'u), + uint64x2(hi: 0xA798FC4196E952E7'u, lo: 0x2C48113823B73705'u), + uint64x2(hi: 0xD17F3B51FCA3A7A0'u, lo: 0xF75A15862CA504C6'u), + uint64x2(hi: 0x82EF85133DE648C4'u, lo: 0x9A984D73DBE722FC'u), + uint64x2(hi: 0xA3AB66580D5FDAF5'u, lo: 0xC13E60D0D2E0EBBB'u), + uint64x2(hi: 0xCC963FEE10B7D1B3'u, lo: 0x318DF905079926A9'u), + uint64x2(hi: 0xFFBBCFE994E5C61F'u, lo: 0xFDF17746497F7053'u), + uint64x2(hi: 0x9FD561F1FD0F9BD3'u, lo: 0xFEB6EA8BEDEFA634'u), + uint64x2(hi: 0xC7CABA6E7C5382C8'u, lo: 0xFE64A52EE96B8FC1'u), + uint64x2(hi: 0xF9BD690A1B68637B'u, lo: 0x3DFDCE7AA3C673B1'u), + uint64x2(hi: 0x9C1661A651213E2D'u, lo: 0x06BEA10CA65C084F'u), + uint64x2(hi: 0xC31BFA0FE5698DB8'u, lo: 0x486E494FCFF30A63'u), + uint64x2(hi: 0xF3E2F893DEC3F126'u, lo: 0x5A89DBA3C3EFCCFB'u), + uint64x2(hi: 0x986DDB5C6B3A76B7'u, lo: 0xF89629465A75E01D'u), + uint64x2(hi: 0xBE89523386091465'u, lo: 0xF6BBB397F1135824'u), + uint64x2(hi: 0xEE2BA6C0678B597F'u, lo: 0x746AA07DED582E2D'u), + uint64x2(hi: 0x94DB483840B717EF'u, lo: 0xA8C2A44EB4571CDD'u), + uint64x2(hi: 0xBA121A4650E4DDEB'u, lo: 0x92F34D62616CE414'u), + uint64x2(hi: 0xE896A0D7E51E1566'u, lo: 0x77B020BAF9C81D18'u), + uint64x2(hi: 0x915E2486EF32CD60'u, lo: 0x0ACE1474DC1D122F'u), + uint64x2(hi: 0xB5B5ADA8AAFF80B8'u, lo: 0x0D819992132456BB'u), + uint64x2(hi: 0xE3231912D5BF60E6'u, lo: 0x10E1FFF697ED6C6A'u), + uint64x2(hi: 0x8DF5EFABC5979C8F'u, lo: 0xCA8D3FFA1EF463C2'u), + uint64x2(hi: 0xB1736B96B6FD83B3'u, lo: 0xBD308FF8A6B17CB3'u), + uint64x2(hi: 0xDDD0467C64BCE4A0'u, lo: 0xAC7CB3F6D05DDBDF'u), + uint64x2(hi: 0x8AA22C0DBEF60EE4'u, lo: 0x6BCDF07A423AA96C'u), + uint64x2(hi: 0xAD4AB7112EB3929D'u, lo: 0x86C16C98D2C953C7'u), + uint64x2(hi: 0xD89D64D57A607744'u, lo: 0xE871C7BF077BA8B8'u), + uint64x2(hi: 0x87625F056C7C4A8B'u, lo: 0x11471CD764AD4973'u), + uint64x2(hi: 0xA93AF6C6C79B5D2D'u, lo: 0xD598E40D3DD89BD0'u), + uint64x2(hi: 0xD389B47879823479'u, lo: 0x4AFF1D108D4EC2C4'u), + uint64x2(hi: 0x843610CB4BF160CB'u, lo: 0xCEDF722A585139BB'u), + uint64x2(hi: 0xA54394FE1EEDB8FE'u, lo: 0xC2974EB4EE658829'u), + uint64x2(hi: 0xCE947A3DA6A9273E'u, lo: 0x733D226229FEEA33'u), + uint64x2(hi: 0x811CCC668829B887'u, lo: 0x0806357D5A3F5260'u), + uint64x2(hi: 0xA163FF802A3426A8'u, lo: 0xCA07C2DCB0CF26F8'u), + uint64x2(hi: 0xC9BCFF6034C13052'u, lo: 0xFC89B393DD02F0B6'u), + uint64x2(hi: 0xFC2C3F3841F17C67'u, lo: 0xBBAC2078D443ACE3'u), + uint64x2(hi: 0x9D9BA7832936EDC0'u, lo: 0xD54B944B84AA4C0E'u), + uint64x2(hi: 0xC5029163F384A931'u, lo: 0x0A9E795E65D4DF12'u), + uint64x2(hi: 0xF64335BCF065D37D'u, lo: 0x4D4617B5FF4A16D6'u), + uint64x2(hi: 0x99EA0196163FA42E'u, lo: 0x504BCED1BF8E4E46'u), + uint64x2(hi: 0xC06481FB9BCF8D39'u, lo: 0xE45EC2862F71E1D7'u), + uint64x2(hi: 0xF07DA27A82C37088'u, lo: 0x5D767327BB4E5A4D'u), + uint64x2(hi: 0x964E858C91BA2655'u, lo: 0x3A6A07F8D510F870'u), + uint64x2(hi: 0xBBE226EFB628AFEA'u, lo: 0x890489F70A55368C'u), + uint64x2(hi: 0xEADAB0ABA3B2DBE5'u, lo: 0x2B45AC74CCEA842F'u), + uint64x2(hi: 0x92C8AE6B464FC96F'u, lo: 0x3B0B8BC90012929E'u), + uint64x2(hi: 0xB77ADA0617E3BBCB'u, lo: 0x09CE6EBB40173745'u), + uint64x2(hi: 0xE55990879DDCAABD'u, lo: 0xCC420A6A101D0516'u), + uint64x2(hi: 0x8F57FA54C2A9EAB6'u, lo: 0x9FA946824A12232E'u), + uint64x2(hi: 0xB32DF8E9F3546564'u, lo: 0x47939822DC96ABFA'u), + uint64x2(hi: 0xDFF9772470297EBD'u, lo: 0x59787E2B93BC56F8'u), + uint64x2(hi: 0x8BFBEA76C619EF36'u, lo: 0x57EB4EDB3C55B65B'u), + uint64x2(hi: 0xAEFAE51477A06B03'u, lo: 0xEDE622920B6B23F2'u), + uint64x2(hi: 0xDAB99E59958885C4'u, lo: 0xE95FAB368E45ECEE'u), + uint64x2(hi: 0x88B402F7FD75539B'u, lo: 0x11DBCB0218EBB415'u), + uint64x2(hi: 0xAAE103B5FCD2A881'u, lo: 0xD652BDC29F26A11A'u), + uint64x2(hi: 0xD59944A37C0752A2'u, lo: 0x4BE76D3346F04960'u), + uint64x2(hi: 0x857FCAE62D8493A5'u, lo: 0x6F70A4400C562DDC'u), + uint64x2(hi: 0xA6DFBD9FB8E5B88E'u, lo: 0xCB4CCD500F6BB953'u), + uint64x2(hi: 0xD097AD07A71F26B2'u, lo: 0x7E2000A41346A7A8'u), + uint64x2(hi: 0x825ECC24C873782F'u, lo: 0x8ED400668C0C28C9'u), + uint64x2(hi: 0xA2F67F2DFA90563B'u, lo: 0x728900802F0F32FB'u), + uint64x2(hi: 0xCBB41EF979346BCA'u, lo: 0x4F2B40A03AD2FFBA'u), + uint64x2(hi: 0xFEA126B7D78186BC'u, lo: 0xE2F610C84987BFA9'u), + uint64x2(hi: 0x9F24B832E6B0F436'u, lo: 0x0DD9CA7D2DF4D7CA'u), + uint64x2(hi: 0xC6EDE63FA05D3143'u, lo: 0x91503D1C79720DBC'u), + uint64x2(hi: 0xF8A95FCF88747D94'u, lo: 0x75A44C6397CE912B'u), + uint64x2(hi: 0x9B69DBE1B548CE7C'u, lo: 0xC986AFBE3EE11ABB'u), + uint64x2(hi: 0xC24452DA229B021B'u, lo: 0xFBE85BADCE996169'u), + uint64x2(hi: 0xF2D56790AB41C2A2'u, lo: 0xFAE27299423FB9C4'u), + uint64x2(hi: 0x97C560BA6B0919A5'u, lo: 0xDCCD879FC967D41B'u), + uint64x2(hi: 0xBDB6B8E905CB600F'u, lo: 0x5400E987BBC1C921'u), + uint64x2(hi: 0xED246723473E3813'u, lo: 0x290123E9AAB23B69'u), + uint64x2(hi: 0x9436C0760C86E30B'u, lo: 0xF9A0B6720AAF6522'u), + uint64x2(hi: 0xB94470938FA89BCE'u, lo: 0xF808E40E8D5B3E6A'u), + uint64x2(hi: 0xE7958CB87392C2C2'u, lo: 0xB60B1D1230B20E05'u), + uint64x2(hi: 0x90BD77F3483BB9B9'u, lo: 0xB1C6F22B5E6F48C3'u), + uint64x2(hi: 0xB4ECD5F01A4AA828'u, lo: 0x1E38AEB6360B1AF4'u), + uint64x2(hi: 0xE2280B6C20DD5232'u, lo: 0x25C6DA63C38DE1B1'u), + uint64x2(hi: 0x8D590723948A535F'u, lo: 0x579C487E5A38AD0F'u), + uint64x2(hi: 0xB0AF48EC79ACE837'u, lo: 0x2D835A9DF0C6D852'u), + uint64x2(hi: 0xDCDB1B2798182244'u, lo: 0xF8E431456CF88E66'u), + uint64x2(hi: 0x8A08F0F8BF0F156B'u, lo: 0x1B8E9ECB641B5900'u), + uint64x2(hi: 0xAC8B2D36EED2DAC5'u, lo: 0xE272467E3D222F40'u), + uint64x2(hi: 0xD7ADF884AA879177'u, lo: 0x5B0ED81DCC6ABB10'u), + uint64x2(hi: 0x86CCBB52EA94BAEA'u, lo: 0x98E947129FC2B4EA'u), + uint64x2(hi: 0xA87FEA27A539E9A5'u, lo: 0x3F2398D747B36225'u), + uint64x2(hi: 0xD29FE4B18E88640E'u, lo: 0x8EEC7F0D19A03AAE'u), + uint64x2(hi: 0x83A3EEEEF9153E89'u, lo: 0x1953CF68300424AD'u), + uint64x2(hi: 0xA48CEAAAB75A8E2B'u, lo: 0x5FA8C3423C052DD8'u), + uint64x2(hi: 0xCDB02555653131B6'u, lo: 0x3792F412CB06794E'u), + uint64x2(hi: 0x808E17555F3EBF11'u, lo: 0xE2BBD88BBEE40BD1'u), + uint64x2(hi: 0xA0B19D2AB70E6ED6'u, lo: 0x5B6ACEAEAE9D0EC5'u), + uint64x2(hi: 0xC8DE047564D20A8B'u, lo: 0xF245825A5A445276'u), + uint64x2(hi: 0xFB158592BE068D2E'u, lo: 0xEED6E2F0F0D56713'u), + uint64x2(hi: 0x9CED737BB6C4183D'u, lo: 0x55464DD69685606C'u), + uint64x2(hi: 0xC428D05AA4751E4C'u, lo: 0xAA97E14C3C26B887'u), + uint64x2(hi: 0xF53304714D9265DF'u, lo: 0xD53DD99F4B3066A9'u), + uint64x2(hi: 0x993FE2C6D07B7FAB'u, lo: 0xE546A8038EFE402A'u), + uint64x2(hi: 0xBF8FDB78849A5F96'u, lo: 0xDE98520472BDD034'u), + uint64x2(hi: 0xEF73D256A5C0F77C'u, lo: 0x963E66858F6D4441'u), + uint64x2(hi: 0x95A8637627989AAD'u, lo: 0xDDE7001379A44AA9'u), + uint64x2(hi: 0xBB127C53B17EC159'u, lo: 0x5560C018580D5D53'u), + uint64x2(hi: 0xE9D71B689DDE71AF'u, lo: 0xAAB8F01E6E10B4A7'u), + uint64x2(hi: 0x9226712162AB070D'u, lo: 0xCAB3961304CA70E9'u), + uint64x2(hi: 0xB6B00D69BB55C8D1'u, lo: 0x3D607B97C5FD0D23'u), + uint64x2(hi: 0xE45C10C42A2B3B05'u, lo: 0x8CB89A7DB77C506B'u), + uint64x2(hi: 0x8EB98A7A9A5B04E3'u, lo: 0x77F3608E92ADB243'u), + uint64x2(hi: 0xB267ED1940F1C61C'u, lo: 0x55F038B237591ED4'u), + uint64x2(hi: 0xDF01E85F912E37A3'u, lo: 0x6B6C46DEC52F6689'u), + uint64x2(hi: 0x8B61313BBABCE2C6'u, lo: 0x2323AC4B3B3DA016'u), + uint64x2(hi: 0xAE397D8AA96C1B77'u, lo: 0xABEC975E0A0D081B'u), + uint64x2(hi: 0xD9C7DCED53C72255'u, lo: 0x96E7BD358C904A22'u), + uint64x2(hi: 0x881CEA14545C7575'u, lo: 0x7E50D64177DA2E55'u), + uint64x2(hi: 0xAA242499697392D2'u, lo: 0xDDE50BD1D5D0B9EA'u), + uint64x2(hi: 0xD4AD2DBFC3D07787'u, lo: 0x955E4EC64B44E865'u), + uint64x2(hi: 0x84EC3C97DA624AB4'u, lo: 0xBD5AF13BEF0B113F'u), + uint64x2(hi: 0xA6274BBDD0FADD61'u, lo: 0xECB1AD8AEACDD58F'u), + uint64x2(hi: 0xCFB11EAD453994BA'u, lo: 0x67DE18EDA5814AF3'u), + uint64x2(hi: 0x81CEB32C4B43FCF4'u, lo: 0x80EACF948770CED8'u), + uint64x2(hi: 0xA2425FF75E14FC31'u, lo: 0xA1258379A94D028E'u), + uint64x2(hi: 0xCAD2F7F5359A3B3E'u, lo: 0x096EE45813A04331'u), + uint64x2(hi: 0xFD87B5F28300CA0D'u, lo: 0x8BCA9D6E188853FD'u), + uint64x2(hi: 0x9E74D1B791E07E48'u, lo: 0x775EA264CF55347E'u), + uint64x2(hi: 0xC612062576589DDA'u, lo: 0x95364AFE032A819E'u), + uint64x2(hi: 0xF79687AED3EEC551'u, lo: 0x3A83DDBD83F52205'u), + uint64x2(hi: 0x9ABE14CD44753B52'u, lo: 0xC4926A9672793543'u), + uint64x2(hi: 0xC16D9A0095928A27'u, lo: 0x75B7053C0F178294'u), + uint64x2(hi: 0xF1C90080BAF72CB1'u, lo: 0x5324C68B12DD6339'u), + uint64x2(hi: 0x971DA05074DA7BEE'u, lo: 0xD3F6FC16EBCA5E04'u), + uint64x2(hi: 0xBCE5086492111AEA'u, lo: 0x88F4BB1CA6BCF585'u), + uint64x2(hi: 0xEC1E4A7DB69561A5'u, lo: 0x2B31E9E3D06C32E6'u), + uint64x2(hi: 0x9392EE8E921D5D07'u, lo: 0x3AFF322E62439FD0'u), + uint64x2(hi: 0xB877AA3236A4B449'u, lo: 0x09BEFEB9FAD487C3'u), + uint64x2(hi: 0xE69594BEC44DE15B'u, lo: 0x4C2EBE687989A9B4'u), + uint64x2(hi: 0x901D7CF73AB0ACD9'u, lo: 0x0F9D37014BF60A11'u), + uint64x2(hi: 0xB424DC35095CD80F'u, lo: 0x538484C19EF38C95'u), + uint64x2(hi: 0xE12E13424BB40E13'u, lo: 0x2865A5F206B06FBA'u), + uint64x2(hi: 0x8CBCCC096F5088CB'u, lo: 0xF93F87B7442E45D4'u), + uint64x2(hi: 0xAFEBFF0BCB24AAFE'u, lo: 0xF78F69A51539D749'u), + uint64x2(hi: 0xDBE6FECEBDEDD5BE'u, lo: 0xB573440E5A884D1C'u), + uint64x2(hi: 0x89705F4136B4A597'u, lo: 0x31680A88F8953031'u), + uint64x2(hi: 0xABCC77118461CEFC'u, lo: 0xFDC20D2B36BA7C3E'u), + uint64x2(hi: 0xD6BF94D5E57A42BC'u, lo: 0x3D32907604691B4D'u), + uint64x2(hi: 0x8637BD05AF6C69B5'u, lo: 0xA63F9A49C2C1B110'u), + uint64x2(hi: 0xA7C5AC471B478423'u, lo: 0x0FCF80DC33721D54'u), + uint64x2(hi: 0xD1B71758E219652B'u, lo: 0xD3C36113404EA4A9'u), + uint64x2(hi: 0x83126E978D4FDF3B'u, lo: 0x645A1CAC083126EA'u), + uint64x2(hi: 0xA3D70A3D70A3D70A'u, lo: 0x3D70A3D70A3D70A4'u), + uint64x2(hi: 0xCCCCCCCCCCCCCCCC'u, lo: 0xCCCCCCCCCCCCCCCD'u), + uint64x2(hi: 0x8000000000000000'u, lo: 0x0000000000000000'u), + uint64x2(hi: 0xA000000000000000'u, lo: 0x0000000000000000'u), + uint64x2(hi: 0xC800000000000000'u, lo: 0x0000000000000000'u), + uint64x2(hi: 0xFA00000000000000'u, lo: 0x0000000000000000'u), + uint64x2(hi: 0x9C40000000000000'u, lo: 0x0000000000000000'u), + uint64x2(hi: 0xC350000000000000'u, lo: 0x0000000000000000'u), + uint64x2(hi: 0xF424000000000000'u, lo: 0x0000000000000000'u), + uint64x2(hi: 0x9896800000000000'u, lo: 0x0000000000000000'u), + uint64x2(hi: 0xBEBC200000000000'u, lo: 0x0000000000000000'u), + uint64x2(hi: 0xEE6B280000000000'u, lo: 0x0000000000000000'u), + uint64x2(hi: 0x9502F90000000000'u, lo: 0x0000000000000000'u), + uint64x2(hi: 0xBA43B74000000000'u, lo: 0x0000000000000000'u), + uint64x2(hi: 0xE8D4A51000000000'u, lo: 0x0000000000000000'u), + uint64x2(hi: 0x9184E72A00000000'u, lo: 0x0000000000000000'u), + uint64x2(hi: 0xB5E620F480000000'u, lo: 0x0000000000000000'u), + uint64x2(hi: 0xE35FA931A0000000'u, lo: 0x0000000000000000'u), + uint64x2(hi: 0x8E1BC9BF04000000'u, lo: 0x0000000000000000'u), + uint64x2(hi: 0xB1A2BC2EC5000000'u, lo: 0x0000000000000000'u), + uint64x2(hi: 0xDE0B6B3A76400000'u, lo: 0x0000000000000000'u), + uint64x2(hi: 0x8AC7230489E80000'u, lo: 0x0000000000000000'u), + uint64x2(hi: 0xAD78EBC5AC620000'u, lo: 0x0000000000000000'u), + uint64x2(hi: 0xD8D726B7177A8000'u, lo: 0x0000000000000000'u), + uint64x2(hi: 0x878678326EAC9000'u, lo: 0x0000000000000000'u), + uint64x2(hi: 0xA968163F0A57B400'u, lo: 0x0000000000000000'u), + uint64x2(hi: 0xD3C21BCECCEDA100'u, lo: 0x0000000000000000'u), + uint64x2(hi: 0x84595161401484A0'u, lo: 0x0000000000000000'u), + uint64x2(hi: 0xA56FA5B99019A5C8'u, lo: 0x0000000000000000'u), + uint64x2(hi: 0xCECB8F27F4200F3A'u, lo: 0x0000000000000000'u), + uint64x2(hi: 0x813F3978F8940984'u, lo: 0x4000000000000000'u), + uint64x2(hi: 0xA18F07D736B90BE5'u, lo: 0x5000000000000000'u), + uint64x2(hi: 0xC9F2C9CD04674EDE'u, lo: 0xA400000000000000'u), + uint64x2(hi: 0xFC6F7C4045812296'u, lo: 0x4D00000000000000'u), + uint64x2(hi: 0x9DC5ADA82B70B59D'u, lo: 0xF020000000000000'u), + uint64x2(hi: 0xC5371912364CE305'u, lo: 0x6C28000000000000'u), + uint64x2(hi: 0xF684DF56C3E01BC6'u, lo: 0xC732000000000000'u), + uint64x2(hi: 0x9A130B963A6C115C'u, lo: 0x3C7F400000000000'u), + uint64x2(hi: 0xC097CE7BC90715B3'u, lo: 0x4B9F100000000000'u), + uint64x2(hi: 0xF0BDC21ABB48DB20'u, lo: 0x1E86D40000000000'u), + uint64x2(hi: 0x96769950B50D88F4'u, lo: 0x1314448000000000'u), + uint64x2(hi: 0xBC143FA4E250EB31'u, lo: 0x17D955A000000000'u), + uint64x2(hi: 0xEB194F8E1AE525FD'u, lo: 0x5DCFAB0800000000'u), + uint64x2(hi: 0x92EFD1B8D0CF37BE'u, lo: 0x5AA1CAE500000000'u), + uint64x2(hi: 0xB7ABC627050305AD'u, lo: 0xF14A3D9E40000000'u), + uint64x2(hi: 0xE596B7B0C643C719'u, lo: 0x6D9CCD05D0000000'u), + uint64x2(hi: 0x8F7E32CE7BEA5C6F'u, lo: 0xE4820023A2000000'u), + uint64x2(hi: 0xB35DBF821AE4F38B'u, lo: 0xDDA2802C8A800000'u), + uint64x2(hi: 0xE0352F62A19E306E'u, lo: 0xD50B2037AD200000'u), + uint64x2(hi: 0x8C213D9DA502DE45'u, lo: 0x4526F422CC340000'u), + uint64x2(hi: 0xAF298D050E4395D6'u, lo: 0x9670B12B7F410000'u), + uint64x2(hi: 0xDAF3F04651D47B4C'u, lo: 0x3C0CDD765F114000'u), + uint64x2(hi: 0x88D8762BF324CD0F'u, lo: 0xA5880A69FB6AC800'u), + uint64x2(hi: 0xAB0E93B6EFEE0053'u, lo: 0x8EEA0D047A457A00'u), + uint64x2(hi: 0xD5D238A4ABE98068'u, lo: 0x72A4904598D6D880'u), + uint64x2(hi: 0x85A36366EB71F041'u, lo: 0x47A6DA2B7F864750'u), + uint64x2(hi: 0xA70C3C40A64E6C51'u, lo: 0x999090B65F67D924'u), + uint64x2(hi: 0xD0CF4B50CFE20765'u, lo: 0xFFF4B4E3F741CF6D'u), + uint64x2(hi: 0x82818F1281ED449F'u, lo: 0xBFF8F10E7A8921A4'u), + uint64x2(hi: 0xA321F2D7226895C7'u, lo: 0xAFF72D52192B6A0D'u), + uint64x2(hi: 0xCBEA6F8CEB02BB39'u, lo: 0x9BF4F8A69F764490'u), + uint64x2(hi: 0xFEE50B7025C36A08'u, lo: 0x02F236D04753D5B4'u), + uint64x2(hi: 0x9F4F2726179A2245'u, lo: 0x01D762422C946590'u), + uint64x2(hi: 0xC722F0EF9D80AAD6'u, lo: 0x424D3AD2B7B97EF5'u), + uint64x2(hi: 0xF8EBAD2B84E0D58B'u, lo: 0xD2E0898765A7DEB2'u), + uint64x2(hi: 0x9B934C3B330C8577'u, lo: 0x63CC55F49F88EB2F'u), + uint64x2(hi: 0xC2781F49FFCFA6D5'u, lo: 0x3CBF6B71C76B25FB'u), + uint64x2(hi: 0xF316271C7FC3908A'u, lo: 0x8BEF464E3945EF7A'u), + uint64x2(hi: 0x97EDD871CFDA3A56'u, lo: 0x97758BF0E3CBB5AC'u), + uint64x2(hi: 0xBDE94E8E43D0C8EC'u, lo: 0x3D52EEED1CBEA317'u), + uint64x2(hi: 0xED63A231D4C4FB27'u, lo: 0x4CA7AAA863EE4BDD'u), + uint64x2(hi: 0x945E455F24FB1CF8'u, lo: 0x8FE8CAA93E74EF6A'u), + uint64x2(hi: 0xB975D6B6EE39E436'u, lo: 0xB3E2FD538E122B44'u), + uint64x2(hi: 0xE7D34C64A9C85D44'u, lo: 0x60DBBCA87196B616'u), + uint64x2(hi: 0x90E40FBEEA1D3A4A'u, lo: 0xBC8955E946FE31CD'u), + uint64x2(hi: 0xB51D13AEA4A488DD'u, lo: 0x6BABAB6398BDBE41'u), + uint64x2(hi: 0xE264589A4DCDAB14'u, lo: 0xC696963C7EED2DD1'u), + uint64x2(hi: 0x8D7EB76070A08AEC'u, lo: 0xFC1E1DE5CF543CA2'u), + uint64x2(hi: 0xB0DE65388CC8ADA8'u, lo: 0x3B25A55F43294BCB'u), + uint64x2(hi: 0xDD15FE86AFFAD912'u, lo: 0x49EF0EB713F39EBE'u), + uint64x2(hi: 0x8A2DBF142DFCC7AB'u, lo: 0x6E3569326C784337'u), + uint64x2(hi: 0xACB92ED9397BF996'u, lo: 0x49C2C37F07965404'u), + uint64x2(hi: 0xD7E77A8F87DAF7FB'u, lo: 0xDC33745EC97BE906'u), + uint64x2(hi: 0x86F0AC99B4E8DAFD'u, lo: 0x69A028BB3DED71A3'u), + uint64x2(hi: 0xA8ACD7C0222311BC'u, lo: 0xC40832EA0D68CE0C'u), + uint64x2(hi: 0xD2D80DB02AABD62B'u, lo: 0xF50A3FA490C30190'u), + uint64x2(hi: 0x83C7088E1AAB65DB'u, lo: 0x792667C6DA79E0FA'u), + uint64x2(hi: 0xA4B8CAB1A1563F52'u, lo: 0x577001B891185938'u), + uint64x2(hi: 0xCDE6FD5E09ABCF26'u, lo: 0xED4C0226B55E6F86'u), + uint64x2(hi: 0x80B05E5AC60B6178'u, lo: 0x544F8158315B05B4'u), + uint64x2(hi: 0xA0DC75F1778E39D6'u, lo: 0x696361AE3DB1C721'u), + uint64x2(hi: 0xC913936DD571C84C'u, lo: 0x03BC3A19CD1E38E9'u), + uint64x2(hi: 0xFB5878494ACE3A5F'u, lo: 0x04AB48A04065C723'u), + uint64x2(hi: 0x9D174B2DCEC0E47B'u, lo: 0x62EB0D64283F9C76'u), + uint64x2(hi: 0xC45D1DF942711D9A'u, lo: 0x3BA5D0BD324F8394'u), + uint64x2(hi: 0xF5746577930D6500'u, lo: 0xCA8F44EC7EE36479'u), + uint64x2(hi: 0x9968BF6ABBE85F20'u, lo: 0x7E998B13CF4E1ECB'u), + uint64x2(hi: 0xBFC2EF456AE276E8'u, lo: 0x9E3FEDD8C321A67E'u), + uint64x2(hi: 0xEFB3AB16C59B14A2'u, lo: 0xC5CFE94EF3EA101E'u), + uint64x2(hi: 0x95D04AEE3B80ECE5'u, lo: 0xBBA1F1D158724A12'u), + uint64x2(hi: 0xBB445DA9CA61281F'u, lo: 0x2A8A6E45AE8EDC97'u), + uint64x2(hi: 0xEA1575143CF97226'u, lo: 0xF52D09D71A3293BD'u), + uint64x2(hi: 0x924D692CA61BE758'u, lo: 0x593C2626705F9C56'u), + uint64x2(hi: 0xB6E0C377CFA2E12E'u, lo: 0x6F8B2FB00C77836C'u), + uint64x2(hi: 0xE498F455C38B997A'u, lo: 0x0B6DFB9C0F956447'u), + uint64x2(hi: 0x8EDF98B59A373FEC'u, lo: 0x4724BD4189BD5EAC'u), + uint64x2(hi: 0xB2977EE300C50FE7'u, lo: 0x58EDEC91EC2CB657'u), + uint64x2(hi: 0xDF3D5E9BC0F653E1'u, lo: 0x2F2967B66737E3ED'u), + uint64x2(hi: 0x8B865B215899F46C'u, lo: 0xBD79E0D20082EE74'u), + uint64x2(hi: 0xAE67F1E9AEC07187'u, lo: 0xECD8590680A3AA11'u), + uint64x2(hi: 0xDA01EE641A708DE9'u, lo: 0xE80E6F4820CC9495'u), + uint64x2(hi: 0x884134FE908658B2'u, lo: 0x3109058D147FDCDD'u), + uint64x2(hi: 0xAA51823E34A7EEDE'u, lo: 0xBD4B46F0599FD415'u), + uint64x2(hi: 0xD4E5E2CDC1D1EA96'u, lo: 0x6C9E18AC7007C91A'u), + uint64x2(hi: 0x850FADC09923329E'u, lo: 0x03E2CF6BC604DDB0'u), + uint64x2(hi: 0xA6539930BF6BFF45'u, lo: 0x84DB8346B786151C'u), + uint64x2(hi: 0xCFE87F7CEF46FF16'u, lo: 0xE612641865679A63'u), + uint64x2(hi: 0x81F14FAE158C5F6E'u, lo: 0x4FCB7E8F3F60C07E'u), + uint64x2(hi: 0xA26DA3999AEF7749'u, lo: 0xE3BE5E330F38F09D'u), + uint64x2(hi: 0xCB090C8001AB551C'u, lo: 0x5CADF5BFD3072CC5'u), + uint64x2(hi: 0xFDCB4FA002162A63'u, lo: 0x73D9732FC7C8F7F6'u), + uint64x2(hi: 0x9E9F11C4014DDA7E'u, lo: 0x2867E7FDDCDD9AFA'u), + uint64x2(hi: 0xC646D63501A1511D'u, lo: 0xB281E1FD541501B8'u), + uint64x2(hi: 0xF7D88BC24209A565'u, lo: 0x1F225A7CA91A4226'u), + uint64x2(hi: 0x9AE757596946075F'u, lo: 0x3375788DE9B06958'u), + uint64x2(hi: 0xC1A12D2FC3978937'u, lo: 0x0052D6B1641C83AE'u), + uint64x2(hi: 0xF209787BB47D6B84'u, lo: 0xC0678C5DBD23A49A'u), + uint64x2(hi: 0x9745EB4D50CE6332'u, lo: 0xF840B7BA963646E0'u), + uint64x2(hi: 0xBD176620A501FBFF'u, lo: 0xB650E5A93BC3D898'u), + uint64x2(hi: 0xEC5D3FA8CE427AFF'u, lo: 0xA3E51F138AB4CEBE'u), + uint64x2(hi: 0x93BA47C980E98CDF'u, lo: 0xC66F336C36B10137'u), + uint64x2(hi: 0xB8A8D9BBE123F017'u, lo: 0xB80B0047445D4184'u), + uint64x2(hi: 0xE6D3102AD96CEC1D'u, lo: 0xA60DC059157491E5'u), + uint64x2(hi: 0x9043EA1AC7E41392'u, lo: 0x87C89837AD68DB2F'u), + uint64x2(hi: 0xB454E4A179DD1877'u, lo: 0x29BABE4598C311FB'u), + uint64x2(hi: 0xE16A1DC9D8545E94'u, lo: 0xF4296DD6FEF3D67A'u), + uint64x2(hi: 0x8CE2529E2734BB1D'u, lo: 0x1899E4A65F58660C'u), + uint64x2(hi: 0xB01AE745B101E9E4'u, lo: 0x5EC05DCFF72E7F8F'u), + uint64x2(hi: 0xDC21A1171D42645D'u, lo: 0x76707543F4FA1F73'u), + uint64x2(hi: 0x899504AE72497EBA'u, lo: 0x6A06494A791C53A8'u), + uint64x2(hi: 0xABFA45DA0EDBDE69'u, lo: 0x0487DB9D17636892'u), + uint64x2(hi: 0xD6F8D7509292D603'u, lo: 0x45A9D2845D3C42B6'u), + uint64x2(hi: 0x865B86925B9BC5C2'u, lo: 0x0B8A2392BA45A9B2'u), + uint64x2(hi: 0xA7F26836F282B732'u, lo: 0x8E6CAC7768D7141E'u), + uint64x2(hi: 0xD1EF0244AF2364FF'u, lo: 0x3207D795430CD926'u), + uint64x2(hi: 0x8335616AED761F1F'u, lo: 0x7F44E6BD49E807B8'u), + uint64x2(hi: 0xA402B9C5A8D3A6E7'u, lo: 0x5F16206C9C6209A6'u), + uint64x2(hi: 0xCD036837130890A1'u, lo: 0x36DBA887C37A8C0F'u), + uint64x2(hi: 0x802221226BE55A64'u, lo: 0xC2494954DA2C9789'u), + uint64x2(hi: 0xA02AA96B06DEB0FD'u, lo: 0xF2DB9BAA10B7BD6C'u), + uint64x2(hi: 0xC83553C5C8965D3D'u, lo: 0x6F92829494E5ACC7'u), + uint64x2(hi: 0xFA42A8B73ABBF48C'u, lo: 0xCB772339BA1F17F9'u), + uint64x2(hi: 0x9C69A97284B578D7'u, lo: 0xFF2A760414536EFB'u), + uint64x2(hi: 0xC38413CF25E2D70D'u, lo: 0xFEF5138519684ABA'u), + uint64x2(hi: 0xF46518C2EF5B8CD1'u, lo: 0x7EB258665FC25D69'u), + uint64x2(hi: 0x98BF2F79D5993802'u, lo: 0xEF2F773FFBD97A61'u), + uint64x2(hi: 0xBEEEFB584AFF8603'u, lo: 0xAAFB550FFACFD8FA'u), + uint64x2(hi: 0xEEAABA2E5DBF6784'u, lo: 0x95BA2A53F983CF38'u), + uint64x2(hi: 0x952AB45CFA97A0B2'u, lo: 0xDD945A747BF26183'u), + uint64x2(hi: 0xBA756174393D88DF'u, lo: 0x94F971119AEEF9E4'u), + uint64x2(hi: 0xE912B9D1478CEB17'u, lo: 0x7A37CD5601AAB85D'u), + uint64x2(hi: 0x91ABB422CCB812EE'u, lo: 0xAC62E055C10AB33A'u), + uint64x2(hi: 0xB616A12B7FE617AA'u, lo: 0x577B986B314D6009'u), + uint64x2(hi: 0xE39C49765FDF9D94'u, lo: 0xED5A7E85FDA0B80B'u), + uint64x2(hi: 0x8E41ADE9FBEBC27D'u, lo: 0x14588F13BE847307'u), + uint64x2(hi: 0xB1D219647AE6B31C'u, lo: 0x596EB2D8AE258FC8'u), + uint64x2(hi: 0xDE469FBD99A05FE3'u, lo: 0x6FCA5F8ED9AEF3BB'u), + uint64x2(hi: 0x8AEC23D680043BEE'u, lo: 0x25DE7BB9480D5854'u), + uint64x2(hi: 0xADA72CCC20054AE9'u, lo: 0xAF561AA79A10AE6A'u), + uint64x2(hi: 0xD910F7FF28069DA4'u, lo: 0x1B2BA1518094DA04'u), + uint64x2(hi: 0x87AA9AFF79042286'u, lo: 0x90FB44D2F05D0842'u), + uint64x2(hi: 0xA99541BF57452B28'u, lo: 0x353A1607AC744A53'u), + uint64x2(hi: 0xD3FA922F2D1675F2'u, lo: 0x42889B8997915CE8'u), + uint64x2(hi: 0x847C9B5D7C2E09B7'u, lo: 0x69956135FEBADA11'u), + uint64x2(hi: 0xA59BC234DB398C25'u, lo: 0x43FAB9837E699095'u), + uint64x2(hi: 0xCF02B2C21207EF2E'u, lo: 0x94F967E45E03F4BB'u), + uint64x2(hi: 0x8161AFB94B44F57D'u, lo: 0x1D1BE0EEBAC278F5'u), + uint64x2(hi: 0xA1BA1BA79E1632DC'u, lo: 0x6462D92A69731732'u), + uint64x2(hi: 0xCA28A291859BBF93'u, lo: 0x7D7B8F7503CFDCFE'u), + uint64x2(hi: 0xFCB2CB35E702AF78'u, lo: 0x5CDA735244C3D43E'u), + uint64x2(hi: 0x9DEFBF01B061ADAB'u, lo: 0x3A0888136AFA64A7'u), + uint64x2(hi: 0xC56BAEC21C7A1916'u, lo: 0x088AAA1845B8FDD0'u), + uint64x2(hi: 0xF6C69A72A3989F5B'u, lo: 0x8AAD549E57273D45'u), + uint64x2(hi: 0x9A3C2087A63F6399'u, lo: 0x36AC54E2F678864B'u), + uint64x2(hi: 0xC0CB28A98FCF3C7F'u, lo: 0x84576A1BB416A7DD'u), + uint64x2(hi: 0xF0FDF2D3F3C30B9F'u, lo: 0x656D44A2A11C51D5'u), + uint64x2(hi: 0x969EB7C47859E743'u, lo: 0x9F644AE5A4B1B325'u), + uint64x2(hi: 0xBC4665B596706114'u, lo: 0x873D5D9F0DDE1FEE'u), + uint64x2(hi: 0xEB57FF22FC0C7959'u, lo: 0xA90CB506D155A7EA'u), + uint64x2(hi: 0x9316FF75DD87CBD8'u, lo: 0x09A7F12442D588F2'u), + uint64x2(hi: 0xB7DCBF5354E9BECE'u, lo: 0x0C11ED6D538AEB2F'u), + uint64x2(hi: 0xE5D3EF282A242E81'u, lo: 0x8F1668C8A86DA5FA'u), + uint64x2(hi: 0x8FA475791A569D10'u, lo: 0xF96E017D694487BC'u), + uint64x2(hi: 0xB38D92D760EC4455'u, lo: 0x37C981DCC395A9AC'u), + uint64x2(hi: 0xE070F78D3927556A'u, lo: 0x85BBE253F47B1417'u), + uint64x2(hi: 0x8C469AB843B89562'u, lo: 0x93956D7478CCEC8E'u), + uint64x2(hi: 0xAF58416654A6BABB'u, lo: 0x387AC8D1970027B2'u), + uint64x2(hi: 0xDB2E51BFE9D0696A'u, lo: 0x06997B05FCC0319E'u), + uint64x2(hi: 0x88FCF317F22241E2'u, lo: 0x441FECE3BDF81F03'u), + uint64x2(hi: 0xAB3C2FDDEEAAD25A'u, lo: 0xD527E81CAD7626C3'u), + uint64x2(hi: 0xD60B3BD56A5586F1'u, lo: 0x8A71E223D8D3B074'u), + uint64x2(hi: 0x85C7056562757456'u, lo: 0xF6872D5667844E49'u), + uint64x2(hi: 0xA738C6BEBB12D16C'u, lo: 0xB428F8AC016561DB'u), + uint64x2(hi: 0xD106F86E69D785C7'u, lo: 0xE13336D701BEBA52'u), + uint64x2(hi: 0x82A45B450226B39C'u, lo: 0xECC0024661173473'u), + uint64x2(hi: 0xA34D721642B06084'u, lo: 0x27F002D7F95D0190'u), + uint64x2(hi: 0xCC20CE9BD35C78A5'u, lo: 0x31EC038DF7B441F4'u), + uint64x2(hi: 0xFF290242C83396CE'u, lo: 0x7E67047175A15271'u), + uint64x2(hi: 0x9F79A169BD203E41'u, lo: 0x0F0062C6E984D386'u), + uint64x2(hi: 0xC75809C42C684DD1'u, lo: 0x52C07B78A3E60868'u), + uint64x2(hi: 0xF92E0C3537826145'u, lo: 0xA7709A56CCDF8A82'u), + uint64x2(hi: 0x9BBCC7A142B17CCB'u, lo: 0x88A66076400BB691'u), + uint64x2(hi: 0xC2ABF989935DDBFE'u, lo: 0x6ACFF893D00EA435'u), + uint64x2(hi: 0xF356F7EBF83552FE'u, lo: 0x0583F6B8C4124D43'u), + uint64x2(hi: 0x98165AF37B2153DE'u, lo: 0xC3727A337A8B704A'u), + uint64x2(hi: 0xBE1BF1B059E9A8D6'u, lo: 0x744F18C0592E4C5C'u), + uint64x2(hi: 0xEDA2EE1C7064130C'u, lo: 0x1162DEF06F79DF73'u), + uint64x2(hi: 0x9485D4D1C63E8BE7'u, lo: 0x8ADDCB5645AC2BA8'u), + uint64x2(hi: 0xB9A74A0637CE2EE1'u, lo: 0x6D953E2BD7173692'u), + uint64x2(hi: 0xE8111C87C5C1BA99'u, lo: 0xC8FA8DB6CCDD0437'u), + uint64x2(hi: 0x910AB1D4DB9914A0'u, lo: 0x1D9C9892400A22A2'u), + uint64x2(hi: 0xB54D5E4A127F59C8'u, lo: 0x2503BEB6D00CAB4B'u), + uint64x2(hi: 0xE2A0B5DC971F303A'u, lo: 0x2E44AE64840FD61D'u), + uint64x2(hi: 0x8DA471A9DE737E24'u, lo: 0x5CEAECFED289E5D2'u), + uint64x2(hi: 0xB10D8E1456105DAD'u, lo: 0x7425A83E872C5F47'u), + uint64x2(hi: 0xDD50F1996B947518'u, lo: 0xD12F124E28F77719'u), + uint64x2(hi: 0x8A5296FFE33CC92F'u, lo: 0x82BD6B70D99AAA6F'u), + uint64x2(hi: 0xACE73CBFDC0BFB7B'u, lo: 0x636CC64D1001550B'u), + uint64x2(hi: 0xD8210BEFD30EFA5A'u, lo: 0x3C47F7E05401AA4E'u), + uint64x2(hi: 0x8714A775E3E95C78'u, lo: 0x65ACFAEC34810A71'u), + uint64x2(hi: 0xA8D9D1535CE3B396'u, lo: 0x7F1839A741A14D0D'u), + uint64x2(hi: 0xD31045A8341CA07C'u, lo: 0x1EDE48111209A050'u), + uint64x2(hi: 0x83EA2B892091E44D'u, lo: 0x934AED0AAB460432'u), + uint64x2(hi: 0xA4E4B66B68B65D60'u, lo: 0xF81DA84D5617853F'u), + uint64x2(hi: 0xCE1DE40642E3F4B9'u, lo: 0x36251260AB9D668E'u), + uint64x2(hi: 0x80D2AE83E9CE78F3'u, lo: 0xC1D72B7C6B426019'u), + uint64x2(hi: 0xA1075A24E4421730'u, lo: 0xB24CF65B8612F81F'u), + uint64x2(hi: 0xC94930AE1D529CFC'u, lo: 0xDEE033F26797B627'u), + uint64x2(hi: 0xFB9B7CD9A4A7443C'u, lo: 0x169840EF017DA3B1'u), + uint64x2(hi: 0x9D412E0806E88AA5'u, lo: 0x8E1F289560EE864E'u), + uint64x2(hi: 0xC491798A08A2AD4E'u, lo: 0xF1A6F2BAB92A27E2'u), + uint64x2(hi: 0xF5B5D7EC8ACB58A2'u, lo: 0xAE10AF696774B1DB'u), + uint64x2(hi: 0x9991A6F3D6BF1765'u, lo: 0xACCA6DA1E0A8EF29'u), + uint64x2(hi: 0xBFF610B0CC6EDD3F'u, lo: 0x17FD090A58D32AF3'u), + uint64x2(hi: 0xEFF394DCFF8A948E'u, lo: 0xDDFC4B4CEF07F5B0'u), + uint64x2(hi: 0x95F83D0A1FB69CD9'u, lo: 0x4ABDAF101564F98E'u), + uint64x2(hi: 0xBB764C4CA7A4440F'u, lo: 0x9D6D1AD41ABE37F1'u), + uint64x2(hi: 0xEA53DF5FD18D5513'u, lo: 0x84C86189216DC5ED'u), + uint64x2(hi: 0x92746B9BE2F8552C'u, lo: 0x32FD3CF5B4E49BB4'u), + uint64x2(hi: 0xB7118682DBB66A77'u, lo: 0x3FBC8C33221DC2A1'u), + uint64x2(hi: 0xE4D5E82392A40515'u, lo: 0x0FABAF3FEAA5334A'u), + uint64x2(hi: 0x8F05B1163BA6832D'u, lo: 0x29CB4D87F2A7400E'u), + uint64x2(hi: 0xB2C71D5BCA9023F8'u, lo: 0x743E20E9EF511012'u), + uint64x2(hi: 0xDF78E4B2BD342CF6'u, lo: 0x914DA9246B255416'u), + uint64x2(hi: 0x8BAB8EEFB6409C1A'u, lo: 0x1AD089B6C2F7548E'u), + uint64x2(hi: 0xAE9672ABA3D0C320'u, lo: 0xA184AC2473B529B1'u), + uint64x2(hi: 0xDA3C0F568CC4F3E8'u, lo: 0xC9E5D72D90A2741E'u), + uint64x2(hi: 0x8865899617FB1871'u, lo: 0x7E2FA67C7A658892'u), + uint64x2(hi: 0xAA7EEBFB9DF9DE8D'u, lo: 0xDDBB901B98FEEAB7'u), + uint64x2(hi: 0xD51EA6FA85785631'u, lo: 0x552A74227F3EA565'u), + uint64x2(hi: 0x8533285C936B35DE'u, lo: 0xD53A88958F87275F'u), + uint64x2(hi: 0xA67FF273B8460356'u, lo: 0x8A892ABAF368F137'u), + uint64x2(hi: 0xD01FEF10A657842C'u, lo: 0x2D2B7569B0432D85'u), + uint64x2(hi: 0x8213F56A67F6B29B'u, lo: 0x9C3B29620E29FC73'u), + uint64x2(hi: 0xA298F2C501F45F42'u, lo: 0x8349F3BA91B47B8F'u), + uint64x2(hi: 0xCB3F2F7642717713'u, lo: 0x241C70A936219A73'u), + uint64x2(hi: 0xFE0EFB53D30DD4D7'u, lo: 0xED238CD383AA0110'u), + uint64x2(hi: 0x9EC95D1463E8A506'u, lo: 0xF4363804324A40AA'u), + uint64x2(hi: 0xC67BB4597CE2CE48'u, lo: 0xB143C6053EDCD0D5'u), + uint64x2(hi: 0xF81AA16FDC1B81DA'u, lo: 0xDD94B7868E94050A'u), + uint64x2(hi: 0x9B10A4E5E9913128'u, lo: 0xCA7CF2B4191C8326'u), + uint64x2(hi: 0xC1D4CE1F63F57D72'u, lo: 0xFD1C2F611F63A3F0'u), + uint64x2(hi: 0xF24A01A73CF2DCCF'u, lo: 0xBC633B39673C8CEC'u), + uint64x2(hi: 0x976E41088617CA01'u, lo: 0xD5BE0503E085D813'u), + uint64x2(hi: 0xBD49D14AA79DBC82'u, lo: 0x4B2D8644D8A74E18'u), + uint64x2(hi: 0xEC9C459D51852BA2'u, lo: 0xDDF8E7D60ED1219E'u), + uint64x2(hi: 0x93E1AB8252F33B45'u, lo: 0xCABB90E5C942B503'u), + uint64x2(hi: 0xB8DA1662E7B00A17'u, lo: 0x3D6A751F3B936243'u), + uint64x2(hi: 0xE7109BFBA19C0C9D'u, lo: 0x0CC512670A783AD4'u), + uint64x2(hi: 0x906A617D450187E2'u, lo: 0x27FB2B80668B24C5'u), + uint64x2(hi: 0xB484F9DC9641E9DA'u, lo: 0xB1F9F660802DEDF6'u), + uint64x2(hi: 0xE1A63853BBD26451'u, lo: 0x5E7873F8A0396973'u), + uint64x2(hi: 0x8D07E33455637EB2'u, lo: 0xDB0B487B6423E1E8'u), + uint64x2(hi: 0xB049DC016ABC5E5F'u, lo: 0x91CE1A9A3D2CDA62'u), + uint64x2(hi: 0xDC5C5301C56B75F7'u, lo: 0x7641A140CC7810FB'u), + uint64x2(hi: 0x89B9B3E11B6329BA'u, lo: 0xA9E904C87FCB0A9D'u), + uint64x2(hi: 0xAC2820D9623BF429'u, lo: 0x546345FA9FBDCD44'u), + uint64x2(hi: 0xD732290FBACAF133'u, lo: 0xA97C177947AD4095'u), + uint64x2(hi: 0x867F59A9D4BED6C0'u, lo: 0x49ED8EABCCCC485D'u), + uint64x2(hi: 0xA81F301449EE8C70'u, lo: 0x5C68F256BFFF5A74'u), + uint64x2(hi: 0xD226FC195C6A2F8C'u, lo: 0x73832EEC6FFF3111'u), + uint64x2(hi: 0x83585D8FD9C25DB7'u, lo: 0xC831FD53C5FF7EAB'u), + uint64x2(hi: 0xA42E74F3D032F525'u, lo: 0xBA3E7CA8B77F5E55'u), + uint64x2(hi: 0xCD3A1230C43FB26F'u, lo: 0x28CE1BD2E55F35EB'u), + uint64x2(hi: 0x80444B5E7AA7CF85'u, lo: 0x7980D163CF5B81B3'u), + uint64x2(hi: 0xA0555E361951C366'u, lo: 0xD7E105BCC332621F'u), + uint64x2(hi: 0xC86AB5C39FA63440'u, lo: 0x8DD9472BF3FEFAA7'u), + uint64x2(hi: 0xFA856334878FC150'u, lo: 0xB14F98F6F0FEB951'u), + uint64x2(hi: 0x9C935E00D4B9D8D2'u, lo: 0x6ED1BF9A569F33D3'u), + uint64x2(hi: 0xC3B8358109E84F07'u, lo: 0x0A862F80EC4700C8'u), + uint64x2(hi: 0xF4A642E14C6262C8'u, lo: 0xCD27BB612758C0FA'u), + uint64x2(hi: 0x98E7E9CCCFBD7DBD'u, lo: 0x8038D51CB897789C'u), + uint64x2(hi: 0xBF21E44003ACDD2C'u, lo: 0xE0470A63E6BD56C3'u), + uint64x2(hi: 0xEEEA5D5004981478'u, lo: 0x1858CCFCE06CAC74'u), + uint64x2(hi: 0x95527A5202DF0CCB'u, lo: 0x0F37801E0C43EBC8'u), + uint64x2(hi: 0xBAA718E68396CFFD'u, lo: 0xD30560258F54E6BA'u), + uint64x2(hi: 0xE950DF20247C83FD'u, lo: 0x47C6B82EF32A2069'u), + uint64x2(hi: 0x91D28B7416CDD27E'u, lo: 0x4CDC331D57FA5441'u), + uint64x2(hi: 0xB6472E511C81471D'u, lo: 0xE0133FE4ADF8E952'u), + uint64x2(hi: 0xE3D8F9E563A198E5'u, lo: 0x58180FDDD97723A6'u), + uint64x2(hi: 0x8E679C2F5E44FF8F'u, lo: 0x570F09EAA7EA7648'u), + uint64x2(hi: 0xB201833B35D63F73'u, lo: 0x2CD2CC6551E513DA'u), + uint64x2(hi: 0xDE81E40A034BCF4F'u, lo: 0xF8077F7EA65E58D1'u), + uint64x2(hi: 0x8B112E86420F6191'u, lo: 0xFB04AFAF27FAF782'u), + uint64x2(hi: 0xADD57A27D29339F6'u, lo: 0x79C5DB9AF1F9B563'u), + uint64x2(hi: 0xD94AD8B1C7380874'u, lo: 0x18375281AE7822BC'u), + uint64x2(hi: 0x87CEC76F1C830548'u, lo: 0x8F2293910D0B15B5'u), + uint64x2(hi: 0xA9C2794AE3A3C69A'u, lo: 0xB2EB3875504DDB22'u), + uint64x2(hi: 0xD433179D9C8CB841'u, lo: 0x5FA60692A46151EB'u), + uint64x2(hi: 0x849FEEC281D7F328'u, lo: 0xDBC7C41BA6BCD333'u), + uint64x2(hi: 0xA5C7EA73224DEFF3'u, lo: 0x12B9B522906C0800'u), + uint64x2(hi: 0xCF39E50FEAE16BEF'u, lo: 0xD768226B34870A00'u), + uint64x2(hi: 0x81842F29F2CCE375'u, lo: 0xE6A1158300D46640'u), + uint64x2(hi: 0xA1E53AF46F801C53'u, lo: 0x60495AE3C1097FD0'u), + uint64x2(hi: 0xCA5E89B18B602368'u, lo: 0x385BB19CB14BDFC4'u), + uint64x2(hi: 0xFCF62C1DEE382C42'u, lo: 0x46729E03DD9ED7B5'u), + uint64x2(hi: 0x9E19DB92B4E31BA9'u, lo: 0x6C07A2C26A8346D1'u), + uint64x2(hi: 0xC5A05277621BE293'u, lo: 0xC7098B7305241885'u), + uint64x2(hi: 0xF70867153AA2DB38'u, lo: 0xB8CBEE4FC66D1EA7'u)] + dragonbox_Assert(k >= kMin) + dragonbox_Assert(k <= kMax) + return pow10[k - kMin] + +## Returns whether value is divisible by 2^e2 + +proc multipleOfPow2*(value: uint64; e2: int32): bool {.inline.} = + dragonbox_Assert(e2 >= 0) + return e2 < 64 and (value and ((uint64(1) shl e2) - 1)) == 0 + +## Returns whether value is divisible by 5^e5 + +proc multipleOfPow5*(value: uint64; e5: int32): bool {.inline.} = + type + MulCmp {.bycopy.} = object + mul: uint64 + cmp: uint64 + + const + mod5 = [MulCmp(mul: 0x0000000000000001'u, cmp: 0xFFFFFFFFFFFFFFFF'u), + MulCmp(mul: 0xCCCCCCCCCCCCCCCD'u, cmp: 0x3333333333333333'u), + MulCmp(mul: 0x8F5C28F5C28F5C29'u, cmp: 0x0A3D70A3D70A3D70'u), + MulCmp(mul: 0x1CAC083126E978D5'u, cmp: 0x020C49BA5E353F7C'u), + MulCmp(mul: 0xD288CE703AFB7E91'u, cmp: 0x0068DB8BAC710CB2'u), + MulCmp(mul: 0x5D4E8FB00BCBE61D'u, cmp: 0x0014F8B588E368F0'u), + MulCmp(mul: 0x790FB65668C26139'u, cmp: 0x000431BDE82D7B63'u), + MulCmp(mul: 0xE5032477AE8D46A5'u, cmp: 0x0000D6BF94D5E57A'u), + MulCmp(mul: 0xC767074B22E90E21'u, cmp: 0x00002AF31DC46118'u), + MulCmp(mul: 0x8E47CE423A2E9C6D'u, cmp: 0x0000089705F4136B'u), + MulCmp(mul: 0x4FA7F60D3ED61F49'u, cmp: 0x000001B7CDFD9D7B'u), + MulCmp(mul: 0x0FEE64690C913975'u, cmp: 0x00000057F5FF85E5'u), + MulCmp(mul: 0x3662E0E1CF503EB1'u, cmp: 0x000000119799812D'u), + MulCmp(mul: 0xA47A2CF9F6433FBD'u, cmp: 0x0000000384B84D09'u), + MulCmp(mul: 0x54186F653140A659'u, cmp: 0x00000000B424DC35'u), + MulCmp(mul: 0x7738164770402145'u, cmp: 0x0000000024075F3D'u), + MulCmp(mul: 0xE4A4D1417CD9A041'u, cmp: 0x000000000734ACA5'u), + MulCmp(mul: 0xC75429D9E5C5200D'u, cmp: 0x000000000170EF54'u), + MulCmp(mul: 0xC1773B91FAC10669'u, cmp: 0x000000000049C977'u), + MulCmp(mul: 0x26B172506559CE15'u, cmp: 0x00000000000EC1E4'u), + MulCmp(mul: 0xD489E3A9ADDEC2D1'u, cmp: 0x000000000002F394'u), + MulCmp(mul: 0x90E860BB892C8D5D'u, cmp: 0x000000000000971D'u), + MulCmp(mul: 0x502E79BF1B6F4F79'u, cmp: 0x0000000000001E39'u), + MulCmp(mul: 0xDCD618596BE30FE5'u, cmp: 0x000000000000060B'u), + MulCmp(mul: 0x2C2AD1AB7BFA3661'u, cmp: 0x0000000000000135'u)] + dragonbox_Assert(e5 >= 0) + dragonbox_Assert(e5 <= 24) + let m5: MulCmp = mod5[e5] + return value * m5.mul <= m5.cmp + +type + FloatingDecimal64* {.bycopy.} = object + significand*: uint64 + exponent*: int32 + + +proc toDecimal64AsymmetricInterval*(e2: int32): FloatingDecimal64 {.inline.} = + ## NB: + ## accept_lower_endpoint = true + ## accept_upper_endpoint = true + const + P: int32 = significandSize + ## Compute k and beta + let minusK: int32 = floorLog10ThreeQuartersPow2(e2) + let betaMinus1: int32 = e2 + floorLog2Pow10(-minusK) + ## Compute xi and zi + let pow10: uint64x2 = computePow10(-minusK) + let lowerEndpoint: uint64 = (pow10.hi - (pow10.hi shr (P + 1))) shr + (64 - P - betaMinus1) + let upperEndpoint: uint64 = (pow10.hi + (pow10.hi shr (P + 0))) shr + (64 - P - betaMinus1) + ## If we don't accept the left endpoint (but we do!) or + ## if the left endpoint is not an integer, increase it + let lowerEndpointIsInteger: bool = (2 <= e2 and e2 <= 3) + let xi: uint64 = lowerEndpoint + uint64(not lowerEndpointIsInteger) + let zi: uint64 = upperEndpoint + ## Try bigger divisor + var q: uint64 = zi div 10 + if q * 10 >= xi: + return FloatingDecimal64(significand: q, exponent: minusK + 1) + q = ((pow10.hi shr (64 - (P + 1) - betaMinus1)) + 1) div 2 + ## When tie occurs, choose one of them according to the rule + if e2 == -77: + dec(q, ord(q mod 2 != 0)) + ## Round to even. + else: + inc(q, ord(q < xi)) + return FloatingDecimal64(significand: q, exponent: minusK) + +proc computeDelta*(pow10: uint64x2; betaMinus1: int32): uint32 {.inline.} = + dragonbox_Assert(betaMinus1 >= 0) + dragonbox_Assert(betaMinus1 <= 63) + return cast[uint32](pow10.hi shr (64 - 1 - betaMinus1)) + +when defined(sizeof_Int128): + proc mul128*(x: uint64; y: uint64): uint64x2 {.inline.} = + ## 1 mulx + type + uint128T = uint128 + let p: uint128T = uint128T(x) * y + let hi: uint64 = cast[uint64](p shr 64) + let lo: uint64 = cast[uint64](p) + return (hi, lo) + +elif defined(vcc) and defined(cpu64): + proc mul128*(x: uint64; y: uint64): uint64x2 {.inline.} = + var hi: uint64 = 0 + var lo: uint64 = umul128(x, y, addr(hi)) + return (hi, lo) + +else: + proc lo32*(x: uint64): uint32 {.inline.} = + return cast[uint32](x) + + proc hi32*(x: uint64): uint32 {.inline.} = + return cast[uint32](x shr 32) + + proc mul128*(a: uint64; b: uint64): uint64x2 {.inline.} = + let b00: uint64 = uint64(lo32(a)) * lo32(b) + let b01: uint64 = uint64(lo32(a)) * hi32(b) + let b10: uint64 = uint64(hi32(a)) * lo32(b) + let b11: uint64 = uint64(hi32(a)) * hi32(b) + let mid1: uint64 = b10 + hi32(b00) + let mid2: uint64 = b01 + lo32(mid1) + let hi: uint64 = b11 + hi32(mid1) + hi32(mid2) + let lo: uint64 = lo32(b00) or uint64(lo32(mid2)) shl 32 + return uint64x2(hi: hi, lo: lo) + +## Returns (x * y) / 2^128 + +proc mulShift*(x: uint64; y: uint64x2): uint64 {.inline.} = + ## 2 mulx + var p1: uint64x2 = mul128(x, y.hi) + var p0: uint64x2 = mul128(x, y.lo) + p1.lo += p0.hi + inc(p1.hi, ord(p1.lo < p0.hi)) + return p1.hi + +proc mulParity*(twoF: uint64; pow10: uint64x2; betaMinus1: int32): bool {.inline.} = + ## 1 mulx, 1 mul + dragonbox_Assert(betaMinus1 >= 1) + dragonbox_Assert(betaMinus1 <= 63) + let p01: uint64 = twoF * pow10.hi + let p10: uint64 = mul128(twoF, pow10.lo).hi + let mid: uint64 = p01 + p10 + return (mid and (uint64(1) shl (64 - betaMinus1))) != 0 + +proc isIntegralEndpoint*(twoF: uint64; e2: int32; minusK: int32): bool {.inline.} = + if e2 < -2: + return false + if e2 <= 9: + return true + if e2 <= 86: + return multipleOfPow5(twoF, minusK) + return false + +proc isIntegralMidpoint*(twoF: uint64; e2: int32; minusK: int32): bool {.inline.} = + if e2 < -4: + return multipleOfPow2(twoF, minusK - e2 + 1) + if e2 <= 9: + return true + if e2 <= 86: + return multipleOfPow5(twoF, minusK) + return false + +proc toDecimal64*(ieeeSignificand: uint64; ieeeExponent: uint64): FloatingDecimal64 {. + inline.} = + const + kappa: int32 = 2 + const + bigDivisor: uint32 = 1000 + ## 10^(kappa + 1) + const + smallDivisor: uint32 = 100 + ## 10^(kappa) + ## + ## Step 1: + ## integer promotion & Schubfach multiplier calculation. + ## + var m2: uint64 + var e2: int32 + if ieeeExponent != 0: + m2 = hiddenBit or ieeeSignificand + e2 = cast[int32](ieeeExponent) - exponentBias + if 0 <= -e2 and -e2 < significandSize and multipleOfPow2(m2, -e2): + ## Small integer. + return FloatingDecimal64(significand: m2 shr -e2, exponent: 0) + if ieeeSignificand == 0 and ieeeExponent > 1: + ## Shorter interval case; proceed like Schubfach. + return toDecimal64AsymmetricInterval(e2) + else: + ## Subnormal case; interval is always regular. + m2 = ieeeSignificand + e2 = 1 - exponentBias + let isEven: bool = (m2 mod 2 == 0) + let acceptLower: bool = isEven + let acceptUpper: bool = isEven + ## Compute k and beta. + let minusK: int32 = floorLog10Pow2(e2) - kappa + let betaMinus1: int32 = e2 + floorLog2Pow10(-minusK) + dragonbox_Assert(betaMinus1 >= 6) + dragonbox_Assert(betaMinus1 <= 9) + let pow10: uint64x2 = computePow10(-minusK) + ## Compute delta + ## 10^kappa <= delta < 10^(kappa + 1) + ## 100 <= delta < 1000 + let delta: uint32 = computeDelta(pow10, betaMinus1) + dragonbox_Assert(delta >= smallDivisor) + dragonbox_Assert(delta < bigDivisor) + let twoFl: uint64 = 2 * m2 - 1 + let twoFc: uint64 = 2 * m2 + let twoFr: uint64 = 2 * m2 + 1 + ## (54 bits) + ## Compute zi + ## (54 + 9 = 63 bits) + let zi: uint64 = mulShift(twoFr shl betaMinus1, pow10) + ## 2 mulx + ## + ## Step 2: + ## Try larger divisor. + ## + var q: uint64 = zi div bigDivisor + ## uint64_t q = Mul128(zi, 0x83126E978D4FDF3Cu).hi >> 9; // 1 mulx + var r: uint32 = cast[uint32](zi) - bigDivisor * cast[uint32](q) + ## r = zi % BigDivisor + ## 0 <= r < 1000 + if r < delta: ## likely ~50% ?! + ## (r > deltai) + ## Exclude the right endpoint if necessary + if r != 0 or acceptUpper or not isIntegralEndpoint(twoFr, e2, minusK): + return FloatingDecimal64(significand: q, exponent: minusK + kappa + 1) + dragonbox_Assert(q != 0) + dec(q) + r = bigDivisor + elif r == delta: ## unlikely + ## Compare fractional parts. + ## Check conditions in the order different from the paper + ## to take advantage of short-circuiting + if (acceptLower and isIntegralEndpoint(twoFl, e2, minusK)) or + mulParity(twoFl, pow10, betaMinus1): + return FloatingDecimal64(significand: q, exponent: minusK + kappa + 1) + else: + discard + ## + ## Step 3: + ## Find the significand with the smaller divisor + ## + q = q * 10 + ## 1 hmul + ## 0 <= r <= 1000 + let dist: uint32 = r - (delta div 2) + (smallDivisor div 2) + let distQ: uint32 = dist div 100 + ## 1 mul + ## const uint32_t dist_r = dist % 100; + q += distQ + ## if /*likely*/ (dist_r == 0) + if dist == distQ * 100: + ## const bool approx_y_parity = ((dist ^ (SmallDivisor / 2)) & 1) != 0; + let approxYParity: bool = (dist and 1) != 0 + ## Check z^(f) >= epsilon^(f) + ## We have either yi == zi - epsiloni or yi == (zi - epsiloni) - 1, + ## where yi == zi - epsiloni if and only if z^(f) >= epsilon^(f) + ## Since there are only 2 possibilities, we only need to care about the + ## parity. Also, zi and r should have the same parity since the divisor + ## is an even number + if mulParity(twoFc, pow10, betaMinus1) != approxYParity: + dec(q) + elif q mod 2 != 0 and isIntegralMidpoint(twoFc, e2, minusK): + dec(q) + return FloatingDecimal64(significand: q, exponent: minusK + kappa) + +## ================================================================================================== +## ToChars +## ================================================================================================== + +proc utoa2Digits*(buf: var openArray[char]; pos: int; digits: uint32) {.inline.} = + const + digits100: array[200, char] = ['0', '0', '0', '1', '0', '2', '0', '3', '0', '4', '0', '5', + '0', '6', '0', '7', '0', '8', '0', '9', '1', '0', '1', '1', '1', '2', '1', '3', '1', '4', + '1', '5', '1', '6', '1', '7', '1', '8', '1', '9', '2', '0', '2', '1', '2', '2', '2', '3', + '2', '4', '2', '5', '2', '6', '2', '7', '2', '8', '2', '9', '3', '0', '3', '1', '3', '2', + '3', '3', '3', '4', '3', '5', '3', '6', '3', '7', '3', '8', '3', '9', '4', '0', '4', '1', + '4', '2', '4', '3', '4', '4', '4', '5', '4', '6', '4', '7', '4', '8', '4', '9', '5', '0', + '5', '1', '5', '2', '5', '3', '5', '4', '5', '5', '5', '6', '5', '7', '5', '8', '5', '9', + '6', '0', '6', '1', '6', '2', '6', '3', '6', '4', '6', '5', '6', '6', '6', '7', '6', '8', + '6', '9', '7', '0', '7', '1', '7', '2', '7', '3', '7', '4', '7', '5', '7', '6', '7', '7', + '7', '8', '7', '9', '8', '0', '8', '1', '8', '2', '8', '3', '8', '4', '8', '5', '8', '6', + '8', '7', '8', '8', '8', '9', '9', '0', '9', '1', '9', '2', '9', '3', '9', '4', '9', '5', + '9', '6', '9', '7', '9', '8', '9', '9'] + dragonbox_Assert(digits <= 99) + buf[pos] = digits100[2 * digits] + buf[pos+1] = digits100[2 * digits + 1] + #copyMem(buf, unsafeAddr(digits100[2 * digits]), 2 * sizeof((char))) + +proc trailingZeros2Digits*(digits: uint32): int32 {.inline.} = + const + trailingZeros100: array[100, int8] = [2'i8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0] + dragonbox_Assert(digits <= 99) + return trailingZeros100[digits] + +when false: + template `+!`(x: cstring; offset: int): cstring = cast[cstring](cast[uint](x) + uint(offset)) + + template dec(x: cstring; offset=1) = x = cast[cstring](cast[uint](x) - uint(offset)) + template inc(x: cstring; offset=1) = x = cast[cstring](cast[uint](x) + uint(offset)) + + proc memset(x: cstring; ch: char; L: int) {.importc, nodecl.} + proc memmove(a, b: cstring; L: int) {.importc, nodecl.} + +proc utoa8DigitsSkipTrailingZeros*(buf: var openArray[char]; pos: int; digits: uint32): int32 {.inline.} = + dragonbox_Assert(digits >= 1) + dragonbox_Assert(digits <= 99999999'u32) + let q: uint32 = digits div 10000 + let r: uint32 = digits mod 10000 + let qH: uint32 = q div 100 + let qL: uint32 = q mod 100 + utoa2Digits(buf, pos, qH) + utoa2Digits(buf, pos + 2, qL) + if r == 0: + return trailingZeros2Digits(if qL == 0: qH else: qL) + (if qL == 0: 6 else: 4) + else: + let rH: uint32 = r div 100 + let rL: uint32 = r mod 100 + utoa2Digits(buf, pos + 4, rH) + utoa2Digits(buf, pos + 6, rL) + return trailingZeros2Digits(if rL == 0: rH else: rL) + (if rL == 0: 2 else: 0) + +proc printDecimalDigitsBackwards*(buf: var openArray[char]; pos: int; output64: uint64): int32 {.inline.} = + var pos = pos + var output64 = output64 + var tz: int32 = 0 + ## number of trailing zeros removed. + var nd: int32 = 0 + ## number of decimal digits processed. + ## At most 17 digits remaining + if output64 >= 100000000'u64: + let q: uint64 = output64 div 100000000'u64 + let r: uint32 = cast[uint32](output64 mod 100000000'u64) + output64 = q + dec(pos, 8) + if r != 0: + tz = utoa8DigitsSkipTrailingZeros(buf, pos, r) + dragonbox_Assert(tz >= 0) + dragonbox_Assert(tz <= 7) + else: + tz = 8 + nd = 8 + dragonbox_Assert(output64 <= high(uint32)) + var output = cast[uint32](output64) + if output >= 10000: + let q: uint32 = output div 10000 + let r: uint32 = output mod 10000 + output = q + dec(pos, 4) + if r != 0: + let rH: uint32 = r div 100 + let rL: uint32 = r mod 100 + utoa2Digits(buf, pos, rH) + utoa2Digits(buf, pos + 2, rL) + if tz == nd: + inc(tz, trailingZeros2Digits(if rL == 0: rH else: rL) + + (if rL == 0: 2 else: 0)) + else: + if tz == nd: + inc(tz, 4) + else: + for i in 0..3: buf[pos+i] = '0' + ## (actually not required...) + inc(nd, 4) + if output >= 100: + let q: uint32 = output div 100 + let r: uint32 = output mod 100 + output = q + dec(pos, 2) + utoa2Digits(buf, pos, r) + if tz == nd: + inc(tz, trailingZeros2Digits(r)) + inc(nd, 2) + if output >= 100: + let q2: uint32 = output div 100 + let r2: uint32 = output mod 100 + output = q2 + dec(pos, 2) + utoa2Digits(buf, pos, r2) + if tz == nd: + inc(tz, trailingZeros2Digits(r2)) + inc(nd, 2) + dragonbox_Assert(output >= 1) + dragonbox_Assert(output <= 99) + if output >= 10: + let q: uint32 = output + dec(pos, 2) + utoa2Digits(buf, pos, q) + if tz == nd: + inc(tz, trailingZeros2Digits(q)) + else: + let q: uint32 = output + dragonbox_Assert(q >= 1) + dragonbox_Assert(q <= 9) + dec(pos) + buf[pos] = chr(ord('0') + q) + return tz + +proc decimalLength*(v: uint64): int32 {.inline.} = + dragonbox_Assert(v >= 1) + dragonbox_Assert(v <= 99999999999999999'u64) + if cast[uint32](v shr 32) != 0: + if v >= 10000000000000000'u64: + return 17 + if v >= 1000000000000000'u64: + return 16 + if v >= 100000000000000'u64: + return 15 + if v >= 10000000000000'u64: + return 14 + if v >= 1000000000000'u64: + return 13 + if v >= 100000000000'u64: + return 12 + if v >= 10000000000'u64: + return 11 + return 10 + let v32: uint32 = cast[uint32](v) + if v32 >= 1000000000'u: + return 10 + if v32 >= 100000000'u: + return 9 + if v32 >= 10000000'u: + return 8 + if v32 >= 1000000'u: + return 7 + if v32 >= 100000'u: + return 6 + if v32 >= 10000'u: + return 5 + if v32 >= 1000'u: + return 4 + if v32 >= 100'u: + return 3 + if v32 >= 10'u: + return 2 + return 1 + +proc formatDigits*(buffer: var openArray[char]; pos: int; digits: uint64; decimalExponent: int32; + forceTrailingDotZero = false): int {.inline.} = + const + minFixedDecimalPoint: int32 = -6 + const + maxFixedDecimalPoint: int32 = 17 + var pos = pos + assert(minFixedDecimalPoint <= -1, "internal error") + assert(maxFixedDecimalPoint >= 17, "internal error") + dragonbox_Assert(digits >= 1) + dragonbox_Assert(digits <= 99999999999999999'u64) + dragonbox_Assert(decimalExponent >= -999) + dragonbox_Assert(decimalExponent <= 999) + var numDigits: int32 = decimalLength(digits) + let decimalPoint: int32 = numDigits + decimalExponent + let useFixed: bool = minFixedDecimalPoint <= decimalPoint and + decimalPoint <= maxFixedDecimalPoint + ## Prepare the buffer. + for i in 0..<32: buffer[pos+i] = '0' + assert(minFixedDecimalPoint >= -30, "internal error") + assert(maxFixedDecimalPoint <= 32, "internal error") + var decimalDigitsPosition: int32 + if useFixed: + if decimalPoint <= 0: + ## 0.[000]digits + decimalDigitsPosition = 2 - decimalPoint + else: + ## dig.its + ## digits[000] + decimalDigitsPosition = 0 + else: + ## dE+123 or d.igitsE+123 + decimalDigitsPosition = 1 + var digitsEnd = pos + int(decimalDigitsPosition + numDigits) + let tz: int32 = printDecimalDigitsBackwards(buffer, digitsEnd, digits) + dec(digitsEnd, tz) + dec(numDigits, tz) + ## decimal_exponent += tz; // => decimal_point unchanged. + if useFixed: + if decimalPoint <= 0: + ## 0.[000]digits + buffer[pos+1] = '.' + pos = digitsEnd + elif decimalPoint < numDigits: + ## dig.its + when true: #defined(vcc) and not defined(clang): + ## VC does not inline the memmove call below. (Even if compiled with /arch:AVX2.) + ## However, memcpy will be inlined. + var tmp: array[16, char] + for i in 0..<16: tmp[i] = buffer[i+pos+decimalPoint] + for i in 0..<16: buffer[i+pos+decimalPoint+1] = tmp[i] + else: + memmove(buffer +! (decimalPoint + 1), buffer +! decimalPoint, 16) + buffer[pos+decimalPoint] = '.' + pos = digitsEnd + 1 + else: + ## digits[000] + inc(pos, decimalPoint) + if forceTrailingDotZero: + buffer[pos] = '.' + buffer[pos+1] = '0' + inc(pos, 2) + else: + ## Copy the first digit one place to the left. + buffer[pos] = buffer[pos+1] + if numDigits == 1: + ## dE+123 + inc(pos) + else: + ## d.igitsE+123 + buffer[pos+1] = '.' + pos = digitsEnd + let scientificExponent: int32 = decimalPoint - 1 + ## SF_ASSERT(scientific_exponent != 0); + buffer[pos] = 'e' + buffer[pos+1] = if scientificExponent < 0: '-' else: '+' + inc(pos, 2) + let k: uint32 = cast[uint32](if scientificExponent < 0: -scientificExponent else: scientificExponent) + if k < 10: + buffer[pos] = chr(ord('0') + k) + inc(pos) + elif k < 100: + utoa2Digits(buffer, pos, k) + inc(pos, 2) + else: + let q: uint32 = k div 100 + let r: uint32 = k mod 100 + buffer[pos] = chr(ord('0') + q) + inc(pos) + utoa2Digits(buffer, pos, r) + inc(pos, 2) + return pos + +proc toChars*(buffer: var openArray[char]; v: float; forceTrailingDotZero = false): int {. + inline.} = + var pos = 0 + let significand: uint64 = physicalSignificand(constructDouble(v)) + let exponent: uint64 = physicalExponent(constructDouble(v)) + if exponent != maxIeeeExponent: + ## Finite + buffer[pos] = '-' + inc(pos, signBit(constructDouble(v))) + if exponent != 0 or significand != 0: + ## != 0 + let dec = toDecimal64(significand, exponent) + return formatDigits(buffer, pos, dec.significand, dec.exponent, + forceTrailingDotZero) + else: + buffer[pos] = '0' + buffer[pos+1] = '.' + buffer[pos+2] = '0' + buffer[pos+3] = ' ' + inc(pos, if forceTrailingDotZero: 3 else: 1) + return pos + if significand == 0: + buffer[pos] = '-' + inc(pos, signBit(constructDouble(v))) + buffer[pos] = 'i' + buffer[pos+1] = 'n' + buffer[pos+2] = 'f' + buffer[pos+3] = ' ' + return pos + 3 + else: + buffer[pos] = 'n' + buffer[pos+1] = 'a' + buffer[pos+2] = 'n' + buffer[pos+3] = ' ' + return pos + 3 + +when false: + proc toString*(value: float): string = + var buffer: array[dtoaMinBufferLength, char] + let last = toChars(addr buffer, value) + let L = cast[int](last) - cast[int](addr(buffer)) + result = newString(L) + copyMem(addr result[0], addr buffer[0], L) + diff --git a/lib/system/formatfloat.nim b/lib/system/formatfloat.nim index cb46c8c36..41bc56adc 100644 --- a/lib/system/formatfloat.nim +++ b/lib/system/formatfloat.nim @@ -7,53 +7,58 @@ # distribution, for details about the copyright. # -proc c_sprintf(buf, frmt: cstring): cint {.header: "", - importc: "sprintf", varargs, noSideEffect.} +when not defined(nimLegacyAddFloat) and not defined(nimscript) and + not defined(js) and defined(nimHasDragonBox): + import dragonbox -proc writeToBuffer(buf: var array[65, char]; value: cstring) = - var i = 0 - while value[i] != '\0': - buf[i] = value[i] - inc i + proc writeFloatToBuffer*(buf: var array[65, char]; value: BiggestFloat): int = + ## This is the implementation to format floats. + ## + ## returns the amount of bytes written to `buf` not counting the + ## terminating '\0' character. + result = toChars(buf, value, forceTrailingDotZero=true) + buf[result] = '\0' -proc writeFloatToBuffer*(buf: var array[65, char]; value: BiggestFloat): int = - ## This is the implementation to format floats in the Nim - ## programming language. The specific format for floating point - ## numbers is not specified in the Nim programming language and - ## might change slightly in the future, but at least wherever you - ## format a float, it should be consistent. - ## - ## returns the amount of bytes written to `buf` not counting the - ## terminating '\0' character. - ## - ## * `buf` - A buffer to write into. The buffer does not need to be - ## initialized and it will be overridden. - ## - var n: int = c_sprintf(addr buf, "%.16g", value) - var hasDot = false - for i in 0..n-1: - if buf[i] == ',': - buf[i] = '.' - hasDot = true - elif buf[i] in {'a'..'z', 'A'..'Z', '.'}: - hasDot = true - if not hasDot: - buf[n] = '.' - buf[n+1] = '0' - buf[n+2] = '\0' - result = n + 2 - else: - result = n - # On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN' or 'nan(ind)' - # of '-1.#IND' are produced. - # We want to get rid of these here: - if buf[n-1] in {'n', 'N', 'D', 'd', ')'}: - writeToBuffer(buf, "nan") - result = 3 - elif buf[n-1] == 'F': - if buf[0] == '-': - writeToBuffer(buf, "-inf") - result = 4 +else: + proc c_sprintf(buf, frmt: cstring): cint {.header: "", + importc: "sprintf", varargs, noSideEffect.} + + proc writeToBuffer(buf: var array[65, char]; value: cstring) = + var i = 0 + while value[i] != '\0': + buf[i] = value[i] + inc i + + proc writeFloatToBuffer*(buf: var array[65, char]; value: BiggestFloat): int = + ## This is the implementation to format floats. + ## + ## returns the amount of bytes written to `buf` not counting the + ## terminating '\0' character. + var n: int = c_sprintf(addr buf, "%.16g", value) + var hasDot = false + for i in 0..n-1: + if buf[i] == ',': + buf[i] = '.' + hasDot = true + elif buf[i] in {'a'..'z', 'A'..'Z', '.'}: + hasDot = true + if not hasDot: + buf[n] = '.' + buf[n+1] = '0' + buf[n+2] = '\0' + result = n + 2 else: - writeToBuffer(buf, "inf") + result = n + # On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN' or 'nan(ind)' + # of '-1.#IND' are produced. + # We want to get rid of these here: + if buf[n-1] in {'n', 'N', 'D', 'd', ')'}: + writeToBuffer(buf, "nan") result = 3 + elif buf[n-1] == 'F': + if buf[0] == '-': + writeToBuffer(buf, "-inf") + result = 4 + else: + writeToBuffer(buf, "inf") + result = 3 diff --git a/tests/float/tfloat6.nim b/tests/float/tfloat6.nim index c4cd6e932..c0e6e419f 100644 --- a/tests/float/tfloat6.nim +++ b/tests/float/tfloat6.nim @@ -1,10 +1,10 @@ discard """ output: ''' -1e-06 : 1e-06 -1e-06 : 1e-06 +0.000001 : 0.000001 +0.000001 : 0.000001 0.001 : 0.001 -1e-06 : 1e-06 -1e-06 : 1e-06 +0.000001 : 0.000001 +0.000001 : 0.000001 10.000001 : 10.000001 100.000001 : 100.000001 ''' From f10eef29b5d77ad45b00b0d4eb7e87f930c2a0a4 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 2 Jun 2021 13:13:23 +0200 Subject: [PATCH 2163/3768] fixes #18059 (#18140) * fixes #18059 --- compiler/semexprs.nim | 28 ++++++++++++++++++++++++++-- compiler/semstmts.nim | 20 +++++++++++++++++++- tests/metatype/tcps.nim | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 tests/metatype/tcps.nim diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 8c4f25e12..c839d04d9 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -54,7 +54,8 @@ proc semOperand(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = result = semExpr(c, n, flags + {efOperand}) if result.typ != nil: # XXX tyGenericInst here? - if result.typ.kind == tyProc and tfUnresolved in result.typ.flags: + if result.typ.kind == tyProc and hasUnresolvedParams(result, {efOperand}): + #and tfUnresolved in result.typ.flags: localError(c.config, n.info, errProcHasNoConcreteType % n.renderTree) if result.typ.kind in {tyVar, tyLent}: result = newDeref(result) elif {efWantStmt, efAllowStmt} * flags != {}: @@ -2649,6 +2650,23 @@ proc shouldBeBracketExpr(n: PNode): bool = n[0] = be return true +proc asBracketExpr(c: PContext; n: PNode): PNode = + proc isGeneric(c: PContext; n: PNode): bool = + if n.kind in {nkIdent, nkAccQuoted}: + let s = qualifiedLookUp(c, n, {}) + result = s != nil and isGenericRoutineStrict(s) + + assert n.kind in nkCallKinds + if n.len > 1 and isGeneric(c, n[1]): + let b = n[0] + if b.kind in nkSymChoices: + for i in 0.. Date: Wed, 2 Jun 2021 13:14:10 +0200 Subject: [PATCH 2164/3768] refactoring: removed empty withBracketExpr template (#18152) --- compiler/semgnrc.nim | 6 ++---- compiler/semtempl.nim | 14 ++++---------- 2 files changed, 6 insertions(+), 14 deletions(-) diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index ebd361db6..4f3c50dae 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -303,8 +303,7 @@ proc semGenericStmt(c: PContext, n: PNode, result = newNodeI(nkCall, n.info) result.add newIdentNode(getIdent(c.cache, "[]"), n.info) for i in 0.. Date: Wed, 2 Jun 2021 09:02:14 -0700 Subject: [PATCH 2166/3768] fix #16993, #18054, #17835 runnableExamples now works with templates and nested templates (#18082) --- compiler/evaltempl.nim | 12 ++-- lib/system/assertions.nim | 47 ++++++--------- .../expected/subdir/subdir_b/utils.html | 4 +- nimdoc/testproject/expected/testproject.html | 11 ++-- nimdoc/testproject/subdir/subdir_b/utils.nim | 10 +++- tests/js/tstdlib_various.nim | 2 +- tests/nimdoc/trunnableexamples.nim | 57 ++++++++++++++++++- tests/nimdoc/trunnableexamples2.nim | 11 ++++ 8 files changed, 108 insertions(+), 46 deletions(-) create mode 100644 tests/nimdoc/trunnableexamples2.nim diff --git a/compiler/evaltempl.nim b/compiler/evaltempl.nim index a85314ac2..8a6bf9287 100644 --- a/compiler/evaltempl.nim +++ b/compiler/evaltempl.nim @@ -83,10 +83,14 @@ proc evalTemplateAux(templ, actual: PNode, c: var TemplCtx, result: PNode) = not c.isDeclarative: c.isDeclarative = true isDeclarative = true - var res = copyNode(c, templ, actual) - for i in 0..`_. + runnableExamples: assert 1 == 1 + runnableExamples("--assertions:off"): + assert 1 == 2 # no code generated, no failure here + runnableExamples("-d:danger"): assert 1 == 2 # ditto assertImpl(cond, msg, astToStr(cond), compileOption("assertions")) template doAssert*(cond: untyped, msg = "") = ## Similar to `assert <#assert.t,untyped,string>`_ but is always turned on regardless of `--assertions`. + runnableExamples: + doAssert 1 == 1 # generates code even when built with `-d:danger` or `--assertions:off` assertImpl(cond, msg, astToStr(cond), true) template onFailedAssert*(msg, code: untyped): untyped {.dirty.} = ## Sets an assertion failure handler that will intercept any assert ## statements following `onFailedAssert` in the current scope. + runnableExamples: + type MyError = object of CatchableError + lineinfo: tuple[filename: string, line: int, column: int] + # block-wide policy to change the failed assert exception type in order to + # include a lineinfo + onFailedAssert(msg): + raise (ref MyError)(msg: msg, lineinfo: instantiationInfo(-2)) + doAssertRaises(MyError): doAssert false template failedAssertImpl(msgIMPL: string): untyped {.dirty.} = let msg = msgIMPL code template doAssertRaises*(exception: typedesc, code: untyped) = ## Raises `AssertionDefect` if specified `code` does not raise `exception`. + runnableExamples: + doAssertRaises(ValueError): raise newException(ValueError, "Hello World") + doAssertRaises(CatchableError): raise newException(ValueError, "Hello World") + doAssertRaises(AssertionDefect): doAssert false var wrong = false const begin = "expected raising '" & astToStr(exception) & "', instead" const msgEnd = " by: " & astToStr(code) diff --git a/nimdoc/testproject/expected/subdir/subdir_b/utils.html b/nimdoc/testproject/expected/subdir/subdir_b/utils.html index 794066cae..826cad02a 100644 --- a/nimdoc/testproject/expected/subdir/subdir_b/utils.html +++ b/nimdoc/testproject/expected/subdir/subdir_b/utils.html @@ -206,9 +206,9 @@ constructor.
    template fromUtilsGen(): untyped
    -this should be shown in utils.html +should be shown in utils.html only

    Example:

    -
    assert 3*2 == 6
    ditto +
    discard "should be in utils.html only, not in module that calls fromUtilsGen"
    ditto
    diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html index d2fe38021..513dd507b 100644 --- a/nimdoc/testproject/expected/testproject.html +++ b/nimdoc/testproject/expected/testproject.html @@ -431,10 +431,7 @@ window.addEventListener('DOMContentLoaded', main); discard "in top2"
    top2 after

    Example:

    import testproject
    -discard "in top3"
    top3 after -

    Example:

    -
    import testproject
    -assert 3*2 == 6

    +discard "in top3"
    top3 after

    Imports

    @@ -574,7 +571,8 @@ My someFunc. Stuff in fromUtilsGen is called

    Example:

    -
    discard 1
    +
    discard """should be shown as examples for fromUtils3
    +       in module calling fromUtilsGen"""
    @@ -957,6 +955,9 @@ cz18
    ok3 +

    Example:

    +
    discard """should be shown as examples for fromUtils2
    +       in module calling fromUtilsGen"""
    diff --git a/nimdoc/testproject/subdir/subdir_b/utils.nim b/nimdoc/testproject/subdir/subdir_b/utils.nim index 128e8e481..4e3aa1f10 100644 --- a/nimdoc/testproject/subdir/subdir_b/utils.nim +++ b/nimdoc/testproject/subdir/subdir_b/utils.nim @@ -50,9 +50,9 @@ template bEnum*(): untyped = discard template fromUtilsGen*(): untyped = - ## this should be shown in utils.html + ## should be shown in utils.html only runnableExamples: - assert 3*2 == 6 + discard "should be in utils.html only, not in module that calls fromUtilsGen" ## ditto iterator fromUtils1*(): int = @@ -64,7 +64,11 @@ template fromUtilsGen*(): untyped = template fromUtils2*() = ## ok3 + runnableExamples: + discard """should be shown as examples for fromUtils2 + in module calling fromUtilsGen""" proc fromUtils3*() = ## came form utils but should be shown where `fromUtilsGen` is called - runnableExamples: discard 1 + runnableExamples: discard """should be shown as examples for fromUtils3 + in module calling fromUtilsGen""" diff --git a/tests/js/tstdlib_various.nim b/tests/js/tstdlib_various.nim index a1bb63d46..4b5ce1de8 100644 --- a/tests/js/tstdlib_various.nim +++ b/tests/js/tstdlib_various.nim @@ -153,7 +153,7 @@ block tsplit2: var errored = false try: discard "hello".split("") - except AssertionError: + except AssertionDefect: errored = true doAssert errored diff --git a/tests/nimdoc/trunnableexamples.nim b/tests/nimdoc/trunnableexamples.nim index bc5ea0e5e..ac7a0e26f 100644 --- a/tests/nimdoc/trunnableexamples.nim +++ b/tests/nimdoc/trunnableexamples.nim @@ -1,6 +1,7 @@ discard """ -cmd: "nim doc --doccmd:-d:testFooExternal --hints:off $file" +cmd: "nim doc --doccmd:--hints:off --hints:off $file" action: "compile" +nimoutFull: true nimout: ''' foo1 foo2 @@ -8,12 +9,23 @@ foo3 foo5 foo6 foo7 +in examplesInTemplate1 +doc in outer +doc in inner1 +doc in inner2 foo8 foo9 ''' joinable: false """ +#[ +pending bug #18077, use instead: +cmd: "nim doc --doccmd:'-d:testFooExternal --hints:off' --hints:off $file" +and merge trunnableexamples2 back here +]# +{.define(testFooExternal).} + proc fun*() = runnableExamples: block: # `defer` only allowed inside a block @@ -109,6 +121,47 @@ when true: # runnableExamples with rdoccmd # passing seq (to run with multiple compilation options) runnableExamples(@["-b:cpp", "-b:js"]): discard +when true: # bug #16993 + template examplesInTemplate1*(cond: untyped) = + ## in examplesInTemplate1 + runnableExamples: + echo "in examplesInTemplate1" + discard + examplesInTemplate1 true + examplesInTemplate1 true + examplesInTemplate1 true + +when true: # bug #18054 + template outer*(body: untyped) = + ## outer template doc string. + runnableExamples: + echo "doc in outer" + ## + template inner1*() = + ## inner1 template doc string. + runnableExamples: + echo "doc in inner1" + ## + + template inner2*() = + ## inner2 template doc string. + runnableExamples: + echo "doc in inner2" + body + outer: + inner1() + inner2() + +when true: # bug #17835 + template anyItFake*(s, pred: untyped): bool = + ## Foo + runnableExamples: discard + true + + proc anyItFakeMain*(n: seq[int]): bool = + result = anyItFake(n, it == 0) + # this was giving: Error: runnableExamples must appear before the first non-comment statement + runnableExamples: block: # bug #17279 when int.sizeof == 8: @@ -118,7 +171,7 @@ runnableExamples: # bug #13491 block: proc fun(): int = doAssert false - doAssertRaises(AssertionError, (discard fun())) + doAssertRaises(AssertionDefect, (discard fun())) block: template foo(body) = discard diff --git a/tests/nimdoc/trunnableexamples2.nim b/tests/nimdoc/trunnableexamples2.nim new file mode 100644 index 000000000..5a437744e --- /dev/null +++ b/tests/nimdoc/trunnableexamples2.nim @@ -0,0 +1,11 @@ +discard """ +cmd: "nim doc --doccmd:-d:testFooExternal --hints:off $file" +action: "compile" +joinable: false +""" + +# pending bug #18077, merge back inside trunnableexamples.nim +when true: # runnableExamples with rdoccmd + runnableExamples "-d:testFoo -d:testBar": + doAssert defined(testFoo) and defined(testBar) + doAssert defined(testFooExternal) From f27f3f65df0bab567cc416cd1332a502833bd12f Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 3 Jun 2021 10:59:06 +0800 Subject: [PATCH 2167/3768] [std/hashcommon]improve docs a bit (#18153) * reduce duplicated deprecated messages --- lib/pure/collections/hashcommon.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/pure/collections/hashcommon.nim b/lib/pure/collections/hashcommon.nim index 030446176..a169418ce 100644 --- a/lib/pure/collections/hashcommon.nim +++ b/lib/pure/collections/hashcommon.nim @@ -34,10 +34,10 @@ proc slotsNeeded(count: Natural): int {.inline.} = result = nextPowerOfTwo(count * 3 div 2 + 4) proc rightSize*(count: Natural): int {.inline, deprecated: "Deprecated since 1.4.0".} = - ## **Deprecated since Nim v1.4.0**, it is not needed anymore - ## because picking the correct size is done internally. + ## It is not needed anymore because + ## picking the correct size is done internally. ## - ## Return the value of `initialSize` to support `count` items. + ## Returns the value of `initialSize` to support `count` items. ## ## If more items are expected to be added, simply add that ## expected extra amount to the parameter before calling this. From bbce3d2da919bef2900e4061b9b8a99cb84fdf57 Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 3 Jun 2021 12:09:04 +0800 Subject: [PATCH 2168/3768] [std/tables] remove unnecessary `do: ` (#18160) --- lib/pure/collections/tables.nim | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index b71bb0845..d617e06e4 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -594,17 +594,17 @@ template withValue*[A, B](t: var Table[A, B], key: A, value, body: untyped) = let u = User(name: "Hello", uid: 99) t[1] = u - t.withValue(1, value) do: + t.withValue(1, value): # block is executed only if `key` in `t` value.name = "Nim" value.uid = 1314 - t.withValue(2, value) do: + t.withValue(2, value): value.name = "No" value.uid = 521 - doAssert t[1].name == "Nim" - doAssert t[1].uid == 1314 + assert t[1].name == "Nim" + assert t[1].uid == 1314 mixin rawGet var hc: Hash @@ -629,16 +629,22 @@ template withValue*[A, B](t: var Table[A, B], key: A, let u = User(name: "Hello", uid: 99) t[1] = u - t.withValue(1, value) do: + t.withValue(1, value): # block is executed only if `key` in `t` value.name = "Nim" value.uid = 1314 - # do: - # # block is executed when `key` not in `t` - # raise newException(KeyError, "Key not found") - doAssert t[1].name == "Nim" - doAssert t[1].uid == 1314 + t.withValue(521, value): + doAssert false + do: + # block is executed when `key` not in `t` + t[1314] = User(name: "exist", uid: 521) + + assert t[1].name == "Nim" + assert t[1].uid == 1314 + assert t[1314].name == "exist" + assert t[1314].uid == 521 + mixin rawGet var hc: Hash var index = rawGet(t, key, hc) From 06960bb9cb6bb548c8f72882169ae65e14f29e89 Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 3 Jun 2021 13:35:24 +0800 Subject: [PATCH 2169/3768] Ref #17697 improve withValue docs (#18154) * Ref #17697 improve withValue docs * address comments --- lib/pure/collections/sharedtables.nim | 81 +++++++++++++++++---------- 1 file changed, 50 insertions(+), 31 deletions(-) diff --git a/lib/pure/collections/sharedtables.nim b/lib/pure/collections/sharedtables.nim index 4ac3befb6..2788ec785 100644 --- a/lib/pure/collections/sharedtables.nim +++ b/lib/pure/collections/sharedtables.nim @@ -60,17 +60,27 @@ template withLock(t, x: untyped) = template withValue*[A, B](t: var SharedTable[A, B], key: A, value, body: untyped) = - ## retrieves the value at `t[key]`. + ## Retrieves the value at `t[key]`. ## `value` can be modified in the scope of the `withValue` call. - ## - ## .. code-block:: nim - ## - ## sharedTable.withValue(key, value) do: - ## # block is executed only if `key` in `t` - ## # value is threadsafe in block - ## value.name = "username" - ## value.uid = 1000 - ## + runnableExamples: + var table: SharedTable[string, string] + init(table) + + table["a"] = "x" + table["b"] = "y" + table["c"] = "z" + + table.withValue("a", value): + assert value[] == "x" + + table.withValue("b", value): + value[] = "modified" + + table.withValue("b", value): + assert value[] == "modified" + + table.withValue("nonexistent", value): + assert false # not called acquire(t.lock) try: var hc: Hash @@ -84,20 +94,29 @@ template withValue*[A, B](t: var SharedTable[A, B], key: A, template withValue*[A, B](t: var SharedTable[A, B], key: A, value, body1, body2: untyped) = - ## retrieves the value at `t[key]`. + ## Retrieves the value at `t[key]`. ## `value` can be modified in the scope of the `withValue` call. - ## - ## .. code-block:: nim - ## - ## sharedTable.withValue(key, value) do: - ## # block is executed only if `key` in `t` - ## # value is threadsafe in block - ## value.name = "username" - ## value.uid = 1000 - ## do: - ## # block is executed when `key` not in `t` - ## raise newException(KeyError, "Key not found") - ## + runnableExamples: + var table: SharedTable[string, string] + init(table) + + table["a"] = "x" + table["b"] = "y" + table["c"] = "z" + + + table.withValue("a", value): + value[] = "m" + + table.withValue("d", value): + discard value + doAssert false + do: # if "d" notin table + table["d"] = "n" + + assert table.mget("a") == "m" + assert table.mget("d") == "n" + acquire(t.lock) try: var hc: Hash @@ -112,7 +131,7 @@ template withValue*[A, B](t: var SharedTable[A, B], key: A, release(t.lock) proc mget*[A, B](t: var SharedTable[A, B], key: A): var B = - ## retrieves the value at `t[key]`. The value can be modified. + ## Retrieves the value at `t[key]`. The value can be modified. ## If `key` is not in `t`, the `KeyError` exception is raised. withLock t: var hc: Hash @@ -126,7 +145,7 @@ proc mget*[A, B](t: var SharedTable[A, B], key: A): var B = raise newException(KeyError, "key not found") proc mgetOrPut*[A, B](t: var SharedTable[A, B], key: A, val: B): var B = - ## retrieves value at `t[key]` or puts `val` if not present, either way + ## Retrieves value at `t[key]` or puts `val` if not present, either way ## returning a value which can be modified. **Note**: This is inherently ## unsafe in the context of multi-threading since it returns a pointer ## to `B`. @@ -134,7 +153,7 @@ proc mgetOrPut*[A, B](t: var SharedTable[A, B], key: A, val: B): var B = mgetOrPutImpl(enlarge) proc hasKeyOrPut*[A, B](t: var SharedTable[A, B], key: A, val: B): bool = - ## returns true if `key` is in the table, otherwise inserts `value`. + ## Returns true if `key` is in the table, otherwise inserts `value`. withLock t: hasKeyOrPutImpl(enlarge) @@ -191,28 +210,28 @@ proc withKey*[A, B](t: var SharedTable[A, B], key: A, st_maybeRehashPutImpl(enlarge) proc `[]=`*[A, B](t: var SharedTable[A, B], key: A, val: B) = - ## puts a (key, value)-pair into `t`. + ## Puts a (key, value)-pair into `t`. withLock t: putImpl(enlarge) proc add*[A, B](t: var SharedTable[A, B], key: A, val: B) = - ## puts a new (key, value)-pair into `t` even if `t[key]` already exists. + ## Puts a new (key, value)-pair into `t` even if `t[key]` already exists. ## This can introduce duplicate keys into the table! withLock t: addImpl(enlarge) proc del*[A, B](t: var SharedTable[A, B], key: A) = - ## deletes `key` from hash table `t`. + ## Deletes `key` from hash table `t`. withLock t: delImpl(tabMakeEmpty, tabCellEmpty, tabCellHash) proc len*[A, B](t: var SharedTable[A, B]): int = - ## number of elements in `t` + ## Number of elements in `t`. withLock t: result = t.counter proc init*[A, B](t: var SharedTable[A, B], initialSize = 32) = - ## creates a new hash table that is empty. + ## Creates a new hash table that is empty. ## ## This proc must be called before any other usage of `t`. let initialSize = slotsNeeded(initialSize) From daaa40973c65c46f8d38dc6033f56fffcd7d8d3f Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 3 Jun 2021 12:09:40 +0200 Subject: [PATCH 2170/3768] added float32 schubfach algorithm; wip (#18155) * added float32 schubfach algorithm; wip * fixes #18418 --- compiler/ccgexprs.nim | 6 +- lib/system/dollars.nim | 4 + lib/system/schubfach.nim | 451 +++++++++++++++++++++++++++++++++++++++ lib/system/strmantle.nim | 13 ++ tests/float/tfloat6.nim | 5 + 5 files changed, 478 insertions(+), 1 deletion(-) create mode 100644 lib/system/schubfach.nim diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 7cb403a94..7ed80145b 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2300,7 +2300,11 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mInt64ToStr: genDollar(p, e, d, "#nimInt64ToStr($1)") of mBoolToStr: genDollar(p, e, d, "#nimBoolToStr($1)") of mCharToStr: genDollar(p, e, d, "#nimCharToStr($1)") - of mFloatToStr: genDollar(p, e, d, "#nimFloatToStr($1)") + of mFloatToStr: + if e[1].typ.skipTypes(abstractInst).kind == tyFloat32: + genDollar(p, e, d, "#nimFloat32ToStr($1)") + else: + genDollar(p, e, d, "#nimFloatToStr($1)") of mCStrToStr: genDollar(p, e, d, "#cstrToNimstr($1)") of mStrToStr, mUnown: expr(p, e[1], d) of mIsolate: genCall(p, e, d) diff --git a/lib/system/dollars.nim b/lib/system/dollars.nim index ce4e8e0ca..5a7a77e56 100644 --- a/lib/system/dollars.nim +++ b/lib/system/dollars.nim @@ -58,6 +58,10 @@ proc `$`*(x: float): string {.magic: "FloatToStr", noSideEffect.} ## The stringify operator for a float argument. Returns `x` ## converted to a decimal string. +proc `$`*(x: float32): string {.magic: "FloatToStr", noSideEffect.} + ## The stringify operator for a float32 argument. Returns `x` + ## converted to a decimal string. + proc `$`*(x: bool): string {.magic: "BoolToStr", noSideEffect.} ## The stringify operator for a boolean argument. Returns `x` ## converted to the string "false" or "true". diff --git a/lib/system/schubfach.nim b/lib/system/schubfach.nim new file mode 100644 index 000000000..c344b7425 --- /dev/null +++ b/lib/system/schubfach.nim @@ -0,0 +1,451 @@ +## Copyright 2020 Alexander Bolz +## +## Distributed under the Boost Software License, Version 1.0. +## (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt) + +## -------------------------------------------------------------------------------------------------- +## This file contains an implementation of the Schubfach algorithm as described in +## +## [1] Raffaello Giulietti, "The Schubfach way to render doubles", +## https://drive.google.com/open?id=1luHhyQF9zKlM8yJ1nebU0OgVYhfC6CBN +## -------------------------------------------------------------------------------------------------- + +template sf_Assert(x: untyped): untyped = + assert(x) + +## ================================================================================================== +## +## ================================================================================================== + +type + ValueType = float32 + BitsType = uint32 + Single {.bycopy.} = object + bits: BitsType + +const + significandSize: int32 = 24 + MaxExponent = 128 + exponentBias: int32 = MaxExponent - 1 + (significandSize - 1) + maxIeeeExponent: BitsType = BitsType(2 * MaxExponent - 1) + hiddenBit: BitsType = BitsType(1) shl (significandSize - 1) + significandMask: BitsType = hiddenBit - 1 + exponentMask: BitsType = maxIeeeExponent shl (significandSize - 1) + signMask: BitsType = not (not BitsType(0) shr 1) + +proc constructSingle(bits: BitsType): Single {.constructor.} = + result.bits = bits + +proc constructSingle(value: ValueType): Single {.constructor.} = + result.bits = cast[typeof(result.bits)](value) + +proc physicalSignificand(this: Single): BitsType {.noSideEffect.} = + return this.bits and significandMask + +proc physicalExponent(this: Single): BitsType {.noSideEffect.} = + return (this.bits and exponentMask) shr (significandSize - 1) + +proc isFinite(this: Single): bool {.noSideEffect.} = + return (this.bits and exponentMask) != exponentMask + +proc isInf(this: Single): bool {.noSideEffect.} = + return (this.bits and exponentMask) == exponentMask and + (this.bits and significandMask) == 0 + +proc isNaN(this: Single): bool {.noSideEffect.} = + return (this.bits and exponentMask) == exponentMask and + (this.bits and significandMask) != 0 + +proc isZero(this: Single): bool {.noSideEffect.} = + return (this.bits and not signMask) == 0 + +proc signBit(this: Single): int {.noSideEffect.} = + return int((this.bits and signMask) != 0) + +## ================================================================================================== +## Returns floor(x / 2^n). +## +## Technically, right-shift of negative integers is implementation defined... +## Should easily be optimized into SAR (or equivalent) instruction. + +proc floorDivPow2(x: int32; n: int32): int32 {.inline.} = + return x shr n + +## Returns floor(log_10(2^e)) +## static inline int32_t FloorLog10Pow2(int32_t e) +## { +## SF_ASSERT(e >= -1500); +## SF_ASSERT(e <= 1500); +## return FloorDivPow2(e * 1262611, 22); +## } +## Returns floor(log_10(3/4 2^e)) +## static inline int32_t FloorLog10ThreeQuartersPow2(int32_t e) +## { +## SF_ASSERT(e >= -1500); +## SF_ASSERT(e <= 1500); +## return FloorDivPow2(e * 1262611 - 524031, 22); +## } +## Returns floor(log_2(10^e)) + +proc floorLog2Pow10(e: int32): int32 {.inline.} = + sf_Assert(e >= -1233) + sf_Assert(e <= 1233) + return floorDivPow2(e * 1741647, 19) + +proc computePow10Single(k: int32): uint64 {.inline.} = + ## There are unique beta and r such that 10^k = beta 2^r and + ## 2^63 <= beta < 2^64, namely r = floor(log_2 10^k) - 63 and + ## beta = 2^-r 10^k. + ## Let g = ceil(beta), so (g-1) 2^r < 10^k <= g 2^r, with the latter + ## value being a pretty good overestimate for 10^k. + ## NB: Since for all the required exponents k, we have g < 2^64, + ## all constants can be stored in 128-bit integers. + const + kMin: int32 = -31 + kMax: int32 = 45 + g: array[kMax - kMin + 1, uint64] = [0x81CEB32C4B43FCF5'u64, 0xA2425FF75E14FC32'u64, + 0xCAD2F7F5359A3B3F'u64, 0xFD87B5F28300CA0E'u64, 0x9E74D1B791E07E49'u64, + 0xC612062576589DDB'u64, 0xF79687AED3EEC552'u64, 0x9ABE14CD44753B53'u64, + 0xC16D9A0095928A28'u64, 0xF1C90080BAF72CB2'u64, 0x971DA05074DA7BEF'u64, + 0xBCE5086492111AEB'u64, 0xEC1E4A7DB69561A6'u64, 0x9392EE8E921D5D08'u64, + 0xB877AA3236A4B44A'u64, 0xE69594BEC44DE15C'u64, 0x901D7CF73AB0ACDA'u64, + 0xB424DC35095CD810'u64, 0xE12E13424BB40E14'u64, 0x8CBCCC096F5088CC'u64, + 0xAFEBFF0BCB24AAFF'u64, 0xDBE6FECEBDEDD5BF'u64, 0x89705F4136B4A598'u64, + 0xABCC77118461CEFD'u64, 0xD6BF94D5E57A42BD'u64, 0x8637BD05AF6C69B6'u64, + 0xA7C5AC471B478424'u64, 0xD1B71758E219652C'u64, 0x83126E978D4FDF3C'u64, + 0xA3D70A3D70A3D70B'u64, 0xCCCCCCCCCCCCCCCD'u64, 0x8000000000000000'u64, + 0xA000000000000000'u64, 0xC800000000000000'u64, 0xFA00000000000000'u64, + 0x9C40000000000000'u64, 0xC350000000000000'u64, 0xF424000000000000'u64, + 0x9896800000000000'u64, 0xBEBC200000000000'u64, 0xEE6B280000000000'u64, + 0x9502F90000000000'u64, 0xBA43B74000000000'u64, 0xE8D4A51000000000'u64, + 0x9184E72A00000000'u64, 0xB5E620F480000000'u64, 0xE35FA931A0000000'u64, + 0x8E1BC9BF04000000'u64, 0xB1A2BC2EC5000000'u64, 0xDE0B6B3A76400000'u64, + 0x8AC7230489E80000'u64, 0xAD78EBC5AC620000'u64, 0xD8D726B7177A8000'u64, + 0x878678326EAC9000'u64, 0xA968163F0A57B400'u64, 0xD3C21BCECCEDA100'u64, + 0x84595161401484A0'u64, 0xA56FA5B99019A5C8'u64, 0xCECB8F27F4200F3A'u64, + 0x813F3978F8940985'u64, 0xA18F07D736B90BE6'u64, 0xC9F2C9CD04674EDF'u64, + 0xFC6F7C4045812297'u64, 0x9DC5ADA82B70B59E'u64, 0xC5371912364CE306'u64, + 0xF684DF56C3E01BC7'u64, 0x9A130B963A6C115D'u64, 0xC097CE7BC90715B4'u64, + 0xF0BDC21ABB48DB21'u64, 0x96769950B50D88F5'u64, 0xBC143FA4E250EB32'u64, + 0xEB194F8E1AE525FE'u64, 0x92EFD1B8D0CF37BF'u64, 0xB7ABC627050305AE'u64, + 0xE596B7B0C643C71A'u64, 0x8F7E32CE7BEA5C70'u64, 0xB35DBF821AE4F38C'u64] + sf_Assert(k >= kMin) + sf_Assert(k <= kMax) + return g[k - kMin] + +proc lo32(x: uint64): uint32 {.inline.} = + return cast[uint32](x) + +proc hi32(x: uint64): uint32 {.inline.} = + return cast[uint32](x shr 32) + +when defined(sizeof_Int128): + proc roundToOdd(g: uint64; cp: uint32): uint32 {.inline.} = + let p: uint128 = uint128(g) * cp + let y1: uint32 = lo32(cast[uint64](p shr 64)) + let y0: uint32 = hi32(cast[uint64](p)) + return y1 or uint32(y0 > 1) + +elif defined(vcc) and defined(cpu64): + proc roundToOdd(g: uint64; cpHi: uint32): uint32 {.inline.} = + var p1: uint64 = 0 + var p0: uint64 = umul128(g, cpHi, addr(p1)) + let y1: uint32 = lo32(p1) + let y0: uint32 = hi32(p0) + return y1 or uint32(y0 > 1) + +else: + proc roundToOdd(g: uint64; cp: uint32): uint32 {.inline.} = + let b01: uint64 = uint64(lo32(g)) * cp + let b11: uint64 = uint64(hi32(g)) * cp + let hi: uint64 = b11 + hi32(b01) + let y1: uint32 = hi32(hi) + let y0: uint32 = lo32(hi) + return y1 or uint32(y0 > 1) + +## Returns whether value is divisible by 2^e2 + +proc multipleOfPow2(value: uint32; e2: int32): bool {.inline.} = + sf_Assert(e2 >= 0) + sf_Assert(e2 <= 31) + return (value and ((uint32(1) shl e2) - 1)) == 0 + +type + FloatingDecimal32 {.bycopy.} = object + digits: uint32 ## num_digits <= 9 + exponent: int32 + +proc toDecimal32(ieeeSignificand: uint32; ieeeExponent: uint32): FloatingDecimal32 {. + inline.} = + var c: uint32 + var q: int32 + if ieeeExponent != 0: + c = hiddenBit or ieeeSignificand + q = cast[int32](ieeeExponent) - exponentBias + if 0 <= -q and -q < significandSize and multipleOfPow2(c, -q): + return FloatingDecimal32(digits: c shr -q, exponent: 0'i32) + else: + c = ieeeSignificand + q = 1 - exponentBias + let isEven: bool = (c mod 2 == 0) + let lowerBoundaryIsCloser: bool = (ieeeSignificand == 0 and ieeeExponent > 1) + ## const int32_t qb = q - 2; + let cbl: uint32 = 4 * c - 2 + uint32(lowerBoundaryIsCloser) + let cb: uint32 = 4 * c + let cbr: uint32 = 4 * c + 2 + ## (q * 1262611 ) >> 22 == floor(log_10( 2^q)) + ## (q * 1262611 - 524031) >> 22 == floor(log_10(3/4 2^q)) + sf_Assert(q >= -1500) + sf_Assert(q <= 1500) + let k: int32 = floorDivPow2(q * 1262611 - (if lowerBoundaryIsCloser: 524031 else: 0), 22) + let h: int32 = q + floorLog2Pow10(-k) + 1 + sf_Assert(h >= 1) + sf_Assert(h <= 4) + let pow10: uint64 = computePow10Single(-k) + let vbl: uint32 = roundToOdd(pow10, cbl shl h) + let vb: uint32 = roundToOdd(pow10, cb shl h) + let vbr: uint32 = roundToOdd(pow10, cbr shl h) + let lower: uint32 = vbl + uint32(not isEven) + let upper: uint32 = vbr - uint32(not isEven) + ## See Figure 4 in [1]. + ## And the modifications in Figure 6. + let s: uint32 = vb div 4 + ## NB: 4 * s == vb & ~3 == vb & -4 + if s >= 10: + let sp: uint32 = s div 10 + ## = vb / 40 + let upInside: bool = lower <= 40 * sp + let wpInside: bool = 40 * sp + 40 <= upper + ## if (up_inside || wp_inside) // NB: At most one of u' and w' is in R_v. + if upInside != wpInside: + return FloatingDecimal32(digits: sp + uint32(wpInside), exponent: k + 1) + let uInside: bool = lower <= 4 * s + let wInside: bool = 4 * s + 4 <= upper + if uInside != wInside: + return FloatingDecimal32(digits: s + uint32(wInside), exponent: k) + let mid: uint32 = 4 * s + 2 + ## = 2(s + t) + let roundUp: bool = vb > mid or (vb == mid and (s and 1) != 0) + return FloatingDecimal32(digits: s + uint32(roundUp), exponent: k) + +## ================================================================================================== +## ToChars +## ================================================================================================== + +proc utoa2Digits(buf: var openArray[char]; pos: int; digits: uint32) {.inline.} = + const + digits100: array[200, char] = ['0', '0', '0', '1', '0', '2', '0', '3', '0', '4', '0', '5', + '0', '6', '0', '7', '0', '8', '0', '9', '1', '0', '1', '1', '1', '2', '1', '3', '1', '4', + '1', '5', '1', '6', '1', '7', '1', '8', '1', '9', '2', '0', '2', '1', '2', '2', '2', '3', + '2', '4', '2', '5', '2', '6', '2', '7', '2', '8', '2', '9', '3', '0', '3', '1', '3', '2', + '3', '3', '3', '4', '3', '5', '3', '6', '3', '7', '3', '8', '3', '9', '4', '0', '4', '1', + '4', '2', '4', '3', '4', '4', '4', '5', '4', '6', '4', '7', '4', '8', '4', '9', '5', '0', + '5', '1', '5', '2', '5', '3', '5', '4', '5', '5', '5', '6', '5', '7', '5', '8', '5', '9', + '6', '0', '6', '1', '6', '2', '6', '3', '6', '4', '6', '5', '6', '6', '6', '7', '6', '8', + '6', '9', '7', '0', '7', '1', '7', '2', '7', '3', '7', '4', '7', '5', '7', '6', '7', '7', + '7', '8', '7', '9', '8', '0', '8', '1', '8', '2', '8', '3', '8', '4', '8', '5', '8', '6', + '8', '7', '8', '8', '8', '9', '9', '0', '9', '1', '9', '2', '9', '3', '9', '4', '9', '5', + '9', '6', '9', '7', '9', '8', '9', '9'] + sf_Assert(digits <= 99) + buf[pos] = digits100[2 * digits] + buf[pos+1] = digits100[2 * digits + 1] + +proc trailingZeros2Digits(digits: uint32): int32 {.inline.} = + const + trailingZeros100: array[100, int8] = [2'i8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0] + sf_Assert(digits <= 99) + return trailingZeros100[digits] + +proc printDecimalDigitsBackwards(buf: var openArray[char]; pos: int; output: uint32): int32 {.inline.} = + var output = output + var pos = pos + var tz: int32 = 0 + ## number of trailing zeros removed. + var nd: int32 = 0 + ## number of decimal digits processed. + ## At most 9 digits remaining + if output >= 10000: + let q: uint32 = output div 10000 + let r: uint32 = output mod 10000 + output = q + dec(pos, 4) + if r != 0: + let rH: uint32 = r div 100 + let rL: uint32 = r mod 100 + utoa2Digits(buf, pos, rH) + utoa2Digits(buf, pos + 2, rL) + tz = trailingZeros2Digits(if rL == 0: rH else: rL) + (if rL == 0: 2 else: 0) + else: + tz = 4 + nd = 4 + if output >= 100: + let q: uint32 = output div 100 + let r: uint32 = output mod 100 + output = q + dec(pos, 2) + utoa2Digits(buf, pos, r) + if tz == nd: + inc(tz, trailingZeros2Digits(r)) + inc(nd, 2) + if output >= 100: + let q2: uint32 = output div 100 + let r2: uint32 = output mod 100 + output = q2 + dec(pos, 2) + utoa2Digits(buf, pos, r2) + if tz == nd: + inc(tz, trailingZeros2Digits(r2)) + inc(nd, 2) + sf_Assert(output >= 1) + sf_Assert(output <= 99) + if output >= 10: + let q: uint32 = output + dec(pos, 2) + utoa2Digits(buf, pos, q) + if tz == nd: + inc(tz, trailingZeros2Digits(q)) + else: + let q: uint32 = output + sf_Assert(q >= 1) + sf_Assert(q <= 9) + dec(pos) + buf[pos] = chr(uint32('0') + q) + return tz + +proc decimalLength(v: uint32): int32 {.inline.} = + sf_Assert(v >= 1) + sf_Assert(v <= 999999999'u) + if v >= 100000000'u: + return 9 + if v >= 10000000'u: + return 8 + if v >= 1000000'u: + return 7 + if v >= 100000'u: + return 6 + if v >= 10000'u: + return 5 + if v >= 1000'u: + return 4 + if v >= 100'u: + return 3 + if v >= 10'u: + return 2 + return 1 + +proc formatDigits(buffer: var openArray[char]; pos: int; digits: uint32; decimalExponent: int32; + forceTrailingDotZero: bool = false): int {.inline.} = + const + minFixedDecimalPoint: int32 = -4 + maxFixedDecimalPoint: int32 = 9 + var pos = pos + assert(minFixedDecimalPoint <= -1, "internal error") + assert(maxFixedDecimalPoint >= 1, "internal error") + sf_Assert(digits >= 1) + sf_Assert(digits <= 999999999'u) + sf_Assert(decimalExponent >= -99) + sf_Assert(decimalExponent <= 99) + var numDigits: int32 = decimalLength(digits) + let decimalPoint: int32 = numDigits + decimalExponent + let useFixed: bool = minFixedDecimalPoint <= decimalPoint and + decimalPoint <= maxFixedDecimalPoint + ## Prepare the buffer. + ## Avoid calling memset/memcpy with variable arguments below... + for i in 0..<32: buffer[pos+i] = '0' + assert(minFixedDecimalPoint >= -30, "internal error") + assert(maxFixedDecimalPoint <= 32, "internal error") + var decimalDigitsPosition: int32 + if useFixed: + if decimalPoint <= 0: + ## 0.[000]digits + decimalDigitsPosition = 2 - decimalPoint + else: + ## dig.its + ## digits[000] + decimalDigitsPosition = 0 + else: + ## dE+123 or d.igitsE+123 + decimalDigitsPosition = 1 + var digitsEnd = pos + decimalDigitsPosition + numDigits + let tz: int32 = printDecimalDigitsBackwards(buffer, digitsEnd, digits) + dec(digitsEnd, tz) + dec(numDigits, tz) + ## decimal_exponent += tz; // => decimal_point unchanged. + if useFixed: + if decimalPoint <= 0: + ## 0.[000]digits + buffer[pos+1] = '.' + pos = digitsEnd + elif decimalPoint < numDigits: + ## dig.its + for i in 0..<8: + buffer[i + decimalPoint + 1] = buffer[i + decimalPoint] + buffer[pos+decimalPoint] = '.' + pos = digitsEnd + 1 + else: + ## digits[000] + inc(pos, decimalPoint) + if forceTrailingDotZero: + buffer[pos] = '.' + buffer[pos+1] = '0' + inc(pos, 2) + else: + buffer[pos] = buffer[pos+1] + if numDigits == 1: + ## dE+123 + inc(pos) + else: + ## d.igitsE+123 + buffer[pos+1] = '.' + pos = digitsEnd + let scientificExponent: int32 = decimalPoint - 1 + ## SF_ASSERT(scientific_exponent != 0); + buffer[pos] = 'e' + buffer[pos+1] = if scientificExponent < 0: '-' else: '+' + inc(pos, 2) + let k: uint32 = cast[uint32](if scientificExponent < 0: -scientificExponent else: scientificExponent) + if k < 10: + buffer[pos] = chr(uint32('0') + k) + inc pos + else: + utoa2Digits(buffer, pos, k) + inc(pos, 2) + return pos + +proc float32ToChars*(buffer: var openArray[char]; v: float32; forceTrailingDotZero = false): int {. + inline.} = + let significand: uint32 = physicalSignificand(constructSingle(v)) + let exponent: uint32 = physicalExponent(constructSingle(v)) + var pos = 0 + if exponent != maxIeeeExponent: + ## Finite + buffer[pos] = '-' + inc(pos, signBit(constructSingle(v))) + if exponent != 0 or significand != 0: + ## != 0 + let dec: auto = toDecimal32(significand, exponent) + return formatDigits(buffer, pos, dec.digits, dec.exponent, forceTrailingDotZero) + else: + buffer[pos] = '0' + buffer[pos+1] = '.' + buffer[pos+2] = '0' + buffer[pos+3] = ' ' + inc(pos, if forceTrailingDotZero: 3 else: 1) + return pos + if significand == 0: + buffer[pos] = '-' + inc(pos, signBit(constructSingle(v))) + buffer[pos] = 'i' + buffer[pos+1] = 'n' + buffer[pos+2] = 'f' + buffer[pos+3] = ' ' + return pos + 3 + else: + buffer[pos] = 'n' + buffer[pos+1] = 'a' + buffer[pos+2] = 'n' + buffer[pos+3] = ' ' + return pos + 3 diff --git a/lib/system/strmantle.nim b/lib/system/strmantle.nim index 42ea9d226..1a32a4afe 100644 --- a/lib/system/strmantle.nim +++ b/lib/system/strmantle.nim @@ -164,6 +164,19 @@ proc nimFloatToStr(f: float): string {.compilerproc.} = result = newStringOfCap(8) result.addFloat f +when not defined(nimLegacyAddFloat) and not defined(nimscript) and + not defined(js) and defined(nimHasDragonBox): + import schubfach + +proc nimFloat32ToStr(f: float32): string {.compilerproc.} = + when declared(float32ToChars): + result = newString(65) + let L = float32ToChars(result, f, forceTrailingDotZero=true) + setLen(result, L) + else: + result = newStringOfCap(8) + result.addFloat f + proc c_strtod(buf: cstring, endptr: ptr cstring): float64 {. importc: "strtod", header: "", noSideEffect.} diff --git a/tests/float/tfloat6.nim b/tests/float/tfloat6.nim index c0e6e419f..fa8fc9da8 100644 --- a/tests/float/tfloat6.nim +++ b/tests/float/tfloat6.nim @@ -21,3 +21,8 @@ echo "0.00000_1".parseFloat(), " : ", 1E-6 echo "1_0.00_0001".parseFloat(), " : ", 10.000001 echo "1__00.00_0001".parseFloat(), " : ", 1_00.000001 + +# bug #18148 + +var a = 1.1'f32 +doAssert $a == "1.1", $a # fails From 282d61bafa4f1b06e1328cf8e517f556fb86d592 Mon Sep 17 00:00:00 2001 From: Araq Date: Thu, 3 Jun 2021 14:00:53 +0200 Subject: [PATCH 2171/3768] added lib/deps.txt --- lib/deps.txt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 lib/deps.txt diff --git a/lib/deps.txt b/lib/deps.txt new file mode 100644 index 000000000..2a44b5fe2 --- /dev/null +++ b/lib/deps.txt @@ -0,0 +1,14 @@ +# Dragonbox.nim and Schubfach.nim + +"Dragonbox" is Nim's "float64 to string" algorithm. +"Schubfach" is Nim's "float32 to string" algorithm. These are based on + +https://github.com/abolz/Drachennest/blob/master/src/dragonbox.cc +https://github.com/abolz/Drachennest/blob/master/src/schubfach_32.cc + +commit e6714a39ad331b4489d0b6aaf3968635bff4eb5e + +The `.cc` files were translated by c2nim via `--cpp --keepBodies --nep1` +and then modified to remove the unsafe code. + +We used c2nim as of commit f0469c909d9e2e28d59687e394bf5ac862f561b6. From 9df631a379998f554748697da7fe95ce4463cb83 Mon Sep 17 00:00:00 2001 From: n5m <72841454+n5m@users.noreply.github.com> Date: Thu, 3 Jun 2021 12:25:52 +0000 Subject: [PATCH 2172/3768] reuse algorithm.fill while building SkipTable (#18138) * reuse algorithm.fill while building SkipTable * Update lib/pure/strutils.nim Co-authored-by: Timothee Cour Co-authored-by: Timothee Cour --- lib/pure/strutils.nim | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 3fb67ead1..8e0a6158b 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -72,7 +72,7 @@ runnableExamples: import parseutils from math import pow, floor, log10 -from algorithm import reverse +from algorithm import fill, reverse import std/enumutils from unicode import toLower, toUpper @@ -1779,17 +1779,7 @@ func initSkipTable*(a: var SkipTable, sub: string) {.rtl, extern: "nsuInitSkipTable".} = ## Preprocess table `a` for `sub`. let m = len(sub) - var i = 0 - while i <= 0xff-7: - a[chr(i + 0)] = m - a[chr(i + 1)] = m - a[chr(i + 2)] = m - a[chr(i + 3)] = m - a[chr(i + 4)] = m - a[chr(i + 5)] = m - a[chr(i + 6)] = m - a[chr(i + 7)] = m - i += 8 + fill(a, m) for i in 0 ..< m - 1: a[sub[i]] = m - 1 - i From 3e57c2f7800dbedd0df735a0236f09410f6db11a Mon Sep 17 00:00:00 2001 From: Artem Klevtsov Date: Thu, 3 Jun 2021 19:41:57 +0700 Subject: [PATCH 2173/3768] Improve db_postgres iterators (#18144) * Fix pqSetSingleRowMode case. Add links to the docs * Add missing PGContextVisibility enum * Remove unused PGContextVisibility enum * Improve db_postgres iterators * Fix instantRows with DbColumns. Cosmetics. * Reduce copy&paste in db_postgres * Move pqclear inside loop --- lib/impure/db_postgres.nim | 179 ++++++++++++++++++++++++------------- 1 file changed, 117 insertions(+), 62 deletions(-) diff --git a/lib/impure/db_postgres.nim b/lib/impure/db_postgres.nim index 4edeac2fc..36e035d3d 100644 --- a/lib/impure/db_postgres.nim +++ b/lib/impure/db_postgres.nim @@ -96,7 +96,6 @@ type ## converted to nil. InstantRow* = object ## a handle that can be res: PPGresult ## used to get a row's - line: int ## column text on demand SqlPrepared* = distinct string ## a identifier for the prepared queries proc dbError*(db: DbConn) {.noreturn.} = @@ -184,6 +183,22 @@ proc setupQuery(db: DbConn, stmtName: SqlPrepared, deallocCStringArray(arr) if pqResultStatus(result) != PGRES_TUPLES_OK: dbError(db) +proc setupSingeRowQuery(db: DbConn, query: SqlQuery, + args: varargs[string]) = + if pqsendquery(db, dbFormat(query, args)) != 1: + dbError(db) + if pqSetSingleRowMode(db) != 1: + dbError(db) + +proc setupSingeRowQuery(db: DbConn, stmtName: SqlPrepared, + args: varargs[string]) = + var arr = allocCStringArray(args) + if pqsendqueryprepared(db, stmtName.string, int32(args.len), arr, nil, nil, 0) != 1: + dbError(db) + if pqSetSingleRowMode(db) != 1: + dbError(db) + deallocCStringArray(arr) + proc prepare*(db: DbConn; stmtName: string, query: SqlQuery; nParams: int): SqlPrepared = ## Creates a new `SqlPrepared` statement. Parameter substitution is done @@ -203,49 +218,70 @@ proc setRow(res: PPGresult, r: var Row, line, cols: int32) = else: add(r[col], x) +template fetchRows(db: DbConn): untyped = + var res: PPGresult = nil + while true: + res = pqgetresult(db) + if res == nil: + break + let status = pqresultStatus(res) + if status == PGRES_TUPLES_OK: + discard + elif status != PGRES_SINGLE_TUPLE: + dbError(db) + else: + let L = pqNfields(res) + var result = newRow(L) + setRow(res, result, 0, L) + yield result + pqclear(res) + iterator fastRows*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} = ## executes the query and iterates over the result dataset. This is very ## fast, but potentially dangerous: If the for-loop-body executes another ## query, the results can be undefined. For Postgres it is safe though. - var res = setupQuery(db, query, args) - var L = pqnfields(res) - var result = newRow(L) - for i in 0'i32..pqntuples(res)-1: - setRow(res, result, i, L) - yield result - pqclear(res) + setupSingeRowQuery(db, query, args) + fetchRows(db) iterator fastRows*(db: DbConn, stmtName: SqlPrepared, args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} = - ## executes the prepared query and iterates over the result dataset. - var res = setupQuery(db, stmtName, args) - var L = pqNfields(res) - var result = newRow(L) - for i in 0'i32..pqNtuples(res)-1: - setRow(res, result, i, L) - yield result - pqClear(res) + ## executes the query and iterates over the result dataset. This is very + ## fast, but potentially dangerous: If the for-loop-body executes another + ## query, the results can be undefined. For Postgres it is safe though. + setupSingeRowQuery(db, stmtName, args) + fetchRows(db) + +template fetchinstantRows(db: DbConn): untyped = + var res: PPGresult = nil + while true: + res = pqgetresult(db) + if res == nil: + break + let status = pqresultStatus(res) + if status == PGRES_TUPLES_OK: + discard + elif status != PGRES_SINGLE_TUPLE: + dbError(db) + else: + yield InstantRow(res: res) + pqclear(res) iterator instantRows*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): InstantRow {.tags: [ReadDbEffect].} = ## same as fastRows but returns a handle that can be used to get column text ## on demand using []. Returned handle is valid only within iterator body. - var res = setupQuery(db, query, args) - for i in 0'i32..pqNtuples(res)-1: - yield InstantRow(res: res, line: i) - pqClear(res) + setupSingeRowQuery(db, query, args) + fetchinstantRows(db) iterator instantRows*(db: DbConn, stmtName: SqlPrepared, args: varargs[string, `$`]): InstantRow {.tags: [ReadDbEffect].} = ## same as fastRows but returns a handle that can be used to get column text ## on demand using []. Returned handle is valid only within iterator body. - var res = setupQuery(db, stmtName, args) - for i in 0'i32..pqNtuples(res)-1: - yield InstantRow(res: res, line: i) - pqClear(res) + setupSingeRowQuery(db, stmtName, args) + fetchinstantRows(db) proc getColumnType(res: PPGresult, col: int) : DbType = ## returns DbType for given column in the row @@ -382,7 +418,7 @@ proc getColumnType(res: PPGresult, col: int) : DbType = of 705: return DbType(kind: DbTypeKind.dbUnknown, name: "unknown") else: return DbType(kind: DbTypeKind.dbUnknown, name: $oid) ## Query the system table pg_type to determine exactly which type is referenced. -proc setColumnInfo(columns: var DbColumns; res: PPGresult; L: int32) = +proc setColumnInfo(columns: var DbColumns; res: PPGresult, L: int32) = setLen(columns, L) for i in 0'i32.. 0: setRow(res, result, 0, L) pqclear(res) +proc getRow*(db: DbConn, query: SqlQuery, + args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} = + ## retrieves a single row. If the query doesn't return any rows, this proc + ## will return a Row with empty strings for each column. + let res = setupQuery(db, query, args) + getRow(res) + proc getRow*(db: DbConn, stmtName: SqlPrepared, args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} = - var res = setupQuery(db, stmtName, args) - var L = pqNfields(res) - result = newRow(L) - if pqntuples(res) > 0: - setRow(res, result, 0, L) - pqClear(res) + let res = setupQuery(db, stmtName, args) + getRow(res) + +proc getAllRows(res: PPGresult): seq[Row] = + let N = pqntuples(res) + let L = pqnfields(res) + result = newSeqOfCap[Row](N) + var row = newRow(L) + for i in 0'i32..N-1: + setRow(res, row, i, L) + result.add(row) + pqclear(res) proc getAllRows*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): seq[Row] {. tags: [ReadDbEffect].} = ## executes the query and returns the whole result dataset. - result = @[] - for r in fastRows(db, query, args): - result.add(r) + let res = setupQuery(db, query, args) + getAllRows(res) proc getAllRows*(db: DbConn, stmtName: SqlPrepared, args: varargs[string, `$`]): seq[Row] {.tags: [ReadDbEffect].} = ## executes the prepared query and returns the whole result dataset. - result = @[] - for r in fastRows(db, stmtName, args): - result.add(r) + let res = setupQuery(db, stmtName, args) + getAllRows(res) iterator rows*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} = @@ -459,18 +515,21 @@ iterator rows*(db: DbConn, stmtName: SqlPrepared, ## same as `fastRows`, but slower and safe. for r in items(getAllRows(db, stmtName, args)): yield r +proc getValue(res: PPGresult): string = + if pqntuples(res) > 0: + var x = pqgetvalue(res, 0, 0) + result = if isNil(x): "" else: $x + else: + result = "" + proc getValue*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): string {. tags: [ReadDbEffect].} = ## executes the query and returns the first column of the first row of the ## result dataset. Returns "" if the dataset contains no rows or the database ## value is NULL. - var res = setupQuery(db, query, args) - if pqntuples(res) > 0: - var x = pqgetvalue(res, 0, 0) - result = if isNil(x): "" else: $x - else: - result = "" + let res = setupQuery(db, query, args) + getValue(res) proc getValue*(db: DbConn, stmtName: SqlPrepared, args: varargs[string, `$`]): string {. @@ -478,12 +537,8 @@ proc getValue*(db: DbConn, stmtName: SqlPrepared, ## executes the query and returns the first column of the first row of the ## result dataset. Returns "" if the dataset contains no rows or the database ## value is NULL. - var res = setupQuery(db, stmtName, args) - if pqntuples(res) > 0: - var x = pqgetvalue(res, 0, 0) - result = if isNil(x): "" else: $x - else: - result = "" + let res = setupQuery(db, stmtName, args) + getValue(res) proc tryInsertID*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): int64 {. From 7ef364a402d3d827f10c893280f8dc7b9ef056f5 Mon Sep 17 00:00:00 2001 From: narimiran Date: Thu, 3 Jun 2021 15:20:42 +0200 Subject: [PATCH 2174/3768] add missing import to asynchttpserver's example --- lib/pure/asynchttpserver.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index fd0cad5ca..8cea7f162 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -18,7 +18,7 @@ runnableExamples("-r:off"): # This example will create an HTTP server on an automatically chosen port. # It will respond to all requests with a `200 OK` response code and "Hello World" # as the response body. - import std/asyncdispatch + import std/asyncdispatch, asynchttpserver proc main {.async.} = var server = newAsyncHttpServer() proc cb(req: Request) {.async.} = From 0aa8b793a5f5ef58afe5e8aa96b7646a8afde765 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 3 Jun 2021 16:27:34 +0200 Subject: [PATCH 2175/3768] clarify what a 'monotonic' timestamp is (#18163) --- lib/std/monotimes.nim | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/std/monotimes.nim b/lib/std/monotimes.nim index 8f6aa5b66..3bb4fa141 100644 --- a/lib/std/monotimes.nim +++ b/lib/std/monotimes.nim @@ -10,7 +10,7 @@ ##[ The `std/monotimes` module implements monotonic timestamps. A monotonic timestamp represents the time that has passed since some system defined -point in time. The monotonic timestamps are guaranteed to always increase, +point in time. The monotonic timestamps are guaranteed not to decrease, meaning that that the following is guaranteed to work: ]## @@ -18,9 +18,8 @@ runnableExamples: import std/os let a = getMonoTime() - sleep(10) let b = getMonoTime() - assert a < b + assert a <= b ##[ This is not guaranteed for the `times.Time` type! This means that the From d31cbfd167c0bf2fcf2b1e6d401a20a16acaaebb Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 3 Jun 2021 22:44:11 +0800 Subject: [PATCH 2176/3768] Revert "add missing import to asynchttpserver's example" (#18164) This reverts commit 7ef364a402d3d827f10c893280f8dc7b9ef056f5. --- lib/pure/asynchttpserver.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index 8cea7f162..fd0cad5ca 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -18,7 +18,7 @@ runnableExamples("-r:off"): # This example will create an HTTP server on an automatically chosen port. # It will respond to all requests with a `200 OK` response code and "Hello World" # as the response body. - import std/asyncdispatch, asynchttpserver + import std/asyncdispatch proc main {.async.} = var server = newAsyncHttpServer() proc cb(req: Request) {.async.} = From 06232b7f2e230a46beea982f172087e9da51814a Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 3 Jun 2021 17:12:45 +0200 Subject: [PATCH 2177/3768] fixes #18058 (#18162) --- compiler/commands.nim | 2 ++ compiler/msgs.nim | 12 ++++-------- compiler/options.nim | 1 + doc/advopt.txt | 2 ++ tests/misc/trunner.nim | 4 ++-- tests/osproc/treadlines.nim | 4 ++-- 6 files changed, 13 insertions(+), 12 deletions(-) diff --git a/compiler/commands.nim b/compiler/commands.nim index 605e2930c..994b224f1 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -919,6 +919,8 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; excl(conf.notes, hintProcessing) excl(conf.mainPackageNotes, hintProcessing) else: localError(conf, info, "expected: dots|filenames|off, got: $1" % arg) + of "unitsep": + conf.unitSep = if switchOn(arg): "\31" else: "" of "listfullpaths": # xxx in future work, use `warningDeprecated` conf.filenameOption = if switchOn(arg): foAbs else: foCanonical diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 6a8c45db5..053b5c928 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -289,10 +289,6 @@ proc `??`* (conf: ConfigRef; info: TLineInfo, filename: string): bool = # only for debugging purposes result = filename in toFilename(conf, info) -const - UnitSep = "\31" - # this needs care to avoid issues similar to https://github.com/nim-lang/Nim/issues/17853 - type MsgFlag* = enum ## flags altering msgWriteln behavior msgStdout, ## force writing to stdout, even stderr is default @@ -309,7 +305,7 @@ proc msgWriteln*(conf: ConfigRef; s: string, flags: MsgFlags = {}) = ## This is used for 'nim dump' etc. where we don't have nimsuggest ## support. #if conf.cmd == cmdIdeTools and optCDebug notin gGlobalOptions: return - let sep = if msgNoUnitSep notin flags: UnitSep else: "" + let sep = if msgNoUnitSep notin flags: conf.unitSep else: "" if not isNil(conf.writelnHook) and msgSkipHook notin flags: conf.writelnHook(s & sep) elif optStdout in conf.globalOptions or msgStdout in flags: @@ -409,7 +405,7 @@ proc quit(conf: ConfigRef; msg: TMsgKind) {.gcsafe.} = styledMsgWriteln(fgRed, """ No stack traceback available To create a stacktrace, rerun compilation with './koch temp $1 ', see $2 for details""" % - [conf.command, "intern.html#debugging-the-compiler".createDocLink], UnitSep) + [conf.command, "intern.html#debugging-the-compiler".createDocLink], conf.unitSep) quit 1 proc handleError(conf: ConfigRef; msg: TMsgKind, eh: TErrorHandling, s: string) = @@ -550,13 +546,13 @@ proc liMessage*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string, msgWrite(conf, ".") else: styledMsgWriteln(styleBright, loc, resetStyle, color, title, resetStyle, s, KindColor, kindmsg, - resetStyle, conf.getSurroundingSrc(info), UnitSep) + resetStyle, conf.getSurroundingSrc(info), conf.unitSep) if hintMsgOrigin in conf.mainPackageNotes: # xxx needs a bit of refactoring to honor `conf.filenameOption` styledMsgWriteln(styleBright, toFileLineCol(info2), resetStyle, " compiler msg initiated here", KindColor, KindFormat % $hintMsgOrigin, - resetStyle, UnitSep) + resetStyle, conf.unitSep) handleError(conf, msg, eh, s) template rawMessage*(conf: ConfigRef; msg: TMsgKind, args: openArray[string]) = diff --git a/compiler/options.nim b/compiler/options.nim index 9cbb747c9..e6c667d92 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -283,6 +283,7 @@ type arcToExpand*: StringTableRef m*: MsgConfig filenameOption*: FilenameOption # how to render paths in compiler messages + unitSep*: string evalTemplateCounter*: int evalMacroCounter*: int exitcode*: int8 diff --git a/doc/advopt.txt b/doc/advopt.txt index ea3370880..e627d4768 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -41,6 +41,8 @@ Advanced options: defaults to `abs` (absolute) --processing:dots|filenames|off show files as they're being processed by nim compiler + --unitsep:on|off use the ASCII unit separator (31) between error + messages, useful for IDE-like tooling --declaredLocs:on|off show declaration locations in messages --spellSuggest|:num show at most `num >= 0` spelling suggestions on typos. if `num` is not specified (or `auto`), return diff --git a/tests/misc/trunner.nim b/tests/misc/trunner.nim index 3297b3a24..b6b302836 100644 --- a/tests/misc/trunner.nim +++ b/tests/misc/trunner.nim @@ -249,7 +249,7 @@ tests/newconfig/bar/mfoo.nims""".splitLines var expected = "" for a in files: let b = dir / a - expected.add &"Hint: used config file '{b}' [Conf]\31\n" + expected.add &"Hint: used config file '{b}' [Conf]\n" doAssert outp.endsWith expected, outp & "\n" & expected block: # mfoo2.customext @@ -257,7 +257,7 @@ tests/newconfig/bar/mfoo.nims""".splitLines let cmd = fmt"{nim} e --hint:conf {filename}" let (outp, exitCode) = execCmdEx(cmd, options = {poStdErrToStdOut}) doAssert exitCode == 0 - var expected = &"Hint: used config file '{filename}' [Conf]\31\n" + var expected = &"Hint: used config file '{filename}' [Conf]\n" doAssert outp.endsWith "123" & "\n" & expected diff --git a/tests/osproc/treadlines.nim b/tests/osproc/treadlines.nim index 200a7c299..bb6a7f129 100644 --- a/tests/osproc/treadlines.nim +++ b/tests/osproc/treadlines.nim @@ -1,7 +1,7 @@ discard """ output: ''' -Error: cannot open 'a.nim'\31 -Error: cannot open 'b.nim'\31 +Error: cannot open 'a.nim' +Error: cannot open 'b.nim' ''' targets: "c" """ From 28f2abe1a20a7f7f758b42c233deb7daa78406b1 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 3 Jun 2021 20:55:41 +0200 Subject: [PATCH 2178/3768] fixes #18112 (#18165) --- compiler/docgen.nim | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 18c568ba0..dc2a63f48 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -20,7 +20,7 @@ import from uri import encodeUrl from std/private/globs import nativeToUnixPath - +from nodejs import findNodeJs const exportSection = skField @@ -430,7 +430,9 @@ proc runAllExamples(d: PDoc) = "rdoccmd", group.rdoccmd, "docCmd", group.docCmd, ] - if os.execShellCmd(cmd) != 0: + if d.conf.backend == backendJs and findNodeJs() == "": + discard "ignore JS runnableExample" + elif os.execShellCmd(cmd) != 0: d.conf.quitOrRaise "[runnableExamples] failed: generated file: '$1' group: '$2' cmd: $3" % [outp.string, group[].prettyString, cmd] else: # keep generated source file `outp` to allow inspection. From d91d78f8aed2b1b59effa3b98765aaf675d7e4c3 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 3 Jun 2021 13:25:36 -0700 Subject: [PATCH 2179/3768] changelog for --unitsep (#18167) --- changelog.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index a23aedbdf..06251abfc 100644 --- a/changelog.md +++ b/changelog.md @@ -61,9 +61,6 @@ - Removed `.travis.yml`, `appveyor.yml.disabled`, `.github/workflows/ci.yml.disabled`. -- Nim compiler now adds ASCII unit separator `\31` before a newline for every generated - message (potentially multiline), so tooling can tell when messages start and end. - - `random.initRand(seed)` now produces non-skewed values for the 1st call to `rand()` after initialization with a small (< 30000) seed. Use `-d:nimLegacyRandomInitRand` to restore previous behavior for a transition time, see PR #17467. @@ -395,6 +392,9 @@ - Added `--processing:dots|filenames|off` which customizes `hintProcessing` +- Added `--unitsep:on|off` to control whether to add ASCII unit separator `\31` before a newline + for every generated message (potentially multiline), so tooling can tell when messages start and end. + - Source+Edit links now appear on top of every docgen'd page when `nim doc --git.url:url ...` is given. From 654a20166eabb614cbf0379c67115981f0532079 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 3 Jun 2021 13:29:45 -0700 Subject: [PATCH 2180/3768] simplify extccomp.nim json logic via jsonutils; fix #18084 (#18100) * simplify extccomp.nim json logic via jsonutils * fix #18084 * simplify further * workaround for bootstrap that can be removed after updating csources_v1 >= 1.2 --- compiler/extccomp.nim | 256 +++++++++++++----------------------------- compiler/nimconf.nim | 11 +- compiler/options.nim | 4 +- lib/std/jsonutils.nim | 18 ++- 4 files changed, 99 insertions(+), 190 deletions(-) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 1cea9edeb..ea2b09866 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -14,7 +14,7 @@ import ropes, platform, condsyms, options, msgs, lineinfos, pathutils -import os, strutils, osproc, std/sha1, streams, sequtils, times, strtabs, json +import std/[os, strutils, osproc, sha1, streams, sequtils, times, strtabs, json, jsonutils, sugar] type TInfoCCProp* = enum # properties of the C compiler: @@ -942,195 +942,91 @@ proc jsonBuildInstructionsFile*(conf: ConfigRef): AbsoluteFile = # works out of the box with `hashMainCompilationParams`. result = getNimcacheDir(conf) / conf.outFile.changeFileExt("json") +const cacheVersion = "D20210525T193831" # update when `BuildCache` spec changes +type BuildCache = object + cacheVersion: string + outputFile: string + compile: seq[(string, string)] + link: seq[string] + linkcmd: string + extraCmds: seq[string] + configFiles: seq[string] # the hash shouldn't be needed + stdinInput: bool + projectIsCmd: bool + cmdInput: string + currentDir: string + cmdline: string + depfiles: seq[(string, string)] + nimexe: string + proc writeJsonBuildInstructions*(conf: ConfigRef) = - # xxx use std/json instead, will result in simpler, more maintainable code. - template lit(x: string) = f.write x - template str(x: string) = - buf.setLen 0 - escapeJson(x, buf) - f.write buf - - proc cfiles(conf: ConfigRef; f: File; buf: var string; clist: CfileList, isExternal: bool) = - var comma = false - for i, it in clist: - if CfileFlag.Cached in it.flags: continue - let compileCmd = getCompileCFileCmd(conf, it) - if comma: lit ",\L" else: comma = true - lit "[" - str it.cname.string - lit ", " - str compileCmd - lit "]" - - proc linkfiles(conf: ConfigRef; f: File; buf, objfiles: var string; clist: CfileList; - llist: seq[string]) = - var pastStart = false - template impl(path) = - let path2 = quoteShell(path) - objfiles.add(' ') - objfiles.add(path2) - if pastStart: lit ",\L" - str path2 - pastStart = true - for it in llist: - let objfile = if noAbsolutePaths(conf): it.extractFilename else: it - impl(addFileExt(objfile, CC[conf.cCompiler].objExt)) - for it in clist: - impl(it.obj) - lit "\L" - - proc depfiles(conf: ConfigRef; f: File; buf: var string) = - var i = 0 - for it in conf.m.fileInfos: + var linkFiles = collect(for it in conf.externalToLink: + var it = it + if conf.noAbsolutePaths: it = it.extractFilename + it.addFileExt(CC[conf.cCompiler].objExt)) + for it in conf.toCompile: linkFiles.add it.obj.string + var bcache = BuildCache( + cacheVersion: cacheVersion, + outputFile: conf.absOutFile.string, + compile: collect(for i, it in conf.toCompile: + if CfileFlag.Cached notin it.flags: (it.cname.string, getCompileCFileCmd(conf, it))), + link: linkFiles, + linkcmd: getLinkCmd(conf, conf.absOutFile, linkFiles.quoteShellCommand), + extraCmds: getExtraCmds(conf, conf.absOutFile), + stdinInput: conf.projectIsStdin, + projectIsCmd: conf.projectIsCmd, + cmdInput: conf.cmdInput, + configFiles: conf.configFiles.mapIt(it.string), + currentDir: getCurrentDir()) + if optRun in conf.globalOptions or isDefined(conf, "nimBetterRun"): + bcache.cmdline = conf.commandLine + bcache.depfiles = collect(for it in conf.m.fileInfos: let path = it.fullPath.string if isAbsolute(path): # TODO: else? - if i > 0: lit "],\L" - lit "[" - str path - lit ", " - str $secureHashFile(path) - inc i - lit "]\L" - - - var buf = newStringOfCap(50) - let jsonFile = conf.jsonBuildInstructionsFile - conf.jsonBuildFile = jsonFile - let output = conf.absOutFile - - var f: File - if open(f, jsonFile.string, fmWrite): - lit "{\L" - lit "\"outputFile\": " - str $output - - lit ",\L\"compile\":[\L" - cfiles(conf, f, buf, conf.toCompile, false) - lit "],\L\"link\":[\L" - var objfiles = "" - # XXX add every file here that is to link - linkfiles(conf, f, buf, objfiles, conf.toCompile, conf.externalToLink) - - lit "],\L\"linkcmd\": " - str getLinkCmd(conf, output, objfiles) - - lit ",\L\"extraCmds\": " - lit $(%* getExtraCmds(conf, conf.absOutFile)) - - lit ",\L\"stdinInput\": " - lit $(%* conf.projectIsStdin) - lit ",\L\"projectIsCmd\": " - lit $(%* conf.projectIsCmd) - lit ",\L\"cmdInput\": " - lit $(%* conf.cmdInput) - lit ",\L\"currentDir\": " - lit $(%* getCurrentDir()) - - if optRun in conf.globalOptions or isDefined(conf, "nimBetterRun"): - lit ",\L\"cmdline\": " - str conf.commandLine - lit ",\L\"depfiles\":[\L" - depfiles(conf, f, buf) - lit "],\L\"nimexe\": \L" - str hashNimExe() - lit "\L" - - lit "\L}\L" - close(f) + (path, $secureHashFile(path))) + bcache.nimexe = hashNimExe() + conf.jsonBuildFile = conf.jsonBuildInstructionsFile + conf.jsonBuildFile.string.writeFile(bcache.toJson.pretty) proc changeDetectedViaJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile): bool = - if not fileExists(jsonFile): return true - if not fileExists(conf.absOutFile): return true - result = false - try: - let data = json.parseFile(jsonFile.string) - for key in "depfiles cmdline stdinInput currentDir".split: - if not data.hasKey(key): return true - if getCurrentDir() != data["currentDir"].getStr: - # fixes bug #16271 - # Note that simply comparing `expandFilename(projectFile)` would - # not be sufficient in case other flags depend implicitly on `getCurrentDir`, - # and would require much more care. Simply re-compiling is safer for now. - # A better strategy for future work would be to cache (with an LRU cache) - # the N most recent unique build instructions, as done with `rdmd`, - # which is both robust and avoids recompilation when switching back and forth - # between projects, see https://github.com/timotheecour/Nim/issues/199 - return true - let oldCmdLine = data["cmdline"].getStr - if conf.commandLine != oldCmdLine: - return true - if hashNimExe() != data["nimexe"].getStr: - return true - let stdinInput = data["stdinInput"].getBool - let projectIsCmd = data["projectIsCmd"].getBool - if conf.projectIsStdin or stdinInput: - # could optimize by returning false if stdin input was the same, - # but I'm not sure how to get full stding input - return true - - if conf.projectIsCmd or projectIsCmd: - if not (conf.projectIsCmd and projectIsCmd): return true - if not data.hasKey("cmdInput"): return true - let cmdInput = data["cmdInput"].getStr - if cmdInput != conf.cmdInput: return true - - let depfilesPairs = data["depfiles"] - doAssert depfilesPairs.kind == JArray - for p in depfilesPairs: - doAssert p.kind == JArray - # >= 2 for forwards compatibility with potential later .json files: - doAssert p.len >= 2 - let depFilename = p[0].getStr - let oldHashValue = p[1].getStr - let newHashValue = $secureHashFile(depFilename) - if oldHashValue != newHashValue: - return true + if not fileExists(jsonFile) or not fileExists(conf.absOutFile): return true + var bcache: BuildCache + try: bcache.fromJson(jsonFile.string.parseFile) except IOError, OSError, ValueError: - echo "Warning: JSON processing failed: ", getCurrentExceptionMsg() - result = true + stderr.write "Warning: JSON processing failed for $#: $#\n" % [jsonFile.string, getCurrentExceptionMsg()] + return true + if bcache.currentDir != getCurrentDir() or # fixes bug #16271 + bcache.configFiles != conf.configFiles.mapIt(it.string) or + bcache.cacheVersion != cacheVersion or bcache.outputFile != conf.absOutFile.string or + bcache.cmdline != conf.commandLine or bcache.nimexe != hashNimExe() or + bcache.projectIsCmd != conf.projectIsCmd or conf.cmdInput != bcache.cmdInput: return true + if bcache.stdinInput or conf.projectIsStdin: return true + # xxx optimize by returning false if stdin input was the same + for (file, hash) in bcache.depfiles: + if $secureHashFile(file) != hash: return true proc runJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile) = - try: - let data = json.parseFile(jsonFile.string) - let output = data["outputFile"].getStr - createDir output.parentDir - let outputCurrent = $conf.absOutFile - if output != outputCurrent: - # previously, any specified output file would be silently ignored; - # simply copying won't work in some cases, for example with `extraCmds`, - # so we just make it an error, user should use same command for jsonscript - # as was used with --compileOnly. - globalError(conf, gCmdLineInfo, "jsonscript command outputFile '$1' must match '$2' which was specified during --compileOnly, see \"outputFile\" entry in '$3' " % [outputCurrent, output, jsonFile.string]) - - let toCompile = data["compile"] - doAssert toCompile.kind == JArray - var cmds: TStringSeq - var prettyCmds: TStringSeq - let prettyCb = proc (idx: int) = writePrettyCmdsStderr(prettyCmds[idx]) - for c in toCompile: - doAssert c.kind == JArray - doAssert c.len >= 2 - - cmds.add(c[1].getStr) - prettyCmds.add displayProgressCC(conf, c[0].getStr, c[1].getStr) - - execCmdsInParallel(conf, cmds, prettyCb) - - let linkCmd = data["linkcmd"] - doAssert linkCmd.kind == JString - execLinkCmd(conf, linkCmd.getStr) - if data.hasKey("extraCmds"): - let extraCmds = data["extraCmds"] - doAssert extraCmds.kind == JArray - for cmd in extraCmds: - doAssert cmd.kind == JString, $cmd.kind - let cmd2 = cmd.getStr - execExternalProgram(conf, cmd2, hintExecuting) - + var bcache: BuildCache + try: bcache.fromJson(jsonFile.string.parseFile) except: let e = getCurrentException() - conf.quitOrRaise "\ncaught exception:\n" & e.msg & "\nstacktrace:\n" & e.getStackTrace() & - "error evaluating JSON file: " & jsonFile.string + conf.quitOrRaise "\ncaught exception:\n$#\nstacktrace:\n$#error evaluating JSON file: $#" % + [e.msg, e.getStackTrace(), jsonFile.string] + let output = bcache.outputFile + createDir output.parentDir + let outputCurrent = $conf.absOutFile + if output != outputCurrent or bcache.cacheVersion != cacheVersion: + globalError(conf, gCmdLineInfo, + "jsonscript command outputFile '$1' must match '$2' which was specified during --compileOnly, see \"outputFile\" entry in '$3' " % + [outputCurrent, output, jsonFile.string]) + var cmds, prettyCmds: TStringSeq + let prettyCb = proc (idx: int) = writePrettyCmdsStderr(prettyCmds[idx]) + for (name, cmd) in bcache.compile: + cmds.add cmd + prettyCmds.add displayProgressCC(conf, name, cmd) + execCmdsInParallel(conf, cmds, prettyCb) + execLinkCmd(conf, bcache.linkcmd) + for cmd in bcache.extraCmds: execExternalProgram(conf, cmd, hintExecuting) proc genMappingFiles(conf: ConfigRef; list: CfileList): Rope = for it in list: diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim index b63e5a0ad..94c93a283 100644 --- a/compiler/nimconf.nim +++ b/compiler/nimconf.nim @@ -240,13 +240,10 @@ proc getSystemConfigPath*(conf: ConfigRef; filename: RelativeFile): AbsoluteFile proc loadConfigs*(cfg: RelativeFile; cache: IdentCache; conf: ConfigRef; idgen: IdGenerator) = setDefaultLibpath(conf) - - var configFiles = newSeq[AbsoluteFile]() - template readConfigFile(path) = let configPath = path if readConfigFile(configPath, cache, conf): - configFiles.add(configPath) + conf.configFiles.add(configPath) template runNimScriptIfExists(path: AbsoluteFile, isMain = false) = let p = path # eval once @@ -256,7 +253,7 @@ proc loadConfigs*(cfg: RelativeFile; cache: IdentCache; conf: ConfigRef; idgen: elif conf.projectIsCmd: s = llStreamOpen(conf.cmdInput) if s == nil and fileExists(p): s = llStreamOpen(p, fmRead) if s != nil: - configFiles.add(p) + conf.configFiles.add(p) runNimScript(cache, p, idgen, freshDefines = false, conf, s) if optSkipSystemConfigFile notin conf.globalOptions: @@ -295,12 +292,12 @@ proc loadConfigs*(cfg: RelativeFile; cache: IdentCache; conf: ConfigRef; idgen: let scriptFile = conf.projectFull.changeFileExt("nims") let scriptIsProj = scriptFile == conf.projectFull template showHintConf = - for filename in configFiles: + for filename in conf.configFiles: # delayed to here so that `hintConf` is honored rawMessage(conf, hintConf, filename.string) if conf.cmd == cmdNimscript: showHintConf() - configFiles.setLen 0 + conf.configFiles.setLen 0 if conf.cmd != cmdIdeTools: if conf.cmd == cmdNimscript: runNimScriptIfExists(conf.projectFull, isMain = true) diff --git a/compiler/options.nim b/compiler/options.nim index e6c667d92..afe8b0fc7 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -308,7 +308,7 @@ type ## should be run ideCmd*: IdeCmd oldNewlines*: bool - cCompiler*: TSystemCC + cCompiler*: TSystemCC # the used compiler modifiedyNotes*: TNoteKinds # notes that have been set/unset from either cmdline/configs cmdlineNotes*: TNoteKinds # notes that have been set/unset from cmdline foreignPackageNotes*: TNoteKinds @@ -353,7 +353,7 @@ type docRoot*: string ## see nim --fullhelp for --docRoot docCmd*: string ## see nim --fullhelp for --docCmd - # the used compiler + configFiles*: seq[AbsoluteFile] # config files (cfg,nims) cIncludes*: seq[AbsoluteDir] # directories to search for included files cLibs*: seq[AbsoluteDir] # directories to search for lib files cLinkedLibs*: seq[string] # libraries to link diff --git a/lib/std/jsonutils.nim b/lib/std/jsonutils.nim index e33ae4401..60d78fea5 100644 --- a/lib/std/jsonutils.nim +++ b/lib/std/jsonutils.nim @@ -34,6 +34,23 @@ import macros from enumutils import symbolName from typetraits import OrdinalEnum +when not defined(nimFixedForwardGeneric): + # xxx remove pending csources_v1 update >= 1.2.0 + proc to[T](node: JsonNode, t: typedesc[T]): T = + when T is string: node.getStr + elif T is bool: node.getBool + else: static: doAssert false, $T # support as needed (only needed during bootstrap) + proc isNamedTuple(T: typedesc): bool = # old implementation + when T isnot tuple: result = false + else: + var t: T + for name, _ in t.fieldPairs: + when name == "Field0": return compiles(t.Field0) + else: return true + return false +else: + proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".} + type Joptions* = object # xxx rename FromJsonOptions ## Options controlling the behavior of `fromJson`. @@ -61,7 +78,6 @@ proc initToJsonOptions*(): ToJsonOptions = ## initializes `ToJsonOptions` with sane options. ToJsonOptions(enumMode: joptEnumOrd, jsonNodeMode: joptJsonNodeAsRef) -proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".} proc distinctBase(T: typedesc): typedesc {.magic: "TypeTrait".} template distinctBase[T](a: T): untyped = distinctBase(typeof(a))(a) From 7e21218a07f919d56cd0974c657aefb11574da26 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Fri, 4 Jun 2021 17:16:51 +0300 Subject: [PATCH 2181/3768] follow-up #18013 - inline syntax highlighting (#18166) --- doc/destructors.rst | 2 +- doc/docgen.rst | 2 +- doc/drnim.rst | 4 +- doc/estp.rst | 2 +- doc/hcr.rst | 31 +++++++++------ doc/intern.rst | 2 +- doc/koch.rst | 27 +++++++------ doc/lib.rst | 12 +++--- doc/manual.rst | 48 ++++++++++++------------ doc/manual_experimental.rst | 20 +++++----- doc/manual_experimental_strictnotnil.rst | 26 ++++++++----- doc/nep1.rst | 11 +++--- doc/nimgrep.rst | 31 ++++++++------- doc/niminst.rst | 24 ++++++------ doc/nims.rst | 45 +++++++++++----------- doc/nimsuggest.rst | 37 +++++++++--------- doc/packaging.rst | 2 +- doc/regexprs.txt | 4 +- doc/testament.rst | 2 +- doc/tools.rst | 15 ++++---- 20 files changed, 183 insertions(+), 164 deletions(-) diff --git a/doc/destructors.rst b/doc/destructors.rst index 3cd4cd925..c99606b95 100644 --- a/doc/destructors.rst +++ b/doc/destructors.rst @@ -5,8 +5,8 @@ Nim Destructors and Move Semantics :Authors: Andreas Rumpf :Version: |nimversion| +.. default-role:: code .. include:: rstcommon.rst -.. default-role:: nim .. contents:: diff --git a/doc/docgen.rst b/doc/docgen.rst index 86352a989..e9ff7f1e3 100644 --- a/doc/docgen.rst +++ b/doc/docgen.rst @@ -5,8 +5,8 @@ :Author: Erik O'Leary :Version: |nimversion| +.. default-role:: code .. include:: rstcommon.rst -.. default-role:: Nim .. contents:: diff --git a/doc/drnim.rst b/doc/drnim.rst index 08de7cd6d..070cd1787 100644 --- a/doc/drnim.rst +++ b/doc/drnim.rst @@ -5,8 +5,8 @@ :Author: Andreas Rumpf :Version: |nimversion| +.. default-role:: code .. include:: rstcommon.rst -.. default-role:: Nim .. contents:: @@ -43,7 +43,7 @@ your code. Installation ============ -Run `koch drnim`:option:, the executable will afterwards be +Run `koch drnim`:cmd:, the executable will afterwards be in ``$nim/bin/drnim``. diff --git a/doc/estp.rst b/doc/estp.rst index 43128bef7..550194d2d 100644 --- a/doc/estp.rst +++ b/doc/estp.rst @@ -2,8 +2,8 @@ Embedded Stack Trace Profiler (ESTP) User Guide =================================================== +.. default-role:: code .. include:: rstcommon.rst -.. default-role:: Nim :Author: Andreas Rumpf :Version: |nimversion| diff --git a/doc/hcr.rst b/doc/hcr.rst index 574c4f39d..dd25e39b3 100644 --- a/doc/hcr.rst +++ b/doc/hcr.rst @@ -1,9 +1,10 @@ -.. default-role:: code - =================================== Hot code reloading =================================== +.. default-role:: code +.. include:: rstcommon.rst + The `hotCodeReloading`:idx: option enables special compilation mode where changes in the code can be applied automatically to a running program. The code reloading happens at the granularity of an individual module. @@ -22,7 +23,7 @@ during development resides. In this example, we use SDL2 to create a window and we reload the logic code when `F9` is pressed. The important lines are marked with `#***`. -To install SDL2 you can use `nimble install sdl2`. +To install SDL2 you can use `nimble install sdl2`:cmd:. .. code-block:: nim @@ -98,34 +99,40 @@ To install SDL2 you can use `nimble install sdl2`. main() -Compile this example via:: +Compile this example via: +```cmd nim c --hotcodereloading:on mymain.nim +``` Now start the program and KEEP it running! -:: - +.. code:: cmd # Unix: mymain & # or Windows (click on the .exe) mymain.exe # edit -For example, change the line:: +For example, change the line: +```nim discard renderer.setDrawColor(255, 128, 128, 0) +``` -into:: +into: +```nim discard renderer.setDrawColor(255, 255, 128, 0) +``` (This will change the color of the rectangle.) Then recompile the project, but do not restart or quit the mymain.exe program! -:: +```cmd nim c --hotcodereloading:on mymain.nim +``` Now give the `mymain` SDL window the focus, press F9, and watch the updated version of the program. @@ -188,15 +195,15 @@ Native code targets =================== Native projects using the hot code reloading option will be implicitly -compiled with the `-d:useNimRtl` option and they will depend on both +compiled with the `-d:useNimRtl`:option: option and they will depend on both the `nimrtl` library and the `nimhcr` library which implements the hot code reloading run-time. Both libraries can be found in the `lib` folder of Nim and can be compiled into dynamic libraries to satisfy runtime demands of the example code above. An example of compiling -`nimhcr.nim` and `nimrtl.nim` when the source dir of Nim is installed +``nimhcr.nim`` and ``nimrtl.nim`` when the source dir of Nim is installed with choosenim follows. -:: +.. code:: console # Unix/MacOS # Make sure you are in the directory containing your .nim files diff --git a/doc/intern.rst b/doc/intern.rst index 2f83e0d06..ab74c4a3a 100644 --- a/doc/intern.rst +++ b/doc/intern.rst @@ -6,8 +6,8 @@ :Author: Andreas Rumpf :Version: |nimversion| +.. default-role:: code .. include:: rstcommon.rst -.. default-role:: Nim .. contents:: "Abstraction is layering ignorance on top of reality." -- Richard Gabriel diff --git a/doc/koch.rst b/doc/koch.rst index 1eb02d785..b8d85dff4 100644 --- a/doc/koch.rst +++ b/doc/koch.rst @@ -1,11 +1,11 @@ -.. default-role:: code - =============================== Nim maintenance script =============================== :Version: |nimversion| +.. default-role:: code +.. include:: rstcommon.rst .. contents:: .. raw:: html @@ -19,7 +19,7 @@ Introduction The `koch`:idx: program is Nim's maintenance script. It is a replacement for make and shell scripting with the advantage that it is much more portable. -The word *koch* means *cook* in German. `koch` is used mainly to build the +The word *koch* means *cook* in German. `koch`:cmd: is used mainly to build the Nim compiler, but it can also be used for other tasks. This document describes the supported commands and their options. @@ -33,12 +33,11 @@ boot command The `boot`:idx: command bootstraps the compiler, and it accepts different options: --d:release - By default a debug version is created, passing this option will +-d:release By default a debug version is created, passing this option will force a release build, which is much faster and should be preferred unless you are debugging the compiler. --d:nimUseLinenoise - Use the linenoise library for interactive mode (not needed on Windows). +-d:nimUseLinenoise Use the linenoise library for interactive mode + (not needed on Windows). After compilation is finished you will hopefully end up with the nim compiler in the `bin` directory. You can add Nim's `bin` directory to @@ -56,16 +55,16 @@ temp command ------------ The temp command builds the Nim compiler but with a different final name -(`nim_temp`), so it doesn't overwrite your normal compiler. You can use +(`nim_temp`:cmd:), so it doesn't overwrite your normal compiler. You can use this command to test different options, the same you would issue for the `boot command <#commands-boot-command>`_. test command ------------ -The `test`:idx: command can also be invoked with the alias `tests`. This -command will compile and run `testament/tester.nim`, which is the main -driver of Nim's test suite. You can pass options to the `test` command, +The `test`:idx: command can also be invoked with the alias `tests`:option:. This +command will compile and run ``testament/tester.nim``, which is the main +driver of Nim's test suite. You can pass options to the `test`:option: command, they will be forwarded to the tester. See its source code for available options. @@ -74,13 +73,13 @@ web command The `web`:idx: command converts the documentation in the `doc` directory from rst to HTML. It also repeats the same operation but places the result in -the `web/upload` which can be used to update the website at +the ``web/upload`` which can be used to update the website at https://nim-lang.org. By default, the documentation will be built in parallel using the number of available CPU cores. If any documentation build sub-commands fail, they will be rerun in serial fashion so that meaningful error output can be gathered for -inspection. The `--parallelBuild:n` switch or configuration option can be +inspection. The `--parallelBuild:n`:option: switch or configuration option can be used to force a specific number of parallel jobs or run everything serially from the start (`n == 1`). @@ -89,4 +88,4 @@ pdf command The `pdf`:idx: command builds PDF versions of Nim documentation: Manual, Tutorial and a few other documents. To run it one needs to -`install Latex/pdflatex `_ first. +`install Latex/xelatex `_ first. diff --git a/doc/lib.rst b/doc/lib.rst index f09fe16b2..988fbf6f0 100644 --- a/doc/lib.rst +++ b/doc/lib.rst @@ -1,5 +1,3 @@ -.. default-role:: code - ==================== Nim Standard Library ==================== @@ -7,11 +5,13 @@ Nim Standard Library :Author: Andreas Rumpf :Version: |nimversion| +.. default-role:: code +.. include:: rstcommon.rst .. contents:: Nim's library is divided into *pure libraries*, *impure libraries*, and *wrappers*. -Pure libraries do not depend on any external `*.dll` or `lib*.so` binary +Pure libraries do not depend on any external ``*.dll`` or ``lib*.so`` binary while impure libraries do. A wrapper is an impure library that is a very low-level interface to a C library. @@ -39,11 +39,11 @@ Automatic imports * `threads `_ Basic Nim thread support. **Note:** This is part of the system module. Do not - import it explicitly. Enabled with `--threads:on`. + import it explicitly. Enabled with `--threads:on`:option:. * `channels `_ Nim message passing support for threads. **Note:** This is part of the - system module. Do not import it explicitly. Enabled with `--threads:on`. + system module. Do not import it explicitly. Enabled with `--threads:on`:option:. Core @@ -366,7 +366,7 @@ Parsers * `parsecfg `_ The `parsecfg` module implements a high-performance configuration file - parser. The configuration file's syntax is similar to the Windows `.ini` + parser. The configuration file's syntax is similar to the Windows ``.ini`` format, but much more powerful, as it is not a line based parser. String literals, raw string literals, and triple quote string literals are supported as in the Nim programming language. diff --git a/doc/manual.rst b/doc/manual.rst index ff34b62ca..867db245b 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -572,7 +572,8 @@ In the following examples, `-1` is a single token: "abc";-1 -In the following examples, `-1` is parsed as two separate tokens (as `- 1`): +In the following examples, `-1` is parsed as two separate tokens +(as `-`:tok: `1`:tok:): .. code-block:: nim @@ -2509,7 +2510,7 @@ of the argument. range. 3. Generic match: `f` is a generic type and `a` matches, for instance `a` is `int` and `f` is a generic (constrained) parameter - type (like in `[T]` or `[T: int|char]`. + type (like in `[T]` or `[T: int|char]`). 4. Subrange or subtype match: `a` is a `range[T]` and `T` matches `f` exactly. Or: `a` is a subtype of `f`. 5. Integral conversion match: `a` is convertible to `f` and `f` and `a` @@ -3840,7 +3841,7 @@ Type bound operators currently include: (some of which are still implementation defined and not yet documented). For more details on some of those procs, see -`lifetimeminustracking-hooks `_. +`Lifetime-tracking hooks `_. Nonoverloadable builtins ------------------------ @@ -5283,7 +5284,7 @@ instantiations cross multiple different modules: In module B has an `init` proc from module C in its scope that is not taken into account when `genericB` is instantiated which leads to the -instantiation of `genericA`. The solution is to `forward`:idx these +instantiation of `genericA`. The solution is to `forward`:idx: these symbols by a `bind` statement inside `genericB`. @@ -7305,19 +7306,19 @@ Produces: supplied pattern to denote the concrete type parameters of the generic type. See the usage of the apostrophe operator in proc patterns for more details. -.. code-block:: nim + .. code-block:: nim - type - VectorIterator {.importcpp: "std::vector<'0>::iterator".} [T] = object + type + VectorIterator {.importcpp: "std::vector<'0>::iterator".} [T] = object - var x: VectorIterator[cint] + var x: VectorIterator[cint] -Produces: + Produces: -.. code-block:: C + .. code-block:: C - std::vector::iterator x; + std::vector::iterator x; ImportJs pragma @@ -7444,7 +7445,7 @@ compile-time define pragmas --------------------------- The pragmas listed here can be used to optionally accept values from -the -d/--define option at compile time. +the `-d/--define`:option: option at compile time. The implementation currently provides the following possible options (various others may be added later). @@ -7461,7 +7462,7 @@ pragma description const FooBar {.intdefine.}: int = 5 echo FooBar -:: +.. code:: cmd nim c -d:FooBar=42 foobar.nim In the above example, providing the `-d`:option: flag causes the symbol @@ -7480,7 +7481,8 @@ pragma pragma ------------- The `pragma` pragma can be used to declare user-defined pragmas. This is -useful because Nim's templates and macros do not affect pragmas. User-defined pragmas are in a different module-wide scope than all other symbols. +useful because Nim's templates and macros do not affect pragmas. +User-defined pragmas are in a different module-wide scope than all other symbols. They cannot be imported from a module. Example: @@ -7553,18 +7555,18 @@ More examples with custom pragmas: - Better serialization/deserialization control: -.. code-block:: nim - type MyObj = object - a {.dontSerialize.}: int - b {.defaultDeserialize: 5.}: int - c {.serializationKey: "_c".}: string + .. code-block:: nim + type MyObj = object + a {.dontSerialize.}: int + b {.defaultDeserialize: 5.}: int + c {.serializationKey: "_c".}: string - Adopting type for gui inspector in a game engine: -.. code-block:: nim - type MyComponent = object - position {.editable, animatable.}: Vector3 - alpha {.editRange: [0.0..1.0], animatable.}: float32 + .. code-block:: nim + type MyComponent = object + position {.editable, animatable.}: Vector3 + alpha {.editRange: [0.0..1.0], animatable.}: float32 Macro pragmas diff --git a/doc/manual_experimental.rst b/doc/manual_experimental.rst index 51037b509..2c1043ecb 100644 --- a/doc/manual_experimental.rst +++ b/doc/manual_experimental.rst @@ -1,5 +1,3 @@ -.. default-role:: code - ========================= Nim Experimental Features ========================= @@ -7,6 +5,8 @@ Nim Experimental Features :Authors: Andreas Rumpf :Version: |nimversion| +.. default-role:: code +.. include:: rstcommon.rst .. contents:: @@ -15,7 +15,7 @@ About this document This document describes features of Nim that are to be considered experimental. Some of these are not covered by the `.experimental` pragma or -`--experimental` switch because they are already behind a special syntax and +`--experimental`:option: switch because they are already behind a special syntax and one may want to use Nim libraries using these features without using them oneself. @@ -1109,7 +1109,7 @@ The `parameter constraint`:idx: expression can use the operators `|` (or), Predicate Meaning =================== ===================================================== `atom` The matching node has no children. -`lit` The matching node is a literal like "abc", 12. +`lit` The matching node is a literal like `"abc"`, `12`. `sym` The matching node must be a symbol (a bound identifier). `ident` The matching node must be an identifier (an unbound @@ -1185,7 +1185,7 @@ constant folding, so the following does not work: The reason is that the compiler already transformed the 1 into "1" for the `echo` statement. However, a term rewriting macro should not change the -semantics anyway. In fact they can be deactivated with the `--patterns:off` +semantics anyway. In fact they can be deactivated with the `--patterns:off`:option: command line option or temporarily with the `patterns` pragma. @@ -1779,7 +1779,7 @@ Noalias annotation ================== Since version 1.4 of the Nim compiler, there is a `.noalias` annotation for variables -and parameters. It is mapped directly to C/C++'s `restrict` keyword and means that +and parameters. It is mapped directly to C/C++'s `restrict`:c: keyword and means that the underlying pointer is pointing to a unique location in memory, no other aliases to this location exist. It is *unchecked* that this alias restriction is followed, if the restriction is violated, the backend optimizer is free to miscompile the code. @@ -1832,8 +1832,8 @@ the `view types section <#view-types-algorithm>`_. View types ========== -**Note**: `--experimental:views` is more effective -with `--experimental:strictFuncs`. +**Note**: `--experimental:views`:option: is more effective +with `--experimental:strictFuncs`:option:. A view type is a type that is or contains one of the following types: @@ -1981,8 +1981,8 @@ The scope of the view does not matter: The analysis requires as much precision about mutations as is reasonably obtainable, so it is more effective with the experimental `strict funcs <#strict-funcs>`_ -feature. In other words `--experimental:views` works better -with `--experimental:strictFuncs`. +feature. In other words `--experimental:views`:option: works better +with `--experimental:strictFuncs`:option:. The analysis is currently control flow insensitive: diff --git a/doc/manual_experimental_strictnotnil.rst b/doc/manual_experimental_strictnotnil.rst index cb3bfeca9..b14e5f9f3 100644 --- a/doc/manual_experimental_strictnotnil.rst +++ b/doc/manual_experimental_strictnotnil.rst @@ -1,8 +1,9 @@ -.. default-role:: code - Strict not nil checking ========================= +.. default-role:: code +.. include:: rstcommon.rst + **Note:** This feature is experimental, you need to enable it with .. code-block:: nim @@ -10,7 +11,7 @@ Strict not nil checking or -.. code-block:: bash +.. code-block:: cmd nim c --experimental:strictNotNil In the second case it would check builtin and imported modules as well. @@ -60,14 +61,16 @@ You can annotate a type where nil isn't a valid value with `not nil`. If a type can include `nil` as a valid value, dereferencing values of the type -is checked by the compiler: if a value which might be nil is derefenced, this produces a warning by default, you can turn this into an error using the compiler options `--warningAsError:strictNotNil` +is checked by the compiler: if a value which might be nil is derefenced, this +produces a warning by default, you can turn this into an error using +the compiler options `--warningAsError:strictNotNil`:option:. If a type is nilable, you should dereference its values only after a `isNil` or equivalent check. local turn on/off --------------------- -You can still turn off nil checking on function/module level by using a `{.strictNotNil: off}.` pragma. +You can still turn off nil checking on function/module level by using a `{.strictNotNil: off.}` pragma. Note: test that/TODO for code/manual. nilability state @@ -75,11 +78,14 @@ nilability state Currently a nilable value can be `Safe`, `MaybeNil` or `Nil` : we use internally `Parent` and `Unreachable` but this is an implementation detail(a parent layer has the actual nilability). -`Safe` means it shouldn't be nil at that point: e.g. after assignment to a non-nil value or `not a.isNil` check -`MaybeNil` means it might be nil, but it might not be nil: e.g. an argument, a call argument or a value after an `if` and `else`. -`Nil` means it should be nil at that point; e.g. after an assignment to `nil` or a `.isNil` check. - -`Unreachable` means it shouldn't be possible to access this in this branch: so we do generate a warning as well. +- `Safe` means it shouldn't be nil at that point: e.g. after assignment to + a non-nil value or `not a.isNil` check +- `MaybeNil` means it might be nil, but it might not be nil: e.g. an argument, + a call argument or a value after an `if` and `else`. +- `Nil` means it should be nil at that point; e.g. after an assignment to + `nil` or a `.isNil` check. +- `Unreachable` means it shouldn't be possible to access this in this branch: + so we do generate a warning as well. We show an error for each dereference (`[]`, `.field`, `[index]` `()` etc) which is of a tracked expression which is in `MaybeNil` or `Nil` state. diff --git a/doc/nep1.rst b/doc/nep1.rst index 8687725bb..fc69bab69 100644 --- a/doc/nep1.rst +++ b/doc/nep1.rst @@ -1,11 +1,12 @@ -.. default-role:: code - ========================================================== Nim Enhancement Proposal #1 - Standard Library Style Guide ========================================================== + :Author: Clay Sweetser, Dominik Picheta :Version: |nimversion| +.. default-role:: code +.. include:: rstcommon.rst .. contents:: @@ -139,7 +140,7 @@ Naming Conventions - rotate and rotated - When the 'returns transformed copy' version already exists like `strutils.replace` - an in-place version should get an `-In` suffix (`replaceIn` for this example). + an in-place version should get an ``-In`` suffix (`replaceIn` for this example). - Use `subjectVerb`, not `verbSubject`, e.g.: `fileExists`, not `existsFile`. @@ -225,8 +226,8 @@ indentation indent Coding Conventions ------------------ -- The 'return' statement should ideally be used when its control-flow properties - are required. Use a procedure's implicit 'result' variable whenever possible. +- The `return` statement should ideally be used when its control-flow properties + are required. Use a procedure's implicit `result` variable whenever possible. This improves readability. .. code-block:: nim diff --git a/doc/nimgrep.rst b/doc/nimgrep.rst index 52fc4d62a..6088a4c45 100644 --- a/doc/nimgrep.rst +++ b/doc/nimgrep.rst @@ -1,14 +1,11 @@ -.. default-role:: code - ========================= nimgrep User's manual ========================= -.. default-role:: literal - :Author: Andreas Rumpf :Version: 1.6.0 +.. default-role:: option .. contents:: Nimgrep is a command line tool for search and replace tasks. It can search for @@ -23,11 +20,12 @@ Apart from that it is a generic text manipulation tool. Installation ============ -Compile nimgrep with the command:: +Compile nimgrep with the command: +.. code:: cmd nim c -d:release tools/nimgrep.nim -And copy the executable somewhere in your `$PATH`. +And copy the executable somewhere in your ``$PATH``. Command line switches @@ -40,31 +38,32 @@ Examples All examples below use default PCRE Regex patterns: -+ To search recursively in Nim files using style-insensitive identifiers:: ++ To search recursively in Nim files using style-insensitive identifiers: - --recursive --ext:'nim|nims' --ignoreStyle + .. code:: cmd + nimgrep --recursive --ext:'nim|nims' --ignoreStyle # short: -r --ext:'nim|nims' -y .. Note:: we used `'` quotes to avoid special treatment of `|` symbol for shells like Bash + To exclude version control directories (Git, Mercurial=hg, Subversion=svn) - from the search:: + from the search: - --excludeDir:'^\.git$' --excludeDir:'^\.hg$' --excludeDir:'^\.svn$' + .. code:: cmd + nimgrep --excludeDir:'^\.git$' --excludeDir:'^\.hg$' --excludeDir:'^\.svn$' # short: --ed:'^\.git$' --ed:'^\.hg$' --ed:'^\.svn$' + To search only in paths containing the `tests` sub-directory recursively:: - --recursive --includeDir:'(^|/)tests($|/)' + .. code:: cmd + nimgrep --recursive --includeDir:'(^|/)tests($|/)' # short: -r --id:'(^|/)tests($|/)' - .. Attention:: note the subtle difference between `--excludeDir` and - `--includeDir`: the former is applied to relative directory entries + .. Attention:: note the subtle difference between `--excludeDir`:option: and + `--includeDir`:option:\: the former is applied to relative directory entries and the latter is applied to the whole paths + Nimgrep can search multi-line, e.g. to find files containing `import` - and then `strutils` use:: - - 'import(.|\n)*?strutils' + and then `strutils` use pattern `'import(.|\n)*?strutils'`:option:. diff --git a/doc/niminst.rst b/doc/niminst.rst index 3ccb47cc8..8fff1f0e8 100644 --- a/doc/niminst.rst +++ b/doc/niminst.rst @@ -1,5 +1,3 @@ -.. default-role:: code - ========================= niminst User's manual ========================= @@ -7,6 +5,8 @@ :Author: Andreas Rumpf :Version: |nimversion| +.. default-role:: code +.. include:: rstcommon.rst .. contents:: Introduction @@ -34,7 +34,7 @@ configuration file. Here's an example of how the syntax looks like: The value of a key-value pair can reference user-defined variables via the `$variable` notation: They can be defined in the command line with the -`--var:name=value` switch. This is useful to not hard-coding the +`--var:name=value`:option: switch. This is useful to not hard-coding the program's version number into the configuration file, for instance. It follows a description of each possible section and how it affects the @@ -91,10 +91,10 @@ Documentation section The `documentation` section supports the `files` key. Listed files will be installed into the OS's native documentation directory -(which might be `$appdir/doc`). +(which might be ``$appdir/doc``). There is a `start` key which determines whether the Windows installer -generates a link to e.g. the `index.html` of your documentation. +generates a link to e.g. the ``index.html`` of your documentation. Other section @@ -125,9 +125,9 @@ Other possible options are: ==================== ======================================================= Key description ==================== ======================================================= -`BinPath` paths to add to the Windows `%PATH%` environment +`BinPath` paths to add to the Windows `%PATH%` environment variable. Example: ``BinPath: r"bin;dist\mingw\bin"`` -`InnoSetup` boolean flag whether an Inno Setup installer should be +`InnoSetup` boolean flag whether an Inno Setup installer should be generated for Windows. Example: `InnoSetup: "Yes"` ==================== ======================================================= @@ -137,7 +137,7 @@ UnixBin section The `UnixBin` section currently only supports the `files` key. Listed files will be installed into the OS's native bin directory -(e.g. `/usr/local/bin`). The exact location depends on the +(e.g. ``/usr/local/bin``). The exact location depends on the installation path the user specifies when running the `install.sh` script. @@ -165,9 +165,9 @@ Possible options are: ==================== ======================================================= Key description ==================== ======================================================= -`path` Path to Inno Setup. +`path` Path to Inno Setup. Example: ``path = r"c:\inno setup 5\iscc.exe"`` -`flags` Flags to pass to Inno Setup. +`flags` Flags to pass to Inno Setup. Example: `flags = "/Q"` ==================== ======================================================= @@ -180,8 +180,8 @@ Possible options are: ==================== ======================================================= Key description ==================== ======================================================= -`path` Path to the C compiler. -`flags` Flags to pass to the C Compiler. +`path` Path to the C compiler. +`flags` Flags to pass to the C Compiler. Example: `flags = "-w"` ==================== ======================================================= diff --git a/doc/nims.rst b/doc/nims.rst index f81637d73..5ae1d7baa 100644 --- a/doc/nims.rst +++ b/doc/nims.rst @@ -1,32 +1,33 @@ -.. default-role:: code - ================================ NimScript ================================ +.. default-role:: code +.. include:: rstcommon.rst + Strictly speaking, `NimScript` is the subset of Nim that can be evaluated by Nim's builtin virtual machine (VM). This VM is used for Nim's compiletime function evaluation features. -The `nim` executable processes the `.nims` configuration files in +The `nim`:cmd: executable processes the ``.nims`` configuration files in the following directories (in this order; later files overwrite previous settings): 1) If environment variable `XDG_CONFIG_HOME` is defined, - `$XDG_CONFIG_HOME/nim/config.nims` or - `~/.config/nim/config.nims` (POSIX) or - `%APPDATA%/nim/config.nims` (Windows). This file can be skipped - with the `--skipUserCfg` command line option. -2) `$parentDir/config.nims` where `$parentDir` stands for any + ``$XDG_CONFIG_HOME/nim/config.nims`` or + ``~/.config/nim/config.nims`` (POSIX) or + ``%APPDATA%/nim/config.nims`` (Windows). This file can be skipped + with the `--skipUserCfg`:option: command line option. +2) ``$parentDir/config.nims`` where ``$parentDir`` stands for any parent directory of the project file's path. These files can be - skipped with the `--skipParentCfg` command line option. -3) `$projectDir/config.nims` where `$projectDir` stands for the - project's path. This file can be skipped with the `--skipProjCfg` + skipped with the `--skipParentCfg`:option: command line option. +3) ``$projectDir/config.nims`` where ``$projectDir`` stands for the + project's path. This file can be skipped with the `--skipProjCfg`:option: command line option. 4) A project can also have a project specific configuration file named - `$project.nims` that resides in the same directory as - `$project.nim`. This file can be skipped with the same - `--skipProjCfg` command line option. + ``$project.nims`` that resides in the same directory as + ``$project.nim``. This file can be skipped with the same + `--skipProjCfg`:option: command line option. For available procs and implementation details see `nimscript `_. @@ -111,8 +112,8 @@ See also: NimScript as a configuration file ================================= -A command-line switch `--FOO` is written as `switch("FOO")` in -NimScript. Similarly, command-line `--FOO:VAL` translates to +A command-line switch `--FOO`:option: is written as `switch("FOO")` in +NimScript. Similarly, command-line `--FOO:VAL`:option: translates to `switch("FOO", "VAL")`. Here are few examples of using the `switch` proc: @@ -125,7 +126,7 @@ Here are few examples of using the `switch` proc: # command-line: --forceBuild switch("forceBuild") -NimScripts also support `--` templates for convenience, which look +NimScripts also support `--`:option: templates for convenience, which look like command-line switches written as-is in the NimScript file. So the above example can be rewritten as: @@ -136,7 +137,7 @@ above example can be rewritten as: **Note**: In general, the *define* switches can also be set in NimScripts using `switch` or `--`, as shown in above -examples. Only the `release` define (`-d:release`) cannot be set +examples. Only the `release` define (`-d:release`:option:) cannot be set in NimScripts. @@ -145,7 +146,7 @@ NimScript as a build tool The `task` template that the `system` module defines allows a NimScript file to be used as a build tool. The following example defines a -task `build` that is an alias for the `c` command: +task `build` that is an alias for the `c`:option: command: .. code-block:: nim task build, "builds an example": @@ -159,7 +160,7 @@ Task Description ========= =================================================== `help` List all the available NimScript tasks along with their docstrings. `build` Build the project with the required - backend (`c`, `cpp` or `js`). + backend (`c`:option:, `cpp`:option: or `js`:option:). `tests` Runs the tests belonging to the project. `bench` Runs benchmarks belonging to the project. ========= =================================================== @@ -180,7 +181,7 @@ Standalone NimScript ==================== NimScript can also be used directly as a portable replacement for Bash and -Batch files. Use `nim myscript.nims` to run `myscript.nims`. For example, +Batch files. Use `nim myscript.nims`:cmd: to run ``myscript.nims``. For example, installation of Nimble could be accomplished with this simple script: .. code-block:: nim @@ -199,7 +200,7 @@ installation of Nimble could be accomplished with this simple script: mvFile "nimble" & $id & "/src/nimble".toExe, "bin/nimble".toExe On Unix, you can also use the shebang `#!/usr/bin/env nim`, as long as your filename -ends with `.nims`: +ends with ``.nims``: .. code-block:: nim diff --git a/doc/nimsuggest.rst b/doc/nimsuggest.rst index 509f72e8a..82693da0e 100644 --- a/doc/nimsuggest.rst +++ b/doc/nimsuggest.rst @@ -1,5 +1,3 @@ -.. default-role:: code - ================================ Nim IDE Integration Guide ================================ @@ -7,14 +5,16 @@ :Author: Unknown :Version: |nimversion| +.. default-role:: code +.. include:: rstcommon.rst .. contents:: Nim differs from many other compilers in that it is really fast, and being so fast makes it suited to provide external queries for text editors about the source code being written. Through the -`nimsuggest` tool, any IDE -can query a `.nim` source file and obtain useful information like +`nimsuggest`:cmd: tool, any IDE +can query a ``.nim`` source file and obtain useful information like definition of symbols or suggestions for completion. This document will guide you through the available options. If you @@ -27,29 +27,30 @@ already available. Installation ============ -Nimsuggest is part of Nim's core. Build it via:: +Nimsuggest is part of Nim's core. Build it via: +.. code:: cmd koch nimsuggest Nimsuggest invocation ===================== -Run it via `nimsuggest --stdin --debug myproject.nim`. Nimsuggest is a +Run it via `nimsuggest --stdin --debug myproject.nim`:cmd:. Nimsuggest is a server that takes queries that are related to `myproject`. There is some -support so that you can throw random `.nim` files which are not part +support so that you can throw random ``.nim`` files which are not part of `myproject` at Nimsuggest too, but usually the query refer to modules/files that are part of `myproject`. -`--stdin` means that Nimsuggest reads the query from `stdin`. This is great +`--stdin`:option: means that Nimsuggest reads the query from `stdin`. This is great for testing things out and playing with it but for an editor communication via sockets is more reasonable so that is the default. It listens to port 6000 by default. -Nimsuggest is basically a frontend for the nim compiler so `--path` flags and +Nimsuggest is basically a frontend for the nim compiler so `--path`:option: flags and `config files `_ can be used to specify additional dependencies like -`nimsuggest --stdin --debug --path:"dependencies" myproject.nim`. +`nimsuggest --stdin --debug --path:"dependencies" myproject.nim`:cmd:. Specifying the location of the query @@ -60,25 +61,25 @@ cryptic 3 letter "command" `def` or `con` or `sug` or `use` followed by a location. A query location consists of: -`file.nim` +``file.nim`` This is the name of the module or include file the query refers to. -`dirtyfile.nim` +``dirtyfile.nim`` This is optional. The `file` parameter is enough for static analysis, but IDEs tend to have *unsaved buffers* where the user may still be in the middle of typing a line. In such situations the IDE can save the current contents to a temporary file and then use the - `dirtyfile.nim` option to tell Nimsuggest that `foobar.nim` should - be taken from `temporary/foobar.nim`. + ``dirtyfile.nim`` option to tell Nimsuggest that ``foobar.nim`` should + be taken from ``temporary/foobar.nim``. -`line` +``line`` An integer with the line you are going to query. For the compiler lines start at **1**. -`col` +``col`` An integer with the column you are going to query. For the compiler columns start at **0**. @@ -149,9 +150,9 @@ tab characters (``\t``). The values of each column are: 1. Three characters indicating the type of returned answer (e.g. `def` for definition, `sug` for suggestion, etc). 2. Type of the symbol. This can be `skProc`, `skLet`, and just - about any of the enums defined in the module `compiler/ast.nim`. + about any of the enums defined in the module ``compiler/ast.nim``. 3. Fully qualified path of the symbol. If you are querying a symbol - defined in the `proj.nim` file, this would have the form + defined in the ``proj.nim`` file, this would have the form `proj.symbolName`. 4. Type/signature. For variables and enums this will contain the type of the symbol, for procs, methods and templates this will diff --git a/doc/packaging.rst b/doc/packaging.rst index ecde73f61..ecb17e482 100644 --- a/doc/packaging.rst +++ b/doc/packaging.rst @@ -45,7 +45,7 @@ The Debian package ships bash and ksh completion and manpages that can be reused Hints on the build process: -:: +.. code:: cmd # build from C sources and then using koch ./build.sh --os $os_type --cpu $cpu_arch diff --git a/doc/regexprs.txt b/doc/regexprs.txt index 9ec08b810..f5544cc28 100644 --- a/doc/regexprs.txt +++ b/doc/regexprs.txt @@ -47,9 +47,11 @@ full documentation of Perl's regular expressions. Because the backslash ``\`` is a meta character both in the Nim programming language and in regular expressions, it is strongly recommended that one uses the *raw* strings of Nim, so that -backslashes are interpreted by the regular expression engine:: +backslashes are interpreted by the regular expression engine: +```nim r"\S" # matches any character that is not whitespace +``` A regular expression is a pattern that is matched against a subject string from left to right. Most characters stand for themselves in a pattern, and diff --git a/doc/testament.rst b/doc/testament.rst index ed1bd63a1..5590cc6d7 100644 --- a/doc/testament.rst +++ b/doc/testament.rst @@ -2,8 +2,8 @@ Testament =========== +.. default-role:: code .. include:: rstcommon.rst -.. default-role:: nim .. contents:: Testament is an advanced automatic unittests runner for Nim tests, is used for the development of Nim itself, diff --git a/doc/tools.rst b/doc/tools.rst index e0044e1ca..0de4ac914 100644 --- a/doc/tools.rst +++ b/doc/tools.rst @@ -1,9 +1,10 @@ -.. default-role:: code - ======================== Tools available with Nim ======================== +.. default-role:: code +.. include:: rstcommon.rst + The standard distribution ships with the following tools: - | `Hot code reloading `_ @@ -11,11 +12,11 @@ The standard distribution ships with the following tools: document explaining how it works. - | `Documentation generator `_ - | The builtin document generator `nim doc` generates HTML documentation - from `.nim` source files. + | The builtin document generator `nim doc`:cmd: generates HTML documentation + from ``.nim`` source files. - | `Nimsuggest for IDE support `_ - | Through the `nimsuggest` tool, any IDE can query a `.nim` source file + | Through the `nimsuggest`:cmd: tool, any IDE can query a ``.nim`` source file and obtain useful information like the definition of symbols or suggestions for completion. @@ -29,11 +30,11 @@ The standard distribution ships with the following tools: | Nim search and replace utility. - | nimpretty - | `nimpretty` is a Nim source code beautifier, + | `nimpretty`:cmd: is a Nim source code beautifier, to format code according to the official style guide. - | `testament `_ - | `testament` is an advanced automatic *unittests runner* for Nim tests, + | `testament`:cmd: is an advanced automatic *unittests runner* for Nim tests, is used for the development of Nim itself, offers process isolation for your tests, it can generate statistics about test cases, supports multiple targets (C, JS, etc), `simulated Dry-Runs `_, From 5423915e5ade14fb98c2aba28f269311506960e5 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 4 Jun 2021 16:20:47 +0200 Subject: [PATCH 2182/3768] real bugfix for #17170 (#18171) * real bugfix for #17170 * better fix --- compiler/closureiters.nim | 10 ++++++---- compiler/injectdestructors.nim | 2 ++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/compiler/closureiters.nim b/compiler/closureiters.nim index adc5a4873..a8da7485e 100644 --- a/compiler/closureiters.nim +++ b/compiler/closureiters.nim @@ -414,8 +414,11 @@ proc exprToStmtList(n: PNode): tuple[s, res: PNode] = proc newEnvVarAsgn(ctx: Ctx, s: PSym, v: PNode): PNode = - result = newTree(nkFastAsgn, ctx.newEnvVarAccess(s), v) - result.info = v.info + if isEmptyType(v.typ): + result = v + else: + result = newTree(nkFastAsgn, ctx.newEnvVarAccess(s), v) + result.info = v.info proc addExprAssgn(ctx: Ctx, output, input: PNode, sym: PSym) = if input.kind == nkStmtListExpr: @@ -427,8 +430,7 @@ proc addExprAssgn(ctx: Ctx, output, input: PNode, sym: PSym) = proc convertExprBodyToAsgn(ctx: Ctx, exprBody: PNode, res: PSym): PNode = result = newNodeI(nkStmtList, exprBody.info) - if exprBody.typ != nil: - ctx.addExprAssgn(result, exprBody, res) + ctx.addExprAssgn(result, exprBody, res) proc newNotCall(g: ModuleGraph; e: PNode): PNode = result = newTree(nkCall, newSymNode(g.getSysMagic(e.info, "not", mNot), e.info), e) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 48a852d26..8ef08adeb 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -364,6 +364,7 @@ proc genMarkCyclic(c: var Con; result, dest: PNode) = proc genCopyNoCheck(c: var Con; dest, ri: PNode): PNode = let t = dest.typ.skipTypes({tyGenericInst, tyAlias, tySink}) result = c.genOp(t, attachedAsgn, dest, ri) + assert ri.typ != nil proc genCopy(c: var Con; dest, ri: PNode): PNode = let t = dest.typ @@ -371,6 +372,7 @@ proc genCopy(c: var Con; dest, ri: PNode): PNode = # try to improve the error message here: c.checkForErrorPragma(t, ri, "=copy") result = c.genCopyNoCheck(dest, ri) + assert ri.typ != nil proc genDiscriminantAsgn(c: var Con; s: var Scope; n: PNode): PNode = # discriminator is ordinal value that doesn't need sink destroy From a77360da5bad40d22e942cdd5cb9614c3548bf34 Mon Sep 17 00:00:00 2001 From: flywind Date: Sat, 5 Jun 2021 03:41:13 +0800 Subject: [PATCH 2183/3768] workaround bug in sharedtables.withValue to unblock frozen CI on OSX (#18172) --- lib/pure/collections/sharedtables.nim | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/pure/collections/sharedtables.nim b/lib/pure/collections/sharedtables.nim index 2788ec785..a85674df9 100644 --- a/lib/pure/collections/sharedtables.nim +++ b/lib/pure/collections/sharedtables.nim @@ -108,10 +108,14 @@ template withValue*[A, B](t: var SharedTable[A, B], key: A, table.withValue("a", value): value[] = "m" + var flag = false table.withValue("d", value): discard value doAssert false do: # if "d" notin table + flag = true + + if flag: table["d"] = "n" assert table.mget("a") == "m" From cc7ec5a6a42c2aead2c411228f023cbeff4220de Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 4 Jun 2021 15:32:46 -0700 Subject: [PATCH 2184/3768] fix #7295: use move(result) inside template to avoid copy with --gc:refc (#18168) --- lib/pure/collections/sequtils.nim | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index e937b8394..6facfcc32 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -1014,27 +1014,27 @@ template applyIt*(varSeq, op: untyped) = template newSeqWith*(len: int, init: untyped): untyped = - ## Creates a new sequence of length `len`, calling `init` to initialize - ## each value of the sequence. - ## - ## Useful for creating "2D" sequences - sequences containing other sequences - ## or to populate fields of the created sequence. + ## Creates a new `seq` of length `len`, calling `init` to initialize + ## each value of the seq. ## + ## Useful for creating "2D" seqs - seqs containing other seqs + ## or to populate fields of the created seq. runnableExamples: - ## Creates a sequence containing 5 bool sequences, each of length of 3. + ## Creates a seq containing 5 bool seqs, each of length of 3. var seq2D = newSeqWith(5, newSeq[bool](3)) assert seq2D.len == 5 assert seq2D[0].len == 3 assert seq2D[4][2] == false - ## Creates a sequence of 20 random numbers from 1 to 10 - import random - var seqRand = newSeqWith(20, rand(10)) + ## Creates a seq with random numbers + import std/random + var seqRand = newSeqWith(20, rand(1.0)) + assert seqRand[0] != seqRand[1] var result = newSeq[typeof(init)](len) for i in 0 ..< len: result[i] = init - result + move(result) # refs bug #7295 func mapLitsImpl(constructor: NimNode; op: NimNode; nested: bool; filter = nnkLiterals): NimNode = From a2b60812568b5b90af9bb15c50873a726dd49b94 Mon Sep 17 00:00:00 2001 From: Euan Date: Fri, 4 Jun 2021 23:46:57 +0100 Subject: [PATCH 2185/3768] Ref #18177 - lbgc.so.5.0 on OpenBSD 6.9 (#18180) --- lib/system.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/system.nim b/lib/system.nim index 942d29fe8..cae0ec3e2 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1146,7 +1146,7 @@ when defined(boehmgc): elif defined(macosx): const boehmLib = "libgc.dylib" elif defined(openbsd): - const boehmLib = "libgc.so.4.0" + const boehmLib = "libgc.so.(4|5).0" elif defined(freebsd): const boehmLib = "libgc-threaded.so.1" else: From 3cc547f2dfc3d2a981ccde38adb1f3deeb9715c2 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 4 Jun 2021 21:58:26 -0700 Subject: [PATCH 2186/3768] macros.treeRepr + friends: collapse SymChoice (#18072) * macros.treeRepr + friends: collapse SymChoice * make repr+friends work with invalid symchoice nodes * address comment --- changelog.md | 3 + lib/core/macros.nim | 63 +++++++++++++------- tests/macros/tdumpast.nim | 122 +++++++++++++++++++++++++++++++------- tests/stdlib/tmacros.nim | 6 ++ 4 files changed, 151 insertions(+), 43 deletions(-) diff --git a/changelog.md b/changelog.md index 06251abfc..1050e4916 100644 --- a/changelog.md +++ b/changelog.md @@ -81,6 +81,9 @@ - Deprecated `proc reversed*[T](a: openArray[T], first: Natural, last: int): seq[T]` in `std/algorithm`. +- In `std/macros`, `treeRepr,lispRepr,astGenRepr` now represent SymChoice nodes in a collapsed way, + use `-d:nimLegacyMacrosCollapseSymChoice` to get previous behavior. + - The configuration subsystem now allows for `-d:release` and `-d:danger` to work as expected. The downside is that these defines now have custom logic that doesn't apply for other defines. diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 89f90a23b..37f855d94 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -856,6 +856,29 @@ proc nestList*(op: NimNode; pack: NimNode; init: NimNode): NimNode = for i in countdown(pack.len - 1, 0): result = newCall(op, pack[i], result) +proc eqIdent*(a: string; b: string): bool {.magic: "EqIdent", noSideEffect.} + ## Style insensitive comparison. + +proc eqIdent*(a: NimNode; b: string): bool {.magic: "EqIdent", noSideEffect.} + ## Style insensitive comparison. `a` can be an identifier or a + ## symbol. `a` may be wrapped in an export marker + ## (`nnkPostfix`) or quoted with backticks (`nnkAccQuoted`), + ## these nodes will be unwrapped. + +proc eqIdent*(a: string; b: NimNode): bool {.magic: "EqIdent", noSideEffect.} + ## Style insensitive comparison. `b` can be an identifier or a + ## symbol. `b` may be wrapped in an export marker + ## (`nnkPostfix`) or quoted with backticks (`nnkAccQuoted`), + ## these nodes will be unwrapped. + +proc eqIdent*(a: NimNode; b: NimNode): bool {.magic: "EqIdent", noSideEffect.} + ## Style insensitive comparison. `a` and `b` can be an + ## identifier or a symbol. Both may be wrapped in an export marker + ## (`nnkPostfix`) or quoted with backticks (`nnkAccQuoted`), + ## these nodes will be unwrapped. + +const collapseSymChoice = not defined(nimLegacyMacrosCollapseSymChoice) + proc treeTraverse(n: NimNode; res: var string; level = 0; isLisp = false, indented = false) {.benign.} = if level > 0: if indented: @@ -883,8 +906,21 @@ proc treeTraverse(n: NimNode; res: var string; level = 0; isLisp = false, indent res.add(" " & $n.strVal.newLit.repr) of nnkNone: assert false + elif n.kind in {nnkOpenSymChoice, nnkClosedSymChoice} and collapseSymChoice: + res.add(" " & $n.len) + if n.len > 0: + var allSameSymName = true + for i in 0.. 0: + res.add(" " & n[0].strVal.newLit.repr) else: res.add(".newTree(") for j in 0..`_. return node.copyNimTree() -proc eqIdent*(a: string; b: string): bool {.magic: "EqIdent", noSideEffect.} - ## Style insensitive comparison. - -proc eqIdent*(a: NimNode; b: string): bool {.magic: "EqIdent", noSideEffect.} - ## Style insensitive comparison. `a` can be an identifier or a - ## symbol. `a` may be wrapped in an export marker - ## (`nnkPostfix`) or quoted with backticks (`nnkAccQuoted`), - ## these nodes will be unwrapped. - -proc eqIdent*(a: string; b: NimNode): bool {.magic: "EqIdent", noSideEffect.} - ## Style insensitive comparison. `b` can be an identifier or a - ## symbol. `b` may be wrapped in an export marker - ## (`nnkPostfix`) or quoted with backticks (`nnkAccQuoted`), - ## these nodes will be unwrapped. - -proc eqIdent*(a: NimNode; b: NimNode): bool {.magic: "EqIdent", noSideEffect.} - ## Style insensitive comparison. `a` and `b` can be an - ## identifier or a symbol. Both may be wrapped in an export marker - ## (`nnkPostfix`) or quoted with backticks (`nnkAccQuoted`), - ## these nodes will be unwrapped. - proc expectIdent*(n: NimNode, name: string) {.since: (1,1).} = ## Check that `eqIdent(n,name)` holds true. If this is not the ## case, compilation aborts with an error message. This is useful diff --git a/tests/macros/tdumpast.nim b/tests/macros/tdumpast.nim index b6bcbe8f0..484b3c2f3 100644 --- a/tests/macros/tdumpast.nim +++ b/tests/macros/tdumpast.nim @@ -2,34 +2,69 @@ import macros -template plus(a, b: untyped): untyped {.dirty} = - a + b +block: + template plus(a, b: untyped): untyped {.dirty} = + a + b -macro call(e: untyped): untyped = - result = newCall("foo", newStrLitNode("bar")) + macro call(e: untyped): untyped = + result = newCall("foo", newStrLitNode("bar")) -macro dumpAST(n: untyped): untyped = - # dump AST as a side-effect and return the inner node - let n = callsite() - echo n.lispRepr - echo n.treeRepr + macro dumpAST(n: untyped): string = + var msg = "" + msg.add "lispRepr:\n" & n.lispRepr & "\n" + msg.add "treeRepr:\n" & n.treeRepr & "\n" - var plusAst = getAst(plus(1, 2)) - echo plusAst.lispRepr + var plusAst = getAst(plus(1, 2)) + msg.add "lispRepr:\n" & n.lispRepr & "\n" - var callAst = getAst(call(4)) - echo callAst.lispRepr + var callAst = getAst(call(4)) + msg.add "callAst.lispRepr:\n" & callAst.lispRepr & "\n" - var e = parseExpr("foo(bar + baz)") - echo e.lispRepr + var e = parseExpr("foo(bar + baz)") + msg.add "e.lispRepr:\n" & e.lispRepr & "\n" + result = msg.newLit - result = n[1] + let a = dumpAST: + proc add(x, y: int): int = + return x + y + const foo = 3 -dumpAST: - proc add(x, y: int): int = - return x + y - - proc sub(x, y: int): int = return x - y + doAssert a == """ +lispRepr: +(StmtList (ProcDef (Ident "add") (Empty) (Empty) (FormalParams (Ident "int") (IdentDefs (Ident "x") (Ident "y") (Ident "int") (Empty))) (Empty) (Empty) (StmtList (ReturnStmt (Infix (Ident "+") (Ident "x") (Ident "y"))))) (ConstSection (ConstDef (Ident "foo") (Empty) (IntLit 3)))) +treeRepr: +StmtList + ProcDef + Ident "add" + Empty + Empty + FormalParams + Ident "int" + IdentDefs + Ident "x" + Ident "y" + Ident "int" + Empty + Empty + Empty + StmtList + ReturnStmt + Infix + Ident "+" + Ident "x" + Ident "y" + ConstSection + ConstDef + Ident "foo" + Empty + IntLit 3 +lispRepr: +(StmtList (ProcDef (Ident "add") (Empty) (Empty) (FormalParams (Ident "int") (IdentDefs (Ident "x") (Ident "y") (Ident "int") (Empty))) (Empty) (Empty) (StmtList (ReturnStmt (Infix (Ident "+") (Ident "x") (Ident "y"))))) (ConstSection (ConstDef (Ident "foo") (Empty) (IntLit 3)))) +callAst.lispRepr: +(Call (Ident "foo") (StrLit "bar")) +e.lispRepr: +(Call (Ident "foo") (Infix (Ident "+") (Ident "bar") (Ident "baz"))) +""" macro fun() = let n = quote do: @@ -79,3 +114,48 @@ block: # repr doAssert repr2((1, 2, 3.0)) == "(1, 2, 3.0)" doAssert repr2((1)) == "(1)" doAssert repr2((1+2)) == "(1 + 2)" + +block: # treeRepr + macro treeRepr2(a: untyped): string = newLit a.treeRepr + macro treeRepr3(a: typed): string = newLit a.treeRepr + + doAssert treeRepr2(1+1 == 2) == """ +Infix + Ident "==" + Infix + Ident "+" + IntLit 1 + IntLit 1 + IntLit 2""" + + proc baz() = discard + proc baz(a: int) = discard + proc baz(a: float) = discard + + doAssert treeRepr3(baz()) == """ +Call + Sym "baz"""" + + let a = treeRepr3(block: + proc bar(a: auto) = baz()) + doAssert a == """ +BlockStmt + Empty + ProcDef + Sym "bar" + Empty + GenericParams + Sym "a:type" + FormalParams + Empty + IdentDefs + Sym "a" + Sym "auto" + Empty + Empty + Bracket + Empty + Empty + StmtList + Call + OpenSymChoice 3 "baz"""" diff --git a/tests/stdlib/tmacros.nim b/tests/stdlib/tmacros.nim index 52ea1e898..18cfdc75c 100644 --- a/tests/stdlib/tmacros.nim +++ b/tests/stdlib/tmacros.nim @@ -1,3 +1,9 @@ +#[ +xxx macros tests need to be reorganized to makes sure each API is tested once +See also: + tests/macros/tdumpast.nim for treeRepr + friends +]# + import std/macros block: # hasArgOfName From 295429f42595ce5c707456e657e4de6b2734df42 Mon Sep 17 00:00:00 2001 From: Rainbow Asteroids Date: Sat, 5 Jun 2021 03:07:26 -0400 Subject: [PATCH 2187/3768] add `dom.scrollIntoView` with options; refs #18093 (#18181) --- changelog.md | 2 ++ lib/js/dom.nim | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/changelog.md b/changelog.md index 1050e4916..7619ee6a3 100644 --- a/changelog.md +++ b/changelog.md @@ -335,6 +335,8 @@ - Added setCurrentException for JS backend. +- Added `dom.scrollIntoView` proc with options + ## Language changes - `nimscript` now handles `except Exception as e`. diff --git a/lib/js/dom.nim b/lib/js/dom.nim index 16fc96b68..2b2332020 100644 --- a/lib/js/dom.nim +++ b/lib/js/dom.nim @@ -1317,6 +1317,11 @@ type ready*: FontFaceSetReady onloadingdone*: proc(event: Event) + ScrollIntoViewOptions* = object + behavior*: cstring + `block`*: cstring + inline*: cstring + since (1, 3): type DomParser* = ref object @@ -1538,6 +1543,7 @@ proc removeAttribute*(n: Node, attr: cstring) proc removeAttributeNode*(n, attr: Node) proc replaceData*(n: Node, start, len: int, text: cstring) proc scrollIntoView*(n: Node) +proc scrollIntoView*(n: Node, options: ScrollIntoViewOptions) proc setAttribute*(n: Node, name, value: cstring) proc setAttributeNode*(n: Node, attr: Node) proc querySelector*(n: Node, selectors: cstring): Element From 9c0666e0bbb7af69e2d87e7e81302ed3d3f06412 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Sat, 5 Jun 2021 10:12:37 +0300 Subject: [PATCH 2188/3768] rst: remove `roSkipPounds` option (#18175) --- lib/packages/docutils/rst.nim | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index dae692fb7..336caa2df 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -201,8 +201,6 @@ from highlite import SourceLanguage, getSourceLanguage type RstParseOption* = enum ## options for the RST parser - roSkipPounds, ## skip ``#`` at line beginning (documentation - ## embedded in Nim comments) roSupportSmilies, ## make the RST parser support smilies like ``:)`` roSupportRawDirective, ## support the ``raw`` directive (don't support ## it for sandboxing) @@ -307,7 +305,6 @@ type buf*: cstring bufpos*: int line*, col*, baseIndent*: int - skipPounds*: bool adornmentLine*: bool escapeNext*: bool @@ -380,9 +377,6 @@ proc getIndentAux(L: var Lexer, start: int): int = else: inc pos elif L.buf[pos] == '\n': inc pos - if L.skipPounds: - if L.buf[pos] == '#': inc pos - if L.buf[pos] == '#': inc pos while true: case L.buf[pos] of ' ', '\v', '\f': @@ -447,26 +441,13 @@ proc rawGetTok(L: var Lexer, tok: var Token) = inc L.col tok.col = max(tok.col - L.baseIndent, 0) -proc getTokens(buffer: string, skipPounds: bool, tokens: var TokenSeq): int = +proc getTokens(buffer: string, tokens: var TokenSeq): int = var L: Lexer var length = tokens.len L.buf = cstring(buffer) L.line = 0 # skip UTF-8 BOM if L.buf[0] == '\xEF' and L.buf[1] == '\xBB' and L.buf[2] == '\xBF': inc L.bufpos, 3 - L.skipPounds = skipPounds - if skipPounds: - if L.buf[L.bufpos] == '#': - inc L.bufpos - inc result - if L.buf[L.bufpos] == '#': - inc L.bufpos - inc result - L.baseIndent = 0 - while L.buf[L.bufpos] == ' ': - inc L.bufpos - inc L.baseIndent - inc result while true: inc length setLen(tokens, length) @@ -2123,7 +2104,7 @@ proc parseSimpleTable(p: var RstParser): PRstNode = q.col = cols[j] q.line = line - 1 q.filename = p.filename - q.col += getTokens(row[j], false, q.tok) + q.col += getTokens(row[j], q.tok) b = newRstNode(rnTableDataCell) b.add(parseDoc(q)) a.add(b) @@ -2176,7 +2157,7 @@ proc parseMarkdownTable(p: var RstParser): PRstNode = q.col = p.col q.line = currentTok(p).line - 1 q.filename = p.filename - q.col += getTokens(getColContents(p, row[j]), false, q.tok) + q.col += getTokens(getColContents(p, row[j]), q.tok) b.add(parseDoc(q)) a.add(b) result.add(a) @@ -2584,7 +2565,6 @@ proc dirInclude(p: var RstParser): PRstNode = q.filename = path q.col += getTokens( inputString[startPosition..endPosition].strip(), - false, q.tok) # workaround a GCC bug; more like the interior pointer bug? #if find(q.tok[high(q.tok)].symbol, "\0\x01\x02") > 0: @@ -2948,7 +2928,7 @@ proc rstParse*(text, filename: string, initParser(p, newSharedState(options, findFile, msgHandler)) p.filename = filename p.line = line - p.col = column + getTokens(text, roSkipPounds in options, p.tok) + p.col = column + getTokens(text, p.tok) let unresolved = parseDoc(p) countTitles(p, unresolved) orderFootnotes(p) From 9c6259e55717acbc954140ef2bb0ca363971c8c7 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 5 Jun 2021 00:45:37 -0700 Subject: [PATCH 2189/3768] up to 20x faster jsonutils deserialization (#18183) * up to 20x faster jsonutils deserialization * noinline --- lib/std/jsonutils.nim | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/std/jsonutils.nim b/lib/std/jsonutils.nim index 60d78fea5..c141985d3 100644 --- a/lib/std/jsonutils.nim +++ b/lib/std/jsonutils.nim @@ -128,14 +128,14 @@ macro initCaseObject(T: typedesc, fun: untyped): untyped = `fun`(`key2`, typedesc[`typ`]) result.add newTree(nnkExprColonExpr, key, val) -proc checkJsonImpl(cond: bool, condStr: string, msg = "") = - if not cond: - # just pick 1 exception type for simplicity; other choices would be: - # JsonError, JsonParser, JsonKindError - raise newException(ValueError, msg) +proc raiseJsonException(condStr: string, msg: string) {.noinline.} = + # just pick 1 exception type for simplicity; other choices would be: + # JsonError, JsonParser, JsonKindError + raise newException(ValueError, condStr & " failed: " & msg) template checkJson(cond: untyped, msg = "") = - checkJsonImpl(cond, astToStr(cond), msg) + if not cond: + raiseJsonException(astToStr(cond), msg) proc hasField[T](obj: T, field: string): bool = for k, _ in fieldPairs(obj): From b9843b156dfc16f1f99cbee8fc8e55b7abd6c21b Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 5 Jun 2021 00:47:28 -0700 Subject: [PATCH 2190/3768] fix #18178, set a CI timeout for github action pipelines instead of the 6 hour default (#18179) --- .github/workflows/ci_docs.yml | 1 + .github/workflows/ci_packages.yml | 1 + 2 files changed, 2 insertions(+) diff --git a/.github/workflows/ci_docs.yml b/.github/workflows/ci_docs.yml index ddef94fbe..6ae228d2d 100644 --- a/.github/workflows/ci_docs.yml +++ b/.github/workflows/ci_docs.yml @@ -44,6 +44,7 @@ jobs: name: ${{ matrix.target }} runs-on: ${{ matrix.os }} + timeout-minutes: 60 # refs bug #18178 steps: - name: 'Checkout' diff --git a/.github/workflows/ci_packages.yml b/.github/workflows/ci_packages.yml index aeb29e147..529e6e8b6 100644 --- a/.github/workflows/ci_packages.yml +++ b/.github/workflows/ci_packages.yml @@ -11,6 +11,7 @@ jobs: batch: ["allowed_failures", "0_3", "1_3", "2_3"] # list of `index_num` name: '${{ matrix.os }} (batch: ${{ matrix.batch }})' runs-on: ${{ matrix.os }} + timeout-minutes: 60 # refs bug #18178 env: NIM_TEST_PACKAGES: "1" NIM_TESTAMENT_BATCH: ${{ matrix.batch }} From 9106ab7db19e22782c544c6362741dff4142820e Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 6 Jun 2021 00:53:22 -0700 Subject: [PATCH 2191/3768] make changeDetectedViaJsonBuildInstructions warning less verbose when json file invalid (eg due to spec change) (#18197) --- compiler/extccomp.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index ea2b09866..831acae68 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -993,7 +993,7 @@ proc changeDetectedViaJsonBuildInstructions*(conf: ConfigRef; jsonFile: Absolute var bcache: BuildCache try: bcache.fromJson(jsonFile.string.parseFile) except IOError, OSError, ValueError: - stderr.write "Warning: JSON processing failed for $#: $#\n" % [jsonFile.string, getCurrentExceptionMsg()] + stderr.write "Warning: JSON processing failed for: $#\n" % jsonFile.string return true if bcache.currentDir != getCurrentDir() or # fixes bug #16271 bcache.configFiles != conf.configFiles.mapIt(it.string) or From e7816a3e77ab0f821d66667dfc448f1898199f59 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Sun, 6 Jun 2021 10:55:20 +0300 Subject: [PATCH 2192/3768] update docgen documentation (#18192) * update docgen documentation * --fullhelp * Update doc/docgen.rst Co-authored-by: Timothee Cour * [skip ci] fix typos Co-authored-by: Timothee Cour --- changelog.md | 7 +++ doc/advopt.txt | 3 +- doc/docgen.rst | 123 ++++++++++++++++++++++++++++++------------------- 3 files changed, 84 insertions(+), 49 deletions(-) diff --git a/changelog.md b/changelog.md index 7619ee6a3..f141ea95a 100644 --- a/changelog.md +++ b/changelog.md @@ -449,6 +449,13 @@ ## Tool changes +- Latex doc generation is revised: output `.tex` files should be compiled + by `xelatex` (not by `pdflatex` as before). Now default Latex settings + provide support for Unicode and do better job for avoiding margin overflows. + +- Implemented `doc2tex` compiler command which converts documentation in + `.nim` files to Latex. + - The rst parser now supports markdown table syntax. Known limitations: - cell alignment is not supported, i.e. alignment annotations in a delimiter diff --git a/doc/advopt.txt b/doc/advopt.txt index e627d4768..e5b4c9568 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -6,7 +6,8 @@ Advanced commands: //e run a Nimscript file //rst2html convert a reStructuredText file to HTML use `--docCmd:skip` to skip compiling snippets - //rst2tex convert a reStructuredText file to TeX + //rst2tex convert a reStructuredText file to LaTeX + //doc2tex extract the documentation to a LaTeX file //jsondoc extract the documentation to a json file //ctags create a tags file //buildIndex build an index for the whole documentation diff --git a/doc/docgen.rst b/doc/docgen.rst index e9ff7f1e3..2074ee06f 100644 --- a/doc/docgen.rst +++ b/doc/docgen.rst @@ -14,12 +14,22 @@ Introduction ============ This document describes the `documentation generation tools`:idx: built into -the `Nim compiler `_, which can generate HTML and JSON output -from input .nim files and projects, as well as HTML and LaTeX from input RST +the `Nim compiler `_, which can generate HTML, Latex and JSON output +from input ``.nim`` files and projects, as well as HTML and LaTeX from input RST (reStructuredText) files. The output documentation will include the module dependencies (`import`), any top-level documentation comments (`##`), and exported symbols (`*`), including procedures, types, and variables. +=================== ====================== ============ ============== +command runs on... input format output format +=================== ====================== ============ ============== +`nim doc`:cmd: documentation comments ``.nim`` ``.html`` HTML +`nim doc2tex`:cmd: ″ ″ ``.tex`` LaTeX +`nim jsondoc`:cmd: ″ ″ ``.json`` JSON +`nim rst2html`:cmd: standalone rst files ``.rst`` ``.html`` HTML +`nim rst2tex`:cmd: ″ ″ ``.tex`` LaTeX +=================== ====================== ============ ============== + Quick start ----------- @@ -53,7 +63,8 @@ Any comments which are preceded by a double-hash (`##`), are interpreted as documentation. Comments are parsed as RST (see `reference `_), providing Nim module authors the ability to easily generate richly formatted -documentation with only their well-documented code. +documentation with only their well-documented code! +Basic Markdown syntax is also supported inside the doc comments. Example: @@ -99,32 +110,22 @@ won't influence RST formatting. ## ## Paragraph. -Nim file input ------------------ - -The following examples will generate documentation for the below contrived -*Nim* module, aptly named 'sample.nim' - -sample.nim: - -.. code-block:: nim - ## This module is a sample. - - import std/strutils - - proc helloWorld*(times: int) = - ## Takes an integer and outputs - ## as many "hello world!"s - - for i in 0 .. times-1: - echo "hello world!" - - helloWorld(5) - Document Types ============== +Example of Nim file input +------------------------- + +The following examples will generate documentation for this sample +*Nim* module, aptly named ``doc/docgen_sample.nim``: + +.. code:: nim + :file: docgen_sample.nim + +All the below commands save their output to ``htmldocs`` directory relative to +the directory of file; +hence the output for this sample will be in ``doc/htmldocs``. HTML ---- @@ -138,7 +139,7 @@ The `doc`:option: command: .. code:: cmd - nim doc sample + nim doc docgen_sample.nim Partial Output:: ... @@ -149,6 +150,27 @@ The full output can be seen here: `docgen_sample.html `_. It runs after semantic checking and includes pragmas attached implicitly by the compiler. +LaTeX +----- + +LaTeX files are intended to be converted to PDF, especially for offline +reading or making hard copies. (LaTeX output is oftentimes better than +HTML -> PDF conversion). + +The `doc2tex`:option: command: + +.. code:: cmd + + nim doc2tex docgen_sample.nim + cd htmldocs + xelatex docgen_sample.tex + xelatex docgen_sample.tex + # It is usually necessary to run `xelatex` 2 times (or even 3 times for + # large documents) to get all labels generated. + # That depends on this warning in the end of `xelatex` output: + # LaTeX Warning: Label(s) may have changed. Rerun to get cross-references right. + +The output is ``docgen_sample.pdf``. JSON ---- @@ -163,7 +185,7 @@ The `jsondoc`:option: command: .. code:: cmd - nim jsondoc sample + nim jsondoc docgen_sample.nim Output:: { @@ -189,7 +211,7 @@ The `jsondoc0`:option: command: .. code:: cmd - nim jsondoc0 sample + nim jsondoc0 docgen_sample.nim Output:: [ @@ -204,7 +226,7 @@ Output:: } ] -Note that the `jsondoc`:option: command outputs it's JSON without pretty-printing it, +Note that the `jsondoc`:option: command outputs its JSON without pretty-printing it, while `jsondoc0`:option: outputs pretty-printed JSON. Related Options @@ -217,9 +239,10 @@ Project switch nim doc --project filename.nim -This will recursively generate documentation of all nim modules imported +This will recursively generate documentation of all Nim modules imported into the input module that belong to the Nimble package that ``filename.nim`` -belongs to. +belongs to. The index files and the corresponding ``theindex.html`` will +also be generated. Index switch @@ -252,12 +275,13 @@ documented item in your source code pointing to the implementation of that item on a GitHub repository. You can click the link to see the implementation of the item. -The `git.commit`:option: switch overrides the hardcoded `devel` branch in config/nimdoc.cfg. +The `git.commit`:option: switch overrides the hardcoded `devel` branch in +``config/nimdoc.cfg``. This is useful to link to a different branch e.g. `--git.commit:master`:option:, or to a tag e.g. `--git.commit:1.2.3`:option: or a commit. Source URLs are generated as ``href="${url}/tree/${commit}/${path}#L${line}"`` -by default and this compatible with GitHub but not with GitLab. +by default and thus compatible with GitHub but not with GitLab. Similarly, `git.devel`:option: switch overrides the hardcoded `devel` branch for the `Edit` link which is also useful if you have a different working @@ -282,9 +306,10 @@ Other Input Formats The *Nim compiler* also has support for RST (reStructuredText) files with the `rst2html`:option: and `rst2tex`:option: commands. Documents like this one are -initially written in a dialect of RST which adds support for nim source code +initially written in a dialect of RST which adds support for Nim source code highlighting with the ``.. code-block:: nim`` prefix. ``code-block`` also -supports highlighting of C++ and some other c-like languages. +supports highlighting of a few other languages supported by the +`packages/docutils/highlite module `_. Usage: @@ -333,16 +358,16 @@ if they don't have any parameters. If there are parameters, they are represented by their types and will be comma-separated. To the plain symbol a suffix may be added depending on the type of the callable: -------------- -------------- -Callable type Suffix -------------- -------------- -proc *empty string* -macro ``.m`` -method ``.e`` -iterator ``.i`` -template ``.t`` -converter ``.c`` -------------- -------------- +============== ============== +Callable type Suffix +============== ============== +`proc`, `func` *empty string* +`macro` ``.m`` +`method` ``.e`` +`iterator` ``.i`` +`template` ``.t`` +`converter` ``.c`` +============== ============== The relationship of type to suffix is made by the proc `complexName` in the ``compiler/docgen.nim`` file. Here are some examples of complex names for @@ -422,10 +447,12 @@ final index, and TOC entries found in ``.nim`` files are discarded. Additional resources ==================== -`Nim Compiler User Guide `_ +* `Nim Compiler User Guide `_ -`RST Quick Reference -`_ +* Documentation for `rst module `_ -- Nim RST/Markdown parser. + +* `RST Quick Reference + `_ The output for HTML and LaTeX comes from the ``config/nimdoc.cfg`` and ``config/nimdoc.tex.cfg`` configuration files. You can add and modify these From e7aa41ff3f6820d8ada3fd650d523c7f7d584fda Mon Sep 17 00:00:00 2001 From: slangmgh <37659406+slangmgh@users.noreply.github.com> Date: Mon, 7 Jun 2021 17:42:32 +0800 Subject: [PATCH 2193/3768] fix 18186 with adding importc _umul128 (#18200) --- lib/system/dragonbox.nim | 3 ++- lib/system/schubfach.nim | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/system/dragonbox.nim b/lib/system/dragonbox.nim index db608c630..eec8749da 100644 --- a/lib/system/dragonbox.nim +++ b/lib/system/dragonbox.nim @@ -867,10 +867,11 @@ when defined(sizeof_Int128): return (hi, lo) elif defined(vcc) and defined(cpu64): + proc umul128(x, y: uint64, z: ptr uint64): uint64 {.importc: "_umul128", header: "".} proc mul128*(x: uint64; y: uint64): uint64x2 {.inline.} = var hi: uint64 = 0 var lo: uint64 = umul128(x, y, addr(hi)) - return (hi, lo) + return uint64x2(hi: hi, lo: lo) else: proc lo32*(x: uint64): uint32 {.inline.} = diff --git a/lib/system/schubfach.nim b/lib/system/schubfach.nim index c344b7425..94d3d3c25 100644 --- a/lib/system/schubfach.nim +++ b/lib/system/schubfach.nim @@ -147,6 +147,7 @@ when defined(sizeof_Int128): return y1 or uint32(y0 > 1) elif defined(vcc) and defined(cpu64): + proc umul128(x, y: uint64, z: ptr uint64): uint64 {.importc: "_umul128", header: "".} proc roundToOdd(g: uint64; cpHi: uint32): uint32 {.inline.} = var p1: uint64 = 0 var p0: uint64 = umul128(g, cpHi, addr(p1)) From 2ec52faae5ba5c233f977a8a4b997734c4ad83e7 Mon Sep 17 00:00:00 2001 From: Federico Ceratto Date: Mon, 7 Jun 2021 10:44:43 +0100 Subject: [PATCH 2194/3768] koch: print help if no command is given (#18199) --- koch.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/koch.nim b/koch.nim index 3d7276aee..665b5e042 100644 --- a/koch.nim +++ b/koch.nim @@ -708,4 +708,5 @@ when isMainModule: of "branchdone": branchDone() else: showHelp(success = false) break - of cmdEnd: break + of cmdEnd: + showHelp(success = false) From 429b1286325e7b6cf86ed4e29cea3e11a8c0e2df Mon Sep 17 00:00:00 2001 From: Bung Date: Mon, 7 Jun 2021 19:57:42 +0800 Subject: [PATCH 2195/3768] change mimedb stroe stringtable to orderedtable (#18065) * change mimedb stroe stringtable to orderedtable * Update lib/pure/mimetypes.nim Co-authored-by: Andreas Rumpf --- lib/pure/mimetypes.nim | 7 ++++--- tests/stdlib/tmimetypes.nim | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/pure/mimetypes.nim b/lib/pure/mimetypes.nim index c10739fec..6ae63a0ae 100644 --- a/lib/pure/mimetypes.nim +++ b/lib/pure/mimetypes.nim @@ -26,12 +26,12 @@ runnableExamples: doAssert m.getMimetype("fakext") == "text/fakelang" doAssert m.getMimetype("FaKeXT") == "text/fakelang" -import strtabs +import tables from strutils import startsWith, toLowerAscii, strip type MimeDB* = object - mimes: StringTableRef + mimes: OrderedTableRef[string, string] const mimes* = { "123": "application/vnd.lotus-1-2-3", @@ -1903,7 +1903,8 @@ const mimes* = { func newMimetypes*(): MimeDB = ## Creates a new Mimetypes database. The database will contain the most ## common mimetypes. - result.mimes = mimes.newStringTable() + {.cast(noSideEffect).}: + result.mimes = mimes.newOrderedTable() func getMimetype*(mimedb: MimeDB, ext: string, default = "text/plain"): string = ## Gets mimetype which corresponds to `ext`. Returns `default` if `ext` diff --git a/tests/stdlib/tmimetypes.nim b/tests/stdlib/tmimetypes.nim index cd41e614b..93c20d4a3 100644 --- a/tests/stdlib/tmimetypes.nim +++ b/tests/stdlib/tmimetypes.nim @@ -6,6 +6,7 @@ import std/mimetypes template main() = var m = newMimetypes() doAssert m.getMimetype("mp4") == "video/mp4" + doAssert m.getExt("application/json") == "json" # see also `runnableExamples`. # xxx we should have a way to avoid duplicating code between runnableExamples and tests From 5bad022d58e21f0da455fbd7d421cd26a2b5c812 Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 7 Jun 2021 21:32:37 +0800 Subject: [PATCH 2196/3768] alternative to #18185 (#18206) --- lib/core/rlocks.nim | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/core/rlocks.nim b/lib/core/rlocks.nim index 5f155262a..a1ee0a6ec 100644 --- a/lib/core/rlocks.nim +++ b/lib/core/rlocks.nim @@ -11,7 +11,10 @@ when not compileOption("threads") and not defined(nimdoc): - {.error: "Rlocks requires --threads:on option.".} + when false: + # make rlocks modlue consistent with locks module, + # so they can replace each other seamlessly. + {.error: "Rlocks requires --threads:on option.".} const insideRLocksModule = true include "system/syslocks" From 21f3b8539a2e440f7cb69502721b6c56070e122f Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 7 Jun 2021 17:22:35 +0200 Subject: [PATCH 2197/3768] fixes #18088 (#18209) --- lib/pure/nativesockets.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim index 605f62321..124afce56 100644 --- a/lib/pure/nativesockets.nim +++ b/lib/pure/nativesockets.nim @@ -438,7 +438,7 @@ proc getHostname*(): string {.tags: [ReadIOEffect].} = ## Returns the local hostname (not the FQDN) # https://tools.ietf.org/html/rfc1035#section-2.3.1 # https://tools.ietf.org/html/rfc2181#section-11 - const size = 64 + const size = 256 result = newString(size) when useWinVersion: let success = winlean.gethostname(result, size) From 51ab7ccec1ffa21cef67f1a75dea889653751cfc Mon Sep 17 00:00:00 2001 From: drtheuns Date: Mon, 7 Jun 2021 18:40:09 +0200 Subject: [PATCH 2198/3768] Fix JS error on index page and detect dark mode (#18191) * Fix JS error on index page and detect dark mode The theindex.html page doesn't have a dark mode switch so the main function will error because `toggleSwitch` is not defined. Checks have been added to prevent this from happening. Also add automatic detection of system settings for dark-mode. This could also be done with pure css, but then the dark mode variable declarations would have to be duplicated to work with the switch so I went with this approach. * Fix nimdoc tests * Fix rst2html tests --- config/nimdoc.cfg | 13 +++++++++---- nimdoc/rst2html/expected/rst_examples.html | 13 +++++++++---- nimdoc/test_out_index_dot_html/expected/index.html | 13 +++++++++---- .../test_out_index_dot_html/expected/theindex.html | 13 +++++++++---- .../testproject/expected/subdir/subdir_b/utils.html | 13 +++++++++---- nimdoc/testproject/expected/testproject.html | 13 +++++++++---- nimdoc/testproject/expected/theindex.html | 13 +++++++++---- 7 files changed, 63 insertions(+), 28 deletions(-) diff --git a/config/nimdoc.cfg b/config/nimdoc.cfg index ed1b346a2..cdf49197d 100644 --- a/config/nimdoc.cfg +++ b/config/nimdoc.cfg @@ -252,7 +252,6 @@ function main() { } } - const toggleSwitch = document.querySelector('.theme-switch input[type="checkbox"]'); function switchTheme(e) { if (e.target.checked) { document.documentElement.setAttribute('data-theme', 'dark'); @@ -263,13 +262,19 @@ function main() { } } - toggleSwitch.addEventListener('change', switchTheme, false); + const toggleSwitch = document.querySelector('.theme-switch input[type="checkbox"]'); + if (toggleSwitch !== null) { + toggleSwitch.addEventListener('change', switchTheme, false); + } - const currentTheme = localStorage.getItem('theme') ? localStorage.getItem('theme') : null; + var currentTheme = localStorage.getItem('theme'); + if (!currentTheme && window.matchMedia('(prefers-color-scheme: dark)').matches) { + currentTheme = 'dark'; + } if (currentTheme) { document.documentElement.setAttribute('data-theme', currentTheme); - if (currentTheme === 'dark') { + if (currentTheme === 'dark' && toggleSwitch !== null) { toggleSwitch.checked = true; } } diff --git a/nimdoc/rst2html/expected/rst_examples.html b/nimdoc/rst2html/expected/rst_examples.html index 5c434193e..68b796be7 100644 --- a/nimdoc/rst2html/expected/rst_examples.html +++ b/nimdoc/rst2html/expected/rst_examples.html @@ -34,7 +34,6 @@ function main() { } } - const toggleSwitch = document.querySelector('.theme-switch input[type="checkbox"]'); function switchTheme(e) { if (e.target.checked) { document.documentElement.setAttribute('data-theme', 'dark'); @@ -45,13 +44,19 @@ function main() { } } - toggleSwitch.addEventListener('change', switchTheme, false); + const toggleSwitch = document.querySelector('.theme-switch input[type="checkbox"]'); + if (toggleSwitch !== null) { + toggleSwitch.addEventListener('change', switchTheme, false); + } - const currentTheme = localStorage.getItem('theme') ? localStorage.getItem('theme') : null; + var currentTheme = localStorage.getItem('theme'); + if (!currentTheme && window.matchMedia('(prefers-color-scheme: dark)').matches) { + currentTheme = 'dark'; + } if (currentTheme) { document.documentElement.setAttribute('data-theme', currentTheme); - if (currentTheme === 'dark') { + if (currentTheme === 'dark' && toggleSwitch !== null) { toggleSwitch.checked = true; } } diff --git a/nimdoc/test_out_index_dot_html/expected/index.html b/nimdoc/test_out_index_dot_html/expected/index.html index bac39fd33..fac758c9b 100644 --- a/nimdoc/test_out_index_dot_html/expected/index.html +++ b/nimdoc/test_out_index_dot_html/expected/index.html @@ -34,7 +34,6 @@ function main() { } } - const toggleSwitch = document.querySelector('.theme-switch input[type="checkbox"]'); function switchTheme(e) { if (e.target.checked) { document.documentElement.setAttribute('data-theme', 'dark'); @@ -45,13 +44,19 @@ function main() { } } - toggleSwitch.addEventListener('change', switchTheme, false); + const toggleSwitch = document.querySelector('.theme-switch input[type="checkbox"]'); + if (toggleSwitch !== null) { + toggleSwitch.addEventListener('change', switchTheme, false); + } - const currentTheme = localStorage.getItem('theme') ? localStorage.getItem('theme') : null; + var currentTheme = localStorage.getItem('theme'); + if (!currentTheme && window.matchMedia('(prefers-color-scheme: dark)').matches) { + currentTheme = 'dark'; + } if (currentTheme) { document.documentElement.setAttribute('data-theme', currentTheme); - if (currentTheme === 'dark') { + if (currentTheme === 'dark' && toggleSwitch !== null) { toggleSwitch.checked = true; } } diff --git a/nimdoc/test_out_index_dot_html/expected/theindex.html b/nimdoc/test_out_index_dot_html/expected/theindex.html index aa9ca2645..34ddf8f6a 100644 --- a/nimdoc/test_out_index_dot_html/expected/theindex.html +++ b/nimdoc/test_out_index_dot_html/expected/theindex.html @@ -34,7 +34,6 @@ function main() { } } - const toggleSwitch = document.querySelector('.theme-switch input[type="checkbox"]'); function switchTheme(e) { if (e.target.checked) { document.documentElement.setAttribute('data-theme', 'dark'); @@ -45,13 +44,19 @@ function main() { } } - toggleSwitch.addEventListener('change', switchTheme, false); + const toggleSwitch = document.querySelector('.theme-switch input[type="checkbox"]'); + if (toggleSwitch !== null) { + toggleSwitch.addEventListener('change', switchTheme, false); + } - const currentTheme = localStorage.getItem('theme') ? localStorage.getItem('theme') : null; + var currentTheme = localStorage.getItem('theme'); + if (!currentTheme && window.matchMedia('(prefers-color-scheme: dark)').matches) { + currentTheme = 'dark'; + } if (currentTheme) { document.documentElement.setAttribute('data-theme', currentTheme); - if (currentTheme === 'dark') { + if (currentTheme === 'dark' && toggleSwitch !== null) { toggleSwitch.checked = true; } } diff --git a/nimdoc/testproject/expected/subdir/subdir_b/utils.html b/nimdoc/testproject/expected/subdir/subdir_b/utils.html index 826cad02a..6aa71e8c2 100644 --- a/nimdoc/testproject/expected/subdir/subdir_b/utils.html +++ b/nimdoc/testproject/expected/subdir/subdir_b/utils.html @@ -34,7 +34,6 @@ function main() { } } - const toggleSwitch = document.querySelector('.theme-switch input[type="checkbox"]'); function switchTheme(e) { if (e.target.checked) { document.documentElement.setAttribute('data-theme', 'dark'); @@ -45,13 +44,19 @@ function main() { } } - toggleSwitch.addEventListener('change', switchTheme, false); + const toggleSwitch = document.querySelector('.theme-switch input[type="checkbox"]'); + if (toggleSwitch !== null) { + toggleSwitch.addEventListener('change', switchTheme, false); + } - const currentTheme = localStorage.getItem('theme') ? localStorage.getItem('theme') : null; + var currentTheme = localStorage.getItem('theme'); + if (!currentTheme && window.matchMedia('(prefers-color-scheme: dark)').matches) { + currentTheme = 'dark'; + } if (currentTheme) { document.documentElement.setAttribute('data-theme', currentTheme); - if (currentTheme === 'dark') { + if (currentTheme === 'dark' && toggleSwitch !== null) { toggleSwitch.checked = true; } } diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html index 513dd507b..65d9f83f5 100644 --- a/nimdoc/testproject/expected/testproject.html +++ b/nimdoc/testproject/expected/testproject.html @@ -34,7 +34,6 @@ function main() { } } - const toggleSwitch = document.querySelector('.theme-switch input[type="checkbox"]'); function switchTheme(e) { if (e.target.checked) { document.documentElement.setAttribute('data-theme', 'dark'); @@ -45,13 +44,19 @@ function main() { } } - toggleSwitch.addEventListener('change', switchTheme, false); + const toggleSwitch = document.querySelector('.theme-switch input[type="checkbox"]'); + if (toggleSwitch !== null) { + toggleSwitch.addEventListener('change', switchTheme, false); + } - const currentTheme = localStorage.getItem('theme') ? localStorage.getItem('theme') : null; + var currentTheme = localStorage.getItem('theme'); + if (!currentTheme && window.matchMedia('(prefers-color-scheme: dark)').matches) { + currentTheme = 'dark'; + } if (currentTheme) { document.documentElement.setAttribute('data-theme', currentTheme); - if (currentTheme === 'dark') { + if (currentTheme === 'dark' && toggleSwitch !== null) { toggleSwitch.checked = true; } } diff --git a/nimdoc/testproject/expected/theindex.html b/nimdoc/testproject/expected/theindex.html index e6efee986..55f7f33c9 100644 --- a/nimdoc/testproject/expected/theindex.html +++ b/nimdoc/testproject/expected/theindex.html @@ -34,7 +34,6 @@ function main() { } } - const toggleSwitch = document.querySelector('.theme-switch input[type="checkbox"]'); function switchTheme(e) { if (e.target.checked) { document.documentElement.setAttribute('data-theme', 'dark'); @@ -45,13 +44,19 @@ function main() { } } - toggleSwitch.addEventListener('change', switchTheme, false); + const toggleSwitch = document.querySelector('.theme-switch input[type="checkbox"]'); + if (toggleSwitch !== null) { + toggleSwitch.addEventListener('change', switchTheme, false); + } - const currentTheme = localStorage.getItem('theme') ? localStorage.getItem('theme') : null; + var currentTheme = localStorage.getItem('theme'); + if (!currentTheme && window.matchMedia('(prefers-color-scheme: dark)').matches) { + currentTheme = 'dark'; + } if (currentTheme) { document.documentElement.setAttribute('data-theme', currentTheme); - if (currentTheme === 'dark') { + if (currentTheme === 'dark' && toggleSwitch !== null) { toggleSwitch.checked = true; } } From 47acc80f4e05053d2653c7218434bc7fad880741 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 9 Jun 2021 17:33:19 +0200 Subject: [PATCH 2199/3768] make strict funcs analysis smarter (#18219) * make strict funcs analysis smarter: varParam[i] = v is different from varParam[i][] = v * added a test case * Update compiler/varpartitions.nim Co-authored-by: Clyybber --- compiler/varpartitions.nim | 87 +++++++++++++++---------- tests/effects/tfuncs_cannot_mutate2.nim | 27 ++++++++ tests/effects/tstrict_funcs.nim | 17 +++++ 3 files changed, 95 insertions(+), 36 deletions(-) create mode 100644 tests/effects/tfuncs_cannot_mutate2.nim diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim index 810ce8586..9cf1e38ad 100644 --- a/compiler/varpartitions.nim +++ b/compiler/varpartitions.nim @@ -175,12 +175,18 @@ proc root(v: var Partitions; start: int): int = v.s[it].con = Connection(kind: dependsOn, parent: result) it = next -proc potentialMutation(v: var Partitions; s: PSym; info: TLineInfo) = +proc potentialMutation(v: var Partitions; s: PSym; level: int; info: TLineInfo) = let id = variableId(v, s) if id >= 0: let r = root(v, id) - let flags = if s.kind == skParam and isConstParam(s): - {isMutated, isMutatedDirectly} + let flags = if s.kind == skParam: + if isConstParam(s): + {isMutated, isMutatedDirectly} + elif s.typ.kind == tyVar and level <= 1: + # varParam[i] = v is different from varParam[i][] = v + {} + else: + {isMutated} else: {isMutated} @@ -339,36 +345,44 @@ proc pathExpr(node: PNode; owner: PSym): PNode = if result == nil and borrowFromConstExpr(n): result = n -proc allRoots(n: PNode; result: var seq[PSym]; followDotExpr = true) = +const + RootEscapes = 1000 # in 'p(r)' we don't know what p does to our poor root. + # so we assume a high level of indirections + +proc allRoots(n: PNode; result: var seq[(PSym, int)]; level: int) = case n.kind of nkSym: if n.sym.kind in {skParam, skVar, skTemp, skLet, skResult, skForVar}: - result.add(n.sym) + result.add((n.sym, level)) - of nkDotExpr, nkDerefExpr, nkBracketExpr, nkHiddenDeref, - nkCheckedFieldExpr, nkAddr, nkHiddenAddr: - if followDotExpr: - allRoots(n[0], result, followDotExpr) + of nkDerefExpr, nkHiddenDeref: + allRoots(n[0], result, level+1) + of nkBracketExpr, nkDotExpr, nkCheckedFieldExpr, nkAddr, nkHiddenAddr: + allRoots(n[0], result, level) of nkExprEqExpr, nkExprColonExpr, nkHiddenStdConv, nkHiddenSubConv, nkConv, nkStmtList, nkStmtListExpr, nkBlockStmt, nkBlockExpr, nkCast, nkObjUpConv, nkObjDownConv: if n.len > 0: - allRoots(n.lastSon, result, followDotExpr) + allRoots(n.lastSon, result, level) of nkCaseStmt, nkObjConstr: for i in 1.. 1: - allRoots(n[1], result, followDotExpr) + # XXX We really need the unwritten RFC here and distinguish between + # proc `[]`(x: var Container): var T # resizes the container + # and + # proc `[]`(x: Container): var T # only allows for slot mutation + allRoots(n[1], result, RootEscapes) else: let m = getMagic(n) case m @@ -387,12 +401,12 @@ proc allRoots(n: PNode; result: var seq[PSym]; followDotExpr = true) = let paramType = typ.n[i].typ if not paramType.isCompileTimeOnly and not typ.sons[0].isEmptyType and canAlias(paramType, typ.sons[0]): - allRoots(it, result, followDotExpr) + allRoots(it, result, RootEscapes) else: - allRoots(it, result, followDotExpr) + allRoots(it, result, RootEscapes) of mSlice: - allRoots(n[1], result, followDotExpr) + allRoots(n[1], result, level+1) else: discard "harmless operation" else: @@ -466,10 +480,10 @@ proc destMightOwn(c: var Partitions; dest: var VarIndex; n: PNode) = dest.flags.incl ownsData elif n.typ.kind in {tyLent, tyVar}: # we know the result is derived from the first argument: - var roots: seq[PSym] - allRoots(n[1], roots) + var roots: seq[(PSym, int)] + allRoots(n[1], roots, RootEscapes) for r in roots: - connect(c, dest.sym, r, n[1].info) + connect(c, dest.sym, r[0], n[1].info) else: let magic = if n[0].kind == nkSym: n[0].sym.magic else: mNone @@ -582,19 +596,19 @@ proc deps(c: var Partitions; dest, src: PNode) = if borrowChecking in c.goals: borrowingAsgn(c, dest, src) - var targets, sources: seq[PSym] - allRoots(dest, targets) - allRoots(src, sources) + var targets, sources: seq[(PSym, int)] + allRoots(dest, targets, 0) + allRoots(src, sources, 0) let destIsComplex = containsPointer(dest.typ) for t in targets: if dest.kind != nkSym and c.inNoSideEffectSection == 0: - potentialMutation(c, t, dest.info) + potentialMutation(c, t[0], t[1], dest.info) if destIsComplex: for s in sources: - connect(c, t, s, dest.info) + connect(c, t[0], s[0], dest.info) if cursorInference in c.goals and src.kind != nkEmpty: let d = pathExpr(dest, c.owner) @@ -602,7 +616,8 @@ proc deps(c: var Partitions; dest, src: PNode) = let vid = variableId(c, d.sym) if vid >= 0: destMightOwn(c, c.s[vid], src) - for s in sources: + for source in sources: + let s = source[0] if s == d.sym: discard "assignments like: it = it.next are fine" elif {sfGlobal, sfThread} * s.flags != {} or hasDisabledAsgn(c.g, s.typ): @@ -638,9 +653,9 @@ proc potentialMutationViaArg(c: var Partitions; n: PNode; callee: PType) = if constParameters in c.goals and tfNoSideEffect in callee.flags: discard "we know there are no hidden mutations through an immutable parameter" elif c.inNoSideEffectSection == 0 and containsPointer(n.typ): - var roots: seq[PSym] - allRoots(n, roots) - for r in roots: potentialMutation(c, r, n.info) + var roots: seq[(PSym, int)] + allRoots(n, roots, RootEscapes) + for r in roots: potentialMutation(c, r[0], r[1], n.info) proc traverse(c: var Partitions; n: PNode) = inc c.abstractTime @@ -682,12 +697,12 @@ proc traverse(c: var Partitions; n: PNode) = if i < L: let paramType = parameters[i].skipTypes({tyGenericInst, tyAlias}) if not paramType.isCompileTimeOnly and paramType.kind in {tyVar, tySink, tyOwned}: - var roots: seq[PSym] - allRoots(it, roots) + var roots: seq[(PSym, int)] + allRoots(it, roots, RootEscapes) if paramType.kind == tyVar: if c.inNoSideEffectSection == 0: - for r in roots: potentialMutation(c, r, it.info) - for r in roots: noCursor(c, r) + for r in roots: potentialMutation(c, r[0], r[1], it.info) + for r in roots: noCursor(c, r[0]) if borrowChecking in c.goals: # a call like 'result.add toOpenArray()' can also be a borrow @@ -703,10 +718,10 @@ proc traverse(c: var Partitions; n: PNode) = when false: # XXX investigate if this is required, it doesn't look # like it is! - var roots: seq[PSym] - allRoots(n[0], roots) + var roots: seq[(PSym, int)] + allRoots(n[0], roots, RootEscapes) for r in roots: - potentialMutation(c, r, it.info) + potentialMutation(c, r[0], r[1], it.info) of nkTupleConstr, nkBracket: for child in n: traverse(c, child) diff --git a/tests/effects/tfuncs_cannot_mutate2.nim b/tests/effects/tfuncs_cannot_mutate2.nim new file mode 100644 index 000000000..d5082e57b --- /dev/null +++ b/tests/effects/tfuncs_cannot_mutate2.nim @@ -0,0 +1,27 @@ +discard """ + errormsg: "'copy' can have side effects" + nimout: '''an object reachable from 'y' is potentially mutated +tfuncs_cannot_mutate2.nim(15, 7) the mutation is here +tfuncs_cannot_mutate2.nim(13, 10) is the statement that connected the mutation to the parameter +''' +""" + +{.experimental: "strictFuncs".} + +func copy[T](x: var openArray[T]; y: openArray[T]) = + for i in 0..high(x): + x[i] = y[i] + + x[0].a = nil + +type + R = ref object + a, b: R + data: string + +proc main = + var a, b: array[3, R] + b = [R(data: "a"), R(data: "b"), R(data: "c")] + copy a, b + +main() diff --git a/tests/effects/tstrict_funcs.nim b/tests/effects/tstrict_funcs.nim index 044bc7ee1..9d20f5d7e 100644 --- a/tests/effects/tstrict_funcs.nim +++ b/tests/effects/tstrict_funcs.nim @@ -27,3 +27,20 @@ block: var x = @[0, 1] let z = x &&& 2 + + +func copy[T](x: var openArray[T]; y: openArray[T]) = + for i in 0..high(x): + x[i] = y[i] + +type + R = ref object + a, b: R + data: string + +proc main = + var a, b: array[3, R] + b = [R(data: "a"), R(data: "b"), R(data: "c")] + copy a, b + +main() From 19918ceb2b099440cd3e43b6d7fff313de6a84a2 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 9 Jun 2021 14:46:00 -0700 Subject: [PATCH 2200/3768] update .github/ISSUE_TEMPLATE/bug_report.md, mention PRs, contributing, devel branch (#18224) --- .github/ISSUE_TEMPLATE/bug_report.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 9571896d8..807e50d35 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -7,6 +7,8 @@ assignees: '' --- +(Consider writing a PR targetting devel branch after filing this, see [contributing.html](https://nim-lang.github.io/Nim/contributing.html)). + Function `echo` outputs the wrong string. ### Example From 79ded694d7f913755dc7261c7468f04fe421e0b3 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 9 Jun 2021 22:00:34 -0700 Subject: [PATCH 2201/3768] avoid re-exporting options from std/wrapnils (#18222) --- lib/std/wrapnils.nim | 2 +- tests/stdlib/twrapnils.nim | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/std/wrapnils.nim b/lib/std/wrapnils.nim index 708faf4cf..ed0a79d79 100644 --- a/lib/std/wrapnils.nim +++ b/lib/std/wrapnils.nim @@ -25,7 +25,6 @@ runnableExamples: assert (?.f2.x2.x2).x3 == nil # this terminates ?. early from options import Option, isSome, get, option, unsafeGet, UnpackDefect -export options.get, options.isSome, options.isNone template fakeDot*(a: Option, b): untyped = ## See top-level example. @@ -90,6 +89,7 @@ macro `?.`*(a: untyped): auto = macro `??.`*(a: untyped): Option = ## Same as `?.` but returns an `Option`. runnableExamples: + import std/options type Foo = ref object x1: ref int x2: int diff --git a/tests/stdlib/twrapnils.nim b/tests/stdlib/twrapnils.nim index af0978762..956204355 100644 --- a/tests/stdlib/twrapnils.nim +++ b/tests/stdlib/twrapnils.nim @@ -1,4 +1,5 @@ import std/wrapnils +from std/options import get, isSome proc checkNotZero(x: float): float = doAssert x != 0 From 0a4858dc59b1464d4057822d60e888fa4403223e Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 10 Jun 2021 09:25:51 +0200 Subject: [PATCH 2202/3768] fixes #18220 (#18227) --- lib/pure/hashes.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim index 46b4fc948..1c49b0317 100644 --- a/lib/pure/hashes.nim +++ b/lib/pure/hashes.nim @@ -509,7 +509,7 @@ proc hashIgnoreCase*(sBuf: string, sPos, ePos: int): Hash = h = h !& ord(c) result = !$h -proc hash*[T: tuple | object | proc](x: T): Hash {.inline.} = +proc hash*[T: tuple | object | proc](x: T): Hash = ## Efficient `hash` overload. runnableExamples: # for `tuple|object`, `hash` must be defined for each component of `x`. From 8a9b20579e4afde6ccd515b9840ab2c68d0fe9e6 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 10 Jun 2021 01:27:12 -0700 Subject: [PATCH 2203/3768] update changelog for wrapnils (#18228) --- changelog.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/changelog.md b/changelog.md index f141ea95a..cc6f826ee 100644 --- a/changelog.md +++ b/changelog.md @@ -285,9 +285,6 @@ - Added `std/strbasics` for high performance string operations. Added `strip`, `setSlice`, `add(a: var string, b: openArray[char])`. - -- Added to `wrapnils` an option-like API via `??.`, `isSome`, `get`. - - `std/options` changed `$some(3)` to `"some(3)"` instead of `"Some(3)"` and `$none(int)` to `"none(int)"` instead of `"None[int]"`. From 7bf0404dd86ff094ebd3d5b4a39bc4c52b442593 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=B6hlich=20A?= Date: Thu, 10 Jun 2021 14:28:00 +0200 Subject: [PATCH 2204/3768] #18216 make moveDir work across partitions on windows (#18223) * return false if AccessDeniedError in tryMoveFSObject - fixes #18216 * add moveDir & moveFile tests * rename `isMoveDir` parameter to `isDir` --- lib/pure/os.nim | 44 ++++++++++++++++++++++++------------------- tests/stdlib/tos.nim | 45 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 19 deletions(-) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index c48d0d84f..4dd5c8d8b 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -2009,28 +2009,32 @@ proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect], n if not tryRemoveFile(file): raiseOSError(osLastError(), file) -proc tryMoveFSObject(source, dest: string): bool {.noWeirdTarget.} = - ## Moves a file or directory from `source` to `dest`. +proc tryMoveFSObject(source, dest: string, isDir: bool): bool {.noWeirdTarget.} = + ## Moves a file (or directory if `isDir` is true) from `source` to `dest`. ## - ## Returns false in case of `EXDEV` error. + ## Returns false in case of `EXDEV` error or `AccessDeniedError` on windows (if `isDir` is true). ## In case of other errors `OSError` is raised. ## Returns true in case of success. when defined(windows): when useWinUnicode: let s = newWideCString(source) let d = newWideCString(dest) - if moveFileExW(s, d, MOVEFILE_COPY_ALLOWED or MOVEFILE_REPLACE_EXISTING) == 0'i32: raiseOSError(osLastError(), $(source, dest)) + result = moveFileExW(s, d, MOVEFILE_COPY_ALLOWED or MOVEFILE_REPLACE_EXISTING) != 0'i32 else: - if moveFileExA(source, dest, MOVEFILE_COPY_ALLOWED or MOVEFILE_REPLACE_EXISTING) == 0'i32: raiseOSError(osLastError(), $(source, dest)) + result = moveFileExA(source, dest, MOVEFILE_COPY_ALLOWED or MOVEFILE_REPLACE_EXISTING) != 0'i32 else: - if c_rename(source, dest) != 0'i32: - let err = osLastError() - if err == EXDEV.OSErrorCode: - return false + result = c_rename(source, dest) == 0'i32 + + if not result: + let err = osLastError() + let isAccessDeniedError = + when defined(windows): + const AccessDeniedError = OSErrorCode(5) + isDir and err == AccessDeniedError else: - # see whether `strerror(errno)` is redundant with what raiseOSError already shows - raiseOSError(err, $(source, dest, strerror(errno))) - return true + err == EXDEV.OSErrorCode + if not isAccessDeniedError: + raiseOSError(err, $(source, dest)) proc moveFile*(source, dest: string) {.rtl, extern: "nos$1", tags: [ReadDirEffect, ReadIOEffect, WriteIOEffect], noWeirdTarget.} = @@ -2051,8 +2055,10 @@ proc moveFile*(source, dest: string) {.rtl, extern: "nos$1", ## * `removeFile proc <#removeFile,string>`_ ## * `tryRemoveFile proc <#tryRemoveFile,string>`_ - if not tryMoveFSObject(source, dest): - when not defined(windows): + if not tryMoveFSObject(source, dest, isDir = false): + when defined(windows): + doAssert false + else: # Fallback to copy & del copyFile(source, dest, {cfSymlinkAsIs}) try: @@ -2060,6 +2066,7 @@ proc moveFile*(source, dest: string) {.rtl, extern: "nos$1", except: discard tryRemoveFile(dest) raise + proc exitStatusLikeShell*(status: cint): cint = ## Converts exit code from `c_system` into a shell exit code. @@ -2614,11 +2621,10 @@ proc moveDir*(source, dest: string) {.tags: [ReadIOEffect, WriteIOEffect], noWei ## * `removeDir proc <#removeDir,string>`_ ## * `existsOrCreateDir proc <#existsOrCreateDir,string>`_ ## * `createDir proc <#createDir,string>`_ - if not tryMoveFSObject(source, dest): - when not defined(windows): - # Fallback to copy & del - copyDir(source, dest) - removeDir(source) + if not tryMoveFSObject(source, dest, isDir = true): + # Fallback to copy & del + copyDir(source, dest) + removeDir(source) proc createHardlink*(src, dest: string) {.noWeirdTarget.} = ## Create a hard link at `dest` which points to the item specified diff --git a/tests/stdlib/tos.nim b/tests/stdlib/tos.nim index b9769d05c..a4f030dde 100644 --- a/tests/stdlib/tos.nim +++ b/tests/stdlib/tos.nim @@ -261,6 +261,51 @@ block fileOperations: removeDir(dname) +block: # moveFile + let tempDir = getTempDir() / "D20210609T151608" + createDir(tempDir) + defer: removeDir(tempDir) + + writeFile(tempDir / "a.txt", "") + moveFile(tempDir / "a.txt", tempDir / "b.txt") + doAssert not fileExists(tempDir / "a.txt") + doAssert fileExists(tempDir / "b.txt") + removeFile(tempDir / "b.txt") + + createDir(tempDir / "moveFile_test") + writeFile(tempDir / "moveFile_test/a.txt", "") + moveFile(tempDir / "moveFile_test/a.txt", tempDir / "moveFile_test/b.txt") + doAssert not fileExists(tempDir / "moveFile_test/a.txt") + doAssert fileExists(tempDir / "moveFile_test/b.txt") + removeDir(tempDir / "moveFile_test") + + createDir(tempDir / "moveFile_test") + writeFile(tempDir / "a.txt", "") + moveFile(tempDir / "a.txt", tempDir / "moveFile_test/b.txt") + doAssert not fileExists(tempDir / "a.txt") + doAssert fileExists(tempDir / "moveFile_test/b.txt") + removeDir(tempDir / "moveFile_test") + +block: # moveDir + let tempDir = getTempDir() / "D20210609T161443" + createDir(tempDir) + defer: removeDir(tempDir) + + createDir(tempDir / "moveDir_test") + moveDir(tempDir / "moveDir_test/", tempDir / "moveDir_test_dest") + doAssert not dirExists(tempDir / "moveDir_test") + doAssert dirExists(tempDir / "moveDir_test_dest") + removeDir(tempDir / "moveDir_test_dest") + + createDir(tempDir / "moveDir_test") + writeFile(tempDir / "moveDir_test/a.txt", "") + moveDir(tempDir / "moveDir_test", tempDir / "moveDir_test_dest") + doAssert not dirExists(tempDir / "moveDir_test") + doAssert not fileExists(tempDir / "moveDir_test/a.txt") + doAssert dirExists(tempDir / "moveDir_test_dest") + doAssert fileExists(tempDir / "moveDir_test_dest/a.txt") + removeDir(tempDir / "moveDir_test_dest") + import times block modificationTime: # Test get/set modification times From 3481ff6172a2e11c990cc67a7fd74a2f0a2445d2 Mon Sep 17 00:00:00 2001 From: Kaushal Modi Date: Thu, 10 Jun 2021 08:29:03 -0400 Subject: [PATCH 2205/3768] Update nims.rst to reflect that -d:release is now allowed in config.nims (#18221) `-d:release` and `-d:danger` started working in `config.nims` after https://github.com/nim-lang/Nim/commit/df429fa28772e077faa30dd6e3a701abf48c7669 . --- doc/nims.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/doc/nims.rst b/doc/nims.rst index 5ae1d7baa..4141c3bdc 100644 --- a/doc/nims.rst +++ b/doc/nims.rst @@ -121,8 +121,8 @@ Here are few examples of using the `switch` proc: .. code-block:: nim # command-line: --opt:size switch("opt", "size") - # command-line: --define:foo or -d:foo - switch("define", "foo") + # command-line: --define:release or -d:release + switch("define", "release") # command-line: --forceBuild switch("forceBuild") @@ -132,13 +132,13 @@ above example can be rewritten as: .. code-block:: nim --opt:size - --define:foo + --define:release --forceBuild **Note**: In general, the *define* switches can also be set in -NimScripts using `switch` or `--`, as shown in above -examples. Only the `release` define (`-d:release`:option:) cannot be set -in NimScripts. +NimScripts using `switch` or `--`, as shown in above examples. Few +`define` switches such as `-d:strip`:option:, `-d:lto`:option: and +`-d:lto_incremental`:option: cannot be set in NimScripts. NimScript as a build tool From 2ea7287217ffe3ad6c3790ff1233845de2aedcf4 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 10 Jun 2021 16:49:17 +0200 Subject: [PATCH 2206/3768] view types: spec changes (#18226) * view types: spec changes * Update doc/manual_experimental.rst Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> * Update doc/manual_experimental.rst Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> --- compiler/varpartitions.nim | 5 +++-- doc/manual_experimental.rst | 31 ++++++++++++++----------------- tests/views/tdont_mutate.nim | 9 ++++----- 3 files changed, 21 insertions(+), 24 deletions(-) diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim index 9cf1e38ad..04c7c629d 100644 --- a/compiler/varpartitions.nim +++ b/compiler/varpartitions.nim @@ -572,7 +572,7 @@ proc borrowingAsgn(c: var Partitions; dest, src: PNode) = borrowFrom(c, dest.sym, src) elif dest.kind in {nkHiddenDeref, nkDerefExpr, nkBracketExpr}: case directViewType(dest[0].typ) - of mutableView: + of mutableView, immutableView: # we do not borrow, but we use the view to mutate the borrowed # location: let viewOrigin = pathExpr(dest, c.owner) @@ -580,12 +580,13 @@ proc borrowingAsgn(c: var Partitions; dest, src: PNode) = let vid = variableId(c, viewOrigin.sym) if vid >= 0: c.s[vid].flags.incl viewDoesMutate - of immutableView: + #[of immutableView: if dest.kind == nkBracketExpr and dest[0].kind == nkHiddenDeref and mutableParameter(dest[0][0]): discard "remains a mutable location anyhow" else: localError(c.g.config, dest.info, "attempt to mutate a borrowed location from an immutable view") + ]# of noView: discard "nothing to do" proc containsPointer(t: PType): bool = diff --git a/doc/manual_experimental.rst b/doc/manual_experimental.rst index 2c1043ecb..3157708da 100644 --- a/doc/manual_experimental.rst +++ b/doc/manual_experimental.rst @@ -475,8 +475,8 @@ Not nil annotation **Note:** This is an experimental feature. It can be enabled with `{.experimental: "notnil"}`. -All types for which `nil` is a valid value can be annotated with the `not -nil` annotation to exclude `nil` as a valid value: +All types for which `nil` is a valid value can be annotated with the +`not nil` annotation to exclude `nil` as a valid value: .. code-block:: nim {.experimental: "notnil"} @@ -1837,8 +1837,7 @@ with `--experimental:strictFuncs`:option:. A view type is a type that is or contains one of the following types: -- `var T` (mutable view into `T`) -- `lent T` (immutable view into `T`) +- `lent T` (view into `T`) - `openArray[T]` (pair of (pointer to array of `T`, size)) For example: @@ -1846,10 +1845,9 @@ For example: .. code-block:: nim type - View1 = var int - View2 = openArray[byte] - View3 = lent string - View4 = Table[openArray[char], int] + View1 = openArray[byte] + View2 = lent string + View3 = Table[openArray[char], int] Exceptions to this rule are types constructed via `ptr` or `proc`. @@ -1860,11 +1858,11 @@ For example, the following types are **not** view types: type NotView1 = proc (x: openArray[int]) NotView2 = ptr openArray[char] - NotView3 = ptr array[4, var int] + NotView3 = ptr array[4, lent int] -A *mutable* view type is a type that is or contains a `var T` type. -An *immutable* view type is a view type that is not a mutable view type. +The mutability aspect of a view type is not part of the type but part +of the locations it's derived from. More on this later. A *view* is a symbol (a let, var, const, etc.) that has a view type. @@ -1948,11 +1946,14 @@ details about how this is done for `var T`. A mutable view can borrow from a mutable location, an immutable view can borrow from both a mutable or an immutable location. +If a view borrows from a mutable location, the view can be used to update the +location. Otherwise it cannot be used for mutations. + The *duration* of a borrow is the span of commands beginning from the assignment to the view and ending with the last usage of the view. For the duration of the borrow operation, no mutations to the borrowed locations -may be performed except via the potentially mutable view that borrowed from the +may be performed except via the view that borrowed from the location. The borrowed location is said to be *sealed* during the borrow. .. code-block:: nim @@ -2064,11 +2065,7 @@ and `b` the location that is borrowed from. - The lifetime of `v` must not exceed `b`'s lifetime. Note: The lifetime of a parameter is the complete proc body. -- If `v` is a mutable view and `v` is used to actually mutate the - borrowed location, then `b` has to be a mutable location. - Note: If it is not actually used for mutation, borrowing a mutable view from an - immutable location is allowed! This allows for many important idioms and will be - justified in an upcoming RFC. +- If `v` is used for a mutation, `b` must be a mutable location too. - During `v`'s lifetime, `G(b)` can only be modified by `v` (and only if `v` is a mutable view). - If `v` is `result` then `b` has to be a location derived from the first diff --git a/tests/views/tdont_mutate.nim b/tests/views/tdont_mutate.nim index d243c7c7a..43acaaf71 100644 --- a/tests/views/tdont_mutate.nim +++ b/tests/views/tdont_mutate.nim @@ -9,8 +9,9 @@ import tables const Whitespace = {' ', '\t', '\n', '\r'} -proc split*(s: string, seps: set[char] = Whitespace, - maxsplit: int = -1): Table[int, openArray[char]] = +proc split*(s: string, seps: set[char] = Whitespace, maxsplit: int = -1): Table[int, openArray[char]] #[tt.Error + 'result' borrows from the immutable location 's' and attempts to mutate it + ]# = var last = 0 var splits = maxsplit result = initTable[int, openArray[char]]() @@ -22,9 +23,7 @@ proc split*(s: string, seps: set[char] = Whitespace, if splits == 0: last = len(s) result[first] = toOpenArray(s, first, last-1) - result[first][0] = 'c' #[tt.Error - attempt to mutate a borrowed location from an immutable view - ]# + result[first][0] = 'c' if splits == 0: break dec(splits) From f65f760dee2d4e19be826c256be50d34f8873d48 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 10 Jun 2021 18:19:20 +0200 Subject: [PATCH 2207/3768] fixes #15884 (#18230) * fixes #15884 * micro optimization --- compiler/renderer.nim | 2 ++ compiler/sem.nim | 9 +++++++++ compiler/semexprs.nim | 2 +- compiler/semstmts.nim | 2 +- compiler/varpartitions.nim | 6 +++++- 5 files changed, 18 insertions(+), 3 deletions(-) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 8bc3c3306..ac3f47948 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -1238,6 +1238,8 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = putWithSpace(g, tkBind, "bind") gsub(g, n, 0) of nkCheckedFieldExpr, nkHiddenAddr, nkHiddenDeref, nkStringToCString, nkCStringToString: + if renderIds in g.flags: + putWithSpace(g, tkAddr, $n.kind) gsub(g, n, 0) of nkLambda: putWithSpace(g, tkProc, "proc") diff --git a/compiler/sem.nim b/compiler/sem.nim index 72b8dfe10..879f6efc9 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -100,6 +100,15 @@ proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode = else: result = fitNodePostMatch(c, formal, result) +proc fitNodeForLocalVar(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode = + let a = fitNode(c, formal, arg, info) + if formal.kind in {tyVar, tyLent}: + #classifyViewType(formal) != noView: + result = newNodeIT(nkHiddenAddr, a.info, formal) + result.add a + else: + result = a + proc inferWithMetatype(c: PContext, formal: PType, arg: PNode, coerceDistincts = false): PNode diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index c839d04d9..ba8b93ae6 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1643,7 +1643,7 @@ proc takeImplicitAddr(c: PContext, n: PNode; isLent: bool): PNode = proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} = if le.kind == nkHiddenDeref: var x = le[0] - if (x.typ.kind in {tyVar, tyLent} or classifyViewType(x.typ) != noView) and x.kind == nkSym and x.sym.kind == skResult: + if x.kind == nkSym and x.sym.kind == skResult and (x.typ.kind in {tyVar, tyLent} or classifyViewType(x.typ) != noView): n[0] = x # 'result[]' --> 'result' n[1] = takeImplicitAddr(c, ri, x.typ.kind == tyLent) x.typ.flags.incl tfVarIsPtr diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index a2401b376..8f87ce83c 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -526,7 +526,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = else: # BUGFIX: ``fitNode`` is needed here! # check type compatibility between def.typ and typ - def = fitNode(c, typ, def, def.info) + def = fitNodeForLocalVar(c, typ, def, def.info) #changeType(def.skipConv, typ, check=true) else: typ = def.typ.skipTypes({tyStatic, tySink}).skipIntLit(c.idgen) diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim index 04c7c629d..697dd2b1d 100644 --- a/compiler/varpartitions.nim +++ b/compiler/varpartitions.nim @@ -519,13 +519,17 @@ const proc isConstSym(s: PSym): bool = result = s.kind in {skConst, skLet} or isConstParam(s) +proc toString(n: PNode): string = + if n.kind == nkEmpty: result = "" + else: result = $n + proc borrowFrom(c: var Partitions; dest: PSym; src: PNode) = const url = "see https://nim-lang.github.io/Nim/manual_experimental.html#view-types-algorithm-path-expressions for details" let s = pathExpr(src, c.owner) if s == nil: - localError(c.g.config, src.info, "cannot borrow from " & $src & ", it is not a path expression; " & url) + localError(c.g.config, src.info, "cannot borrow from " & src.toString & ", it is not a path expression; " & url) elif s.kind == nkSym: if dest.kind == skResult: if s.sym.kind != skParam or s.sym.position != 0: From 6b97889f44d06f6649b6547a4ffdb18b94d6e578 Mon Sep 17 00:00:00 2001 From: flywind Date: Fri, 11 Jun 2021 00:26:58 +0800 Subject: [PATCH 2208/3768] fix #9437(fix `re.replace` wrong behaviour) (#17546) * fix nim js cmp fails at CT * fix --- lib/impure/re.nim | 20 ++++++++++++++++++-- tests/stdlib/tre.nim | 9 ++++++--- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/lib/impure/re.nim b/lib/impure/re.nim index 40860cc88..78ea7b002 100644 --- a/lib/impure/re.nim +++ b/lib/impure/re.nim @@ -199,6 +199,18 @@ proc findBounds*(s: string, pattern: Regex, ## `(-1,0)` is returned. result = findBounds(cstring(s), pattern, matches, start, s.len) +proc findBoundsImpl(buf: cstring, pattern: Regex, + start = 0, bufSize = 0, flags = 0): tuple[first, last: int] = + var rtarray = initRtArray[cint](3) + let rawMatches = rtarray.getRawData + let res = pcre.exec(pattern.h, pattern.e, buf, bufSize.cint, start.cint, flags.int32, + cast[ptr cint](rawMatches), 3) + + if res < 0'i32: + result = (-1, 0) + else: + result = (int(rawMatches[0]), int(rawMatches[1]-1)) + proc findBounds*(buf: cstring, pattern: Regex, start = 0, bufSize: int): tuple[first, last: int] = ## returns the `first` and `last` position of `pattern` in `buf`, @@ -433,12 +445,16 @@ proc replace*(s: string, sub: Regex, by = ""): string = doAssert "var1=key; var2=key2".replace(re"(\w+)=(\w+)", "?") == "?; ?" result = "" var prev = 0 + var flags = int32(0) while prev < s.len: - var match = findBounds(s, sub, prev) + var match = findBoundsImpl(s.cstring, sub, prev, s.len, flags) + flags = 0 if match.first < 0: break add(result, substr(s, prev, match.first-1)) add(result, by) - if match.last + 1 == prev: break + if match.first > match.last: + # 0-len match + flags = pcre.NOTEMPTY_ATSTART prev = match.last + 1 add(result, substr(s, prev)) diff --git a/tests/stdlib/tre.nim b/tests/stdlib/tre.nim index ea1b5af32..2857c6c9e 100644 --- a/tests/stdlib/tre.nim +++ b/tests/stdlib/tre.nim @@ -99,10 +99,13 @@ proc testAll() = accum.add($x) doAssert(accum == @["a","b","c"]) - block: - # bug #9306 + block: # bug #9306 doAssert replace("bar", re"^", "foo") == "foobar" - doAssert replace("foo", re"", "-") == "-foo" doAssert replace("foo", re"$", "bar") == "foobar" + + block: # bug #9437 + doAssert replace("foo", re"", "-") == "-f-o-o-" + doAssert replace("ooo", re"o", "-") == "---" + testAll() From c64d9176190b3b691391f1645873dd4ca5b09933 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 10 Jun 2021 23:36:06 -0700 Subject: [PATCH 2209/3768] handle tyUserTypeClassInst in addDeclaredLoc (#18236) --- compiler/astmsgs.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/astmsgs.nim b/compiler/astmsgs.nim index 5be8dc38f..924f0ddea 100644 --- a/compiler/astmsgs.nim +++ b/compiler/astmsgs.nim @@ -13,7 +13,7 @@ proc addDeclaredLoc*(result: var string, conf: ConfigRef; typ: PType) = # xxx figure out how to resolve `tyGenericParam`, e.g. for # proc fn[T](a: T, b: T) = discard # fn(1.1, "a") - let typ = typ.skipTypes(abstractInst + {tyStatic} - {tyRange}) + let typ = typ.skipTypes(abstractInst + {tyStatic, tyUserTypeClassInst} - {tyRange}) result.add " [$1" % typ.kind.toHumanStr if typ.sym != nil: result.add " declared in " & toFileLineCol(conf, typ.sym.info) From 57609902c4438da2bcc156296128591a9906e922 Mon Sep 17 00:00:00 2001 From: Miran Date: Fri, 11 Jun 2021 10:07:23 +0200 Subject: [PATCH 2210/3768] use more meaningful name than "workaround14447" (#18237) --- compiler/lambdalifting.nim | 2 +- lib/system/iterators.nim | 2 +- testament/important_packages.nim | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index 6f43649a1..c555cedfe 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -292,7 +292,7 @@ proc markAsClosure(g: ModuleGraph; owner: PSym; n: PNode) = if illegalCapture(s): localError(g.config, n.info, ("'$1' is of type <$2> which cannot be captured as it would violate memory" & - " safety, declared here: $3; using '-d:nimWorkaround14447' helps in some cases") % + " safety, declared here: $3; using '-d:nimNoLentIterators' helps in some cases") % [s.name.s, typeToString(s.typ), g.config$s.info]) elif not (owner.typ.callConv == ccClosure or owner.typ.callConv == ccNimCall and tfExplicitCallConv notin owner.typ.flags): localError(g.config, n.info, "illegal capture '$1' because '$2' has the calling convention: <$3>" % diff --git a/lib/system/iterators.nim b/lib/system/iterators.nim index 0f79970b8..f23f7aa11 100644 --- a/lib/system/iterators.nim +++ b/lib/system/iterators.nim @@ -1,4 +1,4 @@ -when defined(nimHasLentIterators) and not defined(nimWorkaround14447): +when defined(nimHasLentIterators) and not defined(nimNoLentIterators): template lent2(T): untyped = lent T else: template lent2(T): untyped = T diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 975dd63ad..3d3c340eb 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -156,5 +156,5 @@ pkg "winim", allowFailure = true pkg "with" pkg "ws" pkg "yaml", "nim build" -pkg "zero_functional", "nim c -r -d:nimWorkaround14447 test.nim" +pkg "zero_functional", "nim c -r -d:nimNoLentIterators test.nim" pkg "zippy" From 2ca6169ba10dca704712b59339a24208e30e79ac Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 11 Jun 2021 11:09:00 +0200 Subject: [PATCH 2211/3768] added a test case ensuring exception inference continues to work --- tests/exception/texception_inference.nim | 32 ++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 tests/exception/texception_inference.nim diff --git a/tests/exception/texception_inference.nim b/tests/exception/texception_inference.nim new file mode 100644 index 000000000..7dd01cca1 --- /dev/null +++ b/tests/exception/texception_inference.nim @@ -0,0 +1,32 @@ +discard """ + output: '''good''' + cmd: "nim c --gc:orc -d:release $file" +""" + +type + Raising[T, E] = object + +proc foo[T, Errors](x: proc (x: Raising[T, Errors])) {.raises: Errors.} = + discard + +proc callback(x: Raising[int, ValueError]) = + echo "callback" + +proc xy() {.raises: [ValueError].} = + foo callback + +proc x[E]() {.raises: [E, IOError].} = + raise newException(E, "text here") + +try: + x[ValueError]() +except ValueError: + echo "good" + +proc callback2(x: Raising[int, IOError]) = + discard + +proc foo2[T, OtherErrors](x: proc(x: Raising[T, OtherErrors])) {.raises: [ValueError, OtherErrors].} = + discard + +foo2 callback2 From 897e50d5fe274850d4772612a90b325e33cca8a2 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 12 Jun 2021 12:35:12 -0700 Subject: [PATCH 2212/3768] getType now works with tyInferred (arising from concepts); refs #18220 (#18241) * getType now works with tyInferred (concepts); refs #18220 * avoid cast * add more docs --- compiler/vmdeps.nim | 2 +- tests/macros/tgettype.nim | 100 +++++++++++++++++++++++++++++--------- 2 files changed, 79 insertions(+), 23 deletions(-) diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim index b67e2e48a..1afda14b0 100644 --- a/compiler/vmdeps.nim +++ b/compiler/vmdeps.nim @@ -289,7 +289,7 @@ proc mapTypeToAstX(cache: IdentCache; t: PType; info: TLineInfo; of tyNot: result = mapTypeToBracket("not", mNot, t, info) of tyIterable: result = mapTypeToBracket("iterable", mIterableType, t, info) of tyAnything: result = atomicType("anything", mNone) - of tyInferred: assert false + of tyInferred: result = mapTypeToAstX(cache, t.lastSon, info, idgen, inst, allowRecursion) of tyStatic, tyFromExpr: if inst: if t.n != nil: result = t.n.copyTree diff --git a/tests/macros/tgettype.nim b/tests/macros/tgettype.nim index bd70a1c30..d91efe1fe 100644 --- a/tests/macros/tgettype.nim +++ b/tests/macros/tgettype.nim @@ -1,29 +1,85 @@ -discard """ -output: ''' -(ObjectTy (Empty) (Sym "Model") (RecList (Sym "name") (Sym "password"))) -(BracketExpr (Sym "typeDesc") (Sym "User")) -''' -""" -import macros +import std/macros +import stdtest/testutils -type - Model = object of RootObj - User = object of Model - name : string - password : string +# getType -macro testUser: string = - result = newLit(User.getType.lispRepr) +block: + type + Model = object of RootObj + User = object of Model + name : string + password : string -macro testGeneric(T: typedesc[Model]): string= - result = newLit(T.getType.lispRepr) + macro testUser: string = + result = newLit(User.getType.lispRepr) -echo testUser -echo User.testGeneric + macro testGeneric(T: typedesc[Model]): string= + result = newLit(T.getType.lispRepr) -macro assertVoid(e: typed): untyped = - assert(getTypeInst(e).typeKind == ntyVoid) + doAssert testUser == """(ObjectTy (Empty) (Sym "Model") (RecList (Sym "name") (Sym "password")))""" + doAssert User.testGeneric == """(BracketExpr (Sym "typeDesc") (Sym "User"))""" -proc voidProc() = discard + macro assertVoid(e: typed): untyped = + assert(getTypeInst(e).typeKind == ntyVoid) -assertVoid voidProc() + proc voidProc() = discard + + assertVoid voidProc() + +block: + # refs #18220; not an actual solution (yet) but at least shows what's currently + # possible + + type Callable1[R, T, U] = concept fn + fn(default(T)) is R + fn is U + + # note that typetraits.arity doesn't work + macro arity(a: typed): int = + # number of params + # this is not production code! + let a2 = a.getType[1] # this used to crash nim, with: `vmdeps.nim(292, 25) `false`` + newLit a2.len - 1 + + type Callable2[R, T, U] = concept fn + fn(default(T)) is R + fn is U + arity(U) == 2 + + proc map1[T, R, U](a: T, fn: Callable1[R, T, U]): R = + let fn = U(fn) + # `cast[U](fn)` would also work; + # this is currently needed otherwise, sigmatch errors with: + # Error: attempting to call routine: 'fn' + # found 'fn' [param declared in tgettype.nim(53, 28)] + # this can be fixed in future work + fn(a) + + proc map2[T, R, U](a: T, fn: Callable2[R, T, U]): R = + let fn = U(fn) + fn(a) + + proc fn1(a: int, a2 = 'x'): string = $(a, a2, "fn1") + proc fn2(a: int, a2 = "zoo"): string = $(a, a2, "fn2") + proc fn3(a: int, a2 = "zoo2"): string = $(a, a2, "fn3") + proc fn4(a: int): string {.inline.} = $(a, "fn4") + proc fn5(a: int): string = $(a, "fn5") + + assertAll: + # Callable1 + 1.map1(fn1) == """(1, 'x', "fn1")""" + 1.map1(fn2) == """(1, "zoo", "fn2")""" + 1.map1(fn3) == """(1, "zoo", "fn3")""" + # fn3's optional param is not honored, because fn3 and fn2 yield same + # generic instantiation; this is a caveat with this approach + # There are several possible ways to improve things in future work. + 1.map1(fn4) == """(1, "fn4")""" + 1.map1(fn5) == """(1, "fn5")""" + + # Callable2; prevents passing procs with optional params to avoid above + # mentioned caveat, but more restrictive + not compiles(1.map2(fn1)) + not compiles(1.map2(fn2)) + not compiles(1.map2(fn3)) + 1.map2(fn4) == """(1, "fn4")""" + 1.map2(fn5) == """(1, "fn5")""" From c871e22da2ad8f9caf82fdba43fccb7230d726e1 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 12 Jun 2021 22:32:47 -0700 Subject: [PATCH 2213/3768] fix #7717 roundtrip float to string; fix `parseFloat` for js (#18248) * refs #7717 roundtrip float to string * make parseFloat more correct * improve float tests * improve float tests * cleanup --- lib/system/jssys.nim | 99 +++++++++++++++++++---------------------- tests/float/tfloat6.nim | 28 ------------ tests/float/tfloats.nim | 84 ++++++++++++++++++++++++++++++++++ 3 files changed, 130 insertions(+), 81 deletions(-) delete mode 100644 tests/float/tfloat6.nim create mode 100644 tests/float/tfloats.nim diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim index 371cb7962..7cb5652c5 100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -716,19 +716,23 @@ proc tenToThePowerOf(b: int): BiggestFloat = const IdentChars = {'a'..'z', 'A'..'Z', '0'..'9', '_'} -# XXX use JS's native way here -proc nimParseBiggestFloat(s: string, number: var BiggestFloat, start = 0): int {. - compilerproc.} = - var - esign = 1.0 - sign = 1.0 - i = start - exponent: int - flags: int - number = 0.0 + +proc parseFloatNative(a: string): float = + let a2 = a.cstring + asm """ + `result` = Number(`a2`); + """ + +#[ +xxx how come code like this doesn't give IndexDefect ? +let z = s[10000] == 'a' +]# +proc nimParseBiggestFloat(s: string, number: var BiggestFloat, start: int): int {.compilerproc.} = + var sign: bool + var i = start if s[i] == '+': inc(i) elif s[i] == '-': - sign = -1.0 + sign = true inc(i) if s[i] == 'N' or s[i] == 'n': if s[i+1] == 'A' or s[i+1] == 'a': @@ -741,51 +745,40 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat, start = 0): int { if s[i+1] == 'N' or s[i+1] == 'n': if s[i+2] == 'F' or s[i+2] == 'f': if s[i+3] notin IdentChars: - number = Inf*sign + number = if sign: -Inf else: Inf return i+3 - start return 0 - while s[i] in {'0'..'9'}: - # Read integer part - flags = flags or 1 - number = number * 10.0 + toFloat(ord(s[i]) - ord('0')) - inc(i) - while s[i] == '_': inc(i) - # Decimal? - if s[i] == '.': - var hd = 1.0 - inc(i) - while s[i] in {'0'..'9'}: - # Read fractional part - flags = flags or 2 - number = number * 10.0 + toFloat(ord(s[i]) - ord('0')) - hd = hd * 10.0 - inc(i) - while s[i] == '_': inc(i) - number = number / hd # this complicated way preserves precision - # Again, read integer and fractional part - if flags == 0: return 0 - # Exponent? - if s[i] in {'e', 'E'}: - inc(i) - if s[i] == '+': - inc(i) - elif s[i] == '-': - esign = -1.0 - inc(i) - if s[i] notin {'0'..'9'}: - return 0 - while s[i] in {'0'..'9'}: - exponent = exponent * 10 + ord(s[i]) - ord('0') - inc(i) - while s[i] == '_': inc(i) - # Calculate Exponent - let hd = tenToThePowerOf(exponent) - if esign > 0.0: number = number * hd - else: number = number / hd - # evaluate sign - number = number * sign - result = i - start + var buf: string + # we could also use an `array[char, N]` buffer to avoid reallocs, or + # use a 2-pass algorithm that first computes the length. + if sign: buf.add '-' + template addInc = + buf.add s[i] + inc(i) + template eatUnderscores = + while s[i] == '_': inc(i) + while s[i] in {'0'..'9'}: # Read integer part + buf.add s[i] + inc(i) + eatUnderscores() + if s[i] == '.': # Decimal? + addInc() + while s[i] in {'0'..'9'}: # Read fractional part + addInc() + eatUnderscores() + # Again, read integer and fractional part + if buf.len == ord(sign): return 0 + if s[i] in {'e', 'E'}: # Exponent? + addInc() + if s[i] == '+': inc(i) + elif s[i] == '-': addInc() + if s[i] notin {'0'..'9'}: return 0 + while s[i] in {'0'..'9'}: + addInc() + eatUnderscores() + number = parseFloatNative(buf) + result = i - start # Workaround for IE, IE up to version 11 lacks 'Math.trunc'. We produce # 'Math.trunc' for Nim's ``div`` and ``mod`` operators: diff --git a/tests/float/tfloat6.nim b/tests/float/tfloat6.nim deleted file mode 100644 index fa8fc9da8..000000000 --- a/tests/float/tfloat6.nim +++ /dev/null @@ -1,28 +0,0 @@ -discard """ - output: ''' -0.000001 : 0.000001 -0.000001 : 0.000001 -0.001 : 0.001 -0.000001 : 0.000001 -0.000001 : 0.000001 -10.000001 : 10.000001 -100.000001 : 100.000001 -''' - disabled: "windows" -""" - -import strutils - -echo "0.00_0001".parseFloat(), " : ", 1E-6 -echo "0.00__00_01".parseFloat(), " : ", 1E-6 -echo "0.0_01".parseFloat(), " : ", 0.001 -echo "0.00_000_1".parseFloat(), " : ", 1E-6 -echo "0.00000_1".parseFloat(), " : ", 1E-6 - -echo "1_0.00_0001".parseFloat(), " : ", 10.000001 -echo "1__00.00_0001".parseFloat(), " : ", 1_00.000001 - -# bug #18148 - -var a = 1.1'f32 -doAssert $a == "1.1", $a # fails diff --git a/tests/float/tfloats.nim b/tests/float/tfloats.nim new file mode 100644 index 000000000..406ddb6d9 --- /dev/null +++ b/tests/float/tfloats.nim @@ -0,0 +1,84 @@ +discard """ + targets: "c cpp js" +""" +# disabled: "windows" + +#[ +xxx merge all or most float tests into this file +]# + +import std/[fenv, math, strutils] + +proc equalsOrNaNs(a, b: float): bool = + if isNaN(a): isNaN(b) + elif a == 0: + b == 0 and signbit(a) == signbit(b) + else: + a == b + +template reject(a) = + doAssertRaises(ValueError): discard parseFloat(a) + +template main = + block: + proc test(a: string, b: float) = + let a2 = a.parseFloat + doAssert equalsOrNaNs(a2, b), $(a, a2, b) + test "0.00_0001", 1E-6 + test "0.00__00_01", 1E-6 + test "0.0_01", 0.001 + test "0.00_000_1", 1E-6 + test "0.00000_1", 1E-6 + test "1_0.00_0001", 10.000001 + test "1__00.00_0001", 1_00.000001 + test "inf", Inf + test "-inf", -Inf + test "-Inf", -Inf + test "-INF", -Inf + test "NaN", NaN + test "-nan", NaN + test ".1", 0.1 + test "-.1", -0.1 + test "-0", -0.0 + when false: # pending bug #18246 + test "-0", -0.0 + test ".1e-1", 0.1e-1 + test "0_1_2_3.0_1_2_3E+0_1_2", 123.0123e12 + test "0_1_2.e-0", 12e0 + test "0_1_2e-0", 12e0 + test "-0e0", -0.0 + test "-0e-0", -0.0 + + reject "a" + reject "" + reject "e1" + reject "infa" + reject "infe1" + reject "_" + reject "1e" + + when false: # gray area; these numbers should probably be invalid + reject "1_" + reject "1_.0" + reject "1.0_" + + block: # bug #18148 + var a = 1.1'f32 + doAssert $a == "1.1", $a # was failing + + block: # bug #7717 + proc test(f: float) = + let f2 = $f + let f3 = parseFloat(f2) + doAssert equalsOrNaNs(f, f3), $(f, f2, f3) + + test 1.0 + epsilon(float64) + test 1000000.0000000123 + test log2(100000.0) + test maximumPositiveValue(float32) + test maximumPositiveValue(float64) + test minimumPositiveValue(float32) + test minimumPositiveValue(float64) + +static: main() +main() From a266c549212d1d6e09dbfa01344edbca8b2f6222 Mon Sep 17 00:00:00 2001 From: Federico Ceratto Date: Sun, 13 Jun 2021 16:50:04 +0100 Subject: [PATCH 2214/3768] Improve httpClient docs on SSL cert verification (#15201) * Improve httpClient docs on SSL cert verification Cert verification is enabled by default after CVE-2021-29495 * Update httpclient.nim Co-authored-by: Dominik Picheta --- lib/pure/httpclient.nim | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 0dbf8a045..2f9f1913f 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -115,7 +115,7 @@ ## ## SSL/TLS support ## =============== -## This requires the OpenSSL library, fortunately it's widely used and installed +## This requires the OpenSSL library. Fortunately it's widely used and installed ## on many operating systems. httpclient will use SSL automatically if you give ## any of the functions a url with the `https` schema, for example: ## `https://github.com/`. @@ -123,12 +123,25 @@ ## You will also have to compile with `ssl` defined like so: ## `nim c -d:ssl ...`. ## -## Certificate validation is NOT performed by default. -## This will change in the future. +## Certificate validation is performed by default. ## ## A set of directories and files from the `ssl_certs `_ ## module are scanned to locate CA certificates. ## +## Example of setting SSL verification parameters in a new client: +## +## .. code-block:: Nim +## import httpclient +## var client = newHttpClient(sslContext=newContext(verifyMode=CVerifyPeer)) +## +## There are three options for verify mode: +## +## * ``CVerifyNone``: certificates are not verified; +## * ``CVerifyPeer``: certificates are verified; +## * ``CVerifyPeerUseEnvVars``: certificates are verified and the optional +## environment variables SSL_CERT_FILE and SSL_CERT_DIR are also used to +## locate certificates +## ## See `newContext `_ to tweak or disable certificate validation. ## ## Timeouts From 065243dc5988fbfcbed8236e19a588b98d63b199 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 13 Jun 2021 23:21:18 -0700 Subject: [PATCH 2215/3768] followup #17777: declaredloc field error msgs now work with generics (#18259) * followup #17777: declaredloc field error msgs now work with generics * fix tests * cleanup --- compiler/astmsgs.nim | 5 ++++ compiler/semcall.nim | 5 ++-- tests/errmsgs/tundeclared_field.nim | 40 ++++++++++++++++++----------- 3 files changed, 33 insertions(+), 17 deletions(-) diff --git a/compiler/astmsgs.nim b/compiler/astmsgs.nim index 924f0ddea..90bdce7cf 100644 --- a/compiler/astmsgs.nim +++ b/compiler/astmsgs.nim @@ -2,6 +2,11 @@ import std/strutils import options, ast, msgs +proc typSym*(t: PType): PSym = + result = t.sym + if result == nil and t.kind == tyGenericInst: # this might need to be refined + result = t[0].sym + proc addDeclaredLoc*(result: var string, conf: ConfigRef; sym: PSym) = result.add " [$1 declared in $2]" % [sym.kind.toHumanStr, toFileLineCol(conf, sym.info)] diff --git a/compiler/semcall.nim b/compiler/semcall.nim index facaa89f7..b56a1dbac 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -326,7 +326,7 @@ proc getMsgDiagnostic(c: PContext, flags: TExprFlags, n, f: PNode): string = let ident = considerQuotedIdent(c, f, n).s if {nfDotField, nfExplicitCall} * n.flags == {nfDotField}: - let sym = n[1].typ.sym + let sym = n[1].typ.typSym var typeHint = "" if sym == nil: # Perhaps we're in a `compiles(foo.bar)` expression, or @@ -337,7 +337,8 @@ proc getMsgDiagnostic(c: PContext, flags: TExprFlags, n, f: PNode): string = discard else: typeHint = " for type " & getProcHeader(c.config, sym) - result = errUndeclaredField % ident & typeHint & " " & result + let suffix = if result.len > 0: " " & result else: "" + result = errUndeclaredField % ident & typeHint & suffix else: if result.len == 0: result = errUndeclaredRoutine % ident else: result = errBadRoutine % [ident, result] diff --git a/tests/errmsgs/tundeclared_field.nim b/tests/errmsgs/tundeclared_field.nim index 2b274ae58..5668050e0 100644 --- a/tests/errmsgs/tundeclared_field.nim +++ b/tests/errmsgs/tundeclared_field.nim @@ -2,38 +2,48 @@ discard """ cmd: '''nim check --hints:off $file''' action: reject nimout: ''' -tundeclared_field.nim(25, 12) Error: undeclared field: 'bad' for type tundeclared_field.A [type declared in tundeclared_field.nim(22, 8)] -tundeclared_field.nim(30, 16) Error: undeclared field: 'bad' for type tundeclared_field.A [type declared in tundeclared_field.nim(28, 8)] -tundeclared_field.nim(36, 4) Error: undeclared field: 'bad' for type tundeclared_field.A [type declared in tundeclared_field.nim(33, 8)] -tundeclared_field.nim(40, 13) Error: cannot instantiate Foo [type declared in tundeclared_field.nim(39, 8)] +tundeclared_field.nim(25, 12) Error: undeclared field: 'bad1' for type tundeclared_field.A [type declared in tundeclared_field.nim(22, 8)] +tundeclared_field.nim(30, 17) Error: undeclared field: 'bad2' for type tundeclared_field.A [type declared in tundeclared_field.nim(28, 8)] +tundeclared_field.nim(36, 4) Error: undeclared field: 'bad3' for type tundeclared_field.A [type declared in tundeclared_field.nim(33, 8)] +tundeclared_field.nim(42, 12) Error: undeclared field: 'bad4' for type tundeclared_field.B [type declared in tundeclared_field.nim(39, 8)] +tundeclared_field.nim(43, 4) Error: undeclared field: 'bad5' for type tundeclared_field.B [type declared in tundeclared_field.nim(39, 8)] +tundeclared_field.nim(44, 23) Error: undeclared field: 'bad6' for type tundeclared_field.B [type declared in tundeclared_field.nim(39, 8)] +tundeclared_field.nim(46, 19) Error: undeclared field: 'bad7' for type tundeclared_field.B [type declared in tundeclared_field.nim(39, 8)] +tundeclared_field.nim(50, 13) Error: cannot instantiate Foo [type declared in tundeclared_field.nim(49, 8)] ''' """ - - - - - - - - +#[ +xxx in future work, generic instantiations (e.g. `B[int]`) should be shown with their instantiation instead of `tundeclared_field.B`, +maybe using TPreferedDesc.preferResolved or preferMixed +]# # line 20 block: type A = object a0: int var a: A - discard a.bad + discard a.bad1 block: type A = object a0: int - var a = A(bad: 0) + var a = A(bad2: 0) block: type A = object a0: int var a: A - a.bad = 0 + a.bad3 = 0 + +block: + type B[T] = object + b0: int + var b: B[int] + discard b.bad4 + b.bad5 = 0 + var b2 = B[int](bad6: 0) + type Bi = B[int] + var b3 = Bi(bad7: 0) block: type Foo[T: SomeInteger] = object From e1e8af535ec195e5749dffa728add95770e5cbd7 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 13 Jun 2021 23:51:40 -0700 Subject: [PATCH 2216/3768] merge BuildMode into SuccessX, remove code duplication w drnim, add useful info to successx, add gc to compilesettings (#18252) * merge BuildMode into SuccessX, add more info * refactor duplicated with drnim * fixup * address comment --- compiler/lineinfos.nim | 7 +++--- compiler/main.nim | 40 ++++++---------------------------- compiler/msgs.nim | 43 +++++++++++++++++++++++++++++++++---- compiler/options.nim | 13 +++++++++-- compiler/vmops.nim | 1 + doc/nimc.rst | 1 - drnim/drnim.nim | 19 +--------------- lib/std/compilesettings.nim | 1 + testament/testament.nim | 3 +-- tests/misc/trunner.nim | 2 +- 10 files changed, 64 insertions(+), 66 deletions(-) diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index bf81aed02..da0a8d623 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -65,7 +65,7 @@ type warnFileChanged = "FileChanged", warnUser = "User", - hintSuccess = "Success", hintSuccessX = "SuccessX", hintBuildMode = "BuildMode", + hintSuccess = "Success", hintSuccessX = "SuccessX", hintCC = "CC", hintLineTooLong = "LineTooLong", hintXDeclaredButNotUsed = "XDeclaredButNotUsed", hintXCannotRaiseY = "XCannotRaiseY", hintConvToBaseNotNeeded = "ConvToBaseNotNeeded", @@ -145,8 +145,7 @@ const warnUser: "$1", hintSuccess: "operation successful: $#", # keep in sync with `testament.isSuccess` - hintSuccessX: "$loc lines; ${sec}s; $mem; proj: $project; out: $output", - hintBuildMode: "$1", + hintSuccessX: "$build\n$loc lines; ${sec}s; $mem; proj: $project; out: $output", hintCC: "CC: $1", hintLineTooLong: "line too long", hintXDeclaredButNotUsed: "'$1' is declared but not used", @@ -200,7 +199,7 @@ proc computeNotesVerbosity(): array[0..3, TNoteKinds] = result[1] = result[2] - {warnProveField, warnProveIndex, warnGcUnsafe, hintPath, hintDependency, hintCodeBegin, hintCodeEnd, hintSource, hintGlobalVar, hintGCStats, hintMsgOrigin, hintPerformance} - result[0] = result[1] - {hintSuccessX, hintBuildMode, hintSuccess, hintConf, + result[0] = result[1] - {hintSuccessX, hintSuccess, hintConf, hintProcessing, hintPattern, hintExecuting, hintLinking, hintCC} const diff --git a/compiler/main.nim b/compiler/main.nim index 71e549ab8..7824e8cb2 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -13,13 +13,14 @@ when not defined(nimcore): {.error: "nimcore MUST be defined for Nim's core tooling".} import - llstream, strutils, os, ast, lexer, syntaxes, options, msgs, - condsyms, times, + std/[strutils, os, times, tables, sha1, with, json], + llstream, ast, lexer, syntaxes, options, msgs, + condsyms, sem, idents, passes, extccomp, - cgen, json, nversion, + cgen, nversion, platform, nimconf, passaux, depends, vm, modules, - modulegraphs, tables, lineinfos, pathutils, vmprofiler, std/[sha1, with] + modulegraphs, lineinfos, pathutils, vmprofiler import ic / [cbackend, integrity, navigator] from ic / ic import rodViewer @@ -387,38 +388,9 @@ proc mainCommand*(graph: ModuleGraph) = rawMessage(conf, errGenerated, "invalid command: " & conf.command) if conf.errorCounter == 0 and conf.cmd notin {cmdTcc, cmdDump, cmdNop}: - # D20210419T170230:here - let mem = - when declared(system.getMaxMem): formatSize(getMaxMem()) & " peakmem" - else: formatSize(getTotalMem()) & " totmem" - let loc = $conf.linesCompiled - let build = if isDefined(conf, "danger"): "Dangerous Release build" - elif isDefined(conf, "release"): "Release build" - else: "***SLOW, DEBUG BUILD***; -d:release makes code run faster." - let sec = formatFloat(epochTime() - conf.lastCmdTime, ffDecimal, 3) - let project = if conf.filenameOption == foAbs: $conf.projectFull else: $conf.projectName - # xxx honor conf.filenameOption more accurately - var output: string - if optCompileOnly in conf.globalOptions and conf.cmd != cmdJsonscript: - output = $conf.jsonBuildFile - elif conf.outFile.isEmpty and conf.cmd notin {cmdJsonscript} + cmdDocLike + cmdBackends: - # for some cmd we expect a valid absOutFile - output = "unknownOutput" - else: - output = $conf.absOutFile - if conf.filenameOption != foAbs: output = output.AbsoluteFile.extractFilename - # xxx honor filenameOption more accurately if optProfileVM in conf.globalOptions: echo conf.dump(conf.vmProfileData) - rawMessage(conf, hintSuccessX, [ - "loc", loc, - "sec", sec, - "mem", mem, - "project", project, - "output", output, - ]) - if conf.cmd in cmdBackends: - rawMessage(conf, hintBuildMode, build) + genSuccessX(conf) when PrintRopeCacheStats: echo "rope cache stats: " diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 053b5c928..6b005be3d 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -8,10 +8,9 @@ # import - options, strutils, os, tables, ropes, terminal, macros, - lineinfos, pathutils -import std/private/miscdollars -import strutils2 + std/[strutils, os, tables, terminal, macros, times], + std/private/miscdollars, + options, ropes, lineinfos, pathutils, strutils2 type InstantiationInfo* = typeof(instantiationInfo()) template instLoc*(): InstantiationInfo = instantiationInfo(-2, fullPaths = true) @@ -656,3 +655,39 @@ proc uniqueModuleName*(conf: ConfigRef; fid: FileIndex): string = # We mangle upper letters and digits too so that there cannot # be clashes with our special meanings of 'Z' and 'O' result.addInt ord(c) + +proc genSuccessX*(conf: ConfigRef) = + let mem = + when declared(system.getMaxMem): formatSize(getMaxMem()) & " peakmem" + else: formatSize(getTotalMem()) & " totmem" + let loc = $conf.linesCompiled + var build = "" + if conf.cmd in cmdBackends: + build.add "gc: $#; " % $conf.selectedGC + if optThreads in conf.globalOptions: build.add "threads: on; " + build.add "opt: " + if optOptimizeSpeed in conf.options: build.add "speed" + elif optOptimizeSize in conf.options: build.add "size" + else: build.add "none (DEBUG BUILD, `-d:release` generates faster code)" + # pending https://github.com/timotheecour/Nim/issues/752, point to optimization.html + let sec = formatFloat(epochTime() - conf.lastCmdTime, ffDecimal, 3) + let project = if conf.filenameOption == foAbs: $conf.projectFull else: $conf.projectName + # xxx honor conf.filenameOption more accurately + var output: string + if optCompileOnly in conf.globalOptions and conf.cmd != cmdJsonscript: + output = $conf.jsonBuildFile + elif conf.outFile.isEmpty and conf.cmd notin {cmdJsonscript} + cmdDocLike + cmdBackends: + # for some cmd we expect a valid absOutFile + output = "unknownOutput" + else: + output = $conf.absOutFile + if conf.filenameOption != foAbs: output = output.AbsoluteFile.extractFilename + # xxx honor filenameOption more accurately + rawMessage(conf, hintSuccessX, [ + "build", build, + "loc", loc, + "sec", sec, + "mem", mem, + "project", project, + "output", output, + ]) diff --git a/compiler/options.nim b/compiler/options.nim index afe8b0fc7..1851770d9 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -167,8 +167,17 @@ const type TStringSeq* = seq[string] TGCMode* = enum # the selected GC - gcUnselected, gcNone, gcBoehm, gcRegions, gcArc, gcOrc, - gcMarkAndSweep, gcHooks, gcRefc, gcV2, gcGo + gcUnselected = "unselected" + gcNone = "none" + gcBoehm = "boehm" + gcRegions = "regions" + gcArc = "arc" + gcOrc = "orc" + gcMarkAndSweep = "markAndSweep" + gcHooks = "hooks" + gcRefc = "refc" + gcV2 = "v2" + gcGo = "go" # gcRefc and the GCs that follow it use a write barrier, # as far as usesWriteBarrier() is concerned diff --git a/compiler/vmops.nim b/compiler/vmops.nim index 04356fc76..85729fd59 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -136,6 +136,7 @@ when defined(nimHasInvariant): of ccompilerPath: result = conf.cCompilerPath of backend: result = $conf.backend of libPath: result = conf.libpath.string + of gc: result = $conf.selectedGC proc querySettingSeqImpl(conf: ConfigRef, switch: BiggestInt): seq[string] = template copySeq(field: untyped): untyped = diff --git a/doc/nimc.rst b/doc/nimc.rst index e467a33af..2b9f33cb5 100644 --- a/doc/nimc.rst +++ b/doc/nimc.rst @@ -108,7 +108,6 @@ Source The source line that triggered a diagnostic message. StackTrace Success, SuccessX Successful compilation of a library or a binary. -BuildMode Kind of build: debug, release, danger User UserRaw XDeclaredButNotUsed Unused symbols in the code. diff --git a/drnim/drnim.nim b/drnim/drnim.nim index 1eded533d..eb0d89aa2 100644 --- a/drnim/drnim.nim +++ b/drnim/drnim.nim @@ -1205,24 +1205,7 @@ proc mainCommand(graph: ModuleGraph) = registerPass graph, semPass compileProject(graph) if conf.errorCounter == 0: - # xxx deduplicate with D20210419T170230 - let mem = - when declared(system.getMaxMem): formatSize(getMaxMem()) & " peakmem" - else: formatSize(getTotalMem()) & " totmem" - let loc = $conf.linesCompiled - let build = if isDefined(conf, "danger"): "Dangerous Release build" - elif isDefined(conf, "release"): "Release build" - else: "***SLOW, DEBUG BUILD***; -d:release makes code run faster." - let sec = formatFloat(epochTime() - conf.lastCmdTime, ffDecimal, 3) - let project = if conf.filenameOption == foAbs: $conf.projectFull else: $conf.projectName - rawMessage(conf, hintSuccessX, [ - "loc", loc, - "sec", sec, - "mem", mem, - "project", project, - "output", "" - ]) - rawMessage(conf, hintBuildMode, build) + genSuccessX(graph.config) proc processCmdLine(pass: TCmdLinePass, cmd: string; config: ConfigRef) = var p = parseopt.initOptParser(cmd) diff --git a/lib/std/compilesettings.nim b/lib/std/compilesettings.nim index 273cfdb57..e1ffc954a 100644 --- a/lib/std/compilesettings.nim +++ b/lib/std/compilesettings.nim @@ -32,6 +32,7 @@ type backend ## the backend (eg: c|cpp|objc|js); both `nim doc --backend:js` ## and `nim js` would imply backend=js libPath ## the absolute path to the stdlib library, i.e. nim's `--lib`, since 1.5.1 + gc ## gc selected MultipleValueSetting* {.pure.} = enum ## \ ## settings resulting in a seq of string values diff --git a/testament/testament.nim b/testament/testament.nim index 789c987c0..ab6620c84 100644 --- a/testament/testament.nim +++ b/testament/testament.nim @@ -108,8 +108,7 @@ proc isSuccess(input: string): bool = # that may appear in user config (eg: `--filenames`). # Passing `XDG_CONFIG_HOME= testament args...` can be used to ignore user config # stored in XDG_CONFIG_HOME, refs https://wiki.archlinux.org/index.php/XDG_Base_Directory - input.startsWith("Hint: ") and - (input.endsWith("[SuccessX]") or input.endsWith("[BuildMode]")) + input.startsWith("Hint: ") and input.endsWith("[SuccessX]") proc getFileDir(filename: string): string = result = filename.splitFile().dir diff --git a/tests/misc/trunner.nim b/tests/misc/trunner.nim index b6b302836..f2ea1d040 100644 --- a/tests/misc/trunner.nim +++ b/tests/misc/trunner.nim @@ -23,7 +23,7 @@ proc isDots(a: string): bool = a.startsWith(".") and a.strip(chars = {'.'}) == "" const - defaultHintsOff = "--hint:successx:off --hint:buildmode:off --hint:exec:off --hint:link:off --hint:cc:off --hint:conf:off --hint:processing:off --hint:QuitCalled:off" + defaultHintsOff = "--hint:successx:off --hint:exec:off --hint:link:off --hint:cc:off --hint:conf:off --hint:processing:off --hint:QuitCalled:off" # useful when you want to turn only some hints on, and some common ones off. # pending https://github.com/timotheecour/Nim/issues/453, simplify to: `--hints:off` nim = getCurrentCompilerExe() From 488acd9d077e8179d35d665ac0591c456bfa93aa Mon Sep 17 00:00:00 2001 From: Saem Ghani Date: Mon, 14 Jun 2021 00:21:33 -0700 Subject: [PATCH 2217/3768] fixes #18235 - proc annotation type macro sym leak (#18249) * fixes #18235 - proc annotation type macro sym leak - also fixed a typo - proc annotations guard symbol exports with shadow scopes - symbol handling is shadow scope aware * test for exporting an existing unexported sym this one is for my homie alaviss. * Special handling not needed in semProcAnnotation * Testcasing * [skip ci] clean-up and add some more comments * [skip ci] rm trailing whitespace Co-authored-by: Clyybber --- compiler/lookups.nim | 27 +++++++++++++++++++++----- compiler/semstmts.nim | 8 ++++---- tests/macros/m18235.nim | 42 +++++++++++++++++++++++++++++++++++++++++ tests/macros/t18235.nim | 18 ++++++++++++++++++ 4 files changed, 86 insertions(+), 9 deletions(-) create mode 100644 tests/macros/m18235.nim create mode 100644 tests/macros/t18235.nim diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 64476d816..a21e35ddc 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -314,6 +314,7 @@ proc addDeclAt*(c: PContext; scope: PScope, sym: PSym) = from ic / ic import addHidden proc addInterfaceDeclAux*(c: PContext, sym: PSym, forceExport = false) = + ## adds symbol to the module for either private or public access. if sfExported in sym.flags or forceExport: # add to interface: if c.module != nil: exportSym(c, sym) @@ -324,10 +325,15 @@ proc addInterfaceDeclAux*(c: PContext, sym: PSym, forceExport = false) = addHidden(c.encoder, c.packedRepr, sym) proc addInterfaceDeclAt*(c: PContext, scope: PScope, sym: PSym) = + ## adds a symbol on the scope and the interface if appropriate addDeclAt(c, scope, sym) - addInterfaceDeclAux(c, sym) + if not scope.isShadowScope: + # adding into a non-shadow scope, we need to handle exports, etc + addInterfaceDeclAux(c, sym) proc addOverloadableSymAt*(c: PContext; scope: PScope, fn: PSym) = + ## adds an symbol to the given scope, will check for and raise errors if it's + ## a redefinition as opposed to an overload. if fn.kind notin OverloadableSyms: internalError(c.config, fn.info, "addOverloadableSymAt") return @@ -338,24 +344,35 @@ proc addOverloadableSymAt*(c: PContext; scope: PScope, fn: PSym) = scope.addSym(fn) proc addInterfaceDecl*(c: PContext, sym: PSym) = - # it adds the symbol to the interface if appropriate + ## adds a decl and the interface if appropriate addDecl(c, sym) - addInterfaceDeclAux(c, sym) + if not c.currentScope.isShadowScope: + addInterfaceDeclAux(c, sym) proc addInterfaceOverloadableSymAt*(c: PContext, scope: PScope, sym: PSym) = - # it adds the symbol to the interface if appropriate + ## adds an overloadable symbol on the scope and the interface if appropriate addOverloadableSymAt(c, scope, sym) - addInterfaceDeclAux(c, sym) + if not scope.isShadowScope: + addInterfaceDeclAux(c, sym) proc openShadowScope*(c: PContext) = + ## opens a shadow scope, just like any other scope except the depth is the + ## same as the parent -- see `isShadowScope`. c.currentScope = PScope(parent: c.currentScope, symbols: newStrTable(), depthLevel: c.scopeDepth) proc closeShadowScope*(c: PContext) = + ## closes the shadow scope, but doesn't merge any of the symbols c.closeScope proc mergeShadowScope*(c: PContext) = + ## close the existing scope and merge in all defined symbols, this will also + ## trigger any export related code if this is into a non-shadow scope. + ## + ## Merges: + ## shadow -> shadow: add symbols to the parent but check for redefinitions etc + ## shadow -> non-shadow: the above, but also handle exports and all that let shadowScope = c.currentScope c.rawCloseScope for sym in shadowScope.symbols: diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 8f87ce83c..d6ad7ab54 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1844,7 +1844,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, # before compiling the proc params & body, set as current the scope # where the proc was declared - let delcarationScope = c.currentScope + let declarationScope = c.currentScope pushOwner(c, s) openScope(c) @@ -1889,7 +1889,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, var (proto, comesFromShadowScope) = if isAnon: (nil, false) - else: searchForProc(c, delcarationScope, s) + else: searchForProc(c, declarationScope, s) if proto == nil and sfForward in s.flags: ## In cases such as a macro generating a proc with a gensymmed name we ## know `searchForProc` will not find it and sfForward will be set. In @@ -1916,9 +1916,9 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, if not hasProto and sfGenSym notin s.flags: #and not isAnon: if s.kind in OverloadableSyms: - addInterfaceOverloadableSymAt(c, delcarationScope, s) + addInterfaceOverloadableSymAt(c, declarationScope, s) else: - addInterfaceDeclAt(c, delcarationScope, s) + addInterfaceDeclAt(c, declarationScope, s) pragmaCallable(c, s, n, validPragmas) if not hasProto: diff --git a/tests/macros/m18235.nim b/tests/macros/m18235.nim new file mode 100644 index 000000000..6bb4547e0 --- /dev/null +++ b/tests/macros/m18235.nim @@ -0,0 +1,42 @@ +import macros + +# Necessary code to update the AST on a symbol across module boundaries when +# processed by a type macro. Used by a test of a corresponding name of this +# file. + +macro eexport(n: typed): untyped = + result = copyNimTree(n) + # turn exported nnkSym -> nnkPostfix(*, nnkIdent), forcing re-sem + result[0] = nnkPostfix.newTree(ident"*").add: + n.name.strVal.ident + +macro unexport(n: typed): untyped = + result = copyNimTree(n) + # turn nnkSym -> nnkIdent, forcing re-sem and dropping any exported-ness + # that might be present + result[0] = n.name.strVal.ident + +proc foo*() {.unexport.} = discard +proc bar() {.eexport.} = discard + +proc foooof*() {.unexport, eexport, unexport.} = discard +proc barrab() {.eexport, unexport, eexport.} = discard + +macro eexportMulti(n: typed): untyped = + # use the call version of `eexport` macro for one or more decls + result = copyNimTree(n) + for i in 0.. Date: Mon, 14 Jun 2021 08:30:04 +0100 Subject: [PATCH 2218/3768] Enable parallel build in build.sh (#18195) Add help --- tools/niminst/buildsh.nimf | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tools/niminst/buildsh.nimf b/tools/niminst/buildsh.nimf index e13221781..e7afd402e 100644 --- a/tools/niminst/buildsh.nimf +++ b/tools/niminst/buildsh.nimf @@ -21,10 +21,23 @@ do optosname=$2 shift 2 ;; + --parallel) + parallel=$2 + shift 2 + ;; --extraBuildArgs) extraBuildArgs=" $2" shift 2 ;; + -h | --help) + echo "Options:" + echo " --os " + echo " --cpu " + echo " --osname Additional OS specification (used for Android)" + echo " --extraBuildArgs Additional arguments passed to the compiler" + echo " --parallel Multiprocess build. Requires GNU parallel" + exit 0 + ;; --) # End of all options shift break; @@ -39,7 +52,15 @@ do esac done +parallel="${parallel:-0}" CC="${CC:-gcc}" +if [ "$parallel" -gt 1 ]; then + if ! command -v sem > /dev/null; then + echo "Error: GNU parallel is required to use --parallel" + exit 1 + fi + CC="sem -j $parallel --id $$ ${CC}" +fi COMP_FLAGS="${CPPFLAGS:-} ${CFLAGS:-} ?{c.ccompiler.flags}$extraBuildArgs" LINK_FLAGS="${LDFLAGS:-} ?{c.linker.flags}" PS4="" @@ -222,6 +243,9 @@ case $myos in $CC $COMP_FLAGS -Ic_code -c ?{f} -o ?{changeFileExt(f, "o")} # add(linkCmd, " \\\n" & changeFileExt(f, "o")) # end for + if [ "$parallel" -gt 0 ]; then + sem --wait --id $$ + fi $CC -o ?{"$binDir/" & toLowerAscii(c.name)} ?linkCmd $LINK_FLAGS ;; # end for From 0adb47aa15e242983c8251d85367c0fe45fc5f12 Mon Sep 17 00:00:00 2001 From: alaviss Date: Mon, 14 Jun 2021 11:26:12 -0500 Subject: [PATCH 2219/3768] system/excpt: check if the exception is not nil before pop (#18247) In CPS we would consume an exception in the except branch by stashing it into a local then remove the exception from Nim environment so as not to leak it to other code that would be running before the continuation continues However since popCurrentException() assumes that the exception always exist, removing the exception from an except branch will cause a SIGSEGV to happen. This commit fixes that. --- lib/system/excpt.nim | 5 +++-- tests/exception/tsetexceptions.nim | 7 +++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index 5b7d4d49f..72225cf96 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -135,8 +135,9 @@ proc pushCurrentException(e: sink(ref Exception)) {.compilerRtl, inl.} = #showErrorMessage2 "A" proc popCurrentException {.compilerRtl, inl.} = - currException = currException.up - #showErrorMessage2 "B" + if currException != nil: + currException = currException.up + #showErrorMessage2 "B" proc popCurrentExceptionEx(id: uint) {.compilerRtl.} = discard "only for bootstrapping compatbility" diff --git a/tests/exception/tsetexceptions.nim b/tests/exception/tsetexceptions.nim index 557fc1898..386a6ae4c 100644 --- a/tests/exception/tsetexceptions.nim +++ b/tests/exception/tsetexceptions.nim @@ -6,3 +6,10 @@ let ex = newException(CatchableError, "test") setCurrentException(ex) doAssert getCurrentException().msg == ex.msg doAssert getCurrentExceptionMsg() == ex.msg +setCurrentException(nil) + +try: + raise newException(CatchableError, "test2") +except: + setCurrentException(nil) +doAssert getCurrentException() == nil From 8c42f5be0274d8d16910401d7d5a45aa35a63afd Mon Sep 17 00:00:00 2001 From: Clyybber Date: Mon, 14 Jun 2021 19:19:58 +0200 Subject: [PATCH 2220/3768] Small scope refactoring (#18263) * Small scope refactoring * Add test for #10251 * Add inline where appropriate --- compiler/astalgo.nim | 3 ++- compiler/lookups.nim | 36 +++++++++++++++++------------------- compiler/semtypes.nim | 6 ++---- tests/errmsgs/t10251.nim | 12 ++++++++++++ 4 files changed, 33 insertions(+), 24 deletions(-) create mode 100644 tests/errmsgs/t10251.nim diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 300089d81..5af8caad8 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -777,9 +777,10 @@ proc strTableInclReportConflict*(t: var TStrTable, n: PSym; replaceSlot = h h = nextTry(h, high(t.data)) if replaceSlot >= 0: + result = t.data[replaceSlot] # found it if not onConflictKeepOld: t.data[replaceSlot] = n # overwrite it with newer definition! - return t.data[replaceSlot] # found it + return result elif mustRehash(t.data.len, t.counter): strTableEnlarge(t) strTableRawInsert(t.data, n) diff --git a/compiler/lookups.nim b/compiler/lookups.nim index a21e35ddc..9dac106e4 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -293,29 +293,28 @@ proc wrongRedefinition*(c: PContext; info: TLineInfo, s: string; "redefinition of '$1'; previous declaration here: $2" % [s, c.config $ conflictsWith]) -proc addDecl*(c: PContext, sym: PSym, info: TLineInfo) = - let conflict = c.currentScope.addUniqueSym(sym) +proc addDeclAt*(c: PContext; scope: PScope, sym: PSym, info: TLineInfo) = + let conflict = scope.addUniqueSym(sym) if conflict != nil: wrongRedefinition(c, info, sym.name.s, conflict.info) -proc addDecl*(c: PContext, sym: PSym) = - let conflict = strTableInclReportConflict(c.currentScope.symbols, sym, true) - if conflict != nil: - wrongRedefinition(c, sym.info, sym.name.s, conflict.info) +proc addDeclAt*(c: PContext; scope: PScope, sym: PSym) {.inline.} = + addDeclAt(c, scope, sym, sym.info) + +proc addDecl*(c: PContext, sym: PSym, info: TLineInfo) {.inline.} = + addDeclAt(c, c.currentScope, sym, info) + +proc addDecl*(c: PContext, sym: PSym) {.inline.} = + addDeclAt(c, c.currentScope, sym) proc addPrelimDecl*(c: PContext, sym: PSym) = discard c.currentScope.addUniqueSym(sym) -proc addDeclAt*(c: PContext; scope: PScope, sym: PSym) = - let conflict = scope.addUniqueSym(sym) - if conflict != nil: - wrongRedefinition(c, sym.info, sym.name.s, conflict.info) - from ic / ic import addHidden -proc addInterfaceDeclAux*(c: PContext, sym: PSym, forceExport = false) = +proc addInterfaceDeclAux(c: PContext, sym: PSym) = ## adds symbol to the module for either private or public access. - if sfExported in sym.flags or forceExport: + if sfExported in sym.flags: # add to interface: if c.module != nil: exportSym(c, sym) else: internalError(c.config, sym.info, "addInterfaceDeclAux") @@ -331,6 +330,10 @@ proc addInterfaceDeclAt*(c: PContext, scope: PScope, sym: PSym) = # adding into a non-shadow scope, we need to handle exports, etc addInterfaceDeclAux(c, sym) +proc addInterfaceDecl*(c: PContext, sym: PSym) {.inline.} = + ## adds a decl and the interface if appropriate + addInterfaceDeclAt(c, c.currentScope, sym) + proc addOverloadableSymAt*(c: PContext; scope: PScope, fn: PSym) = ## adds an symbol to the given scope, will check for and raise errors if it's ## a redefinition as opposed to an overload. @@ -343,16 +346,11 @@ proc addOverloadableSymAt*(c: PContext; scope: PScope, fn: PSym) = else: scope.addSym(fn) -proc addInterfaceDecl*(c: PContext, sym: PSym) = - ## adds a decl and the interface if appropriate - addDecl(c, sym) - if not c.currentScope.isShadowScope: - addInterfaceDeclAux(c, sym) - proc addInterfaceOverloadableSymAt*(c: PContext, scope: PScope, sym: PSym) = ## adds an overloadable symbol on the scope and the interface if appropriate addOverloadableSymAt(c, scope, sym) if not scope.isShadowScope: + # adding into a non-shadow scope, we need to handle exports, etc addInterfaceDeclAux(c, sym) proc openShadowScope*(c: PContext) = diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 84556ca1a..38307a9ac 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -138,15 +138,13 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = identToReplace[] = symNode if e.position == 0: hasNull = true if result.sym != nil and sfExported in result.sym.flags: - incl(e.flags, {sfUsed, sfExported}) - if result.sym != nil and not isPure: - addInterfaceDeclAux(c, e, forceExport = sfExported in result.sym.flags) + e.flags.incl {sfUsed, sfExported} result.n.add symNode styleCheckDef(c.config, e) onDef(e.info, e) if sfGenSym notin e.flags: - if not isPure: addDecl(c, e) + if not isPure: addInterfaceDecl(c, e) else: declarePureEnumField(c, e) if isPure and (let conflict = strTableInclReportConflict(symbols, e); conflict != nil): wrongRedefinition(c, e.info, e.name.s, conflict.info) diff --git a/tests/errmsgs/t10251.nim b/tests/errmsgs/t10251.nim new file mode 100644 index 000000000..5ad373ba3 --- /dev/null +++ b/tests/errmsgs/t10251.nim @@ -0,0 +1,12 @@ +discard """ +errormsg: "redefinition of 'foo'; previous declaration here: t10251.nim(9, 9)" +line: 11 +column: 9 +""" + +type + Enum1 = enum + foo, bar, baz + Enum2 = enum + foo, bar, baz + From e80d7ff0f2716fd1f892a936a78986531b107fb6 Mon Sep 17 00:00:00 2001 From: Mark Pointing Date: Tue, 15 Jun 2021 03:29:23 +1000 Subject: [PATCH 2221/3768] httpclient.nim Fixes #14794 and an issue where content-header is not set on postContent (#18208) * Fixed missing newline after bound marker in mulipart post (#14794) and a problem where calling postContent with multipart data does not set content-length header. * Update lib/pure/httpclient.nim * Added comment outlining the reason for changes to httpclient.nim and added tests to ensure that multipart post has a newline at the end of the body, and that the content-length header is present. * Fixed typo in comments. * Removed redundant blank lines in thttpclient_standalone.nim. Co-authored-by: Mark Pointing Co-authored-by: Andreas Rumpf --- lib/pure/httpclient.nim | 8 ++++-- tests/stdlib/thttpclient_standalone.nim | 33 ++++++++++++++++++++++++- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 2f9f1913f..33da6ef6c 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -975,7 +975,7 @@ proc format(client: HttpClient | AsyncHttpClient, if entry.isFile: length += entry.fileSize + httpNewLine.len - result.add "--" & bound & "--" + result.add "--" & bound & "--" & httpNewLine for s in result: length += s.len client.headers["Content-Length"] = $length @@ -1010,12 +1010,16 @@ proc requestAux(client: HttpClient | AsyncHttpClient, url: Uri, await newConnection(client, url) - let newHeaders = client.headers.override(headers) + var newHeaders: HttpHeaders var data: seq[string] if multipart != nil and multipart.content.len > 0: + # `format` modifies `client.headers`, see + # https://github.com/nim-lang/Nim/pull/18208#discussion_r647036979 data = await client.format(multipart) + newHeaders = client.headers.override(headers) else: + newHeaders = client.headers.override(headers) # Only change headers if they have not been specified already if not newHeaders.hasKey("Content-Length"): if body.len != 0: diff --git a/tests/stdlib/thttpclient_standalone.nim b/tests/stdlib/thttpclient_standalone.nim index 44a88e91e..5fa1ea41a 100644 --- a/tests/stdlib/thttpclient_standalone.nim +++ b/tests/stdlib/thttpclient_standalone.nim @@ -2,7 +2,7 @@ discard """ cmd: "nim c --threads:on $file" """ -import asynchttpserver, httpclient, asyncdispatch +import asynchttpserver, httpclient, asyncdispatch, strutils block: # bug #16436 proc startServer() {.async.} = @@ -21,3 +21,34 @@ block: # bug #16436 asyncCheck startServer() doAssertRaises(ProtocolError): waitFor runClient() + +block: # bug #14794 (And test for presence of content-length header when using postContent) + proc startServer() {.async.} = + var killServer = false + proc cb(req: Request) {.async.} = + doAssert(req.body.endsWith(httpNewLine), "Multipart body does not end with a newline.") + # this next line is probably not required because asynchttpserver does not call + # the callback when there is no content-length header. It instead errors with + # Error: unhandled exception: 411 Length Required + # Added for good measure in case the server becomes more permissive. + doAssert(req.headers.hasKey("content-length"), "Content-Length header is not present.") + killServer = true + asyncCheck req.respond(Http200, "OK") + + var server = newAsyncHttpServer() + server.listen(Port(5556)) + while not killServer: + if server.shouldAcceptRequest(): + await server.acceptRequest(cb) + else: + poll() + + proc runClient() {.async.} = + let c = newAsyncHttpClient() + var data = newMultipartData() + data.add("file.txt", "This is intended to be an example text file.\r\nThis would be the second line.\r\n") + let r = await c.postContent("http://127.0.0.1:5556", multipart = data) + c.close() + + asyncCheck startServer() + waitFor runClient() From 8ef6073543b14bd48665ae075df66c2c36f564dd Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 14 Jun 2021 23:56:10 -0700 Subject: [PATCH 2222/3768] add test case for pure enum redefinition error within enum (fixed in recent PR) (#18266) * add test case for pure enum redefinition error within enum (fixed in recent PR) * remove code duplication * Revert "remove code duplication" (would require bootstrap >= 1.4) This reverts commit 3f793874c231f847ef015e37a5fd6851f85d9675. * fixup --- compiler/astalgo.nim | 8 ++++---- compiler/lookups.nim | 2 ++ tests/errmsgs/t10251.nim | 21 ++++++++++++++------- 3 files changed, 20 insertions(+), 11 deletions(-) diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 5af8caad8..3c97665f0 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -759,9 +759,9 @@ proc strTableAdd*(t: var TStrTable, n: PSym) = proc strTableInclReportConflict*(t: var TStrTable, n: PSym; onConflictKeepOld = false): PSym = - # returns true if n is already in the string table: - # It is essential that `n` is written nevertheless! - # This way the newest redefinition is picked by the semantic analyses! + # if `t` has a conflicting symbol (same identifier as `n`), return it + # otherwise return `nil`. Incl `n` to `t` unless `onConflictKeepOld = true` + # and a conflict was found. assert n.name != nil var h: Hash = n.name.h and high(t.data) var replaceSlot = -1 @@ -780,7 +780,7 @@ proc strTableInclReportConflict*(t: var TStrTable, n: PSym; result = t.data[replaceSlot] # found it if not onConflictKeepOld: t.data[replaceSlot] = n # overwrite it with newer definition! - return result + return result # but return the old one elif mustRehash(t.data.len, t.counter): strTableEnlarge(t) strTableRawInsert(t.data, n) diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 9dac106e4..f72b58ac6 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -293,6 +293,8 @@ proc wrongRedefinition*(c: PContext; info: TLineInfo, s: string; "redefinition of '$1'; previous declaration here: $2" % [s, c.config $ conflictsWith]) +# xxx pending bootstrap >= 1.4, replace all those overloads with a single one: +# proc addDecl*(c: PContext, sym: PSym, info = sym.info, scope = c.currentScope) {.inline.} = proc addDeclAt*(c: PContext; scope: PScope, sym: PSym, info: TLineInfo) = let conflict = scope.addUniqueSym(sym) if conflict != nil: diff --git a/tests/errmsgs/t10251.nim b/tests/errmsgs/t10251.nim index 5ad373ba3..0c7fe0b3d 100644 --- a/tests/errmsgs/t10251.nim +++ b/tests/errmsgs/t10251.nim @@ -1,12 +1,19 @@ discard """ -errormsg: "redefinition of 'foo'; previous declaration here: t10251.nim(9, 9)" -line: 11 -column: 9 + action:reject + cmd: "nim check $options $file" + nimout: ''' +t10251.nim(15, 5) Error: redefinition of 'foo'; previous declaration here: t10251.nim(13, 5) +t10251.nim(19, 23) Error: redefinition of 'goo1'; previous declaration here: t10251.nim(19, 11) +''' """ +# line 10 type - Enum1 = enum - foo, bar, baz - Enum2 = enum - foo, bar, baz + Enum1 = enum + foo, bar, baz + Enum2 = enum + foo, bar, baz +type + Enum3 {.pure.} = enum # fixed (by accident?) in https://github.com/nim-lang/Nim/pull/18263 + goo0, goo1, goo2, goo1 From d3b27eb63e18fbe36a7579a3322db316115a3d81 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 15 Jun 2021 14:56:49 -0700 Subject: [PATCH 2223/3768] link stable and devel docs in nim docs (#18272) [backport:1.2] --- config/nimdoc.cfg | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/config/nimdoc.cfg b/config/nimdoc.cfg index cdf49197d..15f887023 100644 --- a/config/nimdoc.cfg +++ b/config/nimdoc.cfg @@ -127,21 +127,12 @@ doc.body_toc_group = """
    From c5cf21c0c4f314e4128658d35000339d67e4824b Mon Sep 17 00:00:00 2001 From: Clyybber Date: Wed, 16 Jun 2021 02:45:05 +0200 Subject: [PATCH 2224/3768] Don't report unused hints for consumed AST (#18270) * Fix #18203 * Add testcase * Fix testcase * Fix test --- compiler/lookups.nim | 4 +++- tests/macros/t18203.nim | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 tests/macros/t18203.nim diff --git a/compiler/lookups.nim b/compiler/lookups.nim index f72b58ac6..4f413f472 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -364,7 +364,9 @@ proc openShadowScope*(c: PContext) = proc closeShadowScope*(c: PContext) = ## closes the shadow scope, but doesn't merge any of the symbols - c.closeScope + ## Does not check for unused symbols or missing forward decls since a macro + ## or template consumes this AST + rawCloseScope(c) proc mergeShadowScope*(c: PContext) = ## close the existing scope and merge in all defined symbols, this will also diff --git a/tests/macros/t18203.nim b/tests/macros/t18203.nim new file mode 100644 index 000000000..aae0a2690 --- /dev/null +++ b/tests/macros/t18203.nim @@ -0,0 +1,15 @@ +discard """ + matrix: "--hint:SuccessX:off --hint:Link:off --hint:Conf:off --hint:CC:off --hint:XDeclaredButNotUsed:on" + nimout: ''' +''' +nimoutFull: true +action: compile +""" + +# bug #18203 +import std/macros + +macro foo(x: typed) = newProc ident"bar" +proc bar() {.foo.} = raise +bar() + From c51680e7012bff32156623fad0996d62f7918222 Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 16 Jun 2021 17:31:20 +0800 Subject: [PATCH 2225/3768] fixes #17696 (#18276) --- lib/pure/collections/sharedlist.nim | 7 ++-- tests/stdlib/tsharedlist.nim | 53 +++++++++++++++++++++++------ 2 files changed, 47 insertions(+), 13 deletions(-) diff --git a/lib/pure/collections/sharedlist.nim b/lib/pure/collections/sharedlist.nim index c72477675..79f2391f6 100644 --- a/lib/pure/collections/sharedlist.nim +++ b/lib/pure/collections/sharedlist.nim @@ -35,8 +35,10 @@ template withLock(t, x: untyped) = release(t.lock) proc iterAndMutate*[A](x: var SharedList[A]; action: proc(x: A): bool) = - ## iterates over the list. If 'action' returns true, the + ## Iterates over the list. If 'action' returns true, the ## current item is removed from the list. + ## + ## .. warning:: It may not preserve the element order after some modifications. withLock(x): var n = x.head while n != nil: @@ -47,8 +49,9 @@ proc iterAndMutate*[A](x: var SharedList[A]; action: proc(x: A): bool) = if action(n.d[i]): acquire(x.lock) let t = x.tail + dec t.dataLen # TODO considering t.dataLen == 0, + # probably the module should be refactored using doubly linked lists n.d[i] = t.d[t.dataLen] - dec t.dataLen else: acquire(x.lock) inc i diff --git a/tests/stdlib/tsharedlist.nim b/tests/stdlib/tsharedlist.nim index a795be0f3..693f1018e 100644 --- a/tests/stdlib/tsharedlist.nim +++ b/tests/stdlib/tsharedlist.nim @@ -1,17 +1,48 @@ -import sharedlist +discard """ + matrix: "--threads:on" +""" -var - list: SharedList[int] - count: int +import std/sharedlist -init(list) +block: + var + list: SharedList[int] + count: int -for i in 1 .. 250: - list.add i + init(list) -for i in list: - inc count + for i in 1 .. 250: + list.add i -doAssert count == 250 + for i in list: + inc count -deinitSharedList(list) + doAssert count == 250 + + deinitSharedList(list) + + +block: # bug #17696 + var keysList = SharedList[string]() + init(keysList) + + keysList.add("a") + keysList.add("b") + keysList.add("c") + keysList.add("d") + keysList.add("e") + keysList.add("f") + + + # Remove element "b" and "d" from the list. + keysList.iterAndMutate(proc (key: string): bool = + if key == "b" or key == "d": # remove only "b" and "d" + return true + return false + ) + + var results: seq[string] + for key in keysList.items: + results.add key + + doAssert results == @["a", "f", "c", "e"] From 13b94c0297806911cf317ff6169dba8595a0333d Mon Sep 17 00:00:00 2001 From: Clyybber Date: Wed, 16 Jun 2021 16:40:22 +0200 Subject: [PATCH 2226/3768] Fix doubly typed forward declarations (#18279) * Add testcase * Fix testcase * Fix doubly typed forward decls * Better fix --- compiler/semstmts.nim | 7 ++++++- tests/macros/tmacros_issues.nim | 22 +++++++++++++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index d6ad7ab54..f29dc24ef 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1890,11 +1890,16 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, var (proto, comesFromShadowScope) = if isAnon: (nil, false) else: searchForProc(c, declarationScope, s) - if proto == nil and sfForward in s.flags: + if proto == nil and sfForward in s.flags and n[bodyPos].kind != nkEmpty: ## In cases such as a macro generating a proc with a gensymmed name we ## know `searchForProc` will not find it and sfForward will be set. In ## such scenarios the sym is shared between forward declaration and we ## can treat the `s` as the proto. + ## To differentiate between that happening and a macro just returning a + ## forward declaration that has been typed before we check if the body + ## is not empty. This has the sideeffect of allowing multiple forward + ## declarations if they share the same sym. + ## See the "doubly-typed forward decls" case in tmacros_issues.nim proto = s let hasProto = proto != nil diff --git a/tests/macros/tmacros_issues.nim b/tests/macros/tmacros_issues.nim index a964f46ba..a81c51658 100644 --- a/tests/macros/tmacros_issues.nim +++ b/tests/macros/tmacros_issues.nim @@ -484,6 +484,26 @@ func expMin: float {.aadMin.} = 1 echo expMin() +# doubly-typed forward decls +macro noop(x: typed) = x +noop: + proc cally() = discard + +cally() + +noop: + proc barry() + +proc barry() = discard + +# some more: +proc barry2() {.noop.} +proc barry2() = discard + +proc barry3() {.noop.} +proc barry3() {.noop.} = discard + + # issue #15389 block double_sem_for_procs: @@ -498,4 +518,4 @@ block double_sem_for_procs: return x1 + 1.0 result = 10.0 - discard exp(5.0) \ No newline at end of file + discard exp(5.0) From 3c854e8c24b394900dc65df680b7679f09cee97c Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 16 Jun 2021 23:04:12 -0700 Subject: [PATCH 2227/3768] followup #16400; use use -d:nimCompilerStackraceHints in more places (#18282) --- compiler/vmgen.nim | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index c6fd75259..bb203e0ab 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -35,6 +35,9 @@ import from modulegraphs import getBody +when defined(nimCompilerStackraceHints): + import std/stackframes + const debugEchoCode* = defined(nimVMDebug) @@ -2000,6 +2003,8 @@ proc procIsCallback(c: PCtx; s: PSym): bool = dec i proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = + when defined(nimCompilerStackraceHints): + setFrameMsg c.config$n.info & " " & $n.kind & " " & $flags case n.kind of nkSym: let s = n.sym From 49e945ed082bacd5cd5ecff305ec47ff3a00f1c5 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 16 Jun 2021 23:07:40 -0700 Subject: [PATCH 2228/3768] increase backoffDuration to avoid timeouts (#18281) --- testament/categories.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testament/categories.nim b/testament/categories.nim index 7f05797ff..bf7855b2c 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -429,7 +429,7 @@ proc testNimblePackages(r: var TResults; cat: Category; packageFilter: string) = let buildPath = packagesDir / pkg.name template tryCommand(cmd: string, workingDir2 = buildPath, reFailed = reInstallFailed, maxRetries = 1): string = var outp: string - let ok = retryCall(maxRetry = maxRetries, backoffDuration = 1.0): + let ok = retryCall(maxRetry = maxRetries, backoffDuration = 10.0): var status: int (outp, status) = execCmdEx(cmd, workingDir = workingDir2) status == QuitSuccess From 969cb97c0a9f045a2fb31866fc804cdf1b2698dd Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Thu, 17 Jun 2021 09:19:52 +0300 Subject: [PATCH 2229/3768] PCRE, nimgrep: add limit for buffer size (#18280) --- lib/impure/re.nim | 13 ++++++++++--- tools/nimgrep.nim | 5 ++++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/lib/impure/re.nim b/lib/impure/re.nim index 78ea7b002..504d8f22e 100644 --- a/lib/impure/re.nim +++ b/lib/impure/re.nim @@ -141,6 +141,10 @@ proc matchOrFind(buf: cstring, pattern: Regex, matches: var openArray[string], else: matches[i-1] = "" return rawMatches[1] - rawMatches[0] +const MaxReBufSize* = high(cint) + ## Maximum PCRE (API 1) buffer start/size equal to `high(cint)`, which even + ## for 64-bit systems can be either 2`31`:sup:-1 or 2`63`:sup:-1. + proc findBounds*(buf: cstring, pattern: Regex, matches: var openArray[string], start = 0, bufSize: int): tuple[first, last: int] = ## returns the starting position and end position of `pattern` in `buf` @@ -167,7 +171,8 @@ proc findBounds*(s: string, pattern: Regex, matches: var openArray[string], ## and the captured substrings in the array `matches`. ## If it does not match, nothing ## is written into `matches` and `(-1,0)` is returned. - result = findBounds(cstring(s), pattern, matches, start, s.len) + result = findBounds(cstring(s), pattern, matches, + min(start, MaxReBufSize), min(s.len, MaxReBufSize)) proc findBounds*(buf: cstring, pattern: Regex, matches: var openArray[tuple[first, last: int]], @@ -197,7 +202,8 @@ proc findBounds*(s: string, pattern: Regex, ## and the captured substrings in the array `matches`. ## If it does not match, nothing is written into `matches` and ## `(-1,0)` is returned. - result = findBounds(cstring(s), pattern, matches, start, s.len) + result = findBounds(cstring(s), pattern, matches, + min(start, MaxReBufSize), min(s.len, MaxReBufSize)) proc findBoundsImpl(buf: cstring, pattern: Regex, start = 0, bufSize = 0, flags = 0): tuple[first, last: int] = @@ -232,7 +238,8 @@ proc findBounds*(s: string, pattern: Regex, ## Note: there is a speed improvement if the matches do not need to be captured. runnableExamples: assert findBounds("01234abc89", re"abc") == (5,7) - result = findBounds(cstring(s), pattern, start, s.len) + result = findBounds(cstring(s), pattern, + min(start, MaxReBufSize), min(s.len, MaxReBufSize)) proc matchOrFind(buf: cstring, pattern: Regex, start, bufSize: int, flags: cint): cint = var diff --git a/tools/nimgrep.nim b/tools/nimgrep.nim index a368c40ef..a589cfb14 100644 --- a/tools/nimgrep.nim +++ b/tools/nimgrep.nim @@ -649,7 +649,7 @@ template updateCounters(output: Output) = proc printInfo(filename:string, output: Output) = case output.kind of openError: - printError("can not open path " & filename & " " & output.msg) + printError("cannot open path '" & filename & "': " & output.msg) of rejected: if optVerbose in options: echo "(rejected: ", output.reason, ")" @@ -719,6 +719,9 @@ iterator searchFile(pattern: Pattern; buffer: string): Output = pre: pre, match: move(curMi)) i = t.last+1 + when typeof(pattern) is Regex: + if buffer.len > MaxReBufSize: + yield Output(kind: openError, msg: "PCRE size limit is " & $MaxReBufSize) func detectBin(buffer: string): bool = for i in 0 ..< min(1024, buffer.len): From fd8b79707c1b936ed1ba42892f5e06f846e2816b Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 17 Jun 2021 08:20:10 +0200 Subject: [PATCH 2230/3768] Revert "system/excpt: check if the exception is not nil before pop (#18247)" (#18265) This reverts commit 0adb47aa15e242983c8251d85367c0fe45fc5f12. --- lib/system/excpt.nim | 5 ++--- tests/exception/tsetexceptions.nim | 7 ------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index 72225cf96..5b7d4d49f 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -135,9 +135,8 @@ proc pushCurrentException(e: sink(ref Exception)) {.compilerRtl, inl.} = #showErrorMessage2 "A" proc popCurrentException {.compilerRtl, inl.} = - if currException != nil: - currException = currException.up - #showErrorMessage2 "B" + currException = currException.up + #showErrorMessage2 "B" proc popCurrentExceptionEx(id: uint) {.compilerRtl.} = discard "only for bootstrapping compatbility" diff --git a/tests/exception/tsetexceptions.nim b/tests/exception/tsetexceptions.nim index 386a6ae4c..557fc1898 100644 --- a/tests/exception/tsetexceptions.nim +++ b/tests/exception/tsetexceptions.nim @@ -6,10 +6,3 @@ let ex = newException(CatchableError, "test") setCurrentException(ex) doAssert getCurrentException().msg == ex.msg doAssert getCurrentExceptionMsg() == ex.msg -setCurrentException(nil) - -try: - raise newException(CatchableError, "test2") -except: - setCurrentException(nil) -doAssert getCurrentException() == nil From 99411674a6da72f608bfa870f51b3a296cd85eee Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 16 Jun 2021 23:20:33 -0700 Subject: [PATCH 2231/3768] tests/exception/tsetexceptions.nim not joinable (#18264) --- tests/exception/tsetexceptions.nim | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/exception/tsetexceptions.nim b/tests/exception/tsetexceptions.nim index 557fc1898..0e353f43e 100644 --- a/tests/exception/tsetexceptions.nim +++ b/tests/exception/tsetexceptions.nim @@ -1,7 +1,10 @@ discard """ targets: "c cpp js" + joinable: false """ +# refs https://github.com/nim-lang/Nim/pull/18247#issuecomment-860877161 + let ex = newException(CatchableError, "test") setCurrentException(ex) doAssert getCurrentException().msg == ex.msg From c83ac16671170e513fa1ca40419892ccc618ec85 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 17 Jun 2021 00:17:46 -0700 Subject: [PATCH 2232/3768] Renamed `-d:nimCompilerStackraceHints` to `-d:nimCompilerStacktraceHints`. (#18283) --- changelog.md | 1 + compiler/ccgexprs.nim | 4 ++-- compiler/semexprs.nim | 4 ++-- compiler/vmgen.nim | 4 ++-- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/changelog.md b/changelog.md index cc6f826ee..26048eedd 100644 --- a/changelog.md +++ b/changelog.md @@ -88,6 +88,7 @@ The downside is that these defines now have custom logic that doesn't apply for other defines. +- Renamed `-d:nimCompilerStackraceHints` to `-d:nimCompilerStacktraceHints`. ## Standard library additions and changes diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 7ed80145b..c07ddf1db 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -9,7 +9,7 @@ # included from cgen.nim -when defined(nimCompilerStackraceHints): +when defined(nimCompilerStacktraceHints): import std/stackframes proc getNullValueAuxT(p: BProc; orig, t: PType; obj, constOrNil: PNode, @@ -2694,7 +2694,7 @@ proc genConstStmt(p: BProc, n: PNode) = genConstDefinition(m, p, sym) proc expr(p: BProc, n: PNode, d: var TLoc) = - when defined(nimCompilerStackraceHints): + when defined(nimCompilerStacktraceHints): setFrameMsg p.config$n.info & " " & $n.kind p.currLineInfo = n.info diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index ba8b93ae6..53a5f149e 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -10,7 +10,7 @@ # this module does the semantic checking for expressions # included from sem.nim -when defined(nimCompilerStackraceHints): +when defined(nimCompilerStacktraceHints): import std/stackframes const @@ -2714,7 +2714,7 @@ proc getNilType(c: PContext): PType = c.nilTypeCache = result proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = - when defined(nimCompilerStackraceHints): + when defined(nimCompilerStacktraceHints): setFrameMsg c.config$n.info & " " & $n.kind result = n if c.config.cmd == cmdIdeTools: suggestExpr(c, n) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index bb203e0ab..14f9e0b30 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -35,7 +35,7 @@ import from modulegraphs import getBody -when defined(nimCompilerStackraceHints): +when defined(nimCompilerStacktraceHints): import std/stackframes const @@ -2003,7 +2003,7 @@ proc procIsCallback(c: PCtx; s: PSym): bool = dec i proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = - when defined(nimCompilerStackraceHints): + when defined(nimCompilerStacktraceHints): setFrameMsg c.config$n.info & " " & $n.kind & " " & $flags case n.kind of nkSym: From 5a3456d220d0c2a4022969b98ab33baaf1fd4d00 Mon Sep 17 00:00:00 2001 From: Adrian Veith Date: Thu, 17 Jun 2021 11:23:38 +0200 Subject: [PATCH 2233/3768] fix for #18284 int32 should be int (#18285) the var exp was typed as int32 - it should be int since frep expects an int --- lib/pure/math.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/math.nim b/lib/pure/math.nim index cff946581..410660d4a 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -997,7 +997,7 @@ when not defined(js): # taken from Go-lang Math.Log2 const ln2 = 0.693147180559945309417232121458176568075500134360255254120680009 template log2Impl[T](x: T): T = - var exp: int32 + var exp: int var frac = frexp(x, exp) # Make sure exact powers of two give an exact answer. # Don't depend on Log(0.5)*(1/Ln2)+exp being exactly exp-1. From ef121f3b99c4d020d3df7cb369f5060c76c73d57 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 17 Jun 2021 16:19:16 -0700 Subject: [PATCH 2234/3768] followup #17876: remove annoying enum name clashes in tests/enum/tenum.nim (#18291) --- tests/enum/tenum.nim | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/tests/enum/tenum.nim b/tests/enum/tenum.nim index 97fd4c68c..37383890c 100644 --- a/tests/enum/tenum.nim +++ b/tests/enum/tenum.nim @@ -128,22 +128,21 @@ block tnamedfields: doAssert $x == "abc", $x -block toptions: +block tfakeOptions: type - # please make sure we have under 32 options (improves code efficiency!) - TOption = enum - optNone, optForceFullMake, optBoehmGC, optRefcGC, optRangeCheck, - optBoundsCheck, optOverflowCheck, optNilCheck, optAssert, optLineDir, - optWarns, optHints, optListCmd, optCompileOnly, - optSafeCode, # only allow safe code - optStyleCheck, optOptimizeSpeed, optOptimizeSize, optGenDynLib, - optGenGuiApp, optStackTrace + TFakeOption = enum + fakeNone, fakeForceFullMake, fakeBoehmGC, fakeRefcGC, fakeRangeCheck, + fakeBoundsCheck, fakeOverflowCheck, fakeNilCheck, fakeAssert, fakeLineDir, + fakeWarns, fakeHints, fakeListCmd, fakeCompileOnly, + fakeSafeCode, # only allow safe code + fakeStyleCheck, fakeOptimizeSpeed, fakeOptimizeSize, fakeGenDynLib, + fakeGenGuiApp, fakeStackTrace - TOptionset = set[TOption] + TFakeOptionset = set[TFakeOption] var - gOptions: TOptionset = {optRefcGC, optRangeCheck, optBoundsCheck, - optOverflowCheck, optAssert, optWarns, optHints, optLineDir, optStackTrace} + gFakeOptions: TFakeOptionset = {fakeRefcGC, fakeRangeCheck, fakeBoundsCheck, + fakeOverflowCheck, fakeAssert, fakeWarns, fakeHints, fakeLineDir, fakeStackTrace} compilerArgs: int gExitcode: int8 From fc76565574b86566e4a642b9aac541e025cf5de3 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 17 Jun 2021 16:38:15 -0700 Subject: [PATCH 2235/3768] disable pkg manu (#18292) --- testament/important_packages.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 3d3c340eb..efee361da 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -82,7 +82,7 @@ pkg "karax", "nim c -r tests/tester.nim" pkg "kdtree", "nimble test -d:nimLegacyRandomInitRand", "https://github.com/jblindsay/kdtree" pkg "loopfusion" pkg "macroutils" -pkg "manu" +pkg "manu", allowFailure=true # pending https://github.com/planetis-m/manu/issues/5 pkg "markdown" pkg "memo" pkg "msgpack4nim", "nim c -r tests/test_spec.nim" From a250481dcde2167d568cc1b61624658419c18e69 Mon Sep 17 00:00:00 2001 From: j-james <35242550+j-james@users.noreply.github.com> Date: Fri, 18 Jun 2021 03:10:45 -0700 Subject: [PATCH 2236/3768] Make the existence of high(type) clearer, especially near subranges (#18286) * Make the existence of high(type) clearer, especially around subranges * Use a better example and dry up the wording --- doc/manual.rst | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/doc/manual.rst b/doc/manual.rst index 867db245b..46a40a690 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -989,10 +989,12 @@ Ordinal types have the following characteristics: - Ordinal types are countable and ordered. This property allows the operation of functions such as `inc`, `ord`, and `dec` on ordinal types to be defined. -- Ordinal values have the smallest possible value. Trying to count further - down than the smallest value produces a panic or a static error. -- Ordinal values have the largest possible value. Trying to count further - than the largest value produces a panic or a static error. +- Ordinal types have a smallest possible value, accessible with `low(type)`. + Trying to count further down than the smallest value produces a panic or + a static error. +- Ordinal types have a largest possible value, accessible with `high(type)`. + Trying to count further up than the largest value produces a panic or + a static error. Integers, bool, characters, and enumeration types (and subranges of these types) belong to ordinal types. @@ -1095,6 +1097,7 @@ lowest and highest value of the type. For example: type Subrange = range[0..5] PositiveFloat = range[0.0..Inf] + Positive* = range[1..high(int)] # as defined in `system` `Subrange` is a subrange of an integer which can only hold the values 0 From 87cd9b24a3df66dcde96bbdec2bd7620015ddeba Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 18 Jun 2021 06:52:08 -0700 Subject: [PATCH 2237/3768] add more APIs to compiler/debugutils; re-export it (#18243) --- compiler/debugutils.nim | 37 +++++++++++++++++++++++++++++++++ compiler/options.nim | 3 +++ compiler/semexprs.nim | 7 +++++++ tests/compiler/tdebugutils.nim | 38 ++++++++++++++++++++++++++++++++++ 4 files changed, 85 insertions(+) create mode 100644 tests/compiler/tdebugutils.nim diff --git a/compiler/debugutils.nim b/compiler/debugutils.nim index 235c100b3..d109d2121 100644 --- a/compiler/debugutils.nim +++ b/compiler/debugutils.nim @@ -4,7 +4,26 @@ Utilities to help with debugging nim compiler. Experimental API, subject to change. ]## +#[ +## example +useful debugging flags: +--stacktrace -d:debug -d:nimDebugUtils + nim c -o:bin/nim_temp --stacktrace -d:debug -d:nimDebugUtils compiler/nim + +## future work +* expose and improve astalgo.debug, replacing it by std/prettyprints, + refs https://github.com/nim-lang/RFCs/issues/385 +]# + import options +import std/wrapnils +export wrapnils + # allows using things like: `?.n.sym.typ.len` + +import std/stackframes +export stackframes + # allows using things like: `setFrameMsg c.config$n.info & " " & $n.kind` + # which doesn't log, but augments stacktrace with side channel information var conf0: ConfigRef @@ -17,3 +36,21 @@ proc onNewConfigRef*(conf: ConfigRef) {.inline.} = proc getConfigRef*(): ConfigRef = ## nil, if -d:nimDebugUtils wasn't specified result = conf0 + +proc isCompilerDebug*(): bool = + ##[ + Provides a simple way for user code to enable/disable logging in the compiler + in a granular way. This can then be used in the compiler as follows: + ```nim + if isCompilerDebug(): + echo ?.n.sym.typ.len + ``` + ]## + runnableExamples: + proc main = + echo 2 + {.define(nimCompilerDebug).} + echo 3.5 # code section in which `isCompilerDebug` will be true + {.undef(nimCompilerDebug).} + echo 'x' + conf0.isDefined("nimCompilerDebug") diff --git a/compiler/options.nim b/compiler/options.nim index 1851770d9..05d8ee5b7 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -472,7 +472,10 @@ const foreignPackageNotesDefault* = { proc isDefined*(conf: ConfigRef; symbol: string): bool when defined(nimDebugUtils): + # this allows inserting debugging utilties in all modules that import `options` + # with a single switch, which is useful when debugging compiler. import debugutils + export debugutils proc initConfigRefCommon(conf: ConfigRef) = conf.selectedGC = gcRefc diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 53a5f149e..c885f4963 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2716,6 +2716,13 @@ proc getNilType(c: PContext): PType = proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = when defined(nimCompilerStacktraceHints): setFrameMsg c.config$n.info & " " & $n.kind + when false: # see `tdebugutils` + if isCompilerDebug(): + echo (">", c.config$n.info, n, flags, n.kind) + defer: + if isCompilerDebug(): + echo ("<", c.config$n.info, n, ?.result.typ) + result = n if c.config.cmd == cmdIdeTools: suggestExpr(c, n) if nfSem in n.flags: return diff --git a/tests/compiler/tdebugutils.nim b/tests/compiler/tdebugutils.nim new file mode 100644 index 000000000..50b15cb78 --- /dev/null +++ b/tests/compiler/tdebugutils.nim @@ -0,0 +1,38 @@ +discard """ + joinable: false + disabled: true +""" + +#[ +This test illustrates some features of `debugutils` to debug the compiler. + +## example +this shows how to enable compiler logging just for a section of user code, +without generating tons of unrelated log messages for code you're not interested +in debugging. + +```sh +# enable some debugging code, e.g. the `when false:` block in `semExpr` +nim c -o:bin/nim_temp --stacktrace -d:debug -d:nimDebugUtils compiler/nim +bin/nim_temp c tests/compiler/tdebugutils.nim +``` + +(use --filenames:abs for abs files) + +## result +("<", "tdebugutils.nim(16, 3)", {.define(nimCompilerDebug).}, nil) +(">", "tdebugutils.nim(17, 3)", let a = 2.5 * 3, {}, nkLetSection) +(">", "tdebugutils.nim(17, 15)", 2.5 * 3, {efAllowDestructor, efWantValue}, nkInfix) +(">", "tdebugutils.nim(17, 11)", 2.5, {efAllowStmt, efDetermineType, efOperand}, nkFloatLit) +("<", "tdebugutils.nim(17, 11)", 2.5, float64) +(">", "tdebugutils.nim(17, 17)", 3, {efAllowStmt, efDetermineType, efOperand}, nkIntLit) +("<", "tdebugutils.nim(17, 17)", 3, int literal(3)) +("<", "tdebugutils.nim(17, 15)", 2.5 * 3, float) +("<", "tdebugutils.nim(17, 3)", let a = 2.5 * 3, nil) +(">", "tdebugutils.nim(18, 3)", {.undef(nimCompilerDebug).}, {}, nkPragma) +]# + +proc main = + {.define(nimCompilerDebug).} + let a = 2.5 * 3 + {.undef(nimCompilerDebug).} From 5600a622297a9b06b2f47039e0397cded1bd425c Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 18 Jun 2021 08:57:51 -0700 Subject: [PATCH 2238/3768] strformat.fmt now supports non-literal const strings (#18274) Co-authored-by: Andreas Rumpf --- changelog.md | 4 ++- lib/core/macros.nim | 3 +- lib/pure/strformat.nim | 63 +++++++++++++++++++++++++----------------- 3 files changed, 43 insertions(+), 27 deletions(-) diff --git a/changelog.md b/changelog.md index 26048eedd..4fd1ef355 100644 --- a/changelog.md +++ b/changelog.md @@ -93,7 +93,9 @@ ## Standard library additions and changes -- Added support for parenthesized expressions in `strformat` +- `strformat`: + added support for parenthesized expressions. + added support for const string's instead of just string literals - Fixed buffer overflow bugs in `net` diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 37f855d94..c09fae6b3 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -476,8 +476,9 @@ proc genSym*(kind: NimSymKind = nskLet; ident = ""): NimNode {. ## needs to occur in a declaration context. proc callsite*(): NimNode {.magic: "NCallSite", benign, deprecated: - "Deprecated since v0.18.1; use varargs[untyped] in the macro prototype instead".} + "Deprecated since v0.18.1; use `varargs[untyped]` in the macro prototype instead".} ## Returns the AST of the invocation expression that invoked this macro. + # see https://github.com/nim-lang/RFCs/issues/387 as candidate replacement. proc toStrLit*(n: NimNode): NimNode = ## Converts the AST `n` to the concrete Nim code and wraps that diff --git a/lib/pure/strformat.nim b/lib/pure/strformat.nim index 52731e970..872141383 100644 --- a/lib/pure/strformat.nim +++ b/lib/pure/strformat.nim @@ -573,15 +573,12 @@ template formatValue(result: var string; value: char; specifier: string) = template formatValue(result: var string; value: cstring; specifier: string) = result.add value -proc strformatImpl(pattern: NimNode; openChar, closeChar: char): NimNode = - if pattern.kind notin {nnkStrLit..nnkTripleStrLit}: - error "string formatting (fmt(), &) only works with string literals", pattern +proc strformatImpl(f: string; openChar, closeChar: char): NimNode = if openChar == ':' or closeChar == ':': error "openChar and closeChar must not be ':'" - let f = pattern.strVal var i = 0 let res = genSym(nskVar, "fmtRes") - result = newNimNode(nnkStmtListExpr, lineInfoFrom = pattern) + result = newNimNode(nnkStmtListExpr) # XXX: https://github.com/nim-lang/Nim/issues/8405 # When compiling with -d:useNimRtl, certain procs such as `count` from the strutils # module are not accessible at compile-time: @@ -633,12 +630,8 @@ proc strformatImpl(pattern: NimNode; openChar, closeChar: char): NimNode = var x: NimNode try: x = parseExpr(subexpr) - except ValueError: - when declared(getCurrentExceptionMsg): - let msg = getCurrentExceptionMsg() - error("could not parse `" & subexpr & "`.\n" & msg, pattern) - else: - error("could not parse `" & subexpr & "`.\n", pattern) + except ValueError as e: + error("could not parse `$#` in `$#`.\n$#" % [subexpr, f, e.msg]) let formatSym = bindSym("formatValue", brOpen) var options = "" if f[i] == ':': @@ -667,19 +660,39 @@ proc strformatImpl(pattern: NimNode; openChar, closeChar: char): NimNode = when defined(debugFmtDsl): echo repr result -macro `&`*(pattern: string): untyped = strformatImpl(pattern, '{', '}') - ## For a specification of the `&` macro, see the module level documentation. - -macro fmt*(pattern: string): untyped = strformatImpl(pattern, '{', '}') - ## An alias for `& <#&.m,string>`_. - -macro fmt*(pattern: string; openChar, closeChar: char): untyped = - ## The same as `fmt <#fmt.m,string>`_, but uses `openChar` instead of `'{'` - ## and `closeChar` instead of `'}'`. +macro fmt*(pattern: static string; openChar: static char, closeChar: static char): string = + ## Interpolates `pattern` using symbols in scope. runnableExamples: - let testInt = 123 - assert "".fmt('<', '>') == "123" - assert """(()"foo" & "bar"())""".fmt(')', '(') == "(foobar)" - assert """ ""{"123+123"}"" """.fmt('"', '"') == " \"{246}\" " + let x = 7 + assert "var is {x * 2}".fmt == "var is 14" + assert "var is {{x}}".fmt == "var is {x}" # escape via doubling + const s = "foo: {x}" + assert s.fmt == "foo: 7" # also works with const strings - strformatImpl(pattern, openChar.intVal.char, closeChar.intVal.char) + assert fmt"\n" == r"\n" # raw string literal + assert "\n".fmt == "\n" # regular literal (likewise with `fmt("\n")` or `fmt "\n"`) + runnableExamples: + # custom `openChar`, `closeChar` + let x = 7 + assert "".fmt('<', '>') == "7" + assert "<<>>".fmt('<', '>') == "<7>" + assert "`x`".fmt('`', '`') == "7" + strformatImpl(pattern, openChar, closeChar) + +template fmt*(pattern: static string): untyped = + ## Alias for `fmt(pattern, '{', '}')`. + fmt(pattern, '{', '}') + +macro `&`*(pattern: string{lit}): string = + ## `&pattern` is the same as `pattern.fmt`. + ## For a specification of the `&` macro, see the module level documentation. + # pending bug #18275, bug #18278, use `pattern: static string` + # consider deprecating this, it's redundant with `fmt` and `fmt` is strictly + # more flexible, readable (no confusion with the binary `&`), self-documenting, + # not to mention #18275, bug #18278. + runnableExamples: + let x = 7 + assert &"{x}\n" == "7\n" # regular string literal + assert &"{x}\n" == "7\n".fmt # `fmt` can be used instead + assert &"{x}\n" != fmt"7\n" # see `fmt` docs, this would use a raw string literal + strformatImpl(pattern.strVal, '{', '}') From 5d15bd7b618cda06e3ae88883914467f89d38112 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 18 Jun 2021 10:23:27 -0700 Subject: [PATCH 2239/3768] refs #18278: do not gag fatal msgs (#18290) --- compiler/lineinfos.nim | 13 ++++++++----- compiler/modules.nim | 3 +-- compiler/msgs.nim | 15 +++++++++++---- compiler/pragmas.nim | 2 +- 4 files changed, 21 insertions(+), 12 deletions(-) diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index da0a8d623..052dae272 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -27,7 +27,10 @@ proc createDocLink*(urlSuffix: string): string = type TMsgKind* = enum - errUnknown, errInternal, errIllFormedAstX, errCannotOpenFile, + # fatal errors + errUnknown, errFatal, errInternal, + # non-fatal errors + errIllFormedAstX, errCannotOpenFile, errXExpected, errGridTableNotImplemented, errMarkdownIllformedTable, @@ -38,7 +41,7 @@ type errProveInit, # deadcode errGenerated, errUser, - + # warnings warnCannotOpenFile = "CannotOpenFile", warnOctalEscape = "OctalEscape", warnXIsNeverRead = "XIsNeverRead", warnXmightNotBeenInit = "XmightNotBeenInit", warnDeprecated = "Deprecated", warnConfigDeprecated = "ConfigDeprecated", @@ -64,7 +67,7 @@ type warnCannotOpen = "CannotOpen", warnFileChanged = "FileChanged", warnUser = "User", - + # hints hintSuccess = "Success", hintSuccessX = "SuccessX", hintCC = "CC", hintLineTooLong = "LineTooLong", hintXDeclaredButNotUsed = "XDeclaredButNotUsed", @@ -83,6 +86,7 @@ type const MsgKindToStr*: array[TMsgKind, string] = [ errUnknown: "unknown error", + errFatal: "fatal error: $1", errInternal: "internal error: $1", errIllFormedAstX: "illformed AST: $1", errCannotOpenFile: "cannot open '$1'", @@ -180,8 +184,7 @@ const ] const - fatalMin* = errUnknown - fatalMax* = errInternal + fatalMsgs* = {errUnknown..errInternal} errMin* = errUnknown errMax* = errUser warnMin* = warnCannotOpenFile diff --git a/compiler/modules.nim b/compiler/modules.nim index 27b93e8df..dcfcfd508 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -153,8 +153,7 @@ proc compileSystemModule*(graph: ModuleGraph) = proc wantMainModule*(conf: ConfigRef) = if conf.projectFull.isEmpty: - fatal(conf, newLineInfo(conf, AbsoluteFile(commandLineDesc), 1, 1), errGenerated, - "command expects a filename") + fatal(conf, gCmdLineInfo, "command expects a filename") conf.projectMainIdx = fileInfoIdx(conf, addFileExt(conf.projectFull, NimExt)) proc compileProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIdx) = diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 6b005be3d..8b4365ec4 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -408,7 +408,7 @@ To create a stacktrace, rerun compilation with './koch temp $1 ', see $2 f quit 1 proc handleError(conf: ConfigRef; msg: TMsgKind, eh: TErrorHandling, s: string) = - if msg >= fatalMin and msg <= fatalMax: + if msg in fatalMsgs: if conf.cmd == cmdIdeTools: log(s) quit(conf, msg) if msg >= errMin and msg <= errMax or @@ -498,6 +498,12 @@ proc liMessage*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string, color: ForegroundColor ignoreMsg = false sev: Severity + let errorOutputsOld = conf.m.errorOutputs + if msg in fatalMsgs: + # don't gag, refs bug #7080, bug #18278; this can happen with `{.fatal.}` + # or inside a `tryConstExpr`. + conf.m.errorOutputs = {eStdOut, eStdErr} + let kind = if msg in warnMin..hintMax and msg != hintUserRaw: $msg else: "" # xxx not sure why hintUserRaw is special case msg of errMin..errMax: @@ -553,6 +559,9 @@ proc liMessage*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string, KindFormat % $hintMsgOrigin, resetStyle, conf.unitSep) handleError(conf, msg, eh, s) + if msg in fatalMsgs: + # most likely would have died here but just in case, we restore state + conf.m.errorOutputs = errorOutputsOld template rawMessage*(conf: ConfigRef; msg: TMsgKind, args: openArray[string]) = let arg = msgKindToString(msg) % args @@ -561,9 +570,7 @@ template rawMessage*(conf: ConfigRef; msg: TMsgKind, args: openArray[string]) = template rawMessage*(conf: ConfigRef; msg: TMsgKind, arg: string) = liMessage(conf, unknownLineInfo, msg, arg, eh = doAbort, instLoc()) -template fatal*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg = "") = - # this fixes bug #7080 so that it is at least obvious 'fatal' was executed. - conf.m.errorOutputs = {eStdOut, eStdErr} +template fatal*(conf: ConfigRef; info: TLineInfo, arg = "", msg = errFatal) = liMessage(conf, info, msg, arg, doAbort, instLoc()) template globalAssert*(conf: ConfigRef; cond: untyped, info: TLineInfo = unknownLineInfo, arg = "") = diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 3a2d9cede..695e595c3 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -1047,7 +1047,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, let s = expectStrLit(c, it) recordPragma(c, it, "error", s) localError(c.config, it.info, errUser, s) - of wFatal: fatal(c.config, it.info, errUser, expectStrLit(c, it)) + of wFatal: fatal(c.config, it.info, expectStrLit(c, it)) of wDefine: processDefine(c, it) of wUndef: processUndef(c, it) of wCompile: processCompile(c, it) From 7714ab468a665e772c6d37d39038974cbcea75dc Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 19 Jun 2021 11:24:46 -0700 Subject: [PATCH 2240/3768] make privateAccess work with generic types and generic instantiations; fix a SIGSEGV (#18260) Co-authored-by: Andreas Rumpf --- compiler/ast.nim | 20 +++++++++++++ compiler/semmagic.nim | 10 ++++--- lib/std/importutils.nim | 32 +++++++++++++------- tests/stdlib/mimportutils.nim | 15 ++++++++++ tests/stdlib/timportutils.nim | 56 +++++++++++++++++++++++++++++++++++ 5 files changed, 118 insertions(+), 15 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 96adb8c1f..8bb8de1d6 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1887,6 +1887,26 @@ proc toObject*(typ: PType): PType = if t.kind == tyRef: t.lastSon else: typ +proc toObjectFromRefPtrGeneric*(typ: PType): PType = + #[ + See also `toObject`. + Finds the underlying `object`, even in cases like these: + type + B[T] = object f0: int + A1[T] = ref B[T] + A2[T] = ref object f1: int + A3 = ref object f2: int + A4 = object f3: int + ]# + result = typ + while true: + case result.kind + of tyGenericBody: result = result.lastSon + of tyRef, tyPtr, tyGenericInst, tyGenericInvocation, tyAlias: result = result[0] + # automatic dereferencing is deep, refs #18298. + else: break + assert result.sym != nil + proc isImportedException*(t: PType; conf: ConfigRef): bool = assert t != nil diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index e4e007678..d3f26e630 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -457,6 +457,11 @@ proc semOld(c: PContext; n: PNode): PNode = localError(c.config, n[1].info, n[1].sym.name.s & " does not belong to " & getCurrOwner(c).name.s) result = n +proc semPrivateAccess(c: PContext, n: PNode): PNode = + let t = n[1].typ[0].toObjectFromRefPtrGeneric + c.currentScope.allowPrivateAccess.add t.sym + result = newNodeIT(nkEmpty, n.info, getSysType(c.graph, n.info, tyVoid)) + proc magicsAfterOverloadResolution(c: PContext, n: PNode, flags: TExprFlags): PNode = ## This is the preferred code point to implement magics. @@ -574,9 +579,6 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, n[0].sym.magic = mSubU result = n of mPrivateAccess: - var t = n[1].typ[0] - if t.kind in {tyRef, tyPtr}: t = t[0] - c.currentScope.allowPrivateAccess.add t.sym - result = newNodeIT(nkEmpty, n.info, getSysType(c.graph, n.info, tyVoid)) + result = semPrivateAccess(c, n) else: result = n diff --git a/lib/std/importutils.nim b/lib/std/importutils.nim index 0c0f546b9..d2da76ea8 100644 --- a/lib/std/importutils.nim +++ b/lib/std/importutils.nim @@ -13,22 +13,32 @@ Possible future APIs: ]# when defined(nimImportutilsExample): - type Foo = object - x1: int # private + type + Foo = object + f0: int # private + Goo*[T] = object + g0: int # private proc initFoo*(): auto = Foo() -proc privateAccess*(t: typedesc[object|(ref object)|(ptr object)]) {.magic: "PrivateAccess".} = +proc privateAccess*(t: typedesc) {.magic: "PrivateAccess".} = ## Enables access to private fields of `t` in current scope. runnableExamples("-d:nimImportutilsExample"): # here we're importing a module containing: - # type Foo = object - # x1: int # private + # type + # Foo = object + # f0: int # private + # Goo*[T] = object + # g0: int # private # proc initFoo*(): auto = Foo() - var a = initFoo() + var f = initFoo() block: - assert not compiles(a.x1) - privateAccess(a.type) - a.x1 = 1 # accessible in this scope + assert not compiles(f.f0) + privateAccess(f.type) + f.f0 = 1 # accessible in this scope block: - assert a.x1 == 1 # still in scope - assert not compiles(a.x1) + assert f.f0 == 1 # still in scope + assert not compiles(f.f0) + + # this also works with generics + privateAccess(Goo) + assert Goo[float](g0: 1).g0 == 1 diff --git a/tests/stdlib/mimportutils.nim b/tests/stdlib/mimportutils.nim index d2b185cd3..e89d58d27 100644 --- a/tests/stdlib/mimportutils.nim +++ b/tests/stdlib/mimportutils.nim @@ -13,5 +13,20 @@ type hd1: float PA* = ref A PtA* = ptr A + E*[T] = object + he1: int + FSub[T1, T2] = object + h3: T1 + h4: T2 + F*[T1, T2] = ref FSub[T1, T2] + G*[T] = ref E[T] + H3*[T] = object + h5: T + H2*[T] = H3[T] + H1*[T] = ref H2[T] + H*[T] = H1[T] + +type BAalias* = typeof(B.default) + # typeof is not a transparent abstraction, creates a `tyAlias` proc initB*(): B = B() diff --git a/tests/stdlib/timportutils.nim b/tests/stdlib/timportutils.nim index 37e2b7102..be912e702 100644 --- a/tests/stdlib/timportutils.nim +++ b/tests/stdlib/timportutils.nim @@ -36,6 +36,55 @@ template main = let c = C(c0: 1, hc1: 2) c.hc1 == 2 + block: + assertAll: + not compiles(E[int](he1: 1)) + privateAccess E[int] + var e = E[int](he1: 1) + e.he1 == 1 + e.he1 = 2 + e.he1 == 2 + e.he1 += 3 + e.he1 == 5 + # xxx caveat: this currently compiles but in future, we may want + # to make `privateAccess E[int]` only affect a specific instantiation; + # note that `privateAccess E` does work to cover all instantiations. + var e2 = E[float](he1: 1) + + block: + assertAll: + not compiles(E[int](he1: 1)) + privateAccess E + var e = E[int](he1: 1) + e.he1 == 1 + + block: + assertAll: + not compiles(F[int, int](h3: 1)) + privateAccess F[int, int] + var e = F[int, int](h3: 1) + e.h3 == 1 + + block: + assertAll: + not compiles(F[int, int](h3: 1)) + privateAccess F[int, int].default[].typeof + var e = F[int, int](h3: 1) + e.h3 == 1 + + block: + assertAll: + var a = G[int]() + var b = a.addr + privateAccess b.type + discard b.he1 + discard b[][].he1 + + block: + assertAll: + privateAccess H[int] + var a = H[int](h5: 2) + block: assertAll: privateAccess PA @@ -44,6 +93,13 @@ template main = pa.ha1 = 3 pa.ha1 == 3 + block: + assertAll: + var b = BAalias() + not compiles(b.hb1) + privateAccess BAalias + discard b.hb1 + block: assertAll: var a = A(a0: 1) From ad5063aed101f0102fb867f0b4e518ede7fa188b Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 19 Jun 2021 14:41:43 -0700 Subject: [PATCH 2241/3768] testutils.assertAll: show correct location on assert failure (#18299) --- testament/lib/stdtest/testutils.nim | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/testament/lib/stdtest/testutils.nim b/testament/lib/stdtest/testutils.nim index 6d8620dc7..a5476b8d7 100644 --- a/testament/lib/stdtest/testutils.nim +++ b/testament/lib/stdtest/testutils.nim @@ -107,10 +107,12 @@ macro assertAll*(body) = # remove this once these support VM, pending #10129 (closed but not yet fixed) result = newStmtList() for a in body: - result.add genAst(a) do: + result.add genAst(a, a2 = a.repr, info = lineInfo(a)) do: # D20210421T014713:here # xxx pending https://github.com/nim-lang/Nim/issues/12030, # `typeof` should introduce its own scope, so that this # is sufficient: `typeof(a)` instead of `typeof(block: a)` when typeof(block: a) is void: a - else: doAssert a + else: + if not a: + raise newException(AssertionDefect, info & " " & a2) From 128d21be1cdf28d6aac6a63910fabf02f186e548 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 20 Jun 2021 00:49:18 -0700 Subject: [PATCH 2242/3768] add `typetraits.pointerBase` to return `T` in `ref T|ptr T` (#18293) * add typetraits.deref to return T in ref T|ptr T * deref => refBase * refBase=>pointerBase * [skip ci] address comment --- changelog.md | 15 ++++++--------- lib/pure/typetraits.nim | 10 ++++++++++ 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/changelog.md b/changelog.md index 4fd1ef355..80e38f420 100644 --- a/changelog.md +++ b/changelog.md @@ -113,8 +113,12 @@ - Make `{.requiresInit.}` pragma to work for `distinct` types. -- Added a macros `enumLen` for returning the number of items in an enum to the - `typetraits.nim` module. +- `typetraits`: + `distinctBase` now is identity instead of error for non distinct types. + Added `enumLen` to return the number of elements in an enum. + Added `HoleyEnum` for enums with holes, `OrdinalEnum` for enums without holes. + Added `hasClosure`. + Added `pointerBase` to return `T` for `ref T | ptr T`. - `prelude` now works with the JavaScript target. Added `sequtils` import to `prelude`. @@ -162,8 +166,6 @@ Added `symbolName` to return the enum symbol name ignoring the human readable name. Added `symbolRank` to return the index in which an enum member is listed in an enum. -- Added `typetraits.HoleyEnum` for enums with holes, `OrdinalEnum` for enums without holes. - - Removed deprecated `iup` module from stdlib, it has already moved to [nimble](https://github.com/nim-lang/iup). @@ -293,7 +295,6 @@ - Added `algorithm.merge`. - - Added `std/jsfetch` module [Fetch](https://developer.mozilla.org/docs/Web/API/Fetch_API) wrapper for JavaScript target. - Added `std/jsheaders` module [Headers](https://developer.mozilla.org/en-US/docs/Web/API/Headers) wrapper for JavaScript target. @@ -322,8 +323,6 @@ - Added `hasDataBuffered` to `asyncnet`. -- Added `hasClosure` to `std/typetraits`. - - Added `std/tempfiles`. - Added `genasts.genAst` that avoids the problems inherent with `quote do` and can @@ -345,8 +344,6 @@ - nil dereference is not allowed at compile time. `cast[ptr int](nil)[]` is rejected at compile time. -- `typetraits.distinctBase` now is identity instead of error for non distinct types. - - `os.copyFile` is now 2.5x faster on OSX, by using `copyfile` from `copyfile.h`; use `-d:nimLegacyCopyFile` for OSX < 10.5. diff --git a/lib/pure/typetraits.nim b/lib/pure/typetraits.nim index 3e215a0ea..e63585f63 100644 --- a/lib/pure/typetraits.nim +++ b/lib/pure/typetraits.nim @@ -100,6 +100,16 @@ proc isNamedTuple*(T: typedesc): bool {.magic: "TypeTrait".} = doAssert not isNamedTuple((string, int)) doAssert isNamedTuple(tuple[name: string, age: int]) +template pointerBase*[T](_: typedesc[ptr T | ref T]): typedesc = + ## Returns `T` for `ref T | ptr T`. + runnableExamples: + assert (ref int).pointerBase is int + type A = ptr seq[float] + assert A.pointerBase is seq[float] + assert (ref A).pointerBase is A # not seq[float] + assert (var s = "abc"; s[0].addr).typeof.pointerBase is char + T + proc distinctBase*(T: typedesc): typedesc {.magic: "TypeTrait".} = ## Returns the base type for distinct types, or the type itself otherwise. ## From 590d457631290beacf97d945c5d3a625cc671645 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Sun, 20 Jun 2021 10:50:03 +0300 Subject: [PATCH 2243/3768] docgen: move to shared RST state (fix #16990) (#18256) * docgen: move to shared RST state (fix #16990) * Update lib/packages/docutils/rst.nim Co-authored-by: Andreas Rumpf * Update lib/packages/docutils/rst.nim Co-authored-by: Andreas Rumpf * Update lib/packages/docutils/rst.nim Co-authored-by: Andreas Rumpf * Update compiler/docgen.nim Co-authored-by: Timothee Cour * Update compiler/docgen.nim Co-authored-by: Timothee Cour * Update compiler/docgen.nim Co-authored-by: Timothee Cour * Update lib/packages/docutils/rst.nim Co-authored-by: Timothee Cour * rename `cmdDoc2` to `cmdDoc` * fix (P)RstSharedState convention * new style of initialization * misc suggestions * 1 more rename * fix a regression Co-authored-by: Andreas Rumpf Co-authored-by: Timothee Cour --- compiler/commands.nim | 2 +- compiler/docgen.nim | 241 ++++++++++++------ compiler/docgen2.nim | 1 + compiler/main.nim | 2 +- compiler/options.nim | 6 +- lib/packages/docutils/rst.nim | 215 +++++++++------- .../expected/subdir/subdir_b/utils.idx | 10 +- tests/stdlib/trstgen.nim | 7 +- 8 files changed, 292 insertions(+), 192 deletions(-) diff --git a/compiler/commands.nim b/compiler/commands.nim index 994b224f1..8f8df71a7 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -443,7 +443,7 @@ proc parseCommand*(command: string): Command = of "check": cmdCheck of "e": cmdNimscript of "doc0": cmdDoc0 - of "doc2", "doc": cmdDoc2 + of "doc2", "doc": cmdDoc of "doc2tex": cmdDoc2tex of "rst2html": cmdRst2html of "rst2tex": cmdRst2tex diff --git a/compiler/docgen.nim b/compiler/docgen.nim index dc2a63f48..2c99adba3 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -28,6 +28,24 @@ const DocColOffset = "## ".len # assuming that a space was added after ## type + ItemFragment = object ## A fragment from each item will be eventually + ## constructed by converting `rst` fields to strings. + case isRst: bool + of true: + rst: PRstNode + of false: ## contains ready markup e.g. from runnableExamples + str: string + ItemPre = seq[ItemFragment] ## A pre-processed item. + Item = object ## Any item in documentation, e.g. symbol + ## entry. Configuration variable ``doc.item`` + ## is used for its HTML rendering. + descRst: ItemPre ## Description of the item (may contain + ## runnableExamples). + substitutions: seq[string] ## Variable names in `doc.item`... + ModSection = object ## Section like Procs, Types, etc. + secItems: seq[Item] ## Pre-processed items. + finalMarkup: string ## The items, after RST pass 2 and rendering. + ModSections = array[TSymKind, ModSection] TSections = array[TSymKind, string] ExampleGroup = ref object ## a group of runnableExamples with same rdoccmd @@ -35,17 +53,25 @@ type docCmd: string ## from user config, e.g. --doccmd:-d:foo code: string ## contains imports; each import contains `body` index: int ## group index + JsonItem = object # pre-processed item: `rst` should be finalized + json: JsonNode + rst: PRstNode + rstField: string TDocumentor = object of rstgen.RstGenerator - modDesc: string # module description + modDescPre: ItemPre # module description, not finalized + modDescFinal: string # module description, after RST pass 2 and rendering module: PSym modDeprecationMsg: string - toc, toc2, section: TSections + section: ModSections # entries of ``.nim`` file (for `proc`s, etc) + toc, toc2: TSections # toc2 - grouped TOC tocTable: array[TSymKind, Table[string, string]] indexValFilename: string analytics: string # Google Analytics javascript, "" if doesn't exist seenSymbols: StringTableRef # avoids duplicate symbol generation for HTML. - jArray: JsonNode + jEntriesPre: seq[JsonItem] # pre-processed RST + JSON content + jEntriesFinal: JsonNode # final JSON after RST pass 2 and rendering types: TStrTable + sharedState: PRstSharedState isPureRst: bool conf*: ConfigRef cache*: IdentCache @@ -58,6 +84,9 @@ type PDoc* = ref TDocumentor ## Alias to type less. +proc add(dest: var ItemPre, rst: PRstNode) = dest.add ItemFragment(isRst: true, rst: rst) +proc add(dest: var ItemPre, str: string) = dest.add ItemFragment(isRst: false, str: str) + proc prettyString(a: object): string = # xxx pending std/prettyprint refs https://github.com/nim-lang/RFCs/issues/203#issuecomment-602534906 for k, v in fieldPairs(a): @@ -151,12 +180,12 @@ template declareClosures = if not fileExists(result): result = "" proc parseRst(text, filename: string, - line, column: int, hasToc: var bool, + line, column: int, rstOptions: RstParseOptions; - conf: ConfigRef): PRstNode = + conf: ConfigRef, sharedState: PRstSharedState): PRstNode = declareClosures() - result = rstParse(text, filename, line, column, hasToc, rstOptions, - docgenFindFile, compilerMsgHandler) + result = rstParsePass1(text, filename, line, column, rstOptions, + sharedState) proc getOutFile2(conf: ConfigRef; filename: RelativeFile, ext: string, guessTarget: bool): AbsoluteFile = @@ -179,10 +208,13 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef, result.conf = conf result.cache = cache result.outDir = conf.outDir.string + const options = {roSupportRawDirective, roSupportMarkdown, + roPreferMarkdown, roNimFile} + result.sharedState = newRstSharedState( + options, filename.string, + docgenFindFile, compilerMsgHandler) initRstGenerator(result[], (if conf.isLatexCmd: outLatex else: outHtml), - conf.configVars, filename.string, - {roSupportRawDirective, roSupportMarkdown, - roPreferMarkdown, roNimFile}, + conf.configVars, filename.string, options, docgenFindFile, compilerMsgHandler) if conf.configVars.hasKey("doc.googleAnalytics"): @@ -203,7 +235,7 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef, result.seenSymbols = newStringTable(modeCaseInsensitive) result.id = 100 - result.jArray = newJArray() + result.jEntriesFinal = newJArray() initStrTable result.types result.onTestSnippet = proc (gen: var RstGenerator; filename, cmd: string; status: int; content: string) = @@ -261,37 +293,31 @@ proc getVarIdx(varnames: openArray[string], id: string): int = return i result = -1 -proc genComment(d: PDoc, n: PNode): string = - result = "" +proc genComment(d: PDoc, n: PNode): PRstNode = if n.comment.len > 0: - let comment = n.comment - when false: - # RFC: to preseve newlines in comments, this would work: - comment = comment.replace("\n", "\n\n") - renderRstToOut(d[], - parseRst(comment, toFullPath(d.conf, n.info), - toLinenumber(n.info), - toColumn(n.info) + DocColOffset, - (var dummy: bool; dummy), d.options, d.conf), - result) + result = parseRst(n.comment, toFullPath(d.conf, n.info), + toLinenumber(n.info), + toColumn(n.info) + DocColOffset, + d.options, d.conf, + d.sharedState) -proc genRecCommentAux(d: PDoc, n: PNode): string = - if n == nil: return "" +proc genRecCommentAux(d: PDoc, n: PNode): PRstNode = + if n == nil: return nil result = genComment(d, n) - if result == "": + if result == nil: if n.kind in {nkStmtList, nkStmtListExpr, nkTypeDef, nkConstDef, nkObjectTy, nkRefTy, nkPtrTy, nkAsgn, nkFastAsgn, nkHiddenStdConv}: # notin {nkEmpty..nkNilLit, nkEnumTy, nkTupleTy}: for i in 0.. 0: msg.add " cmd: " & rdoccmd - dispA(d.conf, dest, "\n

    $1

    \n", + var s: string + dispA(d.conf, s, "\n

    $1

    \n", "\n\n\\textbf{$1}\n", [msg]) + dest.add s inc d.listingCounter let id = $d.listingCounter dest.add(d.config.getOrDefault"doc.listing_start" % [id, "langNim", ""]) @@ -597,7 +625,7 @@ proc getRoutineBody(n: PNode): PNode = doAssert result.len == 2 result = result[1] -proc getAllRunnableExamples(d: PDoc, n: PNode, dest: var string) = +proc getAllRunnableExamples(d: PDoc, n: PNode, dest: var ItemPre) = var n = n var state = rsStart template fn(n2, topLevel) = @@ -706,7 +734,7 @@ proc complexName(k: TSymKind, n: PNode, baseName: string): string = ## types will be added with a preceding dash. Return types won't be added. ## ## If you modify the output of this proc, please update the anchor generation - ## section of ``doc/docgen.txt``. + ## section of ``doc/docgen.rst``. result = baseName case k of skProc, skFunc: discard @@ -804,7 +832,7 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags) = var result = "" var literal, plainName = "" var kind = tkEof - var comm = "" + var comm: ItemPre if n.kind in routineDefs: getAllRunnableExamples(d, n, comm) else: @@ -840,9 +868,9 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags) = let seeSrc = genSeeSrc(d, toFullPath(d.conf, n.info), n.info.line.int) - d.section[k].add(getConfigVar(d.conf, "doc.item") % - ["name", name, "uniqueName", uniqueName, - "header", result, "desc", comm, "itemID", $d.id, + d.section[k].secItems.add Item(descRst: comm, substitutions: @[ + "name", name, "uniqueName", uniqueName, + "header", result, "itemID", $d.id, "header_plain", plainNameEsc, "itemSym", cleanPlainSymbol, "itemSymOrID", symbolOrId, "itemSymEnc", plainSymbolEnc, "itemSymOrIDEnc", symbolOrIdEnc, "seeSrc", seeSrc, @@ -886,25 +914,26 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags) = if k == skType and nameNode.kind == nkSym: d.types.strTableAdd nameNode.sym -proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonNode = +proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonItem = if not isVisible(d, nameNode): return var name = getName(d, nameNode) comm = genRecComment(d, n) r: TSrcGen initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments}) - result = %{ "name": %name, "type": %($k), "line": %n.info.line.int, - "col": %n.info.col} - if comm.len > 0: - result["description"] = %comm + result.json = %{ "name": %name, "type": %($k), "line": %n.info.line.int, + "col": %n.info.col} + if comm != nil: + result.rst = comm + result.rstField = "description" if r.buf.len > 0: - result["code"] = %r.buf + result.json["code"] = %r.buf if k in routineKinds: - result["signature"] = newJObject() + result.json["signature"] = newJObject() if n[paramsPos][0].kind != nkEmpty: - result["signature"]["return"] = %($n[paramsPos][0]) + result.json["signature"]["return"] = %($n[paramsPos][0]) if n[paramsPos].len > 1: - result["signature"]["arguments"] = newJArray() + result.json["signature"]["arguments"] = newJArray() for paramIdx in 1 ..< n[paramsPos].len: for identIdx in 0 ..< n[paramsPos][paramIdx].len - 2: let @@ -912,22 +941,22 @@ proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonNode = paramType = $n[paramsPos][paramIdx][^2] if n[paramsPos][paramIdx][^1].kind != nkEmpty: let paramDefault = $n[paramsPos][paramIdx][^1] - result["signature"]["arguments"].add %{"name": %paramName, "type": %paramType, "default": %paramDefault} + result.json["signature"]["arguments"].add %{"name": %paramName, "type": %paramType, "default": %paramDefault} else: - result["signature"]["arguments"].add %{"name": %paramName, "type": %paramType} + result.json["signature"]["arguments"].add %{"name": %paramName, "type": %paramType} if n[pragmasPos].kind != nkEmpty: - result["signature"]["pragmas"] = newJArray() + result.json["signature"]["pragmas"] = newJArray() for pragma in n[pragmasPos]: - result["signature"]["pragmas"].add %($pragma) + result.json["signature"]["pragmas"].add %($pragma) if n[genericParamsPos].kind != nkEmpty: - result["signature"]["genericParams"] = newJArray() + result.json["signature"]["genericParams"] = newJArray() for genericParam in n[genericParamsPos]: var param = %{"name": %($genericParam)} if genericParam.sym.typ.sons.len > 0: param["types"] = newJArray() for kind in genericParam.sym.typ.sons: param["types"].add %($kind) - result["signature"]["genericParams"].add param + result.json["signature"]["genericParams"].add param proc checkForFalse(n: PNode): bool = result = n.kind == nkIdent and cmpIgnoreStyle(n.ident.s, "false") == 0 @@ -946,8 +975,8 @@ proc traceDeps(d: PDoc, it: PNode) = traceDeps(d, a) elif it.kind == nkSym and belongsToPackage(d.conf, it.sym): let external = externalDep(d, it.sym) - if d.section[k] != "": d.section[k].add(", ") - dispA(d.conf, d.section[k], + if d.section[k].finalMarkup != "": d.section[k].finalMarkup.add(", ") + dispA(d.conf, d.section[k].finalMarkup, "$1", "$1", [esc(d.target, external.prettyLink), changeFileExt(external, "html")]) @@ -956,8 +985,8 @@ proc exportSym(d: PDoc; s: PSym) = const k = exportSection if s.kind == skModule and belongsToPackage(d.conf, s): let external = externalDep(d, s) - if d.section[k] != "": d.section[k].add(", ") - dispA(d.conf, d.section[k], + if d.section[k].finalMarkup != "": d.section[k].finalMarkup.add(", ") + dispA(d.conf, d.section[k].finalMarkup, "$1", "$1", [esc(d.target, external.prettyLink), changeFileExt(external, "html")]) @@ -968,9 +997,9 @@ proc exportSym(d: PDoc; s: PSym) = complexSymbol = complexName(s.kind, s.ast, s.name.s) symbolOrId = d.newUniquePlainSymbol(complexSymbol) external = externalDep(d, module) - if d.section[k] != "": d.section[k].add(", ") + if d.section[k].finalMarkup != "": d.section[k].finalMarkup.add(", ") # XXX proper anchor generation here - dispA(d.conf, d.section[k], + dispA(d.conf, d.section[k].finalMarkup, "$1", "$1", [esc(d.target, s.name.s), changeFileExt(external, "html"), @@ -1034,13 +1063,16 @@ proc documentRaises*(cache: IdentCache; n: PNode) = if p5 != nil: n[pragmasPos].add p5 proc generateDoc*(d: PDoc, n, orig: PNode, docFlags: DocFlags = kDefault) = + ## Goes through nim nodes recursively and collects doc comments. + ## Main function for `doc`:option: command, + ## which is implemented in ``docgen2.nim``. template genItemAux(skind) = genItem(d, n, n[namePos], skind, docFlags) case n.kind of nkPragma: let pragmaNode = findPragma(n, wDeprecated) d.modDeprecationMsg.add(genDeprecationMsg(d, pragmaNode)) - of nkCommentStmt: d.modDesc.add(genComment(d, n)) + of nkCommentStmt: d.modDescPre.add(genComment(d, n)) of nkProcDef, nkFuncDef: when useEffectSystem: documentRaises(d.cache, n) genItemAux(skProc) @@ -1079,21 +1111,63 @@ proc generateDoc*(d: PDoc, n, orig: PNode, docFlags: DocFlags = kDefault) = of nkExportExceptStmt: discard "transformed into nkExportStmt by semExportExcept" of nkFromStmt, nkImportExceptStmt: traceDeps(d, n[0]) of nkCallKinds: - var comm = "" + var comm: ItemPre getAllRunnableExamples(d, n, comm) - if comm != "": d.modDesc.add(comm) + if comm.len != 0: d.modDescPre.add(comm) else: discard -proc add(d: PDoc; j: JsonNode) = - if j != nil: d.jArray.add j +proc finishGenerateDoc*(d: var PDoc) = + ## Perform 2nd RST pass for resolution of links/footnotes/headings... + # Main title/subtitle are allowed only in the first RST fragment of document + var firstRst = PRstNode(nil) + for fragment in d.modDescPre: + if fragment.isRst: + firstRst = fragment.rst + break + preparePass2(d.sharedState, firstRst) + + # Finalize fragments of ``.nim`` or ``.rst`` file + proc renderItemPre(d: PDoc, fragments: ItemPre, result: var string) = + for f in fragments: + case f.isRst: + of true: + var resolved = resolveSubs(d.sharedState, f.rst) + renderRstToOut(d[], resolved, result) + of false: result &= f.str + for k in TSymKind: + for item in d.section[k].secItems: + var itemDesc: string + renderItemPre(d, item.descRst, itemDesc) + d.section[k].finalMarkup.add( + getConfigVar(d.conf, "doc.item") % ( + item.substitutions & @["desc", itemDesc])) + itemDesc = "" + d.section[k].secItems.setLen 0 + renderItemPre(d, d.modDescPre, d.modDescFinal) + d.modDescPre.setLen 0 + d.hasToc = d.hasToc or d.sharedState.hasToc + + # Finalize fragments of ``.json`` file + for i, entry in d.jEntriesPre: + if entry.rst != nil: + let resolved = resolveSubs(d.sharedState, entry.rst) + var str: string + renderRstToOut(d[], resolved, str) + entry.json[entry.rstField] = %str + d.jEntriesFinal.add entry.json + d.jEntriesPre[i].rst = nil + +proc add(d: PDoc; j: JsonItem) = + if j.json != nil or j.rst != nil: d.jEntriesPre.add j proc generateJson*(d: PDoc, n: PNode, includeComments: bool = true) = case n.kind of nkCommentStmt: if includeComments: - d.add %*{"comment": genComment(d, n)} + d.add JsonItem(rst: genComment(d, n), rstField: "comment", + json: %Table[string, string]()) else: - d.modDesc.add(genComment(d, n)) + d.modDescPre.add(genComment(d, n)) of nkProcDef, nkFuncDef: when useEffectSystem: documentRaises(d.cache, n) d.add genJsonItem(d, n, n[namePos], skProc) @@ -1173,11 +1247,11 @@ proc genSection(d: PDoc, kind: TSymKind, groupedToc = false) = "Imports", "Types", "Vars", "Lets", "Consts", "Vars", "Procs", "Funcs", "Methods", "Iterators", "Converters", "Macros", "Templates", "Exports" ] - if d.section[kind] == "": return + if d.section[kind].finalMarkup == "": return var title = sectionNames[kind] - d.section[kind] = getConfigVar(d.conf, "doc.section") % [ + d.section[kind].finalMarkup = getConfigVar(d.conf, "doc.section") % [ "sectionid", $ord(kind), "sectionTitle", title, - "sectionTitleID", $(ord(kind) + 50), "content", d.section[kind]] + "sectionTitleID", $(ord(kind) + 50), "content", d.section[kind].finalMarkup] var tocSource = d.toc if groupedToc: @@ -1209,7 +1283,7 @@ proc genOutFile(d: PDoc, groupedToc = false): string = if toc != "" or d.target == outLatex: # for Latex $doc.toc will automatically generate TOC if `d.hasToc` is set toc = getConfigVar(d.conf, "doc.toc") % ["content", toc] - for i in TSymKind: code.add(d.section[i]) + for i in TSymKind: code.add(d.section[i].finalMarkup) # Extract the title. Non API modules generate an entry in the index table. if d.meta[metaTitle].len != 0: @@ -1234,7 +1308,7 @@ proc genOutFile(d: PDoc, groupedToc = false): string = let seeSrc = genSeeSrc(d, d.filename, 1) content = getConfigVar(d.conf, bodyname) % [ "title", title, "subtitle", subtitle, - "tableofcontents", toc, "moduledesc", d.modDesc, "date", getDateStr(), + "tableofcontents", toc, "moduledesc", d.modDescFinal, "date", getDateStr(), "time", getClockStr(), "content", code, "deprecationMsg", d.modDeprecationMsg, "theindexhref", relLink(d.conf.outDir, d.destFile.AbsoluteFile, @@ -1248,7 +1322,7 @@ proc genOutFile(d: PDoc, groupedToc = false): string = "dochackjs", relLink(d.conf.outDir, d.destFile.AbsoluteFile, docHackJsFname.RelativeFile), "title", title, "subtitle", subtitle, "tableofcontents", toc, - "moduledesc", d.modDesc, "date", getDateStr(), "time", getClockStr(), + "moduledesc", d.modDescFinal, "date", getDateStr(), "time", getClockStr(), "content", content, "author", d.meta[metaAuthor], "version", esc(d.target, d.meta[metaVersion]), "analytics", d.analytics, "deprecationMsg", d.modDeprecationMsg] @@ -1297,12 +1371,12 @@ proc writeOutput*(d: PDoc, useWarning = false, groupedToc = false) = proc writeOutputJson*(d: PDoc, useWarning = false) = runAllExamples(d) var modDesc: string - for desc in d.modDesc: + for desc in d.modDescFinal: modDesc &= desc let content = %*{"orig": d.filename, "nimble": getPackageName(d.conf, d.filename), "moduleDescription": modDesc, - "entries": d.jArray} + "entries": d.jEntriesFinal} if optStdout in d.conf.globalOptions: write(stdout, $content) else: @@ -1324,12 +1398,14 @@ proc handleDocOutputOptions*(conf: ConfigRef) = conf.outDir = AbsoluteDir(conf.outDir / conf.outFile) proc commandDoc*(cache: IdentCache, conf: ConfigRef) = + ## implementation of deprecated ``doc0`` command (without semantic checking) handleDocOutputOptions conf var ast = parseFile(conf.projectMainIdx, cache, conf) if ast == nil: return var d = newDocumentor(conf.projectFull, cache, conf) d.hasToc = true generateDoc(d, ast, ast) + finishGenerateDoc(d) writeOutput(d) generateIndex(d) @@ -1339,14 +1415,13 @@ proc commandRstAux(cache: IdentCache, conf: ConfigRef; var d = newDocumentor(filen, cache, conf, outExt) d.isPureRst = true - var rst = parseRst(readFile(filen.string), filen.string, + let rst = parseRst(readFile(filen.string), filen.string, line=LineRstInit, column=ColRstInit, - d.hasToc, - {roSupportRawDirective, roSupportMarkdown, roPreferMarkdown}, - conf) - var modDesc = newStringOfCap(30_000) - renderRstToOut(d[], rst, modDesc) - d.modDesc = modDesc + {roSupportRawDirective, roSupportMarkdown, + roPreferMarkdown}, conf, + d.sharedState) + d.modDescPre = @[ItemFragment(isRst: true, rst: rst)] + finishGenerateDoc(d) writeOutput(d) generateIndex(d) @@ -1357,6 +1432,7 @@ proc commandRst2TeX*(cache: IdentCache, conf: ConfigRef) = commandRstAux(cache, conf, conf.projectFull, TexExt) proc commandJson*(cache: IdentCache, conf: ConfigRef) = + ## implementation of a deprecated jsondoc0 command var ast = parseFile(conf.projectMainIdx, cache, conf) if ast == nil: return var d = newDocumentor(conf.projectFull, cache, conf) @@ -1366,7 +1442,8 @@ proc commandJson*(cache: IdentCache, conf: ConfigRef) = warnUser, "the ':test:' attribute is not supported by this backend") d.hasToc = true generateJson(d, ast) - let json = d.jArray + finishGenerateDoc(d) + let json = d.jEntriesFinal let content = pretty(json) if optStdout in d.conf.globalOptions: diff --git a/compiler/docgen2.nim b/compiler/docgen2.nim index d077ddc67..bfdb4568c 100644 --- a/compiler/docgen2.nim +++ b/compiler/docgen2.nim @@ -31,6 +31,7 @@ template closeImpl(body: untyped) {.dirty.} = let useWarning = sfMainModule notin g.module.flags let groupedToc = true if shouldProcess(g): + finishGenerateDoc(g.doc) body try: generateIndex(g.doc) diff --git a/compiler/main.nim b/compiler/main.nim index 7824e8cb2..1c8034c6c 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -282,7 +282,7 @@ proc mainCommand*(graph: ModuleGraph) = else: rawMessage(conf, errGenerated, "'run' command not available; rebuild with -d:tinyc") of cmdDoc0: docLikeCmd commandDoc(cache, conf) - of cmdDoc2: + of cmdDoc: docLikeCmd(): conf.setNoteDefaults(warnLockLevel, false) # issue #13218 conf.setNoteDefaults(warnRedefinitionOfLabel, false) # issue #13218 diff --git a/compiler/options.nim b/compiler/options.nim index 05d8ee5b7..ffb0f4501 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -143,8 +143,8 @@ type cmdIdeTools # ide tools (e.g. nimsuggest) cmdNimscript # evaluate nimscript cmdDoc0 - cmdDoc2 - cmdDoc2tex + cmdDoc # convert .nim doc comments to HTML + cmdDoc2tex # convert .nim doc comments to LaTeX cmdRst2html # convert a reStructuredText file to HTML cmdRst2tex # convert a reStructuredText file to TeX cmdJsondoc0 @@ -161,7 +161,7 @@ type const cmdBackends* = {cmdCompileToC, cmdCompileToCpp, cmdCompileToOC, cmdCompileToJS, cmdCrun} - cmdDocLike* = {cmdDoc0, cmdDoc2, cmdDoc2tex, cmdJsondoc0, cmdJsondoc, + cmdDocLike* = {cmdDoc0, cmdDoc, cmdDoc2tex, cmdJsondoc0, cmdJsondoc, cmdCtags, cmdBuildindex} type diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 336caa2df..031a86d98 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -441,7 +441,7 @@ proc rawGetTok(L: var Lexer, tok: var Token) = inc L.col tok.col = max(tok.col - L.baseIndent, 0) -proc getTokens(buffer: string, tokens: var TokenSeq): int = +proc getTokens(buffer: string, tokens: var TokenSeq) = var L: Lexer var length = tokens.len L.buf = cstring(buffer) @@ -489,7 +489,7 @@ type autoSymIdx: int # order of occurence: fnAutoSymbol label: string # valid for fnAutoNumberLabel - SharedState = object + RstSharedState = object options: RstParseOptions # parsing options hLevels: LevelMap # hierarchy of heading styles hTitleCnt: int # =0 if no title, =1 if only main title, @@ -498,8 +498,8 @@ type currRole: string # current interpreted text role currRoleKind: RstNodeKind # ... and its node kind subs: seq[Substitution] # substitutions - refs: seq[Substitution] # references - anchors: seq[AnchorSubst] # internal target substitutions + refs*: seq[Substitution] # references + anchors*: seq[AnchorSubst] # internal target substitutions lineFootnoteNum: seq[int] # footnote line, auto numbers .. [#] lineFootnoteNumRef: seq[int] # footnote line, their reference [#]_ lineFootnoteSym: seq[int] # footnote line, auto symbols .. [*] @@ -508,19 +508,19 @@ type # number, order of occurrence msgHandler: MsgHandler # How to handle errors. findFile: FindFileHandler # How to find files. + filename: string + hasToc*: bool - PSharedState = ref SharedState + PRstSharedState* = ref RstSharedState RstParser = object of RootObj idx*: int tok*: TokenSeq - s*: PSharedState + s*: PRstSharedState indentStack*: seq[int] - filename*: string line*, col*: int ## initial line/column of whole text or ## documenation fragment that will be added ## in case of error/warning reporting to ## (relative) line/column of the token. - hasToc*: bool curAnchor*: string # variable to track latest anchor in s.anchors EParseError* = object of ValueError @@ -578,35 +578,42 @@ proc whichRoleAux(sym: string): RstNodeKind = else: # unknown role result = rnUnknownRole -proc newSharedState(options: RstParseOptions, - findFile: FindFileHandler, - msgHandler: MsgHandler): PSharedState = - new(result) - result.currRole = defaultRole(options) - result.currRoleKind = whichRoleAux(result.currRole) - result.subs = @[] - result.refs = @[] - result.options = options - result.msgHandler = if not isNil(msgHandler): msgHandler else: defaultMsgHandler - result.findFile = if not isNil(findFile): findFile else: defaultFindFile +proc newRstSharedState*(options: RstParseOptions, + filename: string, + findFile: FindFileHandler, + msgHandler: MsgHandler): PRstSharedState = + let r = defaultRole(options) + result = PRstSharedState( + currRole: r, + currRoleKind: whichRoleAux(r), + options: options, + msgHandler: if not isNil(msgHandler): msgHandler else: defaultMsgHandler, + filename: filename, + findFile: if not isNil(findFile): findFile else: defaultFindFile + ) proc curLine(p: RstParser): int = p.line + currentTok(p).line proc findRelativeFile(p: RstParser; filename: string): string = - result = p.filename.splitFile.dir / filename + result = p.s.filename.splitFile.dir / filename if not fileExists(result): result = p.s.findFile(filename) proc rstMessage(p: RstParser, msgKind: MsgKind, arg: string) = - p.s.msgHandler(p.filename, curLine(p), + p.s.msgHandler(p.s.filename, curLine(p), p.col + currentTok(p).col, msgKind, arg) +proc rstMessage(s: PRstSharedState, msgKind: MsgKind, arg: string) = + ## Print warnings for footnotes/substitutions. + ## TODO: their line/column info is not known, to fix it. + s.msgHandler(s.filename, LineRstInit, ColRstInit, msgKind, arg) + proc rstMessage(p: RstParser, msgKind: MsgKind, arg: string, line, col: int) = - p.s.msgHandler(p.filename, p.line + line, + p.s.msgHandler(p.s.filename, p.line + line, p.col + col, msgKind, arg) proc rstMessage(p: RstParser, msgKind: MsgKind) = - p.s.msgHandler(p.filename, curLine(p), + p.s.msgHandler(p.s.filename, curLine(p), p.col + currentTok(p).col, msgKind, currentTok(p).symbol) @@ -680,12 +687,10 @@ proc popInd(p: var RstParser) = # whether it should continue its processing or not, and decided not to, # then this B.E. handler should step back (e.g. do `dec p.idx`). -proc initParser(p: var RstParser, sharedState: PSharedState) = +proc initParser(p: var RstParser, sharedState: PRstSharedState) = p.indentStack = @[0] p.tok = @[] p.idx = 0 - p.filename = "" - p.hasToc = false p.col = ColRstInit p.line = LineRstInit p.s = sharedState @@ -754,14 +759,14 @@ proc rstnodeToRefname(n: PRstNode): string = var b = false rstnodeToRefnameAux(n, result, b) -proc findSub(p: var RstParser, n: PRstNode): int = +proc findSub(s: PRstSharedState, n: PRstNode): int = var key = addNodes(n) # the spec says: if no exact match, try one without case distinction: - for i in countup(0, high(p.s.subs)): - if key == p.s.subs[i].key: + for i in countup(0, high(s.subs)): + if key == s.subs[i].key: return i - for i in countup(0, high(p.s.subs)): - if cmpIgnoreStyle(key, p.s.subs[i].key) == 0: + for i in countup(0, high(s.subs)): + if cmpIgnoreStyle(key, s.subs[i].key) == 0: return i result = -1 @@ -783,10 +788,10 @@ proc setRef(p: var RstParser, key: string, value: PRstNode) = return p.s.refs.add(Substitution(key: key, value: value)) -proc findRef(p: var RstParser, key: string): PRstNode = - for i in countup(0, high(p.s.refs)): - if key == p.s.refs[i].key: - return p.s.refs[i].value +proc findRef(s: PRstSharedState, key: string): PRstNode = + for i in countup(0, high(s.refs)): + if key == s.refs[i].key: + return s.refs[i].value proc addAnchor(p: var RstParser, refn: string, reset: bool) = ## add anchor `refn` to anchor aliases and update last anchor ``curAnchor`` @@ -800,8 +805,8 @@ proc addAnchor(p: var RstParser, refn: string, reset: bool) = else: p.curAnchor = refn -proc findMainAnchor(p: RstParser, refn: string): string = - for subst in p.s.anchors: +proc findMainAnchor(s: PRstSharedState, refn: string): string = + for subst in s.anchors: if subst.mainAnchor == refn: # no need to rename result = subst.mainAnchor break @@ -838,28 +843,28 @@ proc addFootnoteSymAuto(p: var RstParser) = p.s.lineFootnoteSym.add curLine(p) p.s.footnotes.add((fnAutoSymbol, -1, -1, p.s.lineFootnoteSym.len, "")) -proc orderFootnotes(p: var RstParser) = +proc orderFootnotes(s: PRstSharedState) = ## numerate auto-numbered footnotes taking into account that all ## manually numbered ones always have preference. - ## Save the result back to p.s.footnotes. + ## Save the result back to `s.footnotes`. # Report an error if found any mismatch in number of automatic footnotes proc listFootnotes(lines: seq[int]): string = result.add $lines.len & " (lines " & join(lines, ", ") & ")" - if p.s.lineFootnoteNum.len != p.s.lineFootnoteNumRef.len: - rstMessage(p, meFootnoteMismatch, - "$1 != $2" % [listFootnotes(p.s.lineFootnoteNum), - listFootnotes(p.s.lineFootnoteNumRef)] & + if s.lineFootnoteNum.len != s.lineFootnoteNumRef.len: + rstMessage(s, meFootnoteMismatch, + "$1 != $2" % [listFootnotes(s.lineFootnoteNum), + listFootnotes(s.lineFootnoteNumRef)] & " for auto-numbered footnotes") - if p.s.lineFootnoteSym.len != p.s.lineFootnoteSymRef.len: - rstMessage(p, meFootnoteMismatch, - "$1 != $2" % [listFootnotes(p.s.lineFootnoteSym), - listFootnotes(p.s.lineFootnoteSymRef)] & + if s.lineFootnoteSym.len != s.lineFootnoteSymRef.len: + rstMessage(s, meFootnoteMismatch, + "$1 != $2" % [listFootnotes(s.lineFootnoteSym), + listFootnotes(s.lineFootnoteSymRef)] & " for auto-symbol footnotes") var result: seq[FootnoteSubst] var manuallyN, autoN, autoSymbol: seq[FootnoteSubst] - for fs in p.s.footnotes: + for fs in s.footnotes: if fs.kind == fnManualNumber: manuallyN.add fs elif fs.kind in {fnAutoNumber, fnAutoNumberLabel}: autoN.add fs else: autoSymbol.add fs @@ -906,26 +911,26 @@ proc orderFootnotes(p: var RstParser) = let label = footnoteAutoSymbols[symbolNum].repeat(nSymbols) result.add((fs.kind, -1, -1, fs.autoSymIdx, label)) - p.s.footnotes = result + s.footnotes = result -proc getFootnoteNum(p: var RstParser, label: string): int = +proc getFootnoteNum(s: PRstSharedState, label: string): int = ## get number from label. Must be called after `orderFootnotes`. result = -1 - for fnote in p.s.footnotes: + for fnote in s.footnotes: if fnote.label == label: return fnote.number -proc getFootnoteNum(p: var RstParser, order: int): int = +proc getFootnoteNum(s: PRstSharedState, order: int): int = ## get number from occurrence. Must be called after `orderFootnotes`. result = -1 - for fnote in p.s.footnotes: + for fnote in s.footnotes: if fnote.autoNumIdx == order: return fnote.number -proc getAutoSymbol(p: var RstParser, order: int): string = +proc getAutoSymbol(s: PRstSharedState, order: int): string = ## get symbol from occurrence of auto-symbol footnote. result = "???" - for fnote in p.s.footnotes: + for fnote in s.footnotes: if fnote.autoSymIdx == order: return fnote.label @@ -1765,17 +1770,18 @@ proc getLevel(p: var RstParser, c: char, hasOverline: bool): int = line: curLine(p), hasPeers: false) result = p.s.hLevels.len - 1 -proc countTitles(p: var RstParser, n: PRstNode) = - ## Fill `p.s.hTitleCnt` +proc countTitles(s: PRstSharedState, n: PRstNode) = + ## Fill `s.hTitleCnt` + if n == nil: return for node in n.sons: if node != nil: if node.kind notin {rnOverline, rnSubstitutionDef, rnDefaultRole}: break if node.kind == rnOverline: - if p.s.hLevels[p.s.hTitleCnt].hasPeers: + if s.hLevels[s.hTitleCnt].hasPeers: break - inc p.s.hTitleCnt - if p.s.hTitleCnt >= 2: + inc s.hTitleCnt + if s.hTitleCnt >= 2: break proc isAdornmentHeadline(p: RstParser, adornmentIdx: int): bool = @@ -2103,8 +2109,7 @@ proc parseSimpleTable(p: var RstParser): PRstNode = initParser(q, p.s) q.col = cols[j] q.line = line - 1 - q.filename = p.filename - q.col += getTokens(row[j], q.tok) + getTokens(row[j], q.tok) b = newRstNode(rnTableDataCell) b.add(parseDoc(q)) a.add(b) @@ -2156,8 +2161,7 @@ proc parseMarkdownTable(p: var RstParser): PRstNode = initParser(q, p.s) q.col = p.col q.line = currentTok(p).line - 1 - q.filename = p.filename - q.col += getTokens(getColContents(p, row[j]), q.tok) + getTokens(getColContents(p, row[j]), q.tok) b.add(parseDoc(q)) a.add(b) result.add(a) @@ -2562,8 +2566,8 @@ proc dirInclude(p: var RstParser): PRstNode = var q: RstParser initParser(q, p.s) - q.filename = path - q.col += getTokens( + q.s.filename = path + getTokens( inputString[startPosition..endPosition].strip(), q.tok) # workaround a GCC bug; more like the interior pointer bug? @@ -2806,7 +2810,27 @@ proc parseDotDot(p: var RstParser): PRstNode = else: result = parseComment(p) -proc resolveSubs(p: var RstParser, n: PRstNode): PRstNode = +proc rstParsePass1*(fragment, filename: string, + line, column: int, + options: RstParseOptions, + sharedState: PRstSharedState): PRstNode = + ## Parses an RST `fragment`. + ## The result should be further processed by + ## `preparePass2` and `resolveSubs` (which is pass 2). + var p: RstParser + initParser(p, sharedState) + p.line = line + p.col = column + getTokens(fragment, p.tok) + result = parseDoc(p) + +proc preparePass2*(s: PRstSharedState, mainNode: PRstNode) = + ## Records titles in node `mainNode` and orders footnotes. + countTitles(s, mainNode) + orderFootnotes(s) + +proc resolveSubs*(s: PRstSharedState, n: PRstNode): PRstNode = + ## Makes pass 2 of RST parsing. ## Resolves substitutions and anchor aliases, groups footnotes. ## Takes input node `n` and returns the same node with recursive ## substitutions in `n.sons` to `result`. @@ -2814,32 +2838,32 @@ proc resolveSubs(p: var RstParser, n: PRstNode): PRstNode = if n == nil: return case n.kind of rnSubstitutionReferences: - var x = findSub(p, n) + var x = findSub(s, n) if x >= 0: - result = p.s.subs[x].value + result = s.subs[x].value else: var key = addNodes(n) var e = getEnv(key) if e != "": result = newLeaf(e) - else: rstMessage(p, mwUnknownSubstitution, key) + else: rstMessage(s, mwUnknownSubstitution, key) of rnHeadline, rnOverline: # fix up section levels depending on presence of a title and subtitle - if p.s.hTitleCnt == 2: + if s.hTitleCnt == 2: if n.level == 1: # it's the subtitle n.level = 0 elif n.level >= 2: # normal sections n.level -= 1 - elif p.s.hTitleCnt == 0: + elif s.hTitleCnt == 0: n.level += 1 of rnRef: let refn = rstnodeToRefname(n) - var y = findRef(p, refn) + var y = findRef(s, refn) if y != nil: result = newRstNode(rnHyperlink) let text = newRstNode(rnInner, n.sons) result.sons = @[text, y] else: - let s = findMainAnchor(p, refn) + let s = findMainAnchor(s, refn) if s != "": result = newRstNode(rnInternalRef) let text = newRstNode(rnInner, n.sons) @@ -2853,16 +2877,16 @@ proc resolveSubs(p: var RstParser, n: PRstNode): PRstNode = of fnAutoNumberLabel, fnAutoNumber: if fnType == fnAutoNumberLabel: let labelR = rstnodeToRefname(n.sons[0]) - num = getFootnoteNum(p, labelR) + num = getFootnoteNum(s, labelR) else: - num = getFootnoteNum(p, n.order) + num = getFootnoteNum(s, n.order) var nn = newRstNode(rnInner) nn.add newLeaf($num) result.sons[0] = nn of fnAutoSymbol: - let sym = getAutoSymbol(p, n.order) + let sym = getAutoSymbol(s, n.order) n.sons[0].sons[0].text = sym - n.sons[1] = resolveSubs(p, n.sons[1]) + n.sons[1] = resolveSubs(s, n.sons[1]) of rnFootnoteRef: var (fnType, num) = getFootnoteType(n.sons[0]) template addLabel(number: int | string) = @@ -2877,31 +2901,31 @@ proc resolveSubs(p: var RstParser, n: PRstNode): PRstNode = addLabel num refn.add $num of fnAutoNumber: - addLabel getFootnoteNum(p, n.order) + addLabel getFootnoteNum(s, n.order) refn.add $n.order of fnAutoNumberLabel: - addLabel getFootnoteNum(p, rstnodeToRefname(n)) + addLabel getFootnoteNum(s, rstnodeToRefname(n)) refn.add rstnodeToRefname(n) of fnAutoSymbol: - addLabel getAutoSymbol(p, n.order) + addLabel getAutoSymbol(s, n.order) refn.add $n.order of fnCitation: result.add n.sons[0] refn.add rstnodeToRefname(n) - let s = findMainAnchor(p, refn) - if s != "": - result.add newLeaf(s) # add link + let anch = findMainAnchor(s, refn) + if anch != "": + result.add newLeaf(anch) # add link else: - rstMessage(p, mwUnknownSubstitution, refn) + rstMessage(s, mwUnknownSubstitution, refn) result.add newLeaf(refn) # add link of rnLeaf: discard of rnContents: - p.hasToc = true + s.hasToc = true else: var regroup = false for i in 0 ..< n.len: - n.sons[i] = resolveSubs(p, n.sons[i]) + n.sons[i] = resolveSubs(s, n.sons[i]) if n.sons[i] != nil and n.sons[i].kind == rnFootnote: regroup = true if regroup: # group footnotes together into rnFootnoteGroup @@ -2924,13 +2948,10 @@ proc rstParse*(text, filename: string, options: RstParseOptions, findFile: FindFileHandler = nil, msgHandler: MsgHandler = nil): PRstNode = - var p: RstParser - initParser(p, newSharedState(options, findFile, msgHandler)) - p.filename = filename - p.line = line - p.col = column + getTokens(text, p.tok) - let unresolved = parseDoc(p) - countTitles(p, unresolved) - orderFootnotes(p) - result = resolveSubs(p, unresolved) - hasToc = p.hasToc + ## Parses the whole `text`. The result is ready for `rstgen.renderRstToOut`. + var sharedState = newRstSharedState(options, filename, findFile, msgHandler) + let unresolved = rstParsePass1(text, filename, line, column, + options, sharedState) + preparePass2(sharedState, unresolved) + result = resolveSubs(sharedState, unresolved) + hasToc = sharedState.hasToc diff --git a/nimdoc/testproject/expected/subdir/subdir_b/utils.idx b/nimdoc/testproject/expected/subdir/subdir_b/utils.idx index b49a777c8..6dc3953af 100644 --- a/nimdoc/testproject/expected/subdir/subdir_b/utils.idx +++ b/nimdoc/testproject/expected/subdir/subdir_b/utils.idx @@ -1,8 +1,3 @@ -This is now a header subdir/subdir_b/utils.html#this-is-now-a-header This is now a header -Next header subdir/subdir_b/utils.html#this-is-now-a-header-next-header Next header -And so on subdir/subdir_b/utils.html#next-header-and-so-on And so on -More headers subdir/subdir_b/utils.html#more-headers More headers -Up to level 6 subdir/subdir_b/utils.html#more-headers-up-to-level-6 Up to level 6 enumValueA subdir/subdir_b/utils.html#enumValueA SomeType.enumValueA enumValueB subdir/subdir_b/utils.html#enumValueB SomeType.enumValueB enumValueC subdir/subdir_b/utils.html#enumValueC SomeType.enumValueC @@ -11,3 +6,8 @@ someType subdir/subdir_b/utils.html#someType_2 utils: someType(): SomeType aEnum subdir/subdir_b/utils.html#aEnum.t utils: aEnum(): untyped bEnum subdir/subdir_b/utils.html#bEnum.t utils: bEnum(): untyped fromUtilsGen subdir/subdir_b/utils.html#fromUtilsGen.t utils: fromUtilsGen(): untyped +This is now a header subdir/subdir_b/utils.html#this-is-now-a-header This is now a header +Next header subdir/subdir_b/utils.html#this-is-now-a-header-next-header Next header +And so on subdir/subdir_b/utils.html#next-header-and-so-on And so on +More headers subdir/subdir_b/utils.html#more-headers More headers +Up to level 6 subdir/subdir_b/utils.html#more-headers-up-to-level-6 Up to level 6 diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim index 34b172935..300229648 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -997,7 +997,7 @@ Test1 """ var error5 = new string let output5 = input5.toHtml(error=error5) - check(error5[] == "input(6, 1) Error: mismatch in number of footnotes " & + check(error5[] == "input(1, 1) Error: mismatch in number of footnotes " & "and their refs: 1 (lines 2) != 0 (lines ) for auto-numbered " & "footnotes") @@ -1011,7 +1011,7 @@ Test1 """ var error6 = new string let output6 = input6.toHtml(error=error6) - check(error6[] == "input(6, 1) Error: mismatch in number of footnotes " & + check(error6[] == "input(1, 1) Error: mismatch in number of footnotes " & "and their refs: 1 (lines 3) != 2 (lines 2, 6) for auto-symbol " & "footnotes") @@ -1034,7 +1034,8 @@ Test1 """ var warnings8 = new seq[string] let output8 = input8.toHtml(warnings=warnings8) - check(warnings8[] == @["input(4, 1) Warning: unknown substitution " & + # TODO: the line 1 is arbitrary because reference lines are not preserved + check(warnings8[] == @["input(1, 1) Warning: unknown substitution " & "\'citation-som\'"]) # check that footnote group does not break parsing of other directives: From 6030e139b5c630b5508dbe12d7b58c56264212b1 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 20 Jun 2021 00:51:07 -0700 Subject: [PATCH 2244/3768] move {.injectStmt.} to experimental; add a test (#18300) * move {.injectStmt.} to experimental; add a test * undocument and deprecat `.injectStmt` but keep its implementation until we have a replacement --- compiler/pragmas.nim | 5 ++-- doc/manual.rst | 11 -------- doc/manual_experimental.rst | 1 + lib/pure/asyncdispatch.nim | 2 -- lib/system/gc_ms.nim | 1 + tests/pragmas/tinjectstmt.nim | 48 +++++++++++++++++++++++++++++++++++ 6 files changed, 53 insertions(+), 15 deletions(-) create mode 100644 tests/pragmas/tinjectstmt.nim diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 695e595c3..8ae931d84 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -1183,6 +1183,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, if sym == nil: invalidPragma(c, it) else: magicsys.registerNimScriptSymbol(c.graph, sym) of wInjectStmt: + warningDeprecated(c.config, it.info, "'.injectStmt' pragma is deprecated") if it.kind notin nkPragmaCallKinds or it.len != 2: localError(c.config, it.info, "expression expected") else: @@ -1194,10 +1195,10 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of wThis: if it.kind in nkPragmaCallKinds and it.len == 2: c.selfName = considerQuotedIdent(c, it[1]) - message(c.config, n.info, warnDeprecated, "the '.this' pragma is deprecated") + message(c.config, n.info, warnDeprecated, "'.this' pragma is deprecated") elif it.kind == nkIdent or it.len == 1: c.selfName = getIdent(c.cache, "self") - message(c.config, n.info, warnDeprecated, "the '.this' pragma is deprecated") + message(c.config, n.info, warnDeprecated, "'.this' pragma is deprecated") else: localError(c.config, it.info, "'this' pragma is allowed to have zero or one arguments") of wNoRewrite: diff --git a/doc/manual.rst b/doc/manual.rst index 46a40a690..e8eec9a57 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -7432,17 +7432,6 @@ work properly (in particular regarding constructor and destructor) for proc main()= var a {.threadvar.}: Foo -InjectStmt pragma ------------------ - -The `injectStmt` pragma can be used to inject a statement before every -other statement in the current module. It is only supposed to be used for -debugging: - -.. code-block:: nim - {.injectStmt: gcInvariants().} - - # ... complex code here that produces crashes ... compile-time define pragmas --------------------------- diff --git a/doc/manual_experimental.rst b/doc/manual_experimental.rst index 3157708da..d10ef8d1b 100644 --- a/doc/manual_experimental.rst +++ b/doc/manual_experimental.rst @@ -380,6 +380,7 @@ pass multiple blocks to a macro: # code to undo it + Special Operators ================= diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 3866ebd24..b88d5ede8 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -176,8 +176,6 @@ export Port, SocketFlag export asyncfutures except callSoon export asyncstreams -#{.injectStmt: newGcInvariant().} - # TODO: Check if yielded future is nil and throw a more meaningful exception type diff --git a/lib/system/gc_ms.nim b/lib/system/gc_ms.nim index c20e93699..9e306c497 100644 --- a/lib/system/gc_ms.nim +++ b/lib/system/gc_ms.nim @@ -429,6 +429,7 @@ proc sweep(gch: var GcHeap) = else: freeCyclicCell(gch, c) when false: + # meant to be used with the now-deprected `.injectStmt`: {.injectStmt: newGcInvariant().} proc newGcInvariant*() = for x in allObjects(gch.region): if isCell(x): diff --git a/tests/pragmas/tinjectstmt.nim b/tests/pragmas/tinjectstmt.nim new file mode 100644 index 000000000..bca041e46 --- /dev/null +++ b/tests/pragmas/tinjectstmt.nim @@ -0,0 +1,48 @@ +discard """ + joinable: false + output:''' +onInject: 1 +onInject: 2 +ok0 +ok1 +onInject: 3 +onInject: 4 +0 +onInject: 5 +onInject: 6 +1 +onInject: 7 +onInject: 8 +2 +ok2 +onInject: 9 +''' +""" + +# test {.injectStmt.} + +#[ +{.injectStmt.} pragma can be used to inject a statement before every +other statement in the current module. It's now undocumented and may be removed +in the future and replaced with something more general and without its limitations. +e.g. (e.g. doesn't work in VM or js backends). +]# + +from system/ansi_c import c_printf + +var count = 0 +proc onInject*() = + count.inc + # echo count # xxx would fail, probably infinite recursion + c_printf("onInject: %d\n", cast[int](count)) + +{.injectStmt: onInject().} +echo "ok0" +proc main()= + echo "ok1" + for a in 0..<3: + echo a + echo "ok2" + +static: main() # xxx injectStmt not honred in VM +main() From e45b858f0402bbae428bdfa86f72613b69c2671a Mon Sep 17 00:00:00 2001 From: flywind Date: Sun, 20 Jun 2021 15:57:59 +0800 Subject: [PATCH 2245/3768] [std/terminal] improve docs a bit (#18296) * Revert "add missing import to asynchttpserver's example" This reverts commit 7ef364a402d3d827f10c893280f8dc7b9ef056f5. * alternative to #18185 * add std/mutexes * cvlose #17696 * Revert "add std/mutexes" This reverts commit 69abc8b64954206da6ffe5fc40a1142b39777762. * tiny * test * improve terminal docs * follow advice --- lib/pure/terminal.nim | 115 +++++++++++++++++------------------------- 1 file changed, 45 insertions(+), 70 deletions(-) diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index cada72196..283652bb3 100644 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -16,6 +16,34 @@ ## Similarly, if you hide the cursor, make sure to unhide it with ## `showCursor` before quitting. +##[ +## Playing with colorful and styled text +]## + +## Procs like `styledWriteLine`, `styledEcho` etc. have a temporary effect on +## text parameters. Style parameters only affect the text parameter right after them. +## After being called, these procs will reset the default style of the terminal. +## While `setBackGroundColor`, `setForeGroundColor` etc. have a lasting +## influence on the terminal, you can use `resetAttributes` to +## reset the default style of the terminal. +runnableExamples("-r:off"): + stdout.styledWriteLine({styleBright, styleBlink, styleUnderscore}, "styled text ") + stdout.styledWriteLine(fgRed, "red text ") + stdout.styledWriteLine(fgWhite, bgRed, "white text in red background") + stdout.styledWriteLine(" ordinary text without style ") + + stdout.setBackGroundColor(bgCyan, true) + stdout.setForeGroundColor(fgBlue) + stdout.write("blue text in cyan background") + stdout.resetAttributes() + + # You can specify multiple text parameters. Style parameters + # only affect the text parameter right after them. + styledEcho styleBright, fgGreen, "[PASS]", resetStyle, fgGreen, " Yay!" + + stdout.styledWriteLine(fgRed, "red text ", styleBright, "bold red", fgDefault, " bold text") + + import macros import strformat from strutils import toLowerAscii, `%` @@ -418,6 +446,9 @@ else: proc eraseLine*(f: File) = ## Erases the entire current line. + runnableExamples("-r:off"): + write(stdout, "never mind") + stdout.eraseLine() # nothing will be printed on the screen when defined(windows): let h = conHandle(f) var scrbuf: CONSOLE_SCREEN_BUFFER_INFO @@ -480,7 +511,7 @@ proc resetAttributes*(f: File) = gBG = 0 type - Style* = enum ## different styles for text output + Style* = enum ## Different styles for text output. styleBright = 1, ## bright text styleDim, ## dim text styleItalic, ## italic (or reverse on terminals not supporting) @@ -534,7 +565,7 @@ proc writeStyled*(txt: string, style: set[Style] = {styleBright}) = stdout.write(ansiStyleCode(gBG)) type - ForegroundColor* = enum ## terminal's foreground colors + ForegroundColor* = enum ## Terminal's foreground colors. fgBlack = 30, ## black fgRed, ## red fgGreen, ## green @@ -546,7 +577,7 @@ type fg8Bit, ## 256-color (not supported, see `enableTrueColors` instead.) fgDefault ## default terminal foreground color - BackgroundColor* = enum ## terminal's background colors + BackgroundColor* = enum ## Terminal's background colors. bgBlack = 40, ## black bgRed, ## red bgGreen, ## green @@ -701,14 +732,10 @@ macro styledWrite*(f: File, m: varargs[typed]): untyped = ## When some argument is `Style`, `set[Style]`, `ForegroundColor`, ## `BackgroundColor` or `TerminalCmd` then it is not sent directly to ## `f`, but instead corresponding terminal style proc is called. - ## - ## Example: - ## - ## .. code-block:: nim - ## - ## stdout.styledWrite(fgRed, "red text ") - ## stdout.styledWrite(fgGreen, "green text") - ## + runnableExamples("-r:off"): + stdout.styledWrite(fgRed, "red text ") + stdout.styledWrite(fgGreen, "green text") + var reset = false result = newNimNode(nnkStmtList) @@ -731,14 +758,10 @@ macro styledWrite*(f: File, m: varargs[typed]): untyped = template styledWriteLine*(f: File, args: varargs[untyped]) = ## Calls `styledWrite` and appends a newline at the end. - ## - ## Example: - ## - ## .. code-block:: nim - ## - ## proc error(msg: string) = - ## styledWriteLine(stderr, fgRed, "Error: ", resetStyle, msg) - ## + runnableExamples: + proc error(msg: string) = + styledWriteLine(stderr, fgRed, "Error: ", resetStyle, msg) + styledWrite(f, args) write(f, "\n") @@ -747,7 +770,7 @@ template styledEcho*(args: varargs[untyped]) = stdout.styledWriteLine(args) proc getch*(): char = - ## Read a single character from the terminal, blocking until it is entered. + ## Reads a single character from the terminal, blocking until it is entered. ## The character is not printed to the terminal. when defined(windows): let fd = getStdHandle(STD_INPUT_HANDLE) @@ -852,7 +875,7 @@ when defined(windows): import os proc enableTrueColors*() = - ## Enable true color. + ## Enables true color. var term = getTerminal() when defined(windows): var @@ -885,7 +908,7 @@ proc enableTrueColors*() = term.trueColorIsEnabled = term.trueColorIsSupported proc disableTrueColors*() = - ## Disable true color. + ## Disables true color. var term = getTerminal() when defined(windows): if term.trueColorIsSupported: @@ -902,51 +925,3 @@ proc newTerminal(): owned(PTerminal) = new result when defined(windows): initTerminal(result) - -when not defined(testing) and isMainModule: - assert ansiStyleCode(styleBright) == "\e[1m" - assert ansiStyleCode(styleStrikethrough) == "\e[9m" - # exitprocs.addExitProc(resetAttributes) - write(stdout, "never mind") - stdout.eraseLine() - stdout.styledWriteLine({styleBright, styleBlink, styleUnderscore}, "styled text ") - stdout.styledWriteLine("italic text ", {styleItalic}) - stdout.setBackGroundColor(bgCyan, true) - stdout.setForeGroundColor(fgBlue) - stdout.write("blue text in cyan background") - stdout.resetAttributes() - echo "" - stdout.writeLine("ordinary text") - echo "more ordinary text" - styledEcho styleBright, fgGreen, "[PASS]", resetStyle, fgGreen, " Yay!" - echo "ordinary text again" - styledEcho styleBright, fgRed, "[FAIL]", resetStyle, fgRed, " Nay :(" - echo "ordinary text again" - setForeGroundColor(fgGreen) - echo "green text" - echo "more green text" - setForeGroundColor(fgBlue) - echo "blue text" - resetAttributes() - echo "ordinary text" - - stdout.styledWriteLine(fgRed, "red text ") - stdout.styledWriteLine(fgWhite, bgRed, "white text in red background") - stdout.styledWriteLine(" ordinary text ") - stdout.styledWriteLine(fgGreen, "green text") - - writeStyled("underscored text", {styleUnderscore}) - stdout.styledWrite(fgRed, " red text ") - writeStyled("bright text ", {styleBright}) - echo "ordinary text" - - stdout.styledWrite(fgRed, "red text ") - stdout.styledWrite(fgWhite, bgRed, "white text in red background") - stdout.styledWrite(" ordinary text ") - stdout.styledWrite(fgGreen, "green text") - echo "" - echo "ordinary text" - stdout.styledWriteLine(fgRed, "red text ", styleBright, "bold red", fgDefault, " bold text") - stdout.styledWriteLine(bgYellow, "text in yellow bg", styleBright, - " bold text in yellow bg", bgDefault, " bold text") - echo "ordinary text" From eb0b323f452f5f06d74a4f747c50ae5115e44eae Mon Sep 17 00:00:00 2001 From: Antonis Geralis <43617260+planetis-m@users.noreply.github.com> Date: Sun, 20 Jun 2021 15:51:13 +0300 Subject: [PATCH 2246/3768] Revert "disable pkg manu (#18292)" (#18307) This reverts commit fc76565574b86566e4a642b9aac541e025cf5de3. --- testament/important_packages.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index efee361da..3d3c340eb 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -82,7 +82,7 @@ pkg "karax", "nim c -r tests/tester.nim" pkg "kdtree", "nimble test -d:nimLegacyRandomInitRand", "https://github.com/jblindsay/kdtree" pkg "loopfusion" pkg "macroutils" -pkg "manu", allowFailure=true # pending https://github.com/planetis-m/manu/issues/5 +pkg "manu" pkg "markdown" pkg "memo" pkg "msgpack4nim", "nim c -r tests/test_spec.nim" From 92cb765714325703d1a8e1b0409bfcd1b17af103 Mon Sep 17 00:00:00 2001 From: Heiko Nickerl Date: Sun, 20 Jun 2021 17:56:33 +0200 Subject: [PATCH 2247/3768] Raise IndexDefect when deleting element at out of bounds index (#17821) Co-authored-by: Timothee Cour Co-authored-by: Heiko Nickerl Co-authored-by: Heiko Nickerl --- changelog.md | 3 ++ lib/system.nim | 58 ++++++++++++++++++++--------------- tests/gc/gcleak3.nim | 8 ++--- tests/stdlib/tsystem.nim | 32 ++++++++++++++++++- tests/system/tsystem_misc.nim | 5 --- 5 files changed, 70 insertions(+), 36 deletions(-) diff --git a/changelog.md b/changelog.md index 80e38f420..5af195e59 100644 --- a/changelog.md +++ b/changelog.md @@ -28,6 +28,9 @@ - `math.round` now is rounded "away from zero" in JS backend which is consistent with other backends. See #9125. Use `-d:nimLegacyJsRound` for previous behavior. +- Instead of deleting the element at the last index, + `system.delete()` now raises `IndexDefect` when given index is out of bounds. + - Changed the behavior of `uri.decodeQuery` when there are unencoded `=` characters in the decoded values. Prior versions would raise an error. This is no longer the case to comply with the HTML spec and other languages diff --git a/lib/system.nim b/lib/system.nim index cae0ec3e2..a4608997a 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1325,30 +1325,6 @@ proc del*[T](x: var seq[T], i: Natural) {.noSideEffect.} = movingCopy(x[i], x[xl]) setLen(x, xl) -proc delete*[T](x: var seq[T], i: Natural) {.noSideEffect.} = - ## Deletes the item at index `i` by moving all `x[i+1..]` items by one position. - ## - ## This is an `O(n)` operation. - ## - ## See also: - ## * `del <#del,seq[T],Natural>`_ for O(1) operation - ## - ## .. code-block:: Nim - ## var i = @[1, 2, 3, 4, 5] - ## i.delete(2) # => @[1, 2, 4, 5] - template defaultImpl = - let xl = x.len - for j in i.int..xl-2: movingCopy(x[j], x[j+1]) - setLen(x, xl-1) - - when nimvm: - defaultImpl() - else: - when defined(js): - {.emit: "`x`.splice(`i`, 1);".} - else: - defaultImpl() - proc insert*[T](x: var seq[T], item: sink T, i = 0.Natural) {.noSideEffect.} = ## Inserts `item` into `x` at position `i`. ## @@ -2154,6 +2130,40 @@ const import system/dollars export dollars +proc delete*[T](x: var seq[T], i: Natural) {.noSideEffect.} = + ## Deletes the item at index `i` by moving all `x[i+1..^1]` items by one position. + ## + ## This is an `O(n)` operation. + ## + ## See also: + ## * `del <#del,seq[T],Natural>`_ for O(1) operation + ## + runnableExamples: + var s = @[1, 2, 3, 4, 5] + s.delete(2) + doAssert s == @[1, 2, 4, 5] + + doAssertRaises(IndexDefect): + s.delete(4) + + if i > high(x): + # xxx this should call `raiseIndexError2(i, high(x))` after some refactoring + raise (ref IndexDefect)(msg: "index out of bounds: '" & $i & "' < '" & $x.len & "' failed") + + template defaultImpl = + let xl = x.len + for j in i.int..xl-2: movingCopy(x[j], x[j+1]) + setLen(x, xl-1) + + when nimvm: + defaultImpl() + else: + when defined(js): + {.emit: "`x`.splice(`i`, 1);".} + else: + defaultImpl() + + const NimVersion*: string = $NimMajor & "." & $NimMinor & "." & $NimPatch ## is the version of Nim as a string. diff --git a/tests/gc/gcleak3.nim b/tests/gc/gcleak3.nim index 588e238e9..5e146d69f 100644 --- a/tests/gc/gcleak3.nim +++ b/tests/gc/gcleak3.nim @@ -17,14 +17,10 @@ for i in 0..1024: s.add(obj) proc limit*[t](a: var seq[t]) = - var loop = s.len() - 512 - for i in 0..loop: - #echo i - #GC_fullCollect() + while s.len > 0: if getOccupiedMem() > 3000_000: quit("still a leak!") - s.delete(i) + s.delete(0) s.limit() - echo "no leak: ", getOccupiedMem() diff --git a/tests/stdlib/tsystem.nim b/tests/stdlib/tsystem.nim index f8b172b44..b48034043 100644 --- a/tests/stdlib/tsystem.nim +++ b/tests/stdlib/tsystem.nim @@ -2,9 +2,10 @@ discard """ targets: "c cpp js" """ +import stdtest/testutils + # TODO: in future work move existing `system` tests here, where they belong -import stdtest/testutils template main = block: # closure @@ -42,5 +43,34 @@ template main = # doAssert rawEnv(inner3) != rawEnv(inner1) # because `a` vs `b` # this doesn't hold outer() + block: # system.delete + block: + var s = @[1] + s.delete(0) + doAssert s == @[] + + block: + var s = @["foo", "bar"] + s.delete(1) + doAssert s == @["foo"] + + block: + var s: seq[string] + doAssertRaises(IndexDefect): + s.delete(0) + + block: + doAssert not compiles(@["foo"].delete(-1)) + + block: # bug #6710 + var s = @["foo"] + s.delete(0) + doAssert s == @[] + + block: # bug #16544: deleting out of bounds index should raise + var s = @["foo"] + doAssertRaises(IndexDefect): + s.delete(1) + static: main() main() diff --git a/tests/system/tsystem_misc.nim b/tests/system/tsystem_misc.nim index cb879d3b3..13b8c18a9 100644 --- a/tests/system/tsystem_misc.nim +++ b/tests/system/tsystem_misc.nim @@ -59,11 +59,6 @@ doAssert high(float) > low(float) doAssert high(float32) > low(float32) doAssert high(float64) > low(float64) -# bug #6710 -var s = @[1] -s.delete(0) - - proc foo(a: openArray[int]) = for x in a: echo x From 7d5e6b0169fb83c7682bfad5d675c21db785c324 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 20 Jun 2021 10:52:22 -0700 Subject: [PATCH 2248/3768] support `--hint:all:off --hint:x` (ditto with `--warnings` + friends) (#17852) * select all hints via `--hint:all:on|off`, etc * simplify code with setutils * address comment --- changelog.md | 4 +++ compiler/commands.nim | 59 +++++++++++++++++++++--------------------- compiler/condsyms.nim | 2 +- doc/advopt.txt | 15 ++++++----- tests/misc/trunner.nim | 9 +++---- 5 files changed, 45 insertions(+), 44 deletions(-) diff --git a/changelog.md b/changelog.md index 5af195e59..570aff542 100644 --- a/changelog.md +++ b/changelog.md @@ -429,6 +429,10 @@ - `--hint:CC` now goes to stderr (like all other hints) instead of stdout. +- `--hint:all:on|off` is now supported to select or deselect all hints; it + differs from `--hints:on|off` which acts as a (reversible) gate. + Likewise with `--warning:all:on|off`. + - json build instructions are now generated in `$nimcache/outFileBasename.json` instead of `$nimcache/projectName.json`. This allows avoiding recompiling a given project compiled with different options if the output file differs. diff --git a/compiler/commands.nim b/compiler/commands.nim index 8f8df71a7..4c1db8ead 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -25,10 +25,10 @@ bootSwitch(usedMarkAndSweep, defined(gcmarkandsweep), "--gc:markAndSweep") bootSwitch(usedGoGC, defined(gogc), "--gc:go") bootSwitch(usedNoGC, defined(nogc), "--gc:none") +import std/[setutils, os, strutils, parseutils, parseopt, sequtils, strtabs] import - os, msgs, options, nversion, condsyms, strutils, extccomp, platform, - wordrecg, parseutils, nimblecmd, parseopt, sequtils, lineinfos, - pathutils, strtabs, pathnorm + msgs, options, nversion, condsyms, extccomp, platform, + wordrecg, nimblecmd, lineinfos, pathutils, pathnorm from ast import eqTypeFlags, tfGcSafe, tfNoSideEffect @@ -182,7 +182,7 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass, info: TLineInfo; orig: string; conf: ConfigRef) = var id = "" # arg = key or [key] or key:val or [key]:val; with val=on|off var i = 0 - var n = hintMin + var notes: set[TMsgKind] var isBracket = false if i < arg.len and arg[i] == '[': isBracket = true @@ -197,37 +197,36 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass, if i == arg.len: discard elif i < arg.len and (arg[i] in {':', '='}): inc(i) else: invalidCmdLineOption(conf, pass, orig, info) - # unfortunately, hintUser and warningUser clash - if state in {wHint, wHintAsError}: - let x = findStr(hintMin, hintMax, id, errUnknown) - if x != errUnknown: n = TNoteKind(x) - else: localError(conf, info, "unknown hint: " & id) - else: - let x = findStr(warnMin, warnMax, id, errUnknown) - if x != errUnknown: n = TNoteKind(x) - else: localError(conf, info, "unknown warning: " & id) + let isSomeHint = state in {wHint, wHintAsError} + template findNote(noteMin, noteMax, name) = + # unfortunately, hintUser and warningUser clash, otherwise implementation would simplify a bit + let x = findStr(noteMin, noteMax, id, errUnknown) + if x != errUnknown: notes = {TNoteKind(x)} + else: localError(conf, info, "unknown $#: $#" % [name, id]) + case id.normalize + of "all": # other note groups would be easy to support via additional cases + notes = if isSomeHint: {hintMin..hintMax} else: {warnMin..warnMax} + elif isSomeHint: findNote(hintMin, hintMax, "hint") + else: findNote(warnMin, warnMax, "warning") var val = substr(arg, i).normalize if val == "": val = "on" if val notin ["on", "off"]: + # xxx in future work we should also allow users to have control over `foreignPackageNotes` + # so that they can enable `hints|warnings|warningAsErrors` for all the code they depend on. localError(conf, info, errOnOrOffExpectedButXFound % arg) - elif n notin conf.cmdlineNotes or pass == passCmd1: - if pass == passCmd1: incl(conf.cmdlineNotes, n) - incl(conf.modifiedyNotes, n) - case val - of "on": - if state in {wWarningAsError, wHintAsError}: - incl(conf.warningAsErrors, n) # xxx rename warningAsErrors to noteAsErrors - else: - incl(conf.notes, n) - incl(conf.mainPackageNotes, n) - of "off": - if state in {wWarningAsError, wHintAsError}: - excl(conf.warningAsErrors, n) - else: - excl(conf.notes, n) - excl(conf.mainPackageNotes, n) - excl(conf.foreignPackageNotes, n) + else: + let isOn = val == "on" + for n in notes: + if n notin conf.cmdlineNotes or pass == passCmd1: + if pass == passCmd1: incl(conf.cmdlineNotes, n) + incl(conf.modifiedyNotes, n) + if state in {wWarningAsError, wHintAsError}: + conf.warningAsErrors[n] = isOn # xxx rename warningAsErrors to noteAsErrors + else: + conf.notes[n] = isOn + conf.mainPackageNotes[n] = isOn + if not isOn: excl(conf.foreignPackageNotes, n) proc processCompile(conf: ConfigRef; filename: string) = var found = findFile(conf, filename) diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index ff0c603cb..b3f8465f0 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -134,5 +134,5 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasUnifiedTuple") defineSymbol("nimHasIterable") defineSymbol("nimHasTypeofVoid") - defineSymbol("nimHasDragonBox") + defineSymbol("nimHasHintAll") diff --git a/doc/advopt.txt b/doc/advopt.txt index e5b4c9568..b56649a86 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -48,14 +48,15 @@ Advanced options: --spellSuggest|:num show at most `num >= 0` spelling suggestions on typos. if `num` is not specified (or `auto`), return an implementation defined set of suggestions. - -w:on|off|list, --warnings:on|off|list - turn all warnings on|off or list all available - --warning:X:on|off turn specific warning X on|off. - `warning:X` means `warning:X:on`, as with similar flags. - --hints:on|off|list turn all hints on|off or list all available - --hint:X:on|off turn specific hint X on|off. - --warningAsError:X:on|off turn specific warning X into an error on|off + --hints:on|off|list. `on|off` enables or disables hints. + `list` reports which hints are selected. + --hint:X:on|off turn specific hint X on|off. `hint:X` means `hint:X:on`, + as with similar flags. `all` can be used for "all hints". --hintAsError:X:on|off turn specific hint X into an error on|off + -w:on|off|list, --warnings:on|off|list + same as `--hints` but for warnings. + --warning:X:on|off ditto + --warningAsError:X:on|off ditto --styleCheck:off|hint|error produce hints or errors for Nim identifiers that do not adhere to Nim's official style guide diff --git a/tests/misc/trunner.nim b/tests/misc/trunner.nim index f2ea1d040..77ae10f3d 100644 --- a/tests/misc/trunner.nim +++ b/tests/misc/trunner.nim @@ -23,9 +23,6 @@ proc isDots(a: string): bool = a.startsWith(".") and a.strip(chars = {'.'}) == "" const - defaultHintsOff = "--hint:successx:off --hint:exec:off --hint:link:off --hint:cc:off --hint:conf:off --hint:processing:off --hint:QuitCalled:off" - # useful when you want to turn only some hints on, and some common ones off. - # pending https://github.com/timotheecour/Nim/issues/453, simplify to: `--hints:off` nim = getCurrentCompilerExe() mode = querySetting(backend) nimcache = buildDir / "nimcacheTrunner" @@ -236,7 +233,7 @@ sub/mmain.idx""", context check execCmdEx(cmd) == ("witness\n", 0) block: # config.nims, nim.cfg, hintConf, bug #16557 - let cmd = fmt"{nim} r {defaultHintsOff} --hint:conf tests/newconfig/bar/mfoo.nim" + let cmd = fmt"{nim} r --hint:all:off --hint:conf tests/newconfig/bar/mfoo.nim" let (outp, exitCode) = execCmdEx(cmd, options = {poStdErrToStdOut}) doAssert exitCode == 0 let dir = getCurrentDir() @@ -268,7 +265,7 @@ tests/newconfig/bar/mfoo.nims""".splitLines check fmt"""{nim} r -b:js {opt} --eval:"echo defined(js)"""".execCmdEx == ("true\n", 0) block: # `hintProcessing` dots should not interfere with `static: echo` + friends - let cmd = fmt"""{nim} r {defaultHintsOff} --hint:processing -f --eval:"static: echo 1+1"""" + let cmd = fmt"""{nim} r --hint:all:off --hint:processing -f --eval:"static: echo 1+1"""" let (outp, exitCode) = execCmdEx(cmd, options = {poStdErrToStdOut}) template check3(cond) = doAssert cond, $(outp,) doAssert exitCode == 0 @@ -282,7 +279,7 @@ tests/newconfig/bar/mfoo.nims""".splitLines check3 "2" in outp block: # nim secret - let opt = fmt"{defaultHintsOff} --hint:processing" + let opt = "--hint:all:off --hint:processing" template check3(cond) = doAssert cond, $(outp,) for extra in ["", "--stdout"]: let cmd = fmt"""{nim} secret {opt} {extra}""" From 1b3c0f142dd5abc3b3509feb194f54e6acf046c0 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Sun, 20 Jun 2021 21:00:42 +0300 Subject: [PATCH 2249/3768] validate rst field for :number-lines:, :status: (#18304) --- compiler/docgen.nim | 1 + compiler/lineinfos.nim | 2 ++ lib/packages/docutils/rst.nim | 1 + lib/packages/docutils/rstgen.nim | 30 ++++++++++++++++++++++-------- tests/stdlib/trstgen.nim | 12 ++++++++++++ 5 files changed, 38 insertions(+), 8 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 2c99adba3..08311eade 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -164,6 +164,7 @@ template declareClosures = of meNewSectionExpected: k = errNewSectionExpected of meGeneralParseError: k = errGeneralParseError of meInvalidDirective: k = errInvalidDirectiveX + of meInvalidRstField: k = errInvalidRstField of meFootnoteMismatch: k = errFootnoteMismatch of mwRedefinitionOfLabel: k = warnRedefinitionOfLabel of mwUnknownSubstitution: k = warnUnknownSubstitutionX diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index 052dae272..abe6bce7c 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -37,6 +37,7 @@ type errGeneralParseError, errNewSectionExpected, errInvalidDirectiveX, + errInvalidRstField, errFootnoteMismatch, errProveInit, # deadcode errGenerated, @@ -96,6 +97,7 @@ const errGeneralParseError: "general parse error", errNewSectionExpected: "new section expected $1", errInvalidDirectiveX: "invalid directive: '$1'", + errInvalidRstField: "invalid field: $1", errFootnoteMismatch: "number of footnotes and their references don't match: $1", errProveInit: "Cannot prove that '$1' is initialized.", # deadcode errGenerated: "$1", diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 031a86d98..64910aa19 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -225,6 +225,7 @@ type meNewSectionExpected = "new section expected $1", meGeneralParseError = "general parse error", meInvalidDirective = "invalid directive: '$1'", + meInvalidRstField = "invalid field: $1", meFootnoteMismatch = "mismatch in number of footnotes and their refs: $1", mwRedefinitionOfLabel = "redefinition of label '$1'", mwUnknownSubstitution = "unknown substitution '$1'", diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index e3f889283..829177263 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -907,6 +907,25 @@ proc renderSmiley(d: PDoc, n: PRstNode, result: var string) = "\\includegraphics{$1}", [d.config.getOrDefault"doc.smiley_format" % n.text]) +proc getField1Int(d: PDoc, n: PRstNode, fieldName: string): int = + # TODO: proper column/line info + template err(msg: string) = + d.msgHandler(d.filename, 1, 0, meInvalidRstField, msg) + let value = n.getFieldValue + var number: int + let nChars = parseInt(value, number) + if nChars == 0: + if value.len == 0: + err("field $1 requires an argument" % [fieldName]) + else: + err("field $1 requires an integer, but '$2' was given" % + [fieldName, value]) + elif nChars < value.len: + err("extra arguments were given to $1: '$2'" % + [fieldName, value[nChars..^1]]) + else: + result = number + proc parseCodeBlockField(d: PDoc, n: PRstNode, params: var CodeBlockParams) = ## Parses useful fields which can appear before a code block. ## @@ -916,9 +935,7 @@ proc parseCodeBlockField(d: PDoc, n: PRstNode, params: var CodeBlockParams) = of "number-lines": params.numberLines = true # See if the field has a parameter specifying a different line than 1. - var number: int - if parseInt(n.getFieldValue, number) > 0: - params.startLine = number + params.startLine = getField1Int(d, n, "number-lines") of "file", "filename": # The ``file`` option is a Nim extension to the official spec, it acts # like it would for other directives like ``raw`` or ``cvs-table``. This @@ -936,9 +953,7 @@ proc parseCodeBlockField(d: PDoc, n: PRstNode, params: var CodeBlockParams) = # consider whether `$docCmd` should be appended here too params.testCmd = unescape(params.testCmd) of "status", "exitcode": - var status: int - if parseInt(n.getFieldValue, status) > 0: - params.status = status + params.status = getField1Int(d, n, n.getArgument) of "default-language": params.langStr = n.getFieldValue.strip params.lang = params.langStr.getSourceLanguage @@ -954,7 +969,6 @@ proc parseCodeBlockParams(d: PDoc, n: PRstNode): CodeBlockParams = if n.isNil: return assert n.kind in {rnCodeBlock, rnInlineCode} - assert(not n.sons[2].isNil) # Parse the field list for rendering parameters if there are any. if not n.sons[1].isNil: @@ -1028,8 +1042,8 @@ proc renderCode(d: PDoc, n: PRstNode, result: var string) = ## option to differentiate between a plain code block and Nim's code block ## extension. assert n.kind in {rnCodeBlock, rnInlineCode} - if n.sons[2] == nil: return var params = d.parseCodeBlockParams(n) + if n.sons[2] == nil: return var m = n.sons[2].sons[0] assert m.kind == rnLeaf diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim index 300229648..d5d902e1d 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -1136,6 +1136,18 @@ Test1 check "
    55\n
    " in output check "x" in output + test "Nim code-block indentation": + let input = dedent """ + .. code-block:: nim + :number-lines: 55 + let a = 1 + """ + var error = new string + let output = input.toHtml(error=error) + check(error[] == "input(1, 1) Error: invalid field: " & + "extra arguments were given to number-lines: ' let a = 1'") + check "" == output + test "RST admonitions": # check that all admonitions are implemented let input0 = dedent """ From 40ec8184ad51ccdaa6042132dc9b37b6f242c362 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 20 Jun 2021 13:49:14 -0700 Subject: [PATCH 2250/3768] followup #17852, disallow all:on for now (#18311) --- compiler/commands.nim | 2 ++ doc/advopt.txt | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/commands.nim b/compiler/commands.nim index 4c1db8ead..1c3de29ba 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -217,6 +217,8 @@ proc processSpecificNote*(arg: string, state: TSpecialWord, pass: TCmdLinePass, localError(conf, info, errOnOrOffExpectedButXFound % arg) else: let isOn = val == "on" + if isOn and id.normalize == "all": + localError(conf, info, "only 'all:off' is supported") for n in notes: if n notin conf.cmdlineNotes or pass == passCmd1: if pass == passCmd1: incl(conf.cmdlineNotes, n) diff --git a/doc/advopt.txt b/doc/advopt.txt index b56649a86..063d018d1 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -51,7 +51,8 @@ Advanced options: --hints:on|off|list. `on|off` enables or disables hints. `list` reports which hints are selected. --hint:X:on|off turn specific hint X on|off. `hint:X` means `hint:X:on`, - as with similar flags. `all` can be used for "all hints". + as with similar flags. `all` is the set of all hints + (only `all:off` is supported). --hintAsError:X:on|off turn specific hint X into an error on|off -w:on|off|list, --warnings:on|off|list same as `--hints` but for warnings. From abd21ef213b7368253050a18e5c4907d22992c8c Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 21 Jun 2021 00:45:26 -0700 Subject: [PATCH 2251/3768] close #17403; improve docs for tuple (#18312) --- doc/manual.rst | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/doc/manual.rst b/doc/manual.rst index e8eec9a57..f80b03369 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -1616,7 +1616,7 @@ heterogeneous storage types with few abstractions. The `()` syntax can be used to construct tuples. The order of the fields in the constructor must match the order of the tuple's definition. Different tuple-types are *equivalent* if they specify the same fields of the same type in the same -order. The *names* of the fields also have to be identical. +order. The *names* of the fields also have to be the same. The assignment operator for tuples copies each component. The default assignment operator for objects copies each component. Overloading @@ -1627,15 +1627,16 @@ of the assignment operator is described `here type Person = tuple[name: string, age: int] # type representing a person: - # a person consists of a name - # and an age - var - person: Person + # it consists of a name and an age. + var person: Person person = (name: "Peter", age: 30) - echo person.name + assert person.name == "Peter" # the same, but less readable: person = ("Peter", 30) - echo person[0] + assert person[0] == "Peter" + assert Person is (string, int) + assert (string, int) is Person + assert Person isnot tuple[other: string, age: int] # `other` is a different identifier A tuple with one unnamed field can be constructed with the parentheses and a trailing comma: From ad70a65e0e3eda39a3ca074af9feadb68f10598f Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 21 Jun 2021 00:46:31 -0700 Subject: [PATCH 2252/3768] fix #18310 system.== in vm for NimNode (#18313) * fix #18310 == in vm * fixup * fixup --- compiler/vm.nim | 11 ++++++----- tests/vm/tvmmisc.nim | 11 +++++++++++ 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/compiler/vm.nim b/compiler/vm.nim index b540ac071..837a1c366 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -10,14 +10,15 @@ ## This file implements the new evaluation engine for Nim code. ## An instruction is 1-3 int32s in memory, it is a register based VM. -import ast except getstr import - strutils, msgs, vmdef, vmgen, nimsets, types, passes, - parser, vmdeps, idents, trees, renderer, options, transf, parseutils, - vmmarshal, gorgeimpl, lineinfos, tables, btrees, macrocacheimpl, + std/[strutils, tables, parseutils], + msgs, vmdef, vmgen, nimsets, types, passes, + parser, vmdeps, idents, trees, renderer, options, transf, + vmmarshal, gorgeimpl, lineinfos, btrees, macrocacheimpl, modulegraphs, sighashes, int128, vmprofiler +import ast except getstr from semfold import leValueConv, ordinalValToString from evaltempl import evalTemplate from magicsys import getSysType @@ -1026,7 +1027,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let nc = regs[rc].node if nb.kind != nc.kind: discard elif (nb == nc) or (nb.kind == nkNilLit): ret = true # intentional - elif (nb.kind in {nkSym, nkTupleConstr, nkClosure} and nb.typ.kind == tyProc) and sameConstant(nb, nc): + elif nb.kind in {nkSym, nkTupleConstr, nkClosure} and nb.typ != nil and nb.typ.kind == tyProc and sameConstant(nb, nc): ret = true # this also takes care of procvar's, represented as nkTupleConstr, e.g. (nil, nil) elif nb.kind == nkIntLit and nc.kind == nkIntLit and nb.intVal == nc.intVal: # TODO: nkPtrLit diff --git a/tests/vm/tvmmisc.nim b/tests/vm/tvmmisc.nim index f0d2c2dbf..a4f6391cc 100644 --- a/tests/vm/tvmmisc.nim +++ b/tests/vm/tvmmisc.nim @@ -256,6 +256,17 @@ block: doAssert f == @[] +block: # bug #18310 + macro t() : untyped = + let + x = nnkTupleConstr.newTree(newLit(1)) + y = nnkTupleConstr.newTree(newLit(2)) + doAssert not (x == y) # not using != intentionally + doAssert not(cast[int](x) == cast[int](y)) + doAssert not(system.`==`(x, y)) + doAssert system.`==`(x, x) + t() + block: # bug #10815 type Opcode = enum From 2deb7011f5388559a70617c398360639102724c4 Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 21 Jun 2021 18:50:47 +0800 Subject: [PATCH 2253/3768] fixes #17768 [backport:1.4] (#18317) * fixes #17768 [backport:1.4] * tiny --- lib/pure/unicode.nim | 10 ++++------ tests/stdlib/tunicode.nim | 9 ++++++++- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim index 903f01fb4..dda631633 100644 --- a/lib/pure/unicode.nim +++ b/lib/pure/unicode.nim @@ -333,7 +333,7 @@ proc runeReverseOffset*(s: string, rev: Positive): (int, int) = ## from the end (starting with 1) and the total ## number of runes in the string. ## - ## Returns a negative value for offset if there are to few runes in + ## Returns a negative value for offset if there are too few runes in ## the string to satisfy the request. ## ## **Beware:** This can lead to unoptimized code and slow execution! @@ -346,16 +346,14 @@ proc runeReverseOffset*(s: string, rev: Positive): (int, int) = a = rev.int o = 0 x = 0 + let times = 2*rev.int-s.runeLen # transformed from rev.int - a < s.runeLen - rev.int while o < s.len: let r = runeLenAt(s, o) o += r - if a < 0: + if a > times: x += r dec a - - if a > 0: - return (-a, rev.int-a) - return (x, -a+rev.int) + result = if a > 0: (-a, rev.int-a) else: (x, -a+rev.int) proc runeAtPos*(s: string, pos: int): Rune = ## Returns the rune at position ``pos``. diff --git a/tests/stdlib/tunicode.nim b/tests/stdlib/tunicode.nim index a346106f9..33405cf38 100644 --- a/tests/stdlib/tunicode.nim +++ b/tests/stdlib/tunicode.nim @@ -1,4 +1,4 @@ -import unicode +import std/unicode proc asRune(s: static[string]): Rune = @@ -213,3 +213,10 @@ block differentSizes: doAssert swapCase("ⱥbCd") == "ȺBcD" doAssert swapCase("XyꟆaB") == "xYᶎAb" doAssert swapCase("aᵹcᲈd") == "AꝽCꙊD" + +block: # bug #17768 + let s1 = "abcdef" + let s2 = "abcdéf" + + doAssert s1.runeSubstr(0, -1) == "abcde" + doAssert s2.runeSubstr(0, -1) == "abcdé" From d398c558a44ad346c2cfadf266a8cf808585de6d Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 21 Jun 2021 03:51:00 -0700 Subject: [PATCH 2254/3768] close #13196 now that we have dragonbox (#18316) --- tests/stdlib/tjson.nim | 11 +++++++++++ tests/stdlib/tjsonutils.nim | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/tests/stdlib/tjson.nim b/tests/stdlib/tjson.nim index e757e6c7e..289ef9d05 100644 --- a/tests/stdlib/tjson.nim +++ b/tests/stdlib/tjson.nim @@ -11,6 +11,8 @@ import std/[json,parsejson,strutils] from std/math import isNaN when not defined(js): import std/streams +import stdtest/testutils +from std/fenv import epsilon proc testRoundtrip[T](t: T, expected: string) = # checks that `T => json => T2 => json2` is such that json2 = json @@ -325,6 +327,15 @@ block: # bug #18007 let a = parseJson($(%NaN)).to(float) doAssert a.isNaN + whenRuntimeJs: discard # refs bug #18009 + do: + testRoundtripVal(0.0): "0.0" + testRoundtripVal(-0.0): "-0.0" + +block: # bug #15397, bug #13196 + testRoundtripVal(1.0 + epsilon(float64)): "1.0000000000000002" + testRoundtripVal(0.12345678901234567890123456789): "0.12345678901234568" + block: let a = "18446744073709551615" let b = a.parseJson diff --git a/tests/stdlib/tjsonutils.nim b/tests/stdlib/tjsonutils.nim index 88c05facf..31ec4316f 100644 --- a/tests/stdlib/tjsonutils.nim +++ b/tests/stdlib/tjsonutils.nim @@ -5,6 +5,7 @@ discard """ import std/jsonutils import std/json from std/math import isNaN, signbit +from std/fenv import epsilon from stdtest/testutils import whenRuntimeJs proc testRoundtrip[T](t: T, expected: string) = @@ -160,6 +161,16 @@ template fn() = doAssert b[2].signbit doAssert not b[3].signbit + block: # bug #15397, bug #13196 + let a = 0.1 + let x = 0.12345678901234567890123456789 + let b = (a + 0.2, 0.3, x) + testRoundtripVal(b): "[0.30000000000000004,0.3,0.12345678901234568]" + + testRoundtripVal(0.12345678901234567890123456789): "0.12345678901234568" + testRoundtripVal(epsilon(float64)): "2.220446049250313e-16" + testRoundtripVal(1.0 + epsilon(float64)): "1.0000000000000002" + block: # case object type Foo = object x0: float From 16461a88101a398fd7349eb56392b27cdb285c17 Mon Sep 17 00:00:00 2001 From: xioren <40043405+xioren@users.noreply.github.com> Date: Mon, 21 Jun 2021 18:53:35 -0700 Subject: [PATCH 2255/3768] add multi type exception catching to manual (#18258) (#18323) --- doc/manual.rst | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/doc/manual.rst b/doc/manual.rst index f80b03369..a99c4b5af 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -4462,10 +4462,8 @@ Example: echo "sum: " & $(parseInt(a) + parseInt(b)) except OverflowDefect: echo "overflow!" - except ValueError: - echo "could not convert string to integer" - except IOError: - echo "IO error!" + except ValueError, IOError: + echo "catch multiple exceptions!" except: echo "Unknown exception!" finally: From 9d3a813fb96950631c69858e8e26b6503abd02fe Mon Sep 17 00:00:00 2001 From: xioren <40043405+xioren@users.noreply.github.com> Date: Mon, 21 Jun 2021 18:56:16 -0700 Subject: [PATCH 2256/3768] convert code-blocks to runnableExamples in io (#18315) Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> --- lib/system/io.nim | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/lib/system/io.nim b/lib/system/io.nim index 594c78209..62a9d1e18 100644 --- a/lib/system/io.nim +++ b/lib/system/io.nim @@ -912,14 +912,14 @@ iterator lines*(filename: string): string {.tags: [ReadIOEffect].} = ## If the file does not exist `IOError` is raised. The trailing newline ## character(s) are removed from the iterated lines. Example: ## - ## .. code-block:: nim - ## import std/strutils - ## - ## proc transformLetters(filename: string) = - ## var buffer = "" - ## for line in filename.lines: - ## buffer.add(line.replace("a", "0") & '\n') - ## writeFile(filename, buffer) + runnableExamples: + import std/strutils + + proc transformLetters(filename: string) = + var buffer = "" + for line in filename.lines: + buffer.add(line.replace("a", "0") & '\n') + writeFile(filename, buffer) var f = open(filename, bufSize=8000) try: var res = newStringOfCap(80) @@ -931,14 +931,13 @@ iterator lines*(f: File): string {.tags: [ReadIOEffect].} = ## Iterate over any line in the file `f`. ## ## The trailing newline character(s) are removed from the iterated lines. - ## Example: ## - ## .. code-block:: nim - ## proc countZeros(filename: File): tuple[lines, zeros: int] = - ## for line in filename.lines: - ## for letter in line: - ## if letter == '0': - ## result.zeros += 1 - ## result.lines += 1 + runnableExamples: + proc countZeros(filename: File): tuple[lines, zeros: int] = + for line in filename.lines: + for letter in line: + if letter == '0': + result.zeros += 1 + result.lines += 1 var res = newStringOfCap(80) while f.readLine(res): yield res From d8488e41e85c84a6b5fe687940b7fee10314d1f1 Mon Sep 17 00:00:00 2001 From: mantielero Date: Tue, 22 Jun 2021 06:19:11 +0200 Subject: [PATCH 2257/3768] readAsText supports both Blob and File (fixes #18187) (#18189) --- lib/js/dom.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/js/dom.nim b/lib/js/dom.nim index 2b2332020..ca325fdd2 100644 --- a/lib/js/dom.nim +++ b/lib/js/dom.nim @@ -1767,5 +1767,5 @@ since (1, 3): ## https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsBinaryString proc readAsDataURL*(f: FileReader, b: Blob) {.importcpp: "#.readAsDataURL(#)".} ## https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsDataURL - proc readAsText*(f: FileReader, b: Blob, encoding = cstring"UTF-8") {.importcpp: "#.readAsText(#, #)".} - ## https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsText + proc readAsText*(f: FileReader, b: Blob|File, encoding = cstring"UTF-8") {.importcpp: "#.readAsText(#, #)".} + ## https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsText \ No newline at end of file From 037715285c088af46c94939f36abef63c95406e8 Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 22 Jun 2021 18:44:56 +0800 Subject: [PATCH 2258/3768] fix #18327 (#18328) --- compiler/semtypes.nim | 2 +- tests/errmsgs/t18327.nim | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 tests/errmsgs/t18327.nim diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 38307a9ac..484997e3a 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1295,7 +1295,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, let param = strTableGet(c.signatures, arg.name) if param != nil: typ = param.typ else: - localError(c.config, a.info, "parameter '$1' requires a type" % param.name.s) + localError(c.config, a.info, "parameter '$1' requires a type" % arg.name.s) typ = errorType(c) let lifted = liftParamType(c, kind, genericParams, typ, arg.name.s, arg.info) diff --git a/tests/errmsgs/t18327.nim b/tests/errmsgs/t18327.nim new file mode 100644 index 000000000..686a1bd0c --- /dev/null +++ b/tests/errmsgs/t18327.nim @@ -0,0 +1,5 @@ +discard """ + errormsg: "parameter 'n' requires a type" +""" + +proc fn3(n) = discard \ No newline at end of file From 9a81e91fa5d01cd6d60a408d0888dce4a9f49898 Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 22 Jun 2021 23:02:32 +0800 Subject: [PATCH 2259/3768] merge similar procs regarding digits (#18318) --- lib/std/private/digitsutils.nim | 88 +++++++++++++++++++++++++++++++++ lib/system/dollars.nim | 23 ++++----- lib/system/dragonbox.nim | 32 ++---------- lib/system/schubfach.nim | 30 ++--------- lib/system/strmantle.nim | 65 +----------------------- 5 files changed, 105 insertions(+), 133 deletions(-) create mode 100644 lib/std/private/digitsutils.nim diff --git a/lib/std/private/digitsutils.nim b/lib/std/private/digitsutils.nim new file mode 100644 index 000000000..cc985c7bd --- /dev/null +++ b/lib/std/private/digitsutils.nim @@ -0,0 +1,88 @@ +const + trailingZeros100: array[100, int8] = [2'i8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0] + + digits100: array[200, char] = ['0', '0', '0', '1', '0', '2', '0', '3', '0', '4', '0', '5', + '0', '6', '0', '7', '0', '8', '0', '9', '1', '0', '1', '1', '1', '2', '1', '3', '1', '4', + '1', '5', '1', '6', '1', '7', '1', '8', '1', '9', '2', '0', '2', '1', '2', '2', '2', '3', + '2', '4', '2', '5', '2', '6', '2', '7', '2', '8', '2', '9', '3', '0', '3', '1', '3', '2', + '3', '3', '3', '4', '3', '5', '3', '6', '3', '7', '3', '8', '3', '9', '4', '0', '4', '1', + '4', '2', '4', '3', '4', '4', '4', '5', '4', '6', '4', '7', '4', '8', '4', '9', '5', '0', + '5', '1', '5', '2', '5', '3', '5', '4', '5', '5', '5', '6', '5', '7', '5', '8', '5', '9', + '6', '0', '6', '1', '6', '2', '6', '3', '6', '4', '6', '5', '6', '6', '6', '7', '6', '8', + '6', '9', '7', '0', '7', '1', '7', '2', '7', '3', '7', '4', '7', '5', '7', '6', '7', '7', + '7', '8', '7', '9', '8', '0', '8', '1', '8', '2', '8', '3', '8', '4', '8', '5', '8', '6', + '8', '7', '8', '8', '8', '9', '9', '0', '9', '1', '9', '2', '9', '3', '9', '4', '9', '5', + '9', '6', '9', '7', '9', '8', '9', '9'] + +# Inspired by https://engineering.fb.com/2013/03/15/developer-tools/three-optimization-tips-for-c +# Generates: +# .. code-block:: nim +# var res = "" +# for i in 0 .. 99: +# if i < 10: +# res.add "0" & $i +# else: +# res.add $i +# doAssert res == digits100 + +proc utoa2Digits*(buf: var openArray[char]; pos: int; digits: uint32) {.inline.} = + assert(digits <= 99) + buf[pos] = digits100[2 * digits] + buf[pos+1] = digits100[2 * digits + 1] + #copyMem(buf, unsafeAddr(digits100[2 * digits]), 2 * sizeof((char))) + +proc trailingZeros2Digits*(digits: uint32): int32 {.inline.} = + assert(digits <= 99) + return trailingZeros100[digits] + +func digits10*(num: uint64): int {.noinline.} = + if num < 10'u64: + result = 1 + elif num < 100'u64: + result = 2 + elif num < 1_000'u64: + result = 3 + elif num < 10_000'u64: + result = 4 + elif num < 100_000'u64: + result = 5 + elif num < 1_000_000'u64: + result = 6 + elif num < 10_000_000'u64: + result = 7 + elif num < 100_000_000'u64: + result = 8 + elif num < 1_000_000_000'u64: + result = 9 + elif num < 10_000_000_000'u64: + result = 10 + elif num < 100_000_000_000'u64: + result = 11 + elif num < 1_000_000_000_000'u64: + result = 12 + else: + result = 12 + digits10(num div 1_000_000_000_000'u64) + +template numToString*(result: var string, origin: uint64, length: int) = + var num = origin + var next = length - 1 + const nbatch = 100 + + while num >= nbatch: + let originNum = num + num = num div nbatch + let index = (originNum - num * nbatch) shl 1 + result[next] = digits100[index + 1] + result[next - 1] = digits100[index] + dec(next, 2) + + # process last 1-2 digits + if num < 10: + result[next] = chr(ord('0') + num) + else: + let index = num * 2 + result[next] = digits100[index + 1] + result[next - 1] = digits100[index] diff --git a/lib/system/dollars.nim b/lib/system/dollars.nim index 5a7a77e56..75390b0e1 100644 --- a/lib/system/dollars.nim +++ b/lib/system/dollars.nim @@ -1,3 +1,6 @@ +import std/private/digitsutils + + proc `$`*(x: int): string {.magic: "IntToStr", noSideEffect.} ## The stringify operator for an integer argument. Returns `x` ## converted to a decimal string. `$` is Nim's general way of @@ -6,22 +9,14 @@ proc `$`*(x: int): string {.magic: "IntToStr", noSideEffect.} template dollarImpl(x: uint | uint64, result: var string) = type destTyp = typeof(x) if x == 0: - result = "0" - else: - result = newString(60) - var i = 0 - var n = x - while n != 0: - let nn = n div destTyp(10) - result[i] = char(n - destTyp(10) * nn + ord('0')) - inc i - n = nn - result.setLen i + return "0" + elif x == 1: + return "1" - let half = i div 2 - # Reverse - for t in 0 .. half-1: swap(result[t], result[i-t-1]) + let length = digits10(x) + setLen(result, length) + numToString(result, x, length) when defined(js): import std/private/since diff --git a/lib/system/dragonbox.nim b/lib/system/dragonbox.nim index eec8749da..336c982c1 100644 --- a/lib/system/dragonbox.nim +++ b/lib/system/dragonbox.nim @@ -21,6 +21,10 @@ ## Note: ## This function may temporarily write up to DtoaMinBufferLength characters into the buffer. + +import std/private/digitsutils + + const dtoaMinBufferLength*: cint = 64 @@ -1038,34 +1042,6 @@ proc toDecimal64*(ieeeSignificand: uint64; ieeeExponent: uint64): FloatingDecima ## ToChars ## ================================================================================================== -proc utoa2Digits*(buf: var openArray[char]; pos: int; digits: uint32) {.inline.} = - const - digits100: array[200, char] = ['0', '0', '0', '1', '0', '2', '0', '3', '0', '4', '0', '5', - '0', '6', '0', '7', '0', '8', '0', '9', '1', '0', '1', '1', '1', '2', '1', '3', '1', '4', - '1', '5', '1', '6', '1', '7', '1', '8', '1', '9', '2', '0', '2', '1', '2', '2', '2', '3', - '2', '4', '2', '5', '2', '6', '2', '7', '2', '8', '2', '9', '3', '0', '3', '1', '3', '2', - '3', '3', '3', '4', '3', '5', '3', '6', '3', '7', '3', '8', '3', '9', '4', '0', '4', '1', - '4', '2', '4', '3', '4', '4', '4', '5', '4', '6', '4', '7', '4', '8', '4', '9', '5', '0', - '5', '1', '5', '2', '5', '3', '5', '4', '5', '5', '5', '6', '5', '7', '5', '8', '5', '9', - '6', '0', '6', '1', '6', '2', '6', '3', '6', '4', '6', '5', '6', '6', '6', '7', '6', '8', - '6', '9', '7', '0', '7', '1', '7', '2', '7', '3', '7', '4', '7', '5', '7', '6', '7', '7', - '7', '8', '7', '9', '8', '0', '8', '1', '8', '2', '8', '3', '8', '4', '8', '5', '8', '6', - '8', '7', '8', '8', '8', '9', '9', '0', '9', '1', '9', '2', '9', '3', '9', '4', '9', '5', - '9', '6', '9', '7', '9', '8', '9', '9'] - dragonbox_Assert(digits <= 99) - buf[pos] = digits100[2 * digits] - buf[pos+1] = digits100[2 * digits + 1] - #copyMem(buf, unsafeAddr(digits100[2 * digits]), 2 * sizeof((char))) - -proc trailingZeros2Digits*(digits: uint32): int32 {.inline.} = - const - trailingZeros100: array[100, int8] = [2'i8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0] - dragonbox_Assert(digits <= 99) - return trailingZeros100[digits] - when false: template `+!`(x: cstring; offset: int): cstring = cast[cstring](cast[uint](x) + uint(offset)) diff --git a/lib/system/schubfach.nim b/lib/system/schubfach.nim index 94d3d3c25..5f974a79e 100644 --- a/lib/system/schubfach.nim +++ b/lib/system/schubfach.nim @@ -10,6 +10,9 @@ ## https://drive.google.com/open?id=1luHhyQF9zKlM8yJ1nebU0OgVYhfC6CBN ## -------------------------------------------------------------------------------------------------- +import std/private/digitsutils + + template sf_Assert(x: untyped): untyped = assert(x) @@ -233,33 +236,6 @@ proc toDecimal32(ieeeSignificand: uint32; ieeeExponent: uint32): FloatingDecimal ## ToChars ## ================================================================================================== -proc utoa2Digits(buf: var openArray[char]; pos: int; digits: uint32) {.inline.} = - const - digits100: array[200, char] = ['0', '0', '0', '1', '0', '2', '0', '3', '0', '4', '0', '5', - '0', '6', '0', '7', '0', '8', '0', '9', '1', '0', '1', '1', '1', '2', '1', '3', '1', '4', - '1', '5', '1', '6', '1', '7', '1', '8', '1', '9', '2', '0', '2', '1', '2', '2', '2', '3', - '2', '4', '2', '5', '2', '6', '2', '7', '2', '8', '2', '9', '3', '0', '3', '1', '3', '2', - '3', '3', '3', '4', '3', '5', '3', '6', '3', '7', '3', '8', '3', '9', '4', '0', '4', '1', - '4', '2', '4', '3', '4', '4', '4', '5', '4', '6', '4', '7', '4', '8', '4', '9', '5', '0', - '5', '1', '5', '2', '5', '3', '5', '4', '5', '5', '5', '6', '5', '7', '5', '8', '5', '9', - '6', '0', '6', '1', '6', '2', '6', '3', '6', '4', '6', '5', '6', '6', '6', '7', '6', '8', - '6', '9', '7', '0', '7', '1', '7', '2', '7', '3', '7', '4', '7', '5', '7', '6', '7', '7', - '7', '8', '7', '9', '8', '0', '8', '1', '8', '2', '8', '3', '8', '4', '8', '5', '8', '6', - '8', '7', '8', '8', '8', '9', '9', '0', '9', '1', '9', '2', '9', '3', '9', '4', '9', '5', - '9', '6', '9', '7', '9', '8', '9', '9'] - sf_Assert(digits <= 99) - buf[pos] = digits100[2 * digits] - buf[pos+1] = digits100[2 * digits + 1] - -proc trailingZeros2Digits(digits: uint32): int32 {.inline.} = - const - trailingZeros100: array[100, int8] = [2'i8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0] - sf_Assert(digits <= 99) - return trailingZeros100[digits] - proc printDecimalDigitsBackwards(buf: var openArray[char]; pos: int; output: uint32): int32 {.inline.} = var output = output var pos = pos diff --git a/lib/system/strmantle.nim b/lib/system/strmantle.nim index 1a32a4afe..b60bc4b80 100644 --- a/lib/system/strmantle.nim +++ b/lib/system/strmantle.nim @@ -9,72 +9,9 @@ # Compilerprocs for strings that do not depend on the string implementation. -const digitsTable = "0001020304050607080910111213141516171819" & - "2021222324252627282930313233343536373839" & - "4041424344454647484950515253545556575859" & - "6061626364656667686970717273747576777879" & - "8081828384858687888990919293949596979899" - # Inspired by https://engineering.fb.com/2013/03/15/developer-tools/three-optimization-tips-for-c - # Generates: - # .. code-block:: nim - # var res = "" - # for i in 0 .. 99: - # if i < 10: - # res.add "0" & $i - # else: - # res.add $i - # doAssert res == digitsTable +import std/private/digitsutils -func digits10(num: uint64): int {.noinline.} = - if num < 10'u64: - result = 1 - elif num < 100'u64: - result = 2 - elif num < 1_000'u64: - result = 3 - elif num < 10_000'u64: - result = 4 - elif num < 100_000'u64: - result = 5 - elif num < 1_000_000'u64: - result = 6 - elif num < 10_000_000'u64: - result = 7 - elif num < 100_000_000'u64: - result = 8 - elif num < 1_000_000_000'u64: - result = 9 - elif num < 10_000_000_000'u64: - result = 10 - elif num < 100_000_000_000'u64: - result = 11 - elif num < 1_000_000_000_000'u64: - result = 12 - else: - result = 12 + digits10(num div 1_000_000_000_000'u64) - -template numToString(result: var string, origin: uint64, length: int) = - var num = origin - var next = length - 1 - const nbatch = 100 - - while num >= nbatch: - let originNum = num - num = num div nbatch - let index = (originNum - num * nbatch) shl 1 - result[next] = digitsTable[index + 1] - result[next - 1] = digitsTable[index] - dec(next, 2) - - # process last 1-2 digits - if num < 10: - result[next] = chr(ord('0') + num) - else: - let index = num * 2 - result[next] = digitsTable[index + 1] - result[next - 1] = digitsTable[index] - proc cmpStrings(a, b: string): int {.inline, compilerproc.} = let alen = a.len let blen = b.len From 5badeea17055c85ea8177baeee62c8d1d06ac13e Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 22 Jun 2021 21:42:39 -0700 Subject: [PATCH 2260/3768] followup #18318: simplify `dollarImpl` and add a test (#18330) --- lib/system/dollars.nim | 7 ------- tests/stdlib/tdigitsutils.nim | 23 +++++++++++++++++++++++ 2 files changed, 23 insertions(+), 7 deletions(-) create mode 100644 tests/stdlib/tdigitsutils.nim diff --git a/lib/system/dollars.nim b/lib/system/dollars.nim index 75390b0e1..3d824a965 100644 --- a/lib/system/dollars.nim +++ b/lib/system/dollars.nim @@ -7,15 +7,8 @@ proc `$`*(x: int): string {.magic: "IntToStr", noSideEffect.} ## spelling `toString`:idx:. template dollarImpl(x: uint | uint64, result: var string) = - type destTyp = typeof(x) - if x == 0: - return "0" - elif x == 1: - return "1" - let length = digits10(x) setLen(result, length) - numToString(result, x, length) when defined(js): diff --git a/tests/stdlib/tdigitsutils.nim b/tests/stdlib/tdigitsutils.nim new file mode 100644 index 000000000..7cf63411f --- /dev/null +++ b/tests/stdlib/tdigitsutils.nim @@ -0,0 +1,23 @@ +import std/private/digitsutils + +template main = + block: # digits10 + doAssert digits10(0'u64) == 1 + # checks correctness on all powers of 10 + [0,-1,1] + var x = 1'u64 + var num = 1 + while true: + # echo (x, num) + doAssert digits10(x) == num + doAssert digits10(x+1) == num + if x > 1: + doAssert digits10(x-1) == num - 1 + num += 1 + let xOld = x + x *= 10 + if x < xOld: + # wrap-around + break + +static: main() +main() From 9c43f05099aaddae236df8b86da0cee342a785b8 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Wed, 23 Jun 2021 08:50:05 +0300 Subject: [PATCH 2261/3768] Markdown: allow to end URL with balanced parenthesis (#18321) * Markdown: allow to end URL with balanced parenthesis * Update lib/packages/docutils/rst.nim Co-authored-by: Timothee Cour * apply suggestion * remove unnecessary if Co-authored-by: Timothee Cour --- lib/packages/docutils/rst.nim | 36 ++++++++++++++++++++++++++++-- tests/stdlib/trst.nim | 41 +++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 2 deletions(-) diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 64910aa19..7833e6e9a 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -1246,15 +1246,43 @@ proc isUrl(p: RstParser, i: int): bool = p.tok[i+3].kind == tkWord and p.tok[i].symbol in ["http", "https", "ftp", "telnet", "file"] +proc checkParen(token: Token, parensStack: var seq[char]): bool {.inline.} = + ## Returns `true` iff `token` is a closing parenthesis for some + ## previous opening parenthesis saved in `parensStack`. + ## This is according Markdown balanced parentheses rule + ## (https://spec.commonmark.org/0.29/#link-destination) + ## to allow links like + ## https://en.wikipedia.org/wiki/APL_(programming_language), + ## we use it for RST also. + result = false + if token.kind == tkPunct: + let c = token.symbol[0] + if c in {'(', '[', '{'}: # push + parensStack.add c + elif c in {')', ']', '}'}: # try pop + # a case like ([) inside a link is allowed and [ is also `pop`ed: + for i in countdown(parensStack.len - 1, 0): + if (parensStack[i] == '(' and c == ')' or + parensStack[i] == '[' and c == ']' or + parensStack[i] == '{' and c == '}'): + parensStack.setLen i + result = true + break + proc parseUrl(p: var RstParser): PRstNode = ## https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#standalone-hyperlinks result = newRstNode(rnStandaloneHyperlink) var lastIdx = p.idx + var closedParenIdx = p.idx - 1 # for balanced parens rule + var parensStack: seq[char] while p.tok[lastIdx].kind in {tkWord, tkPunct, tkOther}: + let isClosing = checkParen(p.tok[lastIdx], parensStack) + if isClosing: + closedParenIdx = lastIdx inc lastIdx dec lastIdx # standalone URL can not end with punctuation in RST - while lastIdx >= p.idx and p.tok[lastIdx].kind == tkPunct and + while lastIdx > closedParenIdx and p.tok[lastIdx].kind == tkPunct and p.tok[lastIdx].symbol != "/": dec lastIdx var s = "" @@ -1393,11 +1421,15 @@ proc parseMarkdownLink(p: var RstParser; father: PRstNode): bool = var desc, link = "" var i = p.idx + var parensStack: seq[char] template parse(endToken, dest) = + parensStack.setLen 0 inc i # skip begin token while true: if p.tok[i].kind in {tkEof, tkIndent}: return false - if p.tok[i].symbol == endToken: break + let isClosing = checkParen(p.tok[i], parensStack) + if p.tok[i].symbol == endToken and not isClosing: + break dest.add p.tok[i].symbol inc i inc i # skip end token diff --git a/tests/stdlib/trst.nim b/tests/stdlib/trst.nim index ec34edc91..c69450cd9 100644 --- a/tests/stdlib/trst.nim +++ b/tests/stdlib/trst.nim @@ -580,3 +580,44 @@ suite "RST inline markup": rnLeaf ' ' rnLeaf 'end' """) + + test "URL with balanced parentheses (Markdown rule)": + # 2 balanced parens, 1 unbalanced: + check(dedent""" + https://en.wikipedia.org/wiki/APL_((programming_language)))""".toAst == + dedent""" + rnInner + rnStandaloneHyperlink + rnLeaf 'https://en.wikipedia.org/wiki/APL_((programming_language))' + rnLeaf ')' + """) + + # the same for Markdown-style link: + check(dedent""" + [foo [bar]](https://en.wikipedia.org/wiki/APL_((programming_language))))""".toAst == + dedent""" + rnInner + rnHyperlink + rnLeaf 'foo [bar]' + rnLeaf 'https://en.wikipedia.org/wiki/APL_((programming_language))' + rnLeaf ')' + """) + + # unbalanced (here behavior is more RST-like actually): + check(dedent""" + https://en.wikipedia.org/wiki/APL_(programming_language(""".toAst == + dedent""" + rnInner + rnStandaloneHyperlink + rnLeaf 'https://en.wikipedia.org/wiki/APL_(programming_language' + rnLeaf '(' + """) + + # unbalanced [, but still acceptable: + check(dedent""" + [my {link example](http://example.com/bracket_(symbol_[))""".toAst == + dedent""" + rnHyperlink + rnLeaf 'my {link example' + rnLeaf 'http://example.com/bracket_(symbol_[)' + """) From 0eee7f572796d3626db1d94d3eb344b399780db1 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 23 Jun 2021 14:39:10 +0200 Subject: [PATCH 2262/3768] OS.nim: style changes (#18331) --- lib/pure/os.nim | 58 ++++++++++++++++++++++++------------------------- 1 file changed, 28 insertions(+), 30 deletions(-) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 4dd5c8d8b..213da2aed 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -320,17 +320,17 @@ else: template `!=?`(a, b: char): bool = toLowerAscii(a) != toLowerAscii(b) when doslikeFileSystem: - proc isAbsFromCurrentDrive(path: string): bool {.noSideEffect, raises: []} = + proc isAbsFromCurrentDrive(path: string): bool {.noSideEffect, raises: [].} = ## An absolute path from the root of the current drive (e.g. "\foo") path.len > 0 and (path[0] == AltSep or (path[0] == DirSep and (path.len == 1 or path[1] notin {DirSep, AltSep, ':'}))) - proc isUNCPrefix(path: string): bool {.noSideEffect, raises: []} = + proc isUNCPrefix(path: string): bool {.noSideEffect, raises: [].} = path[0] == DirSep and path[1] == DirSep - proc sameRoot(path1, path2: string): bool {.noSideEffect, raises: []} = + proc sameRoot(path1, path2: string): bool {.noSideEffect, raises: [].} = ## Return true if path1 and path2 have a same root. ## ## Detail of windows path formats: @@ -935,7 +935,7 @@ proc getConfigDir*(): string {.rtl, extern: "nos$1", proc getCacheDir*(): string = ## Returns the cache directory of the current user for applications. - ## + ## ## This makes use of the following environment variables: ## ## * On Windows: `getEnv("LOCALAPPDATA")` @@ -1038,7 +1038,7 @@ proc expandTilde*(path: string): string {. ## Expands ``~`` or a path starting with ``~/`` to a full path, replacing ## ``~`` with `getHomeDir() <#getHomeDir>`_ (otherwise returns ``path`` unmodified). ## - ## Windows: this is still supported despite Windows platform not having this + ## Windows: this is still supported despite the Windows platform not having this ## convention; also, both ``~/`` and ``~\`` are handled. ## ## .. warning:: `~bob` and `~bob/` are not yet handled correctly. @@ -1101,18 +1101,14 @@ proc quoteShellWindows*(s: string): string {.noSideEffect, rtl, extern: "nosp$1" proc quoteShellPosix*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} = ## Quote ``s``, so it can be safely passed to POSIX shell. - ## Based on Python's `pipes.quote`. const safeUnixChars = {'%', '+', '-', '.', '/', '_', ':', '=', '@', '0'..'9', 'A'..'Z', 'a'..'z'} if s.len == 0: - return "''" - - let safe = s.allCharsInSet(safeUnixChars) - - if safe: - return s + result = "''" + elif s.allCharsInSet(safeUnixChars): + result = s else: - return "'" & s.replace("'", "'\"'\"'") & "'" + result = "'" & s.replace("'", "'\"'\"'") & "'" when defined(windows) or defined(posix) or defined(nintendoswitch): proc quoteShell*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} = @@ -1122,9 +1118,9 @@ when defined(windows) or defined(posix) or defined(nintendoswitch): ## <#quoteShellWindows,string>`_. Otherwise, calls `quoteShellPosix proc ## <#quoteShellPosix,string>`_. when defined(windows): - return quoteShellWindows(s) + result = quoteShellWindows(s) else: - return quoteShellPosix(s) + result = quoteShellPosix(s) proc quoteShellCommand*(args: openArray[string]): string = ## Concatenates and quotes shell arguments `args`. @@ -1217,7 +1213,7 @@ proc dirExists*(dir: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect] result = (a and FILE_ATTRIBUTE_DIRECTORY) != 0'i32 else: var res: Stat - return stat(dir, res) >= 0'i32 and S_ISDIR(res.st_mode) + result = stat(dir, res) >= 0'i32 and S_ISDIR(res.st_mode) proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect], @@ -1239,7 +1235,7 @@ proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1", result = (a and FILE_ATTRIBUTE_REPARSE_POINT) != 0'i32 else: var res: Stat - return lstat(link, res) >= 0'i32 and S_ISLNK(res.st_mode) + result = lstat(link, res) >= 0'i32 and S_ISLNK(res.st_mode) when not defined(windows): @@ -1752,17 +1748,18 @@ proc isAdmin*: bool {.noWeirdTarget.} = addr administratorsGroup)): raiseOSError(osLastError(), "could not get SID for Administrators group") - defer: + try: + var b: WINBOOL + if not isSuccess(checkTokenMembership(0, administratorsGroup, addr b)): + raiseOSError(osLastError(), "could not check access token membership") + + result = isSuccess(b) + finally: if freeSid(administratorsGroup) != nil: raiseOSError(osLastError(), "failed to free SID for Administrators group") - var b: WINBOOL - if not isSuccess(checkTokenMembership(0, administratorsGroup, addr b)): - raiseOSError(osLastError(), "could not check access token membership") - - return isSuccess(b) else: - return geteuid() == 0 + result = geteuid() == 0 proc createSymlink*(src, dest: string) {.noWeirdTarget.} = ## Create a symbolic link at `dest` which points to the item specified @@ -1828,10 +1825,11 @@ when hasCCopyfile: COPYFILE_XATTR {.nodecl.}: copyfile_flags_t {.pop.} -type CopyFlag* = enum ## Copy options. - cfSymlinkAsIs, ## Copy symlinks as symlinks - cfSymlinkFollow, ## Copy the files symlinks point to - cfSymlinkIgnore ## Ignore symlinks +type + CopyFlag* = enum ## Copy options. + cfSymlinkAsIs, ## Copy symlinks as symlinks + cfSymlinkFollow, ## Copy the files symlinks point to + cfSymlinkIgnore ## Ignore symlinks const copyFlagSymlink = {cfSymlinkAsIs, cfSymlinkFollow, cfSymlinkIgnore} @@ -2027,7 +2025,7 @@ proc tryMoveFSObject(source, dest: string, isDir: bool): bool {.noWeirdTarget.} if not result: let err = osLastError() - let isAccessDeniedError = + let isAccessDeniedError = when defined(windows): const AccessDeniedError = OSErrorCode(5) isDir and err == AccessDeniedError @@ -2066,7 +2064,7 @@ proc moveFile*(source, dest: string) {.rtl, extern: "nos$1", except: discard tryRemoveFile(dest) raise - + proc exitStatusLikeShell*(status: cint): cint = ## Converts exit code from `c_system` into a shell exit code. From 63456c6d7f84f29da38703fb3e59acf00acec1b8 Mon Sep 17 00:00:00 2001 From: Antonis Geralis <43617260+planetis-m@users.noreply.github.com> Date: Wed, 23 Jun 2021 18:56:20 +0300 Subject: [PATCH 2263/3768] Add some tests (#18333) --- tests/destructor/tv2_cast.nim | 72 ++++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 2 deletions(-) diff --git a/tests/destructor/tv2_cast.nim b/tests/destructor/tv2_cast.nim index ef0b3a936..5502cb38d 100644 --- a/tests/destructor/tv2_cast.nim +++ b/tests/destructor/tv2_cast.nim @@ -1,10 +1,78 @@ discard """ - cmd: '''nim c --gc:arc $file''' output: '''@[1] @[116, 101, 115, 116] @[1953719668, 875770417]''' + cmd: '''nim c --gc:arc --expandArc:main --expandArc:main1 --expandArc:main2 --expandArc:main3 --hints:off --assertions:off $file''' + nimout: '''--expandArc: main + +var + data + :tmpD +`=copy`(data, cast[string](encode(cast[seq[byte]]( + :tmpD = newString(100) + :tmpD)))) +`=destroy`(:tmpD) +`=destroy`(data) +-- end of expandArc ------------------------ +--expandArc: main1 + +var + s + data +s = newString(100) +`=copy`(data, cast[string](encode(toOpenArrayByte(s, 0, len(s) - 1)))) +`=destroy`(data) +`=destroy`(s) +-- end of expandArc ------------------------ +--expandArc: main2 + +var + s + data +s = newSeq(100) +`=copy`(data, cast[string](encode(s))) +`=destroy`(data) +`=destroy_1`(s) +-- end of expandArc ------------------------ +--expandArc: main3 + +var + data + :tmpD +`=copy`(data, cast[string](encode do: + :tmpD = newSeq(100) + :tmpD)) +`=destroy`(:tmpD) +`=destroy_1`(data) +-- end of expandArc ------------------------''' """ +func encode*(src: openArray[byte]): seq[byte] = + result = newSeq[byte](src.len) + +template compress*(src: string): string = + cast[string](encode(cast[seq[byte]](src))) + +proc main = + let data = compress(newString(100)) +main() + +proc main1 = + var + s = newString(100) + let data = cast[string](encode(s.toOpenArrayByte(0, s.len-1))) +main1() + +proc main2 = + var + s = newSeq[byte](100) + let data = cast[string](encode(s)) +main2() + +proc main3 = + let data = cast[string](encode(newSeq[byte](100))) +main3() + # bug #11018 discard cast[seq[uint8]](@[1]) discard cast[seq[uint8]]("test") @@ -20,4 +88,4 @@ echo a #issue 11204 var ac {.compileTime.} = @["a", "b"] -const bc = ac.len \ No newline at end of file +const bc = ac.len From 496bd790e1b641a7c025bde2c6a1c6dca433ed5d Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 24 Jun 2021 03:33:19 +0800 Subject: [PATCH 2264/3768] [std/times]getTime now uses high resolution API on windows (#17901) --- lib/pure/times.nim | 5 +++-- lib/windows/winlean.nim | 3 +++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index a92eccc8c..7bb393aa0 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -880,6 +880,7 @@ since((1, 1)): export fromUnixFloat export toUnixFloat + proc fromWinTime*(win: int64): Time = ## Convert a Windows file time (100-nanosecond intervals since ## `1601-01-01T00:00:00Z`) to a `Time`. @@ -912,7 +913,7 @@ proc getTime*(): Time {.tags: [TimeEffect], benign.} = result = initTime(ts.tv_sec.int64, ts.tv_nsec.int) elif defined(windows): var f {.noinit.}: FILETIME - getSystemTimeAsFileTime(f) + getSystemTimePreciseAsFileTime(f) result = fromWinTime(rdFileTime(f)) proc `-`*(a, b: Time): Duration {.operator, extern: "ntDiffTime".} = @@ -2595,7 +2596,7 @@ proc epochTime*(): float {.tags: [TimeEffect].} = toBiggestFloat(ts.tv_nsec.int64) / 1_000_000_000 elif defined(windows): var f {.noinit.}: winlean.FILETIME - getSystemTimeAsFileTime(f) + getSystemTimePreciseAsFileTime(f) var i64 = rdFileTime(f) - epochDiff var secs = i64 div rateDiff var subsecs = i64 mod rateDiff diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index d2d7d26cc..a71dd97d8 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -928,6 +928,9 @@ proc getProcessTimes*(hProcess: Handle; lpCreationTime, lpExitTime, lpKernelTime, lpUserTime: var FILETIME): WINBOOL {.stdcall, dynlib: "kernel32", importc: "GetProcessTimes".} +proc getSystemTimePreciseAsFileTime*(lpSystemTimeAsFileTime: var FILETIME) {. + importc: "GetSystemTimePreciseAsFileTime", dynlib: "kernel32", stdcall, sideEffect.} + type inet_ntop_proc = proc(family: cint, paddr: pointer, pStringBuffer: cstring, stringBufSize: int32): cstring {.gcsafe, stdcall, tags: [].} From 0f91b67f5c15328330f74a8769aed9961940aab2 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 23 Jun 2021 23:31:17 +0200 Subject: [PATCH 2265/3768] fixes #18326 (#18335) * fixes #18326 * make tests green again --- compiler/isolation_check.nim | 4 ++-- tests/isolate/tisolate.nim | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/compiler/isolation_check.nim b/compiler/isolation_check.nim index 43db5d59b..777e7f6ce 100644 --- a/compiler/isolation_check.nim +++ b/compiler/isolation_check.nim @@ -64,7 +64,7 @@ proc canAlias(arg, ret: PType; marker: var IntSet): bool = else: result = false -proc isValueOnlyType(t: PType): bool = +proc isValueOnlyType(t: PType): bool = # t doesn't contain pointers and references proc wrap(t: PType): bool {.nimcall.} = t.kind in {tyRef, tyPtr, tyVar, tyLent} result = not types.searchTypeFor(t, wrap) @@ -88,7 +88,7 @@ proc checkIsolate*(n: PNode): bool = # XXX: as long as we don't update the analysis while examining arguments # we can do an early check of the return type, otherwise this is a # bug and needs to be moved below - if n[0].typ.flags * {tfGcSafe, tfNoSideEffect} == {}: + if tfNoSideEffect notin n[0].typ.flags: return false for i in 1.. Date: Wed, 23 Jun 2021 23:31:55 -0700 Subject: [PATCH 2266/3768] fix #18332: XDeclaredButNotUsed hints now in deterministic order (#18336) --- compiler/lookups.nim | 10 ++++++---- tests/errmsgs/treportunused.nim | 24 +++++++++++++----------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 4f413f472..ee79673d0 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -8,11 +8,10 @@ # # This module implements lookup helpers. - +import std/[algorithm, strutils] import intsets, ast, astalgo, idents, semdata, types, msgs, options, - renderer, nimfix/prettybase, lineinfos, strutils, - modulegraphs, astmsgs + renderer, nimfix/prettybase, lineinfos, modulegraphs, astmsgs proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) @@ -268,6 +267,7 @@ proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) = var it: TTabIter var s = initTabIter(it, scope.symbols) var missingImpls = 0 + var unusedSyms: seq[tuple[sym: PSym, key: string]] while s != nil: if sfForward in s.flags and s.kind notin {skType, skModule}: # too many 'implementation of X' errors are annoying @@ -282,8 +282,10 @@ proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) = # maybe they can be made skGenericParam as well. if s.typ != nil and tfImplicitTypeParam notin s.typ.flags and s.typ.kind != tyGenericParam: - message(c.config, s.info, hintXDeclaredButNotUsed, s.name.s) + unusedSyms.add (s, toFileLineCol(c.config, s.info)) s = nextIter(it, scope.symbols) + for (s, _) in sortedByIt(unusedSyms, it.key): + message(c.config, s.info, hintXDeclaredButNotUsed, s.name.s) proc wrongRedefinition*(c: PContext; info: TLineInfo, s: string; conflictsWith: TLineInfo) = diff --git a/tests/errmsgs/treportunused.nim b/tests/errmsgs/treportunused.nim index b339e06bf..3e945013e 100644 --- a/tests/errmsgs/treportunused.nim +++ b/tests/errmsgs/treportunused.nim @@ -1,16 +1,18 @@ discard """ + matrix: "--hint:all:off --hint:XDeclaredButNotUsed" + nimoutFull: true nimout: ''' -treportunused.nim(19, 10) Hint: 's1' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(26, 5) Hint: 's8' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(22, 6) Hint: 's4' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(25, 7) Hint: 's7' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(24, 7) Hint: 's6' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(23, 6) Hint: 's5' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(20, 10) Hint: 's2' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(29, 6) Hint: 's11' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(27, 5) Hint: 's9' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(21, 10) Hint: 's3' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(28, 6) Hint: 's10' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(21, 10) Hint: 's1' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(22, 10) Hint: 's2' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(23, 10) Hint: 's3' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(24, 6) Hint: 's4' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(25, 6) Hint: 's5' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(26, 7) Hint: 's6' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(27, 7) Hint: 's7' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(28, 5) Hint: 's8' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(29, 5) Hint: 's9' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(30, 6) Hint: 's10' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(31, 6) Hint: 's11' is declared but not used [XDeclaredButNotUsed] ''' action: compile """ From 55c1953f636ff2d2e2b9e9599194fa6c3fb49050 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 24 Jun 2021 00:58:11 -0700 Subject: [PATCH 2267/3768] fix #18334: handle path with spaces on windows during bootstrap (#18337) --- compiler/nim.nim | 27 +++++++++++++++------------ koch.nim | 2 +- 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/compiler/nim.nim b/compiler/nim.nim index f18238f60..91b1bc2db 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -7,21 +7,24 @@ # distribution, for details about the copyright. # -when defined(gcc) and defined(windows): - when defined(x86): - {.link: "../icons/nim.res".} - else: - {.link: "../icons/nim_icon.o".} +import std/[os, strutils, parseopt] +when defined(windows) and not defined(nimKochBootstrap): + # remove workaround pending bootstrap >= 1.5.1 + # refs https://github.com/nim-lang/Nim/issues/18334#issuecomment-867114536 + # alternative would be to prepend `currentSourcePath.parentDir.quoteShell` + when defined(gcc): + when defined(x86): + {.link: "../icons/nim.res".} + else: + {.link: "../icons/nim_icon.o".} -when defined(amd64) and defined(windows) and defined(vcc): - {.link: "../icons/nim-amd64-windows-vcc.res".} -when defined(i386) and defined(windows) and defined(vcc): - {.link: "../icons/nim-i386-windows-vcc.res".} + when defined(amd64) and defined(vcc): + {.link: "../icons/nim-amd64-windows-vcc.res".} + when defined(i386) and defined(vcc): + {.link: "../icons/nim-i386-windows-vcc.res".} import - commands, options, msgs, - extccomp, strutils, os, main, parseopt, - idents, lineinfos, cmdlinehelper, + commands, options, msgs, extccomp, main, idents, lineinfos, cmdlinehelper, pathutils, modulegraphs from browsers import openDefaultBrowser diff --git a/koch.nim b/koch.nim index 665b5e042..51be53cc7 100644 --- a/koch.nim +++ b/koch.nim @@ -308,7 +308,7 @@ proc boot(args: string) = var nimi = i.thVersion if i == 0: nimi = nimStart - extraOption.add " --skipUserCfg --skipParentCfg" + extraOption.add " --skipUserCfg --skipParentCfg -d:nimKochBootstrap" # The configs are skipped for bootstrap # (1st iteration) to prevent newer flags from breaking bootstrap phase. let ret = execCmdEx(nimStart & " --version") From 0c8d3ae9852721147ddf878e204c14e949ed676a Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Thu, 24 Jun 2021 11:28:28 +0300 Subject: [PATCH 2268/3768] rst: allow comment to continue on second line (#18338) --- lib/packages/docutils/rst.nim | 28 ++++++--------- tests/stdlib/trst.nim | 66 +++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+), 18 deletions(-) diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 7833e6e9a..8ffbbd4d3 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -1615,24 +1615,16 @@ proc getDirective(p: var RstParser): string = "too many colons for a directive (should be ::)", p.tok[afterIdx].line, p.tok[afterIdx].col) -proc parseComment(p: var RstParser): PRstNode = - case currentTok(p).kind - of tkIndent, tkEof: - if currentTok(p).kind != tkEof and nextTok(p).kind == tkIndent: - inc p.idx # empty comment - else: - var indent = currentTok(p).ival - while true: - case currentTok(p).kind - of tkEof: - break - of tkIndent: - if currentTok(p).ival < indent: break - else: - discard - inc p.idx +proc parseComment(p: var RstParser, col: int): PRstNode = + if currentTok(p).kind != tkEof and nextTok(p).kind == tkIndent: + inc p.idx # empty comment else: - while currentTok(p).kind notin {tkIndent, tkEof}: inc p.idx + while currentTok(p).kind != tkEof: + if currentTok(p).kind == tkIndent and currentTok(p).ival > col or + currentTok(p).kind != tkIndent and currentTok(p).col > col: + inc p.idx + else: + break result = nil proc parseLine(p: var RstParser, father: PRstNode) = @@ -2841,7 +2833,7 @@ proc parseDotDot(p: var RstParser): PRstNode = (n = parseFootnote(p); n != nil): result = n else: - result = parseComment(p) + result = parseComment(p, col) proc rstParsePass1*(fragment, filename: string, line, column: int, diff --git a/tests/stdlib/trst.nim b/tests/stdlib/trst.nim index c69450cd9..d53092d3b 100644 --- a/tests/stdlib/trst.nim +++ b/tests/stdlib/trst.nim @@ -83,6 +83,72 @@ suite "RST parsing": rnLeaf 'set' """) + test "RST comment": + check(dedent""" + .. comment1 + comment2 + someParagraph""".toAst == + dedent""" + rnLeaf 'someParagraph' + """) + + check(dedent""" + .. + comment1 + comment2 + someParagraph""".toAst == + dedent""" + rnLeaf 'someParagraph' + """) + + test "check that additional line right after .. ends comment": + check(dedent""" + .. + + notAcomment1 + notAcomment2 + someParagraph""".toAst == + dedent""" + rnInner + rnBlockQuote + rnInner + rnLeaf 'notAcomment1' + rnLeaf ' ' + rnLeaf 'notAcomment2' + rnParagraph + rnLeaf 'someParagraph' + """) + + test "but blank lines after 2nd non-empty line don't end the comment": + check(dedent""" + .. + comment1 + + + comment2 + someParagraph""".toAst == + dedent""" + rnLeaf 'someParagraph' + """) + + test "using .. as separator b/w directives and block quotes": + check(dedent""" + .. note:: someNote + + .. + + someBlockQuote""".toAst == + dedent""" + rnInner + rnAdmonition adType=note + [nil] + [nil] + rnLeaf 'someNote' + rnBlockQuote + rnInner + rnLeaf 'someBlockQuote' + """) + suite "RST indentation": test "nested bullet lists": let input = dedent """ From 565e07a9936657fa356777eec5f0f52b2d5d7e02 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 24 Jun 2021 02:55:31 -0700 Subject: [PATCH 2269/3768] enable VM tracing in user code via `{.define(nimVmTrace).}` (#18244) * enable VM tracing in user code via `{.define(nimVmTrace).}` * add vmutils.vmTrace * add vmTrace --- changelog.md | 2 ++ compiler/options.nim | 1 + compiler/vm.nim | 7 +++++-- compiler/vmops.nim | 3 +++ lib/std/vmutils.nim | 11 +++++++++++ tests/stdlib/tvmutils.nim | 31 +++++++++++++++++++++++++++++++ 6 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 lib/std/vmutils.nim create mode 100644 tests/stdlib/tvmutils.nim diff --git a/changelog.md b/changelog.md index 570aff542..6672f7538 100644 --- a/changelog.md +++ b/changelog.md @@ -387,6 +387,8 @@ - Added `--declaredlocs` to show symbol declaration location in messages. +- You can now enable/disable VM tracing in user code via `vmutils.vmTrace`. + - Deprecated `TaintedString` and `--taintmode`. - Deprecated `--nilseqs` which is now a noop. diff --git a/compiler/options.nim b/compiler/options.nim index ffb0f4501..135ecf1de 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -330,6 +330,7 @@ type warnCounter*: int errorMax*: int maxLoopIterationsVM*: int ## VM: max iterations of all loops + isVmTrace*: bool configVars*: StringTableRef symbols*: StringTableRef ## We need to use a StringTableRef here as defined ## symbols are always guaranteed to be style diff --git a/compiler/vm.nim b/compiler/vm.nim index 837a1c366..9541af701 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -548,9 +548,12 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = "pc", $pc, "opcode", alignLeft($c.code[pc].opcode, 15), "ra", regDescr("ra", ra), "rb", regDescr("rb", instr.regB), "rc", regDescr("rc", instr.regC)] - + if c.config.isVmTrace: + # unlike nimVMDebug, this doesn't require re-compiling nim and is controlled by user code + let info = c.debug[pc] + # other useful variables: c.loopIterations + echo "$# [$#] $#" % [c.config$info, $instr.opcode, c.config.sourceLine(info)] c.profiler.enter(c, tos) - case instr.opcode of opcEof: return regs[ra] of opcRet: diff --git a/compiler/vmops.nim b/compiler/vmops.nim index 85729fd59..67a196b8f 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -249,6 +249,9 @@ proc registerAdditionalOps*(c: PCtx) = "isExported() requires a symbol. '" & $n & "' is of kind '" & $n.kind & "'", n.info) setResult(a, sfExported in n.sym.flags) + registerCallback c, "stdlib.vmutils.vmTrace", proc (a: VmArgs) = + c.config.isVmTrace = getBool(a, 0) + proc hashVmImpl(a: VmArgs) = var res = hashes.hash(a.getString(0), a.getInt(1).int, a.getInt(2).int) if c.config.backend == backendJs: diff --git a/lib/std/vmutils.nim b/lib/std/vmutils.nim new file mode 100644 index 000000000..e16912a3c --- /dev/null +++ b/lib/std/vmutils.nim @@ -0,0 +1,11 @@ +##[ +Experimental API, subject to change. +]## + +proc vmTrace*(on: bool) {.compileTime.} = + runnableExamples: + static: vmTrace(true) + proc fn = + var a = 1 + vmTrace(false) + static: fn() diff --git a/tests/stdlib/tvmutils.nim b/tests/stdlib/tvmutils.nim new file mode 100644 index 000000000..f43557ad8 --- /dev/null +++ b/tests/stdlib/tvmutils.nim @@ -0,0 +1,31 @@ +discard """ + joinable: false + nimout: ''' +0 +1 +2 +tvmutils.nim(28, 13) [opcLdImmInt] if i == 4: +tvmutils.nim(28, 10) [opcEqInt] if i == 4: +tvmutils.nim(28, 10) [opcFJmp] if i == 4: +tvmutils.nim(28, 13) [opcLdImmInt] if i == 4: +tvmutils.nim(28, 10) [opcEqInt] if i == 4: +tvmutils.nim(28, 10) [opcFJmp] if i == 4: +tvmutils.nim(29, 7) [opcLdConst] vmTrace(false) +tvmutils.nim(29, 15) [opcLdImmInt] vmTrace(false) +tvmutils.nim(29, 14) [opcIndCall] vmTrace(false) +5 +6 +''' +""" +# line 20 (only showing a subset of nimout to avoid making the test rigid) +import std/vmutils + +proc main() = + for i in 0..<7: + echo i + if i == 2: + vmTrace(true) + if i == 4: + vmTrace(false) + +static: main() From 16038d44f6b246c5f39d1a8609a8c45f2cd005a4 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 24 Jun 2021 18:37:04 +0200 Subject: [PATCH 2270/3768] fixes #18320 (#18343) * TSymFlag has 47 flags already * fixes #18320 --- compiler/ast.nim | 2 +- compiler/varpartitions.nim | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 8bb8de1d6..56043692f 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -229,7 +229,7 @@ type TNodeKinds* = set[TNodeKind] type - TSymFlag* = enum # 46 flags! + TSymFlag* = enum # 47 flags! sfUsed, # read access of sym (for warnings) or simply used sfExported, # symbol is exported from module sfFromGeneric, # symbol is instantiation of a generic; this is needed diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim index 697dd2b1d..213c3b80e 100644 --- a/compiler/varpartitions.nim +++ b/compiler/varpartitions.nim @@ -51,6 +51,7 @@ type SubgraphFlag = enum isMutated, # graph might be mutated isMutatedDirectly, # graph is mutated directly by a non-var parameter. + isMutatedByVarParam, # graph is mutated by a var parameter. connectsConstParam # graph is connected to a non-var parameter. VarFlag = enum @@ -184,7 +185,7 @@ proc potentialMutation(v: var Partitions; s: PSym; level: int; info: TLineInfo) {isMutated, isMutatedDirectly} elif s.typ.kind == tyVar and level <= 1: # varParam[i] = v is different from varParam[i][] = v - {} + {isMutatedByVarParam} else: {isMutated} else: @@ -878,7 +879,7 @@ proc computeGraphPartitions*(s: PSym; n: PNode; g: ModuleGraph; goals: set[Goal] proc dangerousMutation(g: MutationInfo; v: VarIndex): bool = #echo "range ", v.aliveStart, " .. ", v.aliveEnd, " ", v.sym - if isMutated in g.flags: + if {isMutated, isMutatedByVarParam} * g.flags != {}: for m in g.mutations: #echo "mutation ", m if m in v.aliveStart..v.aliveEnd: @@ -941,4 +942,5 @@ proc computeCursors*(s: PSym; n: PNode; g: ModuleGraph) = discard "cannot cursor into a graph that is mutated" else: v.sym.flags.incl sfCursor - #echo "this is now a cursor ", v.sym, " ", par.s[rid].flags, " ", g.config $ v.sym.info + when false: + echo "this is now a cursor ", v.sym, " ", par.s[rid].flags, " ", g.config $ v.sym.info From f128f7971f6eda5dc9b21af3dbaeccc5dc71da5a Mon Sep 17 00:00:00 2001 From: Kaushal Modi Date: Thu, 24 Jun 2021 13:19:50 -0400 Subject: [PATCH 2271/3768] float parsing: Add test for a fixed issue (#18232) Fixes https://github.com/nim-lang/Nim/issues/14407 . This issue was fixed by https://github.com/nim-lang/Nim/pull/18139. --- tests/errmsgs/treportunused.nim | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/tests/errmsgs/treportunused.nim b/tests/errmsgs/treportunused.nim index 3e945013e..65e00163b 100644 --- a/tests/errmsgs/treportunused.nim +++ b/tests/errmsgs/treportunused.nim @@ -2,17 +2,19 @@ discard """ matrix: "--hint:all:off --hint:XDeclaredButNotUsed" nimoutFull: true nimout: ''' -treportunused.nim(21, 10) Hint: 's1' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(22, 10) Hint: 's2' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(23, 10) Hint: 's3' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(24, 6) Hint: 's4' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(25, 6) Hint: 's5' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(26, 7) Hint: 's6' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(27, 7) Hint: 's7' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(28, 5) Hint: 's8' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(29, 5) Hint: 's9' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(30, 6) Hint: 's10' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(31, 6) Hint: 's11' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(23, 10) Hint: 's1' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(24, 10) Hint: 's2' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(25, 10) Hint: 's3' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(26, 6) Hint: 's4' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(27, 6) Hint: 's5' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(28, 7) Hint: 's6' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(29, 7) Hint: 's7' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(30, 5) Hint: 's8' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(31, 5) Hint: 's9' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(32, 6) Hint: 's10' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(33, 6) Hint: 's11' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(37, 3) Hint: 'v0.99' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(38, 3) Hint: 'v0.99.99' is declared but not used [XDeclaredButNotUsed] ''' action: compile """ @@ -29,3 +31,8 @@ let s8 = 0 var s9: int type s10 = object type s11 = type(1.2) + +# https://github.com/nim-lang/Nim/issues/14407 +let + `v0.99` = "0.99" + `v0.99.99` = "0.99.99" From 6be8a66833ee25ad183fa3e4a667e3c0948413d8 Mon Sep 17 00:00:00 2001 From: Smooth Operator Date: Thu, 24 Jun 2021 13:48:57 -0400 Subject: [PATCH 2272/3768] couple tiny typo fixes (#18344) --- lib/pure/times.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index 7bb393aa0..fb48a199c 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -359,7 +359,7 @@ type Timezone* = ref object ## \ ## Timezone interface for supporting `DateTime <#DateTime>`_\s of arbitrary ## timezones. The `times` module only supplies implementations for the - ## systems local time and UTC. + ## system's local time and UTC. zonedTimeFromTimeImpl: proc (x: Time): ZonedTime {.tags: [], raises: [], benign.} zonedTimeFromAdjTimeImpl: proc (x: Time): ZonedTime @@ -1145,7 +1145,7 @@ proc name*(zone: Timezone): string = ## If the timezone doesn't exist in the tz database, or if the timezone ## name is unknown, then any string that describes the timezone ## unambiguously might be used. For example, the string "LOCAL" is used - ## for the systems local timezone. + ## for the system's local timezone. ## ## See also: https://en.wikipedia.org/wiki/Tz_database zone.name From 0d194cdbf90953f28450c4bf1db744d2c1332996 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 25 Jun 2021 06:22:52 +0200 Subject: [PATCH 2273/3768] fixes #18287 (#18346) --- compiler/varpartitions.nim | 48 ++++++++++++++++----------------- tests/arc/topt_no_cursor.nim | 51 +++++++++++++++++++++++++++++++++++- 2 files changed, 74 insertions(+), 25 deletions(-) diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim index 213c3b80e..8f422face 100644 --- a/compiler/varpartitions.nim +++ b/compiler/varpartitions.nim @@ -416,7 +416,6 @@ proc allRoots(n: PNode; result: var seq[(PSym, int)]; level: int) = proc destMightOwn(c: var Partitions; dest: var VarIndex; n: PNode) = ## Analyse if 'n' is an expression that owns the data, if so mark 'dest' ## with 'ownsData'. - if n.typ == nil: return case n.kind of nkEmpty, nkCharLit..nkNilLit: # primitive literals including the empty are harmless: @@ -475,30 +474,31 @@ proc destMightOwn(c: var Partitions; dest: var VarIndex; n: PNode) = destMightOwn(c, dest, n[0]) of nkCallKinds: - if hasDestructor(n.typ): - # calls do construct, what we construct must be destroyed, - # so dest cannot be a cursor: - dest.flags.incl ownsData - elif n.typ.kind in {tyLent, tyVar}: - # we know the result is derived from the first argument: - var roots: seq[(PSym, int)] - allRoots(n[1], roots, RootEscapes) - for r in roots: - connect(c, dest.sym, r[0], n[1].info) + if n.typ != nil: + if hasDestructor(n.typ): + # calls do construct, what we construct must be destroyed, + # so dest cannot be a cursor: + dest.flags.incl ownsData + elif n.typ.kind in {tyLent, tyVar}: + # we know the result is derived from the first argument: + var roots: seq[(PSym, int)] + allRoots(n[1], roots, RootEscapes) + for r in roots: + connect(c, dest.sym, r[0], n[1].info) - else: - let magic = if n[0].kind == nkSym: n[0].sym.magic else: mNone - # this list is subtle, we try to answer the question if after 'dest = f(src)' - # there is a connection betwen 'src' and 'dest' so that mutations to 'src' - # also reflect 'dest': - if magic in {mNone, mMove, mSlice, mAppendStrCh, mAppendStrStr, mAppendSeqElem, mArrToSeq}: - for i in 1.. Date: Fri, 25 Jun 2021 14:12:23 +0200 Subject: [PATCH 2274/3768] fixes #18240 (#18354) * ORC: track escaping parameters properly * fixes #18240 --- compiler/sempass2.nim | 37 +++++++++++++++++++++++++------------ tests/arc/torcmisc.nim | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 12 deletions(-) diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index e66c9596a..e124baf26 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -77,6 +77,7 @@ type config: ConfigRef graph: ModuleGraph c: PContext + escapingParams: IntSet PEffects = var TEffects proc `<`(a, b: TLockLevel): bool {.borrow.} @@ -900,6 +901,25 @@ proc castBlock(tracked: PEffects, pragma: PNode, bc: var PragmaBlockContext) = localError(tracked.config, pragma.info, "invalid pragma block: " & $pragma) +proc trackInnerProc(tracked: PEffects, n: PNode) = + case n.kind + of nkSym: + let s = n.sym + if s.kind == skParam and s.owner == tracked.owner: + tracked.escapingParams.incl s.id + of nkNone..pred(nkSym), succ(nkSym)..nkNilLit: + discard + of nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef, nkLambda, nkFuncDef, nkDo: + if n[0].kind == nkSym and n[0].sym.ast != nil: + trackInnerProc(tracked, getBody(tracked.graph, n[0].sym)) + of nkTypeSection, nkMacroDef, nkTemplateDef, nkError, + nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt, + nkExportStmt, nkPragma, nkCommentStmt, nkBreakState, + nkTypeOfExpr, nkMixinStmt, nkBindStmt: + discard + else: + for ch in n: trackInnerProc(tracked, ch) + proc track(tracked: PEffects, n: PNode) = case n.kind of nkSym: @@ -1095,8 +1115,10 @@ proc track(tracked: PEffects, n: PNode) = track(tracked, n.lastSon) unapplyBlockContext(tracked, bc) - of nkTypeSection, nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef, - nkMacroDef, nkTemplateDef, nkLambda, nkDo, nkFuncDef: + of nkProcDef, nkConverterDef, nkMethodDef, nkIteratorDef, nkLambda, nkFuncDef, nkDo: + if n[0].kind == nkSym and n[0].sym.ast != nil: + trackInnerProc(tracked, getBody(tracked.graph, n[0].sym)) + of nkTypeSection, nkMacroDef, nkTemplateDef: discard of nkCast: if n.len == 2: @@ -1259,15 +1281,6 @@ proc hasRealBody(s: PSym): bool = ## which is not a real implementation, refs #14314 result = {sfForward, sfImportc} * s.flags == {} -proc maybeWrappedInClosure(tracked: PEffects; t: PType): bool {.inline.} = - ## The spec does say when to produce destructors. However, the spec - ## was written in mind with the idea that "lambda lifting" already - ## happened. Not true in our implementation, so we need to workaround - ## here: - result = tracked.isInnerProc and - sfSystemModule notin tracked.c.module.flags and - tfCheckedForDestructor notin t.flags and containsGarbageCollectedRef(t) - proc trackProc*(c: PContext; s: PSym, body: PNode) = let g = c.graph var effects = s.typ.n[0] @@ -1287,7 +1300,7 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) = let typ = param.typ if isSinkTypeForParam(typ) or (t.config.selectedGC in {gcArc, gcOrc} and - (isClosure(typ.skipTypes(abstractInst)) or maybeWrappedInClosure(t, typ))): + (isClosure(typ.skipTypes(abstractInst)) or param.id in t.escapingParams)): createTypeBoundOps(t, typ, param.info) when false: if typ.kind == tyOut and param.id notin t.init: diff --git a/tests/arc/torcmisc.nim b/tests/arc/torcmisc.nim index 20dd18fb3..e41ad7c77 100644 --- a/tests/arc/torcmisc.nim +++ b/tests/arc/torcmisc.nim @@ -32,3 +32,35 @@ when true: waitFor hello(Flags()) echo "success" +# bug #18240 +import tables + +type + TopicHandler* = proc(topic: string, + data: seq[byte]): Future[void] {.gcsafe, raises: [Defect].} + + PeerID* = object + data*: seq[byte] + + PeerInfo* = ref object of RootObj + peerId*: PeerID + + Connection* = ref object of RootObj + peerInfo*: PeerInfo + + PubSubPeer* = ref object of RootObj + codec*: string + + PubSub* = ref object of RootObj + topics*: Table[string, seq[TopicHandler]] + peers*: Table[PeerID, PubSubPeer] + +proc getOrCreatePeer*(myParam: PubSub, peerId: PeerID, protos: seq[string]): PubSubPeer = + myParam.peers.withValue(peerId, peer): + return peer[] + +method handleConn*(myParam: PubSub, + conn: Connection, + proto: string) {.base, async.} = + myParam.peers.withValue(conn.peerInfo.peerId, peer): + let peerB = peer[] From 8535b26a754b2daea46627019e3145d7c12d826e Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 25 Jun 2021 07:47:10 -0700 Subject: [PATCH 2275/3768] docs now show nimExperimentalX APIs (#18345) * docs now show nimExperimentalX APIs * fix for windows --- lib/std/jsfetch.nim | 2 +- lib/wrappers/linenoise/linenoise.nim | 12 ++++++------ tests/config.nims | 3 +++ tools/kochdocs.nim | 3 ++- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/std/jsfetch.nim b/lib/std/jsfetch.nim index 36bc772d5..1e7bb4d29 100644 --- a/lib/std/jsfetch.nim +++ b/lib/std/jsfetch.nim @@ -3,7 +3,7 @@ when not defined(js): {.fatal: "Module jsfetch is designed to be used with the JavaScript backend.".} -when defined(nimExperimentalJsfetch) or defined(nimdoc): +when defined(nimExperimentalJsfetch): import std/[asyncjs, jsheaders, jsformdata] from std/httpcore import HttpMethod from std/jsffi import JsObject diff --git a/lib/wrappers/linenoise/linenoise.nim b/lib/wrappers/linenoise/linenoise.nim index 5b1fa7190..186b3b252 100644 --- a/lib/wrappers/linenoise/linenoise.nim +++ b/lib/wrappers/linenoise/linenoise.nim @@ -32,17 +32,17 @@ proc printKeyCodes*() {.importc: "linenoisePrintKeyCodes".} proc free*(s: cstring) {.importc: "free", header: "".} -when defined nimExperimentalLinenoiseExtra: +when defined(nimExperimentalLinenoiseExtra) and not defined(windows): # C interface - type linenoiseStatus = enum + type LinenoiseStatus = enum linenoiseStatus_ctrl_unknown linenoiseStatus_ctrl_C linenoiseStatus_ctrl_D - type linenoiseData* = object - status: linenoiseStatus + type LinenoiseData* = object + status: LinenoiseStatus - proc linenoiseExtra(prompt: cstring, data: ptr linenoiseData): cstring {.importc.} + proc linenoiseExtra(prompt: cstring, data: ptr LinenoiseData): cstring {.importc.} # stable nim interface type Status* = enum @@ -65,7 +65,7 @@ when defined nimExperimentalLinenoiseExtra: if ret.line.len > 0: echo ret.line if ret.status == lnCtrlD: break echo "exiting" - var data: linenoiseData + var data: LinenoiseData let buf = linenoiseExtra(prompt, data.addr) result.line = $buf free(buf) diff --git a/tests/config.nims b/tests/config.nims index 578b69e76..539de5e8d 100644 --- a/tests/config.nims +++ b/tests/config.nims @@ -30,4 +30,7 @@ hint("Processing", off) # switch("hint", "ConvFromXtoItselfNotNeeded") # experimental API's are enabled in testament, refs https://github.com/timotheecour/Nim/issues/575 +# sync with `kochdocs.docDefines` or refactor. switch("define", "nimExperimentalAsyncjsThen") +switch("define", "nimExperimentalJsfetch") +switch("define", "nimExperimentalLinenoiseExtra") diff --git a/tools/kochdocs.nim b/tools/kochdocs.nim index f25564fad..469059348 100644 --- a/tools/kochdocs.nim +++ b/tools/kochdocs.nim @@ -8,7 +8,8 @@ const gaCode* = " --doc.googleAnalytics:UA-48159761-1" # errormax: subsequent errors are probably consequences of 1st one; a simple # bug could cause unlimited number of errors otherwise, hard to debug in CI. - nimArgs = "--errormax:3 --hint:Conf:off --hint:Path:off --hint:Processing:off --hint:XDeclaredButNotUsed:off --warning:UnusedImport:off -d:boot --putenv:nimversion=$#" % system.NimVersion + docDefines = "-d:nimExperimentalAsyncjsThen -d:nimExperimentalJsfetch -d:nimExperimentalLinenoiseExtra" + nimArgs = "--errormax:3 --hint:Conf:off --hint:Path:off --hint:Processing:off --hint:XDeclaredButNotUsed:off --warning:UnusedImport:off -d:boot --putenv:nimversion=$# $#" % [system.NimVersion, docDefines] gitUrl = "https://github.com/nim-lang/Nim" docHtmlOutput = "doc/html" webUploadOutput = "web/upload" From f6bea08eac7938e460ca9088693f0cf3f274e75b Mon Sep 17 00:00:00 2001 From: tomc1998 Date: Fri, 25 Jun 2021 21:06:24 +0100 Subject: [PATCH 2276/3768] Add sink annotation to option some() (#18358) --- lib/pure/options.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pure/options.nim b/lib/pure/options.nim index bd5aa389e..850bfa555 100644 --- a/lib/pure/options.nim +++ b/lib/pure/options.nim @@ -92,7 +92,7 @@ type UnpackDefect* = object of Defect UnpackError* {.deprecated: "See corresponding Defect".} = UnpackDefect -proc option*[T](val: T): Option[T] {.inline.} = +proc option*[T](val: sink T): Option[T] {.inline.} = ## Can be used to convert a pointer type (`ptr`, `pointer`, `ref` or `proc`) to an option type. ## It converts `nil` to `none(T)`. When `T` is no pointer type, this is equivalent to `some(val)`. ## @@ -112,7 +112,7 @@ proc option*[T](val: T): Option[T] {.inline.} = when T isnot SomePointer: result.has = true -proc some*[T](val: T): Option[T] {.inline.} = +proc some*[T](val: sink T): Option[T] {.inline.} = ## Returns an `Option` that has the value `val`. ## ## **See also:** From 39fbf3c84bd83613407e22b3de215d9a221b9422 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 26 Jun 2021 03:39:52 +0200 Subject: [PATCH 2277/3768] ensure 'koch boot --gc:orc' stays green (#18353) * ensure 'koch boot --gc:orc' stays green * disable for C++ code generator for now --- koch.nim | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/koch.nim b/koch.nim index 51be53cc7..6a0b7a0d3 100644 --- a/koch.nim +++ b/koch.nim @@ -582,6 +582,11 @@ proc runCI(cmd: string) = when defined(posix): execFold("Run nimsuggest tests", "nim r nimsuggest/tester") + when not defined(bsd): + if not doUseCpp: + # the BSDs are overwhelmed already, so only run this test on the other machines: + kochExecFold("Boot Nim ORC", "boot -d:release --gc:orc --lib:lib") + proc testUnixInstall(cmdLineRest: string) = csource("-d:danger" & cmdLineRest) xz(false, cmdLineRest) From b8f761b7e2cb3f28abd6486d28ea19228887cdf5 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 26 Jun 2021 06:21:46 -0700 Subject: [PATCH 2278/3768] even lighter version of #17938: fix most issues with UnusedImport, XDeclaredButNotUsed, etc; fix #17511, #17510, #14246 (without realModule) (#18362) * {.used: symbol} * add tests * fix tests with --import * --import works without giving spurious unused warnings * new warning warnDuplicateModuleImport for `import foo; import foo` * fix test, add resolveModuleAlias, use proper line info for module aliases * fix spurious warnings * fix deprecation msg for deprecated modules even with `import foo as bar` * disable a test for i386 pending sorting XDeclaredButNotUsed errors * UnusedImport now works with re-exported symbols * fix typo [skip ci] * ic support * add genPNode to allow writing PNode-based compiler code similarly to `genAst` * fix DuplicateModuleImport warning * adjust test * fixup * fixup * fixup * fix after rebase * fix for IC * keep the proc inline, move the const out * [skip ci] fix changelog * experiment: remove calls to resolveModuleAlias * followup * fixup * fix tests/modules/tselfimport.nim * workaround tests/deprecated/tmodule1.nim * fix properly * simplify --- compiler/importer.nim | 34 +++++++++-------- compiler/lineinfos.nim | 2 + compiler/lookups.nim | 18 +++++---- compiler/passaux.nim | 2 +- lib/system/schubfach.nim | 59 +++++++++++++++--------------- testament/categories.nim | 1 + tests/deprecated/tmodule1.nim | 22 ++++++++--- tests/modules/tselfimport.nim | 2 +- tests/pragmas/mused2a.nim | 23 ++++++++++++ tests/pragmas/mused2b.nim | 3 ++ tests/pragmas/mused2c.nim | 1 + tests/pragmas/tused2.nim | 46 +++++++++++++++++++++++ tests/statictypes/tstatictypes.nim | 2 +- 13 files changed, 155 insertions(+), 60 deletions(-) create mode 100644 tests/pragmas/mused2a.nim create mode 100644 tests/pragmas/mused2b.nim create mode 100644 tests/pragmas/mused2c.nim create mode 100644 tests/pragmas/tused2.nim diff --git a/compiler/importer.nim b/compiler/importer.nim index 692cdb604..7a5c4de4a 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -13,6 +13,7 @@ import intsets, ast, astalgo, msgs, options, idents, lookups, semdata, modulepaths, sigmatch, lineinfos, sets, modulegraphs, wordrecg +from strutils import `%` proc readExceptSet*(c: PContext, n: PNode): IntSet = assert n.kind in {nkImportExceptStmt, nkExportExceptStmt} @@ -224,19 +225,20 @@ proc importForwarded(c: PContext, n: PNode, exceptSet: IntSet; fromMod: PSym; im proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden: bool): PSym = result = realModule - c.unusedImports.add((realModule, n.info)) template createModuleAliasImpl(ident): untyped = - createModuleAlias(realModule, nextSymId c.idgen, ident, realModule.info, c.config.options) + createModuleAlias(realModule, nextSymId c.idgen, ident, n.info, c.config.options) if n.kind != nkImportAs: discard elif n.len != 2 or n[1].kind != nkIdent: localError(c.config, n.info, "module alias must be an identifier") elif n[1].ident.id != realModule.name.id: # some misguided guy will write 'import abc.foo as foo' ... result = createModuleAliasImpl(n[1].ident) + if result == realModule: + # avoids modifying `realModule`, see D20201209T194412 for `import {.all.}` + result = createModuleAliasImpl(realModule.name) if importHidden: - if result == realModule: # avoids modifying `realModule`, see D20201209T194412. - result = createModuleAliasImpl(realModule.name) result.options.incl optImportHidden + c.unusedImports.add((result, n.info)) proc transformImportAs(c: PContext; n: PNode): tuple[node: PNode, importHidden: bool] = var ret: typeof(result) @@ -274,23 +276,22 @@ proc myImportModule(c: PContext, n: var PNode, importStmtResult: PNode): PSym = toFullPath(c.config, c.graph.importStack[i+1]) c.recursiveDep = err + var realModule: PSym discard pushOptionEntry(c) - result = importModuleAs(c, n, c.graph.importModuleCallback(c.graph, c.module, f), transf.importHidden) + realModule = c.graph.importModuleCallback(c.graph, c.module, f) + result = importModuleAs(c, n, realModule, transf.importHidden) popOptionEntry(c) #echo "set back to ", L c.graph.importStack.setLen(L) # we cannot perform this check reliably because of - # test: modules/import_in_config) - when true: - if result.info.fileIndex == c.module.info.fileIndex and - result.info.fileIndex == n.info.fileIndex: - localError(c.config, n.info, "A module cannot import itself") - if sfDeprecated in result.flags: - if result.constraint != nil: - message(c.config, n.info, warnDeprecated, result.constraint.strVal & "; " & result.name.s & " is deprecated") - else: - message(c.config, n.info, warnDeprecated, result.name.s & " is deprecated") + # test: modules/import_in_config) # xxx is that still true? + if realModule == c.module: + localError(c.config, n.info, "module '$1' cannot import itself" % realModule.name.s) + if sfDeprecated in realModule.flags: + var prefix = "" + if realModule.constraint != nil: prefix = realModule.constraint.strVal & "; " + message(c.config, n.info, warnDeprecated, prefix & realModule.name.s & " is deprecated") suggestSym(c.graph, n.info, result, c.graph.usageSym, false) importStmtResult.add newSymNode(result, n.info) #newStrNode(toFullPath(c.config, f), n.info) @@ -303,6 +304,9 @@ proc impMod(c: PContext; it: PNode; importStmtResult: PNode) = addDecl(c, m, it.info) # add symbol to symbol table of module importAllSymbols(c, m) #importForwarded(c, m.ast, emptySet, m) + for s in allSyms(c.graph, m): # fixes bug #17510, for re-exported symbols + if s.owner != m: + c.exportIndirections.incl((m.id, s.id)) proc evalImport*(c: PContext, n: PNode): PNode = result = newNodeI(nkImportStmt, n.info) diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index abe6bce7c..11fa45480 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -67,6 +67,7 @@ type warnResultUsed = "ResultUsed", warnCannotOpen = "CannotOpen", warnFileChanged = "FileChanged", + warnDuplicateModuleImport = "DuplicateModuleImport", warnUser = "User", # hints hintSuccess = "Success", hintSuccessX = "SuccessX", @@ -148,6 +149,7 @@ const warnResultUsed: "used 'result' variable", warnCannotOpen: "cannot open: $1", warnFileChanged: "file changed: $1", + warnDuplicateModuleImport: "$1", warnUser: "$1", hintSuccess: "operation successful: $#", # keep in sync with `testament.isSuccess` diff --git a/compiler/lookups.nim b/compiler/lookups.nim index ee79673d0..0e057a79a 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -58,8 +58,8 @@ proc considerQuotedIdent*(c: PContext; n: PNode, origin: PNode = nil): PIdent = template addSym*(scope: PScope, s: PSym) = strTableAdd(scope.symbols, s) -proc addUniqueSym*(scope: PScope, s: PSym): PSym = - result = strTableInclReportConflict(scope.symbols, s) +proc addUniqueSym*(scope: PScope, s: PSym, onConflictKeepOld: bool): PSym = + result = strTableInclReportConflict(scope.symbols, s, onConflictKeepOld) proc openScope*(c: PContext): PScope {.discardable.} = result = PScope(parent: c.currentScope, @@ -288,19 +288,23 @@ proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) = message(c.config, s.info, hintXDeclaredButNotUsed, s.name.s) proc wrongRedefinition*(c: PContext; info: TLineInfo, s: string; - conflictsWith: TLineInfo) = + conflictsWith: TLineInfo, note = errGenerated) = ## Emit a redefinition error if in non-interactive mode if c.config.cmd != cmdInteractive: - localError(c.config, info, + localError(c.config, info, note, "redefinition of '$1'; previous declaration here: $2" % [s, c.config $ conflictsWith]) # xxx pending bootstrap >= 1.4, replace all those overloads with a single one: # proc addDecl*(c: PContext, sym: PSym, info = sym.info, scope = c.currentScope) {.inline.} = proc addDeclAt*(c: PContext; scope: PScope, sym: PSym, info: TLineInfo) = - let conflict = scope.addUniqueSym(sym) + let conflict = scope.addUniqueSym(sym, onConflictKeepOld = true) if conflict != nil: - wrongRedefinition(c, info, sym.name.s, conflict.info) + var note = errGenerated + if sym.kind == skModule and conflict.kind == skModule and sym.owner == conflict.owner: + # import foo; import foo + note = warnDuplicateModuleImport + wrongRedefinition(c, info, sym.name.s, conflict.info, note) proc addDeclAt*(c: PContext; scope: PScope, sym: PSym) {.inline.} = addDeclAt(c, scope, sym, sym.info) @@ -312,7 +316,7 @@ proc addDecl*(c: PContext, sym: PSym) {.inline.} = addDeclAt(c, c.currentScope, sym) proc addPrelimDecl*(c: PContext, sym: PSym) = - discard c.currentScope.addUniqueSym(sym) + discard c.currentScope.addUniqueSym(sym, onConflictKeepOld = false) from ic / ic import addHidden diff --git a/compiler/passaux.nim b/compiler/passaux.nim index 9abc97be4..87cb3a730 100644 --- a/compiler/passaux.nim +++ b/compiler/passaux.nim @@ -10,7 +10,7 @@ ## implements some little helper passes import - ast, passes, idents, msgs, options, lineinfos + ast, passes, msgs, options, lineinfos from modulegraphs import ModuleGraph, PPassContext diff --git a/lib/system/schubfach.nim b/lib/system/schubfach.nim index 5f974a79e..d072cde99 100644 --- a/lib/system/schubfach.nim +++ b/lib/system/schubfach.nim @@ -95,6 +95,36 @@ proc floorLog2Pow10(e: int32): int32 {.inline.} = sf_Assert(e <= 1233) return floorDivPow2(e * 1741647, 19) +const + kMin: int32 = -31 + kMax: int32 = 45 + g: array[kMax - kMin + 1, uint64] = [0x81CEB32C4B43FCF5'u64, 0xA2425FF75E14FC32'u64, + 0xCAD2F7F5359A3B3F'u64, 0xFD87B5F28300CA0E'u64, 0x9E74D1B791E07E49'u64, + 0xC612062576589DDB'u64, 0xF79687AED3EEC552'u64, 0x9ABE14CD44753B53'u64, + 0xC16D9A0095928A28'u64, 0xF1C90080BAF72CB2'u64, 0x971DA05074DA7BEF'u64, + 0xBCE5086492111AEB'u64, 0xEC1E4A7DB69561A6'u64, 0x9392EE8E921D5D08'u64, + 0xB877AA3236A4B44A'u64, 0xE69594BEC44DE15C'u64, 0x901D7CF73AB0ACDA'u64, + 0xB424DC35095CD810'u64, 0xE12E13424BB40E14'u64, 0x8CBCCC096F5088CC'u64, + 0xAFEBFF0BCB24AAFF'u64, 0xDBE6FECEBDEDD5BF'u64, 0x89705F4136B4A598'u64, + 0xABCC77118461CEFD'u64, 0xD6BF94D5E57A42BD'u64, 0x8637BD05AF6C69B6'u64, + 0xA7C5AC471B478424'u64, 0xD1B71758E219652C'u64, 0x83126E978D4FDF3C'u64, + 0xA3D70A3D70A3D70B'u64, 0xCCCCCCCCCCCCCCCD'u64, 0x8000000000000000'u64, + 0xA000000000000000'u64, 0xC800000000000000'u64, 0xFA00000000000000'u64, + 0x9C40000000000000'u64, 0xC350000000000000'u64, 0xF424000000000000'u64, + 0x9896800000000000'u64, 0xBEBC200000000000'u64, 0xEE6B280000000000'u64, + 0x9502F90000000000'u64, 0xBA43B74000000000'u64, 0xE8D4A51000000000'u64, + 0x9184E72A00000000'u64, 0xB5E620F480000000'u64, 0xE35FA931A0000000'u64, + 0x8E1BC9BF04000000'u64, 0xB1A2BC2EC5000000'u64, 0xDE0B6B3A76400000'u64, + 0x8AC7230489E80000'u64, 0xAD78EBC5AC620000'u64, 0xD8D726B7177A8000'u64, + 0x878678326EAC9000'u64, 0xA968163F0A57B400'u64, 0xD3C21BCECCEDA100'u64, + 0x84595161401484A0'u64, 0xA56FA5B99019A5C8'u64, 0xCECB8F27F4200F3A'u64, + 0x813F3978F8940985'u64, 0xA18F07D736B90BE6'u64, 0xC9F2C9CD04674EDF'u64, + 0xFC6F7C4045812297'u64, 0x9DC5ADA82B70B59E'u64, 0xC5371912364CE306'u64, + 0xF684DF56C3E01BC7'u64, 0x9A130B963A6C115D'u64, 0xC097CE7BC90715B4'u64, + 0xF0BDC21ABB48DB21'u64, 0x96769950B50D88F5'u64, 0xBC143FA4E250EB32'u64, + 0xEB194F8E1AE525FE'u64, 0x92EFD1B8D0CF37BF'u64, 0xB7ABC627050305AE'u64, + 0xE596B7B0C643C71A'u64, 0x8F7E32CE7BEA5C70'u64, 0xB35DBF821AE4F38C'u64] + proc computePow10Single(k: int32): uint64 {.inline.} = ## There are unique beta and r such that 10^k = beta 2^r and ## 2^63 <= beta < 2^64, namely r = floor(log_2 10^k) - 63 and @@ -103,35 +133,6 @@ proc computePow10Single(k: int32): uint64 {.inline.} = ## value being a pretty good overestimate for 10^k. ## NB: Since for all the required exponents k, we have g < 2^64, ## all constants can be stored in 128-bit integers. - const - kMin: int32 = -31 - kMax: int32 = 45 - g: array[kMax - kMin + 1, uint64] = [0x81CEB32C4B43FCF5'u64, 0xA2425FF75E14FC32'u64, - 0xCAD2F7F5359A3B3F'u64, 0xFD87B5F28300CA0E'u64, 0x9E74D1B791E07E49'u64, - 0xC612062576589DDB'u64, 0xF79687AED3EEC552'u64, 0x9ABE14CD44753B53'u64, - 0xC16D9A0095928A28'u64, 0xF1C90080BAF72CB2'u64, 0x971DA05074DA7BEF'u64, - 0xBCE5086492111AEB'u64, 0xEC1E4A7DB69561A6'u64, 0x9392EE8E921D5D08'u64, - 0xB877AA3236A4B44A'u64, 0xE69594BEC44DE15C'u64, 0x901D7CF73AB0ACDA'u64, - 0xB424DC35095CD810'u64, 0xE12E13424BB40E14'u64, 0x8CBCCC096F5088CC'u64, - 0xAFEBFF0BCB24AAFF'u64, 0xDBE6FECEBDEDD5BF'u64, 0x89705F4136B4A598'u64, - 0xABCC77118461CEFD'u64, 0xD6BF94D5E57A42BD'u64, 0x8637BD05AF6C69B6'u64, - 0xA7C5AC471B478424'u64, 0xD1B71758E219652C'u64, 0x83126E978D4FDF3C'u64, - 0xA3D70A3D70A3D70B'u64, 0xCCCCCCCCCCCCCCCD'u64, 0x8000000000000000'u64, - 0xA000000000000000'u64, 0xC800000000000000'u64, 0xFA00000000000000'u64, - 0x9C40000000000000'u64, 0xC350000000000000'u64, 0xF424000000000000'u64, - 0x9896800000000000'u64, 0xBEBC200000000000'u64, 0xEE6B280000000000'u64, - 0x9502F90000000000'u64, 0xBA43B74000000000'u64, 0xE8D4A51000000000'u64, - 0x9184E72A00000000'u64, 0xB5E620F480000000'u64, 0xE35FA931A0000000'u64, - 0x8E1BC9BF04000000'u64, 0xB1A2BC2EC5000000'u64, 0xDE0B6B3A76400000'u64, - 0x8AC7230489E80000'u64, 0xAD78EBC5AC620000'u64, 0xD8D726B7177A8000'u64, - 0x878678326EAC9000'u64, 0xA968163F0A57B400'u64, 0xD3C21BCECCEDA100'u64, - 0x84595161401484A0'u64, 0xA56FA5B99019A5C8'u64, 0xCECB8F27F4200F3A'u64, - 0x813F3978F8940985'u64, 0xA18F07D736B90BE6'u64, 0xC9F2C9CD04674EDF'u64, - 0xFC6F7C4045812297'u64, 0x9DC5ADA82B70B59E'u64, 0xC5371912364CE306'u64, - 0xF684DF56C3E01BC7'u64, 0x9A130B963A6C115D'u64, 0xC097CE7BC90715B4'u64, - 0xF0BDC21ABB48DB21'u64, 0x96769950B50D88F5'u64, 0xBC143FA4E250EB32'u64, - 0xEB194F8E1AE525FE'u64, 0x92EFD1B8D0CF37BF'u64, 0xB7ABC627050305AE'u64, - 0xE596B7B0C643C71A'u64, 0x8F7E32CE7BEA5C70'u64, 0xB35DBF821AE4F38C'u64] sf_Assert(k >= kMin) sf_Assert(k <= kMax) return g[k - kMin] diff --git a/testament/categories.nim b/testament/categories.nim index bf7855b2c..b5e8a5454 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -508,6 +508,7 @@ proc icTests(r: var TResults; testsDir: string, cat: Category, options: string; const tempExt = "_temp.nim" for it in walkDirRec(testsDir): + # for it in ["tests/ic/timports.nim"]: # debugging: to try a specific test if isTestFile(it) and not it.endsWith(tempExt): let nimcache = nimcacheDir(it, options, getTestSpecTarget()) removeDir(nimcache) diff --git a/tests/deprecated/tmodule1.nim b/tests/deprecated/tmodule1.nim index 954836889..f26e4ce3f 100644 --- a/tests/deprecated/tmodule1.nim +++ b/tests/deprecated/tmodule1.nim @@ -1,13 +1,23 @@ discard """ - nimout: '''tmodule1.nim(11, 8) Warning: goodbye; importme is deprecated [Deprecated] -tmodule1.nim(14, 10) Warning: Ty is deprecated [Deprecated] -tmodule1.nim(17, 10) Warning: hello; Ty1 is deprecated [Deprecated] -tmodule1.nim(20, 8) Warning: aVar is deprecated [Deprecated] -tmodule1.nim(22, 3) Warning: aProc is deprecated [Deprecated] -tmodule1.nim(23, 3) Warning: hello; aProc1 is deprecated [Deprecated] + matrix: "--hint:all:off" + nimoutFull: true + nimout: ''' +tmodule1.nim(21, 8) Warning: goodbye; importme is deprecated [Deprecated] +tmodule1.nim(24, 10) Warning: Ty is deprecated [Deprecated] +tmodule1.nim(27, 10) Warning: hello; Ty1 is deprecated [Deprecated] +tmodule1.nim(30, 8) Warning: aVar is deprecated [Deprecated] +tmodule1.nim(32, 3) Warning: aProc is deprecated [Deprecated] +tmodule1.nim(33, 3) Warning: hello; aProc1 is deprecated [Deprecated] ''' """ + + + + + + +# line 20 import importme block: diff --git a/tests/modules/tselfimport.nim b/tests/modules/tselfimport.nim index 7e50bef7c..ba5d9b4cf 100644 --- a/tests/modules/tselfimport.nim +++ b/tests/modules/tselfimport.nim @@ -1,5 +1,5 @@ discard """ - errormsg: "A module cannot import itself" + errormsg: "module 'tselfimport' cannot import itself" file: "tselfimport.nim" line: 7 """ diff --git a/tests/pragmas/mused2a.nim b/tests/pragmas/mused2a.nim new file mode 100644 index 000000000..d9b2bb9bf --- /dev/null +++ b/tests/pragmas/mused2a.nim @@ -0,0 +1,23 @@ +import std/strutils + +from std/os import fileExists + +import std/typetraits as typetraits2 +from std/setutils import complement + + + + + +proc fn1() = discard +proc fn2*() = discard + + +let fn4 = 0 +let fn5* = 0 + + +const fn7 = 0 +const fn8* = 0 + +type T1 = object diff --git a/tests/pragmas/mused2b.nim b/tests/pragmas/mused2b.nim new file mode 100644 index 000000000..39c92b964 --- /dev/null +++ b/tests/pragmas/mused2b.nim @@ -0,0 +1,3 @@ +import mused2c +export mused2c + diff --git a/tests/pragmas/mused2c.nim b/tests/pragmas/mused2c.nim new file mode 100644 index 000000000..a374e634e --- /dev/null +++ b/tests/pragmas/mused2c.nim @@ -0,0 +1 @@ +proc baz*() = discard \ No newline at end of file diff --git a/tests/pragmas/tused2.nim b/tests/pragmas/tused2.nim new file mode 100644 index 000000000..f80c198d8 --- /dev/null +++ b/tests/pragmas/tused2.nim @@ -0,0 +1,46 @@ +discard """ + matrix: "--hint:all:off --hint:XDeclaredButNotUsed --path:." + joinable: false + nimoutFull: true + nimout: ''' +mused2a.nim(12, 6) Hint: 'fn1' is declared but not used [XDeclaredButNotUsed] +mused2a.nim(16, 5) Hint: 'fn4' is declared but not used [XDeclaredButNotUsed] +mused2a.nim(20, 7) Hint: 'fn7' is declared but not used [XDeclaredButNotUsed] +mused2a.nim(23, 6) Hint: 'T1' is declared but not used [XDeclaredButNotUsed] +mused2a.nim(1, 11) Warning: imported and not used: 'strutils' [UnusedImport] +mused2a.nim(3, 9) Warning: imported and not used: 'os' [UnusedImport] +mused2a.nim(5, 23) Warning: imported and not used: 'typetraits2' [UnusedImport] +mused2a.nim(6, 9) Warning: imported and not used: 'setutils' [UnusedImport] +tused2.nim(42, 8) Warning: imported and not used: 'mused2a' [UnusedImport] +tused2.nim(45, 11) Warning: imported and not used: 'strutils' [UnusedImport] +''' +""" + + + + + + + + + + + + + + + + + + + + + + +# line 40 + +import mused2a +import mused2b + +import std/strutils +baz() diff --git a/tests/statictypes/tstatictypes.nim b/tests/statictypes/tstatictypes.nim index 16cb55c28..5df3f35fd 100644 --- a/tests/statictypes/tstatictypes.nim +++ b/tests/statictypes/tstatictypes.nim @@ -271,7 +271,7 @@ block: fails(foo) -import macros, tables +import tables var foo{.compileTime.} = [ "Foo", From 1b9b8060075efab82912dc33ba64e671d102b999 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Sat, 26 Jun 2021 19:10:46 +0300 Subject: [PATCH 2279/3768] rst: fix bug 20 from #17340 (#18360) and a leftover bug: priority of option list inside definition list --- lib/packages/docutils/rst.nim | 4 ++- tests/stdlib/trst.nim | 47 +++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 8ffbbd4d3..419cf50e7 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -2245,7 +2245,7 @@ proc parseOptionList(p: var RstParser): PRstNode = popInd(p) else: parseLine(p, b) - if currentTok(p).kind == tkIndent: inc p.idx + while currentTok(p).kind == tkIndent: inc p.idx c.add(a) c.add(b) c.order = order; inc order @@ -2262,6 +2262,8 @@ proc parseDefinitionList(p: var RstParser): PRstNode = var col = currentTok(p).col result = newRstNodeA(p, rnDefList) while true: + if isOptionList(p): + break # option list has priority over def.list j = p.idx var a = newRstNode(rnDefName) parseLine(p, a) diff --git a/tests/stdlib/trst.nim b/tests/stdlib/trst.nim index d53092d3b..fb9552479 100644 --- a/tests/stdlib/trst.nim +++ b/tests/stdlib/trst.nim @@ -83,6 +83,53 @@ suite "RST parsing": rnLeaf 'set' """) + test "items of 1 option list can be separated by blank lines": + check(dedent""" + -a desc1 + + -b desc2 + """.toAst == + dedent""" + rnOptionList + rnOptionListItem order=1 + rnOptionGroup + rnLeaf '-' + rnLeaf 'a' + rnDescription + rnLeaf 'desc1' + rnOptionListItem order=2 + rnOptionGroup + rnLeaf '-' + rnLeaf 'b' + rnDescription + rnLeaf 'desc2' + """) + + test "option list has priority over definition list": + check(dedent""" + defName + defBody + + -b desc2 + """.toAst == + dedent""" + rnInner + rnDefList + rnDefItem + rnDefName + rnLeaf 'defName' + rnDefBody + rnInner + rnLeaf 'defBody' + rnOptionList + rnOptionListItem order=1 + rnOptionGroup + rnLeaf '-' + rnLeaf 'b' + rnDescription + rnLeaf 'desc2' + """) + test "RST comment": check(dedent""" .. comment1 From 0b7361e938134305d6893ee2876b5fc8f9ba419b Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 27 Jun 2021 11:39:16 -0700 Subject: [PATCH 2280/3768] followup #18362: make `UnusedImport` work robustly (#18366) * warnDuplicateModuleImport => hintDuplicateModuleImport * improve DuplicateModuleImport msg, add test --- compiler/cgen.nim | 2 - compiler/dfa.nim | 2 +- compiler/docgen.nim | 2 +- compiler/importer.nim | 16 +++++-- compiler/injectdestructors.nim | 6 +-- compiler/jsgen.nim | 2 - compiler/lineinfos.nim | 6 +-- compiler/lookups.nim | 12 ++++-- compiler/semdata.nim | 3 +- compiler/suggest.nim | 5 ++- tests/misc/trunner.nim | 26 +++++++++++- tests/pragmas/mused3.nim | 76 ++++++++++++++++++++++++++++++++++ tests/pragmas/mused3a.nim | 41 ++++++++++++++++++ tests/pragmas/mused3b.nim | 12 ++++++ 14 files changed, 186 insertions(+), 25 deletions(-) create mode 100644 tests/pragmas/mused3.nim create mode 100644 tests/pragmas/mused3a.nim create mode 100644 tests/pragmas/mused3b.nim diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 5d6de2a03..3b004d399 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -24,8 +24,6 @@ import strutils except `%` # collides with ropes.`%` from ic / ic import ModuleBackendFlag from modulegraphs import ModuleGraph, PPassContext -from lineinfos import - warnGcMem, errXMustBeCompileTime, hintDependency, errGenerated, errCannotOpenFile import dynlib when not declared(dynlib.libCandidates): diff --git a/compiler/dfa.nim b/compiler/dfa.nim index ea60dbc59..402945042 100644 --- a/compiler/dfa.nim +++ b/compiler/dfa.nim @@ -29,7 +29,7 @@ ## "A Graph–Free Approach to Data–Flow Analysis" by Markus Mohnen. ## https://link.springer.com/content/pdf/10.1007/3-540-45937-5_6.pdf -import ast, types, intsets, lineinfos, renderer +import ast, intsets, lineinfos, renderer import std/private/asciitables type diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 08311eade..e6bb7cef8 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -16,7 +16,7 @@ import packages/docutils/rst, packages/docutils/rstgen, json, xmltree, trees, types, typesrenderer, astalgo, lineinfos, intsets, - pathutils, trees, tables, nimpaths, renderverbatim, osproc + pathutils, tables, nimpaths, renderverbatim, osproc from uri import encodeUrl from std/private/globs import nativeToUnixPath diff --git a/compiler/importer.nim b/compiler/importer.nim index 7a5c4de4a..af392f849 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -12,7 +12,7 @@ import intsets, ast, astalgo, msgs, options, idents, lookups, semdata, modulepaths, sigmatch, lineinfos, sets, - modulegraphs, wordrecg + modulegraphs, wordrecg, tables from strutils import `%` proc readExceptSet*(c: PContext, n: PNode): IntSet = @@ -239,6 +239,7 @@ proc importModuleAs(c: PContext; n: PNode, realModule: PSym, importHidden: bool) if importHidden: result.options.incl optImportHidden c.unusedImports.add((result, n.info)) + c.importModuleMap[result.id] = realModule.id proc transformImportAs(c: PContext; n: PNode): tuple[node: PNode, importHidden: bool] = var ret: typeof(result) @@ -296,6 +297,13 @@ proc myImportModule(c: PContext, n: var PNode, importStmtResult: PNode): PSym = importStmtResult.add newSymNode(result, n.info) #newStrNode(toFullPath(c.config, f), n.info) +proc afterImport(c: PContext, m: PSym) = + # fixes bug #17510, for re-exported symbols + let realModuleId = c.importModuleMap[m.id] + for s in allSyms(c.graph, m): + if s.owner.id != realModuleId: + c.exportIndirections.incl((m.id, s.id)) + proc impMod(c: PContext; it: PNode; importStmtResult: PNode) = var it = it let m = myImportModule(c, it, importStmtResult) @@ -304,9 +312,7 @@ proc impMod(c: PContext; it: PNode; importStmtResult: PNode) = addDecl(c, m, it.info) # add symbol to symbol table of module importAllSymbols(c, m) #importForwarded(c, m.ast, emptySet, m) - for s in allSyms(c.graph, m): # fixes bug #17510, for re-exported symbols - if s.owner != m: - c.exportIndirections.incl((m.id, s.id)) + afterImport(c, m) proc evalImport*(c: PContext, n: PNode): PNode = result = newNodeI(nkImportStmt, n.info) @@ -345,6 +351,7 @@ proc evalFrom*(c: PContext, n: PNode): PNode = if n[i].kind != nkNilLit: importSymbol(c, n[i], m, im.imported) c.addImport im + afterImport(c, m) proc evalImportExcept*(c: PContext, n: PNode): PNode = result = newNodeI(nkImportStmt, n.info) @@ -355,3 +362,4 @@ proc evalImportExcept*(c: PContext, n: PNode): PNode = addDecl(c, m, n.info) # add symbol to symbol table of module importAllSymbolsExcept(c, m, readExceptSet(c, n)) #importForwarded(c, m.ast, exceptSet, m) + afterImport(c, m) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 8ef08adeb..6447557c5 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -15,7 +15,7 @@ import intsets, strtabs, ast, astalgo, msgs, renderer, magicsys, types, idents, - strutils, options, dfa, lowerings, tables, modulegraphs, msgs, + strutils, options, dfa, lowerings, tables, modulegraphs, lineinfos, parampatterns, sighashes, liftdestructors, optimizer, varpartitions @@ -73,7 +73,7 @@ proc nestedScope(parent: var Scope): Scope = proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope; isDecl = false): PNode -import sets, hashes, tables +import sets, hashes proc hash(n: PNode): Hash = hash(cast[pointer](n)) @@ -245,8 +245,6 @@ template isUnpackedTuple(n: PNode): bool = ## hence unpacked tuples themselves don't need to be destroyed (n.kind == nkSym and n.sym.kind == skTemp and n.sym.typ.kind == tyTuple) -from strutils import parseInt - proc checkForErrorPragma(c: Con; t: PType; ri: PNode; opname: string) = var m = "'" & opname & "' is not available for type <" & typeToString(t) & ">" if (opname == "=" or opname == "=copy") and ri != nil: diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 3b183b9d6..393ac83c2 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -37,8 +37,6 @@ import import json, sets, math, tables, intsets, strutils -from modulegraphs import ModuleGraph, PPassContext - type TJSGen = object of PPassContext module: PSym diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index 11fa45480..131fe480b 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -67,12 +67,12 @@ type warnResultUsed = "ResultUsed", warnCannotOpen = "CannotOpen", warnFileChanged = "FileChanged", - warnDuplicateModuleImport = "DuplicateModuleImport", warnUser = "User", # hints hintSuccess = "Success", hintSuccessX = "SuccessX", hintCC = "CC", - hintLineTooLong = "LineTooLong", hintXDeclaredButNotUsed = "XDeclaredButNotUsed", + hintLineTooLong = "LineTooLong", + hintXDeclaredButNotUsed = "XDeclaredButNotUsed", hintDuplicateModuleImport = "DuplicateModuleImport", hintXCannotRaiseY = "XCannotRaiseY", hintConvToBaseNotNeeded = "ConvToBaseNotNeeded", hintConvFromXtoItselfNotNeeded = "ConvFromXtoItselfNotNeeded", hintExprAlwaysX = "ExprAlwaysX", hintQuitCalled = "QuitCalled", hintProcessing = "Processing", hintCodeBegin = "CodeBegin", @@ -149,7 +149,6 @@ const warnResultUsed: "used 'result' variable", warnCannotOpen: "cannot open: $1", warnFileChanged: "file changed: $1", - warnDuplicateModuleImport: "$1", warnUser: "$1", hintSuccess: "operation successful: $#", # keep in sync with `testament.isSuccess` @@ -157,6 +156,7 @@ const hintCC: "CC: $1", hintLineTooLong: "line too long", hintXDeclaredButNotUsed: "'$1' is declared but not used", + hintDuplicateModuleImport: "$1", hintXCannotRaiseY: "$1", hintConvToBaseNotNeeded: "conversion to base object is not needed", hintConvFromXtoItselfNotNeeded: "conversion from $1 to itself is pointless", diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 0e057a79a..9b878dd4b 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -300,11 +300,15 @@ proc wrongRedefinition*(c: PContext; info: TLineInfo, s: string; proc addDeclAt*(c: PContext; scope: PScope, sym: PSym, info: TLineInfo) = let conflict = scope.addUniqueSym(sym, onConflictKeepOld = true) if conflict != nil: - var note = errGenerated if sym.kind == skModule and conflict.kind == skModule and sym.owner == conflict.owner: - # import foo; import foo - note = warnDuplicateModuleImport - wrongRedefinition(c, info, sym.name.s, conflict.info, note) + # e.g.: import foo; import foo + # xxx we could refine this by issuing a different hint for the case + # where a duplicate import happens inside an include. + localError(c.config, info, hintDuplicateModuleImport, + "duplicate import of '$1'; previous import here: $2" % + [sym.name.s, c.config $ conflict.info]) + else: + wrongRedefinition(c, info, sym.name.s, conflict.info, errGenerated) proc addDeclAt*(c: PContext; scope: PScope, sym: PSym) {.inline.} = addDeclAt(c, scope, sym, sym.info) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index f763a00e4..bd863505c 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -156,7 +156,8 @@ type features*: set[Feature] inTypeContext*, inConceptDecl*: int unusedImports*: seq[(PSym, TLineInfo)] - exportIndirections*: HashSet[(int, int)] + exportIndirections*: HashSet[(int, int)] # (module.id, symbol.id) + importModuleMap*: Table[int, int] # (module.id, module.id) lastTLineInfo*: TLineInfo template config*(c: PContext): ConfigRef = c.graph.config diff --git a/compiler/suggest.nim b/compiler/suggest.nim index a5b4ac87d..eaa30040a 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -32,7 +32,7 @@ # included from sigmatch.nim -import algorithm, sets, prefixmatches, lineinfos, parseutils, linter +import algorithm, sets, prefixmatches, lineinfos, parseutils, linter, tables from wordrecg import wDeprecated, wError, wAddr, wYield when defined(nimsuggest): @@ -572,7 +572,8 @@ proc markOwnerModuleAsUsed(c: PContext; s: PSym) = var i = 0 while i <= high(c.unusedImports): let candidate = c.unusedImports[i][0] - if candidate == module or c.exportIndirections.contains((candidate.id, s.id)): + if candidate == module or c.importModuleMap.getOrDefault(candidate.id, int.low) == module.id or + c.exportIndirections.contains((candidate.id, s.id)): # mark it as used: c.unusedImports.del(i) else: diff --git a/tests/misc/trunner.nim b/tests/misc/trunner.nim index 77ae10f3d..8426e9aee 100644 --- a/tests/misc/trunner.nim +++ b/tests/misc/trunner.nim @@ -31,7 +31,7 @@ const proc runNimCmd(file, options = "", rtarg = ""): auto = let fileabs = testsDir / file.unixToNativePath # doAssert fileabs.fileExists, fileabs # disabled because this allows passing `nim r --eval:code fakefile` - let cmd = fmt"{nim} {mode} {options} --hints:off {fileabs} {rtarg}" + let cmd = fmt"{nim} {mode} --hint:all:off {options} {fileabs} {rtarg}" result = execCmdEx(cmd) when false: # for debugging echo cmd @@ -352,5 +352,29 @@ running: v2 doAssert outp2 == "12345\n", outp2 doAssert status2 == 0 + block: # UnusedImport + proc fn(opt: string, expected: string) = + let output = runNimCmdChk("pragmas/mused3.nim", fmt"--warning:all:off --warning:UnusedImport --hint:DuplicateModuleImport {opt}") + doAssert output == expected, opt & "\noutput:\n" & output & "expected:\n" & expected + fn("-d:case1"): """ +mused3.nim(13, 8) Warning: imported and not used: 'mused3b' [UnusedImport] +""" + fn("-d:case2"): "" + fn("-d:case3"): "" + fn("-d:case4"): "" + fn("-d:case5"): "" + fn("-d:case6"): "" + fn("-d:case7"): "" + fn("-d:case8"): "" + fn("-d:case9"): "" + fn("-d:case10"): "" + when false: + fn("-d:case11"): """ + Warning: imported and not used: 'm2' [UnusedImport] + """ + fn("-d:case12"): """ +mused3.nim(75, 10) Hint: duplicate import of 'mused3a'; previous import here: mused3.nim(74, 10) [DuplicateModuleImport] +""" + else: discard # only during debugging, tests added here will run with `-d:nimTestsTrunnerDebugging` enabled diff --git a/tests/pragmas/mused3.nim b/tests/pragmas/mused3.nim new file mode 100644 index 000000000..0beec1d44 --- /dev/null +++ b/tests/pragmas/mused3.nim @@ -0,0 +1,76 @@ +#[ +ran from trunner +]# + + + + + + +# line 10 +when defined case1: + from mused3a import nil + from mused3b import nil + mused3a.fn1() + +when defined case2: + from mused3a as m1 import nil + m1.fn1() + +when defined case3: + from mused3a import fn1 + fn1() + +when defined case4: + from mused3a as m1 import fn1 + m1.fn1() + +when defined case5: + import mused3a as m1 + fn1() + +when defined case6: + import mused3a except nonexistent + fn1() + +when defined case7: + import mused3a + mused3a.fn1() + +when defined case8: + # re-export test + import mused3a except nonexistent + gn1() + +when defined case9: + # re-export test + import mused3a + gn1() + +when defined case10: + #[ + edge case which happens a lot in compiler code: + don't report UnusedImport for mused3b here even though it works without `import mused3b`, + because `a.b0.f0` is accessible from both mused3a and mused3b (fields are given implicit access) + ]# + import mused3a + import mused3b + var a: Bar + discard a.b0.f0 + +when false: + when defined case11: + #[ + xxx minor bug: this should give: + Warning: imported and not used: 'm2' [UnusedImport] + but doesn't, because currently implementation in `markOwnerModuleAsUsed` + only looks at `fn1`, not fully qualified call `m1.fn1() + ]# + from mused3a as m1 import nil + from mused3a as m2 import nil + m1.fn1() + +when defined case12: + import mused3a + import mused3a + fn1() diff --git a/tests/pragmas/mused3a.nim b/tests/pragmas/mused3a.nim new file mode 100644 index 000000000..c33d1ecb3 --- /dev/null +++ b/tests/pragmas/mused3a.nim @@ -0,0 +1,41 @@ +when defined case1: + proc fn1*() = discard + +when defined case2: + proc fn1*() = discard + +when defined case3: + proc fn1*() = discard + proc fn2*() = discard + +when defined case4: + proc fn1*() = discard + proc fn2*() = discard + +when defined case5: + proc fn1*() = discard + +when defined case6: + proc fn1*() = discard + +when defined case7: + proc fn1*() = discard + +when defined case8: + import mused3b + export mused3b + +when defined case9: + import mused3b + export mused3b + +when defined case10: + import mused3b + type Bar* = object + b0*: Foo + +when defined case11: + proc fn1*() = discard + +when defined case12: + proc fn1*() = discard diff --git a/tests/pragmas/mused3b.nim b/tests/pragmas/mused3b.nim new file mode 100644 index 000000000..de288bb08 --- /dev/null +++ b/tests/pragmas/mused3b.nim @@ -0,0 +1,12 @@ +when defined case1: + proc gn1*()=discard + +when defined case8: + proc gn1*()=discard + +when defined case9: + proc gn1*()=discard + +when defined case10: + type Foo* = object + f0*: int From 0804b4b37d5fcc43d975f56a4c2c88dd58a0b882 Mon Sep 17 00:00:00 2001 From: Federico Ceratto Date: Sun, 27 Jun 2021 19:51:28 +0100 Subject: [PATCH 2281/3768] Switch packaging.rst to use makefile Minor cleanup --- doc/packaging.rst | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/doc/packaging.rst b/doc/packaging.rst index ecb17e482..fcdc4ae04 100644 --- a/doc/packaging.rst +++ b/doc/packaging.rst @@ -48,14 +48,15 @@ Hints on the build process: .. code:: cmd # build from C sources and then using koch - ./build.sh --os $os_type --cpu $cpu_arch - ./bin/nim c koch + make -j # supports parallel build + # alternatively: ./build.sh --os $os_type --cpu $cpu_arch + ./bin/nim c -d:release koch ./koch boot -d:release # optionally generate docs into doc/html ./koch docs - ./koch tools -d:release + ./koch tools # extract files to be really installed ./install.sh From 908b2cc2e44bededdcb943778f44b908a4f3c1df Mon Sep 17 00:00:00 2001 From: Federico Ceratto Date: Sun, 27 Jun 2021 21:12:12 +0100 Subject: [PATCH 2282/3768] Switch IRC links to Libera Chat (#18370) --- readme.md | 4 ++-- tools/website.nimf | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/readme.md b/readme.md index 37babf711..7c6e28be3 100644 --- a/readme.md +++ b/readme.md @@ -16,7 +16,7 @@ the latest release, check out [Nim's website][nim-site] or [bleeding edge docs]( [![Follow @nim_lang on Twitter][badge-nim-twitter]][nim-twitter] * The [forum][nim-forum] - the best place to ask questions and to discuss Nim. -* [#nim IRC Channel (Freenode)][nim-irc] - a place to discuss Nim in real-time. +* [#nim IRC Channel (Libera Chat)][nim-irc] - a place to discuss Nim in real-time. Also where most development decisions get made. * [Discord][nim-discord] - an additional place to discuss Nim in real-time. Most channels there are bridged to IRC. @@ -206,7 +206,7 @@ Copyright © 2006-2021 Andreas Rumpf, all rights reserved. [nim-forum]: https://forum.nim-lang.org [nim-issues]: https://github.com/nim-lang/Nim/issues [nim-issues-easy]: https://github.com/nim-lang/Nim/labels/Easy -[nim-irc]: https://webchat.freenode.net/?channels=nim +[nim-irc]: https://web.libera.chat/#nim [nim-twitter]: https://twitter.com/nim_lang [nim-stackoverflow]: https://stackoverflow.com/questions/tagged/nim-lang [nim-stackoverflow-newest]: https://stackoverflow.com/questions/tagged/nim-lang?sort=newest&pageSize=15 diff --git a/tools/website.nimf b/tools/website.nimf index bde84b32f..cea30f74f 100644 --- a/tools/website.nimf +++ b/tools/website.nimf @@ -188,7 +188,7 @@ runForever()
    From e720bbdd76303e2cc0a3a169e870859470a1da84 Mon Sep 17 00:00:00 2001 From: gemath <33119724+gemath@users.noreply.github.com> Date: Mon, 28 Jun 2021 12:33:20 +0200 Subject: [PATCH 2283/3768] Peg captures get stack-like behavior (#18369) * Implements reverse capture indexing. * Now works for modified backrefs too. * Changed reverse indexing syntax prefix for back-references to '$^'. --- doc/pegdocs.txt | 10 +++-- lib/pure/pegs.nim | 90 +++++++++++++++++++++++++++--------------- tests/stdlib/tpegs.nim | 28 +++++++++++++ 3 files changed, 94 insertions(+), 34 deletions(-) diff --git a/doc/pegdocs.txt b/doc/pegdocs.txt index 4c557aed8..0363d4874 100644 --- a/doc/pegdocs.txt +++ b/doc/pegdocs.txt @@ -27,7 +27,10 @@ notation meaning ``{E}`` Capture: Apply expression `E` and store the substring that matched `E` into a *capture* that can be accessed after the matching process. -``$i`` Back reference to the ``i``th capture. ``i`` counts from 1. +``{}`` Empty capture: Delete the last capture. No character + is consumed. +``$i`` Back reference to the ``i``th capture. ``i`` counts forwards + from 1 or backwards (last capture to first) from ^1. ``$`` Anchor: Matches at the end of the input. No character is consumed. Same as ``!.``. ``^`` Anchor: Matches at the start of the input. No character @@ -149,14 +152,15 @@ The PEG parser implements this grammar (written in PEG syntax):: rule <- identifier \s* "<-" expr ig identNoArrow <- identifier !(\s* "<-") prefixOpr <- ig '&' / ig '!' / ig '@' / ig '{@}' / ig '@@' - literal <- ig identifier? '$' [0-9]+ / '$' / '^' / + literal <- ig identifier? '$' '^'? [0-9]+ / '$' / '^' / ig identNoArrow / ig charset / ig stringlit / ig builtin / ig '.' / ig '_' / - (ig "(" expr ig ")") + (ig "(" expr ig ")") / + (ig "{" expr? ig "}") postfixOpr <- ig '?' / ig '*' / ig '+' primary <- prefixOpr* (literal postfixOpr*) diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim index b5d9b1d07..b6b05cdc1 100644 --- a/lib/pure/pegs.nim +++ b/lib/pure/pegs.nim @@ -83,7 +83,7 @@ type of pkChar, pkGreedyRepChar: ch: char of pkCharChoice, pkGreedyRepSet: charChoice: ref set[char] of pkNonTerminal: nt: NonTerminal - of pkBackRef..pkBackRefIgnoreStyle: index: range[0..MaxSubpatterns] + of pkBackRef..pkBackRefIgnoreStyle: index: range[-MaxSubpatterns..MaxSubpatterns-1] else: sons: seq[Peg] NonTerminal* = ref NonTerminalObj @@ -106,7 +106,7 @@ proc nt*(p: Peg): NonTerminal = p.nt ## Returns the *NonTerminal* object of a given *Peg* variant object ## where present. -proc index*(p: Peg): range[0..MaxSubpatterns] = p.index +proc index*(p: Peg): range[-MaxSubpatterns..MaxSubpatterns-1] = p.index ## Returns the back-reference index of a captured sub-pattern in the ## *Captures* object for a given *Peg* variant object where present. @@ -304,34 +304,37 @@ proc endAnchor*: Peg {.inline.} = ## constructs the PEG ``$`` which matches the end of the input. result = !any() -proc capture*(a: Peg): Peg {.noSideEffect, rtl, extern: "npegsCapture".} = +proc capture*(a: Peg = Peg(kind: pkEmpty)): Peg {.noSideEffect, rtl, extern: "npegsCapture".} = ## constructs a capture with the PEG `a` result = Peg(kind: pkCapture, sons: @[a]) -proc backref*(index: range[1..MaxSubpatterns]): Peg {. +proc backref*(index: range[1..MaxSubpatterns], reverse: bool = false): Peg {. noSideEffect, rtl, extern: "npegs$1".} = ## constructs a back reference of the given `index`. `index` starts counting - ## from 1. - result = Peg(kind: pkBackRef, index: index-1) + ## from 1. `reverse` specifies wether indexing starts from the end of the + ## capture list. + result = Peg(kind: pkBackRef, index: (if reverse: -index else: index - 1)) -proc backrefIgnoreCase*(index: range[1..MaxSubpatterns]): Peg {. +proc backrefIgnoreCase*(index: range[1..MaxSubpatterns], reverse: bool = false): Peg {. noSideEffect, rtl, extern: "npegs$1".} = ## constructs a back reference of the given `index`. `index` starts counting - ## from 1. Ignores case for matching. - result = Peg(kind: pkBackRefIgnoreCase, index: index-1) + ## from 1. `reverse` specifies wether indexing starts from the end of the + ## capture list. Ignores case for matching. + result = Peg(kind: pkBackRefIgnoreCase, index: (if reverse: -index else: index - 1)) -proc backrefIgnoreStyle*(index: range[1..MaxSubpatterns]): Peg {. +proc backrefIgnoreStyle*(index: range[1..MaxSubpatterns], reverse: bool = false): Peg {. noSideEffect, rtl, extern: "npegs$1".} = ## constructs a back reference of the given `index`. `index` starts counting - ## from 1. Ignores style for matching. - result = Peg(kind: pkBackRefIgnoreStyle, index: index-1) + ## from 1. `reverse` specifies wether indexing starts from the end of the + ## capture list. Ignores style for matching. + result = Peg(kind: pkBackRefIgnoreStyle, index: (if reverse: -index else: index - 1)) proc spaceCost(n: Peg): int = case n.kind of pkEmpty: discard of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar, pkGreedyRepChar, pkCharChoice, pkGreedyRepSet, - pkAny..pkWhitespace, pkGreedyAny: + pkAny..pkWhitespace, pkGreedyAny, pkBackRef..pkBackRefIgnoreStyle: result = 1 of pkNonTerminal: # we cannot inline a rule with a non-terminal @@ -561,8 +564,10 @@ template matchOrParse(mopProc: untyped) = # Parse handler code must run in an *of* clause of its own for each # *PegKind*, so we encapsulate the identical clause body for # *pkBackRef..pkBackRefIgnoreStyle* here. - if p.index >= c.ml: return -1 - var (a, b) = c.matches[p.index] + var index = p.index + if index < 0: index.inc(c.ml) + if index < 0 or index >= c.ml: return -1 + var (a, b) = c.matches[index] var n: Peg case p.kind of pkBackRef: @@ -822,15 +827,19 @@ template matchOrParse(mopProc: untyped) = leave(pkNotPredicate, s, p, start, result) of pkCapture: enter(pkCapture, s, p, start) - var idx = c.ml # reserve a slot for the subpattern - inc(c.ml) - result = mopProc(s, p.sons[0], start, c) - if result >= 0: - if idx < MaxSubpatterns: - c.matches[idx] = (start, start+result-1) - #else: silently ignore the capture + if p.sons.len == 0 or p.sons[0].kind == pkEmpty: + # empty capture removes last match + dec(c.ml) + c.matches[c.ml] = (0, 0) + result = 0 # match of length 0 else: - c.ml = idx + var idx = c.ml # reserve a slot for the subpattern + result = mopProc(s, p.sons[0], start, c) + if result >= 0: + inc(c.ml) + if idx < MaxSubpatterns: + c.matches[idx] = (start, start+result-1) + #else: silently ignore the capture leave(pkCapture, s, p, start, result) of pkBackRef: enter(pkBackRef, s, p, start) @@ -1395,6 +1404,7 @@ type tkCurlyLe, ## '{' tkCurlyRi, ## '}' tkCurlyAt, ## '{@}' + tkEmptyCurl, ## '{}' tkArrow, ## '<-' tkBar, ## '/' tkStar, ## '*' @@ -1427,7 +1437,7 @@ type const tokKindToStr: array[TokKind, string] = [ "invalid", "[EOF]", ".", "_", "identifier", "string literal", - "character set", "(", ")", "{", "}", "{@}", + "character set", "(", ")", "{", "}", "{@}", "{}", "<-", "/", "*", "+", "&", "!", "?", "@", "built-in", "escaped", "$", "$", "^" ] @@ -1564,13 +1574,21 @@ proc getString(c: var PegLexer, tok: var Token) = proc getDollar(c: var PegLexer, tok: var Token) = var pos = c.bufpos + 1 + var neg = false + if pos < c.buf.len and c.buf[pos] == '^': + neg = true + inc(pos) if pos < c.buf.len and c.buf[pos] in {'0'..'9'}: tok.kind = tkBackref tok.index = 0 while pos < c.buf.len and c.buf[pos] in {'0'..'9'}: tok.index = tok.index * 10 + ord(c.buf[pos]) - ord('0') inc(pos) + if neg: + tok.index = -tok.index else: + if neg: + dec(pos) tok.kind = tkDollar c.bufpos = pos @@ -1670,6 +1688,10 @@ proc getTok(c: var PegLexer, tok: var Token) = tok.kind = tkCurlyAt inc(c.bufpos, 2) add(tok.literal, "{@}") + elif c.buf[c.bufpos] == '}' and c.bufpos < c.buf.len: + tok.kind = tkEmptyCurl + inc(c.bufpos) + add(tok.literal, "{}") else: tok.kind = tkCurlyLe add(tok.literal, '{') @@ -1705,7 +1727,7 @@ proc getTok(c: var PegLexer, tok: var Token) = return if c.buf[c.bufpos] in {'\'', '"'} or c.buf[c.bufpos] == '$' and c.bufpos+1 < c.buf.len and - c.buf[c.bufpos+1] in {'0'..'9'}: + c.buf[c.bufpos+1] in {'^', '0'..'9'}: case tok.literal of "i": tok.modifier = modIgnoreCase of "y": tok.modifier = modIgnoreStyle @@ -1819,10 +1841,13 @@ proc modifiedTerm(s: string, m: Modifier): Peg = of modIgnoreStyle: result = termIgnoreStyle(s) proc modifiedBackref(s: int, m: Modifier): Peg = + var + reverse = s < 0 + index = if reverse: -s else: s case m - of modNone, modVerbatim: result = backref(s) - of modIgnoreCase: result = backrefIgnoreCase(s) - of modIgnoreStyle: result = backrefIgnoreStyle(s) + of modNone, modVerbatim: result = backref(index, reverse) + of modIgnoreCase: result = backrefIgnoreCase(index, reverse) + of modIgnoreStyle: result = backrefIgnoreStyle(index, reverse) proc builtin(p: var PegParser): Peg = # do not use "y", "skip" or "i" as these would be ambiguous @@ -1896,6 +1921,9 @@ proc primary(p: var PegParser): Peg = result = capture(parseExpr(p)).token(p) eat(p, tkCurlyRi) inc(p.captures) + of tkEmptyCurl: + result = capture() + getTok(p) of tkAny: result = any().token(p) getTok(p) @@ -1915,11 +1943,11 @@ proc primary(p: var PegParser): Peg = result = startAnchor() getTok(p) of tkBackref: + if abs(p.tok.index) > p.captures or p.tok.index == 0: + pegError(p, "invalid back reference index: " & $p.tok.index) var m = p.tok.modifier if m == modNone: m = p.modifier result = modifiedBackref(p.tok.index, m).token(p) - if p.tok.index < 0 or p.tok.index > p.captures: - pegError(p, "invalid back reference index: " & $p.tok.index) getTok(p) else: pegError(p, "expression expected, but found: " & p.tok.literal) @@ -1943,7 +1971,7 @@ proc seqExpr(p: var PegParser): Peg = case p.tok.kind of tkAmp, tkNot, tkAt, tkStringLit, tkCharSet, tkParLe, tkCurlyLe, tkAny, tkAnyRune, tkBuiltin, tkEscaped, tkDollar, tkBackref, - tkHat, tkCurlyAt: + tkHat, tkCurlyAt, tkEmptyCurl: result = sequence(result, primary(p)) of tkIdentifier: if not arrowIsNextTok(p): diff --git a/tests/stdlib/tpegs.nim b/tests/stdlib/tpegs.nim index f2c93c2e5..1261d55b8 100644 --- a/tests/stdlib/tpegs.nim +++ b/tests/stdlib/tpegs.nim @@ -293,6 +293,34 @@ block: doAssert "test1".match(peg"""{@}$""") doAssert "test2".match(peg"""{(!$ .)*} $""") + + doAssert "abbb".match(peg"{a} {b} $2 $^1") + doAssert "abBA".match(peg"{a} {b} i$2 i$^2") + + doAssert "abba".match(peg"{a} {b} $^1 {} $^1") + + block: + let grammar = peg""" +program <- {''} stmt* $ +stmt <- call / block +call <- 'call()' EOL +EOL <- \n / $ +block <- 'block:' \n indBody +indBody <- {$^1 ' '+} stmt ($^1 stmt)* {} +""" + let program = """ +call() +block: + block: + call() + call() + call() +call() +""" + var c: Captures + doAssert program.len == program.rawMatch(grammar, 0, c) + doAssert c.ml == 1 + pegsTest() static: pegsTest() From 6e74fecb4275272e5224e2a0ba45df5ca2c40b82 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Mon, 28 Jun 2021 15:00:14 +0200 Subject: [PATCH 2284/3768] Simplify addInt, remove digits10 (#18356) * Simplify addInt, remove digits10 Co-authored-by: Charles Blake * Fix bootstrapping * Add noInit to tmp array * noInit -> noinit Co-authored-by: Charles Blake --- lib/std/private/digitsutils.nim | 58 +++++++++++++-------------------- lib/system/dollars.nim | 4 +-- lib/system/strmantle.nim | 10 ++---- tests/stdlib/tdigitsutils.nim | 23 ------------- 4 files changed, 27 insertions(+), 68 deletions(-) delete mode 100644 tests/stdlib/tdigitsutils.nim diff --git a/lib/std/private/digitsutils.nim b/lib/std/private/digitsutils.nim index cc985c7bd..7aefc36bc 100644 --- a/lib/std/private/digitsutils.nim +++ b/lib/std/private/digitsutils.nim @@ -38,51 +38,39 @@ proc trailingZeros2Digits*(digits: uint32): int32 {.inline.} = assert(digits <= 99) return trailingZeros100[digits] -func digits10*(num: uint64): int {.noinline.} = - if num < 10'u64: - result = 1 - elif num < 100'u64: - result = 2 - elif num < 1_000'u64: - result = 3 - elif num < 10_000'u64: - result = 4 - elif num < 100_000'u64: - result = 5 - elif num < 1_000_000'u64: - result = 6 - elif num < 10_000_000'u64: - result = 7 - elif num < 100_000_000'u64: - result = 8 - elif num < 1_000_000_000'u64: - result = 9 - elif num < 10_000_000_000'u64: - result = 10 - elif num < 100_000_000_000'u64: - result = 11 - elif num < 1_000_000_000_000'u64: - result = 12 - else: - result = 12 + digits10(num div 1_000_000_000_000'u64) - -template numToString*(result: var string, origin: uint64, length: int) = +func addIntImpl*(result: var string, origin: uint64) = + var tmp {.noinit.}: array[24, char] var num = origin - var next = length - 1 + var next = tmp.len - 1 const nbatch = 100 while num >= nbatch: let originNum = num num = num div nbatch let index = (originNum - num * nbatch) shl 1 - result[next] = digits100[index + 1] - result[next - 1] = digits100[index] + tmp[next] = digits100[index + 1] + tmp[next - 1] = digits100[index] dec(next, 2) # process last 1-2 digits if num < 10: - result[next] = chr(ord('0') + num) + tmp[next] = chr(ord('0') + num) else: let index = num * 2 - result[next] = digits100[index + 1] - result[next - 1] = digits100[index] + tmp[next] = digits100[index + 1] + tmp[next - 1] = digits100[index] + dec next + let n = result.len + let length = tmp.len - next + result.setLen n + length + when nimvm: + for i in 0.. 1: - doAssert digits10(x-1) == num - 1 - num += 1 - let xOld = x - x *= 10 - if x < xOld: - # wrap-around - break - -static: main() -main() From 0be17f5d9c6b66a8f8dda83c9a3c77c61be3844d Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 28 Jun 2021 17:08:45 +0200 Subject: [PATCH 2285/3768] fixes #18319 (#18375) --- compiler/jsgen.nim | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 393ac83c2..a9339c7fd 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -29,7 +29,7 @@ implements the required case distinction. import - ast, trees, magicsys, options, + ast, astalgo, trees, magicsys, options, nversion, msgs, idents, types, ropes, passes, ccgutils, wordrecg, renderer, cgmeth, lowerings, sighashes, modulegraphs, lineinfos, rodutils, @@ -2234,6 +2234,16 @@ proc genTupleConstr(p: PProc, n: PNode, r: var TCompRes) = r.res.addf("Field$#: $#", [i.rope, a.res]) r.res.add("}") +proc lookupFieldAgain(ty: PType; field: PSym): PSym = + var ty = ty + while ty != nil: + ty = ty.skipTypes(skipPtrs) + assert(ty.kind in {tyTuple, tyObject}) + result = lookupInRecord(ty.n, field.name) + if result != nil: break + ty = ty[0] + if result == nil: result = field + proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) = var a: TCompRes r.kind = resExpr @@ -2247,7 +2257,7 @@ proc genObjConstr(p: PProc, n: PNode, r: var TCompRes) = gen(p, val, a) var f = it[0].sym if f.loc.r == nil: f.loc.r = mangleName(p.module, f) - fieldIDs.incl(f.id) + fieldIDs.incl(lookupFieldAgain(n.typ, f).id) let typ = val.typ.skipTypes(abstractInst) if a.typ == etyBaseIndex: From cb9496693a718cdb9ad8b764f82f524efe943bd2 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 28 Jun 2021 08:15:20 -0700 Subject: [PATCH 2286/3768] `./koch tools` now builds bin/nim_dbg, a debug version of nim (#18289) --- koch.nim | 10 ++++++++-- tools/kochdocs.nim | 5 +++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/koch.nim b/koch.nim index 6a0b7a0d3..d3957069b 100644 --- a/koch.nim +++ b/koch.nim @@ -215,8 +215,14 @@ proc buildTools(args: string = "") = options = "-d:release " & args) when defined(windows): buildVccTool(args) bundleNimpretty(args) - nimCompileFold("Compile testament", "testament/testament.nim", - options = "-d:release " & args) + nimCompileFold("Compile testament", "testament/testament.nim", options = "-d:release" & args) + + # pre-packages a debug version of nim which can help in many cases investigate issuses + # withouth having to rebuild compiler. + # `-d:nimDebugUtils` only makes sense when temporarily editing/debugging compiler + # `-d:debug` should be changed to a flag that doesn't require re-compiling nim + # `--opt:speed` is a sensible default even for a debug build, it doesn't affect nim stacktraces + nimCompileFold("Compile nim_dbg", "compiler/nim.nim", options = "--opt:speed --stacktrace -d:debug --stacktraceMsgs -d:nimCompilerStacktraceHints" & args, outputName = "nim_dbg") proc nsis(latest: bool; args: string) = bundleNimbleExe(latest, args) diff --git a/tools/kochdocs.nim b/tools/kochdocs.nim index 469059348..8e8085f73 100644 --- a/tools/kochdocs.nim +++ b/tools/kochdocs.nim @@ -98,8 +98,9 @@ proc nimCompile*(input: string, outputDir = "bin", mode = "c", options = "") = let cmd = findNim().quoteShell() & " " & mode & " -o:" & output & " " & options & " " & input exec cmd -proc nimCompileFold*(desc, input: string, outputDir = "bin", mode = "c", options = "") = - let output = outputDir / input.splitFile.name.exe +proc nimCompileFold*(desc, input: string, outputDir = "bin", mode = "c", options = "", outputName = "") = + let outputName2 = if outputName.len == 0: input.splitFile.name.exe else: outputName.exe + let output = outputDir / outputName2 let cmd = findNim().quoteShell() & " " & mode & " -o:" & output & " " & options & " " & input execFold(desc, cmd) From 72d6a9c885504a1752ee0639aa110d4bf42d95c8 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Mon, 28 Jun 2021 17:41:37 +0200 Subject: [PATCH 2287/3768] Fix #16426 (#18377) --- compiler/parser.nim | 13 ++++++++----- tests/parser/tstmtlists.nim | 15 +++++++++++++++ 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/compiler/parser.nim b/compiler/parser.nim index 9cddf8e4f..b6c01e9c6 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -537,13 +537,16 @@ proc parseGStrLit(p: var Parser, a: PNode): PNode = proc complexOrSimpleStmt(p: var Parser): PNode proc simpleExpr(p: var Parser, mode = pmNormal): PNode -proc parseIfExpr(p: var Parser, kind: TNodeKind): PNode +proc parseIfOrWhenExpr(p: var Parser, kind: TNodeKind): PNode proc semiStmtList(p: var Parser, result: PNode) = inc p.inSemiStmtList withInd(p): # Be lenient with the first stmt/expr - let a = if p.tok.tokType == tkIf: parseIfExpr(p, nkIfStmt) else: complexOrSimpleStmt(p) + let a = case p.tok.tokType + of tkIf: parseIfOrWhenExpr(p, nkIfStmt) + of tkWhen: parseIfOrWhenExpr(p, nkWhenStmt) + else: complexOrSimpleStmt(p) result.add a while p.tok.tokType != tkEof: @@ -1203,13 +1206,13 @@ proc parseExpr(p: var Parser): PNode = result = parseBlock(p) of tkIf: nimprettyDontTouch: - result = parseIfExpr(p, nkIfExpr) + result = parseIfOrWhenExpr(p, nkIfExpr) of tkFor: nimprettyDontTouch: result = parseFor(p) of tkWhen: nimprettyDontTouch: - result = parseIfExpr(p, nkWhenExpr) + result = parseIfOrWhenExpr(p, nkWhenExpr) of tkCase: # Currently we think nimpretty is good enough with case expressions, # so it is allowed to touch them: @@ -1559,7 +1562,7 @@ proc parseIfOrWhen(p: var Parser, kind: TNodeKind): PNode = branch.add(parseStmt(p)) result.add(branch) -proc parseIfExpr(p: var Parser, kind: TNodeKind): PNode = +proc parseIfOrWhenExpr(p: var Parser, kind: TNodeKind): PNode = #| condExpr = expr colcom expr optInd #| ('elif' expr colcom expr optInd)* #| 'else' colcom expr diff --git a/tests/parser/tstmtlists.nim b/tests/parser/tstmtlists.nim index b6c7e4a94..fa47ba364 100644 --- a/tests/parser/tstmtlists.nim +++ b/tests/parser/tstmtlists.nim @@ -50,6 +50,8 @@ hello 9 hello 10 +lucky +lucky ''' """ @@ -163,3 +165,16 @@ template dim: int = else: int.high) +# lenient indentation: + +echo (if 0 == 1: + "0 == 1" +else: + "lucky") + +# bug #16426 +echo (when 0 == 1: + "0 == 1" +else: + "lucky") + From dd2cbe3164e476dafda4b59d6ed518d79f69f195 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Mon, 28 Jun 2021 19:25:30 +0200 Subject: [PATCH 2288/3768] Cleanup lookups.nim again.. (#18379) --- compiler/lookups.nim | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 9b878dd4b..44a30eefa 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -58,8 +58,8 @@ proc considerQuotedIdent*(c: PContext; n: PNode, origin: PNode = nil): PIdent = template addSym*(scope: PScope, s: PSym) = strTableAdd(scope.symbols, s) -proc addUniqueSym*(scope: PScope, s: PSym, onConflictKeepOld: bool): PSym = - result = strTableInclReportConflict(scope.symbols, s, onConflictKeepOld) +proc addUniqueSym*(scope: PScope, s: PSym): PSym = + result = strTableInclReportConflict(scope.symbols, s) proc openScope*(c: PContext): PScope {.discardable.} = result = PScope(parent: c.currentScope, @@ -298,7 +298,7 @@ proc wrongRedefinition*(c: PContext; info: TLineInfo, s: string; # xxx pending bootstrap >= 1.4, replace all those overloads with a single one: # proc addDecl*(c: PContext, sym: PSym, info = sym.info, scope = c.currentScope) {.inline.} = proc addDeclAt*(c: PContext; scope: PScope, sym: PSym, info: TLineInfo) = - let conflict = scope.addUniqueSym(sym, onConflictKeepOld = true) + let conflict = scope.addUniqueSym(sym) if conflict != nil: if sym.kind == skModule and conflict.kind == skModule and sym.owner == conflict.owner: # e.g.: import foo; import foo @@ -320,7 +320,7 @@ proc addDecl*(c: PContext, sym: PSym) {.inline.} = addDeclAt(c, c.currentScope, sym) proc addPrelimDecl*(c: PContext, sym: PSym) = - discard c.currentScope.addUniqueSym(sym, onConflictKeepOld = false) + discard c.currentScope.addUniqueSym(sym) from ic / ic import addHidden From 808db3b2845dab9822bc82ae80073a68396db999 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Mon, 28 Jun 2021 22:01:06 +0200 Subject: [PATCH 2289/3768] Fix unused warning for $ on empty object (#18381) --- lib/system/dollars.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/system/dollars.nim b/lib/system/dollars.nim index c23ea347d..baae8f090 100644 --- a/lib/system/dollars.nim +++ b/lib/system/dollars.nim @@ -113,7 +113,7 @@ proc `$`*[T: tuple|object](x: T): string = ## $() == "()" result = "(" const isNamed = T is object or isNamedTuple(T) - var count = 0 + var count {.used.} = 0 for name, value in fieldPairs(x): if count > 0: result.add(", ") when isNamed: From 6387e289631c22b8fa81e8e478c97cc500b90e26 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 28 Jun 2021 13:09:14 -0700 Subject: [PATCH 2290/3768] simplify rdstdin (#18382) --- lib/impure/rdstdin.nim | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/lib/impure/rdstdin.nim b/lib/impure/rdstdin.nim index c580b89d1..32048b131 100644 --- a/lib/impure/rdstdin.nim +++ b/lib/impure/rdstdin.nim @@ -52,16 +52,6 @@ elif defined(genode): else: import linenoise - proc readLineFromStdin*(prompt: string): string {. - tags: [ReadIOEffect, WriteIOEffect].} = - var buffer = linenoise.readLine(prompt) - if isNil(buffer): - raise newException(IOError, "Linenoise returned nil") - result = $buffer - if result.len > 0: - historyAdd(buffer) - linenoise.free(buffer) - proc readLineFromStdin*(prompt: string, line: var string): bool {. tags: [ReadIOEffect, WriteIOEffect].} = var buffer = linenoise.readLine(prompt) @@ -73,3 +63,7 @@ else: historyAdd(buffer) linenoise.free(buffer) result = true + + proc readLineFromStdin*(prompt: string): string {.inline.} = + if not readLineFromStdin(prompt, result): + raise newException(IOError, "Linenoise returned nil") From 97fc95012d2725b625c492b6b72336a89c501076 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 29 Jun 2021 15:34:39 +0200 Subject: [PATCH 2291/3768] fixes #16270 (#18388) --- compiler/ast.nim | 3 ++- compiler/semexprs.nim | 1 + compiler/sigmatch.nim | 9 +++++++-- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 56043692f..2a2a84e76 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -499,7 +499,7 @@ type nfFirstWrite# this node is a first write TNodeFlags* = set[TNodeFlag] - TTypeFlag* = enum # keep below 32 for efficiency reasons (now: ~40) + TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 43) tfVarargs, # procedure has C styled varargs # tyArray type represeting a varargs list tfNoSideEffect, # procedure type does not allow side effects @@ -566,6 +566,7 @@ type # (for importc types); type is fully specified, allowing to compute # sizeof, alignof, offsetof at CT tfExplicitCallConv + tfIsConstructor TTypeFlags* = set[TTypeFlag] diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index c885f4963..acf408db0 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2412,6 +2412,7 @@ proc semWhen(c: PContext, n: PNode, semCheck = true): PNode = proc semSetConstr(c: PContext, n: PNode): PNode = result = newNodeI(nkCurly, n.info) result.typ = newTypeS(tySet, c) + result.typ.flags.incl tfIsConstructor if n.len == 0: rawAddSon(result.typ, newTypeS(tyEmpty, c)) else: diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index b2003c0a8..071685057 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1341,8 +1341,13 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, result = isSubtype else: result = typeRel(c, f[0], a[0], flags) - if result <= isConvertible: - result = isNone # BUGFIX! + if result < isGeneric: + if result <= isConvertible: + result = isNone + elif tfIsConstructor notin a.flags: + # set constructors are a bit special... + result = isNone + of tyPtr, tyRef: skipOwned(a) if a.kind == f.kind: From 36d24846b6685a44ff2185a823cdcb0f3bc236d3 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 29 Jun 2021 18:08:50 -0700 Subject: [PATCH 2292/3768] typo: enmRange => enumElementsAsSet (#18394) --- lib/std/setutils.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/std/setutils.nim b/lib/std/setutils.nim index aba5f348a..84cc93623 100644 --- a/lib/std/setutils.nim +++ b/lib/std/setutils.nim @@ -36,7 +36,7 @@ template toSet*(iter: untyped): untyped = incl(result, x) result -macro enmRange(enm: typed): untyped = result = newNimNode(nnkCurly).add(enm.getType[1][1..^1]) +macro enumElementsAsSet(enm: typed): untyped = result = newNimNode(nnkCurly).add(enm.getType[1][1..^1]) # func fullSet*(T: typedesc): set[T] {.inline.} = # xxx would give: Error: ordinal type expected func fullSet*[T](U: typedesc[T]): set[T] {.inline.} = @@ -49,7 +49,7 @@ func fullSet*[T](U: typedesc[T]): set[T] {.inline.} = when T is Ordinal: {T.low..T.high} else: # Hole filled enum - enmRange(T) + enumElementsAsSet(T) func complement*[T](s: set[T]): set[T] {.inline.} = ## Returns the set complement of `a`. From afe5eb569b36ed310f6f93a33ae52274a6dce02e Mon Sep 17 00:00:00 2001 From: Elliot Waite Date: Tue, 29 Jun 2021 19:21:30 -0700 Subject: [PATCH 2293/3768] Add commas to docs for clarity (#18398) --- doc/tut2.rst | 2 +- lib/pure/asyncdispatch.nim | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/tut2.rst b/doc/tut2.rst index 2624b2690..725f68dd5 100644 --- a/doc/tut2.rst +++ b/doc/tut2.rst @@ -79,7 +79,7 @@ to introduce new object roots apart from `system.RootObj`. (This is used in the GTK wrapper for instance.) Ref objects should be used whenever inheritance is used. It isn't strictly -necessary, but with non-ref objects assignments such as `let person: Person = +necessary, but with non-ref objects, assignments such as `let person: Person = Student(id: 123)` will truncate subclass fields. **Note**: Composition (*has-a* relation) is often preferable to inheritance diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index b88d5ede8..17fd5f824 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -61,8 +61,8 @@ ## callback on this future which will be called once the future completes. ## All the callback does is write the data stored in the future to `stdout`. ## The `read` function is used for this and it checks whether the future -## completes with an error for you (if it did it will simply raise the -## error), if there is no error however it returns the value of the future. +## completes with an error for you (if it did, it will simply raise the +## error), if there is no error, however, it returns the value of the future. ## ## Asynchronous procedures ## ======================= From 0483a5ffaf3284b6bfc51e2e08bbc1d5a57cd939 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 29 Jun 2021 22:16:31 -0700 Subject: [PATCH 2294/3768] improvements to hint:processing: show import stack, distinguish nims, show includes etc (#18372) * improvements to hint:processing * fix tests; do not show hintProcessing for nimscript unless given -d:nimHintProcessingNims * fix trunner and avoid need for -d:nimHintProcessingNims * fix some tests --- compiler/lineinfos.nim | 5 +++-- compiler/modulegraphs.nim | 12 ++++++++++++ compiler/modules.nim | 11 ++++++----- compiler/passaux.nim | 13 ++++--------- compiler/semstmts.nim | 1 + nimsuggest/tests/tchk1.nim | 2 +- nimsuggest/tests/tchk_compiles.nim | 2 +- nimsuggest/tests/ttempl_inst.nim | 2 +- 8 files changed, 29 insertions(+), 19 deletions(-) diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index 131fe480b..801c20d25 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -75,7 +75,7 @@ type hintXDeclaredButNotUsed = "XDeclaredButNotUsed", hintDuplicateModuleImport = "DuplicateModuleImport", hintXCannotRaiseY = "XCannotRaiseY", hintConvToBaseNotNeeded = "ConvToBaseNotNeeded", hintConvFromXtoItselfNotNeeded = "ConvFromXtoItselfNotNeeded", hintExprAlwaysX = "ExprAlwaysX", - hintQuitCalled = "QuitCalled", hintProcessing = "Processing", hintCodeBegin = "CodeBegin", + hintQuitCalled = "QuitCalled", hintProcessing = "Processing", hintProcessingStmt = "ProcessingStmt", hintCodeBegin = "CodeBegin", hintCodeEnd = "CodeEnd", hintConf = "Conf", hintPath = "Path", hintConditionAlwaysTrue = "CondTrue", hintConditionAlwaysFalse = "CondFalse", hintName = "Name", hintPattern = "Pattern", hintExecuting = "Exec", hintLinking = "Link", hintDependency = "Dependency", @@ -163,6 +163,7 @@ const hintExprAlwaysX: "expression evaluates always to '$1'", hintQuitCalled: "quit() called", hintProcessing: "$1", + hintProcessingStmt: "$1", hintCodeBegin: "generated code listing:", hintCodeEnd: "end of listing", hintConf: "used config file '$1'", @@ -202,7 +203,7 @@ type proc computeNotesVerbosity(): array[0..3, TNoteKinds] = result[3] = {low(TNoteKind)..high(TNoteKind)} - {warnObservableStores, warnResultUsed} - result[2] = result[3] - {hintStackTrace, warnUninit, hintExtendedContext, hintDeclaredLoc} + result[2] = result[3] - {hintStackTrace, warnUninit, hintExtendedContext, hintDeclaredLoc, hintProcessingStmt} result[1] = result[2] - {warnProveField, warnProveIndex, warnGcUnsafe, hintPath, hintDependency, hintCodeBegin, hintCodeEnd, hintSource, hintGlobalVar, hintGCStats, hintMsgOrigin, hintPerformance} diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index 0d8d4f867..90b130e92 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -582,3 +582,15 @@ proc moduleFromRodFile*(g: ModuleGraph; fileIdx: FileIndex; proc configComplete*(g: ModuleGraph) = rememberStartupConfig(g.startupPackedConfig, g.config) + +from std/strutils import repeat, `%` + +proc onProcessing*(graph: ModuleGraph, fileIdx: FileIndex, moduleStatus: string, fromModule: PSym, ) = + let conf = graph.config + let isNimscript = conf.isDefined("nimscript") + if (not isNimscript) or hintProcessing in conf.cmdlineNotes: + let path = toFilenameOption(conf, fileIdx, conf.filenameOption) + let indent = ">".repeat(graph.importStack.len) + let fromModule2 = if fromModule != nil: $fromModule.name.s else: "(toplevel)" + let mode = if isNimscript: "(nims) " else: "" + rawMessage(conf, hintProcessing, "$#$# $#: $#: $#" % [mode, indent, fromModule2, moduleStatus, path]) diff --git a/compiler/modules.nim b/compiler/modules.nim index dcfcfd508..6fba606b2 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -84,12 +84,13 @@ proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym = partialInitModule(result, graph, fileIdx, filename) graph.registerModule(result) -proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags): PSym = +proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags, fromModule: PSym = nil): PSym = var flags = flags if fileIdx == graph.config.projectMainIdx2: flags.incl sfMainModule result = graph.getModule(fileIdx) - template processModuleAux = + template processModuleAux(moduleStatus) = + onProcessing(graph, fileIdx, moduleStatus, fromModule = fromModule) var s: PLLStream if sfMainModule in flags: if graph.config.projectIsStdin: s = stdin.llStreamOpen @@ -103,7 +104,7 @@ proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags): P result = newModule(graph, fileIdx) result.flags.incl flags registerModule(graph, result) - processModuleAux() + processModuleAux("import") else: if sfSystemModule in flags: graph.systemModule = result @@ -117,13 +118,13 @@ proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags): P # reset module fields: initStrTables(graph, result) result.ast = nil - processModuleAux() + processModuleAux("import(dirty)") graph.markClientsDirty(fileIdx) proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PSym = # this is called by the semantic checking phase assert graph.config != nil - result = compileModule(graph, fileIdx, {}) + result = compileModule(graph, fileIdx, {}, s) graph.addDep(s, fileIdx) # keep track of import relationships if graph.config.hcrOn: diff --git a/compiler/passaux.nim b/compiler/passaux.nim index 87cb3a730..68b783248 100644 --- a/compiler/passaux.nim +++ b/compiler/passaux.nim @@ -19,18 +19,13 @@ type config: ConfigRef proc verboseOpen(graph: ModuleGraph; s: PSym; idgen: IdGenerator): PPassContext = - let conf = graph.config - result = VerboseRef(config: conf, idgen: idgen) - let path = toFilenameOption(conf, s.position.FileIndex, conf.filenameOption) - rawMessage(conf, hintProcessing, path) + # xxx consider either removing this or keeping for documentation for how to add a pass + result = VerboseRef(config: graph.config, idgen: idgen) proc verboseProcess(context: PPassContext, n: PNode): PNode = + # called from `process` in `processTopLevelStmt`. result = n let v = VerboseRef(context) - if v.config.verbosity == 3: - # system.nim deactivates all hints, for verbosity:3 we want the processing - # messages nonetheless, so we activate them again (but honor cmdlineNotes) - v.config.setNote(hintProcessing) - message(v.config, n.info, hintProcessing, $v.idgen[]) + message(v.config, n.info, hintProcessingStmt, $v.idgen[]) const verbosePass* = makePass(open = verboseOpen, process = verboseProcess) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index f29dc24ef..2907d5172 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -2158,6 +2158,7 @@ proc incMod(c: PContext, n: PNode, it: PNode, includeStmtResult: PNode) = var f = checkModuleName(c.config, it) if f != InvalidFileIdx: addIncludeFileDep(c, f) + onProcessing(c.graph, f, "include", c.module) if containsOrIncl(c.includedFiles, f.int): localError(c.config, n.info, errRecursiveDependencyX % toMsgFilename(c.config, f)) else: diff --git a/nimsuggest/tests/tchk1.nim b/nimsuggest/tests/tchk1.nim index 794392c1f..f8e2989c5 100644 --- a/nimsuggest/tests/tchk1.nim +++ b/nimsuggest/tests/tchk1.nim @@ -17,7 +17,7 @@ proc main = discard """ $nimsuggest --tester $file >chk $1 -chk;;skUnknown;;;;Hint;;???;;0;;-1;;"tests/tchk1.nim [Processing]";;0 +chk;;skUnknown;;;;Hint;;???;;0;;-1;;">> (toplevel): import(dirty): tests/tchk1.nim [Processing]";;0 chk;;skUnknown;;;;Error;;$file;;12;;0;;"identifier expected, but got \'keyword template\'";;0 chk;;skUnknown;;;;Error;;$file;;14;;0;;"nestable statement requires indentation";;0 chk;;skUnknown;;;;Error;;$file;;12;;0;;"implementation of \'foo\' expected";;0 diff --git a/nimsuggest/tests/tchk_compiles.nim b/nimsuggest/tests/tchk_compiles.nim index 2afa06899..c8a3daac4 100644 --- a/nimsuggest/tests/tchk_compiles.nim +++ b/nimsuggest/tests/tchk_compiles.nim @@ -4,5 +4,5 @@ discard compiles(2 + "hello") discard """ $nimsuggest --tester $file >chk $1 -chk;;skUnknown;;;;Hint;;???;;0;;-1;;"tests/tchk_compiles.nim [Processing]";;0 +chk;;skUnknown;;;;Hint;;???;;0;;-1;;">> (toplevel): import(dirty): tests/tchk_compiles.nim [Processing]";;0 """ diff --git a/nimsuggest/tests/ttempl_inst.nim b/nimsuggest/tests/ttempl_inst.nim index da413d99a..5f5b10fe9 100644 --- a/nimsuggest/tests/ttempl_inst.nim +++ b/nimsuggest/tests/ttempl_inst.nim @@ -7,7 +7,7 @@ foo() discard """ $nimsuggest --tester $file >chk $1 -chk;;skUnknown;;;;Hint;;???;;0;;-1;;"tests/ttempl_inst.nim [Processing]";;0 +chk;;skUnknown;;;;Hint;;???;;0;;-1;;">> (toplevel): import(dirty): tests/ttempl_inst.nim [Processing]";;0 chk;;skUnknown;;;;Hint;;$file;;4;;3;;"template/generic instantiation from here";;0 chk;;skUnknown;;;;Warning;;$file;;2;;11;;"foo [User]";;0 """ From 16f0b556643a8a9cc73781de6d71d0041a194640 Mon Sep 17 00:00:00 2001 From: Bung Date: Wed, 30 Jun 2021 13:28:41 +0800 Subject: [PATCH 2295/3768] remove isKeywordIgnoreCase from highlite, not used , can't even compile with -d:useNimRtl (#18399) --- lib/packages/docutils/highlite.nim | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/packages/docutils/highlite.nim b/lib/packages/docutils/highlite.nim index 014303837..d36b2c877 100644 --- a/lib/packages/docutils/highlite.nim +++ b/lib/packages/docutils/highlite.nim @@ -188,9 +188,6 @@ const proc isKeyword(x: openArray[string], y: string): int = binarySearch(x, y) -proc isKeywordIgnoreCase(x: openArray[string], y: string): int = - binarySearch(x, y, cmpIgnoreCase) - proc nimNextToken(g: var GeneralTokenizer, keywords: openArray[string] = @[]) = const hexChars = {'0'..'9', 'A'..'F', 'a'..'f', '_'} From eaa1c3a37754725f91f54f23080980b82c2f2356 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C8=98tefan=20Talpalaru?= Date: Wed, 30 Jun 2021 07:29:10 +0200 Subject: [PATCH 2296/3768] genDepend: fix for module names that are reserved DOT keywords (#18392) (like "node" and "edge") --- compiler/depends.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/depends.nim b/compiler/depends.nim index 7225b6b47..30fc961c5 100644 --- a/compiler/depends.nim +++ b/compiler/depends.nim @@ -25,7 +25,7 @@ type dotGraph: Rope proc addDependencyAux(b: Backend; importing, imported: string) = - b.dotGraph.addf("$1 -> \"$2\";$n", [rope(importing), rope(imported)]) + b.dotGraph.addf("\"$1\" -> \"$2\";$n", [rope(importing), rope(imported)]) # s1 -> s2_4[label="[0-9]"]; proc addDotDependency(c: PPassContext, n: PNode): PNode = From 19263f27756e56026fe8d1ac05966b84010ae8d1 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 30 Jun 2021 12:36:24 +0200 Subject: [PATCH 2297/3768] fixes #18400 (#18402) --- lib/system/schubfach.nim | 2 +- tests/float/tfloat4.nim | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/system/schubfach.nim b/lib/system/schubfach.nim index d072cde99..06813f632 100644 --- a/lib/system/schubfach.nim +++ b/lib/system/schubfach.nim @@ -359,7 +359,7 @@ proc formatDigits(buffer: var openArray[char]; pos: int; digits: uint32; decimal pos = digitsEnd elif decimalPoint < numDigits: ## dig.its - for i in 0..<8: + for i in countdown(7, 0): buffer[i + decimalPoint + 1] = buffer[i + decimalPoint] buffer[pos+decimalPoint] = '.' pos = digitsEnd + 1 diff --git a/tests/float/tfloat4.nim b/tests/float/tfloat4.nim index 6a87cbe66..5bedca371 100644 --- a/tests/float/tfloat4.nim +++ b/tests/float/tfloat4.nim @@ -54,4 +54,10 @@ doAssert 9999999999999999.0 == "9999999999999999.0".parseFloat doAssert 0.999999999999999 == ".999999999999999".parseFloat doAssert 0.9999999999999999 == ".9999999999999999".parseFloat +# bug #18400 +var s = [-13.888888'f32] +assert $s[0] == "-13.888888" +var x = 1.23456789012345'f32 +assert $x == "1.2345679" + echo("passed all tests.") From 28a4814019ea5d985d323def3766fe7f44f2e3e1 Mon Sep 17 00:00:00 2001 From: Endeg Date: Wed, 30 Jun 2021 23:19:22 +0600 Subject: [PATCH 2298/3768] Missing spaces when compiling testament and nim_dbg. (#18403) --- koch.nim | 4 ++-- lib/pure/times.nim | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/koch.nim b/koch.nim index d3957069b..4ba0d0eb5 100644 --- a/koch.nim +++ b/koch.nim @@ -215,14 +215,14 @@ proc buildTools(args: string = "") = options = "-d:release " & args) when defined(windows): buildVccTool(args) bundleNimpretty(args) - nimCompileFold("Compile testament", "testament/testament.nim", options = "-d:release" & args) + nimCompileFold("Compile testament", "testament/testament.nim", options = "-d:release " & args) # pre-packages a debug version of nim which can help in many cases investigate issuses # withouth having to rebuild compiler. # `-d:nimDebugUtils` only makes sense when temporarily editing/debugging compiler # `-d:debug` should be changed to a flag that doesn't require re-compiling nim # `--opt:speed` is a sensible default even for a debug build, it doesn't affect nim stacktraces - nimCompileFold("Compile nim_dbg", "compiler/nim.nim", options = "--opt:speed --stacktrace -d:debug --stacktraceMsgs -d:nimCompilerStacktraceHints" & args, outputName = "nim_dbg") + nimCompileFold("Compile nim_dbg", "compiler/nim.nim", options = "--opt:speed --stacktrace -d:debug --stacktraceMsgs -d:nimCompilerStacktraceHints " & args, outputName = "nim_dbg") proc nsis(latest: bool; args: string) = bundleNimbleExe(latest, args) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index fb48a199c..fcdd4ec8d 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -913,7 +913,7 @@ proc getTime*(): Time {.tags: [TimeEffect], benign.} = result = initTime(ts.tv_sec.int64, ts.tv_nsec.int) elif defined(windows): var f {.noinit.}: FILETIME - getSystemTimePreciseAsFileTime(f) + getSystemTimeAsFileTime(f) result = fromWinTime(rdFileTime(f)) proc `-`*(a, b: Time): Duration {.operator, extern: "ntDiffTime".} = @@ -2596,7 +2596,7 @@ proc epochTime*(): float {.tags: [TimeEffect].} = toBiggestFloat(ts.tv_nsec.int64) / 1_000_000_000 elif defined(windows): var f {.noinit.}: winlean.FILETIME - getSystemTimePreciseAsFileTime(f) + getSystemTimeAsFileTime(f) var i64 = rdFileTime(f) - epochDiff var secs = i64 div rateDiff var subsecs = i64 mod rateDiff From 17911a116d8677228480d74ceb2b9593b992253e Mon Sep 17 00:00:00 2001 From: Mark Pointing Date: Thu, 1 Jul 2021 03:22:38 +1000 Subject: [PATCH 2299/3768] Removed hard coded ports from thttpclient_standalone.nim. (#18374) Co-authored-by: Dominik Picheta Co-authored-by: Mark Pointing Co-authored-by: Andreas Rumpf Co-authored-by: Timothee Cour --- tests/stdlib/thttpclient_standalone.nim | 49 +++++++++++++------------ 1 file changed, 26 insertions(+), 23 deletions(-) diff --git a/tests/stdlib/thttpclient_standalone.nim b/tests/stdlib/thttpclient_standalone.nim index 5fa1ea41a..362b1cb86 100644 --- a/tests/stdlib/thttpclient_standalone.nim +++ b/tests/stdlib/thttpclient_standalone.nim @@ -2,29 +2,37 @@ discard """ cmd: "nim c --threads:on $file" """ -import asynchttpserver, httpclient, asyncdispatch, strutils +import asynchttpserver, httpclient, asyncdispatch, strutils, net block: # bug #16436 - proc startServer() {.async.} = + proc startServer(): AsyncHttpServer = + result = newAsyncHttpServer() + result.listen(Port(0)) + + proc processRequest(server: AsyncHttpServer) {.async.} = proc cb(req: Request) {.async.} = let headers = { "Content-length": "15"} # Provide invalid content-length await req.respond(Http200, "Hello World", headers.newHttpHeaders()) - var server = newAsyncHttpServer() - await server.serve(Port(5555), cb) + await server.acceptRequest(cb) - proc runClient() {.async.} = + proc runClient(port: Port) {.async.} = let c = newAsyncHttpClient(headers = {"Connection": "close"}.newHttpHeaders) - let r = await c.getContent("http://127.0.0.1:5555") + discard await c.getContent("http://127.0.0.1:" & $port) doAssert false, "should fail earlier" - asyncCheck startServer() + let server = startServer() + asyncCheck processRequest(server) + let port = server.getPort() doAssertRaises(ProtocolError): - waitFor runClient() + waitFor runClient(port) block: # bug #14794 (And test for presence of content-length header when using postContent) - proc startServer() {.async.} = - var killServer = false + proc startServer(): AsyncHttpServer = + result = newAsyncHttpServer() + result.listen(Port(0)) + + proc runServer(server: AsyncHttpServer) {.async.} = proc cb(req: Request) {.async.} = doAssert(req.body.endsWith(httpNewLine), "Multipart body does not end with a newline.") # this next line is probably not required because asynchttpserver does not call @@ -32,23 +40,18 @@ block: # bug #14794 (And test for presence of content-length header when using p # Error: unhandled exception: 411 Length Required # Added for good measure in case the server becomes more permissive. doAssert(req.headers.hasKey("content-length"), "Content-Length header is not present.") - killServer = true asyncCheck req.respond(Http200, "OK") - var server = newAsyncHttpServer() - server.listen(Port(5556)) - while not killServer: - if server.shouldAcceptRequest(): - await server.acceptRequest(cb) - else: - poll() + await server.acceptRequest(cb) - proc runClient() {.async.} = + proc runClient(port: Port) {.async.} = let c = newAsyncHttpClient() - var data = newMultipartData() + let data = newMultipartData() data.add("file.txt", "This is intended to be an example text file.\r\nThis would be the second line.\r\n") - let r = await c.postContent("http://127.0.0.1:5556", multipart = data) + discard await c.postContent("http://127.0.0.1:" & $port, multipart = data) c.close() - asyncCheck startServer() - waitFor runClient() + let server = startServer() + let port = server.getPort() + asyncCheck runServer(server) + waitFor runClient(port) From 41c29cb3a1674a8b85844888e831b79050d91293 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 1 Jul 2021 06:51:08 +0200 Subject: [PATCH 2300/3768] fixes #18130 (#18407) --- compiler/injectdestructors.nim | 2 +- tests/destructor/tv2_cast.nim | 87 +++++++++++++++++++++++++++------- 2 files changed, 72 insertions(+), 17 deletions(-) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 6447557c5..6e4eaa817 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -1057,7 +1057,7 @@ proc moveOrCopy(dest, ri: PNode; c: var Con; s: var Scope, isDecl = false): PNod result = c.genCopy(dest, ri) result.add p(ri, c, s, consumed) c.finishCopy(result, dest, isFromSink = false) - of nkHiddenSubConv, nkHiddenStdConv, nkConv, nkObjDownConv, nkObjUpConv: + of nkHiddenSubConv, nkHiddenStdConv, nkConv, nkObjDownConv, nkObjUpConv, nkCast: result = c.genSink(dest, p(ri, c, s, sinkArg), isDecl) of nkStmtListExpr, nkBlockExpr, nkIfExpr, nkCaseStmt, nkTryStmt: template process(child, s): untyped = moveOrCopy(dest, child, c, s, isDecl) diff --git a/tests/destructor/tv2_cast.nim b/tests/destructor/tv2_cast.nim index 5502cb38d..917cf0eb3 100644 --- a/tests/destructor/tv2_cast.nim +++ b/tests/destructor/tv2_cast.nim @@ -1,48 +1,81 @@ discard """ output: '''@[1] @[116, 101, 115, 116] -@[1953719668, 875770417]''' +@[1953719668, 875770417] +destroying O1''' cmd: '''nim c --gc:arc --expandArc:main --expandArc:main1 --expandArc:main2 --expandArc:main3 --hints:off --assertions:off $file''' nimout: '''--expandArc: main var data :tmpD -`=copy`(data, cast[string](encode(cast[seq[byte]]( - :tmpD = newString(100) - :tmpD)))) -`=destroy`(:tmpD) -`=destroy`(data) + :tmpD_1 + :tmpD_2 +data = + wasMoved(:tmpD) + `=copy`(:tmpD, cast[string]( + :tmpD_2 = encode(cast[seq[byte]]( + :tmpD_1 = newString(100) + :tmpD_1)) + :tmpD_2)) + :tmpD +`=destroy`(:tmpD_2) +`=destroy_1`(:tmpD_1) +`=destroy_1`(data) -- end of expandArc ------------------------ --expandArc: main1 var s data + :tmpD + :tmpD_1 s = newString(100) -`=copy`(data, cast[string](encode(toOpenArrayByte(s, 0, len(s) - 1)))) -`=destroy`(data) -`=destroy`(s) +data = + wasMoved(:tmpD) + `=copy`(:tmpD, cast[string]( + :tmpD_1 = encode(toOpenArrayByte(s, 0, len(s) - 1)) + :tmpD_1)) + :tmpD +`=destroy`(:tmpD_1) +`=destroy_1`(data) +`=destroy_1`(s) -- end of expandArc ------------------------ --expandArc: main2 var s data + :tmpD + :tmpD_1 s = newSeq(100) -`=copy`(data, cast[string](encode(s))) -`=destroy`(data) -`=destroy_1`(s) +data = + wasMoved(:tmpD) + `=copy`(:tmpD, cast[string]( + :tmpD_1 = encode(s) + :tmpD_1)) + :tmpD +`=destroy`(:tmpD_1) +`=destroy_1`(data) +`=destroy`(s) -- end of expandArc ------------------------ --expandArc: main3 var data :tmpD -`=copy`(data, cast[string](encode do: - :tmpD = newSeq(100) - :tmpD)) -`=destroy`(:tmpD) + :tmpD_1 + :tmpD_2 +data = + wasMoved(:tmpD) + `=copy`(:tmpD, cast[string]( + :tmpD_2 = encode do: + :tmpD_1 = newSeq(100) + :tmpD_1 + :tmpD_2)) + :tmpD +`=destroy`(:tmpD_2) +`=destroy`(:tmpD_1) `=destroy_1`(data) -- end of expandArc ------------------------''' """ @@ -89,3 +122,25 @@ echo a #issue 11204 var ac {.compileTime.} = @["a", "b"] const bc = ac.len + + +type + O = object of RootRef + i: int + + O1 = object of O + O2 = object of O + +proc `=destroy`(o: var O) = + echo "destroying O" + +proc `=destroy`(o: var O1) = + echo "destroying O1" + +proc `=destroy`(o: var O2) = + echo "destroying O2" + +proc test = + let o3 = cast[ref O2]((ref O1)()) + +test() From 779b4e307bdeb018bb2884920144a8c0d1f3b377 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 1 Jul 2021 02:14:01 -0700 Subject: [PATCH 2301/3768] don't use `{.rtl.}` for generics, otherwise `-d:useNimRtl` gives `ambiguous identifier` nimrtl error (#18406) --- lib/pure/strutils.nim | 2 +- lib/system/inclrtl.nim | 13 +++++++------ tests/dll/client.nim | 8 +++++--- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 8e0a6158b..9e0837942 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -1760,7 +1760,7 @@ func join*(a: openArray[string], sep: string = ""): string {.rtl, else: result = "" -func join*[T: not string](a: openArray[T], sep: string = ""): string {.rtl.} = +func join*[T: not string](a: openArray[T], sep: string = ""): string = ## Converts all elements in the container `a` to strings using `$`, ## and concatenates them with `sep`. runnableExamples: diff --git a/lib/system/inclrtl.nim b/lib/system/inclrtl.nim index 0bc51d693..ca41f39c6 100644 --- a/lib/system/inclrtl.nim +++ b/lib/system/inclrtl.nim @@ -30,12 +30,13 @@ when defined(createNimRtl): {.pragma: inl.} {.pragma: compilerRtl, compilerproc, exportc: "nimrtl_$1", dynlib.} elif defined(useNimRtl): - when defined(windows): - const nimrtl* = "nimrtl.dll" - elif defined(macosx): - const nimrtl* = "libnimrtl.dylib" - else: - const nimrtl* = "libnimrtl.so" + #[ + `{.rtl.}` should only be used for non-generic procs. + ]# + const nimrtl* = + when defined(windows): "nimrtl.dll" + elif defined(macosx): "libnimrtl.dylib" + else: "libnimrtl.so" {.pragma: rtl, importc: "nimrtl_$1", dynlib: nimrtl, gcsafe.} {.pragma: inl.} {.pragma: compilerRtl, compilerproc, importc: "nimrtl_$1", dynlib: nimrtl.} diff --git a/tests/dll/client.nim b/tests/dll/client.nim index 150af3a17..62697569f 100644 --- a/tests/dll/client.nim +++ b/tests/dll/client.nim @@ -1,5 +1,4 @@ discard """ - output: "Done" cmd: "nim $target --debuginfo --hints:on --define:useNimRtl $options $file" """ @@ -37,5 +36,8 @@ proc eval(n: PNode): int = for i in 0..100_000: discard eval(buildTree(2)) -echo "Done" - +# bug https://forum.nim-lang.org/t/8176; Error: ambiguous identifier: 'nimrtl' +import std/strutils +doAssert join(@[1, 2]) == "12" +doAssert join(@[1.5, 2.5]) == "1.52.5" +doAssert join(@["a", "bc"]) == "abc" From 3ceaf5c1309ac8d4d68e6f39c13b021bcc1b15f4 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 1 Jul 2021 17:35:04 +0200 Subject: [PATCH 2302/3768] fixes #18030 (#18415) --- compiler/seminst.nim | 2 +- tests/arc/tarcmisc.nim | 30 ++++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 68ab2a310..e1fad236c 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -146,8 +146,8 @@ proc instantiateBody(c: PContext, n, params: PNode, result, orig: PSym) = freshGenSyms(c, b, result, orig, symMap) b = semProcBody(c, b) result.ast[bodyPos] = hloBody(c, b) - trackProc(c, result, result.ast[bodyPos]) excl(result.flags, sfForward) + trackProc(c, result, result.ast[bodyPos]) dec c.inGenericInst proc fixupInstantiatedSymbols(c: PContext, s: PSym) = diff --git a/tests/arc/tarcmisc.nim b/tests/arc/tarcmisc.nim index a52538085..5d5d8e914 100644 --- a/tests/arc/tarcmisc.nim +++ b/tests/arc/tarcmisc.nim @@ -28,6 +28,8 @@ aaaaa hello ok true +copying +123 closed destroying variable: 20 destroying variable: 10 @@ -433,3 +435,31 @@ proc t17712 = echo el != nil t17712() + +# bug #18030 + +type + Foo = object + n: int + +proc `=copy`(dst: var Foo, src: Foo) = + echo "copying" + dst.n = src.n + +proc `=sink`(dst: var Foo, src: Foo) = + echo "sinking" + dst.n = src.n + +var a: Foo + +proc putValue[T](n: T) + +proc useForward = + putValue(123) + +proc putValue[T](n: T) = + var b = Foo(n:n) + a = b + echo b.n + +useForward() From 60eac79f7f0478565a24ff423aa52ab53527d153 Mon Sep 17 00:00:00 2001 From: flywind Date: Fri, 2 Jul 2021 20:38:01 +0800 Subject: [PATCH 2303/3768] [minor]remove unnecessary stringify (#18419) --- compiler/lexer.nim | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 59b983060..c7a9aa47f 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -536,9 +536,9 @@ proc getNumber(L: var Lexer, result: var Token) = try: len = parseBiggestUInt(result.literal, iNumber) except ValueError: - raise newException(OverflowDefect, "number out of range: " & $result.literal) + raise newException(OverflowDefect, "number out of range: " & result.literal) if len != result.literal.len: - raise newException(ValueError, "invalid integer: " & $result.literal) + raise newException(ValueError, "invalid integer: " & result.literal) result.iNumber = cast[int64](iNumber) else: var iNumber: int64 @@ -546,9 +546,9 @@ proc getNumber(L: var Lexer, result: var Token) = try: len = parseBiggestInt(result.literal, iNumber) except ValueError: - raise newException(OverflowDefect, "number out of range: " & $result.literal) + raise newException(OverflowDefect, "number out of range: " & result.literal) if len != result.literal.len: - raise newException(ValueError, "invalid integer: " & $result.literal) + raise newException(ValueError, "invalid integer: " & result.literal) result.iNumber = iNumber # Explicit bounds checks. From d1d2498c7b933b6364af450df5088aaa89b4cf4b Mon Sep 17 00:00:00 2001 From: Antonis Geralis <43617260+planetis-m@users.noreply.github.com> Date: Sat, 3 Jul 2021 00:20:59 +0300 Subject: [PATCH 2304/3768] Add lent annotation to avoid extra copies in sample (#18404) * Add lent annotation to avoid extra copies in sample * Remove lent * Update random.nim --- lib/pure/random.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/random.nim b/lib/pure/random.nim index 6a5f35bde..07db0365c 100644 --- a/lib/pure/random.nim +++ b/lib/pure/random.nim @@ -410,7 +410,7 @@ proc sample*[T](r: var Rand; a: openArray[T]): T = result = a[r.rand(a.low..a.high)] -proc sample*[T](a: openArray[T]): T = +proc sample*[T](a: openArray[T]): lent T = ## Returns a random element from `a`. ## ## If `randomize <#randomize>`_ has not been called, the order of outcomes From ffa638ed5023834a7a5040a54c6cfde550d68054 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 5 Jul 2021 05:26:40 -0700 Subject: [PATCH 2305/3768] std/net: show addr+port on address already in use error; code-block => runnableExamples (#18428) * std/net: show addr+port on address already in use error; code-block => runnableExamples * var=>let --- lib/pure/net.nim | 80 ++++++++++++++++++++++-------------------------- 1 file changed, 37 insertions(+), 43 deletions(-) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index ff179fb2c..ec3e4e74d 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -44,46 +44,44 @@ ## After you create a socket with the `newSocket` procedure, you can easily ## connect it to a server running at a known hostname (or IP address) and port. ## To do so over TCP, use the example below. -## -## .. code-block:: Nim -## var socket = newSocket() -## socket.connect("google.com", Port(80)) -## -## For SSL, use the following example (and make sure to compile with `-d:ssl`): -## -## .. code-block:: Nim -## var socket = newSocket() -## var ctx = newContext() -## wrapSocket(ctx, socket) -## socket.connect("google.com", Port(443)) -## + +runnableExamples("-r:off"): + let socket = newSocket() + socket.connect("google.com", Port(80)) + +## For SSL, use the following example: + +runnableExamples("-r:off -d:ssl"): + let socket = newSocket() + let ctx = newContext() + wrapSocket(ctx, socket) + socket.connect("google.com", Port(443)) + ## UDP is a connectionless protocol, so UDP sockets don't have to explicitly ## call the `connect `_ procedure. They can ## simply start sending data immediately. -## -## .. code-block:: Nim -## var socket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) -## socket.sendTo("192.168.0.1", Port(27960), "status\n") -## + +runnableExamples("-r:off"): + let socket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) + socket.sendTo("192.168.0.1", Port(27960), "status\n") + ## Creating a server ## ----------------- ## ## After you create a socket with the `newSocket` procedure, you can create a ## TCP server by calling the `bindAddr` and `listen` procedures. -## -## .. code-block:: Nim -## var socket = newSocket() -## socket.bindAddr(Port(1234)) -## socket.listen() -## -## You can then begin accepting connections using the `accept` procedure. -## -## .. code-block:: Nim -## var client: Socket -## var address = "" -## while true: -## socket.acceptAddr(client, address) -## echo("Client connected from: ", address) + +runnableExamples("-r:off"): + let socket = newSocket() + socket.bindAddr(Port(1234)) + socket.listen() + + # You can then begin accepting connections using the `accept` procedure. + var client: Socket + var address = "" + while true: + socket.acceptAddr(client, address) + echo "Client connected from: ", address import std/private/since @@ -960,7 +958,7 @@ proc bindAddr*(socket: Socket, port = Port(0), address = "") {. var aiList = getAddrInfo(realaddr, port, socket.domain) if bindAddr(socket.fd, aiList.ai_addr, aiList.ai_addrlen.SockLen) < 0'i32: freeaddrinfo(aiList) - raiseOSError(osLastError()) + raiseOSError(osLastError(), "address: $# port: $#" % [address, $port]) freeaddrinfo(aiList) proc acceptAddr*(server: Socket, client: var owned(Socket), address: var string, @@ -1233,12 +1231,10 @@ proc getPeerAddr*(socket: Socket): (string, Port) = proc setSockOpt*(socket: Socket, opt: SOBool, value: bool, level = SOL_SOCKET) {.tags: [WriteIOEffect].} = ## Sets option `opt` to a boolean value specified by `value`. - ## - ## .. code-block:: Nim - ## var socket = newSocket() - ## socket.setSockOpt(OptReusePort, true) - ## socket.setSockOpt(OptNoDelay, true, level=IPPROTO_TCP.toInt) - ## + runnableExamples("-r:off"): + let socket = newSocket() + socket.setSockOpt(OptReusePort, true) + socket.setSockOpt(OptNoDelay, true, level = IPPROTO_TCP.cint) var valuei = cint(if value: 1 else: 0) setSockOptInt(socket.fd, cint(level), toCInt(opt), valuei) @@ -2025,10 +2021,8 @@ proc getPrimaryIPAddr*(dest = parseIpAddress("8.8.8.8")): IpAddress = ## ## Supports IPv4 and v6. ## Raises OSError if external networking is not set up. - ## - ## .. code-block:: Nim - ## echo $getPrimaryIPAddr() # "192.168.1.2" - + runnableExamples("-r:off"): + echo getPrimaryIPAddr() # "192.168.1.2" let socket = if dest.family == IpAddressFamily.IPv4: newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) From 1bed7773146d6acf704b0ae5cfe62455f7a127a1 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 5 Jul 2021 18:04:07 +0200 Subject: [PATCH 2306/3768] fixes #18411 (#18432) [backport:1.4] --- lib/system/excpt.nim | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index 5b7d4d49f..9f65db2fe 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -446,9 +446,7 @@ proc raiseExceptionAux(e: sink(ref Exception)) {.nodestroy.} = pushCurrentException(e) {.emit: "throw `e`;".} elif defined(nimQuirky) or gotoBasedExceptions: - # XXX This check should likely also be done in the setjmp case below. - if e != currException: - pushCurrentException(e) + pushCurrentException(e) when gotoBasedExceptions: inc nimInErrorMode else: From 927a832dde9403492d4a2838780bf59bd1a48177 Mon Sep 17 00:00:00 2001 From: pyautogui <79544976+pyautogui@users.noreply.github.com> Date: Mon, 5 Jul 2021 10:12:58 -0700 Subject: [PATCH 2307/3768] deprecate std/mersenne (#18395) Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Co-authored-by: Timothee Cour Co-authored-by: Andreas Rumpf --- changelog.md | 2 ++ lib/pure/mersenne.nim | 12 ++++-------- tests/stdlib/tmersenne.nim | 1 + 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/changelog.md b/changelog.md index 6672f7538..a4c5b4e62 100644 --- a/changelog.md +++ b/changelog.md @@ -4,6 +4,8 @@ ## Changes affecting backward compatibility +- Deprecated `std/mersenne` + - `cuchar` now aliases `uint8` instead of `char` - `repr` now doesn't insert trailing newline; previous behavior was very inconsistent, diff --git a/lib/pure/mersenne.nim b/lib/pure/mersenne.nim index 6778e2d62..6ec0d66b4 100644 --- a/lib/pure/mersenne.nim +++ b/lib/pure/mersenne.nim @@ -2,24 +2,20 @@ # # Nim's Runtime Library # (c) Copyright 2015 Nim Contributors -# # See the file "copying.txt", included in this # distribution, for details about the copyright. -# - ## The [Mersenne Twister](https://en.wikipedia.org/wiki/Mersenne_Twister) ## random number generator. -## -## **Note:** The procs in this module work at compile-time. +## .. note:: The procs in this module work at compile-time. + +{.deprecated: "use `std/random` instead.".} runnableExamples: var rand = newMersenneTwister(uint32.high) ## must be "var" doAssert rand.getNum() != rand.getNum() ## pseudorandom number - ## See also ## ======== ## * `random module`_ for Nim's standard random number generator - type MersenneTwister* = object ## The Mersenne Twister. @@ -35,6 +31,7 @@ proc newMersenneTwister*(seed: uint32): MersenneTwister = (result.mt[i-1] shr 30'u32)) + i) proc generateNumbers(m: var MersenneTwister) = + for i in 0..623: var y = (m.mt[i] and 0x80000000'u32) + (m.mt[(i+1) mod 624] and 0x7fffffff'u32) @@ -48,7 +45,6 @@ proc getNum*(m: var MersenneTwister): uint32 = generateNumbers(m) result = m.mt[m.index] m.index = (m.index + 1) mod m.mt.len - result = result xor (result shr 11'u32) result = result xor ((result shl 7'u32) and 0x9d2c5680'u32) result = result xor ((result shl 15'u32) and 0xefc60000'u32) diff --git a/tests/stdlib/tmersenne.nim b/tests/stdlib/tmersenne.nim index 2707aa2f2..54eb7b216 100644 --- a/tests/stdlib/tmersenne.nim +++ b/tests/stdlib/tmersenne.nim @@ -7,5 +7,6 @@ template main() = doAssert mt.getNum == 1071751096'u32 doAssert mt.getNum == 3805347140'u32 + static: main() main() From 36654fd577f46c49191c5e6f7e499ea31193018c Mon Sep 17 00:00:00 2001 From: Antonis Geralis <43617260+planetis-m@users.noreply.github.com> Date: Mon, 5 Jul 2021 21:55:46 +0300 Subject: [PATCH 2308/3768] Cleanup interval and timeout procs (#18431) clear/setTimeout procs were using ref ref Timeout. Added more interval overloads. Corrections. --- lib/js/dom.nim | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/lib/js/dom.nim b/lib/js/dom.nim index ca325fdd2..3f0b9f630 100644 --- a/lib/js/dom.nim +++ b/lib/js/dom.nim @@ -1301,7 +1301,7 @@ type width*: int TimeOut* {.importc.} = ref object of RootObj - Interval* {.importc.} = object of RootObj + Interval* {.importc.} = ref object of RootObj AddEventListenerOptions* = object capture*: bool @@ -1481,6 +1481,8 @@ else: proc setTimeout*(action: proc(); ms: int): TimeOut {.importc, nodecl.} proc clearTimeout*(t: TimeOut) {.importc, nodecl.} +proc setInterval*(action: proc(); ms: int): Interval {.importc, nodecl.} +proc clearInterval*(i: Interval) {.importc, nodecl.} {.push importcpp.} @@ -1494,8 +1496,8 @@ proc removeEventListener*(et: EventTarget; ev: cstring; cb: proc(ev: Event)) proc alert*(w: Window, msg: cstring) proc back*(w: Window) proc blur*(w: Window) -proc clearInterval*(w: Window, interval: ref Interval) -proc clearTimeout*(w: Window, timeout: ref TimeOut) +proc clearInterval*(w: Window, interval: Interval) +proc clearTimeout*(w: Window, timeout: TimeOut) proc close*(w: Window) proc confirm*(w: Window, msg: cstring): bool proc disableExternalCapture*(w: Window) @@ -1520,10 +1522,10 @@ proc resizeTo*(w: Window, x, y: int) proc routeEvent*(w: Window, event: Event) proc scrollBy*(w: Window, x, y: int) proc scrollTo*(w: Window, x, y: int) -proc setInterval*(w: Window, code: cstring, pause: int): ref Interval -proc setInterval*(w: Window, function: proc (), pause: int): ref Interval -proc setTimeout*(w: Window, code: cstring, pause: int): ref TimeOut -proc setTimeout*(w: Window, function: proc (), pause: int): ref Interval +proc setInterval*(w: Window, code: cstring, pause: int): Interval +proc setInterval*(w: Window, function: proc (), pause: int): Interval +proc setTimeout*(w: Window, code: cstring, pause: int): TimeOut +proc setTimeout*(w: Window, function: proc (), pause: int): Interval proc stop*(w: Window) proc requestAnimationFrame*(w: Window, function: proc (time: float)): int proc cancelAnimationFrame*(w: Window, id: int) @@ -1768,4 +1770,4 @@ since (1, 3): proc readAsDataURL*(f: FileReader, b: Blob) {.importcpp: "#.readAsDataURL(#)".} ## https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsDataURL proc readAsText*(f: FileReader, b: Blob|File, encoding = cstring"UTF-8") {.importcpp: "#.readAsText(#, #)".} - ## https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsText \ No newline at end of file + ## https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsText From c522f7f33ca08e6a441ebbb08ea9d2d79a3c500c Mon Sep 17 00:00:00 2001 From: Antonis Geralis <43617260+planetis-m@users.noreply.github.com> Date: Tue, 6 Jul 2021 08:21:50 +0300 Subject: [PATCH 2309/3768] Add entry (#18434) --- changelog.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index a4c5b4e62..028743bab 100644 --- a/changelog.md +++ b/changelog.md @@ -86,8 +86,8 @@ - Deprecated `proc reversed*[T](a: openArray[T], first: Natural, last: int): seq[T]` in `std/algorithm`. -- In `std/macros`, `treeRepr,lispRepr,astGenRepr` now represent SymChoice nodes in a collapsed way, - use `-d:nimLegacyMacrosCollapseSymChoice` to get previous behavior. +- In `std/macros`, `treeRepr,lispRepr,astGenRepr` now represent SymChoice nodes in a collapsed way, + use `-d:nimLegacyMacrosCollapseSymChoice` to get previous behavior. - The configuration subsystem now allows for `-d:release` and `-d:danger` to work as expected. The downside is that these defines now have custom logic that doesn't apply for @@ -95,6 +95,8 @@ - Renamed `-d:nimCompilerStackraceHints` to `-d:nimCompilerStacktraceHints`. +- In `std/dom`, `Interval` is now a `ref object`, same as `Timeout`. Definitions of `setTimeout`, + `clearTimeout`, `setInterval`, `clearInterval` were updated. ## Standard library additions and changes @@ -341,6 +343,8 @@ - Added `dom.scrollIntoView` proc with options +- Added `dom.setInterval`, `dom.clearInterval` overloads. + ## Language changes - `nimscript` now handles `except Exception as e`. From 252eea8cae33e486b179398e193aea9459954338 Mon Sep 17 00:00:00 2001 From: Jason Beetham Date: Mon, 5 Jul 2021 23:28:38 -0600 Subject: [PATCH 2310/3768] Make procedure mismatch more informative with pragma/call convention mismatches (#18384) * Added more concise calling convention/pragma mismatch messages * Now only adds callConvMsg/lock message when sensible * Fixed message formatting * Added tests, and fixed some bugs * Tests joined, and always indenting * More tests and more bug fixes * Fixed first test in tprocmismatch * Using var param for writting mismatches * Better logic for handling proc type rel and conv/pragma mismatch * Refactored getProcConvMismatch * Fixed callConv message formatting * Fixed test for proper message * Cleanup to address issues * getProcConvMismatch now returns tuple, and reformatted code --- compiler/semcall.nim | 8 ++- compiler/sigmatch.nim | 33 +----------- compiler/types.nim | 87 ++++++++++++++++++++++++++++++++ tests/errmsgs/tproc_mismatch.nim | 74 +++++++++++++++++++++++++++ 4 files changed, 170 insertions(+), 32 deletions(-) create mode 100644 tests/errmsgs/tproc_mismatch.nim diff --git a/compiler/semcall.nim b/compiler/semcall.nim index b56a1dbac..cec656fc2 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -247,7 +247,13 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): candidates.add typeToString(got) candidates.addDeclaredLocMaybe(c.config, got) doAssert wanted != nil - if got != nil: effectProblem(wanted, got, candidates, c) + if got != nil: + if got.kind == tyProc and wanted.kind == tyProc: + # These are proc mismatches so, + # add the extra explict detail of the mismatch + candidates.addPragmaAndCallConvMismatch(wanted, got, c.config) + effectProblem(wanted, got, candidates, c) + of kUnknown: discard "do not break 'nim check'" candidates.add "\n" if err.firstMismatch.arg == 1 and nArg.kind == nkTupleConstr and diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 071685057..73ab5e9a3 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -86,21 +86,6 @@ type TTypeRelFlags* = set[TTypeRelFlag] - TTypeRelation* = enum # order is important! - isNone, isConvertible, - isIntConv, - isSubtype, - isSubrange, # subrange of the wanted type; no type conversion - # but apart from that counts as ``isSubtype`` - isBothMetaConvertible # generic proc parameter was matched against - # generic type, e.g., map(mySeq, x=>x+1), - # maybe recoverable by rerun if the parameter is - # the proc's return value - isInferred, # generic proc was matched against a concrete type - isInferredConvertible, # same as above, but requiring proc CC conversion - isGeneric, - isFromIntLit, # conversion *from* int literal; proven safe - isEqual const isNilConversion = isConvertible # maybe 'isIntConv' fits better? @@ -640,23 +625,9 @@ proc procTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = return isNone elif a[0] != nil: return isNone + + result = getProcConvMismatch(c.c.config, f, a, result)[1] - if tfNoSideEffect in f.flags and tfNoSideEffect notin a.flags: - return isNone - elif tfThread in f.flags and a.flags * {tfThread, tfNoSideEffect} == {} and - optThreadAnalysis in c.c.config.globalOptions: - # noSideEffect implies ``tfThread``! - return isNone - elif f.flags * {tfIterator} != a.flags * {tfIterator}: - return isNone - elif f.callConv != a.callConv: - # valid to pass a 'nimcall' thingie to 'closure': - if f.callConv == ccClosure and a.callConv == ccNimCall: - result = if result == isInferred: isInferredConvertible - elif result == isBothMetaConvertible: isBothMetaConvertible - else: isConvertible - else: - return isNone when useEffectSystem: if compatibleEffects(f, a) != efCompat: return isNone when defined(drnim): diff --git a/compiler/types.nim b/compiler/types.nim index bc970a6d2..bdf08ffe9 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -26,6 +26,29 @@ type # most useful, shows: symbol + resolved symbols if it differs, e.g.: # tuple[a: MyInt{int}, b: float] + TTypeRelation* = enum # order is important! + isNone, isConvertible, + isIntConv, + isSubtype, + isSubrange, # subrange of the wanted type; no type conversion + # but apart from that counts as ``isSubtype`` + isBothMetaConvertible # generic proc parameter was matched against + # generic type, e.g., map(mySeq, x=>x+1), + # maybe recoverable by rerun if the parameter is + # the proc's return value + isInferred, # generic proc was matched against a concrete type + isInferredConvertible, # same as above, but requiring proc CC conversion + isGeneric, + isFromIntLit, # conversion *from* int literal; proven safe + isEqual + + ProcConvMismatch* = enum + pcmNoSideEffect + pcmNotGcSafe + pcmLockDifference + pcmNotIterator + pcmDifferentCallConv + proc typeToString*(typ: PType; prefer: TPreferedDesc = preferName): string template `$`*(typ: PType): string = typeToString(typ) @@ -1471,6 +1494,68 @@ proc skipHiddenSubConv*(n: PNode; g: ModuleGraph; idgen: IdGenerator): PNode = else: result = n +proc getProcConvMismatch*(c: ConfigRef, f, a: PType, rel = isNone): (set[ProcConvMismatch], TTypeRelation) = + ## Returns a set of the reason of mismatch, and the relation for conversion. + result[1] = rel + if tfNoSideEffect in f.flags and tfNoSideEffect notin a.flags: + # Formal is pure, but actual is not + result[0].incl pcmNoSideEffect + result[1] = isNone + + if tfThread in f.flags and a.flags * {tfThread, tfNoSideEffect} == {} and + optThreadAnalysis in c.globalOptions: + # noSideEffect implies ``tfThread``! + result[0].incl pcmNotGcSafe + result[1] = isNone + + if f.flags * {tfIterator} != a.flags * {tfIterator}: + # One of them is an iterator so not convertible + result[0].incl pcmNotIterator + result[1] = isNone + + if f.callConv != a.callConv: + # valid to pass a 'nimcall' thingie to 'closure': + if f.callConv == ccClosure and a.callConv == ccNimCall: + case result[1] + of isInferred: result[1] = isInferredConvertible + of isBothMetaConvertible: result[1] = isBothMetaConvertible + elif result[1] != isNone: result[1] = isConvertible + else: + result[1] = isNone + result[0].incl pcmDifferentCallConv + + if f.lockLevel.ord != UnspecifiedLockLevel.ord and + a.lockLevel.ord != UnspecifiedLockLevel.ord: + # proctypeRel has more logic to catch this difference, + # so dont need to do `rel = isNone` + # but it's a pragma mismatch reason which is why it's here + result[0].incl pcmLockDifference + +proc addPragmaAndCallConvMismatch*(message: var string, formal, actual: PType, conf: ConfigRef) = + assert formal.kind == tyProc and actual.kind == tyProc + let (convMismatch, _) = getProcConvMismatch(conf, formal, actual) + var + gotPragmas = "" + expectedPragmas = "" + for reason in convMismatch: + case reason + of pcmDifferentCallConv: + message.add "\n Calling convention mismatch: got '{.$1.}', but expected '{.$2.}'." % [$actual.callConv, $formal.callConv] + of pcmNoSideEffect: + expectedPragmas.add "noSideEffect, " + of pcmNotGcSafe: + expectedPragmas.add "gcsafe, " + of pcmLockDifference: + gotPragmas.add("locks: " & $actual.lockLevel & ", ") + expectedPragmas.add("locks: " & $formal.lockLevel & ", ") + of pcmNotIterator: discard + + if expectedPragmas.len > 0: + gotPragmas.setLen(max(0, gotPragmas.len - 2)) # Remove ", " + expectedPragmas.setLen(max(0, expectedPragmas.len - 2)) # Remove ", " + message.add "\n Pragma mismatch: got '{.$1.}', but expected '{.$2.}'." % [gotPragmas, expectedPragmas] + + proc typeMismatch*(conf: ConfigRef; info: TLineInfo, formal, actual: PType, n: PNode) = if formal.kind != tyError and actual.kind != tyError: let actualStr = typeToString(actual) @@ -1491,6 +1576,7 @@ proc typeMismatch*(conf: ConfigRef; info: TLineInfo, formal, actual: PType, n: P if verbose: msg.addDeclaredLoc(conf, formal) if formal.kind == tyProc and actual.kind == tyProc: + msg.addPragmaAndCallConvMismatch(formal, actual, conf) case compatibleEffects(formal, actual) of efCompat: discard of efRaisesDiffer: @@ -1503,6 +1589,7 @@ proc typeMismatch*(conf: ConfigRef; info: TLineInfo, formal, actual: PType, n: P msg.add "\n.tag effect is 'any tag allowed'" of efLockLevelsDiffer: msg.add "\nlock levels differ" + if formal.kind == tyEnum and actual.kind == tyEnum: msg.add "\nmaybe use `-d:nimLegacyConvEnumEnum` for a transition period" localError(conf, info, msg) diff --git a/tests/errmsgs/tproc_mismatch.nim b/tests/errmsgs/tproc_mismatch.nim new file mode 100644 index 000000000..400c3d441 --- /dev/null +++ b/tests/errmsgs/tproc_mismatch.nim @@ -0,0 +1,74 @@ +discard """ + action: reject + cmd: '''nim check --hints:off $options $file''' + nimoutFull: true + nimout: ''' +tproc_mismatch.nim(35, 52) Error: type mismatch: got but expected 'proc (a: int, c: float){.closure, noSideEffect.}' + Calling convention mismatch: got '{.cdecl.}', but expected '{.closure.}'. +tproc_mismatch.nim(39, 6) Error: type mismatch: got +but expected one of: +proc bar(a: proc ()) + first type mismatch at position: 1 + required type for a: proc (){.closure.} + but expression 'fn1' is of type: proc (){.inline, noSideEffect, gcsafe, locks: 0.} + Calling convention mismatch: got '{.inline.}', but expected '{.closure.}'. + +expression: bar(fn1) +tproc_mismatch.nim(43, 8) Error: type mismatch: got but expected 'proc (){.closure.}' + Calling convention mismatch: got '{.inline.}', but expected '{.closure.}'. +tproc_mismatch.nim(48, 8) Error: type mismatch: got but expected 'proc (){.closure, noSideEffect.}' + Pragma mismatch: got '{..}', but expected '{.noSideEffect.}'. +tproc_mismatch.nim(52, 8) Error: type mismatch: got but expected 'proc (a: float){.closure.}' +tproc_mismatch.nim(61, 9) Error: type mismatch: got but expected 'proc (a: int){.closure, gcsafe.}' + Pragma mismatch: got '{..}', but expected '{.gcsafe.}'. +tproc_mismatch.nim(69, 9) Error: type mismatch: got but expected 'proc (a: int): int{.cdecl.}' + Calling convention mismatch: got '{.nimcall.}', but expected '{.cdecl.}'. +tproc_mismatch.nim(70, 9) Error: type mismatch: got but expected 'proc (a: int): int{.nimcall.}' + Calling convention mismatch: got '{.cdecl.}', but expected '{.nimcall.}'. +tproc_mismatch.nim(74, 9) Error: type mismatch: got but expected 'proc (a: int){.closure, locks: 1.}' + Pragma mismatch: got '{.locks: 3.}', but expected '{.locks: 1.}'. +lock levels differ +''' +""" +block: # CallConv mismatch + func a(a: int, c: float) {.cdecl.} = discard + var b: proc(a: int, c: float) {.noSideEffect.} = a +block: # Parameter CallConv mismatch + proc fn1() {.inline.} = discard + proc bar(a: proc()) = discard + bar(fn1) +block: # CallConv mismatch + proc fn1() {.inline.} = discard + var fn: proc() + fn = fn1 +block: # Pragma mismatch + var a = "" + proc fn1() = a.add "b" + var fn: proc() {.noSideEffect.} + fn = fn1 +block: # Fail match not do to Pragma or CallConv + proc fn1(a: int) = discard + var fn: proc(a: float) + fn = fn1 +block: # Infered noSideEffect assign + type Foo = ref object + x0: int + var g0 = Foo(x0: 1) + proc fn1(a: int) = g0.x0 = a + var fn2: proc(a: int) + var fn3: proc(a: int) {.gcsafe.} + fn2 = fn1 + fn3 = fn1 +block: # Indrection through pragmas + {.pragma: inl1, inline.} + {.pragma: inl2, inline.} + {.pragma: p1, nimcall.} + {.pragma: p2, cdecl.} + var fn1: proc(a: int): int {.inl1, p1.} + var fn2: proc(a: int): int {.inl2, p2.} + fn2 = fn1 + fn1 = fn2 +block: # Lock levels differ + var fn1: proc(a: int){.locks: 3.} + var fn2: proc(a: int){.locks: 1.} + fn2 = fn1 From b88496ac67bea92c40cf1d0f3dc55b0fa91550a4 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Tue, 6 Jul 2021 21:09:56 +0300 Subject: [PATCH 2311/3768] fix compilation on Debian 7 (no git -C) (#18427) * fix compilation on Debian 7 (no git -C) * address review * allow specify branch for testing unmerged csources * temporarily change csources checkout parameters for testing * Update tools/deps.nim Co-authored-by: Timothee Cour * fix failure * Update config/build_config.txt * set proper git branch/hash Co-authored-by: Timothee Cour Co-authored-by: Andreas Rumpf --- build_all.bat | 2 +- ci/build_autogen.bat | 2 +- ci/funs.sh | 8 ++++++-- config/build_config.txt | 3 ++- tools/ci_generate.nim | 2 +- tools/deps.nim | 9 +++++++-- tools/niminst/makefile.nimf | 3 ++- 7 files changed, 20 insertions(+), 9 deletions(-) diff --git a/build_all.bat b/build_all.bat index 1bff761b3..c8ba05e19 100644 --- a/build_all.bat +++ b/build_all.bat @@ -10,7 +10,7 @@ SET nim_csources=bin\nim_csources_%nim_csourcesHash%.exe echo "building from csources: %nim_csources%" if not exist %nim_csourcesDir% ( - git clone -q --depth 1 %nim_csourcesUrl% %nim_csourcesDir% + git clone -q --depth 1 -b %nim_csourcesBranch% %nim_csourcesUrl% %nim_csourcesDir% ) if not exist %nim_csources% ( diff --git a/ci/build_autogen.bat b/ci/build_autogen.bat index 8b1113175..0b6ca566e 100644 --- a/ci/build_autogen.bat +++ b/ci/build_autogen.bat @@ -10,7 +10,7 @@ SET nim_csources=bin\nim_csources_%nim_csourcesHash%.exe echo "building from csources: %nim_csources%" if not exist %nim_csourcesDir% ( - git clone -q --depth 1 %nim_csourcesUrl% %nim_csourcesDir% + git clone -q --depth 1 -b %nim_csourcesBranch% %nim_csourcesUrl% %nim_csourcesDir% ) if not exist %nim_csources% ( diff --git a/ci/funs.sh b/ci/funs.sh index 4a8887658..e25922c8c 100644 --- a/ci/funs.sh +++ b/ci/funs.sh @@ -120,8 +120,12 @@ nimBuildCsourcesIfNeeded(){ else # Note: using git tags would allow fetching just what's needed, unlike git hashes, e.g. # via `git clone -q --depth 1 --branch $tag $nim_csourcesUrl`. - echo_run git clone -q --depth 1 $nim_csourcesUrl "$nim_csourcesDir" - echo_run git -C "$nim_csourcesDir" checkout $nim_csourcesHash + echo_run git clone -q --depth 1 -b $nim_csourcesBranch \ + $nim_csourcesUrl "$nim_csourcesDir" + # old `git` versions don't support -C option, using `cd` explicitly: + echo_run cd "$nim_csourcesDir" + echo_run git checkout $nim_csourcesHash + echo_run cd "$OLDPWD" # if needed we could also add: `git reset --hard $nim_csourcesHash` fi _nimBuildCsourcesIfNeeded "$@" diff --git a/config/build_config.txt b/config/build_config.txt index 2484d7a52..75dbb1da3 100644 --- a/config/build_config.txt +++ b/config/build_config.txt @@ -1,4 +1,5 @@ nim_comment="key-value pairs for windows/posix bootstrapping build scripts" nim_csourcesDir=csources_v1 nim_csourcesUrl=https://github.com/nim-lang/csources_v1.git -nim_csourcesHash=a8a5241f9475099c823cfe1a5e0ca4022ac201ff +nim_csourcesBranch=master +nim_csourcesHash=9a7f751d23c49c75a0b6f63a234c575dc0df7231 diff --git a/tools/ci_generate.nim b/tools/ci_generate.nim index 52b84f0d8..ecf0023d3 100644 --- a/tools/ci_generate.nim +++ b/tools/ci_generate.nim @@ -61,7 +61,7 @@ SET nim_csources=bin\nim_csources_%nim_csourcesHash%.exe echo "building from csources: %nim_csources%" if not exist %nim_csourcesDir% ( - git clone -q --depth 1 %nim_csourcesUrl% %nim_csourcesDir% + git clone -q --depth 1 -b %nim_csourcesBranch% %nim_csourcesUrl% %nim_csourcesDir% ) if not exist %nim_csources% ( diff --git a/tools/deps.nim b/tools/deps.nim index 8ed95b59a..95b2916a1 100644 --- a/tools/deps.nim +++ b/tools/deps.nim @@ -28,8 +28,13 @@ proc cloneDependency*(destDirBase: string, url: string, commit = commitHead, # from failing execRetry fmt"git clone -q {url} {destDir2}" if isGitRepo(destDir): - execRetry fmt"git -C {destDir2} fetch -q" - exec fmt"git -C {destDir2} checkout -q {commit}" + let oldDir = getCurrentDir() + setCurrentDir(destDir2) + try: + execRetry "git fetch -q" + exec fmt"git checkout -q {commit}" + finally: + setCurrentDir(oldDir) elif allowBundled: discard "this dependency was bundled with Nim, don't do anything" else: diff --git a/tools/niminst/makefile.nimf b/tools/niminst/makefile.nimf index aff0c5da0..8c40cf50f 100644 --- a/tools/niminst/makefile.nimf +++ b/tools/niminst/makefile.nimf @@ -25,7 +25,8 @@ endif ifeq ($(uos),linux) myos = linux - LDFLAGS += -ldl -lm + # add -lrt to avoid "undefined reference to `clock_gettime'" with glibc<2.17 + LDFLAGS += -ldl -lm -lrt endif ifeq ($(uos),dragonfly) myos = freebsd From b72ecaf639ca7b2edf5a762cfea3a11f7cc5da9a Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 6 Jul 2021 21:00:59 -0700 Subject: [PATCH 2312/3768] fix shebangs #! /xxx => #!/xxx (#18444) --- build_all.sh | 2 +- lib/pure/unidecode/gen.py | 2 +- tools/ci_generate.nim | 2 +- tools/niminst/buildsh.nimf | 2 +- tools/niminst/deinstall.nimf | 2 +- tools/niminst/install.nimf | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/build_all.sh b/build_all.sh index 37374aa7c..fb3d15d45 100755 --- a/build_all.sh +++ b/build_all.sh @@ -1,4 +1,4 @@ -#! /bin/sh +#!/bin/sh # DO NO EDIT DIRECTLY! auto-generated by `nim r tools/ci_generate.nim` # build development version of the compiler; can be rerun safely. diff --git a/lib/pure/unidecode/gen.py b/lib/pure/unidecode/gen.py index 0b180a381..2fb69f7b2 100644 --- a/lib/pure/unidecode/gen.py +++ b/lib/pure/unidecode/gen.py @@ -1,4 +1,4 @@ -#! usr/bin/env python3 +#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Generates the unidecode.dat module diff --git a/tools/ci_generate.nim b/tools/ci_generate.nim index ecf0023d3..a8a80e026 100644 --- a/tools/ci_generate.nim +++ b/tools/ci_generate.nim @@ -82,7 +82,7 @@ if not exist %nim_csources% ( proc genPosixScript(): string = result = fmt""" -#! /bin/sh +#!/bin/sh # {doNotEdit} # build development version of the compiler; can be rerun safely. diff --git a/tools/niminst/buildsh.nimf b/tools/niminst/buildsh.nimf index e7afd402e..f74413c09 100644 --- a/tools/niminst/buildsh.nimf +++ b/tools/niminst/buildsh.nimf @@ -1,6 +1,6 @@ #? stdtmpl(subsChar='?') | standard #proc generateBuildShellScript(c: ConfigData): string = -# result = "#! /bin/sh\n# Generated from niminst\n" & +# result = "#!/bin/sh\n# Generated from niminst\n" & # "# Template is in tools/niminst/buildsh.nimf\n" & # "# To regenerate run ``niminst csource`` or ``koch csource``\n" diff --git a/tools/niminst/deinstall.nimf b/tools/niminst/deinstall.nimf index 8b4477369..0dcfda75e 100644 --- a/tools/niminst/deinstall.nimf +++ b/tools/niminst/deinstall.nimf @@ -1,6 +1,6 @@ #? stdtmpl(subsChar='?') | standard #proc generateDeinstallScript(c: ConfigData): string = -# result = "#! /bin/sh\n# Generated by niminst\n" +# result = "#!/bin/sh\n# Generated by niminst\n" # var proj = c.name.toLowerAscii if [ $# -eq 1 ] ; then diff --git a/tools/niminst/install.nimf b/tools/niminst/install.nimf index a78914ecd..c2fc96e94 100644 --- a/tools/niminst/install.nimf +++ b/tools/niminst/install.nimf @@ -1,6 +1,6 @@ #? stdtmpl(subsChar = '?') | standard #proc generateInstallScript(c: ConfigData): string = -# result = "#! /bin/sh\n# Generated by niminst\n" +# result = "#!/bin/sh\n# Generated by niminst\n" # var proj = c.name.toLowerAscii set -e From d1447fe25d40e35d4746d570701d23333ff480a0 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 6 Jul 2021 21:04:36 -0700 Subject: [PATCH 2313/3768] major improvements to `std/wrapnils`: optimal codegen, case objects, lvalue semantics (#18435) * wrapnils now generates optimal code; also handles case objects * changelog * unsafeAddr => addr --- changelog.md | 4 + lib/std/wrapnils.nim | 185 +++++++++++++++++++-------- tests/stdlib/twrapnils.nim | 249 ++++++++++++++++++++++++++++--------- 3 files changed, 329 insertions(+), 109 deletions(-) diff --git a/changelog.md b/changelog.md index 028743bab..1deb97161 100644 --- a/changelog.md +++ b/changelog.md @@ -279,6 +279,10 @@ issues like https://github.com/nim-lang/Nim/issues/13063 (which affected error messages) for modules importing `std/wrapnils`. Added `??.` macro which returns an `Option`. + `std/wrapnils` can now be used to protect against `FieldDefect` errors in + case objects, generates optimal code (no overhead compared to manual + if-else branches), and preserves lvalue semantics which allows modifying + an expression. - Added `math.frexp` overload procs. Deprecated `c_frexp`, use `frexp` instead. diff --git a/lib/std/wrapnils.nim b/lib/std/wrapnils.nim index ed0a79d79..facba85fa 100644 --- a/lib/std/wrapnils.nim +++ b/lib/std/wrapnils.nim @@ -1,9 +1,20 @@ -## This module allows chains of field-access and indexing where the LHS can be nil. -## This simplifies code by reducing need for if-else branches around intermediate values -## that may be nil. +## This module allows evaluating expressions safely against the following conditions: +## * nil dereferences +## * field accesses with incorrect discriminant in case objects +## +## `default(T)` is returned in those cases when evaluating an expression of type `T`. +## This simplifies code by reducing need for if-else branches. ## ## Note: experimental module, unstable API. +#[ +TODO: +consider handling indexing operations, eg: +doAssert ?.default(seq[int])[3] == default(int) +]# + +import macros + runnableExamples: type Foo = ref object x1: string @@ -24,8 +35,124 @@ runnableExamples: assert (?.f2.x2.x2).x3 == nil # this terminates ?. early +runnableExamples: + # ?. also allows case object + type B = object + b0: int + case cond: bool + of false: discard + of true: + b1: float + + var b = B(cond: false, b0: 3) + doAssertRaises(FieldDefect): discard b.b1 # wrong discriminant + doAssert ?.b.b1 == 0.0 # safe + b = B(cond: true, b1: 4.5) + doAssert ?.b.b1 == 4.5 + + # lvalue semantics are preserved: + if (let p = ?.b.b1.addr; p != nil): p[] = 4.7 + doAssert b.b1 == 4.7 + +proc finalize(n: NimNode, lhs: NimNode, level: int): NimNode = + if level == 0: + result = quote: `lhs` = `n` + else: + result = quote: (let `lhs` = `n`) + +proc process(n: NimNode, lhs: NimNode, level: int): NimNode = + var n = n.copyNimTree + var it = n + let addr2 = bindSym"addr" + var old: tuple[n: NimNode, index: int] + while true: + if it.len == 0: + result = finalize(n, lhs, level) + break + elif it.kind == nnkCheckedFieldExpr: + let dot = it[0] + let obj = dot[0] + let objRef = quote do: `addr2`(`obj`) + # avoids a copy and preserves lvalue semantics, see tests + let check = it[1] + let okSet = check[1] + let kind1 = check[2] + let tmp = genSym(nskLet, "tmpCase") + let body = process(objRef, tmp, level + 1) + let tmp3 = nnkDerefExpr.newTree(tmp) + it[0][0] = tmp3 + let dot2 = nnkDotExpr.newTree(@[tmp, dot[1]]) + if old.n != nil: old.n[old.index] = dot2 + else: n = dot2 + let assgn = finalize(n, lhs, level) + result = quote do: + `body` + if `tmp3`.`kind1` notin `okSet`: break + `assgn` + break + elif it.kind in {nnkHiddenDeref, nnkDerefExpr}: + let tmp = genSym(nskLet, "tmp") + let body = process(it[0], tmp, level + 1) + it[0] = tmp + let assgn = finalize(n, lhs, level) + result = quote do: + `body` + if `tmp` == nil: break + `assgn` + break + elif it.kind == nnkCall: # consider extending to `nnkCallKinds` + # `copyNimTree` needed to avoid `typ = nil` issues + old = (it, 1) + it = it[1].copyNimTree + else: + old = (it, 0) + it = it[0] + +macro `?.`*(a: typed): auto = + ## Transforms `a` into an expression that can be safely evaluated even in + ## presence of intermediate nil pointers/references, in which case a default + ## value is produced. + let lhs = genSym(nskVar, "lhs") + let body = process(a, lhs, 0) + result = quote do: + var `lhs`: type(`a`) + block: + `body` + `lhs` + +# the code below is not needed for `?.` from options import Option, isSome, get, option, unsafeGet, UnpackDefect +macro `??.`*(a: typed): Option = + ## Same as `?.` but returns an `Option`. + runnableExamples: + import std/options + type Foo = ref object + x1: ref int + x2: int + # `?.` can't distinguish between a valid vs invalid default value, but `??.` can: + var f1 = Foo(x1: int.new, x2: 2) + doAssert (??.f1.x1[]).get == 0 # not enough to tell when the chain was valid. + doAssert (??.f1.x1[]).isSome # a nil didn't occur in the chain + doAssert (??.f1.x2).get == 2 + + var f2: Foo + doAssert not (??.f2.x1[]).isSome # f2 was nil + + doAssertRaises(UnpackDefect): discard (??.f2.x1[]).get + doAssert ?.f2.x1[] == 0 # in contrast, this returns default(int) + + let lhs = genSym(nskVar, "lhs") + let lhs2 = genSym(nskVar, "lhs") + let body = process(a, lhs2, 0) + result = quote do: + var `lhs`: Option[type(`a`)] + block: + var `lhs2`: type(`a`) + `body` + `lhs` = option(`lhs2`) + `lhs` + template fakeDot*(a: Option, b): untyped = ## See top-level example. let a1 = a # to avoid double evaluations @@ -58,51 +185,7 @@ func `[]`*[U](a: Option[U]): auto {.inline.} = if a2 != nil: result = option(a2[]) -import macros - -func replace(n: NimNode): NimNode = - if n.kind == nnkDotExpr: - result = newCall(bindSym"fakeDot", replace(n[0]), n[1]) - elif n.kind == nnkPar: - doAssert n.len == 1 - result = newCall(bindSym"option", n[0]) - elif n.kind in {nnkCall, nnkObjConstr}: - result = newCall(bindSym"option", n) - elif n.len == 0: - result = newCall(bindSym"option", n) - else: - n[0] = replace(n[0]) - result = n - -proc safeGet[T](a: Option[T]): T {.inline.} = - get(a, default(T)) - -macro `?.`*(a: untyped): auto = - ## Transforms `a` into an expression that can be safely evaluated even in - ## presence of intermediate nil pointers/references, in which case a default - ## value is produced. - result = replace(a) - result = quote do: - # `result`.val # TODO: expose a way to do this directly in std/options, e.g.: `getAsIs` - safeGet(`result`) - -macro `??.`*(a: untyped): Option = - ## Same as `?.` but returns an `Option`. - runnableExamples: - import std/options - type Foo = ref object - x1: ref int - x2: int - # `?.` can't distinguish between a valid vs invalid default value, but `??.` can: - var f1 = Foo(x1: int.new, x2: 2) - doAssert (??.f1.x1[]).get == 0 # not enough to tell when the chain was valid. - doAssert (??.f1.x1[]).isSome # a nil didn't occur in the chain - doAssert (??.f1.x2).get == 2 - - var f2: Foo - doAssert not (??.f2.x1[]).isSome # f2 was nil - from std/options import UnpackDefect - doAssertRaises(UnpackDefect): discard (??.f2.x1[]).get - doAssert ?.f2.x1[] == 0 # in contrast, this returns default(int) - - result = replace(a) +when false: + # xxx: expose a way to do this directly in std/options, e.g.: `getAsIs` + proc safeGet[T](a: Option[T]): T {.inline.} = + get(a, default(T)) diff --git a/tests/stdlib/twrapnils.nim b/tests/stdlib/twrapnils.nim index 956204355..1a66f4cd9 100644 --- a/tests/stdlib/twrapnils.nim +++ b/tests/stdlib/twrapnils.nim @@ -7,80 +7,213 @@ proc checkNotZero(x: float): float = proc main() = var witness = 0 - type Bar = object - b1: int - b2: ptr string + block: + type Bar = object + b1: int + b2: ptr string - type Foo = ref object - x1: float - x2: Foo - x3: string - x4: Bar - x5: seq[int] - x6: ptr Bar - x7: array[2, string] - x8: seq[int] - x9: ref Bar + type Foo = ref object + x1: float + x2: Foo + x3: string + x4: Bar + x5: seq[int] + x6: ptr Bar + x7: array[2, string] + x8: seq[int] + x9: ref Bar - type Gook = ref object - foo: Foo + type Gook = ref object + foo: Foo - proc fun(a: Bar): auto = a.b2 + proc fun(a: Bar): auto = a.b2 - var a: Foo + var a: Foo - var x6: ptr Bar - when nimvm: discard # pending https://github.com/timotheecour/Nim/issues/568 - else: - x6 = create(Bar) - x6.b1 = 42 - var a2 = Foo(x1: 1.0, x5: @[10, 11], x6: x6) - var a3 = Foo(x1: 1.2, x3: "abc") - a3.x2 = a3 + var x6: ptr Bar + when nimvm: discard # pending https://github.com/timotheecour/Nim/issues/568 + else: + x6 = create(Bar) + x6.b1 = 42 + var a2 = Foo(x1: 1.0, x5: @[10, 11], x6: x6) + var a3 = Foo(x1: 1.2, x3: "abc") + a3.x2 = a3 - var gook = Gook(foo: a) + var gook = Gook(foo: a) - proc initFoo(x1: float): auto = - witness.inc - result = Foo(x1: x1) + proc initFoo(x1: float): auto = + witness.inc + result = Foo(x1: x1) - doAssert ?.a.x2.x2.x1 == 0.0 - doAssert ?.a3.x2.x2.x1 == 1.2 - doAssert ?.a3.x2.x2.x3[1] == 'b' + doAssert ?.a.x2.x2.x1 == 0.0 + doAssert ?.a3.x2.x2.x1 == 1.2 + doAssert ?.a3.x2.x2.x3[1] == 'b' - doAssert ?.a3.x2.x2.x5.len == 0 - doAssert a3.x2.x2.x3.len == 3 + doAssert ?.a3.x2.x2.x5.len == 0 + doAssert a3.x2.x2.x3.len == 3 - doAssert ?.a.x2.x2.x3[1] == default(char) - # here we only apply wrapnil around gook.foo, not gook (and assume gook is not nil) - doAssert ?.(gook.foo).x2.x2.x1 == 0.0 + doAssert ?.a.x2.x2.x3[1] == default(char) + # here we only apply wrapnil around gook.foo, not gook (and assume gook is not nil) + doAssert ?.(gook.foo).x2.x2.x1 == 0.0 - when nimvm: discard - else: - doAssert ?.a2.x6[] == Bar(b1: 42) # deref for ptr Bar + when nimvm: discard + else: + doAssert ?.a2.x6[] == Bar(b1: 42) # deref for ptr Bar - doAssert ?.a2.x1.checkNotZero == 1.0 - doAssert a == nil - # shows that checkNotZero won't be called if a nil is found earlier in chain - doAssert ?.a.x1.checkNotZero == 0.0 + doAssert ?.a2.x1.checkNotZero == 1.0 + doAssert a == nil + # shows that checkNotZero won't be called if a nil is found earlier in chain + doAssert ?.a.x1.checkNotZero == 0.0 - when nimvm: discard - else: - # checks that a chain without nil but with an empty seq still raises - doAssertRaises(IndexDefect): discard ?.a2.x8[3] + when nimvm: discard + else: + # checks that a chain without nil but with an empty seq still raises + doAssertRaises(IndexDefect): discard ?.a2.x8[3] - # make sure no double evaluation bug - doAssert witness == 0 - doAssert ?.initFoo(1.3).x1 == 1.3 - doAssert witness == 1 + # make sure no double evaluation bug + doAssert witness == 0 + doAssert ?.initFoo(1.3).x1 == 1.3 + doAssert witness == 1 - # here, it's used twice, to deref `ref Bar` and then `ptr string` - doAssert ?.a.x9[].fun[] == "" + # here, it's used twice, to deref `ref Bar` and then `ptr string` + doAssert ?.a.x9[].fun[] == "" - block: # `??.` - doAssert (??.a3.x2.x2.x3.len).get == 3 - doAssert (??.a2.x4).isSome - doAssert not (??.a.x4).isSome + block: # `??.` + doAssert (??.a3.x2.x2.x3.len).get == 3 + doAssert (??.a2.x4).isSome + doAssert not (??.a.x4).isSome + + block: + type + A = object + b: B + B = object + c: C + C = object + d: D + D = ref object + e: E + e2: array[2, E] + e3: seq[E] + d3: D + i4: int + E = object + f: int + d2: D + proc identity[T](a: T): T = a + proc identity2[T](a: T, ignore: int): T = a + var a: A + doAssert ?.a.b.c.d.e.f == 0 + doAssert ?.a.b.c.d.e.d2.d3[].d3.e.d2.e.f == 0 + doAssert ?.a.b.c.d.d3[].e.f == 0 + doAssert ?.a.b.c.d.e2[0].d2.e3[0].f == 0 + doAssert ?.a == A.default + doAssert ?.a.b.c.d.e == E.default + doAssert ?.a.b.c.d.e.d2 == nil + + doAssert ?.a.identity.b.c.identity2(12).d.d3.e.f == 0 + doAssert ?.a.b.c.d.d3.e2[0].f == 0 + a.b.c.d = D() + a.b.c.d.d3 = a.b.c.d + a.b.c.d.e2[0].f = 5 + doAssert ?.a.b.c.d.d3.e2[0].f == 5 + + var d: D = nil + doAssert ?.d.identity.i4 == 0 + doAssert ?.d.i4.identity == 0 + + block: # case objects + type + Kind = enum k0, k1, k2 + V = object + case kind: Kind + of k0: + x0: int + of k1: + x1: int + of k2: + x2: int + A = object + v0: V + + block: + var a = V(kind: k0, x0: 3) + doAssert ?.a.x0 == 3 + doAssert ?.a.x1 == 0 + a = V(kind: k1, x1: 5) + doAssert ?.a.x0 == 0 + doAssert ?.a.x1 == 5 + + block: + var a = A(v0: V(kind: k0, x0: 10)) + doAssert ?.a.v0.x0 == 10 + doAssert ?.a.v0.x1 == 0 + a.v0 = V(kind: k2, x2: 8) + doAssert ?.a.v0.x0 == 0 + doAssert ?.a.v0.x1 == 0 + doAssert ?.a.v0.x2 == 8 + + block: # `nnkCall` + type + A = object + a0: int + d: D + D = ref object + i4: int + + proc identity[T](a: T): T = a + var d: D = nil + doAssert ?.d.i4.identity == 0 + doAssert ?.identity(?.d.i4) == 0 + doAssert ?.identity(d.i4) == 0 + doAssert ?.identity(d) == nil + doAssert ?.identity(d[]) == default(typeof(d[])) + doAssert ?.identity(d[]).i4 == 0 + var a: A + doAssert ?.identity(a) == default(A) + doAssert ?.identity(a.a0) == 0 + doAssert ?.identity(a.d) == nil + doAssert ?.identity(a.d.i4) == 0 + + block: # lvalue semantic propagation + type + A = ref object + a0: A + a1: seq[A] + a2: int + + B = object + b0: int + case cond: bool + of false: discard + of true: + b1: float + + block: + var a: A + doAssert ?.a.a0.a1[0].a2.addr == nil + a = A(a2: 3) + doAssert ?.a.a0.a1[0].a2.addr == nil + a.a0 = a + a.a1 = @[a] + let p = ?.a.a0.a1[0].a2.addr + doAssert p != nil + p[] = 5 + doAssert a.a2 == 5 + + block: + var b = B(cond: false, b0: 3) + let p = ?.b.b1.addr + doAssert p == nil + b = B(cond: true, b1: 4.5) + let p2 = ?.b.b1.addr + doAssert p2 != nil + p2[] = 4.6 + doAssert b.b1 == 4.6 + # useful pattern, impossible with Options + if (let p3 = ?.b.b1.addr; p3 != nil): + p3[] = 4.7 + doAssert b.b1 == 4.7 main() static: main() From 3eb3e6b9a38aed1f26014bd5dece216b0c625b1a Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 7 Jul 2021 09:39:01 +0200 Subject: [PATCH 2314/3768] ORC: use =destroy instead of =dispose (#18440) * ORC refactoring in preparation for further changes (=dispose must die) * ORC: embrace =destroy, avoid =dispose * ORC: no need for =dispose * closes #18421 --- compiler/ast.nim | 3 +- compiler/ccgtypes.nim | 5 +- compiler/liftdestructors.nim | 49 ++--------- compiler/semstmts.nim | 2 - doc/manual.rst | 2 +- lib/system.nim | 1 - lib/system/cellseqs_v2.nim | 26 +++--- lib/system/cyclebreaker.nim | 3 +- lib/system/orc.nim | 40 +++++---- tests/arc/thamming_orc.nim | 155 +++++++++++++++++++++++++++++++++++ 10 files changed, 205 insertions(+), 81 deletions(-) create mode 100644 tests/arc/thamming_orc.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index 2a2a84e76..26050426a 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -911,7 +911,6 @@ type attachedAsgn, attachedSink, attachedTrace, - attachedDispose, attachedDeepCopy TType* {.acyclic.} = object of TIdObj # \ @@ -1408,7 +1407,7 @@ const MaxLockLevel* = 1000'i16 UnknownLockLevel* = TLockLevel(1001'i16) AttachedOpToStr*: array[TTypeAttachedOp, string] = [ - "=destroy", "=copy", "=sink", "=trace", "=dispose", "=deepcopy"] + "=destroy", "=copy", "=sink", "=trace", "=deepcopy"] proc `$`*(x: TLockLevel): string = if x.ord == UnspecifiedLockLevel.ord: result = "" diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 6e5ffeaa4..67e701804 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -1328,14 +1328,13 @@ proc genTypeInfoV2Impl(m: BModule, t, origType: PType, name: Rope; info: TLineIn m.s[cfsData].addf("N_LIB_PRIVATE TNimTypeV2 $1;$n", [name]) let destroyImpl = genHook(m, t, info, attachedDestructor) let traceImpl = genHook(m, t, info, attachedTrace) - let disposeImpl = genHook(m, t, info, attachedDispose) var flags = 0 if not canFormAcycle(t): flags = flags or 1 - addf(m.s[cfsTypeInit3], "$1.destructor = (void*)$2; $1.size = sizeof($3); $1.align = NIM_ALIGNOF($3); $1.name = $4;$n; $1.traceImpl = (void*)$5; $1.disposeImpl = (void*)$6; $1.flags = $7;", [ + addf(m.s[cfsTypeInit3], "$1.destructor = (void*)$2; $1.size = sizeof($3); $1.align = NIM_ALIGNOF($3); $1.name = $4;$n; $1.traceImpl = (void*)$5; $1.flags = $6;", [ name, destroyImpl, getTypeDesc(m, t), typeName, - traceImpl, disposeImpl, rope(flags)]) + traceImpl, rope(flags)]) if t.kind == tyObject and t.len > 0 and t[0] != nil and optEnableDeepCopy in m.config.globalOptions: discard genTypeInfoV1(m, t, info) diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 43f87e179..1824557e6 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -421,11 +421,6 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool = result = considerAsgnOrSink(c, t, body, x, y, op) if op != nil: setAttachedOp(c.g, c.idgen.module, t, c.kind, op) - of attachedDispose: - var op = getAttachedOp(c.g, t, c.kind) - result = considerAsgnOrSink(c, t, body, x, nil, op) - if op != nil: - setAttachedOp(c.g, c.idgen.module, t, c.kind, op) of attachedDeepCopy: let op = getAttachedOp(c.g, t, attachedDeepCopy) if op != nil: @@ -516,9 +511,6 @@ proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = if canFormAcycle(t.elemType): # follow all elements: forallElements(c, t, body, x, y) - of attachedDispose: - forallElements(c, t, body, x, y) - body.add genBuiltin(c, mDestroy, "destroy", x) proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = createTypeBoundOps(c.g, c.c, t, body.info, c.idgen) @@ -556,11 +548,6 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = if op == nil: return # protect from recursion body.add newHookCall(c, op, x, y) - of attachedDispose: - let op = getAttachedOp(c.g, t, c.kind) - if op == nil: - return # protect from recursion - body.add newHookCall(c, op, x, nil) proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = case c.kind @@ -572,7 +559,7 @@ proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = doAssert t.destructor != nil moveCall.add destructorCall(c, t.destructor, x) body.add moveCall - of attachedDestructor, attachedDispose: + of attachedDestructor: body.add genBuiltin(c, mDestroy, "destroy", x) of attachedTrace: discard "strings are atomic and have no inner elements that are to trace" @@ -667,16 +654,6 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = # If the ref is polymorphic we have to account for this body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, genAddrOf(x, c.idgen), y) #echo "can follow ", elemType, " static ", isFinal(elemType) - of attachedDispose: - # this is crucial! dispose is like =destroy but we don't follow refs - # as that is dealt within the cycle collector. - if not isCyclic: - body.add genIf(c, cond, actions) - when false: - let cond = copyTree(x) - cond.typ = getSysType(c.g, x.info, tyBool) - actions.add callCodegenProc(c.g, "nimRawDispose", c.info, x) - body.add genIf(c, cond, actions) proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = ## Closures are really like refs except they always use a virtual destructor @@ -725,14 +702,6 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = of attachedDeepCopy: assert(false, "cannot happen") of attachedTrace: body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, genAddrOf(xenv, c.idgen), y) - of attachedDispose: - # this is crucial! dispose is like =destroy but we don't follow refs - # as that is dealt within the cycle collector. - when false: - let cond = copyTree(xenv) - cond.typ = getSysType(c.g, xenv.info, tyBool) - actions.add callCodegenProc(c.g, "nimRawDispose", c.info, xenv) - body.add genIf(c, cond, actions) proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = case c.kind @@ -756,7 +725,7 @@ proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = else: body.sons.insert(des, 0) of attachedDeepCopy: assert(false, "cannot happen") - of attachedTrace, attachedDispose: discard + of attachedTrace: discard proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = var actions = newNodeI(nkStmtList, c.info) @@ -781,7 +750,7 @@ proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = of attachedDestructor: body.add genIf(c, x, actions) of attachedDeepCopy: assert(false, "cannot happen") - of attachedTrace, attachedDispose: discard + of attachedTrace: discard proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = if c.kind == attachedDeepCopy: @@ -815,7 +784,7 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = else: body.sons.insert(des, 0) of attachedDeepCopy: assert(false, "cannot happen") - of attachedTrace, attachedDispose: discard + of attachedTrace: discard proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = let xx = genBuiltin(c, mAccessEnv, "accessEnv", x) @@ -830,7 +799,7 @@ proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = of attachedDestructor: body.add genIf(c, xx, actions) of attachedDeepCopy: assert(false, "cannot happen") - of attachedTrace, attachedDispose: discard + of attachedTrace: discard proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) = case t.kind @@ -946,7 +915,7 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp result.typ = newProcType(info, nextTypeId(idgen), owner) result.typ.addParam dest - if kind notin {attachedDestructor, attachedDispose}: + if kind != attachedDestructor: result.typ.addParam src if kind == attachedAsgn and g.config.selectedGC == gcOrc and @@ -980,7 +949,7 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp; let dest = result.typ.n[1].sym let d = newDeref(newSymNode(dest)) - let src = if kind in {attachedDestructor, attachedDispose}: newNodeIT(nkSym, info, getSysType(g, info, tyPointer)) + let src = if kind == attachedDestructor: newNodeIT(nkSym, info, getSysType(g, info, tyPointer)) else: newSymNode(result.typ.n[2].sym) # register this operation already: @@ -1098,13 +1067,13 @@ proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInf # we do not generate '=trace' nor '=dispose' procs if we # have the cycle detection disabled, saves code size. - let lastAttached = if g.config.selectedGC == gcOrc: attachedDispose + let lastAttached = if g.config.selectedGC == gcOrc: attachedTrace else: attachedSink # bug #15122: We need to produce all prototypes before entering the # mind boggling recursion. Hacks like these imply we should rewrite # this module. - var generics: array[attachedDestructor..attachedDispose, bool] + var generics: array[attachedDestructor..attachedTrace, bool] for k in attachedDestructor..lastAttached: generics[k] = getAttachedOp(g, canon, k) != nil if not generics[k]: diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 2907d5172..c3402aee3 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1753,8 +1753,6 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = "signature for '" & s.name.s & "' must be proc[T: object](x: var T; y: T)") of "=trace": bindTypeHook(c, s, n, attachedTrace) - of "=dispose": - bindTypeHook(c, s, n, attachedDispose) else: if sfOverriden in s.flags: localError(c.config, n.info, errGenerated, diff --git a/doc/manual.rst b/doc/manual.rst index a99c4b5af..9ddf02009 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -3841,7 +3841,7 @@ the operator is in scope (including if it is private). doAssert witness == 3 Type bound operators currently include: -`=destroy`, `=copy`, `=sink`, `=trace`, `=dispose`, `=deepcopy` +`=destroy`, `=copy`, `=sink`, `=trace`, `=deepcopy` (some of which are still implementation defined and not yet documented). For more details on some of those procs, see diff --git a/lib/system.nim b/lib/system.nim index a4608997a..090ab309e 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1779,7 +1779,6 @@ when not defined(js) and defined(nimV2): align: int name: cstring traceImpl: pointer - disposeImpl: pointer typeInfoV1: pointer # for backwards compat, usually nil flags: int PNimTypeV2 = ptr TNimTypeV2 diff --git a/lib/system/cellseqs_v2.nim b/lib/system/cellseqs_v2.nim index 59cf67dcd..27be48d78 100644 --- a/lib/system/cellseqs_v2.nim +++ b/lib/system/cellseqs_v2.nim @@ -10,20 +10,20 @@ # Cell seqs for cyclebreaker and cyclicrefs_v2. type - CellTuple = (PT, PNimTypeV2) - CellArray = ptr UncheckedArray[CellTuple] - CellSeq = object + CellTuple[T] = (T, PNimTypeV2) + CellArray[T] = ptr UncheckedArray[CellTuple[T]] + CellSeq[T] = object len, cap: int - d: CellArray + d: CellArray[T] -proc add(s: var CellSeq, c: PT; t: PNimTypeV2) {.inline.} = +proc add[T](s: var CellSeq[T], c: T; t: PNimTypeV2) {.inline.} = if s.len >= s.cap: s.cap = s.cap * 3 div 2 when compileOption("threads"): - var d = cast[CellArray](allocShared(uint(s.cap * sizeof(CellTuple)))) + var d = cast[CellArray[T]](allocShared(uint(s.cap * sizeof(CellTuple[T])))) else: - var d = cast[CellArray](alloc(s.cap * sizeof(CellTuple))) - copyMem(d, s.d, s.len * sizeof(CellTuple)) + var d = cast[CellArray[T]](alloc(s.cap * sizeof(CellTuple[T]))) + copyMem(d, s.d, s.len * sizeof(CellTuple[T])) when compileOption("threads"): deallocShared(s.d) else: @@ -33,15 +33,15 @@ proc add(s: var CellSeq, c: PT; t: PNimTypeV2) {.inline.} = s.d[s.len] = (c, t) inc(s.len) -proc init(s: var CellSeq, cap: int = 1024) = +proc init[T](s: var CellSeq[T], cap: int = 1024) = s.len = 0 s.cap = cap when compileOption("threads"): - s.d = cast[CellArray](allocShared(uint(s.cap * sizeof(CellTuple)))) + s.d = cast[CellArray[T]](allocShared(uint(s.cap * sizeof(CellTuple[T])))) else: - s.d = cast[CellArray](alloc(s.cap * sizeof(CellTuple))) + s.d = cast[CellArray[T]](alloc(s.cap * sizeof(CellTuple[T]))) -proc deinit(s: var CellSeq) = +proc deinit[T](s: var CellSeq[T]) = if s.d != nil: when compileOption("threads"): deallocShared(s.d) @@ -51,6 +51,6 @@ proc deinit(s: var CellSeq) = s.len = 0 s.cap = 0 -proc pop(s: var CellSeq): (PT, PNimTypeV2) = +proc pop[T](s: var CellSeq[T]): (T, PNimTypeV2) = result = s.d[s.len-1] dec s.len diff --git a/lib/system/cyclebreaker.nim b/lib/system/cyclebreaker.nim index a3159f95c..6ec0f01f7 100644 --- a/lib/system/cyclebreaker.nim +++ b/lib/system/cyclebreaker.nim @@ -53,7 +53,6 @@ depth-first traversal suffices. ]# -type PT = ptr pointer include cellseqs_v2 const @@ -78,7 +77,7 @@ proc nimMarkCyclic(p: pointer) {.compilerRtl, inl.} = discard type GcEnv = object - traceStack: CellSeq + traceStack: CellSeq[ptr pointer] proc trace(p: pointer; desc: PNimTypeV2; j: var GcEnv) {.inline.} = when false: diff --git a/lib/system/orc.nim b/lib/system/orc.nim index df81afcf1..6273e1a21 100644 --- a/lib/system/orc.nim +++ b/lib/system/orc.nim @@ -14,7 +14,6 @@ # R.D. Lins / Information Processing Letters 109 (2008) 71–78 # -type PT = Cell include cellseqs_v2 const @@ -68,10 +67,10 @@ const type GcEnv = object - traceStack: CellSeq + traceStack: CellSeq[ptr pointer] when useJumpStack: - jumpStack: CellSeq # Lins' jump stack in order to speed up traversals - toFree: CellSeq + jumpStack: CellSeq[ptr pointer] # Lins' jump stack in order to speed up traversals + toFree: CellSeq[Cell] freed, touched, edges, rcSum: int keepThreshold: bool @@ -92,13 +91,13 @@ proc free(s: Cell; desc: PNimTypeV2) {.inline.} = when logOrc: writeCell("free", s, desc) - if desc.disposeImpl != nil: - cast[DisposeProc](desc.disposeImpl)(p) + if desc.destructor != nil: + cast[DestructorProc](desc.destructor)(p) when false: cstderr.rawWrite desc.name cstderr.rawWrite " " - if desc.disposeImpl == nil: + if desc.destructor == nil: cstderr.rawWrite "lacks dispose" if desc.traceImpl != nil: cstderr.rawWrite ", but has trace\n" @@ -125,16 +124,16 @@ proc nimTraceRef(q: pointer; desc: PNimTypeV2; env: pointer) {.compilerRtl, inli orcAssert strstr(desc.name, "TType") == nil, "following a TType but it's acyclic!" var j = cast[ptr GcEnv](env) - j.traceStack.add(head p[], desc) + j.traceStack.add(p, desc) proc nimTraceRefDyn(q: pointer; env: pointer) {.compilerRtl, inline.} = let p = cast[ptr pointer](q) if p[] != nil: var j = cast[ptr GcEnv](env) - j.traceStack.add(head p[], cast[ptr PNimTypeV2](p[])[]) + j.traceStack.add(p, cast[ptr PNimTypeV2](p[])[]) var - roots {.threadvar.}: CellSeq + roots {.threadvar.}: CellSeq[Cell] proc unregisterCycle(s: Cell) = # swap with the last element. O(1) @@ -162,7 +161,8 @@ proc scanBlack(s: Cell; desc: PNimTypeV2; j: var GcEnv) = trace(s, desc, j) when logOrc: writeCell("root still alive", s, desc) while j.traceStack.len > until: - let (t, desc) = j.traceStack.pop() + let (entry, desc) = j.traceStack.pop() + let t = head entry[] inc t.rc, rcIncrement if t.color != colBlack: t.setColor colBlack @@ -187,7 +187,8 @@ proc markGray(s: Cell; desc: PNimTypeV2; j: var GcEnv) = orcAssert(j.traceStack.len == 0, "markGray: trace stack not empty") trace(s, desc, j) while j.traceStack.len > 0: - let (t, desc) = j.traceStack.pop() + let (entry, desc) = j.traceStack.pop() + let t = head entry[] dec t.rc, rcIncrement inc j.edges when useJumpStack: @@ -195,7 +196,7 @@ proc markGray(s: Cell; desc: PNimTypeV2; j: var GcEnv) = t.rc = t.rc or jumpStackFlag when traceCollector: cprintf("[Now in jumpstack] %p %ld color %ld in jumpstack %ld\n", t, t.rc shr rcShift, t.color, t.rc and jumpStackFlag) - j.jumpStack.add(t, desc) + j.jumpStack.add(entry, desc) if t.color != colGray: t.setColor colGray inc j.touched @@ -225,7 +226,8 @@ proc scan(s: Cell; desc: PNimTypeV2; j: var GcEnv) = # that are still alive; we also need to mark what they # refer to as alive: while j.jumpStack.len > 0: - let (t, desc) = j.jumpStack.pop + let (entry, desc) = j.jumpStack.pop + let t = head entry[] # not in jump stack anymore! t.rc = t.rc and not jumpStackFlag if t.color == colGray and (t.rc shr rcShift) >= 0: @@ -239,7 +241,8 @@ proc scan(s: Cell; desc: PNimTypeV2; j: var GcEnv) = s.setColor(colWhite) trace(s, desc, j) while j.traceStack.len > 0: - let (t, desc) = j.traceStack.pop() + let (entry, desc) = j.traceStack.pop() + let t = head entry[] if t.color == colGray: if (t.rc shr rcShift) >= 0: scanBlack(t, desc, j) @@ -249,7 +252,8 @@ proc scan(s: Cell; desc: PNimTypeV2; j: var GcEnv) = # that are still alive; we also need to mark what they # refer to as alive: while j.jumpStack.len > 0: - let (t, desc) = j.jumpStack.pop + let (entry, desc) = j.jumpStack.pop + let t = head entry[] # not in jump stack anymore! t.rc = t.rc and not jumpStackFlag if t.color == colGray and (t.rc shr rcShift) >= 0: @@ -285,7 +289,9 @@ proc collectColor(s: Cell; desc: PNimTypeV2; col: int; j: var GcEnv) = j.toFree.add(s, desc) trace(s, desc, j) while j.traceStack.len > 0: - let (t, desc) = j.traceStack.pop() + let (entry, desc) = j.traceStack.pop() + let t = head entry[] + entry[] = nil # ensure that the destructor does touch moribund objects! if t.color == col and t.rootIdx == 0: j.toFree.add(t, desc) t.setColor(colBlack) diff --git a/tests/arc/thamming_orc.nim b/tests/arc/thamming_orc.nim new file mode 100644 index 000000000..463f7117e --- /dev/null +++ b/tests/arc/thamming_orc.nim @@ -0,0 +1,155 @@ +discard """ + output: '''(allocCount: 1114, deallocCount: 1112) +created 439 destroyed 402''' + cmd: "nim c --gc:orc -d:nimAllocStats $file" +""" + +# bug #18421 + +# test Nim Hamming Number Lazy List algo with reference counts and not... +# compile with "-d:release -d:danger" and test with various +# memory managment GC's, allocators, threading, etc. +# it should be guaranteed to work with zero memory leaks with `--gc:orc`... + +# compile with `-d:trace20` to trace creation and destruction of first 20 values. + +from math import log2 + +# implement our own basic BigInt so the bigints library isn't necessary... +type + BigInt = object + digits: seq[uint32] +let zeroBigInt = BigInt(digits: @[ 0'u32 ]) +let oneBigInt = BigInt(digits: @[ 1'u32 ]) + +proc shladd(bi: var BigInt; n: int; a: BigInt) = + # assume that both `bi` and `a` are sized correctly with + # msuint32 for both not containing a zero + let alen = a.digits.len + let mx = max(bi.digits.len, a.digits.len) + for i in bi.digits.len ..< mx: bi.digits.add 0'u32 + var cry = 0'u64 + for i in 0 ..< alen: + cry += (bi.digits[i].uint64 shl n) + a.digits[i].uint64 + bi.digits[i] = cry.uint32; cry = cry shr 32 + for i in alen ..< mx: + cry += bi.digits[i].uint64 shl n + bi.digits[i] = cry.uint32; cry = cry shr 32 + if cry > 0'u64: + bi.digits.add cry.uint32 + +proc `$`(x: BigInt): string = + if x.digits.len == 0 or (x.digits.len == 1 and x.digits[0] == 0'u32): + return "0" + result = ""; var n = x; var msd = n.digits.high + while msd >= 0: + if n.digits[msd] == 0'u32: msd.dec; continue + var brw = 0.uint64 + for i in countdown(msd, 0): + let dvdnd = n.digits[i].uint64 + (brw shl 32) + let q = dvdnd div 10'u64; brw = dvdnd - q * 10'u64 + n.digits[i] = q.uint32 + result &= $brw + for i in 0 .. result.high shr 1: # reverse result string in place + let tmp = result[^(i + 1)] + result[^(i + 1)] = result[i] + result[i] = tmp + +type TriVal = (uint32, uint32, uint32) +type LogRep = (float64, TriVal) +type LogRepf = proc(x: LogRep): LogRep +const one: LogRep = (0.0'f64, (0'u32, 0'u32, 0'u32)) +proc `<`(me: LogRep, othr: LogRep): bool = me[0] < othr[0] + +proc convertTriVal2BigInt(tpl: TriVal): BigInt = + result = oneBigInt + let (x2, x3, x5) = tpl + for _ in 1 .. x2: result.shladd 1, zeroBigInt + for _ in 1 .. x3: result.shladd 1, result + for _ in 1 .. x5: result.shladd 2, result + +const lb2 = 1.0'f64 +const lb3 = 3.0'f64.log2 +const lb5 = 5.0'f64.log2 + +proc mul2(me: LogRep): LogRep = + let (lr, tpl) = me; let (x2, x3, x5) = tpl + (lr + lb2, (x2 + 1, x3, x5)) + +proc mul3(me: LogRep): LogRep = + let (lr, tpl) = me; let (x2, x3, x5) = tpl + (lr + lb3, (x2, x3 + 1, x5)) + +proc mul5(me: LogRep): LogRep = + let (lr, tpl) = me; let (x2, x3, x5) = tpl + (lr + lb5, (x2, x3, x5 + 1)) + +type + LazyListObj = object + hd: LogRep + tlf: proc(): LazyList {.closure.} + tl: LazyList + LazyList = ref LazyListObj + +var destroyed = 0 + +proc `=destroy`(ll: var LazyListObj) = + if ll.tlf == nil and ll.tl == nil: return + destroyed += 1 + + when defined(trace20): + echo "destroying: ", (destroyed, ll.hd[1].convertTriVal2BigInt) + if ll.tlf != nil: ll.tlf.`=destroy` + if ll.tl != nil: ll.tl.`=destroy` + #wasMoved(ll) + +proc rest(ll: LazyList): LazyList = # not thread-safe; needs lock on thunk + if ll.tlf != nil: ll.tl = ll.tlf(); ll.tlf = nil + ll.tl + +var created = 0 +iterator hammings(until: int): TriVal = + proc merge(x, y: LazyList): LazyList = + let xh = x.hd; let yh = y.hd; created += 1 + when defined(trace20): + echo "merge create: ", (created - 1, (if xh < yh: xh else: yh)[1].convertTriVal2BigInt) + if xh < yh: LazyList(hd: xh, tlf: proc(): auto = merge x.rest, y) + else: LazyList(hd: yh, tlf: proc(): auto = merge x, y.rest) + proc smult(mltf: LogRepf; s: LazyList): LazyList = + proc smults(ss: LazyList): LazyList = + when defined(trace20): + echo "mult create: ", (created, ss.hd.mltf[1].convertTriVal2BigInt) + created += 1; LazyList(hd: ss.hd.mltf, tlf: proc(): auto = ss.rest.smults) + s.smults + proc unnsm(s: LazyList, mltf: LogRepf): LazyList = + var r: LazyList = nil + when defined(trace20): + echo "first create: ", (created, one[1].convertTriVal2BigInt) + let frst = LazyList(hd: one, tlf: proc(): LazyList = r); created += 1 + r = if s == nil: smult(mltf, frst) else: s.merge smult(mltf, frst) + r + yield one[1] + var hmpll: LazyList = ((nil.unnsm mul5).unnsm mul3).unnsm mul2 + for _ in 2 .. until: + yield hmpll.hd[1]; hmpll = hmpll.rest # almost forever + +proc main = + var s = "" + for h in hammings(20): s &= $h.convertTrival2BigInt & " " + doAssert s == "1 2 3 4 5 6 8 9 10 12 15 16 18 20 24 25 27 30 32 36 ", + "Algorithmic error finding first 20 Hamming numbers!!!" + + when not defined(trace20): + var lsth: TriVal; created = 0; destroyed = 0 + for h in hammings(200): lsth = h + doAssert $lsth.convertTriVal2BigInt == "16200", + "Algorithmic error finding 200th Hamming number!!!" + +let mem = getOccupiedMem() +main() +GC_FullCollect() +let mb = getOccupiedMem() - mem +#doAssert mb == 0, "Found memory leak of " & $mb & " bytes!!!" + +echo getAllocStats() +echo "created ", created, " destroyed ", destroyed From ffce6de84c94348dd5a615c9855a14ed64807449 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 7 Jul 2021 00:43:16 -0700 Subject: [PATCH 2315/3768] improve rendering of newOSError.additionalInfo (#18443) * improve rendering of newOSError.additionalInfo * fixup --- lib/pure/includes/oserr.nim | 5 +++-- lib/pure/net.nim | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/pure/includes/oserr.nim b/lib/pure/includes/oserr.nim index b0740cbe6..e42771d52 100644 --- a/lib/pure/includes/oserr.nim +++ b/lib/pure/includes/oserr.nim @@ -80,8 +80,9 @@ proc newOSError*( e.msg = osErrorMsg(errorCode) if additionalInfo.len > 0: if e.msg.len > 0 and e.msg[^1] != '\n': e.msg.add '\n' - e.msg.add "Additional info: " - e.msg.addQuoted additionalInfo + e.msg.add "Additional info: " + e.msg.add additionalInfo + # don't add trailing `.` etc, which negatively impacts "jump to file" in IDEs. if e.msg == "": e.msg = "unknown OS error" return e diff --git a/lib/pure/net.nim b/lib/pure/net.nim index ec3e4e74d..9c3827751 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -958,7 +958,9 @@ proc bindAddr*(socket: Socket, port = Port(0), address = "") {. var aiList = getAddrInfo(realaddr, port, socket.domain) if bindAddr(socket.fd, aiList.ai_addr, aiList.ai_addrlen.SockLen) < 0'i32: freeaddrinfo(aiList) - raiseOSError(osLastError(), "address: $# port: $#" % [address, $port]) + var address2: string + address2.addQuoted address + raiseOSError(osLastError(), "address: $# port: $#" % [address2, $port]) freeaddrinfo(aiList) proc acceptAddr*(server: Socket, client: var owned(Socket), address: var string, From 2d758795f05d8edf567011eeb49f0cdd38b6dc13 Mon Sep 17 00:00:00 2001 From: Aditya Siram Date: Wed, 7 Jul 2021 21:21:30 -0500 Subject: [PATCH 2316/3768] Rename test variable due to unfortunate connotations (#18453) --- tests/stdlib/twrapnils.nim | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/stdlib/twrapnils.nim b/tests/stdlib/twrapnils.nim index 1a66f4cd9..a0549c1bc 100644 --- a/tests/stdlib/twrapnils.nim +++ b/tests/stdlib/twrapnils.nim @@ -23,7 +23,7 @@ proc main() = x8: seq[int] x9: ref Bar - type Gook = ref object + type Goo = ref object foo: Foo proc fun(a: Bar): auto = a.b2 @@ -39,7 +39,7 @@ proc main() = var a3 = Foo(x1: 1.2, x3: "abc") a3.x2 = a3 - var gook = Gook(foo: a) + var goo = Goo(foo: a) proc initFoo(x1: float): auto = witness.inc @@ -53,8 +53,8 @@ proc main() = doAssert a3.x2.x2.x3.len == 3 doAssert ?.a.x2.x2.x3[1] == default(char) - # here we only apply wrapnil around gook.foo, not gook (and assume gook is not nil) - doAssert ?.(gook.foo).x2.x2.x1 == 0.0 + # here we only apply wrapnil around goo.foo, not goo (and assume goo is not nil) + doAssert ?.(goo.foo).x2.x2.x1 == 0.0 when nimvm: discard else: From 5a42f4a53e22051493232652cda7fb3d32c16da8 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 8 Jul 2021 03:26:57 -0700 Subject: [PATCH 2317/3768] followup #18252: show special flags -d:danger, -d:release when given (#18451) --- compiler/msgs.nim | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 8b4365ec4..242b06587 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -677,6 +677,10 @@ proc genSuccessX*(conf: ConfigRef) = elif optOptimizeSize in conf.options: build.add "size" else: build.add "none (DEBUG BUILD, `-d:release` generates faster code)" # pending https://github.com/timotheecour/Nim/issues/752, point to optimization.html + var flags = "" + if isDefined(conf, "danger"): flags.add " -d:danger" + elif isDefined(conf, "release"): flags.add " -d:release" + if flags.len > 0: build.add "; options:" & flags let sec = formatFloat(epochTime() - conf.lastCmdTime, ffDecimal, 3) let project = if conf.filenameOption == foAbs: $conf.projectFull else: $conf.projectName # xxx honor conf.filenameOption more accurately From 836b061ae30bb471dc7deef41bb454e1d047bab7 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 8 Jul 2021 03:28:06 -0700 Subject: [PATCH 2318/3768] improve `--declaredLocs` to help disambiguate types (generics, aliases etc) (#18389) * improve --declaredlocs to help disambiguate types (generics, aliases etc) * avoid a cyclic deps * fix test after rebase --- changelog.md | 2 +- compiler/astmsgs.nim | 2 +- compiler/semcall.nim | 12 ++--- compiler/semexprs.nim | 2 +- compiler/types.nim | 8 +++ tests/errmsgs/tdeclaredlocs.nim | 92 ++++++++++++++++++++++++++++++++ tests/errmsgs/tproc_mismatch.nim | 2 +- tests/errmsgs/tsigmatch.nim | 6 +-- 8 files changed, 112 insertions(+), 14 deletions(-) create mode 100644 tests/errmsgs/tdeclaredlocs.nim diff --git a/changelog.md b/changelog.md index 1deb97161..f4c31fbc9 100644 --- a/changelog.md +++ b/changelog.md @@ -395,7 +395,7 @@ ## Compiler changes -- Added `--declaredlocs` to show symbol declaration location in messages. +- Added `--declaredLocs` to show symbol declaration location in messages. - You can now enable/disable VM tracing in user code via `vmutils.vmTrace`. diff --git a/compiler/astmsgs.nim b/compiler/astmsgs.nim index 90bdce7cf..d9105b761 100644 --- a/compiler/astmsgs.nim +++ b/compiler/astmsgs.nim @@ -18,7 +18,7 @@ proc addDeclaredLoc*(result: var string, conf: ConfigRef; typ: PType) = # xxx figure out how to resolve `tyGenericParam`, e.g. for # proc fn[T](a: T, b: T) = discard # fn(1.1, "a") - let typ = typ.skipTypes(abstractInst + {tyStatic, tyUserTypeClassInst} - {tyRange}) + let typ = typ.skipTypes(abstractInst + {tyStatic, tySequence, tyArray, tySet, tyUserTypeClassInst, tyVar, tyRef, tyPtr} - {tyRange}) result.add " [$1" % typ.kind.toHumanStr if typ.sym != nil: result.add " declared in " & toFileLineCol(conf, typ.sym.info) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index cec656fc2..d8dba3e6c 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -231,11 +231,10 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): of kMissingParam: candidates.add("\n missing parameter: " & nameParam) of kTypeMismatch, kVarNeeded: doAssert nArg != nil - var wanted = err.firstMismatch.formal.typ + let wanted = err.firstMismatch.formal.typ doAssert err.firstMismatch.formal != nil candidates.add("\n required type for " & nameParam & ": ") - candidates.add typeToString(wanted) - candidates.addDeclaredLocMaybe(c.config, wanted) + candidates.addTypeDeclVerboseMaybe(c.config, wanted) candidates.add "\n but expression '" if err.firstMismatch.kind == kVarNeeded: candidates.add renderNotLValue(nArg) @@ -243,9 +242,8 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): else: candidates.add renderTree(nArg) candidates.add "' is of type: " - var got = nArg.typ - candidates.add typeToString(got) - candidates.addDeclaredLocMaybe(c.config, got) + let got = nArg.typ + candidates.addTypeDeclVerboseMaybe(c.config, got) doAssert wanted != nil if got != nil: if got.kind == tyProc and wanted.kind == tyProc: @@ -274,7 +272,7 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): const errTypeMismatch = "type mismatch: got <" - errButExpected = "but expected one of: " + errButExpected = "but expected one of:" errUndeclaredField = "undeclared field: '$1'" errUndeclaredRoutine = "attempting to call undeclared routine: '$1'" errBadRoutine = "attempting to call routine: '$1'$2" diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index acf408db0..f46d44bf0 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -988,7 +988,7 @@ proc semIndirectOp(c: PContext, n: PNode, flags: TExprFlags): PNode = break if not hasErrorType: let typ = n[0].typ - msg.add(">\nbut expected one of: \n" & + msg.add(">\nbut expected one of:\n" & typeToString(typ)) # prefer notin preferToResolveSymbols # t.sym != nil diff --git a/compiler/types.nim b/compiler/types.nim index bdf08ffe9..49ebb1dae 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -50,6 +50,14 @@ type pcmDifferentCallConv proc typeToString*(typ: PType; prefer: TPreferedDesc = preferName): string + +proc addTypeDeclVerboseMaybe*(result: var string, conf: ConfigRef; typ: PType) = + if optDeclaredLocs in conf.globalOptions: + result.add typeToString(typ, preferMixed) + result.addDeclaredLoc(conf, typ) + else: + result.add typeToString(typ) + template `$`*(typ: PType): string = typeToString(typ) proc base*(t: PType): PType = diff --git a/tests/errmsgs/tdeclaredlocs.nim b/tests/errmsgs/tdeclaredlocs.nim new file mode 100644 index 000000000..926ebf217 --- /dev/null +++ b/tests/errmsgs/tdeclaredlocs.nim @@ -0,0 +1,92 @@ +discard """ + action: reject + matrix: "--declaredLocs --hints:off" + nimoutFull: true + nimout: ''' +tdeclaredlocs.nim(92, 3) Error: type mismatch: got +but expected one of: +proc fn(a: Bam) [proc declared in tdeclaredlocs.nim(86, 6)] + first type mismatch at position: 1 + required type for a: Bam [object declared in tdeclaredlocs.nim(78, 3)] + but expression 'a' is of type: seq[MyInt2{char}] [char declared in tdeclaredlocs.nim(73, 3)] +proc fn(a: Goo[MyInt2]) [proc declared in tdeclaredlocs.nim(89, 6)] + first type mismatch at position: 1 + required type for a: Goo[MyInt2{char}] [object declared in tdeclaredlocs.nim(79, 3)] + but expression 'a' is of type: seq[MyInt2{char}] [char declared in tdeclaredlocs.nim(73, 3)] +proc fn(a: Goo[cint]) [proc declared in tdeclaredlocs.nim(88, 6)] + first type mismatch at position: 1 + required type for a: Goo[cint{int32}] [object declared in tdeclaredlocs.nim(79, 3)] + but expression 'a' is of type: seq[MyInt2{char}] [char declared in tdeclaredlocs.nim(73, 3)] +proc fn(a: array[3, Bar]) [proc declared in tdeclaredlocs.nim(82, 6)] + first type mismatch at position: 1 + required type for a: array[0..2, Bar] [object declared in tdeclaredlocs.nim(74, 3)] + but expression 'a' is of type: seq[MyInt2{char}] [char declared in tdeclaredlocs.nim(73, 3)] +proc fn(a: seq[Bar]) [proc declared in tdeclaredlocs.nim(81, 6)] + first type mismatch at position: 1 + required type for a: seq[Bar] [object declared in tdeclaredlocs.nim(74, 3)] + but expression 'a' is of type: seq[MyInt2{char}] [char declared in tdeclaredlocs.nim(73, 3)] +proc fn(a: seq[MyInt1]) [proc declared in tdeclaredlocs.nim(80, 6)] + first type mismatch at position: 1 + required type for a: seq[MyInt1{int}] [int declared in tdeclaredlocs.nim(72, 3)] + but expression 'a' is of type: seq[MyInt2{char}] [char declared in tdeclaredlocs.nim(73, 3)] +proc fn(a: set[Baz]) [proc declared in tdeclaredlocs.nim(84, 6)] + first type mismatch at position: 1 + required type for a: set[Baz{enum}] [enum declared in tdeclaredlocs.nim(75, 3)] + but expression 'a' is of type: seq[MyInt2{char}] [char declared in tdeclaredlocs.nim(73, 3)] +proc fn(a: set[MyInt2]) [proc declared in tdeclaredlocs.nim(83, 6)] + first type mismatch at position: 1 + required type for a: set[MyInt2{char}] [char declared in tdeclaredlocs.nim(73, 3)] + but expression 'a' is of type: seq[MyInt2{char}] [char declared in tdeclaredlocs.nim(73, 3)] +proc fn(a: var SetBaz) [proc declared in tdeclaredlocs.nim(85, 6)] + first type mismatch at position: 1 + required type for a: var SetBaz [enum declared in tdeclaredlocs.nim(75, 3)] + but expression 'a' is of type: seq[MyInt2{char}] [char declared in tdeclaredlocs.nim(73, 3)] +proc fn(a: var ref ptr Bam) [proc declared in tdeclaredlocs.nim(87, 6)] + first type mismatch at position: 1 + required type for a: var ref ptr Bam [object declared in tdeclaredlocs.nim(78, 3)] + but expression 'a' is of type: seq[MyInt2{char}] [char declared in tdeclaredlocs.nim(73, 3)] + +expression: fn(a) +''' +""" + +#[ +see also: tests/errmsgs/tsigmatch.nim +]# + + + + + + + + + + + + + + +# line 70 +type + MyInt1 = int + MyInt2 = char + Bar = object + Baz = enum k0, k1 + Baz2 = Baz + SetBaz = set[Baz2] + Bam = ref object + Goo[T] = object +proc fn(a: seq[MyInt1]) = discard +proc fn(a: seq[Bar]) = discard +proc fn(a: array[3, Bar]) = discard +proc fn(a: set[MyInt2]) = discard +proc fn(a: set[Baz]) = discard +proc fn(a: var SetBaz) = discard +proc fn(a: Bam) = discard +proc fn(a: var ref ptr Bam) = discard +proc fn(a: Goo[cint]) = discard +proc fn(a: Goo[MyInt2]) = discard + +var a: seq[MyInt2] +fn(a) diff --git a/tests/errmsgs/tproc_mismatch.nim b/tests/errmsgs/tproc_mismatch.nim index 400c3d441..4ddc7635e 100644 --- a/tests/errmsgs/tproc_mismatch.nim +++ b/tests/errmsgs/tproc_mismatch.nim @@ -6,7 +6,7 @@ discard """ tproc_mismatch.nim(35, 52) Error: type mismatch: got but expected 'proc (a: int, c: float){.closure, noSideEffect.}' Calling convention mismatch: got '{.cdecl.}', but expected '{.closure.}'. tproc_mismatch.nim(39, 6) Error: type mismatch: got -but expected one of: +but expected one of: proc bar(a: proc ()) first type mismatch at position: 1 required type for a: proc (){.closure.} diff --git a/tests/errmsgs/tsigmatch.nim b/tests/errmsgs/tsigmatch.nim index 4f40be2d4..023b7d518 100644 --- a/tests/errmsgs/tsigmatch.nim +++ b/tests/errmsgs/tsigmatch.nim @@ -89,9 +89,9 @@ expression: fun1(default(Mystring), "asdf") - - - +#[ +see also: tests/errmsgs/tdeclaredlocs.nim +]# From 0d74f60742ecff2ac96d97e9ad421aa1b2e8fc85 Mon Sep 17 00:00:00 2001 From: Miran Date: Thu, 8 Jul 2021 16:09:56 +0200 Subject: [PATCH 2319/3768] Revert "Make 'echo' raise IOErrors when appropriate (#16367)" (#18460) This reverts commit 23d23ecb081be6702d74024be8f96d92d9f88a59. --- changelog.md | 3 --- lib/system/io.nim | 24 ++++++++---------------- tests/exception/t16366.nim | 11 ----------- 3 files changed, 8 insertions(+), 30 deletions(-) delete mode 100644 tests/exception/t16366.nim diff --git a/changelog.md b/changelog.md index f4c31fbc9..849640e06 100644 --- a/changelog.md +++ b/changelog.md @@ -24,9 +24,6 @@ - Type mismatch errors now show more context, use `-d:nimLegacyTypeMismatch` for previous behavior. -- `echo` and `debugEcho` will now raise `IOError` if writing to stdout fails. Previous behavior - silently ignored errors. See #16366. Use `-d:nimLegacyEchoNoRaise` for previous behavior. - - `math.round` now is rounded "away from zero" in JS backend which is consistent with other backends. See #9125. Use `-d:nimLegacyJsRound` for previous behavior. diff --git a/lib/system/io.nim b/lib/system/io.nim index 62a9d1e18..591ea3e92 100644 --- a/lib/system/io.nim +++ b/lib/system/io.nim @@ -227,9 +227,6 @@ when defined(windows): # But we cannot call printf directly as the string might contain \0. # So we have to loop over all the sections separated by potential \0s. var i = c_fprintf(f, "%s", s) - if i < 0: - if doRaise: raiseEIO("cannot write string to file") - return while i < s.len: if s[i] == '\0': let w = c_fputc('\0', f) @@ -787,13 +784,6 @@ when declared(stdout): not defined(nintendoswitch) and not defined(freertos) and hostOS != "any" - const echoDoRaise = not defined(nimLegacyEchoNoRaise) and not defined(guiapp) # see PR #16366 - - template checkErrMaybe(succeeded: bool): untyped = - if not succeeded: - when echoDoRaise: - checkErr(stdout) - proc echoBinSafe(args: openArray[string]) {.compilerproc.} = when defined(androidNDK): var s = "" @@ -806,18 +796,20 @@ when declared(stdout): proc flockfile(f: File) {.importc, nodecl.} proc funlockfile(f: File) {.importc, nodecl.} flockfile(stdout) - defer: funlockfile(stdout) when defined(windows) and compileOption("threads"): acquireSys echoLock - defer: releaseSys echoLock for s in args: when defined(windows): - writeWindows(stdout, s, doRaise = echoDoRaise) + writeWindows(stdout, s) else: - checkErrMaybe(c_fwrite(s.cstring, cast[csize_t](s.len), 1, stdout) == s.len) + discard c_fwrite(s.cstring, cast[csize_t](s.len), 1, stdout) const linefeed = "\n" - checkErrMaybe(c_fwrite(linefeed.cstring, linefeed.len, 1, stdout) == linefeed.len) - checkErrMaybe(c_fflush(stdout) == 0) + discard c_fwrite(linefeed.cstring, linefeed.len, 1, stdout) + discard c_fflush(stdout) + when stdOutLock: + funlockfile(stdout) + when defined(windows) and compileOption("threads"): + releaseSys echoLock when defined(windows) and not defined(nimscript) and not defined(js): diff --git a/tests/exception/t16366.nim b/tests/exception/t16366.nim deleted file mode 100644 index 4bcad79ed..000000000 --- a/tests/exception/t16366.nim +++ /dev/null @@ -1,11 +0,0 @@ -discard """ - action: run - exitcode: 0 - targets: "c cpp" - disabled: openbsd -""" - -echo "foo1" -close stdout -doAssertRaises(IOError): - echo "foo" From 0e44d137f97a8206af1f2747391615e2f237f000 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Fri, 9 Jul 2021 03:24:57 +0300 Subject: [PATCH 2320/3768] rm redundant blank lines before literal blocks (#18465) --- lib/packages/docutils/rst.nim | 2 +- lib/packages/docutils/rstgen.nim | 2 +- nimdoc/rst2html/expected/rst_examples.html | 15 +++++---------- tests/stdlib/trst.nim | 16 ++++++++++++++++ 4 files changed, 23 insertions(+), 12 deletions(-) diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 419cf50e7..9abdf3fc3 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -1762,7 +1762,7 @@ proc parseLiteralBlock(p: var RstParser): PRstNode = var n = newLeaf("") if currentTok(p).kind == tkIndent: var indent = currentTok(p).ival - inc p.idx + while currentTok(p).kind == tkIndent: inc p.idx # skip blank lines while true: case currentTok(p).kind of tkEof: diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 829177263..b9c8f688b 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -1258,7 +1258,7 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = doAssert false, "renderRstToOut" of rnLiteralBlock: renderAux(d, n, "$1
    \n", - "\n\n\\begin{rstpre}\n$2\n$1\n\\end{rstpre}\n\n", result) + "\n\n$2\\begin{rstpre}\n$1\n\\end{rstpre}\n\n", result) of rnQuotedLiteralBlock: doAssert false, "renderRstToOut" of rnLineBlock: diff --git a/nimdoc/rst2html/expected/rst_examples.html b/nimdoc/rst2html/expected/rst_examples.html index 68b796be7..1df91ff1f 100644 --- a/nimdoc/rst2html/expected/rst_examples.html +++ b/nimdoc/rst2html/expected/rst_examples.html @@ -133,8 +133,7 @@ window.addEventListener('DOMContentLoaded', main);

    Note: Assignments, moves, and destruction are specified in the destructors document.

    The language constructs are explained using an extended BNF, in which (a)* means 0 or more a's, a+ means 1 or more a's, and (a)? means an optional a. Parentheses may be used to group elements.

    & is the lookahead operator; &a means that an a is expected but not consumed. It will be consumed in the following rule.

    -

    Non-terminals start with a lowercase letter, abstract terminal symbols are in UPPERCASE. Verbatim terminal symbols (including keywords) are quoted with '. An example:

    -ifStmt = 'if' expr ':' stmts ('elif' expr ':' stmts)* ('else' stmts)?
    +

    Non-terminals start with a lowercase letter, abstract terminal symbols are in UPPERCASE. Verbatim terminal symbols (including keywords) are quoted with '. An example:

    ifStmt = 'if' expr ':' stmts ('elif' expr ':' stmts)* ('else' stmts)?

    In a typical Nim program, most of the code is compiled into the executable. However, some of the code may be executed at compile-time. This can include constant expressions, macro definitions, and Nim procedures used by macro definitions. Most of the Nim language is supported at compile-time, but there are some restrictions -- see Restrictions on Compile-Time Execution for details. We use the term runtime to cover both compile-time execution and code execution in the executable.

    var a: array[0..1, char]
    @@ -146,8 +145,7 @@ ifStmt = 'if' expr ':' stmts ('elif' expr ':' stmts)* ('else' stmts)?

    Encoding

    All Nim source files are in the UTF-8 encoding (or its ASCII subset). Other encodings are not supported. Any of the standard platform line termination sequences can be used - the Unix form using ASCII LF (linefeed), the Windows form using the ASCII sequence CR LF (return followed by linefeed), or the old Macintosh form using the ASCII CR (return) character. All of these forms can be used equally, regardless of the platform.

    Indentation

    Nim's standard grammar describes an indentation sensitive language. This means that all the control structures are recognized by indentation. Indentation consists only of spaces; tabulators are not allowed.

    -

    With this notation we can now easily define the core of the grammar: A block of statements (simplified example):

    -ifStmt = 'if' expr ':' stmt
    +

    With this notation we can now easily define the core of the grammar: A block of statements (simplified example):

    ifStmt = 'if' expr ':' stmt
              (IND{=} 'elif' expr ':' stmt)*
              (IND{=} 'else' ':' stmt)?
     
    @@ -174,18 +172,15 @@ stmt = IND{>} stmt ^+ IND{=} DED  # list of statements
     \x HHcharacter with hex value HH; exactly two hex digits are allowed
     \u HHHHunicode codepoint with hex value HHHH; exactly four hex digits are allowed
     \u {H+}unicode codepoint; all hex digits enclosed in {} are used for the codepoint
    -
    """"long string within quotes""""

    Produces:

    -"long string within quotes"
    +
    """"long string within quotes""""

    Produces:

    "long string within quotes"

    -

    Operators

    Nim allows user defined operators. An operator is any combination of the following characters:

    -=     +     -     *     /     <     >
    +

    Operators

    Nim allows user defined operators. An operator is any combination of the following characters:

    =     +     -     *     /     <     >
     @     $     ~     &     %     |
     !     ?     ^     .     :     \

    (The grammar uses the terminal OPR to refer to operator symbols as defined here.)

    -

    The following strings denote other tokens:

    -`   (    )     {    }     [    ]    ,  ;   [.    .]  {.   .}  (.  .)  [:
    +

    The following strings denote other tokens:

    `   (    )     {    }     [    ]    ,  ;   [.    .]  {.   .}  (.  .)  [:

    Otherwise, precedence is determined by the first character.

    diff --git a/tests/stdlib/trst.nim b/tests/stdlib/trst.nim index fb9552479..fe99d6cfa 100644 --- a/tests/stdlib/trst.nim +++ b/tests/stdlib/trst.nim @@ -196,6 +196,22 @@ suite "RST parsing": rnLeaf 'someBlockQuote' """) + test "no redundant blank lines in literal blocks": + check(dedent""" + Check:: + + + code + + """.toAst == + dedent""" + rnInner + rnLeaf 'Check' + rnLeaf ':' + rnLiteralBlock + rnLeaf 'code' + """) + suite "RST indentation": test "nested bullet lists": let input = dedent """ From 86f5a56fcdbdbd4748c979e6f517d8dfc64857f7 Mon Sep 17 00:00:00 2001 From: Ivan Bobev Date: Fri, 9 Jul 2021 03:26:49 +0300 Subject: [PATCH 2321/3768] Fix a bug with starting of asynchronous processes (#18464) The asynchronous process completion handler callback should be called only once. This is achieved by passing `WT_EXECUTEONLYONCE` flag to the `registerWaitForSingleObject` Windows API procedure. Related to cheatfate/asynctools#35 --- lib/pure/asyncdispatch.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 17fd5f824..d98ca77df 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -1022,7 +1022,7 @@ when defined(windows) or defined(nimdoc): raiseOSError(osLastError()) var pcd = cast[PostCallbackDataPtr](allocShared0(sizeof(PostCallbackData))) - var flags = WT_EXECUTEINWAITTHREAD.DWORD + var flags = WT_EXECUTEINWAITTHREAD.DWORD or WT_EXECUTEONLYONCE.DWORD proc proccb(fd: AsyncFD, bytesCount: DWORD, errcode: OSErrorCode) = closeWaitable(hProcess) From 6869157cd17229dbadb8b1b43c218d5fb28e8eb2 Mon Sep 17 00:00:00 2001 From: flywind Date: Fri, 9 Jul 2021 13:44:38 +0800 Subject: [PATCH 2322/3768] [docs] improve std/encodings (#18458) * [docs] improve std/encodings * tiny * shrink * Apply suggestions from code review Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> * Apply suggestions from code review * Update lib/pure/encodings.nim Co-authored-by: Varriount Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Co-authored-by: Varriount --- lib/pure/encodings.nim | 53 ++++++++++++++++++++++++++++++++---------- 1 file changed, 41 insertions(+), 12 deletions(-) diff --git a/lib/pure/encodings.nim b/lib/pure/encodings.nim index 89db0a278..dcdc474ae 100644 --- a/lib/pure/encodings.nim +++ b/lib/pure/encodings.nim @@ -7,15 +7,42 @@ # distribution, for details about the copyright. # -## Converts between different character encodings. On UNIX, this uses +## Routines for converting between different character encodings. On UNIX, this uses ## the `iconv`:idx: library, on Windows the Windows API. +## +## The following example shows how to change character encodings. +runnableExamples: + let + orig = "öäüß" + # convert `orig` from "UTF-8" to "CP1252" + cp1252 = convert(orig, "CP1252", "UTF-8") + # convert `cp1252` from "CP1252" to "ibm850" + ibm850 = convert(cp1252, "ibm850", "CP1252") + current = getCurrentEncoding() + assert orig == "\195\182\195\164\195\188\195\159" + assert ibm850 == "\148\132\129\225" + assert convert(ibm850, current, "ibm850") == orig + +## The example below uses a reuseable `EncodingConverter` object which is +## created by `open` with `destEncoding` and `srcEncoding` specified. You can use +## `convert` on this object multiple times. +runnableExamples: + var fromGB2312 = open("utf-8", "gb2312") + let first = "\203\173\197\194\163\191\210\187" & + "\203\242\209\204\211\234\200\206\198\189\201\250" + assert fromGB2312.convert(first) == "谁怕?一蓑烟雨任平生" + + let second = "\211\208\176\215\205\183\200\231" & + "\208\194\163\172\199\227\184\199\200\231\185\202" + assert fromGB2312.convert(second) == "有白头如新,倾盖如故" + import os when not defined(windows): type ConverterObj = object - EncodingConverter* = ptr ConverterObj ## can convert between two character sets + EncodingConverter* = ptr ConverterObj ## Can convert between two character sets. else: type @@ -24,8 +51,8 @@ else: dest, src: CodePage type - EncodingError* = object of ValueError ## exception that is raised - ## for encoding errors + EncodingError* = object of ValueError ## Exception that is raised + ## for encoding errors. when defined(windows): import parseutils, strutils @@ -298,7 +325,7 @@ else: importc: "iconv", importIconv.} proc getCurrentEncoding*(uiApp = false): string = - ## retrieves the current encoding. On Unix, always "UTF-8" is returned. + ## Retrieves the current encoding. On Unix, "UTF-8" is always returned. ## The `uiApp` parameter is Windows specific. If true, the UI's code-page ## is returned, if false, the Console's code-page is returned. when defined(windows): @@ -307,7 +334,7 @@ proc getCurrentEncoding*(uiApp = false): string = result = "UTF-8" proc open*(destEncoding = "UTF-8", srcEncoding = "CP1252"): EncodingConverter = - ## opens a converter that can convert from `srcEncoding` to `destEncoding`. + ## Opens a converter that can convert from `srcEncoding` to `destEncoding`. ## Raises `IOError` if it cannot fulfill the request. when not defined(windows): result = iconvOpen(destEncoding, srcEncoding) @@ -326,7 +353,7 @@ proc open*(destEncoding = "UTF-8", srcEncoding = "CP1252"): EncodingConverter = "cannot find encoding " & srcEncoding) proc close*(c: EncodingConverter) = - ## frees the resources the converter `c` holds. + ## Frees the resources the converter `c` holds. when not defined(windows): iconvClose(c) @@ -421,12 +448,13 @@ when defined(windows): else: convertFromWideString(codePageTo, wideString) proc convert*(c: EncodingConverter, s: string): string = - ## converts `s` to `destEncoding` that was given to the converter `c`. It - ## assumed that `s` is in `srcEncoding`. - ## utf-16BE, utf-32 conversions not supported on windows result = convertWin(c.src, c.dest, s) else: proc convert*(c: EncodingConverter, s: string): string = + ## Converts `s` to `destEncoding` that was given to the converter `c`. It + ## assumes that `s` is in `srcEncoding`. + ## + ## .. warning:: UTF-16BE and UTF-32 conversions are not supported on Windows. result = newString(s.len) var inLen = csize_t len(s) var outLen = csize_t len(result) @@ -467,10 +495,11 @@ else: proc convert*(s: string, destEncoding = "UTF-8", srcEncoding = "CP1252"): string = - ## converts `s` to `destEncoding`. It assumed that `s` is in `srcEncoding`. + ## Converts `s` to `destEncoding`. It assumed that `s` is in `srcEncoding`. ## This opens a converter, uses it and closes it again and is thus more ## convenient but also likely less efficient than re-using a converter. - ## utf-16BE, utf-32 conversions not supported on windows + ## + ## .. warning:: UTF-16BE and UTF-32 conversions are not supported on Windows. var c = open(destEncoding, srcEncoding) try: result = convert(c, s) From ae7e7756fea146126ffc5200b2e66bfe2dab4cd4 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 9 Jul 2021 02:41:28 -0700 Subject: [PATCH 2323/3768] runnableExamples now show originating location in stacktraces on failure (#18457) * runnableExamples now show originating location in stacktraces on failure * PRTEMP * fix indentation inside multiline strings --- compiler/docgen.nim | 25 +++++++++++++++++++------ compiler/renderverbatim.nim | 11 ++++++----- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index e6bb7cef8..1ae00e22e 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -468,6 +468,10 @@ proc runAllExamples(d: PDoc) = proc quoted(a: string): string = result.addQuoted(a) +proc toInstantiationInfo(conf: ConfigRef, info: TLineInfo): auto = + # xxx expose in compiler/lineinfos.nim + (conf.toMsgFilename(info), info.line.int, info.col.int + ColOffset) + proc prepareExample(d: PDoc; n: PNode, topLevel: bool): tuple[rdoccmd: string, code: string] = ## returns `rdoccmd` and source code for this runnableExamples var rdoccmd = "" @@ -481,6 +485,7 @@ proc prepareExample(d: PDoc; n: PNode, topLevel: bool): tuple[rdoccmd: string, c let useRenderModule = false let loc = d.conf.toFileLineCol(n.info) let code = extractRunnableExamplesSource(d.conf, n) + let codeIndent = extractRunnableExamplesSource(d.conf, n, indent = 2) if d.conf.errorCounter > 0: return (rdoccmd, code) @@ -489,7 +494,7 @@ proc prepareExample(d: PDoc; n: PNode, topLevel: bool): tuple[rdoccmd: string, c let outputDir = d.exampleOutputDir createDir(outputDir) inc d.exampleCounter - let outp = outputDir / RelativeFile(extractFilename(d.filename.changeFileExt"" & ("_examples$1.nim" % $d.exampleCounter))) + let outp = outputDir / RelativeFile("$#_examples_$#.nim" % [d.filename.extractFilename.changeFileExt"", $d.exampleCounter]) if useRenderModule: var docComment = newTree(nkCommentStmt) @@ -507,13 +512,21 @@ proc prepareExample(d: PDoc; n: PNode, topLevel: bool): tuple[rdoccmd: string, c renderModule(runnableExamples, outp.string, conf = d.conf) else: - let code2 = """ + var code2 = code + if code.len > 0 and "codeReordering" notin code: + # hacky but simplest solution, until we devise a way to make `{.line.}` + # work without introducing a scope + code2 = """ +{.line: $#.}: +$# +""" % [$toInstantiationInfo(d.conf, n.info), codeIndent] + code2 = """ #[ -$1 +$# ]# -import $2 -$3 -""" % [comment, d.filename.quoted, code] +import $# +$# +""" % [comment, d.filename.quoted, code2] writeFile(outp.string, code2) if rdoccmd notin d.exampleGroups: diff --git a/compiler/renderverbatim.nim b/compiler/renderverbatim.nim index a20c8873d..fc74f2322 100644 --- a/compiler/renderverbatim.nim +++ b/compiler/renderverbatim.nim @@ -83,7 +83,7 @@ proc startOfLineInsideTriple(ldata: LineData, line: int): bool = if index >= ldata.lines.len: false else: ldata.lines[index] -proc extractRunnableExamplesSource*(conf: ConfigRef; n: PNode): string = +proc extractRunnableExamplesSource*(conf: ConfigRef; n: PNode, indent = 0): string = ## TLineInfo.offsetA,offsetB would be cleaner but it's only enabled for nimpretty, ## we'd need to check performance impact to enable it for nimdoc. var first = n.lastSon.info @@ -102,7 +102,7 @@ proc extractRunnableExamplesSource*(conf: ConfigRef; n: PNode): string = let last = n.lastNodeRec.info var info = first - var indent = info.col + var indent2 = info.col let numLines = numLines(conf, info.fileIndex).uint16 var lastNonemptyPos = 0 @@ -118,14 +118,15 @@ proc extractRunnableExamplesSource*(conf: ConfigRef; n: PNode): string = info.line = line let src = sourceLine(conf, info) let special = startOfLineInsideTriple(ldata, line.int) - if line > last.line and not special and not isInIndentationBlock(src, indent): + if line > last.line and not special and not isInIndentationBlock(src, indent2): break if line > first.line: result.add "\n" if special: result.add src lastNonemptyPos = result.len - elif src.len > indent: - result.add src[indent..^1] + elif src.len > indent2: + for i in 0.. Date: Fri, 9 Jul 2021 15:15:49 +0200 Subject: [PATCH 2324/3768] ORC: support for custom =trace procs (#18459) * ORC: support custom =trace procs (WIP) * Update tests/arc/tcustomtrace.nim Co-authored-by: Clyybber * =trace is now documented and seems to work * make test green Co-authored-by: Clyybber --- changelog.md | 2 + compiler/ast.nim | 2 +- compiler/ccgexprs.nim | 1 + compiler/condsyms.nim | 1 + compiler/jsgen.nim | 2 +- compiler/liftdestructors.nim | 16 ++- compiler/semmagic.nim | 6 + compiler/semstmts.nim | 13 +- compiler/vmgen.nim | 2 +- doc/destructors.rst | 37 ++++++ doc/manual.rst | 5 +- lib/system.nim | 5 + tests/arc/tcustomtrace.nim | 240 +++++++++++++++++++++++++++++++++++ 13 files changed, 321 insertions(+), 11 deletions(-) create mode 100644 tests/arc/tcustomtrace.nim diff --git a/changelog.md b/changelog.md index 849640e06..bc64e9579 100644 --- a/changelog.md +++ b/changelog.md @@ -388,6 +388,8 @@ - `typeof(voidStmt)` now works and returns `void`. +- The `gc:orc` algorithm was refined so that custom container types can participate in the + cycle collection process. ## Compiler changes diff --git a/compiler/ast.nim b/compiler/ast.nim index 26050426a..f96e464af 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -667,7 +667,7 @@ type mIsPartOf, mAstToStr, mParallel, mSwap, mIsNil, mArrToSeq, mNewString, mNewStringOfCap, mParseBiggestFloat, - mMove, mWasMoved, mDestroy, + mMove, mWasMoved, mDestroy, mTrace, mDefault, mUnown, mIsolate, mAccessEnv, mReset, mArray, mOpenArray, mRange, mSet, mSeq, mVarargs, mRef, mPtr, mVar, mDistinct, mVoid, mTuple, diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index c07ddf1db..09326ceeb 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2422,6 +2422,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mDestroy: genDestroy(p, e) of mAccessEnv: unaryExpr(p, e, d, "$1.ClE_0") of mSlice: genSlice(p, e, d) + of mTrace: discard "no code to generate" else: when defined(debugMagics): echo p.prc.name.s, " ", p.prc.id, " ", p.prc.flags, " ", p.prc.ast[genericParamsPos].kind diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index b3f8465f0..33cd1818b 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -136,3 +136,4 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasTypeofVoid") defineSymbol("nimHasDragonBox") defineSymbol("nimHasHintAll") + defineSymbol("nimHasTrace") diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index a9339c7fd..82fba02a6 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -2079,7 +2079,7 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = gen(p, n[1], x) useMagic(p, "nimCopy") r.res = "nimCopy(null, $1, $2)" % [x.rdLoc, genTypeInfo(p, n.typ)] - of mDestroy: discard "ignore calls to the default destructor" + of mDestroy, mTrace: discard "ignore calls to the default destructor" of mOrd: genOrd(p, n, r) of mLengthStr, mLengthSeq, mLengthOpenArray, mLengthArray: var x: TCompRes diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 1824557e6..b2b70edee 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -416,11 +416,23 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool = body.add destructorCall(c, op, x) result = true #result = addDestructorCall(c, t, body, x) - of attachedAsgn, attachedSink, attachedTrace: + of attachedAsgn, attachedSink: var op = getAttachedOp(c.g, t, c.kind) result = considerAsgnOrSink(c, t, body, x, y, op) if op != nil: setAttachedOp(c.g, c.idgen.module, t, c.kind, op) + of attachedTrace: + var op = getAttachedOp(c.g, t, c.kind) + if op != nil and sfOverriden in op.flags: + if op.ast.isGenericRoutine: + # patch generic =trace: + op = instantiateGeneric(c, op, t, t.typeInst) + setAttachedOp(c.g, c.idgen.module, t, c.kind, op) + + result = considerAsgnOrSink(c, t, body, x, y, op) + if op != nil: + setAttachedOp(c.g, c.idgen.module, t, c.kind, op) + of attachedDeepCopy: let op = getAttachedOp(c.g, t, attachedDeepCopy) if op != nil: @@ -1065,7 +1077,7 @@ proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInf # 4. We have a custom destructor. # 5. We have a (custom) generic destructor. - # we do not generate '=trace' nor '=dispose' procs if we + # we do not generate '=trace' procs if we # have the cycle detection disabled, saves code size. let lastAttached = if g.config.selectedGC == gcOrc: attachedTrace else: attachedSink diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index d3f26e630..04f4c4729 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -551,6 +551,12 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, let op = getAttachedOp(c.graph, t, attachedDestructor) if op != nil: result[0] = newSymNode(op) + of mTrace: + result = n + let t = n[1].typ.skipTypes(abstractVar) + let op = getAttachedOp(c.graph, t, attachedTrace) + if op != nil: + result[0] = newSymNode(op) of mUnown: result = semUnown(c, n) of mExists, mForall: diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index c3402aee3..fd2f8a1d9 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1649,6 +1649,8 @@ proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) = var noError = false let cond = if op == attachedDestructor: t.len == 2 and t[0] == nil and t[1].kind == tyVar + elif op == attachedTrace: + t.len == 3 and t[0] == nil and t[1].kind == tyVar and t[2].kind == tyPointer else: t.len >= 2 and t[0] == nil @@ -1673,8 +1675,12 @@ proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) = localError(c.config, n.info, errGenerated, "type bound operation `" & s.name.s & "` can be defined only in the same module with its type (" & obj.typeToString() & ")") if not noError and sfSystemModule notin s.owner.flags: - localError(c.config, n.info, errGenerated, - "signature for '" & s.name.s & "' must be proc[T: object](x: var T)") + if op == attachedTrace: + localError(c.config, n.info, errGenerated, + "signature for '=trace' must be proc[T: object](x: var T; env: pointer)") + else: + localError(c.config, n.info, errGenerated, + "signature for '" & s.name.s & "' must be proc[T: object](x: var T)") incl(s.flags, sfUsed) incl(s.flags, sfOverriden) @@ -1752,7 +1758,8 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = localError(c.config, n.info, errGenerated, "signature for '" & s.name.s & "' must be proc[T: object](x: var T; y: T)") of "=trace": - bindTypeHook(c, s, n, attachedTrace) + if s.magic != mTrace: + bindTypeHook(c, s, n, attachedTrace) else: if sfOverriden in s.flags: localError(c.config, n.info, errGenerated, diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 14f9e0b30..e5e4a854e 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1362,7 +1362,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = globalError(c.config, n.info, sizeOfLikeMsg("offsetof")) of mRunnableExamples: discard "just ignore any call to runnableExamples" - of mDestroy: discard "ignore calls to the default destructor" + of mDestroy, mTrace: discard "ignore calls to the default destructor" of mMove: let arg = n[1] let a = c.genx(arg) diff --git a/doc/destructors.rst b/doc/destructors.rst index c99606b95..a94ccb9e7 100644 --- a/doc/destructors.rst +++ b/doc/destructors.rst @@ -42,6 +42,12 @@ written as: for i in 0..`_. diff --git a/lib/system.nim b/lib/system.nim index 090ab309e..8aa9bd718 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -488,6 +488,11 @@ proc `=sink`*[T](x: var T; y: T) {.inline, magic: "Asgn".} = ## Generic `sink`:idx: implementation that can be overridden. shallowCopy(x, y) +when defined(nimHasTrace): + proc `=trace`*[T](x: var T; env: pointer) {.inline, magic: "Trace".} = + ## Generic `trace`:idx: implementation that can be overridden. + discard + type HSlice*[T, U] = object ## "Heterogeneous" slice type. a*: T ## The lower bound (inclusive). diff --git a/tests/arc/tcustomtrace.nim b/tests/arc/tcustomtrace.nim new file mode 100644 index 000000000..3977194d9 --- /dev/null +++ b/tests/arc/tcustomtrace.nim @@ -0,0 +1,240 @@ +discard """ + outputsub: '''1 +2 +3 +4 +5 +6 +89 +90 +90 +0 0 1 +0 1 2 +0 2 3 +1 0 4 +1 1 5 +1 2 6 +1 3 7 +after 6 6 +MEM 0''' +joinable: false + cmd: "nim c --gc:orc -d:useMalloc $file" + valgrind: "true" +""" + +import typetraits + +type + myseq*[T] = object + len, cap: int + data: ptr UncheckedArray[T] + +# XXX make code memory safe for overflows in '*' +var + allocCount, deallocCount: int + +proc `=destroy`*[T](x: var myseq[T]) = + if x.data != nil: + when not supportsCopyMem(T): + for i in 0..= x.cap: resize(x) + result = addr(x.data[x.len]) + inc x.len + +template add*[T](x: var myseq[T]; y: T) = + reserveSlot(x)[] = y + +proc shrink*[T](x: var myseq[T]; newLen: int) = + assert newLen <= x.len + assert newLen >= 0 + when not supportsCopyMem(T): + for i in countdown(x.len - 1, newLen - 1): + `=destroy`(x.data[i]) + x.len = newLen + +proc grow*[T](x: var myseq[T]; newLen: int; value: T) = + if newLen <= x.len: return + assert newLen >= 0 + let oldCap = x.cap + if oldCap == 0: x.cap = newLen + else: x.cap = max(newLen, (oldCap * 3) shr 1) + if x.data == nil: inc allocCount + x.data = cast[type(x.data)](realloc0(x.data, oldCap * sizeof(T), x.cap * sizeof(T))) + for i in x.len.. Date: Sat, 10 Jul 2021 09:41:07 +0300 Subject: [PATCH 2325/3768] sync with the same template from locks module (#18414) --- lib/core/rlocks.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/rlocks.nim b/lib/core/rlocks.nim index a1ee0a6ec..0444b9a83 100644 --- a/lib/core/rlocks.nim +++ b/lib/core/rlocks.nim @@ -48,7 +48,7 @@ proc release*(lock: var RLock) {.inline.} = ## Releases the given lock. releaseSys(lock) -template withRLock*(lock: var RLock, code: untyped): untyped = +template withRLock*(lock: RLock, code: untyped) = ## Acquires the given lock and then executes the code. acquire(lock) {.locks: [lock].}: From 3645f5fc7ae885e22cb52b9f865edf062cfb8e59 Mon Sep 17 00:00:00 2001 From: flywind Date: Sat, 10 Jul 2021 15:55:01 +0800 Subject: [PATCH 2326/3768] more important packages (#18472) --- testament/important_packages.nim | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 3d3c340eb..afceffb2e 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -37,6 +37,7 @@ pkg "alea", allowFailure = true pkg "argparse" pkg "arraymancer", "nim c tests/tests_cpu.nim", allowFailure = true pkg "ast_pattern_matching", "nim c -r --oldgensym:on tests/test1.nim", allowFailure = true +pkg "asyncthreadpool" pkg "awk" pkg "bigints", url = "https://github.com/Araq/nim-bigints" pkg "binaryheap", "nim c -r binaryheap.nim" @@ -56,6 +57,7 @@ pkg "combparser", "nimble test --gc:orc" pkg "compactdict" pkg "comprehension", "nimble test", "https://github.com/alehander42/comprehension" pkg "criterion", allowFailure = true # pending https://github.com/disruptek/criterion/issues/3 (wrongly closed) +pkg "datamancer" pkg "dashing", "nim c tests/functional.nim" pkg "delaunay" pkg "docopt" @@ -81,6 +83,7 @@ pkg "jstin" pkg "karax", "nim c -r tests/tester.nim" pkg "kdtree", "nimble test -d:nimLegacyRandomInitRand", "https://github.com/jblindsay/kdtree" pkg "loopfusion" +pkg "lockfreequeues" pkg "macroutils" pkg "manu" pkg "markdown" @@ -89,6 +92,7 @@ pkg "msgpack4nim", "nim c -r tests/test_spec.nim" pkg "nake", "nim c nakefile.nim" pkg "neo", "nim c -d:blas=openblas tests/all.nim" pkg "nesm", "nimble tests", allowFailure = true # notice plural 'tests' +pkg "netty" pkg "nico", allowFailure = true pkg "nicy", "nim c -r src/nicy.nim" pkg "nigui", "nim c -o:niguii -r src/nigui.nim" @@ -134,10 +138,12 @@ pkg "rosencrantz", "nim c -o:rsncntz -r rosencrantz.nim" pkg "sdl1", "nim c -r src/sdl.nim" pkg "sdl2_nim", "nim c -r sdl2/sdl.nim" pkg "sigv4", "nim c --gc:arc -r sigv4.nim", "https://github.com/disruptek/sigv4" +pkg "sim" pkg "snip", "nimble test", "https://github.com/genotrance/snip" pkg "stint", "nim r stint.nim" pkg "strslice" pkg "strunicode", "nim c -r src/strunicode.nim" +pkg "supersnappy" pkg "synthesis" pkg "telebot", "nim c -o:tbot -r src/telebot.nim", allowFailure = true # pending https://github.com/ba0f3/telebot.nim/issues/59 pkg "tempdir" From 980a9ed5239ef03e73946ffcc84671a6eaa35616 Mon Sep 17 00:00:00 2001 From: sivchari Date: Sun, 11 Jul 2021 15:19:20 +0900 Subject: [PATCH 2327/3768] fix comment (#18473) --- lib/impure/re.nim | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/impure/re.nim b/lib/impure/re.nim index 504d8f22e..d6922f4f1 100644 --- a/lib/impure/re.nim +++ b/lib/impure/re.nim @@ -41,12 +41,12 @@ const type RegexFlag* = enum ## options for regular expressions - reIgnoreCase = 0, ## do caseless matching - reMultiLine = 1, ## `^` and `$` match newlines within data - reDotAll = 2, ## `.` matches anything including NL - reExtended = 3, ## ignore whitespace and `#` comments - reStudy = 4 ## study the expression (may be omitted if the - ## expression will be used only once) + reIgnoreCase = 0, ## do caseless matching + reMultiLine = 1, ## `^` and `$` match newlines within data + reDotAll = 2, ## `.` matches anything including NL + reExtended = 3, ## ignore whitespace and `#` comments + reStudy = 4 ## study the expression (may be omitted if the + ## expression will be used only once) RegexDesc = object h: ptr Pcre From 9ffc70851b586dbdc579d8c6af30a6a3604d7313 Mon Sep 17 00:00:00 2001 From: GordonBGood Date: Mon, 12 Jul 2021 16:55:40 +0700 Subject: [PATCH 2328/3768] thamming_orc test created/destroyed counts match (#18471) The thamming_orc.nim code now counts all created objects being tested, not just the ones following the "first 20" test, and the position of the `destroyed += 1` counter has been adjusted so it counts all the calls that are as a result of `=trace` tracing and not just the original destruction calls. --- tests/arc/thamming_orc.nim | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/arc/thamming_orc.nim b/tests/arc/thamming_orc.nim index 463f7117e..777efb38e 100644 --- a/tests/arc/thamming_orc.nim +++ b/tests/arc/thamming_orc.nim @@ -1,6 +1,6 @@ discard """ output: '''(allocCount: 1114, deallocCount: 1112) -created 439 destroyed 402''' +created 491 destroyed 491''' cmd: "nim c --gc:orc -d:nimAllocStats $file" """ @@ -94,8 +94,8 @@ type var destroyed = 0 proc `=destroy`(ll: var LazyListObj) = - if ll.tlf == nil and ll.tl == nil: return destroyed += 1 + if ll.tlf == nil and ll.tl == nil: return when defined(trace20): echo "destroying: ", (destroyed, ll.hd[1].convertTriVal2BigInt) @@ -140,7 +140,7 @@ proc main = "Algorithmic error finding first 20 Hamming numbers!!!" when not defined(trace20): - var lsth: TriVal; created = 0; destroyed = 0 + var lsth: TriVal for h in hammings(200): lsth = h doAssert $lsth.convertTriVal2BigInt == "16200", "Algorithmic error finding 200th Hamming number!!!" @@ -149,7 +149,7 @@ let mem = getOccupiedMem() main() GC_FullCollect() let mb = getOccupiedMem() - mem -#doAssert mb == 0, "Found memory leak of " & $mb & " bytes!!!" +doAssert mb == 0, "Found memory leak of " & $mb & " bytes!!!" echo getAllocStats() echo "created ", created, " destroyed ", destroyed From 195300a938c7c8dd4ec7db3ca7cc76dddeab953e Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 12 Jul 2021 20:26:46 +0800 Subject: [PATCH 2329/3768] [source code filter]fix Nightlies bug (#18475) * workaround Nightlies bug * Update tools/niminst/makefile.nimf --- tools/niminst/makefile.nimf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/niminst/makefile.nimf b/tools/niminst/makefile.nimf index 8c40cf50f..d14960ac5 100644 --- a/tools/niminst/makefile.nimf +++ b/tools/niminst/makefile.nimf @@ -25,7 +25,7 @@ endif ifeq ($(uos),linux) myos = linux - # add -lrt to avoid "undefined reference to `clock_gettime'" with glibc<2.17 + ## add -lrt to avoid "undefined reference to `clock_gettime'" with glibc<2.17 LDFLAGS += -ldl -lm -lrt endif ifeq ($(uos),dragonfly) From 1b128ac04533421547298ecca0d9b8e7de477280 Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 13 Jul 2021 06:18:07 +0800 Subject: [PATCH 2330/3768] close #17986 add testcase (#18477) --- tests/stdlib/tjsonutils.nim | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/stdlib/tjsonutils.nim b/tests/stdlib/tjsonutils.nim index 31ec4316f..b0c444cbf 100644 --- a/tests/stdlib/tjsonutils.nim +++ b/tests/stdlib/tjsonutils.nim @@ -311,6 +311,18 @@ template fn() = doAssert guide == AboutLifeUniverseAndEverythingElse( question: "6*9=?", answer: 42) + block refObject: #bug 17986 + type A = ref object + case is_a: bool + of true: + a: int + else: + b: int + + var a = A() + fromJson(a, """{"is_a": true, "a":1, "extra_key": 1}""".parse_json, Joptions(allowExtraKeys: true)) + doAssert $a[] == "(is_a: true, a: 1)" + block testAllowMissingKeys: var guide = AboutLifeUniverseAndEverythingElse( question: "6*9=?", answer: 54) From 15fdcd0c4d4d5b44a08766120d88b085eb358ae1 Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 13 Jul 2021 15:22:33 +0800 Subject: [PATCH 2331/3768] workaround #18481 (#18482) --- lib/pure/encodings.nim | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/lib/pure/encodings.nim b/lib/pure/encodings.nim index dcdc474ae..36e891dd3 100644 --- a/lib/pure/encodings.nim +++ b/lib/pure/encodings.nim @@ -12,29 +12,31 @@ ## ## The following example shows how to change character encodings. runnableExamples: - let - orig = "öäüß" - # convert `orig` from "UTF-8" to "CP1252" - cp1252 = convert(orig, "CP1252", "UTF-8") - # convert `cp1252` from "CP1252" to "ibm850" - ibm850 = convert(cp1252, "ibm850", "CP1252") - current = getCurrentEncoding() - assert orig == "\195\182\195\164\195\188\195\159" - assert ibm850 == "\148\132\129\225" - assert convert(ibm850, current, "ibm850") == orig + when defined(windows): + let + orig = "öäüß" + # convert `orig` from "UTF-8" to "CP1252" + cp1252 = convert(orig, "CP1252", "UTF-8") + # convert `cp1252` from "CP1252" to "ibm850" + ibm850 = convert(cp1252, "ibm850", "CP1252") + current = getCurrentEncoding() + assert orig == "\195\182\195\164\195\188\195\159" + assert ibm850 == "\148\132\129\225" + assert convert(ibm850, current, "ibm850") == orig ## The example below uses a reuseable `EncodingConverter` object which is ## created by `open` with `destEncoding` and `srcEncoding` specified. You can use ## `convert` on this object multiple times. runnableExamples: - var fromGB2312 = open("utf-8", "gb2312") - let first = "\203\173\197\194\163\191\210\187" & - "\203\242\209\204\211\234\200\206\198\189\201\250" - assert fromGB2312.convert(first) == "谁怕?一蓑烟雨任平生" + when defined(windows): + var fromGB2312 = open("utf-8", "gb2312") + let first = "\203\173\197\194\163\191\210\187" & + "\203\242\209\204\211\234\200\206\198\189\201\250" + assert fromGB2312.convert(first) == "谁怕?一蓑烟雨任平生" - let second = "\211\208\176\215\205\183\200\231" & - "\208\194\163\172\199\227\184\199\200\231\185\202" - assert fromGB2312.convert(second) == "有白头如新,倾盖如故" + let second = "\211\208\176\215\205\183\200\231" & + "\208\194\163\172\199\227\184\199\200\231\185\202" + assert fromGB2312.convert(second) == "有白头如新,倾盖如故" import os From b3aca78e228feb93d5dcd6f00cdd81e4178c24cc Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 13 Jul 2021 12:44:29 +0200 Subject: [PATCH 2332/3768] closes #18433 (#18484) * beneficial refactoring; use system.Endianness * closes #18433 --- compiler/options.nim | 4 ++-- compiler/platform.nim | 6 ++---- tests/arc/tfuncobj.nim | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 6 deletions(-) create mode 100644 tests/arc/tfuncobj.nim diff --git a/compiler/options.nim b/compiler/options.nim index 135ecf1de..45ca14e76 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -592,8 +592,8 @@ proc isDefined*(conf: ConfigRef; symbol: string): bool = result = conf.target.targetOS == osNintendoSwitch of "freertos", "lwip": result = conf.target.targetOS == osFreeRTOS - of "littleendian": result = CPU[conf.target.targetCPU].endian == platform.littleEndian - of "bigendian": result = CPU[conf.target.targetCPU].endian == platform.bigEndian + of "littleendian": result = CPU[conf.target.targetCPU].endian == littleEndian + of "bigendian": result = CPU[conf.target.targetCPU].endian == bigEndian of "cpu8": result = CPU[conf.target.targetCPU].bit == 8 of "cpu16": result = CPU[conf.target.targetCPU].bit == 16 of "cpu32": result = CPU[conf.target.targetCPU].bit == 32 diff --git a/compiler/platform.nim b/compiler/platform.nim index 413a91304..7bc77d809 100644 --- a/compiler/platform.nim +++ b/compiler/platform.nim @@ -196,13 +196,11 @@ type cpuSparc64, cpuMips64, cpuMips64el, cpuRiscV32, cpuRiscV64, cpuEsp, cpuWasm32 type - TEndian* = enum - littleEndian, bigEndian - TInfoCPU* = tuple[name: string, intSize: int, endian: TEndian, + TInfoCPU* = tuple[name: string, intSize: int, endian: Endianness, floatSize, bit: int] const - EndianToStr*: array[TEndian, string] = ["littleEndian", "bigEndian"] + EndianToStr*: array[Endianness, string] = ["littleEndian", "bigEndian"] CPU*: array[succ(low(TSystemCPU))..high(TSystemCPU), TInfoCPU] = [ (name: "i386", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32), (name: "m68k", intSize: 32, endian: bigEndian, floatSize: 64, bit: 32), diff --git a/tests/arc/tfuncobj.nim b/tests/arc/tfuncobj.nim new file mode 100644 index 000000000..50cd9425e --- /dev/null +++ b/tests/arc/tfuncobj.nim @@ -0,0 +1,38 @@ +discard """ + outputsub: '''f1 +f2 +f3''' + cmd: "nim c --gc:orc $file" + valgrind: true +""" + +type + FuncObj = object + fn: proc (env: pointer) {.cdecl.} + env: pointer + +proc `=destroy`(x: var FuncObj) = + GC_unref(cast[RootRef](x.env)) + +proc `=copy`(x: var FuncObj, y: FuncObj) {.error.} + +# bug #18433 + +proc main = + var fs: seq[FuncObj] + + proc wrap(p: proc()) = + proc closeOver() = p() + let env = rawEnv closeOver + GC_ref(cast[RootRef](env)) + fs.add(FuncObj(fn: cast[proc(env: pointer){.cdecl.}](rawProc closeOver), env: env)) + + wrap(proc() {.closure.} = echo "f1") + wrap(proc() {.closure.} = echo "f2") + wrap(proc() {.closure.} = echo "f3") + + for a in fs: + a.fn(a.env) + +main() + From 12da32a891b08f34acbb676001f0c211ecfe1af8 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 13 Jul 2021 14:17:59 +0200 Subject: [PATCH 2333/3768] fixes #17893 (#18485) * fixes #17893 --- compiler/liftdestructors.nim | 7 +--- tests/arc/tshared_ptr_crash.nim | 67 +++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 6 deletions(-) create mode 100644 tests/arc/tshared_ptr_crash.nim diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index b2b70edee..cc7e36cdb 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -416,12 +416,7 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool = body.add destructorCall(c, op, x) result = true #result = addDestructorCall(c, t, body, x) - of attachedAsgn, attachedSink: - var op = getAttachedOp(c.g, t, c.kind) - result = considerAsgnOrSink(c, t, body, x, y, op) - if op != nil: - setAttachedOp(c.g, c.idgen.module, t, c.kind, op) - of attachedTrace: + of attachedAsgn, attachedSink, attachedTrace: var op = getAttachedOp(c.g, t, c.kind) if op != nil and sfOverriden in op.flags: if op.ast.isGenericRoutine: diff --git a/tests/arc/tshared_ptr_crash.nim b/tests/arc/tshared_ptr_crash.nim new file mode 100644 index 000000000..1794834db --- /dev/null +++ b/tests/arc/tshared_ptr_crash.nim @@ -0,0 +1,67 @@ +discard """ + cmd: "nim c --threads:on --gc:arc $file" + action: compile +""" + +# bug #17893 + +type + SharedPtr*[T] = object + val: ptr tuple[value: T, atomicCounter: int] + +proc `=destroy`*[T](p: var SharedPtr[T]) = + mixin `=destroy` + if p.val != nil: + if atomicLoadN(addr p.val[].atomicCounter, AtomicConsume) == 0: + `=destroy`(p.val[]) + deallocShared(p.val) + else: + discard atomicDec(p.val[].atomicCounter) + +proc `=copy`*[T](dest: var SharedPtr[T], src: SharedPtr[T]) = + if src.val != nil: + discard atomicInc(src.val[].atomicCounter) + if dest.val != nil: + `=destroy`(dest) + dest.val = src.val + +proc newSharedPtr*[T](val: sink T): SharedPtr[T] {.nodestroy.} = + result.val = cast[typeof(result.val)](allocShared(sizeof(result.val[]))) + result.val.atomicCounter = 0 + result.val.value = val + +proc isNil*[T](p: SharedPtr[T]): bool {.inline.} = + p.val == nil + +proc `[]`*[T](p: SharedPtr[T]): var T {.inline.} = + when compileOption("boundChecks"): + doAssert(p.val != nil, "deferencing nil shared pointer") + result = p.val.value + +type + Sender*[T] = object + queue: SharedPtr[seq[T]] + +proc newSender*[T](queue: sink SharedPtr[seq[T]]): Sender[T] = + result = Sender[T](queue: queue) + +proc send*[T](self: Sender[T]; t: sink T) = + self.queue[].add t + +proc newChannel*(): Sender[int] = + let queue = newSharedPtr(newSeq[int]()) + result = newSender(queue) + + +var + p: Thread[Sender[int]] + +proc threadFn(tx: Sender[int]) = + send tx, 0 + +proc multiThreadedChannel = + let tx = newChannel() + createThread(p, threadFn, tx) + joinThread(p) + +multiThreadedChannel() From a6ff6e7871d757821ba2b84cb8f175f48721bb9a Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 14 Jul 2021 10:52:09 -0700 Subject: [PATCH 2334/3768] fix https://github.com/dom96/choosenim/issues/256 WSL CRLF (#18452) * fix https://github.com/dom96/choosenim/issues/256 WSL CRLF * fixup --- .gitattributes | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitattributes b/.gitattributes index 674da7939..2c9ff103c 100644 --- a/.gitattributes +++ b/.gitattributes @@ -4,3 +4,6 @@ # duplicated, which is easily identifiable and fixable. /changelog.md merge=union +# bug https://github.com/dom96/choosenim/issues/256 for WSL CRLF +*.sh text eol=lf +/config/build_config.txt text eol=lf From 8c6dd2b9a9157c0e0bd822d8a80d1014fc87ecd7 Mon Sep 17 00:00:00 2001 From: GordonBGood Date: Thu, 15 Jul 2021 19:35:53 +0700 Subject: [PATCH 2335/3768] Improve description of how =trace is used (#18491) * [skip ci] thamming_orc test created/destroyed counts match The thamming_orc.nim code now counts all created objects being tested, not just the ones following the "first 20" test, and the position of the `destroyed += 1` counter has been adjusted so it counts all the calls that are as a result of `=trace` tracing and not just the original destruction calls. * Improve description of how `=trace` is used The following nuances weren't previously fully explained: 1. That `=trace` is only used by `--gc:orc`. 2. That `=trace` is almost certainly used along with `=destroy` when manual resource allocation has been used, but it is only required if there is a possibility of cyclic references in the wrapped types (ie. generic types). 3. That, currently, a forward definition is required for the second of the pair to avoid an auto compiler generation conflict. The pattern of the use of `=trace` has also been made more extensive, showing how both a custom `=destroy` and `=trace` are used for manual allocation of resources when there is any possibility of a cyclic reference in the resource-wrapped values. * Update doc/destructors.rst Co-authored-by: Andreas Rumpf --- doc/destructors.rst | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/doc/destructors.rst b/doc/destructors.rst index a94ccb9e7..a3fd7514e 100644 --- a/doc/destructors.rst +++ b/doc/destructors.rst @@ -222,16 +222,33 @@ The prototype of this hook for a type `T` needs to be: `env` is used by ORC to keep track of its internal state, it should be passed around to calls of the built-in `=trace` operation. -The general pattern in `=trace` looks like: +Usually there will only be a need for a custom `=trace` when a custom `=destroy` that deallocates manually allocated resources is also used, and then only when there is a chance of cyclic references from items within the manually allocated resources when it is desired that `--gc:orc` be able to break and collect these cyclic referenced resources. Currently however, there is a mutual use problem in that whichever of `=destroy`/`=trace` is used first will automatically create a version of the other which will then conflict with the creation of the second of the pair. The work around for this problem is to forward declare the second of the "hooks" to prevent the automatic creation. + +The general pattern in using `=destroy` with `=trace` looks like: .. code-block:: nim - proc `=trace`(dest: var T; env: pointer) = - for child in childrenThatCanContainPointers(dest): - `=trace`(child, env) + type + Test[T] = object + size: Natural + arr: ptr UncheckedArray[T] # raw pointer field + + proc makeTest[T](size: Natural): Test[T] = # custom allocation... + Test[T](size: size, arr: cast[ptr UncheckedArray[T]](alloc0(sizeof(T) * size))) -**Note**: The `=trace` hooks is currently more experimental and less refined + proc `=destroy`[T](dest: var Test[T]) = + if dest.arr != nil: + for i in 0 ..< dest.size: dest.arr[i].`=destroy` + dest.arr.dealloc + + proc `=trace`[T](dest: var Test[T]; env: pointer) = + if dest.arr != nil: # trace the `T`'s which may be cyclic + for i in 0 ..< dest.size: dest.arr[i].`=trace` env + + # following may be other custom "hooks" as required... + +**Note**: The `=trace` hooks (which are only used by `--gc:orc`) are currently more experimental and less refined than the other hooks. From af3d2d8ad90c31161c34f0d4f82cd0ac4a9b3d00 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 15 Jul 2021 17:58:47 +0200 Subject: [PATCH 2336/3768] added `nimAllocPagesViaMalloc` switch (#18490) * added switch * alloc.nim needs page aligned memory blocks --- changelog.md | 3 +++ doc/nimc.rst | 14 +++++++++++++- lib/system/alloc.nim | 3 ++- lib/system/osalloc.nim | 31 +++++++++++++++++++++++++++---- tests/arc/tarcmisc.nim | 2 +- 5 files changed, 46 insertions(+), 7 deletions(-) diff --git a/changelog.md b/changelog.md index bc64e9579..d44f28b94 100644 --- a/changelog.md +++ b/changelog.md @@ -391,6 +391,9 @@ - The `gc:orc` algorithm was refined so that custom container types can participate in the cycle collection process. +- On embedded devices `malloc` can now be used instead of `mmap` via `-d:nimAllocPagesViaMalloc`. + This is only supported for `--gc:orc` or `--gc:arc`. + ## Compiler changes diff --git a/doc/nimc.rst b/doc/nimc.rst index 2b9f33cb5..070956860 100644 --- a/doc/nimc.rst +++ b/doc/nimc.rst @@ -176,7 +176,7 @@ directories (in this order; later files overwrite previous settings): ``%APPDATA%/nim/nim.cfg`` (Windows). This file can be skipped with the `--skipUserCfg`:option: command line option. -3) ``$parentDir/nim.cfg`` where ``$parentDir`` stands for any parent +3) ``$parentDir/nim.cfg`` where ``$parentDir`` stands for any parent directory of the project file's path. These files can be skipped with the `--skipParentCfg`:option: command-line option. @@ -634,6 +634,18 @@ optimization in the compiler and linker. Check the `Cross-compilation`_ section for instructions on how to compile the program for your target. + +nimAllocPagesViaMalloc +---------------------- + +Nim's default allocator is based on TLSF, this algorithm was designed for embedded +devices. This allocator gets blocks/pages of memory via a currently undocumented +`osalloc` API which usually uses POSIX's `mmap` call. On many environments `mmap` +is not available but C's `malloc` is. You can use the `nimAllocPagesViaMalloc` +define to use `malloc` instead of `mmap`. `nimAllocPagesViaMalloc` is currently +only supported with `--gc:arc` or `--gc:orc`. (Since version 1.6) + + Nim for realtime systems ======================== diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim index c598b8250..479458950 100644 --- a/lib/system/alloc.nim +++ b/lib/system/alloc.nim @@ -749,8 +749,9 @@ proc rawAlloc(a: var MemRegion, requestedSize: int): pointer = inc(a.allocCounter) sysAssert(allocInv(a), "rawAlloc: begin") sysAssert(roundup(65, 8) == 72, "rawAlloc: roundup broken") - sysAssert(requestedSize >= sizeof(FreeCell), "rawAlloc: requested size too small") var size = roundup(requestedSize, MemAlign) + sysAssert(size >= sizeof(FreeCell), "rawAlloc: requested size too small") + sysAssert(size >= requestedSize, "insufficient allocated size!") #c_fprintf(stdout, "alloc; size: %ld; %ld\n", requestedSize, size) if size <= SmallChunkSize-smallChunkOverhead(): diff --git a/lib/system/osalloc.nim b/lib/system/osalloc.nim index 32d3b166d..2830adb48 100644 --- a/lib/system/osalloc.nim +++ b/lib/system/osalloc.nim @@ -28,7 +28,30 @@ const doNotUnmap = not (defined(amd64) or defined(i386)) or defined(windows) or defined(nimAllocNoUnmap) -when defined(emscripten) and not defined(StandaloneHeapSize): +when defined(nimAllocPagesViaMalloc): + when not defined(gcArc) and not defined(gcOrc): + {.error: "-d:nimAllocPagesViaMalloc is only supported with --gc:arc or --gc:orc".} + + proc osTryAllocPages(size: int): pointer {.inline.} = + let base = c_malloc(csize_t size + PageSize - 1 + sizeof(uint32)) + if base == nil: raiseOutOfMem() + # memory layout: padding + offset (4 bytes) + user_data + # in order to deallocate: read offset at user_data - 4 bytes, + # then deallocate user_data - offset + let offset = PageSize - (cast[int](base) and (PageSize - 1)) + cast[ptr uint32](base +! (offset - sizeof(uint32)))[] = uint32(offset) + result = base +! offset + + proc osAllocPages(size: int): pointer {.inline.} = + result = osTryAllocPages(size) + if result == nil: raiseOutOfMem() + + proc osDeallocPages(p: pointer, size: int) {.inline.} = + # read offset at p - 4 bytes, then deallocate (p - offset) pointer + let offset = cast[ptr uint32](p -! sizeof(uint32))[] + c_free(p -! offset) + +elif defined(emscripten) and not defined(StandaloneHeapSize): const PROT_READ = 1 # page can be read PROT_WRITE = 2 # page can be written @@ -197,11 +220,11 @@ elif defined(posix) and not defined(StandaloneHeapSize): PROT_WRITE = 2 # page can be written when defined(netbsd) or defined(openbsd): - # OpenBSD security for setjmp/longjmp coroutines - var MAP_STACK {.importc: "MAP_STACK", header: "".}: cint + # OpenBSD security for setjmp/longjmp coroutines + var MAP_STACK {.importc: "MAP_STACK", header: "".}: cint else: const MAP_STACK = 0 # avoid sideeffects - + when defined(macosx) or defined(freebsd): const MAP_ANONYMOUS = 0x1000 const MAP_PRIVATE = 0x02 # Changes are private diff --git a/tests/arc/tarcmisc.nim b/tests/arc/tarcmisc.nim index 5d5d8e914..7daea62c8 100644 --- a/tests/arc/tarcmisc.nim +++ b/tests/arc/tarcmisc.nim @@ -34,7 +34,7 @@ closed destroying variable: 20 destroying variable: 10 ''' - cmd: "nim c --gc:arc --deepcopy:on $file" + cmd: "nim c --gc:arc --deepcopy:on -d:nimAllocPagesViaMalloc $file" """ proc takeSink(x: sink string): bool = true From a9701f6531288ab3ba2c2a7ff0124802ae78122c Mon Sep 17 00:00:00 2001 From: quantimnot <54247259+quantimnot@users.noreply.github.com> Date: Thu, 15 Jul 2021 14:43:57 -0400 Subject: [PATCH 2337/3768] Extended side effect error messages (#18418) * Extended side effect error messages * Applied feedback: - refactored `markSideEffect` - refactored string interpolations - single message - skip diagnostics in `system.compiles` context Other: - started a test of diagnostic messages [ci skip] Tests aren't updated yet because messaging isn't nailed down. * - Added hints of where for side effect call locations. - Tried to clarify the reasons. * fix tests * Applied PR review feedback: - moved collection of side effects from TSym to TContext - used pragma shorthand form `.sideEffect` and `.noSideEffect` in messages - added leading '>' to structured messages for readability - changed `sempass2.markSideEffect` to a proc - replaced `system.echo` in the test to make the test compatible with Windows * Applied NEP1 formatting suggestion Co-authored-by: quantimnot --- compiler/semdata.nim | 1 + compiler/sempass2.nim | 74 ++++++++++++++++++++------ tests/effects/tdiagnostic_messages.nim | 37 +++++++++++++ 3 files changed, 95 insertions(+), 17 deletions(-) create mode 100644 tests/effects/tdiagnostic_messages.nim diff --git a/compiler/semdata.nim b/compiler/semdata.nim index bd863505c..12bb22ff9 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -159,6 +159,7 @@ type exportIndirections*: HashSet[(int, int)] # (module.id, symbol.id) importModuleMap*: Table[int, int] # (module.id, module.id) lastTLineInfo*: TLineInfo + sideEffects*: Table[int, seq[(TLineInfo, PSym)]] # symbol.id index template config*(c: PContext): ConfigRef = c.graph.config diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index e124baf26..1e1672cad 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -10,7 +10,7 @@ import intsets, ast, astalgo, msgs, renderer, magicsys, types, idents, trees, wordrecg, strutils, options, guards, lineinfos, semfold, semdata, - modulegraphs, varpartitions, typeallowed, nilcheck, errorhandling + modulegraphs, varpartitions, typeallowed, nilcheck, errorhandling, tables when defined(useDfa): import dfa @@ -223,13 +223,21 @@ proc markGcUnsafe(a: PEffects; reason: PNode) = a.owner.gcUnsafetyReason = newSym(skUnknown, a.owner.name, nextSymId a.c.idgen, a.owner, reason.info, {}) -when true: - template markSideEffect(a: PEffects; reason: typed) = - if not a.inEnforcedNoSideEffects: a.hasSideEffect = true -else: - template markSideEffect(a: PEffects; reason: typed) = - if not a.inEnforcedNoSideEffects: a.hasSideEffect = true - markGcUnsafe(a, reason) +proc markSideEffect(a: PEffects; reason: PNode | PSym; useLoc: TLineInfo) = + if not a.inEnforcedNoSideEffects: + a.hasSideEffect = true + if a.owner.kind in routineKinds: + var sym: PSym + when reason is PNode: + if reason.kind == nkSym: + sym = reason.sym + else: + let kind = if reason.kind == nkHiddenDeref: skParam else: skUnknown + sym = newSym(kind, a.owner.name, nextSymId a.c.idgen, a.owner, reason.info, {}) + else: + sym = reason + a.c.sideEffects.mgetOrPut(a.owner.id, @[]).add (useLoc, sym) + when false: markGcUnsafe(a, reason) proc listGcUnsafety(s: PSym; onlyWarning: bool; cycleCheck: var IntSet; conf: ConfigRef) = let u = s.gcUnsafetyReason @@ -259,6 +267,31 @@ proc listGcUnsafety(s: PSym; onlyWarning: bool; conf: ConfigRef) = var cycleCheck = initIntSet() listGcUnsafety(s, onlyWarning, cycleCheck, conf) +proc listSideEffects(result: var string; s: PSym; cycleCheck: var IntSet; + conf: ConfigRef; context: PContext; indentLevel: int) = + template addHint(msg; lineInfo; sym; level = indentLevel) = + result.addf("$# $# Hint: '$#' $#\n", repeat(">", level), conf $ lineInfo, sym, msg) + if context.sideEffects.hasKey(s.id): + for (useLineInfo, u) in context.sideEffects[s.id]: + if u != nil and not cycleCheck.containsOrIncl(u.id): + case u.kind + of skLet, skVar: + addHint("accesses global state '$#'" % u.name.s, useLineInfo, s.name.s) + addHint("accessed by '$#'" % s.name.s, u.info, u.name.s, indentLevel + 1) + of routineKinds: + addHint("calls `.sideEffect` '$#'" % u.name.s, useLineInfo, s.name.s) + addHint("called by '$#'" % s.name.s, u.info, u.name.s, indentLevel + 1) + listSideEffects(result, u, cycleCheck, conf, context, indentLevel + 2) + of skParam, skForVar: + addHint("calls routine via hidden pointer indirection", useLineInfo, s.name.s) + else: + addHint("calls routine via pointer indirection", useLineInfo, s.name.s) + +proc listSideEffects(result: var string; s: PSym; conf: ConfigRef; context: PContext) = + var cycleCheck = initIntSet() + result.addf("'$#' can have side effects\n", s.name.s) + listSideEffects(result, s, cycleCheck, conf, context, 1) + proc useVarNoInitCheck(a: PEffects; n: PNode; s: PSym) = if {sfGlobal, sfThread} * s.flags != {} and s.kind in {skVar, skLet} and s.magic != mNimvm: @@ -267,9 +300,7 @@ proc useVarNoInitCheck(a: PEffects; n: PNode; s: PSym) = (tfHasGCedMem in s.typ.flags or s.typ.isGCedMem): #if a.config.hasWarn(warnGcUnsafe): warnAboutGcUnsafe(n) markGcUnsafe(a, s) - markSideEffect(a, s) - else: - markSideEffect(a, s) + markSideEffect(a, s, n.info) if s.owner != a.owner and s.kind in {skVar, skLet, skForVar, skResult, skParam} and {sfGlobal, sfThread} * s.flags == {}: a.isInnerProc = true @@ -502,7 +533,7 @@ proc propagateEffects(tracked: PEffects, n: PNode, s: PSym) = if tracked.config.hasWarn(warnGcUnsafe): warnAboutGcUnsafe(n, tracked.config) markGcUnsafe(tracked, s) if tfNoSideEffect notin s.typ.flags: - markSideEffect(tracked, s) + markSideEffect(tracked, s, n.info) mergeLockLevels(tracked, n, s.getLockLevel) proc procVarCheck(n: PNode; conf: ConfigRef) = @@ -584,7 +615,7 @@ proc trackOperandForIndirectCall(tracked: PEffects, n: PNode, paramType: PType; if tracked.config.hasWarn(warnGcUnsafe): warnAboutGcUnsafe(n, tracked.config) markGcUnsafe(tracked, a) elif tfNoSideEffect notin op.flags and not isOwnedProcVar(a, tracked.owner): - markSideEffect(tracked, a) + markSideEffect(tracked, a, n.info) else: mergeRaises(tracked, effectList[exceptionEffects], n) mergeTags(tracked, effectList[tagEffects], n) @@ -592,7 +623,7 @@ proc trackOperandForIndirectCall(tracked: PEffects, n: PNode, paramType: PType; if tracked.config.hasWarn(warnGcUnsafe): warnAboutGcUnsafe(n, tracked.config) markGcUnsafe(tracked, a) elif tfNoSideEffect notin op.flags: - markSideEffect(tracked, a) + markSideEffect(tracked, a, n.info) if paramType != nil and paramType.kind in {tyVar}: invalidateFacts(tracked.guards, n) if n.kind == nkSym and isLocalVar(tracked, n.sym): @@ -749,7 +780,7 @@ proc trackCall(tracked: PEffects; n: PNode) = if tfNoSideEffect notin op.flags and not importedFromC(a): # and it's not a recursive call: if not (a.kind == nkSym and a.sym == tracked.owner): - markSideEffect(tracked, a) + markSideEffect(tracked, a, n.info) # p's effects are ours too: var a = n[0] @@ -771,7 +802,7 @@ proc trackCall(tracked: PEffects; n: PNode) = if a.sym == tracked.owner: tracked.isRecursive = true # even for recursive calls we need to check the lock levels (!): mergeLockLevels(tracked, n, a.sym.getLockLevel) - if sfSideEffect in a.sym.flags: markSideEffect(tracked, a) + if sfSideEffect in a.sym.flags: markSideEffect(tracked, a, n.info) else: mergeLockLevels(tracked, n, op.lockLevel) var effectList = op.n[0] @@ -1336,6 +1367,7 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) = effects[ensuresEffects] = ensuresSpec var mutationInfo = MutationInfo() + var hasMutationSideEffect = false if {strictFuncs, views} * c.features != {}: var goals: set[Goal] = {} if strictFuncs in c.features: goals.incl constParameters @@ -1343,6 +1375,7 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) = var partitions = computeGraphPartitions(s, body, g, goals) if not t.hasSideEffect and t.hasDangerousAssign: t.hasSideEffect = varpartitions.hasSideEffect(partitions, mutationInfo) + hasMutationSideEffect = t.hasSideEffect if views in c.features: checkBorrowedLocations(partitions, body, g.config) @@ -1357,7 +1390,14 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) = when false: listGcUnsafety(s, onlyWarning=false, g.config) else: - localError(g.config, s.info, ("'$1' can have side effects" % s.name.s) & (g.config $ mutationInfo)) + if hasMutationSideEffect: + localError(g.config, s.info, "'$1' can have side effects$2" % [s.name.s, g.config $ mutationInfo]) + elif c.compilesContextId == 0: # don't render extended diagnostic messages in `system.compiles` context + var msg = "" + listSideEffects(msg, s, g.config, t.c) + message(g.config, s.info, errGenerated, msg) + else: + localError(g.config, s.info, "") # simple error for `system.compiles` context if not t.gcUnsafe: s.typ.flags.incl tfGcSafe if not t.hasSideEffect and sfSideEffect notin s.flags: diff --git a/tests/effects/tdiagnostic_messages.nim b/tests/effects/tdiagnostic_messages.nim new file mode 100644 index 000000000..2ce4895a3 --- /dev/null +++ b/tests/effects/tdiagnostic_messages.nim @@ -0,0 +1,37 @@ +discard """ + nimoutFull: true + action: "reject" + cmd: "nim r --hint:Conf:off $file" + nimout: ''' +tdiagnostic_messages.nim(36, 6) Error: 'a' can have side effects +> tdiagnostic_messages.nim(37, 30) Hint: 'a' calls `.sideEffect` 'callWithSideEffects' +>> tdiagnostic_messages.nim(29, 6) Hint: 'callWithSideEffects' called by 'a' +>>> tdiagnostic_messages.nim(31, 34) Hint: 'callWithSideEffects' calls `.sideEffect` 'indirectCallViaVarParam' +>>>> tdiagnostic_messages.nim(25, 6) Hint: 'indirectCallViaVarParam' called by 'callWithSideEffects' +>>>>> tdiagnostic_messages.nim(26, 7) Hint: 'indirectCallViaVarParam' calls routine via hidden pointer indirection +>>> tdiagnostic_messages.nim(32, 33) Hint: 'callWithSideEffects' calls `.sideEffect` 'indirectCallViaPointer' +>>>> tdiagnostic_messages.nim(27, 6) Hint: 'indirectCallViaPointer' called by 'callWithSideEffects' +>>>>> tdiagnostic_messages.nim(28, 32) Hint: 'indirectCallViaPointer' calls routine via pointer indirection +>>> tdiagnostic_messages.nim(33, 10) Hint: 'callWithSideEffects' calls `.sideEffect` 'myEcho' +>>>> tdiagnostic_messages.nim(24, 6) Hint: 'myEcho' called by 'callWithSideEffects' +>>> tdiagnostic_messages.nim(34, 3) Hint: 'callWithSideEffects' accesses global state 'globalVar' +>>>> tdiagnostic_messages.nim(23, 5) Hint: 'globalVar' accessed by 'callWithSideEffects' + +''' +""" + +var globalVar = 0 +proc myEcho(a: string) {.sideEffect.} = discard +proc indirectCallViaVarParam(call: var proc(): int {.nimcall.}): int = + call() +proc indirectCallViaPointer(call: pointer): int = + cast[ptr proc(): int](call)[]() +proc callWithSideEffects(): int = + var p = proc (): int {.nimcall.} = 0 + discard indirectCallViaVarParam(p) + discard indirectCallViaPointer(addr p) + myEcho "" + globalVar + +func a: int = + discard callWithSideEffects() From 1d6863a7899fd87fd9eb017ae370ef37db18ad32 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 15 Jul 2021 11:54:07 -0700 Subject: [PATCH 2338/3768] deprecate `sequtils.delete` and add an overload with saner semantics consistent with `system.delete` (#18487) * deprecate sequtils.delete and add an overload with saner semantics * AssertionDefect => IndexDefect * improve tsequtils * add tests; use splice in js for optimization --- changelog.md | 3 + lib/pure/collections/sequtils.nim | 45 +++++++++- tests/stdlib/tsequtils.nim | 144 ++++++++++++++++++++---------- 3 files changed, 144 insertions(+), 48 deletions(-) diff --git a/changelog.md b/changelog.md index d44f28b94..d07ddddb4 100644 --- a/changelog.md +++ b/changelog.md @@ -346,6 +346,9 @@ - Added `dom.setInterval`, `dom.clearInterval` overloads. +- Deprecated `sequtils.delete` and added an overload taking a `Slice` that raises a defect + if the slice is out of bounds. + ## Language changes - `nimscript` now handles `except Exception as e`. diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index 6facfcc32..5b41ea00d 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -509,12 +509,51 @@ proc keepIf*[T](s: var seq[T], pred: proc(x: T): bool {.closure.}) inc(pos) setLen(s, pos) -func delete*[T](s: var seq[T]; first, last: Natural) = +func delete*[T](s: var seq[T]; slice: Slice[int]) = + ## Deletes the items `s[slice]`, raising `IndexDefect` if the slice contains + ## elements out of range. + ## + ## This operation moves all elements after `s[slice]` in linear time. + runnableExamples: + var a = @[10, 11, 12, 13, 14] + doAssertRaises(IndexDefect): a.delete(4..5) + assert a == @[10, 11, 12, 13, 14] + a.delete(4..4) + assert a == @[10, 11, 12, 13] + a.delete(1..2) + assert a == @[10, 13] + a.delete(1..<1) # empty slice + assert a == @[10, 13] + when compileOption("boundChecks"): + if not (slice.a < s.len and slice.a >= 0 and slice.b < s.len): + raise newException(IndexDefect, $(slice: slice, len: s.len)) + if slice.b >= slice.a: + template defaultImpl = + var i = slice.a + var j = slice.b + 1 + var newLen = s.len - j + i + while i < newLen: + when defined(gcDestructors): + s[i] = move(s[j]) + else: + s[i].shallowCopy(s[j]) + inc(i) + inc(j) + setLen(s, newLen) + when nimvm: defaultImpl() + else: + when defined(js): + let n = slice.b - slice.a + 1 + let first = slice.a + {.emit: "`s`.splice(`first`, `n`);".} + else: + defaultImpl() + +func delete*[T](s: var seq[T]; first, last: Natural) {.deprecated: "use `delete(s, first..last)`".} = ## Deletes the items of a sequence `s` at positions `first..last` ## (including both ends of the range). ## This modifies `s` itself, it does not return a copy. - ## - runnableExamples: + runnableExamples("--warning:deprecated:off"): let outcome = @[1, 1, 1, 1, 1, 1, 1, 1] var dest = @[1, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1] dest.delete(3, 8) diff --git a/tests/stdlib/tsequtils.nim b/tests/stdlib/tsequtils.nim index 385e6e651..6c9c92873 100644 --- a/tests/stdlib/tsequtils.nim +++ b/tests/stdlib/tsequtils.nim @@ -1,3 +1,9 @@ +discard """ + targets: "c js" +""" + +# xxx move all tests under `main` + import std/sequtils import strutils from algorithm import sorted @@ -190,17 +196,6 @@ block: # keepIf test keepIf(floats, proc(x: float): bool = x > 10) doAssert floats == @[13.0, 12.5, 10.1] -block: # delete tests - let outcome = @[1, 1, 1, 1, 1, 1, 1, 1] - var dest = @[1, 1, 1, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1] - dest.delete(3, 8) - doAssert outcome == dest, """\ - Deleting range 3-9 from [1,1,1,2,2,2,2,2,2,1,1,1,1,1] - is [1,1,1,1,1,1,1,1]""" - var x = @[1, 2, 3] - x.delete(100, 100) - doAssert x == @[1, 2, 3] - block: # insert tests var dest = @[1, 1, 1, 1, 1, 1, 1, 1] let @@ -298,43 +293,45 @@ block: # toSeq test doAssert myIter.toSeq == @[1, 2] doAssert toSeq(myIter) == @[1, 2] - block: - iterator myIter(): int {.closure.} = - yield 1 - yield 2 + when not defined(js): + # pending #4695 + block: + iterator myIter(): int {.closure.} = + yield 1 + yield 2 - doAssert myIter.toSeq == @[1, 2] - doAssert toSeq(myIter) == @[1, 2] - - block: - proc myIter(): auto = - iterator ret(): int {.closure.} = - yield 1 - yield 2 - result = ret - - doAssert myIter().toSeq == @[1, 2] - doAssert toSeq(myIter()) == @[1, 2] - - block: - proc myIter(n: int): auto = - var counter = 0 - iterator ret(): int {.closure.} = - while counter < n: - yield counter - counter.inc - result = ret + doAssert myIter.toSeq == @[1, 2] + doAssert toSeq(myIter) == @[1, 2] block: - let myIter3 = myIter(3) - doAssert myIter3.toSeq == @[0, 1, 2] + proc myIter(): auto = + iterator ret(): int {.closure.} = + yield 1 + yield 2 + result = ret + + doAssert myIter().toSeq == @[1, 2] + doAssert toSeq(myIter()) == @[1, 2] + block: - let myIter3 = myIter(3) - doAssert toSeq(myIter3) == @[0, 1, 2] - block: - # makes sure this does not hang forever - doAssert myIter(3).toSeq == @[0, 1, 2] - doAssert toSeq(myIter(3)) == @[0, 1, 2] + proc myIter(n: int): auto = + var counter = 0 + iterator ret(): int {.closure.} = + while counter < n: + yield counter + counter.inc + result = ret + + block: + let myIter3 = myIter(3) + doAssert myIter3.toSeq == @[0, 1, 2] + block: + let myIter3 = myIter(3) + doAssert toSeq(myIter3) == @[0, 1, 2] + block: + # makes sure this does not hang forever + doAssert myIter(3).toSeq == @[0, 1, 2] + doAssert toSeq(myIter(3)) == @[0, 1, 2] block: # tests https://github.com/nim-lang/Nim/issues/7187 @@ -452,4 +449,61 @@ block: for i in 0.. Date: Thu, 15 Jul 2021 23:13:01 +0300 Subject: [PATCH 2339/3768] Add changes required by Nimble lock file support (#12104) Implemented support for Nimble local cache with package directories with a checksum of the package at the end of their names. Now the compiler supports package paths in the form: * /path_to_nimble_cache_dir/pkgs/package_name-1.2.3- FEBADEAEA2345E777F0F6F8433F7F0A52EDD5D1B * /path_to_nimble_cache_dir/pkgs/package_name-#head- 042D4BE2B90ED0672E717D71850ABDB0A2D19CD2 * /path_to_nimble_cache_dir/pkgs/package_name-#branch-name- DBC1F902CB79946E990E38AF51F0BAD36ACFABD9 Related to nim-lang/nimble#127 --- compiler/modulepaths.nim | 2 +- compiler/nimblecmd.nim | 78 +++++++++++++++++++----------- lib/std/sha1.nim | 4 ++ tests/compiler/tnimblecmd.nim | 91 +++++++++++++++++++++++++++-------- tests/stdlib/tsha1.nim | 10 ++++ 5 files changed, 135 insertions(+), 50 deletions(-) diff --git a/compiler/modulepaths.nim b/compiler/modulepaths.nim index 8511b1592..a16b669c4 100644 --- a/compiler/modulepaths.nim +++ b/compiler/modulepaths.nim @@ -21,7 +21,7 @@ when false: for k, p in os.walkDir(dir, relative=true): if k == pcDir and p.len > pkg.len+1 and p[pkg.len] == '-' and p.startsWith(pkg): - let (_, a) = getPathVersion(p) + let (_, a, _) = getPathVersionChecksum(p) if bestv.len == 0 or bestv < a: bestv = a best = dir / p diff --git a/compiler/nimblecmd.nim b/compiler/nimblecmd.nim index 28398289c..9cd2941ba 100644 --- a/compiler/nimblecmd.nim +++ b/compiler/nimblecmd.nim @@ -9,8 +9,8 @@ ## Implements some helper procs for Nimble (Nim's package manager) support. -import parseutils, strutils, strtabs, os, options, msgs, sequtils, - lineinfos, pathutils +import parseutils, strutils, os, options, msgs, sequtils, lineinfos, pathutils, + std/sha1, tables proc addPath*(conf: ConfigRef; path: AbsoluteDir, info: TLineInfo) = if not conf.searchPaths.contains(path): @@ -18,6 +18,7 @@ proc addPath*(conf: ConfigRef; path: AbsoluteDir, info: TLineInfo) = type Version* = distinct string + PackageInfo = Table[string, tuple[version, checksum: string]] proc `$`*(ver: Version): string {.borrow.} @@ -62,43 +63,64 @@ proc `<`*(ver: Version, ver2: Version): bool = else: return false -proc getPathVersion*(p: string): tuple[name, version: string] = - ## Splits path ``p`` in the format ``/home/user/.nimble/pkgs/package-0.1`` - ## into ``(/home/user/.nimble/pkgs/package, 0.1)`` - result.name = "" - result.version = "" +proc getPathVersionChecksum*(p: string): tuple[name, version, checksum: string] = + ## Splits path ``p`` in the format + ## ``/home/user/.nimble/pkgs/package-0.1-febadeaea2345e777f0f6f8433f7f0a52edd5d1b`` into + ## ``("/home/user/.nimble/pkgs/package", "0.1", "febadeaea2345e777f0f6f8433f7f0a52edd5d1b")`` - const specialSeparator = "-#" - let last = p.rfind(p.lastPathPart) # the index where the last path part begins - var sepIdx = p.find(specialSeparator, start = last) - if sepIdx == -1: - sepIdx = p.rfind('-', start = last) + const checksumSeparator = '-' + const versionSeparator = '-' + const specialVersionSepartator = "-#" + const separatorNotFound = -1 - if sepIdx == -1: - result.name = p - return + var checksumSeparatorIndex = p.rfind(checksumSeparator) + if checksumSeparatorIndex != separatorNotFound: + result.checksum = p.substr(checksumSeparatorIndex + 1) + if not result.checksum.isValidSha1Hash(): + result.checksum = "" + checksumSeparatorIndex = p.len() + else: + checksumSeparatorIndex = p.len() - for i in sepIdx.. Date: Fri, 16 Jul 2021 07:42:35 +0200 Subject: [PATCH 2340/3768] added Atlas helper tool (#18497) * added Atlas helper tool * further improvements --- koch.nim | 8 +- tools/atlas/atlas.md | 77 +++++++ tools/atlas/atlas.nim | 390 +++++++++++++++++++++++++++++++++ tools/atlas/osutils.nim | 59 +++++ tools/atlas/packagesjson.nim | 117 ++++++++++ tools/atlas/parse_requires.nim | 101 +++++++++ 6 files changed, 751 insertions(+), 1 deletion(-) create mode 100644 tools/atlas/atlas.md create mode 100644 tools/atlas/atlas.nim create mode 100644 tools/atlas/osutils.nim create mode 100644 tools/atlas/packagesjson.nim create mode 100644 tools/atlas/parse_requires.nim diff --git a/koch.nim b/koch.nim index 4ba0d0eb5..da7576e65 100644 --- a/koch.nim +++ b/koch.nim @@ -222,7 +222,13 @@ proc buildTools(args: string = "") = # `-d:nimDebugUtils` only makes sense when temporarily editing/debugging compiler # `-d:debug` should be changed to a flag that doesn't require re-compiling nim # `--opt:speed` is a sensible default even for a debug build, it doesn't affect nim stacktraces - nimCompileFold("Compile nim_dbg", "compiler/nim.nim", options = "--opt:speed --stacktrace -d:debug --stacktraceMsgs -d:nimCompilerStacktraceHints " & args, outputName = "nim_dbg") + nimCompileFold("Compile nim_dbg", "compiler/nim.nim", options = + "--opt:speed --stacktrace -d:debug --stacktraceMsgs -d:nimCompilerStacktraceHints " & args, + outputName = "nim_dbg") + + nimCompileFold("Compile atlas", "tools/atlas/atlas.nim", options = "-d:release " & args, + outputName = "atlas") + proc nsis(latest: bool; args: string) = bundleNimbleExe(latest, args) diff --git a/tools/atlas/atlas.md b/tools/atlas/atlas.md new file mode 100644 index 000000000..a36817dc5 --- /dev/null +++ b/tools/atlas/atlas.md @@ -0,0 +1,77 @@ +# Atlas Package Cloner + +Atlas is a simple package cloner tool that automates some of the +workflows and needs for Nim's stdlib evolution. + +Atlas is compatible with Nimble in the sense that it supports the Nimble +file format. + + +## How it works + +Atlas uses git commits internally; version requirements are translated +to git commits via `git show-ref --tags`. + +Atlas uses URLs internally; Nimble package names are translated to URLs +via Nimble's `packages.json` file. + +Atlas does not call the Nim compiler for a build, instead it creates/patches +a `nim.cfg` file for the compiler. For example: + +``` +############# begin Atlas config section ########## +--noNimblePath +--path:"../nimx" +--path:"../sdl2/src" +--path:"../opengl/src" +############# end Atlas config section ########## +``` + +The version selection is deterministic, it picks up the *minimum* required +version. Thanks to this design, lock files are not required. + + +## Dependencies + +Dependencies are neither installed globally, nor locally into the current +project. Instead a "workspace" is used. The workspace is the nearest parent +directory of the current directory that does not contain a `.git` subdirectory. +Dependencies are managed as **siblings**, not as children. Dependencies are +kept as git repositories. + +Thanks to this setup, it's easy to develop multiple projects at the same time. + +A project plus its dependencies are stored in a workspace: + + $workspace / main project + $workspace / dependency A + $workspace / dependency B + + +No attempts are being made at keeping directory hygiene inside the +workspace, you're supposed to create appropriate `$workspace` directories +at your own leisure. + + +## Commands + +Atlas supports the following commands: + + +### Clone + +Clones a URL and all of its dependencies (recursively) into the workspace. +Creates or patches a `nim.cfg` file with the required `--path` entries. + + +### Clone + +The `` is translated into an URL via `packages.json` and +then `clone ` is performed. + + +### Search + +Search the package index `packages.json` for a package that the given terms +in its description (or name or list of tags). + diff --git a/tools/atlas/atlas.nim b/tools/atlas/atlas.nim new file mode 100644 index 000000000..bc1cc6ae3 --- /dev/null +++ b/tools/atlas/atlas.nim @@ -0,0 +1,390 @@ +# +# Atlas Package Cloner +# (c) Copyright 2021 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Simple tool to automate frequent workflows: Can "clone" +## a Nimble dependency and its dependencies recursively. + +import std/[parseopt, strutils, os, osproc, sequtils, unicode, tables, sets] +import parse_requires, osutils, packagesjson + +const + Version = "0.1" + Usage = "atlas - Nim Package Manager Version " & Version & """ + + (c) 2021 Andreas Rumpf +Usage: + atlas [options] [command] [arguments] +Command: + clone url|pkgname clone a package and all of its dependencies + search keyw keywB... search for package that contains the given keywords + +Options: + --keepCommits do not perform any `git checkouts` + --version show the version + --help show this help +""" + +proc writeHelp() = + stdout.write(Usage) + stdout.flushFile() + quit(0) + +proc writeVersion() = + stdout.write(Version & "\n") + stdout.flushFile() + quit(0) + +type + PackageName = distinct string + DepRelation = enum + normal, strictlyLess, strictlyGreater + + Dependency = object + name: PackageName + url, commit: string + rel: DepRelation # "requires x < 1.0" is silly, but Nimble allows it so we have too. + AtlasContext = object + projectDir, workspace: string + hasPackageList: bool + keepCommits: bool + p: Table[string, string] # name -> url mapping + processed: HashSet[string] # the key is (url / commit) + errors: int + +const + InvalidCommit = "" + +proc toDepRelation(s: string): DepRelation = + case s + of "<": strictlyLess + of ">": strictlyGreater + else: normal + +proc isCleanGit(dir: string): string = + result = "" + let (outp, status) = osproc.execCmdEx("git diff") + if outp.len != 0: + result = "'git diff' not empty" + elif status != 0: + result = "'git diff' returned non-zero" + +proc message(c: var AtlasContext; category: string; p: PackageName; args: varargs[string]) = + var msg = category & "(" & p.string & ")" + for a in args: + msg.add ' ' + msg.add a + stdout.writeLine msg + inc c.errors + +proc warn(c: var AtlasContext; p: PackageName; args: varargs[string]) = + message(c, "[Warning] ", p, args) + +proc error(c: var AtlasContext; p: PackageName; args: varargs[string]) = + message(c, "[Error] ", p, args) + +proc sameVersionAs(tag, ver: string): bool = + const VersionChars = {'0'..'9', '.'} + + proc safeCharAt(s: string; i: int): char {.inline.} = + if i >= 0 and i < s.len: s[i] else: '\0' + + let idx = find(tag, ver) + if idx >= 0: + # we found the version as a substring inside the `tag`. But we + # need to watch out the the boundaries are not part of a + # larger/different version number: + result = safeCharAt(tag, idx-1) notin VersionChars and + safeCharAt(tag, idx+ver.len) notin VersionChars + +proc versionToCommit(d: Dependency): string = + let (outp, status) = osproc.execCmdEx("git show-ref --tags") + if status == 0: + var useNextOne = false + for line in splitLines(outp): + let commitsAndTags = strutils.splitWhitespace(line) + if commitsAndTags.len == 2: + case d.rel + of normal: + if commitsAndTags[1].sameVersionAs(d.commit): + return commitsAndTags[0] + of strictlyLess: + if d.commit == InvalidCommit or not commitsAndTags[1].sameVersionAs(d.commit): + return commitsAndTags[0] + of strictlyGreater: + if commitsAndTags[1].sameVersionAs(d.commit): + useNextOne = true + elif useNextOne: + return commitsAndTags[0] + + return "" + +proc checkoutGitCommit(c: var AtlasContext; p: PackageName; commit: string) = + let (outp, status) = osproc.execCmdEx("git checkout " & quoteShell(commit)) + if status != 0: + error(c, p, "could not checkout commit", commit) + +proc gitPull(c: var AtlasContext; p: PackageName) = + let (_, status) = osproc.execCmdEx("git pull") + if status != 0: + error(c, p, "could not 'git pull'") + +proc updatePackages(c: var AtlasContext) = + if dirExists(c.workspace / PackagesDir): + withDir(c.workspace / PackagesDir): + gitPull(c, PackageName PackagesDir) + else: + withDir c.workspace: + let err = cloneUrl("https://github.com/nim-lang/packages", PackagesDir, false) + if err != "": + error c, PackageName(PackagesDir), err + +proc fillPackageLookupTable(c: var AtlasContext) = + if not c.hasPackageList: + c.hasPackageList = true + updatePackages(c) + let plist = getPackages(c.workspace) + for entry in plist: + c.p[unicode.toLower entry.name] = entry.url + +proc toUrl(c: var AtlasContext; p: string): string = + if p.isUrl: + result = p + else: + fillPackageLookupTable(c) + result = c.p.getOrDefault(unicode.toLower p) + if result.len == 0: + inc c.errors + +proc toName(p: string): PackageName = + if p.isUrl: + result = PackageName splitFile(p).name + else: + result = PackageName p + +proc needsCommitLookup(commit: string): bool {.inline} = + '.' in commit or commit == InvalidCommit + +proc checkoutCommit(c: var AtlasContext; w: Dependency) = + let dir = c.workspace / w.name.string + withDir dir: + if w.commit.len == 0 or cmpIgnoreCase(w.commit, "#head") == 0: + gitPull(c, w.name) + else: + let err = isCleanGit(dir) + if err != "": + warn c, w.name, err + else: + let requiredCommit = if needsCommitLookup(w.commit): versionToCommit(w) else: w.commit + let (cc, status) = osproc.execCmdEx("git log -n 1 --format=%H") + let currentCommit = strutils.strip(cc) + if requiredCommit == "" or status != 0: + if requiredCommit == "" and w.commit == InvalidCommit: + warn c, w.name, "package has no tagged releases" + else: + warn c, w.name, "cannot find specified version/commit", w.commit + else: + if currentCommit != requiredCommit: + # checkout the later commit: + # git merge-base --is-ancestor + let (mergeBase, status) = osproc.execCmdEx("git merge-base " & + currentCommit.quoteShell & " " & requiredCommit.quoteShell) + if status == 0 and (mergeBase == currentCommit or mergeBase == requiredCommit): + # conflict resolution: pick the later commit: + if mergeBase == currentCommit: + checkoutGitCommit(c, w.name, requiredCommit) + else: + checkoutGitCommit(c, w.name, requiredCommit) + when false: + warn c, w.name, "do not know which commit is more recent:", + currentCommit, "(current) or", w.commit, " =", requiredCommit, "(required)" + +proc findNimbleFile(c: AtlasContext; dep: Dependency): string = + result = c.workspace / dep.name.string / (dep.name.string & ".nimble") + if not fileExists(result): + result = "" + for x in walkFiles(c.workspace / dep.name.string / "*.nimble"): + if result.len == 0: + result = x + else: + # ambiguous .nimble file + return "" + +proc addUniqueDep(c: var AtlasContext; work: var seq[Dependency]; + tokens: seq[string]) = + let oldErrors = c.errors + let url = toUrl(c, tokens[0]) + if oldErrors != c.errors: + warn c, toName(tokens[0]), "cannot resolve package name" + elif not c.processed.containsOrIncl(url / tokens[2]): + work.add Dependency(name: toName(tokens[0]), url: url, commit: tokens[2], + rel: toDepRelation(tokens[1])) + +proc collectNewDeps(c: var AtlasContext; work: var seq[Dependency]; + dep: Dependency; result: var seq[string]; + isMainProject: bool) = + let nimbleFile = findNimbleFile(c, dep) + if nimbleFile != "": + let nimbleInfo = extractRequiresInfo(nimbleFile) + for r in nimbleInfo.requires: + var tokens: seq[string] = @[] + for token in tokenizeRequires(r): + tokens.add token + if tokens.len == 1: + # nimx uses dependencies like 'requires "sdl2"'. + # Via this hack we map them to the first tagged release. + # (See the `isStrictlySmallerThan` logic.) + tokens.add "<" + tokens.add InvalidCommit + elif tokens.len == 2 and tokens[1].startsWith("#"): + # Dependencies can also look like 'requires "sdl2#head" + var commit = tokens[1] + tokens[1] = "==" + tokens.add commit + + if tokens.len >= 3 and cmpIgnoreCase(tokens[0], "nim") != 0: + c.addUniqueDep work, tokens + + result.add dep.name.string / nimbleInfo.srcDir + else: + result.add dep.name.string + +proc clone(c: var AtlasContext; start: string): seq[string] = + # non-recursive clone. + let oldErrors = c.errors + var work = @[Dependency(name: toName(start), url: toUrl(c, start), commit: "")] + + if oldErrors != c.errors: + error c, toName(start), "cannot resolve package name" + return + + c.projectDir = work[0].name.string + result = @[] + var i = 0 + while i < work.len: + let w = work[i] + let oldErrors = c.errors + if not dirExists(c.workspace / w.name.string): + withDir c.workspace: + let err = cloneUrl(w.url, w.name.string, false) + if err != "": + error c, w.name, err + if oldErrors == c.errors: + if not c.keepCommits: checkoutCommit(c, w) + # even if the checkout fails, we can make use of the somewhat + # outdated .nimble file to clone more of the most likely still relevant + # dependencies: + collectNewDeps(c, work, w, result, i == 0) + inc i + +const + configPatternBegin = "############# begin Atlas config section ##########\n" + configPatternEnd = "############# end Atlas config section ##########\n" + +proc patchNimCfg(c: AtlasContext; deps: seq[string]) = + var paths = "--noNimblePath\n" + for d in deps: + paths.add "--path:\"../" & d.replace("\\", "/") & "\"\n" + + let cfg = c.projectDir / "nim.cfg" + var cfgContent = configPatternBegin & paths & configPatternEnd + if not fileExists(cfg): + writeFile(cfg, cfgContent) + else: + let content = readFile(cfg) + let start = content.find(configPatternBegin) + if start >= 0: + cfgContent = content.substr(0, start-1) & cfgContent + let theEnd = content.find(configPatternEnd, start) + if theEnd >= 0: + cfgContent.add content.substr(theEnd+len(configPatternEnd)) + else: + cfgContent = content & "\n" & cfgContent + if cfgContent != content: + # do not touch the file if nothing changed + # (preserves the file date information): + writeFile(cfg, cfgContent) + +proc error*(msg: string) = + when defined(debug): + writeStackTrace() + quit "[Error] " & msg + +proc main = + var action = "" + var args: seq[string] = @[] + template singleArg() = + if args.len != 1: + error action & " command takes a single package name" + + template noArgs() = + if args.len != 0: + error action & " command takes no arguments" + + var c = AtlasContext( + projectDir: getCurrentDir(), + workspace: getCurrentDir()) + + for kind, key, val in getopt(): + case kind + of cmdArgument: + if action.len == 0: + action = key.normalize + else: + args.add key + of cmdLongOption, cmdShortOption: + case normalize(key) + of "help", "h": writeHelp() + of "version", "v": writeVersion() + of "keepcommits": c.keepCommits = true + else: writeHelp() + of cmdEnd: assert false, "cannot happen" + + while c.workspace.len > 0 and dirExists(c.workspace / ".git"): + c.workspace = c.workspace.parentDir() + + case action + of "": + error "No action." + of "clone": + singleArg() + let deps = clone(c, args[0]) + patchNimCfg c, deps + if c.errors > 0: + error "There were problems." + of "refresh": + noArgs() + updatePackages(c) + of "search", "list": + updatePackages(c) + search getPackages(c.workspace), args + else: + error "Invalid action: " & action + +when isMainModule: + main() + +when false: + # some testing code for the `patchNimCfg` logic: + var c = AtlasContext( + projectDir: getCurrentDir(), + workspace: getCurrentDir().parentDir) + + patchNimCfg(c, @[PackageName"abc", PackageName"xyz"]) + +when false: + assert sameVersionAs("v0.2.0", "0.2.0") + assert sameVersionAs("v1", "1") + + assert sameVersionAs("1.90", "1.90") + + assert sameVersionAs("v1.2.3-zuzu", "1.2.3") + assert sameVersionAs("foo-1.2.3.4", "1.2.3.4") + + assert not sameVersionAs("foo-1.2.3.4", "1.2.3") + assert not sameVersionAs("foo", "1.2.3") + assert not sameVersionAs("", "1.2.3") diff --git a/tools/atlas/osutils.nim b/tools/atlas/osutils.nim new file mode 100644 index 000000000..31392b113 --- /dev/null +++ b/tools/atlas/osutils.nim @@ -0,0 +1,59 @@ +## OS utilities like 'withDir'. +## (c) 2021 Andreas Rumpf + +import os, strutils, osproc + +template withDir*(dir, body) = + let oldDir = getCurrentDir() + try: + setCurrentDir(dir) + body + finally: + setCurrentDir(oldDir) + +proc isUrl*(x: string): bool = + x.startsWith("git://") or x.startsWith("https://") or x.startsWith("http://") + +proc cloneUrl*(url, dest: string; cloneUsingHttps: bool): string = + ## Returns an error message on error or else "". + result = "" + var modUrl = + if url.startsWith("git://") and cloneUsingHttps: + "https://" & url[6 .. ^1] + else: url + + # github + https + trailing url slash causes a + # checkout/ls-remote to fail with Repository not found + var isGithub = false + if modUrl.contains("github.com") and modUrl.endswith("/"): + modUrl = modUrl[0 .. ^2] + isGithub = true + + let (_, exitCode) = execCmdEx("git ls-remote --quiet --tags " & modUrl) + var xcode = exitCode + if isGithub and exitCode != QuitSuccess: + # retry multiple times to avoid annoying github timeouts: + for i in 0..4: + os.sleep(4000) + xcode = execCmdEx("git ls-remote --quiet --tags " & modUrl)[1] + if xcode == QuitSuccess: break + + if xcode == QuitSuccess: + # retry multiple times to avoid annoying github timeouts: + let cmd = "git clone " & modUrl & " " & dest + for i in 0..4: + if execShellCmd(cmd) == 0: return "" + os.sleep(4000) + result = "exernal program failed: " & cmd + elif not isGithub: + let (_, exitCode) = execCmdEx("hg identify " & modUrl) + if exitCode == QuitSuccess: + let cmd = "hg clone " & modUrl & " " & dest + for i in 0..4: + if execShellCmd(cmd) == 0: return "" + os.sleep(4000) + result = "exernal program failed: " & cmd + else: + result = "Unable to identify url: " & modUrl + else: + result = "Unable to identify url: " & modUrl diff --git a/tools/atlas/packagesjson.nim b/tools/atlas/packagesjson.nim new file mode 100644 index 000000000..e1e23ee29 --- /dev/null +++ b/tools/atlas/packagesjson.nim @@ -0,0 +1,117 @@ + +import std / [json, os, sets, strutils] +import osutils + +type + Package* = ref object + # Required fields in a package. + name*: string + url*: string # Download location. + license*: string + downloadMethod*: string + description*: string + tags*: seq[string] # \ + # From here on, optional fields set to the empty string if not available. + version*: string + dvcsTag*: string + web*: string # Info url for humans. + +proc optionalField(obj: JsonNode, name: string, default = ""): string = + if hasKey(obj, name) and obj[name].kind == JString: + result = obj[name].str + else: + result = default + +proc requiredField(obj: JsonNode, name: string): string = + result = optionalField(obj, name, "") + +proc fromJson*(obj: JSonNode): Package = + result = Package() + result.name = obj.requiredField("name") + if result.name.len == 0: return nil + result.version = obj.optionalField("version") + result.url = obj.requiredField("url") + if result.url.len == 0: return nil + result.downloadMethod = obj.requiredField("method") + if result.downloadMethod.len == 0: return nil + result.dvcsTag = obj.optionalField("dvcs-tag") + result.license = obj.optionalField("license") + result.tags = @[] + for t in obj["tags"]: + result.tags.add(t.str) + result.description = obj.requiredField("description") + result.web = obj.optionalField("web") + +const PackagesDir* = "packages" + +proc getPackages*(workspaceDir: string): seq[Package] = + result = @[] + var uniqueNames = initHashSet[string]() + var jsonFiles = 0 + for kind, path in walkDir(workspaceDir / PackagesDir): + if kind == pcFile and path.endsWith(".json"): + inc jsonFiles + let packages = json.parseFile(path) + for p in packages: + let pkg = p.fromJson() + if pkg != nil and not uniqueNames.containsOrIncl(pkg.name): + result.add(pkg) + +proc `$`*(pkg: Package): string = + result = pkg.name & ":\n" + result &= " url: " & pkg.url & " (" & pkg.downloadMethod & ")\n" + result &= " tags: " & pkg.tags.join(", ") & "\n" + result &= " description: " & pkg.description & "\n" + result &= " license: " & pkg.license & "\n" + if pkg.web.len > 0: + result &= " website: " & pkg.web & "\n" + +proc search*(pkgList: seq[Package]; terms: seq[string]) = + var found = false + template onFound = + echo pkg + echo("") + found = true + break forPackage + + for pkg in pkgList: + if terms.len > 0: + block forPackage: + for term in terms: + let word = term.toLower + # Search by name. + if word in pkg.name.toLower: + onFound() + # Search by tag. + for tag in pkg.tags: + if word in tag.toLower: + onFound() + else: + echo(pkg) + echo(" ") + + if not found and terms.len > 0: + echo("No package found.") + +type PkgCandidates* = array[3, seq[Package]] + +proc determineCandidates*(pkgList: seq[Package]; + terms: seq[string]): PkgCandidates = + result[0] = @[] + result[1] = @[] + result[2] = @[] + for pkg in pkgList: + block termLoop: + for term in terms: + let word = term.toLower + if word == pkg.name.toLower: + result[0].add pkg + break termLoop + elif word in pkg.name.toLower: + result[1].add pkg + break termLoop + else: + for tag in pkg.tags: + if word in tag.toLower: + result[2].add pkg + break termLoop diff --git a/tools/atlas/parse_requires.nim b/tools/atlas/parse_requires.nim new file mode 100644 index 000000000..7e26a1656 --- /dev/null +++ b/tools/atlas/parse_requires.nim @@ -0,0 +1,101 @@ +## Utility API for Nim package managers. +## (c) 2021 Andreas Rumpf + +import std / strutils +import ".." / compiler / [ast, idents, msgs, syntaxes, options, pathutils] + +type + NimbleFileInfo* = object + requires*: seq[string] + srcDir*: string + tasks*: seq[(string, string)] + +proc extract(n: PNode; conf: ConfigRef; result: var NimbleFileInfo) = + case n.kind + of nkStmtList, nkStmtListExpr: + for child in n: + extract(child, conf, result) + of nkCallKinds: + if n[0].kind == nkIdent: + case n[0].ident.s + of "requires": + for i in 1.. 0: ch = ch.lastSon + if ch.kind in {nkStrLit..nkTripleStrLit}: + result.requires.add ch.strVal + else: + localError(conf, ch.info, "'requires' takes string literals") + of "task": + if n.len >= 3 and n[1].kind == nkIdent and n[2].kind in {nkStrLit..nkTripleStrLit}: + result.tasks.add((n[1].ident.s, n[2].strVal)) + else: discard + of nkAsgn, nkFastAsgn: + if n[0].kind == nkIdent and cmpIgnoreCase(n[0].ident.s, "srcDir") == 0: + if n[1].kind in {nkStrLit..nkTripleStrLit}: + result.srcDir = n[1].strVal + else: + localError(conf, n[1].info, "assignments to 'srcDir' must be string literals") + else: + discard + +proc extractRequiresInfo*(nimbleFile: string): NimbleFileInfo = + ## Extract the `requires` information from a Nimble file. This does **not** + ## evaluate the Nimble file. Errors are produced on stderr/stdout and are + ## formatted as the Nim compiler does it. The parser uses the Nim compiler + ## as an API. The result can be empty, this is not an error, only parsing + ## errors are reported. + var conf = newConfigRef() + conf.foreignPackageNotes = {} + conf.notes = {} + conf.mainPackageNotes = {} + + let fileIdx = fileInfoIdx(conf, AbsoluteFile nimbleFile) + var parser: Parser + if setupParser(parser, fileIdx, newIdentCache(), conf): + extract(parseAll(parser), conf, result) + closeParser(parser) + +const Operators* = {'<', '>', '=', '&', '@', '!', '^'} + +proc token(s: string; idx: int; lit: var string): int = + var i = idx + if i >= s.len: return i + while s[i] in Whitespace: inc(i) + case s[i] + of Letters, '#': + lit.add s[i] + inc i + while i < s.len and s[i] notin (Whitespace + {'@', '#'}): + lit.add s[i] + inc i + of '0'..'9': + while i < s.len and s[i] in {'0'..'9', '.'}: + lit.add s[i] + inc i + of '"': + inc i + while i < s.len and s[i] != '"': + lit.add s[i] + inc i + inc i + of Operators: + while i < s.len and s[i] in Operators: + lit.add s[i] + inc i + else: + lit.add s[i] + inc i + result = i + +iterator tokenizeRequires*(s: string): string = + var start = 0 + var tok = "" + while start < s.len: + tok.setLen 0 + start = token(s, start, tok) + yield tok + +when isMainModule: + for x in tokenizeRequires("jester@#head >= 1.5 & <= 1.8"): + echo x From 96a7f9b31c84f14906b201d5e3b17c93d3878af6 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 16 Jul 2021 15:50:02 +0200 Subject: [PATCH 2341/3768] deprecate cuchar, don't redefine it (#18505) --- changelog.md | 3 ++- lib/posix/posix_linux_amd64.nim | 2 +- lib/posix/posix_nintendoswitch.nim | 12 ++++----- lib/pure/bitops.nim | 42 +++++++++++++++--------------- lib/pure/net.nim | 6 ++--- lib/pure/terminal.nim | 2 +- lib/std/sysrand.nim | 2 +- lib/system.nim | 4 +-- lib/windows/winlean.nim | 4 +-- lib/wrappers/openssl.nim | 24 ++++++++--------- tests/stdlib/topenssl.nim | 8 +++--- 11 files changed, 55 insertions(+), 54 deletions(-) diff --git a/changelog.md b/changelog.md index d07ddddb4..568ff0bba 100644 --- a/changelog.md +++ b/changelog.md @@ -6,7 +6,8 @@ - Deprecated `std/mersenne` -- `cuchar` now aliases `uint8` instead of `char` +- `cuchar` is now deprecated as it aliased `char` where arguably it should have aliased `uint8`. + Please use `char` or `uint8` instead. - `repr` now doesn't insert trailing newline; previous behavior was very inconsistent, see #16034. Use `-d:nimLegacyReprWithNewline` for previous behavior. diff --git a/lib/posix/posix_linux_amd64.nim b/lib/posix/posix_linux_amd64.nim index 7f6a589f0..5c0b4d6cf 100644 --- a/lib/posix/posix_linux_amd64.nim +++ b/lib/posix/posix_linux_amd64.nim @@ -47,7 +47,7 @@ type d_ino*: Ino d_off*: Off d_reclen*: cushort - d_type*: int8 # cuchar really! + d_type*: int8 # uint8 really! d_name*: array[256, cchar] Tflock* {.importc: "struct flock", final, pure, diff --git a/lib/posix/posix_nintendoswitch.nim b/lib/posix/posix_nintendoswitch.nim index 44e431437..4cef80bb7 100644 --- a/lib/posix/posix_nintendoswitch.nim +++ b/lib/posix/posix_nintendoswitch.nim @@ -33,7 +33,7 @@ type Dirent* {.importc: "struct dirent", header: "", final, pure.} = object ## dirent_t struct d_ino*: Ino - d_type*: int8 # cuchar really! + d_type*: int8 # uint8 really! d_name*: array[256, cchar] Tflock* {.importc: "struct flock", final, pure, @@ -347,20 +347,20 @@ type SockAddr* {.importc: "struct sockaddr", header: "", pure, final.} = object ## struct sockaddr - sa_len: cuchar + sa_len: uint8 sa_family*: TSa_Family ## Address family. sa_data*: array[14, char] ## Socket address (variable-length data). Sockaddr_storage* {.importc: "struct sockaddr_storage", header: "", pure, final.} = object ## struct sockaddr_storage - ss_len: cuchar + ss_len: uint8 ss_family*: TSa_Family ## Address family. - ss_padding1: array[64 - sizeof(cuchar) - sizeof(cshort), char] + ss_padding1: array[64 - sizeof(uint8) - sizeof(cshort), char] ss_align: clonglong ss_padding2: array[ - 128 - sizeof(cuchar) - sizeof(cshort) - - (64 - sizeof(cuchar) - sizeof(cshort)) - 64, char] + 128 - sizeof(uint8) - sizeof(cshort) - + (64 - sizeof(uint8) - sizeof(cshort)) - 64, char] Tif_nameindex* {.importc: "struct if_nameindex", final, pure, header: "".} = object ## struct if_nameindex diff --git a/lib/pure/bitops.nim b/lib/pure/bitops.nim index b5b08985e..6d3865e5d 100644 --- a/lib/pure/bitops.nim +++ b/lib/pure/bitops.nim @@ -450,15 +450,15 @@ when useGCC_builtins: elif useVCC_builtins: # Search the mask data from most significant bit (MSB) to least significant bit (LSB) for a set bit (1). - func bitScanReverse(index: ptr culong, mask: culong): cuchar {. + func bitScanReverse(index: ptr culong, mask: culong): uint8 {. importc: "_BitScanReverse", header: "".} - func bitScanReverse64(index: ptr culong, mask: uint64): cuchar {. + func bitScanReverse64(index: ptr culong, mask: uint64): uint8 {. importc: "_BitScanReverse64", header: "".} # Search the mask data from least significant bit (LSB) to the most significant bit (MSB) for a set bit (1). - func bitScanForward(index: ptr culong, mask: culong): cuchar {. + func bitScanForward(index: ptr culong, mask: culong): uint8 {. importc: "_BitScanForward", header: "".} - func bitScanForward64(index: ptr culong, mask: uint64): cuchar {. + func bitScanForward64(index: ptr culong, mask: uint64): uint8 {. importc: "_BitScanForward64", header: "".} template vcc_scan_impl(fnc: untyped; v: untyped): int = @@ -468,15 +468,15 @@ elif useVCC_builtins: elif useICC_builtins: # Returns the number of trailing 0-bits in x, starting at the least significant bit position. If x is 0, the result is undefined. - func bitScanForward(p: ptr uint32, b: uint32): cuchar {. + func bitScanForward(p: ptr uint32, b: uint32): uint8 {. importc: "_BitScanForward", header: "".} - func bitScanForward64(p: ptr uint32, b: uint64): cuchar {. + func bitScanForward64(p: ptr uint32, b: uint64): uint8 {. importc: "_BitScanForward64", header: "".} # Returns the number of leading 0-bits in x, starting at the most significant bit position. If x is 0, the result is undefined. - func bitScanReverse(p: ptr uint32, b: uint32): cuchar {. + func bitScanReverse(p: ptr uint32, b: uint32): uint8 {. importc: "_BitScanReverse", header: "".} - func bitScanReverse64(p: ptr uint32, b: uint64): cuchar {. + func bitScanReverse64(p: ptr uint32, b: uint64): uint8 {. importc: "_BitScanReverse64", header: "".} template icc_scan_impl(fnc: untyped; v: untyped): int = @@ -664,7 +664,7 @@ when useBuiltinsRotate: when defined(gcc): # GCC was tested until version 4.8.1 and intrinsics were present. Not tested # in previous versions. - func builtin_rotl8(value: cuchar, shift: cint): cuchar + func builtin_rotl8(value: uint8, shift: cint): uint8 {.importc: "__rolb", header: "".} func builtin_rotl16(value: cushort, shift: cint): cushort {.importc: "__rolw", header: "".} @@ -674,7 +674,7 @@ when useBuiltinsRotate: func builtin_rotl64(value: culonglong, shift: cint): culonglong {.importc: "__rolq", header: "".} - func builtin_rotr8(value: cuchar, shift: cint): cuchar + func builtin_rotr8(value: uint8, shift: cint): uint8 {.importc: "__rorb", header: "".} func builtin_rotr16(value: cushort, shift: cint): cushort {.importc: "__rorw", header: "".} @@ -690,7 +690,7 @@ when useBuiltinsRotate: # https://releases.llvm.org/8.0.0/tools/clang/docs/ReleaseNotes.html#non-comprehensive-list-of-changes-in-this-release # https://releases.llvm.org/8.0.0/tools/clang/docs/LanguageExtensions.html#builtin-rotateleft # source for correct declarations: https://github.com/llvm/llvm-project/blob/main/clang/include/clang/Basic/Builtins.def - func builtin_rotl8(value: cuchar, shift: cuchar): cuchar + func builtin_rotl8(value: uint8, shift: uint8): uint8 {.importc: "__builtin_rotateleft8", nodecl.} func builtin_rotl16(value: cushort, shift: cushort): cushort {.importc: "__builtin_rotateleft16", nodecl.} @@ -700,7 +700,7 @@ when useBuiltinsRotate: func builtin_rotl64(value: culonglong, shift: culonglong): culonglong {.importc: "__builtin_rotateleft64", nodecl.} - func builtin_rotr8(value: cuchar, shift: cuchar): cuchar + func builtin_rotr8(value: uint8, shift: uint8): uint8 {.importc: "__builtin_rotateright8", nodecl.} func builtin_rotr16(value: cushort, shift: cushort): cushort {.importc: "__builtin_rotateright16", nodecl.} @@ -716,9 +716,9 @@ when useBuiltinsRotate: # https://docs.microsoft.com/en-us/cpp/intrinsics/rotl8-rotl16?view=msvc-160 # https://docs.microsoft.com/en-us/cpp/intrinsics/rotr8-rotr16?view=msvc-160 # https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/rotl-rotl64-rotr-rotr64?view=msvc-160 - func builtin_rotl8(value: cuchar, shift: cuchar): cuchar + func builtin_rotl8(value: uint8, shift: uint8): uint8 {.importc: "_rotl8", header: "".} - func builtin_rotl16(value: cushort, shift: cuchar): cushort + func builtin_rotl16(value: cushort, shift: uint8): cushort {.importc: "_rotl16", header: "".} func builtin_rotl32(value: cuint, shift: cint): cuint {.importc: "_rotl", header: "".} @@ -726,9 +726,9 @@ when useBuiltinsRotate: func builtin_rotl64(value: culonglong, shift: cint): culonglong {.importc: "_rotl64", header: "".} - func builtin_rotr8(value: cuchar, shift: cuchar): cuchar + func builtin_rotr8(value: uint8, shift: uint8): uint8 {.importc: "_rotr8", header: "".} - func builtin_rotr16(value: cushort, shift: cuchar): cushort + func builtin_rotr16(value: cushort, shift: uint8): cushort {.importc: "_rotr16", header: "".} func builtin_rotr32(value: cuint, shift: cint): cuint {.importc: "_rotr", header: "".} @@ -738,7 +738,7 @@ when useBuiltinsRotate: elif defined(icl): # Tested on Intel(R) C++ Intel(R) 64 Compiler Classic Version 2021.1.2 Build # 20201208_000000 x64 and x86. Not tested in previous versions. - func builtin_rotl8(value: cuchar, shift: cint): cuchar + func builtin_rotl8(value: uint8, shift: cint): uint8 {.importc: "__rolb", header: "".} func builtin_rotl16(value: cushort, shift: cint): cushort {.importc: "__rolw", header: "".} @@ -748,7 +748,7 @@ when useBuiltinsRotate: func builtin_rotl64(value: culonglong, shift: cint): culonglong {.importc: "__rolq", header: "".} - func builtin_rotr8(value: cuchar, shift: cint): cuchar + func builtin_rotr8(value: uint8, shift: cint): uint8 {.importc: "__rorb", header: "".} func builtin_rotr16(value: cushort, shift: cint): cushort {.importc: "__rorw", header: "".} @@ -777,7 +777,7 @@ func shiftTypeTo(size: static int, shift: int): auto {.inline.} = when (defined(vcc) and (size in [4, 8])) or defined(gcc) or defined(icl): cint(shift) elif (defined(vcc) and (size in [1, 2])) or (defined(clang) and size == 1): - cuchar(shift) + uint8(shift) elif defined(clang): when size == 2: cushort(shift) @@ -802,7 +802,7 @@ func rotateLeftBits*[T: SomeUnsignedInt](value: T, shift: range[0..(sizeof(T) * when useBuiltinsRotate: const size = sizeof(T) when size == 1: - builtin_rotl8(value.cuchar, shiftTypeTo(size, shift)).T + builtin_rotl8(value.uint8, shiftTypeTo(size, shift)).T elif size == 2: builtin_rotl16(value.cushort, shiftTypeTo(size, shift)).T elif size == 4: @@ -830,7 +830,7 @@ func rotateRightBits*[T: SomeUnsignedInt](value: T, shift: range[0..(sizeof(T) * when useBuiltinsRotate: const size = sizeof(T) when size == 1: - builtin_rotr8(value.cuchar, shiftTypeTo(size, shift)).T + builtin_rotr8(value.uint8, shiftTypeTo(size, shift)).T elif size == 2: builtin_rotr16(value.cushort, shiftTypeTo(size, shift)).T elif size == 4: diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 9c3827751..934dd034e 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -503,7 +503,7 @@ when defineSsl: ## Retrieve the ssl pointer of `socket`. ## Useful for interfacing with `openssl`. self.sslHandle - + proc raiseSSLError*(s = "") = ## Raises a new SSL error. if s != "": @@ -688,7 +688,7 @@ when defineSsl: return ctx.getExtraInternal().clientGetPskFunc proc pskClientCallback(ssl: SslPtr; hint: cstring; identity: cstring; - max_identity_len: cuint; psk: ptr cuchar; + max_identity_len: cuint; psk: ptr uint8; max_psk_len: cuint): cuint {.cdecl.} = let ctx = SslContext(context: ssl.SSL_get_SSL_CTX) let hintString = if hint == nil: "" else: $hint @@ -714,7 +714,7 @@ when defineSsl: proc serverGetPskFunc*(ctx: SslContext): SslServerGetPskFunc = return ctx.getExtraInternal().serverGetPskFunc - proc pskServerCallback(ssl: SslCtx; identity: cstring; psk: ptr cuchar; + proc pskServerCallback(ssl: SslCtx; identity: cstring; psk: ptr uint8; max_psk_len: cint): cuint {.cdecl.} = let ctx = SslContext(context: ssl.SSL_get_SSL_CTX) let pskString = (ctx.serverGetPskFunc)($identity) diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index 283652bb3..ce5660557 100644 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -761,7 +761,7 @@ template styledWriteLine*(f: File, args: varargs[untyped]) = runnableExamples: proc error(msg: string) = styledWriteLine(stderr, fgRed, "Error: ", resetStyle, msg) - + styledWrite(f, args) write(f, "\n") diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index 4c51577ac..75983e3c6 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -132,7 +132,7 @@ elif defined(windows): type PVOID = pointer BCRYPT_ALG_HANDLE = PVOID - PUCHAR = ptr cuchar + PUCHAR = ptr uint8 NTSTATUS = clong ULONG = culong diff --git a/lib/system.nim b/lib/system.nim index 8aa9bd718..57d620e57 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1427,8 +1427,8 @@ type # these work for most platforms: ## This is the same as the type `long double` in *C*. ## This C type is not supported by Nim's code generator. - cuchar* {.importc: "unsigned char", nodecl.} = uint8 - ## This is the same as the type `unsigned char` in *C*. + cuchar* {.importc: "unsigned char", nodecl, deprecated: "use `char` or `uint8` instead".} = char + ## Deprecated: Use `uint8` instead. cushort* {.importc: "unsigned short", nodecl.} = uint16 ## This is the same as the type `unsigned short` in *C*. cuint* {.importc: "unsigned int", nodecl.} = uint32 diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index a71dd97d8..d901af244 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -42,7 +42,7 @@ type PULONG_PTR* = ptr uint HDC* = Handle HGLRC* = Handle - BYTE* = cuchar + BYTE* = uint8 SECURITY_ATTRIBUTES* {.final, pure.} = object nLength*: int32 @@ -717,7 +717,7 @@ const FILE_WRITE_DATA* = 0x00000002 # file & pipe # Error Constants -const +const ERROR_FILE_NOT_FOUND* = 2 ## https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499- ERROR_PATH_NOT_FOUND* = 3 ERROR_ACCESS_DENIED* = 5 diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim index 1a8893957..c756fa938 100644 --- a/lib/wrappers/openssl.nim +++ b/lib/wrappers/openssl.nim @@ -520,16 +520,16 @@ proc OPENSSL_sk_num*(stack: PSTACK): int {.cdecl, dynlib: DLLSSLName, importc.} proc OPENSSL_sk_value*(stack: PSTACK, index: int): pointer {.cdecl, dynlib: DLLSSLName, importc.} -proc d2i_X509*(px: ptr PX509, i: ptr ptr cuchar, len: cint): PX509 {.cdecl, +proc d2i_X509*(px: ptr PX509, i: ptr ptr uint8, len: cint): PX509 {.cdecl, dynlib: DLLUtilName, importc.} -proc i2d_X509*(cert: PX509; o: ptr ptr cuchar): cint {.cdecl, +proc i2d_X509*(cert: PX509; o: ptr ptr uint8): cint {.cdecl, dynlib: DLLUtilName, importc.} proc d2i_X509*(b: string): PX509 = ## decode DER/BER bytestring into X.509 certificate struct var bb = b.cstring - let i = cast[ptr ptr cuchar](addr bb) + let i = cast[ptr ptr uint8](addr bb) let ret = d2i_X509(addr result, i, b.len.cint) if ret.isNil: raise newException(Exception, "X.509 certificate decoding failed") @@ -539,7 +539,7 @@ proc i2d_X509*(cert: PX509): string = let encoded_length = i2d_X509(cert, nil) result = newString(encoded_length) var q = result.cstring - let o = cast[ptr ptr cuchar](addr q) + let o = cast[ptr ptr uint8](addr q) let length = i2d_X509(cert, o) if length.int <= 0: raise newException(Exception, "X.509 certificate encoding failed") @@ -599,11 +599,11 @@ proc SSL_CTX_set_tlsext_servername_arg*(ctx: SslCtx, arg: pointer): int = type PskClientCallback* = proc (ssl: SslPtr; - hint: cstring; identity: cstring; max_identity_len: cuint; psk: ptr cuchar; + hint: cstring; identity: cstring; max_identity_len: cuint; psk: ptr uint8; max_psk_len: cuint): cuint {.cdecl.} PskServerCallback* = proc (ssl: SslPtr; - identity: cstring; psk: ptr cuchar; max_psk_len: cint): cuint {.cdecl.} + identity: cstring; psk: ptr uint8; max_psk_len: cint): cuint {.cdecl.} proc SSL_CTX_set_psk_client_callback*(ctx: SslCtx; callback: PskClientCallback) {.cdecl, dynlib: DLLSSLName, importc.} ## Set callback called when OpenSSL needs PSK (for client). @@ -672,13 +672,13 @@ proc PEM_read_bio_RSAPublicKey*(bp: BIO, x: ptr PRSA, cb: pem_password_cb, u: po dynlib: DLLUtilName, importc.} proc PEM_read_bio_RSAPrivateKey*(bp: BIO, x: ptr PRSA, cb: pem_password_cb, u: pointer): PRSA {.cdecl, dynlib: DLLUtilName, importc.} -proc RSA_private_encrypt*(flen: cint, fr: ptr cuchar, to: ptr cuchar, rsa: PRSA, padding: PaddingType): cint {.cdecl, +proc RSA_private_encrypt*(flen: cint, fr: ptr uint8, to: ptr uint8, rsa: PRSA, padding: PaddingType): cint {.cdecl, dynlib: DLLUtilName, importc.} -proc RSA_public_encrypt*(flen: cint, fr: ptr cuchar, to: ptr cuchar, rsa: PRSA, padding: PaddingType): cint {.cdecl, +proc RSA_public_encrypt*(flen: cint, fr: ptr uint8, to: ptr uint8, rsa: PRSA, padding: PaddingType): cint {.cdecl, dynlib: DLLUtilName, importc.} -proc RSA_private_decrypt*(flen: cint, fr: ptr cuchar, to: ptr cuchar, rsa: PRSA, padding: PaddingType): cint {.cdecl, +proc RSA_private_decrypt*(flen: cint, fr: ptr uint8, to: ptr uint8, rsa: PRSA, padding: PaddingType): cint {.cdecl, dynlib: DLLUtilName, importc.} -proc RSA_public_decrypt*(flen: cint, fr: ptr cuchar, to: ptr cuchar, rsa: PRSA, padding: PaddingType): cint {.cdecl, +proc RSA_public_decrypt*(flen: cint, fr: ptr uint8, to: ptr uint8, rsa: PRSA, padding: PaddingType): cint {.cdecl, dynlib: DLLUtilName, importc.} proc RSA_free*(rsa: PRSA) {.cdecl, dynlib: DLLUtilName, importc.} proc RSA_size*(rsa: PRSA): cint {.cdecl, dynlib: DLLUtilName, importc.} @@ -744,8 +744,8 @@ type proc md5_Init*(c: var MD5_CTX): cint{.importc: "MD5_Init".} proc md5_Update*(c: var MD5_CTX; data: pointer; len: csize_t): cint{.importc: "MD5_Update".} proc md5_Final*(md: cstring; c: var MD5_CTX): cint{.importc: "MD5_Final".} -proc md5*(d: ptr cuchar; n: csize_t; md: ptr cuchar): ptr cuchar{.importc: "MD5".} -proc md5_Transform*(c: var MD5_CTX; b: ptr cuchar){.importc: "MD5_Transform".} +proc md5*(d: ptr uint8; n: csize_t; md: ptr uint8): ptr uint8{.importc: "MD5".} +proc md5_Transform*(c: var MD5_CTX; b: ptr uint8){.importc: "MD5_Transform".} {.pop.} from strutils import toHex, toLowerAscii diff --git a/tests/stdlib/topenssl.nim b/tests/stdlib/topenssl.nim index 75e1ba868..4c38b4216 100644 --- a/tests/stdlib/topenssl.nim +++ b/tests/stdlib/topenssl.nim @@ -12,8 +12,8 @@ proc rsaPublicEncrypt(fr: string): string = doAssert rsa != nil doAssert BIO_free(bio) >= 0 result = newString(RSA_size(rsa)) - let frdata = cast[ptr cuchar](fr.cstring) - var todata = cast[ptr cuchar](result.cstring) + let frdata = cast[ptr uint8](fr.cstring) + var todata = cast[ptr uint8](result.cstring) doAssert RSA_public_encrypt(fr.len.cint, frdata, todata, rsa, RSA_PKCS1_PADDING) != -1 RSA_free(rsa) @@ -26,8 +26,8 @@ proc rasPrivateDecrypt(fr: string): string = doAssert BIO_free(bio) >= 0 let rsaLen = RSA_size(rsa) result = newString(rsaLen) - let frdata = cast[ptr cuchar](fr.cstring) - var todata = cast[ptr cuchar](result.cstring) + let frdata = cast[ptr uint8](fr.cstring) + var todata = cast[ptr uint8](result.cstring) let lenOrig = RSA_private_decrypt(rsaLen, frdata, todata, rsa, RSA_PKCS1_PADDING) doAssert lenOrig >= 0 and lenOrig < result.len doAssert result[lenOrig] == '\0' From 25efb5386293540b0542833625d3fb6e22f3cfbc Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 16 Jul 2021 18:29:49 +0200 Subject: [PATCH 2342/3768] make dragonbox opt-in via -d:nimFpRoundtrips (#18504) * make dragonbox opt-in via -d:nimFpRoundtrips * make tests green again * make tests green again --- changelog.md | 12 ++++++------ lib/system/formatfloat.nim | 2 +- lib/system/strmantle.nim | 2 +- tests/errmsgs/treportunused.nim | 14 ++++++++------ tests/float/nim.cfg | 1 + tests/float/tfloats.nim | 4 ++++ tests/stdlib/tjson.nim | 6 +++--- tests/stdlib/tjsonutils.nim | 21 +++++++++++---------- 8 files changed, 35 insertions(+), 27 deletions(-) create mode 100644 tests/float/nim.cfg diff --git a/changelog.md b/changelog.md index 568ff0bba..289202587 100644 --- a/changelog.md +++ b/changelog.md @@ -76,9 +76,6 @@ - `json` and `jsonutils` now serialize NaN, Inf, -Inf as strings, so that `%[NaN, -Inf]` is the string `["nan","-inf"]` instead of `[nan,-inf]` which was invalid json. -- `system.addFloat` now uses the "Dragonbox" algorithm, which ensures correct roundtrips of floating point - numbers, that the minimum length representation of a floating point number is used and correct rounding. - Use `-d:nimLegacyAddFloat` for a transition period. - `strformat` is now part of `include std/prelude`. @@ -102,6 +99,12 @@ added support for parenthesized expressions. added support for const string's instead of just string literals + +- `system.addFloat` and `system.$` now can produce string representations of floating point numbers + that are minimal in size and that "roundtrip" (via the "Dragonbox" algorithm). This currently has + to be enabled via `-d:nimFpRoundtrips`. It is expected that this behavior becomes the new default + in upcoming versions. + - Fixed buffer overflow bugs in `net` - Exported `sslHandle` from `net` and `asyncnet`. @@ -116,8 +119,6 @@ the OpenSSL DLLs (e.g. libssl-1_1-x64.dll, libcrypto-1_1-x64.dll) you now also need to ship `cacert.pem` with your `.exe` file. -- Make `{.requiresInit.}` pragma to work for `distinct` types. - - `typetraits`: `distinctBase` now is identity instead of error for non distinct types. Added `enumLen` to return the number of elements in an enum. @@ -468,7 +469,6 @@ with `--styleCheck:error` or `--styleCheck:hint`. - ## Tool changes - Latex doc generation is revised: output `.tex` files should be compiled diff --git a/lib/system/formatfloat.nim b/lib/system/formatfloat.nim index 41bc56adc..772f0a848 100644 --- a/lib/system/formatfloat.nim +++ b/lib/system/formatfloat.nim @@ -7,7 +7,7 @@ # distribution, for details about the copyright. # -when not defined(nimLegacyAddFloat) and not defined(nimscript) and +when defined(nimFpRoundtrips) and not defined(nimscript) and not defined(js) and defined(nimHasDragonBox): import dragonbox diff --git a/lib/system/strmantle.nim b/lib/system/strmantle.nim index 63e341763..041272175 100644 --- a/lib/system/strmantle.nim +++ b/lib/system/strmantle.nim @@ -97,7 +97,7 @@ proc nimFloatToStr(f: float): string {.compilerproc.} = result = newStringOfCap(8) result.addFloat f -when not defined(nimLegacyAddFloat) and not defined(nimscript) and +when defined(nimFpRoundtrips) and not defined(nimscript) and not defined(js) and defined(nimHasDragonBox): import schubfach diff --git a/tests/errmsgs/treportunused.nim b/tests/errmsgs/treportunused.nim index 65e00163b..f9b7c3d11 100644 --- a/tests/errmsgs/treportunused.nim +++ b/tests/errmsgs/treportunused.nim @@ -13,12 +13,12 @@ treportunused.nim(30, 5) Hint: 's8' is declared but not used [XDeclaredButNotUse treportunused.nim(31, 5) Hint: 's9' is declared but not used [XDeclaredButNotUsed] treportunused.nim(32, 6) Hint: 's10' is declared but not used [XDeclaredButNotUsed] treportunused.nim(33, 6) Hint: 's11' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(37, 3) Hint: 'v0.99' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(38, 3) Hint: 'v0.99.99' is declared but not used [XDeclaredButNotUsed] ''' action: compile """ +#treportunused.nim(37, 3) Hint: 'v0.99' is declared but not used [XDeclaredButNotUsed] +#treportunused.nim(38, 3) Hint: 'v0.99.99' is declared but not used [XDeclaredButNotUsed] # bug #9764 iterator s1(a:string): int = discard iterator s2(): int = discard @@ -32,7 +32,9 @@ var s9: int type s10 = object type s11 = type(1.2) -# https://github.com/nim-lang/Nim/issues/14407 -let - `v0.99` = "0.99" - `v0.99.99` = "0.99.99" +when false: + # enabled again when Nim bootstraps with -d:nimFpRoundtrips + # https://github.com/nim-lang/Nim/issues/14407 + let + `v0.99` = "0.99" + `v0.99.99` = "0.99.99" diff --git a/tests/float/nim.cfg b/tests/float/nim.cfg new file mode 100644 index 000000000..d27bbf43b --- /dev/null +++ b/tests/float/nim.cfg @@ -0,0 +1 @@ +-d:nimFpRoundtrips diff --git a/tests/float/tfloats.nim b/tests/float/tfloats.nim index 406ddb6d9..30d9c50d6 100644 --- a/tests/float/tfloats.nim +++ b/tests/float/tfloats.nim @@ -66,6 +66,8 @@ template main = var a = 1.1'f32 doAssert $a == "1.1", $a # was failing +proc runtimeOnlyTests = + # enable for 'static' once -d:nimFpRoundtrips became the default block: # bug #7717 proc test(f: float) = let f2 = $f @@ -82,3 +84,5 @@ template main = static: main() main() + +runtimeOnlyTests() diff --git a/tests/stdlib/tjson.nim b/tests/stdlib/tjson.nim index 289ef9d05..000f72038 100644 --- a/tests/stdlib/tjson.nim +++ b/tests/stdlib/tjson.nim @@ -303,7 +303,7 @@ let jsonNode = %*mynode doAssert $jsonNode == """{"kind":"P","pChildren":[{"kind":"Text","textStr":"mychild"},{"kind":"Br"}]}""" doAssert $jsonNode.to(ContentNode) == """(kind: P, pChildren: @[(kind: Text, textStr: "mychild"), (kind: Br)])""" -block: # bug #17383 +when defined(nimFpRoundtrips): # bug #17383 testRoundtrip(int32.high): "2147483647" testRoundtrip(uint32.high): "4294967295" when int.sizeof == 4: @@ -316,7 +316,7 @@ block: # bug #17383 testRoundtrip(int64.high): "9223372036854775807" testRoundtrip(uint64.high): "18446744073709551615" -block: # bug #18007 +when defined(nimFpRoundtrips): # bug #18007 testRoundtrip([NaN, Inf, -Inf, 0.0, -0.0, 1.0]): """["nan","inf","-inf",0.0,-0.0,1.0]""" # pending https://github.com/nim-lang/Nim/issues/18025 use: # testRoundtrip([float32(NaN), Inf, -Inf, 0.0, -0.0, 1.0]) @@ -332,7 +332,7 @@ block: # bug #18007 testRoundtripVal(0.0): "0.0" testRoundtripVal(-0.0): "-0.0" -block: # bug #15397, bug #13196 +when defined(nimFpRoundtrips): # bug #15397, bug #13196 testRoundtripVal(1.0 + epsilon(float64)): "1.0000000000000002" testRoundtripVal(0.12345678901234567890123456789): "0.12345678901234568" diff --git a/tests/stdlib/tjsonutils.nim b/tests/stdlib/tjsonutils.nim index b0c444cbf..3e9c422e0 100644 --- a/tests/stdlib/tjsonutils.nim +++ b/tests/stdlib/tjsonutils.nim @@ -44,7 +44,7 @@ proc `$`(a: MyEnum): string = if a == me2: "me2Modif" else: system.`$`(a) -template fn() = +template fn() = block: # toJson, jsonTo type Foo = distinct float testRoundtrip('x', """120""") @@ -161,15 +161,16 @@ template fn() = doAssert b[2].signbit doAssert not b[3].signbit - block: # bug #15397, bug #13196 - let a = 0.1 - let x = 0.12345678901234567890123456789 - let b = (a + 0.2, 0.3, x) - testRoundtripVal(b): "[0.30000000000000004,0.3,0.12345678901234568]" + when defined(nimFpRoundtrips): + block: # bug #15397, bug #13196 + let a = 0.1 + let x = 0.12345678901234567890123456789 + let b = (a + 0.2, 0.3, x) + testRoundtripVal(b): "[0.30000000000000004,0.3,0.12345678901234568]" - testRoundtripVal(0.12345678901234567890123456789): "0.12345678901234568" - testRoundtripVal(epsilon(float64)): "2.220446049250313e-16" - testRoundtripVal(1.0 + epsilon(float64)): "1.0000000000000002" + testRoundtripVal(0.12345678901234567890123456789): "0.12345678901234568" + testRoundtripVal(epsilon(float64)): "2.220446049250313e-16" + testRoundtripVal(1.0 + epsilon(float64)): "1.0000000000000002" block: # case object type Foo = object @@ -433,7 +434,7 @@ template fn() = """{"b": true, "bt": false, "btf": "test"}""" testRoundtrip(Variant(b: true, bt: true, btt: 'c')): """{"b": true, "bt": true, "btt": "c"}""" - + # TODO: Add additional tests with missing and extra JSON keys, both when # allowed and forbidden analogous to the tests for the not nested # variant objects. From 923a1c6ea7d9f45b6389680717692690218228fb Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 16 Jul 2021 22:54:47 -0700 Subject: [PATCH 2343/3768] fix nativeToUnixPath (#18501) --- lib/std/private/globs.nim | 11 ++++++++--- tests/stdlib/tglobs.nim | 20 ++++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) create mode 100644 tests/stdlib/tglobs.nim diff --git a/lib/std/private/globs.nim b/lib/std/private/globs.nim index 55f6a40bd..b3726c9c3 100644 --- a/lib/std/private/globs.nim +++ b/lib/std/private/globs.nim @@ -45,10 +45,15 @@ iterator walkDirRecFilter*(dir: string, follow: proc(entry: PathEntry): bool = n proc nativeToUnixPath*(path: string): string = # pending https://github.com/nim-lang/Nim/pull/13265 - doAssert not path.isAbsolute # not implemented here; absolute files need more care for the drive + result = path + when defined(windows): + if path.len >= 2 and path[0] in {'a'..'z', 'A'..'Z'} and path[1] == ':': + result[0] = '/' + result[1] = path[0] + if path.len > 2 and path[2] != '\\': + doAssert false, "paths like `C:foo` are currently unsupported, path: " & path when DirSep == '\\': - result = replace(path, '\\', '/') - else: result = path + result = replace(result, '\\', '/') when isMainModule: import sugar diff --git a/tests/stdlib/tglobs.nim b/tests/stdlib/tglobs.nim new file mode 100644 index 000000000..739a127f8 --- /dev/null +++ b/tests/stdlib/tglobs.nim @@ -0,0 +1,20 @@ +import std/private/globs + +template main = + when defined(windows): + doAssert nativeToUnixPath("C:") == "/C" + doAssert nativeToUnixPath(r"D:\") == "/D/" + doAssert nativeToUnixPath(r"E:\a") == "/E/a" + doAssert nativeToUnixPath(r"E:\a1\") == "/E/a1/" + doAssert nativeToUnixPath(r"E:\a1\bc") == "/E/a1/bc" + doAssert nativeToUnixPath(r"\a1\bc") == "/a1/bc" + doAssert nativeToUnixPath(r"a1\bc") == "a1/bc" + doAssert nativeToUnixPath("a1") == "a1" + doAssert nativeToUnixPath("") == "" + doAssert nativeToUnixPath(".") == "." + doAssert nativeToUnixPath("..") == ".." + doAssert nativeToUnixPath(r"..\") == "../" + doAssert nativeToUnixPath(r"..\..\.\") == "../.././" + +static: main() +main() From ac5435ecd0a08d1a0bdd9d6d6cd89577634ecb0f Mon Sep 17 00:00:00 2001 From: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Date: Sun, 18 Jul 2021 10:49:03 +0200 Subject: [PATCH 2344/3768] Make error message for empty new-styled concept more descriptive (#18506) * Allow empty new-styled concept Slightly improve error messages * Make empty new-styled concepts an error --- compiler/concepts.nim | 15 +++++++-------- compiler/parser.nim | 2 ++ tests/concepts/tspec.nim | 5 ++--- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/compiler/concepts.nim b/compiler/concepts.nim index de994f1b8..885b69c60 100644 --- a/compiler/concepts.nim +++ b/compiler/concepts.nim @@ -11,8 +11,7 @@ ## for details. Note this is a first implementation and only the "Concept matching" ## section has been implemented. -import ast, astalgo, semdata, lookups, lineinfos, idents, msgs, renderer, - types, intsets +import ast, astalgo, semdata, lookups, lineinfos, idents, msgs, renderer, types, intsets from magicsys import addSonSkipIntLit @@ -23,7 +22,7 @@ const ## -------------------------------------- proc declareSelf(c: PContext; info: TLineInfo) = - ## adds the magical 'Self' symbols to the current scope. + ## Adds the magical 'Self' symbols to the current scope. let ow = getCurrOwner(c) let s = newSym(skType, getIdent(c.cache, "Self"), nextSymId(c.idgen), ow, info) s.typ = newType(tyTypeDesc, nextTypeId(c.idgen), ow) @@ -32,7 +31,7 @@ proc declareSelf(c: PContext; info: TLineInfo) = addDecl(c, s, info) proc isSelf*(t: PType): bool {.inline.} = - ## is this the magical 'Self' type? + ## Is this the magical 'Self' type? t.kind == tyTypeDesc and tfPacked in t.flags proc makeTypeDesc*(c: PContext, typ: PType): PType = @@ -45,8 +44,8 @@ proc makeTypeDesc*(c: PContext, typ: PType): PType = proc semConceptDecl(c: PContext; n: PNode): PNode = ## Recursive helper for semantic checking for the concept declaration. - ## Currently we only support lists of statements containing 'proc' - ## declarations and the like. + ## Currently we only support (possibly empty) lists of statements + ## containing 'proc' declarations and the like. case n.kind of nkStmtList, nkStmtListExpr: result = shallowCopy(n) @@ -60,7 +59,7 @@ proc semConceptDecl(c: PContext; n: PNode): PNode = result[i] = n[i] result[^1] = semConceptDecl(c, n[^1]) else: - localError(c.config, n.info, "unexpected construct in the new-styled concept " & renderTree(n)) + localError(c.config, n.info, "unexpected construct in the new-styled concept: " & renderTree(n)) result = n proc semConceptDeclaration*(c: PContext; n: PNode): PNode = @@ -97,7 +96,7 @@ proc existingBinding(m: MatchCon; key: PType): PType = proc conceptMatchNode(c: PContext; n: PNode; m: var MatchCon): bool proc matchType(c: PContext; f, a: PType; m: var MatchCon): bool = - ## the heart of the concept matching process. 'f' is the formal parameter of some + ## The heart of the concept matching process. 'f' is the formal parameter of some ## routine inside the concept that we're looking for. 'a' is the formal parameter ## of a routine that might match. const diff --git a/compiler/parser.nim b/compiler/parser.nim index b6c01e9c6..d00a17e9f 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -2058,6 +2058,8 @@ proc parseTypeClass(p: var Parser): PNode = skipComment(p, result) # an initial IND{>} HAS to follow: if not realInd(p): + if result.isNewStyleConcept: + parMessage(p, "routine expected, but found '$1' (empty new-styled concepts are not allowed)", p.tok) result.add(p.emptyNode) else: result.add(parseStmt(p)) diff --git a/tests/concepts/tspec.nim b/tests/concepts/tspec.nim index 1bca3c11b..52f13a40a 100644 --- a/tests/concepts/tspec.nim +++ b/tests/concepts/tspec.nim @@ -7,8 +7,7 @@ discard """ 2 3 yes int -string int -true''' +string int''' joinable: false """ @@ -102,5 +101,5 @@ type Monoid = concept proc z(x: typedesc[int]): int = 0 -echo(int is Monoid) +doAssert int is Monoid From 8091545f95143d520c64b14f88bb5ce2818c893c Mon Sep 17 00:00:00 2001 From: Miran Date: Sun, 18 Jul 2021 11:04:19 +0200 Subject: [PATCH 2345/3768] Revert #17398 and #17402 (#18480) * Revert "followup #17398: `getTempDir`, `getConfigDir` now do not have trailing DirSep (#17402)" This reverts commit 2356d0603f70cad90f76fa57999054bf5c0a9157. * Revert "fix #17393 getHomeDir and expandTilde should not include trailing `/` (#17398)" This reverts commit bebf2ce24a43bef4cde5c90c4010631a1e4a7927. * fix test --- changelog.md | 3 --- lib/pure/os.nim | 40 ++++++++++++++----------------------- tests/stdlib/tos.nim | 6 +++--- tests/stdlib/ttempfiles.nim | 2 +- 4 files changed, 19 insertions(+), 32 deletions(-) diff --git a/changelog.md b/changelog.md index 289202587..16f2192dc 100644 --- a/changelog.md +++ b/changelog.md @@ -39,9 +39,6 @@ underlying code is also updated the same way. - Custom pragma values have now an API for use in macros. -- In `std/os`, `getHomeDir`, `expandTilde`, `getTempDir`, `getConfigDir` now do not include trailing `DirSep`, - unless `-d:nimLegacyHomeDir` is specified (for a transition period). - - On POSIX systems, the default signal handlers used for Nim programs (it's used for printing the stacktrace on fatal signals) will now re-raise the signal for the OS default handlers to handle. diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 213da2aed..dadc380e6 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -901,14 +901,9 @@ proc getHomeDir*(): string {.rtl, extern: "nos$1", ## * `setCurrentDir proc <#setCurrentDir,string>`_ runnableExamples: assert getHomeDir() == expandTilde("~") - # `getHomeDir()` doesn't end in `DirSep` even if `$HOME` (on posix) or - # `$USERPROFILE` (on windows) does, unless `-d:nimLegacyHomeDir` is specified. - from std/strutils import endsWith - assert not getHomeDir().endsWith DirSep - when defined(windows): result = getEnv("USERPROFILE") - else: result = getEnv("HOME") - result.normalizePathEnd(trailingSep = defined(nimLegacyHomeDir)) + when defined(windows): return getEnv("USERPROFILE") & "\\" + else: return getEnv("HOME") & "/" proc getConfigDir*(): string {.rtl, extern: "nos$1", tags: [ReadEnvEffect, ReadIOEffect].} = @@ -917,21 +912,23 @@ proc getConfigDir*(): string {.rtl, extern: "nos$1", ## On non-Windows OSs, this proc conforms to the XDG Base Directory ## spec. Thus, this proc returns the value of the `XDG_CONFIG_HOME` environment ## variable if it is set, otherwise it returns the default configuration - ## directory ("~/.config"). + ## directory ("~/.config/"). + ## + ## An OS-dependent trailing slash is always present at the end of the + ## returned string: `\\` on Windows and `/` on all other OSs. ## ## See also: ## * `getHomeDir proc <#getHomeDir>`_ ## * `getTempDir proc <#getTempDir>`_ - ## * `getCacheDir proc <#getCacheDir>`_ - runnableExamples: - from std/strutils import endsWith - # See `getHomeDir` for behavior regarding trailing DirSep. - assert not getConfigDir().endsWith DirSep + ## * `expandTilde proc <#expandTilde,string>`_ + ## * `getCurrentDir proc <#getCurrentDir>`_ + ## * `setCurrentDir proc <#setCurrentDir,string>`_ when defined(windows): result = getEnv("APPDATA") else: result = getEnv("XDG_CONFIG_HOME", getEnv("HOME") / ".config") - result.normalizePathEnd(trailingSep = defined(nimLegacyHomeDir)) + result.normalizePathEnd(trailingSep = true) + proc getCacheDir*(): string = ## Returns the cache directory of the current user for applications. @@ -1006,11 +1003,9 @@ proc getTempDir*(): string {.rtl, extern: "nos$1", ## See also: ## * `getHomeDir proc <#getHomeDir>`_ ## * `getConfigDir proc <#getConfigDir>`_ - ## * `getCacheDir proc <#getCacheDir>`_ - runnableExamples: - from std/strutils import endsWith - # See `getHomeDir` for behavior regarding trailing DirSep. - assert not getTempDir().endsWith(DirSep) + ## * `expandTilde proc <#expandTilde,string>`_ + ## * `getCurrentDir proc <#getCurrentDir>`_ + ## * `setCurrentDir proc <#setCurrentDir,string>`_ const tempDirDefault = "/tmp" when defined(tempDir): const tempDir {.strdefine.}: string = tempDirDefault @@ -1031,7 +1026,7 @@ proc getTempDir*(): string {.rtl, extern: "nos$1", getTempDirImpl(result) if result.len == 0: result = tempDirDefault - result.normalizePathEnd(trailingSep = defined(nimLegacyHomeDir)) + normalizePathEnd(result, trailingSep=true) proc expandTilde*(path: string): string {. tags: [ReadEnvEffect, ReadIOEffect].} = @@ -1041,8 +1036,6 @@ proc expandTilde*(path: string): string {. ## Windows: this is still supported despite the Windows platform not having this ## convention; also, both ``~/`` and ``~\`` are handled. ## - ## .. warning:: `~bob` and `~bob/` are not yet handled correctly. - ## ## See also: ## * `getHomeDir proc <#getHomeDir>`_ ## * `getConfigDir proc <#getConfigDir>`_ @@ -1053,9 +1046,6 @@ proc expandTilde*(path: string): string {. assert expandTilde("~" / "appname.cfg") == getHomeDir() / "appname.cfg" assert expandTilde("~/foo/bar") == getHomeDir() / "foo/bar" assert expandTilde("/foo/bar") == "/foo/bar" - assert expandTilde("~") == getHomeDir() - from std/strutils import endsWith - assert not expandTilde("~").endsWith(DirSep) if len(path) == 0 or path[0] != '~': result = path diff --git a/tests/stdlib/tos.nim b/tests/stdlib/tos.nim index a4f030dde..912558461 100644 --- a/tests/stdlib/tos.nim +++ b/tests/stdlib/tos.nim @@ -594,12 +594,12 @@ block getTempDir: if existsEnv("TMPDIR"): let origTmpDir = getEnv("TMPDIR") putEnv("TMPDIR", "/mytmp") - doAssert getTempDir() == "/mytmp" + doAssert getTempDir() == "/mytmp/" delEnv("TMPDIR") - doAssert getTempDir() == "/tmp" + doAssert getTempDir() == "/tmp/" putEnv("TMPDIR", origTmpDir) else: - doAssert getTempDir() == "/tmp" + doAssert getTempDir() == "/tmp/" block: # getCacheDir doAssert getCacheDir().dirExists diff --git a/tests/stdlib/ttempfiles.nim b/tests/stdlib/ttempfiles.nim index d740e1850..297410686 100644 --- a/tests/stdlib/ttempfiles.nim +++ b/tests/stdlib/ttempfiles.nim @@ -40,7 +40,7 @@ block: # createTempDir doAssert dirExists(dir1) doAssert dir1.lastPathPart.contains(re"^D20210502T100442(\w+).tmp$") - doAssert dir1.parentDir == getTempDir() + doAssert dir1.parentDir == getTempDir().normalizePathEnd() block: let dir3 = createTempDir(prefix, "_mytmp", ".") From 3723140044b99fde3f47df4f2e6a3ed4abdaba89 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 18 Jul 2021 02:05:35 -0700 Subject: [PATCH 2346/3768] add NIM_STATIC_ASSERT(CHAR_BIT == 8, "") to fail-fast where assumption is violated (#18512) --- lib/nimbase.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/nimbase.h b/lib/nimbase.h index cbd35605b..e480c55fd 100644 --- a/lib/nimbase.h +++ b/lib/nimbase.h @@ -325,6 +325,9 @@ typedef unsigned char NIM_BOOL; // best effort #endif NIM_STATIC_ASSERT(sizeof(NIM_BOOL) == 1, ""); // check whether really needed +NIM_STATIC_ASSERT(CHAR_BIT == 8, ""); + // fail fast for (rare) environments where this doesn't hold, as some implicit + // assumptions would need revisiting (e.g. `uint8` or https://github.com/nim-lang/Nim/pull/18505) #define NIM_TRUE true #define NIM_FALSE false From adba5eb45e0ae0d370aea4d653a4f00a4c075695 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 18 Jul 2021 02:10:08 -0700 Subject: [PATCH 2347/3768] deprecate strutils.delete and add an overload with saner semantics consistent with sequtils.delete; follows #18487 (#18510) --- changelog.md | 2 +- lib/pure/strutils.nim | 36 +++++++++++++++++++++++++++++++----- tests/stdlib/tstrutils.nim | 26 +++++++++++++++++++++++++- 3 files changed, 57 insertions(+), 7 deletions(-) diff --git a/changelog.md b/changelog.md index 16f2192dc..6dddd8a01 100644 --- a/changelog.md +++ b/changelog.md @@ -346,7 +346,7 @@ - Added `dom.setInterval`, `dom.clearInterval` overloads. - Deprecated `sequtils.delete` and added an overload taking a `Slice` that raises a defect - if the slice is out of bounds. + if the slice is out of bounds, likewise with `strutils.delete`. ## Language changes diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 9e0837942..ac078df4e 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -1476,12 +1476,39 @@ func dedent*(s: string, count: Natural = indentation(s)): string {.rtl, doAssert x == "Hello\n There\n" unindent(s, count, " ") -func delete*(s: var string, first, last: int) {.rtl, extern: "nsuDelete".} = - ## Deletes in `s` (must be declared as `var`) the characters at positions - ## `first .. last` (both ends included). +func delete*(s: var string, slice: Slice[int]) = + ## Deletes the items `s[slice]`, raising `IndexDefect` if the slice contains + ## elements out of range. ## - ## This modifies `s` itself, it does not return a copy. + ## This operation moves all elements after `s[slice]` in linear time, and + ## is the string analog to `sequtils.delete`. runnableExamples: + var a = "abcde" + doAssertRaises(IndexDefect): a.delete(4..5) + assert a == "abcde" + a.delete(4..4) + assert a == "abcd" + a.delete(1..2) + assert a == "ad" + a.delete(1..<1) # empty slice + assert a == "ad" + when compileOption("boundChecks"): + if not (slice.a < s.len and slice.a >= 0 and slice.b < s.len): + raise newException(IndexDefect, $(slice: slice, len: s.len)) + if slice.b >= slice.a: + var i = slice.a + var j = slice.b + 1 + var newLen = s.len - j + i + # if j < s.len: moveMem(addr s[i], addr s[j], s.len - j) # pending benchmark + while i < newLen: + s[i] = s[j] + inc(i) + inc(j) + setLen(s, newLen) + +func delete*(s: var string, first, last: int) {.rtl, extern: "nsuDelete", deprecated: "use `delete(s, first..last)`".} = + ## Deletes in `s` the characters at positions `first .. last` (both ends included). + runnableExamples("--warning:deprecated:off"): var a = "abracadabra" a.delete(4, 5) @@ -1502,7 +1529,6 @@ func delete*(s: var string, first, last: int) {.rtl, extern: "nsuDelete".} = inc(j) setLen(s, newLen) - func startsWith*(s: string, prefix: char): bool {.inline.} = ## Returns true if `s` starts with character `prefix`. ## diff --git a/tests/stdlib/tstrutils.nim b/tests/stdlib/tstrutils.nim index 68ee5812b..234991bdb 100644 --- a/tests/stdlib/tstrutils.nim +++ b/tests/stdlib/tstrutils.nim @@ -198,7 +198,30 @@ template main() = s.removePrefix("") doAssert s == "\r\n\r\nhello" - block: # delete + block: # delete(slice) + var s = "0123456789ABCDEFGH" + delete(s, 4 .. 5) + doAssert s == "01236789ABCDEFGH" + delete(s, s.len-1 .. s.len-1) + doAssert s == "01236789ABCDEFG" + delete(s, 0..0) + doAssert s == "1236789ABCDEFG" + s = "" + doAssertRaises(IndexDefect): delete(s, 0..0) + doAssert s == "" + s = "abc" + doAssertRaises(IndexDefect): delete(s, -1 .. -2) + doAssertRaises(IndexDefect): delete(s, 2..3) + doAssertRaises(IndexDefect): delete(s, 3..2) + delete(s, 2..2) + doAssert s == "ab" + delete(s, 1..0) + doAssert s == "ab" + delete(s, 0..0) + doAssert s == "b" + + block: # delete(first, last) + {.push warning[deprecated]:off.} var s = "0123456789ABCDEFGH" delete(s, 4, 5) doAssert s == "01236789ABCDEFGH" @@ -206,6 +229,7 @@ template main() = doAssert s == "01236789ABCDEFG" delete(s, 0, 0) doAssert s == "1236789ABCDEFG" + {.pop.} block: # find doAssert "0123456789ABCDEFGH".find('A') == 10 From 99c4b69097638aca8377ea3746e52cc19c24ced8 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 18 Jul 2021 15:16:26 +0200 Subject: [PATCH 2348/3768] fixed system.delete (#18507) --- changelog.md | 13 ++++++++++++- lib/system.nim | 17 ++++++++++------- tests/stdlib/tsystem.nim | 8 ++++---- 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/changelog.md b/changelog.md index 6dddd8a01..097beac2c 100644 --- a/changelog.md +++ b/changelog.md @@ -4,7 +4,18 @@ ## Changes affecting backward compatibility -- Deprecated `std/mersenne` +- Deprecated `std/mersenne`. + +- `system.delete` had a most surprising behavior when the index passed to it was out of + bounds (it would delete the last entry then). Compile with `-d:nimStrictDelete` so + that an index error is produced instead. But be aware that your code might depend on + this quirky behavior so a review process is required on your part before you can + use `-d:nimStrictDelete`. To make this review easier, use the `-d:nimAuditDelete` + switch, it pretends that `system.delete` is deprecated so that it is easier to see + where it was used in your code. + + `-d:nimStrictDelete` will become the default in upcoming versions. + - `cuchar` is now deprecated as it aliased `char` where arguably it should have aliased `uint8`. Please use `char` or `uint8` instead. diff --git a/lib/system.nim b/lib/system.nim index 57d620e57..2790187f4 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2134,7 +2134,12 @@ const import system/dollars export dollars -proc delete*[T](x: var seq[T], i: Natural) {.noSideEffect.} = +when defined(nimAuditDelete): + {.pragma: auditDelete, deprecated: "review this call for out of bounds behavior".} +else: + {.pragma: auditDelete.} + +proc delete*[T](x: var seq[T], i: Natural) {.noSideEffect, auditDelete.} = ## Deletes the item at index `i` by moving all `x[i+1..^1]` items by one position. ## ## This is an `O(n)` operation. @@ -2147,12 +2152,10 @@ proc delete*[T](x: var seq[T], i: Natural) {.noSideEffect.} = s.delete(2) doAssert s == @[1, 2, 4, 5] - doAssertRaises(IndexDefect): - s.delete(4) - - if i > high(x): - # xxx this should call `raiseIndexError2(i, high(x))` after some refactoring - raise (ref IndexDefect)(msg: "index out of bounds: '" & $i & "' < '" & $x.len & "' failed") + when defined(nimStrictDelete): + if i > high(x): + # xxx this should call `raiseIndexError2(i, high(x))` after some refactoring + raise (ref IndexDefect)(msg: "index out of bounds: '" & $i & "' < '" & $x.len & "' failed") template defaultImpl = let xl = x.len diff --git a/tests/stdlib/tsystem.nim b/tests/stdlib/tsystem.nim index b48034043..00be16275 100644 --- a/tests/stdlib/tsystem.nim +++ b/tests/stdlib/tsystem.nim @@ -53,8 +53,8 @@ template main = var s = @["foo", "bar"] s.delete(1) doAssert s == @["foo"] - - block: + + when false: var s: seq[string] doAssertRaises(IndexDefect): s.delete(0) @@ -66,8 +66,8 @@ template main = var s = @["foo"] s.delete(0) doAssert s == @[] - - block: # bug #16544: deleting out of bounds index should raise + + when false: # bug #16544: deleting out of bounds index should raise var s = @["foo"] doAssertRaises(IndexDefect): s.delete(1) From 220b55c5d7c40aa93df7879ebc6c9f71147c3eda Mon Sep 17 00:00:00 2001 From: Antonis Geralis <43617260+planetis-m@users.noreply.github.com> Date: Sun, 18 Jul 2021 16:29:49 +0300 Subject: [PATCH 2349/3768] attempt to support short commit hashes (#18514) * attempt to support short commit hashes Not sure if that's the correct way and what happens when the short hash matches "head" * need to remove # * Output needs to be stripped of newlines --- tools/atlas/atlas.nim | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/tools/atlas/atlas.nim b/tools/atlas/atlas.nim index bc1cc6ae3..371a1c13f 100644 --- a/tools/atlas/atlas.nim +++ b/tools/atlas/atlas.nim @@ -123,8 +123,12 @@ proc versionToCommit(d: Dependency): string = return "" +proc shortToCommit(short: string): string = + let (cc, status) = osproc.execCmdEx("git rev-parse " & quoteShell(short)) + result = if status == 0: strutils.strip(cc) else: "" + proc checkoutGitCommit(c: var AtlasContext; p: PackageName; commit: string) = - let (outp, status) = osproc.execCmdEx("git checkout " & quoteShell(commit)) + let (_, status) = osproc.execCmdEx("git checkout " & quoteShell(commit)) if status != 0: error(c, p, "could not checkout commit", commit) @@ -169,17 +173,23 @@ proc toName(p: string): PackageName = proc needsCommitLookup(commit: string): bool {.inline} = '.' in commit or commit == InvalidCommit +proc isShortCommitHash(commit: string): bool {.inline.} = + commit.len >= 4 and commit.len < 40 + proc checkoutCommit(c: var AtlasContext; w: Dependency) = let dir = c.workspace / w.name.string withDir dir: - if w.commit.len == 0 or cmpIgnoreCase(w.commit, "#head") == 0: + if w.commit.len == 0 or cmpIgnoreCase(w.commit, "head") == 0: gitPull(c, w.name) else: let err = isCleanGit(dir) if err != "": warn c, w.name, err else: - let requiredCommit = if needsCommitLookup(w.commit): versionToCommit(w) else: w.commit + let requiredCommit = + if needsCommitLookup(w.commit): versionToCommit(w) + elif isShortCommitHash(w.commit): shortToCommit(w.commit) + else: w.commit let (cc, status) = osproc.execCmdEx("git log -n 1 --format=%H") let currentCommit = strutils.strip(cc) if requiredCommit == "" or status != 0: @@ -191,8 +201,9 @@ proc checkoutCommit(c: var AtlasContext; w: Dependency) = if currentCommit != requiredCommit: # checkout the later commit: # git merge-base --is-ancestor - let (mergeBase, status) = osproc.execCmdEx("git merge-base " & + let (cc, status) = osproc.execCmdEx("git merge-base " & currentCommit.quoteShell & " " & requiredCommit.quoteShell) + let mergeBase = strutils.strip(cc) if status == 0 and (mergeBase == currentCommit or mergeBase == requiredCommit): # conflict resolution: pick the later commit: if mergeBase == currentCommit: @@ -242,7 +253,7 @@ proc collectNewDeps(c: var AtlasContext; work: var seq[Dependency]; tokens.add InvalidCommit elif tokens.len == 2 and tokens[1].startsWith("#"): # Dependencies can also look like 'requires "sdl2#head" - var commit = tokens[1] + var commit = tokens[1][1 .. ^1] tokens[1] = "==" tokens.add commit From 488e9c2991cd6f4ae946dd719271ea284a051891 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 19 Jul 2021 03:42:45 -0700 Subject: [PATCH 2350/3768] nim doc now correctly renders deprecated pragmas for routines and types (#18515) --- compiler/ast.nim | 31 ++++++++++++++++++++ compiler/docgen.nim | 15 ++-------- compiler/renderer.nim | 4 +-- nimdoc/testproject/expected/testproject.html | 18 ++++++++++-- nimdoc/testproject/expected/testproject.idx | 1 + nimdoc/testproject/expected/theindex.html | 4 +++ nimdoc/testproject/testproject.nim | 3 ++ 7 files changed, 59 insertions(+), 17 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index f96e464af..f1f73dcee 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1168,6 +1168,37 @@ template `[]=`*(n: Indexable, i: int; x: Indexable) = n.sons[i] = x template `[]`*(n: Indexable, i: BackwardsIndex): Indexable = n[n.len - i.int] template `[]=`*(n: Indexable, i: BackwardsIndex; x: Indexable) = n[n.len - i.int] = x +proc getDeclPragma*(n: PNode): PNode = + ## return the `nkPragma` node for declaration `n`, or `nil` if no pragma was found. + ## Currently only supports routineDefs + {nkTypeDef}. + case n.kind + of routineDefs: + if n[pragmasPos].kind != nkEmpty: result = n[pragmasPos] + of nkTypeDef: + #[ + type F3*{.deprecated: "x3".} = int + + TypeSection + TypeDef + PragmaExpr + Postfix + Ident "*" + Ident "F3" + Pragma + ExprColonExpr + Ident "deprecated" + StrLit "x3" + Empty + Ident "int" + ]# + if n[0].kind == nkPragmaExpr: + result = n[0][1] + else: + # support as needed for `nkIdentDefs` etc. + result = nil + if result != nil: + assert result.kind == nkPragma, $(result.kind, n.kind) + when defined(useNodeIds): const nodeIdToDebug* = -1 # 2322968 var gNodeId: int diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 1ae00e22e..9c75c0a6f 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -764,14 +764,6 @@ proc complexName(k: TSymKind, n: PNode, baseName: string): string = result.add(defaultParamSeparator) result.add(params) -proc isCallable(n: PNode): bool = - ## Returns true if `n` contains a callable node. - case n.kind - of nkProcDef, nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, - nkConverterDef, nkFuncDef: result = true - else: - result = false - proc docstringSummary(rstText: string): string = ## Returns just the first line or a brief chunk of text from a rst string. ## @@ -862,9 +854,8 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags) = break plainName.add(literal) - var pragmaNode: PNode = nil - if n.isCallable and n[pragmasPos].kind != nkEmpty: - pragmaNode = findPragma(n[pragmasPos], wDeprecated) + var pragmaNode = getDeclPragma(n) + if pragmaNode != nil: pragmaNode = findPragma(pragmaNode, wDeprecated) inc(d.id) let @@ -920,7 +911,7 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags) = # because it doesn't include object fields or documentation comments. So we # use the plain one for callable elements, and the complex for the rest. var linkTitle = changeFileExt(extractFilename(d.filename), "") & ": " - if n.isCallable: linkTitle.add(xmltree.escape(plainName.strip)) + if n.kind in routineDefs: linkTitle.add(xmltree.escape(plainName.strip)) else: linkTitle.add(xmltree.escape(complexSymbol.strip)) setIndexTerm(d[], external, symbolOrId, name, linkTitle, diff --git a/compiler/renderer.nim b/compiler/renderer.nim index ac3f47948..3346e629c 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -593,8 +593,8 @@ proc isHideable(config: ConfigRef, n: PNode): bool = # xxx compare `ident` directly with `getIdent(cache, wRaises)`, but # this requires a `cache`. case n.kind - of nkExprColonExpr: result = n[0].kind == nkIdent and n[0].ident.s in ["raises", "tags", "extern"] - of nkIdent: result = n.ident.s in ["gcsafe"] + of nkExprColonExpr: result = n[0].kind == nkIdent and n[0].ident.s in ["raises", "tags", "extern", "deprecated"] + of nkIdent: result = n.ident.s in ["gcsafe", "deprecated"] else: result = false proc gcommaAux(g: var TSrcGen, n: PNode, ind: int, start: int = 0, diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html index 65d9f83f5..7e5a685d2 100644 --- a/nimdoc/testproject/expected/testproject.html +++ b/nimdoc/testproject/expected/testproject.html @@ -107,7 +107,9 @@ window.addEventListener('DOMContentLoaded', main);
  • Types
      -
    • FooBuzz
    • +
    • A
    • Types

      + +
      FooBuzz {....deprecated: "FooBuzz msg".} = int
      +
      +
      + Deprecated: FooBuzz msg +
      + + + +
      A {.inject.} = enum
         aA
      @@ -544,7 +556,7 @@ This should be visible. -
      proc baz[T](a, b: T): T {.deprecated.}
      +
      proc baz[T](a, b: T): T {....deprecated.}
      Deprecated @@ -554,7 +566,7 @@ This is deprecated without message.
      -
      proc buzz[T](a, b: T): T {.deprecated: "since v0.20".}
      +
      proc buzz[T](a, b: T): T {....deprecated: "since v0.20".}
      Deprecated: since v0.20 diff --git a/nimdoc/testproject/expected/testproject.idx b/nimdoc/testproject/expected/testproject.idx index a8cdc3be2..4d0e63c0c 100644 --- a/nimdoc/testproject/expected/testproject.idx +++ b/nimdoc/testproject/expected/testproject.idx @@ -6,6 +6,7 @@ C_D testproject.html#C_D testproject: C_D bar testproject.html#bar,T,T testproject: bar[T](a, b: T): T baz testproject.html#baz,T,T testproject: baz[T](a, b: T): T buzz testproject.html#buzz,T,T testproject: buzz[T](a, b: T): T +FooBuzz testproject.html#FooBuzz testproject: FooBuzz aVariable testproject.html#aVariable testproject: aVariable A testproject.html#A testproject: A B testproject.html#B testproject: B diff --git a/nimdoc/testproject/expected/theindex.html b/nimdoc/testproject/expected/theindex.html index 55f7f33c9..cfaf6856d 100644 --- a/nimdoc/testproject/expected/theindex.html +++ b/nimdoc/testproject/expected/theindex.html @@ -179,6 +179,10 @@ window.addEventListener('DOMContentLoaded', main);
    • testproject: foo(a, b: SomeType)
    +
    FooBuzz:
    fromUtils1:
    • testproject: fromUtils1(): int
    • diff --git a/nimdoc/testproject/testproject.nim b/nimdoc/testproject/testproject.nim index b88b41098..57960c69f 100644 --- a/nimdoc/testproject/testproject.nim +++ b/nimdoc/testproject/testproject.nim @@ -39,6 +39,9 @@ proc buzz*[T](a, b: T): T {.deprecated: "since v0.20".} = ## This is deprecated with a message. result = a + b +type + FooBuzz* {.deprecated: "FooBuzz msg".} = int + import std/macros var aVariable*: array[1, int] From bdfee32dc8e81dd6c727d711a7b1c1fad1728383 Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 19 Jul 2021 18:43:31 +0800 Subject: [PATCH 2351/3768] close #18489 (#18524) --- tests/stdlib/tstrtabs2.nim | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 tests/stdlib/tstrtabs2.nim diff --git a/tests/stdlib/tstrtabs2.nim b/tests/stdlib/tstrtabs2.nim new file mode 100644 index 000000000..cb534f198 --- /dev/null +++ b/tests/stdlib/tstrtabs2.nim @@ -0,0 +1,15 @@ +discard """ + targets: "c cpp js" +""" + +import std/strtabs + +macro m = + var t = {"name": "John"}.newStringTable + doAssert t["name"] == "John" + +block: + var t = {"name": "John"}.newStringTable + doAssert t["name"] == "John" + +m() From 1e7a10001a8f823dddabf10b59e0ef9893716787 Mon Sep 17 00:00:00 2001 From: ynfle <23086821+ynfle@users.noreply.github.com> Date: Mon, 19 Jul 2021 13:44:06 +0300 Subject: [PATCH 2352/3768] Docs(manual): add clarification for default params (#18522) * Docs(manual): add clarification for default params * Docs(manual): fix wording to refer to call time * Docs(manual): Clarify default parameter wording Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> --- doc/manual.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/manual.rst b/doc/manual.rst index 34781141e..f0bd85638 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -3543,7 +3543,8 @@ separation of types and subsequent identifiers more distinct. proc foo(a; b: int; c, d: bool): int A parameter may be declared with a default value which is used if the caller -does not provide a value for the argument. +does not provide a value for the argument. The value will be reevaluated +every time the function is called. .. code-block:: nim # b is optional with 47 as its default value From 73ce40aaf71196faa08072216bd826a94b5d40b1 Mon Sep 17 00:00:00 2001 From: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Date: Mon, 19 Jul 2021 12:45:13 +0200 Subject: [PATCH 2353/3768] Revert #17321 (#18521) Use better names in example --- lib/pure/collections/lists.nim | 270 ++++++++++++++++----------------- 1 file changed, 135 insertions(+), 135 deletions(-) diff --git a/lib/pure/collections/lists.nim b/lib/pure/collections/lists.nim index c83be7aa3..af53bb68b 100644 --- a/lib/pure/collections/lists.nim +++ b/lib/pure/collections/lists.nim @@ -19,15 +19,15 @@ ## ## ## Lists runnableExamples: - var l = initDoublyLinkedList[int]() + var list = initDoublyLinkedList[int]() let a = newDoublyLinkedNode[int](3) b = newDoublyLinkedNode[int](7) c = newDoublyLinkedNode[int](9) - l.add(a) - l.add(b) - l.prepend(c) + list.add(a) + list.add(b) + list.prepend(c) assert a.next == b assert a.prev == c @@ -38,15 +38,15 @@ runnableExamples: ## ## Rings runnableExamples: - var l = initSinglyLinkedRing[int]() + var ring = initSinglyLinkedRing[int]() let a = newSinglyLinkedNode[int](3) b = newSinglyLinkedNode[int](7) c = newSinglyLinkedNode[int](9) - l.add(a) - l.add(b) - l.prepend(c) + ring.add(a) + ring.add(b) + ring.prepend(c) assert c.next == a assert a.next == b @@ -189,21 +189,21 @@ func toDoublyLinkedList*[T](elems: openArray[T]): DoublyLinkedList[T] {.since: ( result.add(elem) template itemsListImpl() {.dirty.} = - var it = list.head + var it = L.head while it != nil: yield it.value it = it.next template itemsRingImpl() {.dirty.} = - var it = ring.head + var it = L.head if it != nil: while true: yield it.value it = it.next - if it == ring.head: break + if it == L.head: break -iterator items*[T](list: SomeLinkedList[T]): T = - ## Yields every value of `list`. +iterator items*[T](L: SomeLinkedList[T]): T = + ## Yields every value of `L`. ## ## **See also:** ## * `mitems iterator <#mitems.i,SomeLinkedList[T]>`_ @@ -218,8 +218,8 @@ iterator items*[T](list: SomeLinkedList[T]): T = itemsListImpl() -iterator items*[T](ring: SomeLinkedRing[T]): T = - ## Yields every value of `ring`. +iterator items*[T](L: SomeLinkedRing[T]): T = + ## Yields every value of `L`. ## ## **See also:** ## * `mitems iterator <#mitems.i,SomeLinkedRing[T]>`_ @@ -234,8 +234,8 @@ iterator items*[T](ring: SomeLinkedRing[T]): T = itemsRingImpl() -iterator mitems*[T](list: var SomeLinkedList[T]): var T = - ## Yields every value of `list` so that you can modify it. +iterator mitems*[T](L: var SomeLinkedList[T]): var T = + ## Yields every value of `L` so that you can modify it. ## ## **See also:** ## * `items iterator <#items.i,SomeLinkedList[T]>`_ @@ -251,8 +251,8 @@ iterator mitems*[T](list: var SomeLinkedList[T]): var T = itemsListImpl() -iterator mitems*[T](ring: var SomeLinkedRing[T]): var T = - ## Yields every value of `ring` so that you can modify it. +iterator mitems*[T](L: var SomeLinkedRing[T]): var T = + ## Yields every value of `L` so that you can modify it. ## ## **See also:** ## * `items iterator <#items.i,SomeLinkedRing[T]>`_ @@ -268,7 +268,7 @@ iterator mitems*[T](ring: var SomeLinkedRing[T]): var T = itemsRingImpl() -iterator nodes*[T](list: SomeLinkedList[T]): SomeLinkedNode[T] = +iterator nodes*[T](L: SomeLinkedList[T]): SomeLinkedNode[T] = ## Iterates over every node of `x`. Removing the current node from the ## list during traversal is supported. ## @@ -287,13 +287,13 @@ iterator nodes*[T](list: SomeLinkedList[T]): SomeLinkedNode[T] = x.value = 5 * x.value - 1 assert $a == "[49, 99, 199, 249]" - var it = list.head + var it = L.head while it != nil: let nxt = it.next yield it it = nxt -iterator nodes*[T](ring: SomeLinkedRing[T]): SomeLinkedNode[T] = +iterator nodes*[T](L: SomeLinkedRing[T]): SomeLinkedNode[T] = ## Iterates over every node of `x`. Removing the current node from the ## list during traversal is supported. ## @@ -312,27 +312,27 @@ iterator nodes*[T](ring: SomeLinkedRing[T]): SomeLinkedNode[T] = x.value = 5 * x.value - 1 assert $a == "[49, 99, 199, 249]" - var it = ring.head + var it = L.head if it != nil: while true: let nxt = it.next yield it it = nxt - if it == ring.head: break + if it == L.head: break -proc `$`*[T](l: SomeLinkedCollection[T]): string = +proc `$`*[T](L: SomeLinkedCollection[T]): string = ## Turns a list into its string representation for logging and printing. runnableExamples: let a = [1, 2, 3, 4].toSinglyLinkedList assert $a == "[1, 2, 3, 4]" result = "[" - for x in nodes(l): + for x in nodes(L): if result.len > 1: result.add(", ") result.addQuoted(x.value) result.add("]") -proc find*[T](l: SomeLinkedCollection[T], value: T): SomeLinkedNode[T] = +proc find*[T](L: SomeLinkedCollection[T], value: T): SomeLinkedNode[T] = ## Searches in the list for a value. Returns `nil` if the value does not ## exist. ## @@ -343,10 +343,10 @@ proc find*[T](l: SomeLinkedCollection[T], value: T): SomeLinkedNode[T] = assert a.find(9).value == 9 assert a.find(1) == nil - for x in nodes(l): + for x in nodes(L): if x.value == value: return x -proc contains*[T](l: SomeLinkedCollection[T], value: T): bool {.inline.} = +proc contains*[T](L: SomeLinkedCollection[T], value: T): bool {.inline.} = ## Searches in the list for a value. Returns `false` if the value does not ## exist, `true` otherwise. This allows the usage of the `in` and `notin` ## operators. @@ -360,7 +360,7 @@ proc contains*[T](l: SomeLinkedCollection[T], value: T): bool {.inline.} = assert(not a.contains(1)) assert 2 notin a - result = find(l, value) != nil + result = find(L, value) != nil proc prepend*[T: SomeLinkedList](a: var T, b: T) {.since: (1, 5, 1).} = ## Prepends a shallow copy of `b` to the beginning of `a`. @@ -411,8 +411,8 @@ proc prependMoved*[T: SomeLinkedList](a, b: var T) {.since: (1, 5, 1).} = (b, a) = (a, b) else: swap a, b -proc add*[T](list: var SinglyLinkedList[T], n: SinglyLinkedNode[T]) {.inline.} = - ## Appends (adds to the end) a node `n` to `list`. Efficiency: O(1). +proc add*[T](L: var SinglyLinkedList[T], n: SinglyLinkedNode[T]) {.inline.} = + ## Appends (adds to the end) a node `n` to `L`. Efficiency: O(1). ## ## **See also:** ## * `add proc <#add,SinglyLinkedList[T],T>`_ for appending a value @@ -426,14 +426,14 @@ proc add*[T](list: var SinglyLinkedList[T], n: SinglyLinkedNode[T]) {.inline.} = assert a.contains(9) n.next = nil - if list.tail != nil: - assert(list.tail.next == nil) - list.tail.next = n - list.tail = n - if list.head == nil: list.head = n + if L.tail != nil: + assert(L.tail.next == nil) + L.tail.next = n + L.tail = n + if L.head == nil: L.head = n -proc add*[T](list: var SinglyLinkedList[T], value: T) {.inline.} = - ## Appends (adds to the end) a value to `list`. Efficiency: O(1). +proc add*[T](L: var SinglyLinkedList[T], value: T) {.inline.} = + ## Appends (adds to the end) a value to `L`. Efficiency: O(1). ## ## **See also:** ## * `add proc <#add,SinglyLinkedList[T],T>`_ for appending a value @@ -446,11 +446,11 @@ proc add*[T](list: var SinglyLinkedList[T], value: T) {.inline.} = a.add(8) assert a.contains(9) - add(list, newSinglyLinkedNode(value)) + add(L, newSinglyLinkedNode(value)) -proc prepend*[T](list: var SinglyLinkedList[T], +proc prepend*[T](L: var SinglyLinkedList[T], n: SinglyLinkedNode[T]) {.inline.} = - ## Prepends (adds to the beginning) a node to `list`. Efficiency: O(1). + ## Prepends (adds to the beginning) a node to `L`. Efficiency: O(1). ## ## **See also:** ## * `add proc <#add,SinglyLinkedList[T],SinglyLinkedNode[T]>`_ @@ -463,12 +463,12 @@ proc prepend*[T](list: var SinglyLinkedList[T], a.prepend(n) assert a.contains(9) - n.next = list.head - list.head = n - if list.tail == nil: list.tail = n + n.next = L.head + L.head = n + if L.tail == nil: L.tail = n -proc prepend*[T](list: var SinglyLinkedList[T], value: T) {.inline.} = - ## Prepends (adds to the beginning) a node to `list`. Efficiency: O(1). +proc prepend*[T](L: var SinglyLinkedList[T], value: T) {.inline.} = + ## Prepends (adds to the beginning) a node to `L`. Efficiency: O(1). ## ## **See also:** ## * `add proc <#add,SinglyLinkedList[T],SinglyLinkedNode[T]>`_ @@ -482,7 +482,7 @@ proc prepend*[T](list: var SinglyLinkedList[T], value: T) {.inline.} = a.prepend(8) assert a.contains(9) - prepend(list, newSinglyLinkedNode(value)) + prepend(L, newSinglyLinkedNode(value)) func copy*[T](a: SinglyLinkedList[T]): SinglyLinkedList[T] {.since: (1, 5, 1).} = ## Creates a shallow copy of `a`. @@ -540,8 +540,8 @@ proc addMoved*[T](a, b: var SinglyLinkedList[T]) {.since: (1, 5, 1).} = b.head = nil b.tail = nil -proc add*[T](list: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) = - ## Appends (adds to the end) a node `n` to `list`. Efficiency: O(1). +proc add*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) = + ## Appends (adds to the end) a node `n` to `L`. Efficiency: O(1). ## ## **See also:** ## * `add proc <#add,DoublyLinkedList[T],T>`_ for appending a value @@ -557,15 +557,15 @@ proc add*[T](list: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) = assert a.contains(9) n.next = nil - n.prev = list.tail - if list.tail != nil: - assert(list.tail.next == nil) - list.tail.next = n - list.tail = n - if list.head == nil: list.head = n + n.prev = L.tail + if L.tail != nil: + assert(L.tail.next == nil) + L.tail.next = n + L.tail = n + if L.head == nil: L.head = n -proc add*[T](list: var DoublyLinkedList[T], value: T) = - ## Appends (adds to the end) a value to `list`. Efficiency: O(1). +proc add*[T](L: var DoublyLinkedList[T], value: T) = + ## Appends (adds to the end) a value to `L`. Efficiency: O(1). ## ## **See also:** ## * `add proc <#add,DoublyLinkedList[T],DoublyLinkedNode[T]>`_ @@ -581,10 +581,10 @@ proc add*[T](list: var DoublyLinkedList[T], value: T) = a.add(8) assert a.contains(9) - add(list, newDoublyLinkedNode(value)) + add(L, newDoublyLinkedNode(value)) -proc prepend*[T](list: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) = - ## Prepends (adds to the beginning) a node `n` to `list`. Efficiency: O(1). +proc prepend*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) = + ## Prepends (adds to the beginning) a node `n` to `L`. Efficiency: O(1). ## ## **See also:** ## * `add proc <#add,DoublyLinkedList[T],DoublyLinkedNode[T]>`_ @@ -600,15 +600,15 @@ proc prepend*[T](list: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) = assert a.contains(9) n.prev = nil - n.next = list.head - if list.head != nil: - assert(list.head.prev == nil) - list.head.prev = n - list.head = n - if list.tail == nil: list.tail = n + n.next = L.head + if L.head != nil: + assert(L.head.prev == nil) + L.head.prev = n + L.head = n + if L.tail == nil: L.tail = n -proc prepend*[T](list: var DoublyLinkedList[T], value: T) = - ## Prepends (adds to the beginning) a value to `list`. Efficiency: O(1). +proc prepend*[T](L: var DoublyLinkedList[T], value: T) = + ## Prepends (adds to the beginning) a value to `L`. Efficiency: O(1). ## ## **See also:** ## * `add proc <#add,DoublyLinkedList[T],DoublyLinkedNode[T]>`_ @@ -624,7 +624,7 @@ proc prepend*[T](list: var DoublyLinkedList[T], value: T) = a.prepend(8) assert a.contains(9) - prepend(list, newDoublyLinkedNode(value)) + prepend(L, newDoublyLinkedNode(value)) func copy*[T](a: DoublyLinkedList[T]): DoublyLinkedList[T] {.since: (1, 5, 1).} = ## Creates a shallow copy of `a`. @@ -705,9 +705,9 @@ proc add*[T: SomeLinkedList](a: var T, b: T) {.since: (1, 5, 1).} = var tmp = b.copy a.addMoved(tmp) -proc remove*[T](list: var SinglyLinkedList[T], n: SinglyLinkedNode[T]): bool {.discardable.} = - ## Removes a node `n` from `list`. - ## Returns `true` if `n` was found in `list`. +proc remove*[T](L: var SinglyLinkedList[T], n: SinglyLinkedNode[T]): bool {.discardable.} = + ## Removes a node `n` from `L`. + ## Returns `true` if `n` was found in `L`. ## Efficiency: O(n); the list is traversed until `n` is found. ## Attempting to remove an element not contained in the list is a no-op. ## When the list is cyclic, the cycle is preserved after removal. @@ -728,12 +728,12 @@ proc remove*[T](list: var SinglyLinkedList[T], n: SinglyLinkedNode[T]): bool {.d ai assert s == [2, 2, 2, 2] - if n == list.head: - list.head = n.next - if list.tail.next == n: - list.tail.next = list.head # restore cycle + if n == L.head: + L.head = n.next + if L.tail.next == n: + L.tail.next = L.head # restore cycle else: - var prev = list.head + var prev = L.head while prev.next != n and prev.next != nil: prev = prev.next if prev.next == nil: @@ -741,9 +741,9 @@ proc remove*[T](list: var SinglyLinkedList[T], n: SinglyLinkedNode[T]): bool {.d prev.next = n.next true -proc remove*[T](list: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) = - ## Removes a node `n` from `list`. Efficiency: O(1). - ## This function assumes, for the sake of efficiency, that `n` is contained in `list`, +proc remove*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) = + ## Removes a node `n` from `L`. Efficiency: O(1). + ## This function assumes, for the sake of efficiency, that `n` is contained in `L`, ## otherwise the effects are undefined. ## When the list is cyclic, the cycle is preserved after removal. runnableExamples: @@ -763,15 +763,15 @@ proc remove*[T](list: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) = ai assert s == [2, 2, 2, 2] - if n == list.tail: list.tail = n.prev - if n == list.head: list.head = n.next + if n == L.tail: L.tail = n.prev + if n == L.head: L.head = n.next if n.next != nil: n.next.prev = n.prev if n.prev != nil: n.prev.next = n.next -proc add*[T](ring: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) = - ## Appends (adds to the end) a node `n` to `ring`. Efficiency: O(1). +proc add*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) = + ## Appends (adds to the end) a node `n` to `L`. Efficiency: O(1). ## ## **See also:** ## * `add proc <#add,SinglyLinkedRing[T],T>`_ for appending a value @@ -784,17 +784,17 @@ proc add*[T](ring: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) = a.add(n) assert a.contains(9) - if ring.head != nil: - n.next = ring.head - assert(ring.tail != nil) - ring.tail.next = n + if L.head != nil: + n.next = L.head + assert(L.tail != nil) + L.tail.next = n else: n.next = n - ring.head = n - ring.tail = n + L.head = n + L.tail = n -proc add*[T](ring: var SinglyLinkedRing[T], value: T) = - ## Appends (adds to the end) a value to `ring`. Efficiency: O(1). +proc add*[T](L: var SinglyLinkedRing[T], value: T) = + ## Appends (adds to the end) a value to `L`. Efficiency: O(1). ## ## **See also:** ## * `add proc <#add,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_ @@ -808,10 +808,10 @@ proc add*[T](ring: var SinglyLinkedRing[T], value: T) = a.add(8) assert a.contains(9) - add(ring, newSinglyLinkedNode(value)) + add(L, newSinglyLinkedNode(value)) -proc prepend*[T](ring: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) = - ## Prepends (adds to the beginning) a node `n` to `ring`. Efficiency: O(1). +proc prepend*[T](L: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) = + ## Prepends (adds to the beginning) a node `n` to `L`. Efficiency: O(1). ## ## **See also:** ## * `add proc <#add,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_ @@ -824,17 +824,17 @@ proc prepend*[T](ring: var SinglyLinkedRing[T], n: SinglyLinkedNode[T]) = a.prepend(n) assert a.contains(9) - if ring.head != nil: - n.next = ring.head - assert(ring.tail != nil) - ring.tail.next = n + if L.head != nil: + n.next = L.head + assert(L.tail != nil) + L.tail.next = n else: n.next = n - ring.tail = n - ring.head = n + L.tail = n + L.head = n -proc prepend*[T](ring: var SinglyLinkedRing[T], value: T) = - ## Prepends (adds to the beginning) a value to `ring`. Efficiency: O(1). +proc prepend*[T](L: var SinglyLinkedRing[T], value: T) = + ## Prepends (adds to the beginning) a value to `L`. Efficiency: O(1). ## ## **See also:** ## * `add proc <#add,SinglyLinkedRing[T],SinglyLinkedNode[T]>`_ @@ -848,12 +848,12 @@ proc prepend*[T](ring: var SinglyLinkedRing[T], value: T) = a.prepend(8) assert a.contains(9) - prepend(ring, newSinglyLinkedNode(value)) + prepend(L, newSinglyLinkedNode(value)) -proc add*[T](ring: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) = - ## Appends (adds to the end) a node `n` to `ring`. Efficiency: O(1). +proc add*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) = + ## Appends (adds to the end) a node `n` to `L`. Efficiency: O(1). ## ## **See also:** ## * `add proc <#add,DoublyLinkedRing[T],T>`_ for appending a value @@ -868,18 +868,18 @@ proc add*[T](ring: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) = a.add(n) assert a.contains(9) - if ring.head != nil: - n.next = ring.head - n.prev = ring.head.prev - ring.head.prev.next = n - ring.head.prev = n + if L.head != nil: + n.next = L.head + n.prev = L.head.prev + L.head.prev.next = n + L.head.prev = n else: n.prev = n n.next = n - ring.head = n + L.head = n -proc add*[T](ring: var DoublyLinkedRing[T], value: T) = - ## Appends (adds to the end) a value to `ring`. Efficiency: O(1). +proc add*[T](L: var DoublyLinkedRing[T], value: T) = + ## Appends (adds to the end) a value to `L`. Efficiency: O(1). ## ## **See also:** ## * `add proc <#add,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_ @@ -895,10 +895,10 @@ proc add*[T](ring: var DoublyLinkedRing[T], value: T) = a.add(8) assert a.contains(9) - add(ring, newDoublyLinkedNode(value)) + add(L, newDoublyLinkedNode(value)) -proc prepend*[T](ring: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) = - ## Prepends (adds to the beginning) a node `n` to `ring`. Efficiency: O(1). +proc prepend*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) = + ## Prepends (adds to the beginning) a node `n` to `L`. Efficiency: O(1). ## ## **See also:** ## * `add proc <#add,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_ @@ -913,18 +913,18 @@ proc prepend*[T](ring: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) = a.prepend(n) assert a.contains(9) - if ring.head != nil: - n.next = ring.head - n.prev = ring.head.prev - ring.head.prev.next = n - ring.head.prev = n + if L.head != nil: + n.next = L.head + n.prev = L.head.prev + L.head.prev.next = n + L.head.prev = n else: n.prev = n n.next = n - ring.head = n + L.head = n -proc prepend*[T](ring: var DoublyLinkedRing[T], value: T) = - ## Prepends (adds to the beginning) a value to `ring`. Efficiency: O(1). +proc prepend*[T](L: var DoublyLinkedRing[T], value: T) = + ## Prepends (adds to the beginning) a value to `L`. Efficiency: O(1). ## ## **See also:** ## * `add proc <#add,DoublyLinkedRing[T],DoublyLinkedNode[T]>`_ @@ -940,11 +940,11 @@ proc prepend*[T](ring: var DoublyLinkedRing[T], value: T) = a.prepend(8) assert a.contains(9) - prepend(ring, newDoublyLinkedNode(value)) + prepend(L, newDoublyLinkedNode(value)) -proc remove*[T](ring: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) = - ## Removes `n` from `ring`. Efficiency: O(1). - ## This function assumes, for the sake of efficiency, that `n` is contained in `ring`, +proc remove*[T](L: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) = + ## Removes `n` from `L`. Efficiency: O(1). + ## This function assumes, for the sake of efficiency, that `n` is contained in `L`, ## otherwise the effects are undefined. runnableExamples: var a = initDoublyLinkedRing[int]() @@ -956,13 +956,13 @@ proc remove*[T](ring: var DoublyLinkedRing[T], n: DoublyLinkedNode[T]) = n.next.prev = n.prev n.prev.next = n.next - if n == ring.head: - let p = ring.head.prev - if p == ring.head: + if n == L.head: + let p = L.head.prev + if p == L.head: # only one element left: - ring.head = nil + L.head = nil else: - ring.head = p + L.head = p proc append*[T](a: var (SinglyLinkedList[T] | SinglyLinkedRing[T]), b: SinglyLinkedList[T] | SinglyLinkedNode[T] | T) = From db6e7fffba021c223fc67e1c0853e5839cd256d2 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 19 Jul 2021 20:07:47 -0700 Subject: [PATCH 2354/3768] minor clenup in vmops (#18529) --- compiler/vmops.nim | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/compiler/vmops.nim b/compiler/vmops.nim index 67a196b8f..283306ecc 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -150,6 +150,9 @@ when defined(nimHasInvariant): of cincludes: copySeq(conf.cIncludes) of clibs: copySeq(conf.cLibs) +proc stackTrace2(c: PCtx, msg: string, n: PNode) = + stackTrace(c, PStackFrame(prc: c.prc.sym, comesFrom: 0, next: nil), c.exceptionInstr, msg, n.info) + proc registerAdditionalOps*(c: PCtx) = proc gorgeExWrapper(a: VmArgs) = let ret = opGorge(getString(a, 0), getString(a, 1), getString(a, 2), @@ -238,15 +241,13 @@ proc registerAdditionalOps*(c: PCtx) = registerCallback c, "stdlib.macros.symBodyHash", proc (a: VmArgs) = let n = getNode(a, 0) if n.kind != nkSym: - stackTrace(c, PStackFrame(prc: c.prc.sym, comesFrom: 0, next: nil), c.exceptionInstr, - "symBodyHash() requires a symbol. '" & $n & "' is of kind '" & $n.kind & "'", n.info) + stackTrace2(c, "symBodyHash() requires a symbol. '$#' is of kind '$#'" % [$n, $n.kind], n) setResult(a, $symBodyDigest(c.graph, n.sym)) registerCallback c, "stdlib.macros.isExported", proc(a: VmArgs) = let n = getNode(a, 0) if n.kind != nkSym: - stackTrace(c, PStackFrame(prc: c.prc.sym, comesFrom: 0, next: nil), c.exceptionInstr, - "isExported() requires a symbol. '" & $n & "' is of kind '" & $n.kind & "'", n.info) + stackTrace2(c, "isExported() requires a symbol. '$#' is of kind '$#'" % [$n, $n.kind], n) setResult(a, sfExported in n.sym.flags) registerCallback c, "stdlib.vmutils.vmTrace", proc (a: VmArgs) = From 44c5afe448ecd9a7009982c16ec85fc8cabe3124 Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 20 Jul 2021 11:14:04 +0800 Subject: [PATCH 2355/3768] [std/os]document `checkDir` and use `runnableExamples` (#18517) Co-authored-by: Timothee Cour --- lib/pure/os.nim | 36 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index dadc380e6..6289c64f0 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -10,27 +10,14 @@ ## This module contains basic operating system facilities like ## retrieving environment variables, reading command line arguments, ## working with directories, running shell commands, etc. -## -## .. code-block:: -## import std/os -## -## let myFile = "/path/to/my/file.nim" -## -## let pathSplit = splitPath(myFile) -## assert pathSplit.head == "/path/to/my" -## assert pathSplit.tail == "file.nim" -## -## assert parentDir(myFile) == "/path/to/my" -## -## let fileSplit = splitFile(myFile) -## assert fileSplit.dir == "/path/to/my" -## assert fileSplit.name == "file" -## assert fileSplit.ext == ".nim" -## -## assert myFile.changeFileExt("c") == "/path/to/my/file.c" -## -## +runnableExamples("-r:off"): + let myFile = "/path/to/my/file.nim" + assert splitPath(myFile) == (head: "/path/to/my", tail: "file.nim") + assert parentDir(myFile) == "/path/to/my" + assert splitFile(myFile) == (dir: "/path/to/my", name: "file", ext: ".nim") + assert myFile.changeFileExt("c") == "/path/to/my/file.c" + ## **See also:** ## * `osproc module `_ for process communication beyond ## `execShellCmd proc <#execShellCmd,string>`_ @@ -2286,6 +2273,10 @@ iterator walkDir*(dir: string; relative = false, checkDir = false): ## ## Walking is not recursive. If ``relative`` is true (default: false) ## the resulting path is shortened to be relative to ``dir``. + ## + ## If `checkDir` is true, `OSError` is raised when `dir` + ## doesn't exist. + ## ## Example: This directory structure:: ## dirA / dirB / fileB1.txt ## / dirC @@ -2385,6 +2376,9 @@ iterator walkDirRec*(dir: string, ## If ``relative`` is true (default: false) the resulting path is ## shortened to be relative to ``dir``, otherwise the full path is returned. ## + ## If `checkDir` is true, `OSError` is raised when `dir` + ## doesn't exist. + ## ## .. warning:: Modifying the directory structure while the iterator ## is traversing may result in undefined behavior! ## @@ -2448,7 +2442,7 @@ proc removeDir*(dir: string, checkDir = false) {.rtl, extern: "nos$1", tags: [ ## in `dir` (recursively). ## ## If this fails, `OSError` is raised. This does not fail if the directory never - ## existed in the first place, unless `checkDir` = true + ## existed in the first place, unless `checkDir` = true. ## ## See also: ## * `tryRemoveFile proc <#tryRemoveFile,string>`_ From 8c7ee96457769a8c8aa1d4bab306b53acf306c8e Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Tue, 20 Jul 2021 09:32:22 +0300 Subject: [PATCH 2356/3768] rst: add missing line/column info for some warnings (#18383) * rst: add missing line/column info for some warnings * add workaround * use TLineInfo/FileIndex for storing file names * fix blank lines in include file (rm harmful strip) * don't use ref TLineInfo * return `hasToc` as output parameter for uniformity * Update compiler/docgen.nim Co-authored-by: Timothee Cour * Update compiler/docgen.nim Co-authored-by: Timothee Cour * Update lib/packages/docutils/rst.nim Co-authored-by: Timothee Cour * address review - stylistic things * Update compiler/docgen.nim Co-authored-by: Timothee Cour * unify RST warnings/errors names * doAssert + minor name change * fix a bug caught by doAssert * apply strbasics.strip to final HTML/Latex * rm redundant filename * fix test after rebase * delete `order` from rnFootnoteRef, also display errors/warnings properly when footnote references are from different files * Update compiler/lineinfos.nim Co-authored-by: Timothee Cour * Update lib/packages/docutils/rstast.nim Co-authored-by: Timothee Cour * Update lib/packages/docutils/rstast.nim Co-authored-by: Timothee Cour * Update lib/packages/docutils/rstast.nim Co-authored-by: Timothee Cour * revert because of error: Error: cannot prove that it's safe to initialize 'info' with the runtime value for the discriminator 'kind' * Update lib/packages/docutils/rstgen.nim Co-authored-by: Timothee Cour * apply suggestion * Update lib/packages/docutils/rst.nim Co-authored-by: Timothee Cour * add Table for string->file name mapping * do not import compiler/lineinfos * fix ambiguous calls Co-authored-by: Timothee Cour Co-authored-by: narimiran --- compiler/docgen.nim | 54 ++++----- compiler/lineinfos.nim | 49 +++++---- compiler/main.nim | 11 +- lib/packages/docutils/rst.nim | 181 ++++++++++++++++++++----------- lib/packages/docutils/rstast.nim | 20 +++- lib/packages/docutils/rstgen.nim | 40 +++---- tests/stdlib/trst.nim | 58 +++++++++- tests/stdlib/trstgen.nim | 122 ++++++++++++--------- 8 files changed, 342 insertions(+), 193 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 9c75c0a6f..f9e72850a 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -12,11 +12,12 @@ import ast, strutils, strtabs, options, msgs, os, idents, - wordrecg, syntaxes, renderer, lexer, packages/docutils/rstast, + wordrecg, syntaxes, renderer, lexer, packages/docutils/rst, packages/docutils/rstgen, json, xmltree, trees, types, typesrenderer, astalgo, lineinfos, intsets, pathutils, tables, nimpaths, renderverbatim, osproc +import packages/docutils/rstast except FileIndex, TLineInfo from uri import encodeUrl from std/private/globs import nativeToUnixPath @@ -159,17 +160,18 @@ template declareClosures = case msgKind of meCannotOpenFile: k = errCannotOpenFile of meExpected: k = errXExpected - of meGridTableNotImplemented: k = errGridTableNotImplemented - of meMarkdownIllformedTable: k = errMarkdownIllformedTable - of meNewSectionExpected: k = errNewSectionExpected - of meGeneralParseError: k = errGeneralParseError - of meInvalidDirective: k = errInvalidDirectiveX - of meInvalidRstField: k = errInvalidRstField - of meFootnoteMismatch: k = errFootnoteMismatch - of mwRedefinitionOfLabel: k = warnRedefinitionOfLabel - of mwUnknownSubstitution: k = warnUnknownSubstitutionX - of mwUnsupportedLanguage: k = warnLanguageXNotSupported - of mwUnsupportedField: k = warnFieldXNotSupported + of meGridTableNotImplemented: k = errRstGridTableNotImplemented + of meMarkdownIllformedTable: k = errRstMarkdownIllformedTable + of meNewSectionExpected: k = errRstNewSectionExpected + of meGeneralParseError: k = errRstGeneralParseError + of meInvalidDirective: k = errRstInvalidDirectiveX + of meInvalidField: k = errRstInvalidField + of meFootnoteMismatch: k = errRstFootnoteMismatch + of mwRedefinitionOfLabel: k = warnRstRedefinitionOfLabel + of mwUnknownSubstitution: k = warnRstUnknownSubstitutionX + of mwBrokenLink: k = warnRstBrokenLink + of mwUnsupportedLanguage: k = warnRstLanguageXNotSupported + of mwUnsupportedField: k = warnRstFieldXNotSupported of mwRstStyle: k = warnRstStyle {.gcsafe.}: globalError(conf, newLineInfo(conf, AbsoluteFile filename, line, col), k, arg) @@ -182,11 +184,9 @@ template declareClosures = proc parseRst(text, filename: string, line, column: int, - rstOptions: RstParseOptions; conf: ConfigRef, sharedState: PRstSharedState): PRstNode = declareClosures() - result = rstParsePass1(text, filename, line, column, rstOptions, - sharedState) + result = rstParsePass1(text, line, column, sharedState) proc getOutFile2(conf: ConfigRef; filename: RelativeFile, ext: string, guessTarget: bool): AbsoluteFile = @@ -202,20 +202,22 @@ proc getOutFile2(conf: ConfigRef; filename: RelativeFile, proc isLatexCmd(conf: ConfigRef): bool = conf.cmd in {cmdRst2tex, cmdDoc2tex} proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef, - outExt: string = HtmlExt, module: PSym = nil): PDoc = + outExt: string = HtmlExt, module: PSym = nil, + isPureRst = false): PDoc = declareClosures() new(result) result.module = module result.conf = conf result.cache = cache result.outDir = conf.outDir.string - const options = {roSupportRawDirective, roSupportMarkdown, - roPreferMarkdown, roNimFile} + result.isPureRst = isPureRst + var options= {roSupportRawDirective, roSupportMarkdown, roPreferMarkdown} + if not isPureRst: options.incl roNimFile result.sharedState = newRstSharedState( options, filename.string, docgenFindFile, compilerMsgHandler) initRstGenerator(result[], (if conf.isLatexCmd: outLatex else: outHtml), - conf.configVars, filename.string, options, + conf.configVars, filename.string, docgenFindFile, compilerMsgHandler) if conf.configVars.hasKey("doc.googleAnalytics"): @@ -299,8 +301,7 @@ proc genComment(d: PDoc, n: PNode): PRstNode = result = parseRst(n.comment, toFullPath(d.conf, n.info), toLinenumber(n.info), toColumn(n.info) + DocColOffset, - d.options, d.conf, - d.sharedState) + d.conf, d.sharedState) proc genRecCommentAux(d: PDoc, n: PNode): PRstNode = if n == nil: return nil @@ -1123,6 +1124,9 @@ proc generateDoc*(d: PDoc, n, orig: PNode, docFlags: DocFlags = kDefault) = proc finishGenerateDoc*(d: var PDoc) = ## Perform 2nd RST pass for resolution of links/footnotes/headings... + # copy file map `filenames` to ``rstgen.nim`` for its warnings + d.filenames = d.sharedState.filenames + # Main title/subtitle are allowed only in the first RST fragment of document var firstRst = PRstNode(nil) for fragment in d.modDescPre: @@ -1417,14 +1421,10 @@ proc commandDoc*(cache: IdentCache, conf: ConfigRef) = proc commandRstAux(cache: IdentCache, conf: ConfigRef; filename: AbsoluteFile, outExt: string) = var filen = addFileExt(filename, "txt") - var d = newDocumentor(filen, cache, conf, outExt) - - d.isPureRst = true + var d = newDocumentor(filen, cache, conf, outExt, isPureRst = true) let rst = parseRst(readFile(filen.string), filen.string, line=LineRstInit, column=ColRstInit, - {roSupportRawDirective, roSupportMarkdown, - roPreferMarkdown}, conf, - d.sharedState) + conf, d.sharedState) d.modDescPre = @[ItemFragment(isRst: true, rst: rst)] finishGenerateDoc(d) writeOutput(d) diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index 801c20d25..8ab52a452 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -32,13 +32,13 @@ type # non-fatal errors errIllFormedAstX, errCannotOpenFile, errXExpected, - errGridTableNotImplemented, - errMarkdownIllformedTable, - errGeneralParseError, - errNewSectionExpected, - errInvalidDirectiveX, - errInvalidRstField, - errFootnoteMismatch, + errRstGridTableNotImplemented, + errRstMarkdownIllformedTable, + errRstNewSectionExpected, + errRstGeneralParseError, + errRstInvalidDirectiveX, + errRstInvalidField, + errRstFootnoteMismatch, errProveInit, # deadcode errGenerated, errUser, @@ -47,10 +47,13 @@ type warnXIsNeverRead = "XIsNeverRead", warnXmightNotBeenInit = "XmightNotBeenInit", warnDeprecated = "Deprecated", warnConfigDeprecated = "ConfigDeprecated", warnSmallLshouldNotBeUsed = "SmallLshouldNotBeUsed", warnUnknownMagic = "UnknownMagic", - warnRedefinitionOfLabel = "RedefinitionOfLabel", warnUnknownSubstitutionX = "UnknownSubstitutionX", - warnLanguageXNotSupported = "LanguageXNotSupported", - warnFieldXNotSupported = "FieldXNotSupported", - warnRstStyle = "warnRstStyle", warnCommentXIgnored = "CommentXIgnored", + warnRstRedefinitionOfLabel = "RedefinitionOfLabel", + warnRstUnknownSubstitutionX = "UnknownSubstitutionX", + warnRstBrokenLink = "BrokenLink", + warnRstLanguageXNotSupported = "LanguageXNotSupported", + warnRstFieldXNotSupported = "FieldXNotSupported", + warnRstStyle = "warnRstStyle", + warnCommentXIgnored = "CommentXIgnored", warnTypelessParam = "TypelessParam", warnUseBase = "UseBase", warnWriteToForeignHeap = "WriteToForeignHeap", warnUnsafeCode = "UnsafeCode", warnUnusedImportX = "UnusedImport", @@ -93,13 +96,13 @@ const errIllFormedAstX: "illformed AST: $1", errCannotOpenFile: "cannot open '$1'", errXExpected: "'$1' expected", - errGridTableNotImplemented: "grid table is not implemented", - errMarkdownIllformedTable: "illformed delimiter row of a markdown table", - errGeneralParseError: "general parse error", - errNewSectionExpected: "new section expected $1", - errInvalidDirectiveX: "invalid directive: '$1'", - errInvalidRstField: "invalid field: $1", - errFootnoteMismatch: "number of footnotes and their references don't match: $1", + errRstGridTableNotImplemented: "grid table is not implemented", + errRstMarkdownIllformedTable: "illformed delimiter row of a markdown table", + errRstNewSectionExpected: "new section expected $1", + errRstGeneralParseError: "general parse error", + errRstInvalidDirectiveX: "invalid directive: '$1'", + errRstInvalidField: "invalid field: $1", + errRstFootnoteMismatch: "number of footnotes and their references don't match: $1", errProveInit: "Cannot prove that '$1' is initialized.", # deadcode errGenerated: "$1", errUser: "$1", @@ -111,10 +114,11 @@ const warnConfigDeprecated: "config file '$1' is deprecated", warnSmallLshouldNotBeUsed: "'l' should not be used as an identifier; may look like '1' (one)", warnUnknownMagic: "unknown magic '$1' might crash the compiler", - warnRedefinitionOfLabel: "redefinition of label '$1'", - warnUnknownSubstitutionX: "unknown substitution '$1'", - warnLanguageXNotSupported: "language '$1' not supported", - warnFieldXNotSupported: "field '$1' not supported", + warnRstRedefinitionOfLabel: "redefinition of label '$1'", + warnRstUnknownSubstitutionX: "unknown substitution '$1'", + warnRstBrokenLink: "broken link '$1'", + warnRstLanguageXNotSupported: "language '$1' not supported", + warnRstFieldXNotSupported: "field '$1' not supported", warnRstStyle: "RST style: $1", warnCommentXIgnored: "comment '$1' ignored", warnTypelessParam: "", # deadcode @@ -196,6 +200,7 @@ const warnMax* = pred(hintSuccess) hintMin* = hintSuccess hintMax* = high(TMsgKind) + rstWarnings* = {warnRstRedefinitionOfLabel..warnRstStyle} type TNoteKind* = range[warnMin..hintMax] # "notes" are warnings or hints diff --git a/compiler/main.nim b/compiler/main.nim index 1c8034c6c..e4ec4d729 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -285,7 +285,7 @@ proc mainCommand*(graph: ModuleGraph) = of cmdDoc: docLikeCmd(): conf.setNoteDefaults(warnLockLevel, false) # issue #13218 - conf.setNoteDefaults(warnRedefinitionOfLabel, false) # issue #13218 + conf.setNoteDefaults(warnRstRedefinitionOfLabel, false) # issue #13218 # because currently generates lots of false positives due to conflation # of labels links in doc comments, e.g. for random.rand: # ## * `rand proc<#rand,Rand,Natural>`_ that returns an integer @@ -295,19 +295,16 @@ proc mainCommand*(graph: ModuleGraph) = commandBuildIndex(conf, $conf.outDir) of cmdRst2html: # XXX: why are warnings disabled by default for rst2html and rst2tex? - for warn in [warnUnknownSubstitutionX, warnLanguageXNotSupported, - warnFieldXNotSupported, warnRstStyle]: + for warn in rstWarnings: conf.setNoteDefaults(warn, true) - conf.setNoteDefaults(warnRedefinitionOfLabel, false) # similar to issue #13218 + conf.setNoteDefaults(warnRstRedefinitionOfLabel, false) # similar to issue #13218 when defined(leanCompiler): conf.quitOrRaise "compiler wasn't built with documentation generator" else: loadConfigs(DocConfig, cache, conf, graph.idgen) commandRst2Html(cache, conf) of cmdRst2tex, cmdDoc2tex: - for warn in [warnRedefinitionOfLabel, warnUnknownSubstitutionX, - warnLanguageXNotSupported, - warnFieldXNotSupported, warnRstStyle]: + for warn in rstWarnings: conf.setNoteDefaults(warn, true) when defined(leanCompiler): conf.quitOrRaise "compiler wasn't built with documentation generator" diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 9abdf3fc3..abea026dd 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -196,7 +196,7 @@ import os, strutils, rstast, std/enumutils, algorithm, lists, sequtils, - std/private/miscdollars + std/private/miscdollars, tables from highlite import SourceLanguage, getSourceLanguage type @@ -217,6 +217,7 @@ type mcWarning = "Warning", mcError = "Error" + # keep the order in sync with compiler/docgen.nim and compiler/lineinfos.nim: MsgKind* = enum ## the possible messages meCannotOpenFile = "cannot open '$1'", meExpected = "'$1' expected", @@ -225,10 +226,11 @@ type meNewSectionExpected = "new section expected $1", meGeneralParseError = "general parse error", meInvalidDirective = "invalid directive: '$1'", - meInvalidRstField = "invalid field: $1", + meInvalidField = "invalid field: $1", meFootnoteMismatch = "mismatch in number of footnotes and their refs: $1", mwRedefinitionOfLabel = "redefinition of label '$1'", mwUnknownSubstitution = "unknown substitution '$1'", + mwBrokenLink = "broken link '$1'", mwUnsupportedLanguage = "language '$1' not supported", mwUnsupportedField = "field '$1' not supported", mwRstStyle = "RST style: $1" @@ -489,7 +491,9 @@ type autoNumIdx: int # order of occurence: fnAutoNumber, fnAutoNumberLabel autoSymIdx: int # order of occurence: fnAutoSymbol label: string # valid for fnAutoNumberLabel - + RstFileTable* = object + filenameToIdx*: Table[string, FileIndex] + idxToFilename*: seq[string] RstSharedState = object options: RstParseOptions # parsing options hLevels: LevelMap # hierarchy of heading styles @@ -501,15 +505,19 @@ type subs: seq[Substitution] # substitutions refs*: seq[Substitution] # references anchors*: seq[AnchorSubst] # internal target substitutions - lineFootnoteNum: seq[int] # footnote line, auto numbers .. [#] - lineFootnoteNumRef: seq[int] # footnote line, their reference [#]_ - lineFootnoteSym: seq[int] # footnote line, auto symbols .. [*] - lineFootnoteSymRef: seq[int] # footnote line, their reference [*]_ + lineFootnoteNum: seq[TLineInfo] # footnote line, auto numbers .. [#] + lineFootnoteNumRef: seq[TLineInfo] # footnote line, their reference [#]_ + currFootnoteNumRef: int # ... their counter for `resolveSubs` + lineFootnoteSym: seq[TLineInfo] # footnote line, auto symbols .. [*] + lineFootnoteSymRef: seq[TLineInfo] # footnote line, their reference [*]_ + currFootnoteSymRef: int # ... their counter for `resolveSubs` footnotes: seq[FootnoteSubst] # correspondence b/w footnote label, # number, order of occurrence msgHandler: MsgHandler # How to handle errors. findFile: FindFileHandler # How to find files. - filename: string + filenames*: RstFileTable # map file name <-> FileIndex (for storing + # file names for warnings after 1st stage) + currFileIdx: FileIndex # current index in `filesnames` hasToc*: bool PRstSharedState* = ref RstSharedState @@ -579,6 +587,25 @@ proc whichRoleAux(sym: string): RstNodeKind = else: # unknown role result = rnUnknownRole +proc len(filenames: RstFileTable): int = filenames.idxToFilename.len + +proc setCurrFilename(s: PRstSharedState, file1: string) = + let nextIdx = s.filenames.len.FileIndex + let v = getOrDefault(s.filenames.filenameToIdx, file1, default = nextIdx) + if v == nextIdx: + s.filenames.filenameToIdx[file1] = v + s.filenames.idxToFilename.add file1 + s.currFileIdx = v + +proc getFilename(filenames: RstFileTable, fid: FileIndex): string = + doAssert(0 <= fid.int and fid.int < filenames.len, + "incorrect FileIndex $1 (range 0..$2)" % [ + $fid.int, $(filenames.len - 1)]) + result = filenames.idxToFilename[fid.int] + +proc currFilename(s: PRstSharedState): string = + getFilename(s.filenames, s.currFileIdx) + proc newRstSharedState*(options: RstParseOptions, filename: string, findFile: FindFileHandler, @@ -589,32 +616,37 @@ proc newRstSharedState*(options: RstParseOptions, currRoleKind: whichRoleAux(r), options: options, msgHandler: if not isNil(msgHandler): msgHandler else: defaultMsgHandler, - filename: filename, findFile: if not isNil(findFile): findFile else: defaultFindFile ) + setCurrFilename(result, filename) proc curLine(p: RstParser): int = p.line + currentTok(p).line proc findRelativeFile(p: RstParser; filename: string): string = - result = p.s.filename.splitFile.dir / filename + result = p.s.currFilename.splitFile.dir / filename if not fileExists(result): result = p.s.findFile(filename) proc rstMessage(p: RstParser, msgKind: MsgKind, arg: string) = - p.s.msgHandler(p.s.filename, curLine(p), + p.s.msgHandler(p.s.currFilename, curLine(p), p.col + currentTok(p).col, msgKind, arg) proc rstMessage(s: PRstSharedState, msgKind: MsgKind, arg: string) = - ## Print warnings for footnotes/substitutions. - ## TODO: their line/column info is not known, to fix it. - s.msgHandler(s.filename, LineRstInit, ColRstInit, msgKind, arg) + s.msgHandler(s.currFilename, LineRstInit, ColRstInit, msgKind, arg) + +proc rstMessage*(filenames: RstFileTable, f: MsgHandler, + info: TLineInfo, msgKind: MsgKind, arg: string) = + ## Print warnings using `info`, i.e. in 2nd-pass warnings for + ## footnotes/substitutions/references or from ``rstgen.nim``. + let file = getFilename(filenames, info.fileIndex) + f(file, info.line.int, info.col.int, msgKind, arg) proc rstMessage(p: RstParser, msgKind: MsgKind, arg: string, line, col: int) = - p.s.msgHandler(p.s.filename, p.line + line, + p.s.msgHandler(p.s.currFilename, p.line + line, p.col + col, msgKind, arg) proc rstMessage(p: RstParser, msgKind: MsgKind) = - p.s.msgHandler(p.s.filename, curLine(p), + p.s.msgHandler(p.s.currFilename, curLine(p), p.col + currentTok(p).col, msgKind, currentTok(p).symbol) @@ -827,11 +859,18 @@ proc addFootnoteNumManual(p: var RstParser, num: int) = return p.s.footnotes.add((fnManualNumber, num, -1, -1, $num)) +proc lineInfo(p: RstParser, iTok: int): TLineInfo = + result.col = int16(p.col + p.tok[iTok].col) + result.line = uint16(p.line + p.tok[iTok].line) + result.fileIndex = p.s.currFileIdx + +proc lineInfo(p: RstParser): TLineInfo = lineInfo(p, p.idx) + proc addFootnoteNumAuto(p: var RstParser, label: string) = ## add auto-numbered footnote. ## Empty label [#] means it'll be resolved by the occurrence. if label == "": # simple auto-numbered [#] - p.s.lineFootnoteNum.add curLine(p) + p.s.lineFootnoteNum.add lineInfo(p) p.s.footnotes.add((fnAutoNumber, -1, p.s.lineFootnoteNum.len, -1, label)) else: # auto-numbered with label [#label] for fnote in p.s.footnotes: @@ -841,7 +880,7 @@ proc addFootnoteNumAuto(p: var RstParser, label: string) = p.s.footnotes.add((fnAutoNumberLabel, -1, -1, -1, label)) proc addFootnoteSymAuto(p: var RstParser) = - p.s.lineFootnoteSym.add curLine(p) + p.s.lineFootnoteSym.add lineInfo(p) p.s.footnotes.add((fnAutoSymbol, -1, -1, p.s.lineFootnoteSym.len, "")) proc orderFootnotes(s: PRstSharedState) = @@ -850,7 +889,15 @@ proc orderFootnotes(s: PRstSharedState) = ## Save the result back to `s.footnotes`. # Report an error if found any mismatch in number of automatic footnotes - proc listFootnotes(lines: seq[int]): string = + proc listFootnotes(locations: seq[TLineInfo]): string = + var lines: seq[string] + for info in locations: + if s.filenames.len > 1: + let file = getFilename(s.filenames, info.fileIndex) + lines.add file & ":" + else: # no need to add file name here if there is only 1 + lines.add "" + lines[^1].add $info.line result.add $lines.len & " (lines " & join(lines, ", ") & ")" if s.lineFootnoteNum.len != s.lineFootnoteNumRef.len: rstMessage(s, meFootnoteMismatch, @@ -1157,7 +1204,7 @@ proc whichRole(p: RstParser, sym: string): RstNodeKind = proc toInlineCode(n: PRstNode, language: string): PRstNode = ## Creates rnInlineCode and attaches `n` contents as code (in 3rd son). - result = newRstNode(rnInlineCode) + result = newRstNode(rnInlineCode, info=n.info) let args = newRstNode(rnDirArg) var lang = language if language == "cpp": lang = "c++" @@ -1179,6 +1226,7 @@ proc toOtherRole(n: PRstNode, kind: RstNodeKind, roleName: string): PRstNode = result = newRstNode(kind, newSons) proc parsePostfix(p: var RstParser, n: PRstNode): PRstNode = + ## Finalizes node `n` that was tentatively determined as interpreted text. var newKind = n.kind var newSons = n.sons @@ -1207,12 +1255,10 @@ proc parsePostfix(p: var RstParser, n: PRstNode): PRstNode = newKind = rnHyperlink newSons = @[a, b] setRef(p, rstnodeToRefname(a), b) - elif n.kind == rnInterpretedText: + result = newRstNode(newKind, newSons) + else: # some link that will be resolved in `resolveSubs` newKind = rnRef - else: - newKind = rnRef - newSons = @[n] - result = newRstNode(newKind, newSons) + result = newRstNode(newKind, sons=newSons, info=n.info) elif match(p, p.idx, ":w:"): # a role: let (roleName, lastIdx) = getRefname(p, p.idx+1) @@ -1300,20 +1346,19 @@ proc parseWordOrRef(p: var RstParser, father: PRstNode) = else: # check for reference (probably, long one like some.ref.with.dots_ ) var saveIdx = p.idx - var isRef = false + var reference: PRstNode = nil inc p.idx while currentTok(p).kind in {tkWord, tkPunct}: if currentTok(p).kind == tkPunct: if isInlineMarkupEnd(p, "_", exact=true): - isRef = true + reference = newRstNode(rnRef, info=lineInfo(p, saveIdx)) break if not validRefnamePunct(currentTok(p).symbol): break inc p.idx - if isRef: - let r = newRstNode(rnRef) - for i in saveIdx..p.idx-1: r.add newLeaf(p.tok[i].symbol) - father.add r + if reference != nil: + for i in saveIdx..p.idx-1: reference.add newLeaf(p.tok[i].symbol) + father.add reference inc p.idx # skip final _ else: # 1 normal word father.add newLeaf(p.tok[saveIdx].symbol) @@ -1387,6 +1432,8 @@ proc parseUntil(p: var RstParser, father: PRstNode, postfix: string, else: rstMessage(p, meExpected, postfix, line, col) proc parseMarkdownCodeblock(p: var RstParser): PRstNode = + result = newRstNodeA(p, rnCodeBlock) + result.info = lineInfo(p) var args = newRstNode(rnDirArg) if currentTok(p).kind == tkWord: args.add(newLeaf(p)) @@ -1411,7 +1458,6 @@ proc parseMarkdownCodeblock(p: var RstParser): PRstNode = inc p.idx var lb = newRstNode(rnLiteralBlock) lb.add(n) - result = newRstNodeA(p, rnCodeBlock) result.add(args) result.add(PRstNode(nil)) result.add(lb) @@ -1495,6 +1541,7 @@ proc parseFootnoteName(p: var RstParser, reference: bool): PRstNode = proc parseInline(p: var RstParser, father: PRstNode) = var n: PRstNode # to be used in `if` condition + let saveIdx = p.idx case currentTok(p).kind of tkPunct: if isInlineMarkupStart(p, "***"): @@ -1537,12 +1584,12 @@ proc parseInline(p: var RstParser, father: PRstNode) = n = n.toOtherRole(k, roleName) father.add(n) elif isInlineMarkupStart(p, "`"): - var n = newRstNode(rnInterpretedText) + var n = newRstNode(rnInterpretedText, info=lineInfo(p, p.idx+1)) parseUntil(p, n, "`", false) # bug #17260 n = parsePostfix(p, n) father.add(n) elif isInlineMarkupStart(p, "|"): - var n = newRstNode(rnSubstitutionReferences) + var n = newRstNode(rnSubstitutionReferences, info=lineInfo(p, p.idx+1)) parseUntil(p, n, "|", false) father.add(n) elif roSupportMarkdown in p.s.options and @@ -1552,15 +1599,14 @@ proc parseInline(p: var RstParser, father: PRstNode) = elif isInlineMarkupStart(p, "[") and nextTok(p).symbol != "[" and (n = parseFootnoteName(p, reference=true); n != nil): var nn = newRstNode(rnFootnoteRef) + nn.info = lineInfo(p, saveIdx+1) nn.add n let (fnType, _) = getFootnoteType(n) case fnType of fnAutoSymbol: - p.s.lineFootnoteSymRef.add curLine(p) - nn.order = p.s.lineFootnoteSymRef.len + p.s.lineFootnoteSymRef.add lineInfo(p) of fnAutoNumber: - p.s.lineFootnoteNumRef.add curLine(p) - nn.order = p.s.lineFootnoteNumRef.len + p.s.lineFootnoteNumRef.add lineInfo(p) else: discard father.add(nn) else: @@ -1693,7 +1739,7 @@ proc parseField(p: var RstParser): PRstNode = ## Returns a parsed rnField node. ## ## rnField nodes have two children nodes, a rnFieldName and a rnFieldBody. - result = newRstNode(rnField) + result = newRstNode(rnField, info=lineInfo(p)) var col = currentTok(p).col var fieldname = newRstNode(rnFieldName) parseUntil(p, fieldname, ":", false) @@ -2469,6 +2515,7 @@ proc parseDirective(p: var RstParser, k: RstNodeKind, flags: DirFlags): PRstNode ## Both rnDirArg and rnFieldList children nodes might be nil, so you need to ## check them before accessing. result = newRstNodeA(p, k) + if k == rnCodeBlock: result.info = lineInfo(p) var args: PRstNode = nil var options: PRstNode = nil if hasArg in flags: @@ -2593,14 +2640,16 @@ proc dirInclude(p: var RstParser): PRstNode = var q: RstParser initParser(q, p.s) - q.s.filename = path + let saveFileIdx = p.s.currFileIdx + setCurrFilename(p.s, path) getTokens( - inputString[startPosition..endPosition].strip(), + inputString[startPosition..endPosition], q.tok) # workaround a GCC bug; more like the interior pointer bug? #if find(q.tok[high(q.tok)].symbol, "\0\x01\x02") > 0: # InternalError("Too many binary zeros in include file") result = parseDoc(q) + p.s.currFileIdx = saveFileIdx proc dirCodeBlock(p: var RstParser, nimExtension = false): PRstNode = ## Parses a code block. @@ -2634,7 +2683,7 @@ proc dirCodeBlock(p: var RstParser, nimExtension = false): PRstNode = if result.sons[1].isNil: result.sons[1] = newRstNode(rnFieldList) assert result.sons[1].kind == rnFieldList # Hook the extra field and specify the Nim language as value. - var extraNode = newRstNode(rnField) + var extraNode = newRstNode(rnField, info=lineInfo(p)) extraNode.add(newRstNode(rnFieldName)) extraNode.add(newRstNode(rnFieldBody)) extraNode.sons[0].add newLeaf("default-language") @@ -2837,9 +2886,8 @@ proc parseDotDot(p: var RstParser): PRstNode = else: result = parseComment(p, col) -proc rstParsePass1*(fragment, filename: string, +proc rstParsePass1*(fragment: string, line, column: int, - options: RstParseOptions, sharedState: PRstSharedState): PRstNode = ## Parses an RST `fragment`. ## The result should be further processed by @@ -2872,7 +2920,8 @@ proc resolveSubs*(s: PRstSharedState, n: PRstNode): PRstNode = var key = addNodes(n) var e = getEnv(key) if e != "": result = newLeaf(e) - else: rstMessage(s, mwUnknownSubstitution, key) + else: rstMessage(s.filenames, s.msgHandler, n.info, + mwUnknownSubstitution, key) of rnHeadline, rnOverline: # fix up section levels depending on presence of a title and subtitle if s.hTitleCnt == 2: @@ -2890,12 +2939,14 @@ proc resolveSubs*(s: PRstSharedState, n: PRstNode): PRstNode = let text = newRstNode(rnInner, n.sons) result.sons = @[text, y] else: - let s = findMainAnchor(s, refn) - if s != "": + let anchor = findMainAnchor(s, refn) + if anchor != "": result = newRstNode(rnInternalRef) let text = newRstNode(rnInner, n.sons) - result.sons = @[text, # visible text of reference - newLeaf(s)] # link itself + result.sons = @[text, # visible text of reference + newLeaf(anchor)] # link itself + else: + rstMessage(s.filenames, s.msgHandler, n.info, mwBrokenLink, refn) of rnFootnote: var (fnType, num) = getFootnoteType(n.sons[0]) case fnType @@ -2922,20 +2973,22 @@ proc resolveSubs*(s: PRstSharedState, n: PRstNode): PRstNode = result.add(nn) var refn = fnType.prefix # create new rnFootnoteRef, add final label, and finalize target refn: - result = newRstNode(rnFootnoteRef) + result = newRstNode(rnFootnoteRef, info = n.info) case fnType of fnManualNumber: addLabel num refn.add $num of fnAutoNumber: - addLabel getFootnoteNum(s, n.order) - refn.add $n.order + inc s.currFootnoteNumRef + addLabel getFootnoteNum(s, s.currFootnoteNumRef) + refn.add $s.currFootnoteNumRef of fnAutoNumberLabel: addLabel getFootnoteNum(s, rstnodeToRefname(n)) refn.add rstnodeToRefname(n) of fnAutoSymbol: - addLabel getAutoSymbol(s, n.order) - refn.add $n.order + inc s.currFootnoteSymRef + addLabel getAutoSymbol(s, s.currFootnoteSymRef) + refn.add $s.currFootnoteSymRef of fnCitation: result.add n.sons[0] refn.add rstnodeToRefname(n) @@ -2943,7 +2996,7 @@ proc resolveSubs*(s: PRstSharedState, n: PRstNode): PRstNode = if anch != "": result.add newLeaf(anch) # add link else: - rstMessage(s, mwUnknownSubstitution, refn) + rstMessage(s.filenames, s.msgHandler, n.info, mwBrokenLink, refn) result.add newLeaf(refn) # add link of rnLeaf: discard @@ -2971,14 +3024,18 @@ proc resolveSubs*(s: PRstSharedState, n: PRstNode): PRstNode = result.sons = newSons proc rstParse*(text, filename: string, - line, column: int, hasToc: var bool, + line, column: int, options: RstParseOptions, findFile: FindFileHandler = nil, - msgHandler: MsgHandler = nil): PRstNode = - ## Parses the whole `text`. The result is ready for `rstgen.renderRstToOut`. + msgHandler: MsgHandler = nil): + tuple[node: PRstNode, filenames: RstFileTable, hasToc: bool] = + ## Parses the whole `text`. The result is ready for `rstgen.renderRstToOut`, + ## note that 2nd tuple element should be fed to `initRstGenerator` + ## argument `filenames` (it is being filled here at least with `filename` + ## and possibly with other files from RST ``.. include::`` statement). var sharedState = newRstSharedState(options, filename, findFile, msgHandler) - let unresolved = rstParsePass1(text, filename, line, column, - options, sharedState) + let unresolved = rstParsePass1(text, line, column, sharedState) preparePass2(sharedState, unresolved) - result = resolveSubs(sharedState, unresolved) - hasToc = sharedState.hasToc + result.node = resolveSubs(sharedState, unresolved) + result.filenames = sharedState.filenames + result.hasToc = sharedState.hasToc diff --git a/lib/packages/docutils/rstast.nim b/lib/packages/docutils/rstast.nim index 2489ce40c..fa0620f44 100644 --- a/lib/packages/docutils/rstast.nim +++ b/lib/packages/docutils/rstast.nim @@ -74,6 +74,11 @@ type rnLeaf # a leaf; the node's text field contains the # leaf val + FileIndex* = distinct int32 + TLineInfo* = object + line*: uint16 + col*: int16 + fileIndex*: FileIndex PRstNode* = ref RstNode ## an RST node RstNodeSeq* = seq[PRstNode] @@ -92,21 +97,32 @@ type level*: int ## level of headings starting from 1 (main ## chapter) to larger ones (minor sub-sections) ## level=0 means it's document title or subtitle - of rnFootnote, rnCitation, rnFootnoteRef, rnOptionListItem: + of rnFootnote, rnCitation, rnOptionListItem: order*: int ## footnote order (for auto-symbol footnotes and ## auto-numbered ones without a label) + of rnRef, rnSubstitutionReferences, + rnInterpretedText, rnField, rnInlineCode, rnCodeBlock, rnFootnoteRef: + info*: TLineInfo ## To have line/column info for warnings at + ## nodes that are post-processed after parsing else: discard anchor*: string ## anchor, internal link target ## (aka HTML id tag, aka Latex label/hypertarget) sons*: RstNodeSeq ## the node's sons +proc `==`*(a, b: FileIndex): bool {.borrow.} + proc len*(n: PRstNode): int = result = len(n.sons) proc newRstNode*(kind: RstNodeKind, sons: seq[PRstNode] = @[], anchor = ""): PRstNode = + result = PRstNode(kind: kind, sons: sons, anchor: anchor) + +proc newRstNode*(kind: RstNodeKind, info: TLineInfo, + sons: seq[PRstNode] = @[]): PRstNode = result = PRstNode(kind: kind, sons: sons) + result.info = info proc newRstNode*(kind: RstNodeKind, s: string): PRstNode {.deprecated.} = assert kind in {rnLeaf, rnSmiley} @@ -388,7 +404,7 @@ proc renderRstToStr*(node: PRstNode, indent=0): string = result.add " adType=" & node.adType of rnHeadline, rnOverline, rnMarkdownHeadline: result.add " level=" & $node.level - of rnFootnote, rnCitation, rnFootnoteRef, rnOptionListItem: + of rnFootnote, rnCitation, rnOptionListItem: result.add (if node.order == 0: "" else: " order=" & $node.order) else: discard diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index b9c8f688b..10a229479 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -40,7 +40,7 @@ ## can be done by simply searching for [footnoteName]. import strutils, os, hashes, strtabs, rstast, rst, highlite, tables, sequtils, - algorithm, parseutils + algorithm, parseutils, std/strbasics import ../../std/private/since @@ -72,11 +72,11 @@ type tocPart*: seq[TocEntry] hasToc*: bool theIndex: string # Contents of the index file to be dumped at the end. - options*: RstParseOptions findFile*: FindFileHandler msgHandler*: MsgHandler outDir*: string ## output directory, initialized by docgen.nim destFile*: string ## output (HTML) file, initialized by docgen.nim + filenames*: RstFileTable filename*: string ## source Nim or Rst file meta*: array[MetaEnum, string] currentSection: string ## \ @@ -112,9 +112,9 @@ proc init(p: var CodeBlockParams) = proc initRstGenerator*(g: var RstGenerator, target: OutputTarget, config: StringTableRef, filename: string, - options: RstParseOptions, findFile: FindFileHandler = nil, - msgHandler: MsgHandler = nil) = + msgHandler: MsgHandler = nil, + filenames = default(RstFileTable)) = ## Initializes a ``RstGenerator``. ## ## You need to call this before using a ``RstGenerator`` with any other @@ -160,9 +160,9 @@ proc initRstGenerator*(g: var RstGenerator, target: OutputTarget, g.target = target g.tocPart = @[] g.filename = filename + g.filenames = filenames g.splitAfter = 20 g.theIndex = "" - g.options = options g.findFile = findFile g.currentSection = "" g.id = 0 @@ -908,9 +908,8 @@ proc renderSmiley(d: PDoc, n: PRstNode, result: var string) = [d.config.getOrDefault"doc.smiley_format" % n.text]) proc getField1Int(d: PDoc, n: PRstNode, fieldName: string): int = - # TODO: proper column/line info template err(msg: string) = - d.msgHandler(d.filename, 1, 0, meInvalidRstField, msg) + rstMessage(d.filenames, d.msgHandler, n.info, meInvalidField, msg) let value = n.getFieldValue var number: int let nChars = parseInt(value, number) @@ -958,7 +957,8 @@ proc parseCodeBlockField(d: PDoc, n: PRstNode, params: var CodeBlockParams) = params.langStr = n.getFieldValue.strip params.lang = params.langStr.getSourceLanguage else: - d.msgHandler(d.filename, 1, 0, mwUnsupportedField, n.getArgument) + rstMessage(d.filenames, d.msgHandler, n.info, mwUnsupportedField, + n.getArgument) proc parseCodeBlockParams(d: PDoc, n: PRstNode): CodeBlockParams = ## Iterates over all code block fields and returns processed params. @@ -1069,7 +1069,8 @@ proc renderCode(d: PDoc, n: PRstNode, result: var string) = dispA(d.target, result, blockStart, blockStart, []) if params.lang == langNone: if len(params.langStr) > 0: - d.msgHandler(d.filename, 1, 0, mwUnsupportedLanguage, params.langStr) + rstMessage(d.filenames, d.msgHandler, n.info, mwUnsupportedLanguage, + params.langStr) for letter in m.text: escChar(d.target, result, letter, emText) else: renderCodeLang(result, params.lang, m.text, d.target) @@ -1564,23 +1565,24 @@ proc rstToHtml*(s: string, options: RstParseOptions, result = "" const filen = "input" + let (rst, filenames, _) = rstParse(s, filen, + line=LineRstInit, column=ColRstInit, + options, myFindFile, msgHandler) var d: RstGenerator - initRstGenerator(d, outHtml, config, filen, options, myFindFile, msgHandler) - var dummyHasToc = false - var rst = rstParse(s, filen, line=LineRstInit, column=ColRstInit, - dummyHasToc, options, myFindFile, msgHandler) + initRstGenerator(d, outHtml, config, filen, myFindFile, msgHandler, filenames) result = "" renderRstToOut(d, rst, result) + strbasics.strip(result) proc rstToLatex*(rstSource: string; options: RstParseOptions): string {.inline, since: (1, 3).} = ## Convenience proc for `renderRstToOut` and `initRstGenerator`. runnableExamples: doAssert rstToLatex("*Hello* **world**", {}) == """\emph{Hello} \textbf{world}""" if rstSource.len == 0: return - var option: bool + let (rst, filenames, _) = rstParse(rstSource, "", + line=LineRstInit, column=ColRstInit, + options) var rstGenera: RstGenerator - rstGenera.initRstGenerator(outLatex, defaultConfig(), "input", options) - rstGenera.renderRstToOut( - rstParse(rstSource, "", line=LineRstInit, column=ColRstInit, - option, options), - result) + rstGenera.initRstGenerator(outLatex, defaultConfig(), "input", filenames=filenames) + rstGenera.renderRstToOut(rst, result) + strbasics.strip(result) diff --git a/tests/stdlib/trst.nim b/tests/stdlib/trst.nim index fe99d6cfa..fc3ccbf4e 100644 --- a/tests/stdlib/trst.nim +++ b/tests/stdlib/trst.nim @@ -5,6 +5,8 @@ discard """ [Suite] RST indentation +[Suite] Warnings + [Suite] RST include directive [Suite] RST escaping @@ -51,9 +53,8 @@ proc toAst(input: string, # we don't find any files in online mode: result = "" - var dummyHasToc = false - var rst = rstParse(input, filen, line=LineRstInit, column=ColRstInit, - dummyHasToc, rstOptions, myFindFile, testMsgHandler) + var (rst, _, _) = rstParse(input, filen, line=LineRstInit, column=ColRstInit, + rstOptions, myFindFile, testMsgHandler) result = renderRstToStr(rst) except EParseError as e: if e.msg != "": @@ -356,6 +357,53 @@ suite "RST indentation": # "template..." should be parsed as a definition list attached to ":test:": check inputWrong.toAst != ast +suite "Warnings": + test "warnings for broken footnotes/links/substitutions": + let input = dedent""" + firstParagraph + + footnoteRef [som]_ + + link `a broken Link`_ + + substitution |undefined subst| + + link short.link_ + + lastParagraph + """ + var warnings = new seq[string] + let output = input.toAst(warnings=warnings) + check(warnings[] == @[ + "input(3, 14) Warning: broken link 'citation-som'", + "input(5, 7) Warning: broken link 'a-broken-link'", + "input(7, 15) Warning: unknown substitution 'undefined subst'", + "input(9, 6) Warning: broken link 'shortdotlink'" + ]) + + test "With include directive and blank lines at the beginning": + "other.rst".writeFile(dedent""" + + + firstParagraph + + here brokenLink_""") + let input = ".. include:: other.rst" + var warnings = new seq[string] + let output = input.toAst(warnings=warnings) + check warnings[] == @["other.rst(5, 6) Warning: broken link 'brokenlink'"] + check(output == dedent""" + rnInner + rnParagraph + rnLeaf 'firstParagraph' + rnParagraph + rnLeaf 'here' + rnLeaf ' ' + rnRef + rnLeaf 'brokenLink' + """) + removeFile("other.rst") + suite "RST include directive": test "Include whole": "other.rst".writeFile("**test1**") @@ -374,7 +422,7 @@ OtherStart .. include:: other.rst :start-after: OtherStart """ - doAssert "Visible" == rstTohtml(input, {}, defaultConfig()) + check "Visible" == rstTohtml(input, {}, defaultConfig()) removeFile("other.rst") test "Include everything before": @@ -406,7 +454,7 @@ And this should **NOT** be visible in `docs.html` :start-after: OtherStart :end-before: OtherEnd """ - doAssert "Visible" == rstTohtml(input, {}, defaultConfig()) + check "Visible" == rstTohtml(input, {}, defaultConfig()) removeFile("other.rst") diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim index d5d902e1d..867677404 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -237,8 +237,7 @@ not in table"""
  • -
    Precedence levelOperatorsFirst characterTerminal symbol
    D1 """ & id"code" & " " & op"\|" & """D2
    E1 | text
    F2 without pipe

    not in table

    -""") +

    not in table

    """) let input2 = """ | A1 header | A2 | | --- | --- |""" @@ -420,9 +419,10 @@ Some chapter "the following intermediate section level(s) are missing on " & "lines 12..15: underline -----)") + test "RST sections overline": # the same as input9good but with overline headings # first overline heading has a special meaning: document title - let input10 = dedent """ + let input = dedent """ ====== Title0 ====== @@ -454,22 +454,23 @@ Some chapter ~~~~~ """ - var option: bool var rstGenera: RstGenerator - var output10: string - rstGenera.initRstGenerator(outHtml, defaultConfig(), "input", {}) - rstGenera.renderRstToOut(rstParse(input10, "", 1, 1, option, {}), output10) + var output: string + let (rst, files, _) = rstParse(input, "", 1, 1, {}) + rstGenera.initRstGenerator(outHtml, defaultConfig(), "input", filenames = files) + rstGenera.renderRstToOut(rst, output) doAssert rstGenera.meta[metaTitle] == "Title0" doAssert rstGenera.meta[metaSubTitle] == "SubTitle0" - doAssert "

    Level1

    " in output10 - doAssert "

    Level2

    " in output10 - doAssert "

    Level3

    " in output10 - doAssert "

    L1

    " in output10 - doAssert "

    Another2

    " in output10 - doAssert "

    More3

    " in output10 + doAssert "

    Level1

    " in output + doAssert "

    Level2

    " in output + doAssert "

    Level3

    " in output + doAssert "

    L1

    " in output + doAssert "

    Another2

    " in output + doAssert "

    More3

    " in output + test "RST sections overline 2": # check that a paragraph prevents interpreting overlines as document titles - let input11 = dedent """ + let input = dedent """ Paragraph ====== @@ -480,18 +481,19 @@ Some chapter SubTitle0 +++++++++ """ - var option11: bool - var rstGenera11: RstGenerator - var output11: string - rstGenera11.initRstGenerator(outHtml, defaultConfig(), "input", {}) - rstGenera11.renderRstToOut(rstParse(input11, "", 1, 1, option11, {}), output11) - doAssert rstGenera11.meta[metaTitle] == "" - doAssert rstGenera11.meta[metaSubTitle] == "" - doAssert "

    Title0

    " in output11 - doAssert "

    SubTitle0

    " in output11 + var rstGenera: RstGenerator + var output: string + let (rst, files, _) = rstParse(input, "", 1, 1, {}) + rstGenera.initRstGenerator(outHtml, defaultConfig(), "input", filenames=files) + rstGenera.renderRstToOut(rst, output) + doAssert rstGenera.meta[metaTitle] == "" + doAssert rstGenera.meta[metaSubTitle] == "" + doAssert "

    Title0

    " in output + doAssert "

    SubTitle0

    " in output + test "RST+Markdown sections": # check that RST and Markdown headings don't interfere - let input12 = dedent """ + let input = dedent """ ====== Title0 ====== @@ -509,14 +511,14 @@ Some chapter MySection2a ----------- """ - var option12: bool - var rstGenera12: RstGenerator - var output12: string - rstGenera12.initRstGenerator(outHtml, defaultConfig(), "input", {}) - rstGenera12.renderRstToOut(rstParse(input12, "", 1, 1, option12, {roSupportMarkdown}), output12) - doAssert rstGenera12.meta[metaTitle] == "Title0" - doAssert rstGenera12.meta[metaSubTitle] == "" - doAssert output12 == + var rstGenera: RstGenerator + var output: string + let (rst, files, _) = rstParse(input, "", 1, 1, {roSupportMarkdown}) + rstGenera.initRstGenerator(outHtml, defaultConfig(), "input", filenames=files) + rstGenera.renderRstToOut(rst, output) + doAssert rstGenera.meta[metaTitle] == "Title0" + doAssert rstGenera.meta[metaSubTitle] == "" + doAssert output == "\n

    MySection1a

    " & # RST "\n

    MySection1b

    " & # Markdown "\n

    MySection1c

    " & # RST @@ -628,7 +630,7 @@ let x = 1 let p2 = """

    Par2 value2.

    """ let p3 = """

    Par3 """ & id"value3" & ".

    " let p4 = """

    Par4 value4.

    """ - let expected = p1 & p2 & "\n" & p3 & "\n" & p4 & "\n" + let expected = p1 & p2 & "\n" & p3 & "\n" & p4 check(input.toHtml == expected) test "role directive": @@ -653,8 +655,8 @@ Check that comment disappears: let output1 = input1.toHtml doAssert output1 == "Check that comment disappears:" - test "RST line blocks": - let input1 = """ + test "RST line blocks + headings": + let input = """ ===== Test1 ===== @@ -665,19 +667,20 @@ Test1 | other line """ - var option: bool var rstGenera: RstGenerator - var output1: string - rstGenera.initRstGenerator(outHtml, defaultConfig(), "input", {}) - rstGenera.renderRstToOut(rstParse(input1, "", 1, 1, option, {}), output1) + var output: string + let (rst, files, _) = rstParse(input, "", 1, 1, {}) + rstGenera.initRstGenerator(outHtml, defaultConfig(), "input", filenames=files) + rstGenera.renderRstToOut(rst, output) doAssert rstGenera.meta[metaTitle] == "Test1" # check that title was not overwritten to '|' - doAssert output1 == "



    line block
    other line

    " - let output1l = rstToLatex(input1, {}) + doAssert output == "



    line block
    other line

    " + let output1l = rstToLatex(input, {}) doAssert "line block\n\n" in output1l doAssert "other line\n\n" in output1l doAssert output1l.count("\\vspace") == 2 + 2 # +2 surrounding paddings + test "RST line blocks": let input2 = dedent""" Paragraph1 @@ -686,7 +689,7 @@ Test1 Paragraph2""" let output2 = input2.toHtml - doAssert "Paragraph1


    Paragraph2

    \n" == output2 + doAssert "Paragraph1


    Paragraph2

    " == output2 let input3 = dedent""" | xxx @@ -853,7 +856,7 @@ Test1 check("input(6, 1) Warning: RST style: \n" & "not enough indentation on line 6" in warnings8[0]) doAssert output8 == "Paragraph.
      " & - "
    1. stringA
    2. \n
    3. stringB
    4. \n
    \n

    C. string1 string2

    \n" + "
  • stringA
  • \n
  • stringB
  • \n\n

    C. string1 string2

    " test "Markdown enumerated lists": let input1 = dedent """ @@ -926,7 +929,7 @@ Test1 Not references[#note]_[1 #]_ [wrong citation]_ and [not&allowed]_. """ let output2 = input2.toHtml - doAssert output2 == "Not references[#note]_[1 #]_ [wrong citation]_ and [not&allowed]_. " + doAssert output2 == "Not references[#note]_[1 #]_ [wrong citation]_ and [not&allowed]_." # check that auto-symbol footnotes work: let input3 = dedent """ @@ -1034,9 +1037,7 @@ Test1 """ var warnings8 = new seq[string] let output8 = input8.toHtml(warnings=warnings8) - # TODO: the line 1 is arbitrary because reference lines are not preserved - check(warnings8[] == @["input(1, 1) Warning: unknown substitution " & - "\'citation-som\'"]) + check(warnings8[] == @["input(3, 7) Warning: broken link 'citation-som'"]) # check that footnote group does not break parsing of other directives: let input9 = dedent """ @@ -1144,10 +1145,33 @@ Test1 """ var error = new string let output = input.toHtml(error=error) - check(error[] == "input(1, 1) Error: invalid field: " & + check(error[] == "input(2, 3) Error: invalid field: " & "extra arguments were given to number-lines: ' let a = 1'") check "" == output + test "code-block warning": + let input = dedent """ + .. code:: Nim + :unsupportedField: anything + + .. code:: unsupportedLang + + anything + + ```anotherLang + someCode + ``` + """ + let warnings = new seq[string] + let output = input.toHtml(warnings=warnings) + check(warnings[] == @[ + "input(2, 4) Warning: field 'unsupportedField' not supported", + "input(4, 11) Warning: language 'unsupportedLang' not supported", + "input(8, 4) Warning: language 'anotherLang' not supported" + ]) + check(output == "
    anything
    " & + "

    \nsomeCode\n

    ") + test "RST admonitions": # check that all admonitions are implemented let input0 = dedent """ @@ -1477,7 +1501,7 @@ Test1 check "(3, 15) Warning: " in warnings[1] check "language 'py:class' not supported" in warnings[1] check("""

    See function spam.

    """ & "\n" & - """

    See also egg.

    """ & "\n" == + """

    See also egg.

    """ == output) test "(not) Roles: check escaping 1": From e38388eb4e92b0e7c7db204fe36d7969967cd609 Mon Sep 17 00:00:00 2001 From: Araq Date: Tue, 20 Jul 2021 08:35:53 +0200 Subject: [PATCH 2357/3768] use .} instead of } for code highlighters --- lib/system/threads.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/system/threads.nim b/lib/system/threads.nim index 7fbfe54ab..2b5eb07fd 100644 --- a/lib/system/threads.nim +++ b/lib/system/threads.nim @@ -41,7 +41,7 @@ ## for i in 0..high(thr): ## createThread(thr[i], threadFunc, (i*10, i*10+5)) ## joinThreads(thr) -## +## ## deinitLock(L) when not declared(ThisIsSystem): @@ -395,7 +395,7 @@ elif defined(netbsd): elif defined(freebsd): proc syscall(arg: cint, arg0: ptr cint): cint {.varargs, importc: "syscall", header: "".} - var SYS_thr_self {.importc:"SYS_thr_self", header:""}: cint + var SYS_thr_self {.importc:"SYS_thr_self", header:"".}: cint proc getThreadId*(): int = ## Gets the ID of the currently running thread. From 5e011e8e42b2e4e322e9b40d4075da6d4a9103e5 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 20 Jul 2021 10:37:36 +0200 Subject: [PATCH 2358/3768] updated the changelog (#18539) --- changelog.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/changelog.md b/changelog.md index 097beac2c..a633c1fb3 100644 --- a/changelog.md +++ b/changelog.md @@ -39,9 +39,6 @@ - `math.round` now is rounded "away from zero" in JS backend which is consistent with other backends. See #9125. Use `-d:nimLegacyJsRound` for previous behavior. -- Instead of deleting the element at the last index, - `system.delete()` now raises `IndexDefect` when given index is out of bounds. - - Changed the behavior of `uri.decodeQuery` when there are unencoded `=` characters in the decoded values. Prior versions would raise an error. This is no longer the case to comply with the HTML spec and other languages From 6286b5599c7d9fc03f3a6809f5c17fbefc23427e Mon Sep 17 00:00:00 2001 From: Paul Roberts Date: Tue, 20 Jul 2021 11:50:43 +0100 Subject: [PATCH 2359/3768] termux ssl (#18520) --- lib/pure/net.nim | 2 +- lib/pure/ssl_certs.nim | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 934dd034e..c2e4810de 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -647,7 +647,7 @@ when defineSsl: if verifyMode != CVerifyNone: # Use the caDir and caFile parameters if set if caDir != "" or caFile != "": - if newCTX.SSL_CTX_load_verify_locations(caFile, caDir) != VerifySuccess: + if newCTX.SSL_CTX_load_verify_locations(if caFile == "": nil else: caFile.cstring, if caDir == "": nil else: caDir.cstring) != VerifySuccess: raise newException(IOError, "Failed to load SSL/TLS CA certificate(s).") else: diff --git a/lib/pure/ssl_certs.nim b/lib/pure/ssl_certs.nim index 2d2644ebe..c7ce04ffa 100644 --- a/lib/pure/ssl_certs.nim +++ b/lib/pure/ssl_certs.nim @@ -34,6 +34,7 @@ elif defined(linux): # Fedora/RHEL "/etc/pki/tls/certs", # Android + "/data/data/com.termux/files/usr/etc/tls/cert.pem", "/system/etc/security/cacerts", ] elif defined(bsd): From dbf8d0b8946419311ac710b1adc995ad206b52c1 Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 20 Jul 2021 20:12:15 +0800 Subject: [PATCH 2360/3768] use more `.}` (#18542) --- changelogs/changelog_0_19_0.md | 2 +- doc/manual_experimental.rst | 4 ++-- lib/posix/posix_linux_amd64.nim | 2 +- lib/system.nim | 8 ++++---- lib/windows/winlean.nim | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/changelogs/changelog_0_19_0.md b/changelogs/changelog_0_19_0.md index 18d3ca2b3..45cefe933 100644 --- a/changelogs/changelog_0_19_0.md +++ b/changelogs/changelog_0_19_0.md @@ -15,7 +15,7 @@ at runtime. Use ``ref object`` with inheritance rather than ``object`` with inheritance to prevent this issue. - The ``not nil`` type annotation now has to be enabled explicitly - via ``{.experimental: "notnil"}`` as we are still not pleased with how this + via ``{.experimental: "notnil".}`` as we are still not pleased with how this feature works with Nim's containers. - The parser now warns about inconsistent spacing around binary operators as these can easily be confused with unary operators. This warning will likely diff --git a/doc/manual_experimental.rst b/doc/manual_experimental.rst index d10ef8d1b..22db184a8 100644 --- a/doc/manual_experimental.rst +++ b/doc/manual_experimental.rst @@ -474,13 +474,13 @@ Not nil annotation ================== **Note:** This is an experimental feature. It can be enabled with -`{.experimental: "notnil"}`. +`{.experimental: "notnil".}`. All types for which `nil` is a valid value can be annotated with the `not nil` annotation to exclude `nil` as a valid value: .. code-block:: nim - {.experimental: "notnil"} + {.experimental: "notnil".} type PObject = ref TObj not nil diff --git a/lib/posix/posix_linux_amd64.nim b/lib/posix/posix_linux_amd64.nim index 5c0b4d6cf..2309e19a9 100644 --- a/lib/posix/posix_linux_amd64.nim +++ b/lib/posix/posix_linux_amd64.nim @@ -334,7 +334,7 @@ type si_status*: cint ## Exit value or signal. si_band*: int ## Band event for SIGPOLL. si_value*: SigVal ## Signal value. - pad {.importc: "_pad"}: array[128 - 56, uint8] + pad {.importc: "_pad".}: array[128 - 56, uint8] Nl_item* {.importc: "nl_item", header: "".} = cint Nl_catd* {.importc: "nl_catd", header: "".} = pointer diff --git a/lib/system.nim b/lib/system.nim index 2790187f4..e878f9f2f 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -536,7 +536,7 @@ include "system/arithmetics" include "system/comparisons" const - appType* {.magic: "AppType"}: string = "" + appType* {.magic: "AppType".}: string = "" ## A string that describes the application type. Possible values: ## `"console"`, `"gui"`, `"lib"`. @@ -1101,15 +1101,15 @@ const ## True only when accessed in the main module. This works thanks to ## compiler magic. It is useful to embed testing code in a module. - CompileDate* {.magic: "CompileDate"}: string = "0000-00-00" + CompileDate* {.magic: "CompileDate".}: string = "0000-00-00" ## The date (in UTC) of compilation as a string of the form ## `YYYY-MM-DD`. This works thanks to compiler magic. - CompileTime* {.magic: "CompileTime"}: string = "00:00:00" + CompileTime* {.magic: "CompileTime".}: string = "00:00:00" ## The time (in UTC) of compilation as a string of the form ## `HH:MM:SS`. This works thanks to compiler magic. - cpuEndian* {.magic: "CpuEndian"}: Endianness = littleEndian + cpuEndian* {.magic: "CpuEndian".}: Endianness = littleEndian ## The endianness of the target CPU. This is a valuable piece of ## information for low-level code only. This works thanks to compiler ## magic. diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index d901af244..b8f3886d7 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -1143,7 +1143,7 @@ proc setFileTime*(hFile: Handle, lpCreationTime: LPFILETIME, type # https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-sid_identifier_authority SID_IDENTIFIER_AUTHORITY* {.importc, header: "".} = object - value* {.importc: "Value"}: array[6, BYTE] + value* {.importc: "Value".}: array[6, BYTE] # https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-sid SID* {.importc, header: "".} = object Revision: BYTE From a8b3e7c05919511db62f1aabd706c46316b4f7b6 Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 20 Jul 2021 21:32:55 +0800 Subject: [PATCH 2361/3768] follow up #18517 (#18537) * follow up #18517 * Update lib/pure/os.nim Co-authored-by: Timothee Cour * Update lib/pure/os.nim Co-authored-by: Timothee Cour --- lib/pure/os.nim | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 6289c64f0..73e3fcb31 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -11,10 +11,11 @@ ## retrieving environment variables, reading command line arguments, ## working with directories, running shell commands, etc. -runnableExamples("-r:off"): +runnableExamples: let myFile = "/path/to/my/file.nim" assert splitPath(myFile) == (head: "/path/to/my", tail: "file.nim") - assert parentDir(myFile) == "/path/to/my" + when defined(posix): + assert parentDir(myFile) == "/path/to/my" assert splitFile(myFile) == (dir: "/path/to/my", name: "file", ext: ".nim") assert myFile.changeFileExt("c") == "/path/to/my/file.c" From cf0cf32d276002e850a87667fff62c4df12999d6 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 20 Jul 2021 13:13:52 -0700 Subject: [PATCH 2362/3768] make -d:nimFpRoundtrips work consistently in vm vs rt, fix #18400, etc (#18531) * compiler/vmhooks: add getVar to allow vmops with var params * addFloat vmops with var param * cgen now renders float32 literals in c backend using roundtrip float to string --- compiler/ast.nim | 4 +- compiler/ccgexprs.nim | 12 +-- compiler/jsgen.nim | 7 +- compiler/nim.cfg | 1 + compiler/rodutils.nim | 10 +- compiler/semfold.nim | 1 - compiler/vmgen.nim | 3 +- compiler/vmhooks.nim | 32 +++--- compiler/vmops.nim | 11 +++ lib/system.nim | 3 - lib/system/dollars.nim | 13 +-- lib/system/formatfloat.nim | 166 ++++++++++++++++++++++---------- lib/system/jssys.nim | 18 +--- lib/system/repr_v2.nim | 6 +- lib/system/strmantle.nim | 41 -------- tests/config.nims | 2 + tests/errmsgs/treportunused.nim | 14 ++- tests/float/nim.cfg | 1 - tests/float/tfloats.nim | 117 ++++++++++++++++++---- tests/stdlib/tjson.nim | 6 +- tests/stdlib/tjsonutils.nim | 17 ++-- 21 files changed, 286 insertions(+), 199 deletions(-) delete mode 100644 tests/float/nim.cfg diff --git a/compiler/ast.nim b/compiler/ast.nim index f1f73dcee..8b836e088 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -652,7 +652,7 @@ type mUnaryMinusI, mUnaryMinusI64, mAbsI, mNot, mUnaryPlusI, mBitnotI, mUnaryPlusF64, mUnaryMinusF64, - mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, + mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mCStrToStr, mStrToStr, mEnumToStr, mAnd, mOr, mImplies, mIff, mExists, mForall, mOld, @@ -720,7 +720,7 @@ const mEqRef, mEqProc, mLePtr, mLtPtr, mEqCString, mXor, mUnaryMinusI, mUnaryMinusI64, mAbsI, mNot, mUnaryPlusI, mBitnotI, mUnaryPlusF64, mUnaryMinusF64, - mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, + mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mCStrToStr, mStrToStr, mEnumToStr, mAnd, mOr, mEqStr, mLeStr, mLtStr, diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 09326ceeb..79c73b602 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -94,9 +94,12 @@ proc genLiteral(p: BProc, n: PNode, ty: PType): Rope = else: result = makeCString(n.strVal) of nkFloatLit, nkFloat64Lit: - result = rope(n.floatVal.toStrMaxPrecision) + if ty.kind == tyFloat32: + result = rope(n.floatVal.float32.toStrMaxPrecision) + else: + result = rope(n.floatVal.toStrMaxPrecision) of nkFloat32Lit: - result = rope(n.floatVal.toStrMaxPrecision("f")) + result = rope(n.floatVal.float32.toStrMaxPrecision) else: internalError(p.config, n.info, "genLiteral(" & $n.kind & ')') result = nil @@ -2300,11 +2303,6 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mInt64ToStr: genDollar(p, e, d, "#nimInt64ToStr($1)") of mBoolToStr: genDollar(p, e, d, "#nimBoolToStr($1)") of mCharToStr: genDollar(p, e, d, "#nimCharToStr($1)") - of mFloatToStr: - if e[1].typ.skipTypes(abstractInst).kind == tyFloat32: - genDollar(p, e, d, "#nimFloat32ToStr($1)") - else: - genDollar(p, e, d, "#nimFloatToStr($1)") of mCStrToStr: genDollar(p, e, d, "#cstrToNimstr($1)") of mStrToStr, mUnown: expr(p, e[1], d) of mIsolate: genCall(p, e, d) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 82fba02a6..4b89b0cde 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -434,7 +434,6 @@ const # magic checked op; magic unchecked op; mBoolToStr: ["nimBoolToStr", "nimBoolToStr"], mIntToStr: ["cstrToNimstr", "cstrToNimstr"], mInt64ToStr: ["cstrToNimstr", "cstrToNimstr"], - mFloatToStr: ["cstrToNimstr", "cstrToNimstr"], mCStrToStr: ["cstrToNimstr", "cstrToNimstr"], mStrToStr: ["", ""]] @@ -656,9 +655,6 @@ proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = of mBoolToStr: applyFormat("nimBoolToStr($1)", "nimBoolToStr($1)") of mIntToStr: applyFormat("cstrToNimstr(($1) + \"\")", "cstrToNimstr(($1) + \"\")") of mInt64ToStr: applyFormat("cstrToNimstr(($1) + \"\")", "cstrToNimstr(($1) + \"\")") - of mFloatToStr: - useMagic(p, "nimFloatToString") - applyFormat "cstrToNimstr(nimFloatToString($1))" of mCStrToStr: applyFormat("cstrToNimstr($1)", "cstrToNimstr($1)") of mStrToStr, mUnown, mIsolate: applyFormat("$1", "$1") else: @@ -682,8 +678,7 @@ proc arith(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = gen(p, n[1], x) gen(p, n[2], y) r.res = "($1 >>> $2)" % [x.rdLoc, y.rdLoc] - of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, - mCStrToStr, mStrToStr, mEnumToStr: + of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mCStrToStr, mStrToStr, mEnumToStr: arithAux(p, n, r, op) of mEqRef: if mapType(n[1].typ) != etyBaseIndex: diff --git a/compiler/nim.cfg b/compiler/nim.cfg index 9ecd00b0b..f10d847ac 100644 --- a/compiler/nim.cfg +++ b/compiler/nim.cfg @@ -4,6 +4,7 @@ hint:XDeclaredButNotUsed:off define:booting define:nimcore +define:nimFpRoundtrips #import:"$projectpath/testability" diff --git a/compiler/rodutils.nim b/compiler/rodutils.nim index e13b08e05..a4f7a5146 100644 --- a/compiler/rodutils.nim +++ b/compiler/rodutils.nim @@ -39,7 +39,10 @@ when not declared(signbit): proc signbit*(x: SomeFloat): bool {.inline.} = result = c_signbit(x) != 0 -proc toStrMaxPrecision*(f: BiggestFloat, literalPostfix = ""): string = +import system/formatfloat + +proc toStrMaxPrecision*(f: BiggestFloat | float32): string = + const literalPostfix = when f is float32: "f" else: "" case classify(f) of fcNan: if signbit(f): @@ -55,9 +58,8 @@ proc toStrMaxPrecision*(f: BiggestFloat, literalPostfix = ""): string = of fcNegInf: result = "-INF" else: - result = newString(81) - let n = c_snprintf(result.cstring, result.len.uint, "%#.16e%s", f, literalPostfix.cstring) - setLen(result, n) + result.addFloatRoundtrip(f) + result.add literalPostfix proc encodeStr*(s: string, result: var string) = for i in 0..", discardable.} - proc writeFloatToBuffer*(buf: var array[65, char]; value: BiggestFloat): int = - ## This is the implementation to format floats. - ## - ## returns the amount of bytes written to `buf` not counting the - ## terminating '\0' character. - result = toChars(buf, value, forceTrailingDotZero=true) - buf[result] = '\0' +proc addCstringN(result: var string, buf: cstring; buflen: int) = + # no nimvm support needed, so it doesn't need to be fast here either + let oldLen = result.len + let newLen = oldLen + buflen + result.setLen newLen + c_memcpy(result[oldLen].addr, buf, buflen.csize_t) -else: - proc c_sprintf(buf, frmt: cstring): cint {.header: "", - importc: "sprintf", varargs, noSideEffect.} +import dragonbox, schubfach - proc writeToBuffer(buf: var array[65, char]; value: cstring) = - var i = 0 - while value[i] != '\0': - buf[i] = value[i] - inc i +proc writeFloatToBufferRoundtrip*(buf: var array[65, char]; value: BiggestFloat): int = + ## This is the implementation to format floats. + ## + ## returns the amount of bytes written to `buf` not counting the + ## terminating '\0' character. + result = toChars(buf, value, forceTrailingDotZero=true) + buf[result] = '\0' - proc writeFloatToBuffer*(buf: var array[65, char]; value: BiggestFloat): int = - ## This is the implementation to format floats. - ## - ## returns the amount of bytes written to `buf` not counting the - ## terminating '\0' character. - var n: int = c_sprintf(addr buf, "%.16g", value) - var hasDot = false - for i in 0..n-1: - if buf[i] == ',': - buf[i] = '.' - hasDot = true - elif buf[i] in {'a'..'z', 'A'..'Z', '.'}: - hasDot = true - if not hasDot: - buf[n] = '.' - buf[n+1] = '0' - buf[n+2] = '\0' - result = n + 2 +proc writeFloatToBufferRoundtrip*(buf: var array[65, char]; value: float32): int = + result = float32ToChars(buf, value, forceTrailingDotZero=true) + buf[result] = '\0' + +proc c_sprintf(buf, frmt: cstring): cint {.header: "", + importc: "sprintf", varargs, noSideEffect.} + +proc writeToBuffer(buf: var array[65, char]; value: cstring) = + var i = 0 + while value[i] != '\0': + buf[i] = value[i] + inc i + +proc writeFloatToBufferSprintf*(buf: var array[65, char]; value: BiggestFloat): int = + ## This is the implementation to format floats. + ## + ## returns the amount of bytes written to `buf` not counting the + ## terminating '\0' character. + var n: int = c_sprintf(addr buf, "%.16g", value) + var hasDot = false + for i in 0..n-1: + if buf[i] == ',': + buf[i] = '.' + hasDot = true + elif buf[i] in {'a'..'z', 'A'..'Z', '.'}: + hasDot = true + if not hasDot: + buf[n] = '.' + buf[n+1] = '0' + buf[n+2] = '\0' + result = n + 2 + else: + result = n + # On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN' or 'nan(ind)' + # of '-1.#IND' are produced. + # We want to get rid of these here: + if buf[n-1] in {'n', 'N', 'D', 'd', ')'}: + writeToBuffer(buf, "nan") + result = 3 + elif buf[n-1] == 'F': + if buf[0] == '-': + writeToBuffer(buf, "-inf") + result = 4 else: - result = n - # On Windows nice numbers like '1.#INF', '-1.#INF' or '1.#NAN' or 'nan(ind)' - # of '-1.#IND' are produced. - # We want to get rid of these here: - if buf[n-1] in {'n', 'N', 'D', 'd', ')'}: - writeToBuffer(buf, "nan") + writeToBuffer(buf, "inf") result = 3 - elif buf[n-1] == 'F': - if buf[0] == '-': - writeToBuffer(buf, "-inf") - result = 4 - else: - writeToBuffer(buf, "inf") - result = 3 + +proc writeFloatToBuffer*(buf: var array[65, char]; value: BiggestFloat | float32): int {.inline.} = + when defined(nimFpRoundtrips): + writeFloatToBufferRoundtrip(buf, value) + else: + writeFloatToBufferSprintf(buf, value) + +proc addFloatRoundtrip*(result: var string; x: float | float32) = + when nimvm: + doAssert false + else: + var buffer {.noinit.}: array[65, char] + let n = writeFloatToBufferRoundtrip(buffer, x) + result.addCstringN(cstring(buffer[0].addr), n) + +proc addFloatSprintf*(result: var string; x: float) = + when nimvm: + doAssert false + else: + var buffer {.noinit.}: array[65, char] + let n = writeFloatToBufferSprintf(buffer, x) + result.addCstringN(cstring(buffer[0].addr), n) + +proc nimFloatToString(a: float): cstring = + ## ensures the result doesn't print like an integer, i.e. return 2.0, not 2 + # print `-0.0` properly + asm """ + function nimOnlyDigitsOrMinus(n) { + return n.toString().match(/^-?\d+$/); + } + if (Number.isSafeInteger(`a`)) + `result` = `a` === 0 && 1 / `a` < 0 ? "-0.0" : `a`+".0" + else { + `result` = `a`+"" + if(nimOnlyDigitsOrMinus(`result`)){ + `result` = `a`+".0" + } + } + """ + +proc addFloat*(result: var string; x: float | float32) {.inline.} = + ## Converts float to its string representation and appends it to `result`. + runnableExamples: + var + s = "foo:" + b = 45.67 + s.addFloat(45.67) + assert s == "foo:45.67" + template impl = + when defined(nimFpRoundtrips): + addFloatRoundtrip(result, x) + else: + addFloatSprintf(result, x) + when defined(js): + when nimvm: impl() + else: + result.add nimFloatToString(x) + else: impl() diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim index 7cb5652c5..b42dc3a20 100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -496,23 +496,6 @@ proc negInt(a: int): int {.compilerproc.} = proc negInt64(a: int64): int64 {.compilerproc.} = result = a*(-1) -proc nimFloatToString(a: float): cstring {.compilerproc.} = - ## ensures the result doesn't print like an integer, i.e. return 2.0, not 2 - # print `-0.0` properly - asm """ - function nimOnlyDigitsOrMinus(n) { - return n.toString().match(/^-?\d+$/); - } - if (Number.isSafeInteger(`a`)) - `result` = `a` === 0 && 1 / `a` < 0 ? "-0.0" : `a`+".0" - else { - `result` = `a`+"" - if(nimOnlyDigitsOrMinus(`result`)){ - `result` = `a`+".0" - } - } - """ - proc absInt(a: int): int {.compilerproc.} = result = if a < 0: a*(-1) else: a @@ -703,6 +686,7 @@ proc addChar(x: string, c: char) {.compilerproc, asmNoStackFrame.} = {.pop.} proc tenToThePowerOf(b: int): BiggestFloat = + # xxx deadcode var b = b var a = 10.0 result = 1.0 diff --git a/lib/system/repr_v2.nim b/lib/system/repr_v2.nim index 618cc2b40..8471ea148 100644 --- a/lib/system/repr_v2.nim +++ b/lib/system/repr_v2.nim @@ -19,9 +19,9 @@ proc repr*(x: uint64): string {.noSideEffect.} = ## converted to a decimal string. $x #Calls `$` from system/strmantle.nim -proc repr*(x: float): string {.magic: "FloatToStr", noSideEffect.} - ## repr for a float argument. Returns `x` - ## converted to a decimal string. +proc repr*(x: float): string = + ## Same as $x + $x proc repr*(x: bool): string {.magic: "BoolToStr", noSideEffect.} ## repr for a boolean argument. Returns `x` diff --git a/lib/system/strmantle.nim b/lib/system/strmantle.nim index 041272175..81ac5c751 100644 --- a/lib/system/strmantle.nim +++ b/lib/system/strmantle.nim @@ -69,47 +69,6 @@ proc nimIntToStr(x: int): string {.compilerRtl.} = result = newStringOfCap(sizeof(x)*4) result.addInt x -proc addCstringN(result: var string, buf: cstring; buflen: int) = - # no nimvm support needed, so it doesn't need to be fast here either - let oldLen = result.len - let newLen = oldLen + buflen - result.setLen newLen - copyMem(result[oldLen].addr, buf, buflen) - -import formatfloat - -proc addFloat*(result: var string; x: float) = - ## Converts float to its string representation and appends it to `result`. - ## - ## .. code-block:: Nim - ## var - ## a = "123" - ## b = 45.67 - ## a.addFloat(b) # a <- "12345.67" - when nimvm: - result.add $x - else: - var buffer {.noinit.}: array[65, char] - let n = writeFloatToBuffer(buffer, x) - result.addCstringN(cstring(buffer[0].addr), n) - -proc nimFloatToStr(f: float): string {.compilerproc.} = - result = newStringOfCap(8) - result.addFloat f - -when defined(nimFpRoundtrips) and not defined(nimscript) and - not defined(js) and defined(nimHasDragonBox): - import schubfach - -proc nimFloat32ToStr(f: float32): string {.compilerproc.} = - when declared(float32ToChars): - result = newString(65) - let L = float32ToChars(result, f, forceTrailingDotZero=true) - setLen(result, L) - else: - result = newStringOfCap(8) - result.addFloat f - proc c_strtod(buf: cstring, endptr: ptr cstring): float64 {. importc: "strtod", header: "", noSideEffect.} diff --git a/tests/config.nims b/tests/config.nims index 539de5e8d..12b303318 100644 --- a/tests/config.nims +++ b/tests/config.nims @@ -34,3 +34,5 @@ hint("Processing", off) switch("define", "nimExperimentalAsyncjsThen") switch("define", "nimExperimentalJsfetch") switch("define", "nimExperimentalLinenoiseExtra") + +switch("define", "nimFpRoundtrips") diff --git a/tests/errmsgs/treportunused.nim b/tests/errmsgs/treportunused.nim index f9b7c3d11..3105b35ea 100644 --- a/tests/errmsgs/treportunused.nim +++ b/tests/errmsgs/treportunused.nim @@ -13,12 +13,12 @@ treportunused.nim(30, 5) Hint: 's8' is declared but not used [XDeclaredButNotUse treportunused.nim(31, 5) Hint: 's9' is declared but not used [XDeclaredButNotUsed] treportunused.nim(32, 6) Hint: 's10' is declared but not used [XDeclaredButNotUsed] treportunused.nim(33, 6) Hint: 's11' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(37, 3) Hint: 'v0.99' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(38, 3) Hint: 'v0.99.99' is declared but not used [XDeclaredButNotUsed] ''' action: compile """ -#treportunused.nim(37, 3) Hint: 'v0.99' is declared but not used [XDeclaredButNotUsed] -#treportunused.nim(38, 3) Hint: 'v0.99.99' is declared but not used [XDeclaredButNotUsed] # bug #9764 iterator s1(a:string): int = discard iterator s2(): int = discard @@ -32,9 +32,7 @@ var s9: int type s10 = object type s11 = type(1.2) -when false: - # enabled again when Nim bootstraps with -d:nimFpRoundtrips - # https://github.com/nim-lang/Nim/issues/14407 - let - `v0.99` = "0.99" - `v0.99.99` = "0.99.99" +# bug #14407 (requires `compiler/nim.cfg` containing define:nimFpRoundtrips) +let + `v0.99` = "0.99" + `v0.99.99` = "0.99.99" diff --git a/tests/float/nim.cfg b/tests/float/nim.cfg deleted file mode 100644 index d27bbf43b..000000000 --- a/tests/float/nim.cfg +++ /dev/null @@ -1 +0,0 @@ --d:nimFpRoundtrips diff --git a/tests/float/tfloats.nim b/tests/float/tfloats.nim index 30d9c50d6..63987bb8d 100644 --- a/tests/float/tfloats.nim +++ b/tests/float/tfloats.nim @@ -1,13 +1,14 @@ discard """ + matrix: "-d:nimFpRoundtrips; -u:nimFpRoundtrips" targets: "c cpp js" """ -# disabled: "windows" #[ xxx merge all or most float tests into this file ]# import std/[fenv, math, strutils] +import stdtest/testutils proc equalsOrNaNs(a, b: float): bool = if isNaN(a): isNaN(b) @@ -62,27 +63,103 @@ template main = reject "1_.0" reject "1.0_" - block: # bug #18148 - var a = 1.1'f32 - doAssert $a == "1.1", $a # was failing + block: # bugs mentioned in https://github.com/nim-lang/Nim/pull/18504#issuecomment-881635317 + block: # example 1 + let a = 0.1+0.2 + doAssert a != 0.3 + when defined(nimFpRoundtrips): + doAssert $a == "0.30000000000000004" + else: + whenRuntimeJs: discard + do: doAssert $a == "0.3" + block: # example 2 + const a = 0.1+0.2 + when defined(nimFpRoundtrips): + doAssert $($a, a) == """("0.30000000000000004", 0.30000000000000004)""" + else: + whenRuntimeJs: discard + do: doAssert $($a, a) == """("0.3", 0.3)""" + block: # example 3 + const a1 = 0.1+0.2 + let a2 = a1 + doAssert a1 != 0.3 + when defined(nimFpRoundtrips): + doAssert $[$a1, $a2] == """["0.30000000000000004", "0.30000000000000004"]""" + else: + whenRuntimeJs: discard + do: doAssert $[$a1, $a2] == """["0.3", "0.3"]""" -proc runtimeOnlyTests = - # enable for 'static' once -d:nimFpRoundtrips became the default - block: # bug #7717 - proc test(f: float) = - let f2 = $f - let f3 = parseFloat(f2) - doAssert equalsOrNaNs(f, f3), $(f, f2, f3) + when defined(nimFpRoundtrips): + block: # bug #18148 + var a = 1.1'f32 + doAssert $a == "1.1", $a # was failing - test 1.0 + epsilon(float64) - test 1000000.0000000123 - test log2(100000.0) - test maximumPositiveValue(float32) - test maximumPositiveValue(float64) - test minimumPositiveValue(float32) - test minimumPositiveValue(float64) + block: # bug #18400 + block: + let a1 = 0.1'f32 + let a2 = 0.2'f32 + let a3 = a1 + a2 + var s = "" + s.addFloat(a3) + whenVMorJs: discard # xxx refs #12884 + do: + doAssert a3 == 0.3'f32 + doAssert $a3 == "0.3" + + block: + let a1 = 0.1 + let a2 = 0.2 + let a3 = a1 + a2 + var s = "" + s.addFloat(a3) + doAssert a3 != 0.3 + doAssert $a3 == "0.30000000000000004" + + block: + var s = [-13.888888'f32] + whenRuntimeJs: discard + do: + doAssert $s == "[-13.888888]" + doAssert $s[0] == "-13.888888" + + block: # bug #7717 + proc test(f: float) = + let f2 = $f + let f3 = parseFloat(f2) + doAssert equalsOrNaNs(f, f3), $(f, f2, f3) + test 1.0 + epsilon(float64) + test 1000000.0000000123 + test log2(100000.0) + test maximumPositiveValue(float32) + test maximumPositiveValue(float64) + test minimumPositiveValue(float32) + test minimumPositiveValue(float64) + + block: # bug #12884 + block: # example 1 + const x0: float32 = 1.32 + let x1 = 1.32 + let x2 = 1.32'f32 + var x3: float32 = 1.32 + doAssert $(x0, x1, x2, x3) == "(1.32, 1.32, 1.32, 1.32)" + block: # example https://github.com/nim-lang/Nim/issues/12884#issuecomment-564967962 + let x = float(1.32'f32) + when nimvm: discard # xxx prints 1.3 + else: + when not defined(js): + doAssert $x == "1.3200000524520874" + doAssert $1.32 == "1.32" + doAssert $1.32'f32 == "1.32" + let x2 = 1.32'f32 + doAssert $x2 == "1.32" + block: + var x = 1.23456789012345'f32 + when nimvm: + discard # xxx, refs #12884 + else: + when not defined(js): + doAssert x == 1.2345679'f32 + doAssert $x == "1.2345679" static: main() main() - -runtimeOnlyTests() diff --git a/tests/stdlib/tjson.nim b/tests/stdlib/tjson.nim index 000f72038..289ef9d05 100644 --- a/tests/stdlib/tjson.nim +++ b/tests/stdlib/tjson.nim @@ -303,7 +303,7 @@ let jsonNode = %*mynode doAssert $jsonNode == """{"kind":"P","pChildren":[{"kind":"Text","textStr":"mychild"},{"kind":"Br"}]}""" doAssert $jsonNode.to(ContentNode) == """(kind: P, pChildren: @[(kind: Text, textStr: "mychild"), (kind: Br)])""" -when defined(nimFpRoundtrips): # bug #17383 +block: # bug #17383 testRoundtrip(int32.high): "2147483647" testRoundtrip(uint32.high): "4294967295" when int.sizeof == 4: @@ -316,7 +316,7 @@ when defined(nimFpRoundtrips): # bug #17383 testRoundtrip(int64.high): "9223372036854775807" testRoundtrip(uint64.high): "18446744073709551615" -when defined(nimFpRoundtrips): # bug #18007 +block: # bug #18007 testRoundtrip([NaN, Inf, -Inf, 0.0, -0.0, 1.0]): """["nan","inf","-inf",0.0,-0.0,1.0]""" # pending https://github.com/nim-lang/Nim/issues/18025 use: # testRoundtrip([float32(NaN), Inf, -Inf, 0.0, -0.0, 1.0]) @@ -332,7 +332,7 @@ when defined(nimFpRoundtrips): # bug #18007 testRoundtripVal(0.0): "0.0" testRoundtripVal(-0.0): "-0.0" -when defined(nimFpRoundtrips): # bug #15397, bug #13196 +block: # bug #15397, bug #13196 testRoundtripVal(1.0 + epsilon(float64)): "1.0000000000000002" testRoundtripVal(0.12345678901234567890123456789): "0.12345678901234568" diff --git a/tests/stdlib/tjsonutils.nim b/tests/stdlib/tjsonutils.nim index 3e9c422e0..205160471 100644 --- a/tests/stdlib/tjsonutils.nim +++ b/tests/stdlib/tjsonutils.nim @@ -161,16 +161,15 @@ template fn() = doAssert b[2].signbit doAssert not b[3].signbit - when defined(nimFpRoundtrips): - block: # bug #15397, bug #13196 - let a = 0.1 - let x = 0.12345678901234567890123456789 - let b = (a + 0.2, 0.3, x) - testRoundtripVal(b): "[0.30000000000000004,0.3,0.12345678901234568]" + block: # bug #15397, bug #13196 + let a = 0.1 + let x = 0.12345678901234567890123456789 + let b = (a + 0.2, 0.3, x) + testRoundtripVal(b): "[0.30000000000000004,0.3,0.12345678901234568]" - testRoundtripVal(0.12345678901234567890123456789): "0.12345678901234568" - testRoundtripVal(epsilon(float64)): "2.220446049250313e-16" - testRoundtripVal(1.0 + epsilon(float64)): "1.0000000000000002" + testRoundtripVal(0.12345678901234567890123456789): "0.12345678901234568" + testRoundtripVal(epsilon(float64)): "2.220446049250313e-16" + testRoundtripVal(1.0 + epsilon(float64)): "1.0000000000000002" block: # case object type Foo = object From f8519657c43f458db9c915cec62c59022041eb05 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 20 Jul 2021 22:15:06 +0200 Subject: [PATCH 2363/3768] fixes #18469 (#18544) * fixes #18469 * Update compiler/injectdestructors.nim --- compiler/injectdestructors.nim | 25 ++++++++++++--- compiler/jsgen.nim | 10 ------ compiler/types.nim | 10 ++++++ tests/arc/tcursor_field_obj_constr.nim | 44 ++++++++++++++++++++++++++ 4 files changed, 74 insertions(+), 15 deletions(-) create mode 100644 tests/arc/tcursor_field_obj_constr.nim diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 6e4eaa817..cf09b3ff5 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -774,7 +774,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode = result = passCopyToSink(n, c, s) else: case n.kind - of nkBracket, nkObjConstr, nkTupleConstr, nkClosure, nkCurly: + of nkBracket, nkTupleConstr, nkClosure, nkCurly: # Let C(x) be the construction, 'x' the vector of arguments. # C(x) either owns 'x' or it doesn't. # If C(x) owns its data, we must consume C(x). @@ -785,13 +785,11 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode = # don't destroy it" # but if C(x) is a ref it MUST own its data since we must destroy it # so then we have no choice but to use 'sinkArg'. - let isRefConstr = n.kind == nkObjConstr and n.typ.skipTypes(abstractInst).kind == tyRef - let m = if isRefConstr: sinkArg - elif mode == normal: normal + let m = if mode == normal: normal else: sinkArg result = copyTree(n) - for i in ord(n.kind in {nkObjConstr, nkClosure}).. Date: Wed, 21 Jul 2021 08:51:03 +0200 Subject: [PATCH 2364/3768] =?UTF-8?q?undo=20RFC=20#294,=20it=20breaks=20co?= =?UTF-8?q?de=20for=20no=20good=20reason,=20the=20compiler=20can=20wa?= =?UTF-8?q?=E2=80=A6=20(#18546)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * undo RFC #294, it breaks code for no good reason, the compiler can warn about the construct instead * Update changelog.md Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> * enable test case Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> --- changelog.md | 9 ++++----- compiler/lineinfos.nim | 2 ++ compiler/semexprs.nim | 4 +--- compiler/types.nim | 2 -- tests/misc/tconv.nim | 8 +++----- 5 files changed, 10 insertions(+), 15 deletions(-) diff --git a/changelog.md b/changelog.md index a633c1fb3..1a904d043 100644 --- a/changelog.md +++ b/changelog.md @@ -23,15 +23,14 @@ - `repr` now doesn't insert trailing newline; previous behavior was very inconsistent, see #16034. Use `-d:nimLegacyReprWithNewline` for previous behavior. -- An enum now can't be converted to another enum directly, you must use `ord` (or `cast`, but - compiler won't help if you misuse it). +- A type conversion from one enum type to another now produces an `[EnumConv]` warning. + You should use `ord` (or `cast`, but the compiler won't help, if you misuse it) instead. ``` type A = enum a1, a2 type B = enum b1, b2 - doAssert not compiles(a1.B) - doAssert compiles(a1.ord.B) + echo a1.B # produces a warning + echo a1.ord.B # produces no warning ``` - for a transition period, use `-d:nimLegacyConvEnumEnum`. - Type mismatch errors now show more context, use `-d:nimLegacyTypeMismatch` for previous behavior. diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index 8ab52a452..a3243d5fc 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -70,6 +70,7 @@ type warnResultUsed = "ResultUsed", warnCannotOpen = "CannotOpen", warnFileChanged = "FileChanged", + warnSuspiciousEnumConv = "EnumConv", warnUser = "User", # hints hintSuccess = "Success", hintSuccessX = "SuccessX", @@ -153,6 +154,7 @@ const warnResultUsed: "used 'result' variable", warnCannotOpen: "cannot open: $1", warnFileChanged: "file changed: $1", + warnSuspiciousEnumConv: "$1", warnUser: "$1", hintSuccess: "operation successful: $#", # keep in sync with `testament.isSuccess` diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index f46d44bf0..c25a4067f 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -168,9 +168,7 @@ proc checkConvertible(c: PContext, targetTyp: PType, src: PNode): TConvStatus = elif (targetBaseTyp.kind in IntegralTypes) and (srcBaseTyp.kind in IntegralTypes): if targetTyp.kind == tyEnum and srcBaseTyp.kind == tyEnum: - if c.config.isDefined("nimLegacyConvEnumEnum"): - message(c.config, src.info, warnUser, "enum to enum conversion is now deprecated") - else: result = convNotLegal + message(c.config, src.info, warnSuspiciousEnumConv, "suspicious code: enum to enum conversion") # `elif` would be incorrect here if targetTyp.kind == tyBool: discard "convOk" diff --git a/compiler/types.nim b/compiler/types.nim index 0a50af56e..798f872c2 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -1598,8 +1598,6 @@ proc typeMismatch*(conf: ConfigRef; info: TLineInfo, formal, actual: PType, n: P of efLockLevelsDiffer: msg.add "\nlock levels differ" - if formal.kind == tyEnum and actual.kind == tyEnum: - msg.add "\nmaybe use `-d:nimLegacyConvEnumEnum` for a transition period" localError(conf, info, msg) proc isTupleRecursive(t: PType, cycleDetector: var IntSet): bool = diff --git a/tests/misc/tconv.nim b/tests/misc/tconv.nim index c93fc57f8..94677b1bf 100644 --- a/tests/misc/tconv.nim +++ b/tests/misc/tconv.nim @@ -1,7 +1,5 @@ discard """ - nimout:''' -tconv.nim(81, 15) Warning: enum to enum conversion is now deprecated [User] -''' + matrix: "--warningAsError:EnumConv" """ template reject(x) = @@ -77,12 +75,12 @@ block: # https://github.com/nim-lang/RFCs/issues/294 reject: k2.Goo reject: k2.string - {.define(nimLegacyConvEnumEnum).} + {.push warningAsError[EnumConv]:off.} discard Goo(k2) accept: Goo(k2) accept: k2.Goo reject: k2.string - {.undef(nimLegacyConvEnumEnum).} + {.pop.} reject: Goo(k2) reject: k2.Goo From b6f9f7a33eb550a75350c2bce8423269734bb185 Mon Sep 17 00:00:00 2001 From: Araq Date: Wed, 21 Jul 2021 09:09:43 +0200 Subject: [PATCH 2365/3768] atlas: minor changes --- tools/atlas/atlas.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/atlas/atlas.nim b/tools/atlas/atlas.nim index 371a1c13f..b10ec0358 100644 --- a/tools/atlas/atlas.nim +++ b/tools/atlas/atlas.nim @@ -14,7 +14,7 @@ import parse_requires, osutils, packagesjson const Version = "0.1" - Usage = "atlas - Nim Package Manager Version " & Version & """ + Usage = "atlas - Nim Package Cloner Version " & Version & """ (c) 2021 Andreas Rumpf Usage: @@ -170,7 +170,7 @@ proc toName(p: string): PackageName = else: result = PackageName p -proc needsCommitLookup(commit: string): bool {.inline} = +proc needsCommitLookup(commit: string): bool {.inline.} = '.' in commit or commit == InvalidCommit proc isShortCommitHash(commit: string): bool {.inline.} = From 9c2442af94fc3db8486ee16e82ea27752e9ae4f5 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 21 Jul 2021 09:46:18 +0200 Subject: [PATCH 2366/3768] unary slices get a deprecation period (#18549) --- changelog.md | 3 +-- lib/system.nim | 17 ++++++++--------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/changelog.md b/changelog.md index 1a904d043..ece88f710 100644 --- a/changelog.md +++ b/changelog.md @@ -63,8 +63,7 @@ - `hashes.hash(proc|ptr|ref|pointer)` now calls `hash(int)` and honors `-d:nimIntHash1`, `hashes.hash(closure)` has also been improved. -- The unary slice `..b` was removed, use `0..b` instead or use `-d:nimLegacyUnarySlice` - for a deprecation period. +- The unary slice `..b` was deprecated, use `0..b` instead. - Removed `.travis.yml`, `appveyor.yml.disabled`, `.github/workflows/ci.yml.disabled`. diff --git a/lib/system.nim b/lib/system.nim index 1da1444d2..e6a2439df 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -511,15 +511,14 @@ proc `..`*[T, U](a: sink T, b: sink U): HSlice[T, U] {.noSideEffect, inline, mag ## echo a[2 .. 3] # @[30, 40] result = HSlice[T, U](a: a, b: b) -when defined(nimLegacyUnarySlice): - proc `..`*[T](b: sink T): HSlice[int, T] - {.noSideEffect, inline, magic: "DotDot", deprecated: "replace `..b` with `0..b`".} = - ## Unary `slice`:idx: operator that constructs an interval `[default(int), b]`. - ## - ## .. code-block:: Nim - ## let a = [10, 20, 30, 40, 50] - ## echo a[.. 2] # @[10, 20, 30] - result = HSlice[int, T](a: 0, b: b) +proc `..`*[T](b: sink T): HSlice[int, T] + {.noSideEffect, inline, magic: "DotDot", deprecated: "replace `..b` with `0..b`".} = + ## Unary `slice`:idx: operator that constructs an interval `[default(int), b]`. + ## + ## .. code-block:: Nim + ## let a = [10, 20, 30, 40, 50] + ## echo a[.. 2] # @[10, 20, 30] + result = HSlice[int, T](a: 0, b: b) when defined(hotCodeReloading): {.pragma: hcrInline, inline.} From f048dad7c3af7d70fbb852a59cb5bdda332e96d7 Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 21 Jul 2021 15:47:06 +0800 Subject: [PATCH 2367/3768] add testcase for #6499 #12229 #7172 (#18547) --- tests/magics/tmagics.nim | 8 ++++++++ tests/threads/t7172.nim | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100644 tests/threads/t7172.nim diff --git a/tests/magics/tmagics.nim b/tests/magics/tmagics.nim index fa138320c..70926a0d7 100644 --- a/tests/magics/tmagics.nim +++ b/tests/magics/tmagics.nim @@ -55,3 +55,11 @@ block t9442: GC_unref(v2) GC_ref(v3) GC_unref(v3) + +block: # bug #6499 + let x = (chr, 0) + doAssert x[1] == 0 + +block: # bug #12229 + proc foo(T: typedesc) = discard + foo(ref) diff --git a/tests/threads/t7172.nim b/tests/threads/t7172.nim new file mode 100644 index 000000000..983765dba --- /dev/null +++ b/tests/threads/t7172.nim @@ -0,0 +1,32 @@ +discard """ + output: ''' +In doStuff() +In initProcess() +initProcess() done +TEST +Crashes before getting here! +''' + joinable: false +""" + +import std/os + +proc whatever() {.thread, nimcall.} = + echo("TEST") + +proc initProcess(): void = + echo("In initProcess()") + var thread: Thread[void] + createThread(thread, whatever) + echo("initProcess() done") + joinThread(thread) + +proc doStuff(): void = + echo("In doStuff()") + # ... + initProcess() + sleep(500) + # ... + echo("Crashes before getting here!") + +doStuff() From f86a530214d56f39664d6aaca3ddddf3b2fdba0c Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 21 Jul 2021 10:22:30 +0200 Subject: [PATCH 2368/3768] breaking changes policies (#18541) * document some std library evolution policies In wanting to improve the standard library, it's helpful to have a set of principles and guidelines to lean back on, that show how to introduce such improvements while at the same time considering existing users of the language. A key idea behind documentation of this sort is that it should highlight a path forwards in making changes rather than trying to prevent them, and although the current snippet does include some language for what one shouldn't do, it also shows a few options for what one can do. This is a riff on https://github.com/nim-lang/Nim/pull/18468 mainly based on what helps enable to the use of Nim productively in environments and teams where the priority and focus is not always on the tool (Nim in this case) but rather on the codebase itself, and its use by end users. We use similar guidance documentation to help coordinate the code of the teams using Nim in https://status-im.github.io/nim-style-guide/ where it is applied not as law, but rather as recommendations for the default approach that should be considered first. * document the new policies * typo * Update doc/contributing.rst Co-authored-by: Timothee Cour * Update doc/contributing.rst * Update doc/contributing.rst Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> * Update doc/contributing.rst Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> * Update doc/contributing.rst * Update doc/contributing.rst Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> * clarify some things * Update doc/contributing.rst Co-authored-by: Dominik Picheta Co-authored-by: Jacek Sieka Co-authored-by: Timothee Cour Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Co-authored-by: Dominik Picheta --- doc/contributing.rst | 114 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) diff --git a/doc/contributing.rst b/doc/contributing.rst index 5a31d85bd..07fa01714 100644 --- a/doc/contributing.rst +++ b/doc/contributing.rst @@ -671,3 +671,117 @@ Conventions Furthermore, module names should use `snake_case` and not use capital letters, which cause issues when going from an OS without case sensitivity to an OS with it. + + +Breaking Changes +================ + +Introducing breaking changes, no matter how well intentioned, +creates long-term problems for the community, in particular those looking to promote +reusable Nim code in libraries: In the Nim distribution, critical security and bugfixes, +language changes and community improvements are bundled in a single distribution - it is +difficult to make partial upgrades with only benign changes. When one library depends on +a legacy behavior, it can no longer be used together with another library that does not, +breaking all downstream applications - the standard library is unique in that it sits at +the root of **all** dependency trees. + +There is a big difference between compile-time breaking changes and run-time breaking +changes. + + +Run-time breaking changes +------------------------- + +Run-time breaking changes are to be avoided at almost all costs: Nim is used for +mission critical applications which depend on behaviours that +are not covered by the test suite. As such, it's important that changes to the +*stable* parts of the standard library are made avoiding changing the existing +behaviors, even when the test suite continues to pass. + +Examples of run-time breaking changes: + +- Raising exceptions of a new type, compared to what's currently being raised. + +- Adding unconstrained or poorly constrained generic procs or macros + ("hash now works for all `ref T`"): This may cause code to behave differently + depending only on which modules are imported - common examples include `==` and `hash`. + +- Changing behavior of existing functions like: + + * "Nim's path handling procs like `getXDir` now consistently lack the trailing slash" + * "Nim's strformat implementation is now more consistent with Python" + +Instead write new code that explicitly announces the feature you think we announced but +didn't. For example, `strformat` does not say "it's compatible with Python", it +says "inspired by Python's f-strings". This new code can be submitted to the stdlib +and the old code can be deprecated or it can be published as a Nimble package. + +Sometimes, a run-time breaking change is most desirable: For example, a string +representation of a floating point number that "roundtrips" is much better than +a string represenation that doesn't. These run-time breaking changes must start in the +state "opt-in" via a new `-d:nimPreviewX` or command line flag and then should become +the new default later, in follow-up versions. This way users can track +regressions more easily. ("git bisect" is not an acceptable alternative, that's for +Nim compiler developers, not for Nim users.) + +Above all else, additive approaches that don't change existing behaviors +should be preferred. + + +Compile-time breaking changes +----------------------------- + +Compile-time breaking changes are usually easier to handle, but for large code bases +it can also be much work and it can hinder the adoption of a new Nim release. +Additive approaches are to be preferred here as well. + +Examples of compile-time breaking changes include (but are not limited to): + +* Renaming functions and modules, or moving things. Instead of a direct rename, + deprecate the old name and introduce a new one. +* Renaming the parameter names: Thanks to Nim's "named parameter" calling syntax + like `f(x = 0, y = 1)` this is a breaking change. Instead live with the existing + parameter names. +* Adding an enum value to an existing enum. Nim's exhaustive case statements stop + compiling after such a change. Instead consider to introduce new `bool` + fields/parameters. This can be impractical though, so we use good judgement + and our list of "important packages" to see if it doesn't break too much code + out there in practice. +* Adding a new proc to an existing stdlib module. However, for aesthetic reasons + this is often preferred over introducing a new module with just a single proc + inside. We use good judgement and our list of "important packages" to see if + it doesn't break too much code out there in practice. The new procs need to + be annotated with a `.since` annotation. + + +Compiler/language spec bugfixes +------------------------------- + +This can even be applied to compiler "bugfixes": If the compiler should have been +"pickier" in its handling of `typedesc`, instead of "fixing typedesc handling bugs", +consider the following solution: + +- Spec out how `typedesc` should really work and also spec out the cases where it + should not be allowed! +- Deprecate `typedesc` and name the new metatype something new like `typeArg`. +- Implement the spec. + + +Non-breaking changes +-------------------- + +Examples of changes that are considered non-breaking (or acceptable breaking changes) include: + +* Creating a new module. +* Adding an overload to an already overloaded proc. +* Adding new default parameters to an existing proc. It is assumed that you do not + use Nim's stdlib procs's addresses (that you don't use them as first class entities). +* Changing the calling convention from `nimcall` to `inline` + (but first RFC https://github.com/nim-lang/RFCs/issues/396 needs to be implemented). +* Changing the behavior from "crashing" into some other, well documented result (including + raising a Defect, but not raising an exception that does not inherit from Defect). +* Adding new fields to an existing object. + +Nim's introspection facilities imply that strictly speaking almost every addition can +break somebody's code. It is impractical to care about these cases, a change that only +affects introspection is not considered to be a breaking change. From 6f34829ee5a060300859002fd41f0181c0adf754 Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 21 Jul 2021 19:46:17 +0800 Subject: [PATCH 2369/3768] clean unused imports on runnableExamples (#18551) --- lib/std/monotimes.nim | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/std/monotimes.nim b/lib/std/monotimes.nim index 3bb4fa141..bd078d7c0 100644 --- a/lib/std/monotimes.nim +++ b/lib/std/monotimes.nim @@ -15,8 +15,6 @@ meaning that that the following is guaranteed to work: ]## runnableExamples: - import std/os - let a = getMonoTime() let b = getMonoTime() assert a <= b From 880353c0ad9300f2543c1b16548b4f2ab6784183 Mon Sep 17 00:00:00 2001 From: IterableTrucks <42210451+IterableTrucks@users.noreply.github.com> Date: Wed, 21 Jul 2021 20:13:50 +0800 Subject: [PATCH 2370/3768] complement notes of two doc strings of tables module (#18527) * complement notes of two doc strings warning the deprecated procedure's side effect * Update lib/pure/collections/tables.nim Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> * Update lib/pure/collections/tables.nim Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Co-authored-by: BillyZee Co-authored-by: Andreas Rumpf Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> --- lib/pure/collections/tables.nim | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index d617e06e4..b845f57cc 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -496,6 +496,8 @@ template tabCellHash(i) = t.data[i].hcode proc del*[A, B](t: var Table[A, B], key: A) = ## Deletes `key` from hash table `t`. Does nothing if the key does not exist. ## + ## .. warning:: If duplicate keys were added, this may need to be called multiple times. + ## ## See also: ## * `pop proc<#pop,Table[A,B],A,B>`_ ## * `clear proc<#clear,Table[A,B]>`_ to empty the whole table @@ -514,6 +516,8 @@ proc pop*[A, B](t: var Table[A, B], key: A, val: var B): bool = ## mapping of the key. Otherwise, returns `false`, and the `val` is ## unchanged. ## + ## .. warning:: If duplicate keys were added, this may need to be called multiple times. + ## ## See also: ## * `del proc<#del,Table[A,B],A>`_ ## * `clear proc<#clear,Table[A,B]>`_ to empty the whole table From 58080525a1dcad23de6ad0cf9ca6988f103de350 Mon Sep 17 00:00:00 2001 From: itsumura-h <39766805+itsumura-h@users.noreply.github.com> Date: Wed, 21 Jul 2021 22:03:48 +0900 Subject: [PATCH 2371/3768] add [1..2] for JArray (#18525) * add [1..2] for JArray * fix BackwardsIndex to int * fix for BackwardsIndex * fix for assert node kind check * fix variable name * Update lib/pure/json.nim * fix for when x.a is BackwardsIndex Co-authored-by: itsumura-h Co-authored-by: Dominik Picheta --- lib/pure/json.nim | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/lib/pure/json.nim b/lib/pure/json.nim index d5bbb86b8..85c3393b2 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -531,6 +531,24 @@ proc `[]`*(node: JsonNode, index: BackwardsIndex): JsonNode {.inline, since: (1, `[]`(node, node.len - int(index)) +proc `[]`*[U, V](a: JsonNode, x: HSlice[U, V]): JsonNode = + ## Slice operation for JArray. + ## + ## Returns the inclusive range `[a[x.a], a[x.b]]`: + runnableExamples: + import json + let arr = %[0,1,2,3,4,5] + doAssert arr[2..4] == %[2,3,4] + doAssert arr[2..^2] == %[2,3,4] + doAssert arr[^4..^2] == %[2,3,4] + + assert(a.kind == JArray) + result = newJArray() + let xa = (when x.a is BackwardsIndex: a.len - int(x.a) else: int(x.a)) + let L = (when x.b is BackwardsIndex: a.len - int(x.b) else: int(x.b)) - xa + 1 + for i in 0.. Date: Wed, 21 Jul 2021 16:55:50 +0200 Subject: [PATCH 2372/3768] fixes #18550 (#18553) * fixes #18550 * update the manual to reflect reality --- changelog.md | 4 ++++ compiler/lineinfos.nim | 2 ++ compiler/sempass2.nim | 16 ++++++++++++++++ compiler/sighashes.nim | 4 +++- compiler/types.nim | 15 +++++++++++++++ doc/manual.rst | 7 +++---- lib/system/io.nim | 8 ++++---- lib/system/widestrs.nim | 2 +- tests/misc/tconv.nim | 10 +++++++++- 9 files changed, 57 insertions(+), 11 deletions(-) diff --git a/changelog.md b/changelog.md index ece88f710..f2a9ca914 100644 --- a/changelog.md +++ b/changelog.md @@ -32,6 +32,10 @@ echo a1.ord.B # produces no warning ``` +- A dangerous implicit conversion to `cstring` now triggers a `[CStringConv]` warning. + This warning will become an error in future versions! Use an explicit conversion + like `cstring(x)` in order to silence the warning. + - Type mismatch errors now show more context, use `-d:nimLegacyTypeMismatch` for previous behavior. diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index a3243d5fc..3f31ceb97 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -71,6 +71,7 @@ type warnCannotOpen = "CannotOpen", warnFileChanged = "FileChanged", warnSuspiciousEnumConv = "EnumConv", + warnCstringConv = "CStringConv", warnUser = "User", # hints hintSuccess = "Success", hintSuccessX = "SuccessX", @@ -155,6 +156,7 @@ const warnCannotOpen: "cannot open: $1", warnFileChanged: "file changed: $1", warnSuspiciousEnumConv: "$1", + warnCstringConv: "$1", warnUser: "$1", hintSuccess: "operation successful: $#", # keep in sync with `testament.isSuccess` diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 1e1672cad..ce21c3ed2 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -951,6 +951,15 @@ proc trackInnerProc(tracked: PEffects, n: PNode) = else: for ch in n: trackInnerProc(tracked, ch) +proc allowCStringConv(n: PNode): bool = + case n.kind + of nkStrLit..nkTripleStrLit: result = true + of nkSym: result = n.sym.kind in {skConst, skParam} + of nkAddr: result = isCharArrayPtr(n.typ, true) + of nkCallKinds: + result = isCharArrayPtr(n.typ, n[0].kind == nkSym and n[0].sym.magic == mAddr) + else: result = isCharArrayPtr(n.typ, false) + proc track(tracked: PEffects, n: PNode) = case n.kind of nkSym: @@ -1157,6 +1166,13 @@ proc track(tracked: PEffects, n: PNode) = if tracked.owner.kind != skMacro: createTypeBoundOps(tracked, n.typ, n.info) of nkHiddenStdConv, nkHiddenSubConv, nkConv: + if n.kind in {nkHiddenStdConv, nkHiddenSubConv} and + n.typ.skipTypes(abstractInst).kind == tyCstring and + not allowCStringConv(n[1]): + message(tracked.config, n.info, warnCstringConv, + "implicit conversion to 'cstring' from a non-const location: $1; this will become a compile time error in the future" % + [$n[1]]) + if n.len == 2: track(tracked, n[1]) if tracked.owner.kind != skMacro: diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim index f6df422e6..0ff97017f 100644 --- a/compiler/sighashes.nim +++ b/compiler/sighashes.nim @@ -14,7 +14,9 @@ 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) = + # XXX suspicious code here; relies on ch being zero terminated? + md5Update(c, unsafeAddr ch, 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/types.nim b/compiler/types.nim index 798f872c2..f0bd99a17 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -1664,3 +1664,18 @@ proc lookupFieldAgain*(ty: PType; field: PSym): PSym = if result != nil: break ty = ty[0] if result == nil: result = field + +proc isCharArrayPtr*(t: PType; allowPointerToChar: bool): bool = + let t = t.skipTypes(abstractInst) + if t.kind == tyPtr: + let pointsTo = t[0].skipTypes(abstractInst) + case pointsTo.kind + of tyUncheckedArray: + result = pointsTo[0].kind == tyChar + of tyArray: + result = pointsTo[1].kind == tyChar and firstOrd(nil, pointsTo[0]) == 0 and + skipTypes(pointsTo[0], {tyRange}).kind in {tyInt..tyInt64} + of tyChar: + result = allowPointerToChar + else: + discard diff --git a/doc/manual.rst b/doc/manual.rst index f0bd85638..9f614c2cd 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -1380,10 +1380,9 @@ variadic proc, it is implicitly converted to `cstring` too: Even though the conversion is implicit, it is not *safe*: The garbage collector does not consider a `cstring` to be a root and may collect the underlying -memory. However, in practice, this almost never happens as the GC considers -stack roots conservatively. One can use the builtin procs `GC_ref` and -`GC_unref` to keep the string data alive for the rare cases where it does -not work. +memory. For this reason, the implicit conversion will be removed in future +releases of the Nim compiler. Certain idioms like conversion of a `const` string +to `cstring` are safe and will remain to be allowed. A `$` proc is defined for cstrings that returns a string. Thus to get a nim string from a cstring: diff --git a/lib/system/io.nim b/lib/system/io.nim index 591ea3e92..d8b2ac741 100644 --- a/lib/system/io.nim +++ b/lib/system/io.nim @@ -630,8 +630,8 @@ const "" else: "" - FormatOpen: array[FileMode, string] = [ - "rb" & NoInheritFlag, "wb" & NoInheritFlag, "w+b" & NoInheritFlag, + FormatOpen: array[FileMode, cstring] = [ + cstring("rb" & NoInheritFlag), "wb" & NoInheritFlag, "w+b" & NoInheritFlag, "r+b" & NoInheritFlag, "ab" & NoInheritFlag ] #"rt", "wt", "w+t", "r+t", "at" @@ -678,7 +678,7 @@ proc open*(f: var File, filename: string, ## This throws no exception if the file could not be opened. ## ## The file handle associated with the resulting `File` is not inheritable. - var p = fopen(filename, FormatOpen[mode]) + var p = fopen(filename.cstring, FormatOpen[mode]) if p != nil: var f2 = cast[File](p) when defined(posix) and not defined(nimscript): @@ -711,7 +711,7 @@ proc reopen*(f: File, filename: string, mode: FileMode = fmRead): bool {. ## Default mode is readonly. Returns true if the file could be reopened. ## ## The file handle associated with `f` won't be inheritable. - if freopen(filename, FormatOpen[mode], f) != nil: + if freopen(filename.cstring, FormatOpen[mode], f) != nil: when not defined(nimInheritHandles) and declared(setInheritable) and NoInheritFlag.len == 0: if not setInheritable(getOsFileHandle(f), false): diff --git a/lib/system/widestrs.nim b/lib/system/widestrs.nim index a999c9167..8b08959b5 100644 --- a/lib/system/widestrs.nim +++ b/lib/system/widestrs.nim @@ -171,7 +171,7 @@ proc newWideCString*(s: cstring): WideCStringObj = result = newWideCString(s, s.len) proc newWideCString*(s: string): WideCStringObj = - result = newWideCString(s, s.len) + result = newWideCString(cstring s, s.len) proc `$`*(w: WideCString, estimate: int, replacement: int = 0xFFFD): string = result = newStringOfCap(estimate + estimate shr 2) diff --git a/tests/misc/tconv.nim b/tests/misc/tconv.nim index 94677b1bf..1cb4b4fbf 100644 --- a/tests/misc/tconv.nim +++ b/tests/misc/tconv.nim @@ -1,5 +1,5 @@ discard """ - matrix: "--warningAsError:EnumConv" + matrix: "--warningAsError:EnumConv --warningAsError:CStringConv" """ template reject(x) = @@ -84,3 +84,11 @@ block: # https://github.com/nim-lang/RFCs/issues/294 reject: Goo(k2) reject: k2.Goo + +reject: + # bug #18550 + proc f(c: char): cstring = + var x = newString(109*1024*1024) + x[0] = c + x + From d5b5827bc2a3724290d3257b0f2fa70004c37c97 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 21 Jul 2021 23:00:29 -0700 Subject: [PATCH 2373/3768] fix setCommand so it behaves like regular nim invocation (#18555) --- compiler/cmdlinehelper.nim | 9 +-------- compiler/options.nim | 10 ++++++++++ compiler/scriptconfig.nim | 11 +---------- 3 files changed, 12 insertions(+), 18 deletions(-) diff --git a/compiler/cmdlinehelper.nim b/compiler/cmdlinehelper.nim index c6a0f200a..007dc2319 100644 --- a/compiler/cmdlinehelper.nim +++ b/compiler/cmdlinehelper.nim @@ -44,14 +44,7 @@ proc processCmdLineAndProjectPath*(self: NimProg, conf: ConfigRef) = elif self.supportsStdinFile and conf.projectName == "-": handleStdinInput(conf) elif conf.projectName != "": - try: - conf.projectFull = canonicalizePath(conf, AbsoluteFile conf.projectName) - except OSError: - conf.projectFull = AbsoluteFile conf.projectName - let p = splitFile(conf.projectFull) - let dir = if p.dir.isEmpty: AbsoluteDir getCurrentDir() else: p.dir - conf.projectPath = AbsoluteDir canonicalizePath(conf, AbsoluteFile dir) - conf.projectName = p.name + setFromProjectName(conf, conf.projectName) else: conf.projectPath = AbsoluteDir canonicalizePath(conf, AbsoluteFile getCurrentDir()) diff --git a/compiler/options.nim b/compiler/options.nim index 45ca14e76..f02f02256 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -695,6 +695,16 @@ proc setDefaultLibpath*(conf: ConfigRef) = proc canonicalizePath*(conf: ConfigRef; path: AbsoluteFile): AbsoluteFile = result = AbsoluteFile path.string.expandFilename +proc setFromProjectName*(conf: ConfigRef; projectName: string) = + try: + conf.projectFull = canonicalizePath(conf, AbsoluteFile projectName) + except OSError: + conf.projectFull = AbsoluteFile projectName + let p = splitFile(conf.projectFull) + let dir = if p.dir.isEmpty: AbsoluteDir getCurrentDir() else: p.dir + conf.projectPath = AbsoluteDir canonicalizePath(conf, AbsoluteFile dir) + conf.projectName = p.name + proc removeTrailingDirSep*(path: string): string = if (path.len > 0) and (path[^1] == DirSep): result = substr(path, 0, path.len - 2) diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim index ef1ceb12b..89510f6b2 100644 --- a/compiler/scriptconfig.nim +++ b/compiler/scriptconfig.nim @@ -151,18 +151,9 @@ proc setupVM*(module: PSym; cache: IdentCache; scriptName: string; setResult(a, strutils.cmpIgnoreCase(a.getString 0, a.getString 1)) cbconf setCommand: conf.setCommandEarly(a.getString 0) - # xxx move remaining logic to commands.nim or other let arg = a.getString 1 incl(conf.globalOptions, optWasNimscript) - if arg.len > 0: - conf.projectName = arg - let path = - if conf.projectName.isAbsolute: AbsoluteFile(conf.projectName) - else: conf.projectPath / RelativeFile(conf.projectName) - try: - conf.projectFull = canonicalizePath(conf, path) - except OSError: - conf.projectFull = path + if arg.len > 0: setFromProjectName(conf, arg) cbconf getCommand: setResult(a, conf.command) cbconf switch: From 58e27ebd4a16ca9f2ba24826d7162384d5321101 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 22 Jul 2021 09:37:41 +0200 Subject: [PATCH 2374/3768] fixes #12815 (#18554) --- changelog.md | 3 +++ compiler/ast.nim | 2 +- compiler/lineinfos.nim | 4 +++- compiler/sempass2.nim | 5 ++++- doc/nimc.rst | 6 ++++++ tests/misc/tconv.nim | 13 +++++++++++++ 6 files changed, 30 insertions(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index f2a9ca914..8e8b0a908 100644 --- a/changelog.md +++ b/changelog.md @@ -36,6 +36,9 @@ This warning will become an error in future versions! Use an explicit conversion like `cstring(x)` in order to silence the warning. +- There is a new warning for *any* type conversion to enum that can be enabled via + `.warning[AnyEnumConv]:on` or `--warning:AnyEnumConv:on`. + - Type mismatch errors now show more context, use `-d:nimLegacyTypeMismatch` for previous behavior. diff --git a/compiler/ast.nim b/compiler/ast.nim index 8b836e088..0eeaa8133 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1064,7 +1064,7 @@ proc getPIdent*(a: PNode): PIdent {.inline.} = # which may simplify code. case a.kind of nkSym: a.sym.name - of nkIdent: a.ident + of nkIdent: a.ident else: nil proc getnimblePkg*(a: PSym): PSym = diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index 3f31ceb97..8cb834156 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -71,6 +71,7 @@ type warnCannotOpen = "CannotOpen", warnFileChanged = "FileChanged", warnSuspiciousEnumConv = "EnumConv", + warnAnyEnumConv = "AnyEnumConv", warnCstringConv = "CStringConv", warnUser = "User", # hints @@ -156,6 +157,7 @@ const warnCannotOpen: "cannot open: $1", warnFileChanged: "file changed: $1", warnSuspiciousEnumConv: "$1", + warnAnyEnumConv: "$1", warnCstringConv: "$1", warnUser: "$1", hintSuccess: "operation successful: $#", @@ -211,7 +213,7 @@ type TNoteKinds* = set[TNoteKind] proc computeNotesVerbosity(): array[0..3, TNoteKinds] = - result[3] = {low(TNoteKind)..high(TNoteKind)} - {warnObservableStores, warnResultUsed} + result[3] = {low(TNoteKind)..high(TNoteKind)} - {warnObservableStores, warnResultUsed, warnAnyEnumConv} result[2] = result[3] - {hintStackTrace, warnUninit, hintExtendedContext, hintDeclaredLoc, hintProcessingStmt} result[1] = result[2] - {warnProveField, warnProveIndex, warnGcUnsafe, hintPath, hintDependency, hintCodeBegin, hintCodeEnd, diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index ce21c3ed2..87ed81098 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -1171,7 +1171,10 @@ proc track(tracked: PEffects, n: PNode) = not allowCStringConv(n[1]): message(tracked.config, n.info, warnCstringConv, "implicit conversion to 'cstring' from a non-const location: $1; this will become a compile time error in the future" % - [$n[1]]) + $n[1]) + + if n.typ.skipTypes(abstractInst).kind == tyEnum: + message(tracked.config, n.info, warnAnyEnumConv, "enum conversion: $1" % $n[1]) if n.len == 2: track(tracked, n[1]) diff --git a/doc/nimc.rst b/doc/nimc.rst index 070956860..3c451706b 100644 --- a/doc/nimc.rst +++ b/doc/nimc.rst @@ -70,6 +70,12 @@ SmallLshouldNotBeUsed The letter 'l' should not be used as an identifier. EachIdentIsTuple The code contains a confusing `var` declaration. +CStringConv Warn about dangerous implicit conversions + to `cstring`. +EnumConv Warn about conversions from enum to enum. +AnyEnumConv Warn about any conversions to an enum type. +ResultUsed Warn about the usage of the + built-in `result` variable. User Some user-defined warning. ========================== ============================================ diff --git a/tests/misc/tconv.nim b/tests/misc/tconv.nim index 1cb4b4fbf..ff6875021 100644 --- a/tests/misc/tconv.nim +++ b/tests/misc/tconv.nim @@ -92,3 +92,16 @@ reject: x[0] = c x +{.push warning[AnyEnumConv]:on, warningAsError[AnyEnumConv]:on.} + +reject: + # bug #12815 + type + Foo = enum + one = 1 + three = 3 + + var va = 2 + var vb = va.Foo + +{.pop.} From 0c4582c66524d58a3c72827d9546a5c5f1c40286 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 22 Jul 2021 11:59:21 +0200 Subject: [PATCH 2375/3768] mitigates #12815 (#18557) * mitigates #12815 * Update doc/nimc.rst Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> --- compiler/lineinfos.nim | 2 ++ compiler/sempass2.nim | 8 ++++++-- doc/nimc.rst | 2 ++ tests/misc/tconv.nim | 19 ++++++++++++++++--- 4 files changed, 26 insertions(+), 5 deletions(-) diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index 8cb834156..c64d7b9dd 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -72,6 +72,7 @@ type warnFileChanged = "FileChanged", warnSuspiciousEnumConv = "EnumConv", warnAnyEnumConv = "AnyEnumConv", + warnHoleEnumConv = "HoleEnumConv", warnCstringConv = "CStringConv", warnUser = "User", # hints @@ -158,6 +159,7 @@ const warnFileChanged: "file changed: $1", warnSuspiciousEnumConv: "$1", warnAnyEnumConv: "$1", + warnHoleEnumConv: "$1", warnCstringConv: "$1", warnUser: "$1", hintSuccess: "operation successful: $#", diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 87ed81098..e2f85dd38 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -1173,8 +1173,12 @@ proc track(tracked: PEffects, n: PNode) = "implicit conversion to 'cstring' from a non-const location: $1; this will become a compile time error in the future" % $n[1]) - if n.typ.skipTypes(abstractInst).kind == tyEnum: - message(tracked.config, n.info, warnAnyEnumConv, "enum conversion: $1" % $n[1]) + let t = n.typ.skipTypes(abstractInst) + if t.kind == tyEnum: + if tfEnumHasHoles in t.flags: + message(tracked.config, n.info, warnHoleEnumConv, "conversion to enum with holes is unsafe: $1" % $n) + else: + message(tracked.config, n.info, warnAnyEnumConv, "enum conversion: $1" % $n) if n.len == 2: track(tracked, n[1]) diff --git a/doc/nimc.rst b/doc/nimc.rst index 3c451706b..ed9077912 100644 --- a/doc/nimc.rst +++ b/doc/nimc.rst @@ -74,6 +74,8 @@ CStringConv Warn about dangerous implicit conversions to `cstring`. EnumConv Warn about conversions from enum to enum. AnyEnumConv Warn about any conversions to an enum type. +HoleEnumConv Warn about conversion to an enum with + holes. These conversions are unsafe. ResultUsed Warn about the usage of the built-in `result` variable. User Some user-defined warning. diff --git a/tests/misc/tconv.nim b/tests/misc/tconv.nim index ff6875021..5e8eac729 100644 --- a/tests/misc/tconv.nim +++ b/tests/misc/tconv.nim @@ -95,13 +95,26 @@ reject: {.push warning[AnyEnumConv]:on, warningAsError[AnyEnumConv]:on.} reject: - # bug #12815 type Foo = enum - one = 1 - three = 3 + one + three var va = 2 var vb = va.Foo {.pop.} + +{.push warningAsError[HoleEnumConv]:on.} + +reject: + # bug #12815 + type + Hole = enum + one = 1 + three = 3 + + var va = 2 + var vb = va.Hole + +{.pop.} From 5386ae75ba91ee2fe280f1fe807676a7c9af4b13 Mon Sep 17 00:00:00 2001 From: Jason Beetham Date: Fri, 23 Jul 2021 00:46:13 -0600 Subject: [PATCH 2376/3768] Fixed template const tuple unpacking (#18562) --- compiler/semtempl.nim | 40 ++++++++++++++++++---------------------- tests/vm/tconst.nim | 10 ++++++++++ 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index 92cc0be8c..a2b0f99ba 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -209,7 +209,7 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) = let pragmaNode = n[1] for i in 0.. Date: Fri, 23 Jul 2021 08:04:29 +0000 Subject: [PATCH 2377/3768] Replace calls to `putenv` with `setenv` (#18530) * Replace calls to C `putenv` with C `setenv` to remove possible memory leaks * Add test of correct behaviour on invalid input * Fix style in tests/stdlib/tos.nim Co-authored-by: Timothee Cour * Update tests/stdlib/tos.nim Co-authored-by: Timothee Cour * Update tests/stdlib/tos.nim Co-authored-by: Timothee Cour * Add comment with bug number to tests/stdlib/tos.nim Co-authored-by: Timothee Cour * Fix possible msvc arch issues Co-authored-by: Timothee Cour --- lib/pure/includes/osenv.nim | 11 ++++++++--- tests/stdlib/tos.nim | 3 +++ 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/lib/pure/includes/osenv.nim b/lib/pure/includes/osenv.nim index d0c92d566..db291c5fd 100644 --- a/lib/pure/includes/osenv.nim +++ b/lib/pure/includes/osenv.nim @@ -45,8 +45,10 @@ else: proc c_getenv(env: cstring): cstring {. importc: "getenv", header: "".} - proc c_putenv(env: cstring): cint {. - importc: "putenv", header: "".} + when defined(vcc): + proc c_putenv_s(envname: cstring, envval: cstring): cint {.importc: "_putenv_s", header: "".} + else: + proc c_setenv(envname: cstring, envval: cstring, overwrite: cint): cint {.importc: "setenv", header: "".} proc c_unsetenv(env: cstring): cint {. importc: "unsetenv", header: "".} @@ -220,8 +222,11 @@ else: if setEnvironmentVariableW(k, v) == 0'i32: raiseOSError(osLastError()) else: if setEnvironmentVariableA(key, val) == 0'i32: raiseOSError(osLastError()) + elif defined(vcc): + if c_putenv_s(key, val) != 0'i32: + raiseOSError(osLastError()) else: - if c_putenv(environment[indx]) != 0'i32: + if c_setenv(key, val, 1'i32) != 0'i32: raiseOSError(osLastError()) proc delEnv*(key: string) {.tags: [WriteEnvEffect].} = diff --git a/tests/stdlib/tos.nim b/tests/stdlib/tos.nim index 912558461..8a12b9b90 100644 --- a/tests/stdlib/tos.nim +++ b/tests/stdlib/tos.nim @@ -614,6 +614,9 @@ block osenv: doAssert existsEnv(dummyEnvVar) == false delEnv(dummyEnvVar) # deleting an already deleted env var doAssert existsEnv(dummyEnvVar) == false + block: # putEnv, bug #18502 + doAssertRaises(OSError): putEnv("DUMMY_ENV_VAR_PUT=DUMMY_VALUE", "NEW_DUMMY_VALUE") + doAssertRaises(OSError): putEnv("", "NEW_DUMMY_VALUE") block: doAssert getEnv("DUMMY_ENV_VAR_NONEXISTENT", "") == "" doAssert getEnv("DUMMY_ENV_VAR_NONEXISTENT", " ") == " " From 76f74fae88fba72f58e43ad5c8fd20a7c4d8b439 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 23 Jul 2021 04:41:16 -0700 Subject: [PATCH 2378/3768] std/random: fix overflow bugs; fixes #16360; fixes #16296; fixes #17670 (#18456) --- lib/pure/random.nim | 32 ++++++++++++++------ tests/stdlib/trandom.nim | 63 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 85 insertions(+), 10 deletions(-) diff --git a/lib/pure/random.nim b/lib/pure/random.nim index 07db0365c..a292386af 100644 --- a/lib/pure/random.nim +++ b/lib/pure/random.nim @@ -204,6 +204,19 @@ proc skipRandomNumbers*(s: var Rand) = s.a0 = s0 s.a1 = s1 +proc rand[T: uint | uint64](r: var Rand; max: T): T = + # xxx export in future work + if max == 0: return + else: + let max = uint64(max) + when T.high.uint64 == uint64.high: + if max == uint64.high: return T(next(r)) + while true: + let x = next(r) + # avoid `mod` bias + if x <= randMax - (randMax mod max): + return T(x mod (max + 1)) + proc rand*(r: var Rand; max: Natural): int {.benign.} = ## Returns a random integer in the range `0..max` using the given state. ## @@ -213,15 +226,13 @@ proc rand*(r: var Rand; max: Natural): int {.benign.} = ## * `rand proc<#rand,Rand,HSlice[T: Ordinal or float or float32 or float64,T: Ordinal or float or float32 or float64]>`_ ## that accepts a slice ## * `rand proc<#rand,typedesc[T]>`_ that accepts an integer or range type - runnableExamples("-r:off"): + runnableExamples: var r = initRand(123) - assert r.rand(100) == 96 # implementation defined - - if max == 0: return - while true: - let x = next(r) - if x <= randMax - (randMax mod Ui(max)): - return int(x mod (uint64(max) + 1u64)) + if false: + assert r.rand(100) == 96 # implementation defined + # bootstrap: can't use `runnableExamples("-r:off")` + cast[int](rand(r, uint64(max))) + # xxx toUnsigned pending https://github.com/nim-lang/Nim/pull/18445 proc rand*(max: int): int {.benign.} = ## Returns a random integer in the range `0..max`. @@ -306,7 +317,10 @@ proc rand*[T: Ordinal or SomeFloat](r: var Rand; x: HSlice[T, T]): T = when T is SomeFloat: result = rand(r, x.b - x.a) + x.a else: # Integers and Enum types - result = T(rand(r, int(x.b) - int(x.a)) + int(x.a)) + when defined(js): + result = cast[T](rand(r, cast[uint](x.b) - cast[uint](x.a)) + cast[uint](x.a)) + else: + result = cast[T](rand(r, cast[uint64](x.b) - cast[uint64](x.a)) + cast[uint64](x.a)) proc rand*[T: Ordinal or SomeFloat](x: HSlice[T, T]): T = ## For a slice `a..b`, returns a value in the range `a..b`. diff --git a/tests/stdlib/trandom.nim b/tests/stdlib/trandom.nim index e47ddad66..39ccca85b 100644 --- a/tests/stdlib/trandom.nim +++ b/tests/stdlib/trandom.nim @@ -3,7 +3,9 @@ discard """ targets: "c js" """ -import std/[random, math, os, stats, sets, tables] +import std/[random, math, stats, sets, tables] +when not defined(js): + import std/os randomize(233) @@ -187,3 +189,62 @@ block: # bug #17467 doAssert x > 1e-4, $(x, i) # This used to fail for each i in 0..<26844, i.e. the 1st produced value # was predictable and < 1e-4, skewing distributions. + +const withUint = false # pending exporting `proc rand[T: uint | uint64](r: var Rand; max: T): T =` + +block: # bug #16360 + var r = initRand() + template test(a) = + let a2 = a + block: + let a3 = r.rand(a2) + doAssert a3 <= a2 + doAssert a3.type is a2.type + block: + let a3 = rand(a2) + doAssert a3 <= a2 + doAssert a3.type is a2.type + when withUint: + test cast[uint](int.high) + test cast[uint](int.high) + 1 + when not defined(js): + # pending bug #16411 + test uint64.high + test uint64.high - 1 + test uint.high - 2 + test uint.high - 1 + test uint.high + test int.high + test int.high - 1 + test int.high - 2 + test 0 + when withUint: + test 0'u + test 0'u64 + +block: # bug #16296 + var r = initRand() + template test(x) = + let a2 = x + let a3 = r.rand(a2) + doAssert a3 <= a2.b + doAssert a3 >= a2.a + doAssert a3.type is a2.a.type + test(-2 .. int.high-1) + test(int.low .. int.high) + test(int.low+1 .. int.high) + test(int.low .. int.high-1) + test(int.low .. 0) + test(int.low .. -1) + test(int.low .. 1) + test(int64.low .. 1'i64) + when not defined(js): + # pending bug #16411 + test(10'u64 .. uint64.high) + +block: # bug #17670 + when not defined(js): + # pending bug #16411 + type UInt48 = range[0'u64..2'u64^48-1] + let x = rand(UInt48) + doAssert x is UInt48 From faabcfa6432cd15ecfee44a061ebf7738b16ad6c Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 23 Jul 2021 16:15:40 +0200 Subject: [PATCH 2379/3768] fixes #18558 (#18563) * fixes #18558 * better fix --- compiler/ccgcalls.nim | 44 +++++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 616028ac6..510b8d9ec 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -317,36 +317,46 @@ proc skipTrivialIndirections(n: PNode): PNode = result = n while true: case result.kind - of {nkDerefExpr, nkHiddenDeref, nkAddr, nkHiddenAddr, nkObjDownConv, nkObjUpConv}: + of nkDerefExpr, nkHiddenDeref, nkAddr, nkHiddenAddr, nkObjDownConv, nkObjUpConv: result = result[0] - of {nkHiddenStdConv, nkHiddenSubConv}: + of nkHiddenStdConv, nkHiddenSubConv: result = result[1] else: break -proc getPotentialWrites(n: PNode, mutate = false): seq[PNode] = +proc getPotentialWrites(n: PNode; mutate: bool; result: var seq[PNode]) = case n.kind: of nkLiterals, nkIdent: discard of nkSym: if mutate: result.add n of nkAsgn, nkFastAsgn: - result.add getPotentialWrites(n[0], true) - result.add getPotentialWrites(n[1], mutate) + getPotentialWrites(n[0], true, result) + getPotentialWrites(n[1], mutate, result) of nkAddr, nkHiddenAddr: - result.add getPotentialWrites(n[0], true) - of nkCallKinds: #TODO: Find out why in f += 1, f is a nkSym and not a nkHiddenAddr - for s in n.sons: - result.add getPotentialWrites(s, true) + getPotentialWrites(n[0], true, result) + of nkCallKinds: + case n.getMagic: + of mIncl, mExcl, mInc, mDec, mAppendStrCh, mAppendStrStr, mAppendSeqElem, + mAddr, mNew, mNewFinalize, mWasMoved, mDestroy, mReset: + getPotentialWrites(n[1], true, result) + for i in 2.. Date: Fri, 23 Jul 2021 14:57:47 -0700 Subject: [PATCH 2380/3768] rename nimFpRoundtrips => nimPreviewFloatRoundtrip (#18566) --- changelog.md | 4 ++-- compiler/nim.cfg | 2 +- lib/system/formatfloat.nim | 4 ++-- tests/config.nims | 2 +- tests/errmsgs/treportunused.nim | 2 +- tests/float/tfloats.nim | 10 +++++----- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/changelog.md b/changelog.md index 8e8b0a908..d33e24b65 100644 --- a/changelog.md +++ b/changelog.md @@ -112,8 +112,8 @@ - `system.addFloat` and `system.$` now can produce string representations of floating point numbers that are minimal in size and that "roundtrip" (via the "Dragonbox" algorithm). This currently has - to be enabled via `-d:nimFpRoundtrips`. It is expected that this behavior becomes the new default - in upcoming versions. + to be enabled via `-d:nimPreviewFloatRoundtrip`. It is expected that this behavior becomes the new default + in upcoming versions, as with other `nimPreviewX` define flags. - Fixed buffer overflow bugs in `net` diff --git a/compiler/nim.cfg b/compiler/nim.cfg index f10d847ac..ec51bd463 100644 --- a/compiler/nim.cfg +++ b/compiler/nim.cfg @@ -4,7 +4,7 @@ hint:XDeclaredButNotUsed:off define:booting define:nimcore -define:nimFpRoundtrips +define:nimPreviewFloatRoundtrip #import:"$projectpath/testability" diff --git a/lib/system/formatfloat.nim b/lib/system/formatfloat.nim index cb46c6ea7..3bcd3257b 100644 --- a/lib/system/formatfloat.nim +++ b/lib/system/formatfloat.nim @@ -74,7 +74,7 @@ proc writeFloatToBufferSprintf*(buf: var array[65, char]; value: BiggestFloat): result = 3 proc writeFloatToBuffer*(buf: var array[65, char]; value: BiggestFloat | float32): int {.inline.} = - when defined(nimFpRoundtrips): + when defined(nimPreviewFloatRoundtrip): writeFloatToBufferRoundtrip(buf, value) else: writeFloatToBufferSprintf(buf, value) @@ -121,7 +121,7 @@ proc addFloat*(result: var string; x: float | float32) {.inline.} = s.addFloat(45.67) assert s == "foo:45.67" template impl = - when defined(nimFpRoundtrips): + when defined(nimPreviewFloatRoundtrip): addFloatRoundtrip(result, x) else: addFloatSprintf(result, x) diff --git a/tests/config.nims b/tests/config.nims index 12b303318..f876cb612 100644 --- a/tests/config.nims +++ b/tests/config.nims @@ -35,4 +35,4 @@ switch("define", "nimExperimentalAsyncjsThen") switch("define", "nimExperimentalJsfetch") switch("define", "nimExperimentalLinenoiseExtra") -switch("define", "nimFpRoundtrips") +switch("define", "nimPreviewFloatRoundtrip") diff --git a/tests/errmsgs/treportunused.nim b/tests/errmsgs/treportunused.nim index 3105b35ea..f5ee79afa 100644 --- a/tests/errmsgs/treportunused.nim +++ b/tests/errmsgs/treportunused.nim @@ -32,7 +32,7 @@ var s9: int type s10 = object type s11 = type(1.2) -# bug #14407 (requires `compiler/nim.cfg` containing define:nimFpRoundtrips) +# bug #14407 (requires `compiler/nim.cfg` containing define:nimPreviewFloatRoundtrip) let `v0.99` = "0.99" `v0.99.99` = "0.99.99" diff --git a/tests/float/tfloats.nim b/tests/float/tfloats.nim index 63987bb8d..480396e81 100644 --- a/tests/float/tfloats.nim +++ b/tests/float/tfloats.nim @@ -1,5 +1,5 @@ discard """ - matrix: "-d:nimFpRoundtrips; -u:nimFpRoundtrips" + matrix: "-d:nimPreviewFloatRoundtrip; -u:nimPreviewFloatRoundtrip" targets: "c cpp js" """ @@ -67,14 +67,14 @@ template main = block: # example 1 let a = 0.1+0.2 doAssert a != 0.3 - when defined(nimFpRoundtrips): + when defined(nimPreviewFloatRoundtrip): doAssert $a == "0.30000000000000004" else: whenRuntimeJs: discard do: doAssert $a == "0.3" block: # example 2 const a = 0.1+0.2 - when defined(nimFpRoundtrips): + when defined(nimPreviewFloatRoundtrip): doAssert $($a, a) == """("0.30000000000000004", 0.30000000000000004)""" else: whenRuntimeJs: discard @@ -83,13 +83,13 @@ template main = const a1 = 0.1+0.2 let a2 = a1 doAssert a1 != 0.3 - when defined(nimFpRoundtrips): + when defined(nimPreviewFloatRoundtrip): doAssert $[$a1, $a2] == """["0.30000000000000004", "0.30000000000000004"]""" else: whenRuntimeJs: discard do: doAssert $[$a1, $a2] == """["0.3", "0.3"]""" - when defined(nimFpRoundtrips): + when defined(nimPreviewFloatRoundtrip): block: # bug #18148 var a = 1.1'f32 doAssert $a == "1.1", $a # was failing From f4ff276a90091f2b88c191aac5a201dcb3ec9795 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 24 Jul 2021 00:30:02 +0200 Subject: [PATCH 2381/3768] refactoring: removed dead code (#18567) --- compiler/semdata.nim | 2 +- compiler/semexprs.nim | 19 +++++++------------ compiler/semobjconstr.nim | 5 ++--- compiler/semstmts.nim | 2 +- 4 files changed, 11 insertions(+), 17 deletions(-) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 12bb22ff9..0a6d09a56 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -65,7 +65,7 @@ type # you may be in position to supply a better error message # to the user. efWantStmt, efAllowStmt, efDetermineType, efExplain, - efAllowDestructor, efWantValue, efOperand, efNoSemCheck, + efWantValue, efOperand, efNoSemCheck, efNoEvaluateGeneric, efInCall, efFromHlo, efNoSem2Check, efNoUndeclared # Use this if undeclared identifiers should not raise an error during diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index c25a4067f..42c6b465e 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -623,10 +623,10 @@ proc semArrayConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = localError(c.config, x.info, "invalid order in array constructor") x = x[1] - let xx = semExprWithType(c, x, flags*{efAllowDestructor}) + let xx = semExprWithType(c, x, {}) result.add xx typ = commonType(c, typ, xx.typ) - #n[i] = semExprWithType(c, x, flags*{efAllowDestructor}) + #n[i] = semExprWithType(c, x, {}) #result.add fitNode(c, typ, n[i]) inc(lastIndex) addSonSkipIntLit(result.typ, typ, c.idgen) @@ -1758,13 +1758,9 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = localError(c.config, a.info, errXCannotBeAssignedTo % renderTree(a, {renderNoComments})) else: - let - lhs = n[0] - lhsIsResult = lhs.kind == nkSym and lhs.sym.kind == skResult - var - rhs = semExprWithType(c, n[1], - if lhsIsResult: {efAllowDestructor} else: {}) - if lhsIsResult: + let lhs = n[0] + let rhs = semExprWithType(c, n[1], {}) + if lhs.kind == nkSym and lhs.sym.kind == skResult: n.typ = c.enforceVoidContext if c.p.owner.kind != skMacro and resultTypeIsInferrable(lhs.sym.typ): var rhsTyp = rhs.typ @@ -2512,8 +2508,7 @@ proc semTupleFieldsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = let id = considerQuotedIdent(c, n[i][0]) if containsOrIncl(ids, id.id): localError(c.config, n[i].info, errFieldInitTwice % id.s) - n[i][1] = semExprWithType(c, n[i][1], - flags*{efAllowDestructor}) + n[i][1] = semExprWithType(c, n[i][1], {}) if n[i][1].typ.kind == tyTypeDesc: localError(c.config, n[i][1].info, "typedesc not allowed as tuple field.") @@ -2533,7 +2528,7 @@ proc semTuplePositionsConstr(c: PContext, n: PNode, flags: TExprFlags): PNode = result.transitionSonsKind(nkTupleConstr) var typ = newTypeS(tyTuple, c) # leave typ.n nil! for i in 0.. Date: Sun, 25 Jul 2021 09:06:04 -0700 Subject: [PATCH 2382/3768] fixes #18385 (#18571) needs a more permanent fix by better incorporating IC, but fixes nimsuggest highlighting for now. --- compiler/vmgen.nim | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index be86550ae..4957a3339 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -2254,8 +2254,16 @@ proc optimizeJumps(c: PCtx; start: int) = else: discard proc genProc(c: PCtx; s: PSym): int = - let pos = c.procToCodePos.getOrDefault(s.id) - if pos == 0: + let + pos = c.procToCodePos.getOrDefault(s.id) + wasNotGenProcBefore = pos == 0 + noRegistersAllocated = s.offset == -1 + if wasNotGenProcBefore or noRegistersAllocated: + # xxx: the noRegisterAllocated check is required in order to avoid issues + # where nimsuggest can crash due as a macro with pos will be loaded + # but it doesn't have offsets for register allocations see: + # https://github.com/nim-lang/Nim/issues/18385 + # Improvements and further use of IC should remove the need for this. #if s.name.s == "outterMacro" or s.name.s == "innerProc": # echo "GENERATING CODE FOR ", s.name.s let last = c.code.len-1 From e08ec0c6745e54de4e174970e513acef3b0d8f5e Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Sun, 25 Jul 2021 13:07:43 -0300 Subject: [PATCH 2383/3768] Documentation only, Sugar arrow and semicolons (#18574) * Document that sugar arrow do not support semicolon as argument separator --- lib/pure/sugar.nim | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/pure/sugar.nim b/lib/pure/sugar.nim index 7cfa932e6..f82e1706f 100644 --- a/lib/pure/sugar.nim +++ b/lib/pure/sugar.nim @@ -54,6 +54,8 @@ proc createProcType(p, b: NimNode): NimNode = macro `=>`*(p, b: untyped): untyped = ## Syntax sugar for anonymous procedures. It also supports pragmas. + ## + ## .. warning:: Semicolons can not be used to separate procedure arguments. runnableExamples: proc passTwoAndTwo(f: (int, int) -> int): int = f(2, 2) @@ -133,6 +135,8 @@ macro `=>`*(p, b: untyped): untyped = macro `->`*(p, b: untyped): untyped = ## Syntax sugar for procedure types. It also supports pragmas. + ## + ## .. warning:: Semicolons can not be used to separate procedure arguments. runnableExamples: proc passTwoAndTwo(f: (int, int) -> int): int = f(2, 2) # is the same as: From 5f7db652577523bdbb4e9b9157b4fb3bed25e01c Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 25 Jul 2021 12:55:33 -0700 Subject: [PATCH 2384/3768] followup #18453 (#18582) --- tests/stdlib/tos.nim | 4 ++-- tests/vm/tvmmisc.nim | 30 +++++++++++++++--------------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/tests/stdlib/tos.nim b/tests/stdlib/tos.nim index 8a12b9b90..d598a05b2 100644 --- a/tests/stdlib/tos.nim +++ b/tests/stdlib/tos.nim @@ -558,9 +558,9 @@ block ospaths: doAssert joinPath("foo","abc") == unixToNativePath"foo/abc" doAssert joinPath("","abc") == unixToNativePath"abc" - doAssert joinPath("gook/.","abc") == unixToNativePath"gook/abc" + doAssert joinPath("zook/.","abc") == unixToNativePath"zook/abc" - # controversial: inconsistent with `joinPath("gook/.","abc")` + # controversial: inconsistent with `joinPath("zook/.","abc")` # on linux, `./foo` and `foo` are treated a bit differently for executables # but not `./foo/bar` and `foo/bar` doAssert joinPath(".", "/lib") == unixToNativePath"./lib" diff --git a/tests/vm/tvmmisc.nim b/tests/vm/tvmmisc.nim index a4f6391cc..f6fbde0f9 100644 --- a/tests/vm/tvmmisc.nim +++ b/tests/vm/tvmmisc.nim @@ -376,7 +376,7 @@ block: # VM wrong register free causes errors in unrelated code in let prc = if not isClosure: bb.sym else: bb[0].sym ]# proc bar2(head: string): string = "asdf" - proc gook(u1: int) = discard + proc zook(u1: int) = discard type PathEntry = object kind: int @@ -385,20 +385,20 @@ block: # VM wrong register free causes errors in unrelated code iterator globOpt(): int = var u1: int - gook(u1) - gook(u1) - gook(u1) - gook(u1) - gook(u1) - gook(u1) - gook(u1) - gook(u1) - gook(u1) - gook(u1) - gook(u1) - gook(u1) - gook(u1) - gook(u1) + zook(u1) + zook(u1) + zook(u1) + zook(u1) + zook(u1) + zook(u1) + zook(u1) + zook(u1) + zook(u1) + zook(u1) + zook(u1) + zook(u1) + zook(u1) + zook(u1) var entry = PathEntry() entry.path = bar2("") From 10da888c075acb901b57ba51e4400df6cdf3efd6 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Mon, 26 Jul 2021 00:01:19 +0300 Subject: [PATCH 2385/3768] docgen: sort symbols (fix #17910) (#18560) * docgen: sort symbols (fix #17910) * add workaround + change naming * switch to a dedicated sort comparator * fix numbers with unequal string lengths * dedicated `sortName` instead of `plainNameEsc`: * more compact names for non-overloaded symbols * more predictable Ascii sort (e.g. `<` instead of `<`) --- compiler/docgen.nim | 98 ++- .../expected/subdir/subdir_b/utils.html | 10 +- nimdoc/testproject/expected/testproject.html | 686 +++++++++--------- 3 files changed, 432 insertions(+), 362 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index f9e72850a..492668543 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -11,7 +11,7 @@ # by knowing how the anchors are going to be named. import - ast, strutils, strtabs, options, msgs, os, idents, + ast, strutils, strtabs, algorithm, sequtils, options, msgs, os, idents, wordrecg, syntaxes, renderer, lexer, packages/docutils/rst, packages/docutils/rstgen, json, xmltree, trees, types, @@ -43,11 +43,15 @@ type descRst: ItemPre ## Description of the item (may contain ## runnableExamples). substitutions: seq[string] ## Variable names in `doc.item`... + sortName: string ## The string used for sorting in output ModSection = object ## Section like Procs, Types, etc. secItems: seq[Item] ## Pre-processed items. finalMarkup: string ## The items, after RST pass 2 and rendering. ModSections = array[TSymKind, ModSection] - TSections = array[TSymKind, string] + TocItem = object ## HTML TOC item + content: string + sortName: string + TocSectionsFinal = array[TSymKind, string] ExampleGroup = ref object ## a group of runnableExamples with same rdoccmd rdoccmd: string ## from 1st arg in `runnableExamples(rdoccmd): body` @@ -64,8 +68,13 @@ type module: PSym modDeprecationMsg: string section: ModSections # entries of ``.nim`` file (for `proc`s, etc) - toc, toc2: TSections # toc2 - grouped TOC - tocTable: array[TSymKind, Table[string, string]] + tocSimple: array[TSymKind, seq[TocItem]] + # TOC entries for non-overloadable symbols (e.g. types, constants)... + tocTable: array[TSymKind, Table[string, seq[TocItem]]] + # ...otherwise (e.g. procs) + toc2: TocSectionsFinal # TOC `content`, which is probably wrapped + # in `doc.section.toc2` + toc: TocSectionsFinal # final TOC (wrapped in `doc.section.toc`) indexValFilename: string analytics: string # Google Analytics javascript, "" if doesn't exist seenSymbols: StringTableRef # avoids duplicate symbol generation for HTML. @@ -88,6 +97,51 @@ type proc add(dest: var ItemPre, rst: PRstNode) = dest.add ItemFragment(isRst: true, rst: rst) proc add(dest: var ItemPre, str: string) = dest.add ItemFragment(isRst: false, str: str) +proc cmpDecimalsIgnoreCase(a, b: string): int = + ## For sorting with correct handling of cases like 'uint8' and 'uint16'. + ## Also handles leading zeros well (however note that leading zeros are + ## significant when lengths of numbers mismatch, e.g. 'bar08' > 'bar8' !). + runnableExamples: + doAssert cmpDecimalsIgnoreCase("uint8", "uint16") < 0 + doAssert cmpDecimalsIgnoreCase("val00032", "val16suffix") > 0 + doAssert cmpDecimalsIgnoreCase("val16suffix", "val16") > 0 + doAssert cmpDecimalsIgnoreCase("val_08_32", "val_08_8") > 0 + doAssert cmpDecimalsIgnoreCase("val_07_32", "val_08_8") < 0 + doAssert cmpDecimalsIgnoreCase("ab8", "ab08") < 0 + doAssert cmpDecimalsIgnoreCase("ab8de", "ab08c") < 0 # sanity check + let aLen = a.len + let bLen = b.len + var + iA = 0 + iB = 0 + while iA < aLen and iB < bLen: + if isDigit(a[iA]) and isDigit(b[iB]): + var + limitA = iA # index after the last (least significant) digit + limitB = iB + while limitA < aLen and isDigit(a[limitA]): inc limitA + while limitB < bLen and isDigit(b[limitB]): inc limitB + var pos = max(limitA-iA, limitB-iA) + while pos > 0: + if limitA-pos < iA: # digit in `a` is 0 effectively + result = ord('0') - ord(b[limitB-pos]) + elif limitB-pos < iB: # digit in `b` is 0 effectively + result = ord(a[limitA-pos]) - ord('0') + else: + result = ord(a[limitA-pos]) - ord(b[limitB-pos]) + if result != 0: return + dec pos + result = (limitA - iA) - (limitB - iB) # consider 'bar08' > 'bar8' + if result != 0: return + iA = limitA + iB = limitB + else: + result = ord(toLowerAscii(a[iA])) - ord(toLowerAscii(b[iB])) + if result != 0: return + inc iA + inc iB + result = (aLen - iA) - (bLen - iB) + proc prettyString(a: object): string = # xxx pending std/prettyprint refs https://github.com/nim-lang/RFCs/issues/203#issuecomment-602534906 for k, v in fieldPairs(a): @@ -862,6 +916,7 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags) = let plainNameEsc = esc(d.target, plainName.strip) uniqueName = if k in routineKinds: plainNameEsc else: name + sortName = if k in routineKinds: plainName.strip else: name cleanPlainSymbol = renderPlainSymbolName(nameNode) complexSymbol = complexName(k, n, cleanPlainSymbol) plainSymbolEnc = encodeUrl(cleanPlainSymbol) @@ -874,7 +929,10 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags) = let seeSrc = genSeeSrc(d, toFullPath(d.conf, n.info), n.info.line.int) - d.section[k].secItems.add Item(descRst: comm, substitutions: @[ + d.section[k].secItems.add Item( + descRst: comm, + sortName: sortName, + substitutions: @[ "name", name, "uniqueName", uniqueName, "header", result, "itemID", $d.id, "header_plain", plainNameEsc, "itemSym", cleanPlainSymbol, @@ -898,12 +956,15 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags) = setIndexTerm(d[], external, symbolOrId, plain, nameNode.sym.name.s & '.' & plain, xmltree.escape(getPlainDocstring(e).docstringSummary)) - d.toc[k].add(getConfigVar(d.conf, "doc.item.toc") % [ + d.tocSimple[k].add TocItem( + sortName: sortName, + content: getConfigVar(d.conf, "doc.item.toc") % [ "name", name, "header_plain", plainNameEsc, "itemSymOrIDEnc", symbolOrIdEnc]) - d.tocTable[k].mgetOrPut(cleanPlainSymbol, "").add( - getConfigVar(d.conf, "doc.item.tocTable") % [ + d.tocTable[k].mgetOrPut(cleanPlainSymbol, newSeq[TocItem]()).add TocItem( + sortName: sortName, + content: getConfigVar(d.conf, "doc.item.tocTable") % [ "name", name, "header_plain", plainNameEsc, "itemSymOrID", symbolOrId.replace(",", ","), "itemSymOrIDEnc", symbolOrIdEnc]) @@ -1143,8 +1204,9 @@ proc finishGenerateDoc*(d: var PDoc) = var resolved = resolveSubs(d.sharedState, f.rst) renderRstToOut(d[], resolved, result) of false: result &= f.str + proc cmp(x, y: Item): int = cmpDecimalsIgnoreCase(x.sortName, y.sortName) for k in TSymKind: - for item in d.section[k].secItems: + for item in d.section[k].secItems.sorted(cmp): var itemDesc: string renderItemPre(d, item.descRst, itemDesc) d.section[k].finalMarkup.add( @@ -1262,18 +1324,26 @@ proc genSection(d: PDoc, kind: TSymKind, groupedToc = false) = "sectionid", $ord(kind), "sectionTitle", title, "sectionTitleID", $(ord(kind) + 50), "content", d.section[kind].finalMarkup] - var tocSource = d.toc + proc cmp(x, y: TocItem): int = cmpDecimalsIgnoreCase(x.sortName, y.sortName) if groupedToc: - for p in d.tocTable[kind].keys: + let overloadableNames = toSeq(keys(d.tocTable[kind])) + for plainName in overloadableNames.sorted(cmpDecimalsIgnoreCase): + var overloadChoices = d.tocTable[kind][plainName] + overloadChoices.sort(cmp) + var content: string + for item in overloadChoices: + content.add item.content d.toc2[kind].add getConfigVar(d.conf, "doc.section.toc2") % [ "sectionid", $ord(kind), "sectionTitle", title, "sectionTitleID", $(ord(kind) + 50), - "content", d.tocTable[kind][p], "plainName", p] - tocSource = d.toc2 + "content", content, "plainName", plainName] + else: + for item in d.tocSimple[kind].sorted(cmp): + d.toc2[kind].add item.content d.toc[kind] = getConfigVar(d.conf, "doc.section.toc") % [ "sectionid", $ord(kind), "sectionTitle", title, - "sectionTitleID", $(ord(kind) + 50), "content", tocSource[kind]] + "sectionTitleID", $(ord(kind) + 50), "content", d.toc2[kind]] proc relLink(outDir: AbsoluteDir, destFile: AbsoluteFile, linkto: RelativeFile): string = $relativeTo(outDir / linkto, destFile.splitFile().dir, '/') diff --git a/nimdoc/testproject/expected/subdir/subdir_b/utils.html b/nimdoc/testproject/expected/subdir/subdir_b/utils.html index 6aa71e8c2..939495ad1 100644 --- a/nimdoc/testproject/expected/subdir/subdir_b/utils.html +++ b/nimdoc/testproject/expected/subdir/subdir_b/utils.html @@ -130,16 +130,16 @@ window.addEventListener('DOMContentLoaded', main);
  • aEnum(): untyped
  • - - + diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html index 7e5a685d2..0f6289bbc 100644 --- a/nimdoc/testproject/expected/testproject.html +++ b/nimdoc/testproject/expected/testproject.html @@ -107,9 +107,7 @@ window.addEventListener('DOMContentLoaded', main);
  • Types
      -
    • FooBuzz
    • -
    • A
    • Foo
    • +
    • FooBuzz
    • Vars
    • @@ -153,35 +153,57 @@ window.addEventListener('DOMContentLoaded', main);
    • Procs
        -
          z10 -
        • z10()
        • + -
            tripleStrLitTest -
          • tripleStrLitTest()
          • + -
              z17 -
            • z17()
            • + + - + + + + - - - - - - - - - - - - - - -
                z4 -
              • z4()
              • +
                  someFunc @@ -285,29 +235,79 @@ window.addEventListener('DOMContentLoaded', main); title="someFunc()">someFunc()
                -
                  z8 -
                • z8(): int
                • + + + + -
                    asyncFun2 -
                  • asyncFun2(): owned(Future[void])
                  • + -
                      asyncFun1 -
                    • asyncFun1(): Future[int]
                    • + + + + + + + + @@ -316,7 +316,12 @@ window.addEventListener('DOMContentLoaded', main);
                    • Methods @@ -379,10 +379,25 @@ window.addEventListener('DOMContentLoaded', main);
                    • Templates
                        - @@ -447,16 +447,6 @@ window.addEventListener('DOMContentLoaded', main);

                        Types

                        - -
                        FooBuzz {....deprecated: "FooBuzz msg".} = int
                        -
                        -
                        - Deprecated: FooBuzz msg -
                        - - - -
                        A {.inject.} = enum
                           aA
                        @@ -480,6 +470,16 @@ The enum B. + + +
                        FooBuzz {....deprecated: "FooBuzz msg".} = int
                        +
                        +
                        + Deprecated: FooBuzz msg +
                        + + +
                        Shapes = enum
                        @@ -496,19 +496,19 @@ Some shapes.
                         

                        Vars

                        - -
                        someVariable: bool
                        -
                        - -This should be visible. - -
                        aVariable: array[1, int]
                        +
                        + +
                        someVariable: bool
                        +
                        + +This should be visible. +
                        @@ -548,12 +548,69 @@ This should be visible.

                        Procs

                        + +
                        proc addfBug14485() {....raises: [], tags: [].}
                        +
                        + +Some proc +

                        Example:

                        +
                        discard "foo() = " & $[1]
                        +#[
                        +0: let's also add some broken html to make sure this won't break in future
                        +1: </span>
                        +2: </span>
                        +3: </span
                        +4: </script>
                        +5: </script
                        +6: </script
                        +7: end of broken html
                        +]#
                        + +
                        + +
                        proc anything() {....raises: [], tags: [].}
                        +
                        + +There is no block quote after blank lines at the beginning. + +
                        + +
                        proc asyncFun1(): Future[int] {....raises: [Exception, ValueError],
                        +                                tags: [RootEffect].}
                        +
                        + +ok1 + +
                        + +
                        proc asyncFun2(): owned(Future[void]) {....raises: [Exception], tags: [RootEffect].}
                        +
                        + + + +
                        + +
                        proc asyncFun3(): owned(Future[void]) {....raises: [Exception], tags: [RootEffect].}
                        +
                        + + +

                        Example:

                        +
                        discard
                        ok1 + +
                        proc bar[T](a, b: T): T
                        +
                        + +
                        proc baz() {....raises: [], tags: [].}
                        +
                        + + +
                        proc baz[T](a, b: T): T {....deprecated.}
                        @@ -575,11 +632,20 @@ This is deprecated without message. This is deprecated with a message. - -
                        func someFunc() {....raises: [], tags: [].}
                        + +
                        proc c_nonexistent(frmt: cstring): cint {.importc: "nonexistent",
                        +    header: "<stdio.h>", varargs, discardable.}
                        -My someFunc. Stuff in quotes here. Some link + + +
                        + +
                        proc c_printf(frmt: cstring): cint {.importc: "printf", header: "<stdio.h>",
                        +                                     varargs, discardable.}
                        +
                        + +the c printf. etc.
                        @@ -598,6 +664,103 @@ came form utils but should be shown where + +
                        proc low2[T: Ordinal | enum | range](x: T): T {.magic: "Low", noSideEffect.}
                        +
                        + +

                        Returns the lowest possible value of an ordinal value x. As a special semantic rule, x may also be a type identifier.

                        +

                        See also:

                        + +
                        low2(2) # => -9223372036854775808
                        +

                        Example:

                        +
                        discard "in low2"
                        + +
                        + +
                        proc low[T: Ordinal | enum | range](x: T): T {.magic: "Low", noSideEffect.}
                        +
                        + +

                        Returns the lowest possible value of an ordinal value x. As a special semantic rule, x may also be a type identifier.

                        +

                        See also:

                        + +
                        low(2) # => -9223372036854775808
                        + +
                        + +
                        proc p1() {....raises: [], tags: [].}
                        +
                        + +cp1 +

                        Example:

                        +
                        doAssert 1 == 1 # regular comments work here
                        c4 +

                        Example:

                        +
                        # c5 regular comments before 1st token work
                        +# regular comment
                        +#[
                        +nested regular comment
                        +]#
                        +doAssert 2 == 2 # c8
                        +## this is a non-nested doc comment
                        +
                        +##[
                        +this is a nested doc comment
                        +]##
                        +discard "c9"
                        +# also work after
                        + +
                        + +
                        func someFunc() {....raises: [], tags: [].}
                        +
                        + +My someFunc. Stuff in quotes here. Some link + +
                        + +
                        proc tripleStrLitTest() {....raises: [], tags: [].}
                        +
                        + + +

                        Example: cmd: --hint:XDeclaredButNotUsed:off

                        +
                        ## mullitline string litterals are tricky as their indentation can span
                        +## below that of the runnableExamples
                        +let s1a = """
                        +should appear at indent 0
                        +  at indent 2
                        +at indent 0
                        +"""
                        +# make sure this works too
                        +let s1b = """start at same line
                        +  at indent 2
                        +at indent 0
                        +""" # comment after
                        +let s2 = """sandwich """
                        +let s3 = """"""
                        +when false:
                        +  let s5 = """
                        +        in s5 """
                        +
                        +let s3b = ["""
                        +%!? #[...] # inside a multiline ...
                        +""", "foo"]
                        +
                        +## make sure handles trailing spaces
                        +let s4 = """ 
                        +"""
                        +
                        +let s5 = """ x
                        +"""
                        +let s6 = """ ""
                        +"""
                        +let s7 = """"""""""
                        +let s8 = ["""""""""", """
                        +  """ ]
                        +discard
                        +# should be in
                        +
                        proc z1(): Foo {....raises: [], tags: [].}
                        @@ -701,13 +864,6 @@ cz13

                        Example:

                        discard
                        - - -
                        proc baz() {....raises: [], tags: [].}
                        -
                        - - -
                        proc z17() {....raises: [], tags: [].}
                        @@ -717,162 +873,6 @@ cz17 rest

                        Example:

                        discard 1
                        rest - - -
                        proc p1() {....raises: [], tags: [].}
                        -
                        - -cp1 -

                        Example:

                        -
                        doAssert 1 == 1 # regular comments work here
                        c4 -

                        Example:

                        -
                        # c5 regular comments before 1st token work
                        -# regular comment
                        -#[
                        -nested regular comment
                        -]#
                        -doAssert 2 == 2 # c8
                        -## this is a non-nested doc comment
                        -
                        -##[
                        -this is a nested doc comment
                        -]##
                        -discard "c9"
                        -# also work after
                        - -
                        - -
                        proc addfBug14485() {....raises: [], tags: [].}
                        -
                        - -Some proc -

                        Example:

                        -
                        discard "foo() = " & $[1]
                        -#[
                        -0: let's also add some broken html to make sure this won't break in future
                        -1: </span>
                        -2: </span>
                        -3: </span
                        -4: </script>
                        -5: </script
                        -6: </script
                        -7: end of broken html
                        -]#
                        - -
                        - -
                        proc c_printf(frmt: cstring): cint {.importc: "printf", header: "<stdio.h>",
                        -                                     varargs, discardable.}
                        -
                        - -the c printf. etc. - -
                        - -
                        proc c_nonexistent(frmt: cstring): cint {.importc: "nonexistent",
                        -    header: "<stdio.h>", varargs, discardable.}
                        -
                        - - - -
                        - -
                        proc low[T: Ordinal | enum | range](x: T): T {.magic: "Low", noSideEffect.}
                        -
                        - -

                        Returns the lowest possible value of an ordinal value x. As a special semantic rule, x may also be a type identifier.

                        -

                        See also:

                        - -
                        low(2) # => -9223372036854775808
                        - -
                        - -
                        proc low2[T: Ordinal | enum | range](x: T): T {.magic: "Low", noSideEffect.}
                        -
                        - -

                        Returns the lowest possible value of an ordinal value x. As a special semantic rule, x may also be a type identifier.

                        -

                        See also:

                        - -
                        low2(2) # => -9223372036854775808
                        -

                        Example:

                        -
                        discard "in low2"
                        - -
                        - -
                        proc tripleStrLitTest() {....raises: [], tags: [].}
                        -
                        - - -

                        Example: cmd: --hint:XDeclaredButNotUsed:off

                        -
                        ## mullitline string litterals are tricky as their indentation can span
                        -## below that of the runnableExamples
                        -let s1a = """
                        -should appear at indent 0
                        -  at indent 2
                        -at indent 0
                        -"""
                        -# make sure this works too
                        -let s1b = """start at same line
                        -  at indent 2
                        -at indent 0
                        -""" # comment after
                        -let s2 = """sandwich """
                        -let s3 = """"""
                        -when false:
                        -  let s5 = """
                        -        in s5 """
                        -
                        -let s3b = ["""
                        -%!? #[...] # inside a multiline ...
                        -""", "foo"]
                        -
                        -## make sure handles trailing spaces
                        -let s4 = """ 
                        -"""
                        -
                        -let s5 = """ x
                        -"""
                        -let s6 = """ ""
                        -"""
                        -let s7 = """"""""""
                        -let s8 = ["""""""""", """
                        -  """ ]
                        -discard
                        -# should be in
                        - -
                        - -
                        proc asyncFun1(): Future[int] {....raises: [Exception, ValueError],
                        -                                tags: [RootEffect].}
                        -
                        - -ok1 - -
                        - -
                        proc asyncFun2(): owned(Future[void]) {....raises: [Exception], tags: [RootEffect].}
                        -
                        - - - -
                        - -
                        proc asyncFun3(): owned(Future[void]) {....raises: [Exception], tags: [RootEffect].}
                        -
                        - - -

                        Example:

                        -
                        discard
                        ok1 - -
                        - -
                        proc anything() {....raises: [], tags: [].}
                        -
                        - -There is no block quote after blank lines at the beginning. -
                        @@ -967,6 +967,13 @@ cz18

                        Templates

                        + +
                        template foo(a, b: SomeType)
                        +
                        + +This does nothing + +
                        template fromUtils2()
                        @@ -976,20 +983,6 @@ ok3
                        discard """should be shown as examples for fromUtils2
                                in module calling fromUtilsGen"""
                        -
                        - -
                        template z6t(): int
                        -
                        - -cz6t - -
                        - -
                        template foo(a, b: SomeType)
                        -
                        - -This does nothing -
                        template myfn()
                        @@ -1012,6 +1005,22 @@ bar discard 0xff # elu par cette crapule # should be in
                        should be still in + + +
                        template testNimDocTrailingExample()
                        +
                        + + +

                        Example:

                        +
                        discard 2
                        + +
                        + +
                        template z6t(): int
                        +
                        + +cz6t +
                        template z14()
                        @@ -1038,15 +1047,6 @@ cz15

                        Example:

                        discard 1
                        in or out? - - -
                        template testNimDocTrailingExample()
                        -
                        - - -

                        Example:

                        -
                        discard 2
                        -
                        From ff280c012731b1978005a743eec96cd885331475 Mon Sep 17 00:00:00 2001 From: Araq Date: Mon, 26 Jul 2021 09:57:19 +0200 Subject: [PATCH 2386/3768] added missing .inline for toCChar --- compiler/msgs.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 242b06587..20a55daee 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -27,7 +27,7 @@ proc flushDot*(conf: ConfigRef) = conf.lastMsgWasDot.excl stdOrrKind write(stdOrr, "\n") -proc toCChar*(c: char; result: var string) = +proc toCChar*(c: char; result: var string) {.inline.} = case c of '\0'..'\x1F', '\x7F'..'\xFF': result.add '\\' From 158d7c7a7a8977f5937311b98bc381399f31db8d Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 26 Jul 2021 14:15:55 +0200 Subject: [PATCH 2387/3768] fixes #18558 again (#18586) --- compiler/ccgcalls.nim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 510b8d9ec..749a7a3b6 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -333,6 +333,8 @@ proc getPotentialWrites(n: PNode; mutate: bool; result: var seq[PNode]) = getPotentialWrites(n[1], mutate, result) of nkAddr, nkHiddenAddr: getPotentialWrites(n[0], true, result) + of nkBracketExpr, nkDotExpr, nkCheckedFieldExpr: + getPotentialWrites(n[0], mutate, result) of nkCallKinds: case n.getMagic: of mIncl, mExcl, mInc, mDec, mAppendStrCh, mAppendStrStr, mAppendSeqElem, From d4c3a09286d1cbb534ed5dbd21d99a18670b3427 Mon Sep 17 00:00:00 2001 From: Antonis Geralis <43617260+planetis-m@users.noreply.github.com> Date: Mon, 26 Jul 2021 16:22:42 +0300 Subject: [PATCH 2388/3768] optimize for the non-throwing case (#18587) --- lib/pure/collections/tables.nim | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index b845f57cc..403345755 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -227,6 +227,12 @@ template dataLen(t): untyped = len(t.data) include tableimpl +proc raiseKeyError[T](key: T) {.noinline, noreturn.} = + when compiles($key): + raise newException(KeyError, "key not found: " & $key) + else: + raise newException(KeyError, "key not found") + template get(t, key): untyped = ## retrieves the value at `t[key]`. The value can be modified. ## If `key` is not in `t`, the `KeyError` exception is raised. @@ -235,10 +241,7 @@ template get(t, key): untyped = var index = rawGet(t, key, hc) if index >= 0: result = t.data[index].val else: - when compiles($key): - raise newException(KeyError, "key not found: " & $key) - else: - raise newException(KeyError, "key not found") + raiseKeyError(key) proc enlarge[A, B](t: var Table[A, B]) = var n: KeyValuePairSeq[A, B] From 664dbb6bfbe06ff0ddc4339c0fe80c49d0838ca4 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 26 Jul 2021 18:00:03 +0200 Subject: [PATCH 2389/3768] atlas tool: testing via mocking (#18588) * atlas: refactoring * Atlas: do some basic testing via mocking --- koch.nim | 2 + tools/atlas/atlas.nim | 187 +++++++++++++++++------ tools/atlas/osutils.nim | 8 - tools/atlas/testdata.nim | 63 ++++++++ tools/atlas/tests/balls.nimble | 32 ++++ tools/atlas/tests/grok.nimble | 5 + tools/atlas/tests/nim-bytes2human.nimble | 7 + tools/atlas/tests/nim.cfg | 11 ++ tools/atlas/tests/npeg.nimble | 48 ++++++ tools/atlas/tests/packages/packages.json | 36 +++++ tools/atlas/tests/sync.nimble | 10 ++ tools/atlas/tests/testes.nimble | 23 +++ tools/atlas/tests/ups.nimble | 13 ++ 13 files changed, 387 insertions(+), 58 deletions(-) create mode 100644 tools/atlas/testdata.nim create mode 100644 tools/atlas/tests/balls.nimble create mode 100644 tools/atlas/tests/grok.nimble create mode 100644 tools/atlas/tests/nim-bytes2human.nimble create mode 100644 tools/atlas/tests/nim.cfg create mode 100644 tools/atlas/tests/npeg.nimble create mode 100644 tools/atlas/tests/packages/packages.json create mode 100644 tools/atlas/tests/sync.nimble create mode 100644 tools/atlas/tests/testes.nimble create mode 100644 tools/atlas/tests/ups.nimble diff --git a/koch.nim b/koch.nim index da7576e65..4296e4529 100644 --- a/koch.nim +++ b/koch.nim @@ -594,6 +594,8 @@ proc runCI(cmd: string) = when defined(posix): execFold("Run nimsuggest tests", "nim r nimsuggest/tester") + execFold("Run atlas tests", "nim c -r -d:atlasTests tools/atlas/atlas.nim clone https://github.com/disruptek/balls") + when not defined(bsd): if not doUseCpp: # the BSDs are overwhelmed already, so only run this test on the other machines: diff --git a/tools/atlas/atlas.nim b/tools/atlas/atlas.nim index b10ec0358..d0990c1f7 100644 --- a/tools/atlas/atlas.nim +++ b/tools/atlas/atlas.nim @@ -39,6 +39,10 @@ proc writeVersion() = stdout.flushFile() quit(0) +const + MockupRun = defined(atlasTests) + TestsDir = "tools/atlas/tests" + type PackageName = distinct string DepRelation = enum @@ -55,9 +59,79 @@ type p: Table[string, string] # name -> url mapping processed: HashSet[string] # the key is (url / commit) errors: int + when MockupRun: + currentDir: string + step: int + mockupSuccess: bool const InvalidCommit = "" + ProduceTest = false + +type + Command = enum + GitDiff = "git diff", + GitTags = "git show-ref --tags", + GitRevParse = "git rev-parse", + GitCheckout = "git checkout", + GitPull = "git pull", + GitCurrentCommit = "git log -n 1 --format=%H" + GitMergeBase = "git merge-base" + +include testdata + +proc exec(c: var AtlasContext; cmd: Command; args: openArray[string]): (string, int) = + when MockupRun: + assert TestLog[c.step].cmd == cmd + case cmd + of GitDiff, GitTags, GitRevParse, GitPull, GitCurrentCommit: + result = (TestLog[c.step].output, TestLog[c.step].exitCode) + of GitCheckout: + assert args[0] == TestLog[c.step].output + of GitMergeBase: + let tmp = TestLog[c.step].output.splitLines() + assert tmp.len == 4, $tmp.len + assert tmp[0] == args[0] + assert tmp[1] == args[1] + assert tmp[3] == "" + result[0] = tmp[2] + result[1] = TestLog[c.step].exitCode + inc c.step + else: + var cmdLine = $cmd + for i in 0.. ", result + +proc cloneUrl(c: var AtlasContext; url, dest: string; cloneUsingHttps: bool): string = + when MockupRun: + result = "" + else: + result = osutils.cloneUrl(url, dest, cloneUsingHttps) + when ProduceTest: + echo "cloned ", url, " into ", dest + +template withDir*(c: var AtlasContext; dir: string; body: untyped) = + when MockupRun: + c.currentDir = dir + body + else: + let oldDir = getCurrentDir() + try: + when ProduceTest: + echo "Current directory is now ", dir + setCurrentDir(dir) + body + finally: + setCurrentDir(oldDir) + +proc extractRequiresInfo(c: var AtlasContext; nimbleFile: string): NimbleFileInfo = + result = extractRequiresInfo(nimbleFile) + when ProduceTest: + echo "nimble ", nimbleFile, " info ", result proc toDepRelation(s: string): DepRelation = case s @@ -65,9 +139,9 @@ proc toDepRelation(s: string): DepRelation = of ">": strictlyGreater else: normal -proc isCleanGit(dir: string): string = +proc isCleanGit(c: var AtlasContext; dir: string): string = result = "" - let (outp, status) = osproc.execCmdEx("git diff") + let (outp, status) = exec(c, GitDiff, []) if outp.len != 0: result = "'git diff' not empty" elif status != 0: @@ -101,8 +175,8 @@ proc sameVersionAs(tag, ver: string): bool = result = safeCharAt(tag, idx-1) notin VersionChars and safeCharAt(tag, idx+ver.len) notin VersionChars -proc versionToCommit(d: Dependency): string = - let (outp, status) = osproc.execCmdEx("git show-ref --tags") +proc versionToCommit(c: var AtlasContext; d: Dependency): string = + let (outp, status) = exec(c, GitTags, []) if status == 0: var useNextOne = false for line in splitLines(outp): @@ -123,35 +197,36 @@ proc versionToCommit(d: Dependency): string = return "" -proc shortToCommit(short: string): string = - let (cc, status) = osproc.execCmdEx("git rev-parse " & quoteShell(short)) +proc shortToCommit(c: var AtlasContext; short: string): string = + let (cc, status) = exec(c, GitRevParse, [short]) result = if status == 0: strutils.strip(cc) else: "" proc checkoutGitCommit(c: var AtlasContext; p: PackageName; commit: string) = - let (_, status) = osproc.execCmdEx("git checkout " & quoteShell(commit)) + let (_, status) = exec(c, GitCheckout, [commit]) if status != 0: error(c, p, "could not checkout commit", commit) proc gitPull(c: var AtlasContext; p: PackageName) = - let (_, status) = osproc.execCmdEx("git pull") + let (_, status) = exec(c, GitPull, []) if status != 0: error(c, p, "could not 'git pull'") proc updatePackages(c: var AtlasContext) = if dirExists(c.workspace / PackagesDir): - withDir(c.workspace / PackagesDir): + withDir(c, c.workspace / PackagesDir): gitPull(c, PackageName PackagesDir) else: - withDir c.workspace: - let err = cloneUrl("https://github.com/nim-lang/packages", PackagesDir, false) + withDir c, c.workspace: + let err = cloneUrl(c, "https://github.com/nim-lang/packages", PackagesDir, false) if err != "": error c, PackageName(PackagesDir), err proc fillPackageLookupTable(c: var AtlasContext) = if not c.hasPackageList: c.hasPackageList = true - updatePackages(c) - let plist = getPackages(c.workspace) + when not MockupRun: + updatePackages(c) + let plist = getPackages(when MockupRun: TestsDir else: c.workspace) for entry in plist: c.p[unicode.toLower entry.name] = entry.url @@ -178,19 +253,19 @@ proc isShortCommitHash(commit: string): bool {.inline.} = proc checkoutCommit(c: var AtlasContext; w: Dependency) = let dir = c.workspace / w.name.string - withDir dir: + withDir c, dir: if w.commit.len == 0 or cmpIgnoreCase(w.commit, "head") == 0: gitPull(c, w.name) else: - let err = isCleanGit(dir) + let err = isCleanGit(c, dir) if err != "": warn c, w.name, err else: let requiredCommit = - if needsCommitLookup(w.commit): versionToCommit(w) - elif isShortCommitHash(w.commit): shortToCommit(w.commit) + if needsCommitLookup(w.commit): versionToCommit(c, w) + elif isShortCommitHash(w.commit): shortToCommit(c, w.commit) else: w.commit - let (cc, status) = osproc.execCmdEx("git log -n 1 --format=%H") + let (cc, status) = exec(c, GitCurrentCommit, []) let currentCommit = strutils.strip(cc) if requiredCommit == "" or status != 0: if requiredCommit == "" and w.commit == InvalidCommit: @@ -201,8 +276,7 @@ proc checkoutCommit(c: var AtlasContext; w: Dependency) = if currentCommit != requiredCommit: # checkout the later commit: # git merge-base --is-ancestor - let (cc, status) = osproc.execCmdEx("git merge-base " & - currentCommit.quoteShell & " " & requiredCommit.quoteShell) + let (cc, status) = exec(c, GitMergeBase, [currentCommit, requiredCommit]) let mergeBase = strutils.strip(cc) if status == 0 and (mergeBase == currentCommit or mergeBase == requiredCommit): # conflict resolution: pick the later commit: @@ -215,15 +289,19 @@ proc checkoutCommit(c: var AtlasContext; w: Dependency) = currentCommit, "(current) or", w.commit, " =", requiredCommit, "(required)" proc findNimbleFile(c: AtlasContext; dep: Dependency): string = - result = c.workspace / dep.name.string / (dep.name.string & ".nimble") - if not fileExists(result): - result = "" - for x in walkFiles(c.workspace / dep.name.string / "*.nimble"): - if result.len == 0: - result = x - else: - # ambiguous .nimble file - return "" + when MockupRun: + result = TestsDir / dep.name.string & ".nimble" + doAssert fileExists(result), "file does not exist " & result + else: + result = c.workspace / dep.name.string / (dep.name.string & ".nimble") + if not fileExists(result): + result = "" + for x in walkFiles(c.workspace / dep.name.string / "*.nimble"): + if result.len == 0: + result = x + else: + # ambiguous .nimble file + return "" proc addUniqueDep(c: var AtlasContext; work: var seq[Dependency]; tokens: seq[string]) = @@ -240,7 +318,7 @@ proc collectNewDeps(c: var AtlasContext; work: var seq[Dependency]; isMainProject: bool) = let nimbleFile = findNimbleFile(c, dep) if nimbleFile != "": - let nimbleInfo = extractRequiresInfo(nimbleFile) + let nimbleInfo = extractRequiresInfo(c, nimbleFile) for r in nimbleInfo.requires: var tokens: seq[string] = @[] for token in tokenizeRequires(r): @@ -280,8 +358,8 @@ proc clone(c: var AtlasContext; start: string): seq[string] = let w = work[i] let oldErrors = c.errors if not dirExists(c.workspace / w.name.string): - withDir c.workspace: - let err = cloneUrl(w.url, w.name.string, false) + withDir c, c.workspace: + let err = cloneUrl(c, w.url, w.name.string, false) if err != "": error c, w.name, err if oldErrors == c.errors: @@ -296,29 +374,34 @@ const configPatternBegin = "############# begin Atlas config section ##########\n" configPatternEnd = "############# end Atlas config section ##########\n" -proc patchNimCfg(c: AtlasContext; deps: seq[string]) = +proc patchNimCfg(c: var AtlasContext; deps: seq[string]) = var paths = "--noNimblePath\n" for d in deps: paths.add "--path:\"../" & d.replace("\\", "/") & "\"\n" - let cfg = c.projectDir / "nim.cfg" var cfgContent = configPatternBegin & paths & configPatternEnd - if not fileExists(cfg): - writeFile(cfg, cfgContent) + + when MockupRun: + assert readFile(TestsDir / "nim.cfg") == cfgContent + c.mockupSuccess = true else: - let content = readFile(cfg) - let start = content.find(configPatternBegin) - if start >= 0: - cfgContent = content.substr(0, start-1) & cfgContent - let theEnd = content.find(configPatternEnd, start) - if theEnd >= 0: - cfgContent.add content.substr(theEnd+len(configPatternEnd)) - else: - cfgContent = content & "\n" & cfgContent - if cfgContent != content: - # do not touch the file if nothing changed - # (preserves the file date information): + let cfg = c.projectDir / "nim.cfg" + if not fileExists(cfg): writeFile(cfg, cfgContent) + else: + let content = readFile(cfg) + let start = content.find(configPatternBegin) + if start >= 0: + cfgContent = content.substr(0, start-1) & cfgContent + let theEnd = content.find(configPatternEnd, start) + if theEnd >= 0: + cfgContent.add content.substr(theEnd+len(configPatternEnd)) + else: + cfgContent = content & "\n" & cfgContent + if cfgContent != content: + # do not touch the file if nothing changed + # (preserves the file date information): + writeFile(cfg, cfgContent) proc error*(msg: string) = when defined(debug): @@ -365,8 +448,12 @@ proc main = singleArg() let deps = clone(c, args[0]) patchNimCfg c, deps - if c.errors > 0: - error "There were problems." + when MockupRun: + if not c.mockupSuccess: + error "There were problems." + else: + if c.errors > 0: + error "There were problems." of "refresh": noArgs() updatePackages(c) diff --git a/tools/atlas/osutils.nim b/tools/atlas/osutils.nim index 31392b113..1bb414c03 100644 --- a/tools/atlas/osutils.nim +++ b/tools/atlas/osutils.nim @@ -3,14 +3,6 @@ import os, strutils, osproc -template withDir*(dir, body) = - let oldDir = getCurrentDir() - try: - setCurrentDir(dir) - body - finally: - setCurrentDir(oldDir) - proc isUrl*(x: string): bool = x.startsWith("git://") or x.startsWith("https://") or x.startsWith("http://") diff --git a/tools/atlas/testdata.nim b/tools/atlas/testdata.nim new file mode 100644 index 000000000..f960e3d08 --- /dev/null +++ b/tools/atlas/testdata.nim @@ -0,0 +1,63 @@ + +type + PerDirData = object + dirname: string + cmd: Command + exitCode: int + output: string + +template toData(a, b, c, d): untyped = + PerDirData(dirname: a, cmd: b, exitCode: c, output: d) + +const + TestLog = [ + toData("balls", GitPull, 0, "Already up to date.\n"), + toData("grok", GitDiff, 0, ""), + toData("grok", GitTags, 0, "2ca193c31fa2377c1e991a080d60ca3215ff6cf0 refs/tags/0.0.1\n48007554b21ba2f65c726ae2fdda88d621865b4a refs/tags/0.0.2\n7092a0286421c7818cd335cca9ebc72d03d866c2 refs/tags/0.0.3\n62707b8ac684efac35d301dbde57dc750880268e refs/tags/0.0.4\n876f2504e0c2f785ffd2cf65a78e2aea474fa8aa refs/tags/0.0.5\nb7eb1f2501aa2382cb3a38353664a13af62a9888 refs/tags/0.0.6\nf5d818bfd6038884b3d8b531c58484ded20a58a4 refs/tags/0.1.0\n961eaddea49c3144d130d105195583d3f11fb6c6 refs/tags/0.2.0\n15ab8ed8d4f896232a976a9008548bd53af72a66 refs/tags/0.2.1\n426a7d7d4603f77ced658e73ad7f3f582413f6cd refs/tags/0.3.0\n83cf7a39b2fe897786fb0fe01a7a5933c3add286 refs/tags/0.3.1\n8d2e3c900edbc95fa0c036fd76f8e4f814aef2c1 refs/tags/0.3.2\n48b43372f49a3bb4dc0969d82a0fca183fb94662 refs/tags/0.3.3\n9ca947a3009ea6ba17814b20eb953272064eb2e6 refs/tags/0.4.0\n1b5643d04fba6d996a16d1ffc13d034a40003f8f refs/tags/0.5.0\n486b0eb580b1c465453d264ac758cc490c19c33e refs/tags/0.5.1\naedb0d9497390e20b9d2541cef2bb05a5cda7a71 refs/tags/0.5.2\n"), + toData("grok", GitCurrentCommit, 0, "349c15fd1e03f1fcdd81a1edefba3fa6116ab911\n"), + toData("grok", GitMergeBase, 0, "349c15fd1e03f1fcdd81a1edefba3fa6116ab911\n1b5643d04fba6d996a16d1ffc13d034a40003f8f\n349c15fd1e03f1fcdd81a1edefba3fa6116ab911\n"), + toData("grok", GitCheckout, 0, "1b5643d04fba6d996a16d1ffc13d034a40003f8f"), # watch out! + + toData("ups", GitDiff, 0, ""), + toData("ups", GitTags, 0, "4008f9339cd22b30e180bc87a6cca7270fd28ac1 refs/tags/0.0.2\n19bc490c22b4f5b0628c31cdedead1375b279356 refs/tags/0.0.3\nff34602aaea824cb46d6588cd5fe1178132e9702 refs/tags/0.0.4\n09de599138f20b745133b6e4fe563e204415a7e8 refs/tags/0.0.5\n85fee3b74798311108a105635df31f892150f5d0 refs/tags/0.0.6\nfd303913b22b121dc42f332109e9c44950b9acd4 refs/tags/0.0.7\n"), + toData("ups", GitCurrentCommit, 0, "74c31af8030112dac758440aa51ef175992f71f3\n"), + toData("ups", GitMergeBase, 0, "74c31af8030112dac758440aa51ef175992f71f3\n4008f9339cd22b30e180bc87a6cca7270fd28ac1\n74c31af8030112dac758440aa51ef175992f71f3\n"), + toData("ups", GitCheckout, 0, "4008f9339cd22b30e180bc87a6cca7270fd28ac1"), + + toData("sync", GitDiff, 0, ""), + toData("sync", GitTags, 0, "c9811af2d03dc78a920d33592d0a3daae3d9e68f refs/tags/v0.1.0\nc99d3a73344c4c02335190b489e7ea8aaa4e9584 refs/tags/v0.2.0\n6186a38c97c0f26e1a0c8016979227d26c41717b refs/tags/v0.3.0\n5506e4e09b849bd59f930fcccee3096abb089b1d refs/tags/v0.4.0\n1e4ef046ed0447aec7ca4ab3b2e2a5347ac41448 refs/tags/v0.5.0\na95f7b0b22c77b98ed63c1c4f435d550de8dbb05 refs/tags/v0.6.0\n214b868ace4ebc9140ee56734147fbd16b238c71 refs/tags/v0.7.0\n786698bf9f597834f36cd530d32faee31545a1d7 refs/tags/v0.9.0\n810bd2d75e9f6e182534ae2488670b51a9f13fc3 refs/tags/v1.0.0\na133d37991e3d4e9553c5261eabcb07d471968f7 refs/tags/v1.1.0\n0872a7cd70ea43a26deb94b8ab12cc5b89a1c212 refs/tags/v1.2.0\n19ac734438b6c04fe89711728a97fcde591324eb refs/tags/v1.3.0\nde5c7337ebc22422190e8aeca37d05651735f440 refs/tags/v1.4.0\n"), + toData("sync", GitCurrentCommit, 0, "de5c7337ebc22422190e8aeca37d05651735f440\n"), + toData("sync", GitMergeBase, 0, "de5c7337ebc22422190e8aeca37d05651735f440\n810bd2d75e9f6e182534ae2488670b51a9f13fc3\n810bd2d75e9f6e182534ae2488670b51a9f13fc3\n"), + + toData("npeg", GitDiff, 0, ""), + toData("npeg", GitTags, 0, "8df2f0c9391995fd086b8aab00e8ab7aded1e8f0 refs/tags/0.1.0\n4c959a72db5283b55eeef491076eefb5e02316f1 refs/tags/0.10.0\n802f47c0f7f4318a4f0858ba5a6a6ed2333bde71 refs/tags/0.11.0\n82c8d92837108dce225358ace2c416bf9a3f30ce refs/tags/0.12.0\n87d2f2c4f6ef7da350d45beb5a336611bde7f518 refs/tags/0.13.0\n39964f0d220bfaade47a568bf03c1cf28aa2bc37 refs/tags/0.14.0\nbe9f03f92304cbeab70572944a8563db9b23b2fb refs/tags/0.14.1\na933fb9832566fc95273e417597bfb4faf564ca6 refs/tags/0.15.0\n6aad2e438c52ff0636c7bfb64338e444ac3e83ba refs/tags/0.16.0\nf4ddffb5848c42c6151743dd9c7eddcaaabc56cc refs/tags/0.17.0\n30b446b39442cdbc53a97018ab8a54149aa7c3b7 refs/tags/0.17.1\n1a9d36aa3b34a6169d4530463f1c17a3fe1e075e refs/tags/0.18.0\ndd34f903a9a63b876cb2db19b7a4ce0bcc252134 refs/tags/0.19.0\nd93d49c81fc8722d7929ac463b435c0f2e10c53b refs/tags/0.2.0\neeae7746c9b1118bcf27744ab2aee26969051256 refs/tags/0.20.0\n8c3471a548129f3bf62df15cd0fd8cca1787d852 refs/tags/0.21.0\nc0e873a17bc713c80e74fec3c30cb62dcd5d194a refs/tags/0.21.1\nbae84c47a1bb259b209b6f6be1582327b784539d refs/tags/0.21.2\nbfcb4bcae76a917c3c88736ca773e4cb67dbb2d8 refs/tags/0.21.3\n0eabb7c462d30932049f0b7e6a030c1562cf9fee refs/tags/0.22.0\n2e75367095f54d4351005078bad98041a55b14c1 refs/tags/0.22.1\n814ea235dd398108d7b18f966694c3d951575701 refs/tags/0.22.2\na812064587d983c129737f8500bf74990e6b8dab refs/tags/0.23.0\nbd969ad3745db0d66022564cac76cf9424651104 refs/tags/0.23.1\na037c646a47623b92718efadc2bb74d03664b360 refs/tags/0.23.2\n078475ccceeaca0fac947492acdd24514da8d863 refs/tags/0.24.0\ne7bd87dc992512fd5825a557a56907647e03c979 refs/tags/0.24.1\n45ea601e1c7f64fb857bc99df984b86673621d2c refs/tags/0.3.0\n1ea9868a3fee3aa487ab7ec9129208a4dd483d0d refs/tags/0.4.0\n39afdb5733d3245386d29d08c5ff61c89268f499 refs/tags/0.5.0\n458c7b5910fcb157af3fc51bc3b3e663fdb3ed4a refs/tags/0.6.0\n06c38bd8563d822455bc237c2a98c153d938ed1b refs/tags/0.7.0\nf446b6056eef6d8dc9d8b47a79aca93d17dc8230 refs/tags/0.8.0\nbb25a195133f9f7af06386d0809793923cc5e8ab refs/tags/0.9.0\n"), + toData("npeg", GitCurrentCommit, 0, "5d80f93aa720898936668b3bc47d0fff101ec414\n"), + toData("npeg", GitMergeBase, 0, "5d80f93aa720898936668b3bc47d0fff101ec414\na037c646a47623b92718efadc2bb74d03664b360\na037c646a47623b92718efadc2bb74d03664b360\n"), + + toData("testes", GitDiff, 0, ""), + toData("testes", GitTags, 0, "3ce9b2968b5f644755a0ced1baa3eece88c2f12e refs/tags/0.1.0\nf73af8318b54737678fab8b54bdcd8a451015e0d refs/tags/0.1.1\nd21d84d37b161a123a43318bae353108755916de refs/tags/0.1.2\n5c36b6095353ed03b08ac939d00aff2d73f79a35 refs/tags/0.1.3\na1220d11237ee8f135f772ff9731c11b2d91ba31 refs/tags/0.1.4\n574f741b90d04a7ce8c9b990e6077708d7ad076e refs/tags/0.1.5\nced0a9e58234b680def6931578e09165a32e6291 refs/tags/0.1.6\nbb248952e8742a6011eb1a45a9d2059aeb0341d7 refs/tags/0.1.7\nabb7d7c552da0a8e0ddc586c15ccf7e74b0d068b refs/tags/0.10.0\n6e42a768a90d6442196b344bcdcb6f834b76e7b7 refs/tags/0.2.0\n9d136c3a0851ca2c021f5fb4f7b63f0a0ef77232 refs/tags/0.2.1\ndcb282b2da863fd2939e1969cec7a99788feb456 refs/tags/0.2.2\nf708a632afaa40a322a1a61c1c13722edac8e8c5 refs/tags/0.3.0\n3213f59e3f9ba052452c59f01d1418360d856af6 refs/tags/0.3.1\nf7bb1743dffd327958dfcebae4cfb6f61cc1cb8c refs/tags/0.3.2\n6b64569ebecad6bc60cc8697713701e7659204f4 refs/tags/0.3.3\nb51c25a4367bd17f419f78cb5a27f319e9d820f5 refs/tags/0.3.4\nb265612710cbd5ddb1b173c94ece8ec5c7ceccac refs/tags/0.3.5\ne404bcfe42e92d7509717a2dfa115cacb4964c5d refs/tags/0.3.6\n5e4d0d5b7e7f314dde701c546c4365c59782d3dc refs/tags/0.3.7\ne13f91c9c913d2b81c59adeaad687efa2b35293a refs/tags/0.3.8\n17599625f09af0ae4b525e63ab726a3002540702 refs/tags/0.3.9\n13e907f70571dd146d8dc29ddec4599b40ba4e85 refs/tags/0.4.0\n155a74cf676495df1e0674dd07b5e4a0291a9a4a refs/tags/0.4.1\nf37abccdc148cb02ca637a6f0bc8821491cce358 refs/tags/0.4.2\n0250d29ebdd02f28f9020445adb5a4e51fd1902c refs/tags/0.5.0\n2fb87db6d9f34109a70205876030c53f815739b7 refs/tags/0.5.1\n629d17ba8d6a1a4eca8145eb089ed5bca4473dfc refs/tags/0.6.0\ne926130f5f1b7903f68be49cc1563225bd9d948d refs/tags/0.7.0\n7365303897e6185796c274425c079916047e3f14 refs/tags/0.7.1\na735c4adabeba637409f41c4325dd8fc5fb91e2d refs/tags/0.7.10\nfe023fd27404889c5122f902456cbba14b767405 refs/tags/0.7.11\n4430e72972c77a5e9c1555d59bba11d840682691 refs/tags/0.7.12\nf0e53eb490a9558c7f594d2e095b70665e36ca88 refs/tags/0.7.13\nf6520e25e7c329c2957cda447f149fc6a930db0d refs/tags/0.7.2\nd509762f7191757c240d3c79c9ecda53f8c0cfe3 refs/tags/0.7.3\nc02e7a783d1c42fd1f91bca7142f7c3733950c05 refs/tags/0.7.4\n8c8a9e496e9b86ba7602709438980ca31e6989d9 refs/tags/0.7.5\n29839c18b4ac83c0111a178322b57ebb8a8d402c refs/tags/0.7.6\n3b62973cf74fafd8ea906644d89ac34d29a8a6cf refs/tags/0.7.7\ne67ff99dc43c391e89a37f97a9d298c3428bbde2 refs/tags/0.7.8\n4b72ecda0d40ed8e5ab8ad4095a0691d30ec6cd0 refs/tags/0.7.9\n2512b8cc3d7f001d277e89978da2049a5feee5c4 refs/tags/0.8.0\n86c47029690bd2731d204245f3f54462227bba0d refs/tags/0.9.0\n9a7f94f78588e9b5ba7ca077e1f7eae0607c6cf6 refs/tags/0.9.1\n08c915dc016d16c1dfa9a77d0b045ec29c9f2074 refs/tags/0.9.2\n3fb658b1ce1e1efa37d6f9f14322bdac8def02a5 refs/tags/0.9.3\n738fda0add962379ffe6aa6ca5f01a6943a98a2e refs/tags/0.9.4\n48d821add361f7ad768ecb35a0b19c38f90c919e refs/tags/0.9.5\nff9ae890f597dac301b2ac6e6805eb9ac5afd49a refs/tags/0.9.6\n483c78f06e60b0ec5e79fc3476df075ee7286890 refs/tags/0.9.7\n416eec87a5ae39a1a6035552e9e9a47d76b13026 refs/tags/1.0.0\na935cfe9445cc5218fbdd7e0afb35aa1587fff61 refs/tags/1.0.1\n4b83863a9181f054bb695b11b5d663406dfd85d2 refs/tags/1.0.2\n295145fddaa4fe29c1e71a5044d968a84f9dbf69 refs/tags/1.1.0\n8f74ea4e5718436c47305b4488842e6458a13dac refs/tags/1.1.1\n4135bb291e53d615a976e997c44fb2bd9e1ad343 refs/tags/1.1.10\n8c09dbcd16612f5989065db02ea2e7a752dd2656 refs/tags/1.1.11\naedfebdb6c016431d84b0c07cf181b957a900640 refs/tags/1.1.12\n2c2e958366ef6998115740bdf110588d730e5738 refs/tags/1.1.2\nbecc77258321e6ec40d89efdddf37bafd0d07fc3 refs/tags/1.1.3\ne070d7c9853bf94c35b81cf0c0a8980c2449bb22 refs/tags/1.1.4\n12c986cbbf65e8571a486e9230808bf887e5f04f refs/tags/1.1.5\n63df8986f5b56913b02d26954fa033eeaf43714c refs/tags/1.1.6\n38e02c9c6bd728b043036fe0d1894d774cab3108 refs/tags/1.1.7\n3c3879fff16450d28ade79a6b08982bf5cefc061 refs/tags/1.1.8\ne32b811b3b2e70a1d189d7a663bc2583e9c18f96 refs/tags/1.1.9\n0c1b4277c08197ce7e7e0aa2bad91d909fcd96ac refs/tags/2.0.0\n"), + toData("testes", GitCurrentCommit, 0, "d9db2ad09aa38fc26625341e1b666602959e144f\n"), + toData("testes", GitMergeBase, 0, "d9db2ad09aa38fc26625341e1b666602959e144f\n416eec87a5ae39a1a6035552e9e9a47d76b13026\nd9db2ad09aa38fc26625341e1b666602959e144f\n"), + toData("testes", GitCheckout, 0, "416eec87a5ae39a1a6035552e9e9a47d76b13026"), + + toData("grok", GitDiff, 0, ""), + toData("grok", GitTags, 0, "2ca193c31fa2377c1e991a080d60ca3215ff6cf0 refs/tags/0.0.1\n48007554b21ba2f65c726ae2fdda88d621865b4a refs/tags/0.0.2\n7092a0286421c7818cd335cca9ebc72d03d866c2 refs/tags/0.0.3\n62707b8ac684efac35d301dbde57dc750880268e refs/tags/0.0.4\n876f2504e0c2f785ffd2cf65a78e2aea474fa8aa refs/tags/0.0.5\nb7eb1f2501aa2382cb3a38353664a13af62a9888 refs/tags/0.0.6\nf5d818bfd6038884b3d8b531c58484ded20a58a4 refs/tags/0.1.0\n961eaddea49c3144d130d105195583d3f11fb6c6 refs/tags/0.2.0\n15ab8ed8d4f896232a976a9008548bd53af72a66 refs/tags/0.2.1\n426a7d7d4603f77ced658e73ad7f3f582413f6cd refs/tags/0.3.0\n83cf7a39b2fe897786fb0fe01a7a5933c3add286 refs/tags/0.3.1\n8d2e3c900edbc95fa0c036fd76f8e4f814aef2c1 refs/tags/0.3.2\n48b43372f49a3bb4dc0969d82a0fca183fb94662 refs/tags/0.3.3\n9ca947a3009ea6ba17814b20eb953272064eb2e6 refs/tags/0.4.0\n1b5643d04fba6d996a16d1ffc13d034a40003f8f refs/tags/0.5.0\n486b0eb580b1c465453d264ac758cc490c19c33e refs/tags/0.5.1\naedb0d9497390e20b9d2541cef2bb05a5cda7a71 refs/tags/0.5.2\n"), + toData("grok", GitCurrentCommit, 0, "4e6526a91a23eaec778184e16ce9a34d25d48bdc\n"), + toData("grok", GitMergeBase, 0, "4e6526a91a23eaec778184e16ce9a34d25d48bdc\n62707b8ac684efac35d301dbde57dc750880268e\n349c15fd1e03f1fcdd81a1edefba3fa6116ab911\n"), + toData("grok", GitCheckout, 0, "62707b8ac684efac35d301dbde57dc750880268e"), + + toData("nim-bytes2human", GitDiff, 0, ""), + toData("nim-bytes2human", GitTags, 0, ""), + toData("nim-bytes2human", GitCurrentcommit, 0, "ec2c1a758cabdd4751a06c8ebf2b923f19e32731\n") + ] + +#[ +Current directory is now E:\atlastest\nim-bytes2human +cmd git diff args [] --> ("", 0) +cmd git show-ref --tags args [] --> ("", 1) +cmd git log -n 1 --format=%H args [] --> (, 0) +[Warning] (nim-bytes2human) package has no tagged releases +nimble E:\atlastest\nim-bytes2human\bytes2human.nimble info (requires: @["nim >= 1.0.0"], srcDir: "src", tasks: @[]) +[Error] There were problems. +Error: execution of an external program failed: 'E:\nim\tools\atlas\atlas.exe clone https://github.com/disruptek/balls' +]# diff --git a/tools/atlas/tests/balls.nimble b/tools/atlas/tests/balls.nimble new file mode 100644 index 000000000..62ab4750b --- /dev/null +++ b/tools/atlas/tests/balls.nimble @@ -0,0 +1,32 @@ +version = "3.4.1" +author = "disruptek" +description = "a unittest framework with balls 🔴🟡🟢" +license = "MIT" + +# requires newTreeFrom +requires "https://github.com/disruptek/grok >= 0.5.0 & < 1.0.0" +requires "https://github.com/disruptek/ups < 1.0.0" +requires "https://github.com/planetis-m/sync >= 1.0.0 & < 2.0.0" +#requires "https://github.com/c-blake/cligen < 2.0.0" + +bin = @["balls"] # build the binary for basic test running +installExt = @["nim"] # we need to install balls.nim also +skipDirs = @["tests"] # so stupid... who doesn't want tests? +#installFiles = @["balls.nim"] # https://github.com/nim-lang/Nim/issues/16661 + +task test, "run tests for ci": + when defined(windows): + exec "balls.cmd" + else: + exec "balls" + +task demo, "produce a demo": + exec "nim c --define:release balls.nim" + when (NimMajor, NimMinor) != (1, 0): + echo "due to nim bug #16307, use nim-1.0" + quit 1 + exec """demo docs/demo.svg "nim c --out=\$1 examples/fails.nim"""" + exec """demo docs/clean.svg "nim c --define:danger -f --out=\$1 tests/test.nim"""" + exec "nim c --define:release --define:ballsDry balls.nim" + exec """demo docs/runner.svg "balls"""" + diff --git a/tools/atlas/tests/grok.nimble b/tools/atlas/tests/grok.nimble new file mode 100644 index 000000000..1b6d77c08 --- /dev/null +++ b/tools/atlas/tests/grok.nimble @@ -0,0 +1,5 @@ +version = "0.0.4" +author = "disruptek" +description = "don't read too much into it" +license = "MIT" +requires "nim >= 1.0.0" diff --git a/tools/atlas/tests/nim-bytes2human.nimble b/tools/atlas/tests/nim-bytes2human.nimble new file mode 100644 index 000000000..9f3ae2479 --- /dev/null +++ b/tools/atlas/tests/nim-bytes2human.nimble @@ -0,0 +1,7 @@ +version = "0.2.2" +author = "Juan Carlos" +description = "Convert bytes to kilobytes, megabytes, gigabytes, etc." +license = "MIT" +srcDir = "src" + +requires "nim >= 1.0.0" # https://github.com/juancarlospaco/nim-bytes2human/issues/2#issue-714338524 diff --git a/tools/atlas/tests/nim.cfg b/tools/atlas/tests/nim.cfg new file mode 100644 index 000000000..5f568569b --- /dev/null +++ b/tools/atlas/tests/nim.cfg @@ -0,0 +1,11 @@ +############# begin Atlas config section ########## +--noNimblePath +--path:"../balls" +--path:"../grok" +--path:"../ups" +--path:"../sync" +--path:"../npeg/src" +--path:"../testes" +--path:"../grok" +--path:"../nim-bytes2human/src" +############# end Atlas config section ########## diff --git a/tools/atlas/tests/npeg.nimble b/tools/atlas/tests/npeg.nimble new file mode 100644 index 000000000..e71fc5aa5 --- /dev/null +++ b/tools/atlas/tests/npeg.nimble @@ -0,0 +1,48 @@ +# Package + +version = "0.24.1" +author = "Ico Doornekamp" +description = "a PEG library" +license = "MIT" +srcDir = "src" +installExt = @["nim"] + +# Dependencies + +requires "nim >= 0.19.0" + +# Test + +task test, "Runs the test suite": + exec "nimble testc && nimble testcpp && nimble testarc && nimble testjs" + +task testc, "C tests": + exec "nim c -r tests/tests.nim" + +task testcpp, "CPP tests": + exec "nim cpp -r tests/tests.nim" + +task testjs, "JS tests": + exec "nim js -r tests/tests.nim" + +task testdanger, "Runs the test suite in danger mode": + exec "nim c -d:danger -r tests/tests.nim" + +task testwin, "Mingw tests": + exec "nim c -d:mingw tests/tests.nim && wine tests/tests.exe" + +task test32, "32 bit tests": + exec "nim c --cpu:i386 --passC:-m32 --passL:-m32 tests/tests.nim && tests/tests" + +task testall, "Test all": + exec "nimble test && nimble testcpp && nimble testdanger && nimble testjs && nimble testwin" + +when (NimMajor, NimMinor) >= (1, 1): + task testarc, "--gc:arc tests": + exec "nim c --gc:arc -r tests/tests.nim" +else: + task testarc, "--gc:arc tests": + exec "true" + +task perf, "Test performance": + exec "nim cpp -r -d:danger tests/performance.nim" diff --git a/tools/atlas/tests/packages/packages.json b/tools/atlas/tests/packages/packages.json new file mode 100644 index 000000000..d054a201b --- /dev/null +++ b/tools/atlas/tests/packages/packages.json @@ -0,0 +1,36 @@ +[ + { + "name": "bytes2human", + "url": "https://github.com/juancarlospaco/nim-bytes2human", + "method": "git", + "tags": [ + "bytes", + "human", + "minimalism", + "size" + ], + "description": "Convert bytes to kilobytes, megabytes, gigabytes, etc.", + "license": "LGPLv3", + "web": "https://github.com/juancarlospaco/nim-bytes2human" + }, + { + "name": "npeg", + "url": "https://github.com/zevv/npeg", + "method": "git", + "tags": [ + "PEG", + "parser", + "parsing", + "regexp", + "regular", + "grammar", + "lexer", + "lexing", + "pattern", + "matching" + ], + "description": "PEG (Parsing Expression Grammars) string matching library for Nim", + "license": "MIT", + "web": "https://github.com/zevv/npeg" + } +] diff --git a/tools/atlas/tests/sync.nimble b/tools/atlas/tests/sync.nimble new file mode 100644 index 000000000..a07ae8925 --- /dev/null +++ b/tools/atlas/tests/sync.nimble @@ -0,0 +1,10 @@ +# Package + +version = "1.4.0" +author = "Antonis Geralis" +description = "Useful synchronization primitives." +license = "MIT" + +# Deps + +requires "nim >= 1.0.0" diff --git a/tools/atlas/tests/testes.nimble b/tools/atlas/tests/testes.nimble new file mode 100644 index 000000000..60fe1d508 --- /dev/null +++ b/tools/atlas/tests/testes.nimble @@ -0,0 +1,23 @@ +version = "1.0.0" +author = "disruptek" +description = "a cure for salty testes" +license = "MIT" + +#requires "cligen >= 0.9.41 & <= 0.9.45" +#requires "bump >= 1.8.18 & < 2.0.0" +requires "https://github.com/disruptek/grok >= 0.0.4 & < 1.0.0" +requires "https://github.com/juancarlospaco/nim-bytes2human" + +bin = @["testes"] # build the binary for basic test running +installExt = @["nim"] # we need to install testes.nim also +skipDirs = @["tests"] # so stupid... who doesn't want tests? + +task test, "run tests for ci": + exec "nim c --run testes.nim" + +task demo, "produce a demo": + when (NimMajor, NimMinor) != (1, 0): + echo "due to nim bug #16307, use nim-1.0" + quit 1 + exec """demo docs/demo.svg "nim c --out=\$1 examples/balls.nim"""" + exec """demo docs/clean.svg "nim c --define:danger --out=\$1 tests/testicles.nim"""" diff --git a/tools/atlas/tests/ups.nimble b/tools/atlas/tests/ups.nimble new file mode 100644 index 000000000..d91abbe60 --- /dev/null +++ b/tools/atlas/tests/ups.nimble @@ -0,0 +1,13 @@ +version = "0.0.2" +author = "disruptek" +description = "a package handler" +license = "MIT" + +requires "npeg >= 0.23.2 & < 1.0.0" +requires "https://github.com/disruptek/testes >= 1.0.0 & < 2.0.0" + +task test, "run tests": + when defined(windows): + exec "testes.cmd" + else: + exec findExe"testes" From 4eca84cff7a16aa227b1b412d84dcecacc47dad5 Mon Sep 17 00:00:00 2001 From: Miran Date: Tue, 27 Jul 2021 01:13:47 +0200 Subject: [PATCH 2390/3768] don't use plus instead of space (#18589) --- compiler/docgen.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 492668543..3bc01bbe6 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -919,9 +919,9 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags) = sortName = if k in routineKinds: plainName.strip else: name cleanPlainSymbol = renderPlainSymbolName(nameNode) complexSymbol = complexName(k, n, cleanPlainSymbol) - plainSymbolEnc = encodeUrl(cleanPlainSymbol) + plainSymbolEnc = encodeUrl(cleanPlainSymbol, usePlus = false) symbolOrId = d.newUniquePlainSymbol(complexSymbol) - symbolOrIdEnc = encodeUrl(symbolOrId) + symbolOrIdEnc = encodeUrl(symbolOrId, usePlus = false) deprecationMsg = genDeprecationMsg(d, pragmaNode) nodeToHighlightedHtml(d, n, result, {renderNoBody, renderNoComments, From 22776c410a916b29c05b798523c94c834c7371ad Mon Sep 17 00:00:00 2001 From: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Date: Tue, 27 Jul 2021 01:15:21 +0200 Subject: [PATCH 2391/3768] Update documentation for dynlib (#17356) * Update documentation for dynlib * Apply suggestions * Improve error handling in example --- lib/pure/dynlib.nim | 78 ++++++++++++++++++++------------------------- 1 file changed, 34 insertions(+), 44 deletions(-) diff --git a/lib/pure/dynlib.nim b/lib/pure/dynlib.nim index 3c1466322..48fd91b8f 100644 --- a/lib/pure/dynlib.nim +++ b/lib/pure/dynlib.nim @@ -17,75 +17,64 @@ ## Loading a simple C function ## --------------------------- ## -## The following example demonstrates loading a function called 'greet' +## The following example demonstrates loading a function called `greet` ## from a library that is determined at runtime based upon a language choice. -## If the library fails to load or the function 'greet' is not found, +## If the library fails to load or the function `greet` is not found, ## it quits with a failure error code. ## -## .. code-block:: Nim -## -## import std/dynlib -## -## type -## greetFunction = proc(): cstring {.gcsafe, stdcall.} -## -## let lang = stdin.readLine() -## -## let lib = case lang -## of "french": -## loadLib("french.dll") -## else: -## loadLib("english.dll") -## -## if lib == nil: -## echo "Error loading library" -## quit(QuitFailure) -## -## let greet = cast[greetFunction](lib.symAddr("greet")) -## -## if greet == nil: -## echo "Error loading 'greet' function from library" -## quit(QuitFailure) -## -## let greeting = greet() -## -## echo greeting -## -## unloadLib(lib) -## +runnableExamples: + type + GreetFunction = proc (): cstring {.gcsafe, stdcall.} -import strutils + proc loadGreet(lang: string) = + let lib = + case lang + of "french": + loadLib("french.dll") + else: + loadLib("english.dll") + assert lib != nil, "Error loading library" + + let greet = cast[GreetFunction](lib.symAddr("greet")) + assert greet != nil, "Error loading 'greet' function from library" + + echo greet() + + unloadLib(lib) + + +import std/strutils type - LibHandle* = pointer ## a handle to a dynamically loaded library + LibHandle* = pointer ## A handle to a dynamically loaded library. proc loadLib*(path: string, globalSymbols = false): LibHandle {.gcsafe.} - ## loads a library from `path`. Returns nil if the library could not + ## Loads a library from `path`. Returns nil if the library could not ## be loaded. proc loadLib*(): LibHandle {.gcsafe.} - ## gets the handle from the current executable. Returns nil if the + ## Gets the handle from the current executable. Returns nil if the ## library could not be loaded. proc unloadLib*(lib: LibHandle) {.gcsafe.} - ## unloads the library `lib` + ## Unloads the library `lib`. proc raiseInvalidLibrary*(name: cstring) {.noinline, noreturn.} = - ## raises an `EInvalidLibrary` exception. + ## Raises a `LibraryError` exception. raise newException(LibraryError, "could not find symbol: " & $name) proc symAddr*(lib: LibHandle, name: cstring): pointer {.gcsafe.} - ## retrieves the address of a procedure/variable from `lib`. Returns nil + ## Retrieves the address of a procedure/variable from `lib`. Returns nil ## if the symbol could not be found. proc checkedSymAddr*(lib: LibHandle, name: cstring): pointer = - ## retrieves the address of a procedure/variable from `lib`. Raises - ## `EInvalidLibrary` if the symbol could not be found. + ## Retrieves the address of a procedure/variable from `lib`. Raises + ## `LibraryError` if the symbol could not be found. result = symAddr(lib, name) if result == nil: raiseInvalidLibrary(name) proc libCandidates*(s: string, dest: var seq[string]) = - ## given a library name pattern `s` write possible library names to `dest`. + ## Given a library name pattern `s`, write possible library names to `dest`. var le = strutils.find(s, '(') var ri = strutils.find(s, ')', le+1) if le >= 0 and ri > le: @@ -97,8 +86,9 @@ proc libCandidates*(s: string, dest: var seq[string]) = add(dest, s) proc loadLibPattern*(pattern: string, globalSymbols = false): LibHandle = - ## loads a library with name matching `pattern`, similar to what `dynlib` + ## Loads a library with name matching `pattern`, similar to what the `dynlib` ## pragma does. Returns nil if the library could not be loaded. + ## ## .. warning:: this proc uses the GC and so cannot be used to load the GC. var candidates = newSeq[string]() libCandidates(pattern, candidates) From 9cb5ab0108c62bfda30b4f9b52b2957fdd364544 Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 27 Jul 2021 13:21:39 +0800 Subject: [PATCH 2392/3768] fix #18578 (#18580) * fix #18578 * add tests * tiny * apply changes * typo * add removeStaticFile --- compiler/extccomp.nim | 15 +++++++++------ tests/compiles/mstaticlib.nim | 1 + tests/compiles/tstaticlib.nim | 22 ++++++++++++++++++++++ 3 files changed, 32 insertions(+), 6 deletions(-) create mode 100644 tests/compiles/mstaticlib.nim create mode 100644 tests/compiles/tstaticlib.nim diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 831acae68..a091060ff 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -665,9 +665,10 @@ proc addExternalFileToCompile*(conf: ConfigRef; filename: AbsoluteFile) = addExternalFileToCompile(conf, c) proc getLinkCmd(conf: ConfigRef; output: AbsoluteFile, - objfiles: string, isDllBuild: bool): string = + objfiles: string, isDllBuild: bool, removeStaticFile: bool): string = if optGenStaticLib in conf.globalOptions: - removeFile output # fixes: bug #16947 + if removeStaticFile: + removeFile output # fixes: bug #16947 result = CC[conf.cCompiler].buildLib % ["libfile", quoteShell(output), "objfiles", objfiles] else: @@ -750,8 +751,9 @@ proc getLinkCmd(conf: ConfigRef; output: AbsoluteFile, if optCDebug in conf.globalOptions and conf.cCompiler == ccVcc: result.add " /Zi /FS /Od" -template getLinkCmd(conf: ConfigRef; output: AbsoluteFile, objfiles: string): string = - getLinkCmd(conf, output, objfiles, optGenDynLib in conf.globalOptions) +template getLinkCmd(conf: ConfigRef; output: AbsoluteFile, objfiles: string, + removeStaticFile = false): string = + getLinkCmd(conf, output, objfiles, optGenDynLib in conf.globalOptions, removeStaticFile) template tryExceptOSErrorMessage(conf: ConfigRef; errorPrefix: string = "", body: untyped) = try: @@ -891,7 +893,7 @@ proc callCCompiler*(conf: ConfigRef) = let objFile = conf.getObjFilePath(x) let buildDll = idx != mainFileIdx let linkTarget = conf.hcrLinkTargetName(objFile, not buildDll) - cmds.add(getLinkCmd(conf, linkTarget, objfiles & " " & quoteShell(objFile), buildDll)) + cmds.add(getLinkCmd(conf, linkTarget, objfiles & " " & quoteShell(objFile), buildDll, removeStaticFile = true)) # try to remove all .pdb files for the current binary so they don't accumulate endlessly in the nimcache # for more info check the comment inside of getLinkCmd() where the /PDB: MSVC flag is used if isVSCompatible(conf): @@ -914,7 +916,8 @@ proc callCCompiler*(conf: ConfigRef) = objfiles.add(quoteShell(objFile)) let mainOutput = if optGenScript notin conf.globalOptions: conf.prepareToWriteOutput else: AbsoluteFile(conf.projectName) - linkCmd = getLinkCmd(conf, mainOutput, objfiles) + + linkCmd = getLinkCmd(conf, mainOutput, objfiles, removeStaticFile = true) extraCmds = getExtraCmds(conf, mainOutput) if optCompileOnly notin conf.globalOptions: const MaxCmdLen = when defined(windows): 8_000 else: 32_000 diff --git a/tests/compiles/mstaticlib.nim b/tests/compiles/mstaticlib.nim new file mode 100644 index 000000000..6ed593691 --- /dev/null +++ b/tests/compiles/mstaticlib.nim @@ -0,0 +1 @@ +echo 1234 \ No newline at end of file diff --git a/tests/compiles/tstaticlib.nim b/tests/compiles/tstaticlib.nim new file mode 100644 index 000000000..a18b59204 --- /dev/null +++ b/tests/compiles/tstaticlib.nim @@ -0,0 +1,22 @@ +import std/[os, osproc, strformat] + + +const dir = "tests/compiles" +const fileName = dir / "mstaticlib.nim" +const nim = getCurrentCompilerExe() + +block: # bug #18578 + const libName = dir / "tstaticlib1.a" + let (_, status) = execCmdEx(fmt"{nim} c -o:{libName} --app:staticlib {fileName}") + doAssert status == 0 + doAssert fileExists(libName) + removeFile(libName) + +block: # bug #16947 + const libName = dir / "tstaticlib2.a" + writeFile(libName, "echo 124") + doAssert fileExists(libName) + let (_, status) = execCmdEx(fmt"{nim} c -o:{libName} --app:staticlib {fileName}") + doAssert status == 0 + doAssert fileExists(libName) + removeFile(libName) From fa0209609d3bedd3466f162aa350d261907851ce Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 27 Jul 2021 09:36:19 +0200 Subject: [PATCH 2393/3768] fixes #18565 (#18593) * fixes #18565 --- compiler/semstmts.nim | 22 +++++++++++----------- compiler/sigmatch.nim | 4 +++- tests/arc/tkeys_lent.nim | 17 +++++++++++++++++ tests/stdlib/tgetfileinfo.nim | 1 + tests/stdlib/tos.nim | 1 + 5 files changed, 33 insertions(+), 12 deletions(-) create mode 100644 tests/arc/tkeys_lent.nim diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index bfce91849..75323a95e 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -2260,20 +2260,20 @@ proc semStmtList(c: PContext, n: PNode, flags: TExprFlags): PNode = # nkNilLit, nkEmpty}: # dec last for i in 0.. Date: Tue, 27 Jul 2021 01:36:59 -0600 Subject: [PATCH 2394/3768] Fixed const tuples in inferred generics (#18598) --- compiler/semgnrc.nim | 33 ++++++++++++------------ tests/tuples/tinferred_generic_const.nim | 14 ++++++++++ 2 files changed, 30 insertions(+), 17 deletions(-) create mode 100644 tests/tuples/tinferred_generic_const.nim diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index 4f3c50dae..3df1e0c3f 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -391,16 +391,24 @@ proc semGenericStmt(c: PContext, n: PNode, a[^1] = semGenericStmtScope(c, a[^1], flags, ctx) closeScope(c) - of nkVarSection, nkLetSection: + of nkVarSection, nkLetSection, nkConstSection: + let varKind = + case n.kind + of nkVarSection: skVar + of nkLetSection: skLet + else: skConst for i in 0.. Date: Tue, 27 Jul 2021 00:37:11 -0700 Subject: [PATCH 2395/3768] add comment to astalgo.debug (#18594) --- compiler/astalgo.nim | 3 +++ 1 file changed, 3 insertions(+) diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 3c97665f0..94fa9da93 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -585,6 +585,9 @@ proc value(this: var DebugPrinter; value: PNode) = this.openCurly this.key "kind" this.value value.kind + if value.comment.len > 0: + this.key "comment" + this.value value.comment when defined(useNodeIds): this.key "id" this.value value.id From 37f5f0d7d08d6057b20d3e319b38dfe82ce1ba65 Mon Sep 17 00:00:00 2001 From: GordonBGood Date: Tue, 27 Jul 2021 14:38:30 +0700 Subject: [PATCH 2396/3768] Clarify use of import via pseudo dir... (#18569) --- doc/manual.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/manual.rst b/doc/manual.rst index 9f614c2cd..1b65ae9ee 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -6190,6 +6190,7 @@ There are two pseudo directories: its semantics are: *Use the search path to look for module name but ignore the standard library locations*. In other words, it is the opposite of `std`. +It is recommended and preferred but not currently enforced that all stdlib module imports include the std/ "pseudo directory" as part of the import name. From import statement --------------------- From 493721c16c06b5681dc270679bdcbb41011614b2 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 27 Jul 2021 00:56:16 -0700 Subject: [PATCH 2397/3768] delEnv now works at CT (#18568) --- compiler/vmops.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/vmops.nim b/compiler/vmops.nim index bc331bbcd..6ee915bd5 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -20,7 +20,7 @@ when declared(math.copySign): when declared(math.signbit): from std/math import signbit -from std/os import getEnv, existsEnv, dirExists, fileExists, putEnv, walkDir, +from std/os import getEnv, existsEnv, delEnv, putEnv, dirExists, fileExists, walkDir, getAppFilename, raiseOSError, osLastError from std/md5 import getMD5 @@ -215,6 +215,7 @@ proc registerAdditionalOps*(c: PCtx) = wrap2s(getEnv, osop) wrap1s(existsEnv, osop) wrap2svoid(putEnv, osop) + wrap1svoid(delEnv, osop) wrap1s(dirExists, osop) wrap1s(fileExists, osop) wrapDangerous(writeFile, ioop) From c86f9590fb5ba6512ba33994fa0711341eb8524b Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 27 Jul 2021 12:48:27 +0200 Subject: [PATCH 2398/3768] fixes #18570 (#18599) --- compiler/injectdestructors.nim | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index cf09b3ff5..985f27878 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -799,7 +799,8 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode = result[i] = p(n[i], c, s, m) of nkObjConstr: # see also the remark about `nkTupleConstr`. - let isRefConstr = n.typ.skipTypes(abstractInst).kind == tyRef + let t = n.typ.skipTypes(abstractInst) + let isRefConstr = t.kind == tyRef let m = if isRefConstr: sinkArg elif mode == normal: normal else: sinkArg @@ -807,7 +808,7 @@ proc p(n: PNode; c: var Con; s: var Scope; mode: ProcessMode): PNode = result = copyTree(n) for i in 1.. Date: Tue, 27 Jul 2021 14:32:56 +0200 Subject: [PATCH 2399/3768] fixes #18579 (#18600) --- lib/system/arc.nim | 6 ++---- tests/arc/tunref_cycle.nim | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 4 deletions(-) create mode 100644 tests/arc/tunref_cycle.nim diff --git a/lib/system/arc.nim b/lib/system/arc.nim index ed5e9f5ca..d66f4b997 100644 --- a/lib/system/arc.nim +++ b/lib/system/arc.nim @@ -207,10 +207,8 @@ proc nimDecRefIsLast(p: pointer): bool {.compilerRtl, inl.} = proc GC_unref*[T](x: ref T) = ## New runtime only supports this operation for 'ref T'. - if nimDecRefIsLast(cast[pointer](x)): - # XXX this does NOT work for virtual destructors! - `=destroy`(x[]) - nimRawDispose(cast[pointer](x), T.alignOf) + var y {.cursor.} = x + `=destroy`(y) proc GC_ref*[T](x: ref T) = ## New runtime only supports this operation for 'ref T'. diff --git a/tests/arc/tunref_cycle.nim b/tests/arc/tunref_cycle.nim new file mode 100644 index 000000000..82551b7f7 --- /dev/null +++ b/tests/arc/tunref_cycle.nim @@ -0,0 +1,26 @@ +discard """ + outputsub: '''inside closure +hello world''' + cmd: "nim c --gc:orc -d:useMalloc $file" + valgrind: true +""" + +# bug #18579 + +var fp: proc (env: pointer) {.cdecl.} +var env: pointer + +proc store(f: proc (){.closure.}) = + proc closeOver() = + echo "inside closure" + f() + (fp,env) = (cast[proc(env: pointer){.cdecl.}](rawProc closeOver), rawEnv closeOver) + GC_ref(cast[RootRef](env)) + +proc run() = + fp(env) + GC_unref(cast[RootRef](env)) + +store(proc() = echo "hello world") +run() +GC_fullCollect() From 4920b06973e69c525405ff3147dc8a823a4c3c58 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 27 Jul 2021 19:04:55 +0200 Subject: [PATCH 2400/3768] fixes #18543 (#18601) * fixes #18543 * make tests green again --- changelog.md | 1 - lib/core/macros.nim | 264 +++++++++++-------------------- testament/important_packages.nim | 1 + tests/pragmas/tcustom_pragma.nim | 25 +-- 4 files changed, 107 insertions(+), 184 deletions(-) diff --git a/changelog.md b/changelog.md index d33e24b65..89e013cff 100644 --- a/changelog.md +++ b/changelog.md @@ -51,7 +51,6 @@ implementations. Old behavior can be obtained with `-d:nimLegacyParseQueryStrict`. `cgi.decodeData` which uses the same underlying code is also updated the same way. -- Custom pragma values have now an API for use in macros. - On POSIX systems, the default signal handlers used for Nim programs (it's used for printing the stacktrace on fatal signals) will now re-raise the diff --git a/lib/core/macros.nim b/lib/core/macros.nim index c09fae6b3..d3eaf6298 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -1068,11 +1068,11 @@ proc newEmptyNode*(): NimNode {.noSideEffect.} = ## Create a new empty node. result = newNimNode(nnkEmpty) -proc newStmtList*(stmts: varargs[NimNode]): NimNode= +proc newStmtList*(stmts: varargs[NimNode]): NimNode = ## Create a new statement list. result = newNimNode(nnkStmtList).add(stmts) -proc newPar*(exprs: varargs[NimNode]): NimNode= +proc newPar*(exprs: varargs[NimNode]): NimNode = ## Create a new parentheses-enclosed expression. newNimNode(nnkPar).add(exprs) @@ -1494,148 +1494,80 @@ macro expandMacros*(body: typed): untyped = echo body.toStrLit result = body -proc findPragmaNodeInRecList(arg, fieldSym: NimNode): NimNode = - case arg.kind - of nnkRecList, nnkRecCase: - for it in arg.children: - result = findPragmaNodeInRecList(it, fieldSym) - if result != nil: return - of nnkOfBranch: - return findPragmaNodeInRecList(arg[1], fieldSym) - of nnkElse: - return findPragmaNodeInRecList(arg[0], fieldSym) - of nnkIdentDefs: - for i in 0.. 0: - impl.expectKind nnkTypeDef - let pragmaExpr = impl[0] - if pragmaExpr.kind == nnkPragmaExpr: - result = pragmaExpr[1] - -proc getPragmaNodeFromType(typ: NimNode): NimNode = - case typ.kind - of nnkSym: - result = getPragmaNodeFromTypeSym(typ) - of nnkProcTy: - result = typ[1] - else: error("illegal typ kind for argument: " & $typ.kind, typ) - -proc getPragmaNodeFromVarLetSym(sym: NimNode): NimNode = - sym.expectKind nnkSym - if sym.symKind notin {nskVar, nskLet}: error("expected var/let sym", sym) - let impl = sym.getImpl - impl.expectKind nnkIdentDefs - impl.expectLen 3 - let pragmaExpr = impl[0] - if pragmaExpr.kind == nnkPragmaExpr: - result = pragmaExpr[1] - -proc getPragmasByName(pragmaExpr: NimNode, name: string): seq[NimNode] = - if pragmaExpr.kind == nnkPragma: - for it in pragmaExpr: - if it.kind in nnkPragmaCallKinds: - if eqIdent(it[0], name): - result.add it - elif it.kind == nnkSym: - if eqIdent(it, name): - result.add it - -proc getCustomPragmaNodes(sym: NimNode, name: string): seq[NimNode] = - sym.expectKind nnkSym - case sym.symKind - of nskField: - result = getPragmaNodeFromObjFieldSym(sym).getPragmasByName(name) - of nskProc: - result = getPragmaNodeFromProcSym(sym).getPragmasByName(name) - of nskType: - result = getPragmaNodeFromTypeSym(sym).getPragmasByName(name) - of nskParam: - # When a typedesc parameter is passed to the macro, it will be of nskParam. - let typeInst = getTypeInst(sym) - if typeInst.kind == nnkBracketExpr and eqIdent(typeInst[0], "typeDesc"): - result = getPragmaNodeFromTypeSym(typeInst[1]).getPragmasByName(name) + if typ.kind == nnkBracketExpr and typ.len > 1 and typ[1].kind == nnkProcTy: + return typ[1][1] + elif typ.typeKind == ntyTypeDesc: + let impl = typ[1].getImpl() + if impl[0].kind == nnkPragmaExpr: + return impl[0][1] else: - error("illegal sym kind for argument: " & $sym.symKind, sym) - of nskVar, nskLet: - # This checks the type of the sym too, this is consistent with how - # field expressions are handled too. If this is changed, make sure to - # change it for fields expressions too. - result = getPragmaNodeFromType(sym.getTypeInst).getPragmasByName(name) - result.add getPragmaNodeFromVarLetSym(sym).getPragmasByName(name) - else: - error("illegal sym kind for argument: " & $sym.symKind, sym) + return impl[0] # handle types which don't have macro at all -since (1, 5): - export getCustomPragmaNodes + if n.kind == nnkSym: # either an variable or a proc + let impl = n.getImpl() + if impl.kind in RoutineNodes: + return impl.pragma + elif impl.kind == nnkIdentDefs and impl[0].kind == nnkPragmaExpr: + return impl[0][1] + else: + let timpl = typ.getImpl() + if timpl.len>0 and timpl[0].len>1: + return timpl[0][1] + else: + return timpl -proc hasCustomPragma*(n: NimNode, name: string): bool = - n.expectKind nnkSym - result = getCustomPragmaNodes(n, name).len > 0 + if n.kind in {nnkDotExpr, nnkCheckedFieldExpr}: + let name = $(if n.kind == nnkCheckedFieldExpr: n[0][1] else: n[1]) + let typInst = getTypeInst(if n.kind == nnkCheckedFieldExpr or n[0].kind == nnkHiddenDeref: n[0][0] else: n[0]) + var typDef = getImpl(if typInst.kind == nnkVarTy: typInst[0] else: typInst) + while typDef != nil: + typDef.expectKind(nnkTypeDef) + let typ = typDef[2] + typ.expectKind({nnkRefTy, nnkPtrTy, nnkObjectTy}) + let isRef = typ.kind in {nnkRefTy, nnkPtrTy} + if isRef and typ[0].kind in {nnkSym, nnkBracketExpr}: # defines ref type for another object(e.g. X = ref X) + typDef = getImpl(typ[0]) + else: # object definition, maybe an object directly defined as a ref type + let + obj = (if isRef: typ[0] else: typ) + var identDefsStack = newSeq[NimNode](obj[2].len) + for i in 0.. 0: + var identDefs = identDefsStack.pop() + if identDefs.kind == nnkRecCase: + identDefsStack.add(identDefs[0]) + for i in 1.. 0) + let pragmaNode = customPragmaNode(n) + for p in pragmaNode: + if (p.kind == nnkSym and p == cp) or + (p.kind in nnkPragmaCallKinds and p.len > 0 and p[0].kind == nnkSym and p[0] == cp): + return newLit(true) + return newLit(false) -iterator iterOverFormalArgs(f: NimNode): tuple[name, typ, val: NimNode] = - f.expectKind nnkFormalParams - for i in 1.. 0 and p[0].kind == nnkSym and p[0] == cp: + if p.len == 2: + result = p[1] + else: + let def = p[0].getImpl[3] + result = newTree(nnkPar) + for i in 1 ..< def.len: + let key = def[i][0] + let val = p[i] + result.add newTree(nnkExprColonExpr, key, val) + break + if result.kind == nnkEmpty: + error(n.repr & " doesn't have a pragma named " & cp.repr()) # returning an empty node results in most cases in a cryptic error, macro unpackVarargs*(callee: untyped; args: varargs[untyped]): untyped = ## Calls `callee` with `args` unpacked as individual arguments. diff --git a/testament/important_packages.nim b/testament/important_packages.nim index afceffb2e..ab29dcd0d 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -114,6 +114,7 @@ pkg "nimsvg" pkg "nimterop", "nimble minitest" pkg "nimwc", "nim c nimwc.nim" pkg "nimx", "nim c --threads:on test/main.nim", allowFailure = true +pkg "nimYAML", "nim c -r test/tserialization.nim" pkg "nitter", "nim c src/nitter.nim", "https://github.com/zedeus/nitter" pkg "norm", "nim c -r tests/sqlite/trows.nim" pkg "npeg", "nimble testarc" diff --git a/tests/pragmas/tcustom_pragma.nim b/tests/pragmas/tcustom_pragma.nim index 3ef5bdcb6..b197a7c55 100644 --- a/tests/pragmas/tcustom_pragma.nim +++ b/tests/pragmas/tcustom_pragma.nim @@ -384,7 +384,7 @@ block: discard Hello(a: 1.0, b: 12) # issue #11511 -block: +when false: template myAttr {.pragma.} type TObj = object @@ -395,23 +395,24 @@ block: let recList = objTy[2] let sym = recList[0] assert sym.kind == nnkSym and sym.eqIdent("a") - let hasAttr = sym.hasCustomPragma("myAttr") + let hasAttr = sym.hasCustomPragma(myAttr) newLit(hasAttr) doAssert hasMyAttr(TObj) -# misc -{.pragma: haha.} -{.pragma: hoho.} -template hehe(key, val: string, haha) {.pragma.} +when false: + # misc + {.pragma: haha.} + {.pragma: hoho.} + template hehe(key, val: string, haha) {.pragma.} -type A {.haha, hoho, haha, hehe("hi", "hu", "he").} = int + type A {.haha, hoho, haha, hehe("hi", "hu", "he").} = int -assert A.getCustomPragmaVal(hehe) == (key: "hi", val: "hu", haha: "he") + assert A.getCustomPragmaVal(hehe) == (key: "hi", val: "hu", haha: "he") -template hehe(key, val: int) {.pragma.} + template hehe(key, val: int) {.pragma.} -var bb {.haha, hoho, hehe(1, 2), haha, hehe("hi", "hu", "he").} = 3 + var bb {.haha, hoho, hehe(1, 2), haha, hehe("hi", "hu", "he").} = 3 -# left-to-right priority/override order for getCustomPragmaVal -assert bb.getCustomPragmaVal(hehe) == (key: "hi", val: "hu", haha: "he") + # left-to-right priority/override order for getCustomPragmaVal + assert bb.getCustomPragmaVal(hehe) == (key: "hi", val: "hu", haha: "he") From 8d2f6bba3a491bc93677da9590397346e63c2bb2 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 27 Jul 2021 10:50:59 -0700 Subject: [PATCH 2401/3768] support same-line doc comments in routines (#18595) * support same-line comments in routines * remove assert as per review comment --- compiler/parser.nim | 7 ++ .../expected/subdir/subdir_b/utils.html | 110 +++++++++++++++++- .../expected/subdir/subdir_b/utils.idx | 9 ++ nimdoc/testproject/expected/theindex.html | 36 ++++++ nimdoc/testproject/subdir/subdir_b/utils.nim | 17 +++ tests/stdlib/trepr.nim | 27 +++++ 6 files changed, 205 insertions(+), 1 deletion(-) diff --git a/compiler/parser.nim b/compiler/parser.nim index d00a17e9f..b089614b2 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -1795,6 +1795,13 @@ proc parseRoutine(p: var Parser, kind: TNodeKind): PNode = else: result.add(p.emptyNode) indAndComment(p, result, maybeMissEquals) + let body = result[^1] + if body.kind == nkStmtList and body.len > 0 and body[0].comment.len > 0 and body[0].kind != nkCommentStmt: + if result.comment.len == 0: + # proc fn*(a: int): int = a ## foo + # => moves comment `foo` to `fn` + swap(result.comment, body[0].comment) + else: discard # xxx either `assert false` or issue a warning (otherwise we'll never know of this edge case) proc newCommentStmt(p: var Parser): PNode = #| commentStmt = COMMENT diff --git a/nimdoc/testproject/expected/subdir/subdir_b/utils.html b/nimdoc/testproject/expected/subdir/subdir_b/utils.html index 939495ad1..533cb5b9d 100644 --- a/nimdoc/testproject/expected/subdir/subdir_b/utils.html +++ b/nimdoc/testproject/expected/subdir/subdir_b/utils.html @@ -115,7 +115,52 @@ window.addEventListener('DOMContentLoaded', main);
                      • Procs
                          -
                            someType + + + + + + + + + +
                              someType
                            • someType(): SomeType
                            • @@ -181,6 +226,69 @@ window.addEventListener('DOMContentLoaded', main);

                              Procs

                              + +
                              proc fn2() {....raises: [], tags: [].}
                              +
                              + +comment + +
                              + +
                              proc fn3(): auto {....raises: [], tags: [].}
                              +
                              + +comment + +
                              + +
                              proc fn4(): auto {....raises: [], tags: [].}
                              +
                              + +comment + +
                              + +
                              proc fn5() {....raises: [], tags: [].}
                              +
                              + +comment + +
                              + +
                              proc fn6() {....raises: [], tags: [].}
                              +
                              + +comment + +
                              + +
                              proc fn7() {....raises: [], tags: [].}
                              +
                              + +comment + +
                              + +
                              proc fn8(): auto {....raises: [], tags: [].}
                              +
                              + +comment + +
                              + +
                              func fn9(a: int): int {....raises: [], tags: [].}
                              +
                              + +comment + +
                              + +
                              func fn10(a: int): int {....raises: [], tags: [].}
                              +
                              + +comment + +
                              proc someType(): SomeType {....raises: [], tags: [].}
                              diff --git a/nimdoc/testproject/expected/subdir/subdir_b/utils.idx b/nimdoc/testproject/expected/subdir/subdir_b/utils.idx index 6dc3953af..a87393f09 100644 --- a/nimdoc/testproject/expected/subdir/subdir_b/utils.idx +++ b/nimdoc/testproject/expected/subdir/subdir_b/utils.idx @@ -3,6 +3,15 @@ enumValueB subdir/subdir_b/utils.html#enumValueB SomeType.enumValueB enumValueC subdir/subdir_b/utils.html#enumValueC SomeType.enumValueC SomeType subdir/subdir_b/utils.html#SomeType utils: SomeType someType subdir/subdir_b/utils.html#someType_2 utils: someType(): SomeType +fn2 subdir/subdir_b/utils.html#fn2 utils: fn2() +fn3 subdir/subdir_b/utils.html#fn3 utils: fn3(): auto +fn4 subdir/subdir_b/utils.html#fn4 utils: fn4(): auto +fn5 subdir/subdir_b/utils.html#fn5 utils: fn5() +fn6 subdir/subdir_b/utils.html#fn6 utils: fn6() +fn7 subdir/subdir_b/utils.html#fn7 utils: fn7() +fn8 subdir/subdir_b/utils.html#fn8 utils: fn8(): auto +fn9 subdir/subdir_b/utils.html#fn9,int utils: fn9(a: int): int +fn10 subdir/subdir_b/utils.html#fn10,int utils: fn10(a: int): int aEnum subdir/subdir_b/utils.html#aEnum.t utils: aEnum(): untyped bEnum subdir/subdir_b/utils.html#bEnum.t utils: bEnum(): untyped fromUtilsGen subdir/subdir_b/utils.html#fromUtilsGen.t utils: fromUtilsGen(): untyped diff --git a/nimdoc/testproject/expected/theindex.html b/nimdoc/testproject/expected/theindex.html index cfaf6856d..f72f6c37e 100644 --- a/nimdoc/testproject/expected/theindex.html +++ b/nimdoc/testproject/expected/theindex.html @@ -171,6 +171,42 @@ window.addEventListener('DOMContentLoaded', main);
                            • SomeType.enumValueC
                            +
                            fn10:
                            +
                            fn2:
                            +
                            fn3:
                            +
                            fn4:
                            +
                            fn5:
                            +
                            fn6:
                            +
                            fn7:
                            +
                            fn8:
                            +
                            fn9:
                            Foo:
                            • testproject: Foo
                            • diff --git a/nimdoc/testproject/subdir/subdir_b/utils.nim b/nimdoc/testproject/subdir/subdir_b/utils.nim index 4e3aa1f10..37c91dd3c 100644 --- a/nimdoc/testproject/subdir/subdir_b/utils.nim +++ b/nimdoc/testproject/subdir/subdir_b/utils.nim @@ -31,6 +31,23 @@ proc someType*(): SomeType = ## constructor. SomeType(2) + +proc fn2*() = discard ## comment +proc fn3*(): auto = 1 ## comment +proc fn4*(): auto = 2 * 3 + 4 ## comment +proc fn5*() ## comment +proc fn5*() = discard +proc fn6*() = + ## comment +proc fn7*() = + ## comment + discard +proc fn8*(): auto = + ## comment + 1+1 +func fn9*(a: int): int = 42 ## comment +func fn10*(a: int): int = a ## comment + # bug #9235 template aEnum*(): untyped = diff --git a/tests/stdlib/trepr.nim b/tests/stdlib/trepr.nim index 356764903..83ae7b119 100644 --- a/tests/stdlib/trepr.nim +++ b/tests/stdlib/trepr.nim @@ -9,6 +9,7 @@ from strutils import endsWith, contains, strip from std/macros import newLit macro deb(a): string = newLit a.repr.strip +macro debTyped(a: typed): string = newLit a.repr.strip template main() = doAssert repr({3,5}) == "{3, 5}" @@ -243,5 +244,31 @@ template bar(): untyped = echo "baz" 4)""" + block: # one liner doc comments + let a1 = deb: + func fn1(): int = 1 ## comment + func fn2(): int = 1 + ## comment + let a2 = debTyped: + func fn1(): int = 1 ## comment + func fn2(): int = 1 + ## comment + doAssert a1 == """ +func fn1(): int = + ## comment + 1 + +func fn2(): int = + ## comment + 1""" + doAssert a2 == """ +func fn1(): int = + ## comment + result = 1 + +func fn2(): int = + ## comment + result = 1""" + static: main() main() From 4c1202972abdfe99232e5d15a6169c7b2e0f5d75 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 27 Jul 2021 21:38:19 -0700 Subject: [PATCH 2402/3768] fix #17072: add times.dateTime with ISO 8601 order (#18590) * fix #17072: add times.dateTime with ISO 8601 order * address comments --- changelog.md | 6 +++++- lib/pure/times.nim | 29 ++++++++++++++++++----------- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/changelog.md b/changelog.md index 89e013cff..be94f92c4 100644 --- a/changelog.md +++ b/changelog.md @@ -331,9 +331,12 @@ - Added `htmlgen.portal` for [making "SPA style" pages using HTML only](https://web.dev/hands-on-portals). -- Added `ZZZ` and `ZZZZ` patterns to `times.nim` `DateTime` parsing, to match time +- `std/times`: + Added `ZZZ` and `ZZZZ` patterns to `times.nim` `DateTime` parsing, to match time zone offsets without colons, e.g. `UTC+7 -> +0700`. + Added `dateTime` and deprecated `initDateTime`. + - Added `jsconsole.dir`, `jsconsole.dirxml`, `jsconsole.timeStamp`. - Added dollar `$` and `len` for `jsre.RegExp`. @@ -357,6 +360,7 @@ - Added `dom.setInterval`, `dom.clearInterval` overloads. + - Deprecated `sequtils.delete` and added an overload taking a `Slice` that raises a defect if the slice is out of bounds, likewise with `strutils.delete`. diff --git a/lib/pure/times.nim b/lib/pure/times.nim index fcdd4ec8d..b2a22250d 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -1333,14 +1333,13 @@ proc now*(): DateTime {.tags: [TimeEffect], benign.} = ## `cpuTime` instead, depending on the use case. getTime().local -proc initDateTime*(monthday: MonthdayRange, month: Month, year: int, - hour: HourRange, minute: MinuteRange, second: SecondRange, - nanosecond: NanosecondRange, - zone: Timezone = local()): DateTime = +proc dateTime*(year: int, month: Month, monthday: MonthdayRange, + hour: HourRange = 0, minute: MinuteRange = 0, second: SecondRange = 0, + nanosecond: NanosecondRange = 0, + zone: Timezone = local()): DateTime = ## Create a new `DateTime <#DateTime>`_ in the specified timezone. runnableExamples: - let dt1 = initDateTime(30, mMar, 2017, 00, 00, 00, 00, utc()) - doAssert $dt1 == "2017-03-30T00:00:00Z" + assert $dateTime(2017, mMar, 30, zone = utc()) == "2017-03-30T00:00:00Z" assertValidDate monthday, month, year let dt = DateTime( @@ -1356,12 +1355,20 @@ proc initDateTime*(monthday: MonthdayRange, month: Month, year: int, proc initDateTime*(monthday: MonthdayRange, month: Month, year: int, hour: HourRange, minute: MinuteRange, second: SecondRange, - zone: Timezone = local()): DateTime = + nanosecond: NanosecondRange, + zone: Timezone = local()): DateTime {.deprecated: "use `dateTime`".} = ## Create a new `DateTime <#DateTime>`_ in the specified timezone. - runnableExamples: - let dt1 = initDateTime(30, mMar, 2017, 00, 00, 00, utc()) - doAssert $dt1 == "2017-03-30T00:00:00Z" - initDateTime(monthday, month, year, hour, minute, second, 0, zone) + runnableExamples("--warning:deprecated:off"): + assert $initDateTime(30, mMar, 2017, 00, 00, 00, 00, utc()) == "2017-03-30T00:00:00Z" + dateTime(year, month, monthday, hour, minute, second, nanosecond, zone) + +proc initDateTime*(monthday: MonthdayRange, month: Month, year: int, + hour: HourRange, minute: MinuteRange, second: SecondRange, + zone: Timezone = local()): DateTime {.deprecated: "use `dateTime`".} = + ## Create a new `DateTime <#DateTime>`_ in the specified timezone. + runnableExamples("--warning:deprecated:off"): + assert $initDateTime(30, mMar, 2017, 00, 00, 00, utc()) == "2017-03-30T00:00:00Z" + dateTime(year, month, monthday, hour, minute, second, 0, zone) proc `+`*(dt: DateTime, dur: Duration): DateTime = runnableExamples: From a273ea70e8817e3509014a1b3dcd16a360ed400b Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 28 Jul 2021 12:46:28 +0200 Subject: [PATCH 2403/3768] implements overloadable enum values; WIP (#18470) * implements overloadable enum values * simpler code --- compiler/ast.nim | 2 +- compiler/lookups.nim | 7 +++-- compiler/options.nim | 3 +- compiler/sem.nim | 8 +++++ compiler/semexprs.nim | 36 +++++++++++++++++++++- compiler/semstmts.nim | 16 +++++++++- compiler/semtypes.nim | 12 ++++++-- compiler/sigmatch.nim | 2 +- tests/enum/toverloadable_enums.nim | 48 ++++++++++++++++++++++++++++++ tests/errmsgs/t10251.nim | 2 +- 10 files changed, 125 insertions(+), 11 deletions(-) create mode 100644 tests/enum/toverloadable_enums.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index 0eeaa8133..5b54dd8ef 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -993,7 +993,7 @@ type const OverloadableSyms* = {skProc, skFunc, skMethod, skIterator, - skConverter, skModule, skTemplate, skMacro} + skConverter, skModule, skTemplate, skMacro, skEnumField} GenericTypes*: TTypeKinds = {tyGenericInvocation, tyGenericBody, tyGenericParam} diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 44a30eefa..331eef525 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -177,13 +177,16 @@ iterator allSyms*(c: PContext): (PSym, int, bool) = proc someSymFromImportTable*(c: PContext; name: PIdent; ambiguous: var bool): PSym = var marked = initIntSet() + var symSet = OverloadableSyms + if overloadableEnums notin c.features: + symSet.excl skEnumField result = nil for im in c.imports.mitems: for s in symbols(im, marked, name, c.graph): if result == nil: result = s else: - if s.kind notin OverloadableSyms or result.kind notin OverloadableSyms: + if s.kind notin symSet or result.kind notin symSet: ambiguous = true proc searchInScopes*(c: PContext, s: PIdent; ambiguous: var bool): PSym = @@ -384,7 +387,7 @@ proc mergeShadowScope*(c: PContext) = ## ## Merges: ## shadow -> shadow: add symbols to the parent but check for redefinitions etc - ## shadow -> non-shadow: the above, but also handle exports and all that + ## shadow -> non-shadow: the above, but also handle exports and all that let shadowScope = c.currentScope c.rawCloseScope for sym in shadowScope.symbols: diff --git a/compiler/options.nim b/compiler/options.nim index f02f02256..e37dea1b6 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -203,7 +203,8 @@ type vmopsDanger, strictFuncs, views, - strictNotNil + strictNotNil, + overloadableEnums LegacyFeature* = enum allowSemcheckedAstModification, diff --git a/compiler/sem.nim b/compiler/sem.nim index 879f6efc9..804325e56 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -90,6 +90,12 @@ proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode = # error correction: result = copyTree(arg) result.typ = formal + elif arg.kind in nkSymChoices and formal.skipTypes(abstractInst).kind == tyEnum: + # Pick the right 'sym' from the sym choice by looking at 'formal' type: + for ch in arg: + if sameType(ch.typ, formal): + return getConstExpr(c.module, ch, c.idgen, c.graph) + typeMismatch(c.config, info, formal, arg.typ, arg) else: result = indexTypesMatch(c, formal, arg.typ, arg) if result == nil: @@ -356,6 +362,8 @@ proc semConstExpr(c: PContext, n: PNode): PNode = if e == nil: localError(c.config, n.info, errConstExprExpected) return n + if e.kind in nkSymChoices and e[0].typ.skipTypes(abstractInst).kind == tyEnum: + return e result = getConstExpr(c.module, e, c.idgen, c.graph) if result == nil: #if e.kind == nkEmpty: globalError(n.info, errConstExprExpected) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 42c6b465e..f2fe54fad 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2707,6 +2707,34 @@ proc getNilType(c: PContext): PType = result.align = c.config.target.ptrSize.int16 c.nilTypeCache = result +proc enumFieldSymChoice(c: PContext, n: PNode, s: PSym): PNode = + var o: TOverloadIter + var i = 0 + var a = initOverloadIter(o, c, n) + while a != nil: + if a.kind in OverloadableSyms-{skModule}: + inc(i) + if i > 1: break + a = nextOverloadIter(o, c, n) + let info = getCallLineInfo(n) + if i <= 1: + if sfGenSym notin s.flags: + result = newSymNode(s, info) + markUsed(c, info, s) + onUse(info, s) + else: + result = n + else: + result = newNodeIT(nkClosedSymChoice, info, newTypeS(tyNone, c)) + a = initOverloadIter(o, c, n) + while a != nil: + if a.kind in OverloadableSyms-{skModule}: + incl(a.flags, sfUsed) + markOwnerModuleAsUsed(c, a) + result.add newSymNode(a, info) + onUse(info, a) + a = nextOverloadIter(o, c, n) + proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = when defined(nimCompilerStacktraceHints): setFrameMsg c.config$n.info & " " & $n.kind @@ -2730,7 +2758,8 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = {checkUndeclared, checkModule, checkAmbiguity, checkPureEnumFields} var s = qualifiedLookUp(c, n, checks) if c.matchedConcept == nil: semCaptureSym(s, c.p.owner) - if s.kind in {skProc, skFunc, skMethod, skConverter, skIterator}: + case s.kind + of skProc, skFunc, skMethod, skConverter, skIterator: #performProcvarCheck(c, n, s) result = symChoice(c, n, s, scClosed) if result.kind == nkSym: @@ -2740,6 +2769,11 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = # "procs literals" are 'owned' if optOwnedRefs in c.config.globalOptions: result.typ = makeVarType(c, result.typ, tyOwned) + of skEnumField: + if overloadableEnums in c.features: + result = enumFieldSymChoice(c, n, s) + else: + result = semSym(c, n, s, flags) else: result = semSym(c, n, s, flags) of nkSym: diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 75323a95e..cc09291c5 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -490,6 +490,18 @@ proc semLowerLetVarCustomPragma(c: PContext, a: PNode, n: PNode): PNode = ret.add result result = semExprNoType(c, ret) +proc errorSymChoiceUseQualifier(c: PContext; n: PNode) = + assert n.kind in nkSymChoices + var err = "ambiguous identifier: '" & $n[0] & "'" + var i = 0 + for child in n: + let candidate = child.sym + if i == 0: err.add " -- use one of the following:\n" + else: err.add "\n" + err.add " " & candidate.owner.name.s & "." & candidate.name.s + inc i + localError(c.config, n.info, errGenerated, err) + proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = if n.len == 1: result = semLowerLetVarCustomPragma(c, n[0], n) @@ -514,7 +526,9 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = if a[^1].kind != nkEmpty: def = semExprWithType(c, a[^1], {}) - if def.kind == nkSym and def.sym.kind in {skTemplate, skMacro}: + if def.kind in nkSymChoices and def[0].typ.skipTypes(abstractInst).kind == tyEnum: + errorSymChoiceUseQualifier(c, def) + elif def.kind == nkSym and def.sym.kind in {skTemplate, skMacro}: typFlags.incl taIsTemplateOrMacro elif def.typ.kind == tyTypeDesc and c.p.owner.kind != skMacro: typFlags.incl taProcContextIsNotMacro diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 484997e3a..db895cab6 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -144,8 +144,13 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = styleCheckDef(c.config, e) onDef(e.info, e) if sfGenSym notin e.flags: - if not isPure: addInterfaceDecl(c, e) - else: declarePureEnumField(c, e) + if not isPure: + if overloadableEnums in c.features: + addInterfaceOverloadableSymAt(c, c.currentScope, e) + else: + addInterfaceDecl(c, e) + else: + declarePureEnumField(c, e) if isPure and (let conflict = strTableInclReportConflict(symbols, e); conflict != nil): wrongRedefinition(c, e.info, e.name.s, conflict.info) inc(counter) @@ -240,7 +245,8 @@ proc semRangeAux(c: PContext, n: PNode, prev: PType): PType = if not hasUnknownTypes: if not sameType(rangeT[0].skipTypes({tyRange}), rangeT[1].skipTypes({tyRange})): - localError(c.config, n.info, "type mismatch") + typeMismatch(c.config, n.info, rangeT[0], rangeT[1], n) + elif not isOrdinalType(rangeT[0]) and rangeT[0].kind notin {tyFloat..tyFloat128} or rangeT[0].kind == tyBool: localError(c.config, n.info, "ordinal or float type expected") diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 6bc641bd6..674dd7e2e 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -2197,7 +2197,7 @@ proc paramTypesMatch*(m: var TCandidate, f, a: PType, var best = -1 for i in 0.. nkSym + +proc x(p: int) = discard +proc x(p: string) = discard + +proc takeCallback(param: proc(p: int)) = discard + +takeCallback x + +echo ord v diff --git a/tests/errmsgs/t10251.nim b/tests/errmsgs/t10251.nim index 0c7fe0b3d..19adf02eb 100644 --- a/tests/errmsgs/t10251.nim +++ b/tests/errmsgs/t10251.nim @@ -2,7 +2,6 @@ discard """ action:reject cmd: "nim check $options $file" nimout: ''' -t10251.nim(15, 5) Error: redefinition of 'foo'; previous declaration here: t10251.nim(13, 5) t10251.nim(19, 23) Error: redefinition of 'goo1'; previous declaration here: t10251.nim(19, 11) ''' """ @@ -14,6 +13,7 @@ type Enum2 = enum foo, bar, baz + type Enum3 {.pure.} = enum # fixed (by accident?) in https://github.com/nim-lang/Nim/pull/18263 goo0, goo1, goo2, goo1 From e616675c419552a85bdf0b47f47586852be8b5be Mon Sep 17 00:00:00 2001 From: Miran Date: Wed, 28 Jul 2021 14:31:13 +0200 Subject: [PATCH 2404/3768] various small documentation improvements (#18602) --- lib/pure/asyncdispatch.nim | 34 ++++++++++----------- lib/pure/collections/deques.nim | 13 ++++---- lib/pure/collections/intsets.nim | 2 +- lib/pure/collections/sharedlist.nim | 2 +- lib/pure/collections/tables.nim | 20 ++++++++----- lib/pure/net.nim | 8 ++--- lib/pure/strformat.nim | 46 ++++++++++++++--------------- lib/pure/strscans.nim | 2 +- lib/std/enumutils.nim | 31 +++++++++++++++---- lib/std/packedsets.nim | 8 ++--- lib/system/threads.nim | 2 +- lib/system_overview.rst | 42 +++++++++++++------------- tests/stdlib/trst.nim | 10 +++---- 13 files changed, 123 insertions(+), 97 deletions(-) diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index d98ca77df..955e66f6b 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -42,12 +42,12 @@ ## ## Code to read some data from a socket may look something like this: ## -## .. code-block:: Nim -## var future = socket.recv(100) -## future.addCallback( -## proc () = -## echo(future.read) -## ) +## .. code-block:: Nim +## var future = socket.recv(100) +## future.addCallback( +## proc () = +## echo(future.read) +## ) ## ## All asynchronous functions returning a `Future` will not block. They ## will not however return immediately. An asynchronous function will have @@ -104,20 +104,20 @@ ## The most reliable way to handle exceptions is to use `yield` on a future ## then check the future's `failed` property. For example: ## -## .. code-block:: Nim -## var future = sock.recv(100) -## yield future -## if future.failed: -## # Handle exception +## .. code-block:: Nim +## var future = sock.recv(100) +## yield future +## if future.failed: +## # Handle exception ## ## The `async` procedures also offer limited support for the try statement. ## -## .. code-block:: Nim -## try: -## let data = await sock.recv(100) -## echo("Received ", data) -## except: -## # Handle exception +## .. code-block:: Nim +## try: +## let data = await sock.recv(100) +## echo("Received ", data) +## except: +## # Handle exception ## ## Unfortunately the semantics of the try statement may not always be correct, ## and occasionally the compilation may fail altogether. diff --git a/lib/pure/collections/deques.nim b/lib/pure/collections/deques.nim index fe9ceca1e..ecdf199d2 100644 --- a/lib/pure/collections/deques.nim +++ b/lib/pure/collections/deques.nim @@ -10,8 +10,9 @@ ## An implementation of a `deque`:idx: (double-ended queue). ## The underlying implementation uses a `seq`. ## -## Note that none of the procs that get an individual value from the deque should be used -## on an empty deque. +## .. note:: None of the procs that get an individual value from the deque should be used +## on an empty deque. +## ## If compiled with the `boundChecks` option, those procs will raise an `IndexDefect` ## on such access. This should not be relied upon, as `-d:danger` or `--checks:off` will ## disable those checks and then the procs may return garbage or crash the program. @@ -198,7 +199,7 @@ iterator items*[T](deq: Deque[T]): lent T = ## Yields every element of `deq`. ## ## **See also:** - ## * `mitems iterator <#mitems,Deque[T]>`_ + ## * `mitems iterator <#mitems.i,Deque[T]>`_ runnableExamples: from std/sequtils import toSeq @@ -214,7 +215,7 @@ iterator mitems*[T](deq: var Deque[T]): var T = ## Yields every element of `deq`, which can be modified. ## ## **See also:** - ## * `items iterator <#items,Deque[T]>`_ + ## * `items iterator <#items.i,Deque[T]>`_ runnableExamples: var a = [10, 20, 30, 40, 50].toDeque assert $a == "[10, 20, 30, 40, 50]" @@ -274,7 +275,7 @@ proc addFirst*[T](deq: var Deque[T], item: sink T) = ## Adds an `item` to the beginning of `deq`. ## ## **See also:** - ## * `addLast proc <#addLast,Deque[T],T>`_ + ## * `addLast proc <#addLast,Deque[T],sinkT>`_ runnableExamples: var a = initDeque[int]() for i in 1 .. 5: @@ -290,7 +291,7 @@ proc addLast*[T](deq: var Deque[T], item: sink T) = ## Adds an `item` to the end of `deq`. ## ## **See also:** - ## * `addFirst proc <#addFirst,Deque[T],T>`_ + ## * `addFirst proc <#addFirst,Deque[T],sinkT>`_ runnableExamples: var a = initDeque[int]() for i in 1 .. 5: diff --git a/lib/pure/collections/intsets.nim b/lib/pure/collections/intsets.nim index 05e5addcc..765a23e97 100644 --- a/lib/pure/collections/intsets.nim +++ b/lib/pure/collections/intsets.nim @@ -8,7 +8,7 @@ # ## Specialization of the generic `packedsets module `_ -## for ordinal sparse sets. +## (see its documentation for more examples) for ordinal sparse sets. import std/private/since import std/packedsets diff --git a/lib/pure/collections/sharedlist.nim b/lib/pure/collections/sharedlist.nim index 79f2391f6..73e147e05 100644 --- a/lib/pure/collections/sharedlist.nim +++ b/lib/pure/collections/sharedlist.nim @@ -35,7 +35,7 @@ template withLock(t, x: untyped) = release(t.lock) proc iterAndMutate*[A](x: var SharedList[A]; action: proc(x: A): bool) = - ## Iterates over the list. If 'action' returns true, the + ## Iterates over the list. If `action` returns true, the ## current item is removed from the list. ## ## .. warning:: It may not preserve the element order after some modifications. diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index 403345755..2695f3693 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -136,6 +136,8 @@ runnableExamples: ## a more complex object as a key you will be greeted by a strange compiler ## error: ## +## .. code:: +## ## Error: type mismatch: got (Person) ## but expected one of: ## hashes.hash(x: openArray[A]): Hash @@ -324,7 +326,7 @@ proc `[]`*[A, B](t: Table[A, B], key: A): B = ## a default value (e.g. zero for int) if the key doesn't exist ## * `getOrDefault proc<#getOrDefault,Table[A,B],A,B>`_ to return ## a custom value if the key doesn't exist - ## * `[]= proc<#[]=,Table[A,B],A,B>`_ for inserting a new + ## * `[]= proc<#[]=,Table[A,B],A,sinkB>`_ for inserting a new ## (key, value) pair in the table ## * `hasKey proc<#hasKey,Table[A,B],A>`_ for checking if a key is in ## the table @@ -345,7 +347,7 @@ proc `[]`*[A, B](t: var Table[A, B], key: A): var B = ## a default value (e.g. zero for int) if the key doesn't exist ## * `getOrDefault proc<#getOrDefault,Table[A,B],A,B>`_ to return ## a custom value if the key doesn't exist - ## * `[]= proc<#[]=,Table[A,B],A,B>`_ for inserting a new + ## * `[]= proc<#[]=,Table[A,B],A,sinkB>`_ for inserting a new ## (key, value) pair in the table ## * `hasKey proc<#hasKey,Table[A,B],A>`_ for checking if a key is in ## the table @@ -488,7 +490,7 @@ proc add*[A, B](t: var Table[A, B], key: A, val: sink B) {.deprecated: ## ## **This can introduce duplicate keys into the table!** ## - ## Use `[]= proc<#[]=,Table[A,B],A,B>`_ for inserting a new + ## Use `[]= proc<#[]=,Table[A,B],A,sinkB>`_ for inserting a new ## (key, value) pair in the table without introducing duplicates. addImpl(enlarge) @@ -499,7 +501,8 @@ template tabCellHash(i) = t.data[i].hcode proc del*[A, B](t: var Table[A, B], key: A) = ## Deletes `key` from hash table `t`. Does nothing if the key does not exist. ## - ## .. warning:: If duplicate keys were added, this may need to be called multiple times. + ## .. warning:: If duplicate keys were added (via the now deprecated `add` proc), + ## this may need to be called multiple times. ## ## See also: ## * `pop proc<#pop,Table[A,B],A,B>`_ @@ -519,7 +522,8 @@ proc pop*[A, B](t: var Table[A, B], key: A, val: var B): bool = ## mapping of the key. Otherwise, returns `false`, and the `val` is ## unchanged. ## - ## .. warning:: If duplicate keys were added, this may need to be called multiple times. + ## .. warning:: If duplicate keys were added (via the now deprecated `add` proc), + ## this may need to be called multiple times. ## ## See also: ## * `del proc<#del,Table[A,B],A>`_ @@ -1028,7 +1032,8 @@ proc add*[A, B](t: TableRef[A, B], key: A, val: sink B) {.deprecated: proc del*[A, B](t: TableRef[A, B], key: A) = ## Deletes `key` from hash table `t`. Does nothing if the key does not exist. ## - ## **If duplicate keys were added, this may need to be called multiple times.** + ## .. warning:: If duplicate keys were added (via the now deprecated `add` proc), + ## this may need to be called multiple times. ## ## See also: ## * `pop proc<#pop,TableRef[A,B],A,B>`_ @@ -1048,7 +1053,8 @@ proc pop*[A, B](t: TableRef[A, B], key: A, val: var B): bool = ## mapping of the key. Otherwise, returns `false`, and the `val` is ## unchanged. ## - ## **If duplicate keys were added, this may need to be called multiple times.** + ## .. warning:: If duplicate keys were added (via the now deprecated `add` proc), + ## this may need to be called multiple times. ## ## See also: ## * `del proc<#del,TableRef[A,B],A>`_ diff --git a/lib/pure/net.nim b/lib/pure/net.nim index c2e4810de..c0f9e1465 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -585,10 +585,10 @@ when defineSsl: ## ## CA certificates will be loaded, in the following order, from: ## - ## - caFile, caDir, parameters, if set - ## - if `verifyMode` is set to `CVerifyPeerUseEnvVars`, - ## the SSL_CERT_FILE and SSL_CERT_DIR environment variables are used - ## - a set of files and directories from the `ssl_certs `_ file. + ## - caFile, caDir, parameters, if set + ## - if `verifyMode` is set to `CVerifyPeerUseEnvVars`, + ## the SSL_CERT_FILE and SSL_CERT_DIR environment variables are used + ## - a set of files and directories from the `ssl_certs `_ file. ## ## The last two parameters specify the certificate file path and the key file ## path, a server socket will most likely not work without these. diff --git a/lib/pure/strformat.nim b/lib/pure/strformat.nim index 872141383..746f01ace 100644 --- a/lib/pure/strformat.nim +++ b/lib/pure/strformat.nim @@ -142,12 +142,12 @@ An expression like `&"{key} is {value:arg} {{z}}"` is transformed into: temp Parts of the string that are enclosed in the curly braces are interpreted -as Nim code, to escape a `{` or `}`, double it. +as Nim code. To escape a `{` or `}`, double it. -Within a curly expression,however, '{','}', must be escaped with a backslash. +Within a curly expression, however, `{`, `}`, must be escaped with a backslash. -To enable evaluating Nim expressions within curlies, inside parentheses -colons do not need to be escaped. +To enable evaluating Nim expressions within curlies, colons inside parentheses +do not need to be escaped. ]## runnableExamples: @@ -177,28 +177,28 @@ The general form of a standard format specifier is:: The square brackets `[]` indicate an optional element. -The optional 'align' flag can be one of the following: +The optional `align` flag can be one of the following: -'<' +`<` Forces the field to be left-aligned within the available space. (This is the default for strings.) -'>' +`>` Forces the field to be right-aligned within the available space. (This is the default for numbers.) -'^' +`^` Forces the field to be centered within the available space. Note that unless a minimum field width is defined, the field width will always be the same size as the data to fill it, so that the alignment option has no meaning in this case. -The optional 'fill' character defines the character to be used to pad +The optional `fill` character defines the character to be used to pad the field to the minimum width. The fill character, if present, must be followed by an alignment flag. -The 'sign' option is only valid for numeric types, and can be one of the following: +The `sign` option is only valid for numeric types, and can be one of the following: ================= ==================================================== Sign Meaning @@ -211,22 +211,22 @@ The 'sign' option is only valid for numeric types, and can be one of the followi positive numbers. ================= ==================================================== -If the '#' character is present, integers use the 'alternate form' for formatting. +If the `#` character is present, integers use the 'alternate form' for formatting. This means that binary, octal and hexadecimal output will be prefixed -with '0b', '0o' and '0x', respectively. +with `0b`, `0o` and `0x`, respectively. -'width' is a decimal integer defining the minimum field width. If not specified, +`width` is a decimal integer defining the minimum field width. If not specified, then the field width will be determined by the content. -If the width field is preceded by a zero ('0') character, this enables +If the width field is preceded by a zero (`0`) character, this enables zero-padding. -The 'precision' is a decimal number indicating how many digits should be displayed +The `precision` is a decimal number indicating how many digits should be displayed after the decimal point in a floating point conversion. For non-numeric types the field indicates the maximum field size - in other words, how many characters will be used from the field content. The precision is ignored for integer conversions. -Finally, the 'type' determines how the data should be presented. +Finally, the `type` determines how the data should be presented. The available integer presentation types are: @@ -240,7 +240,7 @@ The available integer presentation types are: lower-case letters for the digits above 9. `X` Hex format. Outputs the number in base 16, using uppercase letters for the digits above 9. -(None) The same as 'd'. +(None) The same as `d`. ================= ==================================================== The available floating point presentation types are: @@ -249,21 +249,21 @@ The available floating point presentation types are: Type Result ================= ==================================================== `e` Exponent notation. Prints the number in scientific - notation using the letter 'e' to indicate the + notation using the letter `e` to indicate the exponent. -`E` Exponent notation. Same as 'e' except it converts +`E` Exponent notation. Same as `e` except it converts the number to uppercase. `f` Fixed point. Displays the number as a fixed-point number. -`F` Fixed point. Same as 'f' except it converts the +`F` Fixed point. Same as `f` except it converts the number to uppercase. `g` General format. This prints the number as a fixed-point number, unless the number is too - large, in which case it switches to 'e' + large, in which case it switches to `e` exponent notation. -`G` General format. Same as 'g' except it switches to 'E' +`G` General format. Same as `g` except it switches to `E` if the number gets to large. -(None) Similar to 'g', except that it prints at least one +(None) Similar to `g`, except that it prints at least one digit after the decimal point. ================= ==================================================== diff --git a/lib/pure/strscans.nim b/lib/pure/strscans.nim index 73b53e3d6..291af7408 100644 --- a/lib/pure/strscans.nim +++ b/lib/pure/strscans.nim @@ -52,7 +52,7 @@ substrings starting with ``$``. These constructions are available: ================= ======================================================== Even though ``$*`` and ``$+`` look similar to the regular expressions ``.*`` -and ``.+`` they work quite differently, there is no non-deterministic +and ``.+``, they work quite differently. There is no non-deterministic state machine involved and the matches are non-greedy. ``[$*]`` matches ``[xyz]`` via ``parseutils.parseUntil``. diff --git a/lib/std/enumutils.nim b/lib/std/enumutils.nim index 09cf24f51..6269950c7 100644 --- a/lib/std/enumutils.nim +++ b/lib/std/enumutils.nim @@ -79,8 +79,14 @@ macro enumNames(a: typed): untyped = iterator items*[T: HoleyEnum](E: typedesc[T]): T = ## Iterates over an enum with holes. runnableExamples: - type A = enum a0 = 2, a1 = 4, a2 - type B[T] = enum b0 = 2, b1 = 4 + type + A = enum + a0 = 2 + a1 = 4 + a2 + B[T] = enum + b0 = 2 + b1 = 4 from std/sequtils import toSeq assert A.toSeq == [a0, a1, a2] assert B[float].toSeq == [B[float].b0, B[float].b1] @@ -130,9 +136,19 @@ template symbolRank*[T: enum](a: T): int = ## for small enums, otherwise is `O(T.enumLen)`. runnableExamples: type - A = enum a0 = -3, a1 = 10, a2, a3 = (20, "f3Alt") # HoleyEnum - B = enum b0, b1, b2 # OrdinalEnum - C = enum c0 = 10, c1, c2 # OrdinalEnum + A = enum # HoleyEnum + a0 = -3 + a1 = 10 + a2 + a3 = (20, "f3Alt") + B = enum # OrdinalEnum + b0 + b1 + b2 + C = enum # OrdinalEnum + c0 = 10 + c1 + c2 assert a2.symbolRank == 2 assert b2.symbolRank == 2 assert c2.symbolRank == 2 @@ -156,7 +172,10 @@ func symbolName*[T: enum](a: T): string = assert b.symbolName == "b0" assert $b == "kb0" static: assert B.high.symbolName == "b2" - type C = enum c0 = -3, c1 = 4, c2 = 20 # HoleyEnum + type C = enum # HoleyEnum + c0 = -3 + c1 = 4 + c2 = 20 assert c1.symbolName == "c1" const names = enumNames(T) names[a.symbolRank] diff --git a/lib/std/packedsets.nim b/lib/std/packedsets.nim index e209d2aae..b2ee917eb 100644 --- a/lib/std/packedsets.nim +++ b/lib/std/packedsets.nim @@ -12,10 +12,10 @@ ## ## Supports any Ordinal type. ## -## **Note**: Currently the assignment operator `=` for `PackedSet[A]` -## performs some rather meaningless shallow copy. Since Nim currently does -## not allow the assignment operator to be overloaded, use the `assign proc -## <#assign,PackedSet[A],PackedSet[A]>`_ to get a deep copy. +## .. note:: Currently the assignment operator `=` for `PackedSet[A]` +## performs some rather meaningless shallow copy. Since Nim currently does +## not allow the assignment operator to be overloaded, use the `assign proc +## <#assign,PackedSet[A],PackedSet[A]>`_ to get a deep copy. ## ## See also ## ======== diff --git a/lib/system/threads.nim b/lib/system/threads.nim index 2b5eb07fd..a9006b0c5 100644 --- a/lib/system/threads.nim +++ b/lib/system/threads.nim @@ -11,7 +11,7 @@ ## ## **Note**: This is part of the system module. Do not import it directly. ## To activate thread support you need to compile -## with the `--threads:on` command line switch. +## with the `--threads:on`:option: command line switch. ## ## Nim's memory model for threads is quite different from other common ## programming languages (C, Pascal): Each thread has its own diff --git a/lib/system_overview.rst b/lib/system_overview.rst index d6cbe1a35..df22697e0 100644 --- a/lib/system_overview.rst +++ b/lib/system_overview.rst @@ -48,27 +48,27 @@ Proc Usage Seqs ---- -============================================== ========================================== -Proc Usage -============================================== ========================================== -`newSeq<#newSeq>`_ Create a new sequence of a given length -`newSeqOfCap<#newSeqOfCap,Natural>`_ Create a new sequence with zero length - and a given capacity -`setLen<#setLen,seq[T],Natural>`_ Set the length of a sequence -`len<#len,seq[T]>`_ Return the length of a sequence -`@<#@,openArray[T]>`_ Turn an array into a sequence -`add<#add,seq[T],sinkT>`_ Add an item to the sequence -`insert<#insert,seq[T],sinkT>`_ Insert an item at a specific position -`delete<#delete,seq[T],Natural>`_ Delete an item while preserving the - order of elements (`O(n)` operation) -`del<#del,seq[T],Natural>`_ `O(1)` removal, doesn't preserve the order -`pop<#pop,seq[T]>`_ Remove and return last item of a sequence -`x & y<#&,seq[T],seq[T]>`_ Concatenate two sequences -`x[a .. b]<#[],openArray[T],HSlice[U,V]>`_ Slice of a sequence (both ends included) -`x[a .. ^b]<#[],openArray[T],HSlice[U,V]>`_ Slice of a sequence but `b` is a - reversed index (both ends included) -`x[a ..\< b]<#[],openArray[T],HSlice[U,V]>`_ Slice of a sequence (excluded upper bound) -============================================== ========================================== +============================================================= ========================================== +Proc Usage +============================================================= ========================================== +`newSeq<#newSeq>`_ Create a new sequence of a given length +`newSeqOfCap<#newSeqOfCap,Natural>`_ Create a new sequence with zero length + and a given capacity +`setLen<#setLen,seq[T],Natural>`_ Set the length of a sequence +`len<#len,seq[T]>`_ Return the length of a sequence +`@<#@,openArray[T]>`_ Turn an array into a sequence +`add<#add,seq[T],sinkT>`_ Add an item to the sequence +`insert<#insert,seq[T],sinkT>`_ Insert an item at a specific position +`delete<#delete,seq[T],Natural>`_ Delete an item while preserving the + order of elements (`O(n)` operation) +`del<#del,seq[T],Natural>`_ `O(1)` removal, doesn't preserve the order +`pop<#pop,seq[T]>`_ Remove and return last item of a sequence +`x & y<#&,seq[T],seq[T]>`_ Concatenate two sequences +`x[a .. b]<#[],openArray[T],HSlice[U: Ordinal,V: Ordinal]>`_ Slice of a sequence (both ends included) +`x[a .. ^b]<#[],openArray[T],HSlice[U: Ordinal,V: Ordinal]>`_ Slice of a sequence but `b` is a + reversed index (both ends included) +`x[a ..< b]<#[],openArray[T],HSlice[U: Ordinal,V: Ordinal]>`_ Slice of a sequence (excluded upper bound) +============================================================= ========================================== **See also:** * `sequtils module `_ for operations on container diff --git a/tests/stdlib/trst.nim b/tests/stdlib/trst.nim index fc3ccbf4e..bc11f219c 100644 --- a/tests/stdlib/trst.nim +++ b/tests/stdlib/trst.nim @@ -182,9 +182,9 @@ suite "RST parsing": test "using .. as separator b/w directives and block quotes": check(dedent""" .. note:: someNote - + .. - + someBlockQuote""".toAst == dedent""" rnInner @@ -300,14 +300,14 @@ suite "RST indentation": let input1 = dedent""" .. code-block:: nim :test: "nim c $1" - + template additive(typ: typedesc) = discard """ let input2 = dedent""" .. code-block:: nim :test: "nim c $1" - + template additive(typ: typedesc) = discard """ @@ -320,7 +320,7 @@ suite "RST indentation": let inputWrong = dedent""" .. code-block:: nim :test: "nim c $1" - + template additive(typ: typedesc) = discard """ From 7d3c3e00efe777dcbda15f2125fa77aa7d17cad4 Mon Sep 17 00:00:00 2001 From: Vindaar Date: Thu, 29 Jul 2021 07:47:34 +0200 Subject: [PATCH 2405/3768] Allow `nnkAccQuoted` in `genEnumCaseStmt` (#18606) * [enumutils] provide node kind for `Invalid node type` error * [enumutils] add support for nnkAccQuoted in `genEnumCaseStmt` For reasons unknown to me, when running `nim doc` on a file that uses `parseEnum` with an enum that contains accented quotes errors at CT with the `Invalid node for type` error. Further errors are raised, probably because the enum parsing fails? --- lib/std/enumutils.nim | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/std/enumutils.nim b/lib/std/enumutils.nim index 6269950c7..81e602ad5 100644 --- a/lib/std/enumutils.nim +++ b/lib/std/enumutils.nim @@ -36,6 +36,10 @@ macro genEnumCaseStmt*(typ: typedesc, argSym: typed, default: typed, case f.kind of nnkEmpty: continue # skip first node of `enumTy` of nnkSym, nnkIdent: fStr = f.strVal + of nnkAccQuoted: + fStr = "" + for ch in f: + fStr.add ch.strVal of nnkEnumFieldDef: case f[1].kind of nnkStrLit: fStr = f[1].strVal @@ -46,7 +50,7 @@ macro genEnumCaseStmt*(typ: typedesc, argSym: typed, default: typed, fStr = f[0].strVal fNum = f[1].intVal else: error("Invalid tuple syntax!", f[1]) - else: error("Invalid node for enum type!", f) + else: error("Invalid node for enum type `" & $f.kind & "`!", f) # add field if string not already added if fNum >= userMin and fNum <= userMax: fStr = normalizer(fStr) From 1bba641fed99bce61de2470c8bf73cd34480d907 Mon Sep 17 00:00:00 2001 From: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Date: Thu, 29 Jul 2021 10:50:17 +0200 Subject: [PATCH 2406/3768] Update `lib.rst` (#18605) * Update lib.rst * Remove "Unstable" category Add `strbasics` * Update doc/lib.rst * Update doc/lib.rst Co-authored-by: Andreas Rumpf --- doc/lib.rst | 44 ++++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/doc/lib.rst b/doc/lib.rst index 988fbf6f0..54728d681 100644 --- a/doc/lib.rst +++ b/doc/lib.rst @@ -41,7 +41,7 @@ Automatic imports Basic Nim thread support. **Note:** This is part of the system module. Do not import it explicitly. Enabled with `--threads:on`:option:. -* `channels `_ +* `channels_builtin `_ Nim message passing support for threads. **Note:** This is part of the system module. Do not import it explicitly. Enabled with `--threads:on`:option:. @@ -49,6 +49,9 @@ Automatic imports Core ---- +* `atomics `_ + Types and operations for atomic operations and lockless algorithms. + * `bitops `_ Provides a series of low-level methods for bit manipulation. @@ -66,7 +69,7 @@ Core * `macrocache `_ Provides an API for macros to collect compile-time information across modules. - + * `macros `_ Contains the AST API and documentation of Nim for writing macros. @@ -113,7 +116,7 @@ Collections The underlying implementation uses a `seq`. * `heapqueue `_ - Implementation of a heap data structure that can be used as a priority queue. + Implementation of a binary heap data structure that can be used as a priority queue. * `intsets `_ Efficient implementation of a set of ints as a sparse bit set. @@ -129,7 +132,7 @@ Collections Efficient implementation of a set of ordinals as a sparse bit set. * `sets `_ - Nim hash and bit set support. + Nim hash set support. * `sharedlist `_ Nim shared linked list support. Contains a shared singly-linked list. @@ -167,11 +170,14 @@ String handling * `ropes `_ This module contains support for a *rope* data type. Ropes can represent very long strings efficiently; - especially concatenation is done in O(1) instead of O(n). + in particular, concatenation is done in O(1) instead of O(n). + +* `strbasics `_ + This module provides some high performance string operations. * `strformat `_ Macro based standard string interpolation/formatting. Inspired by - Python's `f`-strings. + Python's f-strings. * `strmisc `_ This module contains uncommon string handling operations that do not @@ -266,9 +272,6 @@ Math libraries * `math `_ Mathematical operations like cosine, square root. -* `mersenne `_ - Mersenne twister random number generator. - * `random `_ Fast and tiny random number generator. @@ -433,7 +436,7 @@ Hashing ------- * `base64 `_ - This module implements a base64 encoder and decoder. + This module implements a Base64 encoder and decoder. * `hashes `_ This module implements efficient computations of hash values for diverse @@ -446,7 +449,7 @@ Hashing An OID is a global ID that consists of a timestamp, a unique counter, and a random value. This combination should suffice to produce a globally distributed unique ID. This implementation was extracted - from the Mongodb interface and it thus binary compatible with a Mongo OID. + from the MongoDB interface and it thus binary compatible with a MongoDB OID. * `sha1 `_ This module implements a sha1 encoder and decoder. @@ -487,9 +490,8 @@ Miscellaneous This module implements the `with` macro for easy function chaining. - -Modules for JS backend ----------------------- +Modules for the JS backend +-------------------------- * `asyncjs `_ Types and macros for writing asynchronous procedures in JavaScript. @@ -497,6 +499,9 @@ Modules for JS backend * `dom `_ Declaration of the Document Object Model for the JS backend. +* `jsbigints `_ + Arbitrary precision integers. + * `jsconsole `_ Wrapper for the `console` object. @@ -519,7 +524,6 @@ Regular expressions expressions. The current implementation uses PCRE. - Database support ---------------- @@ -583,7 +587,7 @@ Database support * `mysql `_ Contains a wrapper for the mySQL API. * `sqlite3 `_ - Contains a wrapper for SQLite 3 API. + Contains a wrapper for the SQLite 3 API. * `odbcsql `_ interface to the ODBC driver. @@ -593,11 +597,3 @@ Network Programming and Internet Protocols * `openssl `_ Wrapper for OpenSSL. - - - -Unstable -======== - -* `atomics `_ - Types and operations for atomic operations and lockless algorithms. From bbe05c15326c7a62d15c0fe47d1c6e1be3ab11a8 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Thu, 29 Jul 2021 14:30:19 +0300 Subject: [PATCH 2407/3768] docgen: draw frame around active anchors (#18607) --- config/nimdoc.cfg | 3 +- doc/nimdoc.css | 6 + .../expected/index.html | 3 +- nimdoc/testproject/expected/nimdoc.out.css | 6 + .../expected/subdir/subdir_b/utils.html | 42 +++-- nimdoc/testproject/expected/testproject.html | 177 ++++++++++++------ 6 files changed, 162 insertions(+), 75 deletions(-) diff --git a/config/nimdoc.cfg b/config/nimdoc.cfg index 15f887023..162b2b4a3 100644 --- a/config/nimdoc.cfg +++ b/config/nimdoc.cfg @@ -46,13 +46,14 @@ doc.section.toc2 = """ # * $seeSrc: generated HTML from doc.item.seesrc (if some switches are used). doc.item = """ - +
                              $header
                              $deprecationMsg $desc $seeSrc
                              +
                              """ # Chunk of HTML emitted for each entry in the HTML table of contents. diff --git a/doc/nimdoc.css b/doc/nimdoc.css index 5c2bf2093..4abea9ce0 100644 --- a/doc/nimdoc.css +++ b/doc/nimdoc.css @@ -234,6 +234,12 @@ select:focus { } /* Docgen styles */ + +:target { + border: 2px solid #B5651D; + border-style: dotted; +} + /* Links */ a { color: var(--anchor); diff --git a/nimdoc/test_out_index_dot_html/expected/index.html b/nimdoc/test_out_index_dot_html/expected/index.html index fac758c9b..3499b5326 100644 --- a/nimdoc/test_out_index_dot_html/expected/index.html +++ b/nimdoc/test_out_index_dot_html/expected/index.html @@ -121,13 +121,14 @@ window.addEventListener('DOMContentLoaded', main);

                              Procs

                              - +
                              proc foo() {....raises: [], tags: [].}
                              I do foo
                              +
                              diff --git a/nimdoc/testproject/expected/nimdoc.out.css b/nimdoc/testproject/expected/nimdoc.out.css index 5c2bf2093..4abea9ce0 100644 --- a/nimdoc/testproject/expected/nimdoc.out.css +++ b/nimdoc/testproject/expected/nimdoc.out.css @@ -234,6 +234,12 @@ select:focus { } /* Docgen styles */ + +:target { + border: 2px solid #B5651D; + border-style: dotted; +} + /* Links */ a { color: var(--anchor); diff --git a/nimdoc/testproject/expected/subdir/subdir_b/utils.html b/nimdoc/testproject/expected/subdir/subdir_b/utils.html index 533cb5b9d..47901ce0d 100644 --- a/nimdoc/testproject/expected/subdir/subdir_b/utils.html +++ b/nimdoc/testproject/expected/subdir/subdir_b/utils.html @@ -213,7 +213,7 @@ window.addEventListener('DOMContentLoaded', main);

                              Types

                              - +
                              SomeType = enum
                                 enumValueA, enumValueB, enumValueC
                              @@ -221,101 +221,114 @@ window.addEventListener('DOMContentLoaded', main);
                              +

                              Procs

                              - +
                              proc fn2() {....raises: [], tags: [].}
                              comment
                              - +
                              +
                              proc fn3(): auto {....raises: [], tags: [].}
                              comment
                              - +
                              +
                              proc fn4(): auto {....raises: [], tags: [].}
                              comment
                              - +
                              +
                              proc fn5() {....raises: [], tags: [].}
                              comment
                              - +
                              +
                              proc fn6() {....raises: [], tags: [].}
                              comment
                              - +
                              +
                              proc fn7() {....raises: [], tags: [].}
                              comment
                              - +
                              +
                              proc fn8(): auto {....raises: [], tags: [].}
                              comment
                              - +
                              +
                              func fn9(a: int): int {....raises: [], tags: [].}
                              comment
                              - +
                              +
                              func fn10(a: int): int {....raises: [], tags: [].}
                              comment
                              - +
                              +
                              proc someType(): SomeType {....raises: [], tags: [].}
                              constructor.
                              +

                              Templates

                              - +
                              template aEnum(): untyped
                              - +
                              +
                              template bEnum(): untyped
                              - +
                              +
                              template fromUtilsGen(): untyped
                              @@ -324,6 +337,7 @@ should be shown in utils.html only
                              discard "should be in utils.html only, not in module that calls fromUtilsGen"
                              ditto
                              +
                              diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html index 0f6289bbc..d98b1b00f 100644 --- a/nimdoc/testproject/expected/testproject.html +++ b/nimdoc/testproject/expected/testproject.html @@ -447,7 +447,7 @@ window.addEventListener('DOMContentLoaded', main);

                              Types

                              - +
                              A {.inject.} = enum
                                 aA
                              @@ -455,7 +455,8 @@ window.addEventListener('DOMContentLoaded', main); The enum A.
                              - +
                              +
                              B {.inject.} = enum
                                 bB
                              @@ -463,7 +464,8 @@ The enum A. The enum B.
                              - +
                              +
                              Foo = enum
                                 enumValueA2
                              @@ -471,7 +473,8 @@ The enum B.
                              - +
                              +
                              FooBuzz {....deprecated: "FooBuzz msg".} = int
                              @@ -481,7 +484,8 @@ The enum B.
                              - +
                              +
                              Shapes = enum
                                 Circle,                   ## A circle
                                 Triangle,                 ## A three-sided shape
                              @@ -491,64 +495,71 @@ The enum B.
                               Some shapes.
                               
                               
                            +

                            Vars

                            - +
                            aVariable: array[1, int]
                            - +
                            +
                            someVariable: bool
                            This should be visible.
                            +

                            Consts

                            - +
                            C_A = 0x7FF0000000000000'f64
                            - +
                            +
                            C_B = 0o377'i8
                            - +
                            +
                            C_C = 0o277'i8
                            - +
                            +
                            C_D = 0o177777'i16
                            +

                            Procs

                            - +
                            proc addfBug14485() {....raises: [], tags: [].}
                            @@ -567,14 +578,16 @@ Some proc ]#
  • - + +
    proc anything() {....raises: [], tags: [].}
    There is no block quote after blank lines at the beginning.
    - +
    +
    proc asyncFun1(): Future[int] {....raises: [Exception, ValueError],
                                     tags: [RootEffect].}
    @@ -582,14 +595,16 @@ There is no block quote after blank lines at the beginning. ok1
    - +
    +
    proc asyncFun2(): owned(Future[void]) {....raises: [Exception], tags: [RootEffect].}
    - +
    +
    proc asyncFun3(): owned(Future[void]) {....raises: [Exception], tags: [RootEffect].}
    @@ -598,21 +613,24 @@ ok1
    discard
    ok1
    - +
    +
    proc bar[T](a, b: T): T
    - +
    +
    proc baz() {....raises: [], tags: [].}
    - +
    +
    proc baz[T](a, b: T): T {....deprecated.}
    @@ -622,7 +640,8 @@ ok1 This is deprecated without message.
    - +
    +
    proc buzz[T](a, b: T): T {....deprecated: "since v0.20".}
    @@ -632,7 +651,8 @@ This is deprecated without message. This is deprecated with a message.
    - +
    +
    proc c_nonexistent(frmt: cstring): cint {.importc: "nonexistent",
         header: "<stdio.h>", varargs, discardable.}
    @@ -640,7 +660,8 @@ This is deprecated with a message.
    - +
    +
    proc c_printf(frmt: cstring): cint {.importc: "printf", header: "<stdio.h>",
                                          varargs, discardable.}
    @@ -648,7 +669,8 @@ This is deprecated with a message. the c printf. etc.
    - +
    +
    proc fromUtils3() {....raises: [], tags: [].}
    @@ -658,14 +680,16 @@ came form utils but should be shown where
    - + +
    proc isValid[T](x: T): bool
    - +
    +
    proc low2[T: Ordinal | enum | range](x: T): T {.magic: "Low", noSideEffect.}
    @@ -678,7 +702,8 @@ came form utils but should be shown where discard "in low2"
    - + +
    proc low[T: Ordinal | enum | range](x: T): T {.magic: "Low", noSideEffect.}
    @@ -689,7 +714,8 @@ came form utils but should be shown where low(2) # => -9223372036854775808
    - + +
    proc p1() {....raises: [], tags: [].}
    @@ -712,14 +738,16 @@ this is a nested doc comment # also work after
    - + +
    func someFunc() {....raises: [], tags: [].}
    My someFunc. Stuff in quotes here. Some link
    - +
    +
    proc tripleStrLitTest() {....raises: [], tags: [].}
    @@ -762,14 +790,16 @@ at indent 0 # should be in
    - + +
    proc z1(): Foo {....raises: [], tags: [].}
    cz1
    - +
    +
    proc z2() {....raises: [], tags: [].}
    @@ -778,49 +808,56 @@ cz2
    discard "in cz2"
    - +
    +
    proc z3() {....raises: [], tags: [].}
    cz3
    - +
    +
    proc z4() {....raises: [], tags: [].}
    cz4
    - +
    +
    proc z5(): int {....raises: [], tags: [].}
    cz5
    - +
    +
    proc z6(): int {....raises: [], tags: [].}
    cz6
    - +
    +
    proc z7(): int {....raises: [], tags: [].}
    cz7
    - +
    +
    proc z8(): int {....raises: [], tags: [].}
    cz8
    - +
    +
    proc z9() {....raises: [], tags: [].}
    @@ -829,7 +866,8 @@ cz8
    doAssert 1 + 1 == 2
    - +
    +
    proc z10() {....raises: [], tags: [].}
    @@ -838,7 +876,8 @@ cz8
    discard 1
    cz10
    - +
    +
    proc z11() {....raises: [], tags: [].}
    @@ -847,7 +886,8 @@ cz8
    discard 1
    - +
    +
    proc z12(): int {....raises: [], tags: [].}
    @@ -856,7 +896,8 @@ cz8
    discard 1
    - +
    +
    proc z13() {....raises: [], tags: [].}
    @@ -865,7 +906,8 @@ cz13
    discard
    - +
    +
    proc z17() {....raises: [], tags: [].}
    @@ -874,38 +916,42 @@ cz17 rest
    discard 1
    rest
    +

    Methods

    - +
    method method1(self: Moo) {.base, ...raises: [], tags: [].}
    foo1
    - +
    +
    method method2(self: Moo): int {.base, ...raises: [], tags: [].}
    foo2
    - +
    +
    method method3(self: Moo): int {.base, ...raises: [], tags: [].}
    foo3
    +

    Iterators

    - +
    iterator fromUtils1(): int {....raises: [], tags: [].}
    @@ -916,14 +962,16 @@ foo3 # ok2
    - + +
    iterator iter1(n: int): int {....raises: [], tags: [].}
    foo1
    - +
    +
    iterator iter2(n: int): int {....raises: [], tags: [].}
    @@ -932,19 +980,21 @@ foo2
    discard # bar
    +

    Macros

    - +
    macro bar(): untyped
    - +
    +
    macro z16()
    @@ -955,26 +1005,29 @@ foo2
    doAssert 2 == 1 + 1
    - +
    +
    macro z18(): int
    cz18
    +

    Templates

    - +
    template foo(a, b: SomeType)
    This does nothing
    - +
    +
    template fromUtils2()
    @@ -984,7 +1037,8 @@ ok3 in module calling fromUtilsGen"""
    - + +
    template myfn()
    @@ -1006,7 +1060,8 @@ bar # should be in
    should be still in - + +
    template testNimDocTrailingExample()
    @@ -1015,14 +1070,16 @@ bar
    discard 2
    - +
    +
    template z6t(): int
    cz6t
    - +
    +
    template z14()
    @@ -1031,7 +1088,8 @@ cz14
    discard
    - +
    +
    template z15()
    @@ -1048,6 +1106,7 @@ cz15
    discard 1
    in or out?
    +
    From 6b3c77e7f4421267c551b1cd84717a19c65be2c5 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 29 Jul 2021 14:05:26 -0700 Subject: [PATCH 2408/3768] Remove tracking of environment from osenv.nim v2 (#18575) * Remove unnecessary environment tracking * try to fix windows * fix delEnv * make putEnv work on windows even with empty values; improve tests: add tests, add js, vm testing * [skip ci] fix changelog Co-authored-by: Caden Haustein --- changelog.md | 3 + lib/pure/includes/osenv.nim | 237 +++++++++----------------- lib/std/private/win_setenv.nim | 92 ++++++++++ tests/destructor/tnewruntime_misc.nim | 2 +- tests/stdlib/tos.nim | 34 +--- tests/stdlib/tosenv.nim | 58 +++++++ 6 files changed, 240 insertions(+), 186 deletions(-) create mode 100644 lib/std/private/win_setenv.nim create mode 100644 tests/stdlib/tosenv.nim diff --git a/changelog.md b/changelog.md index be94f92c4..a19697c8c 100644 --- a/changelog.md +++ b/changelog.md @@ -97,11 +97,14 @@ The downside is that these defines now have custom logic that doesn't apply for other defines. +- `std/os`: `putEnv` now raises if the 1st argument contains a `=` + - Renamed `-d:nimCompilerStackraceHints` to `-d:nimCompilerStacktraceHints`. - In `std/dom`, `Interval` is now a `ref object`, same as `Timeout`. Definitions of `setTimeout`, `clearTimeout`, `setInterval`, `clearInterval` were updated. + ## Standard library additions and changes - `strformat`: diff --git a/lib/pure/includes/osenv.nim b/lib/pure/includes/osenv.nim index db291c5fd..6aaafbfda 100644 --- a/lib/pure/includes/osenv.nim +++ b/lib/pure/includes/osenv.nim @@ -40,112 +40,15 @@ when defined(nodejs): # {.error: "requires -d:nodejs".} else: - when defined(windows): - from parseutils import skipIgnoreCase proc c_getenv(env: cstring): cstring {. importc: "getenv", header: "".} - when defined(vcc): + when defined(windows): proc c_putenv_s(envname: cstring, envval: cstring): cint {.importc: "_putenv_s", header: "".} + from std/private/win_setenv import setEnvImpl else: proc c_setenv(envname: cstring, envval: cstring, overwrite: cint): cint {.importc: "setenv", header: "".} - proc c_unsetenv(env: cstring): cint {. - importc: "unsetenv", header: "".} - - # Environment handling cannot be put into RTL, because the `envPairs` - # iterator depends on `environment`. - - var - envComputed {.threadvar.}: bool - environment {.threadvar.}: seq[string] - - when defined(nimV2): - proc unpairedEnvAllocs*(): int = - result = environment.len - if result > 0: inc result - - when defined(windows) and not defined(nimscript): - # because we support Windows GUI applications, things get really - # messy here... - when useWinUnicode: - when defined(cpp): - proc strEnd(cstr: WideCString, c = 0'i32): WideCString {. - importcpp: "(NI16*)wcschr((const wchar_t *)#, #)", header: "".} - else: - proc strEnd(cstr: WideCString, c = 0'i32): WideCString {. - importc: "wcschr", header: "".} - else: - proc strEnd(cstr: cstring, c = 0'i32): cstring {. - importc: "strchr", header: "".} - - proc getEnvVarsC() = - if not envComputed: - environment = @[] - when useWinUnicode: - var - env = getEnvironmentStringsW() - e = env - if e == nil: return # an error occurred - while true: - var eend = strEnd(e) - add(environment, $e) - e = cast[WideCString](cast[ByteAddress](eend)+2) - if eend[1].int == 0: break - discard freeEnvironmentStringsW(env) - else: - var - env = getEnvironmentStringsA() - e = env - if e == nil: return # an error occurred - while true: - var eend = strEnd(e) - add(environment, $e) - e = cast[cstring](cast[ByteAddress](eend)+1) - if eend[1] == '\0': break - discard freeEnvironmentStringsA(env) - envComputed = true - - else: - const - useNSGetEnviron = (defined(macosx) and not defined(ios) and not defined(emscripten)) or defined(nimscript) - - when useNSGetEnviron: - # From the manual: - # Shared libraries and bundles don't have direct access to environ, - # which is only available to the loader ld(1) when a complete program - # is being linked. - # The environment routines can still be used, but if direct access to - # environ is needed, the _NSGetEnviron() routine, defined in - # , can be used to retrieve the address of environ - # at runtime. - proc NSGetEnviron(): ptr cstringArray {. - importc: "_NSGetEnviron", header: "".} - elif defined(haiku): - var gEnv {.importc: "environ", header: "".}: cstringArray - else: - var gEnv {.importc: "environ".}: cstringArray - - proc getEnvVarsC() = - # retrieves the variables of char** env of C's main proc - if not envComputed: - environment = @[] - when useNSGetEnviron: - var gEnv = NSGetEnviron()[] - var i = 0 - while gEnv[i] != nil: - add environment, $gEnv[i] - inc(i) - envComputed = true - - proc findEnvVar(key: string): int = - getEnvVarsC() - var temp = key & '=' - for i in 0..high(environment): - when defined(windows): - if skipIgnoreCase(environment[i], temp) == len(temp): return i - else: - if startsWith(environment[i], temp): return i - return -1 + proc c_unsetenv(env: cstring): cint {.importc: "unsetenv", header: "".} proc getEnv*(key: string, default = ""): string {.tags: [ReadEnvEffect].} = ## Returns the value of the `environment variable`:idx: named `key`. @@ -163,16 +66,9 @@ else: assert getEnv("unknownEnv") == "" assert getEnv("unknownEnv", "doesn't exist") == "doesn't exist" - when nimvm: - discard "built into the compiler" - else: - var i = findEnvVar(key) - if i >= 0: - return substr(environment[i], find(environment[i], '=')+1) - else: - var env = c_getenv(key) - if env == nil: return default - result = $env + let env = c_getenv(key) + if env == nil: return default + result = $env proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} = ## Checks whether the environment variable named `key` exists. @@ -186,11 +82,7 @@ else: runnableExamples: assert not existsEnv("unknownEnv") - when nimvm: - discard "built into the compiler" - else: - if c_getenv(key) != nil: return true - else: return findEnvVar(key) >= 0 + return c_getenv(key) != nil proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} = ## Sets the value of the `environment variable`:idx: named `key` to `val`. @@ -201,33 +93,14 @@ else: ## * `existsEnv proc <#existsEnv,string>`_ ## * `delEnv proc <#delEnv,string>`_ ## * `envPairs iterator <#envPairs.i>`_ - - # Note: by storing the string in the environment sequence, - # we guarantee that we don't free the memory before the program - # ends (this is needed for POSIX compliance). It is also needed so that - # the process itself may access its modified environment variables! - when nimvm: - discard "built into the compiler" + when defined(windows): + if key.len == 0 or '=' in key: + raise newException(OSError, "invalid key, got: " & $(key, val)) + if setEnvImpl(key, val, 1'i32) != 0'i32: + raiseOSError(osLastError(), $(key, val)) else: - var indx = findEnvVar(key) - if indx >= 0: - environment[indx] = key & '=' & val - else: - add environment, (key & '=' & val) - indx = high(environment) - when defined(windows) and not defined(nimscript): - when useWinUnicode: - var k = newWideCString(key) - var v = newWideCString(val) - if setEnvironmentVariableW(k, v) == 0'i32: raiseOSError(osLastError()) - else: - if setEnvironmentVariableA(key, val) == 0'i32: raiseOSError(osLastError()) - elif defined(vcc): - if c_putenv_s(key, val) != 0'i32: - raiseOSError(osLastError()) - else: - if c_setenv(key, val, 1'i32) != 0'i32: - raiseOSError(osLastError()) + if c_setenv(key, val, 1'i32) != 0'i32: + raiseOSError(osLastError(), $(key, val)) proc delEnv*(key: string) {.tags: [WriteEnvEffect].} = ## Deletes the `environment variable`:idx: named `key`. @@ -238,21 +111,45 @@ else: ## * `existsEnv proc <#existsEnv,string>`_ ## * `putEnv proc <#putEnv,string,string>`_ ## * `envPairs iterator <#envPairs.i>`_ - when nimvm: - discard "built into the compiler" + template bail = raiseOSError(osLastError(), key) + when defined(windows): + #[ + # https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/putenv-s-wputenv-s?view=msvc-160 + > You can remove a variable from the environment by specifying an empty string (that is, "") for value_string + note that nil is not legal + ]# + if key.len == 0 or '=' in key: + raise newException(OSError, "invalid key, got: " & key) + if c_putenv_s(key, "") != 0'i32: bail else: - var indx = findEnvVar(key) - if indx < 0: return # Do nothing if the env var is not already set - when defined(windows) and not defined(nimscript): - when useWinUnicode: - var k = newWideCString(key) - if setEnvironmentVariableW(k, nil) == 0'i32: raiseOSError(osLastError()) - else: - if setEnvironmentVariableA(key, nil) == 0'i32: raiseOSError(osLastError()) + if c_unsetenv(key) != 0'i32: bail + + when defined(windows): + when useWinUnicode: + when defined(cpp): + proc strEnd(cstr: WideCString, c = 0'i32): WideCString {.importcpp: "(NI16*)wcschr((const wchar_t *)#, #)", + header: "".} else: - if c_unsetenv(key) != 0'i32: - raiseOSError(osLastError()) - environment.delete(indx) + proc strEnd(cstr: WideCString, c = 0'i32): WideCString {.importc: "wcschr", + header: "".} + else: + proc strEnd(cstr: cstring, c = 0'i32): cstring {.importc: "strchr", + header: "".} + elif defined(macosx) and not defined(ios) and not defined(emscripten): + # From the manual: + # Shared libraries and bundles don't have direct access to environ, + # which is only available to the loader ld(1) when a complete program + # is being linked. + # The environment routines can still be used, but if direct access to + # environ is needed, the _NSGetEnviron() routine, defined in + # , can be used to retrieve the address of environ + # at runtime. + proc NSGetEnviron(): ptr cstringArray {.importc: "_NSGetEnviron", + header: "".} + elif defined(haiku): + var gEnv {.importc: "environ", header: "".}: cstringArray + else: + var gEnv {.importc: "environ".}: cstringArray iterator envPairs*(): tuple[key, value: string] {.tags: [ReadEnvEffect].} = ## Iterate over all `environments variables`:idx:. @@ -265,8 +162,30 @@ else: ## * `existsEnv proc <#existsEnv,string>`_ ## * `putEnv proc <#putEnv,string,string>`_ ## * `delEnv proc <#delEnv,string>`_ - getEnvVarsC() - for i in 0..high(environment): - var p = find(environment[i], '=') - yield (substr(environment[i], 0, p-1), - substr(environment[i], p+1)) + when defined(windows): + block: + template impl(get_fun, typ, size, zero, free_fun) = + let env = get_fun() + var e = env + if e == nil: break + while true: + let eend = strEnd(e) + let kv = $e + let p = find(kv, '=') + yield (substr(kv, 0, p-1), substr(kv, p+1)) + e = cast[typ](cast[ByteAddress](eend)+size) + if typeof(zero)(eend[1]) == zero: break + discard free_fun(env) + when useWinUnicode: + impl(getEnvironmentStringsW, WideCString, 2, 0, freeEnvironmentStringsW) + else: + impl(getEnvironmentStringsA, cstring, 1, '\0', freeEnvironmentStringsA) + else: + var i = 0 + when defined(macosx) and not defined(ios) and not defined(emscripten): + var gEnv = NSGetEnviron()[] + while gEnv[i] != nil: + let kv = $gEnv[i] + inc(i) + let p = find(kv, '=') + yield (substr(kv, 0, p-1), substr(kv, p+1)) diff --git a/lib/std/private/win_setenv.nim b/lib/std/private/win_setenv.nim new file mode 100644 index 000000000..067e656a3 --- /dev/null +++ b/lib/std/private/win_setenv.nim @@ -0,0 +1,92 @@ +#[ +Copyright (c) Facebook, Inc. and its affiliates. +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. + +Adapted `setenv` from https://github.com/facebook/folly/blob/master/folly/portability/Stdlib.cpp +translated from C to nim. +]# + +#[ +Introduced in https://github.com/facebook/folly/commit/5d8ca09a3f96afefb44e35808f03651a096ab9c7 + +TODO: +check errno_t vs cint +]# + +when not defined(windows): discard +else: + proc setEnvironmentVariableA*(lpName, lpValue: cstring): int32 {. + stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableA", sideEffect.} + # same as winlean.setEnvironmentVariableA + + proc c_getenv(env: cstring): cstring {.importc: "getenv", header: "".} + proc c_putenv_s(envname: cstring, envval: cstring): cint {.importc: "_putenv_s", header: "".} + proc c_wgetenv(varname: WideCString): WideCString {.importc: "_wgetenv", header: "".} + + var errno {.importc, header: "".}: cint + type wchar_t {.importc: "wchar_t".} = int16 + var gWenviron {.importc:"_wenviron".}: ptr ptr wchar_t + # xxx `ptr UncheckedArray[WideCString]` did not work + + proc mbstowcs_s(pReturnValue: ptr csize_t, wcstr: WideCString, sizeInWords: csize_t, mbstr: cstring, count: csize_t): cint {.importc: "mbstowcs_s", header: "".} + # xxx cint vs errno_t? + + proc setEnvImpl*(name: cstring, value: cstring, overwrite: cint): cint = + const EINVAL = cint(22) + const MAX_ENV = 32767 + # xxx get it from: `var MAX_ENV {.importc: "_MAX_ENV", header:"".}: cint` + if overwrite == 0 and c_getenv(name) != nil: return 0 + if value[0] != '\0': + let e = c_putenv_s(name, value) + if e != 0: + errno = e + return -1 + return 0 + #[ + We are trying to set the value to an empty string, but `_putenv_s` deletes + entries if the value is an empty string, and just calling + SetEnvironmentVariableA doesn't update `_environ`, + so we have to do these terrible things. + ]# + if c_putenv_s(name, " ") != 0: + errno = EINVAL + return -1 + # Here lies the documentation we blatently ignore to make this work. + var s = c_getenv(name) + s[0] = '\0' + #[ + This would result in a double null termination, which normally signifies the + end of the environment variable list, so we stick a completely empty + environment variable into the list instead. + ]# + s[1] = '=' + #[ + If gWenviron is null, the wide environment has not been initialized + yet, and we don't need to try to update it. We have to do this otherwise + we'd be forcing the initialization and maintenance of the wide environment + even though it's never actually used in most programs. + ]# + if gWenviron != nil: + # var buf: array[MAX_ENV + 1, WideCString] + var buf: array[MAX_ENV + 1, Utf16Char] + let buf2 = cast[WideCString](buf[0].addr) + var len: csize_t + if mbstowcs_s(len.addr, buf2, buf.len.csize_t, name, MAX_ENV) != 0: + errno = EINVAL + return -1 + c_wgetenv(buf2)[0] = '\0'.Utf16Char + c_wgetenv(buf2)[1] = '='.Utf16Char + + # And now, we have to update the outer environment to have a proper empty value. + if setEnvironmentVariableA(name, value) == 0: + errno = EINVAL + return -1 + return 0 diff --git a/tests/destructor/tnewruntime_misc.nim b/tests/destructor/tnewruntime_misc.nim index 48ea36b7c..ac061f2f7 100644 --- a/tests/destructor/tnewruntime_misc.nim +++ b/tests/destructor/tnewruntime_misc.nim @@ -7,7 +7,7 @@ axc ... destroying GenericObj[T] GenericObj[system.int] test -(allocCount: 13, deallocCount: 11) +(allocCount: 12, deallocCount: 10) 3''' """ diff --git a/tests/stdlib/tos.nim b/tests/stdlib/tos.nim index fedf2b3e3..dcb2d44f4 100644 --- a/tests/stdlib/tos.nim +++ b/tests/stdlib/tos.nim @@ -331,7 +331,7 @@ block walkDirRec: doAssert p.startsWith("walkdir_test") var s: seq[string] - for p in walkDirRec("walkdir_test", {pcFile}, {pcDir}, relative=true): + for p in walkDirRec("walkdir_test", {pcFile}, {pcDir}, relative = true): s.add(p) doAssert s.len == 2 @@ -553,19 +553,19 @@ block ospaths: doAssert joinPath("/", "") == unixToNativePath"/" doAssert joinPath("/" / "") == unixToNativePath"/" # weird test case... doAssert joinPath("/", "/a/b/c") == unixToNativePath"/a/b/c" - doAssert joinPath("foo/","") == unixToNativePath"foo/" - doAssert joinPath("foo/","abc") == unixToNativePath"foo/abc" - doAssert joinPath("foo//./","abc/.//") == unixToNativePath"foo/abc/" - doAssert joinPath("foo","abc") == unixToNativePath"foo/abc" - doAssert joinPath("","abc") == unixToNativePath"abc" + doAssert joinPath("foo/", "") == unixToNativePath"foo/" + doAssert joinPath("foo/", "abc") == unixToNativePath"foo/abc" + doAssert joinPath("foo//./", "abc/.//") == unixToNativePath"foo/abc/" + doAssert joinPath("foo", "abc") == unixToNativePath"foo/abc" + doAssert joinPath("", "abc") == unixToNativePath"abc" - doAssert joinPath("zook/.","abc") == unixToNativePath"zook/abc" + doAssert joinPath("zook/.", "abc") == unixToNativePath"zook/abc" # controversial: inconsistent with `joinPath("zook/.","abc")` # on linux, `./foo` and `foo` are treated a bit differently for executables # but not `./foo/bar` and `foo/bar` doAssert joinPath(".", "/lib") == unixToNativePath"./lib" - doAssert joinPath(".","abc") == unixToNativePath"./abc" + doAssert joinPath(".", "abc") == unixToNativePath"./abc" # cases related to issue #13455 doAssert joinPath("foo", "", "") == "foo" @@ -605,24 +605,6 @@ block getTempDir: block: # getCacheDir doAssert getCacheDir().dirExists -block osenv: - block delEnv: - const dummyEnvVar = "DUMMY_ENV_VAR" # This env var wouldn't be likely to exist to begin with - doAssert existsEnv(dummyEnvVar) == false - putEnv(dummyEnvVar, "1") - doAssert existsEnv(dummyEnvVar) == true - delEnv(dummyEnvVar) - doAssert existsEnv(dummyEnvVar) == false - delEnv(dummyEnvVar) # deleting an already deleted env var - doAssert existsEnv(dummyEnvVar) == false - block: # putEnv, bug #18502 - doAssertRaises(OSError): putEnv("DUMMY_ENV_VAR_PUT=DUMMY_VALUE", "NEW_DUMMY_VALUE") - doAssertRaises(OSError): putEnv("", "NEW_DUMMY_VALUE") - block: - doAssert getEnv("DUMMY_ENV_VAR_NONEXISTENT", "") == "" - doAssert getEnv("DUMMY_ENV_VAR_NONEXISTENT", " ") == " " - doAssert getEnv("DUMMY_ENV_VAR_NONEXISTENT", "Arrakis") == "Arrakis" - block isRelativeTo: doAssert isRelativeTo("/foo", "/") doAssert isRelativeTo("/foo/bar", "/foo") diff --git a/tests/stdlib/tosenv.nim b/tests/stdlib/tosenv.nim new file mode 100644 index 000000000..df5759d0c --- /dev/null +++ b/tests/stdlib/tosenv.nim @@ -0,0 +1,58 @@ +discard """ + matrix: "--threads" + joinable: false + targets: "c js cpp" +""" + +import std/os +from std/sequtils import toSeq +import stdtest/testutils + +template main = + block: # delEnv, existsEnv, getEnv, envPairs + for val in ["val", ""]: # ensures empty val works too + const key = "NIM_TESTS_TOSENV_KEY" + doAssert not existsEnv(key) + putEnv(key, val) + doAssert existsEnv(key) + doAssert getEnv(key) == val + when nimvm: discard + else: + doAssert (key, val) in toSeq(envPairs()) + delEnv(key) + when nimvm: discard + else: + doAssert (key, val) notin toSeq(envPairs()) + doAssert not existsEnv(key) + delEnv(key) # deleting an already deleted env var + doAssert not existsEnv(key) + + block: + doAssert getEnv("NIM_TESTS_TOSENV_NONEXISTENT", "") == "" + doAssert getEnv("NIM_TESTS_TOSENV_NONEXISTENT", " ") == " " + doAssert getEnv("NIM_TESTS_TOSENV_NONEXISTENT", "defval") == "defval" + + whenVMorJs: discard # xxx improve + do: + doAssertRaises(OSError, putEnv("NIM_TESTS_TOSENV_PUT=DUMMY_VALUE", "NEW_DUMMY_VALUE")) + doAssertRaises(OSError, putEnv("", "NEW_DUMMY_VALUE")) + doAssert not existsEnv("") + doAssert not existsEnv("NIM_TESTS_TOSENV_PUT=DUMMY_VALUE") + doAssert not existsEnv("NIM_TESTS_TOSENV_PUT") + +static: main() +main() + +when not defined(js): + block: # bug #18533 + proc c_getenv(env: cstring): cstring {.importc: "getenv", header: "".} + var thr: Thread[void] + proc threadFunc {.thread.} = putEnv("foo", "fooVal2") + + putEnv("foo", "fooVal1") + doAssert getEnv("foo") == "fooVal1" + createThread(thr, threadFunc) + joinThreads(thr) + doAssert getEnv("foo") == $c_getenv("foo") + + doAssertRaises(OSError): delEnv("foo=bar") From afc0259b125f5be032bf4c13721669c5452f9143 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 29 Jul 2021 19:28:33 -0700 Subject: [PATCH 2409/3768] fix #18385 followup, by building nimsuggest with -d:release during testing (#18581) --- koch.nim | 5 +++++ nimsuggest/tester.nim | 15 +++++++++++---- 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/koch.nim b/koch.nim index 4296e4529..8dd897028 100644 --- a/koch.nim +++ b/koch.nim @@ -592,6 +592,11 @@ proc runCI(cmd: string) = execFold("Run rst2html tests", "nim r nimdoc/rsttester") execFold("Run nimpretty tests", "nim r nimpretty/tester.nim") when defined(posix): + # refs #18385, build with -d:release instead of -d:danger for testing + # We could also skip building nimsuggest in buildTools, or build it with -d:release + # in bundleNimsuggest depending on some environment variable when we are in CI. One advantage + # of rebuilding is this won't affect bin/nimsuggest when running runCI locally + execFold("build nimsuggest_testing", "nim c -o:bin/nimsuggest_testing -d:release nimsuggest/nimsuggest") execFold("Run nimsuggest tests", "nim r nimsuggest/tester") execFold("Run atlas tests", "nim c -r -d:atlasTests tools/atlas/atlas.nim clone https://github.com/disruptek/balls") diff --git a/nimsuggest/tester.nim b/nimsuggest/tester.nim index b62aa8783..1db33706a 100644 --- a/nimsuggest/tester.nim +++ b/nimsuggest/tester.nim @@ -6,6 +6,7 @@ # `nim r nimsuggest/tester.nim nimsuggest/tests/tsug_accquote.nim` import os, osproc, strutils, streams, re, sexp, net +from sequtils import toSeq type Test = object @@ -23,7 +24,7 @@ import std/compilesettings proc parseTest(filename: string; epcMode=false): Test = const cursorMarker = "#[!]#" - let nimsug = "bin" / addFileExt("nimsuggest", ExeExt) + let nimsug = "bin" / addFileExt("nimsuggest_testing", ExeExt) doAssert nimsug.fileExists, nimsug const libpath = querySetting(libPath) result.filename = filename @@ -272,6 +273,7 @@ proc runEpcTest(filename: string): int = let a = outp.readAll().strip() let port = parseInt(a) socket.connect("localhost", Port(port)) + for req, resp in items(s.script): if not runCmd(req, s.dest): socket.sendEpcStr(req) @@ -279,8 +281,12 @@ proc runEpcTest(filename: string): int = if not req.startsWith("mod "): let answer = sexpToAnswer(sx) doReport(filename, answer, resp, report) - finally: + socket.sendEpcStr "return arg" + # bugfix: this was in `finally` block, causing the original error to be + # potentially masked by another one in case `socket.sendEpcStr` raises + # (e.g. if socket couldn't connect in the 1st place) + finally: close(p) if report.len > 0: echo "==== EPC ========================================" @@ -336,8 +342,9 @@ proc main() = failures += runTest(xx) failures += runEpcTest(xx) else: - for x in walkFiles(tpath / "t*.nim"): - echo "Test ", x + let files = toSeq(walkFiles(tpath / "t*.nim")) + for i, x in files: + echo "$#/$# test: $#" % [$i, $files.len, x] when defined(i386): if x == "nimsuggest/tests/tmacro_highlight.nim": echo "skipping" # workaround bug #17945 From 9a26f5059be48b19d821c17fe1066c05d167bc7d Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 30 Jul 2021 00:30:22 -0700 Subject: [PATCH 2410/3768] refs #16613: check opcWrDeref for nil (#18613) --- compiler/vm.nim | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/compiler/vm.nim b/compiler/vm.nim index 9541af701..73a228b6c 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -812,11 +812,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = # vmgen generates opcWrDeref, which means that we must dereference # twice. # TODO: This should likely be handled differently in vmgen. - if (nfIsRef notin regs[ra].nodeAddr[].flags and - nfIsRef notin n.flags): - regs[ra].nodeAddr[][] = n[] - else: - regs[ra].nodeAddr[] = n + let nAddr = regs[ra].nodeAddr + if nAddr[] == nil: stackTrace(c, tos, pc, "opcWrDeref internal error") # refs bug #16613 + if (nfIsRef notin nAddr[].flags and nfIsRef notin n.flags): nAddr[][] = n[] + else: nAddr[] = n of rkRegisterAddr: regs[ra].regAddr[] = regs[rc] of rkNode: # xxx: also check for nkRefTy as in opcLdDeref? From c6fadb179975f79ad9914f09ce10f7fa6cbf0735 Mon Sep 17 00:00:00 2001 From: flywind Date: Fri, 30 Jul 2021 15:32:00 +0800 Subject: [PATCH 2411/3768] [minor] reduce `substr` (#18611) * minor * correct * unify the type of addrLen * Update lib/packages/docutils/rstgen.nim --- lib/packages/docutils/rstgen.nim | 2 +- lib/pure/nativesockets.nim | 10 ++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 10a229479..3734392c3 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -675,7 +675,7 @@ proc readIndexDir(dir: string): var x = fileEntries[0].link let i = find(x, '#') if i > 0: - x = x.substr(0, i-1) + x.setLen(i) if i != 0: # don't add entries starting with '#' result.modules.add(x.changeFileExt("")) diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim index 124afce56..13c08dd92 100644 --- a/lib/pure/nativesockets.nim +++ b/lib/pure/nativesockets.nim @@ -14,6 +14,7 @@ import os, options import std/private/since +import std/strbasics when hostOS == "solaris": @@ -468,7 +469,7 @@ proc getAddrString*(sockAddr: ptr SockAddr): string = if sockAddr.sa_family.cint == nativeAfInet: result = $inet_ntoa(cast[ptr Sockaddr_in](sockAddr).sin_addr) elif sockAddr.sa_family.cint == nativeAfInet6: - let addrLen = when not useWinVersion: posix.INET6_ADDRSTRLEN + let addrLen = when not useWinVersion: posix.INET6_ADDRSTRLEN.int else: 46 # it's actually 46 in both cases result = newString(addrLen) let addr6 = addr cast[ptr Sockaddr_in6](sockAddr).sin6_addr @@ -477,7 +478,7 @@ proc getAddrString*(sockAddr: ptr SockAddr): string = result.len.int32) == nil: raiseOSError(osLastError()) if posix.IN6_IS_ADDR_V4MAPPED(addr6) != 0: - result = result.substr("::ffff:".len) + result.setSlice("::ffff:".len.. Date: Fri, 30 Jul 2021 12:05:14 +0200 Subject: [PATCH 2412/3768] fixes #18371 (#18617) --- compiler/vmops.nim | 7 +++++-- lib/std/effecttraits.nim | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/compiler/vmops.nim b/compiler/vmops.nim index 6ee915bd5..a1a4996b2 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -303,12 +303,15 @@ proc registerAdditionalOps*(c: PCtx) = proc getEffectList(c: PCtx; a: VmArgs; effectIndex: int) = let fn = getNode(a, 0) + var list = newNodeI(nkBracket, fn.info) if fn.typ != nil and fn.typ.n != nil and fn.typ.n[0].len >= effectListLen and fn.typ.n[0][effectIndex] != nil: - var list = newNodeI(nkBracket, fn.info) for e in fn.typ.n[0][effectIndex]: list.add opMapTypeInstToAst(c.cache, e.typ.skipTypes({tyRef}), e.info, c.idgen) - setResult(a, list) + else: + list.add newIdentNode(getIdent(c.cache, "UncomputedEffects"), fn.info) + + setResult(a, list) registerCallback c, "stdlib.effecttraits.getRaisesListImpl", proc (a: VmArgs) = getEffectList(c, a, exceptionEffects) diff --git a/lib/std/effecttraits.nim b/lib/std/effecttraits.nim index 358280db0..635ec82d0 100644 --- a/lib/std/effecttraits.nim +++ b/lib/std/effecttraits.nim @@ -11,7 +11,7 @@ ## for Nim's macro system. ## **Since**: Version 1.4. ## -## One can test for the existance of this standard module +## One can test for the existence of this standard module ## via `defined(nimHasEffectTraitsModule)`. import macros From ecfc47b332a67bb373d433f01277bc924b75f77f Mon Sep 17 00:00:00 2001 From: Antonis Geralis <43617260+planetis-m@users.noreply.github.com> Date: Fri, 30 Jul 2021 17:36:16 +0300 Subject: [PATCH 2413/3768] Modify atlas test for short commit hashes (#18619) * test commit hashes support * Update testdata.nim * remove extra newlines --- tools/atlas/packagesjson.nim | 2 -- tools/atlas/testdata.nim | 2 +- tools/atlas/tests/balls.nimble | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/tools/atlas/packagesjson.nim b/tools/atlas/packagesjson.nim index e1e23ee29..ed83ce85c 100644 --- a/tools/atlas/packagesjson.nim +++ b/tools/atlas/packagesjson.nim @@ -70,7 +70,6 @@ proc search*(pkgList: seq[Package]; terms: seq[string]) = var found = false template onFound = echo pkg - echo("") found = true break forPackage @@ -88,7 +87,6 @@ proc search*(pkgList: seq[Package]; terms: seq[string]) = onFound() else: echo(pkg) - echo(" ") if not found and terms.len > 0: echo("No package found.") diff --git a/tools/atlas/testdata.nim b/tools/atlas/testdata.nim index f960e3d08..aefaeacd2 100644 --- a/tools/atlas/testdata.nim +++ b/tools/atlas/testdata.nim @@ -25,7 +25,7 @@ const toData("ups", GitCheckout, 0, "4008f9339cd22b30e180bc87a6cca7270fd28ac1"), toData("sync", GitDiff, 0, ""), - toData("sync", GitTags, 0, "c9811af2d03dc78a920d33592d0a3daae3d9e68f refs/tags/v0.1.0\nc99d3a73344c4c02335190b489e7ea8aaa4e9584 refs/tags/v0.2.0\n6186a38c97c0f26e1a0c8016979227d26c41717b refs/tags/v0.3.0\n5506e4e09b849bd59f930fcccee3096abb089b1d refs/tags/v0.4.0\n1e4ef046ed0447aec7ca4ab3b2e2a5347ac41448 refs/tags/v0.5.0\na95f7b0b22c77b98ed63c1c4f435d550de8dbb05 refs/tags/v0.6.0\n214b868ace4ebc9140ee56734147fbd16b238c71 refs/tags/v0.7.0\n786698bf9f597834f36cd530d32faee31545a1d7 refs/tags/v0.9.0\n810bd2d75e9f6e182534ae2488670b51a9f13fc3 refs/tags/v1.0.0\na133d37991e3d4e9553c5261eabcb07d471968f7 refs/tags/v1.1.0\n0872a7cd70ea43a26deb94b8ab12cc5b89a1c212 refs/tags/v1.2.0\n19ac734438b6c04fe89711728a97fcde591324eb refs/tags/v1.3.0\nde5c7337ebc22422190e8aeca37d05651735f440 refs/tags/v1.4.0\n"), + toData("sync", GitRevParse, 0, "810bd2d75e9f6e182534ae2488670b51a9f13fc3\n"), toData("sync", GitCurrentCommit, 0, "de5c7337ebc22422190e8aeca37d05651735f440\n"), toData("sync", GitMergeBase, 0, "de5c7337ebc22422190e8aeca37d05651735f440\n810bd2d75e9f6e182534ae2488670b51a9f13fc3\n810bd2d75e9f6e182534ae2488670b51a9f13fc3\n"), diff --git a/tools/atlas/tests/balls.nimble b/tools/atlas/tests/balls.nimble index 62ab4750b..143e757e9 100644 --- a/tools/atlas/tests/balls.nimble +++ b/tools/atlas/tests/balls.nimble @@ -6,7 +6,7 @@ license = "MIT" # requires newTreeFrom requires "https://github.com/disruptek/grok >= 0.5.0 & < 1.0.0" requires "https://github.com/disruptek/ups < 1.0.0" -requires "https://github.com/planetis-m/sync >= 1.0.0 & < 2.0.0" +requires "https://github.com/planetis-m/sync#810bd2d" #requires "https://github.com/c-blake/cligen < 2.0.0" bin = @["balls"] # build the binary for basic test running From 52e276c82daf2b8a33f4e4304eac3074bb37b70d Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 31 Jul 2021 23:54:08 -0700 Subject: [PATCH 2414/3768] exportC => exportc (#18625) --- tests/template/template_pragmas.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/template/template_pragmas.nim b/tests/template/template_pragmas.nim index d6c99080d..da532b010 100644 --- a/tests/template/template_pragmas.nim +++ b/tests/template/template_pragmas.nim @@ -3,7 +3,7 @@ proc output_x:string {.compileTime.} = "x" template t = const x = output_x() let - bar {.exportC:"bar" & x.} = 100 + bar {.exportc:"bar" & x.} = 100 static: doAssert(compiles (t())) From 916d0c21af6153338a8064ff953b579ff87c6ba6 Mon Sep 17 00:00:00 2001 From: flywind Date: Sun, 1 Aug 2021 17:19:43 +0800 Subject: [PATCH 2415/3768] fix #18620 (#18624) * fix #18620 * add testcase --- lib/system/excpt.nim | 5 +---- tests/exception/t18620.nim | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 4 deletions(-) create mode 100644 tests/exception/t18620.nim diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index 9f65db2fe..4814b77b4 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -524,10 +524,7 @@ proc getStackTrace(e: ref Exception): string = proc getStackTraceEntries*(e: ref Exception): seq[StackTraceEntry] = ## Returns the attached stack trace to the exception `e` as ## a `seq`. This is not yet available for the JS backend. - when not defined(nimSeqsV2): - shallowCopy(result, e.trace) - else: - result = move(e.trace) + shallowCopy(result, e.trace) proc getStackTraceEntries*(): seq[StackTraceEntry] = ## Returns the stack trace entries for the current stack trace. diff --git a/tests/exception/t18620.nim b/tests/exception/t18620.nim new file mode 100644 index 000000000..ee23f8bac --- /dev/null +++ b/tests/exception/t18620.nim @@ -0,0 +1,17 @@ +discard """ + matrix: "--gc:arc; --gc:refc" +""" + +proc hello() = + raise newException(ValueError, "You are wrong") + +var flag = false + +try: + hello() +except ValueError as e: + flag = true + doAssert len(getStackTraceEntries(e)) > 0 + doAssert len(getStackTraceEntries(e)) > 0 + +doAssert flag From 2bc07554ccd64d7405d0f386f17f7b1faa95bd6f Mon Sep 17 00:00:00 2001 From: flywind Date: Sun, 1 Aug 2021 19:52:24 +0800 Subject: [PATCH 2416/3768] make proc names consistent (#18626) * rename `endswith` to `endsWith` * rename --- compiler/semtypes.nim | 10 +++++----- tests/arc/tasyncawait.nim | 2 +- tests/async/tasyncawait.nim | 2 +- tests/async/tasyncnetudp.nim | 2 +- tests/async/tasyncssl.nim | 2 +- tests/async/tnewasyncudp.nim | 2 +- tests/async/twinasyncrw.nim | 2 +- tools/atlas/osutils.nim | 2 +- tools/trimcc.nim | 2 +- 9 files changed, 13 insertions(+), 13 deletions(-) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index db895cab6..cb7f847a9 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1700,14 +1700,14 @@ proc semStaticType(c: PContext, childNode: PNode, prev: PType): PType = result.rawAddSon(base) result.flags.incl tfHasStatic -proc semTypeof(c: PContext; n: PNode; prev: PType): PType = +proc semTypeOf(c: PContext; n: PNode; prev: PType): PType = openScope(c) let t = semExprWithType(c, n, {efInTypeof}) closeScope(c) fixupTypeOf(c, prev, t) result = t.typ -proc semTypeof2(c: PContext; n: PNode; prev: PType): PType = +proc semTypeOf2(c: PContext; n: PNode; prev: PType): PType = openScope(c) var m = BiggestInt 1 # typeOfIter if n.len == 3: @@ -1731,7 +1731,7 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = of nkTypeOfExpr: # for ``typeof(countup(1,3))``, see ``tests/ttoseq``. checkSonsLen(n, 1, c.config) - result = semTypeof(c, n[0], prev) + result = semTypeOf(c, n[0], prev) if result.kind == tyTypeDesc: result.flags.incl tfExplicit of nkPar: if n.len == 1: result = semTypeNode(c, n[0], prev) @@ -1833,9 +1833,9 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = result = semAnyRef(c, n, tyRef, prev) elif op.id == ord(wType): checkSonsLen(n, 2, c.config) - result = semTypeof(c, n[1], prev) + result = semTypeOf(c, n[1], prev) elif op.s == "typeof" and n[0].kind == nkSym and n[0].sym.magic == mTypeOf: - result = semTypeof2(c, n, prev) + result = semTypeOf2(c, n, prev) elif op.s == "owned" and optOwnedRefs notin c.config.globalOptions and n.len == 2: result = semTypeExpr(c, n[1], prev) else: diff --git a/tests/arc/tasyncawait.nim b/tests/arc/tasyncawait.nim index 75d9bc9b5..91a7453cd 100644 --- a/tests/arc/tasyncawait.nim +++ b/tests/arc/tasyncawait.nim @@ -40,7 +40,7 @@ proc readMessages(client: AsyncFD) {.async.} = clientCount.inc break else: - if line.startswith("Message "): + if line.startsWith("Message "): msgCount.inc else: doAssert false diff --git a/tests/async/tasyncawait.nim b/tests/async/tasyncawait.nim index f658a15ed..e86542b2d 100644 --- a/tests/async/tasyncawait.nim +++ b/tests/async/tasyncawait.nim @@ -34,7 +34,7 @@ proc readMessages(client: AsyncFD) {.async.} = clientCount.inc break else: - if line.startswith("Message "): + if line.startsWith("Message "): msgCount.inc else: doAssert false diff --git a/tests/async/tasyncnetudp.nim b/tests/async/tasyncnetudp.nim index ef6dfc5e1..dade96fb2 100644 --- a/tests/async/tasyncnetudp.nim +++ b/tests/async/tasyncnetudp.nim @@ -59,7 +59,7 @@ proc readMessages(server: AsyncSocket) {.async.} = while i < maxResponses: let (data, fromIp, fromPort) = await recvFrom(server, 16384) - if data.startswith("Message ") and fromIp == "127.0.0.1": + if data.startsWith("Message ") and fromIp == "127.0.0.1": await sendTo(server, fromIp, fromPort, data) inc(msgCount) diff --git a/tests/async/tasyncssl.nim b/tests/async/tasyncssl.nim index a582818eb..222aaa3a1 100644 --- a/tests/async/tasyncssl.nim +++ b/tests/async/tasyncssl.nim @@ -36,7 +36,7 @@ when defined(ssl): inc(clientCount) break else: - if line.startswith("Message "): + if line.startsWith("Message "): inc(msgCount) else: doAssert false diff --git a/tests/async/tnewasyncudp.nim b/tests/async/tnewasyncudp.nim index 76462c21d..ba1e4134e 100644 --- a/tests/async/tnewasyncudp.nim +++ b/tests/async/tnewasyncudp.nim @@ -81,7 +81,7 @@ proc readMessages(server: AsyncFD) {.async.} = addr(slen)) size = 0 var grammString = $cstring(addr buffer) - if grammString.startswith("Message ") and + if grammString.startsWith("Message ") and saddr.sin_addr.s_addr == nativesockets.ntohl(INADDR_LOOPBACK.uint32): await sendTo(server, addr grammString[0], len(grammString), cast[ptr SockAddr](addr saddr), slen) diff --git a/tests/async/twinasyncrw.nim b/tests/async/twinasyncrw.nim index 69b092607..ad27ca38f 100644 --- a/tests/async/twinasyncrw.nim +++ b/tests/async/twinasyncrw.nim @@ -220,7 +220,7 @@ when defined(windows): clientCount.inc break else: - if line.startswith("Message "): + if line.startsWith("Message "): msgCount.inc else: doAssert false diff --git a/tools/atlas/osutils.nim b/tools/atlas/osutils.nim index 1bb414c03..6134830b5 100644 --- a/tools/atlas/osutils.nim +++ b/tools/atlas/osutils.nim @@ -17,7 +17,7 @@ proc cloneUrl*(url, dest: string; cloneUsingHttps: bool): string = # github + https + trailing url slash causes a # checkout/ls-remote to fail with Repository not found var isGithub = false - if modUrl.contains("github.com") and modUrl.endswith("/"): + if modUrl.contains("github.com") and modUrl.endsWith("/"): modUrl = modUrl[0 .. ^2] isGithub = true diff --git a/tools/trimcc.nim b/tools/trimcc.nim index 959eaf310..41795e1aa 100644 --- a/tools/trimcc.nim +++ b/tools/trimcc.nim @@ -31,7 +31,7 @@ proc processIncludes(dir: string, whitelist: StringTableRef) = if ('.' notin name and "include" in path) or ("c++" in path): let n = whitelist.getOrDefault(name) if n != "processed": whitelist[name] = "found" - if name.endswith(".h"): + if name.endsWith(".h"): let n = whitelist.getOrDefault(name) if n == "found": includes(path, name, whitelist) of pcDir: processIncludes(path, whitelist) From 499d4690fbbb59b6baf1698b1ed1574dde6ea7c5 Mon Sep 17 00:00:00 2001 From: Kyle Brown Date: Mon, 2 Aug 2021 00:38:15 -0700 Subject: [PATCH 2417/3768] Add Artix to distro list (#18629) --- lib/pure/distros.nim | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/pure/distros.nim b/lib/pure/distros.nim index c1bc44eeb..7a9910c1e 100644 --- a/lib/pure/distros.nim +++ b/lib/pure/distros.nim @@ -52,6 +52,7 @@ type CentOS Deepin ArchLinux + Artix Antergos PCLinuxOS Mageia @@ -133,7 +134,7 @@ type const - LacksDevPackages* = {Distribution.Gentoo, Distribution.Slackware, Distribution.ArchLinux} + LacksDevPackages* = {Distribution.Gentoo, Distribution.Slackware, Distribution.ArchLinux, Distribution.Artix} # we cache the result of the 'cmdRelease' # execution for faster platform detections. @@ -180,6 +181,8 @@ proc detectOsImpl(d: Distribution): bool = result = "rhel" in osReleaseID() of Distribution.ArchLinux: result = "arch" in osReleaseID() + of Distribution.Artix: + result = "artix" in osReleaseID() of Distribution.NixOS: # Check if this is a Nix build or NixOS environment result = existsEnv("NIX_BUILD_TOP") or existsEnv("__NIXOS_SET_ENVIRONMENT_DONE") @@ -249,7 +252,7 @@ proc foreignDepInstallCmd*(foreignPackageName: string): (string, bool) = result = ("pkg_add " & p, true) elif detectOs(PCLinuxOS): result = ("rpm -ivh " & p, true) - elif detectOs(ArchLinux) or detectOs(Manjaro): + elif detectOs(ArchLinux) or detectOs(Manjaro) or detectOs(Artix): result = ("pacman -S " & p, true) else: result = (" install " & p, true) From c0abdaa2b4587526f235ef41186e84c4f87813e1 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 2 Aug 2021 22:22:47 -0700 Subject: [PATCH 2418/3768] improve runnableExamples and docs for std/nre (#18634) * improve runnableExamples and docs for std/nre * avoid too long lines in example --- lib/impure/nre.nim | 117 +++++++++++++++++++-------------------------- 1 file changed, 49 insertions(+), 68 deletions(-) diff --git a/lib/impure/nre.nim b/lib/impure/nre.nim index 7b2d7d3ee..01ef77427 100644 --- a/lib/impure/nre.nim +++ b/lib/impure/nre.nim @@ -23,12 +23,12 @@ when defined(js): ## **Note**: If you love `sequtils.toSeq` we have bad news for you. This ## library doesn't work with it due to documented compiler limitations. As ## a workaround, use this: -## -## .. code-block:: nim -## -## import std/nre except toSeq -## -## +runnableExamples: + # either `import std/nre except toSeq` or fully qualify `sequtils.toSeq`: + import std/sequtils + iterator iota(n: int): int = + for i in 0..\w)").get.captures["letter"] == "a"` - ## - `"abc".match(re"(\w)\w").get.captures[-1] == "ab"` + ## `nil` is returned. See examples for `match`. ## ## `captureBounds[]: HSlice[int, int]` ## gets the bounds of the given capture according to the same rules as ## the above. If the capture is not filled, then `None` is returned. - ## The bounds are both inclusive. - ## - ## - `"abc".match(re"(\w)").get.captureBounds[0] == 0 .. 0` - ## - `0 in "abc".match(re"(\w)").get.captureBounds == true` - ## - `"abc".match(re"").get.captureBounds[-1] == 0 .. -1` - ## - `"abc".match(re"abc").get.captureBounds[-1] == 0 .. 2` + ## The bounds are both inclusive. See examples for `match`. ## ## `match: string` ## the full text of the match. @@ -198,7 +184,6 @@ type pattern*: Regex ## The regex doing the matching. ## Not nil. str*: string ## The string that was matched against. - ## Not nil. pcreMatchBounds: seq[HSlice[cint, cint]] ## First item is the bounds of the match ## Other items are the captures ## `a` is inclusive start, `b` is exclusive end @@ -226,18 +211,6 @@ type ## for whatever reason. The message contains the error ## code. -runnableExamples: - # This MUST be kept in sync with the examples in RegexMatch - doAssert "abc".match(re"(\w)").get.captures[0] == "a" - doAssert "abc".match(re"(?\w)").get.captures["letter"] == "a" - doAssert "abc".match(re"(\w)\w").get.captures[-1] == "ab" - - doAssert "abc".match(re"(\w)").get.captureBounds[0] == 0 .. 0 - doAssert 0 in "abc".match(re"(\w)").get.captureBounds == true - doAssert "abc".match(re"").get.captureBounds[-1] == 0 .. -1 - doAssert "abc".match(re"abc").get.captureBounds[-1] == 0 .. 2 - - proc destroyRegex(pattern: Regex) = pcre.free_substring(cast[cstring](pattern.pcreObj)) if pattern.pcreExtra != nil: @@ -540,18 +513,27 @@ proc matchImpl(str: string, pattern: Regex, start, endpos: int, flags: int): Opt proc match*(str: string, pattern: Regex, start = 0, endpos = int.high): Option[RegexMatch] = ## Like `find(...)<#find,string,Regex,int>`_, but anchored to the start of the ## string. - ## runnableExamples: - doAssert "foo".match(re"f").isSome - doAssert "foo".match(re"o").isNone + assert "foo".match(re"f").isSome + assert "foo".match(re"o").isNone + assert "abc".match(re"(\w)").get.captures[0] == "a" + assert "abc".match(re"(?\w)").get.captures["letter"] == "a" + assert "abc".match(re"(\w)\w").get.captures[-1] == "ab" + + assert "abc".match(re"(\w)").get.captureBounds[0] == 0 .. 0 + assert 0 in "abc".match(re"(\w)").get.captureBounds + assert "abc".match(re"").get.captureBounds[-1] == 0 .. -1 + assert "abc".match(re"abc").get.captureBounds[-1] == 0 .. 2 return str.matchImpl(pattern, start, endpos, pcre.ANCHORED) iterator findIter*(str: string, pattern: Regex, start = 0, endpos = int.high): RegexMatch = ## Works the same as `find(...)<#find,string,Regex,int>`_, but finds every - ## non-overlapping match. `"2222".find(re"22")` is `"22", "22"`, not - ## `"22", "22", "22"`. - ## + ## non-overlapping match: + runnableExamples: + import std/sugar + assert collect(for a in "2222".findIter(re"22"): a.match) == @["22", "22"] + # not @["22", "22", "22"] ## Arguments are the same as `find(...)<#find,string,Regex,int>`_ ## ## Variants: @@ -619,11 +601,10 @@ proc contains*(str: string, pattern: Regex, start = 0, endpos = int.high): bool ## Determine if the string contains the given pattern between the end and ## start positions: ## This function is equivalent to `isSome(str.find(pattern, start, endpos))`. - ## runnableExamples: - doAssert "abc".contains(re"bc") - doAssert not "abc".contains(re"cd") - doAssert not "abc".contains(re"a", start = 1) + assert "abc".contains(re"bc") + assert not "abc".contains(re"cd") + assert not "abc".contains(re"a", start = 1) return isSome(str.find(pattern, start, endpos)) @@ -635,16 +616,16 @@ proc split*(str: string, pattern: Regex, maxSplit = -1, start = 0): seq[string] ## runnableExamples: # - If the match is zero-width, then the string is still split: - doAssert "123".split(re"") == @["1", "2", "3"] + assert "123".split(re"") == @["1", "2", "3"] # - If the pattern has a capture in it, it is added after the string # split: - doAssert "12".split(re"(\d)") == @["", "1", "", "2", ""] + assert "12".split(re"(\d)") == @["", "1", "", "2", ""] # - If `maxsplit != -1`, then the string will only be split # `maxsplit - 1` times. This means that there will be `maxsplit` # strings in the output seq. - doAssert "1.2.3".split(re"\.", maxsplit = 2) == @["1", "2.3"] + assert "1.2.3".split(re"\.", maxsplit = 2) == @["1", "2.3"] result = @[] var lastIdx = start @@ -747,9 +728,9 @@ proc escapeRe*(str: string): string {.gcsafe.} = ## ## Escaped char: `\ + * ? [ ^ ] $ ( ) { } = ! < > | : -` runnableExamples: - doAssert escapeRe("fly+wind") == "fly\\+wind" - doAssert escapeRe("!") == "\\!" - doAssert escapeRe("nim*") == "nim\\*" + assert escapeRe("fly+wind") == "fly\\+wind" + assert escapeRe("!") == "\\!" + assert escapeRe("nim*") == "nim\\*" #([\\+*?[^\]$(){}=!<>|:-]) const SpecialCharMatcher = {'\\', '+', '*', '?', '[', '^', ']', '$', '(', From 6563a685c1076cb5342f65d192b8f2a983220dba Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Tue, 3 Aug 2021 13:16:33 -0300 Subject: [PATCH 2419/3768] Documentation only, add 1 example (#18633) --- doc/manual_experimental.rst | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/doc/manual_experimental.rst b/doc/manual_experimental.rst index 22db184a8..3272a4d0d 100644 --- a/doc/manual_experimental.rst +++ b/doc/manual_experimental.rst @@ -1157,6 +1157,24 @@ the ordinary AST predicates: a = b inc a, c +Another example: + +.. code-block:: nim + proc somefunc(s: string) = assert s == "variable" + proc somefunc(s: string{nkStrLit}) = assert s == "literal" + proc somefunc(s: string{nkRStrLit}) = assert s == r"raw" + proc somefunc(s: string{nkTripleStrLit}) = assert s == """triple""" + proc somefunc(s: static[string]) = assert s == "constant" + + # Use parameter constraints to provide overloads based on both the input parameter type and form. + var variable = "variable" + somefunc(variable) + const constant = "constant" + somefunc(constant) + somefunc("literal") + somefunc(r"raw") + somefunc("""triple""") + Pattern operators ----------------- From 2cddf7fc96a01bcd0f6029f38de005314333daca Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Sun, 8 Aug 2021 14:26:34 -0300 Subject: [PATCH 2420/3768] Documentation only, add 1 example (#18621) * ReSync with Devel * ReSync * Documentation only, add 1 example to For loop macro * Flip it * Update doc/manual.rst Co-authored-by: Andreas Rumpf Co-authored-by: Andreas Rumpf --- doc/manual.rst | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/doc/manual.rst b/doc/manual.rst index 1b65ae9ee..41ed49715 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -5830,6 +5830,27 @@ For loop macro A macro that takes as its only input parameter an expression of the special type `system.ForLoopStmt` can rewrite the entirety of a `for` loop: +.. code-block:: nim + :test: "nim c $1" + + import std/macros + + macro example(loop: ForLoopStmt) = + result = newTree(nnkForStmt) # Create a new For loop. + result.add loop[^3] # This is "item". + result.add loop[^2][^1] # This is "[1, 2, 3]". + result.add newCall(bindSym"echo", loop[0]) + + for item in example([1, 2, 3]): discard + +Expands to: + +.. code-block:: nim + for item in items([1, 2, 3]): + echo item + +Another example: + .. code-block:: nim :test: "nim c $1" @@ -5866,7 +5887,6 @@ type `system.ForLoopStmt` can rewrite the entirety of a `for` loop: echo a, " ", b - Special Types ============= From 9269a72f6f6f19e4bcffce82ad47c68755e7a696 Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 9 Aug 2021 01:27:43 +0800 Subject: [PATCH 2421/3768] [nre]fix #17129 (#18632) * fix #17129 * correct * give reference implementaion links * add comment * typo * I'm conservative * change --- lib/impure/nre.nim | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/impure/nre.nim b/lib/impure/nre.nim index 01ef77427..f346d84cb 100644 --- a/lib/impure/nre.nim +++ b/lib/impure/nre.nim @@ -539,7 +539,7 @@ iterator findIter*(str: string, pattern: Regex, start = 0, endpos = int.high): R ## Variants: ## ## - `proc findAll(...)` returns a `seq[string]` - # see pcredemo for explanation + # see pcredemo for explanation => https://www.pcre.org/original/doc/html/pcredemo.html let matchesCrLf = pattern.matchesCrLf() let unicode = uint32(getinfo[culong](pattern, pcre.INFO_OPTIONS) and pcre.UTF8) > 0u32 @@ -560,7 +560,7 @@ iterator findIter*(str: string, pattern: Regex, start = 0, endpos = int.high): R # either the end of the input or the string # cannot be split here - we also need to bail # if we've never matched and we've already tried to... - if offset >= strlen or neverMatched: + if flags == 0 or offset >= strlen or neverMatched: # All matches found break if matchesCrLf and offset < (str.len - 1) and @@ -576,7 +576,6 @@ iterator findIter*(str: string, pattern: Regex, start = 0, endpos = int.high): R else: neverMatched = false offset = match.get.matchBounds.b + 1 - yield match.get proc find*(str: string, pattern: Regex, start = 0, endpos = int.high): Option[RegexMatch] = From 24445d31b394a120a8571c0d9dc4ab07b7672cd7 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 8 Aug 2021 10:28:49 -0700 Subject: [PATCH 2422/3768] improve several tests in testament (#18635) * silence error output from template_various.nim * any => auto in tests * avoid showing failed for parseSpec since this is expected behavior in 2 cases: tincludefile.nim, tnav1.nim * enforce InheritFromException * fixup --- testament/categories.nim | 2 +- tests/config.nims | 1 + tests/generics/mdotlookup.nim | 4 +- tests/iter/tyieldintry.nim | 60 +++++++++++++--------------- tests/overload/toverload_various.nim | 2 +- tests/template/template_various.nim | 6 ++- tests/template/tparams_gensymed.nim | 2 +- 7 files changed, 38 insertions(+), 39 deletions(-) diff --git a/testament/categories.nim b/testament/categories.nim index b5e8a5454..6375105ee 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -588,7 +588,7 @@ proc runJoinedTest(r: var TResults, cat: Category, testsDir: string, options: st except ValueError: # e.g. for `tests/navigator/tincludefile.nim` which have multiple # specs; this will be handled elsewhere - echo "parseSpec failed for: '$1', assuming this will be handled outside of megatest" % file + echo "parseSpec raised ValueError for: '$1', assuming this will be handled outside of megatest" % file continue if isJoinableSpec(spec): specs.add spec diff --git a/tests/config.nims b/tests/config.nims index f876cb612..d7f6ae7f9 100644 --- a/tests/config.nims +++ b/tests/config.nims @@ -28,6 +28,7 @@ hint("Processing", off) # switch("define", "nimTestsEnableFlaky") # switch("hint", "ConvFromXtoItselfNotNeeded") +# switch("warningAsError", "InheritFromException") # would require fixing a few tests # experimental API's are enabled in testament, refs https://github.com/timotheecour/Nim/issues/575 # sync with `kochdocs.docDefines` or refactor. diff --git a/tests/generics/mdotlookup.nim b/tests/generics/mdotlookup.nim index 470fa0a51..3112c133f 100644 --- a/tests/generics/mdotlookup.nim +++ b/tests/generics/mdotlookup.nim @@ -1,9 +1,9 @@ -proc baz(o: any): int = 5 # if bar is exported, it works +proc baz(o: auto): int = 5 # if bar is exported, it works type MyObj = object x: int -proc foo*(b: any) = +proc foo*(b: auto) = var o: MyObj echo b.baz, " ", o.x.baz, " ", b.baz() diff --git a/tests/iter/tyieldintry.nim b/tests/iter/tyieldintry.nim index 3d5260800..9862fe1de 100644 --- a/tests/iter/tyieldintry.nim +++ b/tests/iter/tyieldintry.nim @@ -1,17 +1,15 @@ discard """ -targets: "c" -output: "ok" + targets: "c cpp" """ -var closureIterResult = newSeq[int]() -# XXX Investigate why this fails now for 'nim cpp' +var closureIterResult = newSeq[int]() proc checkpoint(arg: int) = closureIterResult.add(arg) type - TestException = object of Exception - AnotherException = object of Exception + TestError = object of CatchableError + AnotherError = object of CatchableError proc testClosureIterAux(it: iterator(): int, exceptionExpected: bool, expectedResults: varargs[int]) = closureIterResult.setLen(0) @@ -21,7 +19,7 @@ proc testClosureIterAux(it: iterator(): int, exceptionExpected: bool, expectedRe try: for i in it(): closureIterResult.add(i) - except TestException: + except TestError: exceptionCaught = true if closureIterResult != @expectedResults or exceptionCaught != exceptionExpected: @@ -39,8 +37,8 @@ proc test(it: iterator(): int, expectedResults: varargs[int]) = proc testExc(it: iterator(): int, expectedResults: varargs[int]) = testClosureIterAux(it, true, expectedResults) -proc raiseException() = - raise newException(TestException, "Test exception!") +proc raiseTestError() = + raise newException(TestError, "Test exception!") block: iterator it(): int {.closure.} = @@ -58,8 +56,8 @@ block: yield 0 try: checkpoint(1) - raiseException() - except TestException: + raiseTestError() + except TestError: checkpoint(2) yield 3 checkpoint(4) @@ -89,7 +87,7 @@ block: yield 0 try: yield 1 - raiseException() + raiseTestError() yield 2 finally: checkpoint(3) @@ -103,8 +101,8 @@ block: iterator it(): int {.closure.} = try: try: - raiseException() - except AnotherException: + raiseTestError() + except AnotherError: yield 123 finally: checkpoint(3) @@ -117,8 +115,8 @@ block: iterator it(): int {.closure.} = try: yield 1 - raiseException() - except AnotherException: + raiseTestError() + except AnotherError: checkpoint(123) finally: checkpoint(2) @@ -134,12 +132,12 @@ block: yield 1 try: yield 2 - raiseException() - except AnotherException: + raiseTestError() + except AnotherError: yield 123 finally: yield 3 - except AnotherException: + except AnotherError: yield 124 finally: yield 4 @@ -170,10 +168,10 @@ block: try: try: yield 0 - raiseException() + raiseTestError() finally: checkpoint(1) - except TestException: + except TestError: yield 2 return finally: @@ -188,10 +186,10 @@ block: try: try: yield 0 - raiseException() + raiseTestError() finally: return # Return in finally should stop exception propagation - except AnotherException: + except AnotherError: yield 2 return finally: @@ -228,9 +226,9 @@ block: var foo = 123 let i = try: yield 0 - raiseException() + raiseTestError() 1 - except TestException as e: + except TestError as e: assert(e.msg == "Test exception!") case foo of 1: @@ -262,9 +260,9 @@ block: template tryexcept: int = try: yield 1 - raiseException() + raiseTestError() 123 - except TestException: + except TestError: yield 2 checkpoint(3) 4 @@ -323,11 +321,11 @@ block: for i in 0 .. 1: try: yield 1 - raiseException() - except TestException as e: + raiseTestError() + except TestError as e: doAssert(e.msg == "Test exception!") yield 2 - except AnotherException: + except AnotherError: yield 123 except: yield 1234 @@ -497,5 +495,3 @@ block: #17849 - yield in case subject yield 5 test(it, 1, 2, 13, 5) - -echo "ok" diff --git a/tests/overload/toverload_various.nim b/tests/overload/toverload_various.nim index f913ce3ee..55a8d17e6 100644 --- a/tests/overload/toverload_various.nim +++ b/tests/overload/toverload_various.nim @@ -395,7 +395,7 @@ block: template bar2[F,T](x: FooUn[F,T]): int = 1 template bar2[F,T1,T2](x: FooBi[F,T1,T2]): int = 2 - proc test(x: any, n: int) = + proc test(x: auto, n: int) = doAssert(foo1(x) == n) doAssert(foo2(x) == n) doAssert(bar1(x) == n) diff --git a/tests/template/template_various.nim b/tests/template/template_various.nim index 75226ea2d..e7a2be748 100644 --- a/tests/template/template_various.nim +++ b/tests/template/template_various.nim @@ -94,7 +94,7 @@ block generic_templates: var i3: int = t1[int]("xx") - +from strutils import contains block tgetast_typeliar: proc error(s: string) = quit s @@ -111,7 +111,9 @@ block tgetast_typeliar: var message : NimNode = newLit(condition.repr) # echo message result = getAst assertOrReturn2(condition, message) - echo result.repr + # echo result.repr + let s = result.repr + doAssert """error("Assertion failed:""" in s proc point(size: int16): tuple[x, y: int16] = # returns random point in square area with given `size` diff --git a/tests/template/tparams_gensymed.nim b/tests/template/tparams_gensymed.nim index 1444667f4..b68d7e253 100644 --- a/tests/template/tparams_gensymed.nim +++ b/tests/template/tparams_gensymed.nim @@ -72,7 +72,7 @@ proc concreteProc(x: int) = forStatic i, 0..3: echo i -proc genericProc(x: any) = +proc genericProc(x: auto) = forStatic i, 0..3: echo i From eb19db6595846c92e5eb999d96605fe4bb0934f7 Mon Sep 17 00:00:00 2001 From: Kyle Brown Date: Sun, 8 Aug 2021 10:30:51 -0700 Subject: [PATCH 2423/3768] Nim pretty and add void (#18652) * pretty and add void * distro fixup * Update lib/pure/distros.nim Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> * tab to space * requested change to be cleaner Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> --- lib/pure/distros.nim | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/lib/pure/distros.nim b/lib/pure/distros.nim index 7a9910c1e..ddf0fd30c 100644 --- a/lib/pure/distros.nim +++ b/lib/pure/distros.nim @@ -109,7 +109,7 @@ type Clonezilla SteamOS Absolute - NixOS ## NixOS or a Nix build environment + NixOS ## NixOS or a Nix build environment AUSTRUMI Arya Porteus @@ -124,6 +124,7 @@ type ExTiX Rockstor GoboLinux + Void BSD FreeBSD @@ -134,7 +135,9 @@ type const - LacksDevPackages* = {Distribution.Gentoo, Distribution.Slackware, Distribution.ArchLinux, Distribution.Artix} + LacksDevPackages* = {Distribution.Gentoo, Distribution.Slackware, + Distribution.ArchLinux, Distribution.Artix, Distribution.Antergos, + Distribution.BlackArch, Distribution.ArchBang} # we cache the result of the 'cmdRelease' # execution for faster platform detections. @@ -153,7 +156,8 @@ template hostnamectl(): untyped = cmdRelease("hostnamectl", hostnamectlRes) proc detectOsWithAllCmd(d: Distribution): bool = let dd = toLowerAscii($d) result = dd in toLowerAscii(osReleaseID()) or dd in toLowerAscii(release()) or - dd in toLowerAscii(uname()) or ("operating system: " & dd) in toLowerAscii(hostnamectl()) + dd in toLowerAscii(uname()) or ("operating system: " & dd) in + toLowerAscii(hostnamectl()) proc detectOsImpl(d: Distribution): bool = case d @@ -173,9 +177,9 @@ proc detectOsImpl(d: Distribution): bool = case d of Distribution.Gentoo: result = ("-" & $d & " ") in uname() - of Distribution.Elementary, Distribution.Ubuntu, Distribution.Debian, Distribution.Fedora, - Distribution.OpenMandriva, Distribution.CentOS, Distribution.Alpine, - Distribution.Mageia, Distribution.Zorin: + of Distribution.Elementary, Distribution.Ubuntu, Distribution.Debian, + Distribution.Fedora, Distribution.OpenMandriva, Distribution.CentOS, + Distribution.Alpine, Distribution.Mageia, Distribution.Zorin, Distribution.Void: result = toLowerAscii($d) in osReleaseID() of Distribution.RedHat: result = "rhel" in osReleaseID() @@ -254,6 +258,8 @@ proc foreignDepInstallCmd*(foreignPackageName: string): (string, bool) = result = ("rpm -ivh " & p, true) elif detectOs(ArchLinux) or detectOs(Manjaro) or detectOs(Artix): result = ("pacman -S " & p, true) + elif detectOs(Void): + result = ("xbps-install " & p, true) else: result = (" install " & p, true) elif defined(haiku): From 0d3af5454b66dfe4a8a7017030708a890207c658 Mon Sep 17 00:00:00 2001 From: Christian Ulrich Date: Sun, 8 Aug 2021 20:11:07 +0200 Subject: [PATCH 2424/3768] Only allow IPv4 literals in strict form (#18656) * Only allow IPv4 literals in strict form The strict form as defined in RFC 6943, section 3.1.1 only allows the dotted form ddd.ddd.ddd.ddd of IPv4 literals, where ddd is a one to three digit decimal number between 0 and 255. Until now octal numbers (with a leading zero) were interpreted as decimal numbers which has security implications, see CVE-2021-29922 and CVE-2021-29923. * Update lib/pure/net.nim Co-authored-by: Dominik Picheta --- lib/pure/net.nim | 25 +++++++++-- tests/stdlib/tnet.nim | 35 +++++++++++++++ tests/stdlib/tparseipv6.nim | 90 ++++++++++++++++++++----------------- 3 files changed, 106 insertions(+), 44 deletions(-) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index c0f9e1465..09cdc8afb 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -281,14 +281,20 @@ proc parseIPv4Address(addressStr: string): IpAddress = byteCount = 0 currentByte: uint16 = 0 separatorValid = false + leadingZero = false result = IpAddress(family: IpAddressFamily.IPv4) for i in 0 .. high(addressStr): if addressStr[i] in strutils.Digits: # Character is a number + if leadingZero: + raise newException(ValueError, + "Invalid IP address. Octal numbers are not allowed") currentByte = currentByte * 10 + cast[uint16](ord(addressStr[i]) - ord('0')) - if currentByte > 255'u16: + if currentByte == 0'u16: + leadingZero = true + elif currentByte > 255'u16: raise newException(ValueError, "Invalid IP Address. Value is out of range") separatorValid = true @@ -300,6 +306,7 @@ proc parseIPv4Address(addressStr: string): IpAddress = currentByte = 0 byteCount.inc separatorValid = false + leadingZero = false else: raise newException(ValueError, "Invalid IP Address. Address contains an invalid character") @@ -388,10 +395,16 @@ proc parseIPv6Address(addressStr: string): IpAddress = result.address_v6[groupCount*2+1] = cast[uint8](currentShort and 0xFF) groupCount.inc() else: # Must parse IPv4 address + var leadingZero = false for i, c in addressStr[v4StartPos..high(addressStr)]: if c in strutils.Digits: # Character is a number + if leadingZero: + raise newException(ValueError, + "Invalid IP address. Octal numbers not allowed") currentShort = currentShort * 10 + cast[uint32](ord(c) - ord('0')) - if currentShort > 255'u32: + if currentShort == 0'u32: + leadingZero = true + elif currentShort > 255'u32: raise newException(ValueError, "Invalid IP Address. Value is out of range") separatorValid = true @@ -402,6 +415,7 @@ proc parseIPv6Address(addressStr: string): IpAddress = currentShort = 0 byteCount.inc() separatorValid = false + leadingZero = false else: # Invalid character raise newException(ValueError, "Invalid IP Address. Address contains an invalid character") @@ -431,7 +445,12 @@ proc parseIPv6Address(addressStr: string): IpAddress = proc parseIpAddress*(addressStr: string): IpAddress = ## Parses an IP address - ## Raises ValueError on error + ## + ## Raises ValueError on error. + ## + ## For IPv4 addresses, only the strict form as + ## defined in RFC 6943 is considered valid, see + ## https://datatracker.ietf.org/doc/html/rfc6943#section-3.1.1. if addressStr.len == 0: raise newException(ValueError, "IP Address string is empty") if addressStr.contains(':'): diff --git a/tests/stdlib/tnet.nim b/tests/stdlib/tnet.nim index b19d31f6c..671e0f7bc 100644 --- a/tests/stdlib/tnet.nim +++ b/tests/stdlib/tnet.nim @@ -50,6 +50,41 @@ block: # parseIpAddress tests expect(ValueError): discard parseIpAddress("gggg:cdba:0000:0000:0000:0000:3257:9652") + block: # ipv4-compatible ipv6 address (embedded ipv4 address) + check parseIpAddress("::ffff:10.0.0.23") == parseIpAddress("::ffff:0a00:0017") + + block: # octal number in ipv4 address + expect(ValueError): + discard parseIpAddress("010.8.8.8") + expect(ValueError): + discard parseIpAddress("8.010.8.8") + + block: # hexadecimal number in ipv4 address + expect(ValueError): + discard parseIpAddress("0xc0.168.0.1") + expect(ValueError): + discard parseIpAddress("192.0xa8.0.1") + + block: # less than 4 numbers in ipv4 address + expect(ValueError): + discard parseIpAddress("127.0.1") + + block: # octal number in embedded ipv4 address + expect(ValueError): + discard parseIpAddress("::ffff:010.8.8.8") + expect(ValueError): + discard parseIpAddress("::ffff:8.010.8.8") + + block: # hexadecimal number in embedded ipv4 address + expect(ValueError): + discard parseIpAddress("::ffff:0xc0.168.0.1") + expect(ValueError): + discard parseIpAddress("::ffff:192.0xa8.0.1") + + block: # less than 4 numbers in embedded ipv4 address + expect(ValueError): + discard parseIpAddress("::ffff:127.0.1") + block: # "IpAddress/Sockaddr conversion" proc test(ipaddrstr: string) = var ipaddr_1 = parseIpAddress(ipaddrstr) diff --git a/tests/stdlib/tparseipv6.nim b/tests/stdlib/tparseipv6.nim index 3e1c23e58..dd9abc511 100644 --- a/tests/stdlib/tparseipv6.nim +++ b/tests/stdlib/tparseipv6.nim @@ -8,23 +8,23 @@ const positives = [ "::f:8:a8f:218.17.235.229", "::b:228.19.241.2", - "::8:c:a:f:8.35.8.096", - "::3:e:a:bc:04.19.2.9", + "::8:c:a:f:8.35.8.96", + "::3:e:a:bc:4.19.2.9", "::2:212.242.248.19", "::df:a5f:3.250.208.9", "::8:c:5:e63:250.208.249.0", "::b:f:181.12.9.98", - "::a:f8:77.08.243.232", - "::a:b:85:e4d9:252.9.229.056", + "::a:f8:77.8.243.232", + "::a:b:85:e4d9:252.9.229.56", "941:c:8a:c:e::917", "e8:7a:e:ad:88a:8:203.235.225.46", - "139c:9e::f8:254.08.21.249", + "139c:9e::f8:254.8.21.249", "b38:f0:e::f9:89.6.12.18", "ef::8", "5::ab", "a::8:255.247.96.253", "b:c0::c:254.248.95.254", - "::8c:2:99.251.024.3", + "::8c:2:99.251.24.3", "98::c:247.240.249.57", "9::9", "628::f1ed:f", @@ -106,68 +106,68 @@ const "::315", "::a:a", "::aed3:a", - "f0eb:0:e8:b:c:a:254.098.233.17", + "f0eb:0:e8:b:c:a:254.98.233.17", "bfa:7fc:c66d:15:e9a:ded:254.119.9.9", - "d:ffa8:9:a:879:3:202.39.08.245", + "d:ffa8:9:a:879:3:202.39.8.245", "8e:2:8:fa8a:f1d1:1aa8:252.254.245.81", - "5:d4:a:e9:8:8:06.38.98.253", - "9c5:4:a5c:f:a6:8c9d:05.250.8.2", + "5:d4:a:e9:8:8:6.38.98.253", + "9c5:4:a5c:f:a6:8c9d:5.250.8.2", "d19a:2:f808:be:f:c:98.86.197.249", - "8:26ac:8:8:cb:f:242.00.254.85", + "8:26ac:8:8:cb:f:242.0.254.85", "38:e:1:0b88:f:0:8.89.248.92", - "e7:ff96:a:f:f:b:253.91.052.195", + "e7:ff96:a:f:f:b:253.91.52.195", "d:8:2:5:894:5:254.0.240.199", "2:98:9:8aa:9c8f:fa:252.98.248.17", - "e9:d4f:890:ccbe:5:8:088.200.228.216", + "e9:d4f:890:ccbe:5:8:88.200.228.216", "3:3:9:5:6a:df5:255.251.8.12", - "0280:3:8:8:4:9:255.000.251.249", + "0280:3:8:8:4:9:255.0.251.249", "8:af7:db:aa:0:9:238.248.250.255", - "ff:ee:9a:9252:a:289:059.083.18.255", + "ff:ee:9a:9252:a:289:59.83.18.255", "9f6:5:fc9:b:a89:a:142.1.250.254", "e:981a:da:bf94:9:f8:254.242.18.95", "3c:1:4:f2:89:f:8.91.255.14", - "e::9a2:c:9.050.80.8", + "e::9a2:c:9.50.80.8", "9::4a:07:fb:211.241.254.228", "9be::2:e:215.189.48.188", - "f::f:d:069.148.99.168", + "f::f:d:69.148.99.168", "f::a:97.18.240.47", "c::a98e:1:251.253.252.254", "668::82:214.87.208.9", "9c0::cf0:ecb:253.208.238.255", - "a::0:f1:210.240.238.049", - "8::a:1:251.238.34.09", + "a::0:f1:210.240.238.49", + "8::a:1:251.238.34.9", "81:dfe::b8:8.255.249.248", - "d3::7:b:9:83.189.08.244", - "8::9:8:8:00.7.11.252", + "d3::7:b:9:83.189.8.244", + "8::9:8:8:0.7.11.252", "2:8::c:a8:250.221.9.249", "2::f:99.8.249.247", "c:22f5::5:2c:243.15.79.89", "e:8e::da:251.243.255.2", "f15f:9::a:255.70.247.218", - "f:b::9f38:31.220.94.022", - "9::9a48:03.98.249.119", + "f:b::9f38:31.220.94.22", + "9::9a48:3.98.249.119", "d:d:9b87::2d:a:249.253.38.8", "d86d:99b::a9b:5:242.236.8.244", "eb:3::f:9cf:1.253.1.228", "b::ba2:255.247.114.64", - "2f:ec:bcb::9:219.254.250.094", + "2f:ec:bcb::9:219.254.250.94", "da8a:f6::a:e0:19.251.241.251", - "5e:c1::a:021.250.8.254", + "5e:c1::a:21.250.8.254", "c:9::8c9b:248.219.212.252", "2:a::8d4a:216.255.198.223", - "1f::66:255.30.08.150", + "1f::66:255.30.8.150", "bc2b:8f::2ff9:6.245.99.230", "a:8::a8:9.251.246.255", - "f:7:7::98:06.14.1.208", + "f:7:7::98:6.14.1.208", "e:2::9:218.249.255.254", "79:f::6:250.255.98.246", - "47:9:fb9f::9:038.136.17.251", + "47:9:fb9f::9:38.136.17.251", "ed::a:247.9.23.239", "6f::f1:88.254.119.9", "a::d:218.199.236.0", - "fc88::9:203.196.04.95", - "::8.048.255.85", - "::253.07.255.36", + "fc88::9:203.196.4.95", + "::8.48.255.85", + "::253.7.255.36", "9:d::253.7.178.229", "::250.84.158.253", "::8.55.204.248", @@ -175,31 +175,39 @@ const "df9:88ca::248.255.108.17", "8e9b::250.206.0.82", "::209.8.254.209", - "::247.088.8.8", + "::247.88.8.8", "::cb:f:ba41:250.208.19.249", "::fe:0e8:243.240.229.5", "::c:223.251.5.226", - "::8:08.03.8.250", + "::8:8.3.8.250", "::f:8.88.11.255", - "::fda:48:aa:05.189.07.2", - "::8:c3f:f:240.06.212.255", + "::fda:48:aa:5.189.7.2", + "::8:c3f:f:240.6.212.255", "::f:0aa:244.123.99.16", - "::c9b5:c:034.8.090.196", + "::c9b5:c:34.8.90.196", "::98:c9:254.14.241.81" ] negatives = ["foo.bar", "::::::::::::", "yet another failure", - "de:6:c:ab5:6a::9:252.06.06.249", - "f9:5f7:fa38:9:b::b6:09.255.248.252", + "de:6:c:ab5:6a::9:252.6.6.249", + "f9:5f7:fa38:9:b::b6:9.255.248.252", "97:c:5b:81:8a::f5dd:144.252.250.9", - "9:8:cd:8:a9::f:247.255.09.255", + "9:8:cd:8:a9::f:247.255.9.255", "18:1:8c:2:3::9:8.254.252.139", - "e:c298:3:e:a::bb12:254.246.05.250", + "e:c298:3:e:a::bb12:254.246.5.250", "e:e:c:8e:fd::8:253.8.49.231", "9:97f:f:e929:8a::c9:0.8.252.10", - "0df:b24:7:89:c::2b:16.249.240.092", + "0df:b24:7:89:c::2b:16.249.240.92", "b:8f5f:485:c:9a::84c:178.7.249.34", + "::3:e:a:bc:091.19.2.9", + "::a:f8:77.08.243.232", + "::8c:2:99.251.029.3", + "::8:c:a:f:8.35.8.096", + "d:ffa8:9:a:879:3:0202.39.8.245", + "139c:9e::f8:254.07.21.249", + "f0eb:0:e8:b:c:a:254.233.043.17", + "::a:b:85:e4d9:252.9.229.056", ] proc ok(pos: openarray[string]) = From a7e622267e1049a454f4f522c0dbce5eb4017495 Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 9 Aug 2021 02:44:14 +0800 Subject: [PATCH 2425/3768] use lent (#18638) --- lib/system/excpt.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index 4814b77b4..3e520570f 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -521,10 +521,10 @@ proc getStackTrace(e: ref Exception): string = else: result = "" -proc getStackTraceEntries*(e: ref Exception): seq[StackTraceEntry] = +proc getStackTraceEntries*(e: ref Exception): lent seq[StackTraceEntry] = ## Returns the attached stack trace to the exception `e` as ## a `seq`. This is not yet available for the JS backend. - shallowCopy(result, e.trace) + e.trace proc getStackTraceEntries*(): seq[StackTraceEntry] = ## Returns the stack trace entries for the current stack trace. From 988c02b7cbef634523b87af04097d06ed453dd61 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 8 Aug 2021 19:24:45 -0700 Subject: [PATCH 2426/3768] renamed: lib/std/private/vmutils.nim -> lib/std/private/bitops_utils.nim to avoid confusion with unrelated std/vmutils (#18660) --- lib/pure/bitops.nim | 4 +--- lib/std/private/{vmutils.nim => bitops_utils.nim} | 0 lib/system/countbits_impl.nim | 2 +- 3 files changed, 2 insertions(+), 4 deletions(-) rename lib/std/private/{vmutils.nim => bitops_utils.nim} (100%) diff --git a/lib/pure/bitops.nim b/lib/pure/bitops.nim index 6d3865e5d..1b3838621 100644 --- a/lib/pure/bitops.nim +++ b/lib/pure/bitops.nim @@ -27,9 +27,7 @@ import macros import std/private/since -from std/private/vmutils import forwardImpl, toUnsigned - - +from std/private/bitops_utils import forwardImpl, toUnsigned func bitnot*[T: SomeInteger](x: T): T {.magic: "BitnotI".} ## Computes the `bitwise complement` of the integer `x`. diff --git a/lib/std/private/vmutils.nim b/lib/std/private/bitops_utils.nim similarity index 100% rename from lib/std/private/vmutils.nim rename to lib/std/private/bitops_utils.nim diff --git a/lib/system/countbits_impl.nim b/lib/system/countbits_impl.nim index 184f97bd4..020423e01 100644 --- a/lib/system/countbits_impl.nim +++ b/lib/system/countbits_impl.nim @@ -9,7 +9,7 @@ ## Contains the used algorithms for counting bits. -from std/private/vmutils import forwardImpl, toUnsigned +from std/private/bitops_utils import forwardImpl, toUnsigned const useBuiltins* = not defined(noIntrinsicsBitOpts) From 8ce782d463b0bca091be5a34db190dc441c23ce4 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 8 Aug 2021 23:51:39 -0700 Subject: [PATCH 2427/3768] Add optional recursive arg to distinctBase (v2) (#18659) * Add optional recursive arg to distinctBase * Add docs and examples Co-authored-by: ALANVF --- compiler/semmagic.nim | 5 +++-- lib/pure/typetraits.nim | 17 ++++++++++++----- lib/std/jsonutils.nim | 4 ++-- lib/system/repr_v2.nim | 2 +- 4 files changed, 18 insertions(+), 10 deletions(-) diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 04f4c4729..87f7da815 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -190,9 +190,10 @@ proc evalTypeTrait(c: PContext; traitCall: PNode, operand: PType, context: PSym) result = newIntNodeT(toInt128(operand.len), traitCall, c.idgen, c.graph) of "distinctBase": var arg = operand.skipTypes({tyGenericInst}) + let rec = semConstExpr(c, traitCall[2]).intVal != 0 while arg.kind == tyDistinct: - arg = arg.base - arg = arg.skipTypes(skippedTypes + {tyGenericInst}) + arg = arg.base.skipTypes(skippedTypes + {tyGenericInst}) + if not rec: break result = getTypeDescNode(c, arg, operand.owner, traitCall.info) else: localError(c.config, traitCall.info, "unknown trait: " & s) diff --git a/lib/pure/typetraits.nim b/lib/pure/typetraits.nim index e63585f63..8dc1b8cf2 100644 --- a/lib/pure/typetraits.nim +++ b/lib/pure/typetraits.nim @@ -110,25 +110,32 @@ template pointerBase*[T](_: typedesc[ptr T | ref T]): typedesc = assert (var s = "abc"; s[0].addr).typeof.pointerBase is char T -proc distinctBase*(T: typedesc): typedesc {.magic: "TypeTrait".} = +proc distinctBase*(T: typedesc, recursive: static bool = true): typedesc {.magic: "TypeTrait".} = ## Returns the base type for distinct types, or the type itself otherwise. + ## If `recursive` is false, only the immediate distinct base will be returned. ## ## **See also:** - ## * `distinctBase template <#distinctBase.t,T>`_ + ## * `distinctBase template <#distinctBase.t,T,static[bool]>`_ runnableExamples: type MyInt = distinct int + type MyOtherInt = distinct MyInt doAssert distinctBase(MyInt) is int + doAssert distinctBase(MyOtherInt) is int + doAssert distinctBase(MyOtherInt, false) is MyInt doAssert distinctBase(int) is int since (1, 1): - template distinctBase*[T](a: T): untyped = - ## Overload of `distinctBase <#distinctBase,typedesc>`_ for values. + template distinctBase*[T](a: T, recursive: static bool = true): untyped = + ## Overload of `distinctBase <#distinctBase,typedesc,static[bool]>`_ for values. runnableExamples: type MyInt = distinct int + type MyOtherInt = distinct MyInt doAssert 12.MyInt.distinctBase == 12 + doAssert 12.MyOtherInt.distinctBase == 12 + doAssert 12.MyOtherInt.distinctBase(false) is MyInt doAssert 12.distinctBase == 12 when T is distinct: - distinctBase(typeof(a))(a) + distinctBase(typeof(a), recursive)(a) else: # avoids hint ConvFromXtoItselfNotNeeded a diff --git a/lib/std/jsonutils.nim b/lib/std/jsonutils.nim index c141985d3..727a75288 100644 --- a/lib/std/jsonutils.nim +++ b/lib/std/jsonutils.nim @@ -78,8 +78,8 @@ proc initToJsonOptions*(): ToJsonOptions = ## initializes `ToJsonOptions` with sane options. ToJsonOptions(enumMode: joptEnumOrd, jsonNodeMode: joptJsonNodeAsRef) -proc distinctBase(T: typedesc): typedesc {.magic: "TypeTrait".} -template distinctBase[T](a: T): untyped = distinctBase(typeof(a))(a) +proc distinctBase(T: typedesc, recursive: static bool = true): typedesc {.magic: "TypeTrait".} +template distinctBase[T](a: T, recursive: static bool = true): untyped = distinctBase(typeof(a), recursive)(a) macro getDiscriminants(a: typedesc): seq[string] = ## return the discriminant keys diff --git a/lib/system/repr_v2.nim b/lib/system/repr_v2.nim index 8471ea148..f99f09799 100644 --- a/lib/system/repr_v2.nim +++ b/lib/system/repr_v2.nim @@ -1,7 +1,7 @@ proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".} ## imported from typetraits -proc distinctBase(T: typedesc): typedesc {.magic: "TypeTrait".} +proc distinctBase(T: typedesc, recursive: static bool = true): typedesc {.magic: "TypeTrait".} ## imported from typetraits proc repr*(x: NimNode): string {.magic: "Repr", noSideEffect.} From 31fc0f97187cef472cf113629a90c7aa126d3693 Mon Sep 17 00:00:00 2001 From: Kyle Brown Date: Tue, 10 Aug 2021 16:26:42 -0700 Subject: [PATCH 2428/3768] Remove unused imports, and deprecated function usage (#18663) * clean up imports and slice to remove delete * revert buggy code * Replace "delete" with setlen to remove depreciation warning --- compiler/docgen.nim | 4 ++-- compiler/jsgen.nim | 2 +- tools/atlas/atlas.nim | 2 +- tools/atlas/packagesjson.nim | 1 - 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 3bc01bbe6..d39a20aae 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -835,7 +835,7 @@ proc docstringSummary(rstText: string): string = result = rstText.substr(2).strip var pos = result.find('\L') if pos > 0: - result.delete(pos, result.len - 1) + result.setLen(pos - 1) result.add("…") if pos < maxDocstringChars: return @@ -843,7 +843,7 @@ proc docstringSummary(rstText: string): string = pos = result.find({'.', ',', ':'}) let last = result.len - 1 if pos > 0 and pos < last: - result.delete(pos, last) + result.setLen(pos - 1) result.add("…") proc genDeprecationMsg(d: PDoc, n: PNode): string = diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 85947535e..49a57b4a6 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -29,7 +29,7 @@ implements the required case distinction. import - ast, astalgo, trees, magicsys, options, + ast, trees, magicsys, options, nversion, msgs, idents, types, ropes, passes, ccgutils, wordrecg, renderer, cgmeth, lowerings, sighashes, modulegraphs, lineinfos, rodutils, diff --git a/tools/atlas/atlas.nim b/tools/atlas/atlas.nim index d0990c1f7..761ca5db8 100644 --- a/tools/atlas/atlas.nim +++ b/tools/atlas/atlas.nim @@ -9,7 +9,7 @@ ## Simple tool to automate frequent workflows: Can "clone" ## a Nimble dependency and its dependencies recursively. -import std/[parseopt, strutils, os, osproc, sequtils, unicode, tables, sets] +import std/[parseopt, strutils, os, osproc, unicode, tables, sets] import parse_requires, osutils, packagesjson const diff --git a/tools/atlas/packagesjson.nim b/tools/atlas/packagesjson.nim index ed83ce85c..0b8599769 100644 --- a/tools/atlas/packagesjson.nim +++ b/tools/atlas/packagesjson.nim @@ -1,6 +1,5 @@ import std / [json, os, sets, strutils] -import osutils type Package* = ref object From d0dd923acf8c2ddfa5e0ea7574c8d5200416e9f5 Mon Sep 17 00:00:00 2001 From: RSDuck Date: Wed, 11 Aug 2021 08:54:49 +0200 Subject: [PATCH 2429/3768] Switch maintanance (#18668) * Fix and improve Nintendo Switch support * Document the necessity for nimAllocPagesViaMalloc * update changelog * Use --gc:orc in examples --- changelog.md | 3 + doc/nimc.rst | 21 ++--- lib/nintendoswitch/switch_memory.nim | 36 --------- lib/pure/os.nim | 9 +-- lib/system/osalloc.nim | 111 --------------------------- 5 files changed, 15 insertions(+), 165 deletions(-) delete mode 100644 lib/nintendoswitch/switch_memory.nim diff --git a/changelog.md b/changelog.md index a19697c8c..0e3c54d77 100644 --- a/changelog.md +++ b/changelog.md @@ -104,6 +104,8 @@ - In `std/dom`, `Interval` is now a `ref object`, same as `Timeout`. Definitions of `setTimeout`, `clearTimeout`, `setInterval`, `clearInterval` were updated. +- The allocator for Nintendo Switch, which was nonfunctional because + of breaking changes in libnx, was removed, in favour of the new `-d:nimAllocPagesViaMalloc` option. ## Standard library additions and changes @@ -363,6 +365,7 @@ - Added `dom.setInterval`, `dom.clearInterval` overloads. +- Allow reading parameters when compiling for Nintendo Switch. - Deprecated `sequtils.delete` and added an overload taking a `Slice` that raises a defect if the slice is out of bounds, likewise with `strutils.delete`. diff --git a/doc/nimc.rst b/doc/nimc.rst index ed9077912..ad5ecaa76 100644 --- a/doc/nimc.rst +++ b/doc/nimc.rst @@ -406,16 +406,18 @@ to your usual `nim c`:cmd: or `nim cpp`:cmd: command and set the `passC`:option: and `passL`:option: command line switches to something like: .. code-block:: cmd - nim c ... --passC="-I$DEVKITPRO/libnx/include" ... + nim c ... --d:nimAllocPagesViaMalloc --gc:orc --passC="-I$DEVKITPRO/libnx/include" ... --passL="-specs=$DEVKITPRO/libnx/switch.specs -L$DEVKITPRO/libnx/lib -lnx" or setup a ``nim.cfg`` file like so:: #nim.cfg + --gc:orc + --d:nimAllocPagesViaMalloc --passC="-I$DEVKITPRO/libnx/include" --passL="-specs=$DEVKITPRO/libnx/switch.specs -L$DEVKITPRO/libnx/lib -lnx" -The DevkitPro setup must be the same as the default with their new installer +The devkitPro setup must be the same as the default with their new installer `here for Mac/Linux `_ or `here for Windows `_. @@ -426,20 +428,19 @@ For example, with the above-mentioned config: nim c --os:nintendoswitch switchhomebrew.nim This will generate a file called ``switchhomebrew.elf`` which can then be turned into -an nro file with the `elf2nro`:cmd: tool in the DevkitPro release. Examples can be found at +an nro file with the `elf2nro`:cmd: tool in the devkitPro release. Examples can be found at `the nim-libnx github repo `_. -There are a few things that don't work because the DevkitPro libraries don't support them. +There are a few things that don't work because the devkitPro libraries don't support them. They are: 1. Waiting for a subprocess to finish. A subprocess can be started, but right now it can't be waited on, which sort of makes subprocesses a bit hard to use -2. Dynamic calls. DevkitPro libraries have no dlopen/dlclose functions. -3. Command line parameters. It doesn't make sense to have these for a console - anyways, so no big deal here. -4. mqueue. Sadly there are no mqueue headers. -5. ucontext. No headers for these either. No coroutines for now :( -6. nl_types. No headers for this. +2. Dynamic calls. Switch OS (Horizon) doesn't support dynamic libraries, so dlopen/dlclose are not available. +3. mqueue. Sadly there are no mqueue headers. +4. ucontext. No headers for these either. No coroutines for now :( +5. nl_types. No headers for this. +6. As mmap is not supported, the nimAllocPagesViaMalloc option has to be used. DLL generation ============== diff --git a/lib/nintendoswitch/switch_memory.nim b/lib/nintendoswitch/switch_memory.nim deleted file mode 100644 index f34bd363a..000000000 --- a/lib/nintendoswitch/switch_memory.nim +++ /dev/null @@ -1,36 +0,0 @@ -## All of these library headers and source can be found in the github repo -## https://github.com/switchbrew/libnx. - -const virtMemHeader = "" -const svcHeader = "" -const mallocHeader = "" - -## Aligns a block of memory with request `size` to `bytes` size. For -## example, a request of memalign(0x1000, 0x1001) == 0x2000 bytes allocated -proc memalign*(bytes: csize, size: csize): pointer {.importc: "memalign", - header: mallocHeader.} - -# Should be required, but not needed now because of how -# svcUnmapMemory frees all memory -#proc free*(address: pointer) {.importc: "free", -# header: mallocHeader.} - -## Maps a memaligned block of memory from `src_addr` to `dst_addr`. The -## Nintendo Switch requires this call in order to make use of memory, otherwise -## an invalid memory access occurs. -proc svcMapMemory*(dst_addr: pointer; src_addr: pointer; size: uint64): uint32 {. - importc: "svcMapMemory", header: svcHeader.} - -## Unmaps (frees) all memory from both `dst_addr` and `src_addr`. **Must** be called -## whenever svcMapMemory is used. The Switch will expect all memory to be allocated -## before gfxExit() calls () -proc svcUnmapMemory*(dst_addr: pointer; src_addr: pointer; size: uint64): uint32 {. - importc: "svcUnmapMemory", header: svcHeader.} - -proc virtmemReserveMap*(size: csize): pointer {.importc: "virtmemReserveMap", - header: virtMemHeader.} - -# Should be required, but not needed now because of how -# svcUnmapMemory frees all memory -#proc virtmemFreeMap*(address: pointer; size: csize) {.importc: "virtmemFreeMap", -# header: virtMemHeader.} diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 73e3fcb31..fd618e93c 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -2910,13 +2910,6 @@ elif defined(nodejs): result = $argv[i] else: raise newException(IndexDefect, formatErrorIndexBound(i - 1, argv.len - 2)) -elif defined(nintendoswitch): - proc paramStr*(i: int): string {.tags: [ReadIOEffect].} = - raise newException(OSError, "paramStr is not implemented on Nintendo Switch") - - proc paramCount*(): int {.tags: [ReadIOEffect].} = - raise newException(OSError, "paramCount is not implemented on Nintendo Switch") - elif defined(windows): # Since we support GUI applications with Nim, we sometimes generate # a WinMain entry proc. But a WinMain proc has no access to the parsed @@ -3190,7 +3183,7 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noW result = getApplAux("/proc/self/exe") elif defined(solaris): result = getApplAux("/proc/" & $getpid() & "/path/a.out") - elif defined(genode) or defined(nintendoswitch): + elif defined(genode): raiseOSError(OSErrorCode(-1), "POSIX command line not supported") elif defined(freebsd) or defined(dragonfly) or defined(netbsd): result = getApplFreebsd() diff --git a/lib/system/osalloc.nim b/lib/system/osalloc.nim index 2830adb48..39bf65d6c 100644 --- a/lib/system/osalloc.nim +++ b/lib/system/osalloc.nim @@ -103,117 +103,6 @@ elif defined(emscripten) and not defined(StandaloneHeapSize): elif defined(genode) and not defined(StandaloneHeapSize): include genode/alloc # osAllocPages, osTryAllocPages, osDeallocPages -elif defined(nintendoswitch) and not defined(StandaloneHeapSize): - - import nintendoswitch/switch_memory - - type - PSwitchBlock = ptr NSwitchBlock - ## This will hold the heap pointer data in a separate - ## block of memory that is PageSize bytes above - ## the requested memory. It's the only good way - ## to pass around data with heap allocations - NSwitchBlock {.pure, inheritable.} = object - realSize: int - heap: pointer # pointer to main heap alloc - heapMirror: pointer # pointer to virtmem mapped heap - - proc alignSize(size: int): int {.inline.} = - ## Align a size integer to be in multiples of PageSize - ## The nintendo switch will not allocate memory that is not - ## aligned to 0x1000 bytes and will just crash. - (size + (PageSize - 1)) and not (PageSize - 1) - - proc deallocate(heapMirror: pointer, heap: pointer, size: int) = - # Unmap the allocated memory - discard svcUnmapMemory(heapMirror, heap, size.uint64) - # These should be called (theoretically), but referencing them crashes the switch. - # The above call seems to free all heap memory, so these are not needed. - # virtmemFreeMap(nswitchBlock.heapMirror, nswitchBlock.realSize.csize) - # free(nswitchBlock.heap) - - proc freeMem(p: pointer) = - # Retrieve the switch block data from the pointer we set before - # The data is located just sizeof(NSwitchBlock) bytes below - # the top of the pointer to the heap - let - nswitchDescrPos = cast[ByteAddress](p) -% sizeof(NSwitchBlock) - nswitchBlock = cast[PSwitchBlock](nswitchDescrPos) - - deallocate( - nswitchBlock.heapMirror, nswitchBlock.heap, nswitchBlock.realSize - ) - - proc storeHeapData(address, heapMirror, heap: pointer, size: int) {.inline.} = - ## Store data in the heap for deallocation purposes later - - # the position of our heap pointer data. Since we allocated PageSize extra - # bytes, we should have a buffer on top of the requested size of at least - # PageSize bytes, which is much larger than sizeof(NSwitchBlock). So we - # decrement the address by sizeof(NSwitchBlock) and use that address - # to store our pointer data - let nswitchBlockPos = cast[ByteAddress](address) -% sizeof(NSwitchBlock) - - # We need to store this in a pointer obj (PSwitchBlock) so that the data sticks - # at the address we've chosen. If NSwitchBlock is used here, the data will - # be all 0 when we try to retrieve it later. - var nswitchBlock = cast[PSwitchBlock](nswitchBlockPos) - nswitchBlock.realSize = size - nswitchBlock.heap = heap - nswitchBlock.heapMirror = heapMirror - - proc getOriginalHeapPosition(address: pointer, difference: int): pointer {.inline.} = - ## This function sets the heap back to the originally requested - ## size - let - pos = cast[int](address) - newPos = cast[ByteAddress](pos) +% difference - - return cast[pointer](newPos) - - template allocPages(size: int, outOfMemoryStmt: untyped): untyped = - # This is to ensure we get a block of memory the requested - # size, as well as space to store our structure - let realSize = alignSize(size + sizeof(NSwitchBlock)) - - let heap = memalign(PageSize, realSize) - - if heap.isNil: - outOfMemoryStmt - - let heapMirror = virtmemReserveMap(realSize.csize) - result = heapMirror - - let rc = svcMapMemory(heapMirror, heap, realSize.uint64) - # Any return code not equal 0 means an error in libnx - if rc.uint32 != 0: - deallocate(heapMirror, heap, realSize) - outOfMemoryStmt - - # set result to be the original size requirement - result = getOriginalHeapPosition(result, realSize - size) - - storeHeapData(result, heapMirror, heap, realSize) - - proc osAllocPages(size: int): pointer {.inline.} = - allocPages(size): - raiseOutOfMem() - - proc osTryAllocPages(size: int): pointer = - allocPages(size): - return nil - - proc osDeallocPages(p: pointer, size: int) = - # Note that in order for the Switch not to crash, a call to - # deallocHeap(runFinalizers = true, allowGcAfterwards = false) - # must be run before gfxExit(). The Switch requires all memory - # to be deallocated before the graphics application has exited. - # - # gfxExit() can be found in in the github - # repo https://github.com/switchbrew/libnx - when reallyOsDealloc: - freeMem(p) - elif defined(posix) and not defined(StandaloneHeapSize): const PROT_READ = 1 # page can be read From 854006575498704c42b5ac465fdc74c3efdb5b97 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 11 Aug 2021 02:42:01 -0700 Subject: [PATCH 2430/3768] --cc:env now works correctly to assign linker executable, allowing to cross-compile/run for windows on osx via wine (#18672) * --cc:env now works correctly to assign linker executable, allowing using wine on osx * fixup --- compiler/extccomp.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index a091060ff..e7e1bcd51 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -252,7 +252,7 @@ compiler envcc: buildGui: "", buildDll: " -shared ", buildLib: "", # XXX: not supported yet - linkerExe: "cc", + linkerExe: "", linkTmpl: "-o $exefile $buildgui $builddll $objfiles $options", includeCmd: " -I", linkDirCmd: "", # XXX: not supported yet From 6c1bd4bb1cb7a6af38b5929fe02b069b03e39db4 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 11 Aug 2021 03:17:17 -0700 Subject: [PATCH 2431/3768] fix: `var a{.foo.} = expr` inside templates (refs #15920) (except when `foo` is overloaded) (#13869) * fix: `var a{.foo.} = expr` inside templates * add test * improve tdecls test * improve tests * add failing test * PRTEMP * fixup --- compiler/semstmts.nim | 35 ++++++++++++---------- compiler/semtempl.nim | 15 ++++++++-- tests/pragmas/tpragmas_misc.nim | 52 +++++++++++++++++++++++++++++++++ tests/stdlib/tdecls.nim | 23 ++++++++++++++- tests/stdlib/tsince.nim | 2 +- 5 files changed, 107 insertions(+), 20 deletions(-) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index cc09291c5..14dc89781 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -453,23 +453,29 @@ proc semLowerLetVarCustomPragma(c: PContext, a: PNode, n: PNode): PNode = return nil let nodePragma = b[1][0] # see: `singlePragma` - if nodePragma.kind notin {nkIdent, nkAccQuoted}: - return nil - let ident = considerQuotedIdent(c, nodePragma) - var userPragma = strTableGet(c.userPragmas, ident) - if userPragma != nil: return nil - - let w = nodePragma.whichPragma - if n.kind == nkVarSection and w in varPragmas or - n.kind == nkLetSection and w in letPragmas or - n.kind == nkConstSection and w in constPragmas: - return nil var amb = false - let sym = searchInScopes(c, ident, amb) - # XXX what if amb is true? - if sym == nil or sfCustomPragma in sym.flags: return nil + var sym: PSym = nil + case nodePragma.kind + of nkIdent, nkAccQuoted: + let ident = considerQuotedIdent(c, nodePragma) + var userPragma = strTableGet(c.userPragmas, ident) + if userPragma != nil: return nil + let w = nodePragma.whichPragma + if n.kind == nkVarSection and w in varPragmas or + n.kind == nkLetSection and w in letPragmas or + n.kind == nkConstSection and w in constPragmas: + return nil + sym = searchInScopes(c, ident, amb) + # XXX what if amb is true? + # CHECKME: should that test also apply to `nkSym` case? + if sym == nil or sfCustomPragma in sym.flags: return nil + of nkSym: + sym = nodePragma.sym + else: + return nil # skip if not in scope; skip `template myAttr() {.pragma.}` + let lhs = b[0] let clash = strTableGet(c.currentScope.symbols, lhs.ident) if clash != nil: @@ -477,7 +483,6 @@ proc semLowerLetVarCustomPragma(c: PContext, a: PNode, n: PNode): PNode = wrongRedefinition(c, lhs.info, lhs.ident.s, clash.info) result = newTree(nkCall) - doAssert nodePragma.kind in {nkIdent, nkAccQuoted}, $nodePragma.kind result.add nodePragma result.add lhs if a[1].kind != nkEmpty: diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index a2b0f99ba..2636784af 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -208,9 +208,18 @@ proc addLocalDecl(c: var TemplCtx, n: var PNode, k: TSymKind) = if (n.kind == nkPragmaExpr and n.len >= 2 and n[1].kind == nkPragma): let pragmaNode = n[1] for i in 0..= version: + body + when false: # bug + template fun3(): int {.since3: (1, 3).} = 12 + + block: # ditto, w + # case with overload + template since2(version: (int, int), body: untyped) {.dirty.} = + when (NimMajor, NimMinor) >= version: + body + template since2(version: (int, int, int), body: untyped) {.dirty.} = + when (NimMajor, NimMinor, NimPatch) >= version: + body + when false: # bug + template fun3(): int {.since2: (1, 3).} = 12 + +when true: # D20210801T100514:here + from macros import genSym + block: + template fn() = + var ret {.gensym.}: int # must special case template pragmas so it doesn't get confused + discard ret + fn() + static: discard genSym() diff --git a/tests/stdlib/tdecls.nim b/tests/stdlib/tdecls.nim index 3567639e0..53e070bee 100644 --- a/tests/stdlib/tdecls.nim +++ b/tests/stdlib/tdecls.nim @@ -1,6 +1,10 @@ +discard """ + targets: "c cpp js" +""" + import std/decls -block: +template fun() = var s = @[10,11,12] var a {.byaddr.} = s[0] a+=100 @@ -34,6 +38,13 @@ block: doAssert compiles(block: var b2 {.byaddr.}: int = s[2]) +proc fun2() = fun() +fun() +fun2() +static: fun2() +when false: # pending bug #13887 + static: fun() + ## We can define custom pragmas in user code template byUnsafeAddr(lhs, typ, expr) = when typ is type(nil): @@ -68,3 +79,13 @@ block: # nkAccQuoted let a {.`cast`.} = s[0] doAssert a == "foo" doAssert a[0].unsafeAddr == s[0][0].unsafeAddr + +block: # bug #15920 + template foo(lhs, typ, expr) = + let lhs = expr + proc fun1()= + let a {.foo.} = 1 + template fun2()= + let a {.foo.} = 1 + fun1() # ok + fun2() # BUG diff --git a/tests/stdlib/tsince.nim b/tests/stdlib/tsince.nim index 14dd09c15..d0320ff12 100644 --- a/tests/stdlib/tsince.nim +++ b/tests/stdlib/tsince.nim @@ -27,6 +27,6 @@ since (99, 3): doAssert false when false: - # pending https://github.com/timotheecour/Nim/issues/129 + # pending bug #15920 # Error: cannot attach a custom pragma to 'fun3' template fun3(): int {.since: (1, 3).} = 12 From bc14b7735929f764b09446073375174236fab161 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 11 Aug 2021 17:19:23 +0200 Subject: [PATCH 2432/3768] fixes #18665 DFA generator bug (#18676) --- compiler/dfa.nim | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/compiler/dfa.nim b/compiler/dfa.nim index 402945042..65c483e9c 100644 --- a/compiler/dfa.nim +++ b/compiler/dfa.nim @@ -769,6 +769,12 @@ proc gen(c: var Con; n: PNode) = of nkCharLit..nkNilLit: discard of nkAsgn, nkFastAsgn: gen(c, n[1]) + + if n[0].kind in PathKinds0: + let a = c.skipTrivials(n[0]) + if a.kind in nkCallKinds: + gen(c, a) + # watch out: 'obj[i].f2 = value' sets 'f2' but # "uses" 'i'. But we are only talking about builtin array indexing so # it doesn't matter and 'x = 34' is NOT a usage of 'x'. From c94933acb7a8fe08e1ef479d02bd20732ea1ea23 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 11 Aug 2021 13:30:51 -0700 Subject: [PATCH 2433/3768] fix #18674 --nimcache now works better with --os:windows (#18675) --- compiler/commands.nim | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/compiler/commands.nim b/compiler/commands.nim index 1c3de29ba..82e908152 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -537,6 +537,10 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; conf.lazyPaths.keepItIf(it != path) of "nimcache": expectArg(conf, switch, arg, pass, info) + var arg = arg + # refs bug #18674, otherwise `--os:windows` messes up with `--nimcache` set + # in config nims files, e.g. via: `import os; switch("nimcache", "/tmp/somedir")` + if conf.target.targetOS == osWindows and DirSep == '/': arg = arg.replace('\\', '/') conf.nimcacheDir = processPath(conf, arg, info, notRelativeToProj=true) of "out", "o": expectArg(conf, switch, arg, pass, info) From 018465a2345b2d11f7d1d711010d97e636af98d0 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 12 Aug 2021 08:25:11 +0200 Subject: [PATCH 2434/3768] fixes #18643 [backport:1.0] (#18678) --- compiler/ccgexprs.nim | 2 +- tests/array/tarray.nim | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 79c73b602..acf83f966 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -920,7 +920,7 @@ proc genArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) = if optBoundsCheck in p.options and ty.kind != tyUncheckedArray: if not isConstExpr(y): # semantic pass has already checked for const index expressions - if firstOrd(p.config, ty) == 0: + if firstOrd(p.config, ty) == 0 and lastOrd(p.config, ty) >= 0: if (firstOrd(p.config, b.t) < firstOrd(p.config, ty)) or (lastOrd(p.config, b.t) > lastOrd(p.config, ty)): linefmt(p, cpsStmts, "if ((NU)($1) > (NU)($2)){ #raiseIndexError2($1, $2); $3}$n", [rdCharLoc(b), intLiteral(lastOrd(p.config, ty)), raiseInstr(p)]) diff --git a/tests/array/tarray.nim b/tests/array/tarray.nim index 2765ad06d..d10011ef2 100644 --- a/tests/array/tarray.nim +++ b/tests/array/tarray.nim @@ -594,3 +594,14 @@ block t17705: a = int(a) var b = array[0, int].high b = int(b) + +block t18643: + # https://github.com/nim-lang/Nim/issues/18643 + let a: array[0, int] = [] + var caught = false + let b = 9999999 + try: + echo a[b] + except IndexDefect: + caught = true + doAssert caught, "IndexDefect not caught!" From 5c1304a4181596e764b05679cd8a0044ab3398dd Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 12 Aug 2021 07:50:08 -0700 Subject: [PATCH 2435/3768] fix #18670 quoteShellCommand, quoteShell, quoteShellWindows on windows (#18671) --- lib/pure/os.nim | 11 ++++++----- tests/stdlib/tos.nim | 13 ++++++++++++- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index fd618e93c..cfdacdb3c 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -1045,15 +1045,12 @@ proc expandTilde*(path: string): string {. # TODO: handle `~bob` and `~bob/` which means home of bob result = path -# TODO: consider whether quoteShellPosix, quoteShellWindows, quoteShell, quoteShellCommand -# belong in `strutils` instead; they are not specific to paths proc quoteShellWindows*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} = ## Quote `s`, so it can be safely passed to Windows API. ## ## Based on Python's `subprocess.list2cmdline`. ## See `this link `_ ## for more details. - let needQuote = {' ', '\t'} in s or s.len == 0 result = "" var backslashBuff = "" @@ -1064,8 +1061,8 @@ proc quoteShellWindows*(s: string): string {.noSideEffect, rtl, extern: "nosp$1" if c == '\\': backslashBuff.add(c) elif c == '\"': - result.add(backslashBuff) - result.add(backslashBuff) + for i in 0.. 0: + result.add(backslashBuff) if needQuote: + result.add(backslashBuff) result.add("\"") + proc quoteShellPosix*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} = ## Quote ``s``, so it can be safely passed to POSIX shell. const safeUnixChars = {'%', '+', '-', '.', '/', '_', ':', '=', '@', diff --git a/tests/stdlib/tos.nim b/tests/stdlib/tos.nim index dcb2d44f4..b7816fd41 100644 --- a/tests/stdlib/tos.nim +++ b/tests/stdlib/tos.nim @@ -622,7 +622,18 @@ block: # quoteShellWindows doAssert quoteShellWindows("aaa\"") == "aaa\\\"" doAssert quoteShellWindows("") == "\"\"" -block: # quoteShellWindows +block: # quoteShellCommand + when defined(windows): + doAssert quoteShellCommand(["a b c", "d", "e"]) == """"a b c" d e""" + doAssert quoteShellCommand(["""ab"c""", r"\", "d"]) == """ab\"c \ d""" + doAssert quoteShellCommand(["""ab"c""", """ \""", "d"]) == """ab\"c " \\" d""" + doAssert quoteShellCommand(["""a\\\b""", """de fg""", "h"]) == """a\\\b "de fg" h""" + doAssert quoteShellCommand(["""a\"b""", "c", "d"]) == """a\\\"b c d""" + doAssert quoteShellCommand(["""a\\b c""", "d", "e"]) == """"a\\b c" d e""" + doAssert quoteShellCommand(["""a\\b\ c""", "d", "e"]) == """"a\\b\ c" d e""" + doAssert quoteShellCommand(["ab", ""]) == """ab """"" + +block: # quoteShellPosix doAssert quoteShellPosix("aaa") == "aaa" doAssert quoteShellPosix("aaa a") == "'aaa a'" doAssert quoteShellPosix("") == "''" From f559319a682fb2abbf2945fd3390585a54f044d5 Mon Sep 17 00:00:00 2001 From: flywind Date: Fri, 13 Aug 2021 00:21:01 +0800 Subject: [PATCH 2436/3768] fix a sqlite bug (#18669) --- lib/impure/db_sqlite.nim | 15 ++++----------- lib/std/private/dbutils.nim | 15 +++++++++++++++ tests/stdlib/tsqlitebindatas.nim | 28 ++++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 11 deletions(-) create mode 100644 lib/std/private/dbutils.nim diff --git a/lib/impure/db_sqlite.nim b/lib/impure/db_sqlite.nim index 7bd807a12..b600576df 100644 --- a/lib/impure/db_sqlite.nim +++ b/lib/impure/db_sqlite.nim @@ -171,7 +171,7 @@ import sqlite3, macros import db_common export db_common -import std/private/since +import std/private/[since, dbutils] type DbConn* = PSqlite3 ## Encapsulates a database connection. @@ -211,14 +211,7 @@ proc dbQuote*(s: string): string = add(result, '\'') proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string = - result = "" - var a = 0 - for c in items(string(formatstr)): - if c == '?': - add(result, dbQuote(args[a])) - inc(a) - else: - add(result, c) + dbFormatImpl(formatstr, dbQuote, args) proc prepare*(db: DbConn; q: string): SqlPrepared {.since: (1, 3).} = ## Creates a new `SqlPrepared` statement. @@ -642,7 +635,7 @@ proc getValue*(db: DbConn, stmtName: SqlPrepared): string proc tryInsertID*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): int64 - {.tags: [WriteDbEffect], raises: [].} = + {.tags: [WriteDbEffect], raises: [DbError].} = ## Executes the query (typically "INSERT") and returns the ## generated ID for the row or -1 in case of an error. ## @@ -699,7 +692,7 @@ proc insertID*(db: DbConn, query: SqlQuery, proc tryInsert*(db: DbConn, query: SqlQuery, pkName: string, args: varargs[string, `$`]): int64 - {.tags: [WriteDbEffect], raises: [], since: (1, 3).} = + {.tags: [WriteDbEffect], raises: [DbError], since: (1, 3).} = ## same as tryInsertID tryInsertID(db, query, args) diff --git a/lib/std/private/dbutils.nim b/lib/std/private/dbutils.nim new file mode 100644 index 000000000..0ae3b3702 --- /dev/null +++ b/lib/std/private/dbutils.nim @@ -0,0 +1,15 @@ +import db_common + + +template dbFormatImpl*(formatstr: SqlQuery, dbQuote: proc (s: string): string, args: varargs[string]): string = + var res = "" + var a = 0 + for c in items(string(formatstr)): + if c == '?': + if a == args.len: + dbError("""The number of "?" given exceeds the number of parameters present in the query.""") + add(res, dbQuote(args[a])) + inc(a) + else: + add(res, c) + res diff --git a/tests/stdlib/tsqlitebindatas.nim b/tests/stdlib/tsqlitebindatas.nim index 643f1e2e6..754c80ae1 100644 --- a/tests/stdlib/tsqlitebindatas.nim +++ b/tests/stdlib/tsqlitebindatas.nim @@ -48,3 +48,31 @@ block tsqlitebindatas: ## db_sqlite binary data db.close() doAssert tryRemoveFile(dbName) + + +block: + block: + const dbName = buildDir / "db.sqlite3" + var db = db_sqlite.open(dbName, "", "", "") + var witness = false + try: + db.exec(sql("CREATE TABLE table1 (url TEXT, other_field INT);")) + db.exec(sql("REPLACE INTO table (url, another_field) VALUES (?, '123');")) + except DbError as e: + witness = true + doAssert e.msg == "The number of \"?\" given exceeds the number of parameters present in the query." + finally: + db.close() + removeFile(dbName) + + doAssert witness + + block: + const dbName = buildDir / "db.sqlite3" + var db = db_sqlite.open(dbName, "", "", "") + try: + db.exec(sql("CREATE TABLE table1 (url TEXT, other_field INT);")) + db.exec(sql("INSERT INTO table1 (url, other_field) VALUES (?, ?);"), "http://domain.com/test?param=1", 123) + finally: + db.close() + removeFile(dbName) From 883c04d444e943ba8e3d071a4dfcaca68ab300f9 Mon Sep 17 00:00:00 2001 From: flywind Date: Fri, 13 Aug 2021 08:57:12 +0800 Subject: [PATCH 2437/3768] don't use space after proc names (#18681) --- lib/pure/strutils.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index ac078df4e..3179c73d2 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -2726,7 +2726,7 @@ func addf*(s: var string, formatstr: string, a: varargs[string, `$`]) {.rtl, add s, formatstr[i] inc(i) -func `%` *(formatstr: string, a: openArray[string]): string {.rtl, +func `%`*(formatstr: string, a: openArray[string]): string {.rtl, extern: "nsuFormatOpenArray".} = ## Interpolates a format string with the values from `a`. ## @@ -2774,7 +2774,7 @@ func `%` *(formatstr: string, a: openArray[string]): string {.rtl, result = newStringOfCap(formatstr.len + a.len shl 4) addf(result, formatstr, a) -func `%` *(formatstr, a: string): string {.rtl, +func `%`*(formatstr, a: string): string {.rtl, extern: "nsuFormatSingleElem".} = ## This is the same as `formatstr % [a]` (see ## `% func<#%25,string,openArray[string]>`_). From 3a1109a53be172a92c63ebc5bdb69c45e78e9ca6 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Thu, 12 Aug 2021 23:57:29 -0700 Subject: [PATCH 2438/3768] initial fix for compiling Nim on Zephyr RTOS (issue #18684) (#18685) * initial fix for compiling Nim on Zephyr RTOS (issue #18684) Co-authored-by: Jaremy J. Creechley --- lib/nimbase.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/nimbase.h b/lib/nimbase.h index e480c55fd..a83bd3006 100644 --- a/lib/nimbase.h +++ b/lib/nimbase.h @@ -75,7 +75,8 @@ __AVR__ #endif /* ------------------------------------------------------------------------- */ -#if defined(__GNUC__) +#if defined(__GNUC__) && !defined(__ZEPHYR__) +/* Zephyr does some magic in it's headers that override the GCC stdlib. This breaks that. */ # define _GNU_SOURCE 1 #endif From b24812df5f952427c1266e48c40b7956bdf3d999 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 13 Aug 2021 00:35:48 -0700 Subject: [PATCH 2439/3768] properly fix #10053 ; `FieldDefect` msg now shows discriminant value + lineinfo, in all backends (c,vm,js) (#11955) * fix #10053 FieldError for vm * fixup * FieldError now also shows runtime value of discriminant * fix field error reporting in vm * also report culprit line info in err msg * fix errors for newruntime 2 * fix for js * fixup * PRTEMP4 * works * works * works perfect * refactor * std/private/repr_impl * suppport --gc:arc * cleanup * refactor * simplify * simplify * simplify * fixup * move out compiler.vmgen.genCustom * fixup * fixup * add tests * revert compiler/debugutils.nim * simplify reprDiscriminant * fixup * lib/std/private/repr_impl.nim -> lib/system/repr_impl.nim * try to fix D20210812T165220 * honor --declaredlocs * control toFileLineCol via --declaredlocs --- compiler/astmsgs.nim | 11 ++++++++++ compiler/ccgexprs.nim | 41 +++++++++++++++++++++++++----------- compiler/ccgtypes.nim | 6 ++++++ compiler/cgen.nim | 2 +- compiler/jsgen.nim | 11 +++++----- compiler/pragmas.nim | 2 +- compiler/renderer.nim | 12 ----------- compiler/vm.nim | 5 ++++- compiler/vmgen.nim | 14 ++++++------ lib/system/chcks.nim | 10 +++++++++ lib/system/indexerrors.nim | 4 ++++ lib/system/jssys.nim | 4 ++-- lib/system/repr.nim | 2 ++ lib/system/repr_impl.nim | 15 +++++++++++++ lib/system/repr_v2.nim | 7 ++++++ lib/system/reprjs.nim | 2 ++ tests/misc/mfield_defect.nim | 30 ++++++++++++++++++++++++++ tests/misc/trunner.nim | 16 +++++++++++--- 18 files changed, 151 insertions(+), 43 deletions(-) create mode 100644 lib/system/repr_impl.nim create mode 100644 tests/misc/mfield_defect.nim diff --git a/compiler/astmsgs.nim b/compiler/astmsgs.nim index d9105b761..a9027126a 100644 --- a/compiler/astmsgs.nim +++ b/compiler/astmsgs.nim @@ -26,3 +26,14 @@ proc addDeclaredLoc*(result: var string, conf: ConfigRef; typ: PType) = proc addDeclaredLocMaybe*(result: var string, conf: ConfigRef; typ: PType) = if optDeclaredLocs in conf.globalOptions: addDeclaredLoc(result, conf, typ) + +template quoteExpr*(a: string): untyped = + ## can be used for quoting expressions in error msgs. + "'" & a & "'" + +proc genFieldDefect*(conf: ConfigRef, field: string, disc: PSym): string = + let obj = disc.owner.name.s # `types.typeToString` might be better, eg for generics + result = "field '$#' is not accessible for type '$#'" % [field, obj] + if optDeclaredLocs in conf.globalOptions: + result.add " [discriminant declared in $#]" % toFileLineCol(conf, disc.info) + result.add " using '$# = " % disc.name.s diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index acf83f966..7eb4a7a1d 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -18,6 +18,8 @@ proc getNullValueAuxT(p: BProc; orig, t: PType; obj, constOrNil: PNode, # -------------------------- constant expressions ------------------------ +proc rdSetElemLoc(conf: ConfigRef; a: TLoc, typ: PType): Rope + proc int64Literal(i: BiggestInt): Rope = if i > low(int64): result = "IL64($1)" % [rope(i)] @@ -873,16 +875,34 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) = v.r.add(".") v.r.add(disc.sym.loc.r) genInExprAux(p, it, u, v, test) - let msg = genFieldDefect(field, disc.sym) + var msg = "" + if optDeclaredLocs in p.config.globalOptions: + # xxx this should be controlled by a separate flag, and + # used for other similar defects so that location information is shown + # even without the expensive `--stacktrace`; binary size could be optimized + # by encoding the file names separately from `file(line:col)`, essentially + # passing around `TLineInfo` + the set of files in the project. + msg.add toFileLineCol(p.config, e.info) & " " + msg.add genFieldDefect(p.config, field.name.s, disc.sym) let strLit = genStringLiteral(p.module, newStrNode(nkStrLit, msg)) - if op.magic == mNot: - linefmt(p, cpsStmts, - "if ($1){ #raiseFieldError($2); $3}$n", - [rdLoc(test), strLit, raiseInstr(p)]) + + ## discriminant check + template fun(code) = linefmt(p, cpsStmts, code, [rdLoc(test)]) + if op.magic == mNot: fun("if ($1) ") else: fun("if (!($1)) ") + + ## call raiseFieldError2 on failure + let discIndex = rdSetElemLoc(p.config, v, u.t) + if optTinyRtti in p.config.globalOptions: + # not sure how to use `genEnumToStr` here + const code = "{ #raiseFieldError2($1, (NI)$3); $2} $n" + linefmt(p, cpsStmts, code, [strLit, raiseInstr(p), discIndex]) else: - linefmt(p, cpsStmts, - "if (!($1)){ #raiseFieldError($2); $3}$n", - [rdLoc(test), strLit, raiseInstr(p)]) + # complication needed for signed types + let first = p.config.firstOrd(disc.sym.typ) + let firstLit = int64Literal(cast[int](first)) + let discName = genTypeInfo(p.config, p.module, disc.sym.typ, e.info) + const code = "{ #raiseFieldError2($1, #reprDiscriminant(((NI)$3) + (NI)$4, $5)); $2} $n" + linefmt(p, cpsStmts, code, [strLit, raiseInstr(p), discIndex, firstLit, discName]) proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) = assert e[0].kind == nkDotExpr @@ -1565,10 +1585,7 @@ proc genNewFinalize(p: BProc, e: PNode) = initLocExpr(p, e[1], a) initLocExpr(p, e[2], f) initLoc(b, locExpr, a.lode, OnHeap) - if optTinyRtti in p.config.globalOptions: - ti = genTypeInfoV2(p.module, refType, e.info) - else: - ti = genTypeInfoV1(p.module, refType, e.info) + ti = genTypeInfo(p.config, p.module, refType, e.info) p.module.s[cfsTypeInit3].addf("$1->finalizer = (void*)$2;$n", [ti, rdLoc(f)]) b.r = ropecg(p.module, "($1) #newObj($2, sizeof($3))", [ getTypeDesc(p.module, refType), diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 67e701804..d05c4cd8b 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -1514,3 +1514,9 @@ proc genTypeInfoV1(m: BModule, t: PType; info: TLineInfo): Rope = proc genTypeSection(m: BModule, n: PNode) = discard + +proc genTypeInfo*(config: ConfigRef, m: BModule, t: PType; info: TLineInfo): Rope = + if optTinyRtti in config.globalOptions: + result = genTypeInfoV2(m, t, info) + else: + result = genTypeInfoV1(m, t, info) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 3b004d399..f55da7f65 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -15,7 +15,7 @@ import ccgutils, os, ropes, math, passes, wordrecg, treetab, cgmeth, rodutils, renderer, cgendata, aliases, lowerings, tables, sets, ndi, lineinfos, pathutils, transf, - injectdestructors + injectdestructors, astmsgs when not defined(leanCompiler): import spawn, semparallel diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 49a57b4a6..3aa5c474e 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -33,7 +33,7 @@ import nversion, msgs, idents, types, ropes, passes, ccgutils, wordrecg, renderer, cgmeth, lowerings, sighashes, modulegraphs, lineinfos, rodutils, - transf, injectdestructors, sourcemap + transf, injectdestructors, sourcemap, astmsgs import json, sets, math, tables, intsets, strutils @@ -1208,12 +1208,13 @@ proc genCheckedFieldOp(p: PProc, n: PNode, addrTyp: PType, r: var TCompRes) = let tmp = p.getTemp() lineF(p, "var $1 = $2;$n", tmp, obj.res) - useMagic(p, "raiseFieldError") + useMagic(p, "raiseFieldError2") useMagic(p, "makeNimstrLit") - let msg = genFieldDefect(field, disc) - lineF(p, "if ($1[$2.$3]$4undefined) { raiseFieldError(makeNimstrLit($5)); }$n", + useMagic(p, "reprDiscriminant") # no need to offset by firstOrd unlike for cgen + let msg = genFieldDefect(p.config, field.name.s, disc) + lineF(p, "if ($1[$2.$3]$4undefined) { raiseFieldError2(makeNimstrLit($5), reprDiscriminant($2.$3, $6)); }$n", setx.res, tmp, disc.loc.r, if negCheck: ~"!==" else: ~"===", - makeJSString(msg)) + makeJSString(msg), genTypeInfo(p, disc.typ)) if addrTyp != nil and mapType(p, addrTyp) == etyBaseIndex: r.typ = etyBaseIndex diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 8ae931d84..6637a8673 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -699,7 +699,7 @@ proc typeBorrow(c: PContext; sym: PSym, n: PNode) = incl(sym.typ.flags, tfBorrowDot) proc markCompilerProc(c: PContext; s: PSym) = - # minor hack ahead: FlowVar is the only generic .compilerProc type which + # minor hack ahead: FlowVar is the only generic .compilerproc type which # should not have an external name set: if s.kind != skType or s.name.s != "FlowVar": makeExternExport(c, s, "$1", s.info) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 3346e629c..393f35289 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -1758,15 +1758,3 @@ proc getTokSym*(r: TSrcGen): PSym = result = r.tokens[r.idx-1].sym else: result = nil - -proc quoteExpr*(a: string): string {.inline.} = - ## can be used for quoting expressions in error msgs. - "'" & a & "'" - -proc genFieldDefect*(field: PSym, disc: PSym): string = - ## this needs to be in a module accessible by jsgen, ccgexprs, and vm to - ## provide this error msg FieldDefect; msgs would be better but it does not - ## import ast - result = field.name.s.quoteExpr & " is not accessible using discriminant " & - disc.name.s.quoteExpr & " of type " & - disc.owner.name.s.quoteExpr diff --git a/compiler/vm.nim b/compiler/vm.nim index 73a228b6c..9fa25efbe 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1472,7 +1472,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = else: return TFullReg(kind: rkNone) of opcInvalidField: - stackTrace(c, tos, pc, errFieldXNotFound & regs[ra].node.strVal) + let msg = regs[ra].node.strVal + let disc = regs[instr.regB].regToNode + let msg2 = formatFieldDefect(msg, $disc) + stackTrace(c, tos, pc, msg2) of opcSetLenStr: decodeB(rkNode) #createStrKeepNode regs[ra] diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 4957a3339..725afdd3f 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -31,7 +31,7 @@ import tables import strutils, ast, types, msgs, renderer, vmdef, - intsets, magicsys, options, lowerings, lineinfos, transf + intsets, magicsys, options, lowerings, lineinfos, transf, astmsgs from modulegraphs import getBody @@ -1742,12 +1742,14 @@ proc genCheckedObjAccessAux(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags let lab1 = c.xjmp(n, if negCheck: opcFJmp else: opcTJmp, rs) c.freeTemp(rs) let strType = getSysType(c.graph, n.info, tyString) - var fieldNameRegister: TDest = c.getTemp(strType) - let strLit = newStrNode($accessExpr[1], accessExpr[1].info) + var msgReg: TDest = c.getTemp(strType) + let fieldName = $accessExpr[1] + let msg = genFieldDefect(c.config, fieldName, disc.sym) + let strLit = newStrNode(msg, accessExpr[1].info) strLit.typ = strType - c.genLit(strLit, fieldNameRegister) - c.gABC(n, opcInvalidField, fieldNameRegister) - c.freeTemp(fieldNameRegister) + c.genLit(strLit, msgReg) + c.gABC(n, opcInvalidField, msgReg, discVal) + c.freeTemp(msgReg) c.patch(lab1) proc genCheckedObjAccess(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags) = diff --git a/lib/system/chcks.nim b/lib/system/chcks.nim index 255d97cb2..8c7c95795 100644 --- a/lib/system/chcks.nim +++ b/lib/system/chcks.nim @@ -26,8 +26,18 @@ proc raiseIndexError() {.compilerproc, noinline.} = sysFatal(IndexDefect, "index out of bounds") proc raiseFieldError(f: string) {.compilerproc, noinline.} = + ## remove after bootstrap > 1.5.1 sysFatal(FieldDefect, f) +when defined(gcdestructors): + proc raiseFieldError2(f: string, discVal: int) {.compilerproc, noinline.} = + ## raised when field is inaccessible given runtime value of discriminant + sysFatal(FieldError, f & $discVal & "'") +else: + proc raiseFieldError2(f: string, discVal: string) {.compilerproc, noinline.} = + ## raised when field is inaccessible given runtime value of discriminant + sysFatal(FieldError, formatFieldDefect(f, discVal)) + proc raiseRangeErrorI(i, a, b: BiggestInt) {.compilerproc, noinline.} = when defined(standalone): sysFatal(RangeDefect, "value out of range") diff --git a/lib/system/indexerrors.nim b/lib/system/indexerrors.nim index 1b91789bd..6a8cb8a0a 100644 --- a/lib/system/indexerrors.nim +++ b/lib/system/indexerrors.nim @@ -1,4 +1,5 @@ # imported by other modules, unlike helpers.nim which is included +# xxx this is now included instead of imported, we should import instead template formatErrorIndexBound*[T](i, a, b: T): string = when defined(standalone): @@ -9,3 +10,6 @@ template formatErrorIndexBound*[T](i, a, b: T): string = template formatErrorIndexBound*[T](i, n: T): string = formatErrorIndexBound(i, 0, n) + +template formatFieldDefect*(f, discVal): string = + f & discVal & "'" diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim index b42dc3a20..63e7b3d9d 100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -171,8 +171,8 @@ proc raiseRangeError() {.compilerproc, noreturn.} = proc raiseIndexError(i, a, b: int) {.compilerproc, noreturn.} = raise newException(IndexDefect, formatErrorIndexBound(int(i), int(a), int(b))) -proc raiseFieldError(f: string) {.compilerproc, noreturn.} = - raise newException(FieldDefect, f) +proc raiseFieldError2(f: string, discVal: string) {.compilerproc, noreturn.} = + raise newException(FieldDefect, formatFieldDefect(f, discVal)) proc setConstr() {.varargs, asmNoStackFrame, compilerproc.} = asm """ diff --git a/lib/system/repr.nim b/lib/system/repr.nim index 020c2281f..e049d18fa 100644 --- a/lib/system/repr.nim +++ b/lib/system/repr.nim @@ -72,6 +72,8 @@ proc reprEnum(e: int, typ: PNimType): string {.compilerRtl.} = result = $e & " (invalid data!)" +include system/repr_impl + type PByteArray = ptr UncheckedArray[byte] # array[0xffff, byte] diff --git a/lib/system/repr_impl.nim b/lib/system/repr_impl.nim new file mode 100644 index 000000000..b9ec1890f --- /dev/null +++ b/lib/system/repr_impl.nim @@ -0,0 +1,15 @@ +#[ +other APIs common to system/repr and system/reprjs could be refactored here, eg: +* reprChar +* reprBool +* reprStr + +Another possibility in future work would be to have a single include file instead +of system/repr and system/reprjs, and use `when defined(js)` inside it. +]# + +proc reprDiscriminant*(e: int, typ: PNimType): string {.compilerRtl.} = + case typ.kind + of tyEnum: reprEnum(e, typ) + of tyBool: $(e != 0) + else: $e diff --git a/lib/system/repr_v2.nim b/lib/system/repr_v2.nim index f99f09799..ba94b881d 100644 --- a/lib/system/repr_v2.nim +++ b/lib/system/repr_v2.nim @@ -1,3 +1,5 @@ +include system/inclrtl + proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".} ## imported from typetraits @@ -66,6 +68,11 @@ proc repr*[Enum: enum](x: Enum): string {.magic: "EnumToStr", noSideEffect.} ## If a `repr` operator for a concrete enumeration is provided, this is ## used instead. (In other words: *Overwriting* is possible.) +proc reprDiscriminant*(e: int): string {.compilerproc.} = + # repr and reprjs can use `PNimType` to symbolize `e`; making this work here + # would require a way to pass the set of enum stringified values to cgen. + $e + proc repr*(p: pointer): string = ## repr of pointer as its hexadecimal value if p == nil: diff --git a/lib/system/reprjs.nim b/lib/system/reprjs.nim index cdc3403f8..28935a4ef 100644 --- a/lib/system/reprjs.nim +++ b/lib/system/reprjs.nim @@ -30,6 +30,8 @@ proc reprEnum(e: int, typ: PNimType): string {.compilerRtl.} = else: result = $e & " (invalid data!)" +include system/repr_impl + proc reprChar(x: char): string {.compilerRtl.} = result = "\'" case x diff --git a/tests/misc/mfield_defect.nim b/tests/misc/mfield_defect.nim new file mode 100644 index 000000000..53bfba40e --- /dev/null +++ b/tests/misc/mfield_defect.nim @@ -0,0 +1,30 @@ +#[ +ran from trunner +]# + + + + + + +# line 10 +type Kind = enum k0, k1, k2, k3, k4 + +type Foo = object + case kind: Kind + of k0: f0: int + of k1: f1: int + of k2: f2: int + of k3: f3: int + of k4: f4: int + +proc main()= + var foo = Foo(kind: k3, f3: 3) + let s1 = foo.f3 + doAssert s1 == 3 + let s2 = foo.f2 + +when defined case1: + static: main() +when defined case2: + main() diff --git a/tests/misc/trunner.nim b/tests/misc/trunner.nim index 8426e9aee..f874d38d9 100644 --- a/tests/misc/trunner.nim +++ b/tests/misc/trunner.nim @@ -37,9 +37,9 @@ proc runNimCmd(file, options = "", rtarg = ""): auto = echo cmd echo result[0] & "\n" & $result[1] -proc runNimCmdChk(file, options = "", rtarg = ""): string = - let (ret, status) = runNimCmd(file, options, rtarg = rtarg) - doAssert status == 0, $(file, options) & "\n" & ret +proc runNimCmdChk(file, options = "", rtarg = "", status = 0): string = + let (ret, status2) = runNimCmd(file, options, rtarg = rtarg) + doAssert status2 == status, $(file, options, status, status2) & "\n" & ret ret proc genShellCmd(filename: string): string = @@ -376,5 +376,15 @@ mused3.nim(13, 8) Warning: imported and not used: 'mused3b' [UnusedImport] mused3.nim(75, 10) Hint: duplicate import of 'mused3a'; previous import here: mused3.nim(74, 10) [DuplicateModuleImport] """ + block: # FieldDefect + proc fn(opt: string, expected: string) = + let output = runNimCmdChk("misc/mfield_defect.nim", fmt"-r --warning:all:off --declaredlocs {opt}", status = 1) + doAssert expected in output, opt & "\noutput:\n" & output & "expected:\n" & expected + fn("-d:case1"): """mfield_defect.nim(25, 15) Error: field 'f2' is not accessible for type 'Foo' [discriminant declared in mfield_defect.nim(14, 8)] using 'kind = k3'""" + fn("-d:case2 --gc:refc"): """mfield_defect.nim(25, 15) field 'f2' is not accessible for type 'Foo' [discriminant declared in mfield_defect.nim(14, 8)] using 'kind = k3'""" + fn("-d:case1 -b:js"): """mfield_defect.nim(25, 15) Error: field 'f2' is not accessible for type 'Foo' [discriminant declared in mfield_defect.nim(14, 8)] using 'kind = k3'""" + fn("-d:case2 -b:js"): """field 'f2' is not accessible for type 'Foo' [discriminant declared in mfield_defect.nim(14, 8)] using 'kind = k3'""" + # 3 instead of k3, because of lack of RTTI + fn("-d:case2 --gc:arc"): """mfield_defect.nim(25, 15) field 'f2' is not accessible for type 'Foo' [discriminant declared in mfield_defect.nim(14, 8)] using 'kind = 3'""" else: discard # only during debugging, tests added here will run with `-d:nimTestsTrunnerDebugging` enabled From 4463a3c3d75daabfef52a139221f729e8af9e81e Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 13 Aug 2021 12:34:28 +0200 Subject: [PATCH 2440/3768] manual: minor improvements (#18687) --- doc/manual.rst | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/doc/manual.rst b/doc/manual.rst index 41ed49715..9c7c219e9 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -31,7 +31,7 @@ the `destructors `_ document. This document describes the lexis, the syntax, and the semantics of the Nim language. To learn how to compile Nim programs and generate documentation see -`Compiler User Guide `_ and `DocGen Tools Guide `_. +the `Compiler User Guide `_ and the `DocGen Tools Guide `_. The language constructs are explained using an extended BNF, in which `(a)*` means 0 or more `a`'s, `a+` means 1 or more `a`'s, and `(a)?` means an @@ -583,12 +583,11 @@ In the following examples, `-1` is parsed as two separate tokens "abc"-1 -There exists a literal for each numerical type that is -defined. The suffix starting with an apostrophe ('\'') is called a +The suffix starting with an apostrophe ('\'') is called a `type suffix`:idx:. Literals without a type suffix are of an integer type unless the literal contains a dot or `E|e` in which case it is of type `float`. This integer type is `int` if the literal is in the range -`low(i32)..high(i32)`, otherwise it is `int64`. +`low(int32)..high(int32)`, otherwise it is `int64`. For notational convenience, the apostrophe of a type suffix is optional if it is not ambiguous (only hexadecimal floating-point literals with a type suffix can be ambiguous). @@ -619,10 +618,9 @@ notation: `0B0_10001110100_0000101001000111101011101111111011000101001101001001'f64` is approximately 1.72826e35 according to the IEEE floating-point standard. -Literals are bounds checked so that they fit the datatype. Non-base-10 -literals are used mainly for flags and bit pattern representations, therefore -bounds checking is done on bit width, not value range. If the literal fits in -the bit width of the datatype, it is accepted. +Literals must match the datatype, for example, `333'i8` is an invalid literal. +Non-base-10 literals are used mainly for flags and bit pattern representations, +therefore the checking is done on bit width and not on value range. Hence: 0b10000000'u8 == 0x80'u8 == 128, but, 0b10000000'i8 == 0x80'i8 == -1 instead of causing an overflow error. @@ -1980,8 +1978,7 @@ details like this when mixing garbage-collected data with unmanaged memory. Procedural type --------------- A procedural type is internally a pointer to a procedure. `nil` is -an allowed value for variables of a procedural type. Nim uses procedural -types to achieve `functional`:idx: programming techniques. +an allowed value for a variable of a procedural type. Examples: @@ -2494,8 +2491,8 @@ An expression `b` can be assigned to an expression `a` iff `a` is an `l-value` and `isImplicitlyConvertible(b.typ, a.typ)` holds. -Overloading resolution -====================== +Overload resolution +=================== In a call `p(args)` the routine `p` that matches best is selected. If multiple routines match equally well, the ambiguity is reported during From 52cc0a82cad856fa58ebbb16a9e7ad8c7012e8cf Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 14 Aug 2021 02:13:30 -0700 Subject: [PATCH 2441/3768] fix #18692 AsyncHttpServer was hanging because client.close was not called (#18693) --- lib/pure/asynchttpserver.nim | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index fd0cad5ca..f23c3d3f9 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -362,7 +362,9 @@ proc processClient(server: AsyncHttpServer, client: AsyncSocket, address: string let retry = await processRequest( server, request, client, address, lineFut, callback ) - if not retry: break + if not retry: + client.close() + break const nimMaxDescriptorsFallback* {.intdefine.} = 16_000 ## fallback value for \ From b3e077863aa6985eebab0c37785ba127b3192c80 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Sat, 14 Aug 2021 21:51:41 -0300 Subject: [PATCH 2442/3768] Documentation only, Terminal (#18696) Add simple progress bar example to terminal module --- lib/pure/terminal.nim | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index ce5660557..25b1a4cdd 100644 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -15,6 +15,21 @@ ## code `exitprocs.addExitProc(resetAttributes)` to restore the defaults. ## Similarly, if you hide the cursor, make sure to unhide it with ## `showCursor` before quitting. +## +## Progress bar +## ============ +## +## Basic progress bar example: +runnableExamples("-r:off"): + import std/[os, strutils] + + for i in 0..100: + stdout.styledWriteLine(fgRed, "0% ", fgWhite, '#'.repeat i, if i > 50: fgGreen else: fgYellow, "\t", $i , "%") + sleep 42 + cursorUp 1 + eraseLine() + + stdout.resetAttributes() ##[ ## Playing with colorful and styled text @@ -43,7 +58,6 @@ runnableExamples("-r:off"): stdout.styledWriteLine(fgRed, "red text ", styleBright, "bold red", fgDefault, " bold text") - import macros import strformat from strutils import toLowerAscii, `%` From 1acba63cb725a0eee8f8b02f585057e030ce6295 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Mon, 16 Aug 2021 00:32:12 -0700 Subject: [PATCH 2443/3768] cross compilation targetting windows now supports `nim r`: `nim r -d:mingw main` (#18682) * cross compilation targetting windows now supports `nim r`: `nim r -d:mingw main` * quoteShell * address comment: remove `conf.getConfigVar("nimrun.exe")` --- changelog.md | 3 +++ compiler/nim.nim | 20 +++++++++++++++----- doc/nimc.rst | 2 ++ 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/changelog.md b/changelog.md index 0e3c54d77..40c6c9ace 100644 --- a/changelog.md +++ b/changelog.md @@ -482,6 +482,9 @@ nim r main # uses cached binary nim r main arg1 arg2 # ditto (runtime arguments are irrelevant) +- `nim r` now supports cross compilation from unix to windows when specifying `-d:mingw` by using wine, + e.g.: `nim r --eval:'import os; echo "a" / "b"'` prints `a\b` + - The style checking of the compiler now supports a `--styleCheck:usages` switch. This switch enforces that every symbol is written as it was declared, not enforcing the official Nim style guide. To be enabled, this has to be combined either diff --git a/compiler/nim.nim b/compiler/nim.nim index 91b1bc2db..b8256d576 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -70,6 +70,13 @@ proc processCmdLine(pass: TCmdLinePass, cmd: string; config: ConfigRef) = config.arguments.len > 0 and config.cmd notin {cmdTcc, cmdNimscript, cmdCrun}: rawMessage(config, errGenerated, errArgsNeedRunOption) +proc getNimRunExe(conf: ConfigRef): string = + # xxx consider defining `conf.getConfigVar("nimrun.exe")` to allow users to + # customize the binary to run the command with, e.g. for custom `nodejs` or `wine`. + if conf.isDefined("mingw"): + if conf.isDefined("i386"): result = "wine" + elif conf.isDefined("amd64"): result = "wine64" + proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = let self = NimProg( supportsStdinFile: true, @@ -95,18 +102,21 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = let output = conf.absOutFile case conf.cmd of cmdBackends, cmdTcc: - var cmdPrefix = "" + let nimRunExe = getNimRunExe(conf) + var cmdPrefix: string + if nimRunExe.len > 0: cmdPrefix.add nimRunExe.quoteShell case conf.backend of backendC, backendCpp, backendObjc: discard of backendJs: # D20210217T215950:here this flag is needed for node < v15.0.0, otherwise # tasyncjs_fail` would fail, refs https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode - cmdPrefix = findNodeJs() & " --unhandled-rejections=strict " + if cmdPrefix.len == 0: cmdPrefix = findNodeJs().quoteShell + cmdPrefix.add " --unhandled-rejections=strict" else: doAssert false, $conf.backend - # No space before command otherwise on windows you'd get a cryptic: - # `The parameter is incorrect` + if cmdPrefix.len > 0: cmdPrefix.add " " + # without the `cmdPrefix.len > 0` check, on windows you'd get a cryptic: + # `The parameter is incorrect` execExternalProgram(conf, cmdPrefix & output.quoteShell & ' ' & conf.arguments) - # execExternalProgram(conf, cmdPrefix & ' ' & output.quoteShell & ' ' & conf.arguments) of cmdDocLike, cmdRst2html, cmdRst2tex: # bugfix(cmdRst2tex was missing) if conf.arguments.len > 0: # reserved for future use diff --git a/doc/nimc.rst b/doc/nimc.rst index ad5ecaa76..98b754729 100644 --- a/doc/nimc.rst +++ b/doc/nimc.rst @@ -317,6 +317,8 @@ To cross-compile for Windows from Linux or macOS using the MinGW-w64 toolchain: .. code:: cmd nim c -d:mingw myproject.nim + # `nim r` also works, running the binary via `wine` or `wine64`: + nim r -d:mingw --eval:'import os; echo "a" / "b"' Use `--cpu:i386`:option: or `--cpu:amd64`:option: to switch the CPU architecture. From 901c5ded527bb06c52bfc1fd38c9a5fadee0f49a Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 16 Aug 2021 21:14:35 +0800 Subject: [PATCH 2444/3768] fix #18702(fix `parseutils.parseFloat`) (#18703) [backport:1.0] * fix #18702 * Apply suggestions from code review --- lib/system/strmantle.nim | 9 +++++---- tests/stdlib/tparseutils.nim | 13 +++++++++++-- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/lib/system/strmantle.nim b/lib/system/strmantle.nim index 81ac5c751..f55168c01 100644 --- a/lib/system/strmantle.nim +++ b/lib/system/strmantle.nim @@ -211,15 +211,16 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat, var ti = 0 let maxlen = t.high - "e+000".len # reserve enough space for exponent - result = i - start + let endPos = i + result = endPos - start i = start # re-parse without error checking, any error should be handled by the code above. - if i < s.len and s[i] == '.': i.inc - while i < s.len and s[i] in {'0'..'9','+','-'}: + if i < endPos and s[i] == '.': i.inc + while i < endPos and s[i] in {'0'..'9','+','-'}: if ti < maxlen: t[ti] = s[i]; inc(ti) inc(i) - while i < s.len and s[i] in {'.', '_'}: # skip underscore and decimal point + while i < endPos and s[i] in {'.', '_'}: # skip underscore and decimal point inc(i) # insert exponent diff --git a/tests/stdlib/tparseutils.nim b/tests/stdlib/tparseutils.nim index 3bc54dff1..db7a0ac8d 100644 --- a/tests/stdlib/tparseutils.nim +++ b/tests/stdlib/tparseutils.nim @@ -1,5 +1,5 @@ -import parseutils -import sequtils +import std/[parseutils, sequtils, sugar] + let input = "$test{} $this is ${an{ example}} " let expected = @[(ikVar, "test"), (ikStr, "{} "), (ikVar, "this"), @@ -41,3 +41,12 @@ doAssert value == 1_000_000 var i64Value: int64 discard parseBiggestInt("9223372036854775807", i64Value) doAssert i64Value == 9223372036854775807 + +block: + var f: float + let res = collect: + for x in ["9.123456789012345+","11.123456789012345+","9.123456789012345-","8.123456789012345+","9.12345678901234-","9.123456789012345"]: + (parseFloat(x, f, 0), $f) + doAssert res == @[(17, "9.123456789012344"), (18, "11.123456789012344"), + (17, "9.123456789012344"), (17, "8.123456789012344"), + (16, "9.12345678901234"), (17, "9.123456789012344")] From 5fd150b7f792754b0431f2cc2e9830fb23ce15f6 Mon Sep 17 00:00:00 2001 From: Emery Hemingway Date: Wed, 18 Aug 2021 14:25:25 +0200 Subject: [PATCH 2445/3768] asynchttpserver: ipv6 support (#18706) Fix #18705 --- lib/pure/asynchttpserver.nim | 13 +++++++------ tests/errmsgs/tgcsafety.nim | 2 +- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index f23c3d3f9..d7daacd03 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -41,7 +41,7 @@ runnableExamples("-r:off"): import asyncnet, asyncdispatch, parseutils, uri, strutils import httpcore -from nativesockets import getLocalAddr, AF_INET +from nativesockets import getLocalAddr, Domain, AF_INET, AF_INET6 import std/private/since export httpcore except parseHeader @@ -82,7 +82,7 @@ proc getPort*(self: AsyncHttpServer): Port {.since: (1, 5, 1).} = server.listen(Port(0)) assert server.getPort.uint16 > 0 server.close() - result = getLocalAddr(self.socket.getFd, AF_INET)[1] + result = getLocalAddr(self.socket)[1] proc newAsyncHttpServer*(reuseAddr = true, reusePort = false, maxBody = 8388608): AsyncHttpServer = @@ -372,13 +372,13 @@ const ## This can be set on the command line during compilation ## via `-d:nimMaxDescriptorsFallback=N` -proc listen*(server: AsyncHttpServer; port: Port; address = "") = +proc listen*(server: AsyncHttpServer; port: Port; address = ""; domain = AF_INET) = ## Listen to the given port and address. when declared(maxDescriptors): server.maxFDs = try: maxDescriptors() except: nimMaxDescriptorsFallback else: server.maxFDs = nimMaxDescriptorsFallback - server.socket = newAsyncSocket() + server.socket = newAsyncSocket(domain) if server.reuseAddr: server.socket.setSockOpt(OptReuseAddr, true) if server.reusePort: @@ -404,7 +404,8 @@ proc acceptRequest*(server: AsyncHttpServer, proc serve*(server: AsyncHttpServer, port: Port, callback: proc (request: Request): Future[void] {.closure, gcsafe.}, address = ""; - assumedDescriptorsPerRequest = -1) {.async.} = + assumedDescriptorsPerRequest = -1; + domain = AF_INET) {.async.} = ## Starts the process of listening for incoming HTTP connections on the ## specified address and port. ## @@ -417,7 +418,7 @@ proc serve*(server: AsyncHttpServer, port: Port, ## ## You should prefer to call `acceptRequest` instead with a custom server ## loop so that you're in control over the error handling and logging. - listen server, port, address + listen server, port, address, domain while true: if shouldAcceptRequest(server, assumedDescriptorsPerRequest): var (address, client) = await server.socket.acceptAddr() diff --git a/tests/errmsgs/tgcsafety.nim b/tests/errmsgs/tgcsafety.nim index 6021d19e1..701adeebf 100644 --- a/tests/errmsgs/tgcsafety.nim +++ b/tests/errmsgs/tgcsafety.nim @@ -6,7 +6,7 @@ tgcsafety.nim(31, 18) Error: type mismatch: got Date: Wed, 18 Aug 2021 21:53:52 +0800 Subject: [PATCH 2446/3768] [wip]better hint message for JS (#18704) * better hint message for JS * both --- compiler/msgs.nim | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 20a55daee..56531eb68 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -669,17 +669,28 @@ proc genSuccessX*(conf: ConfigRef) = else: formatSize(getTotalMem()) & " totmem" let loc = $conf.linesCompiled var build = "" + var flags = "" + const debugModeHints = "none (DEBUG BUILD, `-d:release` generates faster code)" if conf.cmd in cmdBackends: - build.add "gc: $#; " % $conf.selectedGC - if optThreads in conf.globalOptions: build.add "threads: on; " - build.add "opt: " - if optOptimizeSpeed in conf.options: build.add "speed" - elif optOptimizeSize in conf.options: build.add "size" - else: build.add "none (DEBUG BUILD, `-d:release` generates faster code)" - # pending https://github.com/timotheecour/Nim/issues/752, point to optimization.html - var flags = "" - if isDefined(conf, "danger"): flags.add " -d:danger" - elif isDefined(conf, "release"): flags.add " -d:release" + if conf.backend != backendJs: + build.add "gc: $#; " % $conf.selectedGC + if optThreads in conf.globalOptions: build.add "threads: on; " + build.add "opt: " + if optOptimizeSpeed in conf.options: build.add "speed" + elif optOptimizeSize in conf.options: build.add "size" + else: build.add debugModeHints + # pending https://github.com/timotheecour/Nim/issues/752, point to optimization.html + if isDefined(conf, "danger"): flags.add " -d:danger" + elif isDefined(conf, "release"): flags.add " -d:release" + else: + build.add "opt: " + if isDefined(conf, "danger"): + build.add "speed" + flags.add " -d:danger" + elif isDefined(conf, "release"): + build.add "speed" + flags.add " -d:release" + else: build.add debugModeHints if flags.len > 0: build.add "; options:" & flags let sec = formatFloat(epochTime() - conf.lastCmdTime, ffDecimal, 3) let project = if conf.filenameOption == foAbs: $conf.projectFull else: $conf.projectName From 7b58dc2de0f606b757a558dfdda9d930ae63f41a Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 18 Aug 2021 10:33:11 -0700 Subject: [PATCH 2447/3768] allow building 1.4.0 from devel (#18708) * allow building 1.4.0 from devel * changelog --- changelog.md | 3 +++ compiler/ast.nim | 8 ++++++-- compiler/ccgexprs.nim | 21 +++++++++++++++++---- compiler/jsgen.nim | 1 + compiler/semfold.nim | 1 + compiler/vmgen.nim | 2 +- 6 files changed, 29 insertions(+), 7 deletions(-) diff --git a/changelog.md b/changelog.md index 40c6c9ace..77a77f2f5 100644 --- a/changelog.md +++ b/changelog.md @@ -485,6 +485,9 @@ - `nim r` now supports cross compilation from unix to windows when specifying `-d:mingw` by using wine, e.g.: `nim r --eval:'import os; echo "a" / "b"'` prints `a\b` +- `nim` can compile version 1.4.0 as follows: `nim c --lib:lib --stylecheck:off -d:nimVersion140 compiler/nim`. + `-d:nimVersion140` is not needed for bootstrapping, only for building 1.4.0 from devel. + - The style checking of the compiler now supports a `--styleCheck:usages` switch. This switch enforces that every symbol is written as it was declared, not enforcing the official Nim style guide. To be enabled, this has to be combined either diff --git a/compiler/ast.nim b/compiler/ast.nim index 5b54dd8ef..08fbc70e1 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -652,7 +652,9 @@ type mUnaryMinusI, mUnaryMinusI64, mAbsI, mNot, mUnaryPlusI, mBitnotI, mUnaryPlusF64, mUnaryMinusF64, - mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mCStrToStr, + mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, + mFloatToStr, # for -d:nimVersion140 + mCStrToStr, mStrToStr, mEnumToStr, mAnd, mOr, mImplies, mIff, mExists, mForall, mOld, @@ -720,7 +722,9 @@ const mEqRef, mEqProc, mLePtr, mLtPtr, mEqCString, mXor, mUnaryMinusI, mUnaryMinusI64, mAbsI, mNot, mUnaryPlusI, mBitnotI, mUnaryPlusF64, mUnaryMinusF64, - mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mCStrToStr, + mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, + mFloatToStr, + mCStrToStr, mStrToStr, mEnumToStr, mAnd, mOr, mEqStr, mLeStr, mLtStr, diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 7eb4a7a1d..374876367 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -894,15 +894,23 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) = let discIndex = rdSetElemLoc(p.config, v, u.t) if optTinyRtti in p.config.globalOptions: # not sure how to use `genEnumToStr` here - const code = "{ #raiseFieldError2($1, (NI)$3); $2} $n" - linefmt(p, cpsStmts, code, [strLit, raiseInstr(p), discIndex]) + if p.config.isDefined("nimVersion140"): + const code = "{ #raiseFieldError($1); $2} $n" + linefmt(p, cpsStmts, code, [strLit, raiseInstr(p)]) + else: + const code = "{ #raiseFieldError2($1, (NI)$3); $2} $n" + linefmt(p, cpsStmts, code, [strLit, raiseInstr(p), discIndex]) else: # complication needed for signed types let first = p.config.firstOrd(disc.sym.typ) let firstLit = int64Literal(cast[int](first)) let discName = genTypeInfo(p.config, p.module, disc.sym.typ, e.info) - const code = "{ #raiseFieldError2($1, #reprDiscriminant(((NI)$3) + (NI)$4, $5)); $2} $n" - linefmt(p, cpsStmts, code, [strLit, raiseInstr(p), discIndex, firstLit, discName]) + if p.config.isDefined("nimVersion140"): + const code = "{ #raiseFieldError($1); $2} $n" + linefmt(p, cpsStmts, code, [strLit, raiseInstr(p)]) + else: + const code = "{ #raiseFieldError2($1, #reprDiscriminant(((NI)$3) + (NI)$4, $5)); $2} $n" + linefmt(p, cpsStmts, code, [strLit, raiseInstr(p), discIndex, firstLit, discName]) proc genCheckedRecordField(p: BProc, e: PNode, d: var TLoc) = assert e[0].kind == nkDotExpr @@ -2320,6 +2328,11 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mInt64ToStr: genDollar(p, e, d, "#nimInt64ToStr($1)") of mBoolToStr: genDollar(p, e, d, "#nimBoolToStr($1)") of mCharToStr: genDollar(p, e, d, "#nimCharToStr($1)") + of mFloatToStr: + if e[1].typ.skipTypes(abstractInst).kind == tyFloat32: + genDollar(p, e, d, "#nimFloat32ToStr($1)") + else: + genDollar(p, e, d, "#nimFloatToStr($1)") of mCStrToStr: genDollar(p, e, d, "#cstrToNimstr($1)") of mStrToStr, mUnown: expr(p, e[1], d) of mIsolate: genCall(p, e, d) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 3aa5c474e..931e56475 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -434,6 +434,7 @@ const # magic checked op; magic unchecked op; mBoolToStr: ["nimBoolToStr", "nimBoolToStr"], mIntToStr: ["cstrToNimstr", "cstrToNimstr"], mInt64ToStr: ["cstrToNimstr", "cstrToNimstr"], + mFloatToStr: ["cstrToNimstr", "cstrToNimstr"], mCStrToStr: ["cstrToNimstr", "cstrToNimstr"], mStrToStr: ["", ""]] diff --git a/compiler/semfold.nim b/compiler/semfold.nim index e5f0643bc..04ed73209 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -290,6 +290,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; idgen: IdGenerator; g: ModuleGraph): P of mBoolToStr: if getOrdValue(a) == 0: result = newStrNodeT("false", n, g) else: result = newStrNodeT("true", n, g) + of mFloatToStr: result = newStrNodeT($getFloat(a), n, g) of mCStrToStr, mCharToStr: if a.kind == nkBracket: var s = "" diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 725afdd3f..f2758ff37 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1127,7 +1127,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = let t = skipTypes(n.typ, abstractVar-{tyTypeDesc}) if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and t.size < 8): c.gABC(n, opcNarrowU, dest, TRegister(t.size*8)) - of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mCStrToStr, mStrToStr, mEnumToStr: + of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr: genConv(c, n, n[1], dest) of mEqStr, mEqCString: genBinaryABC(c, n, dest, opcEqStr) of mLeStr: genBinaryABC(c, n, dest, opcLeStr) From 394f4ac7bb92fe5aaf902495c6b43b3451667602 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 19 Aug 2021 02:33:52 -0700 Subject: [PATCH 2448/3768] improvements to `addInt` and `$` for integer types (#18592) * improvements to $(SomeInteger) and addInt * remove mIntToStr, mInt64ToStr * improvements * fix tests/pragmas/tinjectstmt.nim; the diff is harmless, cgen code is identical with -d:danger or debug mode * rm tests/system/tstrmantle.nim * revert compiler/jsgen.nim for -d:nimVersion140 --- compiler/ast.nim | 8 +-- lib/std/private/digitsutils.nim | 72 +++++++++++++++----- lib/std/private/miscdollars.nim | 9 ++- lib/system.nim | 9 ++- lib/system/assertions.nim | 5 +- lib/system/dollars.nim | 56 ++++++---------- lib/system/repr_v2.nim | 17 +++-- lib/system/strmantle.nim | 30 --------- nimsuggest/tests/tqualified_highlight.nim | 2 +- tests/pragmas/tinjectstmt.nim | 13 ++-- tests/system/tdollars.nim | 81 +++++++++++++++-------- tests/system/tstrmantle.nim | 46 ------------- 12 files changed, 162 insertions(+), 186 deletions(-) delete mode 100644 tests/system/tstrmantle.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index 08fbc70e1..0bdcc0e97 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -652,8 +652,8 @@ type mUnaryMinusI, mUnaryMinusI64, mAbsI, mNot, mUnaryPlusI, mBitnotI, mUnaryPlusF64, mUnaryMinusF64, - mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, - mFloatToStr, # for -d:nimVersion140 + mCharToStr, mBoolToStr, + mIntToStr, mInt64ToStr, mFloatToStr, # for -d:nimVersion140 mCStrToStr, mStrToStr, mEnumToStr, mAnd, mOr, @@ -722,8 +722,8 @@ const mEqRef, mEqProc, mLePtr, mLtPtr, mEqCString, mXor, mUnaryMinusI, mUnaryMinusI64, mAbsI, mNot, mUnaryPlusI, mBitnotI, mUnaryPlusF64, mUnaryMinusF64, - mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, - mFloatToStr, + mCharToStr, mBoolToStr, + mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr, mAnd, mOr, diff --git a/lib/std/private/digitsutils.nim b/lib/std/private/digitsutils.nim index 7aefc36bc..268a3ba7d 100644 --- a/lib/std/private/digitsutils.nim +++ b/lib/std/private/digitsutils.nim @@ -29,18 +29,34 @@ const # doAssert res == digits100 proc utoa2Digits*(buf: var openArray[char]; pos: int; digits: uint32) {.inline.} = - assert(digits <= 99) buf[pos] = digits100[2 * digits] buf[pos+1] = digits100[2 * digits + 1] #copyMem(buf, unsafeAddr(digits100[2 * digits]), 2 * sizeof((char))) proc trailingZeros2Digits*(digits: uint32): int32 {.inline.} = - assert(digits <= 99) return trailingZeros100[digits] -func addIntImpl*(result: var string, origin: uint64) = +when defined(js): + proc numToString(a: SomeInteger): cstring {.importjs: "((#) + \"\")".} + +func addChars[T](result: var string, x: T, start: int, n: int) {.inline.} = + let old = result.len + result.setLen old + n + template impl = + for i in 0.. 0: result.add "(" - # simplify this after moving moving `include strmantle` above import assertions` - when declared(addInt): result.addInt line - else: result.add $line + addInt(result, line) if col > 0: result.add ", " - when declared(addInt): result.addInt col - else: result.add $col + addInt(result, col) result.add ")" diff --git a/lib/system.nim b/lib/system.nim index e6a2439df..740c4419f 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2463,14 +2463,13 @@ when notJSnotNims: else: {.error: "Only closure iterator is allowed!".} +from std/private/digitsutils import addInt +export addInt + when defined(js): include "system/jssys" include "system/reprjs" -when defined(js) or defined(nimscript): - proc addInt*(result: var string; x: int64) = - result.add $x - proc quit*(errormsg: string, errorcode = QuitFailure) {.noreturn.} = ## A shorthand for `echo(errormsg); quit(errorcode)`. when defined(nimscript) or defined(js) or (hostOS == "standalone"): @@ -2918,7 +2917,7 @@ proc addQuoted*[T](s: var string, x: T) = s.addEscapedChar(x) s.add("'") # prevent temporary string allocation - elif T is SomeSignedInt: + elif T is SomeInteger: s.addInt(x) elif T is SomeFloat: s.addFloat(x) diff --git a/lib/system/assertions.nim b/lib/system/assertions.nim index cd789845b..6f64a55b7 100644 --- a/lib/system/assertions.nim +++ b/lib/system/assertions.nim @@ -12,7 +12,6 @@ import std/private/miscdollars type InstantiationInfo = tuple[filename: string, line: int, column: int] -proc `$`(x: int): string {.magic: "IntToStr", noSideEffect.} proc `$`(info: InstantiationInfo): string = # The +1 is needed here # instead of overriding `$` (and changing its meaning), consider explicit name. @@ -108,7 +107,9 @@ template doAssertRaises*(exception: typedesc, code: untyped) = wrong = true except exception: discard - except Exception as e: raiseAssert(begin & " raised '" & $e.name & "'" & msgEnd) + except Exception as e: + mixin `$` # alternatively, we could define $cstring in this module + raiseAssert(begin & " raised '" & $e.name & "'" & msgEnd) except: raisedForeign() if wrong: raiseAssert(begin & " nothing was raised" & msgEnd) diff --git a/lib/system/dollars.nim b/lib/system/dollars.nim index 8634db382..46085d2aa 100644 --- a/lib/system/dollars.nim +++ b/lib/system/dollars.nim @@ -1,45 +1,31 @@ +## `$` is Nim's general way of spelling `toString`:idx:. +runnableExamples: + assert $0.1 == "0.1" + assert $(-2*3) == "-6" + import std/private/digitsutils import system/formatfloat export addFloat -proc `$`*(x: int): string {.magic: "IntToStr", noSideEffect.} - ## The stringify operator for an integer argument. Returns `x` - ## converted to a decimal string. `$` is Nim's general way of - ## spelling `toString`:idx:. +proc `$`*(x: int): string {.raises: [].} = + ## Outplace version of `addInt`. + result.addInt(x) -template dollarImpl(x: uint | uint64, result: var string) = - addIntImpl(result, x) +proc `$`*(x: int64): string {.raises: [].} = + ## Outplace version of `addInt`. + result.addInt(x) -when defined(js): - import std/private/since - since (1, 3): - proc `$`*(x: uint): string = - ## Caveat: currently implemented as $(cast[int](x)), tied to current - ## semantics of js' Number type. - # for c, see strmantle.`$` - when nimvm: - dollarImpl(x, result) - else: - result = $(int(x)) +proc `$`*(x: uint64): string {.raises: [].} = + ## Outplace version of `addInt`. + addInt(result, x) - proc `$`*(x: uint64): string = - ## Compatibility note: - ## the results may change in future releases if/when js target implements - ## 64bit ints. - # pending https://github.com/nim-lang/RFCs/issues/187 - when nimvm: - dollarImpl(x, result) - else: - result = $(cast[int](x)) -else: - proc `$`*(x: uint64): string {.noSideEffect, raises: [].} = - ## The stringify operator for an unsigned integer argument. Returns `x` - ## converted to a decimal string. - dollarImpl(x, result) - -proc `$`*(x: int64): string {.magic: "Int64ToStr", noSideEffect.} - ## The stringify operator for an integer argument. Returns `x` - ## converted to a decimal string. +# same as old `ctfeWhitelist` behavior, whether or not this is a good idea. +template gen(T) = + # xxx simplify this by supporting this in compiler: int{lit} | uint64{lit} | int64{lit} + func `$`*(x: T{lit}): string {.compileTime.} = result.addInt(x) +gen(int) +gen(uint64) +gen(int64) func `$`*(x: float | float32): string = ## Outplace version of `addFloat`. diff --git a/lib/system/repr_v2.nim b/lib/system/repr_v2.nim index ba94b881d..6ab5f3c3f 100644 --- a/lib/system/repr_v2.nim +++ b/lib/system/repr_v2.nim @@ -8,18 +8,17 @@ proc distinctBase(T: typedesc, recursive: static bool = true): typedesc {.magic: proc repr*(x: NimNode): string {.magic: "Repr", noSideEffect.} -proc repr*(x: int): string {.magic: "IntToStr", noSideEffect.} - ## repr for an integer argument. Returns `x` - ## converted to a decimal string. +proc repr*(x: int): string = + ## Same as $x + $x -proc repr*(x: int64): string {.magic: "Int64ToStr", noSideEffect.} - ## repr for an integer argument. Returns `x` - ## converted to a decimal string. +proc repr*(x: int64): string = + ## Same as $x + $x proc repr*(x: uint64): string {.noSideEffect.} = - ## repr for an unsigned integer argument. Returns `x` - ## converted to a decimal string. - $x #Calls `$` from system/strmantle.nim + ## Same as $x + $x proc repr*(x: float): string = ## Same as $x diff --git a/lib/system/strmantle.nim b/lib/system/strmantle.nim index f55168c01..9cf4f9e55 100644 --- a/lib/system/strmantle.nim +++ b/lib/system/strmantle.nim @@ -43,32 +43,6 @@ proc hashString(s: string): int {.compilerproc.} = h = h + h shl 15 result = cast[int](h) -proc addInt*(result: var string; x: int64) = - ## Converts integer to its string representation and appends it to `result`. - ## - ## .. code-block:: Nim - ## var - ## a = "123" - ## b = 45 - ## a.addInt(b) # a <- "12345" - var num: uint64 - - if x < 0: - if x == low(int64): - num = uint64(x) - else: - num = uint64(-x) - let base = result.len - setLen(result, base + 1) - result[base] = '-' - else: - num = uint64(x) - addIntImpl(result, num) - -proc nimIntToStr(x: int): string {.compilerRtl.} = - result = newStringOfCap(sizeof(x)*4) - result.addInt x - proc c_strtod(buf: cstring, endptr: ptr cstring): float64 {. importc: "strtod", header: "", noSideEffect.} @@ -240,10 +214,6 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat, when defined(nimHasInvariant): {.pop.} # staticBoundChecks -proc nimInt64ToStr(x: int64): string {.compilerRtl.} = - result = newStringOfCap(sizeof(x)*4) - result.addInt x - proc nimBoolToStr(x: bool): string {.compilerRtl.} = return if x: "true" else: "false" diff --git a/nimsuggest/tests/tqualified_highlight.nim b/nimsuggest/tests/tqualified_highlight.nim index 3b521ecc1..b83669e72 100644 --- a/nimsuggest/tests/tqualified_highlight.nim +++ b/nimsuggest/tests/tqualified_highlight.nim @@ -10,5 +10,5 @@ highlight;;skProc;;1;;7;;4 highlight;;skTemplate;;2;;7;;4 highlight;;skTemplate;;2;;7;;4 highlight;;skTemplate;;2;;7;;4 -highlight;;skProc;;3;;8;;1 +highlight;;skFunc;;3;;8;;1 """ diff --git a/tests/pragmas/tinjectstmt.nim b/tests/pragmas/tinjectstmt.nim index bca041e46..c6256bda6 100644 --- a/tests/pragmas/tinjectstmt.nim +++ b/tests/pragmas/tinjectstmt.nim @@ -7,15 +7,18 @@ ok0 ok1 onInject: 3 onInject: 4 -0 onInject: 5 +0 onInject: 6 -1 onInject: 7 onInject: 8 +1 +onInject: 9 +onInject: 10 +onInject: 11 2 ok2 -onInject: 9 +onInject: 12 ''' """ @@ -25,7 +28,7 @@ onInject: 9 {.injectStmt.} pragma can be used to inject a statement before every other statement in the current module. It's now undocumented and may be removed in the future and replaced with something more general and without its limitations. -e.g. (e.g. doesn't work in VM or js backends). +(e.g. doesn't work in VM or js backends). ]# from system/ansi_c import c_printf @@ -44,5 +47,5 @@ proc main()= echo a echo "ok2" -static: main() # xxx injectStmt not honred in VM +static: main() # xxx injectStmt not honored in VM main() diff --git a/tests/system/tdollars.nim b/tests/system/tdollars.nim index 34801d9ee..17d195e76 100644 --- a/tests/system/tdollars.nim +++ b/tests/system/tdollars.nim @@ -102,40 +102,41 @@ block: # #14350, #16674, #16686 for JS doAssert nil2 == cstring("") block: - block: - let x = -1'i8 - let y = uint32(x) - - doAssert $y == "4294967295" - - block: - let x = -1'i16 - let y = uint32(x) - - doAssert $y == "4294967295" - - block: - let x = -1'i32 - let y = uint32(x) - - doAssert $y == "4294967295" + when defined(js): # bug #18591 + let a1 = -1'i8 + let a2 = uint8(a1) + # if `uint8(a1)` changes meaning to `cast[uint8](a1)` in future, update this test; + # until then, this is the correct semantics. + let a3 = $a2 + doAssert a2 < 3 + doAssert a3 == "-1" + proc intToStr(a: uint8): cstring {.importjs: "(# + \"\")".} + doAssert $intToStr(a2) == "-1" + else: + block: + let x = -1'i8 + let y = uint32(x) + doAssert $y == "4294967295" + block: + let x = -1'i16 + let y = uint32(x) + doAssert $y == "4294967295" + block: + let x = -1'i32 + let y = uint32(x) + doAssert $y == "4294967295" + block: + proc foo1(arg: int): string = + let x = uint32(arg) + $x + doAssert $foo1(-1) == "4294967295" block: let x = 4294967295'u32 doAssert $x == "4294967295" - block: doAssert $(4294967295'u32) == "4294967295" - - block: - proc foo1(arg: int): string = - let x = uint32(arg) - $x - - doAssert $foo1(-1) == "4294967295" - - proc main()= block: let a = -0.0 @@ -159,6 +160,32 @@ proc main()= doAssert $uint32.high == "4294967295" + block: # addInt + var res = newStringOfCap(24) + template test2(a, b) = + res.setLen(0) + res.addInt a + doAssert res == b + + for i in 0 .. 9: + res.addInt int64(i) + doAssert res == "0123456789" + + res.setLen(0) + for i in -9 .. 0: + res.addInt int64(i) + doAssert res == "-9-8-7-6-5-4-3-2-10" + + when not defined(js): + test2 high(int64), "9223372036854775807" + test2 low(int64), "-9223372036854775808" + + test2 high(int32), "2147483647" + test2 low(int32), "-2147483648" + test2 high(int16), "32767" + test2 low(int16), "-32768" + test2 high(int8), "127" + test2 low(int8), "-128" static: main() main() diff --git a/tests/system/tstrmantle.nim b/tests/system/tstrmantle.nim deleted file mode 100644 index 1f195adde..000000000 --- a/tests/system/tstrmantle.nim +++ /dev/null @@ -1,46 +0,0 @@ -var res = newStringOfCap(24) - -for i in 0 .. 9: - res.addInt int64(i) - -doAssert res == "0123456789" - -res.setLen(0) - -for i in -9 .. 0: - res.addInt int64(i) - -doAssert res == "-9-8-7-6-5-4-3-2-10" - -res.setLen(0) -res.addInt high(int64) -doAssert res == "9223372036854775807" - -res.setLen(0) -res.addInt low(int64) -doAssert res == "-9223372036854775808" - -res.setLen(0) -res.addInt high(int32) -doAssert res == "2147483647" - -res.setLen(0) -res.addInt low(int32) -doAssert res == "-2147483648" - -res.setLen(0) -res.addInt high(int16) -doAssert res == "32767" - -res.setLen(0) -res.addInt low(int16) -doAssert res == "-32768" - - -res.setLen(0) -res.addInt high(int8) -doAssert res == "127" - -res.setLen(0) -res.addInt low(int8) -doAssert res == "-128" From 373bbd9bb4cbcb06fbfd80ab0e968127dc098873 Mon Sep 17 00:00:00 2001 From: Tomohiro Date: Thu, 19 Aug 2021 18:35:40 +0900 Subject: [PATCH 2449/3768] Add ceilDiv to math (#18596) * Use assert in runnableExamples and improve boundary check * Add more tests for ceilDiv * Fix comment in ceilDiv * Calling ceilDiv with int type T such like sizeof(T) > 8 is error --- changelog.md | 2 ++ lib/pure/math.nim | 52 ++++++++++++++++++++++++++++++++++++++++++ tests/stdlib/tmath.nim | 40 ++++++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+) diff --git a/changelog.md b/changelog.md index 77a77f2f5..447401cd0 100644 --- a/changelog.md +++ b/changelog.md @@ -148,6 +148,8 @@ - Added `clamp` in `math` which allows using a `Slice` to clamp to a value. +- Added `ceilDiv` in `math` for round up integer division. + - The JSON module can now handle integer literals and floating point literals of arbitrary length and precision. Numbers that do not fit the underlying `BiggestInt` or `BiggestFloat` fields are diff --git a/lib/pure/math.nim b/lib/pure/math.nim index 410660d4a..66f1a2e2c 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -941,6 +941,58 @@ func euclMod*[T: SomeNumber](x, y: T): T {.since: (1, 5, 1).} = if result < 0: result += abs(y) +func ceilDiv*[T: SomeInteger](x, y: T): T {.inline, since: (1, 5, 1).} = + ## Ceil division is conceptually defined as `ceil(x / y)`. + ## + ## Assumes `x >= 0` and `y > 0` (and `x + y - 1 <= high(T)` if T is SomeUnsignedInt). + ## + ## This is different from the `system.div `_ + ## operator, which works like `trunc(x / y)`. + ## That is, `div` rounds towards `0` and `ceilDiv` rounds up. + ## + ## This function has the above input limitation, because that allows the + ## compiler to generate faster code and it is rarely used with + ## negative values or unsigned integers close to `high(T)/2`. + ## If you need a `ceilDiv` that works with any input, see: + ## https://github.com/demotomohiro/divmath. + ## + ## **See also:** + ## * `system.div proc `_ for integer division + ## * `floorDiv func <#floorDiv,T,T>`_ for integer division which rounds down. + runnableExamples: + assert ceilDiv(12, 3) == 4 + assert ceilDiv(13, 3) == 5 + + when sizeof(T) == 8: + type UT = uint64 + elif sizeof(T) == 4: + type UT = uint32 + elif sizeof(T) == 2: + type UT = uint16 + elif sizeof(T) == 1: + type UT = uint8 + else: + {.fatal: "Unsupported int type".} + + assert x >= 0 and y > 0 + when T is SomeUnsignedInt: + assert x + y - 1 >= x + + # If the divisor is const, the backend C/C++ compiler generates code without a `div` + # instruction, as it is slow on most CPUs. + # If the divisor is a power of 2 and a const unsigned integer type, the + # compiler generates faster code. + # If the divisor is const and a signed integer, generated code becomes slower + # than the code with unsigned integers, because division with signed integers + # need to works for both positive and negative value without `idiv`/`sdiv`. + # That is why this code convert parameters to unsigned. + # This post contains a comparison of the performance of signed/unsigned integers: + # https://github.com/nim-lang/Nim/pull/18596#issuecomment-894420984. + # If signed integer arguments were not converted to unsigned integers, + # `ceilDiv` wouldn't work for any positive signed integer value, because + # `x + (y - 1)` can overflow. + ((x.UT + (y.UT - 1.UT)) div y.UT).T + func frexp*[T: float32|float64](x: T): tuple[frac: T, exp: int] {.inline.} = ## Splits `x` into a normalized fraction `frac` and an integral power of 2 `exp`, ## such that `abs(frac) in 0.5..<1` and `x == frac * 2 ^ exp`, except for special diff --git a/tests/stdlib/tmath.nim b/tests/stdlib/tmath.nim index 2cf544ddb..9bc5b994f 100644 --- a/tests/stdlib/tmath.nim +++ b/tests/stdlib/tmath.nim @@ -106,6 +106,46 @@ template main() = doAssert euclDiv(-9, -3) == 3 doAssert euclMod(-9, -3) == 0 + block: # ceilDiv + doAssert ceilDiv(8, 3) == 3 + doAssert ceilDiv(8, 4) == 2 + doAssert ceilDiv(8, 5) == 2 + doAssert ceilDiv(11, 3) == 4 + doAssert ceilDiv(12, 3) == 4 + doAssert ceilDiv(13, 3) == 5 + doAssert ceilDiv(41, 7) == 6 + doAssert ceilDiv(0, 1) == 0 + doAssert ceilDiv(1, 1) == 1 + doAssert ceilDiv(1, 2) == 1 + doAssert ceilDiv(2, 1) == 2 + doAssert ceilDiv(2, 2) == 1 + doAssert ceilDiv(0, high(int)) == 0 + doAssert ceilDiv(1, high(int)) == 1 + doAssert ceilDiv(0, high(int) - 1) == 0 + doAssert ceilDiv(1, high(int) - 1) == 1 + doAssert ceilDiv(high(int) div 2, high(int) div 2 + 1) == 1 + doAssert ceilDiv(high(int) div 2, high(int) div 2 + 2) == 1 + doAssert ceilDiv(high(int) div 2 + 1, high(int) div 2) == 2 + doAssert ceilDiv(high(int) div 2 + 2, high(int) div 2) == 2 + doAssert ceilDiv(high(int) div 2 + 1, high(int) div 2 + 1) == 1 + doAssert ceilDiv(high(int), 1) == high(int) + doAssert ceilDiv(high(int) - 1, 1) == high(int) - 1 + doAssert ceilDiv(high(int) - 1, 2) == high(int) div 2 + doAssert ceilDiv(high(int) - 1, high(int)) == 1 + doAssert ceilDiv(high(int) - 1, high(int) - 1) == 1 + doAssert ceilDiv(high(int) - 1, high(int) - 2) == 2 + doAssert ceilDiv(high(int), high(int)) == 1 + doAssert ceilDiv(high(int), high(int) - 1) == 2 + doAssert ceilDiv(255'u8, 1'u8) == 255'u8 + doAssert ceilDiv(254'u8, 2'u8) == 127'u8 + when not defined(danger): + doAssertRaises(AssertionDefect): discard ceilDiv(41, 0) + doAssertRaises(AssertionDefect): discard ceilDiv(41, -1) + doAssertRaises(AssertionDefect): discard ceilDiv(-1, 1) + doAssertRaises(AssertionDefect): discard ceilDiv(-1, -1) + doAssertRaises(AssertionDefect): discard ceilDiv(254'u8, 3'u8) + doAssertRaises(AssertionDefect): discard ceilDiv(255'u8, 2'u8) + block: # splitDecimal() tests doAssert splitDecimal(54.674).intpart == 54.0 doAssert splitDecimal(54.674).floatpart ==~ 0.674 From 8fa0decf6b4917e5559b9fd84a18825849dc11df Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 19 Aug 2021 21:54:45 +0800 Subject: [PATCH 2450/3768] fix a typo (#18715) --- compiler/semcall.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index d8dba3e6c..2478420f7 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -280,7 +280,7 @@ const proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) = # Gives a detailed error message; this is separated from semOverloadedCall, - # as semOverlodedCall is already pretty slow (and we need this information + # as semOverloadedCall is already pretty slow (and we need this information # only in case of an error). if c.config.m.errorOutputs == {}: # fail fast: From 13b97291835046eb1368ff4cd240cdcc7d0a2899 Mon Sep 17 00:00:00 2001 From: flywind Date: Fri, 20 Aug 2021 01:56:52 +0800 Subject: [PATCH 2451/3768] fix #18627(Program segfaults with ARC when using openArray[string]) (#18713) * fix #18627 * add testcase * rename * another * remove tyVarargs --- compiler/ast.nim | 2 +- tests/arc/topenarray.nim | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 tests/arc/topenarray.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index 0bdcc0e97..e7507c9c6 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1595,7 +1595,7 @@ proc propagateToOwner*(owner, elem: PType; propagateHasAsgn = true) = if mask != {} and propagateHasAsgn: let o2 = owner.skipTypes({tyGenericInst, tyAlias, tySink}) if o2.kind in {tyTuple, tyObject, tyArray, - tySequence, tySet, tyDistinct, tyOpenArray, tyVarargs}: + tySequence, tySet, tyDistinct}: o2.flags.incl mask owner.flags.incl mask diff --git a/tests/arc/topenarray.nim b/tests/arc/topenarray.nim new file mode 100644 index 000000000..03ec7adf2 --- /dev/null +++ b/tests/arc/topenarray.nim @@ -0,0 +1,24 @@ +discard """ + input: "hi" + output: ''' +hi +Nim +''' + matrix: "--gc:arc -d:useMalloc; --gc:arc" +""" +block: # bug 18627 + proc setPosition(params: openArray[string]) = + for i in params.toOpenArray(0, params.len - 1): + echo i + + proc uciLoop() = + let params = @[readLine(stdin)] + setPosition(params) + + uciLoop() + + proc uciLoop2() = + let params = @["Nim"] + for i in params.toOpenArray(0, params.len - 1): + echo i + uciLoop2() From f2910077ac41a614154f3851f5bffc02b06124a2 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Fri, 20 Aug 2021 01:13:03 -0700 Subject: [PATCH 2452/3768] sigmatch: support optional params with last block arg(s) (#18631) * sigmatch: support optional params with last block arg * add tests * works with multiple block args * cleanup * address comment --- compiler/sigmatch.nim | 14 +++++++- tests/misc/trfc405.nim | 81 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 tests/misc/trfc405.nim diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 674dd7e2e..465a9a51d 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -2305,6 +2305,17 @@ proc incrIndexType(t: PType) = template isVarargsUntyped(x): untyped = x.kind == tyVarargs and x[0].kind == tyUntyped +proc findFirstArgBlock(m: var TCandidate, n: PNode): int = + # see https://github.com/nim-lang/RFCs/issues/405 + result = int.high + for a2 in countdown(n.len-1, 0): + # checking `nfBlockArg in n[a2].flags` wouldn't work inside templates + if n[a2].kind != nkStmtList: break + let formalLast = m.callee.n[m.callee.n.len - (n.len - a2)] + if formalLast.kind == nkSym and formalLast.sym.ast == nil: + result = a2 + else: break + proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var IntSet) = template noMatch() = @@ -2345,7 +2356,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var Int formalLen = m.callee.n.len formal = if formalLen > 1: m.callee.n[1].sym else: nil # current routine parameter container: PNode = nil # constructed container - + let firstArgBlock = findFirstArgBlock(m, n) while a < n.len: c.openShadowScope @@ -2441,6 +2452,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var Int if m.callee.n[f].kind != nkSym: internalError(c.config, n[a].info, "matches") noMatch() + if a >= firstArgBlock: f = max(f, m.callee.n.len - (n.len - a)) formal = m.callee.n[f].sym m.firstMismatch.kind = kTypeMismatch if containsOrIncl(marker, formal.position) and container.isNil: diff --git a/tests/misc/trfc405.nim b/tests/misc/trfc405.nim new file mode 100644 index 000000000..adc39b1df --- /dev/null +++ b/tests/misc/trfc405.nim @@ -0,0 +1,81 @@ +# https://github.com/nim-lang/RFCs/issues/405 + +template main = + template fn1(a = 1, b = 2, body): auto = (a, b, astToStr(body)) + let a1 = fn1(10, 20): + foo + doAssert a1 == (10, 20, "\nfoo") + + template fn2(a = 1, b = 2, body): auto = (a, b, astToStr(body)) + let a2 = fn2(a = 10): foo + doAssert a2 == (10, 2, "\nfoo") + let a2b = fn2(b = 20): foo + doAssert a2b == (1, 20, "\nfoo") + + template fn3(x: int, a = 1, b = 2, body): auto = (a, b, astToStr(body)) + let a3 = fn3(3, 10, 20): foo + doAssert a3 == (10, 20, "\nfoo") + let a3b = fn3(3, a = 10): foo + doAssert a3b == (10, 2, "\nfoo") + + template fn4(x: int, y: int, body): auto = (x, y, astToStr(body)) + let a4 = fn4(1, 2): foo + doAssert a4 == (1, 2, "\nfoo") + + template fn5(x = 1, y = 2, body: untyped = 3): auto = (x, y, astToStr(body)) + doAssert compiles(fn5(1, 2, foo)) + doAssert not compiles(fn5(1, foo)) + + block: + # with an overload + var witness = 0 + template fn6() = discard + template fn6(procname: string, body: untyped): untyped = witness.inc + fn6("abc"): discard + assert witness == 1 + + block: + # with overloads + var witness = 0 + template fn6() = discard + template fn6(a: int) = discard + template fn6(procname: string, body: untyped): untyped = witness.inc + fn6("abc"): discard + assert witness == 1 + + template fn6(b = 1.5, body: untyped): untyped = witness.inc + fn6(1.3): discard + assert witness == 2 + + block: + var witness = 0 + template fn6(a: int) = discard + template fn6(a: string) = discard + template fn6(ignore: string, b = 1.5, body: untyped): untyped = witness.inc + fn6(""): + foobar1 + foobar2 + doAssert witness == 1 + fn6(""): discard + doAssert witness == 2 + + block: # multi block args + template fn8(a = 1, b = 2, body1: untyped, body2: untyped): auto = (a, b, astToStr(body1), astToStr(body2)) + let a1 = fn8(): + foobar1 + foobar2 + do: + foobar3 + foobar4 + doAssert a1 == (1, 2, "\nfoobar1\nfoobar2", "\nfoobar3\nfoobar4") + + let a2 = fn8(b = 20): + foobar1 + foobar2 + do: + foobar3 + foobar4 + doAssert a2 == (1, 20, "\nfoobar1\nfoobar2", "\nfoobar3\nfoobar4") + +static: main() +main() From e52221e2967d7511f26dc020cae5fe62dcae5696 Mon Sep 17 00:00:00 2001 From: flywind Date: Sat, 21 Aug 2021 02:00:04 +0800 Subject: [PATCH 2453/3768] [docs]fix an item missing (#18717) --- doc/astspec.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/astspec.txt b/doc/astspec.txt index 92212bf8c..6d44f7f7e 100644 --- a/doc/astspec.txt +++ b/doc/astspec.txt @@ -364,6 +364,7 @@ AST: nnkExprColonExpr(nnkIdent("a"), nnkIntLit(1)), nnkExprColonExpr(nnkIdent("b"), nnkIntLit(2)), nnkExprColonExpr(nnkIdent("c"), nnkIntLit(3))) + nnkTupleConstr() Since the one tuple would be syntactically identical to parentheses with an expression in them, the parser expects a trailing comma for From f0c6593412c117c7f8e19421016303416d6b8bd3 Mon Sep 17 00:00:00 2001 From: flywind Date: Sat, 21 Aug 2021 14:21:06 +0800 Subject: [PATCH 2454/3768] add testcase for overloadable_enums (#18722) * add testcase for overloadable_enums * link --- tests/enum/toverloadable_enums.nim | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/tests/enum/toverloadable_enums.nim b/tests/enum/toverloadable_enums.nim index 6f3aac3e2..2152d7fff 100644 --- a/tests/enum/toverloadable_enums.nim +++ b/tests/enum/toverloadable_enums.nim @@ -46,3 +46,32 @@ proc takeCallback(param: proc(p: int)) = discard takeCallback x echo ord v + +block: # https://github.com/nim-lang/RFCs/issues/8 + type + Enum1 = enum + A, B, C + Enum2 = enum + A, Z + + proc f(e: Enum1): int = ord(e) + proc g(e: Enum2): int = ord(e) + + proc h(e: Enum1): int = ord(e) + proc h(e: Enum2): int = ord(e) + + let fA = f(A) # Type of A is well defined + let gA = g(A) # Same as above + + let hA1 = h(Enum1.A) # A requires disambiguation + let hA2 = h(Enum2.A) # Similarly + let hA3 = h(B) + let hA4 = h(B) + let x = ord(Enum1.A) # Also + doAssert fA == 0 + doAssert gA == 0 + doAssert hA1 == 0 + doAssert hA2 == 0 + doAssert x == 0 + doAssert hA3 == 1 + doAssert hA4 == 1 From 5b26f2bd81d6fc7d48befbfb4fa3317f713af787 Mon Sep 17 00:00:00 2001 From: flywind Date: Sat, 21 Aug 2021 14:22:00 +0800 Subject: [PATCH 2455/3768] fix deprecated example (#18721) --- doc/astspec.txt | 16 ++++++++++++---- tests/astspec/tastspec.nim | 22 +++++++++++++++------- 2 files changed, 27 insertions(+), 11 deletions(-) diff --git a/doc/astspec.txt b/doc/astspec.txt index 6d44f7f7e..6d3fa1f8c 100644 --- a/doc/astspec.txt +++ b/doc/astspec.txt @@ -1082,7 +1082,7 @@ its entirety to see some of the complexities. Concrete syntax: .. code-block:: nim - type Obj[T] = object {.inheritable.} + type Obj[T] {.inheritable.} = object name: string case isFat: bool of true: @@ -1094,10 +1094,18 @@ AST: .. code-block:: nim # ... + nnkPragmaExpr( + nnkIdent("Obj"), + nnkPragma(nnkIdent("inheritable")) + ), + nnkGenericParams( + nnkIdentDefs( + nnkIdent("T"), + nnkEmpty(), + nnkEmpty()) + ), nnkObjectTy( - nnkPragma( - nnkIdent("inheritable") - ), + nnkEmpty(), nnkEmpty(), nnkRecList( # list of object parameters nnkIdentDefs( diff --git a/tests/astspec/tastspec.nim b/tests/astspec/tastspec.nim index 33a245b1b..c99d8ec79 100644 --- a/tests/astspec/tastspec.nim +++ b/tests/astspec/tastspec.nim @@ -871,11 +871,20 @@ static: scope: macro testRecCase(ast: untyped): untyped = - ast.peelOff({nnkStmtList, nnkTypeSection})[2].matchAst: - of nnkObjectTy( - nnkPragma( - ident"inheritable" + ast.peelOff({nnkStmtList, nnkTypeSection}).matchAst: + of nnkTypeDef( + nnkPragmaExpr( + ident"Obj", + nnkPragma(ident"inheritable") ), + nnkGenericParams( + nnkIdentDefs( + ident"T", + nnkEmpty(), + nnkEmpty()) + ), + nnkObjectTy( + nnkEmpty(), nnkEmpty(), nnkRecList( # list of object parameters nnkIdentDefs( @@ -914,6 +923,7 @@ static: ident"T" ), nnkEmpty() + ) ) ) ) @@ -922,10 +932,8 @@ static: ): echo "ok" - - testRecCase: - type Obj[T] = object {.inheritable.} + type Obj[T] {.inheritable.} = object name: string case isFat: bool of true: From 061a9183f7b49289b05cf134893d703760f467a7 Mon Sep 17 00:00:00 2001 From: flywind Date: Sun, 22 Aug 2021 12:21:53 +0800 Subject: [PATCH 2456/3768] replace wrt with proper word (#18724) * what does wrt mean? * clarify --- compiler/ast.nim | 2 +- compiler/docgen.nim | 2 +- compiler/sighashes.nim | 2 +- lib/core/macros.nim | 6 ++++-- tests/iter/titer3.nim | 2 +- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index e7507c9c6..446c00f49 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -860,7 +860,7 @@ type case kind*: TSymKind of routineKinds: #procInstCache*: seq[PInstantiation] - gcUnsafetyReason*: PSym # for better error messages wrt gcsafe + gcUnsafetyReason*: PSym # for better error messages regarding gcsafe transformedBody*: PNode # cached body after transf pass of skLet, skVar, skField, skForVar: guard*: PSym diff --git a/compiler/docgen.nim b/compiler/docgen.nim index d39a20aae..706b54bcd 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -172,7 +172,7 @@ proc presentationPath*(conf: ConfigRef, file: AbsoluteFile): RelativeFile = if result.isEmpty: bail() elif conf.docRoot.len > 0: # we're (currently) requiring `isAbsolute` to avoid confusion when passing - # a relative path (would it be relative wrt $PWD or to projectfile) + # a relative path (would it be relative with regard to $PWD or to projectfile) conf.globalAssert conf.docRoot.isAbsolute, arg=conf.docRoot conf.globalAssert conf.docRoot.dirExists, arg=conf.docRoot # needed because `canonicalizePath` called on `file` diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim index 0ff97017f..8534f4c0c 100644 --- a/compiler/sighashes.nim +++ b/compiler/sighashes.nim @@ -390,7 +390,7 @@ proc idOrSig*(s: PSym, currentModule: string, sigCollisions: var CountTable[SigHash]): Rope = if s.kind in routineKinds and s.typ != nil: # signatures for exported routines are reliable enough to - # produce a unique name and this means produced C++ is more stable wrt + # produce a unique name and this means produced C++ is more stable regarding # Nim changes: let sig = hashProc(s) result = rope($sig) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index d3eaf6298..645f5067a 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -442,9 +442,11 @@ proc ident*(name: string): NimNode {.magic: "StrToIdent", noSideEffect.} ## Create a new ident node from a string. type - BindSymRule* = enum ## specifies how `bindSym` behaves + BindSymRule* = enum ## Specifies how `bindSym` behaves. The difference + ## between open and closed symbols can be found in + ## ``_ brClosed, ## only the symbols in current scope are bound - brOpen, ## open wrt overloaded symbols, but may be a single + brOpen, ## open for overloaded symbols, but may be a single ## symbol if not ambiguous (the rules match that of ## binding in generics) brForceOpen ## same as brOpen, but it will always be open even diff --git a/tests/iter/titer3.nim b/tests/iter/titer3.nim index 9dcfd7be5..defd56c98 100644 --- a/tests/iter/titer3.nim +++ b/tests/iter/titer3.nim @@ -26,7 +26,7 @@ var x = [[1, 2, 3], [4, 5, 6]] for y in iter1(x[0]): write(stdout, $y) writeLine(stdout, "") -# ensure closure and inline iterators have the same behaviour wrt +# ensure closure and inline iterators have the same behaviour regarding # parameter passing iterator clo(a: int): int {.closure.} = From cc08d5c2c641e9390e9e05012defc9616f5d6ba8 Mon Sep 17 00:00:00 2001 From: flywind Date: Sun, 22 Aug 2021 13:40:20 +0800 Subject: [PATCH 2457/3768] fix #17898(randomPathName called twice in a row can return the same string on windows) (#18729) * close #17898 * no need to consider js --- lib/std/tempfiles.nim | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/lib/std/tempfiles.nim b/lib/std/tempfiles.nim index c97db67b6..f1cfd5885 100644 --- a/lib/std/tempfiles.nim +++ b/lib/std/tempfiles.nim @@ -17,7 +17,7 @@ See also: * `mkstemp` (posix), refs https://man7.org/linux/man-pages/man3/mkstemp.3.html ]# -import os, random +import os, random, std/monotimes const @@ -96,11 +96,25 @@ proc safeOpen(filename: string): File = discard posix.close(fileHandle) # TODO handles failure when closing file raiseOSError(osLastError(), filename) + +type + NimTempPathState = object + state: Rand + isInit: bool + +var nimTempPathState {.threadvar.}: NimTempPathState + template randomPathName(length: Natural): string = var res = newString(length) - var state = initRand() + if not nimTempPathState.isInit: + var time = getMonoTime().ticks + when compileOption("threads"): + time = time xor int64(getThreadId()) + nimTempPathState.isInit = true + nimTempPathState.state = initRand(time) + for i in 0 ..< length: - res[i] = state.sample(letters) + res[i] = nimTempPathState.state.sample(letters) res proc getTempDirImpl(dir: string): string {.inline.} = From e65dc36ddd6bbb1a87a607ee8c8491732730fa95 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Sun, 22 Aug 2021 16:32:55 -0300 Subject: [PATCH 2458/3768] Implement RFC-391 (#18585) --- lib/std/jsfetch.nim | 177 ++++++++++++++++++++++---------------------- tests/config.nims | 1 - tools/kochdocs.nim | 2 +- 3 files changed, 89 insertions(+), 91 deletions(-) diff --git a/lib/std/jsfetch.nim b/lib/std/jsfetch.nim index 1e7bb4d29..a25fe7ca8 100644 --- a/lib/std/jsfetch.nim +++ b/lib/std/jsfetch.nim @@ -1,121 +1,120 @@ ## - Fetch for the JavaScript target: https://developer.mozilla.org/docs/Web/API/Fetch_API -## .. Note:: jsfetch is Experimental. jsfetch module requires `-d:nimExperimentalJsfetch` +## .. Note:: jsfetch is Experimental. when not defined(js): {.fatal: "Module jsfetch is designed to be used with the JavaScript backend.".} -when defined(nimExperimentalJsfetch): - import std/[asyncjs, jsheaders, jsformdata] - from std/httpcore import HttpMethod - from std/jsffi import JsObject +import std/[asyncjs, jsheaders, jsformdata] +from std/httpcore import HttpMethod +from std/jsffi import JsObject - type - FetchOptions* = ref object of JsRoot ## Options for Fetch API. - keepalive*: bool - metod* {.importjs: "method".}: cstring - body*, integrity*, referrer*, mode*, credentials*, cache*, redirect*, referrerPolicy*: cstring +type + FetchOptions* = ref object of JsRoot ## Options for Fetch API. + keepalive*: bool + metod* {.importjs: "method".}: cstring + body*, integrity*, referrer*, mode*, credentials*, cache*, redirect*, referrerPolicy*: cstring - FetchModes* = enum ## Mode options. - fmCors = "cors" - fmNoCors = "no-cors" - fmSameOrigin = "same-origin" + FetchModes* = enum ## Mode options. + fmCors = "cors" + fmNoCors = "no-cors" + fmSameOrigin = "same-origin" - FetchCredentials* = enum ## Credential options. See https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials - fcInclude = "include" - fcSameOrigin = "same-origin" - fcOmit = "omit" + FetchCredentials* = enum ## Credential options. See https://developer.mozilla.org/en-US/docs/Web/API/Request/credentials + fcInclude = "include" + fcSameOrigin = "same-origin" + fcOmit = "omit" - FetchCaches* = enum ## https://developer.mozilla.org/docs/Web/API/Request/cache - fchDefault = "default" - fchNoStore = "no-store" - fchReload = "reload" - fchNoCache = "no-cache" - fchForceCache = "force-cache" + FetchCaches* = enum ## https://developer.mozilla.org/docs/Web/API/Request/cache + fchDefault = "default" + fchNoStore = "no-store" + fchReload = "reload" + fchNoCache = "no-cache" + fchForceCache = "force-cache" - FetchRedirects* = enum ## Redirects options. - frFollow = "follow" - frError = "error" - frManual = "manual" + FetchRedirects* = enum ## Redirects options. + frFollow = "follow" + frError = "error" + frManual = "manual" - FetchReferrerPolicies* = enum ## Referrer Policy options. - frpNoReferrer = "no-referrer" - frpNoReferrerWhenDowngrade = "no-referrer-when-downgrade" - frpOrigin = "origin" - frpOriginWhenCrossOrigin = "origin-when-cross-origin" - frpUnsafeUrl = "unsafe-url" + FetchReferrerPolicies* = enum ## Referrer Policy options. + frpNoReferrer = "no-referrer" + frpNoReferrerWhenDowngrade = "no-referrer-when-downgrade" + frpOrigin = "origin" + frpOriginWhenCrossOrigin = "origin-when-cross-origin" + frpUnsafeUrl = "unsafe-url" - Body* = ref object of JsRoot ## https://developer.mozilla.org/en-US/docs/Web/API/Body - bodyUsed*: bool + Body* = ref object of JsRoot ## https://developer.mozilla.org/en-US/docs/Web/API/Body + bodyUsed*: bool - Response* = ref object of JsRoot ## https://developer.mozilla.org/en-US/docs/Web/API/Response - bodyUsed*, ok*, redirected*: bool - typ* {.importjs: "type".}: cstring - url*, statusText*: cstring - status*: cint - headers*: Headers - body*: Body + Response* = ref object of JsRoot ## https://developer.mozilla.org/en-US/docs/Web/API/Response + bodyUsed*, ok*, redirected*: bool + typ* {.importjs: "type".}: cstring + url*, statusText*: cstring + status*: cint + headers*: Headers + body*: Body - Request* = ref object of JsRoot ## https://developer.mozilla.org/en-US/docs/Web/API/Request - bodyUsed*, ok*, redirected*: bool - typ* {.importjs: "type".}: cstring - url*, statusText*: cstring - status*: cint - headers*: Headers - body*: Body + Request* = ref object of JsRoot ## https://developer.mozilla.org/en-US/docs/Web/API/Request + bodyUsed*, ok*, redirected*: bool + typ* {.importjs: "type".}: cstring + url*, statusText*: cstring + status*: cint + headers*: Headers + body*: Body - func newResponse*(body: cstring | FormData): Response {.importjs: "(new Response(#))".} - ## Constructor for `Response`. This does *not* call `fetch()`. Same as `new Response()`. +func newResponse*(body: cstring | FormData): Response {.importjs: "(new Response(#))".} + ## Constructor for `Response`. This does *not* call `fetch()`. Same as `new Response()`. - func newRequest*(url: cstring): Request {.importjs: "(new Request(#))".} - ## Constructor for `Request`. This does *not* call `fetch()`. Same as `new Request()`. +func newRequest*(url: cstring): Request {.importjs: "(new Request(#))".} + ## Constructor for `Request`. This does *not* call `fetch()`. Same as `new Request()`. - func clone*(self: Response | Request): Response {.importjs: "#.$1()".} - ## https://developer.mozilla.org/en-US/docs/Web/API/Response/clone +func clone*(self: Response | Request): Response {.importjs: "#.$1()".} + ## https://developer.mozilla.org/en-US/docs/Web/API/Response/clone - proc text*(self: Response): Future[cstring] {.importjs: "#.$1()".} - ## https://developer.mozilla.org/en-US/docs/Web/API/Body/text +proc text*(self: Response): Future[cstring] {.importjs: "#.$1()".} + ## https://developer.mozilla.org/en-US/docs/Web/API/Body/text - proc json*(self: Response): Future[JsObject] {.importjs: "#.$1()".} - ## https://developer.mozilla.org/en-US/docs/Web/API/Body/json +proc json*(self: Response): Future[JsObject] {.importjs: "#.$1()".} + ## https://developer.mozilla.org/en-US/docs/Web/API/Body/json - proc formData*(self: Body): Future[FormData] {.importjs: "#.$1()".} - ## https://developer.mozilla.org/en-US/docs/Web/API/Body/formData +proc formData*(self: Body): Future[FormData] {.importjs: "#.$1()".} + ## https://developer.mozilla.org/en-US/docs/Web/API/Body/formData - proc unsafeNewFetchOptions*(metod, body, mode, credentials, cache, referrerPolicy: cstring; - keepalive: bool; redirect = "follow".cstring; referrer = "client".cstring; integrity = "".cstring): FetchOptions {.importjs: - "{method: #, body: #, mode: #, credentials: #, cache: #, referrerPolicy: #, keepalive: #, redirect: #, referrer: #, integrity: #}".} - ## .. warning:: Unsafe `newfetchOptions`. +proc unsafeNewFetchOptions*(metod, body, mode, credentials, cache, referrerPolicy: cstring; + keepalive: bool; redirect = "follow".cstring; referrer = "client".cstring; integrity = "".cstring): FetchOptions {.importjs: + "{method: #, body: #, mode: #, credentials: #, cache: #, referrerPolicy: #, keepalive: #, redirect: #, referrer: #, integrity: #}".} + ## .. warning:: Unsafe `newfetchOptions`. - func newfetchOptions*(metod: HttpMethod; body: cstring; - mode: FetchModes; credentials: FetchCredentials; cache: FetchCaches; referrerPolicy: FetchReferrerPolicies; - keepalive: bool; redirect = frFollow; referrer = "client".cstring; integrity = "".cstring): FetchOptions = - ## Constructor for `FetchOptions`. - result = FetchOptions( - body: body, mode: $mode, credentials: $credentials, cache: $cache, referrerPolicy: $referrerPolicy, - keepalive: keepalive, redirect: $redirect, referrer: referrer, integrity: integrity, - metod: (case metod - of HttpHead: "HEAD".cstring - of HttpGet: "GET".cstring - of HttpPost: "POST".cstring - of HttpPut: "PUT".cstring - of HttpDelete: "DELETE".cstring - of HttpPatch: "PATCH".cstring - else: "GET".cstring - ) +func newfetchOptions*(metod: HttpMethod; body: cstring; + mode: FetchModes; credentials: FetchCredentials; cache: FetchCaches; referrerPolicy: FetchReferrerPolicies; + keepalive: bool; redirect = frFollow; referrer = "client".cstring; integrity = "".cstring): FetchOptions = + ## Constructor for `FetchOptions`. + result = FetchOptions( + body: body, mode: $mode, credentials: $credentials, cache: $cache, referrerPolicy: $referrerPolicy, + keepalive: keepalive, redirect: $redirect, referrer: referrer, integrity: integrity, + metod: (case metod + of HttpHead: "HEAD".cstring + of HttpGet: "GET".cstring + of HttpPost: "POST".cstring + of HttpPut: "PUT".cstring + of HttpDelete: "DELETE".cstring + of HttpPatch: "PATCH".cstring + else: "GET".cstring ) + ) - proc fetch*(url: cstring | Request): Future[Response] {.importjs: "$1(#)".} - ## `fetch()` API, simple `GET` only, returns a `Future[Response]`. +proc fetch*(url: cstring | Request): Future[Response] {.importjs: "$1(#)".} + ## `fetch()` API, simple `GET` only, returns a `Future[Response]`. - proc fetch*(url: cstring | Request; options: FetchOptions): Future[Response] {.importjs: "$1(#, #)".} - ## `fetch()` API that takes a `FetchOptions`, returns a `Future[Response]`. +proc fetch*(url: cstring | Request; options: FetchOptions): Future[Response] {.importjs: "$1(#, #)".} + ## `fetch()` API that takes a `FetchOptions`, returns a `Future[Response]`. - func toCstring*(self: Request | Response | Body | FetchOptions): cstring {.importjs: "JSON.stringify(#)".} +func toCstring*(self: Request | Response | Body | FetchOptions): cstring {.importjs: "JSON.stringify(#)".} - func `$`*(self: Request | Response | Body | FetchOptions): string = $toCstring(self) +func `$`*(self: Request | Response | Body | FetchOptions): string = $toCstring(self) -runnableExamples("-d:nimExperimentalJsfetch -r:off"): +runnableExamples("-r:off"): import std/[asyncjs, jsconsole, jsheaders, jsformdata] from std/httpcore import HttpMethod from std/jsffi import JsObject diff --git a/tests/config.nims b/tests/config.nims index d7f6ae7f9..0327f0b76 100644 --- a/tests/config.nims +++ b/tests/config.nims @@ -33,7 +33,6 @@ hint("Processing", off) # experimental API's are enabled in testament, refs https://github.com/timotheecour/Nim/issues/575 # sync with `kochdocs.docDefines` or refactor. switch("define", "nimExperimentalAsyncjsThen") -switch("define", "nimExperimentalJsfetch") switch("define", "nimExperimentalLinenoiseExtra") switch("define", "nimPreviewFloatRoundtrip") diff --git a/tools/kochdocs.nim b/tools/kochdocs.nim index 8e8085f73..fe3335c0f 100644 --- a/tools/kochdocs.nim +++ b/tools/kochdocs.nim @@ -8,7 +8,7 @@ const gaCode* = " --doc.googleAnalytics:UA-48159761-1" # errormax: subsequent errors are probably consequences of 1st one; a simple # bug could cause unlimited number of errors otherwise, hard to debug in CI. - docDefines = "-d:nimExperimentalAsyncjsThen -d:nimExperimentalJsfetch -d:nimExperimentalLinenoiseExtra" + docDefines = "-d:nimExperimentalAsyncjsThen -d:nimExperimentalLinenoiseExtra" nimArgs = "--errormax:3 --hint:Conf:off --hint:Path:off --hint:Processing:off --hint:XDeclaredButNotUsed:off --warning:UnusedImport:off -d:boot --putenv:nimversion=$# $#" % [system.NimVersion, docDefines] gitUrl = "https://github.com/nim-lang/Nim" docHtmlOutput = "doc/html" From 2d84f6e7c836bf18c0ecbe07fb413166861196ac Mon Sep 17 00:00:00 2001 From: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Date: Mon, 23 Aug 2021 00:13:39 +0200 Subject: [PATCH 2459/3768] Fix #18718 (#18731) Update tests --- lib/pure/stats.nim | 13 +++--- tests/stdlib/tstats.nim | 93 ++++++++++++++++++++++------------------- 2 files changed, 59 insertions(+), 47 deletions(-) diff --git a/lib/pure/stats.nim b/lib/pure/stats.nim index e46dff212..58014e503 100644 --- a/lib/pure/stats.nim +++ b/lib/pure/stats.nim @@ -42,7 +42,7 @@ runnableExamples: template `~=`(a, b: float): bool = almostEqual(a, b) - var statistics: RunningStat ## Must be var + var statistics: RunningStat # must be var statistics.push(@[1.0, 2.0, 1.0, 4.0, 1.0, 4.0, 1.0, 2.0]) doAssert statistics.n == 8 doAssert statistics.mean() ~= 2.0 @@ -76,7 +76,7 @@ type proc clear*(s: var RunningStat) = ## Resets `s`. s.n = 0 - s.min = toBiggestFloat(int.high) + s.min = 0.0 s.max = 0.0 s.sum = 0.0 s.mom1 = 0.0 @@ -86,11 +86,14 @@ proc clear*(s: var RunningStat) = proc push*(s: var RunningStat, x: float) = ## Pushes a value `x` for processing. - if s.n == 0: s.min = x + if s.n == 0: + s.min = x + s.max = x + else: + if s.min > x: s.min = x + if s.max < x: s.max = x inc(s.n) # See Knuth TAOCP vol 2, 3rd edition, page 232 - if s.min > x: s.min = x - if s.max < x: s.max = x s.sum += x let n = toFloat(s.n) let delta = x - s.mom1 diff --git a/tests/stdlib/tstats.nim b/tests/stdlib/tstats.nim index 37240c884..92a2ed8b8 100644 --- a/tests/stdlib/tstats.nim +++ b/tests/stdlib/tstats.nim @@ -1,46 +1,55 @@ -include stats +import std/stats -proc clean(x: float): float = - result = round(1.0e8*x).float * 1.0e-8 +proc `~=`(x, y: float): bool = + abs(x - y) < 10e-8 -var rs: RunningStat -rs.push(@[1.0, 2.0, 1.0, 4.0, 1.0, 4.0, 1.0, 2.0]) -doAssert(rs.n == 8) -doAssert(clean(rs.mean) == 2.0) -doAssert(clean(rs.variance()) == 1.5) -doAssert(clean(rs.varianceS()) == 1.71428571) -doAssert(clean(rs.skewness()) == 0.81649658) -doAssert(clean(rs.skewnessS()) == 1.01835015) -doAssert(clean(rs.kurtosis()) == -1.0) -doAssert(clean(rs.kurtosisS()) == -0.7000000000000001) +template main() = + var rs: RunningStat + rs.push(@[1.0, 2.0, 1.0, 4.0, 1.0, 4.0, 1.0, 2.0]) + doAssert(rs.n == 8) + doAssert rs.mean ~= 2.0 + doAssert rs.variance() ~= 1.5 + doAssert rs.varianceS() ~= 1.71428571 + doAssert rs.skewness() ~= 0.81649658 + doAssert rs.skewnessS() ~= 1.01835015 + doAssert rs.kurtosis() ~= -1.0 + doAssert rs.kurtosisS() ~= -0.7000000000000001 -var rs1, rs2: RunningStat -rs1.push(@[1.0, 2.0, 1.0, 4.0]) -rs2.push(@[1.0, 4.0, 1.0, 2.0]) -let rs3 = rs1 + rs2 -doAssert(clean(rs3.mom2) == clean(rs.mom2)) -doAssert(clean(rs3.mom3) == clean(rs.mom3)) -doAssert(clean(rs3.mom4) == clean(rs.mom4)) -rs1 += rs2 -doAssert(clean(rs1.mom2) == clean(rs.mom2)) -doAssert(clean(rs1.mom3) == clean(rs.mom3)) -doAssert(clean(rs1.mom4) == clean(rs.mom4)) -rs1.clear() -rs1.push(@[1.0, 2.2, 1.4, 4.9]) -doAssert(rs1.sum == 9.5) -doAssert(rs1.mean() == 2.375) + var rs1, rs2: RunningStat + rs1.push(@[1.0, 2.0, 1.0, 4.0]) + rs2.push(@[1.0, 4.0, 1.0, 2.0]) + let rs3 = rs1 + rs2 + doAssert rs3.variance ~= rs.variance + doAssert rs3.skewness ~= rs.skewness + doAssert rs3.kurtosis ~= rs.kurtosis + rs1 += rs2 + doAssert rs1.variance ~= rs.variance + doAssert rs1.skewness ~= rs.skewness + doAssert rs1.kurtosis ~= rs.kurtosis + rs1.clear() + rs1.push(@[1.0, 2.2, 1.4, 4.9]) + doAssert(rs1.sum == 9.5) + doAssert(rs1.mean() == 2.375) -when not defined(cpu32): - # XXX For some reason on 32bit CPUs these results differ - var rr: RunningRegress - rr.push(@[0.0, 1.0, 2.8, 3.0, 4.0], @[0.0, 1.0, 2.3, 3.0, 4.0]) - doAssert(rr.slope() == 0.9695585996955861) - doAssert(rr.intercept() == -0.03424657534246611) - doAssert(rr.correlation() == 0.9905100362239381) - var rr1, rr2: RunningRegress - rr1.push(@[0.0, 1.0], @[0.0, 1.0]) - rr2.push(@[2.8, 3.0, 4.0], @[2.3, 3.0, 4.0]) - let rr3 = rr1 + rr2 - doAssert(rr3.correlation() == rr.correlation()) - doAssert(clean(rr3.slope()) == clean(rr.slope())) - doAssert(clean(rr3.intercept()) == clean(rr.intercept())) \ No newline at end of file + when not defined(cpu32): + # XXX For some reason on 32bit CPUs these results differ + var rr: RunningRegress + rr.push(@[0.0, 1.0, 2.8, 3.0, 4.0], @[0.0, 1.0, 2.3, 3.0, 4.0]) + doAssert(rr.slope() == 0.9695585996955861) + doAssert(rr.intercept() == -0.03424657534246611) + doAssert(rr.correlation() == 0.9905100362239381) + var rr1, rr2: RunningRegress + rr1.push(@[0.0, 1.0], @[0.0, 1.0]) + rr2.push(@[2.8, 3.0, 4.0], @[2.3, 3.0, 4.0]) + let rr3 = rr1 + rr2 + doAssert(rr3.correlation() == rr.correlation()) + doAssert rr3.slope() ~= rr.slope() + doAssert rr3.intercept() ~= rr.intercept() + + block: # bug #18718 + var rs: RunningStat + rs.push(-1.0) + doAssert rs.max == -1.0 + +static: main() +main() From 8a8267e2bc36ac8070fa2842fecd9cfe53e0a9ef Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 23 Aug 2021 21:05:22 +0800 Subject: [PATCH 2460/3768] fix a typo (#18736) --- compiler/sighashes.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim index 8534f4c0c..01a0fa784 100644 --- a/compiler/sighashes.nim +++ b/compiler/sighashes.nim @@ -166,7 +166,7 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) = if t.n.len > 0: let oldFlags = symWithFlags.flags # Hack to prevent endless recursion - # xxx intead, use a hash table to indicate we've already visited a type, which + # xxx instead, use a hash table to indicate we've already visited a type, which # would also be more efficient. symWithFlags.flags.excl {sfAnon, sfGenSym} hashTree(c, t.n, flags + {CoHashTypeInsideNode}) From 9674ff4361468137afdbedb86196b7082aaa6efd Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 24 Aug 2021 02:13:10 +0800 Subject: [PATCH 2461/3768] make deprecated message better (#18737) --- lib/pure/mersenne.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/mersenne.nim b/lib/pure/mersenne.nim index 6ec0d66b4..37c5085b1 100644 --- a/lib/pure/mersenne.nim +++ b/lib/pure/mersenne.nim @@ -8,7 +8,7 @@ ## random number generator. ## .. note:: The procs in this module work at compile-time. -{.deprecated: "use `std/random` instead.".} +{.deprecated: "use `std/random` instead".} runnableExamples: var rand = newMersenneTwister(uint32.high) ## must be "var" From 24178bf8aa37c179caab9e16682940e8340f2bfc Mon Sep 17 00:00:00 2001 From: Miran Date: Tue, 24 Aug 2021 17:15:26 +0200 Subject: [PATCH 2462/3768] get rid of the warnings during bootstrapping (#18741) * fix bootstrapping hints and warnings * revert removals in ccgtypes --- compiler/nimlexbase.nim | 4 ++-- compiler/semfold.nim | 2 +- compiler/sighashes.nim | 2 +- compiler/suggest.nim | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/nimlexbase.nim b/compiler/nimlexbase.nim index 99d07d093..b8cb9f78a 100644 --- a/compiler/nimlexbase.nim +++ b/compiler/nimlexbase.nim @@ -104,7 +104,7 @@ proc fillBuffer(L: var TBaseLexer) = oldBufLen = L.bufLen L.bufLen = L.bufLen * 2 L.bufStorage.setLen(L.bufLen) - L.buf = L.bufStorage + L.buf = L.bufStorage.cstring assert(L.bufLen - oldBufLen == oldBufLen) charsRead = llStreamRead(L.stream, addr(L.buf[oldBufLen]), oldBufLen) @@ -147,7 +147,7 @@ proc openBaseLexer(L: var TBaseLexer, inputstream: PLLStream, bufLen = 8192) = L.bufpos = 0 L.offsetBase = 0 L.bufStorage = newString(bufLen) - L.buf = L.bufStorage + L.buf = L.bufStorage.cstring L.bufLen = bufLen L.sentinel = bufLen - 1 L.lineStart = 0 diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 04ed73209..1f8efc4de 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -144,7 +144,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; idgen: IdGenerator; g: ModuleGraph): P if a.typ.kind == tyString: result = newIntNodeT(toInt128(a.strVal.len), n, idgen, g) elif a.typ.kind == tyCstring: - result = newIntNodeT(toInt128(nimCStrLen(a.strVal)), n, idgen, g) + result = newIntNodeT(toInt128(nimCStrLen(a.strVal.cstring)), n, idgen, g) else: result = newIntNodeT(toInt128(a.len), n, idgen, g) of mUnaryPlusI, mUnaryPlusF64: result = a # throw `+` away diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim index 01a0fa784..d02728627 100644 --- a/compiler/sighashes.nim +++ b/compiler/sighashes.nim @@ -18,7 +18,7 @@ proc `&=`(c: var MD5Context, ch: char) = # XXX suspicious code here; relies on ch being zero terminated? md5Update(c, unsafeAddr ch, 1) proc `&=`(c: var MD5Context, r: Rope) = - for l in leaves(r): md5Update(c, l, l.len) + for l in leaves(r): md5Update(c, l.cstring, l.len) proc `&=`(c: var MD5Context, i: BiggestInt) = md5Update(c, cast[cstring](unsafeAddr i), sizeof(i)) proc `&=`(c: var MD5Context, f: BiggestFloat) = diff --git a/compiler/suggest.nim b/compiler/suggest.nim index eaa30040a..84c94d793 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -32,7 +32,7 @@ # included from sigmatch.nim -import algorithm, sets, prefixmatches, lineinfos, parseutils, linter, tables +import algorithm, sets, prefixmatches, parseutils, tables from wordrecg import wDeprecated, wError, wAddr, wYield when defined(nimsuggest): From 3d1bba04ab1092630983695734d6984ddff4688c Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 24 Aug 2021 23:16:12 +0800 Subject: [PATCH 2463/3768] [minor]use `addInt` and `addFloat` (#18733) * unify int and uint * back * minor --- compiler/ic/ic.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/ic/ic.nim b/compiler/ic/ic.nim index bd3be27f0..7387d165b 100644 --- a/compiler/ic/ic.nim +++ b/compiler/ic/ic.nim @@ -94,10 +94,10 @@ proc toString*(tree: PackedTree; n: NodePos; m: PackedModule; nesting: int; result.addInt m.numbers[LitId tree.nodes[pos].operand] of externUIntLit: result.add " " - result.add $cast[uint64](m.numbers[LitId tree.nodes[pos].operand]) + result.addInt cast[uint64](m.numbers[LitId tree.nodes[pos].operand]) of nkFloatLit..nkFloat128Lit: result.add " " - result.add $cast[BiggestFloat](m.numbers[LitId tree.nodes[pos].operand]) + result.addFloat cast[BiggestFloat](m.numbers[LitId tree.nodes[pos].operand]) else: result.add "(\n" for i in 1..(nesting+1)*2: result.add ' ' From 3aa16c1de00c723d48e48fe3fdf07a276d1b4b6a Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Tue, 24 Aug 2021 21:50:40 -0700 Subject: [PATCH 2464/3768] fix RFC #341: dot-like operators are now parsed with same precedence as `.` (#18711) * fix RFC #341: dot-like operators are now parsed with same precedence as `.` * fixup * [skip ci] address comment in changelog * address comment * update grammmar * add manual entry * fixup * -d:nimPreviewDotLikeOps * address comment to unblock PR: move nimPreviewDotLikeOps out of config/config.nims --- changelog.md | 4 ++++ compiler/lineinfos.nim | 2 ++ compiler/parser.nim | 34 +++++++++++++++++++++++++++++----- doc/grammar.txt | 2 ++ doc/manual.rst | 9 +++++++++ tests/config.nims | 1 + tests/misc/trfc_341.nim | 27 +++++++++++++++++++++++++++ 7 files changed, 74 insertions(+), 5 deletions(-) create mode 100644 tests/misc/trfc_341.nim diff --git a/changelog.md b/changelog.md index 447401cd0..3ba008cf3 100644 --- a/changelog.md +++ b/changelog.md @@ -104,6 +104,10 @@ - In `std/dom`, `Interval` is now a `ref object`, same as `Timeout`. Definitions of `setTimeout`, `clearTimeout`, `setInterval`, `clearInterval` were updated. +- With `-d:nimPreviewDotLikeOps` (default in devel), dot-like operators (operators starting with `.`, but not with `..`) + now have the same precedence as `.`, so that `a.?b.c` is now parsed as `(a.?b).c` instead of `a.?(b.c)`. + A warning is generated when a dot-like operator is used without `-d:nimPreviewDotLikeOps`. + - The allocator for Nintendo Switch, which was nonfunctional because of breaking changes in libnx, was removed, in favour of the new `-d:nimAllocPagesViaMalloc` option. diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index c64d7b9dd..fd7ce0c04 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -46,6 +46,7 @@ type warnCannotOpenFile = "CannotOpenFile", warnOctalEscape = "OctalEscape", warnXIsNeverRead = "XIsNeverRead", warnXmightNotBeenInit = "XmightNotBeenInit", warnDeprecated = "Deprecated", warnConfigDeprecated = "ConfigDeprecated", + warnDotLikeOps = "DotLikeOps", warnSmallLshouldNotBeUsed = "SmallLshouldNotBeUsed", warnUnknownMagic = "UnknownMagic", warnRstRedefinitionOfLabel = "RedefinitionOfLabel", warnRstUnknownSubstitutionX = "UnknownSubstitutionX", @@ -116,6 +117,7 @@ const warnXmightNotBeenInit: "'$1' might not have been initialized", warnDeprecated: "$1", warnConfigDeprecated: "config file '$1' is deprecated", + warnDotLikeOps: "$1", warnSmallLshouldNotBeUsed: "'l' should not be used as an identifier; may look like '1' (one)", warnUnknownMagic: "unknown magic '$1' might crash the compiler", warnRstRedefinitionOfLabel: "redefinition of label '$1'", diff --git a/compiler/parser.nim b/compiler/parser.nim index b089614b2..30180e545 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -466,6 +466,17 @@ proc dotExpr(p: var Parser, a: PNode): PNode = exprColonEqExprListAux(p, tkParRi, y) result = y +proc dotLikeExpr(p: var Parser, a: PNode): PNode = + #| dotLikeExpr = expr DOTLIKEOP optInd symbol + var info = p.parLineInfo + result = newNodeI(nkInfix, info) + optInd(p, result) + var opNode = newIdentNodeP(p.tok.ident, p) + getTok(p) + result.add(opNode) + result.add(a) + result.add(parseSymbol(p, smAfterDot)) + proc qualifiedIdent(p: var Parser): PNode = #| qualifiedIdent = symbol ('.' optInd symbol)? result = parseSymbol(p) @@ -788,10 +799,15 @@ proc commandExpr(p: var Parser; r: PNode; mode: PrimaryMode): PNode = p.hasProgress = false result.add commandParam(p, isFirstParam, mode) +proc isDotLike(tok: Token): bool = + result = tok.tokType == tkOpr and tok.ident.s.len > 1 and + tok.ident.s[0] == '.' and tok.ident.s[1] != '.' + proc primarySuffix(p: var Parser, r: PNode, baseIndent: int, mode: PrimaryMode): PNode = #| primarySuffix = '(' (exprColonEqExpr comma?)* ')' #| | '.' optInd symbol generalizedLit? + #| | DOTLIKEOP optInd symbol generalizedLit? #| | '[' optInd exprColonEqExprList optPar ']' #| | '{' optInd exprColonEqExprList optPar '}' #| | &( '`'|IDENT|literal|'cast'|'addr'|'type') expr # command syntax @@ -840,11 +856,19 @@ proc primarySuffix(p: var Parser, r: PNode, # `foo ref` or `foo ptr`. Unfortunately, these two are also # used as infix operators for the memory regions feature and # the current parsing rules don't play well here. - if p.inPragma == 0 and (isUnary(p.tok) or p.tok.tokType notin {tkOpr, tkDotDot}): - # actually parsing {.push hints:off.} as {.push(hints:off).} is a sweet - # solution, but pragmas.nim can't handle that - result = commandExpr(p, result, mode) - break + let isDotLike2 = p.tok.isDotLike + if isDotLike2 and p.lex.config.isDefined("nimPreviewDotLikeOps"): + # synchronize with `tkDot` branch + result = dotLikeExpr(p, result) + result = parseGStrLit(p, result) + else: + if isDotLike2: + parMessage(p, warnDotLikeOps, "dot-like operators will be parsed differently with `-d:nimPreviewDotLikeOps`") + if p.inPragma == 0 and (isUnary(p.tok) or p.tok.tokType notin {tkOpr, tkDotDot}): + # actually parsing {.push hints:off.} as {.push(hints:off).} is a sweet + # solution, but pragmas.nim can't handle that + result = commandExpr(p, result, mode) + break else: break diff --git a/doc/grammar.txt b/doc/grammar.txt index d4f4a0515..f58621b97 100644 --- a/doc/grammar.txt +++ b/doc/grammar.txt @@ -29,6 +29,7 @@ exprList = expr ^+ comma exprColonEqExprList = exprColonEqExpr (comma exprColonEqExpr)* (comma)? dotExpr = expr '.' optInd (symbol | '[:' exprList ']') explicitGenericInstantiation = '[:' exprList ']' ( '(' exprColonEqExpr ')' )? +dotLikeExpr = expr DOTLIKEOP optInd symbol qualifiedIdent = symbol ('.' optInd symbol)? setOrTableConstr = '{' ((exprColonEqExpr comma)* | ':' ) '}' castExpr = 'cast' ('[' optInd typeDesc optPar ']' '(' optInd expr optPar ')') / @@ -56,6 +57,7 @@ tupleConstr = '(' optInd (exprColonEqExpr comma?)* optPar ')' arrayConstr = '[' optInd (exprColonEqExpr comma?)* optPar ']' primarySuffix = '(' (exprColonEqExpr comma?)* ')' | '.' optInd symbol generalizedLit? + | DOTLIKEOP optInd symbol generalizedLit? | '[' optInd exprColonEqExprList optPar ']' | '{' optInd exprColonEqExprList optPar '}' | &( '`'|IDENT|literal|'cast'|'addr'|'type') expr # command syntax diff --git a/doc/manual.rst b/doc/manual.rst index 9c7c219e9..72d50a901 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -748,6 +748,7 @@ has the second-lowest precedence. Otherwise, precedence is determined by the first character. + ================ ======================================================= ================== =============== Precedence level Operators First character Terminal symbol ================ ======================================================= ================== =============== @@ -783,6 +784,14 @@ of a call or whether it is parsed as a tuple constructor: .. code-block:: nim echo (1, 2) # pass the tuple (1, 2) to echo +Dot-like operators +------------------ + +Terminal symbol in the grammar: `DOTLIKEOP`. + +Dot-like operators are operators starting with `.`, but not with `..`, for e.g. `.?`; +they have the same precedence as `.`, so that `a.?b.c` is parsed as `(a.?b).c` instead of `a.?(b.c)`. + Grammar ------- diff --git a/tests/config.nims b/tests/config.nims index 0327f0b76..86787db1d 100644 --- a/tests/config.nims +++ b/tests/config.nims @@ -36,3 +36,4 @@ switch("define", "nimExperimentalAsyncjsThen") switch("define", "nimExperimentalLinenoiseExtra") switch("define", "nimPreviewFloatRoundtrip") +switch("define", "nimPreviewDotLikeOps") diff --git a/tests/misc/trfc_341.nim b/tests/misc/trfc_341.nim new file mode 100644 index 000000000..37cf675c6 --- /dev/null +++ b/tests/misc/trfc_341.nim @@ -0,0 +1,27 @@ +# test for https://github.com/nim-lang/RFCs/issues/341 +import std/json +import std/jsonutils +import std/macros + +macro fn1(a: untyped): string = newLit a.lispRepr + +doAssert fn1(a.?b.c) == """(DotExpr (Infix (Ident ".?") (Ident "a") (Ident "b")) (Ident "c"))""" + +template `.?`(a: JsonNode, b: untyped{ident}): JsonNode = + a[astToStr(b)] + +proc identity[T](a: T): T = a +proc timesTwo[T](a: T): T = a * 2 + +template main = + let a = (a1: 1, a2: "abc", a3: (a4: 2.5)) + let j = a.toJson + doAssert j.?a1.getInt == 1 + doAssert j.?a3.?a4.getFloat == 2.5 + doAssert j.?a3.?a4.getFloat.timesTwo == 5.0 + doAssert j.?a3.identity.?a4.getFloat.timesTwo == 5.0 + doAssert j.identity.?a3.identity.?a4.identity.getFloat.timesTwo == 5.0 + doAssert j.identity.?a3.?a4.identity.getFloat.timesTwo == 5.0 + +static: main() +main() From c70e4040bd69e1ee32b6c676dbfc763c7cf4df37 Mon Sep 17 00:00:00 2001 From: Dankr4d Date: Wed, 25 Aug 2021 19:27:00 +0200 Subject: [PATCH 2465/3768] fixes #14511 [backport:1.4] (#18732) * fixes #14511 [backport:1.4] Signed-off-by: Dankr4d * Replaced fix with code from alaviss, for better readability, with small changes. Signed-off-by: Dankr4d * - Specified output in test. Signed-off-by: Dankr4d * Replaced case in nnkRecCase with a simpler version, which just adds the last son. Signed-off-by: Dankr4d * Update tests/macros/t14511.nim * Update tests/macros/t14511.nim Co-authored-by: Andreas Rumpf --- lib/core/macros.nim | 21 ++++++++-------- tests/macros/t14511.nim | 54 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 11 deletions(-) create mode 100644 tests/macros/t14511.nim diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 645f5067a..806ea7e31 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -1541,18 +1541,17 @@ proc customPragmaNode(n: NimNode): NimNode = for i in 0.. 0: var identDefs = identDefsStack.pop() - if identDefs.kind == nnkRecCase: - identDefsStack.add(identDefs[0]) - for i in 1.. Date: Wed, 25 Aug 2021 22:04:18 -0700 Subject: [PATCH 2466/3768] followup #18711 grammar_nanny now reports unused non-terminals (#18746) * followup #18711 cleanup unused grammar rules * make tools/grammar_nanny.nim report unused terminals * revert removal of some grammar comments --- tools/grammar_nanny.nim | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/grammar_nanny.nim b/tools/grammar_nanny.nim index 397041559..5120ef976 100644 --- a/tools/grammar_nanny.nim +++ b/tools/grammar_nanny.nim @@ -4,7 +4,7 @@ import std / [strutils, sets] import ".." / compiler / [ - llstream, ast, lexer, options, msgs, idents, + llstream, lexer, options, msgs, idents, lineinfos, pathutils] proc checkGrammarFileImpl(cache: IdentCache, config: ConfigRef) = @@ -35,6 +35,10 @@ proc checkGrammarFileImpl(cache: IdentCache, config: ConfigRef) = usedSyms.incl word else: rawGetTok(L, tok) + for u in declaredSyms: + if u notin usedSyms: + echo "Unused non-terminal: ", u + for u in usedSyms: if u notin declaredSyms: echo "Undeclared non-terminal: ", u From 041edaa1df96fc7963f01bf5048f2bee86b6b363 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Thu, 26 Aug 2021 18:07:54 -0300 Subject: [PATCH 2467/3768] Implement RFC-413 (#18749) * Implement RFC 413 * Implement RFC 413 * Implement RFC 413 * Implement RFC 413 * https://github.com/nim-lang/Nim/pull/18749#discussion_r696320995 * Update lib/js/dom_extensions.nim Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> * Update lib/js/dom_extensions.nim * https://github.com/nim-lang/Nim/pull/18749#discussion_r696913310 Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Co-authored-by: Andreas Rumpf --- changelog.md | 3 +++ lib/js/dom.nim | 3 +++ lib/js/dom_extensions.nim | 6 ++---- tests/effects/tstrict_funcs_imports_js.nim | 1 - 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/changelog.md b/changelog.md index 3ba008cf3..65ae61833 100644 --- a/changelog.md +++ b/changelog.md @@ -371,6 +371,9 @@ - Added `dom.setInterval`, `dom.clearInterval` overloads. +- Merged `dom_extensions` module into `dom` module, + it was a module with a single line, see https://github.com/nim-lang/RFCs/issues/413 + - Allow reading parameters when compiling for Nintendo Switch. - Deprecated `sequtils.delete` and added an overload taking a `Slice` that raises a defect diff --git a/lib/js/dom.nim b/lib/js/dom.nim index 3f0b9f630..aca79faca 100644 --- a/lib/js/dom.nim +++ b/lib/js/dom.nim @@ -1771,3 +1771,6 @@ since (1, 3): ## https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsDataURL proc readAsText*(f: FileReader, b: Blob|File, encoding = cstring"UTF-8") {.importcpp: "#.readAsText(#, #)".} ## https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsText + +since (1, 5): + proc elementsFromPoint*(n: DocumentOrShadowRoot; x, y: float): seq[Element] {.importcpp.} diff --git a/lib/js/dom_extensions.nim b/lib/js/dom_extensions.nim index a1ceff5b4..c15a03195 100644 --- a/lib/js/dom_extensions.nim +++ b/lib/js/dom_extensions.nim @@ -1,5 +1,3 @@ import std/dom - -{.push importcpp.} -proc elementsFromPoint*(n: DocumentOrShadowRoot; x, y: float): seq[Element] -{.pop.} +export elementsFromPoint +{.deprecated: "use `std/dom` instead".} diff --git a/tests/effects/tstrict_funcs_imports_js.nim b/tests/effects/tstrict_funcs_imports_js.nim index b7fcd343a..667887ff0 100644 --- a/tests/effects/tstrict_funcs_imports_js.nim +++ b/tests/effects/tstrict_funcs_imports_js.nim @@ -7,7 +7,6 @@ discard """ import asyncjs, dom, - dom_extensions, jsconsole, jsffi, jsre From 5eba3725707df0f62dcf9c380eaa518635b0f0c0 Mon Sep 17 00:00:00 2001 From: flywind Date: Fri, 27 Aug 2021 17:50:06 +0800 Subject: [PATCH 2468/3768] [minor] break loops if it is ambiguous (#18745) * [minor] break loops if it is ambiguous * Update compiler/lookups.nim Co-authored-by: Timothee Cour Co-authored-by: Timothee Cour --- compiler/lookups.nim | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 331eef525..4c072eed8 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -181,13 +181,14 @@ proc someSymFromImportTable*(c: PContext; name: PIdent; ambiguous: var bool): PS if overloadableEnums notin c.features: symSet.excl skEnumField result = nil - for im in c.imports.mitems: - for s in symbols(im, marked, name, c.graph): - if result == nil: - result = s - else: - if s.kind notin symSet or result.kind notin symSet: + block outer: + for im in c.imports.mitems: + for s in symbols(im, marked, name, c.graph): + if result == nil: + result = s + elif s.kind notin symSet or result.kind notin symSet: ambiguous = true + break outer proc searchInScopes*(c: PContext, s: PIdent; ambiguous: var bool): PSym = for scope in allScopes(c.currentScope): From 719718df2f98f2bd9aa4286ff8aeac3f02e4d46c Mon Sep 17 00:00:00 2001 From: Miran Date: Sat, 28 Aug 2021 08:27:56 +0200 Subject: [PATCH 2469/3768] more minor bootstrapping cleanups (#18759) --- compiler/ccgtypes.nim | 3 --- compiler/cgen.nim | 1 - compiler/vmops.nim | 1 - 3 files changed, 5 deletions(-) diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index d05c4cd8b..492e1f181 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -12,7 +12,6 @@ # ------------------------- Name Mangling -------------------------------- import sighashes, modulegraphs -from lowerings import createObj proc genProcHeader(m: BModule, prc: PSym, asPtr: bool = false): Rope @@ -256,8 +255,6 @@ proc addAbiCheck(m: BModule, t: PType, name: Rope) = m.s[cfsTypeInfo].addf("NIM_STATIC_ASSERT(sizeof($1) == $2, $3);$n", [name, rope(size), msg2.rope]) # see `testCodegenABICheck` for example error message it generates -from ccgutils import ccgIntroducedPtr -export ccgIntroducedPtr proc fillResult(conf: ConfigRef; param: PNode) = fillLoc(param.sym.loc, locParam, param, ~"Result", diff --git a/compiler/cgen.nim b/compiler/cgen.nim index f55da7f65..a06a87172 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -23,7 +23,6 @@ when not defined(leanCompiler): import strutils except `%` # collides with ropes.`%` from ic / ic import ModuleBackendFlag -from modulegraphs import ModuleGraph, PPassContext import dynlib when not declared(dynlib.libCandidates): diff --git a/compiler/vmops.nim b/compiler/vmops.nim index a1a4996b2..08225b065 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -29,7 +29,6 @@ from std/hashes import hash from std/osproc import nil from system/formatfloat import addFloatRoundtrip, addFloatSprintf -from sighashes import symBodyDigest # There are some useful procs in vmconv. import vmconv From 014edde36a2f61b0a232ceb4800681a2ce261759 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 28 Aug 2021 16:34:27 -0700 Subject: [PATCH 2470/3768] followup #18759: make compiler build cleanly with 0 warnings/hints (#18764) --- compiler/semtypes.nim | 2 -- compiler/vmops.nim | 6 ++++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index cb7f847a9..9f7a17a53 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -10,8 +10,6 @@ # this module does the semantic checking of type declarations # included from sem.nim -import math - const errStringOrIdentNodeExpected = "string or ident node expected" errStringLiteralExpected = "string literal expected" diff --git a/compiler/vmops.nim b/compiler/vmops.nim index 08225b065..e287dd41d 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -15,10 +15,12 @@ from std/math import sqrt, ln, log10, log2, exp, round, arccos, arcsin, lgamma when declared(math.copySign): - from std/math import copySign + # pending bug #18762, avoid renaming math + from std/math as math2 import copySign when declared(math.signbit): - from std/math import signbit + # ditto + from std/math as math3 import signbit from std/os import getEnv, existsEnv, delEnv, putEnv, dirExists, fileExists, walkDir, getAppFilename, raiseOSError, osLastError From c07d8da7b9d491500096dbd4c224f5762cbf89ac Mon Sep 17 00:00:00 2001 From: flywind Date: Sun, 29 Aug 2021 15:18:54 +0800 Subject: [PATCH 2471/3768] add missing docs (#18758) --- lib/nimrtl.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/nimrtl.nim b/lib/nimrtl.nim index 301ffb43b..93349b287 100644 --- a/lib/nimrtl.nim +++ b/lib/nimrtl.nim @@ -26,6 +26,7 @@ batchable: false ## * unicode ## * pegs ## * ropes +## * cstrutils ## when system.appType != "lib": From 0ddd9519c0007b705d70b5a75b634831396dd904 Mon Sep 17 00:00:00 2001 From: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Date: Sun, 29 Aug 2021 10:42:52 +0200 Subject: [PATCH 2472/3768] Remove `Covariance` section from the experimental manual (#18688) * Remove `Covariance` section * Add blank lines after `.. code-block::` * Fix CI? --- doc/manual_experimental.rst | 164 ++++++++++++++---------------------- 1 file changed, 63 insertions(+), 101 deletions(-) diff --git a/doc/manual_experimental.rst b/doc/manual_experimental.rst index 3272a4d0d..458512292 100644 --- a/doc/manual_experimental.rst +++ b/doc/manual_experimental.rst @@ -70,6 +70,7 @@ type `void` are treated as non-existent, `void` as a return type means that the procedure does not return a value: .. code-block:: nim + proc nothing(x, y: void): void = echo "ha" @@ -78,6 +79,7 @@ the procedure does not return a value: The `void` type is particularly useful for generic code: .. code-block:: nim + proc callProc[T](p: proc (x: T), x: T) = when T is void: p() @@ -93,6 +95,7 @@ The `void` type is particularly useful for generic code: However, a `void` type cannot be inferred in generic code: .. code-block:: nim + callProc(emptyProc) # Error: type mismatch: got (proc ()) # but expected one of: @@ -102,106 +105,6 @@ The `void` type is only valid for parameters and return types; other symbols cannot have the type `void`. - -Covariance -========== - -Covariance in Nim can be introduced only through pointer-like types such -as `ptr` and `ref`. Sequence, Array and OpenArray types, instantiated -with pointer-like types will be considered covariant if and only if they -are also immutable. The introduction of a `var` modifier or additional -`ptr` or `ref` indirections would result in invariant treatment of -these types. - -`proc` types are currently always invariant, but future versions of Nim -may relax this rule. - -User-defined generic types may also be covariant with respect to some of -their parameters. By default, all generic params are considered invariant, -but you may choose the apply the prefix modifier `in` to a parameter to -make it contravariant or `out` to make it covariant: - -.. code-block:: nim - type - AnnotatedPtr[out T] = - metadata: MyTypeInfo - p: ref T - - RingBuffer[out T] = - startPos: int - data: seq[T] - - Action {.importcpp: "std::function".} [in T] = object - -When the designated generic parameter is used to instantiate a pointer-like -type as in the case of `AnnotatedPtr` above, the resulting generic type will -also have pointer-like covariance: - -.. code-block:: nim - type - GuiWidget = object of RootObj - Button = object of GuiWidget - ComboBox = object of GuiWidget - - var - widgetPtr: AnnotatedPtr[GuiWidget] - buttonPtr: AnnotatedPtr[Button] - - ... - - proc drawWidget[T](x: AnnotatedPtr[GuiWidget]) = ... - - # you can call procs expecting base types by supplying a derived type - drawWidget(buttonPtr) - - # and you can convert more-specific pointer types to more general ones - widgetPtr = buttonPtr - -Just like with regular pointers, covariance will be enabled only for immutable -values: - -.. code-block:: nim - proc makeComboBox[T](x: var AnnotatedPtr[GuiWidget]) = - x.p = new(ComboBox) - - makeComboBox(buttonPtr) # Error, AnnotatedPtr[Button] cannot be modified - # to point to a ComboBox - -On the other hand, in the `RingBuffer` example above, the designated generic -param is used to instantiate the non-pointer `seq` type, which means that -the resulting generic type will have covariance that mimics an array or -sequence (i.e. it will be covariant only when instantiated with `ptr` and -`ref` types): - -.. code-block:: nim - - type - Base = object of RootObj - Derived = object of Base - - proc consumeBaseValues(b: RingBuffer[Base]) = ... - - var derivedValues: RingBuffer[Derived] - - consumeBaseValues(derivedValues) # Error, Base and Derived values may differ - # in size - - proc consumeBasePointers(b: RingBuffer[ptr Base]) = ... - - var derivedPointers: RingBuffer[ptr Derived] - - consumeBaseValues(derivedPointers) # This is legal - -Please note that Nim will treat the user-defined pointer-like types as -proper alternatives to the built-in pointer types. That is, types such -as `seq[AnnotatedPtr[T]]` or `RingBuffer[AnnotatedPtr[T]]` will also be -considered covariant and you can create new pointer-like types by instantiating -other user-defined pointer-like types. - -The contravariant parameters introduced with the `in` modifier are currently -useful only when interfacing with imported types having such semantics. - - Automatic dereferencing ======================= @@ -209,6 +112,7 @@ Automatic dereferencing is performed for the first argument of a routine call. This feature has to be enabled via `{.experimental: "implicitDeref".}`: .. code-block:: nim + {.experimental: "implicitDeref".} proc depth(x: NodeObj): int = ... @@ -219,6 +123,7 @@ This feature has to be enabled via `{.experimental: "implicitDeref".}`: echo n.depth # no need to write n[].depth either + Code reordering =============== @@ -281,6 +186,7 @@ statement) can have their entire initialization statement reordered. Be wary of what code is executed at the top level: .. code-block:: nim + {.experimental: "codeReordering".} proc a() = @@ -300,6 +206,7 @@ what code is executed at the top level: code is the same as it would be with code reordering disabled. .. code-block:: nim + {.experimental: "codeReordering".} proc x() = @@ -313,6 +220,7 @@ It is important to note that reordering *only* works for symbols at top level scope. Therefore, the following will *fail to compile:* .. code-block:: nim + {.experimental: "codeReordering".} proc a() = @@ -331,6 +239,7 @@ has different names. This does not need an `experimental` switch, but is an unstable feature. .. code-block:: Nim + proc foo(x: int) = echo "Using x: ", x proc foo(y: int) = @@ -349,6 +258,7 @@ As a special more convenient notation, proc expressions involved in procedure calls can use the `do` keyword: .. code-block:: nim + sort(cities) do (x,y: string) -> int: cmp(x.len, y.len) @@ -371,6 +281,7 @@ parentheses is just a block of code. The `do` notation can be used to pass multiple blocks to a macro: .. code-block:: nim + macro performWithUndo(task, undo: untyped) = ... performWithUndo do: @@ -380,7 +291,6 @@ pass multiple blocks to a macro: # code to undo it - Special Operators ================= @@ -404,6 +314,7 @@ the expression, where the unknown field or proc name is passed to an `untyped` parameter: .. code-block:: nim + a.b # becomes `.`(a, b) a.b(c, d) # becomes `.`(a, b, c, d) @@ -411,6 +322,7 @@ The matched dot operators can be symbols of any callable kind (procs, templates and macros), depending on the desired effect: .. code-block:: nim + template `.`(js: PJsonNode, field: untyped): JSON = js[astToStr(field)] var js = parseJson("{ x: 1, y: 2}") @@ -435,6 +347,7 @@ operator `.=` This operator will be matched against assignments to missing fields. .. code-block:: nim + a.b = c # becomes `.=`(a, b, c) Call operator @@ -445,6 +358,7 @@ for existing routines. The experimental `callOperator` switch must be enabled to use this operator. .. code-block:: nim + {.experimental: "callOperator".} template `()`(a: int, b: float): untyped = $(a, b) @@ -480,6 +394,7 @@ All types for which `nil` is a valid value can be annotated with the `not nil` annotation to exclude `nil` as a valid value: .. code-block:: nim + {.experimental: "notnil".} type @@ -502,6 +417,7 @@ here. .. include:: manual_experimental_strictnotnil.rst + Concepts ======== @@ -511,6 +427,7 @@ arbitrary set of requirements that the matched type must satisfy. Concepts are written in the following form: .. code-block:: nim + type Comparable = concept x, y (x < y) is bool @@ -536,6 +453,7 @@ instance. You can also apply the `type` modifier to create a named instance of the type itself: .. code-block:: nim + type MyConcept = concept x, var v, ref r, ptr p, static s, type T ... @@ -545,6 +463,7 @@ and parameters are expected. This provides a more convenient way to check for the presence of callable symbols with specific signatures: .. code-block:: nim + type OutputStream = concept var s s.write(string) @@ -555,6 +474,7 @@ type, following the `concept` keyword is also considered to have the explicit modifier and will be matched only as a type. .. code-block:: nim + type # Let's imagine a user-defined casting framework with operators # such as `val.to(string)` and `val.to(JSonValue)`. We can test @@ -592,6 +512,7 @@ concept and, as a result, a wrong overload is selected, you can apply the `explain` pragma to either the concept body or a particular call-site. .. code-block:: nim + type MyConcept {.explain.} = concept ... @@ -607,6 +528,7 @@ Generic concepts and type binding rules The concept types can be parametric just like the regular generic types: .. code-block:: nim + ### matrixalgo.nim import std/typetraits @@ -680,6 +602,7 @@ Unbound static params will be inferred from expressions involving the `==` operator and also when types dependent on them are being matched: .. code-block:: nim + type MatrixReducer[M, N: static int; T] = concept x x.reduce(SquareMatrix[N, T]) is array[M, int] @@ -694,6 +617,7 @@ matched without permanently inferring it. This may be useful when you need to match several procs accepting the same wide class of types: .. code-block:: nim + type Enumerable[T] = concept e for v in e: @@ -716,6 +640,7 @@ types used in multiple signatures, without actually requiring any concrete types, thus allowing you to encode implementation-defined types: .. code-block:: nim + type MyConcept = concept x type T1 = auto @@ -776,6 +701,7 @@ accessible through the dot operator in procs where the concept was successfully matched to a concrete type: .. code-block:: nim + type DateTime = concept t1, t2, type T const Min = T.MinDate @@ -810,6 +736,7 @@ one. As an alternative way of defining concept refinements, you can use the object inheritance syntax involving the `of` keyword: .. code-block:: nim + type Graph = concept g, type G of EquallyComparable, Copyable type @@ -853,6 +780,7 @@ object inheritance syntax involving the `of` keyword: the concept body: .. code-block:: nim + type Stringable = concept x $x is string @@ -904,6 +832,7 @@ object inheritance syntax involving the `of` keyword: types to the corresponding VTable type. .. code-block:: nim + type IntEnumerable = vtref Enumerable[int] @@ -970,6 +899,7 @@ language may weaken this restriction.) The signature has to be: .. code-block:: nim + proc `=deepCopy`(x: T): T This mechanism will be used by most data structures that support shared memory @@ -1037,6 +967,7 @@ phase of the compiler: This means they provide an easy way to enhance the compilation pipeline with user defined optimizations: .. code-block:: nim + template optMul{`*`(a, 2)}(a: int): int = a+a let x = 3 @@ -1059,6 +990,7 @@ Unfortunately optimizations are hard to get right and even the tiny example is **wrong**: .. code-block:: nim + template optMul{`*`(a, 2)}(a: int): int = a+a proc f(): int = @@ -1071,6 +1003,7 @@ We cannot duplicate 'a' if it denotes an expression that has a side effect! Fortunately Nim supports side effect analysis: .. code-block:: nim + template optMul{`*`(a, 2)}(a: int{noSideEffect}): int = a+a proc f(): int = @@ -1088,11 +1021,13 @@ cannot really do that however as the following code only swaps arguments blindly: .. code-block:: nim + template mulIsCommutative{`*`(a, b)}(a, b: int): int = b*a What optimizers really need to do is a *canonicalization*: .. code-block:: nim + template canonMul{`*`(a, b)}(a: int{lit}, b: int): int = b*a The `int{lit}` parameter pattern matches against an expression of @@ -1152,6 +1087,7 @@ but also to every other bound parameter; syntactically they need to occur after the ordinary AST predicates: .. code-block:: nim + template ex{a = b + c}(a: int{noalias}, b, c: int) = # this transformation is only valid if 'b' and 'c' do not alias 'a': a = b @@ -1160,6 +1096,7 @@ the ordinary AST predicates: Another example: .. code-block:: nim + proc somefunc(s: string) = assert s == "variable" proc somefunc(s: string{nkStrLit}) = assert s == "literal" proc somefunc(s: string{nkRStrLit}) = assert s == r"raw" @@ -1189,6 +1126,7 @@ The `|` operator The `|` operator if used as infix operator creates an ordered choice: .. code-block:: nim + template t{0|1}(): untyped = 3 let a = 1 # outputs 3: @@ -1198,6 +1136,7 @@ The matching is performed after the compiler performed some optimizations like constant folding, so the following does not work: .. code-block:: nim + template t{0|1}(): untyped = 3 # outputs 1: echo 1 @@ -1215,6 +1154,7 @@ A pattern expression can be bound to a pattern parameter via the `expr{param}` notation: .. code-block:: nim + template t{(0|1|2){x}}(x: untyped): untyped = x+1 let a = 1 # outputs 2: @@ -1227,6 +1167,7 @@ The `~` operator The `~` operator is the **not** operator in patterns: .. code-block:: nim + template t{x = (~x){y} and (~x){z}}(x, y, z: bool) = x = y if x: x = z @@ -1246,6 +1187,7 @@ The `*` operator can *flatten* a nested binary expression like `a & b & c` to `&(a, b, c)`: .. code-block:: nim + var calls = 0 @@ -1270,6 +1212,7 @@ which is flattened into a call expression; thus the invocation of `optConc` produces: .. code-block:: nim + `&&`("my", space & "awe", "some ", "concat") @@ -1280,6 +1223,7 @@ The `**` is much like the `*` operator, except that it gathers not only all the arguments, but also the matched operators in reverse polish notation: .. code-block:: nim + import std/macros type @@ -1324,6 +1268,7 @@ parameter is of the type `varargs` it is treated specially and it can match 0 or more arguments in the AST to be matched against: .. code-block:: nim + template optWrite{ write(f, x) ((write|writeLine){w})(f, y) @@ -1339,6 +1284,7 @@ The following example shows how some simple partial evaluation can be implemented with term rewriting: .. code-block:: nim + proc p(x, y: int; cond: bool): int = result = if cond: x + y else: x - y @@ -1352,6 +1298,7 @@ Example: Hoisting The following example shows how some form of hoisting can be implemented: .. code-block:: nim + import std/pegs template optPeg{peg(pattern)}(pattern: string{lit}): Peg = @@ -1375,6 +1322,7 @@ Parameter constraints can also be used for ordinary routine parameters; these constraints affect ordinary overloading resolution then: .. code-block:: nim + proc optLit(a: string{lit|`const`}) = echo "string literal" proc optLit(a: string) = @@ -1426,6 +1374,7 @@ Spawn statement `spawn`:idx: can be used to pass a task to the thread pool: .. code-block:: nim + import std/threadpool proc processLine(line: string) = @@ -1458,6 +1407,7 @@ with the `^` operator is **blocking**. However, one can use `blockUntilAny` to wait on multiple flow variables at the same time: .. code-block:: nim + import std/threadpool, ... # wait until 2 out of 3 servers received the update: @@ -1552,6 +1502,7 @@ Protecting global variables Object fields and global variables can be annotated via a `guard` pragma: .. code-block:: nim + var glock: TLock var gdata {.guard: glock.}: int @@ -1559,6 +1510,7 @@ The compiler then ensures that every access of `gdata` is within a `locks` section: .. code-block:: nim + proc invalid = # invalid: unguarded access: echo gdata @@ -1577,6 +1529,7 @@ semantics and should not be used directly! It should only be used in templates that also implement some form of locking at runtime: .. code-block:: nim + template lock(a: TLock; body: untyped) = pthread_mutex_lock(a) {.locks: [a].}: @@ -1590,6 +1543,7 @@ The guard does not need to be of any particular type. It is flexible enough to model low level lockfree mechanisms: .. code-block:: nim + var dummyLock {.compileTime.}: int var atomicCounter {.guard: dummyLock.}: int @@ -1617,6 +1571,7 @@ Since objects can reside on the heap or on the stack this greatly enhances the expressivity of the language: .. code-block:: nim + type ProtectedCounter = object v {.guard: L.}: int @@ -1631,6 +1586,7 @@ The access to field `x.v` is allowed since its guard `x.L` is active. After template expansion, this amounts to: .. code-block:: nim + proc incCounters(counters: var openArray[ProtectedCounter]) = for i in 0..counters.high: pthread_mutex_lock(counters[i].L) @@ -1651,6 +1607,7 @@ Two paths are considered equivalent if they are syntactically the same. This means the following compiles (for now) even though it really should not: .. code-block:: nim + {.locks: [a[i].L].}: inc i access a[i].v @@ -1671,6 +1628,7 @@ of the same level can only be acquired *at the same time* within a single `locks` section: .. code-block:: nim + var a, b: TLock[2] var x: TLock[1] # invalid locking order: TLock[1] cannot be acquired before TLock[2]: @@ -1697,6 +1655,7 @@ the runtime check is required to ensure a global ordering for two locks `a` and `b` of the same lock level: .. code-block:: nim + template multilock(a, b: ptr TLock; body: untyped) = if cast[ByteAddress](a) < cast[ByteAddress](b): pthread_mutex_lock(a) @@ -1717,6 +1676,7 @@ level. This then means that the routine may acquire locks of up to this level. This is essential so that procs can be called within a `locks` section: .. code-block:: nim + proc p() {.locks: 3.} = discard var a: TLock[4] @@ -1739,6 +1699,7 @@ cannot be inferred statically, leading to compiler warnings. By using having unknown lock level as well: .. code-block:: nim + type SomeBase* = ref object of RootObj type SomeDerived* = ref object of SomeBase memberProc*: proc () @@ -1761,6 +1722,7 @@ e.g. when rewriting term to same term plus extra content. e.g. with given example `echo("ab")` will be rewritten just once: .. code-block:: nim + template pwnEcho{echo(x)}(x: untyped) = {.noRewrite.}: echo("pwned!") From fa7c1aa8647aa97a4a204751db727c3cbeded749 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sun, 29 Aug 2021 06:54:07 -0700 Subject: [PATCH 2473/3768] move PNode.comment to a side channel, reducing memory usage during compilation by a factor 1.25x (#18760) * move PNode.comment so a side channel, reducing memory usage * fix a bug * fixup * use sfHasComment to speedup comment lookups * fix for IC * Update compiler/parser.nim Co-authored-by: Andreas Rumpf --- compiler/ast.nim | 77 +++++++++++++++++++++++++++++++------------ compiler/commands.nim | 3 +- compiler/parser.nim | 14 +++++--- 3 files changed, 67 insertions(+), 27 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 446c00f49..5893a5671 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -10,7 +10,7 @@ # abstract syntax tree + symbol table import - lineinfos, hashes, options, ropes, idents, int128 + lineinfos, hashes, options, ropes, idents, int128, tables from strutils import toLowerAscii export int128 @@ -497,6 +497,7 @@ type nfExecuteOnReload # A top-level statement that will be executed during reloads nfLastRead # this node is a last read nfFirstWrite# this node is a first write + nfHasComment # node has a comment TNodeFlags* = set[TNodeFlag] TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 43) @@ -774,7 +775,6 @@ type ident*: PIdent else: sons*: TNodeSeq - comment*: string TStrTable* = object # a table[PIdent] of PSym counter*: int @@ -991,6 +991,38 @@ type TImplication* = enum impUnknown, impNo, impYes +template nodeId(n: PNode): int = cast[int](n) + +type Gconfig = object + # we put comments in a side channel to avoid increasing `sizeof(TNode)`, which + # reduces memory usage given that `PNode` is the most allocated type by far. + comments: Table[int, string] # nodeId => comment + useIc*: bool + +var gconfig {.threadvar.}: Gconfig + +proc setUseIc*(useIc: bool) = gconfig.useIc = useIc + +proc comment*(n: PNode): string = + if nfHasComment in n.flags and not gconfig.useIc: + # IC doesn't track comments, see `packed_ast`, so this could fail + result = gconfig.comments[n.nodeId] + +proc `comment=`*(n: PNode, a: string) = + let id = n.nodeId + if a.len > 0: + # if needed, we could periodically cleanup gconfig.comments when its size increases, + # to ensure only live nodes (and with nfHasComment) have an entry in gconfig.comments; + # for compiling compiler, the waste is very small: + # num calls to newNodeImpl: 14984160 (num of PNode allocations) + # size of gconfig.comments: 33585 + # num of nodes with comments that were deleted and hence wasted: 3081 + n.flags.incl nfHasComment + gconfig.comments[id] = a + elif nfHasComment in n.flags: + n.flags.excl nfHasComment + gconfig.comments.del(id) + # BUGFIX: a module is overloadable so that a proc can have the # same name as an imported module. This is necessary because of # the poor naming choices in the standard library. @@ -1207,9 +1239,17 @@ when defined(useNodeIds): const nodeIdToDebug* = -1 # 2322968 var gNodeId: int -proc newNode*(kind: TNodeKind): PNode = - ## new node with unknown line info, no type, and no children - result = PNode(kind: kind, info: unknownLineInfo) +template newNodeImpl(info2) = + result = PNode(kind: kind, info: info2) + when false: + # this would add overhead, so we skip it; it results in a small amount of leaked entries + # for old PNode that gets re-allocated at the same address as a PNode that + # has `nfHasComment` set (and an entry in that table). Only `nfHasComment` + # should be used to test whether a PNode has a comment; gconfig.comments + # can contain extra entries for deleted PNode's with comments. + gconfig.comments.del(cast[int](result)) + +template setIdMaybe() = when defined(useNodeIds): result.id = gNodeId if result.id == nodeIdToDebug: @@ -1217,27 +1257,22 @@ proc newNode*(kind: TNodeKind): PNode = writeStackTrace() inc gNodeId +proc newNode*(kind: TNodeKind): PNode = + ## new node with unknown line info, no type, and no children + newNodeImpl(unknownLineInfo) + setIdMaybe() + proc newNodeI*(kind: TNodeKind, info: TLineInfo): PNode = ## new node with line info, no type, and no children - result = PNode(kind: kind, info: info) - when defined(useNodeIds): - result.id = gNodeId - if result.id == nodeIdToDebug: - echo "KIND ", result.kind - writeStackTrace() - inc gNodeId + newNodeImpl(info) + setIdMaybe() proc newNodeI*(kind: TNodeKind, info: TLineInfo, children: int): PNode = ## new node with line info, type, and children - result = PNode(kind: kind, info: info) + newNodeImpl(info) if children > 0: newSeq(result.sons, children) - when defined(useNodeIds): - result.id = gNodeId - if result.id == nodeIdToDebug: - echo "KIND ", result.kind - writeStackTrace() - inc gNodeId + setIdMaybe() proc newNodeIT*(kind: TNodeKind, info: TLineInfo, typ: PType): PNode = ## new node with line info, type, and no children @@ -1644,8 +1679,8 @@ proc copyNode*(src: PNode): PNode = template transitionNodeKindCommon(k: TNodeKind) = let obj {.inject.} = n[] - n[] = TNode(kind: k, typ: obj.typ, info: obj.info, flags: obj.flags, - comment: obj.comment) + n[] = TNode(kind: k, typ: obj.typ, info: obj.info, flags: obj.flags) + # n.comment = obj.comment # shouldn't be needed, the address doesnt' change when defined(useNodeIds): n.id = obj.id diff --git a/compiler/commands.nim b/compiler/commands.nim index 82e908152..079717862 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -8,7 +8,7 @@ # # This module handles the parsing of command line arguments. - +from ast import setUseIc # We do this here before the 'import' statement so 'defined' does not get # confused with 'TGCMode.gcMarkAndSweep' etc. @@ -862,6 +862,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "v2": conf.symbolFiles = v2Sf of "stress": conf.symbolFiles = stressTest else: localError(conf, info, "invalid option for --incremental: " & arg) + setUseIc(conf.symbolFiles != disabledSf) of "skipcfg": processOnOffSwitchG(conf, {optSkipSystemConfigFile}, arg, pass, info) of "skipprojcfg": diff --git a/compiler/parser.nim b/compiler/parser.nim index 30180e545..225493902 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -170,13 +170,15 @@ proc validInd(p: var Parser): bool {.inline.} = proc rawSkipComment(p: var Parser, node: PNode) = if p.tok.tokType == tkComment: if node != nil: + var rhs = node.comment when defined(nimpretty): if p.tok.commentOffsetB > p.tok.commentOffsetA: - node.comment.add fileSection(p.lex.config, p.lex.fileIdx, p.tok.commentOffsetA, p.tok.commentOffsetB) + rhs.add fileSection(p.lex.config, p.lex.fileIdx, p.tok.commentOffsetA, p.tok.commentOffsetB) else: - node.comment.add p.tok.literal + rhs.add p.tok.literal else: - node.comment.add p.tok.literal + rhs.add p.tok.literal + node.comment = move rhs else: parMessage(p, errInternal, "skipComment") getTok(p) @@ -1824,8 +1826,10 @@ proc parseRoutine(p: var Parser, kind: TNodeKind): PNode = if result.comment.len == 0: # proc fn*(a: int): int = a ## foo # => moves comment `foo` to `fn` - swap(result.comment, body[0].comment) - else: discard # xxx either `assert false` or issue a warning (otherwise we'll never know of this edge case) + result.comment = body[0].comment + body[0].comment = "" + else: + assert false, p.lex.config$body.info # avoids hard to track bugs, fail early. proc newCommentStmt(p: var Parser): PNode = #| commentStmt = COMMENT From f02de25ca1b660b0b6e1ae2d4981106744814fe3 Mon Sep 17 00:00:00 2001 From: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Date: Tue, 31 Aug 2021 08:14:05 +0200 Subject: [PATCH 2474/3768] Fix #15150 (#18730) --- lib/pure/asyncfile.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/pure/asyncfile.nim b/lib/pure/asyncfile.nim index c51b338e3..222a89b97 100644 --- a/lib/pure/asyncfile.nim +++ b/lib/pure/asyncfile.nim @@ -202,10 +202,11 @@ proc readBuffer*(f: AsyncFile, buf: pointer, size: int): Future[int] = proc read*(f: AsyncFile, size: int): Future[string] = ## Read `size` bytes from the specified file asynchronously starting at - ## the current position of the file pointer. + ## the current position of the file pointer. `size` should be greater than zero. ## ## If the file pointer is past the end of the file then an empty string is ## returned. + assert size > 0 var retFuture = newFuture[string]("asyncfile.read") when defined(windows) or defined(nimdoc): From 8f4bdb3596bb7650f2688b8243dc3164abfc21c9 Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 31 Aug 2021 19:32:37 +0800 Subject: [PATCH 2475/3768] [minor]break loops after a candidate is added to seqs (#18770) * [minor]break loops when added * Update compiler/lookups.nim Co-authored-by: Clyybber Co-authored-by: Clyybber --- compiler/lookups.nim | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/compiler/lookups.nim b/compiler/lookups.nim index 4c072eed8..fc30408e5 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -211,14 +211,16 @@ proc debugScopes*(c: PContext; limit=0, max = int.high) {.deprecated.} = proc searchInScopesFilterBy*(c: PContext, s: PIdent, filter: TSymKinds): seq[PSym] = result = @[] - for scope in allScopes(c.currentScope): - var ti: TIdentIter - var candidate = initIdentIter(ti, scope.symbols, s) - while candidate != nil: - if candidate.kind in filter: - if result.len == 0: + block outer: + for scope in allScopes(c.currentScope): + var ti: TIdentIter + var candidate = initIdentIter(ti, scope.symbols, s) + while candidate != nil: + if candidate.kind in filter: result.add candidate - candidate = nextIdentIter(ti, scope.symbols) + # Break here, because further symbols encountered would be shadowed + break outer + candidate = nextIdentIter(ti, scope.symbols) if result.len == 0: var marked = initIntSet() From 3469f3a39320cf35d02d11b78c7db0be79e992f8 Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 31 Aug 2021 20:05:45 +0800 Subject: [PATCH 2476/3768] add Apple Silicon to supporting platforms (#18772) * add Apple Silicon to supporting platforms * Update readme.md Co-authored-by: flywind Co-authored-by: Andreas Rumpf --- readme.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/readme.md b/readme.md index 7c6e28be3..b28c25ec1 100644 --- a/readme.md +++ b/readme.md @@ -35,7 +35,7 @@ architecture combinations: * Windows (Windows XP or greater) - x86 and x86_64 * Linux (most, if not all, distributions) - x86, x86_64, ppc64 and armv6l - * Mac OS X (10.04 or greater) - x86, x86_64 and ppc64 + * Mac OS X (10.04 or greater) - x86, x86_64, ppc64 and Apple Silicon (based on the ARM64 architecture) More platforms are supported, however, they are not tested regularly and they may not be as stable as the above-listed platforms. From 72fa5833adac590e3c78e2b774cd33f28827594b Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 2 Sep 2021 13:41:14 +0800 Subject: [PATCH 2477/3768] fix #9778 (pairs iterator calling a helper proc with tuple return type will cut the iterator yield into half) (#18767) * test someSymFromImportTable * Update compiler/lookups.nim * test nkTupleConstr * use isConstExpr * add tests * add comments and todo * use todo --- compiler/transf.nim | 55 +++++++-- tests/stdlib/tyield.nim | 256 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 300 insertions(+), 11 deletions(-) create mode 100644 tests/stdlib/tyield.nim diff --git a/compiler/transf.nim b/compiler/transf.nim index fb59887b0..80794b581 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -364,7 +364,7 @@ proc transformYield(c: PTransf, n: PNode): PNode = if e.typ.isNil: return result # can happen in nimsuggest for unknown reasons if c.transCon.forStmt.len != 3: e = skipConv(e) - if e.kind in {nkPar, nkTupleConstr}: + if e.kind == nkTupleConstr: for i in 0.. 0: - var changeNode = result[0] - changeNode.info = c.transCon.forStmt.info - for i, child in changeNode: - child.info = changeNode.info + for idx in 0 ..< result.len: + var changeNode = result[idx] + changeNode.info = c.transCon.forStmt.info + for i, child in changeNode: + child.info = changeNode.info proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PNode = result = transformSons(c, n) diff --git a/tests/stdlib/tyield.nim b/tests/stdlib/tyield.nim new file mode 100644 index 000000000..85be97365 --- /dev/null +++ b/tests/stdlib/tyield.nim @@ -0,0 +1,256 @@ +discard """ + targets: "c cpp js" +""" + +import std/[sugar, algorithm] + +block: + var x = @[(6.0, 6, '6'), + (5.0, 5, '5'), + (4.0, 4, '4'), + (3.0, 3, '3'), + (2.0, 2, '2'), + (1.0, 1, '1')] + + let y = x.reversed + + block: + let res = collect: + for (f, i, c) in x: + (f, i, c) + + doAssert res == x + + iterator popAscending[T](q: var seq[T]): T = + while q.len > 0: yield q.pop + + block: + var res = collect: + for f, i, c in popAscending(x): + (f, i, c) + + doAssert res == y + + let z = reversed(res) + let res2 = collect: + for (f, i, c) in popAscending(res): + (f, i, c) + + doAssert res2 == z + + +block: + var visits = 0 + block: + proc bar(): (int, int) = + inc visits + (visits, visits) + + iterator foo(): (int, int) = + yield bar() + + for a, b in foo(): + doAssert a == b + + doAssert visits == 1 + + block: + proc iterAux(a: seq[int], i: var int): (int, string) = + result = (a[i], $a[i]) + inc i + + iterator pairs(a: seq[int]): (int, string) = + var i = 0 + while i < a.len: + yield iterAux(a, i) + + var x = newSeq[int](10) + for i in 0 ..< x.len: + x[i] = i + + let res = collect: + for k, v in x: + (k, v) + + let expected = collect: + for i in 0 ..< x.len: + (i, $i) + + doAssert res == expected + + block: + proc bar(): (int, int, int) = + inc visits + (visits, visits, visits) + + iterator foo(): (int, int, int) = + yield bar() + + for a, b, c in foo(): + doAssert a == b + + doAssert visits == 2 + + + block: + + proc bar(): int = + inc visits + visits + + proc car(): int = + inc visits + visits + + iterator foo(): (int, int) = + yield (bar(), car()) + yield (bar(), car()) + + for a, b in foo(): + doAssert b == a + 1 + + doAssert visits == 6 + + + block: + proc bar(): (int, int) = + inc visits + (visits, visits) + + proc t2(): int = 99 + + iterator foo(): (int, int) = + yield (12, t2()) + yield bar() + + let res = collect: + for (a, b) in foo(): + (a, b) + + doAssert res == @[(12, 99), (7, 7)] + doAssert visits == 7 + + block: + proc bar(): (int, int) = + inc visits + (visits, visits) + + proc t2(): int = 99 + + iterator foo(): (int, int) = + yield ((12, t2())) + yield (bar()) + + let res = collect: + for (a, b) in foo(): + (a, b) + + doAssert res == @[(12, 99), (8, 8)] + doAssert visits == 8 + + block: + proc bar(): (int, int) = + inc visits + (visits, visits) + + proc t1(): int = 99 + proc t2(): int = 99 + + iterator foo(): (int, int) = + yield (t1(), t2()) + yield bar() + + let res = collect: + for a, b in foo(): + (a, b) + + doAssert res == @[(99, 99), (9, 9)] + doAssert visits == 9 + + + block: + proc bar(): ((int, int), string) = + inc visits + ((visits, visits), $visits) + + proc t2(): int = 99 + + iterator foo(): ((int, int), string) = + yield ((1, 2), $t2()) + yield bar() + + let res = collect: + for a, b in foo(): + (a, b) + + doAssert res == @[((1, 2), "99"), ((10, 10), "10")] + doAssert visits == 10 + + + block: + proc bar(): (int, int) = + inc visits + (visits, visits) + + iterator foo(): (int, int) = + yield (for i in 0 ..< 10: discard bar(); bar()) + yield (bar()) + + let res = collect: + for (a, b) in foo(): + (a, b) + + doAssert res == @[(21, 21), (22, 22)] + + block: + proc bar(): (int, int) = + inc visits + (visits, visits) + + proc t2(): int = 99 + + iterator foo(): (int, int) = + yield if true: bar() else: (t2(), t2()) + yield (bar()) + + let res = collect: + for a, b in foo(): + (a, b) + + doAssert res == @[(23, 23), (24, 24)] + + +block: + iterator foo(): (int, int, int) = + var time = 777 + yield (1, time, 3) + + let res = collect: + for a, b, c in foo(): + (a, b, c) + + doAssert res == @[(1, 777, 3)] + +block: + iterator foo(): (int, int, int) = + var time = 777 + yield (1, time, 3) + + let res = collect: + for t in foo(): + (t[0], t[1], t[2]) + + doAssert res == @[(1, 777, 3)] + + +block: + proc bar(): (int, int, int) = + (1, 2, 3) + iterator foo(): (int, int, int) = + yield bar() + + let res = collect: + for a, b, c in foo(): + (a, b, c) + + doAssert res == @[(1, 2, 3)] From e0ef859130f429df1891e31a85955daa753346b4 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 2 Sep 2021 12:10:14 +0200 Subject: [PATCH 2478/3768] strict effects (#18777) * fixes #17369 * megatest is green for --cpu:arm64 * docgen output includes more tags/raises * implemented 'effectsOf' * algorithm.nim: uses new effectsOf annotation * closes #18376 * closes #17475 * closes #13905 * allow effectsOf: [a, b] * added a test case * parameters that are not ours cannot be declared as .effectsOf * documentation * manual: added the 'sort' example * bootstrap with the new better options --- changelog.md | 17 ++ compiler/ast.nim | 6 +- compiler/ccgcalls.nim | 4 +- compiler/commands.nim | 3 +- compiler/condsyms.nim | 1 + compiler/docgen.nim | 7 +- compiler/lineinfos.nim | 2 + compiler/nim.cfg | 5 + compiler/options.nim | 3 +- compiler/pragmas.nim | 30 ++- compiler/sem.nim | 7 +- compiler/semcall.nim | 2 + compiler/semdata.nim | 4 +- compiler/sempass2.nim | 116 +++++--- compiler/semstmts.nim | 9 +- compiler/types.nim | 17 +- compiler/wordrecg.nim | 2 +- doc/manual.rst | 252 ++++++++++-------- lib/pure/algorithm.nim | 23 +- lib/pure/times.nim | 2 +- lib/std/private/globs.nim | 7 +- lib/system/io.nim | 2 +- lib/system/nimscript.nim | 6 +- .../expected/index.html | 2 +- nimdoc/testproject/expected/testproject.html | 10 +- tests/effects/teffects1.nim | 3 +- tests/effects/teffects2.nim | 3 +- tests/effects/teffects7.nim | 4 +- tests/effects/teffects8.nim | 3 +- tests/effects/tstrict_effects.nim | 27 ++ tests/effects/tstrict_effects2.nim | 28 ++ tests/effects/tstrict_effects3.nim | 17 ++ tests/effects/tstrict_effects_sort.nim | 27 ++ tests/pragmas/tuserpragma2.nim | 3 +- tests/stdlib/tstdlib_various.nim | 31 +-- 35 files changed, 482 insertions(+), 203 deletions(-) create mode 100644 tests/effects/tstrict_effects.nim create mode 100644 tests/effects/tstrict_effects2.nim create mode 100644 tests/effects/tstrict_effects3.nim create mode 100644 tests/effects/tstrict_effects_sort.nim diff --git a/changelog.md b/changelog.md index 65ae61833..10824cfc6 100644 --- a/changelog.md +++ b/changelog.md @@ -427,6 +427,23 @@ - On embedded devices `malloc` can now be used instead of `mmap` via `-d:nimAllocPagesViaMalloc`. This is only supported for `--gc:orc` or `--gc:arc`. +- The effect system was refined and there is a new `.effectsOf` annotation that does + explicitly what was previously done implicitly. See the manual for details. + To write code that is portable with older Nim versions, use this idiom: + +```nim + +when defined(nimHasEffectsOf): + {.experimental: "strictEffects".} +else: + {.pragma: effectsOf.} + +proc mysort(s: seq; cmp: proc(a, b: T): int) {.effectsOf: cmp.} + +``` + + To enable the new effect system, use --experimental:strictEffects. + ## Compiler changes diff --git a/compiler/ast.nim b/compiler/ast.nim index 5893a5671..50d048edd 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -229,7 +229,7 @@ type TNodeKinds* = set[TNodeKind] type - TSymFlag* = enum # 47 flags! + TSymFlag* = enum # 48 flags! sfUsed, # read access of sym (for warnings) or simply used sfExported, # symbol is exported from module sfFromGeneric, # symbol is instantiation of a generic; this is needed @@ -299,6 +299,7 @@ type sfUsedInFinallyOrExcept # symbol is used inside an 'except' or 'finally' sfSingleUsedTemp # For temporaries that we know will only be used once sfNoalias # 'noalias' annotation, means C's 'restrict' + sfEffectsDelayed # an 'effectsDelayed' parameter TSymFlags* = set[TSymFlag] @@ -568,6 +569,7 @@ type # sizeof, alignof, offsetof at CT tfExplicitCallConv tfIsConstructor + tfEffectSystemWorkaround TTypeFlags* = set[TTypeFlag] @@ -1781,7 +1783,7 @@ proc containsNode*(n: PNode, kinds: TNodeKinds): bool = proc hasSubnodeWith*(n: PNode, kind: TNodeKind): bool = case n.kind - of nkEmpty..nkNilLit: result = n.kind == kind + of nkEmpty..nkNilLit, nkFormalParams: result = n.kind == kind else: for i in 0.. pragma: arg result.transitionSonsKind(n.kind) +proc processEffectsOf(c: PContext, n: PNode; owner: PSym) = + proc processParam(c: PContext; n: PNode) = + let r = c.semExpr(c, n) + if r.kind == nkSym and r.sym.kind == skParam: + if r.sym.owner == owner: + incl r.sym.flags, sfEffectsDelayed + else: + localError(c.config, n.info, errGenerated, "parameter cannot be declared as .effectsOf") + else: + localError(c.config, n.info, errGenerated, "parameter name expected") + + if n.kind notin nkPragmaCallKinds or n.len != 2: + localError(c.config, n.info, errGenerated, "parameter name expected") + else: + let it = n[1] + if it.kind in {nkCurly, nkBracket}: + for x in items(it): processParam(c, x) + else: + processParam(c, it) + proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, validPragmas: TSpecialWords, comesFromPush, isStatement: bool): bool = @@ -895,6 +915,8 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of wNoalias: noVal(c, it) incl(sym.flags, sfNoalias) + of wEffectsOf: + processEffectsOf(c, it, sym) of wThreadVar: noVal(c, it) incl(sym.flags, {sfThread, sfGlobal}) diff --git a/compiler/sem.nim b/compiler/sem.nim index 804325e56..24709cf21 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -400,9 +400,10 @@ when not defined(nimHasSinkInference): include hlo, seminst, semcall proc resetSemFlag(n: PNode) = - excl n.flags, nfSem - for i in 0.. 0: rr = rr.lastSon - localError(g.config, r.info, errGenerated, renderTree(rr) & " " & msg & typeToString(r.typ)) + message(g.config, r.info, if emitWarnings: warnEffect else: errGenerated, + renderTree(rr) & " " & msg & typeToString(r.typ)) popInfoContext(g.config) # hint about unnecessarily listed exception types: if hints: @@ -1258,11 +1293,11 @@ proc checkMethodEffects*(g: ModuleGraph; disp, branch: PSym) = let p = disp.ast[pragmasPos] let raisesSpec = effectSpec(p, wRaises) if not isNil(raisesSpec): - checkRaisesSpec(g, raisesSpec, actual[exceptionEffects], + checkRaisesSpec(g, false, raisesSpec, actual[exceptionEffects], "can raise an unlisted exception: ", hints=off, subtypeRelation) let tagsSpec = effectSpec(p, wTags) if not isNil(tagsSpec): - checkRaisesSpec(g, tagsSpec, actual[tagEffects], + checkRaisesSpec(g, false, tagsSpec, actual[tagEffects], "can have an unlisted effect: ", hints=off, subtypeRelation) if sfThread in disp.flags and notGcSafe(branch.typ): localError(g.config, branch.info, "base method is GC-safe, but '$1' is not" % @@ -1283,7 +1318,7 @@ proc checkMethodEffects*(g: ModuleGraph; disp, branch: PSym) = "base method has lock level $1, but dispatcher has $2" % [$branch.typ.lockLevel, $disp.typ.lockLevel]) -proc setEffectsForProcType*(g: ModuleGraph; t: PType, n: PNode) = +proc setEffectsForProcType*(g: ModuleGraph; t: PType, n: PNode; s: PSym = nil) = var effects = t.n[0] if t.kind != tyProc or effects.kind != nkEffectList: return if n.kind != nkEmpty: @@ -1292,9 +1327,14 @@ proc setEffectsForProcType*(g: ModuleGraph; t: PType, n: PNode) = let raisesSpec = effectSpec(n, wRaises) if not isNil(raisesSpec): effects[exceptionEffects] = raisesSpec + elif s != nil and (s.magic != mNone or {sfImportc, sfExportc} * s.flags == {sfImportc}): + effects[exceptionEffects] = newNodeI(nkArgList, effects.info) + let tagsSpec = effectSpec(n, wTags) if not isNil(tagsSpec): effects[tagEffects] = tagsSpec + elif s != nil and (s.magic != mNone or {sfImportc, sfExportc} * s.flags == {sfImportc}): + effects[tagEffects] = newNodeI(nkArgList, effects.info) let requiresSpec = propSpec(n, wRequires) if not isNil(requiresSpec): @@ -1304,15 +1344,21 @@ proc setEffectsForProcType*(g: ModuleGraph; t: PType, n: PNode) = effects[ensuresEffects] = ensuresSpec effects[pragmasEffects] = n + if s != nil and s.magic != mNone: + if s.magic != mEcho: + t.flags.incl tfNoSideEffect -proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects; c: PContext) = +proc rawInitEffects(g: ModuleGraph; effects: PNode) = newSeq(effects.sons, effectListLen) - effects[exceptionEffects] = newNodeI(nkArgList, s.info) - effects[tagEffects] = newNodeI(nkArgList, s.info) + effects[exceptionEffects] = newNodeI(nkArgList, effects.info) + effects[tagEffects] = newNodeI(nkArgList, effects.info) effects[requiresEffects] = g.emptyNode effects[ensuresEffects] = g.emptyNode effects[pragmasEffects] = g.emptyNode +proc initEffects(g: ModuleGraph; effects: PNode; s: PSym; t: var TEffects; c: PContext) = + rawInitEffects(g, effects) + t.exc = effects[exceptionEffects] t.tags = effects[tagEffects] t.owner = s @@ -1341,10 +1387,14 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) = if effects.kind != nkEffectList: return # effects already computed? if not s.hasRealBody: return - if effects.len == effectListLen: return + let emitWarnings = tfEffectSystemWorkaround in s.typ.flags + if effects.len == effectListLen and not emitWarnings: return + + var inferredEffects = newNodeI(nkEffectList, s.info) var t: TEffects - initEffects(g, effects, s, t, c) + initEffects(g, inferredEffects, s, t, c) + rawInitEffects g, effects track(t, body) if s.kind != skMacro: @@ -1369,17 +1419,21 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) = let p = s.ast[pragmasPos] let raisesSpec = effectSpec(p, wRaises) if not isNil(raisesSpec): - checkRaisesSpec(g, raisesSpec, t.exc, "can raise an unlisted exception: ", + checkRaisesSpec(g, emitWarnings, raisesSpec, t.exc, "can raise an unlisted exception: ", hints=on, subtypeRelation, hintsArg=s.ast[0]) # after the check, use the formal spec: effects[exceptionEffects] = raisesSpec + else: + effects[exceptionEffects] = t.exc let tagsSpec = effectSpec(p, wTags) if not isNil(tagsSpec): - checkRaisesSpec(g, tagsSpec, t.tags, "can have an unlisted effect: ", + checkRaisesSpec(g, emitWarnings, tagsSpec, t.tags, "can have an unlisted effect: ", hints=off, subtypeRelation) # after the check, use the formal spec: effects[tagEffects] = tagsSpec + else: + effects[tagEffects] = t.tags let requiresSpec = propSpec(p, wRequires) if not isNil(requiresSpec): diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 14dc89781..df0e2778b 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1953,6 +1953,10 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, if not hasProto: implicitPragmas(c, s, n.info, validPragmas) + if n[pragmasPos].kind != nkEmpty: + setEffectsForProcType(c.graph, s.typ, n[pragmasPos], s) + s.typ.flags.incl tfEffectSystemWorkaround + # To ease macro generation that produce forwarded .async procs we now # allow a bit redundancy in the pragma declarations. The rule is # a prototype's pragma list must be a superset of the current pragma @@ -2211,8 +2215,9 @@ proc evalInclude(c: PContext, n: PNode): PNode = incMod(c, n, it, result) proc setLine(n: PNode, info: TLineInfo) = - for i in 0..`_ pretends to be free of side effects +so that it can be used for debugging routines marked as `noSideEffect`. + +`func` is syntactic sugar for a proc with no side effects: + +.. code-block:: nim + func `+` (x, y: int): int + + +To override the compiler's side effect analysis a `{.noSideEffect.}` +`cast` pragma block can be used: + +.. code-block:: nim + + func f() = + {.cast(noSideEffect).}: + echo "test" + +**Side effects are usually inferred. The inference for side effects is +analogous to the inference for exception tracking.** + + +GC safety effect +---------------- + +We call a proc `p` `GC safe`:idx: when it doesn't access any global variable +that contains GC'ed memory (`string`, `seq`, `ref` or a closure) either +directly or indirectly through a call to a GC unsafe proc. + +**The GC safety property is usually inferred. The inference for GC safety is +analogous to the inference for exception tracking.** + +The `gcsafe`:idx: annotation can be used to mark a proc to be gcsafe, +otherwise this property is inferred by the compiler. Note that `noSideEffect` +implies `gcsafe`. + +Routines that are imported from C are always assumed to be `gcsafe`. + +To override the compiler's gcsafety analysis a `{.cast(gcsafe).}` pragma block can +be used: + +.. code-block:: nim + + var + someGlobal: string = "some string here" + perThread {.threadvar.}: string + + proc setPerThread() = + {.cast(gcsafe).}: + deepCopy(perThread, someGlobal) + + +See also: + +- `Shared heap memory management `_. + + Effects pragma -------------- @@ -6366,55 +6486,6 @@ This pragma can also take in an optional warning string to relay to developers. proc thing(x: bool) {.deprecated: "use thong instead".} -noSideEffect pragma -------------------- - -The `noSideEffect` pragma is used to mark a proc/iterator that can have only -side effects through parameters. This means that the proc/iterator only changes locations that are -reachable from its parameters and the return value only depends on the -parameters. If none of its parameters have the type `var`, `ref`, `ptr`, `cstring`, or `proc`, -then no locations are modified. - -It is a static error to mark a proc/iterator to have no side effect if the compiler cannot verify this. - -As a special semantic rule, the built-in `debugEcho -`_ pretends to be free of side effects -so that it can be used for debugging routines marked as `noSideEffect`. - -`func` is syntactic sugar for a proc with no side effects: - -.. code-block:: nim - func `+` (x, y: int): int - - -To override the compiler's side effect analysis a `{.noSideEffect.}` -`cast` pragma block can be used: - -.. code-block:: nim - - func f() = - {.cast(noSideEffect).}: - echo "test" - -When a `noSideEffect` proc has proc params `bar`, whether it can be used inside a `noSideEffect` context -depends on what the compiler knows about `bar`: - -.. code-block:: nim - :test: "nim c $1" - - func foo(bar: proc(): int): int = bar() - var count = 0 - proc fn1(): int = 1 - proc fn2(): int = (count.inc; count) - func fun1() = discard foo(fn1) # ok because fn1 is inferred as `func` - # func fun2() = discard foo(fn2) # would give: Error: 'fun2' can have side effects - - # with callbacks, the compiler is conservative, ie that bar will have side effects - var foo2: type(foo) = foo - func main() = - discard foo(fn1) # ok - # discard foo2(fn1) # now this errors - compileTime pragma ------------------ @@ -7861,6 +7932,10 @@ collected) heap, and sharing of memory is restricted to global variables. This helps to prevent race conditions. GC efficiency is improved quite a lot, because the GC never has to stop other threads and see what they reference. +The only way to create a thread is via `spawn` or +`createThread`. The invoked proc must not use `var` parameters nor must +any of its parameters contain a `ref` or `closure` type. This enforces +the *no heap sharing restriction*. Thread pragma ------------- @@ -7875,43 +7950,6 @@ A thread proc is passed to `createThread` or `spawn` and invoked indirectly; so the `thread` pragma implies `procvar`. -GC safety ---------- - -We call a proc `p` `GC safe`:idx: when it doesn't access any global variable -that contains GC'ed memory (`string`, `seq`, `ref` or a closure) either -directly or indirectly through a call to a GC unsafe proc. - -The `gcsafe`:idx: annotation can be used to mark a proc to be gcsafe, -otherwise this property is inferred by the compiler. Note that `noSideEffect` -implies `gcsafe`. The only way to create a thread is via `spawn` or -`createThread`. The invoked proc must not use `var` parameters nor must -any of its parameters contain a `ref` or `closure` type. This enforces -the *no heap sharing restriction*. - -Routines that are imported from C are always assumed to be `gcsafe`. -To disable the GC-safety checking the `--threadAnalysis:off`:option: command-line -switch can be used. This is a temporary workaround to ease the porting effort -from old code to the new threading model. - -To override the compiler's gcsafety analysis a `{.cast(gcsafe).}` pragma block can -be used: - -.. code-block:: nim - - var - someGlobal: string = "some string here" - perThread {.threadvar.}: string - - proc setPerThread() = - {.cast(gcsafe).}: - deepCopy(perThread, someGlobal) - - -See also: - -- `Shared heap memory management `_. - Threadvar pragma ---------------- diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim index c96f599e8..1ddcc9843 100644 --- a/lib/pure/algorithm.nim +++ b/lib/pure/algorithm.nim @@ -147,8 +147,13 @@ proc reversed*[T](a: openArray[T], first: Natural, last: int): seq[T] {.inline, deprecated: "use: `reversed(toOpenArray(a, first, last))`".} = reversed(toOpenArray(a, first, last)) +when defined(nimHasEffectsOf): + {.experimental: "strictEffects".} +else: + {.pragma: effectsOf.} + proc binarySearch*[T, K](a: openArray[T], key: K, - cmp: proc (x: T, y: K): int {.closure.}): int = + cmp: proc (x: T, y: K): int {.closure.}): int {.effectsOf: cmp.} = ## Binary search for `key` in `a`. Return the index of `key` or -1 if not found. ## Assumes that `a` is sorted according to `cmp`. ## @@ -210,7 +215,7 @@ const onlySafeCode = true proc lowerBound*[T, K](a: openArray[T], key: K, - cmp: proc(x: T, k: K): int {.closure.}): int = + cmp: proc(x: T, k: K): int {.closure.}): int {.effectsOf: cmp.} = ## Returns the index of the first element in `a` that is not less than ## (i.e. greater or equal to) `key`, or last if no such element is found. ## In other words if you have a sorted sequence and you call @@ -260,7 +265,7 @@ proc lowerBound*[T](a: openArray[T], key: T): int = lowerBound(a, key, cmp[T]) ## * `upperBound proc<#upperBound,openArray[T],T>`_ proc upperBound*[T, K](a: openArray[T], key: K, - cmp: proc(x: T, k: K): int {.closure.}): int = + cmp: proc(x: T, k: K): int {.closure.}): int {.effectsOf: cmp.} = ## Returns the index of the first element in `a` that is greater than ## `key`, or last if no such element is found. ## In other words if you have a sorted sequence and you call @@ -318,7 +323,7 @@ template `<-`(a, b) = copyMem(addr(a), addr(b), sizeof(T)) proc mergeAlt[T](a, b: var openArray[T], lo, m, hi: int, - cmp: proc (x, y: T): int {.closure.}, order: SortOrder) = + cmp: proc (x, y: T): int {.closure.}, order: SortOrder) {.effectsOf: cmp.} = # Optimization: If max(left) <= min(right) there is nothing to do! # 1 2 3 4 ## 5 6 7 8 # -> O(n) for sorted arrays. @@ -358,7 +363,7 @@ proc mergeAlt[T](a, b: var openArray[T], lo, m, hi: int, func sort*[T](a: var openArray[T], cmp: proc (x, y: T): int {.closure.}, - order = SortOrder.Ascending) = + order = SortOrder.Ascending) {.effectsOf: cmp.} = ## Default Nim sort (an implementation of merge sort). The sorting ## is guaranteed to be stable (that is, equal elements stay in the same order) ## and the worst case is guaranteed to be O(n log n). @@ -420,7 +425,7 @@ proc sort*[T](a: var openArray[T], order = SortOrder.Ascending) = sort[T](a, ## * `sortedByIt template<#sortedByIt.t,untyped,untyped>`_ proc sorted*[T](a: openArray[T], cmp: proc(x, y: T): int {.closure.}, - order = SortOrder.Ascending): seq[T] = + order = SortOrder.Ascending): seq[T] {.effectsOf: cmp.} = ## Returns `a` sorted by `cmp` in the specified `order`. ## ## **See also:** @@ -497,7 +502,7 @@ template sortedByIt*(seq1, op: untyped): untyped = func isSorted*[T](a: openArray[T], cmp: proc(x, y: T): int {.closure.}, - order = SortOrder.Ascending): bool = + order = SortOrder.Ascending): bool {.effectsOf: cmp.} = ## Checks to see whether `a` is already sorted in `order` ## using `cmp` for the comparison. The parameters are identical ## to `sort`. Requires O(n) time. @@ -545,7 +550,7 @@ proc isSorted*[T](a: openArray[T], order = SortOrder.Ascending): bool = proc merge*[T]( result: var seq[T], x, y: openArray[T], cmp: proc(x, y: T): int {.closure.} -) {.since: (1, 5, 1).} = +) {.since: (1, 5, 1), effectsOf: cmp.} = ## Merges two sorted `openArray`. `x` and `y` are assumed to be sorted. ## If you do not wish to provide your own `cmp`, ## you may use `system.cmp` or instead call the overloaded @@ -638,7 +643,7 @@ proc product*[T](x: openArray[seq[T]]): seq[seq[T]] = ## Produces the Cartesian product of the array. ## Every element of the result is a combination of one element from each seq in `x`, ## with the ith element coming from `x[i]`. - ## + ## ## .. warning:: complexity may explode. runnableExamples: assert product(@[@[1], @[2]]) == @[@[1, 2]] diff --git a/lib/pure/times.nim b/lib/pure/times.nim index b2a22250d..b41bb28b5 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -1333,7 +1333,7 @@ proc now*(): DateTime {.tags: [TimeEffect], benign.} = ## `cpuTime` instead, depending on the use case. getTime().local -proc dateTime*(year: int, month: Month, monthday: MonthdayRange, +proc dateTime*(year: int, month: Month, monthday: MonthdayRange, hour: HourRange = 0, minute: MinuteRange = 0, second: SecondRange = 0, nanosecond: NanosecondRange = 0, zone: Timezone = local()): DateTime = diff --git a/lib/std/private/globs.nim b/lib/std/private/globs.nim index b3726c9c3..190316f93 100644 --- a/lib/std/private/globs.nim +++ b/lib/std/private/globs.nim @@ -8,13 +8,18 @@ import os when defined(windows): from strutils import replace +when defined(nimHasEffectsOf): + {.experimental: "strictEffects".} +else: + {.pragma: effectsOf.} + type PathEntry* = object kind*: PathComponent path*: string iterator walkDirRecFilter*(dir: string, follow: proc(entry: PathEntry): bool = nil, - relative = false, checkDir = true): PathEntry {.tags: [ReadDirEffect].} = + relative = false, checkDir = true): PathEntry {.tags: [ReadDirEffect], effectsOf: follow.} = ## Improved `os.walkDirRec`. #[ note: a yieldFilter isn't needed because caller can filter at call site, without diff --git a/lib/system/io.nim b/lib/system/io.nim index d8b2ac741..59f070f73 100644 --- a/lib/system/io.nim +++ b/lib/system/io.nim @@ -78,7 +78,7 @@ proc c_fputs(c: cstring, f: File): cint {. proc c_fgets(c: cstring, n: cint, f: File): cstring {. importc: "fgets", header: "", tags: [ReadIOEffect].} proc c_fgetc(stream: File): cint {. - importc: "fgetc", header: "", tags: [ReadIOEffect].} + importc: "fgetc", header: "", tags: [].} proc c_ungetc(c: cint, f: File): cint {. importc: "ungetc", header: "", tags: [].} proc c_putc(c: cint, stream: File): cint {. diff --git a/lib/system/nimscript.nim b/lib/system/nimscript.nim index 9ece10e3e..b4554d778 100644 --- a/lib/system/nimscript.nim +++ b/lib/system/nimscript.nim @@ -261,7 +261,7 @@ proc cpDir*(`from`, to: string) {.raises: [OSError].} = checkOsError() proc exec*(command: string) {. - raises: [OSError], tags: [ExecIOEffect].} = + raises: [OSError], tags: [ExecIOEffect, WriteIOEffect].} = ## Executes an external process. If the external process terminates with ## a non-zero exit code, an OSError exception is raised. ## @@ -274,7 +274,7 @@ proc exec*(command: string) {. checkOsError() proc exec*(command: string, input: string, cache = "") {. - raises: [OSError], tags: [ExecIOEffect].} = + raises: [OSError], tags: [ExecIOEffect, WriteIOEffect].} = ## Executes an external process. If the external process terminates with ## a non-zero exit code, an OSError exception is raised. log "exec: " & command: @@ -284,7 +284,7 @@ proc exec*(command: string, input: string, cache = "") {. echo output proc selfExec*(command: string) {. - raises: [OSError], tags: [ExecIOEffect].} = + raises: [OSError], tags: [ExecIOEffect, WriteIOEffect].} = ## Executes an external command with the current nim/nimble executable. ## `Command` must not contain the "nim " part. let c = selfExe() & " " & command diff --git a/nimdoc/test_out_index_dot_html/expected/index.html b/nimdoc/test_out_index_dot_html/expected/index.html index 3499b5326..458a0394f 100644 --- a/nimdoc/test_out_index_dot_html/expected/index.html +++ b/nimdoc/test_out_index_dot_html/expected/index.html @@ -122,7 +122,7 @@ window.addEventListener('DOMContentLoaded', main);

    Procs

    -
    proc foo() {....raises: [], tags: [].}
    +
    proc foo() {....raises: [], tags: [WriteIOEffect].}
    I do foo diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html index d98b1b00f..dbacb57a7 100644 --- a/nimdoc/testproject/expected/testproject.html +++ b/nimdoc/testproject/expected/testproject.html @@ -654,7 +654,7 @@ This is deprecated with a message.
    proc c_nonexistent(frmt: cstring): cint {.importc: "nonexistent",
    -    header: "<stdio.h>", varargs, discardable.}
    + header: "<stdio.h>", varargs, discardable, ...raises: [], tags: [].}
    @@ -663,7 +663,7 @@ This is deprecated with a message.
    proc c_printf(frmt: cstring): cint {.importc: "printf", header: "<stdio.h>",
    -                                     varargs, discardable.}
    + varargs, discardable, ...raises: [], tags: [].}
    the c printf. etc. @@ -690,7 +690,8 @@ came form utils but should be shown where
    -
    proc low2[T: Ordinal | enum | range](x: T): T {.magic: "Low", noSideEffect.}
    +
    proc low2[T: Ordinal | enum | range](x: T): T {.magic: "Low", noSideEffect,
    +    ...raises: [], tags: [].}

    Returns the lowest possible value of an ordinal value x. As a special semantic rule, x may also be a type identifier.

    @@ -704,7 +705,8 @@ came form utils but should be shown where
    -
    proc low[T: Ordinal | enum | range](x: T): T {.magic: "Low", noSideEffect.}
    +
    proc low[T: Ordinal | enum | range](x: T): T {.magic: "Low", noSideEffect,
    +    ...raises: [], tags: [].}

    Returns the lowest possible value of an ordinal value x. As a special semantic rule, x may also be a type identifier.

    diff --git a/tests/effects/teffects1.nim b/tests/effects/teffects1.nim index 66d6a3518..82efefe77 100644 --- a/tests/effects/teffects1.nim +++ b/tests/effects/teffects1.nim @@ -6,7 +6,7 @@ teffects1.nim(22, 29) Hint: 'lier' cannot raise 'IO2Error' [XCannotRaiseY] teffects1.nim(38, 21) Error: type mismatch: got but expected 'MyProcType = proc (x: int): string{.closure.}' .raise effects differ''' """ - +{.push warningAsError[Effect]: on.} type TObj {.pure, inheritable.} = object TObjB = object of TObj @@ -41,3 +41,4 @@ type mismatch: got but ]# {.pop.} +{.pop.} diff --git a/tests/effects/teffects2.nim b/tests/effects/teffects2.nim index e4b50aba5..ec3c0a887 100644 --- a/tests/effects/teffects2.nim +++ b/tests/effects/teffects2.nim @@ -2,7 +2,7 @@ discard """ errormsg: "can raise an unlisted exception: ref IOError" line: 19 """ - +{.push warningAsError[Effect]: on.} type TObj = object {.pure, inheritable.} TObjB = object of TObj @@ -17,3 +17,4 @@ proc lier(): int {.raises: [IOError].} = proc forw: int = raise newException(IOError, "arg") +{.pop.} diff --git a/tests/effects/teffects7.nim b/tests/effects/teffects7.nim index 73865b18d..9b7fbf5f0 100644 --- a/tests/effects/teffects7.nim +++ b/tests/effects/teffects7.nim @@ -2,7 +2,7 @@ discard """ errormsg: "can raise an unlisted exception: ref ValueError" line: 10 """ - +{.push warningAsError[Effect]: on.} proc foo() {.raises: [].} = try: discard @@ -12,3 +12,5 @@ proc foo() {.raises: [].} = discard foo() + +{.pop.} diff --git a/tests/effects/teffects8.nim b/tests/effects/teffects8.nim index fb3c088d6..359b3a1df 100644 --- a/tests/effects/teffects8.nim +++ b/tests/effects/teffects8.nim @@ -2,7 +2,7 @@ discard """ errormsg: "can raise an unlisted exception: Exception" line: 10 """ - +{.push warningAsError[Effect]: on.} proc foo() {.raises: [].} = try: discard @@ -10,3 +10,4 @@ proc foo() {.raises: [].} = raise foo() +{.pop.} diff --git a/tests/effects/tstrict_effects.nim b/tests/effects/tstrict_effects.nim new file mode 100644 index 000000000..eee8fb71a --- /dev/null +++ b/tests/effects/tstrict_effects.nim @@ -0,0 +1,27 @@ +discard """ + errormsg: "s1 can raise an unlisted exception: CatchableError" + line: 27 +""" + +{.push warningAsError[Effect]: on.} +{.experimental: "strictEffects".} + +# bug #18376 + +{.push raises: [Defect].} +type Call = proc (x: int): int {.gcsafe, raises: [Defect, CatchableError].} + +type Bar* = object + foo*: Call + +proc passOn*(x: Call) = discard + +proc barCal(b: var Bar, s: string, s1: Call) = + #compiler complains that his line can throw CatchableError + passOn s1 + + +proc passOnB*(x: Call) {.effectsOf: x.} = discard + +proc barCal2(b: var Bar, s: string, s1: Call) = + passOnB s1 diff --git a/tests/effects/tstrict_effects2.nim b/tests/effects/tstrict_effects2.nim new file mode 100644 index 000000000..acc0a0540 --- /dev/null +++ b/tests/effects/tstrict_effects2.nim @@ -0,0 +1,28 @@ +discard """ + errormsg: "can raise an unlisted exception: Exception" + line: 23 +""" + +{.push warningAsError[Effect]: on.} +{.experimental: "strictEffects".} + +# bug #13905 + +proc atoi(v: cstring): cint {.importc: "atoi", cdecl, raises: [].} + +type Conv = proc(v: cstring): cint {.cdecl, raises: [].} + +var x: Conv = atoi + +# bug #17475 + +type + Callback = proc() + +proc f(callback: Callback) {.raises: [].} = + callback() + +proc main = + f(proc () = raise newException(IOError, "IO")) + +main() diff --git a/tests/effects/tstrict_effects3.nim b/tests/effects/tstrict_effects3.nim new file mode 100644 index 000000000..1ab15cc80 --- /dev/null +++ b/tests/effects/tstrict_effects3.nim @@ -0,0 +1,17 @@ +discard """ + action: compile +""" + +{.push warningAsError[Effect]: on.} + +{.experimental: "strictEffects".} + +proc fn(a: int, p1, p2: proc()) {.effectsOf: p1.} = + if a == 7: + p1() + if a<0: + raise newException(ValueError, $a) + +proc main() {.raises: [ValueError].} = + fn(1, proc()=discard, proc() = raise newException(IOError, "foo")) +main() diff --git a/tests/effects/tstrict_effects_sort.nim b/tests/effects/tstrict_effects_sort.nim new file mode 100644 index 000000000..8928ed0d3 --- /dev/null +++ b/tests/effects/tstrict_effects_sort.nim @@ -0,0 +1,27 @@ +discard """ + errormsg: "cmpE can raise an unlisted exception: Exception" + line: 27 +""" + +{.push warningAsError[Effect]: on.} + +{.experimental: "strictEffects".} + +import algorithm + +type + MyInt = distinct int + +var toSort = @[MyInt 1, MyInt 2, MyInt 3] + +proc cmpN(a, b: MyInt): int = + cmp(a.int, b.int) + +proc harmless {.raises: [].} = + toSort.sort cmpN + +proc cmpE(a, b: MyInt): int {.raises: [Exception].} = + cmp(a.int, b.int) + +proc harmfull {.raises: [].} = + toSort.sort cmpE diff --git a/tests/pragmas/tuserpragma2.nim b/tests/pragmas/tuserpragma2.nim index ce16c4649..c3f31cd5e 100644 --- a/tests/pragmas/tuserpragma2.nim +++ b/tests/pragmas/tuserpragma2.nim @@ -3,9 +3,10 @@ discard """ file: "tuserpragma2.nim" line: 11 """ - +{.push warningAsError[Effect]: on.} # bug #7216 {.pragma: my_pragma, raises: [].} proc test1 {.my_pragma.} = raise newException(Exception, "msg") +{.pop.} diff --git a/tests/stdlib/tstdlib_various.nim b/tests/stdlib/tstdlib_various.nim index b153fd2ba..bc90d6ef4 100644 --- a/tests/stdlib/tstdlib_various.nim +++ b/tests/stdlib/tstdlib_various.nim @@ -20,12 +20,6 @@ Hi Andreas! How do you feel, Rumpf? @[0, 2, 1] @[0, 1, 2] 055this should be the casehugh@["(", "+", " 1", " 2", ")"] -caught a crash! -caught a crash! -caught a crash! -caught a crash! -caught a crash! -caught a crash! [5] [4, 5] [3, 4, 5] @@ -161,18 +155,21 @@ block tropes: block tsegfaults: - proc main = - try: - var x: ptr int - echo x[] + when not defined(arm64): + var crashes = 0 + proc main = try: - raise newException(ValueError, "not a crash") - except ValueError: - discard - except NilAccessDefect: - echo "caught a crash!" - for i in 0..5: - main() + var x: ptr int + echo x[] + try: + raise newException(ValueError, "not a crash") + except ValueError: + discard + except NilAccessDefect: + inc crashes + for i in 0..5: + main() + assert crashes == 6 From 7c8ea490a2be36d171d362c43212195ea8ce489f Mon Sep 17 00:00:00 2001 From: Tomohiro Date: Thu, 2 Sep 2021 21:12:14 +0900 Subject: [PATCH 2479/3768] Fix initrand to avoid random number sequences overlapping (#18744) * Fix initrand to avoid random number sequences overlapping * Minor fix * Fix compile error on js backend * Disable new test for js backend * Minor fix * tempfiles module uses random.initRand() * Remove unused module import from lib/std/tempfiles.nim * Initialize baseState in initRand() * Run tests/stdlib/trandom.nim from tests/test_nimscript.nims * baseState is initialized only with sysrand.urandom and quit if failed * Add comments --- lib/pure/random.nim | 74 +++++++++++++++++++++++++++++++++------ lib/std/tempfiles.nim | 7 ++-- tests/stdlib/trandom.nim | 32 ++++++++++++++--- tests/test_nimscript.nims | 2 ++ 4 files changed, 95 insertions(+), 20 deletions(-) diff --git a/lib/pure/random.nim b/lib/pure/random.nim index a292386af..b8aeb86e0 100644 --- a/lib/pure/random.nim +++ b/lib/pure/random.nim @@ -108,10 +108,19 @@ when defined(js): a0: 0x69B4C98Cu32, a1: 0xFED1DD30u32) # global for backwards compatibility else: - # racy for multi-threading but good enough for now: - var state = Rand( + const DefaultRandSeed = Rand( a0: 0x69B4C98CB8530805u64, - a1: 0xFED1DD3004688D67CAu64) # global for backwards compatibility + a1: 0xFED1DD3004688D67CAu64) + + # racy for multi-threading but good enough for now: + var state = DefaultRandSeed # global for backwards compatibility + +func isValid(r: Rand): bool {.inline.} = + ## Check whether state of `r` is valid. + ## + ## In `xoroshiro128+`, if all bits of `a0` and `a1` are zero, + ## they are always zero after calling `next(r: var Rand)`. + not (r.a0 == 0 and r.a1 == 0) since (1, 5): template randState*(): untyped = @@ -617,15 +626,28 @@ proc shuffle*[T](x: var openArray[T]) = shuffle(state, x) -when not defined(nimscript) and not defined(standalone): - import times +when not defined(standalone): + when defined(js): + import std/times + else: + when defined(nimscript): + import std/hashes + else: + import std/[hashes, os, sysrand, monotimes] + + when compileOption("threads"): + import locks + var baseSeedLock: Lock + baseSeedLock.initLock + + var baseState: Rand proc initRand(): Rand = - ## Initializes a new Rand state with a seed based on the current time. + ## Initializes a new Rand state. ## ## The resulting state is independent of the default RNG's state. ## - ## **Note:** Does not work for NimScript or the compile-time VM. + ## **Note:** Does not work for the compile-time VM. ## ## See also: ## * `initRand proc<#initRand,int64>`_ that accepts a seed for a new Rand state @@ -635,20 +657,50 @@ when not defined(nimscript) and not defined(standalone): let time = int64(times.epochTime() * 1000) and 0x7fff_ffff result = initRand(time) else: - let now = times.getTime() - result = initRand(convert(Seconds, Nanoseconds, now.toUnix) + now.nanosecond) + proc getRandomState(): Rand = + when defined(nimscript): + result = Rand( + a0: CompileTime.hash.Ui, + a1: CompileDate.hash.Ui) + if not result.isValid: + result = DefaultRandSeed + else: + var urand: array[sizeof(Rand), byte] + + for i in 0 .. 7: + if sysrand.urandom(urand): + copyMem(result.addr, urand[0].addr, sizeof(Rand)) + if result.isValid: + break + + if not result.isValid: + # Don't try to get alternative random values from other source like time or process/thread id, + # because such code would be never tested and is a liability for security. + quit("Failed to initializes baseState in random module as sysrand.urandom doesn't work.") + + when compileOption("threads"): + baseSeedLock.withLock: + if not baseState.isValid: + baseState = getRandomState() + result = baseState + baseState.skipRandomNumbers + else: + if not baseState.isValid: + baseState = getRandomState() + result = baseState + baseState.skipRandomNumbers since (1, 5, 1): export initRand proc randomize*() {.benign.} = ## Initializes the default random number generator with a seed based on - ## the current time. + ## random number source. ## ## This proc only needs to be called once, and it should be called before ## the first usage of procs from this module that use the default RNG. ## - ## **Note:** Does not work for NimScript or the compile-time VM. + ## **Note:** Does not work for the compile-time VM. ## ## **See also:** ## * `randomize proc<#randomize,int64>`_ that accepts a seed diff --git a/lib/std/tempfiles.nim b/lib/std/tempfiles.nim index f1cfd5885..26786d463 100644 --- a/lib/std/tempfiles.nim +++ b/lib/std/tempfiles.nim @@ -17,7 +17,7 @@ See also: * `mkstemp` (posix), refs https://man7.org/linux/man-pages/man3/mkstemp.3.html ]# -import os, random, std/monotimes +import os, random const @@ -107,11 +107,8 @@ var nimTempPathState {.threadvar.}: NimTempPathState template randomPathName(length: Natural): string = var res = newString(length) if not nimTempPathState.isInit: - var time = getMonoTime().ticks - when compileOption("threads"): - time = time xor int64(getThreadId()) nimTempPathState.isInit = true - nimTempPathState.state = initRand(time) + nimTempPathState.state = initRand() for i in 0 ..< length: res[i] = nimTempPathState.state.sample(letters) diff --git a/tests/stdlib/trandom.nim b/tests/stdlib/trandom.nim index 39ccca85b..61e858f86 100644 --- a/tests/stdlib/trandom.nim +++ b/tests/stdlib/trandom.nim @@ -23,9 +23,10 @@ proc main() = doAssert a in [[0,1], [1,0]] doAssert rand(0) == 0 - doAssert sample("a") == 'a' + when not defined(nimscript): + doAssert sample("a") == 'a' - when compileOption("rangeChecks"): + when compileOption("rangeChecks") and not defined(nimscript): doAssertRaises(RangeDefect): discard rand(-1) @@ -92,7 +93,7 @@ block: # random int block: # again gives new numbers var rand1 = rand(1000000) - when not defined(js): + when not (defined(js) or defined(nimscript)): os.sleep(200) var rand2 = rand(1000000) @@ -122,7 +123,7 @@ block: # random float block: # again gives new numbers var rand1: float = rand(1000000.0) - when not defined(js): + when not (defined(js) or defined(nimscript)): os.sleep(200) var rand2: float = rand(1000000.0) @@ -248,3 +249,26 @@ block: # bug #17670 type UInt48 = range[0'u64..2'u64^48-1] let x = rand(UInt48) doAssert x is UInt48 + +block: # bug #17898 + # Checks whether `initRand()` generates unique states. + # size should be 2^64, but we don't have time and space. + + # Disable this test for js until js gets proper skipRandomNumbers. + when not defined(js): + const size = 1000 + var + rands: array[size, Rand] + randSet: HashSet[Rand] + for i in 0.. Date: Thu, 2 Sep 2021 14:35:25 +0200 Subject: [PATCH 2480/3768] sequtils now support strict effect tracking (#18782) --- lib/pure/collections/sequtils.nim | 23 ++++++++++++++--------- tests/stdlib/tsequtils.nim | 5 +++++ 2 files changed, 19 insertions(+), 9 deletions(-) diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index 5b41ea00d..12b961371 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -84,6 +84,11 @@ import std/private/since import macros +when defined(nimHasEffectsOf): + {.experimental: "strictEffects".} +else: + {.pragma: effectsOf.} + macro evalOnceAs(expAlias, exp: untyped, letAssigneable: static[bool]): untyped = ## Injects `expAlias` in caller scope, to avoid bugs involving multiple @@ -356,7 +361,7 @@ func distribute*[T](s: seq[T], num: Positive, spread = true): seq[seq[T]] = first = last proc map*[T, S](s: openArray[T], op: proc (x: T): S {.closure.}): - seq[S]{.inline.} = + seq[S] {.inline, effectsOf: op.} = ## Returns a new sequence with the results of the `op` proc applied to every ## item in the container `s`. ## @@ -382,7 +387,7 @@ proc map*[T, S](s: openArray[T], op: proc (x: T): S {.closure.}): result[i] = op(s[i]) proc apply*[T](s: var openArray[T], op: proc (x: var T) {.closure.}) - {.inline.} = + {.inline, effectsOf: op.} = ## Applies `op` to every item in `s`, modifying it directly. ## ## Note that the container `s` must be declared as a `var`, @@ -401,7 +406,7 @@ proc apply*[T](s: var openArray[T], op: proc (x: var T) {.closure.}) for i in 0 ..< s.len: op(s[i]) proc apply*[T](s: var openArray[T], op: proc (x: T): T {.closure.}) - {.inline.} = + {.inline, effectsOf: op.} = ## Applies `op` to every item in `s` modifying it directly. ## ## Note that the container `s` must be declared as a `var` @@ -420,7 +425,7 @@ proc apply*[T](s: var openArray[T], op: proc (x: T): T {.closure.}) for i in 0 ..< s.len: s[i] = op(s[i]) -proc apply*[T](s: openArray[T], op: proc (x: T) {.closure.}) {.inline, since: (1, 3).} = +proc apply*[T](s: openArray[T], op: proc (x: T) {.closure.}) {.inline, since: (1, 3), effectsOf: op.} = ## Same as `apply` but for a proc that does not return anything ## and does not mutate `s` directly. runnableExamples: @@ -429,7 +434,7 @@ proc apply*[T](s: openArray[T], op: proc (x: T) {.closure.}) {.inline, since: (1 assert message == "01234" for i in 0 ..< s.len: op(s[i]) -iterator filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): T = +iterator filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): T {.effectsOf: pred.} = ## Iterates through a container `s` and yields every item that fulfills the ## predicate `pred` (a function that returns a `bool`). ## @@ -453,7 +458,7 @@ iterator filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): T = yield s[i] proc filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): seq[T] - {.inline.} = + {.inline, effectsOf: pred.} = ## Returns a new sequence with all the items of `s` that fulfill the ## predicate `pred` (a function that returns a `bool`). ## @@ -480,7 +485,7 @@ proc filter*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): seq[T] result.add(s[i]) proc keepIf*[T](s: var seq[T], pred: proc(x: T): bool {.closure.}) - {.inline.} = + {.inline, effectsOf: pred.} = ## Keeps the items in the passed sequence `s` if they fulfill the ## predicate `pred` (a function that returns a `bool`). ## @@ -685,7 +690,7 @@ since (1, 1): if pred: result += 1 result -proc all*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): bool = +proc all*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): bool {.effectsOf: pred.} = ## Iterates through a container and checks if every item fulfills the ## predicate. ## @@ -727,7 +732,7 @@ template allIt*(s, pred: untyped): bool = break result -proc any*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): bool = +proc any*[T](s: openArray[T], pred: proc(x: T): bool {.closure.}): bool {.effectsOf: pred.} = ## Iterates through a container and checks if at least one item ## fulfills the predicate. ## diff --git a/tests/stdlib/tsequtils.nim b/tests/stdlib/tsequtils.nim index 6c9c92873..338473407 100644 --- a/tests/stdlib/tsequtils.nim +++ b/tests/stdlib/tsequtils.nim @@ -8,6 +8,9 @@ import std/sequtils import strutils from algorithm import sorted +{.experimental: "strictEffects".} +{.push warningAsError[Effect]: on.} + # helper for testing double substitution side effects which are handled # by `evalOnceAs` var counter = 0 @@ -507,3 +510,5 @@ template main = static: main() main() + +{.pop.} From a7cae2bda24871c55348173816a43144b9e668a5 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 2 Sep 2021 17:15:11 +0200 Subject: [PATCH 2481/3768] fixes #16325 [backport:1.4] (#18784) --- compiler/sigmatch.nim | 3 +-- .../tproc_types_dont_like_subtypes.nim | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 tests/overload/tproc_types_dont_like_subtypes.nim diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 465a9a51d..9cf18277f 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -595,8 +595,7 @@ proc procParamTypeRel(c: var TCandidate, f, a: PType): TTypeRelation = # if f is metatype. result = typeRel(c, f, a) - # v--- is this correct? - if result <= isIntConv or inconsistentVarTypes(f, a): + if result <= isSubrange or inconsistentVarTypes(f, a): result = isNone #if result == isEqual: diff --git a/tests/overload/tproc_types_dont_like_subtypes.nim b/tests/overload/tproc_types_dont_like_subtypes.nim new file mode 100644 index 000000000..d322f41be --- /dev/null +++ b/tests/overload/tproc_types_dont_like_subtypes.nim @@ -0,0 +1,20 @@ +discard """ + errormsg: "got " + line: 20 +""" + +type + A = ref object of RootObj + B = ref object of A + + P = proc (a: A) + +# bug #16325 + +proc doThings(a: A, p: P) = + p(a) + +var x = proc (b: B) {.closure.} = + echo "B" + +doThings(B(), x) From f46569bafdfdd3290baf132f025d61a0e550dd58 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 2 Sep 2021 17:16:03 +0200 Subject: [PATCH 2482/3768] fixes #18494 (#18783) --- lib/system/alloc.nim | 46 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 10 deletions(-) diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim index 479458950..6d83a2c17 100644 --- a/lib/system/alloc.nim +++ b/lib/system/alloc.nim @@ -1053,22 +1053,48 @@ template instantiateForRegion(allocator: untyped) {.dirty.} = inc(result, it.size) it = it.next - proc getFreeMem(): int = - result = allocator.freeMem - #sysAssert(result == countFreeMem()) + when hasThreadSupport: + var sharedHeap: MemRegion + var heapLock: SysLock + initSysLock(heapLock) - proc getTotalMem(): int = return allocator.currMem - proc getOccupiedMem(): int = return allocator.occ #getTotalMem() - getFreeMem() - proc getMaxMem*(): int = return getMaxMem(allocator) + proc getFreeMem(): int = + #sysAssert(result == countFreeMem()) + when hasThreadSupport and defined(gcDestructors): + acquireSys(heapLock) + result = sharedHeap.freeMem + releaseSys(heapLock) + else: + result = allocator.freeMem + + proc getTotalMem(): int = + when hasThreadSupport and defined(gcDestructors): + acquireSys(heapLock) + result = sharedHeap.currMem + releaseSys(heapLock) + else: + result = allocator.currMem + + proc getOccupiedMem(): int = + when hasThreadSupport and defined(gcDestructors): + acquireSys(heapLock) + result = sharedHeap.occ + releaseSys(heapLock) + else: + result = allocator.occ #getTotalMem() - getFreeMem() + + proc getMaxMem*(): int = + when hasThreadSupport and defined(gcDestructors): + acquireSys(heapLock) + result = getMaxMem(sharedHeap) + releaseSys(heapLock) + else: + result = getMaxMem(allocator) when defined(nimTypeNames): proc getMemCounters*(): (int, int) = getMemCounters(allocator) # -------------------- shared heap region ---------------------------------- - when hasThreadSupport: - var sharedHeap: MemRegion - var heapLock: SysLock - initSysLock(heapLock) proc allocSharedImpl(size: Natural): pointer = when hasThreadSupport: From 64165f03fc96d6e8293f1b065a2e1e45f9fc1bf8 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 2 Sep 2021 22:15:08 -0700 Subject: [PATCH 2483/3768] testament --megatest:off now makes tests non-joinable so that they all run (#18787) --- testament/categories.nim | 2 +- testament/testament.nim | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/testament/categories.nim b/testament/categories.nim index 6375105ee..6702d24a2 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -541,7 +541,7 @@ proc processSingleTest(r: var TResults, cat: Category, options, test: string, ta proc isJoinableSpec(spec: TSpec): bool = # xxx simplify implementation using a whitelist of fields that are allowed to be # set to non-default values (use `fieldPairs`), to avoid issues like bug #16576. - result = not spec.sortoutput and + result = useMegatest and not spec.sortoutput and spec.action == actionRun and not fileExists(spec.file.changeFileExt("cfg")) and not fileExists(spec.file.changeFileExt("nims")) and diff --git a/testament/testament.nim b/testament/testament.nim index ab6620c84..105f00156 100644 --- a/testament/testament.nim +++ b/testament/testament.nim @@ -28,6 +28,7 @@ var useColors = true var backendLogging = true var simulate = false var optVerbose = false +var useMegatest = true proc verboseCmd(cmd: string) = if optVerbose: @@ -645,7 +646,6 @@ proc main() = var targetsStr = "" var isMainProcess = true var skipFrom = "" - var useMegatest = true var p = initOptParser() p.next() From 0887dcc398a38c91e99a043acbb095afc7200460 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 3 Sep 2021 09:03:26 +0200 Subject: [PATCH 2484/3768] fixes #18786 (#18788) --- compiler/sempass2.nim | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index b584bb232..cae2f926c 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -296,12 +296,11 @@ proc useVarNoInitCheck(a: PEffects; n: PNode; s: PSym) = if {sfGlobal, sfThread} * s.flags != {} and s.kind in {skVar, skLet} and s.magic != mNimvm: if s.guard != nil: guardGlobal(a, n, s.guard) - if strictEffects notin a.c.features: - if {sfGlobal, sfThread} * s.flags == {sfGlobal} and - (tfHasGCedMem in s.typ.flags or s.typ.isGCedMem): - #if a.config.hasWarn(warnGcUnsafe): warnAboutGcUnsafe(n) - markGcUnsafe(a, s) - markSideEffect(a, s, n.info) + if {sfGlobal, sfThread} * s.flags == {sfGlobal} and + (tfHasGCedMem in s.typ.flags or s.typ.isGCedMem): + #if a.config.hasWarn(warnGcUnsafe): warnAboutGcUnsafe(n) + markGcUnsafe(a, s) + markSideEffect(a, s, n.info) if s.owner != a.owner and s.kind in {skVar, skLet, skForVar, skResult, skParam} and {sfGlobal, sfThread} * s.flags == {}: a.isInnerProc = true From 0635697a89b3aef2a188a0bddb46965bae7ee70f Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Fri, 3 Sep 2021 14:20:57 +0200 Subject: [PATCH 2485/3768] Document `-d:async_backend` flag (#18700) * Document `-d:asyncBackend` flag Since libraries may want to support multiple async backends, it's useful to standardize the way this is done, so as to simplify usage of any async backend within the ecosystem. A similar text is being added to chronos here: https://github.com/status-im/nim-chronos/pull/216 See also https://github.com/status-im/nim-chronos/issues/207 * typos, add none * Update lib/pure/asyncdispatch.nim * Update lib/pure/asyncdispatch.nim Co-authored-by: Andreas Rumpf --- lib/pure/asyncdispatch.nim | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 955e66f6b..d87e2360f 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -165,6 +165,34 @@ ## ================ ## ## * The effect system (`raises: []`) does not work with async procedures. +## +## +## Multiple async backend support +## ============================== +## +## Thanks to its powerful macro support, Nim allows ``async``/``await`` to be +## implemented in libraries with only minimal support from the language - as +## such, multiple ``async`` libraries exist, including ``asyncdispatch`` and +## ``chronos``, and more may come to be developed in the future. +## +## Libraries built on top of async/await may wish to support multiple async +## backends - the best way to do so is to create separate modules for each backend +## that may be imported side-by-side. +## +## An alternative way is to select backend using a global compile flag - this +## method makes it difficult to compose applications that use both backends as may +## happen with transitive dependencies, but may be appropriate in some cases - +## libraries choosing this path should call the flag `asyncBackend`, allowing +## applications to choose the backend with `-d:asyncBackend=`. +## +## Known `async` backends include: +## +## * `none` - ``-d:asyncBackend=none`` - disable ``async`` support completely +## * `asyncdispatch -``-d:asyncBackend=asyncdispatch`` +## * `chronos ` - ``-d:asyncBackend=chronos`` +## +## ``none`` can be used when a library supports both a synchronous and +## asynchronous API, to disable the latter. import os, tables, strutils, times, heapqueue, options, asyncstreams import options, math, std/monotimes From 06ff0e96249884f4ac6c49dadc96147be1c31097 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 3 Sep 2021 15:18:55 +0200 Subject: [PATCH 2486/3768] fixes #18769 (#18790) --- compiler/semgnrc.nim | 8 +++++++- tests/enum/toverloadable_enums.nim | 11 ++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index 3df1e0c3f..e249d88e8 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -107,6 +107,12 @@ proc semGenericStmtSymbol(c: PContext, n: PNode, s: PSym, else: result = n onUse(n.info, s) + of skEnumField: + if overloadableEnums in c.features: + result = symChoice(c, n, s, scOpen) + else: + result = newSymNode(s, n.info) + onUse(n.info, s) else: result = newSymNode(s, n.info) onUse(n.info, s) @@ -407,7 +413,7 @@ proc semGenericStmt(c: PContext, n: PNode, a[^1] = semGenericStmt(c, a[^1], flags, ctx) for j in 0.. Date: Fri, 3 Sep 2021 22:41:00 +0800 Subject: [PATCH 2487/3768] remove unnecessary if statement (#18792) --- compiler/transf.nim | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/transf.nim b/compiler/transf.nim index 80794b581..3250f2ec4 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -434,12 +434,12 @@ proc transformYield(c: PTransf, n: PNode): PNode = else: # we need to introduce new local variables: result.add(introduceNewLocalVars(c, c.transCon.forLoopBody)) - if result.len > 0: - for idx in 0 ..< result.len: - var changeNode = result[idx] - changeNode.info = c.transCon.forStmt.info - for i, child in changeNode: - child.info = changeNode.info + + for idx in 0 ..< result.len: + var changeNode = result[idx] + changeNode.info = c.transCon.forStmt.info + for i, child in changeNode: + child.info = changeNode.info proc transformAddrDeref(c: PTransf, n: PNode, a, b: TNodeKind): PNode = result = transformSons(c, n) From c2b20516d33520b1d339b447ece32ade8625fefc Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 3 Sep 2021 17:31:16 +0200 Subject: [PATCH 2488/3768] implemented Unicode operators (#18789) * implemented Unicode operators; refs https://github.com/nim-lang/RFCs/issues/388 * bugfix * better test * arguably more elegant implementation * Update changelog.md Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> --- changelog.md | 7 +++ compiler/lexer.nim | 80 +++++++++++++++++++++++++++--- compiler/options.nim | 3 +- doc/manual.rst | 19 +++++++ tests/lexer/nim.cfg | 1 + tests/lexer/tunicode_operators.nim | 14 ++++++ 6 files changed, 117 insertions(+), 7 deletions(-) create mode 100644 tests/lexer/nim.cfg create mode 100644 tests/lexer/tunicode_operators.nim diff --git a/changelog.md b/changelog.md index 10824cfc6..bc4898c9d 100644 --- a/changelog.md +++ b/changelog.md @@ -445,6 +445,13 @@ proc mysort(s: seq; cmp: proc(a, b: T): int) {.effectsOf: cmp.} To enable the new effect system, use --experimental:strictEffects. +- Nim now supports a small subset of Unicode operators as operator symbols. + The supported symbols are: "∙ ∘ × ★ ⊗ ⊘ ⊙ ⊛ ⊠ ⊡ ∩ ∧ ⊓ ± ⊕ ⊖ ⊞ ⊟ ∪ ∨ ⊔". + To enable this feature, use `--experimental:unicodeOperators`. Note that due + to parser limitations you **cannot** enable this feature via a + pragma `{.experimental: "unicodeOperators".}` reliably. + + ## Compiler changes - Added `--declaredLocs` to show symbol declaration location in messages. diff --git a/compiler/lexer.nim b/compiler/lexer.nim index c7a9aa47f..ddf98661a 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -882,15 +882,66 @@ proc endOperator(L: var Lexer, tok: var Token, pos: int, else: tok.tokType = TokType(tok.ident.id - oprLow + ord(tkColon)) L.bufpos = pos +const + UnicodeOperatorStartChars = {'\226', '\194', '\195'} + # the allowed unicode characters ("∙ ∘ × ★ ⊗ ⊘ ⊙ ⊛ ⊠ ⊡ ∩ ∧ ⊓ ± ⊕ ⊖ ⊞ ⊟ ∪ ∨ ⊔") + # all start with one of these. + +type + UnicodeOprPred = enum + Mul, Add + +proc unicodeOprLen(buf: cstring; pos: int): (int8, UnicodeOprPred) = + template m(len): untyped = (int8(len), Mul) + template a(len): untyped = (int8(len), Add) + result = 0.m + case buf[pos] + of '\226': + if buf[pos+1] == '\136': + if buf[pos+2] == '\152': result = 3.m # ∘ + elif buf[pos+2] == '\153': result = 3.m # ∙ + elif buf[pos+2] == '\167': result = 3.m # ∧ + elif buf[pos+2] == '\168': result = 3.a # ∨ + elif buf[pos+2] == '\169': result = 3.m # ∩ + elif buf[pos+2] == '\170': result = 3.a # ∪ + elif buf[pos+1] == '\138': + if buf[pos+2] == '\147': result = 3.m # ⊓ + elif buf[pos+2] == '\148': result = 3.a # ⊔ + elif buf[pos+2] == '\149': result = 3.a # ⊕ + elif buf[pos+2] == '\150': result = 3.a # ⊖ + elif buf[pos+2] == '\151': result = 3.m # ⊗ + elif buf[pos+2] == '\152': result = 3.m # ⊘ + elif buf[pos+2] == '\153': result = 3.m # ⊙ + elif buf[pos+2] == '\155': result = 3.m # ⊛ + elif buf[pos+2] == '\158': result = 3.a # ⊞ + elif buf[pos+2] == '\159': result = 3.a # ⊟ + elif buf[pos+2] == '\160': result = 3.m # ⊠ + elif buf[pos+2] == '\161': result = 3.m # ⊡ + elif buf[pos+1] == '\152' and buf[pos+2] == '\133': result = 3.m # ★ + of '\194': + if buf[pos+1] == '\177': result = 2.a # ± + of '\195': + if buf[pos+1] == '\151': result = 2.m # × + else: + discard + proc getOperator(L: var Lexer, tok: var Token) = var pos = L.bufpos tokenBegin(tok, pos) var h: Hash = 0 while true: var c = L.buf[pos] - if c notin OpChars: break - h = h !& ord(c) - inc(pos) + if c in OpChars: + h = h !& ord(c) + inc(pos) + elif c in UnicodeOperatorStartChars and unicodeOperators in L.config.features: + let oprLen = unicodeOprLen(L.buf, pos)[0] + if oprLen == 0: break + for i in 0..', '!': result = 5 of '.': considerAsgn(6) of '?': result = 2 + of UnicodeOperatorStartChars: + if tok.ident.s[^1] == '=': + result = 1 + else: + let (len, pred) = unicodeOprLen(cstring(tok.ident.s), 0) + if len != 0: + result = if pred == Mul: MulPred else: PlusPred + else: + result = 2 else: considerAsgn(2) of tkDiv, tkMod, tkShl, tkShr: result = 9 of tkDotDot: result = 6 @@ -1167,10 +1230,15 @@ proc rawGetTok*(L: var Lexer, tok: var Token) = var c = L.buf[L.bufpos] tok.line = L.lineNumber tok.col = getColNumber(L, L.bufpos) - if c in SymStartChars - {'r', 'R'}: + if c in SymStartChars - {'r', 'R'} - UnicodeOperatorStartChars: getSymbol(L, tok) else: case c + of UnicodeOperatorStartChars: + if unicodeOperators in L.config.features and unicodeOprLen(L.buf, L.bufpos)[0] != 0: + getOperator(L, tok) + else: + getSymbol(L, tok) of '#': scanComment(L, tok) of '*': diff --git a/compiler/options.nim b/compiler/options.nim index 89a16a49c..ea302aed6 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -205,7 +205,8 @@ type views, strictNotNil, overloadableEnums, - strictEffects + strictEffects, + unicodeOperators LegacyFeature* = enum allowSemcheckedAstModification, diff --git a/doc/manual.rst b/doc/manual.rst index 02c688968..997253f16 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -700,6 +700,25 @@ contain a dot: `{..}` are the three tokens `{`:tok:, `..`:tok:, `}`:tok: and not the two tokens `{.`:tok:, `.}`:tok:. +Unicode Operators +----------------- + +Under the `--experimental:unicodeOperators` switch these Unicode operators are +also parsed as operators:: + + ∙ ∘ × ★ ⊗ ⊘ ⊙ ⊛ ⊠ ⊡ ∩ ∧ ⊓ # same priority as * (multiplication) + ± ⊕ ⊖ ⊞ ⊟ ∪ ∨ ⊔ # same priority as + (addition) + + +If enabled, Unicode operators can be combined with non-Unicode operator +symbols. The usual precedence extensions then apply, for example, `⊠=` is an +assignment like operator just like `*=` is. + +No Unicode normalization step is performed. + +**Note**: Due to parser limitations one **cannot** enable this feature via a +pragma `{.experimental: "unicodeOperators".}` reliably. + Syntax ====== diff --git a/tests/lexer/nim.cfg b/tests/lexer/nim.cfg new file mode 100644 index 000000000..f7a301a10 --- /dev/null +++ b/tests/lexer/nim.cfg @@ -0,0 +1 @@ +--experimental:unicodeOperators diff --git a/tests/lexer/tunicode_operators.nim b/tests/lexer/tunicode_operators.nim new file mode 100644 index 000000000..74fcbb763 --- /dev/null +++ b/tests/lexer/tunicode_operators.nim @@ -0,0 +1,14 @@ +#{.experimental: "unicodeOperators".} + +proc `⊙`(x, y: int): int = x * y +proc `⊙=`(x: var int, y: int) = x *= y + +proc `⊞++`(x, y: int): int = x + y + +var x = 45 +x ⊙= 9 ⊞++ 4 ⊙ 3 + +var y = 45 +y *= 9 + 4 * 3 + +assert x == y From cddf8ec6f684e5636a114e0e286bc6609e01f228 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 3 Sep 2021 21:52:24 +0200 Subject: [PATCH 2489/3768] implements https://github.com/nim-lang/RFCs/issues/407 (#18793) --- compiler/semdata.nim | 1 + compiler/semexprs.nim | 16 ++++++++++++---- compiler/semobjconstr.nim | 31 ++++++++++++++++-------------- compiler/sempass2.nim | 2 ++ compiler/semstmts.nim | 14 ++++++++++++++ compiler/sigmatch.nim | 14 ++++++++++++-- compiler/wordrecg.nim | 1 + doc/manual.rst | 35 ++++++++++++++++++++++++++++++++++ tests/objvariant/treassign.nim | 9 +++++++++ 9 files changed, 103 insertions(+), 20 deletions(-) diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 31affb24f..422d1223a 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -160,6 +160,7 @@ type importModuleMap*: Table[int, int] # (module.id, module.id) lastTLineInfo*: TLineInfo sideEffects*: Table[int, seq[(TLineInfo, PSym)]] # symbol.id index + inUncheckedAssignSection*: int template config*(c: PContext): ConfigRef = c.graph.config diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index f2fe54fad..3edeae324 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -691,8 +691,12 @@ proc newHiddenAddrTaken(c: PContext, n: PNode): PNode = else: result = newNodeIT(nkHiddenAddr, n.info, makeVarType(c, n.typ)) result.add n - if isAssignable(c, n) notin {arLValue, arLocalLValue}: - localError(c.config, n.info, errVarForOutParamNeededX % renderNotLValue(n)) + let aa = isAssignable(c, n) + if aa notin {arLValue, arLocalLValue}: + if aa == arDiscriminant and c.inUncheckedAssignSection > 0: + discard "allow access within a cast(unsafeAssign) section" + else: + localError(c.config, n.info, errVarForOutParamNeededX % renderNotLValue(n)) proc analyseIfAddressTaken(c: PContext, n: PNode): PNode = result = n @@ -738,9 +742,13 @@ proc analyseIfAddressTakenInCall(c: PContext, n: PNode) = if i < t.len and t[i] != nil and skipTypes(t[i], abstractInst-{tyTypeDesc}).kind in {tyVar}: let it = n[i] - if isAssignable(c, it) notin {arLValue, arLocalLValue}: + let aa = isAssignable(c, it) + if aa notin {arLValue, arLocalLValue}: if it.kind != nkHiddenAddr: - localError(c.config, it.info, errVarForOutParamNeededX % $it) + if aa == arDiscriminant and c.inUncheckedAssignSection > 0: + discard "allow access within a cast(unsafeAssign) section" + else: + localError(c.config, it.info, errVarForOutParamNeededX % $it) # bug #5113: disallow newSeq(result) where result is a 'var T': if n[0].sym.magic in {mNew, mNewFinalize, mNewSeq}: var arg = n[1] #.skipAddr diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim index 1c97d32c8..1563e5c16 100644 --- a/compiler/semobjconstr.nim +++ b/compiler/semobjconstr.nim @@ -200,20 +200,22 @@ proc semConstructFields(c: PContext, n: PNode, if selectedBranch != -1: template badDiscriminatorError = - let fields = fieldsPresentInBranch(selectedBranch) - localError(c.config, constrCtx.initExpr.info, - ("cannot prove that it's safe to initialize $1 with " & - "the runtime value for the discriminator '$2' ") % - [fields, discriminator.sym.name.s]) + if c.inUncheckedAssignSection == 0: + let fields = fieldsPresentInBranch(selectedBranch) + localError(c.config, constrCtx.initExpr.info, + ("cannot prove that it's safe to initialize $1 with " & + "the runtime value for the discriminator '$2' ") % + [fields, discriminator.sym.name.s]) mergeInitStatus(result, initNone) template wrongBranchError(i) = - let fields = fieldsPresentInBranch(i) - localError(c.config, constrCtx.initExpr.info, - "a case selecting discriminator '$1' with value '$2' " & - "appears in the object construction, but the field(s) $3 " & - "are in conflict with this value." % - [discriminator.sym.name.s, discriminatorVal.renderTree, fields]) + if c.inUncheckedAssignSection == 0: + let fields = fieldsPresentInBranch(i) + localError(c.config, constrCtx.initExpr.info, + ("a case selecting discriminator '$1' with value '$2' " & + "appears in the object construction, but the field(s) $3 " & + "are in conflict with this value.") % + [discriminator.sym.name.s, discriminatorVal.renderTree, fields]) template valuesInConflictError(valsDiff) = localError(c.config, discriminatorVal.info, ("possible values " & @@ -251,9 +253,10 @@ proc semConstructFields(c: PContext, n: PNode, badDiscriminatorError() elif discriminatorVal.sym.kind notin {skLet, skParam} or discriminatorVal.sym.typ.kind in {tyVar}: - localError(c.config, discriminatorVal.info, - "runtime discriminator must be immutable if branch fields are " & - "initialized, a 'let' binding is required.") + if c.inUncheckedAssignSection == 0: + localError(c.config, discriminatorVal.info, + "runtime discriminator must be immutable if branch fields are " & + "initialized, a 'let' binding is required.") elif ctorCase[ctorIdx].kind == nkElifBranch: localError(c.config, discriminatorVal.info, "branch initialization " & "with a runtime discriminator is not supported inside of an " & diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index cae2f926c..2df6327b8 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -961,6 +961,8 @@ proc castBlock(tracked: PEffects, pragma: PNode, bc: var PragmaBlockContext) = else: bc.exc = newNodeI(nkArgList, pragma.info) bc.exc.add n + of wUncheckedAssign: + discard "handled in sempass1" else: localError(tracked.config, pragma.info, "invalid pragma block: " & $pragma) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index df0e2778b..2c5f3ba54 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -2223,7 +2223,21 @@ proc semPragmaBlock(c: PContext, n: PNode): PNode = checkSonsLen(n, 2, c.config) let pragmaList = n[0] pragma(c, nil, pragmaList, exprPragmas, isStatement = true) + + var inUncheckedAssignSection = 0 + for p in pragmaList: + if whichPragma(p) == wCast: + case whichPragma(p[1]) + of wGcSafe, wNoSideEffect, wTags, wRaises: + discard "handled in sempass2" + of wUncheckedAssign: + inUncheckedAssignSection = 1 + else: + localError(c.config, p.info, "invalid pragma block: " & $p) + + inc c.inUncheckedAssignSection, inUncheckedAssignSection n[1] = semExpr(c, n[1]) + dec c.inUncheckedAssignSection, inUncheckedAssignSection result = n result.typ = n[1].typ for i in 0.. 0 + else: + result = false + proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType, arg: PNode): PNode = result = nil @@ -1895,7 +1905,7 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType, let constraint = c.converters[i].typ.n[1].sym.constraint if not constraint.isNil and not matchNodeKinds(constraint, arg): continue - if src.kind in {tyVar, tyLent} and not arg.isLValue: + if src.kind in {tyVar, tyLent} and not isLValue(c, arg): continue let destIsGeneric = containsGenericType(dest) @@ -2338,7 +2348,7 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var Int if argConverter.typ.kind notin {tyVar}: m.firstMismatch.kind = kVarNeeded noMatch() - elif not n.isLValue: + elif not isLValue(c, n): m.firstMismatch.kind = kVarNeeded noMatch() diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index 6ff68be75..4ba807518 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -36,6 +36,7 @@ type wMemTracker = "memtracker", wObjChecks = "objchecks", wIntDefine = "intdefine", wStrDefine = "strdefine", wBoolDefine = "booldefine", wCursor = "cursor", wNoalias = "noalias", wEffectsOf = "effectsOf", + wUncheckedAssign = "uncheckedAssign", wImmediate = "immediate", wConstructor = "constructor", wDestructor = "destructor", wDelegator = "delegator", wOverride = "override", wImportCpp = "importcpp", diff --git a/doc/manual.rst b/doc/manual.rst index 997253f16..714f98bdc 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -1854,6 +1854,41 @@ A small example: let unknownKindBounded = range[nkAdd..nkSub](unknownKind) z = Node(kind: unknownKindBounded, leftOp: Node(), rightOp: Node()) + +cast uncheckedAssign +-------------------- + +Some restrictions for case objects can be disabled via a `{.cast(unsafeAssign).}` section: + +.. code-block:: nim + :test: "nim c $1" + + type + TokenKind* = enum + strLit, intLit + Token = object + case kind*: TokenKind + of strLit: + s*: string + of intLit: + i*: int64 + + proc passToVar(x: var TokenKind) = discard + + var t = Token(kind: strLit, s: "abc") + + {.cast(uncheckedAssign).}: + # inside the 'cast' section it is allowed to pass 't.kind' to a 'var T' parameter: + passToVar(t.kind) + + # inside the 'cast' section it is allowed to set field 's' even though the + # constructed 'kind' field has an unknown value: + t = Token(kind: t.kind, s: "abc") + + # inside the 'cast' section it is allowed to assign to the 't.kind' field directly: + t.kind = intLit + + Set type -------- diff --git a/tests/objvariant/treassign.nim b/tests/objvariant/treassign.nim index 2938b30a3..9549cb29c 100644 --- a/tests/objvariant/treassign.nim +++ b/tests/objvariant/treassign.nim @@ -25,3 +25,12 @@ t.curr = TokenObject(kind: Token.bar, bar: BasicNumber(value: 12.34)) t.curr = TokenObject(kind: Token.foo, foo: "foo") echo "SUCCESS" + +proc passToVar(x: var Token) = discard + +{.cast(uncheckedAssign).}: + passToVar(t.curr.kind) + + t.curr = TokenObject(kind: t.curr.kind, foo: "abc") + + t.curr.kind = Token.foo From ac7acd827cd05ea916c53513e68d13be0d63a604 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 4 Sep 2021 08:18:00 +0200 Subject: [PATCH 2490/3768] =?UTF-8?q?we=20need=20something=20better=20than?= =?UTF-8?q?=20warningAsError=20for=20effect=20handling=20viol=E2=80=A6=20(?= =?UTF-8?q?#18796)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * we need something better than warningAsError for effect handling violations --- compiler/sempass2.nim | 4 ++-- lib/system.nim | 3 +-- nimdoc/test_out_index_dot_html/expected/index.html | 2 +- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 2df6327b8..6a0e8772a 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -1420,7 +1420,7 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) = let p = s.ast[pragmasPos] let raisesSpec = effectSpec(p, wRaises) if not isNil(raisesSpec): - checkRaisesSpec(g, emitWarnings, raisesSpec, t.exc, "can raise an unlisted exception: ", + checkRaisesSpec(g, false, raisesSpec, t.exc, "can raise an unlisted exception: ", hints=on, subtypeRelation, hintsArg=s.ast[0]) # after the check, use the formal spec: effects[exceptionEffects] = raisesSpec @@ -1429,7 +1429,7 @@ proc trackProc*(c: PContext; s: PSym, body: PNode) = let tagsSpec = effectSpec(p, wTags) if not isNil(tagsSpec): - checkRaisesSpec(g, emitWarnings, tagsSpec, t.tags, "can have an unlisted effect: ", + checkRaisesSpec(g, false, tagsSpec, t.tags, "can have an unlisted effect: ", hints=off, subtypeRelation) # after the check, use the formal spec: effects[tagEffects] = tagsSpec diff --git a/lib/system.nim b/lib/system.nim index 740c4419f..c93af0854 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2004,8 +2004,7 @@ elif hasAlloc: inc(i) {.pop.} -proc echo*(x: varargs[typed, `$`]) {.magic: "Echo", tags: [WriteIOEffect], - benign, sideEffect.} +proc echo*(x: varargs[typed, `$`]) {.magic: "Echo", benign, sideEffect.} ## Writes and flushes the parameters to the standard output. ## ## Special built-in that takes a variable number of arguments. Each argument diff --git a/nimdoc/test_out_index_dot_html/expected/index.html b/nimdoc/test_out_index_dot_html/expected/index.html index 458a0394f..3499b5326 100644 --- a/nimdoc/test_out_index_dot_html/expected/index.html +++ b/nimdoc/test_out_index_dot_html/expected/index.html @@ -122,7 +122,7 @@ window.addEventListener('DOMContentLoaded', main);

    Procs

    -
    proc foo() {....raises: [], tags: [WriteIOEffect].}
    +
    proc foo() {....raises: [], tags: [].}
    I do foo From 686096a912107e620142e78ef98a380bb8a4764f Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 4 Sep 2021 12:52:24 +0200 Subject: [PATCH 2491/3768] documented overloadable enums and changelog improvements (#18797) --- changelog.md | 51 ++++++++++++++++++++++++++++---------------------- doc/manual.rst | 41 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 22 deletions(-) diff --git a/changelog.md b/changelog.md index bc4898c9d..d553864b6 100644 --- a/changelog.md +++ b/changelog.md @@ -114,16 +114,18 @@ ## Standard library additions and changes - `strformat`: - added support for parenthesized expressions. - added support for const string's instead of just string literals + - added support for parenthesized expressions. + - added support for const string's instead of just string literals +- `os.copyFile` is now 2.5x faster on OSX, by using `copyfile` from `copyfile.h`; + use `-d:nimLegacyCopyFile` for OSX < 10.5. - `system.addFloat` and `system.$` now can produce string representations of floating point numbers that are minimal in size and that "roundtrip" (via the "Dragonbox" algorithm). This currently has to be enabled via `-d:nimPreviewFloatRoundtrip`. It is expected that this behavior becomes the new default in upcoming versions, as with other `nimPreviewX` define flags. -- Fixed buffer overflow bugs in `net` +- Fixed buffer overflow bugs in `net`. - Exported `sslHandle` from `net` and `asyncnet`. @@ -187,10 +189,11 @@ - Added `std/sysrand` module to get random numbers from a secure source provided by the operating system. -- Added `std/enumutils` module. Added `genEnumCaseStmt` macro that generates case statement to parse string to enum. - Added `items` for enums with holes. - Added `symbolName` to return the enum symbol name ignoring the human readable name. - Added `symbolRank` to return the index in which an enum member is listed in an enum. +- Added `std/enumutils` module. Added `genEnumCaseStmt` macro that generates + case statement to parse string to enum. +- Added `items` for enums with holes. +- Added `symbolName` to return the enum symbol name ignoring the human readable name. +- Added `symbolRank` to return the index in which an enum member is listed in an enum. - Removed deprecated `iup` module from stdlib, it has already moved to [nimble](https://github.com/nim-lang/iup). @@ -381,15 +384,10 @@ ## Language changes -- `nimscript` now handles `except Exception as e`. - - The `cstring` doesn't support `[]=` operator in JS backend. - nil dereference is not allowed at compile time. `cast[ptr int](nil)[]` is rejected at compile time. -- `os.copyFile` is now 2.5x faster on OSX, by using `copyfile` from `copyfile.h`; - use `-d:nimLegacyCopyFile` for OSX < 10.5. - - The required name of case statement macros for the experimental `caseStmtMacros` feature has changed from `match` to `` `case` ``. @@ -403,18 +401,11 @@ - Tuple expressions are now parsed consistently as `nnkTupleConstr` node. Will affect macros expecting nodes to be of `nnkPar`. -- `nim e` now accepts arbitrary file extensions for the nimscript file, - although `.nims` is still the preferred extension in general. - - Added `iterable[T]` type class to match called iterators, which enables writing: `template fn(a: iterable)` instead of `template fn(a: untyped)` - A new import syntax `import foo {.all.}` now allows to import all symbols (public or private) - from `foo`. It works in combination with all pre-existing import features. - This reduces or eliminates the need for workarounds such as using `include` (which has known issues) - when you need a private symbol for testing or making some internal APIs public just because - another internal module needs those. - It also helps mitigate the lack of cyclic imports in some cases. + from `foo`. This can be useful for testing purposes. - Added a new module `std/importutils`, and an API `privateAccess`, which allows access to private fields for an object type in the current scope. @@ -422,7 +413,7 @@ - `typeof(voidStmt)` now works and returns `void`. - The `gc:orc` algorithm was refined so that custom container types can participate in the - cycle collection process. + cycle collection process. See the documentation of `=trace` for more details. - On embedded devices `malloc` can now be used instead of `mmap` via `-d:nimAllocPagesViaMalloc`. This is only supported for `--gc:orc` or `--gc:arc`. @@ -449,7 +440,18 @@ proc mysort(s: seq; cmp: proc(a, b: T): int) {.effectsOf: cmp.} The supported symbols are: "∙ ∘ × ★ ⊗ ⊘ ⊙ ⊛ ⊠ ⊡ ∩ ∧ ⊓ ± ⊕ ⊖ ⊞ ⊟ ∪ ∨ ⊔". To enable this feature, use `--experimental:unicodeOperators`. Note that due to parser limitations you **cannot** enable this feature via a - pragma `{.experimental: "unicodeOperators".}` reliably. + pragma `{.experimental: "unicodeOperators".}` reliably, you need to enable + it via the command line or in a configuration file. + +- There is a new `cast` section `{.cast(uncheckedAssign).}: body` that disables some + compiler checks regarding `case objects`. This allows serialization libraries + to avoid ugly, non-portable solutions. See https://github.com/nim-lang/RFCs/issues/407 + for more details. + +- Enum values can now be overloaded. This needs to be enabled + via `{.experimental: "overloadableEnums".}`. We hope that this feature allows for the + development of more fluent (less ugly) APIs. See https://github.com/nim-lang/RFCs/issues/373 + for more details. ## Compiler changes @@ -529,6 +531,11 @@ proc mysort(s: seq; cmp: proc(a, b: T): int) {.effectsOf: cmp.} ## Tool changes +- `nimscript` now handles `except Exception as e`. + +- `nim e` now accepts arbitrary file extensions for the nimscript file, + although `.nims` is still the preferred extension in general. + - Latex doc generation is revised: output `.tex` files should be compiled by `xelatex` (not by `pdflatex` as before). Now default Latex settings provide support for Unicode and do better job for avoiding margin overflows. diff --git a/doc/manual.rst b/doc/manual.rst index 714f98bdc..e9c730c7b 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -1323,6 +1323,47 @@ as `MyEnum.value`: To implement bit fields with enums see `Bit fields <#set-type-bit-fields>`_ +Overloadable enum field names +----------------------------- + +To be enabled via `{.experimental: "overloadableEnums".}`. + +Enum field names are overloadable much like routines. When an overloaded +enum field is used, it produces a closed sym choice construct, here +written as `(E|E)`. +During overload resolution the right `E` is picked, if possible. +For (array/object...) constructors the right `E` is picked, comparable to +how `[byte(1), 2, 3]` works, one needs to use `[T.E, E2, E3]`. Ambiguous +enum fields produce a static error: + +.. code-block:: nim + :test: "nim c $1" + + {.experimental: "overloadableEnums".} + + type + E1 = enum + value1, + value2 + E2 = enum + value1, + value2 = 4 + + const + Lookuptable = [ + E1.value1: "1", + value2: "2" + ] + + proc p(e: E1) = + # disambiguation in 'case' statements: + case e + of value1: echo "A" + of value2: echo "B" + + p value2 + + String type ----------- All string literals are of the type `string`. A string in Nim is very From 3c2edd142b527c34163cbd7ea9ad94518f46e9ae Mon Sep 17 00:00:00 2001 From: Sven Keller Date: Sat, 4 Sep 2021 12:53:02 +0200 Subject: [PATCH 2492/3768] fix for js strict mode (#18799) * Assignments, which would accidentally create global variables, instead throw an error in strict mode * Assignment to a getter-only property Co-authored-by: Sven Keller --- compiler/jsgen.nim | 2 +- lib/system/jssys.nim | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 931e56475..f009c83f2 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -1769,7 +1769,7 @@ proc genVarInit(p: PProc, v: PSym, n: PNode) = inc p.extraIndent elif useGlobalPragmas: lineF(p, "if (globalThis.$1 === undefined) {$n", varName) - varCode = $varName + varCode = "globalThis." & $varName inc p.extraIndent else: varCode = "var $2" diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim index 63e7b3d9d..cd4f378be 100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -593,12 +593,9 @@ proc nimCopy(dest, src: JSRef, ti: PNimType): JSRef = `result` = null; } else { - if (`dest` === null || `dest` === undefined) { + if (`dest` === null || `dest` === undefined || `dest`.length != `src`.length) { `dest` = new Array(`src`.length); } - else { - `dest`.length = `src`.length; - } `result` = `dest`; for (var i = 0; i < `src`.length; ++i) { `result`[i] = nimCopy(`result`[i], `src`[i], `ti`.base); From b3ad68edea72f04275c9b1e330cafb77254d945c Mon Sep 17 00:00:00 2001 From: flywind Date: Sat, 4 Sep 2021 21:57:02 +0800 Subject: [PATCH 2493/3768] remove channels (#18801) * remove channels * test --- changelog.md | 2 - doc/manual.rst | 2 +- lib/std/channels.nim | 498 ----------------------------- tests/stdlib/tchannels.nim | 33 -- tests/stdlib/tchannels_pthread.nim | 322 ------------------- tests/stdlib/tchannels_simple.nim | 67 ---- 6 files changed, 1 insertion(+), 923 deletions(-) delete mode 100644 lib/std/channels.nim delete mode 100644 tests/stdlib/tchannels.nim delete mode 100644 tests/stdlib/tchannels_pthread.nim delete mode 100644 tests/stdlib/tchannels_simple.nim diff --git a/changelog.md b/changelog.md index d553864b6..3658fb105 100644 --- a/changelog.md +++ b/changelog.md @@ -341,8 +341,6 @@ - Added `jscore.debugger` to [call any available debugging functionality, such as breakpoints.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/debugger). -- Added `std/channels`. - - Added `htmlgen.portal` for [making "SPA style" pages using HTML only](https://web.dev/hands-on-portals). - `std/times`: diff --git a/doc/manual.rst b/doc/manual.rst index e9c730c7b..7976ffe0f 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -8016,7 +8016,7 @@ Threads To enable thread support the `--threads:on`:option: command-line switch needs to be used. The system_ module then contains several threading primitives. -See the `threads `_ and `channels `_ modules +See the `channels `_ modules for the low-level thread API. There are also high-level parallelism constructs available. See `spawn `_ for further details. diff --git a/lib/std/channels.nim b/lib/std/channels.nim deleted file mode 100644 index 5ff141384..000000000 --- a/lib/std/channels.nim +++ /dev/null @@ -1,498 +0,0 @@ -# -# -# The Nim Compiler -# (c) Copyright 2021 Andreas Prell, Mamy André-Ratsimbazafy & Nim Contributors -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - - -# Based on https://github.com/mratsim/weave/blob/5696d94e6358711e840f8c0b7c684fcc5cbd4472/unused/channels/channels_legacy.nim -# Those are translations of @aprell (Andreas Prell) original channels from C to Nim -# (https://github.com/aprell/tasking-2.0/blob/master/src/channel_shm/channel.c) -# And in turn they are an implementation of Michael & Scott lock-based queues -# (note the paper has 2 channels: lock-free and lock-based) with additional caching: -# Simple, Fast, and Practical Non-Blocking and Blocking Concurrent Queue Algorithms -# Maged M. Michael, Michael L. Scott, 1996 -# https://www.cs.rochester.edu/~scott/papers/1996_PODC_queues.pdf - -## This module only works with `--gc:arc` or `--gc:orc`. -## -## .. warning:: This module is experimental and its interface may change. -## -## The following is a simple example of two different ways to use channels: -## blocking and non-blocking. -## - -runnableExamples("--threads:on --gc:orc"): - import std/os - - # In this example a channel is declared at module scope. - # Channels are generic, and they include support for passing objects between - # threads. - # Note that isolated data passed through channels is moved around. - var chan = newChannel[string]() - - # This proc will be run in another thread using the threads module. - proc firstWorker() = - chan.send("Hello World!") - - # This is another proc to run in a background thread. This proc takes a while - # to send the message since it sleeps for 2 seconds (or 2000 milliseconds). - proc secondWorker() = - sleep(2000) - chan.send("Another message") - - # Launch the worker. - var worker1: Thread[void] - createThread(worker1, firstWorker) - - # Block until the message arrives, then print it out. - let dest = chan.recv() - assert dest == "Hello World!" - - # Wait for the thread to exit before moving on to the next example. - worker1.joinThread() - - # Launch the other worker. - var worker2: Thread[void] - createThread(worker2, secondWorker) - # This time, use a non-blocking approach with tryRecv. - # Since the main thread is not blocked, it could be used to perform other - # useful work while it waits for data to arrive on the channel. - var messages: seq[string] - while true: - var msg = "" - if chan.tryRecv(msg): - messages.add msg # "Another message" - break - - messages.add "Pretend I'm doing useful work..." - # For this example, sleep in order not to flood stdout with the above - # message. - sleep(400) - - # Wait for the second thread to exit before cleaning up the channel. - worker2.joinThread() - - # Clean up the channel. - assert chan.close() - - assert messages[^1] == "Another message" - assert messages.len >= 2 - - -when not defined(gcArc) and not defined(gcOrc) and not defined(nimdoc): - {.error: "This channel implementation requires --gc:arc or --gc:orc".} - -import std/[locks, atomics, isolation] -import system/ansi_c - -# Channel (Shared memory channels) -# ---------------------------------------------------------------------------------- - -const - cacheLineSize {.intdefine.} = 64 # TODO: some Samsung phone have 128 cache-line - nimChannelCacheSize* {.intdefine.} = 100 - -type - ChannelRaw = ptr ChannelObj - ChannelObj = object - headLock, tailLock: Lock - notFullCond, notEmptyCond: Cond - closed: Atomic[bool] - size: int - itemsize: int # up to itemsize bytes can be exchanged over this channel - head {.align: cacheLineSize.} : int # Items are taken from head and new items are inserted at tail - tail: int - buffer: ptr UncheckedArray[byte] - atomicCounter: Atomic[int] - - ChannelCache = ptr ChannelCacheObj - ChannelCacheObj = object - next: ChannelCache - chanSize: int - chanN: int - numCached: int - cache: array[nimChannelCacheSize, ChannelRaw] - -# ---------------------------------------------------------------------------------- - -proc numItems(chan: ChannelRaw): int {.inline.} = - result = chan.tail - chan.head - if result < 0: - inc(result, 2 * chan.size) - - assert result <= chan.size - -template isFull(chan: ChannelRaw): bool = - abs(chan.tail - chan.head) == chan.size - -template isEmpty(chan: ChannelRaw): bool = - chan.head == chan.tail - -# Unbuffered / synchronous channels -# ---------------------------------------------------------------------------------- - -template numItemsUnbuf(chan: ChannelRaw): int = - chan.head - -template isFullUnbuf(chan: ChannelRaw): bool = - chan.head == 1 - -template isEmptyUnbuf(chan: ChannelRaw): bool = - chan.head == 0 - -# ChannelRaw kinds -# ---------------------------------------------------------------------------------- - -func isUnbuffered(chan: ChannelRaw): bool = - chan.size - 1 == 0 - -# ChannelRaw status and properties -# ---------------------------------------------------------------------------------- - -proc isClosed(chan: ChannelRaw): bool {.inline.} = load(chan.closed, moRelaxed) - -proc peek(chan: ChannelRaw): int {.inline.} = - (if chan.isUnbuffered: numItemsUnbuf(chan) else: numItems(chan)) - -# Per-thread channel cache -# ---------------------------------------------------------------------------------- - -var channelCache {.threadvar.}: ChannelCache -var channelCacheLen {.threadvar.}: int - -proc allocChannelCache(size, n: int): bool = - ## Allocate a free list for storing channels of a given type - var p = channelCache - - # Avoid multiple free lists for the exact same type of channel - while not p.isNil: - if size == p.chanSize and n == p.chanN: - return false - p = p.next - - p = cast[ptr ChannelCacheObj](c_malloc(csize_t sizeof(ChannelCacheObj))) - if p.isNil: - raise newException(OutOfMemDefect, "Could not allocate memory") - - p.chanSize = size - p.chanN = n - p.numCached = 0 - - p.next = channelCache - channelCache = p - inc channelCacheLen - result = true - -proc freeChannelCache*() = - ## Frees the entire channel cache, including all channels - var p = channelCache - var q: ChannelCache - - while not p.isNil: - q = p.next - for i in 0 ..< p.numCached: - let chan = p.cache[i] - if not chan.buffer.isNil: - c_free(chan.buffer) - deinitLock(chan.headLock) - deinitLock(chan.tailLock) - deinitCond(chan.notFullCond) - deinitCond(chan.notEmptyCond) - c_free(chan) - c_free(p) - dec channelCacheLen - p = q - - assert(channelCacheLen == 0) - channelCache = nil - -# Channels memory ops -# ---------------------------------------------------------------------------------- - -proc allocChannel(size, n: int): ChannelRaw = - when nimChannelCacheSize > 0: - var p = channelCache - - while not p.isNil: - if size == p.chanSize and n == p.chanN: - # Check if free list contains channel - if p.numCached > 0: - dec p.numCached - result = p.cache[p.numCached] - assert(result.isEmpty) - return - else: - # All the other lists in cache won't match - break - p = p.next - - result = cast[ChannelRaw](c_malloc(csize_t sizeof(ChannelObj))) - if result.isNil: - raise newException(OutOfMemDefect, "Could not allocate memory") - - # To buffer n items, we allocate for n - result.buffer = cast[ptr UncheckedArray[byte]](c_malloc(csize_t n*size)) - if result.buffer.isNil: - raise newException(OutOfMemDefect, "Could not allocate memory") - - initLock(result.headLock) - initLock(result.tailLock) - initCond(result.notFullCond) - initCond(result.notEmptyCond) - - result.closed.store(false, moRelaxed) # We don't need atomic here, how to? - result.size = n - result.itemsize = size - result.head = 0 - result.tail = 0 - result.atomicCounter.store(0, moRelaxed) - - when nimChannelCacheSize > 0: - # Allocate a cache as well if one of the proper size doesn't exist - discard allocChannelCache(size, n) - -proc freeChannel(chan: ChannelRaw) = - if chan.isNil: - return - - when nimChannelCacheSize > 0: - var p = channelCache - while not p.isNil: - if chan.itemsize == p.chanSize and - chan.size == p.chanN: - if p.numCached < nimChannelCacheSize: - # If space left in cache, cache it - p.cache[p.numCached] = chan - inc p.numCached - return - else: - # All the other lists in cache won't match - break - p = p.next - - if not chan.buffer.isNil: - c_free(chan.buffer) - - deinitLock(chan.headLock) - deinitLock(chan.tailLock) - deinitCond(chan.notFullCond) - deinitCond(chan.notEmptyCond) - - c_free(chan) - -# MPMC Channels (Multi-Producer Multi-Consumer) -# ---------------------------------------------------------------------------------- - -proc sendUnbufferedMpmc(chan: ChannelRaw, data: sink pointer, size: int, nonBlocking: bool): bool = - if nonBlocking and chan.isFullUnbuf: - return false - - acquire(chan.headLock) - - if nonBlocking and chan.isFullUnbuf: - # Another thread was faster - release(chan.headLock) - return false - - while chan.isFullUnbuf: - wait(chan.notFullcond, chan.headLock) - - assert chan.isEmptyUnbuf - assert size <= chan.itemsize - copyMem(chan.buffer, data, size) - - chan.head = 1 - - signal(chan.notEmptyCond) - release(chan.headLock) - result = true - -proc sendMpmc(chan: ChannelRaw, data: sink pointer, size: int, nonBlocking: bool): bool = - assert not chan.isNil - assert not data.isNil - - if isUnbuffered(chan): - return sendUnbufferedMpmc(chan, data, size, nonBlocking) - - if nonBlocking and chan.isFull: - return false - - acquire(chan.tailLock) - - if nonBlocking and chan.isFull: - # Another thread was faster - release(chan.tailLock) - return false - - while chan.isFull: - wait(chan.notFullcond, chan.tailLock) - - assert not chan.isFull - assert size <= chan.itemsize - - let writeIdx = if chan.tail < chan.size: chan.tail - else: chan.tail - chan.size - - copyMem(chan.buffer[writeIdx * chan.itemsize].addr, data, size) - - inc chan.tail - if chan.tail == 2 * chan.size: - chan.tail = 0 - - signal(chan.notEmptyCond) - release(chan.tailLock) - result = true - -proc recvUnbufferedMpmc(chan: ChannelRaw, data: pointer, size: int, nonBlocking: bool): bool = - if nonBlocking and chan.isEmptyUnbuf: - return false - - acquire(chan.headLock) - - if nonBlocking and chan.isEmptyUnbuf: - # Another thread was faster - release(chan.headLock) - return false - - while chan.isEmptyUnbuf: - wait(chan.notEmptyCond, chan.headLock) - - assert chan.isFullUnbuf - assert size <= chan.itemsize - - copyMem(data, chan.buffer, size) - - chan.head = 0 - - signal(chan.notFullCond) - release(chan.headLock) - result = true - -proc recvMpmc(chan: ChannelRaw, data: pointer, size: int, nonBlocking: bool): bool = - assert not chan.isNil - assert not data.isNil - - if isUnbuffered(chan): - return recvUnbufferedMpmc(chan, data, size, nonBlocking) - - if nonBlocking and chan.isEmpty: - return false - - acquire(chan.headLock) - - if nonBlocking and chan.isEmpty: - # Another thread took the last data - release(chan.headLock) - return false - - while chan.isEmpty: - wait(chan.notEmptyCond, chan.headLock) - - assert not chan.isEmpty - assert size <= chan.itemsize - - let readIdx = if chan.head < chan.size: chan.head - else: chan.head - chan.size - - copyMem(data, chan.buffer[readIdx * chan.itemsize].addr, size) - - inc chan.head - if chan.head == 2 * chan.size: - chan.head = 0 - - signal(chan.notFullCond) - release(chan.headLock) - result = true - -proc channelCloseMpmc(chan: ChannelRaw): bool = - # Unsynchronized - - if chan.isClosed: - # ChannelRaw already closed - return false - - store(chan.closed, true, moRelaxed) - result = true - -proc channelOpenMpmc(chan: ChannelRaw): bool = - # Unsynchronized - - if not chan.isClosed: - # ChannelRaw already open - return false - - store(chan.closed, false, moRelaxed) - result = true - -# Public API -# ---------------------------------------------------------------------------------- - -type - Channel*[T] = object ## Typed channels - d: ChannelRaw - -proc `=destroy`*[T](c: var Channel[T]) = - if c.d != nil: - if load(c.d.atomicCounter, moAcquire) == 0: - if c.d.buffer != nil: - freeChannel(c.d) - else: - atomicDec(c.d.atomicCounter) - -proc `=copy`*[T](dest: var Channel[T], src: Channel[T]) = - ## Shares `Channel` by reference counting. - if src.d != nil: - atomicInc(src.d.atomicCounter) - - if dest.d != nil: - `=destroy`(dest) - dest.d = src.d - -func trySend*[T](c: Channel[T], src: var Isolated[T]): bool {.inline.} = - ## Sends item to the channel(non blocking). - var data = src.extract - result = sendMpmc(c.d, data.addr, sizeof(T), true) - if result: - wasMoved(data) - -template trySend*[T](c: Channel[T], src: T): bool = - ## Helper templates for `trySend`. - trySend(c, isolate(src)) - -func tryRecv*[T](c: Channel[T], dst: var T): bool {.inline.} = - ## Receives item from the channel(non blocking). - recvMpmc(c.d, dst.addr, sizeof(T), true) - -func send*[T](c: Channel[T], src: sink Isolated[T]) {.inline.} = - ## Sends item to the channel(blocking). - var data = src.extract - discard sendMpmc(c.d, data.addr, sizeof(T), false) - wasMoved(data) - -template send*[T](c: Channel[T]; src: T) = - ## Helper templates for `send`. - send(c, isolate(src)) - -func recv*[T](c: Channel[T]): T {.inline.} = - ## Receives item from the channel(blocking). - discard recvMpmc(c.d, result.addr, sizeof(result), false) - -func open*[T](c: Channel[T]): bool {.inline.} = - result = c.d.channelOpenMpmc() - -func close*[T](c: Channel[T]): bool {.inline.} = - result = c.d.channelCloseMpmc() - -func peek*[T](c: Channel[T]): int {.inline.} = peek(c.d) - -proc newChannel*[T](elements = 30): Channel[T] = - ## Returns a new `Channel`. `elements` should be positive. - ## `elements` is used to specify whether a channel is buffered or not. - ## If `elements` = 1, the channel is unbuffered. If `elements` > 1, the - ## channel is buffered. - assert elements >= 1, "Elements must be positive!" - result = Channel[T](d: allocChannel(sizeof(T), elements)) diff --git a/tests/stdlib/tchannels.nim b/tests/stdlib/tchannels.nim deleted file mode 100644 index 492faf500..000000000 --- a/tests/stdlib/tchannels.nim +++ /dev/null @@ -1,33 +0,0 @@ -discard """ - timeout: 60.0 # but typically < 1s (in isolation but other tests running in parallel can affect this since based on epochTime) - disabled: "freebsd" - matrix: "--gc:arc --threads:on; --gc:arc --threads:on -d:danger" -""" - -when true: - # bug #17380: this was either blocking (without -d:danger) or crashing with SIGSEGV (with -d:danger) - import std/[channels, isolation] - const - N1 = 10 - N2 = 100 - var - sender: array[N1, Thread[void]] - receiver: array[5, Thread[void]] - - var chan = newChannel[seq[string]](N1 * N2) # large enough to not block - proc sendHandler() = - chan.send(isolate(@["Hello, Nim"])) - proc recvHandler() = - template fn = - let x = chan.recv() - fn() - - template benchmark() = - for t in mitems(sender): - t.createThread(sendHandler) - joinThreads(sender) - for t in mitems(receiver): - t.createThread(recvHandler) - joinThreads(receiver) - for i in 0.. 0 - -when defined(blockingTest): - const nonBlocking = false -else: - const nonBlocking = true - -type - Pthread {.importc: "pthread_t", header: "".} = distinct culong - PthreadAttr* {.byref, importc: "pthread_attr_t", header: "".} = object - Errno* = distinct cint - -proc pthread_create[T]( - thread: var Pthread, - attr: ptr PthreadAttr, # In Nim this is a var and how Nim sets a custom stack - fn: proc (x: ptr T): pointer {.thread, noconv.}, - arg: ptr T - ): Errno {.header: "".} - -proc pthread_join( - thread: Pthread, - thread_exit_status: ptr pointer - ): Errno {.header: "".} - -template channel_send_loop(chan: ChannelRaw, - data: sink pointer, - size: int, - body: untyped): untyped = - while not sendMpmc(chan, data, size, nonBlocking): - body - -template channel_receive_loop(chan: ChannelRaw, - data: pointer, - size: int, - body: untyped): untyped = - while not recvMpmc(chan, data, size, nonBlocking): - body - - -# Without threads:on or release, -# worker threads will crash on popFrame - -import std/unittest - -type ThreadArgs = object - ID: int - chan: ChannelRaw - -template Worker(id: int, body: untyped): untyped {.dirty.} = - if args.ID == id: - body - - -const Sender = 1 -const Receiver = 0 - -proc runSuite( - name: string, - fn: proc(args: ptr ThreadArgs): pointer {.noconv, gcsafe.} - ) = - var chan: ChannelRaw - - for i in Unbuffered .. Buffered: - if i == Unbuffered: - chan = allocChannel(size = 32, n = 1) - check: - peek(chan) == 0 - capacity(chan) == 1 - isBuffered(chan) == false - isUnbuffered(chan) == true - - else: - chan = allocChannel(size = int.sizeof.int, n = 7) - check: - peek(chan) == 0 - capacity(chan) == 7 - isBuffered(chan) == true - isUnbuffered(chan) == false - - var threads: array[2, Pthread] - var args = [ - ThreadArgs(ID: 0, chan: chan), - ThreadArgs(ID: 1, chan: chan) - ] - - discard pthread_create(threads[0], nil, fn, args[0].addr) - discard pthread_create(threads[1], nil, fn, args[1].addr) - - discard pthread_join(threads[0], nil) - discard pthread_join(threads[1], nil) - - freeChannel(chan) - -# ---------------------------------------------------------------------------------- - -proc thread_func(args: ptr ThreadArgs): pointer {.noconv.} = - - # Worker RECEIVER: - # --------- - # <- chan - # <- chan - # <- chan - # - # Worker SENDER: - # --------- - # chan <- 42 - # chan <- 53 - # chan <- 64 - # - - Worker(Receiver): - var val: int - for j in 0 ..< 3: - channel_receive_loop(args.chan, val.addr, val.sizeof.int): - # Busy loop, normally it should yield - discard - check: val == 42 + j*11 - - Worker(Sender): - var val: int - check: peek(args.chan) == 0 - for j in 0 ..< 3: - val = 42 + j*11 - channel_send_loop(args.chan, val.addr, val.sizeof.int): - # Busy loop, normally it should yield - discard - - return nil - -runSuite("[ChannelRaw] 2 threads can send data", thread_func) - -# ---------------------------------------------------------------------------------- - -iterator pairs(chan: ChannelRaw, T: typedesc): (int, T) = - var i = 0 - var x: T - while not isClosed(chan) or peek(chan) > 0: - let r = recvMpmc(chan, x.addr, x.sizeof.int, true) - # printf("x: %d, r: %d\n", x, r) - if r: - yield (i, x) - inc i - -proc thread_func_2(args: ptr ThreadArgs): pointer {.noconv.} = - # Worker RECEIVER: - # --------- - # <- chan until closed and empty - # - # Worker SENDER: - # --------- - # chan <- 42, 53, 64, ... - - const N = 100 - - Worker(Receiver): - for j, val in pairs(args.chan, int): - # TODO: Need special handling that doesn't allocate - # in thread with no GC - # when check fails - # - check: val == 42 + j*11 - - Worker(Sender): - var val: int - check: peek(args.chan) == 0 - for j in 0 ..< N: - val = 42 + j*11 - channel_send_loop(args.chan, val.addr, int.sizeof.int): - discard - discard channelCloseMpmc(args.chan) - - return nil - -runSuite("[ChannelRaw] channel_close, freeChannel, channelCache", thread_func_2) - -# ---------------------------------------------------------------------------------- - -proc isCached(chan: ChannelRaw): bool = - assert not chan.isNil - - var p = channelCache - while not p.isNil: - if chan.itemsize == p.chanSize and - chan.size == p.chanN: - for i in 0 ..< p.numCached: - if chan == p.cache[i]: - return true - # No more channel in cache can match - return false - p = p.next - return false - -block: # [ChannelRaw] ChannelRaw caching implementation - - # Start from clean cache slate - freeChannelCache() - - block: # Explicit caches allocation - check: - allocChannelCache(int sizeof(char), 4) - allocChannelCache(int sizeof(int), 8) - allocChannelCache(int sizeof(ptr float64), 16) - - # Don't create existing channel cache - not allocChannelCache(int sizeof(char), 4) - not allocChannelCache(int sizeof(int), 8) - not allocChannelCache(int sizeof(ptr float64), 16) - - check: - channelCacheLen == 3 - - # --------------------------------- - var chan, stash: array[10, ChannelRaw] - - block: # Implicit caches allocation - - chan[0] = allocChannel(sizeof(char), 4) - chan[1] = allocChannel(sizeof(int32), 8) - chan[2] = allocChannel(sizeof(ptr float64), 16) - - chan[3] = allocChannel(sizeof(char), 5) - chan[4] = allocChannel(sizeof(int64), 8) - chan[5] = allocChannel(sizeof(ptr float32), 24) - - # We have caches ready to store specific channel kinds - check: channelCacheLen == 6 # Cumulated with previous test - # But they are not in cache while in use - check: - not chan[0].isCached - not chan[1].isCached - not chan[2].isCached - not chan[3].isCached - not chan[4].isCached - not chan[5].isCached - - block: # Freed channels are returned to cache - stash[0..5] = chan.toOpenArray(0, 5) - for i in 0 .. 5: - # Free the channels - freeChannel(chan[i]) - - check: - stash[0].isCached - stash[1].isCached - stash[2].isCached - stash[3].isCached - stash[4].isCached - stash[5].isCached - - block: # Cached channels are being reused - - chan[6] = allocChannel(sizeof(char), 4) - chan[7] = allocChannel(sizeof(int32), 8) - chan[8] = allocChannel(sizeof(ptr float32), 16) - chan[9] = allocChannel(sizeof(ptr float64), 16) - - # All (itemsize, queue size, implementation) were already allocated - check: channelCacheLen == 6 - - # We reused old channels from cache - check: - chan[6] == stash[0] - chan[7] == stash[1] - chan[8] == stash[2] - # chan[9] - required a fresh alloc - - block: # Clearing the cache - - stash[6..9] = chan.toOpenArray(6, 9) - - for i in 6 .. 9: - freeChannel(chan[i]) - - check: - stash[6].isCached - stash[7].isCached - stash[8].isCached - stash[9].isCached - - freeChannelCache() - - # Check that nothing is cached anymore - for i in 0 .. 9: - check: not stash[i].isCached - # And length is reset to 0 - check: channelCacheLen == 0 - - # Cache can grow again - chan[0] = allocChannel(sizeof((int, float, int32, uint)), 1) - chan[1] = allocChannel(sizeof(int32), 0) - chan[2] = allocChannel(sizeof(int32), 0) - - check: channelCacheLen == 2 - - # Interleave cache clear and channel free - freeChannelCache() - check: channelCacheLen == 0 - - freeChannel(chan[0]) - freeChannel(chan[1]) - freeChannel(chan[2]) diff --git a/tests/stdlib/tchannels_simple.nim b/tests/stdlib/tchannels_simple.nim deleted file mode 100644 index 56e5fb8f1..000000000 --- a/tests/stdlib/tchannels_simple.nim +++ /dev/null @@ -1,67 +0,0 @@ -discard """ - matrix: "--threads:on --gc:orc; --threads:on --gc:arc" - disabled: "freebsd" -""" - -import std/channels -import std/os - -var chan = newChannel[string]() - -# This proc will be run in another thread using the threads module. -proc firstWorker() = - chan.send("Hello World!") - -# This is another proc to run in a background thread. This proc takes a while -# to send the message since it sleeps for 2 seconds (or 2000 milliseconds). -proc secondWorker() = - sleep(2000) - chan.send("Another message") - - -# Launch the worker. -var worker1: Thread[void] -createThread(worker1, firstWorker) - -# Block until the message arrives, then print it out. -let dest = chan.recv() -doAssert dest == "Hello World!" - -# Wait for the thread to exit before moving on to the next example. -worker1.joinThread() - -# Launch the other worker. -var worker2: Thread[void] -createThread(worker2, secondWorker) -# This time, use a non-blocking approach with tryRecv. -# Since the main thread is not blocked, it could be used to perform other -# useful work while it waits for data to arrive on the channel. - -var messages: seq[string] -var msg = "" -while true: - let tried = chan.tryRecv(msg) - if tried: - messages.add move(msg) - break - - messages.add "Pretend I'm doing useful work..." - # For this example, sleep in order not to flood stdout with the above - # message. - sleep(400) - -# Wait for the second thread to exit before cleaning up the channel. -worker2.joinThread() - -# Clean up the channel. -doAssert chan.close() -doAssert messages[^1] == "Another message" -doAssert messages.len >= 2 - - -block: - let chan0 = newChannel[int]() - let chan1 = chan0 - block: - let chan3 = chan0 - let chan4 = chan0 From e8dad482a309b1c33056aba22550d691845414d7 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 4 Sep 2021 15:57:16 +0200 Subject: [PATCH 2494/3768] fixes #16246 (#18800) --- compiler/seminst.nim | 3 ++- compiler/semtypinst.nim | 2 +- compiler/types.nim | 8 ++++++++ tests/arc/tarcmisc.nim | 11 +++++++++++ 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/compiler/seminst.nim b/compiler/seminst.nim index e1fad236c..456e40a94 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -91,7 +91,8 @@ proc sameInstantiation(a, b: TInstantiation): bool = for i in 0..a.concreteTypes.high: if not compareTypes(a.concreteTypes[i], b.concreteTypes[i], flags = {ExactTypeDescValues, - ExactGcSafety}): return + ExactGcSafety, + PickyCAliases}): return result = true proc genericCacheGet(g: ModuleGraph; genericSym: PSym, entry: TInstantiation; diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 18febb52e..9f9eb9489 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -51,7 +51,7 @@ proc searchInstTypes*(g: ModuleGraph; key: PType): PType = for j in 1..high(key.sons): # XXX sameType is not really correct for nested generics? if not compareTypes(inst[j], key[j], - flags = {ExactGenericParams}): + flags = {ExactGenericParams, PickyCAliases}): break matchType return inst diff --git a/compiler/types.nim b/compiler/types.nim index 61f563514..cf6579317 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -896,6 +896,7 @@ type ExactConstraints ExactGcSafety AllowCommonBase + PickyCAliases # be picky about the distinction between 'cint' and 'int32' TTypeCmpFlags* = set[TTypeCmpFlag] @@ -1144,6 +1145,13 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = of tyEmpty, tyChar, tyBool, tyNil, tyPointer, tyString, tyCstring, tyInt..tyUInt64, tyTyped, tyUntyped, tyVoid: result = sameFlags(a, b) + if result and PickyCAliases in c.flags: + # additional requirement for the caching of generics for importc'ed types: + # the symbols must be identical too: + let symFlagsA = if a.sym != nil: a.sym.flags else: {} + let symFlagsB = if b.sym != nil: b.sym.flags else: {} + if (symFlagsA+symFlagsB) * {sfImportc, sfExportc} != {}: + result = symFlagsA == symFlagsB of tyStatic, tyFromExpr: result = exprStructuralEquivalent(a.n, b.n) and sameFlags(a, b) if result and a.len == b.len and a.len == 1: diff --git a/tests/arc/tarcmisc.nim b/tests/arc/tarcmisc.nim index 7daea62c8..951ae25f3 100644 --- a/tests/arc/tarcmisc.nim +++ b/tests/arc/tarcmisc.nim @@ -463,3 +463,14 @@ proc putValue[T](n: T) = echo b.n useForward() + + +# bug #16246 + +proc testWeirdTypeAliases() = + var values = newSeq[cuint](8) + # var values: seq[cuint] does not produce codegen error + var drawCb = proc(): seq[uint32] = + result = newSeq[uint32](10) + +testWeirdTypeAliases() From 5c85e480a6a13ac12c69f7e5500322eb5474be11 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 4 Sep 2021 17:49:27 +0200 Subject: [PATCH 2495/3768] unicode operator bugfixes (#18802) --- compiler/lexer.nim | 106 ++++++++++++++++------------- tests/lexer/tunicode_operators.nim | 4 +- 2 files changed, 63 insertions(+), 47 deletions(-) diff --git a/compiler/lexer.nim b/compiler/lexer.nim index ddf98661a..ede16fdf1 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -838,50 +838,6 @@ proc getCharacter(L: var Lexer; tok: var Token) = lexMessage(L, errGenerated, "missing closing ' for character literal") tokenEndIgnore(tok, L.bufpos) -proc getSymbol(L: var Lexer, tok: var Token) = - var h: Hash = 0 - var pos = L.bufpos - tokenBegin(tok, pos) - var suspicious = false - while true: - var c = L.buf[pos] - case c - of 'a'..'z', '0'..'9', '\x80'..'\xFF': - h = h !& ord(c) - inc(pos) - of 'A'..'Z': - c = chr(ord(c) + (ord('a') - ord('A'))) # toLower() - h = h !& ord(c) - inc(pos) - suspicious = true - of '_': - if L.buf[pos+1] notin SymChars: - lexMessage(L, errGenerated, "invalid token: trailing underscore") - break - inc(pos) - suspicious = true - else: break - tokenEnd(tok, pos-1) - h = !$h - tok.ident = L.cache.getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h) - if (tok.ident.id < ord(tokKeywordLow) - ord(tkSymbol)) or - (tok.ident.id > ord(tokKeywordHigh) - ord(tkSymbol)): - tok.tokType = tkSymbol - else: - tok.tokType = TokType(tok.ident.id + ord(tkSymbol)) - if suspicious and {optStyleHint, optStyleError} * L.config.globalOptions != {}: - lintReport(L.config, getLineInfo(L), tok.ident.s.normalize, tok.ident.s) - L.bufpos = pos - - -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) - 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 - const UnicodeOperatorStartChars = {'\226', '\194', '\195'} # the allowed unicode characters ("∙ ∘ × ★ ⊗ ⊘ ⊙ ⊛ ⊠ ⊡ ∩ ∧ ⊓ ± ⊕ ⊖ ⊞ ⊟ ∪ ∨ ⊔") @@ -925,6 +881,56 @@ proc unicodeOprLen(buf: cstring; pos: int): (int8, UnicodeOprPred) = else: discard +proc getSymbol(L: var Lexer, tok: var Token) = + var h: Hash = 0 + var pos = L.bufpos + tokenBegin(tok, pos) + var suspicious = false + while true: + var c = L.buf[pos] + case c + of 'a'..'z', '0'..'9': + h = h !& ord(c) + inc(pos) + of 'A'..'Z': + c = chr(ord(c) + (ord('a') - ord('A'))) # toLower() + h = h !& ord(c) + inc(pos) + suspicious = true + of '_': + if L.buf[pos+1] notin SymChars: + lexMessage(L, errGenerated, "invalid token: trailing underscore") + break + inc(pos) + suspicious = true + of '\x80'..'\xFF': + if c in UnicodeOperatorStartChars and unicodeOperators in L.config.features and unicodeOprLen(L.buf, pos)[0] != 0: + break + else: + h = h !& ord(c) + inc(pos) + else: break + tokenEnd(tok, pos-1) + h = !$h + tok.ident = L.cache.getIdent(addr(L.buf[L.bufpos]), pos - L.bufpos, h) + if (tok.ident.id < ord(tokKeywordLow) - ord(tkSymbol)) or + (tok.ident.id > ord(tokKeywordHigh) - ord(tkSymbol)): + tok.tokType = tkSymbol + else: + tok.tokType = TokType(tok.ident.id + ord(tkSymbol)) + if suspicious and {optStyleHint, optStyleError} * L.config.globalOptions != {}: + lintReport(L.config, getLineInfo(L), tok.ident.s.normalize, tok.ident.s) + L.bufpos = pos + + +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) + 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 + proc getOperator(L: var Lexer, tok: var Token) = var pos = L.bufpos tokenBegin(tok, pos) @@ -1346,7 +1352,11 @@ proc rawGetTok*(L: var Lexer, tok: var Token) = getNumber(L, tok) let c = L.buf[L.bufpos] if c in SymChars+{'_'}: - lexMessage(L, errGenerated, "invalid token: no whitespace between number and identifier") + if c in UnicodeOperatorStartChars and unicodeOperators in L.config.features and + unicodeOprLen(L.buf, L.bufpos)[0] != 0: + discard + else: + lexMessage(L, errGenerated, "invalid token: no whitespace between number and identifier") of '-': if L.buf[L.bufpos+1] in {'0'..'9'} and (L.bufpos-1 == 0 or L.buf[L.bufpos-1] in UnaryMinusWhitelist): @@ -1357,7 +1367,11 @@ proc rawGetTok*(L: var Lexer, tok: var Token) = getNumber(L, tok) let c = L.buf[L.bufpos] if c in SymChars+{'_'}: - lexMessage(L, errGenerated, "invalid token: no whitespace between number and identifier") + if c in UnicodeOperatorStartChars and unicodeOperators in L.config.features and + unicodeOprLen(L.buf, L.bufpos)[0] != 0: + discard + else: + lexMessage(L, errGenerated, "invalid token: no whitespace between number and identifier") else: getOperator(L, tok) else: diff --git a/tests/lexer/tunicode_operators.nim b/tests/lexer/tunicode_operators.nim index 74fcbb763..6ad40beab 100644 --- a/tests/lexer/tunicode_operators.nim +++ b/tests/lexer/tunicode_operators.nim @@ -5,8 +5,10 @@ proc `⊙=`(x: var int, y: int) = x *= y proc `⊞++`(x, y: int): int = x + y +const a = 9 + var x = 45 -x ⊙= 9 ⊞++ 4 ⊙ 3 +x ⊙= a⊞++4⊙3 var y = 45 y *= 9 + 4 * 3 From 2e2d9288465709d84a5d91abbb035583c23efe45 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 4 Sep 2021 19:52:57 +0200 Subject: [PATCH 2496/3768] formal grammar updates [backport:1.2] (#18803) --- compiler/parser.nim | 50 +++++++++++++++++++---------------------- doc/grammar.txt | 37 +++++++++++++----------------- tools/grammar_nanny.nim | 1 + 3 files changed, 40 insertions(+), 48 deletions(-) diff --git a/compiler/parser.nim b/compiler/parser.nim index 225493902..7d3c913da 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -275,12 +275,6 @@ proc isRightAssociative(tok: Token): bool {.inline.} = result = tok.tokType == tkOpr and tok.ident.s[0] == '^' # or (tok.ident.s.len > 1 and tok.ident.s[^1] == '>') -proc isOperator(tok: Token): bool = - ## Determines if the given token is an operator type token. - tok.tokType in {tkOpr, tkDiv, tkMod, tkShl, tkShr, tkIn, tkNotin, tkIs, - tkIsnot, tkNot, tkOf, tkAs, tkFrom, tkDotDot, tkAnd, - tkOr, tkXor} - proc isUnary(tok: Token): bool = ## Check if the given token is a unary operator tok.tokType in {tkOpr, tkDotDot} and @@ -323,6 +317,14 @@ proc checkBinary(p: Parser) {.inline.} = #| mulExpr = dollarExpr (OP9 optInd dollarExpr)* #| dollarExpr = primary (OP10 optInd primary)* +proc isOperator(tok: Token): bool = + #| operatorB = OP0 | OP1 | OP2 | OP3 | OP4 | OP5 | OP6 | OP7 | OP8 | OP9 | + #| 'div' | 'mod' | 'shl' | 'shr' | 'in' | 'notin' | + #| 'is' | 'isnot' | 'not' | 'of' | 'as' | 'from' | '..' | 'and' | 'or' | 'xor' + tok.tokType in {tkOpr, tkDiv, tkMod, tkShl, tkShr, tkIn, tkNotin, tkIs, + tkIsnot, tkNot, tkOf, tkAs, tkFrom, tkDotDot, tkAnd, + tkOr, tkXor} + proc colcom(p: var Parser, n: PNode) = eat(p, tkColon) skipComment(p, n) @@ -447,8 +449,6 @@ proc exprColonEqExprList(p: var Parser, kind: TNodeKind, exprColonEqExprListAux(p, endTok, result) proc dotExpr(p: var Parser, a: PNode): PNode = - #| dotExpr = expr '.' optInd (symbol | '[:' exprList ']') - #| explicitGenericInstantiation = '[:' exprList ']' ( '(' exprColonEqExpr ')' )? var info = p.parLineInfo getTok(p) result = newNodeI(nkDotExpr, info) @@ -469,7 +469,6 @@ proc dotExpr(p: var Parser, a: PNode): PNode = result = y proc dotLikeExpr(p: var Parser, a: PNode): PNode = - #| dotLikeExpr = expr DOTLIKEOP optInd symbol var info = p.parLineInfo result = newNodeI(nkInfix, info) optInd(p, result) @@ -661,7 +660,7 @@ proc identOrLiteral(p: var Parser, mode: PrimaryMode): PNode = #| | NIL #| generalizedLit = GENERALIZED_STR_LIT | GENERALIZED_TRIPLESTR_LIT #| identOrLiteral = generalizedLit | symbol | literal - #| | par | arrayConstr | setOrTableConstr + #| | par | arrayConstr | setOrTableConstr | tupleConstr #| | castExpr #| tupleConstr = '(' optInd (exprColonEqExpr comma?)* optPar ')' #| arrayConstr = '[' optInd (exprColonEqExpr comma?)* optPar ']' @@ -808,7 +807,7 @@ proc isDotLike(tok: Token): bool = proc primarySuffix(p: var Parser, r: PNode, baseIndent: int, mode: PrimaryMode): PNode = #| primarySuffix = '(' (exprColonEqExpr comma?)* ')' - #| | '.' optInd symbol generalizedLit? + #| | '.' optInd symbol ('[:' exprList ']' ( '(' exprColonEqExpr ')' )?)? generalizedLit? #| | DOTLIKEOP optInd symbol generalizedLit? #| | '[' optInd exprColonEqExprList optPar ']' #| | '{' optInd exprColonEqExprList optPar '}' @@ -1013,11 +1012,9 @@ proc parseIdentColonEquals(p: var Parser, flags: DeclaredIdentFlags): PNode = result.add(newNodeP(nkEmpty, p)) proc parseTuple(p: var Parser, indentAllowed = false): PNode = - #| inlTupleDecl = 'tuple' - #| '[' optInd (identColonEquals (comma/semicolon)?)* optPar ']' - #| extTupleDecl = 'tuple' + #| tupleDecl = 'tuple' + #| '[' optInd (identColonEquals (comma/semicolon)?)* optPar ']' | #| COMMENT? (IND{>} identColonEquals (IND{=} identColonEquals)*)? - #| tupleClass = 'tuple' result = newNodeP(nkTupleTy, p) getTok(p) if p.tok.tokType == tkBracketLe: @@ -1123,7 +1120,7 @@ proc parseDoBlock(p: var Parser; info: TLineInfo): PNode = genericParams = p.emptyNode, pragmas = pragmas, exceptions = p.emptyNode) proc parseProcExpr(p: var Parser; isExpr: bool; kind: TNodeKind): PNode = - #| procExpr = 'proc' paramListColon pragma? ('=' COMMENT? stmt)? + #| routineExpr = ('proc' | 'func' | 'iterator') paramListColon pragma? ('=' COMMENT? stmt)? # either a proc type or a anonymous proc let info = parLineInfo(p) getTok(p) @@ -1165,7 +1162,6 @@ proc parseSymbolList(p: var Parser, result: PNode) = proc parseTypeDescKAux(p: var Parser, kind: TNodeKind, mode: PrimaryMode): PNode = - #| distinct = 'distinct' optInd typeDesc result = newNodeP(kind, p) getTok(p) if p.tok.indent != -1 and p.tok.indent <= p.currInd: return @@ -1254,12 +1250,14 @@ proc parseObject(p: var Parser): PNode proc parseTypeClass(p: var Parser): PNode proc primary(p: var Parser, mode: PrimaryMode): PNode = - #| typeKeyw = 'var' | 'out' | 'ref' | 'ptr' | 'shared' | 'tuple' - #| | 'proc' | 'iterator' | 'distinct' | 'object' | 'enum' - #| primary = typeKeyw optInd typeDesc + #| primary = operatorB primary primarySuffix* | + #| tupleDecl | routineExpr | enumDecl + #| objectDecl | conceptDecl | ('bind' primary) + #| ('var' | 'out' | 'ref' | 'ptr' | 'distinct') primary #| / prefixOperator* identOrLiteral primarySuffix* - #| / 'bind' primary if isOperator(p.tok): + # Note 'sigil like' operators are currently not reflected in the grammar + # and should be removed for Nim 2.0, I don't think anybody uses them. let isSigil = isSigilLike(p.tok) result = newNodeP(nkPrefix, p) var a = newIdentNodeP(p.tok.ident, p) @@ -1342,7 +1340,6 @@ proc parseTypeDesc(p: var Parser): PNode = proc parseTypeDefAux(p: var Parser): PNode = #| typeDefAux = simpleExpr ('not' expr)? - #| | 'concept' typeClass result = simpleExpr(p, pmTypeDef) result = binaryNot(p, result) @@ -1695,7 +1692,6 @@ proc parseTry(p: var Parser; isExpr: bool): PNode = if b == nil: parMessage(p, "expected 'except'") proc parseExceptBlock(p: var Parser, kind: TNodeKind): PNode = - #| exceptBlock = 'except' colcom stmt result = newNodeP(kind, p) getTok(p) colcom(p, result) @@ -1867,7 +1863,7 @@ proc parseSection(p: var Parser, kind: TNodeKind, parMessage(p, errIdentifierExpected, p.tok) proc parseEnum(p: var Parser): PNode = - #| enum = 'enum' optInd (symbol pragma? optInd ('=' optInd expr COMMENT?)? comma?)+ + #| enumDecl = 'enum' optInd (symbol pragma? optInd ('=' optInd expr COMMENT?)? comma?)+ result = newNodeP(nkEnumTy, p) getTok(p) result.add(p.emptyNode) @@ -2015,7 +2011,7 @@ proc parseObjectPart(p: var Parser): PNode = result = p.emptyNode proc parseObject(p: var Parser): PNode = - #| object = 'object' pragma? ('of' typeDesc)? COMMENT? objectPart + #| objectDecl = 'object' pragma? ('of' typeDesc)? COMMENT? objectPart result = newNodeP(nkObjectTy, p) getTok(p) if p.tok.tokType == tkCurlyDotLe and p.validInd: @@ -2057,8 +2053,8 @@ proc parseTypeClassParam(p: var Parser): PNode = result = p.parseSymbol proc parseTypeClass(p: var Parser): PNode = - #| typeClassParam = ('var' | 'out')? symbol - #| typeClass = typeClassParam ^* ',' (pragma)? ('of' typeDesc ^* ',')? + #| conceptParam = ('var' | 'out')? symbol + #| conceptDecl = 'concept' conceptParam ^* ',' (pragma)? ('of' typeDesc ^* ',')? #| &IND{>} stmt result = newNodeP(nkTypeClassTy, p) getTok(p) diff --git a/doc/grammar.txt b/doc/grammar.txt index f58621b97..8f86dd98c 100644 --- a/doc/grammar.txt +++ b/doc/grammar.txt @@ -22,14 +22,14 @@ ampExpr = plusExpr (OP7 optInd plusExpr)* plusExpr = mulExpr (OP8 optInd mulExpr)* mulExpr = dollarExpr (OP9 optInd dollarExpr)* dollarExpr = primary (OP10 optInd primary)* +operatorB = OP0 | OP1 | OP2 | OP3 | OP4 | OP5 | OP6 | OP7 | OP8 | OP9 | + 'div' | 'mod' | 'shl' | 'shr' | 'in' | 'notin' | + 'is' | 'isnot' | 'not' | 'of' | 'as' | 'from' | '..' | 'and' | 'or' | 'xor' symbol = '`' (KEYW|IDENT|literal|(operator|'('|')'|'['|']'|'{'|'}'|'=')+)+ '`' | IDENT | KEYW exprColonEqExpr = expr (':'|'=' expr)? exprList = expr ^+ comma exprColonEqExprList = exprColonEqExpr (comma exprColonEqExpr)* (comma)? -dotExpr = expr '.' optInd (symbol | '[:' exprList ']') -explicitGenericInstantiation = '[:' exprList ']' ( '(' exprColonEqExpr ')' )? -dotLikeExpr = expr DOTLIKEOP optInd symbol qualifiedIdent = symbol ('.' optInd symbol)? setOrTableConstr = '{' ((exprColonEqExpr comma)* | ':' ) '}' castExpr = 'cast' ('[' optInd typeDesc optPar ']' '(' optInd expr optPar ')') / @@ -51,12 +51,12 @@ literal = | INT_LIT | INT8_LIT | INT16_LIT | INT32_LIT | INT64_LIT | NIL generalizedLit = GENERALIZED_STR_LIT | GENERALIZED_TRIPLESTR_LIT identOrLiteral = generalizedLit | symbol | literal - | par | arrayConstr | setOrTableConstr + | par | arrayConstr | setOrTableConstr | tupleConstr | castExpr tupleConstr = '(' optInd (exprColonEqExpr comma?)* optPar ')' arrayConstr = '[' optInd (exprColonEqExpr comma?)* optPar ']' primarySuffix = '(' (exprColonEqExpr comma?)* ')' - | '.' optInd symbol generalizedLit? + | '.' optInd symbol ('[:' exprList ']' ( '(' exprColonEqExpr ')' )?)? generalizedLit? | DOTLIKEOP optInd symbol generalizedLit? | '[' optInd exprColonEqExprList optPar ']' | '{' optInd exprColonEqExprList optPar '}' @@ -70,17 +70,14 @@ declColonEquals = identWithPragma (comma identWithPragma)* comma? (':' optInd typeDesc)? ('=' optInd expr)? identColonEquals = IDENT (comma IDENT)* comma? (':' optInd typeDesc)? ('=' optInd expr)?) -inlTupleDecl = 'tuple' - '[' optInd (identColonEquals (comma/semicolon)?)* optPar ']' -extTupleDecl = 'tuple' +tupleDecl = 'tuple' + '[' optInd (identColonEquals (comma/semicolon)?)* optPar ']' | COMMENT? (IND{>} identColonEquals (IND{=} identColonEquals)*)? -tupleClass = 'tuple' paramList = '(' declColonEquals ^* (comma/semicolon) ')' paramListArrow = paramList? ('->' optInd typeDesc)? paramListColon = paramList? (':' optInd typeDesc)? doBlock = 'do' paramListArrow pragma? colcom stmt -procExpr = 'proc' paramListColon pragma? ('=' COMMENT? stmt)? -distinct = 'distinct' optInd typeDesc +routineExpr = ('proc' | 'func' | 'iterator') paramListColon pragma? ('=' COMMENT? stmt)? forStmt = 'for' (identWithPragma ^+ comma) 'in' expr colcom stmt forExpr = forStmt expr = (blockExpr @@ -90,14 +87,13 @@ expr = (blockExpr | forExpr | tryExpr) / simpleExpr -typeKeyw = 'var' | 'out' | 'ref' | 'ptr' | 'shared' | 'tuple' - | 'proc' | 'iterator' | 'distinct' | 'object' | 'enum' -primary = typeKeyw optInd typeDesc +primary = operatorB primary primarySuffix* | + tupleDecl | routineExpr | enumDecl + objectDecl | conceptDecl | ('bind' primary) + ('var' | 'out' | 'ref' | 'ptr' | 'distinct') primary / prefixOperator* identOrLiteral primarySuffix* - / 'bind' primary typeDesc = simpleExpr ('not' expr)? typeDefAux = simpleExpr ('not' expr)? - | 'concept' typeClass postExprBlocks = ':' stmt? ( IND{=} doBlock | IND{=} 'of' exprList ':' stmt | IND{=} 'elif' expr ':' stmt @@ -147,7 +143,6 @@ tryStmt = 'try' colcom stmt &(IND{=}? 'except'|'finally') tryExpr = 'try' colcom stmt &(optInd 'except'|'finally') (optInd 'except' exprList colcom stmt)* (optInd 'finally' colcom stmt)? -exceptBlock = 'except' colcom stmt blockStmt = 'block' symbol? colcom stmt blockExpr = 'block' symbol? colcom stmt staticStmt = 'static' colcom stmt @@ -162,7 +157,7 @@ routine = optInd identVis pattern? genericParamList? paramListColon pragma? ('=' COMMENT? stmt)? indAndComment commentStmt = COMMENT section(RULE) = COMMENT? RULE / (IND{>} (RULE / COMMENT)^+IND{=} DED) -enum = 'enum' optInd (symbol pragma? optInd ('=' optInd expr COMMENT?)? comma?)+ +enumDecl = 'enum' optInd (symbol pragma? optInd ('=' optInd expr COMMENT?)? comma?)+ objectWhen = 'when' expr colcom objectPart COMMENT? ('elif' expr colcom objectPart COMMENT?)* ('else' colcom objectPart COMMENT?)? @@ -175,9 +170,9 @@ objectCase = 'case' identWithPragma ':' typeDesc ':'? COMMENT? | IND{=} objectBranches) objectPart = IND{>} objectPart^+IND{=} DED / objectWhen / objectCase / 'nil' / 'discard' / declColonEquals -object = 'object' pragma? ('of' typeDesc)? COMMENT? objectPart -typeClassParam = ('var' | 'out')? symbol -typeClass = typeClassParam ^* ',' (pragma)? ('of' typeDesc ^* ',')? +objectDecl = 'object' pragma? ('of' typeDesc)? COMMENT? objectPart +conceptParam = ('var' | 'out')? symbol +conceptDecl = 'concept' conceptParam ^* ',' (pragma)? ('of' typeDesc ^* ',')? &IND{>} stmt typeDef = identWithPragmaDot genericParamList? '=' optInd typeDefAux indAndComment? / identVisDot genericParamList? pragma '=' optInd typeDefAux diff --git a/tools/grammar_nanny.nim b/tools/grammar_nanny.nim index 5120ef976..502412c3c 100644 --- a/tools/grammar_nanny.nim +++ b/tools/grammar_nanny.nim @@ -13,6 +13,7 @@ proc checkGrammarFileImpl(cache: IdentCache, config: ConfigRef) = var stream = llStreamOpen(data) var declaredSyms = initHashSet[string]() var usedSyms = initHashSet[string]() + usedSyms.incl "module" # 'module' is the start rule. if stream != nil: declaredSyms.incl "section" # special case for 'section(RULE)' in the grammar var From 4320b158dd2e5d7420cc813a1e9e2aecc946de7d Mon Sep 17 00:00:00 2001 From: flywind Date: Sun, 5 Sep 2021 16:50:54 +0800 Subject: [PATCH 2497/3768] add weave to important packages (#17665) * add weave to important packages * Update testament/important_packages.nim --- testament/important_packages.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index ab29dcd0d..a91246562 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -158,6 +158,7 @@ pkg "tiny_sqlite" pkg "unicodedb", "nim c -d:release -r tests/tests.nim" pkg "unicodeplus", "nim c -d:release -r tests/tests.nim" pkg "unpack" +pkg "weave", "nimble test_gc_arc", allowFailure = true pkg "websocket", "nim c websocket.nim" pkg "winim", allowFailure = true pkg "with" From f373c17ad926b669bb3b5819ae1dff4bde1da88a Mon Sep 17 00:00:00 2001 From: Miran Date: Mon, 6 Sep 2021 09:40:31 +0200 Subject: [PATCH 2498/3768] use new Nimble, with lockfiles (#18810) --- koch.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/koch.nim b/koch.nim index 8dd897028..e42ea53de 100644 --- a/koch.nim +++ b/koch.nim @@ -10,7 +10,7 @@ # const - NimbleStableCommit = "d13f3b8ce288b4dc8c34c219a4e050aaeaf43fc9" # master + NimbleStableCommit = "795704833ddfd0cdaefb45c60551d3ea205279ef" # master # examples of possible values: #head, #ea82b54, 1.2.3 FusionStableHash = "#372ee4313827ef9f2ea388840f7d6b46c2b1b014" HeadHash = "#head" From 7ae52d779184cd8a16b760e384777a75ed54464b Mon Sep 17 00:00:00 2001 From: Jason Beetham Date: Mon, 6 Sep 2021 01:43:26 -0600 Subject: [PATCH 2499/3768] Fix recursive generic typed defs (#18809) --- compiler/semtypinst.nim | 2 +- tests/generics/trecursivegenerics.nim | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 tests/generics/trecursivegenerics.nim diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 9f9eb9489..01b21498b 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -378,7 +378,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = cl.typeMap = newTypeMapLayer(cl) for i in 1.. Date: Mon, 6 Sep 2021 13:12:14 +0200 Subject: [PATCH 2500/3768] fixes #14165, fixes #18739, fix the second example of #6269 (#18812) --- compiler/transf.nim | 11 ++++++---- tests/iter/titer.nim | 50 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/compiler/transf.nim b/compiler/transf.nim index 3250f2ec4..5846e6e3b 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -738,7 +738,7 @@ proc transformFor(c: PTransf, n: PNode): PNode = pushInfoContext(c.graph.config, n.info) inc(c.inlining) stmtList.add(transform(c, body)) - #findWrongOwners(c, stmtList.pnode) + #findWrongOwners(c, stmtList.PNode) dec(c.inlining) popInfoContext(c.graph.config) popTransCon(c) @@ -1019,10 +1019,11 @@ proc transform(c: PTransf, n: PNode): PNode = of nkAsgn: result = transformAsgn(c, n) of nkIdentDefs, nkConstDef: - result = n + result = newTransNode(n) result[0] = transform(c, n[0]) # Skip the second son since it only contains an unsemanticized copy of the # variable type used by docgen + result[1] = n[1] result[2] = transform(c, n[2]) # XXX comment handling really sucks: if importantComments(c.graph.config): @@ -1033,8 +1034,10 @@ proc transform(c: PTransf, n: PNode): PNode = # (bug #2604). We need to patch this environment here too: let a = n[1] if a.kind == nkSym: - n[1] = transformSymAux(c, a) - return n + result = copyTree(n) + result[1] = transformSymAux(c, a) + else: + result = n of nkExceptBranch: result = transformExceptBranch(c, n) of nkCheckedFieldExpr: diff --git a/tests/iter/titer.nim b/tests/iter/titer.nim index 8e1c13e82..3a2450ae3 100644 --- a/tests/iter/titer.nim +++ b/tests/iter/titer.nim @@ -62,3 +62,53 @@ type Rule[T] = (int, T) var t: seq[Rule[int]] for (c, t) in t: discard + + + +import std/sugar + +# bug #14165 +iterator log_nodups_hamming(): int {.inline.} = + let lb3 = 1 + let lb4 = 123 + proc mul3(): int = lb3 + lb4 + yield mul3() + +for h in log_nodups_hamming(): + break +for h in log_nodups_hamming(): + break +for h in log_nodups_hamming(): + break + +# bug #18536 +iterator envPairs*(): int = + var foo: seq[int] + proc fun() = + foo = @[] + fun() + yield 3 + +proc main() = + for a in envPairs(): + discard + for a in envPairs(): + discard +static: main() +main() + +# bug #6269 +iterator makeFn(outer_val: int): proc(a: int): int = + for i in 0..1: + yield proc(a:int): int = + return a + i.int + +let v1 = 42 + +let res = collect: + for fn1 in makeFn(v1): + let v2 = fn1(v1) + for fn2 in makeFn(v2): + fn2(v2) + +doAssert res == @[42, 43, 43, 44] From cc5422ae5070a2cdd69dbd5b78663305623a0fbc Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 6 Sep 2021 13:19:11 +0200 Subject: [PATCH 2501/3768] Atlas: added 'extract' command, as promised (#18813) --- tools/atlas/atlas.nim | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tools/atlas/atlas.nim b/tools/atlas/atlas.nim index 761ca5db8..370138029 100644 --- a/tools/atlas/atlas.nim +++ b/tools/atlas/atlas.nim @@ -9,7 +9,7 @@ ## Simple tool to automate frequent workflows: Can "clone" ## a Nimble dependency and its dependencies recursively. -import std/[parseopt, strutils, os, osproc, unicode, tables, sets] +import std/[parseopt, strutils, os, osproc, unicode, tables, sets, json, jsonutils] import parse_requires, osutils, packagesjson const @@ -22,6 +22,8 @@ Usage: Command: clone url|pkgname clone a package and all of its dependencies search keyw keywB... search for package that contains the given keywords + extract file.nimble extract the requirements and custom commands from + the given Nimble file Options: --keepCommits do not perform any `git checkouts` @@ -460,6 +462,12 @@ proc main = of "search", "list": updatePackages(c) search getPackages(c.workspace), args + of "extract": + singleArg() + if fileExists(args[0]): + echo toJson(extractRequiresInfo(args[0])) + else: + error "File does not exist: " & args[0] else: error "Invalid action: " & action From 90bfd342504fd1a6a9e4c8f232bf7c35eab92f82 Mon Sep 17 00:00:00 2001 From: Jason Beetham Date: Mon, 6 Sep 2021 07:30:49 -0600 Subject: [PATCH 2502/3768] '[]' can now be used for iterators (#18814) --- compiler/semexprs.nim | 2 +- tests/iter/tarrayiter.nim | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 tests/iter/tarrayiter.nim diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 3edeae324..c0a2a346a 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1596,7 +1596,7 @@ proc semArrayAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = result = semSubscript(c, n, flags) if result == nil: # overloaded [] operator: - result = semExpr(c, buildOverloadedSubscripts(n, getIdent(c.cache, "[]"))) + result = semExpr(c, buildOverloadedSubscripts(n, getIdent(c.cache, "[]")), flags) proc propertyWriteAccess(c: PContext, n, nOrig, a: PNode): PNode = var id = considerQuotedIdent(c, a[1], a) diff --git a/tests/iter/tarrayiter.nim b/tests/iter/tarrayiter.nim new file mode 100644 index 000000000..eb7ba591a --- /dev/null +++ b/tests/iter/tarrayiter.nim @@ -0,0 +1,14 @@ +block: + iterator `[]`(a: int, r: int): int = + for q in 0 .. r: + yield a + + for val in 10[2]: discard + + type Custom = distinct string + + iterator `[]`(a: Custom, r: int): char = + for q in 0 .. r: + yield a.string[q] + + for val in Custom("test")[2]: discard \ No newline at end of file From 34a53e804943ceaf3900feb6e89194aa03236c0a Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 6 Sep 2021 17:43:03 +0200 Subject: [PATCH 2503/3768] fixes #12642 (#18811) * fixes #12642 * update important packages; refs #18804 * fixes #18805; refs #18806 * fixes a regression * Update testament/categories.nim Co-authored-by: flywind * progress * progress Co-authored-by: flywind --- compiler/ccgcalls.nim | 1 + compiler/sem.nim | 6 ++++++ compiler/transf.nim | 5 +++-- testament/categories.nim | 6 ++++-- testament/important_packages.nim | 5 ++--- tests/effects/teffects6.nim | 20 ++++++++++++++++++++ 6 files changed, 36 insertions(+), 7 deletions(-) diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 828e666a8..f6cc6da74 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -369,6 +369,7 @@ proc genParams(p: BProc, ri: PNode, typ: PType): Rope = if ri[i].skipTrivialIndirections.kind == nkSym: needTmp[i - 1] = potentialAlias(ri[i], potentialWrites) else: + #if not ri[i].typ.isCompileTimeOnly: var potentialReads: seq[PNode] getPotentialReads(ri[i], potentialReads) for n in potentialReads: diff --git a/compiler/sem.nim b/compiler/sem.nim index 24709cf21..ad0ba1f7c 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -174,6 +174,12 @@ proc commonType*(c: PContext; x, y: PType): PType = result = b #.skipIntLit elif a.kind in IntegralTypes and a.n != nil: result = a #.skipIntLit + elif a.kind == tyProc and b.kind == tyProc: + if a.callConv == ccClosure and b.callConv != ccClosure: + result = x + elif compatibleEffects(a, b) != efCompat or + (b.flags * {tfNoSideEffect, tfGcSafe}) < (a.flags * {tfNoSideEffect, tfGcSafe}): + result = y else: var k = tyNone if a.kind in {tyRef, tyPtr}: diff --git a/compiler/transf.nim b/compiler/transf.nim index 5846e6e3b..89fa89701 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -1023,8 +1023,9 @@ proc transform(c: PTransf, n: PNode): PNode = result[0] = transform(c, n[0]) # Skip the second son since it only contains an unsemanticized copy of the # variable type used by docgen - result[1] = n[1] - result[2] = transform(c, n[2]) + let last = n.len-1 + for i in 1.. Date: Mon, 6 Sep 2021 12:11:21 -0600 Subject: [PATCH 2504/3768] Fixes implicit and explicit generics in procedures (#18808) * Fixes implicit and explicit generics * moved block logic into 'maybeInstantiateGeneric' * Added more tests * Update compiler/semexprs.nim Co-authored-by: Andreas Rumpf --- compiler/semexprs.nim | 28 +++++++++++--- tests/generics/timplicit_and_explicit.nim | 45 +++++++++++++++++++++++ 2 files changed, 68 insertions(+), 5 deletions(-) create mode 100644 tests/generics/timplicit_and_explicit.nim diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index c0a2a346a..0f6f71cc2 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1493,6 +1493,28 @@ proc semDeref(c: PContext, n: PNode): PNode = else: result = nil #GlobalError(n[0].info, errCircumNeedsPointer) +proc maybeInstantiateGeneric(c: PContext, n: PNode, s: PSym): PNode = + ## Instantiates generic if not lacking implicit generics, + ## otherwise returns n. + let + neededGenParams = s.ast[genericParamsPos].len + heldGenParams = n.len - 1 + var implicitParams = 0 + for x in s.ast[genericParamsPos]: + if tfImplicitTypeParam in x.typ.flags: + inc implicitParams + if heldGenParams != neededGenParams and implicitParams + heldGenParams == neededGenParams: + # This is an implicit + explicit generic procedure without all args passed, + # kicking back the sem'd symbol fixes #17212 + # Uncertain the hackiness of this solution. + result = n + else: + result = explicitGenericInstantiation(c, n, s) + if result == n: + n[0] = copyTree(result) + else: + n[0] = result + proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = ## returns nil if not a built-in subscript operator; also called for the ## checking of assignments @@ -1568,11 +1590,7 @@ proc semSubscript(c: PContext, n: PNode, flags: TExprFlags): PNode = of skProc, skFunc, skMethod, skConverter, skIterator: # type parameters: partial generic specialization n[0] = semSymGenericInstantiation(c, n[0], s) - result = explicitGenericInstantiation(c, n, s) - if result == n: - n[0] = copyTree(result) - else: - n[0] = result + result = maybeInstantiateGeneric(c, n, s) of skMacro, skTemplate: if efInCall in flags: # We are processing macroOrTmpl[] in macroOrTmpl[](...) call. diff --git a/tests/generics/timplicit_and_explicit.nim b/tests/generics/timplicit_and_explicit.nim new file mode 100644 index 000000000..ba24b79e9 --- /dev/null +++ b/tests/generics/timplicit_and_explicit.nim @@ -0,0 +1,45 @@ + +block: # basic test + proc doStuff[T](a: SomeInteger): T = discard + proc doStuff[T;Y](a: SomeInteger, b: Y): Y = discard + assert typeof(doStuff[int](100)) is int + assert typeof(doStuff[int](100, 1.0)) is float + assert typeof(doStuff[int](100, "Hello")) is string + + proc t[T](x: T; z: int | float): seq[T] = result.add(x & $z) + + assert t[string]("Hallo", 2.0) == @["Hallo" & $2.0] + + proc t2[T](z: int | float): seq[T] = result.add($z) + + assert t2[string](2.0) == @[$2.0] + +block: # template test + template someThing[T;Y](a: SomeFloat, b: SomeOrdinal): (T, Y) = (a, b) + assert typeof(someThing[float64, int](1.0, 100)) is (float64, int) + +block: # static test + proc t[T](s: static bool) = discard + proc t2[T](s: static string) = discard + t[string](true) + t2[int]("hello") + t2[string]("world") + t2[float]("test222222") + +block: #11152 + proc f[T](X: typedesc) = discard + f[int](string) + +block: #15622 + proc test1[T](a: T, b: static[string] = "") = discard + test1[int64](123) + proc test2[T](a: T, b: static[string] = "") = discard + test2[int64, static[string]](123) + +block: #4688 + proc convertTo[T](v: int or float): T = (T)(v) + discard convertTo[float](1) + +block: #4164 + proc printStr[T](s: static[string]): T = discard + discard printStr[int]("hello static") \ No newline at end of file From ee2eb5cae2585c5cd0a476814bd5879c130e5d30 Mon Sep 17 00:00:00 2001 From: Jason Beetham Date: Tue, 7 Sep 2021 09:11:08 -0600 Subject: [PATCH 2505/3768] Fix subranges of distinct types (#18816) [backport] --- compiler/typeallowed.nim | 2 +- tests/distinct/tdistinct.nim | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/compiler/typeallowed.nim b/compiler/typeallowed.nim index 2f0c039a4..d00aa538f 100644 --- a/compiler/typeallowed.nim +++ b/compiler/typeallowed.nim @@ -124,7 +124,7 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, result = typeAllowedAux(marker, lastSon(t), kind, c, flags) of tyRange: if skipTypes(t[0], abstractInst-{tyTypeDesc}).kind notin - {tyChar, tyEnum, tyInt..tyFloat128, tyInt..tyUInt64}: result = t + {tyChar, tyEnum, tyInt..tyFloat128, tyInt..tyUInt64, tyRange}: result = t of tyOpenArray: # you cannot nest openArrays/sinks/etc. if (kind != skParam or taIsOpenArray in flags) and views notin c.features: diff --git a/tests/distinct/tdistinct.nim b/tests/distinct/tdistinct.nim index 876975a7c..fd60b4ac0 100644 --- a/tests/distinct/tdistinct.nim +++ b/tests/distinct/tdistinct.nim @@ -167,6 +167,20 @@ template main() = s.Foo.add('c') doAssert s.string == "c" # was failing test() + block: #18061 + type + A = distinct (0..100) + B = A(0) .. A(10) + proc test(b: B) = discard + let + a = A(10) + b = B(a) + test(b) + + proc test(a: A) = discard + discard cast[B](A(1)) + var c: B + static: main() main() From c56008450a86daf454fa0e2e0722711831cd78ae Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 8 Sep 2021 17:47:14 +0800 Subject: [PATCH 2506/3768] fix CI failure(upstream changes) (#18820) * fix upstream changes * Update tests/js/tunittest_error2.nim Co-authored-by: flywind Co-authored-by: Andreas Rumpf --- tests/js/tunittest_error2.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/js/tunittest_error2.nim b/tests/js/tunittest_error2.nim index 273e39d9d..9c5af7529 100644 --- a/tests/js/tunittest_error2.nim +++ b/tests/js/tunittest_error2.nim @@ -1,7 +1,7 @@ discard """ exitcode: 1 outputsub: ''' -Unhandled exception: Cannot read property 'charCodeAt' of null [] +[] [FAILED] Bad test ''' matrix: "-d:nodejs" From 23e10ea85ea35c1a5963bfd12fb69efe6358076a Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 9 Sep 2021 14:46:55 +0800 Subject: [PATCH 2507/3768] active some packages (#18825) --- testament/important_packages.nim | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 025121990..657ddb2d1 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -63,7 +63,7 @@ pkg "delaunay" pkg "docopt" pkg "easygl", "nim c -o:egl -r src/easygl.nim", "https://github.com/jackmott/easygl" pkg "elvis" -pkg "fidget", allowFailure = true # pending https://github.com/treeform/fidget/issues/155 +pkg "fidget" pkg "fragments", "nim c -r fragments/dsl.nim" pkg "fusion" pkg "gara" @@ -76,7 +76,7 @@ pkg "gnuplot", "nim c gnuplot.nim" pkg "hts", "nim c -o:htss src/hts.nim" pkg "httpauth" pkg "illwill", "nimble examples" -pkg "inim", allowFailure=true +pkg "inim" pkg "itertools", "nim doc src/itertools.nim" pkg "iterutils" pkg "jstin" @@ -100,7 +100,7 @@ pkg "nimcrypto", "nim r --path:. tests/testall.nim" # `--path:.` workaround need pkg "NimData", "nim c -o:nimdataa src/nimdata.nim" pkg "nimes", "nim c src/nimes.nim" pkg "nimfp", "nim c -o:nfp -r src/fp.nim" -pkg "nimgame2", "nim c -d:nimLegacyConvEnumEnum nimgame2/nimgame.nim", allowFailure = true +pkg "nimgame2", "nim c -d:nimLegacyConvEnumEnum nimgame2/nimgame.nim" # XXX Doesn't work with deprecated 'randomize', will create a PR. pkg "nimgen", "nim c -o:nimgenn -r src/nimgen/runcfg.nim" pkg "nimlsp" @@ -145,7 +145,7 @@ pkg "strslice" pkg "strunicode", "nim c -r src/strunicode.nim" pkg "supersnappy" pkg "synthesis" -pkg "telebot", "nim c -o:tbot -r src/telebot.nim", allowFailure = true # pending https://github.com/ba0f3/telebot.nim/issues/59 +pkg "telebot", "nim c -o:tbot -r src/telebot.nim" pkg "tempdir" pkg "templates" pkg "tensordsl", "nim c -r tests/tests.nim", "https://krux02@bitbucket.org/krux02/tensordslnim.git" From a896f9f19eec221b8651849297bdbc8be72b00af Mon Sep 17 00:00:00 2001 From: Federico Ceratto Date: Thu, 9 Sep 2021 12:57:21 +0100 Subject: [PATCH 2508/3768] Clarify unknown processor error (#18829) --- tools/niminst/makefile.nimf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/niminst/makefile.nimf b/tools/niminst/makefile.nimf index d14960ac5..a0e9a40f7 100644 --- a/tools/niminst/makefile.nimf +++ b/tools/niminst/makefile.nimf @@ -164,7 +164,7 @@ ifeq ($(ucpu),riscv64) mycpu = riscv64 endif ifndef mycpu - $(error unknown processor: $(ucpu)) + $(error unknown CPU architecture: $(ucpu) See makefile.nimf) endif # for osA in 1..c.oses.len: From 0ef830577b6f6ee2314ecdfe56a631ffd4f8e4c7 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Fri, 10 Sep 2021 05:20:32 -0300 Subject: [PATCH 2509/3768] distros.foreignDeps made public (#18830) * Deprecate distros.echoForeignDeps * Deprecate distros.echoForeignDeps * https://github.com/nim-lang/Nim/pull/18830#discussion_r705364162 * https://github.com/nim-lang/Nim/pull/18830#discussion_r705364162 --- changelog.md | 1 + lib/pure/distros.nim | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/changelog.md b/changelog.md index 3658fb105..9e449dcaa 100644 --- a/changelog.md +++ b/changelog.md @@ -380,6 +380,7 @@ - Deprecated `sequtils.delete` and added an overload taking a `Slice` that raises a defect if the slice is out of bounds, likewise with `strutils.delete`. + ## Language changes - The `cstring` doesn't support `[]=` operator in JS backend. diff --git a/lib/pure/distros.nim b/lib/pure/distros.nim index ddf0fd30c..797698d61 100644 --- a/lib/pure/distros.nim +++ b/lib/pure/distros.nim @@ -27,11 +27,11 @@ ## ## See `packaging `_ for hints on distributing Nim using OS packages. -from strutils import contains, toLowerAscii +from std/strutils import contains, toLowerAscii when not defined(nimscript): - from osproc import execProcess - from os import existsEnv + from std/osproc import execProcess + from std/os import existsEnv type Distribution* {.pure.} = enum ## the list of known distributions @@ -211,7 +211,7 @@ template detectOs*(d: untyped): bool = detectOsImpl(Distribution.d) when not defined(nimble): - var foreignDeps: seq[string] = @[] + var foreignDeps*: seq[string] = @[] ## Registered foreign deps. proc foreignCmd*(cmd: string; requiresSudo = false) = ## Registers a foreign command to the internal list of commands From 6c2f041368e36bb7a6b217c7ac0c5de94c7b4a7a Mon Sep 17 00:00:00 2001 From: flywind Date: Sat, 11 Sep 2021 09:25:48 +0800 Subject: [PATCH 2510/3768] fix code-block which causes missing docs --- lib/system/channels_builtin.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/system/channels_builtin.nim b/lib/system/channels_builtin.nim index c7a445766..30267e375 100644 --- a/lib/system/channels_builtin.nim +++ b/lib/system/channels_builtin.nim @@ -26,7 +26,7 @@ ## The following is a simple example of two different ways to use channels: ## blocking and non-blocking. ## -## .. code-block :: Nim +## .. code-block:: Nim ## # Be sure to compile with --threads:on. ## # The channels and threads modules are part of system and should not be ## # imported. @@ -112,7 +112,7 @@ ## using e.g. `system.allocShared0` and pass these pointers through thread ## arguments: ## -## .. code-block :: Nim +## .. code-block:: Nim ## proc worker(channel: ptr Channel[string]) = ## let greeting = channel[].recv() ## echo greeting From 1f68f71ec24d3ec6b8e83411a6f1604277f9d493 Mon Sep 17 00:00:00 2001 From: flywind Date: Sat, 11 Sep 2021 17:33:52 +0800 Subject: [PATCH 2511/3768] [minor] fix docs (#18834) --- lib/pure/collections/sets.nim | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim index 3f91d9030..7b1c58ac8 100644 --- a/lib/pure/collections/sets.nim +++ b/lib/pure/collections/sets.nim @@ -603,12 +603,10 @@ proc isValid*[A](s: HashSet[A]): bool {.deprecated: ## Returns `true` if the set has been initialized (with `initHashSet proc ## <#initHashSet>`_ or `init proc <#init,HashSet[A]>`_). ## - ## **Examples:** - ## - ## .. code-block :: - ## proc savePreferences(options: HashSet[string]) = - ## assert options.isValid, "Pass an initialized set!" - ## # Do stuff here, may crash in release builds! + runnableExamples: + proc savePreferences(options: HashSet[string]) = + assert options.isValid, "Pass an initialized set!" + # Do stuff here, may crash in release builds! result = s.data.len > 0 From 66e53bdd7b465edd9045314d4d6a60ef6e0b5e32 Mon Sep 17 00:00:00 2001 From: Jason Beetham Date: Sat, 11 Sep 2021 05:05:53 -0600 Subject: [PATCH 2512/3768] Fixed type inference for 'set` and 'tuple' (#18827) * improved built in typeclass inference * Smarter logic to fit node * Forgot the untyped check --- compiler/sem.nim | 3 ++- tests/metatype/typeclassinference.nim | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/compiler/sem.nim b/compiler/sem.nim index ad0ba1f7c..bdecbe602 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -77,7 +77,8 @@ template semIdeForTemplateOrGeneric(c: PContext; n: PNode; proc fitNodePostMatch(c: PContext, formal: PType, arg: PNode): PNode = let x = arg.skipConv - if x.kind in {nkPar, nkTupleConstr, nkCurly} and formal.kind != tyUntyped: + if (x.kind == nkCurly and formal.kind == tySet and formal.base.kind != tyGenericParam) or + (x.kind in {nkPar, nkTupleConstr}) and formal.kind notin {tyUntyped, tyBuiltInTypeClass}: changeType(c, x, formal, check=true) result = arg result = skipHiddenSubConv(result, c.graph, c.idgen) diff --git a/tests/metatype/typeclassinference.nim b/tests/metatype/typeclassinference.nim index c845e04f7..b3f197718 100644 --- a/tests/metatype/typeclassinference.nim +++ b/tests/metatype/typeclassinference.nim @@ -19,3 +19,25 @@ var ptr1: ptr = addr(str1) var str2: string = "hello, world!" var ptr2: ptr = str2 +block: # built in typeclass inference + proc tupleA(): tuple = return (1, 2) + proc tupleB(): tuple = (1f, 2f) + assert typeof(tupleA()) is (int, int) + assert typeof(tupleB()) is (float32, float32) + + proc a(val: int or float): tuple = + when typeof(val) is int: + (10, 10) + else: + (30f, 30f) + + assert typeof(a(10)) is (int, int) + assert typeof(a(10.0)) is (float32, float32) + + proc b(val: int or float): set = + when typeof(val) is int: + {10u8, 3} + else: + {'a', 'b'} + assert typeof(b(10)) is set[uint8] + assert typeof(b(10.0)) is set[char] \ No newline at end of file From 5d1608c9764416e74b36f6a772f16a66d58ddc28 Mon Sep 17 00:00:00 2001 From: Jason Beetham Date: Sat, 11 Sep 2021 14:20:22 -0600 Subject: [PATCH 2513/3768] Generic pointer procs now error if no types supplied (#18832) * more precise logic for pointer procs * added test for generic pointer procs * Fixed generic getting bracket expr if erroring --- compiler/semexprs.nim | 2 +- compiler/semstmts.nim | 2 +- tests/generics/tpointerprocs.nim | 28 ++++++++++++++++++++++++++++ 3 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 tests/generics/tpointerprocs.nim diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 0f6f71cc2..41381b057 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1511,7 +1511,7 @@ proc maybeInstantiateGeneric(c: PContext, n: PNode, s: PSym): PNode = else: result = explicitGenericInstantiation(c, n, s) if result == n: - n[0] = copyTree(result) + n[0] = copyTree(result[0]) else: n[0] = result diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 2c5f3ba54..364444c98 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -553,7 +553,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = typ = typ.lastSon if hasEmpty(typ): localError(c.config, def.info, errCannotInferTypeOfTheLiteral % typ.kind.toHumanStr) - elif typ.kind == tyProc and hasUnresolvedParams(def, {}): + elif typ.kind == tyProc and def.kind == nkSym and isGenericRoutine(def.sym.ast): # tfUnresolved in typ.flags: localError(c.config, def.info, errProcHasNoConcreteType % def.renderTree) when false: diff --git a/tests/generics/tpointerprocs.nim b/tests/generics/tpointerprocs.nim new file mode 100644 index 000000000..2bcaf15b3 --- /dev/null +++ b/tests/generics/tpointerprocs.nim @@ -0,0 +1,28 @@ +discard """ +cmd: "nim check $options --hints:off $file" +action: "reject" +nimout:''' +tpointerprocs.nim(15, 11) Error: 'foo' doesn't have a concrete type, due to unspecified generic parameters. +tpointerprocs.nim(27, 11) Error: cannot instantiate: 'foo[int]'; got 1 typeof(s) but expected 2 +tpointerprocs.nim(27, 14) Error: expression 'foo[int]' has no type (or is ambiguous) +tpointerprocs.nim(28, 11) Error: expression 'bar' has no type (or is ambiguous) +''' +""" + +block: + proc foo(x: int | float): float = result = 1.0 + let + bar = foo + baz = bar + +block: + proc foo(x: int | float): float = result = 1.0 + let + bar = foo[int] + baz = bar + +block: + proc foo(x: int | float, y: int or string): float = result = 1.0 + let + bar = foo[int] + baz = bar \ No newline at end of file From 3f3e0fa303a2729d0d6a9ffe54936c1c830e2d7e Mon Sep 17 00:00:00 2001 From: Jason Beetham Date: Mon, 13 Sep 2021 01:35:19 -0600 Subject: [PATCH 2514/3768] Fixed #18838 (#18841) [backport] --- compiler/semtypinst.nim | 6 ++++- tests/generics/trecursivegenerics.nim | 36 ++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 01b21498b..cb1ead027 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -344,7 +344,11 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = x = lookupTypeVar(cl, x) if x != nil: if header == t: header = instCopyType(cl, t) - header[i] = x + header[i] = + if x.kind == tyGenericInst: + t[i] + else: + x propagateToOwner(header, x) else: propagateToOwner(header, x) diff --git a/tests/generics/trecursivegenerics.nim b/tests/generics/trecursivegenerics.nim index 56c074e3f..e4c0e720f 100644 --- a/tests/generics/trecursivegenerics.nim +++ b/tests/generics/trecursivegenerics.nim @@ -1,6 +1,7 @@ block: # Replicates #18728 type FlipFlop[A, B] = ref object + val: A next: FlipFlop[B, A] Trinary[A, B, C] = ref object @@ -9,4 +10,37 @@ block: # Replicates #18728 assert typeof(FlipFlop[int, string]().next) is FlipFlop[string, int] assert typeof(FlipFlop[string, int]().next) is FlipFlop[int, string] assert typeof(Trinary[int, float, string]().next) is Trinary[float, string, int] - assert typeof(Trinary[int, float, string]().next.next) is Trinary[string, int, float] \ No newline at end of file + assert typeof(Trinary[int, float, string]().next.next) is Trinary[string, int, float] + var a = FlipFlop[int, string](val: 100, next: FlipFlop[string, int](val: "Hello")) + assert a.val == 100 + assert a.next.val == "Hello" + +block: # 18838 + type + DoublyLinkedNodeObj[T] = object + value: T + + DoublyLinkedNode[T] = ref DoublyLinkedNodeObj[T] + + Item[T] = ref object + link: DoublyLinkedNode[Item[T]] + + Box = object + + proc newDoublyLinkedNode[T](value: T): DoublyLinkedNode[T] = + new(result) + result.value = value + + let link = newDoublyLinkedNode(Item[Box]()) + +import lists +block: + type + Box = object + Item[T] = ref object + link:DoublyLinkedNode[ Item[T] ] + + ItemSimple = ref object + link:DoublyLinkedNode[ ItemSimple ] + + let link = newDoublyLinkedNode( Item[Box]() ) \ No newline at end of file From 179fad934d52641c5e064efce4bdd98578cc5e9e Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 13 Sep 2021 11:05:09 +0200 Subject: [PATCH 2515/3768] doc improvements (#18843) * cleaned up destructors documentation [backport] * Spec updates [backport:1.0] --- doc/destructors.rst | 53 +++++--------------- doc/manual.rst | 118 ++++++++------------------------------------ 2 files changed, 33 insertions(+), 138 deletions(-) diff --git a/doc/destructors.rst b/doc/destructors.rst index a3fd7514e..28d8fa5f9 100644 --- a/doc/destructors.rst +++ b/doc/destructors.rst @@ -222,7 +222,14 @@ The prototype of this hook for a type `T` needs to be: `env` is used by ORC to keep track of its internal state, it should be passed around to calls of the built-in `=trace` operation. -Usually there will only be a need for a custom `=trace` when a custom `=destroy` that deallocates manually allocated resources is also used, and then only when there is a chance of cyclic references from items within the manually allocated resources when it is desired that `--gc:orc` be able to break and collect these cyclic referenced resources. Currently however, there is a mutual use problem in that whichever of `=destroy`/`=trace` is used first will automatically create a version of the other which will then conflict with the creation of the second of the pair. The work around for this problem is to forward declare the second of the "hooks" to prevent the automatic creation. +Usually there will only be a need for a custom `=trace` when a custom `=destroy` that deallocates +manually allocated resources is also used, and then only when there is a chance of cyclic +references from items within the manually allocated resources when it is desired that `--gc:orc` +be able to break and collect these cyclic referenced resources. Currently however, there is a +mutual use problem in that whichever of `=destroy`/`=trace` is used first will automatically +create a version of the other which will then conflict with the creation of the second of the +pair. The work around for this problem is to forward declare the second of the "hooks" to +prevent the automatic creation. The general pattern in using `=destroy` with `=trace` looks like: @@ -696,43 +703,22 @@ should eventually be replaced by a better solution. Copy on write ============= -String literals are implemented as [copy-on-write](https://en.wikipedia.org/wiki/Copy-on-write). +String literals are implemented as "copy on write". When assigning a string literal to a variable, a copy of the literal won't be created. Instead the variable simply points to the literal. The literal is shared between different variables which are pointing to it. The copy operation is deferred until the first write. -.. code-block:: nim - var x = "abc" # no copy - var y = x # no copy - -The string literal "abc" is stored in static memory and not allocated on the heap. -The variable `x` points to the literal and the variable `y` points to the literal too. -There is no copy during assigning operations. +For example: .. code-block:: nim var x = "abc" # no copy var y = x # no copy y[0] = 'h' # copy -The program above shows when the copy operations happen. -When mutating the variable `y`, the Nim compiler creates a fresh copy of `x`, -the variable `y` won't point to the string literal anymore. -Instead it points to the copy of `x` of which the memory can be mutated -and the variable `y` becomes a mutable string. -.. Note:: The abstraction fails for `addr x` because whether the address is going to be used for mutations is unknown. - -Let's look at a silly example demonstrating this behaviour: - -.. code-block:: nim - var x = "abc" - var y = x - - moveMem(addr y[0], addr x[0], 3) - -The program fails because we need to prepare a fresh copy for the variable `y`. -`prepareMutation` should be called before the address operation. +The abstraction fails for `addr x` because whether the address is going to be used for mutations is unknown. +`prepareMutation` needs to be called before the "address of" operation. For example: .. code-block:: nim var x = "abc" @@ -741,18 +727,3 @@ The program fails because we need to prepare a fresh copy for the variable `y`. prepareMutation(y) moveMem(addr y[0], addr x[0], 3) assert y == "abc" - -Now `prepareMutation` solves the problem. -It manually creates a fresh copy and makes the variable `y` mutable. - -.. code-block:: nim - var x = "abc" - var y = x - - prepareMutation(y) - moveMem(addr y[0], addr x[0], 3) - moveMem(addr y[0], addr x[0], 3) - moveMem(addr y[0], addr x[0], 3) - assert y == "abc" - -No matter how many times `moveMem` is called, the program compiles and runs. diff --git a/doc/manual.rst b/doc/manual.rst index 7976ffe0f..af163bceb 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -2400,121 +2400,38 @@ describe the type checking done by the compiler. Type equality ------------- + Nim uses structural type equivalence for most types. Only for objects, -enumerations and distinct types name equivalence is used. The following -algorithm, *in pseudo-code*, determines type equality: - -.. code-block:: nim - proc typeEqualsAux(a, b: PType, - s: var HashSet[(PType, PType)]): bool = - if (a,b) in s: return true - incl(s, (a,b)) - if a.kind == b.kind: - case a.kind - of int, intXX, float, floatXX, char, string, cstring, pointer, - bool, nil, void: - # leaf type: kinds identical; nothing more to check - result = true - of ref, ptr, var, set, seq, openarray: - result = typeEqualsAux(a.baseType, b.baseType, s) - of range: - result = typeEqualsAux(a.baseType, b.baseType, s) and - (a.rangeA == b.rangeA) and (a.rangeB == b.rangeB) - of array: - result = typeEqualsAux(a.baseType, b.baseType, s) and - typeEqualsAux(a.indexType, b.indexType, s) - of tuple: - if a.tupleLen == b.tupleLen: - for i in 0..a.tupleLen-1: - if not typeEqualsAux(a[i], b[i], s): return false - result = true - of object, enum, distinct: - result = a == b - of proc: - result = typeEqualsAux(a.parameterTuple, b.parameterTuple, s) and - typeEqualsAux(a.resultType, b.resultType, s) and - a.callingConvention == b.callingConvention - - proc typeEquals(a, b: PType): bool = - var s: HashSet[(PType, PType)] = {} - result = typeEqualsAux(a, b, s) - -Since types are graphs which can have cycles, the above algorithm needs an -auxiliary set `s` to detect this case. - - -Type equality modulo type distinction -------------------------------------- - -The following algorithm (in pseudo-code) determines whether two types -are equal with no respect to `distinct` types. For brevity the cycle check -with an auxiliary set `s` is omitted: - -.. code-block:: nim - proc typeEqualsOrDistinct(a, b: PType): bool = - if a.kind == b.kind: - case a.kind - of int, intXX, float, floatXX, char, string, cstring, pointer, - bool, nil, void: - # leaf type: kinds identical; nothing more to check - result = true - of ref, ptr, var, set, seq, openarray: - result = typeEqualsOrDistinct(a.baseType, b.baseType) - of range: - result = typeEqualsOrDistinct(a.baseType, b.baseType) and - (a.rangeA == b.rangeA) and (a.rangeB == b.rangeB) - of array: - result = typeEqualsOrDistinct(a.baseType, b.baseType) and - typeEqualsOrDistinct(a.indexType, b.indexType) - of tuple: - if a.tupleLen == b.tupleLen: - for i in 0..a.tupleLen-1: - if not typeEqualsOrDistinct(a[i], b[i]): return false - result = true - of distinct: - result = typeEqualsOrDistinct(a.baseType, b.baseType) - of object, enum: - result = a == b - of proc: - result = typeEqualsOrDistinct(a.parameterTuple, b.parameterTuple) and - typeEqualsOrDistinct(a.resultType, b.resultType) and - a.callingConvention == b.callingConvention - elif a.kind == distinct: - result = typeEqualsOrDistinct(a.baseType, b) - elif b.kind == distinct: - result = typeEqualsOrDistinct(a, b.baseType) +enumerations and distinct types and for generic types name equivalence is used. Subtype relation ---------------- -If object `a` inherits from `b`, `a` is a subtype of `b`. This subtype -relation is extended to the types `var`, `ref`, `ptr`: -.. code-block:: nim - proc isSubtype(a, b: PType): bool = - if a.kind == b.kind: - case a.kind - of object: - var aa = a.baseType - while aa != nil and aa != b: aa = aa.baseType - result = aa == b - of var, ref, ptr: - result = isSubtype(a.baseType, b.baseType) +If object `a` inherits from `b`, `a` is a subtype of `b`. -.. XXX nil is a special value! +This subtype relation is extended to the types `var`, `ref`, `ptr`. +If `A` is a subtype of `B` and `A` and `B` are `object` types then: +- `var A` is a subtype of `var B` +- `ref A` is a subtype of `ref B` +- `ptr A` is a subtype of `ptr B`. +**Note**: In later versions of the language the subtype relation might +be changed to *require* the pointer indirection in order to prevent +"object slicing". Convertible relation -------------------- + A type `a` is **implicitly** convertible to type `b` iff the following algorithm returns true: .. code-block:: nim proc isImplicitlyConvertible(a, b: PType): bool = - if isSubtype(a, b) or isCovariant(a, b): + if isSubtype(a, b): return true if isIntLiteral(a): return b in {int8, int16, int32, int64, int, uint, uint8, uint16, @@ -2540,7 +2457,12 @@ algorithm returns true: result = b == pointer of string: result = b == cstring + of proc: + result = typeEquals(a, b) or compatibleParametersAndEffects(a, b) +We used the predicate `typeEquals(a, b)` for the "type equality" property +and the predicate `isSubtype(a, b)` for the "subtype relation". +`compatibleParametersAndEffects(a, b)` is currently not specified. Implicit conversions are also performed for Nim's `range` type constructor. @@ -2563,7 +2485,9 @@ algorithm returns true: proc isExplicitlyConvertible(a, b: PType): bool = result = false if isImplicitlyConvertible(a, b): return true - if typeEqualsOrDistinct(a, b): return true + if typeEquals(a, b): return true + if a == distinct and typeEquals(a.baseType, b): return true + if b == distinct and typeEquals(b.baseType, a): return true if isIntegralType(a) and isIntegralType(b): return true if isSubtype(a, b) or isSubtype(b, a): return true From ef390e6a68db74a61137d7690dd2b10ec3dee050 Mon Sep 17 00:00:00 2001 From: Christian Ulrich Date: Mon, 13 Sep 2021 18:47:33 +0200 Subject: [PATCH 2516/3768] add changelog entry for #18656 (#18839) [backport] --- changelog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/changelog.md b/changelog.md index 9e449dcaa..ab037c328 100644 --- a/changelog.md +++ b/changelog.md @@ -111,6 +111,11 @@ - The allocator for Nintendo Switch, which was nonfunctional because of breaking changes in libnx, was removed, in favour of the new `-d:nimAllocPagesViaMalloc` option. +- `net.parseIpAddress` now only allows IPv4 addresses in strict form as defined + in [RFC 6943](https://www.rfc-editor.org/rfc/rfc6943#section-3.1.1). + Specifically, octal numbers in IPv4 addresses are no longer accepted (before + they were parsed as decimal numbers). + ## Standard library additions and changes - `strformat`: From 172253cb551a475d7d32093e562521990e71a1ed Mon Sep 17 00:00:00 2001 From: Jason Beetham Date: Tue, 14 Sep 2021 11:34:52 -0600 Subject: [PATCH 2517/3768] Dotborrow now works with generic distincts (#18848) --- compiler/semexprs.nim | 4 ++-- compiler/semstmts.nim | 3 ++- tests/borrow/tborrow.nim | 20 ++++++++++++++++++++ tests/borrow/tinvalidborrow.nim | 17 +++++++++++++---- 4 files changed, 37 insertions(+), 7 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 41381b057..ebdfe02f0 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1280,7 +1280,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = if p != nil and p.selfSym != nil: var ty = skipTypes(p.selfSym.typ, {tyGenericInst, tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink, tyOwned}) - while tfBorrowDot in ty.flags: ty = ty.skipTypes({tyDistinct}) + while tfBorrowDot in ty.flags: ty = ty.skipTypes({tyDistinct, tyGenericInst}) var check: PNode = nil if ty.kind == tyObject: while true: @@ -1412,7 +1412,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = if ty.kind in tyUserTypeClasses and ty.isResolvedUserTypeClass: ty = ty.lastSon ty = skipTypes(ty, {tyGenericInst, tyVar, tyLent, tyPtr, tyRef, tyOwned, tyAlias, tySink}) - while tfBorrowDot in ty.flags: ty = ty.skipTypes({tyDistinct}) + while tfBorrowDot in ty.flags: ty = ty.skipTypes({tyDistinct, tyGenericInst}) var check: PNode = nil if ty.kind == tyObject: while true: diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 364444c98..c2f05ccce 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1296,7 +1296,8 @@ proc typeSectionRightSidePass(c: PContext, n: PNode) = incl a[2].flags, nfSem # bug #10548 if sfExportc in s.flags and s.typ.kind == tyAlias: localError(c.config, name.info, "{.exportc.} not allowed for type aliases") - if tfBorrowDot in s.typ.flags and s.typ.kind != tyDistinct: + + if tfBorrowDot in s.typ.flags and s.typ.skipTypes({tyGenericBody}).kind != tyDistinct: excl s.typ.flags, tfBorrowDot localError(c.config, name.info, "only a 'distinct' type can borrow `.`") let aa = a[2] diff --git a/tests/borrow/tborrow.nim b/tests/borrow/tborrow.nim index 9c403021e..2fc788fa0 100644 --- a/tests/borrow/tborrow.nim +++ b/tests/borrow/tborrow.nim @@ -33,3 +33,23 @@ let b = Radians(1.0) a -= b echo a.float64 + +block: #14449 + type + Foo[T] = object + foo: T + + Bar[T] {.borrow:`.`.} = distinct Foo[T] + SomeThing {.borrow:`.`.} = distinct Foo[float] + OtherThing {.borrow:`.`.} = distinct SomeThing + + var + a: Bar[int] + b: SomeThing + c: OtherThing + a.foo = 300 + b.foo = 400 + c.foo = 42 + assert a.foo == 300 + assert b.foo == 400d + assert c.foo == 42d \ No newline at end of file diff --git a/tests/borrow/tinvalidborrow.nim b/tests/borrow/tinvalidborrow.nim index 89aa4e2e8..08148608d 100644 --- a/tests/borrow/tinvalidborrow.nim +++ b/tests/borrow/tinvalidborrow.nim @@ -1,16 +1,25 @@ discard """ - errormsg: "no symbol to borrow from found" - line: 11 + cmd: "nim check --hints:off --warnings:off $file" + action: "reject" + nimout:''' +tinvalidborrow.nim(18, 3) Error: only a 'distinct' type can borrow `.` +tinvalidborrow.nim(19, 3) Error: only a 'distinct' type can borrow `.` +tinvalidborrow.nim(20, 1) Error: no symbol to borrow from found +''' """ # bug #516 type TAtom = culong - + Test {.borrow:`.`.} = distinct int + Foo[T] = object + a: int + Bar[T] {.borrow:`.`.} = Foo[T] + OtherFoo {.borrow:`.`.} = Foo[int] proc `==`*(a, b: TAtom): bool {.borrow.} var d, e: TAtom -echo( $(d == e) ) +discard( $(d == e) ) From bf1700bab17da3c8776914ac5bdfff016eb70442 Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 15 Sep 2021 01:39:55 +0800 Subject: [PATCH 2518/3768] add testcase for #7308 (#18849) --- tests/arc/tarcmisc.nim | 11 ---------- tests/ccgbugs/tctypes.nim | 43 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 11 deletions(-) create mode 100644 tests/ccgbugs/tctypes.nim diff --git a/tests/arc/tarcmisc.nim b/tests/arc/tarcmisc.nim index 951ae25f3..7daea62c8 100644 --- a/tests/arc/tarcmisc.nim +++ b/tests/arc/tarcmisc.nim @@ -463,14 +463,3 @@ proc putValue[T](n: T) = echo b.n useForward() - - -# bug #16246 - -proc testWeirdTypeAliases() = - var values = newSeq[cuint](8) - # var values: seq[cuint] does not produce codegen error - var drawCb = proc(): seq[uint32] = - result = newSeq[uint32](10) - -testWeirdTypeAliases() diff --git a/tests/ccgbugs/tctypes.nim b/tests/ccgbugs/tctypes.nim new file mode 100644 index 000000000..be6009115 --- /dev/null +++ b/tests/ccgbugs/tctypes.nim @@ -0,0 +1,43 @@ +discard """ + targets: "c cpp" + matrix: "--gc:refc; --gc:arc" +""" + +# bug #7308 +proc foo(x: seq[int32]) = + var y = newSeq[cint](1) + +proc bar = + var t = newSeq[int32](1) + foo(t) + +bar() + + +# bug #16246 + +proc testWeirdTypeAliases() = + var values = newSeq[cuint](8) + # var values: seq[cuint] does not produce codegen error + var drawCb = proc(): seq[uint32] = + result = newSeq[uint32](10) + +testWeirdTypeAliases() + +block: # bug #11797 + block: + type cdouble2 = cdouble + type Foo1 = seq[cdouble] + type Foo2 = seq[cdouble2] + static: doAssert Foo1 is Foo2 + var a1: Foo1 + var a2: Foo2 + doAssert a1 == @[] + doAssert a2 == @[] + + block: + proc foo[T: int|cint](fun: proc(): T) = discard + proc foo1(): cint = 1 + proc foo3(): int32 = 2 + foo(proc(): cint = foo1()) + foo(proc(): int32 = foo3()) From cebf7cdc1ede7385e8d774b83c52d17e7021899b Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 15 Sep 2021 01:40:42 +0800 Subject: [PATCH 2519/3768] fix #10128 (#18852) --- compiler/ccgexprs.nim | 6 +++--- tests/ccgbugs/t10128.nim | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 tests/ccgbugs/t10128.nim diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 374876367..7a3d769aa 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1006,14 +1006,14 @@ proc genOpenArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) = # emit range check: if optBoundsCheck in p.options: linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2Len_0)){ #raiseIndexError2($1,$2Len_0-1); $3}$n", - [rdLoc(b), rdLoc(a), raiseInstr(p)]) # BUGFIX: ``>=`` and not ``>``! + [rdCharLoc(b), rdLoc(a), raiseInstr(p)]) # BUGFIX: ``>=`` and not ``>``! inheritLocation(d, a) putIntoDest(p, d, n, ropecg(p.module, "$1[$2]", [rdLoc(a), rdCharLoc(b)]), a.storage) else: if optBoundsCheck in p.options: linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2.Field1)){ #raiseIndexError2($1,$2.Field1-1); $3}$n", - [rdLoc(b), rdLoc(a), raiseInstr(p)]) # BUGFIX: ``>=`` and not ``>``! + [rdCharLoc(b), rdLoc(a), raiseInstr(p)]) # BUGFIX: ``>=`` and not ``>``! inheritLocation(d, a) putIntoDest(p, d, n, ropecg(p.module, "$1.Field0[$2]", [rdLoc(a), rdCharLoc(b)]), a.storage) @@ -1028,7 +1028,7 @@ proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) = if optBoundsCheck in p.options: linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)$2){ #raiseIndexError2($1,$2-1); $3}$n", - [rdLoc(b), lenExpr(p, a), raiseInstr(p)]) + [rdCharLoc(b), lenExpr(p, a), raiseInstr(p)]) if d.k == locNone: d.storage = OnHeap if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}: a.r = ropecg(p.module, "(*$1)", [a.r]) diff --git a/tests/ccgbugs/t10128.nim b/tests/ccgbugs/t10128.nim new file mode 100644 index 000000000..48970916f --- /dev/null +++ b/tests/ccgbugs/t10128.nim @@ -0,0 +1,18 @@ +# bug #10128 +let data = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz" +var seq2 = newSeq[char](data.len) +for i in 0.. Date: Thu, 16 Sep 2021 00:48:58 -0600 Subject: [PATCH 2520/3768] Fixed borrowing dot from aliases (#18854) --- compiler/semexprs.nim | 4 ++-- tests/borrow/tborrow.nim | 38 +++++++++++++++++++++++++++++++++++++- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index ebdfe02f0..5a1426f2b 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1280,7 +1280,7 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = if p != nil and p.selfSym != nil: var ty = skipTypes(p.selfSym.typ, {tyGenericInst, tyVar, tyLent, tyPtr, tyRef, tyAlias, tySink, tyOwned}) - while tfBorrowDot in ty.flags: ty = ty.skipTypes({tyDistinct, tyGenericInst}) + while tfBorrowDot in ty.flags: ty = ty.skipTypes({tyDistinct, tyGenericInst, tyAlias}) var check: PNode = nil if ty.kind == tyObject: while true: @@ -1412,7 +1412,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = if ty.kind in tyUserTypeClasses and ty.isResolvedUserTypeClass: ty = ty.lastSon ty = skipTypes(ty, {tyGenericInst, tyVar, tyLent, tyPtr, tyRef, tyOwned, tyAlias, tySink}) - while tfBorrowDot in ty.flags: ty = ty.skipTypes({tyDistinct, tyGenericInst}) + while tfBorrowDot in ty.flags: ty = ty.skipTypes({tyDistinct, tyGenericInst, tyAlias}) var check: PNode = nil if ty.kind == tyObject: while true: diff --git a/tests/borrow/tborrow.nim b/tests/borrow/tborrow.nim index 2fc788fa0..35652e2e0 100644 --- a/tests/borrow/tborrow.nim +++ b/tests/borrow/tborrow.nim @@ -52,4 +52,40 @@ block: #14449 c.foo = 42 assert a.foo == 300 assert b.foo == 400d - assert c.foo == 42d \ No newline at end of file + assert c.foo == 42d + +block: # Borrow from muliple aliasses #16666 + type + AImpl = object + i: int + + A = AImpl + + B {.borrow: `.`.} = distinct A + C = B + D {.borrow: `.`.} = distinct C + E {.borrow: `.`.} = distinct D + + let + b = default(B) + d = default(D) + e = default(E) + + assert b.i == 0 + assert d.i == 0 + assert e.i == 0 + +block: # Borrow from generic alias + type + AImpl[T] = object + i: T + B[T] = AImpl[T] + C {.borrow: `.`.} = distinct B[int] + D = B[float] + E {.borrow: `.`.} = distinct D + + let + c = default(C) + e = default(E) + assert c.i == int(0) + assert e.i == 0d \ No newline at end of file From 6cd219c3a38c5f1a0712b0b5d07a3ea4ab02ff74 Mon Sep 17 00:00:00 2001 From: alaviss Date: Fri, 17 Sep 2021 04:51:26 +0000 Subject: [PATCH 2521/3768] semtypinst: don't wrap type nodes from expressions in static[T] (#18860) --- compiler/semtypinst.nim | 2 +- tests/generics/t18859.nim | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 tests/generics/t18859.nim diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index cb1ead027..2ca355e7d 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -548,7 +548,7 @@ proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = result = n.typ.skipTypes({tyTypeDesc}) # result = n.typ.base else: - if n.typ.kind != tyStatic: + if n.typ.kind != tyStatic and n.kind != nkType: # XXX: In the future, semConstExpr should # return tyStatic values to let anyone make # use of this knowledge. The patching here diff --git a/tests/generics/t18859.nim b/tests/generics/t18859.nim new file mode 100644 index 000000000..ca6c3d10b --- /dev/null +++ b/tests/generics/t18859.nim @@ -0,0 +1,17 @@ +import macros + +macro symFromDesc(T: typedesc): untyped = + let typ = getType(T) + typ[1] + +template produceType(T: typedesc): untyped = + type + XT = object + x: symFromDesc(T) + + XT + +type + X[T] = produceType(T) + +var x: X[int] From ca51bb8fd97e36e001afc7e9ad150dc77e9ca83b Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 19 Sep 2021 18:31:55 +0200 Subject: [PATCH 2522/3768] documented overload disambiguation [backport] (#18865) * documented overload disambiguation [backport] * Update doc/manual.rst Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> * documented overload disambiguation [backport] * documented overload disambiguation [backport] Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> --- doc/manual.rst | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/doc/manual.rst b/doc/manual.rst index af163bceb..09826441c 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -2708,6 +2708,24 @@ a parameter typed as `untyped` (for unresolved expressions) or the type class assert toSeq2(items(@[1,2])) == @[1, 2] # but items(@[1,2]) is +Overload disambiguation +======================= + +For routine calls "overload resolution" is performed. There is a weaker form of +overload resolution called *overload disambiguation* that is performed when an +overloaded symbol is used in a context where there is additional type information +available. Let `p` be an overloaded symbol. These contexts are: + +- In a function call `q(..., p, ...)` when the corresponding formal parameter + of `q` is a `proc` type. If `q` itself is overloaded then the cartesian product + of every interpretation of `q` and `p` must be considered. +- In an object constructor `Obj(..., field: p, ...)` when `field` is a `proc` + type. Analogous rules exist for array/set/tuple constructors. +- In a declaration like `x: T = p` when `T` is a `proc` type. + +As usual, ambiguous matches produce a compile-time error. + + Statements and expressions ========================== @@ -4414,7 +4432,7 @@ would. For example: if n > 0: yield n for e in toItr(recCountDown(n - 1)): - yield e + yield e for i in toItr(recCountDown(6)): # Emits: 6 5 4 3 2 1 echo i From 3241df2a13662e0158ec9d0b0c07996b22fb0939 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 19 Sep 2021 23:35:50 +0200 Subject: [PATCH 2523/3768] fixes #18858 [backport] (#18868) * fixes #18858 [backport] * ensure async tests work with --experimental:strictEffects [backport] * ensure async tests work with --experimental:strictEffects [backport] --- lib/pure/asyncfutures.nim | 2 +- lib/pure/osproc.nim | 6 +++++- lib/pure/unicode.nim | 5 ++++- tests/async/nim.cfg | 1 + 4 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 tests/async/nim.cfg diff --git a/lib/pure/asyncfutures.nim b/lib/pure/asyncfutures.nim index af3fbb3ec..6906d03fd 100644 --- a/lib/pure/asyncfutures.nim +++ b/lib/pure/asyncfutures.nim @@ -93,7 +93,7 @@ proc setCallSoonProc*(p: (proc(cbproc: proc ()) {.gcsafe.})) = ## Change current implementation of `callSoon`. This is normally called when dispatcher from `asyncdispatcher` is initialized. callSoonProc = p -proc callSoon*(cbproc: proc ()) = +proc callSoon*(cbproc: proc () {.gcsafe.}) = ## Call `cbproc` "soon". ## ## If async dispatcher is running, `cbproc` will be executed during next dispatcher tick. diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 1ac37661b..6a0ac9a8b 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -330,12 +330,16 @@ proc countProcessors*(): int {.rtl, extern: "nosp$1".} = ## It is implemented just calling `cpuinfo.countProcessors`. result = cpuinfo.countProcessors() +when not defined(nimHasEffectsOf): + {.pragma: effectsOf.} + proc execProcesses*(cmds: openArray[string], options = {poStdErrToStdOut, poParentStreams}, n = countProcessors(), beforeRunEvent: proc(idx: int) = nil, afterRunEvent: proc(idx: int, p: Process) = nil): int {.rtl, extern: "nosp$1", - tags: [ExecIOEffect, TimeEffect, ReadEnvEffect, RootEffect].} = + tags: [ExecIOEffect, TimeEffect, ReadEnvEffect, RootEffect], + effectsOf: [beforeRunEvent, afterRunEvent].} = ## Executes the commands `cmds` in parallel. ## Creates `n` processes that execute in parallel. ## diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim index dda631633..660be7814 100644 --- a/lib/pure/unicode.nim +++ b/lib/pure/unicode.nim @@ -705,8 +705,11 @@ proc capitalize*(s: string): string {.noSideEffect, fastRuneAt(s, i, rune, doInc = true) result = $toUpper(rune) & substr(s, i) +when not defined(nimHasEffectsOf): + {.pragma: effectsOf.} + proc translate*(s: string, replacements: proc(key: string): string): string {. - rtl, extern: "nuc$1".} = + rtl, extern: "nuc$1", effectsOf: replacements.} = ## Translates words in a string using the ``replacements`` proc to substitute ## words inside ``s`` with their replacements. ## diff --git a/tests/async/nim.cfg b/tests/async/nim.cfg new file mode 100644 index 000000000..be50f572c --- /dev/null +++ b/tests/async/nim.cfg @@ -0,0 +1 @@ +--experimental:strictEffects From c56ba3f06a3d77ff5eabb3b48ecd6a752e5a3ea8 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 20 Sep 2021 09:09:53 +0200 Subject: [PATCH 2524/3768] fixes #18847 [backport] (#18870) --- compiler/parser.nim | 5 +++-- tests/parser/tdoc_comments.nim | 4 ++++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/compiler/parser.nim b/compiler/parser.nim index 7d3c913da..5e9a7424a 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -1824,8 +1824,9 @@ proc parseRoutine(p: var Parser, kind: TNodeKind): PNode = # => moves comment `foo` to `fn` result.comment = body[0].comment body[0].comment = "" - else: - assert false, p.lex.config$body.info # avoids hard to track bugs, fail early. + #else: + # assert false, p.lex.config$body.info # avoids hard to track bugs, fail early. + # Yeah, that worked so well. There IS a bug in this logic, now what? proc newCommentStmt(p: var Parser): PNode = #| commentStmt = COMMENT diff --git a/tests/parser/tdoc_comments.nim b/tests/parser/tdoc_comments.nim index fa1374b45..3c86e69ea 100644 --- a/tests/parser/tdoc_comments.nim +++ b/tests/parser/tdoc_comments.nim @@ -69,3 +69,7 @@ type MyEnum3* = enum value5 ## only document the enum value + +# bug #18847 +proc close*() = ## asdfasdfsdfa + discard ## adsfasdfads From ee9795f76f1125e0addbf8cf5471cf188ad7f57a Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 20 Sep 2021 15:50:20 +0800 Subject: [PATCH 2525/3768] fix #18844 (#18851) --- lib/pure/concurrency/atomics.nim | 8 ++++---- tests/stdlib/concurrency/tatomics.nim | 22 +++++++++++++++++++++- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/lib/pure/concurrency/atomics.nim b/lib/pure/concurrency/atomics.nim index bdf1e8cc2..b252789fb 100644 --- a/lib/pure/concurrency/atomics.nim +++ b/lib/pure/concurrency/atomics.nim @@ -288,10 +288,10 @@ else: type # Atomic* {.importcpp: "_Atomic('0)".} [T] = object - AtomicInt8 {.importc: "_Atomic NI8", size: 1.} = object - AtomicInt16 {.importc: "_Atomic NI16", size: 2.} = object - AtomicInt32 {.importc: "_Atomic NI32", size: 4.} = object - AtomicInt64 {.importc: "_Atomic NI64", size: 8.} = object + AtomicInt8 {.importc: "_Atomic NI8".} = int8 + AtomicInt16 {.importc: "_Atomic NI16".} = int16 + AtomicInt32 {.importc: "_Atomic NI32".} = int32 + AtomicInt64 {.importc: "_Atomic NI64".} = int64 template atomicType*(T: typedesc[Trivial]): untyped = # Maps the size of a trivial type to it's internal atomic type diff --git a/tests/stdlib/concurrency/tatomics.nim b/tests/stdlib/concurrency/tatomics.nim index 260d00990..beae0ed6d 100644 --- a/tests/stdlib/concurrency/tatomics.nim +++ b/tests/stdlib/concurrency/tatomics.nim @@ -1,6 +1,6 @@ # test atomic operations -import atomics, bitops +import std/[atomics, bitops] type Object = object @@ -607,3 +607,23 @@ block clear: doAssert not location.testAndSet location.clear(moRelease) doAssert not location.testAndSet + +block: # bug #18844 + when not defined(cpp): # cpp pending pr #18836 + type + Deprivation = object of RootObj + memes: Atomic[int] + Zoomer = object + dopamine: Deprivation + + block: + var x = Deprivation() + var y = Zoomer() + doAssert x.memes.load == 0 + doAssert y.dopamine.memes.load == 0 + + block: + var x: Deprivation + var y: Zoomer + doAssert x.memes.load == 0 + doAssert y.dopamine.memes.load == 0 From 14ced06bb1ef7f23fc6391d16763f1421a3edc80 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 20 Sep 2021 11:37:54 +0200 Subject: [PATCH 2526/3768] fixes #18863 [backport] (#18871) --- compiler/semstmts.nim | 2 +- compiler/semtempl.nim | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index c2f05ccce..81d6eeda2 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1905,7 +1905,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, if tfTriggersCompileTime in s.typ.flags: incl(s.flags, sfCompileTime) if n[patternPos].kind != nkEmpty: - n[patternPos] = semPattern(c, n[patternPos]) + n[patternPos] = semPattern(c, n[patternPos], s) if s.kind == skIterator: s.typ.flags.incl(tfIterator) elif s.kind == skFunc: diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index 2636784af..eda4ca6cc 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -334,7 +334,7 @@ proc semTemplSomeDecl(c: var TemplCtx, n: PNode, symKind: TSymKind; start = 0) = illFormedAst(a, c.c.config) -proc semPattern(c: PContext, n: PNode): PNode +proc semPattern(c: PContext, n: PNode; s: PSym): PNode proc semTemplBodySons(c: var TemplCtx, n: PNode): PNode = result = n @@ -645,7 +645,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = if allUntyped: incl(s.flags, sfAllUntyped) if n[patternPos].kind != nkEmpty: - n[patternPos] = semPattern(c, n[patternPos]) + n[patternPos] = semPattern(c, n[patternPos], s) var ctx: TemplCtx ctx.toBind = initIntSet() @@ -798,7 +798,7 @@ proc semPatternBody(c: var TemplCtx, n: PNode): PNode = for i in 0.. Date: Mon, 20 Sep 2021 13:24:44 +0200 Subject: [PATCH 2527/3768] [backport] Revert "use new Nimble, with lockfiles (#18810)" (#18872) This reverts commit f373c17ad926b669bb3b5819ae1dff4bde1da88a. For more details, see: - https://github.com/nim-lang/nimble/issues/940 - https://github.com/nim-lang/Nim/issues/18840 - https://forum.nim-lang.org/t/8404 --- koch.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/koch.nim b/koch.nim index e42ea53de..8dd897028 100644 --- a/koch.nim +++ b/koch.nim @@ -10,7 +10,7 @@ # const - NimbleStableCommit = "795704833ddfd0cdaefb45c60551d3ea205279ef" # master + NimbleStableCommit = "d13f3b8ce288b4dc8c34c219a4e050aaeaf43fc9" # master # examples of possible values: #head, #ea82b54, 1.2.3 FusionStableHash = "#372ee4313827ef9f2ea388840f7d6b46c2b1b014" HeadHash = "#head" From 928ea6bb4c0cc791f1b81e55dbebd14d5c6a7315 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 20 Sep 2021 13:32:33 +0200 Subject: [PATCH 2528/3768] atlas: added --cfgHere switch [backport] (#18873) --- tools/atlas/atlas.nim | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/tools/atlas/atlas.nim b/tools/atlas/atlas.nim index 370138029..e604c3167 100644 --- a/tools/atlas/atlas.nim +++ b/tools/atlas/atlas.nim @@ -27,6 +27,8 @@ Command: Options: --keepCommits do not perform any `git checkouts` + --cfgHere also create/maintain a nim.cfg in the current + working directory --version show the version --help show this help """ @@ -58,6 +60,7 @@ type projectDir, workspace: string hasPackageList: bool keepCommits: bool + cfgHere: bool p: Table[string, string] # name -> url mapping processed: HashSet[string] # the key is (url / commit) errors: int @@ -353,7 +356,7 @@ proc clone(c: var AtlasContext; start: string): seq[string] = error c, toName(start), "cannot resolve package name" return - c.projectDir = work[0].name.string + c.projectDir = c.workspace / work[0].name.string result = @[] var i = 0 while i < work.len: @@ -376,10 +379,16 @@ const configPatternBegin = "############# begin Atlas config section ##########\n" configPatternEnd = "############# end Atlas config section ##########\n" -proc patchNimCfg(c: var AtlasContext; deps: seq[string]) = +proc patchNimCfg(c: var AtlasContext; deps: seq[string]; cfgHere: bool) = var paths = "--noNimblePath\n" - for d in deps: - paths.add "--path:\"../" & d.replace("\\", "/") & "\"\n" + if cfgHere: + let cwd = getCurrentDir() + for d in deps: + let x = relativePath(c.workspace / d, cwd, '/') + paths.add "--path:\"" & x & "\"\n" + else: + for d in deps: + paths.add "--path:\"../" & d.replace("\\", "/") & "\"\n" var cfgContent = configPatternBegin & paths & configPatternEnd @@ -387,7 +396,8 @@ proc patchNimCfg(c: var AtlasContext; deps: seq[string]) = assert readFile(TestsDir / "nim.cfg") == cfgContent c.mockupSuccess = true else: - let cfg = c.projectDir / "nim.cfg" + let cfg = if cfgHere: getCurrentDir() / "nim.cfg" + else: c.projectDir / "nim.cfg" if not fileExists(cfg): writeFile(cfg, cfgContent) else: @@ -437,6 +447,7 @@ proc main = of "help", "h": writeHelp() of "version", "v": writeVersion() of "keepcommits": c.keepCommits = true + of "cfghere": c.cfgHere = true else: writeHelp() of cmdEnd: assert false, "cannot happen" @@ -449,7 +460,9 @@ proc main = of "clone": singleArg() let deps = clone(c, args[0]) - patchNimCfg c, deps + patchNimCfg c, deps, false + if c.cfgHere: + patchNimCfg c, deps, true when MockupRun: if not c.mockupSuccess: error "There were problems." From 0ad601d3c1657216d27be839173861594e3e2421 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 22 Sep 2021 09:43:06 +0200 Subject: [PATCH 2529/3768] fixes #18856 [backport] (#18879) --- compiler/semexprs.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 5a1426f2b..ee2626922 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1661,7 +1661,7 @@ proc takeImplicitAddr(c: PContext, n: PNode; isLent: bool): PNode = localError(c.config, n.info, errXStackEscape % renderTree(n, {renderNoComments})) else: localError(c.config, n.info, errExprHasNoAddress) - result = newNodeIT(nkHiddenAddr, n.info, makePtrType(c, n.typ)) + result = newNodeIT(nkHiddenAddr, n.info, if n.typ.kind in {tyVar, tyLent}: n.typ else: makePtrType(c, n.typ)) result.add(n) proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} = From 6163bdd279a2c33d4290223cc7f466c8070b8ce4 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 22 Sep 2021 15:07:36 +0200 Subject: [PATCH 2530/3768] closes #16132 [backport] (#18880) * closes #16132 [backport] * fixes #16132 [backport] --- compiler/semexprs.nim | 13 ++++++++----- tests/views/tviews1.nim | 14 +++++++++++++- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index ee2626922..9134c1301 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1667,11 +1667,14 @@ proc takeImplicitAddr(c: PContext, n: PNode; isLent: bool): PNode = proc asgnToResultVar(c: PContext, n, le, ri: PNode) {.inline.} = if le.kind == nkHiddenDeref: var x = le[0] - if x.kind == nkSym and x.sym.kind == skResult and (x.typ.kind in {tyVar, tyLent} or classifyViewType(x.typ) != noView): - n[0] = x # 'result[]' --> 'result' - n[1] = takeImplicitAddr(c, ri, x.typ.kind == tyLent) - x.typ.flags.incl tfVarIsPtr - #echo x.info, " setting it for this type ", typeToString(x.typ), " ", n.info + if x.kind == nkSym: + if x.sym.kind == skResult and (x.typ.kind in {tyVar, tyLent} or classifyViewType(x.typ) != noView): + n[0] = x # 'result[]' --> 'result' + n[1] = takeImplicitAddr(c, ri, x.typ.kind == tyLent) + x.typ.flags.incl tfVarIsPtr + #echo x.info, " setting it for this type ", typeToString(x.typ), " ", n.info + elif sfGlobal in x.sym.flags: + x.typ.flags.incl tfVarIsPtr proc borrowCheck(c: PContext, n, le, ri: PNode) = const diff --git a/tests/views/tviews1.nim b/tests/views/tviews1.nim index 51f17b9d6..49d79c5b5 100644 --- a/tests/views/tviews1.nim +++ b/tests/views/tviews1.nim @@ -5,7 +5,8 @@ discard """ 3 2 3 -3''' +3 +15''' targets: "c cpp" """ @@ -26,3 +27,14 @@ proc main(s: seq[int]) = take x main(@[11, 22, 33]) + +var x: int + +proc foo(x: var int): var int = + once: x = 42 + return x + +var y: var int = foo(x) +y = 15 +echo foo(x) +# bug #16132 From 7d2a4c0880223c2c80a5c30227c26fa497898f39 Mon Sep 17 00:00:00 2001 From: Miran Date: Thu, 23 Sep 2021 11:00:36 +0200 Subject: [PATCH 2531/3768] update csources hash (#18885) --- config/build_config.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/build_config.txt b/config/build_config.txt index 75dbb1da3..aee15e695 100644 --- a/config/build_config.txt +++ b/config/build_config.txt @@ -2,4 +2,4 @@ nim_comment="key-value pairs for windows/posix bootstrapping build scripts" nim_csourcesDir=csources_v1 nim_csourcesUrl=https://github.com/nim-lang/csources_v1.git nim_csourcesBranch=master -nim_csourcesHash=9a7f751d23c49c75a0b6f63a234c575dc0df7231 +nim_csourcesHash=5e9be0383f842c9c22ae86e41946a896c43e95d0 From 90a2b5afd8368777e5da9ab97c28130ba683e1d3 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 23 Sep 2021 16:47:24 +0200 Subject: [PATCH 2532/3768] correct effect tracking for .borrowed procs [backport] (#18882) * correct effect tracking for .borrowed procs [backport] * progress * fix error message in a test * correctly fix it Co-authored-by: narimiran --- compiler/ast.nim | 2 +- compiler/ccgexprs.nim | 4 ++-- compiler/jsgen.nim | 2 +- compiler/sempass2.nim | 13 +++++++++---- compiler/semstmts.nim | 5 ++++- compiler/vmgen.nim | 2 +- lib/system.nim | 2 +- tests/effects/tgcsafe3.nim | 2 +- 8 files changed, 20 insertions(+), 12 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 50d048edd..0a3b1b72d 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -673,7 +673,7 @@ type mSwap, mIsNil, mArrToSeq, mNewString, mNewStringOfCap, mParseBiggestFloat, mMove, mWasMoved, mDestroy, mTrace, - mDefault, mUnown, mIsolate, mAccessEnv, mReset, + mDefault, mUnown, mFinished, mIsolate, mAccessEnv, mReset, mArray, mOpenArray, mRange, mSet, mSeq, mVarargs, mRef, mPtr, mVar, mDistinct, mVoid, mTuple, mOrdinal, mIterableType, diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 7a3d769aa..ac4a26bd6 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2335,7 +2335,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = genDollar(p, e, d, "#nimFloatToStr($1)") of mCStrToStr: genDollar(p, e, d, "#cstrToNimstr($1)") of mStrToStr, mUnown: expr(p, e[1], d) - of mIsolate: genCall(p, e, d) + of mIsolate, mFinished: genCall(p, e, d) of mEnumToStr: if optTinyRtti in p.config.globalOptions: genEnumToStr(p, e, d) @@ -2929,7 +2929,7 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = if n[genericParamsPos].kind == nkEmpty: var prc = n[namePos].sym if useAliveDataFromDce in p.module.flags: - if p.module.alive.contains(prc.itemId.item) and prc.magic in {mNone, mIsolate}: + if p.module.alive.contains(prc.itemId.item) and prc.magic in {mNone, mIsolate, mFinished}: genProc(p.module, prc) elif prc.skipGenericOwner.kind == skModule and sfCompileTime notin prc.flags: if ({sfExportc, sfCompilerProc} * prc.flags == {sfExportc}) or diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index f009c83f2..ff4d2839e 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -657,7 +657,7 @@ proc arithAux(p: PProc, n: PNode, r: var TCompRes, op: TMagic) = of mIntToStr: applyFormat("cstrToNimstr(($1) + \"\")", "cstrToNimstr(($1) + \"\")") of mInt64ToStr: applyFormat("cstrToNimstr(($1) + \"\")", "cstrToNimstr(($1) + \"\")") of mCStrToStr: applyFormat("cstrToNimstr($1)", "cstrToNimstr($1)") - of mStrToStr, mUnown, mIsolate: applyFormat("$1", "$1") + of mStrToStr, mUnown, mIsolate, mFinished: applyFormat("$1", "$1") else: assert false, $op diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 6a0e8772a..9a27d14fa 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -245,9 +245,14 @@ proc listGcUnsafety(s: PSym; onlyWarning: bool; cycleCheck: var IntSet; conf: Co let msgKind = if onlyWarning: warnGcUnsafe2 else: errGenerated case u.kind of skLet, skVar: - message(conf, s.info, msgKind, - ("'$#' is not GC-safe as it accesses '$#'" & - " which is a global using GC'ed memory") % [s.name.s, u.name.s]) + if u.typ.skipTypes(abstractInst).kind == tyProc: + message(conf, s.info, msgKind, + "'$#' is not GC-safe as it calls '$#'" % + [s.name.s, u.name.s]) + else: + message(conf, s.info, msgKind, + ("'$#' is not GC-safe as it accesses '$#'" & + " which is a global using GC'ed memory") % [s.name.s, u.name.s]) of routineKinds: # recursive call *always* produces only a warning so the full error # message is printed: @@ -850,7 +855,7 @@ proc trackCall(tracked: PEffects; n: PNode) = mergeRaises(tracked, effectList[exceptionEffects], n) mergeTags(tracked, effectList[tagEffects], n) gcsafeAndSideeffectCheck() - if a.kind != nkSym or a.sym.magic != mNBindSym: + if a.kind != nkSym or a.sym.magic notin {mNBindSym, mFinished}: for i in 1.. 0: + s.typ.n[0] = b.typ.n[0] + s.typ.flags = b.typ.flags else: localError(c.config, n.info, errNoSymbolToBorrowFromFound) @@ -1954,7 +1957,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, if not hasProto: implicitPragmas(c, s, n.info, validPragmas) - if n[pragmasPos].kind != nkEmpty: + if n[pragmasPos].kind != nkEmpty and sfBorrow notin s.flags: setEffectsForProcType(c.graph, s.typ, n[pragmasPos], s) s.typ.flags.incl tfEffectSystemWorkaround diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index f2758ff37..bb095d3ee 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1029,7 +1029,7 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = c.genAsgnPatch(n[1], d) c.freeTemp(d) of mOrd, mChr, mArrToSeq, mUnown: c.gen(n[1], dest) - of mIsolate: + of mIsolate, mFinished: genCall(c, n, dest) of mNew, mNewFinalize: unused(c, n, dest) diff --git a/lib/system.nim b/lib/system.nim index c93af0854..a5356cb54 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2453,7 +2453,7 @@ when notJSnotNims: else: {.error: "Only closure function and iterator are allowed!".} - proc finished*[T: proc](x: T): bool {.noSideEffect, inline.} = + proc finished*[T: proc](x: T): bool {.noSideEffect, inline, magic: "Finished".} = ## It can be used to determine if a first class iterator has finished. when T is "iterator": {.emit: """ diff --git a/tests/effects/tgcsafe3.nim b/tests/effects/tgcsafe3.nim index 5137efe4c..36ea5112c 100644 --- a/tests/effects/tgcsafe3.nim +++ b/tests/effects/tgcsafe3.nim @@ -1,5 +1,5 @@ discard """ - errormsg: "'myproc' is not GC-safe as it accesses 'global_proc' which is a global using GC'ed memory" + errormsg: "'myproc' is not GC-safe as it calls 'global_proc'" line: 12 cmd: "nim $target --hints:on --threads:on $options $file" """ From 561b01004cfeb572f82327cd8a3de55d076cb5ff Mon Sep 17 00:00:00 2001 From: Miran Date: Fri, 24 Sep 2021 11:24:55 +0200 Subject: [PATCH 2533/3768] [backport] change some examples in tut1.rst (#18884) * [backport] change some examples in tut1.rst * Update doc/tut1.rst * Update doc/tut1.rst Co-authored-by: Andreas Rumpf --- doc/tut1.rst | 47 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 12 deletions(-) diff --git a/doc/tut1.rst b/doc/tut1.rst index 210ea324e..f492f12b0 100644 --- a/doc/tut1.rst +++ b/doc/tut1.rst @@ -1643,9 +1643,25 @@ variables! For example: echo badname echo badext +Tuple unpacking is also supported in for-loops: + +.. code-block:: nim + :test: "nim c $1" + let a = [(10, 'a'), (20, 'b'), (30, 'c')] + + for (x, c) in a: + echo x + # This will output: 10; 20; 30 + + # Accessing the index is also possible: + for i, (x, c) in a: + echo i, c + # This will output: 0a; 1b; 2c + Fields of tuples are always public, they don't need to be explicity marked to be exported, unlike for example fields in an object type. + Reference and pointer types --------------------------- @@ -1675,13 +1691,18 @@ operators perform implicit dereferencing operations for reference types: Node = ref object le, ri: Node data: int - var - n: Node - new(n) - n.data = 9 + + var n = Node(data: 9) + echo n.data # no need to write n[].data; in fact n[].data is highly discouraged! -To allocate a new traced object, the built-in procedure `new` must be used. +To allocate a new traced object, the built-in procedure `new` can be used: + +.. code-block:: nim + + var n: Node + new(n) + To deal with untraced memory, the procedures `alloc`, `dealloc` and `realloc` can be used. The `system `_ module's documentation contains further details. @@ -1701,15 +1722,17 @@ Example: .. code-block:: nim :test: "nim c $1" - proc echoItem(x: int) = echo x + proc greet(name: string): string = + "Hello, " & name & "!" - proc forEach(action: proc (x: int)) = - const - data = [2, 3, 5, 7, 11] - for d in items(data): - action(d) + proc bye(name: string): string = + "Goodbye, " & name & "." - forEach(echoItem) + proc communicate(greeting: proc (x: string): string, name: string) = + echo greeting(name) + + communicate(greet, "John") + communicate(bye, "Mary") A subtle issue with procedural types is that the calling convention of the procedure influences the type compatibility: procedural types are only compatible From 5d315ebcc2d4f46b4a74c6ab10146466c894b9de Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 24 Sep 2021 13:24:41 +0200 Subject: [PATCH 2534/3768] ported Nim to proprietary CrossOS [backport] (#18889) --- ci/funs.sh | 2 ++ compiler/installer.ini | 1 + compiler/options.nim | 6 ++++-- compiler/platform.nim | 6 +++++- lib/pure/os.nim | 2 +- tools/niminst/buildsh.nimf | 5 +++++ 6 files changed, 18 insertions(+), 4 deletions(-) diff --git a/ci/funs.sh b/ci/funs.sh index e25922c8c..1638931c6 100644 --- a/ci/funs.sh +++ b/ci/funs.sh @@ -74,6 +74,8 @@ _nimBuildCsourcesIfNeeded(){ makeX=gmake elif [ "$unamestr" = 'NetBSD' ]; then makeX=gmake + elif [ "$unamestr" = 'CROSSOS' ]; then + makeX=gmake else makeX=make fi diff --git a/compiler/installer.ini b/compiler/installer.ini index 3f1630a92..9e31b4e3f 100644 --- a/compiler/installer.ini +++ b/compiler/installer.ini @@ -13,6 +13,7 @@ Platforms: """ netbsd: i386;amd64 openbsd: i386;amd64;arm;arm64 dragonfly: i386;amd64 + crossos: amd64 haiku: i386;amd64 android: i386;arm;arm64 nintendoswitch: arm64 diff --git a/compiler/options.nim b/compiler/options.nim index ea302aed6..eafcd816d 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -577,11 +577,13 @@ proc isDefined*(conf: ConfigRef; symbol: string): bool = osQnx, osAtari, osAix, osHaiku, osVxWorks, osSolaris, osNetbsd, osFreebsd, osOpenbsd, osDragonfly, osMacosx, osIos, - osAndroid, osNintendoSwitch, osFreeRTOS} + osAndroid, osNintendoSwitch, osFreeRTOS, osCrossos} of "linux": result = conf.target.targetOS in {osLinux, osAndroid} of "bsd": - result = conf.target.targetOS in {osNetbsd, osFreebsd, osOpenbsd, osDragonfly} + result = conf.target.targetOS in {osNetbsd, osFreebsd, osOpenbsd, osDragonfly, osCrossos} + of "freebsd": + result = conf.target.targetOS in {osFreebsd, osCrossos} of "emulatedthreadvars": result = platform.OS[conf.target.targetOS].props.contains(ospLacksThreadVars) of "msdos": result = conf.target.targetOS == osDos diff --git a/compiler/platform.nim b/compiler/platform.nim index 7bc77d809..1bc00b629 100644 --- a/compiler/platform.nim +++ b/compiler/platform.nim @@ -20,7 +20,7 @@ type TSystemOS* = enum # Also add OS in initialization section and alias # conditionals to condsyms (end of module). osNone, osDos, osWindows, osOs2, osLinux, osMorphos, osSkyos, osSolaris, - osIrix, osNetbsd, osFreebsd, osOpenbsd, osDragonfly, osAix, osPalmos, osQnx, + osIrix, osNetbsd, osFreebsd, osOpenbsd, osDragonfly, osCrossos, osAix, osPalmos, osQnx, osAmiga, osAtari, osNetware, osMacos, osMacosx, osIos, osHaiku, osAndroid, osVxWorks osGenode, osJS, osNimVM, osStandalone, osNintendoSwitch, osFreeRTOS, osAny @@ -105,6 +105,10 @@ const scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".", props: {ospNeedsPIC, ospPosix}), + (name: "CROSSOS", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/", + objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/", + scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".", + props: {ospNeedsPIC, ospPosix}), (name: "AIX", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/", objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/", scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".", diff --git a/lib/pure/os.nim b/lib/pure/os.nim index cfdacdb3c..dd1f9f0b6 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -3007,7 +3007,7 @@ when not weirdTarget and (defined(freebsd) or defined(dragonfly) or defined(netb proc sysctl(name: ptr cint, namelen: cuint, oldp: pointer, oldplen: ptr csize_t, newp: pointer, newplen: csize_t): cint {.importc: "sysctl",header: """#include - #include """} + #include """.} const CTL_KERN = 1 KERN_PROC = 14 diff --git a/tools/niminst/buildsh.nimf b/tools/niminst/buildsh.nimf index f74413c09..7b30cd86c 100644 --- a/tools/niminst/buildsh.nimf +++ b/tools/niminst/buildsh.nimf @@ -109,6 +109,11 @@ case $uos in CC="clang" LINK_FLAGS="$LINK_FLAGS -lm" ;; + *crossos* ) + myos="crossos" + CC="clang" + LINK_FLAGS="$LINK_FLAGS -lm" + ;; *openbsd* ) myos="openbsd" CC="clang" From f7d642f2f3925feb5addc315eb59caf326cad470 Mon Sep 17 00:00:00 2001 From: Clyybber Date: Fri, 24 Sep 2021 14:59:48 +0200 Subject: [PATCH 2535/3768] [backport] arc: improve compile time of (nested) loops (#18890) --- compiler/dfa.nim | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/compiler/dfa.nim b/compiler/dfa.nim index 65c483e9c..0539f6699 100644 --- a/compiler/dfa.nim +++ b/compiler/dfa.nim @@ -308,6 +308,11 @@ when true: # We unroll every loop 3 times. We emulate 0, 1, 2 iterations # through the loop. We need to prove this is correct for our # purposes. But Herb Sutter claims it is. (Proof by authority.) + # + # EDIT: Actually, we only need to unroll 2 times + # because Nim doesn't have a way of breaking/goto-ing into + # a loop iteration. Unrolling 2 times is much better for compile + # times of nested loops than 3 times, so we do that here. #[ while cond: body @@ -347,12 +352,12 @@ when true: # 'while true' is an idiom in Nim and so we produce # better code for it: withBlock(nil): - for i in 0..2: + for i in 0..1: c.gen(n[1]) else: withBlock(nil): - var endings: array[3, TPosition] - for i in 0..2: + var endings: array[2, TPosition] + for i in 0..1: c.gen(n[0]) endings[i] = c.forkI(n) c.gen(n[1]) From 7e5eab571e6e8e57928b40f535802aec04e04633 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 24 Sep 2021 16:27:34 +0200 Subject: [PATCH 2536/3768] closes #18690; make view types stricter [backport] (#18891) * closes #18690 * don't allow capturing of view types [backport] --- compiler/lambdalifting.nim | 6 ++---- tests/views/tviews1.nim | 13 ++++++++++++- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index c555cedfe..a622f6de6 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -12,7 +12,7 @@ import intsets, strutils, options, ast, astalgo, msgs, idents, renderer, types, magicsys, lowerings, tables, modulegraphs, lineinfos, - transf, liftdestructors + transf, liftdestructors, typeallowed discard """ The basic approach is that captured vars need to be put on the heap and @@ -191,9 +191,7 @@ proc interestingVar(s: PSym): bool {.inline.} = s.typ.kind notin {tyStatic, tyTypeDesc} proc illegalCapture(s: PSym): bool {.inline.} = - result = skipTypes(s.typ, abstractInst).kind in - {tyVar, tyOpenArray, tyVarargs, tyLent} or - s.kind == skResult + result = classifyViewType(s.typ) != noView or s.kind == skResult proc isInnerProc(s: PSym): bool = if s.kind in {skProc, skFunc, skMethod, skConverter, skIterator} and s.magic == mNone: diff --git a/tests/views/tviews1.nim b/tests/views/tviews1.nim index 49d79c5b5..3dbf664fc 100644 --- a/tests/views/tviews1.nim +++ b/tests/views/tviews1.nim @@ -6,7 +6,8 @@ discard """ 2 3 3 -15''' +15 +(oa: [1, 3, 4])''' targets: "c cpp" """ @@ -38,3 +39,13 @@ var y: var int = foo(x) y = 15 echo foo(x) # bug #16132 + +# bug #18690 + +type + F = object + oa: openarray[int] + +let s1 = @[1,3,4,5,6] +var test = F(oa: toOpenArray(s1, 0, 2)) +echo test From 4adada0d8016016104d6259a1167a7f8b37f1611 Mon Sep 17 00:00:00 2001 From: flywind Date: Fri, 24 Sep 2021 22:34:12 +0800 Subject: [PATCH 2537/3768] fix a typo (rename `temporal` to `temporary` (#18892) [backport] --- lib/pure/strutils.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 3179c73d2..873949ca6 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -2039,7 +2039,7 @@ func countLines*(s: string): int {.rtl, extern: "nsuCountLines".} = ## Returns the number of lines in the string `s`. ## ## This is the same as `len(splitLines(s))`, but much more efficient - ## because it doesn't modify the string creating temporal objects. Every + ## because it doesn't modify the string creating temporary objects. Every ## `character literal `_ ## newline combination (CR, LF, CR-LF) is supported. ## From 8bdb98539570fdf3c41ee5509d5b90913ac1803e Mon Sep 17 00:00:00 2001 From: flywind Date: Sat, 25 Sep 2021 19:22:00 +0800 Subject: [PATCH 2538/3768] fix wrong name (rnimsyn => renderer; pnimsyn => parser; scanner => lexer) (#18895) * fix wrong module name * rephrase more word --- compiler/filter_tmpl.nim | 2 +- compiler/lexer.nim | 4 ++-- compiler/nimconf.nim | 2 +- compiler/renderer.nim | 4 ++-- doc/manual.rst | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/compiler/filter_tmpl.nim b/compiler/filter_tmpl.nim index 6165ff2f3..84ed99164 100644 --- a/compiler/filter_tmpl.nim +++ b/compiler/filter_tmpl.nim @@ -22,7 +22,7 @@ type info: TLineInfo indent, emitPar: int x: string # the current input line - outp: PLLStream # the output will be parsed by pnimsyn + outp: PLLStream # the output will be parsed by parser subsChar, nimDirective: char emit, conc, toStr: string curly, bracket, par: int diff --git a/compiler/lexer.nim b/compiler/lexer.nim index ede16fdf1..506b0e924 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -7,12 +7,12 @@ # distribution, for details about the copyright. # -# This scanner is handwritten for efficiency. I used an elegant buffering +# This lexer is handwritten for efficiency. I used an elegant buffering # scheme which I have not seen anywhere else: # We guarantee that a whole line is in the buffer. Thus only when scanning # the \n or \r character we have to check whether we need to read in the next # chunk. (\n or \r already need special handling for incrementing the line -# counter; choosing both \n and \r allows the scanner to properly read Unix, +# counter; choosing both \n and \r allows the lexer to properly read Unix, # DOS or Macintosh text files, even when it is not the native format. import diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim index 94c93a283..1cf22e20a 100644 --- a/compiler/nimconf.nim +++ b/compiler/nimconf.nim @@ -14,7 +14,7 @@ import options, idents, wordrecg, strtabs, lineinfos, pathutils, scriptconfig # ---------------- configuration file parser ----------------------------- -# we use Nim's scanner here to save space and work +# we use Nim's lexer here to save space and work proc ppGetTok(L: var Lexer, tok: var Token) = # simple filter diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 393f35289..22a2d4cbd 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -410,7 +410,7 @@ proc atom(g: TSrcGen; n: PNode): string = if (n.typ != nil) and (n.typ.sym != nil): result = n.typ.sym.name.s else: result = "[type node]" else: - internalError(g.config, "rnimsyn.atom " & $n.kind) + internalError(g.config, "renderer.atom " & $n.kind) result = "" proc lcomma(g: TSrcGen; n: PNode, start: int = 0, theEnd: int = - 1): int = @@ -1699,7 +1699,7 @@ proc gsub(g: var TSrcGen, n: PNode, c: TContext, fromStmtList = false) = gsub(g, n[0], c) else: #nkNone, nkExplicitTypeListCall: - internalError(g.config, n.info, "rnimsyn.gsub(" & $n.kind & ')') + internalError(g.config, n.info, "renderer.gsub(" & $n.kind & ')') proc renderTree*(n: PNode, renderFlags: TRenderFlags = {}): string = if n == nil: return "" diff --git a/doc/manual.rst b/doc/manual.rst index 09826441c..dfcf0ba54 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -216,7 +216,7 @@ comment: .. code-block:: nim i = 0 # This is a single comment over multiple lines. - # The scanner merges these two pieces. + # The lexer merges these two pieces. # The comment continues here. From e3b19cbe52e0578de315d259fbb066bfe991a60f Mon Sep 17 00:00:00 2001 From: Aditya Siram Date: Sat, 25 Sep 2021 07:17:41 -0500 Subject: [PATCH 2539/3768] fixes #18878 (#18883) --- lib/core/typeinfo.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/core/typeinfo.nim b/lib/core/typeinfo.nim index d66dc1898..18d2091b9 100644 --- a/lib/core/typeinfo.nim +++ b/lib/core/typeinfo.nim @@ -216,7 +216,8 @@ proc extendSeq*(x: Any) = when defined(gcDestructors): var s = cast[ptr NimSeqV2Reimpl](x.value) let elem = x.rawType.base - s.p = cast[ptr NimSeqPayloadReimpl](prepareSeqAdd(s.len, s.p, 1, elem.size, elem.align)) + if s.p == nil or s.p.cap < s.len+1: + s.p = cast[ptr NimSeqPayloadReimpl](prepareSeqAdd(s.len, s.p, 1, elem.size, elem.align)) inc s.len else: var y = cast[ptr PGenSeq](x.value)[] From 57eb763c65c2d517e2b6ca7b2ca2d3bb5ab6c3d5 Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 27 Sep 2021 19:13:02 +0800 Subject: [PATCH 2540/3768] activate winim (#18907) `winim` is dependent on windows API. --- testament/important_packages.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 657ddb2d1..0c47104ac 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -159,7 +159,7 @@ pkg "unicodeplus", "nim c -d:release -r tests/tests.nim" pkg "unpack" pkg "weave", "nimble test_gc_arc", allowFailure = true pkg "websocket", "nim c websocket.nim" -pkg "winim", allowFailure = true +pkg "winim", "nim c winim.nim" pkg "with" pkg "ws", allowFailure = true pkg "yaml", "nim c -r test/tserialization.nim" From 5325a366e79b6e34fcd2b2dfe675897212f8ca2d Mon Sep 17 00:00:00 2001 From: Federico Ceratto Date: Mon, 27 Sep 2021 16:35:14 +0100 Subject: [PATCH 2541/3768] Add armv8l support (Closes: #18898) (#18901) [backport] --- tools/niminst/buildsh.nimf | 2 +- tools/niminst/makefile.nimf | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/niminst/buildsh.nimf b/tools/niminst/buildsh.nimf index 7b30cd86c..9850abd89 100644 --- a/tools/niminst/buildsh.nimf +++ b/tools/niminst/buildsh.nimf @@ -213,7 +213,7 @@ case $ucpu in mycpu="alpha" ;; *aarch64*|*arm64* ) mycpu="arm64" ;; - *arm*|*armv6l*|*armv71* ) + *arm*|*armv6l*|*armv7l*|*armv8l* ) mycpu="arm" ;; *riscv64|riscv* ) mycpu="riscv64" ;; diff --git a/tools/niminst/makefile.nimf b/tools/niminst/makefile.nimf index a0e9a40f7..69e87b837 100644 --- a/tools/niminst/makefile.nimf +++ b/tools/niminst/makefile.nimf @@ -157,6 +157,9 @@ endif ifeq ($(ucpu),armv7hl) mycpu = arm endif +ifeq ($(ucpu),armv8l) + mycpu = arm +endif ifeq ($(ucpu),aarch64) mycpu = arm64 endif From cdf9ac675b4348a7b5239186637c54920bb92619 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 27 Sep 2021 19:25:00 +0200 Subject: [PATCH 2542/3768] this ensures libp2p continues to compile [backport] (#18908) --- compiler/cgen.nim | 11 ++++++++--- compiler/guards.nim | 10 ++++++++-- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index a06a87172..43410dc60 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -855,10 +855,15 @@ proc closureSetup(p: BProc, prc: PSym) = [rdLoc(env.loc), getTypeDesc(p.module, env.typ)]) proc containsResult(n: PNode): bool = - if n.kind == nkSym and n.sym.kind == skResult: - result = true + result = false + case n.kind + of nkEmpty..pred(nkSym), succ(nkSym)..nkNilLit, nkFormalParams: + discard + of nkSym: + if n.sym.kind == skResult: + result = true else: - for i in 0.. Date: Mon, 27 Sep 2021 22:23:31 +0200 Subject: [PATCH 2543/3768] fixes 'lent T' inside object constructor [backport] (#18911) * fixes 'lent T' inside object constructor [backport] * progress --- compiler/sem.nim | 3 ++- compiler/semobjconstr.nim | 2 +- compiler/semstmts.nim | 2 +- tests/views/tviews1.nim | 17 +++++++++++++++++ 4 files changed, 21 insertions(+), 3 deletions(-) diff --git a/compiler/sem.nim b/compiler/sem.nim index bdecbe602..70c57864c 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -107,12 +107,13 @@ proc fitNode(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode = else: result = fitNodePostMatch(c, formal, result) -proc fitNodeForLocalVar(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode = +proc fitNodeConsiderViewType(c: PContext, formal: PType, arg: PNode; info: TLineInfo): PNode = let a = fitNode(c, formal, arg, info) if formal.kind in {tyVar, tyLent}: #classifyViewType(formal) != noView: result = newNodeIT(nkHiddenAddr, a.info, formal) result.add a + formal.flags.incl tfVarIsPtr else: result = a diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim index 1563e5c16..001956ede 100644 --- a/compiler/semobjconstr.nim +++ b/compiler/semobjconstr.nim @@ -79,7 +79,7 @@ proc semConstrField(c: PContext, flags: TExprFlags, var initValue = semExprFlagDispatched(c, assignment[1], flags) if initValue != nil: - initValue = fitNode(c, field.typ, initValue, assignment.info) + initValue = fitNodeConsiderViewType(c, field.typ, initValue, assignment.info) assignment[0] = newSymNode(field) assignment[1] = initValue assignment.flags.incl nfSem diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 3b699f6c8..4607e857a 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -545,7 +545,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = else: # BUGFIX: ``fitNode`` is needed here! # check type compatibility between def.typ and typ - def = fitNodeForLocalVar(c, typ, def, def.info) + def = fitNodeConsiderViewType(c, typ, def, def.info) #changeType(def.skipConv, typ, check=true) else: typ = def.typ.skipTypes({tyStatic, tySink}).skipIntLit(c.idgen) diff --git a/tests/views/tviews1.nim b/tests/views/tviews1.nim index 3dbf664fc..ced487ce8 100644 --- a/tests/views/tviews1.nim +++ b/tests/views/tviews1.nim @@ -49,3 +49,20 @@ type let s1 = @[1,3,4,5,6] var test = F(oa: toOpenArray(s1, 0, 2)) echo test + +type + Foo = object + x: string + y: seq[int] + data: array[10000, byte] + + View[T] = object + x: lent T + +proc mainB = + let f = Foo(y: @[1, 2, 3]) + let foo = View[Foo](x: f) + assert foo.x.x == "" + assert foo.x.y == @[1, 2, 3] + +mainB() From 5eb357f5a2db81d7027eaf5430cda74370e45a67 Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 28 Sep 2021 12:45:25 +0800 Subject: [PATCH 2544/3768] change comment (#18913) --- lib/system.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/system.nim b/lib/system.nim index a5356cb54..358aae938 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -160,7 +160,7 @@ when defined(nimHashOrdinalFixed): ## as well as their subtypes. See also ## `SomeOrdinal`. else: - # bootstrap <= 0.20.0 + # bootstrap < 1.2.0 type OrdinalImpl[T] {.magic: Ordinal.} Ordinal* = OrdinalImpl | uint | uint64 From 56bd5cbb877b69f5c4a49470845621267a316f91 Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 28 Sep 2021 15:05:22 +0800 Subject: [PATCH 2545/3768] fix a typo (canonilization => canonicalization) (#18914) --- compiler/patterns.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/patterns.nim b/compiler/patterns.nim index 1ef903161..4b39de3ba 100644 --- a/compiler/patterns.nim +++ b/compiler/patterns.nim @@ -36,7 +36,7 @@ proc putLazy(c: PPatternContext, sym: PSym, n: PNode) = proc matches(c: PPatternContext, p, n: PNode): bool proc canonKind(n: PNode): TNodeKind = - ## nodekind canonilization for pattern matching + ## nodekind canonicalization for pattern matching result = n.kind case result of nkCallKinds: result = nkCall From 08cf4cb1e58fad35be3846e98a308500ac2cfdb0 Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 28 Sep 2021 17:35:04 +0800 Subject: [PATCH 2546/3768] test ord (#18909) --- compiler/semmagic.nim | 4 ---- 1 file changed, 4 deletions(-) diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 87f7da815..874098294 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -216,10 +216,6 @@ proc semOrd(c: PContext, n: PNode): PNode = let parType = n[1].typ if isOrdinalType(parType, allowEnumWithHoles=true): discard - elif parType.kind == tySet: - let a = toInt64(firstOrd(c.config, parType)) - let b = toInt64(lastOrd(c.config, parType)) - result.typ = makeRangeType(c, a, b, n.info) else: localError(c.config, n.info, errOrdinalTypeExpected) result.typ = errorType(c) From f061971a9be5222f0f158f605220f3bda42f7488 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Wed, 29 Sep 2021 00:32:39 -0700 Subject: [PATCH 2547/3768] envPairs works in vm, nims (#18615) * envPairs works in vm, nims * fixup --- compiler/vmops.nim | 14 +- lib/pure/includes/osenv.nim | 349 ++++++++++++++-------------- lib/pure/os.nim | 3 +- testament/lib/stdtest/testutils.nim | 7 +- tests/js/tos.nim | 28 +-- tests/stdlib/tosenv.nim | 18 +- tests/test_nimscript.nims | 4 + 7 files changed, 213 insertions(+), 210 deletions(-) diff --git a/compiler/vmops.nim b/compiler/vmops.nim index e287dd41d..b9801234d 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -13,7 +13,7 @@ from std/math import sqrt, ln, log10, log2, exp, round, arccos, arcsin, arctan, arctan2, cos, cosh, hypot, sinh, sin, tan, tanh, pow, trunc, floor, ceil, `mod`, cbrt, arcsinh, arccosh, arctanh, erf, erfc, gamma, lgamma - +from std/sequtils import toSeq when declared(math.copySign): # pending bug #18762, avoid renaming math from std/math as math2 import copySign @@ -22,8 +22,8 @@ when declared(math.signbit): # ditto from std/math as math3 import signbit -from std/os import getEnv, existsEnv, delEnv, putEnv, dirExists, fileExists, walkDir, - getAppFilename, raiseOSError, osLastError +from std/os import getEnv, existsEnv, delEnv, putEnv, envPairs, + dirExists, fileExists, walkDir, getAppFilename, raiseOSError, osLastError from std/md5 import getMD5 from std/times import cpuTime @@ -156,6 +156,12 @@ proc stackTrace2(c: PCtx, msg: string, n: PNode) = stackTrace(c, PStackFrame(prc: c.prc.sym, comesFrom: 0, next: nil), c.exceptionInstr, msg, n.info) proc registerAdditionalOps*(c: PCtx) = + + template wrapIterator(fqname: string, iter: untyped) = + registerCallback c, fqname, proc(a: VmArgs) = + setResult(a, toLit(toSeq(iter))) + + proc gorgeExWrapper(a: VmArgs) = let ret = opGorge(getString(a, 0), getString(a, 1), getString(a, 2), a.currentLineInfo, c.config) @@ -341,3 +347,5 @@ proc registerAdditionalOps*(c: PCtx) = let p = a.getVar(0) let x = a.getFloat(1) addFloatSprintf(p.strVal, x) + + wrapIterator("stdlib.os.envPairsImplSeq"): envPairs() diff --git a/lib/pure/includes/osenv.nim b/lib/pure/includes/osenv.nim index 6aaafbfda..00a82327c 100644 --- a/lib/pure/includes/osenv.nim +++ b/lib/pure/includes/osenv.nim @@ -3,189 +3,200 @@ when not declared(os) and not declared(ospaths): {.error: "This is an include file for os.nim!".} -when defined(nodejs): - proc getEnv*(key: string, default = ""): string {.tags: [ReadEnvEffect].} = - var ret = default.cstring - let key2 = key.cstring - {.emit: "const value = process.env[`key2`];".} - {.emit: "if (value !== undefined) { `ret` = value };".} - result = $ret +when not defined(nimscript): + when defined(nodejs): + proc getEnv*(key: string, default = ""): string {.tags: [ReadEnvEffect].} = + var ret = default.cstring + let key2 = key.cstring + {.emit: "const value = process.env[`key2`];".} + {.emit: "if (value !== undefined) { `ret` = value };".} + result = $ret - proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} = - var key2 = key.cstring - var ret: bool - {.emit: "`ret` = `key2` in process.env;".} - result = ret + proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} = + var key2 = key.cstring + var ret: bool + {.emit: "`ret` = `key2` in process.env;".} + result = ret - proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} = - var key2 = key.cstring - var val2 = val.cstring - {.emit: "process.env[`key2`] = `val2`;".} + proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} = + var key2 = key.cstring + var val2 = val.cstring + {.emit: "process.env[`key2`] = `val2`;".} - proc delEnv*(key: string) {.tags: [WriteEnvEffect].} = - var key2 = key.cstring - {.emit: "delete process.env[`key2`];".} + proc delEnv*(key: string) {.tags: [WriteEnvEffect].} = + var key2 = key.cstring + {.emit: "delete process.env[`key2`];".} - iterator envPairs*(): tuple[key, value: string] {.tags: [ReadEnvEffect].} = - var num: int - var keys: RootObj - {.emit: "`keys` = Object.keys(process.env); `num` = `keys`.length;".} - for i in 0..".} - when defined(windows): - proc c_putenv_s(envname: cstring, envval: cstring): cint {.importc: "_putenv_s", header: "".} - from std/private/win_setenv import setEnvImpl else: - proc c_setenv(envname: cstring, envval: cstring, overwrite: cint): cint {.importc: "setenv", header: "".} - proc c_unsetenv(env: cstring): cint {.importc: "unsetenv", header: "".} - proc getEnv*(key: string, default = ""): string {.tags: [ReadEnvEffect].} = - ## Returns the value of the `environment variable`:idx: named `key`. - ## - ## If the variable does not exist, `""` is returned. To distinguish - ## whether a variable exists or it's value is just `""`, call - ## `existsEnv(key) proc <#existsEnv,string>`_. - ## - ## See also: - ## * `existsEnv proc <#existsEnv,string>`_ - ## * `putEnv proc <#putEnv,string,string>`_ - ## * `delEnv proc <#delEnv,string>`_ - ## * `envPairs iterator <#envPairs.i>`_ - runnableExamples: - assert getEnv("unknownEnv") == "" - assert getEnv("unknownEnv", "doesn't exist") == "doesn't exist" - - let env = c_getenv(key) - if env == nil: return default - result = $env - - proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} = - ## Checks whether the environment variable named `key` exists. - ## Returns true if it exists, false otherwise. - ## - ## See also: - ## * `getEnv proc <#getEnv,string,string>`_ - ## * `putEnv proc <#putEnv,string,string>`_ - ## * `delEnv proc <#delEnv,string>`_ - ## * `envPairs iterator <#envPairs.i>`_ - runnableExamples: - assert not existsEnv("unknownEnv") - - return c_getenv(key) != nil - - proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} = - ## Sets the value of the `environment variable`:idx: named `key` to `val`. - ## If an error occurs, `OSError` is raised. - ## - ## See also: - ## * `getEnv proc <#getEnv,string,string>`_ - ## * `existsEnv proc <#existsEnv,string>`_ - ## * `delEnv proc <#delEnv,string>`_ - ## * `envPairs iterator <#envPairs.i>`_ + proc c_getenv(env: cstring): cstring {. + importc: "getenv", header: "".} when defined(windows): - if key.len == 0 or '=' in key: - raise newException(OSError, "invalid key, got: " & $(key, val)) - if setEnvImpl(key, val, 1'i32) != 0'i32: - raiseOSError(osLastError(), $(key, val)) + proc c_putenv_s(envname: cstring, envval: cstring): cint {.importc: "_putenv_s", header: "".} + from std/private/win_setenv import setEnvImpl else: - if c_setenv(key, val, 1'i32) != 0'i32: - raiseOSError(osLastError(), $(key, val)) + proc c_setenv(envname: cstring, envval: cstring, overwrite: cint): cint {.importc: "setenv", header: "".} + proc c_unsetenv(env: cstring): cint {.importc: "unsetenv", header: "".} - proc delEnv*(key: string) {.tags: [WriteEnvEffect].} = - ## Deletes the `environment variable`:idx: named `key`. - ## If an error occurs, `OSError` is raised. - ## - ## See also:ven - ## * `getEnv proc <#getEnv,string,string>`_ - ## * `existsEnv proc <#existsEnv,string>`_ - ## * `putEnv proc <#putEnv,string,string>`_ - ## * `envPairs iterator <#envPairs.i>`_ - template bail = raiseOSError(osLastError(), key) - when defined(windows): - #[ - # https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/putenv-s-wputenv-s?view=msvc-160 - > You can remove a variable from the environment by specifying an empty string (that is, "") for value_string - note that nil is not legal - ]# - if key.len == 0 or '=' in key: - raise newException(OSError, "invalid key, got: " & key) - if c_putenv_s(key, "") != 0'i32: bail - else: - if c_unsetenv(key) != 0'i32: bail + proc getEnv*(key: string, default = ""): string {.tags: [ReadEnvEffect].} = + ## Returns the value of the `environment variable`:idx: named `key`. + ## + ## If the variable does not exist, `""` is returned. To distinguish + ## whether a variable exists or it's value is just `""`, call + ## `existsEnv(key) proc <#existsEnv,string>`_. + ## + ## See also: + ## * `existsEnv proc <#existsEnv,string>`_ + ## * `putEnv proc <#putEnv,string,string>`_ + ## * `delEnv proc <#delEnv,string>`_ + ## * `envPairs iterator <#envPairs.i>`_ + runnableExamples: + assert getEnv("unknownEnv") == "" + assert getEnv("unknownEnv", "doesn't exist") == "doesn't exist" - when defined(windows): - when useWinUnicode: - when defined(cpp): - proc strEnd(cstr: WideCString, c = 0'i32): WideCString {.importcpp: "(NI16*)wcschr((const wchar_t *)#, #)", - header: "".} + let env = c_getenv(key) + if env == nil: return default + result = $env + + proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} = + ## Checks whether the environment variable named `key` exists. + ## Returns true if it exists, false otherwise. + ## + ## See also: + ## * `getEnv proc <#getEnv,string,string>`_ + ## * `putEnv proc <#putEnv,string,string>`_ + ## * `delEnv proc <#delEnv,string>`_ + ## * `envPairs iterator <#envPairs.i>`_ + runnableExamples: + assert not existsEnv("unknownEnv") + + return c_getenv(key) != nil + + proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} = + ## Sets the value of the `environment variable`:idx: named `key` to `val`. + ## If an error occurs, `OSError` is raised. + ## + ## See also: + ## * `getEnv proc <#getEnv,string,string>`_ + ## * `existsEnv proc <#existsEnv,string>`_ + ## * `delEnv proc <#delEnv,string>`_ + ## * `envPairs iterator <#envPairs.i>`_ + when defined(windows): + if key.len == 0 or '=' in key: + raise newException(OSError, "invalid key, got: " & $(key, val)) + if setEnvImpl(key, val, 1'i32) != 0'i32: + raiseOSError(osLastError(), $(key, val)) else: - proc strEnd(cstr: WideCString, c = 0'i32): WideCString {.importc: "wcschr", - header: "".} - else: - proc strEnd(cstr: cstring, c = 0'i32): cstring {.importc: "strchr", - header: "".} - elif defined(macosx) and not defined(ios) and not defined(emscripten): - # From the manual: - # Shared libraries and bundles don't have direct access to environ, - # which is only available to the loader ld(1) when a complete program - # is being linked. - # The environment routines can still be used, but if direct access to - # environ is needed, the _NSGetEnviron() routine, defined in - # , can be used to retrieve the address of environ - # at runtime. - proc NSGetEnviron(): ptr cstringArray {.importc: "_NSGetEnviron", - header: "".} - elif defined(haiku): - var gEnv {.importc: "environ", header: "".}: cstringArray - else: - var gEnv {.importc: "environ".}: cstringArray + if c_setenv(key, val, 1'i32) != 0'i32: + raiseOSError(osLastError(), $(key, val)) + + proc delEnv*(key: string) {.tags: [WriteEnvEffect].} = + ## Deletes the `environment variable`:idx: named `key`. + ## If an error occurs, `OSError` is raised. + ## + ## See also:ven + ## * `getEnv proc <#getEnv,string,string>`_ + ## * `existsEnv proc <#existsEnv,string>`_ + ## * `putEnv proc <#putEnv,string,string>`_ + ## * `envPairs iterator <#envPairs.i>`_ + template bail = raiseOSError(osLastError(), key) + when defined(windows): + #[ + # https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/putenv-s-wputenv-s?view=msvc-160 + > You can remove a variable from the environment by specifying an empty string (that is, "") for value_string + note that nil is not legal + ]# + if key.len == 0 or '=' in key: + raise newException(OSError, "invalid key, got: " & key) + if c_putenv_s(key, "") != 0'i32: bail + else: + if c_unsetenv(key) != 0'i32: bail - iterator envPairs*(): tuple[key, value: string] {.tags: [ReadEnvEffect].} = - ## Iterate over all `environments variables`:idx:. - ## - ## In the first component of the tuple is the name of the current variable stored, - ## in the second its value. - ## - ## See also: - ## * `getEnv proc <#getEnv,string,string>`_ - ## * `existsEnv proc <#existsEnv,string>`_ - ## * `putEnv proc <#putEnv,string,string>`_ - ## * `delEnv proc <#delEnv,string>`_ when defined(windows): - block: - template impl(get_fun, typ, size, zero, free_fun) = - let env = get_fun() - var e = env - if e == nil: break - while true: - let eend = strEnd(e) - let kv = $e - let p = find(kv, '=') - yield (substr(kv, 0, p-1), substr(kv, p+1)) - e = cast[typ](cast[ByteAddress](eend)+size) - if typeof(zero)(eend[1]) == zero: break - discard free_fun(env) - when useWinUnicode: - impl(getEnvironmentStringsW, WideCString, 2, 0, freeEnvironmentStringsW) + when useWinUnicode: + when defined(cpp): + proc strEnd(cstr: WideCString, c = 0'i32): WideCString {.importcpp: "(NI16*)wcschr((const wchar_t *)#, #)", + header: "".} else: - impl(getEnvironmentStringsA, cstring, 1, '\0', freeEnvironmentStringsA) + proc strEnd(cstr: WideCString, c = 0'i32): WideCString {.importc: "wcschr", + header: "".} + else: + proc strEnd(cstr: cstring, c = 0'i32): cstring {.importc: "strchr", + header: "".} + elif defined(macosx) and not defined(ios) and not defined(emscripten): + # From the manual: + # Shared libraries and bundles don't have direct access to environ, + # which is only available to the loader ld(1) when a complete program + # is being linked. + # The environment routines can still be used, but if direct access to + # environ is needed, the _NSGetEnviron() routine, defined in + # , can be used to retrieve the address of environ + # at runtime. + proc NSGetEnviron(): ptr cstringArray {.importc: "_NSGetEnviron", + header: "".} + elif defined(haiku): + var gEnv {.importc: "environ", header: "".}: cstringArray else: - var i = 0 - when defined(macosx) and not defined(ios) and not defined(emscripten): - var gEnv = NSGetEnviron()[] - while gEnv[i] != nil: - let kv = $gEnv[i] - inc(i) - let p = find(kv, '=') - yield (substr(kv, 0, p-1), substr(kv, p+1)) + var gEnv {.importc: "environ".}: cstringArray + + iterator envPairsImpl(): tuple[key, value: string] {.tags: [ReadEnvEffect].} = + when defined(windows): + block: + template impl(get_fun, typ, size, zero, free_fun) = + let env = get_fun() + var e = env + if e == nil: break + while true: + let eend = strEnd(e) + let kv = $e + let p = find(kv, '=') + yield (substr(kv, 0, p-1), substr(kv, p+1)) + e = cast[typ](cast[ByteAddress](eend)+size) + if typeof(zero)(eend[1]) == zero: break + discard free_fun(env) + when useWinUnicode: + impl(getEnvironmentStringsW, WideCString, 2, 0, freeEnvironmentStringsW) + else: + impl(getEnvironmentStringsA, cstring, 1, '\0', freeEnvironmentStringsA) + else: + var i = 0 + when defined(macosx) and not defined(ios) and not defined(emscripten): + var gEnv = NSGetEnviron()[] + while gEnv[i] != nil: + let kv = $gEnv[i] + inc(i) + let p = find(kv, '=') + yield (substr(kv, 0, p-1), substr(kv, p+1)) + +proc envPairsImplSeq(): seq[tuple[key, value: string]] = discard # vmops + +iterator envPairs*(): tuple[key, value: string] {.tags: [ReadEnvEffect].} = + ## Iterate over all `environments variables`:idx:. + ## + ## In the first component of the tuple is the name of the current variable stored, + ## in the second its value. + ## + ## Works in native backends, nodejs and vm, like the following APIs: + ## * `getEnv proc <#getEnv,string,string>`_ + ## * `existsEnv proc <#existsEnv,string>`_ + ## * `putEnv proc <#putEnv,string,string>`_ + ## * `delEnv proc <#delEnv,string>`_ + when nimvm: + for ai in envPairsImplSeq(): yield ai + else: + when defined(nimscript): discard + else: + for ai in envPairsImpl(): yield ai diff --git a/lib/pure/os.nim b/lib/pure/os.nim index dd1f9f0b6..e3b8e9f1c 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -871,8 +871,7 @@ proc unixToNativePath*(path: string, drive=""): string {. inc(i) include "includes/oserr" -when not defined(nimscript): - include "includes/osenv" +include "includes/osenv" proc getHomeDir*(): string {.rtl, extern: "nos$1", tags: [ReadEnvEffect, ReadIOEffect].} = diff --git a/testament/lib/stdtest/testutils.nim b/testament/lib/stdtest/testutils.nim index a5476b8d7..12e6a56ab 100644 --- a/testament/lib/stdtest/testutils.nim +++ b/testament/lib/stdtest/testutils.nim @@ -1,5 +1,8 @@ import std/private/miscdollars -from std/os import getEnv +when defined(nimscript): + import std/os # xxx investigate why needed +else: + from std/os import getEnv import std/[macros, genasts] template flakyAssert*(cond: untyped, msg = "", notifySuccess = true) = @@ -26,7 +29,7 @@ template flakyAssert*(cond: untyped, msg = "", notifySuccess = true) = msg2.add $expr & " " & msg echo msg2 -when not defined(js): +when not defined(js) and not defined(nimscript): import std/strutils proc greedyOrderedSubsetLines*(lhs, rhs: string, allowPrefixMatch = false): bool = diff --git a/tests/js/tos.nim b/tests/js/tos.nim index 07eb3aaa3..bfe3cd9b4 100644 --- a/tests/js/tos.nim +++ b/tests/js/tos.nim @@ -1,3 +1,5 @@ +# xxx consider merging this in tests/stdlib/tos.nim for increased coverage (with selecting disabling) + static: doAssert defined(nodejs) import os @@ -19,29 +21,3 @@ block: if not isWindows: doAssert cwd.isAbsolute doAssert relativePath(getCurrentDir() / "foo", "bar") == "../foo" - -import std/sequtils - -template main = - putEnv("foo", "bar") - doAssert getEnv("foo") == "bar" - doAssert existsEnv("foo") - - putEnv("foo", "") - doAssert existsEnv("foo") - putEnv("foo", "bar2") - doAssert getEnv("foo") == "bar2" - - when nimvm: - discard - else: - # need support in vmops: envPairs, delEnv - let s = toSeq(envPairs()) - doAssert ("foo", "bar2") in s - doAssert ("foo", "bar") notin s - - delEnv("foo") - doAssert not existsEnv("foo") - -static: main() -main() diff --git a/tests/stdlib/tosenv.nim b/tests/stdlib/tosenv.nim index df5759d0c..0a50031a1 100644 --- a/tests/stdlib/tosenv.nim +++ b/tests/stdlib/tosenv.nim @@ -13,16 +13,18 @@ template main = for val in ["val", ""]: # ensures empty val works too const key = "NIM_TESTS_TOSENV_KEY" doAssert not existsEnv(key) - putEnv(key, val) + + putEnv(key, "tempval") + doAssert existsEnv(key) + doAssert getEnv(key) == "tempval" + + putEnv(key, val) # change a key that already exists doAssert existsEnv(key) doAssert getEnv(key) == val - when nimvm: discard - else: - doAssert (key, val) in toSeq(envPairs()) + + doAssert (key, val) in toSeq(envPairs()) delEnv(key) - when nimvm: discard - else: - doAssert (key, val) notin toSeq(envPairs()) + doAssert (key, val) notin toSeq(envPairs()) doAssert not existsEnv(key) delEnv(key) # deleting an already deleted env var doAssert not existsEnv(key) @@ -43,7 +45,7 @@ template main = static: main() main() -when not defined(js): +when not defined(js) and not defined(nimscript): block: # bug #18533 proc c_getenv(env: cstring): cstring {.importc: "getenv", header: "".} var thr: Thread[void] diff --git a/tests/test_nimscript.nims b/tests/test_nimscript.nims index 90b6181aa..ecb409677 100644 --- a/tests/test_nimscript.nims +++ b/tests/test_nimscript.nims @@ -71,7 +71,11 @@ import std/[ decls, compilesettings, with, wrapnils ] +# non-std imports +import stdtest/testutils +# tests (increase coverage via code reuse) import stdlib/trandom +import stdlib/tosenv echo "Nimscript imports are successful." From b74b914e4f36199797b77e98bd2d771b4056a0d6 Mon Sep 17 00:00:00 2001 From: Miran Date: Wed, 29 Sep 2021 11:14:47 +0200 Subject: [PATCH 2548/3768] [backport] use old style hints in .cfg files (#18917) refs #18085 --- compiler/nim.cfg | 4 ++-- compiler/nimfix/nimfix.nim.cfg | 2 +- config/nim.cfg | 4 ++-- drnim/nim.cfg | 2 +- nimsuggest/nimsuggest.nim.cfg | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/compiler/nim.cfg b/compiler/nim.cfg index d6ac6a937..4a0287cb5 100644 --- a/compiler/nim.cfg +++ b/compiler/nim.cfg @@ -1,6 +1,6 @@ # Special configuration file for the Nim project -hint:XDeclaredButNotUsed:off +hint[XDeclaredButNotUsed]:off define:booting define:nimcore @@ -23,7 +23,7 @@ define:useStdoutAsStdmsg #gc:markAndSweep @if nimHasWarningObservableStores: - warning:ObservableStores: off + warning[ObservableStores]: off @end @if nimHasEffectsOf: diff --git a/compiler/nimfix/nimfix.nim.cfg b/compiler/nimfix/nimfix.nim.cfg index 27ebf10b9..0d9dbfa4b 100644 --- a/compiler/nimfix/nimfix.nim.cfg +++ b/compiler/nimfix/nimfix.nim.cfg @@ -1,7 +1,7 @@ # Special configuration file for the Nim project # gc:markAndSweep -hint:XDeclaredButNotUsed:off +hint[XDeclaredButNotUsed]:off path:"$projectPath/.." path:"$lib/packages/docutils" diff --git a/config/nim.cfg b/config/nim.cfg index c0a844e03..3b964d124 100644 --- a/config/nim.cfg +++ b/config/nim.cfg @@ -13,8 +13,8 @@ cc = gcc # additional options always passed to the compiler: --parallel_build: "0" # 0 to auto-detect number of processors -hint:LineTooLong:off -#hint:XDeclaredButNotUsed:off +hint[LineTooLong]=off +#hint[XDeclaredButNotUsed]=off # Examples of how to setup a cross-compiler: diff --git a/drnim/nim.cfg b/drnim/nim.cfg index 57dca974c..58c1725d9 100644 --- a/drnim/nim.cfg +++ b/drnim/nim.cfg @@ -1,6 +1,6 @@ # Special configuration file for the Nim project -hint:XDeclaredButNotUsed:off +hint[XDeclaredButNotUsed]:off define:booting define:nimcore diff --git a/nimsuggest/nimsuggest.nim.cfg b/nimsuggest/nimsuggest.nim.cfg index 6e5ee8c71..394449740 100644 --- a/nimsuggest/nimsuggest.nim.cfg +++ b/nimsuggest/nimsuggest.nim.cfg @@ -2,7 +2,7 @@ gc:markAndSweep -hint:XDeclaredButNotUsed:off +hint[XDeclaredButNotUsed]:off path:"$lib/packages/docutils" From f35e9a5752bb4cd4e30105cd27b6bc56cbf22329 Mon Sep 17 00:00:00 2001 From: Jonas Schubert Erlandsson Date: Wed, 29 Sep 2021 19:14:31 +0300 Subject: [PATCH 2549/3768] Adds source to single tags list (#18920) --- lib/pure/htmlparser.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/htmlparser.nim b/lib/pure/htmlparser.nim index c572146a7..21dff69ff 100644 --- a/lib/pure/htmlparser.nim +++ b/lib/pure/htmlparser.nim @@ -215,7 +215,7 @@ const tagMenu, tagNoframes} SingleTags* = {tagArea, tagBase, tagBasefont, tagBr, tagCol, tagFrame, tagHr, tagImg, tagIsindex, - tagLink, tagMeta, tagParam, tagWbr} + tagLink, tagMeta, tagParam, tagWbr, tagSource} proc allLower(s: string): bool = for c in s: From 6bb32da4aecbe80a9cc0cce90322c0e741ee8b85 Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 30 Sep 2021 13:46:30 +0800 Subject: [PATCH 2550/3768] [std/tasks] more gcsafe stuffs [backport] (#18926) --- lib/std/tasks.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/std/tasks.nim b/lib/std/tasks.nim index 97c4df3f7..e2ea5377f 100644 --- a/lib/std/tasks.nim +++ b/lib/std/tasks.nim @@ -61,12 +61,12 @@ type Task* = object ## `Task` contains the callback and its arguments. callback: proc (args: pointer) {.nimcall, gcsafe.} args: pointer - destroy: proc (args: pointer) {.nimcall.} + destroy: proc (args: pointer) {.nimcall, gcsafe.} proc `=copy`*(x: var Task, y: Task) {.error.} -proc `=destroy`*(t: var Task) {.inline.} = +proc `=destroy`*(t: var Task) {.inline, gcsafe.} = ## Frees the resources allocated for a `Task`. if t.args != nil: if t.destroy != nil: @@ -219,7 +219,7 @@ macro toTask*(e: typed{nkCall | nkInfix | nkPrefix | nkPostfix | nkCommand | nkC let `objTemp` = cast[ptr `scratchObjType`](args) `functionStmtList` - proc `destroyName`(args: pointer) {.nimcall.} = + proc `destroyName`(args: pointer) {.gcsafe, nimcall.} = let `objTemp2` = cast[ptr `scratchObjType`](args) `tempNode` From c3dd53bb2cf492029ae256f64f64d2577621b995 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 30 Sep 2021 11:35:42 +0200 Subject: [PATCH 2551/3768] typos [backport] (#18929) --- doc/destructors.rst | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/doc/destructors.rst b/doc/destructors.rst index 28d8fa5f9..195dc8f97 100644 --- a/doc/destructors.rst +++ b/doc/destructors.rst @@ -145,7 +145,7 @@ not free the resources afterward by setting the object to its default value default value is written as `wasMoved(x)`. When not provided the compiler is using a combination of `=destroy` and `copyMem` instead. This is efficient hence users rarely need to implement their own `=sink` operator, it is enough to -provide `=destroy` and `=copy`, compiler will take care of the rest. +provide `=destroy` and `=copy`, the compiler will take care of the rest. The prototype of this hook for a type `T` needs to be: @@ -169,7 +169,7 @@ How self-assignments are handled is explained later in this document. `=copy` hook ---------------- +------------ The ordinary assignment in Nim conceptually copies the values. The `=copy` hook is called for assignments that couldn't be transformed into `=sink` @@ -206,11 +206,11 @@ by the compiler. Notice that there is no `=` before the `{.error.}` pragma. `=trace` hook ---------------- +------------- A custom **container** type can support Nim's cycle collector `--gc:orc` via the `=trace` hook. If the container does not implement `=trace`, cyclic data -structure which are constructed with the help of the container might leak +structures which are constructed with the help of the container might leak memory or resources, but memory safety is not compromised. The prototype of this hook for a type `T` needs to be: @@ -225,10 +225,10 @@ to calls of the built-in `=trace` operation. Usually there will only be a need for a custom `=trace` when a custom `=destroy` that deallocates manually allocated resources is also used, and then only when there is a chance of cyclic references from items within the manually allocated resources when it is desired that `--gc:orc` -be able to break and collect these cyclic referenced resources. Currently however, there is a +is able to break and collect these cyclic referenced resources. Currently however, there is a mutual use problem in that whichever of `=destroy`/`=trace` is used first will automatically create a version of the other which will then conflict with the creation of the second of the -pair. The work around for this problem is to forward declare the second of the "hooks" to +pair. The workaround for this problem is to forward declare the second of the "hooks" to prevent the automatic creation. The general pattern in using `=destroy` with `=trace` looks like: @@ -250,8 +250,9 @@ The general pattern in using `=destroy` with `=trace` looks like: dest.arr.dealloc proc `=trace`[T](dest: var Test[T]; env: pointer) = - if dest.arr != nil: # trace the `T`'s which may be cyclic - for i in 0 ..< dest.size: dest.arr[i].`=trace` env + if dest.arr != nil: + # trace the `T`'s which may be cyclic + for i in 0 ..< dest.size: `=trace`(dest.arr[i], env) # following may be other custom "hooks" as required... @@ -265,7 +266,7 @@ Move semantics A "move" can be regarded as an optimized copy operation. If the source of the copy operation is not used afterward, the copy can be replaced by a move. This document uses the notation `lastReadOf(x)` to describe that `x` is not -used afterwards. This property is computed by a static control flow analysis +used afterward. This property is computed by a static control flow analysis but can also be enforced by using `system.move` explicitly. @@ -601,7 +602,7 @@ a form of copy elision. To see how and when we can do that, think about this question: In `dest = src` when do we really have to *materialize* the full copy? - Only if `dest` or `src` are mutated -afterwards. If `dest` is a local variable that is simple to analyze. And if `src` is a +afterward. If `dest` is a local variable that is simple to analyze. And if `src` is a location derived from a formal parameter, we also know it is not mutated! In other words, we do a compile-time copy-on-write analysis. From c38ab3e257fbb08622f9ec6f0068a0fd3a5d82d7 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 30 Sep 2021 12:09:42 +0200 Subject: [PATCH 2552/3768] fixes #18921 [backport] (#18930) --- compiler/commands.nim | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/compiler/commands.nim b/compiler/commands.nim index 9b7e35791..a8caad916 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -482,18 +482,20 @@ proc setCommandEarly*(conf: ConfigRef, command: string) = else: conf.foreignPackageNotes = foreignPackageNotesDefault -proc specialDefine(conf: ConfigRef, key: string) = +proc specialDefine(conf: ConfigRef, key: string; pass: TCmdLinePass) = # Keep this syncronized with the default config/nim.cfg! if cmpIgnoreStyle(key, "nimQuirky") == 0: conf.exc = excQuirky elif cmpIgnoreStyle(key, "release") == 0 or cmpIgnoreStyle(key, "danger") == 0: - conf.options.excl {optStackTrace, optLineTrace, optLineDir, optOptimizeSize} - conf.globalOptions.excl {optExcessiveStackTrace, optCDebug} - conf.options.incl optOptimizeSpeed + if pass in {passCmd1, passPP}: + conf.options.excl {optStackTrace, optLineTrace, optLineDir, optOptimizeSize} + conf.globalOptions.excl {optExcessiveStackTrace, optCDebug} + conf.options.incl optOptimizeSpeed if cmpIgnoreStyle(key, "danger") == 0 or cmpIgnoreStyle(key, "quick") == 0: - conf.options.excl {optObjCheck, optFieldCheck, optRangeCheck, optBoundsCheck, - optOverflowCheck, optAssert, optStackTrace, optLineTrace, optLineDir} - conf.globalOptions.excl {optCDebug} + if pass in {passCmd1, passPP}: + conf.options.excl {optObjCheck, optFieldCheck, optRangeCheck, optBoundsCheck, + optOverflowCheck, optAssert, optStackTrace, optLineTrace, optLineDir} + conf.globalOptions.excl {optCDebug} proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; conf: ConfigRef) = @@ -565,10 +567,10 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; expectArg(conf, switch, arg, pass, info) if {':', '='} in arg: splitSwitch(conf, arg, key, val, pass, info) - specialDefine(conf, key) + specialDefine(conf, key, pass) defineSymbol(conf.symbols, key, val) else: - specialDefine(conf, arg) + specialDefine(conf, arg, pass) defineSymbol(conf.symbols, arg) of "undef", "u": expectArg(conf, switch, arg, pass, info) From f915b3aa86929d87e162b6ddd7589e6337a30397 Mon Sep 17 00:00:00 2001 From: Jason Beetham Date: Thu, 30 Sep 2021 08:55:43 -0600 Subject: [PATCH 2553/3768] Alternative to #18928 (#18931) * fixed #18841 * Added test --- compiler/semtypinst.nim | 12 +++---- tests/generics/trecursivegenerics.nim | 52 ++++++++++++++++++++++++++- 2 files changed, 57 insertions(+), 7 deletions(-) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 2ca355e7d..0bb53e04d 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -344,11 +344,7 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = x = lookupTypeVar(cl, x) if x != nil: if header == t: header = instCopyType(cl, t) - header[i] = - if x.kind == tyGenericInst: - t[i] - else: - x + header[i] = x propagateToOwner(header, x) else: propagateToOwner(header, x) @@ -382,7 +378,11 @@ proc handleGenericInvocation(cl: var TReplTypeVars, t: PType): PType = cl.typeMap = newTypeMapLayer(cl) for i in 1.. Date: Thu, 30 Sep 2021 22:57:06 +0800 Subject: [PATCH 2554/3768] alternative to #18918 (#18927) * fix #16558 * add testcase --- compiler/semexprs.nim | 2 ++ tests/arc/t16558.nim | 9 +++++++++ 2 files changed, 11 insertions(+) create mode 100644 tests/arc/t16558.nim diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 9134c1301..342b8b05f 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -217,6 +217,8 @@ proc isCastable(c: PContext; dst, src: PType): bool = let s = skipTypes(src, abstractInst) if d.kind == tyRef and s.kind == tyRef and s[0].isFinal != d[0].isFinal: return false + elif d.kind in IntegralTypes and s.kind in {tyString, tySequence}: + return false var dstSize, srcSize: BiggestInt dstSize = computeSize(conf, dst) diff --git a/tests/arc/t16558.nim b/tests/arc/t16558.nim new file mode 100644 index 000000000..7b6eb4669 --- /dev/null +++ b/tests/arc/t16558.nim @@ -0,0 +1,9 @@ +discard """ + matrix: "--gc:arc" + errormsg: "expression cannot be cast to int" +""" + +block: # bug #16558 + var value = "hi there" + var keepInt: int + keepInt = cast[int](value) From 4b764f4c7cff02b1f698d90d4605ae8fa85c3ee6 Mon Sep 17 00:00:00 2001 From: flywind Date: Fri, 1 Oct 2021 16:19:02 +0800 Subject: [PATCH 2555/3768] close #17319; add testcase (#18934) --- tests/arc/tarcmisc.nim | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/arc/tarcmisc.nim b/tests/arc/tarcmisc.nim index 7daea62c8..d259e1197 100644 --- a/tests/arc/tarcmisc.nim +++ b/tests/arc/tarcmisc.nim @@ -463,3 +463,22 @@ proc putValue[T](n: T) = echo b.n useForward() + + +# bug #17319 +type + BrokenObject = ref object + brokenType: seq[int] + +proc use(obj: BrokenObject) = + discard + +method testMethod(self: BrokenObject) {.base.} = + iterator testMethodIter() {.closure.} = + use(self) + + var nameIterVar = testMethodIter + nameIterVar() + +let mikasa = BrokenObject() +mikasa.testMethod() From 2aeac26f088c5170f7f0c4d8e73d099a32e8895a Mon Sep 17 00:00:00 2001 From: flywind Date: Fri, 1 Oct 2021 19:57:06 +0800 Subject: [PATCH 2556/3768] correct licence header (#18935) * rename licence * spaces --- lib/pure/lexbase.nim | 2 +- lib/std/compilesettings.nim | 2 +- lib/std/setutils.nim | 2 +- lib/std/sha1.nim | 2 +- lib/std/strbasics.nim | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/pure/lexbase.nim b/lib/pure/lexbase.nim index 166d8286b..bbc0a38ae 100644 --- a/lib/pure/lexbase.nim +++ b/lib/pure/lexbase.nim @@ -1,6 +1,6 @@ # # -# The Nim Compiler +# Nim's Runtime Library # (c) Copyright 2009 Andreas Rumpf # # See the file "copying.txt", included in this diff --git a/lib/std/compilesettings.nim b/lib/std/compilesettings.nim index e1ffc954a..5bb0bd673 100644 --- a/lib/std/compilesettings.nim +++ b/lib/std/compilesettings.nim @@ -1,6 +1,6 @@ # # -# The Nim Compiler +# Nim's Runtime Library # (c) Copyright 2020 Nim Contributors # # See the file "copying.txt", included in this diff --git a/lib/std/setutils.nim b/lib/std/setutils.nim index 84cc93623..4664d6dcc 100644 --- a/lib/std/setutils.nim +++ b/lib/std/setutils.nim @@ -1,6 +1,6 @@ # # -# The Nim Compiler +# Nim's Runtime Library # (c) Copyright 2020 Nim Contributors # # See the file "copying.txt", included in this diff --git a/lib/std/sha1.nim b/lib/std/sha1.nim index 3cc8a0249..120a81702 100644 --- a/lib/std/sha1.nim +++ b/lib/std/sha1.nim @@ -1,6 +1,6 @@ # # -# The Nim Compiler +# Nim's Runtime Library # (c) Copyright 2015 Nim Contributors # # See the file "copying.txt", included in this diff --git a/lib/std/strbasics.nim b/lib/std/strbasics.nim index 9232204ab..0d4c96f44 100644 --- a/lib/std/strbasics.nim +++ b/lib/std/strbasics.nim @@ -1,6 +1,6 @@ # # -# The Nim Compiler +# Nim's Runtime Library # (c) Copyright 2021 Nim Contributors # # See the file "copying.txt", included in this From 7577ea9e4ce4d9966edc1f1cc27bde1b2ec9f369 Mon Sep 17 00:00:00 2001 From: flywind Date: Sat, 2 Oct 2021 02:14:10 +0800 Subject: [PATCH 2557/3768] [std/tempfiles] docs improvement (#18936) * unify comments * more --- lib/std/tempfiles.nim | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/lib/std/tempfiles.nim b/lib/std/tempfiles.nim index 26786d463..ce84c2a37 100644 --- a/lib/std/tempfiles.nim +++ b/lib/std/tempfiles.nim @@ -122,23 +122,24 @@ proc getTempDirImpl(dir: string): string {.inline.} = proc genTempPath*(prefix, suffix: string, dir = ""): string = ## Generates a path name in `dir`. ## - ## If `dir` is empty, (`getTempDir `_) will be used. ## The path begins with `prefix` and ends with `suffix`. + ## + ## .. note:: `dir` must exist (empty `dir` will resolve to `getTempDir `_). let dir = getTempDirImpl(dir) result = dir / (prefix & randomPathName(nimTempPathLength) & suffix) proc createTempFile*(prefix, suffix: string, dir = ""): tuple[cfile: File, path: string] = ## Creates a new temporary file in the directory `dir`. - ## + ## ## This generates a path name using `genTempPath(prefix, suffix, dir)` and ## returns a file handle to an open file and the path of that file, possibly after ## retrying to ensure it doesn't already exist. - ## + ## ## If failing to create a temporary file, `OSError` will be raised. ## ## .. note:: It is the caller's responsibility to close `result.cfile` and ## remove `result.file` when no longer needed. - ## .. note:: `dir` must exist (empty `dir` will resolve to `getTempDir()`). + ## .. note:: `dir` must exist (empty `dir` will resolve to `getTempDir `_). runnableExamples: import std/os doAssertRaises(OSError): discard createTempFile("", "", "nonexistent") @@ -170,7 +171,7 @@ proc createTempDir*(prefix, suffix: string, dir = ""): string = ## If failing to create a temporary directory, `OSError` will be raised. ## ## .. note:: It is the caller's responsibility to remove the directory when no longer needed. - ## .. note:: `dir` must exist (empty `dir` will resolve to `getTempDir()`). + ## .. note:: `dir` must exist (empty `dir` will resolve to `getTempDir `_). runnableExamples: import std/os doAssertRaises(OSError): discard createTempDir("", "", "nonexistent") From 5c4692fad4c807a010d58d553e92360128699197 Mon Sep 17 00:00:00 2001 From: flywind Date: Sat, 2 Oct 2021 02:26:29 +0800 Subject: [PATCH 2558/3768] rename nimLegacyJsonutilsHoleyEnum [backport] (#18938) * enable nimPreviewJsonutilsHoleyEnum [backport] * docuement nimPreviewJsonutilsHoleyEnum --- changelog.md | 3 +-- lib/std/jsonutils.nim | 6 +++++- tests/config.nims | 4 +++- 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/changelog.md b/changelog.md index ab037c328..38ace19ed 100644 --- a/changelog.md +++ b/changelog.md @@ -77,8 +77,7 @@ initialization with a small (< 30000) seed. Use `-d:nimLegacyRandomInitRand` to restore previous behavior for a transition time, see PR #17467. -- `jsonutils` now serializes/deserializes holey enums as regular enums (via `ord`) instead of as strings. - Use `-d:nimLegacyJsonutilsHoleyEnum` for a transition period. `toJson` now serializes `JsonNode` +- With `-d:nimPreviewJsonutilsHoleyEnum`, `jsonutils` now can serialize/deserialize holey enums as regular enums (via `ord`) instead of as strings. It is expected that this behavior becomes the new default in upcoming versions. `toJson` now serializes `JsonNode` as is via reference (without a deep copy) instead of treating `JsonNode` as a regular ref object, this can be customized via `jsonNodeMode`. diff --git a/lib/std/jsonutils.nim b/lib/std/jsonutils.nim index 727a75288..19384b5d1 100644 --- a/lib/std/jsonutils.nim +++ b/lib/std/jsonutils.nim @@ -298,6 +298,10 @@ proc jsonTo*(b: JsonNode, T: typedesc, opt = Joptions()): T = proc toJson*[T](a: T, opt = initToJsonOptions()): JsonNode = ## serializes `a` to json; uses `toJsonHook(a: T)` if it's in scope to ## customize serialization, see strtabs.toJsonHook for an example. + ## + ## .. note:: With `-d:nimPreviewJsonutilsHoleyEnum`, `toJson` now can + ## serialize/deserialize holey enums as regular enums (via `ord`) instead of as strings. + ## It is expected that this behavior becomes the new default in upcoming versions. when compiles(toJsonHook(a)): result = toJsonHook(a) elif T is object | tuple: when T is object or isNamedTuple(T): @@ -328,7 +332,7 @@ proc toJson*[T](a: T, opt = initToJsonOptions()): JsonNode = elif T is enum: case opt.enumMode of joptEnumOrd: - when T is Ordinal or not defined(nimLegacyJsonutilsHoleyEnum): %(a.ord) + when T is Ordinal or defined(nimPreviewJsonutilsHoleyEnum): %(a.ord) else: toJson($a, opt) of joptEnumSymbol: when T is OrdinalEnum: diff --git a/tests/config.nims b/tests/config.nims index 86787db1d..ed2a1b7e9 100644 --- a/tests/config.nims +++ b/tests/config.nims @@ -30,10 +30,12 @@ hint("Processing", off) # switch("hint", "ConvFromXtoItselfNotNeeded") # switch("warningAsError", "InheritFromException") # would require fixing a few tests -# experimental API's are enabled in testament, refs https://github.com/timotheecour/Nim/issues/575 +# experimental APIs are enabled in testament, refs https://github.com/timotheecour/Nim/issues/575 # sync with `kochdocs.docDefines` or refactor. switch("define", "nimExperimentalAsyncjsThen") switch("define", "nimExperimentalLinenoiseExtra") +# preview APIs are expected to be the new default in upcoming versions switch("define", "nimPreviewFloatRoundtrip") switch("define", "nimPreviewDotLikeOps") +switch("define", "nimPreviewJsonutilsHoleyEnum") From e9268b52d9a34e7e851105d569bccced4b7261fd Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 3 Oct 2021 07:21:08 +0200 Subject: [PATCH 2559/3768] with this patch :idx: can be used for the index generation for LaTeX (#18946) --- config/nimdoc.tex.cfg | 6 ++++-- lib/packages/docutils/rstgen.nim | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/config/nimdoc.tex.cfg b/config/nimdoc.tex.cfg index 75382ce26..4aff9b379 100644 --- a/config/nimdoc.tex.cfg +++ b/config/nimdoc.tex.cfg @@ -3,7 +3,7 @@ # (c) 2012 Andreas Rumpf # Feel free to edit the templates as you need. -split.item.toc = "20" +split.item.toc = "20" # too long entries in the table of contents wrap around # after this number of characters @@ -97,12 +97,14 @@ doc.file = """ \usepackage{parskip} % paragraphs delimited by vertical space, no indent \usepackage{graphicx} +\newcommand{\nimindexterm}[2]{#2\label{#1}} + \usepackage{dingbat} % for \carriagereturn, etc \usepackage{fvextra} % for code blocks (works better than original fancyvrb) \fvset{ breaklines, breakafter={=}:|\_\{\}[](){,}.;+-*/'", - breaksymbolleft=\color{red}{\ensuremath{\hookrightarrow}}, + breaksymbolleft=\color{red}{\ensuremath{\hookrightarrow}}, breaksymbolright=\color{red}{\small\carriagereturn} } \fvinlineset{% diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 3734392c3..7d7eb4fb8 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -406,7 +406,7 @@ proc renderIndexTerm*(d: PDoc, n: PRstNode, result: var string) = var term = "" renderAux(d, n, term) setIndexTerm(d, changeFileExt(extractFilename(d.filename), HtmlExt), id, term, d.currentSection) - dispA(d.target, result, "$2", "$2\\label{$1}", + dispA(d.target, result, "$2", "\\nimindexterm{$2}{$1}", [id, term]) type From 45c4332c81521224a4db90fe9bf46efc0f93aaf7 Mon Sep 17 00:00:00 2001 From: flywind Date: Sun, 3 Oct 2021 13:22:53 +0800 Subject: [PATCH 2560/3768] document -d:nimStrictDelete [backport] (#18939) --- lib/system.nim | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/system.nim b/lib/system.nim index 358aae938..caa036f37 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2142,6 +2142,10 @@ proc delete*[T](x: var seq[T], i: Natural) {.noSideEffect, auditDelete.} = ## ## This is an `O(n)` operation. ## + ## .. note:: With `-d:nimStrictDelete`, an index error is produced when the index passed + ## to it was out of bounds. `-d:nimStrictDelete` will become the default + ## in upcoming versions. + ## ## See also: ## * `del <#del,seq[T],Natural>`_ for O(1) operation ## From 6b0db5be1f21cec1b75822c021ccee79035a6f74 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sun, 3 Oct 2021 06:25:15 +0100 Subject: [PATCH 2561/3768] Fixes build_all.sh failing on Nimble builds (#18945) When running `build_all.sh` I was getting: ``` bin/nim c -o:bin/nimsuggest -d:danger --skipUserCfg --skipParentCfg --hints:off nimsuggest/nimsuggest.nim bin/nim c -o:bin/nimgrep -d:release --skipUserCfg --skipParentCfg --hints:off tools/nimgrep.nim bin/nim c -o:bin/nimpretty -d:release --skipUserCfg --skipParentCfg --hints:off nimpretty/nimpretty.nim bin/nim c -o:bin/testament -d:release --skipUserCfg --skipParentCfg --hints:off testament/testament.nim bin/nim c -o:bin/nim_dbg --opt:speed --stacktrace -d:debug --stacktraceMsgs -d:nimCompilerStacktraceHints --skipUserCfg --skipParentCfg --hints:off compiler/nim.nim bin/nim c -o:bin/atlas -d:release --skipUserCfg --skipParentCfg --hints:off tools/atlas/atlas.nim /home/dom/.choosenim/toolchains/nim-#devel/koch.nim(722) koch /home/dom/.choosenim/toolchains/nim-#devel/koch.nim(149) bundleNimbleExe /home/dom/.choosenim/toolchains/nim-#devel/tools/deps.nim(32) cloneDependency /home/dom/.choosenim/toolchains/nim-#devel/lib/pure/os.nim(1438) setCurrentDir /home/dom/.choosenim/toolchains/nim-#devel/lib/pure/includes/oserr.nim(95) raiseOSError Error: unhandled exception: No such file or directory Additional info: '/home/dom/.choosenim/toolchains/nim-#devel/dist/nimble' [OSError] ``` With this patch it builds. --- tools/deps.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/deps.nim b/tools/deps.nim index 95b2916a1..59872070d 100644 --- a/tools/deps.nim +++ b/tools/deps.nim @@ -22,14 +22,14 @@ proc cloneDependency*(destDirBase: string, url: string, commit = commitHead, let name = p.splitFile.name var destDir = destDirBase if appendRepoName: destDir = destDir / name - let destDir2 = destDir.quoteShell + let quotedDestDir = destDir.quoteShell if not dirExists(destDir): # note: old code used `destDir / .git` but that wouldn't prevent git clone # from failing - execRetry fmt"git clone -q {url} {destDir2}" + execRetry fmt"git clone -q {url} {quotedDestDir}" if isGitRepo(destDir): let oldDir = getCurrentDir() - setCurrentDir(destDir2) + setCurrentDir(destDir) try: execRetry "git fetch -q" exec fmt"git checkout -q {commit}" From 97c24dd54892a1f256707bc73230fe5443be36ce Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 4 Oct 2021 15:19:20 +0800 Subject: [PATCH 2562/3768] correct changelog [backport] (#18940) --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 38ace19ed..f8dc9087c 100644 --- a/changelog.md +++ b/changelog.md @@ -103,7 +103,7 @@ - In `std/dom`, `Interval` is now a `ref object`, same as `Timeout`. Definitions of `setTimeout`, `clearTimeout`, `setInterval`, `clearInterval` were updated. -- With `-d:nimPreviewDotLikeOps` (default in devel), dot-like operators (operators starting with `.`, but not with `..`) +- With `-d:nimPreviewDotLikeOps`, dot-like operators (operators starting with `.`, but not with `..`) now have the same precedence as `.`, so that `a.?b.c` is now parsed as `(a.?b).c` instead of `a.?(b.c)`. A warning is generated when a dot-like operator is used without `-d:nimPreviewDotLikeOps`. From 500d5f0f38dc59895908f25f2c43c2a4c61d7874 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 4 Oct 2021 12:34:29 +0200 Subject: [PATCH 2563/3768] typo [backport] (#18948) --- doc/manual.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual.rst b/doc/manual.rst index dfcf0ba54..4b212fe19 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -1201,7 +1201,7 @@ The only operations that are affected by the `floatChecks` pragma are the `+`, `-`, `*`, `/` operators for floating-point types. An implementation should always use the maximum precision available to evaluate -floating pointer values during semantic analysis; this means expressions like +floating-point values during semantic analysis; this means expressions like `0.09'f32 + 0.01'f32 == 0.09'f64 + 0.01'f64` that are evaluating during constant folding are true. From f017eadc97b041e678196cb2b72c3c66767e7ab0 Mon Sep 17 00:00:00 2001 From: quantimnot <54247259+quantimnot@users.noreply.github.com> Date: Mon, 4 Oct 2021 15:27:13 -0400 Subject: [PATCH 2564/3768] Add support for `strictEffects` to `std/pegs` (#18949) * Add support for `strictEffects` to `std/pegs` * Fixed support of older Nim versions Co-authored-by: quantimnot --- lib/pure/pegs.nim | 5 ++++- tests/stdlib/tpegs.nim | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim index b6b05cdc1..11270e34c 100644 --- a/lib/pure/pegs.nim +++ b/lib/pure/pegs.nim @@ -1285,9 +1285,12 @@ proc parallelReplace*(s: string, subs: varargs[ # copy the rest: add(result, substr(s, i)) +when not defined(nimHasEffectsOf): + {.pragma: effectsOf.} + proc replace*(s: string, sub: Peg, cb: proc( match: int, cnt: int, caps: openArray[string]): string): string {. - rtl, extern: "npegs$1cb".} = + rtl, extern: "npegs$1cb", effectsOf: cb.} = ## Replaces `sub` in `s` by the resulting strings from the callback. ## The callback proc receives the index of the current match (starting with 0), ## the count of captures and an open array with the captures of each match. Examples: diff --git a/tests/stdlib/tpegs.nim b/tests/stdlib/tpegs.nim index 1261d55b8..c3d8942cf 100644 --- a/tests/stdlib/tpegs.nim +++ b/tests/stdlib/tpegs.nim @@ -51,6 +51,9 @@ Event parser output ''' """ +when defined(nimHasEffectsOf): + {.experimental: "strictEffects".} + import std/[strutils, streams, pegs] const From e7bac9177335c75b4eeed18ec5f8a0b0489c0561 Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 5 Oct 2021 03:28:13 +0800 Subject: [PATCH 2565/3768] rename nimEnableHashRef [backport] (#18941) * rename nimEnableHashRef [backport] * Apply suggestions from code review Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> --- changelog.md | 3 ++- lib/pure/hashes.nim | 18 +++++++++++++----- tests/collections/ttables.nim | 1 - tests/config.nims | 1 + tests/stdlib/thashes.nim | 1 - 5 files changed, 16 insertions(+), 8 deletions(-) diff --git a/changelog.md b/changelog.md index f8dc9087c..1925878bf 100644 --- a/changelog.md +++ b/changelog.md @@ -64,7 +64,8 @@ for previous behavior. - `hashes.hash` can now support `object` and `ref` (can be overloaded in user code), - if `-d:nimEnableHashRef` is used. + if `-d:nimPreviewHashRef` is used. It is expected that this behavior + becomes the new default in upcoming versions. - `hashes.hash(proc|ptr|ref|pointer)` now calls `hash(int)` and honors `-d:nimIntHash1`, `hashes.hash(closure)` has also been improved. diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim index 1c49b0317..d9f534670 100644 --- a/lib/pure/hashes.nim +++ b/lib/pure/hashes.nim @@ -51,8 +51,12 @@ runnableExamples: h = h !& hash(x.bar) result = !$h -## **Note:** If the type has a `==` operator, the following must hold: -## If two values compare equal, their hashes must also be equal. +## .. important:: Use `-d:nimPreviewHashRef` to +## enable hashing `ref`s. It is expected that this behavior +## becomes the new default in upcoming versions. +## +## .. note:: If the type has a `==` operator, the following must hold: +## If two values compare equal, their hashes must also be equal. ## ## See also ## ======== @@ -236,10 +240,14 @@ proc hash*[T](x: ptr[T]): Hash {.inline.} = assert cast[pointer](a[0].addr).hash == a[0].addr.hash hash(cast[pointer](x)) -when defined(nimEnableHashRef): +when defined(nimPreviewHashRef) or defined(nimdoc): proc hash*[T](x: ref[T]): Hash {.inline.} = ## Efficient `hash` overload. - runnableExamples: + ## + ## .. important:: Use `-d:nimPreviewHashRef` to + ## enable hashing `ref`s. It is expected that this behavior + ## becomes the new default in upcoming versions. + runnableExamples("-d:nimPreviewHashRef"): type A = ref object x: int let a = A(x: 3) @@ -247,7 +255,7 @@ when defined(nimEnableHashRef): assert ha != A(x: 3).hash # A(x: 3) is a different ref object from `a`. a.x = 4 assert ha == a.hash # the hash only depends on the address - runnableExamples: + runnableExamples("-d:nimPreviewHashRef"): # you can overload `hash` if you want to customize semantics type A[T] = ref object x, y: T diff --git a/tests/collections/ttables.nim b/tests/collections/ttables.nim index 751509062..638f4241b 100644 --- a/tests/collections/ttables.nim +++ b/tests/collections/ttables.nim @@ -8,7 +8,6 @@ And we get here ''' joinable: false targets: "c cpp js" -matrix: "-d:nimEnableHashRef" """ # xxx wrap in a template to test in VM, see https://github.com/timotheecour/Nim/issues/534#issuecomment-769565033 diff --git a/tests/config.nims b/tests/config.nims index ed2a1b7e9..894c4bea0 100644 --- a/tests/config.nims +++ b/tests/config.nims @@ -39,3 +39,4 @@ switch("define", "nimExperimentalLinenoiseExtra") switch("define", "nimPreviewFloatRoundtrip") switch("define", "nimPreviewDotLikeOps") switch("define", "nimPreviewJsonutilsHoleyEnum") +switch("define", "nimPreviewHashRef") diff --git a/tests/stdlib/thashes.nim b/tests/stdlib/thashes.nim index 1e9b02d0c..46576ef12 100644 --- a/tests/stdlib/thashes.nim +++ b/tests/stdlib/thashes.nim @@ -1,6 +1,5 @@ discard """ targets: "c cpp js" - matrix: "-d:nimEnableHashRef" """ import std/hashes From f1f1e85ec673b9ea2ea5d3a97aa27993be153602 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 5 Oct 2021 12:29:59 +0200 Subject: [PATCH 2566/3768] fixes #18954 (#18955) --- lib/pure/sugar.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pure/sugar.nim b/lib/pure/sugar.nim index f82e1706f..d7beef8d9 100644 --- a/lib/pure/sugar.nim +++ b/lib/pure/sugar.nim @@ -121,9 +121,9 @@ macro `=>`*(p, b: untyped): untyped = else: error("Incorrect procedure parameter.", c) params.add(identDefs) - of nnkIdent: + of nnkIdent, nnkOpenSymChoice, nnkClosedSymChoice, nnkSym: var identDefs = newNimNode(nnkIdentDefs) - identDefs.add(p) + identDefs.add(ident $p) identDefs.add(ident"auto") identDefs.add(newEmptyNode()) params.add(identDefs) From 9fc2918314a75fd1923af049966e4c4e9d8cf710 Mon Sep 17 00:00:00 2001 From: quantimnot <54247259+quantimnot@users.noreply.github.com> Date: Wed, 6 Oct 2021 03:41:42 -0400 Subject: [PATCH 2567/3768] Document how to compile a reproducible build (#18953) * Make compiler build more reproducible Removed the compile time from the version output. Added Git commit's author's datetime in UTC timezone. Fixes #18508 See https://reproducible-builds.org/docs/timestamps/ See https://reproducible-builds.org/docs/source-date-epoch/ * Revert "Make compiler build more reproducible" This reverts commit 2f9359ae89622c2d95ef243d13251a310e94a199. Documented how to use compile reproducible builds with the `SOURCE_DATE_EPOCH` environment variable. * Corrected `readme.md` formatting Co-authored-by: quantimnot --- doc/intern.rst | 11 +++++++++++ doc/packaging.rst | 4 ++++ readme.md | 3 ++- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/doc/intern.rst b/doc/intern.rst index ab74c4a3a..fe0d333fd 100644 --- a/doc/intern.rst +++ b/doc/intern.rst @@ -68,6 +68,17 @@ More information about its options can be found in the `koch `_ documentation. +Reproducible builds +------------------- + +Set the compilation timestamp with the `SOURCE_DATE_EPOCH` environment variable. + +.. code:: cmd + + export SOURCE_DATE_EPOCH=$(git log -n 1 --format=%at) + koch boot # or `./build_all.sh` + + Developing the compiler ======================= diff --git a/doc/packaging.rst b/doc/packaging.rst index fcdc4ae04..7976dfe92 100644 --- a/doc/packaging.rst +++ b/doc/packaging.rst @@ -6,6 +6,9 @@ This page provide hints on distributing Nim using OS packages. See `distros `_ for tools to detect Linux distribution at runtime. +See `here `_ for how to +compile reproducible builds. + Supported architectures ----------------------- @@ -71,3 +74,4 @@ What to install: - Optionally: manpages, documentation, shell completion - When installing documentation, .idx files are not required - The "compiler" directory contains compiler sources and should not be part of the compiler binary package + diff --git a/readme.md b/readme.md index b28c25ec1..7f333d5d4 100644 --- a/readme.md +++ b/readme.md @@ -71,7 +71,6 @@ the installation instructions on the website to do so: https://nim-lang.org/inst For package maintainers: see [packaging guidelines](https://nim-lang.github.io/Nim/packaging.html). - First, get Nim from github: ``` @@ -89,6 +88,8 @@ should add the ``bin`` directory to your PATH. See also [rebuilding the compiler](doc/intern.rst#rebuilding-the-compiler). +See also [reproducible builds](doc/intern.rst#reproducible-builds). + ## Koch ``koch`` is the build tool used to build various parts of Nim and to generate From cb30dc51701f0d4bc5dedc2615de68e4c1e5ba71 Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 6 Oct 2021 15:42:35 +0800 Subject: [PATCH 2568/3768] enable tests for #2710 (#18961) --- tests/metatype/ttypedesc3.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/metatype/ttypedesc3.nim b/tests/metatype/ttypedesc3.nim index b691d3488..98a59f613 100644 --- a/tests/metatype/ttypedesc3.nim +++ b/tests/metatype/ttypedesc3.nim @@ -3,6 +3,7 @@ output: ''' proc Base proc Child method Base +method Child yield Base yield Child 12 @@ -24,8 +25,7 @@ Base.pr Child.pr Base.me -when false: - Child.me #<- bug #2710 +Child.me #<- bug #2710 for s in Base.it: echo s for s in Child.it: echo s #<- bug #2662 From b2873f0f63f7741ee995363a6bb225a228d03d18 Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 6 Oct 2021 18:47:46 +0800 Subject: [PATCH 2569/3768] [tools] use the right parameter [backport:1.0] (#18957) --- tools/finish.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/finish.nim b/tools/finish.nim index 4b5131045..69838829c 100644 --- a/tools/finish.nim +++ b/tools/finish.nim @@ -41,7 +41,7 @@ proc downloadMingw(): DownloadResult = let curl = findExe"curl" var cmd: string if curl.len > 0: - cmd = quoteShell(curl) & " --out " & "dist" / mingw & " " & url + cmd = quoteShell(curl) & " --output " & "dist" / mingw & " " & url elif fileExists"bin/nimgrab.exe": cmd = r"bin\nimgrab.exe " & url & " dist" / mingw if cmd.len > 0: From f03872d99ec2d25de829415a75162f9d8297bb19 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Wed, 6 Oct 2021 14:53:01 +0300 Subject: [PATCH 2570/3768] rst: minor fixes (#18960) --- lib/packages/docutils/rst.nim | 1 + lib/packages/docutils/rstast.nim | 4 ++-- lib/packages/docutils/rstgen.nim | 2 +- tests/stdlib/trst.nim | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index abea026dd..29234f28b 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -172,6 +172,7 @@ ## ``header``, ``footer``, ``meta``, ``class`` ## - no ``role`` directives and no custom interpreted text roles ## - some standard roles are not supported (check `RST roles list`_) +## - no generic admonition support ## * inline markup ## - no simple-inline-markup ## - no embedded aliases diff --git a/lib/packages/docutils/rstast.nim b/lib/packages/docutils/rstast.nim index fa0620f44..e1ed7c099 100644 --- a/lib/packages/docutils/rstast.nim +++ b/lib/packages/docutils/rstast.nim @@ -381,7 +381,7 @@ proc renderRstToText*(node: PRstNode): string = result.add renderRstToText(node.sons[i]) if node.kind in code: result.add "`" -proc renderRstToStr*(node: PRstNode, indent=0): string = +proc treeRepr*(node: PRstNode, indent=0): string = ## Writes the parsed RST `node` into an AST tree with compact string ## representation in the format (one line per every sub-node): ## ``indent - kind - [text|level|order|adType] - anchor (if non-zero)`` @@ -411,4 +411,4 @@ proc renderRstToStr*(node: PRstNode, indent=0): string = result.add (if node.anchor == "": "" else: " anchor='" & node.anchor & "'") result.add "\n" for son in node.sons: - result.add renderRstToStr(son, indent=indent+2) + result.add treeRepr(son, indent=indent+2) diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 7d7eb4fb8..8eac37307 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -1164,7 +1164,7 @@ proc renderAdmonition(d: PDoc, n: PRstNode, result: var string) = case n.adType of "hint", "note", "tip": htmlCls = "admonition-info"; texSz = "\\normalsize"; texColor = "green" - of "attention", "admonition", "important", "warning": + of "attention", "admonition", "important", "warning", "caution": htmlCls = "admonition-warning"; texSz = "\\large"; texColor = "orange" of "danger", "error": htmlCls = "admonition-error"; texSz = "\\Large"; texColor = "red" diff --git a/tests/stdlib/trst.nim b/tests/stdlib/trst.nim index bc11f219c..e9f67324c 100644 --- a/tests/stdlib/trst.nim +++ b/tests/stdlib/trst.nim @@ -55,7 +55,7 @@ proc toAst(input: string, var (rst, _, _) = rstParse(input, filen, line=LineRstInit, column=ColRstInit, rstOptions, myFindFile, testMsgHandler) - result = renderRstToStr(rst) + result = treeRepr(rst) except EParseError as e: if e.msg != "": result = e.msg From eede2bfb5de30f7f411d9437e19f253f8dac629b Mon Sep 17 00:00:00 2001 From: Miran Date: Thu, 7 Oct 2021 10:48:09 +0200 Subject: [PATCH 2571/3768] update csources hash (#18969) --- config/build_config.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/build_config.txt b/config/build_config.txt index aee15e695..15f44bb57 100644 --- a/config/build_config.txt +++ b/config/build_config.txt @@ -2,4 +2,4 @@ nim_comment="key-value pairs for windows/posix bootstrapping build scripts" nim_csourcesDir=csources_v1 nim_csourcesUrl=https://github.com/nim-lang/csources_v1.git nim_csourcesBranch=master -nim_csourcesHash=5e9be0383f842c9c22ae86e41946a896c43e95d0 +nim_csourcesHash=561b417c65791cd8356b5f73620914ceff845d10 From 8eef55715725a9dba2ff0afbdbb564383f3a29a6 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 7 Oct 2021 12:01:06 +0200 Subject: [PATCH 2572/3768] fixes a 'mixin' statement handling regression [backport:1.2] (#18968) --- compiler/semtempl.nim | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index eda4ca6cc..c10917bcb 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -110,9 +110,14 @@ proc semBindStmt(c: PContext, n: PNode, toBind: var IntSet): PNode = proc semMixinStmt(c: PContext, n: PNode, toMixin: var IntSet): PNode = result = copyNode(n) + var count = 0 for i in 0.. Date: Thu, 7 Oct 2021 18:01:27 +0800 Subject: [PATCH 2573/3768] typo [backport] (#18967) --- lib/pure/strscans.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/strscans.nim b/lib/pure/strscans.nim index 291af7408..99442075a 100644 --- a/lib/pure/strscans.nim +++ b/lib/pure/strscans.nim @@ -35,7 +35,7 @@ substrings starting with ``$``. These constructions are available: ``$o`` Matches an octal integer. This uses ``parseutils.parseOct``. ``$i`` Matches a decimal integer. This uses ``parseutils.parseInt``. ``$h`` Matches a hex integer. This uses ``parseutils.parseHex``. -``$f`` Matches a floating pointer number. Uses ``parseFloat``. +``$f`` Matches a floating-point number. Uses ``parseFloat``. ``$w`` Matches an ASCII identifier: ``[A-Za-z_][A-Za-z_0-9]*``. ``$c`` Matches a single ASCII character. ``$s`` Skips optional whitespace. From 19774a72e7fcc7c5b7649bb956b149362c05ea15 Mon Sep 17 00:00:00 2001 From: quantimnot <54247259+quantimnot@users.noreply.github.com> Date: Thu, 7 Oct 2021 06:16:58 -0400 Subject: [PATCH 2574/3768] Fixed `strictFuncs` support for `std/pegs` (#18951) * Fixed `strictFuncs` support for `std/pegs` Enabled `std/pegs` in the `strictFuncs` import test. Fixes #18057 Fixes #16892 See #18111 * Rebased from `devel` * Conditionally compile `std/pegs` in `koch` This is for supporting `csources` bootstrap. Co-authored-by: quantimnot --- lib/pure/pegs.nim | 280 ++++++++++++------------ tests/effects/tstrict_funcs_imports.nim | 2 +- tools/kochdocs.nim | 11 +- 3 files changed, 151 insertions(+), 142 deletions(-) diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim index 11270e34c..bac8b1a0e 100644 --- a/lib/pure/pegs.nim +++ b/lib/pure/pegs.nim @@ -87,26 +87,26 @@ type else: sons: seq[Peg] NonTerminal* = ref NonTerminalObj -proc kind*(p: Peg): PegKind = p.kind +func kind*(p: Peg): PegKind = p.kind ## Returns the *PegKind* of a given *Peg* object. -proc term*(p: Peg): string = p.term +func term*(p: Peg): string = p.term ## Returns the *string* representation of a given *Peg* variant object ## where present. -proc ch*(p: Peg): char = p.ch +func ch*(p: Peg): char = p.ch ## Returns the *char* representation of a given *Peg* variant object ## where present. -proc charChoice*(p: Peg): ref set[char] = p.charChoice +func charChoice*(p: Peg): ref set[char] = p.charChoice ## Returns the *charChoice* field of a given *Peg* variant object ## where present. -proc nt*(p: Peg): NonTerminal = p.nt +func nt*(p: Peg): NonTerminal = p.nt ## Returns the *NonTerminal* object of a given *Peg* variant object ## where present. -proc index*(p: Peg): range[-MaxSubpatterns..MaxSubpatterns-1] = p.index +func index*(p: Peg): range[-MaxSubpatterns..MaxSubpatterns-1] = p.index ## Returns the back-reference index of a captured sub-pattern in the ## *Captures* object for a given *Peg* variant object where present. @@ -120,59 +120,59 @@ iterator pairs*(p: Peg): (int, Peg) {.inline.} = for i in 0 ..< p.sons.len: yield (i, p.sons[i]) -proc name*(nt: NonTerminal): string = nt.name +func name*(nt: NonTerminal): string = nt.name ## Gets the name of the symbol represented by the parent *Peg* object variant ## of a given *NonTerminal*. -proc line*(nt: NonTerminal): int = nt.line +func line*(nt: NonTerminal): int = nt.line ## Gets the line number of the definition of the parent *Peg* object variant ## of a given *NonTerminal*. -proc col*(nt: NonTerminal): int = nt.col +func col*(nt: NonTerminal): int = nt.col ## Gets the column number of the definition of the parent *Peg* object variant ## of a given *NonTerminal*. -proc flags*(nt: NonTerminal): set[NonTerminalFlag] = nt.flags +func flags*(nt: NonTerminal): set[NonTerminalFlag] = nt.flags ## Gets the *NonTerminalFlag*-typed flags field of the parent *Peg* variant ## object of a given *NonTerminal*. -proc rule*(nt: NonTerminal): Peg = nt.rule +func rule*(nt: NonTerminal): Peg = nt.rule ## Gets the *Peg* object representing the rule definition of the parent *Peg* ## object variant of a given *NonTerminal*. -proc term*(t: string): Peg {.noSideEffect, rtl, extern: "npegs$1Str".} = +func term*(t: string): Peg {.rtl, extern: "npegs$1Str".} = ## constructs a PEG from a terminal string if t.len != 1: result = Peg(kind: pkTerminal, term: t) else: result = Peg(kind: pkChar, ch: t[0]) -proc termIgnoreCase*(t: string): Peg {. - noSideEffect, rtl, extern: "npegs$1".} = +func termIgnoreCase*(t: string): Peg {. + rtl, extern: "npegs$1".} = ## constructs a PEG from a terminal string; ignore case for matching result = Peg(kind: pkTerminalIgnoreCase, term: t) -proc termIgnoreStyle*(t: string): Peg {. - noSideEffect, rtl, extern: "npegs$1".} = +func termIgnoreStyle*(t: string): Peg {. + rtl, extern: "npegs$1".} = ## constructs a PEG from a terminal string; ignore style for matching result = Peg(kind: pkTerminalIgnoreStyle, term: t) -proc term*(t: char): Peg {.noSideEffect, rtl, extern: "npegs$1Char".} = +func term*(t: char): Peg {.rtl, extern: "npegs$1Char".} = ## constructs a PEG from a terminal char assert t != '\0' result = Peg(kind: pkChar, ch: t) -proc charSet*(s: set[char]): Peg {.noSideEffect, rtl, extern: "npegs$1".} = +func charSet*(s: set[char]): Peg {.rtl, extern: "npegs$1".} = ## constructs a PEG from a character set `s` assert '\0' notin s result = Peg(kind: pkCharChoice) new(result.charChoice) result.charChoice[] = s -proc len(a: Peg): int {.inline.} = return a.sons.len -proc add(d: var Peg, s: Peg) {.inline.} = add(d.sons, s) +func len(a: Peg): int {.inline.} = return a.sons.len +func add(d: var Peg, s: Peg) {.inline.} = add(d.sons, s) -proc addChoice(dest: var Peg, elem: Peg) = +func addChoice(dest: var Peg, elem: Peg) = var L = dest.len-1 if L >= 0 and dest.sons[L].kind == pkCharChoice: # caution! Do not introduce false aliasing here! @@ -195,12 +195,12 @@ template multipleOp(k: PegKind, localOpt: untyped) = if result.len == 1: result = result.sons[0] -proc `/`*(a: varargs[Peg]): Peg {. - noSideEffect, rtl, extern: "npegsOrderedChoice".} = +func `/`*(a: varargs[Peg]): Peg {. + rtl, extern: "npegsOrderedChoice".} = ## constructs an ordered choice with the PEGs in `a` multipleOp(pkOrderedChoice, addChoice) -proc addSequence(dest: var Peg, elem: Peg) = +func addSequence(dest: var Peg, elem: Peg) = var L = dest.len-1 if L >= 0 and dest.sons[L].kind == pkTerminal: # caution! Do not introduce false aliasing here! @@ -212,12 +212,12 @@ proc addSequence(dest: var Peg, elem: Peg) = else: add(dest, elem) else: add(dest, elem) -proc sequence*(a: varargs[Peg]): Peg {. - noSideEffect, rtl, extern: "npegs$1".} = +func sequence*(a: varargs[Peg]): Peg {. + rtl, extern: "npegs$1".} = ## constructs a sequence with all the PEGs from `a` multipleOp(pkSequence, addSequence) -proc `?`*(a: Peg): Peg {.noSideEffect, rtl, extern: "npegsOptional".} = +func `?`*(a: Peg): Peg {.rtl, extern: "npegsOptional".} = ## constructs an optional for the PEG `a` if a.kind in {pkOption, pkGreedyRep, pkGreedyAny, pkGreedyRepChar, pkGreedyRepSet}: @@ -227,7 +227,7 @@ proc `?`*(a: Peg): Peg {.noSideEffect, rtl, extern: "npegsOptional".} = else: result = Peg(kind: pkOption, sons: @[a]) -proc `*`*(a: Peg): Peg {.noSideEffect, rtl, extern: "npegsGreedyRep".} = +func `*`*(a: Peg): Peg {.rtl, extern: "npegsGreedyRep".} = ## constructs a "greedy repetition" for the PEG `a` case a.kind of pkGreedyRep, pkGreedyRepChar, pkGreedyRepSet, pkGreedyAny, pkOption: @@ -242,94 +242,94 @@ proc `*`*(a: Peg): Peg {.noSideEffect, rtl, extern: "npegsGreedyRep".} = else: result = Peg(kind: pkGreedyRep, sons: @[a]) -proc `!*`*(a: Peg): Peg {.noSideEffect, rtl, extern: "npegsSearch".} = +func `!*`*(a: Peg): Peg {.rtl, extern: "npegsSearch".} = ## constructs a "search" for the PEG `a` result = Peg(kind: pkSearch, sons: @[a]) -proc `!*\`*(a: Peg): Peg {.noSideEffect, rtl, +func `!*\`*(a: Peg): Peg {.rtl, extern: "npgegsCapturedSearch".} = ## constructs a "captured search" for the PEG `a` result = Peg(kind: pkCapturedSearch, sons: @[a]) -proc `+`*(a: Peg): Peg {.noSideEffect, rtl, extern: "npegsGreedyPosRep".} = +func `+`*(a: Peg): Peg {.rtl, extern: "npegsGreedyPosRep".} = ## constructs a "greedy positive repetition" with the PEG `a` return sequence(a, *a) -proc `&`*(a: Peg): Peg {.noSideEffect, rtl, extern: "npegsAndPredicate".} = +func `&`*(a: Peg): Peg {.rtl, extern: "npegsAndPredicate".} = ## constructs an "and predicate" with the PEG `a` result = Peg(kind: pkAndPredicate, sons: @[a]) -proc `!`*(a: Peg): Peg {.noSideEffect, rtl, extern: "npegsNotPredicate".} = +func `!`*(a: Peg): Peg {.rtl, extern: "npegsNotPredicate".} = ## constructs a "not predicate" with the PEG `a` result = Peg(kind: pkNotPredicate, sons: @[a]) -proc any*: Peg {.inline.} = +func any*: Peg {.inline.} = ## constructs the PEG `any character`:idx: (``.``) result = Peg(kind: pkAny) -proc anyRune*: Peg {.inline.} = +func anyRune*: Peg {.inline.} = ## constructs the PEG `any rune`:idx: (``_``) result = Peg(kind: pkAnyRune) -proc newLine*: Peg {.inline.} = +func newLine*: Peg {.inline.} = ## constructs the PEG `newline`:idx: (``\n``) result = Peg(kind: pkNewLine) -proc unicodeLetter*: Peg {.inline.} = +func unicodeLetter*: Peg {.inline.} = ## constructs the PEG ``\letter`` which matches any Unicode letter. result = Peg(kind: pkLetter) -proc unicodeLower*: Peg {.inline.} = +func unicodeLower*: Peg {.inline.} = ## constructs the PEG ``\lower`` which matches any Unicode lowercase letter. result = Peg(kind: pkLower) -proc unicodeUpper*: Peg {.inline.} = +func unicodeUpper*: Peg {.inline.} = ## constructs the PEG ``\upper`` which matches any Unicode uppercase letter. result = Peg(kind: pkUpper) -proc unicodeTitle*: Peg {.inline.} = +func unicodeTitle*: Peg {.inline.} = ## constructs the PEG ``\title`` which matches any Unicode title letter. result = Peg(kind: pkTitle) -proc unicodeWhitespace*: Peg {.inline.} = +func unicodeWhitespace*: Peg {.inline.} = ## constructs the PEG ``\white`` which matches any Unicode ## whitespace character. result = Peg(kind: pkWhitespace) -proc startAnchor*: Peg {.inline.} = +func startAnchor*: Peg {.inline.} = ## constructs the PEG ``^`` which matches the start of the input. result = Peg(kind: pkStartAnchor) -proc endAnchor*: Peg {.inline.} = +func endAnchor*: Peg {.inline.} = ## constructs the PEG ``$`` which matches the end of the input. result = !any() -proc capture*(a: Peg = Peg(kind: pkEmpty)): Peg {.noSideEffect, rtl, extern: "npegsCapture".} = +func capture*(a: Peg = Peg(kind: pkEmpty)): Peg {.rtl, extern: "npegsCapture".} = ## constructs a capture with the PEG `a` result = Peg(kind: pkCapture, sons: @[a]) -proc backref*(index: range[1..MaxSubpatterns], reverse: bool = false): Peg {. - noSideEffect, rtl, extern: "npegs$1".} = +func backref*(index: range[1..MaxSubpatterns], reverse: bool = false): Peg {. + rtl, extern: "npegs$1".} = ## constructs a back reference of the given `index`. `index` starts counting ## from 1. `reverse` specifies wether indexing starts from the end of the ## capture list. result = Peg(kind: pkBackRef, index: (if reverse: -index else: index - 1)) -proc backrefIgnoreCase*(index: range[1..MaxSubpatterns], reverse: bool = false): Peg {. - noSideEffect, rtl, extern: "npegs$1".} = +func backrefIgnoreCase*(index: range[1..MaxSubpatterns], reverse: bool = false): Peg {. + rtl, extern: "npegs$1".} = ## constructs a back reference of the given `index`. `index` starts counting ## from 1. `reverse` specifies wether indexing starts from the end of the ## capture list. Ignores case for matching. result = Peg(kind: pkBackRefIgnoreCase, index: (if reverse: -index else: index - 1)) -proc backrefIgnoreStyle*(index: range[1..MaxSubpatterns], reverse: bool = false): Peg {. - noSideEffect, rtl, extern: "npegs$1".} = +func backrefIgnoreStyle*(index: range[1..MaxSubpatterns], reverse: bool = false): Peg {. + rtl, extern: "npegs$1".} = ## constructs a back reference of the given `index`. `index` starts counting ## from 1. `reverse` specifies wether indexing starts from the end of the ## capture list. Ignores style for matching. result = Peg(kind: pkBackRefIgnoreStyle, index: (if reverse: -index else: index - 1)) -proc spaceCost(n: Peg): int = +func spaceCost(n: Peg): int = case n.kind of pkEmpty: discard of pkTerminal, pkTerminalIgnoreCase, pkTerminalIgnoreStyle, pkChar, @@ -344,8 +344,8 @@ proc spaceCost(n: Peg): int = inc(result, spaceCost(n.sons[i])) if result >= InlineThreshold: break -proc nonterminal*(n: NonTerminal): Peg {. - noSideEffect, rtl, extern: "npegs$1".} = +func nonterminal*(n: NonTerminal): Peg {. + rtl, extern: "npegs$1".} = ## constructs a PEG that consists of the nonterminal symbol assert n != nil if ntDeclared in n.flags and spaceCost(n.rule) < InlineThreshold: @@ -354,8 +354,8 @@ proc nonterminal*(n: NonTerminal): Peg {. else: result = Peg(kind: pkNonTerminal, nt: n) -proc newNonTerminal*(name: string, line, column: int): NonTerminal {. - noSideEffect, rtl, extern: "npegs$1".} = +func newNonTerminal*(name: string, line, column: int): NonTerminal {. + rtl, extern: "npegs$1".} = ## constructs a nonterminal symbol result = NonTerminal(name: name, line: line, col: column) @@ -390,7 +390,7 @@ template natural*: Peg = # ------------------------- debugging ----------------------------------------- -proc esc(c: char, reserved = {'\0'..'\255'}): string = +func esc(c: char, reserved = {'\0'..'\255'}): string = case c of '\b': result = "\\b" of '\t': result = "\\t" @@ -406,14 +406,14 @@ proc esc(c: char, reserved = {'\0'..'\255'}): string = elif c in reserved: result = '\\' & c else: result = $c -proc singleQuoteEsc(c: char): string = return "'" & esc(c, {'\''}) & "'" +func singleQuoteEsc(c: char): string = return "'" & esc(c, {'\''}) & "'" -proc singleQuoteEsc(str: string): string = +func singleQuoteEsc(str: string): string = result = "'" for c in items(str): add result, esc(c, {'\''}) add result, '\'' -proc charSetEscAux(cc: set[char]): string = +func charSetEscAux(cc: set[char]): string = const reserved = {'^', '-', ']'} result = "" var c1 = 0 @@ -430,13 +430,13 @@ proc charSetEscAux(cc: set[char]): string = c1 = c2 inc(c1) -proc charSetEsc(cc: set[char]): string = +func charSetEsc(cc: set[char]): string = if card(cc) >= 128+64: result = "[^" & charSetEscAux({'\1'..'\xFF'} - cc) & ']' else: result = '[' & charSetEscAux(cc) & ']' -proc toStrAux(r: Peg, res: var string) = +func toStrAux(r: Peg, res: var string) = case r.kind of pkEmpty: add(res, "()") of pkAny: add(res, '.') @@ -522,7 +522,7 @@ proc toStrAux(r: Peg, res: var string) = of pkStartAnchor: add(res, '^') -proc `$` *(r: Peg): string {.noSideEffect, rtl, extern: "npegsToString".} = +func `$` *(r: Peg): string {.rtl, extern: "npegsToString".} = ## converts a PEG to its string representation result = "" toStrAux(r, result) @@ -535,7 +535,7 @@ type ml: int origStart: int -proc bounds*(c: Captures, +func bounds*(c: Captures, i: range[0..MaxSubpatterns-1]): tuple[first, last: int] = ## returns the bounds ``[first..last]`` of the `i`'th capture. result = c.matches[i] @@ -548,11 +548,11 @@ when not useUnicode: inc(i) template runeLenAt(s, i): untyped = 1 - proc isAlpha(a: char): bool {.inline.} = return a in {'a'..'z', 'A'..'Z'} - proc isUpper(a: char): bool {.inline.} = return a in {'A'..'Z'} - proc isLower(a: char): bool {.inline.} = return a in {'a'..'z'} - proc isTitle(a: char): bool {.inline.} = return false - proc isWhiteSpace(a: char): bool {.inline.} = return a in {' ', '\9'..'\13'} + func isAlpha(a: char): bool {.inline.} = return a in {'a'..'z', 'A'..'Z'} + func isUpper(a: char): bool {.inline.} = return a in {'A'..'Z'} + func isLower(a: char): bool {.inline.} = return a in {'a'..'z'} + func isTitle(a: char): bool {.inline.} = return false + func isWhiteSpace(a: char): bool {.inline.} = return a in {' ', '\9'..'\13'} template matchOrParse(mopProc: untyped) = # Used to make the main matcher proc *rawMatch* as well as event parser @@ -860,8 +860,8 @@ template matchOrParse(mopProc: untyped) = leave(pkStartAnchor, s, p, start, result) of pkRule, pkList: assert false -proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int - {.noSideEffect, rtl, extern: "npegs$1".} = +func rawMatch*(s: string, p: Peg, start: int, c: var Captures): int + {.rtl, extern: "npegs$1".} = ## low-level matching proc that implements the PEG interpreter. Use this ## for maximum efficiency (every other PEG operation ends up calling this ## proc). @@ -873,7 +873,11 @@ proc rawMatch*(s: string, p: Peg, start: int, c: var Captures): int template leave(pk, s, p, start, length) = discard matchOrParse(matchIt) - result = matchIt(s, p, start, c) + {.cast(noSideEffect).}: + # This cast is allowed because the `matchOrParse` template is used for + # both matching and parsing, but side effects are only possible when it's + # used by `eventParser`. + result = matchIt(s, p, start, c) macro mkHandlerTplts(handlers: untyped): untyped = # Transforms the handler spec in *handlers* into handler templates. @@ -900,7 +904,7 @@ macro mkHandlerTplts(handlers: untyped): untyped = # StmtList # # ... - proc mkEnter(hdName, body: NimNode): NimNode = + func mkEnter(hdName, body: NimNode): NimNode = template helper(hdName, body) {.dirty.} = template hdName(s, p, start) = let s {.inject.} = s @@ -1069,8 +1073,8 @@ template fillMatches(s, caps, c) = else: caps[k] = "" -proc matchLen*(s: string, pattern: Peg, matches: var openArray[string], - start = 0): int {.noSideEffect, rtl, extern: "npegs$1Capture".} = +func matchLen*(s: string, pattern: Peg, matches: var openArray[string], + start = 0): int {.rtl, extern: "npegs$1Capture".} = ## the same as ``match``, but it returns the length of the match, ## if there is no match, -1 is returned. Note that a match length ## of zero can happen. It's possible that a suffix of `s` remains @@ -1080,8 +1084,8 @@ proc matchLen*(s: string, pattern: Peg, matches: var openArray[string], result = rawMatch(s, pattern, start, c) if result >= 0: fillMatches(s, matches, c) -proc matchLen*(s: string, pattern: Peg, - start = 0): int {.noSideEffect, rtl, extern: "npegs$1".} = +func matchLen*(s: string, pattern: Peg, + start = 0): int {.rtl, extern: "npegs$1".} = ## the same as ``match``, but it returns the length of the match, ## if there is no match, -1 is returned. Note that a match length ## of zero can happen. It's possible that a suffix of `s` remains @@ -1090,22 +1094,22 @@ proc matchLen*(s: string, pattern: Peg, c.origStart = start result = rawMatch(s, pattern, start, c) -proc match*(s: string, pattern: Peg, matches: var openArray[string], - start = 0): bool {.noSideEffect, rtl, extern: "npegs$1Capture".} = +func match*(s: string, pattern: Peg, matches: var openArray[string], + start = 0): bool {.rtl, extern: "npegs$1Capture".} = ## returns ``true`` if ``s[start..]`` matches the ``pattern`` and ## the captured substrings in the array ``matches``. If it does not ## match, nothing is written into ``matches`` and ``false`` is ## returned. result = matchLen(s, pattern, matches, start) != -1 -proc match*(s: string, pattern: Peg, - start = 0): bool {.noSideEffect, rtl, extern: "npegs$1".} = +func match*(s: string, pattern: Peg, + start = 0): bool {.rtl, extern: "npegs$1".} = ## returns ``true`` if ``s`` matches the ``pattern`` beginning from ``start``. result = matchLen(s, pattern, start) != -1 -proc find*(s: string, pattern: Peg, matches: var openArray[string], - start = 0): int {.noSideEffect, rtl, extern: "npegs$1Capture".} = +func find*(s: string, pattern: Peg, matches: var openArray[string], + start = 0): int {.rtl, extern: "npegs$1Capture".} = ## returns the starting position of ``pattern`` in ``s`` and the captured ## substrings in the array ``matches``. If it does not match, nothing ## is written into ``matches`` and -1 is returned. @@ -1119,9 +1123,9 @@ proc find*(s: string, pattern: Peg, matches: var openArray[string], return -1 # could also use the pattern here: (!P .)* P -proc findBounds*(s: string, pattern: Peg, matches: var openArray[string], +func findBounds*(s: string, pattern: Peg, matches: var openArray[string], start = 0): tuple[first, last: int] {. - noSideEffect, rtl, extern: "npegs$1Capture".} = + rtl, extern: "npegs$1Capture".} = ## returns the starting position and end position of ``pattern`` in ``s`` ## and the captured ## substrings in the array ``matches``. If it does not match, nothing @@ -1136,8 +1140,8 @@ proc findBounds*(s: string, pattern: Peg, matches: var openArray[string], return (i, i+L-1) return (-1, 0) -proc find*(s: string, pattern: Peg, - start = 0): int {.noSideEffect, rtl, extern: "npegs$1".} = +func find*(s: string, pattern: Peg, + start = 0): int {.rtl, extern: "npegs$1".} = ## returns the starting position of ``pattern`` in ``s``. If it does not ## match, -1 is returned. var c: Captures @@ -1160,8 +1164,8 @@ iterator findAll*(s: string, pattern: Peg, start = 0): string = yield substr(s, i, i+L-1) inc(i, L) -proc findAll*(s: string, pattern: Peg, start = 0): seq[string] {. - noSideEffect, rtl, extern: "npegs$1".} = +func findAll*(s: string, pattern: Peg, start = 0): seq[string] {. + rtl, extern: "npegs$1".} = ## returns all matching *substrings* of `s` that match `pattern`. ## If it does not match, @[] is returned. result = @[] @@ -1192,31 +1196,31 @@ template `=~`*(s: string, pattern: Peg): bool = # ------------------------- more string handling ------------------------------ -proc contains*(s: string, pattern: Peg, start = 0): bool {. - noSideEffect, rtl, extern: "npegs$1".} = +func contains*(s: string, pattern: Peg, start = 0): bool {. + rtl, extern: "npegs$1".} = ## same as ``find(s, pattern, start) >= 0`` return find(s, pattern, start) >= 0 -proc contains*(s: string, pattern: Peg, matches: var openArray[string], - start = 0): bool {.noSideEffect, rtl, extern: "npegs$1Capture".} = +func contains*(s: string, pattern: Peg, matches: var openArray[string], + start = 0): bool {.rtl, extern: "npegs$1Capture".} = ## same as ``find(s, pattern, matches, start) >= 0`` return find(s, pattern, matches, start) >= 0 -proc startsWith*(s: string, prefix: Peg, start = 0): bool {. - noSideEffect, rtl, extern: "npegs$1".} = +func startsWith*(s: string, prefix: Peg, start = 0): bool {. + rtl, extern: "npegs$1".} = ## returns true if `s` starts with the pattern `prefix` result = matchLen(s, prefix, start) >= 0 -proc endsWith*(s: string, suffix: Peg, start = 0): bool {. - noSideEffect, rtl, extern: "npegs$1".} = +func endsWith*(s: string, suffix: Peg, start = 0): bool {. + rtl, extern: "npegs$1".} = ## returns true if `s` ends with the pattern `suffix` var c: Captures c.origStart = start for i in start .. s.len-1: if rawMatch(s, suffix, i, c) == s.len - i: return true -proc replacef*(s: string, sub: Peg, by: string): string {. - noSideEffect, rtl, extern: "npegs$1".} = +func replacef*(s: string, sub: Peg, by: string): string {. + rtl, extern: "npegs$1".} = ## Replaces `sub` in `s` by the string `by`. Captures can be accessed in `by` ## with the notation ``$i`` and ``$#`` (see strutils.`%`). Examples: ## @@ -1244,8 +1248,8 @@ proc replacef*(s: string, sub: Peg, by: string): string {. inc(i, x) add(result, substr(s, i)) -proc replace*(s: string, sub: Peg, by = ""): string {. - noSideEffect, rtl, extern: "npegs$1".} = +func replace*(s: string, sub: Peg, by = ""): string {. + rtl, extern: "npegs$1".} = ## Replaces `sub` in `s` by the string `by`. Captures cannot be accessed ## in `by`. result = "" @@ -1261,9 +1265,9 @@ proc replace*(s: string, sub: Peg, by = ""): string {. inc(i, x) add(result, substr(s, i)) -proc parallelReplace*(s: string, subs: varargs[ +func parallelReplace*(s: string, subs: varargs[ tuple[pattern: Peg, repl: string]]): string {. - noSideEffect, rtl, extern: "npegs$1".} = + rtl, extern: "npegs$1".} = ## Returns a modified copy of `s` with the substitutions in `subs` ## applied in parallel. result = "" @@ -1288,7 +1292,7 @@ proc parallelReplace*(s: string, subs: varargs[ when not defined(nimHasEffectsOf): {.pragma: effectsOf.} -proc replace*(s: string, sub: Peg, cb: proc( +func replace*(s: string, sub: Peg, cb: proc( match: int, cnt: int, caps: openArray[string]): string): string {. rtl, extern: "npegs$1cb", effectsOf: cb.} = ## Replaces `sub` in `s` by the resulting strings from the callback. @@ -1297,7 +1301,7 @@ proc replace*(s: string, sub: Peg, cb: proc( ## ## .. code-block:: nim ## - ## proc handleMatches*(m: int, n: int, c: openArray[string]): string = + ## func handleMatches*(m: int, n: int, c: openArray[string]): string = ## result = "" ## if m > 0: ## result.add ", " @@ -1380,8 +1384,8 @@ iterator split*(s: string, sep: Peg): string = if first < last: yield substr(s, first, last-1) -proc split*(s: string, sep: Peg): seq[string] {. - noSideEffect, rtl, extern: "npegs$1".} = +func split*(s: string, sep: Peg): seq[string] {. + rtl, extern: "npegs$1".} = ## Splits the string `s` into substrings. result = @[] for it in split(s, sep): result.add it @@ -1445,20 +1449,20 @@ const "@", "built-in", "escaped", "$", "$", "^" ] -proc handleCR(L: var PegLexer, pos: int): int = +func handleCR(L: var PegLexer, pos: int): int = assert(L.buf[pos] == '\c') inc(L.lineNumber) result = pos+1 if result < L.buf.len and L.buf[result] == '\L': inc(result) L.lineStart = result -proc handleLF(L: var PegLexer, pos: int): int = +func handleLF(L: var PegLexer, pos: int): int = assert(L.buf[pos] == '\L') inc(L.lineNumber) result = pos+1 L.lineStart = result -proc init(L: var PegLexer, input, filename: string, line = 1, col = 0) = +func init(L: var PegLexer, input, filename: string, line = 1, col = 0) = L.buf = input L.bufpos = 0 L.lineNumber = line @@ -1466,18 +1470,18 @@ proc init(L: var PegLexer, input, filename: string, line = 1, col = 0) = L.lineStart = 0 L.filename = filename -proc getColumn(L: PegLexer): int {.inline.} = +func getColumn(L: PegLexer): int {.inline.} = result = abs(L.bufpos - L.lineStart) + L.colOffset -proc getLine(L: PegLexer): int {.inline.} = +func getLine(L: PegLexer): int {.inline.} = result = L.lineNumber -proc errorStr(L: PegLexer, msg: string, line = -1, col = -1): string = +func errorStr(L: PegLexer, msg: string, line = -1, col = -1): string = var line = if line < 0: getLine(L) else: line var col = if col < 0: getColumn(L) else: col result = "$1($2, $3) Error: $4" % [L.filename, $line, $col, msg] -proc getEscapedChar(c: var PegLexer, tok: var Token) = +func getEscapedChar(c: var PegLexer, tok: var Token) = inc(c.bufpos) if c.bufpos >= len(c.buf): tok.kind = tkInvalid @@ -1537,7 +1541,7 @@ proc getEscapedChar(c: var PegLexer, tok: var Token) = add(tok.literal, c.buf[c.bufpos]) inc(c.bufpos) -proc skip(c: var PegLexer) = +func skip(c: var PegLexer) = var pos = c.bufpos while pos < c.buf.len: case c.buf[pos] @@ -1554,7 +1558,7 @@ proc skip(c: var PegLexer) = break # EndOfFile also leaves the loop c.bufpos = pos -proc getString(c: var PegLexer, tok: var Token) = +func getString(c: var PegLexer, tok: var Token) = tok.kind = tkStringLit var pos = c.bufpos + 1 var quote = c.buf[pos-1] @@ -1575,7 +1579,7 @@ proc getString(c: var PegLexer, tok: var Token) = inc(pos) c.bufpos = pos -proc getDollar(c: var PegLexer, tok: var Token) = +func getDollar(c: var PegLexer, tok: var Token) = var pos = c.bufpos + 1 var neg = false if pos < c.buf.len and c.buf[pos] == '^': @@ -1595,7 +1599,7 @@ proc getDollar(c: var PegLexer, tok: var Token) = tok.kind = tkDollar c.bufpos = pos -proc getCharSet(c: var PegLexer, tok: var Token) = +func getCharSet(c: var PegLexer, tok: var Token) = tok.kind = tkCharSet tok.charset = {} var pos = c.bufpos + 1 @@ -1652,7 +1656,7 @@ proc getCharSet(c: var PegLexer, tok: var Token) = c.bufpos = pos if caret: tok.charset = {'\1'..'\xFF'} - tok.charset -proc getSymbol(c: var PegLexer, tok: var Token) = +func getSymbol(c: var PegLexer, tok: var Token) = var pos = c.bufpos while pos < c.buf.len: add(tok.literal, c.buf[pos]) @@ -1661,7 +1665,7 @@ proc getSymbol(c: var PegLexer, tok: var Token) = c.bufpos = pos tok.kind = tkIdentifier -proc getBuiltin(c: var PegLexer, tok: var Token) = +func getBuiltin(c: var PegLexer, tok: var Token) = if c.bufpos+1 < c.buf.len and c.buf[c.bufpos+1] in strutils.Letters: inc(c.bufpos) getSymbol(c, tok) @@ -1670,7 +1674,7 @@ proc getBuiltin(c: var PegLexer, tok: var Token) = tok.kind = tkEscaped getEscapedChar(c, tok) # may set tok.kind to tkInvalid -proc getTok(c: var PegLexer, tok: var Token) = +func getTok(c: var PegLexer, tok: var Token) = tok.kind = tkInvalid tok.modifier = modNone setLen(tok.literal, 0) @@ -1792,7 +1796,7 @@ proc getTok(c: var PegLexer, tok: var Token) = add(tok.literal, c.buf[c.bufpos]) inc(c.bufpos) -proc arrowIsNextTok(c: PegLexer): bool = +func arrowIsNextTok(c: PegLexer): bool = # the only look ahead we need var pos = c.bufpos while pos < c.buf.len and c.buf[pos] in {'\t', ' '}: inc(pos) @@ -1813,23 +1817,23 @@ type identIsVerbatim: bool skip: Peg -proc pegError(p: PegParser, msg: string, line = -1, col = -1) = +func pegError(p: PegParser, msg: string, line = -1, col = -1) = var e: ref EInvalidPeg new(e) e.msg = errorStr(p, msg, line, col) raise e -proc getTok(p: var PegParser) = +func getTok(p: var PegParser) = getTok(p, p.tok) if p.tok.kind == tkInvalid: pegError(p, "'" & p.tok.literal & "' is invalid token") -proc eat(p: var PegParser, kind: TokKind) = +func eat(p: var PegParser, kind: TokKind) = if p.tok.kind == kind: getTok(p) else: pegError(p, tokKindToStr[kind] & " expected") -proc parseExpr(p: var PegParser): Peg {.gcsafe.} +func parseExpr(p: var PegParser): Peg {.gcsafe.} -proc getNonTerminal(p: var PegParser, name: string): NonTerminal = +func getNonTerminal(p: var PegParser, name: string): NonTerminal = for i in 0..high(p.nonterms): result = p.nonterms[i] if cmpIgnoreStyle(result.name, name) == 0: return @@ -1837,13 +1841,13 @@ proc getNonTerminal(p: var PegParser, name: string): NonTerminal = result = newNonTerminal(name, getLine(p), getColumn(p)) add(p.nonterms, result) -proc modifiedTerm(s: string, m: Modifier): Peg = +func modifiedTerm(s: string, m: Modifier): Peg = case m of modNone, modVerbatim: result = term(s) of modIgnoreCase: result = termIgnoreCase(s) of modIgnoreStyle: result = termIgnoreStyle(s) -proc modifiedBackref(s: int, m: Modifier): Peg = +func modifiedBackref(s: int, m: Modifier): Peg = var reverse = s < 0 index = if reverse: -s else: s @@ -1852,7 +1856,7 @@ proc modifiedBackref(s: int, m: Modifier): Peg = of modIgnoreCase: result = backrefIgnoreCase(index, reverse) of modIgnoreStyle: result = backrefIgnoreStyle(index, reverse) -proc builtin(p: var PegParser): Peg = +func builtin(p: var PegParser): Peg = # do not use "y", "skip" or "i" as these would be ambiguous case p.tok.literal of "n": result = newLine() @@ -1872,11 +1876,11 @@ proc builtin(p: var PegParser): Peg = of "white": result = unicodeWhitespace() else: pegError(p, "unknown built-in: " & p.tok.literal) -proc token(terminal: Peg, p: PegParser): Peg = +func token(terminal: Peg, p: PegParser): Peg = if p.skip.kind == pkEmpty: result = terminal else: result = sequence(p.skip, terminal) -proc primary(p: var PegParser): Peg = +func primary(p: var PegParser): Peg = case p.tok.kind of tkAmp: getTok(p) @@ -1968,7 +1972,7 @@ proc primary(p: var PegParser): Peg = getTok(p) else: break -proc seqExpr(p: var PegParser): Peg = +func seqExpr(p: var PegParser): Peg = result = primary(p) while true: case p.tok.kind @@ -1982,13 +1986,13 @@ proc seqExpr(p: var PegParser): Peg = else: break else: break -proc parseExpr(p: var PegParser): Peg = +func parseExpr(p: var PegParser): Peg = result = seqExpr(p) while p.tok.kind == tkBar: getTok(p) result = result / seqExpr(p) -proc parseRule(p: var PegParser): NonTerminal = +func parseRule(p: var PegParser): NonTerminal = if p.tok.kind == tkIdentifier and arrowIsNextTok(p): result = getNonTerminal(p, p.tok.literal) if ntDeclared in result.flags: @@ -2002,7 +2006,7 @@ proc parseRule(p: var PegParser): NonTerminal = else: pegError(p, "rule expected, but found: " & p.tok.literal) -proc rawParse(p: var PegParser): Peg = +func rawParse(p: var PegParser): Peg = ## parses a rule or a PEG expression while p.tok.kind == tkBuiltin: case p.tok.literal @@ -2032,7 +2036,7 @@ proc rawParse(p: var PegParser): Peg = elif ntUsed notin nt.flags and i > 0: pegError(p, "unused rule: " & nt.name, nt.line, nt.col) -proc parsePeg*(pattern: string, filename = "pattern", line = 1, col = 0): Peg = +func parsePeg*(pattern: string, filename = "pattern", line = 1, col = 0): Peg = ## constructs a Peg object from `pattern`. `filename`, `line`, `col` are ## used for error messages, but they only provide start offsets. `parsePeg` ## keeps track of line and column numbers within `pattern`. @@ -2047,14 +2051,14 @@ proc parsePeg*(pattern: string, filename = "pattern", line = 1, col = 0): Peg = getTok(p) result = rawParse(p) -proc peg*(pattern: string): Peg = +func peg*(pattern: string): Peg = ## constructs a Peg object from the `pattern`. The short name has been ## chosen to encourage its use as a raw string modifier:: ## ## peg"{\ident} \s* '=' \s* {.*}" result = parsePeg(pattern, "pattern") -proc escapePeg*(s: string): string = +func escapePeg*(s: string): string = ## escapes `s` so that it is matched verbatim when used as a peg. result = "" var inQuote = false diff --git a/tests/effects/tstrict_funcs_imports.nim b/tests/effects/tstrict_funcs_imports.nim index 4e9b9fe66..264771a1a 100644 --- a/tests/effects/tstrict_funcs_imports.nim +++ b/tests/effects/tstrict_funcs_imports.nim @@ -84,7 +84,7 @@ import parseutils, parsexml, pathnorm, - # pegs, + pegs, posix_utils, prelude, punycode, diff --git a/tools/kochdocs.nim b/tools/kochdocs.nim index fe3335c0f..0c5c8f4a3 100644 --- a/tools/kochdocs.nim +++ b/tools/kochdocs.nim @@ -1,6 +1,9 @@ ## Part of 'koch' responsible for the documentation generation. -import os, strutils, osproc, sets, pathnorm, pegs, sequtils +import os, strutils, osproc, sets, pathnorm, sequtils +# XXX: Remove this feature check once the csources supports it. +when defined(nimHasCastPragmaBlocks): + import std/pegs from std/private/globs import nativeToUnixPath, walkDirRecFilter, PathEntry import "../compiler/nimpaths" @@ -338,7 +341,9 @@ proc buildDocs*(args: string, localOnly = false, localOutDir = "") = if not localOnly: buildDocsDir(args, webUploadOutput / NimVersion) - let gaFilter = peg"@( y'--doc.googleAnalytics:' @(\s / $) )" - args = args.replace(gaFilter) + # XXX: Remove this feature check once the csources supports it. + when defined(nimHasCastPragmaBlocks): + let gaFilter = peg"@( y'--doc.googleAnalytics:' @(\s / $) )" + args = args.replace(gaFilter) buildDocsDir(args, localOutDir) From 6f15af41a7ea7aeb09b9c9b9c1d7ca3b505107c9 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 7 Oct 2021 15:07:24 +0200 Subject: [PATCH 2575/3768] fixes a regression caused by overloadable enums even though they're opt-in (#18970) --- compiler/semtempl.nim | 10 +++++++--- tests/enum/mregression.nim | 4 ++++ tests/enum/tregression.nim | 13 +++++++++++++ 3 files changed, 24 insertions(+), 3 deletions(-) create mode 100644 tests/enum/mregression.nim create mode 100644 tests/enum/tregression.nim diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index c10917bcb..49ce55a64 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -250,7 +250,7 @@ proc semTemplSymbol(c: PContext, n: PNode, s: PSym; isField: bool): PNode = of skUnknown: # Introduced in this pass! Leave it as an identifier. result = n - of OverloadableSyms: + of OverloadableSyms-{skEnumField}: result = symChoice(c, n, s, scOpen, isField) of skGenericParam: if isField and sfGenSym in s.flags: result = n @@ -261,8 +261,12 @@ proc semTemplSymbol(c: PContext, n: PNode, s: PSym; isField: bool): PNode = if isField and sfGenSym in s.flags: result = n else: result = newSymNodeTypeDesc(s, c.idgen, n.info) else: - if isField and sfGenSym in s.flags: result = n - else: result = newSymNode(s, n.info) + if s.kind == skEnumField and overloadableEnums in c.features: + result = symChoice(c, n, s, scOpen, isField) + elif isField and sfGenSym in s.flags: + result = n + else: + result = newSymNode(s, n.info) # Issue #12832 when defined(nimsuggest): suggestSym(c.graph, n.info, s, c.graph.usageSym, false) diff --git a/tests/enum/mregression.nim b/tests/enum/mregression.nim new file mode 100644 index 000000000..9c3e3cf8b --- /dev/null +++ b/tests/enum/mregression.nim @@ -0,0 +1,4 @@ + +type + OtherEnum* = enum + Success, Failed, More diff --git a/tests/enum/tregression.nim b/tests/enum/tregression.nim new file mode 100644 index 000000000..597f0c80a --- /dev/null +++ b/tests/enum/tregression.nim @@ -0,0 +1,13 @@ +discard """ + action: "compile" +""" +import options, mregression + +type + MyEnum = enum + Success + +template t = + echo some(Success) + +t() From 08107c2192e6aebb386df2711f2935731ee599fd Mon Sep 17 00:00:00 2001 From: flywind Date: Fri, 8 Oct 2021 00:54:21 +0800 Subject: [PATCH 2576/3768] [minor] give more friendly description (#18973) --- nimdoc/tester.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nimdoc/tester.nim b/nimdoc/tester.nim index 1e438b97b..32dce0709 100644 --- a/nimdoc/tester.nim +++ b/nimdoc/tester.nim @@ -80,4 +80,4 @@ let testNimDoc(test2Dir, test2DocsDir, test2Switches, fixup) if failures > 0: - quit "$# failures occurred; see note in tester.nim regarding -d:nimTestsNimdocFixup" % $failures + quit "$# failures occurred; see note in nimdoc/tester.nim regarding -d:nimTestsNimdocFixup" % $failures From 787def271b1cabc6f898caa42f892125de9fa908 Mon Sep 17 00:00:00 2001 From: antonl05 <88133270+antonl05@users.noreply.github.com> Date: Sat, 9 Oct 2021 14:17:07 +0530 Subject: [PATCH 2577/3768] add OpenIndiana to list (#18972) * add OpenIndiana support * point sunos to solaris --- tools/niminst/buildsh.nimf | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tools/niminst/buildsh.nimf b/tools/niminst/buildsh.nimf index 9850abd89..5f68a6a17 100644 --- a/tools/niminst/buildsh.nimf +++ b/tools/niminst/buildsh.nimf @@ -139,6 +139,11 @@ case $uos in myos="solaris" LINK_FLAGS="$LINK_FLAGS -ldl -lm -lsocket -lnsl" ;; + *SunOS* ) + myos="solaris" + LINK_FLAGS="$LINK_FLAGS -ldl -lm -lsocket -lnsl" + isOpenIndiana="yes" + ;; *haiku* ) myos="haiku" LINK_FLAGS="$LINK_FLAGS -lroot -lnetwork" @@ -159,7 +164,12 @@ esac case $ucpu in *i386* | *i486* | *i586* | *i686* | *bepc* | *i86pc* ) - mycpu="i386" ;; + if [ isOpenIndiana -eq "yes" ] ; then + mycpu="amd64" + else + mycpu="i386" + fi + ;; *amd*64* | *x86-64* | *x86_64* ) mycpu="amd64" ;; *sparc*|*sun* ) From 83128f217f63045974a48e61b65386abbfc97352 Mon Sep 17 00:00:00 2001 From: narimiran Date: Mon, 11 Oct 2021 14:38:59 +0200 Subject: [PATCH 2578/3768] disable testing of `fidget`, to make CIs green --- testament/important_packages.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 0c47104ac..e037cc609 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -63,7 +63,7 @@ pkg "delaunay" pkg "docopt" pkg "easygl", "nim c -o:egl -r src/easygl.nim", "https://github.com/jackmott/easygl" pkg "elvis" -pkg "fidget" +pkg "fidget", allowFailure = true pkg "fragments", "nim c -r fragments/dsl.nim" pkg "fusion" pkg "gara" From 0ae2d1ea88fdc7b6910ddbc960c7db086be383e4 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Tue, 12 Oct 2021 06:41:30 -0700 Subject: [PATCH 2579/3768] Improve error message when NodeJS is not installed and 'nim js -r' is run (#18978) --- compiler/nodejs.nim | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/nodejs.nim b/compiler/nodejs.nim index 781035bb7..283643e8d 100644 --- a/compiler/nodejs.nim +++ b/compiler/nodejs.nim @@ -3,5 +3,8 @@ import os proc findNodeJs*(): string {.inline.} = ## Find NodeJS executable and return it as a string. result = findExe("nodejs") - if result == "": + if result.len == 0: result = findExe("node") + if result.len == 0: + echo "Please install NodeJS first, see https://nodejs.org/en/download" + raise newException(IOError, "NodeJS not found in PATH: " & result) From 2aa97a228aa3a34b48f1af9901f29379fb131a31 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Wed, 13 Oct 2021 10:09:45 +0100 Subject: [PATCH 2580/3768] Removes deprecated {.injectStmt.}. Fixes #18666 (#18984) --- compiler/ccgcalls.nim | 1 - compiler/ccgstmts.nim | 5 ---- compiler/cgen.nim | 3 --- compiler/cgendata.nim | 1 - compiler/pragmas.nim | 8 +----- compiler/wordrecg.nim | 2 +- lib/system/gc_ms.nim | 10 ------- tests/pragmas/tinjectstmt.nim | 51 ----------------------------------- 8 files changed, 2 insertions(+), 79 deletions(-) delete mode 100644 tests/pragmas/tinjectstmt.nim diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index f6cc6da74..12f366a53 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -801,6 +801,5 @@ proc genAsgnCall(p: BProc, le, ri: PNode, d: var TLoc) = genNamedParamCall(p, ri, d) else: genPrefixCall(p, le, ri, d) - postStmtActions(p) proc genCall(p: BProc, e: PNode, d: var TLoc) = genAsgnCall(p, nil, e, d) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 6cbff6ee9..559849f0d 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -1509,11 +1509,6 @@ proc genPragma(p: BProc, n: PNode) = for it in n.sons: case whichPragma(it) of wEmit: genEmit(p, it) - of wInjectStmt: - var p = newProc(nil, p.module) - p.options.excl {optLineTrace, optStackTrace} - genStmts(p, it[1]) - p.module.injectStmt = p.s(cpsStmts) else: discard diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 43410dc60..412a428da 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -271,9 +271,6 @@ proc genLineDir(p: BProc, t: PNode) = linefmt(p, cpsStmts, "nimln_($1, $2);$n", [line, quotedFilename(p.config, t.info)]) -proc postStmtActions(p: BProc) {.inline.} = - p.s(cpsStmts).add(p.module.injectStmt) - proc accessThreadLocalVar(p: BProc, s: PSym) proc emulatedThreadVars(conf: ConfigRef): bool {.inline.} proc genProc(m: BModule, prc: PSym) diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index 3678adacf..88b8d4090 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -170,7 +170,6 @@ type labels*: Natural # for generating unique module-scope names extensionLoaders*: array['0'..'9', Rope] # special procs for the # OpenGL wrapper - injectStmt*: Rope sigConflicts*: CountTable[SigHash] g*: BModuleList ndi*: NdiFile diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index e9f52c71f..a6e6131d7 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -55,7 +55,7 @@ const wDeprecated, wFloatChecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll, wLinearScanEnd, wPatterns, wTrMacros, wEffects, wNoForward, wReorder, wComputedGoto, - wInjectStmt, wExperimental, wThis, wUsed, wInvariant, wAssume, wAssert} + wExperimental, wThis, wUsed, wInvariant, wAssume, wAssert} lambdaPragmas* = {FirstCallConv..LastCallConv, wNoSideEffect, wSideEffect, wNoreturn, wNosinks, wDynlib, wHeader, wThread, wAsmNoStackFrame, @@ -1204,12 +1204,6 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of wExportNims: if sym == nil: invalidPragma(c, it) else: magicsys.registerNimScriptSymbol(c.graph, sym) - of wInjectStmt: - warningDeprecated(c.config, it.info, "'.injectStmt' pragma is deprecated") - if it.kind notin nkPragmaCallKinds or it.len != 2: - localError(c.config, it.info, "expression expected") - else: - it[1] = c.semExpr(c, it[1]) of wExperimental: if not isTopLevel(c): localError(c.config, n.info, "'experimental' pragma only valid as toplevel statement or in a 'push' environment") diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index 4ba807518..ce06effab 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -80,7 +80,7 @@ type wLocalPassc = "localPassC", wBorrow = "borrow", wDiscardable = "discardable", wFieldChecks = "fieldChecks", wSubsChar = "subschar", wAcyclic = "acyclic", wShallow = "shallow", wUnroll = "unroll", wLinearScanEnd = "linearScanEnd", - wComputedGoto = "computedGoto", wInjectStmt = "injectStmt", wExperimental = "experimental", + wComputedGoto = "computedGoto", wExperimental = "experimental", wWrite = "write", wGensym = "gensym", wInject = "inject", wDirty = "dirty", wInheritable = "inheritable", wThreadVar = "threadvar", wEmit = "emit", wAsmNoStackFrame = "asmNoStackFrame", wImplicitStatic = "implicitStatic", diff --git a/lib/system/gc_ms.nim b/lib/system/gc_ms.nim index 9e306c497..0675b9f2e 100644 --- a/lib/system/gc_ms.nim +++ b/lib/system/gc_ms.nim @@ -428,16 +428,6 @@ proc sweep(gch: var GcHeap) = if c.refcount == rcBlack: c.refcount = rcWhite else: freeCyclicCell(gch, c) -when false: - # meant to be used with the now-deprected `.injectStmt`: {.injectStmt: newGcInvariant().} - proc newGcInvariant*() = - for x in allObjects(gch.region): - if isCell(x): - var c = cast[PCell](x) - if c.typ == nil: - writeStackTrace() - quit 1 - proc markGlobals(gch: var GcHeap) = if gch.gcThreadId == 0: when defined(nimTracing): diff --git a/tests/pragmas/tinjectstmt.nim b/tests/pragmas/tinjectstmt.nim deleted file mode 100644 index c6256bda6..000000000 --- a/tests/pragmas/tinjectstmt.nim +++ /dev/null @@ -1,51 +0,0 @@ -discard """ - joinable: false - output:''' -onInject: 1 -onInject: 2 -ok0 -ok1 -onInject: 3 -onInject: 4 -onInject: 5 -0 -onInject: 6 -onInject: 7 -onInject: 8 -1 -onInject: 9 -onInject: 10 -onInject: 11 -2 -ok2 -onInject: 12 -''' -""" - -# test {.injectStmt.} - -#[ -{.injectStmt.} pragma can be used to inject a statement before every -other statement in the current module. It's now undocumented and may be removed -in the future and replaced with something more general and without its limitations. -(e.g. doesn't work in VM or js backends). -]# - -from system/ansi_c import c_printf - -var count = 0 -proc onInject*() = - count.inc - # echo count # xxx would fail, probably infinite recursion - c_printf("onInject: %d\n", cast[int](count)) - -{.injectStmt: onInject().} -echo "ok0" -proc main()= - echo "ok1" - for a in 0..<3: - echo a - echo "ok2" - -static: main() # xxx injectStmt not honored in VM -main() From e645be4d0c6c53cd9483d4fde24c4ddc7bd1bf96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9E=97=E4=BA=A6=E6=81=A9?= Date: Wed, 13 Oct 2021 17:35:47 +0800 Subject: [PATCH 2581/3768] add ghci like type annotation buildEchoStmt (1049) (#18875) * add ghci like type annotation buildEchoStmt (1049) * Update compiler/semexprs.nim * Update compiler/semexprs.nim Co-authored-by: flywind Co-authored-by: Andreas Rumpf Co-authored-by: flywind --- compiler/semexprs.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 342b8b05f..c11cecfa9 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1057,6 +1057,7 @@ proc buildEchoStmt(c: PContext, n: PNode): PNode = else: result.add localErrorNode(c, n, "system needs: echo") result.add(n) + result.add(newStrNode(nkStrLit, ": " & n.typ.typeToString)) result = semExpr(c, result) proc semExprNoType(c: PContext, n: PNode): PNode = From 2ac3ba713b61e4e6b70c98da59dc05925f8b5e6b Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 13 Oct 2021 20:57:25 +0800 Subject: [PATCH 2582/3768] fix #18985 (#18988) --- compiler/semtypes.nim | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 9f7a17a53..d03fa88a8 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -2076,7 +2076,10 @@ proc processMagicType(c: PContext, m: PSym) = setMagicType(c.config, m, tySequence, szUncomputedSize) if optSeqDestructors in c.config.globalOptions: incl m.typ.flags, tfHasAsgn - assert c.graph.sysTypes[tySequence] == nil + if defined(nimsuggest) or c.config.cmd == cmdCheck: # bug #18985 + discard + else: + assert c.graph.sysTypes[tySequence] == nil c.graph.sysTypes[tySequence] = m.typ of mOrdinal: setMagicIntegral(c.config, m, tyOrdinal, szUncomputedSize) From e2b19bb2deba0932cf324c0127ffa7f2cbed8b5d Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 13 Oct 2021 20:58:31 +0800 Subject: [PATCH 2583/3768] remove deprecated stuffs from std/times (#18937) --- lib/pure/times.nim | 50 +++++++++++++++++++++++----------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index b41bb28b5..113f73d2a 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -488,7 +488,7 @@ proc fromEpochDay(epochday: int64): proc getDayOfYear*(monthday: MonthdayRange, month: Month, year: int): YeardayRange {.tags: [], raises: [], benign.} = ## Returns the day of the year. - ## Equivalent with `initDateTime(monthday, month, year, 0, 0, 0).yearday`. + ## Equivalent with `dateTime(year, month, monthday, 0, 0, 0, 0).yearday`. runnableExamples: doAssert getDayOfYear(1, mJan, 2000) == 0 doAssert getDayOfYear(10, mJan, 2000) == 9 @@ -508,7 +508,7 @@ proc getDayOfYear*(monthday: MonthdayRange, month: Month, year: int): proc getDayOfWeek*(monthday: MonthdayRange, month: Month, year: int): WeekDay {.tags: [], raises: [], benign.} = ## Returns the day of the week enum from day, month and year. - ## Equivalent with `initDateTime(monthday, month, year, 0, 0, 0).weekday`. + ## Equivalent with `dateTime(year, month, monthday, 0, 0, 0, 0).weekday`. runnableExamples: doAssert getDayOfWeek(13, mJun, 1990) == dWed doAssert $getDayOfWeek(13, mJun, 1990) == "Wednesday" @@ -1060,13 +1060,13 @@ proc isLeapDay*(dt: DateTime): bool {.since: (1, 1).} = ## Returns whether `t` is a leap day, i.e. Feb 29 in a leap year. This matters ## as it affects time offset calculations. runnableExamples: - let dt = initDateTime(29, mFeb, 2020, 00, 00, 00, utc()) + let dt = dateTime(2020, mFeb, 29, 00, 00, 00, 00, utc()) doAssert dt.isLeapDay doAssert dt+1.years-1.years != dt - let dt2 = initDateTime(28, mFeb, 2020, 00, 00, 00, utc()) + let dt2 = dateTime(2020, mFeb, 28, 00, 00, 00, 00, utc()) doAssert not dt2.isLeapDay doAssert dt2+1.years-1.years == dt2 - doAssertRaises(Exception): discard initDateTime(29, mFeb, 2021, 00, 00, 00, utc()) + doAssertRaises(Exception): discard dateTime(2021, mFeb, 29, 00, 00, 00, 00, utc()) assertDateTimeInitialized dt dt.year.isLeapYear and dt.month == mFeb and dt.monthday == 29 @@ -1372,7 +1372,7 @@ proc initDateTime*(monthday: MonthdayRange, month: Month, year: int, proc `+`*(dt: DateTime, dur: Duration): DateTime = runnableExamples: - let dt = initDateTime(30, mMar, 2017, 00, 00, 00, utc()) + let dt = dateTime(2017, mMar, 30, 00, 00, 00, 00, utc()) let dur = initDuration(hours = 5) doAssert $(dt + dur) == "2017-03-30T05:00:00Z" @@ -1380,7 +1380,7 @@ proc `+`*(dt: DateTime, dur: Duration): DateTime = proc `-`*(dt: DateTime, dur: Duration): DateTime = runnableExamples: - let dt = initDateTime(30, mMar, 2017, 00, 00, 00, utc()) + let dt = dateTime(2017, mMar, 30, 00, 00, 00, 00, utc()) let dur = initDuration(days = 5) doAssert $(dt - dur) == "2017-03-25T00:00:00Z" @@ -1389,8 +1389,8 @@ proc `-`*(dt: DateTime, dur: Duration): DateTime = proc `-`*(dt1, dt2: DateTime): Duration = ## Compute the duration between `dt1` and `dt2`. runnableExamples: - let dt1 = initDateTime(30, mMar, 2017, 00, 00, 00, utc()) - let dt2 = initDateTime(25, mMar, 2017, 00, 00, 00, utc()) + let dt1 = dateTime(2017, mMar, 30, 00, 00, 00, 00, utc()) + let dt2 = dateTime(2017, mMar, 25, 00, 00, 00, 00, utc()) doAssert dt1 - dt2 == initDuration(days = 5) @@ -1998,10 +1998,10 @@ proc toDateTime(p: ParsedTime, zone: Timezone, f: TimeFormat, if p.utcOffset.isNone: # No timezone parsed - assume timezone is `zone` - result = initDateTime(monthday, month, year, hour, minute, second, nanosecond, zone) + result = dateTime(year, month, monthday, hour, minute, second, nanosecond, zone) else: # Otherwise convert to `zone` - result = (initDateTime(monthday, month, year, hour, minute, second, nanosecond, utc()).toTime + + result = (dateTime(year, month, monthday, hour, minute, second, nanosecond, utc()).toTime + initDuration(seconds = p.utcOffset.get())).inZone(zone) proc format*(dt: DateTime, f: TimeFormat, @@ -2009,7 +2009,7 @@ proc format*(dt: DateTime, f: TimeFormat, ## Format `dt` using the format specified by `f`. runnableExamples: let f = initTimeFormat("yyyy-MM-dd") - let dt = initDateTime(01, mJan, 2000, 00, 00, 00, utc()) + let dt = dateTime(2000, mJan, 01, 00, 00, 00, 00, utc()) doAssert "2000-01-01" == dt.format(f) assertDateTimeInitialized dt result = "" @@ -2034,7 +2034,7 @@ proc format*(dt: DateTime, f: string, loc: DateTimeLocale = DefaultLocale): stri ## See `Parsing and formatting dates`_ for documentation of the ## `format` argument. runnableExamples: - let dt = initDateTime(01, mJan, 2000, 00, 00, 00, utc()) + let dt = dateTime(2000, mJan, 01, 00, 00, 00, 00, utc()) doAssert "2000-01-01" == format(dt, "yyyy-MM-dd") let dtFormat = initTimeFormat(f) result = dt.format(dtFormat, loc) @@ -2057,7 +2057,7 @@ proc format*(time: Time, f: string, zone: Timezone = local()): string ## See `Parsing and formatting dates`_ for documentation of the ## `f` argument. runnableExamples: - var dt = initDateTime(01, mJan, 1970, 00, 00, 00, utc()) + var dt = dateTime(1970, mJan, 01, 00, 00, 00, 00, utc()) var tm = dt.toTime() doAssert format(tm, "yyyy-MM-dd'T'HH:mm:ss", utc()) == "1970-01-01T00:00:00" time.inZone(zone).format(f) @@ -2083,7 +2083,7 @@ proc parse*(input: string, f: TimeFormat, zone: Timezone = local(), ## Month and day names from the passed in `loc` are used. runnableExamples: let f = initTimeFormat("yyyy-MM-dd") - let dt = initDateTime(01, mJan, 2000, 00, 00, 00, utc()) + let dt = dateTime(2000, mJan, 01, 00, 00, 00, 00, utc()) doAssert dt == "2000-01-01".parse(f, utc()) var inpIdx = 0 # Input index var patIdx = 0 # Pattern index @@ -2125,7 +2125,7 @@ proc parse*(input, f: string, tz: Timezone = local(), ## See `Parsing and formatting dates`_ for documentation of the ## `f` argument. runnableExamples: - let dt = initDateTime(01, mJan, 2000, 00, 00, 00, utc()) + let dt = dateTime(2000, mJan, 01, 00, 00, 00, 00, utc()) doAssert dt == parse("2000-01-01", "yyyy-MM-dd", utc()) let dtFormat = initTimeFormat(f) result = input.parse(dtFormat, tz, loc = loc) @@ -2159,7 +2159,7 @@ proc `$`*(dt: DateTime): string {.tags: [], raises: [], benign.} = ## Converts a `DateTime` object to a string representation. ## It uses the format `yyyy-MM-dd'T'HH:mm:sszzz`. runnableExamples: - let dt = initDateTime(01, mJan, 2000, 12, 00, 00, utc()) + let dt = dateTime(2000, mJan, 01, 12, 00, 00, 00, utc()) doAssert $dt == "2000-01-01T12:00:00Z" doAssert $default(DateTime) == "Uninitialized DateTime" if not dt.isInitialized: @@ -2171,7 +2171,7 @@ proc `$`*(time: Time): string {.tags: [], raises: [], benign.} = ## Converts a `Time` value to a string representation. It will use the local ## time zone and use the format `yyyy-MM-dd'T'HH:mm:sszzz`. runnableExamples: - let dt = initDateTime(01, mJan, 1970, 00, 00, 00, local()) + let dt = dateTime(1970, mJan, 01, 00, 00, 00, 00, local()) let tm = dt.toTime() doAssert $tm == "1970-01-01T00:00:00" & format(dt, "zzz") $time.local @@ -2193,7 +2193,7 @@ proc initTimeInterval*(nanoseconds, microseconds, milliseconds, ## `seconds`, `minutes`, `hours`, `days`, `months`, and `years`. runnableExamples: let day = initTimeInterval(hours = 24) - let dt = initDateTime(01, mJan, 2000, 12, 00, 00, utc()) + let dt = dateTime(2000, mJan, 01, 12, 00, 00, 00, utc()) doAssert $(dt + day) == "2000-01-02T12:00:00Z" doAssert initTimeInterval(hours = 24) != initTimeInterval(days = 1) result.nanoseconds = nanoseconds @@ -2279,8 +2279,8 @@ proc between*(startDt, endDt: DateTime): TimeInterval = ## - If `startDt.timezone != endDt.timezone`, then the result will be ## equivalent to `between(startDt.utc, endDt.utc)`. runnableExamples: - var a = initDateTime(25, mMar, 2015, 12, 0, 0, utc()) - var b = initDateTime(1, mApr, 2017, 15, 0, 15, utc()) + var a = dateTime(2015, mMar, 25, 12, 0, 0, 00, utc()) + var b = dateTime(2017, mApr, 1, 15, 0, 15, 00, utc()) var ti = initTimeInterval(years = 2, weeks = 1, hours = 3, seconds = 15) doAssert between(a, b) == ti doAssert between(a, b) == -between(b, a) @@ -2360,8 +2360,8 @@ proc between*(startDt, endDt: DateTime): TimeInterval = startDate = endDate # Handle hours, minutes, seconds, milliseconds, microseconds and nanoseconds - let newStartDt = initDateTime(startDate.monthday, startDate.month.Month, - startDate.year, startDt.hour, startDt.minute, startDt.second, + let newStartDt = dateTime(startDate.year, startDate.month.Month, + startDate.monthday, startDt.hour, startDt.minute, startDt.second, startDt.nanosecond, startDt.timezone) let dur = endDt - newStartDt let parts = toParts(dur) @@ -2512,7 +2512,7 @@ proc `+`*(dt: DateTime, interval: TimeInterval): DateTime = ## So adding one month to `31 October` will result in `31 November`, which ## will overflow and result in `1 December`. runnableExamples: - let dt = initDateTime(30, mMar, 2017, 00, 00, 00, utc()) + let dt = dateTime(2017, mMar, 30, 00, 00, 00, 00, utc()) doAssert $(dt + 1.months) == "2017-04-30T00:00:00Z" # This is correct and happens due to monthday overflow. doAssert $(dt - 1.months) == "2017-03-02T00:00:00Z" @@ -2535,7 +2535,7 @@ proc `-`*(dt: DateTime, interval: TimeInterval): DateTime = ## then the `months` component and so on. The returned `DateTime` will ## have the same timezone as the input. runnableExamples: - let dt = initDateTime(30, mMar, 2017, 00, 00, 00, utc()) + let dt = dateTime(2017, mMar, 30, 00, 00, 00, 00, utc()) doAssert $(dt - 5.days) == "2017-03-25T00:00:00Z" dt + (-interval) From f93bfc0a329f193c8b83062e10140c2fb55a7873 Mon Sep 17 00:00:00 2001 From: Miran Date: Wed, 13 Oct 2021 20:31:04 +0200 Subject: [PATCH 2584/3768] [backport] add v1.6 changelog (#18932) --- changelog.md | 538 +------------------- changelogs/changelog_1_6_0.md | 927 ++++++++++++++++++++++++++++++++++ 2 files changed, 928 insertions(+), 537 deletions(-) create mode 100644 changelogs/changelog_1_6_0.md diff --git a/changelog.md b/changelog.md index 1925878bf..ab63bd8b5 100644 --- a/changelog.md +++ b/changelog.md @@ -1,560 +1,24 @@ -# v1.6.x - yyyy-mm-dd +# v1.8.x - yyyy-mm-dd ## Changes affecting backward compatibility -- Deprecated `std/mersenne`. -- `system.delete` had a most surprising behavior when the index passed to it was out of - bounds (it would delete the last entry then). Compile with `-d:nimStrictDelete` so - that an index error is produced instead. But be aware that your code might depend on - this quirky behavior so a review process is required on your part before you can - use `-d:nimStrictDelete`. To make this review easier, use the `-d:nimAuditDelete` - switch, it pretends that `system.delete` is deprecated so that it is easier to see - where it was used in your code. - - `-d:nimStrictDelete` will become the default in upcoming versions. - - -- `cuchar` is now deprecated as it aliased `char` where arguably it should have aliased `uint8`. - Please use `char` or `uint8` instead. - -- `repr` now doesn't insert trailing newline; previous behavior was very inconsistent, - see #16034. Use `-d:nimLegacyReprWithNewline` for previous behavior. - -- A type conversion from one enum type to another now produces an `[EnumConv]` warning. - You should use `ord` (or `cast`, but the compiler won't help, if you misuse it) instead. - ``` - type A = enum a1, a2 - type B = enum b1, b2 - echo a1.B # produces a warning - echo a1.ord.B # produces no warning - ``` - -- A dangerous implicit conversion to `cstring` now triggers a `[CStringConv]` warning. - This warning will become an error in future versions! Use an explicit conversion - like `cstring(x)` in order to silence the warning. - -- There is a new warning for *any* type conversion to enum that can be enabled via - `.warning[AnyEnumConv]:on` or `--warning:AnyEnumConv:on`. - -- Type mismatch errors now show more context, use `-d:nimLegacyTypeMismatch` for previous - behavior. - -- `math.round` now is rounded "away from zero" in JS backend which is consistent - with other backends. See #9125. Use `-d:nimLegacyJsRound` for previous behavior. - -- Changed the behavior of `uri.decodeQuery` when there are unencoded `=` - characters in the decoded values. Prior versions would raise an error. This is - no longer the case to comply with the HTML spec and other languages - implementations. Old behavior can be obtained with - `-d:nimLegacyParseQueryStrict`. `cgi.decodeData` which uses the same - underlying code is also updated the same way. - -- On POSIX systems, the default signal handlers used for Nim programs (it's - used for printing the stacktrace on fatal signals) will now re-raise the - signal for the OS default handlers to handle. - - This lets the OS perform its default actions, which might include core - dumping (on select signals) and notifying the parent process about the cause - of termination. - -- On POSIX systems, we now ignore `SIGPIPE` signals, use `-d:nimLegacySigpipeHandler` - for previous behavior. - -- `hashes.hash` can now support `object` and `ref` (can be overloaded in user code), - if `-d:nimPreviewHashRef` is used. It is expected that this behavior - becomes the new default in upcoming versions. - -- `hashes.hash(proc|ptr|ref|pointer)` now calls `hash(int)` and honors `-d:nimIntHash1`, - `hashes.hash(closure)` has also been improved. - -- The unary slice `..b` was deprecated, use `0..b` instead. - -- Removed `.travis.yml`, `appveyor.yml.disabled`, `.github/workflows/ci.yml.disabled`. - -- `random.initRand(seed)` now produces non-skewed values for the 1st call to `rand()` after - initialization with a small (< 30000) seed. Use `-d:nimLegacyRandomInitRand` to restore - previous behavior for a transition time, see PR #17467. - -- With `-d:nimPreviewJsonutilsHoleyEnum`, `jsonutils` now can serialize/deserialize holey enums as regular enums (via `ord`) instead of as strings. It is expected that this behavior becomes the new default in upcoming versions. `toJson` now serializes `JsonNode` - as is via reference (without a deep copy) instead of treating `JsonNode` as a regular ref object, - this can be customized via `jsonNodeMode`. - -- `json` and `jsonutils` now serialize NaN, Inf, -Inf as strings, so that - `%[NaN, -Inf]` is the string `["nan","-inf"]` instead of `[nan,-inf]` which was invalid json. - - -- `strformat` is now part of `include std/prelude`. - -- Deprecated `proc reversed*[T](a: openArray[T], first: Natural, last: int): seq[T]` in `std/algorithm`. - -- In `std/macros`, `treeRepr,lispRepr,astGenRepr` now represent SymChoice nodes in a collapsed way, - use `-d:nimLegacyMacrosCollapseSymChoice` to get previous behavior. - -- The configuration subsystem now allows for `-d:release` and `-d:danger` to work as expected. - The downside is that these defines now have custom logic that doesn't apply for - other defines. - -- `std/os`: `putEnv` now raises if the 1st argument contains a `=` - -- Renamed `-d:nimCompilerStackraceHints` to `-d:nimCompilerStacktraceHints`. - -- In `std/dom`, `Interval` is now a `ref object`, same as `Timeout`. Definitions of `setTimeout`, - `clearTimeout`, `setInterval`, `clearInterval` were updated. - -- With `-d:nimPreviewDotLikeOps`, dot-like operators (operators starting with `.`, but not with `..`) - now have the same precedence as `.`, so that `a.?b.c` is now parsed as `(a.?b).c` instead of `a.?(b.c)`. - A warning is generated when a dot-like operator is used without `-d:nimPreviewDotLikeOps`. - -- The allocator for Nintendo Switch, which was nonfunctional because - of breaking changes in libnx, was removed, in favour of the new `-d:nimAllocPagesViaMalloc` option. - -- `net.parseIpAddress` now only allows IPv4 addresses in strict form as defined - in [RFC 6943](https://www.rfc-editor.org/rfc/rfc6943#section-3.1.1). - Specifically, octal numbers in IPv4 addresses are no longer accepted (before - they were parsed as decimal numbers). ## Standard library additions and changes -- `strformat`: - - added support for parenthesized expressions. - - added support for const string's instead of just string literals - -- `os.copyFile` is now 2.5x faster on OSX, by using `copyfile` from `copyfile.h`; - use `-d:nimLegacyCopyFile` for OSX < 10.5. - -- `system.addFloat` and `system.$` now can produce string representations of floating point numbers - that are minimal in size and that "roundtrip" (via the "Dragonbox" algorithm). This currently has - to be enabled via `-d:nimPreviewFloatRoundtrip`. It is expected that this behavior becomes the new default - in upcoming versions, as with other `nimPreviewX` define flags. - -- Fixed buffer overflow bugs in `net`. - -- Exported `sslHandle` from `net` and `asyncnet`. - -- Added `sections` iterator in `parsecfg`. - -- Make custom op in macros.quote work for all statements. - -- On Windows the SSL library now checks for valid certificates. - It uses the `cacert.pem` file for this purpose which was extracted - from `https://curl.se/ca/cacert.pem`. Besides - the OpenSSL DLLs (e.g. libssl-1_1-x64.dll, libcrypto-1_1-x64.dll) you - now also need to ship `cacert.pem` with your `.exe` file. - -- `typetraits`: - `distinctBase` now is identity instead of error for non distinct types. - Added `enumLen` to return the number of elements in an enum. - Added `HoleyEnum` for enums with holes, `OrdinalEnum` for enums without holes. - Added `hasClosure`. - Added `pointerBase` to return `T` for `ref T | ptr T`. - -- `prelude` now works with the JavaScript target. - Added `sequtils` import to `prelude`. - `prelude` can now be used via `include std/prelude`, but `include prelude` still works. - -- Added `almostEqual` in `math` for comparing two float values using a machine epsilon. - -- Added `clamp` in `math` which allows using a `Slice` to clamp to a value. - -- Added `ceilDiv` in `math` for round up integer division. - -- The JSON module can now handle integer literals and floating point literals of - arbitrary length and precision. - Numbers that do not fit the underlying `BiggestInt` or `BiggestFloat` fields are - kept as string literals and one can use external BigNum libraries to handle these. - The `parseFloat` family of functions also has now optional `rawIntegers` and - `rawFloats` parameters that can be used to enforce that all integer or float - literals remain in the "raw" string form so that client code can easily treat - small and large numbers uniformly. - -- Added `BackwardsIndex` overload for `JsonNode`. - -- `json.%`,`json.to`, `jsonutils.formJson`,`jsonutils.toJson` now work with `uint|uint64` - instead of raising (as in 1.4) or giving wrong results (as in 1.2). - -- `jsonutils` now handles `cstring` (including as Table key), and `set`. - -- added `jsonutils.jsonTo` overload with `opt = Joptions()` param. - -- `jsonutils.toJson` now supports customization via `ToJsonOptions`. - -- Added an overload for the `collect` macro that inferes the container type based - on the syntax of the last expression. Works with std seqs, tables and sets. - -- Added `randState` template that exposes the default random number generator. - Useful for library authors. - -- Added `random.initRand()` overload with no argument which uses the current time as a seed. - -- `random.initRand(seed)` now allows `seed == 0`. - -- Added `std/sysrand` module to get random numbers from a secure source - provided by the operating system. - -- Added `std/enumutils` module. Added `genEnumCaseStmt` macro that generates - case statement to parse string to enum. -- Added `items` for enums with holes. -- Added `symbolName` to return the enum symbol name ignoring the human readable name. -- Added `symbolRank` to return the index in which an enum member is listed in an enum. - -- Removed deprecated `iup` module from stdlib, it has already moved to - [nimble](https://github.com/nim-lang/iup). - -- various functions in `httpclient` now accept `url` of type `Uri`. Moreover `request` function's - `httpMethod` argument of type `string` was deprecated in favor of `HttpMethod` enum type. - -- `nodejs` backend now supports osenv: `getEnv`, `putEnv`, `envPairs`, `delEnv`, `existsEnv`. - -- Added `cmpMem` to `system`. - -- `doAssertRaises` now correctly handles foreign exceptions. - -- Added `asyncdispatch.activeDescriptors` that returns the number of currently - active async event handles/file descriptors. - -- Added `getPort` to `asynchttpserver`. - -- `--gc:orc` is now 10% faster than previously for common workloads. If - you have trouble with its changed behavior, compile with `-d:nimOldOrc`. - -- `os.FileInfo` (returned by `getFileInfo`) now contains `blockSize`, - determining preferred I/O block size for this file object. - -- Added `os.getCacheDir()` to return platform specific cache directory. - -- Added a simpler to use `io.readChars` overload. - -- Added `**` to jsffi. - -- `writeStackTrace` is available in JS backend now. - -- Added `decodeQuery` to `std/uri`. - -- `strscans.scanf` now supports parsing single characters. - -- `strscans.scanTuple` added which uses `strscans.scanf` internally, - returning a tuple which can be unpacked for easier usage of `scanf`. - -- Added `setutils.toSet` that can take any iterable and convert it to a built-in `set`, - if the iterable yields a built-in settable type. - -- Added `setutils.fullSet` which returns a full built-in `set` for a valid type. - -- Added `setutils.complement` which returns the complement of a built-in `set`. - -- Added `setutils.[]=`. - -- Added `math.isNaN`. - -- Added `jsbigints` module, arbitrary precision integers for JavaScript target. - -- Added `math.copySign`. - -- Added new operations for singly- and doubly linked lists: `lists.toSinglyLinkedList` - and `lists.toDoublyLinkedList` convert from `openArray`s; `lists.copy` implements - shallow copying; `lists.add` concatenates two lists - an O(1) variation that consumes - its argument, `addMoved`, is also supplied. - -- Added `euclDiv` and `euclMod` to `math`. - -- Added `httpcore.is1xx` and missing HTTP codes. - -- Added `jsconsole.jsAssert` for JavaScript target. - -- Added `posix_utils.osReleaseFile` to get system identification from `os-release` file on Linux and the BSDs. - https://www.freedesktop.org/software/systemd/man/os-release.html - -- Added `socketstream` module that wraps sockets in the stream interface - -- Added `sugar.dumpToString` which improves on `sugar.dump`. - -- Added `math.signbit`. - -- Removed the optional `longestMatch` parameter of the `critbits._WithPrefix` iterators (it never worked reliably) - -- In `lists`: renamed `append` to `add` and retained `append` as an alias; - added `prepend` and `prependMoved` analogously to `add` and `addMoved`; - added `remove` for `SinglyLinkedList`s. - -- Deprecated `any`. See https://github.com/nim-lang/RFCs/issues/281 - -- Added optional `options` argument to `copyFile`, `copyFileToDir`, and - `copyFileWithPermissions`. By default, on non-Windows OSes, symlinks are - followed (copy files symlinks point to); on Windows, `options` argument is - ignored and symlinks are skipped. - -- On non-Windows OSes, `copyDir` and `copyDirWithPermissions` copy symlinks as - symlinks (instead of skipping them as it was before); on Windows symlinks are - skipped. - -- On non-Windows OSes, `moveFile` and `moveDir` move symlinks as symlinks - (instead of skipping them sometimes as it was before). - -- Added optional `followSymlinks` argument to `setFilePermissions`. - -- Added `os.isAdmin` to tell whether the caller's process is a member of the - Administrators local group (on Windows) or a root (on POSIX). - -- Added experimental `linenoise.readLineStatus` to get line and status (e.g. ctrl-D or ctrl-C). - -- Added `compilesettings.SingleValueSetting.libPath`. - -- `std/wrapnils` doesn't use `experimental:dotOperators` anymore, avoiding - issues like https://github.com/nim-lang/Nim/issues/13063 (which affected error messages) - for modules importing `std/wrapnils`. - Added `??.` macro which returns an `Option`. - `std/wrapnils` can now be used to protect against `FieldDefect` errors in - case objects, generates optimal code (no overhead compared to manual - if-else branches), and preserves lvalue semantics which allows modifying - an expression. - -- Added `math.frexp` overload procs. Deprecated `c_frexp`, use `frexp` instead. - -- `parseopt.initOptParser` has been made available and `parseopt` has been - added back to `prelude` for all backends. Previously `initOptParser` was - unavailable if the `os` module did not have `paramCount` or `paramStr`, - but the use of these in `initOptParser` were conditionally to the runtime - arguments passed to it, so `initOptParser` has been changed to raise - `ValueError` when the real command line is not available. `parseopt` was - previously excluded from `prelude` for JS, as it could not be imported. - -- Added `system.prepareStrMutation` for better support of low - level `moveMem`, `copyMem` operations for Orc's copy-on-write string - implementation. - -- Added `std/strbasics` for high performance string operations. - Added `strip`, `setSlice`, `add(a: var string, b: openArray[char])`. - -- `std/options` changed `$some(3)` to `"some(3)"` instead of `"Some(3)"` - and `$none(int)` to `"none(int)"` instead of `"None[int]"`. - -- Added `algorithm.merge`. - -- Added `std/jsfetch` module [Fetch](https://developer.mozilla.org/docs/Web/API/Fetch_API) wrapper for JavaScript target. - -- Added `std/jsheaders` module [Headers](https://developer.mozilla.org/en-US/docs/Web/API/Headers) wrapper for JavaScript target. - -- Added `std/jsformdata` module [FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData) wrapper for JavaScript target. - -- `system.addEscapedChar` now renders `\r` as `\r` instead of `\c`, to be compatible - with most other languages. - -- Removed support for named procs in `sugar.=>`. - -- Added `jscore.debugger` to [call any available debugging functionality, such as breakpoints.](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/debugger). - -- Added `htmlgen.portal` for [making "SPA style" pages using HTML only](https://web.dev/hands-on-portals). - -- `std/times`: - Added `ZZZ` and `ZZZZ` patterns to `times.nim` `DateTime` parsing, to match time - zone offsets without colons, e.g. `UTC+7 -> +0700`. - - Added `dateTime` and deprecated `initDateTime`. - -- Added `jsconsole.dir`, `jsconsole.dirxml`, `jsconsole.timeStamp`. - -- Added dollar `$` and `len` for `jsre.RegExp`. - -- Added `std/tasks`. - -- Added `hasDataBuffered` to `asyncnet`. - -- Added `std/tempfiles`. - -- Added `genasts.genAst` that avoids the problems inherent with `quote do` and can - be used as a replacement. - -- Added `copyWithin` [for `seq` and `array` for JavaScript targets](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/copyWithin). - -- Fixed premature garbage collection in asyncdispatch, when a stack trace override is in place. - -- Added setCurrentException for JS backend. - -- Added `dom.scrollIntoView` proc with options - -- Added `dom.setInterval`, `dom.clearInterval` overloads. - -- Merged `dom_extensions` module into `dom` module, - it was a module with a single line, see https://github.com/nim-lang/RFCs/issues/413 - -- Allow reading parameters when compiling for Nintendo Switch. - -- Deprecated `sequtils.delete` and added an overload taking a `Slice` that raises a defect - if the slice is out of bounds, likewise with `strutils.delete`. ## Language changes -- The `cstring` doesn't support `[]=` operator in JS backend. - -- nil dereference is not allowed at compile time. `cast[ptr int](nil)[]` is rejected at compile time. - -- The required name of case statement macros for the experimental - `caseStmtMacros` feature has changed from `match` to `` `case` ``. - -- `typedesc[Foo]` now renders as such instead of `type Foo` in compiler messages. - -- The unary minus in `-1` is now part of the integer literal, it is now parsed as a single token. - This implies that edge cases like `-128'i8` finally work correctly. - -- Custom numeric literals (e.g. `-128'bignum`) are now supported. - -- Tuple expressions are now parsed consistently as - `nnkTupleConstr` node. Will affect macros expecting nodes to be of `nnkPar`. - -- Added `iterable[T]` type class to match called iterators, which enables writing: - `template fn(a: iterable)` instead of `template fn(a: untyped)` - -- A new import syntax `import foo {.all.}` now allows to import all symbols (public or private) - from `foo`. This can be useful for testing purposes. - -- Added a new module `std/importutils`, and an API `privateAccess`, which allows access to private fields - for an object type in the current scope. - -- `typeof(voidStmt)` now works and returns `void`. - -- The `gc:orc` algorithm was refined so that custom container types can participate in the - cycle collection process. See the documentation of `=trace` for more details. - -- On embedded devices `malloc` can now be used instead of `mmap` via `-d:nimAllocPagesViaMalloc`. - This is only supported for `--gc:orc` or `--gc:arc`. - -- The effect system was refined and there is a new `.effectsOf` annotation that does - explicitly what was previously done implicitly. See the manual for details. - To write code that is portable with older Nim versions, use this idiom: - -```nim - -when defined(nimHasEffectsOf): - {.experimental: "strictEffects".} -else: - {.pragma: effectsOf.} - -proc mysort(s: seq; cmp: proc(a, b: T): int) {.effectsOf: cmp.} - -``` - - To enable the new effect system, use --experimental:strictEffects. - - -- Nim now supports a small subset of Unicode operators as operator symbols. - The supported symbols are: "∙ ∘ × ★ ⊗ ⊘ ⊙ ⊛ ⊠ ⊡ ∩ ∧ ⊓ ± ⊕ ⊖ ⊞ ⊟ ∪ ∨ ⊔". - To enable this feature, use `--experimental:unicodeOperators`. Note that due - to parser limitations you **cannot** enable this feature via a - pragma `{.experimental: "unicodeOperators".}` reliably, you need to enable - it via the command line or in a configuration file. - -- There is a new `cast` section `{.cast(uncheckedAssign).}: body` that disables some - compiler checks regarding `case objects`. This allows serialization libraries - to avoid ugly, non-portable solutions. See https://github.com/nim-lang/RFCs/issues/407 - for more details. - -- Enum values can now be overloaded. This needs to be enabled - via `{.experimental: "overloadableEnums".}`. We hope that this feature allows for the - development of more fluent (less ugly) APIs. See https://github.com/nim-lang/RFCs/issues/373 - for more details. ## Compiler changes -- Added `--declaredLocs` to show symbol declaration location in messages. - -- You can now enable/disable VM tracing in user code via `vmutils.vmTrace`. - -- Deprecated `TaintedString` and `--taintmode`. - -- Deprecated `--nilseqs` which is now a noop. - -- Added `--spellSuggest` to show spelling suggestions on typos. - -- Added `--filenames:abs|canonical|legacyRelProj` which replaces --listFullPaths:on|off - -- Added `--processing:dots|filenames|off` which customizes `hintProcessing` - -- Added `--unitsep:on|off` to control whether to add ASCII unit separator `\31` before a newline - for every generated message (potentially multiline), so tooling can tell when messages start and end. - -- Source+Edit links now appear on top of every docgen'd page when - `nim doc --git.url:url ...` is given. - -- Added `nim --eval:cmd` to evaluate a command directly, see `nim --help`. - -- VM now supports `addr(mystring[ind])` (index + index assignment) - -- Added `--hintAsError` with similar semantics as `--warningAsError`. - -- TLS: OSX now uses native TLS (`--tlsEmulation:off`), TLS now works with importcpp non-POD types, - such types must use `.cppNonPod` and `--tlsEmulation:off`should be used. - -- Now array literals(JS backend) uses JS typed arrays when the corresponding js typed array exists, - for example `[byte(1), 2, 3]` generates `new Uint8Array([1, 2, 3])`. - -- docgen: rst files can now use single backticks instead of double backticks and correctly render - in both rst2html (as before) as well as common tools rendering rst directly (e.g. github), by - adding: `default-role:: code` directive inside the rst file, which is now handled by rst2html. - -- Added `-d:nimStrictMode` in CI in several places to ensure code doesn't have certain hints/warnings - -- Added `then`, `catch` to `asyncjs`, for now hidden behind `-d:nimExperimentalAsyncjsThen`. - -- `--newruntime` and `--refchecks` are deprecated. - -- Added `unsafeIsolate` and `extract` to `std/isolation`. - -- `--hint:CC` now goes to stderr (like all other hints) instead of stdout. - -- `--hint:all:on|off` is now supported to select or deselect all hints; it - differs from `--hints:on|off` which acts as a (reversible) gate. - Likewise with `--warning:all:on|off`. - -- json build instructions are now generated in `$nimcache/outFileBasename.json` - instead of `$nimcache/projectName.json`. This allows avoiding recompiling a given project - compiled with different options if the output file differs. - -- `--usenimcache` (implied by `nim r main`) now generates an output file that includes a hash of - some of the compilation options, which allows caching generated binaries: - nim r main # recompiles - nim r -d:foo main # recompiles - nim r main # uses cached binary - nim r main arg1 arg2 # ditto (runtime arguments are irrelevant) - -- `nim r` now supports cross compilation from unix to windows when specifying `-d:mingw` by using wine, - e.g.: `nim r --eval:'import os; echo "a" / "b"'` prints `a\b` - -- `nim` can compile version 1.4.0 as follows: `nim c --lib:lib --stylecheck:off -d:nimVersion140 compiler/nim`. - `-d:nimVersion140` is not needed for bootstrapping, only for building 1.4.0 from devel. - -- The style checking of the compiler now supports a `--styleCheck:usages` switch. This switch - enforces that every symbol is written as it was declared, not enforcing - the official Nim style guide. To be enabled, this has to be combined either - with `--styleCheck:error` or `--styleCheck:hint`. ## Tool changes -- `nimscript` now handles `except Exception as e`. -- `nim e` now accepts arbitrary file extensions for the nimscript file, - although `.nims` is still the preferred extension in general. -- Latex doc generation is revised: output `.tex` files should be compiled - by `xelatex` (not by `pdflatex` as before). Now default Latex settings - provide support for Unicode and do better job for avoiding margin overflows. - -- Implemented `doc2tex` compiler command which converts documentation in - `.nim` files to Latex. - -- The rst parser now supports markdown table syntax. - Known limitations: - - cell alignment is not supported, i.e. alignment annotations in a delimiter - row (`:---`, `:--:`, `---:`) are ignored, - - every table row must start with `|`, e.g. `| cell 1 | cell 2 |`. - -- `fusion` is now un-bundled from nim, `./koch fusion` will - install it via nimble at a fixed hash. - -- testament: added `nimoutFull: bool` spec to compare full output of compiler - instead of a subset. diff --git a/changelogs/changelog_1_6_0.md b/changelogs/changelog_1_6_0.md new file mode 100644 index 000000000..1ce6c2093 --- /dev/null +++ b/changelogs/changelog_1_6_0.md @@ -0,0 +1,927 @@ +# v1.6.0 - 2021-10-14 + + + +# Major new features + +With so many new features, pinpointing the most salient ones is a subjective exercise, +but here are a select few: + + +## `iterable[T]` + +The `iterable[T]` type class was added to match called iterators, +which solves a number of long-standing issues related to iterators. +Example: + +```nim +iterator iota(n: int): int = + for i in 0.. response.json()) + .then((json: JsObject) => console.log(json)) + .catch((err: Error) => console.log("Request Failed", err)) +discard fn() +``` + + +## New `std/tasks` module + +Provides basic primitives for creating parallel programs. +Example: +```nim +import std/tasks +var num = 0 +proc hello(a: int) = num+=a + +let b = toTask hello(13) # arguments must be isolated, see `std/isolation` +b.invoke() +assert num == 13 +b.invoke() # can be called again +assert num == 26 +``` + + +## New module: `std/genasts` + +Provides an API `genAst` that avoids the problems inherent with `quote do` and can +be used as a replacement. +Example showing how this could be used for writing a simplified version of `unittest.check`: +```nim +import std/[genasts, macros, strutils] +macro check2(cond: bool): untyped = + assert cond.kind == nnkInfix, "$# not implemented" % $cond.kind + result = genAst(cond, s = repr(cond), lhs = cond[1], rhs = cond[2]): + # each local symbol we access must be explicitly captured + if not cond: + doAssert false, "'$#'' failed: lhs: '$#', rhs: '$#'" % [s, $lhs, $rhs] +let a = 3 +check2 a*2 == a+3 +if false: check2 a*2 < a+1 # would error with: 'a * 2 < a + 1'' failed: lhs: '6', rhs: '4' +``` + +See PR [#17426](https://github.com/nim-lang/Nim/pull/17426) for details. + + +## New module: `std/setutils` + +- Added `setutils.toSet` that can take any iterable and convert it to a built-in `set`, + if the iterable yields a built-in settable type. +- Added `setutils.fullSet` which returns a full built-in `set` for a valid type. +- Added `setutils.complement` which returns the complement of a built-in `set`. +- Added `setutils.[]=`. + + +## New module: `std/enumutils` + +- Added `genEnumCaseStmt` macro that generates + case statement to parse string to enum. +- Added `items` for enums with holes. +- Added `symbolName` to return the `enum` symbol name ignoring the human-readable name. +- Added `symbolRank` to return the index in which an `enum` member is listed in an enum. + + +## `system` + +- Added `system.prepareMutation` for better support of low + level `moveMem`, `copyMem` operations for `gc:orc`'s copy-on-write string + implementation. +- `system.addEscapedChar` now renders `\r` as `\r` instead of `\c`, to be compatible + with most other languages. +- Added `cmpMem` to `system`. +- `doAssertRaises` now correctly handles foreign exceptions. +- `addInt` now supports unsigned integers. + +Compatibility notes: +- `system.delete` had surprising behavior when the index passed to it was out of + bounds (it would delete the last entry then). Compile with `-d:nimStrictDelete` so + that an index error is produced instead. Be aware however that your code might depend on + this quirky behavior so a review process is required on your part before you can + use `-d:nimStrictDelete`. To make this review easier, use the `-d:nimAuditDelete` + switch, which pretends that `system.delete` is deprecated so that it is easier + to see where it was used in your code. + `-d:nimStrictDelete` will become the default in upcoming versions. +- `cuchar` is now deprecated as it aliased `char` where arguably it should have aliased `uint8`. + Please use `char` or `uint8` instead. +- `repr` now doesn't insert trailing newlines; the previous behavior was very inconsistent, + see [#16034](https://github.com/nim-lang/Nim/pull/16034). + Use `-d:nimLegacyReprWithNewline` for the previous behavior. + `repr` now also renders ASTs correctly for user defined literals, setters, `do`, etc. +- Deprecated `any`. See RFC [#281](https://github.com/nim-lang/RFCs/issues/281). +- The unary slice `..b` was deprecated, use `0..b` instead. + + +## `std/math` + +- Added `almostEqual` for comparing two float values using a machine epsilon. +- Added `clamp` which allows using a `Slice` to clamp to a value. +- Added `ceilDiv` for integer division that rounds up. +- Added `isNaN`. +- Added `copySign`. +- Added `euclDiv` and `euclMod`. +- Added `signbit`. +- Added `frexp` overload procs. Deprecated `c_frexp`, use `frexp` instead. + +Compatibility notes: +- `math.round` now rounds "away from zero" in the JS backend, which is consistent + with other backends. See [#9125](https://github.com/nim-lang/Nim/pull/9125). + Use `-d:nimLegacyJsRound` for the previous behavior. + + +## Random number generators: `std/random`, `std/sysrand`, `std/oids` + +- Added `std/sysrand` module (see details above). +- Added `randState` template that exposes the default random number generator. + Useful for library authors. +- Added `initRand()` overload with no argument which uses the current time as a seed. +- `initRand(seed)` now allows `seed == 0`. +- Fixed overflow bugs. +- Fix `initRand` to avoid random number sequences overlapping, refs + [#18744](https://github.com/nim-lang/Nim/pull/18744). +- `std/oids` now uses `std/random`. + +Compatibility notes: +- Deprecated `std/mersenne`. +- `random.initRand(seed)` now produces non-skewed values for the first call to + `rand()` after initialization with a small (< 30000) seed. + Use `-d:nimLegacyRandomInitRand` to restore previous behavior for a transition + time, see PR [#17467](https://github.com/nim-lang/Nim/pull/17467). + + +## `std/json`, `std/jsonutils` + +- With `-d:nimPreviewJsonutilsHoleyEnum`, `jsonutils` now can serialize/deserialize + holey enums as regular enums (via `ord`) instead of as strings. + It is expected that this behavior becomes the new default in upcoming versions. + `toJson` now serializes `JsonNode` as is via reference (without a deep copy) + instead of treating `JsonNode` as a regular ref object, + this can be customized via `jsonNodeMode`. +- `std/json` and `std/jsonutils` now serialize `NaN`, `Inf`, `-Inf` as strings, + so that `%[NaN, -Inf]` is the string `["nan","-inf"]` instead of `[nan,-inf]` + which was invalid JSON. +- `std/json` can now handle integer literals and floating point literals of + arbitrary length and precision. + Numbers that do not fit the underlying `BiggestInt` or `BiggestFloat` fields are + kept as string literals and one can use external BigNum libraries to handle these. + The `parseFloat` family of functions also has now optional `rawIntegers` and + `rawFloats` parameters that can be used to enforce that all integer or float + literals remain in the "raw" string form so that client code can easily treat + small and large numbers uniformly. +- Added `BackwardsIndex` overload for `JsonNode`. +- `json.%`,`json.to`, `jsonutils.fromJson`,`jsonutils.toJson` now work with `uint|uint64` + instead of raising (as in 1.4) or giving wrong results (as in 1.2). +- `std/jsonutils` now handles `cstring` (including as Table key), and `set`. +- Added `jsonutils.jsonTo` overload with `opt = Joptions()` param. +- `jsonutils.toJson` now supports customization via `ToJsonOptions`. +- `std/json`, `std/jsonutils` now support round-trip serialization when + `-d:nimPreviewFloatRoundtrip` is used. + + +## `std/typetraits`, `std/compilesettings` + +- `distinctBase` now is identity instead of error for non distinct types. +- `distinctBase` now allows controlling whether to be recursive or not. +- Added `enumLen` to return the number of elements in an enum. +- Added `HoleyEnum` for enums with holes, `OrdinalEnum` for enums without holes. +- Added `hasClosure`. +- Added `pointerBase` to return `T` for `ref T | ptr T`. +- Added `compilesettings.SingleValueSetting.libPath`. + + +## networking: `std/net`, `std/asyncnet`, `std/htmlgen`, `std/httpclient`, `std/asyncdispatch`, `std/asynchttpserver`, `std/httpcore` + +- Fixed buffer overflow bugs in `std/net`. +- Exported `sslHandle` from `std/net` and `std/asyncnet`. +- Added `hasDataBuffered` to `std/asyncnet`. +- Various functions in `std/httpclient` now accept `url` of type `Uri`. + Moreover, the `request` function's `httpMethod` argument of type `string` was + deprecated in favor of `HttpMethod` `enum` type; see + [#15919](https://github.com/nim-lang/Nim/pull/15919). +- Added `asyncdispatch.activeDescriptors` that returns the number of currently + active async event handles/file descriptors. +- Added `getPort` to `std/asynchttpserver` to resolve OS-assigned `Port(0)`; + this is usually recommended instead of hardcoding ports which can lead to + "Address already in use" errors. +- Fixed premature garbage collection in `std/asyncdispatch`, when a stacktrace + override is in place. +- Added `httpcore.is1xx` and missing HTTP codes. +- Added `htmlgen.portal` for [making "SPA style" pages using HTML only](https://web.dev/hands-on-portals). + +Compatibility notes: +- On Windows, the SSL library now checks for valid certificates. + For this purpose it uses the `cacert.pem` file, which was extracted + from `https://curl.se/ca/cacert.pem`. Besides + the OpenSSL DLLs (e.g. `libssl-1_1-x64.dll`, `libcrypto-1_1-x64.dll`) you + now also need to ship `cacert.pem` with your `.exe` file. + + +## `std/hashes` + +- `hashes.hash` can now support `object` and `ref` (can be overloaded in user code), + if `-d:nimPreviewHashRef` is used. It is expected that this behavior + becomes the new default in upcoming versions. +- `hashes.hash(proc|ptr|ref|pointer)` now calls `hash(int)` and honors `-d:nimIntHash1`. + `hashes.hash(closure)` has also been improved. + + +## OS: `std/os`, `std/io`, `std/socketstream`, `std/linenoise`, `std/tempfiles` + +- `os.FileInfo` (returned by `getFileInfo`) now contains `blockSize`, + determining preferred I/O block size for this file object. +- Added `os.getCacheDir()` to return platform specific cache directory. +- Improved `os.getTempDir()`, see PR [#16914](https://github.com/nim-lang/Nim/pull/16914). +- Added `os.isAdmin` to tell whether the caller's process is a member of the + Administrators local group (on Windows) or a root (on POSIX). +- Added optional `options` argument to `copyFile`, `copyFileToDir`, and + `copyFileWithPermissions`. By default, on non-Windows OSes, symlinks are + followed (copy files symlinks point to); on Windows, `options` argument is + ignored and symlinks are skipped. +- On non-Windows OSes, `copyDir` and `copyDirWithPermissions` copy symlinks as + symlinks (instead of skipping them as it was before); on Windows symlinks are + skipped. +- On non-Windows OSes, `moveFile` and `moveDir` move symlinks as symlinks + (instead of skipping them sometimes as it was before). +- Added optional `followSymlinks` argument to `setFilePermissions`. +- Added a simpler to use `io.readChars` overload. +- Added `socketstream` module that wraps sockets in the stream interface. +- Added experimental `linenoise.readLineStatus` to get line and status (e.g. ctrl-D or ctrl-C). + + +## Environment variable handling + +- Empty environment variable values are now supported across OS's and backends. +- Environment variable APIs now work in multithreaded scenarios, by delegating to + direct OS calls instead of trying to keep track of the environment. +- `putEnv`, `delEnv` now work at compile time. +- NodeJS backend now supports osenv: `getEnv`, `putEnv`, `envPairs`, `delEnv`, `existsEnv`. + +Compatibility notes: +- `std/os`: `putEnv` now raises if the first argument contains a `=`. + + +## POSIX + +- On POSIX systems, the default signal handlers used for Nim programs (it's + used for printing the stacktrace on fatal signals) will now re-raise the + signal for the OS default handlers to handle. + This lets the OS perform its default actions, which might include core + dumping (on select signals) and notifying the parent process about the cause + of termination. +- On POSIX systems, we now ignore `SIGPIPE` signals, use `-d:nimLegacySigpipeHandler` + for previous behavior. +- Added `posix_utils.osReleaseFile` to get system identification from `os-release` + file on Linux and the BSDs. + ([link](https://www.freedesktop.org/software/systemd/man/os-release.html)) +- Removed undefined behavior for `posix.open`. + + +## `std/prelude` + +- `std/strformat` is now part of `include std/prelude`. +- Added `std/sequtils` import to `std/prelude`. +- `std/prelude` now works with the JS target. +- `std/prelude` can now be used via `include std/prelude`, but `include prelude` still works. + + +## String manipulation: `std/strformat`, `std/strbasics` + +- Added support for parenthesized expressions. +- Added support for const strings instead of just string literals. +- Added `std/strbasics` for high-performance string operations. +- Added `strip`, `setSlice`, `add(a: var string, b: openArray[char])`. + + +## `std/wrapnils` + +- `std/wrapnils` doesn't use `experimental:dotOperators` anymore, avoiding + issues like bug [#13063](https://github.com/nim-lang/Nim/issues/13063) + (which affected error messages) for modules importing `std/wrapnils`. +- Added `??.` macro which returns an `Option`. +- `std/wrapnils` can now be used to protect against `FieldDefect` errors in + case objects, generates optimal code (no overhead compared to manual + if-else branches), and preserves lvalue semantics which allows modifying + an expression. + + +## Containers: `std/algorithm`, `std/lists`, `std/sequtils`, `std/options`, `std/packedsets` + +- Removed the optional `longestMatch` parameter of the `critbits._WithPrefix` + iterators (it never worked reliably). +- Added `algorithm.merge`. +- In `std/lists`: renamed `append` to `add` and retained `append` as an alias; + added `prepend` and `prependMoved` analogously to `add` and `addMoved`; + added `remove` for `SinglyLinkedList`s. +- Added new operations for singly- and doubly linked lists: `lists.toSinglyLinkedList` + and `lists.toDoublyLinkedList` convert from `openArray`s; `lists.copy` implements + shallow copying; `lists.add` concatenates two lists - an O(1) variation that consumes + its argument, `addMoved`, is also supplied. + See PRs [#16362](https://github.com/nim-lang/Nim/pull/16362), + [#16536](https://github.com/nim-lang/Nim/pull/16536). +- New module: `std/packedsets`. + Generalizes `std/intsets`, see PR [#15564](https://github.com/nim-lang/Nim/pull/15564). + +Compatibility notes: +- Deprecated `sequtils.delete` and added an overload taking a `Slice` that raises + a defect if the slice is out of bounds, likewise with `strutils.delete`. +- Deprecated `proc reversed*[T](a: openArray[T], first: Natural, last: int): seq[T]` + in `std/algorithm`. +- `std/options` changed `$some(3)` to `"some(3)"` instead of `"Some(3)"` + and `$none(int)` to `"none(int)"` instead of `"None[int]"`. + + +## `std/times` + +- Added `ZZZ` and `ZZZZ` patterns to `times.nim` `DateTime` parsing, to match time + zone offsets without colons, e.g. `UTC+7 -> +0700`. +- Added `dateTime` and deprecated `initDateTime`. + + +## `std/macros` and AST + +- New module `std/genasts`, see description above. +- The required name of case statement macros for the experimental + `caseStmtMacros` feature has changed from `match` to `` `case` ``. +- Tuple expressions are now parsed consistently as + `nnkTupleConstr` node. Will affect macros expecting nodes to be of `nnkPar`. +- In `std/macros`, `treeRepr,lispRepr,astGenRepr` now represent SymChoice nodes + in a collapsed way. + Use `-d:nimLegacyMacrosCollapseSymChoice` to get the previous behavior. +- Made custom op in `macros.quote` work for all statements. + + +## `std/sugar` + +- Added `sugar.dumpToString` which improves on `sugar.dump`. +- Added an overload for the `collect` macro that infers the container type based + on the syntax of the last expression. Works with std seqs, tables and sets. + +Compatibility notes: +- Removed support for named procs in `sugar.=>`. + + +## Parsing: `std/parsecfg`, `std/strscans`, `std/uri` + +- Added `sections` iterator in `parsecfg`. +- `strscans.scanf` now supports parsing single characters. +- Added `strscans.scanTuple` which uses `strscans.scanf` internally, + returning a tuple which can be unpacked for easier usage of `scanf`. +- Added `decodeQuery` to `std/uri`. +- `parseopt.initOptParser` has been made available and `parseopt` has been + added back to `std/prelude` for all backends. Previously `initOptParser` was + unavailable if the `std/os` module did not have `paramCount` or `paramStr`, + but the use of these in `initOptParser` were conditionally to the runtime + arguments passed to it, so `initOptParser` has been changed to raise + `ValueError` when the real command line is not available. `parseopt` was + previously excluded from `std/prelude` for JS, as it could not be imported. + +Compatibility notes: +- Changed the behavior of `uri.decodeQuery` when there are unencoded `=` + characters in the decoded values. Prior versions would raise an error. This is + no longer the case to comply with the HTML spec and other languages' + implementations. Old behavior can be obtained with + `-d:nimLegacyParseQueryStrict`. `cgi.decodeData` which uses the same + underlying code is also updated the same way. + + +## JS stdlib changes + +- Added `std/jsbigints` module, which provides arbitrary precision integers for the JS target. +- Added `setCurrentException` for the JS backend. +- `writeStackTrace` is available in the JS backend now. +- Added `then`, `catch` to `std/asyncjs` for promise pipelining, for now hidden + behind `-d:nimExperimentalAsyncjsThen`. +- Added `std/jsfetch` module [Fetch](https://developer.mozilla.org/docs/Web/API/Fetch_API) + wrapper for the JS target. +- Added `std/jsheaders` module [Headers](https://developer.mozilla.org/en-US/docs/Web/API/Headers) + wrapper for the JS target. +- Added `std/jsformdata` module [FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData) + wrapper for the JS target. +- Added `jscore.debugger` to [call any available debugging functionality, such as breakpoints](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/debugger). +- Added `jsconsole.dir`, `jsconsole.dirxml`, `jsconsole.timeStamp`. +- Added dollar `$` and `len` for `jsre.RegExp`. +- Added `jsconsole.jsAssert` for the JS target. +- Added `**` to `std/jsffi`. +- Added `copyWithin` [for `seq` and `array` for JS targets](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/copyWithin). +- In `std/dom`, `Interval` is now a `ref object`, same as `Timeout`. + Definitions of `setTimeout`, `clearTimeout`, `setInterval`, `clearInterval` were updated. +- Added `dom.scrollIntoView` proc with options. +- Added `dom.setInterval`, `dom.clearInterval` overloads. +- Merged `std/dom_extensions` into the `std/dom` module, + as it was a module with a single line, see RFC [#413](https://github.com/nim-lang/RFCs/issues/413). +- `$` now gives more correct results on the JS backend. + + +## JS compiler changes + +- `cstring` doesn't support the `[]=` operator anymore in the JS backend. +- Array literals now use JS typed arrays when the corresponding JS typed array exists, + for example `[byte(1), 2, 3]` generates `new Uint8Array([1, 2, 3])`. + + +## VM and nimscript backend + +- VM now supports `addr(mystring[ind])` (index + index assignment). +- `nimscript` now handles `except Exception as e`. +- `nil` dereference is not allowed at compile time. `cast[ptr int](nil)[]` is rejected at compile time. +- `static[T]` now works better, refs [#17590](https://github.com/nim-lang/Nim/pull/17590), + [#15853](https://github.com/nim-lang/Nim/pull/15853). +- `distinct T` conversions now work in VM. +- `items(cstring)` now works in VM. +- Fix `addr`, `len`, `high` in VM ([#16002](https://github.com/nim-lang/Nim/pull/16002), + [#16610](https://github.com/nim-lang/Nim/pull/16610)). +- `std/cstrutils` now works in VM. + + +## OS/platform-specific notes + +- Support for Apple silicon/M1. +- Support for 32-bit RISC-V, refs [#16231](https://github.com/nim-lang/Nim/pull/16231). +- Support for armv8l, refs [#18901](https://github.com/nim-lang/Nim/pull/18901). +- Support for CROSSOS, refs [#18889](https://github.com/nim-lang/Nim/pull/18889). +- The allocator for Nintendo Switch, which was nonfunctional because + of breaking changes in libnx, was removed, in favor of the new `-d:nimAllocPagesViaMalloc` option. +- Allow reading parameters when compiling for Nintendo Switch. +- `--nimcache` now correctly works in a cross-compilation setting. +- Cross compilation targeting Windows was improved. +- This now works from macOS/Linux: `nim r -d:mingw main` + + + +## Performance / memory optimizations + +- The comment field in PNode AST was moved to a side channel, reducing overall + memory usage during compilation by a factor 1.25x +- `std/jsonutils` deserialization is now up to 20x faster. +- `os.copyFile` is now 2.5x faster on macOS, by using `copyfile` from `copyfile.h`; + use `-d:nimLegacyCopyFile` for macOS < 10.5. +- Float to string conversion is now 10x faster thanks to the Dragonbox algorithm, + with `-d:nimPreviewFloatRoundtrip`. +- `newSeqWith` is 3x faster. +- CI now supports batching (making Windows CI 2.3X faster). +- Sets now uses the optimized `countSetBits` proc, see PR + [#17334](https://github.com/nim-lang/Nim/pull/17334). + + +## Debugging + +- You can now enable/disable VM tracing in user code via `vmutils.vmTrace`. +- `koch tools` now builds `bin/nim_dbg` which allows easy access to a debug version + of Nim without recompiling. +- Added new module `compiler/debugutils` to help with debugging Nim compiler. +- Renamed `-d:nimCompilerStackraceHints` to `-d:nimCompilerStacktraceHints` and + used it in more contexts; this flag which works in tandem with `--stackTraceMsgs` + to show user code context in compiler stacktraces. + + +## Type system + +- `typeof(voidStmt)` now works and returns `void`. +- `enum` values can now be overloaded. This needs to be enabled + via `{.experimental: "overloadableEnums".}`. We hope that this feature allows + for the development of more fluent (less ugly) APIs. + See RFC [#373](https://github.com/nim-lang/RFCs/issues/373) for more details. +- A type conversion from one `enum` type to another now produces an `[EnumConv]` warning. + You should use `ord` (or `cast`, but the compiler won't help, if you misuse it) instead. + ```nim + type A = enum a1, a2 + type B = enum b1, b2 + echo a1.B # produces a warning + echo a1.ord.B # produces no warning + ``` +- A dangerous implicit conversion to `cstring` now triggers a `[CStringConv]` warning. + This warning will become an error in future versions! Use an explicit conversion + like `cstring(x)` in order to silence the warning. +- There is a new warning for *any* type conversion to `enum` that can be enabled via + `.warning[AnyEnumConv]:on` or `--warning:AnyEnumConv:on`. +- Reusing a type name in a different scope now works, refs [#17710](https://github.com/nim-lang/Nim/pull/17710). +- Fixed implicit and explicit generics in procedures, refs [#18808](https://github.com/nim-lang/Nim/pull/18808). + + +## New-style concepts + +Example: +```nim +type + Comparable = concept # no T, an atom + proc cmp(a, b: Self): int +``` +The new design does not rely on `system.compiles` and may compile faster. +See PR [#15251](https://github.com/nim-lang/Nim/pull/15251) +and RFC [#168](https://github.com/nim-lang/RFCs/issues/168) for details. + + +## Lexical / syntactic + +- Nim now supports a small subset of Unicode operators as operator symbols. + The supported symbols are: "∙ ∘ × ★ ⊗ ⊘ ⊙ ⊛ ⊠ ⊡ ∩ ∧ ⊓ ± ⊕ ⊖ ⊞ ⊟ ∪ ∨ ⊔". + To enable this feature, use `--experimental:unicodeOperators`. Note that due + to parser limitations you **cannot** enable this feature via a + pragma `{.experimental: "unicodeOperators".}` reliably, you need to enable + it via the command line or in a configuration file. +- `var a {.foo.} = expr` now works inside templates (except when `foo` is overloaded). + + +## Compiler messages, error messages, hints, warnings + +- Significant improvement to error messages involving effect mismatches, + see PRs [#18384](https://github.com/nim-lang/Nim/pull/18384), + [#18418](https://github.com/nim-lang/Nim/pull/18418). +- Added `--declaredLocs` to show symbol declaration location in error messages. +- Added `--spellSuggest` to show spelling suggestions on typos. +- Added `--processing:dots|filenames|off` which customizes `hintProcessing`; + `--processing:filenames` shows which include/import modules are being compiled as an import stack. +- `FieldDefect` messages now shows discriminant value + lineinfo, in all backends (C, JS, VM) +- Added `--hintAsError` with similar semantics as `--warningAsError`. +- Added `--unitsep:on|off` to control whether to add ASCII unit separator `\31` + before a newline for every generated message (potentially multiline), + so tooling can tell when messages start and end. +- Added `--filenames:abs|canonical|legacyRelProj` which replaces `--listFullPaths:on|off` +- `--hint:all:on|off` is now supported to select or deselect all hints; it + differs from `--hints:on|off` which acts as a (reversible) gate. + Likewise with `--warning:all:on|off`. +- The style checking of the compiler now supports a `--styleCheck:usages` switch. + This switch enforces that every symbol is written as it was declared, not enforcing + the official Nim style guide. To be enabled, this has to be combined either + with `--styleCheck:error` or `--styleCheck:hint`. +- Type mismatch errors now show more context, use `-d:nimLegacyTypeMismatch` for + previous behavior. +- `typedesc[Foo]` now renders as such instead of `type Foo` in compiler messages. +- `runnableExamples` now show originating location in stacktraces on failure. +- `SuccessX` message now shows more useful information. +- New `DuplicateModuleImport` warning; improved `UnusedImport` and + `XDeclaredButNotUsed` accuracy. + +Compatibility notes: +- `--hint:CC` now prints to stderr (like all other hints) instead of stdout. + + +## Building and running Nim programs, configuration system + +- JSON build instructions are now generated in `$nimcache/outFileBasename.json` + instead of `$nimcache/projectName.json`. This allows avoiding recompiling a + given project compiled with different options if the output file differs. +- `--usenimcache` (implied by `nim r main`) now generates an output file that includes + a hash of some of the compilation options, which allows caching generated binaries: + ```bash + nim r main # recompiles + nim r -d:foo main # recompiles + nim r main # uses cached binary + nim r main arg1 arg2 # likewise (runtime arguments are irrelevant) + ``` +- `nim r` now supports cross compilation from unix to windows when specifying + `-d:mingw` by using Wine, e.g.: + `nim r --eval:'import os; echo "a" / "b"'` prints `a\b`. +- `nim` can compile version 1.4.0 as follows: + `nim c --lib:lib --stylecheck:off -d:nimVersion140 compiler/nim`. + `-d:nimVersion140` is not needed for bootstrapping, only for building 1.4.0 from devel. +- `nim e` now accepts arbitrary file extensions for the nimscript file, + although `.nims` is still the preferred extension in general. +- The configuration subsystem now allows for `-d:release` and `-d:danger` to work as expected. + The downside is that these defines now have custom logic that doesn't apply for + other defines. + + +## Multithreading + +- TLS: macOS now uses native TLS (`--tlsEmulation:off`). TLS now works with + `importcpp` non-POD types; such types must use `.cppNonPod` and + `--tlsEmulation:off`should be used. +- Added `unsafeIsolate` and `extract` to `std/isolation`. +- Added `std/tasks`, see description above. + + +## Memory management + +- `--gc:arc` now bootstraps (PR [#17342](https://github.com/nim-lang/Nim/pull/17342)). +- Lots of improvements to `gc:arc`, `gc:orc`, see PR + [#15697](https://github.com/nim-lang/Nim/pull/15697), + [#16849](https://github.com/nim-lang/Nim/pull/16849), + [#17993](https://github.com/nim-lang/Nim/pull/17993). +- `--gc:orc` is now 10% faster than previously for common workloads. + If you have trouble with its changed behavior, compile with `-d:nimOldOrc`. +- The `--gc:orc` algorithm was refined so that custom container types can participate in the + cycle collection process. See the documentation of `=trace` for more details. +- On embedded devices `malloc` can now be used instead of `mmap` via `-d:nimAllocPagesViaMalloc`. + This is only supported for `--gc:orc` or `--gc:arc`. + +Compatibility notes: +- `--newruntime` and `--refchecks` are deprecated, + use `--gc:arc`, `--gc:orc`, or `--gc:none` as appropriate instead. + + +## Docgen + +- docgen: RST files can now use single backticks instead of double backticks and + correctly render in both `nim rst2html` (as before) as well as common tools rendering + RST directly (e.g. GitHub). + This is done by adding the `default-role:: code` directive inside the RST file + (which is now handled by `nim rst2html`). +- Source+Edit links now appear on top of every docgen'd page when + `nim doc --git.url:url ...` is given. +- Latex doc generation is revised: output `.tex` files should be compiled + by `xelatex` (not by `pdflatex` as before). Now default Latex settings + provide support for Unicode and better avoid margin overflows. + The minimum required version is TeXLive 2018 (or an equivalent MikTeX version). +- The RST parser now supports footnotes, citations, admonitions, and short style + references with symbols. +- The RST parser now supports Markdown table syntax. + Known limitations: + - cell alignment is not supported, i.e. alignment annotations in a delimiter + row (`:---`, `:--:`, `---:`) are ignored + - every table row must start with `|`, e.g. `| cell 1 | cell 2 |`. +- Implemented `doc2tex` compiler command which converts documentation in + `.nim` files to Latex. +- docgen now supports syntax highlighting for inline code. +- docgen now supports same-line doc comments: + ```nim + func fn*(a: int): int = 42 ## Doc comment + ``` +- docgen now renders `deprecated` and other pragmas. +- `runnableExamples` now works with templates and nested templates. +- `runnableExamples: "-r:off"` now works for examples that should compile but not run. +- `runnableExamples` now renders code verbatim, and produces correct code in all cases. +- docgen now shows correct, canonical import paths in docs. +- docgen now shows all routines in sidebar, and the proc signature is now shown in sidebar. + + +## Effects and checks + +- Significant improvement to error messages involving effect mismatches +- There is a new `cast` section `{.cast(uncheckedAssign).}: body` that disables some + compiler checks regarding `case objects`. This allows serialization libraries + to avoid ugly, non-portable solutions. See RFC + [#407](https://github.com/nim-lang/RFCs/issues/407) for more details. + + +## Tools + +- Major improvements to `nimgrep`, see PR [#15612 +](https://github.com/nim-lang/Nim/pull/15612). +- `fusion` is now un-bundled from Nim, `./koch fusion` will + install it via Nimble at a fixed hash. +- `testament`: added `nimoutFull: bool` spec to compare full output of compiler + instead of a subset; many bugfixes to testament. + + +## Misc/cleanups + +- Deprecated `TaintedString` and `--taintmode`. +- Deprecated `--nilseqs` which is now a noop. +- Added `-d:nimStrictMode` in CI in several places to ensure code doesn't have + certain hints/warnings. +- Removed `.travis.yml`, `appveyor.yml.disabled`, `.github/workflows/ci.yml.disabled`. +- `[skip ci]` now works in azure and CI pipelines, see detail in PR + [#17561](https://github.com/nim-lang/Nim/pull/17561). From c4c9876785a17f9b43bb1e0748eff67a71a7487f Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 14 Oct 2021 15:23:01 +0800 Subject: [PATCH 2585/3768] fix a potential bug (#18900) --- lib/std/private/digitsutils.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/private/digitsutils.nim b/lib/std/private/digitsutils.nim index 268a3ba7d..9be2ab3ef 100644 --- a/lib/std/private/digitsutils.nim +++ b/lib/std/private/digitsutils.nim @@ -95,7 +95,7 @@ proc addInt*(result: var string; x: int64) = var num: uint64 if x < 0: if x == low(int64): - num = uint64(x) + num = cast[uint64](x) else: num = uint64(-x) let base = result.len From 3493783d0f3f90b2a90c87b93c92360168272f34 Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 14 Oct 2021 15:23:36 +0800 Subject: [PATCH 2586/3768] test whether it is unnecessary (#18893) --- compiler/semtypinst.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 0bb53e04d..5d300fc58 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -35,7 +35,7 @@ proc checkConstructedType*(conf: ConfigRef; info: TLineInfo, typ: PType) = proc searchInstTypes*(g: ModuleGraph; key: PType): PType = let genericTyp = key[0] if not (genericTyp.kind == tyGenericBody and - key[0] == genericTyp and genericTyp.sym != nil): return + genericTyp.sym != nil): return for inst in typeInstCacheItems(g, genericTyp.sym): if inst.id == key.id: return inst From 8a4eeba218b22d4258f95a279771851082a2e197 Mon Sep 17 00:00:00 2001 From: Miran Date: Thu, 14 Oct 2021 09:56:58 +0200 Subject: [PATCH 2587/3768] try to fix broken ssl test (#18991) --- tests/untestable/thttpclient_ssl_remotenetwork.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/untestable/thttpclient_ssl_remotenetwork.nim b/tests/untestable/thttpclient_ssl_remotenetwork.nim index f56fe274b..d8137e516 100644 --- a/tests/untestable/thttpclient_ssl_remotenetwork.nim +++ b/tests/untestable/thttpclient_ssl_remotenetwork.nim @@ -48,7 +48,7 @@ when enableRemoteNetworking and (defined(nimTestsEnableFlaky) or not defined(win ("https://sha256.badssl.com/", good, "sha256"), ("https://sha384.badssl.com/", good, "sha384"), ("https://sha512.badssl.com/", good, "sha512"), - ("https://1000-sans.badssl.com/", good, "1000-sans"), + ("https://1000-sans.badssl.com/", bad, "1000-sans"), ("https://10000-sans.badssl.com/", good_broken, "10000-sans"), ("https://ecc256.badssl.com/", good, "ecc256"), ("https://ecc384.badssl.com/", good, "ecc384"), From 73330711a3d753d25b073fb1dc24cb16e559724d Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 14 Oct 2021 23:55:05 +0800 Subject: [PATCH 2588/3768] make choosenim work on windows [backport] (#18993) --- bin/nim-gdb.bash | 1 - compiler/installer.ini | 1 - 2 files changed, 2 deletions(-) delete mode 120000 bin/nim-gdb.bash diff --git a/bin/nim-gdb.bash b/bin/nim-gdb.bash deleted file mode 120000 index f6eba4b0c..000000000 --- a/bin/nim-gdb.bash +++ /dev/null @@ -1 +0,0 @@ -nim-gdb \ No newline at end of file diff --git a/compiler/installer.ini b/compiler/installer.ini index 9e31b4e3f..41eee09f1 100644 --- a/compiler/installer.ini +++ b/compiler/installer.ini @@ -120,7 +120,6 @@ Files: "bin/nim" InstallScript: "yes" UninstallScript: "yes" Files: "bin/nim-gdb" -Files: "bin/nim-gdb.bash" [InnoSetup] From f4525efcf3b915be8e17c9730e9e0ffd0afe3a0f Mon Sep 17 00:00:00 2001 From: ee7 <45465154+ee7@users.noreply.github.com> Date: Fri, 15 Oct 2021 09:10:27 +0200 Subject: [PATCH 2589/3768] changelog_1_6_0: mention breaking change in effect tracking (#18995) --- changelogs/changelog_1_6_0.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/changelogs/changelog_1_6_0.md b/changelogs/changelog_1_6_0.md index 1ce6c2093..4a1047d20 100644 --- a/changelogs/changelog_1_6_0.md +++ b/changelogs/changelog_1_6_0.md @@ -905,6 +905,36 @@ Compatibility notes: to avoid ugly, non-portable solutions. See RFC [#407](https://github.com/nim-lang/RFCs/issues/407) for more details. +Compatibility notes: +- Fixed effect tracking for borrowed procs (see [#18882](https://github.com/nim-lang/Nim/pull/18882)). + One consequence is that, under some circumstances, Nim could previously permit a procedure with side effects to be written with `func` - you may need to change some occurrences of `func` to `proc`. + To illustrate, Nim versions before 1.6.0 compile the below without error + ```nim + proc print(s: string) = + echo s + + type + MyString = distinct string + + proc print(s: MyString) {.borrow.} + + func foo(s: MyString) = + print(s) + ``` + but Nim 1.6.0 produces the error + ``` + Error: 'foo' can have side effects + ``` + similar to how we expect that + ```nim + func print(s: string) = + echo s + ``` + produces + ``` + Error: 'print' can have side effects + ``` + ## Tools From 3b1a601fe1ad6ac27a21b5f28442474d47c60a2f Mon Sep 17 00:00:00 2001 From: ee7 <45465154+ee7@users.noreply.github.com> Date: Sat, 16 Oct 2021 11:25:05 +0200 Subject: [PATCH 2590/3768] sequtils: fix errors from `strictFuncs` use (#18998) Nim 1.4.x compiled the below code without error when using `--experimental:strictFuncs` import std/sequtils type Foo = ref object let foo1 = Foo() let foo2 = Foo() let foos = @[foo1, foo2] let fooTuples = @[(foo1, 1), (foo2, 2)] discard repeat(foo1, 3) discard zip(foos, foos) discard unzip(fooTuples) However, since 2020-12-09, devel Nim produced errors like /tmp/bar.nim(11, 15) template/generic instantiation of `repeat` from here /foo/nim/pure/collections/sequtils.nim(172, 6) Error: 'repeat' can have side effects an object reachable from 'x' is potentially mutated /foo/nim/pure/collections/sequtils.nim(183, 15) the mutation is here /foo/nim/pure/collections/sequtils.nim(183, 15) is the statement that connected the mutation to the parameter This commit reverts some `proc` to `func` changes so that code that: - calls `repeat`, `zip`, or `unzip` - and instantiates them with types containing `ref` can once again be compiled with `strictFuncs`. Otherwise, a user might be forced to drop or alter their `strictFuncs` use when upgrading from Nim 1.4.x, or when writing new code that uses these procedures (at least for now, with the current `strictFuncs` implementation). This commit also adds tests to assert that the remaining funcs in this module can be compiled with `strictFuncs` when used with types containing `ref`. The original batch of `proc` to `func` changes in `sequtils.nim` was in commit 6f57ebae349f, which was partially reverted in 38eb021f8158. See also: https://github.com/nim-lang/Nim/issues/16305 --- lib/pure/collections/sequtils.nim | 6 +++--- tests/stdlib/tsequtils.nim | 26 ++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index 12b961371..64a7be7a9 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -169,7 +169,7 @@ func cycle*[T](s: openArray[T], n: Natural): seq[T] = result[o] = e inc o -func repeat*[T](x: T, n: Natural): seq[T] = +proc repeat*[T](x: T, n: Natural): seq[T] = ## Returns a new sequence with the item `x` repeated `n` times. ## `n` must be a non-negative number (zero or more). ## @@ -246,7 +246,7 @@ func maxIndex*[T](s: openArray[T]): int {.since: (1, 1).} = template zipImpl(s1, s2, retType: untyped): untyped = - func zip*[S, T](s1: openArray[S], s2: openArray[T]): retType = + proc zip*[S, T](s1: openArray[S], s2: openArray[T]): retType = ## Returns a new sequence with a combination of the two input containers. ## ## The input containers can be of different types. @@ -289,7 +289,7 @@ when (NimMajor, NimMinor) <= (1, 0): else: zipImpl(s1, s2, seq[(S, T)]) -func unzip*[S, T](s: openArray[(S, T)]): (seq[S], seq[T]) {.since: (1, 1).} = +proc unzip*[S, T](s: openArray[(S, T)]): (seq[S], seq[T]) {.since: (1, 1).} = ## Returns a tuple of two sequences split out from a sequence of 2-field tuples. runnableExamples: let diff --git a/tests/stdlib/tsequtils.nim b/tests/stdlib/tsequtils.nim index 338473407..179f619f0 100644 --- a/tests/stdlib/tsequtils.nim +++ b/tests/stdlib/tsequtils.nim @@ -10,6 +10,7 @@ from algorithm import sorted {.experimental: "strictEffects".} {.push warningAsError[Effect]: on.} +{.experimental: "strictFuncs".} # helper for testing double substitution side effects which are handled # by `evalOnceAs` @@ -456,6 +457,31 @@ block: # xxx: obscure CT error: basic_types.nim(16, 16) Error: internal error: symbol has no generated name: true doAssert: iter(3).mapIt(2*it).foldl(a + b) == 6 +block: # strictFuncs tests with ref object + type Foo = ref object + + let foo1 = Foo() + let foo2 = Foo() + let foos = @[foo1, foo2] + + # Procedures that are `func` + discard concat(foos, foos) + discard count(foos, foo1) + discard cycle(foos, 3) + discard deduplicate(foos) + discard minIndex(foos) + discard maxIndex(foos) + discard distribute(foos, 2) + var mutableFoos = foos + mutableFoos.delete(0..1) + mutableFoos.insert(foos) + + # Some procedures that are `proc`, but were reverted from `func` + discard repeat(foo1, 3) + discard zip(foos, foos) + let fooTuples = @[(foo1, 1), (foo2, 2)] + discard unzip(fooTuples) + template main = # xxx move all tests here block: # delete tests From 162b07d72cbe79bad1ddf99413ba508f07a831b1 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Sun, 17 Oct 2021 06:38:07 +0300 Subject: [PATCH 2591/3768] fix parsesql.treeRepr when nil is in input (#18974) * fix parsesql.treeRepr when nil is in input * return newNode(nkNone) as default value, not nil --- lib/pure/parsesql.nim | 6 +++--- tests/stdlib/tparsesql.nim | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/lib/pure/parsesql.nim b/lib/pure/parsesql.nim index eb5d7c2cc..9d74d08d0 100644 --- a/lib/pure/parsesql.nim +++ b/lib/pure/parsesql.nim @@ -982,7 +982,7 @@ proc parseInsert(p: var SqlParser): SqlNode = parseParIdentList(p, n) result.add n else: - result.add(nil) + result.add(newNode(nkNone)) if isKeyw(p, "default"): getTok(p) eat(p, "values") @@ -1017,7 +1017,7 @@ proc parseUpdate(p: var SqlParser): SqlNode = if isKeyw(p, "where"): result.add(parseWhere(p)) else: - result.add(nil) + result.add(newNode(nkNone)) proc parseDelete(p: var SqlParser): SqlNode = getTok(p) @@ -1029,7 +1029,7 @@ proc parseDelete(p: var SqlParser): SqlNode = if isKeyw(p, "where"): result.add(parseWhere(p)) else: - result.add(nil) + result.add(newNode(nkNone)) proc parseSelect(p: var SqlParser): SqlNode = getTok(p) diff --git a/tests/stdlib/tparsesql.nim b/tests/stdlib/tparsesql.nim index ba9e601a1..b440dc90f 100644 --- a/tests/stdlib/tparsesql.nim +++ b/tests/stdlib/tparsesql.nim @@ -3,6 +3,17 @@ discard """ """ import parsesql +doAssert treeRepr(parseSql("INSERT INTO STATS VALUES (10, 5.5); ") +) == """ + +nkStmtList + nkInsert + nkIdent STATS + nkNone + nkValueList + nkIntegerLit 10 + nkNumericLit 5.5""" + doAssert $parseSQL("SELECT foo FROM table;") == "select foo from table;" doAssert $parseSQL(""" SELECT @@ -183,8 +194,31 @@ SET ContactName = 'Alfred Schmidt', City= 'Frankfurt' WHERE CustomerID = 1; """) == "update Customers set ContactName = 'Alfred Schmidt' , City = 'Frankfurt' where CustomerID = 1;" +doAssert treeRepr(parseSql("""UPDATE Customers + SET ContactName = 'Alice', City= 'Frankfurt';""") +) == """ + +nkStmtList + nkUpdate + nkIdent Customers + nkAsgn + nkIdent ContactName + nkStringLit Alice + nkAsgn + nkIdent City + nkStringLit Frankfurt + nkNone""" + doAssert $parseSQL("DELETE FROM table_name;") == "delete from table_name;" +doAssert treeRepr(parseSQL("DELETE FROM table_name;") +) == """ + +nkStmtList + nkDelete + nkIdent table_name + nkNone""" + doAssert $parseSQL("DELETE * FROM table_name;") == "delete from table_name;" doAssert $parseSQL(""" From f77dea01fd980734a3ed4acb812575827540aff9 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Sat, 16 Oct 2021 23:37:52 -0700 Subject: [PATCH 2592/3768] define `nimVersion` automatically and avoid needing -d:nimVersion140 (#18726) * define `nimVersion` and avoid needing -d:nimVersion140 * fix changelog --- changelog.md | 3 ++- compiler/ast.nim | 2 +- compiler/ccgexprs.nim | 4 ++-- compiler/options.nim | 18 ++++++++++++++++++ config/config.nims | 2 ++ 5 files changed, 25 insertions(+), 4 deletions(-) diff --git a/changelog.md b/changelog.md index ab63bd8b5..fbda70582 100644 --- a/changelog.md +++ b/changelog.md @@ -1,7 +1,6 @@ # v1.8.x - yyyy-mm-dd - ## Changes affecting backward compatibility @@ -16,6 +15,8 @@ ## Compiler changes +- `nim` can now compile version 1.4.0 as follows: `nim c --lib:lib --stylecheck:off compiler/nim`, + without requiring `-d:nimVersion140` which is now a noop. ## Tool changes diff --git a/compiler/ast.nim b/compiler/ast.nim index 0a3b1b72d..abd2ff01e 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -656,7 +656,7 @@ type mUnaryPlusI, mBitnotI, mUnaryPlusF64, mUnaryMinusF64, mCharToStr, mBoolToStr, - mIntToStr, mInt64ToStr, mFloatToStr, # for -d:nimVersion140 + mIntToStr, mInt64ToStr, mFloatToStr, # for compiling nimStdlibVersion < 1.5.1 (not bootstrapping) mCStrToStr, mStrToStr, mEnumToStr, mAnd, mOr, diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index ac4a26bd6..fc09fefc3 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -894,7 +894,7 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) = let discIndex = rdSetElemLoc(p.config, v, u.t) if optTinyRtti in p.config.globalOptions: # not sure how to use `genEnumToStr` here - if p.config.isDefined("nimVersion140"): + if p.config.getStdlibVersion < (1,5,1): const code = "{ #raiseFieldError($1); $2} $n" linefmt(p, cpsStmts, code, [strLit, raiseInstr(p)]) else: @@ -905,7 +905,7 @@ proc genFieldCheck(p: BProc, e: PNode, obj: Rope, field: PSym) = let first = p.config.firstOrd(disc.sym.typ) let firstLit = int64Literal(cast[int](first)) let discName = genTypeInfo(p.config, p.module, disc.sym.typ, e.info) - if p.config.isDefined("nimVersion140"): + if p.config.getStdlibVersion < (1,5,1): const code = "{ #raiseFieldError($1); $2} $n" linefmt(p, cpsStmts, code, [strLit, raiseInstr(p)]) else: diff --git a/compiler/options.nim b/compiler/options.nim index eafcd816d..d3f868316 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -165,6 +165,7 @@ const cmdCtags, cmdBuildindex} type + NimVer* = tuple[major: int, minor: int, patch: int] TStringSeq* = seq[string] TGCMode* = enum # the selected GC gcUnselected = "unselected" @@ -346,6 +347,7 @@ type outDir*: AbsoluteDir jsonBuildFile*: AbsoluteFile prefixDir*, libpath*, nimcacheDir*: AbsoluteDir + nimStdlibVersion*: NimVer dllOverrides, moduleOverrides*, cfileSpecificOptions*: StringTableRef projectName*: string # holds a name like 'nim' projectPath*: AbsoluteDir # holds a path like /home/alice/projects/nim/compiler/ @@ -389,6 +391,16 @@ type cppCustomNamespace*: string vmProfileData*: ProfileData +proc parseNimVersion*(a: string): NimVer = + # could be moved somewhere reusable + if a.len > 0: + let b = a.split(".") + assert b.len == 3, a + template fn(i) = result[i] = b[i].parseInt # could be optimized if needed + fn(0) + fn(1) + fn(2) + proc assignIfDefault*[T](result: var T, val: T, def = default(T)) = ## if `result` was already assigned to a value (that wasn't `def`), this is a noop. if result == def: result = val @@ -560,6 +572,12 @@ proc newPartialConfigRef*(): ConfigRef = proc cppDefine*(c: ConfigRef; define: string) = c.cppDefines.incl define +proc getStdlibVersion*(conf: ConfigRef): NimVer = + if conf.nimStdlibVersion == (0,0,0): + let s = conf.symbols.getOrDefault("nimVersion", "") + conf.nimStdlibVersion = s.parseNimVersion + result = conf.nimStdlibVersion + proc isDefined*(conf: ConfigRef; symbol: string): bool = if conf.symbols.hasKey(symbol): result = true diff --git a/config/config.nims b/config/config.nims index 2ae86012c..893d6960f 100644 --- a/config/config.nims +++ b/config/config.nims @@ -14,3 +14,5 @@ when defined(nimStrictMode): # switch("hint", "ConvFromXtoItselfNotNeeded") switch("hintAsError", "ConvFromXtoItselfNotNeeded") # future work: XDeclaredButNotUsed + +switch("define", "nimVersion:" & NimVersion) From f0af4a36b932af3932b611c1f728ebfbfe3a2749 Mon Sep 17 00:00:00 2001 From: Jason Beetham Date: Sun, 17 Oct 2021 08:24:43 -0600 Subject: [PATCH 2593/3768] Added setGlobalValue to VM api (#19007) --- compiler/nimeval.nim | 4 ++++ compiler/vm.nim | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/compiler/nimeval.nim b/compiler/nimeval.nim index 577c7b586..82e2f0812 100644 --- a/compiler/nimeval.nim +++ b/compiler/nimeval.nim @@ -57,6 +57,10 @@ proc callRoutine*(i: Interpreter; routine: PSym; args: openArray[PNode]): PNode proc getGlobalValue*(i: Interpreter; letOrVar: PSym): PNode = result = vm.getGlobalValue(PCtx i.graph.vm, letOrVar) +proc setGlobalValue*(i: Interpreter; letOrVar: PSym, val: PNode) = + ## Sets a global value to a given PNode, does not do any type checking. + vm.setGlobalValue(PCtx i.graph.vm, letOrVar, val) + proc implementRoutine*(i: Interpreter; pkg, module, name: string; impl: proc (a: VmArgs) {.closure, gcsafe.}) = assert i != nil diff --git a/compiler/vm.nim b/compiler/vm.nim index 9fa25efbe..1b443aff1 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -2162,6 +2162,11 @@ proc getGlobalValue*(c: PCtx; s: PSym): PNode = internalAssert c.config, s.kind in {skLet, skVar} and sfGlobal in s.flags result = c.globals[s.position-1] +proc setGlobalValue*(c: PCtx; s: PSym, val: PNode) = + ## Does not do type checking so ensure the `val` matches the `s.typ` + internalAssert c.config, s.kind in {skLet, skVar} and sfGlobal in s.flags + c.globals[s.position-1] = val + include vmops proc setupGlobalCtx*(module: PSym; graph: ModuleGraph; idgen: IdGenerator) = From c239db4817586f769327a6d27c244682111f903a Mon Sep 17 00:00:00 2001 From: narimiran Date: Tue, 19 Oct 2021 17:01:56 +0200 Subject: [PATCH 2594/3768] bump NimVersion to 1.7.1 --- lib/system.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/system.nim b/lib/system.nim index caa036f37..012aefe6a 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2121,7 +2121,7 @@ const ## when (NimMajor, NimMinor, NimPatch) >= (1, 3, 1): discard # see also std/private/since - NimMinor* {.intdefine.}: int = 5 + NimMinor* {.intdefine.}: int = 7 ## is the minor number of Nim's version. ## Odd for devel, even for releases. From 62701cd3b92677c2014cf70b1cf5a5ba6e0468bf Mon Sep 17 00:00:00 2001 From: John Novak Date: Thu, 21 Oct 2021 01:06:00 +1000 Subject: [PATCH 2595/3768] Fix isInvalidFilename & always operate on the full passed in string (#19004) * Fix isInvalidFilename segfault & deprecate the method * Update lib/pure/os.nim Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> * Update lib/pure/os.nim Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> --- lib/pure/os.nim | 91 +++++++++++++++++++++++++------------------------ 1 file changed, 46 insertions(+), 45 deletions(-) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index e3b8e9f1c..5320aa87e 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -39,8 +39,8 @@ const weirdTarget = defined(nimscript) or defined(js) since (1, 1): const invalidFilenameChars* = {'/', '\\', ':', '*', '?', '"', '<', '>', '|', '^', '\0'} ## \ - ## Characters that may produce invalid filenames across Linux, Windows, Mac, etc. - ## You can check if your filename contains these char and strip them for safety. + ## Characters that may produce invalid filenames across Linux, Windows and Mac. + ## You can check if your filename contains any of these chars and strip them for safety. ## Mac bans ``':'``, Linux bans ``'/'``, Windows bans all others. invalidFilenames* = [ "CON", "PRN", "AUX", "NUL", @@ -297,7 +297,7 @@ proc isAbsolute*(path: string): bool {.rtl, noSideEffect, extern: "nos$1", raise result = path[0] == '$' elif defined(posix) or defined(js): # `or defined(js)` wouldn't be needed pending https://github.com/nim-lang/Nim/issues/13469 - # This works around the problem for posix, but windows is still broken with nim js -d:nodejs + # This works around the problem for posix, but Windows is still broken with nim js -d:nodejs result = path[0] == '/' else: doAssert false # if ever hits here, adapt as needed @@ -321,7 +321,7 @@ when doslikeFileSystem: proc sameRoot(path1, path2: string): bool {.noSideEffect, raises: [].} = ## Return true if path1 and path2 have a same root. ## - ## Detail of windows path formats: + ## Detail of Windows path formats: ## https://docs.microsoft.com/en-us/dotnet/standard/io/file-path-formats assert(isAbsolute(path1)) @@ -370,7 +370,7 @@ proc relativePath*(path, base: string, sep = DirSep): string {. ## this can be useful to ensure the relative path only contains `'/'` ## so that it can be used for URL constructions. ## - ## On windows, if a root of `path` and a root of `base` are different, + ## On Windows, if a root of `path` and a root of `base` are different, ## returns `path` as is because it is impossible to make a relative path. ## That means an absolute path can be returned. ## @@ -558,27 +558,25 @@ iterator parentDirs*(path: string, fromRoot=false, inclusive=true): string = ## See also: ## * `parentDir proc <#parentDir,string>`_ ## - ## **Examples:** - ## - ## .. code-block:: - ## let g = "a/b/c" - ## - ## for p in g.parentDirs: - ## echo p - ## # a/b/c - ## # a/b - ## # a - ## - ## for p in g.parentDirs(fromRoot=true): - ## echo p - ## # a/ - ## # a/b/ - ## # a/b/c - ## - ## for p in g.parentDirs(inclusive=false): - ## echo p - ## # a/b - ## # a + runnableExamples: + let g = "a/b/c" + + for p in g.parentDirs: + echo p + # a/b/c + # a/b + # a + + for p in g.parentDirs(fromRoot=true): + echo p + # a/ + # a/b/ + # a/b/c + + for p in g.parentDirs(inclusive=false): + echo p + # a/b + # a if not fromRoot: var current = path @@ -944,8 +942,7 @@ proc getCacheDir*(): string = proc getCacheDir*(app: string): string = ## Returns the cache directory for an application `app`. ## - ## * On windows, this uses: `getCacheDir() / app / "cache"` - ## + ## * On Windows, this uses: `getCacheDir() / app / "cache"` ## * On other platforms, this uses: `getCacheDir() / app` when defined(windows): getCacheDir() / app / "cache" @@ -1988,7 +1985,7 @@ proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect], n proc tryMoveFSObject(source, dest: string, isDir: bool): bool {.noWeirdTarget.} = ## Moves a file (or directory if `isDir` is true) from `source` to `dest`. ## - ## Returns false in case of `EXDEV` error or `AccessDeniedError` on windows (if `isDir` is true). + ## Returns false in case of `EXDEV` error or `AccessDeniedError` on Windows (if `isDir` is true). ## In case of other errors `OSError` is raised. ## Returns true in case of success. when defined(windows): @@ -2144,7 +2141,7 @@ iterator walkPattern*(pattern: string): string {.tags: [ReadDirEffect], noWeirdT ## * `walkDirRec iterator <#walkDirRec.i,string>`_ runnableExamples: import std/sequtils - let paths = toSeq(walkPattern("lib/pure/*")) # works on windows too + let paths = toSeq(walkPattern("lib/pure/*")) # works on Windows too assert "lib/pure/concurrency".unixToNativePath in paths assert "lib/pure/os.nim".unixToNativePath in paths walkCommon(pattern, defaultWalkFilter) @@ -2163,7 +2160,7 @@ iterator walkFiles*(pattern: string): string {.tags: [ReadDirEffect], noWeirdTar ## * `walkDirRec iterator <#walkDirRec.i,string>`_ runnableExamples: import std/sequtils - assert "lib/pure/os.nim".unixToNativePath in toSeq(walkFiles("lib/pure/*.nim")) # works on windows too + assert "lib/pure/os.nim".unixToNativePath in toSeq(walkFiles("lib/pure/*.nim")) # works on Windows too walkCommon(pattern, isFile) iterator walkDirs*(pattern: string): string {.tags: [ReadDirEffect], noWeirdTarget.} = @@ -2180,7 +2177,7 @@ iterator walkDirs*(pattern: string): string {.tags: [ReadDirEffect], noWeirdTarg ## * `walkDirRec iterator <#walkDirRec.i,string>`_ runnableExamples: import std/sequtils - let paths = toSeq(walkDirs("lib/pure/*")) # works on windows too + let paths = toSeq(walkDirs("lib/pure/*")) # works on Windows too assert "lib/pure/concurrency".unixToNativePath in paths walkCommon(pattern, isDir) @@ -2278,7 +2275,9 @@ iterator walkDir*(dir: string; relative = false, checkDir = false): ## If `checkDir` is true, `OSError` is raised when `dir` ## doesn't exist. ## - ## Example: This directory structure:: + ## **Example:** + ## + ## This directory structure:: ## dirA / dirB / fileB1.txt ## / dirC ## / fileA1.txt @@ -2291,7 +2290,6 @@ iterator walkDir*(dir: string; relative = false, checkDir = false): # this also works at compile time assert collect(for k in walkDir("dirA"): k.path).join(" ") == "dirA/dirB dirA/dirC dirA/fileA2.txt dirA/fileA1.txt" - ## ## See also: ## * `walkPattern iterator <#walkPattern.i,string>`_ ## * `walkFiles iterator <#walkFiles.i,string>`_ @@ -3271,7 +3269,7 @@ template rawToFormalFileInfo(rawInfo, path, formalInfo): untyped = formalInfo.lastAccessTime = fromWinTime(rdFileTime(rawInfo.ftLastAccessTime)) formalInfo.lastWriteTime = fromWinTime(rdFileTime(rawInfo.ftLastWriteTime)) formalInfo.creationTime = fromWinTime(rdFileTime(rawInfo.ftCreationTime)) - formalInfo.blockSize = 8192 # xxx use windows API instead of hardcoding + formalInfo.blockSize = 8192 # xxx use Windows API instead of hardcoding # Retrieve basic permissions if (rawInfo.dwFileAttributes and FILE_ATTRIBUTE_READONLY) != 0'i32: @@ -3494,27 +3492,30 @@ proc setLastModificationTime*(file: string, t: times.Time) {.noWeirdTarget.} = discard h.closeHandle if res == 0'i32: raiseOSError(osLastError(), file) -func isValidFilename*(filename: string, maxLen = 259.Positive): bool {.since: (1, 1).} = +func isValidFilename*(filename: string, maxLen = 259.Positive): bool {.since: (1, 1), deprecated: "Deprecated since v1.5.1".} = ## Returns true if ``filename`` is valid for crossplatform use. ## ## This is useful if you want to copy or save files across Windows, Linux, Mac, etc. ## You can pass full paths as argument too, but func only checks filenames. - ## It uses ``invalidFilenameChars``, ``invalidFilenames`` and ``maxLen`` to verify the specified ``filename``. ## - ## .. code-block:: nim - ## assert not isValidFilename(" foo") ## Leading white space - ## assert not isValidFilename("foo ") ## Trailing white space - ## assert not isValidFilename("foo.") ## Ends with Dot - ## assert not isValidFilename("con.txt") ## "CON" is invalid (Windows) - ## assert not isValidFilename("OwO:UwU") ## ":" is invalid (Mac) - ## assert not isValidFilename("aux.bat") ## "AUX" is invalid (Windows) + ## It uses `invalidFilenameChars`, `invalidFilenames` and `maxLen` to verify the specified `filename`. ## + runnableExamples: + assert not isValidFilename(" foo") # Leading white space + assert not isValidFilename("foo ") # Trailing white space + assert not isValidFilename("foo.") # Ends with dot + assert not isValidFilename("con.txt") # "CON" is invalid (Windows) + assert not isValidFilename("OwO:UwU") # ":" is invalid (Mac) + assert not isValidFilename("aux.bat") # "AUX" is invalid (Windows) + assert not isValidFilename("") # Empty string + assert not isValidFilename("foo/") # Filename is empty + # https://docs.microsoft.com/en-us/dotnet/api/system.io.pathtoolongexception # https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file # https://msdn.microsoft.com/en-us/library/windows/desktop/aa365247%28v=vs.85%29.aspx result = true let f = filename.splitFile() - if unlikely(f.name.len + f.ext.len > maxLen or + if unlikely(f.name.len + f.ext.len > maxLen or f.name.len == 0 or f.name[0] == ' ' or f.name[^1] == ' ' or f.name[^1] == '.' or find(f.name, invalidFilenameChars) != -1): return false for invalid in invalidFilenames: From 1a45da9150c6cf5da324e754f49dc4cf957f1180 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 21 Oct 2021 16:53:15 +0200 Subject: [PATCH 2596/3768] use two underscores for easy demangling [backport:1.6] (#19028) --- compiler/ccgtypes.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 492e1f181..23e1a5867 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -39,7 +39,7 @@ proc mangleName(m: BModule; s: PSym): Rope = result = s.loc.r if result == nil: result = s.name.s.mangle.rope - result.add "_" + result.add "__" result.add m.g.graph.ifaces[s.itemId.module].uniqueName result.add "_" result.add rope s.itemId.item From 5e2b9341f343c3f80f92cfd160a0186f23521a42 Mon Sep 17 00:00:00 2001 From: Danil Yarantsev Date: Thu, 21 Oct 2021 18:01:48 +0300 Subject: [PATCH 2597/3768] Add Elbrus 2000 architecture (#19024) * Add Elbrus 2000 architecture * Add e2k to niminst * Update compiler/installer.ini Co-authored-by: Andreas Rumpf --- compiler/platform.nim | 6 ++++-- lib/system/platforms.nim | 4 +++- tools/nim.zsh-completion | 1 + tools/niminst/buildsh.nimf | 2 ++ tools/niminst/makefile.nimf | 3 +++ 5 files changed, 13 insertions(+), 3 deletions(-) diff --git a/compiler/platform.nim b/compiler/platform.nim index 1bc00b629..18d43f006 100644 --- a/compiler/platform.nim +++ b/compiler/platform.nim @@ -197,7 +197,8 @@ type cpuNone, cpuI386, cpuM68k, cpuAlpha, cpuPowerpc, cpuPowerpc64, cpuPowerpc64el, cpuSparc, cpuVm, cpuHppa, cpuIa64, cpuAmd64, cpuMips, cpuMipsel, cpuArm, cpuArm64, cpuJS, cpuNimVM, cpuAVR, cpuMSP430, - cpuSparc64, cpuMips64, cpuMips64el, cpuRiscV32, cpuRiscV64, cpuEsp, cpuWasm32 + cpuSparc64, cpuMips64, cpuMips64el, cpuRiscV32, cpuRiscV64, cpuEsp, cpuWasm32, + cpuE2k type TInfoCPU* = tuple[name: string, intSize: int, endian: Endianness, @@ -232,7 +233,8 @@ const (name: "riscv32", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32), (name: "riscv64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64), (name: "esp", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32), - (name: "wasm32", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32)] + (name: "wasm32", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32), + (name: "e2k", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64)] type Target* = object diff --git a/lib/system/platforms.nim b/lib/system/platforms.nim index bd6541b6a..5bca8cb1c 100644 --- a/lib/system/platforms.nim +++ b/lib/system/platforms.nim @@ -35,7 +35,8 @@ type msp430, ## TI MSP430 microcontroller riscv32, ## RISC-V 32-bit processor riscv64, ## RISC-V 64-bit processor - wasm32 ## WASM, 32-bit + wasm32, ## WASM, 32-bit + e2k ## MCST Elbrus 2000 OsPlatform* {.pure.} = enum ## the OS this program will run on. none, dos, windows, os2, linux, morphos, skyos, solaris, @@ -93,5 +94,6 @@ const elif defined(riscv32): CpuPlatform.riscv32 elif defined(riscv64): CpuPlatform.riscv64 elif defined(wasm32): CpuPlatform.wasm32 + elif defined(e2k): CpuPlatform.e2k else: CpuPlatform.none ## the CPU this program will run on. diff --git a/tools/nim.zsh-completion b/tools/nim.zsh-completion index f7aeb72c6..07a221e9c 100644 --- a/tools/nim.zsh-completion +++ b/tools/nim.zsh-completion @@ -74,6 +74,7 @@ _nim() { '*--cpu=arm64[compile for ARM64 architecture]' \ '*--cpu=avr[compile for AVR architecture]' \ '*--cpu=esp[compile for ESP architecture]' \ + '*--cpu=e2k[compile for Elbrus 2000 architecture]' \ '*--cpu=hppa[compile for HPPA architecture]' \ '*--cpu=i386[compile for i386 architecture]' \ '*--cpu=ia64[compile for ia64 architecture]' \ diff --git a/tools/niminst/buildsh.nimf b/tools/niminst/buildsh.nimf index 5f68a6a17..b81ac731f 100644 --- a/tools/niminst/buildsh.nimf +++ b/tools/niminst/buildsh.nimf @@ -227,6 +227,8 @@ case $ucpu in mycpu="arm" ;; *riscv64|riscv* ) mycpu="riscv64" ;; + *e2k* ) + mycpu="e2k" ;; *) echo 2>&1 "Error: unknown processor: $ucpu" exit 1 diff --git a/tools/niminst/makefile.nimf b/tools/niminst/makefile.nimf index 69e87b837..c4f2f0e79 100644 --- a/tools/niminst/makefile.nimf +++ b/tools/niminst/makefile.nimf @@ -166,6 +166,9 @@ endif ifeq ($(ucpu),riscv64) mycpu = riscv64 endif +ifeq ($(ucpu),e2k) + mycpu = e2k +endif ifndef mycpu $(error unknown CPU architecture: $(ucpu) See makefile.nimf) endif From d6345874236e03d21f4f6acbad34546ce27052b7 Mon Sep 17 00:00:00 2001 From: Abishek PY <43115551+vj-abishek@users.noreply.github.com> Date: Fri, 22 Oct 2021 15:40:32 +0530 Subject: [PATCH 2598/3768] feat: copy to clipboard (#18963) * feat: copy to clipboard * fix: CI failure related issue * fix: CI failure issue * fix: copy to clipboard button bug * feat: copy pragmadots value to clipboard --- doc/nimdoc.css | 32 ++++++++++++- nimdoc/testproject/expected/nimdoc.out.css | 32 ++++++++++++- tools/dochack/dochack.nim | 56 ++++++++++++++++++++++ 3 files changed, 118 insertions(+), 2 deletions(-) diff --git a/doc/nimdoc.css b/doc/nimdoc.css index 4abea9ce0..3353f1cda 100644 --- a/doc/nimdoc.css +++ b/doc/nimdoc.css @@ -38,6 +38,10 @@ Modified by Boyd Greenfield and narimiran --program: #6060c0; --option: #508000; --raw-data: #a4255b; + + --clipboard-image-normal: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: black' fill='none' viewBox='0 0 24 24' stroke='currentColor'%3E %3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2' /%3E %3C/svg%3E"); + --clipboard-image-selected: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: black' viewBox='0 0 20 20' fill='currentColor'%3E %3Cpath d='M8 3a1 1 0 011-1h2a1 1 0 110 2H9a1 1 0 01-1-1z' /%3E %3Cpath d='M6 3a2 2 0 00-2 2v11a2 2 0 002 2h8a2 2 0 002-2V5a2 2 0 00-2-2 3 3 0 01-3 3H9a3 3 0 01-3-3z' /%3E %3C/svg%3E"); + --clipboard-image: var(--clipboard-image-normal) } [data-theme="dark"] { @@ -68,6 +72,10 @@ Modified by Boyd Greenfield and narimiran --program: #9090c0; --option: #90b010; --raw-data: #8be9fd; + + --clipboard-image-normal: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: lightgray' fill='none' viewBox='0 0 24 24' stroke='currentColor'%3E %3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2' /%3E %3C/svg%3E"); + --clipboard-image-selected: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: lightgray' viewBox='0 0 20 20' fill='currentColor'%3E %3Cpath d='M8 3a1 1 0 011-1h2a1 1 0 110 2H9a1 1 0 01-1-1z' /%3E %3Cpath d='M6 3a2 2 0 00-2 2v11a2 2 0 002 2h8a2 2 0 002-2V5a2 2 0 00-2-2 3 3 0 01-3 3H9a3 3 0 01-3-3z' /%3E %3C/svg%3E"); + --clipboard-image: var(--clipboard-image-normal); } .theme-switch-wrapper { @@ -572,6 +580,10 @@ span.tok { margin-right: 0.2em; } +.copyToClipBoard { + position: relative; +} + pre { font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace; color: var(--text); @@ -590,7 +602,25 @@ pre { border: 1px solid var(--border); -webkit-border-radius: 6px; -moz-border-radius: 6px; - border-radius: 6px; } + border-radius: 6px; +} + +.copyToClipBoardBtn { + visibility: hidden; + position: absolute; + width: 24px; + border-radius: 4px; + background-image: var(--clipboard-image); + right: 5px; + top: 13px; + background-color: var(--secondary-background); + padding: 11px; + border: 0; +} + +.copyToClipBoard:hover .copyToClipBoardBtn { + visibility: visible; +} .pre-scrollable { max-height: 340px; diff --git a/nimdoc/testproject/expected/nimdoc.out.css b/nimdoc/testproject/expected/nimdoc.out.css index 4abea9ce0..3353f1cda 100644 --- a/nimdoc/testproject/expected/nimdoc.out.css +++ b/nimdoc/testproject/expected/nimdoc.out.css @@ -38,6 +38,10 @@ Modified by Boyd Greenfield and narimiran --program: #6060c0; --option: #508000; --raw-data: #a4255b; + + --clipboard-image-normal: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: black' fill='none' viewBox='0 0 24 24' stroke='currentColor'%3E %3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2' /%3E %3C/svg%3E"); + --clipboard-image-selected: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: black' viewBox='0 0 20 20' fill='currentColor'%3E %3Cpath d='M8 3a1 1 0 011-1h2a1 1 0 110 2H9a1 1 0 01-1-1z' /%3E %3Cpath d='M6 3a2 2 0 00-2 2v11a2 2 0 002 2h8a2 2 0 002-2V5a2 2 0 00-2-2 3 3 0 01-3 3H9a3 3 0 01-3-3z' /%3E %3C/svg%3E"); + --clipboard-image: var(--clipboard-image-normal) } [data-theme="dark"] { @@ -68,6 +72,10 @@ Modified by Boyd Greenfield and narimiran --program: #9090c0; --option: #90b010; --raw-data: #8be9fd; + + --clipboard-image-normal: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: lightgray' fill='none' viewBox='0 0 24 24' stroke='currentColor'%3E %3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2' /%3E %3C/svg%3E"); + --clipboard-image-selected: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' style='color: lightgray' viewBox='0 0 20 20' fill='currentColor'%3E %3Cpath d='M8 3a1 1 0 011-1h2a1 1 0 110 2H9a1 1 0 01-1-1z' /%3E %3Cpath d='M6 3a2 2 0 00-2 2v11a2 2 0 002 2h8a2 2 0 002-2V5a2 2 0 00-2-2 3 3 0 01-3 3H9a3 3 0 01-3-3z' /%3E %3C/svg%3E"); + --clipboard-image: var(--clipboard-image-normal); } .theme-switch-wrapper { @@ -572,6 +580,10 @@ span.tok { margin-right: 0.2em; } +.copyToClipBoard { + position: relative; +} + pre { font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace; color: var(--text); @@ -590,7 +602,25 @@ pre { border: 1px solid var(--border); -webkit-border-radius: 6px; -moz-border-radius: 6px; - border-radius: 6px; } + border-radius: 6px; +} + +.copyToClipBoardBtn { + visibility: hidden; + position: absolute; + width: 24px; + border-radius: 4px; + background-image: var(--clipboard-image); + right: 5px; + top: 13px; + background-color: var(--secondary-background); + padding: 11px; + border: 0; +} + +.copyToClipBoard:hover .copyToClipBoardBtn { + visibility: visible; +} .pre-scrollable { max-height: 340px; diff --git a/tools/dochack/dochack.nim b/tools/dochack/dochack.nim index 4a491cf88..83e81f1c0 100644 --- a/tools/dochack/dochack.nim +++ b/tools/dochack/dochack.nim @@ -341,3 +341,59 @@ proc search*() {.exportc.} = if timer != nil: clearTimeout(timer) timer = setTimeout(wrapper, 400) + +proc copyToClipboard*() {.exportc.} = + {.emit: """ + + function updatePreTags() { + + const allPreTags = document.querySelectorAll("pre") + + allPreTags.forEach((e) => { + + const div = document.createElement("div") + div.classList.add("copyToClipBoard") + + const preTag = document.createElement("pre") + preTag.innerHTML = e.innerHTML + + const button = document.createElement("button") + button.value = e.textContent.replace('...', '') + button.classList.add("copyToClipBoardBtn") + + div.appendChild(preTag) + div.appendChild(button) + + e.outerHTML = div.outerHTML + + }) + } + + + function copyTextToClipboard(e) { + const clipBoardContent = e.target.value + navigator.clipboard.writeText(clipBoardContent).then(function() { + e.target.style.setProperty("--clipboard-image", "var(--clipboard-image-selected)") + }, function(err) { + console.error("Could not copy text: ", err); + }); + } + + window.addEventListener("click", (e) => { + if (e.target.classList.contains("copyToClipBoardBtn")) { + copyTextToClipboard(e) + } + }) + + window.addEventListener("mouseover", (e) => { + if (e.target.nodeName === "PRE") { + e.target.nextElementSibling.style.setProperty("--clipboard-image", "var(--clipboard-image-normal)") + } + }) + + window.addEventListener("DOMContentLoaded", updatePreTags) + + """ + .} + +copyToClipboard() \ No newline at end of file From 4d8108046b71b6e40d88cde7496e687e68116877 Mon Sep 17 00:00:00 2001 From: flywind Date: Fri, 22 Oct 2021 18:15:08 +0800 Subject: [PATCH 2599/3768] remove exception (#18906) --- compiler/semfold.nim | 8 +------- tests/system/tdollars.nim | 7 +++++++ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 1f8efc4de..5ae91d306 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -292,13 +292,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; idgen: IdGenerator; g: ModuleGraph): P else: result = newStrNodeT("true", n, g) of mFloatToStr: result = newStrNodeT($getFloat(a), n, g) of mCStrToStr, mCharToStr: - if a.kind == nkBracket: - var s = "" - for b in a.sons: - s.add b.getStrOrChar - result = newStrNodeT(s, n, g) - else: - result = newStrNodeT(getStrOrChar(a), n, g) + result = newStrNodeT(getStrOrChar(a), n, g) of mStrToStr: result = newStrNodeT(getStrOrChar(a), n, g) of mEnumToStr: result = newStrNodeT(ordinalValToString(a, g), n, g) of mArrToSeq: diff --git a/tests/system/tdollars.nim b/tests/system/tdollars.nim index 17d195e76..93fa5cb9e 100644 --- a/tests/system/tdollars.nim +++ b/tests/system/tdollars.nim @@ -187,5 +187,12 @@ proc main()= test2 high(int8), "127" test2 low(int8), "-128" + block: + const + a: array[3, char] = ['N', 'i', 'm'] + aStr = $(a) + + doAssert aStr == """['N', 'i', 'm']""" + static: main() main() From 490c4226a55e8abd7cb504d4e24fcb714bff23f1 Mon Sep 17 00:00:00 2001 From: Etan Kissling Date: Fri, 22 Oct 2021 17:32:02 +0200 Subject: [PATCH 2600/3768] allow converting static vars to `openArray` (#19035) When assigning constant output to a seq, and then passing that static seq to other functions that take `openArray`, the compiler may end up producing errors, as it does not know how to convert `static[seq[T]]` to `openArray[T]`. By ignoring the `static` wrapper on the type for the purpose of determining data memory location and length, this gets resolved cleanly. Unfortunately, it is relatively tricky to come up with a minimal example, as there are followup problems from the failing conversion, e.g., this may lead to `internal error: inconsistent environment type`, instead of the relevant `openArrayLoc` error message. --- compiler/ccgcalls.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 12f366a53..fe0516f9a 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -214,7 +214,7 @@ proc openArrayLoc(p: BProc, formalType: PType, n: PNode): Rope = else: var a: TLoc initLocExpr(p, if n.kind == nkHiddenStdConv: n[1] else: n, a) - case skipTypes(a.t, abstractVar).kind + case skipTypes(a.t, abstractVar+{tyStatic}).kind of tyOpenArray, tyVarargs: if reifiedOpenArray(n): if a.t.kind in {tyVar, tyLent}: From 6621e1a3ead7166fa116bfbb8f0d2acb4e318d81 Mon Sep 17 00:00:00 2001 From: BarrOff <58253563+BarrOff@users.noreply.github.com> Date: Fri, 22 Oct 2021 23:42:54 +0000 Subject: [PATCH 2601/3768] use the correct header for TIOCGWINSZ on Solaris (#19037) --- lib/posix/termios.nim | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/posix/termios.nim b/lib/posix/termios.nim index b03ce74f3..f755c720d 100644 --- a/lib/posix/termios.nim +++ b/lib/posix/termios.nim @@ -237,8 +237,11 @@ proc tcFlow*(fd: cint; action: cint): cint {.importc: "tcflow", header: "".} # Get process group ID for session leader for controlling terminal FD. -# Window size ioctl. Should work on on any Unix that xterm has been ported to. -var TIOCGWINSZ*{.importc, header: "".}: culong +# Window size ioctl. Solaris based systems have an uncommen place for this. +when defined(solaris) or defined(sunos): + var TIOCGWINSZ*{.importc, header: "".}: culong +else: + var TIOCGWINSZ*{.importc, header: "".}: culong when defined(nimHasStyleChecks): {.push styleChecks: off.} From 41d99185918923063bd6ae34898c8ac18a941169 Mon Sep 17 00:00:00 2001 From: BarrOff <58253563+BarrOff@users.noreply.github.com> Date: Sun, 24 Oct 2021 05:24:31 +0000 Subject: [PATCH 2602/3768] use `gmake` in the buildscript on Solaris systems (#19036) --- ci/funs.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ci/funs.sh b/ci/funs.sh index 1638931c6..8e7f8faf7 100644 --- a/ci/funs.sh +++ b/ci/funs.sh @@ -76,6 +76,8 @@ _nimBuildCsourcesIfNeeded(){ makeX=gmake elif [ "$unamestr" = 'CROSSOS' ]; then makeX=gmake + elif [ "$unamestr" = 'SunOS' ]; then + makeX=gmake else makeX=make fi From 141b76e36519219915ada9086d1c9b1d0b465659 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Sun, 24 Oct 2021 09:51:57 +0000 Subject: [PATCH 2603/3768] Add Zephyr Support (#19003) * Porting Nim to run on Zephyr. Includes changes to `std/net`. Squashed commit of the following: tweaking more memory / malloc things revert back bitmasks tweaking nim to use kernel heap as C malloc doesn't work fixing socket polling on zephyr cleanup getting maximum sockets for process or for rtos'es reorganizing and fixing net for async / system merge netlite changes back into nativesockets merge netlite changes back into nativesockets reverting native sockets back tweaking nim / zephyr network adding option to run 'net-lite' from linux bridging zephyr's max connections fixing net errors fixing compilation with getAddrString fixing compilation with getAddrString experimenting with a nativesockets_lite ... getAddrString experimenting with a nativesockets_lite ... getAddrString experimenting with a nativesockets_lite ... getLocalAddr experimenting with a nativesockets_lite ... getLocalAddr experimenting with a nativesockets_lite ... add note regarding incorrect FreeRTOS Sockadd_in fields changing to NIM_STATIC_ASSERT cleaning up the static_assert error messages cleaning up the static_assert error messages setting up static assert ftw! testing compile time asserts reworking Sockaddr objects to more closely match various platforms reworking Sockaddr objects to more closely match various platforms reworking Sockaddr objects to more closely match various platforms finding missing items (issue #18684) fixup posix constants (issue #18684) adding plumbing for zephyr os (issue #18684) adding plumbing for zephyr os (issue #18684) * fixing constant capitalizations * remove extra debug prints and fix TSa_Family/cint issue * remove extra debug prints and fix TSa_Family/cint issue * Porting Nim to run on Zephyr. Includes changes to `std/net`. Squashed commit of the following: tweaking more memory / malloc things revert back bitmasks tweaking nim to use kernel heap as C malloc doesn't work fixing socket polling on zephyr cleanup getting maximum sockets for process or for rtos'es reorganizing and fixing net for async / system merge netlite changes back into nativesockets merge netlite changes back into nativesockets reverting native sockets back tweaking nim / zephyr network adding option to run 'net-lite' from linux bridging zephyr's max connections fixing net errors fixing compilation with getAddrString fixing compilation with getAddrString experimenting with a nativesockets_lite ... getAddrString experimenting with a nativesockets_lite ... getAddrString experimenting with a nativesockets_lite ... getLocalAddr experimenting with a nativesockets_lite ... getLocalAddr experimenting with a nativesockets_lite ... add note regarding incorrect FreeRTOS Sockadd_in fields changing to NIM_STATIC_ASSERT cleaning up the static_assert error messages cleaning up the static_assert error messages setting up static assert ftw! testing compile time asserts reworking Sockaddr objects to more closely match various platforms reworking Sockaddr objects to more closely match various platforms reworking Sockaddr objects to more closely match various platforms finding missing items (issue #18684) fixup posix constants (issue #18684) adding plumbing for zephyr os (issue #18684) adding plumbing for zephyr os (issue #18684) * fixing constant capitalizations * remove extra debug prints and fix TSa_Family/cint issue * remove extra debug prints and fix TSa_Family/cint issue * fixing PR issues * Porting Nim to run on Zephyr. Includes changes to `std/net`. Squashed commit of the following: tweaking more memory / malloc things revert back bitmasks tweaking nim to use kernel heap as C malloc doesn't work fixing socket polling on zephyr cleanup getting maximum sockets for process or for rtos'es reorganizing and fixing net for async / system merge netlite changes back into nativesockets merge netlite changes back into nativesockets reverting native sockets back tweaking nim / zephyr network adding option to run 'net-lite' from linux bridging zephyr's max connections fixing net errors fixing compilation with getAddrString fixing compilation with getAddrString experimenting with a nativesockets_lite ... getAddrString experimenting with a nativesockets_lite ... getAddrString experimenting with a nativesockets_lite ... getLocalAddr experimenting with a nativesockets_lite ... getLocalAddr experimenting with a nativesockets_lite ... add note regarding incorrect FreeRTOS Sockadd_in fields changing to NIM_STATIC_ASSERT cleaning up the static_assert error messages cleaning up the static_assert error messages setting up static assert ftw! testing compile time asserts reworking Sockaddr objects to more closely match various platforms reworking Sockaddr objects to more closely match various platforms reworking Sockaddr objects to more closely match various platforms finding missing items (issue #18684) fixup posix constants (issue #18684) adding plumbing for zephyr os (issue #18684) adding plumbing for zephyr os (issue #18684) * fixing constant capitalizations * remove extra debug prints and fix TSa_Family/cint issue * remove extra debug prints and fix TSa_Family/cint issue * Remerge * fixing constant capitalizations * remove extra debug prints and fix TSa_Family/cint issue * remove extra debug prints and fix TSa_Family/cint issue * fixing PR issues * fix maxDescriptors on zephyr/freertos * move maxDescriptors to selector.nim -- fixes compile issue * change realloc impl on zephyr to match ansi c behavior * change realloc impl on zephyr to match ansi c behavior * force compileOnly mode for tlwip Co-authored-by: Jaremy J. Creechley Co-authored-by: Jaremy Creechley --- compiler/options.nim | 4 +- compiler/platform.nim | 6 +- lib/posix/posix.nim | 6 +- lib/posix/posix_freertos_consts.nim | 2 + lib/posix/posix_other.nim | 94 ++-- lib/posix/posix_other_consts.nim | 12 +- lib/pure/asyncdispatch.nim | 7 +- lib/pure/asyncnet.nim | 14 +- lib/pure/ioselects/ioselectors_epoll.nim | 5 +- lib/pure/ioselects/ioselectors_poll.nim | 5 +- lib/pure/ioselects/ioselectors_select.nim | 2 +- lib/pure/nativesockets.nim | 615 ++++++++++++---------- lib/pure/net.nim | 17 +- lib/pure/selectors.nim | 19 + lib/system/ansi_c.nim | 31 +- lib/system/bitmasks.nim | 1 + lib/system/dyncalls.nim | 2 +- lib/system/io.nim | 9 +- lib/system/mm/malloc.nim | 15 +- tests/stdlib/tlwip.nim | 2 +- 20 files changed, 522 insertions(+), 346 deletions(-) diff --git a/compiler/options.nim b/compiler/options.nim index d3f868316..2feaa2d43 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -595,7 +595,7 @@ proc isDefined*(conf: ConfigRef; symbol: string): bool = osQnx, osAtari, osAix, osHaiku, osVxWorks, osSolaris, osNetbsd, osFreebsd, osOpenbsd, osDragonfly, osMacosx, osIos, - osAndroid, osNintendoSwitch, osFreeRTOS, osCrossos} + osAndroid, osNintendoSwitch, osFreeRTOS, osCrossos, osZephyr} of "linux": result = conf.target.targetOS in {osLinux, osAndroid} of "bsd": @@ -615,6 +615,8 @@ proc isDefined*(conf: ConfigRef; symbol: string): bool = result = conf.target.targetOS == osNintendoSwitch of "freertos", "lwip": result = conf.target.targetOS == osFreeRTOS + of "zephyr": + result = conf.target.targetOS == osZephyr of "littleendian": result = CPU[conf.target.targetCPU].endian == littleEndian of "bigendian": result = CPU[conf.target.targetCPU].endian == bigEndian of "cpu8": result = CPU[conf.target.targetCPU].bit == 8 diff --git a/compiler/platform.nim b/compiler/platform.nim index 18d43f006..eb1398677 100644 --- a/compiler/platform.nim +++ b/compiler/platform.nim @@ -22,7 +22,7 @@ type osNone, osDos, osWindows, osOs2, osLinux, osMorphos, osSkyos, osSolaris, osIrix, osNetbsd, osFreebsd, osOpenbsd, osDragonfly, osCrossos, osAix, osPalmos, osQnx, osAmiga, osAtari, osNetware, osMacos, osMacosx, osIos, osHaiku, osAndroid, osVxWorks - osGenode, osJS, osNimVM, osStandalone, osNintendoSwitch, osFreeRTOS, osAny + osGenode, osJS, osNimVM, osStandalone, osNintendoSwitch, osFreeRTOS, osZephyr, osAny type TInfoOSProp* = enum @@ -185,6 +185,10 @@ const objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/", scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".", props: {ospPosix}), + (name: "Zephyr", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/", + objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/", + scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".", + props: {ospPosix}), (name: "Any", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/", objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/", scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".", diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index 45fe00d5d..c2504f994 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -151,8 +151,10 @@ proc htons*(a1: uint16): uint16 {.importc, header: "".} proc ntohl*(a1: uint32): uint32 {.importc, header: "".} proc ntohs*(a1: uint16): uint16 {.importc, header: "".} -proc inet_addr*(a1: cstring): InAddrT {.importc, header: "".} -proc inet_ntoa*(a1: InAddr): cstring {.importc, header: "".} +when not defined(zephyr): + proc inet_addr*(a1: cstring): InAddrT {.importc, header: "".} + proc inet_ntoa*(a1: InAddr): cstring {.importc, header: "".} + proc inet_ntop*(a1: cint, a2: pointer, a3: cstring, a4: int32): cstring {. importc:"(char *)$1", header: "".} proc inet_pton*(a1: cint, a2: cstring, a3: pointer): cint {. diff --git a/lib/posix/posix_freertos_consts.nim b/lib/posix/posix_freertos_consts.nim index efe87aacf..ca500534a 100644 --- a/lib/posix/posix_freertos_consts.nim +++ b/lib/posix/posix_freertos_consts.nim @@ -363,6 +363,8 @@ var SEM_FAILED* {.importc: "SEM_FAILED", header: "".}: pointer # # var RLIMIT_NOFILE* {.importc: "RLIMIT_NOFILE", header: "".}: cint +var FD_MAX* {.importc: "CONFIG_LWIP_MAX_SOCKETS", header: "".}: cint + # var FD_SETSIZE* {.importc: "FD_SETSIZE", header: "".}: cint diff --git a/lib/posix/posix_other.nim b/lib/posix/posix_other.nim index 6584bfab2..688867b98 100644 --- a/lib/posix/posix_other.nim +++ b/lib/posix/posix_other.nim @@ -10,7 +10,7 @@ when defined(nimHasStyleChecks): {.push styleChecks: off.} -when defined(freertos): +when defined(freertos) or defined(zephyr): const hasSpawnH = false # should exist for every Posix system nowadays hasAioH = false @@ -391,9 +391,29 @@ when hasSpawnH: header: "", final, pure.} = object when defined(linux): + const Sockaddr_max_length* = 255 # from sys/un.h const Sockaddr_un_path_length* = 108 +elif defined(zephyr): + when defined(net_ipv6): + const Sockaddr_max_length* = 24 + elif defined(net_raw): + const Sockaddr_max_length* = 20 + else: + const Sockaddr_max_length* = 8 + const Sockaddr_un_path_length* = Sockaddr_max_length + # Zephyr is heavily customizable so it's easy to get to a state + # where Nim & Zephyr IPv6 settings are out of sync, causing painful runtime failures. + {.emit: ["NIM_STATIC_ASSERT(NET_SOCKADDR_MAX_SIZE == ", + Sockaddr_max_length, + ",\"NET_SOCKADDR_MAX_SIZE and Sockaddr_max_length size mismatch!", + " Check that Nim and Zephyr IPv4/IPv6 settings match.", + " Try adding -d:net_ipv6 to enable IPv6 for Nim on Zephyr.\" );"].} +elif defined(freertos) or defined(lwip): + const Sockaddr_max_length* = 14 + const Sockaddr_un_path_length* = 108 else: + const Sockaddr_max_length* = 255 # according to http://pubs.opengroup.org/onlinepubs/009604499/basedefs/sys/un.h.html # this is >=92 const Sockaddr_un_path_length* = 92 @@ -408,48 +428,50 @@ when defined(lwip): pure, final.} = object ## struct sockaddr sa_len*: uint8 ## Address family. sa_family*: TSa_Family ## Address family. - sa_data*: array[0..255, char] ## Socket address (variable-length data). + sa_data*: array[0..Sockaddr_max_length-sizeof(uint8)-sizeof(TSa_Family), char] ## Socket address (variable-length data). + + Sockaddr_storage* {.importc: "struct sockaddr_storage", + header: "", + pure, final.} = object ## struct sockaddr_storage + s2_len*: uint8 ## Address family. + ss_family*: TSa_Family ## Address family. + s2_data1*: array[2, char] ## Address family. + s2_data2*: array[3, uint32] ## Address family. + when defined(lwip6) or defined(net_ipv6): + s2_data3*: array[3, uint32] ## Address family. +elif defined(zephyr): + type + SockAddr* {.importc: "struct sockaddr", header: "", + pure, final.} = object ## struct sockaddr + sa_family*: TSa_Family ## Address family. + data*: array[0..Sockaddr_max_length-sizeof(TSa_Family), char] ## Socket address (variable-length data). + + Sockaddr_storage* {.importc: "struct sockaddr_storage", + header: "", + pure, final.} = object ## struct sockaddr_storage + ss_family*: TSa_Family ## Address family. + data*: array[0..Sockaddr_max_length-sizeof(TSa_Family), char] ## Socket address (variable-length data). + {.emit: ["NIM_STATIC_ASSERT(sizeof(struct sockaddr) == ", sizeof(Sockaddr), ",\"struct size mismatch\" );"].} + {.emit: ["NIM_STATIC_ASSERT(sizeof(struct sockaddr_storage) == ", sizeof(Sockaddr_storage), ",\"struct size mismatch\" );"].} else: type SockAddr* {.importc: "struct sockaddr", header: "", pure, final.} = object ## struct sockaddr sa_family*: TSa_Family ## Address family. - sa_data*: array[0..255, char] ## Socket address (variable-length data). + sa_data*: array[0..Sockaddr_max_length-sizeof(TSa_Family), char] ## Socket address (variable-length data). + + Sockaddr_storage* {.importc: "struct sockaddr_storage", + header: "", + pure, final.} = object ## struct sockaddr_storage + ss_family*: TSa_Family ## Address family. type Sockaddr_un* {.importc: "struct sockaddr_un", header: "", pure, final.} = object ## struct sockaddr_un sun_family*: TSa_Family ## Address family. - sun_path*: array[0..Sockaddr_un_path_length-1, char] ## Socket path + sun_path*: array[0..Sockaddr_un_path_length-sizeof(TSa_Family), char] ## Socket path -when defined(lwip): - when not defined(lwip6): - type - Sockaddr_storage* {.importc: "struct sockaddr_storage", - header: "", - pure, final.} = object ## struct sockaddr_storage - s2_len*: uint8 ## Address family. - ss_family*: TSa_Family ## Address family. - s2_data1*: array[2, char] ## Address family. - s2_data2*: array[3, uint32] ## Address family. - else: - type - Sockaddr_storage* {.importc: "struct sockaddr_storage", - header: "", - pure, final.} = object ## struct sockaddr_storage - s2_len*: uint8 ## Address family. - ss_family*: TSa_Family ## Address family. - s2_data1*: array[2, char] ## Address family. - s2_data2*: array[3, uint32] ## Address family. - s2_data3*: array[3, uint32] ## Address family. -else: - type - Sockaddr_storage* {.importc: "struct sockaddr_storage", - header: "", - pure, final.} = object ## struct sockaddr_storage - ss_family*: TSa_Family ## Address family. - type Tif_nameindex* {.importc: "struct if_nameindex", final, pure, header: "".} = object ## struct if_nameindex @@ -494,6 +516,7 @@ type header: "".} = object ## struct in_addr s_addr*: InAddrScalar + # TODO: Fixme for FreeRTOS/LwIP, these are incorrect Sockaddr_in* {.importc: "struct sockaddr_in", pure, final, header: "".} = object ## struct sockaddr_in sin_family*: TSa_Family ## AF_INET. @@ -577,7 +600,12 @@ when not defined(lwip): events*: cshort ## The input event flags (see below). revents*: cshort ## The output event flags (see below). - Tnfds* {.importc: "nfds_t", header: "".} = cint + when defined(zephyr): + type + Tnfds* = distinct cint + else: + type + Tnfds* {.importc: "nfds_t", header: "".} = cint var errno* {.importc, header: "".}: cint ## error variable @@ -625,7 +653,7 @@ elif defined(solaris): # Solaris doesn't have MSG_NOSIGNAL const MSG_NOSIGNAL* = 0'i32 -elif defined(freertos) or defined(lwip): +elif defined(zephyr) or defined(freertos) or defined(lwip): # LwIP/FreeRTOS doesn't have MSG_NOSIGNAL const MSG_NOSIGNAL* = 0x20'i32 diff --git a/lib/posix/posix_other_consts.nim b/lib/posix/posix_other_consts.nim index f43407b40..59ac6f9ed 100644 --- a/lib/posix/posix_other_consts.nim +++ b/lib/posix/posix_other_consts.nim @@ -462,6 +462,9 @@ var RLIMIT_NOFILE* {.importc: "RLIMIT_NOFILE", header: "".}: cin # var FD_SETSIZE* {.importc: "FD_SETSIZE", header: "".}: cint +when defined(zephyr): + # Zephyr specific hardcoded value + var FD_MAX* {.importc: "CONFIG_POSIX_MAX_FDS ", header: "".}: cint # var MSG_CTRUNC* {.importc: "MSG_CTRUNC", header: "".}: cint @@ -487,10 +490,15 @@ var SO_SNDTIMEO* {.importc: "SO_SNDTIMEO", header: "".}: cint var SO_TYPE* {.importc: "SO_TYPE", header: "".}: cint var SOCK_DGRAM* {.importc: "SOCK_DGRAM", header: "".}: cint var SOCK_RAW* {.importc: "SOCK_RAW", header: "".}: cint -var SOCK_SEQPACKET* {.importc: "SOCK_SEQPACKET", header: "".}: cint +when defined(zephyr): + const SOCK_SEQPACKET* = cint(5) + var SOMAXCONN* {.importc: "CONFIG_NET_SOCKETS_POLL_MAX", header: "".}: cint +else: + var SOCK_SEQPACKET* {.importc: "SOCK_SEQPACKET", header: "".}: cint + var SOMAXCONN* {.importc: "SOMAXCONN", header: "".}: cint + var SOCK_STREAM* {.importc: "SOCK_STREAM", header: "".}: cint var SOL_SOCKET* {.importc: "SOL_SOCKET", header: "".}: cint -var SOMAXCONN* {.importc: "SOMAXCONN", header: "".}: cint var MSG_PEEK* {.importc: "MSG_PEEK", header: "".}: cint var MSG_TRUNC* {.importc: "MSG_TRUNC", header: "".}: cint var MSG_WAITALL* {.importc: "MSG_WAITALL", header: "".}: cint diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index d87e2360f..e15fb0851 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -1973,15 +1973,18 @@ proc activeDescriptors*(): int {.inline.} = when defined(posix): import posix -when defined(linux) or defined(windows) or defined(macosx) or defined(bsd): +when defined(linux) or defined(windows) or defined(macosx) or defined(bsd) or + defined(zephyr) or defined(freertos): proc maxDescriptors*(): int {.raises: OSError.} = ## Returns the maximum number of active file descriptors for the current ## process. This involves a system call. For now `maxDescriptors` is ## supported on the following OSes: Windows, Linux, OSX, BSD. when defined(windows): result = 16_700_000 + elif defined(zephyr) or defined(freertos): + result = FD_MAX else: var fdLim: RLimit if getrlimit(RLIMIT_NOFILE, fdLim) < 0: raiseOSError(osLastError()) - result = int(fdLim.rlim_cur) - 1 + result = int(fdLim.rlim_cur) - 1 \ No newline at end of file diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index 5523fa10e..abb4dd075 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -103,6 +103,7 @@ export SOBool # TODO: Remove duplication introduced by PR #4683. const defineSsl = defined(ssl) or defined(nimdoc) +const useNimNetLite = defined(nimNetLite) or defined(freertos) or defined(zephyr) when defineSsl: import openssl @@ -178,11 +179,12 @@ proc getLocalAddr*(socket: AsyncSocket): (string, Port) = ## This is high-level interface for `getsockname`:idx:. getLocalAddr(socket.fd, socket.domain) -proc getPeerAddr*(socket: AsyncSocket): (string, Port) = - ## Get the socket's peer address and port number. - ## - ## This is high-level interface for `getpeername`:idx:. - getPeerAddr(socket.fd, socket.domain) +when not useNimNetLite: + proc getPeerAddr*(socket: AsyncSocket): (string, Port) = + ## Get the socket's peer address and port number. + ## + ## This is high-level interface for `getpeername`:idx:. + getPeerAddr(socket.fd, socket.domain) proc newAsyncSocket*(domain, sockType, protocol: cint, buffered = true, @@ -655,7 +657,7 @@ proc hasDataBuffered*(s: AsyncSocket): bool {.since: (1, 5).} = # xxx dedup with std/net s.isBuffered and s.bufLen > 0 and s.currPos != s.bufLen -when defined(posix): +when defined(posix) and not useNimNetLite: proc connectUnix*(socket: AsyncSocket, path: string): owned(Future[void]) = ## Binds Unix socket to `path`. diff --git a/lib/pure/ioselects/ioselectors_epoll.nim b/lib/pure/ioselects/ioselectors_epoll.nim index b62b4c2db..05d9d19a8 100644 --- a/lib/pure/ioselects/ioselectors_epoll.nim +++ b/lib/pure/ioselects/ioselectors_epoll.nim @@ -73,10 +73,7 @@ type proc newSelector*[T](): Selector[T] = # Retrieve the maximum fd count (for current OS) via getrlimit() - var a = RLimit() - if getrlimit(posix.RLIMIT_NOFILE, a) != 0: - raiseOSError(osLastError()) - var maxFD = int(a.rlim_max) + var maxFD = maxDescriptors() doAssert(maxFD > 0) # Start with a reasonable size, checkFd() will grow this on demand const numFD = 1024 diff --git a/lib/pure/ioselects/ioselectors_poll.nim b/lib/pure/ioselects/ioselectors_poll.nim index 6b10e19ae..0d8fef78a 100644 --- a/lib/pure/ioselects/ioselectors_poll.nim +++ b/lib/pure/ioselects/ioselectors_poll.nim @@ -53,10 +53,7 @@ else: body proc newSelector*[T](): Selector[T] = - var a = RLimit() - if getrlimit(posix.RLIMIT_NOFILE, a) != 0: - raiseIOSelectorsError(osLastError()) - var maxFD = int(a.rlim_max) + var maxFD = maxDescriptors() when hasThreadSupport: result = cast[Selector[T]](allocShared0(sizeof(SelectorImpl[T]))) diff --git a/lib/pure/ioselects/ioselectors_select.nim b/lib/pure/ioselects/ioselectors_select.nim index 6f216ac85..88cbaf28c 100644 --- a/lib/pure/ioselects/ioselectors_select.nim +++ b/lib/pure/ioselects/ioselectors_select.nim @@ -313,7 +313,7 @@ proc selectInto*[T](s: Selector[T], timeout: int, verifySelectParams(timeout) if timeout != -1: - when defined(genode) or defined(freertos): + when defined(genode) or defined(freertos) or defined(zephyr): tv.tv_sec = Time(timeout div 1_000) else: tv.tv_sec = timeout.int32 div 1_000 diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim index 13c08dd92..4037d7181 100644 --- a/lib/pure/nativesockets.nim +++ b/lib/pure/nativesockets.nim @@ -21,6 +21,7 @@ when hostOS == "solaris": {.passl: "-lsocket -lnsl".} const useWinVersion = defined(windows) or defined(nimdoc) +const useNimNetLite = defined(nimNetLite) or defined(freertos) or defined(zephyr) when useWinVersion: import winlean @@ -35,9 +36,12 @@ else: export SocketHandle, Sockaddr_in, Addrinfo, INADDR_ANY, SockAddr, SockLen, Sockaddr_in6, Sockaddr_storage, - inet_ntoa, recv, `==`, connect, send, accept, recvfrom, sendto, + recv, `==`, connect, send, accept, recvfrom, sendto, freeAddrInfo +when not useNimNetLite: + export inet_ntoa + export SO_ERROR, SOL_SOCKET, @@ -332,125 +336,6 @@ template htons*(x: uint16): untyped = ## order, this is a no-op; otherwise, it performs a 2-byte swap operation. nativesockets.ntohs(x) -proc getServByName*(name, proto: string): Servent {.tags: [ReadIOEffect].} = - ## Searches the database from the beginning and finds the first entry for - ## which the service name specified by `name` matches the s_name member - ## and the protocol name specified by `proto` matches the s_proto member. - ## - ## On posix this will search through the `/etc/services` file. - when useWinVersion: - var s = winlean.getservbyname(name, proto) - else: - var s = posix.getservbyname(name, proto) - if s == nil: raiseOSError(osLastError(), "Service not found.") - result.name = $s.s_name - result.aliases = cstringArrayToSeq(s.s_aliases) - result.port = Port(s.s_port) - result.proto = $s.s_proto - -proc getServByPort*(port: Port, proto: string): Servent {.tags: [ReadIOEffect].} = - ## Searches the database from the beginning and finds the first entry for - ## which the port specified by `port` matches the s_port member and the - ## protocol name specified by `proto` matches the s_proto member. - ## - ## On posix this will search through the `/etc/services` file. - when useWinVersion: - var s = winlean.getservbyport(ze(int16(port)).cint, proto) - else: - var s = posix.getservbyport(ze(int16(port)).cint, proto) - if s == nil: raiseOSError(osLastError(), "Service not found.") - result.name = $s.s_name - result.aliases = cstringArrayToSeq(s.s_aliases) - result.port = Port(s.s_port) - result.proto = $s.s_proto - -proc getHostByAddr*(ip: string): Hostent {.tags: [ReadIOEffect].} = - ## This function will lookup the hostname of an IP Address. - var myaddr: InAddr - myaddr.s_addr = inet_addr(ip) - - when useWinVersion: - var s = winlean.gethostbyaddr(addr(myaddr), sizeof(myaddr).cuint, - cint(AF_INET)) - if s == nil: raiseOSError(osLastError()) - else: - var s = - when defined(android4): - posix.gethostbyaddr(cast[cstring](addr(myaddr)), sizeof(myaddr).cint, - cint(posix.AF_INET)) - else: - posix.gethostbyaddr(addr(myaddr), sizeof(myaddr).SockLen, - cint(posix.AF_INET)) - if s == nil: - raiseOSError(osLastError(), $hstrerror(h_errno)) - - result.name = $s.h_name - result.aliases = cstringArrayToSeq(s.h_aliases) - when useWinVersion: - result.addrtype = Domain(s.h_addrtype) - else: - if s.h_addrtype == posix.AF_INET: - result.addrtype = AF_INET - elif s.h_addrtype == posix.AF_INET6: - result.addrtype = AF_INET6 - else: - raiseOSError(osLastError(), "unknown h_addrtype") - if result.addrtype == AF_INET: - result.addrList = @[] - var i = 0 - while not isNil(s.h_addr_list[i]): - var inaddrPtr = cast[ptr InAddr](s.h_addr_list[i]) - result.addrList.add($inet_ntoa(inaddrPtr[])) - inc(i) - else: - result.addrList = cstringArrayToSeq(s.h_addr_list) - result.length = int(s.h_length) - -proc getHostByName*(name: string): Hostent {.tags: [ReadIOEffect].} = - ## This function will lookup the IP address of a hostname. - when useWinVersion: - var s = winlean.gethostbyname(name) - else: - var s = posix.gethostbyname(name) - if s == nil: raiseOSError(osLastError()) - result.name = $s.h_name - result.aliases = cstringArrayToSeq(s.h_aliases) - when useWinVersion: - result.addrtype = Domain(s.h_addrtype) - else: - if s.h_addrtype == posix.AF_INET: - result.addrtype = AF_INET - elif s.h_addrtype == posix.AF_INET6: - result.addrtype = AF_INET6 - else: - raiseOSError(osLastError(), "unknown h_addrtype") - if result.addrtype == AF_INET: - result.addrList = @[] - var i = 0 - while not isNil(s.h_addr_list[i]): - var inaddrPtr = cast[ptr InAddr](s.h_addr_list[i]) - result.addrList.add($inet_ntoa(inaddrPtr[])) - inc(i) - else: - result.addrList = cstringArrayToSeq(s.h_addr_list) - result.length = int(s.h_length) - -proc getHostname*(): string {.tags: [ReadIOEffect].} = - ## Returns the local hostname (not the FQDN) - # https://tools.ietf.org/html/rfc1035#section-2.3.1 - # https://tools.ietf.org/html/rfc2181#section-11 - const size = 256 - result = newString(size) - when useWinVersion: - let success = winlean.gethostname(result, size) - else: - # Posix - let success = posix.gethostname(result, size) - if success != 0.cint: - raiseOSError(osLastError()) - let x = len(cstring(result)) - result.setLen(x) - proc getSockDomain*(socket: SocketHandle): Domain = ## Returns the socket's domain (AF_INET or AF_INET6). var name: Sockaddr_in6 @@ -464,162 +349,354 @@ proc getSockDomain*(socket: SocketHandle): Domain = else: raise newException(IOError, "Unknown socket family in getSockDomain") -proc getAddrString*(sockAddr: ptr SockAddr): string = - ## Returns the string representation of address within sockAddr - if sockAddr.sa_family.cint == nativeAfInet: - result = $inet_ntoa(cast[ptr Sockaddr_in](sockAddr).sin_addr) - elif sockAddr.sa_family.cint == nativeAfInet6: - let addrLen = when not useWinVersion: posix.INET6_ADDRSTRLEN.int - else: 46 # it's actually 46 in both cases - 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], - result.len.int32) == nil: - raiseOSError(osLastError()) - if posix.IN6_IS_ADDR_V4MAPPED(addr6) != 0: - result.setSlice("::ffff:".len..= Sockaddr_un_path_length: + raise newException(ValueError, "socket path too long") + copyMem(addr result.sun_path, path.cstring, path.len + 1) + + proc getSockName*(socket: SocketHandle): Port = + ## Returns the socket's associated port number. + var name: Sockaddr_in + when useWinVersion: + name.sin_family = uint16(ord(AF_INET)) + else: + name.sin_family = TSa_Family(posix.AF_INET) + #name.sin_port = htons(cint16(port)) + #name.sin_addr.s_addr = htonl(INADDR_ANY) + var namelen = sizeof(name).SockLen + if getsockname(socket, cast[ptr SockAddr](addr(name)), + addr(namelen)) == -1'i32: + raiseOSError(osLastError()) + result = Port(nativesockets.ntohs(name.sin_port)) + + proc getLocalAddr*(socket: SocketHandle, domain: Domain): (string, Port) = + ## Returns the socket's local address and port number. + ## + ## Similar to POSIX's `getsockname`:idx:. + case domain + of AF_INET: + var name: Sockaddr_in + when useWinVersion: + name.sin_family = uint16(ord(AF_INET)) + else: + name.sin_family = TSa_Family(posix.AF_INET) + var namelen = sizeof(name).SockLen + if getsockname(socket, cast[ptr SockAddr](addr(name)), + addr(namelen)) == -1'i32: raiseOSError(osLastError()) - else: - raise newException(IOError, "Unknown socket family in getAddrString") - setLen(strAddress, len(cstring(strAddress))) + result = ($inet_ntoa(name.sin_addr), + Port(nativesockets.ntohs(name.sin_port))) + of AF_INET6: + var name: Sockaddr_in6 + when useWinVersion: + name.sin6_family = uint16(ord(AF_INET6)) + else: + name.sin6_family = TSa_Family(posix.AF_INET6) + var namelen = sizeof(name).SockLen + if getsockname(socket, cast[ptr SockAddr](addr(name)), + addr(namelen)) == -1'i32: + raiseOSError(osLastError()) + # 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: + raiseOSError(osLastError()) + setLen(result[0], result[0].cstring.len) + result[1] = Port(nativesockets.ntohs(name.sin6_port)) + else: + raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr") -when defined(posix) and not defined(nimdoc): - proc makeUnixAddr*(path: string): Sockaddr_un = - result.sun_family = AF_UNIX.TSa_Family - if path.len >= Sockaddr_un_path_length: - raise newException(ValueError, "socket path too long") - copyMem(addr result.sun_path, path.cstring, path.len + 1) + proc getPeerAddr*(socket: SocketHandle, domain: Domain): (string, Port) = + ## Returns the socket's peer address and port number. + ## + ## Similar to POSIX's `getpeername`:idx: + case domain + of AF_INET: + var name: Sockaddr_in + when useWinVersion: + name.sin_family = uint16(ord(AF_INET)) + else: + name.sin_family = TSa_Family(posix.AF_INET) + var namelen = sizeof(name).SockLen + if getpeername(socket, cast[ptr SockAddr](addr(name)), + addr(namelen)) == -1'i32: + raiseOSError(osLastError()) + result = ($inet_ntoa(name.sin_addr), + Port(nativesockets.ntohs(name.sin_port))) + of AF_INET6: + var name: Sockaddr_in6 + when useWinVersion: + name.sin6_family = uint16(ord(AF_INET6)) + else: + name.sin6_family = TSa_Family(posix.AF_INET6) + var namelen = sizeof(name).SockLen + if getpeername(socket, cast[ptr SockAddr](addr(name)), + addr(namelen)) == -1'i32: + raiseOSError(osLastError()) + # 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: + raiseOSError(osLastError()) + setLen(result[0], result[0].cstring.len) + result[1] = Port(nativesockets.ntohs(name.sin6_port)) + else: + raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr") + +when useNimNetLite: -proc getSockName*(socket: SocketHandle): Port = - ## Returns the socket's associated port number. - var name: Sockaddr_in when useWinVersion: - name.sin_family = uint16(ord(AF_INET)) - else: - name.sin_family = TSa_Family(posix.AF_INET) - #name.sin_port = htons(cint16(port)) - #name.sin_addr.s_addr = htonl(INADDR_ANY) - var namelen = sizeof(name).SockLen - if getsockname(socket, cast[ptr SockAddr](addr(name)), - addr(namelen)) == -1'i32: - raiseOSError(osLastError()) - result = Port(nativesockets.ntohs(name.sin_port)) + const + INET_ADDRSTRLEN = 16 + INET6_ADDRSTRLEN = 46 # it's actually 46 in both cases -proc getLocalAddr*(socket: SocketHandle, domain: Domain): (string, Port) = - ## Returns the socket's local address and port number. - ## - ## Similar to POSIX's `getsockname`:idx:. - case domain - of AF_INET: - var name: Sockaddr_in - when useWinVersion: - name.sin_family = uint16(ord(AF_INET)) - else: - name.sin_family = TSa_Family(posix.AF_INET) - var namelen = sizeof(name).SockLen - if getsockname(socket, cast[ptr SockAddr](addr(name)), - addr(namelen)) == -1'i32: - raiseOSError(osLastError()) - result = ($inet_ntoa(name.sin_addr), - Port(nativesockets.ntohs(name.sin_port))) - of AF_INET6: - var name: Sockaddr_in6 - when useWinVersion: - name.sin6_family = uint16(ord(AF_INET6)) - else: - name.sin6_family = TSa_Family(posix.AF_INET6) - var namelen = sizeof(name).SockLen - if getsockname(socket, cast[ptr SockAddr](addr(name)), - addr(namelen)) == -1'i32: - raiseOSError(osLastError()) - # 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: - raiseOSError(osLastError()) - setLen(result[0], result[0].cstring.len) - result[1] = Port(nativesockets.ntohs(name.sin6_port)) - else: - raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr") + proc sockAddrToStr(sa: ptr Sockaddr): string {.noinit.} = + let af_family = sa.sa_family + var nl, v4Slice: cint + var si_addr: ptr InAddr -proc getPeerAddr*(socket: SocketHandle, domain: Domain): (string, Port) = - ## Returns the socket's peer address and port number. - ## - ## Similar to POSIX's `getpeername`:idx: - case domain - of AF_INET: - var name: Sockaddr_in - when useWinVersion: - name.sin_family = uint16(ord(AF_INET)) + if af_family == AF_INET.TSa_Family: + nl = INET_ADDRSTRLEN + si_addr = cast[ptr Sockaddr_in](sa).sin_addr.addr() + elif af_family == AF_INET6.TSa_Family: + nl = INET6_ADDRSTRLEN + let si6_addr = cast[ptr Sockaddr_in6](sa).sin6_addr.addr() + si_addr = cast[ptr InAddr](si6_addr) # let's us reuse logic below + when defined(posix) and not defined(nimdoc) and not defined(zephyr): + if posix.IN6_IS_ADDR_V4MAPPED(si6_addr) != 0: + v4Slice = "::ffff:".len() else: - name.sin_family = TSa_Family(posix.AF_INET) - var namelen = sizeof(name).SockLen - if getpeername(socket, cast[ptr SockAddr](addr(name)), - addr(namelen)) == -1'i32: - raiseOSError(osLastError()) - result = ($inet_ntoa(name.sin_addr), - Port(nativesockets.ntohs(name.sin_port))) - of AF_INET6: - var name: Sockaddr_in6 - when useWinVersion: - name.sin6_family = uint16(ord(AF_INET6)) + when defined(posix) and not defined(nimdoc): + if af_family.cint == nativeAfUnix: + return "unix" + return "" + + result = newString(nl) + let namePtr = result.cstring() + if namePtr == inet_ntop(af_family.cint, si_addr, namePtr, nl): + result.setLen(len(namePtr)) + if v4Slice > 0: result.setSlice(v4Slice.int ..< nl.int) else: - name.sin6_family = TSa_Family(posix.AF_INET6) - var namelen = sizeof(name).SockLen - if getpeername(socket, cast[ptr SockAddr](addr(name)), - addr(namelen)) == -1'i32: + return "" + + proc sockAddrToStr(sa: var Sockaddr_in | var Sockaddr_in6): string = + result = sockAddrToStr(cast[ptr SockAddr](unsafeAddr(sa))) + + proc getAddrString*(sockAddr: ptr SockAddr): string = + result = sockAddrToStr(sockAddr) + if result.len() == 0: raiseOSError(osLastError()) - # 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: - raiseOSError(osLastError()) - setLen(result[0], result[0].cstring.len) - result[1] = Port(nativesockets.ntohs(name.sin6_port)) - else: - raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr") + + proc getAddrString*(sockAddr: ptr SockAddr, strAddress: var string) {.noinit.} = + strAddress = getAddrString(sockAddr) + + proc getLocalAddr*(socket: SocketHandle, domain: Domain): (string, Port) = + ## Returns the socket's local address and port number. + ## + ## Similar to POSIX's `getsockname`:idx:. + template sockGetNameOrRaiseError(socket: untyped, name: untyped) = + var namelen = sizeof(socket).SockLen + if getsockname(socket, cast[ptr SockAddr](addr(name)), + addr(namelen)) == -1'i32: + raiseOSError(osLastError()) + + case domain + of AF_INET: + var name = Sockaddr_in(sin_family: TSa_Family(posix.AF_INET)) + sockGetNameOrRaiseError(socket, name) + result = (sockAddrToStr(name), + Port(nativesockets.ntohs(name.sin_port))) + of AF_INET6: + var name = Sockaddr_in6(sin6_family: TSa_Family(posix.AF_INET6)) + sockGetNameOrRaiseError(socket, name) + result = (sockAddrToStr(name), + Port(nativesockets.ntohs(name.sin6_port))) + else: + raiseOSError(OSErrorCode(-1), "invalid socket family in getLocalAddr") + proc getSockOptInt*(socket: SocketHandle, level, optname: int): int {. tags: [ReadIOEffect].} = @@ -733,14 +810,14 @@ proc accept*(fd: SocketHandle, inheritable = defined(nimInheritHandles)): (Socke ## child processes. ## ## Returns (osInvalidSocket, "") if an error occurred. - var sockAddress: Sockaddr_in + var sockAddress: Sockaddr var addrLen = sizeof(sockAddress).SockLen var sock = when (defined(linux) or defined(bsd)) and not defined(nimdoc): - accept4(fd, cast[ptr SockAddr](addr(sockAddress)), addr(addrLen), + accept4(fd, addr(sockAddress), addr(addrLen), if inheritable: 0 else: SOCK_CLOEXEC) else: - accept(fd, cast[ptr SockAddr](addr(sockAddress)), addr(addrLen)) + accept(fd, addr(sockAddress), addr(addrLen)) when declared(setInheritable) and not (defined(linux) or defined(bsd)): if not setInheritable(sock, inheritable): close sock @@ -748,7 +825,11 @@ proc accept*(fd: SocketHandle, inheritable = defined(nimInheritHandles)): (Socke if sock == osInvalidSocket: return (osInvalidSocket, "") else: - return (sock, $inet_ntoa(sockAddress.sin_addr)) + when useNimNetLite: + var name = sockAddrToStr(addr sockAddress) + return (sock, name) + else: + return (sock, $inet_ntoa(cast[Sockaddr_in](sockAddress).sin_addr)) when defined(windows): var wsa: WSAData diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 09cdc8afb..7b0ff78e7 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -85,12 +85,14 @@ runnableExamples("-r:off"): import std/private/since -import nativesockets, os, strutils, times, sets, options, std/monotimes +import nativesockets +import os, strutils, times, sets, options, std/monotimes import ssl_config export nativesockets.Port, nativesockets.`$`, nativesockets.`==` export Domain, SockType, Protocol const useWinVersion = defined(windows) or defined(nimdoc) +const useNimNetLite = defined(nimNetLite) or defined(freertos) or defined(zephyr) const defineSsl = defined(ssl) or defined(nimdoc) when useWinVersion: @@ -1243,11 +1245,12 @@ proc getLocalAddr*(socket: Socket): (string, Port) = ## This is high-level interface for `getsockname`:idx:. getLocalAddr(socket.fd, socket.domain) -proc getPeerAddr*(socket: Socket): (string, Port) = - ## Get the socket's peer address and port number. - ## - ## This is high-level interface for `getpeername`:idx:. - getPeerAddr(socket.fd, socket.domain) +when not useNimNetLite: + proc getPeerAddr*(socket: Socket): (string, Port) = + ## Get the socket's peer address and port number. + ## + ## This is high-level interface for `getpeername`:idx:. + getPeerAddr(socket.fd, socket.domain) proc setSockOpt*(socket: Socket, opt: SOBool, value: bool, level = SOL_SOCKET) {.tags: [WriteIOEffect].} = @@ -1259,7 +1262,7 @@ proc setSockOpt*(socket: Socket, opt: SOBool, value: bool, var valuei = cint(if value: 1 else: 0) setSockOptInt(socket.fd, cint(level), toCInt(opt), valuei) -when defined(posix) or defined(nimdoc): +when defined(nimdoc) or (defined(posix) and not useNimNetLite): proc connectUnix*(socket: Socket, path: string) = ## Connects to Unix socket on `path`. ## This only works on Unix-style systems: Mac OS X, BSD and Linux diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim index 82550e09b..ec441f6da 100644 --- a/lib/pure/selectors.nim +++ b/lib/pure/selectors.nim @@ -323,6 +323,23 @@ else: # Anything higher is the time to wait in milliseconds. doAssert(timeout >= -1, "Cannot select with a negative value, got: " & $timeout) + when defined(linux) or defined(windows) or defined(macosx) or defined(bsd) or + defined(zephyr) or defined(freertos): + template maxDescriptors*(): int = + ## Returns the maximum number of active file descriptors for the current + ## process. This involves a system call. For now `maxDescriptors` is + ## supported on the following OSes: Windows, Linux, OSX, BSD. + when defined(windows): + 16_700_000 + elif defined(zephyr) or defined(freertos): + FD_MAX + else: + var fdLim: RLimit + var res = int(getrlimit(RLIMIT_NOFILE, fdLim)) + if res >= 0: + res = int(fdLim.rlim_cur) - 1 + res + when defined(linux) and not defined(emscripten): include ioselects/ioselectors_epoll elif bsdPlatform: @@ -337,5 +354,7 @@ else: include ioselects/ioselectors_select elif defined(freertos) or defined(lwip): include ioselects/ioselectors_select + elif defined(zephyr): + include ioselects/ioselectors_poll else: include ioselects/ioselectors_poll diff --git a/lib/system/ansi_c.nim b/lib/system/ansi_c.nim index e5f26e070..259d36633 100644 --- a/lib/system/ansi_c.nim +++ b/lib/system/ansi_c.nim @@ -142,14 +142,29 @@ proc c_sprintf*(buf, frmt: cstring): cint {. importc: "sprintf", header: "", varargs, noSideEffect.} # we use it only in a way that cannot lead to security issues -proc c_malloc*(size: csize_t): pointer {. - importc: "malloc", header: "".} -proc c_calloc*(nmemb, size: csize_t): pointer {. - importc: "calloc", header: "".} -proc c_free*(p: pointer) {. - importc: "free", header: "".} -proc c_realloc*(p: pointer, newsize: csize_t): pointer {. - importc: "realloc", header: "".} +when defined(zephyr) and not defined(zephyrUseLibcMalloc): + proc c_malloc*(size: csize_t): pointer {. + importc: "k_malloc", header: "".} + proc c_calloc*(nmemb, size: csize_t): pointer {. + importc: "k_calloc", header: "".} + proc c_free*(p: pointer) {. + importc: "k_free", header: "".} + proc c_realloc*(p: pointer, newsize: csize_t): pointer = + # Zephyr's kernel malloc doesn't support realloc + result = c_malloc(newSize) + # match the ansi c behavior + if not result.isNil(): + copyMem(result, p, newSize) + c_free(p) +else: + proc c_malloc*(size: csize_t): pointer {. + importc: "malloc", header: "".} + proc c_calloc*(nmemb, size: csize_t): pointer {. + importc: "calloc", header: "".} + proc c_free*(p: pointer) {. + importc: "free", header: "".} + proc c_realloc*(p: pointer, newsize: csize_t): pointer {. + importc: "realloc", header: "".} proc c_fwrite*(buf: pointer, size, n: csize_t, f: CFilePtr): cint {. importc: "fwrite", header: "".} diff --git a/lib/system/bitmasks.nim b/lib/system/bitmasks.nim index d7c55a4d9..caf86568a 100644 --- a/lib/system/bitmasks.nim +++ b/lib/system/bitmasks.nim @@ -15,6 +15,7 @@ const PageSize = 1 shl PageShift PageMask = PageSize-1 + MemAlign = # also minimal allocatable memory block when defined(useMalloc): when defined(amd64): 16 diff --git a/lib/system/dyncalls.nim b/lib/system/dyncalls.nim index c14fcf31e..36c2c5fe1 100644 --- a/lib/system/dyncalls.nim +++ b/lib/system/dyncalls.nim @@ -176,7 +176,7 @@ elif defined(genode): proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr = raiseAssert("nimGetProcAddr not implemented") -elif defined(nintendoswitch) or defined(freertos): +elif defined(nintendoswitch) or defined(freertos) or defined(zephyr): proc nimUnloadLibrary(lib: LibHandle) = cstderr.rawWrite("nimUnLoadLibrary not implemented") cstderr.rawWrite("\n") diff --git a/lib/system/io.nim b/lib/system/io.nim index 59f070f73..6f4accc2a 100644 --- a/lib/system/io.nim +++ b/lib/system/io.nim @@ -347,7 +347,7 @@ when defined(nimdoc) or (defined(posix) and not defined(nimscript)) or defined(w ## availability with `declared() `. when SupportIoctlInheritCtl: result = c_ioctl(f, if inheritable: FIONCLEX else: FIOCLEX) != -1 - elif defined(freertos): + elif defined(freertos) or defined(zephyr): result = true elif defined(posix): var flags = c_fcntl(f, F_GETFD) @@ -780,8 +780,11 @@ when declared(stdout): var echoLock: SysLock initSysLock echoLock - const stdOutLock = not defined(windows) and not defined(android) and - not defined(nintendoswitch) and not defined(freertos) and + const stdOutLock = not defined(windows) and + not defined(android) and + not defined(nintendoswitch) and + not defined(freertos) and + not defined(zephyr) and hostOS != "any" proc echoBinSafe(args: openArray[string]) {.compilerproc.} = diff --git a/lib/system/mm/malloc.nim b/lib/system/mm/malloc.nim index 2ba5c0fec..d41dce705 100644 --- a/lib/system/mm/malloc.nim +++ b/lib/system/mm/malloc.nim @@ -2,13 +2,22 @@ {.push stackTrace: off.} proc allocImpl(size: Natural): pointer = - c_malloc(size.csize_t) + result = c_malloc(size.csize_t) + when defined(zephyr): + if result == nil: + raiseOutOfMem() proc alloc0Impl(size: Natural): pointer = - c_calloc(size.csize_t, 1) + result = c_calloc(size.csize_t, 1) + when defined(zephyr): + if result == nil: + raiseOutOfMem() proc reallocImpl(p: pointer, newSize: Natural): pointer = - c_realloc(p, newSize.csize_t) + result = c_realloc(p, newSize.csize_t) + when defined(zephyr): + if result == nil: + raiseOutOfMem() proc realloc0Impl(p: pointer, oldsize, newSize: Natural): pointer = result = realloc(p, newSize.csize_t) diff --git a/tests/stdlib/tlwip.nim b/tests/stdlib/tlwip.nim index 7f7822236..fc53be592 100644 --- a/tests/stdlib/tlwip.nim +++ b/tests/stdlib/tlwip.nim @@ -1,6 +1,6 @@ discard """ targets: "c" - cmd: "nim $target --os:freertos --gc:arc $options $file" + cmd: "nim $target --compileOnly --os:freertos --gc:arc $options $file" disabled: "bsd" disabled: "windows" action: compile From 582468da1cc266d0dd922ec5c620713bbc45d02a Mon Sep 17 00:00:00 2001 From: Tail Wag Games Date: Mon, 25 Oct 2021 03:13:30 -0500 Subject: [PATCH 2604/3768] deinitializing locks at program exit (#19043) * deinitializing locks at program exit * deinitLock shouldn't be called for js backend I guess... * I suppose this is the best way to detect the ewruntime option * I guess I need these guards here too... * fixing merge conflict --- lib/std/exitprocs.nim | 3 +++ lib/system/io.nim | 56 ++++++++++++++++++++++++------------------- 2 files changed, 35 insertions(+), 24 deletions(-) diff --git a/lib/std/exitprocs.nim b/lib/std/exitprocs.nim index c6537f7f8..c76583a8c 100644 --- a/lib/std/exitprocs.nim +++ b/lib/std/exitprocs.nim @@ -44,6 +44,9 @@ proc callClosures() {.noconv.} = of kClosure: fun.fun1() of kNoconv: fun.fun2() + when not defined(js) and not defined(nimOwnedEnabled): + deinitLock(gFunsLock) + template fun() = if gFuns.len == 0: addAtExit(callClosures) diff --git a/lib/system/io.nim b/lib/system/io.nim index 6f4accc2a..e7369392a 100644 --- a/lib/system/io.nim +++ b/lib/system/io.nim @@ -20,19 +20,19 @@ type incompleteStruct.} = object File* = ptr CFile ## The type representing a file handle. - FileMode* = enum ## The file mode when opening a file. - fmRead, ## Open the file for read access only. - fmWrite, ## Open the file for write access only. - ## If the file does not exist, it will be - ## created. Existing files will be cleared! - fmReadWrite, ## Open the file for read and write access. - ## If the file does not exist, it will be - ## created. Existing files will be cleared! - fmReadWriteExisting, ## Open the file for read and write access. - ## If the file does not exist, it will not be - ## created. The existing file will not be cleared. - fmAppend ## Open the file for writing only; append data - ## at the end. + FileMode* = enum ## The file mode when opening a file. + fmRead, ## Open the file for read access only. + fmWrite, ## Open the file for write access only. + ## If the file does not exist, it will be + ## created. Existing files will be cleared! + fmReadWrite, ## Open the file for read and write access. + ## If the file does not exist, it will be + ## created. Existing files will be cleared! + fmReadWriteExisting, ## Open the file for read and write access. + ## If the file does not exist, it will not be + ## created. The existing file will not be cleared. + fmAppend ## Open the file for writing only; append data + ## at the end. FileHandle* = cint ## type that represents an OS file handle; this is ## useful for low-level file access @@ -40,7 +40,8 @@ type # text file handling: when not defined(nimscript) and not defined(js): # duplicated between io and ansi_c - const stdioUsesMacros = (defined(osx) or defined(freebsd) or defined(dragonfly)) and not defined(emscripten) + const stdioUsesMacros = (defined(osx) or defined(freebsd) or defined( + dragonfly)) and not defined(emscripten) const stderrName = when stdioUsesMacros: "__stderrp" else: "stderr" const stdoutName = when stdioUsesMacros: "__stdoutp" else: "stdout" const stdinName = when stdioUsesMacros: "__stdinp" else: "stdin" @@ -158,7 +159,7 @@ proc checkErr(f: File) = # shouldn't happen quit(1) -{.push stackTrace:off, profiler:off.} +{.push stackTrace: off, profiler: off.} proc readBuffer*(f: File, buffer: pointer, len: Natural): int {. tags: [ReadIOEffect], benign.} = ## reads `len` bytes into the buffer pointed to by `buffer`. Returns @@ -167,7 +168,8 @@ proc readBuffer*(f: File, buffer: pointer, len: Natural): int {. result = cast[int](c_fread(buffer, 1, cast[csize_t](len), f)) if result != len: checkErr(f) -proc readBytes*(f: File, a: var openArray[int8|uint8], start, len: Natural): int {. +proc readBytes*(f: File, a: var openArray[int8|uint8], start, + len: Natural): int {. tags: [ReadIOEffect], benign.} = ## reads `len` bytes into the buffer `a` starting at `a[start]`. Returns ## the actual number of bytes that have been read which may be less than @@ -418,8 +420,8 @@ proc readLine*(f: File, line: var string): bool {.tags: [ReadIOEffect], numberOfCharsRead -= 2 # handle Ctrl+Z as EOF for i in 0..", final, pure.} = object ## struct stat filler_1: array[24, char] - st_mode: Mode ## Mode of file + st_mode: Mode ## Mode of file filler_2: array[144 - 24 - 4, char] proc modeIsDir(m: Mode): bool = @@ -660,7 +662,7 @@ when defined(posix) and not defined(nimscript): Stat {.importc: "struct stat", header: "", final, pure.} = object ## struct stat - st_mode: Mode ## Mode of file + st_mode: Mode ## Mode of file proc modeIsDir(m: Mode): bool {.importc: "S_ISDIR", header: "".} ## Test for a directory. @@ -727,7 +729,8 @@ proc open*(f: var File, filehandle: FileHandle, ## ## The passed file handle will no longer be inheritable. when not defined(nimInheritHandles) and declared(setInheritable): - let oshandle = when defined(windows): FileHandle getOsfhandle(filehandle) else: filehandle + let oshandle = when defined(windows): FileHandle getOsfhandle( + filehandle) else: filehandle if not setInheritable(oshandle, false): return false f = c_fdopen(filehandle, FormatOpen[mode]) @@ -780,6 +783,10 @@ when declared(stdout): var echoLock: SysLock initSysLock echoLock + when not defined(js) and not defined(nimOwnedEnabled): + import std/exitprocs + addExitProc(proc() {.noconv.} = deinitSys echoLock) + const stdOutLock = not defined(windows) and not defined(android) and not defined(nintendoswitch) and @@ -898,7 +905,8 @@ proc readLines*(filename: string, n: Natural): seq[string] = else: sysFatal(IOError, "cannot open: " & filename) -template readLines*(filename: string): seq[string] {.deprecated: "use readLines with two arguments".} = +template readLines*(filename: string): seq[ + string] {.deprecated: "use readLines with two arguments".} = readLines(filename, 1) iterator lines*(filename: string): string {.tags: [ReadIOEffect].} = @@ -915,7 +923,7 @@ iterator lines*(filename: string): string {.tags: [ReadIOEffect].} = for line in filename.lines: buffer.add(line.replace("a", "0") & '\n') writeFile(filename, buffer) - var f = open(filename, bufSize=8000) + var f = open(filename, bufSize = 8000) try: var res = newStringOfCap(80) while f.readLine(res): yield res From 3ecb369ca1a12754486429bda4ab0bc60f9b86d7 Mon Sep 17 00:00:00 2001 From: quantimnot <54247259+quantimnot@users.noreply.github.com> Date: Tue, 26 Oct 2021 02:33:26 -0400 Subject: [PATCH 2605/3768] Document file changes that may be needed for add a new platform (#19055) Co-authored-by: quantimnot --- doc/intern.rst | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/doc/intern.rst b/doc/intern.rst index fe0d333fd..e8fb4ac86 100644 --- a/doc/intern.rst +++ b/doc/intern.rst @@ -207,6 +207,42 @@ implementation uses C's `setjmp`:c: function to store all registers on the hardware stack. It may be necessary that the new platform needs to replace this generic code by some assembler code. +Files that may need changed for your platform include: + +* `compiler/platform.nim` + Add os/cpu properties. +* `lib/system.nim` + Add os/cpu to the documentation for `system.hostOS` and `system.hostCPU`. +* `compiler/options.nim` + Add special os/cpu property checks in `isDefined`. +* `compiler/installer.ini` + Add os/cpu to `Project.Platforms` field. +* `lib/system/platforms.nim` + Add os/cpu. +* `lib/pure/include/osseps.nim` + Add os specializations. +* `lib/pure/distros.nim` + Add os, package handler. +* `tools/niminst/makefile.nimf` + Add os/cpu compiler/linker flags. +* `tools/niminst/buildsh.nimf` + Add os/cpu compiler/linker flags. + +If the `--os` or `--cpu` options aren't passed to the compiler, then Nim will +determine the current host os, cpu and endianess from `system.cpuEndian`, +`system.hostOS` and `system.hostCPU`. Those values are derived from +`compiler/platform.nim`. + +In order for the new platform to be bootstrapped from the `csources`, it must: + +* have `compiler/platform.nim` updated +* have `compiler/installer.ini` updated +* have `tools/niminst/buildsh.nimf` updated +* have `tools/niminst/makefile.nimf` updated +* be backported to the Nim version used by the `csources` +* the new `csources` must be pushed +* the new `csources` revision must be updated in `config/build_config.txt` + Runtime type information ======================== From 83a2515af7aeb9a1c12015321243399a0d1f4c95 Mon Sep 17 00:00:00 2001 From: Jason Beetham Date: Tue, 26 Oct 2021 03:27:11 -0600 Subject: [PATCH 2606/3768] Fixed generic distinct conversions for 'var' (#18837) * SameTypeAux now properly traverses generic distincts * Smarter traversal of distincts * Removed redundant check * Fixed nkConv for jsgen * Added test for non distinct nkConv * using skiptypes for distinct now * Fixed genaddr for nkconv --- compiler/jsgen.nim | 140 +++++++++++++---------- compiler/types.nim | 8 +- tests/converter/texplicit_conversion.nim | 6 + tests/distinct/tdistinct.nim | 17 +++ 4 files changed, 104 insertions(+), 67 deletions(-) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index ff4d2839e..7e8c25173 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -1288,75 +1288,89 @@ template isIndirect(x: PSym): bool = v.kind notin {skProc, skFunc, skConverter, skMethod, skIterator, skConst, skTemp, skLet}) -proc genAddr(p: PProc, n: PNode, r: var TCompRes) = - case n[0].kind - of nkSym: - let s = n[0].sym - if s.loc.r == nil: internalError(p.config, n.info, "genAddr: 3") - case s.kind - of skParam: - r.res = s.loc.r - r.address = nil - r.typ = etyNone - of skVar, skLet, skResult: - r.kind = resExpr - let jsType = mapType(p, n.typ) - if jsType == etyObject: - # make addr() a no-op: - r.typ = etyNone - if isIndirect(s): - r.res = s.loc.r & "[0]" - else: - r.res = s.loc.r - r.address = nil - elif {sfGlobal, sfAddrTaken} * s.flags != {} or jsType == etyBaseIndex: - # for ease of code generation, we do not distinguish between - # sfAddrTaken and sfGlobal. - r.typ = etyBaseIndex - r.address = s.loc.r - r.res = rope("0") +proc genSymAddr(p: PProc, n: PNode, typ: PType, r: var TCompRes) = + let s = n.sym + if s.loc.r == nil: internalError(p.config, n.info, "genAddr: 3") + case s.kind + of skParam: + r.res = s.loc.r + r.address = nil + r.typ = etyNone + of skVar, skLet, skResult: + r.kind = resExpr + let jsType = mapType(p): + if typ.isNil: + n.typ else: - # 'var openArray' for instance produces an 'addr' but this is harmless: - gen(p, n[0], r) - #internalError(p.config, n.info, "genAddr: 4 " & renderTree(n)) - else: internalError(p.config, n.info, $("genAddr: 2", s.kind)) - of nkCheckedFieldExpr: - genCheckedFieldOp(p, n[0], n.typ, r) - of nkDotExpr: - if mapType(p, n.typ) == etyBaseIndex: - genFieldAddr(p, n[0], r) + typ + if jsType == etyObject: + # make addr() a no-op: + r.typ = etyNone + if isIndirect(s): + r.res = s.loc.r & "[0]" + else: + r.res = s.loc.r + r.address = nil + elif {sfGlobal, sfAddrTaken} * s.flags != {} or jsType == etyBaseIndex: + # for ease of code generation, we do not distinguish between + # sfAddrTaken and sfGlobal. + r.typ = etyBaseIndex + r.address = s.loc.r + r.res = rope("0") else: - genFieldAccess(p, n[0], r) - of nkBracketExpr: - var ty = skipTypes(n[0].typ, abstractVarRange) - if ty.kind in MappedToObject: - gen(p, n[0], r) - else: - let kindOfIndexedExpr = skipTypes(n[0][0].typ, abstractVarRange).kind - case kindOfIndexedExpr - of tyArray, tyOpenArray, tySequence, tyString, tyCstring, tyVarargs: - genArrayAddr(p, n[0], r) - of tyTuple: - genFieldAddr(p, n[0], r) - else: internalError(p.config, n[0].info, "expr(nkBracketExpr, " & $kindOfIndexedExpr & ')') - of nkObjDownConv: - gen(p, n[0], r) - of nkHiddenDeref: - gen(p, n[0], r) - of nkHiddenAddr: - gen(p, n[0], r) - of nkStmtListExpr: - if n.len == 1: gen(p, n[0], r) - else: internalError(p.config, n[0].info, "genAddr for complex nkStmtListExpr") - of nkCallKinds: - if n[0].typ.kind == tyOpenArray: # 'var openArray' for instance produces an 'addr' but this is harmless: - # namely toOpenArray(a, 1, 3) + gen(p, n, r) + #internalError(p.config, n.info, "genAddr: 4 " & renderTree(n)) + else: internalError(p.config, n.info, $("genAddr: 2", s.kind)) + +proc genAddr(p: PProc, n: PNode, r: var TCompRes) = + if n.kind == nkSym: + genSymAddr(p, n, nil, r) + else: + case n[0].kind + of nkSym: + genSymAddr(p, n[0], n.typ, r) + of nkCheckedFieldExpr: + genCheckedFieldOp(p, n[0], n.typ, r) + of nkDotExpr: + if mapType(p, n.typ) == etyBaseIndex: + genFieldAddr(p, n[0], r) + else: + genFieldAccess(p, n[0], r) + of nkBracketExpr: + var ty = skipTypes(n[0].typ, abstractVarRange) + if ty.kind in MappedToObject: + gen(p, n[0], r) + else: + let kindOfIndexedExpr = skipTypes(n[0][0].typ, abstractVarRange).kind + case kindOfIndexedExpr + of tyArray, tyOpenArray, tySequence, tyString, tyCstring, tyVarargs: + genArrayAddr(p, n[0], r) + of tyTuple: + genFieldAddr(p, n[0], r) + of tyGenericBody: + genAddr(p, n[^1], r) + else: internalError(p.config, n[0].info, "expr(nkBracketExpr, " & $kindOfIndexedExpr & ')') + of nkObjDownConv: gen(p, n[0], r) + of nkHiddenDeref: + gen(p, n[0], r) + of nkHiddenAddr: + gen(p, n[0], r) + of nkConv: + genAddr(p, n[0], r) + of nkStmtListExpr: + if n.len == 1: gen(p, n[0], r) + else: internalError(p.config, n[0].info, "genAddr for complex nkStmtListExpr") + of nkCallKinds: + if n[0].typ.kind == tyOpenArray: + # 'var openArray' for instance produces an 'addr' but this is harmless: + # namely toOpenArray(a, 1, 3) + gen(p, n[0], r) + else: + internalError(p.config, n[0].info, "genAddr: " & $n[0].kind) else: internalError(p.config, n[0].info, "genAddr: " & $n[0].kind) - else: - internalError(p.config, n[0].info, "genAddr: " & $n[0].kind) proc attachProc(p: PProc; content: Rope; s: PSym) = p.g.code.add(content) diff --git a/compiler/types.nim b/compiler/types.nim index cf6579317..2c7b91d9f 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -1121,15 +1121,15 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = case c.cmp of dcEq: return false of dcEqIgnoreDistinct: - while a.kind == tyDistinct: a = a[0] - while b.kind == tyDistinct: b = b[0] + a = a.skipTypes({tyDistinct, tyGenericInst}) + b = b.skipTypes({tyDistinct, tyGenericInst}) if a.kind != b.kind: return false of dcEqOrDistinctOf: - while a.kind == tyDistinct: a = a[0] + a = a.skipTypes({tyDistinct, tyGenericInst}) if a.kind != b.kind: return false # this is required by tunique_type but makes no sense really: - if x.kind == tyGenericInst and IgnoreTupleFields notin c.flags: + if tyDistinct notin {x.kind, y.kind} and x.kind == tyGenericInst and IgnoreTupleFields notin c.flags: let lhs = x.skipGenericAlias rhs = y.skipGenericAlias diff --git a/tests/converter/texplicit_conversion.nim b/tests/converter/texplicit_conversion.nim index 6b2e96faf..e36d78ad5 100644 --- a/tests/converter/texplicit_conversion.nim +++ b/tests/converter/texplicit_conversion.nim @@ -11,3 +11,9 @@ converter toInt(s: string): int = let x = (int)"234" echo x + +block: # Test for nkconv + proc foo(o: var int) = + assert o == 0 + var a = 0 + foo(int(a)) \ No newline at end of file diff --git a/tests/distinct/tdistinct.nim b/tests/distinct/tdistinct.nim index fd60b4ac0..dd8237854 100644 --- a/tests/distinct/tdistinct.nim +++ b/tests/distinct/tdistinct.nim @@ -8,6 +8,7 @@ false false false Foo +foo ''' """ @@ -140,6 +141,22 @@ block tRequiresInit: let s = "test" doAssert s == "test" +block: #17322 + type + A[T] = distinct string + + proc foo(a: var A) = + a.string.add "foo" + + type + B = distinct A[int] + + var b: B + foo(A[int](b)) + echo A[int](b).string + b.string.add "bar" + assert b.string == "foobar" + type Foo = distinct string template main() = From 8d5a27518929bd4c54f4beb7e40a5fc382d3dd05 Mon Sep 17 00:00:00 2001 From: Jason Beetham Date: Tue, 26 Oct 2021 03:29:07 -0600 Subject: [PATCH 2607/3768] Fixed distinct composite type class proc borrowing (#18904) * Fixed composite type class proc borrowing * Moved borrow search into transf * added borrow check to symbol flag --- compiler/semcall.nim | 13 +++++++++++-- compiler/seminst.nim | 6 +++++- compiler/transf.nim | 8 ++++++++ tests/borrow/typeclassborrow.nim | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 56 insertions(+), 3 deletions(-) create mode 100644 tests/borrow/typeclassborrow.nim diff --git a/compiler/semcall.nim b/compiler/semcall.nim index a3064788e..36658d472 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -703,7 +703,16 @@ proc searchForBorrowProc(c: PContext, startScope: PScope, fn: PSym): PSym = call.add(newIdentNode(fn.name, fn.info)) for i in 1.. Date: Tue, 26 Oct 2021 13:32:52 -0500 Subject: [PATCH 2608/3768] Fix #19052; [backport:1.6.0] (#19053) * Fix #19052; [backport:1.6.0] Adds a compile flag to avoid a getrandom syscall, fixing #19052. This is neccesary when the getrandom syscall is missing, as noted in #19052, particularly in kernel versions < 3.17 when getrandom was introduced. Specifically relevant is this is missing from kernel 3.10, which is the supported kernel throughout RHEL 7 and CentOS 7, which is widely used at many organizations. Without this, versions of nim that include sysrand (i.e. versions >= 1.6.0) will not compile without modification, however with this change a compile flag may be used to fall back using /dev/urandom as done with any unknown Posix OS (preferred here as a fallback since it already supplies a cryptographically secure PRNG and existing code deals with entropy pool init, etc). The change is placed behind a compile flag, as discussed in github ticket #19052 (summed up here): * First, I can't seem to catch that a importc such as SYS_getrandom is declared without using it (the declared proc returns true, but compiler throws an undeclared identifier flag when referencing it). * Second, it seemed preferable to be behaviorally explicit vs implicit when considering this is intended to be a cryptographically secure PRNG. * Third, if I intend to compile on a kernel >= 3.17 while running the binary on at least one system < 3.17, I'll want to be able to target this without relying on a compile time determination if the getrandom syscall is available. * Documenting compile flag for -d:nimNoGetRandom and adding changelog entry Related to #19052 and comments in PR #19053. Also created a new changelog file since none currently exists. Co-authored-by: Timothy Alexander --- changelogs/changelog.md | 31 +++++++++++++++++++++++++++++++ lib/std/sysrand.nim | 7 ++++++- 2 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 changelogs/changelog.md diff --git a/changelogs/changelog.md b/changelogs/changelog.md new file mode 100644 index 000000000..475f568ec --- /dev/null +++ b/changelogs/changelog.md @@ -0,0 +1,31 @@ +# v1.xx.x - yyyy-mm-dd + +## Changes affecting backward compatibility + +## Standard library additions and changes + +### New compile flag (`-d:nimNoGetRandom`) when building `std/sysrand` to remove dependency on linux `getrandom` syscall + +This compile flag only affects linux builds and is necessary if either compiling on a linux kernel version < 3.17, or if code built will be executing on kernel < 3.17. + +On linux kernels < 3.17 (such as kernel 3.10 in RHEL7 and CentOS7), the `getrandom` syscall was not yet introduced. Without this, the `std/sysrand` module will not build properly, and if code is built on a kernel >= 3.17 without the flag, any usage of the `std/sysrand` module will fail to execute on a kernel < 3.17 (since it attempts to perform a syscall to `getrandom`, which isn't present in the current kernel). A compile flag has been added to force the `std/sysrand` module to use /dev/urandom (available since linux kernel 1.3.30), rather than the `getrandom` syscall. This allows for use of a cryptographically secure PRNG, regardless of kernel support for the `getrandom` syscall. + +When building for RHEL7/CentOS7 for example, the entire build process for nim from a source package would then be: +```sh +$ yum install devtoolset-8 # Install GCC version 8 vs the standard 4.8.5 on RHEL7/CentOS7. Alternatively use -d:nimEmulateOverflowChecks. See issue #13692 for details +$ scl enable devtoolset-8 bash # Run bash shell with default toolchain of gcc 8 +$ sh build.sh # per unix install instructions +$ bin/nim c koch # per unix install instructions +$ ./koch boot -d:release # per unix install instructions +$ ./koch tools -d:nimNoGetRandom # pass the nimNoGetRandom flag to compile std/sysrand without support for getrandom syscall +``` + +This is necessary to pass when building nim on kernel versions < 3.17 in particular to avoid an error of "SYS_getrandom undeclared" during the build process for stdlib (sysrand in particular). + +## Language changes + + +## Compiler changes + + +## Tool changes diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index 75983e3c6..b35f24a72 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -38,6 +38,11 @@ ## .. _randomFillSync: https://nodejs.org/api/crypto.html#crypto_crypto_randomfillsync_buffer_offset_size ## .. _/dev/urandom: https://en.wikipedia.org/wiki//dev/random ## +## On a Linux target, a call to the `getrandom` syscall can be avoided (e.g. +## for targets running kernel version < 3.17) by passing a compile flag of +## `-d:nimNoGetRandom`. If this flag is passed, sysrand will use `/dev/urandom` +## as with any other POSIX compliant OS. +## runnableExamples: doAssert urandom(0).len == 0 @@ -159,7 +164,7 @@ elif defined(windows): result = randomBytes(addr dest[0], size) -elif defined(linux): +elif defined(linux) and not defined(nimNoGetRandom): # TODO using let, pending bootstrap >= 1.4.0 var SYS_getrandom {.importc: "SYS_getrandom", header: "".}: clong const syscallHeader = """#include From f50bcf82c3e45921961ee4145bac88701fec62b0 Mon Sep 17 00:00:00 2001 From: Netsu <52031254+WeebNetsu@users.noreply.github.com> Date: Tue, 26 Oct 2021 21:42:22 +0200 Subject: [PATCH 2609/3768] Minor update to terminal docs (#19056) * Update terminal.nim - Added some extra docs to cursorUp/Down/Forward/Backward - I was able to use hideCursor and showCursor without adding stdout, removed the parameter - Added docs to terminalHeight()* and terminalWidth()* * Update lib/pure/terminal.nim Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> * Update lib/pure/terminal.nim Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> * Added back f: file to cursor movement * Removed unnecessary comments Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> --- lib/pure/terminal.nim | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index 25b1a4cdd..c9aafc037 100644 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -170,6 +170,7 @@ when defined(windows): return 0 proc terminalWidth*(): int = + ## Returns the terminal width in columns. var w: int = 0 w = terminalWidthIoctl([getStdHandle(STD_INPUT_HANDLE), getStdHandle(STD_OUTPUT_HANDLE), @@ -178,6 +179,7 @@ when defined(windows): return 80 proc terminalHeight*(): int = + ## Returns the terminal height in rows. var h: int = 0 h = terminalHeightIoctl([getStdHandle(STD_INPUT_HANDLE), getStdHandle(STD_OUTPUT_HANDLE), @@ -389,6 +391,9 @@ when defined(windows): proc cursorUp*(f: File, count = 1) = ## Moves the cursor up by `count` rows. + runnableExamples("-r:off"): + stdout.cursorUp(2) + write(stdout, "Hello World!") # anything written at that location will be erased/replaced with this when defined(windows): let h = conHandle(f) var p = getCursorPos(h) @@ -399,6 +404,9 @@ proc cursorUp*(f: File, count = 1) = proc cursorDown*(f: File, count = 1) = ## Moves the cursor down by `count` rows. + runnableExamples("-r:off"): + stdout.cursorDown(2) + write(stdout, "Hello World!") # anything written at that location will be erased/replaced with this when defined(windows): let h = conHandle(f) var p = getCursorPos(h) @@ -409,6 +417,9 @@ proc cursorDown*(f: File, count = 1) = proc cursorForward*(f: File, count = 1) = ## Moves the cursor forward by `count` columns. + runnableExamples("-r:off"): + stdout.cursorForward(2) + write(stdout, "Hello World!") # anything written at that location will be erased/replaced with this when defined(windows): let h = conHandle(f) var p = getCursorPos(h) @@ -419,6 +430,9 @@ proc cursorForward*(f: File, count = 1) = proc cursorBackward*(f: File, count = 1) = ## Moves the cursor backward by `count` columns. + runnableExamples("-r:off"): + stdout.cursorBackward(2) + write(stdout, "Hello World!") # anything written at that location will be erased/replaced with this when defined(windows): let h = conHandle(f) var p = getCursorPos(h) From ee65767d44cca79ce2677564151be69a60f6a94d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Derek=20=E5=91=86?= <116649+derekdai@users.noreply.github.com> Date: Wed, 27 Oct 2021 17:45:05 +0800 Subject: [PATCH 2610/3768] fix a tiny formating issue in doc/destructors.rst (#19058) --- doc/destructors.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/destructors.rst b/doc/destructors.rst index 195dc8f97..b4575b2e2 100644 --- a/doc/destructors.rst +++ b/doc/destructors.rst @@ -653,7 +653,7 @@ The ability to override a hook leads to a phase ordering problem: discard -The solution is to define `proc `=destroy`[T](f: var Foo[T])` before +The solution is to define ````proc `=destroy`[T](f: var Foo[T])```` before it is used. The compiler generates implicit hooks for all types in *strategic places* so that an explicitly provided hook that comes too "late" can be detected reliably. These *strategic places* From cd2b093d07303aa7bfcb4a007281f56de3755110 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Derek=20=E5=91=86?= <116649+derekdai@users.noreply.github.com> Date: Thu, 28 Oct 2021 15:35:57 +0800 Subject: [PATCH 2611/3768] fix a tiny code snippet formatting issue in `doc/constructors.rst`, again (#19065) --- doc/destructors.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/destructors.rst b/doc/destructors.rst index b4575b2e2..6e4509666 100644 --- a/doc/destructors.rst +++ b/doc/destructors.rst @@ -653,7 +653,7 @@ The ability to override a hook leads to a phase ordering problem: discard -The solution is to define ````proc `=destroy`[T](f: var Foo[T])```` before +The solution is to define ``proc `=destroy`[T](f: var Foo[T])`` before it is used. The compiler generates implicit hooks for all types in *strategic places* so that an explicitly provided hook that comes too "late" can be detected reliably. These *strategic places* From c80e2c173686bd12904e5487752dc0ce20cb8bcb Mon Sep 17 00:00:00 2001 From: ynfle <23086821+ynfle@users.noreply.github.com> Date: Thu, 28 Oct 2021 11:02:18 +0300 Subject: [PATCH 2612/3768] Docs: change clipboard cursor type to `pointer` (#19064) --- tools/dochack/dochack.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/dochack/dochack.nim b/tools/dochack/dochack.nim index 83e81f1c0..3a663808d 100644 --- a/tools/dochack/dochack.nim +++ b/tools/dochack/dochack.nim @@ -360,6 +360,7 @@ proc copyToClipboard*() {.exportc.} = const button = document.createElement("button") button.value = e.textContent.replace('...', '') button.classList.add("copyToClipBoardBtn") + button.style = "cursor: pointer" div.appendChild(preTag) div.appendChild(button) @@ -396,4 +397,4 @@ proc copyToClipboard*() {.exportc.} = """ .} -copyToClipboard() \ No newline at end of file +copyToClipboard() From 7ba2659f733b97db63b7552415ad048e34d4a11a Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Thu, 28 Oct 2021 20:20:52 +0300 Subject: [PATCH 2613/3768] docgen: implement doc link resolution in current module (#18642) --- compiler/docgen.nim | 154 ++++++++- compiler/lineinfos.nim | 2 + compiler/typesrenderer.nim | 66 +++- config/nimdoc.cfg | 11 + config/nimdoc.tex.cfg | 10 +- doc/docgen.rst | 170 ++++++++++ doc/nimdoc.css | 4 + lib/packages/docutils/dochelpers.nim | 267 +++++++++++++++ lib/packages/docutils/rst.nim | 319 ++++++++++++++---- lib/packages/docutils/rstast.nim | 3 + lib/packages/docutils/rstgen.nim | 27 +- lib/pure/strutils.nim | 5 + .../expected/index.html | 4 + nimdoc/testproject/expected/nimdoc.out.css | 4 + .../expected/subdir/subdir_b/utils.html | 297 +++++++++++++++- .../expected/subdir/subdir_b/utils.idx | 14 + nimdoc/testproject/expected/testproject.html | 210 +++++++++++- nimdoc/testproject/expected/theindex.html | 52 ++- nimdoc/testproject/subdir/subdir_b/utils.nim | 65 ++++ .../subdir/subdir_b/utils_helpers.nim | 1 + .../subdir/subdir_b/utils_overview.rst | 8 + tests/stdlib/tdochelpers.nim | 155 +++++++++ tests/stdlib/trst.nim | 109 +++++- tests/stdlib/trstgen.nim | 19 +- 24 files changed, 1833 insertions(+), 143 deletions(-) create mode 100644 lib/packages/docutils/dochelpers.nim create mode 100644 nimdoc/testproject/subdir/subdir_b/utils_helpers.nim create mode 100644 nimdoc/testproject/subdir/subdir_b/utils_overview.rst create mode 100644 tests/stdlib/tdochelpers.nim diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 1acfc7489..2639840d1 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -13,7 +13,7 @@ import ast, strutils, strtabs, algorithm, sequtils, options, msgs, os, idents, wordrecg, syntaxes, renderer, lexer, - packages/docutils/rst, packages/docutils/rstgen, + packages/docutils/[rst, rstgen, dochelpers], json, xmltree, trees, types, typesrenderer, astalgo, lineinfos, intsets, pathutils, tables, nimpaths, renderverbatim, osproc @@ -44,8 +44,14 @@ type ## runnableExamples). substitutions: seq[string] ## Variable names in `doc.item`... sortName: string ## The string used for sorting in output + info: rstast.TLineInfo ## place where symbol was defined (for messages) + anchor: string ## e.g. HTML anchor + name: string ## short name of the symbol, not unique + ## (includes backticks ` if present) + detailedName: string ## longer name like `proc search(x: int): int` ModSection = object ## Section like Procs, Types, etc. - secItems: seq[Item] ## Pre-processed items. + secItems: Table[string, seq[Item]] + ## Map basic name -> pre-processed items. finalMarkup: string ## The items, after RST pass 2 and rendering. ModSections = array[TSymKind, ModSection] TocItem = object ## HTML TOC item @@ -91,12 +97,22 @@ type thisDir*: AbsoluteDir exampleGroups: OrderedTable[string, ExampleGroup] wroteSupportFiles*: bool + nimToRstFid: Table[lineinfos.FileIndex, rstast.FileIndex] + ## map Nim FileIndex -> RST one, it's needed because we keep them separate PDoc* = ref TDocumentor ## Alias to type less. proc add(dest: var ItemPre, rst: PRstNode) = dest.add ItemFragment(isRst: true, rst: rst) proc add(dest: var ItemPre, str: string) = dest.add ItemFragment(isRst: false, str: str) +proc addRstFileIndex(d: PDoc, info: lineinfos.TLineInfo): rstast.FileIndex = + let invalid = rstast.FileIndex(-1) + result = d.nimToRstFid.getOrDefault(info.fileIndex, default = invalid) + if result == invalid: + let fname = toFullPath(d.conf, info) + result = addFilename(d.sharedState, fname) + d.nimToRstFid[info.fileIndex] = result + proc cmpDecimalsIgnoreCase(a, b: string): int = ## For sorting with correct handling of cases like 'uint8' and 'uint16'. ## Also handles leading zeros well (however note that leading zeros are @@ -223,6 +239,7 @@ template declareClosures = of meFootnoteMismatch: k = errRstFootnoteMismatch of mwRedefinitionOfLabel: k = warnRstRedefinitionOfLabel of mwUnknownSubstitution: k = warnRstUnknownSubstitutionX + of mwAmbiguousLink: k = warnRstAmbiguousLink of mwBrokenLink: k = warnRstBrokenLink of mwUnsupportedLanguage: k = warnRstLanguageXNotSupported of mwUnsupportedField: k = warnRstFieldXNotSupported @@ -236,7 +253,7 @@ template declareClosures = result = getCurrentDir() / s if not fileExists(result): result = "" -proc parseRst(text, filename: string, +proc parseRst(text: string, line, column: int, conf: ConfigRef, sharedState: PRstSharedState): PRstNode = declareClosures() @@ -352,7 +369,8 @@ proc getVarIdx(varnames: openArray[string], id: string): int = proc genComment(d: PDoc, n: PNode): PRstNode = if n.comment.len > 0: - result = parseRst(n.comment, toFullPath(d.conf, n.info), + d.sharedState.currFileIdx = addRstFileIndex(d, n.info) + result = parseRst(n.comment, toLinenumber(n.info), toColumn(n.info) + DocColOffset, d.conf, d.sharedState) @@ -885,6 +903,57 @@ proc genSeeSrc(d: PDoc, path: string, line: int): string = "path", path.string, "line", $line, "url", gitUrl, "commit", commit, "devel", develBranch]]) +proc symbolPriority(k: TSymKind): int = + result = case k + of skMacro: -3 + of skTemplate: -2 + of skIterator: -1 + else: 0 # including skProc which have higher priority + # documentation itself has even higher priority 1 + +proc toLangSymbol(k: TSymKind, n: PNode, baseName: string): LangSymbol = + ## Converts symbol info (names/types/parameters) in `n` into format + ## `LangSymbol` convenient for ``rst.nim``/``dochelpers.nim``. + result.name = baseName.nimIdentNormalize + result.symKind = k.toHumanStr + if k in routineKinds: + var + paramTypes: seq[string] + renderParamTypes(paramTypes, n[paramsPos], toNormalize=true) + let paramNames = renderParamNames(n[paramsPos], toNormalize=true) + # In some rare cases (system.typeof) parameter type is not set for default: + doAssert paramTypes.len <= paramNames.len + for i in 0 ..< paramNames.len: + if i < paramTypes.len: + result.parameters.add (paramNames[i], paramTypes[i]) + else: + result.parameters.add (paramNames[i], "") + result.parametersProvided = true + + result.outType = renderOutType(n[paramsPos], toNormalize=true) + + if k in {skProc, skFunc, skType, skIterator}: + # Obtain `result.generics` + # Use `n[miscPos]` since n[genericParamsPos] does not contain constraints + var genNode: PNode = nil + if k == skType: + genNode = n[1] # FIXME: what is index 1? + else: + if n[miscPos].kind != nkEmpty: + genNode = n[miscPos][1] # FIXME: what is index 1? + if genNode != nil: + var literal = "" + var r: TSrcGen + initTokRender(r, genNode, {renderNoBody, renderNoComments, + renderNoPragmas, renderNoProcDefs}) + var kind = tkEof + while true: + getNextTok(r, kind, literal) + if kind == tkEof: + break + if kind != tkSpaces: + result.generics.add(literal.nimIdentNormalize) + proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags) = if (docFlags != kForceExport) and not isVisible(d, nameNode): return let @@ -915,6 +984,8 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags) = inc(d.id) let plainNameEsc = esc(d.target, plainName.strip) + detailedName = k.toHumanStr & " " & ( + if k in routineKinds: plainName else: name) uniqueName = if k in routineKinds: plainNameEsc else: name sortName = if k in routineKinds: plainName.strip else: name cleanPlainSymbol = renderPlainSymbolName(nameNode) @@ -923,20 +994,32 @@ proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags) = symbolOrId = d.newUniquePlainSymbol(complexSymbol) symbolOrIdEnc = encodeUrl(symbolOrId, usePlus = false) deprecationMsg = genDeprecationMsg(d, pragmaNode) + rstLangSymbol = toLangSymbol(k, n, cleanPlainSymbol) + + # we generate anchors automatically for subsequent use in doc comments + let lineinfo = rstast.TLineInfo( + line: nameNode.info.line, col: nameNode.info.col, + fileIndex: addRstFileIndex(d, nameNode.info)) + addAnchorNim(d.sharedState, refn = symbolOrId, tooltip = detailedName, + rstLangSymbol, priority = symbolPriority(k), info = lineinfo) nodeToHighlightedHtml(d, n, result, {renderNoBody, renderNoComments, renderDocComments, renderSyms}, symbolOrIdEnc) let seeSrc = genSeeSrc(d, toFullPath(d.conf, n.info), n.info.line.int) - d.section[k].secItems.add Item( + d.section[k].secItems.mgetOrPut(cleanPlainSymbol, newSeq[Item]()).add Item( descRst: comm, sortName: sortName, + info: lineinfo, + anchor: symbolOrId, + detailedName: detailedName, + name: name, substitutions: @[ - "name", name, "uniqueName", uniqueName, + "uniqueName", uniqueName, "header", result, "itemID", $d.id, "header_plain", plainNameEsc, "itemSym", cleanPlainSymbol, - "itemSymOrID", symbolOrId, "itemSymEnc", plainSymbolEnc, + "itemSymEnc", plainSymbolEnc, "itemSymOrIDEnc", symbolOrIdEnc, "seeSrc", seeSrc, "deprecationMsg", deprecationMsg]) @@ -1184,6 +1267,11 @@ proc generateDoc*(d: PDoc, n, orig: PNode, docFlags: DocFlags = kDefault) = if comm.len != 0: d.modDescPre.add(comm) else: discard +proc overloadGroupName(s: string, k: TSymKind): string = + ## Turns a name like `f` into anchor `f-procs-all` + #s & " " & k.toHumanStr & "s all" + s & "-" & k.toHumanStr & "s-all" + proc finishGenerateDoc*(d: var PDoc) = ## Perform 2nd RST pass for resolution of links/footnotes/headings... # copy file map `filenames` to ``rstgen.nim`` for its warnings @@ -1197,6 +1285,21 @@ proc finishGenerateDoc*(d: var PDoc) = break preparePass2(d.sharedState, firstRst) + # add anchors to overload groups before RST resolution + for k in TSymKind: + if k in routineKinds: + for plainName, overloadChoices in d.section[k].secItems: + if overloadChoices.len > 1: + let refn = overloadGroupName(plainName, k) + let tooltip = "$1 ($2 overloads)" % [ + k.toHumanStr & " " & plainName, $overloadChoices.len] + addAnchorNim(d.sharedState, refn, tooltip, + LangSymbol(symKind: k.toHumanStr, name: plainName, + isGroup: true), + priority = symbolPriority(k), + # select index `0` just to have any meaningful warning: + info = overloadChoices[0].info) + # Finalize fragments of ``.nim`` or ``.rst`` file proc renderItemPre(d: PDoc, fragments: ItemPre, result: var string) = for f in fragments: @@ -1207,14 +1310,33 @@ proc finishGenerateDoc*(d: var PDoc) = of false: result &= f.str proc cmp(x, y: Item): int = cmpDecimalsIgnoreCase(x.sortName, y.sortName) for k in TSymKind: - for item in d.section[k].secItems.sorted(cmp): - var itemDesc: string - renderItemPre(d, item.descRst, itemDesc) - d.section[k].finalMarkup.add( - getConfigVar(d.conf, "doc.item") % ( - item.substitutions & @["desc", itemDesc])) - itemDesc = "" - d.section[k].secItems.setLen 0 + # add symbols to section for each `k`, while optionally wrapping + # overloadable items with the same basic name by ``doc.item2`` + let overloadableNames = toSeq(keys(d.section[k].secItems)) + for plainName in overloadableNames.sorted(cmpDecimalsIgnoreCase): + var overloadChoices = d.section[k].secItems[plainName] + overloadChoices.sort(cmp) + var nameContent = "" + for item in overloadChoices: + var itemDesc: string + renderItemPre(d, item.descRst, itemDesc) + nameContent.add( + getConfigVar(d.conf, "doc.item") % ( + item.substitutions & @[ + "desc", itemDesc, + "name", item.name, + "itemSymOrID", item.anchor])) + if k in routineKinds: + let plainNameEsc1 = esc(d.target, plainName.strip) + let plainNameEsc2 = esc(d.target, plainName.strip, escMode=emUrl) + d.section[k].finalMarkup.add( + getConfigVar(d.conf, "doc.item2") % ( + @["header_plain", plainNameEsc1, + "overloadGroupName", overloadGroupName(plainNameEsc2, k), + "content", nameContent])) + else: + d.section[k].finalMarkup.add(nameContent) + d.section[k].secItems.clear renderItemPre(d, d.modDescPre, d.modDescFinal) d.modDescPre.setLen 0 d.hasToc = d.hasToc or d.sharedState.hasToc @@ -1493,7 +1615,7 @@ proc commandRstAux(cache: IdentCache, conf: ConfigRef; filename: AbsoluteFile, outExt: string) = var filen = addFileExt(filename, "txt") var d = newDocumentor(filen, cache, conf, outExt, isPureRst = true) - let rst = parseRst(readFile(filen.string), filen.string, + let rst = parseRst(readFile(filen.string), line=LineRstInit, column=ColRstInit, conf, d.sharedState) d.modDescPre = @[ItemFragment(isRst: true, rst: rst)] diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index 8bd5a0890..e13387be6 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -50,6 +50,7 @@ type warnSmallLshouldNotBeUsed = "SmallLshouldNotBeUsed", warnUnknownMagic = "UnknownMagic", warnRstRedefinitionOfLabel = "RedefinitionOfLabel", warnRstUnknownSubstitutionX = "UnknownSubstitutionX", + warnRstAmbiguousLink = "AmbiguousLink", warnRstBrokenLink = "BrokenLink", warnRstLanguageXNotSupported = "LanguageXNotSupported", warnRstFieldXNotSupported = "FieldXNotSupported", @@ -123,6 +124,7 @@ const warnUnknownMagic: "unknown magic '$1' might crash the compiler", warnRstRedefinitionOfLabel: "redefinition of label '$1'", warnRstUnknownSubstitutionX: "unknown substitution '$1'", + warnRstAmbiguousLink: "ambiguous doc link $1", warnRstBrokenLink: "broken link '$1'", warnRstLanguageXNotSupported: "language '$1' not supported", warnRstFieldXNotSupported: "field '$1' not supported", diff --git a/compiler/typesrenderer.nim b/compiler/typesrenderer.nim index 0da05d70d..c81ff283e 100644 --- a/compiler/typesrenderer.nim +++ b/compiler/typesrenderer.nim @@ -11,6 +11,12 @@ import renderer, strutils, ast, types const defaultParamSeparator* = "," +template mayNormalize(s: string): string = + if toNormalize: + s.nimIdentNormalize + else: + s + proc renderPlainSymbolName*(n: PNode): string = ## Returns the first non '*' nkIdent node from the tree. ## @@ -30,24 +36,26 @@ proc renderPlainSymbolName*(n: PNode): string = result = "" #internalError(n.info, "renderPlainSymbolName() with " & $n.kind) -proc renderType(n: PNode): string = +proc renderType(n: PNode, toNormalize: bool): string = ## Returns a string with the node type or the empty string. + ## This proc should be kept in sync with `toLangSymbols` from + ## ``lib/packages/docutils/dochelpers.nim``. case n.kind: - of nkIdent: result = n.ident.s - of nkSym: result = typeToString(n.sym.typ) + of nkIdent: result = mayNormalize(n.ident.s) + of nkSym: result = mayNormalize(typeToString(n.sym.typ)) of nkVarTy: if n.len == 1: - result = renderType(n[0]) + result = renderType(n[0], toNormalize) else: result = "var" of nkRefTy: if n.len == 1: - result = "ref." & renderType(n[0]) + result = "ref." & renderType(n[0], toNormalize) else: result = "ref" of nkPtrTy: if n.len == 1: - result = "ptr." & renderType(n[0]) + result = "ptr." & renderType(n[0], toNormalize) else: result = "ptr" of nkProcTy: @@ -57,36 +65,53 @@ proc renderType(n: PNode): string = assert params.kind == nkFormalParams assert params.len > 0 result = "proc(" - for i in 1..= 3 let typePos = n.len - 2 - let typeStr = renderType(n[typePos]) + let typeStr = renderType(n[typePos], toNormalize) result = typeStr for i in 1..= 2 - result = renderType(n[0]) & '[' - for i in 1.. 1: result.add ", " - result.add(renderType(n[i])) + result.add(renderType(n[i], toNormalize)) else: result = "" -proc renderParamTypes(found: var seq[string], n: PNode) = +proc renderParamNames*(n: PNode, toNormalize=false): seq[string] = + ## Returns parameter names of routine `n`. + doAssert n.kind == nkFormalParams + case n.kind + of nkFormalParams: + for i in 1.. 0 - var typeStr = renderType(n[typePos]) + var typeStr = renderType(n[typePos], toNormalize) if typeStr.len < 1 and n[typePos+1].kind != nkEmpty: # Try with the last node, maybe its a default value. let typ = n[typePos+1].typ @@ -111,7 +136,8 @@ proc renderParamTypes(found: var seq[string], n: PNode) = found.add($n) #internalError(n.info, "renderParamTypes(found,n) with " & $n.kind) -proc renderParamTypes*(n: PNode, sep = defaultParamSeparator): string = +proc renderParamTypes*(n: PNode, sep = defaultParamSeparator, + toNormalize=false): string = ## Returns the types contained in `n` joined by `sep`. ## ## This proc expects to be passed as `n` the parameters of any callable. The @@ -120,6 +146,10 @@ proc renderParamTypes*(n: PNode, sep = defaultParamSeparator): string = ## other characters may appear too, like ``[]`` or ``|``. result = "" var found: seq[string] = @[] - renderParamTypes(found, n) + renderParamTypes(found, n, toNormalize) if found.len > 0: result = found.join(sep) + +proc renderOutType*(n: PNode, toNormalize=false): string = + assert n.kind == nkFormalParams + result = renderType(n[0], toNormalize) diff --git a/config/nimdoc.cfg b/config/nimdoc.cfg index 162b2b4a3..c1074f344 100644 --- a/config/nimdoc.cfg +++ b/config/nimdoc.cfg @@ -56,6 +56,17 @@ $seeSrc
    """ +# A wrapper of a few overloaded `doc.item`s with the same basic name +# * $header_plain - see above +# * $overloadGroupName - the anchor for this whole group +# * $content - string containing `doc.item`s themselves +doc.item2 = """ + +
    +$content +
    +""" + # Chunk of HTML emitted for each entry in the HTML table of contents. # See doc.item for available substitution variables. diff --git a/config/nimdoc.tex.cfg b/config/nimdoc.tex.cfg index 4aff9b379..d7ab652eb 100644 --- a/config/nimdoc.tex.cfg +++ b/config/nimdoc.tex.cfg @@ -19,7 +19,8 @@ doc.section.toc = "" doc.item = """ \vspace{1em} -\phantomsection\addcontentsline{toc}{subsection}{$uniqueName} +\phantomsection\addcontentsline{toc}{subsubsection}{$uniqueName} +\label{$itemSymOrID}\hypertarget{$itemSymOrID}{} \begin{rstdocitem} $header @@ -30,6 +31,13 @@ $desc \end{addmargin} """ +doc.item2 = """ +\phantomsection\addcontentsline{toc}{subsection}{$header_plain} +\label{$overloadGroupName}\hypertarget{$overloadGroupName}{} + +$content +""" + doc.item.toc = "" doc.toc = r"\tableofcontents \newpage" diff --git a/doc/docgen.rst b/doc/docgen.rst index 2074ee06f..51e7102eb 100644 --- a/doc/docgen.rst +++ b/doc/docgen.rst @@ -229,6 +229,176 @@ Output:: Note that the `jsondoc`:option: command outputs its JSON without pretty-printing it, while `jsondoc0`:option: outputs pretty-printed JSON. + +Referencing Nim symbols: simple documentation links +=================================================== + +You can reference Nim identifiers from Nim documentation comments, currently +only inside their ``.nim`` file (or inside a ``.rst`` file included from +a ``.nim``). The point is that such links will be resolved automatically +by `nim doc`:cmd: (or `nim jsondoc`:cmd: or `nim doc2tex`:cmd:). +This pertains to any exported symbol like `proc`, `const`, `iterator`, etc. +Syntax for referencing is basically a normal RST one: addition of +underscore `_` to a *link text*. +Link text is either one word or a group of words enclosed by backticks `\`` +(for a one word case backticks are usually omitted). +Link text will be displayed *as is* while *link target* will be set to +the anchor [*]_ of Nim symbol that corresponds to link text. + +.. [*] anchors' format is described in `HTML anchor generation`_ section below. + +If you have a constant: + +.. code:: Nim + + const pi* = 3.14 + +then it should be referenced in one of the 2 forms: + +A. non-qualified (no symbol kind specification):: + pi_ +B. qualified (with symbol kind specification):: + `const pi`_ + +For routine kinds there are more options. Consider this definition: + +.. code:: Nim + + proc foo*(a: int, b: float): string + +Generally following syntax is allowed for referencing `foo`: + +* short (without parameters): + + A. non-qualified:: + + foo_ + + B. qualified:: + + `proc foo`_ + +* longer variants (with parameters): + + A. non-qualified: + + 1) specifying parameters names:: + + `foo(a, b)`_ + + 2) specifying parameters types:: + + `foo(int, float)`_ + + 3) specifying both names and types:: + + `foo(a: int, b: float)`_ + + 4) output parameter can also be specified if you wish:: + + `foo(a: int, b: float): string`_ + + B. qualified: all 4 options above are valid. + Particularly you can use the full format:: + + `proc foo(a: int, b: float): string`_ + +.. Tip:: Avoid cluttering your text with extraneous information by using + one of shorter forms:: + + binarySearch_ + `binarySearch(a, key, cmp)`_ + + Brevity is better for reading! If you use a short form and have an + ambiguity problem (see below) then just add some additional info. + +Symbol kind like `proc` can also be specified in the postfix form:: + + `foo proc`_ + `walkDir(d: string) iterator`_ + +.. Warning:: An ambiguity in resolving documentation links may arise because of: + + 1. clash with other RST anchors + * manually setup anchors + * automatically set up, e.g. section names + 2. collision with other Nim symbols: + + * routines with different parameters can exist e.g. for + `proc` and `template`. In this case they are split between their + corresponding sections in output file. Qualified references are + useful in this case -- just disambiguate by referring to these + sections explicitly:: + + See `foo proc`_ and `foo template`_. + + * because in Nim `proc` and `iterator` belong to different namespaces, + so there can be a collision even if parameters are the same. + Use `\`proc foo\`_`:literal: or `\`iterator foo\`_`:literal: then. + + Any ambiguity is always reported with Nim compiler warnings and an anchor + with higher priority is selected. Manual anchors have highest + priority, then go automatic RST anchors; then Nim-generated anchors + (while procs have higher priority than other Nim symbol kinds). + +Generic parameters can also be used. All in all, this long form will be +recognized fine:: + + `proc binarySearch*[T; K](a: openArray[T], key: K, cmp: proc(T, K)): int`_ + +**Limitations**: + +1. The parameters of a nested routine type can be specified only with types + (without parameter names, see form A.2 above). + E.g. for this signature: + + .. code:: Nim + + proc binarySearch*[T, K](a: openArray[T]; key: K; + cmp: proc (x: T; y: K): int {.closure.}): int + ~~ ~~ ~~~~~ + + you cannot use names underlined by `~~` so it must be referenced with + ``cmp: proc(T, K)``. Hence these forms are valid:: + + `binarySearch(a: openArray[T], key: K, cmp: proc(T, K))`_ + `binarySearch(openArray[T], K, proc(T, K))`_ + `binarySearch(a, key, cmp)`_ +2. Default values in routine parameters are not recognized, one needs to + specify the type and/or name instead. E.g. for referencing `proc f(x = 7)` + use one of the mentioned forms:: + + `f(int)`_ or `f(x)`_ or `f(x: int)`_. +3. Generic parameters must be given the same way as in the + definition of referenced symbol. + + * their names should be the same + * parameters list should be given the same way, e.g. without substitutions + between commas (,) and semicolons (;). + +.. Note:: A bit special case is operators + (as their signature is also defined with `\``): + + .. code:: Nim + + func `$`(x: MyType): string + func `[]`*[T](x: openArray[T]): T + + A short form works without additional backticks:: + + `$`_ + `[]`_ + + However for fully-qualified reference copy-pasting backticks (`) into other + backticks will not work in our RST parser (because we use Markdown-like + inline markup rules). You need either to delete backticks or keep + them and escape with backslash \\:: + + no backticks: `func $`_ + escaped: `func \`$\``_ + no backticks: `func [][T](x: openArray[T]): T`_ + escaped: `func \`[]\`[T](x: openArray[T]): T`_ + Related Options =============== diff --git a/doc/nimdoc.css b/doc/nimdoc.css index 3353f1cda..7c819ea30 100644 --- a/doc/nimdoc.css +++ b/doc/nimdoc.css @@ -263,6 +263,10 @@ a.reference-toplevel { font-weight: bold; } +a.nimdoc { + word-spacing: 0.3em; +} + a.toc-backref { text-decoration: none; color: var(--text); } diff --git a/lib/packages/docutils/dochelpers.nim b/lib/packages/docutils/dochelpers.nim new file mode 100644 index 000000000..c488c4d99 --- /dev/null +++ b/lib/packages/docutils/dochelpers.nim @@ -0,0 +1,267 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2021 Nim contributors +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Integration helpers between ``docgen.nim`` and ``rst.nim``. +## +## Function `toLangSymbol(linkText)`_ produces a signature `docLink` of +## `type LangSymbol`_ in ``rst.nim``, while `match(generated, docLink)`_ +## matches it with `generated`, produced from `PNode` by ``docgen.rst``. + +import rstast + +type + LangSymbol* = object ## symbol signature in Nim + symKind*: string ## "proc", "const", etc + name*: string ## plain symbol name without any parameters + generics*: string ## generic parameters (without brackets) + isGroup*: bool ## is LangSymbol a group with overloads? + # the following fields are valid iff `isGroup` == false + # (always false when parsed by `toLangSymbol` because link like foo_ + # can point to just a single symbol foo, e.g. proc). + parametersProvided*: bool ## to disambiguate `proc f`_ and `proc f()`_ + parameters*: seq[tuple[name: string, `type`: string]] + ## name-type seq, e.g. for proc + outType*: string ## result type, e.g. for proc + +func nimIdentBackticksNormalize*(s: string): string = + ## Normalizes the string `s` as a Nim identifier. + ## + ## Unlike `nimIdentNormalize` removes spaces and backticks. + ## + ## .. Warning:: No checking (e.g. that identifiers cannot start from + ## digits or '_', or that number of backticks is even) is performed. + runnableExamples: + doAssert nimIdentBackticksNormalize("Foo_bar") == "Foobar" + doAssert nimIdentBackticksNormalize("FoO BAr") == "Foobar" + doAssert nimIdentBackticksNormalize("`Foo BAR`") == "Foobar" + doAssert nimIdentBackticksNormalize("` Foo BAR `") == "Foobar" + # not a valid identifier: + doAssert nimIdentBackticksNormalize("`_x_y`") == "_xy" + result = newString(s.len) + var firstChar = true + var j = 0 + for i in 0..len(s) - 1: + if s[i] in {'A'..'Z'}: + if not firstChar: # to lowercase + result[j] = chr(ord(s[i]) + (ord('a') - ord('A'))) + else: + result[j] = s[i] + firstChar = false + inc j + elif s[i] notin {'_', ' ', '`'}: + result[j] = s[i] + inc j + firstChar = false + elif s[i] == '_' and firstChar: + result[j] = '_' + inc j + firstChar = false + else: discard # just omit '`' or ' ' + if j != s.len: setLen(result, j) + +proc toLangSymbol*(linkText: PRstNode): LangSymbol = + ## Parses `linkText` into a more structured form using a state machine. + ## + ## This proc is designed to allow link syntax with operators even + ## without escaped backticks inside:: + ## + ## `proc *`_ + ## `proc []`_ + ## + ## This proc should be kept in sync with the `renderTypes` proc from + ## ``compiler/typesrenderer.nim``. + assert linkText.kind in {rnRef, rnInner} + + const NimDefs = ["proc", "func", "macro", "method", "iterator", + "template", "converter", "const", "type", "var"] + type + State = enum + inBeginning + afterSymKind + beforeSymbolName # auxiliary state to catch situations like `proc []`_ after space + atSymbolName + afterSymbolName + genericsPar + parameterName + parameterType + outType + var state = inBeginning + var curIdent = "" + template flushIdent() = + if curIdent != "": + case state + of inBeginning: doAssert false, "incorrect state inBeginning" + of afterSymKind: result.symKind = curIdent + of beforeSymbolName: doAssert false, "incorrect state beforeSymbolName" + of atSymbolName: result.name = curIdent.nimIdentBackticksNormalize + of afterSymbolName: doAssert false, "incorrect state afterSymbolName" + of genericsPar: result.generics = curIdent + of parameterName: result.parameters.add (curIdent, "") + of parameterType: + for a in countdown(result.parameters.len - 1, 0): + if result.parameters[a].`type` == "": + result.parameters[a].`type` = curIdent + of outType: result.outType = curIdent + curIdent = "" + var parens = 0 + let L = linkText.sons.len + template s(i: int): string = linkText.sons[i].text + var i = 0 + template nextState = + case s(i) + of " ": + if state == afterSymKind: + flushIdent + state = beforeSymbolName + of "`": + curIdent.add "`" + inc i + while i < L: # add contents between ` ` as a whole + curIdent.add s(i) + if s(i) == "`": + break + inc i + curIdent = curIdent.nimIdentBackticksNormalize + if state in {inBeginning, afterSymKind, beforeSymbolName}: + state = atSymbolName + flushIdent + state = afterSymbolName + of "[": + if state notin {inBeginning, afterSymKind, beforeSymbolName}: + inc parens + if state in {inBeginning, afterSymKind, beforeSymbolName}: + state = atSymbolName + curIdent.add s(i) + elif state in {atSymbolName, afterSymbolName} and parens == 1: + flushIdent + state = genericsPar + curIdent.add s(i) + else: curIdent.add s(i) + of "]": + if state notin {inBeginning, afterSymKind, beforeSymbolName, atSymbolName}: + dec parens + if state == genericsPar and parens == 0: + curIdent.add s(i) + flushIdent + else: curIdent.add s(i) + of "(": + inc parens + if state in {inBeginning, afterSymKind, beforeSymbolName}: + result.parametersProvided = true + state = atSymbolName + flushIdent + state = parameterName + elif state in {atSymbolName, afterSymbolName, genericsPar} and parens == 1: + result.parametersProvided = true + flushIdent + state = parameterName + else: curIdent.add s(i) + of ")": + dec parens + if state in {parameterName, parameterType} and parens == 0: + flushIdent + state = outType + else: curIdent.add s(i) + of "{": # remove pragmas + while i < L: + if s(i) == "}": + break + inc i + of ",", ";": + if state in {parameterName, parameterType} and parens == 1: + flushIdent + state = parameterName + else: curIdent.add s(i) + of "*": # skip export symbol + if state == atSymbolName: + flushIdent + state = afterSymbolName + elif state == afterSymbolName: + discard + else: curIdent.add "*" + of ":": + if state == outType: discard + elif state == parameterName: + flushIdent + state = parameterType + else: curIdent.add ":" + else: + let isPostfixSymKind = i > 0 and i == L - 1 and + result.symKind == "" and s(i) in NimDefs + if isPostfixSymKind: # for links like `foo proc`_ + result.symKind = s(i) + else: + case state + of inBeginning: + if s(i) in NimDefs: + state = afterSymKind + else: + state = atSymbolName + curIdent.add s(i) + of afterSymKind, beforeSymbolName: + state = atSymbolName + curIdent.add s(i) + of parameterType: + case s(i) + of "ref": curIdent.add "ref." + of "ptr": curIdent.add "ptr." + of "var": discard + else: curIdent.add s(i).nimIdentBackticksNormalize + of atSymbolName: + curIdent.add s(i) + else: + curIdent.add s(i).nimIdentBackticksNormalize + while i < L: + nextState + inc i + if state == afterSymKind: # treat `type`_ as link to symbol `type` + state = atSymbolName + flushIdent + result.isGroup = false + +proc match*(generated: LangSymbol, docLink: LangSymbol): bool = + ## Returns true if `generated` can be a target for `docLink`. + ## If `generated` is an overload group then only `symKind` and `name` + ## are compared for success. + result = true + if docLink.symKind != "": + if generated.symKind == "proc": + result = docLink.symKind in ["proc", "func"] + else: + result = generated.symKind == docLink.symKind + if not result: return + result = generated.name == docLink.name + if not result: return + if generated.isGroup: + # if `()` were added then it's not a reference to the whole group: + return not docLink.parametersProvided + if docLink.generics != "": + result = generated.generics == docLink.generics + if not result: return + if docLink.outType != "": + result = generated.outType == docLink.outType + if not result: return + if docLink.parametersProvided: + result = generated.parameters.len == docLink.parameters.len + if not result: return + var onlyType = false + for i in 0 ..< generated.parameters.len: + let g = generated.parameters[i] + let d = docLink.parameters[i] + if i == 0: + if g.`type` == d.name: + onlyType = true # only types, not names, are provided in `docLink` + if onlyType: + result = g.`type` == d.name: + else: + if d.`type` != "": + result = g.`type` == d.`type` + if not result: return + result = g.name == d.name + if not result: return diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 29234f28b..2e908f4e5 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -114,7 +114,7 @@ ## .. _`extra features`: ## ## Optional additional features, turned on by ``options: RstParseOption`` in -## `rstParse proc <#rstParse,string,string,int,int,bool,RstParseOptions,FindFileHandler,MsgHandler>`_: +## `proc rstParse`_: ## ## * emoji / smiley symbols ## * Markdown tables @@ -196,7 +196,7 @@ ## .. _Sphinx roles: https://www.sphinx-doc.org/en/master/usage/restructuredtext/roles.html import - os, strutils, rstast, std/enumutils, algorithm, lists, sequtils, + os, strutils, rstast, dochelpers, std/enumutils, algorithm, lists, sequtils, std/private/miscdollars, tables from highlite import SourceLanguage, getSourceLanguage @@ -231,6 +231,7 @@ type meFootnoteMismatch = "mismatch in number of footnotes and their refs: $1", mwRedefinitionOfLabel = "redefinition of label '$1'", mwUnknownSubstitution = "unknown substitution '$1'", + mwAmbiguousLink = "ambiguous doc link $1", mwBrokenLink = "broken link '$1'", mwUnsupportedLanguage = "language '$1' not supported", mwUnsupportedField = "field '$1' not supported", @@ -473,12 +474,42 @@ type hasPeers: bool # has headings on the same level of hierarchy? LevelMap = seq[LevelInfo] # Saves for each possible title adornment # style its level in the current document. + SubstitutionKind = enum + rstSubstitution = "substitution", + hyperlinkAlias = "hyperlink alias", + implicitHyperlinkAlias = "implicitly-generated hyperlink alias" Substitution = object + kind*: SubstitutionKind key*: string value*: PRstNode - AnchorSubst = tuple - mainAnchor: string - aliases: seq[string] + info*: TLineInfo # place where the substitution was defined + AnchorRule = enum + arInternalRst, ## For automatically generated RST anchors (from + ## headings, footnotes, inline internal targets): + ## case-insensitive, 1-space-significant (by RST spec) + arNim ## For anchors generated by ``docgen.rst``: Nim-style case + ## sensitivity, etc. (see `proc normalizeNimName`_ for details) + arHyperlink, ## For links with manually set anchors in + ## form `text `_ + RstAnchorKind = enum + manualDirectiveAnchor = "manual directive anchor", + manualInlineAnchor = "manual inline anchor", + footnoteAnchor = "footnote anchor", + headlineAnchor = "implicitly-generated headline anchor" + AnchorSubst = object + mainAnchor: ref string # A reference name that will be inserted directly + # into HTML/Latex. It's declared as `ref` because + # it can be shared between aliases. + info: TLineInfo # where the anchor was defined + priority: int + case kind: range[arInternalRst .. arNim] + of arInternalRst: + anchorType: RstAnchorKind + of arNim: + tooltip: string # displayed tooltip for Nim-generated anchors + langSym: LangSymbol + AnchorSubstTable = Table[string, seq[AnchorSubst]] + # use `seq` to account for duplicate anchors FootnoteType = enum fnManualNumber, # manually numbered footnote like [3] fnAutoNumber, # auto-numbered footnote [#] @@ -505,7 +536,8 @@ type currRoleKind: RstNodeKind # ... and its node kind subs: seq[Substitution] # substitutions refs*: seq[Substitution] # references - anchors*: seq[AnchorSubst] # internal target substitutions + anchors*: AnchorSubstTable + # internal target substitutions lineFootnoteNum: seq[TLineInfo] # footnote line, auto numbers .. [#] lineFootnoteNumRef: seq[TLineInfo] # footnote line, their reference [#]_ currFootnoteNumRef: int # ... their counter for `resolveSubs` @@ -518,7 +550,7 @@ type findFile: FindFileHandler # How to find files. filenames*: RstFileTable # map file name <-> FileIndex (for storing # file names for warnings after 1st stage) - currFileIdx: FileIndex # current index in `filesnames` + currFileIdx*: FileIndex # current index in `filenames` hasToc*: bool PRstSharedState* = ref RstSharedState @@ -532,6 +564,7 @@ type ## in case of error/warning reporting to ## (relative) line/column of the token. curAnchor*: string # variable to track latest anchor in s.anchors + curAnchorName*: string # corresponding name in human-readable format EParseError* = object of ValueError @@ -590,13 +623,16 @@ proc whichRoleAux(sym: string): RstNodeKind = proc len(filenames: RstFileTable): int = filenames.idxToFilename.len -proc setCurrFilename(s: PRstSharedState, file1: string) = +proc addFilename*(s: PRstSharedState, file1: string): FileIndex = + ## Returns index of filename, adding it if it has not been used before let nextIdx = s.filenames.len.FileIndex - let v = getOrDefault(s.filenames.filenameToIdx, file1, default = nextIdx) - if v == nextIdx: - s.filenames.filenameToIdx[file1] = v + result = getOrDefault(s.filenames.filenameToIdx, file1, default = nextIdx) + if result == nextIdx: + s.filenames.filenameToIdx[file1] = result s.filenames.idxToFilename.add file1 - s.currFileIdx = v + +proc setCurrFilename*(s: PRstSharedState, file1: string) = + s.currFileIdx = addFilename(s, file1) proc getFilename(filenames: RstFileTable, fid: FileIndex): string = doAssert(0 <= fid.int and fid.int < filenames.len, @@ -730,6 +766,8 @@ proc initParser(p: var RstParser, sharedState: PRstSharedState) = p.s = sharedState proc addNodesAux(n: PRstNode, result: var string) = + if n == nil: + return if n.kind == rnLeaf: result.add(n.text) else: @@ -738,6 +776,11 @@ proc addNodesAux(n: PRstNode, result: var string) = proc addNodes(n: PRstNode): string = n.addNodesAux(result) +proc linkName(n: PRstNode): string = + ## Returns a normalized reference name, see: + ## https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#reference-names + n.addNodes.toLowerAscii + proc rstnodeToRefnameAux(n: PRstNode, r: var string, b: var bool) = template special(s) = if b: @@ -804,15 +847,26 @@ proc findSub(s: PRstSharedState, n: PRstNode): int = return i result = -1 +proc lineInfo(p: RstParser, iTok: int): TLineInfo = + result.col = int16(p.col + p.tok[iTok].col) + result.line = uint16(p.line + p.tok[iTok].line) + result.fileIndex = p.s.currFileIdx + +proc lineInfo(p: RstParser): TLineInfo = lineInfo(p, p.idx) +# TODO: we need this simplification because we don't preserve exact starting +# token of currently parsed element: +proc prevLineInfo(p: RstParser): TLineInfo = lineInfo(p, p.idx-1) + proc setSub(p: var RstParser, key: string, value: PRstNode) = var length = p.s.subs.len for i in 0 ..< length: if key == p.s.subs[i].key: p.s.subs[i].value = value return - p.s.subs.add(Substitution(key: key, value: value)) + p.s.subs.add(Substitution(key: key, value: value, info: prevLineInfo(p))) -proc setRef(p: var RstParser, key: string, value: PRstNode) = +proc setRef(p: var RstParser, key: string, value: PRstNode, + refType: SubstitutionKind) = var length = p.s.refs.len for i in 0 ..< length: if key == p.s.refs[i].key: @@ -820,37 +874,111 @@ proc setRef(p: var RstParser, key: string, value: PRstNode) = rstMessage(p, mwRedefinitionOfLabel, key) p.s.refs[i].value = value return - p.s.refs.add(Substitution(key: key, value: value)) + p.s.refs.add(Substitution(kind: refType, key: key, value: value, + info: prevLineInfo(p))) -proc findRef(s: PRstSharedState, key: string): PRstNode = +proc findRef(s: PRstSharedState, key: string): seq[Substitution] = for i in countup(0, high(s.refs)): if key == s.refs[i].key: - return s.refs[i].value + result.add s.refs[i] -proc addAnchor(p: var RstParser, refn: string, reset: bool) = - ## add anchor `refn` to anchor aliases and update last anchor ``curAnchor`` - if p.curAnchor == "": - p.s.anchors.add (refn, @[refn]) +# Ambiguity in links: we don't follow procedure of removing implicit targets +# defined in https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#implicit-hyperlink-targets +# Instead we just give explicit links a higher priority than to implicit ones +# and report ambiguities as warnings. Hopefully it is easy to remove +# ambiguities manually. Nim auto-generated links from ``docgen.nim`` +# have lowest priority: 1 (for procs) and below for other symbol types. + +proc refPriority(k: SubstitutionKind): int = + case k + of rstSubstitution: result = 8 + of hyperlinkAlias: result = 7 + of implicitHyperlinkAlias: result = 2 + +proc internalRefPriority(k: RstAnchorKind): int = + case k + of manualDirectiveAnchor: result = 6 + of manualInlineAnchor: result = 5 + of footnoteAnchor: result = 4 + of headlineAnchor: result = 3 + +proc addAnchorRst(p: var RstParser, name: string, refn: string, reset: bool, + anchorType: RstAnchorKind) = + ## Adds anchor `refn` with an alias `name` and + ## updates the corresponding `curAnchor` / `curAnchorName`. + let prio = internalRefPriority(anchorType) + if p.curAnchorName == "": + var anchRef = new string + anchRef[] = refn + p.s.anchors.mgetOrPut(name, newSeq[AnchorSubst]()).add( + AnchorSubst(kind: arInternalRst, mainAnchor: anchRef, priority: prio, + info: prevLineInfo(p), anchorType: anchorType)) else: - p.s.anchors[^1].mainAnchor = refn - p.s.anchors[^1].aliases.add refn + # override previous mainAnchor by `ref` in all aliases + var anchRef = p.s.anchors[p.curAnchorName][0].mainAnchor + anchRef[] = refn + p.s.anchors.mgetOrPut(name, newSeq[AnchorSubst]()).add( + AnchorSubst(kind: arInternalRst, mainAnchor: anchRef, priority: prio, + info: prevLineInfo(p), anchorType: anchorType)) if reset: p.curAnchor = "" + p.curAnchorName = "" else: p.curAnchor = refn + p.curAnchorName = name -proc findMainAnchor(s: PRstSharedState, refn: string): string = - for subst in s.anchors: - if subst.mainAnchor == refn: # no need to rename - result = subst.mainAnchor - break - var toLeave = false - for anchor in subst.aliases: - if anchor == refn: # this anchor will be named as mainAnchor - result = subst.mainAnchor - toLeave = true - if toLeave: - break +proc addAnchorNim*(s: var PRstSharedState, refn: string, tooltip: string, + langSym: LangSymbol, priority: int, + info: TLineInfo) = + ## Adds an anchor `refn` (`mainAnchor`), which follows + ## the rule `arNim` (i.e. a symbol in ``*.nim`` file) + var anchRef = new string + anchRef[] = refn + s.anchors.mgetOrPut(langSym.name, newSeq[AnchorSubst]()).add( + AnchorSubst(kind: arNim, mainAnchor: anchRef, langSym: langSym, + tooltip: tooltip, priority: priority, + info: info)) + +proc findMainAnchorNim(s: PRstSharedState, signature: PRstNode, + info: TLineInfo): + seq[AnchorSubst] = + let langSym = toLangSymbol(signature) + let substitutions = s.anchors.getOrDefault(langSym.name, + newSeq[AnchorSubst]()) + if substitutions.len == 0: + return + # map symKind (like "proc") -> found symbols/groups: + var found: Table[string, seq[AnchorSubst]] + for s in substitutions: + if s.kind == arNim: + if match(s.langSym, langSym): + found.mgetOrPut(s.langSym.symKind, newSeq[AnchorSubst]()).add s + for symKind, sList in found: + if sList.len == 1: + result.add sList[0] + else: # > 1, there are overloads, potential ambiguity in this `symKind` + if langSym.parametersProvided: + # there are non-group signatures, select only them + for s in sList: + if not s.langSym.isGroup: + result.add s + else: # when there are many overloads a link like foo_ points to all + # of them, so selecting the group + var foundGroup = true + for s in sList: + if s.langSym.isGroup: + result.add s + foundGroup = true + break + doAssert foundGroup, "docgen has not generated the group" + +proc findMainAnchorRst(s: PRstSharedState, linkText: string, info: TLineInfo): + seq[AnchorSubst] = + let name = linkText.toLowerAscii + let substitutions = s.anchors.getOrDefault(name, newSeq[AnchorSubst]()) + for s in substitutions: + if s.kind == arInternalRst: + result.add s proc addFootnoteNumManual(p: var RstParser, num: int) = ## add manually-numbered footnote @@ -860,13 +988,6 @@ proc addFootnoteNumManual(p: var RstParser, num: int) = return p.s.footnotes.add((fnManualNumber, num, -1, -1, $num)) -proc lineInfo(p: RstParser, iTok: int): TLineInfo = - result.col = int16(p.col + p.tok[iTok].col) - result.line = uint16(p.line + p.tok[iTok].line) - result.fileIndex = p.s.currFileIdx - -proc lineInfo(p: RstParser): TLineInfo = lineInfo(p, p.idx) - proc addFootnoteNumAuto(p: var RstParser, label: string) = ## add auto-numbered footnote. ## Empty label [#] means it'll be resolved by the occurrence. @@ -989,6 +1110,7 @@ proc newRstNodeA(p: var RstParser, kind: RstNodeKind): PRstNode = if p.curAnchor != "": result.anchor = p.curAnchor p.curAnchor = "" + p.curAnchorName = "" template newLeaf(s: string): PRstNode = newRstLeaf(s) @@ -1255,7 +1377,7 @@ proc parsePostfix(p: var RstParser, n: PRstNode): PRstNode = else: newKind = rnHyperlink newSons = @[a, b] - setRef(p, rstnodeToRefname(a), b) + setRef(p, rstnodeToRefname(a), b, implicitHyperlinkAlias) result = newRstNode(newKind, newSons) else: # some link that will be resolved in `resolveSubs` newKind = rnRef @@ -1562,7 +1684,8 @@ proc parseInline(p: var RstParser, father: PRstNode) = inc p.idx parseUntil(p, n, "`", false) let refn = rstnodeToRefname(n) - p.s.anchors.add (refn, @[refn]) + addAnchorRst(p, name = linkName(n), refn = refn, reset = true, + anchorType=manualInlineAnchor) father.add(n) elif roSupportMarkdown in p.s.options and currentTok(p).symbol == "```": inc p.idx @@ -2084,7 +2207,8 @@ proc parseHeadline(p: var RstParser): PRstNode = result.level = getLevel(p, c, hasOverline=false) checkHeadingHierarchy(p, result.level) p.s.hCurLevel = result.level - addAnchor(p, rstnodeToRefname(result), reset=true) + addAnchorRst(p, linkName(result), rstnodeToRefname(result), reset=true, + anchorType=headlineAnchor) proc parseOverline(p: var RstParser): PRstNode = var c = currentTok(p).symbol[0] @@ -2106,7 +2230,8 @@ proc parseOverline(p: var RstParser): PRstNode = if currentTok(p).kind == tkAdornment: inc p.idx if currentTok(p).kind == tkIndent: inc p.idx - addAnchor(p, rstnodeToRefname(result), reset=true) + addAnchorRst(p, linkName(result), rstnodeToRefname(result), reset=true, + anchorType=headlineAnchor) type IntSeq = seq[int] @@ -2837,7 +2962,7 @@ proc parseFootnote(p: var RstParser): PRstNode = anchor.add $p.s.lineFootnoteSym.len of fnCitation: anchor.add rstnodeToRefname(label) - addAnchor(p, anchor, reset=true) + addAnchorRst(p, anchor, anchor, reset=true, anchorType=footnoteAnchor) result.anchor = anchor if currentTok(p).kind == tkWhite: inc p.idx discard parseBlockContent(p, result, parseSectionWrapper) @@ -2858,13 +2983,23 @@ proc parseDotDot(p: var RstParser): PRstNode = elif match(p, p.idx, " _"): # hyperlink target: inc p.idx, 2 - var a = getReferenceName(p, ":") + var ending = ":" + if currentTok(p).symbol == "`": + inc p.idx + ending = "`" + var a = getReferenceName(p, ending) + if ending == "`": + if currentTok(p).symbol == ":": + inc p.idx + else: + rstMessage(p, meExpected, ":") if currentTok(p).kind == tkWhite: inc p.idx var b = untilEol(p) if len(b) == 0: # set internal anchor - addAnchor(p, rstnodeToRefname(a), reset=false) + addAnchorRst(p, linkName(a), rstnodeToRefname(a), reset=false, + anchorType=manualDirectiveAnchor) else: # external hyperlink - setRef(p, rstnodeToRefname(a), b) + setRef(p, rstnodeToRefname(a), b, refType=hyperlinkAlias) elif match(p, p.idx, " |"): # substitution definitions: inc p.idx, 2 @@ -2892,7 +3027,7 @@ proc rstParsePass1*(fragment: string, sharedState: PRstSharedState): PRstNode = ## Parses an RST `fragment`. ## The result should be further processed by - ## `preparePass2` and `resolveSubs` (which is pass 2). + ## preparePass2_ and resolveSubs_ (which is pass 2). var p: RstParser initParser(p, sharedState) p.line = line @@ -2905,6 +3040,65 @@ proc preparePass2*(s: PRstSharedState, mainNode: PRstNode) = countTitles(s, mainNode) orderFootnotes(s) +proc resolveLink(s: PRstSharedState, n: PRstNode) : PRstNode = + # Associate this link alias with its target and change node kind to + # rnHyperlink or rnInternalRef appropriately. + type LinkDef = object + ar: AnchorRule + priority: int + tooltip: string + target: PRstNode + info: TLineInfo + proc cmp(x, y: LinkDef): int = + result = cmp(x.priority, y.priority) + if result == 0: + result = cmp(x.target, y.target) + var foundLinks: seq[LinkDef] + let text = newRstNode(rnInner, n.sons) + let refn = rstnodeToRefname(n) + var hyperlinks = findRef(s, refn) + for y in hyperlinks: + foundLinks.add LinkDef(ar: arHyperlink, priority: refPriority(y.kind), + target: y.value, info: y.info, + tooltip: "(" & $y.kind & ")") + let substRst = findMainAnchorRst(s, text.addNodes, n.info) + for subst in substRst: + foundLinks.add LinkDef(ar: arInternalRst, priority: subst.priority, + target: newLeaf(subst.mainAnchor[]), + info: subst.info, + tooltip: "(" & $subst.anchorType & ")") + if roNimFile in s.options: + let substNim = findMainAnchorNim(s, signature=text, n.info) + for subst in substNim: + foundLinks.add LinkDef(ar: arNim, priority: subst.priority, + target: newLeaf(subst.mainAnchor[]), + info: subst.info, tooltip: subst.tooltip) + foundLinks.sort(cmp = cmp, order = Descending) + let linkText = addNodes(n) + if foundLinks.len >= 1: + let kind = if foundLinks[0].ar == arHyperlink: rnHyperlink + elif foundLinks[0].ar == arNim: rnNimdocRef + else: rnInternalRef + result = newRstNode(kind) + result.sons = @[text, foundLinks[0].target] + if kind == rnNimdocRef: result.tooltip = foundLinks[0].tooltip + if foundLinks.len > 1: # report ambiguous link + var targets = newSeq[string]() + for l in foundLinks: + var t = " " + if s.filenames.len > 1: + t.add getFilename(s.filenames, l.info.fileIndex) + let n = l.info.line + let c = l.info.col + ColRstOffset + t.add "($1, $2): $3" % [$n, $c, l.tooltip] + targets.add t + rstMessage(s.filenames, s.msgHandler, n.info, mwAmbiguousLink, + "`$1`\n clash:\n$2" % [ + linkText, targets.join("\n")]) + else: # nothing found + result = n + rstMessage(s.filenames, s.msgHandler, n.info, mwBrokenLink, linkText) + proc resolveSubs*(s: PRstSharedState, n: PRstNode): PRstNode = ## Makes pass 2 of RST parsing. ## Resolves substitutions and anchor aliases, groups footnotes. @@ -2933,21 +3127,7 @@ proc resolveSubs*(s: PRstSharedState, n: PRstNode): PRstNode = elif s.hTitleCnt == 0: n.level += 1 of rnRef: - let refn = rstnodeToRefname(n) - var y = findRef(s, refn) - if y != nil: - result = newRstNode(rnHyperlink) - let text = newRstNode(rnInner, n.sons) - result.sons = @[text, y] - else: - let anchor = findMainAnchor(s, refn) - if anchor != "": - result = newRstNode(rnInternalRef) - let text = newRstNode(rnInner, n.sons) - result.sons = @[text, # visible text of reference - newLeaf(anchor)] # link itself - else: - rstMessage(s.filenames, s.msgHandler, n.info, mwBrokenLink, refn) + result = resolveLink(s, n) of rnFootnote: var (fnType, num) = getFootnoteType(n.sons[0]) case fnType @@ -2993,9 +3173,10 @@ proc resolveSubs*(s: PRstSharedState, n: PRstNode): PRstNode = of fnCitation: result.add n.sons[0] refn.add rstnodeToRefname(n) - let anch = findMainAnchor(s, refn) - if anch != "": - result.add newLeaf(anch) # add link + # TODO: correctly report ambiguities + let anchorInfo = findMainAnchorRst(s, refn, n.info) + if anchorInfo.len != 0: + result.add newLeaf(anchorInfo[0].mainAnchor[]) # add link else: rstMessage(s.filenames, s.msgHandler, n.info, mwBrokenLink, refn) result.add newLeaf(refn) # add link diff --git a/lib/packages/docutils/rstast.nim b/lib/packages/docutils/rstast.nim index e1ed7c099..bc7b9a650 100644 --- a/lib/packages/docutils/rstast.nim +++ b/lib/packages/docutils/rstast.nim @@ -43,6 +43,7 @@ type rnFootnoteGroup, # footnote group - exists for a purely stylistic # reason: to display a few footnotes as 1 block rnStandaloneHyperlink, rnHyperlink, rnRef, rnInternalRef, rnFootnoteRef, + rnNimdocRef, # reference to automatically generated Nim symbol rnDirective, # a general directive rnDirArg, # a directive argument (for some directives). # here are directives that are not rnDirective: @@ -104,6 +105,8 @@ type rnInterpretedText, rnField, rnInlineCode, rnCodeBlock, rnFootnoteRef: info*: TLineInfo ## To have line/column info for warnings at ## nodes that are post-processed after parsing + of rnNimdocRef: + tooltip*: string else: discard anchor*: string ## anchor, internal link target diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 8eac37307..a556aa2e3 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -60,8 +60,8 @@ type MetaEnum* = enum metaNone, metaTitle, metaSubtitle, metaAuthor, metaVersion - EscapeMode = enum # in Latex text inside options [] and URLs is - # escaped slightly differently than in normal text + EscapeMode* = enum # in Latex text inside options [] and URLs is + # escaped slightly differently than in normal text emText, emOption, emUrl # emText is currently used for code also RstGenerator* = object of RootObj @@ -201,7 +201,9 @@ proc addTexChar(dest: var string, c: char, escMode: EscapeMode) = ## All escapes that need to work in text and code blocks (`emText` mode) ## should start from \ (to be compatible with fancyvrb/fvextra). case c - of '_', '$', '&', '#', '%': add(dest, "\\" & c) + of '_', '&', '#', '%': add(dest, "\\" & c) + # commands \label and \pageref don't accept \$ by some reason but OK with $: + of '$': (if escMode == emUrl: add(dest, c) else: add(dest, "\\" & c)) # \~ and \^ have a special meaning unless they are followed by {} of '~', '^': add(dest, "\\" & c & "{}") # Latex loves to substitute ` to opening quote, even in texttt mode! @@ -1180,7 +1182,8 @@ proc renderAdmonition(d: PDoc, n: PRstNode, result: var string) = "$1\n\\end{rstadmonition}\n", result) -proc renderHyperlink(d: PDoc, text, link: PRstNode, result: var string, external: bool) = +proc renderHyperlink(d: PDoc, text, link: PRstNode, result: var string, + external: bool, nimdoc = false, tooltip="") = var linkStr = "" block: let mode = d.escMode @@ -1189,14 +1192,19 @@ proc renderHyperlink(d: PDoc, text, link: PRstNode, result: var string, external d.escMode = mode var textStr = "" renderRstToOut(d, text, textStr) + let nimDocStr = if nimdoc: " nimdoc" else: "" + var tooltipStr = "" + if tooltip != "": + tooltipStr = """ title="$1"""" % [ esc(d.target, tooltip) ] if external: dispA(d.target, result, - "$1", - "\\href{$2}{$1}", [textStr, linkStr]) + "$1", + "\\href{$2}{$1}", [textStr, linkStr, nimDocStr, tooltipStr]) else: dispA(d.target, result, - "$1", - "\\hyperlink{$2}{$1} (p.~\\pageref{$2})", [textStr, linkStr]) + "$1", + "\\hyperlink{$2}{$1} (p.~\\pageref{$2})", + [textStr, linkStr, nimDocStr, tooltipStr]) proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = if n == nil: return @@ -1329,6 +1337,9 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = renderHyperlink(d, text=n.sons[0], link=n.sons[0], result, external=true) of rnInternalRef: renderHyperlink(d, text=n.sons[0], link=n.sons[1], result, external=false) + of rnNimdocRef: + renderHyperlink(d, text=n.sons[0], link=n.sons[1], result, external=false, + nimdoc=true, tooltip=n.tooltip) of rnHyperlink: renderHyperlink(d, text=n.sons[0], link=n.sons[1], result, external=true) of rnFootnoteRef: diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 873949ca6..2234a8625 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -273,6 +273,11 @@ func nimIdentNormalize*(s: string): string = ## ## That means to convert to lower case and remove any '_' on all characters ## except first one. + ## + ## .. Warning:: Backticks (`) are not handled: they remain *as is* and + ## spaces are preserved. See `nimIdentBackticksNormalize + ## `_ for + ## an alternative approach. runnableExamples: doAssert nimIdentNormalize("Foo_bar") == "Foobar" result = newString(s.len) diff --git a/nimdoc/test_out_index_dot_html/expected/index.html b/nimdoc/test_out_index_dot_html/expected/index.html index 3499b5326..69dd2eb2e 100644 --- a/nimdoc/test_out_index_dot_html/expected/index.html +++ b/nimdoc/test_out_index_dot_html/expected/index.html @@ -121,6 +121,8 @@ window.addEventListener('DOMContentLoaded', main);

    Procs

    + +
    proc foo() {....raises: [], tags: [].}
    @@ -130,6 +132,8 @@ I do foo
    +
    +
    diff --git a/nimdoc/testproject/expected/nimdoc.out.css b/nimdoc/testproject/expected/nimdoc.out.css index 3353f1cda..7c819ea30 100644 --- a/nimdoc/testproject/expected/nimdoc.out.css +++ b/nimdoc/testproject/expected/nimdoc.out.css @@ -263,6 +263,10 @@ a.reference-toplevel { font-weight: bold; } +a.nimdoc { + word-spacing: 0.3em; +} + a.toc-backref { text-decoration: none; color: var(--text); } diff --git a/nimdoc/testproject/expected/subdir/subdir_b/utils.html b/nimdoc/testproject/expected/subdir/subdir_b/utils.html index 47901ce0d..c64039941 100644 --- a/nimdoc/testproject/expected/subdir/subdir_b/utils.html +++ b/nimdoc/testproject/expected/subdir/subdir_b/utils.html @@ -106,7 +106,10 @@ window.addEventListener('DOMContentLoaded', main);
  • Types
      -
    • G
    • +
    • SomeType
    • @@ -115,9 +118,54 @@ window.addEventListener('DOMContentLoaded', main);
    • Procs
        -
          fn2 + + + + + + + +
            fn3 @@ -159,6 +207,11 @@ window.addEventListener('DOMContentLoaded', main);
          • fn10(a: int): int
          • +
          +
            someType
          • +
          • + Iterators + +
          • Templates
              @@ -197,7 +261,13 @@ window.addEventListener('DOMContentLoaded', main);
              -

              +

              This is a description of the utils module.

              +

              Links work:

              + +

              This is now a header

              Next header

              And so on

              @@ -209,10 +279,31 @@ window.addEventListener('DOMContentLoaded', main);
              1. Other case value
              2. Second case.
              -

              +

              Ref group fn2 or specific function like fn2() or fn2( int ) or fn2(int, float).

              +

              Ref generics like this: binarySearch or binarySearch(openArray[T], K, proc (T, K)) or proc binarySearch(openArray[T], K, proc (T, K)) or in different style: proc binarysearch(openarray[T], K, proc(T, K)). Can be combined with export symbols and type parameters: binarysearch*[T, K](openArray[T], K, proc (T, K)). With spaces binary search.

              +

              Note that proc can be used in postfix form: binarySearch proc.

              +

              Ref. type like G and type G and G[T] and type G*[T].

              +Ref. [] is the same as proc `[]`(G[T]) because there are no overloads. The full form: proc `[]`*[T](x: G[T]): TRef. []= aka `[]=`(G[T], int, T).Ref. $ aka proc $ or proc `$`.Ref. $(a: ref SomeType).Ref. foo_bar aka iterator foo_bar_.Ref. fn[T; U,V: SomeFloat]().Ref. 'big or func `'big` or `'big`(string).

              Types

              +
              +
              G[T] = object
              +  val: T
              +
              +
              + + + +
              +
              SomeType = enum
                 enumValueA, enumValueB, enumValueC
              @@ -227,6 +318,109 @@ window.addEventListener('DOMContentLoaded', main);

              Procs

              + +
              +
              +
              proc `$`[T](a: G[T]): string
              +
              + + + +
              +
              +
              +
              proc `$`[T](a: ref SomeType): string
              +
              + + + +
              +
              + +
              + +
              +
              +
              func `'big`(a: string): SomeType {....raises: [], tags: [].}
              +
              + + + +
              +
              + +
              + +
              +
              +
              proc `[]`[T](x: G[T]): T
              +
              + + + +
              +
              + +
              + +
              +
              +
              proc `[]=`[T](a: var G[T]; index: int; value: T)
              +
              + + + +
              +
              + +
              + +
              +
              +
              proc binarySearch[T, K](a: openArray[T]; key: K;
              +                        cmp: proc (x: T; y: K): int {.closure.}): int
              +
              + + + +
              +
              + +
              + +
              +
              +
              proc f(x: G[int]) {....raises: [], tags: [].}
              +
              + +There is also variant f(G[string]) + +
              +
              +
              +
              proc f(x: G[string]) {....raises: [], tags: [].}
              +
              + +See also f(G[int]). + +
              +
              + +
              + +
              +
              +
              proc fn[T; U, V: SomeFloat]()
              +
              + + + +
              +
              + +
              + +
              proc fn2() {....raises: [], tags: [].}
              @@ -235,6 +429,26 @@ comment
              +
              +
              proc fn2(x: int) {....raises: [], tags: [].}
              +
              + +fn2 comment + +
              +
              +
              +
              proc fn2(x: int; y: float) {....raises: [], tags: [].}
              +
              + + + +
              +
              + +
              + +
              proc fn3(): auto {....raises: [], tags: [].}
              @@ -243,6 +457,10 @@ comment
              + +
              + +
              proc fn4(): auto {....raises: [], tags: [].}
              @@ -251,6 +469,10 @@ comment
              + +
              + +
              proc fn5() {....raises: [], tags: [].}
              @@ -259,6 +481,10 @@ comment
              + +
              + +
              proc fn6() {....raises: [], tags: [].}
              @@ -267,6 +493,10 @@ comment
              + +
              + +
              proc fn7() {....raises: [], tags: [].}
              @@ -275,6 +505,10 @@ comment
              + +
              + +
              proc fn8(): auto {....raises: [], tags: [].}
              @@ -283,6 +517,10 @@ comment
              + +
              + +
              func fn9(a: int): int {....raises: [], tags: [].}
              @@ -291,6 +529,10 @@ comment
              + +
              + +
              func fn10(a: int): int {....raises: [], tags: [].}
              @@ -299,6 +541,22 @@ comment
              + +
              + +
              +
              +
              proc funWithGenerics[T, U: SomeFloat](a: T; b: U)
              +
              + + + +
              +
              + +
              + +
              proc someType(): SomeType {....raises: [], tags: [].}
              @@ -308,10 +566,31 @@ constructor.
              +
              + +
              +
              +

              Iterators

              +
              + +
              +
              +
              iterator fooBar(a: seq[SomeType]): int {....raises: [], tags: [].}
              +
              + + + +
              +
              + +
              +

              Templates

              + +
              template aEnum(): untyped
              @@ -320,6 +599,10 @@ constructor.
              + +
              + +
              template bEnum(): untyped
              @@ -328,6 +611,10 @@ constructor.
              + +
              + +
              template fromUtilsGen(): untyped
              @@ -339,6 +626,8 @@ should be shown in utils.html only
              +
              +
              diff --git a/nimdoc/testproject/expected/subdir/subdir_b/utils.idx b/nimdoc/testproject/expected/subdir/subdir_b/utils.idx index a87393f09..441bd8675 100644 --- a/nimdoc/testproject/expected/subdir/subdir_b/utils.idx +++ b/nimdoc/testproject/expected/subdir/subdir_b/utils.idx @@ -1,9 +1,14 @@ +funWithGenerics subdir/subdir_b/utils.html#funWithGenerics,T,U utils: funWithGenerics[T, U: SomeFloat](a: T; b: U) enumValueA subdir/subdir_b/utils.html#enumValueA SomeType.enumValueA enumValueB subdir/subdir_b/utils.html#enumValueB SomeType.enumValueB enumValueC subdir/subdir_b/utils.html#enumValueC SomeType.enumValueC SomeType subdir/subdir_b/utils.html#SomeType utils: SomeType +G subdir/subdir_b/utils.html#G utils: G someType subdir/subdir_b/utils.html#someType_2 utils: someType(): SomeType fn2 subdir/subdir_b/utils.html#fn2 utils: fn2() +fn2 subdir/subdir_b/utils.html#fn2,int utils: fn2(x: int) +fn2 subdir/subdir_b/utils.html#fn2,int,float utils: fn2(x: int; y: float) +binarySearch subdir/subdir_b/utils.html#binarySearch,openArray[T],K,proc(T,K) utils: binarySearch[T, K](a: openArray[T]; key: K;\n cmp: proc (x: T; y: K): int {.closure.}): int fn3 subdir/subdir_b/utils.html#fn3 utils: fn3(): auto fn4 subdir/subdir_b/utils.html#fn4 utils: fn4(): auto fn5 subdir/subdir_b/utils.html#fn5 utils: fn5() @@ -15,6 +20,15 @@ fn10 subdir/subdir_b/utils.html#fn10,int utils: fn10(a: int): int aEnum subdir/subdir_b/utils.html#aEnum.t utils: aEnum(): untyped bEnum subdir/subdir_b/utils.html#bEnum.t utils: bEnum(): untyped fromUtilsGen subdir/subdir_b/utils.html#fromUtilsGen.t utils: fromUtilsGen(): untyped +f subdir/subdir_b/utils.html#f,G[int] utils: f(x: G[int]) +f subdir/subdir_b/utils.html#f,G[string] utils: f(x: G[string]) +`[]` subdir/subdir_b/utils.html#[],G[T] utils: `[]`[T](x: G[T]): T +`[]=` subdir/subdir_b/utils.html#[]=,G[T],int,T utils: `[]=`[T](a: var G[T]; index: int; value: T) +`$` subdir/subdir_b/utils.html#$,G[T] utils: `$`[T](a: G[T]): string +`$` subdir/subdir_b/utils.html#$,ref.SomeType utils: `$`[T](a: ref SomeType): string +fooBar subdir/subdir_b/utils.html#fooBar.i,seq[SomeType] utils: fooBar(a: seq[SomeType]): int +fn subdir/subdir_b/utils.html#fn utils: fn[T; U, V: SomeFloat]() +`'big` subdir/subdir_b/utils.html#'big,string utils: `'big`(a: string): SomeType This is now a header subdir/subdir_b/utils.html#this-is-now-a-header This is now a header Next header subdir/subdir_b/utils.html#this-is-now-a-header-next-header Next header And so on subdir/subdir_b/utils.html#next-header-and-so-on And so on diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html index dbacb57a7..e45492dac 100644 --- a/nimdoc/testproject/expected/testproject.html +++ b/nimdoc/testproject/expected/testproject.html @@ -559,6 +559,8 @@ This should be visible.

              Procs

              + +
              proc addfBug14485() {....raises: [], tags: [].}
              @@ -579,6 +581,10 @@ Some proc
              + +
              + +
              proc anything() {....raises: [], tags: [].}
              @@ -587,6 +593,10 @@ There is no block quote after blank lines at the beginning.
              + +
              + +
              proc asyncFun1(): Future[int] {....raises: [Exception, ValueError],
                                               tags: [RootEffect].}
              @@ -596,6 +606,10 @@ ok1
  • + + + +
    proc asyncFun2(): owned(Future[void]) {....raises: [Exception], tags: [RootEffect].}
    @@ -604,6 +618,10 @@ ok1
    + +
    + +
    proc asyncFun3(): owned(Future[void]) {....raises: [Exception], tags: [RootEffect].}
    @@ -614,6 +632,10 @@ ok1
    + +
    + +
    proc bar[T](a, b: T): T
    @@ -622,6 +644,10 @@ ok1
    + +
    + +
    proc baz() {....raises: [], tags: [].}
    @@ -641,6 +667,10 @@ This is deprecated without message.
    + +
    + +
    proc buzz[T](a, b: T): T {....deprecated: "since v0.20".}
    @@ -652,6 +682,10 @@ This is deprecated with a message.
    + +
    + +
    proc c_nonexistent(frmt: cstring): cint {.importc: "nonexistent",
         header: "<stdio.h>", varargs, discardable, ...raises: [], tags: [].}
    @@ -661,6 +695,10 @@ This is deprecated with a message.
    + +
    + +
    proc c_printf(frmt: cstring): cint {.importc: "printf", header: "<stdio.h>",
                                          varargs, discardable, ...raises: [], tags: [].}
    @@ -670,6 +708,10 @@ the c printf. etc.
    + +
    + +
    proc fromUtils3() {....raises: [], tags: [].}
    @@ -681,6 +723,10 @@ came form utils but should be shown where
    + +
    + +
    proc isValid[T](x: T): bool
    @@ -689,6 +735,27 @@ came form utils but should be shown where
    + +
    + +
    +
    +
    proc low[T: Ordinal | enum | range](x: T): T {.magic: "Low", noSideEffect,
    +    ...raises: [], tags: [].}
    +
    + +

    Returns the lowest possible value of an ordinal value x. As a special semantic rule, x may also be a type identifier.

    +

    See also:

    + +
    low(2) # => -9223372036854775808
    + +
    +
    + +
    + +
    proc low2[T: Ordinal | enum | range](x: T): T {.magic: "Low", noSideEffect,
         ...raises: [], tags: [].}
    @@ -704,19 +771,10 @@ came form utils but should be shown where
    -
    -
    proc low[T: Ordinal | enum | range](x: T): T {.magic: "Low", noSideEffect,
    -    ...raises: [], tags: [].}
    -
    -

    Returns the lowest possible value of an ordinal value x. As a special semantic rule, x may also be a type identifier.

    -

    See also:

    - -
    low(2) # => -9223372036854775808
    - -
    + +
    proc p1() {....raises: [], tags: [].}
    @@ -741,6 +799,10 @@ this is a nested doc comment
    + +
    + +
    func someFunc() {....raises: [], tags: [].}
    @@ -749,6 +811,10 @@ My someFunc. Stuff in
    + +
    + +
    proc tripleStrLitTest() {....raises: [], tags: [].}
    @@ -793,6 +859,10 @@ at indent 0
    + +
    + +
    proc z1(): Foo {....raises: [], tags: [].}
    @@ -801,6 +871,10 @@ cz1
    + +
    + +
    proc z2() {....raises: [], tags: [].}
    @@ -811,6 +885,10 @@ cz2
    + +
    + +
    proc z3() {....raises: [], tags: [].}
    @@ -819,6 +897,10 @@ cz3
    + +
    + +
    proc z4() {....raises: [], tags: [].}
    @@ -827,6 +909,10 @@ cz4
    + +
    + +
    proc z5(): int {....raises: [], tags: [].}
    @@ -835,6 +921,10 @@ cz5
    + +
    + +
    proc z6(): int {....raises: [], tags: [].}
    @@ -843,6 +933,10 @@ cz6
    + +
    + +
    proc z7(): int {....raises: [], tags: [].}
    @@ -851,6 +945,10 @@ cz7
    + +
    + +
    proc z8(): int {....raises: [], tags: [].}
    @@ -859,6 +957,10 @@ cz8
    + +
    + +
    proc z9() {....raises: [], tags: [].}
    @@ -869,6 +971,10 @@ cz8
    + +
    + +
    proc z10() {....raises: [], tags: [].}
    @@ -879,6 +985,10 @@ cz8
    + +
    + +
    proc z11() {....raises: [], tags: [].}
    @@ -889,6 +999,10 @@ cz8
    + +
    + +
    proc z12(): int {....raises: [], tags: [].}
    @@ -899,6 +1013,10 @@ cz8
    + +
    + +
    proc z13() {....raises: [], tags: [].}
    @@ -909,6 +1027,10 @@ cz13
    + +
    + +
    proc z17() {....raises: [], tags: [].}
    @@ -920,10 +1042,14 @@ cz17 rest
    +
    +

    Methods

    + +
    method method1(self: Moo) {.base, ...raises: [], tags: [].}
    @@ -932,6 +1058,10 @@ foo1
    + +
    + +
    method method2(self: Moo): int {.base, ...raises: [], tags: [].}
    @@ -940,6 +1070,10 @@ foo2
    + +
    + +
    method method3(self: Moo): int {.base, ...raises: [], tags: [].}
    @@ -949,10 +1083,14 @@ foo3
    +
    +

    Iterators

    + +
    iterator fromUtils1(): int {....raises: [], tags: [].}
    @@ -965,6 +1103,10 @@ foo3
    + +
    + +
    iterator iter1(n: int): int {....raises: [], tags: [].}
    @@ -973,6 +1115,10 @@ foo1
    + +
    + +
    iterator iter2(n: int): int {....raises: [], tags: [].}
    @@ -984,10 +1130,14 @@ foo2
    +
    +

    Macros

    + +
    macro bar(): untyped
    @@ -996,6 +1146,10 @@ foo2
    + +
    + +
    macro z16()
    @@ -1008,6 +1162,10 @@ foo2
    + +
    + +
    macro z18(): int
    @@ -1017,10 +1175,14 @@ cz18
    +
    +

    Templates

    + +
    template foo(a, b: SomeType)
    @@ -1029,6 +1191,10 @@ This does nothing
    + +
    + +
    template fromUtils2()
    @@ -1040,6 +1206,10 @@ ok3
    + +
    + +
    template myfn()
    @@ -1063,6 +1233,10 @@ bar
    + +
    + +
    template testNimDocTrailingExample()
    @@ -1073,6 +1247,10 @@ bar
    + +
    + +
    template z6t(): int
    @@ -1081,6 +1259,10 @@ cz6t
    + +
    + +
    template z14()
    @@ -1091,6 +1273,10 @@ cz14
    + +
    + +
    template z15()
    @@ -1110,6 +1296,8 @@ cz15
    +
    +
    diff --git a/nimdoc/testproject/expected/theindex.html b/nimdoc/testproject/expected/theindex.html index f72f6c37e..da76334d4 100644 --- a/nimdoc/testproject/expected/theindex.html +++ b/nimdoc/testproject/expected/theindex.html @@ -71,7 +71,25 @@ window.addEventListener('DOMContentLoaded', main);

    Index

    Modules: subdir/subdir_b/utils, testproject.

    API symbols

    -
    A:
    +
    binarySearch:
    buzz:
    +
    f:
    +
    fn:
    fn10:
    • utils: fn10(a: int): int
    • @@ -178,6 +212,10 @@ window.addEventListener('DOMContentLoaded', main);
      fn2:
      fn3:
      +
      fooBar:
      FooBuzz:
      +
      funWithGenerics:
      +
      G:
      isValid:
      • testproject: isValid[T](x: T): bool
      • diff --git a/nimdoc/testproject/subdir/subdir_b/utils.nim b/nimdoc/testproject/subdir/subdir_b/utils.nim index 37c91dd3c..5d881e620 100644 --- a/nimdoc/testproject/subdir/subdir_b/utils.nim +++ b/nimdoc/testproject/subdir/subdir_b/utils.nim @@ -1,5 +1,7 @@ ##[ +.. include:: ./utils_overview.rst + # This is now a header ## Next header @@ -19,13 +21,32 @@ More text. 1. Other case value 2. Second case. +Ref group fn2_ or specific function like `fn2()`_ +or `fn2( int )`_ or `fn2(int, +float)`_. + +Ref generics like this: binarySearch_ or `binarySearch(openArray[T], K, +proc (T, K))`_ or `proc binarySearch(openArray[T], K, proc (T, K))`_ or +in different style: `proc binarysearch(openarray[T], K, proc(T, K))`_. +Can be combined with export symbols and type parameters: +`binarysearch*[T, K](openArray[T], K, proc (T, K))`_. +With spaces `binary search`_. + +Note that `proc` can be used in postfix form: `binarySearch proc`_. + +Ref. type like G_ and `type G`_ and `G[T]`_ and `type G*[T]`_. + ]## +include ./utils_helpers + type SomeType* = enum enumValueA, enumValueB, enumValueC + G*[T] = object + val: T proc someType*(): SomeType = ## constructor. @@ -33,6 +54,14 @@ proc someType*(): SomeType = proc fn2*() = discard ## comment +proc fn2*(x: int) = + ## fn2 comment + discard +proc fn2*(x: int, y: float) = + discard +proc binarySearch*[T, K](a: openArray[T]; key: K; + cmp: proc (x: T; y: K): int {.closure.}): int = + discard proc fn3*(): auto = 1 ## comment proc fn4*(): auto = 2 * 3 + 4 ## comment proc fn5*() ## comment @@ -89,3 +118,39 @@ template fromUtilsGen*(): untyped = ## came form utils but should be shown where `fromUtilsGen` is called runnableExamples: discard """should be shown as examples for fromUtils3 in module calling fromUtilsGen""" + +proc f*(x: G[int]) = + ## There is also variant `f(G[string])`_ + discard +proc f*(x: G[string]) = + ## See also `f(G[int])`_. + discard + +## Ref. `[]`_ is the same as `proc \`[]\`(G[T])`_ because there are no +## overloads. The full form: `proc \`[]\`*[T](x: G[T]): T`_ + +proc `[]`*[T](x: G[T]): T = x.val + +## Ref. `[]=`_ aka `\`[]=\`(G[T], int, T)`_. + +proc `[]=`*[T](a: var G[T], index: int, value: T) = discard + +## Ref. `$`_ aka `proc $`_ or `proc \`$\``_. + +proc `$`*[T](a: G[T]): string = "" + +## Ref. `$(a: ref SomeType)`_. + +proc `$`*[T](a: ref SomeType): string = "" + +## Ref. foo_bar_ aka `iterator foo_bar_`_. + +iterator fooBar*(a: seq[SomeType]): int = discard + +## Ref. `fn[T; U,V: SomeFloat]()`_. + +proc fn*[T; U, V: SomeFloat]() = discard + +## Ref. `'big`_ or `func \`'big\``_ or `\`'big\`(string)`_. + +func `'big`*(a: string): SomeType = discard diff --git a/nimdoc/testproject/subdir/subdir_b/utils_helpers.nim b/nimdoc/testproject/subdir/subdir_b/utils_helpers.nim new file mode 100644 index 000000000..2c45ffb83 --- /dev/null +++ b/nimdoc/testproject/subdir/subdir_b/utils_helpers.nim @@ -0,0 +1 @@ +proc funWithGenerics*[T, U: SomeFloat](a: T, b: U) = discard diff --git a/nimdoc/testproject/subdir/subdir_b/utils_overview.rst b/nimdoc/testproject/subdir/subdir_b/utils_overview.rst new file mode 100644 index 000000000..58ce930bf --- /dev/null +++ b/nimdoc/testproject/subdir/subdir_b/utils_overview.rst @@ -0,0 +1,8 @@ +This is a description of the utils module. + +Links work: + +* other module: `iterators `_ (not in this dir, just an example) +* internal: `fn2(x)`_ +* internal included from another module: `funWithGenerics*[T, U: + SomeFloat](a: T, b: U)`_. diff --git a/tests/stdlib/tdochelpers.nim b/tests/stdlib/tdochelpers.nim new file mode 100644 index 000000000..5bcdf32d5 --- /dev/null +++ b/tests/stdlib/tdochelpers.nim @@ -0,0 +1,155 @@ +discard """ + output: ''' + +[Suite] Integration with Nim +''' +""" + +# tests for dochelpers.nim module + +import ../../lib/packages/docutils/[rstast, rst, dochelpers] +import unittest + +proc rstParseTest(text: string): PRstNode = + proc testMsgHandler(filename: string, line, col: int, msgkind: MsgKind, + arg: string) = + doAssert msgkind == mwBrokenLink + let r = rstParse(text, "-input-", LineRstInit, ColRstInit, + {roPreferMarkdown, roSupportMarkdown, roNimFile}, + msgHandler=testMsgHandler) + result = r.node + +suite "Integration with Nim": + test "simple symbol parsing (shortest form)": + let input1 = "g_".rstParseTest + check input1.toLangSymbol == LangSymbol(symKind: "", name: "g") + + test "simple symbol parsing (group of words)": + let input1 = "`Y`_".rstParseTest + check input1.toLangSymbol == LangSymbol(symKind: "", name: "Y") + + # this means not a statement 'type', it's a backticked identifier `type`: + let input2 = "`type`_".rstParseTest + check input2.toLangSymbol == LangSymbol(symKind: "", name: "type") + + let input3 = "`[]`_".rstParseTest + check input3.toLangSymbol == LangSymbol(symKind: "", name: "[]") + + let input4 = "`X Y Z`_".rstParseTest + check input4.toLangSymbol == LangSymbol(symKind: "", name: "Xyz") + + test "simple proc parsing": + let input1 = "proc f".rstParseTest + check input1.toLangSymbol == LangSymbol(symKind: "proc", name: "f") + + test "another backticked name": + let input1 = """`template \`type\``_""".rstParseTest + check input1.toLangSymbol == LangSymbol(symKind: "template", name: "type") + + test "simple proc parsing with parameters": + let input1 = "`proc f*()`_".rstParseTest + let input2 = "`proc f()`_".rstParseTest + let expected = LangSymbol(symKind: "proc", name: "f", + parametersProvided: true) + check input1.toLangSymbol == expected + check input2.toLangSymbol == expected + + test "symbol parsing with 1 parameter": + let input = "`f(G[int])`_".rstParseTest + let expected = LangSymbol(symKind: "", name: "f", + parameters: @[("G[int]", "")], + parametersProvided: true) + check input.toLangSymbol == expected + + test "more proc parsing": + let input1 = "`proc f[T](x:G[T]):M[T]`_".rstParseTest + let input2 = "`proc f[ T ] ( x: G [T] ): M[T]`_".rstParseTest + let input3 = "`proc f*[T](x: G[T]): M[T]`_".rstParseTest + let expected = LangSymbol(symKind: "proc", + name: "f", + generics: "[T]", + parameters: @[("x", "G[T]")], + parametersProvided: true, + outType: "M[T]") + check(input1.toLangSymbol == expected) + check(input2.toLangSymbol == expected) + check(input3.toLangSymbol == expected) + + test "advanced proc parsing with Nim identifier normalization": + let input = """`proc binarySearch*[T, K](a: openArray[T]; key: K; + cmp: proc (x: T; y: K): int)`_""".rstParseTest + let expected = LangSymbol(symKind: "proc", + name: "binarysearch", + generics: "[T,K]", + parameters: @[ + ("a", "openarray[T]"), + ("key", "K"), + ("cmp", "proc(x:T;y:K):int")], + parametersProvided: true, + outType: "") + check(input.toLangSymbol == expected) + + test "the same without proc": + let input = """`binarySearch*[T, K](a: openArray[T]; key: K; + cmp: proc (x: T; y: K): int {.closure.})`_""".rstParseTest + let expected = LangSymbol(symKind: "", + name: "binarysearch", + generics: "[T,K]", + parameters: @[ + ("a", "openarray[T]"), + ("key", "K"), + ("cmp", "proc(x:T;y:K):int")], + parametersProvided: true, + outType: "") + check(input.toLangSymbol == expected) + + test "operator $ with and without backticks": + let input1 = """`func \`$\`*[T](a: \`open Array\`[T]): string`_""". + rstParseTest + let input2 = """`func $*[T](a: \`open Array\`[T]): string`_""". + rstParseTest + let expected = LangSymbol(symKind: "func", + name: "$", + generics: "[T]", + parameters: @[("a", "openarray[T]")], + parametersProvided: true, + outType: "string") + check(input1.toLangSymbol == expected) + check(input2.toLangSymbol == expected) + + test "operator [] with and without backticks": + let input1 = """`func \`[]\`[T](a: \`open Array\`[T], idx: int): T`_""". + rstParseTest + let input2 = """`func [][T](a: \`open Array\`[T], idx: int): T`_""". + rstParseTest + let expected = LangSymbol(symKind: "func", + name: "[]", + generics: "[T]", + parameters: @[("a", "openarray[T]"), + ("idx", "int")], + parametersProvided: true, + outType: "T") + check(input1.toLangSymbol == expected) + check(input2.toLangSymbol == expected) + + test "postfix symbol specifier #1": + let input = """`walkDir iterator`_""". + rstParseTest + let expected = LangSymbol(symKind: "iterator", + name: "walkdir") + check(input.toLangSymbol == expected) + + test "postfix symbol specifier #2": + let input1 = """`\`[]\`[T](a: \`open Array\`[T], idx: int): T func`_""". + rstParseTest + let input2 = """`[][T](a: \`open Array\`[T], idx: int): T func`_""". + rstParseTest + let expected = LangSymbol(symKind: "func", + name: "[]", + generics: "[T]", + parameters: @[("a", "openarray[T]"), + ("idx", "int")], + parametersProvided: true, + outType: "T") + check(input1.toLangSymbol == expected) + check(input2.toLangSymbol == expected) diff --git a/tests/stdlib/trst.nim b/tests/stdlib/trst.nim index e9f67324c..e4609a671 100644 --- a/tests/stdlib/trst.nim +++ b/tests/stdlib/trst.nim @@ -17,9 +17,7 @@ discard """ # tests for rst module -import ../../lib/packages/docutils/rstgen -import ../../lib/packages/docutils/rst -import ../../lib/packages/docutils/rstast +import ../../lib/packages/docutils/[rstgen, rst, rstast] import unittest, strutils import std/private/miscdollars import os @@ -61,6 +59,52 @@ proc toAst(input: string, result = e.msg suite "RST parsing": + test "References are whitespace-neutral and case-insensitive": + # refname is 'lexical-analysis', the same for all the 3 variants: + check(dedent""" + Lexical Analysis + ================ + + Ref. `Lexical Analysis`_ or `Lexical analysis`_ or `lexical analysis`_. + """.toAst == + dedent""" + rnInner + rnHeadline level=1 + rnLeaf 'Lexical' + rnLeaf ' ' + rnLeaf 'Analysis' + rnParagraph + rnLeaf 'Ref' + rnLeaf '.' + rnLeaf ' ' + rnInternalRef + rnInner + rnLeaf 'Lexical' + rnLeaf ' ' + rnLeaf 'Analysis' + rnLeaf 'lexical-analysis' + rnLeaf ' ' + rnLeaf 'or' + rnLeaf ' ' + rnInternalRef + rnInner + rnLeaf 'Lexical' + rnLeaf ' ' + rnLeaf 'analysis' + rnLeaf 'lexical-analysis' + rnLeaf ' ' + rnLeaf 'or' + rnLeaf ' ' + rnInternalRef + rnInner + rnLeaf 'lexical' + rnLeaf ' ' + rnLeaf 'analysis' + rnLeaf 'lexical-analysis' + rnLeaf '.' + rnLeaf ' ' + """) + test "option list has priority over definition list": check(dedent""" --defusages @@ -376,9 +420,9 @@ suite "Warnings": let output = input.toAst(warnings=warnings) check(warnings[] == @[ "input(3, 14) Warning: broken link 'citation-som'", - "input(5, 7) Warning: broken link 'a-broken-link'", + "input(5, 7) Warning: broken link 'a broken Link'", "input(7, 15) Warning: unknown substitution 'undefined subst'", - "input(9, 6) Warning: broken link 'shortdotlink'" + "input(9, 6) Warning: broken link 'short.link'" ]) test "With include directive and blank lines at the beginning": @@ -391,7 +435,7 @@ suite "Warnings": let input = ".. include:: other.rst" var warnings = new seq[string] let output = input.toAst(warnings=warnings) - check warnings[] == @["other.rst(5, 6) Warning: broken link 'brokenlink'"] + check warnings[] == @["other.rst(5, 6) Warning: broken link 'brokenLink'"] check(output == dedent""" rnInner rnParagraph @@ -404,6 +448,59 @@ suite "Warnings": """) removeFile("other.rst") + test "warnings for ambiguous links (references + anchors)": + # Reference like `x`_ generates a link alias x that may clash with others + let input = dedent""" + Manual reference: `foo <#foo,string,string>`_ + + .. _foo: + + Paragraph. + + Ref foo_ + """ + var warnings = new seq[string] + let output = input.toAst(warnings=warnings) + check(warnings[] == @[ + dedent """ + input(7, 5) Warning: ambiguous doc link `foo` + clash: + (3, 8): (manual directive anchor) + (1, 45): (implicitly-generated hyperlink alias)""" + ]) + # reference should be resolved to the manually set anchor: + check(output == + dedent""" + rnInner + rnParagraph + rnLeaf 'Manual' + rnLeaf ' ' + rnLeaf 'reference' + rnLeaf ':' + rnLeaf ' ' + rnHyperlink + rnInner + rnLeaf 'foo' + rnInner + rnLeaf '#' + rnLeaf 'foo' + rnLeaf ',' + rnLeaf 'string' + rnLeaf ',' + rnLeaf 'string' + rnParagraph anchor='foo' + rnLeaf 'Paragraph' + rnLeaf '.' + rnParagraph + rnLeaf 'Ref' + rnLeaf ' ' + rnInternalRef + rnInner + rnLeaf 'foo' + rnLeaf 'foo' + rnLeaf ' ' + """) + suite "RST include directive": test "Include whole": "other.rst".writeFile("**test1**") diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim index 867677404..45cf1a68e 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -1054,8 +1054,9 @@ Test1 Paragraph2 ref `internal anchor`_. """ let output9 = input9.toHtml - #doAssert "id=\"internal-anchor\"" in output9 - #doAssert "internal anchor" notin output9 + # _`internal anchor` got erased: + check "href=\"#internal-anchor\"" notin output9 + check "href=\"#citation-another\"" in output9 doAssert output9.count("
        " & "
        ") == 1 doAssert output9.count("
        ") == 3 @@ -1330,12 +1331,12 @@ Test1 """ let output1 = input1.toHtml # "target101" should be erased and changed to "section-xyz": - doAssert "href=\"#target101\"" notin output1 - doAssert "id=\"target101\"" notin output1 - doAssert "href=\"#target102\"" notin output1 - doAssert "id=\"target102\"" notin output1 - doAssert "id=\"section-xyz\"" in output1 - doAssert "href=\"#section-xyz\"" in output1 + check "href=\"#target101\"" notin output1 + check "id=\"target101\"" notin output1 + check "href=\"#target102\"" notin output1 + check "id=\"target102\"" notin output1 + check "id=\"section-xyz\"" in output1 + check "href=\"#section-xyz\"" in output1 let input2 = dedent """ .. _target300: @@ -1405,7 +1406,7 @@ Test1 let output1 = input1.toHtml doAssert "id=\"secdot1\"" in output1 doAssert "id=\"Z2minusothercolonsecplusc-2\"" in output1 - doAssert "id=\"linkdot1-2021\"" in output1 + check "id=\"linkdot1-2021\"" in output1 let ref1 = "sec.1" let ref2 = "2-other:sec+c_2" let ref3 = "link.1_2021" From ee703c5db4ceb8d0228b4429c53d85d5f1ff91ea Mon Sep 17 00:00:00 2001 From: Tomasz Kramkowski Date: Fri, 29 Oct 2021 10:39:41 +0100 Subject: [PATCH 2614/3768] Fix nimIdentNormalize, fixes #19067 (#19068) * Make nimIdentNormalize return "" when passed ""; fixes #19067 Fixes #19067 * Add tests for nimIdentNormalize --- lib/pure/strutils.nim | 5 +++-- tests/stdlib/tstrutils.nim | 7 +++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 2234a8625..e4a4a5ec1 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -281,8 +281,9 @@ func nimIdentNormalize*(s: string): string = runnableExamples: doAssert nimIdentNormalize("Foo_bar") == "Foobar" result = newString(s.len) - if s.len > 0: - result[0] = s[0] + if s.len == 0: + return + result[0] = s[0] var j = 1 for i in 1..len(s) - 1: if s[i] in {'A'..'Z'}: diff --git a/tests/stdlib/tstrutils.nim b/tests/stdlib/tstrutils.nim index 234991bdb..8000b6b07 100644 --- a/tests/stdlib/tstrutils.nim +++ b/tests/stdlib/tstrutils.nim @@ -841,5 +841,12 @@ bar doAssert s.endsWith('a') == false doAssert s.endsWith('\0') == false + block: # nimIdentNormalize + doAssert nimIdentNormalize("") == "" + doAssert nimIdentNormalize("foo") == "foo" + doAssert nimIdentNormalize("foo_bar") == "foobar" + doAssert nimIdentNormalize("Foo_bar") == "Foobar" + doAssert nimIdentNormalize("_Foo_bar") == "_foobar" + static: main() main() From cfdac6666f5772479724e082fb08c2db694d8d40 Mon Sep 17 00:00:00 2001 From: Tail Wag Games Date: Fri, 29 Oct 2021 06:42:44 -0500 Subject: [PATCH 2615/3768] Freeing critical sections via atexit in system/alloc and system/io (#19062) * adding new system module sysexitprocs and including system exit procedures when registering exit handlers defined in userland * fixing failing tests and adding initialization guard to handle cases where the module's global init logic isn't invoked first as is the case with some gc implementaions * js backend shouldn't try to invoke actual system exit procs * fixing formatting in sysexitprocs.nim * 256 was too much - my max number of plugins in my engine is 64 and I require two hooks per runtime it looks like with tls emulation turned off, so for my purposes 128 should be sufficient * so atExit should be enough here, can get rid of all the extra cruft I had added on top since I didn't realize atExit already provided a stack * done being cute - since newruntime prevents correct cpp codegen for object variants apparently and breaks tests if I try to use std/exitprocs, ddSysExitProc is just going into both modules. Since system doesn't include system/io, polluting system with it doesn't make sense either... at least it is only importc'd when it is required in either module and we don't have to have any weird when defined(nimOwnedEnabled) with a comment explaining why --- lib/std/exitprocs.nim | 3 --- lib/system/alloc.nim | 3 +++ lib/system/io.nim | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/std/exitprocs.nim b/lib/std/exitprocs.nim index c76583a8c..c6537f7f8 100644 --- a/lib/std/exitprocs.nim +++ b/lib/std/exitprocs.nim @@ -44,9 +44,6 @@ proc callClosures() {.noconv.} = of kClosure: fun.fun1() of kNoconv: fun.fun2() - when not defined(js) and not defined(nimOwnedEnabled): - deinitLock(gFunsLock) - template fun() = if gFuns.len == 0: addAtExit(callClosures) diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim index 6d83a2c17..2c6ab4462 100644 --- a/lib/system/alloc.nim +++ b/lib/system/alloc.nim @@ -1054,9 +1054,12 @@ template instantiateForRegion(allocator: untyped) {.dirty.} = it = it.next when hasThreadSupport: + proc addSysExitProc(quitProc: proc() {.noconv.}) {.importc: "atexit", header: "".} + var sharedHeap: MemRegion var heapLock: SysLock initSysLock(heapLock) + addSysExitProc(proc() {.noconv.} = deinitSys(heapLock)) proc getFreeMem(): int = #sysAssert(result == countFreeMem()) diff --git a/lib/system/io.nim b/lib/system/io.nim index e7369392a..2ad43acdb 100644 --- a/lib/system/io.nim +++ b/lib/system/io.nim @@ -777,15 +777,15 @@ proc setStdIoUnbuffered*() {.tags: [], benign.} = when declared(stdout): when defined(windows) and compileOption("threads"): + proc addSysExitProc(quitProc: proc() {.noconv.}) {.importc: "atexit", header: "".} + const insideRLocksModule = false include "system/syslocks" + var echoLock: SysLock initSysLock echoLock - - when not defined(js) and not defined(nimOwnedEnabled): - import std/exitprocs - addExitProc(proc() {.noconv.} = deinitSys echoLock) + addSysExitProc(proc() {.noconv.} = deinitSys(echoLock)) const stdOutLock = not defined(windows) and not defined(android) and From f755e452d2a2c7656f07793b7f01dc654a7c5253 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Derek=20=E5=91=86?= <116649+derekdai@users.noreply.github.com> Date: Sat, 30 Oct 2021 01:55:48 +0800 Subject: [PATCH 2616/3768] fix #18971 (#19070) [backport:1.6] since the example code return value from global variable, instead of first argument, the `n.len` is 1 which causes compiler crashes. --- compiler/varpartitions.nim | 2 +- tests/arc/t18971.nim | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 tests/arc/t18971.nim diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim index 8f422face..709d00fa0 100644 --- a/compiler/varpartitions.nim +++ b/compiler/varpartitions.nim @@ -479,7 +479,7 @@ proc destMightOwn(c: var Partitions; dest: var VarIndex; n: PNode) = # calls do construct, what we construct must be destroyed, # so dest cannot be a cursor: dest.flags.incl ownsData - elif n.typ.kind in {tyLent, tyVar}: + elif n.typ.kind in {tyLent, tyVar} and n.len > 1: # we know the result is derived from the first argument: var roots: seq[(PSym, int)] allRoots(n[1], roots, RootEscapes) diff --git a/tests/arc/t18971.nim b/tests/arc/t18971.nim new file mode 100644 index 000000000..9b587d956 --- /dev/null +++ b/tests/arc/t18971.nim @@ -0,0 +1,10 @@ +discard """ + cmd: "nim c --gc:arc $file" +""" + +type MyObj = ref object + +var o = MyObj() +proc x: var MyObj = o + +var o2 = x() From 2bda4a30a6b12dd0840dc347e454e54fe26721e7 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 30 Oct 2021 10:14:23 +0200 Subject: [PATCH 2617/3768] fixes #19000 (#19032) * fixes #19000 * progress --- lib/system/memalloc.nim | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/system/memalloc.nim b/lib/system/memalloc.nim index f8ebc8c5f..49766e69d 100644 --- a/lib/system/memalloc.nim +++ b/lib/system/memalloc.nim @@ -116,6 +116,9 @@ when hasAlloc and not defined(js): ## ## See also: ## * `create <#create,typedesc>`_ + static: + when sizeof(T) <= 0: + {.fatal: "createU does not support types T where sizeof(T) == 0".} cast[ptr T](alloc(T.sizeof * size)) template alloc0*(size: Natural): pointer = @@ -141,6 +144,9 @@ when hasAlloc and not defined(js): ## ## The allocated memory belongs to its allocating thread! ## Use `createShared <#createShared,typedesc>`_ to allocate from a shared heap. + static: + when sizeof(T) <= 0: + {.fatal: "create does not support types T where sizeof(T) == 0".} cast[ptr T](alloc0(sizeof(T) * size)) template realloc*(p: pointer, newSize: Natural): pointer = From 2f730afe9e7eb981258aea257deba0240f488dd1 Mon Sep 17 00:00:00 2001 From: flywind Date: Sun, 31 Oct 2021 13:22:00 +0800 Subject: [PATCH 2618/3768] fix #18410 (Errors initializing an object of RootObj with the C++ backend) [backport] (#18836) * fix #18410 * one line comment * typo * typo * cover cpp --- compiler/cgen.nim | 3 ++- tests/arc/tconst_to_sink.nim | 3 ++- tests/cpp/torc.nim | 15 +++++++++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 tests/cpp/torc.nim diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 412a428da..b669483cc 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -363,7 +363,8 @@ proc genObjectInit(p: BProc, section: TCProcSection, t: PType, a: var TLoc, else: linefmt(p, section, "$1.m_type = $2;$n", [r, genTypeInfoV1(p.module, t, a.lode.info)]) of frEmbedded: - if optTinyRtti in p.config.globalOptions: + # inheritance in C++ does not allow struct initialization: bug #18410 + if not p.module.compileToCpp and optTinyRtti in p.config.globalOptions: var tmp: TLoc if mode == constructRefObj: let objType = t.skipTypes(abstractInst+{tyRef}) diff --git a/tests/arc/tconst_to_sink.nim b/tests/arc/tconst_to_sink.nim index ddcc46e67..25f659341 100644 --- a/tests/arc/tconst_to_sink.nim +++ b/tests/arc/tconst_to_sink.nim @@ -1,6 +1,7 @@ discard """ output: '''@[(s1: "333", s2: ""), (s1: "abc", s2: "def"), (s1: "3x", s2: ""), (s1: "3x", s2: ""), (s1: "3x", s2: ""), (s1: "3x", s2: ""), (s1: "lastone", s2: "")]''' - cmd: "nim c --gc:arc $file" + matrix: "--gc:arc" + targets: "c cpp" """ # bug #13240 diff --git a/tests/cpp/torc.nim b/tests/cpp/torc.nim new file mode 100644 index 000000000..7fc474f94 --- /dev/null +++ b/tests/cpp/torc.nim @@ -0,0 +1,15 @@ +discard """ + targets: "cpp" + matrix: "--gc:orc" +""" + +import std/options + +# bug #18410 +type + O = object of RootObj + val: pointer + +proc p(): Option[O] = none(O) + +doAssert $p() == "none(O)" \ No newline at end of file From 4c510d5577a0ced6a31b8417fed74acdd604b9a3 Mon Sep 17 00:00:00 2001 From: haxscramper Date: Tue, 2 Nov 2021 10:54:04 +0300 Subject: [PATCH 2619/3768] [FIX] Do not break formatted string line (#19085) [backport] Otherwise, compiler produces broken error message - `$1` is not interpolated `Error: The $1 type doesn't have a default value. The following fields must be initialized: importGraph.` --- compiler/semobjconstr.nim | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim index 001956ede..30ab76957 100644 --- a/compiler/semobjconstr.nim +++ b/compiler/semobjconstr.nim @@ -368,8 +368,7 @@ proc defaultConstructionError(c: PContext, t: PType, info: TLineInfo) = let initResult = semConstructTypeAux(c, constrCtx, {}) assert constrCtx.missingFields.len > 0 localError(c.config, info, - "The $1 type doesn't have a default value. The following fields must " & - "be initialized: $2." % [typeToString(t), listSymbolNames(constrCtx.missingFields)]) + "The $1 type doesn't have a default value. The following fields must be initialized: $2." % [typeToString(t), listSymbolNames(constrCtx.missingFields)]) elif objType.kind == tyDistinct: localError(c.config, info, "The $1 distinct type doesn't have a default value." % typeToString(t)) From 24a75842998709b29ef50ef4cc10d30304efe3aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Derek=20=E5=91=86?= <116649+derekdai@users.noreply.github.com> Date: Tue, 2 Nov 2021 15:55:50 +0800 Subject: [PATCH 2620/3768] update numbers of lifetime-tracking hooks in doc/destructors.rst (#19088) --- doc/destructors.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/destructors.rst b/doc/destructors.rst index 6e4509666..d6f240dee 100644 --- a/doc/destructors.rst +++ b/doc/destructors.rst @@ -101,7 +101,7 @@ The memory management for Nim's standard `string` and `seq` types as well as other standard collections is performed via so-called "Lifetime-tracking hooks", which are particular `type bound operators `_. -There are 3 different hooks for each (generic or concrete) object type `T` (`T` can also be a +There are 4 different hooks for each (generic or concrete) object type `T` (`T` can also be a `distinct` type) that are called implicitly by the compiler. (Note: The word "hook" here does not imply any kind of dynamic binding From cc984217a98438de5a36447ea6574eb2c5da4ae4 Mon Sep 17 00:00:00 2001 From: Jason Beetham Date: Tue, 2 Nov 2021 02:02:53 -0600 Subject: [PATCH 2621/3768] Fix VM's sametype impl to work for generics/typedescs (#19073) * Fix vm's sametype implementation to properly handle generics and typedescs * actually fixed sametype + have test * added comments and removed unsafe code --- compiler/types.nim | 5 ++++ compiler/vm.nim | 3 ++- tests/stdlib/tmacros.nim | 58 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 65 insertions(+), 1 deletion(-) diff --git a/compiler/types.nim b/compiler/types.nim index 2c7b91d9f..7cf02c240 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -1128,6 +1128,11 @@ proc sameTypeAux(x, y: PType, c: var TSameTypeClosure): bool = a = a.skipTypes({tyDistinct, tyGenericInst}) if a.kind != b.kind: return false + #[ + The following code should not run in the case either side is an generic alias, + but it's not presently possible to distinguish the genericinsts from aliases of + objects ie `type A[T] = SomeObject` + ]# # this is required by tunique_type but makes no sense really: if tyDistinct notin {x.kind, y.kind} and x.kind == tyGenericInst and IgnoreTupleFields notin c.flags: let diff --git a/compiler/vm.nim b/compiler/vm.nim index 1b443aff1..b64545081 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1044,7 +1044,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = strictSymEquality=true)) of opcSameNodeType: decodeBC(rkInt) - regs[ra].intVal = ord(regs[rb].node.typ.sameTypeOrNil regs[rc].node.typ) + regs[ra].intVal = ord(regs[rb].node.typ.sameTypeOrNil(regs[rc].node.typ, {ExactTypeDescValues, ExactGenericParams})) + # The types should exactly match which is why we pass `{ExactTypeDescValues..ExactGcSafety}`. of opcXor: decodeBC(rkInt) regs[ra].intVal = ord(regs[rb].intVal != regs[rc].intVal) diff --git a/tests/stdlib/tmacros.nim b/tests/stdlib/tmacros.nim index 18cfdc75c..64a474743 100644 --- a/tests/stdlib/tmacros.nim +++ b/tests/stdlib/tmacros.nim @@ -66,3 +66,61 @@ block: # unpackVarargs doAssert call1(toString) == "" doAssert call1(toString, 10) == "10" doAssert call1(toString, 10, 11) == "1011" + +block: # SameType + type + A = int + B = distinct int + C = object + Generic[T, Y] = object + macro isSameType(a, b: typed): untyped = + newLit(sameType(a, b)) + + static: + assert Generic[int, int].isSameType(Generic[int, int]) + assert Generic[A, string].isSameType(Generic[int, string]) + assert not Generic[A, string].isSameType(Generic[B, string]) + assert not Generic[int, string].isSameType(Generic[int, int]) + assert isSameType(int, A) + assert isSameType(10, 20) + assert isSameType("Hello", "world") + assert not isSameType("Hello", cstring"world") + assert not isSameType(int, B) + assert not isSameType(int, Generic[int, int]) + assert not isSameType(C, string) + assert not isSameType(C, int) + + + #[ + # compiler sameType fails for the following, read more in `types.nim`'s `sameTypeAux`. + type + D[T] = C + G[T] = T + static: + assert isSameType(D[int], C) + assert isSameType(D[int], D[float]) + assert isSameType(G[float](1.0), float(1.0)) + assert isSameType(float(1.0), G[float](1.0)) + ]# + + type Tensor[T] = object + data: T + + macro testTensorInt(x: typed): untyped = + let + tensorIntType = getTypeInst(Tensor[int])[1] + xTyp = x.getTypeInst + + newLit(xTyp.sameType(tensorIntType)) + + var + x: Tensor[int] + x1 = Tensor[float]() + x2 = Tensor[A]() + x3 = Tensor[B]() + + static: + assert testTensorInt(x) + assert not testTensorInt(x1) + assert testTensorInt(x2) + assert not testTensorInt(x3) From 2e0db988e7efd05b07f5165cf1357efea9391eb3 Mon Sep 17 00:00:00 2001 From: Kaushal Modi Date: Tue, 2 Nov 2021 04:06:46 -0400 Subject: [PATCH 2622/3768] tests: Fix warnings in tstrscans (#19082) --- tests/stdlib/tstrscans.nim | 60 +++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 26 deletions(-) diff --git a/tests/stdlib/tstrscans.nim b/tests/stdlib/tstrscans.nim index d443c5dda..24a3c02f7 100644 --- a/tests/stdlib/tstrscans.nim +++ b/tests/stdlib/tstrscans.nim @@ -15,7 +15,7 @@ block ParsePasswd: else: break - const etc_passwd = """root:x:0:0:root:/root:/bin/bash + const etcPasswd = """root:x:0:0:root:/root:/bin/bash daemon:x:1:1:daemon:/usr/sbin:/bin/sh bin:x:2:2:bin:/bin:/bin/sh sys:x:3:3:sys:/dev:/bin/sh @@ -23,7 +23,7 @@ nobody:x:65534:65534:nobody:/nonexistent:/bin/sh messagebus:x:103:107::/var/run/dbus:/bin/false """ - const parsed_etc_passwd = @[ + const parsedEtcPasswd = @[ "root:x:0:0:root:/root:/bin/bash", "daemon:x:1:1:daemon:/usr/sbin:/bin/sh", "bin:x:2:2:bin:/bin:/bin/sh", @@ -31,7 +31,7 @@ messagebus:x:103:107::/var/run/dbus:/bin/false "nobody:x:65534:65534:nobody:/nonexistent:/bin/sh", "messagebus:x:103:107::/var/run/dbus:/bin/false", ] - doAssert etc_passwd.parsePasswd == parsed_etc_passwd + doAssert etcPasswd.parsePasswd == parsedEtcPasswd block LastNot: var idx : int @@ -139,27 +139,27 @@ block: break var key, val: string - var intval: int - var floatval: float - doAssert scanf("abc:: xyz 89 33.25", "$w$s::$s$w$s$i $f", key, val, intval, floatVal) + var intVal: int + var floatVal: float + doAssert scanf("abc:: xyz 89 33.25", "$w$s::$s$w$s$i $f", key, val, intVal, floatVal) doAssert key == "abc" doAssert val == "xyz" - doAssert intval == 89 + doAssert intVal == 89 doAssert floatVal == 33.25 - var binval: int - var octval: int - var hexval: int - doAssert scanf("0b0101 0o1234 0xabcd", "$b$s$o$s$h", binval, octval, hexval) - doAssert binval == 0b0101 - doAssert octval == 0o1234 - doAssert hexval == 0xabcd + var binVal: int + var octVal: int + var hexVal: int + doAssert scanf("0b0101 0o1234 0xabcd", "$b$s$o$s$h", binVal, octVal, hexVal) + doAssert binVal == 0b0101 + doAssert octVal == 0o1234 + doAssert hexVal == 0xabcd - let xx = scanf("$abc", "$$$i", intval) + let xx = scanf("$abc", "$$$i", intVal) doAssert xx == false - let xx2 = scanf("$1234", "$$$i", intval) + let xx2 = scanf("$1234", "$$$i", intVal) doAssert xx2 let yy = scanf(";.--Breakpoint00 [output]", @@ -246,24 +246,32 @@ block: result = 0 while start+result < input.len and input[start+result] in seps: inc result + type + ScanRetType = tuple + success: bool + lo: int + hi: int + ch: char + word: string + var res = 0 for line in input.splitLines: - let (success, lo, hi, c ,w) = scanTuple(line, "$i-$i $c: $w") - if success: + let ret: ScanRetType = scanTuple(line, "$i-$i $c: $w") + if ret.success: inc res doAssert res == 4 - let (_, key, val, intval, floatVal) = scanTuple("abc:: xyz 89 33.25", "$w$s::$s$w$s$i $f") + let (_, key, val, intVal, floatVal) = scanTuple("abc:: xyz 89 33.25", "$w$s::$s$w$s$i $f") doAssert key == "abc" doAssert val == "xyz" - doAssert intval == 89 + doAssert intVal == 89 doAssert floatVal == 33.25 - let (_, binVal, octVal, hexVal) = scanTuple("0b0101 0o1234 0xabcd", "$b$s$o$s$h", binval, octval, hexval) - doAssert binval == 0b0101 - doAssert octval == 0o1234 - doAssert hexval == 0xabcd + let (_, binVal, octVal, hexVal) = scanTuple("0b0101 0o1234 0xabcd", "$b$s$o$s$h", binVal, octVal, hexVal) + doAssert binVal == 0b0101 + doAssert octVal == 0o1234 + doAssert hexVal == 0xabcd var (xx,_) = scanTuple("$abc", "$$$i") doAssert xx == false @@ -272,9 +280,9 @@ block: let (xx2, _) = block: scanTuple("$1234", "$$$i") doAssert xx2 - var (yy, intval2, key2) = scanTuple(";.--Breakpoint00 [output]", + var (yy, intVal2, key2) = scanTuple(";.--Breakpoint00 [output]", "$[someSep]Breakpoint${twoDigits}$[someSep({';','.','-'})] [$+]$.", int) doAssert yy doAssert key2 == "output" - doAssert intVal2 == 13 \ No newline at end of file + doAssert intVal2 == 13 From e01e543fce8eab147d4e7eb5b89bba80c25e9cb3 Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 2 Nov 2021 16:10:25 +0800 Subject: [PATCH 2623/3768] bootstrapping Nim compiler with `cpp --gc:orc` (#19087) --- koch.nim | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/koch.nim b/koch.nim index 8dd897028..06515aad7 100644 --- a/koch.nim +++ b/koch.nim @@ -602,9 +602,8 @@ proc runCI(cmd: string) = execFold("Run atlas tests", "nim c -r -d:atlasTests tools/atlas/atlas.nim clone https://github.com/disruptek/balls") when not defined(bsd): - if not doUseCpp: - # the BSDs are overwhelmed already, so only run this test on the other machines: - kochExecFold("Boot Nim ORC", "boot -d:release --gc:orc --lib:lib") + # the BSDs are overwhelmed already, so only run this test on the other machines: + kochExecFold("Boot Nim ORC", "boot -d:release --gc:orc --lib:lib") proc testUnixInstall(cmdLineRest: string) = csource("-d:danger" & cmdLineRest) From 12d2901e89d394cb1eb1242acc0107a2d20e3b3d Mon Sep 17 00:00:00 2001 From: Kaushal Modi Date: Tue, 2 Nov 2021 04:12:54 -0400 Subject: [PATCH 2624/3768] libs/impore/re: Add note about the requirement of `matches` to be pre-allocated (#19081) Add few runnableExamples for `findBounds` for clarity. Fixes https://github.com/nim-lang/Nim/issues/18775 --- lib/impure/re.nim | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/lib/impure/re.nim b/lib/impure/re.nim index d6922f4f1..8e5efbb28 100644 --- a/lib/impure/re.nim +++ b/lib/impure/re.nim @@ -152,6 +152,9 @@ proc findBounds*(buf: cstring, pattern: Regex, matches: var openArray[string], ## and the captured ## substrings in the array `matches`. If it does not match, nothing ## is written into `matches` and `(-1,0)` is returned. + ## + ## Note: The memory for `matches` needs to be allocated before this function is + ## called, otherwise it will just remain empty. var rtarray = initRtArray[cint]((matches.len+1)*3) rawMatches = rtarray.getRawData @@ -171,6 +174,14 @@ proc findBounds*(s: string, pattern: Regex, matches: var openArray[string], ## and the captured substrings in the array `matches`. ## If it does not match, nothing ## is written into `matches` and `(-1,0)` is returned. + ## + ## .. note:: The memory for `matches` needs to be allocated before this function is called, otherwise it will just remain empty. + runnableExamples: + var matches = newSeq[string](1) + let (first, last) = findBounds("Hello World", re"(W\w+)", matches) + doAssert first == 6 + doAssert last == 10 + doAssert matches[0] == "World" result = findBounds(cstring(s), pattern, matches, min(start, MaxReBufSize), min(s.len, MaxReBufSize)) @@ -182,6 +193,8 @@ proc findBounds*(buf: cstring, pattern: Regex, ## and the captured substrings in the array `matches`. ## If it does not match, nothing is written into `matches` and ## `(-1,0)` is returned. + ## + ## .. note:: The memory for `matches` needs to be allocated before this function is called, otherwise it will just remain empty. var rtarray = initRtArray[cint]((matches.len+1)*3) rawMatches = rtarray.getRawData @@ -202,6 +215,14 @@ proc findBounds*(s: string, pattern: Regex, ## and the captured substrings in the array `matches`. ## If it does not match, nothing is written into `matches` and ## `(-1,0)` is returned. + ## + ## .. note:: The memory for `matches` needs to be allocated before this function is called, otherwise it will just remain empty. + runnableExamples: + var matches = newSeq[tuple[first, last: int]](1) + let (first, last) = findBounds("Hello World", re"(\w+)", matches) + doAssert first == 0 + doAssert last == 4 + doAssert matches[0] == (0, 4) result = findBounds(cstring(s), pattern, matches, min(start, MaxReBufSize), min(s.len, MaxReBufSize)) @@ -255,6 +276,8 @@ proc matchLen*(s: string, pattern: Regex, matches: var openArray[string], ## the same as `match`, but it returns the length of the match, ## if there is no match, `-1` is returned. Note that a match length ## of zero can happen. + ## + ## .. note:: The memory for `matches` needs to be allocated before this function is called, otherwise it will just remain empty. result = matchOrFind(cstring(s), pattern, matches, start.cint, s.len.cint, pcre.ANCHORED) proc matchLen*(buf: cstring, pattern: Regex, matches: var openArray[string], @@ -262,6 +285,8 @@ proc matchLen*(buf: cstring, pattern: Regex, matches: var openArray[string], ## the same as `match`, but it returns the length of the match, ## if there is no match, `-1` is returned. Note that a match length ## of zero can happen. + ## + ## .. note:: The memory for `matches` needs to be allocated before this function is called, otherwise it will just remain empty. return matchOrFind(buf, pattern, matches, start.cint, bufSize.cint, pcre.ANCHORED) proc matchLen*(s: string, pattern: Regex, start = 0): int {.inline.} = @@ -292,6 +317,7 @@ proc match*(s: string, pattern: Regex, matches: var openArray[string], ## match, nothing is written into `matches` and `false` is ## returned. ## + ## .. note:: The memory for `matches` needs to be allocated before this function is called, otherwise it will just remain empty. runnableExamples: import std/sequtils var matches: array[2, string] @@ -306,6 +332,8 @@ proc match*(buf: cstring, pattern: Regex, matches: var openArray[string], ## match, nothing is written into `matches` and `false` is ## returned. ## `buf` has length `bufSize` (not necessarily `'\0'` terminated). + ## + ## .. note:: The memory for `matches` needs to be allocated before this function is called, otherwise it will just remain empty. result = matchLen(buf, pattern, matches, start, bufSize) != -1 proc find*(buf: cstring, pattern: Regex, matches: var openArray[string], @@ -314,6 +342,8 @@ proc find*(buf: cstring, pattern: Regex, matches: var openArray[string], ## substrings in the array `matches`. If it does not match, nothing ## is written into `matches` and `-1` is returned. ## `buf` has length `bufSize` (not necessarily `'\0'` terminated). + ## + ## .. note:: The memory for `matches` needs to be allocated before this function is called, otherwise it will just remain empty. var rtarray = initRtArray[cint]((matches.len+1)*3) rawMatches = rtarray.getRawData @@ -332,6 +362,8 @@ proc find*(s: string, pattern: Regex, matches: var openArray[string], ## returns the starting position of `pattern` in `s` and the captured ## substrings in the array `matches`. If it does not match, nothing ## is written into `matches` and `-1` is returned. + ## + ## .. note:: The memory for `matches` needs to be allocated before this function is called, otherwise it will just remain empty. result = find(cstring(s), pattern, matches, start, s.len) proc find*(buf: cstring, pattern: Regex, start = 0, bufSize: int): int = @@ -433,6 +465,8 @@ proc contains*(s: string, pattern: Regex, start = 0): bool {.inline.} = proc contains*(s: string, pattern: Regex, matches: var openArray[string], start = 0): bool {.inline.} = ## same as `find(s, pattern, matches, start) >= 0` + ## + ## .. note:: The memory for `matches` needs to be allocated before this function is called, otherwise it will just remain empty. return find(s, pattern, matches, start) >= 0 proc startsWith*(s: string, prefix: Regex): bool {.inline.} = From efaaa38eefa38d842ef9225b6ac3f60c4c842209 Mon Sep 17 00:00:00 2001 From: Kaushal Modi Date: Tue, 2 Nov 2021 05:34:11 -0400 Subject: [PATCH 2625/3768] docstyle.rst: Do not export it to HTML as a standalone doc (#19083) .. This doc is included in contributing.rst. Fixes https://github.com/nim-lang/Nim/issues/14593. --- tools/kochdocs.nim | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tools/kochdocs.nim b/tools/kochdocs.nim index 0c5c8f4a3..42e648fb4 100644 --- a/tools/kochdocs.nim +++ b/tools/kochdocs.nim @@ -111,7 +111,10 @@ proc getRst2html(): seq[string] = for a in walkDirRecFilter("doc"): let path = a.path if a.kind == pcFile and path.splitFile.ext == ".rst" and path.lastPathPart notin - ["docs.rst", "nimfix.rst"]: + ["docs.rst", "nimfix.rst", + "docstyle.rst" # docstyle.rst shouldn't be converted to html separately; + # it's included in contributing.rst. + ]: # maybe we should still show nimfix, could help reviving it # `docs` is redundant with `overview`, might as well remove that file? result.add path From a55b90827ed87b61972147e8120c9afa949d17f9 Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 2 Nov 2021 23:22:11 +0800 Subject: [PATCH 2626/3768] remove the mention of travis from readme (#19093) --- readme.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/readme.md b/readme.md index 7f333d5d4..af07c2a7b 100644 --- a/readme.md +++ b/readme.md @@ -149,7 +149,7 @@ read [this guide][pull-request-instructions]. Ideally, you should make sure that all tests pass before submitting a pull request. However, if you are short on time, you can just run the tests specific to your -changes by only running the corresponding categories of tests. Travis CI verifies +changes by only running the corresponding categories of tests. CI verifies that all tests pass before allowing the pull request to be accepted, so only running specific tests should be harmless. Integration tests should go in ``tests/untestable``. @@ -220,7 +220,6 @@ Copyright © 2006-2021 Andreas Rumpf, all rights reserved. [nimsuggest-repo]: https://github.com/nim-lang/nimsuggest [csources-repo-deprecated]: https://github.com/nim-lang/csources [csources-v1-repo]: https://github.com/nim-lang/csources_v1 -[badge-nim-travisci]: https://img.shields.io/travis/nim-lang/Nim/devel.svg?style=flat-square [badge-nim-irc]: https://img.shields.io/badge/chat-on_irc-blue.svg?style=flat-square [badge-nim-discord]: https://img.shields.io/discord/371759389889003530?color=blue&label=discord&logo=discord&logoColor=gold&style=flat-square [badge-nim-gitter]: https://img.shields.io/badge/chat-on_gitter-blue.svg?style=flat-square From 9d51197aa4eb404c3cce8c5989146dea8d3de024 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 2 Nov 2021 16:30:59 +0100 Subject: [PATCH 2627/3768] fixes #19078 [backport] (#19090) --- compiler/condsyms.nim | 2 ++ compiler/pragmas.nim | 4 +++- compiler/wordrecg.nim | 2 +- lib/std/private/digitsutils.nim | 9 ++++++--- 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index df355bc26..5a7e78d5f 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -138,3 +138,5 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasHintAll") defineSymbol("nimHasTrace") defineSymbol("nimHasEffectsOf") + + defineSymbol("nimHasEnforceNoRaises") diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index a6e6131d7..702a9e082 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -31,7 +31,7 @@ const wAsmNoStackFrame, wDiscardable, wNoInit, wCodegenDecl, wGensym, wInject, wRaises, wEffectsOf, wTags, wLocks, wDelegator, wGcSafe, wConstructor, wLiftLocals, wStackTrace, wLineTrace, wNoDestroy, - wRequires, wEnsures} + wRequires, wEnsures, wEnforceNoRaises} converterPragmas* = procPragmas methodPragmas* = procPragmas+{wBase}-{wImportCpp} templatePragmas* = {wDeprecated, wError, wGensym, wInject, wDirty, @@ -1237,6 +1237,8 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, pragmaProposition(c, it) of wEnsures: pragmaEnsures(c, it) + of wEnforceNoRaises: + sym.flags.incl sfNeverRaises else: invalidPragma(c, it) elif comesFromPush and whichKeyword(ident) != wInvalid: discard "ignore the .push pragma; it doesn't apply" diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index ce06effab..22f6cc71d 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -86,7 +86,7 @@ type wAsmNoStackFrame = "asmNoStackFrame", wImplicitStatic = "implicitStatic", wGlobal = "global", wCodegenDecl = "codegenDecl", wUnchecked = "unchecked", wGuard = "guard", wLocks = "locks", wPartial = "partial", wExplain = "explain", - wLiftLocals = "liftlocals", + wLiftLocals = "liftlocals", wEnforceNoRaises = "enforceNoRaises", wAuto = "auto", wBool = "bool", wCatch = "catch", wChar = "char", wClass = "class", wCompl = "compl", wConst_cast = "const_cast", wDefault = "default", diff --git a/lib/std/private/digitsutils.nim b/lib/std/private/digitsutils.nim index 9be2ab3ef..588bcaec0 100644 --- a/lib/std/private/digitsutils.nim +++ b/lib/std/private/digitsutils.nim @@ -78,14 +78,17 @@ func addIntImpl(result: var string, x: uint64) {.inline.} = dec next addChars(result, tmp, next, tmp.len - next) -func addInt*(result: var string, x: uint64) = +when not defined(nimHasEnforceNoRaises): + {.pragma: enforceNoRaises.} + +func addInt*(result: var string, x: uint64) {.enforceNoRaises.} = when nimvm: addIntImpl(result, x) else: when not defined(js): addIntImpl(result, x) else: addChars(result, numToString(x)) -proc addInt*(result: var string; x: int64) = +proc addInt*(result: var string; x: int64) {.enforceNoRaises.} = ## Converts integer to its string representation and appends it to `result`. runnableExamples: var s = "foo" @@ -110,5 +113,5 @@ proc addInt*(result: var string; x: int64) = addChars(result, numToString(x)) else: impl() -proc addInt*(result: var string; x: int) {.inline.} = +proc addInt*(result: var string; x: int) {.inline, enforceNoRaises.} = addInt(result, int64(x)) From b155864967ba08861c39403003095d4822b03b4e Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 2 Nov 2021 23:31:30 +0800 Subject: [PATCH 2628/3768] build testament in package CI (#19092) * build testament in package CI * Update testament/important_packages.nim --- koch.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/koch.nim b/koch.nim index 06515aad7..f87828cdc 100644 --- a/koch.nim +++ b/koch.nim @@ -559,7 +559,8 @@ proc runCI(cmd: string) = let batchParam = "--batch:$1" % "NIM_TESTAMENT_BATCH".getEnv("_") if getEnv("NIM_TEST_PACKAGES", "0") == "1": - execFold("Test selected Nimble packages", "nim r testament/testament $# pcat nimble-packages" % batchParam) + nimCompileFold("Compile testament", "testament/testament.nim", options = "-d:release") + execFold("Test selected Nimble packages", "testament $# pcat nimble-packages" % batchParam) else: buildTools() From 539bced70df035ddbdbf4c3d9c7fd6074f0b1073 Mon Sep 17 00:00:00 2001 From: Kaushal Modi Date: Wed, 3 Nov 2021 01:46:34 -0400 Subject: [PATCH 2629/3768] Add test for issue 15435 (#19079) * Add test for issue 15435 Closes https://github.com/nim-lang/Nim/issues/15435. * Specify bug # in comment Addresses https://github.com/nim-lang/Nim/pull/19079#discussion_r740279547 --- tests/sets/t15435.nim | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 tests/sets/t15435.nim diff --git a/tests/sets/t15435.nim b/tests/sets/t15435.nim new file mode 100644 index 000000000..5ead7e641 --- /dev/null +++ b/tests/sets/t15435.nim @@ -0,0 +1,29 @@ +# bug #15435 +discard """ +errormsg: "type mismatch: got " +nimout: '''t15435.nim(28, 13) Error: type mismatch: got +but expected one of: +proc `<`[T](x, y: set[T]): bool + first type mismatch at position: 2 + required type for y: set[T] + but expression 'x' is of type: set[range 1..5(uint8)] +20 other mismatching symbols have been suppressed; compile with --showAllMismatches:on to see them + +expression: {1'u8, 5} < x''' +""" + + + + + + +## line 20 +var + x: set[range[1u8..5u8]] + +x.incl(1) +x.incl(3) +x.incl(5) + +if {1u8, 5} < x: + echo "successful" From 5fed1c05ce7ea9cbeb2b70bb8583e836c981abd4 Mon Sep 17 00:00:00 2001 From: Kaushal Modi Date: Wed, 3 Nov 2021 01:48:30 -0400 Subject: [PATCH 2630/3768] manual: Document the use of `static` as a proc call (#19084) * manual: Document the use of `static` as a proc call Also adds tests. Fixes https://github.com/nim-lang/Nim/issues/16987 . * Update doc/manual.rst Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> * Use the "bug #NNNN" comment syntax for consistency Ref: https://nim-lang.github.io/Nim/contributing.html#writing-tests-stdlib > Always refer to a GitHub issue using the following exact syntax: bug for tooling. * manual: Undocument usage of foo.static foo.static and foo.static() are not expected to work. Ref: https://github.com/nim-lang/Nim/pull/19084/files#r741203578 Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> --- doc/manual.rst | 14 ++++++++++++++ tests/system/tstatic_callable.nim | 12 ++++++++++++ tests/system/tstatic_callable_error.nim | 14 ++++++++++++++ 3 files changed, 40 insertions(+) create mode 100644 tests/system/tstatic_callable.nim create mode 100644 tests/system/tstatic_callable_error.nim diff --git a/doc/manual.rst b/doc/manual.rst index 4b212fe19..1371dba17 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -2975,6 +2975,20 @@ Even some code that has side effects is permitted in a static block: static: echo "echo at compile time" +`static` can also be used like a routine. + +.. code-block:: nim + + proc getNum(a: int): int = a + + # Below calls "echo getNum(123)" at compile time. + static: + echo getNum(123) + + # Below call evaluates the "getNum(123)" at compile time, but its + # result gets used at run time. + echo static(getNum(123)) + There are limitations on what Nim code can be executed at compile time; see `Restrictions on Compile-Time Execution <#restrictions-on-compileminustime-execution>`_ for details. diff --git a/tests/system/tstatic_callable.nim b/tests/system/tstatic_callable.nim new file mode 100644 index 000000000..92d1fca8d --- /dev/null +++ b/tests/system/tstatic_callable.nim @@ -0,0 +1,12 @@ +# bug #16987 + +proc getNum(a: int): int = a + +# Below calls "doAssert getNum(123) == 123" at compile time. +static: + doAssert getNum(123) == 123 + +# Below calls evaluate the "getNum(123)" at compile time, but the +# results of those calls get used at run time. +doAssert (static getNum(123)) == 123 +doAssert (static(getNum(123))) == 123 diff --git a/tests/system/tstatic_callable_error.nim b/tests/system/tstatic_callable_error.nim new file mode 100644 index 000000000..c6f1e3d07 --- /dev/null +++ b/tests/system/tstatic_callable_error.nim @@ -0,0 +1,14 @@ +# bug #16987 + +discard """ +errormsg: "cannot evaluate at compile time: inp" +nimout: ''' +tstatic_callable_error.nim(14, 21) Error: cannot evaluate at compile time: inp''' +""" + + +# line 10 +proc getNum(a: int): int = a + +let inp = 123 +echo (static getNum(inp)) From dbbafd320ce1edb0e51167c8e9f6ba3d865672c3 Mon Sep 17 00:00:00 2001 From: Kaushal Modi Date: Wed, 3 Nov 2021 03:08:50 -0400 Subject: [PATCH 2631/3768] manual: Document that comma propagates the default values of parameters (#19080) * manual: Document that comma propagates the default values of parameters Fixes https://github.com/nim-lang/Nim/issues/15949. * Use the "bug #NNNN" comment syntax for consistency Ref: https://nim-lang.github.io/Nim/contributing.html#writing-tests-stdlib > Always refer to a GitHub issue using the following exact syntax: bug for tooling. --- doc/manual.rst | 10 +++++++++- tests/proc/t15949.nim | 20 ++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 tests/proc/t15949.nim diff --git a/doc/manual.rst b/doc/manual.rst index 1371dba17..001402036 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -3603,9 +3603,17 @@ does not provide a value for the argument. The value will be reevaluated every time the function is called. .. code-block:: nim - # b is optional with 47 as its default value + # b is optional with 47 as its default value. proc foo(a: int, b: int = 47): int +Just as the comma propagates the types from right to left until the +first parameter or until a semicolon is hit, it also propagates the +default value starting from the parameter declared with it. + +.. code-block:: nim + # Both a and b are optional with 47 as their default values. + proc foo(a, b: int = 47): int + Parameters can be declared mutable and so allow the proc to modify those arguments, by using the type modifier `var`. diff --git a/tests/proc/t15949.nim b/tests/proc/t15949.nim new file mode 100644 index 000000000..bc3fddc84 --- /dev/null +++ b/tests/proc/t15949.nim @@ -0,0 +1,20 @@ +# bug #15949 + +discard """ +errormsg: "parameter 'a' requires a type" +nimout: ''' +t15949.nim(20, 14) Error: parameter 'a' requires a type''' +""" + + +# line 10 +proc procGood(a, b = 1): (int, int) = (a, b) + +doAssert procGood() == (1, 1) +doAssert procGood(b = 3) == (1, 3) +doAssert procGood(a = 2) == (2, 1) +doAssert procGood(a = 5, b = 6) == (5, 6) + +# The type (and default value propagation breaks in the below example +# as semicolon is used instead of comma. +proc procBad(a; b = 1): (int, int) = (a, b) From f7cfc46ae17b81d9ccdc84573da00561f24ea99f Mon Sep 17 00:00:00 2001 From: David Krause Date: Wed, 3 Nov 2021 09:14:59 +0100 Subject: [PATCH 2632/3768] Added support for sending "ehlo" and receiving multiline "ehlo" response. (#19077) * Added support for sending "ehlo" and receiving multiline "ehlo" response. Signed-off-by: David Krause * fix typo Signed-off-by: David Krause * send "EHLO" first, if not supported, send "HELO" to smtp server. Signed-off-by: David Krause * fix english Signed-off-by: David Krause * add changelog entry for smtp `ehlo` Signed-off-by: David Krause * recvEhlo must not be exported Signed-off-by: David Krause --- changelog.md | 2 ++ lib/pure/smtp.nim | 33 ++++++++++++++++++++++++--------- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/changelog.md b/changelog.md index fbda70582..1636d125f 100644 --- a/changelog.md +++ b/changelog.md @@ -7,7 +7,9 @@ ## Standard library additions and changes +## `std/smtp` +- Sends `ehlo` first. If the mail server does not understand, it sends `helo` as a fallback. ## Language changes diff --git a/lib/pure/smtp.nim b/lib/pure/smtp.nim index 5ba048608..f5196ce1e 100644 --- a/lib/pure/smtp.nim +++ b/lib/pure/smtp.nim @@ -100,7 +100,6 @@ proc debugRecv*(smtp: Smtp | AsyncSmtp): Future[string] {.multisync.} = ## `SMTP extensions`_. ## ## See `checkReply(reply)<#checkReply,AsyncSmtp,string>`_. - result = await smtp.sock.recvLine() if smtp.debug: echo("S:" & result) @@ -171,8 +170,7 @@ proc `$`*(msg: Message): string = result.add("\c\L") result.add(msg.msgBody) -proc newSmtp*(useSsl = false, debug = false, - sslContext: SslContext = nil): Smtp = +proc newSmtp*(useSsl = false, debug = false, sslContext: SslContext = nil): Smtp = ## Creates a new `Smtp` instance. new result result.debug = debug @@ -186,12 +184,10 @@ proc newSmtp*(useSsl = false, debug = false, else: {.error: "SMTP module compiled without SSL support".} -proc newAsyncSmtp*(useSsl = false, debug = false, - sslContext: SslContext = nil): AsyncSmtp = +proc newAsyncSmtp*(useSsl = false, debug = false, sslContext: SslContext = nil): AsyncSmtp = ## Creates a new `AsyncSmtp` instance. new result result.debug = debug - result.sock = newAsyncSocket() if useSsl: when compiledWithSsl: @@ -220,7 +216,6 @@ proc checkReply*(smtp: Smtp | AsyncSmtp, reply: string) {.multisync.} = ## would need to call when using this module. One exception to ## this is if you are implementing any ## `SMTP extensions`_. - var line = await smtp.debugRecv() if not line.startsWith(reply): await quitExcpt(smtp, "Expected " & reply & " reply, got: " & line) @@ -230,6 +225,22 @@ proc helo*(smtp: Smtp | AsyncSmtp) {.multisync.} = await smtp.debugSend("HELO " & smtp.address & "\c\L") await smtp.checkReply("250") +proc recvEhlo(smtp: Smtp | AsyncSmtp): Future[bool] {.multisync.} = + ## Skips "250-" lines, read until "250 " found. + ## Return `true` if server supports `EHLO`, false otherwise. + while true: + var line = await smtp.sock.recvLine() + if smtp.debug: + echo("S:" & line) + if line.startsWith("250-"): continue + elif line.startsWith("250 "): return true # last line + else: return false + +proc ehlo*(smtp: Smtp | AsyncSmtp): Future[bool] {.multisync.} = + ## Sends EHLO request. + await smtp.debugSend("EHLO " & smtp.address & "\c\L") + return await smtp.recvEhlo() + proc connect*(smtp: Smtp | AsyncSmtp, address: string, port: Port) {.multisync.} = ## Establishes a connection with a SMTP server. @@ -237,7 +248,9 @@ proc connect*(smtp: Smtp | AsyncSmtp, smtp.address = address await smtp.sock.connect(address, port) await smtp.checkReply("220") - await smtp.helo() + let speaksEsmtp = await smtp.ehlo() + if not speaksEsmtp: + await smtp.helo() proc startTls*(smtp: Smtp | AsyncSmtp, sslContext: SslContext = nil) {.multisync.} = ## Put the SMTP connection in TLS (Transport Layer Security) mode. @@ -249,7 +262,9 @@ proc startTls*(smtp: Smtp | AsyncSmtp, sslContext: SslContext = nil) {.multisync getSSLContext().wrapConnectedSocket(smtp.sock, handshakeAsClient) else: sslContext.wrapConnectedSocket(smtp.sock, handshakeAsClient) - await smtp.helo() + let speaksEsmtp = await smtp.ehlo() + if not speaksEsmtp: + await smtp.helo() else: {.error: "SMTP module compiled without SSL support".} From b2edc3468cf0857b337bff09cb68649d49803ccf Mon Sep 17 00:00:00 2001 From: Constantine Molchanov Date: Wed, 3 Nov 2021 11:16:24 +0300 Subject: [PATCH 2633/3768] Use testament to check Norm test pass (#19018) * Use testament to check Norm test pass This is what I actually use to test Norm, so it's better to use it. This should not currently pass. This is expected because this is exactly the problem I want to highlight with this PR. My tests do indeed not pass at the moment. * Remove clearNimblePath from testament command. Co-authored-by: flywind --- testament/important_packages.nim | 2 +- testament/specs.nim | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index e037cc609..fc5c808ab 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -115,7 +115,7 @@ pkg "nimterop", "nimble minitest" pkg "nimwc", "nim c nimwc.nim" pkg "nimx", "nim c --threads:on test/main.nim", allowFailure = true pkg "nitter", "nim c src/nitter.nim", "https://github.com/zedeus/nitter" -pkg "norm", "nim c -r tests/sqlite/trows.nim" +pkg "norm", "testament r tests/sqlite/trows.nim" pkg "npeg", "nimble testarc" pkg "numericalnim", "nim c -r tests/test_integrate.nim" pkg "optionsutils" diff --git a/testament/specs.nim b/testament/specs.nim index 6fd0ab22d..c7e332262 100644 --- a/testament/specs.nim +++ b/testament/specs.nim @@ -103,7 +103,7 @@ type proc getCmd*(s: TSpec): string = if s.cmd.len == 0: - result = compilerPrefix & " $target --hints:on -d:testing --clearNimblePath --nimblePath:build/deps/pkgs $options $file" + result = compilerPrefix & " $target --hints:on -d:testing --nimblePath:build/deps/pkgs $options $file" else: result = s.cmd From f2f15e972645496f901fcfde197048de368453d6 Mon Sep 17 00:00:00 2001 From: Don-Duong Quach Date: Wed, 3 Nov 2021 08:47:31 -0700 Subject: [PATCH 2634/3768] fix for #19020, credit to @ElegantBeef (#19021) --- lib/core/macros.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 806ea7e31..6dbb92875 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -1611,7 +1611,7 @@ macro getCustomPragmaVal*(n: typed, cp: typed{nkSym}): untyped = let pragmaNode = customPragmaNode(n) for p in pragmaNode: if p.kind in nnkPragmaCallKinds and p.len > 0 and p[0].kind == nnkSym and p[0] == cp: - if p.len == 2: + if p.len == 2 or (p.len == 3 and p[1].kind == nnkSym and p[1].symKind == nskType): result = p[1] else: let def = p[0].getImpl[3] From fce89cb60a3428c61da3c7e6c2d3e6cdcb14bb3b Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 7 Nov 2021 16:38:02 +0100 Subject: [PATCH 2635/3768] fixes another effect inference bug [backport:1.6] (#19100) * fixes another effect inference bug [backport:1.6] --- compiler/sempass2.nim | 3 ++ lib/pure/asyncdispatch.nim | 4 +- lib/pure/json.nim | 10 +++-- lib/system/excpt.nim | 13 ++++--- lib/system/seqs_v2.nim | 2 +- tests/effects/tnestedprocs.nim | 63 +++++++++++++++++++++++++++++++ tests/pragmas/tcompile_pragma.nim | 1 + 7 files changed, 85 insertions(+), 11 deletions(-) create mode 100644 tests/effects/tnestedprocs.nim diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 9a27d14fa..5624d7adb 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -851,6 +851,9 @@ proc trackCall(tracked: PEffects; n: PNode) = elif isIndirectCall(tracked, a): assumeTheWorst(tracked, n, op) gcsafeAndSideeffectCheck() + else: + if strictEffects in tracked.c.features and a.kind == nkSym and a.sym.kind in routineKinds: + propagateEffects(tracked, n, a.sym) else: mergeRaises(tracked, effectList[exceptionEffects], n) mergeTags(tracked, effectList[tagEffects], n) diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index e15fb0851..c924d0a3d 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -733,7 +733,7 @@ when defined(windows) or defined(nimdoc): proc acceptAddr*(socket: AsyncFD, flags = {SocketFlag.SafeDisconn}, inheritable = defined(nimInheritHandles)): - owned(Future[tuple[address: string, client: AsyncFD]]) = + owned(Future[tuple[address: string, client: AsyncFD]]) {.gcsafe.} = ## Accepts a new connection. Returns a future containing the client socket ## corresponding to that connection and the remote address of the client. ## The future will complete when the connection is successfully accepted. @@ -800,7 +800,7 @@ when defined(windows) or defined(nimdoc): var ol = newCustom() ol.data = CompletionData(fd: socket, cb: - proc (fd: AsyncFD, bytesCount: DWORD, errcode: OSErrorCode) = + proc (fd: AsyncFD, bytesCount: DWORD, errcode: OSErrorCode) {.gcsafe.} = if not retFuture.finished: if errcode == OSErrorCode(-1): completeAccept() diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 85c3393b2..c831bf85d 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -437,7 +437,7 @@ macro `%*`*(x: untyped): untyped = ## `%` for every element. result = toJsonImpl(x) -proc `==`*(a, b: JsonNode): bool = +proc `==`*(a, b: JsonNode): bool {.noSideEffect.} = ## Check two nodes for equality if a.isNil: if b.isNil: return true @@ -464,12 +464,16 @@ proc `==`*(a, b: JsonNode): bool = if a.fields.len != b.fields.len: return false for key, val in a.fields: if not b.fields.hasKey(key): return false - if b.fields[key] != val: return false + when defined(nimHasEffectsOf): + {.noSideEffect.}: + if b.fields[key] != val: return false + else: + if b.fields[key] != val: return false result = true proc hash*(n: OrderedTable[string, JsonNode]): Hash {.noSideEffect.} -proc hash*(n: JsonNode): Hash = +proc hash*(n: JsonNode): Hash {.noSideEffect.} = ## Compute the hash for a JSON node case n.kind of JArray: diff --git a/lib/system/excpt.nim b/lib/system/excpt.nim index 3e520570f..a71328c14 100644 --- a/lib/system/excpt.nim +++ b/lib/system/excpt.nim @@ -354,7 +354,7 @@ var onUnhandledException*: (proc (errorMsg: string) {. ## The default is to write a stacktrace to `stderr` and then call `quit(1)`. ## Unstable API. -proc reportUnhandledErrorAux(e: ref Exception) {.nodestroy.} = +proc reportUnhandledErrorAux(e: ref Exception) {.nodestroy, gcsafe.} = when hasSomeStackTrace: var buf = newStringOfCap(2000) if e.trace.len == 0: @@ -362,7 +362,8 @@ proc reportUnhandledErrorAux(e: ref Exception) {.nodestroy.} = else: var trace = $e.trace add(buf, trace) - `=destroy`(trace) + {.gcsafe.}: + `=destroy`(trace) add(buf, "Error: unhandled exception: ") add(buf, e.msg) add(buf, " [") @@ -373,7 +374,8 @@ proc reportUnhandledErrorAux(e: ref Exception) {.nodestroy.} = onUnhandledException(buf) else: showErrorMessage2(buf) - `=destroy`(buf) + {.gcsafe.}: + `=destroy`(buf) else: # ugly, but avoids heap allocations :-) template xadd(buf, s, slen) = @@ -387,7 +389,8 @@ proc reportUnhandledErrorAux(e: ref Exception) {.nodestroy.} = if e.trace.len != 0: var trace = $e.trace add(buf, trace) - `=destroy`(trace) + {.gcsafe.}: + `=destroy`(trace) add(buf, "Error: unhandled exception: ") add(buf, e.msg) add(buf, " [") @@ -398,7 +401,7 @@ proc reportUnhandledErrorAux(e: ref Exception) {.nodestroy.} = else: showErrorMessage(buf.addr, L) -proc reportUnhandledError(e: ref Exception) {.nodestroy.} = +proc reportUnhandledError(e: ref Exception) {.nodestroy, gcsafe.} = if unhandledExceptionHook != nil: unhandledExceptionHook(e) when hostOS != "any": diff --git a/lib/system/seqs_v2.nim b/lib/system/seqs_v2.nim index 375eef340..0ac51109e 100644 --- a/lib/system/seqs_v2.nim +++ b/lib/system/seqs_v2.nim @@ -73,7 +73,7 @@ proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize, elemAlign: int): poin q.cap = newCap result = q -proc shrink*[T](x: var seq[T]; newLen: Natural) = +proc shrink*[T](x: var seq[T]; newLen: Natural) {.tags: [].} = when nimvm: setLen(x, newLen) else: diff --git a/tests/effects/tnestedprocs.nim b/tests/effects/tnestedprocs.nim new file mode 100644 index 000000000..125896d44 --- /dev/null +++ b/tests/effects/tnestedprocs.nim @@ -0,0 +1,63 @@ +discard """ + cmd: "nim check --hints:off $file" + nimout: '''tnestedprocs.nim(27, 8) Error: 'inner' can have side effects +> tnestedprocs.nim(29, 13) Hint: 'inner' calls `.sideEffect` 'outer2' +>> tnestedprocs.nim(26, 6) Hint: 'outer2' called by 'inner' + +tnestedprocs.nim(45, 8) Error: 'inner' can have side effects +> tnestedprocs.nim(47, 13) Hint: 'inner' calls `.sideEffect` 'outer6' +>> tnestedprocs.nim(44, 6) Hint: 'outer6' called by 'inner' + +tnestedprocs.nim(58, 41) Error: type mismatch: got but expected 'proc (){.closure, noSideEffect.}' + Pragma mismatch: got '{..}', but expected '{.noSideEffect.}'. +''' + errormsg: "type mismatch: got but expected 'proc (){.closure, noSideEffect.}'" +""" +{.experimental: "strictEffects".} +proc outer {.noSideEffect.} = + proc inner(p: int) = + if p == 0: + outer() + + inner(4) + +outer() + +proc outer2 = + proc inner(p: int) {.noSideEffect.} = + if p == 0: + outer2() + + inner(4) + +outer2() + +proc outer3(p: int) {.noSideEffect.} = + proc inner(p: int) {.noSideEffect.} = + if p == 0: + p.outer3() + + inner(4) + +outer3(5) + +proc outer6 = + proc inner(p: int) {.noSideEffect.} = + if p == 0: + outer6() + + inner(4) + echo "bad" + +outer6() + + +proc outer4 = + proc inner(p: int) {.noSideEffect.} = + if p == 0: + let x: proc () {.noSideEffect.} = outer4 + x() + + inner(4) + +outer4() diff --git a/tests/pragmas/tcompile_pragma.nim b/tests/pragmas/tcompile_pragma.nim index 4e09a7501..5b99352dd 100644 --- a/tests/pragmas/tcompile_pragma.nim +++ b/tests/pragmas/tcompile_pragma.nim @@ -1,5 +1,6 @@ discard """ output: '''34''' + joinable: false """ {.compile("cfunction.c", "-DNUMBER_HERE=34").} From b423ab138f41c24db3b168fd30f1ecce7850a170 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Mon, 8 Nov 2021 07:21:15 +0000 Subject: [PATCH 2636/3768] Remove invalid statements about try in async docs. (#19108) --- lib/pure/asyncdispatch.nim | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index c924d0a3d..20ad60427 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -101,16 +101,9 @@ ## Handling Exceptions ## ------------------- ## -## The most reliable way to handle exceptions is to use `yield` on a future -## then check the future's `failed` property. For example: +## You can handle exceptions in the same way as in ordinary Nim code; +## by using the try statement: ## -## .. code-block:: Nim -## var future = sock.recv(100) -## yield future -## if future.failed: -## # Handle exception -## -## The `async` procedures also offer limited support for the try statement. ## ## .. code-block:: Nim ## try: @@ -119,9 +112,16 @@ ## except: ## # Handle exception ## -## Unfortunately the semantics of the try statement may not always be correct, -## and occasionally the compilation may fail altogether. -## As such it is better to use the former style when possible. +## +## +## An alternative approach to handling exceptions is to use `yield` on a future +## then check the future's `failed` property. For example: +## +## .. code-block:: Nim +## var future = sock.recv(100) +## yield future +## if future.failed: +## # Handle exception ## ## ## Discarding futures @@ -1987,4 +1987,4 @@ when defined(linux) or defined(windows) or defined(macosx) or defined(bsd) or var fdLim: RLimit if getrlimit(RLIMIT_NOFILE, fdLim) < 0: raiseOSError(osLastError()) - result = int(fdLim.rlim_cur) - 1 \ No newline at end of file + result = int(fdLim.rlim_cur) - 1 From b21eb1ed36aa068cc9b0a304742e4c81b5112304 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Mon, 8 Nov 2021 15:10:01 +0300 Subject: [PATCH 2637/3768] change os.nim doc links to new style (#19102) --- compiler/docgen.nim | 7 + doc/docgen.rst | 7 + lib/packages/docutils/dochelpers.nim | 19 +- lib/pure/includes/osenv.nim | 42 +- lib/pure/includes/oserr.nim | 21 +- lib/pure/includes/osseps.nim | 6 +- lib/pure/os.nim | 558 +++++++++++++-------------- tests/stdlib/tdochelpers.nim | 6 + 8 files changed, 345 insertions(+), 321 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 2639840d1..7ba952083 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -954,6 +954,13 @@ proc toLangSymbol(k: TSymKind, n: PNode, baseName: string): LangSymbol = if kind != tkSpaces: result.generics.add(literal.nimIdentNormalize) + if k == skType: + case n[2].kind + of nkEnumTy: result.symTypeKind = "enum" + of nkObjectTy: result.symTypeKind = "object" + of nkTupleTy: result.symTypeKind = "tuple" + else: discard + proc genItem(d: PDoc, n, nameNode: PNode, k: TSymKind, docFlags: DocFlags) = if (docFlags != kForceExport) and not isVisible(d, nameNode): return let diff --git a/doc/docgen.rst b/doc/docgen.rst index 51e7102eb..48166b2c5 100644 --- a/doc/docgen.rst +++ b/doc/docgen.rst @@ -399,6 +399,13 @@ recognized fine:: no backticks: `func [][T](x: openArray[T]): T`_ escaped: `func \`[]\`[T](x: openArray[T]): T`_ +.. Note:: Types that defined as `enum`, or `object`, or `tuple` can also be + referenced with those names directly (instead of `type`):: + + type CopyFlag = enum + ... + ## Ref. `CopyFlag enum`_ + Related Options =============== diff --git a/lib/packages/docutils/dochelpers.nim b/lib/packages/docutils/dochelpers.nim index c488c4d99..8f97a7119 100644 --- a/lib/packages/docutils/dochelpers.nim +++ b/lib/packages/docutils/dochelpers.nim @@ -17,7 +17,9 @@ import rstast type LangSymbol* = object ## symbol signature in Nim - symKind*: string ## "proc", "const", etc + symKind*: string ## "proc", "const", "type", etc + symTypeKind*: string ## ""|enum|object|tuple - + ## valid only when `symKind == "type"` name*: string ## plain symbol name without any parameters generics*: string ## generic parameters (without brackets) isGroup*: bool ## is LangSymbol a group with overloads? @@ -79,7 +81,14 @@ proc toLangSymbol*(linkText: PRstNode): LangSymbol = assert linkText.kind in {rnRef, rnInner} const NimDefs = ["proc", "func", "macro", "method", "iterator", - "template", "converter", "const", "type", "var"] + "template", "converter", "const", "type", "var", + "enum", "object", "tuple"] + template resolveSymKind(x: string) = + if x in ["enum", "object", "tuple"]: + result.symKind = "type" + result.symTypeKind = x + else: + result.symKind = x type State = enum inBeginning @@ -97,7 +106,7 @@ proc toLangSymbol*(linkText: PRstNode): LangSymbol = if curIdent != "": case state of inBeginning: doAssert false, "incorrect state inBeginning" - of afterSymKind: result.symKind = curIdent + of afterSymKind: resolveSymKind curIdent of beforeSymbolName: doAssert false, "incorrect state beforeSymbolName" of atSymbolName: result.name = curIdent.nimIdentBackticksNormalize of afterSymbolName: doAssert false, "incorrect state afterSymbolName" @@ -195,7 +204,7 @@ proc toLangSymbol*(linkText: PRstNode): LangSymbol = let isPostfixSymKind = i > 0 and i == L - 1 and result.symKind == "" and s(i) in NimDefs if isPostfixSymKind: # for links like `foo proc`_ - result.symKind = s(i) + resolveSymKind s(i) else: case state of inBeginning: @@ -235,6 +244,8 @@ proc match*(generated: LangSymbol, docLink: LangSymbol): bool = result = docLink.symKind in ["proc", "func"] else: result = generated.symKind == docLink.symKind + if result and docLink.symKind == "type" and docLink.symTypeKind != "": + result = generated.symTypeKind == docLink.symTypeKind if not result: return result = generated.name == docLink.name if not result: return diff --git a/lib/pure/includes/osenv.nim b/lib/pure/includes/osenv.nim index 00a82327c..1a01ab9bc 100644 --- a/lib/pure/includes/osenv.nim +++ b/lib/pure/includes/osenv.nim @@ -56,13 +56,13 @@ when not defined(nimscript): ## ## If the variable does not exist, `""` is returned. To distinguish ## whether a variable exists or it's value is just `""`, call - ## `existsEnv(key) proc <#existsEnv,string>`_. + ## `existsEnv(key) proc`_. ## ## See also: - ## * `existsEnv proc <#existsEnv,string>`_ - ## * `putEnv proc <#putEnv,string,string>`_ - ## * `delEnv proc <#delEnv,string>`_ - ## * `envPairs iterator <#envPairs.i>`_ + ## * `existsEnv proc`_ + ## * `putEnv proc`_ + ## * `delEnv proc`_ + ## * `envPairs iterator`_ runnableExamples: assert getEnv("unknownEnv") == "" assert getEnv("unknownEnv", "doesn't exist") == "doesn't exist" @@ -76,10 +76,10 @@ when not defined(nimscript): ## Returns true if it exists, false otherwise. ## ## See also: - ## * `getEnv proc <#getEnv,string,string>`_ - ## * `putEnv proc <#putEnv,string,string>`_ - ## * `delEnv proc <#delEnv,string>`_ - ## * `envPairs iterator <#envPairs.i>`_ + ## * `getEnv proc`_ + ## * `putEnv proc`_ + ## * `delEnv proc`_ + ## * `envPairs iterator`_ runnableExamples: assert not existsEnv("unknownEnv") @@ -90,10 +90,10 @@ when not defined(nimscript): ## If an error occurs, `OSError` is raised. ## ## See also: - ## * `getEnv proc <#getEnv,string,string>`_ - ## * `existsEnv proc <#existsEnv,string>`_ - ## * `delEnv proc <#delEnv,string>`_ - ## * `envPairs iterator <#envPairs.i>`_ + ## * `getEnv proc`_ + ## * `existsEnv proc`_ + ## * `delEnv proc`_ + ## * `envPairs iterator`_ when defined(windows): if key.len == 0 or '=' in key: raise newException(OSError, "invalid key, got: " & $(key, val)) @@ -108,10 +108,10 @@ when not defined(nimscript): ## If an error occurs, `OSError` is raised. ## ## See also:ven - ## * `getEnv proc <#getEnv,string,string>`_ - ## * `existsEnv proc <#existsEnv,string>`_ - ## * `putEnv proc <#putEnv,string,string>`_ - ## * `envPairs iterator <#envPairs.i>`_ + ## * `getEnv proc`_ + ## * `existsEnv proc`_ + ## * `putEnv proc`_ + ## * `envPairs iterator`_ template bail = raiseOSError(osLastError(), key) when defined(windows): #[ @@ -190,10 +190,10 @@ iterator envPairs*(): tuple[key, value: string] {.tags: [ReadEnvEffect].} = ## in the second its value. ## ## Works in native backends, nodejs and vm, like the following APIs: - ## * `getEnv proc <#getEnv,string,string>`_ - ## * `existsEnv proc <#existsEnv,string>`_ - ## * `putEnv proc <#putEnv,string,string>`_ - ## * `delEnv proc <#delEnv,string>`_ + ## * `getEnv proc`_ + ## * `existsEnv proc`_ + ## * `putEnv proc`_ + ## * `delEnv proc`_ when nimvm: for ai in envPairsImplSeq(): yield ai else: diff --git a/lib/pure/includes/oserr.nim b/lib/pure/includes/oserr.nim index e42771d52..01403fa70 100644 --- a/lib/pure/includes/oserr.nim +++ b/lib/pure/includes/oserr.nim @@ -18,7 +18,7 @@ proc `$`*(err: OSErrorCode): string {.borrow.} proc osErrorMsg*(errorCode: OSErrorCode): string = ## Converts an OS error code into a human readable string. ## - ## The error code can be retrieved using the `osLastError proc <#osLastError>`_. + ## The error code can be retrieved using the `osLastError proc`_. ## ## If conversion fails, or `errorCode` is `0` then `""` will be ## returned. @@ -28,8 +28,8 @@ proc osErrorMsg*(errorCode: OSErrorCode): string = ## message. ## ## See also: - ## * `raiseOSError proc <#raiseOSError,OSErrorCode,string>`_ - ## * `osLastError proc <#osLastError>`_ + ## * `raiseOSError proc`_ + ## * `osLastError proc`_ runnableExamples: when defined(linux): assert osErrorMsg(OSErrorCode(0)) == "" @@ -63,18 +63,17 @@ proc newOSError*( ## Creates a new `OSError exception `_. ## ## The `errorCode` will determine the - ## message, `osErrorMsg proc <#osErrorMsg,OSErrorCode>`_ will be used + ## message, `osErrorMsg proc`_ will be used ## to get this message. ## - ## The error code can be retrieved using the `osLastError proc - ## <#osLastError>`_. + ## The error code can be retrieved using the `osLastError proc`_. ## ## If the error code is `0` or an error message could not be retrieved, ## the message `unknown OS error` will be used. ## ## See also: - ## * `osErrorMsg proc <#osErrorMsg,OSErrorCode>`_ - ## * `osLastError proc <#osLastError>`_ + ## * `osErrorMsg proc`_ + ## * `osLastError proc`_ var e: owned(ref OSError); new(e) e.errorCode = errorCode.int32 e.msg = osErrorMsg(errorCode) @@ -90,7 +89,7 @@ proc newOSError*( proc raiseOSError*(errorCode: OSErrorCode, additionalInfo = "") {.noinline.} = ## Raises an `OSError exception `_. ## - ## Read the description of the `newOSError proc <#newOSError,OSErrorCode,string>`_ to learn + ## Read the description of the `newOSError proc`_ to learn ## how the exception object is created. raise newOSError(errorCode, additionalInfo) @@ -109,8 +108,8 @@ proc osLastError*(): OSErrorCode {.sideEffect.} = ## immediately after an OS call fails. On POSIX systems this is not a problem. ## ## See also: - ## * `osErrorMsg proc <#osErrorMsg,OSErrorCode>`_ - ## * `raiseOSError proc <#raiseOSError,OSErrorCode,string>`_ + ## * `osErrorMsg proc`_ + ## * `raiseOSError proc`_ when defined(nimscript): discard elif defined(windows): diff --git a/lib/pure/includes/osseps.nim b/lib/pure/includes/osseps.nim index 10c85047b..1ea587e3c 100644 --- a/lib/pure/includes/osseps.nim +++ b/lib/pure/includes/osseps.nim @@ -37,9 +37,9 @@ const when doslikeFileSystem: '/' else: DirSep ## An alternative character used by the operating system to separate - ## pathname components, or the same as `DirSep <#DirSep>`_ if only one separator + ## pathname components, or the same as DirSep_ if only one separator ## character exists. This is set to `'/'` on Windows systems - ## where `DirSep <#DirSep>`_ is a backslash (`'\\'`). + ## where DirSep_ is a backslash (`'\\'`). PathSep* = when defined(macos) or defined(RISCOS): ',' @@ -55,7 +55,7 @@ const defined(PalmOS) or defined(MorphOS): false else: true ## True if the file system is case sensitive, false otherwise. Used by - ## `cmpPaths proc <#cmpPaths,string,string>`_ to compare filenames properly. + ## `cmpPaths proc`_ to compare filenames properly. ExeExt* = when doslikeFileSystem: "exe" diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 5320aa87e..513e09b48 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -21,9 +21,9 @@ runnableExamples: ## **See also:** ## * `osproc module `_ for process communication beyond -## `execShellCmd proc <#execShellCmd,string>`_ +## `execShellCmd proc`_ ## * `parseopt module `_ for command-line parser beyond -## `parseCmdLine proc <#parseCmdLine,string>`_ +## `parseCmdLine proc`_ ## * `uri module `_ ## * `distros module `_ ## * `dynlib module `_ @@ -152,9 +152,9 @@ proc joinPath*(head, tail: string): string {. ## head has one). ## ## See also: - ## * `joinPath(varargs) proc <#joinPath,varargs[string]>`_ - ## * `/ proc <#/,string,string>`_ - ## * `splitPath proc <#splitPath,string>`_ + ## * `joinPath(parts: varargs[string]) proc`_ + ## * `/ proc`_ + ## * `splitPath proc`_ ## * `uri.combine proc `_ ## * `uri./ proc `_ runnableExamples: @@ -189,17 +189,17 @@ proc joinPath*(head, tail: string): string {. proc joinPath*(parts: varargs[string]): string {.noSideEffect, rtl, extern: "nos$1OpenArray".} = - ## The same as `joinPath(head, tail) proc <#joinPath,string,string>`_, + ## The same as `joinPath(head, tail) proc`_, ## but works with any number of directory parts. ## ## You need to pass at least one element or the proc ## will assert in debug builds and crash on release builds. ## ## See also: - ## * `joinPath(head, tail) proc <#joinPath,string,string>`_ - ## * `/ proc <#/,string,string>`_ - ## * `/../ proc <#/../,string,string>`_ - ## * `splitPath proc <#splitPath,string>`_ + ## * `joinPath(head, tail) proc`_ + ## * `/ proc`_ + ## * `/../ proc`_ + ## * `splitPath proc`_ runnableExamples: when defined(posix): assert joinPath("a") == "a" @@ -214,13 +214,13 @@ proc joinPath*(parts: varargs[string]): string {.noSideEffect, joinPathImpl(result, state, parts[i]) proc `/`*(head, tail: string): string {.noSideEffect, inline.} = - ## The same as `joinPath(head, tail) proc <#joinPath,string,string>`_. + ## The same as `joinPath(head, tail) proc`_. ## ## See also: - ## * `/../ proc <#/../,string,string>`_ - ## * `joinPath(head, tail) proc <#joinPath,string,string>`_ - ## * `joinPath(varargs) proc <#joinPath,varargs[string]>`_ - ## * `splitPath proc <#splitPath,string>`_ + ## * `/../ proc`_ + ## * `joinPath(head, tail) proc`_ + ## * `joinPath(parts: varargs[string]) proc`_ + ## * `splitPath proc`_ ## * `uri.combine proc `_ ## * `uri./ proc `_ runnableExamples: @@ -239,11 +239,11 @@ proc splitPath*(path: string): tuple[head, tail: string] {. ## ``head / tail == path`` (except for edge cases like "/usr"). ## ## See also: - ## * `joinPath(head, tail) proc <#joinPath,string,string>`_ - ## * `joinPath(varargs) proc <#joinPath,varargs[string]>`_ - ## * `/ proc <#/,string,string>`_ - ## * `/../ proc <#/../,string,string>`_ - ## * `relativePath proc <#relativePath,string,string>`_ + ## * `joinPath(head, tail) proc`_ + ## * `joinPath(parts: varargs[string]) proc`_ + ## * `/ proc`_ + ## * `/../ proc`_ + ## * `relativePath proc`_ runnableExamples: assert splitPath("usr/local/bin") == ("usr/local", "bin") assert splitPath("usr/local/bin/") == ("usr/local/bin", "") @@ -366,7 +366,7 @@ proc relativePath*(path, base: string, sep = DirSep): string {. rtl, extern: "nos$1".} = ## Converts `path` to a path relative to `base`. ## - ## The `sep` (default: `DirSep <#DirSep>`_) is used for the path normalizations, + ## The `sep` (default: DirSep_) is used for the path normalizations, ## this can be useful to ensure the relative path only contains `'/'` ## so that it can be used for URL constructions. ## @@ -375,9 +375,9 @@ proc relativePath*(path, base: string, sep = DirSep): string {. ## That means an absolute path can be returned. ## ## See also: - ## * `splitPath proc <#splitPath,string>`_ - ## * `parentDir proc <#parentDir,string>`_ - ## * `tailDir proc <#tailDir,string>`_ + ## * `splitPath proc`_ + ## * `parentDir proc`_ + ## * `tailDir proc`_ runnableExamples: assert relativePath("/Users/me/bar/z.nim", "/Users/other/bad", '/') == "../../me/bar/z.nim" assert relativePath("/Users/me/bar/z.nim", "/Users/other", '/') == "../me/bar/z.nim" @@ -476,14 +476,13 @@ proc parentDir*(path: string): string {. ## ## This is similar to ``splitPath(path).head`` when ``path`` doesn't end ## in a dir separator, but also takes care of path normalizations. - ## The remainder can be obtained with `lastPathPart(path) proc - ## <#lastPathPart,string>`_. + ## The remainder can be obtained with `lastPathPart(path) proc`_. ## ## See also: - ## * `relativePath proc <#relativePath,string,string>`_ - ## * `splitPath proc <#splitPath,string>`_ - ## * `tailDir proc <#tailDir,string>`_ - ## * `parentDirs iterator <#parentDirs.i,string>`_ + ## * `relativePath proc`_ + ## * `splitPath proc`_ + ## * `tailDir proc`_ + ## * `parentDirs iterator`_ runnableExamples: assert parentDir("") == "" when defined(posix): @@ -511,9 +510,9 @@ proc tailDir*(path: string): string {. ## Returns the tail part of `path`. ## ## See also: - ## * `relativePath proc <#relativePath,string,string>`_ - ## * `splitPath proc <#splitPath,string>`_ - ## * `parentDir proc <#parentDir,string>`_ + ## * `relativePath proc`_ + ## * `splitPath proc`_ + ## * `parentDir proc`_ runnableExamples: assert tailDir("/bin") == "bin" assert tailDir("bin") == "" @@ -556,7 +555,7 @@ iterator parentDirs*(path: string, fromRoot=false, inclusive=true): string = ## only the directories appearing in the relative path. ## ## See also: - ## * `parentDir proc <#parentDir,string>`_ + ## * `parentDir proc`_ ## runnableExamples: let g = "a/b/c" @@ -599,8 +598,8 @@ proc `/../`*(head, tail: string): string {.noSideEffect.} = ## directory. Then ``head / tail`` is performed instead. ## ## See also: - ## * `/ proc <#/,string,string>`_ - ## * `parentDir proc <#parentDir,string>`_ + ## * `/ proc`_ + ## * `parentDir proc`_ runnableExamples: when defined(posix): assert "a/b/c" /../ "d/e" == "a/b/d/e" @@ -621,11 +620,11 @@ proc searchExtPos*(path: string): int = ## of extension. Returns -1 otherwise. ## ## See also: - ## * `splitFile proc <#splitFile,string>`_ - ## * `extractFilename proc <#extractFilename,string>`_ - ## * `lastPathPart proc <#lastPathPart,string>`_ - ## * `changeFileExt proc <#changeFileExt,string,string>`_ - ## * `addFileExt proc <#addFileExt,string,string>`_ + ## * `splitFile proc`_ + ## * `extractFilename proc`_ + ## * `lastPathPart proc`_ + ## * `changeFileExt proc`_ + ## * `addFileExt proc`_ runnableExamples: assert searchExtPos("a/b/c") == -1 assert searchExtPos("c.nim") == 1 @@ -645,7 +644,7 @@ proc splitFile*(path: string): tuple[dir, name, ext: string] {. noSideEffect, rtl, extern: "nos$1".} = ## Splits a filename into `(dir, name, extension)` tuple. ## - ## `dir` does not end in `DirSep <#DirSep>`_ unless it's `/`. + ## `dir` does not end in DirSep_ unless it's `/`. ## `extension` includes the leading dot. ## ## If `path` has no extension, `ext` is the empty string. @@ -653,11 +652,11 @@ proc splitFile*(path: string): tuple[dir, name, ext: string] {. ## If `path` has no filename component, `name` and `ext` are empty strings. ## ## See also: - ## * `searchExtPos proc <#searchExtPos,string>`_ - ## * `extractFilename proc <#extractFilename,string>`_ - ## * `lastPathPart proc <#lastPathPart,string>`_ - ## * `changeFileExt proc <#changeFileExt,string,string>`_ - ## * `addFileExt proc <#addFileExt,string,string>`_ + ## * `searchExtPos proc`_ + ## * `extractFilename proc`_ + ## * `lastPathPart proc`_ + ## * `changeFileExt proc`_ + ## * `addFileExt proc`_ runnableExamples: var (dir, name, ext) = splitFile("usr/local/nimc.html") assert dir == "usr/local" @@ -698,15 +697,14 @@ proc extractFilename*(path: string): string {. noSideEffect, rtl, extern: "nos$1".} = ## Extracts the filename of a given `path`. ## - ## This is the same as ``name & ext`` from `splitFile(path) proc - ## <#splitFile,string>`_. + ## This is the same as ``name & ext`` from `splitFile(path) proc`_. ## ## See also: - ## * `searchExtPos proc <#searchExtPos,string>`_ - ## * `splitFile proc <#splitFile,string>`_ - ## * `lastPathPart proc <#lastPathPart,string>`_ - ## * `changeFileExt proc <#changeFileExt,string,string>`_ - ## * `addFileExt proc <#addFileExt,string,string>`_ + ## * `searchExtPos proc`_ + ## * `splitFile proc`_ + ## * `lastPathPart proc`_ + ## * `changeFileExt proc`_ + ## * `addFileExt proc`_ runnableExamples: assert extractFilename("foo/bar/") == "" assert extractFilename("foo/bar") == "bar" @@ -719,15 +717,15 @@ proc extractFilename*(path: string): string {. proc lastPathPart*(path: string): string {. noSideEffect, rtl, extern: "nos$1".} = - ## Like `extractFilename proc <#extractFilename,string>`_, but ignores + ## Like `extractFilename proc`_, but ignores ## trailing dir separator; aka: `baseName`:idx: in some other languages. ## ## See also: - ## * `searchExtPos proc <#searchExtPos,string>`_ - ## * `splitFile proc <#splitFile,string>`_ - ## * `extractFilename proc <#extractFilename,string>`_ - ## * `changeFileExt proc <#changeFileExt,string,string>`_ - ## * `addFileExt proc <#addFileExt,string,string>`_ + ## * `searchExtPos proc`_ + ## * `splitFile proc`_ + ## * `extractFilename proc`_ + ## * `changeFileExt proc`_ + ## * `addFileExt proc`_ runnableExamples: assert lastPathPart("foo/bar/") == "bar" assert lastPathPart("foo/bar") == "bar" @@ -747,11 +745,11 @@ proc changeFileExt*(filename, ext: string): string {. ## of none such beast.) ## ## See also: - ## * `searchExtPos proc <#searchExtPos,string>`_ - ## * `splitFile proc <#splitFile,string>`_ - ## * `extractFilename proc <#extractFilename,string>`_ - ## * `lastPathPart proc <#lastPathPart,string>`_ - ## * `addFileExt proc <#addFileExt,string,string>`_ + ## * `searchExtPos proc`_ + ## * `splitFile proc`_ + ## * `extractFilename proc`_ + ## * `lastPathPart proc`_ + ## * `addFileExt proc`_ runnableExamples: assert changeFileExt("foo.bar", "baz") == "foo.baz" assert changeFileExt("foo.bar", "") == "foo" @@ -771,11 +769,11 @@ proc addFileExt*(filename, ext: string): string {. ## (Although I know of none such beast.) ## ## See also: - ## * `searchExtPos proc <#searchExtPos,string>`_ - ## * `splitFile proc <#splitFile,string>`_ - ## * `extractFilename proc <#extractFilename,string>`_ - ## * `lastPathPart proc <#lastPathPart,string>`_ - ## * `changeFileExt proc <#changeFileExt,string,string>`_ + ## * `searchExtPos proc`_ + ## * `splitFile proc`_ + ## * `extractFilename proc`_ + ## * `lastPathPart proc`_ + ## * `changeFileExt proc`_ runnableExamples: assert addFileExt("foo.bar", "baz") == "foo.bar" assert addFileExt("foo.bar", "") == "foo.bar" @@ -875,15 +873,15 @@ proc getHomeDir*(): string {.rtl, extern: "nos$1", tags: [ReadEnvEffect, ReadIOEffect].} = ## Returns the home directory of the current user. ## - ## This proc is wrapped by the `expandTilde proc <#expandTilde,string>`_ + ## This proc is wrapped by the `expandTilde proc`_ ## for the convenience of processing paths coming from user configuration files. ## ## See also: - ## * `getConfigDir proc <#getConfigDir>`_ - ## * `getTempDir proc <#getTempDir>`_ - ## * `expandTilde proc <#expandTilde,string>`_ - ## * `getCurrentDir proc <#getCurrentDir>`_ - ## * `setCurrentDir proc <#setCurrentDir,string>`_ + ## * `getConfigDir proc`_ + ## * `getTempDir proc`_ + ## * `expandTilde proc`_ + ## * `getCurrentDir proc`_ + ## * `setCurrentDir proc`_ runnableExamples: assert getHomeDir() == expandTilde("~") @@ -903,11 +901,11 @@ proc getConfigDir*(): string {.rtl, extern: "nos$1", ## returned string: `\\` on Windows and `/` on all other OSs. ## ## See also: - ## * `getHomeDir proc <#getHomeDir>`_ - ## * `getTempDir proc <#getTempDir>`_ - ## * `expandTilde proc <#expandTilde,string>`_ - ## * `getCurrentDir proc <#getCurrentDir>`_ - ## * `setCurrentDir proc <#setCurrentDir,string>`_ + ## * `getHomeDir proc`_ + ## * `getTempDir proc`_ + ## * `expandTilde proc`_ + ## * `getCurrentDir proc`_ + ## * `setCurrentDir proc`_ when defined(windows): result = getEnv("APPDATA") else: @@ -927,9 +925,9 @@ proc getCacheDir*(): string = ## * On other platforms: `getEnv("XDG_CACHE_HOME", getEnv("HOME") / ".cache")` ## ## **See also:** - ## * `getHomeDir proc <#getHomeDir>`_ - ## * `getTempDir proc <#getTempDir>`_ - ## * `getConfigDir proc <#getConfigDir>`_ + ## * `getHomeDir proc`_ + ## * `getTempDir proc`_ + ## * `getConfigDir proc`_ # follows https://crates.io/crates/platform-dirs when defined(windows): result = getEnv("LOCALAPPDATA") @@ -985,11 +983,11 @@ proc getTempDir*(): string {.rtl, extern: "nos$1", ## **Note:** This proc does not check whether the returned path exists. ## ## See also: - ## * `getHomeDir proc <#getHomeDir>`_ - ## * `getConfigDir proc <#getConfigDir>`_ - ## * `expandTilde proc <#expandTilde,string>`_ - ## * `getCurrentDir proc <#getCurrentDir>`_ - ## * `setCurrentDir proc <#setCurrentDir,string>`_ + ## * `getHomeDir proc`_ + ## * `getConfigDir proc`_ + ## * `expandTilde proc`_ + ## * `getCurrentDir proc`_ + ## * `setCurrentDir proc`_ const tempDirDefault = "/tmp" when defined(tempDir): const tempDir {.strdefine.}: string = tempDirDefault @@ -1015,17 +1013,17 @@ proc getTempDir*(): string {.rtl, extern: "nos$1", proc expandTilde*(path: string): string {. tags: [ReadEnvEffect, ReadIOEffect].} = ## Expands ``~`` or a path starting with ``~/`` to a full path, replacing - ## ``~`` with `getHomeDir() <#getHomeDir>`_ (otherwise returns ``path`` unmodified). + ## ``~`` with `getHomeDir()`_ (otherwise returns ``path`` unmodified). ## ## Windows: this is still supported despite the Windows platform not having this ## convention; also, both ``~/`` and ``~\`` are handled. ## ## See also: - ## * `getHomeDir proc <#getHomeDir>`_ - ## * `getConfigDir proc <#getConfigDir>`_ - ## * `getTempDir proc <#getTempDir>`_ - ## * `getCurrentDir proc <#getCurrentDir>`_ - ## * `setCurrentDir proc <#setCurrentDir,string>`_ + ## * `getHomeDir proc`_ + ## * `getConfigDir proc`_ + ## * `getTempDir proc`_ + ## * `getCurrentDir proc`_ + ## * `setCurrentDir proc`_ runnableExamples: assert expandTilde("~" / "appname.cfg") == getHomeDir() / "appname.cfg" assert expandTilde("~/foo/bar") == getHomeDir() / "foo/bar" @@ -1089,9 +1087,8 @@ when defined(windows) or defined(posix) or defined(nintendoswitch): proc quoteShell*(s: string): string {.noSideEffect, rtl, extern: "nosp$1".} = ## Quote ``s``, so it can be safely passed to shell. ## - ## When on Windows, it calls `quoteShellWindows proc - ## <#quoteShellWindows,string>`_. Otherwise, calls `quoteShellPosix proc - ## <#quoteShellPosix,string>`_. + ## When on Windows, it calls `quoteShellWindows proc`_. + ## Otherwise, calls `quoteShellPosix proc`_. when defined(windows): result = quoteShellWindows(s) else: @@ -1158,8 +1155,8 @@ proc fileExists*(filename: string): bool {.rtl, extern: "nos$1", ## Directories, device files, named pipes and sockets return false. ## ## See also: - ## * `dirExists proc <#dirExists,string>`_ - ## * `symlinkExists proc <#symlinkExists,string>`_ + ## * `dirExists proc`_ + ## * `symlinkExists proc`_ when defined(windows): when useWinUnicode: wrapUnary(a, getFileAttributesW, filename) @@ -1177,8 +1174,8 @@ proc dirExists*(dir: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect] ## is returned. Follows symlinks. ## ## See also: - ## * `fileExists proc <#fileExists,string>`_ - ## * `symlinkExists proc <#symlinkExists,string>`_ + ## * `fileExists proc`_ + ## * `symlinkExists proc`_ when defined(windows): when useWinUnicode: wrapUnary(a, getFileAttributesW, dir) @@ -1197,8 +1194,8 @@ proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1", ## regardless of whether the link points to a directory or file. ## ## See also: - ## * `fileExists proc <#fileExists,string>`_ - ## * `dirExists proc <#dirExists,string>`_ + ## * `fileExists proc`_ + ## * `dirExists proc`_ when defined(windows): when useWinUnicode: wrapUnary(a, getFileAttributesW, link) @@ -1228,7 +1225,7 @@ proc findExe*(exe: string, followSymlinks: bool = true; ## in directories listed in the ``PATH`` environment variable. ## ## Returns `""` if the `exe` cannot be found. `exe` - ## is added the `ExeExts <#ExeExts>`_ file extensions if it has none. + ## is added the `ExeExts`_ file extensions if it has none. ## ## If the system supports symlinks it also resolves them until it ## meets the actual file. This behavior can be disabled if desired @@ -1283,9 +1280,9 @@ proc getLastModificationTime*(file: string): times.Time {.rtl, extern: "nos$1", ## Returns the `file`'s last modification time. ## ## See also: - ## * `getLastAccessTime proc <#getLastAccessTime,string>`_ - ## * `getCreationTime proc <#getCreationTime,string>`_ - ## * `fileNewer proc <#fileNewer,string,string>`_ + ## * `getLastAccessTime proc`_ + ## * `getCreationTime proc`_ + ## * `fileNewer proc`_ when defined(posix): var res: Stat if stat(file, res) < 0'i32: raiseOSError(osLastError(), file) @@ -1301,9 +1298,9 @@ proc getLastAccessTime*(file: string): times.Time {.rtl, extern: "nos$1", noWeir ## Returns the `file`'s last read or write access time. ## ## See also: - ## * `getLastModificationTime proc <#getLastModificationTime,string>`_ - ## * `getCreationTime proc <#getCreationTime,string>`_ - ## * `fileNewer proc <#fileNewer,string,string>`_ + ## * `getLastModificationTime proc`_ + ## * `getCreationTime proc`_ + ## * `fileNewer proc`_ when defined(posix): var res: Stat if stat(file, res) < 0'i32: raiseOSError(osLastError(), file) @@ -1323,9 +1320,9 @@ proc getCreationTime*(file: string): times.Time {.rtl, extern: "nos$1", noWeirdT ## `here `_ for details. ## ## See also: - ## * `getLastModificationTime proc <#getLastModificationTime,string>`_ - ## * `getLastAccessTime proc <#getLastAccessTime,string>`_ - ## * `fileNewer proc <#fileNewer,string,string>`_ + ## * `getLastModificationTime proc`_ + ## * `getLastAccessTime proc`_ + ## * `fileNewer proc`_ when defined(posix): var res: Stat if stat(file, res) < 0'i32: raiseOSError(osLastError(), file) @@ -1342,9 +1339,9 @@ proc fileNewer*(a, b: string): bool {.rtl, extern: "nos$1", noWeirdTarget.} = ## modification time is later than `b`'s. ## ## See also: - ## * `getLastModificationTime proc <#getLastModificationTime,string>`_ - ## * `getLastAccessTime proc <#getLastAccessTime,string>`_ - ## * `getCreationTime proc <#getCreationTime,string>`_ + ## * `getLastModificationTime proc`_ + ## * `getLastAccessTime proc`_ + ## * `getCreationTime proc`_ when defined(posix): # If we don't have access to nanosecond resolution, use '>=' when not StatHasNanoseconds: @@ -1362,10 +1359,10 @@ when not defined(nimscript): ## So the path returned by this proc is determined at run time. ## ## See also: - ## * `getHomeDir proc <#getHomeDir>`_ - ## * `getConfigDir proc <#getConfigDir>`_ - ## * `getTempDir proc <#getTempDir>`_ - ## * `setCurrentDir proc <#setCurrentDir,string>`_ + ## * `getHomeDir proc`_ + ## * `getConfigDir proc`_ + ## * `getTempDir proc`_ + ## * `setCurrentDir proc`_ ## * `currentSourcePath template `_ ## * `getProjectPath proc `_ when defined(nodejs): @@ -1421,10 +1418,10 @@ proc setCurrentDir*(newDir: string) {.inline, tags: [], noWeirdTarget.} = ## is raised if `newDir` cannot been set. ## ## See also: - ## * `getHomeDir proc <#getHomeDir>`_ - ## * `getConfigDir proc <#getConfigDir>`_ - ## * `getTempDir proc <#getTempDir>`_ - ## * `getCurrentDir proc <#getCurrentDir>`_ + ## * `getHomeDir proc`_ + ## * `getConfigDir proc`_ + ## * `getTempDir proc`_ + ## * `getCurrentDir proc`_ when defined(windows): when useWinUnicode: if setCurrentDirectoryW(newWideCString(newDir)) == 0'i32: @@ -1441,8 +1438,8 @@ proc absolutePath*(path: string, root = getCurrentDir()): string = ## If `path` is absolute, return it, ignoring `root`. ## ## See also: - ## * `normalizedPath proc <#normalizedPath,string>`_ - ## * `normalizePath proc <#normalizePath,string>`_ + ## * `normalizedPath proc`_ + ## * `normalizePath proc`_ runnableExamples: assert absolutePath("a") == getCurrentDir() / "a" @@ -1479,9 +1476,9 @@ proc normalizePath*(path: var string) {.rtl, extern: "nos$1", tags: [].} = ## Triple dot is not handled. ## ## See also: - ## * `absolutePath proc <#absolutePath,string>`_ - ## * `normalizedPath proc <#normalizedPath,string>`_ for outplace version - ## * `normalizeExe proc <#normalizeExe,string>`_ + ## * `absolutePath proc`_ + ## * `normalizedPath proc`_ for outplace version + ## * `normalizeExe proc`_ runnableExamples: when defined(posix): var a = "a///b//..//c///d" @@ -1522,8 +1519,8 @@ proc normalizedPath*(path: string): string {.rtl, extern: "nos$1", tags: [].} = ## Returns a normalized path for the current OS. ## ## See also: - ## * `absolutePath proc <#absolutePath,string>`_ - ## * `normalizePath proc <#normalizePath,string>`_ for the in-place version + ## * `absolutePath proc`_ + ## * `normalizePath proc`_ for the in-place version runnableExamples: when defined(posix): assert normalizedPath("a///b//..//c///d") == "a/c/d" @@ -1561,7 +1558,7 @@ proc sameFile*(path1, path2: string): bool {.rtl, extern: "nos$1", ## sym-linked paths to the same file or directory. ## ## See also: - ## * `sameFileContent proc <#sameFileContent,string,string>`_ + ## * `sameFileContent proc`_ when defined(windows): var success = true var f1 = openHandle(path1) @@ -1598,9 +1595,9 @@ type FilePermission* = enum ## File access permission, modelled after UNIX. ## ## See also: - ## * `getFilePermissions <#getFilePermissions,string>`_ - ## * `setFilePermissions <#setFilePermissions,string,set[FilePermission]>`_ - ## * `FileInfo object <#FileInfo>`_ + ## * `getFilePermissions`_ + ## * `setFilePermissions`_ + ## * `FileInfo object`_ fpUserExec, ## execute access for the file owner fpUserWrite, ## write access for the file owner fpUserRead, ## read access for the file owner @@ -1620,8 +1617,8 @@ proc getFilePermissions*(filename: string): set[FilePermission] {. ## permission is available in any case. ## ## See also: - ## * `setFilePermissions proc <#setFilePermissions,string,set[FilePermission]>`_ - ## * `FilePermission enum <#FilePermission>`_ + ## * `setFilePermissions proc`_ + ## * `FilePermission enum`_ when defined(posix): var a: Stat if stat(filename, a) < 0'i32: raiseOSError(osLastError(), filename) @@ -1666,8 +1663,8 @@ proc setFilePermissions*(filename: string, permissions: set[FilePermission], ## ``fpUserWrite`` permission. ## ## See also: - ## * `getFilePermissions <#getFilePermissions,string>`_ - ## * `FilePermission enum <#FilePermission>`_ + ## * `getFilePermissions proc`_ + ## * `FilePermission enum`_ when defined(posix): var p = 0.Mode if fpUserRead in permissions: p = p or S_IRUSR.Mode @@ -1744,8 +1741,8 @@ proc createSymlink*(src, dest: string) {.noWeirdTarget.} = ## of symlinks to root users (administrators) or users with developper mode enabled. ## ## See also: - ## * `createHardlink proc <#createHardlink,string,string>`_ - ## * `expandSymlink proc <#expandSymlink,string>`_ + ## * `createHardlink proc`_ + ## * `expandSymlink proc`_ when defined(windows): const SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE = 2 @@ -1769,7 +1766,7 @@ proc expandSymlink*(symlinkPath: string): string {.noWeirdTarget.} = ## On Windows this is a noop, `symlinkPath` is simply returned. ## ## See also: - ## * `createSymlink proc <#createSymlink,string,string>`_ + ## * `createSymlink proc`_ when defined(windows): result = symlinkPath else: @@ -1823,11 +1820,11 @@ proc copyFile*(source, dest: string, options = {cfSymlinkFollow}) {.rtl, ## copy the source file's attributes into dest. ## ## On other platforms you need - ## to use `getFilePermissions <#getFilePermissions,string>`_ and - ## `setFilePermissions <#setFilePermissions,string,set[FilePermission]>`_ + ## to use `getFilePermissions`_ and + ## `setFilePermissions`_ ## procs ## to copy them by hand (or use the convenience `copyFileWithPermissions - ## proc <#copyFileWithPermissions,string,string>`_), + ## proc`_), ## otherwise `dest` will inherit the default permissions of a newly ## created file for the user. ## @@ -1838,12 +1835,12 @@ proc copyFile*(source, dest: string, options = {cfSymlinkFollow}) {.rtl, ## `-d:nimLegacyCopyFile` is used. ## ## See also: - ## * `CopyFlag enum <#CopyFlag>`_ - ## * `copyDir proc <#copyDir,string,string>`_ - ## * `copyFileWithPermissions proc <#copyFileWithPermissions,string,string>`_ - ## * `tryRemoveFile proc <#tryRemoveFile,string>`_ - ## * `removeFile proc <#removeFile,string>`_ - ## * `moveFile proc <#moveFile,string,string>`_ + ## * `CopyFlag enum`_ + ## * `copyDir proc`_ + ## * `copyFileWithPermissions proc`_ + ## * `tryRemoveFile proc`_ + ## * `removeFile proc`_ + ## * `moveFile proc`_ doAssert card(copyFlagSymlink * options) == 1, "There should be exactly " & "one cfSymlink* in options" @@ -1908,8 +1905,8 @@ proc copyFileToDir*(source, dir: string, options = {cfSymlinkFollow}) ## ignored on Windows: symlinks are skipped. ## ## See also: - ## * `CopyFlag enum <#CopyFlag>`_ - ## * `copyFile proc <#copyDir,string,string>`_ + ## * `CopyFlag enum`_ + ## * `copyFile proc`_ if dir.len == 0: # treating "" as "." is error prone raise newException(ValueError, "dest is empty") copyFile(source, dir / source.lastPathPart, options) @@ -1942,10 +1939,10 @@ proc tryRemoveFile*(file: string): bool {.rtl, extern: "nos$1", tags: [WriteDirE ## On Windows, ignores the read-only attribute. ## ## See also: - ## * `copyFile proc <#copyFile,string,string>`_ - ## * `copyFileWithPermissions proc <#copyFileWithPermissions,string,string>`_ - ## * `removeFile proc <#removeFile,string>`_ - ## * `moveFile proc <#moveFile,string,string>`_ + ## * `copyFile proc`_ + ## * `copyFileWithPermissions proc`_ + ## * `removeFile proc`_ + ## * `moveFile proc`_ result = true when defined(windows): when useWinUnicode: @@ -1974,11 +1971,11 @@ proc removeFile*(file: string) {.rtl, extern: "nos$1", tags: [WriteDirEffect], n ## On Windows, ignores the read-only attribute. ## ## See also: - ## * `removeDir proc <#removeDir,string>`_ - ## * `copyFile proc <#copyFile,string,string>`_ - ## * `copyFileWithPermissions proc <#copyFileWithPermissions,string,string>`_ - ## * `tryRemoveFile proc <#tryRemoveFile,string>`_ - ## * `moveFile proc <#moveFile,string,string>`_ + ## * `removeDir proc`_ + ## * `copyFile proc`_ + ## * `copyFileWithPermissions proc`_ + ## * `tryRemoveFile proc`_ + ## * `moveFile proc`_ if not tryRemoveFile(file): raiseOSError(osLastError(), file) @@ -2022,11 +2019,11 @@ proc moveFile*(source, dest: string) {.rtl, extern: "nos$1", ## Can be used to `rename files`:idx:. ## ## See also: - ## * `moveDir proc <#moveDir,string,string>`_ - ## * `copyFile proc <#copyFile,string,string>`_ - ## * `copyFileWithPermissions proc <#copyFileWithPermissions,string,string>`_ - ## * `removeFile proc <#removeFile,string>`_ - ## * `tryRemoveFile proc <#tryRemoveFile,string>`_ + ## * `moveDir proc`_ + ## * `copyFile proc`_ + ## * `copyFileWithPermissions proc`_ + ## * `removeFile proc`_ + ## * `tryRemoveFile proc`_ if not tryMoveFSObject(source, dest, isDir = false): when defined(windows): @@ -2135,10 +2132,10 @@ iterator walkPattern*(pattern: string): string {.tags: [ReadDirEffect], noWeirdT ## notation is supported. ## ## See also: - ## * `walkFiles iterator <#walkFiles.i,string>`_ - ## * `walkDirs iterator <#walkDirs.i,string>`_ - ## * `walkDir iterator <#walkDir.i,string>`_ - ## * `walkDirRec iterator <#walkDirRec.i,string>`_ + ## * `walkFiles iterator`_ + ## * `walkDirs iterator`_ + ## * `walkDir iterator`_ + ## * `walkDirRec iterator`_ runnableExamples: import std/sequtils let paths = toSeq(walkPattern("lib/pure/*")) # works on Windows too @@ -2154,10 +2151,10 @@ iterator walkFiles*(pattern: string): string {.tags: [ReadDirEffect], noWeirdTar ## notation is supported. ## ## See also: - ## * `walkPattern iterator <#walkPattern.i,string>`_ - ## * `walkDirs iterator <#walkDirs.i,string>`_ - ## * `walkDir iterator <#walkDir.i,string>`_ - ## * `walkDirRec iterator <#walkDirRec.i,string>`_ + ## * `walkPattern iterator`_ + ## * `walkDirs iterator`_ + ## * `walkDir iterator`_ + ## * `walkDirRec iterator`_ runnableExamples: import std/sequtils assert "lib/pure/os.nim".unixToNativePath in toSeq(walkFiles("lib/pure/*.nim")) # works on Windows too @@ -2171,10 +2168,10 @@ iterator walkDirs*(pattern: string): string {.tags: [ReadDirEffect], noWeirdTarg ## notation is supported. ## ## See also: - ## * `walkPattern iterator <#walkPattern.i,string>`_ - ## * `walkFiles iterator <#walkFiles.i,string>`_ - ## * `walkDir iterator <#walkDir.i,string>`_ - ## * `walkDirRec iterator <#walkDirRec.i,string>`_ + ## * `walkPattern iterator`_ + ## * `walkFiles iterator`_ + ## * `walkDir iterator`_ + ## * `walkDirRec iterator`_ runnableExamples: import std/sequtils let paths = toSeq(walkDirs("lib/pure/*")) # works on Windows too @@ -2235,15 +2232,15 @@ type PathComponent* = enum ## Enumeration specifying a path component. ## ## See also: - ## * `walkDirRec iterator <#walkDirRec.i,string>`_ - ## * `FileInfo object <#FileInfo>`_ + ## * `walkDirRec iterator`_ + ## * `FileInfo object`_ pcFile, ## path refers to a file pcLinkToFile, ## path refers to a symbolic link to a file pcDir, ## path refers to a directory pcLinkToDir ## path refers to a symbolic link to a directory proc getCurrentCompilerExe*(): string {.compileTime.} = discard - ## This is `getAppFilename() <#getAppFilename>`_ at compile time. + ## This is `getAppFilename()`_ at compile time. ## ## Can be used to retrieve the currently executing ## Nim compiler from a Nim or nimscript program, or the nimble binary @@ -2291,10 +2288,10 @@ iterator walkDir*(dir: string; relative = false, checkDir = false): assert collect(for k in walkDir("dirA"): k.path).join(" ") == "dirA/dirB dirA/dirC dirA/fileA2.txt dirA/fileA1.txt" ## See also: - ## * `walkPattern iterator <#walkPattern.i,string>`_ - ## * `walkFiles iterator <#walkFiles.i,string>`_ - ## * `walkDirs iterator <#walkDirs.i,string>`_ - ## * `walkDirRec iterator <#walkDirRec.i,string>`_ + ## * `walkPattern iterator`_ + ## * `walkFiles iterator`_ + ## * `walkDirs iterator`_ + ## * `walkDirRec iterator`_ when nimvm: for k, v in items(staticWalkDir(dir, relative)): @@ -2401,10 +2398,10 @@ iterator walkDirRec*(dir: string, ## ## ## See also: - ## * `walkPattern iterator <#walkPattern.i,string>`_ - ## * `walkFiles iterator <#walkFiles.i,string>`_ - ## * `walkDirs iterator <#walkDirs.i,string>`_ - ## * `walkDir iterator <#walkDir.i,string>`_ + ## * `walkPattern iterator`_ + ## * `walkFiles iterator`_ + ## * `walkDirs iterator`_ + ## * `walkDir iterator`_ var stack = @[""] var checkDir = checkDir @@ -2444,13 +2441,13 @@ proc removeDir*(dir: string, checkDir = false) {.rtl, extern: "nos$1", tags: [ ## existed in the first place, unless `checkDir` = true. ## ## See also: - ## * `tryRemoveFile proc <#tryRemoveFile,string>`_ - ## * `removeFile proc <#removeFile,string>`_ - ## * `existsOrCreateDir proc <#existsOrCreateDir,string>`_ - ## * `createDir proc <#createDir,string>`_ - ## * `copyDir proc <#copyDir,string,string>`_ - ## * `copyDirWithPermissions proc <#copyDirWithPermissions,string,string>`_ - ## * `moveDir proc <#moveDir,string,string>`_ + ## * `tryRemoveFile proc`_ + ## * `removeFile proc`_ + ## * `existsOrCreateDir proc`_ + ## * `createDir proc`_ + ## * `copyDir proc`_ + ## * `copyDirWithPermissions proc`_ + ## * `moveDir proc`_ for kind, path in walkDir(dir, checkDir = checkDir): case kind of pcFile, pcLinkToFile, pcLinkToDir: removeFile(path) @@ -2512,11 +2509,11 @@ proc existsOrCreateDir*(dir: string): bool {.rtl, extern: "nos$1", ## Returns `true` if the directory already exists, and `false` otherwise. ## ## See also: - ## * `removeDir proc <#removeDir,string>`_ - ## * `createDir proc <#createDir,string>`_ - ## * `copyDir proc <#copyDir,string,string>`_ - ## * `copyDirWithPermissions proc <#copyDirWithPermissions,string,string>`_ - ## * `moveDir proc <#moveDir,string,string>`_ + ## * `removeDir proc`_ + ## * `createDir proc`_ + ## * `copyDir proc`_ + ## * `copyDirWithPermissions proc`_ + ## * `moveDir proc`_ result = not rawCreateDir(dir) if result: # path already exists - need to check that it is indeed a directory @@ -2534,11 +2531,11 @@ proc createDir*(dir: string) {.rtl, extern: "nos$1", ## most usages this does not indicate an error. ## ## See also: - ## * `removeDir proc <#removeDir,string>`_ - ## * `existsOrCreateDir proc <#existsOrCreateDir,string>`_ - ## * `copyDir proc <#copyDir,string,string>`_ - ## * `copyDirWithPermissions proc <#copyDirWithPermissions,string,string>`_ - ## * `moveDir proc <#moveDir,string,string>`_ + ## * `removeDir proc`_ + ## * `existsOrCreateDir proc`_ + ## * `copyDir proc`_ + ## * `copyDirWithPermissions proc`_ + ## * `moveDir proc`_ var omitNext = false when doslikeFileSystem: omitNext = isAbsolute(dir) @@ -2568,17 +2565,17 @@ proc copyDir*(source, dest: string) {.rtl, extern: "nos$1", ## ## On other platforms created files and directories will inherit the ## default permissions of a newly created file/directory for the user. - ## Use `copyDirWithPermissions proc <#copyDirWithPermissions,string,string>`_ + ## Use `copyDirWithPermissions proc`_ ## to preserve attributes recursively on these platforms. ## ## See also: - ## * `copyDirWithPermissions proc <#copyDirWithPermissions,string,string>`_ - ## * `copyFile proc <#copyFile,string,string>`_ - ## * `copyFileWithPermissions proc <#copyFileWithPermissions,string,string>`_ - ## * `removeDir proc <#removeDir,string>`_ - ## * `existsOrCreateDir proc <#existsOrCreateDir,string>`_ - ## * `createDir proc <#createDir,string>`_ - ## * `moveDir proc <#moveDir,string,string>`_ + ## * `copyDirWithPermissions proc`_ + ## * `copyFile proc`_ + ## * `copyFileWithPermissions proc`_ + ## * `removeDir proc`_ + ## * `existsOrCreateDir proc`_ + ## * `createDir proc`_ + ## * `moveDir proc`_ createDir(dest) for kind, path in walkDir(source): var noSource = splitPath(path).tail @@ -2596,12 +2593,12 @@ proc moveDir*(source, dest: string) {.tags: [ReadIOEffect, WriteIOEffect], noWei ## If this fails, `OSError` is raised. ## ## See also: - ## * `moveFile proc <#moveFile,string,string>`_ - ## * `copyDir proc <#copyDir,string,string>`_ - ## * `copyDirWithPermissions proc <#copyDirWithPermissions,string,string>`_ - ## * `removeDir proc <#removeDir,string>`_ - ## * `existsOrCreateDir proc <#existsOrCreateDir,string>`_ - ## * `createDir proc <#createDir,string>`_ + ## * `moveFile proc`_ + ## * `copyDir proc`_ + ## * `copyDirWithPermissions proc`_ + ## * `removeDir proc`_ + ## * `existsOrCreateDir proc`_ + ## * `createDir proc`_ if not tryMoveFSObject(source, dest, isDir = true): # Fallback to copy & del copyDir(source, dest) @@ -2615,7 +2612,7 @@ proc createHardlink*(src, dest: string) {.noWeirdTarget.} = ## root users (administrators). ## ## See also: - ## * `createSymlink proc <#createSymlink,string,string>`_ + ## * `createSymlink proc`_ when defined(windows): when useWinUnicode: var wSrc = newWideCString(src) @@ -2638,13 +2635,12 @@ proc copyFileWithPermissions*(source, dest: string, ## if `source` is a symlink, copies the file symlink points to. `options` is ## ignored on Windows: symlinks are skipped. ## - ## This is a wrapper proc around `copyFile <#copyFile,string,string>`_, - ## `getFilePermissions <#getFilePermissions,string>`_ and - ## `setFilePermissions<#setFilePermissions,string,set[FilePermission]>`_ + ## This is a wrapper proc around `copyFile`_, + ## `getFilePermissions`_ and `setFilePermissions`_ ## procs on non-Windows platforms. ## - ## On Windows this proc is just a wrapper for `copyFile proc - ## <#copyFile,string,string>`_ since that proc already copies attributes. + ## On Windows this proc is just a wrapper for `copyFile proc`_ since + ## that proc already copies attributes. ## ## On non-Windows systems permissions are copied after the file itself has ## been copied, which won't happen atomically and could lead to a race @@ -2653,13 +2649,13 @@ proc copyFileWithPermissions*(source, dest: string, ## `OSError`. ## ## See also: - ## * `CopyFlag enum <#CopyFlag>`_ - ## * `copyFile proc <#copyFile,string,string>`_ - ## * `copyDir proc <#copyDir,string,string>`_ - ## * `tryRemoveFile proc <#tryRemoveFile,string>`_ - ## * `removeFile proc <#removeFile,string>`_ - ## * `moveFile proc <#moveFile,string,string>`_ - ## * `copyDirWithPermissions proc <#copyDirWithPermissions,string,string>`_ + ## * `CopyFlag enum`_ + ## * `copyFile proc`_ + ## * `copyDir proc`_ + ## * `tryRemoveFile proc`_ + ## * `removeFile proc`_ + ## * `moveFile proc`_ + ## * `copyDirWithPermissions proc`_ copyFile(source, dest, options) when not defined(windows): try: @@ -2678,13 +2674,12 @@ proc copyDirWithPermissions*(source, dest: string, ## On non-Windows OSes, symlinks are copied as symlinks. On Windows, symlinks ## are skipped. ## - ## If this fails, `OSError` is raised. This is a wrapper proc around `copyDir - ## <#copyDir,string,string>`_ and `copyFileWithPermissions - ## <#copyFileWithPermissions,string,string>`_ procs + ## If this fails, `OSError` is raised. This is a wrapper proc around + ## `copyDir`_ and `copyFileWithPermissions`_ procs ## on non-Windows platforms. ## - ## On Windows this proc is just a wrapper for `copyDir proc - ## <#copyDir,string,string>`_ since that proc already copies attributes. + ## On Windows this proc is just a wrapper for `copyDir proc`_ since + ## that proc already copies attributes. ## ## On non-Windows systems permissions are copied after the file or directory ## itself has been copied, which won't happen atomically and could lead to a @@ -2693,13 +2688,13 @@ proc copyDirWithPermissions*(source, dest: string, ## `OSError`. ## ## See also: - ## * `copyDir proc <#copyDir,string,string>`_ - ## * `copyFile proc <#copyFile,string,string>`_ - ## * `copyFileWithPermissions proc <#copyFileWithPermissions,string,string>`_ - ## * `removeDir proc <#removeDir,string>`_ - ## * `moveDir proc <#moveDir,string,string>`_ - ## * `existsOrCreateDir proc <#existsOrCreateDir,string>`_ - ## * `createDir proc <#createDir,string>`_ + ## * `copyDir proc`_ + ## * `copyFile proc`_ + ## * `copyFileWithPermissions proc`_ + ## * `removeDir proc`_ + ## * `moveDir proc`_ + ## * `existsOrCreateDir proc`_ + ## * `createDir proc`_ createDir(dest) when not defined(windows): try: @@ -2769,9 +2764,9 @@ proc parseCmdLine*(c: string): seq[string] {. ## ## See also: ## * `parseopt module `_ - ## * `paramCount proc <#paramCount>`_ - ## * `paramStr proc <#paramStr,int>`_ - ## * `commandLineParams proc <#commandLineParams>`_ + ## * `paramCount proc`_ + ## * `paramStr proc`_ + ## * `commandLineParams proc`_ result = @[] var i = 0 @@ -2838,9 +2833,8 @@ when defined(nimdoc): ## ## Unlike `argc`:idx: in C, if your binary was called without parameters this ## will return zero. - ## You can query each individual parameter with `paramStr proc <#paramStr,int>`_ - ## or retrieve all of them in one go with `commandLineParams proc - ## <#commandLineParams>`_. + ## You can query each individual parameter with `paramStr proc`_ + ## or retrieve all of them in one go with `commandLineParams proc`_. ## ## **Availability**: When generating a dynamic library (see `--app:lib`) on ## Posix this proc is not defined. @@ -2848,9 +2842,9 @@ when defined(nimdoc): ## ## See also: ## * `parseopt module `_ - ## * `parseCmdLine proc <#parseCmdLine,string>`_ - ## * `paramStr proc <#paramStr,int>`_ - ## * `commandLineParams proc <#commandLineParams>`_ + ## * `parseCmdLine proc`_ + ## * `paramStr proc`_ + ## * `commandLineParams proc`_ ## ## **Examples:** ## @@ -2865,13 +2859,13 @@ when defined(nimdoc): ## ## `i` should be in the range `1..paramCount()`, the `IndexDefect` ## exception will be raised for invalid values. Instead of iterating - ## over `paramCount() <#paramCount>`_ with this proc you can - ## call the convenience `commandLineParams() <#commandLineParams>`_. + ## over `paramCount()`_ with this proc you can + ## call the convenience `commandLineParams()`_. ## ## Similarly to `argv`:idx: in C, ## it is possible to call `paramStr(0)` but this will return OS specific ## contents (usually the name of the invoked executable). You should avoid - ## this and call `getAppFilename() <#getAppFilename>`_ instead. + ## this and call `getAppFilename()`_ instead. ## ## **Availability**: When generating a dynamic library (see `--app:lib`) on ## Posix this proc is not defined. @@ -2879,10 +2873,10 @@ when defined(nimdoc): ## ## See also: ## * `parseopt module `_ - ## * `parseCmdLine proc <#parseCmdLine,string>`_ - ## * `paramCount proc <#paramCount>`_ - ## * `commandLineParams proc <#commandLineParams>`_ - ## * `getAppFilename proc <#getAppFilename>`_ + ## * `parseCmdLine proc`_ + ## * `paramCount proc`_ + ## * `commandLineParams proc`_ + ## * `getAppFilename proc`_ ## ## **Examples:** ## @@ -2971,7 +2965,7 @@ when declared(paramCount) or defined(nimdoc): ## Convenience proc which returns the command line parameters. ## ## This returns **only** the parameters. If you want to get the application - ## executable filename, call `getAppFilename() <#getAppFilename>`_. + ## executable filename, call `getAppFilename()`_. ## ## **Availability**: On Posix there is no portable way to get the command ## line from a DLL and thus the proc isn't defined in this environment. You @@ -2980,10 +2974,10 @@ when declared(paramCount) or defined(nimdoc): ## ## See also: ## * `parseopt module `_ - ## * `parseCmdLine proc <#parseCmdLine,string>`_ - ## * `paramCount proc <#paramCount>`_ - ## * `paramStr proc <#paramStr,int>`_ - ## * `getAppFilename proc <#getAppFilename>`_ + ## * `parseCmdLine proc`_ + ## * `paramCount proc`_ + ## * `paramStr proc`_ + ## * `getAppFilename proc`_ ## ## **Examples:** ## @@ -3133,8 +3127,8 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noW ## This proc will resolve symlinks. ## ## See also: - ## * `getAppDir proc <#getAppDir>`_ - ## * `getCurrentCompilerExe proc <#getCurrentCompilerExe>`_ + ## * `getAppDir proc`_ + ## * `getCurrentCompilerExe proc`_ # Linux: /proc//exe # Solaris: @@ -3198,7 +3192,7 @@ proc getAppDir*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noWeirdT ## Returns the directory of the application's executable. ## ## See also: - ## * `getAppFilename proc <#getAppFilename>`_ + ## * `getAppFilename proc`_ result = splitFile(getAppFilename()).dir proc sleep*(milsecs: int) {.rtl, extern: "nos$1", tags: [TimeEffect], noWeirdTarget.} = @@ -3242,9 +3236,9 @@ type ## Contains information associated with a file object. ## ## See also: - ## * `getFileInfo(handle) proc <#getFileInfo,FileHandle>`_ - ## * `getFileInfo(file) proc <#getFileInfo,File>`_ - ## * `getFileInfo(path) proc <#getFileInfo,string>`_ + ## * `getFileInfo(handle) proc`_ + ## * `getFileInfo(file) proc`_ + ## * `getFileInfo(path, followSymlink) proc`_ id*: tuple[device: DeviceId, file: FileId] ## Device and file id. kind*: PathComponent ## Kind of file object - directory, symlink, etc. size*: BiggestInt ## Size of file. @@ -3334,8 +3328,8 @@ proc getFileInfo*(handle: FileHandle): FileInfo {.noWeirdTarget.} = ## is invalid, `OSError` is raised. ## ## See also: - ## * `getFileInfo(file) proc <#getFileInfo,File>`_ - ## * `getFileInfo(path) proc <#getFileInfo,string>`_ + ## * `getFileInfo(file) proc`_ + ## * `getFileInfo(path, followSymlink) proc`_ # Done: ID, Kind, Size, Permissions, Link Count when defined(windows): @@ -3356,8 +3350,8 @@ proc getFileInfo*(file: File): FileInfo {.noWeirdTarget.} = ## Retrieves file information for the file object. ## ## See also: - ## * `getFileInfo(handle) proc <#getFileInfo,FileHandle>`_ - ## * `getFileInfo(path) proc <#getFileInfo,string>`_ + ## * `getFileInfo(handle) proc`_ + ## * `getFileInfo(path, followSymlink) proc`_ if file.isNil: raise newException(IOError, "File is nil") result = getFileInfo(file.getFileHandle()) @@ -3366,7 +3360,7 @@ proc getFileInfo*(path: string, followSymlink = true): FileInfo {.noWeirdTarget. ## Retrieves file information for the file object pointed to by `path`. ## ## Due to intrinsic differences between operating systems, the information - ## contained by the returned `FileInfo object <#FileInfo>`_ will be slightly + ## contained by the returned `FileInfo object`_ will be slightly ## different across platforms, and in some cases, incomplete or inaccurate. ## ## When `followSymlink` is true (default), symlinks are followed and the @@ -3378,8 +3372,8 @@ proc getFileInfo*(path: string, followSymlink = true): FileInfo {.noWeirdTarget. ## file information, `OSError` is raised. ## ## See also: - ## * `getFileInfo(handle) proc <#getFileInfo,FileHandle>`_ - ## * `getFileInfo(file) proc <#getFileInfo,File>`_ + ## * `getFileInfo(handle) proc`_ + ## * `getFileInfo(file) proc`_ when defined(windows): var handle = openHandle(path, followSymlink) @@ -3406,7 +3400,7 @@ proc sameFileContent*(path1, path2: string): bool {.rtl, extern: "nos$1", ## binary content. ## ## See also: - ## * `sameFile proc <#sameFile,string,string>`_ + ## * `sameFile proc`_ var a, b: File if not open(a, path1): return false diff --git a/tests/stdlib/tdochelpers.nim b/tests/stdlib/tdochelpers.nim index 5bcdf32d5..9d440a1d6 100644 --- a/tests/stdlib/tdochelpers.nim +++ b/tests/stdlib/tdochelpers.nim @@ -153,3 +153,9 @@ suite "Integration with Nim": outType: "T") check(input1.toLangSymbol == expected) check(input2.toLangSymbol == expected) + + test "type of type": + check ("`CopyFlag enum`_".rstParseTest.toLangSymbol == + LangSymbol(symKind: "type", + symTypeKind: "enum", + name: "Copyflag")) From 83a9c3ba31d180cd5e31026d8b7603bf7adea18c Mon Sep 17 00:00:00 2001 From: Zoom Date: Mon, 8 Nov 2021 18:47:20 +0000 Subject: [PATCH 2638/3768] Update readme.md: matrix links (#19109) Added links to the main #nim-lang room on Matrix and to the Nim matrix space. Added Matrix badge. --- readme.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/readme.md b/readme.md index af07c2a7b..7d45556db 100644 --- a/readme.md +++ b/readme.md @@ -11,6 +11,7 @@ the latest release, check out [Nim's website][nim-site] or [bleeding edge docs]( [![Join the IRC chat][badge-nim-irc]][nim-irc] [![Join the Discord server][badge-nim-discord]][nim-discord] [![Join the Gitter chat][badge-nim-gitter]][nim-gitter] +[![Join the Matrix room](https://img.shields.io/matrix/nim-lang:matrix.org?color=blue&style=flat&logo=matrix)](https://matrix.to/#/#nim-lang:matrix.org) [![Get help][badge-nim-forum-gethelp]][nim-forum] [![View Nim posts on Stack Overflow][badge-nim-stackoverflow]][nim-stackoverflow-newest] [![Follow @nim_lang on Twitter][badge-nim-twitter]][nim-twitter] @@ -22,6 +23,7 @@ the latest release, check out [Nim's website][nim-site] or [bleeding edge docs]( channels there are bridged to IRC. * [Gitter][nim-gitter] - an additional place to discuss Nim in real-time. There is a bridge between Gitter and the IRC channel. +* [Matrix][nim-matrix] - the main room to discuss Nim in real-time. [Matrix space][nim-matrix-space] contains a list of rooms, most of them are bridged to IRC. * [Telegram][nim-telegram] - an additional place to discuss Nim in real-time. There is the official Telegram channel. Not bridged to IRC. * [Stack Overflow][nim-stackoverflow] - a popular Q/A site for programming related @@ -213,6 +215,8 @@ Copyright © 2006-2021 Andreas Rumpf, all rights reserved. [nim-stackoverflow-newest]: https://stackoverflow.com/questions/tagged/nim-lang?sort=newest&pageSize=15 [nim-discord]: https://discord.gg/nim [nim-gitter]: https://gitter.im/nim-lang/Nim +[nim-matrix]: https://matrix.to/#/#nim-lang:matrix.org +[nim-matrix-space]: https://matrix.to/#/#nim:envs.net [nim-telegram]: https://t.me/nim_lang [nim-bountysource]: https://www.bountysource.com/teams/nim [nim-bitcoin]: https://blockchain.info/address/1BXfuKM2uvoD6mbx4g5xM3eQhLzkCK77tJ From b7c66ce860cfbadfa8ed60784fc387a0818e2e5d Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 9 Nov 2021 12:43:16 +0100 Subject: [PATCH 2639/3768] fixes #19013 [backport:1.6] (#19111) * fixes #19013 [backport:1.6] * added test case --- compiler/ast.nim | 8 ++++++++ compiler/isolation_check.nim | 17 ++++++++++++++++- compiler/varpartitions.nim | 7 ------- tests/isolate/tisolate2.nim | 22 ++++++++++++++++++++++ 4 files changed, 46 insertions(+), 8 deletions(-) create mode 100644 tests/isolate/tisolate2.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index abd2ff01e..9086860b9 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -2101,3 +2101,11 @@ proc skipAddr*(n: PNode): PNode {.inline.} = proc isNewStyleConcept*(n: PNode): bool {.inline.} = assert n.kind == nkTypeClassTy result = n[0].kind == nkEmpty + +const + nodesToIgnoreSet* = {nkNone..pred(nkSym), succ(nkSym)..nkNilLit, + nkTypeSection, nkProcDef, nkConverterDef, + nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo, + nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt, + nkExportStmt, nkPragma, nkCommentStmt, nkBreakState, + nkTypeOfExpr, nkMixinStmt, nkBindStmt} diff --git a/compiler/isolation_check.nim b/compiler/isolation_check.nim index 777e7f6ce..a8c5a3651 100644 --- a/compiler/isolation_check.nim +++ b/compiler/isolation_check.nim @@ -77,6 +77,17 @@ proc canAlias*(arg, ret: PType): bool = var marker = initIntSet() result = canAlias(arg, ret, marker) +proc containsVariable(n: PNode): bool = + case n.kind + of nodesToIgnoreSet: + result = false + of nkSym: + result = n.sym.kind in {skForVar, skParam, skVar, skLet, skConst, skResult, skTemp} + else: + for ch in n: + if containsVariable(ch): return true + result = false + proc checkIsolate*(n: PNode): bool = if types.containsTyRef(n.typ): # XXX Maybe require that 'n.typ' is acyclic. This is not much @@ -96,7 +107,11 @@ proc checkIsolate*(n: PNode): bool = else: let argType = n[i].typ if argType != nil and not isCompileTimeOnly(argType) and containsTyRef(argType): - if argType.canAlias(n.typ): + if argType.canAlias(n.typ) or containsVariable(n[i]): + # bug #19013: Alias information is not enough, we need to check for potential + # "overlaps". I claim the problem can only happen by reading again from a location + # that materialized which is only possible if a variable that contains a `ref` + # is involved. return false result = true of nkIfStmt, nkIfExpr: diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim index 709d00fa0..721de900e 100644 --- a/compiler/varpartitions.nim +++ b/compiler/varpartitions.nim @@ -647,13 +647,6 @@ proc deps(c: var Partitions; dest, src: PNode) = when explainCursors: echo "D not a cursor ", d.sym, " reassignedTo ", c.s[srcid].reassignedTo c.s[vid].flags.incl preventCursor -const - nodesToIgnoreSet = {nkNone..pred(nkSym), succ(nkSym)..nkNilLit, - nkTypeSection, nkProcDef, nkConverterDef, - nkMethodDef, nkIteratorDef, nkMacroDef, nkTemplateDef, nkLambda, nkDo, - nkFuncDef, nkConstSection, nkConstDef, nkIncludeStmt, nkImportStmt, - nkExportStmt, nkPragma, nkCommentStmt, nkBreakState, - nkTypeOfExpr, nkMixinStmt, nkBindStmt} proc potentialMutationViaArg(c: var Partitions; n: PNode; callee: PType) = if constParameters in c.goals and tfNoSideEffect in callee.flags: diff --git a/tests/isolate/tisolate2.nim b/tests/isolate/tisolate2.nim new file mode 100644 index 000000000..9bf92d82e --- /dev/null +++ b/tests/isolate/tisolate2.nim @@ -0,0 +1,22 @@ +discard """ + errormsg: "expression cannot be isolated: a_to_b(a)" + line: 22 +""" + +# bug #19013 +import std/isolation + +type Z = ref object + i: int + +type A = object + z: Z + +type B = object + z: Z + +func a_to_b(a: A): B = + result = B(z: a.z) + +let a = A(z: Z(i: 3)) +let b = isolate(a_to_b(a)) From 6ff61766daec10d4cf78488b38bbe05d034e3a1c Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 9 Nov 2021 18:59:59 +0100 Subject: [PATCH 2640/3768] fixes #19011 [backport:1.6] (#19114) --- compiler/parser.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/parser.nim b/compiler/parser.nim index 5e9a7424a..aedf62d54 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -1877,7 +1877,7 @@ proc parseEnum(p: var Parser): PNode = var symPragma = a var pragma: PNode - if p.tok.tokType == tkCurlyDotLe: + if (p.tok.indent < 0 or p.tok.indent >= p.currInd) and p.tok.tokType == tkCurlyDotLe: pragma = optPragmas(p) symPragma = newNodeP(nkPragmaExpr, p) symPragma.add(a) From b0f4a9b845e4d483547f1a3818e3caf53cd90964 Mon Sep 17 00:00:00 2001 From: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Date: Tue, 9 Nov 2021 19:00:33 +0100 Subject: [PATCH 2641/3768] Add deprecation pragmas in lib/deprecated/pure (#19113) --- lib/deprecated/pure/LockFreeHash.nim | 6 ++++-- lib/deprecated/pure/events.nim | 2 ++ lib/deprecated/pure/ospaths.nim | 5 +++-- lib/deprecated/pure/securehash.nim | 5 ++--- lib/deprecated/pure/sharedstrings.nim | 2 ++ 5 files changed, 13 insertions(+), 7 deletions(-) diff --git a/lib/deprecated/pure/LockFreeHash.nim b/lib/deprecated/pure/LockFreeHash.nim index 97e1ef6ad..69e12226f 100644 --- a/lib/deprecated/pure/LockFreeHash.nim +++ b/lib/deprecated/pure/LockFreeHash.nim @@ -1,10 +1,12 @@ #nim c -t:-march=i686 --cpu:amd64 --threads:on -d:release lockfreehash.nim -import math, hashes - #------------------------------------------------------------------------------ ## Memory Utility Functions +{.deprecated.} + +import math, hashes + proc newHeap*[T](): ptr T = result = cast[ptr T](alloc0(sizeof(T))) diff --git a/lib/deprecated/pure/events.nim b/lib/deprecated/pure/events.nim index 218cb45d5..85fc349c2 100644 --- a/lib/deprecated/pure/events.nim +++ b/lib/deprecated/pure/events.nim @@ -36,6 +36,8 @@ ## myobj.SomeEvent.addHandler(handleevent) ## ee.emit(myobj.SomeEvent, genericargs) +{.deprecated.} + type EventArgs* = object of RootObj ## Base object for event arguments that are passed to callback functions. EventHandler* = tuple[name: string, handlers: seq[proc(e: EventArgs) {.closure.}]] ## An eventhandler for an event. diff --git a/lib/deprecated/pure/ospaths.nim b/lib/deprecated/pure/ospaths.nim index 6c7fe4fb3..e253a3e5a 100644 --- a/lib/deprecated/pure/ospaths.nim +++ b/lib/deprecated/pure/ospaths.nim @@ -7,8 +7,9 @@ # distribution, for details about the copyright. # -## This module is deprecated, `import os` instead. -{.deprecated: "import os.nim instead".} +## This module is deprecated, `import std/os` instead. + +{.deprecated: "use `std/os` instead".} import os export ReadEnvEffect, WriteEnvEffect, ReadDirEffect, WriteDirEffect, OSErrorCode, diff --git a/lib/deprecated/pure/securehash.nim b/lib/deprecated/pure/securehash.nim index 2f4530d88..ea7930f81 100644 --- a/lib/deprecated/pure/securehash.nim +++ b/lib/deprecated/pure/securehash.nim @@ -1,6 +1,5 @@ - - ## This module is a deprecated alias for the `sha1` module. -{.deprecated.} + +{.deprecated: "use `std/sha1` instead".} include "../std/sha1" diff --git a/lib/deprecated/pure/sharedstrings.nim b/lib/deprecated/pure/sharedstrings.nim index 4d4ae151b..99963e1f2 100644 --- a/lib/deprecated/pure/sharedstrings.nim +++ b/lib/deprecated/pure/sharedstrings.nim @@ -9,6 +9,8 @@ ## Shared string support for Nim. +{.deprecated.} + type UncheckedCharArray = UncheckedArray[char] From 46fb85542518c8594c24d168b025cc8a64b8fb3d Mon Sep 17 00:00:00 2001 From: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Date: Tue, 9 Nov 2021 19:00:43 +0100 Subject: [PATCH 2642/3768] Deprecate `std/sharedlist` and `std/sharedtables` (#19112) --- changelogs/changelog.md | 2 ++ changelogs/changelog_1_4_0.md | 2 +- doc/lib.rst | 6 ------ lib/pure/collections/lists.nim | 1 - lib/pure/collections/sharedlist.nim | 2 ++ lib/pure/collections/sharedtables.nim | 2 ++ lib/pure/collections/tables.nim | 1 - 7 files changed, 7 insertions(+), 9 deletions(-) diff --git a/changelogs/changelog.md b/changelogs/changelog.md index 475f568ec..d793c4313 100644 --- a/changelogs/changelog.md +++ b/changelogs/changelog.md @@ -4,6 +4,8 @@ ## Standard library additions and changes +- `std/sharedlist` and `std/sharedtables` are now deprecated, see RFC [#433](https://github.com/nim-lang/RFCs/issues/433). + ### New compile flag (`-d:nimNoGetRandom`) when building `std/sysrand` to remove dependency on linux `getrandom` syscall This compile flag only affects linux builds and is necessary if either compiling on a linux kernel version < 3.17, or if code built will be executing on kernel < 3.17. diff --git a/changelogs/changelog_1_4_0.md b/changelogs/changelog_1_4_0.md index 091048ad1..77f66ffde 100644 --- a/changelogs/changelog_1_4_0.md +++ b/changelogs/changelog_1_4_0.md @@ -282,7 +282,7 @@ - Removed the deprecated `asyncdispatch.newAsyncNativeSocket`. - Removed the deprecated `dom.releaseEvents` and `dom.captureEvents`. -- Removed `sharedlists.initSharedList`, was deprecated and produces undefined behaviour. +- Removed `sharedlist.initSharedList`, was deprecated and produces undefined behaviour. - There is a new experimental feature called "strictFuncs" which makes the definition of `.noSideEffect` stricter. [See here](manual_experimental.html#stricts-funcs) diff --git a/doc/lib.rst b/doc/lib.rst index 54728d681..881d857d3 100644 --- a/doc/lib.rst +++ b/doc/lib.rst @@ -134,12 +134,6 @@ Collections * `sets `_ Nim hash set support. -* `sharedlist `_ - Nim shared linked list support. Contains a shared singly-linked list. - -* `sharedtables `_ - Nim shared hash table support. Contains shared tables. - * `tables `_ Nim hash table support. Contains tables, ordered tables, and count tables. diff --git a/lib/pure/collections/lists.nim b/lib/pure/collections/lists.nim index af53bb68b..8b255fb60 100644 --- a/lib/pure/collections/lists.nim +++ b/lib/pure/collections/lists.nim @@ -56,7 +56,6 @@ runnableExamples: ## # See also ## * `deques module `_ for double-ended queues -## * `sharedlist module `_ for shared singly-linked lists import std/private/since diff --git a/lib/pure/collections/sharedlist.nim b/lib/pure/collections/sharedlist.nim index 73e147e05..e61883220 100644 --- a/lib/pure/collections/sharedlist.nim +++ b/lib/pure/collections/sharedlist.nim @@ -11,6 +11,8 @@ ## ## Unstable API. +{.deprecated.} + {.push stackTrace: off.} import diff --git a/lib/pure/collections/sharedtables.nim b/lib/pure/collections/sharedtables.nim index a85674df9..816ab49ab 100644 --- a/lib/pure/collections/sharedtables.nim +++ b/lib/pure/collections/sharedtables.nim @@ -14,6 +14,8 @@ ## ## Unstable API. +{.deprecated.} + import hashes, math, locks diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index 2695f3693..00f71ef1d 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -191,7 +191,6 @@ runnableExamples: ## ## * `json module`_ for table-like structure which allows ## heterogeneous members -## * `sharedtables module`_ for shared hash table support ## * `strtabs module`_ for efficient hash tables ## mapping from strings to strings ## * `hashes module`_ for helper functions for hashing From 997ccc588955dab5cdbfd4c0fc067d91ccc1a7c8 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Tue, 9 Nov 2021 21:01:47 +0300 Subject: [PATCH 2643/3768] fix nimindexterm in rst2tex/doc2tex [backport] (#19106) * fix nimindexterm (rst2tex/doc2tex) [backport] * Add support for indexing in rst --- config/nimdoc.tex.cfg | 12 +++++++++++- lib/packages/docutils/rstgen.nim | 2 +- tools/kochdocs.nim | 6 ++++++ 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/config/nimdoc.tex.cfg b/config/nimdoc.tex.cfg index d7ab652eb..457f452dd 100644 --- a/config/nimdoc.tex.cfg +++ b/config/nimdoc.tex.cfg @@ -63,6 +63,11 @@ doc.file = """ % % Compile it by: xelatex (up to 3 times to get labels generated) % ------- +% For example: +% xelatex file.tex +% xelatex file.tex +% makeindex file +% xelatex file.tex % \documentclass[a4paper,11pt]{article} \usepackage[a4paper,xetex,left=3cm,right=3cm,top=1.5cm,bottom=2cm]{geometry} @@ -105,7 +110,9 @@ doc.file = """ \usepackage{parskip} % paragraphs delimited by vertical space, no indent \usepackage{graphicx} -\newcommand{\nimindexterm}[2]{#2\label{#1}} +\usepackage{makeidx} +\newcommand{\nimindexterm}[2]{#2\index{#2}\label{#1}} +\makeindex \usepackage{dingbat} % for \carriagereturn, etc \usepackage{fvextra} % for code blocks (works better than original fancyvrb) @@ -249,5 +256,8 @@ doc.file = """ \maketitle $content + +\printindex + \end{document} """ diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index a556aa2e3..4cbd05379 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -408,7 +408,7 @@ proc renderIndexTerm*(d: PDoc, n: PRstNode, result: var string) = var term = "" renderAux(d, n, term) setIndexTerm(d, changeFileExt(extractFilename(d.filename), HtmlExt), id, term, d.currentSection) - dispA(d.target, result, "$2", "\\nimindexterm{$2}{$1}", + dispA(d.target, result, "$2", "\\nimindexterm{$1}{$2}", [id, term]) type diff --git a/tools/kochdocs.nim b/tools/kochdocs.nim index 42e648fb4..70ee5ad07 100644 --- a/tools/kochdocs.nim +++ b/tools/kochdocs.nim @@ -301,6 +301,12 @@ proc nim2pdf(src: string, dst: string, nimArgs: string) = # `>` should work on windows, if not, we can use `execCmdEx` let cmd = "xelatex -interaction=nonstopmode -output-directory=$# $# > $#" % [outDir.quoteShell, texFile.quoteShell, xelatexLog.quoteShell] exec(cmd) # on error, user can inspect `xelatexLog` + if i == 1: # build .ind file + var texFileBase = texFile + texFileBase.removeSuffix(".tex") + let cmd = "makeindex $# > $#" % [ + texFileBase.quoteShell, xelatexLog.quoteShell] + exec(cmd) moveFile(texFile.changeFileExt("pdf"), dst) proc buildPdfDoc*(nimArgs, destPath: string) = From 15157d06c3e0bba0c372a094917a13b916e15b54 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 9 Nov 2021 22:48:04 +0100 Subject: [PATCH 2644/3768] fixes #14470 [backport:1.2] (#19115) --- compiler/ccgtypes.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 23e1a5867..e7e099659 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -220,7 +220,8 @@ proc isInvalidReturnType(conf: ConfigRef; rettype: PType): bool = # such a poor programming language. # We exclude records with refs too. This enhances efficiency and # is necessary for proper code generation of assignments. - if rettype == nil: result = true + if rettype == nil or getSize(conf, rettype) > conf.target.floatSize*3: + result = true else: case mapType(conf, rettype, skResult) of ctArray: From a78ee8ae8453aabde04fa0103e02378efe870827 Mon Sep 17 00:00:00 2001 From: treeform Date: Tue, 9 Nov 2021 23:52:34 -0800 Subject: [PATCH 2645/3768] Call {.cursor.} a pragma. (#19116) * Call {.cursor.} a pragma. Its hard to find .curser annotation while googling because all other things like it are called pragmas. See https://nim-lang.org/docs/manual.html#pragmas Also the . in front of the name makes it hard to find and search for. Can we just call it cursor pragma? * Small fix for comment. --- doc/destructors.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/destructors.rst b/doc/destructors.rst index d6f240dee..83a50230b 100644 --- a/doc/destructors.rst +++ b/doc/destructors.rst @@ -555,14 +555,14 @@ for expressions of type `lent T` or of type `var T`. echo t[0] # accessor does not copy the element! -The .cursor annotation -====================== +The cursor pragma +================= Under the `--gc:arc|orc`:option: modes Nim's `ref` type is implemented via the same runtime "hooks" and thus via reference counting. This means that cyclic structures cannot be freed immediately (`--gc:orc`:option: ships with a cycle collector). -With the `.cursor` annotation one can break up cycles declaratively: +With the `cursor` pragma one can break up cycles declaratively: .. code-block:: nim @@ -575,7 +575,7 @@ But please notice that this is not C++'s weak_ptr, it means the right field is n involved in the reference counting, it is a raw pointer without runtime checks. Automatic reference counting also has the disadvantage that it introduces overhead -when iterating over linked structures. The `.cursor` annotation can also be used +when iterating over linked structures. The `cursor` pragma can also be used to avoid this overhead: .. code-block:: nim @@ -586,10 +586,10 @@ to avoid this overhead: it = it.next -In fact, `.cursor` more generally prevents object construction/destruction pairs +In fact, `cursor` more generally prevents object construction/destruction pairs and so can also be useful in other contexts. The alternative solution would be to use raw pointers (`ptr`) instead which is more cumbersome and also more dangerous -for Nim's evolution: Later on, the compiler can try to prove `.cursor` annotations +for Nim's evolution: Later on, the compiler can try to prove `cursor` pragmas to be safe, but for `ptr` the compiler has to remain silent about possible problems. @@ -597,7 +597,7 @@ problems. Cursor inference / copy elision =============================== -The current implementation also performs `.cursor` inference. Cursor inference is +The current implementation also performs `cursor` inference. Cursor inference is a form of copy elision. To see how and when we can do that, think about this question: In `dest = src` when @@ -612,7 +612,7 @@ indirections: .. code-block:: nim proc main(tab: Table[string, string]) = - let v = tab["key"] # inferred as .cursor because 'tab' is not mutated. + let v = tab["key"] # inferred as cursor because 'tab' is not mutated. # no copy into 'v', no destruction of 'v'. use(v) useItAgain(v) From 77b696c2c92b5f478526290c5e184a4c41060f7b Mon Sep 17 00:00:00 2001 From: Ryan Oldenburg Date: Wed, 10 Nov 2021 23:48:22 -0600 Subject: [PATCH 2646/3768] Remove tlsEmulation enabled from Windows + GCC config (#19119) [backport:1.6] This flag has a very significant performance impact on programs compiled with --threads:on. It is also apparently not needed anymore for standard circumstances. Can we remove the config? See https://github.com/nim-lang/Nim/issues/18146#issuecomment-876802676 for discussion and perf impact. [backport:1.6] --- config/nim.cfg | 3 --- 1 file changed, 3 deletions(-) diff --git a/config/nim.cfg b/config/nim.cfg index 3b964d124..86c6e2141 100644 --- a/config/nim.cfg +++ b/config/nim.cfg @@ -154,9 +154,6 @@ nimblepath="$home/.nimble/pkgs/" # Configuration for the GNU C/C++ compiler: @if windows: #gcc.path = r"$nim\dist\mingw\bin" - @if gcc or tcc: - tlsEmulation:on - @end @end gcc.maxerrorsimpl = "-fmax-errors=3" From 036d894e6a13cf9792c3250e34252a225c73e468 Mon Sep 17 00:00:00 2001 From: Federico Ceratto Date: Thu, 11 Nov 2021 07:41:21 +0000 Subject: [PATCH 2647/3768] Add security tip for setCookie (#19117) * Add security tip for setCookie * Update lib/pure/cookies.nim Co-authored-by: Dominik Picheta * Update lib/pure/cookies.nim Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Co-authored-by: Andreas Rumpf Co-authored-by: Dominik Picheta Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> --- lib/pure/cookies.nim | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/pure/cookies.nim b/lib/pure/cookies.nim index 25d701eb4..132f64637 100644 --- a/lib/pure/cookies.nim +++ b/lib/pure/cookies.nim @@ -50,6 +50,9 @@ proc setCookie*(key, value: string, domain = "", path = "", maxAge = none(int), sameSite = SameSite.Default): string = ## Creates a command in the format of ## `Set-Cookie: key=value; Domain=...; ...` + ## + + ## .. tip: Cookies can be vulnerable. Consider setting `secure=true`, `httpOnly=true` and `sameSite=Strict`. result = "" if not noName: result.add("Set-Cookie: ") result.add key & "=" & value From 7313b70a6af3fec6f367dd72bdd4f7651bf60d21 Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 11 Nov 2021 15:53:27 +0800 Subject: [PATCH 2648/3768] correct cookie docs (#19122) --- lib/pure/cookies.nim | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/pure/cookies.nim b/lib/pure/cookies.nim index 132f64637..7bf34f75b 100644 --- a/lib/pure/cookies.nim +++ b/lib/pure/cookies.nim @@ -51,8 +51,7 @@ proc setCookie*(key, value: string, domain = "", path = "", ## Creates a command in the format of ## `Set-Cookie: key=value; Domain=...; ...` ## - - ## .. tip: Cookies can be vulnerable. Consider setting `secure=true`, `httpOnly=true` and `sameSite=Strict`. + ## .. tip:: Cookies can be vulnerable. Consider setting `secure=true`, `httpOnly=true` and `sameSite=Strict`. result = "" if not noName: result.add("Set-Cookie: ") result.add key & "=" & value From cde83d90aa888ccc83bd78e6b6fb54d0ab0b2c26 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 11 Nov 2021 19:07:08 +0100 Subject: [PATCH 2649/3768] refactoring: orc can use getThreadId() (#19123) * refactoring: orc can use getThreadId() * progress --- lib/system/orc.nim | 7 +- lib/system/threadids.nim | 103 ++++++++++++++++++++++++++++++ lib/system/threadlocalstorage.nim | 3 - lib/system/threads.nim | 93 +-------------------------- 4 files changed, 110 insertions(+), 96 deletions(-) create mode 100644 lib/system/threadids.nim diff --git a/lib/system/orc.nim b/lib/system/orc.nim index 6273e1a21..f9f5cd81f 100644 --- a/lib/system/orc.nim +++ b/lib/system/orc.nim @@ -79,10 +79,12 @@ proc trace(s: Cell; desc: PNimTypeV2; j: var GcEnv) {.inline.} = var p = s +! sizeof(RefHeader) cast[TraceProc](desc.traceImpl)(p, addr(j)) +include threadids + when logOrc: proc writeCell(msg: cstring; s: Cell; desc: PNimTypeV2) = - cfprintf(cstderr, "%s %s %ld root index: %ld; RC: %ld; color: %ld\n", - msg, desc.name, s.refId, s.rootIdx, s.rc shr rcShift, s.color) + cfprintf(cstderr, "%s %s %ld root index: %ld; RC: %ld; color: %ld; thread: %ld\n", + msg, desc.name, s.refId, s.rootIdx, s.rc shr rcShift, s.color, getThreadId()) proc free(s: Cell; desc: PNimTypeV2) {.inline.} = when traceCollector: @@ -419,6 +421,7 @@ proc registerCycle(s: Cell; desc: PNimTypeV2) = proc GC_runOrc* = ## Forces a cycle collection pass. collectCycles() + orcAssert roots.len == 0, "roots not empty!" proc GC_enableOrc*() = ## Enables the cycle collector subsystem of `--gc:orc`. This is a `--gc:orc` diff --git a/lib/system/threadids.nim b/lib/system/threadids.nim new file mode 100644 index 000000000..3a6eadcbb --- /dev/null +++ b/lib/system/threadids.nim @@ -0,0 +1,103 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2020 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +# we need to cache current threadId to not perform syscall all the time +var threadId {.threadvar.}: int + +when defined(windows): + proc getCurrentThreadId(): int32 {. + stdcall, dynlib: "kernel32", importc: "GetCurrentThreadId".} + + proc getThreadId*(): int = + ## Gets the ID of the currently running thread. + if threadId == 0: + threadId = int(getCurrentThreadId()) + result = threadId + +elif defined(linux): + proc syscall(arg: clong): clong {.varargs, importc: "syscall", header: "".} + when defined(amd64): + const NR_gettid = clong(186) + else: + var NR_gettid {.importc: "__NR_gettid", header: "".}: clong + + proc getThreadId*(): int = + ## Gets the ID of the currently running thread. + if threadId == 0: + threadId = int(syscall(NR_gettid)) + result = threadId + +elif defined(dragonfly): + proc lwp_gettid(): int32 {.importc, header: "unistd.h".} + + proc getThreadId*(): int = + ## Gets the ID of the currently running thread. + if threadId == 0: + threadId = int(lwp_gettid()) + result = threadId + +elif defined(openbsd): + proc getthrid(): int32 {.importc: "getthrid", header: "".} + + proc getThreadId*(): int = + ## Gets the ID of the currently running thread. + if threadId == 0: + threadId = int(getthrid()) + result = threadId + +elif defined(netbsd): + proc lwp_self(): int32 {.importc: "_lwp_self", header: "".} + + proc getThreadId*(): int = + ## Gets the ID of the currently running thread. + if threadId == 0: + threadId = int(lwp_self()) + result = threadId + +elif defined(freebsd): + proc syscall(arg: cint, arg0: ptr cint): cint {.varargs, importc: "syscall", header: "".} + var SYS_thr_self {.importc:"SYS_thr_self", header:"".}: cint + + proc getThreadId*(): int = + ## Gets the ID of the currently running thread. + var tid = 0.cint + if threadId == 0: + discard syscall(SYS_thr_self, addr tid) + threadId = tid + result = threadId + +elif defined(macosx): + proc syscall(arg: cint): cint {.varargs, importc: "syscall", header: "".} + var SYS_thread_selfid {.importc:"SYS_thread_selfid", header:"".}: cint + + proc getThreadId*(): int = + ## Gets the ID of the currently running thread. + if threadId == 0: + threadId = int(syscall(SYS_thread_selfid)) + result = threadId + +elif defined(solaris): + type thread_t {.importc: "thread_t", header: "".} = distinct int + proc thr_self(): thread_t {.importc, header: "".} + + proc getThreadId*(): int = + ## Gets the ID of the currently running thread. + if threadId == 0: + threadId = int(thr_self()) + result = threadId + +elif defined(haiku): + type thr_id {.importc: "thread_id", header: "".} = distinct int32 + proc find_thread(name: cstring): thr_id {.importc, header: "".} + + proc getThreadId*(): int = + ## Gets the ID of the currently running thread. + if threadId == 0: + threadId = int(find_thread(nil)) + result = threadId diff --git a/lib/system/threadlocalstorage.nim b/lib/system/threadlocalstorage.nim index 30962ef77..e772f270c 100644 --- a/lib/system/threadlocalstorage.nim +++ b/lib/system/threadlocalstorage.nim @@ -29,9 +29,6 @@ when defined(windows): proc terminateThread(hThread: SysThread, dwExitCode: int32): int32 {. stdcall, dynlib: "kernel32", importc: "TerminateThread".} - proc getCurrentThreadId(): int32 {. - stdcall, dynlib: "kernel32", importc: "GetCurrentThreadId".} - type ThreadVarSlot = distinct int32 diff --git a/lib/system/threads.nim b/lib/system/threads.nim index a9006b0c5..a8a413bcb 100644 --- a/lib/system/threads.nim +++ b/lib/system/threads.nim @@ -343,94 +343,5 @@ else: proc createThread*(t: var Thread[void], tp: proc () {.thread, nimcall.}) = createThread[void](t, tp) -# we need to cache current threadId to not perform syscall all the time -var threadId {.threadvar.}: int - -when defined(windows): - proc getThreadId*(): int = - ## Gets the ID of the currently running thread. - if threadId == 0: - threadId = int(getCurrentThreadId()) - result = threadId - -elif defined(linux): - proc syscall(arg: clong): clong {.varargs, importc: "syscall", header: "".} - when defined(amd64): - const NR_gettid = clong(186) - else: - var NR_gettid {.importc: "__NR_gettid", header: "".}: clong - - proc getThreadId*(): int = - ## Gets the ID of the currently running thread. - if threadId == 0: - threadId = int(syscall(NR_gettid)) - result = threadId - -elif defined(dragonfly): - proc lwp_gettid(): int32 {.importc, header: "unistd.h".} - - proc getThreadId*(): int = - ## Gets the ID of the currently running thread. - if threadId == 0: - threadId = int(lwp_gettid()) - result = threadId - -elif defined(openbsd): - proc getthrid(): int32 {.importc: "getthrid", header: "".} - - proc getThreadId*(): int = - ## get the ID of the currently running thread. - if threadId == 0: - threadId = int(getthrid()) - result = threadId - -elif defined(netbsd): - proc lwp_self(): int32 {.importc: "_lwp_self", header: "".} - - proc getThreadId*(): int = - ## Gets the ID of the currently running thread. - if threadId == 0: - threadId = int(lwp_self()) - result = threadId - -elif defined(freebsd): - proc syscall(arg: cint, arg0: ptr cint): cint {.varargs, importc: "syscall", header: "".} - var SYS_thr_self {.importc:"SYS_thr_self", header:"".}: cint - - proc getThreadId*(): int = - ## Gets the ID of the currently running thread. - var tid = 0.cint - if threadId == 0: - discard syscall(SYS_thr_self, addr tid) - threadId = tid - result = threadId - -elif defined(macosx): - proc syscall(arg: cint): cint {.varargs, importc: "syscall", header: "".} - var SYS_thread_selfid {.importc:"SYS_thread_selfid", header:"".}: cint - - proc getThreadId*(): int = - ## Gets the ID of the currently running thread. - if threadId == 0: - threadId = int(syscall(SYS_thread_selfid)) - result = threadId - -elif defined(solaris): - type thread_t {.importc: "thread_t", header: "".} = distinct int - proc thr_self(): thread_t {.importc, header: "".} - - proc getThreadId*(): int = - ## Gets the ID of the currently running thread. - if threadId == 0: - threadId = int(thr_self()) - result = threadId - -elif defined(haiku): - type thr_id {.importc: "thread_id", header: "".} = distinct int32 - proc find_thread(name: cstring): thr_id {.importc, header: "".} - - proc getThreadId*(): int = - ## Gets the ID of the currently running thread. - if threadId == 0: - threadId = int(find_thread(nil)) - result = threadId +when not defined(gcOrc): + include threadids From 528ef6c218d90653f0f814c3dd256caa2cc389dc Mon Sep 17 00:00:00 2001 From: orthoplex <39635932+orthoplex@users.noreply.github.com> Date: Thu, 11 Nov 2021 20:01:51 +0100 Subject: [PATCH 2650/3768] fixed colorNames sorting mistake (#19125) [backport] --- lib/pure/colors.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/colors.nim b/lib/pure/colors.nim index 4a3f47e92..2193f69a6 100644 --- a/lib/pure/colors.nim +++ b/lib/pure/colors.nim @@ -353,8 +353,8 @@ const ("lightcoral", colLightCoral), ("lightcyan", colLightCyan), ("lightgoldenrodyellow", colLightGoldenRodYellow), - ("lightgrey", colLightGrey), ("lightgreen", colLightGreen), + ("lightgrey", colLightGrey), ("lightpink", colLightPink), ("lightsalmon", colLightSalmon), ("lightseagreen", colLightSeaGreen), From 3aaa12dbe5796cc55c6161e4d30a70a7f75cb3b5 Mon Sep 17 00:00:00 2001 From: flywind Date: Fri, 12 Nov 2021 14:59:48 +0800 Subject: [PATCH 2651/3768] update manual (#19130) [backport] --- doc/manual.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/manual.rst b/doc/manual.rst index 001402036..328035b6d 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -6721,11 +6721,11 @@ statement, as seen in stack backtraces: if not cond: # change run-time line information of the 'raise' statement: {.line: instantiationInfo().}: - raise newException(EAssertionFailed, msg) + raise newException(AssertionDefect, msg) If the `line` pragma is used with a parameter, the parameter needs be a `tuple[filename: string, line: int]`. If it is used without a parameter, -`system.InstantiationInfo()` is used. +`system.instantiationInfo()` is used. linearScanEnd pragma From f8b71f7075b68aefc3955388ad5ec3178aee7658 Mon Sep 17 00:00:00 2001 From: Jake Leahy Date: Fri, 12 Nov 2021 18:00:19 +1100 Subject: [PATCH 2652/3768] Fix type mismatch with future logging (#19131) --- lib/pure/asyncfutures.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/asyncfutures.nim b/lib/pure/asyncfutures.nim index 6906d03fd..4782f0a73 100644 --- a/lib/pure/asyncfutures.nim +++ b/lib/pure/asyncfutures.nim @@ -225,7 +225,7 @@ proc complete*[T](future: FutureVar[T], val: T) = fut.finished = true fut.value = val fut.callbacks.call() - when isFutureLoggingEnabled: logFutureFinish(future) + when isFutureLoggingEnabled: logFutureFinish(fut) proc fail*[T](future: Future[T], error: ref Exception) = ## Completes `future` with `error`. From c6fc3b2eae5aac68480a787b7fa3b81e3748bc2f Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 12 Nov 2021 11:19:24 +0100 Subject: [PATCH 2653/3768] fixes #19051 [backport:1.6] (#19133) --- compiler/sizealignoffsetimpl.nim | 19 +++++++++++++++++-- tests/ccgbugs/t5296.nim | 9 ++++++++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/compiler/sizealignoffsetimpl.nim b/compiler/sizealignoffsetimpl.nim index 44ba32579..c5cd3ba64 100644 --- a/compiler/sizealignoffsetimpl.nim +++ b/compiler/sizealignoffsetimpl.nim @@ -196,6 +196,11 @@ proc computeUnionObjectOffsetsFoldFunction(conf: ConfigRef; n: PNode; packed: bo accum.offset = szUnknownSize proc computeSizeAlign(conf: ConfigRef; typ: PType) = + template setSize(typ, s) = + typ.size = s + typ.align = s + typ.paddingAtEnd = 0 + ## computes and sets ``size`` and ``align`` members of ``typ`` assert typ != nil let hasSize = typ.size != szUncomputedSize @@ -258,14 +263,14 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) = of tyArray: computeSizeAlign(conf, typ[1]) - let elemSize = typ[1].size + let elemSize = typ[1].size let len = lengthOrd(conf, typ[0]) if elemSize < 0: typ.size = elemSize typ.align = int16(elemSize) elif len < 0: typ.size = szUnknownSize - typ.align = szUnknownSize + typ.align = szUnknownSize else: typ.size = toInt64Checked(len * int32(elemSize), szTooBigSize) typ.align = typ[1].align @@ -445,6 +450,16 @@ proc computeSizeAlign(conf: ConfigRef; typ: PType) = typ.size = szUnknownSize typ.align = szUnknownSize typ.paddingAtEnd = szUnknownSize + of tyInt, tyUInt: + setSize typ, conf.target.intSize.int16 + of tyBool, tyChar, tyUInt8, tyInt8: + setSize typ, 1 + of tyInt16, tyUInt16: + setSize typ, 2 + of tyInt32, tyUInt32: + setSize typ, 4 + of tyInt64, tyUInt64: + setSize typ, 8 else: typ.size = szUnknownSize typ.align = szUnknownSize diff --git a/tests/ccgbugs/t5296.nim b/tests/ccgbugs/t5296.nim index 990b4bee2..8fbed35c4 100644 --- a/tests/ccgbugs/t5296.nim +++ b/tests/ccgbugs/t5296.nim @@ -1,6 +1,7 @@ discard """ cmd: "nim c -d:release $file" -output: 1 +output: '''1 +-1''' """ proc bug() : void = @@ -12,3 +13,9 @@ proc bug() : void = echo x bug() + +# bug #19051 +type GInt[T] = int + +var a = 1 +echo -a From 0a1049881eaa08cb7d10194d9b446d8a9ca52122 Mon Sep 17 00:00:00 2001 From: Clay Sweetser Date: Fri, 12 Nov 2021 22:51:43 -0500 Subject: [PATCH 2654/3768] Merge file size fields correctly on Windows (#19141) * Merge file size fields correctly on Windows Merge file size fields correctly on Windows - Merge the two 32-bit file size fields from `BY_HANDLE_FILE_INFORMATION` correctly in `rawToFormalFileInfo`. - Fixes #19135 * Update os.nim --- lib/pure/os.nim | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 513e09b48..9b08fe2e1 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -3255,7 +3255,11 @@ template rawToFormalFileInfo(rawInfo, path, formalInfo): untyped = ## 'rawInfo' is either a 'BY_HANDLE_FILE_INFORMATION' structure on Windows, ## or a 'Stat' structure on posix when defined(windows): - template merge(a, b): untyped = a or (b shl 32) + template merge(a, b): untyped = + int64( + (uint64(cast[uint32](a))) or + (uint64(cast[uint32](b)) shl 32) + ) formalInfo.id.device = rawInfo.dwVolumeSerialNumber formalInfo.id.file = merge(rawInfo.nFileIndexLow, rawInfo.nFileIndexHigh) formalInfo.size = merge(rawInfo.nFileSizeLow, rawInfo.nFileSizeHigh) From ade85ee91fee849f4b036ed8e42e31b978144522 Mon Sep 17 00:00:00 2001 From: Christoph Krybus Date: Sat, 13 Nov 2021 04:51:58 +0100 Subject: [PATCH 2655/3768] Fix punycode.decode function (#19136) * Refactor: rename proc to func * Fix punycode.decode function This function could only properly decode punycodes containing a single encoded unicode character. As soon as there was more than one punycode character group to decode it produced invalid output - the number of characters was correct, but their position was not. * Update tpunycode.nim Co-authored-by: Clay Sweetser --- lib/pure/punycode.nim | 31 +++--- tests/stdlib/tpunycode.nim | 202 ++++++++++++++++++++++++++++++++++++- 2 files changed, 217 insertions(+), 16 deletions(-) diff --git a/lib/pure/punycode.nim b/lib/pure/punycode.nim index dfeeea35e..49e46496e 100644 --- a/lib/pure/punycode.nim +++ b/lib/pure/punycode.nim @@ -28,7 +28,7 @@ const type PunyError* = object of ValueError -proc decodeDigit(x: char): int {.raises: [PunyError].} = +func decodeDigit(x: char): int {.raises: [PunyError].} = if '0' <= x and x <= '9': result = ord(x) - (ord('0') - 26) elif 'A' <= x and x <= 'Z': @@ -38,7 +38,7 @@ proc decodeDigit(x: char): int {.raises: [PunyError].} = else: raise newException(PunyError, "Bad input") -proc encodeDigit(digit: int): Rune {.raises: [PunyError].} = +func encodeDigit(digit: int): Rune {.raises: [PunyError].} = if 0 <= digit and digit < 26: result = Rune(digit + ord('a')) elif 26 <= digit and digit < 36: @@ -46,10 +46,10 @@ proc encodeDigit(digit: int): Rune {.raises: [PunyError].} = else: raise newException(PunyError, "internal error in punycode encoding") -proc isBasic(c: char): bool = ord(c) < 0x80 -proc isBasic(r: Rune): bool = int(r) < 0x80 +func isBasic(c: char): bool = ord(c) < 0x80 +func isBasic(r: Rune): bool = int(r) < 0x80 -proc adapt(delta, numPoints: int, first: bool): int = +func adapt(delta, numPoints: int, first: bool): int = var d = if first: delta div Damp else: delta div 2 d += d div numPoints var k = 0 @@ -58,7 +58,7 @@ proc adapt(delta, numPoints: int, first: bool): int = k += Base result = k + (Base - TMin + 1) * d div (d + Skew) -proc encode*(prefix, s: string): string {.raises: [PunyError].} = +func encode*(prefix, s: string): string {.raises: [PunyError].} = ## Encode a string that may contain Unicode. ## Prepend `prefix` to the result result = prefix @@ -114,18 +114,18 @@ proc encode*(prefix, s: string): string {.raises: [PunyError].} = inc d inc n -proc encode*(s: string): string {.raises: [PunyError].} = +func encode*(s: string): string {.raises: [PunyError].} = ## Encode a string that may contain Unicode. Prefix is empty. result = encode("", s) -proc decode*(encoded: string): string {.raises: [PunyError].} = +func decode*(encoded: string): string {.raises: [PunyError].} = ## Decode a Punycode-encoded string var n = InitialN i = 0 bias = InitialBias var d = rfind(encoded, Delimiter) - result = "" + var output: seq[Rune] if d > 0: # found Delimiter @@ -133,7 +133,7 @@ proc decode*(encoded: string): string {.raises: [PunyError].} = var c = encoded[j] # char if not c.isBasic: raise newException(PunyError, "Encoded contains a non-basic char") - result.add(c) # add the character + output.add(Rune(c)) # add the character inc d else: d = 0 # set to first index @@ -161,16 +161,17 @@ proc decode*(encoded: string): string {.raises: [PunyError].} = break w *= Base - t k += Base - bias = adapt(i - oldi, runeLen(result) + 1, oldi == 0) + bias = adapt(i - oldi, len(output) + 1, oldi == 0) - if i div (runeLen(result) + 1) > high(int32) - n: + if i div (len(output) + 1) > high(int32) - n: raise newException(PunyError, "Value too large") - n += i div (runeLen(result) + 1) - i = i mod (runeLen(result) + 1) - insert(result, $Rune(n), i) + n += i div (len(output) + 1) + i = i mod (len(output) + 1) + insert(output, Rune(n), i) inc i + result = $output runnableExamples: static: diff --git a/tests/stdlib/tpunycode.nim b/tests/stdlib/tpunycode.nim index 596c5ef63..bb2b4beb4 100644 --- a/tests/stdlib/tpunycode.nim +++ b/tests/stdlib/tpunycode.nim @@ -1,5 +1,205 @@ -import punycode +import punycode, std/unicode doAssert(decode(encode("", "bücher")) == "bücher") doAssert(decode(encode("münchen")) == "münchen") doAssert encode("xn--", "münchen") == "xn--mnchen-3ya" + +# source: https://datatracker.ietf.org/doc/html/rfc3492.html#section-7 +let punycode_testcases = [ + # (A) Arabic (Egyptian): + ( + input: + "\u0644\u064A\u0647\u0645\u0627\u0628\u062A\u0643\u0644" & + "\u0645\u0648\u0634\u0639\u0631\u0628\u064A\u061F", + output: + "egbpdaj6bu4bxfgehfvwxn" + ), + + # (B) Chinese (simplified): + ( + input: + "\u4ED6\u4EEC\u4E3A\u4EC0\u4E48\u4E0D\u8BF4\u4E2D\u6587", + output: + "ihqwcrb4cv8a8dqg056pqjye" + ), + + # (C) Chinese (traditional): + ( + input: + "\u4ED6\u5011\u7232\u4EC0\u9EBD\u4E0D\u8AAA\u4E2D\u6587", + output: + "ihqwctvzc91f659drss3x8bo0yb" + ), + + # (D) Czech: Proprostnemluvesky + ( + input: + "\u0050\u0072\u006F\u010D\u0070\u0072\u006F\u0073\u0074" & + "\u011B\u006E\u0065\u006D\u006C\u0075\u0076\u00ED\u010D" & + "\u0065\u0073\u006B\u0079", + output: + "Proprostnemluvesky-uyb24dma41a" + ), + + # (E) Hebrew: + ( + input: + "\u05DC\u05DE\u05D4\u05D4\u05DD\u05E4\u05E9\u05D5\u05D8" & + "\u05DC\u05D0\u05DE\u05D3\u05D1\u05E8\u05D9\u05DD\u05E2" & + "\u05D1\u05E8\u05D9\u05EA", + output: + "4dbcagdahymbxekheh6e0a7fei0b" + ), + + # (F) Hindi (Devanagari): + ( + input: + "\u092F\u0939\u0932\u094B\u0917\u0939\u093F\u0928\u094D" & + "\u0926\u0940\u0915\u094D\u092F\u094B\u0902\u0928\u0939" & + "\u0940\u0902\u092C\u094B\u0932\u0938\u0915\u0924\u0947" & + "\u0939\u0948\u0902", + output: + "i1baa7eci9glrd9b2ae1bj0hfcgg6iyaf8o0a1dig0cd" + ), + + # (G) Japanese (kanji and hiragana): + ( + input: + "\u306A\u305C\u307F\u3093\u306A\u65E5\u672C\u8A9E\u3092" & + "\u8A71\u3057\u3066\u304F\u308C\u306A\u3044\u306E\u304B", + output: + "n8jok5ay5dzabd5bym9f0cm5685rrjetr6pdxa" + ), + + # (H) Korean (Hangul syllables): + ( + input: + "\uC138\uACC4\uC758\uBAA8\uB4E0\uC0AC\uB78C\uB4E4\uC774" & + "\uD55C\uAD6D\uC5B4\uB97C\uC774\uD574\uD55C\uB2E4\uBA74" & + "\uC5BC\uB9C8\uB098\uC88B\uC744\uAE4C", + output: + "989aomsvi5e83db1d2a355cv1e0vak1dwrv93d5xbh15a0dt30a5jpsd879ccm6fea98c" + ), + + # (I) Russian (Cyrillic): + ( + input: + "\u043F\u043E\u0447\u0435\u043C\u0443\u0436\u0435\u043E" & + "\u043D\u0438\u043D\u0435\u0433\u043E\u0432\u043E\u0440" & + "\u044F\u0442\u043F\u043E\u0440\u0443\u0441\u0441\u043A" & + "\u0438", + output: + "b1abfaaepdrnnbgefbaDotcwatmq2g4l" + ), + + # (J) Spanish: PorqunopuedensimplementehablarenEspaol + ( + input: + "\u0050\u006F\u0072\u0071\u0075\u00E9\u006E\u006F\u0070" & + "\u0075\u0065\u0064\u0065\u006E\u0073\u0069\u006D\u0070" & + "\u006C\u0065\u006D\u0065\u006E\u0074\u0065\u0068\u0061" & + "\u0062\u006C\u0061\u0072\u0065\u006E\u0045\u0073\u0070" & + "\u0061\u00F1\u006F\u006C", + output: + "PorqunopuedensimplementehablarenEspaol-fmd56a" + ), + + # (K) Vietnamese: + # Tisaohkhngthch\ + # nitingVit + ( + input: + "\u0054\u1EA1\u0069\u0073\u0061\u006F\u0068\u1ECD\u006B" & + "\u0068\u00F4\u006E\u0067\u0074\u0068\u1EC3\u0063\u0068" & + "\u1EC9\u006E\u00F3\u0069\u0074\u0069\u1EBF\u006E\u0067" & + "\u0056\u0069\u1EC7\u0074", + output: + "TisaohkhngthchnitingVit-kjcr8268qyxafd2f1b9g" + ), + + # (L) 3B + ( + input: + "\u0033\u5E74\u0042\u7D44\u91D1\u516B\u5148\u751F", + output: + "3B-ww4c5e180e575a65lsy2b" + ), + + # (M) -with-SUPER-MONKEYS + ( + input: + "\u5B89\u5BA4\u5948\u7F8E\u6075\u002D\u0077\u0069\u0074" & + "\u0068\u002D\u0053\u0055\u0050\u0045\u0052\u002D\u004D" & + "\u004F\u004E\u004B\u0045\u0059\u0053", + output: + "-with-SUPER-MONKEYS-pc58ag80a8qai00g7n9n" + ), + + # (N) Hello-Another-Way- + ( + input: + "\u0048\u0065\u006C\u006C\u006F\u002D\u0041\u006E\u006F" & + "\u0074\u0068\u0065\u0072\u002D\u0057\u0061\u0079\u002D" & + "\u305D\u308C\u305E\u308C\u306E\u5834\u6240", + output: + "Hello-Another-Way--fc4qua05auwb3674vfr0b" + ), + + # (O) 2 + ( + input: + "\u3072\u3068\u3064\u5C4B\u6839\u306E\u4E0B\u0032", + output: + "2-u9tlzr9756bt3uc0v" + ), + + # (P) MajiKoi5 + ( + input: + "\u004D\u0061\u006A\u0069\u3067\u004B\u006F\u0069\u3059" & + "\u308B\u0035\u79D2\u524D", + output: + "MajiKoi5-783gue6qz075azm5e" + ), + + # (Q) de + ( + input: + "\u30D1\u30D5\u30A3\u30FC\u0064\u0065\u30EB\u30F3\u30D0", + output: + "de-jg4avhby1noc0d" + ), + + # (R) + ( + input: + "\u305D\u306E\u30B9\u30D4\u30FC\u30C9\u3067", + output: + "d9juau41awczczp" + ), + + # (S) -> $1.00 <- + ( + input: + "\u002D\u003E\u0020\u0024\u0031\u002E\u0030\u0030\u0020" & + "\u003C\u002D", + output: + "-> $1.00 <--" + ) +] + +block encodeTests: + for (uni, puny) in punycode_testcases: + # Need to convert both strings to lower case, since + # some of the extended encodings use upper case, but our + # code produces only lower case. Converting just puny to + # lower is also insufficient, since some of the input characters + # are upper case. + doAssert encode(uni).toLower() == puny.toLower() + +block decodeTests: + for (uni, puny) in punycode_testcases: + doAssert decode(puny) == uni + +block decodeInvalidTest: + doAssertRaises(PunyError): discard decode("xn--w&") From 270a5a372ddcb8fa97eacdf926dd90bfe189298f Mon Sep 17 00:00:00 2001 From: Anuken Date: Sat, 13 Nov 2021 19:09:15 -0500 Subject: [PATCH 2656/3768] Fix undeclared 'SYS_getrandom' on emscripten (#19144) --- lib/std/sysrand.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index b35f24a72..6b354e5a3 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -164,7 +164,7 @@ elif defined(windows): result = randomBytes(addr dest[0], size) -elif defined(linux) and not defined(nimNoGetRandom): +elif defined(linux) and not defined(nimNoGetRandom) and not defined(emscripten): # TODO using let, pending bootstrap >= 1.4.0 var SYS_getrandom {.importc: "SYS_getrandom", header: "".}: clong const syscallHeader = """#include From bec490016548e73f01317736ff8863f9ce6aab31 Mon Sep 17 00:00:00 2001 From: flywind Date: Sun, 14 Nov 2021 19:46:21 +0800 Subject: [PATCH 2657/3768] wrong spaces (3 => 2) (#19145) --- lib/system/threads.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/system/threads.nim b/lib/system/threads.nim index a8a413bcb..0e5198035 100644 --- a/lib/system/threads.nim +++ b/lib/system/threads.nim @@ -190,7 +190,7 @@ when defined(windows): threadProcWrapperBody(closure) # implicitly return 0 elif defined(genode): - proc threadProcWrapper[TArg](closure: pointer) {.noconv.} = + proc threadProcWrapper[TArg](closure: pointer) {.noconv.} = threadProcWrapperBody(closure) else: proc threadProcWrapper[TArg](closure: pointer): pointer {.noconv.} = From 6976d18519d102c9cc857a84a4b10b009c1e1a38 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Sun, 14 Nov 2021 03:49:30 -0800 Subject: [PATCH 2658/3768] Implement zephyr urandom and monotime (#19142) * implement urandom for Zephyr * add monotime on zephyr Co-authored-by: Jaremy Creechley --- lib/std/monotimes.nim | 7 +++++++ lib/std/sysrand.nim | 12 +++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/lib/std/monotimes.nim b/lib/std/monotimes.nim index bd078d7c0..5c67a5d4c 100644 --- a/lib/std/monotimes.nim +++ b/lib/std/monotimes.nim @@ -76,6 +76,10 @@ when defined(js): elif defined(posix) and not defined(osx): import posix +when defined(zephyr): + proc k_uptime_ticks(): int64 {.importc: "k_uptime_ticks", header: "".} + proc k_ticks_to_ns_floor64(ticks: int64): int64 {.importc: "k_ticks_to_ns_floor64", header: "".} + elif defined(windows): proc QueryPerformanceCounter(res: var uint64) {. importc: "QueryPerformanceCounter", stdcall, dynlib: "kernel32".} @@ -98,6 +102,9 @@ proc getMonoTime*(): MonoTime {.tags: [TimeEffect].} = mach_timebase_info(machAbsoluteTimeFreq) result = MonoTime(ticks: ticks * machAbsoluteTimeFreq.numer div machAbsoluteTimeFreq.denom) + elif defined(zephyr): + let ticks = k_ticks_to_ns_floor64(k_uptime_ticks()) + result = MonoTime(ticks: ticks) elif defined(posix): var ts: Timespec discard clock_gettime(CLOCK_MONOTONIC, ts) diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index 6b354e5a3..1b7b2c024 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -63,7 +63,7 @@ when defined(posix): import posix const - batchImplOS = defined(freebsd) or defined(openbsd) or (defined(macosx) and not defined(ios)) + batchImplOS = defined(freebsd) or defined(openbsd) or defined(zephyr) or (defined(macosx) and not defined(ios)) batchSize {.used.} = 256 when batchImplOS: @@ -207,6 +207,16 @@ elif defined(openbsd): proc getRandomImpl(p: pointer, size: int): int {.inline.} = result = getentropy(p, cint(size)).int +elif defined(zephyr): + proc sys_csrand_get(dst: pointer, length: csize_t): cint {.importc: "sys_csrand_get", header: "".} + # Fill the destination buffer with cryptographically secure + # random data values + # + + proc getRandomImpl(p: pointer, size: int): int {.inline.} = + # 0 if success, -EIO if entropy reseed error + result = sys_csrand_get(p, csize_t(size)).int + elif defined(freebsd): type cssize_t {.importc: "ssize_t", header: "".} = int From 309ec7167e6ea17112901533539be0a1884c8b65 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 16 Nov 2021 15:49:04 +0100 Subject: [PATCH 2659/3768] fixes .raises inference for newSeq builtin under --gc:orc [backport] (#19158) --- lib/system/seqs_v2.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/system/seqs_v2.nim b/lib/system/seqs_v2.nim index 0ac51109e..0c487b31d 100644 --- a/lib/system/seqs_v2.nim +++ b/lib/system/seqs_v2.nim @@ -73,7 +73,7 @@ proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize, elemAlign: int): poin q.cap = newCap result = q -proc shrink*[T](x: var seq[T]; newLen: Natural) {.tags: [].} = +proc shrink*[T](x: var seq[T]; newLen: Natural) {.tags: [], raises: [].} = when nimvm: setLen(x, newLen) else: From 92d6fb86c66ceda154972314ba695cea4aac9301 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Tue, 16 Nov 2021 11:30:07 -0800 Subject: [PATCH 2660/3768] Enable customizing PageShift to set PageSize for embedded targets (#19129) * Enable customizing PageSize (via PageShift). This enables adjusting PageSize for embedded targets without abusing cpu16. * copy nimPageXYZ settings for mmpaptest * add docs for Nim manual * add docs for Nim manual * docs tweaks Co-authored-by: Jaremy Creechley --- doc/nimc.rst | 24 ++++++++++++++++++++++++ lib/system/bitmasks.nim | 10 +++++++--- tests/mmaptest.nim | 7 +++++-- 3 files changed, 36 insertions(+), 5 deletions(-) diff --git a/doc/nimc.rst b/doc/nimc.rst index 98b754729..d35dad6c2 100644 --- a/doc/nimc.rst +++ b/doc/nimc.rst @@ -656,6 +656,30 @@ is not available but C's `malloc` is. You can use the `nimAllocPagesViaMalloc` define to use `malloc` instead of `mmap`. `nimAllocPagesViaMalloc` is currently only supported with `--gc:arc` or `--gc:orc`. (Since version 1.6) +nimPage256 / nimPage512 / nimPage1k +=================================== + +Adjust the page size for Nim's GC allocator. This enables using +`nimAllocPagesViaMalloc` on devices with less RAM. The default +page size requires too much RAM to work. + +Recommended settings: + +- < 32 kB of RAM use `nimPage256` + +- < 512 kB of RAM use `nimPage512` + +- < 2 MB of RAM use `nimPage1k` + +Initial testing hasn't shown much difference between 512B or 1kB page sizes +in terms of performance or latency. Using `nimPages256` will limit the +total amount of allocatable RAM. + +nimMemAlignTiny +=============== + +Sets `MemAlign` to `4` bytes which reduces the memory alignment +to better match some embedded devices. Nim for realtime systems ======================== diff --git a/lib/system/bitmasks.nim b/lib/system/bitmasks.nim index caf86568a..9cb57bc45 100644 --- a/lib/system/bitmasks.nim +++ b/lib/system/bitmasks.nim @@ -10,14 +10,18 @@ # Page size of the system; in most cases 4096 bytes. For exotic OS or # CPU this needs to be changed: const - PageShift = when defined(cpu16): 8 else: 12 # \ - # my tests showed no improvements for using larger page sizes. + PageShift = when defined(nimPage256) or defined(cpu16): 8 + elif defined(nimPage512): 9 + elif defined(nimPage1k): 10 + else: 12 # \ # my tests showed no improvements for using larger page sizes. + PageSize = 1 shl PageShift PageMask = PageSize-1 MemAlign = # also minimal allocatable memory block - when defined(useMalloc): + when defined(nimMemAlignTiny): 4 + elif defined(useMalloc): when defined(amd64): 16 else: 8 else: 16 diff --git a/tests/mmaptest.nim b/tests/mmaptest.nim index 7a93cdd45..33010606f 100644 --- a/tests/mmaptest.nim +++ b/tests/mmaptest.nim @@ -19,8 +19,11 @@ proc `+!!`(p: pointer, size: int): pointer {.inline.} = result = cast[pointer](cast[int](p) + size) const - PageShift = when defined(cpu16): 8 else: 12 # \ - # my tests showed no improvements for using larger page sizes. + PageShift = when defined(nimPage256) or defined(cpu16): 8 + elif defined(nimPage512): 9 + elif defined(nimPage1k): 10 + else: 12 # \ # my tests showed no improvements for using larger page sizes. + PageSize = 1 shl PageShift var p = osAllocPages(3 * PageSize) From fe46c8b5f13f854e4109183d2199b922f939548c Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 17 Nov 2021 17:15:54 +0800 Subject: [PATCH 2661/3768] fix marshal bugs in VM (#19161) [backport:1.6] --- compiler/vmmarshal.nim | 4 ++-- tests/ccgbugs/t8967.nim | 12 ++++++++---- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/compiler/vmmarshal.nim b/compiler/vmmarshal.nim index d48d2408c..d28f0325b 100644 --- a/compiler/vmmarshal.nim +++ b/compiler/vmmarshal.nim @@ -92,7 +92,8 @@ proc storeAny(s: var string; t: PType; a: PNode; stored: var IntSet; if a[i].kind == nkRange: var x = copyNode(a[i][0]) storeAny(s, t.lastSon, x, stored, conf) - while x.intVal+1 <= a[i][1].intVal: + inc x.intVal + while x.intVal <= a[i][1].intVal: s.add(", ") storeAny(s, t.lastSon, x, stored, conf) inc x.intVal @@ -231,7 +232,6 @@ proc loadAny(p: var JsonParser, t: PType, result = newNode(nkCurly) while p.kind != jsonArrayEnd and p.kind != jsonEof: result.add loadAny(p, t.lastSon, tab, cache, conf, idgen) - next(p) if p.kind == jsonArrayEnd: next(p) else: raiseParseErr(p, "']' end of array expected") of tyPtr, tyRef: diff --git a/tests/ccgbugs/t8967.nim b/tests/ccgbugs/t8967.nim index e342b7eae..0301a2e4f 100644 --- a/tests/ccgbugs/t8967.nim +++ b/tests/ccgbugs/t8967.nim @@ -4,7 +4,11 @@ discard """ import marshal -let orig: set[char] = {'A'..'Z'} -let m = $$orig -let old = to[set[char]](m) -doAssert orig - old == {} +template main() = + let orig: set[char] = {'A'..'Z'} + let m = $$orig + let old = to[set[char]](m) + doAssert orig - old == {} + +static: main() +main() From a8611c665f26adbf7f6dd2438359b4c5d930bf86 Mon Sep 17 00:00:00 2001 From: OxFred <88140942+OxFred@users.noreply.github.com> Date: Wed, 17 Nov 2021 13:16:12 +0100 Subject: [PATCH 2662/3768] Update dependency (#19151) --- testament/important_packages.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index fc5c808ab..e11799e73 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -55,7 +55,7 @@ pkg "chronos", "nim c -r -d:release tests/testall", allowFailure = true # pendin pkg "cligen", "nim c --path:. -r cligen.nim" pkg "combparser", "nimble test --gc:orc" pkg "compactdict" -pkg "comprehension", "nimble test", "https://github.com/alehander42/comprehension" +pkg "comprehension", "nimble test", "https://github.com/alehander92/comprehension" pkg "criterion", allowFailure = true # pending https://github.com/disruptek/criterion/issues/3 (wrongly closed) pkg "datamancer" pkg "dashing", "nim c tests/functional.nim" From eb5358dcdb1b650db658def14cc8c60b4bb7c203 Mon Sep 17 00:00:00 2001 From: quantimnot <54247259+quantimnot@users.noreply.github.com> Date: Fri, 19 Nov 2021 02:19:01 -0500 Subject: [PATCH 2663/3768] Testament now checks OS, CPU, endianess, bitsizes 8-64 in discard statements (#19137) * Testament checks host/cpu in compiler/platform for discard spec statement * Remove duplicated value checks, add comments, add all cpu bit sizes and endianesses Co-authored-by: quantimnot --- testament/specs.nim | 70 ++++++++++++++++++++++++++++++++------------- 1 file changed, 50 insertions(+), 20 deletions(-) diff --git a/testament/specs.nim b/testament/specs.nim index c7e332262..42400f22d 100644 --- a/testament/specs.nim +++ b/testament/specs.nim @@ -9,6 +9,7 @@ import sequtils, parseutils, strutils, os, streams, parsecfg, tables, hashes, sets +import compiler/platform type TestamentData* = ref object # better to group globals under 1 object; could group the other ones here too @@ -329,42 +330,71 @@ proc parseSpec*(filename: string): TSpec = # Valgrind only supports OSX <= 17.x result.useValgrind = disabled of "disabled": - case e.value.normalize + let value = e.value.normalize + case value of "y", "yes", "true", "1", "on": result.err = reDisabled of "n", "no", "false", "0", "off": discard - of "win", "windows": + # These values are defined in `compiler/options.isDefined` + of "win": when defined(windows): result.err = reDisabled of "linux": when defined(linux): result.err = reDisabled of "bsd": when defined(bsd): result.err = reDisabled - of "osx", "macosx": # xxx remove `macosx` alias? + of "osx": when defined(osx): result.err = reDisabled - of "unix": - when defined(unix): result.err = reDisabled - of "posix": + of "unix", "posix": when defined(posix): result.err = reDisabled + of "freebsd": + when defined(freebsd): result.err = reDisabled + of "littleendian": + when defined(littleendian): result.err = reDisabled + of "bigendian": + when defined(bigendian): result.err = reDisabled + of "cpu8", "8bit": + when defined(cpu8): result.err = reDisabled + of "cpu16", "16bit": + when defined(cpu16): result.err = reDisabled + of "cpu32", "32bit": + when defined(cpu32): result.err = reDisabled + of "cpu64", "64bit": + when defined(cpu64): result.err = reDisabled + # These values are for CI environments of "travis": # deprecated if isTravis: result.err = reDisabled of "appveyor": # deprecated if isAppVeyor: result.err = reDisabled of "azure": if isAzure: result.err = reDisabled - of "32bit": - if sizeof(int) == 4: - result.err = reDisabled - of "freebsd": - when defined(freebsd): result.err = reDisabled - of "arm64": - when defined(arm64): result.err = reDisabled - of "i386": - when defined(i386): result.err = reDisabled - of "openbsd": - when defined(openbsd): result.err = reDisabled - of "netbsd": - when defined(netbsd): result.err = reDisabled else: - result.parseErrors.addLine "cannot interpret as a bool: ", e.value + # Check whether the value exists as an OS or CPU that is + # defined in `compiler/platform`. + block checkHost: + for os in platform.OS: + # Check if the value exists as OS. + if value == os.name.normalize: + # The value exists; is it the same as the current host? + if value == hostOS.normalize: + # The value exists and is the same as the current host, + # so disable the test. + result.err = reDisabled + # The value was defined, so there is no need to check further + # values or raise an error. + break checkHost + for cpu in platform.CPU: + # Check if the value exists as CPU. + if value == cpu.name.normalize: + # The value exists; is it the same as the current host? + if value == hostCPU.normalize: + # The value exists and is the same as the current host, + # so disable the test. + result.err = reDisabled + # The value was defined, so there is no need to check further + # values or raise an error. + break checkHost + # The value doesn't exist as an OS, CPU, or any previous value + # defined in this case statement, so raise an error. + result.parseErrors.addLine "cannot interpret as a bool: ", e.value of "cmd": if e.value.startsWith("nim "): result.cmd = compilerPrefix & e.value[3..^1] From 1b143f5e79c940ba7f70e0512f36b5c61a6bc24d Mon Sep 17 00:00:00 2001 From: hlaaftana <10591326+hlaaftana@users.noreply.github.com> Date: Mon, 22 Nov 2021 12:41:35 +0300 Subject: [PATCH 2664/3768] Accept nnkTypeSection from typedef macro pragmas (#19168) --- changelog.md | 29 ++++++++++++++ compiler/semstmts.nim | 18 ++++++--- doc/manual.rst | 1 + tests/pragmas/ttypedef_macro.nim | 66 ++++++++++++++++++++++++++++++++ 4 files changed, 109 insertions(+), 5 deletions(-) create mode 100644 tests/pragmas/ttypedef_macro.nim diff --git a/changelog.md b/changelog.md index 1636d125f..8e70513e0 100644 --- a/changelog.md +++ b/changelog.md @@ -13,7 +13,36 @@ ## Language changes +- Pragma macros on type definitions can now return `nnkTypeSection` nodes as well as `nnkTypeDef`, + allowing multiple type definitions to be injected in place of the original type definition. + ```nim + import macros + + macro multiply(amount: static int, s: untyped): untyped = + let name = $s[0].basename + result = newNimNode(nnkTypeSection) + for i in 1 .. amount: + result.add(newTree(nnkTypeDef, ident(name & $i), s[1], s[2])) + + type + Foo = object + Bar {.multiply: 3.} = object + x, y, z: int + Baz = object + + # becomes + + type + Foo = object + Bar1 = object + x, y, z: int + Bar2 = object + x, y, z: int + Bar3 = object + x, y, z: int + Baz = object + ``` ## Compiler changes diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 4607e857a..36d76608e 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1112,7 +1112,12 @@ proc typeDefLeftSidePass(c: PContext, typeSection: PNode, i: int) = if name.kind == nkPragmaExpr: let rewritten = applyTypeSectionPragmas(c, name[1], typeDef) if rewritten != nil: - typeSection[i] = rewritten + case rewritten.kind + of nkTypeDef: + typeSection[i] = rewritten + of nkTypeSection: + typeSection.sons[i .. i] = rewritten.sons + else: illFormedAst(rewritten, c.config) typeDefLeftSidePass(c, typeSection, i) return pragma(c, s, name[1], typePragmas) @@ -1143,16 +1148,19 @@ proc typeDefLeftSidePass(c: PContext, typeSection: PNode, i: int) = proc typeSectionLeftSidePass(c: PContext, n: PNode) = # process the symbols on the left side for the whole type section, before # we even look at the type definitions on the right - for i in 0.. Date: Tue, 23 Nov 2021 00:13:03 -0800 Subject: [PATCH 2665/3768] Implement threads on Zephyr (#19156) * pthreads setup for zephyr - enable tweak stack size - update lib/system/threads.nim - Fix int/uint in casting pointer. * add documentation and tweak flag names * add documentation and tweak flag names * fix configuration flag names * fix configuration flag names * cleanup Co-authored-by: Jaremy Creechley --- doc/nimc.rst | 9 ++++++++ lib/system/threadlocalstorage.nim | 2 ++ lib/system/threads.nim | 34 +++++++++++++++++++++++-------- 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/doc/nimc.rst b/doc/nimc.rst index d35dad6c2..6be4c204e 100644 --- a/doc/nimc.rst +++ b/doc/nimc.rst @@ -681,6 +681,15 @@ nimMemAlignTiny Sets `MemAlign` to `4` bytes which reduces the memory alignment to better match some embedded devices. +Thread stack size +================= + +Nim's thread API provides a simple wrapper around more advanced +RTOS task features. Customizing the stack size and stack guard size can +be done by setting `-d:nimThreadStackSize=16384` or `-d:nimThreadStackGuard=32`. + +Currently only Zephyr and FreeRTOS support these configurations. + Nim for realtime systems ======================== diff --git a/lib/system/threadlocalstorage.nim b/lib/system/threadlocalstorage.nim index e772f270c..cea9abb42 100644 --- a/lib/system/threadlocalstorage.nim +++ b/lib/system/threadlocalstorage.nim @@ -153,6 +153,8 @@ else: proc pthread_attr_init(a1: var Pthread_attr): cint {. importc, header: pthreadh.} + proc pthread_attr_setstack*(a1: ptr Pthread_attr, a2: pointer, a3: int): cint {. + importc, header: pthreadh.} proc pthread_attr_setstacksize(a1: var Pthread_attr, a2: int): cint {. importc, header: pthreadh.} proc pthread_attr_destroy(a1: var Pthread_attr): cint {. diff --git a/lib/system/threads.nim b/lib/system/threads.nim index 0e5198035..ac727be0a 100644 --- a/lib/system/threads.nim +++ b/lib/system/threads.nim @@ -47,14 +47,23 @@ when not declared(ThisIsSystem): {.error: "You must not import this module explicitly".} -const - StackGuardSize = 4096 - ThreadStackMask = - when defined(genode): - 1024*64*sizeof(int)-1 - else: - 1024*256*sizeof(int)-1 - ThreadStackSize = ThreadStackMask+1 - StackGuardSize +when defined(zephyr) or defined(freertos): + const + nimThreadStackSize {.intdefine.} = 8192 + nimThreadStackGuard {.intdefine.} = 128 + + StackGuardSize = nimThreadStackGuard + ThreadStackSize = nimThreadStackSize - nimThreadStackGuard +else: + const + StackGuardSize = 4096 + ThreadStackMask = + when defined(genode): + 1024*64*sizeof(int)-1 + else: + 1024*256*sizeof(int)-1 + + ThreadStackSize = ThreadStackMask+1 - StackGuardSize #const globalsSlot = ThreadVarSlot(0) #sysAssert checkSlot.int == globalsSlot.int @@ -321,7 +330,14 @@ else: when hasSharedHeap: t.core.stackSize = ThreadStackSize var a {.noinit.}: Pthread_attr doAssert pthread_attr_init(a) == 0 - let setstacksizeResult = pthread_attr_setstacksize(a, ThreadStackSize) + when defined(zephyr): + var + rawstk = allocShared0(ThreadStackSize + StackGuardSize) + stk = cast[pointer](cast[uint](rawstk) + StackGuardSize) + let setstacksizeResult = pthread_attr_setstack(addr a, stk, ThreadStackSize) + else: + let setstacksizeResult = pthread_attr_setstacksize(a, ThreadStackSize) + when not defined(ios): # This fails on iOS doAssert(setstacksizeResult == 0) From 040d23e799e94aa3e0c6b254f1dc708b2eada96b Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Tue, 23 Nov 2021 15:02:03 +0300 Subject: [PATCH 2666/3768] implement RST & Markdown quote blocks (#19147) * implement RST & Markdown quote blocks * compile with nim 1.0 * Fix indentation --- config/nimdoc.tex.cfg | 10 +- doc/nimdoc.css | 5 + lib/packages/docutils/rst.nim | 210 +++++++++++- lib/packages/docutils/rstast.nim | 9 +- lib/packages/docutils/rstgen.nim | 29 +- nimdoc/testproject/expected/nimdoc.out.css | 5 + tests/stdlib/trst.nim | 361 +++++++++++++++++++++ 7 files changed, 617 insertions(+), 12 deletions(-) diff --git a/config/nimdoc.tex.cfg b/config/nimdoc.tex.cfg index 457f452dd..3912d1279 100644 --- a/config/nimdoc.tex.cfg +++ b/config/nimdoc.tex.cfg @@ -139,9 +139,17 @@ doc.file = """ \usepackage[most]{tcolorbox} % boxes around admonitions, code blocks, doc.item \newtcolorbox{rstadmonition}[1][]{blanker, breakable, - left=3mm, right=3mm, top=1mm, bottom=1mm, + left=3mm, right=0mm, top=1mm, bottom=1mm, before upper=\indent, parbox=false, #1} +\newtcolorbox{rstquote}[1][]{blanker, breakable, + left=3mm, right=3mm, top=1mm, bottom=1mm, + parbox=false, + borderline west={0.3em}{0pt}{lightgray}, + borderline north={0.05em}{0pt}{lightgray}, + borderline east={0.05em}{0pt}{lightgray}, + borderline south={0.05em}{0pt}{lightgray}} + \definecolor{rstframecolor}{rgb}{0.85, 0.8, 0.6} \newtcolorbox{rstprebox}[1][]{blanker, breakable, diff --git a/doc/nimdoc.css b/doc/nimdoc.css index 7c819ea30..0014cf196 100644 --- a/doc/nimdoc.css +++ b/doc/nimdoc.css @@ -567,6 +567,11 @@ blockquote { border-left: 5px solid #bbc; } +blockquote.markdown-quote { + font-size: 0.9rem; /* use rem to avoid recursion */ + font-style: normal; +} + .pre, span.tok { font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace; font-weight: 500; diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 2e908f4e5..4c28894fb 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -53,6 +53,8 @@ ## + field lists ## + option lists ## + indented literal blocks +## + quoted literal blocks +## + line blocks ## + simple tables ## + directives (see official documentation in `RST directives list`_): ## - ``image``, ``figure`` for including images and videos @@ -121,6 +123,7 @@ ## * Markdown code blocks ## * Markdown links ## * Markdown headlines +## * Markdown block quotes ## * using ``1`` as auto-enumerator in enumerated lists like RST ``#`` ## (auto-enumerator ``1`` can not be used with ``#`` in the same list) ## @@ -145,7 +148,7 @@ ## 2) Compatibility mode which is RST rules. ## ## .. Note:: in both modes the parser interpretes text between single -## backticks (code) identically: +## backticks (code) identically: ## backslash does not escape; the only exception: ``\`` folowed by ` ## does escape so that we can always input a single backtick ` in ## inline code. However that makes impossible to input code with @@ -156,13 +159,35 @@ ## ``\`` -- GOOD ## So single backticks can always be input: `\`` will turn to ` code ## +## .. Attention:: +## We don't support some obviously poor design choices of Markdown (or RST). +## +## - no support for the rule of 2 spaces causing a line break in Markdown +## (use RST "line blocks" syntax for making line breaks) +## +## - interpretation of Markdown block quotes is also slightly different, +## e.g. case +## +## :: +## +## >>> foo +## > bar +## >>baz +## +## is a single 3rd-level quote `foo bar baz` in original Markdown, while +## in Nim we naturally see it as 3rd-level quote `foo` + 1st level `bar` + +## 2nd level `baz`: +## +## >>> foo +## > bar +## >>baz +## ## Limitations ## ----------- ## ## * no Unicode support in character width calculations ## * body elements ## - no roman numerals in enumerated lists -## - no quoted literal blocks ## - no doctest blocks ## - no grid tables ## - some directives are missing (check official `RST directives list`_): @@ -472,6 +497,10 @@ type line: int # the last line of this style occurrence # (for error message) hasPeers: bool # has headings on the same level of hierarchy? + LiteralBlockKind = enum # RST-style literal blocks after `::` + lbNone, + lbIndentedLiteralBlock, + lbQuotedLiteralBlock LevelMap = seq[LevelInfo] # Saves for each possible title adornment # style its level in the current document. SubstitutionKind = enum @@ -1953,6 +1982,44 @@ proc parseLiteralBlock(p: var RstParser): PRstNode = inc p.idx result.add(n) +proc parseQuotedLiteralBlock(p: var RstParser): PRstNode = + result = newRstNodeA(p, rnLiteralBlock) + var n = newLeaf("") + if currentTok(p).kind == tkIndent: + var indent = currInd(p) + while currentTok(p).kind == tkIndent: inc p.idx # skip blank lines + var quoteSym = currentTok(p).symbol[0] + while true: + case currentTok(p).kind + of tkEof: + break + of tkIndent: + if currentTok(p).ival < indent: + break + elif currentTok(p).ival == indent: + if nextTok(p).kind == tkPunct and nextTok(p).symbol[0] == quoteSym: + n.text.add("\n") + inc p.idx + elif nextTok(p).kind == tkIndent: + break + else: + rstMessage(p, mwRstStyle, "no newline after quoted literal block") + break + else: + rstMessage(p, mwRstStyle, + "unexpected indentation in quoted literal block") + break + else: + n.text.add(currentTok(p).symbol) + inc p.idx + result.add(n) + +proc parseRstLiteralBlock(p: var RstParser, kind: LiteralBlockKind): PRstNode = + if kind == lbIndentedLiteralBlock: + result = parseLiteralBlock(p) + else: + result = parseQuotedLiteralBlock(p) + proc getLevel(p: var RstParser, c: char, hasOverline: bool): int = ## Returns (preliminary) heading level corresponding to `c` and ## `hasOverline`. If level does not exist, add it first. @@ -2023,6 +2090,33 @@ proc isLineBlock(p: RstParser): bool = p.tok[j].col > currentTok(p).col or p.tok[j].symbol == "\n" +proc isMarkdownBlockQuote(p: RstParser): bool = + result = currentTok(p).symbol[0] == '>' + +proc whichRstLiteralBlock(p: RstParser): LiteralBlockKind = + ## Checks that the following tokens are either Indented Literal Block or + ## Quoted Literal Block (which is not quite the same as Markdown quote block). + ## https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html#quoted-literal-blocks + if currentTok(p).symbol == "::" and nextTok(p).kind == tkIndent: + if currInd(p) > nextTok(p).ival: + result = lbNone + if currInd(p) < nextTok(p).ival: + result = lbIndentedLiteralBlock + elif currInd(p) == nextTok(p).ival: + var i = p.idx + 1 + while p.tok[i].kind == tkIndent: inc i + const validQuotingCharacters = { + '!', '"', '#', '$', '%', '&', '\'', '(', ')', '*', '+', ',', '-', + '.', '/', ':', ';', '<', '=', '>', '?', '@', '[', '\\', ']', '^', + '_', '`', '{', '|', '}', '~'} + if p.tok[i].kind in {tkPunct, tkAdornment} and + p.tok[i].symbol[0] in validQuotingCharacters: + result = lbQuotedLiteralBlock + else: + result = lbNone + else: + result = lbNone + proc predNL(p: RstParser): bool = result = true if p.idx > 0: @@ -2078,6 +2172,8 @@ proc whichSection(p: RstParser): RstNodeKind = elif match(p, p.idx + 1, " a"): result = rnTable elif currentTok(p).symbol == "|" and isLineBlock(p): result = rnLineBlock + elif roSupportMarkdown in p.s.options and isMarkdownBlockQuote(p): + result = rnMarkdownBlockQuote elif match(p, p.idx + 1, "i") and isAdornmentHeadline(p, p.idx): result = rnOverline else: @@ -2090,6 +2186,8 @@ proc whichSection(p: RstParser): RstNodeKind = result = rnMarkdownTable elif currentTok(p).symbol == "|" and isLineBlock(p): result = rnLineBlock + elif roSupportMarkdown in p.s.options and isMarkdownBlockQuote(p): + result = rnMarkdownBlockQuote elif match(p, tokenAfterNewline(p), "aI") and isAdornmentHeadline(p, tokenAfterNewline(p)): result = rnHeadline @@ -2143,6 +2241,102 @@ proc parseLineBlock(p: var RstParser): PRstNode = else: break +proc parseDoc(p: var RstParser): PRstNode {.gcsafe.} + +proc getQuoteSymbol(p: RstParser, idx: int): tuple[sym: string, depth: int, tokens: int] = + result = ("", 0, 0) + var i = idx + result.sym &= p.tok[i].symbol + result.depth += p.tok[i].symbol.len + inc result.tokens + inc i + while p.tok[i].kind == tkWhite and i+1 < p.tok.len and + p.tok[i+1].kind == tkPunct and p.tok[i+1].symbol[0] == '>': + result.sym &= p.tok[i].symbol + result.sym &= p.tok[i+1].symbol + result.depth += p.tok[i+1].symbol.len + inc result.tokens, 2 + inc i, 2 + +proc parseMarkdownQuoteSegment(p: var RstParser, curSym: string, col: int): + PRstNode = + ## We define *segment* as a group of lines that starts with exactly the + ## same quote symbol. If the following lines don't contain any `>` (*lazy* + ## continuation) they considered as continuation of the current segment. + var q: RstParser # to delete `>` at a start of line and then parse normally + initParser(q, p.s) + q.col = p.col + q.line = p.line + var minCol = int.high # minimum colum num in the segment + while true: # move tokens of segment from `p` to `q` skipping `curSym` + case currentTok(p).kind + of tkEof: + break + of tkIndent: + if nextTok(p).kind in {tkIndent, tkEof}: + break + else: + if nextTok(p).symbol[0] == '>': + var (quoteSym, _, quoteTokens) = getQuoteSymbol(p, p.idx + 1) + if quoteSym == curSym: # the segment continues + var iTok = tokenAfterNewline(p, p.idx+1) + if p.tok[iTok].kind notin {tkEof, tkIndent} and + p.tok[iTok].symbol[0] != '>': + rstMessage(p, mwRstStyle, + "two or more quoted lines are followed by unquoted line " & + $(curLine(p) + 1)) + break + q.tok.add currentTok(p) + var ival = currentTok(p).ival + quoteSym.len + inc p.idx, (1 + quoteTokens) # skip newline and > > > + if currentTok(p).kind == tkWhite: + ival += currentTok(p).symbol.len + inc p.idx + # fix up previous `tkIndent`s to ival (as if >>> were not there) + var j = q.tok.len - 1 + while j >= 0 and q.tok[j].kind == tkIndent: + q.tok[j].ival = ival + dec j + else: # next segment started + break + elif currentTok(p).ival < col: + break + else: # the segment continues, a case like: + # > beginning + # continuation + q.tok.add currentTok(p) + inc p.idx + else: + if currentTok(p).col < minCol: minCol = currentTok(p).col + q.tok.add currentTok(p) + inc p.idx + q.indentStack = @[minCol] + # if initial indentation `minCol` is > 0 then final newlines + # should be omitted so that parseDoc could advance to the end of tokens: + var j = q.tok.len - 1 + while q.tok[j].kind == tkIndent: dec j + q.tok.setLen (j+1) + q.tok.add Token(kind: tkEof, line: currentTok(p).line) + result = parseDoc(q) + +proc parseMarkdownBlockQuote(p: var RstParser): PRstNode = + var (curSym, quotationDepth, quoteTokens) = getQuoteSymbol(p, p.idx) + let col = currentTok(p).col + result = newRstNodeA(p, rnMarkdownBlockQuote) + inc p.idx, quoteTokens # skip first > + while true: + var item = newRstNode(rnMarkdownBlockQuoteItem) + item.quotationDepth = quotationDepth + if currentTok(p).kind == tkWhite: inc p.idx + item.add parseMarkdownQuoteSegment(p, curSym, col) + result.add(item) + if currentTok(p).kind == tkIndent and currentTok(p).ival == col and + nextTok(p).kind != tkEof and nextTok(p).symbol[0] == '>': + (curSym, quotationDepth, quoteTokens) = getQuoteSymbol(p, p.idx + 1) + inc p.idx, (1 + quoteTokens) # skip newline and > > > + else: + break + proc parseParagraph(p: var RstParser, result: PRstNode) = while true: case currentTok(p).kind @@ -2158,16 +2352,17 @@ proc parseParagraph(p: var RstParser, result: PRstNode) = result.add newLeaf(" ") of rnLineBlock: result.addIfNotNil(parseLineBlock(p)) + of rnMarkdownBlockQuote: + result.addIfNotNil(parseMarkdownBlockQuote(p)) else: break else: break of tkPunct: - if currentTok(p).symbol == "::" and - nextTok(p).kind == tkIndent and - currInd(p) < nextTok(p).ival: + if (let literalBlockKind = whichRstLiteralBlock(p); + literalBlockKind != lbNone): result.add newLeaf(":") inc p.idx # skip '::' - result.add(parseLiteralBlock(p)) + result.add(parseRstLiteralBlock(p, literalBlockKind)) break else: parseInline(p, result) @@ -2257,8 +2452,6 @@ proc getColumns(p: var RstParser, cols: var IntSeq) = # last column has no limit: cols[L - 1] = 32000 -proc parseDoc(p: var RstParser): PRstNode {.gcsafe.} - proc parseSimpleTable(p: var RstParser): PRstNode = var cols: IntSeq @@ -2585,6 +2778,7 @@ proc parseSection(p: var RstParser, result: PRstNode) = a = parseLiteralBlock(p) of rnBulletList: a = parseBulletList(p) of rnLineBlock: a = parseLineBlock(p) + of rnMarkdownBlockQuote: a = parseMarkdownBlockQuote(p) of rnDirective: a = parseDotDot(p) of rnEnumList: a = parseEnumList(p) of rnLeaf: rstMessage(p, meNewSectionExpected, "(syntax error)") diff --git a/lib/packages/docutils/rstast.nim b/lib/packages/docutils/rstast.nim index bc7b9a650..1d5da5e1c 100644 --- a/lib/packages/docutils/rstast.nim +++ b/lib/packages/docutils/rstast.nim @@ -32,7 +32,10 @@ type rnFieldName, # consisting of a field name ... rnFieldBody, # ... and a field body rnOptionList, rnOptionListItem, rnOptionGroup, rnOption, rnOptionString, - rnOptionArgument, rnDescription, rnLiteralBlock, rnQuotedLiteralBlock, + rnOptionArgument, rnDescription, rnLiteralBlock, + rnMarkdownBlockQuote, # a quote starting from punctuation like >>> + rnMarkdownBlockQuoteItem, # a quotation block, quote lines starting with + # the same number of chars rnLineBlock, # the | thingie rnLineBlockItem, # a son of rnLineBlock - one line inside it. # When `RstNode` lineIndent="\n" the line's empty @@ -101,6 +104,8 @@ type of rnFootnote, rnCitation, rnOptionListItem: order*: int ## footnote order (for auto-symbol footnotes and ## auto-numbered ones without a label) + of rnMarkdownBlockQuoteItem: + quotationDepth*: int ## number of characters in line prefix of rnRef, rnSubstitutionReferences, rnInterpretedText, rnField, rnInlineCode, rnCodeBlock, rnFootnoteRef: info*: TLineInfo ## To have line/column info for warnings at @@ -409,6 +414,8 @@ proc treeRepr*(node: PRstNode, indent=0): string = result.add " level=" & $node.level of rnFootnote, rnCitation, rnOptionListItem: result.add (if node.order == 0: "" else: " order=" & $node.order) + of rnMarkdownBlockQuoteItem: + result.add " quotationDepth=" & $node.quotationDepth else: discard result.add (if node.anchor == "": "" else: " anchor='" & node.anchor & "'") diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 4cbd05379..dc6ca25c1 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -89,6 +89,7 @@ type onTestSnippet*: proc (d: var RstGenerator; filename, cmd: string; status: int; content: string) escMode*: EscapeMode + curQuotationDepth: int PDoc = var RstGenerator ## Alias to type less. @@ -167,6 +168,7 @@ proc initRstGenerator*(g: var RstGenerator, target: OutputTarget, g.currentSection = "" g.id = 0 g.escMode = emText + g.curQuotationDepth = 0 let fileParts = filename.splitFile if fileParts.ext == ".nim": g.currentSection = "Module " & fileParts.name @@ -1268,8 +1270,31 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = of rnLiteralBlock: renderAux(d, n, "$1
    \n", "\n\n$2\\begin{rstpre}\n$1\n\\end{rstpre}\n\n", result) - of rnQuotedLiteralBlock: - doAssert false, "renderRstToOut" + of rnMarkdownBlockQuote: + d.curQuotationDepth = 1 + var tmp = "" + renderAux(d, n, "$1", "$1", tmp) + let itemEnding = + if d.target == outHtml: "" else: "\\end{rstquote}" + tmp.add itemEnding.repeat(d.curQuotationDepth - 1) + dispA(d.target, result, + "$1\n", + "\n\\begin{rstquote}\n$2\n$1\\end{rstquote}\n", [tmp, n.anchor.idS]) + of rnMarkdownBlockQuoteItem: + let addQuotationDepth = n.quotationDepth - d.curQuotationDepth + var itemPrefix: string # start or ending (quotation grey bar on the left) + if addQuotationDepth >= 0: + let s = + if d.target == outHtml: "
    " + else: "\\begin{rstquote}" + itemPrefix = s.repeat(addQuotationDepth) + else: + let s = + if d.target == outHtml: "
    " + else: "\\end{rstquote}" + itemPrefix = s.repeat(-addQuotationDepth) + renderAux(d, n, itemPrefix & "

    $1

    ", itemPrefix & "\n$1", result) + d.curQuotationDepth = n.quotationDepth of rnLineBlock: if n.sons.len == 1 and n.sons[0].lineIndent == "\n": # whole line block is one empty line, no need to add extra spacing diff --git a/nimdoc/testproject/expected/nimdoc.out.css b/nimdoc/testproject/expected/nimdoc.out.css index 7c819ea30..0014cf196 100644 --- a/nimdoc/testproject/expected/nimdoc.out.css +++ b/nimdoc/testproject/expected/nimdoc.out.css @@ -567,6 +567,11 @@ blockquote { border-left: 5px solid #bbc; } +blockquote.markdown-quote { + font-size: 0.9rem; /* use rem to avoid recursion */ + font-style: normal; +} + .pre, span.tok { font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace; font-weight: 500; diff --git a/tests/stdlib/trst.nim b/tests/stdlib/trst.nim index e4609a671..0a9eb8038 100644 --- a/tests/stdlib/trst.nim +++ b/tests/stdlib/trst.nim @@ -105,6 +105,367 @@ suite "RST parsing": rnLeaf ' ' """) + test "RST quoted literal blocks": + let expected = + dedent""" + rnInner + rnLeaf 'Paragraph' + rnLeaf ':' + rnLiteralBlock + rnLeaf '>x' + """ + + check(dedent""" + Paragraph:: + + >x""".toAst == expected) + + check(dedent""" + Paragraph:: + + >x""".toAst == expected) + + test "RST quoted literal blocks, :: at a separate line": + let expected = + dedent""" + rnInner + rnInner + rnLeaf 'Paragraph' + rnLiteralBlock + rnLeaf '>x + >>y' + """ + + check(dedent""" + Paragraph + + :: + + >x + >>y""".toAst == expected) + + check(dedent""" + Paragraph + + :: + + >x + >>y""".toAst == expected) + + test "Markdown quoted blocks": + check(dedent""" + Paragraph. + >x""".toAst == + dedent""" + rnInner + rnLeaf 'Paragraph' + rnLeaf '.' + rnMarkdownBlockQuote + rnMarkdownBlockQuoteItem quotationDepth=1 + rnLeaf 'x' + """) + + # bug #17987 + check(dedent""" + foo https://github.com/nim-lang/Nim/issues/8258 + + > bar""".toAst == + dedent""" + rnInner + rnInner + rnLeaf 'foo' + rnLeaf ' ' + rnStandaloneHyperlink + rnLeaf 'https://github.com/nim-lang/Nim/issues/8258' + rnMarkdownBlockQuote + rnMarkdownBlockQuoteItem quotationDepth=1 + rnLeaf 'bar' + """) + + let expected = dedent""" + rnInner + rnLeaf 'Paragraph' + rnLeaf '.' + rnMarkdownBlockQuote + rnMarkdownBlockQuoteItem quotationDepth=1 + rnInner + rnLeaf 'x1' + rnLeaf ' ' + rnLeaf 'x2' + rnMarkdownBlockQuoteItem quotationDepth=2 + rnInner + rnLeaf 'y1' + rnLeaf ' ' + rnLeaf 'y2' + rnMarkdownBlockQuoteItem quotationDepth=1 + rnLeaf 'z' + """ + + check(dedent""" + Paragraph. + >x1 x2 + >>y1 y2 + >z""".toAst == expected) + + check(dedent""" + Paragraph. + > x1 x2 + >> y1 y2 + > z""".toAst == expected) + + check(dedent""" + >x + >y + >z""".toAst == + dedent""" + rnMarkdownBlockQuote + rnMarkdownBlockQuoteItem quotationDepth=1 + rnInner + rnLeaf 'x' + rnLeaf ' ' + rnLeaf 'y' + rnLeaf ' ' + rnLeaf 'z' + """) + + check(dedent""" + > z + > > >y + """.toAst == + dedent""" + rnMarkdownBlockQuote + rnMarkdownBlockQuoteItem quotationDepth=1 + rnLeaf 'z' + rnMarkdownBlockQuoteItem quotationDepth=3 + rnLeaf 'y' + """) + + test "Markdown quoted blocks: lazy": + let expected = dedent""" + rnInner + rnMarkdownBlockQuote + rnMarkdownBlockQuoteItem quotationDepth=2 + rnInner + rnLeaf 'x' + rnLeaf ' ' + rnLeaf 'continuation1' + rnLeaf ' ' + rnLeaf 'continuation2' + rnParagraph + rnLeaf 'newParagraph' + """ + check(dedent""" + >>x + continuation1 + continuation2 + + newParagraph""".toAst == expected) + + check(dedent""" + >> x + continuation1 + continuation2 + + newParagraph""".toAst == expected) + + # however mixing more than 1 non-lazy line and lazy one(s) splits quote + # in our parser, which appeared the easiest way to handle such cases: + var warnings = new seq[string] + check(dedent""" + >> x + >> continuation1 + continuation2 + + newParagraph""".toAst(warnings=warnings) == + dedent""" + rnInner + rnMarkdownBlockQuote + rnMarkdownBlockQuoteItem quotationDepth=2 + rnLeaf 'x' + rnMarkdownBlockQuoteItem quotationDepth=2 + rnInner + rnLeaf 'continuation1' + rnLeaf ' ' + rnLeaf 'continuation2' + rnParagraph + rnLeaf 'newParagraph' + """) + check(warnings[] == @[ + "input(2, 1) Warning: RST style: two or more quoted lines " & + "are followed by unquoted line 3"]) + + test "Markdown quoted blocks: not lazy": + # here is where we deviate from CommonMark specification: 'bar' below is + # not considered as continuation of 2-level '>> foo' quote. + check(dedent""" + >>> foo + > bar + >> baz + """.toAst() == + dedent""" + rnMarkdownBlockQuote + rnMarkdownBlockQuoteItem quotationDepth=3 + rnLeaf 'foo' + rnMarkdownBlockQuoteItem quotationDepth=1 + rnLeaf 'bar' + rnMarkdownBlockQuoteItem quotationDepth=2 + rnLeaf 'baz' + """) + + + test "Markdown quoted blocks: inline markup works": + check(dedent""" + > hi **bold** text + """.toAst == dedent""" + rnMarkdownBlockQuote + rnMarkdownBlockQuoteItem quotationDepth=1 + rnInner + rnLeaf 'hi' + rnLeaf ' ' + rnStrongEmphasis + rnLeaf 'bold' + rnLeaf ' ' + rnLeaf 'text' + """) + + test "Markdown quoted blocks: blank line separator": + let expected = dedent""" + rnInner + rnMarkdownBlockQuote + rnMarkdownBlockQuoteItem quotationDepth=1 + rnInner + rnLeaf 'x' + rnLeaf ' ' + rnLeaf 'y' + rnMarkdownBlockQuote + rnMarkdownBlockQuoteItem quotationDepth=1 + rnInner + rnLeaf 'z' + rnLeaf ' ' + rnLeaf 't' + """ + check(dedent""" + >x + >y + + > z + > t""".toAst == expected) + + check(dedent""" + >x + y + + > z + t""".toAst == expected) + + test "Markdown quoted blocks: nested body blocks/elements work #1": + let expected = dedent""" + rnMarkdownBlockQuote + rnMarkdownBlockQuoteItem quotationDepth=1 + rnBulletList + rnBulletItem + rnInner + rnLeaf 'x' + rnBulletItem + rnInner + rnLeaf 'y' + """ + + check(dedent""" + > - x + - y + """.toAst == expected) + + # TODO: if bug #17340 point 28 is resolved then this may work: + # check(dedent""" + # > - x + # - y + # """.toAst == expected) + + check(dedent""" + > - x + > - y + """.toAst == expected) + + check(dedent""" + > + > - x + > + > - y + > + """.toAst == expected) + + test "Markdown quoted blocks: nested body blocks/elements work #2": + let expected = dedent""" + rnAdmonition adType=note + [nil] + [nil] + rnDefList + rnDefItem + rnDefName + rnLeaf 'deflist' + rnLeaf ':' + rnDefBody + rnMarkdownBlockQuote + rnMarkdownBlockQuoteItem quotationDepth=2 + rnInner + rnLeaf 'quote' + rnLeaf ' ' + rnLeaf 'continuation' + """ + + check(dedent""" + .. Note:: deflist: + >> quote + continuation + """.toAst == expected) + + check(dedent""" + .. Note:: + deflist: + >> quote + continuation + """.toAst == expected) + + check(dedent""" + .. Note:: + deflist: + >> quote + >> continuation + """.toAst == expected) + + # spaces are not significant between `>`: + check(dedent""" + .. Note:: + deflist: + > > quote + > > continuation + """.toAst == expected) + + test "Markdown quoted blocks: de-indent handled well": + check(dedent""" + > + > - x + > - y + > + > Paragraph. + """.toAst == dedent""" + rnMarkdownBlockQuote + rnMarkdownBlockQuoteItem quotationDepth=1 + rnInner + rnBlockQuote + rnBulletList + rnBulletItem + rnInner + rnLeaf 'x' + rnBulletItem + rnInner + rnLeaf 'y' + rnParagraph + rnLeaf 'Paragraph' + rnLeaf '.' + """) + test "option list has priority over definition list": check(dedent""" --defusages From 5933aece9b5f7320e8feb2ef610b59d347c74d27 Mon Sep 17 00:00:00 2001 From: hlaaftana <10591326+hlaaftana@users.noreply.github.com> Date: Tue, 23 Nov 2021 18:30:17 +0300 Subject: [PATCH 2667/3768] `caseStmtMacros` no longer experimental, experimental manual refactor (#19173) * `caseStmtMacros` no longer experimental, experimental manual refactor * Update doc/manual.rst * apply review suggestions * apply review Co-authored-by: Andreas Rumpf --- changelog.md | 2 + compiler/options.nim | 2 +- compiler/semstmts.nim | 8 +- doc/manual.rst | 409 ++++++++--- doc/manual_experimental.rst | 1180 +++++++++++++------------------ lib/core/macros.nim | 7 +- tests/macros/tcasestmtmacro.nim | 2 - 7 files changed, 814 insertions(+), 796 deletions(-) diff --git a/changelog.md b/changelog.md index 8e70513e0..b645bc5c7 100644 --- a/changelog.md +++ b/changelog.md @@ -43,6 +43,8 @@ x, y, z: int Baz = object ``` +- [Case statement macros](manual.html#macros-case-statement-macros) are no longer experimental, + meaning you no longer need to enable the experimental switch `caseStmtMacros` to use them. ## Compiler changes diff --git a/compiler/options.nim b/compiler/options.nim index 2feaa2d43..d8e5057f9 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -195,7 +195,7 @@ type notnil, dynamicBindSym, forLoopMacros, # not experimental anymore; remains here for backwards compatibility - caseStmtMacros, + caseStmtMacros, # ditto codeReordering, compiletimeFFI, ## This requires building nim with `-d:nimHasLibFFI` diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 36d76608e..f302dd4c3 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -987,10 +987,10 @@ proc semCase(c: PContext, n: PNode; flags: TExprFlags): PNode = else: popCaseContext(c) closeScope(c) - if caseStmtMacros in c.features: - result = handleCaseStmtMacro(c, n, flags) - if result != nil: - return result + #if caseStmtMacros in c.features: + result = handleCaseStmtMacro(c, n, flags) + if result != nil: + return result localError(c.config, n[0].info, errSelectorMustBeOfCertainTypes) return for i in 1..`_. +See also `custom numeric literals <#custom-numeric-literals>`_. -Numeric Literals +Numeric literals ---------------- Numeric literals have the form:: @@ -625,7 +625,7 @@ Hence: 0b10000000'u8 == 0x80'u8 == 128, but, 0b10000000'i8 == 0x80'i8 == -1 instead of causing an overflow error. -Custom Numeric Literals +Custom numeric literals ~~~~~~~~~~~~~~~~~~~~~~~ If the suffix is not predefined, then the suffix is assumed to be a call @@ -700,26 +700,6 @@ contain a dot: `{..}` are the three tokens `{`:tok:, `..`:tok:, `}`:tok: and not the two tokens `{.`:tok:, `.}`:tok:. -Unicode Operators ------------------ - -Under the `--experimental:unicodeOperators` switch these Unicode operators are -also parsed as operators:: - - ∙ ∘ × ★ ⊗ ⊘ ⊙ ⊛ ⊠ ⊡ ∩ ∧ ⊓ # same priority as * (multiplication) - ± ⊕ ⊖ ⊞ ⊟ ∪ ∨ ⊔ # same priority as + (addition) - - -If enabled, Unicode operators can be combined with non-Unicode operator -symbols. The usual precedence extensions then apply, for example, `⊠=` is an -assignment like operator just like `*=` is. - -No Unicode normalization step is performed. - -**Note**: Due to parser limitations one **cannot** enable this feature via a -pragma `{.experimental: "unicodeOperators".}` reliably. - - Syntax ====== @@ -1323,46 +1303,6 @@ as `MyEnum.value`: To implement bit fields with enums see `Bit fields <#set-type-bit-fields>`_ -Overloadable enum field names ------------------------------ - -To be enabled via `{.experimental: "overloadableEnums".}`. - -Enum field names are overloadable much like routines. When an overloaded -enum field is used, it produces a closed sym choice construct, here -written as `(E|E)`. -During overload resolution the right `E` is picked, if possible. -For (array/object...) constructors the right `E` is picked, comparable to -how `[byte(1), 2, 3]` works, one needs to use `[T.E, E2, E3]`. Ambiguous -enum fields produce a static error: - -.. code-block:: nim - :test: "nim c $1" - - {.experimental: "overloadableEnums".} - - type - E1 = enum - value1, - value2 - E2 = enum - value1, - value2 = 4 - - const - Lookuptable = [ - E1.value1: "1", - value2: "2" - ] - - proc p(e: E1) = - # disambiguation in 'case' statements: - case e - of value1: echo "A" - of value2: echo "B" - - p value2 - String type ----------- @@ -1684,11 +1624,6 @@ must match the order of the tuple's definition. Different tuple-types are *equivalent* if they specify the same fields of the same type in the same order. The *names* of the fields also have to be the same. -The assignment operator for tuples copies each component. -The default assignment operator for objects copies each component. Overloading -of the assignment operator is described `here -`_. - .. code-block:: nim type @@ -1765,6 +1700,10 @@ introduce new object roots apart from `system.RootObj`. Student = ref object of Person # Error: inheritance only works with non-final objects id: int +The assignment operator for tuples and objects copies each component. +The methods to override this copying behavior are described `here +`_. + Object construction ------------------- @@ -2685,6 +2624,7 @@ Varargs matching See `Varargs <#types-varargs>`_. + iterable -------- @@ -2725,6 +2665,24 @@ available. Let `p` be an overloaded symbol. These contexts are: As usual, ambiguous matches produce a compile-time error. +Named argument overloading +-------------------------- + +Routines with the same type signature can be called individually if +a parameter has different names between them. + +.. code-block:: Nim + proc foo(x: int) = + echo "Using x: ", x + proc foo(y: int) = + echo "Using y: ", y + + foo(x = 2) # Using x: 2 + foo(y = 2) # Using y: 2 + +Not supplying the parameter name in such cases results in an +ambiguity error. + Statements and expressions ========================== @@ -3200,7 +3158,7 @@ Return statement Example: .. code-block:: nim - return 40+2 + return 40 + 2 The `return` statement ends the execution of the current procedure. It is only allowed in procedures. If there is an `expr`, this is syntactic @@ -3836,8 +3794,8 @@ behavior inside loop bodies. See `closureScope `_ and `capture `_ for details on how to change this behavior. -Anonymous Procs ---------------- +Anonymous procedures +-------------------- Unnamed procedures can be used as lambda expressions to pass into other procedures: @@ -3845,8 +3803,8 @@ procedures: .. code-block:: nim var cities = @["Frankfurt", "Tokyo", "New York", "Kyiv"] - cities.sort(proc (x,y: string): int = - cmp(x.len, y.len)) + cities.sort(proc (x, y: string): int = + cmp(x.len, y.len)) Procs as expressions can appear both as nested procs and inside top-level @@ -3854,6 +3812,42 @@ executable code. The `sugar `_ module contains the `=>` macro which enables a more succinct syntax for anonymous procedures resembling lambdas as they are in languages like JavaScript, C#, etc. +Do notation +----------- + +As a special convenience notation that keeps most elements of a +regular proc expression, the `do` keyword can be used to pass +anonymous procedures to routines: + +.. code-block:: nim + var cities = @["Frankfurt", "Tokyo", "New York", "Kyiv"] + + sort(cities) do (x, y: string) -> int: + cmp(x.len, y.len) + + # Less parentheses using the method plus command syntax: + cities = cities.map do (x: string) -> string: + "City of " & x + +`do` is written after the parentheses enclosing the regular proc params. +The proc expression represented by the `do` block is appended to the routine +call as the last argument. In calls using the command syntax, the `do` block +will bind to the immediately preceding expression rather than the command call. + +`do` with a parameter list corresponds to an anonymous `proc`, however +`do` without parameters is treated as a normal statement list. This allows +macros to receive both indented statement lists as an argument in inline +calls, as well as a direct mirror of Nim's routine syntax. + +.. code-block:: nim + # Passing a statement list to an inline macro: + macroResults.add quote do: + if not `ex`: + echo `info`, ": Check failed: ", `expString` + + # Processing a routine definition in a macro: + rpc(router, "add") do (a, b: int) -> int: + result = a + b Func ---- @@ -5133,7 +5127,7 @@ code: deletedKeys: seq[bool] -Type Classes +Type classes ------------ A type class is a special pseudo-type that can be used to match against @@ -5863,7 +5857,15 @@ twice: While macros enable advanced compile-time code transformations, they cannot change Nim's syntax. -Debug Example +**Style note:** For code readability, it is best to use the least powerful +programming construct that remains expressive. So the "check list" is: + +(1) Use an ordinary proc/iterator, if possible. +(2) Else: Use a generic proc/iterator, if possible. +(3) Else: Use a template, if possible. +(4) Else: Use a macro. + +Debug example ------------- The following example implements a powerful `debug` command that accepts a @@ -5921,7 +5923,7 @@ constructor expression. This is why `debug` iterates over all of `args`'s children. -BindSym +bindSym ------- The above `debug` macro relies on the fact that `write`, `writeLine` and @@ -5970,43 +5972,38 @@ However, the symbols `write`, `writeLine` and `stdout` are already bound and are not looked up again. As the example shows, `bindSym` does work with overloaded symbols implicitly. -Case-Of Macro -------------- +Note that the symbol names passed to `bindSym` have to be constant. The +experimental feature `dynamicBindSym` (`experimental manual +`_) +allows this value to be computed dynamically. -In Nim, it is possible to have a macro with the syntax of a *case-of* -expression just with the difference that all *of-branches* are passed to -and processed by the macro implementation. It is then up the macro -implementation to transform the *of-branches* into a valid Nim -statement. The following example should show how this feature could be -used for a lexical analyzer. +Post-statement blocks +--------------------- + +Macros can receive `of`, `elif`, `else`, `except`, `finally` and `do` +blocks (including their different forms such as `do` with routine parameters) +as arguments if called in statement form. .. code-block:: nim - import std/macros + macro performWithUndo(task, undo: untyped) = ... - macro case_token(args: varargs[untyped]): untyped = - echo args.treeRepr - # creates a lexical analyzer from regular expressions - # ... (implementation is an exercise for the reader ;-) - discard - - case_token: # this colon tells the parser it is a macro statement - of r"[A-Za-z_]+[A-Za-z_0-9]*": - return tkIdentifier - of r"0-9+": - return tkInteger - of r"[\+\-\*\?]+": - return tkOperator + performWithUndo do: + # multiple-line block of code + # to perform the task + do: + # code to undo it + + let num = 12 + # a single colon may be used if there is no initial block + match (num mod 3, num mod 5): + of (0, 0): + echo "FizzBuzz" + of (0, _): + echo "Fizz" + of (_, 0): + echo "Buzz" else: - return tkUnknown - - -**Style note**: For code readability, it is best to use the least powerful -programming construct that still suffices. So the "check list" is: - -(1) Use an ordinary proc/iterator, if possible. -(2) Else: Use a generic proc/iterator, if possible. -(3) Else: Use a template, if possible. -(4) Else: Use a macro. + echo num For loop macro @@ -6072,6 +6069,48 @@ Another example: echo a, " ", b +Case statement macros +--------------------- + +Macros named `` `case` `` can provide implementations of `case` statements +for certain types. The following is an example of such an implementation +for tuples, leveraging the existing equality operator for tuples +(as provided in `system.==`): + +.. code-block:: nim + :test: "nim c $1" + import std/macros + + macro `case`(n: tuple): untyped = + result = newTree(nnkIfStmt) + let selector = n[0] + for i in 1 ..< n.len: + let it = n[i] + case it.kind + of nnkElse, nnkElifBranch, nnkElifExpr, nnkElseExpr: + result.add it + of nnkOfBranch: + for j in 0..it.len-2: + let cond = newCall("==", selector, it[j]) + result.add newTree(nnkElifBranch, cond, it[^1]) + else: + error "custom 'case' for tuple cannot handle this node", it + + case ("foo", 78) + of ("foo", 78): echo "yes" + of ("bar", 88): echo "no" + else: discard + +`case` macros are subject to overload resolution. The type of the +`case` statement's selector expression is matched against the type +of the first argument of the `case` macro. Then the complete `case` +statement is passed in place of the argument and the macro is evaluated. + +In other words, the macro needs to transform the full `case` statement +but only the statement's selector expression is used to determine which +macro to call. + + Special Types ============= @@ -6959,7 +6998,8 @@ experimental pragma The `experimental` pragma enables experimental language features. Depending on the concrete feature, this means that the feature is either considered too unstable for an otherwise stable release or that the future of the feature -is uncertain (it may be removed at any time). +is uncertain (it may be removed at any time). See the +`experimental manual `_ for more details. Example: @@ -7060,6 +7100,21 @@ alignment requirement of the type are ignored. This pragma has no effect on the JS backend. +Noalias pragma +============== + +Since version 1.4 of the Nim compiler, there is a `.noalias` annotation for variables +and parameters. It is mapped directly to C/C++'s `restrict`:c: keyword and means that +the underlying pointer is pointing to a unique location in memory, no other aliases to +this location exist. It is *unchecked* that this alias restriction is followed. If the +restriction is violated, the backend optimizer is free to miscompile the code. +This is an **unsafe** language feature. + +Ideally in later versions of the language, the restriction will be enforced at +compile time. (This is also why the name `noalias` was choosen instead of a more +verbose name like `unsafeAssumeNoAlias`.) + + Volatile pragma --------------- The `volatile` pragma is for variables only. It declares the variable as @@ -7641,7 +7696,7 @@ Example: {.pragma: rtl, importc, dynlib: "client.dll", cdecl.} proc p*(a, b: int): int {.rtl.} = - result = a+b + result = a + b In the example, a new pragma named `rtl` is introduced that either imports a symbol from a dynamic library or exports the symbol for dynamic library @@ -8032,3 +8087,137 @@ Threads and exceptions The interaction between threads and exceptions is simple: A *handled* exception in one thread cannot affect any other thread. However, an *unhandled* exception in one thread terminates the whole *process*. + + +Guards and locks +================ + +Nim provides common low level concurrency mechanisms like locks, atomic +intrinsics or condition variables. + +Nim significantly improves on the safety of these features via additional +pragmas: + +1) A `guard`:idx: annotation is introduced to prevent data races. +2) Every access of a guarded memory location needs to happen in an + appropriate `locks`:idx: statement. + + +Guards and locks sections +------------------------- + +Protecting global variables +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Object fields and global variables can be annotated via a `guard` pragma: + +.. code-block:: nim + + var glock: TLock + var gdata {.guard: glock.}: int + +The compiler then ensures that every access of `gdata` is within a `locks` +section: + +.. code-block:: nim + + proc invalid = + # invalid: unguarded access: + echo gdata + + proc valid = + # valid access: + {.locks: [glock].}: + echo gdata + +Top level accesses to `gdata` are always allowed so that it can be initialized +conveniently. It is *assumed* (but not enforced) that every top level statement +is executed before any concurrent action happens. + +The `locks` section deliberately looks ugly because it has no runtime +semantics and should not be used directly! It should only be used in templates +that also implement some form of locking at runtime: + +.. code-block:: nim + + template lock(a: TLock; body: untyped) = + pthread_mutex_lock(a) + {.locks: [a].}: + try: + body + finally: + pthread_mutex_unlock(a) + + +The guard does not need to be of any particular type. It is flexible enough to +model low level lockfree mechanisms: + +.. code-block:: nim + + var dummyLock {.compileTime.}: int + var atomicCounter {.guard: dummyLock.}: int + + template atomicRead(x): untyped = + {.locks: [dummyLock].}: + memoryReadBarrier() + x + + echo atomicRead(atomicCounter) + + +The `locks` pragma takes a list of lock expressions `locks: [a, b, ...]` +in order to support *multi lock* statements. Why these are essential is +explained in the `lock levels <#guards-and-locks-lock-levels>`_ section. + + +Protecting general locations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The `guard` annotation can also be used to protect fields within an object. +The guard then needs to be another field within the same object or a +global variable. + +Since objects can reside on the heap or on the stack, this greatly enhances +the expressivity of the language: + +.. code-block:: nim + + type + ProtectedCounter = object + v {.guard: L.}: int + L: TLock + + proc incCounters(counters: var openArray[ProtectedCounter]) = + for i in 0..counters.high: + lock counters[i].L: + inc counters[i].v + +The access to field `x.v` is allowed since its guard `x.L` is active. +After template expansion, this amounts to: + +.. code-block:: nim + + proc incCounters(counters: var openArray[ProtectedCounter]) = + for i in 0..counters.high: + pthread_mutex_lock(counters[i].L) + {.locks: [counters[i].L].}: + try: + inc counters[i].v + finally: + pthread_mutex_unlock(counters[i].L) + +There is an analysis that checks that `counters[i].L` is the lock that +corresponds to the protected location `counters[i].v`. This analysis is called +`path analysis`:idx: because it deals with paths to locations +like `obj.field[i].fieldB[j]`. + +The path analysis is **currently unsound**, but that doesn't make it useless. +Two paths are considered equivalent if they are syntactically the same. + +This means the following compiles (for now) even though it really should not: + +.. code-block:: nim + + {.locks: [a[i].L].}: + inc i + access a[i].v diff --git a/doc/manual_experimental.rst b/doc/manual_experimental.rst index 458512292..4ed8439df 100644 --- a/doc/manual_experimental.rst +++ b/doc/manual_experimental.rst @@ -19,47 +19,8 @@ Some of these are not covered by the `.experimental` pragma or one may want to use Nim libraries using these features without using them oneself. -**Note**: Unless otherwise indicated, these features are not to be removed, -but refined and overhauled. - - -Package level objects -===================== - -Every Nim module resides in a (nimble) package. An object type can be attached -to the package it resides in. If that is done, the type can be referenced from -other modules as an `incomplete`:idx: object type. This feature allows to -break up recursive type dependencies across module boundaries. Incomplete -object types are always passed `byref` and can only be used in pointer like -contexts (`var/ref/ptr IncompleteObject`) in general since the compiler does -not yet know the size of the object. To complete an incomplete object -the `package` pragma has to be used. `package` implies `byref`. - -As long as a type `T` is incomplete, neither `sizeof(T)` nor runtime -type information for `T` is available. - - -Example: - -.. code-block:: nim - - # module A (in an arbitrary package) - type - Pack.SomeObject = object ## declare as incomplete object of package 'Pack' - Triple = object - a, b, c: ref SomeObject ## pointers to incomplete objects are allowed - - ## Incomplete objects can be used as parameters: - proc myproc(x: SomeObject) = discard - - -.. code-block:: nim - - # module B (in package "Pack") - type - SomeObject* {.package.} = object ## Use 'package' to complete the object - s, t: string - x, y: int +.. note:: Unless otherwise indicated, these features are not to be removed, + but refined and overhauled. Void type @@ -105,23 +66,106 @@ The `void` type is only valid for parameters and return types; other symbols cannot have the type `void`. -Automatic dereferencing -======================= +Unicode Operators +================= -Automatic dereferencing is performed for the first argument of a routine call. -This feature has to be enabled via `{.experimental: "implicitDeref".}`: +Under the `--experimental:unicodeOperators`:option: switch, +these Unicode operators are also parsed as operators:: + + ∙ ∘ × ★ ⊗ ⊘ ⊙ ⊛ ⊠ ⊡ ∩ ∧ ⊓ # same priority as * (multiplication) + ± ⊕ ⊖ ⊞ ⊟ ∪ ∨ ⊔ # same priority as + (addition) + + +If enabled, Unicode operators can be combined with non-Unicode operator +symbols. The usual precedence extensions then apply, for example, `⊠=` is an +assignment like operator just like `*=` is. + +No Unicode normalization step is performed. + +.. note:: Due to parser limitations one **cannot** enable this feature via a + pragma `{.experimental: "unicodeOperators".}` reliably. + + +Overloadable enum value names +============================= + +Enabled via `{.experimental: "overloadableEnums".}`. + +Enum value names are overloadable, much like routines. If both of the enums +`T` and `U` have a member named `foo`, then the identifier `foo` corresponds +to a choice between `T.foo` and `U.foo`. During overload resolution, +the correct type of `foo` is decided from the context. If the type of `foo` is +ambiguous, a static error will be produced. + +.. code-block:: nim + :test: "nim c $1" + + {.experimental: "overloadableEnums".} + + type + E1 = enum + value1, + value2 + E2 = enum + value1, + value2 = 4 + + const + Lookuptable = [ + E1.value1: "1", + # no need to qualify value2, known to be E1.value2 + value2: "2" + ] + + proc p(e: E1) = + # disambiguation in 'case' statements: + case e + of value1: echo "A" + of value2: echo "B" + + p value2 + + +Package level objects +===================== + +Every Nim module resides in a (nimble) package. An object type can be attached +to the package it resides in. If that is done, the type can be referenced from +other modules as an `incomplete`:idx: object type. This feature allows to +break up recursive type dependencies across module boundaries. Incomplete +object types are always passed `byref` and can only be used in pointer like +contexts (`var/ref/ptr IncompleteObject`) in general, since the compiler does +not yet know the size of the object. To complete an incomplete object, +the `package` pragma has to be used. `package` implies `byref`. + +As long as a type `T` is incomplete, no runtime type information for `T` is +available. + + +Example: .. code-block:: nim - {.experimental: "implicitDeref".} + # module A (in an arbitrary package) + type + Pack.SomeObject = object # declare as incomplete object of package 'Pack' + Triple = object + a, b, c: ref SomeObject # pointers to incomplete objects are allowed - proc depth(x: NodeObj): int = ... + # Incomplete objects can be used as parameters: + proc myproc(x: SomeObject) = discard - var - n: Node - new(n) - echo n.depth - # no need to write n[].depth either + +.. code-block:: nim + + # module B (in package "Pack") + type + SomeObject* {.package.} = object # Use 'package' to complete the object + s, t: string + x, y: int + +This feature will likely be superseded in the future by support for +recursive module dependencies. Code reordering @@ -230,65 +274,30 @@ scope. Therefore, the following will *fail to compile:* a() - -Named argument overloading -========================== - -Routines with the same type signature can be called differently if a parameter -has different names. This does not need an `experimental` switch, but is an -unstable feature. - -.. code-block:: Nim - - proc foo(x: int) = - echo "Using x: ", x - proc foo(y: int) = - echo "Using y: ", y - - foo(x = 2) - # Using x: 2 - foo(y = 2) - # Using y: 2 +This feature will likely be replaced with a better solution to remove +the need for forward declarations. -Do notation -=========== +Automatic dereferencing +======================= -As a special more convenient notation, proc expressions involved in procedure -calls can use the `do` keyword: +Automatic dereferencing is performed for the first argument of a routine call. +This feature has to be enabled via `{.experimental: "implicitDeref".}`: .. code-block:: nim - sort(cities) do (x,y: string) -> int: - cmp(x.len, y.len) + {.experimental: "implicitDeref".} - # Less parenthesis using the method plus command syntax: - cities = cities.map do (x:string) -> string: - "City of " & x + type + NodeObj = object + # ... + Node = ref NodeObj - # In macros, the do notation is often used for quasi-quoting - macroResults.add quote do: - if not `ex`: - echo `info`, ": Check failed: ", `expString` + proc depth(x: NodeObj): int = ... -`do` is written after the parentheses enclosing the regular proc params. -The proc expression represented by the do block is appended to them. -In calls using the command syntax, the do block will bind to the immediately -preceding expression, transforming it in a call. - -`do` with parentheses is an anonymous `proc`; however a `do` without -parentheses is just a block of code. The `do` notation can be used to -pass multiple blocks to a macro: - -.. code-block:: nim - - macro performWithUndo(task, undo: untyped) = ... - - performWithUndo do: - # multiple-line block of code - # to perform the task - do: - # code to undo it + let n = Node() + echo n.depth + # no need to write n[].depth Special Operators @@ -297,8 +306,8 @@ Special Operators dot operators ------------- -**Note**: Dot operators are still experimental and so need to be enabled -via `{.experimental: "dotOperators".}`. +.. note:: Dot operators are still experimental and so need to be enabled + via `{.experimental: "dotOperators".}`. Nim offers a special family of dot operators that can be used to intercept and rewrite proc call and field access attempts, referring @@ -418,6 +427,314 @@ here. .. include:: manual_experimental_strictnotnil.rst +Aliasing restrictions in parameter passing +========================================== + +.. note:: The aliasing restrictions are currently not enforced by the + implementation and need to be fleshed out further. + +"Aliasing" here means that the underlying storage locations overlap in memory +at runtime. An "output parameter" is a parameter of type `var T`, +an input parameter is any parameter that is not of type `var`. + +1. Two output parameters should never be aliased. +2. An input and an output parameter should not be aliased. +3. An output parameter should never be aliased with a global or thread local + variable referenced by the called proc. +4. An input parameter should not be aliased with a global or thread local + variable updated by the called proc. + +One problem with rules 3 and 4 is that they affect specific global or thread +local variables, but Nim's effect tracking only tracks "uses no global variable" +via `.noSideEffect`. The rules 3 and 4 can also be approximated by a different rule: + +5. A global or thread local variable (or a location derived from such a location) + can only passed to a parameter of a `.noSideEffect` proc. + + +Strict funcs +============ + +Since version 1.4, a stricter definition of "side effect" is available. +In addition to the existing rule that a side effect is calling a function +with side effects, the following rule is also enforced: + +Any mutation to an object does count as a side effect if that object is reachable +via a parameter that is not declared as a `var` parameter. + +For example: + +.. code-block:: nim + + {.experimental: "strictFuncs".} + + type + Node = ref object + le, ri: Node + data: string + + func len(n: Node): int = + # valid: len does not have side effects + var it = n + while it != nil: + inc result + it = it.ri + + func mut(n: Node) = + let m = n # is the statement that connected the mutation to the parameter + m.data = "yeah" # the mutation is here + # Error: 'mut' can have side effects + # an object reachable from 'n' is potentially mutated + + +The algorithm behind this analysis is described in +the `view types section <#view-types-algorithm>`_. + + +View types +========== + +.. tip:: `--experimental:views`:option: is more effective + with `--experimental:strictFuncs`:option:. + +A view type is a type that is or contains one of the following types: + +- `lent T` (view into `T`) +- `openArray[T]` (pair of (pointer to array of `T`, size)) + +For example: + +.. code-block:: nim + + type + View1 = openArray[byte] + View2 = lent string + View3 = Table[openArray[char], int] + + +Exceptions to this rule are types constructed via `ptr` or `proc`. +For example, the following types are **not** view types: + +.. code-block:: nim + + type + NotView1 = proc (x: openArray[int]) + NotView2 = ptr openArray[char] + NotView3 = ptr array[4, lent int] + + +The mutability aspect of a view type is not part of the type but part +of the locations it's derived from. More on this later. + +A *view* is a symbol (a let, var, const, etc.) that has a view type. + +Since version 1.4, Nim allows view types to be used as local variables. +This feature needs to be enabled via `{.experimental: "views".}`. + +A local variable of a view type *borrows* from the locations and +it is statically enforced that the view does not outlive the location +it was borrowed from. + +For example: + +.. code-block:: nim + + {.experimental: "views".} + + proc take(a: openArray[int]) = + echo a.len + + proc main(s: seq[int]) = + var x: openArray[int] = s # 'x' is a view into 's' + # it is checked that 'x' does not outlive 's' and + # that 's' is not mutated. + for i in 0 .. high(x): + echo x[i] + take(x) + + take(x.toOpenArray(0, 1)) # slicing remains possible + let y = x # create a view from a view + take y + # it is checked that 'y' does not outlive 'x' and + # that 'x' is not mutated as long as 'y' lives. + + + main(@[11, 22, 33]) + + +A local variable of a view type can borrow from a location +derived from a parameter, another local variable, a global `const` or `let` +symbol or a thread-local `var` or `let`. + +Let `p` the proc that is analysed for the correctness of the borrow operation. + +Let `source` be one of: + +- A formal parameter of `p`. Note that this does not cover parameters of + inner procs. +- The `result` symbol of `p`. +- A local `var` or `let` or `const` of `p`. Note that this does + not cover locals of inner procs. +- A thread-local `var` or `let`. +- A global `let` or `const`. +- A constant array/seq/object/tuple constructor. + + +Path expressions +---------------- + +A location derived from `source` is then defined as a path expression that +has `source` as the owner. A path expression `e` is defined recursively: + +- `source` itself is a path expression. +- Container access like `e[i]` is a path expression. +- Tuple access `e[0]` is a path expression. +- Object field access `e.field` is a path expression. +- `system.toOpenArray(e, ...)` is a path expression. +- Pointer dereference `e[]` is a path expression. +- An address `addr e`, `unsafeAddr e` is a path expression. +- A type conversion `T(e)` is a path expression. +- A cast expression `cast[T](e)` is a path expression. +- `f(e, ...)` is a path expression if `f`'s return type is a view type. + Because the view can only have been borrowed from `e`, we then know + that the owner of `f(e, ...)` is `e`. + + +If a view type is used as a return type, the location must borrow from a location +that is derived from the first parameter that is passed to the proc. +See `the manual `_ +for details about how this is done for `var T`. + +A mutable view can borrow from a mutable location, an immutable view can borrow +from both a mutable or an immutable location. + +If a view borrows from a mutable location, the view can be used to update the +location. Otherwise it cannot be used for mutations. + +The *duration* of a borrow is the span of commands beginning from the assignment +to the view and ending with the last usage of the view. + +For the duration of the borrow operation, no mutations to the borrowed locations +may be performed except via the view that borrowed from the +location. The borrowed location is said to be *sealed* during the borrow. + +.. code-block:: nim + + {.experimental: "views".} + + type + Obj = object + field: string + + proc dangerous(s: var seq[Obj]) = + let v: lent Obj = s[0] # seal 's' + s.setLen 0 # prevented at compile-time because 's' is sealed. + echo v.field + + +The scope of the view does not matter: + +.. code-block:: nim + + proc valid(s: var seq[Obj]) = + let v: lent Obj = s[0] # begin of borrow + echo v.field # end of borrow + s.setLen 0 # valid because 'v' isn't used afterwards + + +The analysis requires as much precision about mutations as is reasonably obtainable, +so it is more effective with the experimental `strict funcs <#strict-funcs>`_ +feature. In other words `--experimental:views`:option: works better +with `--experimental:strictFuncs`:option:. + +The analysis is currently control flow insensitive: + +.. code-block:: nim + + proc invalid(s: var seq[Obj]) = + let v: lent Obj = s[0] + if false: + s.setLen 0 + echo v.field + +In this example, the compiler assumes that `s.setLen 0` invalidates the +borrow operation of `v` even though a human being can easily see that it +will never do that at runtime. + + +Start of a borrow +----------------- + +A borrow starts with one of the following: + +- The assignment of a non-view-type to a view-type. +- The assignment of a location that is derived from a local parameter + to a view-type. + + +End of a borrow +--------------- + +A borrow operation ends with the last usage of the view variable. + + +Reborrows +--------- + +A view `v` can borrow from multiple different locations. However, the borrow +is always the full span of `v`'s lifetime and every location that is borrowed +from is sealed during `v`'s lifetime. + + +Algorithm +--------- + +The following section is an outline of the algorithm that the current implementation +uses. The algorithm performs two traversals over the AST of the procedure or global +section of code that uses a view variable. No fixpoint iterations are performed, the +complexity of the analysis is O(N) where N is the number of nodes of the AST. + +The first pass over the AST computes the lifetime of each local variable based on +a notion of an "abstract time", in the implementation it's a simple integer that is +incremented for every visited node. + +In the second pass, information about the underlying object "graphs" is computed. +Let `v` be a parameter or a local variable. Let `G(v)` be the graph +that `v` belongs to. A graph is defined by the set of variables that belong +to the graph. Initially for all `v`: `G(v) = {v}`. Every variable can only +be part of a single graph. + +Assignments like `a = b` "connect" two variables, both variables end up in the +same graph `{a, b} = G(a) = G(b)`. Unfortunately, the pattern to look for is +much more complex than that and can involve multiple assignment targets +and sources:: + + f(x, y) = g(a, b) + +connects `x` and `y` to `a` and `b`: `G(x) = G(y) = G(a) = G(b) = {x, y, a, b}`. +A type based alias analysis rules out some of these combinations, for example +a `string` value cannot possibly be connected to a `seq[int]`. + +A pattern like `v[] = value` or `v.field = value` marks `G(v)` as mutated. +After the second pass a set of disjoint graphs was computed. + +For strict functions it is then enforced that there is no graph that is both mutated +and has an element that is an immutable parameter (that is a parameter that is not +of type `var T`). + +For borrow checking, a different set of checks is performed. Let `v` be the view +and `b` the location that is borrowed from. + +- The lifetime of `v` must not exceed `b`'s lifetime. Note: The lifetime of + a parameter is the complete proc body. +- If `v` is used for a mutation, `b` must be a mutable location too. +- During `v`'s lifetime, `G(b)` can only be modified by `v` (and only if + `v` is a mutable view). +- If `v` is `result` then `b` has to be a location derived from the first + formal parameter or from a constant location. +- A view cannot be used for a read or a write access before it was assigned to. + + Concepts ======== @@ -441,10 +758,10 @@ Concepts are written in the following form: for value in s: value is T -The concept is a match if: +The concept matches if: -a) all of the expressions within the body can be compiled for the tested type -b) all statically evaluable boolean expressions in the body must be true +a) all expressions within the body can be compiled for the tested type +b) all statically evaluable boolean expressions in the body are true The identifiers following the `concept` keyword represent instances of the currently matched type. You can apply any of the standard type modifiers such @@ -874,15 +1191,17 @@ There are 4 operations that are bound to a type: 3. Destruction 4. Deep copying for communication between threads -These operations can be *overridden* instead of *overloaded*. This means the -implementation is automatically lifted to structured types. For instance if type -`T` has an overridden assignment operator `=` this operator is also used -for assignments of the type `seq[T]`. Since these operations are bound to a -type they have to be bound to a nominal type for reasons of simplicity of -implementation: This means an overridden `deepCopy` for `ref T` is really -bound to `T` and not to `ref T`. This also means that one cannot override -`deepCopy` for both `ptr T` and `ref T` at the same time; instead a -helper distinct or object type has to be used for one pointer type. +These operations can be *overridden* instead of *overloaded*. This means that +the implementation is automatically lifted to structured types. For instance, +if the type `T` has an overridden assignment operator `=`, this operator is +also used for assignments of the type `seq[T]`. + +Since these operations are bound to a type, they have to be bound to a +nominal type for reasons of simplicity of implementation; this means an +overridden `deepCopy` for `ref T` is really bound to `T` and not to `ref T`. +This also means that one cannot override `deepCopy` for both `ptr T` and +`ref T` at the same time, instead a distinct or object helper type has to be +used for one pointer type. Assignments, moves and destruction are specified in the `destructors `_ document. @@ -902,60 +1221,29 @@ The signature has to be: proc `=deepCopy`(x: T): T -This mechanism will be used by most data structures that support shared memory -like channels to implement thread safe automatic memory management. +This mechanism will be used by most data structures that support shared memory, +like channels, to implement thread safe automatic memory management. The builtin `deepCopy` can even clone closures and their environments. See the documentation of `spawn <#parallel-amp-spawn-spawn-statement>`_ for details. -Case statement macros -===================== +Dynamic arguments for bindSym +============================= -Macros named `case` can rewrite `case` statements for certain types in order to -implement `pattern matching`:idx:. The following example implements a -simplistic form of pattern matching for tuples, leveraging the existing -equality operator for tuples (as provided in `system.==`): +This experimental feature allows the symbol name argument of `macros.bindSym` +to be computed dynamically. .. code-block:: nim - :test: "nim c $1" + {.experimental: "dynamicBindSym".} - {.experimental: "caseStmtMacros".} + import macros - import std/macros + macro callOp(opName, arg1, arg2): untyped = + result = newCall(bindSym($opName), arg1, arg2) - macro `case`(n: tuple): untyped = - result = newTree(nnkIfStmt) - let selector = n[0] - for i in 1 ..< n.len: - let it = n[i] - case it.kind - of nnkElse, nnkElifBranch, nnkElifExpr, nnkElseExpr: - result.add it - of nnkOfBranch: - for j in 0..it.len-2: - let cond = newCall("==", selector, it[j]) - result.add newTree(nnkElifBranch, cond, it[^1]) - else: - error "custom 'case' for tuple cannot handle this node", it - - case ("foo", 78) - of ("foo", 78): echo "yes" - of ("bar", 88): echo "no" - else: discard - - -Currently case statement macros must be enabled explicitly -via `{.experimental: "caseStmtMacros".}`. - -`case` macros are subject to overload resolution. The type of the -`case` statement's selector expression is matched against the type -of the first argument of the `case` macro. Then the complete `case` -statement is passed in place of the argument and the macro is evaluated. - -In other words, the macro needs to transform the full `case` statement -but only the statement's selector expression is used to determine which -macro to call. + echo callOp("+", 1, 2) + echo callOp("-", 5, 4) Term rewriting macros @@ -968,30 +1256,30 @@ compilation pipeline with user defined optimizations: .. code-block:: nim - template optMul{`*`(a, 2)}(a: int): int = a+a + template optMul{`*`(a, 2)}(a: int): int = a + a let x = 3 echo x * 2 The compiler now rewrites `x * 2` as `x + x`. The code inside the -curlies is the pattern to match against. The operators `*`, `**`, +curly brackets is the pattern to match against. The operators `*`, `**`, `|`, `~` have a special meaning in patterns if they are written in infix notation, so to match verbatim against `*` the ordinary function call syntax needs to be used. -Term rewriting macro are applied recursively, up to a limit. This means that +Term rewriting macros are applied recursively, up to a limit. This means that if the result of a term rewriting macro is eligible for another rewriting, the compiler will try to perform it, and so on, until no more optimizations are applicable. To avoid putting the compiler into an infinite loop, there is a hard limit on how many times a single term rewriting macro can be applied. Once this limit has been passed, the term rewriting macro will be ignored. -Unfortunately optimizations are hard to get right and even the tiny example +Unfortunately optimizations are hard to get right and even this tiny example is **wrong**: .. code-block:: nim - template optMul{`*`(a, 2)}(a: int): int = a+a + template optMul{`*`(a, 2)}(a: int): int = a + a proc f(): int = echo "side effect!" @@ -1004,7 +1292,7 @@ Fortunately Nim supports side effect analysis: .. code-block:: nim - template optMul{`*`(a, 2)}(a: int{noSideEffect}): int = a+a + template optMul{`*`(a, 2)}(a: int{noSideEffect}): int = a + a proc f(): int = echo "side effect!" @@ -1022,13 +1310,13 @@ blindly: .. code-block:: nim - template mulIsCommutative{`*`(a, b)}(a, b: int): int = b*a + template mulIsCommutative{`*`(a, b)}(a, b: int): int = b * a What optimizers really need to do is a *canonicalization*: .. code-block:: nim - template canonMul{`*`(a, b)}(a: int{lit}, b: int): int = b*a + template canonMul{`*`(a, b)}(a: int{lit}, b: int): int = b * a The `int{lit}` parameter pattern matches against an expression of type `int`, but only if it's a literal. @@ -1143,7 +1431,7 @@ constant folding, so the following does not work: The reason is that the compiler already transformed the 1 into "1" for the `echo` statement. However, a term rewriting macro should not change the -semantics anyway. In fact they can be deactivated with the `--patterns:off`:option: +semantics anyway. In fact, they can be deactivated with the `--patterns:off`:option: command line option or temporarily with the `patterns` pragma. @@ -1155,7 +1443,7 @@ notation: .. code-block:: nim - template t{(0|1|2){x}}(x: untyped): untyped = x+1 + template t{(0|1|2){x}}(x: untyped): untyped = x + 1 let a = 1 # outputs 2: echo a @@ -1164,7 +1452,7 @@ notation: The `~` operator ~~~~~~~~~~~~~~~~~~ -The `~` operator is the **not** operator in patterns: +The `~` operator is the 'not' operator in patterns: .. code-block:: nim @@ -1257,14 +1545,14 @@ an `nnkArgList` node containing:: Sym "x" Sym "-" -(Which is the reverse polish notation of `x + y * z - x`.) +(This is the reverse polish notation of `x + y * z - x`.) Parameters ---------- Parameters in a pattern are type checked in the matching process. If a -parameter is of the type `varargs` it is treated specially and it can match +parameter is of the type `varargs`, it is treated specially and can match 0 or more arguments in the AST to be matched against: .. code-block:: nim @@ -1276,6 +1564,27 @@ parameter is of the type `varargs` it is treated specially and it can match w(f, x, y) +noRewrite pragma +---------------- + +Term rewriting macros and templates are currently greedy and +they will rewrite as long as there is a match. +There was no way to ensure some rewrite happens only once, +e.g. when rewriting term to same term plus extra content. + +`noRewrite` pragma can actually prevent further rewriting on marked code, +e.g. with given example `echo("ab")` will be rewritten just once: + +.. code-block:: nim + + template pwnEcho{echo(x)}(x: untyped) = + {.noRewrite.}: echo("pwned!") + + echo "ab" + +`noRewrite` pragma can be useful to control term-rewriting macros recursion. + + Example: Partial evaluation --------------------------- @@ -1319,7 +1628,7 @@ AST based overloading ===================== Parameter constraints can also be used for ordinary routine parameters; these -constraints affect ordinary overloading resolution then: +constraints then affect ordinary overloading resolution: .. code-block:: nim @@ -1356,22 +1665,25 @@ module to work. Somewhat confusingly, `spawn` is also used in the `parallel` statement with slightly different semantics. `spawn` always takes a call expression of -the form `f(a, ...)`. Let `T` be `f`'s return type. If `T` is `void` -then `spawn`'s return type is also `void` otherwise it is `FlowVar[T]`. +the form `f(a, ...)`. Let `T` be `f`'s return type. If `T` is `void`, +then `spawn`'s return type is also `void`, otherwise it is `FlowVar[T]`. -Within a `parallel` section sometimes the `FlowVar[T]` is eliminated +Within a `parallel` section, the `FlowVar[T]` is sometimes eliminated to `T`. This happens when `T` does not contain any GC'ed memory. The compiler can ensure the location in `location = spawn f(...)` is not read prematurely within a `parallel` section and so there is no need for the overhead of an indirection via `FlowVar[T]` to ensure correctness. -**Note**: Currently exceptions are not propagated between `spawn`'ed tasks! +.. note:: Currently exceptions are not propagated between `spawn`'ed tasks! + +This feature is likely to be removed in the future as external packages +can have better solutions. Spawn statement --------------- -`spawn`:idx: can be used to pass a task to the thread pool: +The `spawn`:idx: statement can be used to pass a task to the thread pool: .. code-block:: nim @@ -1393,10 +1705,10 @@ that `spawn` takes is restricted: * `f`'s parameters may not be of type `var`. This means one has to use raw `ptr`'s for data passing reminding the programmer to be careful. -* `ref` parameters are deeply copied which is a subtle semantic change and - can cause performance problems but ensures memory safety. This deep copy - is performed via `system.deepCopy` and so can be overridden. -* For *safe* data exchange between `f` and the caller a global `TChannel` +* `ref` parameters are deeply copied, which is a subtle semantic change and + can cause performance problems, but ensures memory safety. This deep copy + is performed via `system.deepCopy`, so it can be overridden. +* For *safe* data exchange between `f` and the caller, a global `Channel` needs to be used. However, since spawn can return a result, often no further communication is required. @@ -1420,10 +1732,10 @@ wait on multiple flow variables at the same time: responses.del(index) discard blockUntilAny(responses) -Data flow variables ensure that no data races -are possible. Due to technical limitations not every type `T` is possible in -a data flow variable: `T` has to be of the type `ref`, `string`, `seq` -or of a type that doesn't contain a type that is garbage collected. This +Data flow variables ensure that no data races are possible. Due to +technical limitations, not every type `T` can be used in +a data flow variable: `T` has to be a `ref`, `string`, `seq` +or of a type that doesn't contain any GC'd type. This restriction is not hard to work-around in practice. @@ -1436,14 +1748,14 @@ Example: .. code-block:: nim :test: "nim c --threads:on $1" - # Compute PI in an inefficient way + # Compute pi in an inefficient way import std/[strutils, math, threadpool] {.experimental: "parallel".} proc term(k: float): float = 4 * math.pow(-1, k) / (2*k + 1) proc pi(n: int): float = - var ch = newSeq[float](n+1) + var ch = newSeq[float](n + 1) parallel: for k in 0..ch.high: ch[k] = spawn term(float(k)) @@ -1454,16 +1766,16 @@ Example: The parallel statement is the preferred mechanism to introduce parallelism in a -Nim program. A subset of the Nim language is valid within a `parallel` +Nim program. Only a subset of the Nim language is valid within a `parallel` section. This subset is checked during semantic analysis to be free of data races. A sophisticated `disjoint checker`:idx: ensures that no data races are -possible even though shared memory is extensively supported! +possible, even though shared memory is extensively supported! The subset is in fact the full language with the following restrictions / changes: * `spawn` within a `parallel` section has special semantics. -* Every location of the form `a[i]` and `a[i..j]` and `dest` where +* Every location of the form `a[i]`, `a[i..j]` and `dest` where `dest` is part of the pattern `dest = spawn f(...)` has to be provably disjoint. This is called the *disjoint check*. * Every other complex location `loc` that is used in a spawned @@ -1477,152 +1789,16 @@ restrictions / changes: yet performed for ordinary slices outside of a `parallel` section. -Guards and locks -================ - -Apart from `spawn` and `parallel` Nim also provides all the common low level -concurrency mechanisms like locks, atomic intrinsics or condition variables. - -Nim significantly improves on the safety of these features via additional -pragmas: - -1) A `guard`:idx: annotation is introduced to prevent data races. -2) Every access of a guarded memory location needs to happen in an - appropriate `locks`:idx: statement. -3) Locks and routines can be annotated with `lock levels`:idx: to allow - potential deadlocks to be detected during semantic analysis. - - -Guards and the locks section ----------------------------- - -Protecting global variables -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Object fields and global variables can be annotated via a `guard` pragma: - -.. code-block:: nim - - var glock: TLock - var gdata {.guard: glock.}: int - -The compiler then ensures that every access of `gdata` is within a `locks` -section: - -.. code-block:: nim - - proc invalid = - # invalid: unguarded access: - echo gdata - - proc valid = - # valid access: - {.locks: [glock].}: - echo gdata - -Top level accesses to `gdata` are always allowed so that it can be initialized -conveniently. It is *assumed* (but not enforced) that every top level statement -is executed before any concurrent action happens. - -The `locks` section deliberately looks ugly because it has no runtime -semantics and should not be used directly! It should only be used in templates -that also implement some form of locking at runtime: - -.. code-block:: nim - - template lock(a: TLock; body: untyped) = - pthread_mutex_lock(a) - {.locks: [a].}: - try: - body - finally: - pthread_mutex_unlock(a) - - -The guard does not need to be of any particular type. It is flexible enough to -model low level lockfree mechanisms: - -.. code-block:: nim - - var dummyLock {.compileTime.}: int - var atomicCounter {.guard: dummyLock.}: int - - template atomicRead(x): untyped = - {.locks: [dummyLock].}: - memoryReadBarrier() - x - - echo atomicRead(atomicCounter) - - -The `locks` pragma takes a list of lock expressions `locks: [a, b, ...]` -in order to support *multi lock* statements. Why these are essential is -explained in the `lock levels <#guards-and-locks-lock-levels>`_ section. - - -Protecting general locations -~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -The `guard` annotation can also be used to protect fields within an object. -The guard then needs to be another field within the same object or a -global variable. - -Since objects can reside on the heap or on the stack this greatly enhances the -expressivity of the language: - -.. code-block:: nim - - type - ProtectedCounter = object - v {.guard: L.}: int - L: TLock - - proc incCounters(counters: var openArray[ProtectedCounter]) = - for i in 0..counters.high: - lock counters[i].L: - inc counters[i].v - -The access to field `x.v` is allowed since its guard `x.L` is active. -After template expansion, this amounts to: - -.. code-block:: nim - - proc incCounters(counters: var openArray[ProtectedCounter]) = - for i in 0..counters.high: - pthread_mutex_lock(counters[i].L) - {.locks: [counters[i].L].}: - try: - inc counters[i].v - finally: - pthread_mutex_unlock(counters[i].L) - -There is an analysis that checks that `counters[i].L` is the lock that -corresponds to the protected location `counters[i].v`. This analysis is called -`path analysis`:idx: because it deals with paths to locations -like `obj.field[i].fieldB[j]`. - -The path analysis is **currently unsound**, but that doesn't make it useless. -Two paths are considered equivalent if they are syntactically the same. - -This means the following compiles (for now) even though it really should not: - -.. code-block:: nim - - {.locks: [a[i].L].}: - inc i - access a[i].v - - Lock levels ------------ +=========== Lock levels are used to enforce a global locking order in order to detect potential deadlocks during semantic analysis. A lock level is an constant integer in the range 0..1_000. Lock level 0 means that no lock is acquired at all. -If a section of code holds a lock of level `M` than it can also acquire any +If a section of code holds a lock of level `M`, it can also acquire any lock of level `N < M`. Another lock of level `M` cannot be acquired. Locks of the same level can only be acquired *at the same time* within a single `locks` section: @@ -1685,7 +1861,7 @@ This is essential so that procs can be called within a `locks` section: p() -As usual `locks` is an inferred effect and there is a subtype +As usual, `locks` is an inferred effect and there is a subtype relation: `proc () {.locks: N.}` is a subtype of `proc () {.locks: M.}` iff (M <= N). @@ -1709,346 +1885,4 @@ having unknown lock level as well: if g.memberProc != nil: g.memberProc() - -noRewrite pragma ----------------- - -Term rewriting macros and templates are currently greedy and -they will rewrite as long as there is a match. -There was no way to ensure some rewrite happens only once, -e.g. when rewriting term to same term plus extra content. - -`noRewrite` pragma can actually prevent further rewriting on marked code, -e.g. with given example `echo("ab")` will be rewritten just once: - -.. code-block:: nim - - template pwnEcho{echo(x)}(x: untyped) = - {.noRewrite.}: echo("pwned!") - - echo "ab" - -`noRewrite` pragma can be useful to control term-rewriting macros recursion. - - -Aliasing restrictions in parameter passing -========================================== - -**Note**: The aliasing restrictions are currently not enforced by the -implementation and need to be fleshed out further. - -"Aliasing" here means that the underlying storage locations overlap in memory -at runtime. An "output parameter" is a parameter of type `var T`, -an input parameter is any parameter that is not of type `var`. - -1. Two output parameters should never be aliased. -2. An input and an output parameter should not be aliased. -3. An output parameter should never be aliased with a global or thread local - variable referenced by the called proc. -4. An input parameter should not be aliased with a global or thread local - variable updated by the called proc. - -One problem with rules 3 and 4 is that they affect specific global or thread -local variables, but Nim's effect tracking only tracks "uses no global variable" -via `.noSideEffect`. The rules 3 and 4 can also be approximated by a different rule: - -5. A global or thread local variable (or a location derived from such a location) - can only passed to a parameter of a `.noSideEffect` proc. - - -Noalias annotation -================== - -Since version 1.4 of the Nim compiler, there is a `.noalias` annotation for variables -and parameters. It is mapped directly to C/C++'s `restrict`:c: keyword and means that -the underlying pointer is pointing to a unique location in memory, no other aliases to -this location exist. It is *unchecked* that this alias restriction is followed, if the -restriction is violated, the backend optimizer is free to miscompile the code. -This is an **unsafe** language feature. - -Ideally in later versions of the language, the restriction will be enforced at -compile time. (Which is also why the name `noalias` was choosen instead of a more -verbose name like `unsafeAssumeNoAlias`.) - - -Strict funcs -============ - -Since version 1.4 a stricter definition of "side effect" is available. In addition -to the existing rule that a side effect is calling a function with side effects -the following rule is also enforced: - -Any mutation to an object does count as a side effect if that object is reachable -via a parameter that is not declared as a `var` parameter. - -For example: - -.. code-block:: nim - - {.experimental: "strictFuncs".} - - type - Node = ref object - le, ri: Node - data: string - - func len(n: Node): int = - # valid: len does not have side effects - var it = n - while it != nil: - inc result - it = it.ri - - func mut(n: Node) = - let m = n # is the statement that connected the mutation to the parameter - m.data = "yeah" # the mutation is here - # Error: 'mut' can have side effects - # an object reachable from 'n' is potentially mutated - - -The algorithm behind this analysis is described in -the `view types section <#view-types-algorithm>`_. - - -View types -========== - -**Note**: `--experimental:views`:option: is more effective -with `--experimental:strictFuncs`:option:. - -A view type is a type that is or contains one of the following types: - -- `lent T` (view into `T`) -- `openArray[T]` (pair of (pointer to array of `T`, size)) - -For example: - -.. code-block:: nim - - type - View1 = openArray[byte] - View2 = lent string - View3 = Table[openArray[char], int] - - -Exceptions to this rule are types constructed via `ptr` or `proc`. -For example, the following types are **not** view types: - -.. code-block:: nim - - type - NotView1 = proc (x: openArray[int]) - NotView2 = ptr openArray[char] - NotView3 = ptr array[4, lent int] - - -The mutability aspect of a view type is not part of the type but part -of the locations it's derived from. More on this later. - -A *view* is a symbol (a let, var, const, etc.) that has a view type. - -Since version 1.4 Nim allows view types to be used as local variables. -This feature needs to be enabled via `{.experimental: "views".}`. - -A local variable of a view type *borrows* from the locations and -it is statically enforced that the view does not outlive the location -it was borrowed from. - -For example: - -.. code-block:: nim - - {.experimental: "views".} - - proc take(a: openArray[int]) = - echo a.len - - proc main(s: seq[int]) = - var x: openArray[int] = s # 'x' is a view into 's' - # it is checked that 'x' does not outlive 's' and - # that 's' is not mutated. - for i in 0 .. high(x): - echo x[i] - take(x) - - take(x.toOpenArray(0, 1)) # slicing remains possible - let y = x # create a view from a view - take y - # it is checked that 'y' does not outlive 'x' and - # that 'x' is not mutated as long as 'y' lives. - - - main(@[11, 22, 33]) - - -A local variable of a view type can borrow from a location -derived from a parameter, another local variable, a global `const` or `let` -symbol or a thread-local `var` or `let`. - -Let `p` the proc that is analysed for the correctness of the borrow operation. - -Let `source` be one of: - -- A formal parameter of `p`. Note that this does not cover parameters of - inner procs. -- The `result` symbol of `p`. -- A local `var` or `let` or `const` of `p`. Note that this does - not cover locals of inner procs. -- A thread-local `var` or `let`. -- A global `let` or `const`. -- A constant array/seq/object/tuple constructor. - - -Path expressions ----------------- - -A location derived from `source` is then defined as a path expression that -has `source` as the owner. A path expression `e` is defined recursively: - -- `source` itself is a path expression. -- Container access like `e[i]` is a path expression. -- Tuple access `e[0]` is a path expression. -- Object field access `e.field` is a path expression. -- `system.toOpenArray(e, ...)` is a path expression. -- Pointer dereference `e[]` is a path expression. -- An address `addr e`, `unsafeAddr e` is a path expression. -- A type conversion `T(e)` is a path expression. -- A cast expression `cast[T](e)` is a path expression. -- `f(e, ...)` is a path expression if `f`'s return type is a view type. - Because the view can only have been borrowed from `e`, we then know - that owner of `f(e, ...)` is `e`. - - -If a view type is used as a return type, the location must borrow from a location -that is derived from the first parameter that is passed to the proc. -See https://nim-lang.org/docs/manual.html#procedures-var-return-type for -details about how this is done for `var T`. - -A mutable view can borrow from a mutable location, an immutable view can borrow -from both a mutable or an immutable location. - -If a view borrows from a mutable location, the view can be used to update the -location. Otherwise it cannot be used for mutations. - -The *duration* of a borrow is the span of commands beginning from the assignment -to the view and ending with the last usage of the view. - -For the duration of the borrow operation, no mutations to the borrowed locations -may be performed except via the view that borrowed from the -location. The borrowed location is said to be *sealed* during the borrow. - -.. code-block:: nim - - {.experimental: "views".} - - type - Obj = object - field: string - - proc dangerous(s: var seq[Obj]) = - let v: lent Obj = s[0] # seal 's' - s.setLen 0 # prevented at compile-time because 's' is sealed. - echo v.field - - -The scope of the view does not matter: - -.. code-block:: nim - - proc valid(s: var seq[Obj]) = - let v: lent Obj = s[0] # begin of borrow - echo v.field # end of borrow - s.setLen 0 # valid because 'v' isn't used afterwards - - -The analysis requires as much precision about mutations as is reasonably obtainable, -so it is more effective with the experimental `strict funcs <#strict-funcs>`_ -feature. In other words `--experimental:views`:option: works better -with `--experimental:strictFuncs`:option:. - -The analysis is currently control flow insensitive: - -.. code-block:: nim - - proc invalid(s: var seq[Obj]) = - let v: lent Obj = s[0] - if false: - s.setLen 0 - echo v.field - -In this example, the compiler assumes that `s.setLen 0` invalidates the -borrow operation of `v` even though a human being can easily see that it -will never do that at runtime. - - -Start of a borrow ------------------ - -A borrow starts with one of the following: - -- The assignment of a non-view-type to a view-type. -- The assignment of a location that is derived from a local parameter - to a view-type. - - -End of a borrow ---------------- - -A borrow operation ends with the last usage of the view variable. - - -Reborrows ---------- - -A view `v` can borrow from multiple different locations. However, the borrow -is always the full span of `v`'s lifetime and every location that is borrowed -from is sealed during `v`'s lifetime. - - -Algorithm ---------- - -The following section is an outline of the algorithm that the current implementation -uses. The algorithm performs two traversals over the AST of the procedure or global -section of code that uses a view variable. No fixpoint iterations are performed, the -complexity of the analysis is O(N) where N is the number of nodes of the AST. - -The first pass over the AST computes the lifetime of each local variable based on -a notion of an "abstract time", in the implementation it's a simple integer that is -incremented for every visited node. - -In the second pass information about the underlying object "graphs" is computed. -Let `v` be a parameter or a local variable. Let `G(v)` be the graph -that `v` belongs to. A graph is defined by the set of variables that belong -to the graph. Initially for all `v`: `G(v) = {v}`. Every variable can only -be part of a single graph. - -Assignments like `a = b` "connect" two variables, both variables end up in the -same graph `{a, b} = G(a) = G(b)`. Unfortunately, the pattern to look for is -much more complex than that and can involve multiple assignment targets -and sources:: - - f(x, y) = g(a, b) - -connects `x` and `y` to `a` and `b`: `G(x) = G(y) = G(a) = G(b) = {x, y, a, b}`. -A type based alias analysis rules out some of these combinations, for example -a `string` value cannot possibly be connected to a `seq[int]`. - -A pattern like `v[] = value` or `v.field = value` marks `G(v)` as mutated. -After the second pass a set of disjoint graphs was computed. - -For strict functions it is then enforced that there is no graph that is both mutated -and has an element that is an immutable parameter (that is a parameter that is not -of type `var T`). - -For borrow checking a different set of checks is performed. Let `v` be the view -and `b` the location that is borrowed from. - -- The lifetime of `v` must not exceed `b`'s lifetime. Note: The lifetime of - a parameter is the complete proc body. -- If `v` is used for a mutation, `b` must be a mutable location too. -- During `v`'s lifetime, `G(b)` can only be modified by `v` (and only if - `v` is a mutable view). -- If `v` is `result` then `b` has to be a location derived from the first - formal parameter or from a constant location. -- A view cannot be used for a read or a write access before it was assigned to. +This feature may be removed in the future due to its practical difficulties. diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 6dbb92875..c7d56c0ed 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -465,12 +465,7 @@ proc bindSym*(ident: string | NimNode, rule: BindSymRule = brClosed): NimNode {. ## If `rule == brForceOpen` always an `nnkOpenSymChoice` tree is ## returned even if the symbol is not ambiguous. ## - ## Experimental feature: - ## use {.experimental: "dynamicBindSym".} to activate it. - ## If called from template / regular code, `ident` and `rule` must be - ## constant expression / literal value. - ## If called from macros / compile time procs / static blocks, - ## `ident` and `rule` can be VM computed value. + ## See the `manual `_ for more details. proc genSym*(kind: NimSymKind = nskLet; ident = ""): NimNode {. magic: "NGenSym", noSideEffect.} diff --git a/tests/macros/tcasestmtmacro.nim b/tests/macros/tcasestmtmacro.nim index 26519f637..32019a92a 100644 --- a/tests/macros/tcasestmtmacro.nim +++ b/tests/macros/tcasestmtmacro.nim @@ -4,8 +4,6 @@ yes ''' """ -{.experimental: "caseStmtMacros".} - import macros macro `case`(n: tuple): untyped = From 2859069dbe1c943494cb6be299b200afa748cf52 Mon Sep 17 00:00:00 2001 From: hlaaftana <10591326+hlaaftana@users.noreply.github.com> Date: Wed, 24 Nov 2021 09:08:07 +0300 Subject: [PATCH 2668/3768] fix #12274 (#19180) --- compiler/parser.nim | 2 +- tests/parser/t12274.nim | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 tests/parser/t12274.nim diff --git a/compiler/parser.nim b/compiler/parser.nim index aedf62d54..0ef6d1f54 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -1146,7 +1146,7 @@ proc isExprStart(p: Parser): bool = of tkSymbol, tkAccent, tkOpr, tkNot, tkNil, tkCast, tkIf, tkFor, tkProc, tkFunc, tkIterator, tkBind, tkBuiltInMagics, tkParLe, tkBracketLe, tkCurlyLe, tkIntLit..tkCustomLit, tkVar, tkRef, tkPtr, - tkTuple, tkObject, tkWhen, tkCase, tkOut: + tkTuple, tkObject, tkWhen, tkCase, tkOut, tkTry, tkBlock: result = true else: result = false diff --git a/tests/parser/t12274.nim b/tests/parser/t12274.nim new file mode 100644 index 000000000..40c85f158 --- /dev/null +++ b/tests/parser/t12274.nim @@ -0,0 +1,9 @@ +var s: seq[int] +s.add block: + let i = 1 + i +s.add try: + 2 +except: + 3 +doAssert s == @[1, 2] From ff39f6e26062651a56d6ac4c24fe5a799c177c50 Mon Sep 17 00:00:00 2001 From: hlaaftana <10591326+hlaaftana@users.noreply.github.com> Date: Wed, 24 Nov 2021 10:20:15 +0300 Subject: [PATCH 2669/3768] make JS trunc polyfill opt-in, closes #16144 (#19183) --- changelog.md | 8 +++++++- lib/system/jssys.nim | 6 +++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/changelog.md b/changelog.md index b645bc5c7..f7f92ad3c 100644 --- a/changelog.md +++ b/changelog.md @@ -3,7 +3,13 @@ ## Changes affecting backward compatibility - +- The `Math.trunc` polyfill for targeting Internet Explorer was + previously emitted for every JavaScript output file except if + the symbol `nodejs` was defined via `-d:nodejs`. Now, it is only + emitted if the symbol `nimJsMathTruncPolyfill` is defined. If you are + targeting Internet Explorer, you may choose to enable this option + or define your own `Math.trunc` polyfill using the [`emit` pragma](https://nim-lang.org/docs/manual.html#implementation-specific-pragmas-emit-pragma). Nim uses + `Math.trunc` for the division and modulo operators for integers. ## Standard library additions and changes diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim index cd4f378be..250bd069d 100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -763,7 +763,8 @@ proc nimParseBiggestFloat(s: string, number: var BiggestFloat, start: int): int # Workaround for IE, IE up to version 11 lacks 'Math.trunc'. We produce # 'Math.trunc' for Nim's ``div`` and ``mod`` operators: -const jsMathTrunc = """ +when defined(nimJsMathTruncPolyfill): + {.emit: """ if (!Math.trunc) { Math.trunc = function(v) { v = +v; @@ -771,5 +772,4 @@ if (!Math.trunc) { return (v - v % 1) || (v < 0 ? -0 : v === 0 ? v : 0); }; } -""" -when not defined(nodejs): {.emit: jsMathTrunc .} +""".} From c7c6b13a325958c6b55cbec2eb812eb06074101b Mon Sep 17 00:00:00 2001 From: hlaaftana <10591326+hlaaftana@users.noreply.github.com> Date: Wed, 24 Nov 2021 14:22:40 +0300 Subject: [PATCH 2670/3768] parseExpr/parseStmt accept filename, fixes #13540 (#19182) --- changelog.md | 3 +++ compiler/vm.nim | 9 ++++----- compiler/vmgen.nim | 4 ++-- lib/core/macros.nim | 14 ++++++++------ tests/macros/mparsefile.nim | 4 ++++ tests/macros/tparsefile.nim | 11 +++++++++++ 6 files changed, 32 insertions(+), 13 deletions(-) create mode 100644 tests/macros/mparsefile.nim create mode 100644 tests/macros/tparsefile.nim diff --git a/changelog.md b/changelog.md index f7f92ad3c..63c3eff40 100644 --- a/changelog.md +++ b/changelog.md @@ -13,6 +13,9 @@ ## Standard library additions and changes +- `macros.parseExpr` and `macros.parseStmt` now accept an optional + filename argument for more informative errors. + ## `std/smtp` - Sends `ehlo` first. If the mail server does not understand, it sends `helo` as a fallback. diff --git a/compiler/vm.nim b/compiler/vm.nim index b64545081..d58a49f9e 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1748,11 +1748,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = elif instr.opcode == opcNHint: message(c.config, info, hintUser, a.strVal) of opcParseExprToAst: - decodeB(rkNode) - # c.debug[pc].line.int - countLines(regs[rb].strVal) ? + decodeBC(rkNode) var error: string let ast = parseString(regs[rb].node.strVal, c.cache, c.config, - toFullPath(c.config, c.debug[pc]), c.debug[pc].line.int, + regs[rc].node.strVal, 0, proc (conf: ConfigRef; info: TLineInfo; msg: TMsgKind; arg: string) {.nosinks.} = if error.len == 0 and msg <= errMax: error = formatMsg(conf, info, msg, arg)) @@ -1764,10 +1763,10 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = else: regs[ra].node = ast[0] of opcParseStmtToAst: - decodeB(rkNode) + decodeBC(rkNode) var error: string let ast = parseString(regs[rb].node.strVal, c.cache, c.config, - toFullPath(c.config, c.debug[pc]), c.debug[pc].line.int, + regs[rc].node.strVal, 0, proc (conf: ConfigRef; info: TLineInfo; msg: TMsgKind; arg: string) {.nosinks.} = if error.len == 0 and msg <= errMax: error = formatMsg(conf, info, msg, arg)) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index bb095d3ee..9a823dff4 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1225,9 +1225,9 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = unused(c, n, dest) genBinaryStmtVar(c, n, opcAddSeqElem) of mParseExprToAst: - genUnaryABC(c, n, dest, opcParseExprToAst) + genBinaryABC(c, n, dest, opcParseExprToAst) of mParseStmtToAst: - genUnaryABC(c, n, dest, opcParseStmtToAst) + genBinaryABC(c, n, dest, opcParseStmtToAst) of mTypeTrait: let tmp = c.genx(n[1]) if dest < 0: dest = c.getTemp(n.typ) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index c7d56c0ed..444783b5a 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -511,27 +511,29 @@ proc lineInfo*(arg: NimNode): string = ## Return line info in the form `filepath(line, column)`. $arg.lineInfoObj -proc internalParseExpr(s: string): NimNode {. +proc internalParseExpr(s, filename: string): NimNode {. magic: "ParseExprToAst", noSideEffect.} -proc internalParseStmt(s: string): NimNode {. +proc internalParseStmt(s, filename: string): NimNode {. magic: "ParseStmtToAst", noSideEffect.} proc internalErrorFlag*(): string {.magic: "NError", noSideEffect.} ## Some builtins set an error flag. This is then turned into a proper ## exception. **Note**: Ordinary application code should not call this. -proc parseExpr*(s: string): NimNode {.noSideEffect.} = +proc parseExpr*(s: string; filename: string = ""): NimNode {.noSideEffect.} = ## Compiles the passed string to its AST representation. ## Expects a single expression. Raises `ValueError` for parsing errors. - result = internalParseExpr(s) + ## A filename can be given for more informative errors. + result = internalParseExpr(s, filename) let x = internalErrorFlag() if x.len > 0: raise newException(ValueError, x) -proc parseStmt*(s: string): NimNode {.noSideEffect.} = +proc parseStmt*(s: string; filename: string = ""): NimNode {.noSideEffect.} = ## Compiles the passed string to its AST representation. ## Expects one or more statements. Raises `ValueError` for parsing errors. - result = internalParseStmt(s) + ## A filename can be given for more informative errors. + result = internalParseStmt(s, filename) let x = internalErrorFlag() if x.len > 0: raise newException(ValueError, x) diff --git a/tests/macros/mparsefile.nim b/tests/macros/mparsefile.nim new file mode 100644 index 000000000..8ac99d568 --- /dev/null +++ b/tests/macros/mparsefile.nim @@ -0,0 +1,4 @@ +let a = 1 +let b = 2 +let c = +let d = 4 diff --git a/tests/macros/tparsefile.nim b/tests/macros/tparsefile.nim new file mode 100644 index 000000000..a41223f80 --- /dev/null +++ b/tests/macros/tparsefile.nim @@ -0,0 +1,11 @@ +import macros + +static: + let fn = "mparsefile.nim" + var raised = false + try: + discard parseStmt(staticRead(fn), filename = fn) + except ValueError as e: + raised = true + doAssert e.msg == "mparsefile.nim(4, 1) Error: invalid indentation" + doAssert raised From a0073d2d4c18f030eccef98a130f7f1f2ad9d67a Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 24 Nov 2021 15:49:32 +0100 Subject: [PATCH 2671/3768] renamed 'gc' switch to 'mm'; [backport:1.6] (#19187) * renamed 'gc' switch to 'mm'; [backport:1.6] * better docs --- changelog.md | 5 +- compiler/commands.nim | 4 +- doc/advopt.txt | 7 +-- doc/backends.rst | 38 +------------- doc/destructors.rst | 12 ++--- doc/docs.rst | 4 +- doc/mm.rst | 95 ++++++++++++++++++++++++++++++++++ doc/nimc.rst | 22 ++++---- doc/{gc.rst => refc.rst} | 109 +++++++++++---------------------------- koch.nim | 2 +- tools/kochdocs.nim | 2 +- 11 files changed, 157 insertions(+), 143 deletions(-) create mode 100644 doc/mm.rst rename doc/{gc.rst => refc.rst} (62%) diff --git a/changelog.md b/changelog.md index 63c3eff40..258cf73e4 100644 --- a/changelog.md +++ b/changelog.md @@ -53,7 +53,7 @@ Baz = object ``` - [Case statement macros](manual.html#macros-case-statement-macros) are no longer experimental, - meaning you no longer need to enable the experimental switch `caseStmtMacros` to use them. + meaning you no longer need to enable the experimental switch `caseStmtMacros` to use them. ## Compiler changes @@ -63,5 +63,6 @@ ## Tool changes - +- The `gc` switch has been renamed to `mm` ("memory management") in order to reflect the + reality better. (Nim moved away from all techniques based on "tracing".) diff --git a/compiler/commands.nim b/compiler/commands.nim index a8caad916..d5c5f24e4 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -248,7 +248,7 @@ template deprecatedAlias(oldName, newName: string) = proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo): bool = case switch.normalize - of "gc": + of "gc", "mm": case arg.normalize of "boehm": result = conf.selectedGC == gcBoehm of "refc": result = conf.selectedGC == gcRefc @@ -596,7 +596,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; processOnOffSwitchG(conf, {optForceFullMake}, arg, pass, info) of "project": processOnOffSwitchG(conf, {optWholeProject, optGenIndex}, arg, pass, info) - of "gc": + of "gc", "mm": if conf.backend == backendJs: return # for: bug #16033 expectArg(conf, switch, arg, pass, info) if pass in {passCmd2, passPP}: diff --git a/doc/advopt.txt b/doc/advopt.txt index 063d018d1..79e784fde 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -122,8 +122,9 @@ Advanced options: --skipUserCfg:on|off do not read the user's configuration file --skipParentCfg:on|off do not read the parent dirs' configuration files --skipProjCfg:on|off do not read the project's configuration file - --gc:refc|arc|orc|markAndSweep|boehm|go|none|regions - select the GC to use; default is 'refc' + --mm:orc|arc|refc|markAndSweep|boehm|go|none|regions + select which memory management to use; default is 'refc' + recommended is 'orc' --exceptions:setjmp|cpp|goto|quirky select the exception handling implementation --index:on|off turn index file generation on|off @@ -163,4 +164,4 @@ Advanced options: --profileVM:on|off turn compile time VM profiler on|off --sinkInference:on|off turn sink parameter inference on|off (default: on) --panics:on|off turn panics into process terminations (default: off) - --deepcopy:on|off enable 'system.deepCopy' for ``--gc:arc|orc`` + --deepcopy:on|off enable 'system.deepCopy' for ``--mm:arc|orc`` diff --git a/doc/backends.rst b/doc/backends.rst index 377e899b0..d55503d10 100644 --- a/doc/backends.rst +++ b/doc/backends.rst @@ -105,7 +105,7 @@ file. However, you can also run the code with `nodejs`:idx: If you experience errors saying that `globalThis` is not defined, be sure to run a recent version of Node.js (at least 12.0). - + Interfacing =========== @@ -387,14 +387,8 @@ A similar thing happens with C code invoking Nim code which returns a proc gimme(): cstring {.exportc.} = result = "Hey there C code! " & $rand(100) -Since Nim's garbage collector is not aware of the C code, once the +Since Nim's reference counting mechanism is not aware of the C code, once the `gimme` proc has finished it can reclaim the memory of the `cstring`. -However, from a practical standpoint, the C code invoking the `gimme` -function directly will be able to use it since Nim's garbage collector has -not had a chance to run *yet*. This gives you enough time to make a copy for -the C side of the program, as calling any further Nim procs *might* trigger -garbage collection making the previously returned string garbage. Or maybe you -are `yourself triggering the collection `_. Custom data types @@ -414,31 +408,3 @@ you can clean it up. And of course, once cleaned you should avoid accessing it from Nim (or C for that matter). Typically C data structures have their own `malloc_structure`:c: and `free_structure`:c: specific functions, so wrapping these for the Nim side should be enough. - - -Thread coordination -------------------- - -When the `NimMain()` function is called Nim initializes the garbage -collector to the current thread, which is usually the main thread of your -application. If your C code later spawns a different thread and calls Nim -code, the garbage collector will fail to work properly and you will crash. - -As long as you don't use the threadvar emulation Nim uses native thread -variables, of which you get a fresh version whenever you create a thread. You -can then attach a GC to this thread via - -.. code-block:: nim - - system.setupForeignThreadGc() - -It is **not** safe to disable the garbage collector and enable it after the -call from your background thread even if the code you are calling is short -lived. - -Before the thread exits, you should tear down the thread's GC to prevent memory -leaks by calling - -.. code-block:: nim - - system.tearDownForeignThreadGc() diff --git a/doc/destructors.rst b/doc/destructors.rst index 83a50230b..c98e94536 100644 --- a/doc/destructors.rst +++ b/doc/destructors.rst @@ -43,7 +43,7 @@ written as: dealloc(x.data) proc `=trace`[T](x: var myseq[T]; env: pointer) = - # `=trace` allows the cycle collector `--gc:orc` + # `=trace` allows the cycle collector `--mm:orc` # to understand how to trace the object graph. if x.data != nil: for i in 0..`_ | Description of some tools that come with the standard distribution. -- | `GC `_ - | Additional documentation about Nim's multi-paradigm memory management strategies +- | `Memory management `_ + | Additional documentation about Nim's memory management strategies | and how to operate them in a realtime setting. - | `Source code filters `_ diff --git a/doc/mm.rst b/doc/mm.rst new file mode 100644 index 000000000..b6941a901 --- /dev/null +++ b/doc/mm.rst @@ -0,0 +1,95 @@ +======================= +Nim's Memory Management +======================= + +.. default-role:: code +.. include:: rstcommon.rst + +:Author: Andreas Rumpf +:Version: |nimversion| + +.. + + + "The road to hell is paved with good intentions." + + +Multi-paradigm Memory Management Strategies +=========================================== + +.. default-role:: option + +Nim offers multiple different memory management strategies. +To choose the memory management strategy use the `--mm:` switch. + +**The recommended switch for newly written Nim code is `--mm:orc`.** + + +ARC/ORC +------- + +`--mm:orc` is a memory management mode primarily based on reference counting. Cycles +in the object graph are handled by a "cycle collector" which is based on "trial deletion". +Since algorithms based on "tracing" are not used, the runtime behavior is oblivious to +the involved heap sizes. + +The reference counting operations (= "RC ops") do not use atomic instructions and do not have to -- +instead entire subgraphs are *moved* between threads. The Nim compiler also aggressively +optimizes away RC ops and exploits `move semantics `_. + +Nim performs a fair share of optimizations for ARC/ORC; you can inspect what it did +to your time critical function via `--expandArc:functionName`. + +`--mm:arc` uses the same mechanism as `--mm:orc`, but it leaves out the cycle collector. +Both ARC and ORC offer deterministic performance for `hard realtime`:idx: systems, but +ARC can be easier to reason about for people coming from Ada/C++/C -- roughly speaking +the memory for a variable is freed when it goes "out of scope". + +We generally advise you to use the `acyclic` annotation in order to optimize away the +cycle collector's overhead +but `--mm:orc` also produces more machine code than `--mm:arc`, so if you're on a target +where code size matters and you know that your code does not produce cycles, you can +use `--mm:arc`. Notice that the default `async`:idx: implementation produces cycles +and leaks memory with `--mm:arc`, in other words, for `async` you need to use `--mm:orc`. + + + +Other MM modes +-------------- + +.. note:: The default `refc` GC is incremental, thread-local and not "stop-the-world". + +--mm:refc This is the default memory management strategy. It's a + deferred reference counting based garbage collector + with a simple Mark&Sweep backup GC in order to collect cycles. Heaps are thread-local. + `This document `_ contains further information. +--mm:markAndSweep Simple Mark-And-Sweep based garbage collector. + Heaps are thread-local. +--mm:boehm Boehm based garbage collector, it offers a shared heap. +--mm:go Go's garbage collector, useful for interoperability with Go. + Offers a shared heap. + +--mm:none No memory management strategy nor a garbage collector. Allocated memory is + simply never freed. You should use `--mm:arc` instead. + +Here is a comparison of the different memory management modes: + +================== ======== ================= ============== =================== +Memory Management Heap Reference Cycles Stop-The-World Command line switch +================== ======== ================= ============== =================== +ORC Shared Cycle Collector No `--mm:orc` +ARC Shared Leak No `--mm:arc` +RefC Local Cycle Collector No `--mm:refc` +Mark & Sweep Local Cycle Collector No `--mm:markAndSweep` +Boehm Shared Cycle Collector Yes `--mm:boehm` +Go Shared Cycle Collector Yes `--mm:go` +None Manual Manual Manual `--mm:none` +================== ======== ================= ============== =================== + +.. default-role:: code +.. include:: rstcommon.rst + +JavaScript's garbage collector is used for the `JavaScript and NodeJS +`_ compilation targets. +The `NimScript `_ target uses the memory management strategy built into +the Nim compiler. diff --git a/doc/nimc.rst b/doc/nimc.rst index 6be4c204e..eb066e748 100644 --- a/doc/nimc.rst +++ b/doc/nimc.rst @@ -408,13 +408,13 @@ to your usual `nim c`:cmd: or `nim cpp`:cmd: command and set the `passC`:option: and `passL`:option: command line switches to something like: .. code-block:: cmd - nim c ... --d:nimAllocPagesViaMalloc --gc:orc --passC="-I$DEVKITPRO/libnx/include" ... + nim c ... --d:nimAllocPagesViaMalloc --mm:orc --passC="-I$DEVKITPRO/libnx/include" ... --passL="-specs=$DEVKITPRO/libnx/switch.specs -L$DEVKITPRO/libnx/lib -lnx" or setup a ``nim.cfg`` file like so:: #nim.cfg - --gc:orc + --mm:orc --d:nimAllocPagesViaMalloc --passC="-I$DEVKITPRO/libnx/include" --passL="-specs=$DEVKITPRO/libnx/switch.specs -L$DEVKITPRO/libnx/lib -lnx" @@ -485,10 +485,10 @@ Define Effect `useMalloc` Makes Nim use C's `malloc`:idx: instead of Nim's own memory manager, albeit prefixing each allocation with its size to support clearing memory on reallocation. - This only works with `--gc:none`:option:, - `--gc:arc`:option: and `--gc:orc`:option:. + This only works with `--mm:none`:option:, + `--mm:arc`:option: and `--mm:orc`:option:. `useRealtimeGC` Enables support of Nim's GC for *soft* realtime - systems. See the documentation of the `gc `_ + systems. See the documentation of the `mm `_ for further information. `logGC` Enable GC logging to stdout. `nodejs` The JS target is actually ``node.js``. @@ -614,9 +614,9 @@ A good start is to use the `any` operating target together with the .. code:: cmd - nim c --os:any --gc:arc -d:useMalloc [...] x.nim + nim c --os:any --mm:arc -d:useMalloc [...] x.nim -- `--gc:arc`:option: will enable the reference counting memory management instead +- `--mm:arc`:option: will enable the reference counting memory management instead of the default garbage collector. This enables Nim to use heap memory which is required for strings and seqs, for example. @@ -654,7 +654,7 @@ devices. This allocator gets blocks/pages of memory via a currently undocumented `osalloc` API which usually uses POSIX's `mmap` call. On many environments `mmap` is not available but C's `malloc` is. You can use the `nimAllocPagesViaMalloc` define to use `malloc` instead of `mmap`. `nimAllocPagesViaMalloc` is currently -only supported with `--gc:arc` or `--gc:orc`. (Since version 1.6) +only supported with `--mm:arc` or `--mm:orc`. (Since version 1.6) nimPage256 / nimPage512 / nimPage1k =================================== @@ -681,19 +681,19 @@ nimMemAlignTiny Sets `MemAlign` to `4` bytes which reduces the memory alignment to better match some embedded devices. -Thread stack size +Thread stack size ================= Nim's thread API provides a simple wrapper around more advanced RTOS task features. Customizing the stack size and stack guard size can be done by setting `-d:nimThreadStackSize=16384` or `-d:nimThreadStackGuard=32`. -Currently only Zephyr and FreeRTOS support these configurations. +Currently only Zephyr and FreeRTOS support these configurations. Nim for realtime systems ======================== -See the documentation of Nim's soft realtime `GC `_ for further +See the `--mm:arc` or `--mm:orc` memory management settings in `MM `_ for further information. diff --git a/doc/gc.rst b/doc/refc.rst similarity index 62% rename from doc/gc.rst rename to doc/refc.rst index 5de02f73d..766097f23 100644 --- a/doc/gc.rst +++ b/doc/refc.rst @@ -1,81 +1,3 @@ -======================= -Nim's Memory Management -======================= - -.. default-role:: code -.. include:: rstcommon.rst - -:Author: Andreas Rumpf -:Version: |nimversion| - -.. - - - "The road to hell is paved with good intentions." - - -Introduction -============ - -A memory-management algorithm optimal for every use-case cannot exist. -Nim provides multiple paradigms for needs ranging from large multi-threaded -applications, to games, hard-realtime systems and small microcontrollers. - -This document describes how the management strategies work; -How to tune the garbage collectors for your needs, like (soft) `realtime systems`:idx:, -and how the memory management strategies other than garbage collectors work. - -.. note:: the default GC is incremental, thread-local and not "stop-the-world" - -Multi-paradigm Memory Management Strategies -=========================================== - -.. default-role:: option - -To choose the memory management strategy use the `--gc:` switch. - ---gc:refc This is the default GC. It's a - deferred reference counting based garbage collector - with a simple Mark&Sweep backup GC in order to collect cycles. Heaps are thread-local. ---gc:markAndSweep Simple Mark-And-Sweep based garbage collector. - Heaps are thread-local. ---gc:boehm Boehm based garbage collector, it offers a shared heap. ---gc:go Go's garbage collector, useful for interoperability with Go. - Offers a shared heap. ---gc:arc Plain reference counting with - `move semantic optimizations `_, offers a shared heap. - It offers deterministic performance for `hard realtime`:idx: systems. Reference cycles - cause memory leaks, beware. - ---gc:orc Same as `--gc:arc` but adds a cycle collector based on "trial deletion". - Unfortunately, that makes its performance profile hard to reason about so it is less - useful for hard real-time systems. - ---gc:none No memory management strategy nor a garbage collector. Allocated memory is - simply never freed. You should use `--gc:arc` instead. - - -================== ======== ================= ============== =================== -Memory Management Heap Reference Cycles Stop-The-World Command line switch -================== ======== ================= ============== =================== -RefC Local Cycle Collector No `--gc:refc` -Mark & Sweep Local Cycle Collector No `--gc:markAndSweep` -ARC Shared Leak No `--gc:arc` -ORC Shared Cycle Collector No `--gc:orc` -Boehm Shared Cycle Collector Yes `--gc:boehm` -Go Shared Cycle Collector Yes `--gc:go` -None Manual Manual Manual `--gc:none` -================== ======== ================= ============== =================== - -.. default-role:: code -.. include:: rstcommon.rst - -JavaScript's garbage collector is used for the `JavaScript and NodeJS -`_ compilation targets. -The `NimScript `_ target uses the memory management strategy built into -the Nim compiler. - - Tweaking the refc GC ==================== @@ -164,6 +86,35 @@ that up to 100 objects are traversed and freed before it checks again. Thus highly specialized environments or for older hardware. +Thread coordination +------------------- + +When the `NimMain()` function is called Nim initializes the garbage +collector to the current thread, which is usually the main thread of your +application. If your C code later spawns a different thread and calls Nim +code, the garbage collector will fail to work properly and you will crash. + +As long as you don't use the threadvar emulation Nim uses native thread +variables, of which you get a fresh version whenever you create a thread. You +can then attach a GC to this thread via + +.. code-block:: nim + + system.setupForeignThreadGc() + +It is **not** safe to disable the garbage collector and enable it after the +call from your background thread even if the code you are calling is short +lived. + +Before the thread exits, you should tear down the thread's GC to prevent memory +leaks by calling + +.. code-block:: nim + + system.tearDownForeignThreadGc() + + + Keeping track of memory ======================= @@ -178,7 +129,7 @@ Other useful procs from `system `_ you can use to keep track of mem * `GC_getStatistics()` Garbage collector statistics as a human-readable string. These numbers are usually only for the running thread, not for the whole heap, -with the exception of `--gc:boehm`:option: and `--gc:go`:option:. +with the exception of `--mm:boehm`:option: and `--mm:go`:option:. In addition to `GC_ref` and `GC_unref` you can avoid the garbage collector by manually allocating memory with procs like `alloc`, `alloc0`, `allocShared`, `allocShared0` or `allocCStringArray`. diff --git a/koch.nim b/koch.nim index f87828cdc..295b1584b 100644 --- a/koch.nim +++ b/koch.nim @@ -604,7 +604,7 @@ proc runCI(cmd: string) = when not defined(bsd): # the BSDs are overwhelmed already, so only run this test on the other machines: - kochExecFold("Boot Nim ORC", "boot -d:release --gc:orc --lib:lib") + kochExecFold("Boot Nim ORC", "boot -d:release --mm:orc --lib:lib") proc testUnixInstall(cmdLineRest: string) = csource("-d:danger" & cmdLineRest) diff --git a/tools/kochdocs.nim b/tools/kochdocs.nim index 70ee5ad07..4fbea8c77 100644 --- a/tools/kochdocs.nim +++ b/tools/kochdocs.nim @@ -129,7 +129,7 @@ tut2.rst tut3.rst nimc.rst niminst.rst -gc.rst +mm.rst """.splitWhitespace().mapIt("doc" / it) doc0 = """ From a59ad2006291eafcd6790f3011b5cd355b219a5b Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Wed, 24 Nov 2021 18:16:20 +0300 Subject: [PATCH 2672/3768] fix inline syntax highlighting in system.nim (#19184) --- lib/system_overview.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/system_overview.rst b/lib/system_overview.rst index df22697e0..0e4ffaf17 100644 --- a/lib/system_overview.rst +++ b/lib/system_overview.rst @@ -1,4 +1,5 @@ .. default-role:: code +.. include:: ../doc/rstcommon.rst The System module imports several separate modules, and their documentation is in separate files: From f91867aa31f3ae2c4f28d24547d62603daaa6d0c Mon Sep 17 00:00:00 2001 From: hlaaftana <10591326+hlaaftana@users.noreply.github.com> Date: Wed, 24 Nov 2021 18:34:42 +0300 Subject: [PATCH 2673/3768] accept object type node from macros (#19179) --- compiler/semexprs.nim | 2 +- tests/macros/ttypenodes.nim | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 tests/macros/ttypenodes.nim diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index c11cecfa9..0df332689 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2856,7 +2856,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = of nkBind: message(c.config, n.info, warnDeprecated, "bind is deprecated") result = semExpr(c, n[0], flags) - of nkTypeOfExpr, nkTupleTy, nkTupleClassTy, nkRefTy..nkEnumTy, nkStaticTy: + of nkTypeOfExpr..nkTupleClassTy, nkStaticTy, nkRefTy..nkEnumTy: if c.matchedConcept != nil and n.len == 1: let modifier = n.modifierTypeKindOfNode if modifier != tyNone: diff --git a/tests/macros/ttypenodes.nim b/tests/macros/ttypenodes.nim new file mode 100644 index 000000000..233ea9780 --- /dev/null +++ b/tests/macros/ttypenodes.nim @@ -0,0 +1,16 @@ +import macros + +macro makeEnum(): untyped = + newTree(nnkEnumTy, newEmptyNode(), ident"a", ident"b", ident"c") + +macro makeObject(): untyped = + newTree(nnkObjectTy, newEmptyNode(), newEmptyNode(), newTree(nnkRecList, + newTree(nnkIdentDefs, ident"x", ident"y", ident"int", newEmptyNode()))) + +type + Foo = makeEnum() + Bar = makeObject() + +doAssert {a, b, c} is set[Foo] +let bar = Bar(x: 3, y: 4) +doAssert (bar.x, bar.y) == (3, 4) From 0d0c249074d6a1041de16108dc247396efef5513 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Wed, 24 Nov 2021 08:37:14 -0800 Subject: [PATCH 2674/3768] swap port to correct port order (#19177) Co-authored-by: Jaremy Creechley --- lib/pure/net.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 7b0ff78e7..06024e46d 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -478,14 +478,14 @@ proc toSockAddr*(address: IpAddress, port: Port, sa: var Sockaddr_storage, sl = sizeof(Sockaddr_in).SockLen let s = cast[ptr Sockaddr_in](addr sa) s.sin_family = typeof(s.sin_family)(toInt(AF_INET)) - s.sin_port = port + s.sin_port = htons(port) copyMem(addr s.sin_addr, unsafeAddr address.address_v4[0], sizeof(s.sin_addr)) of IpAddressFamily.IPv6: sl = sizeof(Sockaddr_in6).SockLen let s = cast[ptr Sockaddr_in6](addr sa) s.sin6_family = typeof(s.sin6_family)(toInt(AF_INET6)) - s.sin6_port = port + s.sin6_port = htons(port) copyMem(addr s.sin6_addr, unsafeAddr address.address_v6[0], sizeof(s.sin6_addr)) From 373c909300c9ecef159666ac196e360fbd7a3ebc Mon Sep 17 00:00:00 2001 From: Iced Quinn <70673392+IcedQuinn@users.noreply.github.com> Date: Sat, 4 Dec 2021 00:42:03 -0600 Subject: [PATCH 2675/3768] feat: TLS-ALPN wrappers for OpenSSL (#19202) Co-authored-by: Iced Quinn --- lib/wrappers/openssl.nim | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim index c756fa938..00ec160cb 100644 --- a/lib/wrappers/openssl.nim +++ b/lib/wrappers/openssl.nim @@ -847,3 +847,17 @@ when not defined(nimDisableCertificateValidation) and not defined(windows): let cert = d2i_X509(certbytes) let encoded = cert.i2d_X509() assert encoded == certbytes + +# Application Layer Protocol Negociation extension (TLS-ALPN, RFC7301) +# Available in at least OpenSSL 1.1.1 and later, not sure if earlier +# --Iced Quinn + +proc SSL_CTX_set_alpn_protos*(ctx: SslCtx; protos: cstring; protos_len: cuint): cint {.cdecl, dynlib: DLLSSLName, importc.} +proc SSL_set_alpn_protos*(ssl: SslPtr; protos: cstring; protos_len: cuint): cint {.cdecl, dynlib: DLLSSLName, importc.} +proc SSL_CTX_set_alpn_select_cb*(ctx: SslCtx; cb: proc(ssl: SslPtr; out_proto: ptr cstring; outlen: cstring; in_proto: cstring; inlen: cuint; arg: pointer): cint {.cdecl.}; arg: pointer): cint {.cdecl, dynlib: DLLSSLName, importc.} +proc SSL_get0_alpn_selected*(ssl: SslPtr; data: ptr cstring; len: ptr cuint) {.cdecl, dynlib: DLLSSLName, importc.} +proc SSL_CTX_set_next_protos_advertised_cb*(ctx: SslCtx; cb: proc(ssl: SslPtr; out_proto: ptr cstring; outlen: ptr cuint; arg: pointer): cint {.cdecl.}; arg: pointer) {.cdecl, dynlib: DLLSSLName, importc.} +proc SSL_CTX_set_next_proto_select_cb*(ctx: SslCtx; cb: proc(s: SslPtr; out_proto: cstring; outlen: cstring; in_proto: cstring; inlen: cuint; arg: pointer): cint {.cdecl.}; arg: pointer) {.cdecl, dynlib: DLLSSLName, importc.} +proc SSL_select_next_proto*(out_proto: ptr cstring; outlen: cstring; server: cstring; server_len: cuint; client: cstring; client_len: cuint): cint {.cdecl, dynlib: DLLSSLName, importc.} +proc SSL_get0_next_proto_negotiated*(s: SslPtr; data: ptr cstring; len: ptr cuint) {.cdecl, dynlib: DLLSSLName, importc.} + From 23c117a950a3ce0e3c85d4f89409075329648230 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 4 Dec 2021 07:42:34 +0100 Subject: [PATCH 2676/3768] misc bugfixes [backport:1.2] (#19203) --- compiler/semobjconstr.nim | 6 +++--- doc/backends.rst | 23 +++++++++-------------- 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim index 30ab76957..6d11d321e 100644 --- a/compiler/semobjconstr.nim +++ b/compiler/semobjconstr.nim @@ -366,9 +366,9 @@ proc defaultConstructionError(c: PContext, t: PType, info: TLineInfo) = if objType.kind == tyObject: var constrCtx = initConstrContext(objType, newNodeI(nkObjConstr, info)) let initResult = semConstructTypeAux(c, constrCtx, {}) - assert constrCtx.missingFields.len > 0 - localError(c.config, info, - "The $1 type doesn't have a default value. The following fields must be initialized: $2." % [typeToString(t), listSymbolNames(constrCtx.missingFields)]) + if constrCtx.missingFields.len > 0: + localError(c.config, info, + "The $1 type doesn't have a default value. The following fields must be initialized: $2." % [typeToString(t), listSymbolNames(constrCtx.missingFields)]) elif objType.kind == tyDistinct: localError(c.config, info, "The $1 distinct type doesn't have a default value." % typeToString(t)) diff --git a/doc/backends.rst b/doc/backends.rst index d55503d10..3a3359fca 100644 --- a/doc/backends.rst +++ b/doc/backends.rst @@ -52,7 +52,7 @@ The commands to compile to either C, C++ or Objective-C are: The most significant difference between these commands is that if you look into the ``nimcache`` directory you will find ``.c``, ``.cpp`` or ``.m`` files, other than that all of them will produce a native binary for your -project. This allows you to take the generated code and place it directly +project. This allows you to take the generated code and place it directly into a project using any of these languages. Here are some typical command- line invocations: @@ -123,7 +123,7 @@ Nim code can interface with the backend through the `Foreign function interface `_ mainly through the `importc pragma `_. The `importc` pragma is the *generic* way of making backend symbols available -in Nim and is available in all the target backends (JavaScript too). The C++ +in Nim and is available in all the target backends (JavaScript too). The C++ or Objective-C backends have their respective `ImportCpp `_ and `ImportObjC `_ @@ -246,11 +246,6 @@ Also, C code requires you to specify a forward declaration for functions or the compiler will assume certain types for the return value and parameters which will likely make your program crash at runtime. -The Nim compiler can generate a C interface header through the `--header`:option: -command-line switch. The generated header will contain all the exported -symbols and the `NimMain` proc which you need to call before any other -Nim code. - Nim invocation example from C ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -269,9 +264,10 @@ Create a ``maths.c`` file with the following content: .. code-block:: c - #include "fib.h" #include + extern int fib(int a); + int main(void) { NimMain(); @@ -286,13 +282,12 @@ program: .. code:: cmd - nim c --noMain --noLinking --header:fib.h fib.nim + nim c --noMain --noLinking fib.nim gcc -o m -I$HOME/.cache/nim/fib_d -Ipath/to/nim/lib $HOME/.cache/nim/fib_d/*.c maths.c The first command runs the Nim compiler with three special options to avoid -generating a `main()`:c: function in the generated files, avoid linking the -object files into a final binary, and explicitly generate a header file for C -integration. All the generated files are placed into the ``nimcache`` +generating a `main()`:c: function in the generated files and to avoid linking the +object files into a final binary. All the generated files are placed into the ``nimcache`` directory. That's why the next command compiles the ``maths.c`` source plus all the ``.c`` files from ``nimcache``. In addition to this path, you also have to tell the C compiler where to find Nim's ``nimbase.h`` header file. @@ -302,12 +297,12 @@ also ask the Nim compiler to generate a statically linked library: .. code:: cmd - nim c --app:staticLib --noMain --header fib.nim + nim c --app:staticLib --noMain fib.nim gcc -o m -Inimcache -Ipath/to/nim/lib libfib.nim.a maths.c The Nim compiler will handle linking the source files generated in the ``nimcache`` directory into the ``libfib.nim.a`` static library, which you can -then link into your C program. Note that these commands are generic and will +then link into your C program. Note that these commands are generic and will vary for each system. For instance, on Linux systems you will likely need to use `-ldl`:option: too to link in required dlopen functionality. From d584dd5b99f4c1e32477db2dfb791d52b0139df1 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 4 Dec 2021 07:42:58 +0100 Subject: [PATCH 2677/3768] fixes #19015 [backport:1.6] (#19204) --- changelog.md | 3 +++ compiler/options.nim | 3 ++- compiler/sigmatch.nim | 3 ++- tests/misc/trfc405.nim | 3 +++ 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index 258cf73e4..a8f1920e9 100644 --- a/changelog.md +++ b/changelog.md @@ -11,6 +11,9 @@ or define your own `Math.trunc` polyfill using the [`emit` pragma](https://nim-lang.org/docs/manual.html#implementation-specific-pragmas-emit-pragma). Nim uses `Math.trunc` for the division and modulo operators for integers. +- Optional parameters in combination with `: body` syntax (RFC #405) are now opt-in via + `experimental:flexibleOptionalParams`. + ## Standard library additions and changes - `macros.parseExpr` and `macros.parseStmt` now accept an optional diff --git a/compiler/options.nim b/compiler/options.nim index d8e5057f9..383b6fa2a 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -207,7 +207,8 @@ type strictNotNil, overloadableEnums, strictEffects, - unicodeOperators + unicodeOperators, + flexibleOptionalParams LegacyFeature* = enum allowSemcheckedAstModification, diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 6bc6eefe0..93be92676 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -2461,7 +2461,8 @@ proc matchesAux(c: PContext, n, nOrig: PNode, m: var TCandidate, marker: var Int if m.callee.n[f].kind != nkSym: internalError(c.config, n[a].info, "matches") noMatch() - if a >= firstArgBlock: f = max(f, m.callee.n.len - (n.len - a)) + if flexibleOptionalParams in c.features and a >= firstArgBlock: + f = max(f, m.callee.n.len - (n.len - a)) formal = m.callee.n[f].sym m.firstMismatch.kind = kTypeMismatch if containsOrIncl(marker, formal.position) and container.isNil: diff --git a/tests/misc/trfc405.nim b/tests/misc/trfc405.nim index adc39b1df..8c967eb77 100644 --- a/tests/misc/trfc405.nim +++ b/tests/misc/trfc405.nim @@ -1,3 +1,6 @@ + +{.experimental: "flexibleOptionalParams".} + # https://github.com/nim-lang/RFCs/issues/405 template main = From f90620fb325d5b5b924774ce84384cb4098ed18e Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 4 Dec 2021 07:43:20 +0100 Subject: [PATCH 2678/3768] fixes #19198 [backport:1.6] (#19209) * fixes #19198 [backport:1.6] * added a test case --- compiler/vm.nim | 6 ++++-- compiler/vmgen.nim | 28 ++++++++++++++++------------ tests/vm/tvmmisc.nim | 23 +++++++++++++++++++++-- 3 files changed, 41 insertions(+), 16 deletions(-) diff --git a/compiler/vm.nim b/compiler/vm.nim index d58a49f9e..3343eb781 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -434,8 +434,10 @@ proc opConv(c: PCtx; dest: var TFullReg, src: TFullReg, desttyp, srctyp: PType): of tyFloat..tyFloat64: dest.intVal = int(src.floatVal) else: - let srcDist = (sizeof(src.intVal) - styp.size) * 8 - let destDist = (sizeof(dest.intVal) - desttyp.size) * 8 + let srcSize = getSize(c.config, styp) + let destSize = getSize(c.config, desttyp) + let srcDist = (sizeof(src.intVal) - srcSize) * 8 + let destDist = (sizeof(dest.intVal) - destSize) * 8 var value = cast[BiggestUInt](src.intVal) value = (value shl srcDist) shr srcDist value = (value shl destDist) shr destDist diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 9a823dff4..c0c4bac19 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -749,18 +749,20 @@ proc genNarrow(c: PCtx; n: PNode; dest: TDest) = let t = skipTypes(n.typ, abstractVar-{tyTypeDesc}) # uint is uint64 in the VM, we we only need to mask the result for # other unsigned types: - if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and t.size < 8): - c.gABC(n, opcNarrowU, dest, TRegister(t.size*8)) - elif t.kind in {tyInt8..tyInt32} or (t.kind == tyInt and t.size < 8): - c.gABC(n, opcNarrowS, dest, TRegister(t.size*8)) + let size = getSize(c.config, t) + if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and size < 8): + c.gABC(n, opcNarrowU, dest, TRegister(size*8)) + elif t.kind in {tyInt8..tyInt32} or (t.kind == tyInt and size < 8): + c.gABC(n, opcNarrowS, dest, TRegister(size*8)) proc genNarrowU(c: PCtx; n: PNode; dest: TDest) = let t = skipTypes(n.typ, abstractVar-{tyTypeDesc}) # uint is uint64 in the VM, we we only need to mask the result for # other unsigned types: + let size = getSize(c.config, t) if t.kind in {tyUInt8..tyUInt32, tyInt8..tyInt32} or - (t.kind in {tyUInt, tyInt} and t.size < 8): - c.gABC(n, opcNarrowU, dest, TRegister(t.size*8)) + (t.kind in {tyUInt, tyInt} and size < 8): + c.gABC(n, opcNarrowU, dest, TRegister(size*8)) proc genBinaryABCnarrow(c: PCtx; n: PNode; dest: var TDest; opc: TOpcode) = genBinaryABC(c, n, dest, opc) @@ -1088,10 +1090,11 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = genBinaryABC(c, n, dest, opcShlInt) # genNarrowU modified let t = skipTypes(n.typ, abstractVar-{tyTypeDesc}) - if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and t.size < 8): - c.gABC(n, opcNarrowU, dest, TRegister(t.size*8)) - elif t.kind in {tyInt8..tyInt32} or (t.kind == tyInt and t.size < 8): - c.gABC(n, opcSignExtend, dest, TRegister(t.size*8)) + let size = getSize(c.config, t) + if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and size < 8): + c.gABC(n, opcNarrowU, dest, TRegister(size*8)) + elif t.kind in {tyInt8..tyInt32} or (t.kind == tyInt and size < 8): + c.gABC(n, opcSignExtend, dest, TRegister(size*8)) of mAshrI: genBinaryABC(c, n, dest, opcAshrInt) of mBitandI: genBinaryABC(c, n, dest, opcBitandInt) of mBitorI: genBinaryABC(c, n, dest, opcBitorInt) @@ -1125,8 +1128,9 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = genUnaryABC(c, n, dest, opcBitnotInt) #genNarrowU modified, do not narrow signed types let t = skipTypes(n.typ, abstractVar-{tyTypeDesc}) - if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and t.size < 8): - c.gABC(n, opcNarrowU, dest, TRegister(t.size*8)) + let size = getSize(c.config, t) + if t.kind in {tyUInt8..tyUInt32} or (t.kind == tyUInt and size < 8): + c.gABC(n, opcNarrowU, dest, TRegister(size*8)) of mCharToStr, mBoolToStr, mIntToStr, mInt64ToStr, mFloatToStr, mCStrToStr, mStrToStr, mEnumToStr: genConv(c, n, n[1], dest) of mEqStr, mEqCString: genBinaryABC(c, n, dest, opcEqStr) diff --git a/tests/vm/tvmmisc.nim b/tests/vm/tvmmisc.nim index f6fbde0f9..dfe086d10 100644 --- a/tests/vm/tvmmisc.nim +++ b/tests/vm/tvmmisc.nim @@ -290,10 +290,10 @@ block: # bug #10815 const a = P() doAssert $a == "" - + when defined osx: # xxx bug https://github.com/nim-lang/Nim/issues/10815#issuecomment-476380734 block: - type CharSet {.union.} = object + type CharSet {.union.} = object cs: set[char] vs: array[4, uint64] const a = Charset(cs: {'a'..'z'}) @@ -553,3 +553,22 @@ block: # bug #8015 doAssert $viaProc.table[0] == "(kind: Fixed, cost: 999)" doAssert viaProc.table[1].handler() == 100 doAssert viaProc.table[2].handler() == 200 + + +# bug #19198 + +block: + type + Foo[n: static int] = int + +block: + static: + let x = int 1 + echo x.type # Foo + +block: + static: + let x = int 1 + let y = x + 1 + # Error: unhandled exception: value out of range: -8 notin 0 .. 65535 [RangeDefect] + echo y From 1cbdc1573af5db8bf555b0b6454424188c9f345c Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 4 Dec 2021 07:44:26 +0100 Subject: [PATCH 2679/3768] fixes #19159 [backport:1.6] (#19210) --- compiler/sempass2.nim | 7 ++++--- tests/effects/tstrict_effects3.nim | 12 ++++++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 5624d7adb..d6cc4180b 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -852,13 +852,14 @@ proc trackCall(tracked: PEffects; n: PNode) = assumeTheWorst(tracked, n, op) gcsafeAndSideeffectCheck() else: - if strictEffects in tracked.c.features and a.kind == nkSym and a.sym.kind in routineKinds: + if strictEffects in tracked.c.features and a.kind == nkSym and + a.sym.kind in routineKinds: propagateEffects(tracked, n, a.sym) else: mergeRaises(tracked, effectList[exceptionEffects], n) mergeTags(tracked, effectList[tagEffects], n) gcsafeAndSideeffectCheck() - if a.kind != nkSym or a.sym.magic notin {mNBindSym, mFinished}: + if a.kind != nkSym or a.sym.magic notin {mNBindSym, mFinished, mExpandToAst, mQuoteAst}: for i in 1.. Date: Sat, 4 Dec 2021 07:54:11 +0100 Subject: [PATCH 2680/3768] Improve documentation around func and method (#19207) * Improve documentation around func and method * Update doc/tut1.rst Co-authored-by: Danil Yarantsev * Update doc/tut1.rst Co-authored-by: Danil Yarantsev * Update doc/tut1.rst Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> * Update doc/tut1.rst Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> * Update doc/tut1.rst Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> * Update doc/tut1.rst Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> * Update doc/tut1.rst Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> * Update doc/tut1.rst Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> * Update doc/tut1.rst Co-authored-by: Zoom * Update doc/tut1.rst Co-authored-by: Zoom * Update doc/tut1.rst Co-authored-by: Zoom * Update doc/tut1.rst Co-authored-by: Zoom * Rewrite of Zooms suggestion * Update doc/tut1.rst Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Co-authored-by: Danil Yarantsev Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Co-authored-by: Zoom --- doc/tut1.rst | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/doc/tut1.rst b/doc/tut1.rst index f492f12b0..2856625b7 100644 --- a/doc/tut1.rst +++ b/doc/tut1.rst @@ -595,8 +595,10 @@ Procedures To define new commands like `echo `_ and `readLine `_ in the examples, the concept of a -*procedure* is needed. (Some languages call them *methods* or *functions*.) -In Nim new procedures are defined with the `proc` keyword: +*procedure* is needed. You might be used to them being called *methods* or +*functions* in other languages, but Nim +`differentiates these concepts `_. In +Nim, new procedures are defined with the `proc` keyword: .. code-block:: nim :test: "nim c $1" @@ -874,6 +876,31 @@ The example also shows that a proc's body can consist of a single expression whose value is then returned implicitly. +Funcs and methods +----------------- + +As mentioned in the introduction, Nim differentiates between procedures, +functions, and methods, defined by the `proc`, `func`, and `method` keywords +respectively. In some ways, Nim is a bit more pedantic in its definitions than +other languages. + +Functions are closer to the concept of a pure mathematical +function, which might be familiar to you if you've ever done functional +programming. Essentially they are procedures with additional limitations set on +them: they can't access global state (except `const`) and can't produce +side-effects. The `func` keyword is basically an alias for `proc` tagged +with `{.noSideEffects.}`. Functions can still change their mutable arguments +however, which are those marked as `var`, along with any `ref` objects. + +Unlike procedures, methods are dynamically dispatched. This sounds a bit +complicated, but it is a concept closely related to inheritance and object oriented +programming. If you overload a procedure (two procedures with the same name but +of different types or with different sets of arguments are said to be overloaded), the procedure to use is determined +at compile-time. Methods, on the other hand, depend on objects that inherit from +the `RootObj`. This is something that is covered in much greater depth in +the `second part of the tutorial`_. + + Iterators ========= From 1ef945668d458163cb7116a8c936ff92b63d679d Mon Sep 17 00:00:00 2001 From: hlaaftana <10591326+hlaaftana@users.noreply.github.com> Date: Sun, 5 Dec 2021 08:44:22 +0300 Subject: [PATCH 2681/3768] treat do with pragmas but no parens as proc (#19191) fixes #19188 --- compiler/parser.nim | 12 +++++++++--- doc/manual.rst | 8 ++++---- tests/parser/tdo.nim | 19 +++++++++++++++++-- 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/compiler/parser.nim b/compiler/parser.nim index 0ef6d1f54..9ff847ef6 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -1110,11 +1110,14 @@ proc optPragmas(p: var Parser): PNode = proc parseDoBlock(p: var Parser; info: TLineInfo): PNode = #| doBlock = 'do' paramListArrow pragma? colcom stmt - let params = parseParamList(p, retColon=false) + var params = parseParamList(p, retColon=false) let pragmas = optPragmas(p) colcom(p, result) result = parseStmt(p) - if params.kind != nkEmpty: + if params.kind != nkEmpty or pragmas.kind != nkEmpty: + if params.kind == nkEmpty: + params = newNodeP(nkFormalParams, p) + params.add(p.emptyNode) # return type result = newProcNode(nkDo, info, body = result, params = params, name = p.emptyNode, pattern = p.emptyNode, genericParams = p.emptyNode, pragmas = pragmas, exceptions = p.emptyNode) @@ -1381,7 +1384,10 @@ proc postExprBlocks(p: var Parser, x: PNode): PNode = if stmtList[0].kind == nkStmtList: stmtList = stmtList[0] stmtList.flags.incl nfBlockArg - if openingParams.kind != nkEmpty: + if openingParams.kind != nkEmpty or openingPragmas.kind != nkEmpty: + if openingParams.kind == nkEmpty: + openingParams = newNodeP(nkFormalParams, p) + openingParams.add(p.emptyNode) # return type result.add newProcNode(nkDo, stmtList.info, body = stmtList, params = openingParams, name = p.emptyNode, pattern = p.emptyNode, diff --git a/doc/manual.rst b/doc/manual.rst index 4fcc516ec..6705e673e 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -3834,10 +3834,10 @@ The proc expression represented by the `do` block is appended to the routine call as the last argument. In calls using the command syntax, the `do` block will bind to the immediately preceding expression rather than the command call. -`do` with a parameter list corresponds to an anonymous `proc`, however -`do` without parameters is treated as a normal statement list. This allows -macros to receive both indented statement lists as an argument in inline -calls, as well as a direct mirror of Nim's routine syntax. +`do` with a parameter list or pragma list corresponds to an anonymous `proc`, +however `do` without parameters or pragmas is treated as a normal statement +list. This allows macros to receive both indented statement lists as an +argument in inline calls, as well as a direct mirror of Nim's routine syntax. .. code-block:: nim # Passing a statement list to an inline macro: diff --git a/tests/parser/tdo.nim b/tests/parser/tdo.nim index 7bd1f7411..382d03398 100644 --- a/tests/parser/tdo.nim +++ b/tests/parser/tdo.nim @@ -1,8 +1,12 @@ discard """ - output: '''true + output: ''' true true -true inner B''' +true +true inner B +running with pragma +ran with pragma +''' """ template withValue(a, b, c, d, e: untyped) = @@ -77,3 +81,14 @@ proc main2 = echo "true inner B" main2() + +proc withPragma(foo: int, bar: proc() {.raises: [].}) = + echo "running with pragma" + bar() + +withPragma(3) do {.raises: [].}: + echo "ran with pragma" + +doAssert not (compiles do: + withPragma(3) do {.raises: [].}: + raise newException(Exception)) From e5475768ba5b540489fb47bdb6fdd338d75175e1 Mon Sep 17 00:00:00 2001 From: flywind Date: Sun, 5 Dec 2021 14:17:52 +0800 Subject: [PATCH 2682/3768] feature_request should be labelled automatically (#19215) --- .github/ISSUE_TEMPLATE/feature_request.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index b9f7caad5..6a9afe071 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -2,7 +2,7 @@ name: Feature request about: Do you want to suggest a new feature? Use this template. title: '' -labels: '' +labels: ["Feature"] assignees: '' --- From faacd63bf69561fe915f3651e6b917bb2c0e5bc2 Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 6 Dec 2021 17:04:54 +0800 Subject: [PATCH 2683/3768] the title of PR should contain a brief description (#19211) In addition to an issue number, the title of a PR should contain a brief description; then users can follow the progress more easily from the title. --- doc/contributing.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/doc/contributing.rst b/doc/contributing.rst index 07fa01714..d3b2770f3 100644 --- a/doc/contributing.rst +++ b/doc/contributing.rst @@ -10,6 +10,9 @@ Contributing Contributing happens via "Pull requests" (PR) on github. Every PR needs to be reviewed before it can be merged and the Continuous Integration should be green. +The title of a PR should contain a brief description. If it fixes an issue, +in addition to the number of the issue, the title should also contain a description +of the issue. The PR has to be approved by two core developers or by Araq. From 7e3da693f5bd8879d91ccadc469a3ec80d77a34f Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 7 Dec 2021 01:58:01 +0800 Subject: [PATCH 2684/3768] [format minor] remove unnecessary spaces (#19216) --- lib/system/atomics.nim | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/system/atomics.nim b/lib/system/atomics.nim index f69cb753d..75731764b 100644 --- a/lib/system/atomics.nim +++ b/lib/system/atomics.nim @@ -73,7 +73,7 @@ when someGcc and hasThreadSupport: proc atomicCompareExchangeN*[T: AtomType](p, expected: ptr T, desired: T, weak: bool, success_memmodel: AtomMemModel, failure_memmodel: AtomMemModel): bool {. - importc: "__atomic_compare_exchange_n ", nodecl.} + importc: "__atomic_compare_exchange_n", nodecl.} ## This proc implements an atomic compare and exchange operation. This compares the ## contents at p with the contents at expected and if equal, writes desired at p. ## If they are not equal, the current contents at p is written into expected. @@ -100,13 +100,13 @@ when someGcc and hasThreadSupport: proc atomicSubFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {. importc: "__atomic_sub_fetch", nodecl.} proc atomicOrFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {. - importc: "__atomic_or_fetch ", nodecl.} + importc: "__atomic_or_fetch", nodecl.} proc atomicAndFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {. importc: "__atomic_and_fetch", nodecl.} proc atomicXorFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {. importc: "__atomic_xor_fetch", nodecl.} proc atomicNandFetch*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {. - importc: "__atomic_nand_fetch ", nodecl.} + importc: "__atomic_nand_fetch", nodecl.} ## Perform the operation return the old value, all memory models are valid proc atomicFetchAdd*[T: AtomType](p: ptr T, val: T, mem: AtomMemModel): T {. @@ -177,8 +177,8 @@ elif someVcc and hasThreadSupport: proc `==`(x1, x2: AtomMemModel): bool {.borrow.} - proc readBarrier() {.importc: "_ReadBarrier", header: "".} - proc writeBarrier() {.importc: "_WriteBarrier", header: "".} + proc readBarrier() {.importc: "_ReadBarrier", header: "".} + proc writeBarrier() {.importc: "_WriteBarrier", header: "".} proc fence*() {.importc: "_ReadWriteBarrier", header: "".} template barrier(mem: AtomMemModel) = From 0213c7313b2edf8e8ec9d81764685744e874ef84 Mon Sep 17 00:00:00 2001 From: Etan Kissling Date: Mon, 6 Dec 2021 20:38:23 +0100 Subject: [PATCH 2685/3768] allow `HSlice` bounded by constants of distinct types (#19219) [backport:1.2] When creating heterogenous slices of distinct types, the compiler does not initialize the internal type's `size` before accessing it. This then leads to this crash message: ``` compiler/int128.nim(594, 11) `false` masking only implemented for 1, 2, 4 and 8 bytes [AssertionError] ``` This patch initializes the `size` properly, fixing the problem. --- compiler/semfold.nim | 16 ++++++++-------- tests/slice/tdistinct.nim | 2 ++ 2 files changed, 10 insertions(+), 8 deletions(-) create mode 100644 tests/slice/tdistinct.nim diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 5ae91d306..d0473b5ce 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -133,7 +133,7 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; idgen: IdGenerator; g: ModuleGraph): P of mCard: result = newIntNodeT(toInt128(nimsets.cardSet(g.config, a)), n, idgen, g) of mBitnotI: if n.typ.isUnsigned: - result = newIntNodeT(bitnot(getInt(a)).maskBytes(int(n.typ.size)), n, idgen, g) + result = newIntNodeT(bitnot(getInt(a)).maskBytes(int(getSize(g.config, n.typ))), n, idgen, g) else: result = newIntNodeT(bitnot(getInt(a)), n, idgen, g) of mLengthArray: result = newIntNodeT(lengthOrd(g.config, a.typ), n, idgen, g) @@ -248,23 +248,23 @@ proc evalOp(m: TMagic, n, a, b, c: PNode; idgen: IdGenerator; g: ModuleGraph): P of mBitorI, mOr: result = newIntNodeT(bitor(getInt(a), getInt(b)), n, idgen, g) of mBitxorI, mXor: result = newIntNodeT(bitxor(getInt(a), getInt(b)), n, idgen, g) of mAddU: - let val = maskBytes(getInt(a) + getInt(b), int(n.typ.size)) + let val = maskBytes(getInt(a) + getInt(b), int(getSize(g.config, n.typ))) result = newIntNodeT(val, n, idgen, g) of mSubU: - let val = maskBytes(getInt(a) - getInt(b), int(n.typ.size)) + let val = maskBytes(getInt(a) - getInt(b), int(getSize(g.config, n.typ))) result = newIntNodeT(val, n, idgen, g) # echo "subU: ", val, " n: ", n, " result: ", val of mMulU: - let val = maskBytes(getInt(a) * getInt(b), int(n.typ.size)) + let val = maskBytes(getInt(a) * getInt(b), int(getSize(g.config, n.typ))) result = newIntNodeT(val, n, idgen, g) of mModU: - let argA = maskBytes(getInt(a), int(a.typ.size)) - let argB = maskBytes(getInt(b), int(a.typ.size)) + let argA = maskBytes(getInt(a), int(getSize(g.config, a.typ))) + let argB = maskBytes(getInt(b), int(getSize(g.config, a.typ))) if argB != Zero: result = newIntNodeT(argA mod argB, n, idgen, g) of mDivU: - let argA = maskBytes(getInt(a), int(a.typ.size)) - let argB = maskBytes(getInt(b), int(a.typ.size)) + let argA = maskBytes(getInt(a), int(getSize(g.config, a.typ))) + let argB = maskBytes(getInt(b), int(getSize(g.config, a.typ))) if argB != Zero: result = newIntNodeT(argA div argB, n, idgen, g) of mLeSet: result = newIntNodeT(toInt128(ord(containsSets(g.config, a, b))), n, idgen, g) diff --git a/tests/slice/tdistinct.nim b/tests/slice/tdistinct.nim new file mode 100644 index 000000000..d99b529a7 --- /dev/null +++ b/tests/slice/tdistinct.nim @@ -0,0 +1,2 @@ +type Foo = distinct uint64 +const slice = 0 ..< 42.Foo From ebd88725f9d899440408cc635b79041942bc4fd0 Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 7 Dec 2021 05:41:42 +0800 Subject: [PATCH 2686/3768] remove `sysspawn` which is dead code (#19218) --- lib/system/sysspawn.nim | 187 ---------------------------------------- 1 file changed, 187 deletions(-) delete mode 100644 lib/system/sysspawn.nim diff --git a/lib/system/sysspawn.nim b/lib/system/sysspawn.nim deleted file mode 100644 index c9b9a68f0..000000000 --- a/lib/system/sysspawn.nim +++ /dev/null @@ -1,187 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2015 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## Implements Nim's 'spawn'. - -when not declared(NimString): - {.error: "You must not import this module explicitly".} - -{.push stackTrace:off.} - -# We declare our own condition variables here to get rid of the dummy lock -# on Windows: - -type - CondVar = object - c: SysCond - stupidLock: SysLock - counter: int - -proc createCondVar(): CondVar = - initSysCond(result.c) - initSysLock(result.stupidLock) - #acquireSys(result.stupidLock) - -proc destroyCondVar(c: var CondVar) {.inline.} = - deinitSysCond(c.c) - -proc await(cv: var CondVar) = - acquireSys(cv.stupidLock) - while cv.counter <= 0: - waitSysCond(cv.c, cv.stupidLock) - dec cv.counter - releaseSys(cv.stupidLock) - -proc signal(cv: var CondVar) = - acquireSys(cv.stupidLock) - inc cv.counter - releaseSys(cv.stupidLock) - signalSysCond(cv.c) - -type - FastCondVar = object - event, slowPath: bool - slow: CondVar - -proc createFastCondVar(): FastCondVar = - initSysCond(result.slow.c) - initSysLock(result.slow.stupidLock) - #acquireSys(result.slow.stupidLock) - result.event = false - result.slowPath = false - -proc await(cv: var FastCondVar) = - #for i in 0 .. 50: - # if cas(addr cv.event, true, false): - # # this is a HIT: Triggers > 95% in my tests. - # return - # cpuRelax() - #cv.slowPath = true - # XXX For some reason this crashes some test programs - await(cv.slow) - cv.event = false - -proc signal(cv: var FastCondVar) = - cv.event = true - #if cas(addr cv.slowPath, true, false): - signal(cv.slow) - -type - Barrier* {.compilerproc.} = object - counter: int - cv: CondVar - -proc barrierEnter*(b: ptr Barrier) {.compilerproc.} = - atomicInc b.counter - -proc barrierLeave*(b: ptr Barrier) {.compilerproc.} = - atomicDec b.counter - if b.counter <= 0: signal(b.cv) - -proc openBarrier*(b: ptr Barrier) {.compilerproc.} = - b.counter = 0 - b.cv = createCondVar() - -proc closeBarrier*(b: ptr Barrier) {.compilerproc.} = - await(b.cv) - destroyCondVar(b.cv) - -{.pop.} - -# ---------------------------------------------------------------------------- - -type - WorkerProc = proc (thread, args: pointer) {.nimcall, gcsafe.} - Worker = object - taskArrived: CondVar - taskStarted: FastCondVar #\ - # task data: - f: WorkerProc - data: pointer - ready: bool # put it here for correct alignment! - -proc nimArgsPassingDone(p: pointer) {.compilerproc.} = - let w = cast[ptr Worker](p) - signal(w.taskStarted) - -var gSomeReady = createFastCondVar() - -proc slave(w: ptr Worker) {.thread.} = - while true: - w.ready = true # If we instead signal "workerReady" we need the scheduler - # to notice this. The scheduler could then optimize the - # layout of the worker threads (e.g. keep the list sorted) - # so that no search for a "ready" thread is necessary. - # This might be implemented later, but is more tricky than - # it looks because 'spawn' itself can run concurrently. - signal(gSomeReady) - await(w.taskArrived) - assert(not w.ready) - # shield against spurious wakeups: - if w.data != nil: - w.f(w, w.data) - w.data = nil - -const NumThreads = 4 - -var - workers: array[NumThreads, Thread[ptr Worker]] - workersData: array[NumThreads, Worker] - -proc setup() = - for i in 0 ..< NumThreads: - workersData[i].taskArrived = createCondVar() - workersData[i].taskStarted = createFastCondVar() - createThread(workers[i], slave, addr(workersData[i])) - -proc preferSpawn*(): bool = - ## Use this proc to determine quickly if a 'spawn' or a direct call is - ## preferable. If it returns 'true' a 'spawn' may make sense. In general - ## it is not necessary to call this directly; use 'spawnX' instead. - result = gSomeReady.event - -proc spawn*(call: typed) {.magic: "Spawn".} - ## always spawns a new task, so that the 'call' is never executed on - ## the calling thread. 'call' has to be proc call 'p(...)' where 'p' - ## is gcsafe and has 'void' as the return type. - -template spawnX*(call: typed) = - ## spawns a new task if a CPU core is ready, otherwise executes the - ## call in the calling thread. Usually it is advised to - ## use 'spawn' in order to not block the producer for an unknown - ## amount of time. 'call' has to be proc call 'p(...)' where 'p' - ## is gcsafe and has 'void' as the return type. - if preferSpawn(): spawn call - else: call - -proc nimSpawn(fn: WorkerProc; data: pointer) {.compilerproc.} = - # implementation of 'spawn' that is used by the code generator. - while true: - for i in 0.. high(workers): - let w = addr(workersData[i]) - if cas(addr w.ready, true, false): - w.data = data - w.f = fn - signal(w.taskArrived) - await(w.taskStarted) - return - await(gSomeReady) - -proc sync*() = - ## a simple barrier to wait for all spawn'ed tasks. If you need more elaborate - ## waiting, you have to use an explicit barrier. - while true: - var allReady = true - for i in 0 .. high(workers): - if not allReady: break - allReady = allReady and workersData[i].ready - if allReady: break - await(gSomeReady) - -setup() From 93c8427fca0bdc0f32c8f7c011c661808ea1e6dc Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 7 Dec 2021 17:59:43 +0100 Subject: [PATCH 2687/3768] re-enable chronos testing once again [backport:1.2] (#19222) --- testament/important_packages.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index e11799e73..7e15e7c65 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -51,7 +51,7 @@ pkg "cascade" pkg "cello" pkg "chroma" pkg "chronicles", "nim c -o:chr -r chronicles.nim" -pkg "chronos", "nim c -r -d:release tests/testall", allowFailure = true # pending https://github.com/nim-lang/Nim/issues/17130 +pkg "chronos", "nim c -r -d:release tests/testall" pkg "cligen", "nim c --path:. -r cligen.nim" pkg "combparser", "nimble test --gc:orc" pkg "compactdict" From cd592ed85ba3bf440d7b24a4bc5b117d1a5f8725 Mon Sep 17 00:00:00 2001 From: Tanguy Date: Tue, 7 Dec 2021 18:01:22 +0100 Subject: [PATCH 2688/3768] fix #19193 (#19195) [backport:1.2] --- compiler/cgen.nim | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index b669483cc..c862d4d30 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -443,8 +443,14 @@ proc constructLoc(p: BProc, loc: var TLoc, isTemp = false) = if optSeqDestructors in p.config.globalOptions and skipTypes(typ, abstractInst + {tyStatic}).kind in {tyString, tySequence}: linefmt(p, cpsStmts, "$1.len = 0; $1.p = NIM_NIL;$n", [rdLoc(loc)]) elif not isComplexValueType(typ): - linefmt(p, cpsStmts, "$1 = ($2)0;$n", [rdLoc(loc), - getTypeDesc(p.module, typ, mapTypeChooser(loc))]) + if containsGarbageCollectedRef(loc.t): + var nilLoc: TLoc + initLoc(nilLoc, locTemp, loc.lode, OnStack) + nilLoc.r = rope("NIM_NIL") + genRefAssign(p, loc, nilLoc) + else: + linefmt(p, cpsStmts, "$1 = ($2)0;$n", [rdLoc(loc), + getTypeDesc(p.module, typ, mapTypeChooser(loc))]) else: if not isTemp or containsGarbageCollectedRef(loc.t): # don't use nimZeroMem for temporary values for performance if we can From 7806ec525eec83d250141a32cfa197949ea1a42b Mon Sep 17 00:00:00 2001 From: rockcavera Date: Wed, 8 Dec 2021 02:48:34 -0300 Subject: [PATCH 2689/3768] Making TCC work again on Windows --cpu:amd64 - fix #16326 (#19221) * fix #16326 * removing comments --- lib/system/io.nim | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/system/io.nim b/lib/system/io.nim index 2ad43acdb..661d1a9ba 100644 --- a/lib/system/io.nim +++ b/lib/system/io.nim @@ -109,8 +109,22 @@ when defined(windows): else: proc c_fseek(f: File, offset: int64, whence: cint): cint {. importc: "_fseeki64", header: "", tags: [].} - proc c_ftell(f: File): int64 {. - importc: "_ftelli64", header: "", tags: [].} + when defined(tcc): + proc c_fsetpos(f: File, pos: var int64): int32 {. + importc: "fsetpos", header: "", tags: [].} + proc c_fgetpos(f: File, pos: var int64): int32 {. + importc: "fgetpos", header: "", tags: [].} + proc c_telli64(f: cint): int64 {. + importc: "_telli64", header: "", tags: [].} + proc c_ftell(f: File): int64 = + # Taken from https://pt.osdn.net/projects/mingw/scm/git/mingw-org-wsl/blobs/5.4-trunk/mingwrt/mingwex/stdio/ftelli64.c + result = -1'i64 + var pos: int64 + if c_fgetpos(f, pos) == 0 and c_fsetpos(f, pos) == 0: + result = c_telli64(c_fileno(f)) + else: + proc c_ftell(f: File): int64 {. + importc: "_ftelli64", header: "", tags: [].} else: proc c_fseek(f: File, offset: int64, whence: cint): cint {. importc: "fseeko", header: "", tags: [].} From 099285494124dedd7d83a848e213f895f54f9e81 Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 8 Dec 2021 15:40:35 +0800 Subject: [PATCH 2690/3768] Atomic inc/dec should use ATOMIC_SEQ_CST (#19212) --- lib/system/atomics.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/system/atomics.nim b/lib/system/atomics.nim index 75731764b..8d29c287d 100644 --- a/lib/system/atomics.nim +++ b/lib/system/atomics.nim @@ -217,7 +217,7 @@ else: proc atomicInc*(memLoc: var int, x: int = 1): int = when someGcc and hasThreadSupport: - result = atomicAddFetch(memLoc.addr, x, ATOMIC_RELAXED) + result = atomicAddFetch(memLoc.addr, x, ATOMIC_SEQ_CST) elif someVcc and hasThreadSupport: result = addAndFetch(memLoc.addr, x) inc(result, x) @@ -228,9 +228,9 @@ proc atomicInc*(memLoc: var int, x: int = 1): int = proc atomicDec*(memLoc: var int, x: int = 1): int = when someGcc and hasThreadSupport: when declared(atomicSubFetch): - result = atomicSubFetch(memLoc.addr, x, ATOMIC_RELAXED) + result = atomicSubFetch(memLoc.addr, x, ATOMIC_SEQ_CST) else: - result = atomicAddFetch(memLoc.addr, -x, ATOMIC_RELAXED) + result = atomicAddFetch(memLoc.addr, -x, ATOMIC_SEQ_CST) elif someVcc and hasThreadSupport: result = addAndFetch(memLoc.addr, -x) dec(result, x) From 502ac4ed5e539146d31920d50020ae35668f4755 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 9 Dec 2021 13:56:51 +0100 Subject: [PATCH 2691/3768] fixes a converter handling regression that caused private converters to leak into client modules; fixes #19213; [backport:1.6] (#19229) --- compiler/importer.nim | 6 ++++-- tests/converter/mdontleak.nim | 3 +++ tests/converter/tdontleak.nim | 10 ++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 tests/converter/mdontleak.nim create mode 100644 tests/converter/tdontleak.nim diff --git a/compiler/importer.nim b/compiler/importer.nim index af392f849..acca2c644 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -185,11 +185,13 @@ template addUnnamedIt(c: PContext, fromMod: PSym; filter: untyped) {.dirty.} = for it in mitems c.graph.ifaces[fromMod.position].converters: if filter: loadPackedSym(c.graph, it) - addConverter(c, it) + if sfExported in it.sym.flags: + addConverter(c, it) for it in mitems c.graph.ifaces[fromMod.position].patterns: if filter: loadPackedSym(c.graph, it) - addPattern(c, it) + if sfExported in it.sym.flags: + addPattern(c, it) for it in mitems c.graph.ifaces[fromMod.position].pureEnums: if filter: loadPackedSym(c.graph, it) diff --git a/tests/converter/mdontleak.nim b/tests/converter/mdontleak.nim new file mode 100644 index 000000000..e55c3f87c --- /dev/null +++ b/tests/converter/mdontleak.nim @@ -0,0 +1,3 @@ + +converter toBool(x: uint32): bool = x != 0 +# Note: This convertes is not exported! diff --git a/tests/converter/tdontleak.nim b/tests/converter/tdontleak.nim new file mode 100644 index 000000000..4965fa90a --- /dev/null +++ b/tests/converter/tdontleak.nim @@ -0,0 +1,10 @@ +discard """ + output: '''5''' +joinable: false +""" + +import mdontleak +# bug #19213 + +let a = 5'u32 +echo a From 742e9d65ad6b56387dc6bf9a2be1b95c510fd0c4 Mon Sep 17 00:00:00 2001 From: wenghongquan <93646063+wenghongquan@users.noreply.github.com> Date: Thu, 9 Dec 2021 21:00:33 +0800 Subject: [PATCH 2692/3768] Add support for LoongArch (#19223) * Add support for LoongArch * Update compiler/installer.ini Co-authored-by: Andreas Rumpf --- compiler/platform.nim | 5 +++-- lib/system.nim | 2 +- lib/system/platforms.nim | 4 +++- tools/nim.zsh-completion | 1 + tools/niminst/buildsh.nimf | 2 ++ tools/niminst/makefile.nimf | 3 +++ 6 files changed, 13 insertions(+), 4 deletions(-) diff --git a/compiler/platform.nim b/compiler/platform.nim index eb1398677..4e6054d5c 100644 --- a/compiler/platform.nim +++ b/compiler/platform.nim @@ -202,7 +202,7 @@ type cpuPowerpc64el, cpuSparc, cpuVm, cpuHppa, cpuIa64, cpuAmd64, cpuMips, cpuMipsel, cpuArm, cpuArm64, cpuJS, cpuNimVM, cpuAVR, cpuMSP430, cpuSparc64, cpuMips64, cpuMips64el, cpuRiscV32, cpuRiscV64, cpuEsp, cpuWasm32, - cpuE2k + cpuE2k, cpuLoongArch64 type TInfoCPU* = tuple[name: string, intSize: int, endian: Endianness, @@ -238,7 +238,8 @@ const (name: "riscv64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64), (name: "esp", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32), (name: "wasm32", intSize: 32, endian: littleEndian, floatSize: 64, bit: 32), - (name: "e2k", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64)] + (name: "e2k", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64), + (name: "loongarch64", intSize: 64, endian: littleEndian, floatSize: 64, bit: 64)] type Target* = object diff --git a/lib/system.nim b/lib/system.nim index 012aefe6a..c424cbc1b 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1126,7 +1126,7 @@ const ## Possible values: ## `"i386"`, `"alpha"`, `"powerpc"`, `"powerpc64"`, `"powerpc64el"`, ## `"sparc"`, `"amd64"`, `"mips"`, `"mipsel"`, `"arm"`, `"arm64"`, - ## `"mips64"`, `"mips64el"`, `"riscv32"`, `"riscv64"`. + ## `"mips64"`, `"mips64el"`, `"riscv32"`, `"riscv64"`, '"loongarch64"'. seqShallowFlag = low(int) strlitFlag = 1 shl (sizeof(int)*8 - 2) # later versions of the codegen \ diff --git a/lib/system/platforms.nim b/lib/system/platforms.nim index 5bca8cb1c..b4c9f1f06 100644 --- a/lib/system/platforms.nim +++ b/lib/system/platforms.nim @@ -36,7 +36,8 @@ type riscv32, ## RISC-V 32-bit processor riscv64, ## RISC-V 64-bit processor wasm32, ## WASM, 32-bit - e2k ## MCST Elbrus 2000 + e2k, ## MCST Elbrus 2000 + loongarch64 ## LoongArch 64-bit processor OsPlatform* {.pure.} = enum ## the OS this program will run on. none, dos, windows, os2, linux, morphos, skyos, solaris, @@ -95,5 +96,6 @@ const elif defined(riscv64): CpuPlatform.riscv64 elif defined(wasm32): CpuPlatform.wasm32 elif defined(e2k): CpuPlatform.e2k + elif defined(loongarch64): CpuPlatform.loongarch64 else: CpuPlatform.none ## the CPU this program will run on. diff --git a/tools/nim.zsh-completion b/tools/nim.zsh-completion index 07a221e9c..45477d860 100644 --- a/tools/nim.zsh-completion +++ b/tools/nim.zsh-completion @@ -95,6 +95,7 @@ _nim() { '*--cpu=sparc64[compile for SPARC64 architecture]' \ '*--cpu=vm[compile for Nim VM]' \ '*--cpu=wasm32[compile to WASM 32]' \ + '*--cpu=loongarch64[compile for LoongArch64 architecture]' \ '*--gc=refc[use reference counting garbage collection]' \ '*--gc=arc[use ARC garbage collection]' \ '*--gc=orc[use ORC garbage collection]' \ diff --git a/tools/niminst/buildsh.nimf b/tools/niminst/buildsh.nimf index b81ac731f..464c70abf 100644 --- a/tools/niminst/buildsh.nimf +++ b/tools/niminst/buildsh.nimf @@ -229,6 +229,8 @@ case $ucpu in mycpu="riscv64" ;; *e2k* ) mycpu="e2k" ;; + *loongarch64* ) + mycpu="loongarch64" ;; *) echo 2>&1 "Error: unknown processor: $ucpu" exit 1 diff --git a/tools/niminst/makefile.nimf b/tools/niminst/makefile.nimf index c4f2f0e79..a1f3fa977 100644 --- a/tools/niminst/makefile.nimf +++ b/tools/niminst/makefile.nimf @@ -169,6 +169,9 @@ endif ifeq ($(ucpu),e2k) mycpu = e2k endif +ifeq ($(ucpu),loongarch64) + mycpu = loongarch64 +endif ifndef mycpu $(error unknown CPU architecture: $(ucpu) See makefile.nimf) endif From 99f8793502e65cd43f6e301557d428758948c3ca Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 9 Dec 2021 22:37:59 +0800 Subject: [PATCH 2693/3768] remove `std/sharedstrings` (#19228) * remove std/sharedstrings it has been broken since 0.18.0 * rephrase the changelog entry --- changelog.md | 2 + lib/deprecated/pure/sharedstrings.nim | 154 -------------------------- testament/testament.nim | 1 - 3 files changed, 2 insertions(+), 155 deletions(-) delete mode 100644 lib/deprecated/pure/sharedstrings.nim diff --git a/changelog.md b/changelog.md index a8f1920e9..ebad54fe4 100644 --- a/changelog.md +++ b/changelog.md @@ -14,6 +14,8 @@ - Optional parameters in combination with `: body` syntax (RFC #405) are now opt-in via `experimental:flexibleOptionalParams`. +- `std/sharedstrings` module is removed. + ## Standard library additions and changes - `macros.parseExpr` and `macros.parseStmt` now accept an optional diff --git a/lib/deprecated/pure/sharedstrings.nim b/lib/deprecated/pure/sharedstrings.nim deleted file mode 100644 index 99963e1f2..000000000 --- a/lib/deprecated/pure/sharedstrings.nim +++ /dev/null @@ -1,154 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2015 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - -## Shared string support for Nim. - -{.deprecated.} - -type - UncheckedCharArray = UncheckedArray[char] - -type - Buffer = ptr object - refcount: int - capacity, realLen: int - data: UncheckedCharArray - - SharedString* = object ## A string that can be shared. Slicing is O(1). - buffer: Buffer - first, len: int - -proc decRef(b: Buffer) {.inline.} = - if atomicDec(b.refcount) <= 0: - deallocShared(b) - -proc incRef(b: Buffer) {.inline.} = - atomicInc(b.refcount) - -{.experimental.} - -proc `=destroy`*(s: SharedString) = - #echo "destroyed" - if not s.buffer.isNil: - decRef(s.buffer) - -when false: - proc `=copy`*(dest: var SharedString; src: SharedString) = - incRef(src.buffer) - if not dest.buffer.isNil: - decRef(dest.buffer) - dest.buffer = src.buffer - dest.first = src.first - dest.len = src.len - -proc len*(s: SharedString): int = s.len - -proc `[]`*(s: SharedString; i: Natural): char = - if i < s.len: result = s.buffer.data[i+s.first] - else: raise newException(IndexDefect, formatErrorIndexBound(i, s.len-1)) - -proc `[]=`*(s: var SharedString; i: Natural; value: char) = - if i < s.len: s.buffer.data[i+s.first] = value - else: raise newException(IndexDefect, formatErrorIndexBound(i, s.len-1)) - -proc `[]`*(s: SharedString; ab: HSlice[int, int]): SharedString = - #incRef(src.buffer) - if ab.a < s.len: - result.buffer = s.buffer - result.first = ab.a - result.len = min(s.len, ab.b - ab.a + 1) - # else: produce empty string ;-) - -proc newBuffer(cap, len: int): Buffer = - assert cap >= len - result = cast[Buffer](allocShared0(sizeof(int)*3 + cap)) - result.refcount = 0 - result.capacity = cap - result.realLen = len - -proc newSharedString*(len: Natural): SharedString = - if len != 0: - # optimization: Don't have an underlying buffer when 'len == 0' - result.buffer = newBuffer(len, len) - result.first = 0 - result.len = len - -proc newSharedString*(s: string): SharedString = - let len = s.len - if len != 0: - # optimization: Don't have an underlying buffer when 'len == 0' - result.buffer = newBuffer(len, len) - copyMem(addr result.buffer.data[0], cstring(s), s.len) - result.first = 0 - result.len = len - -when declared(atomicLoadN): - template load(x): untyped = atomicLoadN(addr x, ATOMIC_SEQ_CST) -else: - # XXX Fixme - template load(x): untyped = x - -proc add*(s: var SharedString; t: cstring; len: Natural) = - if len == 0: return - let newLen = s.len + len - if s.buffer.isNil: - s.buffer = newBuffer(len, len) - copyMem(addr s.buffer.data[0], t, len) - s.len = len - elif newLen >= s.buffer.capacity or s.first != 0 or - s.len != s.buffer.realLen or load(s.buffer.refcount) > 1: - let oldBuf = s.buffer - s.buffer = newBuffer(max(s.buffer.capacity * 3 div 2, newLen), newLen) - copyMem(addr s.buffer.data[0], addr oldBuf.data[s.first], s.len) - copyMem(addr s.buffer.data[s.len], t, len) - decRef(oldBuf) - else: - copyMem(addr s.buffer.data[s.len], t, len) - s.buffer.realLen += len - s.len += len - -proc add*(s: var SharedString; t: string) = - s.add(t.cstring, t.len) - -proc rawData*(s: var SharedString): pointer = - if s.buffer.isNil: result = nil - else: result = addr s.buffer.data[s.first] - -proc add*(s: var SharedString; t: SharedString) = - if t.buffer.isNil: return - s.add(cast[cstring](addr s.buffer.data[s.first]), t.len) - -proc `$`*(s: SharedString): string = - result = newString(s.len) - if s.len > 0: - copyMem(addr result[0], addr s.buffer.data[s.first], s.len) - -proc `==`*(s: SharedString; t: string): bool = - if s.buffer.isNil: result = t.len == 0 - else: result = t.len == s.len and equalMem(addr s.buffer.data[s.first], - cstring(t), t.len) - -proc `==`*(s, t: SharedString): bool = - if s.buffer.isNil: result = t.len == 0 - else: result = t.len == s.len and equalMem(addr s.buffer.data[s.first], - addr t.buffer.data[t.first], t.len) - -iterator items*(s: SharedString): char = - let buf = s.buffer.data - let x = s.first - if buf != nil: - for i in 0.. Date: Fri, 10 Dec 2021 01:49:31 +0800 Subject: [PATCH 2694/3768] add comments to spawn and pinnedSpawn (#19230) `spawn` uses `nimSpawn3` internally and `pinnedSpawn` uses `nimSpawn4` internally. I comment it in order to help contributors get the gist of its functionality. --- lib/pure/concurrency/threadpool.nim | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/pure/concurrency/threadpool.nim b/lib/pure/concurrency/threadpool.nim index f8a5f9ba8..9334b4833 100644 --- a/lib/pure/concurrency/threadpool.nim +++ b/lib/pure/concurrency/threadpool.nim @@ -448,19 +448,21 @@ proc preferSpawn*(): bool = ## <#spawnX.t>`_ instead. result = gSomeReady.counter > 0 -proc spawn*(call: sink typed) {.magic: "Spawn".} +proc spawn*(call: sink typed) {.magic: "Spawn".} = ## Always spawns a new task, so that the `call` is never executed on ## the calling thread. ## ## `call` has to be a proc call `p(...)` where `p` is gcsafe and has a ## return type that is either `void` or compatible with `FlowVar[T]`. + discard "It uses `nimSpawn3` internally" -proc pinnedSpawn*(id: ThreadId; call: sink typed) {.magic: "Spawn".} +proc pinnedSpawn*(id: ThreadId; call: sink typed) {.magic: "Spawn".} = ## Always spawns a new task on the worker thread with `id`, so that ## the `call` is **always** executed on the thread. ## ## `call` has to be a proc call `p(...)` where `p` is gcsafe and has a ## return type that is either `void` or compatible with `FlowVar[T]`. + discard "It uses `nimSpawn4` internally" template spawnX*(call) = ## Spawns a new task if a CPU core is ready, otherwise executes the From 32d4bf352585a4fc4a6fa1bd24b270af087b0372 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 9 Dec 2021 22:23:16 +0100 Subject: [PATCH 2695/3768] fixes an old ARC bug: the produced copy/sink operations don't copy the hidden type field for objects with enabled inheritance; fixes #19205 [backport:1.6] (#19232) --- compiler/ast.nim | 2 +- compiler/ccgexprs.nim | 8 ++++++++ compiler/ccgtypes.nim | 2 +- compiler/liftdestructors.nim | 10 ++++++++++ compiler/types.nim | 3 +++ tests/arc/tarcmisc.nim | 15 +++++++++++++++ 6 files changed, 38 insertions(+), 2 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index 9086860b9..4286f940f 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -673,7 +673,7 @@ type mSwap, mIsNil, mArrToSeq, mNewString, mNewStringOfCap, mParseBiggestFloat, mMove, mWasMoved, mDestroy, mTrace, - mDefault, mUnown, mFinished, mIsolate, mAccessEnv, mReset, + mDefault, mUnown, mFinished, mIsolate, mAccessEnv, mAccessTypeField, mReset, mArray, mOpenArray, mRange, mSet, mSeq, mVarargs, mRef, mPtr, mVar, mDistinct, mVoid, mTuple, mOrdinal, mIterableType, diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index fc09fefc3..1f1035115 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1741,6 +1741,13 @@ proc genGetTypeInfoV2(p: BProc, e: PNode, d: var TLoc) = # use the dynamic type stored at offset 0: putIntoDest(p, d, e, rdMType(p, a, nilCheck)) +proc genAccessTypeField(p: BProc; e: PNode; d: var TLoc) = + var a: TLoc + initLocExpr(p, e[1], a) + var nilCheck = Rope(nil) + # use the dynamic type stored at offset 0: + putIntoDest(p, d, e, rdMType(p, a, nilCheck)) + template genDollar(p: BProc, n: PNode, d: var TLoc, frmt: string) = var a: TLoc initLocExpr(p, n[1], a) @@ -2449,6 +2456,7 @@ proc genMagicExpr(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mMove: genMove(p, e, d) of mDestroy: genDestroy(p, e) of mAccessEnv: unaryExpr(p, e, d, "$1.ClE_0") + of mAccessTypeField: genAccessTypeField(p, e, d) of mSlice: genSlice(p, e, d) of mTrace: discard "no code to generate" else: diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index e7e099659..740ce5f19 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -583,7 +583,7 @@ proc getRecordDesc(m: BModule, typ: PType, name: Rope, if typ.kind == tyObject: if typ[0] == nil: - if (typ.sym != nil and sfPure in typ.sym.flags) or tfFinal in typ.flags: + if lacksMTypeField(typ): appcg(m, result, " {$n", []) else: if optTinyRtti in m.config.globalOptions: diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index cc7e36cdb..51b4ddfb0 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -941,6 +941,12 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp incl result.flags, sfFromGeneric incl result.flags, sfGeneratedOp +proc genTypeFieldCopy(c: var TLiftCtx; t: PType; body, x, y: PNode) = + let xx = genBuiltin(c, mAccessTypeField, "accessTypeField", x) + let yy = genBuiltin(c, mAccessTypeField, "accessTypeField", y) + xx.typ = getSysType(c.g, c.info, tyPointer) + yy.typ = xx.typ + body.add newAsgnStmt(xx, yy) proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp; info: TLineInfo; idgen: IdGenerator): PSym = @@ -980,6 +986,10 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp; fillStrOp(a, typ, result.ast[bodyPos], d, src) else: fillBody(a, typ, result.ast[bodyPos], d, src) + if tk == tyObject and a.kind in {attachedAsgn, attachedSink, attachedDeepCopy} and not lacksMTypeField(typ): + # bug #19205: Do not forget to also copy the hidden type field: + genTypeFieldCopy(a, typ, result.ast[bodyPos], d, src) + if not a.canRaise: incl result.flags, sfNeverRaises completePartialOp(g, idgen.module, typ, kind, result) diff --git a/compiler/types.nim b/compiler/types.nim index 7cf02c240..007a61356 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -1705,3 +1705,6 @@ proc isCharArrayPtr*(t: PType; allowPointerToChar: bool): bool = result = allowPointerToChar else: discard + +proc lacksMTypeField*(typ: PType): bool {.inline.} = + (typ.sym != nil and sfPure in typ.sym.flags) or tfFinal in typ.flags diff --git a/tests/arc/tarcmisc.nim b/tests/arc/tarcmisc.nim index d259e1197..45d1514cd 100644 --- a/tests/arc/tarcmisc.nim +++ b/tests/arc/tarcmisc.nim @@ -30,6 +30,7 @@ ok true copying 123 +42 closed destroying variable: 20 destroying variable: 10 @@ -482,3 +483,17 @@ method testMethod(self: BrokenObject) {.base.} = let mikasa = BrokenObject() mikasa.testMethod() + +# bug #19205 +type + InputSectionBase* = object of RootObj + relocations*: seq[int] # traced reference. string has a similar SIGSEGV. + InputSection* = object of InputSectionBase + +proc fooz(sec: var InputSectionBase) = + if sec of InputSection: # this line SIGSEGV. + echo 42 + +var sec = create(InputSection) +sec[] = InputSection(relocations: newSeq[int]()) +fooz sec[] From 69aabdab800077e9aaa08344494c83138a02f57c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C8=98tefan=20Talpalaru?= Date: Fri, 10 Dec 2021 06:31:29 +0100 Subject: [PATCH 2696/3768] nimRawSetjmp: support Windows (#19197) * nimRawSetjmp: support Windows Using `_setjmp()` directly is required to avoid some rare (but very annoying) exception-related stack corruption leading to segfaults on Windows, with Mingw-w64 and SEH. More details: https://github.com/status-im/nimbus-eth2/issues/3121 Also add "nimBuiltinSetjmp" - mostly for benchmarking. * fix for Apple's Clang++ --- compiler/ccgstmts.nim | 13 +++- doc/nimc.rst | 14 ++++ lib/system/ansi_c.nim | 46 +++++++++-- tests/exception/texceptions.nim | 6 +- tests/exception/texceptions2.nim | 130 +++++++++++++++++++++++++++++++ 5 files changed, 199 insertions(+), 10 deletions(-) create mode 100644 tests/exception/texceptions2.nim diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 559849f0d..7fecc1475 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -1356,8 +1356,19 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) = linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", [safePoint]) elif isDefined(p.config, "nimSigSetjmp"): linefmt(p, cpsStmts, "$1.status = sigsetjmp($1.context, 0);$n", [safePoint]) + elif isDefined(p.config, "nimBuiltinSetjmp"): + linefmt(p, cpsStmts, "$1.status = __builtin_setjmp($1.context);$n", [safePoint]) elif isDefined(p.config, "nimRawSetjmp"): - linefmt(p, cpsStmts, "$1.status = _setjmp($1.context);$n", [safePoint]) + if isDefined(p.config, "mswindows"): + # The Windows `_setjmp()` takes two arguments, with the second being an + # undocumented buffer used by the SEH mechanism for stack unwinding. + # Mingw-w64 has been trying to get it right for years, but it's still + # prone to stack corruption during unwinding, so we disable that by setting + # it to NULL. + # More details: https://github.com/status-im/nimbus-eth2/issues/3121 + linefmt(p, cpsStmts, "$1.status = _setjmp($1.context, 0);$n", [safePoint]) + else: + linefmt(p, cpsStmts, "$1.status = _setjmp($1.context);$n", [safePoint]) else: linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", [safePoint]) lineCg(p, cpsStmts, "if ($1.status == 0) {$n", [safePoint]) diff --git a/doc/nimc.rst b/doc/nimc.rst index eb066e748..0ab1501ff 100644 --- a/doc/nimc.rst +++ b/doc/nimc.rst @@ -165,6 +165,20 @@ ignored too. `--define:FOO`:option: and `--define:foo`:option: are identical. Compile-time symbols starting with the `nim` prefix are reserved for the implementation and should not be used elsewhere. +========================== ============================================ +Name Description +========================== ============================================ +nimStdSetjmp Use the standard `setjmp()/longjmp()` library + functions for setjmp-based exceptions. This is + the default on most platforms. +nimSigSetjmp Use `sigsetjmp()/siglongjmp()` for setjmp-based exceptions. +nimRawSetjmp Use `_setjmp()/_longjmp()` on POSIX and `_setjmp()/longjmp()` + on Windows, for setjmp-based exceptions. It's the default on + BSDs and BSD-like platforms, where it's significantly faster + than the standard functions. +nimBuiltinSetjmp Use `__builtin_setjmp()/__builtin_longjmp()` for setjmp-based + exceptions. This will not work if an exception is being thrown + and caught inside the same procedure. Useful for benchmarking. Configuration files ------------------- diff --git a/lib/system/ansi_c.nim b/lib/system/ansi_c.nim index 259d36633..23fb9fdef 100644 --- a/lib/system/ansi_c.nim +++ b/lib/system/ansi_c.nim @@ -31,7 +31,10 @@ proc c_abort*() {. importc: "abort", header: "", noSideEffect, noreturn.} -when defined(linux) and defined(amd64): +when defined(nimBuiltinSetjmp): + type + C_JmpBuf* = array[5, pointer] +elif defined(linux) and defined(amd64): type C_JmpBuf* {.importc: "jmp_buf", header: "", bycopy.} = object abi: array[200 div sizeof(clong), clong] @@ -92,18 +95,47 @@ when defined(macosx): elif defined(haiku): const SIGBUS* = cint(30) -when defined(nimSigSetjmp) and not defined(nimStdSetjmp): +# "nimRawSetjmp" is defined by default for certain platforms, so we need the +# "nimStdSetjmp" escape hatch with it. +when defined(nimSigSetjmp): proc c_longjmp*(jmpb: C_JmpBuf, retval: cint) {. header: "", importc: "siglongjmp".} - template c_setjmp*(jmpb: C_JmpBuf): cint = + proc c_setjmp*(jmpb: C_JmpBuf): cint = proc c_sigsetjmp(jmpb: C_JmpBuf, savemask: cint): cint {. header: "", importc: "sigsetjmp".} c_sigsetjmp(jmpb, 0) +elif defined(nimBuiltinSetjmp): + proc c_longjmp*(jmpb: C_JmpBuf, retval: cint) = + # Apple's Clang++ has trouble converting array names to pointers, so we need + # to be very explicit here. + proc c_builtin_longjmp(jmpb: ptr pointer, retval: cint) {. + importc: "__builtin_longjmp", nodecl.} + # The second parameter needs to be 1 and sometimes the C/C++ compiler checks it. + c_builtin_longjmp(unsafeAddr jmpb[0], 1) + proc c_setjmp*(jmpb: C_JmpBuf): cint = + proc c_builtin_setjmp(jmpb: ptr pointer): cint {. + importc: "__builtin_setjmp", nodecl.} + c_builtin_setjmp(unsafeAddr jmpb[0]) elif defined(nimRawSetjmp) and not defined(nimStdSetjmp): - proc c_longjmp*(jmpb: C_JmpBuf, retval: cint) {. - header: "", importc: "_longjmp".} - proc c_setjmp*(jmpb: C_JmpBuf): cint {. - header: "", importc: "_setjmp".} + when defined(windows): + # No `_longjmp()` on Windows. + proc c_longjmp*(jmpb: C_JmpBuf, retval: cint) {. + header: "", importc: "longjmp".} + # The Windows `_setjmp()` takes two arguments, with the second being an + # undocumented buffer used by the SEH mechanism for stack unwinding. + # Mingw-w64 has been trying to get it right for years, but it's still + # prone to stack corruption during unwinding, so we disable that by setting + # it to NULL. + # More details: https://github.com/status-im/nimbus-eth2/issues/3121 + proc c_setjmp*(jmpb: C_JmpBuf): cint = + proc c_setjmp_win(jmpb: C_JmpBuf, ctx: pointer): cint {. + header: "", importc: "_setjmp".} + c_setjmp_win(jmpb, nil) + else: + proc c_longjmp*(jmpb: C_JmpBuf, retval: cint) {. + header: "", importc: "_longjmp".} + proc c_setjmp*(jmpb: C_JmpBuf): cint {. + header: "", importc: "_setjmp".} else: proc c_longjmp*(jmpb: C_JmpBuf, retval: cint) {. header: "", importc: "longjmp".} diff --git a/tests/exception/texceptions.nim b/tests/exception/texceptions.nim index adee5d1d5..62d24c934 100644 --- a/tests/exception/texceptions.nim +++ b/tests/exception/texceptions.nim @@ -1,4 +1,6 @@ discard """ + disabled: "windows" # no sigsetjmp() there + matrix: "-d:nimStdSetjmp; -d:nimSigSetjmp; -d:nimRawSetjmp; -d:nimBuiltinSetjmp" output: ''' BEFORE @@ -17,7 +19,7 @@ FINALLY echo "" -proc no_expcetion = +proc no_exception = try: echo "BEFORE" @@ -28,7 +30,7 @@ proc no_expcetion = finally: echo "FINALLY" -try: no_expcetion() +try: no_exception() except: echo "RECOVER" echo "" diff --git a/tests/exception/texceptions2.nim b/tests/exception/texceptions2.nim new file mode 100644 index 000000000..97fd856a0 --- /dev/null +++ b/tests/exception/texceptions2.nim @@ -0,0 +1,130 @@ +discard """ + disabled: "posix" # already covered by texceptions.nim + matrix: "-d:nimStdSetjmp; -d:nimRawSetjmp; -d:nimBuiltinSetjmp" + output: ''' + +BEFORE +FINALLY + +BEFORE +EXCEPT +FINALLY +RECOVER + +BEFORE +EXCEPT: IOError: hi +FINALLY +''' +""" + +echo "" + +proc no_exception = + try: + echo "BEFORE" + + except: + echo "EXCEPT" + raise + + finally: + echo "FINALLY" + +try: no_exception() +except: echo "RECOVER" + +echo "" + +proc reraise_in_except = + try: + echo "BEFORE" + raise newException(IOError, "") + + except IOError: + echo "EXCEPT" + raise + + finally: + echo "FINALLY" + +try: reraise_in_except() +except: echo "RECOVER" + +echo "" + +proc return_in_except = + try: + echo "BEFORE" + raise newException(IOError, "hi") + + except: + echo "EXCEPT: ", getCurrentException().name, ": ", getCurrentExceptionMsg() + return + + finally: + echo "FINALLY" + +try: return_in_except() +except: echo "RECOVER" + +block: #10417 + proc moo() {.noreturn.} = discard + + let bar = + try: + 1 + except: + moo() + + doAssert(bar == 1) + +# Make sure the VM handles the exceptions correctly +block: + proc fun1(): seq[int] = + try: + try: + raise newException(ValueError, "xx") + except: + doAssert("xx" == getCurrentExceptionMsg()) + raise newException(KeyError, "yy") + except: + doAssert("yy" == getCurrentExceptionMsg()) + result.add(1212) + try: + try: + raise newException(AssertionDefect, "a") + finally: + result.add(42) + except AssertionDefect: + result.add(99) + finally: + result.add(10) + result.add(4) + result.add(0) + try: + result.add(1) + except KeyError: + result.add(-1) + except ValueError: + result.add(-1) + except IndexDefect: + result.add(2) + except: + result.add(3) + + try: + try: + result.add(1) + return + except: + result.add(-1) + finally: + result.add(2) + except KeyError: + doAssert(false) + finally: + result.add(3) + + let x1 = fun1() + const x2 = fun1() + doAssert(x1 == x2) From d39147219a41f54b45b585a103bfb04cce71c52c Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Thu, 9 Dec 2021 22:38:39 -0800 Subject: [PATCH 2697/3768] Revert "swap port to correct port order (#19177)" (#19234) This reverts commit 0d0c249074d6a1041de16108dc247396efef5513. --- lib/pure/net.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 06024e46d..7b0ff78e7 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -478,14 +478,14 @@ proc toSockAddr*(address: IpAddress, port: Port, sa: var Sockaddr_storage, sl = sizeof(Sockaddr_in).SockLen let s = cast[ptr Sockaddr_in](addr sa) s.sin_family = typeof(s.sin_family)(toInt(AF_INET)) - s.sin_port = htons(port) + s.sin_port = port copyMem(addr s.sin_addr, unsafeAddr address.address_v4[0], sizeof(s.sin_addr)) of IpAddressFamily.IPv6: sl = sizeof(Sockaddr_in6).SockLen let s = cast[ptr Sockaddr_in6](addr sa) s.sin6_family = typeof(s.sin6_family)(toInt(AF_INET6)) - s.sin6_port = htons(port) + s.sin6_port = port copyMem(addr s.sin6_addr, unsafeAddr address.address_v6[0], sizeof(s.sin6_addr)) From c98954233981cb30807bd8be8b805663d44964d1 Mon Sep 17 00:00:00 2001 From: MichalMarsalek Date: Fri, 10 Dec 2021 07:39:12 +0100 Subject: [PATCH 2698/3768] move toDeque to after addLast (#19233) [backport:1.0] Changes the order of procs definitions in order to avoid calling an undefined proc. --- lib/pure/collections/deques.nim | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/pure/collections/deques.nim b/lib/pure/collections/deques.nim index ecdf199d2..c4eb5331e 100644 --- a/lib/pure/collections/deques.nim +++ b/lib/pure/collections/deques.nim @@ -87,20 +87,6 @@ proc initDeque*[T](initialSize: int = defaultInitialSize): Deque[T] = ## * `toDeque proc <#toDeque,openArray[T]>`_ result.initImpl(initialSize) -proc toDeque*[T](x: openArray[T]): Deque[T] {.since: (1, 3).} = - ## Creates a new deque that contains the elements of `x` (in the same order). - ## - ## **See also:** - ## * `initDeque proc <#initDeque,int>`_ - runnableExamples: - let a = toDeque([7, 8, 9]) - assert len(a) == 3 - assert $a == "[7, 8, 9]" - - result.initImpl(x.len) - for item in items(x): - result.addLast(item) - proc len*[T](deq: Deque[T]): int {.inline.} = ## Returns the number of elements of `deq`. result = deq.count @@ -303,6 +289,20 @@ proc addLast*[T](deq: var Deque[T], item: sink T) = deq.data[deq.tail] = item deq.tail = (deq.tail + 1) and deq.mask +proc toDeque*[T](x: openArray[T]): Deque[T] {.since: (1, 3).} = + ## Creates a new deque that contains the elements of `x` (in the same order). + ## + ## **See also:** + ## * `initDeque proc <#initDeque,int>`_ + runnableExamples: + let a = toDeque([7, 8, 9]) + assert len(a) == 3 + assert $a == "[7, 8, 9]" + + result.initImpl(x.len) + for item in items(x): + result.addLast(item) + proc peekFirst*[T](deq: Deque[T]): lent T {.inline.} = ## Returns the first element of `deq`, but does not remove it from the deque. ## From 9338aa24977e84a33b9a7802eaff0777fcf4d9c3 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 10 Dec 2021 09:24:20 +0100 Subject: [PATCH 2699/3768] fixes a possible 'javascript:' protocol exploit [backport:1.0] (#19134) * fixes a possible 'javascript:' protocol exploit [backport:1.0] * add tests * Update tests/stdlib/trstgen.nim * add the same logic for hyperlinks * move the logic into a proc Co-authored-by: narimiran --- lib/packages/docutils/rstgen.nim | 15 +++++++++++++- tests/stdlib/trstgen.nim | 35 ++++++++++++++++++++++++++++---- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index dc6ca25c1..9018087f7 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -40,7 +40,7 @@ ## can be done by simply searching for [footnoteName]. import strutils, os, hashes, strtabs, rstast, rst, highlite, tables, sequtils, - algorithm, parseutils, std/strbasics + algorithm, parseutils, std/strbasics, strscans import ../../std/private/since @@ -827,6 +827,16 @@ proc renderOverline(d: PDoc, n: PRstNode, result: var string) = rstnodeToRefname(n).idS, tmp, $chr(n.level - 1 + ord('A')), tocName]) +proc safeProtocol(linkStr: var string) = + var protocol = "" + if scanf(linkStr, "$w:", protocol): + # if it has a protocol at all, ensure that it's not 'javascript:' or worse: + if cmpIgnoreCase(protocol, "http") == 0 or cmpIgnoreCase(protocol, "https") == 0 or + cmpIgnoreCase(protocol, "ftp") == 0: + discard "it's fine" + else: + linkStr = "" + proc renderTocEntry(d: PDoc, e: TocEntry, result: var string) = dispA(d.target, result, "
  • $2
  • \n", @@ -891,6 +901,8 @@ proc renderImage(d: PDoc, n: PRstNode, result: var string) = # support for `:target:` links for images: var target = esc(d.target, getFieldValue(n, "target").strip(), escMode=emUrl) + safeProtocol(target) + if target.len > 0: # `htmlOut` needs to be of the following format for link to work for images: # @@ -1192,6 +1204,7 @@ proc renderHyperlink(d: PDoc, text, link: PRstNode, result: var string, d.escMode = emUrl renderRstToOut(d, link, linkStr) d.escMode = mode + safeProtocol(linkStr) var textStr = "" renderRstToOut(d, text, textStr) let nimDocStr = if nimdoc: " nimdoc" else: "" diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim index 45cf1a68e..2be65b35b 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -398,7 +398,7 @@ Some chapter Level2 ------ - + Level3 ~~~~~~ @@ -407,7 +407,7 @@ Some chapter More ~~~~ - + Another ------- @@ -683,7 +683,7 @@ Test1 test "RST line blocks": let input2 = dedent""" Paragraph1 - + | Paragraph2""" @@ -704,7 +704,7 @@ Test1 # check that '| ' with a few spaces is still parsed as new line let input4 = dedent""" | xxx - | + | | zzz""" let output4 = input4.toHtml @@ -1549,3 +1549,30 @@ suite "RST/Code highlight": check strip(rstToHtml(pythonCode, {}, newStringTable(modeCaseSensitive))) == strip(expected) + + +suite "invalid targets": + test "invalid image target": + let input1 = dedent """.. image:: /images/myimage.jpg + :target: https://bar.com + :alt: Alt text for the image""" + let output1 = input1.toHtml + check output1 == """Alt text for the image""" + + let input2 = dedent """.. image:: /images/myimage.jpg + :target: javascript://bar.com + :alt: Alt text for the image""" + let output2 = input2.toHtml + check output2 == """Alt text for the image""" + + let input3 = dedent """.. image:: /images/myimage.jpg + :target: bar.com + :alt: Alt text for the image""" + let output3 = input3.toHtml + check output3 == """Alt text for the image""" + + test "invalid links": + check("(([Nim](https://nim-lang.org/)))".toHtml == + """((Nim))""") + check("(([Nim](javascript://nim-lang.org/)))".toHtml == + """((Nim))""") From 7ff43d07b2b983271d666b1c654b1426cbbbf236 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 10 Dec 2021 14:28:59 +0100 Subject: [PATCH 2700/3768] added --nimMainPrefix switch; fixes #15955; refs #16945 [backport:1.6] (#19235) --- changelog.md | 2 ++ compiler/cgen.nim | 37 ++++++++++++++++++++----------------- compiler/commands.nim | 1 + compiler/options.nim | 1 + doc/advopt.txt | 2 ++ doc/backends.rst | 3 +++ doc/nimc.rst | 7 +++++++ 7 files changed, 36 insertions(+), 17 deletions(-) diff --git a/changelog.md b/changelog.md index ebad54fe4..df8051a16 100644 --- a/changelog.md +++ b/changelog.md @@ -71,3 +71,5 @@ - The `gc` switch has been renamed to `mm` ("memory management") in order to reflect the reality better. (Nim moved away from all techniques based on "tracing".) +- There is a new switch `--nimMainPrefix:prefix` to influence the `NimMain` that the + compiler produces. This is particularly useful for generating static libraries. diff --git a/compiler/cgen.nim b/compiler/cgen.nim index c862d4d30..032c22ec0 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -154,6 +154,11 @@ macro ropecg(m: BModule, frmt: static[FormatStr], args: untyped): Rope = inc(i) result.add newCall(formatValue, resVar, args[num]) inc(num) + of '^': + flushStrLit() + inc(i) + result.add newCall(formatValue, resVar, args[^1]) + inc(num) of '0'..'9': var j = 0 while true: @@ -1366,7 +1371,7 @@ proc genMainProc(m: BModule) = "}$N$N" MainProcs = - "\tNimMain();$N" + "\t$^NimMain();$N" MainProcsWithResult = MainProcs & ("\treturn $1nim_program_result;$N") @@ -1376,7 +1381,7 @@ proc genMainProc(m: BModule) = "}$N$N" NimMainProc = - "N_CDECL(void, NimMain)(void) {$N" & + "N_CDECL(void, $5NimMain)(void) {$N" & "\tvoid (*volatile inner)(void);$N" & "$4" & "\tinner = NimMainInner;$N" & @@ -1456,28 +1461,27 @@ proc genMainProc(m: BModule) = if optGenGuiApp in m.config.globalOptions: const nimMain = WinNimMain appcg(m, m.s[cfsProcs], nimMain, - [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode]) + [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix]) else: const nimMain = WinNimDllMain appcg(m, m.s[cfsProcs], nimMain, - [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode]) + [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix]) elif m.config.target.targetOS == osGenode: const nimMain = GenodeNimMain appcg(m, m.s[cfsProcs], nimMain, - [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode]) + [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix]) elif optGenDynLib in m.config.globalOptions: const nimMain = PosixNimDllMain appcg(m, m.s[cfsProcs], nimMain, - [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode]) + [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix]) elif m.config.target.targetOS == osStandalone: const nimMain = NimMainBody appcg(m, m.s[cfsProcs], nimMain, - [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode]) + [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix]) else: const nimMain = NimMainBody appcg(m, m.s[cfsProcs], nimMain, - [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode]) - + [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix]) if optNoMain notin m.config.globalOptions: if m.config.cppCustomNamespace.len > 0: @@ -1487,23 +1491,22 @@ proc genMainProc(m: BModule) = m.config.globalOptions * {optGenGuiApp, optGenDynLib} != {}: if optGenGuiApp in m.config.globalOptions: const otherMain = WinCMain - appcg(m, m.s[cfsProcs], otherMain, [if m.hcrOn: "*" else: ""]) + appcg(m, m.s[cfsProcs], otherMain, [if m.hcrOn: "*" else: "", m.config.nimMainPrefix]) else: const otherMain = WinCDllMain - appcg(m, m.s[cfsProcs], otherMain, []) + appcg(m, m.s[cfsProcs], otherMain, [m.config.nimMainPrefix]) elif m.config.target.targetOS == osGenode: const otherMain = ComponentConstruct - appcg(m, m.s[cfsProcs], otherMain, []) + appcg(m, m.s[cfsProcs], otherMain, [m.config.nimMainPrefix]) elif optGenDynLib in m.config.globalOptions: const otherMain = PosixCDllMain - appcg(m, m.s[cfsProcs], otherMain, []) + appcg(m, m.s[cfsProcs], otherMain, [m.config.nimMainPrefix]) elif m.config.target.targetOS == osStandalone: const otherMain = StandaloneCMain - appcg(m, m.s[cfsProcs], otherMain, []) + appcg(m, m.s[cfsProcs], otherMain, [m.config.nimMainPrefix]) else: const otherMain = PosixCMain - appcg(m, m.s[cfsProcs], otherMain, [if m.hcrOn: "*" else: ""]) - + appcg(m, m.s[cfsProcs], otherMain, [if m.hcrOn: "*" else: "", m.config.nimMainPrefix]) if m.config.cppCustomNamespace.len > 0: m.s[cfsProcs].add openNamespaceNim(m.config.cppCustomNamespace) @@ -1885,7 +1888,7 @@ proc writeHeader(m: BModule) = if optGenDynLib in m.config.globalOptions: result.add("N_LIB_IMPORT ") - result.addf("N_CDECL(void, NimMain)(void);$n", []) + result.addf("N_CDECL(void, $1NimMain)(void);$n", [rope m.config.nimMainPrefix]) if m.config.cppCustomNamespace.len > 0: result.add closeNamespaceNim() result.addf("#endif /* $1 */$n", [guard]) if not writeRope(result, m.filename): diff --git a/compiler/commands.nim b/compiler/commands.nim index d5c5f24e4..c4df46bc2 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -1052,6 +1052,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "": # comes from "-" in for example: `nim c -r -` (gets stripped from -) handleStdinInput(conf) of "nilseqs", "nilchecks", "mainmodule", "m", "symbol", "taintmode", "cs", "deadcodeelim": warningOptionNoop(switch) + of "nimmainprefix": conf.nimMainPrefix = arg else: if strutils.find(switch, '.') >= 0: options.setConfigVar(conf, switch, arg) else: invalidCmdLineOption(conf, pass, switch, info) diff --git a/compiler/options.nim b/compiler/options.nim index 383b6fa2a..9e957332f 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -390,6 +390,7 @@ type structuredErrorHook*: proc (config: ConfigRef; info: TLineInfo; msg: string; severity: Severity) {.closure, gcsafe.} cppCustomNamespace*: string + nimMainPrefix*: string vmProfileData*: ProfileData proc parseNimVersion*(a: string): NimVer = diff --git a/doc/advopt.txt b/doc/advopt.txt index 79e784fde..e27c75ada 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -135,6 +135,8 @@ Advanced options: --cppCompileToNamespace:namespace use the provided namespace for the generated C++ code, if no namespace is provided "Nim" will be used + --nimMainPrefix:prefix use `{prefix}NimMain` instead of `NimMain` in the produced + C/C++ code --expandMacro:MACRO dump every generated AST from MACRO --expandArc:PROCNAME show how PROCNAME looks like after diverse optimizations before the final backend phase (mostly ARC/ORC specific) diff --git a/doc/backends.rst b/doc/backends.rst index 3a3359fca..65dd8a6f2 100644 --- a/doc/backends.rst +++ b/doc/backends.rst @@ -246,6 +246,9 @@ Also, C code requires you to specify a forward declaration for functions or the compiler will assume certain types for the return value and parameters which will likely make your program crash at runtime. +The name `NimMain` can be influenced via the `--nimMainPrefix:prefix` switch. +Use `--nimMainPrefix:MyLib` and the function to call is named `MyLibNimMain`. + Nim invocation example from C ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/doc/nimc.rst b/doc/nimc.rst index 0ab1501ff..6b4f3c9a1 100644 --- a/doc/nimc.rst +++ b/doc/nimc.rst @@ -385,6 +385,10 @@ of your program. NimMain() # initialize garbage collector memory, types and stack +The name `NimMain` can be influenced via the `--nimMainPrefix:prefix` switch. +Use `--nimMainPrefix:MyLib` and the function to call is named `MyLibNimMain`. + + Cross-compilation for iOS ========================= @@ -413,6 +417,9 @@ of your program. Note: XCode's "make clean" gets confused about the generated nim.c files, so you need to clean those files manually to do a clean build. +The name `NimMain` can be influenced via the `--nimMainPrefix:prefix` switch. +Use `--nimMainPrefix:MyLib` and the function to call is named `MyLibNimMain`. + Cross-compilation for Nintendo Switch ===================================== From 908fc2a22e7336670173bcd06ab30f440ca6d321 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 10 Dec 2021 17:13:48 +0100 Subject: [PATCH 2701/3768] let Nim support Nimble 0.14 with lock-file support [backport:1.6] (#19236) --- changelog.md | 4 ++++ config/nim.cfg | 2 ++ 2 files changed, 6 insertions(+) diff --git a/changelog.md b/changelog.md index df8051a16..6f47ef8a4 100644 --- a/changelog.md +++ b/changelog.md @@ -71,5 +71,9 @@ - The `gc` switch has been renamed to `mm` ("memory management") in order to reflect the reality better. (Nim moved away from all techniques based on "tracing".) +- Nim now supports Nimble version 0.14 which added support for lock-files. This is done by + a simple configuration change setting that you can do yourself too. In `$nim/config/nim.cfg` + replace `pkgs` by `pkgs2`. + - There is a new switch `--nimMainPrefix:prefix` to influence the `NimMain` that the compiler produces. This is particularly useful for generating static libraries. diff --git a/config/nim.cfg b/config/nim.cfg index 86c6e2141..59d7f48d2 100644 --- a/config/nim.cfg +++ b/config/nim.cfg @@ -44,10 +44,12 @@ path="$lib/core" path="$lib/pure" @if not windows: + nimblepath="/opt/nimble/pkgs2/" nimblepath="/opt/nimble/pkgs/" @else: # TODO: @end +nimblepath="$home/.nimble/pkgs2/" nimblepath="$home/.nimble/pkgs/" # Syncronize with compiler/commands.specialDefine From 1a92edeb894a10a13f60905a79304bd26e1c144e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C8=98tefan=20Talpalaru?= Date: Sat, 11 Dec 2021 05:15:44 +0100 Subject: [PATCH 2702/3768] nimc.rst: fix table markup (#19239) --- doc/nimc.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/nimc.rst b/doc/nimc.rst index 6b4f3c9a1..aa6650491 100644 --- a/doc/nimc.rst +++ b/doc/nimc.rst @@ -179,6 +179,8 @@ nimRawSetjmp Use `_setjmp()/_longjmp()` on POSIX and `_setjm nimBuiltinSetjmp Use `__builtin_setjmp()/__builtin_longjmp()` for setjmp-based exceptions. This will not work if an exception is being thrown and caught inside the same procedure. Useful for benchmarking. +========================== ============================================ + Configuration files ------------------- From a3ef5df680e55d9bf68027fcb0ec6358b4279d09 Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sat, 11 Dec 2021 08:24:23 +0000 Subject: [PATCH 2703/3768] Update uri.nim (#19148) [backport:1.0] --- lib/pure/uri.nim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim index a828298c2..5de333b8f 100644 --- a/lib/pure/uri.nim +++ b/lib/pure/uri.nim @@ -216,6 +216,8 @@ func parseAuthority(authority: string, result: var Uri) = result.isIpv6 = true of ']': inIPv6 = false + of '\0': + break else: if inPort: result.port.add(authority[i]) From 4b5cecd902cc4126ff9d6cda9edb78a13a421239 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Sun, 12 Dec 2021 13:39:56 -0800 Subject: [PATCH 2704/3768] Various std net improvements (#19132) * Variant of that works with raw IpAddresses. - Add doc tests for new net proc's. - Aadd recvFrom impl - Add recvFrom impl -- tweak handling data var - Update lib/pure/net.nim Co-authored-by: Dominik Picheta - cleaning up sendTo args - remove extra connect test - cleaning up sendTo args - fix inet_ntop test - fix test failing - byte len * fix test failing - byte len * debugging odd windows build failure * debugging odd windows build failure * more experiments to figure out the windows failure * try manual assigment on InAddr Co-authored-by: Jaremy Creechley --- lib/posix/posix.nim | 4 +- lib/pure/net.nim | 61 +++++++++++++++++++++++----- tests/stdlib/tnet_ll.nim | 88 ++++++++++++++++++++-------------------- 3 files changed, 97 insertions(+), 56 deletions(-) diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index c2504f994..57dd6e2c7 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -155,9 +155,9 @@ when not defined(zephyr): proc inet_addr*(a1: cstring): InAddrT {.importc, header: "".} proc inet_ntoa*(a1: InAddr): cstring {.importc, header: "".} -proc inet_ntop*(a1: cint, a2: pointer, a3: cstring, a4: int32): cstring {. +proc inet_ntop*(a1: cint, a2: pointer | ptr InAddr | ptr In6Addr, a3: cstring, a4: int32): cstring {. importc:"(char *)$1", header: "".} -proc inet_pton*(a1: cint, a2: cstring, a3: pointer): cint {. +proc inet_pton*(a1: cint, a2: cstring, a3: pointer | ptr InAddr | ptr In6Addr): cint {. importc, header: "".} var diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 7b0ff78e7..2d1bb0b33 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -65,6 +65,11 @@ runnableExamples("-r:off"): let socket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) socket.sendTo("192.168.0.1", Port(27960), "status\n") +runnableExamples("-r:off"): + let socket = newSocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP) + let ip = parseIpAddress("192.168.0.1") + doAssert socket.sendTo(ip, Port(27960), "status\c\l") == 8 + ## Creating a server ## ----------------- ## @@ -1607,11 +1612,12 @@ proc recvLine*(socket: Socket, timeout = -1, result = "" readLine(socket, result, timeout, flags, maxLength) -proc recvFrom*(socket: Socket, data: var string, length: int, - address: var string, port: var Port, flags = 0'i32): int {. +proc recvFrom*[T: string | IpAddress](socket: Socket, data: var string, length: int, + address: var T, port: var Port, flags = 0'i32): int {. tags: [ReadIOEffect].} = ## Receives data from `socket`. This function should normally be used with - ## connection-less sockets (UDP sockets). + ## connection-less sockets (UDP sockets). The source address of the data + ## packet is stored in the `address` argument as either a string or an IpAddress. ## ## If an error occurs an OSError exception will be raised. Otherwise the return ## value will be the length of data received. @@ -1620,31 +1626,37 @@ proc recvFrom*(socket: Socket, data: var string, length: int, ## so when `socket` is buffered the non-buffered implementation will be ## used. Therefore if `socket` contains something in its buffer this ## function will make no effort to return it. - template adaptRecvFromToDomain(domain: Domain) = + template adaptRecvFromToDomain(sockAddress: untyped, domain: Domain) = var addrLen = sizeof(sockAddress).SockLen result = recvfrom(socket.fd, cstring(data), length.cint, flags.cint, cast[ptr SockAddr](addr(sockAddress)), addr(addrLen)) if result != -1: data.setLen(result) - address = getAddrString(cast[ptr SockAddr](addr(sockAddress))) - when domain == AF_INET6: - port = ntohs(sockAddress.sin6_port).Port + + when typeof(address) is string: + address = getAddrString(cast[ptr SockAddr](addr(sockAddress))) + when domain == AF_INET6: + port = ntohs(sockAddress.sin6_port).Port + else: + port = ntohs(sockAddress.sin_port).Port else: - port = ntohs(sockAddress.sin_port).Port + data.setLen(result) + sockAddress.fromSockAddr(addrLen, address, port) else: raiseOSError(osLastError()) assert(socket.protocol != IPPROTO_TCP, "Cannot `recvFrom` on a TCP socket") # TODO: Buffered sockets data.setLen(length) + case socket.domain of AF_INET6: var sockAddress: Sockaddr_in6 - adaptRecvFromToDomain(AF_INET6) + adaptRecvFromToDomain(sockAddress, AF_INET6) of AF_INET: var sockAddress: Sockaddr_in - adaptRecvFromToDomain(AF_INET) + adaptRecvFromToDomain(sockAddress, AF_INET) else: raise newException(ValueError, "Unknown socket address family") @@ -1707,7 +1719,8 @@ proc sendTo*(socket: Socket, address: string, port: Port, data: pointer, tags: [WriteIOEffect].} = ## This proc sends `data` to the specified `address`, ## which may be an IP address or a hostname, if a hostname is specified - ## this function will try each IP of that hostname. + ## this function will try each IP of that hostname. This function + ## should normally be used with connection-less sockets (UDP sockets). ## ## If an error occurs an OSError exception will be raised. ## @@ -1741,12 +1754,38 @@ proc sendTo*(socket: Socket, address: string, port: Port, ## This proc sends `data` to the specified `address`, ## which may be an IP address or a hostname, if a hostname is specified ## this function will try each IP of that hostname. + ## + ## Generally for use with connection-less (UDP) sockets. ## ## If an error occurs an OSError exception will be raised. ## ## This is the high-level version of the above `sendTo` function. socket.sendTo(address, port, cstring(data), data.len, socket.domain) +proc sendTo*(socket: Socket, address: IpAddress, port: Port, + data: string, flags = 0'i32): int {. + discardable, tags: [WriteIOEffect].} = + ## This proc sends `data` to the specified `IpAddress` and returns + ## the number of bytes written. + ## + ## Generally for use with connection-less (UDP) sockets. + ## + ## If an error occurs an OSError exception will be raised. + ## + ## This is the high-level version of the above `sendTo` function. + assert(socket.protocol != IPPROTO_TCP, "Cannot `sendTo` on a TCP socket") + assert(not socket.isClosed, "Cannot `sendTo` on a closed socket") + + var sa: Sockaddr_storage + var sl: Socklen + toSockAddr(address, port, sa, sl) + result = sendto(socket.fd, cstring(data), data.len().cint, flags.cint, + cast[ptr SockAddr](addr sa), sl) + + if result == -1'i32: + let osError = osLastError() + raiseOSError(osError) + proc isSsl*(socket: Socket): bool = ## Determines whether `socket` is a SSL socket. diff --git a/tests/stdlib/tnet_ll.nim b/tests/stdlib/tnet_ll.nim index affa21947..248dbb79b 100644 --- a/tests/stdlib/tnet_ll.nim +++ b/tests/stdlib/tnet_ll.nim @@ -1,43 +1,45 @@ -discard """ - action: run - output: ''' - -[Suite] inet_ntop tests -''' -""" - -when defined(windows): - import winlean -elif defined(posix): - import posix -else: - {.error: "Unsupported OS".} - -import unittest, strutils - -suite "inet_ntop tests": - - setup: - when defined(windows): - var wsa: WSAData - discard wsaStartup(0x101'i16, wsa.addr) - - 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 res = if r == nil: "" else: $r - check: res == "19.18.17.16" - - - test "IP V6": - when defined(windows): - let ipv6Support = (getVersion() and 0xff) > 0x5 - else: - let ipv6Support = true - - 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 res = if r == nil: "" else: $r - check: not ipv6Support or res == "10:110:20:120:30:130:40:140" +discard """ + action: run + output: ''' + +[Suite] inet_ntop tests +''' +""" + +when defined(windows): + import winlean +elif defined(posix): + import posix +else: + {.error: "Unsupported OS".} + +import unittest, strutils + +suite "inet_ntop tests": + + setup: + when defined(windows): + var wsa: WSAData + discard wsaStartup(0x101'i16, wsa.addr) + + test "IP V4": + # regular + var ip4 = InAddr() + ip4.s_addr = 0x10111213'u32 + + var buff: array[0..255, char] + let r = inet_ntop(AF_INET, cast[pointer](ip4.s_addr.addr), buff[0].addr, buff.len.int32) + let res = if r == nil: "" else: $r + check: res == "19.18.17.16" + + test "IP V6": + when defined(windows): + let ipv6Support = (getVersion() and 0xff) > 0x5 + else: + let ipv6Support = true + + var ip6 = [0x1000'u16, 0x1001, 0x2000, 0x2001, 0x3000, 0x3001, 0x4000, 0x4001] + var buff: array[0..255, char] + let r = inet_ntop(AF_INET6, cast[pointer](ip6[0].addr), buff[0].addr, buff.len.int32) + let res = if r == nil: "" else: $r + check: not ipv6Support or res == "10:110:20:120:30:130:40:140" From 0ff4b2ba7ee74ed2758b0eb5992e4ccab7433952 Mon Sep 17 00:00:00 2001 From: Carlo Capocasa Date: Mon, 13 Dec 2021 07:29:22 +0100 Subject: [PATCH 2705/3768] fix bug #14468 zero-width split (#19248) --- lib/impure/re.nim | 11 +++++++---- tests/stdlib/tre.nim | 6 ++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/lib/impure/re.nim b/lib/impure/re.nim index 8e5efbb28..4eacf0091 100644 --- a/lib/impure/re.nim +++ b/lib/impure/re.nim @@ -556,19 +556,22 @@ iterator split*(s: string, sep: Regex; maxsplit = -1): string = @["", "this", "is", "an", "example", ""] var last = 0 var splits = maxsplit - var x: int + var x = -1 + if len(s) == 0: + last = 1 + if matchLen(s, sep, 0) == 0: + x = 0 while last <= len(s): var first = last var sepLen = 1 + if x == 0: + inc(last) while last < len(s): x = matchLen(s, sep, last) if x >= 0: sepLen = x break inc(last) - if x == 0: - if last >= len(s): break - inc last if splits == 0: last = len(s) yield substr(s, first, last-1) if splits == 0: break diff --git a/tests/stdlib/tre.nim b/tests/stdlib/tre.nim index 2857c6c9e..9f27f7db2 100644 --- a/tests/stdlib/tre.nim +++ b/tests/stdlib/tre.nim @@ -108,4 +108,10 @@ proc testAll() = doAssert replace("foo", re"", "-") == "-f-o-o-" doAssert replace("ooo", re"o", "-") == "---" + block: # bug #14468 + accum = @[] + for word in split("this is an example", re"\b"): + accum.add(word) + doAssert(accum == @["this", " ", "is", " ", "an", " ", "example"]) + testAll() From 78b86b79425f37683ce522dbdd567f52ea26aa35 Mon Sep 17 00:00:00 2001 From: Nan Xiao Date: Tue, 14 Dec 2021 16:54:01 +0800 Subject: [PATCH 2706/3768] basicopt.txt: Unify the format (#19251) --- doc/basicopt.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/basicopt.txt b/doc/basicopt.txt index a2db1c92e..390fc6fd2 100644 --- a/doc/basicopt.txt +++ b/doc/basicopt.txt @@ -27,11 +27,11 @@ Options: -a, --assertions:on|off turn assertions on|off --opt:none|speed|size optimize not at all or for speed|size Note: use -d:release for a release build! - --debugger:native Use native debugger (gdb) + --debugger:native use native debugger (gdb) --app:console|gui|lib|staticlib generate a console app|GUI app|DLL|static library -r, --run run the compiled program with given arguments - --eval:cmd evaluates nim code directly; e.g.: `nim --eval:"echo 1"` + --eval:cmd evaluate nim code directly; e.g.: `nim --eval:"echo 1"` defaults to `e` (nimscript) but customizable: `nim r --eval:'for a in stdin.lines: echo a'` --fullhelp show all command line switches From c55930f2e644fe04526eb4878e7e106229812fe4 Mon Sep 17 00:00:00 2001 From: Nick Wilburn Date: Tue, 14 Dec 2021 06:22:10 -0600 Subject: [PATCH 2707/3768] fix: fixes bug in CVerifyPeerUseEnvVars (#19247) Previously CVerifyPeerUseEnvVars was not being passed into scanSslCertificates, which meant that we weren't scanning additional certificate locations given via the SSL_CERT_FILE and SSL_CERT_DIR environment variables --- lib/pure/net.nim | 3 ++- tests/stdlib/thttpclient_ssl.nim | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 2d1bb0b33..ced6b2fb2 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -680,7 +680,8 @@ when defineSsl: # Scan for certs in known locations. For CVerifyPeerUseEnvVars also scan # the SSL_CERT_FILE and SSL_CERT_DIR env vars var found = false - for fn in scanSSLCertificates(): + let useEnvVars = (if verifyMode == CVerifyPeerUseEnvVars: true else: false) + for fn in scanSSLCertificates(useEnvVars = useEnvVars): if newCTX.SSL_CTX_load_verify_locations(fn, nil) == VerifySuccess: found = true break diff --git a/tests/stdlib/thttpclient_ssl.nim b/tests/stdlib/thttpclient_ssl.nim index 1c531eae9..3acdacfe3 100644 --- a/tests/stdlib/thttpclient_ssl.nim +++ b/tests/stdlib/thttpclient_ssl.nim @@ -129,3 +129,19 @@ when not defined(windows): msg.contains("certificate verify failed")): echo "CVerifyPeer exception: " & msg check(false) + + test "HttpClient with CVerifyPeerUseEnvVars": + const port = 12346.Port + let t = spawn runServer(port) + sleep(100) + + putEnv("SSL_CERT_FILE", getCurrentDir() / certFile) + var client = newHttpClient(sslContext=newContext(verifyMode=CVerifyPeerUseEnvVars)) + try: + log "client: connect" + discard client.getContent("https://127.0.0.1:12346") + except: + let msg = getCurrentExceptionMsg() + log "client: exception: " & msg + log "getContent should not have raised an exception" + fail() From 3e31f55aed4cd9fdcc289687fd0e31edfc0af805 Mon Sep 17 00:00:00 2001 From: PMunch Date: Tue, 14 Dec 2021 14:28:42 +0100 Subject: [PATCH 2708/3768] Fix #19253 (#19254) This fixes 19253 by marking the bodyStream as completed when no content was sent. --- lib/pure/httpclient.nim | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 33da6ef6c..67ba8cf2b 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -869,6 +869,9 @@ proc parseResponse(client: HttpClient | AsyncHttpClient, client.parseBodyFut.addCallback do(): if client.parseBodyFut.failed: client.bodyStream.fail(client.parseBodyFut.error) + else: + when client is AsyncHttpClient: + result.bodyStream.complete() proc newConnection(client: HttpClient | AsyncHttpClient, url: Uri) {.multisync.} = From c17baaefbcff5c207a4e95242fa0790e64ca6c8c Mon Sep 17 00:00:00 2001 From: Miran Date: Tue, 14 Dec 2021 18:16:49 +0100 Subject: [PATCH 2709/3768] [backport:1.0] json: limit recursion depth (#19252) * json: limit recursion depth * do not run this check for JS backend --- lib/pure/json.nim | 12 +++++++++--- tests/stdlib/tjson.nim | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 3 deletions(-) diff --git a/lib/pure/json.nim b/lib/pure/json.nim index c831bf85d..922cd4e2f 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -202,6 +202,8 @@ type of JArray: elems*: seq[JsonNode] +const DepthLimit = 1000 + proc newJString*(s: string): JsonNode = ## Creates a new `JString JsonNode`. result = JsonNode(kind: JString, str: s) @@ -849,7 +851,7 @@ iterator mpairs*(node: var JsonNode): tuple[key: string, val: var JsonNode] = for key, val in mpairs(node.fields): yield (key, val) -proc parseJson(p: var JsonParser; rawIntegers, rawFloats: bool): JsonNode = +proc parseJson(p: var JsonParser; rawIntegers, rawFloats: bool, depth = 0): JsonNode = ## Parses JSON from a JSON Parser `p`. case p.tok of tkString: @@ -885,6 +887,8 @@ proc parseJson(p: var JsonParser; rawIntegers, rawFloats: bool): JsonNode = result = newJNull() discard getTok(p) of tkCurlyLe: + if depth > DepthLimit: + raiseParseErr(p, "}") result = newJObject() discard getTok(p) while p.tok != tkCurlyRi: @@ -893,16 +897,18 @@ proc parseJson(p: var JsonParser; rawIntegers, rawFloats: bool): JsonNode = var key = p.a discard getTok(p) eat(p, tkColon) - var val = parseJson(p, rawIntegers, rawFloats) + var val = parseJson(p, rawIntegers, rawFloats, depth+1) result[key] = val if p.tok != tkComma: break discard getTok(p) eat(p, tkCurlyRi) of tkBracketLe: + if depth > DepthLimit: + raiseParseErr(p, "]") result = newJArray() discard getTok(p) while p.tok != tkBracketRi: - result.add(parseJson(p, rawIntegers, rawFloats)) + result.add(parseJson(p, rawIntegers, rawFloats, depth+1)) if p.tok != tkComma: break discard getTok(p) eat(p, tkBracketRi) diff --git a/tests/stdlib/tjson.nim b/tests/stdlib/tjson.nim index 289ef9d05..336558ff3 100644 --- a/tests/stdlib/tjson.nim +++ b/tests/stdlib/tjson.nim @@ -345,3 +345,35 @@ block: doAssert c == "18446744073709552000" else: doAssert c == "18446744073709551615" + +block: + let a = """ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ + [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ +""" + + when not defined(js): + try: + discard parseJson(a) + except JsonParsingError: + doAssert getCurrentExceptionMsg().contains("] expected") From 5d2bab7558b54813b212a26fbcdb51556ff37b05 Mon Sep 17 00:00:00 2001 From: Sven Keller Date: Thu, 16 Dec 2021 08:58:32 +0100 Subject: [PATCH 2710/3768] suggestion to respect typedarray type (#19257) * suggestion to respect typedarray * Update jssys.nim Co-authored-by: Sven Keller --- lib/system/jssys.nim | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim index 250bd069d..6608a2927 100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -587,7 +587,33 @@ proc nimCopy(dest, src: JSRef, ti: PNimType): JSRef = else: asm "`result` = (`dest` === null || `dest` === undefined) ? {} : `dest`;" nimCopyAux(result, src, ti.node) - of tySequence, tyArrayConstr, tyOpenArray, tyArray: + of tyArrayConstr, tyArray: + # In order to prevent a type change (TypedArray -> Array) and to have better copying performance, + # arrays constructors are considered separately + asm """ + if(ArrayBuffer.isView(`src`)) { + if(`dest` === null || `dest` === undefined || `dest`.length != `src`.length) { + `dest` = new `src`.constructor(`src`); + } else { + `dest`.set(`src`, 0); + } + `result` = `dest`; + } else { + if (`src` === null) { + `result` = null; + } + else { + if (`dest` === null || `dest` === undefined || `dest`.length != `src`.length) { + `dest` = new Array(`src`.length); + } + `result` = `dest`; + for (var i = 0; i < `src`.length; ++i) { + `result`[i] = nimCopy(`result`[i], `src`[i], `ti`.base); + } + } + } + """ + of tySequence, tyOpenArray: asm """ if (`src` === null) { `result` = null; From 609388e33fb9a19d65b5dee8711d411916981802 Mon Sep 17 00:00:00 2001 From: snxx Date: Fri, 17 Dec 2021 13:58:05 +0600 Subject: [PATCH 2711/3768] Add icon for Nim (#19258) * Add Nim's file icon * Add installer icon * Add uninstaller icon --- tools/niminst/nim-file.ico | Bin 0 -> 3311 bytes tools/niminst/setup.ico | Bin 0 -> 2551 bytes tools/niminst/uninstall.ico | Bin 0 -> 2226 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 tools/niminst/nim-file.ico create mode 100644 tools/niminst/setup.ico create mode 100644 tools/niminst/uninstall.ico diff --git a/tools/niminst/nim-file.ico b/tools/niminst/nim-file.ico new file mode 100644 index 0000000000000000000000000000000000000000..0685fedcb0b7851d874bbe9606912b243e712e68 GIT binary patch literal 3311 zcmcImc{r47AAV=dVJZ$K8ctcWv{wMn{(~UoFwoaF+h5(Ea8~eT?Oosm0PHyi+L~7b#)y-_?!!MI9h;na7f`h} zXFk}JSzA3vZA#{%w7YC|(+9um$fjy{M#buOB%}P^+uxT{Ug&jKK!`AQP*HcO$LWs*MNm@IPit%3;KnR(&oYeK@_I zSh=1>ik^&tLGXN2Er&&)q$j97f^z{}5D_GRg#9^fTj56fJ=Xm$V4MOJJ9{EJ>l952 z3tBRglp{ME8tL1hKslps`^bHLZTz1k2+%9Hp3wP`Cp~{e)7m9?C|N9^OCBDc6J5vR zK~BV!x;7|ibOHheL~Lik=$3ZSIX+Sp3E-_n6B2^j&k8D|_pz+%sB7xRaUlSCte5;j z6b8t8fy)0D!_|d4L{*21>MqF}(D>%hm$L#?%^gICwrKDocpZ#ReTSeO4d z2(g9#g@!CF9Nhml>R?p}OwEY+3=gnB9^@FAL{HCTDH-guS>Iyjm@}Sx)+(8$g<98X zsN`)Mr%a@A`%xW^LWBTVPK|1r;iLMVw~0?HPZsuPNW*MN<=nEcz8*546B#wA5;%k> z+QJ~RRXYv$8Xa4@$V*gR9$xF@zA^iEi@JbkhxM(HC$|ffhLiTLoxiS*ySo^bdGUB8 z0FkAh#O$RNX?Ao_C@bc5O?6X&Yi+LV;#CjVM=AUZj`XXRp2npLv+3uak&WjW2$ZRhOjr|2RixqFRg_i$slK! zTJ$cZR&xg#t&QY;VPP`qr_lWTuI^Cw@|hzU4a$hE(zT!M-9lUd){UgI6n^A47O!aV zv}5J*0K4L7O@yXbq;da(0(;lDK=VZ@GD8H4UL-~i5l%YWkJ~VwPbj~ilK@@~H1WzN zJlbhc7V)Jzq%^5I4-6!m)&+@qBdhZ-V6fAMl%5|xdw9VT65;XXb8b4Y7NaPX`_r`f z)y@dwOwTc2Fztfr6r77?`rBl`35>l*pd2feYU%XO8|RZ)P^0zt&);pyWc{yL~qxG~pw& z!lmze=F8WV)SRyDdbRS|jiLOHqzEBk!HjO4Yp9gxuWj3*?xqJDli0H*eiJlYAJdX+Arakc=^FnZu-H z!Bbmm{;kgrsU+a-OqUPbq;ag z4%{0@t)?`u1m-qA06$z*jCYbw2lGO$$=<7A8ODd65UBu>kk8^F7Fz=30`jVEUQDGV z*1k$^Fff!lGe9?gy|cguMFMc7^M^hCA*?($_UEwb)lAZI*WxF{k7e++TAtL!uyL92(JM*mdVd<5i3=-OyiP$htOvEZHTx*xEkz9(Uz_$E?7#*bdqVpU_6w0riY?K$P8bD>cD z`?Thj?Kcw=m%Uo^*(%Qbz5@gNUCk4%`k9*z{6T%fdzQZi^)ap_TAAEoq|0*w6VETQ zt^Vk|VlLc>$(eaz60c^?L*io-0uVxJuXpb)8z z`YVQmHv-JCqGCO{BYrq;W3yuX{j;A$3pNoouX70yqh~6HVBMbCR`$B&tuwtfsEV8P z%i<2a@1-aNK`;?&N@vjW0+0FLD25L?hkRc4;5Wb1`(>5$7hwRriwF8#Ny+PGAKOh0 z`J7F?2XBIP9+|7E9@cqtcH`?ET0n+5-7i<#yk_#F*)y=YYsrC3O9Hm`u>WTx@XuZY zh8O`IcDL58x& zNTR4C`wRl7#+CN2(EO_Zn{uXOOjrS>UGa;2KcUA zbwo=3gM8mkXn@_}a0t{^*KKMl=bi}*3y8(DXyj(Plig?Bb#k)yXXxotG0E` z@e{Lc8=7tGzkV6DPUk~&rYnNoC;UGe_%DdNS!bQS&N+MU@B7X=>l^?8aoT}_09jy$ z9{`SX{6uR@xZq*Q!yH!-VQPHsVD+H$9pVhmA*Egbz+Z|m{>?6Y?AxS^|C6m>+Gx`k z_0L^%wWw{2@4XsdYJSV+h@$!*q5PLrJra+JXdltOo#4-ZvpNRiMiM}Z2e!XG=db85 zprWoIkF+aS6_@z4+_23BHr>&jViv%~&Mfx-U?3XIdApF>y2kux{u6 zSvars>y=p}Qb5EzQzKZWYO88(U9U7Cs&*jm{|ih4a`EybSKIM*nc9`xr*4YpQv3uP zfJeuRCL`eA8ZIDeK&nPZL4S$(f0X5mUPuBm)O;~q@oFREs4royN9=ulaXM~Ml5Q`e zt7S}93p5?qNE;V+!Q-W!K}gwGZ+WTYKJfyDoe`7fXPoVif9VW65=NN6kkKlv!!2)N31nU4}y_GNYOB_%GCZ&*2UsyEwBp+wCmJ^VKN-NH_vh2Ppb2-y_Mj_%SY<#i z$fkB7v*QJMNIvAo)|Q}+EgI=K{!kS!>PCzRlTMw8M@ONT#aqL7dPQvI-Pe4kFFXKH z60wn&qOF-%i-1bc|^kzKuITh7&e=u#AiWro#r0*K&aGN5yIcy69>CAFla|R%G9wZFP za05)uxW~}l=F!}Fj+$8kQZ;-WVH8^d>lDcO9iAAu)HeLbX?*i=n*iXOWC&+?WypY( z?Ug$956=A{O5!B`Bb!htFB8iF{GUJ{m2u$`v$gZ}V8betHr{)(z+Fkss-SqNWYxzs z;A@q@&e_<^>M%tZm{aC}D{12Xp)}W=qd5x@i5__J#S->xJsP3aqNa*9AXo#3HNaV< z=>U3K?1?A%Y#aPED2UG*fGnXvY2+;+p; zh*4GX)^C`tuUK}G&TfP?KyT-(NVM&V^n!KvfNWtKj{8e9b4oq^$VYD_Cqbjbj$A+Q z+M=hEiJx8&^KrK0l+7dN9UVU)ndMYyMx}{V5}8@`Ay=2BYevo)PHr}nN3bdibG@nB+nz|wb)16u*-POZ7(FTadnF4) z>z0a$W9rKN76hBHDc-887OgOB>+P|WvMmU0+YpASay``K8GTuA|3f1LLR%hJFQ5(3 zvwy$gXa3SBnNnlf9#6|%m{_yBsq%2hA`yo}g=J9f{Cp$#G8;2G$LVQertoM;lOqdZFtxuG6saLOIfUh2&s2Ce>o@^QfIPZ^7(p zcWjM!Zc0QT`%8{M;!U=e(iU}AI3}MX?)BgeQxoIwdQ=!~HI+uSNLE>_-Qy^V8jzXq zVn#P87;G=H*Oj(AHnrPdFhXH_@KLTjTzQ0sI3XH?(u-V=<<#!GI1OkxbL!DHGU__H zFfxgq#k9wbU;Tnd(#@M*7n~VFaYV=uexsG>jdhjA-digE&1#=#vpRSo{hd|DE8ThH zwe-r4vFmR~Z{)6hR0TwzxC~9IvFPUh(G42Md%BP6(6nJLk_6(XgH7SIx+1Owfi$)qzOSAs|(PiH0fs|pft*#7L5$qg2-7&~z z9b;wHswd*-Fy}r;S0MSXB-Jq(&%R((mMDR{bNb@}l+1l^EGTbrwS0}=(72u4L2+!* zfeI28Y|zSyOHc3yoMY~bM{a$>xeU3e`5Gy<+(m0CM&D|#oDU+OISmF**`P~xB@GaL z2DU`}pqaSPx5yWXa}Z{9$=4iqmTLb#WEfC3>i2&eF7jqR=$Ph%1|ERC{Hl49R^yiX zV?6Grll(IZ8SjAl6%Z|Q8Ip&_GGXTK9chw31L*=ko1K-~ znCY=szQ`<@$xzs@F3|nm9y8am!U8`Ebm){dkbViXRTSEW?rrl-khJCUkz7GndY`Y= zBx4*LJZG27%zH{#8yz;7mk}CL{lu&tZXjXNCm9i06pqR6 zD<1+sVj3jlv~45Dg#ne!sJ$tQ45aP^{}qDffB!;a;;2zT$`kD)?{GUo^-!<$LY=;d wL%Sh-oNRoYf7Z{m|A-R+ow1$5#O@i^ICKpvQHMNZ-`BJO;KT7(uw57M@WEOFY8<42S`WP!n3 z*&ln-C)|k!FC}Y#KifKdj$t>U#GEDGf9I~09&~lbuYtW$Sl39)~C^FGZ5o^>%}TKsq`5l1Z+@3QpwM zzcKhjCg_VdDjf44z%%7Vp6Sbb!!G^~)b~)J9k@x)Uj14-y2iLI2HY-L_#+ihD_#e` zDU=3TRy1(X1V2}t>eC)v;*wdpu(B5tw;(w(23LyXjw<|TIBiYaciKN823k1$&|jQ0 z+|A)phY1a>hAaqSnQCL^R+u$zB=nbe^*bdt-=!L&jM4s_aHLggzvMH>uUVYt>^_;! zpEXh2{blpW;)26Ps9$=8RdaacgD7rT2_<*Qjdx^AQsApzRnf zE?BR&I@8%T>nqQEsW2FDU@=&wsKC=%L?=`*lrmb<;PKf*Uoq38Qio+eoCL*vHzRa5yP*?X8Ei zZDUn~KFlj-XVi4@OQq#_Uy;X(4_IF`gWW`sJbB9hd-FThzWm5;2fOAZxn&t+!YbW` zw;$+3Z``oM&D|;w6}DDb#|S7~%W2f0t`SfJ^|+Jj=hTF+*aLjvz9kp(X_+Lm%&EznGb?ZqQX zgG_}0)G6N6*xPUbUwkLGSTl9qsGAm@+07QWLlPkGE<{A;chhV_Qn#|_0n7hx3DJky zbX1AIQ?~4e{aetJHcrzjUXO~};5K=65$B(KpZVL=&ly7f;b=e1=K!pdY5kYd>iw*3 z?I25M1)AVObU;R;d{qwT=cYxC%-wAHE)tcOsm54b_?eTJ0gq2HhRFpN?iO*4HRVf{ zP6VxYQ*=Nfm7Plo(}Q+38zO4>H4hWQoo3AIt|K&eZs^{-*x9RU<9pm^OL9AkgfmMx)dyX}&^I0)G$(X5h5Iz-|U>pHG2_ccrHayazRp}&W@I+s}5FOzk{PC9X z+{DTGkv!7+om&N_2b8cvPt56<%dOA3rwud?i2)sI_%(eKw%r|U<)Heul!K2 z<}6EpDjs_{&qFAE1zdQS6OH6R=;4QwALS&7fxwloR}vOpVjVX9JC3))oMzX`9(P%S z9#QG3On-eZb?TOtx_5Jvu>JpqkkCSs%JnVTHfg=x) z`I$;Li9hYO c(!hC-V4HpC^wmC3csCFYjFlt0+R`unUtQjos{jB1 literal 0 HcmV?d00001 From 5ddaf3e1fbff4c0519088612f9ec3b10d62d2316 Mon Sep 17 00:00:00 2001 From: rockcavera Date: Fri, 17 Dec 2021 05:01:33 -0300 Subject: [PATCH 2712/3768] fix #19244 - solves the problem of the InAddr object constructor in Windows. (#19259) * Update winlean.nim * Update tnet_ll.nim --- lib/windows/winlean.nim | 2 +- tests/stdlib/tnet_ll.nim | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index b8f3886d7..6e626e4ba 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -478,7 +478,7 @@ type PSockAddr = ptr SockAddr - InAddr* {.importc: "IN_ADDR", header: "winsock2.h".} = object + InAddr* {.importc: "IN_ADDR", header: "winsock2.h", union.} = object s_addr*: uint32 # IP address Sockaddr_in* {.importc: "SOCKADDR_IN", diff --git a/tests/stdlib/tnet_ll.nim b/tests/stdlib/tnet_ll.nim index 248dbb79b..2d340cea8 100644 --- a/tests/stdlib/tnet_ll.nim +++ b/tests/stdlib/tnet_ll.nim @@ -26,6 +26,7 @@ suite "inet_ntop tests": # regular var ip4 = InAddr() ip4.s_addr = 0x10111213'u32 + check: ip4.s_addr == 0x10111213'u32 var buff: array[0..255, char] let r = inet_ntop(AF_INET, cast[pointer](ip4.s_addr.addr), buff[0].addr, buff.len.int32) @@ -43,3 +44,8 @@ suite "inet_ntop tests": let r = inet_ntop(AF_INET6, cast[pointer](ip6[0].addr), buff[0].addr, buff.len.int32) let res = if r == nil: "" else: $r check: not ipv6Support or res == "10:110:20:120:30:130:40:140" + + test "InAddr": + # issue 19244 + var ip4 = InAddr(s_addr: 0x10111213'u32) + check: ip4.s_addr == 0x10111213'u32 From 3d9b6cfebce898dede2f63d0288050a106a7f5e4 Mon Sep 17 00:00:00 2001 From: flywind Date: Fri, 17 Dec 2021 18:16:59 +0800 Subject: [PATCH 2713/3768] [formatter] async.nim uses two spaces (#19264) according to https://nim-lang.org/docs/nep1.html#introduction-spacing-and-whitespace-conventions, two spaces should be preferred in stdlib. --- lib/pure/async.nim | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/pure/async.nim b/lib/pure/async.nim index 97b29f81d..482ab32c6 100644 --- a/lib/pure/async.nim +++ b/lib/pure/async.nim @@ -1,6 +1,6 @@ when defined(js): - import asyncjs - export asyncjs + import asyncjs + export asyncjs else: - import asyncmacro, asyncfutures - export asyncmacro, asyncfutures + import asyncmacro, asyncfutures + export asyncmacro, asyncfutures From 610516e02756dd1447fc807077bacf86eb75acdd Mon Sep 17 00:00:00 2001 From: Don-Duong Quach Date: Fri, 17 Dec 2021 11:58:31 -0800 Subject: [PATCH 2714/3768] Fixed typo in manual.rst unsafeAssign->uncheckedAssign. Fixes part 1 of #19266 (#19267) --- doc/manual.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual.rst b/doc/manual.rst index 6705e673e..d9f5d4ea6 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -1838,7 +1838,7 @@ A small example: cast uncheckedAssign -------------------- -Some restrictions for case objects can be disabled via a `{.cast(unsafeAssign).}` section: +Some restrictions for case objects can be disabled via a `{.cast(uncheckedAssign).}` section: .. code-block:: nim :test: "nim c $1" From b812431f831748c9013a12074cc6a7a838b580cd Mon Sep 17 00:00:00 2001 From: xioren <40043405+xioren@users.noreply.github.com> Date: Sun, 19 Dec 2021 05:01:17 -0800 Subject: [PATCH 2715/3768] use uppercase "type" for Proxy-Authorization header (#19273) Some servers will reject authorization requests with a lowercase "basic" type. Changing to "Basic" seems to solve these issues. https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Proxy-Authorization --- lib/pure/httpclient.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 67ba8cf2b..7686d15bd 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -523,7 +523,7 @@ proc generateHeaders(requestUrl: Uri, httpMethod: HttpMethod, headers: HttpHeade # Proxy auth header. if not proxy.isNil and proxy.auth != "": let auth = base64.encode(proxy.auth) - add(result, "Proxy-Authorization: basic " & auth & httpNewLine) + add(result, "Proxy-Authorization: Basic " & auth & httpNewLine) for key, val in headers: add(result, key & ": " & val & httpNewLine) From ea475a4e98bc1cfc861f870baf6f2cc4c979ac7a Mon Sep 17 00:00:00 2001 From: MichalMarsalek Date: Mon, 20 Dec 2021 10:40:32 +0100 Subject: [PATCH 2716/3768] Update colors.nim (#19274) * Update colors.nim Added `lightgray` alias to `lightgrey` and `...grey`aliases for the rest of the gray colors. Added color `rebeccapurple`. Fixed the incorrect values for the `PaleVioletRed` and `MediumPurple` colors. This module should now be matching the CSS colors. I used the seq[tuple] syntax for defining the names. * Document colors changes. --- changelog.md | 2 + lib/pure/colors.nim | 307 +++++++++++++++++++++++--------------------- 2 files changed, 164 insertions(+), 145 deletions(-) diff --git a/changelog.md b/changelog.md index 6f47ef8a4..52159b865 100644 --- a/changelog.md +++ b/changelog.md @@ -15,11 +15,13 @@ `experimental:flexibleOptionalParams`. - `std/sharedstrings` module is removed. +- Constants `colors.colPaleVioletRed` and `colors.colMediumPurple` changed to match the CSS color standard. ## Standard library additions and changes - `macros.parseExpr` and `macros.parseStmt` now accept an optional filename argument for more informative errors. +- Module `colors` expanded with missing colors from the CSS color standard. ## `std/smtp` diff --git a/lib/pure/colors.nim b/lib/pure/colors.nim index 2193f69a6..8cda2fab8 100644 --- a/lib/pure/colors.nim +++ b/lib/pure/colors.nim @@ -6,7 +6,8 @@ # distribution, for details about the copyright. # -## This module implements color handling for Nim. +## This module implements color handling for Nim, +## namely color mixing and parsing the CSS color names. import strutils from algorithm import binarySearch @@ -170,6 +171,7 @@ const colDarkGoldenRod* = Color(0xB8860B) colDarkGray* = Color(0xA9A9A9) colDarkGreen* = Color(0x006400) + colDarkGrey* = Color(0xA9A9A9) colDarkKhaki* = Color(0xBDB76B) colDarkMagenta* = Color(0x8B008B) colDarkOliveGreen* = Color(0x556B2F) @@ -180,11 +182,13 @@ const colDarkSeaGreen* = Color(0x8FBC8F) colDarkSlateBlue* = Color(0x483D8B) colDarkSlateGray* = Color(0x2F4F4F) + colDarkSlateGrey* = Color(0x2F4F4F) colDarkTurquoise* = Color(0x00CED1) colDarkViolet* = Color(0x9400D3) colDeepPink* = Color(0xFF1493) colDeepSkyBlue* = Color(0x00BFFF) colDimGray* = Color(0x696969) + colDimGrey* = Color(0x696969) colDodgerBlue* = Color(0x1E90FF) colFireBrick* = Color(0xB22222) colFloralWhite* = Color(0xFFFAF0) @@ -196,6 +200,7 @@ const colGoldenRod* = Color(0xDAA520) colGray* = Color(0x808080) colGreen* = Color(0x008000) + colGrey* = Color(0x808080) colGreenYellow* = Color(0xADFF2F) colHoneyDew* = Color(0xF0FFF0) colHotPink* = Color(0xFF69B4) @@ -211,13 +216,15 @@ const colLightCoral* = Color(0xF08080) colLightCyan* = Color(0xE0FFFF) colLightGoldenRodYellow* = Color(0xFAFAD2) - colLightGrey* = Color(0xD3D3D3) + colLightGray* = Color(0xD3D3D3) colLightGreen* = Color(0x90EE90) + colLightGrey* = Color(0xD3D3D3) colLightPink* = Color(0xFFB6C1) colLightSalmon* = Color(0xFFA07A) colLightSeaGreen* = Color(0x20B2AA) colLightSkyBlue* = Color(0x87CEFA) colLightSlateGray* = Color(0x778899) + colLightSlateGrey* = Color(0x778899) colLightSteelBlue* = Color(0xB0C4DE) colLightYellow* = Color(0xFFFFE0) colLime* = Color(0x00FF00) @@ -228,7 +235,7 @@ const colMediumAquaMarine* = Color(0x66CDAA) colMediumBlue* = Color(0x0000CD) colMediumOrchid* = Color(0xBA55D3) - colMediumPurple* = Color(0x9370D8) + colMediumPurple* = Color(0x9370DB) colMediumSeaGreen* = Color(0x3CB371) colMediumSlateBlue* = Color(0x7B68EE) colMediumSpringGreen* = Color(0x00FA9A) @@ -249,7 +256,7 @@ const colPaleGoldenRod* = Color(0xEEE8AA) colPaleGreen* = Color(0x98FB98) colPaleTurquoise* = Color(0xAFEEEE) - colPaleVioletRed* = Color(0xD87093) + colPaleVioletRed* = Color(0xDB7093) colPapayaWhip* = Color(0xFFEFD5) colPeachPuff* = Color(0xFFDAB9) colPeru* = Color(0xCD853F) @@ -257,6 +264,7 @@ const colPlum* = Color(0xDDA0DD) colPowderBlue* = Color(0xB0E0E6) colPurple* = Color(0x800080) + colRebeccaPurple* = Color(0x663399) colRed* = Color(0xFF0000) colRosyBrown* = Color(0xBC8F8F) colRoyalBlue* = Color(0x4169E1) @@ -270,6 +278,7 @@ const colSkyBlue* = Color(0x87CEEB) colSlateBlue* = Color(0x6A5ACD) colSlateGray* = Color(0x708090) + colSlateGrey* = Color(0x708090) colSnow* = Color(0xFFFAFA) colSpringGreen* = Color(0x00FF7F) colSteelBlue* = Color(0x4682B4) @@ -285,147 +294,155 @@ const colYellow* = Color(0xFFFF00) colYellowGreen* = Color(0x9ACD32) - colorNames = [ - ("aliceblue", colAliceBlue), - ("antiquewhite", colAntiqueWhite), - ("aqua", colAqua), - ("aquamarine", colAquamarine), - ("azure", colAzure), - ("beige", colBeige), - ("bisque", colBisque), - ("black", colBlack), - ("blanchedalmond", colBlanchedAlmond), - ("blue", colBlue), - ("blueviolet", colBlueViolet), - ("brown", colBrown), - ("burlywood", colBurlyWood), - ("cadetblue", colCadetBlue), - ("chartreuse", colChartreuse), - ("chocolate", colChocolate), - ("coral", colCoral), - ("cornflowerblue", colCornflowerBlue), - ("cornsilk", colCornsilk), - ("crimson", colCrimson), - ("cyan", colCyan), - ("darkblue", colDarkBlue), - ("darkcyan", colDarkCyan), - ("darkgoldenrod", colDarkGoldenRod), - ("darkgray", colDarkGray), - ("darkgreen", colDarkGreen), - ("darkkhaki", colDarkKhaki), - ("darkmagenta", colDarkMagenta), - ("darkolivegreen", colDarkOliveGreen), - ("darkorange", colDarkorange), - ("darkorchid", colDarkOrchid), - ("darkred", colDarkRed), - ("darksalmon", colDarkSalmon), - ("darkseagreen", colDarkSeaGreen), - ("darkslateblue", colDarkSlateBlue), - ("darkslategray", colDarkSlateGray), - ("darkturquoise", colDarkTurquoise), - ("darkviolet", colDarkViolet), - ("deeppink", colDeepPink), - ("deepskyblue", colDeepSkyBlue), - ("dimgray", colDimGray), - ("dodgerblue", colDodgerBlue), - ("firebrick", colFireBrick), - ("floralwhite", colFloralWhite), - ("forestgreen", colForestGreen), - ("fuchsia", colFuchsia), - ("gainsboro", colGainsboro), - ("ghostwhite", colGhostWhite), - ("gold", colGold), - ("goldenrod", colGoldenRod), - ("gray", colGray), - ("green", colGreen), - ("greenyellow", colGreenYellow), - ("honeydew", colHoneyDew), - ("hotpink", colHotPink), - ("indianred", colIndianRed), - ("indigo", colIndigo), - ("ivory", colIvory), - ("khaki", colKhaki), - ("lavender", colLavender), - ("lavenderblush", colLavenderBlush), - ("lawngreen", colLawnGreen), - ("lemonchiffon", colLemonChiffon), - ("lightblue", colLightBlue), - ("lightcoral", colLightCoral), - ("lightcyan", colLightCyan), - ("lightgoldenrodyellow", colLightGoldenRodYellow), - ("lightgreen", colLightGreen), - ("lightgrey", colLightGrey), - ("lightpink", colLightPink), - ("lightsalmon", colLightSalmon), - ("lightseagreen", colLightSeaGreen), - ("lightskyblue", colLightSkyBlue), - ("lightslategray", colLightSlateGray), - ("lightsteelblue", colLightSteelBlue), - ("lightyellow", colLightYellow), - ("lime", colLime), - ("limegreen", colLimeGreen), - ("linen", colLinen), - ("magenta", colMagenta), - ("maroon", colMaroon), - ("mediumaquamarine", colMediumAquaMarine), - ("mediumblue", colMediumBlue), - ("mediumorchid", colMediumOrchid), - ("mediumpurple", colMediumPurple), - ("mediumseagreen", colMediumSeaGreen), - ("mediumslateblue", colMediumSlateBlue), - ("mediumspringgreen", colMediumSpringGreen), - ("mediumturquoise", colMediumTurquoise), - ("mediumvioletred", colMediumVioletRed), - ("midnightblue", colMidnightBlue), - ("mintcream", colMintCream), - ("mistyrose", colMistyRose), - ("moccasin", colMoccasin), - ("navajowhite", colNavajoWhite), - ("navy", colNavy), - ("oldlace", colOldLace), - ("olive", colOlive), - ("olivedrab", colOliveDrab), - ("orange", colOrange), - ("orangered", colOrangeRed), - ("orchid", colOrchid), - ("palegoldenrod", colPaleGoldenRod), - ("palegreen", colPaleGreen), - ("paleturquoise", colPaleTurquoise), - ("palevioletred", colPaleVioletRed), - ("papayawhip", colPapayaWhip), - ("peachpuff", colPeachPuff), - ("peru", colPeru), - ("pink", colPink), - ("plum", colPlum), - ("powderblue", colPowderBlue), - ("purple", colPurple), - ("red", colRed), - ("rosybrown", colRosyBrown), - ("royalblue", colRoyalBlue), - ("saddlebrown", colSaddleBrown), - ("salmon", colSalmon), - ("sandybrown", colSandyBrown), - ("seagreen", colSeaGreen), - ("seashell", colSeaShell), - ("sienna", colSienna), - ("silver", colSilver), - ("skyblue", colSkyBlue), - ("slateblue", colSlateBlue), - ("slategray", colSlateGray), - ("snow", colSnow), - ("springgreen", colSpringGreen), - ("steelblue", colSteelBlue), - ("tan", colTan), - ("teal", colTeal), - ("thistle", colThistle), - ("tomato", colTomato), - ("turquoise", colTurquoise), - ("violet", colViolet), - ("wheat", colWheat), - ("white", colWhite), - ("whitesmoke", colWhiteSmoke), - ("yellow", colYellow), - ("yellowgreen", colYellowGreen)] + colorNames = { + "aliceblue": colAliceBlue, + "antiquewhite": colAntiqueWhite, + "aqua": colAqua, + "aquamarine": colAquamarine, + "azure": colAzure, + "beige": colBeige, + "bisque": colBisque, + "black": colBlack, + "blanchedalmond": colBlanchedAlmond, + "blue": colBlue, + "blueviolet": colBlueViolet, + "brown": colBrown, + "burlywood": colBurlyWood, + "cadetblue": colCadetBlue, + "chartreuse": colChartreuse, + "chocolate": colChocolate, + "coral": colCoral, + "cornflowerblue": colCornflowerBlue, + "cornsilk": colCornsilk, + "crimson": colCrimson, + "cyan": colCyan, + "darkblue": colDarkBlue, + "darkcyan": colDarkCyan, + "darkgoldenrod": colDarkGoldenRod, + "darkgray": colDarkGray, + "darkgreen": colDarkGreen, + "darkgrey": colDarkGrey, + "darkkhaki": colDarkKhaki, + "darkmagenta": colDarkMagenta, + "darkolivegreen": colDarkOliveGreen, + "darkorange": colDarkorange, + "darkorchid": colDarkOrchid, + "darkred": colDarkRed, + "darksalmon": colDarkSalmon, + "darkseagreen": colDarkSeaGreen, + "darkslateblue": colDarkSlateBlue, + "darkslategray": colDarkSlateGray, + "darkslategrey": colDarkSlateGrey, + "darkturquoise": colDarkTurquoise, + "darkviolet": colDarkViolet, + "deeppink": colDeepPink, + "deepskyblue": colDeepSkyBlue, + "dimgray": colDimGray, + "dimgrey": colDimGrey, + "dodgerblue": colDodgerBlue, + "firebrick": colFireBrick, + "floralwhite": colFloralWhite, + "forestgreen": colForestGreen, + "fuchsia": colFuchsia, + "gainsboro": colGainsboro, + "ghostwhite": colGhostWhite, + "gold": colGold, + "goldenrod": colGoldenRod, + "gray": colGray, + "green": colGreen, + "grey": colGrey, + "greenyellow": colGreenYellow, + "honeydew": colHoneyDew, + "hotpink": colHotPink, + "indianred": colIndianRed, + "indigo": colIndigo, + "ivory": colIvory, + "khaki": colKhaki, + "lavender": colLavender, + "lavenderblush": colLavenderBlush, + "lawngreen": colLawnGreen, + "lemonchiffon": colLemonChiffon, + "lightblue": colLightBlue, + "lightcoral": colLightCoral, + "lightcyan": colLightCyan, + "lightgoldenrodyellow": colLightGoldenRodYellow, + "lightgray": colLightGray, + "lightgreen": colLightGreen, + "lightgrey": colLightGrey, + "lightpink": colLightPink, + "lightsalmon": colLightSalmon, + "lightseagreen": colLightSeaGreen, + "lightskyblue": colLightSkyBlue, + "lightslategray": colLightSlateGray, + "lightslategrey": colLightSlateGrey, + "lightsteelblue": colLightSteelBlue, + "lightyellow": colLightYellow, + "lime": colLime, + "limegreen": colLimeGreen, + "linen": colLinen, + "magenta": colMagenta, + "maroon": colMaroon, + "mediumaquamarine": colMediumAquaMarine, + "mediumblue": colMediumBlue, + "mediumorchid": colMediumOrchid, + "mediumpurple": colMediumPurple, + "mediumseagreen": colMediumSeaGreen, + "mediumslateblue": colMediumSlateBlue, + "mediumspringgreen": colMediumSpringGreen, + "mediumturquoise": colMediumTurquoise, + "mediumvioletred": colMediumVioletRed, + "midnightblue": colMidnightBlue, + "mintcream": colMintCream, + "mistyrose": colMistyRose, + "moccasin": colMoccasin, + "navajowhite": colNavajoWhite, + "navy": colNavy, + "oldlace": colOldLace, + "olive": colOlive, + "olivedrab": colOliveDrab, + "orange": colOrange, + "orangered": colOrangeRed, + "orchid": colOrchid, + "palegoldenrod": colPaleGoldenRod, + "palegreen": colPaleGreen, + "paleturquoise": colPaleTurquoise, + "palevioletred": colPaleVioletRed, + "papayawhip": colPapayaWhip, + "peachpuff": colPeachPuff, + "peru": colPeru, + "pink": colPink, + "plum": colPlum, + "powderblue": colPowderBlue, + "purple": colPurple, + "rebeccapurple": colRebeccaPurple, + "red": colRed, + "rosybrown": colRosyBrown, + "royalblue": colRoyalBlue, + "saddlebrown": colSaddleBrown, + "salmon": colSalmon, + "sandybrown": colSandyBrown, + "seagreen": colSeaGreen, + "seashell": colSeaShell, + "sienna": colSienna, + "silver": colSilver, + "skyblue": colSkyBlue, + "slateblue": colSlateBlue, + "slategray": colSlateGray, + "slategrey": colSlateGrey, + "snow": colSnow, + "springgreen": colSpringGreen, + "steelblue": colSteelBlue, + "tan": colTan, + "teal": colTeal, + "thistle": colThistle, + "tomato": colTomato, + "turquoise": colTurquoise, + "violet": colViolet, + "wheat": colWheat, + "white": colWhite, + "whitesmoke": colWhiteSmoke, + "yellow": colYellow, + "yellowgreen": colYellowGreen} proc `$`*(c: Color): string = ## Converts a color into its textual representation. From 4da7dbffc504db9159285ca59e2cdfe9b103afb8 Mon Sep 17 00:00:00 2001 From: Jake Leahy Date: Tue, 21 Dec 2021 03:29:03 +1100 Subject: [PATCH 2717/3768] Extract runnables that specify `doccmd` (#19275) [backport:1.6] --- lib/core/macros.nim | 4 ++-- tests/stdlib/tmacros.nim | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 444783b5a..ed7e85c64 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -1715,8 +1715,8 @@ proc extractDocCommentsAndRunnables*(n: NimNode): NimNode = case ni.kind of nnkCommentStmt: result.add ni - of nnkCall: - if ni[0].kind == nnkIdent and ni[0].strVal == "runnableExamples": + of nnkCall, nnkCommand: + if ni[0].kind == nnkIdent and ni[0].eqIdent "runnableExamples": result.add ni else: break else: break diff --git a/tests/stdlib/tmacros.nim b/tests/stdlib/tmacros.nim index 64a474743..299eac49b 100644 --- a/tests/stdlib/tmacros.nim +++ b/tests/stdlib/tmacros.nim @@ -124,3 +124,23 @@ block: # SameType assert not testTensorInt(x1) assert testTensorInt(x2) assert not testTensorInt(x3) + +block: # extractDocCommentsAndRunnables + macro checkRunnables(prc: untyped) = + let runnables = prc.body.extractDocCommentsAndRunnables() + doAssert runnables[0][0].eqIdent("runnableExamples") + + macro checkComments(comment: static[string], prc: untyped) = + let comments = prc.body.extractDocCommentsAndRunnables() + doAssert comments[0].strVal == comment + + proc a() {.checkRunnables.} = + runnableExamples: discard + discard + + proc b() {.checkRunnables.} = + runnableExamples "-d:ssl": discard + discard + + proc c() {.checkComments("Hello world").} = + ## Hello world From 19898e1225862560b216b765187fc0b98dca7608 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Mon, 20 Dec 2021 23:10:15 +0300 Subject: [PATCH 2718/3768] Fix group reference (with capital letters (#19196) in group name) --- compiler/docgen.nim | 3 +- lib/packages/docutils/rst.nim | 2 +- .../expected/subdir/subdir_b/utils.html | 28 +++++++++++++++++++ .../expected/subdir/subdir_b/utils.idx | 2 ++ nimdoc/testproject/expected/theindex.html | 6 ++++ nimdoc/testproject/subdir/subdir_b/utils.nim | 6 ++++ 6 files changed, 45 insertions(+), 2 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 7ba952083..90e0f54b3 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -1301,7 +1301,8 @@ proc finishGenerateDoc*(d: var PDoc) = let tooltip = "$1 ($2 overloads)" % [ k.toHumanStr & " " & plainName, $overloadChoices.len] addAnchorNim(d.sharedState, refn, tooltip, - LangSymbol(symKind: k.toHumanStr, name: plainName, + LangSymbol(symKind: k.toHumanStr, + name: nimIdentBackticksNormalize(plainName), isGroup: true), priority = symbolPriority(k), # select index `0` just to have any meaningful warning: diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 4c28894fb..a04eb696f 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -993,7 +993,7 @@ proc findMainAnchorNim(s: PRstSharedState, signature: PRstNode, result.add s else: # when there are many overloads a link like foo_ points to all # of them, so selecting the group - var foundGroup = true + var foundGroup = false for s in sList: if s.langSym.isGroup: result.add s diff --git a/nimdoc/testproject/expected/subdir/subdir_b/utils.html b/nimdoc/testproject/expected/subdir/subdir_b/utils.html index c64039941..574ac5b9b 100644 --- a/nimdoc/testproject/expected/subdir/subdir_b/utils.html +++ b/nimdoc/testproject/expected/subdir/subdir_b/utils.html @@ -207,6 +207,13 @@ window.addEventListener('DOMContentLoaded', main);
  • fn10(a: int): int
  • + +
      funWithGenerics
    • Note that proc can be used in postfix form: binarySearch proc.

      Ref. type like G and type G and G[T] and type G*[T].

      +

      Group ref. with capital letters works: fN11 or fn11

      Ref. [] is the same as proc `[]`(G[T]) because there are no overloads. The full form: proc `[]`*[T](x: G[T]): TRef. []= aka `[]=`(G[T], int, T).Ref. $ aka proc $ or proc `$`.Ref. $(a: ref SomeType).Ref. foo_bar aka iterator foo_bar_.Ref. fn[T; U,V: SomeFloat]().Ref. 'big or func `'big` or `'big`(string).

      Types

      @@ -539,6 +547,26 @@ comment comment + +
      + + + +
      +
      +
      func fN11() {....raises: [], tags: [].}
      +
      + + + +
      +
      +
      +
      func fN11(x: int) {....raises: [], tags: [].}
      +
      + + +
      diff --git a/nimdoc/testproject/expected/subdir/subdir_b/utils.idx b/nimdoc/testproject/expected/subdir/subdir_b/utils.idx index 441bd8675..5c8fee527 100644 --- a/nimdoc/testproject/expected/subdir/subdir_b/utils.idx +++ b/nimdoc/testproject/expected/subdir/subdir_b/utils.idx @@ -17,6 +17,8 @@ fn7 subdir/subdir_b/utils.html#fn7 utils: fn7() fn8 subdir/subdir_b/utils.html#fn8 utils: fn8(): auto fn9 subdir/subdir_b/utils.html#fn9,int utils: fn9(a: int): int fn10 subdir/subdir_b/utils.html#fn10,int utils: fn10(a: int): int +fN11 subdir/subdir_b/utils.html#fN11 utils: fN11() +fN11 subdir/subdir_b/utils.html#fN11,int utils: fN11(x: int) aEnum subdir/subdir_b/utils.html#aEnum.t utils: aEnum(): untyped bEnum subdir/subdir_b/utils.html#bEnum.t utils: bEnum(): untyped fromUtilsGen subdir/subdir_b/utils.html#fromUtilsGen.t utils: fromUtilsGen(): untyped diff --git a/nimdoc/testproject/expected/theindex.html b/nimdoc/testproject/expected/theindex.html index da76334d4..88d4b86aa 100644 --- a/nimdoc/testproject/expected/theindex.html +++ b/nimdoc/testproject/expected/theindex.html @@ -209,6 +209,12 @@ window.addEventListener('DOMContentLoaded', main);
    • utils: fn10(a: int): int
    +
    fN11:
    fn2:
    • utils: fn2()
    • diff --git a/nimdoc/testproject/subdir/subdir_b/utils.nim b/nimdoc/testproject/subdir/subdir_b/utils.nim index 5d881e620..e201a3d38 100644 --- a/nimdoc/testproject/subdir/subdir_b/utils.nim +++ b/nimdoc/testproject/subdir/subdir_b/utils.nim @@ -36,6 +36,7 @@ Note that `proc` can be used in postfix form: `binarySearch proc`_. Ref. type like G_ and `type G`_ and `G[T]`_ and `type G*[T]`_. +Group ref. with capital letters works: fN11_ or fn11_ ]## include ./utils_helpers @@ -77,6 +78,11 @@ proc fn8*(): auto = func fn9*(a: int): int = 42 ## comment func fn10*(a: int): int = a ## comment +# Note capital letter N will be handled correctly in +# group references like fN11_ or fn11_: +func fN11*() = discard +func fN11*(x: int) = discard + # bug #9235 template aEnum*(): untyped = From 7a5314c571ec2c370f63d5fab0afd336e04ec161 Mon Sep 17 00:00:00 2001 From: pkubaj Date: Thu, 23 Dec 2021 02:52:33 +0100 Subject: [PATCH 2719/3768] Fix build on FreeBSD/powerpc (#19282) It's currently misdetected as powerpc64. --- tools/niminst/buildsh.nimf | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/niminst/buildsh.nimf b/tools/niminst/buildsh.nimf index 464c70abf..5f1db9cd3 100644 --- a/tools/niminst/buildsh.nimf +++ b/tools/niminst/buildsh.nimf @@ -192,8 +192,10 @@ case $ucpu in mycpu="powerpc64" ;; *power*|*ppc* ) if [ "$myos" = "freebsd" ] ; then - COMP_FLAGS="$COMP_FLAGS -m64" - LINK_FLAGS="$LINK_FLAGS -m64" + if [ "$ucpu" != "powerpc" ] ; then + COMP_FLAGS="$COMP_FLAGS -m64" + LINK_FLAGS="$LINK_FLAGS -m64" + fi mycpu=`uname -p` case $mycpu in powerpc64le) From 81d32cf7e53b244776c4196098be154c1223d726 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 23 Dec 2021 07:03:40 +0100 Subject: [PATCH 2720/3768] Revert "Update uri.nim (#19148) [backport:1.0]" (#19280) This reverts commit a3ef5df680e55d9bf68027fcb0ec6358b4279d09. --- lib/pure/uri.nim | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim index 5de333b8f..a828298c2 100644 --- a/lib/pure/uri.nim +++ b/lib/pure/uri.nim @@ -216,8 +216,6 @@ func parseAuthority(authority: string, result: var Uri) = result.isIpv6 = true of ']': inIPv6 = false - of '\0': - break else: if inPort: result.port.add(authority[i]) From fa96e56ad06f0562b5d608cc896a9b17c9512958 Mon Sep 17 00:00:00 2001 From: Jason Beetham Date: Wed, 22 Dec 2021 23:12:56 -0700 Subject: [PATCH 2721/3768] Fixed object field access of static objects in generics (#19283) [backport] --- compiler/semexprs.nim | 2 +- tests/system/tstatic.nim | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 0df332689..c593efe55 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1414,7 +1414,7 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = if ty.kind in tyUserTypeClasses and ty.isResolvedUserTypeClass: ty = ty.lastSon - ty = skipTypes(ty, {tyGenericInst, tyVar, tyLent, tyPtr, tyRef, tyOwned, tyAlias, tySink}) + ty = skipTypes(ty, {tyGenericInst, tyVar, tyLent, tyPtr, tyRef, tyOwned, tyAlias, tySink, tyStatic}) while tfBorrowDot in ty.flags: ty = ty.skipTypes({tyDistinct, tyGenericInst, tyAlias}) var check: PNode = nil if ty.kind == tyObject: diff --git a/tests/system/tstatic.nim b/tests/system/tstatic.nim index 1f1b9dbe3..6e2893e2b 100644 --- a/tests/system/tstatic.nim +++ b/tests/system/tstatic.nim @@ -44,6 +44,14 @@ template main() = proc parseInt(f: static[bool]): int {.used.} = discard doAssert "123".parseInt == 123 + block: + type + MyType = object + field: float32 + AType[T: static MyType] = distinct range[0f32 .. T.field] + var a: AType[MyType(field: 5f32)] + proc n(S: static Slice[int]): range[S.a..S.b] = discard + assert typeof(n 1..2) is range[1..2] static: main() From fdbec969d8601af0f666426b523e8ebba644ab56 Mon Sep 17 00:00:00 2001 From: Tomohiro Date: Sat, 25 Dec 2021 18:31:35 +0900 Subject: [PATCH 2722/3768] Fix #19107 (#19286) [backport] --- lib/pure/strformat.nim | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/lib/pure/strformat.nim b/lib/pure/strformat.nim index 746f01ace..40a33951c 100644 --- a/lib/pure/strformat.nim +++ b/lib/pure/strformat.nim @@ -574,6 +574,9 @@ template formatValue(result: var string; value: cstring; specifier: string) = result.add value proc strformatImpl(f: string; openChar, closeChar: char): NimNode = + template missingCloseChar = + error("invalid format string: missing closing character '" & closeChar & "'") + if openChar == ':' or closeChar == ':': error "openChar and closeChar must not be ':'" var i = 0 @@ -618,6 +621,8 @@ proc strformatImpl(f: string; openChar, closeChar: char): NimNode = let start = i inc i i += f.skipWhitespace(i) + if i == f.len: + missingCloseChar if f[i] == closeChar or f[i] == ':': result.add newCall(bindSym"add", res, newLit(subexpr & f[start ..< i])) else: @@ -627,6 +632,9 @@ proc strformatImpl(f: string; openChar, closeChar: char): NimNode = subexpr.add f[i] inc i + if i == f.len: + missingCloseChar + var x: NimNode try: x = parseExpr(subexpr) @@ -639,10 +647,10 @@ proc strformatImpl(f: string; openChar, closeChar: char): NimNode = while i < f.len and f[i] != closeChar: options.add f[i] inc i + if i == f.len: + missingCloseChar if f[i] == closeChar: inc i - else: - doAssert false, "invalid format string: missing '}'" result.add newCall(formatSym, res, x, newLit(options)) elif f[i] == closeChar: if i Date: Mon, 27 Dec 2021 17:28:19 +0100 Subject: [PATCH 2723/3768] fixes grammar typos [backport] (#19289) --- compiler/parser.nim | 6 +++--- doc/grammar.txt | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/parser.nim b/compiler/parser.nim index 9ff847ef6..7bc2c78e0 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -582,10 +582,10 @@ proc parsePar(p: var Parser): PNode = #| | 'finally' | 'except' | 'for' | 'block' | 'const' | 'let' #| | 'when' | 'var' | 'mixin' #| par = '(' optInd - #| ( &parKeyw (ifExpr \ complexOrSimpleStmt) ^+ ';' - #| | ';' (ifExpr \ complexOrSimpleStmt) ^+ ';' + #| ( &parKeyw (ifExpr / complexOrSimpleStmt) ^+ ';' + #| | ';' (ifExpr / complexOrSimpleStmt) ^+ ';' #| | pragmaStmt - #| | simpleExpr ( ('=' expr (';' (ifExpr \ complexOrSimpleStmt) ^+ ';' )? ) + #| | simpleExpr ( ('=' expr (';' (ifExpr / complexOrSimpleStmt) ^+ ';' )? ) #| | (':' expr (',' exprColonEqExpr ^+ ',' )? ) ) ) #| optPar ')' # diff --git a/doc/grammar.txt b/doc/grammar.txt index 8f86dd98c..bf6d381de 100644 --- a/doc/grammar.txt +++ b/doc/grammar.txt @@ -37,10 +37,10 @@ parKeyw = 'discard' | 'include' | 'if' | 'while' | 'case' | 'try' | 'finally' | 'except' | 'for' | 'block' | 'const' | 'let' | 'when' | 'var' | 'mixin' par = '(' optInd - ( &parKeyw (ifExpr \ complexOrSimpleStmt) ^+ ';' - | ';' (ifExpr \ complexOrSimpleStmt) ^+ ';' + ( &parKeyw (ifExpr / complexOrSimpleStmt) ^+ ';' + | ';' (ifExpr / complexOrSimpleStmt) ^+ ';' | pragmaStmt - | simpleExpr ( ('=' expr (';' (ifExpr \ complexOrSimpleStmt) ^+ ';' )? ) + | simpleExpr ( ('=' expr (';' (ifExpr / complexOrSimpleStmt) ^+ ';' )? ) | (':' expr (',' exprColonEqExpr ^+ ',' )? ) ) ) optPar ')' literal = | INT_LIT | INT8_LIT | INT16_LIT | INT32_LIT | INT64_LIT From 77ad8b81e414088c3116bf33f96efacd27eeb471 Mon Sep 17 00:00:00 2001 From: rockcavera Date: Wed, 29 Dec 2021 03:26:54 -0300 Subject: [PATCH 2724/3768] fix 19292 (#19293) --- lib/std/private/win_setenv.nim | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/std/private/win_setenv.nim b/lib/std/private/win_setenv.nim index 067e656a3..91c3f15b0 100644 --- a/lib/std/private/win_setenv.nim +++ b/lib/std/private/win_setenv.nim @@ -23,20 +23,21 @@ check errno_t vs cint when not defined(windows): discard else: + type wchar_t {.importc: "wchar_t".} = int16 + proc setEnvironmentVariableA*(lpName, lpValue: cstring): int32 {. stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableA", sideEffect.} # same as winlean.setEnvironmentVariableA proc c_getenv(env: cstring): cstring {.importc: "getenv", header: "".} proc c_putenv_s(envname: cstring, envval: cstring): cint {.importc: "_putenv_s", header: "".} - proc c_wgetenv(varname: WideCString): WideCString {.importc: "_wgetenv", header: "".} + proc c_wgetenv(varname: ptr wchar_t): ptr wchar_t {.importc: "_wgetenv", header: "".} var errno {.importc, header: "".}: cint - type wchar_t {.importc: "wchar_t".} = int16 var gWenviron {.importc:"_wenviron".}: ptr ptr wchar_t # xxx `ptr UncheckedArray[WideCString]` did not work - proc mbstowcs_s(pReturnValue: ptr csize_t, wcstr: WideCString, sizeInWords: csize_t, mbstr: cstring, count: csize_t): cint {.importc: "mbstowcs_s", header: "".} + proc mbstowcs_s(pReturnValue: ptr csize_t, wcstr: ptr wchar_t, sizeInWords: csize_t, mbstr: cstring, count: csize_t): cint {.importc: "mbstowcs_s", header: "".} # xxx cint vs errno_t? proc setEnvImpl*(name: cstring, value: cstring, overwrite: cint): cint = @@ -77,13 +78,15 @@ else: if gWenviron != nil: # var buf: array[MAX_ENV + 1, WideCString] var buf: array[MAX_ENV + 1, Utf16Char] - let buf2 = cast[WideCString](buf[0].addr) + let buf2 = cast[ptr wchar_t](buf[0].addr) var len: csize_t if mbstowcs_s(len.addr, buf2, buf.len.csize_t, name, MAX_ENV) != 0: errno = EINVAL return -1 - c_wgetenv(buf2)[0] = '\0'.Utf16Char - c_wgetenv(buf2)[1] = '='.Utf16Char + let ptrToEnv = cast[WideCString](c_wgetenv(buf2)) + ptrToEnv[0] = '\0'.Utf16Char + let ptrToEnv2 = cast[WideCString](c_wgetenv(buf2)) + ptrToEnv2[1] = '='.Utf16Char # And now, we have to update the outer environment to have a proper empty value. if setEnvironmentVariableA(name, value) == 0: From a9223d4cdd2e6c23506b666a0958cd2ae3bf5dfd Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 29 Dec 2021 12:18:32 +0100 Subject: [PATCH 2725/3768] nep1: make header reflect reality (#19294) --- doc/nep1.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/nep1.rst b/doc/nep1.rst index fc69bab69..8eb31332f 100644 --- a/doc/nep1.rst +++ b/doc/nep1.rst @@ -1,6 +1,6 @@ -========================================================== -Nim Enhancement Proposal #1 - Standard Library Style Guide -========================================================== +============================ +Standard Library Style Guide +============================ :Author: Clay Sweetser, Dominik Picheta :Version: |nimversion| From 23d64be860958fb7149baed44f6e493394950d69 Mon Sep 17 00:00:00 2001 From: BarrOff <58253563+BarrOff@users.noreply.github.com> Date: Thu, 30 Dec 2021 20:10:36 +0000 Subject: [PATCH 2726/3768] enable `maxDescriptors` on Illumos/Solaris (#19295) --- lib/pure/asyncdispatch.nim | 4 ++-- lib/pure/selectors.nim | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 20ad60427..5e2a8d98e 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -1974,11 +1974,11 @@ when defined(posix): import posix when defined(linux) or defined(windows) or defined(macosx) or defined(bsd) or - defined(zephyr) or defined(freertos): + defined(solaris) or defined(zephyr) or defined(freertos): proc maxDescriptors*(): int {.raises: OSError.} = ## Returns the maximum number of active file descriptors for the current ## process. This involves a system call. For now `maxDescriptors` is - ## supported on the following OSes: Windows, Linux, OSX, BSD. + ## supported on the following OSes: Windows, Linux, OSX, BSD, Solaris. when defined(windows): result = 16_700_000 elif defined(zephyr) or defined(freertos): diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim index ec441f6da..313702370 100644 --- a/lib/pure/selectors.nim +++ b/lib/pure/selectors.nim @@ -324,11 +324,11 @@ else: doAssert(timeout >= -1, "Cannot select with a negative value, got: " & $timeout) when defined(linux) or defined(windows) or defined(macosx) or defined(bsd) or - defined(zephyr) or defined(freertos): + defined(solaris) or defined(zephyr) or defined(freertos): template maxDescriptors*(): int = ## Returns the maximum number of active file descriptors for the current ## process. This involves a system call. For now `maxDescriptors` is - ## supported on the following OSes: Windows, Linux, OSX, BSD. + ## supported on the following OSes: Windows, Linux, OSX, BSD, Solaris. when defined(windows): 16_700_000 elif defined(zephyr) or defined(freertos): From dc5c88ca79e871aefa76c7fefae4282c639a3283 Mon Sep 17 00:00:00 2001 From: rockcavera Date: Thu, 30 Dec 2021 17:52:48 -0300 Subject: [PATCH 2727/3768] Fix #19297 - fixing broken list after adding empty list (#19299) * Update lists.nim * Update tlists.nim * removed check `if b.tail != nil` The tail of the list being null it is still possible to retrieve its end by going through all nodes from the head. So checking for null from `b.tail` is unnecessary. However, setting `a.tail = b.tail` only if `a.head != nil`, so you don't break a good list with an already broken one. --- lib/pure/collections/lists.nim | 11 ++++++----- tests/stdlib/tlists.nim | 12 ++++++++++++ 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/lib/pure/collections/lists.nim b/lib/pure/collections/lists.nim index 8b255fb60..de5550d6e 100644 --- a/lib/pure/collections/lists.nim +++ b/lib/pure/collections/lists.nim @@ -530,11 +530,12 @@ proc addMoved*[T](a, b: var SinglyLinkedList[T]) {.since: (1, 5, 1).} = ci assert s == [0, 1, 0, 1, 0, 1] - if a.tail != nil: - a.tail.next = b.head - a.tail = b.tail - if a.head == nil: - a.head = b.head + if b.head != nil: + if a.head == nil: + a.head = b.head + else: + a.tail.next = b.head + a.tail = b.tail if a.addr != b.addr: b.head = nil b.tail = nil diff --git a/tests/stdlib/tlists.nim b/tests/stdlib/tlists.nim index 54fa8b24f..2f6d5ee6a 100644 --- a/tests/stdlib/tlists.nim +++ b/tests/stdlib/tlists.nim @@ -233,6 +233,18 @@ template main = doAssert l.toSeq == [1] doAssert l.remove(l.head) == true doAssert l.toSeq == [] + + block issue19297: # add (appends a shallow copy) + var a: SinglyLinkedList[int] + var b: SinglyLinkedList[int] + + doAssert a.toSeq == @[] + a.add(1) + doAssert a.toSeq == @[1] + a.add(b) + doAssert a.toSeq == @[1] + a.add(2) + doAssert a.toSeq == @[1, 2] static: main() main() From ac37eed5a28042d50e23d9ae145f2d46b8dc5058 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 31 Dec 2021 09:21:30 +0100 Subject: [PATCH 2728/3768] fixes #16617 [backport] (#19300) --- compiler/typeallowed.nim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/typeallowed.nim b/compiler/typeallowed.nim index d00aa538f..78cd74e56 100644 --- a/compiler/typeallowed.nim +++ b/compiler/typeallowed.nim @@ -57,6 +57,8 @@ proc typeAllowedAux(marker: var IntSet, typ: PType, kind: TSymKind, of tyVar, tyLent: if kind in {skProc, skFunc, skConst} and (views notin c.features): result = t + elif taIsOpenArray in flags: + result = t elif t.kind == tyLent and ((kind != skResult and views notin c.features) or kind == skParam): # lent can't be used as parameters. result = t From faaf271928acaa19b53c40d9c5b37e6441cb96c1 Mon Sep 17 00:00:00 2001 From: hlaaftana <10591326+hlaaftana@users.noreply.github.com> Date: Sat, 1 Jan 2022 15:33:16 +0300 Subject: [PATCH 2729/3768] Update JS and nimscript import tests (#19306) * add new modules, except experimental ones * remove deprecated modules mersenne and sharedlist * better describe why some modules fail and some modules don't --- tests/js/tstdlib_imports.nim | 43 +++++++++++++++++-------------- tests/test_nimscript.nims | 50 +++++++++++++++++++++--------------- 2 files changed, 53 insertions(+), 40 deletions(-) diff --git a/tests/js/tstdlib_imports.nim b/tests/js/tstdlib_imports.nim index 6fe3772d3..9986fa059 100644 --- a/tests/js/tstdlib_imports.nim +++ b/tests/js/tstdlib_imports.nim @@ -7,46 +7,48 @@ discard """ import std/[ # Core: bitops, typetraits, lenientops, macros, volatile, typeinfo, - # fails: endians, rlocks - # works but shouldn't: cpuinfo, locks + # fails due to FFI: rlocks + # fails due to cstring cast/copyMem: endians + # works but uses FFI: cpuinfo, locks # Algorithms: - algorithm, sequtils, + algorithm, enumutils, sequtils, setutils, # Collections: critbits, deques, heapqueue, intsets, lists, options, sets, - sharedlist, tables, - # fails: sharedtables + tables, packedsets, # Strings: cstrutils, editdistance, wordwrap, parseutils, ropes, pegs, punycode, strformat, strmisc, strscans, strtabs, strutils, unicode, unidecode, - # fails: encodings + # fails due to FFI: encodings # Time handling: monotimes, times, # Generic operator system services: os, streams, - # fails: distros, dynlib, marshal, memfiles, osproc, terminal + # fails intentionally: dynlib, marshal, memfiles + # fails due to FFI: osproc, terminal + # fails due to osproc import: distros # Math libraries: - complex, math, mersenne, random, rationals, stats, sums, - # works but shouldn't: fenv + complex, math, random, rationals, stats, sums, sysrand, + # works but uses FFI: fenv # Internet protocols: cookies, httpcore, mimetypes, uri, - # fails: asyncdispatch, asyncfile, asyncftpclient, asynchttpserver, + # fails due to FFI: asyncdispatch, asyncfile, asyncftpclient, asynchttpserver, # asyncnet, cgi, httpclient, nativesockets, net, selectors, smtp - # works but shouldn't test: asyncstreams, asyncfutures + # works but no need to test: asyncstreams, asyncfutures # Threading: - # fails: threadpool + # fails due to FFI: threadpool # Parsers: htmlparser, json, lexbase, parsecfg, parsecsv, parsesql, parsexml, - parseopt, + parseopt, jsonutils, # XML processing: xmltree, xmlparser, @@ -56,16 +58,19 @@ import std/[ # Hashing: base64, hashes, - # fails: md5, oids, sha1 + # fails due to cstring cast/zeroMem/copyMem/moveMem: md5 + # fails due to cstring cast/endians import: oids + # fails due to copyMem/endians import: sha1 # Miscellaneous: - colors, logging, sugar, unittest, varints, - # fails: browsers, coro - # works but shouldn't: segfaults + colors, logging, sugar, unittest, varints, enumerate, with, + # fails due to FFI: browsers, coro + # works but uses FFI: segfaults # Modules for JS backend: - asyncjs, dom, jsconsole, jscore, jsffi, + asyncjs, dom, jsconsole, jscore, jsffi, jsbigints, # Unlisted in lib.html: - decls, compilesettings, with, wrapnils + decls, compilesettings, wrapnils, exitprocs, effecttraits, + genasts, importutils, isolation, jsfetch, jsformdata, jsheaders ] diff --git a/tests/test_nimscript.nims b/tests/test_nimscript.nims index ecb409677..761ddafdf 100644 --- a/tests/test_nimscript.nims +++ b/tests/test_nimscript.nims @@ -8,47 +8,49 @@ from stdtest/specialpaths import buildDir import std/[ # Core: bitops, typetraits, lenientops, macros, volatile, - # fails: typeinfo, endians - # works but shouldn't: cpuinfo, rlocks, locks + # fails due to FFI: typeinfo + # fails due to cstring cast/copyMem: endians + # works but uses FFI: cpuinfo, rlocks, locks # Algorithms: - algorithm, sequtils, + algorithm, enumutils, sequtils, setutils, # Collections: critbits, deques, heapqueue, intsets, lists, options, sets, - sharedlist, tables, - # fails: sharedtables + tables, packedsets, # Strings: editdistance, wordwrap, parseutils, ropes, pegs, punycode, strformat, strmisc, strscans, strtabs, - strutils, unicode, unidecode, - # works but shouldn't: cstrutils, encodings + strutils, unicode, unidecode, cstrutils, + # works but uses FFI: encodings # Time handling: - # fails: monotimes, times + # fails due to FFI: monotimes, times # but times.getTime() implemented for VM # Generic operator system services: - os, streams, - # fails: distros, dynlib, marshal, memfiles, osproc, terminal + os, streams, distros, + # fails due to FFI: memfiles, osproc, terminal + # works but uses FFI: dynlib + # intentionally fails: marshal # Math libraries: - complex, math, mersenne, random, rationals, stats, sums, - # works but shouldn't: fenv + complex, math, random, rationals, stats, sums, + # works but uses FFI: fenv, sysrand # Internet protocols: httpcore, mimetypes, uri, - # fails: asyncdispatch, asyncfile, asyncftpclient, asynchttpserver, + # fails due to FFI: asyncdispatch, asyncfile, asyncftpclient, asynchttpserver, # asyncnet, cgi, cookies, httpclient, nativesockets, net, selectors, smtp - # works but shouldn't test: asyncstreams, asyncfutures + # works but no need to test: asyncstreams, asyncfutures # Threading: - # fails: threadpool + # fails due to FFI: threadpool # Parsers: htmlparser, json, lexbase, parsecfg, parsecsv, parsesql, parsexml, - parseopt, + parseopt, jsonutils, # XML processing: xmltree, xmlparser, @@ -58,17 +60,23 @@ import std/[ # Hashing: base64, hashes, - # fails: md5, oids, sha1 + # fails due to cstring cast/zeroMem/copyMem/moveMem: md5 + # fails due to cstring cast/times import/endians import: oids + # fails due to copyMem/endians import: sha1 # Miscellaneous: - colors, sugar, varints, - # fails: browsers, coro, logging (times), segfaults, unittest (uses methods) + colors, sugar, varints, enumerate, with, + # fails due to FFI: browsers, coro, segfaults + # fails due to times import/methods: logging + # fails due to methods: unittest # Modules for JS backend: - # fails: asyncjs, dom, jsconsole, jscore, jsffi, + # fails intentionally: asyncjs, dom, jsconsole, jscore, jsffi, jsbigints, + # jsfetch, jsformdata, jsheaders # Unlisted in lib.html: - decls, compilesettings, with, wrapnils + decls, compilesettings, wrapnils, effecttraits, genasts, + importutils, isolation ] # non-std imports From ef634cc2516ce81a05486f726e423ac458c4467a Mon Sep 17 00:00:00 2001 From: rockcavera Date: Sat, 1 Jan 2022 09:33:29 -0300 Subject: [PATCH 2730/3768] Update manual.rst (#19301) --- doc/manual.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual.rst b/doc/manual.rst index d9f5d4ea6..8788e53a3 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -5018,7 +5018,7 @@ be used: See also: -- `Shared heap memory management `_. +- `Shared heap memory management `_. From 61d682e733005534a16af72fc9b0c0e74d2f9ebf Mon Sep 17 00:00:00 2001 From: hlaaftana <10591326+hlaaftana@users.noreply.github.com> Date: Sun, 2 Jan 2022 09:29:55 +0300 Subject: [PATCH 2731/3768] Allow full commands and blocks in type sections (#19181) * allow full commands and blocks in type sections * update grammar * fix changelog [skip ci] * more tests * even more tests --- changelog.md | 3 + compiler/parser.nim | 28 ++- doc/grammar.txt | 5 +- tests/parser/ttypecommandcomma.nim | 9 + tests/parser/ttypecommandindent1.nim | 9 + tests/parser/ttypecommandindent2.nim | 11 + tests/parser/ttypecommandindent3.nim | 11 + tests/parser/ttypesectioncalls.nim | 328 +++++++++++++++++++++++++++ 8 files changed, 393 insertions(+), 11 deletions(-) create mode 100644 tests/parser/ttypecommandcomma.nim create mode 100644 tests/parser/ttypecommandindent1.nim create mode 100644 tests/parser/ttypecommandindent2.nim create mode 100644 tests/parser/ttypecommandindent3.nim create mode 100644 tests/parser/ttypesectioncalls.nim diff --git a/changelog.md b/changelog.md index 52159b865..89aaff664 100644 --- a/changelog.md +++ b/changelog.md @@ -61,6 +61,9 @@ ``` - [Case statement macros](manual.html#macros-case-statement-macros) are no longer experimental, meaning you no longer need to enable the experimental switch `caseStmtMacros` to use them. +- Full command syntax and block arguments i.e. `foo a, b: c` are now allowed + for the right-hand side of type definitions in type sections. Previously + they would error with "invalid indentation". ## Compiler changes diff --git a/compiler/parser.nim b/compiler/parser.nim index 7bc2c78e0..0fb60440c 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -811,7 +811,7 @@ proc primarySuffix(p: var Parser, r: PNode, #| | DOTLIKEOP optInd symbol generalizedLit? #| | '[' optInd exprColonEqExprList optPar ']' #| | '{' optInd exprColonEqExprList optPar '}' - #| | &( '`'|IDENT|literal|'cast'|'addr'|'type') expr # command syntax + #| | &( '`'|IDENT|literal|'cast'|'addr'|'type') expr (comma expr)* # command syntax result = r # progress guaranteed @@ -821,14 +821,14 @@ proc primarySuffix(p: var Parser, r: PNode, of tkParLe: # progress guaranteed if p.tok.strongSpaceA > 0: - # inside type sections, expressions such as `ref (int, bar)` - # are parsed as a nkCommand with a single tuple argument (nkPar) + result = commandExpr(p, result, mode) + # type sections allow full command syntax if mode == pmTypeDef: - result = newNodeP(nkCommand, p) - result.add r - result.add primary(p, pmNormal) - else: - result = commandExpr(p, result, mode) + var isFirstParam = false + while p.tok.tokType == tkComma: + getTok(p) + optInd(p, result) + result.add(commandParam(p, isFirstParam, mode)) break result = namedParams(p, result, nkCall, tkParRi) if result.len > 1 and result[1].kind == nkExprColonExpr: @@ -869,9 +869,18 @@ proc primarySuffix(p: var Parser, r: PNode, # actually parsing {.push hints:off.} as {.push(hints:off).} is a sweet # solution, but pragmas.nim can't handle that result = commandExpr(p, result, mode) + if mode == pmTypeDef: + var isFirstParam = false + while p.tok.tokType == tkComma: + getTok(p) + optInd(p, result) + result.add(commandParam(p, isFirstParam, mode)) break else: break + # type sections allow post-expr blocks + if mode == pmTypeDef: + result = postExprBlocks(p, result) proc parseOperators(p: var Parser, headNode: PNode, limit: int, mode: PrimaryMode): PNode = @@ -1342,7 +1351,8 @@ proc parseTypeDesc(p: var Parser): PNode = result = binaryNot(p, result) proc parseTypeDefAux(p: var Parser): PNode = - #| typeDefAux = simpleExpr ('not' expr)? + #| typeDefAux = simpleExpr ('not' expr + #| | postExprBlocks)? result = simpleExpr(p, pmTypeDef) result = binaryNot(p, result) diff --git a/doc/grammar.txt b/doc/grammar.txt index bf6d381de..1e41b9eeb 100644 --- a/doc/grammar.txt +++ b/doc/grammar.txt @@ -60,7 +60,7 @@ primarySuffix = '(' (exprColonEqExpr comma?)* ')' | DOTLIKEOP optInd symbol generalizedLit? | '[' optInd exprColonEqExprList optPar ']' | '{' optInd exprColonEqExprList optPar '}' - | &( '`'|IDENT|literal|'cast'|'addr'|'type') expr # command syntax + | &( '`'|IDENT|literal|'cast'|'addr'|'type') expr (comma expr)* # command syntax pragma = '{.' optInd (exprColonEqExpr comma?)* optPar ('.}' | '}') identVis = symbol OPR? # postfix position identVisDot = symbol '.' optInd symbol OPR? @@ -93,7 +93,8 @@ primary = operatorB primary primarySuffix* | ('var' | 'out' | 'ref' | 'ptr' | 'distinct') primary / prefixOperator* identOrLiteral primarySuffix* typeDesc = simpleExpr ('not' expr)? -typeDefAux = simpleExpr ('not' expr)? +typeDefAux = simpleExpr ('not' expr + | postExprBlocks)? postExprBlocks = ':' stmt? ( IND{=} doBlock | IND{=} 'of' exprList ':' stmt | IND{=} 'elif' expr ':' stmt diff --git a/tests/parser/ttypecommandcomma.nim b/tests/parser/ttypecommandcomma.nim new file mode 100644 index 000000000..7ca59a799 --- /dev/null +++ b/tests/parser/ttypecommandcomma.nim @@ -0,0 +1,9 @@ +discard """ + errormsg: "invalid indentation" + line: 8 + column: 19 +""" + +type + Foo = call(1, 2), 3: + 4 \ No newline at end of file diff --git a/tests/parser/ttypecommandindent1.nim b/tests/parser/ttypecommandindent1.nim new file mode 100644 index 000000000..9aa274e2a --- /dev/null +++ b/tests/parser/ttypecommandindent1.nim @@ -0,0 +1,9 @@ +discard """ + errormsg: "invalid indentation" + line: 9 + column: 3 +""" + +type + Foo = call x, y, z: + abc diff --git a/tests/parser/ttypecommandindent2.nim b/tests/parser/ttypecommandindent2.nim new file mode 100644 index 000000000..0883df90f --- /dev/null +++ b/tests/parser/ttypecommandindent2.nim @@ -0,0 +1,11 @@ +discard """ + errormsg: "invalid indentation" + line: 10 + column: 5 +""" + +type + Foo = call x, y, z: + abc + do: + def diff --git a/tests/parser/ttypecommandindent3.nim b/tests/parser/ttypecommandindent3.nim new file mode 100644 index 000000000..f9fdcc1e4 --- /dev/null +++ b/tests/parser/ttypecommandindent3.nim @@ -0,0 +1,11 @@ +discard """ + errormsg: "expression expected, but found 'keyword do'" + line: 10 + column: 1 +""" + +type + Foo = call x, y, z: + abc +do: + def diff --git a/tests/parser/ttypesectioncalls.nim b/tests/parser/ttypesectioncalls.nim new file mode 100644 index 000000000..003444fc5 --- /dev/null +++ b/tests/parser/ttypesectioncalls.nim @@ -0,0 +1,328 @@ +discard """ +nimout: ''' +StmtList + TypeSection + TypeDef + Ident "A" + Empty + Call + Ident "call" + IntLit 1 + TypeSection + TypeDef + Ident "B" + Empty + Command + Ident "call" + IntLit 2 + TypeDef + Ident "C" + Empty + Call + Ident "call" + StmtList + IntLit 3 + TypeDef + Ident "D" + Empty + Call + Ident "call" + StmtList + IntLit 4 + TypeSection + TypeDef + Ident "E" + Empty + Call + Ident "call" + IntLit 5 + IntLit 6 + TypeDef + Ident "F" + Empty + Command + Ident "call" + IntLit 7 + IntLit 8 + TypeDef + Ident "G" + Empty + Call + Ident "call" + IntLit 9 + StmtList + IntLit 10 + TypeDef + Ident "H" + Empty + Call + Ident "call" + IntLit 11 + StmtList + IntLit 12 + TypeDef + Ident "I" + Empty + Command + Ident "call" + IntLit 13 + StmtList + IntLit 14 + TypeDef + Ident "J" + Empty + Command + Ident "call" + IntLit 15 + StmtList + IntLit 16 + TypeSection + TypeDef + Ident "K" + Empty + Call + Ident "call" + IntLit 17 + IntLit 18 + IntLit 19 + TypeDef + Ident "L" + Empty + Command + Ident "call" + IntLit 20 + IntLit 21 + IntLit 22 + TypeDef + Ident "M" + Empty + Call + Ident "call" + IntLit 23 + IntLit 24 + StmtList + IntLit 25 + TypeDef + Ident "N" + Empty + Command + Ident "call" + IntLit 26 + IntLit 27 + StmtList + IntLit 28 + TypeDef + Ident "O" + Empty + Command + Ident "call" + IntLit 29 + IntLit 30 + StmtList + IntLit 31 + TypeSection + TypeDef + Ident "P" + Empty + Command + Ident "call" + TupleConstr + IntLit 32 + IntLit 33 + Infix + Ident "+" + Infix + Ident "*" + IntLit 34 + IntLit 35 + IntLit 36 + StmtList + IntLit 37 + TypeDef + Ident "R" + Empty + Command + Ident "call" + Infix + Ident "@" + TupleConstr + IntLit 38 + IntLit 39 + Infix + Ident "shl" + IntLit 40 + IntLit 41 + Infix + Ident "-" + Infix + Ident "*" + IntLit 42 + IntLit 43 + IntLit 44 + StmtList + IntLit 45 + TypeDef + Ident "S" + Empty + Command + Ident "call" + IntLit 46 + StmtList + IntLit 47 + StmtList + IntLit 48 + TypeDef + Ident "T" + Empty + Call + Ident "call" + StmtList + IntLit 49 + StmtList + IntLit 50 + StmtList + IntLit 51 +a: IntLit 1 +a: IntLit 2 +a: StmtList + IntLit 3 +a: StmtList + IntLit 4 +a: IntLit 5 +b: IntLit 6 +a: IntLit 7 +b: IntLit 8 +a: IntLit 9 +b: StmtList + IntLit 10 +a: IntLit 11 +b: StmtList + IntLit 12 +a: IntLit 13 +b: StmtList + IntLit 14 +a: IntLit 15 +b: StmtList + IntLit 16 +a: IntLit 17 +b: IntLit 18 +c: IntLit 19 +a: IntLit 20 +b: IntLit 21 +c: IntLit 22 +a: IntLit 23 +b: IntLit 24 +c: StmtList + IntLit 25 +a: IntLit 26 +b: IntLit 27 +c: StmtList + IntLit 28 +a: IntLit 29 +b: IntLit 30 +c: StmtList + IntLit 31 +a: TupleConstr + IntLit 32 + IntLit 33 +b: Infix + Ident "+" + Infix + Ident "*" + IntLit 34 + IntLit 35 + IntLit 36 +c: StmtList + IntLit 37 +a: Infix + Ident "@" + TupleConstr + IntLit 38 + IntLit 39 + Infix + Ident "shl" + IntLit 40 + IntLit 41 +b: Infix + Ident "-" + Infix + Ident "*" + IntLit 42 + IntLit 43 + IntLit 44 +c: StmtList + IntLit 45 +a: IntLit 46 +b: StmtList + IntLit 47 +c: StmtList + IntLit 48 +a: StmtList + IntLit 49 +b: StmtList + IntLit 50 +c: StmtList + IntLit 51 +''' +""" +import macros + +macro call(a): untyped = + echo "a: ", a.treeRepr + result = ident"int" +macro call(a, b): untyped = + echo "a: ", a.treeRepr + echo "b: ", b.treeRepr + result = ident"int" +macro call(a, b, c): untyped = + echo "a: ", a.treeRepr + echo "b: ", b.treeRepr + echo "c: ", c.treeRepr + result = ident"int" + +macro sections(x): untyped = + echo x.treeRepr + result = newStmtList(x) + for ts in x: + for td in ts: + let t = td[0] + result.add quote do: + doAssert `t` is int + +sections: + type A = call(1) + type + B = call 2 + C = call: 3 + D = call(): 4 + type + E = call(5, 6) + F = call 7, 8 + G = call(9): 10 + H = call(11): + 12 + I = call 13: 14 + J = call 15: + 16 + type + K = call(17, 18, 19) + L = call 20, 21, 22 + M = call(23, 24): 25 + N = call 26, 27: 28 + O = call 29, 30: + 31 + type + P = call (32, 33), 34 * 35 + 36: + 37 + R = call (38, 39) @ 40 shl 41, 42 * 43 - 44: + 45 + S = call 46: + 47 + do: + 48 + T = call: + 49 + do: + 50 + do: + 51 From 53e1d57419431050a54dcdf539a2a02bf6d28c5b Mon Sep 17 00:00:00 2001 From: BarrOff <58253563+BarrOff@users.noreply.github.com> Date: Sun, 2 Jan 2022 06:37:13 +0000 Subject: [PATCH 2732/3768] add compile time option for POSIX sigwait on Illumos/Solaris (#19296) * add compile time option for POSIX sigwait on Illumos/Solaris * fix link to documentation of `sigwait` on Illumos/Solaris --- lib/posix/posix.nim | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index 57dd6e2c7..912e395a7 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -772,6 +772,13 @@ else: proc sigtimedwait*(a1: var Sigset, a2: var SigInfo, a3: var Timespec): cint {.importc, header: "".} +when defined(sunos) or defined(solaris): + # The following compile time flag is needed on Illumos/Solaris to use the POSIX + # `sigwait` implementation. See the documentation here: + # https://docs.oracle.com/cd/E19455-01/806-5257/6je9h033k/index.html + # https://www.illumos.org/man/2/sigwait + {.passc: "-D_POSIX_PTHREAD_SEMANTICS".} + proc sigwait*(a1: var Sigset, a2: var cint): cint {. importc, header: "".} proc sigwaitinfo*(a1: var Sigset, a2: var SigInfo): cint {. From bbd5086bc319dc22971a06ef24d02dc49ff308f3 Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 3 Jan 2022 16:02:58 +0800 Subject: [PATCH 2733/3768] [docs] clarify the raised exception (#19308) * [docs] clarify the raised exception Lest developers wanna know what the exception is. * Apply suggestions from @konsumlamm Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> --- lib/pure/uri.nim | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim index a828298c2..7583dbd1e 100644 --- a/lib/pure/uri.nim +++ b/lib/pure/uri.nim @@ -151,10 +151,10 @@ func encodeQuery*(query: openArray[(string, string)], usePlus = true, result.add(encodeUrl(val, usePlus)) iterator decodeQuery*(data: string): tuple[key, value: string] = - ## Reads and decodes query string `data` and yields the `(key, value)` pairs - ## the data consists of. If compiled with `-d:nimLegacyParseQueryStrict`, an - ## error is raised when there is an unencoded `=` character in a decoded - ## value, which was the behavior in Nim < 1.5.1 + ## Reads and decodes the query string `data` and yields the `(key, value)` pairs + ## the data consists of. If compiled with `-d:nimLegacyParseQueryStrict`, + ## a `UriParseError` is raised when there is an unencoded `=` character in a decoded + ## value, which was the behavior in Nim < 1.5.1. runnableExamples: import std/sequtils assert toSeq(decodeQuery("foo=1&bar=2=3")) == @[("foo", "1"), ("bar", "2=3")] From e49d52eb6174b2721293968e7811928c303c3776 Mon Sep 17 00:00:00 2001 From: Carlo Capocasa Date: Mon, 3 Jan 2022 09:11:23 +0100 Subject: [PATCH 2734/3768] Add Week-Of-Year Implementation to Times Module (#17223) * initial * more tests * Apply suggestions from code review idiomatize Co-authored-by: Timothee Cour * test iron age dates * add examples * fix typo * consistent param mention * add since pragrams * add changelog * Update lib/pure/times.nim Co-authored-by: Timothee Cour * fix examples * fix negative years * add getWeeksInYear tests * add back fix dropped by rebase * week-year tuple api * add changelog * fix doc tags * add docstrings * fix typos Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Co-authored-by: Timothee Cour Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> --- changelog.md | 6 +++ lib/pure/times.nim | 82 +++++++++++++++++++++++++++++++++++ tests/stdlib/ttimes.nim | 95 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 183 insertions(+) diff --git a/changelog.md b/changelog.md index 89aaff664..1b97fd99a 100644 --- a/changelog.md +++ b/changelog.md @@ -27,6 +27,12 @@ - Sends `ehlo` first. If the mail server does not understand, it sends `helo` as a fallback. +- Added `IsoWeekRange`, a range type to represent the number of weeks in an ISO week-based year. +- Added `IsoYear`, a distinct int type to prevent bugs from confusing the week-based year and the regular year. +- Added `initDateTime` in `times` to create a datetime from a weekday, and ISO 8601 week number and week-based year. +- Added `getIsoWeekAndYear` in `times` to get an ISO week number along with the corresponding ISO week-based year from a datetime. +- Added `getIsoWeeksInYear` in `times` to return the number of weeks in an ISO week-based year. + ## Language changes - Pragma macros on type definitions can now return `nnkTypeSection` nodes as well as `nnkTypeDef`, diff --git a/lib/pure/times.nim b/lib/pure/times.nim index 113f73d2a..7851bf158 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -288,6 +288,13 @@ type YeardayRange* = range[0..365] NanosecondRange* = range[0..999_999_999] + IsoWeekRange* = range[1 .. 53] + ## An ISO 8601 calendar week number. + IsoYear* = distinct int + ## An ISO 8601 calendar year number. + ## + ## .. warning:: The ISO week-based year can correspond to the following or previous year from 29 December to January 3. + Time* = object ## Represents a point in time. seconds: int64 nanosecond: NanosecondRange @@ -529,6 +536,54 @@ proc getDaysInYear*(year: int): int = doAssert getDaysInYear(2001) == 365 result = 365 + (if isLeapYear(year): 1 else: 0) +proc `==`*(a, b: IsoYear): bool {.borrow.} +proc `$`*(p: IsoYear): string {.borrow.} + +proc getWeeksInIsoYear*(y: IsoYear): IsoWeekRange {.since: (1, 5).} = + ## Returns the number of weeks in the specified ISO 8601 week-based year, which can be + ## either 53 or 52. + runnableExamples: + assert getWeeksInIsoYear(IsoYear(2000)) == 52 + assert getWeeksInIsoYear(IsoYear(2001)) == 53 + + var y = int(y) + + # support negative years + y = if y < 0: 400 + y mod 400 else: y + + # source: https://webspace.science.uu.nl/~gent0113/calendar/isocalendar.htm + let p = (y + (y div 4) - (y div 100) + (y div 400)) mod 7 + let y1 = y - 1 + let p1 = (y1 + (y1 div 4) - (y1 div 100) + (y1 div 400)) mod 7 + if p == 4 or p1 == 3: 53 else: 52 + +proc getIsoWeekAndYear*(dt: DateTime): + tuple[isoweek: IsoWeekRange, isoyear: IsoYear] {.since: (1, 5).} = + ## Returns the ISO 8601 week and year. + ## + ## .. warning:: The ISO week-based year can correspond to the following or previous year from 29 December to January 3. + runnableExamples: + assert getIsoWeekAndYear(initDateTime(21, mApr, 2018, 00, 00, 00)) == (isoweek: 16.IsoWeekRange, isoyear: 2018.IsoYear) + block: + let (w, y) = getIsoWeekAndYear(initDateTime(30, mDec, 2019, 00, 00, 00)) + assert w == 01.IsoWeekRange + assert y == 2020.IsoYear + assert getIsoWeekAndYear(initDateTime(13, mSep, 2020, 00, 00, 00)) == (isoweek: 37.IsoWeekRange, isoyear: 2020.IsoYear) + block: + let (w, y) = getIsoWeekAndYear(initDateTime(2, mJan, 2021, 00, 00, 00)) + assert w.int > 52 + assert w.int < 54 + assert y.int mod 100 == 20 + + # source: https://webspace.science.uu.nl/~gent0113/calendar/isocalendar.htm + var w = (dt.yearday.int - dt.weekday.int + 10) div 7 + if w < 1: + (isoweek: getWeeksInIsoYear(IsoYear(dt.year - 1)), isoyear: IsoYear(dt.year - 1)) + elif (w > getWeeksInIsoYear(IsoYear(dt.year))): + (isoweek: IsoWeekRange(1), isoyear: IsoYear(dt.year + 1)) + else: + (isoweek: IsoWeekRange(w), isoyear: IsoYear(dt.year)) + proc stringifyUnit(value: int | int64, unit: TimeUnit): string = ## Stringify time unit with it's name, lowercased let strUnit = $unit @@ -2578,6 +2633,33 @@ proc `+=`*(t: var Time, b: TimeInterval) = proc `-=`*(t: var Time, b: TimeInterval) = t = t - b +# +# Day of year +# + +proc initDateTime*(weekday: WeekDay, isoweek: IsoWeekRange, isoyear: IsoYear, + hour: HourRange, minute: MinuteRange, second: SecondRange, + nanosecond: NanosecondRange, + zone: Timezone = local()): DateTime {.since: (1, 5).} = + ## Create a new `DateTime <#DateTime>`_ from a weekday and an ISO 8601 week number and year + ## in the specified timezone. + ## + ## .. warning:: The ISO week-based year can correspond to the following or previous year from 29 December to January 3. + runnableExamples: + assert initDateTime(21, mApr, 2018, 00, 00, 00) == initDateTime(dSat, 16, 2018.IsoYear, 00, 00, 00) + assert initDateTime(30, mDec, 2019, 00, 00, 00) == initDateTime(dMon, 01, 2020.IsoYear, 00, 00, 00) + assert initDateTime(13, mSep, 2020, 00, 00, 00) == initDateTime(dSun, 37, 2020.IsoYear, 00, 00, 00) + assert initDateTime(2, mJan, 2021, 00, 00, 00) == initDateTime(dSat, 53, 2020.IsoYear, 00, 00, 00) + + # source https://webspace.science.uu.nl/~gent0113/calendar/isocalendar.htm + let d = isoweek * 7 + weekday.int - initDateTime(4, mJan, isoyear.int, 00, 00, 00).weekday.int - 4 + initDateTime(1, mJan, isoyear.int, hour, minute, second, nanosecond, zone) + initTimeInterval(days=d) + +proc initDateTime*(weekday: WeekDay, isoweek: IsoWeekRange, isoyear: IsoYear, + hour: HourRange, minute: MinuteRange, second: SecondRange, + zone: Timezone = local()): DateTime {.since: (1, 5).} = + initDateTime(weekday, isoweek, isoyear, hour, minute, second, 0, zone) + # # Other # diff --git a/tests/stdlib/ttimes.nim b/tests/stdlib/ttimes.nim index 66157b91c..2cf945786 100644 --- a/tests/stdlib/ttimes.nim +++ b/tests/stdlib/ttimes.nim @@ -646,3 +646,98 @@ block: # ttimes doAssert initDuration(milliseconds = 500).inMilliseconds == 500 doAssert initDuration(milliseconds = -500).inMilliseconds == -500 doAssert initDuration(nanoseconds = -999999999).inMilliseconds == -999 + + block: # getIsoWeekAndYear + doAssert getIsoWeekAndYear(initDateTime(04, mNov, 2019, 00, 00, 00)) == (isoweek: 45.IsoWeekRange, isoyear: 2019.IsoYear) + doAssert initDateTime(dMon, 45, 2019.IsoYear, 00, 00, 00) == initDateTime(04, mNov, 2019, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(28, mDec, 2019, 00, 00, 00)) == (isoweek: 52.IsoWeekRange, isoyear: 2019.IsoYear) + doAssert initDateTime(dSat, 52, 2019.IsoYear, 00, 00, 00) == initDateTime(28, mDec, 2019, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(29, mDec, 2019, 00, 00, 00)) == (isoweek: 52.IsoWeekRange, isoyear: 2019.IsoYear) + doAssert initDateTime(dSun, 52, 2019.IsoYear, 00, 00, 00) == initDateTime(29, mDec, 2019, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(30, mDec, 2019, 00, 00, 00)) == (isoweek: 01.IsoWeekRange, isoyear: 2020.IsoYear) + doAssert initDateTime(dMon, 01, 2020.IsoYear, 00, 00, 00) == initDateTime(30, mDec, 2019, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(31, mDec, 2019, 00, 00, 00)) == (isoweek: 01.IsoWeekRange, isoyear: 2020.IsoYear) + doAssert initDateTime(dTue, 01, 2020.IsoYear, 00, 00, 00) == initDateTime(31, mDec, 2019, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(01, mJan, 2020, 00, 00, 00)) == (isoweek: 01.IsoWeekRange, isoyear: 2020.IsoYear) + doAssert initDateTime(dWed, 01, 2020.IsoYear, 00, 00, 00) == initDateTime(01, mJan, 2020, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(02, mJan, 2020, 00, 00, 00)) == (isoweek: 01.IsoWeekRange, isoyear: 2020.IsoYear) + doAssert initDateTime(dThu, 01, 2020.IsoYear, 00, 00, 00) == initDateTime(02, mJan, 2020, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(05, mApr, 2020, 00, 00, 00)) == (isoweek: 14.IsoWeekRange, isoyear: 2020.IsoYear) + doAssert initDateTime(dSun, 14, 2020.IsoYear, 00, 00, 00) == initDateTime(05, mApr, 2020, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(06, mApr, 2020, 00, 00, 00)) == (isoweek: 15.IsoWeekRange, isoyear: 2020.IsoYear) + doAssert initDateTime(dMon, 15, 2020.IsoYear, 00, 00, 00) == initDateTime(06, mApr, 2020, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(10, mApr, 2020, 00, 00, 00)) == (isoweek: 15.IsoWeekRange, isoyear: 2020.IsoYear) + doAssert initDateTime(dFri, 15, 2020.IsoYear, 00, 00, 00) == initDateTime(10, mApr, 2020, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(12, mApr, 2020, 00, 00, 00)) == (isoweek: 15.IsoWeekRange, isoyear: 2020.IsoYear) + doAssert initDateTime(dSun, 15, 2020.IsoYear, 00, 00, 00) == initDateTime(12, mApr, 2020, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(13, mApr, 2020, 00, 00, 00)) == (isoweek: 16.IsoWeekRange, isoyear: 2020.IsoYear) + doAssert initDateTime(dMon, 16, 2020.IsoYear, 00, 00, 00) == initDateTime(13, mApr, 2020, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(15, mApr, 2020, 00, 00, 00)) == (isoweek: 16.IsoWeekRange, isoyear: 2020.IsoYear) + doAssert initDateTime(dThu, 16, 2020.IsoYear, 00, 00, 00) == initDateTime(16, mApr, 2020, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(17, mJul, 2020, 00, 00, 00)) == (isoweek: 29.IsoWeekRange, isoyear: 2020.IsoYear) + doAssert initDateTime(dFri, 29, 2020.IsoYear, 00, 00, 00) == initDateTime(17, mJul, 2020, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(19, mJul, 2020, 00, 00, 00)) == (isoweek: 29.IsoWeekRange, isoyear: 2020.IsoYear) + doAssert initDateTime(dSun, 29, 2020.IsoYear, 00, 00, 00) == initDateTime(19, mJul, 2020, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(20, mJul, 2020, 00, 00, 00)) == (isoweek: 30.IsoWeekRange, isoyear: 2020.IsoYear) + doAssert initDateTime(dMon, 30, 2020.IsoYear, 00, 00, 00) == initDateTime(20, mJul, 2020, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(23, mJul, 2020, 00, 00, 00)) == (isoweek: 30.IsoWeekRange, isoyear: 2020.IsoYear) + doAssert initDateTime(dThu, 30, 2020.IsoYear, 00, 00, 00) == initDateTime(23, mJul, 2020, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(31, mDec, 2020, 00, 00, 00)) == (isoweek: 53.IsoWeekRange, isoyear: 2020.IsoYear) + doAssert initDateTime(dThu, 53, 2020.IsoYear, 00, 00, 00) == initDateTime(31, mDec, 2020, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(01, mJan, 2021, 00, 00, 00)) == (isoweek: 53.IsoWeekRange, isoyear: 2020.IsoYear) + doAssert initDateTime(dFri, 53, 2020.IsoYear, 00, 00, 00) == initDateTime(01, mJan, 2021, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(02, mJan, 2021, 00, 00, 00)) == (isoweek: 53.IsoWeekRange, isoyear: 2020.IsoYear) + doAssert initDateTime(dSat, 53, 2020.IsoYear, 00, 00, 00) == initDateTime(02, mJan, 2021, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(03, mJan, 2021, 00, 00, 00)) == (isoweek: 53.IsoWeekRange, isoyear: 2020.IsoYear) + doAssert initDateTime(dSun, 53, 2020.IsoYear, 00, 00, 00) == initDateTime(03, mJan, 2021, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(04, mJan, 2021, 00, 00, 00)) == (isoweek: 01.IsoWeekRange, isoyear: 2021.IsoYear) + doAssert initDateTime(dMon, 01, 2021.IsoYear, 00, 00, 00) == initDateTime(04, mJan, 2021, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(01, mFeb, 2021, 00, 00, 00)) == (isoweek: 05.IsoWeekRange, isoyear: 2021.IsoYear) + doAssert initDateTime(dMon, 05, 2021.IsoYear, 00, 00, 00) == initDateTime(01, mFeb, 2021, 00, 00, 00) + + doAssert getIsoWeekAndYear(initDateTime(01, mFeb, 2021, 01, 02, 03, 400_000_000, staticTz(hours=1))) == (isoweek: 05.IsoWeekRange, isoyear: 2021.IsoYear) + doAssert initDateTime(dMon, 05, 2021.IsoYear, 01, 02, 03, 400_000_000, staticTz(hours=1)) == initDateTime(01, mFeb, 2021, 01, 02, 03, 400_000_000, staticTz(hours=1)) + + doAssert getIsoWeekAndYear(initDateTime(01, mApr, +0001, 00, 00, 00)) == (isoweek: 13.IsoWeekRange, isoyear: 0001.IsoYear) + doAssert initDateTime(dSun, 13, 0001.IsoYear, 00, 00, 00) == initDateTime(01, mApr, 0001, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(01, mApr, +0000, 00, 00, 00)) == (isoweek: 13.IsoWeekRange, isoyear: 0000.IsoYear) + doAssert initDateTime(dSat, 13, 0000.IsoYear, 00, 00, 00) == initDateTime(01, mApr, 0000, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(01, mApr, -0001, 00, 00, 00)) == (isoweek: 13.IsoWeekRange, isoyear: (-0001).IsoYear) + doAssert initDateTime(dThu, 13, (-0001).IsoYear, 00, 00, 00) == initDateTime(01, mApr, -0001, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(01, mApr, -0002, 00, 00, 00)) == (isoweek: 14.IsoWeekRange, isoyear: (-0002).IsoYear) + doAssert initDateTime(dWed, 14, (-0002).IsoYear, 00, 00, 00) == initDateTime(01, mApr, -0002, 00, 00, 00) + doAssert getIsoWeekAndYear(initDateTime(01, mApr, -0753, 00, 00, 00)) == (isoweek: 14.IsoWeekRange, isoyear: (-0753).IsoYear) + doAssert initDateTime(dMon, 14, (-0753).IsoYear, 00, 00, 00) == initDateTime(01, mApr, -0753, 00, 00, 00) + + block: # getWeeksInIsoYear + doAssert getWeeksInIsoYear((-0014).IsoYear) == 52 + doAssert getWeeksInIsoYear((-0013).IsoYear) == 53 + doAssert getWeeksInIsoYear((-0012).IsoYear) == 52 + + doAssert getWeeksInIsoYear((-0009).IsoYear) == 52 + doAssert getWeeksInIsoYear((-0008).IsoYear) == 53 + doAssert getWeeksInIsoYear((-0007).IsoYear) == 52 + + doAssert getWeeksInIsoYear((-0003).IsoYear) == 52 + doAssert getWeeksInIsoYear((-0002).IsoYear) == 53 + doAssert getWeeksInIsoYear((-0001).IsoYear) == 52 + + doAssert getWeeksInIsoYear(0003.IsoYear) == 52 + doAssert getWeeksInIsoYear(0004.IsoYear) == 53 + doAssert getWeeksInIsoYear(0005.IsoYear) == 52 + + doAssert getWeeksInIsoYear(1653.IsoYear) == 52 + doAssert getWeeksInIsoYear(1654.IsoYear) == 53 + doAssert getWeeksInIsoYear(1655.IsoYear) == 52 + + doAssert getWeeksInIsoYear(1997.IsoYear) == 52 + doAssert getWeeksInIsoYear(1998.IsoYear) == 53 + doAssert getWeeksInIsoYear(1999.IsoYear) == 52 + + doAssert getWeeksInIsoYear(2008.IsoYear) == 52 + doAssert getWeeksInIsoYear(2009.IsoYear) == 53 + doAssert getWeeksInIsoYear(2010.IsoYear) == 52 + + doAssert getWeeksInIsoYear(2014.IsoYear) == 52 + doAssert getWeeksInIsoYear(2015.IsoYear) == 53 + doAssert getWeeksInIsoYear(2016.IsoYear) == 52 From bd4f76a2e3b9282548e62ba3b620b09d6c564373 Mon Sep 17 00:00:00 2001 From: Nan Xiao Date: Mon, 3 Jan 2022 17:30:26 +0800 Subject: [PATCH 2735/3768] docs: Fix typo in tut1.rst (#19309) --- doc/tut1.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/tut1.rst b/doc/tut1.rst index 2856625b7..d8e0ec8e4 100644 --- a/doc/tut1.rst +++ b/doc/tut1.rst @@ -913,7 +913,7 @@ Let's return to the simple counting example: echo i Can a `countup `_ proc be written that -supports this loop? Lets try: +supports this loop? Let's try: .. code-block:: nim proc countup(a, b: int): int = From 19bcb43a0e21dd222ffd3fad8c5fb2d8e85d59ea Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 3 Jan 2022 20:40:43 +0800 Subject: [PATCH 2736/3768] enable multiple packages (arraymancer, fidget ...) (#19311) The cause of arraymancer failure has been tracked here: https://github.com/mratsim/Arraymancer/issues/505 And it was fixed by https://github.com/mratsim/Arraymancer/pull/542 --- testament/important_packages.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 7e15e7c65..381aa27ac 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -35,7 +35,7 @@ proc pkg(name: string; cmd = "nimble test"; url = "", useHead = true, allowFailu pkg "alea", allowFailure = true pkg "argparse" -pkg "arraymancer", "nim c tests/tests_cpu.nim", allowFailure = true +pkg "arraymancer", "nim c tests/tests_cpu.nim" pkg "ast_pattern_matching", "nim c -r --oldgensym:on tests/test1.nim", allowFailure = true pkg "asyncthreadpool" pkg "awk" @@ -63,7 +63,7 @@ pkg "delaunay" pkg "docopt" pkg "easygl", "nim c -o:egl -r src/easygl.nim", "https://github.com/jackmott/easygl" pkg "elvis" -pkg "fidget", allowFailure = true +pkg "fidget" pkg "fragments", "nim c -r fragments/dsl.nim" pkg "fusion" pkg "gara" @@ -91,7 +91,7 @@ pkg "memo" pkg "msgpack4nim", "nim c -r tests/test_spec.nim" pkg "nake", "nim c nakefile.nim" pkg "neo", "nim c -d:blas=openblas tests/all.nim" -pkg "nesm", "nimble tests", allowFailure = true # notice plural 'tests' +pkg "nesm", "nimble tests" # notice plural 'tests' pkg "netty" pkg "nico", allowFailure = true pkg "nicy", "nim c -r src/nicy.nim" From 526a32e1697489b73e54c1b984ba5413842f1252 Mon Sep 17 00:00:00 2001 From: rockcavera Date: Mon, 3 Jan 2022 16:14:08 -0300 Subject: [PATCH 2737/3768] Fix #19314 - fixing broken `DoublyLinkedList` after adding empty `DoublyLinkedList` (#19315) [backport] * Update lists.nim * Update tlists.nim --- lib/pure/collections/lists.nim | 12 ++++++------ tests/stdlib/tlists.nim | 12 ++++++++++++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/lib/pure/collections/lists.nim b/lib/pure/collections/lists.nim index de5550d6e..f7627ae67 100644 --- a/lib/pure/collections/lists.nim +++ b/lib/pure/collections/lists.nim @@ -675,12 +675,12 @@ proc addMoved*[T](a, b: var DoublyLinkedList[T]) {.since: (1, 5, 1).} = assert s == [0, 1, 0, 1, 0, 1] if b.head != nil: - b.head.prev = a.tail - if a.tail != nil: - a.tail.next = b.head - a.tail = b.tail - if a.head == nil: - a.head = b.head + if a.head == nil: + a.head = b.head + else: + b.head.prev = a.tail + a.tail.next = b.head + a.tail = b.tail if a.addr != b.addr: b.head = nil b.tail = nil diff --git a/tests/stdlib/tlists.nim b/tests/stdlib/tlists.nim index 2f6d5ee6a..14cbf2f9d 100644 --- a/tests/stdlib/tlists.nim +++ b/tests/stdlib/tlists.nim @@ -245,6 +245,18 @@ template main = doAssert a.toSeq == @[1] a.add(2) doAssert a.toSeq == @[1, 2] + + block issue19314: # add (appends a shallow copy) + var a: DoublyLinkedList[int] + var b: DoublyLinkedList[int] + + doAssert a.toSeq == @[] + a.add(1) + doAssert a.toSeq == @[1] + a.add(b) + doAssert a.toSeq == @[1] + a.add(2) + doAssert a.toSeq == @[1, 2] static: main() main() From fcf1df6e56934b1b5021a0898b9152b86e0cb5f7 Mon Sep 17 00:00:00 2001 From: Smarcy Date: Mon, 3 Jan 2022 21:04:15 +0100 Subject: [PATCH 2738/3768] fixed typos (#19316) --- nimpretty/nimpretty.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nimpretty/nimpretty.nim b/nimpretty/nimpretty.nim index a7940349d..e3b4c9a3a 100644 --- a/nimpretty/nimpretty.nim +++ b/nimpretty/nimpretty.nim @@ -69,8 +69,8 @@ proc main = var backup = false # when `on`, create a backup file of input in case - # `prettyPrint` could over-write it (note that the backup may happen even - # if input is not actually over-written, when nimpretty is a noop). + # `prettyPrint` could overwrite it (note that the backup may happen even + # if input is not actually overwritten, when nimpretty is a noop). # --backup was un-documented (rely on git instead). var opt = PrettyOptions(indWidth: 0, maxLineLen: 80) From 35cae73aa5c39b950386febfa5182bab920ddfbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C8=98tefan=20Talpalaru?= Date: Tue, 4 Jan 2022 07:56:10 +0100 Subject: [PATCH 2739/3768] devel: style fix (#19318) this allows "--styleCheck:usages --styleCheck:error" --- lib/pure/nativesockets.nim | 2 +- lib/pure/net.nim | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim index 4037d7181..eb0514a8f 100644 --- a/lib/pure/nativesockets.nim +++ b/lib/pure/nativesockets.nim @@ -810,7 +810,7 @@ proc accept*(fd: SocketHandle, inheritable = defined(nimInheritHandles)): (Socke ## child processes. ## ## Returns (osInvalidSocket, "") if an error occurred. - var sockAddress: Sockaddr + var sockAddress: SockAddr var addrLen = sizeof(sockAddress).SockLen var sock = when (defined(linux) or defined(bsd)) and not defined(nimdoc): diff --git a/lib/pure/net.nim b/lib/pure/net.nim index ced6b2fb2..8dc92e9d6 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -1778,7 +1778,7 @@ proc sendTo*(socket: Socket, address: IpAddress, port: Port, assert(not socket.isClosed, "Cannot `sendTo` on a closed socket") var sa: Sockaddr_storage - var sl: Socklen + var sl: SockLen toSockAddr(address, port, sa, sl) result = sendto(socket.fd, cstring(data), data.len().cint, flags.cint, cast[ptr SockAddr](addr sa), sl) From 39a27783fc0d43e9828bb031cc811042226407b9 Mon Sep 17 00:00:00 2001 From: Nan Xiao Date: Tue, 4 Jan 2022 17:42:21 +0800 Subject: [PATCH 2740/3768] docs: Fix typo in tut1.rst (#19324) --- doc/tut1.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/tut1.rst b/doc/tut1.rst index d8e0ec8e4..3107aef45 100644 --- a/doc/tut1.rst +++ b/doc/tut1.rst @@ -1052,7 +1052,7 @@ errors. Lossless `Automatic type conversion`:idx: is performed in expressions where different kinds of integer types are used. However, if the type conversion -would cause loss of information, the ``RangeDefect``:idx: is raised (if the error +would cause loss of information, the `RangeDefect`:idx: is raised (if the error cannot be detected at compile time). From 0bcd7062c6e7a4131d166dacea41d661b7a09748 Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 4 Jan 2022 18:10:46 +0800 Subject: [PATCH 2741/3768] correct the comments (#19322) --expandArc ``` var a b a = matrix(5, 5, 1.0) b = matrix(5, 5, 2.0) `=sink`(b, - let blitTmp = b wasMoved(b) blitTmp + a) `=destroy`(b) `=destroy`(a) ``` --- compiler/optimizer.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/optimizer.nim b/compiler/optimizer.nim index 744c82ab5..f484fdbf5 100644 --- a/compiler/optimizer.nim +++ b/compiler/optimizer.nim @@ -127,11 +127,11 @@ proc analyse(c: var Con; b: var BasicBlock; n: PNode) = for i in 0 ..< n.len: analyse(c, b, n[i]) else: - #[ Test tmatrix.test3: + #[ Test destructor/tmatrix.test3: Prevent this from being elided. We should probably find a better solution... - `=sink`(b, - ( + `=sink`(b, - let blitTmp = b; wasMoved(b); blitTmp + a) From 1869826668e2e3a0fd69cc1b69fb12b07993e417 Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 4 Jan 2022 18:49:54 +0800 Subject: [PATCH 2742/3768] add std/private/win_getsysteminfo; refactor the usage of `GetSystemInfo` (#19310) * add std/private/win_getsysteminfo * import at the top level * wrappers follow nep1 too * follow review comment --- lib/pure/concurrency/cpuinfo.nim | 24 ++++++------------------ lib/pure/reservedmem.nim | 19 ++----------------- lib/std/private/win_getsysteminfo.nim | 15 +++++++++++++++ 3 files changed, 23 insertions(+), 35 deletions(-) create mode 100644 lib/std/private/win_getsysteminfo.nim diff --git a/lib/pure/concurrency/cpuinfo.nim b/lib/pure/concurrency/cpuinfo.nim index ee43b8e11..16d32002d 100644 --- a/lib/pure/concurrency/cpuinfo.nim +++ b/lib/pure/concurrency/cpuinfo.nim @@ -18,6 +18,9 @@ include "system/inclrtl" when defined(posix) and not (defined(macosx) or defined(bsd)): import posix +when defined(windows): + import std/private/win_getsysteminfo + when defined(freebsd) or defined(macosx): {.emit: "#include ".} @@ -54,25 +57,10 @@ proc countProcessors*(): int {.rtl, extern: "ncpi$1".} = ## Returns the number of the processors/cores the machine has. ## Returns 0 if it cannot be detected. when defined(windows): - type - SYSTEM_INFO {.final, pure.} = object - u1: int32 - dwPageSize: int32 - lpMinimumApplicationAddress: pointer - lpMaximumApplicationAddress: pointer - dwActiveProcessorMask: ptr int32 - dwNumberOfProcessors: int32 - dwProcessorType: int32 - dwAllocationGranularity: int32 - wProcessorLevel: int16 - wProcessorRevision: int16 - - proc GetSystemInfo(lpSystemInfo: var SYSTEM_INFO) {.stdcall, dynlib: "kernel32", importc: "GetSystemInfo".} - var - si: SYSTEM_INFO - GetSystemInfo(si) - result = si.dwNumberOfProcessors + si: SystemInfo + getSystemInfo(addr si) + result = int(si.dwNumberOfProcessors) elif defined(macosx) or defined(bsd): var mib: array[0..3, cint] diff --git a/lib/pure/reservedmem.nim b/lib/pure/reservedmem.nim index 232a2b383..99fbe1429 100644 --- a/lib/pure/reservedmem.nim +++ b/lib/pure/reservedmem.nim @@ -42,25 +42,10 @@ type when defined(windows): import winlean - - type - SYSTEM_INFO {.final, pure.} = object - u1: uint32 - dwPageSize: uint32 - lpMinimumApplicationAddress: pointer - lpMaximumApplicationAddress: pointer - dwActiveProcessorMask: ptr uint32 - dwNumberOfProcessors: uint32 - dwProcessorType: uint32 - dwAllocationGranularity: uint32 - wProcessorLevel: uint16 - wProcessorRevision: uint16 - - proc getSystemInfo(lpSystemInfo: ptr SYSTEM_INFO) {.stdcall, - dynlib: "kernel32", importc: "GetSystemInfo".} + import std/private/win_getsysteminfo proc getAllocationGranularity: uint = - var sysInfo: SYSTEM_INFO + var sysInfo: SystemInfo getSystemInfo(addr sysInfo) return uint(sysInfo.dwAllocationGranularity) diff --git a/lib/std/private/win_getsysteminfo.nim b/lib/std/private/win_getsysteminfo.nim new file mode 100644 index 000000000..b98478231 --- /dev/null +++ b/lib/std/private/win_getsysteminfo.nim @@ -0,0 +1,15 @@ +type + SystemInfo* = object + u1: uint32 + dwPageSize: uint32 + lpMinimumApplicationAddress: pointer + lpMaximumApplicationAddress: pointer + dwActiveProcessorMask: ptr uint32 + dwNumberOfProcessors*: uint32 + dwProcessorType: uint32 + dwAllocationGranularity*: uint32 + wProcessorLevel: uint16 + wProcessorRevision: uint16 + +proc getSystemInfo*(lpSystemInfo: ptr SystemInfo) {.stdcall, + dynlib: "kernel32", importc: "GetSystemInfo".} From 9df195ef581879cf9e5c3c3154755bd1dba677c6 Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 4 Jan 2022 20:29:50 +0800 Subject: [PATCH 2743/3768] style usages part one (openarray => openArray) (#19321) * style usages (openArray) * revert doc changes --- tests/arc/tmovebug.nim | 4 ++-- tests/arc/tmovebugcopy.nim | 4 ++-- tests/array/tarray.nim | 2 +- tests/async/tioselectors.nim | 4 ++-- tests/closure/tclosure.nim | 4 ++-- tests/compiles/trecursive_generic_in_compiles.nim | 2 +- tests/concepts/tconcepts_issues.nim | 8 ++++---- tests/converter/tconverter_with_varargs.nim | 2 +- tests/cpp/tcovariancerules.nim | 8 ++++---- tests/destructor/tmove_objconstr.nim | 2 +- tests/errmsgs/t17460.nim | 2 +- tests/errmsgs/tproper_stacktrace.nim | 2 +- tests/errmsgs/tsigmatch.nim | 4 ++-- tests/generics/tgenerics_issues.nim | 2 +- tests/generics/tgenerics_various.nim | 4 ++-- tests/iter/titer13.nim | 2 +- tests/iter/titer_issues.nim | 4 ++-- tests/iter/tmoditer.nim | 2 +- tests/iter/tpermutations.nim | 2 +- tests/js/t9410.nim | 4 ++-- tests/js/tbasics.nim | 2 +- tests/js/tbyvar.nim | 4 ++-- tests/lent/tbasic_lent_check.nim | 2 +- tests/magics/tmagics.nim | 2 +- tests/manyloc/named_argument_bug/tri_engine/gfx/gl/gl.nim | 2 +- .../named_argument_bug/tri_engine/gfx/gl/primitive.nim | 2 +- tests/misc/t9039.nim | 2 +- tests/misc/theaproots.nim | 2 +- tests/misc/tsimplesort.nim | 2 +- tests/openarray/topena1.nim | 2 +- tests/openarray/topenarrayrepr.nim | 4 ++-- tests/openarray/topenlen.nim | 2 +- tests/openarray/tptrarrayderef.nim | 6 +++--- tests/overload/toverload_various.nim | 4 ++-- tests/showoff/tdrdobbs_examples.nim | 4 ++-- tests/stdlib/tdochelpers.nim | 4 ++-- tests/stdlib/tparseipv6.nim | 4 ++-- tests/stdlib/trepr.nim | 2 +- tests/system/tostring.nim | 2 +- tests/system/tsystem_misc.nim | 4 ++-- tests/template/otests.nim | 4 ++-- tests/template/sunset.nimf | 2 +- tests/template/template_issues.nim | 2 +- tests/tuples/tuple_with_nil.nim | 4 ++-- tests/typerel/tcommontype.nim | 2 +- tests/typerel/texplicitcmp.nim | 2 +- tests/typerel/tstr_as_openarray.nim | 6 +++--- tests/views/tviews1.nim | 2 +- tests/vm/tyaytypedesc.nim | 2 +- 49 files changed, 76 insertions(+), 76 deletions(-) diff --git a/tests/arc/tmovebug.nim b/tests/arc/tmovebug.nim index 7977d330a..002bd6796 100644 --- a/tests/arc/tmovebug.nim +++ b/tests/arc/tmovebug.nim @@ -292,7 +292,7 @@ when false: # bug #13456 -iterator combinations[T](s: openarray[T], k: int): seq[T] = +iterator combinations[T](s: openArray[T], k: int): seq[T] = let n = len(s) assert k >= 0 and k <= n var pos = newSeq[int](k) @@ -455,7 +455,7 @@ initFoo7(2) # bug #14902 -iterator zip[T](s: openarray[T]): (T, T) = +iterator zip[T](s: openArray[T]): (T, T) = var i = 0 while i < 10: yield (s[i mod 2], s[i mod 2 + 1]) diff --git a/tests/arc/tmovebugcopy.nim b/tests/arc/tmovebugcopy.nim index 7c5228147..ec4315777 100644 --- a/tests/arc/tmovebugcopy.nim +++ b/tests/arc/tmovebugcopy.nim @@ -253,7 +253,7 @@ when false: # bug #13456 -iterator combinations[T](s: openarray[T], k: int): seq[T] = +iterator combinations[T](s: openArray[T], k: int): seq[T] = let n = len(s) assert k >= 0 and k <= n var pos = newSeq[int](k) @@ -416,7 +416,7 @@ initFoo7(2) # bug #14902 -iterator zip[T](s: openarray[T]): (T, T) = +iterator zip[T](s: openArray[T]): (T, T) = var i = 0 while i < 10: yield (s[i mod 2], s[i mod 2 + 1]) diff --git a/tests/array/tarray.nim b/tests/array/tarray.nim index d10011ef2..e9f330e3b 100644 --- a/tests/array/tarray.nim +++ b/tests/array/tarray.nim @@ -44,7 +44,7 @@ block tarray: arr: TMyarray - proc sum(a: openarray[int]): int = + proc sum(a: openArray[int]): int = result = 0 var i = 0 while i < len(a): diff --git a/tests/async/tioselectors.nim b/tests/async/tioselectors.nim index f7487525a..fdb8d9597 100644 --- a/tests/async/tioselectors.nim +++ b/tests/async/tioselectors.nim @@ -288,8 +288,8 @@ when not defined(windows): events: set[Event] proc vnode_test(): bool = - proc validate(test: openarray[ReadyKey], - check: openarray[valType]): bool = + proc validate(test: openArray[ReadyKey], + check: openArray[valType]): bool = result = false if len(test) == len(check): for checkItem in check: diff --git a/tests/closure/tclosure.nim b/tests/closure/tclosure.nim index 546d7026d..8ae6c44bb 100644 --- a/tests/closure/tclosure.nim +++ b/tests/closure/tclosure.nim @@ -38,10 +38,10 @@ joinable: false block tclosure: - proc map(n: var openarray[int], fn: proc (x: int): int {.closure}) = + proc map(n: var openArray[int], fn: proc (x: int): int {.closure}) = for i in 0..n.len-1: n[i] = fn(n[i]) - proc each(n: openarray[int], fn: proc(x: int) {.closure.}) = + proc each(n: openArray[int], fn: proc(x: int) {.closure.}) = for i in 0..n.len-1: fn(n[i]) diff --git a/tests/compiles/trecursive_generic_in_compiles.nim b/tests/compiles/trecursive_generic_in_compiles.nim index 88d3faeed..ab31ad0f5 100644 --- a/tests/compiles/trecursive_generic_in_compiles.nim +++ b/tests/compiles/trecursive_generic_in_compiles.nim @@ -50,7 +50,7 @@ proc `==`*[T](xs, ys: List[T]): bool = proc asList*[T](xs: varargs[T]): List[T] = ## Creates list from varargs - proc initListImpl(i: int, xs: openarray[T]): List[T] = + proc initListImpl(i: int, xs: openArray[T]): List[T] = if i > high(xs): Nil[T]() else: diff --git a/tests/concepts/tconcepts_issues.nim b/tests/concepts/tconcepts_issues.nim index c76049bdd..1a0dd8063 100644 --- a/tests/concepts/tconcepts_issues.nim +++ b/tests/concepts/tconcepts_issues.nim @@ -324,15 +324,15 @@ block t6691: block t6782: type Reader = concept c - c.read(openarray[byte], int, int) is int + c.read(openArray[byte], int, int) is int Rdr = concept c - c.rd(openarray[byte], int, int) is int + c.rd(openArray[byte], int, int) is int type TestFile = object - proc read(r: TestFile, dest: openarray[byte], offset: int, limit: int): int = + proc read(r: TestFile, dest: openArray[byte], offset: int, limit: int): int = result = 0 - proc rd(r: TestFile, dest: openarray[byte], offset: int, limit: int): int = + proc rd(r: TestFile, dest: openArray[byte], offset: int, limit: int): int = result = 0 doAssert TestFile is Reader diff --git a/tests/converter/tconverter_with_varargs.nim b/tests/converter/tconverter_with_varargs.nim index 6d7e31e85..fae83221b 100644 --- a/tests/converter/tconverter_with_varargs.nim +++ b/tests/converter/tconverter_with_varargs.nim @@ -8,7 +8,7 @@ type converter to_py*(i: int) : PPyRef = nil when false: - proc to_tuple*(vals: openarray[PPyRef]): PPyRef = + proc to_tuple*(vals: openArray[PPyRef]): PPyRef = discard proc abc(args: varargs[PPyRef]) = diff --git a/tests/cpp/tcovariancerules.nim b/tests/cpp/tcovariancerules.nim index 5ac7d8bac..9d49f2cbd 100644 --- a/tests/cpp/tcovariancerules.nim +++ b/tests/cpp/tcovariancerules.nim @@ -79,16 +79,16 @@ proc wantsCovariantSeq2(s: seq[AnimalRef]) = proc wantsCovariantSeq3(s: seq[RefAlias[Animal]]) = for a in s: echo a.x -proc wantsCovariantOpenArray(s: openarray[ref Animal]) = +proc wantsCovariantOpenArray(s: openArray[ref Animal]) = for a in s: echo a.x -proc modifiesCovariantOpenArray(s: var openarray[ref Animal]) = +proc modifiesCovariantOpenArray(s: var openArray[ref Animal]) = for a in s: echo a.x -proc modifiesDerivedOpenArray(s: var openarray[ref Dog]) = +proc modifiesDerivedOpenArray(s: var openArray[ref Dog]) = for a in s: echo a.x -proc wantsNonCovariantOpenArray(s: openarray[Animal]) = +proc wantsNonCovariantOpenArray(s: openArray[Animal]) = for a in s: echo a.x proc wantsCovariantArray(s: array[2, ref Animal]) = diff --git a/tests/destructor/tmove_objconstr.nim b/tests/destructor/tmove_objconstr.nim index 5faaabb8b..9eee4bfdb 100644 --- a/tests/destructor/tmove_objconstr.nim +++ b/tests/destructor/tmove_objconstr.nim @@ -178,7 +178,7 @@ proc myfuncLoop(x: int): MySeqNonCopyable = discard myfuncLoop(3) #------------------------------------------------------------ -# Move into table via openarray +# Move into table via openArray #------------------------------------------------------------ type diff --git a/tests/errmsgs/t17460.nim b/tests/errmsgs/t17460.nim index 8192cc4c8..e377bc48a 100644 --- a/tests/errmsgs/t17460.nim +++ b/tests/errmsgs/t17460.nim @@ -3,7 +3,7 @@ discard """ errormsg: "wrong number of variables" """ -iterator xclusters*[T](a: openarray[T]; s: static[int]): array[s, T] {.inline.} = +iterator xclusters*[T](a: openArray[T]; s: static[int]): array[s, T] {.inline.} = var result: array[s, T] # iterators have no default result variable var i = 0 while i < len(a): diff --git a/tests/errmsgs/tproper_stacktrace.nim b/tests/errmsgs/tproper_stacktrace.nim index c7dfbaf2a..8617984fb 100644 --- a/tests/errmsgs/tproper_stacktrace.nim +++ b/tests/errmsgs/tproper_stacktrace.nim @@ -6,7 +6,7 @@ import strscans, strutils proc raiseTestException*() = raise newException(Exception, "test") -proc matchStackTrace(actualEntries: openarray[StackTraceEntry], expected: string) = +proc matchStackTrace(actualEntries: openArray[StackTraceEntry], expected: string) = var expectedEntries = newSeq[StackTraceEntry]() var i = 0 diff --git a/tests/errmsgs/tsigmatch.nim b/tests/errmsgs/tsigmatch.nim index 023b7d518..8f32ef9e6 100644 --- a/tests/errmsgs/tsigmatch.nim +++ b/tests/errmsgs/tsigmatch.nim @@ -132,12 +132,12 @@ block: echo foo(fun) block: - # bug #10285 Function signature don't match when inside seq/array/openarray + # bug #10285 Function signature don't match when inside seq/array/openArray # Note: the error message now shows `closure` which helps debugging the issue # out why it doesn't match proc takesFunc(f: proc (x: int) {.gcsafe, locks: 0.}) = echo "takes single Func" - proc takesFuncs(fs: openarray[proc (x: int) {.gcsafe, locks: 0.}]) = + proc takesFuncs(fs: openArray[proc (x: int) {.gcsafe, locks: 0.}]) = echo "takes multiple Func" takesFunc(proc (x: int) {.gcsafe, locks: 0.} = echo x) # works takesFuncs([proc (x: int) {.gcsafe, locks: 0.} = echo x]) # fails diff --git a/tests/generics/tgenerics_issues.nim b/tests/generics/tgenerics_issues.nim index 9e70efcd2..092926c01 100644 --- a/tests/generics/tgenerics_issues.nim +++ b/tests/generics/tgenerics_issues.nim @@ -603,7 +603,7 @@ block t7854: block t5864: - proc defaultStatic(s: openarray, N: static[int] = 1): int = N + proc defaultStatic(s: openArray, N: static[int] = 1): int = N proc defaultGeneric[T](a: T = 2): int = a let a = [1, 2, 3, 4].defaultStatic() diff --git a/tests/generics/tgenerics_various.nim b/tests/generics/tgenerics_various.nim index 285108cd3..6c76502e1 100644 --- a/tests/generics/tgenerics_various.nim +++ b/tests/generics/tgenerics_various.nim @@ -230,8 +230,8 @@ block tvarargs_vs_generics: echo "direct" proc withDirectType[T](arg: T) = echo "generic" - proc withOpenArray(args: openarray[string]) = - echo "openarray" + proc withOpenArray(args: openArray[string]) = + echo "openArray" proc withOpenArray[T](arg: T) = echo "generic" proc withVarargs(args: varargs[string]) = diff --git a/tests/iter/titer13.nim b/tests/iter/titer13.nim index 0d4a399c5..086c40ca4 100644 --- a/tests/iter/titer13.nim +++ b/tests/iter/titer13.nim @@ -72,7 +72,7 @@ block: echo a block t5859: - proc flatIterator[T](s: openarray[T]): auto {.noSideEffect.}= + proc flatIterator[T](s: openArray[T]): auto {.noSideEffect.}= result = iterator(): auto = when (T is not seq|array): for item in s: diff --git a/tests/iter/titer_issues.nim b/tests/iter/titer_issues.nim index 4a76b2fb0..65f66ad26 100644 --- a/tests/iter/titer_issues.nim +++ b/tests/iter/titer_issues.nim @@ -135,7 +135,7 @@ block t3837_chained: block t3221_complex: - iterator permutations[T](ys: openarray[T]): seq[T] = + iterator permutations[T](ys: openArray[T]): seq[T] = var d = 1 c = newSeq[int](ys.len) @@ -228,7 +228,7 @@ block t2023_objiter: block: # bug #13739 - iterator myIter(arg: openarray[int]): int = + iterator myIter(arg: openArray[int]): int = var tmp = 0 let len = arg.len while tmp < len: diff --git a/tests/iter/tmoditer.nim b/tests/iter/tmoditer.nim index b92a416fb..99e5b642d 100644 --- a/tests/iter/tmoditer.nim +++ b/tests/iter/tmoditer.nim @@ -43,7 +43,7 @@ proc `=copy`(dst: var NonCopyable, src: NonCopyable) {.error.} proc `=sink`(dst: var NonCopyable, src: NonCopyable) = dst.x = src.x -iterator lentItems[T](a: openarray[T]): lent T = +iterator lentItems[T](a: openArray[T]): lent T = for i in 0..a.high: yield a[i] diff --git a/tests/iter/tpermutations.nim b/tests/iter/tpermutations.nim index c5067ba31..30a66460f 100644 --- a/tests/iter/tpermutations.nim +++ b/tests/iter/tpermutations.nim @@ -12,7 +12,7 @@ perm: 6778800.0 det: 0.0 import sequtils, sugar -iterator permutations*[T](ys: openarray[T]): tuple[perm: seq[T], sign: int] = +iterator permutations*[T](ys: openArray[T]): tuple[perm: seq[T], sign: int] = var d = 1 c = newSeq[int](ys.len) diff --git a/tests/js/t9410.nim b/tests/js/t9410.nim index 78c329a24..042520dc5 100644 --- a/tests/js/t9410.nim +++ b/tests/js/t9410.nim @@ -447,10 +447,10 @@ template tests = doAssert seqOfSeqs == @[@[10, 2], @[30, 4]] when false: - block: # openarray + block: # openArray # Error: internal error: genAddr: nkStmtListExpr var calls = 0 - proc getvarint(x: var openarray[int]): var int = + proc getvarint(x: var openArray[int]): var int = calls += 1 if true: x[1] diff --git a/tests/js/tbasics.nim b/tests/js/tbasics.nim index b297bb037..ef84f8bb5 100644 --- a/tests/js/tbasics.nim +++ b/tests/js/tbasics.nim @@ -54,7 +54,7 @@ for x in someGlobal: doAssert(x == 0) proc tdefault = var x = default(int) doAssert(x == 0) - proc inner(v: openarray[string]) = + proc inner(v: openArray[string]) = doAssert(v.len == 0) inner(default(seq[string])) diff --git a/tests/js/tbyvar.nim b/tests/js/tbyvar.nim index a4c60b0b3..93724a2f1 100644 --- a/tests/js/tbyvar.nim +++ b/tests/js/tbyvar.nim @@ -39,9 +39,9 @@ proc main = main() -# Test: pass var seq to var openarray +# Test: pass var seq to var openArray var s = @[2, 1] -proc foo(a: var openarray[int]) = a[0] = 123 +proc foo(a: var openArray[int]) = a[0] = 123 proc bar(s: var seq[int], a: int) = doAssert(a == 5) diff --git a/tests/lent/tbasic_lent_check.nim b/tests/lent/tbasic_lent_check.nim index 049fa276a..e33da06f8 100644 --- a/tests/lent/tbasic_lent_check.nim +++ b/tests/lent/tbasic_lent_check.nim @@ -39,7 +39,7 @@ template main2 = # bug #15958 p[] = 20.0 doAssert byLent(p)[] == 20.0 - proc byLent2[T](a: openarray[T]): lent T = a[0] + proc byLent2[T](a: openArray[T]): lent T = a[0] doAssert byLent2(a) == 11 doAssert byLent2(a).unsafeAddr == a[0].unsafeAddr doAssert byLent2(b) == 21 diff --git a/tests/magics/tmagics.nim b/tests/magics/tmagics.nim index 70926a0d7..0e412940c 100644 --- a/tests/magics/tmagics.nim +++ b/tests/magics/tmagics.nim @@ -18,7 +18,7 @@ block tlowhigh: for i in low(a) .. high(a): a[i] = 0 - proc sum(a: openarray[int]): int = + proc sum(a: openArray[int]): int = result = 0 for i in low(a)..high(a): inc(result, a[i]) diff --git a/tests/manyloc/named_argument_bug/tri_engine/gfx/gl/gl.nim b/tests/manyloc/named_argument_bug/tri_engine/gfx/gl/gl.nim index 7d787c07b..29e23f9d0 100644 --- a/tests/manyloc/named_argument_bug/tri_engine/gfx/gl/gl.nim +++ b/tests/manyloc/named_argument_bug/tri_engine/gfx/gl/gl.nim @@ -53,7 +53,7 @@ else: const glRealType* = cGLfloat -proc setUniformV4*[T](loc: GLint, vecs: var openarray[TV4[T]]) = +proc setUniformV4*[T](loc: GLint, vecs: var openArray[TV4[T]]) = glUniform4fv(loc, vecs.len.GLsizei, cast[ptr GLfloat](vecs[0].addr)) proc setUniformV4*[T](loc: GLint, vec: TV4[T]) = diff --git a/tests/manyloc/named_argument_bug/tri_engine/gfx/gl/primitive.nim b/tests/manyloc/named_argument_bug/tri_engine/gfx/gl/primitive.nim index 3bfaf1cbc..accc2d96b 100644 --- a/tests/manyloc/named_argument_bug/tri_engine/gfx/gl/primitive.nim +++ b/tests/manyloc/named_argument_bug/tri_engine/gfx/gl/primitive.nim @@ -47,7 +47,7 @@ proc newVert*(rect: rect.TRect): seq[TVert] = proc newVertAttrib(i: GLuint, size: GLint, stride: GLsizei, offset: GLvoid): TVertAttrib = TVertAttrib(i: i, size: size, stride: stride, offset: offset) -proc genBuf*[T](vboTarget, objUsage: GLenum, data: var openarray[T]): GLuint = +proc genBuf*[T](vboTarget, objUsage: GLenum, data: var openArray[T]): GLuint = result = 0.GLuint ?glGenBuffers(1, result.addr) ?glBindBuffer(vboTarget, result) diff --git a/tests/misc/t9039.nim b/tests/misc/t9039.nim index ba636d1db..3271cd34e 100644 --- a/tests/misc/t9039.nim +++ b/tests/misc/t9039.nim @@ -15,7 +15,7 @@ but expression 'nesting + 1' is of type: int # line 15 func default(T: typedesc[array]): T = discard doAssert default(array[3, int]) == [0, 0, 0] -func shapeBad*[T: not char](s: openarray[T], rank: static[int], nesting = 0, parent_shape = default(array[rank, int])): array[rank, int] = +func shapeBad*[T: not char](s: openArray[T], rank: static[int], nesting = 0, parent_shape = default(array[rank, int])): array[rank, int] = result = parent_shape result[nesting] = s.len when (T is seq|array): diff --git a/tests/misc/theaproots.nim b/tests/misc/theaproots.nim index 1ea3c86b9..2dd345254 100644 --- a/tests/misc/theaproots.nim +++ b/tests/misc/theaproots.nim @@ -28,7 +28,7 @@ proc acc(x: var Foo): var ref Bar = proc test(maybeFoo: var Foo, maybeSeq: var seq[ref Bar], - bars: var openarray[ref Bar], + bars: var openArray[ref Bar], maybeTup: var Tup) = var bb: ref Bar maybeFoo.rmaybe = bb diff --git a/tests/misc/tsimplesort.nim b/tests/misc/tsimplesort.nim index e4a8e0b37..4793c63df 100644 --- a/tests/misc/tsimplesort.nim +++ b/tests/misc/tsimplesort.nim @@ -112,7 +112,7 @@ proc initTable*[A, B](initialSize=64): TTable[A, B] = result.counter = 0 newSeq(result.data, initialSize) -proc toTable*[A, B](pairs: openarray[tuple[key: A, +proc toTable*[A, B](pairs: openArray[tuple[key: A, val: B]]): TTable[A, B] = ## creates a new hash table that contains the given `pairs`. result = initTable[A, B](nextPowerOfTwo(pairs.len+10)) diff --git a/tests/openarray/topena1.nim b/tests/openarray/topena1.nim index ed3a0cedb..380c57f2a 100644 --- a/tests/openarray/topena1.nim +++ b/tests/openarray/topena1.nim @@ -6,4 +6,4 @@ discard """ # Tests a special bug var - x: ref openarray[string] #ERROR_MSG invalid type + x: ref openArray[string] #ERROR_MSG invalid type diff --git a/tests/openarray/topenarrayrepr.nim b/tests/openarray/topenarrayrepr.nim index 3784d4bbb..fc40d88c3 100644 --- a/tests/openarray/topenarrayrepr.nim +++ b/tests/openarray/topenarrayrepr.nim @@ -2,12 +2,12 @@ discard """ output: "5 - [1]" """ type - TProc = proc (n: int, m: openarray[int64]) {.nimcall.} + TProc = proc (n: int, m: openArray[int64]) {.nimcall.} proc Foo(x: int, P: TProc) = P(x, [ 1'i64 ]) -proc Bar(n: int, m: openarray[int64]) = +proc Bar(n: int, m: openArray[int64]) = echo($n & " - " & repr(m)) Foo(5, Bar) #OUT 5 - [1] diff --git a/tests/openarray/topenlen.nim b/tests/openarray/topenlen.nim index 83d58ac5c..164241b85 100644 --- a/tests/openarray/topenlen.nim +++ b/tests/openarray/topenlen.nim @@ -5,7 +5,7 @@ discard """ proc choose(b: openArray[string]): string = return b[0] -proc p(a, b: openarray[string]): int = +proc p(a, b: openArray[string]): int = result = a.len + b.len - 1 for j in 0 .. a.len: inc(result) discard choose(a) diff --git a/tests/openarray/tptrarrayderef.nim b/tests/openarray/tptrarrayderef.nim index 5e77430d1..1b7ef0df0 100644 --- a/tests/openarray/tptrarrayderef.nim +++ b/tests/openarray/tptrarrayderef.nim @@ -15,14 +15,14 @@ var raa = [11,12,13] #bug #3586 -proc mutate[T](arr:openarray[T], brr: openArray[T]) = +proc mutate[T](arr:openArray[T], brr: openArray[T]) = for i in 0..arr.len-1: doAssert(arr[i] == brr[i]) mutate(arr, arr) #bug #2240 -proc f(a: openarray[int], b: openArray[int]) = +proc f(a: openArray[int], b: openArray[int]) = for i in 0..a.len-1: doAssert(a[i] == b[i]) @@ -37,7 +37,7 @@ ra[2] = 13 f(ra[], raa) #bug #2240b -proc fillBuffer(buf: var openarray[char]) = +proc fillBuffer(buf: var openArray[char]) = for i in 0..buf.len-1: buf[i] = chr(i) diff --git a/tests/overload/toverload_various.nim b/tests/overload/toverload_various.nim index 55a8d17e6..0741fce60 100644 --- a/tests/overload/toverload_various.nim +++ b/tests/overload/toverload_various.nim @@ -264,8 +264,8 @@ proc init*[T](hmctx: HMAC[T], key: ptr byte, ulen: uint) = const sizeBlock = hmctx.sizeBlock echo sizeBlock -proc hmac*[A, B](HashType: typedesc, key: openarray[A], - data: openarray[B]) = +proc hmac*[A, B](HashType: typedesc, key: openArray[A], + data: openArray[B]) = var ctx: HMAC[HashType] ctx.init(nil, 0) diff --git a/tests/showoff/tdrdobbs_examples.nim b/tests/showoff/tdrdobbs_examples.nim index 0e10c6dd8..c61e177dc 100644 --- a/tests/showoff/tdrdobbs_examples.nim +++ b/tests/showoff/tdrdobbs_examples.nim @@ -40,12 +40,12 @@ const msb3999 = mostSignificantBit(3999) echo msb3999, " ", mostSignificantBit(0), " ", square(44) -proc filter[T](a: openarray[T], predicate: proc (x: T): bool): seq[T] = +proc filter[T](a: openArray[T], predicate: proc (x: T): bool): seq[T] = result = @[] # @[] constructs the empty seq for x in a: if predicate(x): result.add(x) -proc map[T, S](a: openarray[T], fn: proc (x: T): S): seq[S] = +proc map[T, S](a: openArray[T], fn: proc (x: T): S): seq[S] = newSeq(result, a.len) for i in 0 ..< a.len: result[i] = fn(a[i]) diff --git a/tests/stdlib/tdochelpers.nim b/tests/stdlib/tdochelpers.nim index 9d440a1d6..8dcb158ca 100644 --- a/tests/stdlib/tdochelpers.nim +++ b/tests/stdlib/tdochelpers.nim @@ -76,7 +76,7 @@ suite "Integration with Nim": check(input3.toLangSymbol == expected) test "advanced proc parsing with Nim identifier normalization": - let input = """`proc binarySearch*[T, K](a: openArray[T]; key: K; + let input = """`proc binarySearch*[T, K](a: openarray[T]; key: K; cmp: proc (x: T; y: K): int)`_""".rstParseTest let expected = LangSymbol(symKind: "proc", name: "binarysearch", @@ -90,7 +90,7 @@ suite "Integration with Nim": check(input.toLangSymbol == expected) test "the same without proc": - let input = """`binarySearch*[T, K](a: openArray[T]; key: K; + let input = """`binarySearch*[T, K](a: openarray[T]; key: K; cmp: proc (x: T; y: K): int {.closure.})`_""".rstParseTest let expected = LangSymbol(symKind: "", name: "binarysearch", diff --git a/tests/stdlib/tparseipv6.nim b/tests/stdlib/tparseipv6.nim index dd9abc511..9b9c464c7 100644 --- a/tests/stdlib/tparseipv6.nim +++ b/tests/stdlib/tparseipv6.nim @@ -210,12 +210,12 @@ const "::a:b:85:e4d9:252.9.229.056", ] -proc ok(pos: openarray[string]) = +proc ok(pos: openArray[string]) = for p in pos: if not isIpAddress(p): echo "failure ", p -proc notok(neg: openarray[string]) = +proc notok(neg: openArray[string]) = for n in neg: if isIpAddress(n): echo "failure ", n diff --git a/tests/stdlib/trepr.nim b/tests/stdlib/trepr.nim index 83ae7b119..82c991805 100644 --- a/tests/stdlib/trepr.nim +++ b/tests/stdlib/trepr.nim @@ -64,7 +64,7 @@ template main() = doAssert repr(arr) == "[1, 2, 3]" block: # bug #7878 - proc reprOpenarray(variable: var openarray[int]): string = repr(variable) + proc reprOpenarray(variable: var openArray[int]): string = repr(variable) when defined(js): discard # BUG: doesn't work else: doAssert reprOpenarray(arr) == "[1, 2, 3]" diff --git a/tests/system/tostring.nim b/tests/system/tostring.nim index fa82acc3b..bdbaa2ce6 100644 --- a/tests/system/tostring.nim +++ b/tests/system/tostring.nim @@ -68,7 +68,7 @@ doAssert yy == "" proc bar(arg: cstring) = doAssert arg[0] == '\0' -proc baz(arg: openarray[char]) = +proc baz(arg: openArray[char]) = doAssert arg.len == 0 proc stringCompare() = diff --git a/tests/system/tsystem_misc.nim b/tests/system/tsystem_misc.nim index 13b8c18a9..7f5914725 100644 --- a/tests/system/tsystem_misc.nim +++ b/tests/system/tsystem_misc.nim @@ -131,12 +131,12 @@ let str = "0123456789" foo(toOpenArrayByte(str, 0, str.high)) -template boundedOpenArray[T](x: seq[T], first, last: int): openarray[T] = +template boundedOpenArray[T](x: seq[T], first, last: int): openArray[T] = toOpenarray(x, max(0, first), min(x.high, last)) # bug #9281 -proc foo[T](x: openarray[T]) = +proc foo[T](x: openArray[T]) = echo x.len let a = @[1, 2, 3] diff --git a/tests/template/otests.nim b/tests/template/otests.nim index 120146343..9cb9d7fcb 100644 --- a/tests/template/otests.nim +++ b/tests/template/otests.nim @@ -163,7 +163,7 @@ when true: #embeddingTest """ # Expression template - proc test_expression(nums: openarray[int] = []): string = + proc test_expression(nums: openArray[int] = []): string = var i = 2 tmpli html""" $(no_substitution()) @@ -171,7 +171,7 @@ when true: #embeddingTest
      Age: $($nums[i] & "!!")
      """ - proc test_statements(nums: openarray[int] = []): string = + proc test_statements(nums: openArray[int] = []): string = tmpli html""" $(test_expression(nums)) $if true { diff --git a/tests/template/sunset.nimf b/tests/template/sunset.nimf index 465b12a5e..bd57bb8e1 100644 --- a/tests/template/sunset.nimf +++ b/tests/template/sunset.nimf @@ -1,6 +1,6 @@ #? stdtmpl #proc sunsetTemplate*(current, ticker, content: string, -# tabs: openarray[array[0..1, string]]): string = +# tabs: openArray[array[0..1, string]]): string = # result = "" diff --git a/tests/template/template_issues.nim b/tests/template/template_issues.nim index 502c6d1ee..1fed694ef 100644 --- a/tests/template/template_issues.nim +++ b/tests/template/template_issues.nim @@ -173,7 +173,7 @@ block t2585: st echo "a ", $fb - proc render(rdat: var RenderData; passes: var openarray[RenderPass]; proj: Mat2; + proc render(rdat: var RenderData; passes: var openArray[RenderPass]; proj: Mat2; indexType = 1) = for i in 0 ..< len(passes): echo "blah ", repr(passes[i]) diff --git a/tests/tuples/tuple_with_nil.nim b/tests/tuples/tuple_with_nil.nim index 7136c8cab..9cad6eccd 100644 --- a/tests/tuples/tuple_with_nil.nim +++ b/tests/tuples/tuple_with_nil.nim @@ -477,7 +477,7 @@ proc writeformat(o: var Writer; b: bool; fmt: Format) = else: raise newException(FormatError, "Boolean values must of one of the following types: s,b,o,x,X,d,n") -proc writeformat(o: var Writer; ary: openarray[system.any]; fmt: Format) = +proc writeformat(o: var Writer; ary: openArray[system.any]; fmt: Format) = ## Write array `ary` according to format `fmt` using output object ## `o` and output function `add`. if ary.len == 0: return @@ -657,7 +657,7 @@ proc literal[T](x: T): NimNode {.compiletime, nosideeffect.} = result = newLit(x) proc generatefmt(fmtstr: string; - args: var openarray[tuple[arg:NimNode, cnt:int]]; + args: var openArray[tuple[arg:NimNode, cnt:int]]; arg: var int;): seq[tuple[val, fmt:NimNode]] {.compiletime.} = ## fmtstr ## the format string diff --git a/tests/typerel/tcommontype.nim b/tests/typerel/tcommontype.nim index 8215ebd5e..488047fd1 100644 --- a/tests/typerel/tcommontype.nim +++ b/tests/typerel/tcommontype.nim @@ -12,7 +12,7 @@ type proc newDog():PDog = new(result) proc newCat():PCat = new(result) -proc test(a:openarray[PAnimal])= +proc test(a:openArray[PAnimal])= echo("dummy") #test(newDog(),newCat()) #does not work diff --git a/tests/typerel/texplicitcmp.nim b/tests/typerel/texplicitcmp.nim index b11aa2f4e..57f1e81b6 100644 --- a/tests/typerel/texplicitcmp.nim +++ b/tests/typerel/texplicitcmp.nim @@ -8,7 +8,7 @@ discard """ import json, tables, algorithm -proc outp(a: openarray[int]) = +proc outp(a: openArray[int]) = stdout.write "[" for i in a: stdout.write($i & " ") stdout.write "]\n" diff --git a/tests/typerel/tstr_as_openarray.nim b/tests/typerel/tstr_as_openarray.nim index fc28d6c93..14cd21479 100644 --- a/tests/typerel/tstr_as_openarray.nim +++ b/tests/typerel/tstr_as_openarray.nim @@ -3,18 +3,18 @@ discard """ """ var s = "HI" -proc x (zz: openarray[char]) = +proc x (zz: openArray[char]) = discard x s -proc z [T] (zz: openarray[T]) = +proc z [T] (zz: openArray[T]) = discard z s z([s,s,s]) -proc y [T] (arg: var openarray[T]) = +proc y [T] (arg: var openArray[T]) = arg[0] = 'X' y s doAssert s == "XI" diff --git a/tests/views/tviews1.nim b/tests/views/tviews1.nim index ced487ce8..249058eb6 100644 --- a/tests/views/tviews1.nim +++ b/tests/views/tviews1.nim @@ -44,7 +44,7 @@ echo foo(x) type F = object - oa: openarray[int] + oa: openArray[int] let s1 = @[1,3,4,5,6] var test = F(oa: toOpenArray(s1, 0, 2)) diff --git a/tests/vm/tyaytypedesc.nim b/tests/vm/tyaytypedesc.nim index a3ad9b707..aab94d643 100644 --- a/tests/vm/tyaytypedesc.nim +++ b/tests/vm/tyaytypedesc.nim @@ -10,7 +10,7 @@ type NodeType* = enum type TokenType* = enum ttWhitespace -proc enumTable*[A, B, C](a: openarray[tuple[key: A, val: B]], ret: typedesc[C]): C = +proc enumTable*[A, B, C](a: openArray[tuple[key: A, val: B]], ret: typedesc[C]): C = for item in a: result[item.key] = item.val From 5ec8b609421a2e74e7d5f8d00293194faf4437d4 Mon Sep 17 00:00:00 2001 From: rockcavera Date: Thu, 6 Jan 2022 08:19:32 -0300 Subject: [PATCH 2744/3768] Update net.nim (#19327) [backport] --- lib/pure/net.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 8dc92e9d6..c1d4c6228 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -1628,7 +1628,7 @@ proc recvFrom*[T: string | IpAddress](socket: Socket, data: var string, length: ## used. Therefore if `socket` contains something in its buffer this ## function will make no effort to return it. template adaptRecvFromToDomain(sockAddress: untyped, domain: Domain) = - var addrLen = sizeof(sockAddress).SockLen + var addrLen = SockLen(sizeof(sockAddress)) result = recvfrom(socket.fd, cstring(data), length.cint, flags.cint, cast[ptr SockAddr](addr(sockAddress)), addr(addrLen)) From c7d5b8c83ddf204c6e0cd7b6937d33318320ed91 Mon Sep 17 00:00:00 2001 From: rockcavera Date: Fri, 7 Jan 2022 04:26:55 -0300 Subject: [PATCH 2745/3768] Fix #19038 - making the Nim compiler work again on Windows XP (#19331) * Update osenv.nim * Update win_setenv.nim * Update lib/pure/includes/osenv.nim * Update lib/pure/includes/osenv.nim * fixing cstring Co-authored-by: Andreas Rumpf --- lib/pure/includes/osenv.nim | 5 +++-- lib/std/private/win_setenv.nim | 41 +++++++++++++++++----------------- 2 files changed, 24 insertions(+), 22 deletions(-) diff --git a/lib/pure/includes/osenv.nim b/lib/pure/includes/osenv.nim index 1a01ab9bc..4a776eb78 100644 --- a/lib/pure/includes/osenv.nim +++ b/lib/pure/includes/osenv.nim @@ -45,7 +45,7 @@ when not defined(nimscript): proc c_getenv(env: cstring): cstring {. importc: "getenv", header: "".} when defined(windows): - proc c_putenv_s(envname: cstring, envval: cstring): cint {.importc: "_putenv_s", header: "".} + proc c_putenv(envstring: cstring): cint {.importc: "_putenv", header: "".} from std/private/win_setenv import setEnvImpl else: proc c_setenv(envname: cstring, envval: cstring, overwrite: cint): cint {.importc: "setenv", header: "".} @@ -121,7 +121,8 @@ when not defined(nimscript): ]# if key.len == 0 or '=' in key: raise newException(OSError, "invalid key, got: " & key) - if c_putenv_s(key, "") != 0'i32: bail + let envToDel = key & "=" + if c_putenv(cstring envToDel) != 0'i32: bail else: if c_unsetenv(key) != 0'i32: bail diff --git a/lib/std/private/win_setenv.nim b/lib/std/private/win_setenv.nim index 91c3f15b0..0dfe0ed46 100644 --- a/lib/std/private/win_setenv.nim +++ b/lib/std/private/win_setenv.nim @@ -30,44 +30,45 @@ else: # same as winlean.setEnvironmentVariableA proc c_getenv(env: cstring): cstring {.importc: "getenv", header: "".} - proc c_putenv_s(envname: cstring, envval: cstring): cint {.importc: "_putenv_s", header: "".} + proc c_putenv(envstring: cstring): cint {.importc: "_putenv", header: "".} proc c_wgetenv(varname: ptr wchar_t): ptr wchar_t {.importc: "_wgetenv", header: "".} var errno {.importc, header: "".}: cint - var gWenviron {.importc:"_wenviron".}: ptr ptr wchar_t + var gWenviron {.importc: "_wenviron".}: ptr ptr wchar_t # xxx `ptr UncheckedArray[WideCString]` did not work - proc mbstowcs_s(pReturnValue: ptr csize_t, wcstr: ptr wchar_t, sizeInWords: csize_t, mbstr: cstring, count: csize_t): cint {.importc: "mbstowcs_s", header: "".} + proc mbstowcs(wcstr: ptr wchar_t, mbstr: cstring, count: csize_t): csize_t {.importc: "mbstowcs", header: "".} # xxx cint vs errno_t? - proc setEnvImpl*(name: cstring, value: cstring, overwrite: cint): cint = + proc setEnvImpl*(name: string, value: string, overwrite: cint): cint = const EINVAL = cint(22) - const MAX_ENV = 32767 - # xxx get it from: `var MAX_ENV {.importc: "_MAX_ENV", header:"".}: cint` - if overwrite == 0 and c_getenv(name) != nil: return 0 - if value[0] != '\0': - let e = c_putenv_s(name, value) + if overwrite == 0 and c_getenv(cstring(name)) != nil: return 0 + if value != "": + let envstring = name & "=" & value + let e = c_putenv(cstring(envstring)) if e != 0: - errno = e + errno = EINVAL return -1 return 0 #[ - We are trying to set the value to an empty string, but `_putenv_s` deletes + We are trying to set the value to an empty string, but `_putenv` deletes entries if the value is an empty string, and just calling SetEnvironmentVariableA doesn't update `_environ`, so we have to do these terrible things. ]# - if c_putenv_s(name, " ") != 0: + let envstring = name & "= " + if c_putenv(cstring(envstring)) != 0: errno = EINVAL return -1 # Here lies the documentation we blatently ignore to make this work. - var s = c_getenv(name) + var s = c_getenv(cstring(name)) s[0] = '\0' #[ This would result in a double null termination, which normally signifies the end of the environment variable list, so we stick a completely empty environment variable into the list instead. ]# + s = c_getenv(cstring(name)) s[1] = '=' #[ If gWenviron is null, the wide environment has not been initialized @@ -77,19 +78,19 @@ else: ]# if gWenviron != nil: # var buf: array[MAX_ENV + 1, WideCString] - var buf: array[MAX_ENV + 1, Utf16Char] + let requiredSize = mbstowcs(nil, cstring(name), 0).int + var buf = newSeq[Utf16Char](requiredSize + 1) let buf2 = cast[ptr wchar_t](buf[0].addr) - var len: csize_t - if mbstowcs_s(len.addr, buf2, buf.len.csize_t, name, MAX_ENV) != 0: + if mbstowcs(buf2, cstring(name), csize_t(requiredSize + 1)) == csize_t(high(uint)): errno = EINVAL return -1 - let ptrToEnv = cast[WideCString](c_wgetenv(buf2)) + var ptrToEnv = cast[WideCString](c_wgetenv(buf2)) ptrToEnv[0] = '\0'.Utf16Char - let ptrToEnv2 = cast[WideCString](c_wgetenv(buf2)) - ptrToEnv2[1] = '='.Utf16Char + ptrToEnv = cast[WideCString](c_wgetenv(buf2)) + ptrToEnv[1] = '='.Utf16Char # And now, we have to update the outer environment to have a proper empty value. - if setEnvironmentVariableA(name, value) == 0: + if setEnvironmentVariableA(cstring(name), cstring(value)) == 0: errno = EINVAL return -1 return 0 From 4306d8fb36d3810b8d38a665f98dc89ce107740e Mon Sep 17 00:00:00 2001 From: flywind Date: Sat, 8 Jan 2022 12:06:28 +0800 Subject: [PATCH 2746/3768] disable fragments (#19341) The original repo has moved to Rust, while the package path stay unchanged. And it causes troubles to https://github.com/nim-lang/Nim/pull/19338 --- testament/important_packages.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 381aa27ac..853a58698 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -64,7 +64,7 @@ pkg "docopt" pkg "easygl", "nim c -o:egl -r src/easygl.nim", "https://github.com/jackmott/easygl" pkg "elvis" pkg "fidget" -pkg "fragments", "nim c -r fragments/dsl.nim" +pkg "fragments", "nim c -r fragments/dsl.nim", allowFailure = true # pending https://github.com/nim-lang/packages/issues/2115 pkg "fusion" pkg "gara" pkg "glob" From 58656aa5bba572672f093499280b69bb0f0d4c06 Mon Sep 17 00:00:00 2001 From: Zachary Marquez Date: Sat, 8 Jan 2022 03:22:56 -0600 Subject: [PATCH 2747/3768] fix nim-lang#19343 (#19344) [backport] Ensure HttpClient onProgress is called once per second Ensure that reported speed is accurate --- lib/pure/httpclient.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/httpclient.nim b/lib/pure/httpclient.nim index 7686d15bd..5b6e439db 100644 --- a/lib/pure/httpclient.nim +++ b/lib/pure/httpclient.nim @@ -673,7 +673,7 @@ proc reportProgress(client: HttpClient | AsyncHttpClient, progress: BiggestInt) {.multisync.} = client.contentProgress += progress client.oneSecondProgress += progress - if (getMonoTime() - client.lastProgressReport).inSeconds > 1: + if (getMonoTime() - client.lastProgressReport).inSeconds >= 1: if not client.onProgressChanged.isNil: await client.onProgressChanged(client.contentTotal, client.contentProgress, From ae92eac06031ed57a755c01efdbb9cc5b72e5dbf Mon Sep 17 00:00:00 2001 From: flywind Date: Sun, 9 Jan 2022 04:34:22 +0800 Subject: [PATCH 2748/3768] stylecheck usages part two: stdlib cleanup (#19338) * stylecheck usages part two: stdlib cleanup typeinfo.nim: importCompilerProc => importcompilerproc nre.nim: newLineFlags => newlineFlags system.nim: JSRoot => JsRoot ref #19319 * prefer importCompilerProc --- compiler/wordrecg.nim | 2 +- lib/impure/nre.nim | 2 +- lib/pure/os.nim | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index 22f6cc71d..c819f4306 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -41,7 +41,7 @@ type wImmediate = "immediate", wConstructor = "constructor", wDestructor = "destructor", wDelegator = "delegator", wOverride = "override", wImportCpp = "importcpp", wCppNonPod = "cppNonPod", - wImportObjC = "importobjc", wImportCompilerProc = "importcompilerproc", + wImportObjC = "importobjc", wImportCompilerProc = "importCompilerProc", wImportc = "importc", wImportJs = "importjs", wExportc = "exportc", wExportCpp = "exportcpp", wExportNims = "exportnims", wIncompleteStruct = "incompleteStruct", # deprecated diff --git a/lib/impure/nre.nim b/lib/impure/nre.nim index f346d84cb..03cbf5220 100644 --- a/lib/impure/nre.nim +++ b/lib/impure/nre.nim @@ -281,7 +281,7 @@ proc matchesCrLf(pattern: Regex): bool = let newlineFlags = flags and (pcre.NEWLINE_CRLF or pcre.NEWLINE_ANY or pcre.NEWLINE_ANYCRLF) - if newLineFlags > 0u32: + if newlineFlags > 0u32: return true # get flags from build config diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 9b08fe2e1..81b432d7f 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -2888,7 +2888,7 @@ when defined(nimdoc): elif defined(nimscript): discard elif defined(nodejs): - type Argv = object of JSRoot + type Argv = object of JsRoot let argv {.importjs: "process.argv".} : Argv proc len(argv: Argv): int {.importjs: "#.length".} proc `[]`(argv: Argv, i: int): cstring {.importjs: "#[#]".} From b098546da051ed026e85a9f398e74c396c79a8ad Mon Sep 17 00:00:00 2001 From: Smarcy Date: Sun, 9 Jan 2022 14:02:08 +0100 Subject: [PATCH 2749/3768] added filemode docs (#19346) --- lib/system/io.nim | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/system/io.nim b/lib/system/io.nim index 661d1a9ba..04a43328d 100644 --- a/lib/system/io.nim +++ b/lib/system/io.nim @@ -22,6 +22,8 @@ type FileMode* = enum ## The file mode when opening a file. fmRead, ## Open the file for read access only. + ## If the file does not exist, it will not + ## be created. fmWrite, ## Open the file for write access only. ## If the file does not exist, it will be ## created. Existing files will be cleared! @@ -32,7 +34,8 @@ type ## If the file does not exist, it will not be ## created. The existing file will not be cleared. fmAppend ## Open the file for writing only; append data - ## at the end. + ## at the end. If the file does not exist, it + ## will be created. FileHandle* = cint ## type that represents an OS file handle; this is ## useful for low-level file access From 955040f0f1d4f9b2eab68c385fab9d4cfbef8ea5 Mon Sep 17 00:00:00 2001 From: gecko Date: Mon, 10 Jan 2022 09:27:59 +0000 Subject: [PATCH 2750/3768] Fix `remove` on last node of singly-linked list [backport:1.6] (#19353) --- changelog.md | 1 + lib/pure/collections/lists.nim | 2 ++ tests/stdlib/tlists.nim | 13 +++++++++++++ 3 files changed, 16 insertions(+) diff --git a/changelog.md b/changelog.md index 1b97fd99a..d07f80623 100644 --- a/changelog.md +++ b/changelog.md @@ -22,6 +22,7 @@ - `macros.parseExpr` and `macros.parseStmt` now accept an optional filename argument for more informative errors. - Module `colors` expanded with missing colors from the CSS color standard. +- Fixed `lists.SinglyLinkedList` being broken after removing the last node ([#19353](https://github.com/nim-lang/Nim/pull/19353)). ## `std/smtp` diff --git a/lib/pure/collections/lists.nim b/lib/pure/collections/lists.nim index f7627ae67..d1de0ea67 100644 --- a/lib/pure/collections/lists.nim +++ b/lib/pure/collections/lists.nim @@ -739,6 +739,8 @@ proc remove*[T](L: var SinglyLinkedList[T], n: SinglyLinkedNode[T]): bool {.disc if prev.next == nil: return false prev.next = n.next + if L.tail == n: + L.tail = prev # update tail if we removed the last node true proc remove*[T](L: var DoublyLinkedList[T], n: DoublyLinkedNode[T]) = diff --git a/tests/stdlib/tlists.nim b/tests/stdlib/tlists.nim index 14cbf2f9d..00c5b1a27 100644 --- a/tests/stdlib/tlists.nim +++ b/tests/stdlib/tlists.nim @@ -258,5 +258,18 @@ template main = a.add(2) doAssert a.toSeq == @[1, 2] + block RemoveLastNodeFromSinglyLinkedList: + var list = initSinglyLinkedList[string]() + let n1 = newSinglyLinkedNode("sonic") + let n2 = newSinglyLinkedNode("the") + let n3 = newSinglyLinkedNode("tiger") + let n4 = newSinglyLinkedNode("hedgehog") + list.add(n1) + list.add(n2) + list.add(n3) + list.remove(n3) + list.add(n4) + doAssert list.toSeq == @["sonic", "the", "hedgehog"] + static: main() main() From 92e5573b20f30f4114a4eb1f35eddc7064bfcd71 Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 10 Jan 2022 17:29:04 +0800 Subject: [PATCH 2751/3768] fix stylecheck error with asyncdispatch (#19350) * stylecheck usages part two: stdlib cleanup typeinfo.nim: importCompilerProc => importcompilerproc nre.nim: newLineFlags => newlineFlags system.nim: JSRoot => JsRoot ref #19319 * prefer importCompilerProc * fix stylecheck error with asyncdispatch it is a partial regression since #12842 * add tests * don't use echo in tests --- compiler/semtempl.nim | 3 ++- tests/stylecheck/taccept.nim | 17 +++++++++++++++++ tests/stylecheck/treject.nim | 17 +++++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 tests/stylecheck/taccept.nim create mode 100644 tests/stylecheck/treject.nim diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index 49ce55a64..b921fda2c 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -270,7 +270,8 @@ proc semTemplSymbol(c: PContext, n: PNode, s: PSym; isField: bool): PNode = # Issue #12832 when defined(nimsuggest): suggestSym(c.graph, n.info, s, c.graph.usageSym, false) - if {optStyleHint, optStyleError} * c.config.globalOptions != {}: + # field access (dot expr) will be handled by builtinFieldAccess + if not isField and {optStyleHint, optStyleError} * c.config.globalOptions != {}: styleCheckUse(c.config, n.info, s) proc semRoutineInTemplName(c: var TemplCtx, n: PNode): PNode = diff --git a/tests/stylecheck/taccept.nim b/tests/stylecheck/taccept.nim new file mode 100644 index 000000000..6fb55f835 --- /dev/null +++ b/tests/stylecheck/taccept.nim @@ -0,0 +1,17 @@ +discard """ + matrix: "--styleCheck:error --styleCheck:usages" +""" + +import asyncdispatch + +type + Name = object + id: int + +template hello = + var iD = "string" + var name: Name + doAssert name.id == 0 + doAssert iD == "string" + +hello() diff --git a/tests/stylecheck/treject.nim b/tests/stylecheck/treject.nim new file mode 100644 index 000000000..b9d97a58d --- /dev/null +++ b/tests/stylecheck/treject.nim @@ -0,0 +1,17 @@ +discard """ + action: reject + nimout: '''treject.nim(14, 13) Error: 'iD' should be: 'id' [field declared in treject.nim(9, 5)]''' + matrix: "--styleCheck:error --styleCheck:usages" +""" + +type + Name = object + id: int + +template hello = + var iD = "string" + var name: Name + echo name.iD + echo iD + +hello() \ No newline at end of file From bcbeff36321aa203cd258fd02c82d9276badf31f Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 10 Jan 2022 18:59:44 +0800 Subject: [PATCH 2752/3768] remove spaces between an identifier and a star (#19355) It makes search easier by searching `+`* instead of `+` which filter lots of unexported versions. Follow https://github.com/nim-lang/Nim/pull/18681 --- lib/js/jsffi.nim | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/lib/js/jsffi.nim b/lib/js/jsffi.nim index 937e3727b..ac963eb89 100644 --- a/lib/js/jsffi.nim +++ b/lib/js/jsffi.nim @@ -163,28 +163,28 @@ macro jsFromAst*(n: untyped): untyped = proc `&`*(a, b: cstring): cstring {.importjs: "(# + #)".} ## Concatenation operator for JavaScript strings. -proc `+` *(x, y: JsObject): JsObject {.importjs: "(# + #)".} -proc `-` *(x, y: JsObject): JsObject {.importjs: "(# - #)".} -proc `*` *(x, y: JsObject): JsObject {.importjs: "(# * #)".} -proc `/` *(x, y: JsObject): JsObject {.importjs: "(# / #)".} -proc `%` *(x, y: JsObject): JsObject {.importjs: "(# % #)".} -proc `+=` *(x, y: JsObject): JsObject {.importjs: "(# += #)", discardable.} -proc `-=` *(x, y: JsObject): JsObject {.importjs: "(# -= #)", discardable.} -proc `*=` *(x, y: JsObject): JsObject {.importjs: "(# *= #)", discardable.} -proc `/=` *(x, y: JsObject): JsObject {.importjs: "(# /= #)", discardable.} -proc `%=` *(x, y: JsObject): JsObject {.importjs: "(# %= #)", discardable.} -proc `++` *(x: JsObject): JsObject {.importjs: "(++#)".} -proc `--` *(x: JsObject): JsObject {.importjs: "(--#)".} -proc `>` *(x, y: JsObject): JsObject {.importjs: "(# > #)".} -proc `<` *(x, y: JsObject): JsObject {.importjs: "(# < #)".} -proc `>=` *(x, y: JsObject): JsObject {.importjs: "(# >= #)".} -proc `<=` *(x, y: JsObject): JsObject {.importjs: "(# <= #)".} -proc `**` *(x, y: JsObject): JsObject {.importjs: "((#) ** #)".} +proc `+`*(x, y: JsObject): JsObject {.importjs: "(# + #)".} +proc `-`*(x, y: JsObject): JsObject {.importjs: "(# - #)".} +proc `*`*(x, y: JsObject): JsObject {.importjs: "(# * #)".} +proc `/`*(x, y: JsObject): JsObject {.importjs: "(# / #)".} +proc `%`*(x, y: JsObject): JsObject {.importjs: "(# % #)".} +proc `+=`*(x, y: JsObject): JsObject {.importjs: "(# += #)", discardable.} +proc `-=`*(x, y: JsObject): JsObject {.importjs: "(# -= #)", discardable.} +proc `*=`*(x, y: JsObject): JsObject {.importjs: "(# *= #)", discardable.} +proc `/=`*(x, y: JsObject): JsObject {.importjs: "(# /= #)", discardable.} +proc `%=`*(x, y: JsObject): JsObject {.importjs: "(# %= #)", discardable.} +proc `++`*(x: JsObject): JsObject {.importjs: "(++#)".} +proc `--`*(x: JsObject): JsObject {.importjs: "(--#)".} +proc `>`*(x, y: JsObject): JsObject {.importjs: "(# > #)".} +proc `<`*(x, y: JsObject): JsObject {.importjs: "(# < #)".} +proc `>=`*(x, y: JsObject): JsObject {.importjs: "(# >= #)".} +proc `<=`*(x, y: JsObject): JsObject {.importjs: "(# <= #)".} +proc `**`*(x, y: JsObject): JsObject {.importjs: "((#) ** #)".} # (#) needed, refs https://github.com/nim-lang/Nim/pull/16409#issuecomment-760550812 proc `and`*(x, y: JsObject): JsObject {.importjs: "(# && #)".} -proc `or` *(x, y: JsObject): JsObject {.importjs: "(# || #)".} +proc `or`*(x, y: JsObject): JsObject {.importjs: "(# || #)".} proc `not`*(x: JsObject): JsObject {.importjs: "(!#)".} -proc `in` *(x, y: JsObject): JsObject {.importjs: "(# in #)".} +proc `in`*(x, y: JsObject): JsObject {.importjs: "(# in #)".} proc `[]`*(obj: JsObject, field: cstring): JsObject {.importjs: getImpl.} ## Returns the value of a property of name `field` from a JsObject `obj`. From 9888a29c3de09c73202b2a955e09e2a7b5e56ea4 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 11 Jan 2022 10:16:51 +0100 Subject: [PATCH 2753/3768] bitsets.nim: cleanup (#19361) --- compiler/bitsets.nim | 35 +++++++++++------------------------ 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/compiler/bitsets.nim b/compiler/bitsets.nim index 2e8aa1db6..e0d94409e 100644 --- a/compiler/bitsets.nim +++ b/compiler/bitsets.nim @@ -23,53 +23,40 @@ const template modElemSize(arg: untyped): untyped = arg and 7 template divElemSize(arg: untyped): untyped = arg shr 3 -proc bitSetInit*(b: var TBitSet, length: int) -proc bitSetUnion*(x: var TBitSet, y: TBitSet) -proc bitSetDiff*(x: var TBitSet, y: TBitSet) -proc bitSetSymDiff*(x: var TBitSet, y: TBitSet) -proc bitSetIntersect*(x: var TBitSet, y: TBitSet) -proc bitSetIncl*(x: var TBitSet, elem: BiggestInt) -proc bitSetExcl*(x: var TBitSet, elem: BiggestInt) -proc bitSetIn*(x: TBitSet, e: BiggestInt): bool -proc bitSetEquals*(x, y: TBitSet): bool -proc bitSetContains*(x, y: TBitSet): bool -proc bitSetCard*(x: TBitSet): BiggestInt -# implementation - -proc bitSetIn(x: TBitSet, e: BiggestInt): bool = +proc bitSetIn*(x: TBitSet, e: BiggestInt): bool = result = (x[int(e.divElemSize)] and (One shl e.modElemSize)) != Zero -proc bitSetIncl(x: var TBitSet, elem: BiggestInt) = +proc bitSetIncl*(x: var TBitSet, elem: BiggestInt) = assert(elem >= 0) x[int(elem.divElemSize)] = x[int(elem.divElemSize)] or (One shl elem.modElemSize) -proc bitSetExcl(x: var TBitSet, elem: BiggestInt) = +proc bitSetExcl*(x: var TBitSet, elem: BiggestInt) = x[int(elem.divElemSize)] = x[int(elem.divElemSize)] and not(One shl elem.modElemSize) -proc bitSetInit(b: var TBitSet, length: int) = +proc bitSetInit*(b: var TBitSet, length: int) = newSeq(b, length) -proc bitSetUnion(x: var TBitSet, y: TBitSet) = +proc bitSetUnion*(x: var TBitSet, y: TBitSet) = for i in 0..high(x): x[i] = x[i] or y[i] -proc bitSetDiff(x: var TBitSet, y: TBitSet) = +proc bitSetDiff*(x: var TBitSet, y: TBitSet) = for i in 0..high(x): x[i] = x[i] and not y[i] -proc bitSetSymDiff(x: var TBitSet, y: TBitSet) = +proc bitSetSymDiff*(x: var TBitSet, y: TBitSet) = for i in 0..high(x): x[i] = x[i] xor y[i] -proc bitSetIntersect(x: var TBitSet, y: TBitSet) = +proc bitSetIntersect*(x: var TBitSet, y: TBitSet) = for i in 0..high(x): x[i] = x[i] and y[i] -proc bitSetEquals(x, y: TBitSet): bool = +proc bitSetEquals*(x, y: TBitSet): bool = for i in 0..high(x): if x[i] != y[i]: return false result = true -proc bitSetContains(x, y: TBitSet): bool = +proc bitSetContains*(x, y: TBitSet): bool = for i in 0..high(x): if (x[i] and not y[i]) != Zero: return false @@ -96,6 +83,6 @@ const populationCount: array[uint8, uint8] = block: arr -proc bitSetCard(x: TBitSet): BiggestInt = +proc bitSetCard*(x: TBitSet): BiggestInt = for it in x: result.inc int(populationCount[it]) From 9a32905d89d23e423bca2d0ce643b9331a6741c0 Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 13 Jan 2022 15:02:41 +0800 Subject: [PATCH 2754/3768] nimscript ignore the threads option (#19370) because nimscript doesn't support threads and causes troubles when the threads option is on --- compiler/commands.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/commands.nim b/compiler/commands.nim index c4df46bc2..8e79a4477 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -717,7 +717,7 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; of "linedir": processOnOffSwitch(conf, {optLineDir}, arg, pass, info) of "assertions", "a": processOnOffSwitch(conf, {optAssert}, arg, pass, info) of "threads": - if conf.backend == backendJs: discard + if conf.backend == backendJs or conf.cmd == cmdNimscript: discard else: processOnOffSwitchG(conf, {optThreads}, arg, pass, info) #if optThreads in conf.globalOptions: conf.setNote(warnGcUnsafe) of "tlsemulation": processOnOffSwitchG(conf, {optTlsEmulation}, arg, pass, info) From ada815e45957e5675a9ac978b85b8467d68c5945 Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 13 Jan 2022 15:03:14 +0800 Subject: [PATCH 2755/3768] make rst thread safe (#19369) split for the convenience of review --- lib/packages/docutils/rst.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index a04eb696f..791953767 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -2822,7 +2822,7 @@ type DirFlag = enum hasArg, hasOptions, argIsFile, argIsWord DirFlags = set[DirFlag] - SectionParser = proc (p: var RstParser): PRstNode {.nimcall.} + SectionParser = proc (p: var RstParser): PRstNode {.nimcall, gcsafe.} proc parseDirective(p: var RstParser, k: RstNodeKind, flags: DirFlags): PRstNode = ## Parses arguments and options for a directive block. @@ -2869,7 +2869,7 @@ proc indFollows(p: RstParser): bool = result = currentTok(p).kind == tkIndent and currentTok(p).ival > currInd(p) proc parseBlockContent(p: var RstParser, father: var PRstNode, - contentParser: SectionParser): bool = + contentParser: SectionParser): bool {.gcsafe.} = ## parse the final content part of explicit markup blocks (directives, ## footnotes, etc). Returns true if succeeded. if currentTok(p).kind != tkIndent or indFollows(p): @@ -3123,7 +3123,7 @@ proc prefix(ftnType: FootnoteType): string = of fnAutoSymbol: result = "footnotesym-" of fnCitation: result = "citation-" -proc parseFootnote(p: var RstParser): PRstNode = +proc parseFootnote(p: var RstParser): PRstNode {.gcsafe.} = ## Parses footnotes and citations, always returns 2 sons: ## ## 1) footnote label, always containing rnInner with 1 or more sons From a9135095f02587430b179a33ae03910d0d4201cd Mon Sep 17 00:00:00 2001 From: Leon Date: Fri, 14 Jan 2022 00:40:27 +1100 Subject: [PATCH 2756/3768] docs: Mention `import foo {.all.}` syntax (#19377) Mention the `import foo {.all.}` syntax in the manual, with a caveat about private imports. Also link to the experimental importutils module. Co-authored-by: adigitoleo --- doc/manual_experimental.rst | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/doc/manual_experimental.rst b/doc/manual_experimental.rst index 4ed8439df..0f82b9950 100644 --- a/doc/manual_experimental.rst +++ b/doc/manual_experimental.rst @@ -168,6 +168,17 @@ This feature will likely be superseded in the future by support for recursive module dependencies. +Importing private symbols +========================= + +In some situations, it may be useful to import all symbols (public or private) +from a module. The syntax `import foo {.all.}` can be used to import all +symbols from the module `foo`. Note that importing private symbols is +generally not recommended. + +See also the experimental `importutils `_ module. + + Code reordering =============== From 40a9c33eff099a37be236826f84eb2064d0ec247 Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 13 Jan 2022 21:43:35 +0800 Subject: [PATCH 2757/3768] update copyright year (#19381) --- compiler/options.nim | 2 +- copying.txt | 2 +- readme.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/options.nim b/compiler/options.nim index 9e957332f..5cfe58119 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -19,7 +19,7 @@ const useEffectSystem* = true useWriteTracking* = false hasFFI* = defined(nimHasLibFFI) - copyrightYear* = "2021" + copyrightYear* = "2022" nimEnableCovariance* = defined(nimEnableCovariance) diff --git a/copying.txt b/copying.txt index a498a9525..60b6a0217 100644 --- a/copying.txt +++ b/copying.txt @@ -1,7 +1,7 @@ ===================================================== Nim -- a Compiler for Nim. https://nim-lang.org/ -Copyright (C) 2006-2021 Andreas Rumpf. All rights reserved. +Copyright (C) 2006-2022 Andreas Rumpf. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/readme.md b/readme.md index 7d45556db..6bc7a2385 100644 --- a/readme.md +++ b/readme.md @@ -203,7 +203,7 @@ Nim. You are explicitly permitted to develop commercial applications using Nim. Please read the [copying.txt](copying.txt) file for more details. -Copyright © 2006-2021 Andreas Rumpf, all rights reserved. +Copyright © 2006-2022 Andreas Rumpf, all rights reserved. [nim-site]: https://nim-lang.org [nim-forum]: https://forum.nim-lang.org From 5853303be09464832ce0f189513c90c9dd15f4a0 Mon Sep 17 00:00:00 2001 From: Leon Date: Fri, 14 Jan 2022 00:45:47 +1100 Subject: [PATCH 2758/3768] docs: Fix broken cross references to `rfind` in strutils (#19382) [backport] Fixes three broken cross references to `rfind` in strutils. Breakage due to signature changes of the `rfind` methods. Co-authored-by: adigitoleo --- lib/pure/strutils.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index e4a4a5ec1..5e505ec3a 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -1865,7 +1865,7 @@ func find*(s: string, sub: char, start: Natural = 0, last = 0): int {.rtl, ## Use `s[start..last].rfind` for a `start`-origin index. ## ## See also: - ## * `rfind func<#rfind,string,char,Natural>`_ + ## * `rfind func<#rfind,string,char,Natural,int>`_ ## * `replace func<#replace,string,char,char>`_ let last = if last == 0: s.high else: last when nimvm: @@ -1893,7 +1893,7 @@ func find*(s: string, chars: set[char], start: Natural = 0, last = 0): int {. ## Use `s[start..last].find` for a `start`-origin index. ## ## See also: - ## * `rfind func<#rfind,string,set[char],Natural>`_ + ## * `rfind func<#rfind,string,set[char],Natural,int>`_ ## * `multiReplace func<#multiReplace,string,varargs[]>`_ let last = if last == 0: s.high else: last for i in int(start)..last: @@ -1910,7 +1910,7 @@ func find*(s, sub: string, start: Natural = 0, last = 0): int {.rtl, ## Use `s[start..last].find` for a `start`-origin index. ## ## See also: - ## * `rfind func<#rfind,string,string,Natural>`_ + ## * `rfind func<#rfind,string,string,Natural,int>`_ ## * `replace func<#replace,string,string,string>`_ if sub.len > s.len - start: return -1 if sub.len == 1: return find(s, sub[0], start, last) From 9b9ae8a487c6fbf77c8c72196e2b74f3371382b2 Mon Sep 17 00:00:00 2001 From: flywind Date: Fri, 14 Jan 2022 00:39:17 +0800 Subject: [PATCH 2759/3768] nrvo shouldn't touch bycopy object[backport:1.2] (#19385) fix nim-lang#19342 --- compiler/ccgtypes.nim | 2 +- tests/objects/m19342.c | 12 ++++++++++++ tests/objects/t19342.nim | 18 ++++++++++++++++++ 3 files changed, 31 insertions(+), 1 deletion(-) create mode 100644 tests/objects/m19342.c create mode 100644 tests/objects/t19342.nim diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 740ce5f19..df1874b26 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -220,7 +220,7 @@ proc isInvalidReturnType(conf: ConfigRef; rettype: PType): bool = # such a poor programming language. # We exclude records with refs too. This enhances efficiency and # is necessary for proper code generation of assignments. - if rettype == nil or getSize(conf, rettype) > conf.target.floatSize*3: + if rettype == nil or (tfByCopy notin rettype.flags and getSize(conf, rettype) > conf.target.floatSize*3): result = true else: case mapType(conf, rettype, skResult) diff --git a/tests/objects/m19342.c b/tests/objects/m19342.c new file mode 100644 index 000000000..113f65309 --- /dev/null +++ b/tests/objects/m19342.c @@ -0,0 +1,12 @@ +struct Node +{ + int data[25]; +}; + + +struct Node hello(int name) { + struct Node x = {999, 1, 2, 3, 4, 5, 6, 7, 8, 9, + 0, 1, 2, 3, 4, 5, 6, 7 ,8, 9, + 1, 2, 3, 4, 5}; + return x; +} \ No newline at end of file diff --git a/tests/objects/t19342.nim b/tests/objects/t19342.nim new file mode 100644 index 000000000..d40d15a4b --- /dev/null +++ b/tests/objects/t19342.nim @@ -0,0 +1,18 @@ +discard """ + targets: "c cpp" +""" + +{.compile: "m19342.c".} + +# bug #19342 +type + Node* {.bycopy.} = object + data: array[25, cint] + +proc myproc(name: cint): Node {.importc: "hello", cdecl.} + +proc parse = + let node = myproc(10) + doAssert node.data[0] == 999 + +parse() From a93f6e7acc9e02deb40a864d345e4c715346a98c Mon Sep 17 00:00:00 2001 From: Jason Beetham Date: Thu, 13 Jan 2022 09:39:55 -0700 Subject: [PATCH 2760/3768] Generic parameters now can constrain statics in type definitions (#19362) * Parameters now can constrain static in type definitions resolved regression with generic procedures * Update compiler/sigmatch.nim Co-authored-by: Andreas Rumpf --- compiler/sigmatch.nim | 14 +++++++-- tests/generics/tstatic_constrained.nim | 42 ++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 2 deletions(-) create mode 100644 tests/generics/tstatic_constrained.nim diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 93be92676..4d8e03fe9 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1739,11 +1739,22 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, let prev = PType(idTableGet(c.bindings, f)) if prev == nil: if aOrig.kind == tyStatic: - if f.base.kind != tyNone: + if f.base.kind notin {tyNone, tyGenericParam}: result = typeRel(c, f.base, a, flags) if result != isNone and f.n != nil: if not exprStructuralEquivalent(f.n, aOrig.n): result = isNone + elif f.base.kind == tyGenericParam: + # Handling things like `type A[T; Y: static T] = object` + if f.base.len > 0: # There is a constraint, handle it + result = typeRel(c, f.base.lastSon, a, flags) + else: + # No constraint + if tfGenericTypeParam in f.flags: + result = isGeneric + else: + # for things like `proc fun[T](a: static[T])` + result = typeRel(c, f.base, a, flags) else: result = isGeneric if result != isNone: put(c, f, aOrig) @@ -1993,7 +2004,6 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, arg = argSemantized a = a c = m.c - if tfHasStatic in fMaybeStatic.flags: # XXX: When implicit statics are the default # this will be done earlier - we just have to diff --git a/tests/generics/tstatic_constrained.nim b/tests/generics/tstatic_constrained.nim new file mode 100644 index 000000000..07318d1bd --- /dev/null +++ b/tests/generics/tstatic_constrained.nim @@ -0,0 +1,42 @@ +discard """ + cmd: "nim check --hints:off --warnings:off $file" + action: "reject" + nimout:''' +tstatic_constrained.nim(41, 20) Error: cannot instantiate MyOtherType [type declared in tstatic_constrained.nim(27, 3)] +got: +but expected: +tstatic_constrained.nim(41, 20) Error: cannot instantiate MyOtherType [type declared in tstatic_constrained.nim(27, 3)] +got: +but expected: +tstatic_constrained.nim(41, 29) Error: object constructor needs an object type [proxy] +tstatic_constrained.nim(41, 29) Error: expression '' has no type (or is ambiguous) +tstatic_constrained.nim(42, 20) Error: cannot instantiate MyOtherType [type declared in tstatic_constrained.nim(27, 3)] +got: +but expected: +tstatic_constrained.nim(42, 20) Error: cannot instantiate MyOtherType [type declared in tstatic_constrained.nim(27, 3)] +got: +but expected: +tstatic_constrained.nim(42, 32) Error: object constructor needs an object type [proxy] +tstatic_constrained.nim(42, 32) Error: expression '' has no type (or is ambiguous) +''' +""" + +type + MyType[T; X: static T] = object + data: T + MyOtherType[T: float or string, Y: static T] = object + +func f[T,X](a: MyType[T,X]): MyType[T,X] = + when T is string: + MyType[T,X](data: a.data & X) + else: + MyType[T,X](data: a.data + X) + +discard MyType[int, 2](data: 1) +discard MyType[string, "Helelello"](data: "Hmmm") +discard MyType[int, 2](data: 1).f() +discard MyType[string, "Helelello"](data: "Hmmm").f() +discard MyOtherType[float, 1.3]() +discard MyOtherType[string, "Hello"]() +discard MyOtherType[int, 10]() +discard MyOtherType[byte, 10u8]() \ No newline at end of file From 7bdfeb78190ca9ff6a0cf702c6ec202f379bff2f Mon Sep 17 00:00:00 2001 From: Jason Beetham Date: Sat, 15 Jan 2022 01:24:23 -0700 Subject: [PATCH 2761/3768] Fixed concept constraints for static types (#19391) --- compiler/sigmatch.nim | 9 +++ tests/generics/tstatic_constrained.nim | 87 ++++++++++++++++++-------- 2 files changed, 71 insertions(+), 25 deletions(-) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 4d8e03fe9..e1195d04a 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1634,6 +1634,15 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, bindConcreteTypeToUserTypeClass(matched, a) if doBind: put(c, f, matched) result = isGeneric + elif a.len > 0 and a.lastSon == f: + # Needed for checking `Y` == `Addable` in the following + #[ + type + Addable = concept a, type A + a + a is A + MyType[T: Addable; Y: static T] = object + ]# + result = isGeneric else: result = isNone diff --git a/tests/generics/tstatic_constrained.nim b/tests/generics/tstatic_constrained.nim index 07318d1bd..3c9201548 100644 --- a/tests/generics/tstatic_constrained.nim +++ b/tests/generics/tstatic_constrained.nim @@ -2,41 +2,78 @@ discard """ cmd: "nim check --hints:off --warnings:off $file" action: "reject" nimout:''' -tstatic_constrained.nim(41, 20) Error: cannot instantiate MyOtherType [type declared in tstatic_constrained.nim(27, 3)] +tstatic_constrained.nim(44, 22) Error: cannot instantiate MyOtherType [type declared in tstatic_constrained.nim(30, 5)] got: but expected: -tstatic_constrained.nim(41, 20) Error: cannot instantiate MyOtherType [type declared in tstatic_constrained.nim(27, 3)] +tstatic_constrained.nim(44, 22) Error: cannot instantiate MyOtherType [type declared in tstatic_constrained.nim(30, 5)] got: but expected: -tstatic_constrained.nim(41, 29) Error: object constructor needs an object type [proxy] -tstatic_constrained.nim(41, 29) Error: expression '' has no type (or is ambiguous) -tstatic_constrained.nim(42, 20) Error: cannot instantiate MyOtherType [type declared in tstatic_constrained.nim(27, 3)] +tstatic_constrained.nim(44, 31) Error: object constructor needs an object type [proxy] +tstatic_constrained.nim(44, 31) Error: expression '' has no type (or is ambiguous) +tstatic_constrained.nim(45, 22) Error: cannot instantiate MyOtherType [type declared in tstatic_constrained.nim(30, 5)] got: but expected: -tstatic_constrained.nim(42, 20) Error: cannot instantiate MyOtherType [type declared in tstatic_constrained.nim(27, 3)] +tstatic_constrained.nim(45, 22) Error: cannot instantiate MyOtherType [type declared in tstatic_constrained.nim(30, 5)] got: but expected: -tstatic_constrained.nim(42, 32) Error: object constructor needs an object type [proxy] -tstatic_constrained.nim(42, 32) Error: expression '' has no type (or is ambiguous) +tstatic_constrained.nim(45, 34) Error: object constructor needs an object type [proxy] +tstatic_constrained.nim(45, 34) Error: expression '' has no type (or is ambiguous) +tstatic_constrained.nim(77, 14) Error: cannot instantiate MyType [type declared in tstatic_constrained.nim(71, 5)] +got: +but expected: ''' """ +block: + type + MyType[T; X: static T] = object + data: T + MyOtherType[T: float or string, Y: static T] = object -type - MyType[T; X: static T] = object - data: T - MyOtherType[T: float or string, Y: static T] = object + func f[T,X](a: MyType[T,X]): MyType[T,X] = + when T is string: + MyType[T,X](data: a.data & X) + else: + MyType[T,X](data: a.data + X) -func f[T,X](a: MyType[T,X]): MyType[T,X] = - when T is string: - MyType[T,X](data: a.data & X) - else: - MyType[T,X](data: a.data + X) + discard MyType[int, 2](data: 1) + discard MyType[string, "Helelello"](data: "Hmmm") + discard MyType[int, 2](data: 1).f() + discard MyType[string, "Helelello"](data: "Hmmm").f() + discard MyOtherType[float, 1.3]() + discard MyOtherType[string, "Hello"]() + discard MyOtherType[int, 10]() + discard MyOtherType[byte, 10u8]() -discard MyType[int, 2](data: 1) -discard MyType[string, "Helelello"](data: "Hmmm") -discard MyType[int, 2](data: 1).f() -discard MyType[string, "Helelello"](data: "Hmmm").f() -discard MyOtherType[float, 1.3]() -discard MyOtherType[string, "Hello"]() -discard MyOtherType[int, 10]() -discard MyOtherType[byte, 10u8]() \ No newline at end of file +block: + type + Moduloable = concept m, type M + m mod m is M + Addable = concept a, type A + a + a is A + Modulo[T: Moduloable; Mod: static T] = distinct T + ModuloAdd[T: Moduloable or Addable; Mod: static T] = distinct T + ModuAddable = Addable or Moduloable + ModdAddClass[T: ModuAddable; Mod: static T] = distinct T + + proc toMod[T](val: T, modVal: static T): Modulo[T, modVal] = + mixin `mod` + Modulo[T, modVal](val mod modVal) + var + a = 3231.toMod(10) + b = 5483.toMod(10) + discard ModuloAdd[int, 3](0) + discard ModdAddClass[int, 3](0) + +block: + type + MyConstraint = int or string + MyOtherConstraint[T] = object + MyType[T: MyConstraint; Y: static T] = object + MyOtherType[T: MyOtherConstraint; Y: static T] = object + + var + a: MyType[int, 10] + b: MyType[string, "hello"] + c: MyType[float, 10d] + d: MyOtherType[MyOtherConstraint[float],MyOtherConstraint[float]()] + e: MyOtherType[MyOtherConstraint[int], MyOtherConstraint[int]()] \ No newline at end of file From 342b74ef70e6a71c09ba536d20e23a7e887d2fb8 Mon Sep 17 00:00:00 2001 From: flywind Date: Sat, 15 Jan 2022 18:25:09 +0800 Subject: [PATCH 2762/3768] move type operation section and remove deepcopy document (#19389) ref #19173; because deepcopy is not fit for ORC/ARC which was used for spawn and spawn will be removed from compiler --- doc/manual.rst | 12 +++++++++ doc/manual_experimental.rst | 54 ++++++++++--------------------------- 2 files changed, 26 insertions(+), 40 deletions(-) diff --git a/doc/manual.rst b/doc/manual.rst index 8788e53a3..007b14b2d 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -3902,6 +3902,18 @@ the operator is in scope (including if it is private). Type bound operators are: `=destroy`, `=copy`, `=sink`, `=trace`, `=deepcopy`. +These operations can be *overridden* instead of *overloaded*. This means that +the implementation is automatically lifted to structured types. For instance, +if the type `T` has an overridden assignment operator `=`, this operator is +also used for assignments of the type `seq[T]`. + +Since these operations are bound to a type, they have to be bound to a +nominal type for reasons of simplicity of implementation; this means an +overridden `deepCopy` for `ref T` is really bound to `T` and not to `ref T`. +This also means that one cannot override `deepCopy` for both `ptr T` and +`ref T` at the same time, instead a distinct or object helper type has to be +used for one pointer type. + For more details on some of those procs, see `Lifetime-tracking hooks `_. diff --git a/doc/manual_experimental.rst b/doc/manual_experimental.rst index 0f82b9950..10958c29a 100644 --- a/doc/manual_experimental.rst +++ b/doc/manual_experimental.rst @@ -1192,51 +1192,25 @@ object inheritance syntax involving the `of` keyword: the `vtptr` magic produced types bound to `ptr` types. -Type bound operations -===================== +.. + deepCopy + -------- + `=deepCopy` is a builtin that is invoked whenever data is passed to + a `spawn`'ed proc to ensure memory safety. The programmer can override its + behaviour for a specific `ref` or `ptr` type `T`. (Later versions of the + language may weaken this restriction.) -There are 4 operations that are bound to a type: + The signature has to be: -1. Assignment -2. Moves -3. Destruction -4. Deep copying for communication between threads + .. code-block:: nim -These operations can be *overridden* instead of *overloaded*. This means that -the implementation is automatically lifted to structured types. For instance, -if the type `T` has an overridden assignment operator `=`, this operator is -also used for assignments of the type `seq[T]`. + proc `=deepCopy`(x: T): T -Since these operations are bound to a type, they have to be bound to a -nominal type for reasons of simplicity of implementation; this means an -overridden `deepCopy` for `ref T` is really bound to `T` and not to `ref T`. -This also means that one cannot override `deepCopy` for both `ptr T` and -`ref T` at the same time, instead a distinct or object helper type has to be -used for one pointer type. + This mechanism will be used by most data structures that support shared memory, + like channels, to implement thread safe automatic memory management. -Assignments, moves and destruction are specified in -the `destructors `_ document. - - -deepCopy --------- - -`=deepCopy` is a builtin that is invoked whenever data is passed to -a `spawn`'ed proc to ensure memory safety. The programmer can override its -behaviour for a specific `ref` or `ptr` type `T`. (Later versions of the -language may weaken this restriction.) - -The signature has to be: - -.. code-block:: nim - - proc `=deepCopy`(x: T): T - -This mechanism will be used by most data structures that support shared memory, -like channels, to implement thread safe automatic memory management. - -The builtin `deepCopy` can even clone closures and their environments. See -the documentation of `spawn <#parallel-amp-spawn-spawn-statement>`_ for details. + The builtin `deepCopy` can even clone closures and their environments. See + the documentation of `spawn <#parallel-amp-spawn-spawn-statement>`_ for details. Dynamic arguments for bindSym From a95399143fdbd518f4d5fe33487b656c4cde7d6d Mon Sep 17 00:00:00 2001 From: hlaaftana <10591326+hlaaftana@users.noreply.github.com> Date: Sat, 15 Jan 2022 15:19:05 +0300 Subject: [PATCH 2763/3768] Use openarray of bytes in md5 (#19307) * Use openarray of bytes in md5 * fix CI * cleanup * use noSideEffect for bootstrapping * fix CI again * actually fix CI by checking if it works * this is getting ridiculous * put old md5 version in compiler, remove vmop --- changelog.md | 1 + compiler/md5_old.nim | 297 +++++++++++++++++++++++++++++++++++ compiler/modulegraphs.nim | 2 +- compiler/sighashes.nim | 2 +- compiler/vmops.nim | 6 - lib/pure/md5.nim | 131 +++++++++++---- tests/js/tstdlib_imports.nim | 3 +- tests/stdlib/tmd5.nim | 19 ++- tests/test_nimscript.nims | 3 +- 9 files changed, 413 insertions(+), 51 deletions(-) create mode 100644 compiler/md5_old.nim diff --git a/changelog.md b/changelog.md index d07f80623..4d27be3a0 100644 --- a/changelog.md +++ b/changelog.md @@ -23,6 +23,7 @@ filename argument for more informative errors. - Module `colors` expanded with missing colors from the CSS color standard. - Fixed `lists.SinglyLinkedList` being broken after removing the last node ([#19353](https://github.com/nim-lang/Nim/pull/19353)). +- `md5` now works at compile time and in JavaScript. ## `std/smtp` diff --git a/compiler/md5_old.nim b/compiler/md5_old.nim new file mode 100644 index 000000000..da4a51198 --- /dev/null +++ b/compiler/md5_old.nim @@ -0,0 +1,297 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2010 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +# `std/md5` without VM and JavaScript support, to circumvent a bug with +# openarrays on Nim < 1.4. + +when defined(nimHasStyleChecks): + {.push styleChecks: off.} + +type + MD5State = array[0..3, uint32] + MD5Block = array[0..15, uint32] + MD5CBits = array[0..7, uint8] + MD5Digest* = array[0..15, uint8] + ## MD5 checksum of a string, obtained with the `toMD5 proc <#toMD5,string>`_. + MD5Buffer = array[0..63, uint8] + MD5Context* {.final.} = object + state: MD5State + count: array[0..1, uint32] + buffer: MD5Buffer + +const + padding: array[0..63, uint8] = [ + 0x80'u8, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 + ] + +proc F(x, y, z: uint32): uint32 {.inline.} = + result = (x and y) or ((not x) and z) + +proc G(x, y, z: uint32): uint32 {.inline.} = + result = (x and z) or (y and (not z)) + +proc H(x, y, z: uint32): uint32 {.inline.} = + result = x xor y xor z + +proc I(x, y, z: uint32): uint32 {.inline.} = + result = y xor (x or (not z)) + +proc rot(x: var uint32, n: uint8) {.inline.} = + x = (x shl n) or (x shr (32'u32 - n)) + +proc FF(a: var uint32, b, c, d, x: uint32, s: uint8, ac: uint32) = + a = a + F(b, c, d) + x + ac + rot(a, s) + a = a + b + +proc GG(a: var uint32, b, c, d, x: uint32, s: uint8, ac: uint32) = + a = a + G(b, c, d) + x + ac + rot(a, s) + a = a + b + +proc HH(a: var uint32, b, c, d, x: uint32, s: uint8, ac: uint32) = + a = a + H(b, c, d) + x + ac + rot(a, s) + a = a + b + +proc II(a: var uint32, b, c, d, x: uint32, s: uint8, ac: uint32) = + a = a + I(b, c, d) + x + ac + rot(a, s) + a = a + b + +proc encode(dest: var MD5Block, src: openArray[uint8]) = + var j = 0 + for i in 0..high(dest): + dest[i] = uint32(ord(src[j])) or + uint32(ord(src[j+1])) shl 8 or + uint32(ord(src[j+2])) shl 16 or + uint32(ord(src[j+3])) shl 24 + inc(j, 4) + +proc decode(dest: var openArray[uint8], src: openArray[uint32]) = + var i = 0 + for j in 0..high(src): + dest[i] = uint8(src[j] and 0xff'u32) + dest[i+1] = uint8(src[j] shr 8 and 0xff'u32) + dest[i+2] = uint8(src[j] shr 16 and 0xff'u32) + dest[i+3] = uint8(src[j] shr 24 and 0xff'u32) + inc(i, 4) + +template slice(s: string | cstring, a, b): openArray[uint8] = + s.toOpenArrayByte(a, b) + +template slice(s: openArray[uint8], a, b): openArray[uint8] = + s.toOpenArray(a, b) + +proc transform(buffer: openArray[uint8], state: var MD5State) = + var + myBlock: MD5Block + encode(myBlock, buffer) + var a = state[0] + var b = state[1] + var c = state[2] + var d = state[3] + FF(a, b, c, d, myBlock[0], 7'u8, 0xD76AA478'u32) + FF(d, a, b, c, myBlock[1], 12'u8, 0xE8C7B756'u32) + FF(c, d, a, b, myBlock[2], 17'u8, 0x242070DB'u32) + FF(b, c, d, a, myBlock[3], 22'u8, 0xC1BDCEEE'u32) + FF(a, b, c, d, myBlock[4], 7'u8, 0xF57C0FAF'u32) + FF(d, a, b, c, myBlock[5], 12'u8, 0x4787C62A'u32) + FF(c, d, a, b, myBlock[6], 17'u8, 0xA8304613'u32) + FF(b, c, d, a, myBlock[7], 22'u8, 0xFD469501'u32) + FF(a, b, c, d, myBlock[8], 7'u8, 0x698098D8'u32) + FF(d, a, b, c, myBlock[9], 12'u8, 0x8B44F7AF'u32) + FF(c, d, a, b, myBlock[10], 17'u8, 0xFFFF5BB1'u32) + FF(b, c, d, a, myBlock[11], 22'u8, 0x895CD7BE'u32) + FF(a, b, c, d, myBlock[12], 7'u8, 0x6B901122'u32) + FF(d, a, b, c, myBlock[13], 12'u8, 0xFD987193'u32) + FF(c, d, a, b, myBlock[14], 17'u8, 0xA679438E'u32) + FF(b, c, d, a, myBlock[15], 22'u8, 0x49B40821'u32) + GG(a, b, c, d, myBlock[1], 5'u8, 0xF61E2562'u32) + GG(d, a, b, c, myBlock[6], 9'u8, 0xC040B340'u32) + GG(c, d, a, b, myBlock[11], 14'u8, 0x265E5A51'u32) + GG(b, c, d, a, myBlock[0], 20'u8, 0xE9B6C7AA'u32) + GG(a, b, c, d, myBlock[5], 5'u8, 0xD62F105D'u32) + GG(d, a, b, c, myBlock[10], 9'u8, 0x02441453'u32) + GG(c, d, a, b, myBlock[15], 14'u8, 0xD8A1E681'u32) + GG(b, c, d, a, myBlock[4], 20'u8, 0xE7D3FBC8'u32) + GG(a, b, c, d, myBlock[9], 5'u8, 0x21E1CDE6'u32) + GG(d, a, b, c, myBlock[14], 9'u8, 0xC33707D6'u32) + GG(c, d, a, b, myBlock[3], 14'u8, 0xF4D50D87'u32) + GG(b, c, d, a, myBlock[8], 20'u8, 0x455A14ED'u32) + GG(a, b, c, d, myBlock[13], 5'u8, 0xA9E3E905'u32) + GG(d, a, b, c, myBlock[2], 9'u8, 0xFCEFA3F8'u32) + GG(c, d, a, b, myBlock[7], 14'u8, 0x676F02D9'u32) + GG(b, c, d, a, myBlock[12], 20'u8, 0x8D2A4C8A'u32) + HH(a, b, c, d, myBlock[5], 4'u8, 0xFFFA3942'u32) + HH(d, a, b, c, myBlock[8], 11'u8, 0x8771F681'u32) + HH(c, d, a, b, myBlock[11], 16'u8, 0x6D9D6122'u32) + HH(b, c, d, a, myBlock[14], 23'u8, 0xFDE5380C'u32) + HH(a, b, c, d, myBlock[1], 4'u8, 0xA4BEEA44'u32) + HH(d, a, b, c, myBlock[4], 11'u8, 0x4BDECFA9'u32) + HH(c, d, a, b, myBlock[7], 16'u8, 0xF6BB4B60'u32) + HH(b, c, d, a, myBlock[10], 23'u8, 0xBEBFBC70'u32) + HH(a, b, c, d, myBlock[13], 4'u8, 0x289B7EC6'u32) + HH(d, a, b, c, myBlock[0], 11'u8, 0xEAA127FA'u32) + HH(c, d, a, b, myBlock[3], 16'u8, 0xD4EF3085'u32) + HH(b, c, d, a, myBlock[6], 23'u8, 0x04881D05'u32) + HH(a, b, c, d, myBlock[9], 4'u8, 0xD9D4D039'u32) + HH(d, a, b, c, myBlock[12], 11'u8, 0xE6DB99E5'u32) + HH(c, d, a, b, myBlock[15], 16'u8, 0x1FA27CF8'u32) + HH(b, c, d, a, myBlock[2], 23'u8, 0xC4AC5665'u32) + II(a, b, c, d, myBlock[0], 6'u8, 0xF4292244'u32) + II(d, a, b, c, myBlock[7], 10'u8, 0x432AFF97'u32) + II(c, d, a, b, myBlock[14], 15'u8, 0xAB9423A7'u32) + II(b, c, d, a, myBlock[5], 21'u8, 0xFC93A039'u32) + II(a, b, c, d, myBlock[12], 6'u8, 0x655B59C3'u32) + II(d, a, b, c, myBlock[3], 10'u8, 0x8F0CCC92'u32) + II(c, d, a, b, myBlock[10], 15'u8, 0xFFEFF47D'u32) + II(b, c, d, a, myBlock[1], 21'u8, 0x85845DD1'u32) + II(a, b, c, d, myBlock[8], 6'u8, 0x6FA87E4F'u32) + II(d, a, b, c, myBlock[15], 10'u8, 0xFE2CE6E0'u32) + II(c, d, a, b, myBlock[6], 15'u8, 0xA3014314'u32) + II(b, c, d, a, myBlock[13], 21'u8, 0x4E0811A1'u32) + II(a, b, c, d, myBlock[4], 6'u8, 0xF7537E82'u32) + II(d, a, b, c, myBlock[11], 10'u8, 0xBD3AF235'u32) + II(c, d, a, b, myBlock[2], 15'u8, 0x2AD7D2BB'u32) + II(b, c, d, a, myBlock[9], 21'u8, 0xEB86D391'u32) + state[0] = state[0] + a + state[1] = state[1] + b + state[2] = state[2] + c + state[3] = state[3] + d + +proc md5Init*(c: var MD5Context) {.raises: [], tags: [], gcsafe.} +proc md5Update*(c: var MD5Context, input: openArray[uint8]) {.raises: [], + tags: [], gcsafe.} +proc md5Final*(c: var MD5Context, digest: var MD5Digest) {.raises: [], tags: [], gcsafe.} + +proc md5Update*(c: var MD5Context, input: cstring, len: int) {.raises: [], + tags: [], gcsafe.} = + ## Updates the `MD5Context` with the `input` data of length `len`. + ## + ## If you use the `toMD5 proc <#toMD5,string>`_, there's no need to call this + ## function explicitly. + md5Update(c, input.slice(0, len - 1)) + + +proc toMD5*(s: string): MD5Digest = + ## Computes the `MD5Digest` value for a string `s`. + ## + ## **See also:** + ## * `getMD5 proc <#getMD5,string>`_ which returns a string representation + ## of the `MD5Digest` + ## * `$ proc <#$,MD5Digest>`_ for converting MD5Digest to string + runnableExamples: + assert $toMD5("abc") == "900150983cd24fb0d6963f7d28e17f72" + + var c: MD5Context + md5Init(c) + md5Update(c, s.slice(0, s.len - 1)) + md5Final(c, result) + +proc `$`*(d: MD5Digest): string = + ## Converts a `MD5Digest` value into its string representation. + const digits = "0123456789abcdef" + result = "" + for i in 0..15: + add(result, digits[(d[i].int shr 4) and 0xF]) + add(result, digits[d[i].int and 0xF]) + +proc getMD5*(s: string): string = + ## Computes an MD5 value of `s` and returns its string representation. + ## + ## **See also:** + ## * `toMD5 proc <#toMD5,string>`_ which returns the `MD5Digest` of a string + runnableExamples: + assert getMD5("abc") == "900150983cd24fb0d6963f7d28e17f72" + + var + c: MD5Context + d: MD5Digest + md5Init(c) + md5Update(c, s.slice(0, s.len - 1)) + md5Final(c, d) + result = $d + +proc `==`*(D1, D2: MD5Digest): bool = + ## Checks if two `MD5Digest` values are identical. + for i in 0..15: + if D1[i] != D2[i]: return false + return true + + +proc clearBuffer(c: var MD5Context) {.inline.} = + zeroMem(addr(c.buffer), sizeof(MD5Buffer)) + +proc md5Init*(c: var MD5Context) = + ## Initializes an `MD5Context`. + ## + ## If you use the `toMD5 proc <#toMD5,string>`_, there's no need to call this + ## function explicitly. + c.state[0] = 0x67452301'u32 + c.state[1] = 0xEFCDAB89'u32 + c.state[2] = 0x98BADCFE'u32 + c.state[3] = 0x10325476'u32 + c.count[0] = 0'u32 + c.count[1] = 0'u32 + clearBuffer(c) + +proc writeBuffer(c: var MD5Context, index: int, + input: openArray[uint8], inputIndex, len: int) {.inline.} = + copyMem(addr(c.buffer[index]), unsafeAddr(input[inputIndex]), len) + +proc md5Update*(c: var MD5Context, input: openArray[uint8]) = + ## Updates the `MD5Context` with the `input` data. + ## + ## If you use the `toMD5 proc <#toMD5,string>`_, there's no need to call this + ## function explicitly. + var Index = int((c.count[0] shr 3) and 0x3F) + c.count[0] = c.count[0] + (uint32(input.len) shl 3) + if c.count[0] < (uint32(input.len) shl 3): c.count[1] = c.count[1] + 1'u32 + c.count[1] = c.count[1] + (uint32(input.len) shr 29) + var PartLen = 64 - Index + if input.len >= PartLen: + writeBuffer(c, Index, input, 0, PartLen) + transform(c.buffer, c.state) + var i = PartLen + while i + 63 < input.len: + transform(input.slice(i, i + 63), c.state) + inc(i, 64) + if i < input.len: + writeBuffer(c, 0, input, i, input.len - i) + elif input.len > 0: + writeBuffer(c, Index, input, 0, input.len) + +proc md5Final*(c: var MD5Context, digest: var MD5Digest) = + ## Finishes the `MD5Context` and stores the result in `digest`. + ## + ## If you use the `toMD5 proc <#toMD5,string>`_, there's no need to call this + ## function explicitly. + var + Bits: MD5CBits + PadLen: int + decode(Bits, c.count) + var Index = int((c.count[0] shr 3) and 0x3F) + if Index < 56: PadLen = 56 - Index + else: PadLen = 120 - Index + md5Update(c, padding.slice(0, PadLen - 1)) + md5Update(c, Bits) + decode(digest, c.state) + clearBuffer(c) + + +when defined(nimHasStyleChecks): + {.pop.} #{.push styleChecks: off.} diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index 90b130e92..28e0ddb82 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -11,7 +11,7 @@ ## represents a complete Nim project. Single modules can either be kept in RAM ## or stored in a rod-file. -import intsets, tables, hashes, md5 +import intsets, tables, hashes, md5_old import ast, astalgo, options, lineinfos,idents, btrees, ropes, msgs, pathutils import ic / [packed_ast, ic] diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim index d02728627..3d36d2533 100644 --- a/compiler/sighashes.nim +++ b/compiler/sighashes.nim @@ -9,7 +9,7 @@ ## Computes hash values for routine (proc, method etc) signatures. -import ast, tables, ropes, md5, modulegraphs +import ast, tables, ropes, md5_old, modulegraphs from hashes import Hash import types diff --git a/compiler/vmops.nim b/compiler/vmops.nim index b9801234d..22a632ee9 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -25,7 +25,6 @@ when declared(math.signbit): from std/os import getEnv, existsEnv, delEnv, putEnv, envPairs, dirExists, fileExists, walkDir, getAppFilename, raiseOSError, osLastError -from std/md5 import getMD5 from std/times import cpuTime from std/hashes import hash from std/osproc import nil @@ -53,9 +52,6 @@ template ioop(op) {.dirty.} = template macrosop(op) {.dirty.} = registerCallback(c, "stdlib.macros." & astToStr(op), `op Wrapper`) -template md5op(op) {.dirty.} = - registerCallback(c, "stdlib.md5." & astToStr(op), `op Wrapper`) - template wrap1f_math(op) {.dirty.} = proc `op Wrapper`(a: VmArgs) {.nimcall.} = doAssert a.numArgs == 1 @@ -212,8 +208,6 @@ proc registerAdditionalOps*(c: PCtx) = of 2: setResult(a, round(getFloat(a, 0), getInt(a, 1).int)) else: doAssert false, $n - wrap1s(getMD5, md5op) - proc `mod Wrapper`(a: VmArgs) {.nimcall.} = setResult(a, `mod`(getFloat(a, 0), getFloat(a, 1))) registerCallback(c, "stdlib.math.mod", `mod Wrapper`) diff --git a/lib/pure/md5.nim b/lib/pure/md5.nim index 11c324548..06c5608a4 100644 --- a/lib/pure/md5.nim +++ b/lib/pure/md5.nim @@ -9,7 +9,7 @@ ## Module for computing [MD5 checksums](https://en.wikipedia.org/wiki/MD5). ## -## **Note:** The procs in this module can be used at compile time. +## This module also works at compile time and in JavaScript. ## ## See also ## ======== @@ -34,15 +34,16 @@ type buffer: MD5Buffer const - padding: cstring = "\x80\0\0\0" & - "\0\0\0\0\0\0\0\0" & - "\0\0\0\0\0\0\0\0" & - "\0\0\0\0\0\0\0\0" & - "\0\0\0\0\0\0\0\0" & - "\0\0\0\0\0\0\0\0" & - "\0\0\0\0\0\0\0\0" & - "\0\0\0\0\0\0\0\0" & - "\0\0\0\0" + padding: array[0..63, uint8] = [ + 0x80'u8, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 + ] proc F(x, y, z: uint32): uint32 {.inline.} = result = (x and y) or ((not x) and z) @@ -79,7 +80,7 @@ proc II(a: var uint32, b, c, d, x: uint32, s: uint8, ac: uint32) = rot(a, s) a = a + b -proc encode(dest: var MD5Block, src: cstring) = +proc encode(dest: var MD5Block, src: openArray[uint8]) = var j = 0 for i in 0..high(dest): dest[i] = uint32(ord(src[j])) or @@ -97,10 +98,48 @@ proc decode(dest: var openArray[uint8], src: openArray[uint32]) = dest[i+3] = uint8(src[j] shr 24 and 0xff'u32) inc(i, 4) -proc transform(buffer: pointer, state: var MD5State) = +template slice(s: string, a, b): openArray[uint8] = + when nimvm: + # toOpenArray is not implemented in VM + var s2 = newSeq[uint8](s.len) + for i in 0 ..< s2.len: + s2[i] = uint8(s[i]) + s2 + else: + s.toOpenArrayByte(a, b) + +template slice(s: cstring, a, b): openArray[uint8] = + when nimvm: + # toOpenArray is not implemented in VM + slice($s, a, b) + else: + when defined(js): + # toOpenArrayByte for cstring is not implemented in JS + slice($s, a, b) + else: + s.toOpenArrayByte(a, b) + +template slice(s: openArray[uint8], a, b): openArray[uint8] = + when nimvm: + s[a .. b] + else: + s.toOpenArray(a, b) + +const useMem = declared(copyMem) + +template memOrNot(withMem, withoutMem): untyped = + when nimvm: + withoutMem + else: + when useMem: + withMem + else: + withoutMem + +proc transform(buffer: openArray[uint8], state: var MD5State) = var myBlock: MD5Block - encode(myBlock, cast[cstring](buffer)) + encode(myBlock, buffer) var a = state[0] var b = state[1] var c = state[2] @@ -175,10 +214,18 @@ proc transform(buffer: pointer, state: var MD5State) = state[3] = state[3] + d proc md5Init*(c: var MD5Context) {.raises: [], tags: [], gcsafe.} -proc md5Update*(c: var MD5Context, input: cstring, len: int) {.raises: [], +proc md5Update*(c: var MD5Context, input: openArray[uint8]) {.raises: [], tags: [], gcsafe.} proc md5Final*(c: var MD5Context, digest: var MD5Digest) {.raises: [], tags: [], gcsafe.} +proc md5Update*(c: var MD5Context, input: cstring, len: int) {.raises: [], + tags: [], gcsafe.} = + ## Updates the `MD5Context` with the `input` data of length `len`. + ## + ## If you use the `toMD5 proc <#toMD5,string>`_, there's no need to call this + ## function explicitly. + md5Update(c, input.slice(0, len - 1)) + proc toMD5*(s: string): MD5Digest = ## Computes the `MD5Digest` value for a string `s`. @@ -192,7 +239,7 @@ proc toMD5*(s: string): MD5Digest = var c: MD5Context md5Init(c) - md5Update(c, cstring(s), len(s)) + md5Update(c, s.slice(0, s.len - 1)) md5Final(c, result) proc `$`*(d: MD5Digest): string = @@ -215,7 +262,7 @@ proc getMD5*(s: string): string = c: MD5Context d: MD5Digest md5Init(c) - md5Update(c, cstring(s), len(s)) + md5Update(c, s.slice(0, s.len - 1)) md5Final(c, d) result = $d @@ -226,6 +273,12 @@ proc `==`*(D1, D2: MD5Digest): bool = return true +proc clearBuffer(c: var MD5Context) {.inline.} = + memOrNot: + zeroMem(addr(c.buffer), sizeof(MD5Buffer)) + do: + reset(c.buffer) + proc md5Init*(c: var MD5Context) = ## Initializes an `MD5Context`. ## @@ -237,29 +290,39 @@ proc md5Init*(c: var MD5Context) = c.state[3] = 0x10325476'u32 c.count[0] = 0'u32 c.count[1] = 0'u32 - zeroMem(addr(c.buffer), sizeof(MD5Buffer)) + clearBuffer(c) -proc md5Update*(c: var MD5Context, input: cstring, len: int) = - ## Updates the `MD5Context` with the `input` data of length `len`. +proc writeBuffer(c: var MD5Context, index: int, + input: openArray[uint8], inputIndex, len: int) {.inline.} = + memOrNot: + copyMem(addr(c.buffer[index]), unsafeAddr(input[inputIndex]), len) + do: + # cannot use system.`[]=` for arrays and openarrays as + # it can raise RangeDefect which gets tracked + for i in 0..`_, there's no need to call this ## function explicitly. - var input = input var Index = int((c.count[0] shr 3) and 0x3F) - c.count[0] = c.count[0] + (uint32(len) shl 3) - if c.count[0] < (uint32(len) shl 3): c.count[1] = c.count[1] + 1'u32 - c.count[1] = c.count[1] + (uint32(len) shr 29) + c.count[0] = c.count[0] + (uint32(input.len) shl 3) + if c.count[0] < (uint32(input.len) shl 3): c.count[1] = c.count[1] + 1'u32 + c.count[1] = c.count[1] + (uint32(input.len) shr 29) var PartLen = 64 - Index - if len >= PartLen: - copyMem(addr(c.buffer[Index]), input, PartLen) - transform(addr(c.buffer), c.state) + if input.len >= PartLen: + writeBuffer(c, Index, input, 0, PartLen) + transform(c.buffer, c.state) var i = PartLen - while i + 63 < len: - transform(addr(input[i]), c.state) + while i + 63 < input.len: + transform(input.slice(i, i + 63), c.state) inc(i, 64) - copyMem(addr(c.buffer[0]), addr(input[i]), len-i) - else: - copyMem(addr(c.buffer[Index]), addr(input[0]), len) + if i < input.len: + writeBuffer(c, 0, input, i, input.len - i) + elif input.len > 0: + writeBuffer(c, Index, input, 0, input.len) proc md5Final*(c: var MD5Context, digest: var MD5Digest) = ## Finishes the `MD5Context` and stores the result in `digest`. @@ -273,10 +336,10 @@ proc md5Final*(c: var MD5Context, digest: var MD5Digest) = var Index = int((c.count[0] shr 3) and 0x3F) if Index < 56: PadLen = 56 - Index else: PadLen = 120 - Index - md5Update(c, padding, PadLen) - md5Update(c, cast[cstring](addr(Bits)), 8) + md5Update(c, padding.slice(0, PadLen - 1)) + md5Update(c, Bits) decode(digest, c.state) - zeroMem(addr(c), sizeof(MD5Context)) + clearBuffer(c) when defined(nimHasStyleChecks): diff --git a/tests/js/tstdlib_imports.nim b/tests/js/tstdlib_imports.nim index 9986fa059..30aca7561 100644 --- a/tests/js/tstdlib_imports.nim +++ b/tests/js/tstdlib_imports.nim @@ -57,8 +57,7 @@ import std/[ htmlgen, # Hashing: - base64, hashes, - # fails due to cstring cast/zeroMem/copyMem/moveMem: md5 + base64, hashes, md5, # fails due to cstring cast/endians import: oids # fails due to copyMem/endians import: sha1 diff --git a/tests/stdlib/tmd5.nim b/tests/stdlib/tmd5.nim index 88a7b8d37..4017ac677 100644 --- a/tests/stdlib/tmd5.nim +++ b/tests/stdlib/tmd5.nim @@ -1,7 +1,16 @@ +discard """ + targets: "c cpp js" +""" + import md5 -doAssert(getMD5("Franz jagt im komplett verwahrlosten Taxi quer durch Bayern") == - "a3cca2b2aa1e3b5b3b5aad99a8529074") -doAssert(getMD5("Frank jagt im komplett verwahrlosten Taxi quer durch Bayern") == - "7e716d0e702df0505fc72e2b89467910") -doAssert($toMD5("") == "d41d8cd98f00b204e9800998ecf8427e") +proc main() {.raises: [].} = + doAssert(getMD5("Franz jagt im komplett verwahrlosten Taxi quer durch Bayern") == + "a3cca2b2aa1e3b5b3b5aad99a8529074") + doAssert(getMD5("Frank jagt im komplett verwahrlosten Taxi quer durch Bayern") == + "7e716d0e702df0505fc72e2b89467910") + doAssert($toMD5("") == "d41d8cd98f00b204e9800998ecf8427e") + +main() + +static: main() diff --git a/tests/test_nimscript.nims b/tests/test_nimscript.nims index 761ddafdf..22a566307 100644 --- a/tests/test_nimscript.nims +++ b/tests/test_nimscript.nims @@ -59,8 +59,7 @@ import std/[ htmlgen, # Hashing: - base64, hashes, - # fails due to cstring cast/zeroMem/copyMem/moveMem: md5 + base64, hashes, md5, # fails due to cstring cast/times import/endians import: oids # fails due to copyMem/endians import: sha1 From d102b2f54c41f19a0545f28109d0a93550b5d886 Mon Sep 17 00:00:00 2001 From: flywind Date: Sun, 16 Jan 2022 18:08:38 +0800 Subject: [PATCH 2764/3768] deprecate unsafeAddr; extend addr (#19373) * deprecate unsafeAddr; extend addr addr is now available for all addressable locations, unsafeAddr is deprecated and become an alias for addr * follow @Vindaar's advice * change the signature of addr * unsafeAddr => addr (stdlib) * Update changelog.md * unsafeAddr => addr (tests) * Revert "unsafeAddr => addr (stdlib)" This reverts commit ab83c99c507048a8396e636bf22d55fdd84d7d1c. * doc changes; thanks to @konsumlamm Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> --- changelog.md | 3 +++ compiler/isolation_check.nim | 2 +- compiler/semmagic.nim | 9 ++----- doc/manual.rst | 15 ++++------- doc/manual_experimental.rst | 2 +- lib/system.nim | 26 +++++++++++++------ tests/arc/tcustomtrace.nim | 2 +- tests/arc/thard_alignment.nim | 2 +- tests/async/tioselectors.nim | 2 +- tests/ccgbugs/tnoalias.nim | 2 +- tests/compiles/t8630.nim | 2 +- tests/concepts/tconcepts_issues.nim | 4 +-- tests/destructor/t12037.nim | 6 ++--- tests/destructor/tarray_indexing.nim | 2 +- tests/destructor/tcustomseqs.nim | 2 +- tests/destructor/tcustomstrings.nim | 2 +- .../destructor/tuse_result_prevents_sinks.nim | 2 +- tests/errmsgs/t10594.nim | 2 +- tests/errmsgs/tnon_concrete_cast.nim | 2 +- tests/js/tfieldchecks.nim | 2 +- tests/lent/tbasic_lent_check.nim | 10 +++---- tests/misc/taddr.nim | 2 +- tests/overload/t8829.nim | 4 +-- tests/overload/tstatic_with_converter.nim | 2 +- tests/stdlib/tdecls.nim | 14 +++++----- tests/stdlib/tmemory.nim | 2 +- tests/stdlib/tsqlitebindatas.nim | 4 +-- tests/strictnotnil/tnilcheck.nim | 2 +- tests/system/tostring.nim | 2 +- tests/types/tlent_var.nim | 2 +- 30 files changed, 69 insertions(+), 66 deletions(-) diff --git a/changelog.md b/changelog.md index 4d27be3a0..9b74d6d9f 100644 --- a/changelog.md +++ b/changelog.md @@ -17,6 +17,9 @@ - `std/sharedstrings` module is removed. - Constants `colors.colPaleVioletRed` and `colors.colMediumPurple` changed to match the CSS color standard. +- `addr` is now available for all addressable locations, `unsafeAddr` is deprecated and +becomes an alias for `addr`. + ## Standard library additions and changes - `macros.parseExpr` and `macros.parseStmt` now accept an optional diff --git a/compiler/isolation_check.nim b/compiler/isolation_check.nim index a8c5a3651..68a212794 100644 --- a/compiler/isolation_check.nim +++ b/compiler/isolation_check.nim @@ -71,7 +71,7 @@ proc isValueOnlyType(t: PType): bool = proc canAlias*(arg, ret: PType): bool = if isValueOnlyType(arg): - # can alias only with unsafeAddr(arg.x) and we don't care if it is not safe + # can alias only with addr(arg.x) and we don't care if it is not safe result = false else: var marker = initIntSet() diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 874098294..c7fc75620 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -14,13 +14,8 @@ proc semAddrArg(c: PContext; n: PNode; isUnsafeAddr = false): PNode = let x = semExprWithType(c, n) if x.kind == nkSym: x.sym.flags.incl(sfAddrTaken) - if isAssignable(c, x, isUnsafeAddr) notin {arLValue, arLocalLValue}: - # Do not suggest the use of unsafeAddr if this expression already is a - # unsafeAddr - if isUnsafeAddr: - localError(c.config, n.info, errExprHasNoAddress) - else: - localError(c.config, n.info, errExprHasNoAddress & "; maybe use 'unsafeAddr'") + if isAssignable(c, x, true) notin {arLValue, arLocalLValue}: + localError(c.config, n.info, errExprHasNoAddress) result = x proc semTypeOf(c: PContext; n: PNode): PNode = diff --git a/doc/manual.rst b/doc/manual.rst index 007b14b2d..4eabd0225 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -3502,8 +3502,9 @@ location is `T`, the `addr` operator result is of the type `ptr T`. An address is always an untraced reference. Taking the address of an object that resides on the stack is **unsafe**, as the pointer may live longer than the object on the stack and can thus reference a non-existing object. One can get -the address of variables, but one can't use it on variables declared through -`let` statements: +the address of variables. For easier interoperability with other compiled languages +such as C, retrieving the address of a `let` variable, a parameter, +or a `for` loop variable can be accomplished too: .. code-block:: nim @@ -3515,24 +3516,18 @@ the address of variables, but one can't use it on variables declared through # --> ref 0x7fff6b71b670 --> 0x10bb81050"Hello" echo cast[ptr string](t3)[] # --> Hello - # The following line doesn't compile: + # The following line also works echo repr(addr(t1)) - # Error: expression has no address - The unsafeAddr operator ----------------------- -For easier interoperability with other compiled languages such as C, retrieving -the address of a `let` variable, a parameter, or a `for` loop variable can -be accomplished by using the `unsafeAddr` operation: +The `unsafeAddr` operator is a deprecated alias for the `addr` operator: .. code-block:: nim - let myArray = [1, 2, 3] foreignProcThatTakesAnAddr(unsafeAddr myArray) - Procedures ========== diff --git a/doc/manual_experimental.rst b/doc/manual_experimental.rst index 10958c29a..407924a13 100644 --- a/doc/manual_experimental.rst +++ b/doc/manual_experimental.rst @@ -603,7 +603,7 @@ has `source` as the owner. A path expression `e` is defined recursively: - Object field access `e.field` is a path expression. - `system.toOpenArray(e, ...)` is a path expression. - Pointer dereference `e[]` is a path expression. -- An address `addr e`, `unsafeAddr e` is a path expression. +- An address `addr e` is a path expression. - A type conversion `T(e)` is a path expression. - A cast expression `cast[T](e)` is a path expression. - `f(e, ...)` is a path expression if `f`'s return type is a view type. diff --git a/lib/system.nim b/lib/system.nim index c424cbc1b..3b3e28344 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -190,8 +190,16 @@ when defined(nimHasDeclaredMagic): else: proc declaredInScope*(x: untyped): bool {.magic: "DefinedInScope", noSideEffect, compileTime.} -proc `addr`*[T](x: var T): ptr T {.magic: "Addr", noSideEffect.} = +proc `addr`*[T](x: T): ptr T {.magic: "Addr", noSideEffect.} = ## Builtin `addr` operator for taking the address of a memory location. + ## + ## .. note:: This works for `let` variables or parameters + ## for better interop with C. When you use it to write a wrapper + ## for a C library and take the address of `let` variables or parameters, + ## you should always check that the original library + ## does never write to data behind the pointer that is returned from + ## this procedure. + ## ## Cannot be overloaded. ## ## See also: @@ -205,15 +213,17 @@ proc `addr`*[T](x: var T): ptr T {.magic: "Addr", noSideEffect.} = ## echo p[] # b discard -proc unsafeAddr*[T](x: T): ptr T {.magic: "Addr", noSideEffect.} = +proc unsafeAddr*[T](x: T): ptr T {.magic: "Addr", noSideEffect, + deprecated: "'unsafeAddr' is a deprecated alias for 'addr'".} = ## Builtin `addr` operator for taking the address of a memory - ## location. This works even for `let` variables or parameters - ## for better interop with C and so it is considered even more - ## unsafe than the ordinary `addr <#addr,T>`_. + ## location. ## - ## **Note**: When you use it to write a wrapper for a C library, you should - ## always check that the original library does never write to data behind the - ## pointer that is returned from this procedure. + ## .. note:: This works for `let` variables or parameters + ## for better interop with C. When you use it to write a wrapper + ## for a C library and take the address of `let` variables or parameters, + ## you should always check that the original library + ## does never write to data behind the pointer that is returned from + ## this procedure. ## ## Cannot be overloaded. discard diff --git a/tests/arc/tcustomtrace.nim b/tests/arc/tcustomtrace.nim index 3977194d9..5e0ecfb24 100644 --- a/tests/arc/tcustomtrace.nim +++ b/tests/arc/tcustomtrace.nim @@ -130,7 +130,7 @@ proc createSeq*[T](elems: varargs[T]): myseq[T] = result.data = cast[type(result.data)](alloc0(result.cap * sizeof(T))) inc allocCount when supportsCopyMem(T): - copyMem(result.data, unsafeAddr(elems[0]), result.cap * sizeof(T)) + copyMem(result.data, addr(elems[0]), result.cap * sizeof(T)) else: for i in 0.. 0 doAssert cmpMem(b.addr, a.addr, sizeof(SomeHash)) < 0 - doAssert cmpMem(a.addr, c.unsafeAddr, sizeof(SomeHash)) == 0 + doAssert cmpMem(a.addr, c.addr, sizeof(SomeHash)) == 0 diff --git a/tests/stdlib/tsqlitebindatas.nim b/tests/stdlib/tsqlitebindatas.nim index 754c80ae1..b2c3247fa 100644 --- a/tests/stdlib/tsqlitebindatas.nim +++ b/tests/stdlib/tsqlitebindatas.nim @@ -27,7 +27,7 @@ block tsqlitebindatas: ## db_sqlite binary data db.exec(createTableStr) var dbuf = newSeq[byte](orig.len*sizeof(float64)) - copyMem(unsafeAddr(dbuf[0]), unsafeAddr(orig[0]), dbuf.len) + copyMem(addr(dbuf[0]), addr(orig[0]), dbuf.len) var insertStmt = db.prepare("INSERT INTO test (id, name, data) VALUES (?, ?, ?)") insertStmt.bindParams(1, origName, dbuf) @@ -42,7 +42,7 @@ block tsqlitebindatas: ## db_sqlite binary data var dataTest = db.getValue(sql"SELECT data FROM test WHERE id = ?", 1) let seqSize = int(dataTest.len*sizeof(byte)/sizeof(float64)) var res: seq[float64] = newSeq[float64](seqSize) - copyMem(unsafeAddr(res[0]), addr(dataTest[0]), dataTest.len) + copyMem(addr(res[0]), addr(dataTest[0]), dataTest.len) doAssert res.len == orig.len doAssert res == orig diff --git a/tests/strictnotnil/tnilcheck.nim b/tests/strictnotnil/tnilcheck.nim index 5b9292522..c2d009b70 100644 --- a/tests/strictnotnil/tnilcheck.nim +++ b/tests/strictnotnil/tnilcheck.nim @@ -194,7 +194,7 @@ proc testAliasChanging(a: Nilable) = # # proc testPtrAlias(a: Nilable) = # # # pointer to a: hm. # # # alias to a? -# # var ptrA = a.unsafeAddr # {0, 1} +# # var ptrA = a.addr # {0, 1} # # if not a.isNil: # {0, 1} # # ptrA[] = nil # {0, 1} 0: MaybeNil 1: MaybeNil # # echo a.a #[ tt.Warning diff --git a/tests/system/tostring.nim b/tests/system/tostring.nim index bdbaa2ce6..cae20865e 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 $cstring(addr arr) == "Hello World!" proc takes(c: cstring) = doAssert c == cstring"" diff --git a/tests/types/tlent_var.nim b/tests/types/tlent_var.nim index 491f6fde8..73b5bef9b 100644 --- a/tests/types/tlent_var.nim +++ b/tests/types/tlent_var.nim @@ -15,7 +15,7 @@ proc test_var(x: var MyObj): var int = var x = MyObj(a: 5) doAssert: test_var(x).addr == x.a.addr -doAssert: test_lent(x).unsafeAddr == x.a.addr +doAssert: test_lent(x).addr == x.a.addr proc varProc(x: var int) = x = 100 From 15e3813d96ad5646476b629f3a20ece89dff064d Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 17 Jan 2022 04:10:35 +0800 Subject: [PATCH 2765/3768] add mm to compilesettings; deprecate gc (#19394) --- compiler/vmops.nim | 1 + lib/std/compilesettings.nim | 3 ++- tests/vm/tcompilesetting.nim | 3 ++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/compiler/vmops.nim b/compiler/vmops.nim index 22a632ee9..018e7b9c6 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -135,6 +135,7 @@ when defined(nimHasInvariant): of backend: result = $conf.backend of libPath: result = conf.libpath.string of gc: result = $conf.selectedGC + of mm: result = $conf.selectedGC proc querySettingSeqImpl(conf: ConfigRef, switch: BiggestInt): seq[string] = template copySeq(field: untyped): untyped = diff --git a/lib/std/compilesettings.nim b/lib/std/compilesettings.nim index 5bb0bd673..6d8bd22f4 100644 --- a/lib/std/compilesettings.nim +++ b/lib/std/compilesettings.nim @@ -32,7 +32,8 @@ type backend ## the backend (eg: c|cpp|objc|js); both `nim doc --backend:js` ## and `nim js` would imply backend=js libPath ## the absolute path to the stdlib library, i.e. nim's `--lib`, since 1.5.1 - gc ## gc selected + gc {.deprecated.} ## gc selected + mm ## memory management selected MultipleValueSetting* {.pure.} = enum ## \ ## settings resulting in a seq of string values diff --git a/tests/vm/tcompilesetting.nim b/tests/vm/tcompilesetting.nim index 5bf559c74..d6c08e70f 100644 --- a/tests/vm/tcompilesetting.nim +++ b/tests/vm/tcompilesetting.nim @@ -1,5 +1,5 @@ discard """ -cmd: "nim c --nimcache:build/myNimCache --nimblePath:myNimblePath $file" +cmd: "nim c --nimcache:build/myNimCache --nimblePath:myNimblePath --gc:arc $file" joinable: false """ @@ -12,6 +12,7 @@ template main = doAssert "myNimblePath" in nimblePaths.querySettingSeq[0] doAssert querySetting(backend) == "c" doAssert fileExists(libPath.querySetting / "system.nim") + doAssert querySetting(mm) == "arc" static: main() main() From 2c5b367001cc53859048d9adf450a5db3a949b92 Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 17 Jan 2022 11:54:27 +0800 Subject: [PATCH 2766/3768] improve changelog a bit (#19400) --- changelog.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index 9b74d6d9f..16b526f85 100644 --- a/changelog.md +++ b/changelog.md @@ -28,9 +28,7 @@ becomes an alias for `addr`. - Fixed `lists.SinglyLinkedList` being broken after removing the last node ([#19353](https://github.com/nim-lang/Nim/pull/19353)). - `md5` now works at compile time and in JavaScript. -## `std/smtp` - -- Sends `ehlo` first. If the mail server does not understand, it sends `helo` as a fallback. +- `std/smtp` sends `ehlo` first. If the mail server does not understand, it sends `helo` as a fallback. - Added `IsoWeekRange`, a range type to represent the number of weeks in an ISO week-based year. - Added `IsoYear`, a distinct int type to prevent bugs from confusing the week-based year and the regular year. From 4f6b59de9698d0c29a2170ecc23c8d009b3181b7 Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 17 Jan 2022 18:14:13 +0800 Subject: [PATCH 2767/3768] mangle names in nimbase.h using cppDefine (#19395) [backport] mangle names in nimbase.h fix comments --- config/config.nims | 5 +++++ tests/ccgbugs/tmangle.nim | 16 ++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 tests/ccgbugs/tmangle.nim diff --git a/config/config.nims b/config/config.nims index 893d6960f..88c344642 100644 --- a/config/config.nims +++ b/config/config.nims @@ -3,6 +3,11 @@ cppDefine "errno" cppDefine "unix" +# mangle the macro names in nimbase.h +cppDefine "NAN_INFINITY" +cppDefine "INF" +cppDefine "NAN" + when defined(nimStrictMode): # xxx add more flags here, and use `-d:nimStrictMode` in more contexts in CI. diff --git a/tests/ccgbugs/tmangle.nim b/tests/ccgbugs/tmangle.nim new file mode 100644 index 000000000..0050cef92 --- /dev/null +++ b/tests/ccgbugs/tmangle.nim @@ -0,0 +1,16 @@ +block: + proc hello() = + let NAN_INFINITY = 12 + doAssert NAN_INFINITY == 12 + let INF = "2.0" + doAssert INF == "2.0" + let NAN = 2.3 + doAssert NAN == 2.3 + + hello() + +block: + proc hello(NAN: float) = + doAssert NAN == 2.0 + + hello(2.0) From dc8ac66873d5189f2e5cf5dbf56a12ab64a38140 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Mon, 17 Jan 2022 04:02:31 -0800 Subject: [PATCH 2768/3768] fix no net compilation on zephyr (#19399) Co-authored-by: Jaremy J. Creechley --- lib/posix/posix_other.nim | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/posix/posix_other.nim b/lib/posix/posix_other.nim index 688867b98..941e13192 100644 --- a/lib/posix/posix_other.nim +++ b/lib/posix/posix_other.nim @@ -399,16 +399,20 @@ elif defined(zephyr): const Sockaddr_max_length* = 24 elif defined(net_raw): const Sockaddr_max_length* = 20 - else: + elif defined(net_ipv4): const Sockaddr_max_length* = 8 + else: + const Sockaddr_max_length* = 255 # just for compilation purposes + const Sockaddr_un_path_length* = Sockaddr_max_length # Zephyr is heavily customizable so it's easy to get to a state # where Nim & Zephyr IPv6 settings are out of sync, causing painful runtime failures. - {.emit: ["NIM_STATIC_ASSERT(NET_SOCKADDR_MAX_SIZE == ", - Sockaddr_max_length, - ",\"NET_SOCKADDR_MAX_SIZE and Sockaddr_max_length size mismatch!", - " Check that Nim and Zephyr IPv4/IPv6 settings match.", - " Try adding -d:net_ipv6 to enable IPv6 for Nim on Zephyr.\" );"].} + when defined(net_ipv4) or defined(net_ipv6) or defined(net_raw): + {.emit: ["NIM_STATIC_ASSERT(NET_SOCKADDR_MAX_SIZE == ", + Sockaddr_max_length, + ",\"NET_SOCKADDR_MAX_SIZE and Sockaddr_max_length size mismatch!", + " Check that Nim and Zephyr IPv4/IPv6 settings match.", + " Try adding -d:net_ipv6 to enable IPv6 for Nim on Zephyr.\" );"].} elif defined(freertos) or defined(lwip): const Sockaddr_max_length* = 14 const Sockaddr_un_path_length* = 108 From 07c7a8a526cf4fd12c96192cda7a750fbea3382f Mon Sep 17 00:00:00 2001 From: hlaaftana <10591326+hlaaftana@users.noreply.github.com> Date: Mon, 17 Jan 2022 15:03:40 +0300 Subject: [PATCH 2769/3768] Optimize lent in JS [backport:1.6] (#19393) * Optimize lent in JS [backport:1.6] * addr on lent doesn't work anymore, don't use it * use unsafeAddr in test again for older versions --- compiler/jsgen.nim | 51 ++++++++++++++++++++++---------- tests/js/tlent.nim | 33 +++++++++++++++++++++ tests/lent/tbasic_lent_check.nim | 20 ++++++++----- 3 files changed, 81 insertions(+), 23 deletions(-) create mode 100644 tests/js/tlent.nim diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 7e8c25173..0a7036b46 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -178,7 +178,7 @@ const proc mapType(typ: PType): TJSTypeKind = let t = skipTypes(typ, abstractInst) case t.kind - of tyVar, tyRef, tyPtr, tyLent: + of tyVar, tyRef, tyPtr: if skipTypes(t.lastSon, abstractInst).kind in MappedToObject: result = etyObject else: @@ -186,7 +186,8 @@ proc mapType(typ: PType): TJSTypeKind = of tyPointer: # treat a tyPointer like a typed pointer to an array of bytes result = etyBaseIndex - of tyRange, tyDistinct, tyOrdinal, tyProxy: + of tyRange, tyDistinct, tyOrdinal, tyProxy, tyLent: + # tyLent is no-op as JS has pass-by-reference semantics result = mapType(t[0]) of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyChar: result = etyInt of tyBool: result = etyBool @@ -1060,14 +1061,14 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = xtyp = etySeq case xtyp of etySeq: - if (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded: + if x.typ.kind in {tyVar, tyLent} or (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded: lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc]) else: useMagic(p, "nimCopy") lineF(p, "$1 = nimCopy(null, $2, $3);$n", [a.rdLoc, b.res, genTypeInfo(p, y.typ)]) of etyObject: - if x.typ.kind in {tyVar} or (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded: + if x.typ.kind in {tyVar, tyLent} or (needsNoCopy(p, y) and needsNoCopy(p, x)) or noCopyNeeded: lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc]) else: useMagic(p, "nimCopy") @@ -1092,10 +1093,18 @@ proc genAsgnAux(p: PProc, x, y: PNode, noCopyNeeded: bool) = lineF(p, "$# = [$#, $#];$n", [a.res, b.address, b.res]) lineF(p, "$1 = $2;$n", [a.address, b.res]) lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc]) + elif a.typ == etyBaseIndex: + # array indexing may not map to var type + if b.address != nil: + lineF(p, "$1 = $2; $3 = $4;$n", [a.address, b.address, a.res, b.res]) + else: + lineF(p, "$1 = $2;$n", [a.address, b.res]) else: internalError(p.config, x.info, $("genAsgn", b.typ, a.typ)) - else: + elif b.address != nil: lineF(p, "$1 = $2; $3 = $4;$n", [a.address, b.address, a.res, b.res]) + else: + lineF(p, "$1 = $2;$n", [a.address, b.res]) else: lineF(p, "$1 = $2;$n", [a.rdLoc, b.rdLoc]) @@ -1288,7 +1297,7 @@ template isIndirect(x: PSym): bool = v.kind notin {skProc, skFunc, skConverter, skMethod, skIterator, skConst, skTemp, skLet}) -proc genSymAddr(p: PProc, n: PNode, typ: PType, r: var TCompRes) = +proc genSymAddr(p: PProc, n: PNode, typ: PType, r: var TCompRes) = let s = n.sym if s.loc.r == nil: internalError(p.config, n.info, "genAddr: 3") case s.kind @@ -1456,13 +1465,17 @@ proc genSym(p: PProc, n: PNode, r: var TCompRes) = else: if s.loc.r == nil: internalError(p.config, n.info, "symbol has no generated name: " & s.name.s) - r.res = s.loc.r + if mapType(p, s.typ) == etyBaseIndex: + r.address = s.loc.r + r.res = s.loc.r & "_Idx" + else: + r.res = s.loc.r r.kind = resVal proc genDeref(p: PProc, n: PNode, r: var TCompRes) = let it = n[0] let t = mapType(p, it.typ) - if t == etyObject: + if t == etyObject or it.typ.kind == tyLent: gen(p, it, r) else: var a: TCompRes @@ -1703,7 +1716,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope = result = putToSeq("0", indirect) of tyFloat..tyFloat128: result = putToSeq("0.0", indirect) - of tyRange, tyGenericInst, tyAlias, tySink, tyOwned: + of tyRange, tyGenericInst, tyAlias, tySink, tyOwned, tyLent: result = createVar(p, lastSon(typ), indirect) of tySet: result = putToSeq("{}", indirect) @@ -1745,7 +1758,7 @@ proc createVar(p: PProc, typ: PType, indirect: bool): Rope = createObjInitList(p, t, initIntSet(), initList) result = ("({$1})") % [initList] if indirect: result = "[$1]" % [result] - of tyVar, tyPtr, tyLent, tyRef, tyPointer: + of tyVar, tyPtr, tyRef, tyPointer: if mapType(p, t) == etyBaseIndex: result = putToSeq("[null, 0]", indirect) else: @@ -2394,16 +2407,17 @@ proc genProc(oldProc: PProc, prc: PSym): Rope = if prc.typ[0] != nil and sfPure notin prc.flags: resultSym = prc.ast[resultPos].sym let mname = mangleName(p.module, resultSym) - if not isIndirect(resultSym) and + let returnAddress = not isIndirect(resultSym) and resultSym.typ.kind in {tyVar, tyPtr, tyLent, tyRef, tyOwned} and - mapType(p, resultSym.typ) == etyBaseIndex: + mapType(p, resultSym.typ) == etyBaseIndex + if returnAddress: resultAsgn = p.indentLine(("var $# = null;$n") % [mname]) resultAsgn.add p.indentLine("var $#_Idx = 0;$n" % [mname]) else: let resVar = createVar(p, resultSym.typ, isIndirect(resultSym)) resultAsgn = p.indentLine(("var $# = $#;$n") % [mname, resVar]) gen(p, prc.ast[resultPos], a) - if mapType(p, resultSym.typ) == etyBaseIndex: + if returnAddress: returnStmt = "return [$#, $#];$n" % [a.address, a.res] else: returnStmt = "return $#;$n" % [a.res] @@ -2579,8 +2593,15 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = of nkObjConstr: genObjConstr(p, n, r) of nkHiddenStdConv, nkHiddenSubConv, nkConv: genConv(p, n, r) of nkAddr, nkHiddenAddr: - genAddr(p, n, r) - of nkDerefExpr, nkHiddenDeref: genDeref(p, n, r) + if n.typ.kind in {tyLent}: + gen(p, n[0], r) + else: + genAddr(p, n, r) + of nkDerefExpr, nkHiddenDeref: + if n.typ.kind in {tyLent}: + gen(p, n[0], r) + else: + genDeref(p, n, r) of nkBracketExpr: genArrayAccess(p, n, r) of nkDotExpr: genFieldAccess(p, n, r) of nkCheckedFieldExpr: genCheckedFieldOp(p, n, nil, r) diff --git a/tests/js/tlent.nim b/tests/js/tlent.nim new file mode 100644 index 000000000..2546e5b1d --- /dev/null +++ b/tests/js/tlent.nim @@ -0,0 +1,33 @@ +discard """ + output: ''' +hmm +100 +hmm +100 +''' +""" + +# #16800 + +type A = object + b: int +var t = A(b: 100) +block: + proc getValues: lent int = + echo "hmm" + result = t.b + echo getValues() +block: + proc getValues: lent int = + echo "hmm" + t.b + echo getValues() + +when false: # still an issue, #16908 + template main = + iterator fn[T](a:T): lent T = yield a + let a = @[10] + for b in fn(a): echo b + + static: main() + main() diff --git a/tests/lent/tbasic_lent_check.nim b/tests/lent/tbasic_lent_check.nim index 0787e36d9..92d731451 100644 --- a/tests/lent/tbasic_lent_check.nim +++ b/tests/lent/tbasic_lent_check.nim @@ -17,22 +17,26 @@ proc main = main() template main2 = # bug #15958 + when defined(js): + proc sameAddress[T](a, b: T): bool {.importjs: "(# === #)".} + else: + template sameAddress(a, b): bool = a.unsafeAddr == b.unsafeAddr proc byLent[T](a: T): lent T = a let a = [11,12] let b = @[21,23] let ss = {1, 2, 3, 5} doAssert byLent(a) == [11,12] - doAssert byLent(a).addr == a.addr + doAssert sameAddress(byLent(a), a) doAssert byLent(b) == @[21,23] - when not defined(js): # pending bug #16073 - doAssert byLent(b).addr == b.addr + # pending bug #16073 + doAssert sameAddress(byLent(b), b) doAssert byLent(ss) == {1, 2, 3, 5} - doAssert byLent(ss).addr == ss.addr + doAssert sameAddress(byLent(ss), ss) let r = new(float) r[] = 10.0 - when not defined(js): # pending bug #16073 - doAssert byLent(r)[] == 10.0 + # bug #16073 + doAssert byLent(r)[] == 10.0 when not defined(js): # pending bug https://github.com/timotheecour/Nim/issues/372 let p = create(float) @@ -41,9 +45,9 @@ template main2 = # bug #15958 proc byLent2[T](a: openArray[T]): lent T = a[0] doAssert byLent2(a) == 11 - doAssert byLent2(a).addr == a[0].addr + doAssert sameAddress(byLent2(a), a[0]) doAssert byLent2(b) == 21 - doAssert byLent2(b).addr == b[0].addr + doAssert sameAddress(byLent2(b), b[0]) proc byLent3[T](a: varargs[T]): lent T = a[1] let From bc3bee54698b108977766b6b2daf8a33bd00d1ef Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 17 Jan 2022 20:06:31 +0800 Subject: [PATCH 2770/3768] Added `std/oserrors` for OS error reporting (#19390) * Added 'std/oserrors' for OS error reporting * add a simple test * New code should not support -d:useWinAnsi anymore thanks to @Araq --- changelog.md | 2 + lib/std/oserrors.nim | 118 +++++++++++++++++++++++++++++++++++++ tests/stdlib/toserrors.nim | 9 +++ 3 files changed, 129 insertions(+) create mode 100644 lib/std/oserrors.nim create mode 100644 tests/stdlib/toserrors.nim diff --git a/changelog.md b/changelog.md index 16b526f85..82f9daa1e 100644 --- a/changelog.md +++ b/changelog.md @@ -74,6 +74,8 @@ becomes an alias for `addr`. for the right-hand side of type definitions in type sections. Previously they would error with "invalid indentation". +- Added `std/oserrors` for OS error reporting. + ## Compiler changes - `nim` can now compile version 1.4.0 as follows: `nim c --lib:lib --stylecheck:off compiler/nim`, diff --git a/lib/std/oserrors.nim b/lib/std/oserrors.nim new file mode 100644 index 000000000..9c2649eab --- /dev/null +++ b/lib/std/oserrors.nim @@ -0,0 +1,118 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2022 Nim contributors +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + + +## The `std/oserrors` module implements OS error reporting. + +type + OSErrorCode* = distinct int32 ## Specifies an OS Error Code. + +when not defined(nimscript): + when defined(windows): + import winlean + else: + var errno {.importc, header: "".}: cint + + proc c_strerror(errnum: cint): cstring {. + importc: "strerror", header: "".} + +proc `==`*(err1, err2: OSErrorCode): bool {.borrow.} +proc `$`*(err: OSErrorCode): string {.borrow.} + +proc osErrorMsg*(errorCode: OSErrorCode): string = + ## Converts an OS error code into a human readable string. + ## + ## The error code can be retrieved using the `osLastError proc`_. + ## + ## If conversion fails, or `errorCode` is `0` then `""` will be + ## returned. + ## + ## See also: + ## * `raiseOSError proc`_ + ## * `osLastError proc`_ + runnableExamples: + when defined(linux): + assert osErrorMsg(OSErrorCode(0)) == "" + assert osErrorMsg(OSErrorCode(1)) == "Operation not permitted" + assert osErrorMsg(OSErrorCode(2)) == "No such file or directory" + + result = "" + when defined(nimscript): + discard + elif defined(windows): + if errorCode != OSErrorCode(0'i32): + var msgbuf: WideCString + if formatMessageW(0x00000100 or 0x00001000 or 0x00000200, + nil, errorCode.int32, 0, addr(msgbuf), 0, nil) != 0'i32: + result = $msgbuf + if msgbuf != nil: localFree(cast[pointer](msgbuf)) + else: + if errorCode != OSErrorCode(0'i32): + result = $c_strerror(errorCode.int32) + +proc newOSError*( + errorCode: OSErrorCode, additionalInfo = "" +): owned(ref OSError) {.noinline.} = + ## Creates a new `OSError exception `_. + ## + ## The `errorCode` will determine the + ## message, `osErrorMsg proc`_ will be used + ## to get this message. + ## + ## The error code can be retrieved using the `osLastError proc`_. + ## + ## If the error code is `0` or an error message could not be retrieved, + ## the message `unknown OS error` will be used. + ## + ## See also: + ## * `osErrorMsg proc`_ + ## * `osLastError proc`_ + var e: owned(ref OSError); new(e) + e.errorCode = errorCode.int32 + e.msg = osErrorMsg(errorCode) + if additionalInfo.len > 0: + if e.msg.len > 0 and e.msg[^1] != '\n': e.msg.add '\n' + e.msg.add "Additional info: " + e.msg.add additionalInfo + # don't add trailing `.` etc, which negatively impacts "jump to file" in IDEs. + if e.msg == "": + e.msg = "unknown OS error" + return e + +proc raiseOSError*(errorCode: OSErrorCode, additionalInfo = "") {.noinline.} = + ## Raises an `OSError exception `_. + ## + ## Read the description of the `newOSError proc`_ to learn + ## how the exception object is created. + raise newOSError(errorCode, additionalInfo) + +{.push stackTrace:off.} +proc osLastError*(): OSErrorCode {.sideEffect.} = + ## Retrieves the last operating system error code. + ## + ## This procedure is useful in the event when an OS call fails. In that case + ## this procedure will return the error code describing the reason why the + ## OS call failed. The `OSErrorMsg` procedure can then be used to convert + ## this code into a string. + ## + ## .. warning:: The behaviour of this procedure varies between Windows and POSIX systems. + ## On Windows some OS calls can reset the error code to `0` causing this + ## procedure to return `0`. It is therefore advised to call this procedure + ## immediately after an OS call fails. On POSIX systems this is not a problem. + ## + ## See also: + ## * `osErrorMsg proc`_ + ## * `raiseOSError proc`_ + when defined(nimscript): + discard + elif defined(windows): + result = cast[OSErrorCode](getLastError()) + else: + result = OSErrorCode(errno) +{.pop.} diff --git a/tests/stdlib/toserrors.nim b/tests/stdlib/toserrors.nim new file mode 100644 index 000000000..e907dfe63 --- /dev/null +++ b/tests/stdlib/toserrors.nim @@ -0,0 +1,9 @@ +discard """ + action: compile +""" + +import std/oserrors + +let x1 = osLastError() +raiseOSError(x1) +echo osErrorMsg(x1) From 5d303762f156a276cb37162229e6b82b01e08253 Mon Sep 17 00:00:00 2001 From: Hamid Bluri Date: Tue, 18 Jan 2022 12:55:39 -0800 Subject: [PATCH 2771/3768] update deprecated example (#19415) `toNimIdent` proc is deprecated, so I replaced it with `ident` proc --- lib/core/macros.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index ed7e85c64..8ab893d21 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -1373,7 +1373,7 @@ template findChild*(n: NimNode; cond: untyped): NimNode {.dirty.} = ## ## .. code-block:: nim ## var res = findChild(n, it.kind == nnkPostfix and - ## it.basename.ident == toNimIdent"foo") + ## it.basename.ident == ident"foo") block: var res: NimNode for it in n.children: From d7869a80092354468594f7d5c5dfdd91f564fec7 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Tue, 18 Jan 2022 23:58:18 +0300 Subject: [PATCH 2772/3768] Improve Zshell completion (#19354) --- tools/nim.zsh-completion | 258 +++++++++++++++++---------------------- 1 file changed, 114 insertions(+), 144 deletions(-) diff --git a/tools/nim.zsh-completion b/tools/nim.zsh-completion index 45477d860..1c3670fd9 100644 --- a/tools/nim.zsh-completion +++ b/tools/nim.zsh-completion @@ -1,150 +1,120 @@ #compdef nim +# Installation note: +# Please name this file as _nim (with underscore!) and copy it to a +# completions directory, either: +# - system wide one, like /usr/share/zsh/functions/Completion/Unix/ on Linux +# - or to a user directory like ~/scripts. Then you also need to add +# that directory in your ~/.zshrc to `fpath` array like so: +# fpath=( ~/scripts "${fpath[@]}" ) + _nim() { - _arguments -C \ - ':command:(( - {compile,c}\:compile\ project\ with\ default\ code\ generator\ C - doc\:generate\ the\ documentation\ for\ inputfile - {compileToC,cc}\:compile\ project\ with\ C\ code\ generator - {compileToCpp,cpp}\:compile\ project\ to\ C++\ code - {compileToOC,objc}\:compile\ project\ to\ Objective\ C\ code - js\:compile\ project\ to\ Javascript - e\:run\ a\ Nimscript\ file - rst2html\:convert\ a\ reStructuredText\ file\ to\ HTML - rst2tex\:convert\ a\ reStructuredText\ file\ to\ TeX - jsondoc\:extract\ the\ documentation\ to\ a\ json\ file - buildIndex\:build\ an\ index\ for\ the\ whole\ documentation - genDepend\:generate\ a\ DOT\ file\ containing\ the\ module\ dependency\ graph - dump\:dump\ all\ defined\ conditionals\ and\ search\ paths - check\:checks\ the\ project\ for\ syntax\ and\ semantic - ))' \ - '*-r[run the application]' \ - '*--run[run the application]' \ - '*-p=[add path to search paths]' \ - '*--path=[add path to search paths]' \ - '*-d=[define a conditional symbol]' \ - '*--define=[define a conditional symbol]' \ - '*-u=[undefine a conditional symbol]' \ - '*--undef=[undefine a conditional symbol]' \ - '*-f[force rebuilding of all modules]' \ - '*--forceBuild[force rebuilding of all modules]' \ - '*--stackTrace=on[turn stack tracing on]' \ - '*--stackTrace=off[turn stack tracing off]' \ - '*--lineTrace=on[turn line tracing on]' \ - '*--lineTrace=off[turn line tracing off]' \ - '*--threads=on[turn support for multi-threading on]' \ - '*--threads=off[turn support for multi-threading off]' \ - '*-x=on[turn all runtime checks on]' \ - '*-x=off[turn all runtime checks off]' \ - '*--checks=on[turn all runtime checks on]' \ - '*--checks=off[turn all runtime checks off]' \ - '*--objChecks=on[turn obj conversion checks on]' \ - '*--objChecks=off[turn obj conversion checks off]' \ - '*--fieldChecks=on[turn case variant field checks on]' \ - '*--fieldChecks=off[turn case variant field checks off]' \ - '*--rangeChecks=on[turn range checks on]' \ - '*--rangeChecks=off[turn range checks off]' \ - '*--boundChecks=on[turn bound checks on]' \ - '*--boundChecks=off[turn bound checks off]' \ - '*--overflowChecks=on[turn int over-/underflow checks on]' \ - '*--overflowChecks=off[turn int over-/underflow checks off]' \ - '*-a[turn assertions on]' \ - '*-a[turn assertions off]' \ - '*--assertions=on[turn assertions on]' \ - '*--assertions=off[turn assertions off]' \ - '*--floatChecks=on[turn all floating point (NaN/Inf) checks on]' \ - '*--floatChecks=off[turn all floating point (NaN/Inf) checks off]' \ - '*--nanChecks=on[turn NaN checks on]' \ - '*--nanChecks=off[turn NaN checks off]' \ - '*--infChecks=on[turn Inf checks on]' \ - '*--infChecks=off[turn Inf checks off]' \ - '*--nilChecks=on[turn nil checks on]' \ - '*--nilChecks=off[turn nil checks off]' \ - '*--opt=none[do not optimize]' \ - '*--opt=speed[optimize for speed|size - use -d:release for a release build]' \ - '*--opt=size[optimize for size]' \ - '*--debugger:native[use native debugger (gdb)]' \ - '*--app=console[generate a console app]' \ - '*--app=gui[generate a GUI app]' \ - '*--app=lib[generate a dynamic library]' \ - '*--app=staticlib[generate a static library]' \ - '*--cpu=alpha[compile for Alpha architecture]' \ - '*--cpu=amd64[compile for x86_64 architecture]' \ - '*--cpu=arm[compile for ARM architecture]' \ - '*--cpu=arm64[compile for ARM64 architecture]' \ - '*--cpu=avr[compile for AVR architecture]' \ - '*--cpu=esp[compile for ESP architecture]' \ - '*--cpu=e2k[compile for Elbrus 2000 architecture]' \ - '*--cpu=hppa[compile for HPPA architecture]' \ - '*--cpu=i386[compile for i386 architecture]' \ - '*--cpu=ia64[compile for ia64 architecture]' \ - '*--cpu=js[compile to JavaScript]' \ - '*--cpu=m68k[compile for m68k architecture]' \ - '*--cpu=mips[compile for MIPS architecture]' \ - '*--cpu=mipsel[compile for MIPS EL architecture]' \ - '*--cpu=mips64[compile for MIPS64 architecture]' \ - '*--cpu=mips64el[compile for MIPS64 EL architecture]' \ - '*--cpu=msp430[compile for msp430 architecture]' \ - '*--cpu=nimvm[compile for Nim VM]' \ - '*--cpu=powerpc[compile for PowerPC architecture]' \ - '*--cpu=powerpc64[compile for PowerPC64 architecture]' \ - '*--cpu=powerpc64el[compile for PowerPC64 EL architecture]' \ - '*--cpu=riscv32[compile for RISC-V 32 architecture]' \ - '*--cpu=riscv64[compile for RISC-V 64 architecture]' \ - '*--cpu=sparc[compile for SPARC architecture]' \ - '*--cpu=sparc64[compile for SPARC64 architecture]' \ - '*--cpu=vm[compile for Nim VM]' \ - '*--cpu=wasm32[compile to WASM 32]' \ - '*--cpu=loongarch64[compile for LoongArch64 architecture]' \ - '*--gc=refc[use reference counting garbage collection]' \ - '*--gc=arc[use ARC garbage collection]' \ - '*--gc=orc[use ORC garbage collection]' \ - '*--gc=markAndSweep[use mark-and-sweep garbage collection]' \ - '*--gc=boehm[use Boehm garbage collection]' \ - '*--gc=go[use Go garbage collection]' \ - '*--gc=regions[use region-based memory management]' \ - '*--gc=none[disable garbage collection]' \ - '*--os=Standalone[generate a stand-alone executable]' \ - '*--os=AIX[compile for AIX]' \ - '*--os=Amiga[compile for Amiga OS]' \ - '*--os=Android[compile for Android]' \ - '*--os=Any[compile for any OS]' \ - '*--os=Atari[compile for Atari]' \ - '*--os=DOS[compile for DOS]' \ - '*--os=DragonFly[compile for DragonFly]' \ - '*--os=FreeBSD[compile for FreeBSD]' \ - '*--os=FreeRTOS[compile for FreeRTOS]' \ - '*--os=Genode[compile for Genode]' \ - '*--os=Haiku[compile for Haiku]' \ - '*--os=iOS[compile for iOS]' \ - '*--os=Irix[compile for Irix]' \ - '*--os=Linux[compile for Linux]' \ - '*--os=MacOS[compile for MacOS]' \ - '*--os=MacOSX[compile for MacOSX]' \ - '*--os=MorphOS[compile for MorphOS]' \ - '*--os=NetBSD[compile for NetBSD]' \ - '*--os=Netware[compile for Netware]' \ - '*--os=NimVM[compile for NimVM]' \ - '*--os=NintendoSwitch[compile for NintendoSwitch]' \ - '*--os=OS2[compile for OS2]' \ - '*--os=OpenBSD[compile for OpenBSD]' \ - '*--os=PalmOS[compile for PalmOS]' \ - '*--os=QNX[compile for QNX]' \ - '*--os=SkyOS[compile for SkyOS]' \ - '*--os=Solaris[compile for Solaris]' \ - '*--os=VxWorks[compile for VxWorks]' \ - '*--os=Windows[compile for Windows]' \ - '*--os=JS[generate javascript]' \ - '*--panics=off[turn panics into process terminations: off by default]' \ - '*--panics=on[turn panics into process terminations]' \ - '*--verbosity=0[set verbosity to 0]' \ - '*--verbosity=1[set verbosity to 1 (default)]' \ - '*--verbosity=2[set verbosity to 2]' \ - '*--verbosity=3[set verbosity to 3]' \ - '*--hints=on[print compilation hints]' \ - '*--hints=off[disable compilation hints]' \ - '*--hints=list[print compilation hints list]' \ - ':filename:_files -g"*.nim"' + local -a nimCommands=( + {compile,c}:'compile project with default code generator C' + {compileToC,cc}:'compile project with C code generator' + {compileToCpp,cpp}:'compile project to C++ code' + {compileToOC,objc}:'compile project to Objective C code' + 'js:compile project to Javascript' + 'e:run a Nimscript file' + 'doc:generate the HTML documentation for inputfile' + 'rst2html:convert a reStructuredText file to HTML' + 'doc2tex:generate the documentation for inputfile to LaTeX' + 'rst2tex:convert a reStructuredText file to TeX' + 'jsondoc:extract the documentation to a json file' + 'buildIndex:build an index for the whole documentation' + 'genDepend:generate a DOT file containing the module dependency graph' + 'dump:dump all defined conditionals and search paths' + 'check:checks the project for syntax and semantic' + {--help,-h}:'basic help' + '--fullhelp:show all switches' + {-v,--version}:'show version' + ) + + _arguments '*:: :->anyState' && return 0 + + if (( CURRENT == 1 )); then + _describe -t commands "Nim commands" nimCommands -V1 + return + fi + + local -a conditionalSymbols=( + release danger mingw androidNDK useNimRtl useMalloc noSignalHandler ssl + debug leanCompiler gcDestructors) + local -a sharedOpts=( + {--define\\:-,-d\\:-}'[define a conditional symbol]:x:($conditionalSymbols)' + {--undef\\:-,-u\\:-}'[undefine a conditional symbol]:x:($conditionalSymbols)' + {--path\\:-,-p\\:-}'[add path to search paths]:x:_files' + '--verbosity\:-[set verbosity level (default\: 1)]:x:(0 1 2 3)' + '--hints\:-[print compilation hints? (or `list`)]:x:(on off list)' + ) + local runOpts=( + {--run,-r}'[run the application]' + ) + local docOpts=( + '--index\:-[enable index .idx files?]:x:(on off)' + '--project\:-[output any dependency for doc?]:x:(on off)' + '--docInternal\:-[generate module-private documentation?]:x:(on off)' + ) + local -a codeOpts=( + {--forceBuild,-f}'[force rebuilding of all modules]' + '--stackTrace\:-[enable stack tracing?]:x:(on off)' + '--lineTrace\:-[enable line tracing?]:x:(on off)' + '--threads\:-[enable support for multi-threading?]:x:(on off)' + {--checks\\:-,-x\\:-}'[enable/disable all runtime checks?]:x:(on off)' + '--objChecks\:-[enable obj conversion checks]:x:(on off)' + '--fieldChecks\:-[enable case variant field checks?]:x:(on off)' + '--rangeChecks\:-[enable range checks?]:x:(on off)' + '--boundChecks\:-[enable bound checks?]:x:(on off)' + '--overflowChecks\:-[enable integer over-/underflow checks?]:x:(on off)' + {--assertions\\:-,-a\\:-}'[enable assertions?]:x:(on off)' + '--floatChecks\:-[enable floating point (NaN/Inf) checks?]:x:(on off)' + '--nanChecks\:-[enable NaN checks?]:x:(on off)' + '--infChecks\:-[enable Inf checks?]:x:(on off)' + '--nilChecks\:-[enable nil checks?]:x:(on off)' + '--expandArc\:-[show how given proc looks before final backend pass]' + '--expandMacro\:-[dump every generated AST from given macro]' + ) + local -a nativeOpts=( + '--opt\:-[optimization mode]:x:(none speed size)' + '--debugger\:native[use native debugger (gdb)]' + '--app\:-[generate this type of app (lib=dynamic)]:x:(console gui lib staticlib)' + '--cpu\:-[target architecture]:x:(alpha amd64 arm arm64 avr e2k esp hppa i386 ia64 js loongarch64 m68k mips mipsel mips64 mips64el msp430 nimvm powerpc powerpc64 powerpc64el riscv32 riscv64 sparc sparc64 vm wasm32)' + '--gc\:-[memory management algorithm to use (default\: refc)]:x:(refc arc orc markAndSweep boehm go regions none)' + '--os\:-[operating system to compile for]:x:(AIX Amiga Android Any Atari DOS DragonFly FreeBSD FreeRTOS Genode Haiku iOS Irix JS Linux MacOS MacOSX MorphOS NetBSD Netware NimVM NintendoSwitch OS2 OpenBSD PalmOS Standalone QNX SkyOS Solaris VxWorks Windows)' + '--panics\:-[turn panics into process termination (default\: off)]:x:(off on)' + ) + + case "$words[1]" in + compile|c|compileToC|cpp|compileToCpp|compileToOC|objc) + _arguments $codeOpts $runOpts $sharedOpts $nativeOpts \ + '*:filename:_files -g"*.nim"' + ;; + js) + _arguments $codeOpts $runOpts $sharedOpts \ + '*:filename:_files -g"*.nim"' + ;; + e) + _arguments $codeOpts $runOpts $sharedOpts \ + '*:filename:_files -g"*.nims"' + ;; + doc|doc2tex|jsondoc) + _arguments $runOpts $sharedOpts '*:filename:_files -g"*.nim"' + ;; + rst2html|rst2tex) + _arguments $runOpts $sharedOpts $docOpts '*:filename:_files -g"*.rst"' + ;; + buildIndex|genDepend|check) + _arguments $sharedOpts '*:filename:_files -g"*.nim"' + ;; + dump) + _arguments $sharedOpts + ;; + *) + _arguments '*:filename:_files -g"*"' + ;; + esac + + return 1 } _nim "$@" From aac54b9c7ffb75c9aab446574171b33bf23a29ed Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 19 Jan 2022 05:02:35 +0800 Subject: [PATCH 2773/3768] fix stricteffects (nimsuggest/sexp) (#19405) * fix stricteffects (nimsuggest/sexp) * Update tstrict_effects3.nim * Update tests/effects/tstrict_effects3.nim --- nimsuggest/sexp.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nimsuggest/sexp.nim b/nimsuggest/sexp.nim index cee538b6e..31f4988e1 100644 --- a/nimsuggest/sexp.nim +++ b/nimsuggest/sexp.nim @@ -409,7 +409,7 @@ macro convertSexp*(x: untyped): untyped = ## `%` for every element. result = toSexp(x) -proc `==`* (a,b: SexpNode): bool = +proc `==`* (a, b: SexpNode): bool {.noSideEffect.} = ## Check two nodes for equality if a.isNil: if b.isNil: return true From b3c178c2024f679a83275f63ca9e21a8b5ba73d7 Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 19 Jan 2022 15:39:22 +0800 Subject: [PATCH 2774/3768] suppress deprecated warnings (#19408) * suppress deprecated warnings once bump version to 1.7.3 enable deprecated messages * deprecate later --- lib/system.nim | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/system.nim b/lib/system.nim index 3b3e28344..02da048e4 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -213,8 +213,7 @@ proc `addr`*[T](x: T): ptr T {.magic: "Addr", noSideEffect.} = ## echo p[] # b discard -proc unsafeAddr*[T](x: T): ptr T {.magic: "Addr", noSideEffect, - deprecated: "'unsafeAddr' is a deprecated alias for 'addr'".} = +proc unsafeAddr*[T](x: T): ptr T {.magic: "Addr", noSideEffect.} = ## Builtin `addr` operator for taking the address of a memory ## location. ## From 23c4bbedcbf12a7ab87baefe589b472837d89dbf Mon Sep 17 00:00:00 2001 From: nblaxall <43897322+nblaxall@users.noreply.github.com> Date: Thu, 20 Jan 2022 00:25:21 +1300 Subject: [PATCH 2775/3768] add an example to setControlCHook (#19416) * add an example to setControlCHook * [skip CI] format example for setControlCHook Co-authored-by: Nathan Blaxall --- lib/system.nim | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/lib/system.nim b/lib/system.nim index 02da048e4..09de9fcde 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2319,6 +2319,15 @@ when notJSnotNims: proc setControlCHook*(hook: proc () {.noconv.}) ## Allows you to override the behaviour of your application when CTRL+C ## is pressed. Only one such hook is supported. + ## Example: + ## + ## .. code-block:: Nim + ## proc ctrlc() {.noconv.} = + ## echo "Ctrl+C fired!" + ## # do clean up stuff + ## quit() + ## + ## setControlCHook(ctrlc) when not defined(noSignalHandler) and not defined(useNimRtl): proc unsetControlCHook*() From 5d34e81f2363e1de97916ac60b028b9a8b287a4b Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 19 Jan 2022 19:37:30 +0800 Subject: [PATCH 2776/3768] fix term rewriting with sideeffect (#19410) * fix term rewriting with sideeffect fix #6217 * add tests * Update tests/template/template_various.nim --- compiler/parampatterns.nim | 7 +++++-- tests/template/t6217.nim | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 tests/template/t6217.nim diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim index eb99004ab..bfbc72675 100644 --- a/compiler/parampatterns.nim +++ b/compiler/parampatterns.nim @@ -143,8 +143,11 @@ proc checkForSideEffects*(n: PNode): TSideEffectAnalysis = let s = op.sym if sfSideEffect in s.flags: return seSideEffect - # assume no side effect: - result = seNoSideEffect + elif tfNoSideEffect in op.typ.flags: + result = seNoSideEffect + else: + # assume side effect: + result = seSideEffect elif tfNoSideEffect in op.typ.flags: # indirect call without side effects: result = seNoSideEffect diff --git a/tests/template/t6217.nim b/tests/template/t6217.nim new file mode 100644 index 000000000..b27b61881 --- /dev/null +++ b/tests/template/t6217.nim @@ -0,0 +1,19 @@ +discard """ + output: ''' +start +side effect! +end +''' +""" + +# bug #6217 + +template optMul{`*`(a, 2)}(a: int{noSideEffect}): int = a+a + +proc f(): int = + echo "side effect!" + result = 55 + +echo "start" +doAssert f() * 2 == 110 +echo "end" From 08261cb9e33445553144219023900b4ced0b0f55 Mon Sep 17 00:00:00 2001 From: metagn <10591326+metagn@users.noreply.github.com> Date: Wed, 19 Jan 2022 14:38:14 +0300 Subject: [PATCH 2777/3768] Don't reject types directly on AST (#19407) Instead of rejecting type expressions based on node kind, evaluate the expression as a type. This is already the behavior for call results, and it has its own error for non-types, which is the same error you would normally get with 2 words swapped. --- compiler/semtypes.nim | 6 ++++-- tests/types/tnontype.nim | 9 +++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 tests/types/tnontype.nim diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index d03fa88a8..b4f385fe6 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -1993,8 +1993,10 @@ proc semTypeNode(c: PContext, n: PNode, prev: PType): PType = of nkStmtListType: result = semStmtListType(c, n, prev) of nkBlockType: result = semBlockType(c, n, prev) else: - localError(c.config, n.info, "type expected, but got: " & renderTree(n)) - result = newOrPrevType(tyError, prev, c) + result = semTypeExpr(c, n, prev) + when false: + localError(c.config, n.info, "type expected, but got: " & renderTree(n)) + result = newOrPrevType(tyError, prev, c) n.typ = result dec c.inTypeContext diff --git a/tests/types/tnontype.nim b/tests/types/tnontype.nim new file mode 100644 index 000000000..4e2bafb32 --- /dev/null +++ b/tests/types/tnontype.nim @@ -0,0 +1,9 @@ +discard """ + errormsg: "expected type, but got: 3" +""" + +type + Foo = (block: + int) + + Bar = 3 From ce44cf03cc4a78741c423b2b3963b48b6d9e6755 Mon Sep 17 00:00:00 2001 From: Tom Date: Wed, 19 Jan 2022 09:41:11 -0700 Subject: [PATCH 2778/3768] Add noQuit option (#19419) [backport:1.6] * Add noQuit option * Add nim prefix in case of conflicts Co-authored-by: flywind Co-authored-by: flywind --- lib/system.nim | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/system.nim b/lib/system.nim index 09de9fcde..63cdc3ca8 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1198,8 +1198,8 @@ proc align(address, alignment: int): int = else: result = (address + (alignment - 1)) and not (alignment - 1) -when defined(nimdoc): - proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit", noreturn.} +when defined(nimNoQuit): + proc quit*(errorcode: int = QuitSuccess) = discard "ignoring quit" ## Stops the program immediately with an exit code. ## ## Before stopping the program the "exit procedures" are called in the @@ -1223,6 +1223,9 @@ when defined(nimdoc): ## It does *not* call the garbage collector to free all the memory, ## unless an `addExitProc` proc calls `GC_fullCollect <#GC_fullCollect>`_. +elif defined(nimdoc): + proc quit*(errorcode: int = QuitSuccess) {.magic: "Exit", noreturn.} + elif defined(genode): include genode/env From 4a38092ac1f8368cb1ebb0245c99c701963662f8 Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 20 Jan 2022 20:55:19 +0800 Subject: [PATCH 2779/3768] Added `std/envvars` for env vars handling (#19421) --- changelog.md | 4 +- lib/std/envvars.nim | 212 ++++++++++++++++++++++++++++++++++++++ tests/stdlib/tenvvars.nim | 59 +++++++++++ 3 files changed, 273 insertions(+), 2 deletions(-) create mode 100644 lib/std/envvars.nim create mode 100644 tests/stdlib/tenvvars.nim diff --git a/changelog.md b/changelog.md index 82f9daa1e..bbbaf6e97 100644 --- a/changelog.md +++ b/changelog.md @@ -36,6 +36,8 @@ becomes an alias for `addr`. - Added `getIsoWeekAndYear` in `times` to get an ISO week number along with the corresponding ISO week-based year from a datetime. - Added `getIsoWeeksInYear` in `times` to return the number of weeks in an ISO week-based year. +- Added `std/oserrors` for OS error reporting. Added `std/envvars` for environment variables handling. + ## Language changes - Pragma macros on type definitions can now return `nnkTypeSection` nodes as well as `nnkTypeDef`, @@ -74,8 +76,6 @@ becomes an alias for `addr`. for the right-hand side of type definitions in type sections. Previously they would error with "invalid indentation". -- Added `std/oserrors` for OS error reporting. - ## Compiler changes - `nim` can now compile version 1.4.0 as follows: `nim c --lib:lib --stylecheck:off compiler/nim`, diff --git a/lib/std/envvars.nim b/lib/std/envvars.nim new file mode 100644 index 000000000..5b135cbd3 --- /dev/null +++ b/lib/std/envvars.nim @@ -0,0 +1,212 @@ +# +# +# Nim's Runtime Library +# (c) Copyright 2022 Nim contributors +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + + +## The `std/envvars` module implements environment variables handling. +import std/oserrors + +type + ReadEnvEffect* = object of ReadIOEffect ## Effect that denotes a read + ## from an environment variable. + WriteEnvEffect* = object of WriteIOEffect ## Effect that denotes a write + ## to an environment variable. + + +when not defined(nimscript): + when defined(nodejs): + proc getEnv*(key: string, default = ""): string {.tags: [ReadEnvEffect].} = + var ret = default.cstring + let key2 = key.cstring + {.emit: "const value = process.env[`key2`];".} + {.emit: "if (value !== undefined) { `ret` = value };".} + result = $ret + + proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} = + var key2 = key.cstring + var ret: bool + {.emit: "`ret` = `key2` in process.env;".} + result = ret + + proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} = + var key2 = key.cstring + var val2 = val.cstring + {.emit: "process.env[`key2`] = `val2`;".} + + proc delEnv*(key: string) {.tags: [WriteEnvEffect].} = + var key2 = key.cstring + {.emit: "delete process.env[`key2`];".} + + iterator envPairsImpl(): tuple[key, value: string] {.tags: [ReadEnvEffect].} = + var num: int + var keys: RootObj + {.emit: "`keys` = Object.keys(process.env); `num` = `keys`.length;".} + for i in 0..".} + when defined(windows): + proc c_putenv(envstring: cstring): cint {.importc: "_putenv", header: "".} + from std/private/win_setenv import setEnvImpl + import winlean + else: + proc c_setenv(envname: cstring, envval: cstring, overwrite: cint): cint {.importc: "setenv", header: "".} + proc c_unsetenv(env: cstring): cint {.importc: "unsetenv", header: "".} + + proc getEnv*(key: string, default = ""): string {.tags: [ReadEnvEffect].} = + ## Returns the value of the `environment variable`:idx: named `key`. + ## + ## If the variable does not exist, `""` is returned. To distinguish + ## whether a variable exists or it's value is just `""`, call + ## `existsEnv(key) proc`_. + ## + ## See also: + ## * `existsEnv proc`_ + ## * `putEnv proc`_ + ## * `delEnv proc`_ + ## * `envPairs iterator`_ + runnableExamples: + assert getEnv("unknownEnv") == "" + assert getEnv("unknownEnv", "doesn't exist") == "doesn't exist" + + let env = c_getenv(key) + if env == nil: return default + result = $env + + proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} = + ## Checks whether the environment variable named `key` exists. + ## Returns true if it exists, false otherwise. + ## + ## See also: + ## * `getEnv proc`_ + ## * `putEnv proc`_ + ## * `delEnv proc`_ + ## * `envPairs iterator`_ + runnableExamples: + assert not existsEnv("unknownEnv") + + return c_getenv(key) != nil + + proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} = + ## Sets the value of the `environment variable`:idx: named `key` to `val`. + ## If an error occurs, `OSError` is raised. + ## + ## See also: + ## * `getEnv proc`_ + ## * `existsEnv proc`_ + ## * `delEnv proc`_ + ## * `envPairs iterator`_ + when defined(windows): + if key.len == 0 or '=' in key: + raise newException(OSError, "invalid key, got: " & $(key, val)) + if setEnvImpl(key, val, 1'i32) != 0'i32: + raiseOSError(osLastError(), $(key, val)) + else: + if c_setenv(key, val, 1'i32) != 0'i32: + raiseOSError(osLastError(), $(key, val)) + + proc delEnv*(key: string) {.tags: [WriteEnvEffect].} = + ## Deletes the `environment variable`:idx: named `key`. + ## If an error occurs, `OSError` is raised. + ## + ## See also:ven + ## * `getEnv proc`_ + ## * `existsEnv proc`_ + ## * `putEnv proc`_ + ## * `envPairs iterator`_ + template bail = raiseOSError(osLastError(), key) + when defined(windows): + #[ + # https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/putenv-s-wputenv-s?view=msvc-160 + > You can remove a variable from the environment by specifying an empty string (that is, "") for value_string + note that nil is not legal + ]# + if key.len == 0 or '=' in key: + raise newException(OSError, "invalid key, got: " & key) + let envToDel = key & "=" + if c_putenv(cstring envToDel) != 0'i32: bail + else: + if c_unsetenv(key) != 0'i32: bail + + when defined(windows): + when defined(cpp): + proc strEnd(cstr: WideCString, c = 0'i32): WideCString {.importcpp: "(NI16*)wcschr((const wchar_t *)#, #)", + header: "".} + else: + proc strEnd(cstr: WideCString, c = 0'i32): WideCString {.importc: "wcschr", + header: "".} + elif defined(macosx) and not defined(ios) and not defined(emscripten): + # From the manual: + # Shared libraries and bundles don't have direct access to environ, + # which is only available to the loader ld(1) when a complete program + # is being linked. + # The environment routines can still be used, but if direct access to + # environ is needed, the _NSGetEnviron() routine, defined in + # , can be used to retrieve the address of environ + # at runtime. + proc NSGetEnviron(): ptr cstringArray {.importc: "_NSGetEnviron", + header: "".} + elif defined(haiku): + var gEnv {.importc: "environ", header: "".}: cstringArray + else: + var gEnv {.importc: "environ".}: cstringArray + + iterator envPairsImpl(): tuple[key, value: string] {.tags: [ReadEnvEffect].} = + when defined(windows): + block: + template impl(get_fun, typ, size, zero, free_fun) = + let env = get_fun() + var e = env + if e == nil: break + while true: + let eend = strEnd(e) + let kv = $e + let p = find(kv, '=') + yield (substr(kv, 0, p-1), substr(kv, p+1)) + e = cast[typ](cast[ByteAddress](eend)+size) + if typeof(zero)(eend[1]) == zero: break + discard free_fun(env) + impl(getEnvironmentStringsW, WideCString, 2, 0, freeEnvironmentStringsW) + else: + var i = 0 + when defined(macosx) and not defined(ios) and not defined(emscripten): + var gEnv = NSGetEnviron()[] + while gEnv[i] != nil: + let kv = $gEnv[i] + inc(i) + let p = find(kv, '=') + yield (substr(kv, 0, p-1), substr(kv, p+1)) + +proc envPairsImplSeq(): seq[tuple[key, value: string]] = discard # vmops + +iterator envPairs*(): tuple[key, value: string] {.tags: [ReadEnvEffect].} = + ## Iterate over all `environments variables`:idx:. + ## + ## In the first component of the tuple is the name of the current variable stored, + ## in the second its value. + ## + ## Works in native backends, nodejs and vm, like the following APIs: + ## * `getEnv proc`_ + ## * `existsEnv proc`_ + ## * `putEnv proc`_ + ## * `delEnv proc`_ + when nimvm: + for ai in envPairsImplSeq(): yield ai + else: + when defined(nimscript): discard + else: + for ai in envPairsImpl(): yield ai diff --git a/tests/stdlib/tenvvars.nim b/tests/stdlib/tenvvars.nim new file mode 100644 index 000000000..406aa3230 --- /dev/null +++ b/tests/stdlib/tenvvars.nim @@ -0,0 +1,59 @@ +discard """ + matrix: "--threads:on" + joinable: false + targets: "c js cpp" +""" + +import std/envvars +from std/sequtils import toSeq +import stdtest/testutils + +template main = + block: # delEnv, existsEnv, getEnv, envPairs + for val in ["val", ""]: # ensures empty val works too + const key = "NIM_TESTS_TOSENV_KEY" + doAssert not existsEnv(key) + + putEnv(key, "tempval") + doAssert existsEnv(key) + doAssert getEnv(key) == "tempval" + + putEnv(key, val) # change a key that already exists + doAssert existsEnv(key) + doAssert getEnv(key) == val + + doAssert (key, val) in toSeq(envPairs()) + delEnv(key) + doAssert (key, val) notin toSeq(envPairs()) + doAssert not existsEnv(key) + delEnv(key) # deleting an already deleted env var + doAssert not existsEnv(key) + + block: + doAssert getEnv("NIM_TESTS_TOSENV_NONEXISTENT", "") == "" + doAssert getEnv("NIM_TESTS_TOSENV_NONEXISTENT", " ") == " " + doAssert getEnv("NIM_TESTS_TOSENV_NONEXISTENT", "defval") == "defval" + + whenVMorJs: discard # xxx improve + do: + doAssertRaises(OSError, putEnv("NIM_TESTS_TOSENV_PUT=DUMMY_VALUE", "NEW_DUMMY_VALUE")) + doAssertRaises(OSError, putEnv("", "NEW_DUMMY_VALUE")) + doAssert not existsEnv("") + doAssert not existsEnv("NIM_TESTS_TOSENV_PUT=DUMMY_VALUE") + doAssert not existsEnv("NIM_TESTS_TOSENV_PUT") + +main() + +when not defined(js) and not defined(nimscript): + block: # bug #18533 + proc c_getenv(env: cstring): cstring {.importc: "getenv", header: "".} + var thr: Thread[void] + proc threadFunc {.thread.} = putEnv("foo", "fooVal2") + + putEnv("foo", "fooVal1") + doAssert getEnv("foo") == "fooVal1" + createThread(thr, threadFunc) + joinThreads(thr) + doAssert getEnv("foo") == $c_getenv("foo") + + doAssertRaises(OSError): delEnv("foo=bar") From 851e515bbae6edf6b28cf6723b2c7a45acce1869 Mon Sep 17 00:00:00 2001 From: James Date: Thu, 20 Jan 2022 04:58:59 -0800 Subject: [PATCH 2780/3768] Resolve cross file resolution errors in atomics (#19422) [backport:1.6] * Resolve call undeclared routine testAndSet * Fix undeclared field atomicType --- lib/pure/concurrency/atomics.nim | 17 +++++++---------- tests/stdlib/concurrency/atomicSample.nim | 9 +++++++++ tests/stdlib/concurrency/tatomic_import.nim | 11 +++++++++++ 3 files changed, 27 insertions(+), 10 deletions(-) create mode 100644 tests/stdlib/concurrency/atomicSample.nim create mode 100644 tests/stdlib/concurrency/tatomic_import.nim diff --git a/lib/pure/concurrency/atomics.nim b/lib/pure/concurrency/atomics.nim index b252789fb..c0a122bf9 100644 --- a/lib/pure/concurrency/atomics.nim +++ b/lib/pure/concurrency/atomics.nim @@ -293,19 +293,16 @@ else: AtomicInt32 {.importc: "_Atomic NI32".} = int32 AtomicInt64 {.importc: "_Atomic NI64".} = int64 - template atomicType*(T: typedesc[Trivial]): untyped = - # Maps the size of a trivial type to it's internal atomic type - when sizeof(T) == 1: AtomicInt8 - elif sizeof(T) == 2: AtomicInt16 - elif sizeof(T) == 4: AtomicInt32 - elif sizeof(T) == 8: AtomicInt64 - type AtomicFlag* {.importc: "atomic_flag", size: 1.} = object Atomic*[T] = object when T is Trivial: - value: T.atomicType + # Maps the size of a trivial type to it's internal atomic type + when sizeof(T) == 1: value: AtomicInt8 + elif sizeof(T) == 2: value: AtomicInt16 + elif sizeof(T) == 4: value: AtomicInt32 + elif sizeof(T) == 8: value: AtomicInt64 else: nonAtomicValue: T guard: AtomicFlag @@ -364,11 +361,11 @@ else: cast[T](atomic_fetch_xor_explicit(addr(location.value), cast[nonAtomicType(T)](value), order)) template withLock[T: not Trivial](location: var Atomic[T]; order: MemoryOrder; body: untyped): untyped = - while location.guard.testAndSet(moAcquire): discard + while testAndSet(location.guard, moAcquire): discard try: body finally: - location.guard.clear(moRelease) + clear(location.guard, moRelease) proc load*[T: not Trivial](location: var Atomic[T]; order: MemoryOrder = moSequentiallyConsistent): T {.inline.} = withLock(location, order): diff --git a/tests/stdlib/concurrency/atomicSample.nim b/tests/stdlib/concurrency/atomicSample.nim new file mode 100644 index 000000000..d56d867df --- /dev/null +++ b/tests/stdlib/concurrency/atomicSample.nim @@ -0,0 +1,9 @@ +import atomics + +type + AtomicWithGeneric*[T] = object + value: Atomic[T] + +proc initAtomicWithGeneric*[T](value: T): AtomicWithGeneric[T] = + result.value.store(value) + diff --git a/tests/stdlib/concurrency/tatomic_import.nim b/tests/stdlib/concurrency/tatomic_import.nim new file mode 100644 index 000000000..e8faaae20 --- /dev/null +++ b/tests/stdlib/concurrency/tatomic_import.nim @@ -0,0 +1,11 @@ +import atomicSample + +block crossFileObjectContainingAGenericWithAComplexObject: + discard initAtomicWithGeneric[string]("foo") + +block crossFileObjectContainingAGenericWithAnInteger: + discard initAtomicWithGeneric[int](1) + discard initAtomicWithGeneric[int8](1) + discard initAtomicWithGeneric[int16](1) + discard initAtomicWithGeneric[int32](1) + discard initAtomicWithGeneric[int64](1) From 927fa890ec91cf13d7e3aa87267b994c2812bdc8 Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 20 Jan 2022 20:59:36 +0800 Subject: [PATCH 2781/3768] enable weave (#19363) [backport:1.6] * enable weave * workaround CI --- compiler/cgen.nim | 9 +++++++-- testament/important_packages.nim | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 032c22ec0..246b8c0e9 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -48,8 +48,13 @@ proc addForwardedProc(m: BModule, prc: PSym) = m.g.forwardedProcs.add(prc) proc findPendingModule(m: BModule, s: PSym): BModule = - let ms = s.itemId.module #getModule(s) - result = m.g.modules[ms] + # TODO fixme + if m.config.symbolFiles == v2Sf: + let ms = s.itemId.module #getModule(s) + result = m.g.modules[ms] + else: + var ms = getModule(s) + result = m.g.modules[ms.position] proc initLoc(result: var TLoc, k: TLocKind, lode: PNode, s: TStorageLoc) = result.k = k diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 853a58698..5f6e19b52 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -157,7 +157,7 @@ pkg "tiny_sqlite" pkg "unicodedb", "nim c -d:release -r tests/tests.nim" pkg "unicodeplus", "nim c -d:release -r tests/tests.nim" pkg "unpack" -pkg "weave", "nimble test_gc_arc", allowFailure = true +pkg "weave", "nimble test_gc_arc" pkg "websocket", "nim c websocket.nim" pkg "winim", "nim c winim.nim" pkg "with" From 1563cb2f6e37f07c303d095dabde74955be1e523 Mon Sep 17 00:00:00 2001 From: Regis Caillaud <35006197+Clonkk@users.noreply.github.com> Date: Thu, 20 Jan 2022 20:50:36 +0100 Subject: [PATCH 2782/3768] Fix #11923 (#19427) * Apply commit https://github.com/nim-lang/Nim/commit/5da931fe811717a45f2dd272ea6281979c3e8f0b that was never merged (was part of a bigger PR). Should fix issue #11932 * add a generic object for custom pragma --- lib/core/macros.nim | 7 ++++++- tests/pragmas/tcustom_pragma.nim | 10 ++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 8ab893d21..28106493f 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -1523,7 +1523,12 @@ proc customPragmaNode(n: NimNode): NimNode = if n.kind in {nnkDotExpr, nnkCheckedFieldExpr}: let name = $(if n.kind == nnkCheckedFieldExpr: n[0][1] else: n[1]) let typInst = getTypeInst(if n.kind == nnkCheckedFieldExpr or n[0].kind == nnkHiddenDeref: n[0][0] else: n[0]) - var typDef = getImpl(if typInst.kind == nnkVarTy: typInst[0] else: typInst) + var typDef = getImpl( + if typInst.kind == nnkVarTy or + typInst.kind == nnkBracketExpr: + typInst[0] + else: typInst + ) while typDef != nil: typDef.expectKind(nnkTypeDef) let typ = typDef[2] diff --git a/tests/pragmas/tcustom_pragma.nim b/tests/pragmas/tcustom_pragma.nim index b197a7c55..1c3709b26 100644 --- a/tests/pragmas/tcustom_pragma.nim +++ b/tests/pragmas/tcustom_pragma.nim @@ -17,11 +17,21 @@ block: MyObj = object myField1, myField2 {.myAttr: "hi".}: int + MyGenericObj[T] = object + myField1, myField2 {.myAttr: "hi".}: int + + var o: MyObj static: doAssert o.myField2.hasCustomPragma(myAttr) doAssert(not o.myField1.hasCustomPragma(myAttr)) + var ogen: MyGenericObj[int] + static: + doAssert ogen.myField2.hasCustomPragma(myAttr) + doAssert(not ogen.myField1.hasCustomPragma(myAttr)) + + import custom_pragma block: # A bit more advanced case type From 2bd1aa186e09565b2103394bd281478fa1b10ef1 Mon Sep 17 00:00:00 2001 From: metagn <10591326+metagn@users.noreply.github.com> Date: Thu, 20 Jan 2022 22:57:50 +0300 Subject: [PATCH 2783/3768] New/better macro pragmas, mark some as experimental (#19406) * New/better macro pragmas, make some experimental fix #15920, close #18212, close #14781, close #6696, close https://github.com/nim-lang/RFCs/issues/220 Variable macro pragmas have been changed to only take a unary section node. They can now also be applied in sections with multiple variables, as well as `const` sections. They also accept arguments. Templates now support macro pragmas, mirroring other routine types. Type and variable macro pragmas have been made experimental. Symbols without parentheses instatiating nullary macros or templates has also been documented in the experimental manual. A check for a redefinition error based on the left hand side of variable definitions when using variable macro pragmas was disabled. This nerfs `byaddr` specifically, however this has been documented as a consequence of the experimental features `byaddr` uses. Given how simple these changes are I'm worried if I'm missing something. * accomodate compiler boot * allow weird pragmas * add test for #10994 * remove some control flow, try remove some logic --- compiler/semstmts.nim | 280 +++++++++++++++++++------------- compiler/semtempl.nim | 5 + doc/manual.rst | 28 ++-- doc/manual_experimental.rst | 72 +++++++- lib/std/decls.nim | 39 +++-- tests/pragmas/tpragmas_misc.nim | 24 ++- tests/pragmas/tvar_macro.nim | 128 +++++++++++++++ tests/stdlib/tdecls.nim | 64 ++------ 8 files changed, 435 insertions(+), 205 deletions(-) create mode 100644 tests/pragmas/tvar_macro.nim diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index f302dd4c3..33e304ab2 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -341,8 +341,12 @@ proc checkNilable(c: PContext; v: PSym) = #include liftdestructors -proc addToVarSection(c: PContext; result: PNode; orig, identDefs: PNode) = - let value = identDefs[^1] +proc addToVarSection(c: PContext; result: var PNode; n: PNode) = + if result.kind != nkStmtList: + result = makeStmtList(result) + result.add n + +proc addToVarSection(c: PContext; result: var PNode; orig, identDefs: PNode) = if result.kind == nkStmtList: let o = copyNode(orig) o.add identDefs @@ -445,55 +449,115 @@ proc setVarType(c: PContext; v: PSym, typ: PType) = "; new type is: " & typeToString(typ, preferDesc)) v.typ = typ -proc semLowerLetVarCustomPragma(c: PContext, a: PNode, n: PNode): PNode = - var b = a[0] - if b.kind == nkPragmaExpr: - if b[1].len != 1: - # we could in future support pragmas w args e.g.: `var foo {.bar:"goo".} = expr` - return nil - let nodePragma = b[1][0] - # see: `singlePragma` +proc isPossibleMacroPragma(c: PContext, it: PNode, key: PNode): bool = + # make sure it's not a normal pragma, and calls an identifier + # considerQuotedIdent below will fail on non-identifiers + result = whichPragma(it) == wInvalid and key.kind in nkIdentKinds + if result: + # make sure it's not a user pragma + let ident = considerQuotedIdent(c, key) + result = strTableGet(c.userPragmas, ident) == nil + if result: + # make sure it's not a custom pragma + var amb = false + let sym = searchInScopes(c, ident, amb) + result = sym == nil or sfCustomPragma notin sym.flags - var amb = false - var sym: PSym = nil - case nodePragma.kind - of nkIdent, nkAccQuoted: - let ident = considerQuotedIdent(c, nodePragma) - var userPragma = strTableGet(c.userPragmas, ident) - if userPragma != nil: return nil - let w = nodePragma.whichPragma - if n.kind == nkVarSection and w in varPragmas or - n.kind == nkLetSection and w in letPragmas or - n.kind == nkConstSection and w in constPragmas: - return nil - sym = searchInScopes(c, ident, amb) - # XXX what if amb is true? - # CHECKME: should that test also apply to `nkSym` case? - if sym == nil or sfCustomPragma in sym.flags: return nil - of nkSym: - sym = nodePragma.sym - else: - return nil - # skip if not in scope; skip `template myAttr() {.pragma.}` +proc copyExcept(n: PNode, i: int): PNode = + result = copyNode(n) + for j in 0..= 1: it[0] else: it + + when false: + let lhs = b[0] + let clash = strTableGet(c.currentScope.symbols, lhs.ident) + if clash != nil: + # refs https://github.com/nim-lang/Nim/issues/8275 + wrongRedefinition(c, lhs.info, lhs.ident.s, clash.info) - result = newTree(nkCall) - result.add nodePragma - result.add lhs - if a[1].kind != nkEmpty: - result.add a[1] - else: - result.add newNodeIT(nkNilLit, a.info, c.graph.sysTypes[tyNil]) - result.add a[2] - result.info = a.info - let ret = newNodeI(nkStmtList, a.info) - ret.add result - result = semExprNoType(c, ret) + if isPossibleMacroPragma(c, it, key): + # we transform ``var p {.m, rest.}`` into ``m(do: var p {.rest.})`` and + # let the semantic checker deal with it: + var x = newNodeI(nkCall, key.info) + x.add(key) + + if it.kind in nkPragmaCallKinds and it.len > 1: + # pass pragma arguments to the macro too: + for i in 1..= 1: it[0] else: it - if whichPragma(it) != wInvalid: - # Not a custom pragma - continue - else: - let ident = considerQuotedIdent(c, key) - if strTableGet(c.userPragmas, ident) != nil: - continue # User defined pragma + if isPossibleMacroPragma(c, it, key): + # we transform ``proc p {.m, rest.}`` into ``m(do: proc p {.rest.})`` and + # let the semantic checker deal with it: + var x = newNodeI(nkCall, key.info) + x.add(key) + + if it.kind in nkPragmaCallKinds and it.len > 1: + # pass pragma arguments to the macro too: + for i in 1.. 1: - # pass pragma arguments to the macro too: - for i in 1..` instead. Foreign function interface diff --git a/doc/manual_experimental.rst b/doc/manual_experimental.rst index 407924a13..3089755cb 100644 --- a/doc/manual_experimental.rst +++ b/doc/manual_experimental.rst @@ -404,6 +404,76 @@ to use this operator. doAssert (a.b)(c) == `()`(a.b, c) +Extended macro pragmas +====================== + +Macro pragmas as described in `the manual `_ +can also be applied to type, variable and constant declarations. + +For types: + +.. code-block:: nim + type + MyObject {.schema: "schema.protobuf".} = object + +This is translated to a call to the `schema` macro with a `nnkTypeDef` +AST node capturing the left-hand side, remaining pragmas and the right-hand +side of the definition. The macro can return either a type section or +another `nnkTypeDef` node, both of which will replace the original row +in the type section. + +In the future, this `nnkTypeDef` argument may be replaced with a unary +type section node containing the type definition, or some other node that may +be more convenient to work with. The ability to return nodes other than type +definitions may also be supported, however currently this is not convenient +when dealing with mutual type recursion. For now, macros can return an unused +type definition where the right-hand node is of kind `nnkStmtListType`. +Declarations in this node will be attached to the same scope as +the parent scope of the type section. + +------ + +For variables and constants, it is largely the same, except a unary node with +the same kind as the section containing a single definition is passed to macros, +and macros can return any expression. + +.. code-block:: nim + var + a = ... + b {.importc, foo, nodecl.} = ... + c = ... + +Assuming `foo` is a macro or a template, this is roughly equivalent to: + +.. code-block:: nim + var a = ... + foo: + var b {.importc, nodecl.} = ... + var c = ... + + +Symbols as template/macro calls +=============================== + +Templates and macros that take no arguments can be called as lone symbols, +i.e. without parentheses. This is useful for repeated uses of complex +expressions that cannot conveniently be represented as runtime values. + +.. code-block:: nim + type Foo = object + bar: int + + var foo = Foo(bar: 10) + template bar: untyped = foo.bar + assert bar == 10 + bar = 15 + assert bar == 15 + +In the future, this may require more specific information on template or macro +signatures to be used. Specializations for some applications of this may also +be introduced to guarantee consistency and circumvent bugs. + + Not nil annotation ================== @@ -613,7 +683,7 @@ has `source` as the owner. A path expression `e` is defined recursively: If a view type is used as a return type, the location must borrow from a location that is derived from the first parameter that is passed to the proc. -See `the manual `_ +See `the manual `_ for details about how this is done for `var T`. A mutable view can borrow from a mutable location, an immutable view can borrow diff --git a/lib/std/decls.nim b/lib/std/decls.nim index dd7d19da7..7b907f5e1 100644 --- a/lib/std/decls.nim +++ b/lib/std/decls.nim @@ -1,19 +1,32 @@ # see `semLowerLetVarCustomPragma` for compiler support that enables these # lowerings -template byaddr*(lhs, typ, ex) = - ## Allows a syntax for lvalue reference, exact analog to - ## `auto& a = ex;` in C++ +import macros + +macro byaddr*(sect) = + ## Allows a syntax for l-value references, being an exact analog to + ## `auto& a = ex;` in C++. + ## + ## Warning: This makes use of 2 experimental features, namely nullary + ## templates instantiated as symbols and variable macro pragmas. + ## For this reason, its behavior is not stable. The current implementation + ## allows redefinition, but this is not an intended consequence. runnableExamples: - var s = @[10,11,12] + var s = @[10, 11, 12] var a {.byaddr.} = s[0] - a+=100 - doAssert s == @[110,11,12] - doAssert a is int + a += 100 + assert s == @[110, 11, 12] + assert a is int var b {.byaddr.}: int = s[0] - doAssert a.addr == b.addr - when typ is typeof(nil): - let tmp = addr(ex) - else: - let tmp: ptr typ = addr(ex) - template lhs: untyped = tmp[] + assert a.addr == b.addr + expectLen sect, 1 + let def = sect[0] + let + lhs = def[0] + typ = def[1] + ex = def[2] + addrTyp = if typ.kind == nnkEmpty: typ else: newTree(nnkPtrTy, typ) + result = quote do: + let tmp: `addrTyp` = addr(`ex`) + template `lhs`: untyped = tmp[] + result.copyLineInfo(def) diff --git a/tests/pragmas/tpragmas_misc.nim b/tests/pragmas/tpragmas_misc.nim index 8cab74053..6dc2e6b80 100644 --- a/tests/pragmas/tpragmas_misc.nim +++ b/tests/pragmas/tpragmas_misc.nim @@ -13,8 +13,8 @@ block: block: # (partial fix) bug #15920 block: # var template pragmas don't work in templates - template foo(lhs, typ, expr) = - let lhs = expr + template foo(expr) = + expr proc fun1()= let a {.foo.} = 1 template fun2()= @@ -24,23 +24,22 @@ block: # (partial fix) bug #15920 template foo2() = discard # distractor (template or other symbol kind) block: - template foo2(lhs, typ, expr) = - let lhs = expr + template foo2(expr) = + expr proc fun1()= let a {.foo2.} = 1 template fun2()= let a {.foo2.} = 1 fun1() # ok - when false: # bug: Error: invalid pragma: foo2 - fun2() + fun2() # bug: Error: invalid pragma: foo2 - block: # proc template pragmas don't work in templates + block: # template pragmas don't work for templates, #18212 # adapted from $nim/lib/std/private/since.nim # case without overload template since3(version: (int, int), body: untyped) {.dirty.} = when (NimMajor, NimMinor) >= version: body - when false: # bug + when true: # bug template fun3(): int {.since3: (1, 3).} = 12 block: # ditto, w @@ -51,7 +50,7 @@ block: # (partial fix) bug #15920 template since2(version: (int, int, int), body: untyped) {.dirty.} = when (NimMajor, NimMinor, NimPatch) >= version: body - when false: # bug + when true: # bug template fun3(): int {.since2: (1, 3).} = 12 when true: # D20210801T100514:here @@ -62,3 +61,10 @@ when true: # D20210801T100514:here discard ret fn() static: discard genSym() + +block: # issue #10994 + macro foo(x): untyped = x + template bar {.pragma.} + + proc a {.bar.} = discard # works + proc b {.bar, foo.} = discard # doesn't diff --git a/tests/pragmas/tvar_macro.nim b/tests/pragmas/tvar_macro.nim new file mode 100644 index 000000000..d6a4ff983 --- /dev/null +++ b/tests/pragmas/tvar_macro.nim @@ -0,0 +1,128 @@ +import macros + +block: # test usage + macro modify(sec) = + result = copy sec + result[0][0] = ident(repr(result[0][0]) & "Modified") + + block: + let foo {.modify.} = 3 + doAssert fooModified == 3 + + block: # in section + let + a = 1 + b {.modify.} = 2 + c = 3 + doAssert (a, bModified, c) == (1, 2, 3) + +block: # with single argument + macro appendToName(name: static string, sec) = + result = sec + result[0][0] = ident(repr(result[0][0]) & name) + + block: + let foo {.appendToName: "Bar".} = 3 + doAssert fooBar == 3 + + block: + let + a = 1 + b {.appendToName("").} = 2 + c = 3 + doAssert (a, b, c) == (1, 2, 3) + +macro appendToNameAndAdd(name: static string, incr: static int, sec) = + result = sec + result[0][0] = ident(repr(result[0][0]) & name) + result[0][2] = infix(result[0][2], "+", newLit(incr)) + +block: # with multiple arguments + block: + let foo {.appendToNameAndAdd("Bar", 5).} = 3 + doAssert fooBar == 8 + + block: + let + a = 1 + b {.appendToNameAndAdd("", 15).} = 2 + c = 3 + doAssert (a, b, c) == (1, 17, 3) + +block: # in other kinds of sections + block: + const + a = 1 + b {.appendToNameAndAdd("", 15).} = 2 + c = 3 + doAssert (a, b, c) == (1, 17, 3) + doAssert static(b) == b + + block: + var + a = 1 + b {.appendToNameAndAdd("", 15).} = 2 + c = 3 + doAssert (a, b, c) == (1, 17, 3) + b += a + c += b + doAssert (a, b, c) == (1, 18, 21) + +block: # with other pragmas + macro appendToNameAndAdd(name: static string, incr, sec) = + result = sec + result[0][0][0] = ident(repr(result[0][0][0]) & name) + result[0][0][1].add(ident"deprecated") + result[0][2] = infix(result[0][2], "+", incr) + + var + a = 1 + foo {.exportc: "exportedFooBar", appendToNameAndAdd("Bar", {'0'..'9'}), used.} = {'a'..'z', 'A'..'Z'} + b = 2 + + doAssert (a, b) == (1, 2) + + let importedFooBar {.importc: "exportedFooBar", nodecl.}: set[char] + + doAssert importedFooBar == fooBar #[tt.Warning + ^ fooBar is deprecated + ]# + + +block: # with stropping + macro `cast`(def) = + let def = def[0] + let + lhs = def[0] + typ = def[1] + ex = def[2] + addrTyp = if typ.kind == nnkEmpty: typ else: newTree(nnkPtrTy, typ) + result = quote do: + let tmp: `addrTyp` = unsafeAddr(`ex`) + template `lhs`: untyped = tmp[] + + macro assign(def) = + result = getAst(`cast`(def)) + + block: + let s = @["foo", "bar"] + let a {.`assign`.} = s[0] + doAssert a == "foo" + doAssert a[0].addr == s[0][0].addr + + block: + let + s = @["foo", "bar"] + a {.`cast`.} = s[0] + doAssert a == "foo" + doAssert a[0].addr == s[0][0].addr + +block: # bug #15920 + macro foo(def) = + result = def + proc fun1()= + let a {.foo.} = 1 + template fun2()= + let a {.foo.} = 1 + fun1() # ok + fun2() # BUG diff --git a/tests/stdlib/tdecls.nim b/tests/stdlib/tdecls.nim index c0d6f8a08..4e7407045 100644 --- a/tests/stdlib/tdecls.nim +++ b/tests/stdlib/tdecls.nim @@ -13,15 +13,18 @@ template fun() = var b {.byaddr.}: int = s[0] doAssert a.addr == b.addr - doAssert not compiles(block: - # redeclaration not allowed - var foo = 0 - var foo {.byaddr.} = s[0]) + when false: + # template specific redeclaration issue + # see https://github.com/nim-lang/Nim/issues/8275 + doAssert not compiles(block: + # redeclaration not allowed + var foo = 0 + var foo {.byaddr.} = s[0]) - doAssert not compiles(block: - # ditto - var foo {.byaddr.} = s[0] - var foo {.byaddr.} = s[0]) + doAssert not compiles(block: + # ditto + var foo {.byaddr.} = s[0] + var foo {.byaddr.} = s[0]) block: var b {.byaddr.} = s[1] # redeclaration ok in sub scope @@ -44,48 +47,3 @@ fun2() static: fun2() when false: # pending bug #13887 static: fun() - -## We can define custom pragmas in user code -template byUnsafeAddr(lhs, typ, expr) = - when typ is type(nil): - let tmp = addr(expr) - else: - let tmp: ptr typ = addr(expr) - template lhs: untyped = tmp[] - -block: - let s = @["foo", "bar"] - let a {.byUnsafeAddr.} = s[0] - doAssert a == "foo" - doAssert a[0].addr == s[0][0].addr - -block: # nkAccQuoted - # shows using a keyword, which requires nkAccQuoted - template `cast`(lhs, typ, expr) = - when typ is type(nil): - let tmp = addr(expr) - else: - let tmp: ptr typ = addr(expr) - template lhs: untyped = tmp[] - - block: - let s = @["foo", "bar"] - let a {.`byUnsafeAddr`.} = s[0] - doAssert a == "foo" - doAssert a[0].addr == s[0][0].addr - - block: - let s = @["foo", "bar"] - let a {.`cast`.} = s[0] - doAssert a == "foo" - doAssert a[0].addr == s[0][0].addr - -block: # bug #15920 - template foo(lhs, typ, expr) = - let lhs = expr - proc fun1()= - let a {.foo.} = 1 - template fun2()= - let a {.foo.} = 1 - fun1() # ok - fun2() # BUG From 15f54de5c4ee2f70e1304201ebc8c8634b731c9a Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 22 Jan 2022 20:33:55 +0100 Subject: [PATCH 2784/3768] RST: allow empty number-lines directives just like it was done for a decade; all my documents rely on this feature [backport (#19431) --- lib/packages/docutils/rstgen.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 9018087f7..d2180cb91 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -931,7 +931,8 @@ proc getField1Int(d: PDoc, n: PRstNode, fieldName: string): int = let nChars = parseInt(value, number) if nChars == 0: if value.len == 0: - err("field $1 requires an argument" % [fieldName]) + # use a good default value: + result = 1 else: err("field $1 requires an integer, but '$2' was given" % [fieldName, value]) From f7c4fb0014f320824d8f05e2e4d9776c09f51711 Mon Sep 17 00:00:00 2001 From: ehmry Date: Mon, 24 Jan 2022 20:57:37 +0100 Subject: [PATCH 2785/3768] os: faster getFileSize (#19438) Use "stat" rather than "open", "seek", and "close" system calls. The Windows implementation remains the same. --- lib/pure/os.nim | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 81b432d7f..07040d611 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -3216,11 +3216,10 @@ proc getFileSize*(file: string): BiggestInt {.rtl, extern: "nos$1", result = rdFileSize(a) findClose(resA) else: - var f: File - if open(f, file): - result = getFileSize(f) - close(f) - else: raiseOSError(osLastError(), file) + var rawInfo: Stat + if stat(file, rawInfo) < 0'i32: + raiseOSError(osLastError(), file) + rawInfo.st_size when defined(windows) or weirdTarget: type From 6ea622552345fc9a6c451615079d2a0ebbc6c4ca Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 25 Jan 2022 08:08:22 +0100 Subject: [PATCH 2786/3768] bugfix: varargs count as open arrays (#19447) --- compiler/typeallowed.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/typeallowed.nim b/compiler/typeallowed.nim index 78cd74e56..3d6ea0edb 100644 --- a/compiler/typeallowed.nim +++ b/compiler/typeallowed.nim @@ -233,7 +233,7 @@ proc classifyViewTypeAux(marker: var IntSet, t: PType): ViewTypeKind = case t.kind of tyVar: result = mutableView - of tyLent, tyOpenArray: + of tyLent, tyOpenArray, tyVarargs: result = immutableView of tyGenericInst, tyDistinct, tyAlias, tyInferred, tySink, tyOwned, tyUncheckedArray, tySequence, tyArray, tyRef, tyStatic: From 4b723c0f5350c5bfb9722dace7a40c66a4630a68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hugo=20Granstr=C3=B6m?= <5092565+HugoGranstrom@users.noreply.github.com> Date: Tue, 25 Jan 2022 10:31:24 +0100 Subject: [PATCH 2787/3768] change run command for numericalnim (#19448) Now it makes runs the custom `nimCI` task that installs the external dependencies --- testament/important_packages.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 5f6e19b52..40a3aef36 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -117,7 +117,7 @@ pkg "nimx", "nim c --threads:on test/main.nim", allowFailure = true pkg "nitter", "nim c src/nitter.nim", "https://github.com/zedeus/nitter" pkg "norm", "testament r tests/sqlite/trows.nim" pkg "npeg", "nimble testarc" -pkg "numericalnim", "nim c -r tests/test_integrate.nim" +pkg "numericalnim", "nimble nimCI" pkg "optionsutils" pkg "ormin", "nim c -o:orminn ormin.nim" pkg "parsetoml" From 6319b00ef7f93fa8d7d44c981f850262d8e0ed6a Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 27 Jan 2022 22:23:34 +0800 Subject: [PATCH 2788/3768] support set other GCs after `mm = orc` or `mm = arc` is set in the global config (#19455) * support set other GCs after arc/orc in global config fix #15535 * set before --- compiler/commands.nim | 54 +++++++++++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 20 deletions(-) diff --git a/compiler/commands.nim b/compiler/commands.nim index 8e79a4477..a1fa55e74 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -497,6 +497,32 @@ proc specialDefine(conf: ConfigRef, key: string; pass: TCmdLinePass) = optOverflowCheck, optAssert, optStackTrace, optLineTrace, optLineDir} conf.globalOptions.excl {optCDebug} +proc registerArcOrc(pass: TCmdLinePass, conf: ConfigRef, isOrc: bool) = + if isOrc: + conf.selectedGC = gcOrc + defineSymbol(conf.symbols, "gcorc") + else: + conf.selectedGC = gcArc + defineSymbol(conf.symbols, "gcarc") + + defineSymbol(conf.symbols, "gcdestructors") + incl conf.globalOptions, optSeqDestructors + incl conf.globalOptions, optTinyRtti + if pass in {passCmd2, passPP}: + defineSymbol(conf.symbols, "nimSeqsV2") + defineSymbol(conf.symbols, "nimV2") + if conf.exc == excNone and conf.backend != backendCpp: + conf.exc = excGoto + +proc unregisterArcOrc(conf: ConfigRef) = + undefSymbol(conf.symbols, "gcdestructors") + undefSymbol(conf.symbols, "gcarc") + undefSymbol(conf.symbols, "gcorc") + undefSymbol(conf.symbols, "nimSeqsV2") + undefSymbol(conf.symbols, "nimV2") + excl conf.globalOptions, optSeqDestructors + excl conf.globalOptions, optTinyRtti + proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; conf: ConfigRef) = var @@ -602,36 +628,21 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; if pass in {passCmd2, passPP}: case arg.normalize of "boehm": + unregisterArcOrc(conf) conf.selectedGC = gcBoehm defineSymbol(conf.symbols, "boehmgc") incl conf.globalOptions, optTlsEmulation # Boehm GC doesn't scan the real TLS of "refc": + unregisterArcOrc(conf) conf.selectedGC = gcRefc of "markandsweep": + unregisterArcOrc(conf) conf.selectedGC = gcMarkAndSweep defineSymbol(conf.symbols, "gcmarkandsweep") of "destructors", "arc": - conf.selectedGC = gcArc - defineSymbol(conf.symbols, "gcdestructors") - defineSymbol(conf.symbols, "gcarc") - incl conf.globalOptions, optSeqDestructors - incl conf.globalOptions, optTinyRtti - if pass in {passCmd2, passPP}: - defineSymbol(conf.symbols, "nimSeqsV2") - defineSymbol(conf.symbols, "nimV2") - if conf.exc == excNone and conf.backend != backendCpp: - conf.exc = excGoto + registerArcOrc(pass, conf, false) of "orc": - conf.selectedGC = gcOrc - defineSymbol(conf.symbols, "gcdestructors") - defineSymbol(conf.symbols, "gcorc") - incl conf.globalOptions, optSeqDestructors - incl conf.globalOptions, optTinyRtti - if pass in {passCmd2, passPP}: - defineSymbol(conf.symbols, "nimSeqsV2") - defineSymbol(conf.symbols, "nimV2") - if conf.exc == excNone and conf.backend != backendCpp: - conf.exc = excGoto + registerArcOrc(pass, conf, true) of "hooks": conf.selectedGC = gcHooks defineSymbol(conf.symbols, "gchooks") @@ -640,12 +651,15 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; if pass in {passCmd2, passPP}: defineSymbol(conf.symbols, "nimSeqsV2") of "go": + unregisterArcOrc(conf) conf.selectedGC = gcGo defineSymbol(conf.symbols, "gogc") of "none": + unregisterArcOrc(conf) conf.selectedGC = gcNone defineSymbol(conf.symbols, "nogc") of "stack", "regions": + unregisterArcOrc(conf) conf.selectedGC = gcRegions defineSymbol(conf.symbols, "gcregions") of "v2": warningOptionNoop(arg) From 0c3892c3c707e75520d6c8b5a65a0eaf8c15fcd4 Mon Sep 17 00:00:00 2001 From: flywind Date: Fri, 28 Jan 2022 16:53:42 +0800 Subject: [PATCH 2789/3768] nvro don't touch cdecl types [backport: 1.6] (#19461) * nvro don't touch cdecl types; fix #19342 again --- compiler/ast.nim | 2 +- compiler/ccgcalls.nim | 6 +++--- compiler/ccgstmts.nim | 21 ++++++++++++++------- compiler/ccgtypes.nim | 18 ++++++++++++------ compiler/cgen.nim | 4 ++-- compiler/semstmts.nim | 1 + tests/objects/t19342_2.nim | 18 ++++++++++++++++++ 7 files changed, 51 insertions(+), 19 deletions(-) create mode 100644 tests/objects/t19342_2.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index 4286f940f..ad5ec12f5 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -501,7 +501,7 @@ type nfHasComment # node has a comment TNodeFlags* = set[TNodeFlag] - TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 43) + TTypeFlag* = enum # keep below 32 for efficiency reasons (now: 45) tfVarargs, # procedure has C styled varargs # tyArray type represeting a varargs list tfNoSideEffect, # procedure type does not allow side effects diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index fe0516f9a..197728ccf 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -76,7 +76,7 @@ proc fixupCall(p: BProc, le, ri: PNode, d: var TLoc, # getUniqueType() is too expensive here: var typ = skipTypes(ri[0].typ, abstractInst) if typ[0] != nil: - if isInvalidReturnType(p.config, typ[0]): + if isInvalidReturnType(p.config, typ): if params != nil: pl.add(~", ") # beware of 'result = p(result)'. We may need to allocate a temporary: if d.k in {locTemp, locNone} or not preventNrvo(p, le, ri): @@ -439,7 +439,7 @@ proc genClosureCall(p: BProc, le, ri: PNode, d: var TLoc) = let rawProc = getClosureType(p.module, typ, clHalf) let canRaise = p.config.exc == excGoto and canRaiseDisp(p, ri[0]) if typ[0] != nil: - if isInvalidReturnType(p.config, typ[0]): + if isInvalidReturnType(p.config, typ): if ri.len > 1: pl.add(~", ") # beware of 'result = p(result)'. We may need to allocate a temporary: if d.k in {locTemp, locNone} or not preventNrvo(p, le, ri): @@ -737,7 +737,7 @@ proc genNamedParamCall(p: BProc, ri: PNode, d: var TLoc) = pl.add(~": ") pl.add(genArg(p, ri[i], param, ri)) if typ[0] != nil: - if isInvalidReturnType(p.config, typ[0]): + if isInvalidReturnType(p.config, typ): if ri.len > 1: pl.add(~" ") # beware of 'result = p(result)'. We always allocate a temporary: if d.k in {locTemp, locNone}: diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 7fecc1475..58a6dd015 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -32,13 +32,20 @@ proc registerTraverseProc(p: BProc, v: PSym, traverseProc: Rope) = "$n\t#nimRegisterGlobalMarker($1);$n$n", [traverseProc]) proc isAssignedImmediately(conf: ConfigRef; n: PNode): bool {.inline.} = - if n.kind == nkEmpty: return false - if isInvalidReturnType(conf, n.typ): - # var v = f() - # is transformed into: var v; f(addr v) - # where 'f' **does not** initialize the result! - return false - result = true + if n.kind == nkEmpty: + result = false + elif n.kind in nkCallKinds and n[0] != nil and n[0].typ != nil and n[0].typ.skipTypes(abstractInst).kind == tyProc: + if isInvalidReturnType(conf, n[0].typ, true): + # var v = f() + # is transformed into: var v; f(addr v) + # where 'f' **does not** initialize the result! + result = false + else: + result = true + elif isInvalidReturnType(conf, n.typ, false): + result = false + else: + result = true proc inExceptBlockLen(p: BProc): int = for x in p.nestedTryStmts: diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index df1874b26..f010b25c9 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -215,12 +215,18 @@ proc isObjLackingTypeField(typ: PType): bool {.inline.} = result = (typ.kind == tyObject) and ((tfFinal in typ.flags) and (typ[0] == nil) or isPureObject(typ)) -proc isInvalidReturnType(conf: ConfigRef; rettype: PType): bool = +proc isInvalidReturnType(conf: ConfigRef; typ: PType, isProc = true): bool = # Arrays and sets cannot be returned by a C procedure, because C is # such a poor programming language. # We exclude records with refs too. This enhances efficiency and # is necessary for proper code generation of assignments. - if rettype == nil or (tfByCopy notin rettype.flags and getSize(conf, rettype) > conf.target.floatSize*3): + var rettype = typ + var isAllowedCall = true + if isProc: + rettype = rettype[0] + isAllowedCall = typ.callConv in {ccClosure, ccInline, ccNimCall} + if rettype == nil or (isAllowedCall and + getSize(conf, rettype) > conf.target.floatSize*3): result = true else: case mapType(conf, rettype, skResult) @@ -257,11 +263,11 @@ proc addAbiCheck(m: BModule, t: PType, name: Rope) = # see `testCodegenABICheck` for example error message it generates -proc fillResult(conf: ConfigRef; param: PNode) = +proc fillResult(conf: ConfigRef; param: PNode, proctype: PType) = fillLoc(param.sym.loc, locParam, param, ~"Result", OnStack) let t = param.sym.typ - if mapReturnType(conf, t) != ctArray and isInvalidReturnType(conf, t): + if mapReturnType(conf, t) != ctArray and isInvalidReturnType(conf, proctype): incl(param.sym.loc.flags, lfIndirect) param.sym.loc.storage = OnUnknown @@ -426,7 +432,7 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope, check: var IntSet, declareEnvironment=true; weakDep=false) = params = nil - if t[0] == nil or isInvalidReturnType(m.config, t[0]): + if t[0] == nil or isInvalidReturnType(m.config, t): rettype = ~"void" else: rettype = getTypeDescAux(m, t[0], check, skResult) @@ -461,7 +467,7 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope, params.addf(", NI $1Len_$2", [param.loc.r, j.rope]) inc(j) arr = arr[0].skipTypes({tySink}) - if t[0] != nil and isInvalidReturnType(m.config, t[0]): + if t[0] != nil and isInvalidReturnType(m.config, t): var arr = t[0] if params != nil: params.add(", ") if mapReturnType(m.config, t[0]) != ctArray: diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 246b8c0e9..10b4b55d0 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -1039,7 +1039,7 @@ proc genProcAux(m: BModule, prc: PSym) = internalError(m.config, prc.info, "proc has no result symbol") let resNode = prc.ast[resultPos] let res = resNode.sym # get result symbol - if not isInvalidReturnType(m.config, prc.typ[0]): + if not isInvalidReturnType(m.config, prc.typ): if sfNoInit in prc.flags: incl(res.flags, sfNoInit) if sfNoInit in prc.flags and p.module.compileToCpp and (let val = easyResultAsgn(procBody); val != nil): var decl = localVarDecl(p, resNode) @@ -1053,7 +1053,7 @@ proc genProcAux(m: BModule, prc: PSym) = initLocalVar(p, res, immediateAsgn=false) returnStmt = ropecg(p.module, "\treturn $1;$n", [rdLoc(res.loc)]) else: - fillResult(p.config, resNode) + fillResult(p.config, resNode, prc.typ) assignParam(p, res, prc.typ[0]) # We simplify 'unsureAsgn(result, nil); unsureAsgn(result, x)' # to 'unsureAsgn(result, x)' diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 33e304ab2..c2b385c80 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -2137,6 +2137,7 @@ proc semProcAux(c: PContext, n: PNode, kind: TSymKind, incl(s.flags, sfWasForwarded) elif sfBorrow in s.flags: semBorrow(c, n, s) sideEffectsCheck(c, s) + closeScope(c) # close scope for parameters # c.currentScope = oldScope popOwner(c) diff --git a/tests/objects/t19342_2.nim b/tests/objects/t19342_2.nim new file mode 100644 index 000000000..6f6d0f2b3 --- /dev/null +++ b/tests/objects/t19342_2.nim @@ -0,0 +1,18 @@ +discard """ + targets: "c cpp" +""" + +{.compile: "m19342.c".} + +# bug #19342 +type + Node* {.byRef.} = object + data: array[25, cint] + +proc myproc(name: cint): Node {.importc: "hello", cdecl.} + +proc parse = + let node = myproc(10) + doAssert node.data[0] == 999 + +parse() \ No newline at end of file From 520881af9add94b26ef7d0630352ad425bb84490 Mon Sep 17 00:00:00 2001 From: flywind Date: Sat, 29 Jan 2022 10:08:38 +0800 Subject: [PATCH 2790/3768] update outdated link (#19465) Ref https://github.com/nim-lang/Nim/issues/19463 --- doc/intern.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/intern.rst b/doc/intern.rst index e8fb4ac86..a8846ae02 100644 --- a/doc/intern.rst +++ b/doc/intern.rst @@ -166,7 +166,7 @@ current commit: You can also bisect using custom options to build the compiler, for example if you don't need a debug version of the compiler (which runs slower), you can replace -`./koch temp`:cmd: by explicit compilation command, see `Rebuilding the compiler`_. +`./koch temp`:cmd: by explicit compilation command, see `Bootstrapping the compiler`_. Runtimes From cb894c7094fb49014f85815a9dafc38b5dda743e Mon Sep 17 00:00:00 2001 From: Dominik Picheta Date: Sat, 29 Jan 2022 13:03:01 +0000 Subject: [PATCH 2791/3768] Merge pull request from GHSA-ggrq-h43f-3w7m This fixes a CVE (currently https://github.com/nim-lang/Nim/security/advisories/GHSA-ggrq-h43f-3w7m) --- compiler/docgen.nim | 2 +- lib/packages/docutils/rst.nim | 22 ++++++++++++++--- tests/stdlib/trstgen.nim | 45 +++++++++++++++++++++++++++++------ 3 files changed, 58 insertions(+), 11 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 90e0f54b3..235922349 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -282,7 +282,7 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef, result.cache = cache result.outDir = conf.outDir.string result.isPureRst = isPureRst - var options= {roSupportRawDirective, roSupportMarkdown, roPreferMarkdown} + var options= {roSupportRawDirective, roSupportMarkdown, roPreferMarkdown, roSandboxDisabled} if not isPureRst: options.incl roNimFile result.sharedState = newRstSharedState( options, filename.string, diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 791953767..93c2231dc 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -235,6 +235,12 @@ type ## to Markdown) -- implies `roSupportMarkdown` roNimFile ## set for Nim files where default interpreted ## text role should be :nim: + roSandboxDisabled ## this option enables certain options + ## (e.g. raw, include) + ## which are disabled by default as they can + ## enable users to read arbitrary data and + ## perform XSS if the parser is used in a web + ## app. RstParseOptions* = set[RstParseOption] @@ -260,7 +266,8 @@ type mwBrokenLink = "broken link '$1'", mwUnsupportedLanguage = "language '$1' not supported", mwUnsupportedField = "field '$1' not supported", - mwRstStyle = "RST style: $1" + mwRstStyle = "RST style: $1", + meSandboxedDirective = "disabled directive: '$1'", MsgHandler* = proc (filename: string, line, col: int, msgKind: MsgKind, arg: string) {.closure, gcsafe.} ## what to do in case of an error @@ -315,6 +322,7 @@ const ":geek:": "icon_e_geek", ":ugeek:": "icon_e_ugeek" } + SandboxDirAllowlist = ["image", "code", "code-block"] type TokType = enum @@ -2987,10 +2995,14 @@ proc dirCodeBlock(p: var RstParser, nimExtension = false): PRstNode = ## ## As an extension this proc will process the ``file`` extension field and if ## present will replace the code block with the contents of the referenced - ## file. + ## file. This behaviour is disabled in sandboxed mode and can be re-enabled + ## with the `roSandboxDisabled` flag. result = parseDirective(p, rnCodeBlock, {hasArg, hasOptions}, parseLiteralBlock) var filename = strip(getFieldValue(result, "file")) if filename != "": + if roSandboxDisabled notin p.s.options: + let tok = p.tok[p.idx-2] + rstMessage(p, meSandboxedDirective, "file", tok.line, tok.col) var path = p.findRelativeFile(filename) if path == "": rstMessage(p, meCannotOpenFile, filename) var n = newRstNode(rnLiteralBlock) @@ -3086,6 +3098,11 @@ proc dirRaw(p: var RstParser): PRstNode = proc selectDir(p: var RstParser, d: string): PRstNode = result = nil + let tok = p.tok[p.idx-2] # report on directive in ".. directive::" + if roSandboxDisabled notin p.s.options: + if d notin SandboxDirAllowlist: + rstMessage(p, meSandboxedDirective, d, tok.line, tok.col) + case d of "admonition", "attention", "caution": result = dirAdmonition(p, d) of "code": result = dirCodeBlock(p) @@ -3112,7 +3129,6 @@ proc selectDir(p: var RstParser, d: string): PRstNode = of "title": result = dirTitle(p) of "warning": result = dirAdmonition(p, d) else: - let tok = p.tok[p.idx-2] # report on directive in ".. directive::" rstMessage(p, meInvalidDirective, d, tok.line, tok.col) proc prefix(ftnType: FootnoteType): string = diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim index 2be65b35b..995c0a151 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -47,6 +47,10 @@ proc optionListLabel(opt: string): string = opt & "
      " +const + NoSandboxOpts = {roPreferMarkdown, roSupportMarkdown, roNimFile, roSandboxDisabled} + + suite "YAML syntax highlighting": test "Basics": let input = """.. code-block:: yaml @@ -631,7 +635,9 @@ let x = 1 let p3 = """

      Par3 """ & id"value3" & ".

      " let p4 = """

      Par4 value4.

      """ let expected = p1 & p2 & "\n" & p3 & "\n" & p4 - check(input.toHtml == expected) + check( + input.toHtml(NoSandboxOpts) == expected + ) test "role directive": let input = dedent""" @@ -642,7 +648,10 @@ let x = 1 :language: brainhelp """ var warnings = new seq[string] - let output = input.toHtml(warnings=warnings) + let output = input.toHtml( + NoSandboxOpts, + warnings=warnings + ) check(warnings[].len == 1 and "language 'brainhelp' not supported" in warnings[0]) test "RST comments": @@ -1187,7 +1196,9 @@ Test1 .. tip:: endOf tip .. warning:: endOf warning """ - let output0 = input0.toHtml + let output0 = input0.toHtml( + NoSandboxOpts + ) for a in ["admonition", "attention", "caution", "danger", "error", "hint", "important", "note", "tip", "warning" ]: doAssert "endOf " & a & "" in output0 @@ -1198,7 +1209,9 @@ Test1 Test paragraph. """ - let output1 = input1.toHtml + let output1 = input1.toHtml( + NoSandboxOpts + ) doAssert "endOfError" in output1 doAssert "

      Test paragraph.

      " in output1 doAssert "class=\"admonition admonition-error\"" in output1 @@ -1210,7 +1223,9 @@ Test1 Test paragraph. """ - let output2 = input2.toHtml + let output2 = input2.toHtml( + NoSandboxOpts + ) doAssert "endOfError Test2p." in output2 doAssert "

      Test paragraph.

      " in output2 doAssert "class=\"admonition admonition-error\"" in output2 @@ -1218,7 +1233,9 @@ Test1 let input3 = dedent """ .. note:: endOfNote """ - let output3 = input3.toHtml + let output3 = input3.toHtml( + NoSandboxOpts + ) doAssert "endOfNote" in output3 doAssert "class=\"admonition admonition-info\"" in output3 @@ -1303,7 +1320,9 @@ Test1 That was a transition. """ - let output1 = input1.toHtml + let output1 = input1.toHtml( + NoSandboxOpts + ) doAssert "

      Nim))""") check("(([Nim](javascript://nim-lang.org/)))".toHtml == """((Nim))""") + +suite "local file inclusion": + test "cannot include files in sandboxed mode": + var error = new string + discard ".. include:: ./readme.md".toHtml(error=error) + check(error[] == "input(1, 11) Error: disabled directive: 'include'") + + test "code-block file directive is disabled": + var error = new string + discard ".. code-block:: nim\n :file: ./readme.md".toHtml(error=error) + check(error[] == "input(2, 20) Error: disabled directive: 'file'") + From 33cd883e27f7ba7564cbeb59d2d38fe02bb507bf Mon Sep 17 00:00:00 2001 From: flywind Date: Sat, 29 Jan 2022 21:06:54 +0800 Subject: [PATCH 2792/3768] [add testcase] NRVO does not occur with init procedures (#19462) * [add testcase] NRVO does not occur with init procedures close #19094 * Update tests/ccgbugs2/tcodegen.nim --- tests/ccgbugs2/tcodegen.nim | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 tests/ccgbugs2/tcodegen.nim diff --git a/tests/ccgbugs2/tcodegen.nim b/tests/ccgbugs2/tcodegen.nim new file mode 100644 index 000000000..84cd76e2f --- /dev/null +++ b/tests/ccgbugs2/tcodegen.nim @@ -0,0 +1,28 @@ +discard """ + targets: "c cpp" +""" + +# bug #19094 +type + X = object + filler: array[2048, int] + innerAddress: uint + +proc initX(): X = + result.innerAddress = cast[uint](result.addr) + +proc initXInPlace(x: var X) = + x.innerAddress = cast[uint](x.addr) + +block: # NRVO1 + var x = initX() + let innerAddress = x.innerAddress + let outerAddress = cast[uint](x.addr) + doAssert(innerAddress == outerAddress) # [OK] + +block: # NRVO2 + var x: X + initXInPlace(x) + let innerAddress = x.innerAddress + let outerAddress = cast[uint](x.addr) + doAssert(innerAddress == outerAddress) # [OK] From 56a901f9e197dd03f8c70ab4ef1042dc33548af0 Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 31 Jan 2022 20:49:20 +0800 Subject: [PATCH 2793/3768] fix broken CI (#19472) * fix broken CI * fix * fix tests --- compiler/docgen.nim | 1 + compiler/lineinfos.nim | 2 ++ tests/stdlib/trst.nim | 12 ++++++------ 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 235922349..3e61032d8 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -237,6 +237,7 @@ template declareClosures = of meInvalidDirective: k = errRstInvalidDirectiveX of meInvalidField: k = errRstInvalidField of meFootnoteMismatch: k = errRstFootnoteMismatch + of meSandboxedDirective: k = errRstSandboxedDirective of mwRedefinitionOfLabel: k = warnRstRedefinitionOfLabel of mwUnknownSubstitution: k = warnRstUnknownSubstitutionX of mwAmbiguousLink: k = warnRstAmbiguousLink diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index e13387be6..575be6196 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -39,6 +39,7 @@ type errRstInvalidDirectiveX, errRstInvalidField, errRstFootnoteMismatch, + errRstSandboxedDirective, errProveInit, # deadcode errGenerated, errUser, @@ -110,6 +111,7 @@ const errRstInvalidDirectiveX: "invalid directive: '$1'", errRstInvalidField: "invalid field: $1", errRstFootnoteMismatch: "number of footnotes and their references don't match: $1", + errRstSandboxedDirective: "disabled directive: '$1'", errProveInit: "Cannot prove that '$1' is initialized.", # deadcode errGenerated: "$1", errUser: "$1", diff --git a/tests/stdlib/trst.nim b/tests/stdlib/trst.nim index 0a9eb8038..771e02477 100644 --- a/tests/stdlib/trst.nim +++ b/tests/stdlib/trst.nim @@ -23,7 +23,7 @@ import std/private/miscdollars import os proc toAst(input: string, - rstOptions: RstParseOptions = {roPreferMarkdown, roSupportMarkdown, roNimFile}, + rstOptions: RstParseOptions = {roPreferMarkdown, roSupportMarkdown, roNimFile, roSandboxDisabled}, error: ref string = nil, warnings: ref seq[string] = nil): string = ## If `error` is nil then no errors should be generated. @@ -866,7 +866,7 @@ suite "RST include directive": test "Include whole": "other.rst".writeFile("**test1**") let input = ".. include:: other.rst" - doAssert "test1" == rstTohtml(input, {}, defaultConfig()) + doAssert "test1" == rstTohtml(input, {roSandboxDisabled}, defaultConfig()) removeFile("other.rst") test "Include starting from": @@ -880,7 +880,7 @@ OtherStart .. include:: other.rst :start-after: OtherStart """ - check "Visible" == rstTohtml(input, {}, defaultConfig()) + check "Visible" == rstTohtml(input, {roSandboxDisabled}, defaultConfig()) removeFile("other.rst") test "Include everything before": @@ -894,7 +894,7 @@ And this should **NOT** be visible in `docs.html` .. include:: other.rst :end-before: OtherEnd """ - doAssert "Visible" == rstTohtml(input, {}, defaultConfig()) + doAssert "Visible" == rstTohtml(input, {roSandboxDisabled}, defaultConfig()) removeFile("other.rst") @@ -912,7 +912,7 @@ And this should **NOT** be visible in `docs.html` :start-after: OtherStart :end-before: OtherEnd """ - check "Visible" == rstTohtml(input, {}, defaultConfig()) + check "Visible" == rstTohtml(input, {roSandboxDisabled}, defaultConfig()) removeFile("other.rst") @@ -932,7 +932,7 @@ And this should **NOT** be visible in `docs.html` :start-after: OtherStart :end-before: OtherEnd """ - doAssert "Visible" == rstTohtml(input, {}, defaultConfig()) + doAssert "Visible" == rstTohtml(input, {roSandboxDisabled}, defaultConfig()) removeFile("other.rst") suite "RST escaping": From 22ae0bef63189dff28676b0dd5f6089864151120 Mon Sep 17 00:00:00 2001 From: tandy1000 Date: Tue, 1 Feb 2022 11:12:55 +0000 Subject: [PATCH 2794/3768] Update jsfetch with latest API and fix missing bindings (#19473) * Update with latest API and fix missing bindings remove deprecated `Body` remove implicit `cstring` convs add `Headers` to `FetchOptions` add `Request` init proc which takes `FetchOptions` * Update lib/std/jsfetch.nim Co-authored-by: Juan Carlos * Update lib/std/jsfetch.nim Co-authored-by: Juan Carlos * remove experimental flag Co-authored-by: Juan Carlos --- lib/std/jsfetch.nim | 46 ++++++++++++++++++++++++--------------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/lib/std/jsfetch.nim b/lib/std/jsfetch.nim index a25fe7ca8..808d699b0 100644 --- a/lib/std/jsfetch.nim +++ b/lib/std/jsfetch.nim @@ -1,5 +1,4 @@ ## - Fetch for the JavaScript target: https://developer.mozilla.org/docs/Web/API/Fetch_API -## .. Note:: jsfetch is Experimental. when not defined(js): {.fatal: "Module jsfetch is designed to be used with the JavaScript backend.".} @@ -12,6 +11,7 @@ type keepalive*: bool metod* {.importjs: "method".}: cstring body*, integrity*, referrer*, mode*, credentials*, cache*, redirect*, referrerPolicy*: cstring + headers*: Headers FetchModes* = enum ## Mode options. fmCors = "cors" @@ -42,16 +42,13 @@ type frpOriginWhenCrossOrigin = "origin-when-cross-origin" frpUnsafeUrl = "unsafe-url" - Body* = ref object of JsRoot ## https://developer.mozilla.org/en-US/docs/Web/API/Body - bodyUsed*: bool - Response* = ref object of JsRoot ## https://developer.mozilla.org/en-US/docs/Web/API/Response bodyUsed*, ok*, redirected*: bool typ* {.importjs: "type".}: cstring url*, statusText*: cstring status*: cint headers*: Headers - body*: Body + body*: cstring Request* = ref object of JsRoot ## https://developer.mozilla.org/en-US/docs/Web/API/Request bodyUsed*, ok*, redirected*: bool @@ -59,8 +56,7 @@ type url*, statusText*: cstring status*: cint headers*: Headers - body*: Body - + body*: cstring func newResponse*(body: cstring | FormData): Response {.importjs: "(new Response(#))".} ## Constructor for `Response`. This does *not* call `fetch()`. Same as `new Response()`. @@ -68,30 +64,34 @@ func newResponse*(body: cstring | FormData): Response {.importjs: "(new Response func newRequest*(url: cstring): Request {.importjs: "(new Request(#))".} ## Constructor for `Request`. This does *not* call `fetch()`. Same as `new Request()`. +func newRequest*(url: cstring; fetchOptions: FetchOptions): Request {.importjs: "(new Request(#, #))".} + ## Constructor for `Request` with `fetchOptions`. Same as `fetch(url, fetchOptions)`. + func clone*(self: Response | Request): Response {.importjs: "#.$1()".} ## https://developer.mozilla.org/en-US/docs/Web/API/Response/clone proc text*(self: Response): Future[cstring] {.importjs: "#.$1()".} - ## https://developer.mozilla.org/en-US/docs/Web/API/Body/text + ## https://developer.mozilla.org/en-US/docs/Web/API/Response/text proc json*(self: Response): Future[JsObject] {.importjs: "#.$1()".} - ## https://developer.mozilla.org/en-US/docs/Web/API/Body/json + ## https://developer.mozilla.org/en-US/docs/Web/API/Response/json -proc formData*(self: Body): Future[FormData] {.importjs: "#.$1()".} - ## https://developer.mozilla.org/en-US/docs/Web/API/Body/formData +proc formData*(self: Response): Future[FormData] {.importjs: "#.$1()".} + ## https://developer.mozilla.org/en-US/docs/Web/API/Response/formData proc unsafeNewFetchOptions*(metod, body, mode, credentials, cache, referrerPolicy: cstring; - keepalive: bool; redirect = "follow".cstring; referrer = "client".cstring; integrity = "".cstring): FetchOptions {.importjs: - "{method: #, body: #, mode: #, credentials: #, cache: #, referrerPolicy: #, keepalive: #, redirect: #, referrer: #, integrity: #}".} + keepalive: bool; redirect = "follow".cstring; referrer = "client".cstring; integrity = "".cstring; headers: Headers = newHeaders()): FetchOptions {.importjs: + "{method: #, body: #, mode: #, credentials: #, cache: #, referrerPolicy: #, keepalive: #, redirect: #, referrer: #, integrity: #, headers: #}".} ## .. warning:: Unsafe `newfetchOptions`. func newfetchOptions*(metod: HttpMethod; body: cstring; mode: FetchModes; credentials: FetchCredentials; cache: FetchCaches; referrerPolicy: FetchReferrerPolicies; - keepalive: bool; redirect = frFollow; referrer = "client".cstring; integrity = "".cstring): FetchOptions = + keepalive: bool; redirect = frFollow; referrer = "client".cstring; integrity = "".cstring, + headers: Headers = newHeaders()): FetchOptions = ## Constructor for `FetchOptions`. result = FetchOptions( - body: body, mode: $mode, credentials: $credentials, cache: $cache, referrerPolicy: $referrerPolicy, - keepalive: keepalive, redirect: $redirect, referrer: referrer, integrity: integrity, + body: body, mode: cstring($mode), credentials: cstring($credentials), cache: cstring($cache), referrerPolicy: cstring($referrerPolicy), + keepalive: keepalive, redirect: cstring($redirect), referrer: referrer, integrity: integrity, metod: (case metod of HttpHead: "HEAD".cstring of HttpGet: "GET".cstring @@ -109,9 +109,9 @@ proc fetch*(url: cstring | Request): Future[Response] {.importjs: "$1(#)".} proc fetch*(url: cstring | Request; options: FetchOptions): Future[Response] {.importjs: "$1(#, #)".} ## `fetch()` API that takes a `FetchOptions`, returns a `Future[Response]`. -func toCstring*(self: Request | Response | Body | FetchOptions): cstring {.importjs: "JSON.stringify(#)".} +func toCstring*(self: Request | Response | FetchOptions): cstring {.importjs: "JSON.stringify(#)".} -func `$`*(self: Request | Response | Body | FetchOptions): string = $toCstring(self) +func `$`*(self: Request | Response | FetchOptions): string = $toCstring(self) runnableExamples("-r:off"): @@ -131,7 +131,8 @@ runnableExamples("-r:off"): keepalive = false, redirect = "follow".cstring, referrer = "client".cstring, - integrity = "".cstring + integrity = "".cstring, + headers = newHeaders() ) assert options0.keepalive == false assert options0.metod == "POST".cstring @@ -143,6 +144,7 @@ runnableExamples("-r:off"): assert options0.redirect == "follow".cstring assert options0.referrer == "client".cstring assert options0.integrity == "".cstring + assert options0.headers.len == 0 block: let options1: FetchOptions = newFetchOptions( @@ -155,7 +157,8 @@ runnableExamples("-r:off"): keepalive = false, redirect = frFollow, referrer = "client".cstring, - integrity = "".cstring + integrity = "".cstring, + headers = newHeaders() ) assert options1.keepalive == false assert options1.metod == $HttpPost @@ -167,6 +170,7 @@ runnableExamples("-r:off"): assert options1.redirect == $frFollow assert options1.referrer == "client".cstring assert options1.integrity == "".cstring + assert options1.headers.len == 0 block: let response: Response = newResponse(body = "-. .. --".cstring) @@ -182,7 +186,7 @@ runnableExamples("-r:off"): assert response.ok assert response.status == 200.cint assert response.headers is Headers - assert response.body is Body + assert response.body is cstring discard example() From 1830a3b505c33be5fc3b9e311c5e7720d9b37b32 Mon Sep 17 00:00:00 2001 From: Jason Beetham Date: Wed, 2 Feb 2022 01:38:21 -0700 Subject: [PATCH 2795/3768] No longer segfault when using a typeclass with a self referencing type (#19467) --- compiler/sigmatch.nim | 3 ++- tests/generics/tgenerics_issues.nim | 12 ++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index e1195d04a..14f4c5e19 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1688,7 +1688,8 @@ proc typeRel(c: var TCandidate, f, aOrig: PType, var aa = a while aa.kind in {tyTypeDesc, tyGenericParam} and aa.len > 0: aa = lastSon(aa) - if aa.kind == tyGenericParam: + if aa.kind in {tyGenericParam} + tyTypeClasses: + # If the constraint is a genericParam or typeClass this isGeneric return isGeneric result = typeRel(c, f.base, aa, flags) if result > isGeneric: result = isGeneric diff --git a/tests/generics/tgenerics_issues.nim b/tests/generics/tgenerics_issues.nim index 092926c01..db7a16569 100644 --- a/tests/generics/tgenerics_issues.nim +++ b/tests/generics/tgenerics_issues.nim @@ -860,3 +860,15 @@ type var a: Tile3[int] +block: # Ensure no segfault from constraint + type + Regex[A: SomeOrdinal] = ref object + val: Regex[A] + MyConstraint = (seq or enum or set) + MyOtherType[A: MyConstraint] = ref object + val: MyOtherType[A] + + var + a = Regex[int]() + b = Regex[bool]() + c = MyOtherType[seq[int]]() From 486cb09ec2caef60011b3d182bfd188dadafdf62 Mon Sep 17 00:00:00 2001 From: Regis Caillaud <35006197+Clonkk@users.noreply.github.com> Date: Wed, 2 Feb 2022 09:44:51 +0100 Subject: [PATCH 2796/3768] Clonkk fix2 11923 (#19451) * fix nnkBracketExpr not compiling for getImpl on customPragmaNode * fix test import * fix alias not working with hasCustomPragmas --- lib/core/macros.nim | 27 ++++++++++++++++++++++----- tests/pragmas/tcustom_pragma.nim | 6 ++++++ 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 28106493f..c0e6e5154 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -1493,6 +1493,22 @@ macro expandMacros*(body: typed): untyped = echo body.toStrLit result = body +proc extractTypeImpl(n: NimNode): NimNode = + ## attempts to extract the type definition of the given symbol + case n.kind + of nnkSym: # can extract an impl + result = n.getImpl.extractTypeImpl() + of nnkObjectTy, nnkRefTy, nnkPtrTy: result = n + of nnkBracketExpr: + if n.typeKind == ntyTypeDesc: + result = n[1].extractTypeImpl() + else: + doAssert n.typeKind == ntyGenericInst + result = n[0].getImpl() + of nnkTypeDef: + result = n[2] + else: error("Invalid node to retrieve type implementation of: " & $n.kind) + proc customPragmaNode(n: NimNode): NimNode = expectKind(n, {nnkSym, nnkDotExpr, nnkBracketExpr, nnkTypeOfExpr, nnkCheckedFieldExpr}) let @@ -1501,7 +1517,10 @@ proc customPragmaNode(n: NimNode): NimNode = if typ.kind == nnkBracketExpr and typ.len > 1 and typ[1].kind == nnkProcTy: return typ[1][1] elif typ.typeKind == ntyTypeDesc: - let impl = typ[1].getImpl() + let impl = getImpl( + if kind(typ[1]) == nnkBracketExpr: typ[1][0] + else: typ[1] + ) if impl[0].kind == nnkPragmaExpr: return impl[0][1] else: @@ -1524,14 +1543,12 @@ proc customPragmaNode(n: NimNode): NimNode = let name = $(if n.kind == nnkCheckedFieldExpr: n[0][1] else: n[1]) let typInst = getTypeInst(if n.kind == nnkCheckedFieldExpr or n[0].kind == nnkHiddenDeref: n[0][0] else: n[0]) var typDef = getImpl( - if typInst.kind == nnkVarTy or - typInst.kind == nnkBracketExpr: - typInst[0] + if typInst.kind in {nnkVarTy, nnkBracketExpr}: typInst[0] else: typInst ) while typDef != nil: typDef.expectKind(nnkTypeDef) - let typ = typDef[2] + let typ = typDef[2].extractTypeImpl() typ.expectKind({nnkRefTy, nnkPtrTy, nnkObjectTy}) let isRef = typ.kind in {nnkRefTy, nnkPtrTy} if isRef and typ[0].kind in {nnkSym, nnkBracketExpr}: # defines ref type for another object(e.g. X = ref X) diff --git a/tests/pragmas/tcustom_pragma.nim b/tests/pragmas/tcustom_pragma.nim index 1c3709b26..db2536188 100644 --- a/tests/pragmas/tcustom_pragma.nim +++ b/tests/pragmas/tcustom_pragma.nim @@ -20,16 +20,22 @@ block: MyGenericObj[T] = object myField1, myField2 {.myAttr: "hi".}: int + MyOtherObj = MyObj + var o: MyObj static: doAssert o.myField2.hasCustomPragma(myAttr) doAssert(not o.myField1.hasCustomPragma(myAttr)) + doAssert(not o.myField1.hasCustomPragma(MyObj)) + doAssert(not o.myField1.hasCustomPragma(MyOtherObj)) var ogen: MyGenericObj[int] static: doAssert ogen.myField2.hasCustomPragma(myAttr) doAssert(not ogen.myField1.hasCustomPragma(myAttr)) + doAssert(not ogen.myField1.hasCustomPragma(MyGenericObj)) + doAssert(not ogen.myField1.hasCustomPragma(MyGenericObj)) import custom_pragma From 891329cd4b3e7e3b0d995cf342c1018239ebcf81 Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 3 Feb 2022 00:10:11 +0800 Subject: [PATCH 2797/3768] move io out of system (#19442) * move io out of system * fix tests * fix tests * next step * rename to syncio * rename * fix nimscript * comma * fix * fix parts of errors * good for now * fix test --- changelog.md | 2 ++ compiler/pathutils.nim | 3 ++ compiler/vmops.nim | 2 +- doc/tut1.rst | 8 ++--- lib/posix/posix.nim | 3 ++ lib/pure/htmlparser.nim | 3 ++ lib/pure/json.nim | 3 ++ lib/pure/logging.nim | 11 ++++--- lib/pure/memfiles.nim | 4 +-- lib/pure/os.nim | 3 ++ lib/pure/parsecfg.nim | 4 +++ lib/pure/parsecsv.nim | 3 ++ lib/pure/pegs.nim | 2 ++ lib/pure/ropes.nim | 3 ++ lib/pure/streams.nim | 9 ++++-- lib/pure/terminal.nim | 3 ++ lib/pure/xmlparser.nim | 3 ++ lib/{system/io.nim => std/syncio.nim} | 45 +++++++++++++-------------- lib/system.nim | 9 ++++-- lib/system_overview.rst | 1 - lib/windows/winlean.nim | 3 ++ tests/cpp/t6986.nim | 3 ++ tests/test_nimscript.nims | 3 ++ tools/kochdocs.nim | 1 - 24 files changed, 92 insertions(+), 42 deletions(-) rename lib/{system/io.nim => std/syncio.nim} (96%) diff --git a/changelog.md b/changelog.md index bbbaf6e97..412e046eb 100644 --- a/changelog.md +++ b/changelog.md @@ -20,6 +20,8 @@ - `addr` is now available for all addressable locations, `unsafeAddr` is deprecated and becomes an alias for `addr`. +- io is about to move out of system; use `-d:nimPreviewSlimSystem` and import `std/syncio`. + ## Standard library additions and changes - `macros.parseExpr` and `macros.parseStmt` now accept an optional diff --git a/compiler/pathutils.nim b/compiler/pathutils.nim index d8f3613b0..8138a245a 100644 --- a/compiler/pathutils.nim +++ b/compiler/pathutils.nim @@ -12,6 +12,9 @@ import os, pathnorm +when defined(nimSlimSystem): + import std/syncio + type AbsoluteFile* = distinct string AbsoluteDir* = distinct string diff --git a/compiler/vmops.nim b/compiler/vmops.nim index 018e7b9c6..6b06cc68d 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -47,7 +47,7 @@ template systemop(op) {.dirty.} = registerCallback(c, "stdlib.system." & astToStr(op), `op Wrapper`) template ioop(op) {.dirty.} = - registerCallback(c, "stdlib.io." & astToStr(op), `op Wrapper`) + registerCallback(c, "stdlib.syncio." & astToStr(op), `op Wrapper`) template macrosop(op) {.dirty.} = registerCallback(c, "stdlib.macros." & astToStr(op), `op Wrapper`) diff --git a/doc/tut1.rst b/doc/tut1.rst index 3107aef45..405df57b1 100644 --- a/doc/tut1.rst +++ b/doc/tut1.rst @@ -84,8 +84,8 @@ done with spaces only, tabulators are not allowed. String literals are enclosed in double-quotes. The `var` statement declares a new variable named `name` of type `string` with the value that is -returned by the `readLine `_ procedure. Since the -compiler knows that `readLine `_ returns a string, +returned by the `readLine `_ procedure. Since the +compiler knows that `readLine `_ returns a string, you can leave out the type in the declaration (this is called `local type inference`:idx:). So this will work too: @@ -97,7 +97,7 @@ Note that this is basically the only form of type inference that exists in Nim: it is a good compromise between brevity and readability. The "hello world" program contains several identifiers that are already known -to the compiler: `echo`, `readLine `_, etc. +to the compiler: `echo`, `readLine `_, etc. These built-ins are declared in the system_ module which is implicitly imported by any other module. @@ -594,7 +594,7 @@ Procedures ========== To define new commands like `echo `_ -and `readLine `_ in the examples, the concept of a +and `readLine `_ in the examples, the concept of a *procedure* is needed. You might be used to them being called *methods* or *functions* in other languages, but Nim `differentiates these concepts `_. In diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index 912e395a7..c10fc6ac9 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -37,6 +37,9 @@ when defined(nimHasStyleChecks): {.push styleChecks: off.} +when defined(nimSlimSystem): + import std/syncio + # TODO these constants don't seem to be fetched from a header file for unknown # platforms - where do they come from and why are they here? when false: diff --git a/lib/pure/htmlparser.nim b/lib/pure/htmlparser.nim index 21dff69ff..6b1300f11 100644 --- a/lib/pure/htmlparser.nim +++ b/lib/pure/htmlparser.nim @@ -51,6 +51,9 @@ import strutils, streams, parsexml, xmltree, unicode, strtabs +when defined(nimPreviewSlimSystem): + import std/syncio + type HtmlTag* = enum ## list of all supported HTML tags; order will always be ## alphabetically diff --git a/lib/pure/json.nim b/lib/pure/json.nim index 922cd4e2f..bdc9fe5ab 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -164,6 +164,9 @@ import hashes, tables, strutils, lexbase, streams, macros, parsejson import options # xxx remove this dependency using same approach as https://github.com/nim-lang/Nim/pull/14563 import std/private/since +when defined(nimPreviewSlimSystem): + import std/syncio + export tables.`$` diff --git a/lib/pure/logging.nim b/lib/pure/logging.nim index b2ace79ab..6751a372a 100644 --- a/lib/pure/logging.nim +++ b/lib/pure/logging.nim @@ -48,7 +48,7 @@ ## .. warning:: ## For loggers that log to a console or to files, only error and fatal ## messages will cause their output buffers to be flushed immediately. -## Use the `flushFile proc `_ to flush the buffer +## Use the `flushFile proc `_ to flush the buffer ## manually if needed. ## ## Handlers @@ -146,6 +146,9 @@ import strutils, times when not defined(js): import os +when defined(nimPreviewSlimSystem): + import std/syncio + type Level* = enum ## \ ## Enumeration of logging levels. @@ -346,7 +349,7 @@ method log*(logger: ConsoleLogger, level: Level, args: varargs[string, `$`]) = ## ## **Note:** Only error and fatal messages will cause the output buffer ## to be flushed immediately. Use the `flushFile proc - ## `_ to flush the buffer manually if needed. + ## `_ to flush the buffer manually if needed. ## ## See also: ## * `log method<#log.e,FileLogger,Level,varargs[string,]>`_ @@ -422,7 +425,7 @@ when not defined(js): ## **Notes:** ## * Only error and fatal messages will cause the output buffer ## to be flushed immediately. Use the `flushFile proc - ## `_ to flush the buffer manually if needed. + ## `_ to flush the buffer manually if needed. ## * This method is not available for the JavaScript backend. ## ## See also: @@ -600,7 +603,7 @@ when not defined(js): ## **Notes:** ## * Only error and fatal messages will cause the output buffer ## to be flushed immediately. Use the `flushFile proc - ## `_ to flush the buffer manually if needed. + ## `_ to flush the buffer manually if needed. ## * This method is not available for the JavaScript backend. ## ## See also: diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim index 407a358fa..f65ca125e 100644 --- a/lib/pure/memfiles.nim +++ b/lib/pure/memfiles.nim @@ -431,7 +431,7 @@ iterator memSlices*(mfile: MemFile, delim = '\l', eat = '\r'): MemSlice {.inline iterator lines*(mfile: MemFile, buf: var string, delim = '\l', eat = '\r'): string {.inline.} = ## Replace contents of passed buffer with each new line, like - ## `readLine(File) `_. + ## `readLine(File) `_. ## `delim`, `eat`, and delimiting logic is exactly as for `memSlices ## <#memSlices.i,MemFile,char,char>`_, but Nim strings are returned. ## @@ -450,7 +450,7 @@ iterator lines*(mfile: MemFile, buf: var string, delim = '\l', iterator lines*(mfile: MemFile, delim = '\l', eat = '\r'): string {.inline.} = ## Return each line in a file as a Nim string, like - ## `lines(File) `_. + ## `lines(File) `_. ## `delim`, `eat`, and delimiting logic is exactly as for `memSlices ## <#memSlices.i,MemFile,char,char>`_, but Nim strings are returned. ## diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 07040d611..76164bc49 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -34,6 +34,9 @@ import std/private/since import strutils, pathnorm +when defined(nimPreviewSlimSystem): + import std/syncio + const weirdTarget = defined(nimscript) or defined(js) since (1, 1): diff --git a/lib/pure/parsecfg.nim b/lib/pure/parsecfg.nim index 0ee19912c..54584a253 100644 --- a/lib/pure/parsecfg.nim +++ b/lib/pure/parsecfg.nim @@ -175,8 +175,12 @@ import strutils, lexbase, streams, tables import std/private/decode_helpers import std/private/since +when defined(nimPreviewSlimSystem): + import std/syncio + include "system/inclrtl" + type CfgEventKind* = enum ## enumeration of all events that may occur when parsing cfgEof, ## end of file reached diff --git a/lib/pure/parsecsv.nim b/lib/pure/parsecsv.nim index 6db794682..a8d1cfaab 100644 --- a/lib/pure/parsecsv.nim +++ b/lib/pure/parsecsv.nim @@ -67,6 +67,9 @@ import lexbase, streams +when defined(nimPreviewSlimSystem): + import std/syncio + type CsvRow* = seq[string] ## A row in a CSV file. CsvParser* = object of BaseLexer ## The parser object. diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim index bac8b1a0e..0eed1c388 100644 --- a/lib/pure/pegs.nim +++ b/lib/pure/pegs.nim @@ -16,6 +16,8 @@ ## include "system/inclrtl" +when defined(nimPreviewSlimSystem): + import std/syncio const useUnicode = true ## change this to deactivate proper UTF-8 support diff --git a/lib/pure/ropes.nim b/lib/pure/ropes.nim index 1300b4479..84b5b47c2 100644 --- a/lib/pure/ropes.nim +++ b/lib/pure/ropes.nim @@ -19,6 +19,9 @@ include system/inclrtl import streams +when defined(nimPreviewSlimSystem): + import std/syncio + {.push debugger: off.} # the user does not want to trace a part # of the standard library! diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim index 7dc81148f..7ad81685f 100644 --- a/lib/pure/streams.nim +++ b/lib/pure/streams.nim @@ -92,10 +92,13 @@ ## See also ## ======== ## * `asyncstreams module `_ -## * `io module `_ for `FileMode enum `_ +## * `io module `_ for `FileMode enum `_ import std/private/since +when defined(nimPreviewSlimSystem): + import std/syncio + proc newEIO(msg: string): owned(ref IOError) = new(result) result.msg = msg @@ -1331,7 +1334,7 @@ proc newFileStream*(f: File): owned FileStream = ## * `newStringStream proc <#newStringStream,string>`_ creates a new stream ## from string. ## * `newFileStream proc <#newFileStream,string,FileMode,int>`_ is the same - ## as using `open proc `_ + ## as using `open proc `_ ## on Examples. ## * `openFileStream proc <#openFileStream,string,FileMode,int>`_ creates a ## file stream from the file name and the mode. @@ -1370,7 +1373,7 @@ proc newFileStream*(filename: string, mode: FileMode = fmRead, ## Creates a new stream from the file named `filename` with the mode `mode`. ## ## If the file cannot be opened, `nil` is returned. See the `io module - ## `_ for a list of available `FileMode enums `_. + ## `_ for a list of available `FileMode enums `_. ## ## **Note:** ## * **This function returns nil in case of failure.** diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index c9aafc037..5755e142a 100644 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -66,6 +66,9 @@ import colors when defined(windows): import winlean +when defined(nimPreviewSlimSystem): + import std/syncio + type PTerminal = ref object trueColorIsSupported: bool diff --git a/lib/pure/xmlparser.nim b/lib/pure/xmlparser.nim index 3d9c288ed..6785fa66e 100644 --- a/lib/pure/xmlparser.nim +++ b/lib/pure/xmlparser.nim @@ -11,6 +11,9 @@ import streams, parsexml, strtabs, xmltree +when defined(nimPreviewSlimSystem): + import std/syncio + type XmlError* = object of ValueError ## Exception that is raised ## for invalid XML. diff --git a/lib/system/io.nim b/lib/std/syncio.nim similarity index 96% rename from lib/system/io.nim rename to lib/std/syncio.nim index 04a43328d..eab96254a 100644 --- a/lib/system/io.nim +++ b/lib/std/syncio.nim @@ -1,18 +1,17 @@ # # # Nim's Runtime Library -# (c) Copyright 2019 Nim contributors +# (c) Copyright 2022 Nim contributors # # See the file "copying.txt", included in this # distribution, for details about the copyright. # -## This is a part of `system.nim`, you should not manually import it. +## This module implements various synchronized I/O operations. - -include inclrtl +include system/inclrtl import std/private/since -import formatfloat +import system/formatfloat # ----------------- IO Part ------------------------------------------------ type @@ -179,7 +178,7 @@ proc checkErr(f: File) = {.push stackTrace: off, profiler: off.} proc readBuffer*(f: File, buffer: pointer, len: Natural): int {. tags: [ReadIOEffect], benign.} = - ## reads `len` bytes into the buffer pointed to by `buffer`. Returns + ## Reads `len` bytes into the buffer pointed to by `buffer`. Returns ## the actual number of bytes that have been read which may be less than ## `len` (if not as many bytes are remaining), but not greater. result = cast[int](c_fread(buffer, 1, cast[csize_t](len), f)) @@ -188,13 +187,13 @@ proc readBuffer*(f: File, buffer: pointer, len: Natural): int {. proc readBytes*(f: File, a: var openArray[int8|uint8], start, len: Natural): int {. tags: [ReadIOEffect], benign.} = - ## reads `len` bytes into the buffer `a` starting at `a[start]`. Returns + ## Reads `len` bytes into the buffer `a` starting at `a[start]`. Returns ## the actual number of bytes that have been read which may be less than ## `len` (if not as many bytes are remaining), but not greater. result = readBuffer(f, addr(a[start]), len) proc readChars*(f: File, a: var openArray[char]): int {.tags: [ReadIOEffect], benign.} = - ## reads up to `a.len` bytes into the buffer `a`. Returns + ## Reads up to `a.len` bytes into the buffer `a`. Returns ## the actual number of bytes that have been read which may be less than ## `a.len` (if not as many bytes are remaining), but not greater. result = readBuffer(f, addr(a[0]), a.len) @@ -202,7 +201,7 @@ proc readChars*(f: File, a: var openArray[char]): int {.tags: [ReadIOEffect], be proc readChars*(f: File, a: var openArray[char], start, len: Natural): int {. tags: [ReadIOEffect], benign, deprecated: "use other `readChars` overload, possibly via: readChars(toOpenArray(buf, start, len-1))".} = - ## reads `len` bytes into the buffer `a` starting at `a[start]`. Returns + ## Reads `len` bytes into the buffer `a` starting at `a[start]`. Returns ## the actual number of bytes that have been read which may be less than ## `len` (if not as many bytes are remaining), but not greater. if (start + len) > len(a): @@ -216,7 +215,7 @@ proc write*(f: File, c: cstring) {.tags: [WriteIOEffect], benign.} = proc writeBuffer*(f: File, buffer: pointer, len: Natural): int {. tags: [WriteIOEffect], benign.} = - ## writes the bytes of buffer pointed to by the parameter `buffer` to the + ## Writes the bytes of buffer pointed to by the parameter `buffer` to the ## file `f`. Returns the number of actual written bytes, which may be less ## than `len` in case of an error. result = cast[int](c_fwrite(buffer, 1, cast[csize_t](len), f)) @@ -224,7 +223,7 @@ proc writeBuffer*(f: File, buffer: pointer, len: Natural): int {. proc writeBytes*(f: File, a: openArray[int8|uint8], start, len: Natural): int {. tags: [WriteIOEffect], benign.} = - ## writes the bytes of `a[start..start+len-1]` to the file `f`. Returns + ## Writes the bytes of `a[start..start+len-1]` to the file `f`. Returns ## the number of actual written bytes, which may be less than `len` in case ## of an error. var x = cast[ptr UncheckedArray[int8]](a) @@ -232,7 +231,7 @@ proc writeBytes*(f: File, a: openArray[int8|uint8], start, len: Natural): int {. proc writeChars*(f: File, a: openArray[char], start, len: Natural): int {. tags: [WriteIOEffect], benign.} = - ## writes the bytes of `a[start..start+len-1]` to the file `f`. Returns + ## Writes the bytes of `a[start..start+len-1]` to the file `f`. Returns ## the number of actual written bytes, which may be less than `len` in case ## of an error. var x = cast[ptr UncheckedArray[int8]](a) @@ -341,7 +340,7 @@ proc flushFile*(f: File) {.tags: [WriteIOEffect].} = discard c_fflush(f) proc getFileHandle*(f: File): FileHandle = - ## returns the file handle of the file `f`. This is only useful for + ## Returns the file handle of the file `f`. This is only useful for ## platform specific programming. ## Note that on Windows this doesn't return the Windows-specific handle, ## but the C library's notion of a handle, whatever that means. @@ -349,7 +348,7 @@ proc getFileHandle*(f: File): FileHandle = c_fileno(f) proc getOsFileHandle*(f: File): FileHandle = - ## returns the OS file handle of the file `f`. This is only useful for + ## Returns the OS file handle of the file `f`. This is only useful for ## platform specific programming. when defined(windows): result = FileHandle getOsfhandle(cint getFileHandle(f)) @@ -380,7 +379,7 @@ when defined(nimdoc) or (defined(posix) and not defined(nimscript)) or defined(w proc readLine*(f: File, line: var string): bool {.tags: [ReadIOEffect], benign.} = - ## reads a line of text from the file `f` into `line`. May throw an IO + ## Reads a line of text from the file `f` into `line`. May throw an IO ## exception. ## A line of text may be delimited by `LF` or `CRLF`. The newline ## character(s) are not part of the returned string. Returns `false` @@ -500,7 +499,7 @@ proc readLine*(f: File, line: var string): bool {.tags: [ReadIOEffect], line.setLen(pos+sp) proc readLine*(f: File): string {.tags: [ReadIOEffect], benign.} = - ## reads a line of text from the file `f`. May throw an IO exception. + ## Reads a line of text from the file `f`. May throw an IO exception. ## A line of text may be delimited by `LF` or `CRLF`. The newline ## character(s) are not part of the returned string. result = newStringOfCap(80) @@ -602,7 +601,7 @@ proc readAll*(file: File): string {.tags: [ReadIOEffect], benign.} = proc writeLine*[Ty](f: File, x: varargs[Ty, `$`]) {.inline, tags: [WriteIOEffect], benign.} = - ## writes the values `x` to `f` and then writes "\\n". + ## Writes the values `x` to `f` and then writes "\\n". ## May throw an IO exception. for i in items(x): write(f, i) @@ -723,7 +722,7 @@ proc open*(f: var File, filename: string, proc reopen*(f: File, filename: string, mode: FileMode = fmRead): bool {. tags: [], benign.} = - ## reopens the file `f` with given `filename` and `mode`. This + ## Reopens the file `f` with given `filename` and `mode`. This ## is often used to redirect the `stdin`, `stdout` or `stderr` ## file variables. ## @@ -765,19 +764,19 @@ proc open*(filename: string, sysFatal(IOError, "cannot open: " & filename) proc setFilePos*(f: File, pos: int64, relativeTo: FileSeekPos = fspSet) {.benign.} = - ## sets the position of the file pointer that is used for read/write + ## Sets the position of the file pointer that is used for read/write ## operations. The file's first byte has the index zero. if c_fseek(f, pos, cint(relativeTo)) != 0: raiseEIO("cannot set file position") proc getFilePos*(f: File): int64 {.benign.} = - ## retrieves the current position of the file pointer that is used to + ## Retrieves the current position of the file pointer that is used to ## read from the file `f`. The file's first byte has the index zero. result = c_ftell(f) if result < 0: raiseEIO("cannot retrieve file position") proc getFileSize*(f: File): int64 {.tags: [ReadIOEffect], benign.} = - ## retrieves the file size (in bytes) of `f`. + ## Retrieves the file size (in bytes) of `f`. let oldPos = getFilePos(f) discard c_fseek(f, 0, 2) # seek the end of the file result = getFilePos(f) @@ -906,7 +905,7 @@ proc writeFile*(filename: string, content: openArray[byte]) {.since: (1, 1).} = raise newException(IOError, "cannot open: " & filename) proc readLines*(filename: string, n: Natural): seq[string] = - ## read `n` lines from the file named `filename`. Raises an IO exception + ## Reads `n` lines from the file named `filename`. Raises an IO exception ## in case of an error. Raises EOF if file does not contain at least `n` lines. ## Available at compile time. A line of text may be delimited by `LF` or `CRLF`. ## The newline character(s) are not part of the returned strings. @@ -948,7 +947,7 @@ iterator lines*(filename: string): string {.tags: [ReadIOEffect].} = close(f) iterator lines*(f: File): string {.tags: [ReadIOEffect].} = - ## Iterate over any line in the file `f`. + ## Iterates over any line in the file `f`. ## ## The trailing newline character(s) are removed from the iterated lines. ## diff --git a/lib/system.nim b/lib/system.nim index 63cdc3ca8..56a6e2887 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2680,7 +2680,7 @@ proc slurp*(filename: string): string {.magic: "Slurp".} ## This is an alias for `staticRead <#staticRead,string>`_. proc staticRead*(filename: string): string {.magic: "Slurp".} - ## Compile-time `readFile `_ proc for easy + ## Compile-time `readFile `_ proc for easy ## `resource`:idx: embedding: ## ## The maximum file size limit that `staticRead` and `slurp` can read is @@ -3150,8 +3150,11 @@ when defined(genode): import system/widestrs export widestrs -import system/io -export io +when not defined(nimPreviewSlimSystem): + {.deprecated: """io is about to move out of system; use `-d:nimPreviewSlimSystem` and + import `std/syncio`.""".} + import std/syncio + export syncio when not defined(createNimHcr) and not defined(nimscript): include nimhcr diff --git a/lib/system_overview.rst b/lib/system_overview.rst index 0e4ffaf17..768fdcd6d 100644 --- a/lib/system_overview.rst +++ b/lib/system_overview.rst @@ -7,7 +7,6 @@ is in separate files: * `iterators `_ * `assertions `_ * `dollars `_ -* `io `_ * `widestrs `_ diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index 6e626e4ba..c02cd9775 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -17,6 +17,9 @@ when defined(nimHasStyleChecks): {.passc: "-DWIN32_LEAN_AND_MEAN".} +when defined(nimPreviewSlimSystem): + from std/syncio import FileHandle + const useWinUnicode* = not defined(useWinAnsi) diff --git a/tests/cpp/t6986.nim b/tests/cpp/t6986.nim index ffd277adb..16e455c3b 100644 --- a/tests/cpp/t6986.nim +++ b/tests/cpp/t6986.nim @@ -5,6 +5,9 @@ discard """ import sequtils, strutils +when defined(nimPreviewSlimSystem): + import std/syncio + let rules = toSeq(lines("input")) .mapIt(it.split(" => ").mapIt(it.replace("/", ""))) diff --git a/tests/test_nimscript.nims b/tests/test_nimscript.nims index 22a566307..b380570ff 100644 --- a/tests/test_nimscript.nims +++ b/tests/test_nimscript.nims @@ -5,6 +5,9 @@ from stdtest/specialpaths import buildDir +when defined(nimSlimSystem): + import std/syncio + import std/[ # Core: bitops, typetraits, lenientops, macros, volatile, diff --git a/tools/kochdocs.nim b/tools/kochdocs.nim index 4fbea8c77..8fd9c84a5 100644 --- a/tools/kochdocs.nim +++ b/tools/kochdocs.nim @@ -187,7 +187,6 @@ proc getDocList(): seq[string] = # don't ignore these even though in lib/system (not include files) const goodSystem = """ -lib/system/io.nim lib/system/nimscript.nim lib/system/assertions.nim lib/system/iterators.nim From 772ed5eff29671b1e99ca17b63c646b341c88123 Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 3 Feb 2022 11:15:46 +0800 Subject: [PATCH 2798/3768] correct typos (#19485) --- compiler/pathutils.nim | 2 +- lib/posix/posix.nim | 2 +- tests/test_nimscript.nims | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/pathutils.nim b/compiler/pathutils.nim index 8138a245a..3a501a417 100644 --- a/compiler/pathutils.nim +++ b/compiler/pathutils.nim @@ -12,7 +12,7 @@ import os, pathnorm -when defined(nimSlimSystem): +when defined(nimPreviewSlimSystem): import std/syncio type diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index c10fc6ac9..9c65d0732 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -37,7 +37,7 @@ when defined(nimHasStyleChecks): {.push styleChecks: off.} -when defined(nimSlimSystem): +when defined(nimPreviewSlimSystem): import std/syncio # TODO these constants don't seem to be fetched from a header file for unknown diff --git a/tests/test_nimscript.nims b/tests/test_nimscript.nims index b380570ff..5fc77f6e4 100644 --- a/tests/test_nimscript.nims +++ b/tests/test_nimscript.nims @@ -5,7 +5,7 @@ from stdtest/specialpaths import buildDir -when defined(nimSlimSystem): +when defined(nimPreviewSlimSystem): import std/syncio import std/[ From 33bfe5b12cf771072d93d7316945f3f5e6c727ff Mon Sep 17 00:00:00 2001 From: flywind Date: Fri, 4 Feb 2022 20:47:03 +0800 Subject: [PATCH 2799/3768] fix parseEnum cannot parse enum with const fields (#19466) fix #19463 --- lib/std/enumutils.nim | 9 +++++++-- tests/stdlib/tstrutils.nim | 11 +++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/lib/std/enumutils.nim b/lib/std/enumutils.nim index 81e602ad5..b7d2b9f89 100644 --- a/lib/std/enumutils.nim +++ b/lib/std/enumutils.nim @@ -14,7 +14,7 @@ from typetraits import OrdinalEnum, HoleyEnum macro genEnumCaseStmt*(typ: typedesc, argSym: typed, default: typed, userMin, userMax: static[int], normalizer: static[proc(s :string): string]): untyped = - # generates a case stmt, which assigns the correct enum field given + # Generates a case stmt, which assigns the correct enum field given # a normalized string comparison to the `argSym` input. # string normalization is done using passed normalizer. # NOTE: for an enum with fields Foo, Bar, ... we cannot generate @@ -49,7 +49,12 @@ macro genEnumCaseStmt*(typ: typedesc, argSym: typed, default: typed, of nnkIntLit: fStr = f[0].strVal fNum = f[1].intVal - else: error("Invalid tuple syntax!", f[1]) + else: + let fAst = f[0].getImpl + if fAst.kind == nnkStrLit: + fStr = fAst.strVal + else: + error("Invalid tuple syntax!", f[1]) else: error("Invalid node for enum type `" & $f.kind & "`!", f) # add field if string not already added if fNum >= userMin and fNum <= userMax: diff --git a/tests/stdlib/tstrutils.nim b/tests/stdlib/tstrutils.nim index 8000b6b07..26fc0c7b0 100644 --- a/tests/stdlib/tstrutils.nim +++ b/tests/stdlib/tstrutils.nim @@ -583,6 +583,17 @@ template main() = let g = parseEnum[Foo]("Bar", A) doAssert g == A + block: # bug #19463 + const CAMPAIGN_TABLE = "wikientries_campaign" + const CHARACTER_TABLE = "wikientries_character" + + type Tables = enum + a = CAMPAIGN_TABLE, + b = CHARACTER_TABLE, + + let myA = CAMPAIGN_TABLE + doAssert $parseEnum[Tables](myA) == "wikientries_campaign" + block: # check enum defined in block type Bar = enum From 7b09fd70ab8154eed3738dfd86f84c5642df3cf2 Mon Sep 17 00:00:00 2001 From: flywind Date: Sat, 5 Feb 2022 06:10:24 +0800 Subject: [PATCH 2800/3768] undefine C symbols in JS backend [backport:1.6] (#19437) fix #19330; fix #19059 --- compiler/cmdlinehelper.nim | 3 ++- compiler/commands.nim | 5 +++-- tests/js/tcsymbol.nim | 6 ++++++ 3 files changed, 11 insertions(+), 3 deletions(-) create mode 100644 tests/js/tcsymbol.nim diff --git a/compiler/cmdlinehelper.nim b/compiler/cmdlinehelper.nim index 007dc2319..02162755c 100644 --- a/compiler/cmdlinehelper.nim +++ b/compiler/cmdlinehelper.nim @@ -64,7 +64,8 @@ proc loadConfigsAndProcessCmdLine*(self: NimProg, cache: IdentCache; conf: Confi if conf.cmd == cmdNimscript: return false # now process command line arguments again, because some options in the # command line can overwrite the config file's settings - extccomp.initVars(conf) + if conf.backend != backendJs: # bug #19059 + extccomp.initVars(conf) self.processCmdLine(passCmd2, "", conf) if conf.cmd == cmdNone: rawMessage(conf, errGenerated, "command missing") diff --git a/compiler/commands.nim b/compiler/commands.nim index a1fa55e74..fb2907bb1 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -899,8 +899,9 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; splitSwitch(conf, arg, key, val, pass, info) os.putEnv(key, val) of "cc": - expectArg(conf, switch, arg, pass, info) - setCC(conf, arg, info) + if conf.backend != backendJs: # bug #19330 + expectArg(conf, switch, arg, pass, info) + setCC(conf, arg, info) of "track": expectArg(conf, switch, arg, pass, info) track(conf, arg, info) diff --git a/tests/js/tcsymbol.nim b/tests/js/tcsymbol.nim new file mode 100644 index 000000000..07e52b9b6 --- /dev/null +++ b/tests/js/tcsymbol.nim @@ -0,0 +1,6 @@ +discard """ + matrix: "--cc:gcc; --cc:tcc" +""" + +doAssert not defined(gcc) +doAssert not defined(tcc) \ No newline at end of file From d0287748fefd9eae71cb3d66caef724473525b28 Mon Sep 17 00:00:00 2001 From: flywind Date: Sun, 6 Feb 2022 15:51:03 +0800 Subject: [PATCH 2801/3768] setjump => setjmp [backport: 1.2] (#19496) --- compiler/commands.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/commands.nim b/compiler/commands.nim index fb2907bb1..6d3f23197 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -238,7 +238,7 @@ const errNoneBoehmRefcExpectedButXFound = "'arc', 'orc', 'markAndSweep', 'boehm', 'go', 'none', 'regions', or 'refc' expected, but '$1' found" errNoneSpeedOrSizeExpectedButXFound = "'none', 'speed' or 'size' expected, but '$1' found" errGuiConsoleOrLibExpectedButXFound = "'gui', 'console' or 'lib' expected, but '$1' found" - errInvalidExceptionSystem = "'goto', 'setjump', 'cpp' or 'quirky' expected, but '$1' found" + errInvalidExceptionSystem = "'goto', 'setjmp', 'cpp' or 'quirky' expected, but '$1' found" template warningOptionNoop(switch: string) = warningDeprecated(conf, info, "'$#' is deprecated, now a noop" % switch) From 6b302bb71b6d9aab708b91f8d68957652ec30480 Mon Sep 17 00:00:00 2001 From: Roj Date: Sun, 6 Feb 2022 18:28:54 -0800 Subject: [PATCH 2802/3768] Improve consistency (#19490) --- readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index 6bc7a2385..7ff056a20 100644 --- a/readme.md +++ b/readme.md @@ -28,7 +28,7 @@ the latest release, check out [Nim's website][nim-site] or [bleeding edge docs]( is the official Telegram channel. Not bridged to IRC. * [Stack Overflow][nim-stackoverflow] - a popular Q/A site for programming related topics that includes posts about Nim. -* [Github Wiki][nim-wiki] - Misc user-contributed content. +* [GitHub Wiki][nim-wiki] - Misc user-contributed content. ## Compiling @@ -73,7 +73,7 @@ the installation instructions on the website to do so: https://nim-lang.org/inst For package maintainers: see [packaging guidelines](https://nim-lang.github.io/Nim/packaging.html). -First, get Nim from github: +First, get Nim from GitHub: ``` git clone https://github.com/nim-lang/Nim.git From 28180e47a99cda88abee3e2d8e130c18100f3a4f Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 7 Feb 2022 14:04:04 +0800 Subject: [PATCH 2803/3768] disable nimlsp (#19499) --- testament/important_packages.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 40a3aef36..456f42cd2 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -103,7 +103,7 @@ pkg "nimfp", "nim c -o:nfp -r src/fp.nim" pkg "nimgame2", "nim c -d:nimLegacyConvEnumEnum nimgame2/nimgame.nim" # XXX Doesn't work with deprecated 'randomize', will create a PR. pkg "nimgen", "nim c -o:nimgenn -r src/nimgen/runcfg.nim" -pkg "nimlsp" +pkg "nimlsp", allowFailure = true pkg "nimly", "nim c -r tests/test_readme_example.nim" pkg "nimongo", "nimble test_ci", allowFailure = true pkg "nimph", "nimble test", "https://github.com/disruptek/nimph", allowFailure = true From 4b0636fba018df407237811f9e687ed4fc249c10 Mon Sep 17 00:00:00 2001 From: Federico Ceratto Date: Mon, 7 Feb 2022 23:05:41 +0000 Subject: [PATCH 2804/3768] Add compilers and hints to default nim.cfg (#18424) --- config/nim.cfg | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/config/nim.cfg b/config/nim.cfg index 59d7f48d2..7fe279f56 100644 --- a/config/nim.cfg +++ b/config/nim.cfg @@ -2,6 +2,7 @@ # (c) 2017 Andreas Rumpf # Feel free to edit the default values as you need. +# See https://nim-lang.org/docs/nimc.html # You may set environment variables with # @putenv "key" "val" @@ -17,11 +18,21 @@ hint[LineTooLong]=off #hint[XDeclaredButNotUsed]=off # Examples of how to setup a cross-compiler: +# Nim can target architectures and OSes different than the local host +# Syntax: ..gcc.exe = "" +# ..gcc.linkerexe = "" -# Cross-compiling for Raspberry Pi. -# (This compiler is available in gcc-arm-linux-gnueabihf package on Ubuntu) +# ARM e.g. Raspberry Pi 2: gcc-arm-linux-gnueabihf package on Debian/Ubuntu arm.linux.gcc.exe = "arm-linux-gnueabihf-gcc" arm.linux.gcc.linkerexe = "arm-linux-gnueabihf-gcc" +# ARM64/aarch64 e.g. Raspberry Pi 3: gcc-aarch64-linux-gnu package on Debian/Ubuntu +arm64.linux.gcc.exe = "aarch64-linux-gnu-gcc" +arm64.linux.gcc.linkerexe = "aarch64-linux-gnu-gcc" +# RISC-V: gcc-riscv64-linux-gnu package on Debian/Ubuntu +riscv32.linux.gcc.exe = "riscv64-linux-gnu-gcc" +riscv32.linux.gcc.linkerexe = "riscv64-linux-gnu-gcc" +riscv64.linux.gcc.exe = "riscv64-linux-gnu-gcc" +riscv64.linux.gcc.linkerexe = "arm-linux-gnueabihf-gcc" # For OpenWRT, you will also need to adjust PATH to point to your toolchain. mips.linux.gcc.exe = "mips-openwrt-linux-gcc" From 801c0f0369779a47da2ef6618abea11feb390973 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Tue, 8 Feb 2022 02:11:53 +0300 Subject: [PATCH 2805/3768] Fix bug 27 of #17340 (#19433) Fixes silent disappearance of Markdown (pseudo-)link when it's detected as unsafe protocol. Now it will be converted to plain text in spirit of [the specification](https://spec.commonmark.org/0.30/#links). For that sake the check for protocol is added to rst.nim also. --- lib/packages/docutils/rst.nim | 66 +++++++++++++++++++++++--------- lib/packages/docutils/rstgen.nim | 17 ++------ tests/stdlib/trst.nim | 27 ++++++++++--- tests/stdlib/trstgen.nim | 11 +++++- 4 files changed, 81 insertions(+), 40 deletions(-) diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index 93c2231dc..d4bfae48f 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -222,7 +222,7 @@ import os, strutils, rstast, dochelpers, std/enumutils, algorithm, lists, sequtils, - std/private/miscdollars, tables + std/private/miscdollars, tables, strscans from highlite import SourceLanguage, getSourceLanguage type @@ -1347,7 +1347,20 @@ proc match(p: RstParser, start: int, expr: string): bool = inc i result = true -proc fixupEmbeddedRef(n, a, b: PRstNode) = +proc safeProtocol*(linkStr: var string): string = + # Returns link's protocol and, if it's not safe, clears `linkStr` + result = "" + if scanf(linkStr, "$w:", result): + # if it has a protocol at all, ensure that it's not 'javascript:' or worse: + if cmpIgnoreCase(result, "http") == 0 or + cmpIgnoreCase(result, "https") == 0 or + cmpIgnoreCase(result, "ftp") == 0: + discard "it's fine" + else: + linkStr = "" + +proc fixupEmbeddedRef(p: var RstParser, n, a, b: PRstNode): bool = + # Returns `true` if the link belongs to an allowed protocol var sep = - 1 for i in countdown(n.len - 2, 0): if n.sons[i].text == "<": @@ -1355,7 +1368,15 @@ proc fixupEmbeddedRef(n, a, b: PRstNode) = break var incr = if sep > 0 and n.sons[sep - 1].text[0] == ' ': 2 else: 1 for i in countup(0, sep - incr): a.add(n.sons[i]) - for i in countup(sep + 1, n.len - 2): b.add(n.sons[i]) + var linkStr = "" + for i in countup(sep + 1, n.len - 2): linkStr.add(n.sons[i].addNodes) + if linkStr != "": + let protocol = safeProtocol(linkStr) + result = linkStr != "" + if not result: + rstMessage(p, mwBrokenLink, protocol, + p.tok[p.idx-3].line, p.tok[p.idx-3].col) + b.add newLeaf(linkStr) proc whichRole(p: RstParser, sym: string): RstNodeKind = result = whichRoleAux(sym) @@ -1407,14 +1428,17 @@ proc parsePostfix(p: var RstParser, n: PRstNode): PRstNode = if p.tok[p.idx-2].symbol == "`" and p.tok[p.idx-3].symbol == ">": var a = newRstNode(rnInner) var b = newRstNode(rnInner) - fixupEmbeddedRef(n, a, b) - if a.len == 0: - newKind = rnStandaloneHyperlink - newSons = @[b] - else: - newKind = rnHyperlink - newSons = @[a, b] - setRef(p, rstnodeToRefname(a), b, implicitHyperlinkAlias) + if fixupEmbeddedRef(p, n, a, b): + if a.len == 0: # e.g. ``_ + newKind = rnStandaloneHyperlink + newSons = @[b] + else: # e.g. `link title `_ + newKind = rnHyperlink + newSons = @[a, b] + setRef(p, rstnodeToRefname(a), b, implicitHyperlinkAlias) + else: # include as plain text, not a link + newKind = rnInner + newSons = n.sons result = newRstNode(newKind, newSons) else: # some link that will be resolved in `resolveSubs` newKind = rnRef @@ -1623,7 +1647,6 @@ proc parseMarkdownCodeblock(p: var RstParser): PRstNode = result.add(lb) proc parseMarkdownLink(p: var RstParser; father: PRstNode): bool = - result = true var desc, link = "" var i = p.idx @@ -1642,14 +1665,21 @@ proc parseMarkdownLink(p: var RstParser; father: PRstNode): bool = parse("]", desc) if p.tok[i].symbol != "(": return false + let linkIdx = i + 1 parse(")", link) - let child = newRstNode(rnHyperlink) - child.add desc - child.add link # only commit if we detected no syntax error: - father.add child - p.idx = i - result = true + let protocol = safeProtocol(link) + if link == "": + result = false + rstMessage(p, mwBrokenLink, protocol, + p.tok[linkIdx].line, p.tok[linkIdx].col) + else: + let child = newRstNode(rnHyperlink) + child.add desc + child.add link + father.add child + p.idx = i + result = true proc getFootnoteType(label: PRstNode): (FootnoteType, int) = if label.sons.len >= 1 and label.sons[0].kind == rnLeaf and diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index d2180cb91..d662a667c 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -40,7 +40,7 @@ ## can be done by simply searching for [footnoteName]. import strutils, os, hashes, strtabs, rstast, rst, highlite, tables, sequtils, - algorithm, parseutils, std/strbasics, strscans + algorithm, parseutils, std/strbasics import ../../std/private/since @@ -826,17 +826,6 @@ proc renderOverline(d: PDoc, n: PRstNode, result: var string) = "\\rstov$4[$5]{$3}$2\n", [$n.level, rstnodeToRefname(n).idS, tmp, $chr(n.level - 1 + ord('A')), tocName]) - -proc safeProtocol(linkStr: var string) = - var protocol = "" - if scanf(linkStr, "$w:", protocol): - # if it has a protocol at all, ensure that it's not 'javascript:' or worse: - if cmpIgnoreCase(protocol, "http") == 0 or cmpIgnoreCase(protocol, "https") == 0 or - cmpIgnoreCase(protocol, "ftp") == 0: - discard "it's fine" - else: - linkStr = "" - proc renderTocEntry(d: PDoc, e: TocEntry, result: var string) = dispA(d.target, result, "

    • $2
    • \n", @@ -901,7 +890,7 @@ proc renderImage(d: PDoc, n: PRstNode, result: var string) = # support for `:target:` links for images: var target = esc(d.target, getFieldValue(n, "target").strip(), escMode=emUrl) - safeProtocol(target) + discard safeProtocol(target) if target.len > 0: # `htmlOut` needs to be of the following format for link to work for images: @@ -1205,7 +1194,7 @@ proc renderHyperlink(d: PDoc, text, link: PRstNode, result: var string, d.escMode = emUrl renderRstToOut(d, link, linkStr) d.escMode = mode - safeProtocol(linkStr) + discard safeProtocol(linkStr) var textStr = "" renderRstToOut(d, text, textStr) let nimDocStr = if nimdoc: " nimdoc" else: "" diff --git a/tests/stdlib/trst.nim b/tests/stdlib/trst.nim index 771e02477..8b98d1403 100644 --- a/tests/stdlib/trst.nim +++ b/tests/stdlib/trst.nim @@ -843,12 +843,7 @@ suite "Warnings": rnInner rnLeaf 'foo' rnInner - rnLeaf '#' - rnLeaf 'foo' - rnLeaf ',' - rnLeaf 'string' - rnLeaf ',' - rnLeaf 'string' + rnLeaf '#foo,string,string' rnParagraph anchor='foo' rnLeaf 'Paragraph' rnLeaf '.' @@ -1256,3 +1251,23 @@ suite "RST inline markup": rnLeaf 'my {link example' rnLeaf 'http://example.com/bracket_(symbol_[)' """) + + test "not a Markdown link": + # bug #17340 (27) `f` will be considered as a protocol and blocked as unsafe + var warnings = new seq[string] + check("[T](f: var Foo)".toAst(warnings = warnings) == + dedent""" + rnInner + rnLeaf '[' + rnLeaf 'T' + rnLeaf ']' + rnLeaf '(' + rnLeaf 'f' + rnLeaf ':' + rnLeaf ' ' + rnLeaf 'var' + rnLeaf ' ' + rnLeaf 'Foo' + rnLeaf ')' + """) + check(warnings[] == @["input(1, 5) Warning: broken link 'f'"]) diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim index 995c0a151..2c28fe506 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -1593,8 +1593,15 @@ suite "invalid targets": test "invalid links": check("(([Nim](https://nim-lang.org/)))".toHtml == """((Nim))""") - check("(([Nim](javascript://nim-lang.org/)))".toHtml == - """((Nim))""") + # unknown protocol is treated just like plain text, not a link + var warnings = new seq[string] + check("(([Nim](javascript://nim-lang.org/)))".toHtml(warnings=warnings) == + """(([Nim](javascript://nim-lang.org/)))""") + check(warnings[] == @["input(1, 9) Warning: broken link 'javascript'"]) + warnings[].setLen 0 + check("`Nim `_".toHtml(warnings=warnings) == + """Nim <javascript://nim-lang.org/>""") + check(warnings[] == @["input(1, 33) Warning: broken link 'javascript'"]) suite "local file inclusion": test "cannot include files in sandboxed mode": From 27e548140b0f51a8dbc6f6094764e02f08ae509e Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 8 Feb 2022 15:34:44 +0800 Subject: [PATCH 2806/3768] don't use a temp for addr [backport: 1.6] (#19503) * don't use a temp for addr fix #19497 * Update compiler/ccgcalls.nim Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> * add a test Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> --- compiler/ccgcalls.nim | 4 ++-- tests/arc/tarc_orc.nim | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 tests/arc/tarc_orc.nim diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 197728ccf..ce5fbfdd7 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -376,8 +376,8 @@ proc genParams(p: BProc, ri: PNode, typ: PType): Rope = if not needTmp[i - 1]: needTmp[i - 1] = potentialAlias(n, potentialWrites) getPotentialWrites(ri[i], false, potentialWrites) - if ri[i].kind == nkHiddenAddr: - # Optimization: don't use a temp, if we would only take the adress anyway + if ri[i].kind in {nkHiddenAddr, nkAddr}: + # Optimization: don't use a temp, if we would only take the address anyway needTmp[i - 1] = false for i in 1.. Date: Fri, 11 Feb 2022 18:16:27 +0800 Subject: [PATCH 2807/3768] fix #16458; make useNimRtl compile for --gc:orc (#19512) * fix #16458; make useNimRtl compile for --gc:orc/arc * fix tests --- lib/system/mmdisp.nim | 5 +++-- lib/system/orc.nim | 4 ++-- lib/system/seqs_v2.nim | 3 ++- tests/arc/t16458.nim | 6 ++++++ 4 files changed, 13 insertions(+), 5 deletions(-) create mode 100644 tests/arc/t16458.nim diff --git a/lib/system/mmdisp.nim b/lib/system/mmdisp.nim index 3317ba627..e5038387f 100644 --- a/lib/system/mmdisp.nim +++ b/lib/system/mmdisp.nim @@ -82,8 +82,9 @@ else: # XXX due to bootstrapping reasons, we cannot use compileOption("gc", "stack") here include "system/gc_regions" elif defined(nimV2) or usesDestructors: - var allocator {.rtlThreadVar.}: MemRegion - instantiateForRegion(allocator) + when not defined(useNimRtl): + var allocator {.rtlThreadVar.}: MemRegion + instantiateForRegion(allocator) when defined(gcHooks): include "system/gc_hooks" elif defined(gcMarkAndSweep): diff --git a/lib/system/orc.nim b/lib/system/orc.nim index f9f5cd81f..4c23aea6c 100644 --- a/lib/system/orc.nim +++ b/lib/system/orc.nim @@ -119,7 +119,7 @@ template orcAssert(cond, msg) = when logOrc: proc strstr(s, sub: cstring): cstring {.header: "", importc.} -proc nimTraceRef(q: pointer; desc: PNimTypeV2; env: pointer) {.compilerRtl, inline.} = +proc nimTraceRef(q: pointer; desc: PNimTypeV2; env: pointer) {.compilerRtl, inl.} = let p = cast[ptr pointer](q) if p[] != nil: @@ -128,7 +128,7 @@ proc nimTraceRef(q: pointer; desc: PNimTypeV2; env: pointer) {.compilerRtl, inli var j = cast[ptr GcEnv](env) j.traceStack.add(p, desc) -proc nimTraceRefDyn(q: pointer; env: pointer) {.compilerRtl, inline.} = +proc nimTraceRefDyn(q: pointer; env: pointer) {.compilerRtl, inl.} = let p = cast[ptr pointer](q) if p[] != nil: var j = cast[ptr GcEnv](env) diff --git a/lib/system/seqs_v2.nim b/lib/system/seqs_v2.nim index 0c487b31d..1194f40ef 100644 --- a/lib/system/seqs_v2.nim +++ b/lib/system/seqs_v2.nim @@ -75,7 +75,8 @@ proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize, elemAlign: int): poin proc shrink*[T](x: var seq[T]; newLen: Natural) {.tags: [], raises: [].} = when nimvm: - setLen(x, newLen) + {.cast(tags: []).}: + setLen(x, newLen) else: #sysAssert newLen <= x.len, "invalid newLen parameter for 'shrink'" when not supportsCopyMem(T): diff --git a/tests/arc/t16458.nim b/tests/arc/t16458.nim new file mode 100644 index 000000000..6ae114287 --- /dev/null +++ b/tests/arc/t16458.nim @@ -0,0 +1,6 @@ +discard """ + matrix: "--gc:orc --d:useNimRtl" + action: "compile" +""" + +echo 134 \ No newline at end of file From ed0dce7292480002be96d0ea5b7775c38767134d Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 12 Feb 2022 15:10:45 +0100 Subject: [PATCH 2808/3768] fixes #19404 by protecting the memory we borrow from. this replaces crashes with minor memory leaks which seems to be acceptable. In the longer run we need a better VM that didn't grow hacks over a decade. (#19515) Co-authored-by: flywind --- compiler/vm.nim | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/compiler/vm.nim b/compiler/vm.nim index 3343eb781..259648add 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -85,9 +85,9 @@ proc bailOut(c: PCtx; tos: PStackFrame) = when not defined(nimComputedGoto): {.pragma: computedGoto.} -proc ensureKind(n: var TFullReg, kind: TRegisterKind) = - if n.kind != kind: - n = TFullReg(kind: kind) +proc ensureKind(n: var TFullReg, k: TRegisterKind) {.inline.} = + if n.kind != k: + n = TFullReg(kind: k) template ensureKind(k: untyped) {.dirty.} = ensureKind(regs[ra], k) @@ -521,6 +521,11 @@ template maybeHandlePtr(node2: PNode, reg: TFullReg, isAssign2: bool): bool = when not defined(nimHasSinkInference): {.pragma: nosinks.} +template takeAddress(reg, source) = + reg.nodeAddr = addr source + when defined(gcDestructors): + GC_ref source + proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = var pc = start var tos = tos @@ -679,7 +684,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = let idx = regs[rc].intVal.int let src = if regs[rb].kind == rkNode: regs[rb].node else: regs[rb].nodeAddr[] if src.kind notin {nkEmpty..nkTripleStrLit} and idx <% src.len: - regs[ra].nodeAddr = addr src.sons[idx] + takeAddress regs[ra], src.sons[idx] else: stackTrace(c, tos, pc, formatErrorIndexBound(idx, src.safeLen-1)) of opcLdStrIdx: @@ -747,11 +752,11 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of nkObjConstr: let n = src.sons[rc + 1] if n.kind == nkExprColonExpr: - regs[ra].nodeAddr = addr n.sons[1] + takeAddress regs[ra], n.sons[1] else: - regs[ra].nodeAddr = addr src.sons[rc + 1] + takeAddress regs[ra], src.sons[rc + 1] else: - regs[ra].nodeAddr = addr src.sons[rc] + takeAddress regs[ra], src.sons[rc] of opcWrObj: # a.b = c decodeBC(rkNode) @@ -778,7 +783,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = decodeB(rkNodeAddr) case regs[rb].kind of rkNode: - regs[ra].nodeAddr = addr(regs[rb].node) + takeAddress regs[ra], regs[rb].node of rkNodeAddr: # bug #14339 regs[ra].nodeAddr = regs[rb].nodeAddr else: From 551225d88808c6029d9246cf2bea5da349d1eb43 Mon Sep 17 00:00:00 2001 From: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Date: Mon, 14 Feb 2022 08:50:38 +0100 Subject: [PATCH 2809/3768] Remove backslash in glob pattern (#19524) --- lib/pure/os.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 76164bc49..7c0c64276 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -2131,7 +2131,7 @@ iterator walkPattern*(pattern: string): string {.tags: [ReadDirEffect], noWeirdT ## Iterate over all the files and directories that match the `pattern`. ## ## On POSIX this uses the `glob`:idx: call. - ## `pattern` is OS dependent, but at least the `"\*.ext"` + ## `pattern` is OS dependent, but at least the `"*.ext"` ## notation is supported. ## ## See also: @@ -2150,7 +2150,7 @@ iterator walkFiles*(pattern: string): string {.tags: [ReadDirEffect], noWeirdTar ## Iterate over all the files that match the `pattern`. ## ## On POSIX this uses the `glob`:idx: call. - ## `pattern` is OS dependent, but at least the `"\*.ext"` + ## `pattern` is OS dependent, but at least the `"*.ext"` ## notation is supported. ## ## See also: @@ -2167,7 +2167,7 @@ iterator walkDirs*(pattern: string): string {.tags: [ReadDirEffect], noWeirdTarg ## Iterate over all the directories that match the `pattern`. ## ## On POSIX this uses the `glob`:idx: call. - ## `pattern` is OS dependent, but at least the `"\*.ext"` + ## `pattern` is OS dependent, but at least the `"*.ext"` ## notation is supported. ## ## See also: From cda77c15209519ea04f889a826ac79c7cbb7e152 Mon Sep 17 00:00:00 2001 From: David Krause Date: Mon, 14 Feb 2022 08:56:35 +0100 Subject: [PATCH 2810/3768] use OrderedTable instead of OrderedTableRef for mimedb (#19522) * use OrderedTable instead of OrderedTableRef for mimedb Signed-off-by: David Krause * added changelog entry for mimedb change Signed-off-by: David Krause --- changelog.md | 2 ++ lib/pure/mimetypes.nim | 4 ++-- tests/stdlib/tmimetypes.nim | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index 412e046eb..b21784bae 100644 --- a/changelog.md +++ b/changelog.md @@ -40,6 +40,8 @@ becomes an alias for `addr`. - Added `std/oserrors` for OS error reporting. Added `std/envvars` for environment variables handling. +- Changed mimedb to use an `OrderedTable` instead of `OrderedTableRef`, to use it in a const. + ## Language changes - Pragma macros on type definitions can now return `nnkTypeSection` nodes as well as `nnkTypeDef`, diff --git a/lib/pure/mimetypes.nim b/lib/pure/mimetypes.nim index 6ae63a0ae..d1566d897 100644 --- a/lib/pure/mimetypes.nim +++ b/lib/pure/mimetypes.nim @@ -31,7 +31,7 @@ from strutils import startsWith, toLowerAscii, strip type MimeDB* = object - mimes: OrderedTableRef[string, string] + mimes: OrderedTable[string, string] const mimes* = { "123": "application/vnd.lotus-1-2-3", @@ -1904,7 +1904,7 @@ func newMimetypes*(): MimeDB = ## Creates a new Mimetypes database. The database will contain the most ## common mimetypes. {.cast(noSideEffect).}: - result.mimes = mimes.newOrderedTable() + result.mimes = mimes.toOrderedTable() func getMimetype*(mimedb: MimeDB, ext: string, default = "text/plain"): string = ## Gets mimetype which corresponds to `ext`. Returns `default` if `ext` diff --git a/tests/stdlib/tmimetypes.nim b/tests/stdlib/tmimetypes.nim index 93c20d4a3..6435309e1 100644 --- a/tests/stdlib/tmimetypes.nim +++ b/tests/stdlib/tmimetypes.nim @@ -7,6 +7,8 @@ template main() = var m = newMimetypes() doAssert m.getMimetype("mp4") == "video/mp4" doAssert m.getExt("application/json") == "json" + m.register("foo", "baa") + doAssert m.getMimetype("foo") == "baa" # see also `runnableExamples`. # xxx we should have a way to avoid duplicating code between runnableExamples and tests From 42eeab5fd181ffca6d1197b704a15f056939cfa8 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Mon, 14 Feb 2022 00:09:21 -0800 Subject: [PATCH 2811/3768] Remove Deprecated oids.oidsToString (#19519) * Remove deprecated oids.oidToString * Remove deprecated oids.oidToString --- changelog.md | 2 ++ lib/pure/oids.nim | 5 ----- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/changelog.md b/changelog.md index b21784bae..ec13152d0 100644 --- a/changelog.md +++ b/changelog.md @@ -39,6 +39,8 @@ becomes an alias for `addr`. - Added `getIsoWeeksInYear` in `times` to return the number of weeks in an ISO week-based year. - Added `std/oserrors` for OS error reporting. Added `std/envvars` for environment variables handling. +- Removed deprecated `oids.oidToString`. + - Changed mimedb to use an `OrderedTable` instead of `OrderedTableRef`, to use it in a const. diff --git a/lib/pure/oids.nim b/lib/pure/oids.nim index da285a187..776c046b1 100644 --- a/lib/pure/oids.nim +++ b/lib/pure/oids.nim @@ -67,11 +67,6 @@ template toStringImpl[T: string | cstring](result: var T, oid: Oid) = when T is cstring: result[N] = '\0' -proc oidToString*(oid: Oid, str: cstring) {.deprecated: "unsafe; use `$`".} = - ## Converts an oid to a string which must have space allocated for 25 elements. - # work around a compiler bug: - var str = str - toStringImpl(str, oid) proc `$`*(oid: Oid): string = ## Converts an OID to a string. From 8e8231f9d6e586a32af7b80892244dc1033db871 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Mon, 14 Feb 2022 05:35:33 -0800 Subject: [PATCH 2812/3768] Remove deprecated math.c_frexp (#19518) * Remove Deprecated math proc * Remove Deprecated math proc * Remove Deprecated math proc --- changelog.md | 3 +++ lib/pure/math.nim | 5 ----- tests/stdlib/tmath.nim | 6 ------ 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/changelog.md b/changelog.md index ec13152d0..3fd66dba8 100644 --- a/changelog.md +++ b/changelog.md @@ -44,6 +44,9 @@ becomes an alias for `addr`. - Changed mimedb to use an `OrderedTable` instead of `OrderedTableRef`, to use it in a const. +- Removed deprecated `math.c_frexp`. + + ## Language changes - Pragma macros on type definitions can now return `nnkTypeSection` nodes as well as `nnkTypeDef`, diff --git a/lib/pure/math.nim b/lib/pure/math.nim index 66f1a2e2c..1c47258bc 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -69,11 +69,6 @@ when defined(c) or defined(cpp): proc c_signbit(x: SomeFloat): cint {.importc: "signbit", header: "".} - func c_frexp*(x: cfloat, exponent: var cint): cfloat {. - importc: "frexpf", header: "", deprecated: "Use `frexp` instead".} - func c_frexp*(x: cdouble, exponent: var cint): cdouble {. - importc: "frexp", header: "", deprecated: "Use `frexp` instead".} - # don't export `c_frexp` in the future and remove `c_frexp2`. func c_frexp2(x: cfloat, exponent: var cint): cfloat {. importc: "frexpf", header: "".} diff --git a/tests/stdlib/tmath.nim b/tests/stdlib/tmath.nim index 9bc5b994f..5e501c09b 100644 --- a/tests/stdlib/tmath.nim +++ b/tests/stdlib/tmath.nim @@ -437,12 +437,6 @@ template main() = doAssert lgamma(-0.0) == Inf doAssert lgamma(-1.0) == Inf - when nimvm: discard - else: - var exponent: cint - doAssert c_frexp(0.0, exponent) == 0.0 - doAssert c_frexp(-0.0, exponent) == -0.0 - doAssert classify(c_frexp(-0.0, exponent)) == fcNegZero static: main() main() From 2ef71c0a46b2209c906921f10f585306ce6199cc Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 14 Feb 2022 21:36:01 +0800 Subject: [PATCH 2813/3768] [testcase] genSym fails to make unique identifier for ref object types (#19506) close #15118 --- tests/macros/tmacros_various.nim | 60 ++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/tests/macros/tmacros_various.nim b/tests/macros/tmacros_various.nim index 00d74cb5c..d702db56a 100644 --- a/tests/macros/tmacros_various.nim +++ b/tests/macros/tmacros_various.nim @@ -23,6 +23,8 @@ x: some string ([("key", "val"), ("keyB", "2")], [("val", "key"), ("2", "keyB")]) ([("key", "val"), ("keyB", "2")], [("val", "key"), ("2", "keyB")]) 0 +0 +0 ''' """ @@ -270,3 +272,61 @@ block: # bug #13511 debugAst: add(foo(), component) + +block: # bug #15118 + macro flop(name: static string) = + let id = genSym(nskType, "env") + let r = + nnkStmtList.newTree( + nnkTypeSection.newTree( + nnkTypeDef.newTree( + id, + newEmptyNode(), + nnkRefTy.newTree( + nnkObjectTy.newTree( + newEmptyNode(), + newEmptyNode(), + nnkRecList.newTree( + nnkIdentDefs.newTree( + newIdentNode(name), + newIdentNode("int"), + newEmptyNode() + ) + ) + ) + ) + ) + ), + + # var f: T + + nnkVarSection.newTree( + nnkIdentDefs.newTree( + newIdentNode("f"), + id, + newEmptyNode() + ) + ), + + # echo f.a + nnkCommand.newTree( + newIdentNode("new"), + newIdentNode("f") + ), + + nnkCommand.newTree( + newIdentNode("echo"), + nnkDotExpr.newTree( + newIdentNode("f"), + newIdentNode(name) + ) + ) + ) + r + + + block: + flop("a") + + block: + flop("b") From b2c5d7b4ff070abe2145e998d090fb15b4df522f Mon Sep 17 00:00:00 2001 From: Sizhe Zhao Date: Tue, 15 Feb 2022 06:15:59 +0800 Subject: [PATCH 2814/3768] Documentation: Fix word usage (#19529) --- doc/lib.rst | 2 +- lib/pure/base64.nim | 4 ++-- lib/pure/hashes.nim | 2 +- lib/pure/md5.nim | 4 ++-- lib/std/sha1.nim | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/lib.rst b/doc/lib.rst index 881d857d3..f72012777 100644 --- a/doc/lib.rst +++ b/doc/lib.rst @@ -446,7 +446,7 @@ Hashing from the MongoDB interface and it thus binary compatible with a MongoDB OID. * `sha1 `_ - This module implements a sha1 encoder and decoder. + This module implements the SHA-1 checksum algorithm. Miscellaneous diff --git a/lib/pure/base64.nim b/lib/pure/base64.nim index 513ec5826..9b7af88ef 100644 --- a/lib/pure/base64.nim +++ b/lib/pure/base64.nim @@ -56,8 +56,8 @@ runnableExamples: ## ======== ## ## * `hashes module`_ for efficient computations of hash values for diverse Nim types -## * `md5 module`_ implements the MD5 checksum algorithm -## * `sha1 module`_ implements a sha1 encoder and decoder +## * `md5 module`_ for the MD5 checksum algorithm +## * `sha1 module`_ for the SHA-1 checksum algorithm template cbBase(a, b): untyped = [ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim index d9f534670..47dacec7d 100644 --- a/lib/pure/hashes.nim +++ b/lib/pure/hashes.nim @@ -62,7 +62,7 @@ runnableExamples: ## ======== ## * `md5 module `_ for the MD5 checksum algorithm ## * `base64 module `_ for a Base64 encoder and decoder -## * `std/sha1 module `_ for a SHA-1 encoder and decoder +## * `std/sha1 module `_ for the SHA-1 checksum algorithm ## * `tables module `_ for hash tables import std/private/since diff --git a/lib/pure/md5.nim b/lib/pure/md5.nim index 06c5608a4..cd4d1e6b8 100644 --- a/lib/pure/md5.nim +++ b/lib/pure/md5.nim @@ -13,8 +13,8 @@ ## ## See also ## ======== -## * `base64 module`_ implements a Base64 encoder and decoder -## * `std/sha1 module `_ for a SHA-1 encoder and decoder +## * `base64 module`_ for a Base64 encoder and decoder +## * `std/sha1 module `_ for the SHA-1 checksum algorithm ## * `hashes module`_ for efficient computations of hash values ## for diverse Nim types diff --git a/lib/std/sha1.nim b/lib/std/sha1.nim index 120a81702..ed962707b 100644 --- a/lib/std/sha1.nim +++ b/lib/std/sha1.nim @@ -12,9 +12,9 @@ ## ## See also ## ======== -## * `base64 module`_ implements a Base64 encoder and decoder +## * `base64 module`_ for a Base64 encoder and decoder ## * `hashes module`_ for efficient computations of hash values for diverse Nim types -## * `md5 module`_ implements the MD5 checksum algorithm +## * `md5 module`_ for the MD5 checksum algorithm runnableExamples: let accessName = secureHash("John Doe") From 30e9f23ec04618616dcf2aaa7980794bad2135ed Mon Sep 17 00:00:00 2001 From: rockcavera Date: Thu, 17 Feb 2022 08:48:57 -0300 Subject: [PATCH 2815/3768] Update chcks.nim (#19540) --- lib/system/chcks.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/system/chcks.nim b/lib/system/chcks.nim index 8c7c95795..a404e9d40 100644 --- a/lib/system/chcks.nim +++ b/lib/system/chcks.nim @@ -29,7 +29,7 @@ proc raiseFieldError(f: string) {.compilerproc, noinline.} = ## remove after bootstrap > 1.5.1 sysFatal(FieldDefect, f) -when defined(gcdestructors): +when defined(nimV2): proc raiseFieldError2(f: string, discVal: int) {.compilerproc, noinline.} = ## raised when field is inaccessible given runtime value of discriminant sysFatal(FieldError, f & $discVal & "'") From f019e999d491ecf462421bd750aec0465d308094 Mon Sep 17 00:00:00 2001 From: tersec Date: Fri, 18 Feb 2022 15:06:11 +0000 Subject: [PATCH 2816/3768] keep casing of noinit and noreturn pragmas consistently documented (#19535) --- doc/manual.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/manual.rst b/doc/manual.rst index 2469a0525..e45133c38 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -2818,7 +2818,7 @@ The implicit initialization can be avoided for optimization reasons with the .. code-block:: nim var - a {.noInit.}: array[0..1023, char] + a {.noinit.}: array[0..1023, char] If a proc is annotated with the `noinit` pragma, this refers to its implicit `result` variable: @@ -6636,7 +6636,7 @@ but accessed at runtime: doAssert nameToProc[2][1]() == "baz" -noReturn pragma +noreturn pragma --------------- The `noreturn` pragma is used to mark a proc that never returns. From 516db3bac389ea554166ee1a6671b5005159a30a Mon Sep 17 00:00:00 2001 From: locriacyber <74560659+locriacyber@users.noreply.github.com> Date: Fri, 18 Feb 2022 22:24:42 +0000 Subject: [PATCH 2817/3768] Remove trailing space in `nim r` command; Amend one error message (#19487) --- compiler/nim.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/nim.nim b/compiler/nim.nim index b8256d576..86c7ab524 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -116,7 +116,8 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = if cmdPrefix.len > 0: cmdPrefix.add " " # without the `cmdPrefix.len > 0` check, on windows you'd get a cryptic: # `The parameter is incorrect` - execExternalProgram(conf, cmdPrefix & output.quoteShell & ' ' & conf.arguments) + let cmd = cmdPrefix & output.quoteShell & ' ' & conf.arguments + execExternalProgram(conf, cmd.strip(leading=false,trailing=true)) of cmdDocLike, cmdRst2html, cmdRst2tex: # bugfix(cmdRst2tex was missing) if conf.arguments.len > 0: # reserved for future use From 0c915b5e47e554d657f6533f262cebbcf1554e8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C8=98tefan=20Talpalaru?= Date: Thu, 24 Feb 2022 08:31:40 +0100 Subject: [PATCH 2818/3768] compile pragma: cache the result sooner (#19554) extccomp.addExternalFileToCompile() relies on hashes to decide whether an external C file needs recompilation or not. Due to short-circuit evaluation of boolean expressions, the procedure that generates a corresponding hash file is not called the first time an external file is compiled, so an avoidable recompilation is triggered the next build. This patch fixes that by moving the proc call with a desired side effect from its boolean expression, so it's executed unconditionally. --- compiler/extccomp.nim | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index e7e1bcd51..124cc2c34 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -649,8 +649,10 @@ proc externalFileChanged(conf: ConfigRef; cfile: Cfile): bool = close(f) proc addExternalFileToCompile*(conf: ConfigRef; c: var Cfile) = + # we want to generate the hash file unconditionally + let extFileChanged = externalFileChanged(conf, c) if optForceFullMake notin conf.globalOptions and fileExists(c.obj) and - not externalFileChanged(conf, c): + not extFileChanged: c.flags.incl CfileFlag.Cached else: # make sure Nim keeps recompiling the external file on reruns From 340b5a1676062aa9bc7f17a2e078eaba96821bf3 Mon Sep 17 00:00:00 2001 From: Jake Leahy Date: Fri, 25 Feb 2022 07:16:46 +1100 Subject: [PATCH 2819/3768] Document links repect --out in .idx file (#19543) * Link in .idx file now respects --out:file switch * Use clearer chained calls Co-authored-by: Clay Sweetser --- compiler/docgen.nim | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 3e61032d8..aad3b9409 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -1499,7 +1499,10 @@ proc genOutFile(d: PDoc, groupedToc = false): string = # Extract the title. Non API modules generate an entry in the index table. if d.meta[metaTitle].len != 0: title = d.meta[metaTitle] - let external = presentationPath(d.conf, AbsoluteFile d.filename).changeFileExt(HtmlExt).string.nativeToUnixPath + let external = AbsoluteFile(d.destFile) + .relativeTo(d.conf.outDir, '/') + .changeFileExt(HtmlExt) + .string setIndexTerm(d[], external, "", title) else: # Modules get an automatic title for the HTML, but no entry in the index. From 9a49451124a781a2cd032b6e7616c67a7815fe57 Mon Sep 17 00:00:00 2001 From: PMunch Date: Fri, 25 Feb 2022 10:43:03 +0100 Subject: [PATCH 2820/3768] Remove volatiles when compiling with ARC/ORC (#19545) This removes volatiles on ARC/ORC targets in NimMain and PreMainInner. This avoids an issue where they couldn't be optimised out on microcontrollers leading to larger code. Since the stack bottom doesn't have to be initialised this way when using ARC or ORC (or None, which is also covered by this PR) these can be safely removed. --- compiler/cgen.nim | 68 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 50 insertions(+), 18 deletions(-) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 10b4b55d0..b78ad10c4 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -1363,16 +1363,25 @@ proc genMainProc(m: BModule) = # The use of a volatile function pointer to call Pre/NimMainInner # prevents inlining of the NimMainInner function and dependent # functions, which might otherwise merge their stack frames. - PreMainBody = "$N" & + + PreMainVolatileBody = + "\tvoid (*volatile inner)(void);$N" & + "\tinner = PreMainInner;$N" & + "$1" & + "\t(*inner)();$N" + + PreMainNonVolatileBody = + "$1" & + "\tPreMainInner();$N" + + PreMainBodyStart = "$N" & "N_LIB_PRIVATE void PreMainInner(void) {$N" & "$2" & "}$N$N" & PosixCmdLine & - "N_LIB_PRIVATE void PreMain(void) {$N" & - "\tvoid (*volatile inner)(void);$N" & - "\tinner = PreMainInner;$N" & - "$1" & - "\t(*inner)();$N" & + "N_LIB_PRIVATE void PreMain(void) {$N" + + PreMainBodyEnd = "}$N$N" MainProcs = @@ -1385,17 +1394,32 @@ proc genMainProc(m: BModule) = "$1" & "}$N$N" - NimMainProc = - "N_CDECL(void, $5NimMain)(void) {$N" & - "\tvoid (*volatile inner)(void);$N" & - "$4" & - "\tinner = NimMainInner;$N" & - "$2" & - "\t(*inner)();$N" & + NimMainVolatileBody = + "\tvoid (*volatile inner)(void);$N" & + "$4" & + "\tinner = NimMainInner;$N" & + "$2" & + "\t(*inner)();$N" + + NimMainNonVolatileBody = + "$4" & + "$2" & + "\tNimMainInner();$N" + + NimMainProcStart = + "N_CDECL(void, $5NimMain)(void) {$N" + + NimMainProcEnd = "}$N$N" + NimMainProc = NimMainProcStart & NimMainVolatileBody & NimMainProcEnd + + NimSlimMainProc = NimMainProcStart & NimMainNonVolatileBody & NimMainProcEnd + NimMainBody = NimMainInner & NimMainProc + NimSlimMainBody = NimMainInner & NimSlimMainProc + PosixCMain = "int main(int argc, char** args, char** env) {$N" & "\tcmdLine = args;$N" & @@ -1456,10 +1480,13 @@ proc genMainProc(m: BModule) = m.includeHeader("") let initStackBottomCall = - if m.config.target.targetOS == osStandalone or m.config.selectedGC == gcNone: "".rope + if m.config.target.targetOS == osStandalone or m.config.selectedGC in {gcNone, gcArc, gcOrc}: "".rope else: ropecg(m, "\t#initStackBottomWith((void *)&inner);$N", []) inc(m.labels) - appcg(m, m.s[cfsProcs], PreMainBody, [m.g.mainDatInit, m.g.otherModsInit]) + if m.config.selectedGC notin {gcNone, gcArc, gcOrc}: + appcg(m, m.s[cfsProcs], PreMainBodyStart & PreMainVolatileBody & PreMainBodyEnd, [m.g.mainDatInit, m.g.otherModsInit]) + else: + appcg(m, m.s[cfsProcs], PreMainBodyStart & PreMainNonVolatileBody & PreMainBodyEnd, [m.g.mainDatInit, m.g.otherModsInit]) if m.config.target.targetOS == osWindows and m.config.globalOptions * {optGenGuiApp, optGenDynLib} != {}: @@ -1484,9 +1511,14 @@ proc genMainProc(m: BModule) = appcg(m, m.s[cfsProcs], nimMain, [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix]) else: - const nimMain = NimMainBody - appcg(m, m.s[cfsProcs], nimMain, - [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix]) + if m.config.selectedGC notin {gcNone, gcArc, gcOrc}: + const nimMain = NimMainBody + appcg(m, m.s[cfsProcs], nimMain, + [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix]) + else: + const nimMain = NimSlimMainBody + appcg(m, m.s[cfsProcs], nimMain, + [m.g.mainModInit, initStackBottomCall, m.labels, preMainCode, m.config.nimMainPrefix]) if optNoMain notin m.config.globalOptions: if m.config.cppCustomNamespace.len > 0: From fe791c67b4c028d2e439e6f74e0a4225ba6cc2f1 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Fri, 25 Feb 2022 01:43:35 -0800 Subject: [PATCH 2821/3768] Remove Deprecated jsre func (#19551) * Remove Deprecated jsre func * Remove Deprecated jsre func * ReStart CI, because OSX is dumb and I have no permission to restart --- changelog.md | 2 +- lib/js/jsre.nim | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/changelog.md b/changelog.md index 3fd66dba8..cbad9b4e6 100644 --- a/changelog.md +++ b/changelog.md @@ -43,7 +43,7 @@ becomes an alias for `addr`. - Changed mimedb to use an `OrderedTable` instead of `OrderedTableRef`, to use it in a const. - +- Removed deprecated `jsre.test` and `jsre.toString`. - Removed deprecated `math.c_frexp`. diff --git a/lib/js/jsre.nim b/lib/js/jsre.nim index a23fccaa8..cd8fb2be7 100644 --- a/lib/js/jsre.nim +++ b/lib/js/jsre.nim @@ -47,10 +47,6 @@ func toCstring*(self: RegExp): cstring {.importjs: "#.toString()".} func `$`*(self: RegExp): string = $toCstring(self) -func test*(self: RegExp; pattern: cstring): bool {.importjs: "#.test(#)", deprecated: "Use contains instead".} - -func toString*(self: RegExp): cstring {.importjs: "#.toString()", deprecated: "Use toCstring instead".} - func contains*(pattern: cstring; self: RegExp): bool = ## Tests for a substring match in its string parameter. runnableExamples: From ef3f343ec2979d22a5d4e51f4328e4a4c21d68dc Mon Sep 17 00:00:00 2001 From: Tanguy Date: Fri, 25 Feb 2022 12:57:58 +0100 Subject: [PATCH 2822/3768] Allow std/macros.params to work with nnkProcTy (#19563) * Allow std/macros.params to work with nnkProcTy * Add tests for proc params & pragma --- lib/core/macros.nim | 15 +++++++++++---- tests/macros/tprochelpers.nim | 22 ++++++++++++++++++++++ 2 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 tests/macros/tprochelpers.nim diff --git a/lib/core/macros.nim b/lib/core/macros.nim index c0e6e5154..4d040cebc 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -1271,12 +1271,19 @@ proc `name=`*(someProc: NimNode; val: NimNode) = else: someProc[0] = val proc params*(someProc: NimNode): NimNode = - someProc.expectRoutine - result = someProc[3] + if someProc.kind == nnkProcTy: + someProc[0] + else: + someProc.expectRoutine + someProc[3] + proc `params=`* (someProc: NimNode; params: NimNode) = - someProc.expectRoutine expectKind(params, nnkFormalParams) - someProc[3] = params + if someProc.kind == nnkProcTy: + someProc[0] = params + else: + someProc.expectRoutine + someProc[3] = params proc pragma*(someProc: NimNode): NimNode = ## Get the pragma of a proc type. diff --git a/tests/macros/tprochelpers.nim b/tests/macros/tprochelpers.nim new file mode 100644 index 000000000..d95a2ced8 --- /dev/null +++ b/tests/macros/tprochelpers.nim @@ -0,0 +1,22 @@ +import std/macros +import stdtest/testutils + +macro test1(prc: untyped): untyped = + assertAll: + prc.params.len == 2 + prc.params[1].len == 4 + prc.pragma.len == 2 + + prc.params = nnkFormalParams.newTree( + ident("int") + ) + prc.pragma = newEmptyNode() + + assertAll: + prc.params.len == 1 + prc.pragma.len == 0 + prc + +proc test(a, b: int): int {.gcsafe, raises: [], test1.} = 5 + +type hello = proc(a, b: int): int {.gcsafe, raises: [], test1.} From 9c17a32e0ec45de83c21d84f98be7f351ccd1172 Mon Sep 17 00:00:00 2001 From: flywind Date: Sat, 26 Feb 2022 03:33:28 +0800 Subject: [PATCH 2823/3768] fix #19266; allow reassign discriminant field (#19567) * add inUncheckedAssignSection * add one more test --- compiler/ccgexprs.nim | 16 +++++++++++++++- compiler/ccgstmts.nim | 2 +- compiler/cgendata.nim | 1 + tests/objvariant/treassign.nim | 22 ++++++++++++++++++++++ 4 files changed, 39 insertions(+), 2 deletions(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 1f1035115..9848366a1 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2932,7 +2932,21 @@ proc expr(p: BProc, n: PNode, d: var TLoc) = nkFromStmt, nkTemplateDef, nkMacroDef, nkStaticStmt: discard of nkPragma: genPragma(p, n) - of nkPragmaBlock: expr(p, n.lastSon, d) + of nkPragmaBlock: + var inUncheckedAssignSection = 0 + let pragmaList = n[0] + for pi in pragmaList: + if whichPragma(pi) == wCast: + case whichPragma(pi[1]) + of wUncheckedAssign: + inUncheckedAssignSection = 1 + else: + discard + + inc p.inUncheckedAssignSection, inUncheckedAssignSection + expr(p, n.lastSon, d) + dec p.inUncheckedAssignSection, inUncheckedAssignSection + of nkProcDef, nkFuncDef, nkMethodDef, nkConverterDef: if n[genericParamsPos].kind == nkEmpty: var prc = n[namePos].sym diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 58a6dd015..240fa55c8 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -1566,7 +1566,7 @@ proc asgnFieldDiscriminant(p: BProc, e: PNode) = initLocExpr(p, e[0], a) getTemp(p, a.t, tmp) expr(p, e[1], tmp) - if optTinyRtti notin p.config.globalOptions: + if optTinyRtti notin p.config.globalOptions and p.inUncheckedAssignSection == 0: let field = dotExpr[1].sym genDiscriminantCheck(p, a, tmp, dotExpr[0].typ, field) message(p.config, e.info, warnCaseTransition) diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index 88b8d4090..449076725 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -99,6 +99,7 @@ type withinTryWithExcept*: int # required for goto based exception handling withinBlockLeaveActions*: int # complex to explain sigConflicts*: CountTable[string] + inUncheckedAssignSection*: int TTypeSeq* = seq[PType] TypeCache* = Table[SigHash, Rope] diff --git a/tests/objvariant/treassign.nim b/tests/objvariant/treassign.nim index 9549cb29c..527204616 100644 --- a/tests/objvariant/treassign.nim +++ b/tests/objvariant/treassign.nim @@ -34,3 +34,25 @@ proc passToVar(x: var Token) = discard t.curr = TokenObject(kind: t.curr.kind, foo: "abc") t.curr.kind = Token.foo + + +block: + type + TokenKind = enum + strLit, intLit + Token = object + case kind*: TokenKind + of strLit: + s*: string + of intLit: + i*: int64 + + var t = Token(kind: strLit, s: "abc") + + {.cast(uncheckedAssign).}: + + # inside the 'cast' section it is allowed to assign to the 't.kind' field directly: + t.kind = intLit + + {.cast(uncheckedAssign).}: + t.kind = strLit \ No newline at end of file From f0bfc0bd3f619c0933df5b71a0a04d4181d99880 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Fri, 25 Feb 2022 11:34:16 -0800 Subject: [PATCH 2824/3768] Remove define for jsfetch (#19530) * Remove define nimExperimentalAsyncjsThen for std/asyncjs.then and std/jsfetch * Remove define nimExperimentalAsyncjsThen for std/asyncjs.then and std/jsfetch * Remove define nimExperimentalAsyncjsThen for std/asyncjs.then and std/jsfetch * Remove define nimExperimentalAsyncjsThen for std/asyncjs.then and std/jsfetch --- changelog.md | 2 +- lib/js/asyncjs.nim | 148 ++++++++++++++++++++++---------------------- lib/std/jsfetch.nim | 15 +++-- tools/kochdocs.nim | 2 +- 4 files changed, 83 insertions(+), 84 deletions(-) diff --git a/changelog.md b/changelog.md index cbad9b4e6..011f95ddb 100644 --- a/changelog.md +++ b/changelog.md @@ -40,7 +40,7 @@ becomes an alias for `addr`. - Added `std/oserrors` for OS error reporting. Added `std/envvars` for environment variables handling. - Removed deprecated `oids.oidToString`. - +- Remove define `nimExperimentalAsyncjsThen` for `std/asyncjs.then` and `std/jsfetch`. - Changed mimedb to use an `OrderedTable` instead of `OrderedTableRef`, to use it in a const. - Removed deprecated `jsre.test` and `jsre.toString`. diff --git a/lib/js/asyncjs.nim b/lib/js/asyncjs.nim index 861fe3fe2..8f91e1acd 100644 --- a/lib/js/asyncjs.nim +++ b/lib/js/asyncjs.nim @@ -64,6 +64,7 @@ when not defined(js) and not defined(nimsuggest): import std/jsffi import std/macros +import std/private/since type Future*[T] = ref object @@ -163,92 +164,91 @@ template maybeFuture(T): untyped = when T is Future: T else: Future[T] -when defined(nimExperimentalAsyncjsThen): - import std/private/since - since (1, 5, 1): - #[ - TODO: - * map `Promise.all()` - * proc toString*(a: Error): cstring {.importjs: "#.toString()".} - Note: - We probably can't have a `waitFor` in js in browser (single threaded), but maybe it would be possible - in in nodejs, see https://nodejs.org/api/child_process.html#child_process_child_process_execsync_command_options - and https://stackoverflow.com/questions/61377358/javascript-wait-for-async-call-to-finish-before-returning-from-function-witho - ]# +since (1, 5, 1): + #[ + TODO: + * map `Promise.all()` + * proc toString*(a: Error): cstring {.importjs: "#.toString()".} - type Error* {.importjs: "Error".} = ref object of JsRoot - ## https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error - message*: cstring - name*: cstring + Note: + We probably can't have a `waitFor` in js in browser (single threaded), but maybe it would be possible + in in nodejs, see https://nodejs.org/api/child_process.html#child_process_child_process_execsync_command_options + and https://stackoverflow.com/questions/61377358/javascript-wait-for-async-call-to-finish-before-returning-from-function-witho + ]# - type OnReject* = proc(reason: Error) + type Error* {.importjs: "Error".} = ref object of JsRoot + ## https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error + message*: cstring + name*: cstring - proc then*[T](future: Future[T], onSuccess: proc, onReject: OnReject = nil): auto = - ## See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then - ## Returns a `Future` from the return type of `onSuccess(T.default)`. - runnableExamples("-d:nimExperimentalAsyncjsThen"): - from std/sugar import `=>` + type OnReject* = proc(reason: Error) - proc fn(n: int): Future[int] {.async.} = - if n >= 7: raise newException(ValueError, "foobar: " & $n) - else: result = n * 2 + proc then*[T](future: Future[T], onSuccess: proc, onReject: OnReject = nil): auto = + ## See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then + ## Returns a `Future` from the return type of `onSuccess(T.default)`. + runnableExamples("-r:off"): + from std/sugar import `=>` - proc asyncFact(n: int): Future[int] {.async.} = - if n > 0: result = n * await asyncFact(n-1) - else: result = 1 + proc fn(n: int): Future[int] {.async.} = + if n >= 7: raise newException(ValueError, "foobar: " & $n) + else: result = n * 2 - proc main() {.async.} = - block: # then - assert asyncFact(3).await == 3*2 - assert asyncFact(3).then(asyncFact).await == 6*5*4*3*2 - let x1 = await fn(3) - assert x1 == 3 * 2 - let x2 = await fn(4) - .then((a: int) => a.float) - .then((a: float) => $a) - assert x2 == "8.0" + proc asyncFact(n: int): Future[int] {.async.} = + if n > 0: result = n * await asyncFact(n-1) + else: result = 1 - block: # then with `onReject` callback - var witness = 1 - await fn(6).then((a: int) => (witness = 2), (r: Error) => (witness = 3)) - assert witness == 2 - await fn(7).then((a: int) => (witness = 2), (r: Error) => (witness = 3)) - assert witness == 3 + proc main() {.async.} = + block: # then + assert asyncFact(3).await == 3*2 + assert asyncFact(3).then(asyncFact).await == 6*5*4*3*2 + let x1 = await fn(3) + assert x1 == 3 * 2 + let x2 = await fn(4) + .then((a: int) => a.float) + .then((a: float) => $a) + assert x2 == "8.0" - template impl(call): untyped = - # see D20210421T014713 - when typeof(block: call) is void: - var ret: Future[void] - else: - var ret = default(maybeFuture(typeof(call))) - typeof(ret) - when T is void: - type A = impl(onSuccess()) + block: # then with `onReject` callback + var witness = 1 + await fn(6).then((a: int) => (witness = 2), (r: Error) => (witness = 3)) + assert witness == 2 + await fn(7).then((a: int) => (witness = 2), (r: Error) => (witness = 3)) + assert witness == 3 + + template impl(call): untyped = + # see D20210421T014713 + when typeof(block: call) is void: + var ret: Future[void] else: - type A = impl(onSuccess(default(T))) - var ret: A - asm "`ret` = `future`.then(`onSuccess`, `onReject`)" - return ret + var ret = default(maybeFuture(typeof(call))) + typeof(ret) + when T is void: + type A = impl(onSuccess()) + else: + type A = impl(onSuccess(default(T))) + var ret: A + asm "`ret` = `future`.then(`onSuccess`, `onReject`)" + return ret - proc catch*[T](future: Future[T], onReject: OnReject): Future[void] = - ## See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch - runnableExamples("-d:nimExperimentalAsyncjsThen"): - from std/sugar import `=>` - from std/strutils import contains + proc catch*[T](future: Future[T], onReject: OnReject): Future[void] = + ## See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch + runnableExamples("-r:off"): + from std/sugar import `=>` + from std/strutils import contains - proc fn(n: int): Future[int] {.async.} = - if n >= 7: raise newException(ValueError, "foobar: " & $n) - else: result = n * 2 + proc fn(n: int): Future[int] {.async.} = + if n >= 7: raise newException(ValueError, "foobar: " & $n) + else: result = n * 2 - proc main() {.async.} = - var reason: Error - await fn(6).catch((r: Error) => (reason = r)) # note: `()` are needed, `=> reason = r` would not work - assert reason == nil - await fn(7).catch((r: Error) => (reason = r)) - assert reason != nil - assert "foobar: 7" in $reason.message + proc main() {.async.} = + var reason: Error + await fn(6).catch((r: Error) => (reason = r)) # note: `()` are needed, `=> reason = r` would not work + assert reason == nil + await fn(7).catch((r: Error) => (reason = r)) + assert reason != nil + assert "foobar: 7" in $reason.message - discard main() + discard main() - asm "`result` = `future`.catch(`onReject`)" + asm "`result` = `future`.catch(`onReject`)" diff --git a/lib/std/jsfetch.nim b/lib/std/jsfetch.nim index 808d699b0..034bb6332 100644 --- a/lib/std/jsfetch.nim +++ b/lib/std/jsfetch.nim @@ -190,12 +190,11 @@ runnableExamples("-r:off"): discard example() - when defined(nimExperimentalAsyncjsThen): - block: - proc example2 {.async.} = - await fetch("https://api.github.com/users/torvalds".cstring) - .then((response: Response) => response.json()) - .then((json: JsObject) => console.log(json)) - .catch((err: Error) => console.log("Request Failed", err)) + block: + proc example2 {.async.} = + await fetch("https://api.github.com/users/torvalds".cstring) + .then((response: Response) => response.json()) + .then((json: JsObject) => console.log(json)) + .catch((err: Error) => console.log("Request Failed", err)) - discard example2() + discard example2() diff --git a/tools/kochdocs.nim b/tools/kochdocs.nim index 8fd9c84a5..764146cf5 100644 --- a/tools/kochdocs.nim +++ b/tools/kochdocs.nim @@ -11,7 +11,7 @@ const gaCode* = " --doc.googleAnalytics:UA-48159761-1" # errormax: subsequent errors are probably consequences of 1st one; a simple # bug could cause unlimited number of errors otherwise, hard to debug in CI. - docDefines = "-d:nimExperimentalAsyncjsThen -d:nimExperimentalLinenoiseExtra" + docDefines = "-d:nimExperimentalLinenoiseExtra" nimArgs = "--errormax:3 --hint:Conf:off --hint:Path:off --hint:Processing:off --hint:XDeclaredButNotUsed:off --warning:UnusedImport:off -d:boot --putenv:nimversion=$# $#" % [system.NimVersion, docDefines] gitUrl = "https://github.com/nim-lang/Nim" docHtmlOutput = "doc/html" From c2c9fbd7458db7adb515aad30cfe65b98fba47bf Mon Sep 17 00:00:00 2001 From: solarizedalias <60473183+solarizedalias@users.noreply.github.com> Date: Sun, 27 Feb 2022 16:22:52 +0900 Subject: [PATCH 2825/3768] make compileOption("panics") work (#19568) --- compiler/commands.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/commands.nim b/compiler/commands.nim index 6d3f23197..8e1d96860 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -332,6 +332,7 @@ proc testCompileOption*(conf: ConfigRef; switch: string, info: TLineInfo): bool result = contains(conf.options, optTrMacros) of "excessivestacktrace": result = contains(conf.globalOptions, optExcessiveStackTrace) of "nilseqs", "nilchecks", "taintmode": warningOptionNoop(switch) + of "panics": result = contains(conf.globalOptions, optPanics) else: invalidCmdLineOption(conf, passCmd1, switch, info) proc processPath(conf: ConfigRef; path: string, info: TLineInfo, From c870ff5946731dd1deff828abfe07c1900a5b364 Mon Sep 17 00:00:00 2001 From: Tomohiro Date: Sun, 27 Feb 2022 23:36:16 +0900 Subject: [PATCH 2826/3768] Add error message to pointer size check assert in lib/nimbase.h (#19549) --- lib/nimbase.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/nimbase.h b/lib/nimbase.h index a83bd3006..e4dec0dd2 100644 --- a/lib/nimbase.h +++ b/lib/nimbase.h @@ -547,7 +547,7 @@ static inline void GCGuard (void *ptr) { asm volatile ("" :: "X" (ptr)); } #endif // Test to see if Nim and the C compiler agree on the size of a pointer. -NIM_STATIC_ASSERT(sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(NI)*8, ""); +NIM_STATIC_ASSERT(sizeof(NI) == sizeof(void*) && NIM_INTBITS == sizeof(NI)*8, "Pointer size mismatch between Nim and C/C++ backend. You probably need to setup the backend compiler for target CPU."); #ifdef USE_NIM_NAMESPACE } From 207237cec229171eafbd3357dcf128bdd270332a Mon Sep 17 00:00:00 2001 From: Evan Typanski Date: Mon, 28 Feb 2022 07:18:41 -0500 Subject: [PATCH 2827/3768] Fix #18662 (#19534) --- compiler/jsgen.nim | 4 +++- compiler/ropes.nim | 7 ------- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 0a7036b46..f4097efa4 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -2796,7 +2796,9 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = var map: SourceMap (code, map) = genSourceMap($(code), outFile.string) writeFile(outFile.string & ".map", $(%map)) - discard writeRopeIfNotEqual(code, outFile) + if not equalsFile(code, outFile): + if not writeRope(code, outFile): + rawMessage(m.config, errCannotOpenFile, outFile.string) proc myOpen(graph: ModuleGraph; s: PSym; idgen: IdGenerator): PPassContext = diff --git a/compiler/ropes.nim b/compiler/ropes.nim index 1e7957337..cd696a545 100644 --- a/compiler/ropes.nim +++ b/compiler/ropes.nim @@ -329,10 +329,3 @@ proc equalsFile*(r: Rope, filename: AbsoluteFile): bool = if result: result = equalsFile(r, f) close(f) - -proc writeRopeIfNotEqual*(r: Rope, filename: AbsoluteFile): bool = - # returns true if overwritten - if not equalsFile(r, filename): - result = writeRope(r, filename) - else: - result = false From d6d36093b18dfa1e2b8b6325016f1def015250b4 Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 1 Mar 2022 14:46:08 +0800 Subject: [PATCH 2828/3768] apply changes from #18017 and some fixes (#19571) * implements https://github.com/nim-lang/RFCs/issues/369 * deprecate unsafeAddr; extend addr addr is now available for all addressable locations, unsafeAddr is deprecated and become an alias for addr * follow @Vindaar's advice * change the signature of addr * unsafeAddr => addr (stdlib) * Update changelog.md * unsafeAddr => addr (tests) * Revert "unsafeAddr => addr (stdlib)" This reverts commit ab83c99c507048a8396e636bf22d55fdd84d7d1c. * doc changes; thanks to @konsumlamm Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> * merge * remove * fix bug Co-authored-by: Araq Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> --- compiler/parampatterns.nim | 60 ++++++++++++++++++++------------------ compiler/semexprs.nim | 14 +++++---- compiler/semmagic.nim | 6 ++-- lib/system.nim | 3 -- 4 files changed, 42 insertions(+), 41 deletions(-) diff --git a/compiler/parampatterns.nim b/compiler/parampatterns.nim index bfbc72675..cceb236ae 100644 --- a/compiler/parampatterns.nim +++ b/compiler/parampatterns.nim @@ -179,6 +179,7 @@ type arLocalLValue, # is an l-value, but local var; must not escape # its stack frame! arDiscriminant, # is a discriminant + arAddressableConst, # an addressable const arLentValue, # lent value arStrange # it is a strange beast like 'typedesc[var T]' @@ -212,7 +213,7 @@ proc exprRoot*(n: PNode): PSym = else: break -proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult = +proc isAssignable*(owner: PSym, n: PNode): TAssignableResult = ## 'owner' can be nil! result = arNone case n.kind @@ -220,20 +221,20 @@ proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult if n.typ != nil and n.typ.kind in {tyVar}: result = arLValue of nkSym: - let kinds = if isUnsafeAddr: {skVar, skResult, skTemp, skParam, skLet, skForVar} - else: {skVar, skResult, skTemp} - if n.sym.kind == skParam and n.sym.typ.kind in {tyVar, tySink}: - result = arLValue - elif isUnsafeAddr and n.sym.kind == skParam: - result = arLValue - elif isUnsafeAddr and n.sym.kind == skConst and dontInlineConstant(n, n.sym.ast): - result = arLValue + const kinds = {skVar, skResult, skTemp, skParam, skLet, skForVar} + if n.sym.kind == skParam: + result = if n.sym.typ.kind in {tyVar, tySink}: arLValue else: arAddressableConst + elif n.sym.kind == skConst and dontInlineConstant(n, n.sym.ast): + result = arAddressableConst elif n.sym.kind in kinds: - if owner != nil and owner == n.sym.owner and - sfGlobal notin n.sym.flags: - result = arLocalLValue + if n.sym.kind in {skParam, skLet, skForVar}: + result = arAddressableConst else: - result = arLValue + if owner != nil and owner == n.sym.owner and + sfGlobal notin n.sym.flags: + result = arLocalLValue + else: + result = arLValue elif n.sym.kind == skType: let t = n.sym.typ.skipTypes({tyTypeDesc}) if t.kind in {tyVar}: result = arStrange @@ -241,10 +242,10 @@ proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult let t = skipTypes(n[0].typ, abstractInst-{tyTypeDesc}) if t.kind in {tyVar, tySink, tyPtr, tyRef}: result = arLValue - elif isUnsafeAddr and t.kind == tyLent: - result = arLValue + elif t.kind == tyLent: + result = arAddressableConst else: - result = isAssignable(owner, n[0], isUnsafeAddr) + result = isAssignable(owner, n[0]) if result != arNone and n[1].kind == nkSym and sfDiscriminant in n[1].sym.flags: result = arDiscriminant @@ -252,23 +253,23 @@ proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult let t = skipTypes(n[0].typ, abstractInst-{tyTypeDesc}) if t.kind in {tyVar, tySink, tyPtr, tyRef}: result = arLValue - elif isUnsafeAddr and t.kind == tyLent: - result = arLValue + elif t.kind == tyLent: + result = arAddressableConst else: - result = isAssignable(owner, n[0], isUnsafeAddr) + result = isAssignable(owner, n[0]) of nkHiddenStdConv, nkHiddenSubConv, nkConv: # Object and tuple conversions are still addressable, so we skip them # XXX why is 'tyOpenArray' allowed here? if skipTypes(n.typ, abstractPtrs-{tyTypeDesc}).kind in {tyOpenArray, tyTuple, tyObject}: - result = isAssignable(owner, n[1], isUnsafeAddr) + result = isAssignable(owner, n[1]) elif compareTypes(n.typ, n[1].typ, dcEqIgnoreDistinct): # types that are equal modulo distinction preserve l-value: - result = isAssignable(owner, n[1], isUnsafeAddr) + result = isAssignable(owner, n[1]) of nkHiddenDeref: let n0 = n[0] if n0.typ.kind == tyLent: - if isUnsafeAddr or (n0.kind == nkSym and n0.sym.kind == skResult): + if n0.kind == nkSym and n0.sym.kind == skResult: result = arLValue else: result = arLentValue @@ -277,18 +278,19 @@ proc isAssignable*(owner: PSym, n: PNode; isUnsafeAddr=false): TAssignableResult of nkDerefExpr, nkHiddenAddr: result = arLValue of nkObjUpConv, nkObjDownConv, nkCheckedFieldExpr: - result = isAssignable(owner, n[0], isUnsafeAddr) + result = isAssignable(owner, n[0]) of nkCallKinds: # builtin slice keeps lvalue-ness: if getMagic(n) in {mArrGet, mSlice}: - result = isAssignable(owner, n[1], isUnsafeAddr) - elif n.typ != nil and n.typ.kind in {tyVar}: - result = arLValue - elif isUnsafeAddr and n.typ != nil and n.typ.kind == tyLent: - result = arLValue + result = isAssignable(owner, n[1]) + elif n.typ != nil: + case n.typ.kind + of tyVar: result = arLValue + of tyLent: result = arLentValue + else: discard of nkStmtList, nkStmtListExpr: if n.typ != nil: - result = isAssignable(owner, n.lastSon, isUnsafeAddr) + result = isAssignable(owner, n.lastSon) of nkVarTy: # XXX: The fact that this is here is a bit of a hack. # The goal is to allow the use of checks such as "foo(var T)" diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index c593efe55..5f3d065b6 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -650,8 +650,8 @@ proc fixAbstractType(c: PContext, n: PNode) = changeType(c, it[1], s, check=true) n[i] = it[1] -proc isAssignable(c: PContext, n: PNode; isUnsafeAddr=false): TAssignableResult = - result = parampatterns.isAssignable(c.p.owner, n, isUnsafeAddr) +proc isAssignable(c: PContext, n: PNode): TAssignableResult = + result = parampatterns.isAssignable(c.p.owner, n) proc isUnresolvedSym(s: PSym): bool = result = s.kind == skGenericParam @@ -1658,9 +1658,11 @@ proc takeImplicitAddr(c: PContext, n: PNode; isLent: bool): PNode = # `proc fun(a: var int): var int = a` discard else: discard - let valid = isAssignable(c, n, isLent) + let valid = isAssignable(c, n) if valid != arLValue: - if valid == arLocalLValue: + if valid in {arAddressableConst, arLentValue} and isLent: + discard "ok" + elif valid == arLocalLValue: localError(c.config, n.info, errXStackEscape % renderTree(n, {renderNoComments})) else: localError(c.config, n.info, errExprHasNoAddress) @@ -1784,7 +1786,7 @@ proc semAsgn(c: PContext, n: PNode; mode=asgnNormal): PNode = if le == nil: localError(c.config, a.info, "expression has no type") elif (skipTypes(le, {tyGenericInst, tyAlias, tySink}).kind notin {tyVar} and - isAssignable(c, a) in {arNone, arLentValue}) or ( + isAssignable(c, a) in {arNone, arLentValue, arAddressableConst}) or ( skipTypes(le, abstractVar).kind in {tyOpenArray, tyVarargs} and views notin c.features): # Direct assignment to a discriminant is allowed! localError(c.config, a.info, errXCannotBeAssignedTo % @@ -2267,7 +2269,7 @@ proc semMagic(c: PContext, n: PNode, s: PSym, flags: TExprFlags): PNode = markUsed(c, n.info, s) checkSonsLen(n, 2, c.config) result[0] = newSymNode(s, n[0].info) - result[1] = semAddrArg(c, n[1], s.name.s == "unsafeAddr") + result[1] = semAddrArg(c, n[1]) result.typ = makePtrType(c, result[1].typ) of mTypeOf: markUsed(c, n.info, s) diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index c7fc75620..ed1826fd4 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -10,11 +10,11 @@ # This include file implements the semantic checking for magics. # included from sem.nim -proc semAddrArg(c: PContext; n: PNode; isUnsafeAddr = false): PNode = +proc semAddrArg(c: PContext; n: PNode): PNode = let x = semExprWithType(c, n) if x.kind == nkSym: x.sym.flags.incl(sfAddrTaken) - if isAssignable(c, x, true) notin {arLValue, arLocalLValue}: + if isAssignable(c, x) notin {arLValue, arLocalLValue, arAddressableConst, arLentValue}: localError(c.config, n.info, errExprHasNoAddress) result = x @@ -466,7 +466,7 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, of mAddr: checkSonsLen(n, 2, c.config) result = n - result[1] = semAddrArg(c, n[1], n[0].sym.name.s == "unsafeAddr") + result[1] = semAddrArg(c, n[1]) result.typ = makePtrType(c, result[1].typ) of mTypeOf: result = semTypeOf(c, n) diff --git a/lib/system.nim b/lib/system.nim index 56a6e2887..572768de2 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -202,9 +202,6 @@ proc `addr`*[T](x: T): ptr T {.magic: "Addr", noSideEffect.} = ## ## Cannot be overloaded. ## - ## See also: - ## * `unsafeAddr <#unsafeAddr,T>`_ - ## ## .. code-block:: Nim ## var ## buf: seq[char] = @['a','b','c'] From 590d39785243afb6da3662cf77c842a5ff4e1412 Mon Sep 17 00:00:00 2001 From: huantian Date: Wed, 2 Mar 2022 05:53:59 +0000 Subject: [PATCH 2829/3768] Fix typo in Nim Tutorial pt3 (#19579) --- doc/tut3.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/tut3.rst b/doc/tut3.rst index 6b5a3b5ca..1c497f1f5 100644 --- a/doc/tut3.rst +++ b/doc/tut3.rst @@ -244,7 +244,7 @@ Building Your First Macro ------------------------- To give a starting point to writing macros we will show now how to -implement the `myDebug` macro mentioned earlier. The first thing to +implement the `myAssert` macro mentioned earlier. The first thing to do is to build a simple example of the macro usage, and then just print the argument. This way it is possible to get an idea of what a correct argument should look like. From 488e18f90d342b5a3b528c0c84891b31b7d1e6ca Mon Sep 17 00:00:00 2001 From: VlkrS <47375452+VlkrS@users.noreply.github.com> Date: Thu, 3 Mar 2022 14:41:35 +0100 Subject: [PATCH 2830/3768] Fix CPU detection for i386 (#19577) * Fix CPU detection for i386 Commit 787def271b1cabc6f898caa42f892125de9fa908 breaks CPU detection for i386 on OpenBSD and probably on other platforms. [ isOpenIndiana -eq "yes" ] always returns 0, so mycpu is always set to "amd64". * Update buildsh.nimf Fix accidental bashism --- tools/niminst/buildsh.nimf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/niminst/buildsh.nimf b/tools/niminst/buildsh.nimf index 5f1db9cd3..7a05ef342 100644 --- a/tools/niminst/buildsh.nimf +++ b/tools/niminst/buildsh.nimf @@ -164,7 +164,7 @@ esac case $ucpu in *i386* | *i486* | *i586* | *i686* | *bepc* | *i86pc* ) - if [ isOpenIndiana -eq "yes" ] ; then + if [ "$isOpenIndiana" = "yes" ] ; then mycpu="amd64" else mycpu="i386" From 645447293851749fcc3394cd387d7070d8a9c735 Mon Sep 17 00:00:00 2001 From: Hamid Bluri Date: Mon, 7 Mar 2022 12:51:42 +0330 Subject: [PATCH 2831/3768] fix not flushing stdout in MSYS (#19590) I did this pull request according to what xflywind said: https://github.com/nim-lang/Nim/pull/19584#issuecomment-1060085141 --- lib/impure/rdstdin.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/impure/rdstdin.nim b/lib/impure/rdstdin.nim index 32048b131..adc0e212d 100644 --- a/lib/impure/rdstdin.nim +++ b/lib/impure/rdstdin.nim @@ -27,6 +27,7 @@ when defined(windows): tags: [ReadIOEffect, WriteIOEffect].} = ## Reads a line from stdin. stdout.write(prompt) + stdout.flushFile() result = readLine(stdin) proc readLineFromStdin*(prompt: string, line: var string): bool {. From 8f9dd5b3492293e4e48647206c8858b58cd51666 Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 9 Mar 2022 01:12:31 +0800 Subject: [PATCH 2832/3768] register callback for marshal in VM (#19578) * register callback for marshal in VM * remove unrelated code * polish * more tests * more tests * add loadVM and toVM --- compiler/vm.nim | 17 +++------------- compiler/vmdef.nim | 5 ++--- compiler/vmgen.nim | 27 ------------------------- compiler/vmops.nim | 36 ++++++++++++++++++++++++++++++++- lib/pure/marshal.nim | 28 ++++++++++++++++++-------- tests/stdlib/tmarshal.nim | 42 +++++++++++++++++++++++++++++++++------ 6 files changed, 96 insertions(+), 59 deletions(-) diff --git a/compiler/vm.nim b/compiler/vm.nim index 259648add..fa1c71c85 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -15,7 +15,7 @@ import std/[strutils, tables, parseutils], msgs, vmdef, vmgen, nimsets, types, passes, parser, vmdeps, idents, trees, renderer, options, transf, - vmmarshal, gorgeimpl, lineinfos, btrees, macrocacheimpl, + gorgeimpl, lineinfos, btrees, macrocacheimpl, modulegraphs, sighashes, int128, vmprofiler import ast except getstr @@ -1224,7 +1224,8 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = c.callbacks[-prc.offset-2].value( VmArgs(ra: ra, rb: rb, rc: rc, slots: cast[ptr UncheckedArray[TFullReg]](addr regs[0]), currentException: c.currentExceptionA, - currentLineInfo: c.debug[pc])) + currentLineInfo: c.debug[pc]) + ) elif importcCond(c, prc): if compiletimeFFI notin c.config.features: globalError(c.config, c.debug[pc], "VM not allowed to do FFI, see `compiletimeFFI`") @@ -2100,18 +2101,6 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = while typ.kind == tyTypeDesc and typ.len > 0: typ = typ[0] createStr regs[ra] regs[ra].node.strVal = typ.typeToString(preferExported) - of opcMarshalLoad: - let ra = instr.regA - let rb = instr.regB - inc pc - let typ = c.types[c.code[pc].regBx - wordExcess] - putIntoReg(regs[ra], loadAny(regs[rb].node.strVal, typ, c.cache, c.config, c.idgen)) - of opcMarshalStore: - decodeB(rkNode) - inc pc - let typ = c.types[c.code[pc].regBx - wordExcess] - createStrKeepNode(regs[ra]) - storeAny(regs[ra].node.strVal, typ, regs[rb].regToNode, c.config) c.profiler.leave(c) diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index 2044b860a..ecdbeff89 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -180,7 +180,6 @@ type opcNBindSym, opcNDynBindSym, opcSetType, # dest.typ = types[Bx] opcTypeTrait, - opcMarshalLoad, opcMarshalStore, opcSymOwner, opcSymIsInstantiationOf @@ -307,8 +306,8 @@ proc registerCallback*(c: PCtx; name: string; callback: VmCallback): int {.disca const firstABxInstr* = opcTJmp largeInstrs* = { # instructions which use 2 int32s instead of 1: - opcSubStr, opcConv, opcCast, opcNewSeq, opcOf, - opcMarshalLoad, opcMarshalStore} + opcSubStr, opcConv, opcCast, opcNewSeq, opcOf + } slotSomeTemp* = slotTempUnknown relativeJumps* = {opcTJmp, opcFJmp, opcJmp, opcJmpBack} diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index c0c4bac19..1551fbb80 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -99,11 +99,6 @@ proc codeListing(c: PCtx, result: var string, start=0; last = -1) = let idx = x.regBx-wordExcess result.addf("\t$#\tr$#, $# ($#)", opc.toStr, x.regA, c.constants[idx].renderTree, $idx) - elif opc in {opcMarshalLoad, opcMarshalStore}: - let y = c.code[i+1] - result.addf("\t$#\tr$#, r$#, $#", opc.toStr, x.regA, x.regB, - c.types[y.regBx-wordExcess].typeToString) - inc i else: result.addf("\t$#\tr$#, $#", opc.toStr, x.regA, x.regBx-wordExcess) result.add("\t# ") @@ -1383,22 +1378,6 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = # mGCref, mGCunref, globalError(c.config, n.info, "cannot generate code for: " & $m) -proc genMarshalLoad(c: PCtx, n: PNode, dest: var TDest) = - ## Signature: proc to*[T](data: string): T - if dest < 0: dest = c.getTemp(n.typ) - var tmp = c.genx(n[1]) - c.gABC(n, opcMarshalLoad, dest, tmp) - c.gABx(n, opcMarshalLoad, 0, c.genType(n.typ)) - c.freeTemp(tmp) - -proc genMarshalStore(c: PCtx, n: PNode, dest: var TDest) = - ## Signature: proc `$$`*[T](x: T): string - if dest < 0: dest = c.getTemp(n.typ) - var tmp = c.genx(n[1]) - c.gABC(n, opcMarshalStore, dest, tmp) - c.gABx(n, opcMarshalStore, 0, c.genType(n[1].typ)) - c.freeTemp(tmp) - proc unneededIndirection(n: PNode): bool = n.typ.skipTypes(abstractInstOwned-{tyTypeDesc}).kind == tyRef @@ -2054,12 +2033,6 @@ proc gen(c: PCtx; n: PNode; dest: var TDest; flags: TGenFlags = {}) = elif s.kind == skMethod: localError(c.config, n.info, "cannot call method " & s.name.s & " at compile time") - elif matches(s, "stdlib.marshal.to"): - # XXX marshal load&store should not be opcodes, but use the - # general callback mechanisms. - genMarshalLoad(c, n, dest) - elif matches(s, "stdlib.marshal.$$"): - genMarshalStore(c, n, dest) else: genCall(c, n, dest) clearDest(c, n, dest) diff --git a/compiler/vmops.nim b/compiler/vmops.nim index 6b06cc68d..f13e4b1cc 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -32,7 +32,7 @@ from system/formatfloat import addFloatRoundtrip, addFloatSprintf # There are some useful procs in vmconv. -import vmconv +import vmconv, vmmarshal template mathop(op) {.dirty.} = registerCallback(c, "stdlib.math." & astToStr(op), `op Wrapper`) @@ -152,6 +152,7 @@ when defined(nimHasInvariant): proc stackTrace2(c: PCtx, msg: string, n: PNode) = stackTrace(c, PStackFrame(prc: c.prc.sym, comesFrom: 0, next: nil), c.exceptionInstr, msg, n.info) + proc registerAdditionalOps*(c: PCtx) = template wrapIterator(fqname: string, iter: untyped) = @@ -344,3 +345,36 @@ proc registerAdditionalOps*(c: PCtx) = addFloatSprintf(p.strVal, x) wrapIterator("stdlib.os.envPairsImplSeq"): envPairs() + + registerCallback c, "stdlib.marshal.toVM", proc(a: VmArgs) = + let typ = a.getNode(0).typ + case typ.kind + of tyInt..tyInt64, tyUInt..tyUInt64: + setResult(a, loadAny(a.getString(1), typ, c.cache, c.config, c.idgen).intVal) + of tyFloat..tyFloat128: + setResult(a, loadAny(a.getString(1), typ, c.cache, c.config, c.idgen).floatVal) + else: + setResult(a, loadAny(a.getString(1), typ, c.cache, c.config, c.idgen)) + + registerCallback c, "stdlib.marshal.loadVM", proc(a: VmArgs) = + let typ = a.getNode(0).typ + let p = a.getReg(1) + var res: string + + var node: PNode + case p.kind + of rkNone: + node = newNode(nkEmpty) + of rkInt: + node = newIntNode(nkIntLit, p.intVal) + of rkFloat: + node = newFloatNode(nkFloatLit, p.floatVal) + of rkNode: + node = p.node + of rkRegisterAddr: + node = p.regAddr.node + of rkNodeAddr: + node = p.nodeAddr[] + + storeAny(res, typ, node, c.config) + setResult(a, res) diff --git a/lib/pure/marshal.nim b/lib/pure/marshal.nim index 993e0f510..452af54d5 100644 --- a/lib/pure/marshal.nim +++ b/lib/pure/marshal.nim @@ -298,6 +298,9 @@ proc store*[T](s: Stream, data: T) = shallowCopy(d, data) storeAny(s, toAny(d), stored) +proc loadVM[T](typ: typedesc[T], x: T): string = + discard "the implementation is in the compiler/vmops" + proc `$$`*[T](x: T): string = ## Returns a string representation of `x` (serialization, marshalling). ## @@ -313,12 +316,18 @@ proc `$$`*[T](x: T): string = let y = $$x assert y == """{"id": 1, "bar": "baz"}""" - var stored = initIntSet() - var d: T - shallowCopy(d, x) - var s = newStringStream() - storeAny(s, toAny(d), stored) - result = s.data + when nimvm: + result = loadVM(T, x) + else: + var stored = initIntSet() + var d: T + shallowCopy(d, x) + var s = newStringStream() + storeAny(s, toAny(d), stored) + result = s.data + +proc toVM[T](typ: typedesc[T], data: string): T = + discard "the implementation is in the compiler/vmops" proc to*[T](data: string): T = ## Reads data and transforms it to a type `T` (deserialization, unmarshalling). @@ -335,5 +344,8 @@ proc to*[T](data: string): T = assert z.id == 1 assert z.bar == "baz" - var tab = initTable[BiggestInt, pointer]() - loadAny(newStringStream(data), toAny(result), tab) + when nimvm: + result = toVM(T, data) + else: + var tab = initTable[BiggestInt, pointer]() + loadAny(newStringStream(data), toAny(result), tab) diff --git a/tests/stdlib/tmarshal.nim b/tests/stdlib/tmarshal.nim index 508205c3a..e539b1c34 100644 --- a/tests/stdlib/tmarshal.nim +++ b/tests/stdlib/tmarshal.nim @@ -4,12 +4,16 @@ import std/marshal proc testit[T](x: T): string = $$to[T]($$x) -let test1: array[0..1, array[0..4, string]] = [ - ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"]] -doAssert testit(test1) == - """[["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"]]""" -let test2: tuple[name: string, s: int] = ("tuple test", 56) -doAssert testit(test2) == """{"Field0": "tuple test", "Field1": 56}""" +template check1 = + let test1: array[0..1, array[0..4, string]] = [ + ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"]] + doAssert testit(test1) == + """[["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"]]""" + let test2: tuple[name: string, s: int] = ("tuple test", 56) + doAssert testit(test2) == """{"Field0": "tuple test", "Field1": 56}""" + +static: check1() +check1() type TE = enum @@ -146,3 +150,29 @@ block: let a: ref A = new(B) doAssert $$a[] == "{}" # not "{f: 0}" + +template checkMarshal(data: typed) = + let orig = data + let m = $$orig + + let old = to[typeof(orig)](m) + doAssert data == old + +template main() = + type + Book = object + page: int + name: string + + let book = Book(page: 12, name: "persona") + + checkMarshal(486) + checkMarshal(3.14) + checkMarshal("azure sky") + checkMarshal(book) + checkMarshal([1, 2, 3]) + checkMarshal(@[1.5, 2.7, 3.9, 4.2]) + checkMarshal(@["dream", "is", "possible"]) + +static: main() +main() From 0d6795a771e46b07244f3a3f43502602acbebdc3 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 9 Mar 2022 07:22:10 +0100 Subject: [PATCH 2833/3768] fixes #19569 (#19595) [backport] * minor code refactorings * fixes #19569 --- compiler/cgen.nim | 2 +- compiler/sizealignoffsetimpl.nim | 14 ++++++-------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index b78ad10c4..2865473fb 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -881,7 +881,7 @@ proc containsResult(n: PNode): bool = if containsResult(n[i]): return true const harmless = {nkConstSection, nkTypeSection, nkEmpty, nkCommentStmt, nkTemplateDef, - nkMacroDef, nkMixinStmt, nkBindStmt} + + nkMacroDef, nkMixinStmt, nkBindStmt, nkFormalParams} + declarativeDefs proc easyResultAsgn(n: PNode): PNode = diff --git a/compiler/sizealignoffsetimpl.nim b/compiler/sizealignoffsetimpl.nim index c5cd3ba64..c2e97aa53 100644 --- a/compiler/sizealignoffsetimpl.nim +++ b/compiler/sizealignoffsetimpl.nim @@ -40,15 +40,15 @@ proc inc(arg: var OffsetAccum; value: int) = else: arg.offset += value -proc alignmentMax(a,b: int): int = +proc alignmentMax(a, b: int): int = if unlikely(a == szIllegalRecursion or b == szIllegalRecursion): raiseIllegalTypeRecursion() if a == szUnknownSize or b == szUnknownSize: szUnknownSize else: - max(a,b) + max(a, b) proc align(arg: var OffsetAccum; value: int) = - if unlikely(value == szIllegalRecursion): raiseIllegalTypeRecursion() + if unlikely(value == szIllegalRecursion): raiseIllegalTypeRecursion() if value == szUnknownSize or arg.maxAlign == szUnknownSize or arg.offset == szUnknownSize: arg.maxAlign = szUnknownSize arg.offset = szUnknownSize @@ -112,7 +112,7 @@ proc setOffsetsToUnknown(n: PNode) = for i in 0.. Date: Wed, 9 Mar 2022 11:42:09 +0100 Subject: [PATCH 2834/3768] fixes #19575 (#19596) [backport] * fixes #19575 * better bugfix --- compiler/closureiters.nim | 6 ++++-- tests/iter/titer_issues.nim | 23 +++++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/compiler/closureiters.nim b/compiler/closureiters.nim index a8da7485e..4d9aeb3f7 100644 --- a/compiler/closureiters.nim +++ b/compiler/closureiters.nim @@ -1351,13 +1351,15 @@ proc preprocess(c: var PreprocessContext; n: PNode): PNode = case n.kind of nkTryStmt: let f = n.lastSon + var didAddSomething = false if f.kind == nkFinally: c.finallys.add f.lastSon + didAddSomething = true for i in 0 ..< n.len: result[i] = preprocess(c, n[i]) - if f.kind == nkFinally: + if didAddSomething: discard c.finallys.pop() of nkWhileStmt, nkBlockStmt: @@ -1384,7 +1386,7 @@ proc preprocess(c: var PreprocessContext; n: PNode): PNode = result = newNodeI(nkStmtList, n.info) for i in countdown(c.finallys.high, fin): var vars = FreshVarsContext(tab: initTable[int, PSym](), config: c.config, info: n.info, idgen: c.idgen) - result.add freshVars(preprocess(c, c.finallys[i]), vars) + result.add freshVars(copyTree(c.finallys[i]), vars) c.idgen = vars.idgen result.add n of nkSkip: discard diff --git a/tests/iter/titer_issues.nim b/tests/iter/titer_issues.nim index 65f66ad26..15fe867c8 100644 --- a/tests/iter/titer_issues.nim +++ b/tests/iter/titer_issues.nim @@ -251,3 +251,26 @@ block: for x in ff(@[1, 2], @[1, 2, 3]): echo x + + +# bug #19575 + +iterator bb() {.closure.} = + while true: + try: discard + except: break + finally: break + +var a = bb + +iterator cc() {.closure.} = + while true: + try: discard + except: + if true: + break + finally: + if true: + break + +var a2 = cc From 6773ffa63d0b3ab8b8894e84ed417f4eaced9122 Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 9 Mar 2022 19:55:20 +0800 Subject: [PATCH 2835/3768] enable nimPreviewDotLikeOps (#19598) --- config/config.nims | 1 + tests/config.nims | 1 - tests/stdlib/texperimental.nim | 1 + 3 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 tests/stdlib/texperimental.nim diff --git a/config/config.nims b/config/config.nims index 88c344642..08d9d9f55 100644 --- a/config/config.nims +++ b/config/config.nims @@ -21,3 +21,4 @@ when defined(nimStrictMode): # future work: XDeclaredButNotUsed switch("define", "nimVersion:" & NimVersion) +switch("define", "nimPreviewDotLikeOps") diff --git a/tests/config.nims b/tests/config.nims index 894c4bea0..8c4305521 100644 --- a/tests/config.nims +++ b/tests/config.nims @@ -37,6 +37,5 @@ switch("define", "nimExperimentalLinenoiseExtra") # preview APIs are expected to be the new default in upcoming versions switch("define", "nimPreviewFloatRoundtrip") -switch("define", "nimPreviewDotLikeOps") switch("define", "nimPreviewJsonutilsHoleyEnum") switch("define", "nimPreviewHashRef") diff --git a/tests/stdlib/texperimental.nim b/tests/stdlib/texperimental.nim new file mode 100644 index 000000000..ba8c4eb80 --- /dev/null +++ b/tests/stdlib/texperimental.nim @@ -0,0 +1 @@ +doAssert defined(nimPreviewDotLikeOps) \ No newline at end of file From 4c76f9f1523a72f560138709642c2d51ea365b85 Mon Sep 17 00:00:00 2001 From: flywind Date: Fri, 11 Mar 2022 00:01:27 +0800 Subject: [PATCH 2836/3768] support useVersion:1.6 (#19601) --- compiler/commands.nim | 4 +++ compiler/options.nim | 1 + doc/advopt.txt | 2 +- doc/manual.rst | 5 ---- tests/system/tuse_version.nim | 5 ++-- tests/system/tuse_version16.nim | 49 +++++++++++++++++++++++++++++++++ 6 files changed, 57 insertions(+), 9 deletions(-) create mode 100644 tests/system/tuse_version16.nim diff --git a/compiler/commands.nim b/compiler/commands.nim index 8e1d96860..b21b48801 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -1043,6 +1043,10 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; defineSymbol(conf.symbols, "NimMajor", "1") defineSymbol(conf.symbols, "NimMinor", "2") conf.globalOptions.incl optNimV12Emulation + of "1.6": + defineSymbol(conf.symbols, "NimMajor", "1") + defineSymbol(conf.symbols, "NimMinor", "6") + conf.globalOptions.incl optNimV16Emulation else: localError(conf, info, "unknown Nim version; currently supported values are: `1.0`, `1.2`") # always be compatible with 1.x.100: diff --git a/compiler/options.nim b/compiler/options.nim index 5cfe58119..9c0c9df72 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -96,6 +96,7 @@ type # please make sure we have under 32 options optPanics # turn panics (sysFatal) into a process termination optNimV1Emulation # emulate Nim v1.0 optNimV12Emulation # emulate Nim v1.2 + optNimV16Emulation # emulate Nim v1.6 optSourcemap optProfileVM # enable VM profiler optEnableDeepCopy # ORC specific: enable 'deepcopy' for all types. diff --git a/doc/advopt.txt b/doc/advopt.txt index e27c75ada..0a2349597 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -161,7 +161,7 @@ Advanced options: enable experimental language feature --legacy:$2 enable obsolete/legacy language feature - --useVersion:1.0|1.2 emulate Nim version X of the Nim compiler, for testing + --useVersion:1.0|1.2|1.6 emulate Nim version X of the Nim compiler, for testing --benchmarkVM:on|off turn benchmarking of VM code with cpuTime() on|off --profileVM:on|off turn compile time VM profiler on|off --sinkInference:on|off turn sink parameter inference on|off (default: on) diff --git a/doc/manual.rst b/doc/manual.rst index e45133c38..4c4b9c506 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -5797,11 +5797,6 @@ However, this means that the method call syntax is not available for tmp(12) -**Note**: The Nim compiler prior to version 1 was more lenient about this -requirement. Use the `--useVersion:0.19`:option: switch for a transition period. - - - Limitations of the method call syntax ------------------------------------- diff --git a/tests/system/tuse_version.nim b/tests/system/tuse_version.nim index 36dac46ec..116ef3596 100644 --- a/tests/system/tuse_version.nim +++ b/tests/system/tuse_version.nim @@ -1,6 +1,5 @@ discard """ - cmd: "nim c --useVersion:1.0 -r $file" - output: "1.0.100" + matrix: "--useVersion:1.0" """ {.warning[UnusedImport]: off.} @@ -47,4 +46,4 @@ import std/[ ] -echo NimVersion +doAssert NimVersion == "1.0.100" diff --git a/tests/system/tuse_version16.nim b/tests/system/tuse_version16.nim new file mode 100644 index 000000000..d5430db32 --- /dev/null +++ b/tests/system/tuse_version16.nim @@ -0,0 +1,49 @@ +discard """ + matrix: "--useVersion:1.6" +""" + +{.warning[UnusedImport]: off.} + +import std/[ + # Core: + bitops, typetraits, lenientops, macros, volatile, + + # Algorithms: + algorithm, sequtils, + + # Collections: + critbits, deques, heapqueue, intsets, lists, options, sets, + sharedlist, tables, + + # Strings: + editdistance, wordwrap, parseutils, ropes, + pegs, punycode, strformat, strmisc, strscans, strtabs, + strutils, unicode, unidecode, + + # Generic operator system services: + os, streams, + + # Math libraries: + complex, math, mersenne, random, rationals, stats, sums, + + # Internet protocols: + httpcore, mimetypes, uri, + + # Parsers: + htmlparser, json, lexbase, parsecfg, parsecsv, parsesql, parsexml, + + # XML processing: + xmltree, xmlparser, + + # Generators: + htmlgen, + + # Hashing: + base64, hashes, + + # Miscellaneous: + colors, sugar, varints, +] + + +doAssert NimVersion == "1.6.100" From 7a50d663467f0e64ea8390e8c86269b25f312816 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Fri, 18 Mar 2022 03:54:20 -0300 Subject: [PATCH 2837/3768] Removed deprecated httpcore func (#19550) * Remove Deprecated httpcore func * Remove Deprecated httpcore func * Fix a test with Deprecated func * Restart CI, Apple can code shit anymore I tell you --- changelog.md | 1 + lib/pure/httpcore.nim | 10 ---------- tests/stdlib/tasynchttpserver.nim | 8 ++++---- 3 files changed, 5 insertions(+), 14 deletions(-) diff --git a/changelog.md b/changelog.md index 011f95ddb..a9bf3842a 100644 --- a/changelog.md +++ b/changelog.md @@ -45,6 +45,7 @@ becomes an alias for `addr`. - Changed mimedb to use an `OrderedTable` instead of `OrderedTableRef`, to use it in a const. - Removed deprecated `jsre.test` and `jsre.toString`. - Removed deprecated `math.c_frexp`. +- Removed deprecated ``` httpcore.`==` ```. ## Language changes diff --git a/lib/pure/httpcore.nim b/lib/pure/httpcore.nim index fdd5926f7..7e995ac46 100644 --- a/lib/pure/httpcore.nim +++ b/lib/pure/httpcore.nim @@ -347,16 +347,6 @@ func `$`*(code: HttpCode): string = func `==`*(a, b: HttpCode): bool {.borrow.} -proc `==`*(rawCode: string, code: HttpCode): bool - {.deprecated: "Deprecated since v1.2; use rawCode == $code instead".} = - ## Compare the string form of the status code with a HttpCode - ## - ## **Note**: According to HTTP/1.1 specification, the reason phrase is - ## optional and should be ignored by the client, making this - ## proc only suitable for comparing the `HttpCode` against the - ## string form of itself. - return cmpIgnoreCase(rawCode, $code) == 0 - func is1xx*(code: HttpCode): bool {.inline, since: (1, 5).} = ## Determines whether `code` is a 1xx HTTP status code. runnableExamples: diff --git a/tests/stdlib/tasynchttpserver.nim b/tests/stdlib/tasynchttpserver.nim index 77ad7a071..d4176d55a 100644 --- a/tests/stdlib/tasynchttpserver.nim +++ b/tests/stdlib/tasynchttpserver.nim @@ -39,7 +39,7 @@ proc test200() {.async.} = return clientResponse proc test(response: AsyncResponse, body: string) {.async.} = - doAssert(response.status == Http200) + doAssert(response.status == $Http200) doAssert(body == "Hello World, 200") doAssert(response.headers.hasKey("Content-Length")) doAssert(response.headers["Content-Length"] == "16") @@ -60,7 +60,7 @@ proc test404() {.async.} = return clientResponse proc test(response: AsyncResponse, body: string) {.async.} = - doAssert(response.status == Http404) + doAssert(response.status == $Http404) doAssert(body == "Hello World, 404") doAssert(response.headers.hasKey("Content-Length")) doAssert(response.headers["Content-Length"] == "16") @@ -81,7 +81,7 @@ proc testCustomEmptyHeaders() {.async.} = return clientResponse proc test(response: AsyncResponse, body: string) {.async.} = - doAssert(response.status == Http200) + doAssert(response.status == $Http200) doAssert(body == "Hello World, 200") doAssert(response.headers.hasKey("Content-Length")) doAssert(response.headers["Content-Length"] == "16") @@ -104,7 +104,7 @@ proc testCustomContentLength() {.async.} = return clientResponse proc test(response: AsyncResponse, body: string) {.async.} = - doAssert(response.status == Http200) + doAssert(response.status == $Http200) doAssert(body == "") doAssert(response.headers.hasKey("Content-Length")) doAssert(response.headers["Content-Length"] == "0") From 7b811deeffc762f8370b4cf7e8a353fd516d6543 Mon Sep 17 00:00:00 2001 From: Araq Date: Fri, 18 Mar 2022 14:17:54 +0100 Subject: [PATCH 2838/3768] db_sqlite: added a space --- lib/impure/db_sqlite.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/impure/db_sqlite.nim b/lib/impure/db_sqlite.nim index b600576df..1638d38c6 100644 --- a/lib/impure/db_sqlite.nim +++ b/lib/impure/db_sqlite.nim @@ -700,7 +700,7 @@ proc insert*(db: DbConn, query: SqlQuery, pkName: string, args: varargs[string, `$`]): int64 {.tags: [WriteDbEffect], since: (1, 3).} = ## same as insertId - result = tryInsert(db, query,pkName, args) + result = tryInsert(db, query, pkName, args) if result < 0: dbError(db) proc execAffectedRows*(db: DbConn, query: SqlQuery, From 3e83d73f272afe8de85189da7d6d513916cc2efd Mon Sep 17 00:00:00 2001 From: Clyybber Date: Fri, 18 Mar 2022 16:41:45 +0100 Subject: [PATCH 2839/3768] compiler: Handle nkCheckedFieldExpr better in dfa (#19616) Simply recurse into their first child, which is always a nkDotExpr instead of treating them seperately. This fixes the rhs sym of a nkCheckedFieldExpr being checked twice in aliases. This double checking didn't cause any issues, but was unintentional and redundant. --- compiler/dfa.nim | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/compiler/dfa.nim b/compiler/dfa.nim index 0539f6699..d68e7be82 100644 --- a/compiler/dfa.nim +++ b/compiler/dfa.nim @@ -605,11 +605,11 @@ proc aliases*(obj, field: PNode): AliasKind = var n = n while true: case n.kind - of PathKinds0 - {nkDotExpr, nkCheckedFieldExpr, nkBracketExpr}: + of PathKinds0 - {nkDotExpr, nkBracketExpr}: n = n[0] of PathKinds1: n = n[1] - of nkDotExpr, nkCheckedFieldExpr, nkBracketExpr: + of nkDotExpr, nkBracketExpr: result.add n n = n[0] of nkSym: @@ -642,8 +642,6 @@ proc aliases*(obj, field: PNode): AliasKind = if currFieldPath.sym != currObjPath.sym: return no of nkDotExpr: if currFieldPath[1].sym != currObjPath[1].sym: return no - of nkCheckedFieldExpr: - if currFieldPath[0][1].sym != currObjPath[0][1].sym: return no of nkBracketExpr: if currFieldPath[1].kind in nkLiterals and currObjPath[1].kind in nkLiterals: if currFieldPath[1].intVal != currObjPath[1].intVal: From 731eabc9309997775c8be41f3e5eb5512460aad0 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sun, 20 Mar 2022 14:02:44 +0100 Subject: [PATCH 2840/3768] fixes #19631 (#19618) Aliasing is hard and we have to watch out not to compile 'x = f(x.a)' into 'f(x.a, addr x)' --- compiler/ccgcalls.nim | 11 ++++++++--- tests/ccgbugs/tcgbug.nim | 31 +++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index ce5fbfdd7..05be51a45 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -21,7 +21,7 @@ proc canRaiseDisp(p: BProc; n: PNode): bool = # we have to be *very* conservative: result = canRaiseConservative(n) -proc preventNrvo(p: BProc; le, ri: PNode): bool = +proc preventNrvo(p: BProc; dest, le, ri: PNode): bool = proc locationEscapes(p: BProc; le: PNode; inTryStmt: bool): bool = var n = le while true: @@ -54,6 +54,11 @@ proc preventNrvo(p: BProc; le, ri: PNode): bool = if canRaise(ri[0]) and locationEscapes(p, le, p.nestedTryStmts.len > 0): message(p.config, le.info, warnObservableStores, $le) + # bug #19613 prevent dangerous aliasing too: + if dest != nil and dest != le: + for i in 1.. 1: pl.add(~", ") # beware of 'result = p(result)'. We may need to allocate a temporary: - if d.k in {locTemp, locNone} or not preventNrvo(p, le, ri): + if d.k in {locTemp, locNone} or not preventNrvo(p, d.lode, le, ri): # Great, we can use 'd': if d.k == locNone: getTemp(p, typ[0], d, needsInit=true) diff --git a/tests/ccgbugs/tcgbug.nim b/tests/ccgbugs/tcgbug.nim index db9c116be..0fe4b8852 100644 --- a/tests/ccgbugs/tcgbug.nim +++ b/tests/ccgbugs/tcgbug.nim @@ -91,3 +91,34 @@ proc test(c: Helper): string = c.formatted echo test(Helper(isKind: true, formatted: "ok")) + + +# bug #19613 + +type + Eth2Digest = object + data: array[42, byte] + + BlockId* = object + root*: Eth2Digest + + BlockSlotId* = object + bid*: BlockId + slot*: uint64 + +func init*(T: type BlockSlotId, bid: BlockId, slot: uint64): T = + #debugecho "init ", bid, " ", slot + BlockSlotId(bid: bid, slot: slot) + +proc bug19613 = + var x: BlockSlotId + x.bid.root.data[0] = 42 + + x = + if x.slot > 0: + BlockSlotId.init(x.bid, x.slot) + else: + BlockSlotId.init(x.bid, x.slot) + doAssert x.bid.root.data[0] == 42 + +bug19613() From c4a0d4c5e35f09430a1c3d465fc62eb1001b7f9f Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 22 Mar 2022 15:36:49 +0100 Subject: [PATCH 2841/3768] =?UTF-8?q?fixes=20#19615;=20emit=20better=20cod?= =?UTF-8?q?e=20for=20integer=20divisions=20when=20the=20divisor=E2=80=A6?= =?UTF-8?q?=20(#19626)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fixes #19615; emit better code for integer divisions when the divisor is known at compile-time * proper bugfix: unsigned numbers cannot be -1 --- compiler/ccgexprs.nim | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 9848366a1..1a3e217b2 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -584,13 +584,23 @@ proc binaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = else: # we handle div by zero here so that we know that the compilerproc's # result is only for overflows. + var needsOverflowCheck = true if m in {mDivI, mModI}: - linefmt(p, cpsStmts, "if ($1 == 0){ #raiseDivByZero(); $2}$n", - [rdLoc(b), raiseInstr(p)]) - - let res = binaryArithOverflowRaw(p, t, a, b, - if t.kind == tyInt64: prc64[m] else: prc[m]) - putIntoDest(p, d, e, "($#)($#)" % [getTypeDesc(p.module, e.typ), res]) + var canBeZero = true + if e[2].kind in {nkIntLit..nkUInt64Lit}: + canBeZero = e[2].intVal == 0 + if e[2].kind in {nkIntLit..nkInt64Lit}: + needsOverflowCheck = e[2].intVal == -1 + if canBeZero: + linefmt(p, cpsStmts, "if ($1 == 0){ #raiseDivByZero(); $2}$n", + [rdLoc(b), raiseInstr(p)]) + if needsOverflowCheck: + let res = binaryArithOverflowRaw(p, t, a, b, + if t.kind == tyInt64: prc64[m] else: prc[m]) + putIntoDest(p, d, e, "($#)($#)" % [getTypeDesc(p.module, e.typ), res]) + else: + let res = "($1)($2 $3 $4)" % [getTypeDesc(p.module, e.typ), rdLoc(a), rope(opr[m]), rdLoc(b)] + putIntoDest(p, d, e, res) proc unaryArithOverflow(p: BProc, e: PNode, d: var TLoc, m: TMagic) = var From 4c8934305c48bb52ed0e5a59b47229521cb333ce Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Tue, 22 Mar 2022 12:08:31 -0700 Subject: [PATCH 2842/3768] system: thread: stack dealloction on Zephyr (#19633) [backport:1.6] Try to free the stack allocation when a thread exits. Possibly works for FreeRTOS as well. --- lib/system/threads.nim | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/system/threads.nim b/lib/system/threads.nim index ac727be0a..1c1d1ca1c 100644 --- a/lib/system/threads.nim +++ b/lib/system/threads.nim @@ -47,7 +47,10 @@ when not declared(ThisIsSystem): {.error: "You must not import this module explicitly".} -when defined(zephyr) or defined(freertos): +const + hasAllocStack = defined(zephyr) # maybe freertos too? + +when hasAllocStack or defined(zephyr) or defined(freertos): const nimThreadStackSize {.intdefine.} = 8192 nimThreadStackGuard {.intdefine.} = 128 @@ -68,6 +71,12 @@ else: #const globalsSlot = ThreadVarSlot(0) #sysAssert checkSlot.int == globalsSlot.int +# Zephyr doesn't include this properly without some help +when defined(zephyr): + {.emit: """/*INCLUDESECTION*/ + #include + """.} + # create for the main thread. Note: do not insert this data into the list # of all threads; it's not to be stopped etc. when not defined(useNimRtl): @@ -98,6 +107,8 @@ type else: dataFn: proc (m: TArg) {.nimcall, gcsafe.} data: TArg + when hasAllocStack: + rawStack: pointer proc `=copy`*[TArg](x: var Thread[TArg], y: Thread[TArg]) {.error.} @@ -161,6 +172,8 @@ else: threadTrouble() finally: afterThreadRuns() + when hasAllocStack: + deallocShared(thrd.rawStack) proc threadProcWrapStackFrame[TArg](thrd: ptr Thread[TArg]) {.raises: [].} = when defined(boehmgc): @@ -330,11 +343,12 @@ else: when hasSharedHeap: t.core.stackSize = ThreadStackSize var a {.noinit.}: Pthread_attr doAssert pthread_attr_init(a) == 0 - when defined(zephyr): + when hasAllocStack: var rawstk = allocShared0(ThreadStackSize + StackGuardSize) stk = cast[pointer](cast[uint](rawstk) + StackGuardSize) let setstacksizeResult = pthread_attr_setstack(addr a, stk, ThreadStackSize) + t.rawStack = rawstk else: let setstacksizeResult = pthread_attr_setstacksize(a, ThreadStackSize) From a8b5ad845c4218b4f20595df097c593acee53d50 Mon Sep 17 00:00:00 2001 From: Dominic Ward Date: Wed, 23 Mar 2022 06:50:36 +0000 Subject: [PATCH 2843/3768] Fix process lines iterator (#19605) * Ensure lines when process done * eliminate post-EOF exit test * Recommend fixes for execCmdEx/execProcess --- lib/pure/osproc.nim | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 6a0ac9a8b..3da9737ec 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -451,7 +451,7 @@ proc execProcesses*(cmds: openArray[string], if afterRunEvent != nil: afterRunEvent(i, p) close(p) -iterator lines*(p: Process): string {.since: (1, 3), tags: [ReadIOEffect].} = +iterator lines*(p: Process, keepNewLines = false): string {.since: (1, 3), tags: [ReadIOEffect].} = ## Convenience iterator for working with `startProcess` to read data from a ## background process. ## @@ -474,11 +474,11 @@ iterator lines*(p: Process): string {.since: (1, 3), tags: [ReadIOEffect].} = ## p.close var outp = p.outputStream var line = newStringOfCap(120) - while true: - if outp.readLine(line): - yield line - else: - if p.peekExitCode != -1: break + while outp.readLine(line): + if keepNewLines: + line.add("\n") + yield line + discard waitForExit(p) proc readLines*(p: Process): (seq[string], int) {.since: (1, 3).} = ## Convenience function for working with `startProcess` to read data from a @@ -514,6 +514,7 @@ when not defined(useNimRtl): var outp = outputStream(p) result = "" var line = newStringOfCap(120) + # consider `p.lines(keepNewLines=true)` to circumvent `running` busy-wait while true: # FIXME: converts CR-LF to LF. if outp.readLine(line): @@ -1622,6 +1623,7 @@ proc execCmdEx*(command: string, options: set[ProcessOption] = { inputStream(p).write(input) close inputStream(p) + # consider `p.lines(keepNewLines=true)` to avoid exit code test result = ("", -1) var line = newStringOfCap(120) while true: From 7f6e800cafc7b73625893fb5280eb8b51a15b252 Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 24 Mar 2022 03:34:53 +0800 Subject: [PATCH 2844/3768] move assertions out of system (#19599) --- compiler/aliases.nim | 3 +++ compiler/ast.nim | 3 +++ compiler/astalgo.nim | 3 +++ compiler/bitsets.nim | 3 +++ compiler/btrees.nim | 3 +++ compiler/ccgutils.nim | 3 +++ compiler/cgen.nim | 4 ++++ compiler/cgmeth.nim | 4 ++++ compiler/closureiters.nim | 3 +++ compiler/commands.nim | 3 +++ compiler/concepts.nim | 3 +++ compiler/dfa.nim | 3 +++ compiler/enumtostr.nim | 4 ++++ compiler/errorhandling.nim | 3 +++ compiler/extccomp.nim | 3 +++ compiler/gorgeimpl.nim | 3 +++ compiler/guards.nim | 3 +++ compiler/hlo.nim | 3 +++ compiler/ic/bitabs.nim | 3 +++ compiler/ic/cbackend.nim | 3 +++ compiler/ic/dce.nim | 4 ++++ compiler/ic/ic.nim | 3 +++ compiler/ic/integrity.nim | 4 ++++ compiler/ic/navigator.nim | 3 +++ compiler/ic/packed_ast.nim | 3 +++ compiler/ic/replayer.nim | 3 +++ compiler/ic/rodfiles.nim | 3 +++ compiler/idents.nim | 3 +++ compiler/importer.nim | 3 +++ compiler/injectdestructors.nim | 3 +++ compiler/int128.nim | 3 +++ compiler/isolation_check.nim | 3 +++ compiler/lambdalifting.nim | 3 +++ compiler/lexer.nim | 3 +++ compiler/liftdestructors.nim | 3 +++ compiler/llstream.nim | 3 +++ compiler/lookups.nim | 4 ++++ compiler/lowerings.nim | 3 +++ compiler/main.nim | 4 ++++ compiler/modulegraphs.nim | 3 +++ compiler/modules.nim | 3 +++ compiler/msgs.nim | 4 ++++ compiler/ndi.nim | 3 +++ compiler/nilcheck.nim | 3 +++ compiler/nim.nim | 4 ++++ compiler/nimblecmd.nim | 3 +++ compiler/nimconf.nim | 3 +++ compiler/nimlexbase.nim | 3 +++ compiler/nimpaths.nim | 4 ++++ compiler/nimsets.nim | 3 +++ compiler/options.nim | 5 +++++ compiler/parser.nim | 3 +++ compiler/passes.nim | 3 +++ compiler/pathutils.nim | 2 +- compiler/patterns.nim | 3 +++ compiler/platform.nim | 4 ++++ compiler/pragmas.nim | 3 +++ compiler/renderer.nim | 3 +++ compiler/reorder.nim | 3 +++ compiler/rodutils.nim | 3 +++ compiler/ropes.nim | 4 ++++ compiler/scriptconfig.nim | 3 +++ compiler/semdata.nim | 3 +++ compiler/semfold.nim | 3 +++ compiler/sempass2.nim | 3 +++ compiler/semtypinst.nim | 3 +++ compiler/sighashes.nim | 4 ++++ compiler/sigmatch.nim | 3 +++ compiler/syntaxes.nim | 3 +++ compiler/transf.nim | 3 +++ compiler/treetab.nim | 3 +++ compiler/typeallowed.nim | 3 +++ compiler/types.nim | 3 +++ compiler/varpartitions.nim | 3 +++ compiler/vmdeps.nim | 3 +++ compiler/vmgen.nim | 3 +++ compiler/vmhooks.nim | 3 +++ compiler/vmmarshal.nim | 3 +++ compiler/vmops.nim | 4 ++++ lib/core/macros.nim | 4 ++++ lib/pure/algorithm.nim | 4 ++++ lib/pure/browsers.nim | 3 +++ lib/pure/collections/hashcommon.nim | 4 ++++ lib/pure/collections/sequtils.nim | 4 ++++ lib/pure/hashes.nim | 4 ++++ lib/pure/json.nim | 2 +- lib/pure/lexbase.nim | 3 +++ lib/pure/math.nim | 4 ++++ lib/pure/options.nim | 4 ++++ lib/pure/os.nim | 2 +- lib/pure/osproc.nim | 4 ++++ lib/pure/parsejson.nim | 3 +++ lib/pure/streamwrapper.nim | 4 ++++ lib/pure/strformat.nim | 4 ++++ lib/pure/strtabs.nim | 4 ++++ lib/pure/strutils.nim | 3 +++ lib/pure/terminal.nim | 2 +- lib/pure/times.nim | 4 ++++ lib/pure/typetraits.nim | 4 ++++ lib/{system => std}/assertions.nim | 14 ++++++++++---- lib/std/enumutils.nim | 4 ++++ lib/std/jsonutils.nim | 3 +++ lib/std/packedsets.nim | 3 +++ lib/std/private/globs.nim | 4 ++++ lib/std/sha1.nim | 3 +++ lib/system.nim | 7 +++++-- lib/system/dragonbox.nim | 2 ++ lib/system/formatfloat.nim | 3 +++ lib/system/iterators.nim | 3 +++ lib/system/schubfach.nim | 3 +++ 110 files changed, 361 insertions(+), 10 deletions(-) rename lib/{system => std}/assertions.nim (94%) diff --git a/compiler/aliases.nim b/compiler/aliases.nim index 0006c9fe6..9ec72faa4 100644 --- a/compiler/aliases.nim +++ b/compiler/aliases.nim @@ -12,6 +12,9 @@ import ast, astalgo, types, trees, intsets +when defined(nimPreviewSlimSystem): + import std/assertions + type TAnalysisResult* = enum arNo, arMaybe, arYes diff --git a/compiler/ast.nim b/compiler/ast.nim index ad5ec12f5..d1e5ae2bf 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -13,6 +13,9 @@ import lineinfos, hashes, options, ropes, idents, int128, tables from strutils import toLowerAscii +when defined(nimPreviewSlimSystem): + import std/assertions + export int128 type diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 94fa9da93..bc5b7d6e1 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -15,6 +15,9 @@ import ast, hashes, intsets, strutils, options, lineinfos, ropes, idents, rodutils, msgs +when defined(nimPreviewSlimSystem): + import std/assertions + proc hashNode*(p: RootRef): Hash proc treeToYaml*(conf: ConfigRef; n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope # Convert a tree into its YAML representation; this is used by the diff --git a/compiler/bitsets.nim b/compiler/bitsets.nim index e0d94409e..67598f9ca 100644 --- a/compiler/bitsets.nim +++ b/compiler/bitsets.nim @@ -10,6 +10,9 @@ # this unit handles Nim sets; it implements bit sets # the code here should be reused in the Nim standard library +when defined(nimPreviewSlimSystem): + import std/assertions + type ElemType = byte TBitSet* = seq[ElemType] # we use byte here to avoid issues with diff --git a/compiler/btrees.nim b/compiler/btrees.nim index d5e7e8099..1701f8d7b 100644 --- a/compiler/btrees.nim +++ b/compiler/btrees.nim @@ -10,6 +10,9 @@ ## BTree implementation with few features, but good enough for the ## Nim compiler's needs. +when defined(nimPreviewSlimSystem): + import std/assertions + const M = 512 # max children per B-tree node = M-1 # (must be even and greater than 2) diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim index 06b75a20f..e19fccfa7 100644 --- a/compiler/ccgutils.nim +++ b/compiler/ccgutils.nim @@ -13,6 +13,9 @@ import ast, types, hashes, strutils, msgs, wordrecg, platform, trees, options, cgendata +when defined(nimPreviewSlimSystem): + import std/assertions + proc getPragmaStmt*(n: PNode, w: TSpecialWord): PNode = case n.kind of nkStmtList: diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 2865473fb..ad59b759f 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -17,6 +17,10 @@ import lowerings, tables, sets, ndi, lineinfos, pathutils, transf, injectdestructors, astmsgs + +when defined(nimPreviewSlimSystem): + import std/assertions + when not defined(leanCompiler): import spawn, semparallel diff --git a/compiler/cgmeth.nim b/compiler/cgmeth.nim index 484bc9d97..dab8826c1 100644 --- a/compiler/cgmeth.nim +++ b/compiler/cgmeth.nim @@ -13,6 +13,10 @@ import intsets, options, ast, msgs, idents, renderer, types, magicsys, sempass2, strutils, modulegraphs, lineinfos +when defined(nimPreviewSlimSystem): + import std/assertions + + proc genConv(n: PNode, d: PType, downcast: bool; conf: ConfigRef): PNode = var dest = skipTypes(d, abstractPtrs) var source = skipTypes(n.typ, abstractPtrs) diff --git a/compiler/closureiters.nim b/compiler/closureiters.nim index 4d9aeb3f7..6370d0dcb 100644 --- a/compiler/closureiters.nim +++ b/compiler/closureiters.nim @@ -133,6 +133,9 @@ import renderer, magicsys, lowerings, lambdalifting, modulegraphs, lineinfos, tables, options +when defined(nimPreviewSlimSystem): + import std/assertions + type Ctx = object g: ModuleGraph diff --git a/compiler/commands.nim b/compiler/commands.nim index b21b48801..4c1cec3d2 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -31,6 +31,9 @@ import from ast import setUseIc, eqTypeFlags, tfGcSafe, tfNoSideEffect +when defined(nimPreviewSlimSystem): + import std/assertions + # but some have deps to imported modules. Yay. bootSwitch(usedTinyC, hasTinyCBackend, "-d:tinyc") bootSwitch(usedFFI, hasFFI, "-d:nimHasLibFFI") diff --git a/compiler/concepts.nim b/compiler/concepts.nim index 885b69c60..89a2bdc10 100644 --- a/compiler/concepts.nim +++ b/compiler/concepts.nim @@ -15,6 +15,9 @@ import ast, astalgo, semdata, lookups, lineinfos, idents, msgs, renderer, types, from magicsys import addSonSkipIntLit +when defined(nimPreviewSlimSystem): + import std/assertions + const logBindings = false diff --git a/compiler/dfa.nim b/compiler/dfa.nim index d68e7be82..5b048ff6e 100644 --- a/compiler/dfa.nim +++ b/compiler/dfa.nim @@ -32,6 +32,9 @@ import ast, intsets, lineinfos, renderer import std/private/asciitables +when defined(nimPreviewSlimSystem): + import std/assertions + type InstrKind* = enum goto, fork, def, use diff --git a/compiler/enumtostr.nim b/compiler/enumtostr.nim index 9bfa7001a..4a28d066c 100644 --- a/compiler/enumtostr.nim +++ b/compiler/enumtostr.nim @@ -1,6 +1,10 @@ import ast, idents, lineinfos, modulegraphs, magicsys +when defined(nimPreviewSlimSystem): + import std/assertions + + proc genEnumToStrProc*(t: PType; info: TLineInfo; g: ModuleGraph; idgen: IdGenerator): PSym = result = newSym(skProc, getIdent(g.cache, "$"), nextSymId idgen, t.owner, info) diff --git a/compiler/errorhandling.nim b/compiler/errorhandling.nim index cda7ab3f4..a8361105e 100644 --- a/compiler/errorhandling.nim +++ b/compiler/errorhandling.nim @@ -12,6 +12,9 @@ import ast, renderer, options, strutils, types +when defined(nimPreviewSlimSystem): + import std/assertions + type ErrorKind* = enum ## expand as you need. RawTypeMismatchError diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 124cc2c34..09a1dfbb3 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -16,6 +16,9 @@ import ropes, platform, condsyms, options, msgs, lineinfos, pathutils import std/[os, strutils, osproc, sha1, streams, sequtils, times, strtabs, json, jsonutils, sugar] +when defined(nimPreviewSlimSystem): + import std/syncio + type TInfoCCProp* = enum # properties of the C compiler: hasSwitchRange, # CC allows ranges in switch statements (GNU C) diff --git a/compiler/gorgeimpl.nim b/compiler/gorgeimpl.nim index d4aeb6a77..f58ac5de9 100644 --- a/compiler/gorgeimpl.nim +++ b/compiler/gorgeimpl.nim @@ -12,6 +12,9 @@ import msgs, std / sha1, os, osproc, streams, options, lineinfos, pathutils +when defined(nimPreviewSlimSystem): + import std/syncio + proc readOutput(p: Process): (string, int) = result[0] = "" var output = p.outputStream diff --git a/compiler/guards.nim b/compiler/guards.nim index d1265d42c..a50593aca 100644 --- a/compiler/guards.nim +++ b/compiler/guards.nim @@ -12,6 +12,9 @@ import ast, astalgo, msgs, magicsys, nimsets, trees, types, renderer, idents, saturate, modulegraphs, options, lineinfos, int128 +when defined(nimPreviewSlimSystem): + import std/assertions + const someEq = {mEqI, mEqF64, mEqEnum, mEqCh, mEqB, mEqRef, mEqProc, mEqStr, mEqSet, mEqCString} diff --git a/compiler/hlo.nim b/compiler/hlo.nim index af54cabbb..8620c4c4d 100644 --- a/compiler/hlo.nim +++ b/compiler/hlo.nim @@ -9,6 +9,9 @@ # This include implements the high level optimization pass. +when defined(nimPreviewSlimSystem): + import std/assertions + proc hlo(c: PContext, n: PNode): PNode proc evalPattern(c: PContext, n, orig: PNode): PNode = diff --git a/compiler/ic/bitabs.nim b/compiler/ic/bitabs.nim index 0bce30b5d..ae673b574 100644 --- a/compiler/ic/bitabs.nim +++ b/compiler/ic/bitabs.nim @@ -3,6 +3,9 @@ import hashes, rodfiles +when defined(nimPreviewSlimSystem): + import std/assertions + type LitId* = distinct uint32 diff --git a/compiler/ic/cbackend.nim b/compiler/ic/cbackend.nim index f5811cb3b..e7ab000e6 100644 --- a/compiler/ic/cbackend.nim +++ b/compiler/ic/cbackend.nim @@ -20,6 +20,9 @@ import std/packedsets, algorithm, tables +when defined(nimPreviewSlimSystem): + import std/assertions + import ".."/[ast, options, lineinfos, modulegraphs, cgendata, cgen, pathutils, extccomp, msgs] diff --git a/compiler/ic/dce.nim b/compiler/ic/dce.nim index 0a436a5d1..bc61a38de 100644 --- a/compiler/ic/dce.nim +++ b/compiler/ic/dce.nim @@ -10,6 +10,10 @@ ## Dead code elimination (=DCE) for IC. import std/[intsets, tables] + +when defined(nimPreviewSlimSystem): + import std/assertions + import ".." / [ast, options, lineinfos, types] import packed_ast, ic, bitabs diff --git a/compiler/ic/ic.nim b/compiler/ic/ic.nim index 7387d165b..193e5f517 100644 --- a/compiler/ic/ic.nim +++ b/compiler/ic/ic.nim @@ -14,6 +14,9 @@ import ".." / [ast, idents, lineinfos, msgs, ropes, options, #import ".." / [renderer, astalgo] from os import removeFile, isAbsolute +when defined(nimPreviewSlimSystem): + import std/[syncio, assertions] + type PackedConfig* = object backend: TBackend diff --git a/compiler/ic/integrity.nim b/compiler/ic/integrity.nim index ed367ef61..d341fd653 100644 --- a/compiler/ic/integrity.nim +++ b/compiler/ic/integrity.nim @@ -11,6 +11,10 @@ ## The set must cover a complete Nim project. import sets + +when defined(nimPreviewSlimSystem): + import std/assertions + import ".." / [ast, modulegraphs] import packed_ast, bitabs, ic diff --git a/compiler/ic/navigator.nim b/compiler/ic/navigator.nim index a1a14885d..cbba591c5 100644 --- a/compiler/ic/navigator.nim +++ b/compiler/ic/navigator.nim @@ -16,6 +16,9 @@ import sets from os import nil from std/private/miscdollars import toLocation +when defined(nimPreviewSlimSystem): + import std/assertions + import ".." / [ast, modulegraphs, msgs, options] import packed_ast, bitabs, ic diff --git a/compiler/ic/packed_ast.nim b/compiler/ic/packed_ast.nim index 17beda2c1..c78fe56f5 100644 --- a/compiler/ic/packed_ast.nim +++ b/compiler/ic/packed_ast.nim @@ -16,6 +16,9 @@ import hashes, tables, strtabs import bitabs import ".." / [ast, options] +when defined(nimPreviewSlimSystem): + import std/assertions + type SymId* = distinct int32 ModuleId* = distinct int32 diff --git a/compiler/ic/replayer.nim b/compiler/ic/replayer.nim index 61aa0e697..0188eaee3 100644 --- a/compiler/ic/replayer.nim +++ b/compiler/ic/replayer.nim @@ -16,6 +16,9 @@ import ".." / [ast, modulegraphs, trees, extccomp, btrees, import tables +when defined(nimPreviewSlimSystem): + import std/assertions + import packed_ast, ic, bitabs proc replayStateChanges*(module: PSym; g: ModuleGraph) = diff --git a/compiler/ic/rodfiles.nim b/compiler/ic/rodfiles.nim index a52bd18b3..d811b9b99 100644 --- a/compiler/ic/rodfiles.nim +++ b/compiler/ic/rodfiles.nim @@ -16,6 +16,9 @@ from typetraits import supportsCopyMem +when defined(nimPreviewSlimSystem): + import std/[syncio, assertions] + ## Overview ## ======== ## `RodFile` represents a Rod File (versioned binary format), and the diff --git a/compiler/idents.nim b/compiler/idents.nim index d2a84fd36..f36ce09f3 100644 --- a/compiler/idents.nim +++ b/compiler/idents.nim @@ -14,6 +14,9 @@ import hashes, wordrecg +when defined(nimPreviewSlimSystem): + import std/assertions + type PIdent* = ref TIdent TIdent*{.acyclic.} = object diff --git a/compiler/importer.nim b/compiler/importer.nim index acca2c644..719b75f0f 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -15,6 +15,9 @@ import modulegraphs, wordrecg, tables from strutils import `%` +when defined(nimPreviewSlimSystem): + import std/assertions + proc readExceptSet*(c: PContext, n: PNode): IntSet = assert n.kind in {nkImportExceptStmt, nkExportExceptStmt} result = initIntSet() diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 985f27878..6500c5bc7 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -19,6 +19,9 @@ import lineinfos, parampatterns, sighashes, liftdestructors, optimizer, varpartitions +when defined(nimPreviewSlimSystem): + import std/assertions + from trees import exprStructuralEquivalent, getRoot type diff --git a/compiler/int128.nim b/compiler/int128.nim index 6ba7c1961..afa07094b 100644 --- a/compiler/int128.nim +++ b/compiler/int128.nim @@ -5,6 +5,9 @@ from math import trunc +when defined(nimPreviewSlimSystem): + import std/assertions + type Int128* = object udata: array[4, uint32] diff --git a/compiler/isolation_check.nim b/compiler/isolation_check.nim index 68a212794..2674605dc 100644 --- a/compiler/isolation_check.nim +++ b/compiler/isolation_check.nim @@ -13,6 +13,9 @@ import ast, types, renderer, intsets +when defined(nimPreviewSlimSystem): + import std/assertions + proc canAlias(arg, ret: PType; marker: var IntSet): bool proc canAliasN(arg: PType; n: PNode; marker: var IntSet): bool = diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index a622f6de6..6838fd80d 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -14,6 +14,9 @@ import idents, renderer, types, magicsys, lowerings, tables, modulegraphs, lineinfos, transf, liftdestructors, typeallowed +when defined(nimPreviewSlimSystem): + import std/assertions + discard """ The basic approach is that captured vars need to be put on the heap and that the calling chain needs to be explicitly modelled. Things to consider: diff --git a/compiler/lexer.nim b/compiler/lexer.nim index 506b0e924..e795d52c0 100644 --- a/compiler/lexer.nim +++ b/compiler/lexer.nim @@ -19,6 +19,9 @@ import hashes, options, msgs, strutils, platform, idents, nimlexbase, llstream, wordrecg, lineinfos, pathutils, parseutils +when defined(nimPreviewSlimSystem): + import std/assertions + const MaxLineLength* = 80 # lines longer than this lead to a warning numChars*: set[char] = {'0'..'9', 'a'..'z', 'A'..'Z'} diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 51b4ddfb0..68c93a179 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -15,6 +15,9 @@ import modulegraphs, lineinfos, idents, ast, renderer, semdata, from trees import isCaseObj +when defined(nimPreviewSlimSystem): + import std/assertions + type TLiftCtx = object g: ModuleGraph diff --git a/compiler/llstream.nim b/compiler/llstream.nim index 865a98ee0..004d990fa 100644 --- a/compiler/llstream.nim +++ b/compiler/llstream.nim @@ -12,6 +12,9 @@ import pathutils +when defined(nimPreviewSlimSystem): + import std/syncio + # support `useGnuReadline`, `useLinenoise` for backwards compatibility const hasRstdin = (defined(nimUseLinenoise) or defined(useLinenoise) or defined(useGnuReadline)) and not defined(windows) diff --git a/compiler/lookups.nim b/compiler/lookups.nim index fc30408e5..d61e15915 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -9,6 +9,10 @@ # This module implements lookup helpers. import std/[algorithm, strutils] + +when defined(nimPreviewSlimSystem): + import std/assertions + import intsets, ast, astalgo, idents, semdata, types, msgs, options, renderer, nimfix/prettybase, lineinfos, modulegraphs, astmsgs diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index 37405d8d9..20003b946 100644 --- a/compiler/lowerings.nim +++ b/compiler/lowerings.nim @@ -15,6 +15,9 @@ const import ast, astalgo, types, idents, magicsys, msgs, options, modulegraphs, lineinfos +when defined(nimPreviewSlimSystem): + import std/assertions + proc newDeref*(n: PNode): PNode {.inline.} = result = newNodeIT(nkHiddenDeref, n.info, n.typ[0]) result.add n diff --git a/compiler/main.nim b/compiler/main.nim index e4ec4d729..3cddbffb3 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -22,6 +22,10 @@ import modules, modulegraphs, lineinfos, pathutils, vmprofiler + +when defined(nimPreviewSlimSystem): + import std/[syncio, assertions] + import ic / [cbackend, integrity, navigator] from ic / ic import rodViewer diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index 28e0ddb82..c44908dc3 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -15,6 +15,9 @@ import intsets, tables, hashes, md5_old import ast, astalgo, options, lineinfos,idents, btrees, ropes, msgs, pathutils import ic / [packed_ast, ic] +when defined(nimPreviewSlimSystem): + import std/assertions + type SigHash* = distinct MD5Digest diff --git a/compiler/modules.nim b/compiler/modules.nim index 6fba606b2..dd5db63fa 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -14,6 +14,9 @@ import idents, lexer, passes, syntaxes, llstream, modulegraphs, lineinfos, pathutils, tables +when defined(nimPreviewSlimSystem): + import std/[syncio, assertions] + import ic / replayer proc resetSystemArtifacts*(g: ModuleGraph) = diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 56531eb68..9d111b2e2 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -12,6 +12,10 @@ import std/private/miscdollars, options, ropes, lineinfos, pathutils, strutils2 +when defined(nimPreviewSlimSystem): + import std/[syncio, assertions] + + type InstantiationInfo* = typeof(instantiationInfo()) template instLoc*(): InstantiationInfo = instantiationInfo(-2, fullPaths = true) diff --git a/compiler/ndi.nim b/compiler/ndi.nim index 5af87237f..a9d9cfe79 100644 --- a/compiler/ndi.nim +++ b/compiler/ndi.nim @@ -12,6 +12,9 @@ import ast, msgs, ropes, options, pathutils +when defined(nimPreviewSlimSystem): + import std/[syncio, assertions] + type NdiFile* = object enabled: bool diff --git a/compiler/nilcheck.nim b/compiler/nilcheck.nim index 9c2d091f7..49ceb8942 100644 --- a/compiler/nilcheck.nim +++ b/compiler/nilcheck.nim @@ -10,6 +10,9 @@ import ast, renderer, intsets, tables, msgs, options, lineinfos, strformat, idents, treetab, hashes import sequtils, strutils, sets +when defined(nimPreviewSlimSystem): + import std/assertions + # IMPORTANT: notes not up to date, i'll update this comment again # # notes: diff --git a/compiler/nim.nim b/compiler/nim.nim index 86c7ab524..bfb07ba20 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -8,6 +8,10 @@ # import std/[os, strutils, parseopt] + +when defined(nimPreviewSlimSystem): + import std/assertions + when defined(windows) and not defined(nimKochBootstrap): # remove workaround pending bootstrap >= 1.5.1 # refs https://github.com/nim-lang/Nim/issues/18334#issuecomment-867114536 diff --git a/compiler/nimblecmd.nim b/compiler/nimblecmd.nim index 9cd2941ba..b6b08ccd4 100644 --- a/compiler/nimblecmd.nim +++ b/compiler/nimblecmd.nim @@ -12,6 +12,9 @@ import parseutils, strutils, os, options, msgs, sequtils, lineinfos, pathutils, std/sha1, tables +when defined(nimPreviewSlimSystem): + import std/[syncio, assertions] + proc addPath*(conf: ConfigRef; path: AbsoluteDir, info: TLineInfo) = if not conf.searchPaths.contains(path): conf.searchPaths.insert(path, 0) diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim index 1cf22e20a..319a3de6d 100644 --- a/compiler/nimconf.nim +++ b/compiler/nimconf.nim @@ -13,6 +13,9 @@ import llstream, commands, os, strutils, msgs, lexer, ast, options, idents, wordrecg, strtabs, lineinfos, pathutils, scriptconfig +when defined(nimPreviewSlimSystem): + import std/syncio + # ---------------- configuration file parser ----------------------------- # we use Nim's lexer here to save space and work diff --git a/compiler/nimlexbase.nim b/compiler/nimlexbase.nim index b8cb9f78a..3bc9af9c9 100644 --- a/compiler/nimlexbase.nim +++ b/compiler/nimlexbase.nim @@ -15,6 +15,9 @@ import llstream, strutils +when defined(nimPreviewSlimSystem): + import std/assertions + const Lrz* = ' ' Apo* = '\'' diff --git a/compiler/nimpaths.nim b/compiler/nimpaths.nim index a93b488fd..216071c5c 100644 --- a/compiler/nimpaths.nim +++ b/compiler/nimpaths.nim @@ -19,6 +19,10 @@ Unstable API import os, strutils +when defined(nimPreviewSlimSystem): + import std/assertions + + const docCss* = "$nimr/doc/nimdoc.css" docHackNim* = "$nimr/tools/dochack/dochack.nim" diff --git a/compiler/nimsets.nim b/compiler/nimsets.nim index 8683604af..49c80065a 100644 --- a/compiler/nimsets.nim +++ b/compiler/nimsets.nim @@ -12,6 +12,9 @@ import ast, astalgo, lineinfos, bitsets, types, options +when defined(nimPreviewSlimSystem): + import std/assertions + proc inSet*(s: PNode, elem: PNode): bool = assert s.kind == nkCurly if s.kind != nkCurly: diff --git a/compiler/options.nim b/compiler/options.nim index 9c0c9df72..69eafb67f 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -14,6 +14,11 @@ import from terminal import isatty from times import utc, fromUnix, local, getTime, format, DateTime from std/private/globs import nativeToUnixPath + +when defined(nimPreviewSlimSystem): + import std/[syncio, assertions] + + const hasTinyCBackend* = defined(tinyc) useEffectSystem* = true diff --git a/compiler/parser.nim b/compiler/parser.nim index 0fb60440c..7973f7d37 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -36,6 +36,9 @@ import when defined(nimpretty): import layouter +when defined(nimPreviewSlimSystem): + import std/assertions + type Parser* = object # A Parser object represents a file that # is being parsed diff --git a/compiler/passes.nim b/compiler/passes.nim index 3debce1f6..ce52b10c5 100644 --- a/compiler/passes.nim +++ b/compiler/passes.nim @@ -16,6 +16,9 @@ import syntaxes, modulegraphs, reorder, lineinfos, pathutils +when defined(nimPreviewSlimSystem): + import std/syncio + type TPassData* = tuple[input: PNode, closeOutput: PNode] diff --git a/compiler/pathutils.nim b/compiler/pathutils.nim index 3a501a417..1ef0143d5 100644 --- a/compiler/pathutils.nim +++ b/compiler/pathutils.nim @@ -13,7 +13,7 @@ import os, pathnorm when defined(nimPreviewSlimSystem): - import std/syncio + import std/[syncio, assertions] type AbsoluteFile* = distinct string diff --git a/compiler/patterns.nim b/compiler/patterns.nim index 4b39de3ba..87e9c825c 100644 --- a/compiler/patterns.nim +++ b/compiler/patterns.nim @@ -13,6 +13,9 @@ import ast, types, semdata, sigmatch, idents, aliases, parampatterns, trees +when defined(nimPreviewSlimSystem): + import std/assertions + type TPatternContext = object owner: PSym diff --git a/compiler/platform.nim b/compiler/platform.nim index 4e6054d5c..4b4316bc2 100644 --- a/compiler/platform.nim +++ b/compiler/platform.nim @@ -16,6 +16,10 @@ import strutils +when defined(nimPreviewSlimSystem): + import std/assertions + + type TSystemOS* = enum # Also add OS in initialization section and alias # conditionals to condsyms (end of module). diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 702a9e082..01399d4ff 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -14,6 +14,9 @@ import wordrecg, ropes, options, strutils, extccomp, math, magicsys, trees, types, lookups, lineinfos, pathutils, linter +when defined(nimPreviewSlimSystem): + import std/assertions + from ic / ic import addCompilerProc const diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 22a2d4cbd..3503c4bf6 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -17,6 +17,9 @@ when defined(nimHasUsed): import lexer, options, idents, strutils, ast, msgs, lineinfos +when defined(nimPreviewSlimSystem): + import std/[syncio, assertions] + type TRenderFlag* = enum renderNone, renderNoBody, renderNoComments, renderDocComments, diff --git a/compiler/reorder.nim b/compiler/reorder.nim index 65c1fb9ee..daeb48248 100644 --- a/compiler/reorder.nim +++ b/compiler/reorder.nim @@ -4,6 +4,9 @@ import msgs, modulegraphs, syntaxes, options, modulepaths, lineinfos +when defined(nimPreviewSlimSystem): + import std/assertions + type DepN = ref object pnode: PNode diff --git a/compiler/rodutils.nim b/compiler/rodutils.nim index a4f7a5146..95b7b2d9e 100644 --- a/compiler/rodutils.nim +++ b/compiler/rodutils.nim @@ -10,6 +10,9 @@ ## Serialization utilities for the compiler. import strutils, math +when defined(nimPreviewSlimSystem): + import std/assertions + # bcc on windows doesn't have C99 functions when defined(windows) and defined(bcc): {.emit: """#if defined(_MSC_VER) && _MSC_VER < 1900 diff --git a/compiler/ropes.nim b/compiler/ropes.nim index cd696a545..a44d84ddc 100644 --- a/compiler/ropes.nim +++ b/compiler/ropes.nim @@ -60,6 +60,10 @@ import from pathutils import AbsoluteFile +when defined(nimPreviewSlimSystem): + import std/[assertions, syncio] + + type FormatStr* = string # later we may change it to CString for better # performance of the code generator (assignments diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim index 89510f6b2..adc228d1e 100644 --- a/compiler/scriptconfig.nim +++ b/compiler/scriptconfig.nim @@ -16,6 +16,9 @@ import os, times, osproc, wordrecg, strtabs, modulegraphs, pathutils +when defined(nimPreviewSlimSystem): + import std/syncio + # we support 'cmpIgnoreStyle' natively for efficiency: from strutils import cmpIgnoreStyle, contains diff --git a/compiler/semdata.nim b/compiler/semdata.nim index 422d1223a..d7a4d2c1c 100644 --- a/compiler/semdata.nim +++ b/compiler/semdata.nim @@ -11,6 +11,9 @@ import tables +when defined(nimPreviewSlimSystem): + import std/assertions + import intsets, options, ast, astalgo, msgs, idents, renderer, magicsys, vmdef, modulegraphs, lineinfos, sets, pathutils diff --git a/compiler/semfold.nim b/compiler/semfold.nim index d0473b5ce..d2bbc63be 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -17,6 +17,9 @@ import from system/memory import nimCStrLen +when defined(nimPreviewSlimSystem): + import std/assertions + proc errorType*(g: ModuleGraph): PType = ## creates a type representing an error state result = newType(tyError, nextTypeId(g.idgen), g.owners[^1]) diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index d6cc4180b..9bc811a03 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -12,6 +12,9 @@ import wordrecg, strutils, options, guards, lineinfos, semfold, semdata, modulegraphs, varpartitions, typeallowed, nilcheck, errorhandling, tables +when defined(nimPreviewSlimSystem): + import std/assertions + when defined(useDfa): import dfa diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 5d300fc58..504b83b4c 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -14,6 +14,9 @@ import ast, astalgo, msgs, types, magicsys, semdata, renderer, options, from concepts import makeTypeDesc +when defined(nimPreviewSlimSystem): + import std/assertions + const tfInstClearedFlags = {tfHasMeta, tfUnresolved} proc checkPartialConstructedType(conf: ConfigRef; info: TLineInfo, t: PType) = diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim index 3d36d2533..1835d9d0f 100644 --- a/compiler/sighashes.nim +++ b/compiler/sighashes.nim @@ -13,6 +13,10 @@ import ast, tables, ropes, md5_old, modulegraphs from hashes import Hash import types +when defined(nimPreviewSlimSystem): + import std/assertions + + proc `&=`(c: var MD5Context, s: string) = md5Update(c, s, s.len) proc `&=`(c: var MD5Context, ch: char) = # XXX suspicious code here; relies on ch being zero terminated? diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 14f4c5e19..f195c4e45 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -15,6 +15,9 @@ import magicsys, idents, lexer, options, parampatterns, strutils, trees, linter, lineinfos, lowerings, modulegraphs, concepts +when defined(nimPreviewSlimSystem): + import std/assertions + type MismatchKind* = enum kUnknown, kAlreadyGiven, kUnknownNamedParam, kTypeMismatch, kVarNeeded, diff --git a/compiler/syntaxes.nim b/compiler/syntaxes.nim index 03a9702a3..e47b0483d 100644 --- a/compiler/syntaxes.nim +++ b/compiler/syntaxes.nim @@ -13,6 +13,9 @@ import strutils, llstream, ast, idents, lexer, options, msgs, parser, filters, filter_tmpl, renderer, lineinfos, pathutils +when defined(nimPreviewSlimSystem): + import std/[syncio, assertions] + export Parser, parseAll, parseTopLevelStmt, closeParser type diff --git a/compiler/transf.nim b/compiler/transf.nim index edb8d3573..bdd7c680c 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -24,6 +24,9 @@ import lowerings, liftlocals, modulegraphs, lineinfos +when defined(nimPreviewSlimSystem): + import std/assertions + proc transformBody*(g: ModuleGraph; idgen: IdGenerator, prc: PSym, cache: bool): PNode import closureiters, lambdalifting diff --git a/compiler/treetab.nim b/compiler/treetab.nim index 26afd102c..d8dd8d33e 100644 --- a/compiler/treetab.nim +++ b/compiler/treetab.nim @@ -12,6 +12,9 @@ import hashes, ast, astalgo, types +when defined(nimPreviewSlimSystem): + import std/assertions + proc hashTree*(n: PNode): Hash = if n.isNil: return diff --git a/compiler/typeallowed.nim b/compiler/typeallowed.nim index 3d6ea0edb..57dd039ad 100644 --- a/compiler/typeallowed.nim +++ b/compiler/typeallowed.nim @@ -13,6 +13,9 @@ import intsets, ast, renderer, options, semdata, types +when defined(nimPreviewSlimSystem): + import std/assertions + type TTypeAllowedFlag* = enum taField, diff --git a/compiler/types.nim b/compiler/types.nim index 007a61356..4d2801633 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -13,6 +13,9 @@ import intsets, ast, astalgo, trees, msgs, strutils, platform, renderer, options, lineinfos, int128, modulegraphs, astmsgs +when defined(nimPreviewSlimSystem): + import std/assertions + type TPreferedDesc* = enum preferName, # default diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim index 721de900e..d04a7b40c 100644 --- a/compiler/varpartitions.nim +++ b/compiler/varpartitions.nim @@ -32,6 +32,9 @@ import ast, types, lineinfos, options, msgs, renderer, typeallowed, modulegraphs from trees import getMagic, isNoSideEffectPragma, stupidStmtListExpr from isolation_check import canAlias +when defined(nimPreviewSlimSystem): + import std/assertions + type AbstractTime = distinct int diff --git a/compiler/vmdeps.nim b/compiler/vmdeps.nim index 1afda14b0..118a3031e 100644 --- a/compiler/vmdeps.nim +++ b/compiler/vmdeps.nim @@ -10,6 +10,9 @@ import ast, types, msgs, os, options, idents, lineinfos from pathutils import AbsoluteFile +when defined(nimPreviewSlimSystem): + import std/syncio + proc opSlurp*(file: string, info: TLineInfo, module: PSym; conf: ConfigRef): string = try: var filename = parentDir(toFullPath(conf, info)) / file diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 1551fbb80..72bcef4db 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -29,6 +29,9 @@ import tables +when defined(nimPreviewSlimSystem): + import std/assertions + import strutils, ast, types, msgs, renderer, vmdef, intsets, magicsys, options, lowerings, lineinfos, transf, astmsgs diff --git a/compiler/vmhooks.nim b/compiler/vmhooks.nim index 1ede87e5e..84ecf586f 100644 --- a/compiler/vmhooks.nim +++ b/compiler/vmhooks.nim @@ -9,6 +9,9 @@ import pathutils +when defined(nimPreviewSlimSystem): + import std/assertions + template setX(k, field) {.dirty.} = a.slots[a.ra].ensureKind(k) a.slots[a.ra].field = v diff --git a/compiler/vmmarshal.nim b/compiler/vmmarshal.nim index d28f0325b..83c441283 100644 --- a/compiler/vmmarshal.nim +++ b/compiler/vmmarshal.nim @@ -12,6 +12,9 @@ import streams, json, intsets, tables, ast, astalgo, idents, types, msgs, options, lineinfos +when defined(nimPreviewSlimSystem): + import std/assertions + proc ptrToInt(x: PNode): int {.inline.} = result = cast[int](x) # don't skip alignment diff --git a/compiler/vmops.nim b/compiler/vmops.nim index f13e4b1cc..640826cc8 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -31,6 +31,10 @@ from std/osproc import nil from system/formatfloat import addFloatRoundtrip, addFloatSprintf +when defined(nimPreviewSlimSystem): + import std/[syncio, assertions] + + # There are some useful procs in vmconv. import vmconv, vmmarshal diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 4d040cebc..f20ca9385 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -10,6 +10,10 @@ include "system/inclrtl" import std/private/since +when defined(nimPreviewSlimSystem): + import std/assertions + + ## This module contains the interface to the compiler's abstract syntax ## tree (`AST`:idx:). Macros operate on this tree. ## diff --git a/lib/pure/algorithm.nim b/lib/pure/algorithm.nim index 1ddcc9843..c43545f78 100644 --- a/lib/pure/algorithm.nim +++ b/lib/pure/algorithm.nim @@ -44,6 +44,10 @@ runnableExamples: import std/private/since +when defined(nimPreviewSlimSystem): + import std/assertions + + type SortOrder* = enum Descending, Ascending diff --git a/lib/pure/browsers.nim b/lib/pure/browsers.nim index 08f5208d2..c36e31b11 100644 --- a/lib/pure/browsers.nim +++ b/lib/pure/browsers.nim @@ -16,6 +16,9 @@ import std/private/since import strutils +when defined(nimPreviewSlimSystem): + import std/assertions + when defined(windows): import winlean from os import absolutePath diff --git a/lib/pure/collections/hashcommon.nim b/lib/pure/collections/hashcommon.nim index a169418ce..deff8fa21 100644 --- a/lib/pure/collections/hashcommon.nim +++ b/lib/pure/collections/hashcommon.nim @@ -10,6 +10,10 @@ # An `include` file which contains common code for # hash sets and tables. +when defined(nimPreviewSlimSystem): + import std/assertions + + const growthFactor = 2 diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index 64a7be7a9..5e9b492c2 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -84,6 +84,10 @@ import std/private/since import macros +when defined(nimPreviewSlimSystem): + import std/assertions + + when defined(nimHasEffectsOf): {.experimental: "strictEffects".} else: diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim index 47dacec7d..e88210757 100644 --- a/lib/pure/hashes.nim +++ b/lib/pure/hashes.nim @@ -67,6 +67,10 @@ runnableExamples: import std/private/since +when defined(nimPreviewSlimSystem): + import std/assertions + + type Hash* = int ## A hash value. Hash tables using these values should ## always have a size of a power of two so they can use the `and` diff --git a/lib/pure/json.nim b/lib/pure/json.nim index bdc9fe5ab..dd9232ea6 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -165,7 +165,7 @@ import options # xxx remove this dependency using same approach as https://githu import std/private/since when defined(nimPreviewSlimSystem): - import std/syncio + import std/[syncio, assertions] export tables.`$` diff --git a/lib/pure/lexbase.nim b/lib/pure/lexbase.nim index bbc0a38ae..336a57ec1 100644 --- a/lib/pure/lexbase.nim +++ b/lib/pure/lexbase.nim @@ -14,6 +14,9 @@ import strutils, streams +when defined(nimPreviewSlimSystem): + import std/assertions + const EndOfFile* = '\0' ## end of file marker NewLines* = {'\c', '\L'} diff --git a/lib/pure/math.nim b/lib/pure/math.nim index 1c47258bc..15324f882 100644 --- a/lib/pure/math.nim +++ b/lib/pure/math.nim @@ -60,6 +60,10 @@ import std/private/since import bitops, fenv +when defined(nimPreviewSlimSystem): + import std/assertions + + when defined(c) or defined(cpp): proc c_isnan(x: float): bool {.importc: "isnan", header: "".} # a generic like `x: SomeFloat` might work too if this is implemented via a C macro. diff --git a/lib/pure/options.nim b/lib/pure/options.nim index 850bfa555..562ed6361 100644 --- a/lib/pure/options.nim +++ b/lib/pure/options.nim @@ -71,6 +71,10 @@ supports pattern matching on `Option`s, with the `Some()` and import typetraits +when defined(nimPreviewSlimSystem): + import std/assertions + + when (NimMajor, NimMinor) >= (1, 1): type SomePointer = ref | ptr | pointer | proc diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 7c0c64276..fa379a228 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -35,7 +35,7 @@ import std/private/since import strutils, pathnorm when defined(nimPreviewSlimSystem): - import std/syncio + import std/[syncio, assertions] const weirdTarget = defined(nimscript) or defined(js) diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 3da9737ec..dca5099a4 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -31,6 +31,10 @@ else: when defined(linux) and defined(useClone): import linux +when defined(nimPreviewSlimSystem): + import std/[syncio, assertions] + + type ProcessOption* = enum ## Options that can be passed to `startProcess proc ## <#startProcess,string,string,openArray[string],StringTableRef,set[ProcessOption]>`_. diff --git a/lib/pure/parsejson.nim b/lib/pure/parsejson.nim index 196d8c360..c92eac26e 100644 --- a/lib/pure/parsejson.nim +++ b/lib/pure/parsejson.nim @@ -14,6 +14,9 @@ import strutils, lexbase, streams, unicode import std/private/decode_helpers +when defined(nimPreviewSlimSystem): + import std/assertions + type JsonEventKind* = enum ## enumeration of all events that may occur when parsing jsonError, ## an error occurred during parsing diff --git a/lib/pure/streamwrapper.nim b/lib/pure/streamwrapper.nim index 7a501760b..a6c1901d2 100644 --- a/lib/pure/streamwrapper.nim +++ b/lib/pure/streamwrapper.nim @@ -13,6 +13,10 @@ import deques, streams +when defined(nimPreviewSlimSystem): + import std/assertions + + type PipeOutStream*[T] = ref object of T # When stream peek operation is called, it reads from base stream diff --git a/lib/pure/strformat.nim b/lib/pure/strformat.nim index 40a33951c..ce3439600 100644 --- a/lib/pure/strformat.nim +++ b/lib/pure/strformat.nim @@ -316,6 +316,10 @@ single letter DSLs. import macros, parseutils, unicode import strutils except format +when defined(nimPreviewSlimSystem): + import std/assertions + + proc mkDigit(v: int, typ: char): string {.inline.} = assert(v < 26) if v < 10: diff --git a/lib/pure/strtabs.nim b/lib/pure/strtabs.nim index 3b90fea50..aa2886cfa 100644 --- a/lib/pure/strtabs.nim +++ b/lib/pure/strtabs.nim @@ -53,6 +53,10 @@ import std/private/since import hashes, strutils +when defined(nimPreviewSlimSystem): + import std/assertions + + when defined(js) or defined(nimscript) or defined(Standalone): {.pragma: rtlFunc.} else: diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 5e505ec3a..bf7bd6aa8 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -82,6 +82,9 @@ include "system/inclrtl" import std/private/since from std/private/strimpl import cmpIgnoreStyleImpl, cmpIgnoreCaseImpl, startsWithImpl, endsWithImpl +when defined(nimPreviewSlimSystem): + import std/assertions + const Whitespace* = {' ', '\t', '\v', '\r', '\l', '\f'} diff --git a/lib/pure/terminal.nim b/lib/pure/terminal.nim index 5755e142a..571c9b13c 100644 --- a/lib/pure/terminal.nim +++ b/lib/pure/terminal.nim @@ -67,7 +67,7 @@ when defined(windows): import winlean when defined(nimPreviewSlimSystem): - import std/syncio + import std/[syncio, assertions] type PTerminal = ref object diff --git a/lib/pure/times.nim b/lib/pure/times.nim index 7851bf158..b70c5cedc 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -201,6 +201,10 @@ import strutils, math, options import std/private/since include "system/inclrtl" +when defined(nimPreviewSlimSystem): + import std/assertions + + when defined(js): import jscore diff --git a/lib/pure/typetraits.nim b/lib/pure/typetraits.nim index 8dc1b8cf2..3fc1c7c5c 100644 --- a/lib/pure/typetraits.nim +++ b/lib/pure/typetraits.nim @@ -15,6 +15,10 @@ import std/private/since export system.`$` # for backward compatibility +when defined(nimPreviewSlimSystem): + import std/assertions + + type HoleyEnum* = (not Ordinal) and enum ## Enum with holes. type OrdinalEnum* = Ordinal and enum ## Enum without holes. diff --git a/lib/system/assertions.nim b/lib/std/assertions.nim similarity index 94% rename from lib/system/assertions.nim rename to lib/std/assertions.nim index 6f64a55b7..3d2112b1a 100644 --- a/lib/system/assertions.nim +++ b/lib/std/assertions.nim @@ -1,7 +1,13 @@ -## This module provides various assertion utilities. -## -## **Note:** This module is reexported by `system` and thus does not need to be -## imported directly (with `system/assertions`). +# +# +# Nim's Runtime Library +# (c) Copyright 2022 Nim contributors +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## This module implements assertion handling. when not declared(sysFatal): include "system/fatal" diff --git a/lib/std/enumutils.nim b/lib/std/enumutils.nim index b7d2b9f89..9d4ff1bcf 100644 --- a/lib/std/enumutils.nim +++ b/lib/std/enumutils.nim @@ -10,6 +10,10 @@ import macros from typetraits import OrdinalEnum, HoleyEnum +when defined(nimPreviewSlimSystem): + import std/assertions + + # xxx `genEnumCaseStmt` needs tests and runnableExamples macro genEnumCaseStmt*(typ: typedesc, argSym: typed, default: typed, diff --git a/lib/std/jsonutils.nim b/lib/std/jsonutils.nim index 19384b5d1..722ea49b5 100644 --- a/lib/std/jsonutils.nim +++ b/lib/std/jsonutils.nim @@ -34,6 +34,9 @@ import macros from enumutils import symbolName from typetraits import OrdinalEnum +when defined(nimPreviewSlimSystem): + import std/assertions + when not defined(nimFixedForwardGeneric): # xxx remove pending csources_v1 update >= 1.2.0 proc to[T](node: JsonNode, t: typedesc[T]): T = diff --git a/lib/std/packedsets.nim b/lib/std/packedsets.nim index b2ee917eb..1e2892658 100644 --- a/lib/std/packedsets.nim +++ b/lib/std/packedsets.nim @@ -24,6 +24,9 @@ import std/private/since import hashes +when defined(nimPreviewSlimSystem): + import std/assertions + type BitScalar = uint diff --git a/lib/std/private/globs.nim b/lib/std/private/globs.nim index 190316f93..28a810372 100644 --- a/lib/std/private/globs.nim +++ b/lib/std/private/globs.nim @@ -8,6 +8,10 @@ import os when defined(windows): from strutils import replace +when defined(nimPreviewSlimSystem): + import std/assertions + + when defined(nimHasEffectsOf): {.experimental: "strictEffects".} else: diff --git a/lib/std/sha1.nim b/lib/std/sha1.nim index ed962707b..50175024c 100644 --- a/lib/std/sha1.nim +++ b/lib/std/sha1.nim @@ -29,6 +29,9 @@ runnableExamples("-r:off"): import strutils from endians import bigEndian32, bigEndian64 +when defined(nimPreviewSlimSystem): + import std/syncio + const Sha1DigestSize = 20 type diff --git a/lib/system.nim b/lib/system.nim index 572768de2..4080fee06 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1830,8 +1830,11 @@ when not defined(nimscript): when defined(nimV2): include system/arc -import system/assertions -export assertions +when not defined(nimPreviewSlimSystem): + {.deprecated: """assertions is about to move out of system; use `-d:nimPreviewSlimSystem` and + import `std/assertions`.""".} + import std/assertions + export assertions import system/iterators export iterators diff --git a/lib/system/dragonbox.nim b/lib/system/dragonbox.nim index 336c982c1..34ae9e210 100644 --- a/lib/system/dragonbox.nim +++ b/lib/system/dragonbox.nim @@ -24,6 +24,8 @@ import std/private/digitsutils +when defined(nimPreviewSlimSystem): + import std/assertions const dtoaMinBufferLength*: cint = 64 diff --git a/lib/system/formatfloat.nim b/lib/system/formatfloat.nim index 3bcd3257b..aada3e1bf 100644 --- a/lib/system/formatfloat.nim +++ b/lib/system/formatfloat.nim @@ -7,6 +7,9 @@ # distribution, for details about the copyright. # +when defined(nimPreviewSlimSystem): + import std/assertions + proc c_memcpy(a, b: pointer, size: csize_t): pointer {.importc: "memcpy", header: "", discardable.} proc addCstringN(result: var string, buf: cstring; buflen: int) = diff --git a/lib/system/iterators.nim b/lib/system/iterators.nim index f23f7aa11..6d33fc0bc 100644 --- a/lib/system/iterators.nim +++ b/lib/system/iterators.nim @@ -1,3 +1,6 @@ +when defined(nimPreviewSlimSystem): + import std/assertions + when defined(nimHasLentIterators) and not defined(nimNoLentIterators): template lent2(T): untyped = lent T else: diff --git a/lib/system/schubfach.nim b/lib/system/schubfach.nim index 06813f632..7d6861e35 100644 --- a/lib/system/schubfach.nim +++ b/lib/system/schubfach.nim @@ -12,6 +12,9 @@ import std/private/digitsutils +when defined(nimPreviewSlimSystem): + import std/assertions + template sf_Assert(x: untyped): untyped = assert(x) From e93eaac223045543e753643765b6982a109b41a1 Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 24 Mar 2022 03:42:08 +0800 Subject: [PATCH 2845/3768] build external deps with userversion:1.6 (#19612) --- koch.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/koch.nim b/koch.nim index 295b1584b..a3cfd5930 100644 --- a/koch.nim +++ b/koch.nim @@ -142,7 +142,7 @@ proc csource(args: string) = proc bundleC2nim(args: string) = cloneDependency(distDir, "https://github.com/nim-lang/c2nim.git") nimCompile("dist/c2nim/c2nim", - options = "--noNimblePath --path:. " & args) + options = "--noNimblePath --useVersion:1.6 --path:. " & args) proc bundleNimbleExe(latest: bool, args: string) = let commit = if latest: "HEAD" else: NimbleStableCommit @@ -150,7 +150,7 @@ proc bundleNimbleExe(latest: bool, args: string) = commit = commit, allowBundled = true) # installer.ini expects it under $nim/bin nimCompile("dist/nimble/src/nimble.nim", - options = "-d:release --noNimblePath " & args) + options = "-d:release --useVersion:1.6 --noNimblePath " & args) proc bundleNimsuggest(args: string) = nimCompileFold("Compile nimsuggest", "nimsuggest/nimsuggest.nim", From d14e8e8dccc87671acb832b4aea8e736ce60c13e Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Wed, 23 Mar 2022 16:42:54 -0300 Subject: [PATCH 2846/3768] Remove Deprecated Nimscript proc (#19629) --- lib/system/nimscript.nim | 8 -------- 1 file changed, 8 deletions(-) diff --git a/lib/system/nimscript.nim b/lib/system/nimscript.nim index b4554d778..0ef5fc584 100644 --- a/lib/system/nimscript.nim +++ b/lib/system/nimscript.nim @@ -136,14 +136,6 @@ proc dirExists*(dir: string): bool {. ## Checks if the directory `dir` exists. builtin -template existsFile*(args: varargs[untyped]): untyped {.deprecated: "use fileExists".} = - # xxx: warning won't be shown for nimsscript because of current logic handling - # `foreignPackageNotes` - fileExists(args) - -template existsDir*(args: varargs[untyped]): untyped {.deprecated: "use dirExists".} = - dirExists(args) - proc selfExe*(): string = ## Returns the currently running nim or nimble executable. # TODO: consider making this as deprecated alias of `getCurrentCompilerExe` From d7a966c4ebdb13a1cc414ce98bbff87ffa922261 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Wed, 23 Mar 2022 16:43:28 -0300 Subject: [PATCH 2847/3768] Remove Deprecated domextension (#19630) * Remove Deprecated domextension * Remove Deprecated domextension --- changelog.md | 1 + lib/js/dom_extensions.nim | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) delete mode 100644 lib/js/dom_extensions.nim diff --git a/changelog.md b/changelog.md index a9bf3842a..73cb5e2b5 100644 --- a/changelog.md +++ b/changelog.md @@ -46,6 +46,7 @@ becomes an alias for `addr`. - Removed deprecated `jsre.test` and `jsre.toString`. - Removed deprecated `math.c_frexp`. - Removed deprecated ``` httpcore.`==` ```. +- Removed deprecated `std/dom_extensions`. ## Language changes diff --git a/lib/js/dom_extensions.nim b/lib/js/dom_extensions.nim deleted file mode 100644 index c15a03195..000000000 --- a/lib/js/dom_extensions.nim +++ /dev/null @@ -1,3 +0,0 @@ -import std/dom -export elementsFromPoint -{.deprecated: "use `std/dom` instead".} From b0bd4320a0cd9a90acf44bca0dddf10fa5022969 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Wed, 23 Mar 2022 16:43:58 -0300 Subject: [PATCH 2848/3768] Remove deprecated typo poDemon (#19631) * Remove Deprecated poDemon * Remove Deprecated poDemon --- changelog.md | 2 ++ lib/pure/osproc.nim | 5 ----- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/changelog.md b/changelog.md index 73cb5e2b5..92d2612e8 100644 --- a/changelog.md +++ b/changelog.md @@ -48,6 +48,8 @@ becomes an alias for `addr`. - Removed deprecated ``` httpcore.`==` ```. - Removed deprecated `std/dom_extensions`. +- Remove deprecated `osproc.poDemon`, symbol with typo. + ## Language changes diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index dca5099a4..512db92a4 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -70,11 +70,6 @@ type Process* = ref ProcessObj ## Represents an operating system process. -const poDemon* {.deprecated.} = poDaemon ## Nim versions before 0.20 - ## used the wrong spelling ("demon"). - ## Now `ProcessOption` uses the correct spelling ("daemon"), - ## and this is needed just for backward compatibility. - proc execProcess*(command: string, workingDir: string = "", args: openArray[string] = [], env: StringTableRef = nil, From 2c01c9c4c8a20772ebbb91f3333feb5dbcc94e9e Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 24 Mar 2022 03:57:13 +0800 Subject: [PATCH 2849/3768] output byref types into --header file [backport: 1.6] (#19505) * output byref types into --header file fix #19445 * fix comments * set targets --- compiler/ccgtypes.nim | 9 +++++++-- tests/ccgbugs/m19445.c | 3 +++ tests/ccgbugs/t19445.nim | 13 +++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) create mode 100644 tests/ccgbugs/m19445.c create mode 100644 tests/ccgbugs/t19445.nim diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index f010b25c9..c5b4d3857 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -471,8 +471,13 @@ proc genProcParams(m: BModule, t: PType, rettype, params: var Rope, var arr = t[0] if params != nil: params.add(", ") if mapReturnType(m.config, t[0]) != ctArray: - params.add(getTypeDescWeak(m, arr, check, skResult)) - params.add("*") + if isHeaderFile in m.flags: + # still generates types for `--header` + params.add(getTypeDescAux(m, arr, check, skResult)) + params.add("*") + else: + params.add(getTypeDescWeak(m, arr, check, skResult)) + params.add("*") else: params.add(getTypeDescAux(m, arr, check, skResult)) params.addf(" Result", []) diff --git a/tests/ccgbugs/m19445.c b/tests/ccgbugs/m19445.c new file mode 100644 index 000000000..74c23d4b4 --- /dev/null +++ b/tests/ccgbugs/m19445.c @@ -0,0 +1,3 @@ +#include "m19445.h" + +const Foo f = {10, 20, 30, 40}; \ No newline at end of file diff --git a/tests/ccgbugs/t19445.nim b/tests/ccgbugs/t19445.nim new file mode 100644 index 000000000..b6e8d028c --- /dev/null +++ b/tests/ccgbugs/t19445.nim @@ -0,0 +1,13 @@ +discard """ + matrix: "--nimcache:tests/ccgbugs/nimcache19445 --cincludes:nimcache19445 --header:m19445" + targets: "c" +""" + +# bug #19445 +type + Foo* {.exportc.} = object + a*, b*, c*, d*: int + +proc dummy(): Foo {.exportc.} = discard + +{.compile:"m19445.c".} \ No newline at end of file From a262a87bbebdaf41ea248f7caa780f8ea27e20e1 Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 24 Mar 2022 04:07:05 +0800 Subject: [PATCH 2850/3768] [add testcase] arc problems with recursive types (#19456) * [add testcase] arc problems with recursive types close #9650 * do test * expand * Update tests/arc/t9650.nim --- tests/arc/t9650.nim | 87 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 tests/arc/t9650.nim diff --git a/tests/arc/t9650.nim b/tests/arc/t9650.nim new file mode 100644 index 000000000..a8182db68 --- /dev/null +++ b/tests/arc/t9650.nim @@ -0,0 +1,87 @@ +discard """ + matrix: "--gc:arc" +""" + +import typetraits + +# bug #9650 +type + SharedPtr*[T] = object + val: ptr tuple[atomicCounter: int, value: T] + + Node*[T] = object + value: T + next: SharedPtr[Node[T]] + + ForwardList*[T] = object + first: SharedPtr[Node[T]] + +proc `=destroy`*[T](p: var SharedPtr[T]) = + if p.val != nil: + let c = atomicDec(p.val[].atomicCounter) + if c == 0: + when not supportsCopyMem(T): + `=destroy`(p.val[]) + dealloc(p.val) + p.val = nil + +proc `=`*[T](dest: var SharedPtr[T], src: SharedPtr[T]) = + if dest.val != src.val: + if dest.val != nil: + `=destroy`(dest) + if src.val != nil: + discard atomicInc(src.val[].atomicCounter) + dest.val = src.val + +proc `=sink`*[T](dest: var SharedPtr[T], src: SharedPtr[T]) = + if dest.val != nil and dest.val != src.val: + `=destroy`(dest) + dest.val = src.val + + +proc newSharedPtr*[T](val: sink T): SharedPtr[T] = + result.val = cast[type(result.val)](alloc(sizeof(result.val[]))) + reset(result.val[]) + result.val.atomicCounter = 1 + result.val.value = val + +proc isNil*[T](p: SharedPtr[T]): bool = + p.val == nil + +template `->`*[T](p: SharedPtr[T], name: untyped): untyped = + p.val.value.name + +proc createNode[T](val: T): SharedPtr[ Node[T] ]= + result = newSharedPtr(Node[T](value: val)) + +proc push_front*[T](list: var ForwardList[T], val: T) = + var newElem = createNode(val) + newElem->next = list.first + list.first = newElem + +proc pop_front*[T](list: var ForwardList[T]) = + let head = list.first + list.first = head->next + +proc toString*[T](list: ForwardList[T]): string = + result = "[" + var head = list.first + while not head.isNil: + result &= $(head->value) & ", " + head = head->next + result &= ']' + +block: + var x: ForwardList[int] + x.push_front(1) + x.push_front(2) + x.push_front(3) + + doAssert toString(x) == "[3, 2, 1, ]" + + x.pop_front() + x.pop_front() + doAssert toString(x) == "[1, ]" + + x.pop_front() + doAssert toString(x) == "[]" From 7c3c61f2f1e898e5c70dfd619d94d09e339836e4 Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 24 Mar 2022 04:09:44 +0800 Subject: [PATCH 2851/3768] fix nim check nimscript [backport: 1.6] (#19444) fix #19440; fix #3858 --- compiler/commands.nim | 2 ++ compiler/main.nim | 6 +++++- tests/newconfig/tconfigcheck.nims | 11 +++++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 tests/newconfig/tconfigcheck.nims diff --git a/compiler/commands.nim b/compiler/commands.nim index 4c1cec3d2..98537e9ef 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -1112,6 +1112,8 @@ proc processArgument*(pass: TCmdLinePass; p: OptParser; else: if pass == passCmd1: config.commandArgs.add p.key if argsCount == 1: + if p.key.endsWith(".nims"): + incl(config.globalOptions, optWasNimscript) # support UNIX style filenames everywhere for portable build scripts: if config.projectName.len == 0: config.projectName = unixToNativePath(p.key) diff --git a/compiler/main.nim b/compiler/main.nim index 3cddbffb3..e27a4f6d1 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -62,6 +62,9 @@ proc commandCheck(graph: ModuleGraph) = let conf = graph.config conf.setErrorMaxHighMaybe defineSymbol(conf.symbols, "nimcheck") + if optWasNimscript in conf.globalOptions: + defineSymbol(conf.symbols, "nimscript") + defineSymbol(conf.symbols, "nimconfig") semanticPasses(graph) # use an empty backend for semantic checking only compileProject(graph) @@ -367,7 +370,8 @@ proc mainCommand*(graph: ModuleGraph) = msgWriteln(conf, "-- end of list --", {msgStdout, msgSkipHook}) for it in conf.searchPaths: msgWriteln(conf, it.string) - of cmdCheck: commandCheck(graph) + of cmdCheck: + commandCheck(graph) of cmdParse: wantMainModule(conf) discard parseFile(conf.projectMainIdx, cache, conf) diff --git a/tests/newconfig/tconfigcheck.nims b/tests/newconfig/tconfigcheck.nims new file mode 100644 index 000000000..2e12a0bda --- /dev/null +++ b/tests/newconfig/tconfigcheck.nims @@ -0,0 +1,11 @@ +discard """ + cmd: "nim check $file" +""" + +mode = ScriptMode.Verbose +proc build() = + echo "building nim... " + exec "sleep 10" + echo getCurrentDir() + +echo "hello" From 400e0260b854f34a91af47bbe0a1b1c9d112e51b Mon Sep 17 00:00:00 2001 From: treeform Date: Wed, 23 Mar 2022 23:35:29 -0700 Subject: [PATCH 2852/3768] Add more info for {.bycopy.} (#18815) * Add more info for {.bycopy.} See confusion here: https://github.com/nim-lang/Nim/issues/18807 I hope this will help people googling to find this. * Update doc/manual.rst Co-authored-by: Andreas Rumpf --- doc/manual.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/manual.rst b/doc/manual.rst index 4c4b9c506..8206d8342 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -7912,6 +7912,7 @@ instructs the compiler to pass the type by value to procs: Vector {.bycopy.} = object x, y, z: float +The Nim compiler automatically determines whether a parameter is passed by value or by reference based on the parameter type's size. If a parameter must be passed by value or by reference, (such as when interfacing with a C library) use the bycopy or byref pragmas. Byref pragma ------------ From afbcba909b37b8c06250b141ddb9da4bf5bb9922 Mon Sep 17 00:00:00 2001 From: flywind Date: Fri, 25 Mar 2022 17:46:42 +0800 Subject: [PATCH 2853/3768] remove unnecessary framePtr code (#19645) --- compiler/jsgen.nim | 9 ++++++--- lib/system/jssys.nim | 3 ++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index f4097efa4..d7955b2f9 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -773,7 +773,8 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = var tmpFramePtr = rope"F" if optStackTrace notin p.options: tmpFramePtr = p.getTemp(true) - line(p, tmpFramePtr & " = framePtr;\L") + if hasFrameInfo(p): + line(p, tmpFramePtr & " = framePtr;\L") lineF(p, "try {$n", []) var a: TCompRes gen(p, n[0], a) @@ -782,7 +783,8 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = if catchBranchesExist: p.body.addf("--excHandler;$n} catch (EXCEPTION) {$n var prevJSError = lastJSError;$n" & " lastJSError = EXCEPTION;$n --excHandler;$n", []) - line(p, "framePtr = $1;$n" % [tmpFramePtr]) + if hasFrameInfo(p): + line(p, "framePtr = $1;$n" % [tmpFramePtr]) while i < n.len and n[i].kind == nkExceptBranch: if n[i].len == 1: # general except section: @@ -841,7 +843,8 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = line(p, "}\L") lineF(p, "lastJSError = prevJSError;$n") line(p, "} finally {\L") - line(p, "framePtr = $1;$n" % [tmpFramePtr]) + if hasFrameInfo(p): + line(p, "framePtr = $1;$n" % [tmpFramePtr]) if i < n.len and n[i].kind == nkFinally: genStmt(p, n[i][0]) line(p, "}\L") diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim index 6608a2927..00a4a8ab6 100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -130,7 +130,8 @@ proc unhandledException(e: ref Exception) {. when NimStackTrace: add(buf, rawWriteStackTrace()) let cbuf = cstring(buf) - framePtr = nil + when NimStackTrace: + framePtr = nil {.emit: """ if (typeof(Error) !== "undefined") { throw new Error(`cbuf`); From 12a0f88a52bae74e31d6d612f0e2aae3f90340a9 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 26 Mar 2022 06:37:04 +0100 Subject: [PATCH 2854/3768] mitigates #19364 [backport]; we make this bug more unlikely to appear by producing better code to begin with; real fix will come later (#19647) --- compiler/lowerings.nim | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index 20003b946..382e48a16 100644 --- a/compiler/lowerings.nim +++ b/compiler/lowerings.nim @@ -74,14 +74,20 @@ proc lowerTupleUnpacking*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: P let value = n.lastSon result = newNodeI(nkStmtList, n.info) - var temp = newSym(skTemp, getIdent(g.cache, genPrefix), nextSymId(idgen), - owner, value.info, g.config.options) - temp.typ = skipTypes(value.typ, abstractInst) - incl(temp.flags, sfFromGeneric) + var tempAsNode: PNode + let avoidTemp = value.kind == nkSym + if avoidTemp: + tempAsNode = value + else: + var temp = newSym(skTemp, getIdent(g.cache, genPrefix), nextSymId(idgen), + owner, value.info, g.config.options) + temp.typ = skipTypes(value.typ, abstractInst) + incl(temp.flags, sfFromGeneric) + tempAsNode = newSymNode(temp) var v = newNodeI(nkVarSection, value.info) - let tempAsNode = newSymNode(temp) - v.addVar(tempAsNode, value) + if not avoidTemp: + v.addVar(tempAsNode, value) result.add(v) for i in 0.. Date: Sat, 26 Mar 2022 06:47:04 +0100 Subject: [PATCH 2855/3768] fixes #19603; some pragmas were really only supported as top level statements. Now this is enforced properly. (#19646) --- compiler/pragmas.nim | 15 +++++++++------ compiler/semexprs.nim | 8 +++++++- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 01399d4ff..81c6a42d3 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -47,18 +47,21 @@ const wDiscardable, wGensym, wInject, wRaises, wEffectsOf, wTags, wLocks, wGcSafe, wRequires, wEnsures} exprPragmas* = {wLine, wLocks, wNoRewrite, wGcSafe, wNoSideEffect} - stmtPragmas* = {wChecks, wObjChecks, wFieldChecks, wRangeChecks, - wBoundChecks, wOverflowChecks, wNilChecks, wStaticBoundchecks, - wStyleChecks, wAssertions, - wWarnings, wHints, - wLineDir, wStackTrace, wLineTrace, wOptimization, wHint, wWarning, wError, + stmtPragmas* = { + wHint, wWarning, wError, wFatal, wDefine, wUndef, wCompile, wLink, wLinksys, wPure, wPush, wPop, wPassl, wPassc, wLocalPassc, wDeadCodeElimUnused, # deprecated, always on wDeprecated, - wFloatChecks, wInfChecks, wNanChecks, wPragma, wEmit, wUnroll, + wPragma, wEmit, wUnroll, wLinearScanEnd, wPatterns, wTrMacros, wEffects, wNoForward, wReorder, wComputedGoto, wExperimental, wThis, wUsed, wInvariant, wAssume, wAssert} + stmtPragmasTopLevel* = {wChecks, wObjChecks, wFieldChecks, wRangeChecks, + wBoundChecks, wOverflowChecks, wNilChecks, wStaticBoundchecks, + wStyleChecks, wAssertions, + wWarnings, wHints, + wLineDir, wStackTrace, wLineTrace, wOptimization, + wFloatChecks, wInfChecks, wNanChecks} lambdaPragmas* = {FirstCallConv..LastCallConv, wNoSideEffect, wSideEffect, wNoreturn, wNosinks, wDynlib, wHeader, wThread, wAsmNoStackFrame, diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 5f3d065b6..f224bd0b3 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2769,6 +2769,12 @@ proc enumFieldSymChoice(c: PContext, n: PNode, s: PSym): PNode = onUse(info, a) a = nextOverloadIter(o, c, n) +proc semPragmaStmt(c: PContext; n: PNode) = + if c.p.owner.kind == skModule: + pragma(c, c.p.owner, n, stmtPragmas+stmtPragmasTopLevel, true) + else: + pragma(c, c.p.owner, n, stmtPragmas, true) + proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = when defined(nimCompilerStacktraceHints): setFrameMsg c.config$n.info & " " & $n.kind @@ -3017,7 +3023,7 @@ proc semExpr(c: PContext, n: PNode, flags: TExprFlags = {}): PNode = of nkUsingStmt: result = semUsing(c, n) of nkAsmStmt: result = semAsm(c, n) of nkYieldStmt: result = semYield(c, n) - of nkPragma: pragma(c, c.p.owner, n, stmtPragmas, true) + of nkPragma: semPragmaStmt(c, n) of nkIteratorDef: result = semIterator(c, n) of nkProcDef: result = semProc(c, n) of nkFuncDef: result = semFunc(c, n) From 4dfe420c2f1791c722ff949072e1a74f3e7c1499 Mon Sep 17 00:00:00 2001 From: tandy1000 Date: Sat, 26 Mar 2022 05:57:39 +0000 Subject: [PATCH 2856/3768] Add support for `Window.matchMedia` (#19648) --- lib/js/dom.nim | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/js/dom.nim b/lib/js/dom.nim index aca79faca..c1a677258 100644 --- a/lib/js/dom.nim +++ b/lib/js/dom.nim @@ -1322,6 +1322,11 @@ type `block`*: cstring inline*: cstring + MediaQueryList* = ref MediaQueryListObj + MediaQueryListObj {.importc.} = object of EventTargetObj + matches*: bool + media*: cstring + since (1, 3): type DomParser* = ref object @@ -1529,6 +1534,7 @@ proc setTimeout*(w: Window, function: proc (), pause: int): Interval proc stop*(w: Window) proc requestAnimationFrame*(w: Window, function: proc (time: float)): int proc cancelAnimationFrame*(w: Window, id: int) +proc matchMedia*(w: Window, mediaQueryString: cstring): MediaQueryList # Node "methods" proc appendData*(n: Node, data: cstring) From 84ac0035190e81cb991544460786bd5618386bec Mon Sep 17 00:00:00 2001 From: flywind Date: Sat, 26 Mar 2022 15:57:29 +0800 Subject: [PATCH 2857/3768] remove tmpFramePtr when optStackTrace is absent (#19649) --- compiler/jsgen.nim | 4 ---- 1 file changed, 4 deletions(-) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index d7955b2f9..b9a73daf9 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -771,10 +771,6 @@ proc genTry(p: PProc, n: PNode, r: var TCompRes) = if catchBranchesExist: p.body.add("++excHandler;\L") var tmpFramePtr = rope"F" - if optStackTrace notin p.options: - tmpFramePtr = p.getTemp(true) - if hasFrameInfo(p): - line(p, tmpFramePtr & " = framePtr;\L") lineF(p, "try {$n", []) var a: TCompRes gen(p, n[0], a) From 86e93eb2938329db921d5edb718fa7f3ecee83b1 Mon Sep 17 00:00:00 2001 From: Omar Flores <47699779+yoyojambo@users.noreply.github.com> Date: Sat, 26 Mar 2022 02:56:10 -0600 Subject: [PATCH 2858/3768] Fixed description as option in advopt.txt (#19635) * Fixed formatting error in warningAsError. There was only a single space character between the warning and its description, so it shows up as part of the name (in bold) and with no description. Copied the way hotCodeReloading was formatted, with the description in a new line. Also changed descriptions from 'ditto' to what the description it references says. * Fixed typo in advopt.txt Corrected 'hints' to 'warnings' in updated advopt.txt. --- doc/advopt.txt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/doc/advopt.txt b/doc/advopt.txt index 0a2349597..244fe035b 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -55,9 +55,13 @@ Advanced options: (only `all:off` is supported). --hintAsError:X:on|off turn specific hint X into an error on|off -w:on|off|list, --warnings:on|off|list - same as `--hints` but for warnings. - --warning:X:on|off ditto - --warningAsError:X:on|off ditto + `on|off` enables or disables warnings. + `list` reports which warnings are selected. + --warning:X:on|off turn specific warning X on|off. `warning:X` means `warning:X:on`, + as with similar flags. `all` is the set of all warning + (only `all:off` is supported). + --warningAsError:X:on|off + turn specific warning X into an error on|off --styleCheck:off|hint|error produce hints or errors for Nim identifiers that do not adhere to Nim's official style guide From 8cdd8867c0237c28f7a43e6f871507eda207a15d Mon Sep 17 00:00:00 2001 From: John Titor Date: Sat, 26 Mar 2022 10:43:26 +0000 Subject: [PATCH 2859/3768] Fix dial ignoring buffered parameter (#19650) [backport] --- lib/pure/net.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index c1d4c6228..9922bbfa4 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -1963,7 +1963,7 @@ proc dial*(address: string, port: Port, closeUnusedFds(ord(domain)) if success: - result = newSocket(lastFd, domain, sockType, protocol) + result = newSocket(lastFd, domain, sockType, protocol, buffered) elif lastError != 0.OSErrorCode: raiseOSError(lastError) else: From 82319ef00dc9ce6f65b1664024a10ee5d9465f38 Mon Sep 17 00:00:00 2001 From: flywind Date: Sat, 26 Mar 2022 18:50:15 +0800 Subject: [PATCH 2860/3768] fix #8219; nim check/dump shouldn't run single nimscript project [backport: 1.6] (#19641) * fix #8219; nim check/dump shouldn't run single nimscript project [backport: 1.6] --- compiler/nimconf.nim | 3 ++- tests/misc/trunner.nim | 5 +++++ tests/newconfig/{tconfigcheck.nims => mconfigcheck.nims} | 6 ++---- 3 files changed, 9 insertions(+), 5 deletions(-) rename tests/newconfig/{tconfigcheck.nims => mconfigcheck.nims} (74%) diff --git a/compiler/nimconf.nim b/compiler/nimconf.nim index 319a3de6d..fceedb2c4 100644 --- a/compiler/nimconf.nim +++ b/compiler/nimconf.nim @@ -301,7 +301,7 @@ proc loadConfigs*(cfg: RelativeFile; cache: IdentCache; conf: ConfigRef; idgen: if conf.cmd == cmdNimscript: showHintConf() conf.configFiles.setLen 0 - if conf.cmd != cmdIdeTools: + if conf.cmd notin {cmdIdeTools, cmdCheck, cmdDump}: if conf.cmd == cmdNimscript: runNimScriptIfExists(conf.projectFull, isMain = true) else: @@ -311,5 +311,6 @@ proc loadConfigs*(cfg: RelativeFile; cache: IdentCache; conf: ConfigRef; idgen: runNimScriptIfExists(scriptFile, isMain = true) else: # 'nimsuggest foo.nims' means to just auto-complete the NimScript file + # `nim check foo.nims' means to check the syntax of the NimScript file discard showHintConf() diff --git a/tests/misc/trunner.nim b/tests/misc/trunner.nim index f874d38d9..5d12c38b6 100644 --- a/tests/misc/trunner.nim +++ b/tests/misc/trunner.nim @@ -249,6 +249,11 @@ tests/newconfig/bar/mfoo.nims""".splitLines expected.add &"Hint: used config file '{b}' [Conf]\n" doAssert outp.endsWith expected, outp & "\n" & expected + block: # bug #8219 + let file = "tests/newconfig/mconfigcheck.nims" + let cmd = fmt"{nim} check --hints:off {file}" + check execCmdEx(cmd) == ("", 0) + block: # mfoo2.customext let filename = testsDir / "newconfig/foo2/mfoo2.customext" let cmd = fmt"{nim} e --hint:conf {filename}" diff --git a/tests/newconfig/tconfigcheck.nims b/tests/newconfig/mconfigcheck.nims similarity index 74% rename from tests/newconfig/tconfigcheck.nims rename to tests/newconfig/mconfigcheck.nims index 2e12a0bda..8df6715f6 100644 --- a/tests/newconfig/tconfigcheck.nims +++ b/tests/newconfig/mconfigcheck.nims @@ -1,11 +1,9 @@ -discard """ - cmd: "nim check $file" -""" - mode = ScriptMode.Verbose proc build() = echo "building nim... " exec "sleep 10" + exec "nonexistant command" echo getCurrentDir() echo "hello" +build() From 7d32425a577053bda356ca71c81d6701d8ad6372 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 26 Mar 2022 13:29:04 +0100 Subject: [PATCH 2861/3768] fixes #14318 (#14335) --- compiler/pragmas.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 81c6a42d3..458c7547a 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -656,7 +656,7 @@ proc pragmaRaisesOrTags(c: PContext, n: PNode) = x.typ = makeTypeFromExpr(c, x) else: var t = skipTypes(c.semTypeNode(c, x, nil), skipPtrs) - if t.kind != tyObject and not t.isMetaType: + if t.kind notin {tyObject, tyOr}: localError(c.config, x.info, errGenerated, "invalid type for raises/tags list") x.typ = t From eae29e8eafded41ce966c131bff5e66964354805 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Mon, 28 Mar 2022 03:37:59 -0700 Subject: [PATCH 2862/3768] Embedded Network patches - eventfd & socket getters (#19632) --- lib/pure/ioselects/ioselectors_poll.nim | 35 ++++++++++++++++++------- lib/pure/net.nim | 10 +++++++ 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/lib/pure/ioselects/ioselectors_poll.nim b/lib/pure/ioselects/ioselectors_poll.nim index 0d8fef78a..12812ac80 100644 --- a/lib/pure/ioselects/ioselectors_poll.nim +++ b/lib/pure/ioselects/ioselectors_poll.nim @@ -14,6 +14,12 @@ import posix, times # Maximum number of events that can be returned const MAX_POLL_EVENTS = 64 +const hasEventFds = defined(zephyr) or defined(nimPollHasEventFds) + +when hasEventFds: + proc eventfd(count: cuint, flags: cint): cint + {.cdecl, importc: "eventfd", header: "".} + when hasThreadSupport: type SelectorImpl[T] = object @@ -184,14 +190,22 @@ proc unregister*[T](s: Selector[T], ev: SelectEvent) = s.pollRemove(fdi.cint) proc newSelectEvent*(): SelectEvent = - var fds: array[2, cint] - if posix.pipe(fds) != 0: - raiseIOSelectorsError(osLastError()) - setNonBlocking(fds[0]) - setNonBlocking(fds[1]) - result = cast[SelectEvent](allocShared0(sizeof(SelectEventImpl))) - result.rfd = fds[0] - result.wfd = fds[1] + when not hasEventFds: + var fds: array[2, cint] + if posix.pipe(fds) != 0: + raiseIOSelectorsError(osLastError()) + setNonBlocking(fds[0]) + setNonBlocking(fds[1]) + result = cast[SelectEvent](allocShared0(sizeof(SelectEventImpl))) + result.rfd = fds[0] + result.wfd = fds[1] + else: + let fdci = eventfd(0, posix.O_NONBLOCK) + if fdci == -1: + raiseIOSelectorsError(osLastError()) + result = cast[SelectEvent](allocShared0(sizeof(SelectEventImpl))) + result.rfd = fdci + result.wfd = fdci proc trigger*(ev: SelectEvent) = var data: uint64 = 1 @@ -200,7 +214,10 @@ proc trigger*(ev: SelectEvent) = proc close*(ev: SelectEvent) = let res1 = posix.close(ev.rfd) - let res2 = posix.close(ev.wfd) + let res2 = + when hasEventFds: 0 + else: posix.close(ev.wfd) + deallocShared(cast[pointer](ev)) if res1 != 0 or res2 != 0: raiseIOSelectorsError(osLastError()) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 9922bbfa4..c59a4e563 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -1798,6 +1798,16 @@ proc isSsl*(socket: Socket): bool = proc getFd*(socket: Socket): SocketHandle = return socket.fd ## Returns the socket's file descriptor +when defined(zephyr) or defined(nimNetSocketExtras): # Remove in future + proc getDomain*(socket: Socket): Domain = return socket.domain + ## Returns the socket's domain + + proc getType*(socket: Socket): SockType = return socket.sockType + ## Returns the socket's type + + proc getProtocol*(socket: Socket): Protocol = return socket.protocol + ## Returns the socket's protocol + when defined(nimHasStyleChecks): {.push styleChecks: off.} From 49844415ce829cb627d0b02dd6b057a1574f44d6 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Thu, 31 Mar 2022 16:06:13 -0300 Subject: [PATCH 2863/3768] Deprecate selfExe (#19660) * Deprecate selfExe Nimscript * Deprecate selfExe Nimscript --- changelog.md | 2 ++ lib/system/nimscript.nim | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index 92d2612e8..8b9d0bc29 100644 --- a/changelog.md +++ b/changelog.md @@ -50,6 +50,8 @@ becomes an alias for `addr`. - Remove deprecated `osproc.poDemon`, symbol with typo. +- Deprecated `selfExe` for Nimscript. + ## Language changes diff --git a/lib/system/nimscript.nim b/lib/system/nimscript.nim index 0ef5fc584..5d1916c7f 100644 --- a/lib/system/nimscript.nim +++ b/lib/system/nimscript.nim @@ -136,9 +136,8 @@ proc dirExists*(dir: string): bool {. ## Checks if the directory `dir` exists. builtin -proc selfExe*(): string = +proc selfExe*(): string {.deprecated: "Deprecated since v1.7; Use getCurrentCompilerExe".} = ## Returns the currently running nim or nimble executable. - # TODO: consider making this as deprecated alias of `getCurrentCompilerExe` builtin proc toExe*(filename: string): string = From 1275763284d540c0e8ad53b3c27e52743ee08f2b Mon Sep 17 00:00:00 2001 From: rockcavera Date: Fri, 1 Apr 2022 03:01:51 -0300 Subject: [PATCH 2864/3768] fix 19655 - fixing url fragment (#19667) --- lib/pure/parseopt.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/parseopt.nim b/lib/pure/parseopt.nim index b2d024a39..aff9312f9 100644 --- a/lib/pure/parseopt.nim +++ b/lib/pure/parseopt.nim @@ -16,7 +16,7 @@ ## ## The following syntax is supported when arguments for the `shortNoVal` and ## `longNoVal` parameters, which are -## `described later<#shortnoval-and-longnoval>`_, are not provided: +## `described later<#nimshortnoval-and-nimlongnoval>`_, are not provided: ## ## 1. Short options: `-abcd`, `-e:5`, `-e=5` ## 2. Long options: `--foo:bar`, `--foo=bar`, `--foo` From a7024f49afe5623ce90a800986dea7447e249d3b Mon Sep 17 00:00:00 2001 From: Nan Xiao Date: Sat, 2 Apr 2022 01:29:15 +0800 Subject: [PATCH 2865/3768] fix 19655 - fixing more url fragments (#19669) --- lib/pure/parseopt.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pure/parseopt.nim b/lib/pure/parseopt.nim index aff9312f9..71deaa535 100644 --- a/lib/pure/parseopt.nim +++ b/lib/pure/parseopt.nim @@ -203,7 +203,7 @@ proc initOptParser*(cmdline = "", shortNoVal: set[char] = {}, ## ## `shortNoVal` and `longNoVal` are used to specify which options ## do not take values. See the `documentation about these - ## parameters<#shortnoval-and-longnoval>`_ for more information on + ## parameters<#nimshortnoval-and-nimlongnoval>`_ for more information on ## how this affects parsing. ## ## See also: @@ -465,7 +465,7 @@ iterator getopt*(cmdline: seq[string] = @[], ## ## `shortNoVal` and `longNoVal` are used to specify which options ## do not take values. See the `documentation about these - ## parameters<#shortnoval-and-longnoval>`_ for more information on + ## parameters<#nimshortnoval-and-nimlongnoval>`_ for more information on ## how this affects parsing. ## ## There is no need to check for `cmdEnd` while iterating. From c3f03cfa5dfa2ca47f8e4cf99bbcdbf5a7d16eda Mon Sep 17 00:00:00 2001 From: flywind Date: Sat, 2 Apr 2022 01:30:02 +0800 Subject: [PATCH 2866/3768] add somes links to docs (#19668) --- lib/core/macros.nim | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index f20ca9385..4d0e64868 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -933,21 +933,21 @@ proc treeTraverse(n: NimNode; res: var string; level = 0; isLisp = false, indent proc treeRepr*(n: NimNode): string {.benign.} = ## Convert the AST `n` to a human-readable tree-like string. ## - ## See also `repr`, `lispRepr`, and `astGenRepr`. + ## See also `repr`, `lispRepr`_, and `astGenRepr`_. result = "" n.treeTraverse(result, isLisp = false, indented = true) proc lispRepr*(n: NimNode; indented = false): string {.benign.} = ## Convert the AST `n` to a human-readable lisp-like string. ## - ## See also `repr`, `treeRepr`, and `astGenRepr`. + ## See also `repr`, `treeRepr`_, and `astGenRepr`_. result = "" n.treeTraverse(result, isLisp = true, indented = indented) proc astGenRepr*(n: NimNode): string {.benign.} = ## Convert the AST `n` to the code required to generate that AST. ## - ## See also `repr`, `treeRepr`, and `lispRepr`. + ## See also `repr`_, `treeRepr`_, and `lispRepr`_. const NodeKinds = {nnkEmpty, nnkIdent, nnkSym, nnkNone, nnkCommentStmt} @@ -1602,7 +1602,7 @@ macro hasCustomPragma*(n: typed, cp: typed{nkSym}): untyped = ## Expands to `true` if expression `n` which is expected to be `nnkDotExpr` ## (if checking a field), a proc or a type has custom pragma `cp`. ## - ## See also `getCustomPragmaVal`. + ## See also `getCustomPragmaVal`_. ## ## .. code-block:: nim ## template myAttr() {.pragma.} @@ -1626,7 +1626,7 @@ macro getCustomPragmaVal*(n: typed, cp: typed{nkSym}): untyped = ## Expands to value of custom pragma `cp` of expression `n` which is expected ## to be `nnkDotExpr`, a proc or a type. ## - ## See also `hasCustomPragma` + ## See also `hasCustomPragma`_. ## ## .. code-block:: nim ## template serializationKey(key: string) {.pragma.} From 83dabb69ae0f6c0bb269594a5b73af964b809bc7 Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 4 Apr 2022 18:05:23 +0800 Subject: [PATCH 2867/3768] Fix bug in freshVarForClosureIter. Fixes #18474 (#19675) [backport] * Fix bug in freshVarForClosureIter. Fixes #18474. freshVarForClosureIter was returning non-fresh symbols sometimes. Fixed by making addField return the generated PSym. * remove discardable Co-authored-by: Nick Smallbone --- compiler/lambdalifting.nim | 13 ++++--------- compiler/lowerings.nim | 3 ++- compiler/spawn.nim | 22 +++++++++++----------- tests/iter/tclosureiters.nim | 18 ++++++++++++++++++ 4 files changed, 35 insertions(+), 21 deletions(-) diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index 6838fd80d..4b7ca89cd 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -275,16 +275,11 @@ proc liftIterSym*(g: ModuleGraph; n: PNode; idgen: IdGenerator; owner: PSym): PN proc freshVarForClosureIter*(g: ModuleGraph; s: PSym; idgen: IdGenerator; owner: PSym): PNode = let envParam = getHiddenParam(g, owner) let obj = envParam.typ.skipTypes({tyOwned, tyRef, tyPtr}) - addField(obj, s, g.cache, idgen) + let field = addField(obj, s, g.cache, idgen) var access = newSymNode(envParam) assert obj.kind == tyObject - let field = getFieldFromObj(obj, s) - if field != nil: - result = rawIndirectAccess(access, field, s.info) - else: - localError(g.config, s.info, "internal error: cannot generate fresh variable") - result = access + result = rawIndirectAccess(access, field, s.info) # ------------------ new stuff ------------------------------------------- @@ -452,7 +447,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) = if s.name.id == getIdent(c.graph.cache, ":state").id: obj.n[0].sym.itemId = ItemId(module: s.itemId.module, item: -s.itemId.item) else: - addField(obj, s, c.graph.cache, c.idgen) + discard addField(obj, s, c.graph.cache, c.idgen) # direct or indirect dependency: elif (innerProc and s.typ.callConv == ccClosure) or interestingVar(s): discard """ @@ -474,7 +469,7 @@ proc detectCapturedVars(n: PNode; owner: PSym; c: var DetectionPass) = if interestingVar(s) and not c.capturedVars.containsOrIncl(s.id): let obj = c.getEnvTypeForOwner(ow, n.info).skipTypes({tyOwned, tyRef, tyPtr}) #getHiddenParam(owner).typ.skipTypes({tyOwned, tyRef, tyPtr}) - addField(obj, s, c.graph.cache, c.idgen) + discard addField(obj, s, c.graph.cache, c.idgen) # create required upFields: var w = owner.skipGenericOwner if isInnerProc(w) or owner.isIterator: diff --git a/compiler/lowerings.nim b/compiler/lowerings.nim index 382e48a16..de2f678d7 100644 --- a/compiler/lowerings.nim +++ b/compiler/lowerings.nim @@ -230,7 +230,7 @@ proc lookupInRecord(n: PNode, id: ItemId): PSym = if n.sym.itemId.module == id.module and n.sym.itemId.item == -abs(id.item): result = n.sym else: discard -proc addField*(obj: PType; s: PSym; cache: IdentCache; idgen: IdGenerator) = +proc addField*(obj: PType; s: PSym; cache: IdentCache; idgen: IdGenerator): PSym = # because of 'gensym' support, we have to mangle the name with its ID. # This is hacky but the clean solution is much more complex than it looks. var field = newSym(skField, getIdent(cache, s.name.s & $obj.n.len), @@ -244,6 +244,7 @@ proc addField*(obj: PType; s: PSym; cache: IdentCache; idgen: IdGenerator) = field.flags = s.flags * {sfCursor} obj.n.add newSymNode(field) fieldCheck() + result = field proc addUniqueField*(obj: PType; s: PSym; cache: IdentCache; idgen: IdGenerator): PSym {.discardable.} = result = lookupInRecord(obj.n, s.itemId) diff --git a/compiler/spawn.nim b/compiler/spawn.nim index e57686cb4..581f722d5 100644 --- a/compiler/spawn.nim +++ b/compiler/spawn.nim @@ -221,7 +221,7 @@ proc setupArgsForConcurrency(g: ModuleGraph; n: PNode; objType: PType; let fieldname = if i < formals.len: formals[i].sym.name else: tmpName var field = newSym(skField, fieldname, nextSymId idgen, objType.owner, n.info, g.config.options) field.typ = argType - objType.addField(field, g.cache, idgen) + discard objType.addField(field, g.cache, idgen) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[i]) let temp = addLocalVar(g, varSection, varInit, idgen, owner, argType, @@ -260,17 +260,17 @@ proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType; slice[0].typ = getSysType(g, n.info, tyInt) # fake type var fieldB = newSym(skField, tmpName, nextSymId idgen, objType.owner, n.info, g.config.options) fieldB.typ = getSysType(g, n.info, tyInt) - objType.addField(fieldB, g.cache, idgen) + discard objType.addField(fieldB, g.cache, idgen) if getMagic(n) == mSlice: let a = genAddrOf(n[1], idgen) field.typ = a.typ - objType.addField(field, g.cache, idgen) + discard objType.addField(field, g.cache, idgen) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a) var fieldA = newSym(skField, tmpName, nextSymId idgen, objType.owner, n.info, g.config.options) fieldA.typ = getSysType(g, n.info, tyInt) - objType.addField(fieldA, g.cache, idgen) + discard objType.addField(fieldA, g.cache, idgen) result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldA), n[2]) result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), n[3]) @@ -281,7 +281,7 @@ proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType; else: let a = genAddrOf(n, idgen) field.typ = a.typ - objType.addField(field, g.cache, idgen) + discard objType.addField(field, g.cache, idgen) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a) result.add newFastAsgnStmt(newDotExpr(scratchObj, fieldB), genHigh(g, n)) @@ -299,7 +299,7 @@ proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType; # it is more efficient to pass a pointer instead: let a = genAddrOf(n, idgen) field.typ = a.typ - objType.addField(field, g.cache, idgen) + discard objType.addField(field, g.cache, idgen) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), a) let threadLocal = addLocalVar(g, varSection, nil, idgen, owner, field.typ, indirectAccess(castExpr, field, n.info), @@ -308,7 +308,7 @@ proc setupArgsForParallelism(g: ModuleGraph; n: PNode; objType: PType; else: # boring case field.typ = argType - objType.addField(field, g.cache, idgen) + discard objType.addField(field, g.cache, idgen) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n) let threadLocal = addLocalVar(g, varSection, varInit, idgen, owner, field.typ, @@ -377,7 +377,7 @@ proc wrapProcForSpawn*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; spawnExp var argType = n[0].typ.skipTypes(abstractInst) var field = newSym(skField, getIdent(g.cache, "fn"), nextSymId idgen, owner, n.info, g.config.options) field.typ = argType - objType.addField(field, g.cache, idgen) + discard objType.addField(field, g.cache, idgen) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), n[0]) fn = indirectAccess(castExpr, field, n.info) elif fn.kind == nkSym and fn.sym.kind == skIterator: @@ -399,7 +399,7 @@ proc wrapProcForSpawn*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; spawnExp typ.rawAddSon(magicsys.getCompilerProc(g, "Barrier").typ) var field = newSym(skField, getIdent(g.cache, "barrier"), nextSymId idgen, owner, n.info, g.config.options) field.typ = typ - objType.addField(field, g.cache, idgen) + discard objType.addField(field, g.cache, idgen) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), barrier) barrierAsExpr = indirectAccess(castExpr, field, n.info) @@ -407,7 +407,7 @@ proc wrapProcForSpawn*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; spawnExp if spawnKind == srFlowVar: var field = newSym(skField, getIdent(g.cache, "fv"), nextSymId idgen, owner, n.info, g.config.options) field.typ = retType - objType.addField(field, g.cache, idgen) + discard objType.addField(field, g.cache, idgen) fvField = newDotExpr(scratchObj, field) fvAsExpr = indirectAccess(castExpr, field, n.info) # create flowVar: @@ -419,7 +419,7 @@ proc wrapProcForSpawn*(g: ModuleGraph; idgen: IdGenerator; owner: PSym; spawnExp var field = newSym(skField, getIdent(g.cache, "fv"), nextSymId idgen, owner, n.info, g.config.options) field.typ = newType(tyPtr, nextTypeId idgen, objType.owner) field.typ.rawAddSon(retType) - objType.addField(field, g.cache, idgen) + discard objType.addField(field, g.cache, idgen) fvAsExpr = indirectAccess(castExpr, field, n.info) result.add newFastAsgnStmt(newDotExpr(scratchObj, field), genAddrOf(dest, idgen)) diff --git a/tests/iter/tclosureiters.nim b/tests/iter/tclosureiters.nim index afeaabc7d..85611373c 100644 --- a/tests/iter/tclosureiters.nim +++ b/tests/iter/tclosureiters.nim @@ -21,6 +21,15 @@ discard """ 2 70 0 +(1, 1) +(1, 2) +(1, 3) +(2, 1) +(2, 2) +(2, 3) +(3, 1) +(3, 2) +(3, 3) ''' """ @@ -152,3 +161,12 @@ var love = iterator: int {.closure.} = for i in love(): echo i + +# bug #18474 +iterator pairs(): (int, int) {.closure.} = + for i in 1..3: + for j in 1..3: + yield (i, j) + +for pair in pairs(): + echo pair From 5a995ffc53ac7c1a51ab62440a58af2f4e43963b Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 6 Apr 2022 22:17:09 +0800 Subject: [PATCH 2868/3768] fix #18986; Import/except doesn't work on devel [backport: 1.6] (#19687) * fix #18986; Import/except doesn't work on devel [backport: 1.6] * add testcase --- compiler/importer.nim | 2 +- tests/converter/m18986.nim | 3 +++ tests/converter/t18986.nim | 10 ++++++++++ 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 tests/converter/m18986.nim create mode 100644 tests/converter/t18986.nim diff --git a/compiler/importer.nim b/compiler/importer.nim index 719b75f0f..c4e37c269 100644 --- a/compiler/importer.nim +++ b/compiler/importer.nim @@ -202,7 +202,7 @@ template addUnnamedIt(c: PContext, fromMod: PSym; filter: untyped) {.dirty.} = proc importAllSymbolsExcept(c: PContext, fromMod: PSym, exceptSet: IntSet) = c.addImport ImportedModule(m: fromMod, mode: importExcept, exceptSet: exceptSet) - addUnnamedIt(c, fromMod, it.sym.id notin exceptSet) + addUnnamedIt(c, fromMod, it.sym.name.id notin exceptSet) proc importAllSymbols*(c: PContext, fromMod: PSym) = c.addImport ImportedModule(m: fromMod, mode: importAll) diff --git a/tests/converter/m18986.nim b/tests/converter/m18986.nim new file mode 100644 index 000000000..0ebf343ae --- /dev/null +++ b/tests/converter/m18986.nim @@ -0,0 +1,3 @@ +import std/macros + +converter Lit*(x: uint): NimNode = newLit(x) diff --git a/tests/converter/t18986.nim b/tests/converter/t18986.nim new file mode 100644 index 000000000..ef300fa49 --- /dev/null +++ b/tests/converter/t18986.nim @@ -0,0 +1,10 @@ +discard """ + output: "Found a 0" +""" + +import m18986 except Lit +import std/macros + +# bug #18986 +var x = 0.uint +echo "Found a ", x From c322faaf384bfcde4dd5a6e6eb9355d10c2e9597 Mon Sep 17 00:00:00 2001 From: Miran Date: Wed, 6 Apr 2022 16:18:10 +0200 Subject: [PATCH 2869/3768] [backport] fix broken SSL tests (#19684) * [backport] fix broken SSL tests * remove a flaky one --- tests/untestable/thttpclient_ssl_remotenetwork.nim | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/tests/untestable/thttpclient_ssl_remotenetwork.nim b/tests/untestable/thttpclient_ssl_remotenetwork.nim index d8137e516..cbb86dc13 100644 --- a/tests/untestable/thttpclient_ssl_remotenetwork.nim +++ b/tests/untestable/thttpclient_ssl_remotenetwork.nim @@ -32,7 +32,7 @@ when enableRemoteNetworking and (defined(nimTestsEnableFlaky) or not defined(win good, bad, dubious, good_broken, bad_broken, dubious_broken CertTest = tuple[url:string, category:Category, desc: string] - const certificate_tests: array[0..55, CertTest] = [ + const certificate_tests: array[0..54, CertTest] = [ ("https://wrong.host.badssl.com/", bad, "wrong.host"), ("https://captive-portal.badssl.com/", bad, "captive-portal"), ("https://expired.badssl.com/", bad, "expired"), @@ -41,13 +41,12 @@ when enableRemoteNetworking and (defined(nimTestsEnableFlaky) or not defined(win ("https://untrusted-root.badssl.com/", bad, "untrusted-root"), ("https://revoked.badssl.com/", bad_broken, "revoked"), ("https://pinning-test.badssl.com/", bad_broken, "pinning-test"), - ("https://no-common-name.badssl.com/", dubious_broken, "no-common-name"), - ("https://no-subject.badssl.com/", dubious_broken, "no-subject"), - ("https://incomplete-chain.badssl.com/", dubious_broken, "incomplete-chain"), + ("https://no-common-name.badssl.com/", bad, "no-common-name"), + ("https://no-subject.badssl.com/", bad, "no-subject"), ("https://sha1-intermediate.badssl.com/", bad, "sha1-intermediate"), ("https://sha256.badssl.com/", good, "sha256"), - ("https://sha384.badssl.com/", good, "sha384"), - ("https://sha512.badssl.com/", good, "sha512"), + ("https://sha384.badssl.com/", bad, "sha384"), + ("https://sha512.badssl.com/", bad, "sha512"), ("https://1000-sans.badssl.com/", bad, "1000-sans"), ("https://10000-sans.badssl.com/", good_broken, "10000-sans"), ("https://ecc256.badssl.com/", good, "ecc256"), From abe3b0eb644c60be4423bd3595382a126c4746db Mon Sep 17 00:00:00 2001 From: locriacyber <74560659+locriacyber@users.noreply.github.com> Date: Wed, 6 Apr 2022 16:08:27 +0000 Subject: [PATCH 2870/3768] Load Google Fonts last (#19683) --- config/nimdoc.cfg | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/config/nimdoc.cfg b/config/nimdoc.cfg index c1074f344..0ae768bfd 100644 --- a/config/nimdoc.cfg +++ b/config/nimdoc.cfg @@ -233,10 +233,6 @@ doc.file = """ - - - - $title @@ -282,8 +278,6 @@ function main() { } } } - -window.addEventListener('DOMContentLoaded', main); @@ -301,7 +295,15 @@ window.addEventListener('DOMContentLoaded', main); + + + $analytics + + + + + """ From e649ddd09ba0b1195163f456dbf6b1308affec37 Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 7 Apr 2022 02:27:51 +0800 Subject: [PATCH 2871/3768] Revert "Load Google Fonts last (#19683)" (#19693) This reverts commit abe3b0eb644c60be4423bd3595382a126c4746db. --- config/nimdoc.cfg | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/config/nimdoc.cfg b/config/nimdoc.cfg index 0ae768bfd..c1074f344 100644 --- a/config/nimdoc.cfg +++ b/config/nimdoc.cfg @@ -233,6 +233,10 @@ doc.file = """ + + + + $title @@ -278,6 +282,8 @@ function main() { } } } + +window.addEventListener('DOMContentLoaded', main); @@ -295,15 +301,7 @@ function main() { - - - $analytics - - - - - """ From cb6ce80cb8dcf21733b437606ada1dfff9884316 Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Wed, 6 Apr 2022 20:28:37 +0200 Subject: [PATCH 2872/3768] std/tasks: fix spelling (#19691) [backport] why aren't these not being caught by style check options? --styleCheck:usages finds it. Co-authored-by: flywind --- lib/std/tasks.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/std/tasks.nim b/lib/std/tasks.nim index e2ea5377f..dbe2e265b 100644 --- a/lib/std/tasks.nim +++ b/lib/std/tasks.nim @@ -87,7 +87,7 @@ template checkIsolate(scratchAssignList: seq[NimNode], procParam, scratchDotExpr let isolatedTemp = genSym(nskTemp, "isoTemp") scratchAssignList.add newVarStmt(isolatedTemp, newCall(newidentNode("isolate"), procParam)) scratchAssignList.add newAssignment(scratchDotExpr, - newcall(newIdentNode("extract"), isolatedTemp)) + newCall(newIdentNode("extract"), isolatedTemp)) template addAllNode(assignParam: NimNode, procParam: NimNode) = let scratchDotExpr = newDotExpr(scratchIdent, formalParams[i][0]) @@ -154,7 +154,7 @@ macro toTask*(e: typed{nkCall | nkInfix | nkPrefix | nkPostfix | nkCommand | nkC error("'toTask'ed function cannot have a 'typed' or 'untyped' parameter") let seqType = nnkBracketExpr.newTree(newIdentNode("seq"), param[1]) - seqCallNode = newcall("@", e[i]) + seqCallNode = newCall("@", e[i]) addAllNode(seqType, seqCallNode) else: addAllNode(param, e[i]) From 065f568470af944cfc43ecfed8df6ac8021b7cbe Mon Sep 17 00:00:00 2001 From: Nan Xiao Date: Thu, 7 Apr 2022 02:28:58 +0800 Subject: [PATCH 2873/3768] No need to export pos from OptParser (#19688) Co-authored-by: flywind --- lib/pure/parseopt.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/parseopt.nim b/lib/pure/parseopt.nim index 71deaa535..fef354733 100644 --- a/lib/pure/parseopt.nim +++ b/lib/pure/parseopt.nim @@ -164,7 +164,7 @@ type ## ## To initialize it, use the ## `initOptParser proc<#initOptParser,string,set[char],seq[string]>`_. - pos*: int + pos: int inShortState: bool allowWhitespaceAfterColon: bool shortNoVal: set[char] From e1929deac7dd41595e998ce68ee703a113ce8746 Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 7 Apr 2022 07:11:25 +0800 Subject: [PATCH 2874/3768] remove useless compilation condition (#19690) ref https://github.com/nim-lang/Nim/pull/18546 --- testament/important_packages.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 456f42cd2..6b0b15fd8 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -100,7 +100,7 @@ pkg "nimcrypto", "nim r --path:. tests/testall.nim" # `--path:.` workaround need pkg "NimData", "nim c -o:nimdataa src/nimdata.nim" pkg "nimes", "nim c src/nimes.nim" pkg "nimfp", "nim c -o:nfp -r src/fp.nim" -pkg "nimgame2", "nim c -d:nimLegacyConvEnumEnum nimgame2/nimgame.nim" +pkg "nimgame2", "nim c nimgame2/nimgame.nim" # XXX Doesn't work with deprecated 'randomize', will create a PR. pkg "nimgen", "nim c -o:nimgenn -r src/nimgen/runcfg.nim" pkg "nimlsp", allowFailure = true From 0978276ed9882a25782f91795b355a0dca7b0d3b Mon Sep 17 00:00:00 2001 From: flywind Date: Thu, 7 Apr 2022 13:07:39 +0800 Subject: [PATCH 2875/3768] use two spaces indentation (#19696) --- lib/core/macros.nim | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 4d0e64868..ba19712cf 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -1505,20 +1505,20 @@ macro expandMacros*(body: typed): untyped = result = body proc extractTypeImpl(n: NimNode): NimNode = - ## attempts to extract the type definition of the given symbol - case n.kind - of nnkSym: # can extract an impl - result = n.getImpl.extractTypeImpl() - of nnkObjectTy, nnkRefTy, nnkPtrTy: result = n - of nnkBracketExpr: - if n.typeKind == ntyTypeDesc: - result = n[1].extractTypeImpl() - else: - doAssert n.typeKind == ntyGenericInst - result = n[0].getImpl() - of nnkTypeDef: - result = n[2] - else: error("Invalid node to retrieve type implementation of: " & $n.kind) + ## attempts to extract the type definition of the given symbol + case n.kind + of nnkSym: # can extract an impl + result = n.getImpl.extractTypeImpl() + of nnkObjectTy, nnkRefTy, nnkPtrTy: result = n + of nnkBracketExpr: + if n.typeKind == ntyTypeDesc: + result = n[1].extractTypeImpl() + else: + doAssert n.typeKind == ntyGenericInst + result = n[0].getImpl() + of nnkTypeDef: + result = n[2] + else: error("Invalid node to retrieve type implementation of: " & $n.kind) proc customPragmaNode(n: NimNode): NimNode = expectKind(n, {nnkSym, nnkDotExpr, nnkBracketExpr, nnkTypeOfExpr, nnkCheckedFieldExpr}) From 810d5e91e435fffb7898ac26b5e4cc5fbb1678d7 Mon Sep 17 00:00:00 2001 From: Ivan Yonchovski Date: Thu, 7 Apr 2022 14:39:27 +0300 Subject: [PATCH 2876/3768] [nimsuggest] return the type when on symbol in let/var (#19639) - make sure `suggestSym` is called after `PSym.typ` is set. --- compiler/semstmts.nim | 9 ++++++--- nimsuggest/tests/tdef_let.nim | 7 +++++++ 2 files changed, 13 insertions(+), 3 deletions(-) create mode 100644 nimsuggest/tests/tdef_let.nim diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index c2b385c80..3513fd767 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -306,7 +306,7 @@ proc identWithin(n: PNode, s: PIdent): bool = if identWithin(n[i], s): return true result = n.kind == nkSym and n.sym.name.id == s.id -proc semIdentDef(c: PContext, n: PNode, kind: TSymKind): PSym = +proc semIdentDef(c: PContext, n: PNode, kind: TSymKind, reportToNimsuggest = true): PSym = if isTopLevel(c): result = semIdentWithPragma(c, kind, n, {sfExported}) incl(result.flags, sfGlobal) @@ -330,7 +330,8 @@ proc semIdentDef(c: PContext, n: PNode, kind: TSymKind): PSym = discard result = n.info let info = getLineInfo(n) - suggestSym(c.graph, info, result, c.graph.usageSym) + if reportToNimsuggest: + suggestSym(c.graph, info, result, c.graph.usageSym) proc checkNilable(c: PContext; v: PSym) = if {sfGlobal, sfImportc} * v.flags == {sfGlobal} and v.typ.requiresInit: @@ -660,7 +661,7 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = if a.kind != nkVarTuple: typ else: tup[j]) addToVarSection(c, result, n, a) continue - var v = semIdentDef(c, a[j], symkind) + var v = semIdentDef(c, a[j], symkind, false) styleCheckDef(c.config, v) onDef(a[j].info, v) if sfGenSym notin v.flags: @@ -725,6 +726,8 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = if v.flags * {sfGlobal, sfThread} == {sfGlobal}: message(c.config, v.info, hintGlobalVar) + suggestSym(c.graph, v.info, v, c.graph.usageSym) + proc semConst(c: PContext, n: PNode): PNode = result = copyNode(n) inc c.inStaticContext diff --git a/nimsuggest/tests/tdef_let.nim b/nimsuggest/tests/tdef_let.nim new file mode 100644 index 000000000..3e9456d2f --- /dev/null +++ b/nimsuggest/tests/tdef_let.nim @@ -0,0 +1,7 @@ +discard """ +$nimsuggest --tester $file +>def $1 +def;;skLet;;tdef_let.intVar;;int;;$file;;7;;4;;"";;100 +""" + +let int#[!]#Var = 10 From c8aeea9d6205198306c3cd802136915f909ba5dc Mon Sep 17 00:00:00 2001 From: flywind Date: Fri, 8 Apr 2022 02:56:34 +0800 Subject: [PATCH 2877/3768] improve the error messages for std/tasks [backport: 1.6] (#19695) --- lib/std/tasks.nim | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/std/tasks.nim b/lib/std/tasks.nim index dbe2e265b..3ceb4c8ba 100644 --- a/lib/std/tasks.nim +++ b/lib/std/tasks.nim @@ -111,10 +111,10 @@ macro toTask*(e: typed{nkCall | nkInfix | nkPrefix | nkPostfix | nkCommand | nkC when compileOption("threads"): if not isGcSafe(e[0]): - error("'toTask' takes a GC safe call expression") + error("'toTask' takes a GC safe call expression", e) if hasClosure(e[0]): - error("closure call is not allowed") + error("closure call is not allowed", e) if e.len > 1: let scratchIdent = genSym(kind = nskTemp, ident = "scratch") @@ -141,17 +141,17 @@ macro toTask*(e: typed{nkCall | nkInfix | nkPrefix | nkPostfix | nkCommand | nkC param = param[0] if param.typeKind in {ntyExpr, ntyStmt}: - error("'toTask'ed function cannot have a 'typed' or 'untyped' parameter") + error("'toTask'ed function cannot have a 'typed' or 'untyped' parameter", e) case param.kind of nnkVarTy: - error("'toTask'ed function cannot have a 'var' parameter") + error("'toTask'ed function cannot have a 'var' parameter", e) of nnkBracketExpr: if param[0].typeKind == ntyTypeDesc: callNode.add nnkExprEqExpr.newTree(formalParams[i][0], e[i]) elif param[0].typeKind in {ntyVarargs, ntyOpenArray}: if param[1].typeKind in {ntyExpr, ntyStmt}: - error("'toTask'ed function cannot have a 'typed' or 'untyped' parameter") + error("'toTask'ed function cannot have a 'typed' or 'untyped' parameter", e) let seqType = nnkBracketExpr.newTree(newIdentNode("seq"), param[1]) seqCallNode = newCall("@", e[i]) @@ -167,7 +167,7 @@ macro toTask*(e: typed{nkCall | nkInfix | nkPrefix | nkPostfix | nkCommand | nkC of nnkCharLit..nnkNilLit: callNode.add nnkExprEqExpr.newTree(formalParams[i][0], e[i]) else: - error("not supported type kinds") + error("'toTask'ed function cannot have a parameter of " & $param.kind & " kind", e) let scratchObjType = genSym(kind = nskType, ident = "ScratchObj") let scratchObj = nnkTypeSection.newTree( From 1807de38e52f45c2fb88dac9b99b47729b12ebae Mon Sep 17 00:00:00 2001 From: flywind Date: Fri, 8 Apr 2022 02:57:50 +0800 Subject: [PATCH 2878/3768] add testcase for #16462 (#19692) --- tests/enum/m16462_1.nim | 3 +++ tests/enum/m16462_2.nim | 3 +++ tests/enum/t16462.nim | 5 +++++ 3 files changed, 11 insertions(+) create mode 100644 tests/enum/m16462_1.nim create mode 100644 tests/enum/m16462_2.nim create mode 100644 tests/enum/t16462.nim diff --git a/tests/enum/m16462_1.nim b/tests/enum/m16462_1.nim new file mode 100644 index 000000000..631c63256 --- /dev/null +++ b/tests/enum/m16462_1.nim @@ -0,0 +1,3 @@ +type + Scancode* {.pure.} = enum + SCANCODE_LEFT = 80 \ No newline at end of file diff --git a/tests/enum/m16462_2.nim b/tests/enum/m16462_2.nim new file mode 100644 index 000000000..631c63256 --- /dev/null +++ b/tests/enum/m16462_2.nim @@ -0,0 +1,3 @@ +type + Scancode* {.pure.} = enum + SCANCODE_LEFT = 80 \ No newline at end of file diff --git a/tests/enum/t16462.nim b/tests/enum/t16462.nim new file mode 100644 index 000000000..9f38286bb --- /dev/null +++ b/tests/enum/t16462.nim @@ -0,0 +1,5 @@ +import m16462_1 except Scancode +import m16462_2 + +# bug #16462 +let a = SCANCODE_LEFT \ No newline at end of file From e78ef57c93d66da483e0482ce0907dfe16dc8d27 Mon Sep 17 00:00:00 2001 From: Timothee Cour Date: Thu, 7 Apr 2022 14:38:01 -0700 Subject: [PATCH 2879/3768] typetraits: add toSigned, toUnsigned (#18445) * typetraits: add toSigned, toUnsigned * improve and add tests Co-authored-by: Andreas Rumpf Co-authored-by: flywind --- lib/pure/bitops.nim | 26 ++++++++-------------- lib/pure/typetraits.nim | 38 +++++++++++++++++++++++++++----- lib/std/private/bitops_utils.nim | 15 ++++++++----- lib/system/countbits_impl.nim | 6 ++--- tests/metatype/ttypetraits.nim | 15 +++++++++++++ tests/stdlib/tbitops_utils.nim | 14 ++++++++++++ tests/stdlib/ttypetraits.nim | 4 ++-- 7 files changed, 85 insertions(+), 33 deletions(-) create mode 100644 tests/stdlib/tbitops_utils.nim diff --git a/lib/pure/bitops.nim b/lib/pure/bitops.nim index 1b3838621..a518c25d2 100644 --- a/lib/pure/bitops.nim +++ b/lib/pure/bitops.nim @@ -27,7 +27,7 @@ import macros import std/private/since -from std/private/bitops_utils import forwardImpl, toUnsigned +from std/private/bitops_utils import forwardImpl, castToUnsigned func bitnot*[T: SomeInteger](x: T): T {.magic: "BitnotI".} ## Computes the `bitwise complement` of the integer `x`. @@ -72,7 +72,7 @@ func bitsliced*[T: SomeInteger](v: T; slice: Slice[int]): T {.inline, since: (1, let upmost = sizeof(T) * 8 - 1 - uv = when v is SomeUnsignedInt: v else: v.toUnsigned + uv = v.castToUnsigned (uv shl (upmost - slice.b) shr (upmost - slice.b + slice.a)).T proc bitslice*[T: SomeInteger](v: var T; slice: Slice[int]) {.inline, since: (1, 3).} = @@ -84,7 +84,7 @@ proc bitslice*[T: SomeInteger](v: var T; slice: Slice[int]) {.inline, since: (1, let upmost = sizeof(T) * 8 - 1 - uv = when v is SomeUnsignedInt: v else: v.toUnsigned + uv = v.castToUnsigned v = (uv shl (upmost - slice.b) shr (upmost - slice.b + slice.a)).T func toMask*[T: SomeInteger](slice: Slice[int]): T {.inline, since: (1, 3).} = @@ -95,10 +95,7 @@ func toMask*[T: SomeInteger](slice: Slice[int]): T {.inline, since: (1, 3).} = let upmost = sizeof(T) * 8 - 1 - bitmask = when T is SomeUnsignedInt: - bitnot(0.T) - else: - bitnot(0.T).toUnsigned + bitmask = bitnot(0.T).castToUnsigned (bitmask shl (upmost - slice.b + slice.a) shr (upmost - slice.b)).T proc masked*[T: SomeInteger](v, mask :T): T {.inline, since: (1, 3).} = @@ -505,8 +502,7 @@ func parityBits*(x: SomeInteger): int {.inline.} = # Can be used a base if creating ASM version. # https://stackoverflow.com/questions/21617970/how-to-check-if-value-has-even-parity-of-bits-or-odd - when x is SomeSignedInt: - let x = x.toUnsigned + let x = x.castToUnsigned when nimvm: result = forwardImpl(parityImpl, x) else: @@ -529,8 +525,7 @@ func firstSetBit*(x: SomeInteger): int {.inline.} = doAssert firstSetBit(0b0000_1111'u8) == 1 # GCC builtin 'builtin_ffs' already handle zero input. - when x is SomeSignedInt: - let x = x.toUnsigned + let x = x.castToUnsigned when nimvm: when noUndefined: if x == 0: @@ -572,8 +567,7 @@ func fastLog2*(x: SomeInteger): int {.inline.} = doAssert fastLog2(0b0000_1000'u8) == 3 doAssert fastLog2(0b0000_1111'u8) == 3 - when x is SomeSignedInt: - let x = x.toUnsigned + let x = x.castToUnsigned when noUndefined: if x == 0: return -1 @@ -615,8 +609,7 @@ func countLeadingZeroBits*(x: SomeInteger): int {.inline.} = doAssert countLeadingZeroBits(0b0000_1000'u8) == 4 doAssert countLeadingZeroBits(0b0000_1111'u8) == 4 - when x is SomeSignedInt: - let x = x.toUnsigned + let x = x.castToUnsigned when noUndefined: if x == 0: return 0 @@ -644,8 +637,7 @@ func countTrailingZeroBits*(x: SomeInteger): int {.inline.} = doAssert countTrailingZeroBits(0b0000_1000'u8) == 3 doAssert countTrailingZeroBits(0b0000_1111'u8) == 0 - when x is SomeSignedInt: - let x = x.toUnsigned + let x = x.castToUnsigned when noUndefined: if x == 0: return 0 diff --git a/lib/pure/typetraits.nim b/lib/pure/typetraits.nim index 3fc1c7c5c..c20f9e645 100644 --- a/lib/pure/typetraits.nim +++ b/lib/pure/typetraits.nim @@ -35,7 +35,7 @@ runnableExamples: assert C[float] is HoleyEnum proc name*(t: typedesc): string {.magic: "TypeTrait".} = - ## Returns the name of the given type. + ## Returns the name of `t`. ## ## Alias for `system.\`$\`(t) `_ since Nim v0.20. runnableExamples: @@ -43,7 +43,7 @@ proc name*(t: typedesc): string {.magic: "TypeTrait".} = doAssert name(seq[string]) == "seq[string]" proc arity*(t: typedesc): int {.magic: "TypeTrait".} = - ## Returns the arity of the given type. This is the number of "type" + ## Returns the arity of `t`. This is the number of "type" ## components or the number of generic parameters a given type `t` has. runnableExamples: doAssert arity(int) == 0 @@ -92,8 +92,7 @@ proc stripGenericParams*(t: typedesc): typedesc {.magic: "TypeTrait".} = doAssert stripGenericParams(int) is int proc supportsCopyMem*(t: typedesc): bool {.magic: "TypeTrait".} - ## This trait returns true if the type `t` is safe to use for - ## `copyMem`:idx:. + ## Returns true if `t` is safe to use for `copyMem`:idx:. ## ## Other languages name a type like these `blob`:idx:. @@ -289,9 +288,38 @@ since (1, 1): proc hasClosureImpl(n: NimNode): bool = discard "see compiler/vmops.nim" proc hasClosure*(fn: NimNode): bool {.since: (1, 5, 1).} = - ## Return true if the func/proc/etc `fn` has `closure`. + ## Returns true if the func/proc/etc `fn` has `closure`. ## `fn` has to be a resolved symbol of kind `nnkSym`. This ## implies that the macro that calls this proc should accept `typed` ## arguments and not `untyped` arguments. expectKind fn, nnkSym result = hasClosureImpl(fn) + +template toUnsigned*(T: typedesc[SomeInteger and not range]): untyped = + ## Returns an unsigned type with same bit size as `T`. + runnableExamples: + assert int8.toUnsigned is uint8 + assert uint.toUnsigned is uint + assert int.toUnsigned is uint + # range types are currently unsupported: + assert not compiles(toUnsigned(range[0..7])) + when T is int8: uint8 + elif T is int16: uint16 + elif T is int32: uint32 + elif T is int64: uint64 + elif T is int: uint + else: T + +template toSigned*(T: typedesc[SomeInteger and not range]): untyped = + ## Returns a signed type with same bit size as `T`. + runnableExamples: + assert int8.toSigned is int8 + assert uint16.toSigned is int16 + # range types are currently unsupported: + assert not compiles(toSigned(range[0..7])) + when T is uint8: int8 + elif T is uint16: int16 + elif T is uint32: int32 + elif T is uint64: int64 + elif T is uint: int + else: T diff --git a/lib/std/private/bitops_utils.nim b/lib/std/private/bitops_utils.nim index d54977b4e..0b9484416 100644 --- a/lib/std/private/bitops_utils.nim +++ b/lib/std/private/bitops_utils.nim @@ -10,8 +10,13 @@ template forwardImpl*(impl, arg) {.dirty.} = else: impl(x.uint64) -template toUnsigned*(x: int8): uint8 = cast[uint8](x) -template toUnsigned*(x: int16): uint16 = cast[uint16](x) -template toUnsigned*(x: int32): uint32 = cast[uint32](x) -template toUnsigned*(x: int64): uint64 = cast[uint64](x) -template toUnsigned*(x: int): uint = cast[uint](x) +# this could also be implemented via: +# import std/typetraits +# template castToUnsigned*(x: SomeInteger): auto = cast[toUnsigned(typeof(x))](x) + +template castToUnsigned*(x: int8): uint8 = cast[uint8](x) +template castToUnsigned*(x: int16): uint16 = cast[uint16](x) +template castToUnsigned*(x: int32): uint32 = cast[uint32](x) +template castToUnsigned*(x: int64): uint64 = cast[uint64](x) +template castToUnsigned*(x: int): uint = cast[uint](x) +template castToUnsigned*[T: SomeUnsignedInt](x: T): T = x diff --git a/lib/system/countbits_impl.nim b/lib/system/countbits_impl.nim index 020423e01..34969cb32 100644 --- a/lib/system/countbits_impl.nim +++ b/lib/system/countbits_impl.nim @@ -9,8 +9,7 @@ ## Contains the used algorithms for counting bits. -from std/private/bitops_utils import forwardImpl, toUnsigned - +from std/private/bitops_utils import forwardImpl, castToUnsigned const useBuiltins* = not defined(noIntrinsicsBitOpts) const noUndefined* = defined(noUndefinedBitOpts) @@ -65,8 +64,7 @@ func countSetBitsImpl*(x: SomeInteger): int {.inline.} = ## Counts the set bits in an integer (also called `Hamming weight`:idx:). # TODO: figure out if ICC support _popcnt32/_popcnt64 on platform without POPCNT. # like GCC and MSVC - when x is SomeSignedInt: - let x = x.toUnsigned + let x = x.castToUnsigned when nimvm: result = forwardImpl(countBitsImpl, x) else: diff --git a/tests/metatype/ttypetraits.nim b/tests/metatype/ttypetraits.nim index 3ff5c5ea6..bfaa23057 100644 --- a/tests/metatype/ttypetraits.nim +++ b/tests/metatype/ttypetraits.nim @@ -1,6 +1,21 @@ import typetraits import macros +block: # toUnsigned, toSigned + var a1: toSigned(int16) + doAssert a1 is int16 + var a2: toSigned(uint16) + doAssert $a2.typeof == "int16" + doAssert toSigned(uint32) is int32 + doAssert uint64.toSigned is int64 + doAssert int64.toSigned is int64 + doAssert int64.toUnsigned is uint64 + doAssert int.toUnsigned is uint + doAssert $uint.toUnsigned == "uint" + # disallowed for now + doAssert not compiles(toUnsigned(range[0..7])) + doAssert not compiles(toSigned(range[0..7])) + block: # isNamedTuple type Foo1 = (a:1,).type type Foo2 = (Field0:1,).type diff --git a/tests/stdlib/tbitops_utils.nim b/tests/stdlib/tbitops_utils.nim new file mode 100644 index 000000000..b571baeae --- /dev/null +++ b/tests/stdlib/tbitops_utils.nim @@ -0,0 +1,14 @@ +import std/private/bitops_utils + +template chk(a, b) = + let a2 = castToUnsigned(a) + doAssert a2 == b + doAssert type(a2) is type(b) + doAssert type(b) is type(a2) + +chk 1'i8, 1'u8 +chk -1'i8, 255'u8 +chk 1'u8, 1'u8 +chk 1'u, 1'u +chk -1, cast[uint](-1) +chk -1'i64, cast[uint64](-1) diff --git a/tests/stdlib/ttypetraits.nim b/tests/stdlib/ttypetraits.nim index de8259ab0..799bcf6e2 100644 --- a/tests/stdlib/ttypetraits.nim +++ b/tests/stdlib/ttypetraits.nim @@ -2,10 +2,10 @@ discard """ targets: "c cpp js" """ +# xxx merge with tests/metatype/ttypetraits.nim + import std/typetraits - - macro testClosure(fn: typed, flag: static bool) = if flag: doAssert hasClosure(fn) From 00775f6880733695d187d84bb742e8c9d6c65d6d Mon Sep 17 00:00:00 2001 From: flywind Date: Fri, 8 Apr 2022 14:04:46 +0800 Subject: [PATCH 2880/3768] fix stylecheck bug with nre (#19356) * stylecheck usages part two: stdlib cleanup typeinfo.nim: importCompilerProc => importcompilerproc nre.nim: newLineFlags => newlineFlags system.nim: JSRoot => JsRoot ref #19319 * prefer importCompilerProc * fix stylecheck error with asyncdispatch it is a partial regression since #12842 * add tests * don't use echo in tests * fix stylecheck bug with nre * Update compiler/linter.nim * no need to check dotexpr again * neither did let/var/const --- compiler/semexprs.nim | 9 ++++++--- tests/stylecheck/taccept.nim | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index f224bd0b3..702b47929 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1188,7 +1188,8 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = let s = getGenSym(c, sym) case s.kind of skConst: - markUsed(c, n.info, s) + if n.kind != nkDotExpr: # dotExpr is already checked by builtinFieldAccess + markUsed(c, n.info, s) onUse(n.info, s) let typ = skipTypes(s.typ, abstractInst-{tyTypeDesc}) case typ.kind @@ -1249,7 +1250,8 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = if s.magic == mNimvm: localError(c.config, n.info, "illegal context for 'nimvm' magic") - markUsed(c, n.info, s) + if n.kind != nkDotExpr: # dotExpr is already checked by builtinFieldAccess + markUsed(c, n.info, s) onUse(n.info, s) result = newSymNode(s, n.info) # We cannot check for access to outer vars for example because it's still @@ -1270,7 +1272,8 @@ proc semSym(c: PContext, n: PNode, sym: PSym, flags: TExprFlags): PNode = n.typ = s.typ return n of skType: - markUsed(c, n.info, s) + if n.kind != nkDotExpr: # dotExpr is already checked by builtinFieldAccess + markUsed(c, n.info, s) onUse(n.info, s) if s.typ.kind == tyStatic and s.typ.base.kind != tyNone and s.typ.n != nil: return s.typ.n diff --git a/tests/stylecheck/taccept.nim b/tests/stylecheck/taccept.nim index 6fb55f835..afe02a65c 100644 --- a/tests/stylecheck/taccept.nim +++ b/tests/stylecheck/taccept.nim @@ -2,7 +2,7 @@ discard """ matrix: "--styleCheck:error --styleCheck:usages" """ -import asyncdispatch +import std/[asyncdispatch, nre] type Name = object From ded8b0e5410518ffed24b46565eb6e42f18a63f1 Mon Sep 17 00:00:00 2001 From: flywind Date: Sat, 9 Apr 2022 23:50:57 +0800 Subject: [PATCH 2881/3768] rewrite docs JS in Nim (#19701) * rewrite docs JS in Nim * fixup * fix nimdoc/rsttester --- config/nimdoc.cfg | 43 ------------------- nimdoc/rst2html/expected/rst_examples.html | 43 ------------------- .../expected/index.html | 43 ------------------- .../expected/theindex.html | 43 ------------------- .../expected/subdir/subdir_b/utils.html | 43 ------------------- nimdoc/testproject/expected/testproject.html | 43 ------------------- nimdoc/testproject/expected/theindex.html | 43 ------------------- tools/dochack/dochack.nim | 36 +++++++++++++++- 8 files changed, 35 insertions(+), 302 deletions(-) diff --git a/config/nimdoc.cfg b/config/nimdoc.cfg index c1074f344..4efa1f637 100644 --- a/config/nimdoc.cfg +++ b/config/nimdoc.cfg @@ -243,49 +243,6 @@ doc.file = """ - -
      diff --git a/nimdoc/rst2html/expected/rst_examples.html b/nimdoc/rst2html/expected/rst_examples.html index 1df91ff1f..2b5218d9f 100644 --- a/nimdoc/rst2html/expected/rst_examples.html +++ b/nimdoc/rst2html/expected/rst_examples.html @@ -22,49 +22,6 @@ - -
      diff --git a/nimdoc/test_out_index_dot_html/expected/index.html b/nimdoc/test_out_index_dot_html/expected/index.html index 69dd2eb2e..c6a116bd6 100644 --- a/nimdoc/test_out_index_dot_html/expected/index.html +++ b/nimdoc/test_out_index_dot_html/expected/index.html @@ -22,49 +22,6 @@ - -
      diff --git a/nimdoc/test_out_index_dot_html/expected/theindex.html b/nimdoc/test_out_index_dot_html/expected/theindex.html index 34ddf8f6a..8ee62a330 100644 --- a/nimdoc/test_out_index_dot_html/expected/theindex.html +++ b/nimdoc/test_out_index_dot_html/expected/theindex.html @@ -22,49 +22,6 @@ - -
      diff --git a/nimdoc/testproject/expected/subdir/subdir_b/utils.html b/nimdoc/testproject/expected/subdir/subdir_b/utils.html index 574ac5b9b..f94da7f40 100644 --- a/nimdoc/testproject/expected/subdir/subdir_b/utils.html +++ b/nimdoc/testproject/expected/subdir/subdir_b/utils.html @@ -22,49 +22,6 @@ - -
      diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html index e45492dac..cba9391af 100644 --- a/nimdoc/testproject/expected/testproject.html +++ b/nimdoc/testproject/expected/testproject.html @@ -22,49 +22,6 @@ - -
      diff --git a/nimdoc/testproject/expected/theindex.html b/nimdoc/testproject/expected/theindex.html index 88d4b86aa..47fae2491 100644 --- a/nimdoc/testproject/expected/theindex.html +++ b/nimdoc/testproject/expected/theindex.html @@ -22,49 +22,6 @@ - -
      diff --git a/tools/dochack/dochack.nim b/tools/dochack/dochack.nim index 3a663808d..c7b8232ef 100644 --- a/tools/dochack/dochack.nim +++ b/tools/dochack/dochack.nim @@ -1,6 +1,39 @@ import dom import fuzzysearch + +proc switchTheme(event: Event) = + if event.target.checked: + document.documentElement.setAttribute("data-theme", "dark") + window.localStorage.setItem("theme", "dark") + else: + document.documentElement.setAttribute("data-theme", "light") + window.localStorage.setItem("theme", "light") + + +proc nimThemeSwitch(event: Event) {.exportC.} = + var pragmaDots = document.getElementsByClassName("pragmadots") + for i in 0.. 0: + document.documentElement.setAttribute("data-theme", currentTheme); + + if currentTheme == "dark" and toggleSwitch != nil: + toggleSwitch.checked = true + proc textContent(e: Element): cstring {. importcpp: "#.textContent", nodecl.} @@ -396,5 +429,6 @@ proc copyToClipboard*() {.exportc.} = """ .} - + copyToClipboard() +window.addEventListener("DOMContentLoaded", nimThemeSwitch) \ No newline at end of file From 152dab09838adc5a4ab8dc220c5017c868563467 Mon Sep 17 00:00:00 2001 From: Leon Date: Sun, 10 Apr 2022 01:51:44 +1000 Subject: [PATCH 2882/3768] fix: Fix introductory macro examples (#19706) Co-authored-by: adigitoleo --- doc/tut3.rst | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/doc/tut3.rst b/doc/tut3.rst index 1c497f1f5..c2c956107 100644 --- a/doc/tut3.rst +++ b/doc/tut3.rst @@ -188,21 +188,35 @@ Backticks are used to insert code from `NimNode` symbols into the generated expression. .. code-block:: nim - macro a(i) = quote do: let `i` = 0 + :test: "nim c $1" + import std/macros + macro a(i) = quote do: + let `i` = 0 + a b + doAssert b == 0 A custom prefix operator can be defined whenever backticks are needed. .. code-block:: nim - macro a(i) = quote("@") do: assert @i == 0 + :test: "nim c $1" + import std/macros + macro a(i) = quote("@") do: + assert @i == 0 + let b = 0 a b The injected symbol needs accent quoted when it resolves to a symbol. .. code-block:: nim - macro a(i) = quote("@") do: let `@i` == 0 + :test: "nim c $1" + import std/macros + macro a(i) = quote("@") do: + let `@i` = 0 + a b + doAssert b == 0 Make sure to inject only symbols of type `NimNode` into the generated syntax tree. You can use `newLit` to convert arbitrary values into From a77ffdb7ea4ec66983d3481cc486c821a5df6b1b Mon Sep 17 00:00:00 2001 From: flywind Date: Sat, 9 Apr 2022 23:52:11 +0800 Subject: [PATCH 2883/3768] fix #17286 nim check -b:js works (#19704) * fix #17286 nim check -b:js works * fix --- compiler/main.nim | 2 ++ tests/misc/t17286.nim | 16 ++++++++++++++++ 2 files changed, 18 insertions(+) create mode 100644 tests/misc/t17286.nim diff --git a/compiler/main.nim b/compiler/main.nim index e27a4f6d1..a4425e510 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -65,6 +65,8 @@ proc commandCheck(graph: ModuleGraph) = if optWasNimscript in conf.globalOptions: defineSymbol(conf.symbols, "nimscript") defineSymbol(conf.symbols, "nimconfig") + elif conf.backend == backendJs: + setTarget(conf.target, osJS, cpuJS) semanticPasses(graph) # use an empty backend for semantic checking only compileProject(graph) diff --git a/tests/misc/t17286.nim b/tests/misc/t17286.nim new file mode 100644 index 000000000..3a54e624e --- /dev/null +++ b/tests/misc/t17286.nim @@ -0,0 +1,16 @@ +discard """ + cmd: "nim check -b:js $file" + action: "compile" +""" + +# bug #17286 + +import std/compilesettings + +static: + doAssert querySetting(backend) == "js" + doAssert defined(js) + doAssert not defined(c) + +import random +randomize() \ No newline at end of file From 26acc97864ede8e19dddd52aa04e0139da356e3e Mon Sep 17 00:00:00 2001 From: Jason Beetham Date: Mon, 11 Apr 2022 06:27:12 -0600 Subject: [PATCH 2884/3768] StringStreams no longer errors when intialized with literals on arc/orc (#19708) --- lib/pure/streams.nim | 2 ++ tests/stdlib/tstreams.nim | 2 ++ 2 files changed, 4 insertions(+) diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim index 7ad81685f..84451f989 100644 --- a/lib/pure/streams.nim +++ b/lib/pure/streams.nim @@ -1274,6 +1274,8 @@ else: # after 1.3 or JS not defined new(result) result.data = s + when defined(gcOrc) or defined(gcArc): + prepareMutation(result.data) # Allows us to mutate using `addr` logic like `copyMem`, otherwise it errors. result.pos = 0 result.closeImpl = ssClose result.atEndImpl = ssAtEnd diff --git a/tests/stdlib/tstreams.nim b/tests/stdlib/tstreams.nim index c2ceed624..d70d4995a 100644 --- a/tests/stdlib/tstreams.nim +++ b/tests/stdlib/tstreams.nim @@ -74,3 +74,5 @@ block: doAssert(ss.peekLine(str)) doAssert(str == "uick brown fox jumped over the lazy dog.") doAssert(ss.getPosition == 5) # haven't moved + ss.setPosition(0) # Ensure we dont error with writing over literals on arc/orc #19707 + ss.write("hello") From 26bcf18f918b2bcc7b6b2e230ae74d7726dca6d0 Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 12 Apr 2022 15:48:48 +0800 Subject: [PATCH 2885/3768] fix #19680; check if stderr is static (#19709) --- compiler/vmgen.nim | 2 +- tests/misc/tvarious1.nim | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 72bcef4db..dadcc3865 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1479,7 +1479,7 @@ proc checkCanEval(c: PCtx; n: PNode) = # proc foo() = var x ... let s = n.sym if {sfCompileTime, sfGlobal} <= s.flags: return - if s.importcCondVar: return + if compiletimeFFI in c.config.features and s.importcCondVar: return if s.kind in {skVar, skTemp, skLet, skParam, skResult} and not s.isOwnedBy(c.prc.sym) and s.owner != c.module and c.mode != emRepl: # little hack ahead for bug #12612: assume gensym'ed variables diff --git a/tests/misc/tvarious1.nim b/tests/misc/tvarious1.nim index 9c0b541db..9c912ee8e 100644 --- a/tests/misc/tvarious1.nim +++ b/tests/misc/tvarious1.nim @@ -46,3 +46,13 @@ echo value var ys = @[4.1, 5.6, 7.2, 1.7, 9.3, 4.4, 3.2] #var x = int(ys.high / 2) #echo ys[x] # Works echo ys[int(ys.high / 2)] # Doesn't work + + +# bug #19680 +var here = "" +when stderr is static: + doAssert false +else: + here = "works" + +doAssert here == "works" From 98cebad7debddfb147ee22bc6f3d81221582c4d6 Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 13 Apr 2022 17:53:02 +0800 Subject: [PATCH 2886/3768] enable style:usages for stdlib tests [backport: 1.6] (#19715) * enable style:usages for stdlib tests * freeAddrInfo * more tests * importc * bufSize * fix more * => parseSql and renderSql --- lib/impure/db_mysql.nim | 20 +++--- lib/posix/posix.nim | 2 +- lib/pure/asyncdispatch.nim | 6 +- lib/pure/asyncnet.nim | 6 +- lib/pure/cookies.nim | 2 +- lib/pure/ioselects/ioselectors_epoll.nim | 2 +- lib/pure/ioselects/ioselectors_select.nim | 16 ++--- lib/pure/json.nim | 12 ++-- lib/pure/nativesockets.nim | 4 +- lib/pure/net.nim | 14 ++-- lib/pure/parsesql.nim | 12 ++-- lib/pure/pegs.nim | 4 +- lib/pure/segfaults.nim | 2 +- lib/std/strbasics.nim | 2 +- lib/std/tasks.nim | 2 +- lib/windows/registry.nim | 18 ++--- lib/windows/winlean.nim | 2 +- tests/stdlib/config.nims | 2 + tests/stdlib/nre/misc.nim | 4 +- tests/stdlib/talgorithm.nim | 4 +- tests/stdlib/tasynchttpserver.nim | 8 +-- tests/stdlib/tbase64.nim | 6 +- tests/stdlib/tbitops.nim | 4 +- tests/stdlib/thttpclient.nim | 2 +- tests/stdlib/tjsonmacro.nim | 4 +- tests/stdlib/tjsonutils.nim | 2 +- tests/stdlib/tnet.nim | 2 +- tests/stdlib/tnetdial.nim | 2 +- tests/stdlib/tosproc.nim | 2 +- tests/stdlib/tparsesql.nim | 86 +++++++++++------------ tests/stdlib/trst.nim | 10 +-- tests/stdlib/trstgen.nim | 8 +-- tests/stdlib/tsha1.nim | 2 +- tests/stdlib/tssl.nim | 4 +- tests/stdlib/tstrbasics.nim | 2 +- tests/stdlib/tstreams.nim | 2 +- tests/stdlib/tstrset.nim | 2 +- tests/stdlib/ttimes.nim | 2 +- tests/stdlib/ttypeinfo.nim | 2 +- tests/stdlib/ttypeinfo.nims | 1 + tests/stdlib/tunicode.nim | 4 +- tests/stdlib/tvarints.nim | 2 +- 42 files changed, 149 insertions(+), 146 deletions(-) create mode 100644 tests/stdlib/config.nims create mode 100644 tests/stdlib/ttypeinfo.nims diff --git a/lib/impure/db_mysql.nim b/lib/impure/db_mysql.nim index 242ea1b0d..df878e25a 100644 --- a/lib/impure/db_mysql.nim +++ b/lib/impure/db_mysql.nim @@ -150,17 +150,17 @@ proc tryExec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): bool {. tags: [ReadDbEffect, WriteDbEffect].} = ## tries to execute the query and returns true if successful, false otherwise. var q = dbFormat(query, args) - return mysql.realQuery(PMySQL db, q, q.len) == 0'i32 + return mysql.real_query(PMySQL db, q, q.len) == 0'i32 proc rawExec(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) = var q = dbFormat(query, args) - if mysql.realQuery(PMySQL db, q, q.len) != 0'i32: dbError(db) + if mysql.real_query(PMySQL db, q, q.len) != 0'i32: dbError(db) proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {. tags: [ReadDbEffect, WriteDbEffect].} = ## executes the query and raises EDB if not successful. var q = dbFormat(query, args) - if mysql.realQuery(PMySQL db, q, q.len) != 0'i32: dbError(db) + if mysql.real_query(PMySQL db, q, q.len) != 0'i32: dbError(db) proc newRow(L: int): Row = newSeq(result, L) @@ -168,7 +168,7 @@ proc newRow(L: int): Row = proc properFreeResult(sqlres: mysql.PRES, row: cstringArray) = if row != nil: - while mysql.fetchRow(sqlres) != nil: discard + while mysql.fetch_row(sqlres) != nil: discard mysql.freeResult(sqlres) iterator fastRows*(db: DbConn, query: SqlQuery, @@ -190,7 +190,7 @@ iterator fastRows*(db: DbConn, query: SqlQuery, backup: Row newSeq(result, L) while true: - row = mysql.fetchRow(sqlres) + row = mysql.fetch_row(sqlres) if row == nil: break for i in 0..L-1: setLen(result[i], 0) @@ -209,7 +209,7 @@ iterator instantRows*(db: DbConn, query: SqlQuery, let L = int(mysql.numFields(sqlres)) var row: cstringArray while true: - row = mysql.fetchRow(sqlres) + row = mysql.fetch_row(sqlres) if row == nil: break yield InstantRow(row: row, len: L) properFreeResult(sqlres, row) @@ -290,7 +290,7 @@ iterator instantRows*(db: DbConn; columns: var DbColumns; query: SqlQuery; setColumnInfo(columns, sqlres, L) var row: cstringArray while true: - row = mysql.fetchRow(sqlres) + row = mysql.fetch_row(sqlres) if row == nil: break yield InstantRow(row: row, len: L) properFreeResult(sqlres, row) @@ -317,7 +317,7 @@ proc getRow*(db: DbConn, query: SqlQuery, if sqlres != nil: var L = int(mysql.numFields(sqlres)) result = newRow(L) - var row = mysql.fetchRow(sqlres) + var row = mysql.fetch_row(sqlres) if row != nil: for i in 0..L-1: setLen(result[i], 0) @@ -335,7 +335,7 @@ proc getAllRows*(db: DbConn, query: SqlQuery, var row: cstringArray var j = 0 while true: - row = mysql.fetchRow(sqlres) + row = mysql.fetch_row(sqlres) if row == nil: break setLen(result, j+1) newSeq(result[j], L) @@ -361,7 +361,7 @@ proc tryInsertId*(db: DbConn, query: SqlQuery, ## executes the query (typically "INSERT") and returns the ## generated ID for the row or -1 in case of an error. var q = dbFormat(query, args) - if mysql.realQuery(PMySQL db, q, q.len) != 0'i32: + if mysql.real_query(PMySQL db, q, q.len) != 0'i32: result = -1'i64 else: result = mysql.insertId(PMySQL db) diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index 9c65d0732..146ba886f 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -1016,7 +1016,7 @@ proc endhostent*() {.importc, header: "".} proc endnetent*() {.importc, header: "".} proc endprotoent*() {.importc, header: "".} proc endservent*() {.importc, header: "".} -proc freeaddrinfo*(a1: ptr AddrInfo) {.importc, header: "".} +proc freeAddrInfo*(a1: ptr AddrInfo) {.importc: "freeaddrinfo", header: "".} proc gai_strerror*(a1: cint): cstring {.importc:"(char *)$1", header: "".} diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 5e2a8d98e..b67b0c308 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -1791,7 +1791,7 @@ template asyncAddrInfoLoop(addrInfo: ptr AddrInfo, fd: untyped, curAddrInfo = curAddrInfo.ai_next if curAddrInfo == nil: - freeaddrinfo(addrInfo) + freeAddrInfo(addrInfo) when shouldCreateFd: closeUnusedFds() if lastException != nil: @@ -1807,7 +1807,7 @@ template asyncAddrInfoLoop(addrInfo: ptr AddrInfo, fd: untyped, try: curFd = createAsyncNativeSocket(domain, sockType, protocol) except: - freeaddrinfo(addrInfo) + freeAddrInfo(addrInfo) closeUnusedFds() raise getCurrentException() when defined(windows): @@ -1817,7 +1817,7 @@ template asyncAddrInfoLoop(addrInfo: ptr AddrInfo, fd: untyped, doConnect(curFd, curAddrInfo).callback = tryNextAddrInfo curAddrInfo = curAddrInfo.ai_next else: - freeaddrinfo(addrInfo) + freeAddrInfo(addrInfo) when shouldCreateFd: closeUnusedFds(ord(domain)) retFuture.complete(curFd) diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index abb4dd075..61c2fc75a 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -648,9 +648,9 @@ proc bindAddr*(socket: AsyncSocket, port = Port(0), address = "") {. var aiList = getAddrInfo(realaddr, port, socket.domain) if bindAddr(socket.fd, aiList.ai_addr, aiList.ai_addrlen.SockLen) < 0'i32: - freeaddrinfo(aiList) + freeAddrInfo(aiList) raiseOSError(osLastError()) - freeaddrinfo(aiList) + freeAddrInfo(aiList) proc hasDataBuffered*(s: AsyncSocket): bool {.since: (1, 5).} = ## Determines whether an AsyncSocket has data buffered. @@ -859,7 +859,7 @@ proc sendTo*(socket: AsyncSocket, address: string, port: Port, data: string, it = it.ai_next - freeaddrinfo(aiList) + freeAddrInfo(aiList) if not success: if lastException != nil: diff --git a/lib/pure/cookies.nim b/lib/pure/cookies.nim index 7bf34f75b..0306558d6 100644 --- a/lib/pure/cookies.nim +++ b/lib/pure/cookies.nim @@ -75,4 +75,4 @@ proc setCookie*(key, value: string, expires: DateTime|Time, ## `Set-Cookie: key=value; Domain=...; ...` result = setCookie(key, value, domain, path, format(expires.utc, "ddd',' dd MMM yyyy HH:mm:ss 'GMT'"), - noname, secure, httpOnly, maxAge, sameSite) + noName, secure, httpOnly, maxAge, sameSite) diff --git a/lib/pure/ioselects/ioselectors_epoll.nim b/lib/pure/ioselects/ioselectors_epoll.nim index 05d9d19a8..8526eb8a3 100644 --- a/lib/pure/ioselects/ioselectors_epoll.nim +++ b/lib/pure/ioselects/ioselectors_epoll.nim @@ -526,4 +526,4 @@ template withData*[T](s: Selector[T], fd: SocketHandle|int, value, body1, body2 proc getFd*[T](s: Selector[T]): int = - return s.epollFd.int + return s.epollFD.int diff --git a/lib/pure/ioselects/ioselectors_select.nim b/lib/pure/ioselects/ioselectors_select.nim index 88cbaf28c..4c38ba604 100644 --- a/lib/pure/ioselects/ioselectors_select.nim +++ b/lib/pure/ioselects/ioselectors_select.nim @@ -26,27 +26,27 @@ else: #include #include """ type - Fdset {.importc: "fd_set", header: platformHeaders, pure, final.} = object + FdSet {.importc: "fd_set", header: platformHeaders, pure, final.} = object var FD_SETSIZE {.importc: "FD_SETSIZE", header: platformHeaders.}: cint -proc IOFD_SET(fd: SocketHandle, fdset: ptr Fdset) +proc IOFD_SET(fd: SocketHandle, fdset: ptr FdSet) {.cdecl, importc: "FD_SET", header: platformHeaders, inline.} -proc IOFD_CLR(fd: SocketHandle, fdset: ptr Fdset) +proc IOFD_CLR(fd: SocketHandle, fdset: ptr FdSet) {.cdecl, importc: "FD_CLR", header: platformHeaders, inline.} -proc IOFD_ZERO(fdset: ptr Fdset) +proc IOFD_ZERO(fdset: ptr FdSet) {.cdecl, importc: "FD_ZERO", header: platformHeaders, inline.} when defined(windows): - proc IOFD_ISSET(fd: SocketHandle, fdset: ptr Fdset): cint + proc IOFD_ISSET(fd: SocketHandle, fdset: ptr FdSet): cint {.stdcall, importc: "FD_ISSET", header: platformHeaders, inline.} - proc ioselect(nfds: cint, readFds, writeFds, exceptFds: ptr Fdset, + proc ioselect(nfds: cint, readFds, writeFds, exceptFds: ptr FdSet, timeout: ptr Timeval): cint {.stdcall, importc: "select", header: platformHeaders.} else: - proc IOFD_ISSET(fd: SocketHandle, fdset: ptr Fdset): cint + proc IOFD_ISSET(fd: SocketHandle, fdset: ptr FdSet): cint {.cdecl, importc: "FD_ISSET", header: platformHeaders, inline.} - proc ioselect(nfds: cint, readFds, writeFds, exceptFds: ptr Fdset, + proc ioselect(nfds: cint, readFds, writeFds, exceptFds: ptr FdSet, timeout: ptr Timeval): cint {.cdecl, importc: "select", header: platformHeaders.} diff --git a/lib/pure/json.nim b/lib/pure/json.nim index dd9232ea6..8e4db93b0 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -958,12 +958,12 @@ proc parseJson*(s: Stream, filename: string = ""; rawIntegers = false, rawFloats when defined(js): from math import `mod` - from std/jsffi import JSObject, `[]`, to + from std/jsffi import JsObject, `[]`, to from std/private/jsutils import getProtoName, isInteger, isSafeInteger - proc parseNativeJson(x: cstring): JSObject {.importjs: "JSON.parse(#)".} + proc parseNativeJson(x: cstring): JsObject {.importjs: "JSON.parse(#)".} - proc getVarType(x: JSObject, isRawNumber: var bool): JsonNodeKind = + proc getVarType(x: JsObject, isRawNumber: var bool): JsonNodeKind = result = JNull case $getProtoName(x) # TODO: Implicit returns fail here. of "[object Array]": return JArray @@ -982,12 +982,12 @@ when defined(js): of "[object String]": return JString else: assert false - proc len(x: JSObject): int = + proc len(x: JsObject): int = asm """ `result` = `x`.length; """ - proc convertObject(x: JSObject): JsonNode = + proc convertObject(x: JsObject): JsonNode = var isRawNumber = false case getVarType(x, isRawNumber) of JArray: @@ -1000,7 +1000,7 @@ when defined(js): if (`x`.hasOwnProperty(property)) { """ var nimProperty: cstring - var nimValue: JSObject + var nimValue: JsObject asm "`nimProperty` = property; `nimValue` = `x`[property];" result[$nimProperty] = nimValue.convertObject() asm "}}" diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim index eb0514a8f..5453226a0 100644 --- a/lib/pure/nativesockets.nim +++ b/lib/pure/nativesockets.nim @@ -633,8 +633,8 @@ when useNimNetLite: INET_ADDRSTRLEN = 16 INET6_ADDRSTRLEN = 46 # it's actually 46 in both cases - proc sockAddrToStr(sa: ptr Sockaddr): string {.noinit.} = - let af_family = sa.sa_family + proc sockAddrToStr(sa: ptr SockAddr): string {.noinit.} = + let af_family = sa.sa_family var nl, v4Slice: cint var si_addr: ptr InAddr diff --git a/lib/pure/net.nim b/lib/pure/net.nim index c59a4e563..370b83e54 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -984,11 +984,11 @@ proc bindAddr*(socket: Socket, port = Port(0), address = "") {. var aiList = getAddrInfo(realaddr, port, socket.domain) if bindAddr(socket.fd, aiList.ai_addr, aiList.ai_addrlen.SockLen) < 0'i32: - freeaddrinfo(aiList) + freeAddrInfo(aiList) var address2: string address2.addQuoted address raiseOSError(osLastError(), "address: $# port: $#" % [address2, $port]) - freeaddrinfo(aiList) + freeAddrInfo(aiList) proc acceptAddr*(server: Socket, client: var owned(Socket), address: var string, flags = {SocketFlag.SafeDisconn}, @@ -1745,7 +1745,7 @@ proc sendTo*(socket: Socket, address: string, port: Port, data: pointer, it = it.ai_next let osError = osLastError() - freeaddrinfo(aiList) + freeAddrInfo(aiList) if not success: raiseOSError(osError) @@ -1960,7 +1960,7 @@ proc dial*(address: string, port: Port, # network system problem (e.g. not enough FDs), and not an unreachable # address. let err = osLastError() - freeaddrinfo(aiList) + freeAddrInfo(aiList) closeUnusedFds() raiseOSError(err) fdPerDomain[ord(domain)] = lastFd @@ -1969,7 +1969,7 @@ proc dial*(address: string, port: Port, break lastError = osLastError() it = it.ai_next - freeaddrinfo(aiList) + freeAddrInfo(aiList) closeUnusedFds(ord(domain)) if success: @@ -1999,7 +1999,7 @@ proc connect*(socket: Socket, address: string, else: lastError = osLastError() it = it.ai_next - freeaddrinfo(aiList) + freeAddrInfo(aiList) if not success: raiseOSError(lastError) when defineSsl: @@ -2051,7 +2051,7 @@ proc connectAsync(socket: Socket, name: string, port = Port(0), it = it.ai_next - freeaddrinfo(aiList) + freeAddrInfo(aiList) if not success: raiseOSError(lastError) proc connect*(socket: Socket, address: string, port = Port(0), diff --git a/lib/pure/parsesql.nim b/lib/pure/parsesql.nim index 9d74d08d0..61196aead 100644 --- a/lib/pure/parsesql.nim +++ b/lib/pure/parsesql.nim @@ -1451,7 +1451,7 @@ proc ra(n: SqlNode, s: var SqlWriter) = s.addKeyw("enum") rs(n, s) -proc renderSQL*(n: SqlNode, upperCase = false): string = +proc renderSql*(n: SqlNode, upperCase = false): string = ## Converts an SQL abstract syntax tree to its string representation. var s: SqlWriter s.buffer = "" @@ -1460,8 +1460,8 @@ proc renderSQL*(n: SqlNode, upperCase = false): string = return s.buffer proc `$`*(n: SqlNode): string = - ## an alias for `renderSQL`. - renderSQL(n) + ## an alias for `renderSql`. + renderSql(n) proc treeReprAux(s: SqlNode, level: int, result: var string) = result.add('\n') @@ -1493,7 +1493,7 @@ proc open(p: var SqlParser, input: Stream, filename: string) = p.tok.literal = "" getTok(p) -proc parseSQL*(input: Stream, filename: string): SqlNode = +proc parseSql*(input: Stream, filename: string): SqlNode = ## parses the SQL from `input` into an AST and returns the AST. ## `filename` is only used for error messages. ## Syntax errors raise an `SqlParseError` exception. @@ -1504,8 +1504,8 @@ proc parseSQL*(input: Stream, filename: string): SqlNode = finally: close(p) -proc parseSQL*(input: string, filename = ""): SqlNode = +proc parseSql*(input: string, filename = ""): SqlNode = ## parses the SQL from `input` into an AST and returns the AST. ## `filename` is only used for error messages. ## Syntax errors raise an `SqlParseError` exception. - parseSQL(newStringStream(input), "") + parseSql(newStringStream(input), "") diff --git a/lib/pure/pegs.nim b/lib/pure/pegs.nim index 0eed1c388..78f03d308 100644 --- a/lib/pure/pegs.nim +++ b/lib/pure/pegs.nim @@ -1023,7 +1023,7 @@ template eventParser*(pegAst, handlers: untyped): (proc(s: string): int) = ## Symbols declared in an *enter* handler can be made visible in the ## corresponding *leave* handler by annotating them with an *inject* pragma. proc rawParse(s: string, p: Peg, start: int, c: var Captures): int - {.genSym.} = + {.gensym.} = # binding from *macros* bind strVal @@ -1058,7 +1058,7 @@ template eventParser*(pegAst, handlers: untyped): (proc(s: string): int) = matchOrParse(parseIt) parseIt(s, p, start, c) - proc parser(s: string): int {.genSym.} = + proc parser(s: string): int {.gensym.} = # the proc to be returned var ms: array[MaxSubpatterns, (int, int)] diff --git a/lib/pure/segfaults.nim b/lib/pure/segfaults.nim index e0da8b81d..b0eac2299 100644 --- a/lib/pure/segfaults.nim +++ b/lib/pure/segfaults.nim @@ -30,7 +30,7 @@ when defined(windows): const EXCEPTION_ACCESS_VIOLATION = DWORD(0xc0000005'i32) - EXCEPTION_CONTINUE_SEARCH = Long(0) + EXCEPTION_CONTINUE_SEARCH = LONG(0) type PEXCEPTION_RECORD = ptr object diff --git a/lib/std/strbasics.nim b/lib/std/strbasics.nim index 0d4c96f44..6f6db5c37 100644 --- a/lib/std/strbasics.nim +++ b/lib/std/strbasics.nim @@ -46,7 +46,7 @@ func setSlice*(s: var string, slice: Slice[int]) = import std/sugar var a = "Hello, Nim!" - doassert a.dup(setSlice(7 .. 9)) == "Nim" + doAssert a.dup(setSlice(7 .. 9)) == "Nim" doAssert a.dup(setSlice(0 .. 0)) == "H" doAssert a.dup(setSlice(0 .. 1)) == "He" doAssert a.dup(setSlice(0 .. 10)) == a diff --git a/lib/std/tasks.nim b/lib/std/tasks.nim index 3ceb4c8ba..7b931ab14 100644 --- a/lib/std/tasks.nim +++ b/lib/std/tasks.nim @@ -85,7 +85,7 @@ template checkIsolate(scratchAssignList: seq[NimNode], procParam, scratchDotExpr # var isoTempB = isolate(literal) # scratch.b = extract(isolateB) let isolatedTemp = genSym(nskTemp, "isoTemp") - scratchAssignList.add newVarStmt(isolatedTemp, newCall(newidentNode("isolate"), procParam)) + scratchAssignList.add newVarStmt(isolatedTemp, newCall(newIdentNode("isolate"), procParam)) scratchAssignList.add newAssignment(scratchDotExpr, newCall(newIdentNode("extract"), isolatedTemp)) diff --git a/lib/windows/registry.nim b/lib/windows/registry.nim index ab7d0b1fe..44e4e4420 100644 --- a/lib/windows/registry.nim +++ b/lib/windows/registry.nim @@ -46,26 +46,26 @@ template call(f) = proc getUnicodeValue*(path, key: string; handle: HKEY): string = let hh = newWideCString path let kk = newWideCString key - var bufsize: int32 + var bufSize: int32 # try a couple of different flag settings: var flags: int32 = RRF_RT_ANY - let err = regGetValue(handle, hh, kk, flags, nil, nil, addr bufsize) + let err = regGetValue(handle, hh, kk, flags, nil, nil, addr bufSize) if err != 0: var newHandle: HKEY call regOpenKeyEx(handle, hh, 0, KEY_READ or KEY_WOW64_64KEY, newHandle) - call regGetValue(newHandle, nil, kk, flags, nil, nil, addr bufsize) + call regGetValue(newHandle, nil, kk, flags, nil, nil, addr bufSize) if bufSize > 0: - var res = newWideCString(bufsize) + var res = newWideCString(bufSize) call regGetValue(newHandle, nil, kk, flags, nil, addr res[0], - addr bufsize) - result = res $ bufsize + addr bufSize) + result = res $ bufSize call regCloseKey(newHandle) else: if bufSize > 0: - var res = newWideCString(bufsize) + var res = newWideCString(bufSize) call regGetValue(handle, hh, kk, flags, nil, addr res[0], - addr bufsize) - result = res $ bufsize + addr bufSize) + result = res $ bufSize proc regSetValue(key: HKEY, lpSubKey, lpValueName: WideCString, dwType: int32; lpData: WideCString; cbData: int32): int32 {. diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index c02cd9775..cd59b68a4 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -670,7 +670,7 @@ proc getaddrinfo*(nodename, servname: cstring, hints: ptr AddrInfo, res: var ptr AddrInfo): cint {. stdcall, importc: "getaddrinfo", dynlib: ws2dll.} -proc freeaddrinfo*(ai: ptr AddrInfo) {. +proc freeAddrInfo*(ai: ptr AddrInfo) {. stdcall, importc: "freeaddrinfo", dynlib: ws2dll.} proc inet_ntoa*(i: InAddr): cstring {. diff --git a/tests/stdlib/config.nims b/tests/stdlib/config.nims new file mode 100644 index 000000000..0c0c5bd88 --- /dev/null +++ b/tests/stdlib/config.nims @@ -0,0 +1,2 @@ +switch("styleCheck", "usages") +switch("styleCheck", "error") \ No newline at end of file diff --git a/tests/stdlib/nre/misc.nim b/tests/stdlib/nre/misc.nim index dbb0ecdf9..b7df08ee9 100644 --- a/tests/stdlib/nre/misc.nim +++ b/tests/stdlib/nre/misc.nim @@ -6,8 +6,8 @@ block: # Misc tests check("перевірка".replace(re"(*U)\w", "") == "") block: # empty or non-empty match - check("abc".findall(re"|.").join(":") == ":a::b::c:") - check("abc".findall(re".|").join(":") == "a:b:c:") + check("abc".findAll(re"|.").join(":") == ":a::b::c:") + check("abc".findAll(re".|").join(":") == "a:b:c:") check("abc".replace(re"|.", "x") == "xxxxxxx") check("abc".replace(re".|", "x") == "xxxx") diff --git a/tests/stdlib/talgorithm.nim b/tests/stdlib/talgorithm.nim index 5bb5a6bdf..61d2bc62f 100644 --- a/tests/stdlib/talgorithm.nim +++ b/tests/stdlib/talgorithm.nim @@ -101,10 +101,10 @@ block: doAssert binarySearch(noData, 7) == -1 let oneData = @[1] doAssert binarySearch(oneData, 1) == 0 - doAssert binarySearch(onedata, 7) == -1 + doAssert binarySearch(oneData, 7) == -1 let someData = @[1, 3, 4, 7] doAssert binarySearch(someData, 1) == 0 - doAssert binarySearch(somedata, 7) == 3 + doAssert binarySearch(someData, 7) == 3 doAssert binarySearch(someData, -1) == -1 doAssert binarySearch(someData, 5) == -1 doAssert binarySearch(someData, 13) == -1 diff --git a/tests/stdlib/tasynchttpserver.nim b/tests/stdlib/tasynchttpserver.nim index d4176d55a..aed21099d 100644 --- a/tests/stdlib/tasynchttpserver.nim +++ b/tests/stdlib/tasynchttpserver.nim @@ -111,9 +111,9 @@ proc testCustomContentLength() {.async.} = runTest(handler, request, test) -waitfor(test200()) -waitfor(test404()) -waitfor(testCustomEmptyHeaders()) -waitfor(testCustomContentLength()) +waitFor(test200()) +waitFor(test404()) +waitFor(testCustomEmptyHeaders()) +waitFor(testCustomContentLength()) echo "OK" diff --git a/tests/stdlib/tbase64.nim b/tests/stdlib/tbase64.nim index 491ec5db0..8ef5adf57 100644 --- a/tests/stdlib/tbase64.nim +++ b/tests/stdlib/tbase64.nim @@ -27,13 +27,13 @@ template main() = const tests = ["", "abc", "xyz", "man", "leasure.", "sure.", "easure.", "asure.", longText, testInputExpandsTo76, testInputExpands] - doAssert encodeMIME("foobarbaz", lineLen=4) == "Zm9v\r\nYmFy\r\nYmF6" + doAssert encodeMime("foobarbaz", lineLen=4) == "Zm9v\r\nYmFy\r\nYmF6" doAssert decode("Zm9v\r\nYmFy\r\nYmF6") == "foobarbaz" for t in items(tests): doAssert decode(encode(t)) == t - doAssert decode(encodeMIME(t, lineLen=40)) == t - doAssert decode(encodeMIME(t, lineLen=76)) == t + doAssert decode(encodeMime(t, lineLen=40)) == t + doAssert decode(encodeMime(t, lineLen=76)) == t doAssertRaises(ValueError): discard decode("SGVsbG\x008gV29ybGQ=") diff --git a/tests/stdlib/tbitops.nim b/tests/stdlib/tbitops.nim index 6b5e0aeb9..f807993d6 100644 --- a/tests/stdlib/tbitops.nim +++ b/tests/stdlib/tbitops.nim @@ -263,8 +263,8 @@ proc main() = doAssert v == 0b1000_0010 v.flipBit(1) doAssert v == 0b1000_0000 - doAssert v.testbit(7) - doAssert not v.testbit(6) + doAssert v.testBit(7) + doAssert not v.testBit(6) block: # multi bit operations var v: uint8 diff --git a/tests/stdlib/thttpclient.nim b/tests/stdlib/thttpclient.nim index e81590d95..8563a0c26 100644 --- a/tests/stdlib/thttpclient.nim +++ b/tests/stdlib/thttpclient.nim @@ -21,7 +21,7 @@ proc makeIPv6HttpServer(hostname: string, port: Port, let fd = createNativeSocket(AF_INET6) setSockOptInt(fd, SOL_SOCKET, SO_REUSEADDR, 1) var aiList = getAddrInfo(hostname, port, AF_INET6) - if bindAddr(fd, aiList.ai_addr, aiList.ai_addrlen.Socklen) < 0'i32: + if bindAddr(fd, aiList.ai_addr, aiList.ai_addrlen.SockLen) < 0'i32: freeAddrInfo(aiList) raiseOSError(osLastError()) freeAddrInfo(aiList) diff --git a/tests/stdlib/tjsonmacro.nim b/tests/stdlib/tjsonmacro.nim index 779c0ce65..9b59c7dc3 100644 --- a/tests/stdlib/tjsonmacro.nim +++ b/tests/stdlib/tjsonmacro.nim @@ -259,7 +259,7 @@ proc testJson() = colors: array[2, BirdColor] var red = BirdColor(name: "red", rgb: [1.0, 0.0, 0.0]) - var blue = Birdcolor(name: "blue", rgb: [0.0, 0.0, 1.0]) + var blue = BirdColor(name: "blue", rgb: [0.0, 0.0, 1.0]) var b = Bird(age: 3, height: 1.734, name: "bardo", colors: [red, blue]) let jnode = %b let data = jnode.to(Bird) @@ -497,7 +497,7 @@ proc testJson() = doAssert array[3, float](t.arr) == [1.0,2.0,7.0] doAssert MyRef(t.person).name == "boney" - doAssert MyObj(t.distFruit).color == 11 + doAssert MyObj(t.distfruit).color == 11 doAssert t.dog.name == "honey" doAssert t.fruit.color == 10 doAssert seq[string](t.emails) == @["abc", "123"] diff --git a/tests/stdlib/tjsonutils.nim b/tests/stdlib/tjsonutils.nim index 205160471..d5809ee73 100644 --- a/tests/stdlib/tjsonutils.nim +++ b/tests/stdlib/tjsonutils.nim @@ -320,7 +320,7 @@ template fn() = b: int var a = A() - fromJson(a, """{"is_a": true, "a":1, "extra_key": 1}""".parse_json, Joptions(allowExtraKeys: true)) + fromJson(a, """{"is_a": true, "a":1, "extra_key": 1}""".parseJson, Joptions(allowExtraKeys: true)) doAssert $a[] == "(is_a: true, a: 1)" block testAllowMissingKeys: diff --git a/tests/stdlib/tnet.nim b/tests/stdlib/tnet.nim index 671e0f7bc..4ec62d88f 100644 --- a/tests/stdlib/tnet.nim +++ b/tests/stdlib/tnet.nim @@ -93,7 +93,7 @@ block: # "IpAddress/Sockaddr conversion" doAssert($ipaddrstr == $ipaddr_1) var sockaddr: Sockaddr_storage - var socklen: Socklen + var socklen: SockLen var ipaddr_2: IpAddress var port_2: Port diff --git a/tests/stdlib/tnetdial.nim b/tests/stdlib/tnetdial.nim index 3de00ff99..b836fb78d 100644 --- a/tests/stdlib/tnetdial.nim +++ b/tests/stdlib/tnetdial.nim @@ -14,7 +14,7 @@ proc initIPv6Server(hostname: string, port: Port): AsyncFD = let fd = createNativeSocket(AF_INET6) setSockOptInt(fd, SOL_SOCKET, SO_REUSEADDR, 1) var aiList = getAddrInfo(hostname, port, AF_INET6) - if bindAddr(fd, aiList.ai_addr, aiList.ai_addrlen.Socklen) < 0'i32: + if bindAddr(fd, aiList.ai_addr, aiList.ai_addrlen.SockLen) < 0'i32: freeAddrInfo(aiList) raiseOSError(osLastError()) freeAddrInfo(aiList) diff --git a/tests/stdlib/tosproc.nim b/tests/stdlib/tosproc.nim index d767aac80..c54e0d112 100644 --- a/tests/stdlib/tosproc.nim +++ b/tests/stdlib/tosproc.nim @@ -12,7 +12,7 @@ see also: tests/osproc/*.nim; consider merging those into a single test here when defined(case_testfile): # compiled test file for child process from posix import exitnow - proc c_exit2(code: c_int): void {.importc: "_exit", header: "".} + proc c_exit2(code: cint): void {.importc: "_exit", header: "".} import os var a = 0 proc fun(b = 0) = diff --git a/tests/stdlib/tparsesql.nim b/tests/stdlib/tparsesql.nim index b440dc90f..8ef67f5dc 100644 --- a/tests/stdlib/tparsesql.nim +++ b/tests/stdlib/tparsesql.nim @@ -14,8 +14,8 @@ nkStmtList nkIntegerLit 10 nkNumericLit 5.5""" -doAssert $parseSQL("SELECT foo FROM table;") == "select foo from table;" -doAssert $parseSQL(""" +doAssert $parseSql("SELECT foo FROM table;") == "select foo from table;" +doAssert $parseSql(""" SELECT CustomerName, ContactName, @@ -31,26 +31,26 @@ SELECT Country FROM table;""") == "select CustomerName, ContactName, Address, City, PostalCode, Country, CustomerName, ContactName, Address, City, PostalCode, Country from table;" -doAssert $parseSQL("SELECT foo FROM table limit 10") == "select foo from table limit 10;" -doAssert $parseSQL("SELECT foo, bar, baz FROM table limit 10") == "select foo, bar, baz from table limit 10;" -doAssert $parseSQL("SELECT foo AS bar FROM table") == "select foo as bar from table;" -doAssert $parseSQL("SELECT foo AS foo_prime, bar AS bar_prime, baz AS baz_prime FROM table") == "select foo as foo_prime, bar as bar_prime, baz as baz_prime from table;" -doAssert $parseSQL("SELECT * FROM table") == "select * from table;" -doAssert $parseSQL("SELECT count(*) FROM table") == "select count(*) from table;" -doAssert $parseSQL("SELECT count(*) as 'Total' FROM table") == "select count(*) as 'Total' from table;" -doAssert $parseSQL("SELECT count(*) as 'Total', sum(a) as 'Aggr' FROM table") == "select count(*) as 'Total', sum(a) as 'Aggr' from table;" +doAssert $parseSql("SELECT foo FROM table limit 10") == "select foo from table limit 10;" +doAssert $parseSql("SELECT foo, bar, baz FROM table limit 10") == "select foo, bar, baz from table limit 10;" +doAssert $parseSql("SELECT foo AS bar FROM table") == "select foo as bar from table;" +doAssert $parseSql("SELECT foo AS foo_prime, bar AS bar_prime, baz AS baz_prime FROM table") == "select foo as foo_prime, bar as bar_prime, baz as baz_prime from table;" +doAssert $parseSql("SELECT * FROM table") == "select * from table;" +doAssert $parseSql("SELECT count(*) FROM table") == "select count(*) from table;" +doAssert $parseSql("SELECT count(*) as 'Total' FROM table") == "select count(*) as 'Total' from table;" +doAssert $parseSql("SELECT count(*) as 'Total', sum(a) as 'Aggr' FROM table") == "select count(*) as 'Total', sum(a) as 'Aggr' from table;" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT * FROM table WHERE a = b and c = d """) == "select * from table where a = b and c = d;" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT * FROM table WHERE not b """) == "select * from table where not b;" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT * FROM @@ -59,63 +59,63 @@ WHERE a and not b """) == "select * from table where a and not b;" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT * FROM table ORDER BY 1 """) == "select * from table order by 1;" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT * FROM table GROUP BY 1 ORDER BY 1 """) == "select * from table group by 1 order by 1;" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT * FROM table ORDER BY 1 LIMIT 100 """) == "select * from table order by 1 limit 100;" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT * FROM table WHERE a = b and c = d or n is null and not b + 1 = 3 """) == "select * from table where a = b and c = d or n is null and not b + 1 = 3;" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT * FROM table WHERE (a = b and c = d) or (n is null and not b + 1 = 3) """) == "select * from table where(a = b and c = d) or (n is null and not b + 1 = 3);" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT * FROM table HAVING a = b and c = d """) == "select * from table having a = b and c = d;" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT a, b FROM table GROUP BY a """) == "select a, b from table group by a;" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT a, b FROM table GROUP BY 1, 2 """) == "select a, b from table group by 1, 2;" -doAssert $parseSQL("SELECT t.a FROM t as t") == "select t.a from t as t;" +doAssert $parseSql("SELECT t.a FROM t as t") == "select t.a from t as t;" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT a, b FROM ( SELECT * FROM t ) """) == "select a, b from(select * from t);" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT a, b FROM ( SELECT * FROM t ) as foo """) == "select a, b from(select * from t) as foo;" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT a, b FROM ( SELECT * FROM ( SELECT * FROM ( @@ -127,49 +127,49 @@ SELECT a, b FROM ( ) as inner5 """) == "select a, b from(select * from(select * from(select * from(select * from innerTable as inner1) as inner2) as inner3) as inner4) as inner5;" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT a, b FROM (SELECT * FROM a), (SELECT * FROM b), (SELECT * FROM c) """) == "select a, b from(select * from a),(select * from b),(select * from c);" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT * FROM Products WHERE Price BETWEEN 10 AND 20; """) == "select * from Products where Price between 10 and 20;" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT id FROM a JOIN b ON a.id == b.id """) == "select id from a join b on a.id == b.id;" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT id FROM a JOIN (SELECT id from c) as b ON a.id == b.id """) == "select id from a join(select id from c) as b on a.id == b.id;" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT id FROM a INNER JOIN b ON a.id == b.id """) == "select id from a inner join b on a.id == b.id;" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT id FROM a OUTER JOIN b ON a.id == b.id """) == "select id from a outer join b on a.id == b.id;" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT id FROM a CROSS JOIN b ON a.id == b.id """) == "select id from a cross join b on a.id == b.id;" -doAssert $parseSQL(""" +doAssert $parseSql(""" CREATE TYPE happiness AS ENUM ('happy', 'very happy', 'ecstatic'); CREATE TABLE holidays ( num_weeks int, @@ -179,16 +179,16 @@ CREATE INDEX table1_attr1 ON table1(attr1); SELECT * FROM myTab WHERE col1 = 'happy'; """) == "create type happiness as enum ('happy' , 'very happy' , 'ecstatic' ); create table holidays(num_weeks int , happiness happiness );; create index table1_attr1 on table1(attr1 );; select * from myTab where col1 = 'happy';" -doAssert $parseSQL(""" +doAssert $parseSql(""" INSERT INTO Customers (CustomerName, ContactName, Address, City, PostalCode, Country) VALUES ('Cardinal', 'Tom B. Erichsen', 'Skagen 21', 'Stavanger', '4006', 'Norway'); """) == "insert into Customers (CustomerName , ContactName , Address , City , PostalCode , Country ) values ('Cardinal' , 'Tom B. Erichsen' , 'Skagen 21' , 'Stavanger' , '4006' , 'Norway' );" -doAssert $parseSQL(""" +doAssert $parseSql(""" INSERT INTO TableName DEFAULT VALUES """) == "insert into TableName default values;" -doAssert $parseSQL(""" +doAssert $parseSql(""" UPDATE Customers SET ContactName = 'Alfred Schmidt', City= 'Frankfurt' WHERE CustomerID = 1; @@ -209,9 +209,9 @@ nkStmtList nkStringLit Frankfurt nkNone""" -doAssert $parseSQL("DELETE FROM table_name;") == "delete from table_name;" +doAssert $parseSql("DELETE FROM table_name;") == "delete from table_name;" -doAssert treeRepr(parseSQL("DELETE FROM table_name;") +doAssert treeRepr(parseSql("DELETE FROM table_name;") ) == """ nkStmtList @@ -219,14 +219,14 @@ nkStmtList nkIdent table_name nkNone""" -doAssert $parseSQL("DELETE * FROM table_name;") == "delete from table_name;" +doAssert $parseSql("DELETE * FROM table_name;") == "delete from table_name;" -doAssert $parseSQL(""" +doAssert $parseSql(""" --Select all: SELECT * FROM Customers; """) == "select * from Customers;" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT * FROM Customers WHERE (CustomerName LIKE 'L%' OR CustomerName LIKE 'R%' /*OR CustomerName LIKE 'S%' OR CustomerName LIKE 'T%'*/ OR CustomerName LIKE 'W%') @@ -235,9 +235,9 @@ ORDER BY CustomerName; """) == "select * from Customers where(CustomerName like 'L%' or CustomerName like 'R%' or CustomerName like 'W%') and Country = 'USA' order by CustomerName;" # parse quoted keywords as identifires -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT `SELECT`, `FROM` as `GROUP` FROM `WHERE`; """) == """select "SELECT", "FROM" as "GROUP" from "WHERE";""" -doAssert $parseSQL(""" +doAssert $parseSql(""" SELECT "SELECT", "FROM" as "GROUP" FROM "WHERE"; """) == """select "SELECT", "FROM" as "GROUP" from "WHERE";""" diff --git a/tests/stdlib/trst.nim b/tests/stdlib/trst.nim index 8b98d1403..9787c13eb 100644 --- a/tests/stdlib/trst.nim +++ b/tests/stdlib/trst.nim @@ -861,7 +861,7 @@ suite "RST include directive": test "Include whole": "other.rst".writeFile("**test1**") let input = ".. include:: other.rst" - doAssert "test1" == rstTohtml(input, {roSandboxDisabled}, defaultConfig()) + doAssert "test1" == rstToHtml(input, {roSandboxDisabled}, defaultConfig()) removeFile("other.rst") test "Include starting from": @@ -875,7 +875,7 @@ OtherStart .. include:: other.rst :start-after: OtherStart """ - check "Visible" == rstTohtml(input, {roSandboxDisabled}, defaultConfig()) + check "Visible" == rstToHtml(input, {roSandboxDisabled}, defaultConfig()) removeFile("other.rst") test "Include everything before": @@ -889,7 +889,7 @@ And this should **NOT** be visible in `docs.html` .. include:: other.rst :end-before: OtherEnd """ - doAssert "Visible" == rstTohtml(input, {roSandboxDisabled}, defaultConfig()) + doAssert "Visible" == rstToHtml(input, {roSandboxDisabled}, defaultConfig()) removeFile("other.rst") @@ -907,7 +907,7 @@ And this should **NOT** be visible in `docs.html` :start-after: OtherStart :end-before: OtherEnd """ - check "Visible" == rstTohtml(input, {roSandboxDisabled}, defaultConfig()) + check "Visible" == rstToHtml(input, {roSandboxDisabled}, defaultConfig()) removeFile("other.rst") @@ -927,7 +927,7 @@ And this should **NOT** be visible in `docs.html` :start-after: OtherStart :end-before: OtherEnd """ - doAssert "Visible" == rstTohtml(input, {roSandboxDisabled}, defaultConfig()) + doAssert "Visible" == rstToHtml(input, {roSandboxDisabled}, defaultConfig()) removeFile("other.rst") suite "RST escaping": diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim index 2c28fe506..d91b5615e 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -417,7 +417,7 @@ Some chapter """ var error9Bad = new string - let output9Bad = input9bad.toHtml(error=error9Bad) + let output9Bad = input9Bad.toHtml(error=error9Bad) check(error9Bad[] == "input(15, 1) Error: new section expected (section " & "level inconsistent: underline ~~~~~ unexpectedly found, while " & "the following intermediate section level(s) are missing on " & @@ -464,7 +464,7 @@ Some chapter rstGenera.initRstGenerator(outHtml, defaultConfig(), "input", filenames = files) rstGenera.renderRstToOut(rst, output) doAssert rstGenera.meta[metaTitle] == "Title0" - doAssert rstGenera.meta[metaSubTitle] == "SubTitle0" + doAssert rstGenera.meta[metaSubtitle] == "SubTitle0" doAssert "

      Level1

      " in output doAssert "

      Level2

      " in output doAssert "

      Level3

      " in output @@ -491,7 +491,7 @@ Some chapter rstGenera.initRstGenerator(outHtml, defaultConfig(), "input", filenames=files) rstGenera.renderRstToOut(rst, output) doAssert rstGenera.meta[metaTitle] == "" - doAssert rstGenera.meta[metaSubTitle] == "" + doAssert rstGenera.meta[metaSubtitle] == "" doAssert "

      Title0

      " in output doAssert "

      SubTitle0

      " in output @@ -521,7 +521,7 @@ Some chapter rstGenera.initRstGenerator(outHtml, defaultConfig(), "input", filenames=files) rstGenera.renderRstToOut(rst, output) doAssert rstGenera.meta[metaTitle] == "Title0" - doAssert rstGenera.meta[metaSubTitle] == "" + doAssert rstGenera.meta[metaSubtitle] == "" doAssert output == "\n

      MySection1a

      " & # RST "\n

      MySection1b

      " & # Markdown diff --git a/tests/stdlib/tsha1.nim b/tests/stdlib/tsha1.nim index a20183039..6dca1f197 100644 --- a/tests/stdlib/tsha1.nim +++ b/tests/stdlib/tsha1.nim @@ -15,7 +15,7 @@ checkVector("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", proc testIsValidSha1Hash = doAssert not isValidSha1Hash("") doAssert not isValidSha1Hash("042D4BE2B90ED0672E717D71850ABDB0A2D19CD11") - doAssert not isValidSha1hash("042G4BE2B90ED0672E717D71850ABDB0A2D19CD1") + doAssert not isValidSha1Hash("042G4BE2B90ED0672E717D71850ABDB0A2D19CD1") doAssert isValidSha1Hash("042D4BE2B90ED0672E717D71850ABDB0A2D19CD1") doAssert isValidSha1Hash("042d4be2b90ed0672e717d71850abdb0a2d19cd1") doAssert isValidSha1Hash("042d4be2b90ed0672e717D71850ABDB0A2D19CD1") diff --git a/tests/stdlib/tssl.nim b/tests/stdlib/tssl.nim index 7625f3694..379c1b1e5 100644 --- a/tests/stdlib/tssl.nim +++ b/tests/stdlib/tssl.nim @@ -37,8 +37,8 @@ proc notifiedShutdown(port: Port) {.thread.} = proc main() = when defined(posix): var - ignoreAction = SigAction(sa_handler: SIG_IGN) - oldSigPipeHandler: SigAction + ignoreAction = Sigaction(sa_handler: SIG_IGN) + oldSigPipeHandler: Sigaction if sigemptyset(ignoreAction.sa_mask) == -1: raiseOSError(osLastError(), "Couldn't create an empty signal set") if sigaction(SIGPIPE, ignoreAction, oldSigPipeHandler) == -1: diff --git a/tests/stdlib/tstrbasics.nim b/tests/stdlib/tstrbasics.nim index b340ad509..bf562d9ec 100644 --- a/tests/stdlib/tstrbasics.nim +++ b/tests/stdlib/tstrbasics.nim @@ -73,7 +73,7 @@ proc main() = block: # setSlice var a = "Hello, Nim!" - doassert a.dup(setSlice(7 .. 9)) == "Nim" + doAssert a.dup(setSlice(7 .. 9)) == "Nim" doAssert a.dup(setSlice(0 .. 0)) == "H" doAssert a.dup(setSlice(0 .. 1)) == "He" doAssert a.dup(setSlice(0 .. 10)) == a diff --git a/tests/stdlib/tstreams.nim b/tests/stdlib/tstreams.nim index d70d4995a..d199037e3 100644 --- a/tests/stdlib/tstreams.nim +++ b/tests/stdlib/tstreams.nim @@ -40,7 +40,7 @@ block tstreams2: block tstreams3: try: var fs = openFileStream("shouldneverexist.txt") - except IoError: + except IOError: echo "threw exception" static: diff --git a/tests/stdlib/tstrset.nim b/tests/stdlib/tstrset.nim index 1b253f862..71a5520c2 100644 --- a/tests/stdlib/tstrset.nim +++ b/tests/stdlib/tstrset.nim @@ -50,7 +50,7 @@ proc search(r: PRadixNode, s: string): PRadixNode = proc contains*(r: PRadixNode, s: string): bool = return search(r, s) != nil -proc testOrincl*(r: var PRadixNode, s: string): bool = +proc testOrIncl*(r: var PRadixNode, s: string): bool = nil proc incl*(r: var PRadixNode, s: string) = discard testOrIncl(r, s) diff --git a/tests/stdlib/ttimes.nim b/tests/stdlib/ttimes.nim index 2cf945786..fd440eb20 100644 --- a/tests/stdlib/ttimes.nim +++ b/tests/stdlib/ttimes.nim @@ -20,7 +20,7 @@ proc staticTz(hours, minutes, seconds: int = 0): Timezone {.noSideEffect.} = result.utcOffset = offset result.time = time - newTimezone("", zonedTimeFromTime, zonedTImeFromAdjTime) + newTimezone("", zonedTimeFromTime, zonedTimeFromAdjTime) template parseTest(s, f, sExpected: string, ydExpected: int) = let diff --git a/tests/stdlib/ttypeinfo.nim b/tests/stdlib/ttypeinfo.nim index 61c661a58..1cddea781 100644 --- a/tests/stdlib/ttypeinfo.nim +++ b/tests/stdlib/ttypeinfo.nim @@ -50,7 +50,7 @@ doAssert($x4[].kind() == "akString") block: # gimme a new scope dammit - var myarr: array[0..4, array[0..4, string]] = [ + var myArr: array[0..4, array[0..4, string]] = [ ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"], ["test", "1", "2", "3", "4"]] diff --git a/tests/stdlib/ttypeinfo.nims b/tests/stdlib/ttypeinfo.nims new file mode 100644 index 000000000..d31d0b26f --- /dev/null +++ b/tests/stdlib/ttypeinfo.nims @@ -0,0 +1 @@ +--styleCheck:off \ No newline at end of file diff --git a/tests/stdlib/tunicode.nim b/tests/stdlib/tunicode.nim index 33405cf38..3a8206c8f 100644 --- a/tests/stdlib/tunicode.nim +++ b/tests/stdlib/tunicode.nim @@ -218,5 +218,5 @@ block: # bug #17768 let s1 = "abcdef" let s2 = "abcdéf" - doAssert s1.runeSubstr(0, -1) == "abcde" - doAssert s2.runeSubstr(0, -1) == "abcdé" + doAssert s1.runeSubStr(0, -1) == "abcde" + doAssert s2.runeSubStr(0, -1) == "abcdé" diff --git a/tests/stdlib/tvarints.nim b/tests/stdlib/tvarints.nim index 772121f9f..3bba4f457 100644 --- a/tests/stdlib/tvarints.nim +++ b/tests/stdlib/tvarints.nim @@ -48,7 +48,7 @@ block: chk 0.1 chk Inf chk NegInf - chk Nan + chk NaN chk 3.1415926535897932384626433 block: From ef7d7f24594b1ea560fbf25d3fa0d6c51122725f Mon Sep 17 00:00:00 2001 From: huantian Date: Wed, 13 Apr 2022 14:03:46 -0700 Subject: [PATCH 2887/3768] Better error message and tests for bad await (#19622) * Better error message and tests for bad await * Use compiles to check if await is valid * temp: disable windows noasync test * Better error report, simplify test Co-authored-by: flywind --- lib/pure/asyncmacro.nim | 16 +++++++++++++--- tests/async/tasync_noasync.nim | 35 +++++++++++++++++++++++++++++++--- 2 files changed, 45 insertions(+), 6 deletions(-) diff --git a/lib/pure/asyncmacro.nim b/lib/pure/asyncmacro.nim index ce538913f..63c8e6e5c 100644 --- a/lib/pure/asyncmacro.nim +++ b/lib/pure/asyncmacro.nim @@ -126,9 +126,19 @@ template await*(f: typed): untyped {.used.} = error "await expects Future[T], got " & $typeof(f) template await*[T](f: Future[T]): auto {.used.} = - var internalTmpFuture: FutureBase = f - yield internalTmpFuture - (cast[typeof(f)](internalTmpFuture)).read() + template yieldFuture = yield FutureBase() + + when compiles(yieldFuture): + var internalTmpFuture: FutureBase = f + yield internalTmpFuture + (cast[typeof(f)](internalTmpFuture)).read() + else: + macro errorAsync(futureError: Future[T]) = + error( + "Can only 'await' inside a proc marked as 'async'. Use " & + "'waitFor' when calling an 'async' proc in a non-async scope instead", + futureError) + errorAsync(f) proc asyncSingleProc(prc: NimNode): NimNode = ## This macro transforms a single procedure into a closure iterator. diff --git a/tests/async/tasync_noasync.nim b/tests/async/tasync_noasync.nim index 54f4f597f..0927148bf 100644 --- a/tests/async/tasync_noasync.nim +++ b/tests/async/tasync_noasync.nim @@ -1,13 +1,42 @@ discard """ - errormsg: "'yield' only allowed in an iterator" - cmd: "nim c $file" - file: "asyncmacro.nim" + cmd: "nim check --hints:off --warnings:off $file" + action: "reject" + nimout: ''' +tasync_noasync.nim(21, 10) Error: Can only 'await' inside a proc marked as 'async'. Use 'waitFor' when calling an 'async' proc in a non-async scope instead +tasync_noasync.nim(25, 12) Error: Can only 'await' inside a proc marked as 'async'. Use 'waitFor' when calling an 'async' proc in a non-async scope instead +tasync_noasync.nim(28, 10) Error: Can only 'await' inside a proc marked as 'async'. Use 'waitFor' when calling an 'async' proc in a non-async scope instead +tasync_noasync.nim(31, 10) Error: Can only 'await' inside a proc marked as 'async'. Use 'waitFor' when calling an 'async' proc in a non-async scope instead +tasync_noasync.nim(35, 10) Error: Can only 'await' inside a proc marked as 'async'. Use 'waitFor' when calling an 'async' proc in a non-async scope instead +tasync_noasync.nim(38, 10) Error: Can only 'await' inside a proc marked as 'async'. Use 'waitFor' when calling an 'async' proc in a non-async scope instead +tasync_noasync.nim(40, 8) Error: Can only 'await' inside a proc marked as 'async'. Use 'waitFor' when calling an 'async' proc in a non-async scope instead +''' """ import async proc a {.async.} = discard +# Bad await usage +proc nonAsyncProc = + await a() + +proc nestedNonAsyncProc {.async.} = + proc nested = + await a() + +iterator customIterator: int = + await a() + +macro awaitInMacro = + await a() + +type DummyRef = ref object of RootObj +method awaitInMethod(_: DummyRef) {.base.} = + await a() + +proc improperMultisync {.multisync.} = + await a() + await a() # if we overload a fallback handler to get From dc4cc2dca53e3772efb3654a4ddbbe8350d1db43 Mon Sep 17 00:00:00 2001 From: Jason Beetham Date: Thu, 14 Apr 2022 03:30:59 -0600 Subject: [PATCH 2888/3768] Fix string stream crashing when created on nimscript due to last fix (#19717) --- lib/pure/streams.nim | 2 +- tests/stdlib/tstreams.nim | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim index 84451f989..2ac6a82f1 100644 --- a/lib/pure/streams.nim +++ b/lib/pure/streams.nim @@ -1274,7 +1274,7 @@ else: # after 1.3 or JS not defined new(result) result.data = s - when defined(gcOrc) or defined(gcArc): + when declared(prepareMutation): prepareMutation(result.data) # Allows us to mutate using `addr` logic like `copyMem`, otherwise it errors. result.pos = 0 result.closeImpl = ssClose diff --git a/tests/stdlib/tstreams.nim b/tests/stdlib/tstreams.nim index d199037e3..6fda30f51 100644 --- a/tests/stdlib/tstreams.nim +++ b/tests/stdlib/tstreams.nim @@ -76,3 +76,7 @@ block: doAssert(ss.getPosition == 5) # haven't moved ss.setPosition(0) # Ensure we dont error with writing over literals on arc/orc #19707 ss.write("hello") + +static: # Ensure streams it doesnt break with nimscript on arc/orc #19716 + let s = newStringStream("a") + discard s.data From bb839029d5065bcd8367661802bf04e1e7eebb58 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Thu, 21 Apr 2022 22:06:09 +0300 Subject: [PATCH 2889/3768] Move common Latex code into class nimdoc.cls (#19734) --- compiler/docgen.nim | 6 +- compiler/nimpaths.nim | 2 + config/nimdoc.tex.cfg | 188 +---------------------------------------- doc/nimdoc.cls | 190 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 198 insertions(+), 188 deletions(-) create mode 100644 doc/nimdoc.cls diff --git a/compiler/docgen.nim b/compiler/docgen.nim index aad3b9409..ecf98d0b6 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -1576,7 +1576,11 @@ proc writeOutput*(d: PDoc, useWarning = false, groupedToc = false) = outfile.string) if not d.wroteSupportFiles: # nimdoc.css + dochack.js let nimr = $d.conf.getPrefixDir() - copyFile(docCss.interp(nimr = nimr), $d.conf.outDir / nimdocOutCss) + case d.target + of outHtml: + copyFile(docCss.interp(nimr = nimr), $d.conf.outDir / nimdocOutCss) + of outLatex: + copyFile(docCls.interp(nimr = nimr), $d.conf.outDir / nimdocOutCls) if optGenIndex in d.conf.globalOptions: let docHackJs2 = getDocHacksJs(nimr, nim = getAppFilename()) copyFile(docHackJs2, $d.conf.outDir / docHackJs2.lastPathPart) diff --git a/compiler/nimpaths.nim b/compiler/nimpaths.nim index 216071c5c..c6e188289 100644 --- a/compiler/nimpaths.nim +++ b/compiler/nimpaths.nim @@ -25,11 +25,13 @@ when defined(nimPreviewSlimSystem): const docCss* = "$nimr/doc/nimdoc.css" + docCls* = "$nimr/doc/nimdoc.cls" docHackNim* = "$nimr/tools/dochack/dochack.nim" docHackJs* = docHackNim.changeFileExt("js") docHackJsFname* = docHackJs.lastPathPart theindexFname* = "theindex.html" nimdocOutCss* = "nimdoc.out.css" + nimdocOutCls* = "nimdoc.cls" # `out` to make it easier to use with gitignore in user's repos htmldocsDirname* = "htmldocs" dotdotMangle* = "_._" ## refs #13223 diff --git a/config/nimdoc.tex.cfg b/config/nimdoc.tex.cfg index 3912d1279..4fb1aec90 100644 --- a/config/nimdoc.tex.cfg +++ b/config/nimdoc.tex.cfg @@ -69,198 +69,12 @@ doc.file = """ % makeindex file % xelatex file.tex % -\documentclass[a4paper,11pt]{article} -\usepackage[a4paper,xetex,left=3cm,right=3cm,top=1.5cm,bottom=2cm]{geometry} - -% for 2-sided printing with larger inner "binding" margin -%\usepackage[a4paper,xetex,twoside,left=4cm,right=2cm,top=1.5cm,bottom=2cm]{geometry} -% for e-readers with 1.77:1 aspect ratio (e.g. 1920x1080) -%\usepackage[xetex,paperheight=27.6cm,paperwidth=15.5cm,left=3mm,right=3mm,top=3mm,bottom=3mm]{geometry} -% for e-readers with 1.45:1 aspect ratio (e.g. 1200x825) -%\usepackage[xetex,paperheight=22.5cm,paperwidth=15.5cm,left=3mm,right=3mm,top=3mm,bottom=3mm]{geometry} -% for e-readers with 1.33:1 aspect ratio (e.g. 1872x1404) -%\usepackage[xetex,paperheight=20.7cm,paperwidth=15.5cm,left=3mm,right=3mm,top=3mm,bottom=3mm]{geometry} - -\usepackage{fontspec} -% logic to select default font with some fall-back fonts. -\IfFontExistsTF{Times New Roman}{% - \setmainfont{Times New Roman} % the default font - \typeout{========================================= nim: using Times New Roman} -}{ - \IfFontExistsTF{FreeSerif}{% - \setmainfont{FreeSerif} % fallback #1 - official GNU font, resembles Times - \typeout{========================================= nim: using FreeSerif} - }{ - \IfFontExistsTF{DejaVuSerif}{% - \setmainfont{DejaVuSerif} % fallback #2 - very widespread free font - \typeout{========================================= nim: using DejaVuSerif} - }{ - \typeout{!!!!!!!!!!!!!!!!!!! Fonts not found !!!!!!!!!!!!!!!!!!!!!!!} - } - } -} - -% default monospace font for code: -\usepackage{GoMono} -\usepackage{relsize} -% make this monospace font 2 steps smaller to hold 80-character line -\newcommand{\rstverbblockfont}{\smaller[2]} -\newcommand{\rstverbinlinefont}{\smaller} - -\usepackage{parskip} % paragraphs delimited by vertical space, no indent -\usepackage{graphicx} - -\usepackage{makeidx} -\newcommand{\nimindexterm}[2]{#2\index{#2}\label{#1}} -\makeindex - -\usepackage{dingbat} % for \carriagereturn, etc -\usepackage{fvextra} % for code blocks (works better than original fancyvrb) -\fvset{ - breaklines, - breakafter={=}:|\_\{\}[](){,}.;+-*/'", - breaksymbolleft=\color{red}{\ensuremath{\hookrightarrow}}, - breaksymbolright=\color{red}{\small\carriagereturn} -} -\fvinlineset{% - breaklines, - breakafter={=}:|\_\{\}[](){,}.;+-*/'", - % that does not work at all when we underline inline code by ulem :-( - commandchars=\\\{\} -} - -\usepackage{scrextend} % for the `addmargin` environment - -\usepackage{xcolor} -\usepackage[urlbordercolor=blue,linkbordercolor=cyan, - pdfborderstyle={/S/U/W 1}]{hyperref} -\usepackage{enumitem} % for option list, enumList, and rstfootnote - -\usepackage[most]{tcolorbox} % boxes around admonitions, code blocks, doc.item - -\newtcolorbox{rstadmonition}[1][]{blanker, breakable, - left=3mm, right=0mm, top=1mm, bottom=1mm, - before upper=\indent, parbox=false, #1} - -\newtcolorbox{rstquote}[1][]{blanker, breakable, - left=3mm, right=3mm, top=1mm, bottom=1mm, - parbox=false, - borderline west={0.3em}{0pt}{lightgray}, - borderline north={0.05em}{0pt}{lightgray}, - borderline east={0.05em}{0pt}{lightgray}, - borderline south={0.05em}{0pt}{lightgray}} - -\definecolor{rstframecolor}{rgb}{0.85, 0.8, 0.6} - -\newtcolorbox{rstprebox}[1][]{blanker, breakable, - left=3mm, right=3mm, top=1mm, bottom=1mm, - borderline ={0.1em}{0pt}{rstframecolor}, - before upper=\indent, parbox=false, #1} - -\newenvironment{rstpre}{% -\VerbatimEnvironment\begingroup\begin{rstprebox}% -\begin{Verbatim}[fontsize=\rstverbblockfont , commandchars=\\\{\}]}% -{\end{Verbatim}\end{rstprebox}\endgroup} - -\newtcolorbox{rstdocitembox}[1][]{blanker, breakable, - left=3mm, right=3mm, top=1mm, bottom=1mm, - borderline ={1pt}{0pt}{cyan}, - before upper=\indent, parbox=false, #1} - -% Inline code formatting: grey underline, -% use \Verb from fvextras e.g. to display -- correctly as double - -\usepackage[normalem]{ulem} -\newcommand\rstuline{\bgroup\markoverwith{\textcolor{rstframecolor}{\rule[-0.8ex]{2pt}{1.0pt}}}\ULon} - -\newcommand{\rstcode}[1]{% -{\rstverbinlinefont\Verb{\rstuline{#1}}}% -} - -\newcommand{\rstcodeitem}[1]{\Verb{#1}} - -\newenvironment{rstdocitem}{% -\VerbatimEnvironment\begingroup\begin{rstdocitembox}% -\begin{Verbatim}[fontsize=\rstverbblockfont , commandchars=\\\{\}]}% -{\end{Verbatim}\end{rstdocitembox}\endgroup} - - -\newenvironment{rstfootnote}{\begin{description}[labelindent=1em,leftmargin=1em,labelwidth=2.6em]}{\end{description}} -\ifdim\linewidth<30em - \def\rstoptleftmargin{0.4\linewidth} - \def\rstoptlabelwidth{0.35\linewidth} -\else - \def\rstoptleftmargin{12em} - \def\rstoptlabelwidth{10.5em} -\fi -\newenvironment{rstoptlist}{% -\begin{description}[font=\sffamily\bfseries,style=nextline,leftmargin=\rstoptleftmargin,labelwidth=\rstoptlabelwidth]}{\end{description}} - -\usepackage{tabulary} % tables with adjustable cell width and no overflow -% make tabulary prevent overflows (https://tex.stackexchange.com/a/195088) -\tymin=60pt -\tymax=\maxdimen -% to pack tabulary into a new environment, special syntax is needed :-( -\newenvironment{rsttab}[1]{\tabulary{\linewidth}{#1}}{\endtabulary} - -\newcommand{\rstsub}[1]{\raisebox{-0.5ex}{\scriptsize{#1}}} -\newcommand{\rstsup}[1]{\raisebox{0.5ex}{\scriptsize{#1}}} - -\newcommand{\rsthA}[2][]{\section[#1]{#2}} -\newcommand{\rsthB}[2][]{\subsection[#1]{#2}} -\newcommand{\rsthC}[2][]{\subsubsection[#1]{#2}} -\newcommand{\rsthD}[2][]{\paragraph[#1]{#2}} -\newcommand{\rsthE}[2][]{\paragraph[#1]{#2}} - -\newcommand{\rstovA}[2][]{\section*[#1]{#2}} -\newcommand{\rstovB}[2][]{\subsection*[#1]{#2}} -\newcommand{\rstovC}[2][]{\subsubsection*[#1]{#2}} -\newcommand{\rstovD}[2][]{\paragraph*[#1]{#2}} -\newcommand{\rstovE}[2][]{\paragraph*[#1]{#2}} - -% Syntax highlighting: -\newcommand{\spanDecNumber}[1]{\textbf{\textcolor{darkgray}{#1}}} -\newcommand{\spanBinNumber}[1]{\textbf{\textcolor{darkgray}{#1}}} -\newcommand{\spanHexNumber}[1]{\textbf{\textcolor{darkgray}{#1}}} -\newcommand{\spanOctNumber}[1]{\textbf{\textcolor{darkgray}{#1}}} -\newcommand{\spanFloatNumber}[1]{\textbf{\textcolor{darkgray}{#1}}} -\newcommand{\spanIdentifier}[1]{#1} -\newcommand{\spanKeyword}[1]{\textbf{#1}} -\newcommand{\spanStringLit}[1]{\textbf{\textcolor{darkgray}{#1}}} -\newcommand{\spanLongStringLit}[1]{\textbf{\textcolor{darkgray}{#1}}} -\newcommand{\spanCharLit}[1]{#1} -\newcommand{\spanEscapeSequence}[1]{#1} -\newcommand{\spanOperator}[1]{\textbf{#1}} -\newcommand{\spanPunctuation}[1]{#1} -\newcommand{\spanComment}[1]{\emph{#1}} -\newcommand{\spanLongComment}[1]{\emph{#1}} -\newcommand{\spanRegularExpression}[1]{#1} -\newcommand{\spanTagStart}[1]{#1} -\newcommand{\spanTagEnd}[1]{#1} -\newcommand{\spanKey}[1]{#1} -\newcommand{\spanValue}[1]{#1} -\newcommand{\spanRawData}[1]{\textbf{\textcolor{darkgray}{#1}}} -\newcommand{\spanAssembler}[1]{#1} -\newcommand{\spanPreprocessor}[1]{#1} -\newcommand{\spanDirective}[1]{#1} -\newcommand{\spanCommand}[1]{#1} -\newcommand{\spanRule}[1]{#1} -\newcommand{\spanHyperlink}[1]{#1} -\newcommand{\spanLabel}[1]{#1} -\newcommand{\spanReference}[1]{#1} -\newcommand{\spanOther}[1]{#1} -\newcommand{\spantok}[1]{\fbox{#1}} -\newcommand{\spanPrompt}[1]{\textcolor{red}{\textbf{#1}}} -\newcommand{\spanProgramOutput}[1]{\textcolor{darkgray}{\textbf{#1}}} -\newcommand{\spanprogram}[1]{\textbf{\underline{#1}}} -\newcommand{\spanoption}[1]{\textbf{\textcolor{darkgray}{#1}}} +\documentclass{nimdoc} \begin{document} \title{$title $version $subtitle} \author{$author} -% Never allow text overflow to margin: -\setlength\emergencystretch{\hsize}\hbadness=10000 - \maketitle $content diff --git a/doc/nimdoc.cls b/doc/nimdoc.cls new file mode 100644 index 000000000..271dc5dc9 --- /dev/null +++ b/doc/nimdoc.cls @@ -0,0 +1,190 @@ +\ProvidesClass{nimdoc}[2022/04/17, 2018/01/01 LaTeX2e nonstandard class] + +\LoadClass[a4paper,11pt]{article} + +\usepackage[a4paper,xetex,left=3cm,right=3cm,top=1.5cm,bottom=2cm]{geometry} + +% for 2-sided printing with larger inner "binding" margin +%\usepackage[a4paper,xetex,twoside,left=4cm,right=2cm,top=1.5cm,bottom=2cm]{geometry} +% for e-readers with 1.77:1 aspect ratio (e.g. 1920x1080) +%\usepackage[xetex,paperheight=27.6cm,paperwidth=15.5cm,left=3mm,right=3mm,top=3mm,bottom=3mm]{geometry} +% for e-readers with 1.45:1 aspect ratio (e.g. 1200x825) +%\usepackage[xetex,paperheight=22.5cm,paperwidth=15.5cm,left=3mm,right=3mm,top=3mm,bottom=3mm]{geometry} +% for e-readers with 1.33:1 aspect ratio (e.g. 1872x1404) +%\usepackage[xetex,paperheight=20.7cm,paperwidth=15.5cm,left=3mm,right=3mm,top=3mm,bottom=3mm]{geometry} + +\usepackage{fontspec} +% logic to select default font with some fall-back fonts. +\IfFontExistsTF{Times New Roman}{% + \setmainfont{Times New Roman} % the default font + \typeout{========================================= nim: using Times New Roman} +}{ + \IfFontExistsTF{FreeSerif}{% + \setmainfont{FreeSerif} % fallback #1 - official GNU font, resembles Times + \typeout{========================================= nim: using FreeSerif} + }{ + \IfFontExistsTF{DejaVuSerif}{% + \setmainfont{DejaVuSerif} % fallback #2 - very widespread free font + \typeout{========================================= nim: using DejaVuSerif} + }{ + \typeout{!!!!!!!!!!!!!!!!!!! Fonts not found !!!!!!!!!!!!!!!!!!!!!!!} + } + } +} + +% default monospace font for code: +\usepackage{GoMono} +\usepackage{relsize} +% make this monospace font 2 steps smaller to hold 80-character line +\newcommand{\rstverbblockfont}{\smaller[2]} +\newcommand{\rstverbinlinefont}{\smaller} + +\usepackage{parskip} % paragraphs delimited by vertical space, no indent +\usepackage{graphicx} + +\usepackage{makeidx} +\newcommand{\nimindexterm}[2]{#2\index{#2}\label{#1}} +\makeindex + +\usepackage{dingbat} % for \carriagereturn, etc +\usepackage{fvextra} % for code blocks (works better than original fancyvrb) +\fvset{ + breaklines, + breakafter={=}:|\_\{\}[](){,}.;+-*/'", + breaksymbolleft=\color{red}{\ensuremath{\hookrightarrow}}, + breaksymbolright=\color{red}{\small\carriagereturn} +} +\fvinlineset{% + breaklines, + breakafter={=}:|\_\{\}[](){,}.;+-*/'", + % that does not work at all when we underline inline code by ulem :-( + commandchars=\\\{\} +} + +\usepackage{scrextend} % for the `addmargin` environment + +\usepackage{xcolor} +\usepackage[urlbordercolor=blue,linkbordercolor=cyan, + pdfborderstyle={/S/U/W 1}]{hyperref} +\usepackage{enumitem} % for option list, enumList, and rstfootnote + +\usepackage[most]{tcolorbox} % boxes around admonitions, code blocks, doc.item + +\newtcolorbox{rstadmonition}[1][]{blanker, breakable, + left=3mm, right=0mm, top=1mm, bottom=1mm, + before upper=\indent, parbox=false, #1} + +\newtcolorbox{rstquote}[1][]{blanker, breakable, + left=3mm, right=3mm, top=1mm, bottom=1mm, + parbox=false, + borderline west={0.3em}{0pt}{lightgray}, + borderline north={0.05em}{0pt}{lightgray}, + borderline east={0.05em}{0pt}{lightgray}, + borderline south={0.05em}{0pt}{lightgray}} + +\definecolor{rstframecolor}{rgb}{0.85, 0.8, 0.6} + +\newtcolorbox{rstprebox}[1][]{blanker, breakable, + left=3mm, right=3mm, top=1mm, bottom=1mm, + borderline ={0.1em}{0pt}{rstframecolor}, + before upper=\indent, parbox=false, #1} + +\newenvironment{rstpre}{% +\VerbatimEnvironment\begingroup\begin{rstprebox}% +\begin{Verbatim}[fontsize=\rstverbblockfont , commandchars=\\\{\}]}% +{\end{Verbatim}\end{rstprebox}\endgroup} + +\newtcolorbox{rstdocitembox}[1][]{blanker, breakable, + left=3mm, right=3mm, top=1mm, bottom=1mm, + borderline ={1pt}{0pt}{cyan}, + before upper=\indent, parbox=false, #1} + +% Inline code formatting: grey underline, +% use \Verb from fvextras e.g. to display -- correctly as double - +\usepackage[normalem]{ulem} +\newcommand\rstuline{\bgroup\markoverwith{\textcolor{rstframecolor}{\rule[-0.8ex]{2pt}{1.0pt}}}\ULon} + +\newcommand{\rstcode}[1]{% +{\rstverbinlinefont\Verb{\rstuline{#1}}}% +} + +\newcommand{\rstcodeitem}[1]{\Verb{#1}} + +\newenvironment{rstdocitem}{% +\VerbatimEnvironment\begingroup\begin{rstdocitembox}% +\begin{Verbatim}[fontsize=\rstverbblockfont , commandchars=\\\{\}]}% +{\end{Verbatim}\end{rstdocitembox}\endgroup} + + +\newenvironment{rstfootnote}{\begin{description}[labelindent=1em,leftmargin=1em,labelwidth=2.6em]}{\end{description}} +\ifdim\linewidth<30em + \def\rstoptleftmargin{0.4\linewidth} + \def\rstoptlabelwidth{0.35\linewidth} +\else + \def\rstoptleftmargin{12em} + \def\rstoptlabelwidth{10.5em} +\fi +\newenvironment{rstoptlist}{% +\begin{description}[font=\sffamily\bfseries,style=nextline,leftmargin=\rstoptleftmargin,labelwidth=\rstoptlabelwidth]}{\end{description}} + +\usepackage{tabulary} % tables with adjustable cell width and no overflow +% make tabulary prevent overflows (https://tex.stackexchange.com/a/195088) +\tymin=60pt +\tymax=\maxdimen +% to pack tabulary into a new environment, special syntax is needed :-( +\newenvironment{rsttab}[1]{\tabulary{\linewidth}{#1}}{\endtabulary} + +\newcommand{\rstsub}[1]{\raisebox{-0.5ex}{\scriptsize{#1}}} +\newcommand{\rstsup}[1]{\raisebox{0.5ex}{\scriptsize{#1}}} + +\newcommand{\rsthA}[2][]{\section[#1]{#2}} +\newcommand{\rsthB}[2][]{\subsection[#1]{#2}} +\newcommand{\rsthC}[2][]{\subsubsection[#1]{#2}} +\newcommand{\rsthD}[2][]{\paragraph[#1]{#2}} +\newcommand{\rsthE}[2][]{\paragraph[#1]{#2}} + +\newcommand{\rstovA}[2][]{\section*[#1]{#2}} +\newcommand{\rstovB}[2][]{\subsection*[#1]{#2}} +\newcommand{\rstovC}[2][]{\subsubsection*[#1]{#2}} +\newcommand{\rstovD}[2][]{\paragraph*[#1]{#2}} +\newcommand{\rstovE}[2][]{\paragraph*[#1]{#2}} + +% Syntax highlighting: +\newcommand{\spanDecNumber}[1]{\textbf{\textcolor{darkgray}{#1}}} +\newcommand{\spanBinNumber}[1]{\textbf{\textcolor{darkgray}{#1}}} +\newcommand{\spanHexNumber}[1]{\textbf{\textcolor{darkgray}{#1}}} +\newcommand{\spanOctNumber}[1]{\textbf{\textcolor{darkgray}{#1}}} +\newcommand{\spanFloatNumber}[1]{\textbf{\textcolor{darkgray}{#1}}} +\newcommand{\spanIdentifier}[1]{#1} +\newcommand{\spanKeyword}[1]{\textbf{#1}} +\newcommand{\spanStringLit}[1]{\textbf{\textcolor{darkgray}{#1}}} +\newcommand{\spanLongStringLit}[1]{\textbf{\textcolor{darkgray}{#1}}} +\newcommand{\spanCharLit}[1]{#1} +\newcommand{\spanEscapeSequence}[1]{#1} +\newcommand{\spanOperator}[1]{\textbf{#1}} +\newcommand{\spanPunctuation}[1]{#1} +\newcommand{\spanComment}[1]{\emph{#1}} +\newcommand{\spanLongComment}[1]{\emph{#1}} +\newcommand{\spanRegularExpression}[1]{#1} +\newcommand{\spanTagStart}[1]{#1} +\newcommand{\spanTagEnd}[1]{#1} +\newcommand{\spanKey}[1]{#1} +\newcommand{\spanValue}[1]{#1} +\newcommand{\spanRawData}[1]{\textbf{\textcolor{darkgray}{#1}}} +\newcommand{\spanAssembler}[1]{#1} +\newcommand{\spanPreprocessor}[1]{#1} +\newcommand{\spanDirective}[1]{#1} +\newcommand{\spanCommand}[1]{#1} +\newcommand{\spanRule}[1]{#1} +\newcommand{\spanHyperlink}[1]{#1} +\newcommand{\spanLabel}[1]{#1} +\newcommand{\spanReference}[1]{#1} +\newcommand{\spanOther}[1]{#1} +\newcommand{\spantok}[1]{\fbox{#1}} +\newcommand{\spanPrompt}[1]{\textcolor{red}{\textbf{#1}}} +\newcommand{\spanProgramOutput}[1]{\textcolor{darkgray}{\textbf{#1}}} +\newcommand{\spanprogram}[1]{\textbf{\underline{#1}}} +\newcommand{\spanoption}[1]{\textbf{\textcolor{darkgray}{#1}}} + +% Never allow text overflow to margin: +\setlength\emergencystretch{\hsize}\hbadness=10000 From 02e8aa9660ae78a4d6a67656709f9379f30c5197 Mon Sep 17 00:00:00 2001 From: huantian Date: Sun, 24 Apr 2022 02:47:39 -0700 Subject: [PATCH 2890/3768] Fix doc: list of async backends (#19741) --- lib/pure/asyncdispatch.nim | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index b67b0c308..b1e1c19fd 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -187,9 +187,9 @@ ## ## Known `async` backends include: ## -## * `none` - ``-d:asyncBackend=none`` - disable ``async`` support completely -## * `asyncdispatch -``-d:asyncBackend=asyncdispatch`` -## * `chronos ` - ``-d:asyncBackend=chronos`` +## * `-d:asyncBackend=none`: disable `async` support completely +## * `-d:asyncBackend=asyncdispatch`: https://nim-lang.org/docs/asyncdispatch.html +## * `-d:asyncBackend=chronos`: https://github.com/status-im/nim-chronos/ ## ## ``none`` can be used when a library supports both a synchronous and ## asynchronous API, to disable the latter. From b10f0e7bca43761316f6424786a771af33254e19 Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 25 Apr 2022 02:20:18 +0800 Subject: [PATCH 2891/3768] fix db_sqlite.tryInsertID does raise exceptions in 1.6.0 #19743 (#19744) --- lib/impure/db_sqlite.nim | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/lib/impure/db_sqlite.nim b/lib/impure/db_sqlite.nim index 1638d38c6..db40dddd8 100644 --- a/lib/impure/db_sqlite.nim +++ b/lib/impure/db_sqlite.nim @@ -635,7 +635,7 @@ proc getValue*(db: DbConn, stmtName: SqlPrepared): string proc tryInsertID*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): int64 - {.tags: [WriteDbEffect], raises: [DbError].} = + {.tags: [WriteDbEffect], raises: [].} = ## Executes the query (typically "INSERT") and returns the ## generated ID for the row or -1 in case of an error. ## @@ -650,16 +650,19 @@ proc tryInsertID*(db: DbConn, query: SqlQuery, ## 1, "item#1") == -1 ## db.close() assert(not db.isNil, "Database not connected.") - var q = dbFormat(query, args) - var stmt: sqlite3.PStmt result = -1 - if prepare_v2(db, q, q.len.cint, stmt, nil) == SQLITE_OK: - if step(stmt) == SQLITE_DONE: - result = last_insert_rowid(db) - if finalize(stmt) != SQLITE_OK: - result = -1 - else: - discard finalize(stmt) + try: + var q = dbFormat(query, args) + var stmt: sqlite3.PStmt + if prepare_v2(db, q, q.len.cint, stmt, nil) == SQLITE_OK: + if step(stmt) == SQLITE_DONE: + result = last_insert_rowid(db) + if finalize(stmt) != SQLITE_OK: + result = -1 + else: + discard finalize(stmt) + except DbError: + discard proc insertID*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): int64 {.tags: [WriteDbEffect].} = @@ -692,7 +695,7 @@ proc insertID*(db: DbConn, query: SqlQuery, proc tryInsert*(db: DbConn, query: SqlQuery, pkName: string, args: varargs[string, `$`]): int64 - {.tags: [WriteDbEffect], raises: [DbError], since: (1, 3).} = + {.tags: [WriteDbEffect], raises: [], since: (1, 3).} = ## same as tryInsertID tryInsertID(db, query, args) From 8e6136dd59c737dab7c244e69f52864bd3fa65c9 Mon Sep 17 00:00:00 2001 From: tersec Date: Mon, 25 Apr 2022 09:06:53 +0000 Subject: [PATCH 2892/3768] document localPassC consistently with compiler; fix ANSI capitalization (#19721) --- doc/manual.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/doc/manual.rst b/doc/manual.rst index 8206d8342..adc1d9d16 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -1370,7 +1370,7 @@ cstring type The `cstring` type meaning `compatible string` is the native representation of a string for the compilation backend. For the C backend the `cstring` type represents a pointer to a zero-terminated char array -compatible with the type `char*` in Ansi C. Its primary purpose lies in easy +compatible with the type `char*` in ANSI C. Its primary purpose lies in easy interfacing with C. The index operation `s[i]` means the i-th *char* of `s`; however no bounds checking for `cstring` is performed making the index operation unsafe. @@ -7218,16 +7218,16 @@ during semantic analysis: {.passc: gorge("pkg-config --cflags sdl").} -LocalPassc pragma +localPassC pragma ----------------- -The `localPassc` pragma can be used to pass additional parameters to the C +The `localPassC` pragma can be used to pass additional parameters to the C compiler, but only for the C/C++ file that is produced from the Nim module the pragma resides in: .. code-block:: Nim # Module A.nim # Produces: A.nim.cpp - {.localPassc: "-Wall -Werror".} # Passed when compiling A.nim.cpp + {.localPassC: "-Wall -Werror".} # Passed when compiling A.nim.cpp PassL pragma From efaa6777a464b94d88cb948390397b490ccee5b8 Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 25 Apr 2022 17:07:55 +0800 Subject: [PATCH 2893/3768] fix #19435; don't create TypeBoundOps for tyOpenArray, tyVarargs [backport: 1.6] (#19723) * fix #19435; openArray wronyly registers typebounds * add testcase * don't create TypeBoundOps for tyOpenArray, tyVarargs --- compiler/sempass2.nim | 2 +- tests/arc/t19435.nim | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 tests/arc/t19435.nim diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 9bc811a03..0ee806f36 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -1054,7 +1054,7 @@ proc track(tracked: PEffects, n: PNode) = addAsgnFact(tracked.guards, n[0], n[1]) notNilCheck(tracked, n[1], n[0].typ) when false: cstringCheck(tracked, n) - if tracked.owner.kind != skMacro: + if tracked.owner.kind != skMacro and n[0].typ.kind notin {tyOpenArray, tyVarargs}: createTypeBoundOps(tracked, n[0].typ, n.info) if n[0].kind != nkSym or not isLocalVar(tracked, n[0].sym): checkForSink(tracked.config, tracked.c.idgen, tracked.owner, n[1]) diff --git a/tests/arc/t19435.nim b/tests/arc/t19435.nim new file mode 100644 index 000000000..519216bad --- /dev/null +++ b/tests/arc/t19435.nim @@ -0,0 +1,29 @@ +discard """ + matrix: "--gc:arc" +""" + +# bug #19435 +{.experimental: "views".} + +type + Bar = object + placeholder: int + Foo = object + placeholder: int + c: seq[Bar] # remove this line to make things right + +func children*(s: var seq[Foo]): openArray[Foo] = + s.toOpenArray(0, s.len-1) + +proc test = + var foos = @[Foo(), Foo()] + + assert foos.children.len == 2 + var flag = true + for a in foos.children: + flag = false + + if flag: + doAssert false + +test() \ No newline at end of file From 2f32b450d39030c287df4d56be9d06473772f5cd Mon Sep 17 00:00:00 2001 From: Danil Yarantsev Date: Mon, 25 Apr 2022 12:15:03 +0300 Subject: [PATCH 2894/3768] Really fix StringStream with ARC at compile-time, improve streams test (#19739) * Fix compile-time StringStream with ARC * make readDataStr work with ARC, improve test --- lib/pure/streams.nim | 10 ++++++++-- tests/stdlib/tstreams.nim | 9 +++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim index 2ac6a82f1..f58273ee8 100644 --- a/lib/pure/streams.nim +++ b/lib/pure/streams.nim @@ -243,6 +243,9 @@ proc readDataStr*(s: Stream, buffer: var string, slice: Slice[int]): int = result = s.readDataStrImpl(s, buffer, slice) else: # fallback + when declared(prepareMutation): + # buffer might potentially be a CoW literal with ARC + prepareMutation(buffer) result = s.readData(addr buffer[slice.a], slice.b + 1 - slice.a) template jsOrVmBlock(caseJsOrVm, caseElse: untyped): untyped = @@ -1274,8 +1277,11 @@ else: # after 1.3 or JS not defined new(result) result.data = s - when declared(prepareMutation): - prepareMutation(result.data) # Allows us to mutate using `addr` logic like `copyMem`, otherwise it errors. + when nimvm: + discard + else: + when declared(prepareMutation): + prepareMutation(result.data) # Allows us to mutate using `addr` logic like `copyMem`, otherwise it errors. result.pos = 0 result.closeImpl = ssClose result.atEndImpl = ssAtEnd diff --git a/tests/stdlib/tstreams.nim b/tests/stdlib/tstreams.nim index 6fda30f51..d9857926e 100644 --- a/tests/stdlib/tstreams.nim +++ b/tests/stdlib/tstreams.nim @@ -1,4 +1,5 @@ discard """ + matrix: "--gc:refc; --gc:arc" input: "Arne" output: ''' Hello! What is your name? @@ -74,9 +75,13 @@ block: doAssert(ss.peekLine(str)) doAssert(str == "uick brown fox jumped over the lazy dog.") doAssert(ss.getPosition == 5) # haven't moved - ss.setPosition(0) # Ensure we dont error with writing over literals on arc/orc #19707 + # bug #19707 - Ensure we dont error with writing over literals on arc/orc + ss.setPosition(0) ss.write("hello") + ss.setPosition(0) + doAssert(ss.peekStr(5) == "hello") +# bug #19716 static: # Ensure streams it doesnt break with nimscript on arc/orc #19716 let s = newStringStream("a") - discard s.data + doAssert s.data == "a" From 465fd06d50d0dd2e65762b718ea9a726d94c110b Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 25 Apr 2022 17:18:26 +0800 Subject: [PATCH 2895/3768] add warnings for gc:option (#19722) --- compiler/commands.nim | 91 +++++++++++++++++++++++-------------------- 1 file changed, 49 insertions(+), 42 deletions(-) diff --git a/compiler/commands.nim b/compiler/commands.nim index 98537e9ef..bab98b376 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -527,6 +527,50 @@ proc unregisterArcOrc(conf: ConfigRef) = excl conf.globalOptions, optSeqDestructors excl conf.globalOptions, optTinyRtti +proc processMemoryManagementOption(switch, arg: string, pass: TCmdLinePass, + info: TLineInfo; conf: ConfigRef) = + if conf.backend == backendJs: return # for: bug #16033 + expectArg(conf, switch, arg, pass, info) + if pass in {passCmd2, passPP}: + case arg.normalize + of "boehm": + unregisterArcOrc(conf) + conf.selectedGC = gcBoehm + defineSymbol(conf.symbols, "boehmgc") + incl conf.globalOptions, optTlsEmulation # Boehm GC doesn't scan the real TLS + of "refc": + unregisterArcOrc(conf) + conf.selectedGC = gcRefc + of "markandsweep": + unregisterArcOrc(conf) + conf.selectedGC = gcMarkAndSweep + defineSymbol(conf.symbols, "gcmarkandsweep") + of "destructors", "arc": + registerArcOrc(pass, conf, false) + of "orc": + registerArcOrc(pass, conf, true) + of "hooks": + conf.selectedGC = gcHooks + defineSymbol(conf.symbols, "gchooks") + incl conf.globalOptions, optSeqDestructors + processOnOffSwitchG(conf, {optSeqDestructors}, arg, pass, info) + if pass in {passCmd2, passPP}: + defineSymbol(conf.symbols, "nimSeqsV2") + of "go": + unregisterArcOrc(conf) + conf.selectedGC = gcGo + defineSymbol(conf.symbols, "gogc") + of "none": + unregisterArcOrc(conf) + conf.selectedGC = gcNone + defineSymbol(conf.symbols, "nogc") + of "stack", "regions": + unregisterArcOrc(conf) + conf.selectedGC = gcRegions + defineSymbol(conf.symbols, "gcregions") + of "v2": warningOptionNoop(arg) + else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg) + proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; conf: ConfigRef) = var @@ -626,48 +670,11 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; processOnOffSwitchG(conf, {optForceFullMake}, arg, pass, info) of "project": processOnOffSwitchG(conf, {optWholeProject, optGenIndex}, arg, pass, info) - of "gc", "mm": - if conf.backend == backendJs: return # for: bug #16033 - expectArg(conf, switch, arg, pass, info) - if pass in {passCmd2, passPP}: - case arg.normalize - of "boehm": - unregisterArcOrc(conf) - conf.selectedGC = gcBoehm - defineSymbol(conf.symbols, "boehmgc") - incl conf.globalOptions, optTlsEmulation # Boehm GC doesn't scan the real TLS - of "refc": - unregisterArcOrc(conf) - conf.selectedGC = gcRefc - of "markandsweep": - unregisterArcOrc(conf) - conf.selectedGC = gcMarkAndSweep - defineSymbol(conf.symbols, "gcmarkandsweep") - of "destructors", "arc": - registerArcOrc(pass, conf, false) - of "orc": - registerArcOrc(pass, conf, true) - of "hooks": - conf.selectedGC = gcHooks - defineSymbol(conf.symbols, "gchooks") - incl conf.globalOptions, optSeqDestructors - processOnOffSwitchG(conf, {optSeqDestructors}, arg, pass, info) - if pass in {passCmd2, passPP}: - defineSymbol(conf.symbols, "nimSeqsV2") - of "go": - unregisterArcOrc(conf) - conf.selectedGC = gcGo - defineSymbol(conf.symbols, "gogc") - of "none": - unregisterArcOrc(conf) - conf.selectedGC = gcNone - defineSymbol(conf.symbols, "nogc") - of "stack", "regions": - unregisterArcOrc(conf) - conf.selectedGC = gcRegions - defineSymbol(conf.symbols, "gcregions") - of "v2": warningOptionNoop(arg) - else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg) + of "gc": + warningDeprecated(conf, info, "`gc:option` is deprecated; use `mm:option` instead") + processMemoryManagementOption(switch, arg, pass, info, conf) + of "mm": + processMemoryManagementOption(switch, arg, pass, info, conf) of "warnings", "w": if processOnOffSwitchOrList(conf, {optWarns}, arg, pass, info): listWarnings(conf) of "warning": processSpecificNote(arg, wWarning, pass, info, switch, conf) From 15ae9323e8f1e045bdd68e16231528f8c08fb611 Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 25 Apr 2022 17:19:21 +0800 Subject: [PATCH 2896/3768] fix NimNode comment repr() regression [backport: 1.2] (#19726) --- compiler/renderer.nim | 2 ++ tests/macros/tmacros1.nim | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index 3503c4bf6..f847aa094 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -165,6 +165,7 @@ proc putNL(g: var TSrcGen) = proc optNL(g: var TSrcGen, indent: int) = g.pendingNL = indent g.lineLen = indent + g.col = g.indent when defined(nimpretty): g.pendingNewlineCount = 0 proc optNL(g: var TSrcGen) = @@ -173,6 +174,7 @@ proc optNL(g: var TSrcGen) = proc optNL(g: var TSrcGen; a, b: PNode) = g.pendingNL = g.indent g.lineLen = g.indent + g.col = g.indent when defined(nimpretty): g.pendingNewlineCount = lineDiff(a, b) proc indentNL(g: var TSrcGen) = diff --git a/tests/macros/tmacros1.nim b/tests/macros/tmacros1.nim index 53af4287c..c588ff7e6 100644 --- a/tests/macros/tmacros1.nim +++ b/tests/macros/tmacros1.nim @@ -60,3 +60,22 @@ macro foo(t: static Tuple): untyped = doAssert t.b == 12345 foo((a: "foo", b: 12345)) + + +# bug #16307 + +macro bug(x: untyped): string = + newLit repr(x) + +let res = bug: + block: + ## one + ## two + ## three + +doAssert res == """ + +block: + ## one + ## two + ## three""" From ee0a47029443e6d717ed4a85825b7dcf971dbdf8 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 25 Apr 2022 11:59:37 +0200 Subject: [PATCH 2897/3768] Revert "fix db_sqlite.tryInsertID does raise exceptions in 1.6.0 #19743 (#19744)" (#19745) This reverts commit b10f0e7bca43761316f6424786a771af33254e19. --- lib/impure/db_sqlite.nim | 25 +++++++++++-------------- 1 file changed, 11 insertions(+), 14 deletions(-) diff --git a/lib/impure/db_sqlite.nim b/lib/impure/db_sqlite.nim index db40dddd8..1638d38c6 100644 --- a/lib/impure/db_sqlite.nim +++ b/lib/impure/db_sqlite.nim @@ -635,7 +635,7 @@ proc getValue*(db: DbConn, stmtName: SqlPrepared): string proc tryInsertID*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): int64 - {.tags: [WriteDbEffect], raises: [].} = + {.tags: [WriteDbEffect], raises: [DbError].} = ## Executes the query (typically "INSERT") and returns the ## generated ID for the row or -1 in case of an error. ## @@ -650,19 +650,16 @@ proc tryInsertID*(db: DbConn, query: SqlQuery, ## 1, "item#1") == -1 ## db.close() assert(not db.isNil, "Database not connected.") + var q = dbFormat(query, args) + var stmt: sqlite3.PStmt result = -1 - try: - var q = dbFormat(query, args) - var stmt: sqlite3.PStmt - if prepare_v2(db, q, q.len.cint, stmt, nil) == SQLITE_OK: - if step(stmt) == SQLITE_DONE: - result = last_insert_rowid(db) - if finalize(stmt) != SQLITE_OK: - result = -1 - else: - discard finalize(stmt) - except DbError: - discard + if prepare_v2(db, q, q.len.cint, stmt, nil) == SQLITE_OK: + if step(stmt) == SQLITE_DONE: + result = last_insert_rowid(db) + if finalize(stmt) != SQLITE_OK: + result = -1 + else: + discard finalize(stmt) proc insertID*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): int64 {.tags: [WriteDbEffect].} = @@ -695,7 +692,7 @@ proc insertID*(db: DbConn, query: SqlQuery, proc tryInsert*(db: DbConn, query: SqlQuery, pkName: string, args: varargs[string, `$`]): int64 - {.tags: [WriteDbEffect], raises: [], since: (1, 3).} = + {.tags: [WriteDbEffect], raises: [DbError], since: (1, 3).} = ## same as tryInsertID tryInsertID(db, query, args) From 42ac50e988a4734624b204740613c795a0e789a3 Mon Sep 17 00:00:00 2001 From: flywind Date: Mon, 25 Apr 2022 18:00:23 +0800 Subject: [PATCH 2898/3768] fixes #19662; Indent level errored for first line (#19718) --- compiler/parser.nim | 4 ++++ compiler/passes.nim | 1 + compiler/syntaxes.nim | 2 +- tests/parser/t19662.nim | 6 ++++++ 4 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 tests/parser/t19662.nim diff --git a/compiler/parser.nim b/compiler/parser.nim index 7973f7d37..52466d9fa 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -2393,6 +2393,10 @@ proc parseAll(p: var Parser): PNode = if p.tok.indent != 0: parMessage(p, errInvalidIndentation) +proc checkFirstLineIndentation*(p: var Parser) = + if p.tok.indent != 0 and p.tok.strongSpaceA > 0: + parMessage(p, errInvalidIndentation) + proc parseTopLevelStmt(p: var Parser): PNode = ## Implements an iterator which, when called repeatedly, returns the next ## top-level statement or emptyNode if end of stream. diff --git a/compiler/passes.nim b/compiler/passes.nim index ce52b10c5..7fb2842f5 100644 --- a/compiler/passes.nim +++ b/compiler/passes.nim @@ -150,6 +150,7 @@ proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator; processImplicits graph, graph.config.implicitImports, nkImportStmt, a, module processImplicits graph, graph.config.implicitIncludes, nkIncludeStmt, a, module + checkFirstLineIndentation(p) while true: if graph.stopCompile(): break var n = parseTopLevelStmt(p) diff --git a/compiler/syntaxes.nim b/compiler/syntaxes.nim index e47b0483d..c00fe8b67 100644 --- a/compiler/syntaxes.nim +++ b/compiler/syntaxes.nim @@ -16,7 +16,7 @@ import when defined(nimPreviewSlimSystem): import std/[syncio, assertions] -export Parser, parseAll, parseTopLevelStmt, closeParser +export Parser, parseAll, parseTopLevelStmt, checkFirstLineIndentation, closeParser type FilterKind = enum diff --git a/tests/parser/t19662.nim b/tests/parser/t19662.nim new file mode 100644 index 000000000..7a1864ac6 --- /dev/null +++ b/tests/parser/t19662.nim @@ -0,0 +1,6 @@ + var i: int # bug #19662 +echo i + +discard """ + errormsg: "invalid indentation" +""" \ No newline at end of file From 82680a12a7a6acfbb6f5fdd22c042e409081b812 Mon Sep 17 00:00:00 2001 From: ehmry Date: Mon, 25 Apr 2022 15:16:11 -0500 Subject: [PATCH 2899/3768] macros: make hasCustomPragma more permissive (#19747) Make hasCustomPragma return false rather than fail for invalid parameters. --- lib/core/macros.nim | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index ba19712cf..b370aaa7b 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -1521,7 +1521,7 @@ proc extractTypeImpl(n: NimNode): NimNode = else: error("Invalid node to retrieve type implementation of: " & $n.kind) proc customPragmaNode(n: NimNode): NimNode = - expectKind(n, {nnkSym, nnkDotExpr, nnkBracketExpr, nnkTypeOfExpr, nnkCheckedFieldExpr}) + expectKind(n, {nnkSym, nnkDotExpr, nnkBracketExpr, nnkTypeOfExpr, nnkType, nnkCheckedFieldExpr}) let typ = n.getTypeInst() @@ -1532,7 +1532,9 @@ proc customPragmaNode(n: NimNode): NimNode = if kind(typ[1]) == nnkBracketExpr: typ[1][0] else: typ[1] ) - if impl[0].kind == nnkPragmaExpr: + if impl.kind == nnkNilLit: + return impl + elif impl[0].kind == nnkPragmaExpr: return impl[0][1] else: return impl[0] # handle types which don't have macro at all @@ -1560,7 +1562,7 @@ proc customPragmaNode(n: NimNode): NimNode = while typDef != nil: typDef.expectKind(nnkTypeDef) let typ = typDef[2].extractTypeImpl() - typ.expectKind({nnkRefTy, nnkPtrTy, nnkObjectTy}) + if typ.kind notin {nnkRefTy, nnkPtrTy, nnkObjectTy}: break let isRef = typ.kind in {nnkRefTy, nnkPtrTy} if isRef and typ[0].kind in {nnkSym, nnkBracketExpr}: # defines ref type for another object(e.g. X = ref X) typDef = getImpl(typ[0]) From ef4ac5a0d23fcffac9963670b73fa69ad0d0aa73 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 26 Apr 2022 08:58:15 +0200 Subject: [PATCH 2900/3768] use signed comparisons for the index checking in the hope it improves the code generation (#19712) --- compiler/ccgexprs.nim | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 1a3e217b2..5a58b4bcd 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -988,12 +988,12 @@ proc genBoundsCheck(p: BProc; arr, a, b: TLoc) = if reifiedOpenArray(arr.lode): linefmt(p, cpsStmts, "if ($2-$1 != -1 && " & - "((NU)($1) >= (NU)($3.Field1) || (NU)($2) >= (NU)($3.Field1))){ #raiseIndexError(); $4}$n", + "($1 < 0 || $1 >= $3.Field1 || $2 < 0 || $2 >= $3.Field1)){ #raiseIndexError(); $4}$n", [rdLoc(a), rdLoc(b), rdLoc(arr), raiseInstr(p)]) else: linefmt(p, cpsStmts, - "if ($2-$1 != -1 && " & - "((NU)($1) >= (NU)($3Len_0) || (NU)($2) >= (NU)($3Len_0))){ #raiseIndexError(); $4}$n", + "if ($2-$1 != -1 && ($1 < 0 || $1 >= $3Len_0 || $2 < 0 || $2 >= $3Len_0))" & + "{ #raiseIndexError(); $4}$n", [rdLoc(a), rdLoc(b), rdLoc(arr), raiseInstr(p)]) of tyArray: let first = intLiteral(firstOrd(p.config, ty)) @@ -1004,7 +1004,7 @@ proc genBoundsCheck(p: BProc; arr, a, b: TLoc) = of tySequence, tyString: linefmt(p, cpsStmts, "if ($2-$1 != -1 && " & - "((NU)($1) >= (NU)$3 || (NU)($2) >= (NU)$3)){ #raiseIndexError(); $4}$n", + "($1 < 0 || $1 >= $3 || $2 < 0 || $2 >= $3)){ #raiseIndexError(); $4}$n", [rdLoc(a), rdLoc(b), lenExpr(p, arr), raiseInstr(p)]) else: discard @@ -1015,14 +1015,14 @@ proc genOpenArrayElem(p: BProc, n, x, y: PNode, d: var TLoc) = if not reifiedOpenArray(x): # emit range check: if optBoundsCheck in p.options: - linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2Len_0)){ #raiseIndexError2($1,$2Len_0-1); $3}$n", + linefmt(p, cpsStmts, "if ($1 < 0 || $1 >= $2Len_0){ #raiseIndexError2($1,$2Len_0-1); $3}$n", [rdCharLoc(b), rdLoc(a), raiseInstr(p)]) # BUGFIX: ``>=`` and not ``>``! inheritLocation(d, a) putIntoDest(p, d, n, ropecg(p.module, "$1[$2]", [rdLoc(a), rdCharLoc(b)]), a.storage) else: if optBoundsCheck in p.options: - linefmt(p, cpsStmts, "if ((NU)($1) >= (NU)($2.Field1)){ #raiseIndexError2($1,$2.Field1-1); $3}$n", + linefmt(p, cpsStmts, "if ($1 < 0 || $1 >= $2.Field1){ #raiseIndexError2($1,$2.Field1-1); $3}$n", [rdCharLoc(b), rdLoc(a), raiseInstr(p)]) # BUGFIX: ``>=`` and not ``>``! inheritLocation(d, a) putIntoDest(p, d, n, @@ -1037,7 +1037,7 @@ proc genSeqElem(p: BProc, n, x, y: PNode, d: var TLoc) = ty = skipTypes(ty.lastSon, abstractVarRange) # emit range check: if optBoundsCheck in p.options: linefmt(p, cpsStmts, - "if ((NU)($1) >= (NU)$2){ #raiseIndexError2($1,$2-1); $3}$n", + "if ($1 < 0 || $1 >= $2){ #raiseIndexError2($1,$2-1); $3}$n", [rdCharLoc(b), lenExpr(p, a), raiseInstr(p)]) if d.k == locNone: d.storage = OnHeap if skipTypes(a.t, abstractVar).kind in {tyRef, tyPtr}: From 8bfc396a4dfa722239818f399a119452a53fe07f Mon Sep 17 00:00:00 2001 From: flywind Date: Wed, 27 Apr 2022 02:14:39 +0800 Subject: [PATCH 2901/3768] fixes #18612; apply cache and memcmp for methods in arc/orc (#19749) * try using endsWith * use memcmp * add cache * cleanup * better * minor * fix * improve test coverage for methods with ARC --- compiler/ccgexprs.nim | 7 +++++-- lib/system/arc.nim | 30 +++++++++++++++++++++++++++--- tests/metatype/ttypedesc3.nim | 3 ++- tests/method/tgeneric_methods.nim | 1 + tests/method/tmethod_issues.nim | 1 + tests/method/tmethod_various.nim | 1 + tests/method/tsingle_methods.nim | 2 +- tests/misc/parsecomb.nim | 4 ++++ tests/pragmas/tlocks.nim | 3 +++ 9 files changed, 45 insertions(+), 7 deletions(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 5a58b4bcd..8e7a21c67 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1615,8 +1615,11 @@ proc genNewFinalize(p: BProc, e: PNode) = proc genOfHelper(p: BProc; dest: PType; a: Rope; info: TLineInfo): Rope = if optTinyRtti in p.config.globalOptions: - result = ropecg(p.module, "#isObj($1.m_type, $2)", - [a, genTypeInfo2Name(p.module, dest)]) + let ti = genTypeInfo2Name(p.module, dest) + inc p.module.labels + let cache = "Nim_OfCheck_CACHE" & p.module.labels.rope + p.module.s[cfsVars].addf("static TNimTypeV2* $#[2];$n", [cache]) + result = ropecg(p.module, "#isObjWithCache($#.m_type, $#, $#)", [a, ti, cache]) else: # unfortunately 'genTypeInfoV1' sets tfObjHasKids as a side effect, so we # have to call it here first: diff --git a/lib/system/arc.nim b/lib/system/arc.nim index d66f4b997..17142b277 100644 --- a/lib/system/arc.nim +++ b/lib/system/arc.nim @@ -227,10 +227,34 @@ template tearDownForeignThreadGc* = ## With `--gc:arc` a nop. discard -proc isObj(obj: PNimTypeV2, subclass: cstring): bool {.compilerRtl, inl.} = - proc strstr(s, sub: cstring): cstring {.header: "", importc.} +type ObjCheckCache = array[0..1, PNimTypeV2] - result = strstr(obj.name, subclass) != nil +proc memcmp(str1, str2: cstring, n: csize_t): cint {.importc, header: "".} + +func endsWith(s, suffix: cstring): bool {.inline.} = + let + sLen = s.len + suffixLen = suffix.len + + if suffixLen <= sLen: + result = memcmp(cstring(addr s[sLen - suffixLen]), suffix, csize_t(suffixLen)) == 0 + +proc isObj(obj: PNimTypeV2, subclass: cstring): bool {.compilerRtl, inl.} = + result = endsWith(obj.name, subclass) + +proc isObjSlowPath(obj: PNimTypeV2, subclass: cstring, cache: var ObjCheckCache): bool {.compilerRtl, inline.} = + if endsWith(obj.name, subclass): + cache[1] = obj + result = true + else: + cache[0] = obj + result = false + +proc isObjWithCache(obj: PNimTypeV2, subclass: cstring, cache: var ObjCheckCache): bool {.compilerRtl.} = + if cache[0] == obj: result = false + elif cache[1] == obj: result = true + else: + result = isObjSlowPath(obj, subclass, cache) proc chckObj(obj: PNimTypeV2, subclass: cstring) {.compilerRtl.} = # checks if obj is of type subclass: diff --git a/tests/metatype/ttypedesc3.nim b/tests/metatype/ttypedesc3.nim index 98a59f613..d3a58853d 100644 --- a/tests/metatype/ttypedesc3.nim +++ b/tests/metatype/ttypedesc3.nim @@ -1,5 +1,6 @@ discard """ -output: ''' + matrix: "--mm:arc; --mm:refc" + output: ''' proc Base proc Child method Base diff --git a/tests/method/tgeneric_methods.nim b/tests/method/tgeneric_methods.nim index 0e2aeeede..ab92c66d8 100644 --- a/tests/method/tgeneric_methods.nim +++ b/tests/method/tgeneric_methods.nim @@ -1,4 +1,5 @@ discard """ + matrix: "--mm:arc; --mm:refc" output: '''wow2 X 1 X 3''' diff --git a/tests/method/tmethod_issues.nim b/tests/method/tmethod_issues.nim index df4c3771a..13467f2b3 100644 --- a/tests/method/tmethod_issues.nim +++ b/tests/method/tmethod_issues.nim @@ -1,4 +1,5 @@ discard """ + matrix: "--mm:arc; --mm:refc" output: ''' wof! wof! diff --git a/tests/method/tmethod_various.nim b/tests/method/tmethod_various.nim index fd022717b..f9d067a72 100644 --- a/tests/method/tmethod_various.nim +++ b/tests/method/tmethod_various.nim @@ -1,4 +1,5 @@ discard """ + matrix: "--mm:arc; --mm:refc" output: ''' do nothing HELLO WORLD! diff --git a/tests/method/tsingle_methods.nim b/tests/method/tsingle_methods.nim index 40269559a..b564e7d87 100644 --- a/tests/method/tsingle_methods.nim +++ b/tests/method/tsingle_methods.nim @@ -1,5 +1,5 @@ discard """ - cmd: "nim c --multimethods:off $file" + matrix: "--mm:arc --multimethods:off; --mm:refc --multimethods:off" output: '''base base base diff --git a/tests/misc/parsecomb.nim b/tests/misc/parsecomb.nim index 4ff2f65d2..4f149cbf6 100644 --- a/tests/misc/parsecomb.nim +++ b/tests/misc/parsecomb.nim @@ -1,3 +1,7 @@ +discard """ + matrix: "--mm:arc; --mm:refc" +""" + type Input[T] = object toks: seq[T] index: int diff --git a/tests/pragmas/tlocks.nim b/tests/pragmas/tlocks.nim index ba66a2dca..6c2a9f9e9 100644 --- a/tests/pragmas/tlocks.nim +++ b/tests/pragmas/tlocks.nim @@ -1,3 +1,6 @@ +discard """ + matrix: "--mm:arc; --mm:refc" +""" type SomeBase* = ref object of RootObj type SomeDerived* = ref object of SomeBase From e4a2c2d474f725b01acfa3a6169686f5e16f2ce9 Mon Sep 17 00:00:00 2001 From: Ivan Yonchovski Date: Fri, 29 Apr 2022 12:16:07 +0300 Subject: [PATCH 2902/3768] Make sure that field usage preserves the original line info (#19751) Currently `struct.field` will generate a node with `info` that points to the symbol definition instead of having the actual node location. --- compiler/semexprs.nim | 2 ++ nimsuggest/tests/tuse_structure.nim | 15 +++++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 nimsuggest/tests/tuse_structure.nim diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 702b47929..b8742ff65 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1436,8 +1436,10 @@ proc builtinFieldAccess(c: PContext, n: PNode, flags: TExprFlags): PNode = # is the access to a public field or in the same module or in a friend? markUsed(c, n[1].info, f) onUse(n[1].info, f) + let info = n[1].info n[0] = makeDeref(n[0]) n[1] = newSymNode(f) # we now have the correct field + n[1].info = info # preserve the original info n.typ = f.typ if check == nil: result = n diff --git a/nimsuggest/tests/tuse_structure.nim b/nimsuggest/tests/tuse_structure.nim new file mode 100644 index 000000000..f65ab9060 --- /dev/null +++ b/nimsuggest/tests/tuse_structure.nim @@ -0,0 +1,15 @@ +# tests for use and structures + +type + Foo* = ref object of RootObj + bar*: string + +proc test(f: Foo) = + echo f.#[!]#bar + +discard """ +$nimsuggest --tester $file +>use $1 +def skField tuse_structure.Foo.bar string $file 5 4 "" 100 +use skField tuse_structure.Foo.bar string $file 8 9 "" 100 +""" From 4680ab61c06782d142492d1fcdebf8e942373c09 Mon Sep 17 00:00:00 2001 From: nc-x Date: Sat, 30 Apr 2022 19:28:58 +0530 Subject: [PATCH 2903/3768] Fix fixAbstractType for user defined typeclasses, fixes #19730 & #18409 (#19732) --- compiler/semexprs.nim | 2 +- tests/concepts/t18409.nim | 37 +++++++++++++++++++++++++++++++++++++ tests/concepts/t19730.nim | 20 ++++++++++++++++++++ 3 files changed, 58 insertions(+), 1 deletion(-) create mode 100644 tests/concepts/t18409.nim create mode 100644 tests/concepts/t19730.nim diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index b8742ff65..ed28d8145 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -645,7 +645,7 @@ proc fixAbstractType(c: PContext, n: PNode) = skipTypes(it.typ, abstractVar).kind notin {tyOpenArray, tyVarargs}: if skipTypes(it[1].typ, abstractVar).kind in {tyNil, tyTuple, tySet} or it[1].isArrayConstr: - var s = skipTypes(it.typ, abstractVar) + var s = skipTypes(it.typ, abstractVar + tyUserTypeClasses) if s.kind != tyUntyped: changeType(c, it[1], s, check=true) n[i] = it[1] diff --git a/tests/concepts/t18409.nim b/tests/concepts/t18409.nim new file mode 100644 index 000000000..0edba2d31 --- /dev/null +++ b/tests/concepts/t18409.nim @@ -0,0 +1,37 @@ +discard """ + action: "compile" +""" + +# A vector space over a field F concept. +type VectorSpace*[F] = concept x, y, type V + vector_add(x, y) is V + scalar_mul(x, F) is V + dimension(V) is Natural + +# Real numbers (here floats) form a vector space. +func vector_add*(v: float, w: float): float = v + w +func scalar_mul*(v: float, s: float): float = v * s +func dimension*(x: typedesc[float]): Natural = 1 + +# 2-tuples of real numbers form a vector space. +func vector_add*(v, w: (float, float)): (float, float) = + (vector_add(v[0], w[0]), vector_add(v[1], w[1])) + +func scalar_mul*(v: (float, float), s: float): (float, float) = + (scalar_mul(v[0], s), scalar_mul(v[1], s)) + +func dimension*(x: typedesc[(float, float)]): Natural = 2 + +# Check concept requirements. +assert float is VectorSpace +assert (float, float) is VectorSpace + +# Commutivity axiom for vector spaces over the same field. +func axiom_commutivity*[F](u, v: VectorSpace[F]): bool = + vector_add(u, v) == vector_add(v, u) + +# This is okay. +assert axiom_commutivity(2.2, 3.3) + +# This is not. +assert axiom_commutivity((2.2, 3.3), (4.4, 5.5)) diff --git a/tests/concepts/t19730.nim b/tests/concepts/t19730.nim new file mode 100644 index 000000000..575d45dda --- /dev/null +++ b/tests/concepts/t19730.nim @@ -0,0 +1,20 @@ +discard """ + output: '''1.01.01.01.0 +1.01.01.01.0 +''' +""" + +type + Color = concept c + c.r is SomeFloat + c.g is SomeFloat + c.b is SomeFloat + c.a is SomeFloat + +proc useColor(color: Color) = + echo(color.r, color.g, color.b, color.a) + +let color = (r: 1.0, g: 1.0, b: 1.0, a: 1.0) +useColor(color) + +useColor((r: 1.0, g: 1.0, b: 1.0, a: 1.0)) From 927978345bbf57631828287850eda13b82277eb7 Mon Sep 17 00:00:00 2001 From: huantian Date: Mon, 2 May 2022 09:06:57 -0700 Subject: [PATCH 2904/3768] Rework discarding futures documentation in asyncdispatch (#19738) * Rework discarding futures docs in asyncdispatch * Fix typos Co-authored-by: Danil Yarantsev * Use rst note:: Co-authored-by: flywind * Split discarding and handling futures. * Update lib/pure/asyncdispatch.nim * Update lib/pure/asyncdispatch.nim * Update lib/pure/asyncdispatch.nim * Update lib/pure/asyncdispatch.nim Co-authored-by: Danil Yarantsev Co-authored-by: flywind Co-authored-by: Dominik Picheta --- lib/pure/asyncdispatch.nim | 39 ++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index b1e1c19fd..755f59ff7 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -123,15 +123,42 @@ ## if future.failed: ## # Handle exception ## -## ## Discarding futures ## ================== ## -## Futures should **never** be discarded. This is because they may contain -## errors. If you do not care for the result of a Future then you should -## use the `asyncCheck` procedure instead of the `discard` keyword. Note -## however that this does not wait for completion, and you should use -## `waitFor` for that purpose. +## Futures should **never** be discarded directly because they may contain +## errors. If you do not care for the result of a Future then you should use +## the `asyncCheck` procedure instead of the `discard` keyword. Note that this +## does not wait for completion, and you should use `waitFor` or `await` for that purpose. +## +## .. note:: `await` also checks if the future fails, so you can safely discard +## its result. +## +## Handling futures +## ================ +## +## There are many different operations that apply to a future. +## The three primary high-level operations are `asyncCheck`, +## `waitFor`, and `await`. +## +## * `asyncCheck`: Raises an exception if the future fails. It neither waits +## for the future to finish nor returns the result of the future. +## * `waitFor`: Polls the event loop and blocks the current thread until the +## future finishes. This is often used to call an async procedure from a +## synchronous context and should never be used in an `async` proc. +## * `await`: Pauses execution in the current async procedure until the future +## finishes. While the current procedure is paused, other async procedures will +## continue running. Should be used instead of `waitFor` in an async +## procedure. +## +## Here is a handy quick reference chart showing their high-level differences: +## ============== ===================== ======================= +## Procedure Context Blocking +## ============== ===================== ======================= +## `asyncCheck` non-async and async non-blocking +## `waitFor` non-async blocks current thread +## `await` async suspends current proc +## ============== ===================== ======================= ## ## Examples ## ======== From 832b8c50345eab6e6f3d037d6e521831007cb6ed Mon Sep 17 00:00:00 2001 From: Jake Leahy Date: Tue, 3 May 2022 12:08:39 +1000 Subject: [PATCH 2905/3768] Fix link to experimental manual in macro pragmas section [skip ci] (#19753) --- doc/manual.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual.rst b/doc/manual.rst index adc1d9d16..a92d6e18f 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -7814,7 +7814,7 @@ the same way. There are a few more applications of macro pragmas, such as in type, variable and constant declarations, but this behavior is considered to be experimental and is documented in the `experimental manual -` instead. +`_ instead. Foreign function interface From 2ecae20c437179ff84e255f952dae7000f7ff316 Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 3 May 2022 18:35:16 +0800 Subject: [PATCH 2906/3768] switch to mainline bigints (#19756) --- testament/important_packages.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 6b0b15fd8..a55327211 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -39,7 +39,7 @@ pkg "arraymancer", "nim c tests/tests_cpu.nim" pkg "ast_pattern_matching", "nim c -r --oldgensym:on tests/test1.nim", allowFailure = true pkg "asyncthreadpool" pkg "awk" -pkg "bigints", url = "https://github.com/Araq/nim-bigints" +pkg "bigints" pkg "binaryheap", "nim c -r binaryheap.nim" pkg "BipBuffer" pkg "blscurve", allowFailure = true # pending https://github.com/status-im/nim-blscurve/issues/39 From 278ecad973c6581aeea0a6ff9372109b0dd6df5e Mon Sep 17 00:00:00 2001 From: Zoom Date: Wed, 4 May 2022 14:24:52 +0000 Subject: [PATCH 2907/3768] Add 'usages' option to the --stylechecks error msg (#19759) --- compiler/commands.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/commands.nim b/compiler/commands.nim index bab98b376..2e08ad615 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -116,7 +116,7 @@ const errInvalidCmdLineOption = "invalid command line option: '$1'" errOnOrOffExpectedButXFound = "'on' or 'off' expected, but '$1' found" errOnOffOrListExpectedButXFound = "'on', 'off' or 'list' expected, but '$1' found" - errOffHintsError = "'off', 'hint' or 'error' expected, but '$1' found" + errOffHintsError = "'off', 'hint', 'error' or 'usages' expected, but '$1' found" proc invalidCmdLineOption(conf: ConfigRef; pass: TCmdLinePass, switch: string, info: TLineInfo) = if switch == " ": localError(conf, info, errInvalidCmdLineOption % "-") @@ -508,7 +508,7 @@ proc registerArcOrc(pass: TCmdLinePass, conf: ConfigRef, isOrc: bool) = else: conf.selectedGC = gcArc defineSymbol(conf.symbols, "gcarc") - + defineSymbol(conf.symbols, "gcdestructors") incl conf.globalOptions, optSeqDestructors incl conf.globalOptions, optTinyRtti From a4401054cc7fbd199a8dfa3ec0f9760ca7498b8e Mon Sep 17 00:00:00 2001 From: Anthony Dario Date: Fri, 6 May 2022 02:16:00 -0700 Subject: [PATCH 2908/3768] Fix broken link in sets documentation. (#19769) --- lib/pure/collections/sets.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim index 7b1c58ac8..114e4582a 100644 --- a/lib/pure/collections/sets.nim +++ b/lib/pure/collections/sets.nim @@ -61,7 +61,7 @@ type HashSet*[A] {.myShallow.} = object ## \ ## A generic hash set. ## - ## Use `init proc <#init,HashSet[A]>`_ or `initHashSet proc <#initHashSet,int>`_ + ## Use `init proc <#init,HashSet[A]>`_ or `initHashSet proc <#initHashSet>`_ ## before calling other procs on it. data: KeyValuePairSeq[A] counter: int From 0455d24d555e4f83537af535c0b2e44291d1fe97 Mon Sep 17 00:00:00 2001 From: Zoom Date: Fri, 6 May 2022 09:19:27 +0000 Subject: [PATCH 2909/3768] Fix questionable suggestion in `strutils` docs (#19765) * Fix questionable suggestion in `stutils` docs - Removes the recommendation to pass a string slice for getting a relative index for `find` and `rfind` functions, as this currently makes a string copy, while a simple subtraction is enough. - Docstring for `SkipTable` type. * Doc layout fixup --- lib/pure/strutils.nim | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index bf7bd6aa8..b98bd2bec 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -278,7 +278,7 @@ func nimIdentNormalize*(s: string): string = ## except first one. ## ## .. Warning:: Backticks (`) are not handled: they remain *as is* and - ## spaces are preserved. See `nimIdentBackticksNormalize + ## spaces are preserved. See `nimIdentBackticksNormalize ## `_ for ## an alternative approach. runnableExamples: @@ -1808,11 +1808,15 @@ func join*[T: not string](a: openArray[T], sep: string = ""): string = add(result, $x) type - SkipTable* = array[char, int] + SkipTable* = array[char, int] ## Character table for efficient substring search. func initSkipTable*(a: var SkipTable, sub: string) {.rtl, extern: "nsuInitSkipTable".} = - ## Preprocess table `a` for `sub`. + ## Initializes table `a` for efficient search of substring `sub`. + ## + ## See also: + ## * `find func<#find,SkipTable,string,string,Natural,int>`_ + # TODO: this should be the `default()` initializer for the type. let m = len(sub) fill(a, m) @@ -1826,6 +1830,9 @@ func find*(a: SkipTable, s, sub: string, start: Natural = 0, last = 0): int {. ## element). ## ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned. + ## + ## See also: + ## * `initSkipTable func<#initSkipTable,SkipTable,string>`_ let last = if last == 0: s.high else: last subLast = sub.len - 1 @@ -1865,7 +1872,7 @@ func find*(s: string, sub: char, start: Natural = 0, last = 0): int {.rtl, ## ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned. ## Otherwise the index returned is relative to `s[0]`, not `start`. - ## Use `s[start..last].rfind` for a `start`-origin index. + ## Subtract `start` from the result for a `start`-origin index. ## ## See also: ## * `rfind func<#rfind,string,char,Natural,int>`_ @@ -1893,7 +1900,7 @@ func find*(s: string, chars: set[char], start: Natural = 0, last = 0): int {. ## ## If `s` contains none of the characters in `chars`, -1 is returned. ## Otherwise the index returned is relative to `s[0]`, not `start`. - ## Use `s[start..last].find` for a `start`-origin index. + ## Subtract `start` from the result for a `start`-origin index. ## ## See also: ## * `rfind func<#rfind,string,set[char],Natural,int>`_ @@ -1910,7 +1917,7 @@ func find*(s, sub: string, start: Natural = 0, last = 0): int {.rtl, ## ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned. ## Otherwise the index returned is relative to `s[0]`, not `start`. - ## Use `s[start..last].find` for a `start`-origin index. + ## Subtract `start` from the result for a `start`-origin index. ## ## See also: ## * `rfind func<#rfind,string,string,Natural,int>`_ @@ -1950,7 +1957,7 @@ func rfind*(s: string, sub: char, start: Natural = 0, last = -1): int {.rtl, ## ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned. ## Otherwise the index returned is relative to `s[0]`, not `start`. - ## Use `s[start..last].find` for a `start`-origin index. + ## Subtract `start` from the result for a `start`-origin index. ## ## See also: ## * `find func<#find,string,char,Natural,int>`_ @@ -1968,7 +1975,7 @@ func rfind*(s: string, chars: set[char], start: Natural = 0, last = -1): int {. ## ## If `s` contains none of the characters in `chars`, -1 is returned. ## Otherwise the index returned is relative to `s[0]`, not `start`. - ## Use `s[start..last].rfind` for a `start`-origin index. + ## Subtract `start` from the result for a `start`-origin index. ## ## See also: ## * `find func<#find,string,set[char],Natural,int>`_ @@ -1986,7 +1993,7 @@ func rfind*(s, sub: string, start: Natural = 0, last = -1): int {.rtl, ## ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned. ## Otherwise the index returned is relative to `s[0]`, not `start`. - ## Use `s[start..last].rfind` for a `start`-origin index. + ## Subtract `start` from the result for a `start`-origin index. ## ## See also: ## * `find func<#find,string,string,Natural,int>`_ From 2c73e84436a11cae1676c7da0228158ba1a885cc Mon Sep 17 00:00:00 2001 From: kraptor Date: Sat, 7 May 2022 20:46:45 +0200 Subject: [PATCH 2910/3768] "ip" protocol as id 0 is long gone (#19760) IANA is using id 0 for "HOPOPT" instead of "ip" for some time now and those systems that still support the old mapping will stop doing so at some point in the future. Some BSDs and openSUSE are already following this change as per IANA list here: https://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml As new unixes (and maybe Windows, who knows) start to adopt the updated IANA list, this will keep failing from time to time, so it's better to remove the "ip" check altogether. --- tests/stdlib/tgetprotobyname.nim | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/stdlib/tgetprotobyname.nim b/tests/stdlib/tgetprotobyname.nim index 0b60d059f..014c18845 100644 --- a/tests/stdlib/tgetprotobyname.nim +++ b/tests/stdlib/tgetprotobyname.nim @@ -1,9 +1,5 @@ import nativesockets -when not defined(netbsd): - # Ref: https://github.com/nim-lang/Nim/issues/15452 - NetBSD doesn't define an `ip` protocol - doAssert getProtoByName("ip") == 0 - doAssert getProtoByName("ipv6") == 41 doAssert getProtoByName("tcp") == 6 doAssert getProtoByName("udp") == 17 From 8c100a37b93607806acec51733fe5e2fda392d44 Mon Sep 17 00:00:00 2001 From: kraptor Date: Sun, 8 May 2022 11:54:04 +0200 Subject: [PATCH 2911/3768] Add new self-signed 4096 bit certificate to testdata (#19758) The test tasyncssl may fail on modern linux versions as they require at least 2048 bit certificates. A new certificate and private key with default values has been added to meet this new requirement. --- tests/testdata/mycert.pem | 105 ++++++++++++++++++++++++++++---------- 1 file changed, 77 insertions(+), 28 deletions(-) diff --git a/tests/testdata/mycert.pem b/tests/testdata/mycert.pem index 69039fe04..61dcb685c 100644 --- a/tests/testdata/mycert.pem +++ b/tests/testdata/mycert.pem @@ -1,32 +1,81 @@ -----BEGIN PRIVATE KEY----- -MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAL9QXX/nuiFbizdI -Uhg1D9gG0GIANENvKwdWTlOlZAoOqvjXPFLGh+87yhvkq4f5FcICkSDao2SfeZcP -JsgD7T01owt8x48898+d91i7nIpr6IXGPyBxHOuaxAITY1D+MbbkhIGUVrEqKEOm -qfS9cqPZaDNkx8xVef0HPCmqEme9AgMBAAECgYBxqrQCvJFQJG3QiL2N+GjTdyj0 -MR7cOf6cu2CKPifz+ccHVgpXO/Gj6Cgq7nAjt5B/1rqXhI+zxzSc1bm6+OpIfakS -E0DLCFacECmL0v3c+XLxTtMhFZF5u7Yq0UMsuWmDSfRb4sbRjC+s+c51i5N0485k -b3un/MDI/i/jD/YZGQJBAPLtcuMIwEblUR1uw7NFezXdauXCRFkekoSlJNvpdM/Z -XDRcuWioek5yD8FvMpTz7H2e26Ev645JT5lIuN4Eti8CQQDJm+Qt9NYUohRsU279 -GYI3vXsXKKqmA22at4I3KRXPSeYV1vtQLYWWqGAXzgGkUEVBY0chmHyDcNwkUsNw -svHTAkEAwOTpD/vX6bOXOD7GqKgoULozcqNScE2FXExhuzliJtTakT17f+4fyABs -IFWynXIevBUTIqeRbJcr3HRRTwIAwwJBAJQ8XkL4IaxcG/4mPpY0ek13sZiumwKj -xKQcx869E78tS9LFFlW2kuHafYUjQIvLRZC1aWinUO3oPsUqYW9s82cCQFjoods5 -YsWEJB2RKCT5nhyAXEZLehxF+FXr+JjLMHkuEINKTnHHKjHJ7LbMcTCKUJAcKDTA -qZFEq5N1aT6DrAU= +MIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQDgpWhnNdNSUbwJ +39mKf+PA879K1wyT23LC3Ipo5g5vaRjB4vrSh5vzrjK5Q/z42yRDLQ2ojEdUne19 +1xMaM+WqTXnfkf9sou4VJ0HsdQ6jOU1LDRPrPvCjHLZjyMu18sGvQf4FATBwWN3n +QhmvcbleTeyv2pgMNeQDfjuIhRJ/aCIi/WYQ466+Rcj0y3/udYX1yPYf2mszXnSE +i2iWgdsjx0qkDU/nJhnKXhKfucAm9Ds0YfQdZYN3rEzfGkJzZUhDG1n30ghu+vUg +qqLi6HU/HqBxKxBlJf2HdMNK4VLkJuFC+wCI0JfF6VYhcNmtZzlQy3M9WjYvbXUY +nvMkQzUECaSaAtmbhFFfgey1Q3IEQS/j1lsVxtuGUE3YaoyaK22/CkVInDz0aka9 +CKq77x7S/V5PP/wwPV4s8XsbT6/34KemuY3oU84+bf5SBuWdzBY0hnDO9c/FaOnZ +4yjLMQi285HLpK417Z32DmzIkb7GjKtP9WLvsWGgbgquEjcFaIT+erkN09cDjsG/ +URWbKcsQkz8v9E4zj0rxdanXoJEtg1gm+dPsiXokZOLTeaMVtDoTkWeCM/VAp25J +MTc656QRUTSzUODK5tXQ3JgE2XIPGV/PsMi7T/q9xFBQmhUk5kfLNlQ1cy/aiTWK +8pjHcmxbyD121AimuTtv9CFYnYtzyQIDAQABAoICAAMYZFrfs/yzYZrlObMd1f6H +nUAjvGmhIXCr50BQwywnz46EWR5jffOal9pTpH2tT0+ZpFGJNUZmMqqENyAqTOTO +0noRIerWR9+EvfTLHBuFo5oAISEhqeEleSHg12W3ZZHLn/tjq84we0Y/c+kl8P7q +pfM6WNP6Ph0KNTnJU5rrzWScBzb+XB8FCSLOVwHrHqBnV3TS5p07lPFqllNUkLdq +fI3MHSi7LqnKKAmJXqtqvBIZs2pgRrJ0bk64pue+IoPCMbgnbbRRwuTjVQE5YLww +6NcGV+B86IRgSHyUpDa+jmYE3VoFPcIdV/F4A5fPD0wcsYbL4mk+4dkn/4OlZWqV +NZILp2IGejKaKtM1fr7fV2IRUbGUBN/+rX4I0SRnuq6Q4Ipx8VASbpgXQPBo9XTg +SHHsFbEu2DL8BHVgXdOy4PW6jQ0Ux2LhuJk6AQ5nIlFBYA+c8rSlZQXJbEbk1VZu +1i7iSOn/kx3ULMjUfhI/Ddm6rQqtiLbXSubXCzu1HMPT1FG9LUfAbq6EpiVkpAk1 +TqlciBHsyz//mk2RmIEx0Bt+0bX8FFGTIUiGyrp5s4hAHbgQZbXBAUYMRzWxhZ2Q +G0KBXx46bv3hJUb0GOgbNVxcaPnyrXaS/Hafcbx2LXlEtKiwGnC/yKJ7Hmcrt+AQ +RTaqNU1o/bkSYC7vHMZdAoIBAQD79uYPZPv5GLCKPZ64gc3+tbFXNqkmL7qv14yD +Gr3VubRbJe3Fx+T1cS+t9cjgOofhjFnwsDaFRoyWOYqRV/znwFsvvsDhHOLBr1u6 +qWQiF2CT1uMdXR3P6KD8h2DUVNNccxKqqIJNCR5oD/ngnnByWkQobzlsnoIdXgZm +ozBZjGr2XUMO5dJqUxaXZwY3j4I2hk/Ka9uroApyptl+DTVbPHvjk3MzU9QKUNor +vXEtQ8EmM8Oy6v/33HBmNs6cF5fMpgWz6u+B357OTxAfu8B42jZ18OeLrvkHFxzu +phOB1uXvqtQ0tdksSHHWj3IIZRK7GDGudnDEZ23vbCaxH4zbAoIBAQDkPn+5N3px +7UAECWrvT10TD3xKeqMkFhqRA9gmDE4N9AdoN6T6PzD7Tr3gOgGLq1tXjCjBqAlx +ZIDTnih3IK3xoRk2zmhq5+LfM8LQRAxAC8IsoQMXAsmW1KlS6MR70m50pFR8NK6r +UmOdrwVUKp3K6Mecid3LmMVLXGMUKwIJc1k7LJHtwrfi0i5xfBtiqQeaR0lJg4Zr ++zEL/4rHfcq06/P3k0+4uLKZ1LGOvwLPiTA3DADPWZbzUXo7McKOFWF/ycGQBrJq +AJikx15dVLnB16bnHXdxrlrd0LON2R+XfT4+dfRymqZLzrBI3L39t/elTmVYnD3P +punkmZuVwNErAoIBAQD5xOiOPibh6S2n/CmI8XQImIgx0kefSRUhFuV9WVbxtOMq +r9CijONUw1zmb40vahYk6gKGa8fAGg1nJadNKRHVkoNSMx/0h7PpGDIwOZa/jLj6 +FLyS8SmKXiqn6nN8SJI1RQUuE1kHkJCJy7yCg80oLn7+LjOYjxCgmAJ0YDSfsGif +zBebfws0xyTP9RrenO4RqtcR7BWYbk+tE+Tp5aIMzUpqcFJ0gRbjGv8K+QJmQpIH +kqzegcI4LFdnm9D4PxMFlVZ14eCGt+wuy4VKT84efwIZrDN77nmCI9FUaWFRBnxt +NsShc9rS4QWoEg6Sb88/lF47ecGTkIwUGPvJ/WKdAoIBAQClF7/zDPn4Zg+j29wJ +dXJxUwYoKUTP2V0l/43dF5Ft7lFdRMKEcCjR3kbhZZOwnyXW0X65dP4/kt7MMt46 +LN0kpc5DIlHM4iXsJNiJJG9n9BljhqNhhZajDvfbDJrypWdX33Vs0f511YZQjERi +eODh4DZiOCbCGaK7u/u+ns0+YLzuXHLBc9Lmsfj+BTMZzgG9ykpsbkJQ4MS9VP3h +BlAVRYaWUWucxZwKQRqdkfRKgYTqjDgZw0e4f/rVzkxX0YdQk3L65p0up3fB2KOd +BqfGWmJTUbEP/XmkcE0wERkUznazX0aNjucydjJ0wZZ7axIp8+bCjWD4TldoDuPH +Ek05AoIBAECgPfBHLQTAsI+wHbFsu/are28BOiJCSEXjRv88CbUbj/qgAppFuXbx +900WwJ1rVWd5x3LFa3VfyuAqYMi5jzmX9kWgEsC0WhgfyIRFiynw45LlcT4u3fWg +vJEx01lGgFVjnYfFUDS9d1MuiXGxIHrNhzHOP2x2CsS5vrFHav7iwG9YULEk8tJr +My0wzjF3UJ2/5DjGK56WuzauLYKrQ6Faw8dWUy4e/bNYId8wglhQQW548JwJEGmq +nq+EzTfEupXH57Bw7MGEOfdlhv98zNT9VcvBAN09vHeF3Hh6AM4aiGSUIt2HIkto +zvw+fqZ2Sk9O5qva+KE1QMVtY1EICI8= -----END PRIVATE KEY----- -----BEGIN CERTIFICATE----- -MIICgDCCAemgAwIBAgIJANpVfZSDAyNgMA0GCSqGSIb3DQEBBQUAMFkxCzAJBgNV -BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX -aWRnaXRzIFB0eSBMdGQxEjAQBgNVBAMMCWxvY2FsaG9zdDAeFw0xMjA2MDMxMjI4 -MDhaFw0xMzA2MDMxMjI4MDhaMFkxCzAJBgNVBAYTAkFVMRMwEQYDVQQIDApTb21l -LVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQxEjAQBgNV -BAMMCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAv1Bdf+e6 -IVuLN0hSGDUP2AbQYgA0Q28rB1ZOU6VkCg6q+Nc8UsaH7zvKG+Srh/kVwgKRINqj -ZJ95lw8myAPtPTWjC3zHjzz3z533WLucimvohcY/IHEc65rEAhNjUP4xtuSEgZRW -sSooQ6ap9L1yo9loM2THzFV5/Qc8KaoSZ70CAwEAAaNQME4wHQYDVR0OBBYEFF2n -Of61swO+XSNrYb4T02tGx8afMB8GA1UdIwQYMBaAFF2nOf61swO+XSNrYb4T02tG -x8afMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAEPHofdf4acaph5/e -+BzZGsMfRqdPgwp5sxjFKeTQI1A49VL7ykkb0iLKGfKZtvE8MjMrYjzt20E2bIZj -8eCivT6TbNrVRoACCly/lH9fZfWOG6dBu/85IrTAhSKi8yjbRzmjWUkdrcEJ+ZtV -1cahfFar4l4QwYgqp2pDd6ie+zE= +MIIFCTCCAvGgAwIBAgIUKqDcJ71wiMObIQ5sga2sZItNseowDQYJKoZIhvcNAQEL +BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTIyMDUwNDExMzAwN1oXDTIyMDYw +MzExMzAwN1owFDESMBAGA1UEAwwJbG9jYWxob3N0MIICIjANBgkqhkiG9w0BAQEF +AAOCAg8AMIICCgKCAgEA4KVoZzXTUlG8Cd/Zin/jwPO/StcMk9tywtyKaOYOb2kY +weL60oeb864yuUP8+NskQy0NqIxHVJ3tfdcTGjPlqk1535H/bKLuFSdB7HUOozlN +Sw0T6z7woxy2Y8jLtfLBr0H+BQEwcFjd50IZr3G5Xk3sr9qYDDXkA347iIUSf2gi +Iv1mEOOuvkXI9Mt/7nWF9cj2H9prM150hItoloHbI8dKpA1P5yYZyl4Sn7nAJvQ7 +NGH0HWWDd6xM3xpCc2VIQxtZ99IIbvr1IKqi4uh1Px6gcSsQZSX9h3TDSuFS5Cbh +QvsAiNCXxelWIXDZrWc5UMtzPVo2L211GJ7zJEM1BAmkmgLZm4RRX4HstUNyBEEv +49ZbFcbbhlBN2GqMmittvwpFSJw89GpGvQiqu+8e0v1eTz/8MD1eLPF7G0+v9+Cn +prmN6FPOPm3+UgblncwWNIZwzvXPxWjp2eMoyzEItvORy6SuNe2d9g5syJG+xoyr +T/Vi77FhoG4KrhI3BWiE/nq5DdPXA47Bv1EVmynLEJM/L/ROM49K8XWp16CRLYNY +JvnT7Il6JGTi03mjFbQ6E5FngjP1QKduSTE3OuekEVE0s1DgyubV0NyYBNlyDxlf +z7DIu0/6vcRQUJoVJOZHyzZUNXMv2ok1ivKYx3JsW8g9dtQIprk7b/QhWJ2Lc8kC +AwEAAaNTMFEwHQYDVR0OBBYEFEZcUeqH6MfIzC56BlD3NSs2mCgGMB8GA1UdIwQY +MBaAFEZcUeqH6MfIzC56BlD3NSs2mCgGMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI +hvcNAQELBQADggIBADGc7WONP6I6Trb7ici9fQ9qT3wh/RGDcmUDmDtARn9SFtOF +hsbszOMZg1Flj10fuD6OYDonKz4rv+Ieo5VkAYXxxd3J+bx2x1pqd1YSIsvugTwv +pnx39uBR9cjOmt4W7RyzhFnXoVfuSBE6LpkBUjcrqi5xwrQ31mOSCPwe8uDZYEWS +pX49MiHXGTyjQ481QLiOtTBZJa5igfnHUJbJbyZMa86zBQ/clS7+OeDwkvaEpjov +2VQf3QouVLghfLZYWSxWdEKD9+IWHn8rV6qksEb/Ogu4ZtzDRGqJow4j0DeSSEu7 +ns1YeT2mVTFwHjGXCWS+0iE885NDVX/b5YptlwH5PW7aqeXyCS9Hrd1C1GnXoXGp +NHltYRTyNWm974xWg7eu2gbbB8Ng02chXysdkBq7l+7OyA0a2EfX3Cbz3/49+Mqn +viqwNO5toSHVCdfV9Jd0p0CcqryYgyt2YNpJB+2nUQpiW4jviAs49PZg2PpCVw/2 +0cqtaPeUh26Si8UzDOuT697PIuGkZ9Q9QVwccVXtCyA0UpJ13P0fMrA+yEMhtwSs +k1tRm0pUQa6t3v26/cAy+kMhviHBJFwi5dx+y3OMvqQqpQJrgfZawm/o2ZQHy1KP +8m4ngrJzb13evKf216qCwllmQo6Ts4yeI1Ddx8UpdX7RUWpD8Uw4zSi7Th4r -----END CERTIFICATE----- From 61c1142667fabec11a7440b0404ce5688cad54d2 Mon Sep 17 00:00:00 2001 From: huantian Date: Sun, 8 May 2022 02:54:25 -0700 Subject: [PATCH 2912/3768] Update nimscript `withDir` doc example (#19776) --- lib/system/nimscript.nim | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/system/nimscript.nim b/lib/system/nimscript.nim index 5d1916c7f..0b49ea2e7 100644 --- a/lib/system/nimscript.nim +++ b/lib/system/nimscript.nim @@ -342,17 +342,17 @@ template withDir*(dir: string; body: untyped): untyped = ## Usage example: ## ## .. code-block:: nim + ## # inside /some/path/ ## withDir "foo": - ## # inside foo - ## #back to last dir - var curDir = getCurrentDir() + ## # move to /some/path/foo/ + ## # back in /some/path/ + let curDir = getCurrentDir() try: cd(dir) body finally: cd(curDir) - proc writeTask(name, desc: string) = if desc.len > 0: var spaces = " " From b2b878f4d62ff2dcaf69979bb0f96e6015b47605 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Sun, 8 May 2022 06:56:46 -0300 Subject: [PATCH 2913/3768] Remove deprecated posix proc (#19774) * Remove deprecated posix proc that takes wrong argument types * Remove deprecated posix proc that takes wrong argument types --- changelog.md | 2 +- lib/posix/posix.nim | 10 ---------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/changelog.md b/changelog.md index 8b9d0bc29..3b2685547 100644 --- a/changelog.md +++ b/changelog.md @@ -47,7 +47,7 @@ becomes an alias for `addr`. - Removed deprecated `math.c_frexp`. - Removed deprecated ``` httpcore.`==` ```. - Removed deprecated `std/dom_extensions`. - +- Removed deprecated `std/posix.CMSG_SPACE` and `std/posix.CMSG_LEN` that takes wrong argument types. - Remove deprecated `osproc.poDemon`, symbol with typo. - Deprecated `selfExe` for Nimscript. diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index 146ba886f..4ebae4361 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -894,19 +894,9 @@ proc CMSG_NXTHDR*(mhdr: ptr Tmsghdr, cmsg: ptr Tcmsghdr): ptr Tcmsghdr {. proc CMSG_FIRSTHDR*(mhdr: ptr Tmsghdr): ptr Tcmsghdr {. importc, header: "".} -{.push warning[deprecated]: off.} -proc CMSG_SPACE*(len: csize): csize {. - importc, header: "", deprecated: "argument `len` should be of type `csize_t`".} -{.pop.} - proc CMSG_SPACE*(len: csize_t): csize_t {. importc, header: "".} -{.push warning[deprecated]: off.} -proc CMSG_LEN*(len: csize): csize {. - importc, header: "", deprecated: "argument `len` should be of type `csize_t`".} -{.pop.} - proc CMSG_LEN*(len: csize_t): csize_t {. importc, header: "".} From 85bc8326acc9bf18e748055e770a40890e7ac069 Mon Sep 17 00:00:00 2001 From: Alfred Morgan Date: Tue, 10 May 2022 05:21:35 -0700 Subject: [PATCH 2914/3768] varargs example erroneously transformed "abc" to "def" (#19781) --- doc/manual.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual.rst b/doc/manual.rst index a92d6e18f..126b0f0d6 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -1556,7 +1556,7 @@ type conversions in this context: myWriteln(stdout, 123, "abc", 4.0) # is transformed to: - myWriteln(stdout, [$123, $"def", $4.0]) + myWriteln(stdout, [$123, $"abc", $4.0]) In this example `$` is applied to any argument that is passed to the parameter `a`. (Note that `$` applied to strings is a nop.) From b9f243eb2aeb4930ba552f8bf487719d628f4a2a Mon Sep 17 00:00:00 2001 From: Alfred Morgan Date: Tue, 10 May 2022 08:45:57 -0700 Subject: [PATCH 2915/3768] string is missing formatting when calling fmt (#19780) it appears the documentation intends to compare & with .fmt but there is no formatting in the string. even though the assert is true it doesn't quite prove that .fmt is an equivalent formatter. --- lib/pure/strformat.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pure/strformat.nim b/lib/pure/strformat.nim index ce3439600..247d0a296 100644 --- a/lib/pure/strformat.nim +++ b/lib/pure/strformat.nim @@ -705,6 +705,6 @@ macro `&`*(pattern: string{lit}): string = runnableExamples: let x = 7 assert &"{x}\n" == "7\n" # regular string literal - assert &"{x}\n" == "7\n".fmt # `fmt` can be used instead - assert &"{x}\n" != fmt"7\n" # see `fmt` docs, this would use a raw string literal + assert &"{x}\n" == "{x}\n".fmt # `fmt` can be used instead + assert &"{x}\n" != fmt"{x}\n" # see `fmt` docs, this would use a raw string literal strformatImpl(pattern.strVal, '{', '}') From c61eb5df32c66431d0d08417c39e268fdf236132 Mon Sep 17 00:00:00 2001 From: flywind Date: Fri, 13 May 2022 01:03:30 +0800 Subject: [PATCH 2916/3768] disable polypbren (#19787) * disable polypbren * Update important_packages.nim --- testament/important_packages.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index a55327211..c8fb5ad3c 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -103,7 +103,7 @@ pkg "nimfp", "nim c -o:nfp -r src/fp.nim" pkg "nimgame2", "nim c nimgame2/nimgame.nim" # XXX Doesn't work with deprecated 'randomize', will create a PR. pkg "nimgen", "nim c -o:nimgenn -r src/nimgen/runcfg.nim" -pkg "nimlsp", allowFailure = true +pkg "nimlsp" pkg "nimly", "nim c -r tests/test_readme_example.nim" pkg "nimongo", "nimble test_ci", allowFailure = true pkg "nimph", "nimble test", "https://github.com/disruptek/nimph", allowFailure = true @@ -125,7 +125,7 @@ pkg "patty" pkg "pixie" pkg "plotly", "nim c examples/all.nim" pkg "pnm" -pkg "polypbren" +pkg "polypbren", allowFailure = true pkg "prologue", "nimble tcompile" pkg "protobuf", "nim c -o:protobuff -r src/protobuf.nim" pkg "pylib" From 19001c070bbe7645ff45fcbd66ab221235715302 Mon Sep 17 00:00:00 2001 From: Anthony Dario Date: Thu, 12 May 2022 20:19:42 -0700 Subject: [PATCH 2917/3768] Fix typo in sequtils documentation (#19789) Found another small typo. --- lib/pure/collections/sequtils.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/collections/sequtils.nim b/lib/pure/collections/sequtils.nim index 5e9b492c2..39908e9c1 100644 --- a/lib/pure/collections/sequtils.nim +++ b/lib/pure/collections/sequtils.nim @@ -914,7 +914,7 @@ template foldl*(sequence, operation, first): untyped = ## ## The `operation` parameter should be an expression which uses the variables ## `a` and `b` for each step of the fold. The `first` parameter is the - ## start value (the first `a`) and therefor defines the type of the result. + ## start value (the first `a`) and therefore defines the type of the result. ## ## **See also:** ## * `foldr template<#foldr.t,untyped,untyped>`_ From 33888a73840a7f9fa46f79e613d488de2d193916 Mon Sep 17 00:00:00 2001 From: flywind Date: Tue, 17 May 2022 13:37:26 +0800 Subject: [PATCH 2918/3768] [manual] TLock => Lock (#19785) * [manual] TLock => Lock * minor --- doc/manual.rst | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/doc/manual.rst b/doc/manual.rst index 126b0f0d6..bfcb41b7a 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -8112,7 +8112,9 @@ Object fields and global variables can be annotated via a `guard` pragma: .. code-block:: nim - var glock: TLock + import std/locks + + var glock: Lock var gdata {.guard: glock.}: int The compiler then ensures that every access of `gdata` is within a `locks` @@ -8139,7 +8141,7 @@ that also implement some form of locking at runtime: .. code-block:: nim - template lock(a: TLock; body: untyped) = + template lock(a: Lock; body: untyped) = pthread_mutex_lock(a) {.locks: [a].}: try: @@ -8181,10 +8183,12 @@ the expressivity of the language: .. code-block:: nim + import std/locks + type ProtectedCounter = object v {.guard: L.}: int - L: TLock + L: Lock proc incCounters(counters: var openArray[ProtectedCounter]) = for i in 0..counters.high: From 06f02bb7716066241b04b2a92a3a61f539eadff2 Mon Sep 17 00:00:00 2001 From: Yardanico Date: Tue, 17 May 2022 10:56:39 +0300 Subject: [PATCH 2919/3768] Always use httpclient in nimgrab (#19767) --- tools/nimgrab.nim | 37 +--- tools/urldownloader.nim | 431 ---------------------------------------- 2 files changed, 9 insertions(+), 459 deletions(-) delete mode 100644 tools/urldownloader.nim diff --git a/tools/nimgrab.nim b/tools/nimgrab.nim index ee5eced1e..7e4161faf 100644 --- a/tools/nimgrab.nim +++ b/tools/nimgrab.nim @@ -1,33 +1,14 @@ +import std/[os, httpclient] +proc syncDownload(url, file: string) = + var client = newHttpClient() + proc onProgressChanged(total, progress, speed: BiggestInt) = + echo "Downloading " & url & " " & $(speed div 1000) & "kb/s" + echo clamp(int(progress*100 div total), 0, 100), "%" -when defined(windows): - import os, urldownloader - - proc syncDownload(url, file: string) = - proc progress(status: DownloadStatus, progress: uint, total: uint, - message: string) {.gcsafe.} = - echo "Downloading " & url - let t = total.BiggestInt - if t != 0: - echo clamp(int(progress.BiggestInt*100 div t), 0, 100), "%" - else: - echo "0%" - - downloadToFile(url, file, {optUseCache}, progress) - echo "100%" - -else: - import os, asyncdispatch, httpclient - - proc syncDownload(url, file: string) = - var client = newHttpClient() - proc onProgressChanged(total, progress, speed: BiggestInt) = - echo "Downloading " & url & " " & $(speed div 1000) & "kb/s" - echo clamp(int(progress*100 div total), 0, 100), "%" - - client.onProgressChanged = onProgressChanged - client.downloadFile(url, file) - echo "100%" + client.onProgressChanged = onProgressChanged + client.downloadFile(url, file) + echo "100%" if os.paramCount() != 2: quit "Usage: nimgrab " diff --git a/tools/urldownloader.nim b/tools/urldownloader.nim deleted file mode 100644 index 73e4034c9..000000000 --- a/tools/urldownloader.nim +++ /dev/null @@ -1,431 +0,0 @@ - -# -# -# Windows native FTP/HTTP/HTTPS file downloader -# (c) Copyright 2017 Eugene Kabanov -# -# See the file "LICENSE", included in this -# distribution, for details about the copyright. -# - -## This module implements native Windows FTP/HTTP/HTTPS downloading feature, -## using ``urlmon.UrlDownloadToFile()``. -## -## - -when not (defined(windows) or defined(nimdoc)): - {.error: "Platform is not supported.".} - -import os - -type - DownloadOptions* = enum - ## Available download options - optUseCache, ## Use Windows cache. - optUseProgressCallback, ## Report progress via callback. - optIgnoreSecurity ## Ignore HTTPS security problems. - - DownloadStatus* = enum - ## Available download status sent to ``progress`` callback. - statusProxyDetecting, ## Automatic Proxy detection. - statusCookieSent ## Cookie will be sent with request. - statusResolving, ## Resolving URL with DNS. - statusConnecting, ## Establish connection to server. - statusRedirecting ## HTTP redirection pending. - statusRequesting, ## Sending request to server. - statusMimetypeAvailable, ## Mimetype received from server. - statusBeginDownloading, ## Download process starting. - statusDownloading, ## Download process pending. - statusEndDownloading, ## Download process finished. - statusCacheAvailable ## File found in Windows cache. - statusUnsupported ## Unsupported status. - statusError ## Error happens. - - DownloadProgressCallback* = proc(status: DownloadStatus, progress: uint, - progressMax: uint, - message: string) - ## Progress callback. - ## - ## status - ## Indicate current stage of downloading process. - ## - ## progress - ## Number of bytes currently downloaded. Available only, if ``status`` is - ## ``statusBeginDownloading``, ``statusDownloading`` or - ## ``statusEndDownloading``. - ## - ## progressMax - ## Number of bytes expected to download. Available only, if ``status`` is - ## ``statusBeginDownloading``, ``statusDownloading`` or - ## ``statusEndDownloading``. - ## - ## message - ## Status message, which depends on ``status`` code. - ## - ## Available messages' values: - ## - ## statusResolving - ## URL hostname to be resolved. - ## statusConnecting - ## IP address - ## statusMimetypeAvailable - ## Downloading resource MIME type. - ## statusCacheAvailable - ## Path to filename stored in Windows cache. - -type - UUID = array[4, uint32] - - LONG = clong - ULONG = culong - HRESULT = clong - DWORD = uint32 - OLECHAR = uint16 - OLESTR = ptr OLECHAR - LPWSTR = OLESTR - UINT = cuint - REFIID = ptr UUID - -const - E_NOINTERFACE = 0x80004002'i32 - E_NOTIMPL = 0x80004001'i32 - S_OK = 0x00000000'i32 - - CP_UTF8 = 65001'u32 - - IID_IUnknown = UUID([0'u32, 0'u32, 192'u32, 1174405120'u32]) - IID_IBindStatusCallback = UUID([2045430209'u32, 298760953'u32, - 2852160140'u32, 195644160'u32]) - - BINDF_GETNEWESTVERSION = 0x00000010'u32 - BINDF_IGNORESECURITYPROBLEM = 0x00000100'u32 - BINDF_RESYNCHRONIZE = 0x00000200'u32 - BINDF_NO_UI = 0x00000800'u32 - BINDF_SILENTOPERATION = 0x00001000'u32 - BINDF_PRAGMA_NO_CACHE = 0x00002000'u32 - - ERROR_FILE_NOT_FOUND = 2 - ERROR_ACCESS_DENIED = 5 - - BINDSTATUS_FINDINGRESOURCE = 1 - BINDSTATUS_CONNECTING = 2 - BINDSTATUS_REDIRECTING = 3 - BINDSTATUS_BEGINDOWNLOADDATA = 4 - BINDSTATUS_DOWNLOADINGDATA = 5 - BINDSTATUS_ENDDOWNLOADDATA = 6 - BINDSTATUS_SENDINGREQUEST = 11 - BINDSTATUS_MIMETYPEAVAILABLE = 13 - BINDSTATUS_CACHEFILENAMEAVAILABLE = 14 - BINDSTATUS_PROXYDETECTING = 32 - BINDSTATUS_COOKIE_SENT = 34 - -type - STGMEDIUM = object - tymed: DWORD - pstg: pointer - pUnkForRelease: pointer - - SECURITY_ATTRIBUTES = object - nLength*: uint32 - lpSecurityDescriptor*: pointer - bInheritHandle*: int32 - - BINDINFO = object - cbSize: ULONG - stgmedData: STGMEDIUM - szExtraInfo: LPWSTR - grfBindInfoF: DWORD - dwBindVerb: DWORD - szCustomVerb: LPWSTR - cbstgmedData: DWORD - dwOptions: DWORD - dwOptionsFlags: DWORD - dwCodePage: DWORD - securityAttributes: SECURITY_ATTRIBUTES - iid: UUID - pUnk: pointer - dwReserved: DWORD - - IBindStatusCallback = object - vtable: ptr IBindStatusCallbackVTable - options: set[DownloadOptions] - objectRefCount: ULONG - binfoFlags: DWORD - progressCallback: DownloadProgressCallback - - PIBindStatusCallback = ptr IBindStatusCallback - LPBINDSTATUSCALLBACK = PIBindStatusCallback - - IBindStatusCallbackVTable = object - QueryInterface: proc (self: PIBindStatusCallback, - riid: ptr UUID, - pvObject: ptr pointer): HRESULT {.gcsafe,stdcall.} - AddRef: proc(self: PIBindStatusCallback): ULONG {.gcsafe, stdcall.} - Release: proc(self: PIBindStatusCallback): ULONG {.gcsafe, stdcall.} - OnStartBinding: proc(self: PIBindStatusCallback, - dwReserved: DWORD, pib: pointer): HRESULT - {.gcsafe, stdcall.} - GetPriority: proc(self: PIBindStatusCallback, pnPriority: ptr LONG): HRESULT - {.gcsafe, stdcall.} - OnLowResource: proc(self: PIBindStatusCallback, dwReserved: DWORD): HRESULT - {.gcsafe, stdcall.} - OnProgress: proc(self: PIBindStatusCallback, ulProgress: ULONG, - ulProgressMax: ULONG, ulStatusCode: ULONG, - szStatusText: LPWSTR): HRESULT - {.gcsafe, stdcall.} - OnStopBinding: proc(self: PIBindStatusCallback, hresult: HRESULT, - szError: LPWSTR): HRESULT - {.gcsafe, stdcall.} - GetBindInfo: proc(self: PIBindStatusCallback, grfBINDF: ptr DWORD, - pbindinfo: ptr BINDINFO): HRESULT - {.gcsafe, stdcall.} - OnDataAvailable: proc(self: PIBindStatusCallback, grfBSCF: DWORD, - dwSize: DWORD, pformatetc: pointer, - pstgmed: pointer): HRESULT - {.gcsafe, stdcall.} - OnObjectAvailable: proc(self: PIBindStatusCallback, riid: REFIID, - punk: pointer): HRESULT - {.gcsafe, stdcall.} - -template FAILED(hr: HRESULT): bool = - (hr < 0) - -proc URLDownloadToFile(pCaller: pointer, szUrl: LPWSTR, szFileName: LPWSTR, - dwReserved: DWORD, - lpfnCb: LPBINDSTATUSCALLBACK): HRESULT - {.stdcall, dynlib: "urlmon.dll", importc: "URLDownloadToFileW".} - -proc WideCharToMultiByte(CodePage: UINT, dwFlags: DWORD, - lpWideCharStr: ptr OLECHAR, cchWideChar: cint, - lpMultiByteStr: ptr char, cbMultiByte: cint, - lpDefaultChar: ptr char, - lpUsedDefaultChar: ptr uint32): cint - {.stdcall, dynlib: "kernel32.dll", importc: "WideCharToMultiByte".} - -proc MultiByteToWideChar(CodePage: UINT, dwFlags: DWORD, - lpMultiByteStr: ptr char, cbMultiByte: cint, - lpWideCharStr: ptr OLECHAR, cchWideChar: cint): cint - {.stdcall, dynlib: "kernel32.dll", importc: "MultiByteToWideChar".} -proc DeleteUrlCacheEntry(lpszUrlName: LPWSTR): int32 - {.stdcall, dynlib: "wininet.dll", importc: "DeleteUrlCacheEntryW".} - -proc `==`(a, b: UUID): bool = - result = false - if a[0] == b[0] and a[1] == b[1] and - a[2] == b[2] and a[3] == b[3]: - result = true - -proc `$`(bstr: LPWSTR): string = - var buffer: char - var count = WideCharToMultiByte(CP_UTF8, 0, bstr, -1, addr(buffer), 0, - nil, nil) - if count == 0: - raiseOsError(osLastError()) - else: - result = newString(count + 8) - let res = WideCharToMultiByte(CP_UTF8, 0, bstr, -1, addr(result[0]), count, - nil, nil) - if res == 0: - raiseOsError(osLastError()) - result.setLen(res - 1) - -proc toBstring(str: string): LPWSTR = - var buffer: OLECHAR - var count = MultiByteToWideChar(CP_UTF8, 0, unsafeAddr(str[0]), -1, - addr(buffer), 0) - if count == 0: - raiseOsError(osLastError()) - else: - result = cast[LPWSTR](alloc0((count + 1) * sizeof(OLECHAR))) - let res = MultiByteToWideChar(CP_UTF8, 0, unsafeAddr(str[0]), -1, - result, count) - if res == 0: - raiseOsError(osLastError()) - -proc freeBstring(bstr: LPWSTR) = - dealloc(bstr) - -proc getStatus(scode: ULONG): DownloadStatus = - case scode - of 0: result = statusError - of BINDSTATUS_PROXYDETECTING: result = statusProxyDetecting - of BINDSTATUS_REDIRECTING: result = statusRedirecting - of BINDSTATUS_COOKIE_SENT: result = statusCookieSent - of BINDSTATUS_FINDINGRESOURCE: result = statusResolving - of BINDSTATUS_CONNECTING: result = statusConnecting - of BINDSTATUS_SENDINGREQUEST: result = statusRequesting - of BINDSTATUS_MIMETYPEAVAILABLE: result = statusMimetypeAvailable - of BINDSTATUS_BEGINDOWNLOADDATA: result = statusBeginDownloading - of BINDSTATUS_DOWNLOADINGDATA: result = statusDownloading - of BINDSTATUS_ENDDOWNLOADDATA: result = statusEndDownloading - of BINDSTATUS_CACHEFILENAMEAVAILABLE: result = statusCacheAvailable - else: result = statusUnsupported - -proc addRef(self: PIBindStatusCallback): ULONG {.gcsafe, stdcall.} = - inc(self.objectRefCount) - result = self.objectRefCount - -proc release(self: PIBindStatusCallback): ULONG {.gcsafe, stdcall.} = - dec(self.objectRefCount) - result = self.objectRefCount - -proc queryInterface(self: PIBindStatusCallback, riid: ptr UUID, - pvObject: ptr pointer): HRESULT {.gcsafe,stdcall.} = - pvObject[] = nil - - if riid[] == IID_IUnknown: - pvObject[] = cast[pointer](self) - elif riid[] == IID_IBindStatusCallback: - pvObject[] = cast[pointer](self) - - if not isNil(pvObject[]): - discard addRef(self) - result = S_OK - else: - result = E_NOINTERFACE - -proc onStartBinding(self: PIBindStatusCallback, dwReserved: DWORD, - pib: pointer): HRESULT {.gcsafe, stdcall.} = - result = S_OK - -proc getPriority(self: PIBindStatusCallback, - pnPriority: ptr LONG): HRESULT {.gcsafe, stdcall.} = - result = E_NOTIMPL - -proc onLowResource(self: PIBindStatusCallback, - dwReserved: DWORD): HRESULT {.gcsafe, stdcall.} = - result = S_OK - -proc onStopBinding(self: PIBindStatusCallback, - hresult: HRESULT, szError: LPWSTR): HRESULT - {.gcsafe, stdcall.} = - result = S_OK - -proc getBindInfo(self: PIBindStatusCallback, - grfBINDF: ptr DWORD, pbindinfo: ptr BINDINFO): HRESULT - {.gcsafe, stdcall.} = - var cbSize = pbindinfo.cbSize - zeroMem(cast[pointer](pbindinfo), cbSize) - pbindinfo.cbSize = cbSize - grfBINDF[] = self.binfoFlags - result = S_OK - -proc onDataAvailable(self: PIBindStatusCallback, - grfBSCF: DWORD, dwSize: DWORD, pformatetc: pointer, - pstgmed: pointer): HRESULT {.gcsafe, stdcall.} = - result = S_OK - -proc onObjectAvailable(self: PIBindStatusCallback, - riid: REFIID, punk: pointer): HRESULT - {.gcsafe, stdcall.} = - result = S_OK - -proc onProgress(self: PIBindStatusCallback, - ulProgress: ULONG, ulProgressMax: ULONG, ulStatusCode: ULONG, - szStatusText: LPWSTR): HRESULT {.gcsafe, stdcall.} = - var message: string - if optUseProgressCallback in self.options: - if not isNil(szStatusText): - message = $szStatusText - else: - message = "" - self.progressCallback(getStatus(ulStatusCode), uint(ulProgress), - uint(ulProgressMax), message) - result = S_OK - -proc newBindStatusCallback(): IBindStatusCallback = - result = IBindStatusCallback() - result.vtable = cast[ptr IBindStatusCallbackVTable]( - alloc0(sizeof(IBindStatusCallbackVTable)) - ) - result.vtable.QueryInterface = queryInterface - result.vtable.AddRef = addRef - result.vtable.Release = release - result.vtable.OnStartBinding = onStartBinding - result.vtable.GetPriority = getPriority - result.vtable.OnLowResource = onLowResource - result.vtable.OnStopBinding = onStopBinding - result.vtable.GetBindInfo = getBindInfo - result.vtable.OnDataAvailable = onDataAvailable - result.vtable.OnObjectAvailable = onObjectAvailable - result.vtable.OnProgress = onProgress - result.objectRefCount = 1 - -proc freeBindStatusCallback(v: var IBindStatusCallback) = - dealloc(v.vtable) - -proc downloadToFile*(szUrl: string, szFileName: string, - options: set[DownloadOptions] = {}, - progresscb: DownloadProgressCallback = nil) = - ## Downloads from URL specified in ``szUrl`` to local filesystem path - ## specified in ``szFileName``. - ## - ## szUrl - ## URL to download, international names are supported. - ## szFileName - ## Destination path for downloading resource. - ## options - ## Downloading options. Currently only 2 options supported. - ## progresscb - ## Callback procedure, which will be called throughout the download - ## process, indicating status and progress. - ## - ## Available downloading options: - ## - ## optUseCache - ## Try to use Windows cache when downloading. - ## optIgnoreSecurity - ## Ignore HTTPS security problems, e.g. self-signed HTTPS certificate. - ## - var bszUrl = szUrl.toBstring() - var bszFile = szFileName.toBstring() - var bstatus = newBindStatusCallback() - - bstatus.options = {} - - if optUseCache notin options: - bstatus.options.incl(optUseCache) - let res = DeleteUrlCacheEntry(bszUrl) - if res == 0: - let err = osLastError() - if err.int notin {ERROR_ACCESS_DENIED, ERROR_FILE_NOT_FOUND}: - freeBindStatusCallback(bstatus) - freeBstring(bszUrl) - freeBstring(bszFile) - raiseOsError(err) - - bstatus.binfoFlags = BINDF_GETNEWESTVERSION or BINDF_RESYNCHRONIZE or - BINDF_PRAGMA_NO_CACHE or BINDF_NO_UI or - BINDF_SILENTOPERATION - - if optIgnoreSecurity in options: - bstatus.binfoFlags = bstatus.binfoFlags or BINDF_IGNORESECURITYPROBLEM - - if not isNil(progresscb): - bstatus.options.incl(optUseProgressCallback) - bstatus.progressCallback = progresscb - - let res = URLDownloadToFile(nil, bszUrl, bszFile, 0, addr bstatus) - if FAILED(res): - freeBindStatusCallback(bstatus) - freeBstring(bszUrl) - freeBstring(bszFile) - raiseOsError(OSErrorCode(res)) - - freeBindStatusCallback(bstatus) - freeBstring(bszUrl) - freeBstring(bszFile) - -when isMainModule: - proc progress(status: DownloadStatus, progress: uint, progressMax: uint, - message: string) {.gcsafe.} = - const downset: set[DownloadStatus] = {statusBeginDownloading, - statusDownloading, statusEndDownloading} - if status in downset: - var message = "Downloaded " & $progress & " of " & $progressMax & "\c" - stdout.write(message) - else: - echo "Status [" & $status & "] message = [" & $message & "]" - - downloadToFile("https://nim-lang.org/download/mingw64.7z", - "test.zip", {optUseCache}, progress) From 63cca93ea9bcab3c1fea39ff3789fd3a656d2db2 Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Thu, 19 May 2022 17:11:53 +0200 Subject: [PATCH 2920/3768] testament: include extra options in test name (#19801) there's currently no (simple) way to disambiguate which option failed --- testament/categories.nim | 12 +++--- testament/testament.nim | 79 +++++++++++++++++++--------------------- 2 files changed, 44 insertions(+), 47 deletions(-) diff --git a/testament/categories.nim b/testament/categories.nim index 46cc3b210..a092fec84 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -437,7 +437,7 @@ proc testNimblePackages(r: var TResults; cat: Category; packageFilter: string) = if pkg.allowFailure: inc r.passed inc r.failedButAllowed - addResult(r, test, targetC, "", cmd & "\n" & outp, reFailed, allowFailure = pkg.allowFailure) + addResult(r, test, targetC, "", "", cmd & "\n" & outp, reFailed, allowFailure = pkg.allowFailure) continue outp @@ -450,21 +450,21 @@ proc testNimblePackages(r: var TResults; cat: Category; packageFilter: string) = discard tryCommand("nimble install --depsOnly -y", maxRetries = 3) discard tryCommand(pkg.cmd, reFailed = reBuildFailed) inc r.passed - r.addResult(test, targetC, "", "", reSuccess, allowFailure = pkg.allowFailure) + r.addResult(test, targetC, "", "", "", reSuccess, allowFailure = pkg.allowFailure) errors = r.total - r.passed if errors == 0: - r.addResult(packageFileTest, targetC, "", "", reSuccess) + r.addResult(packageFileTest, targetC, "", "", "", reSuccess) else: - r.addResult(packageFileTest, targetC, "", "", reBuildFailed) + r.addResult(packageFileTest, targetC, "", "", "", reBuildFailed) except JsonParsingError: errors = 1 - r.addResult(packageFileTest, targetC, "", "Invalid package file", reBuildFailed) + r.addResult(packageFileTest, targetC, "", "", "Invalid package file", reBuildFailed) raise except ValueError: errors = 1 - r.addResult(packageFileTest, targetC, "", "Unknown package", reBuildFailed) + r.addResult(packageFileTest, targetC, "", "", "Unknown package", reBuildFailed) raise # bug #18805 finally: if errors == 0: removeDir(packagesDir) diff --git a/testament/testament.nim b/testament/testament.nim index 4b5a8a147..a4329b982 100644 --- a/testament/testament.nim +++ b/testament/testament.nim @@ -259,7 +259,8 @@ Tests skipped: $4 / $1
      """ % [$x.total, $x.passed, $x.failedButAllowed, $x.skipped] proc addResult(r: var TResults, test: TTest, target: TTarget, - expected, given: string, successOrig: TResultEnum, allowFailure = false, givenSpec: ptr TSpec = nil) = + extraOptions, expected, given: string, successOrig: TResultEnum, + allowFailure = false, givenSpec: ptr TSpec = nil) = # instead of `ptr TSpec` we could also use `Option[TSpec]`; passing `givenSpec` makes it easier to get what we need # instead of having to pass individual fields, or abusing existing ones like expected vs given. # test.name is easier to find than test.name.extractFilename @@ -269,6 +270,7 @@ proc addResult(r: var TResults, test: TTest, target: TTarget, if allowFailure: name.add " (allowed to fail) " if test.options.len > 0: name.add ' ' & test.options + if extraOptions.len > 0: name.add ' ' & extraOptions let duration = epochTime() - test.startTime let success = if test.spec.timeout > 0.0 and duration > test.spec.timeout: reTimeout @@ -333,7 +335,8 @@ proc addResult(r: var TResults, test: TTest, target: TTarget, discard waitForExit(p) close(p) -proc checkForInlineErrors(r: var TResults, expected, given: TSpec, test: TTest, target: TTarget) = +proc checkForInlineErrors(r: var TResults, expected, given: TSpec, test: TTest, + target: TTarget, extraOptions: string) = let pegLine = peg"{[^(]*} '(' {\d+} ', ' {\d+} ') ' {[^:]*} ':' \s* {.*}" var covered = initIntSet() for line in splitLines(given.nimout): @@ -367,10 +370,10 @@ proc checkForInlineErrors(r: var TResults, expected, given: TSpec, test: TTest, e.add ": " e.add expected.inlineErrors[j].msg - r.addResult(test, target, e, given.nimout, reMsgsDiffer) + r.addResult(test, target, extraOptions, e, given.nimout, reMsgsDiffer) break coverCheck - r.addResult(test, target, "", given.msg, reSuccess) + r.addResult(test, target, extraOptions, "", given.msg, reSuccess) inc(r.passed) proc nimoutCheck(expected, given: TSpec): bool = @@ -381,22 +384,23 @@ proc nimoutCheck(expected, given: TSpec): bool = elif expected.nimout.len > 0 and not greedyOrderedSubsetLines(expected.nimout, given.nimout): result = false -proc cmpMsgs(r: var TResults, expected, given: TSpec, test: TTest, target: TTarget) = +proc cmpMsgs(r: var TResults, expected, given: TSpec, test: TTest, + target: TTarget, extraOptions: string) = if expected.inlineErrors.len > 0: - checkForInlineErrors(r, expected, given, test, target) + checkForInlineErrors(r, expected, given, test, target, extraOptions) elif strip(expected.msg) notin strip(given.msg): - r.addResult(test, target, expected.msg, given.msg, reMsgsDiffer) + r.addResult(test, target, extraOptions, expected.msg, given.msg, reMsgsDiffer) elif not nimoutCheck(expected, given): - r.addResult(test, target, expected.nimout, given.nimout, reMsgsDiffer) + r.addResult(test, target, extraOptions, expected.nimout, given.nimout, reMsgsDiffer) elif extractFilename(expected.file) != extractFilename(given.file) and "internal error:" notin expected.msg: - r.addResult(test, target, expected.file, given.file, reFilesDiffer) + r.addResult(test, target, extraOptions, expected.file, given.file, reFilesDiffer) elif expected.line != given.line and expected.line != 0 or expected.column != given.column and expected.column != 0: - r.addResult(test, target, $expected.line & ':' & $expected.column, + r.addResult(test, target, extraOptions, $expected.line & ':' & $expected.column, $given.line & ':' & $given.column, reLinesDiffer) else: - r.addResult(test, target, expected.msg, given.msg, reSuccess) + r.addResult(test, target, extraOptions, expected.msg, given.msg, reSuccess) inc(r.passed) proc generatedFile(test: TTest, target: TTarget): string = @@ -434,8 +438,8 @@ proc codegenCheck(test: TTest, target: TTarget, spec: TSpec, expectedMsg: var st given.err = reCodeNotFound echo getCurrentExceptionMsg() -proc compilerOutputTests(test: TTest, target: TTarget, given: var TSpec, - expected: TSpec; r: var TResults) = +proc compilerOutputTests(test: TTest, target: TTarget, extraOptions: string, + given: var TSpec, expected: TSpec; r: var TResults) = var expectedmsg: string = "" var givenmsg: string = "" if given.err == reSuccess: @@ -449,7 +453,7 @@ proc compilerOutputTests(test: TTest, target: TTarget, given: var TSpec, else: givenmsg = "$ " & given.cmd & '\n' & given.nimout if given.err == reSuccess: inc(r.passed) - r.addResult(test, target, expectedmsg, givenmsg, given.err) + r.addResult(test, target, extraOptions, expectedmsg, givenmsg, given.err) proc getTestSpecTarget(): TTarget = if getEnv("NIM_COMPILE_TO_CPP", "false") == "true": @@ -457,16 +461,6 @@ proc getTestSpecTarget(): TTarget = else: result = targetC -proc checkDisabled(r: var TResults, test: TTest): bool = - if test.spec.err in {reDisabled, reJoined}: - # targetC is a lie, but parameter is required - r.addResult(test, targetC, "", "", test.spec.err) - inc(r.skipped) - inc(r.total) - result = false - else: - result = true - var count = 0 proc equalModuloLastNewline(a, b: string): bool = @@ -474,8 +468,13 @@ proc equalModuloLastNewline(a, b: string): bool = result = a == b or b.endsWith("\n") and a == b[0 ..< ^1] proc testSpecHelper(r: var TResults, test: var TTest, expected: TSpec, - target: TTarget, nimcache: string, extraOptions = "") = + target: TTarget, extraOptions: string, nimcache: string) = test.startTime = epochTime() + if test.spec.err in {reDisabled, reJoined}: + r.addResult(test, target, extraOptions, "", "", test.spec.err) + inc(r.skipped) + return + template callNimCompilerImpl(): untyped = # xxx this used to also pass: `--stdout --hint:Path:off`, but was done inconsistently # with other branches @@ -483,21 +482,21 @@ proc testSpecHelper(r: var TResults, test: var TTest, expected: TSpec, case expected.action of actionCompile: var given = callNimCompilerImpl() - compilerOutputTests(test, target, given, expected, r) + compilerOutputTests(test, target, extraOptions, given, expected, r) of actionRun: var given = callNimCompilerImpl() if given.err != reSuccess: - r.addResult(test, target, "", "$ " & given.cmd & '\n' & given.nimout, given.err, givenSpec = given.addr) + r.addResult(test, target, extraOptions, "", "$ " & given.cmd & '\n' & given.nimout, given.err, givenSpec = given.addr) else: let isJsTarget = target == targetJS var exeFile = changeFileExt(test.name, if isJsTarget: "js" else: ExeExt) if not fileExists(exeFile): - r.addResult(test, target, expected.output, + r.addResult(test, target, extraOptions, expected.output, "executable not found: " & exeFile, reExeNotFound) else: let nodejs = if isJsTarget: findNodeJs() else: "" if isJsTarget and nodejs == "": - r.addResult(test, target, expected.output, "nodejs binary not in PATH", + r.addResult(test, target, extraOptions, expected.output, "nodejs binary not in PATH", reExeNotFound) else: var exeCmd: string @@ -528,24 +527,24 @@ proc testSpecHelper(r: var TResults, test: var TTest, expected: TSpec, else: buf if exitCode != expected.exitCode: - r.addResult(test, target, "exitcode: " & $expected.exitCode, + r.addResult(test, target, extraOptions, "exitcode: " & $expected.exitCode, "exitcode: " & $exitCode & "\n\nOutput:\n" & bufB, reExitcodesDiffer) elif (expected.outputCheck == ocEqual and not expected.output.equalModuloLastNewline(bufB)) or (expected.outputCheck == ocSubstr and expected.output notin bufB): given.err = reOutputsDiffer - r.addResult(test, target, expected.output, bufB, reOutputsDiffer) + r.addResult(test, target, extraOptions, expected.output, bufB, reOutputsDiffer) else: - compilerOutputTests(test, target, given, expected, r) + compilerOutputTests(test, target, extraOptions, given, expected, r) of actionReject: let given = callNimCompilerImpl() - cmpMsgs(r, expected, given, test, target) + cmpMsgs(r, expected, given, test, target, extraOptions) -proc targetHelper(r: var TResults, test: TTest, expected: TSpec, extraOptions = "") = +proc targetHelper(r: var TResults, test: TTest, expected: TSpec, extraOptions: string) = for target in expected.targets: inc(r.total) if target notin gTargets: - r.addResult(test, target, "", "", reDisabled) + r.addResult(test, target, extraOptions, "", "", reDisabled) inc(r.skipped) elif simulate: inc count @@ -553,16 +552,15 @@ proc targetHelper(r: var TResults, test: TTest, expected: TSpec, extraOptions = else: let nimcache = nimcacheDir(test.name, test.options, target) var testClone = test - testSpecHelper(r, testClone, expected, target, nimcache, extraOptions) + testSpecHelper(r, testClone, expected, target, extraOptions, nimcache) proc testSpec(r: var TResults, test: TTest, targets: set[TTarget] = {}) = var expected = test.spec if expected.parseErrors.len > 0: # targetC is a lie, but a parameter is required - r.addResult(test, targetC, "", expected.parseErrors, reInvalidSpec) + r.addResult(test, targetC, "", "", expected.parseErrors, reInvalidSpec) inc(r.total) return - if not checkDisabled(r, test): return expected.targets.incl targets # still no target specified at all @@ -572,14 +570,13 @@ proc testSpec(r: var TResults, test: TTest, targets: set[TTarget] = {}) = for m in test.spec.matrix: targetHelper(r, test, expected, m) else: - targetHelper(r, test, expected) + targetHelper(r, test, expected, "") proc testSpecWithNimcache(r: var TResults, test: TTest; nimcache: string) {.used.} = - if not checkDisabled(r, test): return for target in test.spec.targets: inc(r.total) var testClone = test - testSpecHelper(r, testClone, test.spec, target, nimcache) + testSpecHelper(r, testClone, test.spec, target, "", nimcache) proc makeTest(test, options: string, cat: Category): TTest = result.cat = cat From a8426fc7890f1bea1c507ddb2fe42662f7a574f8 Mon Sep 17 00:00:00 2001 From: quantimnot <54247259+quantimnot@users.noreply.github.com> Date: Mon, 23 May 2022 00:12:34 -0400 Subject: [PATCH 2921/3768] Fix default testament target in docs and cli help (#19796) Co-authored-by: quantimnot --- doc/testament.rst | 4 ++-- testament/testament.nim | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/testament.rst b/doc/testament.rst index 5590cc6d7..427a7ff71 100644 --- a/doc/testament.rst +++ b/doc/testament.rst @@ -36,7 +36,7 @@ Options (for debugging) --failing Only show failing/ignored tests --targets:"c cpp js objc" - Run tests for specified targets (default: all) + Run tests for specified targets (default: c) --nim:path Use a particular nim executable (default: $PATH/nim) --directory:dir Change to directory dir before reading the tests or doing anything else. @@ -164,7 +164,7 @@ Example "template" **to edit** and write a Testament unittest: # Timeout seconds to run the test. Fractional values are supported. timeout: 1.5 - # Targets to run the test into (c, cpp, objc, js). + # Targets to run the test into (c, cpp, objc, js). Defaults to c. targets: "c js" # flags with which to run the test, delimited by `;` diff --git a/testament/testament.nim b/testament/testament.nim index a4329b982..d2b0e7fb4 100644 --- a/testament/testament.nim +++ b/testament/testament.nim @@ -54,7 +54,7 @@ Options: --verbose print commands (compiling and running tests) --simulate see what tests would be run but don't run them (for debugging) --failing only show failing/ignored tests - --targets:"c cpp js objc" run tests for specified targets (default: all) + --targets:"c cpp js objc" run tests for specified targets (default: c) --nim:path use a particular nim executable (default: $$PATH/nim) --directory:dir Change to directory dir before reading the tests or doing anything else. --colors:on|off Turn messages coloring on|off. From 05c0419658e3f97c29a12a16e99c0f8b842622c8 Mon Sep 17 00:00:00 2001 From: quantimnot <54247259+quantimnot@users.noreply.github.com> Date: Mon, 23 May 2022 00:17:32 -0400 Subject: [PATCH 2922/3768] Fix global destructor injection for JS backend (#19797) * Fix global destructor injection for JS backend * Moved global destructors injection before the final call to transform and generate JS code. It had previously been after and thus not no JS was generated for them. * Added some internal documentation of `jsgen`. * Enable a current destructor test to cover the JS backend as well. * Fixes the JS aspect of #17237. * Fixed global destructor injection order for JS backend Co-authored-by: quantimnot --- compiler/jsgen.nim | 30 +++++++++++++++++++++++++----- tests/destructor/t5342.nim | 3 ++- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index b9a73daf9..6d34fcae2 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -2392,6 +2392,7 @@ proc optionalLine(p: Rope): Rope = return p & "\L" proc genProc(oldProc: PProc, prc: PSym): Rope = + ## Generate a JS procedure ('function'). var resultSym: PSym a: TCompRes @@ -2673,6 +2674,7 @@ proc gen(p: PProc, n: PNode, r: var TCompRes) = else: internalError(p.config, n.info, "gen: unknown node type: " & $n.kind) proc newModule(g: ModuleGraph; module: PSym): BModule = + ## Create a new JS backend module node. new(result) result.module = module result.sigConflicts = initCountTable[SigHash]() @@ -2684,6 +2686,7 @@ proc newModule(g: ModuleGraph; module: PSym): BModule = PGlobals(g.backend).inSystem = true proc genHeader(): Rope = + ## Generate the JS header. result = rope("""/* Generated by the Nim Compiler v$1 */ var framePtr = null; var excHandler = 0; @@ -2714,6 +2717,8 @@ proc addHcrInitGuards(p: PProc, n: PNode, genStmt(p, n) proc genModule(p: PProc, n: PNode) = + ## Generate the JS module code. + ## Called for each top level node in a Nim module. if optStackTrace in p.options: p.body.add(frameCreate(p, makeJSString("module " & p.module.module.name.s), @@ -2742,6 +2747,7 @@ proc genModule(p: PProc, n: PNode) = p.body.add(frameDestroy(p)) proc myProcess(b: PPassContext, n: PNode): PNode = + ## Generate JS code for a node. result = n let m = BModule(b) if passes.skipCodegen(m.config, n): return n @@ -2754,6 +2760,7 @@ proc myProcess(b: PPassContext, n: PNode): PNode = p.g.code.add(p.body) proc wholeCode(graph: ModuleGraph; m: BModule): Rope = + ## Combine source code from all nodes. let globals = PGlobals(graph.backend) for prc in globals.forwarded: if not globals.generatedSyms.containsOrIncl(prc.id): @@ -2779,28 +2786,41 @@ proc getClassName(t: PType): Rope = else: result = rope(s.name.s) proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = - result = myProcess(b, n) + ## Finalize JS code generation of a Nim module. + ## Param `n` may contain nodes returned from the last module close call. var m = BModule(b) if sfMainModule in m.module.flags: - for destructorCall in graph.globalDestructors: - n.add destructorCall + # Add global destructors to the module. + # This must come before the last call to `myProcess`. + for i in countdown(high(graph.globalDestructors), 0): + n.add graph.globalDestructors[i] + # Process any nodes left over from the last call to `myClose`. + result = myProcess(b, n) + # Some codegen is different (such as no stacktraces; see `initProcOptions`) + # when `std/system` is being processed. if sfSystemModule in m.module.flags: PGlobals(graph.backend).inSystem = false + # Check if codegen should continue before any files are generated. + # It may bail early is if too many errors have been raised. if passes.skipCodegen(m.config, n): return n + # Nim modules are compiled into a single JS file. + # If this is the main module, then this is the final call to `myClose`. if sfMainModule in m.module.flags: var code = genHeader() & wholeCode(graph, m) let outFile = m.config.prepareToWriteOutput() - + # Generate an optional source map. if optSourcemap in m.config.globalOptions: var map: SourceMap (code, map) = genSourceMap($(code), outFile.string) writeFile(outFile.string & ".map", $(%map)) + # Check if the generated JS code matches the output file, or else + # write it to the file. if not equalsFile(code, outFile): if not writeRope(code, outFile): rawMessage(m.config, errCannotOpenFile, outFile.string) - proc myOpen(graph: ModuleGraph; s: PSym; idgen: IdGenerator): PPassContext = + ## Create the JS backend pass context `BModule` for a Nim module. result = newModule(graph, s) result.idgen = idgen diff --git a/tests/destructor/t5342.nim b/tests/destructor/t5342.nim index 19354ea64..0acd5ef9d 100644 --- a/tests/destructor/t5342.nim +++ b/tests/destructor/t5342.nim @@ -1,5 +1,6 @@ discard """ - matrix: "--gc:refc; --gc:arc" + matrix: "--mm:refc; --mm:arc" + targets: "c js" output: ''' 1 2 From b1b5e3ab10749851e942d624ee85188d8b06822e Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Tue, 24 May 2022 12:37:19 -0300 Subject: [PATCH 2923/3768] Add Array.shift (#19811) * Add Array.shift for JavaScript targets * Add Array.shift for JavaScript targets --- changelog.md | 2 +- lib/js/jscore.nim | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 3b2685547..e120dcfb8 100644 --- a/changelog.md +++ b/changelog.md @@ -37,7 +37,7 @@ becomes an alias for `addr`. - Added `initDateTime` in `times` to create a datetime from a weekday, and ISO 8601 week number and week-based year. - Added `getIsoWeekAndYear` in `times` to get an ISO week number along with the corresponding ISO week-based year from a datetime. - Added `getIsoWeeksInYear` in `times` to return the number of weeks in an ISO week-based year. - +- Added [`Array.shift`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift) for JavaScript targets. - Added `std/oserrors` for OS error reporting. Added `std/envvars` for environment variables handling. - Removed deprecated `oids.oidToString`. - Remove define `nimExperimentalAsyncjsThen` for `std/asyncjs.then` and `std/jsfetch`. diff --git a/lib/js/jscore.nim b/lib/js/jscore.nim index 61c188431..4518f32ce 100644 --- a/lib/js/jscore.nim +++ b/lib/js/jscore.nim @@ -123,3 +123,12 @@ since (1, 5): assert [1, 2, 3, 4, 5].copyWithin(0, 3) == @[4, 5, 3, 4, 5] assert [1, 2, 3, 4, 5].copyWithin(0, 3, 4) == @[4, 2, 3, 4, 5] assert [1, 2, 3, 4, 5].copyWithin(-2, -3, -1) == @[1, 2, 3, 3, 4] + + +since (1, 7): + func shift*[T](self: seq[T]): T {.importjs: "#.$1()".} = + ## https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift + runnableExamples: + var arrai = @[1, 2, 3] + assert arrai.shift() == 1 + assert arrai == @[2, 3] From ec0cec3170a3f175b9e02ca98e82c963fe64df4f Mon Sep 17 00:00:00 2001 From: tandy1000 Date: Tue, 24 May 2022 16:37:39 +0100 Subject: [PATCH 2924/3768] Add `document.hidden` and `document.visibilityState` properties (#19817) --- lib/js/dom.nim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/js/dom.nim b/lib/js/dom.nim index c1a677258..1a62780a7 100644 --- a/lib/js/dom.nim +++ b/lib/js/dom.nim @@ -216,11 +216,13 @@ type defaultCharset*: cstring fgColor*: cstring head*: Element + hidden*: bool lastModified*: cstring linkColor*: cstring referrer*: cstring title*: cstring URL*: cstring + visibilityState*: cstring vlinkColor*: cstring anchors*: seq[AnchorElement] forms*: seq[FormElement] From d81edcacc6740a99b0360cf31ddc2f2a1626006c Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Tue, 24 May 2022 23:53:01 +0800 Subject: [PATCH 2925/3768] Add link to std/tempfiles in the docs; fix #19155 (#19807) --- doc/lib.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/lib.rst b/doc/lib.rst index f72012777..2f3a315a8 100644 --- a/doc/lib.rst +++ b/doc/lib.rst @@ -251,6 +251,10 @@ Generic Operating System Services This module contains a few procedures to control the *terminal* (also called *console*). The implementation simply uses ANSI escape sequences and does not depend on any other module. + +* `tempfiles `_ + This module provides some utils to generate temporary path names and + create temporary files and directories. Math libraries From 14960fa754f42c820727bed9308e075b2cb923f6 Mon Sep 17 00:00:00 2001 From: Michael New Date: Sun, 29 May 2022 19:40:15 -0700 Subject: [PATCH 2926/3768] Fix typo and incorrect pragma name (#19847) --- doc/manual_experimental_strictnotnil.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/manual_experimental_strictnotnil.rst b/doc/manual_experimental_strictnotnil.rst index b14e5f9f3..ebfca7e51 100644 --- a/doc/manual_experimental_strictnotnil.rst +++ b/doc/manual_experimental_strictnotnil.rst @@ -172,7 +172,7 @@ We might need to check for `strictFuncs` pure funcs and not do that then. For field expressions `a.field`, we calculate an integer value based on a hash of the tree and just accept equivalent trees as equivalent expressions. For item expression `a[index]`, we also calculate an integer value based on a hash of the tree and accept equivalent trees as equivalent expressions: for static values only. -For now we support only constant indices: we dont track expression with no-const indices. For those we just report a warning even if they are safe for now: one can use a local variable to workaround. For loops this might be annoying: so one should be able to turn off locally the warning using the `{.warning[StrictCheckNotNil]:off}.`. +For now we support only constant indices: we dont track expression with no-const indices. For those we just report a warning even if they are safe for now: one can use a local variable to workaround. For loops this might be annoying: so one should be able to turn off locally the warning using the `{.warning[StrictNotNil]:off.}`. For bracket expressions, in the future we might count `a[]` as the same general expression. This means we should should the index but otherwise handle it the same for assign (maybe "aliasing" all the non-static elements) and differentiate only for static: e.g. `a[0]` and `a[1]`. From 004fc23a3fa79fc32e0fb5d4f438ddc22964b0da Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 30 May 2022 16:41:24 +0800 Subject: [PATCH 2927/3768] [vm] remove unused opcSubstr opcode (#19834) remove unused opcSubstr --- compiler/vm.nim | 8 -------- compiler/vmdef.nim | 4 ++-- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/compiler/vm.nim b/compiler/vm.nim index fa1c71c85..bbcff77ba 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1185,14 +1185,6 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcContainsSet: decodeBC(rkInt) regs[ra].intVal = ord(inSet(regs[rb].node, regs[rc].regToNode)) - of opcSubStr: - decodeBC(rkNode) - inc pc - assert c.code[pc].opcode == opcSubStr - let rd = c.code[pc].regA - createStr regs[ra] - regs[ra].node.strVal = substr(regs[rb].node.strVal, - regs[rc].intVal.int, regs[rd].intVal.int) of opcParseFloat: decodeBC(rkInt) inc pc diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index ecdbeff89..c653501ca 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -102,7 +102,7 @@ type opcMulSet, opcPlusSet, opcMinusSet, opcConcatStr, opcContainsSet, opcRepr, opcSetLenStr, opcSetLenSeq, opcIsNil, opcOf, opcIs, - opcSubStr, opcParseFloat, opcConv, opcCast, + opcParseFloat, opcConv, opcCast, opcQuit, opcInvalidField, opcNarrowS, opcNarrowU, opcSignExtend, @@ -306,7 +306,7 @@ proc registerCallback*(c: PCtx; name: string; callback: VmCallback): int {.disca const firstABxInstr* = opcTJmp largeInstrs* = { # instructions which use 2 int32s instead of 1: - opcSubStr, opcConv, opcCast, opcNewSeq, opcOf + opcConv, opcCast, opcNewSeq, opcOf } slotSomeTemp* = slotTempUnknown relativeJumps* = {opcTJmp, opcFJmp, opcJmp, opcJmpBack} From 497af2c0d9f311e050ba57842a6326ca70489d55 Mon Sep 17 00:00:00 2001 From: quantimnot <54247259+quantimnot@users.noreply.github.com> Date: Mon, 30 May 2022 05:07:47 -0400 Subject: [PATCH 2928/3768] Doc preferred import style in compiler (#19832) Doc prefered import style in compiler Co-authored-by: quantimnot --- doc/intern.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/intern.rst b/doc/intern.rst index a8846ae02..4ca7eff20 100644 --- a/doc/intern.rst +++ b/doc/intern.rst @@ -186,6 +186,7 @@ Coding Guidelines * Use a space after a colon, but not before it. * [deprecated] Start types with a capital `T`, unless they are pointers/references which start with `P`. +* Prefer `import package`:nim: over `from package import symbol`:nim:. See also the `API naming design `_ document. From 15f0b4867679120580b2f14bbb7a8b302505b34d Mon Sep 17 00:00:00 2001 From: Alfred Morgan Date: Mon, 30 May 2022 03:09:18 -0700 Subject: [PATCH 2929/3768] Zectbumo fixes 19824 (#19825) * borrowed `$` to make Time string friendly * added sep character parameter * Revert "added sep character parameter" This reverts commit 45f4b019a4883b6ba577ade1f94677266beb5960. * added sep character parameter * Revert "borrowed `$` to make Time string friendly" This reverts commit 10e2e44c9a04970f38cf66556635bdbb50b69136. * added uri tests and made changelong entry * Update lib/pure/uri.nim Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> * Update lib/pure/uri.nim Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> * Update tests/stdlib/turi.nim Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> * Update tests/stdlib/turi.nim Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> --- changelog.md | 1 + lib/pure/uri.nim | 14 ++++++++------ tests/stdlib/turi.nim | 4 ++++ 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/changelog.md b/changelog.md index e120dcfb8..9548f3034 100644 --- a/changelog.md +++ b/changelog.md @@ -39,6 +39,7 @@ becomes an alias for `addr`. - Added `getIsoWeeksInYear` in `times` to return the number of weeks in an ISO week-based year. - Added [`Array.shift`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift) for JavaScript targets. - Added `std/oserrors` for OS error reporting. Added `std/envvars` for environment variables handling. +- Added `sep` parameter in `std/uri` to specify the query separator. - Removed deprecated `oids.oidToString`. - Remove define `nimExperimentalAsyncjsThen` for `std/asyncjs.then` and `std/jsfetch`. diff --git a/lib/pure/uri.nim b/lib/pure/uri.nim index 7583dbd1e..1dbb018c2 100644 --- a/lib/pure/uri.nim +++ b/lib/pure/uri.nim @@ -123,13 +123,13 @@ func decodeUrl*(s: string, decodePlus = true): string = setLen(result, j) func encodeQuery*(query: openArray[(string, string)], usePlus = true, - omitEq = true): string = + omitEq = true, sep = '&'): string = ## Encodes a set of (key, value) parameters into a URL query string. ## ## Every (key, value) pair is URL-encoded and written as `key=value`. If the ## value is an empty string then the `=` is omitted, unless `omitEq` is ## false. - ## The pairs are joined together by a `&` character. + ## The pairs are joined together by the `sep` character. ## ## The `usePlus` parameter is passed down to the `encodeUrl` function that ## is used for the URL encoding of the string values. @@ -140,9 +140,10 @@ func encodeQuery*(query: openArray[(string, string)], usePlus = true, assert encodeQuery({: }) == "" assert encodeQuery({"a": "1", "b": "2"}) == "a=1&b=2" assert encodeQuery({"a": "1", "b": ""}) == "a=1&b" + assert encodeQuery({"a": "1", "b": ""}, omitEq = false, sep = ';') == "a=1;b=" for elem in query: - # Encode the `key = value` pairs and separate them with a '&' - if result.len > 0: result.add('&') + # Encode the `key = value` pairs and separate them with 'sep' + if result.len > 0: result.add(sep) let (key, val) = elem result.add(encodeUrl(key, usePlus)) # Omit the '=' if the value string is empty @@ -150,7 +151,7 @@ func encodeQuery*(query: openArray[(string, string)], usePlus = true, result.add('=') result.add(encodeUrl(val, usePlus)) -iterator decodeQuery*(data: string): tuple[key, value: string] = +iterator decodeQuery*(data: string, sep = '&'): tuple[key, value: string] = ## Reads and decodes the query string `data` and yields the `(key, value)` pairs ## the data consists of. If compiled with `-d:nimLegacyParseQueryStrict`, ## a `UriParseError` is raised when there is an unencoded `=` character in a decoded @@ -158,6 +159,7 @@ iterator decodeQuery*(data: string): tuple[key, value: string] = runnableExamples: import std/sequtils assert toSeq(decodeQuery("foo=1&bar=2=3")) == @[("foo", "1"), ("bar", "2=3")] + assert toSeq(decodeQuery("foo=1;bar=2=3", ';')) == @[("foo", "1"), ("bar", "2=3")] assert toSeq(decodeQuery("&a&=b&=&&")) == @[("", ""), ("a", ""), ("", "b"), ("", ""), ("", "")] proc parseData(data: string, i: int, field: var string, sep: char): int = @@ -186,7 +188,7 @@ iterator decodeQuery*(data: string): tuple[key, value: string] = when defined(nimLegacyParseQueryStrict): i = parseData(data, i, value, '=') else: - i = parseData(data, i, value, '&') + i = parseData(data, i, value, sep) yield (name, value) if i < data.len: when defined(nimLegacyParseQueryStrict): diff --git a/tests/stdlib/turi.nim b/tests/stdlib/turi.nim index a3b6afe2c..79ddd773b 100644 --- a/tests/stdlib/turi.nim +++ b/tests/stdlib/turi.nim @@ -274,7 +274,9 @@ template main() = doAssert encodeQuery({"foo": ""}) == "foo" doAssert encodeQuery({"foo": ""}, omitEq = false) == "foo=" doAssert encodeQuery({"a": "1", "b": "", "c": "3"}) == "a=1&b&c=3" + doAssert encodeQuery({"a": "1", "b": "", "c": "3"}, sep = ';') == "a=1;b;c=3" doAssert encodeQuery({"a": "1", "b": "", "c": "3"}, omitEq = false) == "a=1&b=&c=3" + doAssert encodeQuery({"a": "1", "b": "", "c": "3"}, omitEq = false, sep = ';') == "a=1;b=;c=3" block: # `?` block: @@ -300,7 +302,9 @@ template main() = block: # decodeQuery doAssert toSeq(decodeQuery("a=1&b=0")) == @[("a", "1"), ("b", "0")] + doAssert toSeq(decodeQuery("a=1;b=0", sep = ';')) == @[("a", "1"), ("b", "0")] doAssert toSeq(decodeQuery("a=1&b=2c=6")) == @[("a", "1"), ("b", "2c=6")] + doAssert toSeq(decodeQuery("a=1;b=2c=6", sep = ';')) == @[("a", "1"), ("b", "2c=6")] block: # bug #17481 let u1 = parseUri("./") From d30c6419a051a815e3fdb354ac79522f17e55bda Mon Sep 17 00:00:00 2001 From: quantimnot <54247259+quantimnot@users.noreply.github.com> Date: Mon, 30 May 2022 12:52:19 -0400 Subject: [PATCH 2930/3768] Refactor and doc package handling, module name mangling (#19821) * Refactor and doc package handling, module name mangling * Consolidate, de-duplicate and extend package handling * Alter how duplicate module names of a package are handled * Alter how module names are mangled * Fix crash when another package is named 'stdlib' (test case added) * Doc what defines a package in the manual Modules with duplicate names within a package used to be given 'fake' packages to resolve conflicts. That prevented the ability to discern if a module belonged to the current project package or a foreign package. They now have the proper package owner and the names are mangled in a consistent manner to prevent codegen clashes. All module names are now mangled the same. Stdlib was treated special before, but now it is same as any other package. This fixes a crash when a foreign package is named 'stdlib'. Module mangling is altered for both file paths and symbols used by the backends. Removed an unused module name to package mapping that may have been intended for IC. The mapping was removed because it wasn't being used and was complicating the issue of package modules with duplicate names not having the proper package owner assigned. * Fix some tests * Refactor `packagehandling` * Remove `packagehandling.withPackageName` and its uses * Move module path mangling from `packagehandling` to `modulepaths` * Move `options.toRodFile` to `ic` to break import cycle * Changed import style to match preferred style Co-authored-by: quantimnot --- compiler/ast.nim | 21 ---- compiler/cgen.nim | 27 ++--- compiler/docgen.nim | 13 +-- compiler/docgen2.nim | 4 +- compiler/extccomp.nim | 11 ++- compiler/ic/cbackend.nim | 5 +- compiler/ic/ic.nim | 19 ++-- compiler/modulegraphs.nim | 18 +++- compiler/modulepaths.nim | 109 +++------------------ compiler/modules.nim | 51 +--------- compiler/options.nim | 7 +- compiler/packagehandling.nim | 23 +---- compiler/packages.nim | 49 +++++++++ compiler/passes.nim | 12 +-- compiler/pragmas.nim | 3 +- doc/manual.rst | 13 +++ tests/ccgbugs/tforward_decl_only.nim | 2 +- tests/modules/a/module_name_clashes.nim | 8 ++ tests/modules/b/module_name_clashes.nim | 3 + tests/modules/tmodule_name_clashes.nim | 16 +++ tests/package/stdlib/stdlib.nimble | 0 tests/package/stdlib/system.nim | 2 + tests/package/tstdlib_name_not_special.nim | 3 + 23 files changed, 175 insertions(+), 244 deletions(-) create mode 100644 compiler/packages.nim create mode 100644 tests/modules/a/module_name_clashes.nim create mode 100644 tests/modules/b/module_name_clashes.nim create mode 100644 tests/modules/tmodule_name_clashes.nim create mode 100644 tests/package/stdlib/stdlib.nimble create mode 100644 tests/package/stdlib/system.nim create mode 100644 tests/package/tstdlib_name_not_special.nim diff --git a/compiler/ast.nim b/compiler/ast.nim index d1e5ae2bf..f8343c1a3 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1108,21 +1108,6 @@ proc getPIdent*(a: PNode): PIdent {.inline.} = of nkIdent: a.ident else: nil -proc getnimblePkg*(a: PSym): PSym = - result = a - while result != nil: - case result.kind - of skModule: - result = result.owner - assert result.kind == skPackage - of skPackage: - if result.owner == nil: - break - else: - result = result.owner - else: - assert false, $result.kind - const moduleShift = when defined(cpu32): 20 else: 24 @@ -1167,13 +1152,7 @@ when false: assert dest.ItemId.item <= src.ItemId.item dest = src -proc getnimblePkgId*(a: PSym): int = - let b = a.getnimblePkg - result = if b == nil: -1 else: b.id - var ggDebug* {.deprecated.}: bool ## convenience switch for trying out things -#var -# gMainPackageId*: int proc isCallExpr*(n: PNode): bool = result = n.kind in nkCallKinds diff --git a/compiler/cgen.nim b/compiler/cgen.nim index ad59b759f..8d24f30c5 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -15,8 +15,7 @@ import ccgutils, os, ropes, math, passes, wordrecg, treetab, cgmeth, rodutils, renderer, cgendata, aliases, lowerings, tables, sets, ndi, lineinfos, pathutils, transf, - injectdestructors, astmsgs - + injectdestructors, astmsgs, modulepaths when defined(nimPreviewSlimSystem): import std/assertions @@ -1306,17 +1305,19 @@ proc getFileHeader(conf: ConfigRef; cfile: Cfile): Rope = if conf.hcrOn: result.add("#define NIM_HOT_CODE_RELOADING\L") addNimDefines(result, conf) -proc getSomeNameForModule(m: PSym): Rope = - assert m.kind == skModule - assert m.owner.kind == skPackage - if {sfSystemModule, sfMainModule} * m.flags == {}: - result = m.owner.name.s.mangle.rope - result.add "_" - result.add m.name.s.mangle +proc getSomeNameForModule(conf: ConfigRef, filename: AbsoluteFile): Rope = + ## Returns a mangled module name. + result.add mangleModuleName(conf, filename).mangle + +proc getSomeNameForModule(m: BModule): Rope = + ## Returns a mangled module name. + assert m.module.kind == skModule + assert m.module.owner.kind == skPackage + result.add mangleModuleName(m.g.config, m.filename).mangle proc getSomeInitName(m: BModule, suffix: string): Rope = if not m.hcrOn: - result = getSomeNameForModule(m.module) + result = getSomeNameForModule(m) result.add suffix proc getInitName(m: BModule): Rope = @@ -1555,11 +1556,11 @@ proc genMainProc(m: BModule) = proc registerInitProcs*(g: BModuleList; m: PSym; flags: set[ModuleBackendFlag]) = ## Called from the IC backend. if HasDatInitProc in flags: - let datInit = getSomeNameForModule(m) & "DatInit000" + let datInit = getSomeNameForModule(g.config, g.config.toFullPath(m.info.fileIndex).AbsoluteFile) & "DatInit000" g.mainModProcs.addf("N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [datInit]) g.mainDatInit.addf("\t$1();$N", [datInit]) if HasModuleInitProc in flags: - let init = getSomeNameForModule(m) & "Init000" + let init = getSomeNameForModule(g.config, g.config.toFullPath(m.info.fileIndex).AbsoluteFile) & "Init000" g.mainModProcs.addf("N_LIB_PRIVATE N_NIMCALL(void, $1)(void);$N", [init]) let initCall = "\t$1();$N" % [init] if sfMainModule in m.flags: @@ -1940,7 +1941,7 @@ proc getCFile(m: BModule): AbsoluteFile = if m.compileToCpp: ".nim.cpp" elif m.config.backend == backendObjc or sfCompileToObjc in m.module.flags: ".nim.m" else: ".nim.c" - result = changeFileExt(completeCfilePath(m.config, withPackageName(m.config, m.cfilename)), ext) + result = changeFileExt(completeCfilePath(m.config, mangleModuleName(m.config, m.cfilename).AbsoluteFile), ext) when false: proc myOpenCached(graph: ModuleGraph; module: PSym, rd: PRodReader): PPassContext = diff --git a/compiler/docgen.nim b/compiler/docgen.nim index ecf98d0b6..f633a57a0 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -16,7 +16,7 @@ import packages/docutils/[rst, rstgen, dochelpers], json, xmltree, trees, types, typesrenderer, astalgo, lineinfos, intsets, - pathutils, tables, nimpaths, renderverbatim, osproc + pathutils, tables, nimpaths, renderverbatim, osproc, packages import packages/docutils/rstast except FileIndex, TLineInfo from uri import encodeUrl @@ -413,9 +413,6 @@ proc getPlainDocstring(n: PNode): string = result = getPlainDocstring(n[i]) if result.len > 0: return -proc belongsToPackage(conf: ConfigRef; module: PSym): bool = - result = module.kind == skModule and module.getnimblePkgId == conf.mainPackageId - proc externalDep(d: PDoc; module: PSym): string = if optWholeProject in d.conf.globalOptions or d.conf.docRoot.len > 0: let full = AbsoluteFile toFullPath(d.conf, FileIndex module.position) @@ -471,7 +468,7 @@ proc nodeToHighlightedHtml(d: PDoc; n: PNode; result: var string; "\\spanIdentifier{$1}", [escLit, procLink]) elif s != nil and s.kind in {skType, skVar, skLet, skConst} and sfExported in s.flags and s.owner != nil and - belongsToPackage(d.conf, s.owner) and d.target == outHtml: + belongsToProjectPackage(d.conf, s.owner) and d.target == outHtml: let external = externalDep(d, s.owner) result.addf "$3", [changeFileExt(external, "html"), literal, @@ -1131,7 +1128,7 @@ proc traceDeps(d: PDoc, it: PNode) = for x in it[2]: a[2] = x traceDeps(d, a) - elif it.kind == nkSym and belongsToPackage(d.conf, it.sym): + elif it.kind == nkSym and belongsToProjectPackage(d.conf, it.sym): let external = externalDep(d, it.sym) if d.section[k].finalMarkup != "": d.section[k].finalMarkup.add(", ") dispA(d.conf, d.section[k].finalMarkup, @@ -1141,7 +1138,7 @@ proc traceDeps(d: PDoc, it: PNode) = proc exportSym(d: PDoc; s: PSym) = const k = exportSection - if s.kind == skModule and belongsToPackage(d.conf, s): + if s.kind == skModule and belongsToProjectPackage(d.conf, s): let external = externalDep(d, s) if d.section[k].finalMarkup != "": d.section[k].finalMarkup.add(", ") dispA(d.conf, d.section[k].finalMarkup, @@ -1150,7 +1147,7 @@ proc exportSym(d: PDoc; s: PSym) = changeFileExt(external, "html")]) elif s.kind != skModule and s.owner != nil: let module = originatingModule(s) - if belongsToPackage(d.conf, module): + if belongsToProjectPackage(d.conf, module): let complexSymbol = complexName(s.kind, s.ast, s.name.s) symbolOrId = d.newUniquePlainSymbol(complexSymbol) diff --git a/compiler/docgen2.nim b/compiler/docgen2.nim index bfdb4568c..9abde9f52 100644 --- a/compiler/docgen2.nim +++ b/compiler/docgen2.nim @@ -11,7 +11,7 @@ # semantic checking. import - options, ast, msgs, passes, docgen, lineinfos, pathutils + options, ast, msgs, passes, docgen, lineinfos, pathutils, packages from modulegraphs import ModuleGraph, PPassContext @@ -23,7 +23,7 @@ type PGen = ref TGen proc shouldProcess(g: PGen): bool = - (optWholeProject in g.doc.conf.globalOptions and g.module.getnimblePkgId == g.doc.conf.mainPackageId) or + (optWholeProject in g.doc.conf.globalOptions and g.doc.conf.belongsToProjectPackage(g.module)) or sfMainModule in g.module.flags or g.config.projectMainIdx == g.module.info.fileIndex template closeImpl(body: untyped) {.dirty.} = diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 09a1dfbb3..515c7ba29 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -12,7 +12,7 @@ # from a lineinfos file, to provide generalized procedures to compile # nim files. -import ropes, platform, condsyms, options, msgs, lineinfos, pathutils +import ropes, platform, condsyms, options, msgs, lineinfos, pathutils, modulepaths import std/[os, strutils, osproc, sha1, streams, sequtils, times, strtabs, json, jsonutils, sugar] @@ -370,6 +370,7 @@ proc initVars*(conf: ConfigRef) = proc completeCfilePath*(conf: ConfigRef; cfile: AbsoluteFile, createSubDir: bool = true): AbsoluteFile = + ## Generate the absolute file path to the generated modules. result = completeGeneratedFilePath(conf, cfile, createSubDir) proc toObjFile*(conf: ConfigRef; filename: AbsoluteFile): AbsoluteFile = @@ -380,7 +381,7 @@ proc addFileToCompile*(conf: ConfigRef; cf: Cfile) = conf.toCompile.add(cf) proc addLocalCompileOption*(conf: ConfigRef; option: string; nimfile: AbsoluteFile) = - let key = completeCfilePath(conf, withPackageName(conf, nimfile)).string + let key = completeCfilePath(conf, mangleModuleName(conf, nimfile).AbsoluteFile).string var value = conf.cfileSpecificOptions.getOrDefault(key) if strutils.find(value, option, 0) < 0: addOpt(value, option) @@ -637,7 +638,7 @@ proc footprint(conf: ConfigRef; cfile: Cfile): SecureHash = proc externalFileChanged(conf: ConfigRef; cfile: Cfile): bool = if conf.backend == backendJs: return false # pre-existing behavior, but not sure it's good - let hashFile = toGeneratedFile(conf, conf.withPackageName(cfile.cname), "sha1") + let hashFile = toGeneratedFile(conf, conf.mangleModuleName(cfile.cname).AbsoluteFile, "sha1") let currentHash = footprint(conf, cfile) var f: File if open(f, hashFile.string, fmRead): @@ -845,9 +846,9 @@ proc hcrLinkTargetName(conf: ConfigRef, objFile: string, isMain = false): Absolu proc displayProgressCC(conf: ConfigRef, path, compileCmd: string): string = if conf.hasHint(hintCC): if optListCmd in conf.globalOptions or conf.verbosity > 1: - result = MsgKindToStr[hintCC] % (demanglePackageName(path.splitFile.name) & ": " & compileCmd) + result = MsgKindToStr[hintCC] % (demangleModuleName(path.splitFile.name) & ": " & compileCmd) else: - result = MsgKindToStr[hintCC] % demanglePackageName(path.splitFile.name) + result = MsgKindToStr[hintCC] % demangleModuleName(path.splitFile.name) proc callCCompiler*(conf: ConfigRef) = var diff --git a/compiler/ic/cbackend.nim b/compiler/ic/cbackend.nim index e7ab000e6..815078a36 100644 --- a/compiler/ic/cbackend.nim +++ b/compiler/ic/cbackend.nim @@ -24,7 +24,7 @@ when defined(nimPreviewSlimSystem): import std/assertions import ".."/[ast, options, lineinfos, modulegraphs, cgendata, cgen, - pathutils, extccomp, msgs] + pathutils, extccomp, msgs, modulepaths] import packed_ast, ic, dce, rodfiles @@ -64,7 +64,8 @@ proc addFileToLink(config: ConfigRef; m: PSym) = if config.backend == backendCpp: ".nim.cpp" elif config.backend == backendObjc: ".nim.m" else: ".nim.c" - let cfile = changeFileExt(completeCfilePath(config, withPackageName(config, filename)), ext) + let cfile = changeFileExt(completeCfilePath(config, + mangleModuleName(config, filename).AbsoluteFile), ext) let objFile = completeCfilePath(config, toObjFile(config, cfile)) if fileExists(objFile): var cf = Cfile(nimname: m.name.s, cname: cfile, diff --git a/compiler/ic/ic.nim b/compiler/ic/ic.nim index 193e5f517..38b6987f9 100644 --- a/compiler/ic/ic.nim +++ b/compiler/ic/ic.nim @@ -10,7 +10,7 @@ import hashes, tables, intsets, std/sha1 import packed_ast, bitabs, rodfiles import ".." / [ast, idents, lineinfos, msgs, ropes, options, - pathutils, condsyms] + pathutils, condsyms, packages, modulepaths] #import ".." / [renderer, astalgo] from os import removeFile, isAbsolute @@ -551,6 +551,10 @@ proc loadError(err: RodFileError; filename: AbsoluteFile; config: ConfigRef;) = rawMessage(config, warnCannotOpenFile, filename.string & " reason: " & $err) #echo "Error: ", $err, " loading file: ", filename.string +proc toRodFile*(conf: ConfigRef; f: AbsoluteFile; ext = RodExt): AbsoluteFile = + result = changeFileExt(completeGeneratedFilePath(conf, + mangleModuleName(conf, f).AbsoluteFile), ext) + proc loadRodFile*(filename: AbsoluteFile; m: var PackedModule; config: ConfigRef; ignoreConfig = false): RodFileError = var f = rodfiles.open(filename.string) @@ -930,17 +934,6 @@ proc loadType(c: var PackedDecoder; g: var PackedModuleGraph; thisModule: int; t result = g[si].types[t.item] assert result.itemId.item > 0 -proc newPackage(config: ConfigRef; cache: IdentCache; fileIdx: FileIndex): PSym = - let filename = AbsoluteFile toFullPath(config, fileIdx) - let name = getIdent(cache, splitFile(filename).name) - let info = newLineInfo(fileIdx, 1, 1) - let - pck = getPackageName(config, filename.string) - pck2 = if pck.len > 0: pck else: "unknown" - pack = getIdent(cache, pck2) - result = newSym(skPackage, getIdent(cache, pck2), - ItemId(module: PackageModuleId, item: int32(fileIdx)), nil, info) - proc setupLookupTables(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache; fileIdx: FileIndex; m: var LoadedModule) = m.iface = initTable[PIdent, seq[PackedItemId]]() @@ -968,7 +961,7 @@ proc setupLookupTables(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCa name: getIdent(cache, splitFile(filename).name), info: newLineInfo(fileIdx, 1, 1), position: int(fileIdx)) - m.module.owner = newPackage(conf, cache, fileIdx) + m.module.owner = getPackage(conf, cache, fileIdx) m.module.flags = m.fromDisk.moduleFlags proc loadToReplayNodes(g: var PackedModuleGraph; conf: ConfigRef; cache: IdentCache; diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index c44908dc3..147381910 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -12,7 +12,7 @@ ## or stored in a rod-file. import intsets, tables, hashes, md5_old -import ast, astalgo, options, lineinfos,idents, btrees, ropes, msgs, pathutils +import ast, astalgo, options, lineinfos,idents, btrees, ropes, msgs, pathutils, packages import ic / [packed_ast, ic] when defined(nimPreviewSlimSystem): @@ -67,7 +67,6 @@ type startupPackedConfig*: PackedConfig packageSyms*: TStrTable - modulesPerPackage*: Table[ItemId, TStrTable] deps*: IntSet # the dependency graph or potentially its transitive closure. importDeps*: Table[FileIndex, seq[FileIndex]] # explicit import module dependencies suggestMode*: bool # whether we are in nimsuggest mode or not. @@ -597,3 +596,18 @@ proc onProcessing*(graph: ModuleGraph, fileIdx: FileIndex, moduleStatus: string, let fromModule2 = if fromModule != nil: $fromModule.name.s else: "(toplevel)" let mode = if isNimscript: "(nims) " else: "" rawMessage(conf, hintProcessing, "$#$# $#: $#: $#" % [mode, indent, fromModule2, moduleStatus, path]) + +proc getPackage*(graph: ModuleGraph; fileIdx: FileIndex): PSym = + ## Returns a package symbol for yet to be defined module for fileIdx. + ## The package symbol is added to the graph if it doesn't exist. + let pkgSym = getPackage(graph.config, graph.cache, fileIdx) + # check if the package is already in the graph + result = graph.packageSyms.strTableGet(pkgSym.name) + if result == nil: + # the package isn't in the graph, so create and add it + result = pkgSym + graph.packageSyms.strTableAdd(pkgSym) + +func belongsToStdlib*(graph: ModuleGraph, sym: PSym): bool = + ## Check if symbol belongs to the 'stdlib' package. + sym.getPackageSymbol.getPackageId == graph.systemModule.getPackageId diff --git a/compiler/modulepaths.nim b/compiler/modulepaths.nim index a16b669c4..e80ea3fa6 100644 --- a/compiler/modulepaths.nim +++ b/compiler/modulepaths.nim @@ -10,100 +10,6 @@ import ast, renderer, strutils, msgs, options, idents, os, lineinfos, pathutils -when false: - const - considerParentDirs = not defined(noParentProjects) - considerNimbleDirs = not defined(noNimbleDirs) - - proc findInNimbleDir(pkg, subdir, dir: string): string = - var best = "" - var bestv = "" - for k, p in os.walkDir(dir, relative=true): - if k == pcDir and p.len > pkg.len+1 and - p[pkg.len] == '-' and p.startsWith(pkg): - let (_, a, _) = getPathVersionChecksum(p) - if bestv.len == 0 or bestv < a: - bestv = a - best = dir / p - - if best.len > 0: - var f: File - if open(f, best / changeFileExt(pkg, ".nimble-link")): - # the second line contains what we're interested in, see: - # https://github.com/nim-lang/nimble#nimble-link - var override = "" - discard readLine(f, override) - discard readLine(f, override) - close(f) - if not override.isAbsolute(): - best = best / override - else: - best = override - let f = if subdir.len == 0: pkg else: subdir - let res = addFileExt(best / f, "nim") - if best.len > 0 and fileExists(res): - result = res - -when false: - proc resolveDollar(project, source, pkg, subdir: string; info: TLineInfo): string = - template attempt(a) = - let x = addFileExt(a, "nim") - if fileExists(x): return x - - case pkg - of "stdlib": - if subdir.len == 0: - return options.libpath - else: - for candidate in stdlibDirs: - attempt(options.libpath / candidate / subdir) - of "root": - let root = project.splitFile.dir - if subdir.len == 0: - return root - else: - attempt(root / subdir) - else: - when considerParentDirs: - var p = parentDir(source.splitFile.dir) - # support 'import $karax': - let f = if subdir.len == 0: pkg else: subdir - - while p.len > 0: - let dir = p / pkg - if dirExists(dir): - attempt(dir / f) - # 2nd attempt: try to use 'karax/karax' - attempt(dir / pkg / f) - # 3rd attempt: try to use 'karax/src/karax' - attempt(dir / "src" / f) - attempt(dir / "src" / pkg / f) - p = parentDir(p) - - when considerNimbleDirs: - if not options.gNoNimblePath: - var nimbleDir = getEnv("NIMBLE_DIR") - if nimbleDir.len == 0: nimbleDir = getHomeDir() / ".nimble" - result = findInNimbleDir(pkg, subdir, nimbleDir / "pkgs") - if result.len > 0: return result - when not defined(windows): - result = findInNimbleDir(pkg, subdir, "/opt/nimble/pkgs") - if result.len > 0: return result - - proc scriptableImport(pkg, sub: string; info: TLineInfo): string = - resolveDollar(gProjectFull, info.toFullPath(), pkg, sub, info) - - proc lookupPackage(pkg, subdir: PNode): string = - let sub = if subdir != nil: renderTree(subdir, {renderNoComments}).replace(" ") else: "" - case pkg.kind - of nkStrLit, nkRStrLit, nkTripleStrLit: - result = scriptableImport(pkg.strVal, sub, pkg.info) - of nkIdent: - result = scriptableImport(pkg.ident.s, sub, pkg.info) - else: - localError(pkg.info, "package name must be an identifier or string literal") - result = "" - proc getModuleName*(conf: ConfigRef; n: PNode): string = # This returns a short relative module name without the nim extension # e.g. like "system", "importer" or "somepath/module" @@ -163,3 +69,18 @@ proc checkModuleName*(conf: ConfigRef; n: PNode; doLocalError=true): FileIndex = result = InvalidFileIdx else: result = fileInfoIdx(conf, fullPath) + +proc mangleModuleName*(conf: ConfigRef; path: AbsoluteFile): string = + ## Mangle a relative module path to avoid path and symbol collisions. + ## + ## Used by backends that need to generate intermediary files from Nim modules. + ## This is needed because the compiler uses a flat cache file hierarchy. + ## + ## Example: + ## `foo-#head/../bar` becomes `@foo-@hhead@s..@sbar` + "@m" & relativeTo(path, conf.projectPath).string.multiReplace( + {$os.DirSep: "@s", $os.AltSep: "@s", "#": "@h", "@": "@@", ":": "@c"}) + +proc demangleModuleName*(path: string): string = + ## Demangle a relative module path. + result = path.multiReplace({"@@": "@", "@h": "#", "@s": "/", "@m": "", "@c": ":"}) diff --git a/compiler/modules.nim b/compiler/modules.nim index dd5db63fa..2becef38f 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -12,7 +12,7 @@ import ast, astalgo, magicsys, msgs, options, idents, lexer, passes, syntaxes, llstream, modulegraphs, - lineinfos, pathutils, tables + lineinfos, pathutils, tables, packages when defined(nimPreviewSlimSystem): import std/[syncio, assertions] @@ -25,56 +25,11 @@ proc resetSystemArtifacts*(g: ModuleGraph) = template getModuleIdent(graph: ModuleGraph, filename: AbsoluteFile): PIdent = getIdent(graph.cache, splitFile(filename).name) -template packageId(): untyped {.dirty.} = ItemId(module: PackageModuleId, item: int32(fileIdx)) - -proc getPackage(graph: ModuleGraph; fileIdx: FileIndex): PSym = - ## returns package symbol (skPackage) for yet to be defined module for fileIdx - let filename = AbsoluteFile toFullPath(graph.config, fileIdx) - let name = getModuleIdent(graph, filename) - let info = newLineInfo(fileIdx, 1, 1) - let - pck = getPackageName(graph.config, filename.string) - pck2 = if pck.len > 0: pck else: "unknown" - pack = getIdent(graph.cache, pck2) - result = graph.packageSyms.strTableGet(pack) - if result == nil: - result = newSym(skPackage, getIdent(graph.cache, pck2), packageId(), nil, info) - #initStrTable(packSym.tab) - graph.packageSyms.strTableAdd(result) - else: - let modules = graph.modulesPerPackage.getOrDefault(result.itemId) - let existing = if modules.data.len > 0: strTableGet(modules, name) else: nil - if existing != nil and existing.info.fileIndex != info.fileIndex: - when false: - # we used to produce an error: - localError(graph.config, info, - "module names need to be unique per Nimble package; module clashes with " & - toFullPath(graph.config, existing.info.fileIndex)) - else: - # but starting with version 0.20 we now produce a fake Nimble package instead - # to resolve the conflicts: - let pck3 = fakePackageName(graph.config, filename) - # this makes the new `result`'s owner be the original `result` - result = newSym(skPackage, getIdent(graph.cache, pck3), packageId(), result, info) - #initStrTable(packSym.tab) - graph.packageSyms.strTableAdd(result) - proc partialInitModule(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; filename: AbsoluteFile) = let packSym = getPackage(graph, fileIdx) result.owner = packSym result.position = int fileIdx - #initStrTable(result.tab(graph)) - when false: - strTableAdd(result.tab, result) # a module knows itself - # This is now implemented via - # c.moduleScope.addSym(module) # a module knows itself - # in sem.nim, around line 527 - - if graph.modulesPerPackage.getOrDefault(packSym.itemId).data.len == 0: - graph.modulesPerPackage[packSym.itemId] = newStrTable() - graph.modulesPerPackage[packSym.itemId].strTableAdd(result) - proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym = let filename = AbsoluteFile toFullPath(graph.config, fileIdx) # We cannot call ``newSym`` here, because we have to circumvent the ID @@ -136,7 +91,7 @@ proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PSym = # localError(result.info, errAttemptToRedefine, result.name.s) # restore the notes for outer module: graph.config.notes = - if s.getnimblePkgId == graph.config.mainPackageId or isDefined(graph.config, "booting"): graph.config.mainPackageNotes + if graph.config.belongsToProjectPackage(s) or isDefined(graph.config, "booting"): graph.config.mainPackageNotes else: graph.config.foreignPackageNotes proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PNode = @@ -171,7 +126,7 @@ proc compileProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIdx) = conf.projectMainIdx2 = projectFile let packSym = getPackage(graph, projectFile) - graph.config.mainPackageId = packSym.getnimblePkgId + graph.config.mainPackageId = packSym.getPackageId graph.importStack.add projectFile if projectFile == systemFileIdx: diff --git a/compiler/options.nim b/compiler/options.nim index 69eafb67f..545dfb1d1 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -807,6 +807,8 @@ proc toGeneratedFile*(conf: ConfigRef; path: AbsoluteFile, proc completeGeneratedFilePath*(conf: ConfigRef; f: AbsoluteFile, createSubDir: bool = true): AbsoluteFile = + ## Return an absolute path of a generated intermediary file. + ## Optionally creates the cache directory if `createSubDir` is `true`. let subdir = getNimcacheDir(conf) if createSubDir: try: @@ -814,11 +816,6 @@ proc completeGeneratedFilePath*(conf: ConfigRef; f: AbsoluteFile, except OSError: conf.quitOrRaise "cannot create directory: " & subdir.string result = subdir / RelativeFile f.string.splitPath.tail - #echo "completeGeneratedFilePath(", f, ") = ", result - -proc toRodFile*(conf: ConfigRef; f: AbsoluteFile; ext = RodExt): AbsoluteFile = - result = changeFileExt(completeGeneratedFilePath(conf, - withPackageName(conf, f)), ext) proc rawFindFile(conf: ConfigRef; f: RelativeFile; suppressStdlib: bool): AbsoluteFile = for it in conf.searchPaths: diff --git a/compiler/packagehandling.nim b/compiler/packagehandling.nim index 4af0c28fa..8cf209779 100644 --- a/compiler/packagehandling.nim +++ b/compiler/packagehandling.nim @@ -37,24 +37,7 @@ proc getNimbleFile*(conf: ConfigRef; path: string): string = proc getPackageName*(conf: ConfigRef; path: string): string = ## returns nimble package name, e.g.: `cligen` let path = getNimbleFile(conf, path) - result = path.splitFile.name - -proc fakePackageName*(conf: ConfigRef; path: AbsoluteFile): string = - # Convert `path` so that 2 modules with same name - # in different directory get different name and they can be - # placed in a directory. - # foo-#head/../bar becomes @foo-@hhead@s..@sbar - result = "@m" & relativeTo(path, conf.projectPath).string.multiReplace( - {$os.DirSep: "@s", $os.AltSep: "@s", "#": "@h", "@": "@@", ":": "@c"}) - -proc demanglePackageName*(path: string): string = - result = path.multiReplace({"@@": "@", "@h": "#", "@s": "/", "@m": "", "@c": ":"}) - -proc withPackageName*(conf: ConfigRef; path: AbsoluteFile): AbsoluteFile = - let x = getPackageName(conf, path.string) - let (p, file, ext) = path.splitFile - if x == "stdlib": - # Hot code reloading now relies on 'stdlib_system' names etc. - result = p / RelativeFile((x & '_' & file) & ext) + if path.len > 0: + return path.splitFile.name else: - result = p / RelativeFile(fakePackageName(conf, path)) + return "unknown" diff --git a/compiler/packages.nim b/compiler/packages.nim new file mode 100644 index 000000000..6ceeb1ccc --- /dev/null +++ b/compiler/packages.nim @@ -0,0 +1,49 @@ +# +# +# The Nim Compiler +# (c) Copyright 2022 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. +# + +## Package related procs. +## +## See Also: +## * `packagehandling` for package path handling +## * `modulegraphs.getPackage` +## * `modulegraphs.belongsToStdlib` + +import "." / [options, ast, lineinfos, idents, pathutils, msgs] + +proc getPackage*(conf: ConfigRef; cache: IdentCache; fileIdx: FileIndex): PSym = + ## Return a new package symbol. + ## + ## See Also: + ## * `modulegraphs.getPackage` + let + filename = AbsoluteFile toFullPath(conf, fileIdx) + name = getIdent(cache, splitFile(filename).name) + info = newLineInfo(fileIdx, 1, 1) + pkgName = getPackageName(conf, filename.string) + pkgIdent = getIdent(cache, pkgName) + newSym(skPackage, pkgIdent, ItemId(module: PackageModuleId, item: int32(fileIdx)), nil, info) + +func getPackageSymbol*(sym: PSym): PSym = + ## Return the owning package symbol. + assert sym != nil + result = sym + while result.kind != skPackage: + result = result.owner + assert result != nil, repr(sym.info) + +func getPackageId*(sym: PSym): int = + ## Return the owning package ID. + sym.getPackageSymbol.id + +func belongsToProjectPackage*(conf: ConfigRef, sym: PSym): bool = + ## Return whether the symbol belongs to the project's package. + ## + ## See Also: + ## * `modulegraphs.belongsToStdlib` + conf.mainPackageId == sym.getPackageId diff --git a/compiler/passes.nim b/compiler/passes.nim index 7fb2842f5..3de27575b 100644 --- a/compiler/passes.nim +++ b/compiler/passes.nim @@ -14,7 +14,7 @@ import options, ast, llstream, msgs, idents, syntaxes, modulegraphs, reorder, - lineinfos, pathutils + lineinfos, pathutils, packages when defined(nimPreviewSlimSystem): import std/syncio @@ -104,7 +104,7 @@ const proc prepareConfigNotes(graph: ModuleGraph; module: PSym) = # don't be verbose unless the module belongs to the main package: - if module.getnimblePkgId == graph.config.mainPackageId: + if graph.config.belongsToProjectPackage(module): graph.config.notes = graph.config.mainPackageNotes else: if graph.config.mainPackageNotes == {}: graph.config.mainPackageNotes = graph.config.notes @@ -114,12 +114,6 @@ proc moduleHasChanged*(graph: ModuleGraph; module: PSym): bool {.inline.} = result = true #module.id >= 0 or isDefined(graph.config, "nimBackendAssumesChange") -proc partOfStdlib(x: PSym): bool = - var it = x.owner - while it != nil and it.kind == skPackage and it.owner != nil: - it = it.owner - result = it != nil and it.name.s == "stdlib" - proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator; stream: PLLStream): bool {.discardable.} = if graph.stopCompile(): return true @@ -141,7 +135,7 @@ proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator; while true: openParser(p, fileIdx, s, graph.cache, graph.config) - if not partOfStdlib(module) or module.name.s == "distros": + if not belongsToStdlib(graph, module) or (belongsToStdlib(graph, module) and module.name.s == "distros"): # XXX what about caching? no processing then? what if I change the # modules to include between compilation runs? we'd need to track that # in ROD files. I think we should enable this feature only diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 458c7547a..2262e441b 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -641,12 +641,13 @@ proc pragmaLine(c: PContext, n: PNode) = n.info = getInfoContext(c.config, -1) proc processPragma(c: PContext, n: PNode, i: int) = + ## Create and add a new custom pragma `{.pragma: name.}` node to the module's context. let it = n[i] if it.kind notin nkPragmaCallKinds and it.safeLen == 2: invalidPragma(c, n) elif it.safeLen != 2 or it[0].kind != nkIdent or it[1].kind != nkIdent: invalidPragma(c, n) - var userPragma = newSym(skTemplate, it[1].ident, nextSymId(c.idgen), nil, it.info, c.config.options) + var userPragma = newSym(skTemplate, it[1].ident, nextSymId(c.idgen), c.module, it.info, c.config.options) userPragma.ast = newTreeI(nkPragma, n.info, n.sons[i+1..^1]) strTableAdd(c.userPragmas, userPragma) diff --git a/doc/manual.rst b/doc/manual.rst index bfcb41b7a..5ab257fec 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -6551,6 +6551,19 @@ iterator in which case the overloading resolution takes place: write(stdout, x) # not ambiguous: uses the module C's x +Packages +-------- +A collection of modules in a file tree with an ``identifier.nimble`` file in the +root of the tree is called a Nimble package. A valid package name can only be a +valid Nim identifier and thus its filename is ``identifier.nimble`` where +``identifier`` is the desired package name. A module without a ``.nimble`` file +is assigned the package identifier: `unknown`. + +The distinction between packages allows diagnostic compiler messages to be +scoped to the current project's package vs foreign packages. + + + Compiler Messages ================= diff --git a/tests/ccgbugs/tforward_decl_only.nim b/tests/ccgbugs/tforward_decl_only.nim index 74fbae303..416e50eb5 100644 --- a/tests/ccgbugs/tforward_decl_only.nim +++ b/tests/ccgbugs/tforward_decl_only.nim @@ -1,7 +1,7 @@ discard """ ccodecheck: "\\i !@('struct tyObject_MyRefObject'[0-z]+' {')" ccodecheck: "\\i !@('mymoduleInit')" -ccodecheck: "\\i @('mymoduleDatInit')" +ccodecheck: "\\i @('atmmymoduledotnim_DatInit000')" output: "hello" """ diff --git a/tests/modules/a/module_name_clashes.nim b/tests/modules/a/module_name_clashes.nim new file mode 100644 index 000000000..209526e22 --- /dev/null +++ b/tests/modules/a/module_name_clashes.nim @@ -0,0 +1,8 @@ +# See `tmodule_name_clashes` + +import ../b/module_name_clashes +type A* = object + b*: B + +proc print*(a: A) = + echo repr a diff --git a/tests/modules/b/module_name_clashes.nim b/tests/modules/b/module_name_clashes.nim new file mode 100644 index 000000000..6a10cac33 --- /dev/null +++ b/tests/modules/b/module_name_clashes.nim @@ -0,0 +1,3 @@ +# See `tmodule_name_clashes` + +type B* = object diff --git a/tests/modules/tmodule_name_clashes.nim b/tests/modules/tmodule_name_clashes.nim new file mode 100644 index 000000000..73b166c77 --- /dev/null +++ b/tests/modules/tmodule_name_clashes.nim @@ -0,0 +1,16 @@ +discard """ +targets: "c" +ccodecheck: "\\i @('atmaatsmodule_name_clashesdotnim_DatInit000')" +ccodecheck: "\\i @('atmbatsmodule_name_clashesdotnim_DatInit000')" +joinable: false +""" + +# Test module name clashes within same package. +# This was created to test that module symbol mangling functioned correctly +# for the C backend when there are one or more modules with the same name in +# a package, and more than one of them require module initialization procs. +# I'm not sure of the simplest method to cause the init procs to be generated. + +import a/module_name_clashes + +print A() diff --git a/tests/package/stdlib/stdlib.nimble b/tests/package/stdlib/stdlib.nimble new file mode 100644 index 000000000..e69de29bb diff --git a/tests/package/stdlib/system.nim b/tests/package/stdlib/system.nim new file mode 100644 index 000000000..475f8ec5b --- /dev/null +++ b/tests/package/stdlib/system.nim @@ -0,0 +1,2 @@ +# this module is part of tstdlib_name_not_special +doAssert true \ No newline at end of file diff --git a/tests/package/tstdlib_name_not_special.nim b/tests/package/tstdlib_name_not_special.nim new file mode 100644 index 000000000..e8226a82d --- /dev/null +++ b/tests/package/tstdlib_name_not_special.nim @@ -0,0 +1,3 @@ +# Test whether a another package named 'stdlib' can be imported and used. +# This caused a crash in the past. +import stdlib/system \ No newline at end of file From 4e3eb7414cfedad3d29b2eeab451de4206d91df4 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Wed, 1 Jun 2022 20:44:26 +0800 Subject: [PATCH 2931/3768] [Minor] remove unused and unnecessary local variable (#19853) --- lib/system/reprjs.nim | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/system/reprjs.nim b/lib/system/reprjs.nim index 28935a4ef..f5a0ed3ea 100644 --- a/lib/system/reprjs.nim +++ b/lib/system/reprjs.nim @@ -115,7 +115,6 @@ proc reprArray(a: pointer, typ: PNimType, # We prepend @ to seq, the C backend prepends the pointer to the seq. result = if typ.kind == tySequence: "@[" else: "[" var len: int = 0 - var i: int = 0 {. emit: "`len` = `a`.length;\n" .} var dereffed: pointer = a From 68aeb4c1a64ce73f38a471372277f5ec788f5a6e Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Wed, 1 Jun 2022 21:01:17 +0800 Subject: [PATCH 2932/3768] change the type of mangleJsName; fixes CStringConv warnings (#19852) change the type of mangleJsName since mangleJsName is used in macros, there is no need to use cstring. Using cstring may increase conversions and cause warnings. --- lib/js/jsffi.nim | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/js/jsffi.nim b/lib/js/jsffi.nim index ac963eb89..35cbf2864 100644 --- a/lib/js/jsffi.nim +++ b/lib/js/jsffi.nim @@ -64,7 +64,7 @@ proc validJsName(name: string): bool = if chr notin {'A'..'Z','a'..'z','_','$','0'..'9'}: return false -template mangleJsName(name: cstring): cstring = +template mangleJsName(name: string): string = inc nameCounter "mangledName" & $nameCounter @@ -233,7 +233,7 @@ macro `.`*(obj: JsObject, field: untyped): JsObject = helper(`obj`) else: if not mangledNames.hasKey($field): - mangledNames[$field] = $mangleJsName($field) + mangledNames[$field] = mangleJsName($field) let importString = "#." & mangledNames[$field] result = quote do: proc helper(o: JsObject): JsObject @@ -251,7 +251,7 @@ macro `.=`*(obj: JsObject, field, value: untyped): untyped = helper(`obj`, `value`) else: if not mangledNames.hasKey($field): - mangledNames[$field] = $mangleJsName($field) + mangledNames[$field] = mangleJsName($field) let importString = "#." & mangledNames[$field] & " = #" result = quote do: proc helper(o: JsObject, v: auto) @@ -282,7 +282,7 @@ macro `.()`*(obj: JsObject, importString = "#." & $field & "(@)" else: if not mangledNames.hasKey($field): - mangledNames[$field] = $mangleJsName($field) + mangledNames[$field] = mangleJsName($field) importString = "#." & mangledNames[$field] & "(@)" result = quote: proc helper(o: JsObject): JsObject @@ -302,7 +302,7 @@ macro `.`*[K: cstring, V](obj: JsAssoc[K, V], importString = "#." & $field else: if not mangledNames.hasKey($field): - mangledNames[$field] = $mangleJsName($field) + mangledNames[$field] = mangleJsName($field) importString = "#." & mangledNames[$field] result = quote do: proc helper(o: type(`obj`)): `obj`.V @@ -319,7 +319,7 @@ macro `.=`*[K: cstring, V](obj: JsAssoc[K, V], importString = "#." & $field & " = #" else: if not mangledNames.hasKey($field): - mangledNames[$field] = $mangleJsName($field) + mangledNames[$field] = mangleJsName($field) importString = "#." & mangledNames[$field] & " = #" result = quote do: proc helper(o: type(`obj`), v: `obj`.V) From f7a13f62d634300c3cf68e36dd7926a6a235d52f Mon Sep 17 00:00:00 2001 From: quantimnot <54247259+quantimnot@users.noreply.github.com> Date: Sat, 4 Jun 2022 00:25:21 -0400 Subject: [PATCH 2933/3768] Stop type aliases from inheriting sfUsed (#19861) Fixes #18201 Co-authored-by: quantimnot --- compiler/ast.nim | 2 +- tests/errmsgs/treportunused.nim | 53 +++++++++++++++++++++++++-------- 2 files changed, 41 insertions(+), 14 deletions(-) diff --git a/compiler/ast.nim b/compiler/ast.nim index f8343c1a3..6610a1333 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -1506,7 +1506,7 @@ proc assignType*(dest, src: PType) = # this fixes 'type TLock = TSysLock': if src.sym != nil: if dest.sym != nil: - dest.sym.flags.incl src.sym.flags-{sfExported} + dest.sym.flags.incl src.sym.flags-{sfUsed, sfExported} if dest.sym.annex == nil: dest.sym.annex = src.sym.annex mergeLoc(dest.sym.loc, src.sym.loc) else: diff --git a/tests/errmsgs/treportunused.nim b/tests/errmsgs/treportunused.nim index f5ee79afa..46afe163d 100644 --- a/tests/errmsgs/treportunused.nim +++ b/tests/errmsgs/treportunused.nim @@ -2,19 +2,27 @@ discard """ matrix: "--hint:all:off --hint:XDeclaredButNotUsed" nimoutFull: true nimout: ''' -treportunused.nim(23, 10) Hint: 's1' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(24, 10) Hint: 's2' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(25, 10) Hint: 's3' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(26, 6) Hint: 's4' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(27, 6) Hint: 's5' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(28, 7) Hint: 's6' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(29, 7) Hint: 's7' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(30, 5) Hint: 's8' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(31, 5) Hint: 's9' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(32, 6) Hint: 's10' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(33, 6) Hint: 's11' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(37, 3) Hint: 'v0.99' is declared but not used [XDeclaredButNotUsed] -treportunused.nim(38, 3) Hint: 'v0.99.99' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(51, 5) Hint: 'A' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(52, 5) Hint: 'B' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(55, 5) Hint: 'D' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(56, 5) Hint: 'E' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(59, 5) Hint: 'G' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(60, 5) Hint: 'H' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(64, 5) Hint: 'K' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(65, 5) Hint: 'L' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(31, 10) Hint: 's1' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(32, 10) Hint: 's2' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(33, 10) Hint: 's3' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(34, 6) Hint: 's4' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(35, 6) Hint: 's5' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(36, 7) Hint: 's6' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(37, 7) Hint: 's7' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(38, 5) Hint: 's8' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(39, 5) Hint: 's9' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(40, 6) Hint: 's10' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(41, 6) Hint: 's11' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(45, 3) Hint: 'v0.99' is declared but not used [XDeclaredButNotUsed] +treportunused.nim(46, 3) Hint: 'v0.99.99' is declared but not used [XDeclaredButNotUsed] ''' action: compile """ @@ -36,3 +44,22 @@ type s11 = type(1.2) let `v0.99` = "0.99" `v0.99.99` = "0.99.99" + +block: # bug #18201 + # Test that unused type aliases raise hint XDeclaredButNotUsed. + type + A = int + B = distinct int + + C = object + D = C + E = distinct C + + F = string + G = F + H = distinct F + + J = enum + Foo + K = J + L = distinct J From 4341b06f65396c63eec3689d0f8bb00db26c362f Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Sat, 4 Jun 2022 08:03:03 +0300 Subject: [PATCH 2934/3768] RST: improve simple tables (#19859) * RST: improve simple tables * nim 1.0 gotchas * Still allow legacy boundaries like `----` --- compiler/docgen.nim | 1 + compiler/lineinfos.nim | 2 + doc/apis.rst | 6 +- doc/astspec.txt | 12 +- doc/manual.rst | 2 +- doc/nep1.rst | 6 +- doc/nimdoc.cls | 8 +- doc/niminst.rst | 22 +-- doc/tut1.rst | 6 +- lib/packages/docutils/rst.nim | 223 +++++++++++++++++++++--------- lib/packages/docutils/rstast.nim | 13 ++ lib/packages/docutils/rstgen.nim | 55 +++++--- lib/pure/coro.nim | 4 +- lib/pure/os.nim | 12 +- lib/pure/xmltree.nim | 6 +- tests/stdlib/trst.nim | 229 +++++++++++++++++++++++++++++++ 16 files changed, 487 insertions(+), 120 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index f633a57a0..390f44f2e 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -232,6 +232,7 @@ template declareClosures = of meExpected: k = errXExpected of meGridTableNotImplemented: k = errRstGridTableNotImplemented of meMarkdownIllformedTable: k = errRstMarkdownIllformedTable + of meIllformedTable: k = errRstIllformedTable of meNewSectionExpected: k = errRstNewSectionExpected of meGeneralParseError: k = errRstGeneralParseError of meInvalidDirective: k = errRstInvalidDirectiveX diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index 575be6196..105de1636 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -34,6 +34,7 @@ type errXExpected, errRstGridTableNotImplemented, errRstMarkdownIllformedTable, + errRstIllformedTable, errRstNewSectionExpected, errRstGeneralParseError, errRstInvalidDirectiveX, @@ -106,6 +107,7 @@ const errXExpected: "'$1' expected", errRstGridTableNotImplemented: "grid table is not implemented", errRstMarkdownIllformedTable: "illformed delimiter row of a markdown table", + errRstIllformedTable: "Illformed table: $1", errRstNewSectionExpected: "new section expected $1", errRstGeneralParseError: "general parse error", errRstInvalidDirectiveX: "invalid directive: '$1'", diff --git a/doc/apis.rst b/doc/apis.rst index e8313749d..f0b8c93e5 100644 --- a/doc/apis.rst +++ b/doc/apis.rst @@ -18,9 +18,9 @@ been renamed to fit this scheme. The ultimate goal is that the programmer can *guess* a name. -------------------- ------------ -------------------------------------- +=================== ============ ====================================== English word To use Notes -------------------- ------------ -------------------------------------- +=================== ============ ====================================== initialize initT `init` is used to create a value type `T` new newP `new` is used to create a @@ -82,4 +82,4 @@ literal lit string str identifier ident indentation indent -------------------- ------------ -------------------------------------- +=================== ============ ====================================== diff --git a/doc/astspec.txt b/doc/astspec.txt index 6d3fa1f8c..dbbe2799d 100644 --- a/doc/astspec.txt +++ b/doc/astspec.txt @@ -50,9 +50,9 @@ A leaf of the AST often corresponds to a terminal symbol in the concrete syntax. Note that the default ``float`` in Nim maps to ``float64`` such that the default AST for a float is ``nnkFloat64Lit`` as below. ------------------ --------------------------------------------- +================= ============================================= Nim expression Corresponding AST ------------------ --------------------------------------------- +================= ============================================= ``42`` ``nnkIntLit(intVal = 42)`` ``42'i8`` ``nnkInt8Lit(intVal = 42)`` ``42'i16`` ``nnkInt16Lit(intVal = 42)`` @@ -72,7 +72,7 @@ Nim expression Corresponding AST ``nil`` ``nnkNilLit()`` ``myIdentifier`` ``nnkIdent(strVal = "myIdentifier")`` ``myIdentifier`` after lookup pass: ``nnkSym(strVal = "myIdentifier", ...)`` ------------------ --------------------------------------------- +================= ============================================= Identifiers are ``nnkIdent`` nodes. After the name lookup pass these nodes get transferred into ``nnkSym`` nodes. @@ -1211,9 +1211,9 @@ AST: In general, declaring types mirrors this syntax (i.e., ``nnkStaticTy`` for ``static``, etc.). Examples follow (exceptions marked by ``*``): -------------- --------------------------------------------- +============= ============================================= Nim type Corresponding AST -------------- --------------------------------------------- +============= ============================================= ``static`` ``nnkStaticTy`` ``tuple`` ``nnkTupleTy`` ``var`` ``nnkVarTy`` @@ -1226,7 +1226,7 @@ Nim type Corresponding AST ``proc`` ``nnkProcTy`` ``iterator`` ``nnkIteratorTy`` ``object`` ``nnkObjectTy`` -------------- --------------------------------------------- +============= ============================================= Take special care when declaring types as ``proc``. The behavior is similar to ``Procedure declaration``, below, but does not treat ``nnkGenericParams``. diff --git a/doc/manual.rst b/doc/manual.rst index 5ab257fec..82c9f5758 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -363,7 +363,7 @@ contain the following `escape sequences`:idx:\ : ``\\`` `backslash`:idx: ``\"`` `quotation mark`:idx: ``\'`` `apostrophe`:idx: - ``\`` '0'..'9'+ `character with decimal value d`:idx:; + ``\`` '0'..'9'+ `character with decimal value d`:idx:; all decimal digits directly following are used for the character ``\a`` `alert`:idx: diff --git a/doc/nep1.rst b/doc/nep1.rst index 8eb31332f..9627071bf 100644 --- a/doc/nep1.rst +++ b/doc/nep1.rst @@ -153,9 +153,9 @@ The library uses a simple naming scheme that makes use of common abbreviations to keep the names short but meaningful. -------------------- ------------ -------------------------------------- +=================== ============ ====================================== English word To use Notes -------------------- ------------ -------------------------------------- +=================== ============ ====================================== initialize initFoo initializes a value type `Foo` new newFoo initializes a reference type `Foo` via `new` or a value type `Foo` @@ -220,7 +220,7 @@ literal lit string str identifier ident indentation indent -------------------- ------------ -------------------------------------- +=================== ============ ====================================== Coding Conventions diff --git a/doc/nimdoc.cls b/doc/nimdoc.cls index 271dc5dc9..37039f130 100644 --- a/doc/nimdoc.cls +++ b/doc/nimdoc.cls @@ -63,7 +63,7 @@ \usepackage{scrextend} % for the `addmargin` environment -\usepackage{xcolor} +\usepackage[table]{xcolor} \usepackage[urlbordercolor=blue,linkbordercolor=cyan, pdfborderstyle={/S/U/W 1}]{hyperref} \usepackage{enumitem} % for option list, enumList, and rstfootnote @@ -84,6 +84,11 @@ \definecolor{rstframecolor}{rgb}{0.85, 0.8, 0.6} +\usepackage{booktabs} +\belowrulesep=0ex +\aboverulesep=0ex +\renewcommand{\arraystretch}{1.1} + \newtcolorbox{rstprebox}[1][]{blanker, breakable, left=3mm, right=3mm, top=1mm, bottom=1mm, borderline ={0.1em}{0pt}{rstframecolor}, @@ -127,6 +132,7 @@ \newenvironment{rstoptlist}{% \begin{description}[font=\sffamily\bfseries,style=nextline,leftmargin=\rstoptleftmargin,labelwidth=\rstoptlabelwidth]}{\end{description}} +\usepackage{multirow} \usepackage{tabulary} % tables with adjustable cell width and no overflow % make tabulary prevent overflows (https://tex.stackexchange.com/a/195088) \tymin=60pt diff --git a/doc/niminst.rst b/doc/niminst.rst index 8fff1f0e8..2da8664a9 100644 --- a/doc/niminst.rst +++ b/doc/niminst.rst @@ -49,20 +49,20 @@ contain the following key-value pairs: ==================== ======================================================= Key description ==================== ======================================================= -`Name` the project's name; this needs to be a single word -`DisplayName` the project's long name; this can contain spaces. If +`Name` the project's name; this needs to be a single word +`DisplayName` the project's long name; this can contain spaces. If not specified, this is the same as `Name`. -`Version` the project's version -`OS` the OSes to generate C code for; for example: +`Version` the project's version +`OS` the OSes to generate C code for; for example: `"windows;linux;macosx"` -`CPU` the CPUs to generate C code for; for example: +`CPU` the CPUs to generate C code for; for example: `"i386;amd64;powerpc"` -`Authors` the project's authors -`Description` the project's description -`App` the application's type: "Console" or "GUI". If +`Authors` the project's authors +`Description` the project's description +`App` the application's type: "Console" or "GUI". If "Console", niminst generates a special batch file for Windows to open up the command-line shell. -`License` the filename of the application's license +`License` the filename of the application's license ==================== ======================================================= @@ -149,9 +149,9 @@ Possible options are: ==================== ======================================================= Key description ==================== ======================================================= -`InstallScript` boolean flag whether an installation shell script +`InstallScript` boolean flag whether an installation shell script should be generated. Example: `InstallScript: "Yes"` -`UninstallScript` boolean flag whether a de-installation shell script +`UninstallScript` boolean flag whether a de-installation shell script should be generated. Example: `UninstallScript: "Yes"` ==================== ======================================================= diff --git a/doc/tut1.rst b/doc/tut1.rst index 405df57b1..66a4c3274 100644 --- a/doc/tut1.rst +++ b/doc/tut1.rst @@ -1185,9 +1185,9 @@ subranges) are called ordinal types. Ordinal types have quite a few special operations: ------------------ -------------------------------------------------------- +================= ======================================================== Operation Comment ------------------ -------------------------------------------------------- +================= ======================================================== `ord(x)` returns the integer value that is used to represent `x`'s value `inc(x)` increments `x` by one @@ -1198,7 +1198,7 @@ Operation Comment `succ(x, n)` returns the `n`'th successor of `x` `pred(x)` returns the predecessor of `x` `pred(x, n)` returns the `n`'th predecessor of `x` ------------------ -------------------------------------------------------- +================= ======================================================== The `inc `_, `dec `_, `succ diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index d4bfae48f..cd5f26278 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -255,6 +255,7 @@ type meExpected = "'$1' expected", meGridTableNotImplemented = "grid table is not implemented", meMarkdownIllformedTable = "illformed delimiter row of a Markdown table", + meIllformedTable = "Illformed table: $1", meNewSectionExpected = "new section expected $1", meGeneralParseError = "general parse error", meInvalidDirective = "invalid directive: '$1'", @@ -2467,81 +2468,175 @@ proc parseOverline(p: var RstParser): PRstNode = anchorType=headlineAnchor) type - IntSeq = seq[int] - ColumnLimits = tuple + ColSpec = object + start, stop: int + RstCols = seq[ColSpec] + ColumnLimits = tuple # for Markdown first, last: int ColSeq = seq[ColumnLimits] -proc tokEnd(p: RstParser): int = - result = currentTok(p).col + currentTok(p).symbol.len - 1 +proc tokStart(p: RstParser, idx: int): int = + result = p.tok[idx].col -proc getColumns(p: var RstParser, cols: var IntSeq) = +proc tokStart(p: RstParser): int = + result = tokStart(p, p.idx) + +proc tokEnd(p: RstParser, idx: int): int = + result = p.tok[idx].col + p.tok[idx].symbol.len - 1 + +proc tokEnd(p: RstParser): int = + result = tokEnd(p, p.idx) + +proc getColumns(p: RstParser, cols: var RstCols, startIdx: int): int = + # Fills table column specification (or separator) `cols` and returns + # the next parser index after it. var L = 0 + result = startIdx while true: inc L setLen(cols, L) - cols[L - 1] = tokEnd(p) - assert(currentTok(p).kind == tkAdornment) - inc p.idx - if currentTok(p).kind != tkWhite: break - inc p.idx - if currentTok(p).kind != tkAdornment: break - if currentTok(p).kind == tkIndent: inc p.idx - # last column has no limit: - cols[L - 1] = 32000 + cols[L - 1].start = tokStart(p, result) + cols[L - 1].stop = tokEnd(p, result) + assert(p.tok[result].kind == tkAdornment) + inc result + if p.tok[result].kind != tkWhite: break + inc result + if p.tok[result].kind != tkAdornment: break + if p.tok[result].kind == tkIndent: inc result + +proc checkColumns(p: RstParser, cols: RstCols) = + var + i = p.idx + col = 0 + if p.tok[i].symbol[0] != '=': + rstMessage(p, mwRstStyle, + "only tables with `=` columns specification are allowed") + for col in 0 ..< cols.len: + if tokEnd(p, i) != cols[col].stop: + rstMessage(p, meIllformedTable, + "end of table column #$1 should end at position $2" % [ + $(col+1), $(cols[col].stop+ColRstOffset)], + p.tok[i].line, tokEnd(p, i)) + inc i + if col == cols.len - 1: + if p.tok[i].kind == tkWhite: + inc i + if p.tok[i].kind notin {tkIndent, tkEof}: + rstMessage(p, meIllformedTable, "extraneous column specification") + elif p.tok[i].kind == tkWhite: + inc i + else: + rstMessage(p, meIllformedTable, "no enough table columns", + p.tok[i].line, p.tok[i].col) + +proc getSpans(p: RstParser, nextLine: int, + cols: RstCols, unitedCols: RstCols): seq[int] = + ## Calculates how many columns a joined cell occupies. + if unitedCols.len > 0: + result = newSeq[int](unitedCols.len) + var + iCell = 0 + jCell = 0 + uCell = 0 + while jCell < cols.len: + if cols[jCell].stop < unitedCols[uCell].stop: + inc jCell + elif cols[jCell].stop == unitedCols[uCell].stop: + result[uCell] = jCell - iCell + 1 + iCell = jCell + 1 + jCell = jCell + 1 + inc uCell + else: + rstMessage(p, meIllformedTable, + "spanning underline does not match main table columns", + p.tok[nextLine].line, p.tok[nextLine].col) + +proc parseSimpleTableRow(p: var RstParser, cols: RstCols, colChar: char): PRstNode = + ## Parses 1 row in RST simple table. + # Consider that columns may be spanning (united by using underline like ----): + let nextLine = tokenAfterNewline(p) + var unitedCols: RstCols + var afterSpan: int + if p.tok[nextLine].kind == tkAdornment and p.tok[nextLine].symbol[0] == '-': + afterSpan = getColumns(p, unitedCols, nextLine) + if unitedCols == cols and p.tok[nextLine].symbol[0] == colChar: + # legacy rst.nim compat.: allow punctuation like `----` in main boundaries + afterSpan = nextLine + unitedCols.setLen 0 + else: + afterSpan = nextLine + template colEnd(i): int = + if i == cols.len - 1: high(int) # last column has no limit + elif unitedCols.len > 0: unitedCols[i].stop else: cols[i].stop + template colStart(i): int = + if unitedCols.len > 0: unitedCols[i].start else: cols[i].start + var row = newSeq[string](if unitedCols.len > 0: unitedCols.len else: cols.len) + var spans: seq[int] = getSpans(p, nextLine, cols, unitedCols) + + let line = currentTok(p).line + # Iterate over the lines a single cell may span: + while true: + var nCell = 0 + # distribute tokens between cells in the current line: + while currentTok(p).kind notin {tkIndent, tkEof}: + if tokEnd(p) <= colEnd(nCell): + if tokStart(p) < colStart(nCell): + if currentTok(p).kind != tkWhite: + rstMessage(p, meIllformedTable, + "this word crosses table column from the left") + else: + inc p.idx + else: + row[nCell].add(currentTok(p).symbol) + inc p.idx + else: + if tokStart(p) < colEnd(nCell) and currentTok(p).kind != tkWhite: + rstMessage(p, meIllformedTable, + "this word crosses table column from the right") + inc nCell + if currentTok(p).kind == tkIndent: inc p.idx + if tokEnd(p) <= colEnd(0): break + # Continued current cells because the 1st column is empty. + if currentTok(p).kind in {tkEof, tkAdornment}: + break + for nCell in countup(1, high(row)): row[nCell].add('\n') + result = newRstNode(rnTableRow) + var q: RstParser + for uCell in 0 ..< row.len: + initParser(q, p.s) + q.col = colStart(uCell) + q.line = line - 1 + getTokens(row[uCell], q.tok) + let cell = newRstNode(rnTableDataCell) + cell.span = if spans.len == 0: 0 else: spans[uCell] + cell.add(parseDoc(q)) + result.add(cell) + if afterSpan > p.idx: + p.idx = afterSpan proc parseSimpleTable(p: var RstParser): PRstNode = - var - cols: IntSeq - row: seq[string] - i, last, line: int - c: char - q: RstParser - a, b: PRstNode + var cols: RstCols result = newRstNodeA(p, rnTable) - cols = @[] - row = @[] - a = nil - c = currentTok(p).symbol[0] + let startIdx = getColumns(p, cols, p.idx) + let colChar = currentTok(p).symbol[0] + checkColumns(p, cols) + p.idx = startIdx + result.colCount = cols.len while true: if currentTok(p).kind == tkAdornment: - last = tokenAfterNewline(p) - if p.tok[last].kind in {tkEof, tkIndent}: + checkColumns(p, cols) + p.idx = tokenAfterNewline(p) + if currentTok(p).kind in {tkEof, tkIndent}: # skip last adornment line: - p.idx = last break - getColumns(p, cols) - setLen(row, cols.len) - if a != nil: - for j in 0 ..< a.len: # fix rnTableDataCell -> rnTableHeaderCell - a.sons[j] = newRstNode(rnTableHeaderCell, a.sons[j].sons) + if result.sons.len > 0: result.sons[^1].endsHeader = true + # fix rnTableDataCell -> rnTableHeaderCell for previous table rows: + for nRow in 0 ..< result.sons.len: + for nCell in 0 ..< result.sons[nRow].len: + result.sons[nRow].sons[nCell].kind = rnTableHeaderCell if currentTok(p).kind == tkEof: break - for j in countup(0, high(row)): row[j] = "" - # the following while loop iterates over the lines a single cell may span: - line = currentTok(p).line - while true: - i = 0 - while currentTok(p).kind notin {tkIndent, tkEof}: - if tokEnd(p) <= cols[i]: - row[i].add(currentTok(p).symbol) - inc p.idx - else: - if currentTok(p).kind == tkWhite: inc p.idx - inc i - if currentTok(p).kind == tkIndent: inc p.idx - if tokEnd(p) <= cols[0]: break - if currentTok(p).kind in {tkEof, tkAdornment}: break - for j in countup(1, high(row)): row[j].add('\n') - a = newRstNode(rnTableRow) - for j in countup(0, high(row)): - initParser(q, p.s) - q.col = cols[j] - q.line = line - 1 - getTokens(row[j], q.tok) - b = newRstNode(rnTableDataCell) - b.add(parseDoc(q)) - a.add(b) - result.add(a) + let tabRow = parseSimpleTableRow(p, cols, colChar) + result.add tabRow proc readTableRow(p: var RstParser): ColSeq = if currentTok(p).symbol == "|": inc p.idx @@ -2574,17 +2669,16 @@ proc isValidDelimiterRow(p: var RstParser, colNum: int): bool = proc parseMarkdownTable(p: var RstParser): PRstNode = var row: ColSeq - colNum: int a, b: PRstNode q: RstParser result = newRstNodeA(p, rnMarkdownTable) proc parseRow(p: var RstParser, cellKind: RstNodeKind, result: PRstNode) = row = readTableRow(p) - if colNum == 0: colNum = row.len # table header - elif row.len < colNum: row.setLen(colNum) + if result.colCount == 0: result.colCount = row.len # table header + elif row.len < result.colCount: row.setLen(result.colCount) a = newRstNode(rnTableRow) - for j in 0 ..< colNum: + for j in 0 ..< result.colCount: b = newRstNode(cellKind) initParser(q, p.s) q.col = p.col @@ -2595,7 +2689,8 @@ proc parseMarkdownTable(p: var RstParser): PRstNode = result.add(a) parseRow(p, rnTableHeaderCell, result) - if not isValidDelimiterRow(p, colNum): rstMessage(p, meMarkdownIllformedTable) + if not isValidDelimiterRow(p, result.colCount): + rstMessage(p, meMarkdownIllformedTable) while predNL(p) and currentTok(p).symbol == "|": parseRow(p, rnTableDataCell, result) diff --git a/lib/packages/docutils/rstast.nim b/lib/packages/docutils/rstast.nim index 1d5da5e1c..bf0bc1be5 100644 --- a/lib/packages/docutils/rstast.nim +++ b/lib/packages/docutils/rstast.nim @@ -112,6 +112,12 @@ type ## nodes that are post-processed after parsing of rnNimdocRef: tooltip*: string + of rnTable, rnGridTable, rnMarkdownTable: + colCount*: int ## Number of (not-united) cells in the table + of rnTableRow: + endsHeader*: bool ## Is last row in the header of table? + of rnTableHeaderCell, rnTableDataCell: + span*: int ## Number of table columns that the cell occupies else: discard anchor*: string ## anchor, internal link target @@ -416,6 +422,13 @@ proc treeRepr*(node: PRstNode, indent=0): string = result.add (if node.order == 0: "" else: " order=" & $node.order) of rnMarkdownBlockQuoteItem: result.add " quotationDepth=" & $node.quotationDepth + of rnTable, rnGridTable, rnMarkdownTable: + result.add " colCount=" & $node.colCount + of rnTableHeaderCell, rnTableDataCell: + if node.span > 0: + result.add " span=" & $node.span + of rnTableRow: + if node.endsHeader: result.add " endsHeader" else: discard result.add (if node.anchor == "": "" else: " anchor='" & node.anchor & "'") diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index d662a667c..8eb4be675 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -1091,10 +1091,6 @@ proc renderContainer(d: PDoc, n: PRstNode, result: var string) = else: dispA(d.target, result, "
      $2
      ", "$2", [arg, tmp]) -proc texColumns(n: PRstNode): string = - let nColumns = if n.sons.len > 0: len(n.sons[0]) else: 1 - result = "L".repeat(nColumns) - proc renderField(d: PDoc, n: PRstNode, result: var string) = var b = false if d.target == outLatex: @@ -1323,24 +1319,49 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = renderAux(d, n, "$1", "\n$2\n\\begin{rsttab}{" & - texColumns(n) & "}\n\\hline\n$1\\end{rsttab}", result) + "L".repeat(n.colCount) & "}\n\\toprule\n$1" & + "\\addlinespace[0.1em]\\bottomrule\n\\end{rsttab}", result) of rnTableRow: if len(n) >= 1: - if d.target == outLatex: - #var tmp = "" - renderRstToOut(d, n.sons[0], result) - for i in countup(1, len(n) - 1): - result.add(" & ") - renderRstToOut(d, n.sons[i], result) - result.add("\\\\\n\\hline\n") - else: + case d.target + of outHtml: result.add("") renderAux(d, n, result) result.add("\n") - of rnTableDataCell: - renderAux(d, n, "$1", "$1", result) - of rnTableHeaderCell: - renderAux(d, n, "$1", "\\textbf{$1}", result) + of outLatex: + if n.sons[0].kind == rnTableHeaderCell: + result.add "\\rowcolor{gray!15} " + var spanLines: seq[(int, int)] + var nCell = 0 + for uCell in 0 .. n.len - 1: + renderRstToOut(d, n.sons[uCell], result) + if n.sons[uCell].span > 0: + spanLines.add (nCell + 1, nCell + n.sons[uCell].span) + nCell += n.sons[uCell].span + else: + nCell += 1 + if uCell != n.len - 1: + result.add(" & ") + result.add("\\\\") + if n.endsHeader: result.add("\\midrule\n") + for (start, stop) in spanLines: + result.add("\\cmidrule(lr){$1-$2}" % [$start, $stop]) + result.add("\n") + of rnTableHeaderCell, rnTableDataCell: + case d.target + of outHtml: + let tag = if n.kind == rnTableHeaderCell: "th" else: "td" + var spanSpec: string + if n.span <= 1: spanSpec = "" + else: + spanSpec = " colspan=\"" & $n.span & "\" style=\"text-align: center\"" + renderAux(d, n, "<$1$2>$$1" % [tag, spanSpec], "", result) + of outLatex: + let text = if n.kind == rnTableHeaderCell: "\\textbf{$1}" else: "$1" + var latexStr: string + if n.span <= 1: latexStr = text + else: latexStr = "\\multicolumn{" & $n.span & "}{c}{" & text & "}" + renderAux(d, n, "", latexStr, result) of rnFootnoteGroup: renderAux(d, n, "
      " & diff --git a/lib/pure/coro.nim b/lib/pure/coro.nim index f4495a536..aaf442a83 100644 --- a/lib/pure/coro.nim +++ b/lib/pure/coro.nim @@ -8,11 +8,11 @@ # ## Nim coroutines implementation, supports several context switching methods: -## -------- ------------ +## ======== ============ ## ucontext available on unix and alike (default) ## setjmp available on unix and alike (x86/64 only) ## fibers available and required on windows. -## -------- ------------ +## ======== ============ ## ## -d:nimCoroutines Required to build this module. ## -d:nimCoroutinesUcontext Use ucontext backend. diff --git a/lib/pure/os.nim b/lib/pure/os.nim index fa379a228..247a0d089 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -2383,21 +2383,21 @@ iterator walkDirRec*(dir: string, ## ## Walking is recursive. `followFilter` controls the behaviour of the iterator: ## - ## --------------------- --------------------------------------------- + ## ===================== ============================================= ## yieldFilter meaning - ## --------------------- --------------------------------------------- + ## ===================== ============================================= ## ``pcFile`` yield real files (default) ## ``pcLinkToFile`` yield symbolic links to files ## ``pcDir`` yield real directories ## ``pcLinkToDir`` yield symbolic links to directories - ## --------------------- --------------------------------------------- + ## ===================== ============================================= ## - ## --------------------- --------------------------------------------- + ## ===================== ============================================= ## followFilter meaning - ## --------------------- --------------------------------------------- + ## ===================== ============================================= ## ``pcDir`` follow real directories (default) ## ``pcLinkToDir`` follow symbolic links to directories - ## --------------------- --------------------------------------------- + ## ===================== ============================================= ## ## ## See also: diff --git a/lib/pure/xmltree.nim b/lib/pure/xmltree.nim index fdb2c3fc5..9a9ecde57 100644 --- a/lib/pure/xmltree.nim +++ b/lib/pure/xmltree.nim @@ -552,15 +552,15 @@ proc escape*(s: string): string = ## ## Escapes these characters: ## - ## ------------ ------------------- + ## ============ =================== ## char is converted to - ## ------------ ------------------- + ## ============ =================== ## ``<`` ``<`` ## ``>`` ``>`` ## ``&`` ``&`` ## ``"`` ``"`` ## ``'`` ``'`` - ## ------------ ------------------- + ## ============ =================== ## ## You can also use `addEscaped proc <#addEscaped,string,string>`_. result = newStringOfCap(s.len) diff --git a/tests/stdlib/trst.nim b/tests/stdlib/trst.nim index 9787c13eb..24bc03027 100644 --- a/tests/stdlib/trst.nim +++ b/tests/stdlib/trst.nim @@ -3,6 +3,8 @@ discard """ [Suite] RST parsing +[Suite] RST tables + [Suite] RST indentation [Suite] Warnings @@ -618,6 +620,233 @@ suite "RST parsing": rnLeaf 'code' """) +suite "RST tables": + + test "formatting in tables works": + check( + dedent""" + ========= === + `build` `a` + ========= === + """.toAst == + dedent""" + rnTable colCount=2 + rnTableRow + rnTableDataCell + rnInlineCode + rnDirArg + rnLeaf 'nim' + [nil] + rnLiteralBlock + rnLeaf 'build' + rnTableDataCell + rnInlineCode + rnDirArg + rnLeaf 'nim' + [nil] + rnLiteralBlock + rnLeaf 'a' + """) + + test "tables with slightly overflowed cells cause an error (1)": + var error = new string + check( + dedent""" + ====== ====== + Inputs Output + ====== ====== + """.toAst(error=error) == "") + check(error[] == "input(2, 2) Error: Illformed table: " & + "this word crosses table column from the right") + + test "tables with slightly overflowed cells cause an error (2)": + var error = new string + check("" == dedent""" + ===== ===== ====== + Input Output + ===== ===== ====== + False False False + ===== ===== ====== + """.toAst(error=error)) + check(error[] == "input(2, 8) Error: Illformed table: " & + "this word crosses table column from the right") + + test "tables with slightly underflowed cells cause an error": + var error = new string + check("" == dedent""" + ===== ===== ====== + Input Output + ===== ===== ====== + False False False + ===== ===== ====== + """.toAst(error=error)) + check(error[] == "input(2, 7) Error: Illformed table: " & + "this word crosses table column from the left") + + test "tables with unequal underlines should be reported (1)": + var error = new string + error[] = "none" + check("" == dedent""" + ===== ====== + Input Output + ===== ====== + False False + ===== ======= + """.toAst(error=error)) + check(error[] == "input(5, 14) Error: Illformed table: " & + "end of table column #2 should end at position 13") + + test "tables with unequal underlines should be reported (2)": + var error = new string + check("" == dedent""" + ===== ====== + Input Output + ===== ======= + False False + ===== ====== + """.toAst(error=error)) + check(error[] == "input(3, 14) Error: Illformed table: " & + "end of table column #2 should end at position 13") + + test "tables with empty first cells": + check( + dedent""" + = = = + x y z + t + = = = + """.toAst == + dedent""" + rnTable colCount=3 + rnTableRow + rnTableDataCell + rnLeaf 'x' + rnTableDataCell + rnInner + rnLeaf 'y' + rnLeaf ' ' + rnTableDataCell + rnInner + rnLeaf 'z' + rnLeaf ' ' + rnLeaf 't' + """) + + test "tables with spanning cells & separators": + check( + dedent""" + ===== ===== ====== + Inputs Output + ------------ ------ + A B A or B + ===== ===== ====== + False False False + True False True + ----- ----- ------ + False True True + True True True + ===== ===== ====== + """.toAst == + dedent""" + rnTable colCount=3 + rnTableRow + rnTableHeaderCell span=2 + rnLeaf 'Inputs' + rnTableHeaderCell span=1 + rnLeaf 'Output' + rnTableRow endsHeader + rnTableHeaderCell + rnLeaf 'A' + rnTableHeaderCell + rnLeaf 'B' + rnTableHeaderCell + rnInner + rnLeaf 'A' + rnLeaf ' ' + rnLeaf 'or' + rnLeaf ' ' + rnLeaf 'B' + rnTableRow + rnTableDataCell + rnLeaf 'False' + rnTableDataCell + rnLeaf 'False' + rnTableDataCell + rnLeaf 'False' + rnTableRow + rnTableDataCell span=1 + rnLeaf 'True' + rnTableDataCell span=1 + rnLeaf 'False' + rnTableDataCell span=1 + rnLeaf 'True' + rnTableRow + rnTableDataCell + rnLeaf 'False' + rnTableDataCell + rnLeaf 'True' + rnTableDataCell + rnLeaf 'True' + rnTableRow + rnTableDataCell + rnLeaf 'True' + rnTableDataCell + rnLeaf 'True' + rnTableDataCell + rnLeaf 'True' + """) + + test "tables with spanning cells with uneqal underlines cause an error": + var error = new string + check( + dedent""" + ===== ===== ====== + Inputs Output + ------------- ------ + A B A or B + ===== ===== ====== + """.toAst(error=error) == "") + check(error[] == "input(3, 1) Error: Illformed table: " & + "spanning underline does not match main table columns") + + let expTable = dedent""" + rnTable colCount=2 + rnTableRow + rnTableDataCell + rnLeaf 'Inputs' + rnTableDataCell + rnLeaf 'Output' + """ + + test "only tables with `=` columns specs are allowed (1)": + var warnings = new seq[string] + check( + dedent""" + ------ ------ + Inputs Output + ------ ------ + """.toAst(warnings=warnings) == + expTable) + check(warnings[] == + @["input(1, 1) Warning: RST style: " & + "only tables with `=` columns specification are allowed", + "input(3, 1) Warning: RST style: " & + "only tables with `=` columns specification are allowed"]) + + test "only tables with `=` columns specs are allowed (2)": + var warnings = new seq[string] + check( + dedent""" + ====== ====== + Inputs Output + ~~~~~~ ~~~~~~ + """.toAst(warnings=warnings) == + expTable) + check(warnings[] == + @["input(3, 1) Warning: RST style: "& + "only tables with `=` columns specification are allowed"]) + + suite "RST indentation": test "nested bullet lists": let input = dedent """ From d0232f0e5b97b697a640c2f622c7596ce6d10eb5 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Sat, 4 Jun 2022 02:03:25 -0300 Subject: [PATCH 2935/3768] Add Microtasks (#19860) * Add Microtasks * Add Microtasks --- changelog.md | 2 +- lib/js/jscore.nim | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 9548f3034..2ae64d543 100644 --- a/changelog.md +++ b/changelog.md @@ -50,7 +50,7 @@ becomes an alias for `addr`. - Removed deprecated `std/dom_extensions`. - Removed deprecated `std/posix.CMSG_SPACE` and `std/posix.CMSG_LEN` that takes wrong argument types. - Remove deprecated `osproc.poDemon`, symbol with typo. - +- Added [`queueMicrotask`](https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask) for JavaScript targets. - Deprecated `selfExe` for Nimscript. diff --git a/lib/js/jscore.nim b/lib/js/jscore.nim index 4518f32ce..e6060535b 100644 --- a/lib/js/jscore.nim +++ b/lib/js/jscore.nim @@ -132,3 +132,8 @@ since (1, 7): var arrai = @[1, 2, 3] assert arrai.shift() == 1 assert arrai == @[2, 3] + + func queueMicrotask*(function: proc) {.importjs: "$1(#)".} = + ## * https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask + ## * https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API/Microtask_guide + runnableExamples: queueMicrotask(proc() = echo "Microtask") From b024a45163ce1a2224ec5891c81cf2cc7d399f11 Mon Sep 17 00:00:00 2001 From: Zoom Date: Tue, 7 Jun 2022 08:35:06 +0000 Subject: [PATCH 2936/3768] Fix `find` routines' api to default to `last=-1` (#19761) This changes the default for the `last` parameter of various `find` routines from `0` to `-1`. Previous default prevents limiting the search to the first character. This is a logic error, as full text search was performed for 2 *valid* values of `last`: `0` and `last.high()`. Adds an overload for `initSkipTable` which returns a newly initialized table. This encapsulates every single usage of a `var`-acting original func in this module. Co-authored-by: flywind --- lib/pure/strutils.nim | 91 +++++++++++++++++++++----------------- tests/stdlib/tstrutils.nim | 42 ++++++++++-------- 2 files changed, 73 insertions(+), 60 deletions(-) diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index b98bd2bec..ffbefd0fb 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -1815,6 +1815,7 @@ func initSkipTable*(a: var SkipTable, sub: string) {.rtl, ## Initializes table `a` for efficient search of substring `sub`. ## ## See also: + ## * `initSkipTable func<#initSkipTable,string>`_ ## * `find func<#find,SkipTable,string,string,Natural,int>`_ # TODO: this should be the `default()` initializer for the type. let m = len(sub) @@ -1823,7 +1824,16 @@ func initSkipTable*(a: var SkipTable, sub: string) {.rtl, for i in 0 ..< m - 1: a[sub[i]] = m - 1 - i -func find*(a: SkipTable, s, sub: string, start: Natural = 0, last = 0): int {. +func initSkipTable*(sub: string): SkipTable {.noinit, rtl, + extern: "nsuInitNewSkipTable".} = + ## Returns a new table initialized for `sub`. + ## + ## See also: + ## * `initSkipTable func<#initSkipTable,SkipTable,string>`_ + ## * `find func<#find,SkipTable,string,string,Natural,int>`_ + initSkipTable(result, sub) + +func find*(a: SkipTable, s, sub: string, start: Natural = 0, last = -1): int {. rtl, extern: "nsuFindStrA".} = ## Searches for `sub` in `s` inside range `start..last` using preprocessed ## table `a`. If `last` is unspecified, it defaults to `s.high` (the last @@ -1832,9 +1842,10 @@ func find*(a: SkipTable, s, sub: string, start: Natural = 0, last = 0): int {. ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned. ## ## See also: + ## * `initSkipTable func<#initSkipTable,string>`_ ## * `initSkipTable func<#initSkipTable,SkipTable,string>`_ let - last = if last == 0: s.high else: last + last = if last < 0: s.high else: last subLast = sub.len - 1 if subLast == -1: @@ -1844,6 +1855,7 @@ func find*(a: SkipTable, s, sub: string, start: Natural = 0, last = 0): int {. # This is an implementation of the Boyer-Moore Horspool algorithms # https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore%E2%80%93Horspool_algorithm + result = -1 var skip = start while last - skip >= subLast: @@ -1853,7 +1865,6 @@ func find*(a: SkipTable, s, sub: string, start: Natural = 0, last = 0): int {. return skip dec i inc skip, a[s[skip + subLast]] - return -1 when not (defined(js) or defined(nimdoc) or defined(nimscript)): func c_memchr(cstr: pointer, c: char, n: csize_t): pointer {. @@ -1865,10 +1876,10 @@ when not (defined(js) or defined(nimdoc) or defined(nimscript)): else: const hasCStringBuiltin = false -func find*(s: string, sub: char, start: Natural = 0, last = 0): int {.rtl, +func find*(s: string, sub: char, start: Natural = 0, last = -1): int {.rtl, extern: "nsuFindChar".} = ## Searches for `sub` in `s` inside range `start..last` (both ends included). - ## If `last` is unspecified, it defaults to `s.high` (the last element). + ## If `last` is unspecified or negative, it defaults to `s.high` (the last element). ## ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned. ## Otherwise the index returned is relative to `s[0]`, not `start`. @@ -1877,26 +1888,30 @@ func find*(s: string, sub: char, start: Natural = 0, last = 0): int {.rtl, ## See also: ## * `rfind func<#rfind,string,char,Natural,int>`_ ## * `replace func<#replace,string,char,char>`_ - let last = if last == 0: s.high else: last - when nimvm: + result = -1 + let last = if last < 0: s.high else: last + + template findImpl = for i in int(start)..last: - if sub == s[i]: return i + if s[i] == sub: + return i + + when nimvm: + findImpl() else: when hasCStringBuiltin: - let L = last-start+1 - if L > 0: - let found = c_memchr(s[start].unsafeAddr, sub, cast[csize_t](L)) + let length = last-start+1 + if length > 0: + let found = c_memchr(s[start].unsafeAddr, sub, cast[csize_t](length)) if not found.isNil: return cast[ByteAddress](found) -% cast[ByteAddress](s.cstring) else: - for i in int(start)..last: - if sub == s[i]: return i - return -1 + findImpl() -func find*(s: string, chars: set[char], start: Natural = 0, last = 0): int {. +func find*(s: string, chars: set[char], start: Natural = 0, last = -1): int {. rtl, extern: "nsuFindCharSet".} = ## Searches for `chars` in `s` inside range `start..last` (both ends included). - ## If `last` is unspecified, it defaults to `s.high` (the last element). + ## If `last` is unspecified or negative, it defaults to `s.high` (the last element). ## ## If `s` contains none of the characters in `chars`, -1 is returned. ## Otherwise the index returned is relative to `s[0]`, not `start`. @@ -1905,15 +1920,16 @@ func find*(s: string, chars: set[char], start: Natural = 0, last = 0): int {. ## See also: ## * `rfind func<#rfind,string,set[char],Natural,int>`_ ## * `multiReplace func<#multiReplace,string,varargs[]>`_ - let last = if last == 0: s.high else: last + result = -1 + let last = if last < 0: s.high else: last for i in int(start)..last: - if s[i] in chars: return i - return -1 + if s[i] in chars: + return i -func find*(s, sub: string, start: Natural = 0, last = 0): int {.rtl, +func find*(s, sub: string, start: Natural = 0, last = -1): int {.rtl, extern: "nsuFindStr".} = ## Searches for `sub` in `s` inside range `start..last` (both ends included). - ## If `last` is unspecified, it defaults to `s.high` (the last element). + ## If `last` is unspecified or negative, it defaults to `s.high` (the last element). ## ## Searching is case-sensitive. If `sub` is not in `s`, -1 is returned. ## Otherwise the index returned is relative to `s[0]`, not `start`. @@ -1925,28 +1941,23 @@ func find*(s, sub: string, start: Natural = 0, last = 0): int {.rtl, if sub.len > s.len - start: return -1 if sub.len == 1: return find(s, sub[0], start, last) - template useSkipTable {.dirty.} = - var a {.noinit.}: SkipTable - initSkipTable(a, sub) - result = find(a, s, sub, start, last) + template useSkipTable = + result = find(initSkipTable(sub), s, sub, start, last) - when not hasCStringBuiltin: + when nimvm: useSkipTable() else: - when nimvm: - useSkipTable() - else: - when hasCStringBuiltin: - if last == 0 and s.len > start: - let found = c_strstr(s[start].unsafeAddr, sub) - if not found.isNil: - result = cast[ByteAddress](found) -% cast[ByteAddress](s.cstring) + when hasCStringBuiltin: + if last < 0 and start < s.len: + let found = c_strstr(s[start].unsafeAddr, sub) + result = if not found.isNil: + cast[ByteAddress](found) -% cast[ByteAddress](s.cstring) else: - result = -1 - else: - useSkipTable() + -1 else: useSkipTable() + else: + useSkipTable() func rfind*(s: string, sub: char, start: Natural = 0, last = -1): int {.rtl, extern: "nsuRFindChar".} = @@ -2121,8 +2132,7 @@ func replace*(s, sub: string, by = ""): string {.rtl, # copy the rest: add result, substr(s, i) else: - var a {.noinit.}: SkipTable - initSkipTable(a, sub) + var a = initSkipTable(sub) let last = s.high var i = 0 while true: @@ -2162,9 +2172,8 @@ func replaceWord*(s, sub: string, by = ""): string {.rtl, ## replaced. if sub.len == 0: return s const wordChars = {'a'..'z', 'A'..'Z', '0'..'9', '_', '\128'..'\255'} - var a {.noinit.}: SkipTable result = "" - initSkipTable(a, sub) + var a = initSkipTable(sub) var i = 0 let last = s.high let sublen = sub.len diff --git a/tests/stdlib/tstrutils.nim b/tests/stdlib/tstrutils.nim index 26fc0c7b0..e17277ef2 100644 --- a/tests/stdlib/tstrutils.nim +++ b/tests/stdlib/tstrutils.nim @@ -232,18 +232,22 @@ template main() = {.pop.} block: # find - doAssert "0123456789ABCDEFGH".find('A') == 10 - doAssert "0123456789ABCDEFGH".find('A', 5) == 10 - doAssert "0123456789ABCDEFGH".find('A', 5, 10) == 10 - doAssert "0123456789ABCDEFGH".find('A', 5, 9) == -1 - doAssert "0123456789ABCDEFGH".find("A") == 10 - doAssert "0123456789ABCDEFGH".find("A", 5) == 10 - doAssert "0123456789ABCDEFGH".find("A", 5, 10) == 10 - doAssert "0123456789ABCDEFGH".find("A", 5, 9) == -1 - doAssert "0123456789ABCDEFGH".find({'A'..'C'}) == 10 - doAssert "0123456789ABCDEFGH".find({'A'..'C'}, 5) == 10 - doAssert "0123456789ABCDEFGH".find({'A'..'C'}, 5, 10) == 10 - doAssert "0123456789ABCDEFGH".find({'A'..'C'}, 5, 9) == -1 + const haystack: string = "0123456789ABCDEFGH" + doAssert haystack.find('A') == 10 + doAssert haystack.find('A', 5) == 10 + doAssert haystack.find('A', 5, 10) == 10 + doAssert haystack.find('A', 5, 9) == -1 + doAssert haystack.find("A") == 10 + doAssert haystack.find("A", 5) == 10 + doAssert haystack.find("A", 5, 10) == 10 + doAssert haystack.find("A", 5, 9) == -1 + doAssert haystack.find({'A'..'C'}) == 10 + doAssert haystack.find({'A'..'C'}, 5) == 10 + doAssert haystack.find({'A'..'C'}, 5, 10) == 10 + doAssert haystack.find({'A'..'C'}, 5, 9) == -1 + doAssert haystack.find('A', 0, 0) == -1 # search limited to the first char + doAssert haystack.find('A', 5, 0) == -1 # last < start + doAssert haystack.find('A', 5, 4) == -1 # last < start block: const haystack: string = "ABCABABABABCAB" @@ -290,17 +294,17 @@ template main() = # when last <= start, searching for non-empty string block: - let last: int = -1 - doAssert "abcd".find("ab", start=0, last=last) == -1 - doAssert "abcd".find("ab", start=1, last=last) == -1 - doAssert "abcd".find("bc", start=1, last=last) == -1 - doAssert "abcd".find("bc", start=2, last=last) == -1 - block: - let last: int = 0 + let last: int = -1 # searching through whole line doAssert "abcd".find("ab", start=0, last=last) == 0 doAssert "abcd".find("ab", start=1, last=last) == -1 doAssert "abcd".find("bc", start=1, last=last) == 1 doAssert "abcd".find("bc", start=2, last=last) == -1 + block: + let last: int = 0 + doAssert "abcd".find("ab", start=0, last=last) == -1 + doAssert "abcd".find("ab", start=1, last=last) == -1 + doAssert "abcd".find("bc", start=1, last=last) == -1 + doAssert "abcd".find("bc", start=2, last=last) == -1 block: let last: int = 1 doAssert "abcd".find("ab", start=0, last=last) == 0 From f2b16c490df858328cbd884ff9ca9d1a675a4a31 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Wed, 8 Jun 2022 11:10:52 -0300 Subject: [PATCH 2937/3768] Add Microtasks docs (#19870) * Add Microtasks * Add Microtasks * Workaround to build js docs in older NodeJS versions --- lib/js/jscore.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/js/jscore.nim b/lib/js/jscore.nim index e6060535b..781e8fd57 100644 --- a/lib/js/jscore.nim +++ b/lib/js/jscore.nim @@ -136,4 +136,4 @@ since (1, 7): func queueMicrotask*(function: proc) {.importjs: "$1(#)".} = ## * https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask ## * https://developer.mozilla.org/en-US/docs/Web/API/HTML_DOM_API/Microtask_guide - runnableExamples: queueMicrotask(proc() = echo "Microtask") + runnableExamples"-r:off": queueMicrotask(proc() = echo "Microtask") From e1702ae1e6da5bf560d7b692bcd5fa513ece8a69 Mon Sep 17 00:00:00 2001 From: Zoom Date: Wed, 8 Jun 2022 14:11:22 +0000 Subject: [PATCH 2938/3768] `changelog.md`: Add `strutils.find` changes (#19868) Added `strutils.find` changes Standard library section restructured to group by `changed/added/deprecated/removed` with paragraphs marked by markdown comments. --- changelog.md | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/changelog.md b/changelog.md index 2ae64d543..356ee9263 100644 --- a/changelog.md +++ b/changelog.md @@ -24,35 +24,39 @@ becomes an alias for `addr`. ## Standard library additions and changes -- `macros.parseExpr` and `macros.parseStmt` now accept an optional +[//]: # "Changes:" +- `macros.parseExpr` and `macros.parseStmt` now accept an optional. filename argument for more informative errors. - Module `colors` expanded with missing colors from the CSS color standard. - Fixed `lists.SinglyLinkedList` being broken after removing the last node ([#19353](https://github.com/nim-lang/Nim/pull/19353)). - `md5` now works at compile time and in JavaScript. - - `std/smtp` sends `ehlo` first. If the mail server does not understand, it sends `helo` as a fallback. +- Changed mimedb to use an `OrderedTable` instead of `OrderedTableRef`, to use it in a const. +- `strutils.find` now use and default to `last=-1` for whole string searches, making limiting it to just the first char (`last=0`) valid. +[//]: # "Additions:" - Added `IsoWeekRange`, a range type to represent the number of weeks in an ISO week-based year. - Added `IsoYear`, a distinct int type to prevent bugs from confusing the week-based year and the regular year. - Added `initDateTime` in `times` to create a datetime from a weekday, and ISO 8601 week number and week-based year. - Added `getIsoWeekAndYear` in `times` to get an ISO week number along with the corresponding ISO week-based year from a datetime. - Added `getIsoWeeksInYear` in `times` to return the number of weeks in an ISO week-based year. -- Added [`Array.shift`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift) for JavaScript targets. - Added `std/oserrors` for OS error reporting. Added `std/envvars` for environment variables handling. - Added `sep` parameter in `std/uri` to specify the query separator. -- Removed deprecated `oids.oidToString`. -- Remove define `nimExperimentalAsyncjsThen` for `std/asyncjs.then` and `std/jsfetch`. - -- Changed mimedb to use an `OrderedTable` instead of `OrderedTableRef`, to use it in a const. -- Removed deprecated `jsre.test` and `jsre.toString`. -- Removed deprecated `math.c_frexp`. -- Removed deprecated ``` httpcore.`==` ```. -- Removed deprecated `std/dom_extensions`. -- Removed deprecated `std/posix.CMSG_SPACE` and `std/posix.CMSG_LEN` that takes wrong argument types. -- Remove deprecated `osproc.poDemon`, symbol with typo. +- Added [`Array.shift`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift) for JavaScript targets. - Added [`queueMicrotask`](https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask) for JavaScript targets. + +[//]: # "Deprecations:" - Deprecated `selfExe` for Nimscript. +[//]: # "Removals:" +- Removed deprecated `oids.oidToString`. +- Removed define `nimExperimentalAsyncjsThen` for `std/asyncjs.then` and `std/jsfetch`. +- Removed deprecated `jsre.test` and `jsre.toString`. +- Removed deprecated `math.c_frexp`. +- Removed deprecated `` httpcore.`==` ``. +- Removed deprecated `std/dom_extensions`. +- Removed deprecated `std/posix.CMSG_SPACE` and `std/posix.CMSG_LEN` that takes wrong argument types. +- Removed deprecated `osproc.poDemon`, symbol with typo. ## Language changes From 415689323c625afb7d39d923c4165a541beb9927 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Thu, 9 Jun 2022 00:17:25 +0800 Subject: [PATCH 2939/3768] clarify how to restart CI (#19845) --- doc/contributing.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/contributing.rst b/doc/contributing.rst index d3b2770f3..0ca0d0cbc 100644 --- a/doc/contributing.rst +++ b/doc/contributing.rst @@ -539,6 +539,7 @@ Debugging CI failures, flaky tests, etc 2. If CI failure seems unrelated to your PR, it could be caused by a flaky test. File a bug for it if it isn't already reported. A PR push (or opening/closing PR) will re-trigger all CI jobs (even successful ones, which can be wasteful). Instead, + request collaboration from the Nim team. The Nim team should follow these instructions to only restart the jobs that failed: * Azure: if on your own fork, it's possible from inside azure console From 836b95c896087a3f24bedad9bc1bcb2c5db1c5a8 Mon Sep 17 00:00:00 2001 From: Antonis Geralis <43617260+planetis-m@users.noreply.github.com> Date: Thu, 9 Jun 2022 09:27:31 +0300 Subject: [PATCH 2940/3768] Deprecate std/sums (#18439) * Deprecate sums * Update changelog.md * Update lib/std/sums.nim * log * format * remove * Update changelog.md Co-authored-by: sandytypical <43030857+xflywind@users.noreply.github.com> --- changelog.md | 2 ++ lib/std/sums.nim | 2 ++ 2 files changed, 4 insertions(+) diff --git a/changelog.md b/changelog.md index 356ee9263..6bd3c99e3 100644 --- a/changelog.md +++ b/changelog.md @@ -11,6 +11,8 @@ or define your own `Math.trunc` polyfill using the [`emit` pragma](https://nim-lang.org/docs/manual.html#implementation-specific-pragmas-emit-pragma). Nim uses `Math.trunc` for the division and modulo operators for integers. +- Deprecated `std/sums`. + - Optional parameters in combination with `: body` syntax (RFC #405) are now opt-in via `experimental:flexibleOptionalParams`. diff --git a/lib/std/sums.nim b/lib/std/sums.nim index b68858ef7..a6ce1b85d 100644 --- a/lib/std/sums.nim +++ b/lib/std/sums.nim @@ -8,6 +8,8 @@ ## Accurate summation functions. +{.deprecated: "use the nimble package `sums` instead.".} + runnableExamples: import std/math From 25d89269eb7fe7ebed142f2d3136959db11c9f02 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 9 Jun 2022 14:21:18 +0200 Subject: [PATCH 2941/3768] give a better error message for some template expansions (#19871) --- compiler/semstmts.nim | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 3513fd767..0516d08ce 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -140,7 +140,9 @@ proc discardCheck(c: PContext, result: PNode, flags: TExprFlags) = n[0] = result elif result.typ.kind != tyError and c.config.cmd != cmdInteractive: var n = result - while n.kind in skipForDiscardable: n = n.lastSon + while n.kind in skipForDiscardable: + if n.kind == nkTryStmt: n = n[0] + else: n = n.lastSon var s = "expression '" & $n & "' is of type '" & result.typ.typeToString & "' and has to be used (or discarded)" if result.info.line != n.info.line or @@ -483,7 +485,7 @@ proc semVarMacroPragma(c: PContext, a: PNode, n: PNode): PNode = for i in 0 ..< pragmas.len: let it = pragmas[i] let key = if it.kind in nkPragmaCallKinds and it.len >= 1: it[0] else: it - + when false: let lhs = b[0] let clash = strTableGet(c.currentScope.symbols, lhs.ident) From 1972005439306a12088400ef338c40c3d3c2ce28 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Thu, 9 Jun 2022 22:51:17 +0800 Subject: [PATCH 2942/3768] fix #19862; make widestrs consistent between refc and orc (#19874) [backport] fix #19862; make widestrs consistent in refc and orc --- lib/system/widestrs.nim | 3 +++ tests/arc/t19862.nim | 13 +++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 tests/arc/t19862.nim diff --git a/lib/system/widestrs.nim b/lib/system/widestrs.nim index 8b08959b5..bb348fd67 100644 --- a/lib/system/widestrs.nim +++ b/lib/system/widestrs.nim @@ -224,3 +224,6 @@ when defined(nimv2): proc `$`*(s: WideCStringObj): string = $(s.data) + + proc len*(w: WideCStringObj): int {.inline.} = + len(w.data) diff --git a/tests/arc/t19862.nim b/tests/arc/t19862.nim new file mode 100644 index 000000000..f7146ec26 --- /dev/null +++ b/tests/arc/t19862.nim @@ -0,0 +1,13 @@ +discard """ + matrix: "--gc:refc; --gc:arc" +""" + +# bug #19862 +type NewString = object + +proc len(s: NewString): int = 10 + +converter toNewString(x: WideCStringObj): NewString = discard + +let w = newWideCString("test") +doAssert len(w) == 4 From eefca1b81f334a5ccdb7d180de5b6a9969f12793 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Fri, 10 Jun 2022 21:24:28 +0800 Subject: [PATCH 2943/3768] not generate initStackBottomWith in arc/orc [backport] (#19875) not generate initStackBottomWith in arc/orc --- compiler/cgen.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 8d24f30c5..862f31107 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -2094,7 +2094,7 @@ proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode) = discard cgsym(m, "rawWrite") # raise dependencies on behalf of genMainProc - if m.config.target.targetOS != osStandalone and m.config.selectedGC != gcNone: + if m.config.target.targetOS != osStandalone and m.config.selectedGC notin {gcNone, gcArc, gcOrc}: discard cgsym(m, "initStackBottomWith") if emulatedThreadVars(m.config) and m.config.target.targetOS != osStandalone: discard cgsym(m, "initThreadVarsEmulation") From 8cde6ba6bcf236478f1d5d5aa722977ce9723a57 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sat, 11 Jun 2022 02:32:27 +0800 Subject: [PATCH 2944/3768] remove noop option `gc:v2` (#19810) * remove noop option gc:v2 * changelog --- changelog.md | 2 ++ compiler/ccgstmts.nim | 2 +- compiler/ccgtypes.nim | 4 ++-- compiler/commands.nim | 2 -- compiler/options.nim | 1 - lib/system/sysstr.nim | 14 ++------------ 6 files changed, 7 insertions(+), 18 deletions(-) diff --git a/changelog.md b/changelog.md index 6bd3c99e3..98655eaa3 100644 --- a/changelog.md +++ b/changelog.md @@ -24,6 +24,8 @@ becomes an alias for `addr`. - io is about to move out of system; use `-d:nimPreviewSlimSystem` and import `std/syncio`. +- The `gc:v2` option is removed. + ## Standard library additions and changes [//]: # "Changes:" diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index 240fa55c8..e791318ee 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -16,7 +16,7 @@ const # above X strings a hash-switch for strings is generated proc getTraverseProc(p: BProc, v: PSym): Rope = - if p.config.selectedGC in {gcMarkAndSweep, gcHooks, gcV2, gcRefc} and + if p.config.selectedGC in {gcMarkAndSweep, gcHooks, gcRefc} and optOwnedRefs notin p.config.globalOptions and containsGarbageCollectedRef(v.loc.t): # we register a specialized marked proc here; this has the advantage diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index c5b4d3857..7c26db40d 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -1483,12 +1483,12 @@ proc genTypeInfoV1(m: BModule, t: PType; info: TLineInfo): Rope = genTupleInfo(m, x, x, result, info) of tySequence: genTypeInfoAux(m, t, t, result, info) - if m.config.selectedGC in {gcMarkAndSweep, gcRefc, gcV2, gcGo}: + if m.config.selectedGC in {gcMarkAndSweep, gcRefc, gcGo}: let markerProc = genTraverseProc(m, origType, sig) m.s[cfsTypeInit3].addf("$1.marker = $2;$n", [tiNameForHcr(m, result), markerProc]) of tyRef: genTypeInfoAux(m, t, t, result, info) - if m.config.selectedGC in {gcMarkAndSweep, gcRefc, gcV2, gcGo}: + if m.config.selectedGC in {gcMarkAndSweep, gcRefc, gcGo}: let markerProc = genTraverseProc(m, origType, sig) m.s[cfsTypeInit3].addf("$1.marker = $2;$n", [tiNameForHcr(m, result), markerProc]) of tyPtr, tyRange, tyUncheckedArray: genTypeInfoAux(m, t, t, result, info) diff --git a/compiler/commands.nim b/compiler/commands.nim index 2e08ad615..31e637aba 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -262,7 +262,6 @@ proc testCompileOptionArg*(conf: ConfigRef; switch, arg: string, info: TLineInfo of "go": result = conf.selectedGC == gcGo of "none": result = conf.selectedGC == gcNone of "stack", "regions": result = conf.selectedGC == gcRegions - of "v2", "generational": warningOptionNoop(arg) else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg) of "opt": case arg.normalize @@ -568,7 +567,6 @@ proc processMemoryManagementOption(switch, arg: string, pass: TCmdLinePass, unregisterArcOrc(conf) conf.selectedGC = gcRegions defineSymbol(conf.symbols, "gcregions") - of "v2": warningOptionNoop(arg) else: localError(conf, info, errNoneBoehmRefcExpectedButXFound % arg) proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; diff --git a/compiler/options.nim b/compiler/options.nim index 545dfb1d1..89fb66d5f 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -183,7 +183,6 @@ type gcMarkAndSweep = "markAndSweep" gcHooks = "hooks" gcRefc = "refc" - gcV2 = "v2" gcGo = "go" # gcRefc and the GCs that follow it use a write barrier, # as far as usesWriteBarrier() is concerned diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim index 49fff41e9..c4f0db718 100644 --- a/lib/system/sysstr.nim +++ b/lib/system/sysstr.nim @@ -304,20 +304,10 @@ proc setLengthSeq(seq: PGenericSeq, elemSize, elemAlign, newLen: int): PGenericS when not defined(boehmGC) and not defined(nogc) and not defined(gcMarkAndSweep) and not defined(gogc) and not defined(gcRegions): - when false: # deadcode: was used by `compileOption("gc", "v2")` + if ntfNoRefs notin extGetCellType(result).base.flags: for i in newLen..result.len-1: - let len0 = gch.tempStack.len forAllChildrenAux(dataPointer(result, elemAlign, elemSize, i), - extGetCellType(result).base, waPush) - let len1 = gch.tempStack.len - for i in len0 ..< len1: - doDecRef(gch.tempStack.d[i], LocalHeap, MaybeCyclic) - gch.tempStack.len = len0 - else: - if ntfNoRefs notin extGetCellType(result).base.flags: - for i in newLen..result.len-1: - forAllChildrenAux(dataPointer(result, elemAlign, elemSize, i), - extGetCellType(result).base, waZctDecRef) + extGetCellType(result).base, waZctDecRef) # XXX: zeroing out the memory can still result in crashes if a wiped-out # cell is aliased by another pointer (ie proc parameter or a let variable). From 1e5dd9022b0d86d63d616431a0f9959ca4c10f40 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sat, 11 Jun 2022 02:33:44 +0800 Subject: [PATCH 2945/3768] [js] add testcase for array indexDefect and remove todo (#19838) * remove unused opcSubstr * [js] add testcase for array indexDefect * Revert "remove unused opcSubstr" This reverts commit cb461f2545234d62c1e0b83318f3e5495c97de52. --- lib/system/jssys.nim | 4 ---- tests/js/tindexdefect.nim | 9 +++++++++ 2 files changed, 9 insertions(+), 4 deletions(-) create mode 100644 tests/js/tindexdefect.nim diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim index 00a4a8ab6..a31de0d86 100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -731,10 +731,6 @@ proc parseFloatNative(a: string): float = `result` = Number(`a2`); """ -#[ -xxx how come code like this doesn't give IndexDefect ? -let z = s[10000] == 'a' -]# proc nimParseBiggestFloat(s: string, number: var BiggestFloat, start: int): int {.compilerproc.} = var sign: bool var i = start diff --git a/tests/js/tindexdefect.nim b/tests/js/tindexdefect.nim new file mode 100644 index 000000000..37994ec2e --- /dev/null +++ b/tests/js/tindexdefect.nim @@ -0,0 +1,9 @@ +discard """ + outputsub: "unhandled exception: index 10000 not in 0 .. 0 [IndexDefect]" + exitcode: 1 + joinable: false +""" + +var s = ['a'] +let z = s[10000] == 'a' +echo z \ No newline at end of file From 6f4bacff67e2e219ef914e24d9f9aaf34a6d1eb1 Mon Sep 17 00:00:00 2001 From: quantimnot <54247259+quantimnot@users.noreply.github.com> Date: Fri, 10 Jun 2022 14:40:08 -0400 Subject: [PATCH 2946/3768] Extend and document compiler debugging utilities (#19841) * Add two debugutils procs that native debuggers can break on use to execute commands when code of interest is being compiled. * Add GDB and LLDB programs to disable and enable breakpoints and watchpoints when code of interest is being compiled. * Extend the `intern.rst` docs regarding debugging the compiler. Co-authored-by: quantimnot --- compiler/debugutils.nim | 16 +++ doc/intern.rst | 222 +++++++++++++++++++++++++++++++++------- doc/koch.rst | 3 + tools/compiler.gdb | 39 +++++++ tools/compiler.lldb | 40 ++++++++ 5 files changed, 281 insertions(+), 39 deletions(-) create mode 100644 tools/compiler.gdb create mode 100644 tools/compiler.lldb diff --git a/compiler/debugutils.nim b/compiler/debugutils.nim index d109d2121..adbb0517f 100644 --- a/compiler/debugutils.nim +++ b/compiler/debugutils.nim @@ -54,3 +54,19 @@ proc isCompilerDebug*(): bool = {.undef(nimCompilerDebug).} echo 'x' conf0.isDefined("nimCompilerDebug") + +proc enteringDebugSection*() {.exportc, dynlib.} = + ## Provides a way for native debuggers to enable breakpoints, watchpoints, etc + ## when code of interest is being compiled. + ## + ## Set your debugger to break on entering `nimCompilerIsEnteringDebugSection` + ## and then execute a desired command. + discard + +proc exitingDebugSection*() {.exportc, dynlib.} = + ## Provides a way for native debuggers to disable breakpoints, watchpoints, etc + ## when code of interest is no longer being compiled. + ## + ## Set your debugger to break on entering `exitingDebugSection` + ## and then execute a desired command. + discard diff --git a/doc/intern.rst b/doc/intern.rst index 4ca7eff20..9103c694c 100644 --- a/doc/intern.rst +++ b/doc/intern.rst @@ -79,55 +79,216 @@ Set the compilation timestamp with the `SOURCE_DATE_EPOCH` environment variable. koch boot # or `./build_all.sh` -Developing the compiler -======================= +Debugging the compiler +====================== -To create a new compiler for each run, use `koch temp`:cmd:\: + +Bisecting for regressions +------------------------- + +There are often times when there is a bug that is caused by a regression in the +compiler or stdlib. Bisecting the Nim repo commits is a usefull tool to identify +what commit introduced the regression. + +Even if it's not known whether a bug is caused by a regression, bisection can reduce +debugging time by ruling it out. If the bug is found to be a regression, then you +focus on the changes introduced by that one specific commit. + +`koch temp`:cmd: returns 125 as the exit code in case the compiler +compilation fails. This exit code tells `git bisect`:cmd: to skip the +current commit: .. code:: cmd - koch temp c test.nim + git bisect start bad-commit good-commit + git bisect run ./koch temp -r c test-source.nim -`koch temp`:cmd: creates a debug build of the compiler, which is useful -to create stacktraces for compiler debugging. +You can also bisect using custom options to build the compiler, for example if +you don't need a debug version of the compiler (which runs slower), you can replace +`./koch temp`:cmd: by explicit compilation command, see `Bootstrapping the compiler`_. -You can of course use GDB or Visual Studio to debug the -compiler (via `--debuginfo --lineDir:on`:option:). However, there -are also lots of procs that aid in debugging: +Building an instrumented compiler +--------------------------------- + +Considering that a useful method of debugging the compiler is inserting debug +logging, or changing code and then observing the outcome of a testcase, it is +fastest to build a compiler that is instrumented for debugging from an +existing release build. `koch temp`:cmd: provides a convenient method of doing +just that. + +By default running `koch temp`:cmd: will build a lean version of the compiler +with `-d:debug`:option: enabled. The compiler is written to `bin/nim_temp` by +default. A lean version of the compiler lacks JS and documentation generation. + +`bin/nim_temp` can be directly used to run testcases, or used with testament +with `testament --nim:bin/nim_temp r tests/category/tsometest`:cmd:. + +`koch temp`:cmd: will build the temporary compiler with the `-d:debug`:option: +enabled. Here are compiler options that are of interest for debugging: + +* `-d:debug`:option:\: enables `assert` statements and stacktraces and all + runtime checks +* `--opt:speed`:option:\: build with optimizations enabled +* `--debugger:native`:option:\: enables `--debuginfo --lineDir:on`:option: for using + a native debugger like GDB, LLDB or CDB +* `-d:nimDebug`:option: cause calls to `quit` to raise an assertion exception +* `-d:nimDebugUtils`:option:\: enables various debugging utilities; + see `compiler/debugutils` +* `-d:stacktraceMsgs -d:nimCompilerStacktraceHints`:option:\: adds some additional + stacktrace hints; see https://github.com/nim-lang/Nim/pull/13351 +* `-u:leanCompiler`:option:\: enable JS and doc generation + +Another method to build and run the compiler is directly through `koch`:cmd:\: + +.. code:: cmd + + koch temp [options] c test.nim + + # (will build with js support) + koch temp [options] js test.nim + + # (will build with doc support) + koch temp [options] doc test.nim + +Debug logging +------------- + +"Printf debugging" is still the most appropriate way to debug many problems +arising in compiler development. The typical usage of breakpoints to debug +the code is often less practical, because almost all of the code paths in the +compiler will be executed hundreds of times before a particular section of the +tested program is reached where the newly developed code must be activated. + +To work-around this problem, you'll typically introduce an if statement in the +compiler code detecting more precisely the conditions where the tested feature +is being used. One very common way to achieve this is to use the `mdbg` condition, +which will be true only in contexts, processing expressions and statements from +the currently compiled main module: .. code-block:: nim - # dealing with PNode: - echo renderTree(someNode) - debug(someNode) # some JSON representation + # inside some compiler module + if mdbg: + debug someAstNode - # dealing with PType: +Using the `isCompilerDebug`:nim: condition along with inserting some statements +into the testcase provides more granular logging: + +.. code-block:: nim + + # compilermodule.nim + if isCompilerDebug(): + debug someAstNode + + # testcase.nim + proc main = + {.define(nimCompilerDebug).} + let a = 2.5 * 3 + {.undef(nimCompilerDebug).} + +Logging can also be scoped to a specific filename as well. This will of course +match against every module with that name. + +.. code-block:: nim + + if `??`(conf, n.info, "module.nim"): + debug(n) + +The above examples also makes use of the `debug`:nim: proc, which is able to +print a human-readable form of an arbitrary AST tree. Other common ways to print +information about the internal compiler types include: + +.. code-block:: nim + + # pretty print PNode + + # pretty prints the Nim ast + echo renderTree(someNode) + + # pretty prints the Nim ast, but annotates symbol IDs + echo renderTree(someNode, {renderIds}) + + # pretty print ast as JSON + debug(someNode) + + # print as YAML + echo treeToYaml(config, someNode) + + + # pretty print PType + + # print type name echo typeToString(someType) + + # pretty print as JSON debug(someType) - # dealing with PSym: + # print as YAML + echo typeToYaml(config, someType) + + + # pretty print PSym + + # print the symbol's name echo symbol.name.s + + # pretty print as JSON debug(symbol) - # pretty prints the Nim ast, but annotates symbol IDs: - echo renderTree(someNode, {renderIds}) - if `??`(conf, n.info, "temp.nim"): - # only output when it comes from "temp.nim" - echo renderTree(n) - if `??`(conf, n.info, "temp.nim"): - # why does it process temp.nim here? - writeStackTrace() + # print as YAML + echo symToYaml(config, symbol) + + + # pretty print TLineInfo + lineInfoToStr(lineInfo) + + + # print the structure of any type + repr(someVar) + +Here are some other helpful utilities: + +.. code-block:: nim + + # how did execution reach this location? + writeStackTrace() These procs may not already be imported by the module you're editing. You can import them directly for debugging: .. code-block:: nim + from astalgo import debug from types import typeToString from renderer import renderTree from msgs import `??` +Native debugging +---------------- + +Stepping through the compiler with a native debugger is a very powerful tool to +both learn and debug it. However, there is still the need to constrain when +breakpoints are triggered. The same methods as in `Debug logging`_ can be applied +here when combined with calls to the debug helpers `enteringDebugSection()`:nim: +and `exitingDebugSection()`:nim:. + +#. Compile the temp compiler with `--debugger:native -d:nimDebugUtils`:option: +#. Set your desired breakpoints or watchpoints. +#. Configure your debugger: + * GDB: execute `source tools/compiler.gdb` at startup + * LLDB execute `command source tools/compiler.lldb` at startup +#. Use one of the scoping helpers like so: + +.. code-block:: nim + + if isCompilerDebug(): + enteringDebugSection() + else: + exitingDebugSection() + +A caveat of this method is that all breakpoints and watchpoints are enabled or +disabled. Also, due to a bug, only breakpoints can be constrained for LLDB. The compiler's architecture =========================== @@ -152,23 +313,6 @@ for the type definitions. The `macros `_ module contains many examples how the AST represents each syntactic structure. -Bisecting for regressions -========================= - -`koch temp`:cmd: returns 125 as the exit code in case the compiler -compilation fails. This exit code tells `git bisect`:cmd: to skip the -current commit: - -.. code:: cmd - - git bisect start bad-commit good-commit - git bisect run ./koch temp -r c test-source.nim - -You can also bisect using custom options to build the compiler, for example if -you don't need a debug version of the compiler (which runs slower), you can replace -`./koch temp`:cmd: by explicit compilation command, see `Bootstrapping the compiler`_. - - Runtimes ======== diff --git a/doc/koch.rst b/doc/koch.rst index b8d85dff4..91dd5d570 100644 --- a/doc/koch.rst +++ b/doc/koch.rst @@ -38,6 +38,9 @@ options: unless you are debugging the compiler. -d:nimUseLinenoise Use the linenoise library for interactive mode (not needed on Windows). +-d:leanCompiler Produce a compiler without JS codegen or + documentation generator in order to use less RAM + for bootstrapping. After compilation is finished you will hopefully end up with the nim compiler in the `bin` directory. You can add Nim's `bin` directory to diff --git a/tools/compiler.gdb b/tools/compiler.gdb new file mode 100644 index 000000000..c81f47152 --- /dev/null +++ b/tools/compiler.gdb @@ -0,0 +1,39 @@ +# create a breakpoint on `debugutils.enteringDebugSection` +define enable_enteringDebugSection + break -function enteringDebugSection + # run these commands once breakpoint enteringDebugSection is hit + command + # enable all breakpoints and watchpoints + enable + # continue execution + cont + end +end + +# create a breakpoint on `debugutils.exitingDebugSection` named exitingDebugSection +define enable_exitingDebugSection + break -function exitingDebugSection + # run these commands once breakpoint exitingDebugSection is hit + command + # disable all breakpoints and watchpoints + disable + # but enable the enteringDebugSection breakpoint + enable_enteringDebugSection + # continue execution + cont + end +end + +# some commands can't be set until the process is running, so set an entry breakpoint +break -function NimMain +# run these commands once breakpoint NimMain is hit +command + # disable all breakpoints and watchpoints + disable + # but enable the enteringDebugSection breakpoint + enable_enteringDebugSection + # no longer need this breakpoint + delete -function NimMain + # continue execution + cont +end diff --git a/tools/compiler.lldb b/tools/compiler.lldb new file mode 100644 index 000000000..e0b375055 --- /dev/null +++ b/tools/compiler.lldb @@ -0,0 +1,40 @@ +# create a breakpoint on `debugutils.enteringDebugSection` named enteringDebugSection +breakpoint set -n 'enteringDebugSection' -N enteringDebugSection +# run these commands once breakpoint enteringDebugSection is hit +breakpoint command add enteringDebugSection + # enable all breakpoints + breakpoint enable + # enable all watchpoints + # watchpoint enable # FIXME: not currently working for unknown reason + # continue execution + continue +DONE + +# create a breakpoint on `debugutils.exitingDebugSection` named exitingDebugSection +breakpoint set -n 'exitingDebugSection' -N exitingDebugSection +# run these commands once breakpoint exitingDebugSection is hit +breakpoint command add exitingDebugSection + # disable all breakpoints + breakpoint disable + # disable all watchpoints + # watchpoint disable # FIXME: not currently working for unknown reason + breakpoint enable enteringDebugSection + # continue execution + continue +DONE + +# some commands can't be set until the process is running, so set an entry breakpoint +breakpoint set -n NimMain -N NimMain +# run these commands once breakpoint NimMain is hit +breakpoint command add NimMain + # disable all breakpoints + breakpoint disable + # disable all watchpoints + # watchpoint disable # FIXME: not currently working for unknown reason + # enable the enteringDebugSection breakpoint though + breakpoint enable enteringDebugSection + # no longer need this breakpoint + breakpoint delete NimMain + # continue execution + continue +DONE From ac89eead8a40a1a8ee170a4650a1d47e203da14c Mon Sep 17 00:00:00 2001 From: Bung Date: Sat, 11 Jun 2022 02:40:34 +0800 Subject: [PATCH 2947/3768] test for #19046 (#19839) * test for #19046 * add threads flag --- tests/misc/t19046.nim | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 tests/misc/t19046.nim diff --git a/tests/misc/t19046.nim b/tests/misc/t19046.nim new file mode 100644 index 000000000..b3bfec7ae --- /dev/null +++ b/tests/misc/t19046.nim @@ -0,0 +1,19 @@ +discard """ + targets: "c cpp" + matrix: "--threads:on" + disabled: "win" + disabled: "osx" + action: compile +""" + +# bug #19046 + +import std/os + +var t: Thread[void] + +proc test = discard +proc main = + createThread(t, test) + pinToCpu(t, 1) +main() \ No newline at end of file From e2e663a14389500b07e2f6fcd5c231536bdf76a0 Mon Sep 17 00:00:00 2001 From: Carlo Capocasa Date: Sat, 11 Jun 2022 18:23:31 +0200 Subject: [PATCH 2948/3768] Friendlier error message with solution (#19880) * Add helpful suggestion, should always apply * mention var param limitation in async docs * Update compiler/lambdalifting.nim whoops thanks Co-authored-by: flywind <43030857+xflywind@users.noreply.github.com> Co-authored-by: flywind <43030857+xflywind@users.noreply.github.com> --- compiler/lambdalifting.nim | 3 ++- lib/pure/asyncdispatch.nim | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/compiler/lambdalifting.nim b/compiler/lambdalifting.nim index 4b7ca89cd..96edba8c8 100644 --- a/compiler/lambdalifting.nim +++ b/compiler/lambdalifting.nim @@ -288,7 +288,8 @@ proc markAsClosure(g: ModuleGraph; owner: PSym; n: PNode) = if illegalCapture(s): localError(g.config, n.info, ("'$1' is of type <$2> which cannot be captured as it would violate memory" & - " safety, declared here: $3; using '-d:nimNoLentIterators' helps in some cases") % + " safety, declared here: $3; using '-d:nimNoLentIterators' helps in some cases." & + " Consider using a which can be captured.") % [s.name.s, typeToString(s.typ), g.config$s.info]) elif not (owner.typ.callConv == ccClosure or owner.typ.callConv == ccNimCall and tfExplicitCallConv notin owner.typ.flags): localError(g.config, n.info, "illegal capture '$1' because '$2' has the calling convention: <$3>" % diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 755f59ff7..0d406b271 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -98,6 +98,10 @@ ## `await`. The following section shows different ways that you can handle ## exceptions in async procs. ## +## .. caution:: +## Procedures marked {.async.} do not support mutable parameters such +## as `var int`. References such as `ref int` should be used instead. +## ## Handling Exceptions ## ------------------- ## @@ -192,6 +196,7 @@ ## ================ ## ## * The effect system (`raises: []`) does not work with async procedures. +## * Mutable parameters are not supported by async procedures. ## ## ## Multiple async backend support From 8fa2c0b532be9fdfd7682e5a8cc846b60bd4ea6c Mon Sep 17 00:00:00 2001 From: Jake Leahy Date: Mon, 13 Jun 2022 16:03:40 +1000 Subject: [PATCH 2949/3768] Pass headers and body correctly to FetchOptions (#19884) [backport] * Pass headers to FetchOptions Don't pass body if method is HttpGet or HttpHead * Syntax fixes * Restart CI --- lib/std/jsfetch.nim | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/std/jsfetch.nim b/lib/std/jsfetch.nim index 034bb6332..7fe154325 100644 --- a/lib/std/jsfetch.nim +++ b/lib/std/jsfetch.nim @@ -90,8 +90,9 @@ func newfetchOptions*(metod: HttpMethod; body: cstring; headers: Headers = newHeaders()): FetchOptions = ## Constructor for `FetchOptions`. result = FetchOptions( - body: body, mode: cstring($mode), credentials: cstring($credentials), cache: cstring($cache), referrerPolicy: cstring($referrerPolicy), - keepalive: keepalive, redirect: cstring($redirect), referrer: referrer, integrity: integrity, + body: if metod notin {HttpHead, HttpGet}: body else: nil, + mode: cstring($mode), credentials: cstring($credentials), cache: cstring($cache), referrerPolicy: cstring($referrerPolicy), + keepalive: keepalive, redirect: cstring($redirect), referrer: referrer, integrity: integrity, headers: headers, metod: (case metod of HttpHead: "HEAD".cstring of HttpGet: "GET".cstring From 2f4900615ab0c42c9608910dd30a14be52455f91 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 13 Jun 2022 14:10:40 +0800 Subject: [PATCH 2950/3768] [cleanup] remove unnecessary procs in vm (#19888) remove unused procs --- compiler/vm.nim | 1 - compiler/vmgen.nim | 7 ++----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/compiler/vm.nim b/compiler/vm.nim index bbcff77ba..1b11cb4f3 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -2252,7 +2252,6 @@ proc setupMacroParam(x: PNode, typ: PType): TFullReg = else: var n = x if n.kind in {nkHiddenSubConv, nkHiddenStdConv}: n = n[1] - n = n.canonValue n.flags.incl nfIsRef n.typ = x.typ result = TFullReg(kind: rkNode, node: n) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index dadcc3865..2e7f6d8b0 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -439,14 +439,11 @@ proc genAndOr(c: PCtx; n: PNode; opc: TOpcode; dest: var TDest) = c.gABC(n, opcAsgnInt, dest, tmp) freeTemp(c, tmp) -proc canonValue*(n: PNode): PNode = - result = n - proc rawGenLiteral(c: PCtx; n: PNode): int = result = c.constants.len #assert(n.kind != nkCall) n.flags.incl nfAllConst - c.constants.add n.canonValue + c.constants.add n internalAssert c.config, result < regBxMax proc sameConstant*(a, b: PNode): bool = @@ -1857,7 +1854,7 @@ proc genVarSection(c: PCtx; n: PNode) = else: let sa = getNullValue(s.typ, a.info, c.config) #if s.ast.isNil: getNullValue(s.typ, a.info) - #else: canonValue(s.ast) + #else: s.ast assert sa.kind != nkCall c.globals.add(sa) s.position = c.globals.len From ab477075860364db2690a0411c0b2c330532042f Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 13 Jun 2022 15:01:40 +0800 Subject: [PATCH 2951/3768] [semfold] fix #19199; properly fold uint to float conversion (#19890) [backport] fix #19199; properly fold float conversion --- compiler/semfold.nim | 2 +- tests/vm/t19199.nim | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 tests/vm/t19199.nim diff --git a/compiler/semfold.nim b/compiler/semfold.nim index d2bbc63be..72c8abaa9 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -409,7 +409,7 @@ proc foldConv(n, a: PNode; idgen: IdGenerator; g: ModuleGraph; check = false): P rangeCheck(n, getInt(result), g) of tyFloat..tyFloat64: case srcTyp.kind - of tyInt..tyInt64, tyEnum, tyBool, tyChar: + of tyInt..tyInt64, tyUInt..tyUInt64, tyEnum, tyBool, tyChar: result = newFloatNodeT(toFloat64(getOrdValue(a)), n, g) else: result = a diff --git a/tests/vm/t19199.nim b/tests/vm/t19199.nim new file mode 100644 index 000000000..6ae48cb54 --- /dev/null +++ b/tests/vm/t19199.nim @@ -0,0 +1,6 @@ +# bug #19199 +proc mikasa(x: float) = doAssert x == 42 + +static: + mikasa 42.uint.float +mikasa 42.uint.float From a4fdaa88ccfa03bf30113ffc194284fcb45d3054 Mon Sep 17 00:00:00 2001 From: Ardek Romak Date: Mon, 13 Jun 2022 10:22:20 +0300 Subject: [PATCH 2952/3768] Correctly import libcrypto functions using dynlib (#19881) --- lib/wrappers/openssl.nim | 72 +++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim index 00ec160cb..1db3fc239 100644 --- a/lib/wrappers/openssl.nim +++ b/lib/wrappers/openssl.nim @@ -93,6 +93,9 @@ else: import dynlib +{.pragma: lcrypto, cdecl, dynlib: DLLUtilName, importc.} +{.pragma: lssl, cdecl, dynlib: DLLSSLName, importc.} + type SslStruct {.final, pure.} = object SslPtr* = ptr SslStruct @@ -102,7 +105,6 @@ type PSTACK* = SslPtr PX509* = SslPtr PX509_NAME* = SslPtr - PEVP_MD* = SslPtr PBIO_METHOD* = SslPtr BIO* = SslPtr EVP_PKEY* = SslPtr @@ -684,48 +686,48 @@ proc RSA_free*(rsa: PRSA) {.cdecl, dynlib: DLLUtilName, importc.} proc RSA_size*(rsa: PRSA): cint {.cdecl, dynlib: DLLUtilName, importc.} # sha types -proc EVP_md_null*(): EVP_MD {.cdecl, importc.} -proc EVP_md2*(): EVP_MD {.cdecl, importc.} -proc EVP_md4*(): EVP_MD {.cdecl, importc.} -proc EVP_md5*(): EVP_MD {.cdecl, importc.} -proc EVP_sha*(): EVP_MD {.cdecl, importc.} -proc EVP_sha1*(): EVP_MD {.cdecl, importc.} -proc EVP_dss*(): EVP_MD {.cdecl, importc.} -proc EVP_dss1*(): EVP_MD {.cdecl, importc.} -proc EVP_ecdsa*(): EVP_MD {.cdecl, importc.} -proc EVP_sha224*(): EVP_MD {.cdecl, importc.} -proc EVP_sha256*(): EVP_MD {.cdecl, importc.} -proc EVP_sha384*(): EVP_MD {.cdecl, importc.} -proc EVP_sha512*(): EVP_MD {.cdecl, importc.} -proc EVP_mdc2*(): EVP_MD {.cdecl, importc.} -proc EVP_ripemd160*(): EVP_MD {.cdecl, importc.} -proc EVP_whirlpool*(): EVP_MD {.cdecl, importc.} -proc EVP_MD_size*(md: EVP_MD): cint {.cdecl, importc.} +proc EVP_md_null*(): EVP_MD {.lcrypto.} +proc EVP_md2*(): EVP_MD {.lcrypto.} +proc EVP_md4*(): EVP_MD {.lcrypto.} +proc EVP_md5*(): EVP_MD {.lcrypto.} +proc EVP_sha*(): EVP_MD {.lcrypto.} +proc EVP_sha1*(): EVP_MD {.lcrypto.} +proc EVP_dss*(): EVP_MD {.lcrypto.} +proc EVP_dss1*(): EVP_MD {.lcrypto.} +proc EVP_ecdsa*(): EVP_MD {.lcrypto.} +proc EVP_sha224*(): EVP_MD {.lcrypto.} +proc EVP_sha256*(): EVP_MD {.lcrypto.} +proc EVP_sha384*(): EVP_MD {.lcrypto.} +proc EVP_sha512*(): EVP_MD {.lcrypto.} +proc EVP_mdc2*(): EVP_MD {.lcrypto.} +proc EVP_ripemd160*(): EVP_MD {.lcrypto.} +proc EVP_whirlpool*(): EVP_MD {.lcrypto.} +proc EVP_MD_size*(md: EVP_MD): cint {.lcrypto.} # hmac functions -proc HMAC*(evp_md: EVP_MD; key: pointer; key_len: cint; d: cstring; n: csize_t; md: cstring; md_len: ptr cuint): cstring {.cdecl, importc.} +proc HMAC*(evp_md: EVP_MD; key: pointer; key_len: cint; d: cstring; n: csize_t; md: cstring; md_len: ptr cuint): cstring {.lcrypto.} # RSA key functions -proc PEM_read_bio_PrivateKey*(bp: BIO, x: ptr EVP_PKEY, cb: pointer, u: pointer): EVP_PKEY {.cdecl, importc.} -proc EVP_PKEY_free*(p: EVP_PKEY) {.cdecl, importc.} -proc EVP_DigestSignInit*(ctx: EVP_MD_CTX, pctx: ptr EVP_PKEY_CTX, typ: EVP_MD, e: ENGINE, pkey: EVP_PKEY): cint {.cdecl, importc.} -proc EVP_DigestInit_ex*(ctx: EVP_MD_CTX, typ: PEVP_MD, engine: SslPtr = nil): cint {.cdecl, importc.} -proc EVP_DigestUpdate*(ctx: EVP_MD_CTX, data: pointer, len: cuint): cint {.cdecl, importc.} -proc EVP_DigestFinal_ex*(ctx: EVP_MD_CTX, buffer: pointer, size: ptr cuint): cint {.cdecl, importc.} -proc EVP_DigestSignFinal*(ctx: EVP_MD_CTX, data: pointer, len: ptr csize_t): cint {.cdecl, importc.} -proc EVP_PKEY_CTX_new*(pkey: EVP_PKEY, e: ENGINE): EVP_PKEY_CTX {.cdecl, importc.} -proc EVP_PKEY_CTX_free*(pkeyCtx: EVP_PKEY_CTX) {.cdecl, importc.} -proc EVP_PKEY_sign_init*(c: EVP_PKEY_CTX): cint {.cdecl, importc.} +proc PEM_read_bio_PrivateKey*(bp: BIO, x: ptr EVP_PKEY, cb: pointer, u: pointer): EVP_PKEY {.lcrypto.} +proc EVP_PKEY_free*(p: EVP_PKEY) {.lcrypto.} +proc EVP_DigestSignInit*(ctx: EVP_MD_CTX, pctx: ptr EVP_PKEY_CTX, typ: EVP_MD, e: ENGINE, pkey: EVP_PKEY): cint {.lcrypto.} +proc EVP_DigestInit_ex*(ctx: EVP_MD_CTX, typ: EVP_MD, engine: SslPtr = nil): cint {.lcrypto.} +proc EVP_DigestUpdate*(ctx: EVP_MD_CTX, data: pointer, len: cuint): cint {.lcrypto.} +proc EVP_DigestFinal_ex*(ctx: EVP_MD_CTX, buffer: pointer, size: ptr cuint): cint {.lcrypto.} +proc EVP_DigestSignFinal*(ctx: EVP_MD_CTX, data: pointer, len: ptr csize_t): cint {.lcrypto.} +proc EVP_PKEY_CTX_new*(pkey: EVP_PKEY, e: ENGINE): EVP_PKEY_CTX {.lcrypto.} +proc EVP_PKEY_CTX_free*(pkeyCtx: EVP_PKEY_CTX) {.lcrypto.} +proc EVP_PKEY_sign_init*(c: EVP_PKEY_CTX): cint {.lcrypto.} when defined(macosx) or defined(windows): - proc EVP_MD_CTX_create*(): EVP_MD_CTX {.cdecl, importc.} - proc EVP_MD_CTX_destroy*(ctx: EVP_MD_CTX) {.cdecl, importc.} - proc EVP_MD_CTX_cleanup*(ctx: EVP_MD_CTX): cint {.cdecl, importc.} + proc EVP_MD_CTX_create*(): EVP_MD_CTX {.lcrypto.} + proc EVP_MD_CTX_destroy*(ctx: EVP_MD_CTX) {.lcrypto.} + proc EVP_MD_CTX_cleanup*(ctx: EVP_MD_CTX): cint {.lcrypto.} else: # some times you will need this instead: - proc EVP_MD_CTX_create*(): EVP_MD_CTX {.cdecl, importc: "EVP_MD_CTX_new".} - proc EVP_MD_CTX_destroy*(ctx: EVP_MD_CTX) {.cdecl, importc: "EVP_MD_CTX_free".} - proc EVP_MD_CTX_cleanup*(ctx: EVP_MD_CTX): cint {.cdecl, importc: "EVP_MD_CTX_cleanup".} + proc EVP_MD_CTX_create*(): EVP_MD_CTX {.cdecl, importc: "EVP_MD_CTX_new", dynlib: DLLUtilName.} + proc EVP_MD_CTX_destroy*(ctx: EVP_MD_CTX) {.cdecl, importc: "EVP_MD_CTX_free", dynlib: DLLUtilName.} + proc EVP_MD_CTX_cleanup*(ctx: EVP_MD_CTX): cint {.cdecl, importc: "EVP_MD_CTX_cleanup", dynlib: DLLUtilName.} # type From b41226001ce596fe520fd326a302c6f4e91c2504 Mon Sep 17 00:00:00 2001 From: Ivan Yonchovski Date: Mon, 13 Jun 2022 12:33:44 +0300 Subject: [PATCH 2953/3768] Initial implementation of nimsuggest v3 (#19826) * Initial implementation of nimsuggest v3 Rework `nimsuggest` to use caching to make usage of ide commands more efficient. Previously, all commands no matter what the state of the process is were causing clean build. In the context of Language Server Protocol(LSP) and lsp clients this was causing perf issues and overall instability. Overall, the goal of v3 is to fit to LSP Server needs - added two new commands: - `recompile` to do clean compilation - `changed` which can be used by the IDEs to notify that a particular file has been changed. The later can be utilized when using LSP file watches. - `globalSymbols` - searching global references - added `segfaults` dependency to allow fallback to clean build when incremental fails. I wish the error to be propagated to the client so we can work on fixing the incremental build failures (typically hitting pointer) - more efficient rebuild flow. ATM incremental rebuild is triggered when the command needs that(i. e. it is global) while the commands that work on the current source rebuild only it Things missing in this PR: - Documentation - Extensive unit testing. Although functional I still see this more as a POC that this approach can work. Next steps: - Implement `sug` request. - Rework/extend the protocol to allow better client/server communication. Ideally we will need push events, diagnostics should be restructored to allow per file notifications, etc. - implement v3 test suite. - better logging * Add tests for v3 and implement ideSug * Remove typeInstCache/procInstCache cleanup * Add ideChkFile command * Avoid contains call when adding symbol info * Remove log * Remove segfaults --- compiler/modulegraphs.nim | 82 ++++++++++--- compiler/options.nim | 13 +- compiler/passes.nim | 7 +- compiler/suggest.nim | 12 +- nimsuggest/nimsuggest.nim | 242 +++++++++++++++++++++++++++++++++++--- nimsuggest/tester.nim | 5 +- nimsuggest/tests/tv3.nim | 25 ++++ 7 files changed, 349 insertions(+), 37 deletions(-) create mode 100644 nimsuggest/tests/tv3.nim diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index 147381910..8294d863e 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -11,7 +11,7 @@ ## represents a complete Nim project. Single modules can either be kept in RAM ## or stored in a rod-file. -import intsets, tables, hashes, md5_old +import intsets, tables, hashes, md5_old, sequtils import ast, astalgo, options, lineinfos,idents, btrees, ropes, msgs, pathutils, packages import ic / [packed_ast, ic] @@ -83,6 +83,8 @@ type doStopCompile*: proc(): bool {.closure.} usageSym*: PSym # for nimsuggest owners*: seq[PSym] + suggestSymbols*: Table[FileIndex, seq[tuple[sym: PSym, info: TLineInfo]]] + suggestErrors*: Table[FileIndex, seq[Suggest]] methods*: seq[tuple[methods: seq[PSym], dispatcher: PSym]] # needs serialization! systemModule*: PSym sysTypes*: array[TTypeKind, PType] @@ -385,9 +387,19 @@ when defined(nimfind): c.graph.onDefinitionResolveForward(c.graph, s, info) else: - template onUse*(info: TLineInfo; s: PSym) = discard - template onDef*(info: TLineInfo; s: PSym) = discard - template onDefResolveForward*(info: TLineInfo; s: PSym) = discard + when defined(nimsuggest): + template onUse*(info: TLineInfo; s: PSym) = discard + + template onDef*(info: TLineInfo; s: PSym) = + let c = getPContext() + if c.graph.config.suggestVersion == 3: + suggestSym(c.graph, info, s, c.graph.usageSym) + + template onDefResolveForward*(info: TLineInfo; s: PSym) = discard + else: + template onUse*(info: TLineInfo; s: PSym) = discard + template onDef*(info: TLineInfo; s: PSym) = discard + template onDefResolveForward*(info: TLineInfo; s: PSym) = discard proc stopCompile*(g: ModuleGraph): bool {.inline.} = result = g.doStopCompile != nil and g.doStopCompile() @@ -434,8 +446,7 @@ proc initOperators*(g: ModuleGraph): Operators = result.opNot = createMagic(g, "not", mNot) result.opContains = createMagic(g, "contains", mInSet) -proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph = - result = ModuleGraph() +proc initModuleGraphFields(result: ModuleGraph) = # A module ID of -1 means that the symbol is not attached to a module at all, # but to the module graph: result.idgen = IdGenerator(module: -1'i32, symId: 0'i32, typeId: 0'i32) @@ -445,9 +456,9 @@ proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph = result.ifaces = @[] result.importStack = @[] result.inclToMod = initTable[FileIndex, FileIndex]() - result.config = config - result.cache = cache result.owners = @[] + result.suggestSymbols = initTable[FileIndex, seq[tuple[sym: PSym, info: TLineInfo]]]() + result.suggestErrors = initTable[FileIndex, seq[Suggest]]() result.methods = @[] initStrTable(result.compilerprocs) initStrTable(result.exposed) @@ -461,6 +472,12 @@ proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph = result.operators = initOperators(result) result.emittedTypeInfo = initTable[string, FileIndex]() +proc newModuleGraph*(cache: IdentCache; config: ConfigRef): ModuleGraph = + result = ModuleGraph() + result.config = config + result.cache = cache + initModuleGraphFields(result) + proc resetAllModules*(g: ModuleGraph) = initStrTable(g.packageSyms) g.deps = initIntSet() @@ -472,6 +489,7 @@ proc resetAllModules*(g: ModuleGraph) = g.methods = @[] initStrTable(g.compilerprocs) initStrTable(g.exposed) + initModuleGraphFields(g) proc getModule*(g: ModuleGraph; fileIdx: FileIndex): PSym = if fileIdx.int32 >= 0: @@ -550,7 +568,19 @@ proc transitiveClosure(g: var IntSet; n: int) = proc markDirty*(g: ModuleGraph; fileIdx: FileIndex) = let m = g.getModule fileIdx - if m != nil: incl m.flags, sfDirty + if m != nil: + g.suggestSymbols.del(fileIdx) + g.suggestErrors.del(fileIdx) + incl m.flags, sfDirty + +proc unmarkAllDirty*(g: ModuleGraph) = + for i in 0i32.. 100: + break + else: + myLog fmt "Discarding {cmd}" + +# v3 end when isMainModule: handleCmdLine(newIdentCache(), newConfigRef()) else: @@ -726,8 +939,9 @@ else: if self.loadConfigsAndProcessCmdLine(cache, conf, graph): mockCommand(graph) if gLogging: + log("Search paths:") for it in conf.searchPaths: - log(it.string) + log(" " & it.string) retval.doStopCompile = proc (): bool = false return NimSuggest(graph: retval, idle: 0, cachedMsgs: @[]) diff --git a/nimsuggest/tester.nim b/nimsuggest/tester.nim index 1db33706a..fea0a8d45 100644 --- a/nimsuggest/tester.nim +++ b/nimsuggest/tester.nim @@ -252,7 +252,10 @@ proc runEpcTest(filename: string): int = for cmd in s.startup: if not runCmd(cmd, s.dest): quit "invalid command: " & cmd - let epccmd = s.cmd.replace("--tester", "--epc --v2 --log") + let epccmd = if s.cmd.contains("--v3"): + s.cmd.replace("--tester", "--epc --log") + else: + s.cmd.replace("--tester", "--epc --v2 --log") let cl = parseCmdLine(epccmd) var p = startProcess(command=cl[0], args=cl[1 .. ^1], options={poStdErrToStdOut, poUsePath, diff --git a/nimsuggest/tests/tv3.nim b/nimsuggest/tests/tv3.nim new file mode 100644 index 000000000..99caa987b --- /dev/null +++ b/nimsuggest/tests/tv3.nim @@ -0,0 +1,25 @@ +# tests v3 + +type + Foo* = ref object of RootObj + bar*: string + +proc test(f: Foo) = + echo f.ba#[!]#r + +discard """ +$nimsuggest --v3 --tester $file +>use $1 +def skField tv3.Foo.bar string $file 5 4 "" 100 +use skField tv3.Foo.bar string $file 8 9 "" 100 +>def $1 +def skField tv3.Foo.bar string $file 5 4 "" 100 +>outline $1 +outline skType tv3.Foo Foo $file 4 2 "" 100 +outline skField tv3.Foo.bar string $file 5 4 "" 100 +outline skProc tv3.test proc (f: Foo){.gcsafe, locks: 0.} $file 7 5 "" 100 +>sug $1 +sug skField bar string $file 5 4 "" 100 Prefix +>globalSymbols test +def skProc tv3.test proc (f: Foo){.gcsafe, locks: 0.} $file 7 5 "" 100 +""" From 251bdc1d5aed98fccb31677dda3c3ead9be2f6bf Mon Sep 17 00:00:00 2001 From: Tanguy Date: Tue, 14 Jun 2022 12:37:31 +0200 Subject: [PATCH 2954/3768] Windows: enable nimRawSetjmp by default [backport] (#19891) * Windows: enable nimRawSetjmp by default See #19197. The default setjmp can randomly segfault on windows * Attempt to disable the flag for bootstraping * Disable styleCheck for c_setjmp --- config/config.nims | 5 +++++ lib/system/ansi_c.nim | 7 +++++++ 2 files changed, 12 insertions(+) diff --git a/config/config.nims b/config/config.nims index 08d9d9f55..b50181eaf 100644 --- a/config/config.nims +++ b/config/config.nims @@ -20,5 +20,10 @@ when defined(nimStrictMode): switch("hintAsError", "ConvFromXtoItselfNotNeeded") # future work: XDeclaredButNotUsed +when defined(windows) and not defined(booting): + # Avoid some rare stack corruption while using exceptions with a SEH-enabled + # toolchain: https://github.com/nim-lang/Nim/pull/19197 + switch("define", "nimRawSetjmp") + switch("define", "nimVersion:" & NimVersion) switch("define", "nimPreviewDotLikeOps") diff --git a/lib/system/ansi_c.nim b/lib/system/ansi_c.nim index 23fb9fdef..5d0ecc01c 100644 --- a/lib/system/ansi_c.nim +++ b/lib/system/ansi_c.nim @@ -116,6 +116,7 @@ elif defined(nimBuiltinSetjmp): proc c_builtin_setjmp(jmpb: ptr pointer): cint {. importc: "__builtin_setjmp", nodecl.} c_builtin_setjmp(unsafeAddr jmpb[0]) + elif defined(nimRawSetjmp) and not defined(nimStdSetjmp): when defined(windows): # No `_longjmp()` on Windows. @@ -127,10 +128,16 @@ elif defined(nimRawSetjmp) and not defined(nimStdSetjmp): # prone to stack corruption during unwinding, so we disable that by setting # it to NULL. # More details: https://github.com/status-im/nimbus-eth2/issues/3121 + when defined(nimHasStyleChecks): + {.push styleChecks: off.} + proc c_setjmp*(jmpb: C_JmpBuf): cint = proc c_setjmp_win(jmpb: C_JmpBuf, ctx: pointer): cint {. header: "", importc: "_setjmp".} c_setjmp_win(jmpb, nil) + + when defined(nimHasStyleChecks): + {.pop.} else: proc c_longjmp*(jmpb: C_JmpBuf, retval: cint) {. header: "", importc: "_longjmp".} From 789b1bcbb60278b335a81df416be1d5a663d7e30 Mon Sep 17 00:00:00 2001 From: Don-Duong Quach Date: Tue, 14 Jun 2022 23:20:34 -0700 Subject: [PATCH 2955/3768] Fixes #19883 so genscript works with vcc (#19893) --- compiler/extccomp.nim | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 515c7ba29..5c074d284 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -434,7 +434,13 @@ proc noAbsolutePaths(conf: ConfigRef): bool {.inline.} = # really: Cross compilation from Linux to Linux for example is entirely # reasonable. # `optGenMapping` is included here for niminst. - result = conf.globalOptions * {optGenScript, optGenMapping} != {} + # We use absolute paths for vcc / cl, see issue #19883. + let options = + if conf.cCompiler == ccVcc: + {optGenMapping} + else: + {optGenScript, optGenMapping} + result = conf.globalOptions * options != {} proc cFileSpecificOptions(conf: ConfigRef; nimname, fullNimFile: string): string = result = conf.compileOptions @@ -921,7 +927,7 @@ proc callCCompiler*(conf: ConfigRef) = objfiles.add(' ') objfiles.add(quoteShell(objFile)) let mainOutput = if optGenScript notin conf.globalOptions: conf.prepareToWriteOutput - else: AbsoluteFile(conf.projectName) + else: AbsoluteFile(conf.outFile) linkCmd = getLinkCmd(conf, mainOutput, objfiles, removeStaticFile = true) extraCmds = getExtraCmds(conf, mainOutput) From e7e8f437c4f95f4f5d038cdddf5036462733288a Mon Sep 17 00:00:00 2001 From: JJ <35242550+j-james@users.noreply.github.com> Date: Wed, 15 Jun 2022 06:40:56 -0700 Subject: [PATCH 2956/3768] Keep the doc sidebar on the screen while scrolling (#19851) * [docgen] Group sidebar sections into
      (open by default) * [docgen] Consistent indentation in generated HTML (this is a boon for working on docgen's html/css output) * [docgen] Move Source/Edit buttons inside main div This makes styling the documentation significantly easier. * [docgen] Somewhat consistent CSS formatting * [docgen] Keep the sidebar onscreen while scrolling * [docgen] Tweak CSS for the sticky sidebar * [docgen] search type=text ==> type=search * [docgen] Update expected doc output * [docgen] Fix Group by Type sidebar placement bug * [docgen] Curse you, whitespace (fix tests) * [docgen] Fix rst2html tests Co-authored-by: sandytypical <43030857+xflywind@users.noreply.github.com> --- config/nimdoc.cfg | 216 ++- doc/nimdoc.css | 164 +- nimdoc/rst2html/expected/rst_examples.html | 85 +- .../expected/index.html | 122 +- .../expected/theindex.html | 24 +- nimdoc/testproject/expected/nimdoc.out.css | 164 +- .../expected/subdir/subdir_b/utils.html | 758 ++++----- nimdoc/testproject/expected/testproject.html | 1454 ++++++++--------- nimdoc/testproject/expected/theindex.html | 24 +- 9 files changed, 1401 insertions(+), 1610 deletions(-) diff --git a/config/nimdoc.cfg b/config/nimdoc.cfg index 4efa1f637..725f9e0a5 100644 --- a/config/nimdoc.cfg +++ b/config/nimdoc.cfg @@ -9,25 +9,28 @@ split.item.toc = "20" doc.section = """
      -

      $sectionTitle

      -
      -$content -
      +

      $sectionTitle

      +
      + $content +
      +
      """ doc.section.toc = """
    • - $sectionTitle -
        - $content -
      +
      + $sectionTitle +
        + $content +
      +
    • """ doc.section.toc2 = """ -
        $plainName - $content -
      +
        $plainName + $content +
      """ # Chunk of HTML emitted for each entry in the HTML table of contents. @@ -47,12 +50,12 @@ doc.section.toc2 = """ doc.item = """
      -
      $header
      -
      -$deprecationMsg -$desc -$seeSrc -
      +
      $header
      +
      + $deprecationMsg + $desc + $seeSrc +
      """ @@ -61,9 +64,8 @@ $seeSrc # * $overloadGroupName - the anchor for this whole group # * $content - string containing `doc.item`s themselves doc.item2 = """ -
      -$content + $content
      """ @@ -73,18 +75,14 @@ $content # This is used for TOC items which are not overloadable (e.g. types). # `$header_plain` would be too verbose here, so we use $name. doc.item.toc = """ -
    • $name
    • +
    • $name
    • """ # This is used for TOC items which are grouped by the same name (e.g. procs). doc.item.tocTable = """ -
    • $header_plain
    • +
    • $header_plain
    • """ - - # HTML rendered for doc.item's seeSrc variable. Note that this will render to # the empty string if you don't pass anything through --git.url. Available # substitutaion variables here are: @@ -94,32 +92,31 @@ doc.item.tocTable = """ # * $line: line of the item in the original source file. # * $url: whatever you did pass through the --git.url switch (which also # gets variables path/line replaced!) -doc.item.seesrc = """  Source -  Edit +doc.item.seesrc = """ +Source   +Edit   """ doc.deprecationmsg = """ -
      - $label $message -
      +
      + $label $message +
      """ doc.toc = """
        -$content + $content
      """ doc.body_toc_groupsection = """ -
      - Group by: - -
      +
      + Group by: + +
      """ @if boot: @@ -130,36 +127,36 @@ doc.body_toc_groupsection = """ doc.body_toc_group = """
      -
      - -     Dark Mode +
      + +     Dark Mode +
      + +
      + Search: +
      + $body_toc_groupsection +
      + $tableofcontents
      - -
      - Search: -
      - $body_toc_groupsection - $tableofcontents -
      - $seeSrc
      -
      - $deprecationMsg -

      $moduledesc

      - $content + $seeSrc + $deprecationMsg +

      $moduledesc

      + $content
      """ @@ -169,39 +166,36 @@ doc.body_toc_group = """ doc.body_toc_group = """
      -
      - -     Dark Mode +
      + +     Dark Mode +
      + +
      + Search: +
      +
      + Group by: + +
      +
      + $tableofcontents
      - -
      - Search: -
      -
      - Group by: - -
      - $tableofcontents -
      - $seeSrc
      -
      - $deprecationMsg -

      $moduledesc

      - $content + $seeSrc + $deprecationMsg +

      $moduledesc

      + $content
      """ @@ -220,14 +214,13 @@ doc.listing_end = "
    " # * $analytics: Google analytics location, includes - -
    -
    -

    $title

    $subtitle - $content -
    +
    +
    +

    $title

    $subtitle + $content
    -
    -$analytics + $analytics """ diff --git a/doc/nimdoc.css b/doc/nimdoc.css index 0014cf196..e72c4a213 100644 --- a/doc/nimdoc.css +++ b/doc/nimdoc.css @@ -159,24 +159,28 @@ body { padding: 0; box-sizing: border-box; } -.column, -.columns { +.column, .columns { width: 100%; float: left; box-sizing: border-box; - margin-left: 1%; -} + margin-left: 1%; } -.column:first-child, -.columns:first-child { +.column:first-child, .columns:first-child { margin-left: 0; } +.container .row { + display: flex; } + .three.columns { - width: 22%; -} + width: 25.0%; + height: 100vh; + position: sticky; + top: 0px; + overflow-y: auto; } .nine.columns { - width: 77.0%; } + width: 75.0%; + padding-left: 1.5em; } .twelve.columns { width: 100%; @@ -269,25 +273,26 @@ a.nimdoc { a.toc-backref { text-decoration: none; - color: var(--text); } + color: var(--text); +} a.link-seesrc { color: #607c9f; font-size: 0.9em; - font-style: italic; } + font-style: italic; +} -a:hover, -a:focus { +a:hover, a:focus { color: var(--anchor-focus); - text-decoration: underline; } + text-decoration: underline; +} a:hover span.Identifier { color: var(--anchor); } -sub, -sup { +sub, sup { position: relative; font-size: 75%; line-height: 0; @@ -314,8 +319,7 @@ img { background: transparent !important; box-shadow: none !important; } - a, - a:visited { + a, a:visited { text-decoration: underline; } a[href]:after { @@ -329,16 +333,14 @@ img { a[href^="#"]:after { content: ""; } - pre, - blockquote { + pre, blockquote { border: 1px solid #999; page-break-inside: avoid; } thead { display: table-header-group; } - tr, - img { + tr, img { page-break-inside: avoid; } img { @@ -353,22 +355,18 @@ img { h1.title { page-break-before: avoid; } - p, - h2, - h3 { + p, h2, h3 { orphans: 3; widows: 3; } - h2, - h3 { + h2, h3 { page-break-after: avoid; } } p { margin-top: 0.5em; - margin-bottom: 0.5em; -} + margin-bottom: 0.5em; } small { font-size: 85%; } @@ -376,8 +374,7 @@ small { strong { font-weight: 600; font-size: 0.95em; - color: var(--strong); -} + color: var(--strong); } em { font-style: italic; } @@ -398,8 +395,7 @@ h1.title { text-align: center; font-weight: 900; margin-top: 0.75em; - margin-bottom: 0em; -} + margin-bottom: 0em; } h2 { font-size: 1.3em; @@ -426,36 +422,29 @@ h6 { font-size: 1.1em; } -ul, -ol { +ul, ol { padding: 0; margin-top: 0.5em; margin-left: 0.75em; } -ul ul, -ul ol, -ol ol, -ol ul { +ul ul, ul ol, ol ol, ol ul { margin-bottom: 0; margin-left: 1.25em; } ul.simple > li { - list-style-type: circle; -} + list-style-type: circle; } ul.simple-boot li { - list-style-type: none; - margin-left: 0em; - margin-bottom: 0.5em; -} + list-style-type: none; + margin-left: 0em; + margin-bottom: 0.5em; } ol.simple > li, ul.simple > li { margin-bottom: 0.2em; margin-left: 0.4em } ul.simple.simple-toc > li { - margin-top: 1em; -} + margin-top: 1em; } ul.simple-toc { list-style: none; @@ -464,8 +453,7 @@ ul.simple-toc { margin-top: 1em; } ul.simple-toc > li { - list-style-type: none; -} + list-style-type: none; } ul.simple-toc-section { list-style-type: circle; @@ -475,12 +463,10 @@ ul.simple-toc-section { ul.nested-toc-section { list-style-type: circle; margin-left: -0.75em; - color: var(--text); -} + color: var(--text); } ul.nested-toc-section > li { - margin-left: 1.25em; -} + margin-left: 1.25em; } ol.arabic { @@ -527,7 +513,8 @@ hr.footnote { margin-top: 0.15em; } div.footnote-group { - margin-left: 1em; } + margin-left: 1em; +} div.footnote-label { display: inline-block; min-width: 1.7em; @@ -611,7 +598,7 @@ pre { border: 1px solid var(--border); -webkit-border-radius: 6px; -moz-border-radius: 6px; - border-radius: 6px; + border-radius: 6px; } .copyToClipBoardBtn { @@ -629,7 +616,7 @@ pre { .copyToClipBoard:hover .copyToClipBoardBtn { visibility: visible; -} +} .pre-scrollable { max-height: 340px; @@ -694,8 +681,8 @@ table th { font-weight: bold; } table th.docinfo-name { - background-color: transparent; - text-align: right; + background-color: transparent; + text-align: right; } table tr:hover { @@ -712,31 +699,31 @@ table.borderless td, table.borderless th { padding: 0 0.5em 0 0 !important; } .admonition { - padding: 0.3em; - background-color: var(--secondary-background); - border-left: 0.4em solid #7f7f84; - margin-bottom: 0.5em; - -webkit-box-shadow: 0 5px 8px -6px rgba(0,0,0,.2); - -moz-box-shadow: 0 5px 8px -6px rgba(0,0,0,.2); - box-shadow: 0 5px 8px -6px rgba(0,0,0,.2); + padding: 0.3em; + background-color: var(--secondary-background); + border-left: 0.4em solid #7f7f84; + margin-bottom: 0.5em; + -webkit-box-shadow: 0 5px 8px -6px rgba(0,0,0,.2); + -moz-box-shadow: 0 5px 8px -6px rgba(0,0,0,.2); + box-shadow: 0 5px 8px -6px rgba(0,0,0,.2); } .admonition-info { - border-color: var(--info-background); + border-color: var(--info-background); } .admonition-info-text { - color: var(--info-background); + color: var(--info-background); } .admonition-warning { - border-color: var(--warning-background); + border-color: var(--warning-background); } .admonition-warning-text { - color: var(--warning-background); + color: var(--warning-background); } .admonition-error { - border-color: var(--error-background); + border-color: var(--error-background); } .admonition-error-text { - color: var(--error-background); + color: var(--error-background); } .first { @@ -770,8 +757,7 @@ div.footer, div.header { font-size: smaller; } div.footer { - padding-top: 5em; -} + padding-top: 5em; } div.line-block { display: block; @@ -790,17 +776,14 @@ div.search_results { background-color: var(--third-background); margin: 3em; padding: 1em; - border: 1px solid #4d4d4d; -} + border: 1px solid #4d4d4d; } div#global-links ul { margin-left: 0; - list-style-type: none; -} + list-style-type: none; } div#global-links > simple-boot { - margin-left: 3em; -} + margin-left: 3em; } hr.docutils { width: 75%; } @@ -980,8 +963,7 @@ span.Directive { span.option { font-weight: bold; font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace; - color: var(--option); -} + color: var(--option); } span.Prompt { font-weight: bold; @@ -997,11 +979,10 @@ span.program { text-decoration: underline; text-decoration-color: var(--hint); text-decoration-thickness: 0.05em; - text-underline-offset: 0.15em; -} + text-underline-offset: 0.15em; } -span.Command, span.Rule, span.Hyperlink, span.Label, span.Reference, -span.Other { +span.Command, span.Rule, span.Hyperlink, +span.Label, span.Reference, span.Other { color: var(--other); } /* Pop type, const, proc, and iterator defs in nim def blocks */ @@ -1039,17 +1020,14 @@ span.pragmadots { border-radius: 4px; margin: 0 2px; cursor: pointer; - font-size: 0.8em; -} + font-size: 0.8em; } span.pragmadots:hover { - background-color: var(--hint); -} + background-color: var(--hint); } + span.pragmawrap { - display: none; -} + display: none; } span.attachedType { display: none; - visibility: hidden; -} + visibility: hidden; } diff --git a/nimdoc/rst2html/expected/rst_examples.html b/nimdoc/rst2html/expected/rst_examples.html index 2b5218d9f..532917055 100644 --- a/nimdoc/rst2html/expected/rst_examples.html +++ b/nimdoc/rst2html/expected/rst_examples.html @@ -1,12 +1,11 @@ - + - + - +Not a Nim Manual @@ -17,45 +16,42 @@ -Not a Nim Manual + - -
    -
    -

    Not a Nim Manual

    -
    +
    +
    +

    Not a Nim Manual

    +
    -
    - -     Dark Mode -
    - -
    - Search: -
    -
    - Group by: - -
    -
    -
    -
    - -

    + + +

    Authors:Andreas Rumpf, Zahary Karadjov
    Authors:Andreas Rumpf, Zahary Karadjov
    Version:|nimversion|

    "Complexity" seems to be a lot like "energy": you can transfer it from the end-user to one/some of the other players, but the total amount seems to remain pretty much constant for a given task. -- Ran

    @@ -262,19 +257,17 @@ stmt = IND{>} stmt ^+ IND{=} DED # list of statements F2 without pipe

    not in table

    - +
    -
    -
    - + diff --git a/nimdoc/test_out_index_dot_html/expected/index.html b/nimdoc/test_out_index_dot_html/expected/index.html index c6a116bd6..0c0dc2268 100644 --- a/nimdoc/test_out_index_dot_html/expected/index.html +++ b/nimdoc/test_out_index_dot_html/expected/index.html @@ -1,12 +1,11 @@ - + - + - +nimdoc/test_out_index_dot_html/foo @@ -17,94 +16,89 @@ -nimdoc/test_out_index_dot_html/foo + - -
    -
    -

    nimdoc/test_out_index_dot_html/foo

    -
    +
    +
    +

    nimdoc/test_out_index_dot_html/foo

    +
    -
    - -     Dark Mode -
    - -
    - Search: -
    -
    - Group by: - -
    -
      -
    • - Procs -
        +
        + +     Dark Mode +
        + +
        + Search: +
        +
        + Group by: + +
        +
        + +
      +
    -
    -
    - -

    -
    -

    Procs

    -
    - -
    -
    -
    proc foo() {....raises: [], tags: [].}
    -
    - -I do foo - -
    + + +

    +
    +

    Procs

    +
    +
    +
    +
    proc foo() {....raises: [], tags: [].}
    +
    + + I do foo + +
    -
    +
    +
    -
    -
    - + diff --git a/nimdoc/test_out_index_dot_html/expected/theindex.html b/nimdoc/test_out_index_dot_html/expected/theindex.html index 8ee62a330..00c81189e 100644 --- a/nimdoc/test_out_index_dot_html/expected/theindex.html +++ b/nimdoc/test_out_index_dot_html/expected/theindex.html @@ -1,12 +1,11 @@ - + - + - +Index @@ -17,31 +16,28 @@ -Index + - -
    -
    -

    Index

    - Modules: index.

    API symbols

    +
    +
    +

    Index

    + Modules: index.

    API symbols

    foo:
    -
    -
    - + diff --git a/nimdoc/testproject/expected/nimdoc.out.css b/nimdoc/testproject/expected/nimdoc.out.css index 0014cf196..e72c4a213 100644 --- a/nimdoc/testproject/expected/nimdoc.out.css +++ b/nimdoc/testproject/expected/nimdoc.out.css @@ -159,24 +159,28 @@ body { padding: 0; box-sizing: border-box; } -.column, -.columns { +.column, .columns { width: 100%; float: left; box-sizing: border-box; - margin-left: 1%; -} + margin-left: 1%; } -.column:first-child, -.columns:first-child { +.column:first-child, .columns:first-child { margin-left: 0; } +.container .row { + display: flex; } + .three.columns { - width: 22%; -} + width: 25.0%; + height: 100vh; + position: sticky; + top: 0px; + overflow-y: auto; } .nine.columns { - width: 77.0%; } + width: 75.0%; + padding-left: 1.5em; } .twelve.columns { width: 100%; @@ -269,25 +273,26 @@ a.nimdoc { a.toc-backref { text-decoration: none; - color: var(--text); } + color: var(--text); +} a.link-seesrc { color: #607c9f; font-size: 0.9em; - font-style: italic; } + font-style: italic; +} -a:hover, -a:focus { +a:hover, a:focus { color: var(--anchor-focus); - text-decoration: underline; } + text-decoration: underline; +} a:hover span.Identifier { color: var(--anchor); } -sub, -sup { +sub, sup { position: relative; font-size: 75%; line-height: 0; @@ -314,8 +319,7 @@ img { background: transparent !important; box-shadow: none !important; } - a, - a:visited { + a, a:visited { text-decoration: underline; } a[href]:after { @@ -329,16 +333,14 @@ img { a[href^="#"]:after { content: ""; } - pre, - blockquote { + pre, blockquote { border: 1px solid #999; page-break-inside: avoid; } thead { display: table-header-group; } - tr, - img { + tr, img { page-break-inside: avoid; } img { @@ -353,22 +355,18 @@ img { h1.title { page-break-before: avoid; } - p, - h2, - h3 { + p, h2, h3 { orphans: 3; widows: 3; } - h2, - h3 { + h2, h3 { page-break-after: avoid; } } p { margin-top: 0.5em; - margin-bottom: 0.5em; -} + margin-bottom: 0.5em; } small { font-size: 85%; } @@ -376,8 +374,7 @@ small { strong { font-weight: 600; font-size: 0.95em; - color: var(--strong); -} + color: var(--strong); } em { font-style: italic; } @@ -398,8 +395,7 @@ h1.title { text-align: center; font-weight: 900; margin-top: 0.75em; - margin-bottom: 0em; -} + margin-bottom: 0em; } h2 { font-size: 1.3em; @@ -426,36 +422,29 @@ h6 { font-size: 1.1em; } -ul, -ol { +ul, ol { padding: 0; margin-top: 0.5em; margin-left: 0.75em; } -ul ul, -ul ol, -ol ol, -ol ul { +ul ul, ul ol, ol ol, ol ul { margin-bottom: 0; margin-left: 1.25em; } ul.simple > li { - list-style-type: circle; -} + list-style-type: circle; } ul.simple-boot li { - list-style-type: none; - margin-left: 0em; - margin-bottom: 0.5em; -} + list-style-type: none; + margin-left: 0em; + margin-bottom: 0.5em; } ol.simple > li, ul.simple > li { margin-bottom: 0.2em; margin-left: 0.4em } ul.simple.simple-toc > li { - margin-top: 1em; -} + margin-top: 1em; } ul.simple-toc { list-style: none; @@ -464,8 +453,7 @@ ul.simple-toc { margin-top: 1em; } ul.simple-toc > li { - list-style-type: none; -} + list-style-type: none; } ul.simple-toc-section { list-style-type: circle; @@ -475,12 +463,10 @@ ul.simple-toc-section { ul.nested-toc-section { list-style-type: circle; margin-left: -0.75em; - color: var(--text); -} + color: var(--text); } ul.nested-toc-section > li { - margin-left: 1.25em; -} + margin-left: 1.25em; } ol.arabic { @@ -527,7 +513,8 @@ hr.footnote { margin-top: 0.15em; } div.footnote-group { - margin-left: 1em; } + margin-left: 1em; +} div.footnote-label { display: inline-block; min-width: 1.7em; @@ -611,7 +598,7 @@ pre { border: 1px solid var(--border); -webkit-border-radius: 6px; -moz-border-radius: 6px; - border-radius: 6px; + border-radius: 6px; } .copyToClipBoardBtn { @@ -629,7 +616,7 @@ pre { .copyToClipBoard:hover .copyToClipBoardBtn { visibility: visible; -} +} .pre-scrollable { max-height: 340px; @@ -694,8 +681,8 @@ table th { font-weight: bold; } table th.docinfo-name { - background-color: transparent; - text-align: right; + background-color: transparent; + text-align: right; } table tr:hover { @@ -712,31 +699,31 @@ table.borderless td, table.borderless th { padding: 0 0.5em 0 0 !important; } .admonition { - padding: 0.3em; - background-color: var(--secondary-background); - border-left: 0.4em solid #7f7f84; - margin-bottom: 0.5em; - -webkit-box-shadow: 0 5px 8px -6px rgba(0,0,0,.2); - -moz-box-shadow: 0 5px 8px -6px rgba(0,0,0,.2); - box-shadow: 0 5px 8px -6px rgba(0,0,0,.2); + padding: 0.3em; + background-color: var(--secondary-background); + border-left: 0.4em solid #7f7f84; + margin-bottom: 0.5em; + -webkit-box-shadow: 0 5px 8px -6px rgba(0,0,0,.2); + -moz-box-shadow: 0 5px 8px -6px rgba(0,0,0,.2); + box-shadow: 0 5px 8px -6px rgba(0,0,0,.2); } .admonition-info { - border-color: var(--info-background); + border-color: var(--info-background); } .admonition-info-text { - color: var(--info-background); + color: var(--info-background); } .admonition-warning { - border-color: var(--warning-background); + border-color: var(--warning-background); } .admonition-warning-text { - color: var(--warning-background); + color: var(--warning-background); } .admonition-error { - border-color: var(--error-background); + border-color: var(--error-background); } .admonition-error-text { - color: var(--error-background); + color: var(--error-background); } .first { @@ -770,8 +757,7 @@ div.footer, div.header { font-size: smaller; } div.footer { - padding-top: 5em; -} + padding-top: 5em; } div.line-block { display: block; @@ -790,17 +776,14 @@ div.search_results { background-color: var(--third-background); margin: 3em; padding: 1em; - border: 1px solid #4d4d4d; -} + border: 1px solid #4d4d4d; } div#global-links ul { margin-left: 0; - list-style-type: none; -} + list-style-type: none; } div#global-links > simple-boot { - margin-left: 3em; -} + margin-left: 3em; } hr.docutils { width: 75%; } @@ -980,8 +963,7 @@ span.Directive { span.option { font-weight: bold; font-family: "Source Code Pro", Monaco, Menlo, Consolas, "Courier New", monospace; - color: var(--option); -} + color: var(--option); } span.Prompt { font-weight: bold; @@ -997,11 +979,10 @@ span.program { text-decoration: underline; text-decoration-color: var(--hint); text-decoration-thickness: 0.05em; - text-underline-offset: 0.15em; -} + text-underline-offset: 0.15em; } -span.Command, span.Rule, span.Hyperlink, span.Label, span.Reference, -span.Other { +span.Command, span.Rule, span.Hyperlink, +span.Label, span.Reference, span.Other { color: var(--other); } /* Pop type, const, proc, and iterator defs in nim def blocks */ @@ -1039,17 +1020,14 @@ span.pragmadots { border-radius: 4px; margin: 0 2px; cursor: pointer; - font-size: 0.8em; -} + font-size: 0.8em; } span.pragmadots:hover { - background-color: var(--hint); -} + background-color: var(--hint); } + span.pragmawrap { - display: none; -} + display: none; } span.attachedType { display: none; - visibility: hidden; -} + visibility: hidden; } diff --git a/nimdoc/testproject/expected/subdir/subdir_b/utils.html b/nimdoc/testproject/expected/subdir/subdir_b/utils.html index f94da7f40..d5a3b84c7 100644 --- a/nimdoc/testproject/expected/subdir/subdir_b/utils.html +++ b/nimdoc/testproject/expected/subdir/subdir_b/utils.html @@ -1,12 +1,11 @@ - + - + - +subdir/subdir_b/utils @@ -17,215 +16,189 @@ -subdir/subdir_b/utils + - -
    -
    -

    subdir/subdir_b/utils

    -
    +
    +
    +

    subdir/subdir_b/utils

    +
    -
    - -     Dark Mode -
    - -
    - Search: -
    -
    - Group by: - -
    - +
  • - Templates - + +
  • -
    -
    - -

    This is a description of the utils module.

    + + +

    This is a description of the utils module.

    Links work:

    • other module: iterators (not in this dir, just an example)
    • internal: fn2(x)
    • @@ -256,377 +229,356 @@

      Ref. type like G and type G and G[T] and type G*[T].

      Group ref. with capital letters works: fN11 or fn11

      Ref. [] is the same as proc `[]`(G[T]) because there are no overloads. The full form: proc `[]`*[T](x: G[T]): TRef. []= aka `[]=`(G[T], int, T).Ref. $ aka proc $ or proc `$`.Ref. $(a: ref SomeType).Ref. foo_bar aka iterator foo_bar_.Ref. fn[T; U,V: SomeFloat]().Ref. 'big or func `'big` or `'big`(string).

      -
      -

      Types

      -
      -
      -
      G[T] = object
      +    
      +

      Types

      +
      +
      +
      G[T] = object
         val: T
       
      -
      - - - -
      +
      + + + +
      -
      SomeType = enum
      +  
      SomeType = enum
         enumValueA, enumValueB, enumValueC
      -
      - - - -
      +
      + + + +
      -
      +
      +
      -

      Procs

      -
      - -
      -
      -
      proc `$`[T](a: G[T]): string
      -
      - - - -
      +

      Procs

      +
      +
      +
      +
      proc `$`[T](a: G[T]): string
      +
      + + + +
      -
      proc `$`[T](a: ref SomeType): string
      -
      - - - -
      +
      proc `$`[T](a: ref SomeType): string
      +
      + + + +
      -
      -
      -
      func `'big`(a: string): SomeType {....raises: [], tags: [].}
      -
      - - - -
      +
      +
      func `'big`(a: string): SomeType {....raises: [], tags: [].}
      +
      + + + +
      -
      -
      -
      proc `[]`[T](x: G[T]): T
      -
      - - - -
      +
      +
      proc `[]`[T](x: G[T]): T
      +
      + + + +
      -
      -
      -
      proc `[]=`[T](a: var G[T]; index: int; value: T)
      -
      - - - -
      +
      +
      proc `[]=`[T](a: var G[T]; index: int; value: T)
      +
      + + + +
      -
      -
      -
      proc binarySearch[T, K](a: openArray[T]; key: K;
      +  
      +
      proc binarySearch[T, K](a: openArray[T]; key: K;
                               cmp: proc (x: T; y: K): int {.closure.}): int
      -
      - - - -
      +
      + + + +
      -
      -
      -
      proc f(x: G[int]) {....raises: [], tags: [].}
      -
      - -There is also variant f(G[string]) - -
      +
      +
      proc f(x: G[int]) {....raises: [], tags: [].}
      +
      + + There is also variant f(G[string]) + +
      -
      proc f(x: G[string]) {....raises: [], tags: [].}
      -
      - -See also f(G[int]). - -
      +
      proc f(x: G[string]) {....raises: [], tags: [].}
      +
      + + See also f(G[int]). + +
      -
      -
      -
      proc fn[T; U, V: SomeFloat]()
      -
      - - - -
      +
      +
      proc fn[T; U, V: SomeFloat]()
      +
      + + + +
      -
      -
      -
      proc fn2() {....raises: [], tags: [].}
      -
      - -comment - -
      +
      +
      proc fn2() {....raises: [], tags: [].}
      +
      + + comment + +
      -
      proc fn2(x: int) {....raises: [], tags: [].}
      -
      - -fn2 comment - -
      +
      proc fn2(x: int) {....raises: [], tags: [].}
      +
      + + fn2 comment + +
      -
      proc fn2(x: int; y: float) {....raises: [], tags: [].}
      -
      - - - -
      +
      proc fn2(x: int; y: float) {....raises: [], tags: [].}
      +
      + + + +
      -
      -
      -
      proc fn3(): auto {....raises: [], tags: [].}
      -
      - -comment - -
      +
      +
      proc fn3(): auto {....raises: [], tags: [].}
      +
      + + comment + +
      -
      -
      -
      proc fn4(): auto {....raises: [], tags: [].}
      -
      - -comment - -
      +
      +
      proc fn4(): auto {....raises: [], tags: [].}
      +
      + + comment + +
      -
      -
      -
      proc fn5() {....raises: [], tags: [].}
      -
      - -comment - -
      +
      +
      proc fn5() {....raises: [], tags: [].}
      +
      + + comment + +
      -
      -
      -
      proc fn6() {....raises: [], tags: [].}
      -
      - -comment - -
      +
      +
      proc fn6() {....raises: [], tags: [].}
      +
      + + comment + +
      -
      -
      -
      proc fn7() {....raises: [], tags: [].}
      -
      - -comment - -
      +
      +
      proc fn7() {....raises: [], tags: [].}
      +
      + + comment + +
      -
      -
      -
      proc fn8(): auto {....raises: [], tags: [].}
      -
      - -comment - -
      +
      +
      proc fn8(): auto {....raises: [], tags: [].}
      +
      + + comment + +
      -
      -
      -
      func fn9(a: int): int {....raises: [], tags: [].}
      -
      - -comment - -
      +
      +
      func fn9(a: int): int {....raises: [], tags: [].}
      +
      + + comment + +
      -
      -
      -
      func fn10(a: int): int {....raises: [], tags: [].}
      -
      - -comment - -
      +
      +
      func fn10(a: int): int {....raises: [], tags: [].}
      +
      + + comment + +
      -
      -
      -
      func fN11() {....raises: [], tags: [].}
      -
      - - - -
      +
      +
      func fN11() {....raises: [], tags: [].}
      +
      + + + +
      -
      func fN11(x: int) {....raises: [], tags: [].}
      -
      - - - -
      +
      func fN11(x: int) {....raises: [], tags: [].}
      +
      + + + +
      -
      -
      -
      proc funWithGenerics[T, U: SomeFloat](a: T; b: U)
      -
      - - - -
      +
      +
      proc funWithGenerics[T, U: SomeFloat](a: T; b: U)
      +
      + + + +
      -
      -
      -
      proc someType(): SomeType {....raises: [], tags: [].}
      -
      - -constructor. - -
      +
      +
      proc someType(): SomeType {....raises: [], tags: [].}
      +
      + + constructor. + +
      -
      +
      +
      -

      Iterators

      -
      - -
      -
      -
      iterator fooBar(a: seq[SomeType]): int {....raises: [], tags: [].}
      -
      - - - -
      +

      Iterators

      +
      +
      +
      +
      iterator fooBar(a: seq[SomeType]): int {....raises: [], tags: [].}
      +
      + + + +
      -
      +
      +
      -

      Templates

      -
      - -
      -
      -
      template aEnum(): untyped
      -
      - - - -
      +

      Templates

      +
      +
      +
      +
      template aEnum(): untyped
      +
      + + + +
      -
      -
      -
      template bEnum(): untyped
      -
      - - - -
      +
      +
      template bEnum(): untyped
      +
      + + + +
      -
      -
      -
      template fromUtilsGen(): untyped
      -
      - -should be shown in utils.html only +
      +
      template fromUtilsGen(): untyped
      +
      + + should be shown in utils.html only

      Example:

      discard "should be in utils.html only, not in module that calls fromUtilsGen"
      ditto - -
      + +
      -
      +
      +
    -
    -
    - + diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html index cba9391af..49f24d204 100644 --- a/nimdoc/testproject/expected/testproject.html +++ b/nimdoc/testproject/expected/testproject.html @@ -1,12 +1,11 @@ - + - + - +testproject @@ -17,372 +16,327 @@ -testproject + - -
    -
    -

    testproject

    -
    +
    +
    +

    testproject

    +
    -
    - -     Dark Mode -
    - -
    - Search: -
    -
    - Group by: - -
    - +
  • - Methods - + +
  • - Iterators - + +
  • - Macros - + +
  • - Templates - + +
  • -
    -
    - -

    This is the top level module. + + +

    This is the top level module.

    Example:

    import testproject
     import subdir / subdir_b / utils
    @@ -396,133 +350,136 @@
     

    Example:

    import testproject
     discard "in top3"
    top3 after

    - +
    -

    Types

    -
    -
    -
    A {.inject.} = enum
    +  

    Types

    +
    +
    +
    A {.inject.} = enum
       aA
    -
    - -The enum A. - -
    +
    + + The enum A. + +
    -
    B {.inject.} = enum
    +  
    B {.inject.} = enum
       bB
    -
    - -The enum B. - -
    +
    + + The enum B. + +
    -
    Foo = enum
    +  
    Foo = enum
       enumValueA2
    -
    - - - -
    +
    + + + +
    -
    FooBuzz {....deprecated: "FooBuzz msg".} = int
    -
    -
    - Deprecated: FooBuzz msg -
    +
    FooBuzz {....deprecated: "FooBuzz msg".} = int
    +
    +
    + Deprecated: FooBuzz msg +
    - - -
    + + +
    -
    Shapes = enum
    +  
    Shapes = enum
       Circle,                   ## A circle
       Triangle,                 ## A three-sided shape
       Rectangle                  ## A four-sided shape
    -
    - -Some shapes. - -
    +
    + + Some shapes. + +
    -
    +
    +
    -

    Vars

    -
    -
    -
    aVariable: array[1, int]
    -
    - - - -
    +

    Vars

    +
    +
    +
    aVariable: array[1, int]
    +
    + + + +
    -
    someVariable: bool
    -
    - -This should be visible. - -
    +
    someVariable: bool
    +
    + + This should be visible. + +
    -
    +
    +
    -

    Consts

    -
    -
    -
    C_A = 0x7FF0000000000000'f64
    -
    - - - -
    +

    Consts

    +
    +
    +
    C_A = 0x7FF0000000000000'f64
    +
    + + + +
    -
    C_B = 0o377'i8
    -
    - - - -
    +
    C_B = 0o377'i8
    +
    + + + +
    -
    C_C = 0o277'i8
    -
    - - - -
    +
    C_C = 0o277'i8
    +
    + + + +
    -
    C_D = 0o177777'i16
    -
    - - - -
    +
    C_D = 0o177777'i16
    +
    + + + +
    -
    +
    +
    -

    Procs

    -
    - -
    -
    -
    proc addfBug14485() {....raises: [], tags: [].}
    -
    - -Some proc +

    Procs

    +
    +
    +
    +
    proc addfBug14485() {....raises: [], tags: [].}
    +
    + + Some proc

    Example:

    discard "foo() = " & $[1]
     #[
    @@ -535,208 +492,194 @@ Some proc
     6: </script
     7: end of broken html
     ]#
    - -
    + +
    -
    -
    -
    proc anything() {....raises: [], tags: [].}
    -
    - -There is no block quote after blank lines at the beginning. - -
    +
    +
    proc anything() {....raises: [], tags: [].}
    +
    + + There is no block quote after blank lines at the beginning. + +
    -
    -
    -
    proc asyncFun1(): Future[int] {....raises: [Exception, ValueError],
    +  
    +
    proc asyncFun1(): Future[int] {....raises: [Exception, ValueError],
                                     tags: [RootEffect].}
    -
    - -ok1 - -
    +
    + + ok1 + +
    -
    -
    -
    proc asyncFun2(): owned(Future[void]) {....raises: [Exception], tags: [RootEffect].}
    -
    - - - -
    +
    +
    proc asyncFun2(): owned(Future[void]) {....raises: [Exception], tags: [RootEffect].}
    +
    + + + +
    -
    -
    -
    proc asyncFun3(): owned(Future[void]) {....raises: [Exception], tags: [RootEffect].}
    -
    - - +
    +
    proc asyncFun3(): owned(Future[void]) {....raises: [Exception], tags: [RootEffect].}
    +
    + +

    Example:

    discard
    ok1 - -
    + +
    -
    -
    -
    proc bar[T](a, b: T): T
    -
    - - - -
    +
    +
    proc bar[T](a, b: T): T
    +
    + + + +
    -
    -
    -
    proc baz() {....raises: [], tags: [].}
    -
    - - - -
    +
    +
    proc baz() {....raises: [], tags: [].}
    +
    + + + +
    -
    proc baz[T](a, b: T): T {....deprecated.}
    -
    -
    - Deprecated -
    +
    proc baz[T](a, b: T): T {....deprecated.}
    +
    +
    + Deprecated +
    -This is deprecated without message. - -
    + This is deprecated without message. + +
    -
    -
    -
    proc buzz[T](a, b: T): T {....deprecated: "since v0.20".}
    -
    -
    - Deprecated: since v0.20 -
    +
    +
    proc buzz[T](a, b: T): T {....deprecated: "since v0.20".}
    +
    +
    + Deprecated: since v0.20 +
    -This is deprecated with a message. - -
    + This is deprecated with a message. + +
    -
    -
    -
    proc c_nonexistent(frmt: cstring): cint {.importc: "nonexistent",
    +  
    +
    proc c_nonexistent(frmt: cstring): cint {.importc: "nonexistent",
         header: "<stdio.h>", varargs, discardable, ...raises: [], tags: [].}
    -
    - - - -
    +
    + + + +
    -
    -
    -
    proc c_printf(frmt: cstring): cint {.importc: "printf", header: "<stdio.h>",
    +  
    +
    proc c_printf(frmt: cstring): cint {.importc: "printf", header: "<stdio.h>",
                                          varargs, discardable, ...raises: [], tags: [].}
    -
    - -the c printf. etc. - -
    +
    + + the c printf. etc. + +
    -
    -
    -
    proc fromUtils3() {....raises: [], tags: [].}
    -
    - -came form utils but should be shown where fromUtilsGen is called +
    +
    proc fromUtils3() {....raises: [], tags: [].}
    +
    + + came form utils but should be shown where fromUtilsGen is called

    Example:

    discard """should be shown as examples for fromUtils3
            in module calling fromUtilsGen"""
    - -
    + +
    -
    -
    -
    proc isValid[T](x: T): bool
    -
    - - - -
    +
    +
    proc isValid[T](x: T): bool
    +
    + + + +
    -
    -
    -
    proc low[T: Ordinal | enum | range](x: T): T {.magic: "Low", noSideEffect,
    +  
    +
    proc low[T: Ordinal | enum | range](x: T): T {.magic: "Low", noSideEffect,
         ...raises: [], tags: [].}
    -
    - -

    Returns the lowest possible value of an ordinal value x. As a special semantic rule, x may also be a type identifier.

    +
    + +

    Returns the lowest possible value of an ordinal value x. As a special semantic rule, x may also be a type identifier.

    See also:

    low(2) # => -9223372036854775808
    - -
    + +
    -
    -
    -
    proc low2[T: Ordinal | enum | range](x: T): T {.magic: "Low", noSideEffect,
    +  
    +
    proc low2[T: Ordinal | enum | range](x: T): T {.magic: "Low", noSideEffect,
         ...raises: [], tags: [].}
    -
    - -

    Returns the lowest possible value of an ordinal value x. As a special semantic rule, x may also be a type identifier.

    +
    + +

    Returns the lowest possible value of an ordinal value x. As a special semantic rule, x may also be a type identifier.

    See also:

    low2(2) # => -9223372036854775808

    Example:

    discard "in low2"
    - -
    + +
    -
    -
    -
    proc p1() {....raises: [], tags: [].}
    -
    - -cp1 +
    +
    proc p1() {....raises: [], tags: [].}
    +
    + + cp1

    Example:

    doAssert 1 == 1 # regular comments work here
    c4

    Example:

    @@ -753,30 +696,28 @@ this is a nested doc comment ]## discard "c9" # also work after
    - - + +
    -
    -
    -
    func someFunc() {....raises: [], tags: [].}
    -
    - -My someFunc. Stuff in quotes here. Some link - -
    +
    +
    func someFunc() {....raises: [], tags: [].}
    +
    + + My someFunc. Stuff in quotes here. Some link + +
    -
    -
    -
    proc tripleStrLitTest() {....raises: [], tags: [].}
    -
    - - +
    +
    proc tripleStrLitTest() {....raises: [], tags: [].}
    +
    + +

    Example: cmd: --hint:XDeclaredButNotUsed:off

    ## mullitline string litterals are tricky as their indentation can span
     ## below that of the runnableExamples
    @@ -813,365 +754,343 @@ at indent 0
       """ ]
     discard
     # should be in
    - -
    + +
    -
    -
    -
    proc z1(): Foo {....raises: [], tags: [].}
    -
    - -cz1 - -
    +
    +
    proc z1(): Foo {....raises: [], tags: [].}
    +
    + + cz1 + +
    -
    -
    -
    proc z2() {....raises: [], tags: [].}
    -
    - -cz2 +
    +
    proc z2() {....raises: [], tags: [].}
    +
    + + cz2

    Example:

    discard "in cz2"
    - -
    + +
    -
    -
    -
    proc z3() {....raises: [], tags: [].}
    -
    - -cz3 - -
    +
    +
    proc z3() {....raises: [], tags: [].}
    +
    + + cz3 + +
    -
    -
    -
    proc z4() {....raises: [], tags: [].}
    -
    - -cz4 - -
    +
    +
    proc z4() {....raises: [], tags: [].}
    +
    + + cz4 + +
    -
    -
    -
    proc z5(): int {....raises: [], tags: [].}
    -
    - -cz5 - -
    +
    +
    proc z5(): int {....raises: [], tags: [].}
    +
    + + cz5 + +
    -
    -
    -
    proc z6(): int {....raises: [], tags: [].}
    -
    - -cz6 - -
    +
    +
    proc z6(): int {....raises: [], tags: [].}
    +
    + + cz6 + +
    -
    -
    -
    proc z7(): int {....raises: [], tags: [].}
    -
    - -cz7 - -
    +
    +
    proc z7(): int {....raises: [], tags: [].}
    +
    + + cz7 + +
    -
    -
    -
    proc z8(): int {....raises: [], tags: [].}
    -
    - -cz8 - -
    +
    +
    proc z8(): int {....raises: [], tags: [].}
    +
    + + cz8 + +
    -
    -
    -
    proc z9() {....raises: [], tags: [].}
    -
    - - +
    +
    proc z9() {....raises: [], tags: [].}
    +
    + +

    Example:

    doAssert 1 + 1 == 2
    - -
    + +
    -
    -
    -
    proc z10() {....raises: [], tags: [].}
    -
    - - +
    +
    proc z10() {....raises: [], tags: [].}
    +
    + +

    Example: cmd: -d:foobar

    discard 1
    cz10 - -
    + +
    -
    -
    -
    proc z11() {....raises: [], tags: [].}
    -
    - - +
    +
    proc z11() {....raises: [], tags: [].}
    +
    + +

    Example:

    discard 1
    - -
    + +
    -
    -
    -
    proc z12(): int {....raises: [], tags: [].}
    -
    - - +
    +
    proc z12(): int {....raises: [], tags: [].}
    +
    + +

    Example:

    discard 1
    - -
    + +
    -
    -
    -
    proc z13() {....raises: [], tags: [].}
    -
    - -cz13 +
    +
    proc z13() {....raises: [], tags: [].}
    +
    + + cz13

    Example:

    discard
    - -
    + +
    -
    -
    -
    proc z17() {....raises: [], tags: [].}
    -
    - -cz17 rest +
    +
    proc z17() {....raises: [], tags: [].}
    +
    + + cz17 rest

    Example:

    discard 1
    rest - -
    + +
    -
    + +
    -

    Methods

    -
    - -
    -
    -
    method method1(self: Moo) {.base, ...raises: [], tags: [].}
    -
    - -foo1 - -
    +

    Methods

    +
    +
    +
    +
    method method1(self: Moo) {.base, ...raises: [], tags: [].}
    +
    + + foo1 + +
    -
    -
    -
    method method2(self: Moo): int {.base, ...raises: [], tags: [].}
    -
    - -foo2 - -
    +
    +
    method method2(self: Moo): int {.base, ...raises: [], tags: [].}
    +
    + + foo2 + +
    -
    -
    -
    method method3(self: Moo): int {.base, ...raises: [], tags: [].}
    -
    - -foo3 - -
    +
    +
    method method3(self: Moo): int {.base, ...raises: [], tags: [].}
    +
    + + foo3 + +
    -
    +
    +
    -

    Iterators

    -
    - -
    -
    -
    iterator fromUtils1(): int {....raises: [], tags: [].}
    -
    - - +

    Iterators

    +
    +
    +
    +
    iterator fromUtils1(): int {....raises: [], tags: [].}
    +
    + +

    Example:

    # ok1
     assert 1 == 1
     # ok2
    - -
    + +
    -
    -
    -
    iterator iter1(n: int): int {....raises: [], tags: [].}
    -
    - -foo1 - -
    +
    +
    iterator iter1(n: int): int {....raises: [], tags: [].}
    +
    + + foo1 + +
    -
    -
    -
    iterator iter2(n: int): int {....raises: [], tags: [].}
    -
    - -foo2 +
    +
    iterator iter2(n: int): int {....raises: [], tags: [].}
    +
    + + foo2

    Example:

    discard # bar
    - -
    + +
    -
    + +
    -

    Macros

    -
    - -
    -
    -
    macro bar(): untyped
    -
    - - - -
    +

    Macros

    +
    +
    +
    +
    macro bar(): untyped
    +
    + + + +
    -
    -
    -
    macro z16()
    -
    - - +
    +
    macro z16()
    +
    + +

    Example:

    discard 1
    cz16 after

    Example:

    doAssert 2 == 1 + 1
    - -
    + +
    -
    -
    -
    macro z18(): int
    -
    - -cz18 - -
    +
    +
    macro z18(): int
    +
    + + cz18 + +
    -
    +
    +
    -

    Templates

    -
    - -
    -
    -
    template foo(a, b: SomeType)
    -
    - -This does nothing - -
    +

    Templates

    +
    +
    +
    +
    template foo(a, b: SomeType)
    +
    + + This does nothing + +
    -
    -
    -
    template fromUtils2()
    -
    - -ok3 +
    +
    template fromUtils2()
    +
    + + ok3

    Example:

    discard """should be shown as examples for fromUtils2
            in module calling fromUtilsGen"""
    - -
    + +
    -
    -
    -
    template myfn()
    -
    - - +
    +
    template myfn()
    +
    + +

    Example:

    import std/strutils
     ## issue #8871 preserve formatting
    @@ -1187,58 +1106,54 @@ bar
     block:
       discard 0xff # elu par cette crapule
     # should be in
    should be still in - -
    + +
    -
    -
    -
    template testNimDocTrailingExample()
    -
    - - +
    +
    template testNimDocTrailingExample()
    +
    + +

    Example:

    discard 2
    - -
    + +
    -
    -
    -
    template z6t(): int
    -
    - -cz6t - -
    +
    +
    template z6t(): int
    +
    + + cz6t + +
    -
    -
    -
    template z14()
    -
    - -cz14 +
    +
    template z14()
    +
    + + cz14

    Example:

    discard
    - -
    + +
    -
    -
    -
    template z15()
    -
    - -cz15 +
    +
    template z15()
    +
    + + cz15

    Example:

    discard

    Example:

    @@ -1249,26 +1164,25 @@ cz15
    assert true

    Example:

    discard 1
    in or out? - -
    + +
    -
    +
    +
    -
    -
    - + diff --git a/nimdoc/testproject/expected/theindex.html b/nimdoc/testproject/expected/theindex.html index 47fae2491..c62b4c7db 100644 --- a/nimdoc/testproject/expected/theindex.html +++ b/nimdoc/testproject/expected/theindex.html @@ -1,12 +1,11 @@ - + - + - +Index @@ -17,17 +16,16 @@ -Index + - -
    -
    -

    Index

    - Modules: subdir/subdir_b/utils, testproject.

    API symbols

    +
    +
    +

    Index

    + Modules: subdir/subdir_b/utils, testproject.

    API symbols

    `$`:
    -
    -
    - + From d33e1127666be23ad2dc879c2f89a41c179e2093 Mon Sep 17 00:00:00 2001 From: Tanguy Date: Wed, 15 Jun 2022 16:38:12 +0200 Subject: [PATCH 2957/3768] Better range error messages (#19867) * Better range error messages * Revert to old behavior for arrays * Small corrections --- compiler/ccgexprs.nim | 6 +++--- lib/system/chcks.nim | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 8e7a21c67..4c15101a9 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -988,12 +988,12 @@ proc genBoundsCheck(p: BProc; arr, a, b: TLoc) = if reifiedOpenArray(arr.lode): linefmt(p, cpsStmts, "if ($2-$1 != -1 && " & - "($1 < 0 || $1 >= $3.Field1 || $2 < 0 || $2 >= $3.Field1)){ #raiseIndexError(); $4}$n", + "($1 < 0 || $1 >= $3.Field1 || $2 < 0 || $2 >= $3.Field1)){ #raiseIndexError4($1, $2, $3.Field1); $4}$n", [rdLoc(a), rdLoc(b), rdLoc(arr), raiseInstr(p)]) else: linefmt(p, cpsStmts, "if ($2-$1 != -1 && ($1 < 0 || $1 >= $3Len_0 || $2 < 0 || $2 >= $3Len_0))" & - "{ #raiseIndexError(); $4}$n", + "{ #raiseIndexError4($1, $2, $3Len_0); $4}$n", [rdLoc(a), rdLoc(b), rdLoc(arr), raiseInstr(p)]) of tyArray: let first = intLiteral(firstOrd(p.config, ty)) @@ -1004,7 +1004,7 @@ proc genBoundsCheck(p: BProc; arr, a, b: TLoc) = of tySequence, tyString: linefmt(p, cpsStmts, "if ($2-$1 != -1 && " & - "($1 < 0 || $1 >= $3 || $2 < 0 || $2 >= $3)){ #raiseIndexError(); $4}$n", + "($1 < 0 || $1 >= $3 || $2 < 0 || $2 >= $3)){ #raiseIndexError4($1, $2, $3); $4}$n", [rdLoc(a), rdLoc(b), lenExpr(p, arr), raiseInstr(p)]) else: discard diff --git a/lib/system/chcks.nim b/lib/system/chcks.nim index a404e9d40..1e1ce9c87 100644 --- a/lib/system/chcks.nim +++ b/lib/system/chcks.nim @@ -16,6 +16,9 @@ proc raiseRangeError(val: BiggestInt) {.compilerproc, noinline.} = else: sysFatal(RangeDefect, "value out of range: ", $val) +proc raiseIndexError4(l1, h1, h2: int) {.compilerproc, noinline.} = + sysFatal(IndexDefect, "index out of bounds: " & $l1 & ".." & $h1 & " notin 0.." & $(h2 - 1)) + proc raiseIndexError3(i, a, b: int) {.compilerproc, noinline.} = sysFatal(IndexDefect, formatErrorIndexBound(i, a, b)) From ef2dd2e473d7082d60088ece05beef81062850bb Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Thu, 16 Jun 2022 14:46:59 +0800 Subject: [PATCH 2958/3768] document nimTestsNimdocFixup for rsttester (#19894) --- nimdoc/rsttester.nim | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/nimdoc/rsttester.nim b/nimdoc/rsttester.nim index daca3dfc7..a0bdfca1e 100644 --- a/nimdoc/rsttester.nim +++ b/nimdoc/rsttester.nim @@ -1,9 +1,14 @@ +# To run this, cd to the git repo root, and run "nim r nimdoc/rsttester.nim". +# to change expected results (after carefully verifying everything), use -d:nimTestsNimdocFixup + import os, strutils from std/private/gitutils import diffFiles const baseDir = "nimdoc/rst2html" +const fixup = defined(nimTestsNimdocFixup) + var failures = 0 proc exec(cmd: string) = @@ -29,7 +34,7 @@ proc testRst2Html(fixup = false) = if failures == 0: removeDir(baseDir / "source/htmldocs") -testRst2Html(defined(fixup)) +testRst2Html(fixup) # Check for failures if failures > 0: quit($failures & " failures occurred.") From 332aa0af75baf328416068459931d5a6e4292d34 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sun, 19 Jun 2022 13:35:25 +0800 Subject: [PATCH 2959/3768] [vm] refactor `stdlib.marshal.load` (#19905) refactor stdlib.marshal.load --- compiler/vmops.nim | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/compiler/vmops.nim b/compiler/vmops.nim index 640826cc8..04b753ef6 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -364,21 +364,5 @@ proc registerAdditionalOps*(c: PCtx) = let typ = a.getNode(0).typ let p = a.getReg(1) var res: string - - var node: PNode - case p.kind - of rkNone: - node = newNode(nkEmpty) - of rkInt: - node = newIntNode(nkIntLit, p.intVal) - of rkFloat: - node = newFloatNode(nkFloatLit, p.floatVal) - of rkNode: - node = p.node - of rkRegisterAddr: - node = p.regAddr.node - of rkNodeAddr: - node = p.nodeAddr[] - - storeAny(res, typ, node, c.config) + storeAny(res, typ, regToNode(p[]), c.config) setResult(a, res) From dd4cc266cd39d7cecfd7215e683b0c6499fc4a36 Mon Sep 17 00:00:00 2001 From: Jake Leahy Date: Sun, 19 Jun 2022 15:36:33 +1000 Subject: [PATCH 2960/3768] Fixes #19900 (#19906) Fixed tocRoot placement --- config/nimdoc.cfg | 4 ++-- nimdoc/rst2html/expected/rst_examples.html | 2 +- nimdoc/test_out_index_dot_html/expected/index.html | 2 +- nimdoc/testproject/expected/subdir/subdir_b/utils.html | 2 +- nimdoc/testproject/expected/testproject.html | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/config/nimdoc.cfg b/config/nimdoc.cfg index 725f9e0a5..3b0afe8d6 100644 --- a/config/nimdoc.cfg +++ b/config/nimdoc.cfg @@ -149,11 +149,11 @@ doc.body_toc_group = """ onkeyup="search()" />
    $body_toc_groupsection -
    $tableofcontents
    $seeSrc +
    $deprecationMsg

    $moduledesc

    $content @@ -188,11 +188,11 @@ doc.body_toc_group = """
    -
    $tableofcontents
    $seeSrc +
    $deprecationMsg

    $moduledesc

    $content diff --git a/nimdoc/rst2html/expected/rst_examples.html b/nimdoc/rst2html/expected/rst_examples.html index 532917055..efc8ac414 100644 --- a/nimdoc/rst2html/expected/rst_examples.html +++ b/nimdoc/rst2html/expected/rst_examples.html @@ -49,7 +49,6 @@
    -
    +

    diff --git a/nimdoc/test_out_index_dot_html/expected/index.html b/nimdoc/test_out_index_dot_html/expected/index.html index 0c0dc2268..43285db6b 100644 --- a/nimdoc/test_out_index_dot_html/expected/index.html +++ b/nimdoc/test_out_index_dot_html/expected/index.html @@ -49,7 +49,6 @@ -
    • @@ -69,6 +68,7 @@
      +

      diff --git a/nimdoc/testproject/expected/subdir/subdir_b/utils.html b/nimdoc/testproject/expected/subdir/subdir_b/utils.html index d5a3b84c7..4d753ad8f 100644 --- a/nimdoc/testproject/expected/subdir/subdir_b/utils.html +++ b/nimdoc/testproject/expected/subdir/subdir_b/utils.html @@ -49,7 +49,6 @@
      -
      +

      This is a description of the utils module.

      Links work:

      diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html index 49f24d204..24dfdc13f 100644 --- a/nimdoc/testproject/expected/testproject.html +++ b/nimdoc/testproject/expected/testproject.html @@ -49,7 +49,6 @@
      -
      • @@ -335,6 +334,7 @@
        +

        This is the top level module.

        Example:

        From 5e32fc785527d9b71ebab643f142fbab13eb29ce Mon Sep 17 00:00:00 2001 From: tersec Date: Mon, 20 Jun 2022 06:01:41 +0000 Subject: [PATCH 2961/3768] Recommend gcc 5.x or newer (#19910) --- readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/readme.md b/readme.md index 7ff056a20..8999091e6 100644 --- a/readme.md +++ b/readme.md @@ -51,8 +51,8 @@ Nim programming language. Those C sources are available within the Next, to build from source you will need: - * A C compiler such as ``gcc`` 3.x/later or an alternative such as ``clang``, - ``Visual C++`` or ``Intel C++``. It is recommended to use ``gcc`` 3.x or + * A C compiler such as ``gcc`` 5.x/later or an alternative such as ``clang``, + ``Visual C++`` or ``Intel C++``. It is recommended to use ``gcc`` 5.x or later. * Either ``git`` or ``wget`` to download the needed source repositories. * The ``build-essential`` package when using ``gcc`` on Ubuntu (and likely From 40464fa7626a616a3881c0b16a74b9ec7d788720 Mon Sep 17 00:00:00 2001 From: Tanguy Date: Mon, 20 Jun 2022 08:21:20 +0200 Subject: [PATCH 2962/3768] Fix nimRawSetjmp for VCC [backport: 1.2] (#19899) --- lib/system/ansi_c.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/system/ansi_c.nim b/lib/system/ansi_c.nim index 5d0ecc01c..74f79167a 100644 --- a/lib/system/ansi_c.nim +++ b/lib/system/ansi_c.nim @@ -118,7 +118,7 @@ elif defined(nimBuiltinSetjmp): c_builtin_setjmp(unsafeAddr jmpb[0]) elif defined(nimRawSetjmp) and not defined(nimStdSetjmp): - when defined(windows): + when defined(windows) and not defined(vcc): # No `_longjmp()` on Windows. proc c_longjmp*(jmpb: C_JmpBuf, retval: cint) {. header: "", importc: "longjmp".} From 0ae44e562f693d270e3cad77675ce5f68474921d Mon Sep 17 00:00:00 2001 From: flintforge Date: Tue, 21 Jun 2022 03:53:46 +0200 Subject: [PATCH 2963/3768] fix typo in nre.nim (#19915) Update nre.nim typo in proc replace description --- lib/impure/nre.nim | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/impure/nre.nim b/lib/impure/nre.nim index 03cbf5220..1012c7c36 100644 --- a/lib/impure/nre.nim +++ b/lib/impure/nre.nim @@ -695,8 +695,7 @@ proc replace*(str: string, pattern: Regex, ## each match and the return value is the replacement value. ## ## If `subproc` is a `proc (string): string`, then it is executed with the - ## full text of the match and and the return value is the replacement - ## value. + ## full text of the match and the return value is the replacement value. ## ## If `subproc` is a string, the syntax is as follows: ## From a65db5e2e924c51af7c654f8e564332299b95cb3 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Tue, 21 Jun 2022 15:37:23 +0800 Subject: [PATCH 2964/3768] [Tiny] correct comment opcDeref => opcLdDeref (#19908) correct comment opcDeref => opcLdDeref --- compiler/vm.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/vm.nim b/compiler/vm.nim index 1b11cb4f3..656508793 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -642,7 +642,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of opcNodeToReg: let ra = instr.regA let rb = instr.regB - # opcDeref might already have loaded it into a register. XXX Let's hope + # opcLdDeref might already have loaded it into a register. XXX Let's hope # this is still correct this way: if regs[rb].kind != rkNode: regs[ra] = regs[rb] From 3cb2d7af0591b12044d4be20dfb6d7f7ff79a4ff Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Wed, 22 Jun 2022 14:43:58 +0800 Subject: [PATCH 2965/3768] [vm]fixes #15974 #12551 #19464 #16020 #16780 #16613 #14553 #19909 #18641 (#19902) [backport] * revert #12217 since the root problem seems to have been fixed; fix #15974;fix #12551; fix #19464 * fix #16020; fix #16780 * fix tests and #16613 * fix #14553 * fix #19909; skip skipRegisterAddr * fix #18641 --- compiler/vm.nim | 28 +++--- compiler/vmgen.nim | 3 - tests/vm/tissues.nim | 50 ++++++++++- tests/vm/tmisc_vm.nim | 196 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 263 insertions(+), 14 deletions(-) diff --git a/compiler/vm.nim b/compiler/vm.nim index 656508793..e681bbc96 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -523,8 +523,7 @@ when not defined(nimHasSinkInference): template takeAddress(reg, source) = reg.nodeAddr = addr source - when defined(gcDestructors): - GC_ref source + GC_ref source proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = var pc = start @@ -1011,6 +1010,12 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = decodeBC(rkInt) template getTyp(n): untyped = n.typ.skipTypes(abstractInst) + template skipRegisterAddr(n: TFullReg): TFullReg = + var tmp = n + while tmp.kind == rkRegisterAddr: + tmp = tmp.regAddr[] + tmp + proc ptrEquality(n1: ptr PNode, n2: PNode): bool = ## true if n2.intVal represents a ptr equal to n1 let p1 = cast[int](n1) @@ -1024,16 +1029,19 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = return t2.kind in PtrLikeKinds and n2.intVal == p1 else: return false - if regs[rb].kind == rkNodeAddr: - if regs[rc].kind == rkNodeAddr: - ret = regs[rb].nodeAddr == regs[rc].nodeAddr + let rbReg = skipRegisterAddr(regs[rb]) + let rcReg = skipRegisterAddr(regs[rc]) + + if rbReg.kind == rkNodeAddr: + if rcReg.kind == rkNodeAddr: + ret = rbReg.nodeAddr == rcReg.nodeAddr else: - ret = ptrEquality(regs[rb].nodeAddr, regs[rc].node) - elif regs[rc].kind == rkNodeAddr: - ret = ptrEquality(regs[rc].nodeAddr, regs[rb].node) + ret = ptrEquality(rbReg.nodeAddr, rcReg.node) + elif rcReg.kind == rkNodeAddr: + ret = ptrEquality(rcReg.nodeAddr, rbReg.node) else: - let nb = regs[rb].node - let nc = regs[rc].node + let nb = rbReg.node + let nc = rcReg.node if nb.kind != nc.kind: discard elif (nb == nc) or (nb.kind == nkNilLit): ret = true # intentional elif nb.kind in {nkSym, nkTupleConstr, nkClosure} and nb.typ != nil and nb.typ.kind == tyProc and sameConstant(nb, nc): diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 2e7f6d8b0..aa2bdbe4c 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1382,9 +1382,6 @@ proc unneededIndirection(n: PNode): bool = n.typ.skipTypes(abstractInstOwned-{tyTypeDesc}).kind == tyRef proc canElimAddr(n: PNode): PNode = - if n[0].typ.skipTypes(abstractInst).kind in {tyObject, tyTuple, tyArray}: - # objects are reference types in the VM - return n[0] case n[0].kind of nkObjUpConv, nkObjDownConv, nkChckRange, nkChckRangeF, nkChckRange64: var m = n[0][0] diff --git a/tests/vm/tissues.nim b/tests/vm/tissues.nim index 1cf3afc00..f0ae6c296 100644 --- a/tests/vm/tissues.nim +++ b/tests/vm/tissues.nim @@ -1,6 +1,6 @@ import macros -block t9043: # issue #9043 +block t9043: # bug #9043 proc foo[N: static[int]](dims: array[N, int]): string = const N1 = N const N2 = dims.len @@ -26,3 +26,51 @@ block t4952: let tree = newTree(nnkExprColonExpr) let t = (n: tree) doAssert: t.n.kind == tree.kind + + +# bug #19909 +type + SinglyLinkedList[T] = ref object + SinglyLinkedListObj[T] = ref object + + +proc addMoved[T](a, b: var SinglyLinkedList[T]) = + if a.addr != b.addr: discard + +proc addMoved[T](a, b: var SinglyLinkedListObj[T]) = + if a.addr != b.addr: discard + +proc main = + var a: SinglyLinkedList[int]; new a + var b: SinglyLinkedList[int]; new b + a.addMoved b + + var a0: SinglyLinkedListObj[int] + var b0: SinglyLinkedListObj[int] + a0.addMoved b0 + +static: main() + + +# bug #18641 + +type A = object + ha1: int +static: + var a = A() + var a2 = a.addr + a2.ha1 = 11 + doAssert a2.ha1 == 11 + a.ha1 = 12 + doAssert a.ha1 == 12 + doAssert a2.ha1 == 12 # ok +static: + proc fn() = + var a = A() + var a2 = a.addr + a2.ha1 = 11 + doAssert a2.ha1 == 11 + a.ha1 = 12 + doAssert a.ha1 == 12 + doAssert a2.ha1 == 12 # fails + fn() diff --git a/tests/vm/tmisc_vm.nim b/tests/vm/tmisc_vm.nim index bbf618622..af09e8871 100644 --- a/tests/vm/tmisc_vm.nim +++ b/tests/vm/tmisc_vm.nim @@ -1,7 +1,12 @@ discard """ + targets: "c js" output: ''' [127, 127, 0, 255][127, 127, 0, 255] (data: 1) +(2, 1) +(2, 1) +(2, 1) +(f0: 5) ''' nimout: '''caught Exception @@ -17,6 +22,19 @@ foo4 (a: 0, b: 0) (a: 0, b: 0) (a: 0, b: 0) +z1 m: (lo: 12) +z2 a: (lo: 3) +x1 a: (lo: 3) +x2 a: (lo: 6) +x3 a: (lo: 0) +z3 a: (lo: 3) +x1 a: (lo: 3) +x2 a: (lo: 6) +x3 a: (lo: 0) +(2, 1) +(2, 1) +(2, 1) +(f0: 5) ''' """ import std/sets @@ -249,3 +267,181 @@ static: echo x2[] let x3 = new(ref MyObject) # cannot generate VM code for ref MyObject echo x3[] + +# bug #19464 +type + Wrapper = object + inner: int + +proc assign(r: var Wrapper, a: Wrapper) = + r = a + +proc myEcho(a: Wrapper) = + var tmp = a + assign(tmp, Wrapper(inner: 0)) # this shouldn't modify `a` + doAssert a.inner == 1 + +static: + var result: Wrapper + assign(result, Wrapper(inner: 1)) + myEcho(result) + +when true: + # bug #15974 + type Foo = object + f0: int + + proc fn(a: var Foo) = + var s: Foo + a = Foo(f0: 2) + s = a + doAssert s.f0 == 2 + a = Foo(f0: 3) + doAssert s.f0 == 2 + + proc test2()= + var a = Foo(f0: 1) + fn(a) + + static: test2() + test2() + +# bug #12551 +type + StUint = object + lo: uint64 + +func `+=`(x: var Stuint, y: Stuint) = + x.lo += y.lo + +func `-`(x, y: Stuint): Stuint = + result.lo = x.lo - y.lo + +func `+`(x, y: Stuint): Stuint = + result.lo = x.lo + y.lo + +func `-=`(x: var Stuint, y: Stuint) = + x = x - y + +func `<`(x, y: Stuint): bool= + x.lo < y.lo + +func `==`(x, y: Stuint): bool = + x.lo == y.lo + +func `<=`(x, y: Stuint): bool = + x.lo <= y.lo + +proc div3n2n(r: var Stuint, b: Stuint) = + var d: Stuint + r = d + r += b + +func div2n1n(r: var Stuint, b: Stuint) = + div3n2n(r, b) + +func divmodBZ(x, y: Stuint, r: var Stuint)= + div2n1n(r, y) + r.lo = 3 + +func `mod`(x, y: Stuint): Stuint = + divmodBZ(x, y, result) + +func doublemod_internal(a, m: Stuint): Stuint = + result = a + if a >= m - a: + result -= m + result += a + +func mulmod_internal(a, b, m: Stuint): Stuint = + var (a, b) = (a, b) + swap(a, b) + debugEcho "x1 a: ", a + a = doublemod_internal(a, m) + debugEcho "x2 a: ", a + a = doublemod_internal(a, m) + debugEcho "x3 a: ", a + +func powmod_internal(a, m: Stuint): Stuint = + var a = a + debugEcho "z1 m: ", m + debugEcho "z2 a: ", a + result = mulmod_internal(result, a, m) + debugEcho "z3 a: ", a + a = mulmod_internal(a, a, m) + +func powmod*(a, m: Stuint) = + discard powmod_internal(a mod m, m) + +static: + var x = Stuint(lo: high(uint64)) + var y = Stuint(lo: 12) + + powmod(x, y) + +# bug #16780 +when true: + template swap*[T](a, b: var T) = + var a2 = addr(a) + var b2 = addr(b) + var aOld = a2[] + a2[] = b2[] + b2[] = aOld + + proc rather = + block: + var a = 1 + var b = 2 + swap(a, b) + echo (a,b) + + block: + type Foo = ref object + x: int + var a = Foo(x:1) + var b = Foo(x:2) + swap(a, b) + echo (a.x, b.x) + + block: + type Foo = object + x: int + var a = Foo(x:1) + var b = Foo(x:2) + swap(a, b) + echo (a.x,b.x) + + static: rather() + rather() + +# bug #16020 +when true: + block: + type Foo = object + f0: int + proc main= + var f = Foo(f0: 3) + var f2 = f.addr + f2[].f0 += 1 + f2.f0 += 1 + echo f + static: main() + main() + +import tables, strutils + +# bug #14553 +const PpcPatterns = @[("aaaa", "bbbb"), ("aaaaa", "bbbbb"), ("aaaaaa", "bbbbbb"), ("aaaaaaa", "bbbbbbb"), ("aaaaaaaa", "bbbbb")] + +static: + var + needSecondIdentifier = initTable[uint32, seq[(string, string)]]() + + for (name, pattern) in PpcPatterns: + let + firstPart = 0'u32 + lastPart = "test" + + needSecondIdentifier.mgetOrPut(firstPart, @[]).add((name, pattern)) + + doAssert needSecondIdentifier[0] == @[("aaaa", "bbbb"), ("aaaaa", "bbbbb"), ("aaaaaa", "bbbbbb"), ("aaaaaaa", "bbbbbbb"), ("aaaaaaaa", "bbbbb")] From caf6aff06b6dd36ef0bbe8c1f8b527952790e208 Mon Sep 17 00:00:00 2001 From: Khaled Hammouda Date: Wed, 22 Jun 2022 06:36:30 -0400 Subject: [PATCH 2966/3768] Fix distinct requiresInit test and manual (#19901) fix distinct test and manual --- doc/manual.rst | 11 +++++++---- tests/distinct/tdistinct.nim | 6 +++--- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/doc/manual.rst b/doc/manual.rst index 82c9f5758..b63a2f68f 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -2851,7 +2851,10 @@ Given the following distinct type definitions: .. code-block:: nim type - DistinctObject {.requiresInit, borrow: `.`.} = distinct MyObject + Foo = object + x: string + + DistinctFoo {.requiresInit, borrow: `.`.} = distinct Foo DistinctString {.requiresInit.} = distinct string The following code blocks will fail to compile: @@ -2864,7 +2867,7 @@ The following code blocks will fail to compile: .. code-block:: nim var s: DistinctString s = "test" - doAssert s == "test" + doAssert string(s) == "test" But these ones will compile successfully: @@ -2873,8 +2876,8 @@ But these ones will compile successfully: doAssert foo.x == "test" .. code-block:: nim - let s = "test" - doAssert s == "test" + let s = DistinctString("test") + doAssert string(s) == "test" Let statement ------------- diff --git a/tests/distinct/tdistinct.nim b/tests/distinct/tdistinct.nim index dd8237854..8ec083020 100644 --- a/tests/distinct/tdistinct.nim +++ b/tests/distinct/tdistinct.nim @@ -135,11 +135,11 @@ block tRequiresInit: reject: var s: DistinctString s = "test" - doAssert s == "test" + doAssert string(s) == "test" accept: - let s = "test" - doAssert s == "test" + let s = DistinctString("test") + doAssert string(s) == "test" block: #17322 type From 0189122d4fadc73a3de18a7a781997c506f837aa Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 27 Jun 2022 23:57:07 +0800 Subject: [PATCH 2967/3768] ref #19830; multiple definition of in Nim generated static libraries (#19934) * ref #19830; multiple definition of in Nim generated static libraries * fix compile errors --- compiler/cgen.nim | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 862f31107..c0b94ebbb 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -1323,7 +1323,7 @@ proc getSomeInitName(m: BModule, suffix: string): Rope = proc getInitName(m: BModule): Rope = if sfMainModule in m.module.flags: # generate constant name for main module, for "easy" debugging. - result = rope"NimMainModule" + result = rope(m.config.nimMainPrefix) & rope"NimMainModule" else: result = getSomeInitName(m, "Init000") @@ -1356,35 +1356,35 @@ proc genMainProc(m: BModule) = preMainCode.add("\tinitStackBottomWith_actual((void *)&inner);\L") preMainCode.add("\t(*inner)();\L") else: - preMainCode.add("\tPreMain();\L") + preMainCode.add("\t$1PreMain();\L" % [rope m.config.nimMainPrefix]) + + var posixCmdLine: Rope + if optNoMain notin m.config.globalOptions: + posixCmdLine.add "\tN_LIB_PRIVATE int cmdCount;\L" + posixCmdLine.add "\tN_LIB_PRIVATE char** cmdLine;\L" + posixCmdLine.add "\tN_LIB_PRIVATE char** gEnv;\L" const - # not a big deal if we always compile these 3 global vars... makes the HCR code easier - PosixCmdLine = - "N_LIB_PRIVATE int cmdCount;$N" & - "N_LIB_PRIVATE char** cmdLine;$N" & - "N_LIB_PRIVATE char** gEnv;$N" - # The use of a volatile function pointer to call Pre/NimMainInner # prevents inlining of the NimMainInner function and dependent # functions, which might otherwise merge their stack frames. PreMainVolatileBody = "\tvoid (*volatile inner)(void);$N" & - "\tinner = PreMainInner;$N" & + "\tinner = $3PreMainInner;$N" & "$1" & "\t(*inner)();$N" PreMainNonVolatileBody = "$1" & - "\tPreMainInner();$N" + "\t$3PreMainInner();$N" PreMainBodyStart = "$N" & - "N_LIB_PRIVATE void PreMainInner(void) {$N" & + "N_LIB_PRIVATE void $3PreMainInner(void) {$N" & "$2" & "}$N$N" & - PosixCmdLine & - "N_LIB_PRIVATE void PreMain(void) {$N" + "$4" & + "N_LIB_PRIVATE void $3PreMain(void) {$N" PreMainBodyEnd = "}$N$N" @@ -1395,21 +1395,21 @@ proc genMainProc(m: BModule) = MainProcsWithResult = MainProcs & ("\treturn $1nim_program_result;$N") - NimMainInner = "N_LIB_PRIVATE N_CDECL(void, NimMainInner)(void) {$N" & + NimMainInner = "N_LIB_PRIVATE N_CDECL(void, $5NimMainInner)(void) {$N" & "$1" & "}$N$N" NimMainVolatileBody = "\tvoid (*volatile inner)(void);$N" & "$4" & - "\tinner = NimMainInner;$N" & + "\tinner = $5NimMainInner;$N" & "$2" & "\t(*inner)();$N" NimMainNonVolatileBody = "$4" & "$2" & - "\tNimMainInner();$N" + "\t$5NimMainInner();$N" NimMainProcStart = "N_CDECL(void, $5NimMain)(void) {$N" @@ -1489,9 +1489,9 @@ proc genMainProc(m: BModule) = else: ropecg(m, "\t#initStackBottomWith((void *)&inner);$N", []) inc(m.labels) if m.config.selectedGC notin {gcNone, gcArc, gcOrc}: - appcg(m, m.s[cfsProcs], PreMainBodyStart & PreMainVolatileBody & PreMainBodyEnd, [m.g.mainDatInit, m.g.otherModsInit]) + appcg(m, m.s[cfsProcs], PreMainBodyStart & PreMainVolatileBody & PreMainBodyEnd, [m.g.mainDatInit, m.g.otherModsInit, m.config.nimMainPrefix, posixCmdLine]) else: - appcg(m, m.s[cfsProcs], PreMainBodyStart & PreMainNonVolatileBody & PreMainBodyEnd, [m.g.mainDatInit, m.g.otherModsInit]) + appcg(m, m.s[cfsProcs], PreMainBodyStart & PreMainNonVolatileBody & PreMainBodyEnd, [m.g.mainDatInit, m.g.otherModsInit, m.config.nimMainPrefix, posixCmdLine]) if m.config.target.targetOS == osWindows and m.config.globalOptions * {optGenGuiApp, optGenDynLib} != {}: From 7c31b6a47b5cfb6f2d1f296390eb457bd75e55c2 Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Tue, 28 Jun 2022 03:13:17 -0300 Subject: [PATCH 2968/3768] Fix jsre (#19917) * Fixes for jsre to make it more safe at runtime on some edge cases * https://github.com/nim-lang/Nim/pull/19917#issuecomment-1162692893 --- lib/js/jsre.nim | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/js/jsre.nim b/lib/js/jsre.nim index cd8fb2be7..0cc2f8a87 100644 --- a/lib/js/jsre.nim +++ b/lib/js/jsre.nim @@ -33,13 +33,13 @@ func compile*(self: RegExp; pattern: cstring; flags: cstring) {.importjs: "#.com func replace*(pattern: cstring; self: RegExp; replacement: cstring): cstring {.importjs: "#.replace(#, #)".} ## Returns a new string with some or all matches of a pattern replaced by given replacement -func split*(pattern: cstring; self: RegExp): seq[cstring] {.importjs: "#.split(#)".} +func split*(pattern: cstring; self: RegExp): seq[cstring] {.importjs: "(#.split(#) || [])".} ## Divides a string into an ordered list of substrings and returns the array -func match*(pattern: cstring; self: RegExp): seq[cstring] {.importjs: "#.match(#)".} +func match*(pattern: cstring; self: RegExp): seq[cstring] {.importjs: "(#.match(#) || [])".} ## Returns an array of matches of a RegExp against given string -func exec*(self: RegExp; pattern: cstring): seq[cstring] {.importjs: "#.exec(#)".} +func exec*(self: RegExp; pattern: cstring): seq[cstring] {.importjs: "(#.exec(#) || [])".} ## Executes a search for a match in its string parameter. func toCstring*(self: RegExp): cstring {.importjs: "#.toString()".} @@ -87,3 +87,5 @@ runnableExamples: assert "do1ne".split(jsregex) == @["do".cstring, "ne".cstring] jsregex.compile(r"[lw]", r"i") assert "hello world".replace(jsregex,"X") == "heXlo world" + let digitsRegex: RegExp = newRegExp(r"\d") + assert "foo".match(digitsRegex) == @[] From 8a344cb25b9b9d1b185ba6e5a8e1cf14421fedb4 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Tue, 28 Jun 2022 16:49:31 +0800 Subject: [PATCH 2969/3768] closes #11042; add testcase (#19935) close #11042; add testcase --- tests/closure/t11042.nim | 55 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 tests/closure/t11042.nim diff --git a/tests/closure/t11042.nim b/tests/closure/t11042.nim new file mode 100644 index 000000000..6a3928316 --- /dev/null +++ b/tests/closure/t11042.nim @@ -0,0 +1,55 @@ +discard """ + output:''' +foo: 1 +foo: 2 +bar: 1 +bar: 2 +foo: 1 +foo: 2 +bar: 1 +bar: 2 +bar: 3 +bar: 4 +bar: 5 +bar: 6 +bar: 7 +bar: 8 +bar: 9 +''' +""" + +# bug #11042 +block: + iterator foo: int = + for x in 1..2: + echo "foo: ", x + for y in 1..2: + discard + + for x in foo(): discard + + let bar = iterator: int = + for x in 1..2: + echo "bar: ", x + for y in 1..2: + discard + + for x in bar(): discard + + +block: + iterator foo: int = + for x in 1..2: + echo "foo: ", x + for y in 1..2: + discard + + for x in foo(): discard + + let bar = iterator: int = + for x in 1..9: + echo "bar: ", x + for y in 1..2: + discard + + for x in bar(): discard \ No newline at end of file From bcff13debcbdd8108237c8033e4dc9c38bb331e0 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Wed, 29 Jun 2022 22:37:24 +0800 Subject: [PATCH 2970/3768] dec inLoop after exiting the while scope in computeLiveRanges [backport] (#19918) * dec inLoop after exiting the while scope in computeLiveRanges * add testcase --- compiler/varpartitions.nim | 2 +- tests/arc/tcursorloop.nim | 45 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 tests/arc/tcursorloop.nim diff --git a/compiler/varpartitions.nim b/compiler/varpartitions.nim index d04a7b40c..a93104b06 100644 --- a/compiler/varpartitions.nim +++ b/compiler/varpartitions.nim @@ -851,7 +851,7 @@ proc computeLiveRanges(c: var Partitions; n: PNode) = # connect(graph, cursorVar) inc c.inLoop for child in n: computeLiveRanges(c, child) - inc c.inLoop + dec c.inLoop of nkElifBranch, nkElifExpr, nkElse, nkOfBranch: inc c.inConditional for child in n: computeLiveRanges(c, child) diff --git a/tests/arc/tcursorloop.nim b/tests/arc/tcursorloop.nim new file mode 100644 index 000000000..a37a6a036 --- /dev/null +++ b/tests/arc/tcursorloop.nim @@ -0,0 +1,45 @@ +discard """ + cmd: '''nim c --gc:arc --expandArc:traverse --hint:Performance:off $file''' + nimout: ''' +--expandArc: traverse + +var + it + jt_cursor +try: + `=copy`(it, root) + block :tmp: + while ( + not (it == nil)): + if true: + echo [it.s] + `=copy`(it, it.ri) + jt_cursor = root + if ( + not (jt_cursor == nil)): + echo [jt_cursor.s] + jt_cursor = jt_cursor.ri +finally: + `=destroy`(it) +-- end of expandArc ------------------------ +''' +""" + +type + Node = ref object + le, ri: Node + s: string + +proc traverse(root: Node) = + var it = root + while it != nil: + if true: + echo it.s + it = it.ri + + var jt = root + if jt != nil: + echo jt.s + jt = jt.ri + +traverse(nil) From ad430c0daad16019a6c440ab735a61856523329a Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Thu, 30 Jun 2022 10:20:19 +0200 Subject: [PATCH 2971/3768] once C++, always C++ [backport] (#19938) * once C++, always C++ When using `{.compile: "file.cc".}` in a nim module, even when compiling with `nim c` the C++ compiler should be used - once any C++ file has been compiled, the C++ linker also needs to be used. * more strict C++ check * simplify code --- compiler/extccomp.nim | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 5c074d284..f1dc01f44 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -493,7 +493,10 @@ proc needsExeExt(conf: ConfigRef): bool {.inline.} = (conf.target.hostOS == osWindows) proc useCpp(conf: ConfigRef; cfile: AbsoluteFile): bool = - conf.backend == backendCpp and not cfile.string.endsWith(".c") + # List of possible file extensions taken from gcc + for ext in [".C", ".cc", ".cpp", ".CPP", ".c++", ".cp", ".cxx"]: + if cfile.string.endsWith(ext): return true + false proc envFlags(conf: ConfigRef): string = result = if conf.backend == backendCpp: @@ -501,14 +504,14 @@ proc envFlags(conf: ConfigRef): string = else: getEnv("CFLAGS") -proc getCompilerExe(conf: ConfigRef; compiler: TSystemCC; cfile: AbsoluteFile): string = +proc getCompilerExe(conf: ConfigRef; compiler: TSystemCC; isCpp: bool): string = if compiler == ccEnv: - result = if useCpp(conf, cfile): + result = if isCpp: getEnv("CXX") else: getEnv("CC") else: - result = if useCpp(conf, cfile): + result = if isCpp: CC[compiler].cppCompiler else: CC[compiler].compilerExe @@ -547,22 +550,25 @@ proc ccHasSaneOverflow*(conf: ConfigRef): bool = proc getLinkerExe(conf: ConfigRef; compiler: TSystemCC): string = result = if CC[compiler].linkerExe.len > 0: CC[compiler].linkerExe - elif optMixedMode in conf.globalOptions and conf.backend != backendCpp: CC[compiler].cppCompiler - else: getCompilerExe(conf, compiler, AbsoluteFile"") + else: getCompilerExe(conf, compiler, optMixedMode in conf.globalOptions or conf.backend == backendCpp) proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile, isMainFile = false; produceOutput = false): string = - let c = conf.cCompiler + let + c = conf.cCompiler + isCpp = useCpp(conf, cfile.cname) # We produce files like module.nim.cpp, so the absolute Nim filename is not # cfile.name but `cfile.cname.changeFileExt("")`: var options = cFileSpecificOptions(conf, cfile.nimname, cfile.cname.changeFileExt("").string) - if useCpp(conf, cfile.cname): + if isCpp: # needs to be prepended so that --passc:-std=c++17 can override default. # we could avoid allocation by making cFileSpecificOptions inplace options = CC[c].cppXsupport & ' ' & options + # If any C++ file was compiled, we need to use C++ driver for linking as well + incl conf.globalOptions, optMixedMode var exe = getConfigVar(conf, c, ".exe") - if exe.len == 0: exe = getCompilerExe(conf, c, cfile.cname) + if exe.len == 0: exe = getCompilerExe(conf, c, isCpp) if needsExeExt(conf): exe = addFileExt(exe, "exe") if (optGenDynLib in conf.globalOptions or (conf.hcrOn and not isMainFile)) and @@ -582,7 +588,7 @@ proc getCompileCFileCmd*(conf: ConfigRef; cfile: Cfile, compilePattern = joinPath(conf.cCompilerPath, exe) else: - compilePattern = getCompilerExe(conf, c, cfile.cname) + compilePattern = getCompilerExe(conf, c, isCpp) includeCmd.add(join([CC[c].includeCmd, quoteShell(conf.projectPath.string)])) From 306810a748ab3e0182f798bea7d2792c64ab856a Mon Sep 17 00:00:00 2001 From: Sam Zaydel Date: Thu, 30 Jun 2022 14:16:50 -0700 Subject: [PATCH 2972/3768] Enable nim-lang to build correctly on illumos-based systems (#19952) --- tools/niminst/buildsh.nimf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/niminst/buildsh.nimf b/tools/niminst/buildsh.nimf index 7a05ef342..6b99c49ee 100644 --- a/tools/niminst/buildsh.nimf +++ b/tools/niminst/buildsh.nimf @@ -164,7 +164,7 @@ esac case $ucpu in *i386* | *i486* | *i586* | *i686* | *bepc* | *i86pc* ) - if [ "$isOpenIndiana" = "yes" ] ; then + if [ "$isOpenIndiana" = "yes" ] || [ `uname -o` == "illumos" ] ; then mycpu="amd64" else mycpu="i386" From 2c0aaac3045a63b5e4a24468341bf9ccfc8820cc Mon Sep 17 00:00:00 2001 From: Juan Carlos Date: Thu, 30 Jun 2022 18:18:11 -0300 Subject: [PATCH 2973/3768] jsffi add missing braces (#19948) js codegen add missing whitespaces and braces --- lib/js/jsffi.nim | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/lib/js/jsffi.nim b/lib/js/jsffi.nim index 35cbf2864..aca4fc292 100644 --- a/lib/js/jsffi.nim +++ b/lib/js/jsffi.nim @@ -348,8 +348,8 @@ iterator pairs*(obj: JsObject): (cstring, JsObject) = var k: cstring var v: JsObject {.emit: "for (var `k` in `obj`) {".} - {.emit: " if (!`obj`.hasOwnProperty(`k`)) continue;".} - {.emit: " `v`=`obj`[`k`];".} + {.emit: " if (!`obj`.hasOwnProperty(`k`)) { continue; }".} + {.emit: " `v` = `obj`[`k`];".} yield (k, v) {.emit: "}".} @@ -357,8 +357,8 @@ iterator items*(obj: JsObject): JsObject = ## Yields the `values` of each field in a JsObject, wrapped into a JsObject. var v: JsObject {.emit: "for (var k in `obj`) {".} - {.emit: " if (!`obj`.hasOwnProperty(k)) continue;".} - {.emit: " `v`=`obj`[k];".} + {.emit: " if (!`obj`.hasOwnProperty(k)) { continue; }".} + {.emit: " `v` = `obj`[k];".} yield v {.emit: "}".} @@ -366,7 +366,7 @@ iterator keys*(obj: JsObject): cstring = ## Yields the `names` of each field in a JsObject. var k: cstring {.emit: "for (var `k` in `obj`) {".} - {.emit: " if (!`obj`.hasOwnProperty(`k`)) continue;".} + {.emit: " if (!`obj`.hasOwnProperty(`k`)) { continue; }".} yield k {.emit: "}".} @@ -376,8 +376,8 @@ iterator pairs*[K: JsKey, V](assoc: JsAssoc[K, V]): (K,V) = var k: cstring var v: V {.emit: "for (var `k` in `assoc`) {".} - {.emit: " if (!`assoc`.hasOwnProperty(`k`)) continue;".} - {.emit: " `v`=`assoc`[`k`];".} + {.emit: " if (!`assoc`.hasOwnProperty(`k`)) { continue; }".} + {.emit: " `v` = `assoc`[`k`];".} yield (k.toJsKey(K), v) {.emit: "}".} @@ -385,8 +385,8 @@ iterator items*[K, V](assoc: JsAssoc[K, V]): V = ## Yields the `values` in a JsAssoc. var v: V {.emit: "for (var k in `assoc`) {".} - {.emit: " if (!`assoc`.hasOwnProperty(k)) continue;".} - {.emit: " `v`=`assoc`[k];".} + {.emit: " if (!`assoc`.hasOwnProperty(k)) { continue; }".} + {.emit: " `v` = `assoc`[k];".} yield v {.emit: "}".} @@ -394,7 +394,7 @@ iterator keys*[K: JsKey, V](assoc: JsAssoc[K, V]): K = ## Yields the `keys` in a JsAssoc. var k: cstring {.emit: "for (var `k` in `assoc`) {".} - {.emit: " if (!`assoc`.hasOwnProperty(`k`)) continue;".} + {.emit: " if (!`assoc`.hasOwnProperty(`k`)) { continue; }".} yield k.toJsKey(K) {.emit: "}".} From ce4078acd40bb27de5d05832f486dbc95918a3c9 Mon Sep 17 00:00:00 2001 From: Tanguy Date: Thu, 30 Jun 2022 23:19:04 +0200 Subject: [PATCH 2974/3768] Allow recursive closure iterators (#19939) --- compiler/semexprs.nim | 3 ++- doc/manual.rst | 5 +---- tests/iter/titer_issues.nim | 13 +++++++++++++ 3 files changed, 16 insertions(+), 5 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index ed28d8145..e07a98417 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -886,7 +886,8 @@ proc semOverloadedCallAnalyseEffects(c: PContext, n: PNode, nOrig: PNode, case callee.kind of skMacro, skTemplate: discard else: - if callee.kind == skIterator and callee.id == c.p.owner.id: + if callee.kind == skIterator and callee.id == c.p.owner.id and + not isClosureIterator(c.p.owner.typ): localError(c.config, n.info, errRecursiveDependencyIteratorX % callee.name.s) # error correction, prevents endless for loop elimination in transf. # See bug #2051: diff --git a/doc/manual.rst b/doc/manual.rst index b63a2f68f..571379a87 100644 --- a/doc/manual.rst +++ b/doc/manual.rst @@ -4326,13 +4326,10 @@ Closure iterators and inline iterators have some restrictions: 1. For now, a closure iterator cannot be executed at compile time. 2. `return` is allowed in a closure iterator but not in an inline iterator (but rarely useful) and ends the iteration. -3. Neither inline nor closure iterators can be (directly)* recursive. +3. Inline iterators cannot be recursive. 4. Neither inline nor closure iterators have the special `result` variable. 5. Closure iterators are not supported by the JS backend. -(*) Closure iterators can be co-recursive with a factory proc which results -in similar syntax to a recursive iterator. More details follow. - Iterators that are neither marked `{.closure.}` nor `{.inline.}` explicitly default to being inline, but this may change in future versions of the implementation. diff --git a/tests/iter/titer_issues.nim b/tests/iter/titer_issues.nim index 15fe867c8..1f7e41e69 100644 --- a/tests/iter/titer_issues.nim +++ b/tests/iter/titer_issues.nim @@ -29,6 +29,7 @@ end 9018 @[1, 2] @[1, 2, 3] +1 ''' """ @@ -274,3 +275,15 @@ iterator cc() {.closure.} = break var a2 = cc + +# bug #16876 +block: + iterator a(num: int): int {.closure.} = + if num == 1: + yield num + else: + for i in a(num - 1): + yield i + + for i in a(5): + echo i From 4897c47c8004e85474b760d770d9d09ae1d58771 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sat, 2 Jul 2022 16:51:21 +0800 Subject: [PATCH 2975/3768] closes #15897; add testcase (#19961) --- tests/views/tviews1.nim | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/views/tviews1.nim b/tests/views/tviews1.nim index 249058eb6..b81b17f30 100644 --- a/tests/views/tviews1.nim +++ b/tests/views/tviews1.nim @@ -66,3 +66,14 @@ proc mainB = assert foo.x.y == @[1, 2, 3] mainB() + + +# bug #15897 +type Outer = ref object + value: int +type Inner = object + owner: var Outer + +var o = Outer(value: 1234) +var v = Inner(owner: o).owner.value +doAssert v == 1234 From d2d8f1342b3125aac8e0592da25df61e6feb95b2 Mon Sep 17 00:00:00 2001 From: rockcavera Date: Mon, 4 Jul 2022 08:52:44 -0300 Subject: [PATCH 2976/3768] Fixing `nimRawSetJmp` for vcc and clangcl on Windows (#19959) * fix vcc rawsetjmp * changing `_longjmp()` to `longjmp()` and `_setjmp()` to `setjmp()` * fix * fix setjmp to clangcl on Windows * fix genTrySetjmp() to clangcl on Windows --- compiler/ccgstmts.nim | 19 ++++++++++++------- lib/system/ansi_c.nim | 34 +++++++++++++++++++--------------- 2 files changed, 31 insertions(+), 22 deletions(-) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index e791318ee..3ecf68402 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -1367,13 +1367,18 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) = linefmt(p, cpsStmts, "$1.status = __builtin_setjmp($1.context);$n", [safePoint]) elif isDefined(p.config, "nimRawSetjmp"): if isDefined(p.config, "mswindows"): - # The Windows `_setjmp()` takes two arguments, with the second being an - # undocumented buffer used by the SEH mechanism for stack unwinding. - # Mingw-w64 has been trying to get it right for years, but it's still - # prone to stack corruption during unwinding, so we disable that by setting - # it to NULL. - # More details: https://github.com/status-im/nimbus-eth2/issues/3121 - linefmt(p, cpsStmts, "$1.status = _setjmp($1.context, 0);$n", [safePoint]) + if isDefined(p.config, "vcc") or isDefined(p.config, "clangcl"): + # For the vcc compiler, use `setjmp()` with one argument. + # See https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/setjmp?view=msvc-170 + linefmt(p, cpsStmts, "$1.status = setjmp($1.context);$n", [safePoint]) + else: + # The Windows `_setjmp()` takes two arguments, with the second being an + # undocumented buffer used by the SEH mechanism for stack unwinding. + # Mingw-w64 has been trying to get it right for years, but it's still + # prone to stack corruption during unwinding, so we disable that by setting + # it to NULL. + # More details: https://github.com/status-im/nimbus-eth2/issues/3121 + linefmt(p, cpsStmts, "$1.status = _setjmp($1.context, 0);$n", [safePoint]) else: linefmt(p, cpsStmts, "$1.status = _setjmp($1.context);$n", [safePoint]) else: diff --git a/lib/system/ansi_c.nim b/lib/system/ansi_c.nim index 74f79167a..0dbded126 100644 --- a/lib/system/ansi_c.nim +++ b/lib/system/ansi_c.nim @@ -118,26 +118,30 @@ elif defined(nimBuiltinSetjmp): c_builtin_setjmp(unsafeAddr jmpb[0]) elif defined(nimRawSetjmp) and not defined(nimStdSetjmp): - when defined(windows) and not defined(vcc): + when defined(windows): # No `_longjmp()` on Windows. proc c_longjmp*(jmpb: C_JmpBuf, retval: cint) {. header: "", importc: "longjmp".} - # The Windows `_setjmp()` takes two arguments, with the second being an - # undocumented buffer used by the SEH mechanism for stack unwinding. - # Mingw-w64 has been trying to get it right for years, but it's still - # prone to stack corruption during unwinding, so we disable that by setting - # it to NULL. - # More details: https://github.com/status-im/nimbus-eth2/issues/3121 - when defined(nimHasStyleChecks): - {.push styleChecks: off.} + when defined(vcc) or defined(clangcl): + proc c_setjmp*(jmpb: C_JmpBuf): cint {. + header: "", importc: "setjmp".} + else: + # The Windows `_setjmp()` takes two arguments, with the second being an + # undocumented buffer used by the SEH mechanism for stack unwinding. + # Mingw-w64 has been trying to get it right for years, but it's still + # prone to stack corruption during unwinding, so we disable that by setting + # it to NULL. + # More details: https://github.com/status-im/nimbus-eth2/issues/3121 + when defined(nimHasStyleChecks): + {.push styleChecks: off.} - proc c_setjmp*(jmpb: C_JmpBuf): cint = - proc c_setjmp_win(jmpb: C_JmpBuf, ctx: pointer): cint {. - header: "", importc: "_setjmp".} - c_setjmp_win(jmpb, nil) + proc c_setjmp*(jmpb: C_JmpBuf): cint = + proc c_setjmp_win(jmpb: C_JmpBuf, ctx: pointer): cint {. + header: "", importc: "_setjmp".} + c_setjmp_win(jmpb, nil) - when defined(nimHasStyleChecks): - {.pop.} + when defined(nimHasStyleChecks): + {.pop.} else: proc c_longjmp*(jmpb: C_JmpBuf, retval: cint) {. header: "", importc: "_longjmp".} From 7d0285853f4327a0a4f683e13971dc3429aad1f3 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Tue, 5 Jul 2022 16:11:34 +0800 Subject: [PATCH 2977/3768] rename gc to mm (#19971) --- compiler/msgs.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 9d111b2e2..371a886f4 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -677,7 +677,7 @@ proc genSuccessX*(conf: ConfigRef) = const debugModeHints = "none (DEBUG BUILD, `-d:release` generates faster code)" if conf.cmd in cmdBackends: if conf.backend != backendJs: - build.add "gc: $#; " % $conf.selectedGC + build.add "mm: $#; " % $conf.selectedGC if optThreads in conf.globalOptions: build.add "threads: on; " build.add "opt: " if optOptimizeSpeed in conf.options: build.add "speed" From 01b40dc1d7c7c5331361341bdf305084c799c05b Mon Sep 17 00:00:00 2001 From: Daniel Clarke Date: Wed, 6 Jul 2022 06:29:05 +1000 Subject: [PATCH 2978/3768] Fixes return values of execCmd on macos (#19963) * Fixes return values of execCmd on macos * update tests to use existing structure Co-authored-by: daniel --- lib/pure/osproc.nim | 2 +- tests/stdlib/tosproc.nim | 13 +++++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 512db92a4..666ccdda8 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -1523,7 +1523,7 @@ elif not defined(useNimRtl): header: "".} proc execCmd(command: string): int = - when defined(linux): + when defined(posix): let tmp = csystem(command) result = if tmp == -1: tmp else: exitStatusLikeShell(tmp) else: diff --git a/tests/stdlib/tosproc.nim b/tests/stdlib/tosproc.nim index c54e0d112..f55dd3e21 100644 --- a/tests/stdlib/tosproc.nim +++ b/tests/stdlib/tosproc.nim @@ -29,6 +29,12 @@ when defined(case_testfile): # compiled test file for child process case arg of "exit_0": if true: quit(0) + of "exit_1": + if true: quit(1) + of "exit_2": + if true: quit(2) + of "exit_42": + if true: quit(42) of "exitnow_139": if true: exitnow(139) of "c_exit2_139": @@ -115,6 +121,13 @@ else: # main driver runTest("c_exit2_139", 139) runTest("quit_139", 139) + block execCmdTest: + let output = compileNimProg("-d:release -d:case_testfile", "D20220705T221100") + doAssert execCmd(output & " exit_0") == 0 + doAssert execCmd(output & " exit_1") == 1 + doAssert execCmd(output & " exit_2") == 2 + doAssert execCmd(output & " exit_42") == 42 + import std/streams block execProcessTest: From 430a1793075866179b34790a461936023fca1c0a Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Wed, 6 Jul 2022 19:06:41 +0800 Subject: [PATCH 2979/3768] default threads on (#19368) * default threads on * make rst gcsafe * ignore threads option for nimscript * threads off * use createShared for threads * test without threads * avr threds off * avr threads off * async threads off * threads off * fix ci * restore option * make CI pleased * fix ic tests * Update config.nims * add changelog * Update changelog.md Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> Co-authored-by: konsumlamm <44230978+konsumlamm@users.noreply.github.com> --- changelog.md | 2 ++ config/nim.cfg | 2 ++ testament/categories.nim | 10 +++++----- tests/avr/thello.nim | 2 +- tests/destructor/trecursive.nim | 2 +- tests/ic/config.nims | 2 ++ tests/manyloc/standalone2/tavr.nim.cfg | 1 + tests/niminaction/Chapter7/Tweeter/src/tweeter.nim | 1 + tests/stdlib/ttasks.nim | 2 +- tests/system/tgcnone.nim | 2 +- 10 files changed, 17 insertions(+), 9 deletions(-) create mode 100644 tests/ic/config.nims diff --git a/changelog.md b/changelog.md index 98655eaa3..c35a403e6 100644 --- a/changelog.md +++ b/changelog.md @@ -26,6 +26,8 @@ becomes an alias for `addr`. - The `gc:v2` option is removed. +- The `threads:on` option becomes the default. + ## Standard library additions and changes [//]: # "Changes:" diff --git a/config/nim.cfg b/config/nim.cfg index 7fe279f56..37d12a02d 100644 --- a/config/nim.cfg +++ b/config/nim.cfg @@ -17,6 +17,8 @@ cc = gcc hint[LineTooLong]=off #hint[XDeclaredButNotUsed]=off +threads:on + # Examples of how to setup a cross-compiler: # Nim can target architectures and OSes different than the local host # Syntax: ..gcc.exe = "" diff --git a/testament/categories.nim b/testament/categories.nim index a092fec84..43ea9f0ee 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -59,10 +59,10 @@ proc runBasicDLLTest(c, r: var TResults, cat: Category, options: string) = var test2 = makeTest("tests/dll/server.nim", options & " --threads:on" & rpath, cat) test2.spec.action = actionCompile testSpec c, test2 - var test3 = makeTest("lib/nimhcr.nim", options & " --outdir:tests/dll" & rpath, cat) + var test3 = makeTest("lib/nimhcr.nim", options & " --threads:off --outdir:tests/dll" & rpath, cat) test3.spec.action = actionCompile testSpec c, test3 - var test4 = makeTest("tests/dll/visibility.nim", options & " --app:lib" & rpath, cat) + var test4 = makeTest("tests/dll/visibility.nim", options & " --threads:off --app:lib" & rpath, cat) test4.spec.action = actionCompile testSpec c, test4 @@ -77,13 +77,13 @@ proc runBasicDLLTest(c, r: var TResults, cat: Category, options: string) = defer: putEnv(libpathenv, libpath) testSpec r, makeTest("tests/dll/client.nim", options & " --threads:on" & rpath, cat) - testSpec r, makeTest("tests/dll/nimhcr_unit.nim", options & rpath, cat) - testSpec r, makeTest("tests/dll/visibility.nim", options & rpath, cat) + testSpec r, makeTest("tests/dll/nimhcr_unit.nim", options & " --threads:off" & rpath, cat) + testSpec r, makeTest("tests/dll/visibility.nim", options & " --threads:off" & rpath, cat) if "boehm" notin options: # force build required - see the comments in the .nim file for more details var hcri = makeTest("tests/dll/nimhcr_integration.nim", - options & " --forceBuild --hotCodeReloading:on" & rpath, cat) + options & " --threads:off --forceBuild --hotCodeReloading:on" & rpath, cat) let nimcache = nimcacheDir(hcri.name, hcri.options, getTestSpecTarget()) let cmd = prepareTestCmd(hcri.spec.getCmd, hcri.name, hcri.options, nimcache, getTestSpecTarget()) diff --git a/tests/avr/thello.nim b/tests/avr/thello.nim index a0191815c..7ebaeae5f 100644 --- a/tests/avr/thello.nim +++ b/tests/avr/thello.nim @@ -1,5 +1,5 @@ discard """ - cmd: "nim c --compileOnly --os:standalone --exceptions:quirky -d:noSignalHandler -d:danger $file" + cmd: "nim c --compileOnly --os:standalone --exceptions:quirky -d:noSignalHandler -d:danger --threads:off $file" action: "compile" """ diff --git a/tests/destructor/trecursive.nim b/tests/destructor/trecursive.nim index 17a40e5a9..e7afa6ba9 100644 --- a/tests/destructor/trecursive.nim +++ b/tests/destructor/trecursive.nim @@ -47,7 +47,7 @@ proc `=destroy`(x: var MyObject) = proc `=`(x: var MyObject, y: MyObject) {.error.} proc newMyObject(i: int): MyObject = - result.p = create(int) + result.p = createShared(int) result.p[] = i proc test: seq[MyObject] = diff --git a/tests/ic/config.nims b/tests/ic/config.nims new file mode 100644 index 000000000..76b29a6aa --- /dev/null +++ b/tests/ic/config.nims @@ -0,0 +1,2 @@ +when defined(windows): + --threads:off diff --git a/tests/manyloc/standalone2/tavr.nim.cfg b/tests/manyloc/standalone2/tavr.nim.cfg index e5291969d..2a31618d0 100644 --- a/tests/manyloc/standalone2/tavr.nim.cfg +++ b/tests/manyloc/standalone2/tavr.nim.cfg @@ -2,3 +2,4 @@ --cpu:avr --os:standalone --compileOnly +--threads:off diff --git a/tests/niminaction/Chapter7/Tweeter/src/tweeter.nim b/tests/niminaction/Chapter7/Tweeter/src/tweeter.nim index fe39278fb..2fac949b9 100644 --- a/tests/niminaction/Chapter7/Tweeter/src/tweeter.nim +++ b/tests/niminaction/Chapter7/Tweeter/src/tweeter.nim @@ -1,5 +1,6 @@ discard """ action: compile +matrix: "--threads:off" """ import asyncdispatch, times diff --git a/tests/stdlib/ttasks.nim b/tests/stdlib/ttasks.nim index 75fed9f9b..e90823aba 100644 --- a/tests/stdlib/ttasks.nim +++ b/tests/stdlib/ttasks.nim @@ -1,6 +1,6 @@ discard """ targets: "c cpp" - matrix: "--gc:orc" + matrix: "--gc:orc --threads:off" """ import std/[tasks, strformat] diff --git a/tests/system/tgcnone.nim b/tests/system/tgcnone.nim index 700176d5f..47c6c6014 100644 --- a/tests/system/tgcnone.nim +++ b/tests/system/tgcnone.nim @@ -1,5 +1,5 @@ discard """ - matrix: "--gc:none -d:useMalloc" + matrix: "--gc:none -d:useMalloc --threads:off" """ # bug #15617 let x = 4 From dcb28fd061743ba141b3914501512b5eb352aa1b Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Thu, 7 Jul 2022 20:14:05 +0800 Subject: [PATCH 2980/3768] cache rope when threads are enabled (#19981) * cache rope * add threadvar --- compiler/ropes.nim | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/compiler/ropes.nim b/compiler/ropes.nim index a44d84ddc..610159c75 100644 --- a/compiler/ropes.nim +++ b/compiler/ropes.nim @@ -86,13 +86,12 @@ proc newRope(data: string = ""): Rope = result.L = -data.len result.data = data -when not compileOption("threads"): - var - cache: array[0..2048*2 - 1, Rope] +var + cache {.threadvar.} : array[0..2048*2 - 1, Rope] - proc resetRopeCache* = - for i in low(cache)..high(cache): - cache[i] = nil +proc resetRopeCache* = + for i in low(cache)..high(cache): + cache[i] = nil proc ropeInvariant(r: Rope): bool = if r == nil: From ad0aee535435ac9b22c2ee9ef02085ffdc901d48 Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Thu, 7 Jul 2022 15:26:58 +0200 Subject: [PATCH 2981/3768] sysrand: fix syscall signature [backport] (#19982) sysrand: fix syscall signature `syscall` is a `C` varags function --- lib/std/sysrand.nim | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/std/sysrand.nim b/lib/std/sysrand.nim index 1b7b2c024..4ee25d01e 100644 --- a/lib/std/sysrand.nim +++ b/lib/std/sysrand.nim @@ -170,9 +170,8 @@ elif defined(linux) and not defined(nimNoGetRandom) and not defined(emscripten): const syscallHeader = """#include #include """ - proc syscall( - n: clong, buf: pointer, bufLen: cint, flags: cuint - ): clong {.importc: "syscall", header: syscallHeader.} + proc syscall(n: clong): clong {. + importc: "syscall", varargs, header: syscallHeader.} # When reading from the urandom source (GRND_RANDOM is not set), # getrandom() will block until the entropy pool has been # initialized (unless the GRND_NONBLOCK flag was specified). If a @@ -211,7 +210,7 @@ elif defined(zephyr): proc sys_csrand_get(dst: pointer, length: csize_t): cint {.importc: "sys_csrand_get", header: "".} # Fill the destination buffer with cryptographically secure # random data values - # + # proc getRandomImpl(p: pointer, size: int): int {.inline.} = # 0 if success, -EIO if entropy reseed error From 7a29a782f84ebceec0fb5c3d39bb484621831fca Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Sat, 9 Jul 2022 06:46:05 +0200 Subject: [PATCH 2982/3768] removed caching logic; saves 400MB for an ORC booting compiler (#19989) --- compiler/injectdestructors.nim | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 6500c5bc7..867d30d96 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -80,12 +80,6 @@ import sets, hashes proc hash(n: PNode): Hash = hash(cast[pointer](n)) -proc aliasesCached(cache: var Table[(PNode, PNode), AliasKind], obj, field: PNode): AliasKind = - let key = (obj, field) - if not cache.hasKey(key): - cache[key] = aliases(obj, field) - cache[key] - type State = ref object lastReads: IntSet @@ -116,9 +110,8 @@ proc mergeStates(a: var State, b: sink State) = a.alreadySeen.incl b.alreadySeen proc computeLastReadsAndFirstWrites(cfg: ControlFlowGraph) = - var cache = initTable[(PNode, PNode), AliasKind]() template aliasesCached(obj, field: PNode): AliasKind = - aliasesCached(cache, obj, field) + aliases(obj, field) var cfg = cfg preprocessCfg(cfg) From e8ee2f9c2ad06cad2f62fe7505acbb43530f28d7 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sat, 9 Jul 2022 17:28:14 +0800 Subject: [PATCH 2983/3768] update section regarding `std/assertions` in changelog (#19992) Update changelog.md --- changelog.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index c35a403e6..770393e13 100644 --- a/changelog.md +++ b/changelog.md @@ -22,7 +22,7 @@ - `addr` is now available for all addressable locations, `unsafeAddr` is deprecated and becomes an alias for `addr`. -- io is about to move out of system; use `-d:nimPreviewSlimSystem` and import `std/syncio`. +- `io` and `assertions` are about to move out of system; use `-d:nimPreviewSlimSystem`, import `std/syncio` and import `std/assertions`. - The `gc:v2` option is removed. From ed2bf02a511ae230243ce384ad16c318ca3fe661 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Sun, 10 Jul 2022 15:37:15 +0800 Subject: [PATCH 2984/3768] remove `when declared(cache)`; cache is always there (#19991) --- compiler/ropes.nim | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/compiler/ropes.nim b/compiler/ropes.nim index 610159c75..677a3ce09 100644 --- a/compiler/ropes.nim +++ b/compiler/ropes.nim @@ -111,16 +111,13 @@ var gCacheMisses* = 0 var gCacheIntTries* = 0 proc insertInCache(s: string): Rope = - when declared(cache): - inc gCacheTries - var h = hash(s) and high(cache) - result = cache[h] - if isNil(result) or result.data != s: - inc gCacheMisses - result = newRope(s) - cache[h] = result - else: + inc gCacheTries + var h = hash(s) and high(cache) + result = cache[h] + if isNil(result) or result.data != s: + inc gCacheMisses result = newRope(s) + cache[h] = result proc rope*(s: string): Rope = ## Converts a string to a rope. From a90763ebd762a4862e2af1dab7d4429f4e984a8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20M=20G=C3=B3mez?= Date: Sun, 10 Jul 2022 15:40:26 +0200 Subject: [PATCH 2985/3768] Fixes Compilation error with --app:lib (#19965) Fixes Compilation error with --app:lib when a module tries to pull os.paramStr on posix by throwing a runtime exception instead. More details here: #19964 --- lib/pure/os.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 247a0d089..a60e913f1 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -2939,7 +2939,7 @@ elif defined(genode): proc paramCount*(): int = raise newException(OSError, "paramCount is not implemented on Genode") -elif weirdTarget: +elif weirdTarget or (defined(posix) and appType == "lib"): proc paramStr*(i: int): string {.tags: [ReadIOEffect].} = raise newException(OSError, "paramStr is not implemented on current platform") From fb5fbf1e087563f0288b8ed684c8dcc1891730b0 Mon Sep 17 00:00:00 2001 From: Tanguy Date: Mon, 11 Jul 2022 11:28:52 +0200 Subject: [PATCH 2986/3768] Fix nested finally handling in closureiters [backport] (#19933) * Fix nested finally handling in closureiters * Fix CI * review comment * third time the charm * Update compiler/closureiters.nim Co-authored-by: Dominik Picheta Co-authored-by: Dominik Picheta --- compiler/closureiters.nim | 31 ++++++++++++--- tests/iter/titer_issues.nim | 75 +++++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 6 deletions(-) diff --git a/compiler/closureiters.nim b/compiler/closureiters.nim index 6370d0dcb..2848942fa 100644 --- a/compiler/closureiters.nim +++ b/compiler/closureiters.nim @@ -121,7 +121,10 @@ # yield 2 # if :unrollFinally: # This node is created by `newEndFinallyNode` # if :curExc.isNil: -# return :tmpResult +# if nearestFinally == 0: +# return :tmpResult +# else: +# :state = nearestFinally # bubble up # else: # closureIterSetupExc(nil) # raise @@ -807,7 +810,10 @@ proc newEndFinallyNode(ctx: var Ctx, info: TLineInfo): PNode = # Generate the following code: # if :unrollFinally: # if :curExc.isNil: - # return :tmpResult + # if nearestFinally == 0: + # return :tmpResult + # else: + # :state = nearestFinally # bubble up # else: # raise let curExc = ctx.newCurExcAccess() @@ -816,11 +822,17 @@ proc newEndFinallyNode(ctx: var Ctx, info: TLineInfo): PNode = let cmp = newTree(nkCall, newSymNode(ctx.g.getSysMagic(info, "==", mEqRef), info), curExc, nilnode) cmp.typ = ctx.g.getSysType(info, tyBool) - let asgn = newTree(nkFastAsgn, - newSymNode(getClosureIterResult(ctx.g, ctx.fn, ctx.idgen), info), - ctx.newTmpResultAccess()) + let retStmt = + if ctx.nearestFinally == 0: + # last finally, we can return + let asgn = newTree(nkFastAsgn, + newSymNode(getClosureIterResult(ctx.g, ctx.fn, ctx.idgen), info), + ctx.newTmpResultAccess()) + newTree(nkReturnStmt, asgn) + else: + # bubble up to next finally + newTree(nkGotoState, ctx.g.newIntLit(info, ctx.nearestFinally)) - let retStmt = newTree(nkReturnStmt, asgn) let branch = newTree(nkElifBranch, cmp, retStmt) let nullifyExc = newTree(nkCall, newSymNode(ctx.g.getCompilerProc("closureIterSetupExc")), nilnode) @@ -864,6 +876,13 @@ proc transformReturnsInTry(ctx: var Ctx, n: PNode): PNode = of nkSkip: discard + of nkTryStmt: + if n.hasYields: + # the inner try will handle these transformations + discard + else: + for i in 0.. try + iterator p1: int {.closure.} = + try: + yield 0 + try: + return + finally: + echo "nested finally" + echo "shouldn't run" + finally: + echo "outer finally" + echo "shouldn't run" + + for _ in p1(): + discard + + # try -> try yield + iterator p2: int {.closure.} = + try: + try: + yield 0 + return + finally: + echo "nested finally" + echo "shouldn't run" + finally: + echo "outer finally" + echo "shouldn't run" + + for _ in p2(): + discard + + # try yield -> try yield + iterator p3: int {.closure.} = + try: + yield 0 + try: + yield 0 + return + finally: + echo "nested finally" + echo "shouldn't run" + finally: + echo "outer finally" + echo "shouldn't run" + + for _ in p3(): + discard + + # try -> try + iterator p4: int {.closure.} = + try: + try: + return + finally: + echo "nested finally" + echo "shouldn't run" + finally: + echo "outer finally" + echo "shouldn't run" + + for _ in p4(): + discard From cf1c14936670ef0c175149f6fba4dffc1cf4ba43 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Mon, 11 Jul 2022 23:27:01 +0800 Subject: [PATCH 2987/3768] tracking the memory usage of orc-booting compiler for each commit (#19941) * yaml * pub * redo * let's comment * now action * newly * code name * build * ready * remove submodule * build * modify name * fix * rephrase * trigger when PR is merged --- .github/workflows/ci_publish.yml | 90 ++++++++++++++++++++++++++++++++ ci/action.nim | 28 ++++++++++ 2 files changed, 118 insertions(+) create mode 100644 .github/workflows/ci_publish.yml create mode 100644 ci/action.nim diff --git a/.github/workflows/ci_publish.yml b/.github/workflows/ci_publish.yml new file mode 100644 index 000000000..94ea5ff8f --- /dev/null +++ b/.github/workflows/ci_publish.yml @@ -0,0 +1,90 @@ +name: Tracking orc-booting compiler memory usage + +on: + push: + branches: + - devel + + +jobs: + build: + strategy: + fail-fast: false + matrix: + os: [ubuntu-20.04] + cpu: [amd64] + name: '${{ matrix.os }}' + runs-on: ${{ matrix.os }} + steps: + - name: 'Checkout' + uses: actions/checkout@v2 + with: + fetch-depth: 2 + + - name: 'Install node.js 16.x' + uses: actions/setup-node@v2 + with: + node-version: '16.x' + + - name: 'Install dependencies (Linux amd64)' + if: runner.os == 'Linux' && matrix.cpu == 'amd64' + run: | + sudo apt-fast update -qq + DEBIAN_FRONTEND='noninteractive' \ + sudo apt-fast install --no-install-recommends -yq \ + libcurl4-openssl-dev libgc-dev libsdl1.2-dev libsfml-dev \ + valgrind libc6-dbg libblas-dev xorg-dev + - name: 'Install dependencies (macOS)' + if: runner.os == 'macOS' + run: brew install boehmgc make sfml gtk+3 + - name: 'Install dependencies (Windows)' + if: runner.os == 'Windows' + shell: bash + run: | + set -e + . ci/funs.sh + nimInternalInstallDepsWindows + echo_run echo "${{ github.workspace }}/dist/mingw64/bin" >> "${GITHUB_PATH}" + + - name: 'Add build binaries to PATH' + shell: bash + run: echo "${{ github.workspace }}/bin" >> "${GITHUB_PATH}" + + - name: 'System information' + shell: bash + run: . ci/funs.sh && nimCiSystemInfo + + - name: 'Build csourcesAny' + shell: bash + run: . ci/funs.sh && nimBuildCsourcesIfNeeded CC=gcc ucpu='${{ matrix.cpu }}' + + - name: 'Build koch' + shell: bash + run: nim c koch + + - name: 'Build Nim' + shell: bash + run: ./koch boot -d:release -d:nimStrictMode --lib:lib + + - name: 'Action' + shell: bash + run: nim c -r -d:release ci/action.nim + + - name: 'Comment' + uses: actions/github-script@v6 + with: + script: | + const fs = require('fs'); + + try { + const data = fs.readFileSync('ci/nimcache/results.txt', 'utf8'); + github.rest.issues.createComment({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + body: data + }) + } catch (err) { + console.error(err); + } + diff --git a/ci/action.nim b/ci/action.nim new file mode 100644 index 000000000..8c3260096 --- /dev/null +++ b/ci/action.nim @@ -0,0 +1,28 @@ +import std/[strutils, os, osproc, parseutils, strformat] + + +proc main() = + var msg = "" + const cmd = "./koch boot --gc:orc -d:release" + + let (output, exitCode) = execCmdEx(cmd) + + doAssert exitCode == 0, output + + var start = rfind(output, "Hint: gc") + if start < 0: + start = rfind(output, "Hint: mm") + doAssert parseUntil(output, msg, "; proj", start) > 0, output + + let (commitHash, _) = execCmdEx("""git log --format="%H" -n 1""") + + let welcomeMessage = fmt"""Thanks for your hard work on this PR! +The lines below are statistics of the Nim compiler built from {commitHash} + +{msg} +""" + createDir "ci/nimcache" + writeFile "ci/nimcache/results.txt", welcomeMessage + +when isMainModule: + main() \ No newline at end of file From d0bae989d63f91345cd0991e00502c05d18dbf6b Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Tue, 12 Jul 2022 09:05:50 +0800 Subject: [PATCH 2988/3768] fix github script failure (#20006) * yaml * pub * redo * let's comment * now action * newly * code name * build * ready * remove submodule * build * modify name * fix * rephrase * trigger when PR is merged * fix CI failure --- .github/workflows/ci_publish.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci_publish.yml b/.github/workflows/ci_publish.yml index 94ea5ff8f..90a3438a7 100644 --- a/.github/workflows/ci_publish.yml +++ b/.github/workflows/ci_publish.yml @@ -1,13 +1,13 @@ name: Tracking orc-booting compiler memory usage on: - push: - branches: - - devel + pull_request: + types: [closed] jobs: build: + if: github.event.pull_request.merged == true strategy: fail-fast: false matrix: From a97b00ad8d18d836d693c224909b607d2af0c4b1 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Tue, 12 Jul 2022 19:07:18 +0800 Subject: [PATCH 2989/3768] try to optimize hot spots for orc-booting compiler (#20001) * optimize hot spots fro orc-booting compiler * remove GC_ref * minor * remove comments * Revert "minor" This reverts commit 4965a190a2d6457044faa1442795e55bdad57602. * emulate cursor --- compiler/astalgo.nim | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index bc5b7d6e1..22f4257fc 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -18,6 +18,9 @@ import when defined(nimPreviewSlimSystem): import std/assertions +when not defined(nimHasCursor): + {.pragma: cursor.} + proc hashNode*(p: RootRef): Hash proc treeToYaml*(conf: ConfigRef; n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope # Convert a tree into its YAML representation; this is used by the @@ -815,16 +818,21 @@ type name*: PIdent proc nextIdentIter*(ti: var TIdentIter, tab: TStrTable): PSym = + # hot spots var h = ti.h and high(tab.data) var start = h - result = tab.data[h] - while result != nil: - if result.name.id == ti.name.id: break + var p {.cursor.} = tab.data[h] + while p != nil: + if p.name.id == ti.name.id: break h = nextTry(h, high(tab.data)) if h == start: - result = nil + p = nil break - result = tab.data[h] + p = tab.data[h] + if p != nil: + result = p # increase the count + else: + result = nil ti.h = nextTry(h, high(tab.data)) proc initIdentIter*(ti: var TIdentIter, tab: TStrTable, s: PIdent): PSym = From 5c510a9ab96f265ffb0323a18deadb5bb175cfdc Mon Sep 17 00:00:00 2001 From: metagn <10591326+metagn@users.noreply.github.com> Date: Tue, 12 Jul 2022 19:03:58 +0300 Subject: [PATCH 2990/3768] allow dots in defined() (#20010) * allow dots in defined() refs https://github.com/nim-lang/RFCs/issues/181 * mention accents in older versions --- changelog.md | 3 +++ compiler/semexprs.nim | 14 +++++++++++++- tests/misc/tdefine.nim | 19 +++++++++++++++++-- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index 770393e13..7498a9567 100644 --- a/changelog.md +++ b/changelog.md @@ -101,6 +101,9 @@ becomes an alias for `addr`. - Full command syntax and block arguments i.e. `foo a, b: c` are now allowed for the right-hand side of type definitions in type sections. Previously they would error with "invalid indentation". +- `defined` now accepts identifiers separated by dots, i.e. `defined(a.b.c)`. + In the command line, this is defined as `-d:a.b.c`. Older versions can + use accents as in ``defined(`a.b.c`)`` to access such defines. ## Compiler changes diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index e07a98417..fed9cae21 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1938,11 +1938,23 @@ proc semYield(c: PContext, n: PNode): PNode = elif c.p.owner.typ[0] != nil: localError(c.config, n.info, errGenerated, "yield statement must yield a value") +proc considerQuotedIdentOrDot(c: PContext, n: PNode, origin: PNode = nil): PIdent = + if n.kind == nkDotExpr: + let a = considerQuotedIdentOrDot(c, n[0], origin).s + let b = considerQuotedIdentOrDot(c, n[1], origin).s + var s = newStringOfCap(a.len + b.len + 1) + s.add(a) + s.add('.') + s.add(b) + result = getIdent(c.cache, s) + else: + result = considerQuotedIdent(c, n, origin) + proc semDefined(c: PContext, n: PNode): PNode = checkSonsLen(n, 2, c.config) # we replace this node by a 'true' or 'false' node: result = newIntNode(nkIntLit, 0) - result.intVal = ord isDefined(c.config, considerQuotedIdent(c, n[1], n).s) + result.intVal = ord isDefined(c.config, considerQuotedIdentOrDot(c, n[1], n).s) result.info = n.info result.typ = getSysType(c.graph, n.info, tyBool) diff --git a/tests/misc/tdefine.nim b/tests/misc/tdefine.nim index f1c6e7a96..c4d11c941 100644 --- a/tests/misc/tdefine.nim +++ b/tests/misc/tdefine.nim @@ -1,6 +1,6 @@ discard """ joinable: false -cmd: "nim c -d:booldef -d:booldef2=false -d:intdef=2 -d:strdef=foobar -r $file" +cmd: "nim c -d:booldef -d:booldef2=false -d:intdef=2 -d:strdef=foobar -d:namespaced.define=false -d:double.namespaced.define -r $file" """ const booldef {.booldefine.} = false @@ -27,4 +27,19 @@ type T = object when intdef2 == 1: field2: int when strdef2 == "abc": - field3: int \ No newline at end of file + field3: int + +doAssert not defined(booldef3) +doAssert not defined(intdef2) +doAssert not defined(strdef2) +discard T(field1: 1, field2: 2, field3: 3) + +doAssert defined(namespaced.define) +const `namespaced.define` {.booldefine.} = true +doAssert not `namespaced.define` + +doAssert defined(double.namespaced.define) +const `double.namespaced.define` {.booldefine.} = false +doAssert `double.namespaced.define` + +doAssert not defined(namespaced.butnotdefined) From 0180c6179aa0ff2dacaa6c063c0fa24cf459a84d Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Wed, 13 Jul 2022 01:35:08 +0800 Subject: [PATCH 2991/3768] fix #18735; genDepend broken for duplicate module names in separate folders (#19988) --- compiler/depends.nim | 58 ++++++++++++++++++++++++++++++++++++++------ compiler/options.nim | 2 +- 2 files changed, 52 insertions(+), 8 deletions(-) diff --git a/compiler/depends.nim b/compiler/depends.nim index 30fc961c5..1dcdec989 100644 --- a/compiler/depends.nim +++ b/compiler/depends.nim @@ -9,10 +9,12 @@ # This module implements a dependency file generator. -import - options, ast, ropes, idents, passes, modulepaths, pathutils +import options, ast, ropes, passes, pathutils, msgs, lineinfos -from modulegraphs import ModuleGraph, PPassContext +import modulegraphs + +import std/[os, strutils, parseutils] +import std/private/globs type TGen = object of PPassContext @@ -28,6 +30,50 @@ proc addDependencyAux(b: Backend; importing, imported: string) = b.dotGraph.addf("\"$1\" -> \"$2\";$n", [rope(importing), rope(imported)]) # s1 -> s2_4[label="[0-9]"]; +proc toNimblePath(s: string, isStdlib: bool): string = + const stdPrefix = "std/" + const pkgPrefix = "pkg/" + if isStdlib: + let sub = "lib/" + var start = s.find(sub) + if start < 0: + doAssert false + else: + start += sub.len + let base = s[start..^1] + + if base.startsWith("system") or base.startsWith("std"): + result = base + else: + for dir in stdlibDirs: + if base.startsWith(dir): + return stdPrefix & base.splitFile.name + + result = stdPrefix & base + else: + var sub = getEnv("NIMBLE_DIR") + if sub.len == 0: + sub = ".nimble/pkgs/" + else: + sub.add "/pkgs/" + var start = s.find(sub) + if start < 0: + result = s + else: + start += sub.len + start += skipUntil(s, '/', start) + start += 1 + result = pkgPrefix & s[start..^1] + +proc addDependency(c: PPassContext, g: PGen, b: Backend, n: PNode) = + doAssert n.kind == nkSym, $n.kind + + let path = splitFile(toProjPath(g.config, n.sym.position.FileIndex)) + let modulePath = splitFile(toProjPath(g.config, g.module.position.FileIndex)) + let parent = nativeToUnixPath(modulePath.dir / modulePath.name).toNimblePath(belongsToStdlib(g.graph, g.module)) + let child = nativeToUnixPath(path.dir / path.name).toNimblePath(belongsToStdlib(g.graph, n.sym)) + addDependencyAux(b, parent, child) + proc addDotDependency(c: PPassContext, n: PNode): PNode = result = n let g = PGen(c) @@ -35,11 +81,9 @@ proc addDotDependency(c: PPassContext, n: PNode): PNode = case n.kind of nkImportStmt: for i in 0.. 0 and not ret.startsWith ".." -const stdlibDirs = [ +const stdlibDirs* = [ "pure", "core", "arch", "pure/collections", "pure/concurrency", From af140966ea5b58ea248459acb74bf84537deb423 Mon Sep 17 00:00:00 2001 From: metagn <10591326+metagn@users.noreply.github.com> Date: Wed, 13 Jul 2022 00:24:17 +0300 Subject: [PATCH 2992/3768] Add #19406 changes to changelog (#20011) --- changelog.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/changelog.md b/changelog.md index 7498a9567..68b133040 100644 --- a/changelog.md +++ b/changelog.md @@ -66,6 +66,12 @@ becomes an alias for `addr`. ## Language changes +- [Case statement macros](manual.html#macros-case-statement-macros) are no longer experimental, + meaning you no longer need to enable the experimental switch `caseStmtMacros` to use them. +- Templates now accept [macro pragmas](https://nim-lang.github.io/Nim/manual.html#userminusdefined-pragmas-macro-pragmas). +- Macro pragmas for var/let/const sections have been redesigned in a way that works + similarly to routine macro pragmas. The new behavior is documented in the + [experimental manual](https://nim-lang.github.io/Nim/manual_experimental.html#extended-macro-pragmas). - Pragma macros on type definitions can now return `nnkTypeSection` nodes as well as `nnkTypeDef`, allowing multiple type definitions to be injected in place of the original type definition. @@ -96,8 +102,6 @@ becomes an alias for `addr`. x, y, z: int Baz = object ``` -- [Case statement macros](manual.html#macros-case-statement-macros) are no longer experimental, - meaning you no longer need to enable the experimental switch `caseStmtMacros` to use them. - Full command syntax and block arguments i.e. `foo a, b: c` are now allowed for the right-hand side of type definitions in type sections. Previously they would error with "invalid indentation". From 489f6ddfefc4993af97eb2884b6d8173a4ee8b9d Mon Sep 17 00:00:00 2001 From: Daniel Clarke Date: Wed, 13 Jul 2022 22:55:33 +1000 Subject: [PATCH 2993/3768] fix #20012 (#20013) * replace gcc asm with __asm__ and add a test * update test case to specify gcc or clang and not cpp Co-authored-by: daniel --- compiler/extccomp.nim | 2 +- tests/compiler/tasm.nim | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 tests/compiler/tasm.nim diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index f1dc01f44..23c43cb67 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -86,7 +86,7 @@ compiler gcc: linkLibCmd: " -l$1", debug: "", pic: "-fPIC", - asmStmtFrmt: "asm($1);$n", + asmStmtFrmt: "__asm__($1);$n", structStmtFmt: "$1 $3 $2 ", # struct|union [packed] $name produceAsm: gnuAsmListing, cppXsupport: "-std=gnu++14 -funsigned-char", diff --git a/tests/compiler/tasm.nim b/tests/compiler/tasm.nim new file mode 100644 index 000000000..9f60231e0 --- /dev/null +++ b/tests/compiler/tasm.nim @@ -0,0 +1,15 @@ +proc testAsm() = + let src = 41 + var dst = 0 + + asm """ + mov %1, %0\n\t + add $1, %0 + : "=r" (`dst`) + : "r" (`src`)""" + + doAssert dst == 42 + +when defined(gcc) or defined(clang) and not defined(cpp): + {.passc: "-std=c99".} + testAsm() \ No newline at end of file From 73ee34f56fc267e6b8b29d12b22f969f7f8a7788 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Wed, 13 Jul 2022 22:37:31 +0800 Subject: [PATCH 2994/3768] use pull_request_target (#20020) --- .github/workflows/ci_publish.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci_publish.yml b/.github/workflows/ci_publish.yml index 90a3438a7..a56ff9a7e 100644 --- a/.github/workflows/ci_publish.yml +++ b/.github/workflows/ci_publish.yml @@ -1,7 +1,7 @@ name: Tracking orc-booting compiler memory usage on: - pull_request: + pull_request_target: types: [closed] From 93211a2bddf8e6a806c3fd4e9322ca21f0a2a9c4 Mon Sep 17 00:00:00 2001 From: silent-observer Date: Thu, 14 Jul 2022 13:03:16 +0300 Subject: [PATCH 2995/3768] Add sink and lent annotations to the critbits module (#20021) * Add sink and lent to critbits * Remove lent for pairs I guess lent doesn't work well inside tuples * Remove lent from template in critbits Apparently this also doesn't work, because some checks failed --- lib/pure/collections/critbits.nim | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/pure/collections/critbits.nim b/lib/pure/collections/critbits.nim index e12984995..dca60b37b 100644 --- a/lib/pure/collections/critbits.nim +++ b/lib/pure/collections/critbits.nim @@ -197,7 +197,7 @@ proc missingOrExcl*[T](c: var CritBitTree[T], key: string): bool = discard exclImpl(c, key) result = c.count == oldCount -proc containsOrIncl*[T](c: var CritBitTree[T], key: string, val: T): bool = +proc containsOrIncl*[T](c: var CritBitTree[T], key: string, val: sink T): bool = ## Returns true if `c` contains the given `key`. If the key does not exist, ## `c[key] = val` is performed. ## @@ -270,7 +270,7 @@ proc incl*(c: var CritBitTree[void], key: string) = discard rawInsert(c, key) -proc incl*[T](c: var CritBitTree[T], key: string, val: T) = +proc incl*[T](c: var CritBitTree[T], key: string, val: sink T) = ## Inserts `key` with value `val` into `c`. ## ## **See also:** @@ -284,7 +284,7 @@ proc incl*[T](c: var CritBitTree[T], key: string, val: T) = var n = rawInsert(c, key) n.val = val -proc `[]=`*[T](c: var CritBitTree[T], key: string, val: T) = +proc `[]=`*[T](c: var CritBitTree[T], key: string, val: sink T) = ## Alias for `incl <#incl,CritBitTree[T],string,T>`_. ## ## **See also:** @@ -300,7 +300,7 @@ template get[T](c: CritBitTree[T], key: string): T = n.val -func `[]`*[T](c: CritBitTree[T], key: string): T {.inline.} = +func `[]`*[T](c: CritBitTree[T], key: string): lent T {.inline.} = ## Retrieves the value at `c[key]`. If `key` is not in `t`, the ## `KeyError` exception is raised. One can check with `hasKey` whether ## the key exists. @@ -342,7 +342,7 @@ iterator keys*[T](c: CritBitTree[T]): string = for x in leaves(c.root): yield x.key -iterator values*[T](c: CritBitTree[T]): T = +iterator values*[T](c: CritBitTree[T]): lent T = ## Yields all values of `c` in the lexicographical order of the ## corresponding keys. ## @@ -415,7 +415,7 @@ iterator keysWithPrefix*[T](c: CritBitTree[T], prefix: string): string = let top = allprefixedAux(c, prefix) for x in leaves(top): yield x.key -iterator valuesWithPrefix*[T](c: CritBitTree[T], prefix: string): T = +iterator valuesWithPrefix*[T](c: CritBitTree[T], prefix: string): lent T = ## Yields all values of `c` starting with `prefix` of the ## corresponding keys. ## @@ -518,7 +518,7 @@ func commonPrefixLen*[T](c: CritBitTree[T]): int {.inline, since((1, 3)).} = else: c.root.byte else: 0 -proc toCritBitTree*[T](pairs: openArray[(string, T)]): CritBitTree[T] {.since: (1, 3).} = +proc toCritBitTree*[T](pairs: sink openArray[(string, T)]): CritBitTree[T] {.since: (1, 3).} = ## Creates a new `CritBitTree` that contains the given `pairs`. runnableExamples: doAssert {"a": "0", "b": "1", "c": "2"}.toCritBitTree is CritBitTree[string] @@ -526,7 +526,7 @@ proc toCritBitTree*[T](pairs: openArray[(string, T)]): CritBitTree[T] {.since: ( for item in pairs: result.incl item[0], item[1] -proc toCritBitTree*(items: openArray[string]): CritBitTree[void] {.since: (1, 3).} = +proc toCritBitTree*(items: sink openArray[string]): CritBitTree[void] {.since: (1, 3).} = ## Creates a new `CritBitTree` that contains the given `items`. runnableExamples: doAssert ["a", "b", "c"].toCritBitTree is CritBitTree[void] From 10c8e2037db9ec3dc53fdaf1cc27e837e204af76 Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Thu, 14 Jul 2022 18:42:56 +0800 Subject: [PATCH 2996/3768] fixes #20015; document `shallowCopy` does a deep copy with ARC/ORC (#20025) --- lib/system.nim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/system.nim b/lib/system.nim index 4080fee06..6e8519cf6 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -473,6 +473,8 @@ proc shallowCopy*[T](x: var T, y: T) {.noSideEffect, magic: "ShallowCopy".} ## Be careful with the changed semantics though! ## There is a reason why the default assignment does a deep copy of sequences ## and strings. + ## + ## .. warning:: `shallowCopy` does a deep copy with ARC/ORC. # :array|openArray|string|seq|cstring|tuple proc `[]`*[I: Ordinal;T](a: T; i: I): T {. From 800cb006e74f8c52bde69cfc1eb9e55fcc439633 Mon Sep 17 00:00:00 2001 From: quantimnot <54247259+quantimnot@users.noreply.github.com> Date: Thu, 14 Jul 2022 08:20:40 -0400 Subject: [PATCH 2997/3768] Change `styleCheck` to ignore foreign packages (#19822) * Change `styleCheck` to ignore foreign packages * Symbols from foreign packages are now ignored. * Fixed `styleCheck` violations in `compiler` package. * Added symbol ownership to custom annotation pragmas. * Minor refactors to cleanup style check callsites. * Minor internal documentation of reasons why a symbol isn't checked. Style violations were fixed in the compiler after thet were exposed by the changes. The compiler wouldn't compile otherwise. Symbol ownership for custom pragma annotations is needed for checking the annotation's style. A NPE was raised otherwise. Fixes #10201 See also nim-lang/RFCs#456 * Fix a misunderstanding about excluding field style checks I had refactored the callsites of `styleCheckUse` to apply the DRY principle, but I misunderstood the field access handling in a template as a general case. This corrects it. * Fix some `styleCheck` violations in `compiler/evalffi` The violations were exposed in CI when the compiler was built with libffi. * Removed some uneeded transitionary code * Add changelog entry Co-authored-by: quantimnot --- changelog.md | 2 ++ compiler/evalffi.nim | 8 ++--- compiler/ic/bitabs.nim | 8 ++--- compiler/linter.nim | 67 +++++++++++++++++++++++++----------------- compiler/msgs.nim | 4 +-- compiler/pragmas.nim | 6 ++-- compiler/semexprs.nim | 2 +- compiler/semgnrc.nim | 2 +- compiler/semmagic.nim | 2 +- compiler/semstmts.nim | 14 ++++----- compiler/semtempl.nim | 12 ++++---- compiler/semtypes.nim | 8 ++--- compiler/suggest.nim | 3 +- compiler/vm.nim | 8 ++--- compiler/vmops.nim | 64 ++++++++++++++++++++-------------------- compiler/wordrecg.nim | 14 ++++----- 16 files changed, 118 insertions(+), 106 deletions(-) diff --git a/changelog.md b/changelog.md index 68b133040..47479fc5c 100644 --- a/changelog.md +++ b/changelog.md @@ -114,6 +114,8 @@ becomes an alias for `addr`. - `nim` can now compile version 1.4.0 as follows: `nim c --lib:lib --stylecheck:off compiler/nim`, without requiring `-d:nimVersion140` which is now a noop. +- `--styleCheck` now only applies to the current package. + ## Tool changes diff --git a/compiler/evalffi.nim b/compiler/evalffi.nim index 5004011ed..d1d88a1fa 100644 --- a/compiler/evalffi.nim +++ b/compiler/evalffi.nim @@ -110,8 +110,8 @@ proc mapCallConv(conf: ConfigRef, cc: TCallingConvention, info: TLineInfo): TABI else: globalError(conf, info, "cannot map calling convention to FFI") -template rd(T, p: untyped): untyped = (cast[ptr T](p))[] -template wr(T, p, v: untyped): untyped = (cast[ptr T](p))[] = v +template rd(typ, p: untyped): untyped = (cast[ptr typ](p))[] +template wr(typ, p, v: untyped): untyped = (cast[ptr typ](p))[] = v template `+!`(x, y: untyped): untyped = cast[pointer](cast[ByteAddress](x) + y) @@ -177,8 +177,8 @@ const maxPackDepth = 20 var packRecCheck = 0 proc pack(conf: ConfigRef, v: PNode, typ: PType, res: pointer) = - template awr(T, v: untyped): untyped = - wr(T, res, v) + template awr(typ, v: untyped): untyped = + wr(typ, res, v) case typ.kind of tyBool: awr(bool, v.intVal != 0) diff --git a/compiler/ic/bitabs.nim b/compiler/ic/bitabs.nim index ae673b574..8adab8388 100644 --- a/compiler/ic/bitabs.nim +++ b/compiler/ic/bitabs.nim @@ -93,13 +93,13 @@ proc getOrIncl*[T](t: var BiTable[T]; v: T): LitId = t.vals.add v -proc `[]`*[T](t: var BiTable[T]; LitId: LitId): var T {.inline.} = - let idx = idToIdx LitId +proc `[]`*[T](t: var BiTable[T]; litId: LitId): var T {.inline.} = + let idx = idToIdx litId assert idx < t.vals.len result = t.vals[idx] -proc `[]`*[T](t: BiTable[T]; LitId: LitId): lent T {.inline.} = - let idx = idToIdx LitId +proc `[]`*[T](t: BiTable[T]; litId: LitId): lent T {.inline.} = + let idx = idToIdx litId assert idx < t.vals.len result = t.vals[idx] diff --git a/compiler/linter.nim b/compiler/linter.nim index 5fb646051..2c0ad4d6f 100644 --- a/compiler/linter.nim +++ b/compiler/linter.nim @@ -12,7 +12,8 @@ import std/strutils from std/sugar import dup -import options, ast, msgs, idents, lineinfos, wordrecg, astmsgs +import options, ast, msgs, idents, lineinfos, wordrecg, astmsgs, semdata, packages +export packages const Letters* = {'a'..'z', 'A'..'Z', '0'..'9', '\x80'..'\xFF', '_'} @@ -85,24 +86,32 @@ proc differ*(line: string, a, b: int, x: string): string = result = y proc nep1CheckDefImpl(conf: ConfigRef; info: TLineInfo; s: PSym; k: TSymKind) = - # operators stay as they are: - if k in {skResult, skTemp} or s.name.s[0] notin Letters: return - if k in {skType, skGenericParam} and sfAnon in s.flags: return - if s.typ != nil and s.typ.kind == tyTypeDesc: return - if {sfImportc, sfExportc} * s.flags != {}: return - if optStyleCheck notin s.options: return let beau = beautifyName(s.name.s, k) if s.name.s != beau: lintReport(conf, info, beau, s.name.s) -template styleCheckDef*(conf: ConfigRef; info: TLineInfo; s: PSym; k: TSymKind) = - if {optStyleHint, optStyleError} * conf.globalOptions != {} and optStyleUsages notin conf.globalOptions: - nep1CheckDefImpl(conf, info, s, k) +template styleCheckDef*(ctx: PContext; info: TLineInfo; sym: PSym; k: TSymKind) = + ## Check symbol definitions adhere to NEP1 style rules. + if optStyleCheck in ctx.config.options and # ignore if styleChecks are off + hintName in ctx.config.notes and # ignore if name checks are not requested + ctx.config.belongsToProjectPackage(ctx.module) and # ignore foreign packages + optStyleUsages notin ctx.config.globalOptions and # ignore if requested to only check name usage + sym.kind != skResult and # ignore `result` + sym.kind != skTemp and # ignore temporary variables created by the compiler + sym.name.s[0] in Letters and # ignore operators TODO: what about unicode symbols??? + k notin {skType, skGenericParam} and # ignore types and generic params + (sym.typ == nil or sym.typ.kind != tyTypeDesc) and # ignore `typedesc` + {sfImportc, sfExportc} * sym.flags == {} and # ignore FFI + sfAnon notin sym.flags: # ignore if created by compiler + nep1CheckDefImpl(ctx.config, info, sym, k) -template styleCheckDef*(conf: ConfigRef; info: TLineInfo; s: PSym) = - styleCheckDef(conf, info, s, s.kind) -template styleCheckDef*(conf: ConfigRef; s: PSym) = - styleCheckDef(conf, s.info, s, s.kind) +template styleCheckDef*(ctx: PContext; info: TLineInfo; s: PSym) = + ## Check symbol definitions adhere to NEP1 style rules. + styleCheckDef(ctx, info, s, s.kind) + +template styleCheckDef*(ctx: PContext; s: PSym) = + ## Check symbol definitions adhere to NEP1 style rules. + styleCheckDef(ctx, s.info, s, s.kind) proc differs(conf: ConfigRef; info: TLineInfo; newName: string): string = let line = sourceLine(conf, info) @@ -116,23 +125,27 @@ proc differs(conf: ConfigRef; info: TLineInfo; newName: string): string = let last = first+identLen(line, first)-1 result = differ(line, first, last, newName) -proc styleCheckUse*(conf: ConfigRef; info: TLineInfo; s: PSym) = - if info.fileIndex.int < 0: return - # we simply convert it to what it looks like in the definition - # for consistency - - # operators stay as they are: - if s.kind == skTemp or s.name.s[0] notin Letters or sfAnon in s.flags: - return - +proc styleCheckUseImpl(conf: ConfigRef; info: TLineInfo; s: PSym) = let newName = s.name.s let badName = differs(conf, info, newName) if badName.len > 0: - # special rules for historical reasons - let forceHint = badName == "nnkArgList" and newName == "nnkArglist" or badName == "nnkArglist" and newName == "nnkArgList" - lintReport(conf, info, newName, badName, forceHint = forceHint, extraMsg = "".dup(addDeclaredLoc(conf, s))) + lintReport(conf, info, newName, badName, "".dup(addDeclaredLoc(conf, s))) -proc checkPragmaUse*(conf: ConfigRef; info: TLineInfo; w: TSpecialWord; pragmaName: string) = +template styleCheckUse*(ctx: PContext; info: TLineInfo; sym: PSym) = + ## Check symbol uses match their definition's style. + if {optStyleHint, optStyleError} * ctx.config.globalOptions != {} and # ignore if styleChecks are off + hintName in ctx.config.notes and # ignore if name checks are not requested + ctx.config.belongsToProjectPackage(ctx.module) and # ignore foreign packages + sym.kind != skTemp and # ignore temporary variables created by the compiler + sym.name.s[0] in Letters and # ignore operators TODO: what about unicode symbols??? + sfAnon notin sym.flags: # ignore temporary variables created by the compiler + styleCheckUseImpl(ctx.config, info, sym) + +proc checkPragmaUseImpl(conf: ConfigRef; info: TLineInfo; w: TSpecialWord; pragmaName: string) = let wanted = $w if pragmaName != wanted: lintReport(conf, info, wanted, pragmaName) + +template checkPragmaUse*(conf: ConfigRef; info: TLineInfo; w: TSpecialWord; pragmaName: string) = + if {optStyleHint, optStyleError} * conf.globalOptions != {}: + checkPragmaUseImpl(conf, info, w, pragmaName) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 371a886f4..6d770e72f 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -622,9 +622,9 @@ template internalAssert*(conf: ConfigRef, e: bool) = let arg = info2.toFileLineCol internalErrorImpl(conf, unknownLineInfo, arg, info2) -template lintReport*(conf: ConfigRef; info: TLineInfo, beau, got: string, forceHint = false, extraMsg = "") = +template lintReport*(conf: ConfigRef; info: TLineInfo, beau, got: string, extraMsg = "") = let m = "'$1' should be: '$2'$3" % [got, beau, extraMsg] - let msg = if optStyleError in conf.globalOptions and not forceHint: errGenerated else: hintName + let msg = if optStyleError in conf.globalOptions: errGenerated else: hintName liMessage(conf, info, msg, m, doNothing, instLoc()) proc quotedFilename*(conf: ConfigRef; i: TLineInfo): Rope = diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 2262e441b..1487a871d 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -829,8 +829,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, let ident = considerQuotedIdent(c, key) var userPragma = strTableGet(c.userPragmas, ident) if userPragma != nil: - if {optStyleHint, optStyleError} * c.config.globalOptions != {}: - styleCheckUse(c.config, key.info, userPragma) + styleCheckUse(c, key.info, userPragma) # number of pragmas increase/decrease with user pragma expansion inc c.instCounter @@ -844,8 +843,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, else: let k = whichKeyword(ident) if k in validPragmas: - if {optStyleHint, optStyleError} * c.config.globalOptions != {}: - checkPragmaUse(c.config, key.info, k, ident.s) + checkPragmaUse(c.config, key.info, k, ident.s) case k of wExportc, wExportCpp: makeExternExport(c, sym, getOptionalStr(c, it, "$1"), it.info) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index fed9cae21..1bb4f49ce 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2599,7 +2599,7 @@ proc semBlock(c: PContext, n: PNode; flags: TExprFlags): PNode = labl.owner = c.p.owner n[0] = newSymNode(labl, n[0].info) suggestSym(c.graph, n[0].info, labl, c.graph.usageSym) - styleCheckDef(c.config, labl) + styleCheckDef(c, labl) onDef(n[0].info, labl) n[1] = semExpr(c, n[1], flags) n.typ = n[1].typ diff --git a/compiler/semgnrc.nim b/compiler/semgnrc.nim index e249d88e8..ca0b05fa2 100644 --- a/compiler/semgnrc.nim +++ b/compiler/semgnrc.nim @@ -179,7 +179,7 @@ proc fuzzyLookup(c: PContext, n: PNode, flags: TSemGenericFlags, proc addTempDecl(c: PContext; n: PNode; kind: TSymKind) = let s = newSymS(skUnknown, getIdentNode(c, n), c) addPrelimDecl(c, s) - styleCheckDef(c.config, n.info, s, kind) + styleCheckDef(c, n.info, s, kind) onDef(n.info, s) proc semGenericStmt(c: PContext, n: PNode, diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index ed1826fd4..d3e12670b 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -428,7 +428,7 @@ proc semQuantifier(c: PContext; n: PNode): PNode = let op = considerQuotedIdent(c, it[0]) if op.id == ord(wIn): let v = newSymS(skForVar, it[1], c) - styleCheckDef(c.config, v) + styleCheckDef(c, v) onDef(it[1].info, v) let domain = semExprWithType(c, it[2], {efWantIterator}) v.typ = domain.typ diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 0516d08ce..03c30b6e6 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -375,7 +375,7 @@ proc semUsing(c: PContext; n: PNode): PNode = let typ = semTypeNode(c, a[^2], nil) for j in 0.. 0: @@ -271,8 +271,8 @@ proc semTemplSymbol(c: PContext, n: PNode, s: PSym; isField: bool): PNode = when defined(nimsuggest): suggestSym(c.graph, n.info, s, c.graph.usageSym, false) # field access (dot expr) will be handled by builtinFieldAccess - if not isField and {optStyleHint, optStyleError} * c.config.globalOptions != {}: - styleCheckUse(c.config, n.info, s) + if not isField: + styleCheckUse(c, n.info, s) proc semRoutineInTemplName(c: var TemplCtx, n: PNode): PNode = result = n @@ -297,7 +297,7 @@ proc semRoutineInTemplBody(c: var TemplCtx, n: PNode, k: TSymKind): PNode = var s = newGenSym(k, ident, c) s.ast = n addPrelimDecl(c.c, s) - styleCheckDef(c.c.config, n.info, s) + styleCheckDef(c.c, n.info, s) onDef(n.info, s) n[namePos] = newSymNode(s, n[namePos].info) else: @@ -431,7 +431,7 @@ proc semTemplBody(c: var TemplCtx, n: PNode): PNode = # labels are always 'gensym'ed: let s = newGenSym(skLabel, n[0], c) addPrelimDecl(c.c, s) - styleCheckDef(c.c.config, s) + styleCheckDef(c.c, s) onDef(n[0].info, s) n[0] = newSymNode(s, n[0].info) n[1] = semTemplBody(c, n[1]) @@ -624,7 +624,7 @@ proc semTemplateDef(c: PContext, n: PNode): PNode = s.owner.name.s == "vm" and s.name.s == "stackTrace": incl(s.flags, sfCallsite) - styleCheckDef(c.config, s) + styleCheckDef(c, s) onDef(n[namePos].info, s) # check parameter list: #s.scope = c.currentScope diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index b4f385fe6..584da1305 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -139,7 +139,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = e.flags.incl {sfUsed, sfExported} result.n.add symNode - styleCheckDef(c.config, e) + styleCheckDef(c, e) onDef(e.info, e) if sfGenSym notin e.flags: if not isPure: @@ -476,7 +476,7 @@ proc semTuple(c: PContext, n: PNode, prev: PType): PType = else: result.n.add newSymNode(field) addSonSkipIntLit(result, typ, c.idgen) - styleCheckDef(c.config, a[j].info, field) + styleCheckDef(c, a[j].info, field) onDef(field.info, field) if result.n.len == 0: result.n = nil if isTupleRecursive(result): @@ -808,7 +808,7 @@ proc semRecordNodeAux(c: PContext, n: PNode, check: var IntSet, pos: var int, localError(c.config, info, "attempt to redefine: '" & f.name.s & "'") if a.kind == nkEmpty: father.add newSymNode(f) else: a.add newSymNode(f) - styleCheckDef(c.config, f) + styleCheckDef(c, f) onDef(f.info, f) if a.kind != nkEmpty: father.add a of nkSym: @@ -1315,7 +1315,7 @@ proc semProcTypeNode(c: PContext, n, genericParams: PNode, result.n.add newSymNode(arg) rawAddSon(result, finalType) addParamOrResult(c, arg, kind) - styleCheckDef(c.config, a[j].info, arg) + styleCheckDef(c, a[j].info, arg) onDef(a[j].info, arg) if {optNimV1Emulation, optNimV12Emulation} * c.config.globalOptions == {}: a[j] = newSymNode(arg) diff --git a/compiler/suggest.nim b/compiler/suggest.nim index 38751fcc7..4b48ef5bb 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -598,8 +598,7 @@ proc markUsed(c: PContext; info: TLineInfo; s: PSym) = if sfError in s.flags: userError(conf, info, s) when defined(nimsuggest): suggestSym(c.graph, info, s, c.graph.usageSym, false) - if {optStyleHint, optStyleError} * conf.globalOptions != {}: - styleCheckUse(conf, info, s) + styleCheckUse(c, info, s) markOwnerModuleAsUsed(c, s) proc safeSemExpr*(c: PContext, n: PNode): PNode = diff --git a/compiler/vm.nim b/compiler/vm.nim index e681bbc96..3cb699482 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -119,13 +119,13 @@ template move(a, b: untyped) {.dirty.} = system.shallowCopy(a, b) proc derefPtrToReg(address: BiggestInt, typ: PType, r: var TFullReg, isAssign: bool): bool = # nim bug: `isAssign: static bool` doesn't work, giving odd compiler error - template fun(field, T, rkind) = + template fun(field, typ, rkind) = if isAssign: - cast[ptr T](address)[] = T(r.field) + cast[ptr typ](address)[] = typ(r.field) else: r.ensureKind(rkind) - let val = cast[ptr T](address)[] - when T is SomeInteger | char: + let val = cast[ptr typ](address)[] + when typ is SomeInteger | char: r.field = BiggestInt(val) else: r.field = val diff --git a/compiler/vmops.nim b/compiler/vmops.nim index 04b753ef6..69599ece3 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -56,13 +56,13 @@ template ioop(op) {.dirty.} = template macrosop(op) {.dirty.} = registerCallback(c, "stdlib.macros." & astToStr(op), `op Wrapper`) -template wrap1f_math(op) {.dirty.} = +template wrap1fMath(op) {.dirty.} = proc `op Wrapper`(a: VmArgs) {.nimcall.} = doAssert a.numArgs == 1 setResult(a, op(getFloat(a, 0))) mathop op -template wrap2f_math(op) {.dirty.} = +template wrap2fMath(op) {.dirty.} = proc `op Wrapper`(a: VmArgs) {.nimcall.} = setResult(a, op(getFloat(a, 0), getFloat(a, 1))) mathop op @@ -172,40 +172,40 @@ proc registerAdditionalOps*(c: PCtx) = proc getProjectPathWrapper(a: VmArgs) = setResult a, c.config.projectPath.string - wrap1f_math(sqrt) - wrap1f_math(cbrt) - wrap1f_math(ln) - wrap1f_math(log10) - wrap1f_math(log2) - wrap1f_math(exp) - wrap1f_math(arccos) - wrap1f_math(arcsin) - wrap1f_math(arctan) - wrap1f_math(arcsinh) - wrap1f_math(arccosh) - wrap1f_math(arctanh) - wrap2f_math(arctan2) - wrap1f_math(cos) - wrap1f_math(cosh) - wrap2f_math(hypot) - wrap1f_math(sinh) - wrap1f_math(sin) - wrap1f_math(tan) - wrap1f_math(tanh) - wrap2f_math(pow) - wrap1f_math(trunc) - wrap1f_math(floor) - wrap1f_math(ceil) - wrap1f_math(erf) - wrap1f_math(erfc) - wrap1f_math(gamma) - wrap1f_math(lgamma) + wrap1fMath(sqrt) + wrap1fMath(cbrt) + wrap1fMath(ln) + wrap1fMath(log10) + wrap1fMath(log2) + wrap1fMath(exp) + wrap1fMath(arccos) + wrap1fMath(arcsin) + wrap1fMath(arctan) + wrap1fMath(arcsinh) + wrap1fMath(arccosh) + wrap1fMath(arctanh) + wrap2fMath(arctan2) + wrap1fMath(cos) + wrap1fMath(cosh) + wrap2fMath(hypot) + wrap1fMath(sinh) + wrap1fMath(sin) + wrap1fMath(tan) + wrap1fMath(tanh) + wrap2fMath(pow) + wrap1fMath(trunc) + wrap1fMath(floor) + wrap1fMath(ceil) + wrap1fMath(erf) + wrap1fMath(erfc) + wrap1fMath(gamma) + wrap1fMath(lgamma) when declared(copySign): - wrap2f_math(copySign) + wrap2fMath(copySign) when declared(signbit): - wrap1f_math(signbit) + wrap1fMath(signbit) registerCallback c, "stdlib.math.round", proc (a: VmArgs) {.nimcall.} = let n = a.numArgs diff --git a/compiler/wordrecg.nim b/compiler/wordrecg.nim index c819f4306..cf2f768b1 100644 --- a/compiler/wordrecg.nim +++ b/compiler/wordrecg.nim @@ -89,23 +89,23 @@ type wLiftLocals = "liftlocals", wEnforceNoRaises = "enforceNoRaises", wAuto = "auto", wBool = "bool", wCatch = "catch", wChar = "char", - wClass = "class", wCompl = "compl", wConst_cast = "const_cast", wDefault = "default", - wDelete = "delete", wDouble = "double", wDynamic_cast = "dynamic_cast", + wClass = "class", wCompl = "compl", wConstCast = "const_cast", wDefault = "default", + wDelete = "delete", wDouble = "double", wDynamicCast = "dynamic_cast", wExplicit = "explicit", wExtern = "extern", wFalse = "false", wFloat = "float", wFriend = "friend", wGoto = "goto", wInt = "int", wLong = "long", wMutable = "mutable", wNamespace = "namespace", wNew = "new", wOperator = "operator", wPrivate = "private", wProtected = "protected", wPublic = "public", wRegister = "register", - wReinterpret_cast = "reinterpret_cast", wRestrict = "restrict", wShort = "short", - wSigned = "signed", wSizeof = "sizeof", wStatic_cast = "static_cast", wStruct = "struct", + wReinterpretCast = "reinterpret_cast", wRestrict = "restrict", wShort = "short", + wSigned = "signed", wSizeof = "sizeof", wStaticCast = "static_cast", wStruct = "struct", wSwitch = "switch", wThis = "this", wThrow = "throw", wTrue = "true", wTypedef = "typedef", wTypeid = "typeid", wTypeof = "typeof", wTypename = "typename", wUnion = "union", wPacked = "packed", wUnsigned = "unsigned", wVirtual = "virtual", - wVoid = "void", wVolatile = "volatile", wWchar_t = "wchar_t", + wVoid = "void", wVolatile = "volatile", wWchar = "wchar_t", wAlignas = "alignas", wAlignof = "alignof", wConstexpr = "constexpr", wDecltype = "decltype", wNullptr = "nullptr", wNoexcept = "noexcept", - wThread_local = "thread_local", wStatic_assert = "static_assert", - wChar16_t = "char16_t", wChar32_t = "char32_t", + wThreadLocal = "thread_local", wStaticAssert = "static_assert", + wChar16 = "char16_t", wChar32 = "char32_t", wStdIn = "stdin", wStdOut = "stdout", wStdErr = "stderr", From 286fcef68ef6f0e3a20ab5b25307c9b4705ce54d Mon Sep 17 00:00:00 2001 From: flywind <43030857+xflywind@users.noreply.github.com> Date: Fri, 15 Jul 2022 15:42:54 +0800 Subject: [PATCH 2998/3768] [Orc] fixes "streams.readDataStr segafaults" when accepting a string literal (#20019) [backport] fixes streams.readDataStr accept a string literal --- lib/pure/streams.nim | 5 +++++ tests/stdlib/tstreams.nim | 10 ++++++++++ 2 files changed, 15 insertions(+) diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim index f58273ee8..50b3085d2 100644 --- a/lib/pure/streams.nim +++ b/lib/pure/streams.nim @@ -1197,6 +1197,11 @@ else: # after 1.3 or JS not defined proc ssReadDataStr(s: Stream, buffer: var string, slice: Slice[int]): int = var s = StringStream(s) + when nimvm: + discard + else: + when declared(prepareMutation): + prepareMutation(buffer) # buffer might potentially be a CoW literal with ARC result = min(slice.b + 1 - slice.a, s.data.len - s.pos) if result > 0: jsOrVmBlock: diff --git a/tests/stdlib/tstreams.nim b/tests/stdlib/tstreams.nim index d9857926e..cc1343651 100644 --- a/tests/stdlib/tstreams.nim +++ b/tests/stdlib/tstreams.nim @@ -85,3 +85,13 @@ block: static: # Ensure streams it doesnt break with nimscript on arc/orc #19716 let s = newStringStream("a") doAssert s.data == "a" + +template main = + var strm = newStringStream("abcde") + var buffer = "12345" + doAssert strm.readDataStr(buffer, 0..3) == 4 + doAssert buffer == "abcd5" + strm.close() + +static: main() +main() From f35c9cf73ddb3150ab6dbe449db4975866ee8a11 Mon Sep 17 00:00:00 2001 From: metagn <10591326+metagn@users.noreply.github.com> Date: Fri, 15 Jul 2022 13:37:08 +0300 Subject: [PATCH 2999/3768] fix #20002 (#20004) While this fix seems innocent, this unlocks the hidden behavior of method calls not being able to call gensym'ed routines inside templates. --- compiler/semtempl.nim | 2 +- tests/template/tinnerouterproc.nim | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 tests/template/tinnerouterproc.nim diff --git a/compiler/semtempl.nim b/compiler/semtempl.nim index a599edb9c..1f76aff75 100644 --- a/compiler/semtempl.nim +++ b/compiler/semtempl.nim @@ -78,7 +78,7 @@ proc symChoice(c: PContext, n: PNode, s: PSym, r: TSymChoiceRule; result = newNodeIT(kind, info, newTypeS(tyNone, c)) a = initOverloadIter(o, c, n) while a != nil: - if a.kind != skModule and (not isField or sfGenSym notin s.flags): + if a.kind != skModule and (not isField or sfGenSym notin a.flags): incl(a.flags, sfUsed) markOwnerModuleAsUsed(c, a) result.add newSymNode(a, info) diff --git a/tests/template/tinnerouterproc.nim b/tests/template/tinnerouterproc.nim new file mode 100644 index 000000000..1f15fb13e --- /dev/null +++ b/tests/template/tinnerouterproc.nim @@ -0,0 +1,8 @@ +block: # #20002 + proc bar(x: int): int = 10 + template foo = + proc bar(x: int): int {.gensym.} = x + 2 + doAssert bar(3) == 5 + discard 3.bar # evaluates to 10 but only check if it compiles for now + block: + foo() From 417b90a7e5b88bfc0ad1bfbbc81a3205c99e128e Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Fri, 15 Jul 2022 20:27:54 +0300 Subject: [PATCH 3000/3768] Improve Markdown code blocks & start moving docs to Markdown style (#19954) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - add additional parameters parsing (other implementations will just ignore them). E.g. if in RST we have: .. code:: nim :test: "nim c $1" ... then in Markdown that will be: ```nim test="nim c $1" ... ``` - implement Markdown interpretation of additional indentation which is less than 4 spaces (>=4 spaces is a code block but it's not implemented yet). RST interpretes it as quoted block, for Markdown it's just normal paragraphs. - add separate `md2html` and `md2tex` commands. This is to separate Markdown behavior in cases when it diverges w.r.t. RST significantly — most conspicously like in the case of additional indentation above, and also currently the contradicting inline rule of Markdown is also turned on only in `md2html` and `md2tex`. **Rationale:** mixing Markdown and RST arbitrarily is a way to nowhere, we need to provide a way to fix the particular behavior. Note that still all commands have **both** Markdown and RST features **enabled**. In this PR `*.nim` files can be processed only in Markdown mode, while `md2html` is for `*.md` files and `rst2html` for `*.rst` files. - rename `*.rst` files to `.*md` as our current default behavior is already Markdown-ish - convert code blocks in `docgen.rst` to Markdown style as an example. Other code blocks will be converted in the follow-up PRs - fix indentation inside Markdown code blocks — additional indentation is preserved there - allow more than 3 backticks open/close blocks (tildas \~ are still not allowed to avoid conflict with RST adornment headings) see also https://github.com/nim-lang/RFCs/issues/355 - better error messages - (other) fix a bug that admonitions cannot be used in sandbox mode; fix annoying warning on line 2711 --- compiler/commands.nim | 5 +- compiler/docgen.nim | 33 ++-- compiler/lineinfos.nim | 2 + compiler/main.nim | 13 +- compiler/nim.nim | 2 +- compiler/options.nim | 2 + doc/{apis.rst => apis.md} | 0 doc/{backends.rst => backends.md} | 3 +- doc/{contributing.rst => contributing.md} | 2 +- doc/{destructors.rst => destructors.md} | 0 doc/{docgen.rst => docgen.md} | 63 ++++---- doc/{docs.rst => docs.md} | 0 doc/{docstyle.rst => docstyle.md} | 0 doc/{drnim.rst => drnim.md} | 0 doc/{estp.rst => estp.md} | 0 doc/{filters.rst => filters.md} | 0 doc/{hcr.rst => hcr.md} | 0 doc/{idetools.rst => idetools.md} | 0 doc/{intern.rst => intern.md} | 6 +- doc/{koch.rst => koch.md} | 0 doc/{lib.rst => lib.md} | 0 doc/{manual.rst => manual.md} | 8 +- .../{var_t_return.rst => var_t_return.md} | 0 ...xperimental.rst => manual_experimental.md} | 2 +- ...st => manual_experimental_strictnotnil.md} | 0 doc/{mm.rst => mm.md} | 2 +- doc/{nep1.rst => nep1.md} | 0 doc/{nimc.rst => nimc.md} | 6 +- doc/{nimfix.rst => nimfix.md} | 0 doc/{nimgrep.rst => nimgrep.md} | 2 +- doc/{niminst.rst => niminst.md} | 0 doc/{nims.rst => nims.md} | 0 doc/{nimsuggest.rst => nimsuggest.md} | 0 doc/{overview.rst => overview.md} | 2 +- doc/{packaging.rst => packaging.md} | 0 doc/{refc.rst => refc.md} | 0 doc/{testament.rst => testament.md} | 0 doc/{tools.rst => tools.md} | 0 doc/{tut1.rst => tut1.md} | 0 doc/{tut2.rst => tut2.md} | 2 +- doc/{tut3.rst => tut3.md} | 2 +- lib/packages/docutils/highlite.nim | 4 +- lib/packages/docutils/rst.nim | 151 ++++++++++++++---- tests/stdlib/thighlite.nim | 6 + tests/stdlib/trst.nim | 117 +++++++++++++- tests/stdlib/trstgen.nim | 16 +- tools/kochdocs.nim | 16 +- 47 files changed, 341 insertions(+), 126 deletions(-) rename doc/{apis.rst => apis.md} (100%) rename doc/{backends.rst => backends.md} (99%) rename doc/{contributing.rst => contributing.md} (99%) rename doc/{destructors.rst => destructors.md} (100%) rename doc/{docgen.rst => docgen.md} (98%) rename doc/{docs.rst => docs.md} (100%) rename doc/{docstyle.rst => docstyle.md} (100%) rename doc/{drnim.rst => drnim.md} (100%) rename doc/{estp.rst => estp.md} (100%) rename doc/{filters.rst => filters.md} (100%) rename doc/{hcr.rst => hcr.md} (100%) rename doc/{idetools.rst => idetools.md} (100%) rename doc/{intern.rst => intern.md} (99%) rename doc/{koch.rst => koch.md} (100%) rename doc/{lib.rst => lib.md} (100%) rename doc/{manual.rst => manual.md} (99%) rename doc/manual/{var_t_return.rst => var_t_return.md} (100%) rename doc/{manual_experimental.rst => manual_experimental.md} (99%) rename doc/{manual_experimental_strictnotnil.rst => manual_experimental_strictnotnil.md} (100%) rename doc/{mm.rst => mm.md} (98%) rename doc/{nep1.rst => nep1.md} (100%) rename doc/{nimc.rst => nimc.md} (99%) rename doc/{nimfix.rst => nimfix.md} (100%) rename doc/{nimgrep.rst => nimgrep.md} (99%) rename doc/{niminst.rst => niminst.md} (100%) rename doc/{nims.rst => nims.md} (100%) rename doc/{nimsuggest.rst => nimsuggest.md} (100%) rename doc/{overview.rst => overview.md} (86%) rename doc/{packaging.rst => packaging.md} (100%) rename doc/{refc.rst => refc.md} (100%) rename doc/{testament.rst => testament.md} (100%) rename doc/{tools.rst => tools.md} (100%) rename doc/{tut1.rst => tut1.md} (100%) rename doc/{tut2.rst => tut2.md} (99%) rename doc/{tut3.rst => tut3.md} (99%) diff --git a/compiler/commands.nim b/compiler/commands.nim index 31e637aba..b849f503d 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -449,6 +449,8 @@ proc parseCommand*(command: string): Command = of "doc2", "doc": cmdDoc of "doc2tex": cmdDoc2tex of "rst2html": cmdRst2html + of "md2tex": cmdMd2tex + of "md2html": cmdMd2html of "rst2tex": cmdRst2tex of "jsondoc0": cmdJsondoc0 of "jsondoc2", "jsondoc": cmdJsondoc @@ -480,7 +482,8 @@ proc setCommandEarly*(conf: ConfigRef, command: string) = # command early customizations # must be handled here to honor subsequent `--hint:x:on|off` case conf.cmd - of cmdRst2html, cmdRst2tex: # xxx see whether to add others: cmdGendepend, etc. + of cmdRst2html, cmdRst2tex, cmdMd2html, cmdMd2tex: + # xxx see whether to add others: cmdGendepend, etc. conf.foreignPackageNotes = {hintSuccessX} else: conf.foreignPackageNotes = foreignPackageNotesDefault diff --git a/compiler/docgen.nim b/compiler/docgen.nim index 390f44f2e..ed5fe06ef 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -88,7 +88,7 @@ type jEntriesFinal: JsonNode # final JSON after RST pass 2 and rendering types: TStrTable sharedState: PRstSharedState - isPureRst: bool + standaloneDoc: bool conf*: ConfigRef cache*: IdentCache exampleCounter: int @@ -230,6 +230,7 @@ template declareClosures = case msgKind of meCannotOpenFile: k = errCannotOpenFile of meExpected: k = errXExpected + of meMissingClosing: k = errRstMissingClosing of meGridTableNotImplemented: k = errRstGridTableNotImplemented of meMarkdownIllformedTable: k = errRstMarkdownIllformedTable of meIllformedTable: k = errRstIllformedTable @@ -276,16 +277,18 @@ proc isLatexCmd(conf: ConfigRef): bool = conf.cmd in {cmdRst2tex, cmdDoc2tex} proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef, outExt: string = HtmlExt, module: PSym = nil, - isPureRst = false): PDoc = + standaloneDoc = false, preferMarkdown = true): PDoc = declareClosures() new(result) result.module = module result.conf = conf result.cache = cache result.outDir = conf.outDir.string - result.isPureRst = isPureRst - var options= {roSupportRawDirective, roSupportMarkdown, roPreferMarkdown, roSandboxDisabled} - if not isPureRst: options.incl roNimFile + result.standaloneDoc = standaloneDoc + var options= {roSupportRawDirective, roSupportMarkdown, roSandboxDisabled} + if preferMarkdown: + options.incl roPreferMarkdown + if not standaloneDoc: options.incl roNimFile result.sharedState = newRstSharedState( options, filename.string, docgenFindFile, compilerMsgHandler) @@ -333,7 +336,7 @@ proc newDocumentor*(filename: AbsoluteFile; cache: IdentCache; conf: ConfigRef, # Make sure the destination directory exists createDir(outp.splitFile.dir) # Include the current file if we're parsing a nim file - let importStmt = if d.isPureRst: "" else: "import \"$1\"\n" % [d.filename.replace("\\", "/")] + let importStmt = if d.standaloneDoc: "" else: "import \"$1\"\n" % [d.filename.replace("\\", "/")] writeFile(outp, importStmt & content) proc interpSnippetCmd(cmd: string): string = @@ -1512,7 +1515,7 @@ proc genOutFile(d: PDoc, groupedToc = false): string = "\\\\\\vspace{0.5em}\\large $1", [esc(d.target, d.meta[metaSubtitle])]) var groupsection = getConfigVar(d.conf, "doc.body_toc_groupsection") - let bodyname = if d.hasToc and not d.isPureRst and not d.conf.isLatexCmd: + let bodyname = if d.hasToc and not d.standaloneDoc and not d.conf.isLatexCmd: groupsection.setLen 0 "doc.body_toc_group" elif d.hasToc: "doc.body_toc" @@ -1626,9 +1629,11 @@ proc commandDoc*(cache: IdentCache, conf: ConfigRef) = generateIndex(d) proc commandRstAux(cache: IdentCache, conf: ConfigRef; - filename: AbsoluteFile, outExt: string) = + filename: AbsoluteFile, outExt: string, + preferMarkdown: bool) = var filen = addFileExt(filename, "txt") - var d = newDocumentor(filen, cache, conf, outExt, isPureRst = true) + var d = newDocumentor(filen, cache, conf, outExt, standaloneDoc = true, + preferMarkdown = preferMarkdown) let rst = parseRst(readFile(filen.string), line=LineRstInit, column=ColRstInit, conf, d.sharedState) @@ -1637,11 +1642,13 @@ proc commandRstAux(cache: IdentCache, conf: ConfigRef; writeOutput(d) generateIndex(d) -proc commandRst2Html*(cache: IdentCache, conf: ConfigRef) = - commandRstAux(cache, conf, conf.projectFull, HtmlExt) +proc commandRst2Html*(cache: IdentCache, conf: ConfigRef, + preferMarkdown=false) = + commandRstAux(cache, conf, conf.projectFull, HtmlExt, preferMarkdown) -proc commandRst2TeX*(cache: IdentCache, conf: ConfigRef) = - commandRstAux(cache, conf, conf.projectFull, TexExt) +proc commandRst2TeX*(cache: IdentCache, conf: ConfigRef, + preferMarkdown=false) = + commandRstAux(cache, conf, conf.projectFull, TexExt, preferMarkdown) proc commandJson*(cache: IdentCache, conf: ConfigRef) = ## implementation of a deprecated jsondoc0 command diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index 105de1636..071316f15 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -32,6 +32,7 @@ type # non-fatal errors errIllFormedAstX, errCannotOpenFile, errXExpected, + errRstMissingClosing, errRstGridTableNotImplemented, errRstMarkdownIllformedTable, errRstIllformedTable, @@ -105,6 +106,7 @@ const errIllFormedAstX: "illformed AST: $1", errCannotOpenFile: "cannot open '$1'", errXExpected: "'$1' expected", + errRstMissingClosing: "$1", errRstGridTableNotImplemented: "grid table is not implemented", errRstMarkdownIllformedTable: "illformed delimiter row of a markdown table", errRstIllformedTable: "Illformed table: $1", diff --git a/compiler/main.nim b/compiler/main.nim index a4425e510..0354bec9c 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -276,7 +276,8 @@ proc mainCommand*(graph: ModuleGraph) = var ret = if optUseNimcache in conf.globalOptions: getNimcacheDir(conf) else: conf.projectPath doAssert ret.string.isAbsolute # `AbsoluteDir` is not a real guarantee - if conf.cmd in cmdDocLike + {cmdRst2html, cmdRst2tex}: ret = ret / htmldocsDir + if conf.cmd in cmdDocLike + {cmdRst2html, cmdRst2tex, cmdMd2html, cmdMd2tex}: + ret = ret / htmldocsDir conf.outDir = ret ## process all commands @@ -302,7 +303,7 @@ proc mainCommand*(graph: ModuleGraph) = commandDoc2(graph, HtmlExt) if optGenIndex in conf.globalOptions and optWholeProject in conf.globalOptions: commandBuildIndex(conf, $conf.outDir) - of cmdRst2html: + of cmdRst2html, cmdMd2html: # XXX: why are warnings disabled by default for rst2html and rst2tex? for warn in rstWarnings: conf.setNoteDefaults(warn, true) @@ -311,16 +312,16 @@ proc mainCommand*(graph: ModuleGraph) = conf.quitOrRaise "compiler wasn't built with documentation generator" else: loadConfigs(DocConfig, cache, conf, graph.idgen) - commandRst2Html(cache, conf) - of cmdRst2tex, cmdDoc2tex: + commandRst2Html(cache, conf, preferMarkdown = (conf.cmd == cmdMd2html)) + of cmdRst2tex, cmdMd2tex, cmdDoc2tex: for warn in rstWarnings: conf.setNoteDefaults(warn, true) when defined(leanCompiler): conf.quitOrRaise "compiler wasn't built with documentation generator" else: - if conf.cmd == cmdRst2tex: + if conf.cmd in {cmdRst2tex, cmdMd2tex}: loadConfigs(DocTexConfig, cache, conf, graph.idgen) - commandRst2TeX(cache, conf) + commandRst2TeX(cache, conf, preferMarkdown = (conf.cmd == cmdMd2tex)) else: docLikeCmd commandDoc2(graph, TexExt) of cmdJsondoc0: docLikeCmd commandJson(cache, conf) diff --git a/compiler/nim.nim b/compiler/nim.nim index bfb07ba20..48472507d 100644 --- a/compiler/nim.nim +++ b/compiler/nim.nim @@ -122,7 +122,7 @@ proc handleCmdLine(cache: IdentCache; conf: ConfigRef) = # `The parameter is incorrect` let cmd = cmdPrefix & output.quoteShell & ' ' & conf.arguments execExternalProgram(conf, cmd.strip(leading=false,trailing=true)) - of cmdDocLike, cmdRst2html, cmdRst2tex: # bugfix(cmdRst2tex was missing) + of cmdDocLike, cmdRst2html, cmdRst2tex, cmdMd2html, cmdMd2tex: # bugfix(cmdRst2tex was missing) if conf.arguments.len > 0: # reserved for future use rawMessage(conf, errGenerated, "'$1 cannot handle arguments" % [$conf.cmd]) diff --git a/compiler/options.nim b/compiler/options.nim index 1ba157587..d567927cb 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -153,6 +153,8 @@ type cmdDoc2tex # convert .nim doc comments to LaTeX cmdRst2html # convert a reStructuredText file to HTML cmdRst2tex # convert a reStructuredText file to TeX + cmdMd2html # convert a Markdown file to HTML + cmdMd2tex # convert a Markdown file to TeX cmdJsondoc0 cmdJsondoc cmdCtags diff --git a/doc/apis.rst b/doc/apis.md similarity index 100% rename from doc/apis.rst rename to doc/apis.md diff --git a/doc/backends.rst b/doc/backends.md similarity index 99% rename from doc/backends.rst rename to doc/backends.md index 65dd8a6f2..a135e78b0 100644 --- a/doc/backends.rst +++ b/doc/backends.md @@ -10,7 +10,8 @@ .. no syntax highlighting here by default: .. contents:: - "Heresy grows from idleness." -- Unknown. + +> "Heresy grows from idleness." -- Unknown. Introduction diff --git a/doc/contributing.rst b/doc/contributing.md similarity index 99% rename from doc/contributing.rst rename to doc/contributing.md index 0ca0d0cbc..51d1d5065 100644 --- a/doc/contributing.rst +++ b/doc/contributing.md @@ -581,7 +581,7 @@ Code reviews -.. include:: docstyle.rst +.. include:: docstyle.md Evolving the stdlib diff --git a/doc/destructors.rst b/doc/destructors.md similarity index 100% rename from doc/destructors.rst rename to doc/destructors.md diff --git a/doc/docgen.rst b/doc/docgen.md similarity index 98% rename from doc/docgen.rst rename to doc/docgen.md index 48166b2c5..5f5c4ecc0 100644 --- a/doc/docgen.rst +++ b/doc/docgen.md @@ -35,14 +35,13 @@ Quick start Generate HTML documentation for a file: -.. code:: cmd - + ```cmd nim doc .nim + ``` Generate HTML documentation for a whole project: -.. code:: cmd - + ```cmd # delete any htmldocs/*.idx file before starting nim doc --project --index:on --git.url: --git.commit: --outdir:htmldocs .nim # this will generate html files, a theindex.html index, css and js under `htmldocs` @@ -54,7 +53,7 @@ Generate HTML documentation for a whole project: # or `$nimcache/htmldocs` with `--usenimcache` which avoids clobbering your sources; # and likewise without `--project`. # Adding `-r` will open in a browser directly. - + ``` Documentation Comments ---------------------- @@ -120,8 +119,8 @@ Example of Nim file input The following examples will generate documentation for this sample *Nim* module, aptly named ``doc/docgen_sample.nim``: -.. code:: nim - :file: docgen_sample.nim + ```nim file=docgen_sample.nim + ``` All the below commands save their output to ``htmldocs`` directory relative to the directory of file; @@ -137,9 +136,9 @@ optionally, an index file. The `doc`:option: command: -.. code:: cmd - + ```cmd nim doc docgen_sample.nim + ``` Partial Output:: ... @@ -159,8 +158,7 @@ HTML -> PDF conversion). The `doc2tex`:option: command: -.. code:: cmd - + ```cmd nim doc2tex docgen_sample.nim cd htmldocs xelatex docgen_sample.tex @@ -169,6 +167,7 @@ The `doc2tex`:option: command: # large documents) to get all labels generated. # That depends on this warning in the end of `xelatex` output: # LaTeX Warning: Label(s) may have changed. Rerun to get cross-references right. + ``` The output is ``docgen_sample.pdf``. @@ -183,9 +182,9 @@ Note that this tool is built off of the `doc`:option: command The `jsondoc`:option: command: -.. code:: cmd - + ```cmd nim jsondoc docgen_sample.nim + ``` Output:: { @@ -209,9 +208,9 @@ renamed to `jsondoc0`:option:. The `jsondoc0`:option: command: -.. code:: cmd - + ```cmd nim jsondoc0 docgen_sample.nim + ``` Output:: [ @@ -249,9 +248,9 @@ the anchor [*]_ of Nim symbol that corresponds to link text. If you have a constant: -.. code:: Nim - + ```Nim const pi* = 3.14 + ``` then it should be referenced in one of the 2 forms: @@ -262,9 +261,9 @@ B. qualified (with symbol kind specification):: For routine kinds there are more options. Consider this definition: -.. code:: Nim - + ```Nim proc foo*(a: int, b: float): string + ``` Generally following syntax is allowed for referencing `foo`: @@ -352,11 +351,11 @@ recognized fine:: (without parameter names, see form A.2 above). E.g. for this signature: - .. code:: Nim - + ```Nim proc binarySearch*[T, K](a: openArray[T]; key: K; cmp: proc (x: T; y: K): int {.closure.}): int ~~ ~~ ~~~~~ + ``` you cannot use names underlined by `~~` so it must be referenced with ``cmp: proc(T, K)``. Hence these forms are valid:: @@ -379,10 +378,10 @@ recognized fine:: .. Note:: A bit special case is operators (as their signature is also defined with `\``): - .. code:: Nim - + ```Nim func `$`(x: MyType): string func `[]`*[T](x: openArray[T]): T + ``` A short form works without additional backticks:: @@ -412,9 +411,9 @@ Related Options Project switch -------------- -.. code:: cmd - + ```cmd nim doc --project filename.nim + ``` This will recursively generate documentation of all Nim modules imported into the input module that belong to the Nimble package that ``filename.nim`` @@ -425,9 +424,9 @@ also be generated. Index switch ------------ -.. code:: cmd - + ```cmd nim doc --index:on filename.nim + ``` This will generate an index of all the exported symbols in the input Nim module, and put it into a neighboring file with the extension of ``.idx``. The @@ -443,9 +442,9 @@ file. See source switch ----------------- -.. code:: cmd - + ```cmd nim doc --git.url: filename.nim + ``` With the `git.url`:option: switch the *See source* hyperlink will appear below each documented item in your source code pointing to the implementation of that @@ -490,9 +489,9 @@ supports highlighting of a few other languages supported by the Usage: -.. code:: cmd - + ```cmd nim rst2html docgen.rst + ``` Output:: You're reading it! @@ -528,7 +527,7 @@ HTML file, most browsers will go to the first one. To differentiate the rest, you will need to use the complex name. A complex name for a callable type is made up of several parts: - (**plain symbol**)(**.type**),(**first param**)?(**,param type**)\* + (**plain symbol**)(**.type**),(**first param**)?(**,param type**)\* The first thing to note is that all callable types have at least a comma, even if they don't have any parameters. If there are parameters, they are diff --git a/doc/docs.rst b/doc/docs.md similarity index 100% rename from doc/docs.rst rename to doc/docs.md diff --git a/doc/docstyle.rst b/doc/docstyle.md similarity index 100% rename from doc/docstyle.rst rename to doc/docstyle.md diff --git a/doc/drnim.rst b/doc/drnim.md similarity index 100% rename from doc/drnim.rst rename to doc/drnim.md diff --git a/doc/estp.rst b/doc/estp.md similarity index 100% rename from doc/estp.rst rename to doc/estp.md diff --git a/doc/filters.rst b/doc/filters.md similarity index 100% rename from doc/filters.rst rename to doc/filters.md diff --git a/doc/hcr.rst b/doc/hcr.md similarity index 100% rename from doc/hcr.rst rename to doc/hcr.md diff --git a/doc/idetools.rst b/doc/idetools.md similarity index 100% rename from doc/idetools.rst rename to doc/idetools.md diff --git a/doc/intern.rst b/doc/intern.md similarity index 99% rename from doc/intern.rst rename to doc/intern.md index 9103c694c..0fd995582 100644 --- a/doc/intern.rst +++ b/doc/intern.md @@ -10,7 +10,7 @@ .. include:: rstcommon.rst .. contents:: - "Abstraction is layering ignorance on top of reality." -- Richard Gabriel +> "Abstraction is layering ignorance on top of reality." -- Richard Gabriel Directory structure @@ -276,8 +276,8 @@ and `exitingDebugSection()`:nim:. #. Compile the temp compiler with `--debugger:native -d:nimDebugUtils`:option: #. Set your desired breakpoints or watchpoints. #. Configure your debugger: - * GDB: execute `source tools/compiler.gdb` at startup - * LLDB execute `command source tools/compiler.lldb` at startup + * GDB: execute `source tools/compiler.gdb` at startup + * LLDB execute `command source tools/compiler.lldb` at startup #. Use one of the scoping helpers like so: .. code-block:: nim diff --git a/doc/koch.rst b/doc/koch.md similarity index 100% rename from doc/koch.rst rename to doc/koch.md diff --git a/doc/lib.rst b/doc/lib.md similarity index 100% rename from doc/lib.rst rename to doc/lib.md diff --git a/doc/manual.rst b/doc/manual.md similarity index 99% rename from doc/manual.rst rename to doc/manual.md index 571379a87..503b9538b 100644 --- a/doc/manual.rst +++ b/doc/manual.md @@ -10,9 +10,9 @@ Nim Manual .. contents:: - "Complexity" seems to be a lot like "energy": you can transfer it from the - end-user to one/some of the other players, but the total amount seems to remain - pretty much constant for a given task. -- Ran +> "Complexity" seems to be a lot like "energy": you can transfer it from the +> end-user to one/some of the other players, but the total amount seems to remain +> pretty much constant for a given task. -- Ran About this document @@ -4025,7 +4025,7 @@ In the standard library every name of a routine that returns a `var` type starts with the prefix `m` per convention. -.. include:: manual/var_t_return.rst +.. include:: manual/var_t_return.md Future directions ~~~~~~~~~~~~~~~~~ diff --git a/doc/manual/var_t_return.rst b/doc/manual/var_t_return.md similarity index 100% rename from doc/manual/var_t_return.rst rename to doc/manual/var_t_return.md diff --git a/doc/manual_experimental.rst b/doc/manual_experimental.md similarity index 99% rename from doc/manual_experimental.rst rename to doc/manual_experimental.md index 3089755cb..3968163c5 100644 --- a/doc/manual_experimental.rst +++ b/doc/manual_experimental.md @@ -505,7 +505,7 @@ The compiler ensures that every code path initializes variables which contain non-nilable pointers. The details of this analysis are still to be specified here. -.. include:: manual_experimental_strictnotnil.rst +.. include:: manual_experimental_strictnotnil.md Aliasing restrictions in parameter passing diff --git a/doc/manual_experimental_strictnotnil.rst b/doc/manual_experimental_strictnotnil.md similarity index 100% rename from doc/manual_experimental_strictnotnil.rst rename to doc/manual_experimental_strictnotnil.md diff --git a/doc/mm.rst b/doc/mm.md similarity index 98% rename from doc/mm.rst rename to doc/mm.md index b6941a901..09b235228 100644 --- a/doc/mm.rst +++ b/doc/mm.md @@ -11,7 +11,7 @@ Nim's Memory Management .. - "The road to hell is paved with good intentions." +> "The road to hell is paved with good intentions." Multi-paradigm Memory Management Strategies diff --git a/doc/nep1.rst b/doc/nep1.md similarity index 100% rename from doc/nep1.rst rename to doc/nep1.md diff --git a/doc/nimc.rst b/doc/nimc.md similarity index 99% rename from doc/nimc.rst rename to doc/nimc.md index aa6650491..28a917d92 100644 --- a/doc/nimc.rst +++ b/doc/nimc.md @@ -11,9 +11,9 @@ .. - "Look at you, hacker. A pathetic creature of meat and bone, panting and - sweating as you run through my corridors. How can you challenge a perfect, - immortal machine?" +> "Look at you, hacker. A pathetic creature of meat and bone, panting and +> sweating as you run through my corridors. How can you challenge a perfect, +> immortal machine?" Introduction diff --git a/doc/nimfix.rst b/doc/nimfix.md similarity index 100% rename from doc/nimfix.rst rename to doc/nimfix.md diff --git a/doc/nimgrep.rst b/doc/nimgrep.md similarity index 99% rename from doc/nimgrep.rst rename to doc/nimgrep.md index 6088a4c45..ff2bf3a8d 100644 --- a/doc/nimgrep.rst +++ b/doc/nimgrep.md @@ -54,7 +54,7 @@ All examples below use default PCRE Regex patterns: nimgrep --excludeDir:'^\.git$' --excludeDir:'^\.hg$' --excludeDir:'^\.svn$' # short: --ed:'^\.git$' --ed:'^\.hg$' --ed:'^\.svn$' -+ To search only in paths containing the `tests` sub-directory recursively:: ++ To search only in paths containing the `tests` sub-directory recursively: .. code:: cmd nimgrep --recursive --includeDir:'(^|/)tests($|/)' diff --git a/doc/niminst.rst b/doc/niminst.md similarity index 100% rename from doc/niminst.rst rename to doc/niminst.md diff --git a/doc/nims.rst b/doc/nims.md similarity index 100% rename from doc/nims.rst rename to doc/nims.md diff --git a/doc/nimsuggest.rst b/doc/nimsuggest.md similarity index 100% rename from doc/nimsuggest.rst rename to doc/nimsuggest.md diff --git a/doc/overview.rst b/doc/overview.md similarity index 86% rename from doc/overview.rst rename to doc/overview.md index e01520d7c..b21eb1e68 100644 --- a/doc/overview.rst +++ b/doc/overview.md @@ -5,5 +5,5 @@ Nim Documentation Overview :Author: Andreas Rumpf :Version: |nimversion| -.. include:: docs.rst +.. include:: docs.md diff --git a/doc/packaging.rst b/doc/packaging.md similarity index 100% rename from doc/packaging.rst rename to doc/packaging.md diff --git a/doc/refc.rst b/doc/refc.md similarity index 100% rename from doc/refc.rst rename to doc/refc.md diff --git a/doc/testament.rst b/doc/testament.md similarity index 100% rename from doc/testament.rst rename to doc/testament.md diff --git a/doc/tools.rst b/doc/tools.md similarity index 100% rename from doc/tools.rst rename to doc/tools.md diff --git a/doc/tut1.rst b/doc/tut1.md similarity index 100% rename from doc/tut1.rst rename to doc/tut1.md diff --git a/doc/tut2.rst b/doc/tut2.md similarity index 99% rename from doc/tut2.rst rename to doc/tut2.md index 725f68dd5..d37d6a16a 100644 --- a/doc/tut2.rst +++ b/doc/tut2.md @@ -13,7 +13,7 @@ Nim Tutorial (Part II) Introduction ============ - "Repetition renders the ridiculous reasonable." -- Norman Wildberger +> "Repetition renders the ridiculous reasonable." -- Norman Wildberger This document is a tutorial for the advanced constructs of the *Nim* programming language. **Note that this document is somewhat obsolete as the** diff --git a/doc/tut3.rst b/doc/tut3.md similarity index 99% rename from doc/tut3.rst rename to doc/tut3.md index c2c956107..2fcfd4220 100644 --- a/doc/tut3.rst +++ b/doc/tut3.md @@ -13,7 +13,7 @@ Nim Tutorial (Part III) Introduction ============ - "With Great Power Comes Great Responsibility." -- Spider Man's Uncle +> "With Great Power Comes Great Responsibility." -- Spider Man's Uncle This document is a tutorial about Nim's macro system. A macro is a function that is executed at compile-time and transforms diff --git a/lib/packages/docutils/highlite.nim b/lib/packages/docutils/highlite.nim index d36b2c877..3af94ab21 100644 --- a/lib/packages/docutils/highlite.nim +++ b/lib/packages/docutils/highlite.nim @@ -125,9 +125,7 @@ proc initGeneralTokenizer*(g: var GeneralTokenizer, buf: cstring) = g.length = 0 g.state = low(TokenClass) g.lang = low(SourceLanguage) - var pos = 0 # skip initial whitespace: - while g.buf[pos] in {' ', '\t'..'\r'}: inc(pos) - g.pos = pos + g.pos = 0 proc initGeneralTokenizer*(g: var GeneralTokenizer, buf: string) = initGeneralTokenizer(g, cstring(buf)) diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index cd5f26278..1721674c8 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -8,20 +8,23 @@ # ## ================================== -## rst +## packages/docutils/rst ## ================================== ## ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ## Nim-flavored reStructuredText and Markdown ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ## -## This module implements a `reStructuredText`:idx: (RST) parser. +## This module implements a `reStructuredText`:idx: (RST) and +## `Markdown`:idx: parser. ## A large subset is implemented with some limitations_ and ## `Nim-specific features`_. -## A few `extra features`_ of the `Markdown`:idx: syntax are -## also supported. +## Both Markdown and RST are mark-up languages whose goal is to +## typeset texts with complex structure, formatting and references +## using simple plaintext representation. ## -## Nim can output the result to HTML [#html]_ or Latex [#latex]_. +## This module is also embedded into Nim compiler; the compiler can output +## the result to HTML [#html]_ or Latex [#latex]_. ## ## .. [#html] commands `nim doc`:cmd: for ``*.nim`` files and ## `nim rst2html`:cmd: for ``*.rst`` files @@ -29,11 +32,13 @@ ## .. [#latex] commands `nim doc2tex`:cmd: for ``*.nim`` and ## `nim rst2tex`:cmd: for ``*.rst``. ## -## If you are new to RST please consider reading the following: +## If you are new to Markdown/RST please consider reading the following: ## -## 1) a short `quick introduction`_ -## 2) an `RST reference`_: a comprehensive cheatsheet for RST -## 3) a more formal 50-page `RST specification`_. +## 1) `Markdown Basic Syntax`_ +## 2) a long specification of Markdown: `CommonMark Spec`_ +## 3) a short `quick introduction`_ to RST +## 4) an `RST reference`_: a comprehensive cheatsheet for RST +## 5) a more formal 50-page `RST specification`_. ## ## Features ## -------- @@ -120,7 +125,13 @@ ## ## * emoji / smiley symbols ## * Markdown tables -## * Markdown code blocks +## * Markdown code blocks. For them the same additional arguments as for RST +## code blocks can be provided (e.g. `test` or `number-lines`) but with +## a one-line syntax like this:: +## +## ```nim test number-lines=10 +## echo "ok" +## ``` ## * Markdown links ## * Markdown headlines ## * Markdown block quotes @@ -211,6 +222,8 @@ ## See `packages/docutils/rstgen module `_ to know how to ## generate HTML or Latex strings to embed them into your documents. ## +## .. _Markdown Basic Syntax: https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax +## .. _CommonMark Spec: https://spec.commonmark.org/0.30 ## .. _quick introduction: https://docutils.sourceforge.io/docs/user/rst/quickstart.html ## .. _RST reference: https://docutils.sourceforge.io/docs/user/rst/quickref.html ## .. _RST specification: https://docutils.sourceforge.io/docs/ref/rst/restructuredtext.html @@ -253,6 +266,7 @@ type MsgKind* = enum ## the possible messages meCannotOpenFile = "cannot open '$1'", meExpected = "'$1' expected", + meMissingClosing = "$1", meGridTableNotImplemented = "grid table is not implemented", meMarkdownIllformedTable = "illformed delimiter row of a Markdown table", meIllformedTable = "Illformed table: $1", @@ -323,7 +337,10 @@ const ":geek:": "icon_e_geek", ":ugeek:": "icon_e_ugeek" } - SandboxDirAllowlist = ["image", "code", "code-block"] + SandboxDirAllowlist = [ + "image", "code", "code-block", "admonition", "attention", "caution", + "container", "contents", "danger", "default-role", "error", "figure", + "hint", "important", "index", "note", "role", "tip", "title", "warning"] type TokType = enum @@ -1616,35 +1633,89 @@ proc parseUntil(p: var RstParser, father: PRstNode, postfix: string, inc p.idx else: rstMessage(p, meExpected, postfix, line, col) +proc parseMarkdownCodeblockFields(p: var RstParser): PRstNode = + ## Parses additional (after language string) code block parameters + ## in a format *suggested* in the `CommonMark Spec`_ with handling of `"`. + if currentTok(p).kind == tkIndent: + result = nil + else: + result = newRstNode(rnFieldList) + while currentTok(p).kind != tkIndent: + if currentTok(p).kind == tkWhite: + inc p.idx + else: + let field = newRstNode(rnField) + var fieldName = "" + while currentTok(p).kind notin {tkWhite, tkIndent, tkEof} and + currentTok(p).symbol != "=": + fieldName.add currentTok(p).symbol + inc p.idx + field.add(newRstNode(rnFieldName, @[newLeaf(fieldName)])) + if currentTok(p).kind == tkWhite: inc p.idx + let fieldBody = newRstNode(rnFieldBody) + if currentTok(p).symbol == "=": + inc p.idx + if currentTok(p).kind == tkWhite: inc p.idx + var fieldValue = "" + if currentTok(p).symbol == "\"": + while true: + fieldValue.add currentTok(p).symbol + inc p.idx + if currentTok(p).kind == tkEof: + rstMessage(p, meExpected, "\"") + elif currentTok(p).symbol == "\"": + fieldValue.add "\"" + inc p.idx + break + else: + while currentTok(p).kind notin {tkWhite, tkIndent, tkEof}: + fieldValue.add currentTok(p).symbol + inc p.idx + fieldBody.add newLeaf(fieldValue) + field.add(fieldBody) + result.add(field) + proc parseMarkdownCodeblock(p: var RstParser): PRstNode = result = newRstNodeA(p, rnCodeBlock) + let line = curLine(p) + let baseCol = currentTok(p).col + let baseSym = currentTok(p).symbol # usually just ``` + inc p.idx result.info = lineInfo(p) var args = newRstNode(rnDirArg) + var fields: PRstNode = nil if currentTok(p).kind == tkWord: args.add(newLeaf(p)) inc p.idx + fields = parseMarkdownCodeblockFields(p) else: args = nil var n = newLeaf("") while true: - case currentTok(p).kind - of tkEof: - rstMessage(p, meExpected, "```") + if currentTok(p).kind == tkEof: + rstMessage(p, meMissingClosing, + "$1 (started at line $2)" % [baseSym, $line]) break - of tkPunct, tkAdornment: - if currentTok(p).symbol == "```": - inc p.idx - break - else: - n.text.add(currentTok(p).symbol) - inc p.idx + elif nextTok(p).kind in {tkPunct, tkAdornment} and + nextTok(p).symbol[0] == baseSym[0] and + nextTok(p).symbol.len >= baseSym.len: + inc p.idx, 2 + break + elif currentTok(p).kind == tkIndent: + n.text.add "\n" + if currentTok(p).ival > baseCol: + n.text.add " ".repeat(currentTok(p).ival - baseCol) + elif currentTok(p).ival < baseCol: + rstMessage(p, mwRstStyle, + "unexpected de-indentation in Markdown code block") + inc p.idx else: n.text.add(currentTok(p).symbol) inc p.idx var lb = newRstNode(rnLiteralBlock) lb.add(n) result.add(args) - result.add(PRstNode(nil)) + result.add(fields) result.add(lb) proc parseMarkdownLink(p: var RstParser; father: PRstNode): bool = @@ -1730,6 +1801,12 @@ proc parseFootnoteName(p: var RstParser, reference: bool): PRstNode = inc i p.idx = i +proc isMarkdownCodeBlock(p: RstParser): bool = + result = (roSupportMarkdown in p.s.options and + currentTok(p).kind in {tkPunct, tkAdornment} and + currentTok(p).symbol[0] == '`' and # tilde ~ is not supported + currentTok(p).symbol.len >= 3) + proc parseInline(p: var RstParser, father: PRstNode) = var n: PRstNode # to be used in `if` condition let saveIdx = p.idx @@ -1755,8 +1832,7 @@ proc parseInline(p: var RstParser, father: PRstNode) = addAnchorRst(p, name = linkName(n), refn = refn, reset = true, anchorType=manualInlineAnchor) father.add(n) - elif roSupportMarkdown in p.s.options and currentTok(p).symbol == "```": - inc p.idx + elif isMarkdownCodeBlock(p): father.add(parseMarkdownCodeblock(p)) elif isInlineMarkupStart(p, "``"): var n = newRstNode(rnInlineLiteral) @@ -1816,8 +1892,7 @@ proc parseInline(p: var RstParser, father: PRstNode) = return parseWordOrRef(p, father) of tkAdornment, tkOther, tkWhite: - if roSupportMarkdown in p.s.options and currentTok(p).symbol == "```": - inc p.idx + if isMarkdownCodeBlock(p): father.add(parseMarkdownCodeblock(p)) return if roSupportSmilies in p.s.options: @@ -2194,7 +2269,7 @@ proc findPipe(p: RstParser, start: int): bool = proc whichSection(p: RstParser): RstNodeKind = if currentTok(p).kind in {tkAdornment, tkPunct}: # for punctuation sequences that can be both tkAdornment and tkPunct - if roSupportMarkdown in p.s.options and currentTok(p).symbol == "```": + if isMarkdownCodeBlock(p): return rnCodeBlock elif currentTok(p).symbol == "::": return rnLiteralBlock @@ -2633,7 +2708,9 @@ proc parseSimpleTable(p: var RstParser): PRstNode = # fix rnTableDataCell -> rnTableHeaderCell for previous table rows: for nRow in 0 ..< result.sons.len: for nCell in 0 ..< result.sons[nRow].len: - result.sons[nRow].sons[nCell].kind = rnTableHeaderCell + template cell: PRstNode = result.sons[nRow].sons[nCell] + cell = PRstNode(kind: rnTableHeaderCell, sons: cell.sons, + span: cell.span, anchor: cell.anchor) if currentTok(p).kind == tkEof: break let tabRow = parseSimpleTableRow(p, cols, colChar) result.add tabRow @@ -2892,11 +2969,19 @@ proc parseSection(p: var RstParser, result: PRstNode) = if currInd(p) == currentTok(p).ival: inc p.idx elif currentTok(p).ival > currInd(p): - pushInd(p, currentTok(p).ival) - var a = newRstNodeA(p, rnBlockQuote) - parseSection(p, a) - result.add(a) - popInd(p) + if roPreferMarkdown in p.s.options: # Markdown => normal paragraphs + if currentTok(p).ival - currInd(p) >= 4: + rstMessage(p, mwRstStyle, + "Markdown indented code not implemented") + pushInd(p, currentTok(p).ival) + parseSection(p, result) + popInd(p) + else: # RST mode => block quotes + pushInd(p, currentTok(p).ival) + var a = newRstNodeA(p, rnBlockQuote) + parseSection(p, a) + result.add(a) + popInd(p) else: while currentTok(p).kind != tkEof and nextTok(p).kind == tkIndent: inc p.idx # skip blank lines diff --git a/tests/stdlib/thighlite.nim b/tests/stdlib/thighlite.nim index 4113a6a80..5134215c1 100644 --- a/tests/stdlib/thighlite.nim +++ b/tests/stdlib/thighlite.nim @@ -12,6 +12,12 @@ block: # Nim tokenizing @[("\"\"\"ok1\\nok2\\nok3\"\"\"", gtLongStringLit) ]) + test "whitespace at beginning of line is preserved": + check(" discard 1".tokenize(langNim) == + @[(" ", gtWhitespace), ("discard", gtKeyword), (" ", gtWhitespace), + ("1", gtDecNumber) + ]) + block: # Cmd (shell) tokenizing test "cmd with dollar and output": check( diff --git a/tests/stdlib/trst.nim b/tests/stdlib/trst.nim index 24bc03027..823697336 100644 --- a/tests/stdlib/trst.nim +++ b/tests/stdlib/trst.nim @@ -24,8 +24,11 @@ import unittest, strutils import std/private/miscdollars import os +const preferMarkdown = {roPreferMarkdown, roSupportMarkdown, roNimFile, roSandboxDisabled} +const preferRst = {roSupportMarkdown, roNimFile, roSandboxDisabled} + proc toAst(input: string, - rstOptions: RstParseOptions = {roPreferMarkdown, roSupportMarkdown, roNimFile, roSandboxDisabled}, + rstOptions: RstParseOptions = preferMarkdown, error: ref string = nil, warnings: ref seq[string] = nil): string = ## If `error` is nil then no errors should be generated. @@ -451,7 +454,7 @@ suite "RST parsing": > - y > > Paragraph. - """.toAst == dedent""" + """.toAst(rstOptions = preferRst) == dedent""" rnMarkdownBlockQuote rnMarkdownBlockQuoteItem quotationDepth=1 rnInner @@ -468,6 +471,93 @@ suite "RST parsing": rnLeaf '.' """) + test "Markdown code blocks with more > 3 backticks": + check(dedent""" + ```` + let a = 1 + ``` + ````""".toAst == + dedent""" + rnCodeBlock + [nil] + [nil] + rnLiteralBlock + rnLeaf ' + let a = 1 + ```' + """) + + test "Markdown code blocks with Nim-specific arguments": + check(dedent""" + ```nim number-lines=1 test + let a = 1 + ```""".toAst == + dedent""" + rnCodeBlock + rnDirArg + rnLeaf 'nim' + rnFieldList + rnField + rnFieldName + rnLeaf 'number-lines' + rnFieldBody + rnLeaf '1' + rnField + rnFieldName + rnLeaf 'test' + rnFieldBody + rnLiteralBlock + rnLeaf ' + let a = 1' + """) + + check(dedent""" + ```nim test = "nim c $1" number-lines = 1 + let a = 1 + ```""".toAst == + dedent""" + rnCodeBlock + rnDirArg + rnLeaf 'nim' + rnFieldList + rnField + rnFieldName + rnLeaf 'test' + rnFieldBody + rnLeaf '"nim c $1"' + rnField + rnFieldName + rnLeaf 'number-lines' + rnFieldBody + rnLeaf '1' + rnLiteralBlock + rnLeaf ' + let a = 1' + """) + + test "additional indentation < 4 spaces is handled fine": + check(dedent""" + Indentation + + ```nim + let a = 1 + ```""".toAst == + dedent""" + rnInner + rnParagraph + rnLeaf 'Indentation' + rnParagraph + rnCodeBlock + rnDirArg + rnLeaf 'nim' + [nil] + rnLiteralBlock + rnLeaf ' + let a = 1' + """) + # | | + # | \ indentation of exactly two spaces before 'let a = 1' + test "option list has priority over definition list": check(dedent""" --defusages @@ -562,7 +652,7 @@ suite "RST parsing": notAcomment1 notAcomment2 - someParagraph""".toAst == + someParagraph""".toAst(rstOptions = preferRst) == dedent""" rnInner rnBlockQuote @@ -574,6 +664,25 @@ suite "RST parsing": rnLeaf 'someParagraph' """) + test "check that additional line right after .. ends comment (Markdown mode)": + # in Markdown small indentation does not matter so this should + # just be split to 2 paragraphs. + check(dedent""" + .. + + notAcomment1 + notAcomment2 + someParagraph""".toAst == + dedent""" + rnInner + rnInner + rnLeaf 'notAcomment1' + rnLeaf ' ' + rnLeaf 'notAcomment2' + rnParagraph + rnLeaf 'someParagraph' + """) + test "but blank lines after 2nd non-empty line don't end the comment": check(dedent""" .. @@ -592,7 +701,7 @@ suite "RST parsing": .. - someBlockQuote""".toAst == + someBlockQuote""".toAst(rstOptions = preferRst) == dedent""" rnInner rnAdmonition adType=note diff --git a/tests/stdlib/trstgen.nim b/tests/stdlib/trstgen.nim index d91b5615e..7fae4ba8b 100644 --- a/tests/stdlib/trstgen.nim +++ b/tests/stdlib/trstgen.nim @@ -9,8 +9,13 @@ import ../../lib/packages/docutils/rst import unittest, strutils, strtabs import std/private/miscdollars +const + NoSandboxOpts = {roPreferMarkdown, roSupportMarkdown, roNimFile, roSandboxDisabled} + preferMarkdown = {roPreferMarkdown, roSupportMarkdown, roNimFile} + preferRst = {roSupportMarkdown, roNimFile} + proc toHtml(input: string, - rstOptions: RstParseOptions = {roPreferMarkdown, roSupportMarkdown, roNimFile}, + rstOptions: RstParseOptions = preferMarkdown, error: ref string = nil, warnings: ref seq[string] = nil): string = ## If `error` is nil then no errors should be generated. @@ -47,9 +52,6 @@ proc optionListLabel(opt: string): string = opt & "
        " -const - NoSandboxOpts = {roPreferMarkdown, roSupportMarkdown, roNimFile, roSandboxDisabled} - suite "YAML syntax highlighting": test "Basics": @@ -1180,7 +1182,7 @@ Test1 "input(8, 4) Warning: language 'anotherLang' not supported" ]) check(output == "
        anything
        " & - "

        \nsomeCode\n

        ") + "

        \nsomeCode

        ") test "RST admonitions": # check that all admonitions are implemented @@ -1321,7 +1323,7 @@ Test1 That was a transition. """ let output1 = input1.toHtml( - NoSandboxOpts + preferRst ) doAssert "

        text""" & "\n

    Authors:Andreas Rumpf, Zahary Karadjov
    Version:|nimversion|
    ") test "Field list: body after newline": - let output = dedent """ + let output = dedent""" :field: text1""".toHtml check " Date: Fri, 15 Jul 2022 20:56:05 +0300 Subject: [PATCH 3001/3768] Implement type command (#19944) * Implement type command - this will be mapped to textDocument/typeDefinition in LSP protocol. It will be very useful for `nim` in particular because typically most of the time the type is inferred. * Update nimsuggest/nimsuggest.nim Co-authored-by: Andreas Rumpf --- compiler/options.nim | 4 +++- nimsuggest/nimsuggest.nim | 12 +++++++++- nimsuggest/tester.nim | 2 +- nimsuggest/tests/tv3_typeDefinition.nim | 32 +++++++++++++++++++++++++ 4 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 nimsuggest/tests/tv3_typeDefinition.nim diff --git a/compiler/options.nim b/compiler/options.nim index d567927cb..9031e86c1 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -192,7 +192,7 @@ type IdeCmd* = enum ideNone, ideSug, ideCon, ideDef, ideUse, ideDus, ideChk, ideChkFile, ideMod, ideHighlight, ideOutline, ideKnown, ideMsg, ideProject, ideGlobalSymbols, - ideRecompile, ideChanged + ideRecompile, ideChanged, ideType Feature* = enum ## experimental features; DO NOT RENAME THESE! implicitDeref, @@ -1006,6 +1006,7 @@ proc parseIdeCmd*(s: string): IdeCmd = of "globalSymbols": ideGlobalSymbols of "recompile": ideRecompile of "changed": ideChanged + of "type": ideType else: ideNone proc `$`*(c: IdeCmd): string = @@ -1027,6 +1028,7 @@ proc `$`*(c: IdeCmd): string = of ideGlobalSymbols: "globalSymbols" of ideRecompile: "recompile" of ideChanged: "changed" + of ideType: "type" proc floatInt64Align*(conf: ConfigRef): int16 = ## Returns either 4 or 8 depending on reasons. diff --git a/nimsuggest/nimsuggest.nim b/nimsuggest/nimsuggest.nim index a18b2f960..6f35be925 100644 --- a/nimsuggest/nimsuggest.nim +++ b/nimsuggest/nimsuggest.nim @@ -457,6 +457,7 @@ proc execCmd(cmd: string; graph: ModuleGraph; cachedMsgs: CachedMsgs) = of "globalsymbols": conf.ideCmd = ideGlobalSymbols of "chkfile": conf.ideCmd = ideChkFile of "recompile": conf.ideCmd = ideRecompile + of "type": conf.ideCmd = ideType else: err() var dirtyfile = "" var orig = "" @@ -786,7 +787,7 @@ proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile, graph.unmarkAllDirty() # these commands require partially compiled project - elif cmd in {ideSug, ideOutline, ideHighlight, ideDef, ideChkFile} and + elif cmd in {ideSug, ideOutline, ideHighlight, ideDef, ideChkFile, ideType} and (graph.needsCompilation(fileIndex) or cmd == ideSug): # for ideSug use v2 implementation if cmd == ideSug: @@ -802,6 +803,15 @@ proc executeNoHooksV3(cmd: IdeCmd, file: AbsoluteFile, dirtyfile: AbsoluteFile, let (sym, info) = graph.findSymData(file, line, col) if sym != nil: graph.suggestResult(sym, sym.info) + of ideType: + let (sym, _) = graph.findSymData(file, line, col) + if sym != nil: + let typeSym = sym.typ.sym + if typeSym != nil: + graph.suggestResult(typeSym, typeSym.info, ideType) + elif sym.typ.len != 0: + let genericType = sym.typ[0].sym + graph.suggestResult(genericType, genericType.info, ideType) of ideUse, ideDus: let symbol = graph.findSymData(file, line, col).sym if symbol != nil: diff --git a/nimsuggest/tester.nim b/nimsuggest/tester.nim index fea0a8d45..6e068e067 100644 --- a/nimsuggest/tester.nim +++ b/nimsuggest/tester.nim @@ -282,7 +282,7 @@ proc runEpcTest(filename: string): int = socket.sendEpcStr(req) let sx = parseSexp(socket.recvEpc()) if not req.startsWith("mod "): - let answer = sexpToAnswer(sx) + let answer = if sx[2].kind == SNil: "" else: sexpToAnswer(sx) doReport(filename, answer, resp, report) socket.sendEpcStr "return arg" diff --git a/nimsuggest/tests/tv3_typeDefinition.nim b/nimsuggest/tests/tv3_typeDefinition.nim new file mode 100644 index 000000000..f86d12cc6 --- /dev/null +++ b/nimsuggest/tests/tv3_typeDefinition.nim @@ -0,0 +1,32 @@ +# tests v3 + +type + Foo* = ref object of RootObj + bar*: string + +proc test(ff: Foo) = + echo f#[!]#f.bar + +type + Fo#[!]#o2* = ref object of RootObj + +type + FooGeneric[T] = ref object of RootObj + bar*: T + +let fooGeneric = FooGeneric[string]() +echo fo#[!]#oGeneric.bar + +# bad type +echo unde#[!]#fined + +discard """ +$nimsuggest --v3 --tester $file +>type $1 +type skType tv3_typeDefinition.Foo Foo $file 4 2 "" 100 +>type $2 +type skType tv3_typeDefinition.Foo2 Foo2 $file 11 2 "" 100 +>type $3 +type skType tv3_typeDefinition.FooGeneric FooGeneric $file 14 2 "" 100 +>type $4 +""" From b0b9a3e5fade002b8f117f7ffb4d8fb93686401d Mon Sep 17 00:00:00 2001 From: Ivan Yonchovski Date: Fri, 15 Jul 2022 20:56:33 +0300 Subject: [PATCH 3002/3768] Use module actual file instead of PSym.info (#19956) After this you can do goto module from module import --- compiler/suggest.nim | 30 ++++++++++++++++++------------ nimsuggest/tests/tv3_import.nim | 7 +++++++ 2 files changed, 25 insertions(+), 12 deletions(-) create mode 100644 nimsuggest/tests/tv3_import.nim diff --git a/compiler/suggest.nim b/compiler/suggest.nim index 4b48ef5bb..7e7d876fb 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -157,19 +157,25 @@ proc symToSuggest*(g: ModuleGraph; s: PSym, isLocal: bool, section: IdeCmd, info result.forth = "" when defined(nimsuggest) and not defined(noDocgen) and not defined(leanCompiler): result.doc = extractDocComment(g, s) - let infox = - if useSuppliedInfo or section in {ideUse, ideHighlight, ideOutline}: - info - else: - s.info - result.filePath = toFullPath(g.config, infox) - result.line = toLinenumber(infox) - result.column = toColumn(infox) + if s.kind == skModule and s.ast.len != 0 and section != ideHighlight: + result.filePath = toFullPath(g.config, s.ast[0].info) + result.line = 1 + result.column = 0 + result.tokenLen = 0 + else: + let infox = + if useSuppliedInfo or section in {ideUse, ideHighlight, ideOutline}: + info + else: + s.info + result.filePath = toFullPath(g.config, infox) + result.line = toLinenumber(infox) + result.column = toColumn(infox) + result.tokenLen = if section != ideHighlight: + s.name.s.len + else: + getTokenLenFromSource(g.config, s.name.s, infox) result.version = g.config.suggestVersion - result.tokenLen = if section != ideHighlight: - s.name.s.len - else: - getTokenLenFromSource(g.config, s.name.s, infox) proc `$`*(suggest: Suggest): string = result = $suggest.section diff --git a/nimsuggest/tests/tv3_import.nim b/nimsuggest/tests/tv3_import.nim new file mode 100644 index 000000000..3c128f85b --- /dev/null +++ b/nimsuggest/tests/tv3_import.nim @@ -0,0 +1,7 @@ +import tv#[!]#3 + +discard """ +$nimsuggest --v3 --tester $file +>def $1 +def skModule tv3 */tv3.nim 1 0 "" 100 +""" From 60dd38c50262774cadb1d365e649696380c0595e Mon Sep 17 00:00:00 2001 From: David Krause Date: Sat, 16 Jul 2022 11:59:58 +0200 Subject: [PATCH 3003/3768] make AsyncSocket.getPeerAddr work ; fix #15022 (#20038) Signed-off-by: David Krause --- lib/pure/asyncdispatch.nim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 0d406b271..ed786cefb 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -1733,6 +1733,8 @@ when defined(windows) or defined(nimdoc): proc (fd: AsyncFD, bytesCount: DWORD, errcode: OSErrorCode) = if not retFuture.finished: if errcode == OSErrorCode(-1): + const SO_UPDATE_CONNECT_CONTEXT = 0x7010 + socket.SocketHandle.setSockOptInt(SOL_SOCKET, SO_UPDATE_CONNECT_CONTEXT, 1) # 15022 retFuture.complete() else: retFuture.fail(newException(OSError, osErrorMsg(errcode))) From cf78c02b70ed105a173cce956ffab90bd57468a1 Mon Sep 17 00:00:00 2001 From: Amjad Ben Hedhili Date: Sat, 16 Jul 2022 21:51:27 +0100 Subject: [PATCH 3004/3768] Make `random.rand` work with `Ordinal` (#20043) * Make `random.rand` work with `Ordinal` * Add changelog entry * It's fine to cast to char --- changelog.md | 1 + lib/pure/random.nim | 15 +++++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/changelog.md b/changelog.md index 47479fc5c..c1dc6e27d 100644 --- a/changelog.md +++ b/changelog.md @@ -39,6 +39,7 @@ becomes an alias for `addr`. - `std/smtp` sends `ehlo` first. If the mail server does not understand, it sends `helo` as a fallback. - Changed mimedb to use an `OrderedTable` instead of `OrderedTableRef`, to use it in a const. - `strutils.find` now use and default to `last=-1` for whole string searches, making limiting it to just the first char (`last=0`) valid. +- `random.rand` now works with `Ordinal`s. [//]: # "Additions:" - Added `IsoWeekRange`, a range type to represent the number of weeks in an ISO week-based year. diff --git a/lib/pure/random.nim b/lib/pure/random.nim index b8aeb86e0..832d54e3d 100644 --- a/lib/pure/random.nim +++ b/lib/pure/random.nim @@ -353,8 +353,8 @@ proc rand*[T: Ordinal or SomeFloat](x: HSlice[T, T]): T = result = rand(state, x) -proc rand*[T: SomeInteger](t: typedesc[T]): T = - ## Returns a random integer in the range `low(T)..high(T)`. +proc rand*[T: Ordinal](t: typedesc[T]): T = + ## Returns a random Ordinal in the range `low(T)..high(T)`. ## ## If `randomize <#randomize>`_ has not been called, the sequence of random ## numbers returned from this proc will always be the same. @@ -368,13 +368,16 @@ proc rand*[T: SomeInteger](t: typedesc[T]): T = ## that accepts a slice runnableExamples: randomize(567) + type E = enum a, b, c, d if false: # implementation defined - assert rand(int8) == -42 - assert rand(uint32) == 578980729'u32 - assert rand(range[1..16]) == 11 + assert rand(E) in a..d + assert rand(char) in low(char)..high(char) + assert rand(int8) in low(int8)..high(int8) + assert rand(uint32) in low(uint32)..high(uint32) + assert rand(range[1..16]) in 1..16 # pending csources >= 1.4.0 or fixing https://github.com/timotheecour/Nim/issues/251#issuecomment-831599772, # use `runnableExamples("-r:off")` instead of `if false` - when T is range: + when T is range or T is enum or T is bool: result = rand(state, low(T)..high(T)) else: result = cast[T](state.next) From 094d86f99783f1366394fa444277c417dcbd1af3 Mon Sep 17 00:00:00 2001 From: Jacek Sieka Date: Sat, 16 Jul 2022 22:56:54 +0200 Subject: [PATCH 3005/3768] testament: use full test name in skips [backport] (#19937) testament: use full test name in skips --- testament/testament.nim | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/testament/testament.nim b/testament/testament.nim index d2b0e7fb4..98fc3c2aa 100644 --- a/testament/testament.nim +++ b/testament/testament.nim @@ -258,6 +258,15 @@ Tests failed and allowed to fail: $3 / $1
    Tests skipped: $4 / $1
    """ % [$x.total, $x.passed, $x.failedButAllowed, $x.skipped] +proc testName(test: TTest, target: TTarget, extraOptions: string, allowFailure: bool): string = + var name = test.name.replace(DirSep, '/') + name.add ' ' & $target + if allowFailure: + name.add " (allowed to fail) " + if test.options.len > 0: name.add ' ' & test.options + if extraOptions.len > 0: name.add ' ' & extraOptions + name.strip() + proc addResult(r: var TResults, test: TTest, target: TTarget, extraOptions, expected, given: string, successOrig: TResultEnum, allowFailure = false, givenSpec: ptr TSpec = nil) = @@ -265,13 +274,7 @@ proc addResult(r: var TResults, test: TTest, target: TTarget, # instead of having to pass individual fields, or abusing existing ones like expected vs given. # test.name is easier to find than test.name.extractFilename # A bit hacky but simple and works with tests/testament/tshould_not_work.nim - var name = test.name.replace(DirSep, '/') - name.add ' ' & $target - if allowFailure: - name.add " (allowed to fail) " - if test.options.len > 0: name.add ' ' & test.options - if extraOptions.len > 0: name.add ' ' & extraOptions - + let name = testName(test, target, extraOptions, allowFailure) let duration = epochTime() - test.startTime let success = if test.spec.timeout > 0.0 and duration > test.spec.timeout: reTimeout else: successOrig @@ -470,6 +473,9 @@ proc equalModuloLastNewline(a, b: string): bool = proc testSpecHelper(r: var TResults, test: var TTest, expected: TSpec, target: TTarget, extraOptions: string, nimcache: string) = test.startTime = epochTime() + if testName(test, target, extraOptions, false) in skips: + test.spec.err = reDisabled + if test.spec.err in {reDisabled, reJoined}: r.addResult(test, target, extraOptions, "", "", test.spec.err) inc(r.skipped) From c43a377057c56d5213eb298feba883363f2b2a26 Mon Sep 17 00:00:00 2001 From: Jake Leahy Date: Sun, 17 Jul 2022 07:41:18 +1000 Subject: [PATCH 3006/3768] Make imports/exports not be a dropdown in sidebar (#19907) * Don't make a section be a dropdown if it has no child links * - Cleaned up code - Updated tests * Document what the 'if' is checking --- compiler/docgen.nim | 17 ++++++++++++++--- config/nimdoc.cfg | 8 ++++++++ nimdoc/testproject/expected/testproject.html | 7 +------ 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/compiler/docgen.nim b/compiler/docgen.nim index ed5fe06ef..d728c535f 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -1474,10 +1474,21 @@ proc genSection(d: PDoc, kind: TSymKind, groupedToc = false) = for item in d.tocSimple[kind].sorted(cmp): d.toc2[kind].add item.content - d.toc[kind] = getConfigVar(d.conf, "doc.section.toc") % [ - "sectionid", $ord(kind), "sectionTitle", title, - "sectionTitleID", $(ord(kind) + 50), "content", d.toc2[kind]] + let sectionValues = @[ + "sectionID", $ord(kind), "sectionTitleID", $(ord(kind) + 50), + "sectionTitle", title + ] + # Check if the toc has any children + if d.toc2[kind] != "": + # Use the dropdown version instead and store the children in the dropdown + d.toc[kind] = getConfigVar(d.conf, "doc.section.toc") % (sectionValues & @[ + "content", d.toc2[kind] + ]) + else: + # Just have the link + d.toc[kind] = getConfigVar(d.conf, "doc.section.toc_item") % sectionValues + proc relLink(outDir: AbsoluteDir, destFile: AbsoluteFile, linkto: RelativeFile): string = $relativeTo(outDir / linkto, destFile.splitFile().dir, '/') diff --git a/config/nimdoc.cfg b/config/nimdoc.cfg index 3b0afe8d6..7aee50d8c 100644 --- a/config/nimdoc.cfg +++ b/config/nimdoc.cfg @@ -16,6 +16,14 @@ doc.section = """ """ +# Just a single item in the TOC (e.g. imports, exports) +doc.section.toc_item = """ +
  • + $sectionTitle +
  • +""" + +# This is a section (e.g. procs, types) in the TOC which gets turned into a drop down doc.section.toc = """
  • diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html index 24dfdc13f..5fb9e697c 100644 --- a/nimdoc/testproject/expected/testproject.html +++ b/nimdoc/testproject/expected/testproject.html @@ -51,12 +51,7 @@
      baz @@ -539,7 +540,15 @@
      -
      +
      +
      proc bar(f: FooBuzz) {....raises: [], tags: [], forbids: [].}
      +
      + + + +
      +
      +
      proc bar[T](a, b: T): T
      diff --git a/nimdoc/testproject/expected/testproject.idx b/nimdoc/testproject/expected/testproject.idx index 4d0e63c0c..4d0172313 100644 --- a/nimdoc/testproject/expected/testproject.idx +++ b/nimdoc/testproject/expected/testproject.idx @@ -7,6 +7,7 @@ bar testproject.html#bar,T,T testproject: bar[T](a, b: T): T baz testproject.html#baz,T,T testproject: baz[T](a, b: T): T buzz testproject.html#buzz,T,T testproject: buzz[T](a, b: T): T FooBuzz testproject.html#FooBuzz testproject: FooBuzz +bar testproject.html#bar testproject: bar(f: FooBuzz) aVariable testproject.html#aVariable testproject: aVariable A testproject.html#A testproject: A B testproject.html#B testproject: B diff --git a/nimdoc/testproject/expected/theindex.html b/nimdoc/testproject/expected/theindex.html index 402541540..9f73040f2 100644 --- a/nimdoc/testproject/expected/theindex.html +++ b/nimdoc/testproject/expected/theindex.html @@ -78,6 +78,8 @@
    bar:
    • testproject: bar(f: FooBuzz)
    • +
    • testproject: bar[T](a, b: T): T
    • testproject: bar(): untyped
    • diff --git a/nimdoc/testproject/testproject.nim b/nimdoc/testproject/testproject.nim index 57960c69f..d236552ac 100644 --- a/nimdoc/testproject/testproject.nim +++ b/nimdoc/testproject/testproject.nim @@ -42,6 +42,11 @@ proc buzz*[T](a, b: T): T {.deprecated: "since v0.20".} = type FooBuzz* {.deprecated: "FooBuzz msg".} = int +using f: FooBuzz + +proc bar*(f) = # `f` should be expanded to `f: FooBuzz` + discard + import std/macros var aVariable*: array[1, int] From 28bed059aadf808fe56eaf2b36274bdf6baec053 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 12 Dec 2022 11:46:51 +0100 Subject: [PATCH 3564/3768] closes #20808 (#21077) --- tests/effects/tfuncs_cannot_mutate3.nim | 35 +++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 tests/effects/tfuncs_cannot_mutate3.nim diff --git a/tests/effects/tfuncs_cannot_mutate3.nim b/tests/effects/tfuncs_cannot_mutate3.nim new file mode 100644 index 000000000..029152029 --- /dev/null +++ b/tests/effects/tfuncs_cannot_mutate3.nim @@ -0,0 +1,35 @@ +discard """ + errormsg: "cannot mutate location kid.parent within a strict func" + line: 16 +""" + +{.experimental: "strictFuncs".} + +type + Node = ref object + name: string + kids: seq[Node] + parent: Node + +func initParents(tree: Node) = + for kid in tree.kids: + kid.parent = tree + initParents(kid) + +proc process(intro: Node): Node = + var tree = Node(name: "root", kids: @[ + intro, + Node(name: "one", kids: @[ + Node(name: "two"), + Node(name: "three"), + ]), + Node(name: "four"), + ]) + initParents(tree) + +proc main() = + var intro = Node(name: "intro") + var tree = process(intro) + echo intro.parent.name + +main() From 19923aa8b7bb93f687b71942b2ab3de39cba2f5a Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 12 Dec 2022 21:14:24 +0800 Subject: [PATCH 3565/3768] work around chronos (#21079) * work around chronos chronos has tests against the devel branch, it will be eventually fine. * Apply suggestions from code review * Update testament/important_packages.nim --- testament/important_packages.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 75283edbc..d0c87919b 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -52,7 +52,7 @@ pkg "cascade" pkg "cello", url = "https://github.com/nim-lang/cello", useHead = true pkg "chroma" pkg "chronicles", "nimble install -y stew@#head; nim c -o:chr -r chronicles.nim" -pkg "chronos", "nimble install -y bearssl@#head stew@#head httputils@#head; nim c -r -d:release tests/testall" +pkg "chronos", "nim c -r -d:release tests/testall", url = "https://github.com/nim-lang/nim-chronos" pkg "cligen", "nim c --path:. -r cligen.nim" pkg "combparser", "nimble test --gc:orc" pkg "compactdict" From fd7da7da629f9f569503220b656047fb2f8da125 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 12 Dec 2022 21:57:27 +0800 Subject: [PATCH 3566/3768] add a changelog for nimble (#21078) --- changelog.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/changelog.md b/changelog.md index 8efb7200c..5762d18ad 100644 --- a/changelog.md +++ b/changelog.md @@ -292,6 +292,4 @@ ## Tool changes -- Nim now supports Nimble version 0.14 which added support for lock-files. This is done by - a simple configuration change setting that you can do yourself too. In `$nim/config/nim.cfg` - replace `pkgs` by `pkgs2`. +- Nim now ships Nimble version 0.14 which added support for lock-files. Libraries are stored in `$nimbleDir/pkgs2` (it was `$nimbleDir/pkgs`). From 584cfa6af81303caeaffe4084472167ad47216fe Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 12 Dec 2022 22:59:09 +0800 Subject: [PATCH 3567/3768] remove implicitDeref feature enum (#21075) remove implicitDeref feature --- compiler/options.nim | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/compiler/options.nim b/compiler/options.nim index a94405e05..7e4f6898f 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -196,7 +196,6 @@ type ideRecompile, ideChanged, ideType, ideDeclaration Feature* = enum ## experimental features; DO NOT RENAME THESE! - implicitDeref, # deadcode; remains here for backwards compatibility dotOperators, callOperator, parallel, @@ -457,7 +456,7 @@ when false: fn(globalOptions) fn(selectedGC) -const oldExperimentalFeatures* = {implicitDeref, dotOperators, callOperator, parallel} +const oldExperimentalFeatures* = {dotOperators, callOperator, parallel} const ChecksOptions* = {optObjCheck, optFieldCheck, optRangeCheck, From 438863601047cc974d831c6c0dd5533e4e3f0dc5 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 12 Dec 2022 23:14:44 +0800 Subject: [PATCH 3568/3768] fixes #21043; fixes a named exception in the infixAs expression which generate an implicit uninitialized let statement (#21081) * fixes #21043; fixes a named exception in the infixAs expression which generate an implicit uninitialized let statement * Update compiler/sempass2.nim Co-authored-by: Andreas Rumpf Co-authored-by: Andreas Rumpf --- compiler/sempass2.nim | 4 ++++ tests/init/tlet.nim | 23 ++++++++++++++++++++++- tests/iter/tyieldintry.nim | 1 + 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index 4520609e3..f13d8d8d4 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -491,6 +491,10 @@ proc trackTryStmt(tracked: PEffects, n: PNode) = let b = n[i] if b.kind == nkExceptBranch: setLen(tracked.init, oldState) + for j in 0.. Date: Tue, 13 Dec 2022 08:19:02 +1100 Subject: [PATCH 3569/3768] Nimsuggest now defines the backends symbol (#21083) * Add testcase * Define the backend symbol * Remove unneeded whitespace --- nimsuggest/nimsuggest.nim | 7 +++++-- nimsuggest/tests/t20440.nim | 7 +++++++ nimsuggest/tests/t20440.nims | 1 + 3 files changed, 13 insertions(+), 2 deletions(-) create mode 100644 nimsuggest/tests/t20440.nim create mode 100644 nimsuggest/tests/t20440.nims diff --git a/nimsuggest/nimsuggest.nim b/nimsuggest/nimsuggest.nim index c4798e88c..c5e015ce9 100644 --- a/nimsuggest/nimsuggest.nim +++ b/nimsuggest/nimsuggest.nim @@ -27,7 +27,7 @@ import compiler / [options, commands, modules, sem, passes, passaux, msgs, sigmatch, ast, idents, modulegraphs, prefixmatches, lineinfos, cmdlinehelper, - pathutils] + pathutils, condsyms] when defined(nimPreviewSlimSystem): import std/typedthreads @@ -553,6 +553,7 @@ proc mainCommand(graph: ModuleGraph) = registerPass graph, verbosePass registerPass graph, semPass conf.setCmd cmdIdeTools + defineSymbol(conf.symbols, $conf.backend) wantMainModule(conf) if not fileExists(conf.projectFull): @@ -952,10 +953,12 @@ else: proc mockCommand(graph: ModuleGraph) = retval = graph let conf = graph.config + conf.setCmd cmdIdeTools + defineSymbol(conf.symbols, $conf.backend) clearPasses(graph) registerPass graph, verbosePass registerPass graph, semPass - conf.setCmd cmdIdeTools + wantMainModule(conf) if not fileExists(conf.projectFull): diff --git a/nimsuggest/tests/t20440.nim b/nimsuggest/tests/t20440.nim new file mode 100644 index 000000000..0456aa074 --- /dev/null +++ b/nimsuggest/tests/t20440.nim @@ -0,0 +1,7 @@ +when not defined(js): + {.fatal: "Crash".} +echo 4 + +discard """ +$nimsuggest --v3 --tester $file +""" diff --git a/nimsuggest/tests/t20440.nims b/nimsuggest/tests/t20440.nims new file mode 100644 index 000000000..1336be3d4 --- /dev/null +++ b/nimsuggest/tests/t20440.nims @@ -0,0 +1 @@ +switch("backend", "js") From d84f64d2e7a28d69c9a5b22b8434d1fb2f00ebf2 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Tue, 13 Dec 2022 09:52:35 +0800 Subject: [PATCH 3570/3768] add CI for version-2-0 branch (#21084) --- .github/workflows/ci_packages.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/ci_packages.yml b/.github/workflows/ci_packages.yml index 93f5bfe19..0bec4cc21 100644 --- a/.github/workflows/ci_packages.yml +++ b/.github/workflows/ci_packages.yml @@ -4,6 +4,7 @@ on: push: branches: - 'devel' + - 'version-2-0' - 'version-1-6' - 'version-1-2' From 1fefb8e92a62c647028d0f00ebd9fc04da169789 Mon Sep 17 00:00:00 2001 From: Jake Leahy Date: Tue, 13 Dec 2022 15:21:51 +1100 Subject: [PATCH 3571/3768] Refactor JS sourcemap generator (#21053) * Parse the new line format * Fix pattern Didn't have space after the path * Remove duplicate sources Sources would sometimes double up with a new line at the end * Remove unused variable * Refactor sourcemap.nim Removes the multiple translations needed, now goes from single high level type to the final SourceMap Adds documentation for procs * Line numbers line up properly now Files aren't linking correctly though * Files now link up correctly Lines are sometimes off but overall seems pretty good Just need to implement parser * Add column info to output Add sourceMappingURL to rope directly to prevent copy * Properly handle columns * Remove debug lines * Add testcase * Finish testcase * Use the outdir folder instead of the folder the test is in to find the sourcemap Co-authored-by: ringabout <43030857+ringabout@users.noreply.github.com> --- compiler/jsgen.nim | 8 +- compiler/sourcemap.nim | 540 ++++++++++++++-------------------------- tests/js/tsourcemap.nim | 96 +++++++ 3 files changed, 283 insertions(+), 361 deletions(-) create mode 100644 tests/js/tsourcemap.nim diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index bcabc42ed..42a3dcf31 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -724,8 +724,9 @@ proc hasFrameInfo(p: PProc): bool = ((p.prc == nil) or not (sfPure in p.prc.flags)) proc lineDir(config: ConfigRef, info: TLineInfo, line: int): Rope = - ropes.`%`("/* line $2 \"$1\" */$n", - [rope(toFullPath(config, info)), rope(line)]) + "/* line $2:$3 \"$1\" */$n" % [ + rope(toFullPath(config, info)), rope(line), rope(info.toColumn) + ] proc genLineDir(p: PProc, n: PNode) = let line = toLinenumber(n.info) @@ -2885,7 +2886,8 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = # Generate an optional source map. if optSourcemap in m.config.globalOptions: var map: SourceMap - (code, map) = genSourceMap($(code), outFile.string) + map = genSourceMap($code, outFile.string) + code &= "\n//# sourceMappingURL=$#.map" % [outFile.string] writeFile(outFile.string & ".map", $(%map)) # Check if the generated JS code matches the output file, or else # write it to the file. diff --git a/compiler/sourcemap.nim b/compiler/sourcemap.nim index b87de75f3..2245250ac 100644 --- a/compiler/sourcemap.nim +++ b/compiler/sourcemap.nim @@ -1,383 +1,207 @@ -import os, strformat, strutils, tables, sets, ropes, json, algorithm +import std/[strutils, strscans, parseutils, assertions] type - SourceNode* = ref object - line*: int - column*: int - source*: string - name*: string - children*: seq[Child] + Segment = object + ## Segment refers to a block of something in the JS output. + ## This could be a token or an entire line + original: int # Column in the Nim source + generated: int # Column in the generated JS + name: int # Index into names list (-1 for no name) - C = enum cSourceNode, cSourceString + Mapping = object + ## Mapping refers to a line in the JS output. + ## It is made up of segments which refer to the tokens in the line + case inSource: bool # Whether the line in JS has Nim equivilant + of true: + file: int # Index into files list + line: int # 0 indexed line of code in the Nim source + segments: seq[Segment] + else: discard - Child* = ref object - case kind*: C: - of cSourceNode: - node*: SourceNode - of cSourceString: - s*: string + SourceInfo = object + mappings: seq[Mapping] + names, files: seq[string] - SourceMap* = ref object + SourceMap* = object version*: int sources*: seq[string] names*: seq[string] mappings*: string file*: string - # sourceRoot*: string - # sourcesContent*: string - SourceMapGenerator = ref object - file: string - sourceRoot: string - skipValidation: bool - sources: seq[string] - names: seq[string] - mappings: seq[Mapping] +func addSegment(info: var SourceInfo, original, generated: int, name: string = "") {.raises: [].} = + ## Adds a new segment into the current line + assert info.mappings.len > 0, "No lines have been added yet" + var segment = Segment(original: original, generated: generated, name: -1) + if name != "": + # Make name be index into names list + segment.name = info.names.find(name) + if segment.name == -1: + segment.name = info.names.len + info.names &= name - Mapping* = ref object - source*: string - original*: tuple[line: int, column: int] - generated*: tuple[line: int, column: int] - name*: string - noSource*: bool - noName*: bool + assert info.mappings[^1].inSource, "Current line isn't in Nim source" + info.mappings[^1].segments &= segment +func newLine(info: var SourceInfo) {.raises: [].} = + ## Add new mapping which doesn't appear in the Nim source + info.mappings &= Mapping(inSource: false) -proc child*(s: string): Child = - Child(kind: cSourceString, s: s) - - -proc child*(node: SourceNode): Child = - Child(kind: cSourceNode, node: node) - - -proc newSourceNode(line: int, column: int, path: string, node: SourceNode, name: string = ""): SourceNode = - SourceNode(line: line, column: column, source: path, name: name, children: @[child(node)]) - - -proc newSourceNode(line: int, column: int, path: string, s: string, name: string = ""): SourceNode = - SourceNode(line: line, column: column, source: path, name: name, children: @[child(s)]) - - -proc newSourceNode(line: int, column: int, path: string, children: seq[Child], name: string = ""): SourceNode = - SourceNode(line: line, column: column, source: path, name: name, children: children) - - - - -# debugging - - -proc text*(sourceNode: SourceNode, depth: int): string = - let empty = " " - result = &"{repeat(empty, depth)}SourceNode({sourceNode.source}:{sourceNode.line}:{sourceNode.column}):\n" - for child in sourceNode.children: - if child.kind == cSourceString: - result.add(&"{repeat(empty, depth + 1)}{child.s}\n") - else: - result.add(child.node.text(depth + 1)) - - -proc `$`*(sourceNode: SourceNode): string = text(sourceNode, 0) +func newLine(info: var SourceInfo, file: string, line: int) {.raises: [].} = + ## Starts a new line in the mappings. Call addSegment after this to add + ## segments into the line + var mapping = Mapping(inSource: true, line: line) + # Set file to file position. Add in if needed + mapping.file = info.files.find(file) + if mapping.file == -1: + mapping.file = info.files.len + info.files &= file + info.mappings &= mapping # base64_VLQ +func encode*(values: seq[int]): string {.raises: [].} = + ## Encodes a series of integers into a VLQ base64 encoded string + # References: + # - https://www.lucidchart.com/techblog/2019/08/22/decode-encoding-base64-vlqs-source-maps/ + # - https://github.com/rails/sprockets/blob/main/guides/source_maps.md#source-map-file + const + alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" + shift = 5 + continueBit = 1 shl 5 + mask = continueBit - 1 + for val in values: + # Sign is stored in first bit + var newVal = abs(val) shl 1 + if val < 0: + newVal = newVal or 1 + # Now comes the variable length part + # This is how we are able to store large numbers + while true: + # We only encode 5 bits. + var masked = newVal and mask + newVal = newVal shr shift + # If there is still something left + # then signify with the continue bit that the + # decoder should keep decoding + if newVal > 0: + masked = masked or continueBit + result &= alphabet[masked] + # If the value is zero then we have nothing left to encode + if newVal == 0: + break +iterator tokenize*(line: string): (int, string) = + ## Goes through a line and splits it into Nim identifiers and + ## normal JS code. This allows us to map mangled names back to Nim names. + ## Yields (column, name). Doesn't yield anything but identifiers. + ## See mangleName in compiler/jsgen.nim for how name mangling is done + var + col = 0 + token = "" + while col < line.len: + var + token: string + name: string + # First we find the next identifier + col += line.skipWhitespace(col) + col += line.skipUntil(IdentStartChars, col) + let identStart = col + col += line.parseIdent(token, col) + # Idents will either be originalName_randomInt or HEXhexCode_randomInt + if token.startsWith("HEX"): + var hex: int + # 3 = "HEX".len and we only want to parse the two integers after it + discard token[3 ..< 5].parseHex(hex) + name = $chr(hex) + elif not token.endsWith("_Idx"): # Ignore address indexes + # It might be in the form originalName_randomInt + let lastUnderscore = token.rfind('_') + if lastUnderscore != -1: + name = token[0.. 0: - var e = n and 31 - n = n shr 5 - if n > 0: - e = e or 32 - - result.add(integers[e]) - z += 1 - - -type TokenState = enum Normal, String, Ident, Mangled - -iterator tokenize*(line: string): (bool, string) = - # result = @[] - var state = Normal - var token = "" - var isMangled = false - for z, ch in line: - if ch.isAlphaAscii: - if state == Normal: - state = Ident - if token.len > 0: - yield (isMangled, token) - token = $ch - isMangled = false - else: - token.add(ch) - elif ch == '_': - if state == Ident: - state = Mangled - isMangled = true - token.add($ch) - elif ch != '"' and not ch.isAlphaNumeric: - if state in {Ident, Mangled}: - state = Normal - if token.len > 0: - yield (isMangled, token) - token = $ch - isMangled = false - else: - token.add($ch) - elif ch == '"': - if state != String: - state = String - if token.len > 0: - yield (isMangled, token) - token = $ch - isMangled = false - else: - state = Normal - token.add($ch) - if token.len > 0: - yield (isMangled, token) - isMangled = false - token = "" +func parse*(source: string): SourceInfo = + ## Parses the JS output for embedded line info + ## So it can convert those into a series of mappings + var + skipFirstLine = true + currColumn = 0 + currLine = 0 + currFile = "" + # Add each line as a node into the output + for line in source.splitLines(): + var + lineNumber: int + linePath: string + column: int + if line.strip().scanf("/* line $i:$i \"$+\" */", lineNumber, column, linePath): + # When we reach the first line mappinsegmentg then we can assume + # we can map the rest of the JS lines to Nim lines + currColumn = column # Column is already zero indexed + currLine = lineNumber - 1 + currFile = linePath + # Lines are zero indexed + result.newLine(currFile, currLine) + # Skip whitespace to find the starting column + result.addSegment(currColumn, line.skipWhitespace()) + elif currFile != "": + result.newLine(currFile, currLine) + # There mightn't be any tokens so add a starting segment + result.addSegment(currColumn, line.skipWhitespace()) + for jsColumn, token in line.tokenize: + result.addSegment(currColumn, jsColumn, token) else: - token.add($ch) - if token.len > 0: - yield (isMangled, token) + result.newLine() -proc parse*(source: string, path: string): SourceNode = - let lines = source.splitLines() - var lastLocation: SourceNode = nil - result = newSourceNode(0, 0, path, @[]) - - # we just use one single parent and add all nim lines - # as its children, I guess in typical codegen - # that happens recursively on ast level - # we also don't have column info, but I doubt more one nim lines can compile to one js - # maybe in macros? +func toSourceMap*(info: SourceInfo, file: string): SourceMap {.raises: [].} = + ## Convert from high level SourceInfo into the required SourceMap object + # Add basic info + result.version = 3 + result.file = file + result.sources = info.files + result.names = info.names + # Convert nodes into mappings. + # Mappings are split into blocks where each block referes to a line in the outputted JS. + # Blocks can be seperated into statements which refere to tokens on the line. + # Since the mappings depend on previous values we need to + # keep track of previous file, name, etc + var + prevFile = 0 + prevLine = 0 + prevName = 0 + prevNimCol = 0 - for i, originalLine in lines: - let line = originalLine.strip - if line.len == 0: - continue - - # this shouldn't be a problem: - # jsgen doesn't generate comments - # and if you emit // line you probably know what you're doing - if line.startsWith("// line"): - if result.children.len > 0: - result.children[^1].node.children.add(child(line & "\n")) - let pos = line.find(" ", 8) - let lineNumber = line[8 .. pos - 1].parseInt - let linePath = line[pos + 2 .. ^2] # quotes - - lastLocation = newSourceNode( - lineNumber, - 0, - linePath, - @[]) - result.children.add(child(lastLocation)) - else: - var last: SourceNode - for token in line.tokenize(): - var name = "" - if token[0]: - name = token[1].split('_', 1)[0] - - - if result.children.len > 0: - result.children[^1].node.children.add( - child( - newSourceNode( - result.children[^1].node.line, - 0, - result.children[^1].node.source, - token[1], - name))) - last = result.children[^1].node.children[^1].node - else: - result.children.add( - child( - newSourceNode(i + 1, 0, path, token[1], name))) - last = result.children[^1].node - let nl = "\n" - if not last.isNil: - last.source.add(nl) + for mapping in info.mappings: + # We know need to encode segments with the following fields + # All these fields are relative to their previous values + # - 0: Column in generated code + # - 1: Index of Nim file in source list + # - 2: Line in Nim source + # - 3: Column in Nim source + # - 4: Index in names list + if mapping.inSource: + # JS Column is special in that it is reset after every line + var prevJSCol = 0 + for segment in mapping.segments: + var values = @[segment.generated - prevJSCol, mapping.file - prevFile, mapping.line - prevLine, segment.original - prevNimCol] + # Add name field if needed + if segment.name != -1: + values &= segment.name - prevName + prevName = segment.name + prevJSCol = segment.generated + prevNimCol = segment.original + prevFile = mapping.file + prevLine = mapping.line + result.mappings &= encode(values) & "," + # Remove trailing , + if mapping.segments.len > 0: + result.mappings.setLen(result.mappings.len - 1) -proc cmp(a: Mapping, b: Mapping): int = - var c = cmp(a.generated, b.generated) - if c != 0: - return c + result.mappings &= ";" - c = cmp(a.source, b.source) - if c != 0: - return c - - c = cmp(a.original, b.original) - if c != 0: - return c - - return cmp(a.name, b.name) - - -proc index*[T](elements: seq[T], element: T): int = - for z in 0 ..< elements.len: - if elements[z] == element: - return z - return -1 - - -proc serializeMappings(map: SourceMapGenerator, mappings: seq[Mapping]): string = - var previous = Mapping(generated: (line: 1, column: 0), original: (line: 0, column: 0), name: "", source: "") - var previousSourceId = 0 - var previousNameId = 0 - var next = "" - var nameId = 0 - var sourceId = 0 - result = "" - - for z, mapping in mappings: - next = "" - - if mapping.generated.line != previous.generated.line: - previous.generated.column = 0 - - while mapping.generated.line != previous.generated.line: - next.add(";") - previous.generated.line += 1 - - else: - if z > 0: - if cmp(mapping, mappings[z - 1]) == 0: - continue - next.add(",") - - next.add(encode(mapping.generated.column - previous.generated.column)) - previous.generated.column = mapping.generated.column - - if not mapping.noSource and mapping.source.len > 0: - sourceId = map.sources.index(mapping.source) - next.add(encode(sourceId - previousSourceId)) - previousSourceId = sourceId - next.add(encode(mapping.original.line - 1 - previous.original.line)) - previous.original.line = mapping.original.line - 1 - next.add(encode(mapping.original.column - previous.original.column)) - previous.original.column = mapping.original.column - - if not mapping.noName and mapping.name.len > 0: - nameId = map.names.index(mapping.name) - next.add(encode(nameId - previousNameId)) - previousNameId = nameId - - result.add(next) - - -proc gen*(map: SourceMapGenerator): SourceMap = - var mappings = map.mappings.sorted do (a: Mapping, b: Mapping) -> int: - cmp(a, b) - result = SourceMap( - file: map.file, - version: 3, - sources: map.sources[0..^1], - names: map.names[0..^1], - mappings: map.serializeMappings(mappings)) - - - -proc addMapping*(map: SourceMapGenerator, mapping: Mapping) = - if not mapping.noSource and mapping.source notin map.sources: - map.sources.add(mapping.source) - - if not mapping.noName and mapping.name.len > 0 and mapping.name notin map.names: - map.names.add(mapping.name) - - # echo "map ", mapping.source, " ", mapping.original, " ", mapping.generated, " ", mapping.name - map.mappings.add(mapping) - - -proc walk*(node: SourceNode, fn: proc(line: string, original: SourceNode)) = - for child in node.children: - if child.kind == cSourceString and child.s.len > 0: - fn(child.s, node) - else: - child.node.walk(fn) - - -proc toSourceMap*(node: SourceNode, file: string): SourceMapGenerator = - var map = SourceMapGenerator(file: file, sources: @[], names: @[], mappings: @[]) - - var generated = (line: 1, column: 0) - var sourceMappingActive = false - var lastOriginal = SourceNode(source: "", line: -1, column: 0, name: "", children: @[]) - - node.walk do (line: string, original: SourceNode): - if original.source.endsWith(".js"): - # ignore it - discard - else: - if original.line != -1: - if lastOriginal.source != original.source or - lastOriginal.line != original.line or - lastOriginal.column != original.column or - lastOriginal.name != original.name: - map.addMapping( - Mapping( - source: original.source, - original: (line: original.line, column: original.column), - generated: (line: generated.line, column: generated.column), - name: original.name)) - - lastOriginal = SourceNode( - source: original.source, - line: original.line, - column: original.column, - name: original.name, - children: lastOriginal.children) - sourceMappingActive = true - elif sourceMappingActive: - map.addMapping( - Mapping( - noSource: true, - noName: true, - generated: (line: generated.line, column: generated.column), - original: (line: -1, column: -1))) - lastOriginal.line = -1 - sourceMappingActive = false - - for z in 0 ..< line.len: - if line[z] in Newlines: - generated.line += 1 - generated.column = 0 - - if z == line.len - 1: - lastOriginal.line = -1 - sourceMappingActive = false - elif sourceMappingActive: - map.addMapping( - Mapping( - source: original.source, - original: (line: original.line, column: original.column), - generated: (line: generated.line, column: generated.column), - name: original.name)) - else: - generated.column += 1 - - map - - -proc genSourceMap*(source: string, outFile: string): (Rope, SourceMap) = - let node = parse(source, outFile) - let map = node.toSourceMap(file = outFile) - ((&"{source}\n//# sourceMappingURL={outFile}.map").rope, map.gen) +proc genSourceMap*(source: string, outFile: string): SourceMap = + let node = parse(source) + result = node.toSourceMap(outFile) diff --git a/tests/js/tsourcemap.nim b/tests/js/tsourcemap.nim new file mode 100644 index 000000000..ff6f6122f --- /dev/null +++ b/tests/js/tsourcemap.nim @@ -0,0 +1,96 @@ +discard """ + action: "run" + target: "js" + cmd: "nim js -r -d:nodejs $options --sourceMap:on $file" +""" +import std/[os, json, strutils, sequtils, algorithm, assertions, paths, compilesettings] + +# Implements a very basic sourcemap parser and then runs it on itself. +# Allows to check for basic problems such as bad counts and lines missing (e.g. issue #21052) + +type + SourceMap = object + version: int + sources: seq[string] + names: seq[string] + mappings: string + file: string + + Line = object + line, column: int + file: string + +const + flag = 1 shl 5 + signBit = 0b1 + fourBits = 0b1111 + fiveBits = 0b11111 + mask = (1 shl 5) - 1 + alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" + +var b64Table: seq[int] = 0.repeat(max(alphabet.mapIt(it.ord)) + 1) +for i, b in alphabet.pairs: + b64Table[b.ord] = i + +# From https://github.com/juancarlospaco/nodejs/blob/main/src/nodejs/jsfs.nim +proc importFs*() {.importjs: "var fs = require(\"fs\");".} +proc readFileSync*(path: cstring): cstring {.importjs: "(fs.$1(#).toString())".} +importFS() +# Read in needed files +let + jsFileName = string(querySetting(outDir).Path / "tsourcemap.js".Path) + mapFileName = jsFileName & ".map" + + data = parseJson($mapFileName.cstring.readFileSync()).to(SourceMap) + jsFile = $readFileSync(jsFileName.cstring) + +proc decodeVLQ(inp: string): seq[int] = + var + shift, value: int + for v in inp.mapIt(b64Table[it.ord]): + value += (v and mask) shl shift + if (v and flag) > 0: + shift += 5 + continue + result &= (value shr 1) * (if (value and 1) > 0: -1 else: 1) + shift = 0 + value = 0 + + +# Keep track of state +var + line = 0 + source = 0 + name = 0 + column = 0 + jsLine = 1 + lines: seq[Line] + +for gline in data.mappings.split(';'): + jsLine += 1 + var jsColumn = 0 + for item in gline.strip().split(','): + let value = item.decodeVLQ() + doAssert value.len in [0, 1, 4, 5] + if value.len == 0: + continue + jsColumn += value[0] + if value.len >= 4: + source += value[1] + line += value[2] + column += value[3] + lines &= Line(line: line, column: column, file: data.sources[source]) + +let jsLines = jsFile.splitLines().len +# There needs to be a mapping for every line in the JS +# If there isn't then the JS lines wont match up with Nim lines. +# Except we don't care about the final line since that doesn't need to line up +doAssert data.mappings.count(';') == jsLines - 1 + +# Check we can find this file somewhere in the source map +var foundSelf = false +for line in lines: + if "tsourcemap.nim" in line.file: + foundSelf = true + doAssert line.line in 0.. Date: Tue, 13 Dec 2022 18:43:37 +1100 Subject: [PATCH 3572/3768] JS backend properly extends string with `setLen` (#21087) * Add test case * Extend string with '0' when setting length to be longer --- compiler/jsgen.nim | 4 +++- tests/js/t20235.nim | 11 +++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 tests/js/t20235.nim diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 42a3dcf31..d8fb6d57d 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -2209,7 +2209,9 @@ proc genMagic(p: PProc, n: PNode, r: var TCompRes) = if optOverflowCheck notin p.options: binaryExpr(p, n, r, "", "$1 -= $2") else: binaryExpr(p, n, r, "subInt", "$1 = subInt($3, $2)", true) of mSetLengthStr: - binaryExpr(p, n, r, "mnewString", "($1.length = $2)") + binaryExpr(p, n, r, "mnewString", + """if ($1.length < $2) { for (var i = $3.length; i < $4; ++i) $3.push(0); } + else {$3.length = $4; }""") of mSetLengthSeq: var x, y: TCompRes gen(p, n[1], x) diff --git a/tests/js/t20235.nim b/tests/js/t20235.nim new file mode 100644 index 000000000..3a69c2bd6 --- /dev/null +++ b/tests/js/t20235.nim @@ -0,0 +1,11 @@ +discard """ + action: "run" + output: "0 4" +""" + +proc main = + var s = "" + s.setLen(4) + echo s[0].ord, " ", s.len + +main() From e4aadcf1c1b39a5fef55ce9f164b3251c44ca000 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Tue, 13 Dec 2022 08:47:01 +0100 Subject: [PATCH 3573/3768] Document that system:pop() may raise IndexDefect (#21070) * Document system:pop() may raise IndexDefect * Add backticks to KeyError --- lib/pure/collections/sets.nim | 6 +++--- lib/system.nim | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/pure/collections/sets.nim b/lib/pure/collections/sets.nim index 09648ea65..11e324923 100644 --- a/lib/pure/collections/sets.nim +++ b/lib/pure/collections/sets.nim @@ -347,7 +347,7 @@ proc missingOrExcl*[A](s: var HashSet[A], key: A): bool = proc pop*[A](s: var HashSet[A]): A = ## Removes and returns an arbitrary element from the set `s`. ## - ## Raises KeyError if the set `s` is empty. + ## Raises `KeyError` if the set `s` is empty. ## ## See also: ## * `clear proc <#clear,HashSet[A]>`_ @@ -425,7 +425,7 @@ proc intersection*[A](s1, s2: HashSet[A]): HashSet[A] = assert c == toHashSet(["b"]) result = initHashSet[A](max(min(s1.data.len, s2.data.len), 2)) - + # iterate over the elements of the smaller set if s1.data.len < s2.data.len: for item in s1: @@ -433,7 +433,7 @@ proc intersection*[A](s1, s2: HashSet[A]): HashSet[A] = else: for item in s2: if item in s1: incl(result, item) - + proc difference*[A](s1, s2: HashSet[A]): HashSet[A] = ## Returns the difference of the sets `s1` and `s2`. diff --git a/lib/system.nim b/lib/system.nim index d0f78bfbd..65055f145 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1664,6 +1664,8 @@ proc contains*[T](a: openArray[T], item: T): bool {.inline.}= proc pop*[T](s: var seq[T]): T {.inline, noSideEffect.} = ## Returns the last item of `s` and decreases `s.len` by one. This treats ## `s` as a stack and implements the common *pop* operation. + ## + ## Raises `IndexDefect` if `s` is empty. runnableExamples: var a = @[1, 3, 5, 7] let b = pop(a) From 2564b5c938602e4f8820d2ed2b778c6d4f1d0cd5 Mon Sep 17 00:00:00 2001 From: Aditya Siram Date: Tue, 13 Dec 2022 08:57:07 -0600 Subject: [PATCH 3574/3768] Fix #20416. Enable the recursion limit for ref/ptr types. (#21092) --- compiler/semtypinst.nim | 18 ++++++++++++++---- tests/generics/tgenerics_issues.nim | 20 ++++++++++++++++++++ 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/compiler/semtypinst.nim b/compiler/semtypinst.nim index 945667a81..0a0ac1704 100644 --- a/compiler/semtypinst.nim +++ b/compiler/semtypinst.nim @@ -499,10 +499,20 @@ proc propagateFieldFlags(t: PType, n: PNode) = proc replaceTypeVarsTAux(cl: var TReplTypeVars, t: PType): PType = template bailout = - if t.sym != nil and sfGeneratedType in t.sym.flags: - # Only consider the recursion limit if the symbol is a type with generic - # parameters that have not been explicitly supplied, typechecking should - # terminate when generic parameters are explicitly supplied. + if (t.sym == nil) or (t.sym != nil and sfGeneratedType in t.sym.flags): + # In the first case 't.sym' can be 'nil' if the type is a ref/ptr, see + # issue https://github.com/nim-lang/Nim/issues/20416 for more details. + # Fortunately for us this works for now because partial ref/ptr types are + # not allowed in object construction, eg. + # type + # Container[T] = ... + # O = object + # val: ref Container + # + # In the second case only consider the recursion limit if the symbol is a + # type with generic parameters that have not been explicitly supplied, + # typechecking should terminate when generic parameters are explicitly + # supplied. if cl.recursionLimit > 100: # bail out, see bug #2509. But note this caching is in general wrong, # look at this example where TwoVectors should not share the generic diff --git a/tests/generics/tgenerics_issues.nim b/tests/generics/tgenerics_issues.nim index db7a16569..3068a22f2 100644 --- a/tests/generics/tgenerics_issues.nim +++ b/tests/generics/tgenerics_issues.nim @@ -872,3 +872,23 @@ block: # Ensure no segfault from constraint a = Regex[int]() b = Regex[bool]() c = MyOtherType[seq[int]]() + +block: # https://github.com/nim-lang/Nim/issues/20416 + type + Item[T] = object + link:ptr Item[T] + data:T + + KVSeq[A,B] = seq[(A,B)] + + MyTable[A,B] = object + data: KVSeq[A,B] + + Container[T] = object + a: MyTable[int,ref Item[T]] + + proc p1(sg:Container) = discard # Make sure that a non parameterized 'Container' argument still compiles + + proc p2[T](sg:Container[T]) = discard + var v : Container[int] + p2(v) From 9a50033d5b09ec3263a53bd2bb4182a8a50e6f4d Mon Sep 17 00:00:00 2001 From: metagn Date: Tue, 13 Dec 2022 23:20:55 +0300 Subject: [PATCH 3575/3768] generic `define` pragma + string alias (#20979) * generic `define` pragma + string alias * clean * add tests and document * remove char/float, minimize changelog --- changelog.md | 30 +++++++++- compiler/ast.nim | 2 +- compiler/condsyms.nim | 2 + compiler/pragmas.nim | 23 +++++--- compiler/semfold.nim | 109 ++++++++++++++++++++++++++++--------- doc/manual.md | 18 ++++++ doc/manual_experimental.md | 23 ++++++++ tests/misc/tdefine.nim | 64 ++++++++++++++++------ 8 files changed, 218 insertions(+), 53 deletions(-) diff --git a/changelog.md b/changelog.md index 5762d18ad..0181d03d2 100644 --- a/changelog.md +++ b/changelog.md @@ -221,9 +221,33 @@ - Full command syntax and block arguments i.e. `foo a, b: c` are now allowed for the right-hand side of type definitions in type sections. Previously they would error with "invalid indentation". -- `defined` now accepts identifiers separated by dots, i.e. `defined(a.b.c)`. - In the command line, this is defined as `-d:a.b.c`. Older versions can - use accents as in ``defined(`a.b.c`)`` to access such defines. + +- Compile-time define changes: + - `defined` now accepts identifiers separated by dots, i.e. `defined(a.b.c)`. + In the command line, this is defined as `-d:a.b.c`. Older versions can + use accents as in ``defined(`a.b.c`)`` to access such defines. + - [Define pragmas for constants](https://nim-lang.github.io/Nim/manual.html#implementation-specific-pragmas-compileminustime-define-pragmas) + now support a string argument for qualified define names. + + ```nim + # -d:package.FooBar=42 + const FooBar {.intdefine: "package.FooBar".}: int = 5 + echo FooBar # 42 + ``` + + This was added to help disambiguate similar define names for different packages. + In older versions, this could only be achieved with something like the following: + + ```nim + const FooBar = block: + const `package.FooBar` {.intdefine.}: int = 5 + `package.FooBar` + ``` + - A generic `define` pragma for constants has been added that interprets + the value of the define based on the type of the constant value. + See the [experimental manual](https://nim-lang.github.io/Nim/manual_experimental.html#generic-define-pragma) + for a list of supported types. + - [Macro pragmas](https://nim-lang.github.io/Nim/manual.html#userminusdefined-pragmas-macro-pragmas) changes: - Templates now accept macro pragmas. - Macro pragmas for var/let/const sections have been redesigned in a way that works diff --git a/compiler/ast.nim b/compiler/ast.nim index 198d21bea..c85a94860 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -712,7 +712,7 @@ type mEqIdent, mEqNimrodNode, mSameNodeType, mGetImpl, mNGenSym, mNHint, mNWarning, mNError, mInstantiationInfo, mGetTypeInfo, mGetTypeInfoV2, - mNimvm, mIntDefine, mStrDefine, mBoolDefine, mRunnableExamples, + mNimvm, mIntDefine, mStrDefine, mBoolDefine, mGenericDefine, mRunnableExamples, mException, mBuiltinType, mSymOwner, mUncheckedArray, mGetImplTransf, mSymIsInstantiationOf, mNodeId, mPrivateAccess, mZeroDefault diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 182e49bf8..41b4c83aa 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -149,3 +149,5 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasOutParams") defineSymbol("nimHasSystemRaisesDefect") defineSymbol("nimHasWarnUnnamedBreak") + defineSymbol("nimHasGenericDefine") + defineSymbol("nimHasDefineAliases") diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index b60331b29..993e6edce 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -81,7 +81,8 @@ const wGuard, wGoto, wCursor, wNoalias, wAlign} constPragmas* = declPragmas + {wHeader, wMagic, wGensym, wInject, - wIntDefine, wStrDefine, wBoolDefine, wCompilerProc, wCore} + wIntDefine, wStrDefine, wBoolDefine, wDefine, + wCompilerProc, wCore} paramPragmas* = {wNoalias, wInject, wGensym} letPragmas* = varPragmas procTypePragmas* = {FirstCallConv..LastCallConv, wVarargs, wNoSideEffect, @@ -476,8 +477,16 @@ proc processPop(c: PContext, n: PNode) = when defined(debugOptions): echo c.config $ n.info, " POP config is now ", c.config.options -proc processDefine(c: PContext, n: PNode) = - if (n.kind in nkPragmaCallKinds and n.len == 2) and (n[1].kind == nkIdent): +proc processDefineConst(c: PContext, n: PNode, sym: PSym, kind: TMagic) = + sym.magic = kind + if n.kind in nkPragmaCallKinds and n.len == 2: + # could also use TLib + n[1] = getStrLitNode(c, n) + +proc processDefine(c: PContext, n: PNode, sym: PSym) = + if sym != nil and sym.kind == skConst: + processDefineConst(c, n, sym, mGenericDefine) + elif (n.kind in nkPragmaCallKinds and n.len == 2) and (n[1].kind == nkIdent): defineSymbol(c.config.symbols, n[1].ident.s) else: invalidPragma(c, n) @@ -1066,7 +1075,7 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, recordPragma(c, it, "error", s) localError(c.config, it.info, errUser, s) of wFatal: fatal(c.config, it.info, expectStrLit(c, it)) - of wDefine: processDefine(c, it) + of wDefine: processDefine(c, it, sym) of wUndef: processUndef(c, it) of wCompile: processCompile(c, it) of wLink: processLink(c, it) @@ -1213,11 +1222,11 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, noVal(c, it) sym.flags.incl sfBase of wIntDefine: - sym.magic = mIntDefine + processDefineConst(c, n, sym, mIntDefine) of wStrDefine: - sym.magic = mStrDefine + processDefineConst(c, n, sym, mStrDefine) of wBoolDefine: - sym.magic = mBoolDefine + processDefineConst(c, n, sym, mBoolDefine) of wUsed: noVal(c, it) if sym == nil: invalidPragma(c, it) diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 9e45e3b2d..7c57eb370 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -13,7 +13,7 @@ import strutils, options, ast, trees, nimsets, platform, math, msgs, idents, renderer, types, - commands, magicsys, modulegraphs, strtabs, lineinfos + commands, magicsys, modulegraphs, strtabs, lineinfos, wordrecg from system/memory import nimCStrLen @@ -371,6 +371,11 @@ proc rangeCheck(n: PNode, value: Int128; g: ModuleGraph) = localError(g.config, n.info, "cannot convert " & $value & " to " & typeToString(n.typ)) +proc floatRangeCheck(n: PNode, value: BiggestFloat; g: ModuleGraph) = + if value < firstFloat(n.typ) or value > lastFloat(n.typ): + localError(g.config, n.info, "cannot convert " & $value & + " to " & typeToString(n.typ)) + proc foldConv(n, a: PNode; idgen: IdGenerator; g: ModuleGraph; check = false): PNode = let dstTyp = skipTypes(n.typ, abstractRange - {tyTypeDesc}) let srcTyp = skipTypes(a.typ, abstractRange - {tyTypeDesc}) @@ -490,6 +495,81 @@ proc newSymNodeTypeDesc*(s: PSym; idgen: IdGenerator; info: TLineInfo): PNode = else: result.typ = s.typ +proc foldDefine(m, s: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode = + var name = s.name.s + let prag = extractPragma(s) + if prag != nil: + for it in prag: + if it.kind in nkPragmaCallKinds and it.len == 2 and it[0].kind == nkIdent: + let word = whichKeyword(it[0].ident) + if word in {wStrDefine, wIntDefine, wBoolDefine, wDefine}: + # should be processed in pragmas.nim already + if it[1].kind in {nkStrLit, nkRStrLit, nkTripleStrLit}: + name = it[1].strVal + if isDefined(g.config, name): + let str = g.config.symbols[name] + case s.magic + of mIntDefine: + try: + result = newIntNodeT(toInt128(str.parseInt), n, idgen, g) + except ValueError: + localError(g.config, s.info, + "{.intdefine.} const was set to an invalid integer: '" & + str & "'") + of mStrDefine: + result = newStrNodeT(str, n, g) + of mBoolDefine: + try: + result = newIntNodeT(toInt128(str.parseBool.int), n, idgen, g) + except ValueError: + localError(g.config, s.info, + "{.booldefine.} const was set to an invalid bool: '" & + str & "'") + of mGenericDefine: + let rawTyp = s.typ + # pretend we don't support distinct types + let typ = rawTyp.skipTypes(abstractVarRange-{tyDistinct}) + try: + template intNode(value): PNode = + let val = toInt128(value) + rangeCheck(n, val, g) + newIntNodeT(val, n, idgen, g) + case typ.kind + of tyString, tyCstring: + result = newStrNodeT(str, n, g) + of tyInt..tyInt64: + result = intNode(str.parseBiggestInt) + of tyUInt..tyUInt64: + result = intNode(str.parseBiggestUInt) + of tyBool: + result = intNode(str.parseBool.int) + of tyEnum: + # compile time parseEnum + let ident = getIdent(g.cache, str) + for e in typ.n: + if e.kind != nkSym: internalError(g.config, "foldDefine for enum") + let es = e.sym + let match = + if es.ast.isNil: + es.name.id == ident.id + else: + es.ast.strVal == str + if match: + result = intNode(es.position) + break + if result.isNil: + raise newException(ValueError, "invalid enum value: " & str) + else: + localError(g.config, s.info, "unsupported type $1 for define '$2'" % + [name, typeToString(rawTyp)]) + except ValueError as e: + localError(g.config, s.info, + "could not process define '$1' of type $2; $3" % + [name, typeToString(rawTyp), e.msg]) + else: result = copyTree(s.astdef) # unreachable + else: + result = copyTree(s.astdef) + proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode = result = nil case n.kind @@ -509,31 +589,8 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode of mBuildOS: result = newStrNodeT(toLowerAscii(platform.OS[g.config.target.hostOS].name), n, g) of mBuildCPU: result = newStrNodeT(platform.CPU[g.config.target.hostCPU].name.toLowerAscii, n, g) of mAppType: result = getAppType(n, g) - of mIntDefine: - if isDefined(g.config, s.name.s): - try: - result = newIntNodeT(toInt128(g.config.symbols[s.name.s].parseInt), n, idgen, g) - except ValueError: - localError(g.config, s.info, - "{.intdefine.} const was set to an invalid integer: '" & - g.config.symbols[s.name.s] & "'") - else: - result = copyTree(s.astdef) - of mStrDefine: - if isDefined(g.config, s.name.s): - result = newStrNodeT(g.config.symbols[s.name.s], n, g) - else: - result = copyTree(s.astdef) - of mBoolDefine: - if isDefined(g.config, s.name.s): - try: - result = newIntNodeT(toInt128(g.config.symbols[s.name.s].parseBool.int), n, idgen, g) - except ValueError: - localError(g.config, s.info, - "{.booldefine.} const was set to an invalid bool: '" & - g.config.symbols[s.name.s] & "'") - else: - result = copyTree(s.astdef) + of mIntDefine, mStrDefine, mBoolDefine, mGenericDefine: + result = foldDefine(m, s, n, idgen, g) else: result = copyTree(s.astdef) of skProc, skFunc, skMethod: diff --git a/doc/manual.md b/doc/manual.md index bd2cf8568..0a295fdb5 100644 --- a/doc/manual.md +++ b/doc/manual.md @@ -8049,6 +8049,24 @@ used. To see if a value was provided, `defined(FooBar)` can be used. The syntax `-d:flag`:option: is actually just a shortcut for `-d:flag=true`:option:. +These pragmas also accept an optional string argument for qualified +define names. + + ```nim + const FooBar {.intdefine: "package.FooBar".}: int = 5 + echo FooBar + ``` + + ```cmd + nim c -d:package.FooBar=42 foobar.nim + ``` + +This helps disambiguate define names in different packages. + +See also the [generic `define` pragma](manual_experimental.html#generic-define-pragma) +for a version of these pragmas that detects the type of the define based on +the constant value. + User-defined pragmas ==================== diff --git a/doc/manual_experimental.md b/doc/manual_experimental.md index 6f5906019..2a56c1354 100644 --- a/doc/manual_experimental.md +++ b/doc/manual_experimental.md @@ -65,6 +65,29 @@ However, a `void` type cannot be inferred in generic code: The `void` type is only valid for parameters and return types; other symbols cannot have the type `void`. +Generic `define` pragma +======================= + +Aside the [typed define pragmas for constants](manual.html#implementation-specific-pragmas-compileminustime-define-pragmas), +there is a generic `{.define.}` pragma that interprets the value of the define +based on the type of the constant value. + + ```nim + const foo {.define: "package.foo".} = 123 + const bar {.define: "package.bar".} = false + ``` + + ```cmd + nim c -d:package.foo=456 -d:package.bar foobar.nim + ``` + +The following types are supported: + +* `string` and `cstring` +* Signed and unsigned integer types +* `bool` +* Enums + Top-down type inference ======================= diff --git a/tests/misc/tdefine.nim b/tests/misc/tdefine.nim index c4d11c941..f3fa4711f 100644 --- a/tests/misc/tdefine.nim +++ b/tests/misc/tdefine.nim @@ -1,12 +1,23 @@ discard """ joinable: false -cmd: "nim c -d:booldef -d:booldef2=false -d:intdef=2 -d:strdef=foobar -d:namespaced.define=false -d:double.namespaced.define -r $file" +cmd: "nim c $options -d:booldef -d:booldef2=false -d:intdef=2 -d:strdef=foobar -d:namespaced.define=false -d:double.namespaced.define -r $file" +matrix: "; -d:useGenericDefine" """ -const booldef {.booldefine.} = false -const booldef2 {.booldefine.} = true -const intdef {.intdefine.} = 0 -const strdef {.strdefine.} = "" +when defined(useGenericDefine): + {.pragma: booldefine2, define.} + {.pragma: intdefine2, define.} + {.pragma: strdefine2, define.} +else: + + {.pragma: booldefine2, booldefine.} + {.pragma: intdefine2, intdefine.} + {.pragma: strdefine2, strdefine.} + +const booldef {.booldefine2.} = false +const booldef2 {.booldefine2.} = true +const intdef {.intdefine2.} = 0 +const strdef {.strdefine2.} = "" doAssert defined(booldef) doAssert defined(booldef2) @@ -17,17 +28,28 @@ doAssert not booldef2 doAssert intdef == 2 doAssert strdef == "foobar" +when defined(useGenericDefine): + block: + const uintdef {.define: "intdef".}: uint = 17 + doAssert intdef == int(uintdef) + const cstrdef {.define: "strdef".}: cstring = "not strdef" + doAssert $cstrdef == strdef + type FooBar = enum foo, bar, foobar + const enumdef {.define: "strdef".} = foo + doAssert $enumdef == strdef + doAssert enumdef == foobar + # Intentionally not defined from command line -const booldef3 {.booldefine.} = true -const intdef2 {.intdefine.} = 1 -const strdef2 {.strdefine.} = "abc" +const booldef3 {.booldefine2.} = true +const intdef2 {.intdefine2.} = 1 +const strdef2 {.strdefine2.} = "abc" type T = object - when booldef3: - field1: int - when intdef2 == 1: - field2: int - when strdef2 == "abc": - field3: int + when booldef3: + field1: int + when intdef2 == 1: + field2: int + when strdef2 == "abc": + field3: int doAssert not defined(booldef3) doAssert not defined(intdef2) @@ -35,11 +57,21 @@ doAssert not defined(strdef2) discard T(field1: 1, field2: 2, field3: 3) doAssert defined(namespaced.define) -const `namespaced.define` {.booldefine.} = true +const `namespaced.define` {.booldefine2.} = true doAssert not `namespaced.define` +when defined(useGenericDefine): + const aliasToNamespacedDefine {.define: "namespaced.define".} = not `namespaced.define` +else: + const aliasToNamespacedDefine {.booldefine: "namespaced.define".} = not `namespaced.define` +doAssert aliasToNamespacedDefine == `namespaced.define` doAssert defined(double.namespaced.define) -const `double.namespaced.define` {.booldefine.} = false +const `double.namespaced.define` {.booldefine2.} = false doAssert `double.namespaced.define` +when defined(useGenericDefine): + const aliasToDoubleNamespacedDefine {.define: "double.namespaced.define".} = not `double.namespaced.define` +else: + const aliasToDoubleNamespacedDefine {.booldefine: "double.namespaced.define".} = not `double.namespaced.define` +doAssert aliasToDoubleNamespacedDefine == `double.namespaced.define` doAssert not defined(namespaced.butnotdefined) From 91ce8c385d4ccbaab8048cf0393b01cd72282272 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Thu, 15 Dec 2022 13:45:36 +0800 Subject: [PATCH 3576/3768] fix #19580; add warning for bare except: clause (#21099) * fix #19580; add warning for bare except: clause * fixes some easy ones * Update doc/manual.md * fixes docs * Update changelog.md * addition * Apply suggestions from code review Co-authored-by: Jacek Sieka * Update doc/tut2.md Co-authored-by: Jacek Sieka --- changelog.md | 2 ++ compiler/condsyms.nim | 1 + compiler/docgen.nim | 8 ++++---- compiler/extccomp.nim | 4 ++-- compiler/lineinfos.nim | 2 ++ compiler/nim.cfg | 4 ++++ compiler/semstmts.nim | 5 ++++- compiler/types.nim | 12 ++++++++++++ doc/manual.md | 20 +++++++++----------- doc/tut2.md | 4 ++-- lib/packages/docutils/rst.nim | 6 +++--- lib/pure/htmlparser.nim | 4 ++-- lib/std/assertions.nim | 2 ++ tests/nimdoc/trunnableexamples.nim | 2 +- 14 files changed, 50 insertions(+), 26 deletions(-) diff --git a/changelog.md b/changelog.md index 0181d03d2..00bdb50c2 100644 --- a/changelog.md +++ b/changelog.md @@ -133,6 +133,8 @@ `foo` had type `proc ()` were assumed by the compiler to mean `foo(a, b, proc () = ...)`. This behavior is now deprecated. Use `foo(a, b) do (): ...` or `foo(a, b, proc () = ...)` instead. +- If no exception or any exception deriving from Exception but not Defect or CatchableError given in except, a `warnBareExcept` warning will be triggered. + ## Standard library additions and changes [//]: # "Changes:" diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 41b4c83aa..9d081ef0a 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -151,3 +151,4 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasWarnUnnamedBreak") defineSymbol("nimHasGenericDefine") defineSymbol("nimHasDefineAliases") + defineSymbol("nimHasWarnBareExcept") diff --git a/compiler/docgen.nim b/compiler/docgen.nim index fc37bd596..f8a804cec 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -1746,7 +1746,7 @@ proc commandJson*(cache: IdentCache, conf: ConfigRef) = let filename = getOutFile(conf, RelativeFile conf.projectName, JsonExt) try: writeFile(filename, content) - except: + except IOError: rawMessage(conf, errCannotOpenFile, filename.string) proc commandTags*(cache: IdentCache, conf: ConfigRef) = @@ -1768,7 +1768,7 @@ proc commandTags*(cache: IdentCache, conf: ConfigRef) = let filename = getOutFile(conf, RelativeFile conf.projectName, TagsExt) try: writeFile(filename, content) - except: + except IOError: rawMessage(conf, errCannotOpenFile, filename.string) proc commandBuildIndex*(conf: ConfigRef, dir: string, outFile = RelativeFile"") = @@ -1789,7 +1789,7 @@ proc commandBuildIndex*(conf: ConfigRef, dir: string, outFile = RelativeFile"") try: writeFile(filename, code) - except: + except IOError: rawMessage(conf, errCannotOpenFile, filename.string) proc commandBuildIndexJson*(conf: ConfigRef, dir: string, outFile = RelativeFile"") = @@ -1803,5 +1803,5 @@ proc commandBuildIndexJson*(conf: ConfigRef, dir: string, outFile = RelativeFile try: writeFile(filename, $body) - except: + except IOError: rawMessage(conf, errCannotOpenFile, filename.string) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 256e02b22..66de46556 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -528,7 +528,7 @@ proc ccHasSaneOverflow*(conf: ConfigRef): bool = var exe = getConfigVar(conf, conf.cCompiler, ".exe") if exe.len == 0: exe = CC[conf.cCompiler].compilerExe # NOTE: should we need the full version, use -dumpfullversion - let (s, exitCode) = try: execCmdEx(exe & " -dumpversion") except: ("", 1) + let (s, exitCode) = try: execCmdEx(exe & " -dumpversion") except IOError, OSError, ValueError: ("", 1) if exitCode == 0: var major: int discard parseInt(s, major) @@ -1018,7 +1018,7 @@ proc changeDetectedViaJsonBuildInstructions*(conf: ConfigRef; jsonFile: Absolute proc runJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile) = var bcache: BuildCache try: bcache.fromJson(jsonFile.string.parseFile) - except: + except ValueError, KeyError, JsonKindError: let e = getCurrentException() conf.quitOrRaise "\ncaught exception:\n$#\nstacktrace:\n$#error evaluating JSON file: $#" % [e.msg, e.getStackTrace(), jsonFile.string] diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index ab9c902c3..cb60d8949 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -86,6 +86,7 @@ type warnImplicitTemplateRedefinition = "ImplicitTemplateRedefinition", warnUnnamedBreak = "UnnamedBreak", warnStmtListLambda = "StmtListLambda", + warnBareExcept = "BareExcept", warnUser = "User", # hints hintSuccess = "Success", hintSuccessX = "SuccessX", @@ -185,6 +186,7 @@ const warnImplicitTemplateRedefinition: "template '$1' is implicitly redefined; this is deprecated, add an explicit .redefine pragma", warnUnnamedBreak: "Using an unnamed break in a block is deprecated; Use a named block with a named break instead", warnStmtListLambda: "statement list expression assumed to be anonymous proc; this is deprecated, use `do (): ...` or `proc () = ...` instead", + warnBareExcept: "$1", warnUser: "$1", hintSuccess: "operation successful: $#", # keep in sync with `testament.isSuccess` diff --git a/compiler/nim.cfg b/compiler/nim.cfg index 20ce3810f..96b47b0e6 100644 --- a/compiler/nim.cfg +++ b/compiler/nim.cfg @@ -38,3 +38,7 @@ define:useStdoutAsStdmsg @if nimHasWarnUnnamedBreak: warningAserror[UnnamedBreak]:on @end + +@if nimHasWarnBareExcept: + warningAserror[BareExcept]:on +@end diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index d19d75611..a7a02d7fa 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -210,6 +210,8 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil) isImported = true elif not isException(typ): localError(c.config, typeNode.info, errExprCannotBeRaised) + elif not isDefectOrCatchableError(typ): + message(c.config, a.info, warnBareExcept, "catch a more precise Exception deriving from CatchableError or Defect.") if containsOrIncl(check, typ.id): localError(c.config, typeNode.info, errExceptionAlreadyHandled) @@ -251,7 +253,8 @@ proc semTry(c: PContext, n: PNode; flags: TExprFlags; expectedType: PType = nil) elif a.len == 1: # count number of ``except: body`` blocks inc catchAllExcepts - + message(c.config, a.info, warnBareExcept, + "The bare except clause is deprecated; use `except CatchableError:` instead") else: # support ``except KeyError, ValueError, ... : body`` if catchAllExcepts > 0: diff --git a/compiler/types.nim b/compiler/types.nim index 4bbbaf13c..457568e32 100644 --- a/compiler/types.nim +++ b/compiler/types.nim @@ -1721,6 +1721,18 @@ proc isDefectException*(t: PType): bool = t = skipTypes(t[0], abstractPtrs) return false +proc isDefectOrCatchableError*(t: PType): bool = + var t = t.skipTypes(abstractPtrs) + while t.kind == tyObject: + if t.sym != nil and t.sym.owner != nil and + sfSystemModule in t.sym.owner.flags and + (t.sym.name.s == "Defect" or + t.sym.name.s == "CatchableError"): + return true + if t[0] == nil: break + t = skipTypes(t[0], abstractPtrs) + return false + proc isSinkTypeForParam*(t: PType): bool = # a parameter like 'seq[owned T]' must not be used only once, but its # elements must, so we detect this case here: diff --git a/doc/manual.md b/doc/manual.md index 0a295fdb5..779e99186 100644 --- a/doc/manual.md +++ b/doc/manual.md @@ -4773,8 +4773,8 @@ Example: echo "overflow!" except ValueError, IOError: echo "catch multiple exceptions!" - except: - echo "Unknown exception!" + except CatchableError: + echo "Catchable exception!" finally: close(f) ``` @@ -4786,9 +4786,6 @@ listed in an `except` clause, the corresponding statements are executed. The statements following the `except` clauses are called `exception handlers`:idx:. -The empty `except`:idx: clause is executed if there is an exception that is -not listed otherwise. It is similar to an `else` clause in `if` statements. - If there is a `finally`:idx: clause, it is always executed after the exception handlers. @@ -4806,11 +4803,11 @@ Try can also be used as an expression; the type of the `try` branch then needs to fit the types of `except` branches, but the type of the `finally` branch always has to be `void`: - ```nim + ```nim test from std/strutils import parseInt let x = try: parseInt("133a") - except: -1 + except ValueError: -1 finally: echo "hi" ``` @@ -4818,8 +4815,9 @@ branch always has to be `void`: To prevent confusing code there is a parsing limitation; if the `try` follows a `(` it has to be written as a one liner: - ```nim - let x = (try: parseInt("133a") except: -1) + ```nim test + from std/strutils import parseInt + let x = (try: parseInt("133a") except ValueError: -1) ``` @@ -4867,7 +4865,7 @@ error message from `e`, and for such situations, it is enough to use ```nim try: # ... - except: + except CatchableError: echo getCurrentExceptionMsg() ``` @@ -5055,7 +5053,7 @@ An empty `raises` list (`raises: []`) means that no exception may be raised: try: unsafeCall() result = true - except: + except CatchableError: result = false ``` diff --git a/doc/tut2.md b/doc/tut2.md index 3c858c64e..4b9049082 100644 --- a/doc/tut2.md +++ b/doc/tut2.md @@ -393,7 +393,7 @@ The `try` statement handles exceptions: echo "could not convert string to integer" except IOError: echo "IO error!" - except: + except CatchableError: echo "Unknown exception!" # reraise the unknown exception: raise @@ -425,7 +425,7 @@ module. Example: ```nim try: doSomethingHere() - except: + except CatchableError: let e = getCurrentException() msg = getCurrentExceptionMsg() diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index f81be7a50..e9f76a26f 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -1610,7 +1610,7 @@ proc getFootnoteType(label: PRstNode): (FootnoteType, int) = elif label.len == 1 and label.sons[0].kind == rnLeaf: try: result = (fnManualNumber, parseInt(label.sons[0].text)) - except: + except ValueError: result = (fnCitation, -1) else: result = (fnCitation, -1) @@ -2899,13 +2899,13 @@ proc parseEnumList(p: var RstParser): PRstNode = let enumerator = p.tok[p.idx + 1 + wildIndex[w]].symbol # check that it's in sequence: enumerator == next(prevEnum) if "n" in wildcards[w]: # arabic numeral - let prevEnumI = try: parseInt(prevEnum) except: 1 + let prevEnumI = try: parseInt(prevEnum) except ValueError: 1 if enumerator in autoEnums: if prevAE != "" and enumerator != prevAE: break prevAE = enumerator curEnum = prevEnumI + 1 - else: curEnum = (try: parseInt(enumerator) except: 1) + else: curEnum = (try: parseInt(enumerator) except ValueError: 1) if curEnum - prevEnumI != 1: break prevEnum = enumerator diff --git a/lib/pure/htmlparser.nim b/lib/pure/htmlparser.nim index 6b1300f11..24eab3abb 100644 --- a/lib/pure/htmlparser.nim +++ b/lib/pure/htmlparser.nim @@ -394,10 +394,10 @@ proc entityToRune*(entity: string): Rune = case entity[1] of '0'..'9': try: runeValue = parseInt(entity[1..^1]) - except: discard + except ValueError: discard of 'x', 'X': # not case sensitive here try: runeValue = parseHexInt(entity[2..^1]) - except: discard + except ValueError: discard else: discard # other entities are not defined with prefix `#` if runeValue notin 0..0x10FFFF: runeValue = 0 # only return legal values return Rune(runeValue) diff --git a/lib/std/assertions.nim b/lib/std/assertions.nim index 03bab1b1b..5623ff8ef 100644 --- a/lib/std/assertions.nim +++ b/lib/std/assertions.nim @@ -98,6 +98,7 @@ template doAssertRaises*(exception: typedesc, code: untyped) = const begin = "expected raising '" & astToStr(exception) & "', instead" const msgEnd = " by: " & astToStr(code) template raisedForeign {.gensym.} = raiseAssert(begin & " raised foreign exception" & msgEnd) + {.warning[BareExcept]:off.} when Exception is exception: try: if true: @@ -116,5 +117,6 @@ template doAssertRaises*(exception: typedesc, code: untyped) = mixin `$` # alternatively, we could define $cstring in this module raiseAssert(begin & " raised '" & $e.name & "'" & msgEnd) except: raisedForeign() + {.warning[BareExcept]:on.} if wrong: raiseAssert(begin & " nothing was raised" & msgEnd) diff --git a/tests/nimdoc/trunnableexamples.nim b/tests/nimdoc/trunnableexamples.nim index ac7a0e26f..c88d8fa3f 100644 --- a/tests/nimdoc/trunnableexamples.nim +++ b/tests/nimdoc/trunnableexamples.nim @@ -65,7 +65,7 @@ when true: # issue #12746 runnableExamples: try: discard - except: + except CatchableError: # just the general except will work discard From d88f46df388604c1d21a2a95482c3255dc5884b6 Mon Sep 17 00:00:00 2001 From: Jake Leahy Date: Fri, 16 Dec 2022 02:05:34 +1100 Subject: [PATCH 3577/3768] Make async stacktraces less verbose (#21091) * Name iterators something human readable Remove intermediate async procs from stacktraces Clean async traceback message from reraises message * Remove unused import/variable * Fix failing tests Don't add {.stackTrace: off.} to anonymous procs (They already don't appear in stacktrace) * Fix failing tests in pragma category Now check that the nim is a routine type first so we don't run into any assertion defects * Hide stack trace pragma in docs and update doc tests User doesn't need to know if something won't appear so this more becomes verbose noise If this is a bad idea we can always add a `when defined(nimdoc)` switch so we don't add {.stackTrace: off.} to the Future[T] returning proc for docs --- compiler/renderer.nim | 4 +- lib/pure/asyncfutures.nim | 16 ++----- lib/pure/asyncmacro.nim | 16 ++++--- nimdoc/testproject/expected/testproject.html | 11 ++--- tests/async/tasync_gcunsafe.nim | 2 +- tests/async/tasync_traceback.nim | 47 ++++---------------- 6 files changed, 33 insertions(+), 63 deletions(-) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index ad74cf2fe..cd0d83eb0 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -619,7 +619,9 @@ proc isHideable(config: ConfigRef, n: PNode): bool = # xxx compare `ident` directly with `getIdent(cache, wRaises)`, but # this requires a `cache`. case n.kind - of nkExprColonExpr: result = n[0].kind == nkIdent and n[0].ident.s in ["raises", "tags", "extern", "deprecated", "forbids"] + of nkExprColonExpr: + result = n[0].kind == nkIdent and + n[0].ident.s.nimIdentNormalize in ["raises", "tags", "extern", "deprecated", "forbids", "stacktrace"] of nkIdent: result = n.ident.s in ["gcsafe", "deprecated"] else: result = false diff --git a/lib/pure/asyncfutures.nim b/lib/pure/asyncfutures.nim index 035b6182d..edf5f7277 100644 --- a/lib/pure/asyncfutures.nim +++ b/lib/pure/asyncfutures.nim @@ -329,29 +329,21 @@ proc `$`*(stackTraceEntries: seq[StackTraceEntry]): string = if leftLen > longestLeft: longestLeft = leftLen - var indent = 2 # Format the entries. for entry in entries: let (filename, procname) = getFilenameProcname(entry) - if procname == "": - if entry.line == reraisedFromBegin: - result.add(spaces(indent) & "#[\n") - indent.inc(2) - elif entry.line == reraisedFromEnd: - indent.dec(2) - result.add(spaces(indent) & "]#\n") - continue + if procname == "" and entry.line == reraisedFromBegin: + break let left = "$#($#)" % [filename, $entry.line] - result.add((spaces(indent) & "$#$# $#\n") % [ + result.add((spaces(2) & "$# $#\n") % [ left, - spaces(longestLeft - left.len + 2), procname ]) let hint = getHint(entry) if hint.len > 0: - result.add(spaces(indent+2) & "## " & hint & "\n") + result.add(spaces(4) & "## " & hint & "\n") proc injectStacktrace[T](future: Future[T]) = when not defined(release): diff --git a/lib/pure/asyncmacro.nim b/lib/pure/asyncmacro.nim index c4f6fd80e..d80a47101 100644 --- a/lib/pure/asyncmacro.nim +++ b/lib/pure/asyncmacro.nim @@ -20,9 +20,8 @@ proc newCallWithLineInfo(fromNode: NimNode; theProc: NimNode, args: varargs[NimN template createCb(retFutureSym, iteratorNameSym, strName, identName, futureVarCompletions: untyped) = bind finished - var nameIterVar = iteratorNameSym - proc identName {.closure.} = + proc identName {.closure, stackTrace: off.} = try: if not nameIterVar.finished: var next = nameIterVar() @@ -151,6 +150,10 @@ proc asyncSingleProc(prc: NimNode): NimNode = result[0][0] = quote do: Future[void] return result + if prc.kind in RoutineNodes and prc.name.kind != nnkEmpty: + # Only non anonymous functions need/can have stack trace disabled + prc.addPragma(nnkExprColonExpr.newTree(ident"stackTrace", ident"off")) + if prc.kind notin {nnkProcDef, nnkLambda, nnkMethodDef, nnkDo}: error("Cannot transform this node kind into an async proc." & " proc/method definition or lambda node expected.", prc) @@ -209,7 +212,7 @@ proc asyncSingleProc(prc: NimNode): NimNode = # -> {.pop.} # -> # -> complete(retFuture, result) - var iteratorNameSym = genSym(nskIterator, $prcName & "Iter") + var iteratorNameSym = genSym(nskIterator, $prcName & " (Async)") var procBody = prc.body.processBody(retFutureSym, futureVarIdents) # don't do anything with forward bodies (empty) if procBody.kind != nnkEmpty: @@ -251,9 +254,10 @@ proc asyncSingleProc(prc: NimNode): NimNode = # friendlier stack traces: var cbName = genSym(nskProc, prcName & NimAsyncContinueSuffix) var procCb = getAst createCb(retFutureSym, iteratorNameSym, - newStrLitNode(prcName), - cbName, - createFutureVarCompletions(futureVarIdents, nil)) + newStrLitNode(prcName), + cbName, + createFutureVarCompletions(futureVarIdents, nil) + ) outerProcBody.add procCb # -> return retFuture diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html index b68b721ff..7d10d7900 100644 --- a/nimdoc/testproject/expected/testproject.html +++ b/nimdoc/testproject/expected/testproject.html @@ -503,7 +503,8 @@
      -
      proc asyncFun1(): Future[int] {....raises: [Exception, ValueError],
      +  
      proc asyncFun1(): Future[int] {....stackTrace: false,
      +                                raises: [Exception, ValueError],
                                       tags: [RootEffect], forbids: [].}
      @@ -515,8 +516,8 @@
      -
      proc asyncFun2(): owned(Future[void]) {....raises: [Exception], tags: [RootEffect],
      -                                        forbids: [].}
      +
      proc asyncFun2(): owned(Future[void]) {....stackTrace: false, raises: [Exception],
      +                                        tags: [RootEffect], forbids: [].}
      @@ -527,8 +528,8 @@
      -
      proc asyncFun3(): owned(Future[void]) {....raises: [Exception], tags: [RootEffect],
      -                                        forbids: [].}
      +
      proc asyncFun3(): owned(Future[void]) {....stackTrace: false, raises: [Exception],
      +                                        tags: [RootEffect], forbids: [].}
      diff --git a/tests/async/tasync_gcunsafe.nim b/tests/async/tasync_gcunsafe.nim index 00c92b109..f3e6bc691 100644 --- a/tests/async/tasync_gcunsafe.nim +++ b/tests/async/tasync_gcunsafe.nim @@ -1,5 +1,5 @@ discard """ - errormsg: "'anotherGCSafeAsyncProcIter' is not GC-safe as it calls 'asyncGCUnsafeProc'" + errormsg: "'anotherGCSafeAsyncProc (Async)' is not GC-safe as it calls 'asyncGCUnsafeProc'" cmd: "nim c --threads:on $file" file: "asyncmacro.nim" """ diff --git a/tests/async/tasync_traceback.nim b/tests/async/tasync_traceback.nim index ec67d34f9..98f71b192 100644 --- a/tests/async/tasync_traceback.nim +++ b/tests/async/tasync_traceback.nim @@ -67,51 +67,22 @@ import re const expected = """ b failure Async traceback: - tasync_traceback\.nim\(\d+?\)\s+?tasync_traceback - asyncmacro\.nim\(\d+?\)\s+?a - asyncmacro\.nim\(\d+?\)\s+?aNimAsyncContinue - ## Resumes an async procedure - tasync_traceback\.nim\(\d+?\)\s+?aIter - asyncmacro\.nim\(\d+?\)\s+?b - asyncmacro\.nim\(\d+?\)\s+?bNimAsyncContinue - ## Resumes an async procedure - tasync_traceback\.nim\(\d+?\)\s+?bIter - #\[ - tasync_traceback\.nim\(\d+?\)\s+?tasync_traceback - asyncmacro\.nim\(\d+?\)\s+?a - asyncmacro\.nim\(\d+?\)\s+?aNimAsyncContinue - ## Resumes an async procedure - asyncmacro\.nim\(\d+?\)\s+?aIter - asyncfutures\.nim\(\d+?\)\s+?read - \]# + tasync_traceback\.nim\(\d+?\) tasync_traceback + tasync_traceback\.nim\(\d+?\) a \(Async\) + tasync_traceback\.nim\(\d+?\) b \(Async\) Exception message: b failure bar failure Async traceback: - tasync_traceback\.nim\(\d+?\)\s+?tasync_traceback - asyncdispatch\.nim\(\d+?\)\s+?waitFor - asyncdispatch\.nim\(\d+?\)\s+?poll + tasync_traceback\.nim\(\d+?\) tasync_traceback + asyncdispatch\.nim\(\d+?\) waitFor + asyncdispatch\.nim\(\d+?\) poll ## Processes asynchronous completion events - asyncdispatch\.nim\(\d+?\)\s+?runOnce - asyncdispatch\.nim\(\d+?\)\s+?processPendingCallbacks + asyncdispatch\.nim\(\d+?\) runOnce + asyncdispatch\.nim\(\d+?\) processPendingCallbacks ## Executes pending callbacks - asyncmacro\.nim\(\d+?\)\s+?barNimAsyncContinue - ## Resumes an async procedure - tasync_traceback\.nim\(\d+?\)\s+?barIter - #\[ - tasync_traceback\.nim\(\d+?\)\s+?tasync_traceback - asyncdispatch\.nim\(\d+?\)\s+?waitFor - asyncdispatch\.nim\(\d+?\)\s+?poll - ## Processes asynchronous completion events - asyncdispatch\.nim\(\d+?\)\s+?runOnce - asyncdispatch\.nim\(\d+?\)\s+?processPendingCallbacks - ## Executes pending callbacks - asyncmacro\.nim\(\d+?\)\s+?fooNimAsyncContinue - ## Resumes an async procedure - asyncmacro\.nim\(\d+?\)\s+?fooIter - asyncfutures\.nim\(\d+?\)\s+?read - \]# + tasync_traceback\.nim\(\d+?\) bar \(Async\) Exception message: bar failure """ From 8054be6e529db9e65a18e4c4a37ec54f64f9121c Mon Sep 17 00:00:00 2001 From: Bung Date: Thu, 15 Dec 2022 23:12:17 +0800 Subject: [PATCH 3578/3768] fix #20588 (#21104) --- compiler/semexprs.nim | 2 ++ tests/arc/t20588.nim | 22 ++++++++++++++++++++++ 2 files changed, 24 insertions(+) create mode 100644 tests/arc/t20588.nim diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 2efa1259a..8ac8f4750 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -319,6 +319,8 @@ proc semConv(c: PContext, n: PNode; expectedType: PType = nil): PNode = return evaluated else: targetType = targetType.base + of tyAnything, tyUntyped, tyTyped: + localError(c.config, n.info, "illegal type conversion to '$1'" % typeToString(targetType)) else: discard maybeLiftType(targetType, c, n[0].info) diff --git a/tests/arc/t20588.nim b/tests/arc/t20588.nim new file mode 100644 index 000000000..d747c656d --- /dev/null +++ b/tests/arc/t20588.nim @@ -0,0 +1,22 @@ +discard """ + cmd: "nim check --warnings:off --hints:off $file" + errormsg: "" + nimout: ''' +t20588.nim(20, 12) Error: illegal type conversion to 'auto' +t20588.nim(21, 14) Error: illegal type conversion to 'typed' +t20588.nim(22, 16) Error: illegal type conversion to 'untyped' +''' +""" + + + + + + + + + + +discard 0.0.auto +discard typed("abc") +discard untyped(4) From d00477dffb2c62732b87a440c61706de5b480f48 Mon Sep 17 00:00:00 2001 From: Jake Leahy Date: Fri, 16 Dec 2022 05:22:45 +1100 Subject: [PATCH 3579/3768] Check file exists in `{.compile.}` pragma (#21105) * Add test * Check file exists before adding it into compilation * Make error message look like other error messages i.e. following the format `error msg: file` --- compiler/pragmas.nim | 7 +++++-- tests/pragmas/tcompile_missing_file.nim | 5 +++++ 2 files changed, 10 insertions(+), 2 deletions(-) create mode 100644 tests/pragmas/tcompile_missing_file.nim diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 993e6edce..92e3d1922 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -513,8 +513,11 @@ proc processCompile(c: PContext, n: PNode) = var cf = Cfile(nimname: splitFile(src).name, cname: src, obj: dest, flags: {CfileFlag.External}, customArgs: customArgs) - extccomp.addExternalFileToCompile(c.config, cf) - recordPragma(c, it, "compile", src.string, dest.string, customArgs) + if not fileExists(src): + localError(c.config, n.info, "cannot find: " & src.string) + else: + extccomp.addExternalFileToCompile(c.config, cf) + recordPragma(c, it, "compile", src.string, dest.string, customArgs) proc getStrLit(c: PContext, n: PNode; i: int): string = n[i] = c.semConstExpr(c, n[i]) diff --git a/tests/pragmas/tcompile_missing_file.nim b/tests/pragmas/tcompile_missing_file.nim new file mode 100644 index 000000000..fd90bd57d --- /dev/null +++ b/tests/pragmas/tcompile_missing_file.nim @@ -0,0 +1,5 @@ +discard """ + joinable: false + errormsg: "cannot find: noexist.c" +""" +{.compile: "noexist.c".} From 78b68421da0a678deb0c17cd03345d62410dbeac Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 16 Dec 2022 11:36:52 +0800 Subject: [PATCH 3580/3768] fixes chronicles (#21114) --- testament/important_packages.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index d0c87919b..c134be6f9 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -51,7 +51,7 @@ pkg "c2nim", "nim c testsuite/tester.nim" pkg "cascade" pkg "cello", url = "https://github.com/nim-lang/cello", useHead = true pkg "chroma" -pkg "chronicles", "nimble install -y stew@#head; nim c -o:chr -r chronicles.nim" +pkg "chronicles", "nim c -o:chr -r chronicles.nim", url = "https://github.com/nim-lang/nim-chronicles" pkg "chronos", "nim c -r -d:release tests/testall", url = "https://github.com/nim-lang/nim-chronos" pkg "cligen", "nim c --path:. -r cligen.nim" pkg "combparser", "nimble test --gc:orc" From 644318ad9cbf079f33d8d7b6c5ead4a4429adb3e Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 16 Dec 2022 14:58:40 +0800 Subject: [PATCH 3581/3768] the devel branch now uses csources_v2 (#21108) * the devel branch now uses csources_v2 * fixes hash * bump csources_v2 --- .gitignore | 1 + config/build_config.txt | 6 +++--- nim.nimble | 2 +- readme.md | 4 ++-- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/.gitignore b/.gitignore index d228b498c..d0c4558df 100644 --- a/.gitignore +++ b/.gitignore @@ -67,6 +67,7 @@ testament.db /csources /csources_v1 +/csources_v2 /dist/ # /lib/fusion # fusion is now unbundled; `git status` should reveal if it's there so users can act on it diff --git a/config/build_config.txt b/config/build_config.txt index 15f44bb57..3ae618f00 100644 --- a/config/build_config.txt +++ b/config/build_config.txt @@ -1,5 +1,5 @@ nim_comment="key-value pairs for windows/posix bootstrapping build scripts" -nim_csourcesDir=csources_v1 -nim_csourcesUrl=https://github.com/nim-lang/csources_v1.git +nim_csourcesDir=csources_v2 +nim_csourcesUrl=https://github.com/nim-lang/csources_v2.git nim_csourcesBranch=master -nim_csourcesHash=561b417c65791cd8356b5f73620914ceff845d10 +nim_csourcesHash=ffd834c20915dc04c78eb0d3b2162832e7ec9c13 diff --git a/nim.nimble b/nim.nimble index a629c0e04..5d1be49b1 100644 --- a/nim.nimble +++ b/nim.nimble @@ -5,7 +5,7 @@ license = "MIT" bin = @["compiler/nim", "nimsuggest/nimsuggest"] skipFiles = @["azure-pipelines.yml" , "build_all.bat" , "build_all.sh" , "build_nimble.bat" , "build_nimble.sh" , "changelog.md" , "koch.nim.cfg" , "nimblemeta.json" , "readme.md" , "security.md" ] -skipDirs = @["build" , "changelogs" , "ci" , "csources_v1" , "drnim" , "nimdoc", "testament"] +skipDirs = @["build" , "changelogs" , "ci" , "csources_v2" , "drnim" , "nimdoc", "testament"] before install: when defined(windows): diff --git a/readme.md b/readme.md index 3d32bcf87..a0f47eb41 100644 --- a/readme.md +++ b/readme.md @@ -47,7 +47,7 @@ Compiling the Nim compiler is quite straightforward if you follow these steps: First, the C source of an older version of the Nim compiler is needed to bootstrap the latest version because the Nim compiler itself is written in the Nim programming language. Those C sources are available within the -[``nim-lang/csources_v1``][csources-v1-repo] repository. +[``nim-lang/csources_v2``][csources-v2-repo] repository. Next, to build from source you will need: @@ -223,7 +223,7 @@ Copyright © 2006-2022 Andreas Rumpf, all rights reserved. [nimble-repo]: https://github.com/nim-lang/nimble [nimsuggest-repo]: https://github.com/nim-lang/nimsuggest [csources-repo-deprecated]: https://github.com/nim-lang/csources -[csources-v1-repo]: https://github.com/nim-lang/csources_v1 +[csources-v2-repo]: https://github.com/nim-lang/csources_v2 [badge-nim-irc]: https://img.shields.io/badge/chat-on_irc-blue.svg?style=flat-square [badge-nim-discord]: https://img.shields.io/discord/371759389889003530?color=blue&label=discord&logo=discord&logoColor=gold&style=flat-square [badge-nim-gitter]: https://img.shields.io/badge/chat-on_gitter-blue.svg?style=flat-square From 8a3b76b28733a87e5cffa3f4b7b99d76f13ee2ba Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 16 Dec 2022 14:59:18 +0800 Subject: [PATCH 3582/3768] csource building prefers `bin/nim` (#21115) --- tools/niminst/niminst.nim | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/tools/niminst/niminst.nim b/tools/niminst/niminst.nim index cd2e5a481..bc763a56f 100644 --- a/tools/niminst/niminst.nim +++ b/tools/niminst/niminst.nim @@ -515,6 +515,17 @@ template gatherFiles(fun, libpath, outDir) = # commenting out for now, see discussion in https://github.com/nim-lang/Nim/pull/13413 # copySrc(libpath / "lib/wrappers/linenoise/linenoise.h") +proc exe(f: string): string = + result = addFileExt(f, ExeExt) + when defined(windows): + result = result.replace('/','\\') + +proc findNim(): string = + let nim = "nim".exe + result = quoteShell("bin" / nim) + if not fileExists(result): + result = "nim" + proc srcdist(c: var ConfigData) = let cCodeDir = getOutputDir(c) / "c_code" if not dirExists(cCodeDir): createDir(cCodeDir) @@ -533,10 +544,10 @@ proc srcdist(c: var ConfigData) = var dir = getOutputDir(c) / buildDir(osA, cpuA) if dirExists(dir): removeDir(dir) createDir(dir) - var cmd = ("nim compile -f --symbolfiles:off --compileonly " & + var cmd = ("$# compile -f --symbolfiles:off --compileonly " & "--gen_mapping --cc:gcc --skipUserCfg" & " --os:$# --cpu:$# $# $#") % - [osname, cpuname, c.nimArgs, c.mainfile] + [findNim(), osname, cpuname, c.nimArgs, c.mainfile] echo(cmd) if execShellCmd(cmd) != 0: quit("Error: call to nim compiler failed") From a9bd78d579168ba50f42d3bc418cd7de70a226f9 Mon Sep 17 00:00:00 2001 From: Bung Date: Fri, 16 Dec 2022 15:01:15 +0800 Subject: [PATCH 3583/3768] fix #12122 (#21096) --- compiler/astalgo.nim | 2 +- compiler/evalffi.nim | 8 ++-- lib/core/typeinfo.nim | 4 +- lib/pure/coro.nim | 4 +- lib/pure/memfiles.nim | 8 ++-- lib/pure/oids.nim | 4 +- lib/pure/strutils.nim | 4 +- lib/std/envvars.nim | 2 +- lib/std/private/strimpl.nim | 4 +- lib/std/syncio.nim | 2 +- lib/system/alloc.nim | 70 ++++++++++++++++----------------- lib/system/assign.nim | 28 ++++++------- lib/system/channels_builtin.nim | 12 +++--- lib/system/ctypes.nim | 2 +- lib/system/deepcopy.nim | 16 ++++---- lib/system/gc.nim | 24 +++++------ lib/system/gc2.nim | 22 +++++------ lib/system/gc_common.nim | 24 +++++------ lib/system/gc_ms.nim | 18 ++++----- lib/system/osalloc.nim | 6 +-- lib/system/repr.nim | 10 ++--- lib/system/sysstr.nim | 4 +- tests/parallel/tguard1.nim | 2 +- 23 files changed, 140 insertions(+), 140 deletions(-) diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index 28c38129e..e9ed1497a 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -333,7 +333,7 @@ proc typeToYamlAux(conf: ConfigRef; n: PType, marker: var IntSet, indent: int, sonsRope = rope("null") elif containsOrIncl(marker, n.id): sonsRope = "\"$1 @$2\"" % [rope($n.kind), rope( - strutils.toHex(cast[ByteAddress](n), sizeof(n) * 2))] + strutils.toHex(cast[int](n), sizeof(n) * 2))] else: if n.len > 0: sonsRope = rope("[") diff --git a/compiler/evalffi.nim b/compiler/evalffi.nim index 37edef86c..0577619b8 100644 --- a/compiler/evalffi.nim +++ b/compiler/evalffi.nim @@ -77,7 +77,7 @@ proc importcSymbol*(conf: ConfigRef, sym: PSym): PNode = theAddr = dllhandle.symAddr(name.cstring) if theAddr.isNil: globalError(conf, sym.info, "cannot import symbol: " & name & " from " & libPathMsg) - result.intVal = cast[ByteAddress](theAddr) + result.intVal = cast[int](theAddr) proc mapType(conf: ConfigRef, t: ast.PType): ptr libffi.Type = if t == nil: return addr libffi.type_void @@ -113,7 +113,7 @@ proc mapCallConv(conf: ConfigRef, cc: TCallingConvention, info: TLineInfo): TABI template rd(typ, p: untyped): untyped = (cast[ptr typ](p))[] template wr(typ, p, v: untyped): untyped = (cast[ptr typ](p))[] = v template `+!`(x, y: untyped): untyped = - cast[pointer](cast[ByteAddress](x) + y) + cast[pointer](cast[int](x) + y) proc packSize(conf: ConfigRef, v: PNode, typ: PType): int = ## computes the size of the blob @@ -369,13 +369,13 @@ proc unpack(conf: ConfigRef, x: pointer, typ: PType, n: PNode): PNode = # in their unboxed representation so nothing it to be unpacked: result = n else: - awi(nkPtrLit, cast[ByteAddress](p)) + awi(nkPtrLit, cast[int](p)) of tyPtr, tyRef, tyVar, tyLent: let p = rd(pointer, x) if p.isNil: setNil() elif n == nil or n.kind == nkPtrLit: - awi(nkPtrLit, cast[ByteAddress](p)) + awi(nkPtrLit, cast[int](p)) elif n != nil and n.len == 1: internalAssert(conf, n.kind == nkRefTy) n[0] = unpack(conf, p, typ.lastSon, n[0]) diff --git a/lib/core/typeinfo.nim b/lib/core/typeinfo.nim index 89b1deb31..5075c8458 100644 --- a/lib/core/typeinfo.nim +++ b/lib/core/typeinfo.nim @@ -132,12 +132,12 @@ else: proc prepareSeqAdd(len: int; p: pointer; addlen, elemSize, elemAlign: int): pointer {. importCompilerProc.} -template `+!!`(a, b): untyped = cast[pointer](cast[ByteAddress](a) + b) +template `+!!`(a, b): untyped = cast[pointer](cast[int](a) + b) proc getDiscriminant(aa: pointer, n: ptr TNimNode): int = assert(n.kind == nkCase) var d: int - let a = cast[ByteAddress](aa) + let a = cast[int](aa) case n.typ.size of 1: d = int(cast[ptr uint8](a +% n.offset)[]) of 2: d = int(cast[ptr uint16](a +% n.offset)[]) diff --git a/lib/pure/coro.nim b/lib/pure/coro.nim index 47dfdd791..3b9bba59b 100644 --- a/lib/pure/coro.nim +++ b/lib/pure/coro.nim @@ -281,8 +281,8 @@ proc start*(c: proc(), stacksize: int = defaultStackSize): CoroutineRef {.discar (proc(p: pointer) {.stdcall.} = runCurrentTask()), nil) else: coro = cast[CoroutinePtr](alloc0(sizeof(Coroutine) + stacksize)) - coro.stack.top = cast[pointer](cast[ByteAddress](coro) + sizeof(Coroutine)) - coro.stack.bottom = cast[pointer](cast[ByteAddress](coro.stack.top) + stacksize) + coro.stack.top = cast[pointer](cast[int](coro) + sizeof(Coroutine)) + coro.stack.bottom = cast[pointer](cast[int](coro.stack.top) + stacksize) when coroBackend == CORO_BACKEND_UCONTEXT: discard getcontext(coro.execContext) coro.execContext.uc_stack.ss_sp = coro.stack.top diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim index 48c07b149..9ff8bcd74 100644 --- a/lib/pure/memfiles.nim +++ b/lib/pure/memfiles.nim @@ -495,8 +495,8 @@ proc mmsSetPosition(s: Stream, pos: int) = proc mmsGetPosition(s: Stream): int = MemMapFileStream(s).pos proc mmsPeekData(s: Stream, buffer: pointer, bufLen: int): int = - let startAddress = cast[ByteAddress](MemMapFileStream(s).mf.mem) - let p = cast[ByteAddress](MemMapFileStream(s).pos) + let startAddress = cast[int](MemMapFileStream(s).mf.mem) + let p = cast[int](MemMapFileStream(s).pos) let l = min(bufLen, MemMapFileStream(s).mf.size - p) moveMem(buffer, cast[pointer](startAddress + p), l) result = l @@ -511,8 +511,8 @@ proc mmsWriteData(s: Stream, buffer: pointer, bufLen: int) = let size = MemMapFileStream(s).mf.size if MemMapFileStream(s).pos + bufLen > size: raise newEIO("cannot write to stream") - let p = cast[ByteAddress](MemMapFileStream(s).mf.mem) + - cast[ByteAddress](MemMapFileStream(s).pos) + let p = cast[int](MemMapFileStream(s).mf.mem) + + cast[int](MemMapFileStream(s).pos) moveMem(cast[pointer](p), buffer, bufLen) inc(MemMapFileStream(s).pos, bufLen) diff --git a/lib/pure/oids.nim b/lib/pure/oids.nim index e6e5e6e56..ad8eeefd7 100644 --- a/lib/pure/oids.nim +++ b/lib/pure/oids.nim @@ -44,7 +44,7 @@ proc hexbyte*(hex: char): int {.inline.} = proc parseOid*(str: cstring): Oid = ## Parses an OID. - var bytes = cast[cstring](cast[pointer](cast[ByteAddress](addr(result.time)) + 4)) + var bytes = cast[cstring](cast[pointer](cast[int](addr(result.time)) + 4)) var i = 0 while i < 12: bytes[i] = chr((hexbyte(str[2 * i]) shl 4) or hexbyte(str[2 * i + 1])) @@ -57,7 +57,7 @@ proc `$`*(oid: Oid): string = result.setLen 24 var o = oid - var bytes = cast[cstring](cast[pointer](cast[ByteAddress](addr(o)) + 4)) + var bytes = cast[cstring](cast[pointer](cast[int](addr(o)) + 4)) var i = 0 while i < 12: let b = bytes[i].ord diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index 0367ea1e5..f628cfc2d 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -1914,7 +1914,7 @@ func find*(s: string, sub: char, start: Natural = 0, last = -1): int {.rtl, if length > 0: let found = c_memchr(s[start].unsafeAddr, sub, cast[csize_t](length)) if not found.isNil: - return cast[ByteAddress](found) -% cast[ByteAddress](s.cstring) + return cast[int](found) -% cast[int](s.cstring) else: findImpl() @@ -1970,7 +1970,7 @@ func find*(s, sub: string, start: Natural = 0, last = -1): int {.rtl, if last < 0 and start < s.len and subLen != 0: let found = memmem(s[start].unsafeAddr, csize_t(s.len - start), sub.cstring, csize_t(subLen)) result = if not found.isNil: - cast[ByteAddress](found) -% cast[ByteAddress](s.cstring) + cast[int](found) -% cast[int](s.cstring) else: -1 else: diff --git a/lib/std/envvars.nim b/lib/std/envvars.nim index 46595a3cf..817a155ec 100644 --- a/lib/std/envvars.nim +++ b/lib/std/envvars.nim @@ -183,7 +183,7 @@ when not defined(nimscript): let kv = $e let p = find(kv, '=') yield (substr(kv, 0, p-1), substr(kv, p+1)) - e = cast[typ](cast[ByteAddress](eend)+size) + e = cast[typ](cast[int](eend)+size) if typeof(zero)(eend[1]) == zero: break discard free_fun(env) impl(getEnvironmentStringsW, WideCString, 2, 0, freeEnvironmentStringsW) diff --git a/lib/std/private/strimpl.nim b/lib/std/private/strimpl.nim index 6a38cbfd2..f8c9236a5 100644 --- a/lib/std/private/strimpl.nim +++ b/lib/std/private/strimpl.nim @@ -93,7 +93,7 @@ func find*(s: cstring, sub: char, start: Natural = 0, last = 0): int = if L > 0: let found = c_memchr(s[start].unsafeAddr, sub, cast[csize_t](L)) if not found.isNil: - return cast[ByteAddress](found) -% cast[ByteAddress](s) + return cast[int](found) -% cast[int](s) return -1 func find*(s, sub: cstring, start: Natural = 0, last = 0): int = @@ -108,6 +108,6 @@ func find*(s, sub: cstring, start: Natural = 0, last = 0): int = if last == 0 and s.len > start: let found = c_strstr(cast[cstring](s[start].unsafeAddr), sub) if not found.isNil: - result = cast[ByteAddress](found) -% cast[ByteAddress](s) + result = cast[int](found) -% cast[int](s) else: result = -1 diff --git a/lib/std/syncio.nim b/lib/std/syncio.nim index 2b5ea6a3c..46f113450 100644 --- a/lib/std/syncio.nim +++ b/lib/std/syncio.nim @@ -480,7 +480,7 @@ proc readLine*(f: File, line: var string): bool {.tags: [ReadIOEffect], let m = c_memchr(addr line[pos], '\L'.ord, cast[csize_t](sp)) if m != nil: # \l found: Could be our own or the one by fgets, in any case, we're done - var last = cast[ByteAddress](m) - cast[ByteAddress](addr line[0]) + var last = cast[int](m) - cast[int](addr line[0]) if last > 0 and line[last-1] == '\c': line.setLen(last-1) return last > 1 or fgetsSuccess diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim index a7db3704b..5d1548977 100644 --- a/lib/system/alloc.nim +++ b/lib/system/alloc.nim @@ -290,7 +290,7 @@ proc llAlloc(a: var MemRegion, size: int): pointer = a.llmem.size = PageSize - sizeof(LLChunk) a.llmem.acc = sizeof(LLChunk) a.llmem.next = old - result = cast[pointer](cast[ByteAddress](a.llmem) + a.llmem.acc) + result = cast[pointer](cast[int](a.llmem) + a.llmem.acc) dec(a.llmem.size, size) inc(a.llmem.acc, size) zeroMem(result, size) @@ -422,7 +422,7 @@ iterator allObjects(m: var MemRegion): pointer {.inline.} = var c = cast[PSmallChunk](c) let size = c.size - var a = cast[ByteAddress](addr(c.data)) + var a = cast[int](addr(c.data)) let limit = a + c.acc while a <% limit: yield cast[pointer](a) @@ -441,13 +441,13 @@ when not defined(gcDestructors): # ------------- chunk management ---------------------------------------------- proc pageIndex(c: PChunk): int {.inline.} = - result = cast[ByteAddress](c) shr PageShift + result = cast[int](c) shr PageShift proc pageIndex(p: pointer): int {.inline.} = - result = cast[ByteAddress](p) shr PageShift + result = cast[int](p) shr PageShift proc pageAddr(p: pointer): PChunk {.inline.} = - result = cast[PChunk](cast[ByteAddress](p) and not PageMask) + result = cast[PChunk](cast[int](p) and not PageMask) #sysAssert(Contains(allocator.chunkStarts, pageIndex(result))) when false: @@ -495,13 +495,13 @@ proc requestOsChunks(a: var MemRegion, size: int): PBigChunk = when defined(memtracker): trackLocation(addr result.size, sizeof(int)) - sysAssert((cast[ByteAddress](result) and PageMask) == 0, "requestOsChunks 1") + sysAssert((cast[int](result) and PageMask) == 0, "requestOsChunks 1") #zeroMem(result, size) result.next = nil result.prev = nil result.size = size # update next.prevSize: - var nxt = cast[ByteAddress](result) +% size + var nxt = cast[int](result) +% size sysAssert((nxt and PageMask) == 0, "requestOsChunks 2") var next = cast[PChunk](nxt) if pageIndex(next) in a.chunkStarts: @@ -509,7 +509,7 @@ proc requestOsChunks(a: var MemRegion, size: int): PBigChunk = next.prevSize = size or (next.prevSize and 1) # set result.prevSize: var lastSize = if a.lastSize != 0: a.lastSize else: PageSize - var prv = cast[ByteAddress](result) -% lastSize + var prv = cast[int](result) -% lastSize sysAssert((nxt and PageMask) == 0, "requestOsChunks 3") var prev = cast[PChunk](prv) if pageIndex(prev) in a.chunkStarts and prev.size == lastSize: @@ -555,13 +555,13 @@ proc listRemove[T](head: var T, c: T) {.inline.} = proc updatePrevSize(a: var MemRegion, c: PBigChunk, prevSize: int) {.inline.} = - var ri = cast[PChunk](cast[ByteAddress](c) +% c.size) - sysAssert((cast[ByteAddress](ri) and PageMask) == 0, "updatePrevSize") + var ri = cast[PChunk](cast[int](c) +% c.size) + sysAssert((cast[int](ri) and PageMask) == 0, "updatePrevSize") if isAccessible(a, ri): ri.prevSize = prevSize or (ri.prevSize and 1) proc splitChunk2(a: var MemRegion, c: PBigChunk, size: int): PBigChunk = - result = cast[PBigChunk](cast[ByteAddress](c) +% size) + result = cast[PBigChunk](cast[int](c) +% size) result.size = c.size - size track("result.size", addr result.size, sizeof(int)) when not defined(nimOptimizedSplitChunk): @@ -590,8 +590,8 @@ proc freeBigChunk(a: var MemRegion, c: PBigChunk) = when coalescLeft: let prevSize = c.prevSize if prevSize != 0: - var le = cast[PChunk](cast[ByteAddress](c) -% prevSize) - sysAssert((cast[ByteAddress](le) and PageMask) == 0, "freeBigChunk 4") + var le = cast[PChunk](cast[int](c) -% prevSize) + sysAssert((cast[int](le) and PageMask) == 0, "freeBigChunk 4") if isAccessible(a, le) and chunkUnused(le): sysAssert(not isSmallChunk(le), "freeBigChunk 5") if not isSmallChunk(le) and le.size < MaxBigChunkSize: @@ -607,8 +607,8 @@ proc freeBigChunk(a: var MemRegion, c: PBigChunk) = addChunkToMatrix(a, c) c = rest when coalescRight: - var ri = cast[PChunk](cast[ByteAddress](c) +% c.size) - sysAssert((cast[ByteAddress](ri) and PageMask) == 0, "freeBigChunk 2") + var ri = cast[PChunk](cast[int](c) +% c.size) + sysAssert((cast[int](ri) and PageMask) == 0, "freeBigChunk 2") if isAccessible(a, ri) and chunkUnused(ri): sysAssert(not isSmallChunk(ri), "freeBigChunk 3") if not isSmallChunk(ri) and c.size < MaxBigChunkSize: @@ -669,7 +669,7 @@ proc getHugeChunk(a: var MemRegion; size: int): PBigChunk = incCurrMem(a, size) # XXX add this to the heap links. But also remove it from it later. when false: a.addHeapLink(result, size) - sysAssert((cast[ByteAddress](result) and PageMask) == 0, "getHugeChunk") + sysAssert((cast[int](result) and PageMask) == 0, "getHugeChunk") result.next = nil result.prev = nil result.size = size @@ -838,7 +838,7 @@ proc rawAlloc(a: var MemRegion, requestedSize: int): pointer = c.prev = nil listAdd(a.freeSmallChunks[s], c) result = addr(c.data) - sysAssert((cast[ByteAddress](result) and (MemAlign-1)) == 0, "rawAlloc 4") + sysAssert((cast[int](result) and (MemAlign-1)) == 0, "rawAlloc 4") else: sysAssert(allocInv(a), "rawAlloc: begin c != nil") sysAssert c.next != c, "rawAlloc 5" @@ -856,7 +856,7 @@ proc rawAlloc(a: var MemRegion, requestedSize: int): pointer = if c.freeList == nil: sysAssert(c.acc + smallChunkOverhead() + size <= SmallChunkSize, "rawAlloc 7") - result = cast[pointer](cast[ByteAddress](addr(c.data)) +% c.acc) + result = cast[pointer](cast[int](addr(c.data)) +% c.acc) inc(c.acc, size) else: result = c.freeList @@ -864,14 +864,14 @@ proc rawAlloc(a: var MemRegion, requestedSize: int): pointer = sysAssert(c.freeList.zeroField == 0, "rawAlloc 8") c.freeList = c.freeList.next dec(c.free, size) - sysAssert((cast[ByteAddress](result) and (MemAlign-1)) == 0, "rawAlloc 9") + sysAssert((cast[int](result) and (MemAlign-1)) == 0, "rawAlloc 9") sysAssert(allocInv(a), "rawAlloc: end c != nil") sysAssert(allocInv(a), "rawAlloc: before c.free < size") if c.free < size: sysAssert(allocInv(a), "rawAlloc: before listRemove test") listRemove(a.freeSmallChunks[s], c) sysAssert(allocInv(a), "rawAlloc: end listRemove test") - sysAssert(((cast[ByteAddress](result) and PageMask) - smallChunkOverhead()) %% + sysAssert(((cast[int](result) and PageMask) - smallChunkOverhead()) %% size == 0, "rawAlloc 21") sysAssert(allocInv(a), "rawAlloc: end small size") inc a.occ, size @@ -893,11 +893,11 @@ proc rawAlloc(a: var MemRegion, requestedSize: int): pointer = sysAssert c.prev == nil, "rawAlloc 10" sysAssert c.next == nil, "rawAlloc 11" result = addr(c.data) - sysAssert((cast[ByteAddress](c) and (MemAlign-1)) == 0, "rawAlloc 13") - sysAssert((cast[ByteAddress](c) and PageMask) == 0, "rawAlloc: Not aligned on a page boundary") + sysAssert((cast[int](c) and (MemAlign-1)) == 0, "rawAlloc 13") + sysAssert((cast[int](c) and PageMask) == 0, "rawAlloc: Not aligned on a page boundary") when not defined(gcDestructors): if a.root == nil: a.root = getBottom(a) - add(a, a.root, cast[ByteAddress](result), cast[ByteAddress](result)+%size) + add(a, a.root, cast[int](result), cast[int](result)+%size) inc a.occ, c.size trackSize(c.size) sysAssert(isAccessible(a, result), "rawAlloc 14") @@ -927,10 +927,10 @@ proc rawDealloc(a: var MemRegion, p: pointer) = dec a.occ, s untrackSize(s) sysAssert a.occ >= 0, "rawDealloc: negative occupied memory (case A)" - sysAssert(((cast[ByteAddress](p) and PageMask) - smallChunkOverhead()) %% + sysAssert(((cast[int](p) and PageMask) - smallChunkOverhead()) %% s == 0, "rawDealloc 3") when not defined(gcDestructors): - #echo("setting to nil: ", $cast[ByteAddress](addr(f.zeroField))) + #echo("setting to nil: ", $cast[int](addr(f.zeroField))) sysAssert(f.zeroField != 0, "rawDealloc 1") f.zeroField = 0 f.next = c.freeList @@ -953,7 +953,7 @@ proc rawDealloc(a: var MemRegion, p: pointer) = else: when defined(gcDestructors): addToSharedFreeList(c, f) - sysAssert(((cast[ByteAddress](p) and PageMask) - smallChunkOverhead()) %% + sysAssert(((cast[int](p) and PageMask) - smallChunkOverhead()) %% s == 0, "rawDealloc 2") else: # set to 0xff to check for usage after free bugs: @@ -975,7 +975,7 @@ when not defined(gcDestructors): if not chunkUnused(c): if isSmallChunk(c): var c = cast[PSmallChunk](c) - var offset = (cast[ByteAddress](p) and (PageSize-1)) -% + var offset = (cast[int](p) and (PageSize-1)) -% smallChunkOverhead() result = (c.acc >% offset) and (offset %% c.size == 0) and (cast[ptr FreeCell](p).zeroField >% 1) @@ -993,12 +993,12 @@ when not defined(gcDestructors): if not chunkUnused(c): if isSmallChunk(c): var c = cast[PSmallChunk](c) - var offset = (cast[ByteAddress](p) and (PageSize-1)) -% + var offset = (cast[int](p) and (PageSize-1)) -% smallChunkOverhead() if c.acc >% offset: - sysAssert(cast[ByteAddress](addr(c.data)) +% offset == - cast[ByteAddress](p), "offset is not what you think it is") - var d = cast[ptr FreeCell](cast[ByteAddress](addr(c.data)) +% + sysAssert(cast[int](addr(c.data)) +% offset == + cast[int](p), "offset is not what you think it is") + var d = cast[ptr FreeCell](cast[int](addr(c.data)) +% offset -% (offset %% c.size)) if d.zeroField >% 1: result = d @@ -1025,7 +1025,7 @@ when not defined(gcDestructors): proc ptrSize(p: pointer): int = when not defined(gcDestructors): - var x = cast[pointer](cast[ByteAddress](p) -% sizeof(FreeCell)) + var x = cast[pointer](cast[int](p) -% sizeof(FreeCell)) var c = pageAddr(p) sysAssert(not chunkUnused(c), "ptrSize") result = c.size -% sizeof(FreeCell) @@ -1043,7 +1043,7 @@ proc alloc(allocator: var MemRegion, size: Natural): pointer {.gcsafe.} = result = rawAlloc(allocator, size+sizeof(FreeCell)) cast[ptr FreeCell](result).zeroField = 1 # mark it as used sysAssert(not isAllocatedPtr(allocator, result), "alloc") - result = cast[pointer](cast[ByteAddress](result) +% sizeof(FreeCell)) + result = cast[pointer](cast[int](result) +% sizeof(FreeCell)) track("alloc", result, size) else: result = rawAlloc(allocator, size) @@ -1055,7 +1055,7 @@ proc alloc0(allocator: var MemRegion, size: Natural): pointer = proc dealloc(allocator: var MemRegion, p: pointer) = when not defined(gcDestructors): sysAssert(p != nil, "dealloc: p is nil") - var x = cast[pointer](cast[ByteAddress](p) -% sizeof(FreeCell)) + var x = cast[pointer](cast[int](p) -% sizeof(FreeCell)) sysAssert(x != nil, "dealloc: x is nil") sysAssert(isAccessible(allocator, x), "is not accessible") sysAssert(cast[ptr FreeCell](x).zeroField == 1, "dealloc: object header corrupted") @@ -1116,7 +1116,7 @@ template instantiateForRegion(allocator: untyped) {.dirty.} = result = interiorAllocatedPtr(allocator, p) proc isAllocatedPtr*(p: pointer): bool = - let p = cast[pointer](cast[ByteAddress](p)-%ByteAddress(sizeof(Cell))) + let p = cast[pointer](cast[int](p)-%ByteAddress(sizeof(Cell))) result = isAllocatedPtr(allocator, p) proc deallocOsPages = deallocOsPages(allocator) diff --git a/lib/system/assign.nim b/lib/system/assign.nim index 20b854107..42587929c 100644 --- a/lib/system/assign.nim +++ b/lib/system/assign.nim @@ -15,8 +15,8 @@ proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) {.benign. proc genericAssignAux(dest, src: pointer, n: ptr TNimNode, shallow: bool) {.benign.} = var - d = cast[ByteAddress](dest) - s = cast[ByteAddress](src) + d = cast[int](dest) + s = cast[int](src) case n.kind of nkSlot: genericAssignAux(cast[pointer](d +% n.offset), @@ -56,8 +56,8 @@ template deepSeqAssignImpl(operation, additionalArg) {.dirty.} = proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) = var - d = cast[ByteAddress](dest) - s = cast[ByteAddress](src) + d = cast[int](dest) + s = cast[int](src) sysAssert(mt != nil, "genericAssignAux 2") case mt.kind of tyString: @@ -89,17 +89,17 @@ proc genericAssignAux(dest, src: pointer, mt: PNimType, shallow: bool) = var ss = nimNewSeqOfCap(mt, seq.len) cast[PGenericSeq](ss).len = seq.len unsureAsgnRef(x, ss) - var dst = cast[ByteAddress](cast[PPointer](dest)[]) + var dst = cast[int](cast[PPointer](dest)[]) copyMem(cast[pointer](dst +% align(GenericSeqSize, mt.base.align)), - cast[pointer](cast[ByteAddress](s2) +% align(GenericSeqSize, mt.base.align)), + cast[pointer](cast[int](s2) +% align(GenericSeqSize, mt.base.align)), seq.len *% mt.base.size) else: unsureAsgnRef(x, newSeq(mt, seq.len)) - var dst = cast[ByteAddress](cast[PPointer](dest)[]) + var dst = cast[int](cast[PPointer](dest)[]) for i in 0..seq.len-1: genericAssignAux( cast[pointer](dst +% align(GenericSeqSize, mt.base.align) +% i *% mt.base.size ), - cast[pointer](cast[ByteAddress](s2) +% align(GenericSeqSize, mt.base.align) +% i *% mt.base.size ), + cast[pointer](cast[int](s2) +% align(GenericSeqSize, mt.base.align) +% i *% mt.base.size ), mt.base, shallow) of tyObject: var it = mt.base @@ -181,15 +181,15 @@ proc genericSeqAssign(dest, src: pointer, mt: PNimType) {.compilerproc.} = proc genericAssignOpenArray(dest, src: pointer, len: int, mt: PNimType) {.compilerproc.} = var - d = cast[ByteAddress](dest) - s = cast[ByteAddress](src) + d = cast[int](dest) + s = cast[int](src) for i in 0..len-1: genericAssign(cast[pointer](d +% i *% mt.base.size), cast[pointer](s +% i *% mt.base.size), mt.base) proc objectInit(dest: pointer, typ: PNimType) {.compilerproc, benign.} proc objectInitAux(dest: pointer, n: ptr TNimNode) {.benign.} = - var d = cast[ByteAddress](dest) + var d = cast[int](dest) case n.kind of nkNone: sysAssert(false, "objectInitAux") of nkSlot: objectInit(cast[pointer](d +% n.offset), n.typ) @@ -203,7 +203,7 @@ proc objectInitAux(dest: pointer, n: ptr TNimNode) {.benign.} = proc objectInit(dest: pointer, typ: PNimType) = # the generic init proc that takes care of initialization of complex # objects on the stack or heap - var d = cast[ByteAddress](dest) + var d = cast[int](dest) case typ.kind of tyObject: # iterate over any structural type @@ -226,7 +226,7 @@ proc objectInit(dest: pointer, typ: PNimType) = proc genericReset(dest: pointer, mt: PNimType) {.compilerproc, benign.} proc genericResetAux(dest: pointer, n: ptr TNimNode) = - var d = cast[ByteAddress](dest) + var d = cast[int](dest) case n.kind of nkNone: sysAssert(false, "genericResetAux") of nkSlot: genericReset(cast[pointer](d +% n.offset), n.typ) @@ -238,7 +238,7 @@ proc genericResetAux(dest: pointer, n: ptr TNimNode) = zeroMem(cast[pointer](d +% n.offset), n.typ.size) proc genericReset(dest: pointer, mt: PNimType) = - var d = cast[ByteAddress](dest) + var d = cast[int](dest) sysAssert(mt != nil, "genericReset 2") case mt.kind of tyRef: diff --git a/lib/system/channels_builtin.nim b/lib/system/channels_builtin.nim index fbe3f0e98..e04f2a0db 100644 --- a/lib/system/channels_builtin.nim +++ b/lib/system/channels_builtin.nim @@ -185,8 +185,8 @@ when not usesDestructors: proc storeAux(dest, src: pointer, n: ptr TNimNode, t: PRawChannel, mode: LoadStoreMode) {.benign.} = var - d = cast[ByteAddress](dest) - s = cast[ByteAddress](src) + d = cast[int](dest) + s = cast[int](src) case n.kind of nkSlot: storeAux(cast[pointer](d +% n.offset), cast[pointer](s +% n.offset), n.typ, t, mode) @@ -205,8 +205,8 @@ when not usesDestructors: cast[pointer](cast[int](p) +% x) var - d = cast[ByteAddress](dest) - s = cast[ByteAddress](src) + d = cast[int](dest) + s = cast[int](src) sysAssert(mt != nil, "mt == nil") case mt.kind of tyString: @@ -245,14 +245,14 @@ when not usesDestructors: x[] = alloc0(t.region, align(GenericSeqSize, mt.base.align) +% seq.len *% mt.base.size) else: unsureAsgnRef(x, newSeq(mt, seq.len)) - var dst = cast[ByteAddress](cast[PPointer](dest)[]) + var dst = cast[int](cast[PPointer](dest)[]) var dstseq = cast[PGenericSeq](dst) dstseq.len = seq.len dstseq.reserved = seq.len for i in 0..seq.len-1: storeAux( cast[pointer](dst +% align(GenericSeqSize, mt.base.align) +% i *% mt.base.size), - cast[pointer](cast[ByteAddress](s2) +% align(GenericSeqSize, mt.base.align) +% + cast[pointer](cast[int](s2) +% align(GenericSeqSize, mt.base.align) +% i *% mt.base.size), mt.base, t, mode) if mode != mStore: dealloc(t.region, s2) diff --git a/lib/system/ctypes.nim b/lib/system/ctypes.nim index 6ba28ed49..f6a341477 100644 --- a/lib/system/ctypes.nim +++ b/lib/system/ctypes.nim @@ -67,7 +67,7 @@ type # these work for most platforms: ## This is the same as the type `unsigned long long` in *C*. type - ByteAddress* = int + ByteAddress* {.deprecated: "use `uint`".} = int ## is the signed integer type that should be used for converting ## pointers to integer addresses for readability. diff --git a/lib/system/deepcopy.nim b/lib/system/deepcopy.nim index 1f30b8427..72d35f518 100644 --- a/lib/system/deepcopy.nim +++ b/lib/system/deepcopy.nim @@ -61,8 +61,8 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType; proc genericDeepCopyAux(dest, src: pointer, n: ptr TNimNode; tab: var PtrTable) {.benign.} = var - d = cast[ByteAddress](dest) - s = cast[ByteAddress](src) + d = cast[int](dest) + s = cast[int](src) case n.kind of nkSlot: genericDeepCopyAux(cast[pointer](d +% n.offset), @@ -85,8 +85,8 @@ proc genericDeepCopyAux(dest, src: pointer, n: ptr TNimNode; proc genericDeepCopyAux(dest, src: pointer, mt: PNimType; tab: var PtrTable) = var - d = cast[ByteAddress](dest) - s = cast[ByteAddress](src) + d = cast[int](dest) + s = cast[int](src) sysAssert(mt != nil, "genericDeepCopyAux 2") case mt.kind of tyString: @@ -113,11 +113,11 @@ proc genericDeepCopyAux(dest, src: pointer, mt: PNimType; tab: var PtrTable) = return sysAssert(dest != nil, "genericDeepCopyAux 3") unsureAsgnRef(x, newSeq(mt, seq.len)) - var dst = cast[ByteAddress](cast[PPointer](dest)[]) + var dst = cast[int](cast[PPointer](dest)[]) for i in 0..seq.len-1: genericDeepCopyAux( cast[pointer](dst +% align(GenericSeqSize, mt.base.align) +% i *% mt.base.size), - cast[pointer](cast[ByteAddress](s2) +% align(GenericSeqSize, mt.base.align) +% i *% mt.base.size), + cast[pointer](cast[int](s2) +% align(GenericSeqSize, mt.base.align) +% i *% mt.base.size), mt.base, tab) of tyObject: # we need to copy m_type field for tyObject, as it could be empty for @@ -199,8 +199,8 @@ proc genericSeqDeepCopy(dest, src: pointer, mt: PNimType) {.compilerproc.} = proc genericDeepCopyOpenArray(dest, src: pointer, len: int, mt: PNimType) {.compilerproc.} = var - d = cast[ByteAddress](dest) - s = cast[ByteAddress](src) + d = cast[int](dest) + s = cast[int](src) for i in 0..len-1: genericDeepCopy(cast[pointer](d +% i *% mt.base.size), cast[pointer](s +% i *% mt.base.size), mt.base) diff --git a/lib/system/gc.nim b/lib/system/gc.nim index b36822aad..7db2b6567 100644 --- a/lib/system/gc.nim +++ b/lib/system/gc.nim @@ -170,11 +170,11 @@ proc addZCT(s: var CellSeq, c: PCell) {.noinline.} = proc cellToUsr(cell: PCell): pointer {.inline.} = # convert object (=pointer to refcount) to pointer to userdata - result = cast[pointer](cast[ByteAddress](cell)+%ByteAddress(sizeof(Cell))) + result = cast[pointer](cast[int](cell)+%ByteAddress(sizeof(Cell))) proc usrToCell(usr: pointer): PCell {.inline.} = # convert pointer to userdata to object (=pointer to refcount) - result = cast[PCell](cast[ByteAddress](usr)-%ByteAddress(sizeof(Cell))) + result = cast[PCell](cast[int](usr)-%ByteAddress(sizeof(Cell))) proc extGetCellType(c: pointer): PNimType {.compilerproc.} = # used for code generation concerning debugging @@ -336,7 +336,7 @@ proc cellsetReset(s: var CellSet) = {.push stacktrace:off.} proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) {.benign.} = - var d = cast[ByteAddress](dest) + var d = cast[int](dest) case n.kind of nkSlot: forAllChildrenAux(cast[pointer](d +% n.offset), n.typ, op) of nkList: @@ -356,7 +356,7 @@ proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) {.benign.} = of nkNone: sysAssert(false, "forAllSlotsAux") proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) = - var d = cast[ByteAddress](dest) + var d = cast[int](dest) if dest == nil: return # nothing to do if ntfNoRefs notin mt.flags: case mt.kind @@ -382,7 +382,7 @@ proc forAllChildren(cell: PCell, op: WalkOp) = of tyRef: # common case forAllChildrenAux(cellToUsr(cell), cell.typ.base, op) of tySequence: - var d = cast[ByteAddress](cellToUsr(cell)) + var d = cast[int](cellToUsr(cell)) var s = cast[PGenericSeq](d) if s != nil: for i in 0..s.len-1: @@ -457,7 +457,7 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer = collectCT(gch) var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell))) #gcAssert typ.kind in {tyString, tySequence} or size >= typ.base.size, "size too small" - gcAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2") + gcAssert((cast[int](res) and (MemAlign-1)) == 0, "newObj: 2") # now it is buffered in the ZCT res.typ = typ setFrameInfo(res) @@ -507,7 +507,7 @@ proc newObjRC1(typ: PNimType, size: int): pointer {.compilerRtl, noinline.} = var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell))) sysAssert(allocInv(gch.region), "newObjRC1 after rawAlloc") - sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2") + sysAssert((cast[int](res) and (MemAlign-1)) == 0, "newObj: 2") # now it is buffered in the ZCT res.typ = typ setFrameInfo(res) @@ -549,9 +549,9 @@ proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer = var oldsize = align(GenericSeqSize, elemAlign) + cast[PGenericSeq](old).len * elemSize copyMem(res, ol, oldsize + sizeof(Cell)) - zeroMem(cast[pointer](cast[ByteAddress](res) +% oldsize +% sizeof(Cell)), + zeroMem(cast[pointer](cast[int](res) +% oldsize +% sizeof(Cell)), newsize-oldsize) - sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3") + sysAssert((cast[int](res) and (MemAlign-1)) == 0, "growObj: 3") # This can be wrong for intermediate temps that are nevertheless on the # heap because of lambda lifting: #gcAssert(res.refcount shr rcShift <=% 1, "growObj: 4") @@ -683,7 +683,7 @@ proc collectCycles(gch: var GcHeap) {.raises: [].} = proc gcMark(gch: var GcHeap, p: pointer) {.inline.} = # the addresses are not as cells on the stack, so turn them to cells: sysAssert(allocInv(gch.region), "gcMark begin") - var c = cast[ByteAddress](p) + var c = cast[int](p) if c >% PageSize: # fast check: does it look like a cell? var objStart = cast[PCell](interiorAllocatedPtr(gch.region, p)) @@ -848,10 +848,10 @@ when withRealTime: stack.bottomSaved = stack.bottom when stackIncreases: stack.bottom = cast[pointer]( - cast[ByteAddress](stack.pos) - sizeof(pointer) * 6 - stackSize) + cast[int](stack.pos) - sizeof(pointer) * 6 - stackSize) else: stack.bottom = cast[pointer]( - cast[ByteAddress](stack.pos) + sizeof(pointer) * 6 + stackSize) + cast[int](stack.pos) + sizeof(pointer) * 6 + stackSize) GC_step(gch, us, strongAdvice) diff --git a/lib/system/gc2.nim b/lib/system/gc2.nim index 0593b396e..ed046b5fd 100644 --- a/lib/system/gc2.nim +++ b/lib/system/gc2.nim @@ -133,11 +133,11 @@ template gcAssert(cond: bool, msg: string) = proc cellToUsr(cell: PCell): pointer {.inline.} = # convert object (=pointer to refcount) to pointer to userdata - result = cast[pointer](cast[ByteAddress](cell)+%ByteAddress(sizeof(Cell))) + result = cast[pointer](cast[int](cell)+%ByteAddress(sizeof(Cell))) proc usrToCell(usr: pointer): PCell {.inline.} = # convert pointer to userdata to object (=pointer to refcount) - result = cast[PCell](cast[ByteAddress](usr)-%ByteAddress(sizeof(Cell))) + result = cast[PCell](cast[int](usr)-%ByteAddress(sizeof(Cell))) proc extGetCellType(c: pointer): PNimType {.compilerproc.} = # used for code generation concerning debugging @@ -252,7 +252,7 @@ proc unsureAsgnRef(dest: PPointer, src: pointer) {.compilerproc.} = dest[] = src proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) {.benign.} = - var d = cast[ByteAddress](dest) + var d = cast[int](dest) case n.kind of nkSlot: forAllChildrenAux(cast[pointer](d +% n.offset), n.typ, op) of nkList: @@ -264,7 +264,7 @@ proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) {.benign.} = of nkNone: sysAssert(false, "forAllSlotsAux") proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) = - var d = cast[ByteAddress](dest) + var d = cast[int](dest) if dest == nil: return # nothing to do if ntfNoRefs notin mt.flags: case mt.kind @@ -290,7 +290,7 @@ proc forAllChildren(cell: PCell, op: WalkOp) = of tyRef: # common case forAllChildrenAux(cellToUsr(cell), cell.typ.base, op) of tySequence: - var d = cast[ByteAddress](cellToUsr(cell)) + var d = cast[int](cellToUsr(cell)) var s = cast[PGenericSeq](d) if s != nil: for i in 0..s.len-1: @@ -330,7 +330,7 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer = gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1") collectCT(gch) var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell))) - gcAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2") + gcAssert((cast[int](res) and (MemAlign-1)) == 0, "newObj: 2") # now it is buffered in the ZCT res.typ = typ when leakDetector and not hasThreadSupport: @@ -388,9 +388,9 @@ proc growObj(old: pointer, newsize: int, gch: var GcHeap): pointer = var oldsize = align(GenericSeqSize, elemAlign) + cast[PGenericSeq](old).len*elemSize copyMem(res, ol, oldsize + sizeof(Cell)) - zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(Cell)), + zeroMem(cast[pointer](cast[int](res)+% oldsize +% sizeof(Cell)), newsize-oldsize) - sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3") + sysAssert((cast[int](res) and (MemAlign-1)) == 0, "growObj: 3") when false: # this is wrong since seqs can be shared via 'shallow': when reallyDealloc: rawDealloc(gch.region, ol) @@ -593,7 +593,7 @@ proc gcMark(gch: var GcHeap, p: pointer) {.inline.} = # the addresses are not as cells on the stack, so turn them to cells: sysAssert(allocInv(gch.region), "gcMark begin") var cell = usrToCell(p) - var c = cast[ByteAddress](cell) + var c = cast[int](cell) if c >% PageSize: # fast check: does it look like a cell? var objStart = cast[PCell](interiorAllocatedPtr(gch.region, cell)) @@ -697,10 +697,10 @@ when withRealTime: stack.bottomSaved = stack.bottom when stackIncreases: stack.bottom = cast[pointer]( - cast[ByteAddress](stack.pos) - sizeof(pointer) * 6 - stackSize) + cast[int](stack.pos) - sizeof(pointer) * 6 - stackSize) else: stack.bottom = cast[pointer]( - cast[ByteAddress](stack.pos) + sizeof(pointer) * 6 + stackSize) + cast[int](stack.pos) + sizeof(pointer) * 6 + stackSize) GC_step(gch, us, strongAdvice) diff --git a/lib/system/gc_common.nim b/lib/system/gc_common.nim index f5f4f164f..54c51e783 100644 --- a/lib/system/gc_common.nim +++ b/lib/system/gc_common.nim @@ -222,9 +222,9 @@ proc stackSize(stack: ptr GcStack): int {.noinline.} = if pos != nil: when stackIncreases: - result = cast[ByteAddress](pos) -% cast[ByteAddress](stack.bottom) + result = cast[int](pos) -% cast[int](stack.bottom) else: - result = cast[ByteAddress](stack.bottom) -% cast[ByteAddress](pos) + result = cast[int](stack.bottom) -% cast[int](pos) else: result = 0 @@ -295,8 +295,8 @@ when not defined(useNimRtl): # the first init must be the one that defines the stack bottom: gch.stack.bottom = theStackBottom elif theStackBottom != gch.stack.bottom: - var a = cast[ByteAddress](theStackBottom) # and not PageMask - PageSize*2 - var b = cast[ByteAddress](gch.stack.bottom) + var a = cast[int](theStackBottom) # and not PageMask - PageSize*2 + var b = cast[int](gch.stack.bottom) #c_fprintf(stdout, "old: %p new: %p;\n",gch.stack.bottom,theStackBottom) when stackIncreases: gch.stack.bottom = cast[pointer](min(a, b)) @@ -312,11 +312,11 @@ when not defined(useNimRtl): proc isOnStack(p: pointer): bool = var stackTop {.volatile, noinit.}: pointer stackTop = addr(stackTop) - var a = cast[ByteAddress](gch.getActiveStack().bottom) - var b = cast[ByteAddress](stackTop) + var a = cast[int](gch.getActiveStack().bottom) + var b = cast[int](stackTop) when not stackIncreases: swap(a, b) - var x = cast[ByteAddress](p) + var x = cast[int](p) result = a <=% x and x <=% b when defined(sparc): # For SPARC architecture. @@ -337,7 +337,7 @@ when defined(sparc): # For SPARC architecture. # Addresses decrease as the stack grows. while sp <= max: gcMark(gch, sp[]) - sp = cast[PPointer](cast[ByteAddress](sp) +% sizeof(pointer)) + sp = cast[PPointer](cast[int](sp) +% sizeof(pointer)) elif defined(ELATE): {.error: "stack marking code is to be written for this architecture".} @@ -354,8 +354,8 @@ elif stackIncreases: template forEachStackSlotAux(gch, gcMark: untyped) {.dirty.} = for stack in gch.stack.items(): - var max = cast[ByteAddress](gch.stack.bottom) - var sp = cast[ByteAddress](addr(registers)) -% sizeof(pointer) + var max = cast[int](gch.stack.bottom) + var sp = cast[int](addr(registers)) -% sizeof(pointer) while sp >=% max: gcMark(gch, cast[PPointer](sp)[]) sp = sp -% sizeof(pointer) @@ -383,8 +383,8 @@ else: gch.getActiveStack().setPosition(addr(registers)) if c_setjmp(registers) == 0'i32: # To fill the C stack with registers. for stack in gch.stack.items(): - var max = cast[ByteAddress](stack.bottom) - var sp = cast[ByteAddress](addr(registers)) + var max = cast[int](stack.bottom) + var sp = cast[int](addr(registers)) when defined(amd64): if stack.isActiveStack(): # words within the jmp_buf structure may not be properly aligned. diff --git a/lib/system/gc_ms.nim b/lib/system/gc_ms.nim index f91b37b94..72d22f08b 100644 --- a/lib/system/gc_ms.nim +++ b/lib/system/gc_ms.nim @@ -94,11 +94,11 @@ template gcAssert(cond: bool, msg: string) = proc cellToUsr(cell: PCell): pointer {.inline.} = # convert object (=pointer to refcount) to pointer to userdata - result = cast[pointer](cast[ByteAddress](cell)+%ByteAddress(sizeof(Cell))) + result = cast[pointer](cast[int](cell)+%ByteAddress(sizeof(Cell))) proc usrToCell(usr: pointer): PCell {.inline.} = # convert pointer to userdata to object (=pointer to refcount) - result = cast[PCell](cast[ByteAddress](usr)-%ByteAddress(sizeof(Cell))) + result = cast[PCell](cast[int](usr)-%ByteAddress(sizeof(Cell))) proc extGetCellType(c: pointer): PNimType {.compilerproc.} = # used for code generation concerning debugging @@ -217,7 +217,7 @@ proc initGC() = gcAssert(gch.gcThreadId >= 0, "invalid computed thread ID") proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) {.benign.} = - var d = cast[ByteAddress](dest) + var d = cast[int](dest) case n.kind of nkSlot: forAllChildrenAux(cast[pointer](d +% n.offset), n.typ, op) of nkList: @@ -229,7 +229,7 @@ proc forAllSlotsAux(dest: pointer, n: ptr TNimNode, op: WalkOp) {.benign.} = of nkNone: sysAssert(false, "forAllSlotsAux") proc forAllChildrenAux(dest: pointer, mt: PNimType, op: WalkOp) = - var d = cast[ByteAddress](dest) + var d = cast[int](dest) if dest == nil: return # nothing to do if ntfNoRefs notin mt.flags: case mt.kind @@ -255,7 +255,7 @@ proc forAllChildren(cell: PCell, op: WalkOp) = forAllChildrenAux(cellToUsr(cell), cell.typ.base, op) of tySequence: when not defined(nimSeqsV2): - var d = cast[ByteAddress](cellToUsr(cell)) + var d = cast[int](cellToUsr(cell)) var s = cast[PGenericSeq](d) if s != nil: for i in 0..s.len-1: @@ -268,7 +268,7 @@ proc rawNewObj(typ: PNimType, size: int, gch: var GcHeap): pointer = gcAssert(typ.kind in {tyRef, tyString, tySequence}, "newObj: 1") collectCT(gch, size + sizeof(Cell)) var res = cast[PCell](rawAlloc(gch.region, size + sizeof(Cell))) - gcAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "newObj: 2") + gcAssert((cast[int](res) and (MemAlign-1)) == 0, "newObj: 2") # now it is buffered in the ZCT res.typ = typ when leakDetector and not hasThreadSupport: @@ -336,9 +336,9 @@ when not defined(nimSeqsV2): var oldsize = align(GenericSeqSize, elemAlign) + cast[PGenericSeq](old).len*elemSize copyMem(res, ol, oldsize + sizeof(Cell)) - zeroMem(cast[pointer](cast[ByteAddress](res)+% oldsize +% sizeof(Cell)), + zeroMem(cast[pointer](cast[int](res)+% oldsize +% sizeof(Cell)), newsize-oldsize) - sysAssert((cast[ByteAddress](res) and (MemAlign-1)) == 0, "growObj: 3") + sysAssert((cast[int](res) and (MemAlign-1)) == 0, "growObj: 3") when withBitvectors: incl(gch.allocated, res) when useCellIds: inc gch.idGenerator @@ -446,7 +446,7 @@ proc markGlobals(gch: var GcHeap) = proc gcMark(gch: var GcHeap, p: pointer) {.inline.} = # the addresses are not as cells on the stack, so turn them to cells: - var c = cast[ByteAddress](p) + var c = cast[int](p) if c >% PageSize: # fast check: does it look like a cell? var objStart = cast[PCell](interiorAllocatedPtr(gch.region, p)) diff --git a/lib/system/osalloc.nim b/lib/system/osalloc.nim index 4817059be..925e98ed3 100644 --- a/lib/system/osalloc.nim +++ b/lib/system/osalloc.nim @@ -80,12 +80,12 @@ elif defined(emscripten) and not defined(StandaloneHeapSize): let pos = cast[int](result) # Convert pointer to PageSize correct one. - var new_pos = cast[ByteAddress](pos) +% (PageSize - (pos %% PageSize)) + var new_pos = cast[int](pos) +% (PageSize - (pos %% PageSize)) if (new_pos-pos) < sizeof(EmscriptenMMapBlock): new_pos = new_pos +% PageSize result = cast[pointer](new_pos) - var mmapDescrPos = cast[ByteAddress](result) -% sizeof(EmscriptenMMapBlock) + var mmapDescrPos = cast[int](result) -% sizeof(EmscriptenMMapBlock) var mmapDescr = cast[EmscriptenMMapBlock](mmapDescrPos) mmapDescr.realSize = realSize @@ -96,7 +96,7 @@ elif defined(emscripten) and not defined(StandaloneHeapSize): proc osTryAllocPages(size: int): pointer = osAllocPages(size) proc osDeallocPages(p: pointer, size: int) {.inline.} = - var mmapDescrPos = cast[ByteAddress](p) -% sizeof(EmscriptenMMapBlock) + var mmapDescrPos = cast[int](p) -% sizeof(EmscriptenMMapBlock) var mmapDescr = cast[EmscriptenMMapBlock](mmapDescrPos) munmap(mmapDescr.realPointer, mmapDescr.realSize) diff --git a/lib/system/repr.nim b/lib/system/repr.nim index e47220656..6b6f7e340 100644 --- a/lib/system/repr.nim +++ b/lib/system/repr.nim @@ -155,7 +155,7 @@ when not defined(useNimRtl): var bs = typ.base.size for i in 0..typ.size div bs - 1: if i > 0: add result, ", " - reprAux(result, cast[pointer](cast[ByteAddress](p) + i*bs), typ.base, cl) + reprAux(result, cast[pointer](cast[int](p) + i*bs), typ.base, cl) add result, "]" when defined(nimSeqsV2): @@ -183,7 +183,7 @@ when not defined(useNimRtl): var bs = typ.base.size for i in 0..cast[PGenericSeq](p).len-1: if i > 0: add result, ", " - reprAux(result, cast[pointer](cast[ByteAddress](payloadPtr(p)) + align(payloadOffset, typ.align) + i*bs), + reprAux(result, cast[pointer](cast[int](payloadPtr(p)) + align(payloadOffset, typ.align) + i*bs), typ.base, cl) add result, "]" @@ -194,14 +194,14 @@ when not defined(useNimRtl): of nkSlot: add result, $n.name add result, " = " - reprAux(result, cast[pointer](cast[ByteAddress](p) + n.offset), n.typ, cl) + reprAux(result, cast[pointer](cast[int](p) + n.offset), n.typ, cl) of nkList: for i in 0..n.len-1: if i > 0: add result, ",\n" reprRecordAux(result, p, n.sons[i], cl) of nkCase: var m = selectBranch(p, n) - reprAux(result, cast[pointer](cast[ByteAddress](p) + n.offset), n.typ, cl) + reprAux(result, cast[pointer](cast[int](p) + n.offset), n.typ, cl) if m != nil: reprRecordAux(result, p, m, cl) proc reprRecord(result: var string, p: pointer, typ: PNimType, @@ -307,7 +307,7 @@ when not defined(useNimRtl): var bs = elemtyp.size for i in 0..length - 1: if i > 0: add result, ", " - reprAux(result, cast[pointer](cast[ByteAddress](p) + i*bs), elemtyp, cl) + reprAux(result, cast[pointer](cast[int](p) + i*bs), elemtyp, cl) add result, "]" deinitReprClosure(cl) diff --git a/lib/system/sysstr.nim b/lib/system/sysstr.nim index be32652d8..b8dc7101d 100644 --- a/lib/system/sysstr.nim +++ b/lib/system/sysstr.nim @@ -17,10 +17,10 @@ proc dataPointer(a: PGenericSeq, elemAlign: int): pointer = - cast[pointer](cast[ByteAddress](a) +% align(GenericSeqSize, elemAlign)) + cast[pointer](cast[int](a) +% align(GenericSeqSize, elemAlign)) proc dataPointer(a: PGenericSeq, elemAlign, elemSize, index: int): pointer = - cast[pointer](cast[ByteAddress](a) +% align(GenericSeqSize, elemAlign) +% (index*%elemSize)) + cast[pointer](cast[int](a) +% align(GenericSeqSize, elemAlign) +% (index*%elemSize)) proc resize(old: int): int {.inline.} = if old <= 0: result = 4 diff --git a/tests/parallel/tguard1.nim b/tests/parallel/tguard1.nim index b1eb7e7c5..f4c92319b 100644 --- a/tests/parallel/tguard1.nim +++ b/tests/parallel/tguard1.nim @@ -5,7 +5,7 @@ output: "90" when false: template lock(a, b: ptr Lock; body: stmt) = - if cast[ByteAddress](a) < cast[ByteAddress](b): + if cast[int](a) < cast[int](b): pthread_mutex_lock(a) pthread_mutex_lock(b) else: From 84200f28a114ef9744ecf2b6060ef140e350139b Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 16 Dec 2022 16:42:31 +0800 Subject: [PATCH 3584/3768] bump csource_v2 hash (#21118) --- config/build_config.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/build_config.txt b/config/build_config.txt index 3ae618f00..0ea963c39 100644 --- a/config/build_config.txt +++ b/config/build_config.txt @@ -2,4 +2,4 @@ nim_comment="key-value pairs for windows/posix bootstrapping build scripts" nim_csourcesDir=csources_v2 nim_csourcesUrl=https://github.com/nim-lang/csources_v2.git nim_csourcesBranch=master -nim_csourcesHash=ffd834c20915dc04c78eb0d3b2162832e7ec9c13 +nim_csourcesHash=32cf8431bb19f8803352a0ab3dbee5fec35d5ec6 From 296e7f598e58430415369a34f83552915f57f3f5 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 16 Dec 2022 19:49:26 +0800 Subject: [PATCH 3585/3768] Bump the devel version to 1.9.1 (#21117) --- lib/system/compilation.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/system/compilation.nim b/lib/system/compilation.nim index 6109e9874..234e8cafe 100644 --- a/lib/system/compilation.nim +++ b/lib/system/compilation.nim @@ -6,11 +6,11 @@ const ## ``` # see also std/private/since - NimMinor* {.intdefine.}: int = 7 + NimMinor* {.intdefine.}: int = 9 ## is the minor number of Nim's version. ## Odd for devel, even for releases. - NimPatch* {.intdefine.}: int = 3 + NimPatch* {.intdefine.}: int = 1 ## is the patch number of Nim's version. ## Odd for devel, even for releases. From 7fa983f5e6b5f52a20e477e2443c8fdede244290 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Sat, 17 Dec 2022 00:21:07 +0800 Subject: [PATCH 3586/3768] Nim now ships nimble with 0.14.1 version (#21120) * Nimble now uses latest patch * Update koch.nim --- koch.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/koch.nim b/koch.nim index 84d0911ac..6b3d327f5 100644 --- a/koch.nim +++ b/koch.nim @@ -10,7 +10,7 @@ # const - NimbleStableCommit = "0777f33d1ddbd505b3aa7b714032125349323ceb" # master + NimbleStableCommit = "afd03bc000f4013c48f52381728327dabfccfc4c" # master # examples of possible values: #head, #ea82b54, 1.2.3 FusionStableHash = "#372ee4313827ef9f2ea388840f7d6b46c2b1b014" HeadHash = "#head" From d83f66a24d990f2e4a135a4a64e1c464702b953d Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Sat, 17 Dec 2022 16:10:56 +0800 Subject: [PATCH 3587/3768] fixes #21116; always mangles the param (#21121) * fixes #21116; always mangles the param * idOrSig * fixes tests * Apply suggestions from code review --- compiler/ccgtypes.nim | 6 +----- tests/ccgbugs/t21116.nim | 10 ++++++++++ tests/ccgbugs/tnoalias.nim | 2 +- 3 files changed, 12 insertions(+), 6 deletions(-) create mode 100644 tests/ccgbugs/t21116.nim diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index d7b56de5b..3c9f9d923 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -48,11 +48,9 @@ proc fillBackendName(m: BModule; s: PSym) = writeMangledName(m.ndi, s, m.config) proc fillParamName(m: BModule; s: PSym) = - ## we cannot use 'sigConflicts' here since we have a BModule, not a BProc. - ## Fortunately C's scoping rules are sane enough so that that doesn't - ## cause any trouble. if s.loc.r == "": var res = s.name.s.mangle + res.add idOrSig(s, res, m.sigConflicts) # Take into account if HCR is on because of the following scenario: # if a module gets imported and it has some more importc symbols in it, # some param names might receive the "_0" suffix to distinguish from what @@ -69,8 +67,6 @@ proc fillParamName(m: BModule; s: PSym) = # and a function called in main or proxy uses `socket` as a parameter name. # That would lead to either needing to reload `proxy` or to overwrite the # executable file for the main module, which is running (or both!) -> error. - if m.hcrOn or isKeyword(s.name) or m.g.config.cppDefines.contains(res): - res.add "_0" s.loc.r = res.rope writeMangledName(m.ndi, s, m.config) diff --git a/tests/ccgbugs/t21116.nim b/tests/ccgbugs/t21116.nim new file mode 100644 index 000000000..6418df539 --- /dev/null +++ b/tests/ccgbugs/t21116.nim @@ -0,0 +1,10 @@ +discard """ + target: "c cpp" + disabled: windows +""" +# bug #21116 +import std/os + +proc p(glob: string) = + for _ in walkFiles(glob): discard +p("dir/*") diff --git a/tests/ccgbugs/tnoalias.nim b/tests/ccgbugs/tnoalias.nim index f200992d7..96c3d390b 100644 --- a/tests/ccgbugs/tnoalias.nim +++ b/tests/ccgbugs/tnoalias.nim @@ -1,5 +1,5 @@ discard """ - ccodecheck: "\\i@'NI* NIM_NOALIAS field;' @'NIM_CHAR* NIM_NOALIAS x,' @'void* NIM_NOALIAS q'" + ccodecheck: "\\i@'NI* NIM_NOALIAS field;' @'NIM_CHAR* NIM_NOALIAS x__0qEngDE9aYoYsF8tWnyPacw,' @'void* NIM_NOALIAS q'" """ type From ae4645e8dfec5c7b5f2d8cbdb672b7a0d55dfb37 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Sat, 17 Dec 2022 16:28:59 +0800 Subject: [PATCH 3588/3768] add 2.0 changelog (#21107) [backport: 2.0] add v2.0 changelog --- changelog.md | 296 +------------------------------ changelogs/changelog_2_0_0.md | 321 ++++++++++++++++++++++++++++++++++ 2 files changed, 324 insertions(+), 293 deletions(-) create mode 100644 changelogs/changelog_2_0_0.md diff --git a/changelog.md b/changelog.md index 00bdb50c2..0389faad9 100644 --- a/changelog.md +++ b/changelog.md @@ -1,321 +1,31 @@ -# v2.0.0 - yyyy-mm-dd +# v2.2.0 - yyyy-mm-dd ## Changes affecting backward compatibility -- `httpclient.contentLength` default to `-1` if the Content-Length header is not set in the response, it followed Apache HttpClient(Java), http(go) and .Net HttpWebResponse(C#) behavior. Previously it raised `ValueError`. -- `addr` is now available for all addressable locations, - `unsafeAddr` is now deprecated and an alias for `addr`. - -- Certain definitions from the default `system` module have been moved to - the following new modules: - - - `std/syncio` - - `std/assertions` - - `std/formatfloat` - - `std/objectdollar` - - `std/widestrs` - - `std/typedthreads` - - `std/sysatomics` - - In the future, these definitions will be removed from the `system` module, - and their respective modules will have to be imported to use them. - Currently, to make these imports required, the `-d:nimPreviewSlimSystem` option - may be used. - -- Enabling `-d:nimPreviewSlimSystem` also removes the following deprecated - symbols in the `system` module: - - Aliases with `Error` suffix to exception types that have a `Defect` suffix - (see [exceptions](https://nim-lang.org/docs/exceptions.html)): - `ArithmeticError`, `DivByZeroError`, `OverflowError`, - `AccessViolationError`, `AssertionError`, `OutOfMemError`, `IndexError`, - `FieldError`, `RangeError`, `StackOverflowError`, `ReraiseError`, - `ObjectAssignmentError`, `ObjectConversionError`, `FloatingPointError`, - `FloatOverflowError`, `FloatUnderflowError`, `FloatInexactError`, - `DeadThreadError`, `NilAccessError` - - `addQuitProc`, replaced by `exitprocs.addExitProc` - - Legacy unsigned conversion operations: `ze`, `ze64`, `toU8`, `toU16`, `toU32` - - `TaintedString`, formerly a distinct alias to `string` - - `PInt32`, `PInt64`, `PFloat32`, `PFloat64`, aliases to - `ptr int32`, `ptr int64`, `ptr float32`, `ptr float64` - -- Enabling `-d:nimPreviewSlimSystem` removes the import of `channels_builtin` in - in the `system` module. - -- Enabling `-d:nimPreviewCstringConversion`, `ptr char`, `ptr array[N, char]` and `ptr UncheckedArray[N, char]` don't support conversion to cstring anymore. - -- The `gc:v2` option is removed. - -- The `mainmodule` and `m` options are removed. - -- The `threads:on` option is now the default. - -- Optional parameters in combination with `: body` syntax (RFC #405) are now opt-in via - `experimental:flexibleOptionalParams`. - -- Automatic dereferencing (experimental feature) is removed. - -- The `Math.trunc` polyfill for targeting Internet Explorer was - previously included in most JavaScript output files. - Now, it is only included with `-d:nimJsMathTruncPolyfill`. - If you are targeting Internet Explorer, you may choose to enable this option - or define your own `Math.trunc` polyfill using the [`emit` pragma](https://nim-lang.org/docs/manual.html#implementation-specific-pragmas-emit-pragma). - Nim uses `Math.trunc` for the division and modulo operators for integers. - -- `shallowCopy` and `shallow` are removed for ARC/ORC. Use `move` when possible or combine assignment and -`sink` for optimization purposes. - -- The `nimPreviewDotLikeOps` define is going to be removed or deprecated. - -- The `{.this.}` pragma, deprecated since 0.19, has been removed. -- `nil` literals can no longer be directly assigned to variables or fields of `distinct` pointer types. They must be converted instead. - ```nim - type Foo = distinct ptr int - - # Before: - var x: Foo = nil - # After: - var x: Foo = Foo(nil) - ``` -- Removed two type pragma syntaxes deprecated since 0.20, namely - `type Foo = object {.final.}`, and `type Foo {.final.} [T] = object`. - -- `foo a = b` now means `foo(a = b)` rather than `foo(a) = b`. This is consistent - with the existing behavior of `foo a, b = c` meaning `foo(a, b = c)`. - This decision was made with the assumption that the old syntax was used rarely; - if your code used the old syntax, please be aware of this change. - -- [Overloadable enums](https://nim-lang.github.io/Nim/manual.html#overloadable-enum-value-names) and Unicode Operators - are no longer experimental. - -- Removed the `nimIncrSeqV3` define. - -- `macros.getImpl` for `const` symbols now returns the full definition node - (as `nnkConstDef`) rather than the AST of the constant value. - -- Lock levels are deprecated, now a noop. - -- ORC is now the default memory management strategy. Use - `--mm:refc` for a transition period. - -- `strictEffects` are no longer experimental. - Use `legacy:laxEffects` to keep backward compatibility. - -- The `gorge`/`staticExec` calls will now return a descriptive message in the output - if the execution fails for whatever reason. To get back legacy behaviour use `-d:nimLegacyGorgeErrors`. - -- Pointer to `cstring` conversion now triggers a `[PtrToCstringConv]` warning. - This warning will become an error in future versions! Use a `cast` operation - like `cast[cstring](x)` instead. - -- `logging` will default to flushing all log level messages. To get the legacy behaviour of only flushing Error and Fatal messages, use `-d:nimV1LogFlushBehavior`. - -- Object fields now support default values, see https://nim-lang.github.io/Nim/manual.html#types-default-values-for-object-fields for details. - -- Redefining templates with the same signature was previously - allowed to support certain macro code. To do this explicitly, the - `{.redefine.}` pragma has been added. Note that this is only for templates. - Implicit redefinition of templates is now deprecated and will give an error in the future. - -- Using an unnamed break in a block is deprecated. This warning will become an error in future versions! Use a named block with a named break instead. - -- Several Standard libraries are moved to nimble packages, use `nimble` to install them: - - `std/punycode` => `punycode` - - `std/asyncftpclient` => `asyncftpclient` - - `std/smtp` => `smtp` - - `std/db_common` => `db_connector/db_common` - - `std/db_sqlite` => `db_connector/db_sqlite` - - `std/db_mysql` => `db_connector/db_mysql` - - `std/db_postgres` => `db_connector/db_postgres` - - `std/db_odbc` => `db_connector/db_odbc` - -- Previously, calls like `foo(a, b): ...` or `foo(a, b) do: ...` where the final argument of - `foo` had type `proc ()` were assumed by the compiler to mean `foo(a, b, proc () = ...)`. - This behavior is now deprecated. Use `foo(a, b) do (): ...` or `foo(a, b, proc () = ...)` instead. - -- If no exception or any exception deriving from Exception but not Defect or CatchableError given in except, a `warnBareExcept` warning will be triggered. ## Standard library additions and changes [//]: # "Changes:" -- OpenSSL 3 is now supported. -- `macros.parseExpr` and `macros.parseStmt` now accept an optional - filename argument for more informative errors. -- Module `colors` expanded with missing colors from the CSS color standard. - `colPaleVioletRed` and `colMediumPurple` have also been changed to match the CSS color standard. -- Fixed `lists.SinglyLinkedList` being broken after removing the last node ([#19353](https://github.com/nim-lang/Nim/pull/19353)). -- The `md5` module now works at compile time and in JavaScript. -- Changed `mimedb` to use an `OrderedTable` instead of `OrderedTableRef` to support `const` tables. -- `strutils.find` now uses and defaults to `last = -1` for whole string searches, - making limiting it to just the first char (`last = 0`) valid. -- `random.rand` now works with `Ordinal`s. -- Undeprecated `os.isvalidfilename`. -- `std/oids` now uses `int64` to store time internally (before it was int32). -- `std/uri.Uri` dollar `$` improved, precalculates the `string` result length from the `Uri`. -- `std/uri.Uri.isIpv6` is now exported. -- `std/logging.ConsoleLogger` and `FileLogger` now have a `flushThreshold` attribute to set what log message levels are automatically flushed. For Nim v1 use `-d:nimFlushAllLogs` to automatically flush all message levels. Flushing all logs is the default behavior for Nim v2. - - -- `std/net.IpAddress` dollar `$` improved, uses a fixed capacity for the `string` result based from the `IpAddressFamily`. -- `std/jsfetch.newFetchOptions` now has default values for all parameters -- `std/jsformdata` now accepts `Blob` data type. [//]: # "Additions:" -- Added ISO 8601 week date utilities in `times`: - - Added `IsoWeekRange`, a range type for weeks in a week-based year. - - Added `IsoYear`, a distinct type for a week-based year in contrast to a regular year. - - Added a `initDateTime` overload to create a datetime from an ISO week date. - - Added `getIsoWeekAndYear` to get an ISO week number and week-based year from a datetime. - - Added `getIsoWeeksInYear` to return the number of weeks in a week-based year. -- Added new modules which were part of `std/os`: - - Added `std/oserrors` for OS error reporting. Added `std/envvars` for environment variables handling. - - Added `std/paths`, `std/dirs`, `std/files`, `std/symlinks` and `std/appdirs`. - - Added `std/cmdline` for reading command line parameters. -- Added `sep` parameter in `std/uri` to specify the query separator. -- Added bindings to [`Array.shift`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift) - and [`queueMicrotask`](https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask) - in `jscore` for JavaScript targets. -- Added `UppercaseLetters`, `LowercaseLetters`, `PunctuationChars`, `PrintableChars` sets to `std/strutils`. -- Added `complex.sgn` for obtaining the phase of complex numbers. -- Added `insertAdjacentText`, `insertAdjacentElement`, `insertAdjacentHTML`, - `after`, `before`, `closest`, `append`, `hasAttributeNS`, `removeAttributeNS`, - `hasPointerCapture`, `releasePointerCapture`, `requestPointerLock`, - `replaceChildren`, `replaceWith`, `scrollIntoViewIfNeeded`, `setHTML`, - `toggleAttribute`, and `matches` to `std/dom`. -- Added [`jsre.hasIndices`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/hasIndices) -- Added `capacity` for `string` and `seq` to return the current capacity, see https://github.com/nim-lang/RFCs/issues/460 -- Added `openArray[char]` overloads for `std/parseutils` allowing more code reuse. -- Added `openArray[char]` overloads for `std/unicode` allowing more code reuse. -- Added `safe` parameter to `base64.encodeMime`. + [//]: # "Deprecations:" -- Deprecated `selfExe` for Nimscript. -- Deprecated `std/sums`. -- Deprecated `std/base64.encode` for collections of arbitrary integer element type. - Now only `byte` and `char` are supported. + [//]: # "Removals:" -- Removed deprecated module `parseopt2`. -- Removed deprecated module `sharedstrings`. -- Removed deprecated module `dom_extensions`. -- Removed deprecated module `LockFreeHash`. -- Removed deprecated module `events`. -- Removed deprecated `oids.oidToString`. -- Removed define `nimExperimentalAsyncjsThen` for `std/asyncjs.then` and `std/jsfetch`. -- Removed deprecated `jsre.test` and `jsre.toString`. -- Removed deprecated `math.c_frexp`. -- Removed deprecated `` httpcore.`==` ``. -- Removed deprecated `std/posix.CMSG_SPACE` and `std/posix.CMSG_LEN` that takes wrong argument types. -- Removed deprecated `osproc.poDemon`, symbol with typo. -- Removed deprecated `tables.rightSize`. - - -- Removed deprecated `posix.CLONE_STOPPED`. ## Language changes -- [Tag tracking](https://nim-lang.github.io/Nim/manual.html#effect-system-tag-tracking) supports the definition of forbidden tags by the `.forbids` pragma - which can be used to disable certain effects in proc types. -- [Case statement macros](https://nim-lang.github.io/Nim/manual.html#macros-case-statement-macros) are no longer experimental, - meaning you no longer need to enable the experimental switch `caseStmtMacros` to use them. -- Full command syntax and block arguments i.e. `foo a, b: c` are now allowed - for the right-hand side of type definitions in type sections. Previously - they would error with "invalid indentation". - -- Compile-time define changes: - - `defined` now accepts identifiers separated by dots, i.e. `defined(a.b.c)`. - In the command line, this is defined as `-d:a.b.c`. Older versions can - use accents as in ``defined(`a.b.c`)`` to access such defines. - - [Define pragmas for constants](https://nim-lang.github.io/Nim/manual.html#implementation-specific-pragmas-compileminustime-define-pragmas) - now support a string argument for qualified define names. - - ```nim - # -d:package.FooBar=42 - const FooBar {.intdefine: "package.FooBar".}: int = 5 - echo FooBar # 42 - ``` - - This was added to help disambiguate similar define names for different packages. - In older versions, this could only be achieved with something like the following: - - ```nim - const FooBar = block: - const `package.FooBar` {.intdefine.}: int = 5 - `package.FooBar` - ``` - - A generic `define` pragma for constants has been added that interprets - the value of the define based on the type of the constant value. - See the [experimental manual](https://nim-lang.github.io/Nim/manual_experimental.html#generic-define-pragma) - for a list of supported types. - -- [Macro pragmas](https://nim-lang.github.io/Nim/manual.html#userminusdefined-pragmas-macro-pragmas) changes: - - Templates now accept macro pragmas. - - Macro pragmas for var/let/const sections have been redesigned in a way that works - similarly to routine macro pragmas. The new behavior is documented in the - [experimental manual](https://nim-lang.github.io/Nim/manual_experimental.html#extended-macro-pragmas). - - Pragma macros on type definitions can now return `nnkTypeSection` nodes as well as `nnkTypeDef`, - allowing multiple type definitions to be injected in place of the original type definition. - - ```nim - import macros - macro multiply(amount: static int, s: untyped): untyped = - let name = $s[0].basename - result = newNimNode(nnkTypeSection) - for i in 1 .. amount: - result.add(newTree(nnkTypeDef, ident(name & $i), s[1], s[2])) - type - Foo = object - Bar {.multiply: 3.} = object - x, y, z: int - Baz = object - # becomes - type - Foo = object - Bar1 = object - x, y, z: int - Bar2 = object - x, y, z: int - Bar3 = object - x, y, z: int - Baz = object - ``` - -- A new form of type inference called [top-down inference](https://nim-lang.github.io/Nim/manual_experimental.html#topminusdown-type-inference) - has been implemented for a variety of basic cases. For example, code like the following now compiles: - - ```nim - let foo: seq[(float, byte, cstring)] = @[(1, 2, "abc")] - ``` - -- `cstring` is now accepted as a selector in `case` statements, removing the - need to convert to `string`. On the JS backend, this is translated directly - to a `switch` statement. - -- Nim now supports `out` parameters and ["strict definitions"](https://nim-lang.github.io/Nim/manual_experimental.html#strict-definitions-and-nimout-parameters). -- Nim now offers a [strict mode](https://nim-lang.github.io/Nim/manual_experimental.html#strict-case-objects) for `case objects`. ## Compiler changes -- The `gc` switch has been renamed to `mm` ("memory management") in order to reflect the - reality better. (Nim moved away from all techniques based on "tracing".) -- Defines the `gcRefc` symbol which allows writing specific code for the refc GC. - -- `nim` can now compile version 1.4.0 as follows: `nim c --lib:lib --stylecheck:off compiler/nim`, - without requiring `-d:nimVersion140` which is now a noop. - -- `--styleCheck`, `--hintAsError` and `--warningAsError` now only apply to the current package. - -- The switch `--nimMainPrefix:prefix` has been added to add a prefix to the names of `NimMain` and - related functions produced on the backend. This prevents conflicts with other Nim - static libraries. - -- When compiling for Release the flag `-fno-math-errno` is used for GCC. ## Tool changes -- Nim now ships Nimble version 0.14 which added support for lock-files. Libraries are stored in `$nimbleDir/pkgs2` (it was `$nimbleDir/pkgs`). diff --git a/changelogs/changelog_2_0_0.md b/changelogs/changelog_2_0_0.md new file mode 100644 index 000000000..00bdb50c2 --- /dev/null +++ b/changelogs/changelog_2_0_0.md @@ -0,0 +1,321 @@ +# v2.0.0 - yyyy-mm-dd + + +## Changes affecting backward compatibility +- `httpclient.contentLength` default to `-1` if the Content-Length header is not set in the response, it followed Apache HttpClient(Java), http(go) and .Net HttpWebResponse(C#) behavior. Previously it raised `ValueError`. + +- `addr` is now available for all addressable locations, + `unsafeAddr` is now deprecated and an alias for `addr`. + +- Certain definitions from the default `system` module have been moved to + the following new modules: + + - `std/syncio` + - `std/assertions` + - `std/formatfloat` + - `std/objectdollar` + - `std/widestrs` + - `std/typedthreads` + - `std/sysatomics` + + In the future, these definitions will be removed from the `system` module, + and their respective modules will have to be imported to use them. + Currently, to make these imports required, the `-d:nimPreviewSlimSystem` option + may be used. + +- Enabling `-d:nimPreviewSlimSystem` also removes the following deprecated + symbols in the `system` module: + - Aliases with `Error` suffix to exception types that have a `Defect` suffix + (see [exceptions](https://nim-lang.org/docs/exceptions.html)): + `ArithmeticError`, `DivByZeroError`, `OverflowError`, + `AccessViolationError`, `AssertionError`, `OutOfMemError`, `IndexError`, + `FieldError`, `RangeError`, `StackOverflowError`, `ReraiseError`, + `ObjectAssignmentError`, `ObjectConversionError`, `FloatingPointError`, + `FloatOverflowError`, `FloatUnderflowError`, `FloatInexactError`, + `DeadThreadError`, `NilAccessError` + - `addQuitProc`, replaced by `exitprocs.addExitProc` + - Legacy unsigned conversion operations: `ze`, `ze64`, `toU8`, `toU16`, `toU32` + - `TaintedString`, formerly a distinct alias to `string` + - `PInt32`, `PInt64`, `PFloat32`, `PFloat64`, aliases to + `ptr int32`, `ptr int64`, `ptr float32`, `ptr float64` + +- Enabling `-d:nimPreviewSlimSystem` removes the import of `channels_builtin` in + in the `system` module. + +- Enabling `-d:nimPreviewCstringConversion`, `ptr char`, `ptr array[N, char]` and `ptr UncheckedArray[N, char]` don't support conversion to cstring anymore. + +- The `gc:v2` option is removed. + +- The `mainmodule` and `m` options are removed. + +- The `threads:on` option is now the default. + +- Optional parameters in combination with `: body` syntax (RFC #405) are now opt-in via + `experimental:flexibleOptionalParams`. + +- Automatic dereferencing (experimental feature) is removed. + +- The `Math.trunc` polyfill for targeting Internet Explorer was + previously included in most JavaScript output files. + Now, it is only included with `-d:nimJsMathTruncPolyfill`. + If you are targeting Internet Explorer, you may choose to enable this option + or define your own `Math.trunc` polyfill using the [`emit` pragma](https://nim-lang.org/docs/manual.html#implementation-specific-pragmas-emit-pragma). + Nim uses `Math.trunc` for the division and modulo operators for integers. + +- `shallowCopy` and `shallow` are removed for ARC/ORC. Use `move` when possible or combine assignment and +`sink` for optimization purposes. + +- The `nimPreviewDotLikeOps` define is going to be removed or deprecated. + +- The `{.this.}` pragma, deprecated since 0.19, has been removed. +- `nil` literals can no longer be directly assigned to variables or fields of `distinct` pointer types. They must be converted instead. + ```nim + type Foo = distinct ptr int + + # Before: + var x: Foo = nil + # After: + var x: Foo = Foo(nil) + ``` +- Removed two type pragma syntaxes deprecated since 0.20, namely + `type Foo = object {.final.}`, and `type Foo {.final.} [T] = object`. + +- `foo a = b` now means `foo(a = b)` rather than `foo(a) = b`. This is consistent + with the existing behavior of `foo a, b = c` meaning `foo(a, b = c)`. + This decision was made with the assumption that the old syntax was used rarely; + if your code used the old syntax, please be aware of this change. + +- [Overloadable enums](https://nim-lang.github.io/Nim/manual.html#overloadable-enum-value-names) and Unicode Operators + are no longer experimental. + +- Removed the `nimIncrSeqV3` define. + +- `macros.getImpl` for `const` symbols now returns the full definition node + (as `nnkConstDef`) rather than the AST of the constant value. + +- Lock levels are deprecated, now a noop. + +- ORC is now the default memory management strategy. Use + `--mm:refc` for a transition period. + +- `strictEffects` are no longer experimental. + Use `legacy:laxEffects` to keep backward compatibility. + +- The `gorge`/`staticExec` calls will now return a descriptive message in the output + if the execution fails for whatever reason. To get back legacy behaviour use `-d:nimLegacyGorgeErrors`. + +- Pointer to `cstring` conversion now triggers a `[PtrToCstringConv]` warning. + This warning will become an error in future versions! Use a `cast` operation + like `cast[cstring](x)` instead. + +- `logging` will default to flushing all log level messages. To get the legacy behaviour of only flushing Error and Fatal messages, use `-d:nimV1LogFlushBehavior`. + +- Object fields now support default values, see https://nim-lang.github.io/Nim/manual.html#types-default-values-for-object-fields for details. + +- Redefining templates with the same signature was previously + allowed to support certain macro code. To do this explicitly, the + `{.redefine.}` pragma has been added. Note that this is only for templates. + Implicit redefinition of templates is now deprecated and will give an error in the future. + +- Using an unnamed break in a block is deprecated. This warning will become an error in future versions! Use a named block with a named break instead. + +- Several Standard libraries are moved to nimble packages, use `nimble` to install them: + - `std/punycode` => `punycode` + - `std/asyncftpclient` => `asyncftpclient` + - `std/smtp` => `smtp` + - `std/db_common` => `db_connector/db_common` + - `std/db_sqlite` => `db_connector/db_sqlite` + - `std/db_mysql` => `db_connector/db_mysql` + - `std/db_postgres` => `db_connector/db_postgres` + - `std/db_odbc` => `db_connector/db_odbc` + +- Previously, calls like `foo(a, b): ...` or `foo(a, b) do: ...` where the final argument of + `foo` had type `proc ()` were assumed by the compiler to mean `foo(a, b, proc () = ...)`. + This behavior is now deprecated. Use `foo(a, b) do (): ...` or `foo(a, b, proc () = ...)` instead. + +- If no exception or any exception deriving from Exception but not Defect or CatchableError given in except, a `warnBareExcept` warning will be triggered. + +## Standard library additions and changes + +[//]: # "Changes:" +- OpenSSL 3 is now supported. +- `macros.parseExpr` and `macros.parseStmt` now accept an optional + filename argument for more informative errors. +- Module `colors` expanded with missing colors from the CSS color standard. + `colPaleVioletRed` and `colMediumPurple` have also been changed to match the CSS color standard. +- Fixed `lists.SinglyLinkedList` being broken after removing the last node ([#19353](https://github.com/nim-lang/Nim/pull/19353)). +- The `md5` module now works at compile time and in JavaScript. +- Changed `mimedb` to use an `OrderedTable` instead of `OrderedTableRef` to support `const` tables. +- `strutils.find` now uses and defaults to `last = -1` for whole string searches, + making limiting it to just the first char (`last = 0`) valid. +- `random.rand` now works with `Ordinal`s. +- Undeprecated `os.isvalidfilename`. +- `std/oids` now uses `int64` to store time internally (before it was int32). +- `std/uri.Uri` dollar `$` improved, precalculates the `string` result length from the `Uri`. +- `std/uri.Uri.isIpv6` is now exported. +- `std/logging.ConsoleLogger` and `FileLogger` now have a `flushThreshold` attribute to set what log message levels are automatically flushed. For Nim v1 use `-d:nimFlushAllLogs` to automatically flush all message levels. Flushing all logs is the default behavior for Nim v2. + + +- `std/net.IpAddress` dollar `$` improved, uses a fixed capacity for the `string` result based from the `IpAddressFamily`. +- `std/jsfetch.newFetchOptions` now has default values for all parameters +- `std/jsformdata` now accepts `Blob` data type. + + +[//]: # "Additions:" +- Added ISO 8601 week date utilities in `times`: + - Added `IsoWeekRange`, a range type for weeks in a week-based year. + - Added `IsoYear`, a distinct type for a week-based year in contrast to a regular year. + - Added a `initDateTime` overload to create a datetime from an ISO week date. + - Added `getIsoWeekAndYear` to get an ISO week number and week-based year from a datetime. + - Added `getIsoWeeksInYear` to return the number of weeks in a week-based year. +- Added new modules which were part of `std/os`: + - Added `std/oserrors` for OS error reporting. Added `std/envvars` for environment variables handling. + - Added `std/paths`, `std/dirs`, `std/files`, `std/symlinks` and `std/appdirs`. + - Added `std/cmdline` for reading command line parameters. +- Added `sep` parameter in `std/uri` to specify the query separator. +- Added bindings to [`Array.shift`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift) + and [`queueMicrotask`](https://developer.mozilla.org/en-US/docs/Web/API/queueMicrotask) + in `jscore` for JavaScript targets. +- Added `UppercaseLetters`, `LowercaseLetters`, `PunctuationChars`, `PrintableChars` sets to `std/strutils`. +- Added `complex.sgn` for obtaining the phase of complex numbers. +- Added `insertAdjacentText`, `insertAdjacentElement`, `insertAdjacentHTML`, + `after`, `before`, `closest`, `append`, `hasAttributeNS`, `removeAttributeNS`, + `hasPointerCapture`, `releasePointerCapture`, `requestPointerLock`, + `replaceChildren`, `replaceWith`, `scrollIntoViewIfNeeded`, `setHTML`, + `toggleAttribute`, and `matches` to `std/dom`. +- Added [`jsre.hasIndices`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/hasIndices) +- Added `capacity` for `string` and `seq` to return the current capacity, see https://github.com/nim-lang/RFCs/issues/460 +- Added `openArray[char]` overloads for `std/parseutils` allowing more code reuse. +- Added `openArray[char]` overloads for `std/unicode` allowing more code reuse. +- Added `safe` parameter to `base64.encodeMime`. + +[//]: # "Deprecations:" +- Deprecated `selfExe` for Nimscript. +- Deprecated `std/sums`. +- Deprecated `std/base64.encode` for collections of arbitrary integer element type. + Now only `byte` and `char` are supported. + +[//]: # "Removals:" +- Removed deprecated module `parseopt2`. +- Removed deprecated module `sharedstrings`. +- Removed deprecated module `dom_extensions`. +- Removed deprecated module `LockFreeHash`. +- Removed deprecated module `events`. +- Removed deprecated `oids.oidToString`. +- Removed define `nimExperimentalAsyncjsThen` for `std/asyncjs.then` and `std/jsfetch`. +- Removed deprecated `jsre.test` and `jsre.toString`. +- Removed deprecated `math.c_frexp`. +- Removed deprecated `` httpcore.`==` ``. +- Removed deprecated `std/posix.CMSG_SPACE` and `std/posix.CMSG_LEN` that takes wrong argument types. +- Removed deprecated `osproc.poDemon`, symbol with typo. +- Removed deprecated `tables.rightSize`. + + +- Removed deprecated `posix.CLONE_STOPPED`. + + +## Language changes + +- [Tag tracking](https://nim-lang.github.io/Nim/manual.html#effect-system-tag-tracking) supports the definition of forbidden tags by the `.forbids` pragma + which can be used to disable certain effects in proc types. +- [Case statement macros](https://nim-lang.github.io/Nim/manual.html#macros-case-statement-macros) are no longer experimental, + meaning you no longer need to enable the experimental switch `caseStmtMacros` to use them. +- Full command syntax and block arguments i.e. `foo a, b: c` are now allowed + for the right-hand side of type definitions in type sections. Previously + they would error with "invalid indentation". + +- Compile-time define changes: + - `defined` now accepts identifiers separated by dots, i.e. `defined(a.b.c)`. + In the command line, this is defined as `-d:a.b.c`. Older versions can + use accents as in ``defined(`a.b.c`)`` to access such defines. + - [Define pragmas for constants](https://nim-lang.github.io/Nim/manual.html#implementation-specific-pragmas-compileminustime-define-pragmas) + now support a string argument for qualified define names. + + ```nim + # -d:package.FooBar=42 + const FooBar {.intdefine: "package.FooBar".}: int = 5 + echo FooBar # 42 + ``` + + This was added to help disambiguate similar define names for different packages. + In older versions, this could only be achieved with something like the following: + + ```nim + const FooBar = block: + const `package.FooBar` {.intdefine.}: int = 5 + `package.FooBar` + ``` + - A generic `define` pragma for constants has been added that interprets + the value of the define based on the type of the constant value. + See the [experimental manual](https://nim-lang.github.io/Nim/manual_experimental.html#generic-define-pragma) + for a list of supported types. + +- [Macro pragmas](https://nim-lang.github.io/Nim/manual.html#userminusdefined-pragmas-macro-pragmas) changes: + - Templates now accept macro pragmas. + - Macro pragmas for var/let/const sections have been redesigned in a way that works + similarly to routine macro pragmas. The new behavior is documented in the + [experimental manual](https://nim-lang.github.io/Nim/manual_experimental.html#extended-macro-pragmas). + - Pragma macros on type definitions can now return `nnkTypeSection` nodes as well as `nnkTypeDef`, + allowing multiple type definitions to be injected in place of the original type definition. + + ```nim + import macros + macro multiply(amount: static int, s: untyped): untyped = + let name = $s[0].basename + result = newNimNode(nnkTypeSection) + for i in 1 .. amount: + result.add(newTree(nnkTypeDef, ident(name & $i), s[1], s[2])) + type + Foo = object + Bar {.multiply: 3.} = object + x, y, z: int + Baz = object + # becomes + type + Foo = object + Bar1 = object + x, y, z: int + Bar2 = object + x, y, z: int + Bar3 = object + x, y, z: int + Baz = object + ``` + +- A new form of type inference called [top-down inference](https://nim-lang.github.io/Nim/manual_experimental.html#topminusdown-type-inference) + has been implemented for a variety of basic cases. For example, code like the following now compiles: + + ```nim + let foo: seq[(float, byte, cstring)] = @[(1, 2, "abc")] + ``` + +- `cstring` is now accepted as a selector in `case` statements, removing the + need to convert to `string`. On the JS backend, this is translated directly + to a `switch` statement. + +- Nim now supports `out` parameters and ["strict definitions"](https://nim-lang.github.io/Nim/manual_experimental.html#strict-definitions-and-nimout-parameters). +- Nim now offers a [strict mode](https://nim-lang.github.io/Nim/manual_experimental.html#strict-case-objects) for `case objects`. + + +## Compiler changes + +- The `gc` switch has been renamed to `mm` ("memory management") in order to reflect the + reality better. (Nim moved away from all techniques based on "tracing".) + +- Defines the `gcRefc` symbol which allows writing specific code for the refc GC. + +- `nim` can now compile version 1.4.0 as follows: `nim c --lib:lib --stylecheck:off compiler/nim`, + without requiring `-d:nimVersion140` which is now a noop. + +- `--styleCheck`, `--hintAsError` and `--warningAsError` now only apply to the current package. + +- The switch `--nimMainPrefix:prefix` has been added to add a prefix to the names of `NimMain` and + related functions produced on the backend. This prevents conflicts with other Nim + static libraries. + +- When compiling for Release the flag `-fno-math-errno` is used for GCC. + + +## Tool changes + +- Nim now ships Nimble version 0.14 which added support for lock-files. Libraries are stored in `$nimbleDir/pkgs2` (it was `$nimbleDir/pkgs`). From b8d200683a77487e7ef5d78abd4c1b981709bddd Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Sat, 17 Dec 2022 20:44:30 +0800 Subject: [PATCH 3589/3768] remove unused imports (#21126) --- lib/impure/rdstdin.nim | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/impure/rdstdin.nim b/lib/impure/rdstdin.nim index 935c83671..b0c648373 100644 --- a/lib/impure/rdstdin.nim +++ b/lib/impure/rdstdin.nim @@ -22,8 +22,6 @@ runnableExamples("-r:off"): if line.len > 0: echo line echo "exiting" -when defined(nimPreviewSlimSystem): - import std/syncio when defined(windows): when defined(nimPreviewSlimSystem): From 100b304ac2aaa65222d7a9043acd0c534ed484cb Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Sat, 17 Dec 2022 21:42:29 +0800 Subject: [PATCH 3590/3768] fixes misplaced changelog entries; pre-existing issue (#21125) * move misplaced changelog entries to the right place * fixes misplaced changelog --- changelogs/changelog.md | 33 --------------------------------- changelogs/changelog_2_0_0.md | 19 +++++++++++++++++++ 2 files changed, 19 insertions(+), 33 deletions(-) delete mode 100644 changelogs/changelog.md diff --git a/changelogs/changelog.md b/changelogs/changelog.md deleted file mode 100644 index d793c4313..000000000 --- a/changelogs/changelog.md +++ /dev/null @@ -1,33 +0,0 @@ -# v1.xx.x - yyyy-mm-dd - -## Changes affecting backward compatibility - -## Standard library additions and changes - -- `std/sharedlist` and `std/sharedtables` are now deprecated, see RFC [#433](https://github.com/nim-lang/RFCs/issues/433). - -### New compile flag (`-d:nimNoGetRandom`) when building `std/sysrand` to remove dependency on linux `getrandom` syscall - -This compile flag only affects linux builds and is necessary if either compiling on a linux kernel version < 3.17, or if code built will be executing on kernel < 3.17. - -On linux kernels < 3.17 (such as kernel 3.10 in RHEL7 and CentOS7), the `getrandom` syscall was not yet introduced. Without this, the `std/sysrand` module will not build properly, and if code is built on a kernel >= 3.17 without the flag, any usage of the `std/sysrand` module will fail to execute on a kernel < 3.17 (since it attempts to perform a syscall to `getrandom`, which isn't present in the current kernel). A compile flag has been added to force the `std/sysrand` module to use /dev/urandom (available since linux kernel 1.3.30), rather than the `getrandom` syscall. This allows for use of a cryptographically secure PRNG, regardless of kernel support for the `getrandom` syscall. - -When building for RHEL7/CentOS7 for example, the entire build process for nim from a source package would then be: -```sh -$ yum install devtoolset-8 # Install GCC version 8 vs the standard 4.8.5 on RHEL7/CentOS7. Alternatively use -d:nimEmulateOverflowChecks. See issue #13692 for details -$ scl enable devtoolset-8 bash # Run bash shell with default toolchain of gcc 8 -$ sh build.sh # per unix install instructions -$ bin/nim c koch # per unix install instructions -$ ./koch boot -d:release # per unix install instructions -$ ./koch tools -d:nimNoGetRandom # pass the nimNoGetRandom flag to compile std/sysrand without support for getrandom syscall -``` - -This is necessary to pass when building nim on kernel versions < 3.17 in particular to avoid an error of "SYS_getrandom undeclared" during the build process for stdlib (sysrand in particular). - -## Language changes - - -## Compiler changes - - -## Tool changes diff --git a/changelogs/changelog_2_0_0.md b/changelogs/changelog_2_0_0.md index 00bdb50c2..f34336ff7 100644 --- a/changelogs/changelog_2_0_0.md +++ b/changelogs/changelog_2_0_0.md @@ -160,6 +160,25 @@ - `std/jsfetch.newFetchOptions` now has default values for all parameters - `std/jsformdata` now accepts `Blob` data type. +- `std/sharedlist` and `std/sharedtables` are now deprecated, see RFC [#433](https://github.com/nim-lang/RFCs/issues/433). + +- New compile flag (`-d:nimNoGetRandom`) when building `std/sysrand` to remove dependency on linux `getrandom` syscall. + + This compile flag only affects linux builds and is necessary if either compiling on a linux kernel version < 3.17, or if code built will be executing on kernel < 3.17. + + On linux kernels < 3.17 (such as kernel 3.10 in RHEL7 and CentOS7), the `getrandom` syscall was not yet introduced. Without this, the `std/sysrand` module will not build properly, and if code is built on a kernel >= 3.17 without the flag, any usage of the `std/sysrand` module will fail to execute on a kernel < 3.17 (since it attempts to perform a syscall to `getrandom`, which isn't present in the current kernel). A compile flag has been added to force the `std/sysrand` module to use /dev/urandom (available since linux kernel 1.3.30), rather than the `getrandom` syscall. This allows for use of a cryptographically secure PRNG, regardless of kernel support for the `getrandom` syscall. + + When building for RHEL7/CentOS7 for example, the entire build process for nim from a source package would then be: + ```sh + $ yum install devtoolset-8 # Install GCC version 8 vs the standard 4.8.5 on RHEL7/CentOS7. Alternatively use -d:nimEmulateOverflowChecks. See issue #13692 for details + $ scl enable devtoolset-8 bash # Run bash shell with default toolchain of gcc 8 + $ sh build.sh # per unix install instructions + $ bin/nim c koch # per unix install instructions + $ ./koch boot -d:release # per unix install instructions + $ ./koch tools -d:nimNoGetRandom # pass the nimNoGetRandom flag to compile std/sysrand without support for getrandom syscall + ``` + + This is necessary to pass when building nim on kernel versions < 3.17 in particular to avoid an error of "SYS_getrandom undeclared" during the build process for stdlib (sysrand in particular). [//]: # "Additions:" - Added ISO 8601 week date utilities in `times`: From 53eed2be4515a3ae853ae4d2fbd84daa49c1d6d6 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Sun, 18 Dec 2022 22:31:13 +0800 Subject: [PATCH 3591/3768] close #11705; add a testcase (#21128) --- tests/template/t11705.nim | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 tests/template/t11705.nim diff --git a/tests/template/t11705.nim b/tests/template/t11705.nim new file mode 100644 index 000000000..65ddc7e6c --- /dev/null +++ b/tests/template/t11705.nim @@ -0,0 +1,17 @@ +type RefObj = ref object + +proc `[]`(val: static[int]) = # works with different name/overload or without static arg + discard + +template noRef*(T: typedesc): typedesc = # works without template indirection + typeof(default(T)[]) + +proc `=destroy`(x: var noRef(RefObj)) = + discard + +proc foo = + var x = new RefObj + doAssert $(x[]) == "()" + +# bug #11705 +foo() From 70c575095e89497c63af5d8b2c3ed7dca802179f Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 19 Dec 2022 17:56:49 +0800 Subject: [PATCH 3592/3768] add changelog for the new strict function checking algorithm (#21129) add changelog for the new strict function checking --- changelogs/changelog_2_0_0.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/changelogs/changelog_2_0_0.md b/changelogs/changelog_2_0_0.md index f34336ff7..a899bb929 100644 --- a/changelogs/changelog_2_0_0.md +++ b/changelogs/changelog_2_0_0.md @@ -135,6 +135,8 @@ - If no exception or any exception deriving from Exception but not Defect or CatchableError given in except, a `warnBareExcept` warning will be triggered. +- The experimental strictFuncs feature now disallows a store to the heap via a `ref` or `ptr` indirection. + ## Standard library additions and changes [//]: # "Changes:" From e278a781fc5bfc115326ed6c1873268d51b25303 Mon Sep 17 00:00:00 2001 From: Bung Date: Mon, 19 Dec 2022 19:35:15 +0800 Subject: [PATCH 3593/3768] fix #21109 (#21127) --- compiler/semexprs.nim | 2 ++ tests/misc/t21109.nim | 13 +++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 tests/misc/t21109.nim diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 8ac8f4750..612af482a 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -1947,6 +1947,8 @@ proc semYieldVarResult(c: PContext, n: PNode, restype: PType) = tupleConstr[i] = takeImplicitAddr(c, tupleConstr[i], e.kind == tyLent) else: localError(c.config, n[0].info, errXExpected, "tuple constructor") + elif e.kind == tyEmpty: + localError(c.config, n[0].info, errTypeExpected) else: when false: # XXX investigate what we really need here. diff --git a/tests/misc/t21109.nim b/tests/misc/t21109.nim new file mode 100644 index 000000000..0f7980896 --- /dev/null +++ b/tests/misc/t21109.nim @@ -0,0 +1,13 @@ +discard """ + action: reject + errormsg: "type expected" + file: "iterators.nim" +""" + + +template b(j: untyped) = j +template m() = discard + +b: + for t, f in @[]: + m() From 886572a5162beea672d696fb03422e7777f56cfb Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Tue, 20 Dec 2022 15:04:13 +0800 Subject: [PATCH 3594/3768] disable "Warning: gc is deprecated" in compiler (#21137) * disable "Warning: gc is deprecated" in compiler * Apply @tersec 's suggestion --- compiler/vmops.nim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/vmops.nim b/compiler/vmops.nim index edf457295..d4a91b22d 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -145,6 +145,7 @@ when defined(nimHasInvariant): from std / compilesettings import SingleValueSetting, MultipleValueSetting proc querySettingImpl(conf: ConfigRef, switch: BiggestInt): string = + {.push warning[Deprecated]:off.} case SingleValueSetting(switch) of arguments: result = conf.arguments of outFile: result = conf.outFile.string @@ -162,6 +163,7 @@ when defined(nimHasInvariant): of libPath: result = conf.libpath.string of gc: result = $conf.selectedGC of mm: result = $conf.selectedGC + {.pop.} proc querySettingSeqImpl(conf: ConfigRef, switch: BiggestInt): seq[string] = template copySeq(field: untyped): untyped = From 40b5c4c4c340d44ee724183306716286e40002cb Mon Sep 17 00:00:00 2001 From: Bung Date: Wed, 21 Dec 2022 03:23:48 +0800 Subject: [PATCH 3595/3768] fix #20248;fix #6215;turns into simple CT error (#21141) --- compiler/semtypes.nim | 2 ++ tests/array/t20248.nim | 14 ++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 tests/array/t20248.nim diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index dca0d753b..534861e97 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -328,6 +328,8 @@ proc semRange(c: PContext, n: PNode, prev: PType): PType = proc semArrayIndex(c: PContext, n: PNode): PType = if isRange(n): result = semRangeAux(c, n, nil) + elif n.kind == nkInfix and n[0].kind == nkIdent and n[0].ident.s == "..<": + result = errorType(c) else: let e = semExprWithType(c, n, {efDetermineType}) if e.typ.kind == tyFromExpr: diff --git a/tests/array/t20248.nim b/tests/array/t20248.nim new file mode 100644 index 000000000..66142548b --- /dev/null +++ b/tests/array/t20248.nim @@ -0,0 +1,14 @@ +discard """ +cmd: "nim check --hints:off $file" +errormsg: "ordinal type expected; given: Error Type" +nimout: ''' +t20248.nim(10, 36) Error: ordinal type expected; given: Error Type +t20248.nim(14, 20) Error: ordinal type expected; given: Error Type +''' +""" + +type Vec[N: static[int]] = array[0 ..< N, float] + +var v: Vec[32] + +var stuff: array[0 ..< 16, int] From 0aec095b261bed8b5841beba7ce8cd52de4d54be Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Wed, 21 Dec 2022 03:26:32 +0800 Subject: [PATCH 3596/3768] fixes #19292; fixes #21122; fixes putEnv and setEnv with vcc (#21143) * fixes #19292; fixes 21122; fixes putEnv and setEnv with vcc * add a test --- lib/std/envvars.nim | 8 ++++++-- lib/std/private/win_setenv.nim | 26 +++++++++++++------------- tests/misc/t21443.nim | 6 ++++++ 3 files changed, 25 insertions(+), 15 deletions(-) create mode 100644 tests/misc/t21443.nim diff --git a/lib/std/envvars.nim b/lib/std/envvars.nim index 817a155ec..ce90f66ba 100644 --- a/lib/std/envvars.nim +++ b/lib/std/envvars.nim @@ -63,9 +63,13 @@ when not defined(nimscript): import winlean when defined(nimPreviewSlimSystem): import std/widestrs - proc c_wgetenv(varname: WideCString): WideCString {.importc: "_wgetenv", + + type wchar_t {.importc: "wchar_t", header: "".} = int16 + proc c_wgetenv(varname: ptr wchar_t): ptr wchar_t {.importc: "_wgetenv", header: "".} - proc getEnvImpl(env: cstring): WideCString = c_wgetenv(env.newWideCString) + proc getEnvImpl(env: cstring): WideCString = + let r: WideCString = env.newWideCString + cast[WideCString](c_wgetenv(cast[ptr wchar_t](r))) else: proc c_getenv(env: cstring): cstring {. importc: "getenv", header: "".} diff --git a/lib/std/private/win_setenv.nim b/lib/std/private/win_setenv.nim index 303889a40..66e199dfe 100644 --- a/lib/std/private/win_setenv.nim +++ b/lib/std/private/win_setenv.nim @@ -33,25 +33,25 @@ else: # same as winlean.setEnvironmentVariableA proc c_getenv(varname: cstring): cstring {.importc: "getenv", header: "".} - proc c_wputenv(envstring: WideCString): cint {.importc: "_wputenv", header: "".} - proc c_wgetenv(varname: WideCString): WideCString {.importc: "_wgetenv", header: "".} + proc c_wputenv(envstring: ptr wchar_t): cint {.importc: "_wputenv", header: "".} + proc c_wgetenv(varname: ptr wchar_t): ptr wchar_t {.importc: "_wgetenv", header: "".} var errno {.importc, header: "".}: cint var genviron {.importc: "_environ".}: ptr ptr char # xxx `ptr UncheckedArray[WideCString]` did not work - proc wcstombs(wcstr: ptr char, mbstr: WideCString, count: csize_t): csize_t {.importc, header: "".} + proc wcstombs(wcstr: ptr char, mbstr: ptr wchar_t, count: csize_t): csize_t {.importc, header: "".} # xxx cint vs errno_t? proc setEnvImpl*(name: string, value: string, overwrite: cint): cint = const EINVAL = cint(22) - let wideName = newWideCString(name) - if overwrite == 0 and c_wgetenv(wideName) != nil: + let wideName: WideCString = newWideCString(name) + if overwrite == 0 and c_wgetenv(cast[ptr wchar_t](wideName)) != nil: return 0 if value != "": - let envstring = name & "=" & value - let e = c_wputenv(newWideCString(envstring)) + let envstring: WideCString = newWideCString(name & "=" & value) + let e = c_wputenv(cast[ptr wchar_t](envstring)) if e != 0: errno = EINVAL return -1 @@ -62,19 +62,19 @@ else: SetEnvironmentVariableA doesn't update `_environ`, so we have to do these terrible things. ]# - let envstring = name & "= " - if c_wputenv(newWideCString(envstring)) != 0: + let envstring: WideCString = newWideCString(name & "= ") + if c_wputenv(cast[ptr wchar_t](envstring)) != 0: errno = EINVAL return -1 # Here lies the documentation we blatently ignore to make this work. - var s = c_wgetenv(wideName) + var s = cast[WideCString](c_wgetenv(cast[ptr wchar_t](wideName))) s[0] = Utf16Char('\0') #[ This would result in a double null termination, which normally signifies the end of the environment variable list, so we stick a completely empty environment variable into the list instead. ]# - s = c_wgetenv(wideName) + s = cast[WideCString](c_wgetenv(cast[ptr wchar_t](wideName))) s[1] = Utf16Char('=') #[ If genviron is null, the MBCS environment has not been initialized @@ -88,12 +88,12 @@ else: # in the current codepage. Skip updating MBCS environment in this case. # For some reason, second `wcstombs` can find non-convertible characters # that the first `wcstombs` cannot. - let requiredSizeS = wcstombs(nil, wideName, 0) + let requiredSizeS = wcstombs(nil, cast[ptr wchar_t](wideName), 0) if requiredSizeS != high(csize_t): let requiredSize = requiredSizeS.int var buf = newSeq[char](requiredSize + 1) let buf2 = buf[0].addr - if wcstombs(buf2, wideName, csize_t(requiredSize + 1)) != high(csize_t): + if wcstombs(buf2, cast[ptr wchar_t](wideName), csize_t(requiredSize + 1)) != high(csize_t): var ptrToEnv = c_getenv(cast[cstring](buf2)) ptrToEnv[0] = '\0' ptrToEnv = c_getenv(cast[cstring](buf2)) diff --git a/tests/misc/t21443.nim b/tests/misc/t21443.nim new file mode 100644 index 000000000..70413c5b3 --- /dev/null +++ b/tests/misc/t21443.nim @@ -0,0 +1,6 @@ +import std/envvars + +# bug #19292 +putEnv("NimPutEnvTest", "test") +# bug #21122 +doAssert getEnv("NimPutEnvTest") == "test" From 8a77798dac66a55b42005db8837508a5af0547cf Mon Sep 17 00:00:00 2001 From: Luca Guzzon Date: Tue, 20 Dec 2022 20:29:42 +0100 Subject: [PATCH 3597/3768] ucpu fix-up for arm64 value on macos m1 (#21142) --- tools/niminst/makefile.nimf | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/niminst/makefile.nimf b/tools/niminst/makefile.nimf index 2fe89ed69..b392ab472 100644 --- a/tools/niminst/makefile.nimf +++ b/tools/niminst/makefile.nimf @@ -173,6 +173,9 @@ endif ifeq ($(ucpu),aarch64) mycpu = arm64 endif +ifeq ($(ucpu),arm64) + mycpu = arm64 +endif ifeq ($(ucpu),riscv64) mycpu = riscv64 endif From 81d8ea95af0cfaaedca2fd1881199e113e6f5b41 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Wed, 21 Dec 2022 17:18:47 +0800 Subject: [PATCH 3598/3768] bump csource_v2 to include fixes for macos m1 (#21147) --- config/build_config.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/build_config.txt b/config/build_config.txt index 0ea963c39..286e0dc2a 100644 --- a/config/build_config.txt +++ b/config/build_config.txt @@ -2,4 +2,4 @@ nim_comment="key-value pairs for windows/posix bootstrapping build scripts" nim_csourcesDir=csources_v2 nim_csourcesUrl=https://github.com/nim-lang/csources_v2.git nim_csourcesBranch=master -nim_csourcesHash=32cf8431bb19f8803352a0ab3dbee5fec35d5ec6 +nim_csourcesHash=b66c420697c574be18a20dd4720248a715b4287e From d0721eadf8cd6bf9436b5e5c9113c4c7bcfcc770 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Thu, 22 Dec 2022 04:46:14 +0800 Subject: [PATCH 3599/3768] fixes #21144; try expression will not match the less indentation except (#21152) fixes #21144; try expression will not match the less indent except --- compiler/parser.nim | 4 +++- tests/parser/ttry.nim | 27 +++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 tests/parser/ttry.nim diff --git a/compiler/parser.nim b/compiler/parser.nim index 6b3cebb7f..00013c218 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -1770,11 +1770,13 @@ proc parseTry(p: var Parser; isExpr: bool): PNode = #| (optInd 'except' optionalExprList colcom stmt)* #| (optInd 'finally' colcom stmt)? result = newNodeP(nkTryStmt, p) + let parentIndent = p.currInd # isExpr getTok(p) colcom(p, result) result.add(parseStmt(p)) var b: PNode = nil - while sameOrNoInd(p) or isExpr: + + while sameOrNoInd(p) or (isExpr and parentIndent <= p.tok.indent): case p.tok.tokType of tkExcept: b = newNodeP(nkExceptBranch, p) diff --git a/tests/parser/ttry.nim b/tests/parser/ttry.nim new file mode 100644 index 000000000..190b0b8dc --- /dev/null +++ b/tests/parser/ttry.nim @@ -0,0 +1,27 @@ +# bug #21144 +block: + try: + let c = try: + 10 + except ValueError as exc: + 10 + except ValueError as exc: + discard + +if true: + block: + let c = try: + 10 + except ValueError as exc: + 10 + except OSError: + 99 + + +try: + let c = try: + 10 + except ValueError as exc: + 10 +except ValueError as exc: + discard \ No newline at end of file From 613829f7a4da5506269fadb3acc78d64ee0cbddf Mon Sep 17 00:00:00 2001 From: Peter Munch-Ellingsen Date: Thu, 22 Dec 2022 04:34:36 +0100 Subject: [PATCH 3600/3768] Implement setLineInfo (#21153) * Implement setLineInfo * Add tests --- compiler/msgs.nim | 7 +++++++ compiler/vm.nim | 14 ++++++++++++-- compiler/vmdef.nim | 3 ++- compiler/vmgen.nim | 14 +++++++++++++- lib/core/macros.nim | 16 ++++++++++++++++ tests/macros/tmacros_various.nim | 23 ++++++++++++++++++++++- 6 files changed, 72 insertions(+), 5 deletions(-) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 2c807806e..8b44c8dc6 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -128,6 +128,13 @@ proc fileInfoIdx*(conf: ConfigRef; filename: AbsoluteFile): FileIndex = var dummy: bool result = fileInfoIdx(conf, filename, dummy) +proc fileInfoIdx*(conf: ConfigRef; filename: RelativeFile; isKnownFile: var bool): FileIndex = + fileInfoIdx(conf, AbsoluteFile expandFilename(filename.string), isKnownFile) + +proc fileInfoIdx*(conf: ConfigRef; filename: RelativeFile): FileIndex = + var dummy: bool + fileInfoIdx(conf, AbsoluteFile expandFilename(filename.string), dummy) + proc newLineInfo*(fileInfoIdx: FileIndex, line, col: int): TLineInfo = result.fileIndex = fileInfoIdx if line < int high(uint16): diff --git a/compiler/vm.nim b/compiler/vm.nim index 53a16bcb0..61261c44c 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1934,14 +1934,24 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = of 1: # getLine regs[ra].node = newIntNode(nkIntLit, n.info.line.int) of 2: # getColumn - regs[ra].node = newIntNode(nkIntLit, n.info.col) + regs[ra].node = newIntNode(nkIntLit, n.info.col.int) else: internalAssert c.config, false regs[ra].node.info = n.info regs[ra].node.typ = n.typ - of opcNSetLineInfo: + of opcNCopyLineInfo: decodeB(rkNode) regs[ra].node.info = regs[rb].node.info + of opcNSetLineInfoLine: + decodeB(rkNode) + regs[ra].node.info.line = regs[rb].intVal.uint16 + of opcNSetLineInfoColumn: + decodeB(rkNode) + regs[ra].node.info.col = regs[rb].intVal.int16 + of opcNSetLineInfoFile: + decodeB(rkNode) + regs[ra].node.info.fileIndex = + fileInfoIdx(c.config, RelativeFile regs[rb].node.strVal) of opcEqIdent: decodeBC(rkInt) # aliases for shorter and easier to understand code below diff --git a/compiler/vmdef.nim b/compiler/vmdef.nim index 54876f37d..114a49557 100644 --- a/compiler/vmdef.nim +++ b/compiler/vmdef.nim @@ -141,7 +141,8 @@ type opcNError, opcNWarning, opcNHint, - opcNGetLineInfo, opcNSetLineInfo, + opcNGetLineInfo, opcNCopyLineInfo, opcNSetLineInfoLine, + opcNSetLineInfoColumn, opcNSetLineInfoFile opcEqIdent, opcStrToIdent, opcGetImpl, diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index 1fdfea7c4..6a2ab1d11 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1335,7 +1335,19 @@ proc genMagic(c: PCtx; n: PNode; dest: var TDest; m: TMagic) = of "copyLineInfo": internalAssert c.config, n.len == 3 unused(c, n, dest) - genBinaryStmt(c, n, opcNSetLineInfo) + genBinaryStmt(c, n, opcNCopyLineInfo) + of "setLine": + internalAssert c.config, n.len == 3 + unused(c, n, dest) + genBinaryStmt(c, n, opcNSetLineInfoLine) + of "setColumn": + internalAssert c.config, n.len == 3 + unused(c, n, dest) + genBinaryStmt(c, n, opcNSetLineInfoColumn) + of "setFile": + internalAssert c.config, n.len == 3 + unused(c, n, dest) + genBinaryStmt(c, n, opcNSetLineInfoFile) else: internalAssert c.config, false of mNHint: unused(c, n, dest) diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 18a70f20c..138ec47a0 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -533,6 +533,22 @@ proc getFile(arg: NimNode): string {.magic: "NLineInfo", noSideEffect.} proc copyLineInfo*(arg: NimNode, info: NimNode) {.magic: "NLineInfo", noSideEffect.} ## Copy lineinfo from `info`. +proc setLine(arg: NimNode, line: uint16) {.magic: "NLineInfo", noSideEffect.} +proc setColumn(arg: NimNode, column: int16) {.magic: "NLineInfo", noSideEffect.} +proc setFile(arg: NimNode, file: string) {.magic: "NLineInfo", noSideEffect.} + +proc setLineInfo*(arg: NimNode, file: string, line: int, column: int) = + ## Sets the line info on the NimNode. The file needs to exists, but can be a + ## relative path. If you want to attach line info to a block using `quote` + ## you'll need to add the line information after the quote block. + arg.setFile(file) + arg.setLine(line.uint16) + arg.setColumn(column.int16) + +proc setLineInfo*(arg: NimNode, lineInfo: LineInfo) = + ## See `setLineInfo proc<#setLineInfo,NimNode,string,int,int>`_ + setLineInfo(arg, lineInfo.filename, lineInfo.line, lineInfo.column) + proc lineInfoObj*(n: NimNode): LineInfo = ## Returns `LineInfo` of `n`, using absolute path for `filename`. result = LineInfo(filename: n.getFile, line: n.getLine, column: n.getColumn) diff --git a/tests/macros/tmacros_various.nim b/tests/macros/tmacros_various.nim index d702db56a..08be4602c 100644 --- a/tests/macros/tmacros_various.nim +++ b/tests/macros/tmacros_various.nim @@ -91,7 +91,7 @@ block tlexerex: -block tlineinfo: +block tcopylineinfo: # issue #5617, feature request type Test = object @@ -103,6 +103,27 @@ block tlineinfo: var z = mixer(Test) doAssert z +block tsetgetlineinfo: + # issue #21098, feature request + type Test = object + + macro mixer1(n: typed): untyped = + let x = newIdentNode("echo") + var lineInfo = n.lineInfoObj + x.setLineInfo lineInfo + result = newLit(x.lineInfo == n.lineInfo) + + macro mixer2(n: typed): untyped = + let x = newIdentNode("echo") + var lineInfo = n.lineInfoObj + lineInfo.line += 1 + x.setLineInfo lineInfo + result = newLit(x.lineInfo != n.lineInfo) + + doAssert mixer1(Test) + + doAssert mixer2(Test) + block tdebugstmt: From c5a72ebddd88b6d3a2712230d36367f180faa7da Mon Sep 17 00:00:00 2001 From: Bung Date: Thu, 22 Dec 2022 13:16:25 +0800 Subject: [PATCH 3601/3768] fix #16541 (#21148) --- compiler/sigmatch.nim | 2 ++ tests/misc/t16541.nim | 12 ++++++++++++ 2 files changed, 14 insertions(+) create mode 100644 tests/misc/t16541.nim diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 51728b103..61a4395f3 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -2150,6 +2150,8 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, elif arg.sym.kind in {skMacro, skTemplate}: return nil else: + if arg.sym.ast == nil: + return nil let inferred = c.semGenerateInstance(c, arg.sym, m.bindings, arg.info) result = newSymNode(inferred, arg.info) if r == isInferredConvertible: diff --git a/tests/misc/t16541.nim b/tests/misc/t16541.nim new file mode 100644 index 000000000..452327e8f --- /dev/null +++ b/tests/misc/t16541.nim @@ -0,0 +1,12 @@ +discard """ + action: "reject" + +""" + +import strutils, sugar, nre + +proc my_replace*(s: string, r: Regex, by: string | (proc (match: string): string)): string = + nre.replace(s, r, by) + +discard my_replace("abcde", re"[bcd]", match => match.to_upper) == "aBCDe" +discard my_replace("abcde", re"[bcd]", (match: string) => match.to_upper) == "aBCDe" From 70fe360456b77912ec7f5014f7815137fa089fce Mon Sep 17 00:00:00 2001 From: Jake Leahy Date: Thu, 22 Dec 2022 18:25:24 +1100 Subject: [PATCH 3602/3768] Use `ErrorColor` when a warning that is turned into an error is raised (#21131) Use ErrorColor when a warning that is turned into an error is raised --- compiler/msgs.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/msgs.nim b/compiler/msgs.nim index 8b44c8dc6..8e391f2fb 100644 --- a/compiler/msgs.nim +++ b/compiler/msgs.nim @@ -541,10 +541,11 @@ proc liMessage*(conf: ConfigRef; info: TLineInfo, msg: TMsgKind, arg: string, ignoreMsg = not conf.hasWarn(msg) if not ignoreMsg and msg in conf.warningAsErrors: title = ErrorTitle + color = ErrorColor else: title = WarningTitle + color = WarningColor if not ignoreMsg: writeContext(conf, info) - color = WarningColor inc(conf.warnCounter) of hintMin..hintMax: sev = Severity.Hint From 93b59da4902886cd68dd7df1dce09a1b455a06dc Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Thu, 22 Dec 2022 15:27:11 +0800 Subject: [PATCH 3603/3768] fixes #20244; fixes castSizes warnings (#21102) * fixes #20244; fixes castSizes warnings * fixes js * fixes js * fixes js * fixes * typo * extend using uint64 * Update lib/std/syncio.nim --- compiler/ccgutils.nim | 2 +- compiler/condsyms.nim | 1 + compiler/nim.cfg | 4 ++++ lib/pure/strutils.nim | 16 ++++++++++++++-- 4 files changed, 20 insertions(+), 3 deletions(-) diff --git a/compiler/ccgutils.nim b/compiler/ccgutils.nim index e19fccfa7..d86ebe461 100644 --- a/compiler/ccgutils.nim +++ b/compiler/ccgutils.nim @@ -53,7 +53,7 @@ proc hashString*(conf: ConfigRef; s: string): BiggestInt = a = a + (a shl 3) a = a xor (a shr 11) a = a + (a shl 15) - result = cast[Hash](a) + result = cast[Hash](uint(a)) template getUniqueType*(key: PType): PType = key diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 9d081ef0a..24c6d82d5 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -146,6 +146,7 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasCallsitePragma") defineSymbol("nimHasAmbiguousEnumHint") + defineSymbol("nimHasWarnCastSizes") defineSymbol("nimHasOutParams") defineSymbol("nimHasSystemRaisesDefect") defineSymbol("nimHasWarnUnnamedBreak") diff --git a/compiler/nim.cfg b/compiler/nim.cfg index 96b47b0e6..b9c8fdc08 100644 --- a/compiler/nim.cfg +++ b/compiler/nim.cfg @@ -31,6 +31,10 @@ define:useStdoutAsStdmsg warning[ObservableStores]:off @end +@if nimHasWarnCastSizes: + warning[CastSizes]:on +@end + @if nimHasWarningAsError: warningAsError[GcUnsafe2]:on @end diff --git a/lib/pure/strutils.nim b/lib/pure/strutils.nim index f628cfc2d..dbdd0d6a1 100644 --- a/lib/pure/strutils.nim +++ b/lib/pure/strutils.nim @@ -944,14 +944,26 @@ func toHex*[T: SomeInteger](x: T, len: Positive): string = doAssert b.toHex(4) == "1001" doAssert toHex(62, 3) == "03E" doAssert toHex(-8, 6) == "FFFFF8" - toHexImpl(cast[BiggestUInt](x), len, x < 0) + when defined(js): + toHexImpl(cast[BiggestUInt](x), len, x < 0) + else: + when T is SomeSignedInt: + toHexImpl(cast[BiggestUInt](BiggestInt(x)), len, x < 0) + else: + toHexImpl(BiggestUInt(x), len, x < 0) func toHex*[T: SomeInteger](x: T): string = ## Shortcut for `toHex(x, T.sizeof * 2)` runnableExamples: doAssert toHex(1984'i64) == "00000000000007C0" doAssert toHex(1984'i16) == "07C0" - toHexImpl(cast[BiggestUInt](x), 2*sizeof(T), x < 0) + when defined(js): + toHexImpl(cast[BiggestUInt](x), 2*sizeof(T), x < 0) + else: + when T is SomeSignedInt: + toHexImpl(cast[BiggestUInt](BiggestInt(x)), 2*sizeof(T), x < 0) + else: + toHexImpl(BiggestUInt(x), 2*sizeof(T), x < 0) func toHex*(s: string): string {.rtl.} = ## Converts a bytes string to its hexadecimal representation. From 7931bdac956cb3ad6734eab91c62920f4e80f919 Mon Sep 17 00:00:00 2001 From: Michael Voronin Date: Thu, 22 Dec 2022 10:32:12 +0300 Subject: [PATCH 3604/3768] Feature/xmltree additions (#20988) * [change] add/insert/delete family of xmltree expanded with several variations. Added replace methods family * [change] Lifted child limitations on insert methods (consulted with @araq) * [tests] add/insert/replace/delete of xmltree XmlNodes tests added --- lib/pure/xmltree.nim | 153 ++++++++++++++++++++++++++++++++++- tests/xml/ttree_add.nim | 51 ++++++++++++ tests/xml/ttree_add1.nim | 53 ++++++++++++ tests/xml/ttree_delete.nim | 47 +++++++++++ tests/xml/ttree_delete1.nim | 48 +++++++++++ tests/xml/ttree_insert.nim | 53 ++++++++++++ tests/xml/ttree_insert1.nim | 51 ++++++++++++ tests/xml/ttree_replace.nim | 46 +++++++++++ tests/xml/ttree_replace1.nim | 53 ++++++++++++ 9 files changed, 552 insertions(+), 3 deletions(-) create mode 100644 tests/xml/ttree_add.nim create mode 100644 tests/xml/ttree_add1.nim create mode 100644 tests/xml/ttree_delete.nim create mode 100644 tests/xml/ttree_delete1.nim create mode 100644 tests/xml/ttree_insert.nim create mode 100644 tests/xml/ttree_insert1.nim create mode 100644 tests/xml/ttree_replace.nim create mode 100644 tests/xml/ttree_replace1.nim diff --git a/lib/pure/xmltree.nim b/lib/pure/xmltree.nim index 6228bd10f..7a2cebe87 100644 --- a/lib/pure/xmltree.nim +++ b/lib/pure/xmltree.nim @@ -31,7 +31,7 @@ runnableExamples: ## * `htmlgen module `_ for html code generator import std/private/since -import macros, strtabs, strutils +import macros, strtabs, strutils, sequtils when defined(nimPreviewSlimSystem): import std/assertions @@ -298,26 +298,60 @@ proc innerText*(n: XmlNode): string = proc add*(father, son: XmlNode) {.inline.} = ## Adds the child `son` to `father`. + ## `father` must be of `xnElement` type ## ## See also: + ## * `add proc <#add,XmlNode,openArray[XmlNode]>`_ ## * `insert proc <#insert,XmlNode,XmlNode,int>`_ + ## * `insert proc <#insert,XmlNode,openArray[XmlNode],int>`_ ## * `delete proc <#delete,XmlNode,Natural>`_ + ## * `delete proc <#delete.XmlNode,Slice[int]>`_ + ## * `replace proc <#replace.XmlNode,int,openArray[XmlNode]>`_ + ## * `replace proc <#replace.XmlNode,Slice[int],openArray[XmlNode]>`_ runnableExamples: var f = newElement("myTag") f.add newText("my text") f.add newElement("sonTag") f.add newEntity("my entity") assert $f == "my text&my entity;" + + assert father.k == xnElement add(father.s, son) +proc add*(father: XmlNode, sons: openArray[XmlNode]) {.inline.} = + ## Adds the children `sons` to `father`. + ## `father` must be of `xnElement` type + ## + ## See also: + ## * `add proc <#add,XmlNode,XmlNode>`_ + ## * `insert proc <#insert,XmlNode,XmlNode,int>`_ + ## * `insert proc <#insert,XmlNode,openArray[XmlNode],int>`_ + ## * `delete proc <#delete,XmlNode,Natural>`_ + ## * `delete proc <#delete.XmlNode,Slice[int]>`_ + ## * `replace proc <#replace.XmlNode,int,openArray[XmlNode]>`_ + ## * `replace proc <#replace.XmlNode,Slice[int],openArray[XmlNode]>`_ + runnableExamples: + var f = newElement("myTag") + f.add(@[newText("my text"), newElement("sonTag"), newEntity("my entity")]) + assert $f == "my text&my entity;" + + assert father.k == xnElement + add(father.s, sons) + + proc insert*(father, son: XmlNode, index: int) {.inline.} = ## Inserts the child `son` to a given position in `father`. ## - ## `father` and `son` must be of `xnElement` kind. + ## `father` must be of `xnElement` kind. ## ## See also: + ## * `insert proc <#insert,XmlNode,openArray[XmlNode],int>`_ ## * `add proc <#add,XmlNode,XmlNode>`_ + ## * `add proc <#add,XmlNode,openArray[XmlNode]>`_ ## * `delete proc <#delete,XmlNode,Natural>`_ + ## * `delete proc <#delete.XmlNode,Slice[int]>`_ + ## * `replace proc <#replace.XmlNode,int,openArray[XmlNode]>`_ + ## * `replace proc <#replace.XmlNode,Slice[int],openArray[XmlNode]>`_ runnableExamples: var f = newElement("myTag") f.add newElement("first") @@ -327,18 +361,52 @@ proc insert*(father, son: XmlNode, index: int) {.inline.} = """ - assert father.k == xnElement and son.k == xnElement + assert father.k == xnElement if len(father.s) > index: insert(father.s, son, index) else: insert(father.s, son, len(father.s)) +proc insert*(father: XmlNode, sons: openArray[XmlNode], index: int) {.inline.} = + ## Inserts the children openArray[`sons`] to a given position in `father`. + ## + ## `father` must be of `xnElement` kind. + ## + ## See also: + ## * `insert proc <#insert,XmlNode,XmlNode,int>`_ + ## * `add proc <#add,XmlNode,XmlNode>`_ + ## * `add proc <#add,XmlNode,openArray[XmlNode]>`_ + ## * `delete proc <#delete,XmlNode,Natural>`_ + ## * `delete proc <#delete.XmlNode,Slice[int]>`_ + ## * `replace proc <#replace.XmlNode,int,openArray[XmlNode]>`_ + ## * `replace proc <#replace.XmlNode,Slice[int],openArray[XmlNode]>`_ + runnableExamples: + var f = newElement("myTag") + f.add newElement("first") + f.insert([newElement("second"), newElement("third")], 0) + assert $f == """ + + + +""" + + assert father.k == xnElement + if len(father.s) > index: + insert(father.s, sons, index) + else: + insert(father.s, sons, len(father.s)) + proc delete*(n: XmlNode, i: Natural) = ## Deletes the `i`'th child of `n`. ## ## See also: + ## * `delete proc <#delete.XmlNode,Slice[int]>`_ ## * `add proc <#add,XmlNode,XmlNode>`_ + ## * `add proc <#add,XmlNode,openArray[XmlNode]>`_ ## * `insert proc <#insert,XmlNode,XmlNode,int>`_ + ## * `insert proc <#insert,XmlNode,openArray[XmlNode],int>`_ + ## * `replace proc <#replace.XmlNode,int,openArray[XmlNode]>`_ + ## * `replace proc <#replace.XmlNode,Slice[int],openArray[XmlNode]>`_ runnableExamples: var f = newElement("myTag") f.add newElement("first") @@ -351,6 +419,85 @@ proc delete*(n: XmlNode, i: Natural) = assert n.k == xnElement n.s.delete(i) +proc delete*(n: XmlNode, slice: Slice[int]) = + ## Deletes the items `n[slice]` of `n`. + ## + ## See also: + ## * `delete proc <#delete.XmlNode,int>`_ + ## * `add proc <#add,XmlNode,XmlNode>`_ + ## * `add proc <#add,XmlNode,openArray[XmlNode]>`_ + ## * `insert proc <#insert,XmlNode,XmlNode,int>`_ + ## * `insert proc <#insert,XmlNode,openArray[XmlNode],int>`_ + ## * `replace proc <#replace.XmlNode,int,openArray[XmlNode]>`_ + ## * `replace proc <#replace.XmlNode,Slice[int],openArray[XmlNode]>`_ + runnableExamples: + var f = newElement("myTag") + f.add newElement("first") + f.insert([newElement("second"), newElement("third")], 0) + f.delete(0..1) + assert $f == """ + +""" + + assert n.k == xnElement + n.s.delete(slice) + +proc replace*(n: XmlNode, i: Natural, replacement: openArray[XmlNode]) = + ## Replaces the `i`'th child of `n` with `replacement` openArray. + ## + ## `n` must be of `xnElement` kind. + ## + ## See also: + ## * `replace proc <#replace.XmlNode,Slice[int],openArray[XmlNode]>`_ + ## * `add proc <#add,XmlNode,XmlNode>`_ + ## * `add proc <#add,XmlNode,openArray[XmlNode]>`_ + ## * `delete proc <#delete,XmlNode,Natural>`_ + ## * `delete proc <#delete.XmlNode,Slice[int]>`_ + ## * `insert proc <#insert,XmlNode,XmlNode,int>`_ + ## * `insert proc <#insert,XmlNode,openArray[XmlNode],int>`_ + runnableExamples: + var f = newElement("myTag") + f.add newElement("first") + f.insert(newElement("second"), 0) + f.replace(0, @[newElement("third"), newElement("fourth")]) + assert $f == """ + + + +""" + + assert n.k == xnElement + n.s.delete(i) + n.s.insert(replacement, i) + +proc replace*(n: XmlNode, slice: Slice[int], replacement: openArray[XmlNode]) = + ## Deletes the items `n[slice]` of `n`. + ## + ## `n` must be of `xnElement` kind. + ## + ## See also: + ## * `replace proc <#replace.XmlNode,int,openArray[XmlNode]>`_ + ## * `add proc <#add,XmlNode,XmlNode>`_ + ## * `add proc <#add,XmlNode,openArray[XmlNode]>`_ + ## * `delete proc <#delete,XmlNode,Natural>`_ + ## * `delete proc <#delete.XmlNode,Slice[int]>`_ + ## * `insert proc <#insert,XmlNode,XmlNode,int>`_ + ## * `insert proc <#insert,XmlNode,openArray[XmlNode],int>`_ + runnableExamples: + var f = newElement("myTag") + f.add newElement("first") + f.insert([newElement("second"), newElement("fifth")], 0) + f.replace(0..1, @[newElement("third"), newElement("fourth")]) + assert $f == """ + + + +""" + + assert n.k == xnElement + n.s.delete(slice) + n.s.insert(replacement, slice.a) + proc len*(n: XmlNode): int {.inline.} = ## Returns the number of `n`'s children. runnableExamples: diff --git a/tests/xml/ttree_add.nim b/tests/xml/ttree_add.nim new file mode 100644 index 000000000..4c6ef6cf9 --- /dev/null +++ b/tests/xml/ttree_add.nim @@ -0,0 +1,51 @@ +discard """ + output: ''' + +
      Some text in body
      +
      Some more text in body
      + + + +
      Some text
      +
      Some more text
      + + +
      Some text in body
      +
      Some more text in body
      + +
      +''' +""" + +# Test xmltree add/insert/delete/replace operations +import xmlparser +import xmltree +var baseDocHead = """ + + +
      Some text
      +
      Some more text
      + +
      +""" +var baseDocHeadTree = parseXml(baseDocHead) +var baseDocBody = """ + +
      Some text in body
      +
      Some more text in body
      + +""" +var baseDocBodyTree = parseXml(baseDocBody) + +proc test_add() = + var testDoc = baseDocHeadTree + var newBody = newElement("body") + for item in baseDocBodyTree.items(): + newBody.add(item) + + echo $newBody + + testDoc.add(newBody) + echo $testDoc + +test_add() diff --git a/tests/xml/ttree_add1.nim b/tests/xml/ttree_add1.nim new file mode 100644 index 000000000..30ec83c02 --- /dev/null +++ b/tests/xml/ttree_add1.nim @@ -0,0 +1,53 @@ +discard """ + output: ''' + +
      Some text in body
      +
      Some more text in body
      + + + +
      Some text
      +
      Some more text
      + + +
      Some text in body
      +
      Some more text in body
      + +
      +''' +""" + +# Test xmltree add/insert/delete/replace operations +import xmlparser +import xmltree +var baseDocHead = """ + + +
      Some text
      +
      Some more text
      + +
      +""" +var baseDocHeadTree = parseXml(baseDocHead) +var baseDocBody = """ + +
      Some text in body
      +
      Some more text in body
      + +""" +var baseDocBodyTree = parseXml(baseDocBody) + +proc test_add() = + var testDoc = baseDocHeadTree + var newBody = newElement("body") + var bodyItems: seq[XmlNode] = @[] + for item in baseDocBodyTree.items(): + bodyItems.add(item) + newBody.add(bodyItems) + + echo $newBody + + testDoc.add(newBody) + echo $testDoc + +test_add() diff --git a/tests/xml/ttree_delete.nim b/tests/xml/ttree_delete.nim new file mode 100644 index 000000000..32b477839 --- /dev/null +++ b/tests/xml/ttree_delete.nim @@ -0,0 +1,47 @@ +discard """ + output: ''' + + +
      Some text
      +
      Some more text
      + + +
      Some text in body
      +
      Some more text in body
      + +
      +''' +""" + +# Test xmltree add/insert/delete/replace operations +import xmlparser +import xmltree +let initialDocBase = """ + + +
      Some text
      +
      Some more text
      + + +
      MORE TEXT
      +
      MORE TEXT Some more text
      +
      + +
      MORE TEXT
      +
      MORE TEXT Some more text
      +
      + +
      Some text in body
      +
      Some more text in body
      + +
      +""" +var initialDocBaseTree = parseXml(initialDocBase) + +proc test_delete() = + var testDoc = initialDocBaseTree + + testDoc.delete(1..2) + echo $testDoc + +test_delete() diff --git a/tests/xml/ttree_delete1.nim b/tests/xml/ttree_delete1.nim new file mode 100644 index 000000000..a8442a093 --- /dev/null +++ b/tests/xml/ttree_delete1.nim @@ -0,0 +1,48 @@ +discard """ + output: ''' + + +
      Some text
      +
      Some more text
      + + +
      Some text in body
      +
      Some more text in body
      + +
      +''' +""" + +# Test xmltree add/insert/delete/replace operations +import xmlparser +import xmltree +let initialDocBase = """ + + +
      Some text
      +
      Some more text
      + + +
      MORE TEXT
      +
      MORE TEXT Some more text
      +
      + +
      MORE TEXT
      +
      MORE TEXT Some more text
      +
      + +
      Some text in body
      +
      Some more text in body
      + +
      +""" +var initialDocBaseTree = parseXml(initialDocBase) + +proc test_delete() = + var testDoc = initialDocBaseTree + + testDoc.delete(1) + testDoc.delete(1) + echo $testDoc + +test_delete() diff --git a/tests/xml/ttree_insert.nim b/tests/xml/ttree_insert.nim new file mode 100644 index 000000000..b2941395b --- /dev/null +++ b/tests/xml/ttree_insert.nim @@ -0,0 +1,53 @@ +discard """ + output: ''' + +
      Some text in body
      +
      Some more text in body
      + + + +
      Some text
      +
      Some more text
      + + +
      Some text in body
      +
      Some more text in body
      + +
      +''' +""" + +# Test xmltree add/insert/delete/replace operations +import xmlparser +import xmltree +var baseDocHead = """ + + +
      Some text
      +
      Some more text
      + +
      +""" +var baseDocHeadTree = parseXml(baseDocHead) +var baseDocBody = """ + +
      Some text in body
      +
      Some more text in body
      + +""" +var baseDocBodyTree = parseXml(baseDocBody) + +proc test_insert() = + var testDoc = baseDocHeadTree + var newBody = newElement("body") + var bodyItems: seq[XmlNode] = @[] + for item in baseDocBodyTree.items(): + bodyItems.insert(item, len(bodyItems)) + newBody.insert(bodyItems, 1) + + echo $newBody + + testDoc.insert(newBody, 1) + echo $testDoc + +test_insert() diff --git a/tests/xml/ttree_insert1.nim b/tests/xml/ttree_insert1.nim new file mode 100644 index 000000000..9aa3faf69 --- /dev/null +++ b/tests/xml/ttree_insert1.nim @@ -0,0 +1,51 @@ +discard """ + output: ''' + +
      Some text in body
      +
      Some more text in body
      + + + +
      Some text
      +
      Some more text
      + + +
      Some text in body
      +
      Some more text in body
      + +
      +''' +""" + +# Test xmltree add/insert/delete/replace operations +import xmlparser +import xmltree +var baseDocHead = """ + + +
      Some text
      +
      Some more text
      + +
      +""" +var baseDocHeadTree = parseXml(baseDocHead) +var baseDocBody = """ + +
      Some text in body
      +
      Some more text in body
      + +""" +var baseDocBodyTree = parseXml(baseDocBody) + +proc test_insert() = + var testDoc = baseDocHeadTree + var newBody = newElement("body") + for item in baseDocBodyTree.items(): + newBody.insert(item, len(newBody)) + + echo $newBody + + testDoc.insert(newBody, 1) + echo $testDoc + +test_insert() diff --git a/tests/xml/ttree_replace.nim b/tests/xml/ttree_replace.nim new file mode 100644 index 000000000..97d2db638 --- /dev/null +++ b/tests/xml/ttree_replace.nim @@ -0,0 +1,46 @@ +discard """ + output: ''' + + +
      Some text
      +
      Some more text
      + + +
      Some text in body
      +
      Some more text in body
      + +
      +''' +""" + +# Test xmltree add/insert/delete/replace operations +import xmlparser +import xmltree +var baseDocBody = """ + +
      Some text in body
      +
      Some more text in body
      + +""" +var baseDocBodyTree = parseXml(baseDocBody) +let initialDocBase = """ + + +
      Some text
      +
      Some more text
      + + +
      Some text in body before replace
      +
      Some more text in body before replace
      + +
      +""" +var initialDocBaseTree = parseXml(initialDocBase) + +proc test_replace() = + var testDoc = initialDocBaseTree + + testDoc.replace(1, @[baseDocBodyTree]) + echo $testDoc + +test_replace() diff --git a/tests/xml/ttree_replace1.nim b/tests/xml/ttree_replace1.nim new file mode 100644 index 000000000..059ce2085 --- /dev/null +++ b/tests/xml/ttree_replace1.nim @@ -0,0 +1,53 @@ +discard """ + output: ''' + + +
      Some text
      +
      Some more text
      + + +
      Some text in body
      +
      Some more text in body
      + +
      +''' +""" + +# Test xmltree add/insert/delete/replace operations +import xmlparser +import xmltree +var baseDocHead = """ + +
      Some text
      +
      Some more text
      + +""" +var baseDocHeadTree = parseXml(baseDocHead) +var baseDocBody = """ + +
      Some text in body
      +
      Some more text in body
      + +""" +var baseDocBodyTree = parseXml(baseDocBody) +let initialDocBase = """ + + +
      Some text before replace
      +
      Some more text before replace
      + + +
      Some text in body before replace
      +
      Some more text in body before replace
      + +
      +""" +var initialDocBaseTree = parseXml(initialDocBase) + +proc test_replace() = + var testDoc = initialDocBaseTree + + testDoc.replace(0..1, @[baseDocHeadTree, baseDocBodyTree]) + echo $testDoc + +test_replace() From 314a24472e83eedf058ffa614edf432e8744d0cb Mon Sep 17 00:00:00 2001 From: Peder Bergebakken Sundt Date: Thu, 22 Dec 2022 09:54:17 +0100 Subject: [PATCH 3605/3768] add link to setutils in system module docs (#21130) --- lib/system_overview.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/system_overview.rst b/lib/system_overview.rst index b338845d6..cc0643bf1 100644 --- a/lib/system_overview.rst +++ b/lib/system_overview.rst @@ -99,6 +99,7 @@ Proc Usage =============================== ====================================== **See also:** +* `setutils module `_ for bit set convenience functions * `sets module `_ for hash sets * `intsets module `_ for efficient int sets From 37daed389735ac66f5c950be9ee1c6658b019016 Mon Sep 17 00:00:00 2001 From: metagn Date: Thu, 22 Dec 2022 11:54:41 +0300 Subject: [PATCH 3606/3768] remove misleading slimsystem deprecated warnings (#21156) refs #20967 --- lib/std/formatfloat.nim | 2 -- lib/system.nim | 3 --- 2 files changed, 5 deletions(-) diff --git a/lib/std/formatfloat.nim b/lib/std/formatfloat.nim index 8abbe59cd..b216d1fd0 100644 --- a/lib/std/formatfloat.nim +++ b/lib/std/formatfloat.nim @@ -11,8 +11,6 @@ when defined(nimPreviewSlimSystem): import std/assertions -else: - {.deprecated: "formatfloat is about to move out of system; use `-d:nimPreviewSlimSystem` and import `std/formatfloat`".} proc c_memcpy(a, b: pointer, size: csize_t): pointer {.importc: "memcpy", header: "", discardable.} diff --git a/lib/system.nim b/lib/system.nim index 65055f145..1b6235fb3 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1630,7 +1630,6 @@ template newException*(exceptn: typedesc, message: string; (ref exceptn)(msg: message, parent: parentException) when not defined(nimPreviewSlimSystem): - {.deprecated: "assertions is about to move out of system; use `-d:nimPreviewSlimSystem` and import `std/assertions`".} import std/assertions export assertions @@ -2068,7 +2067,6 @@ when not defined(js): when hostOS != "standalone": include system/threadimpl when not defined(nimPreviewSlimSystem): - {.deprecated: "threads is about to move out of system; use `-d:nimPreviewSlimSystem` and import `std/typedthreads`".} import std/typedthreads export typedthreads @@ -2782,7 +2780,6 @@ when notJSnotNims: releaseSys echoLock when not defined(nimPreviewSlimSystem): - {.deprecated: "io is about to move out of system; use `-d:nimPreviewSlimSystem` and import `std/syncio`".} import std/syncio export syncio From 18c115c8d0b38e6dbb7fae5bdda94bfca1a0ecef Mon Sep 17 00:00:00 2001 From: Jake Leahy Date: Fri, 23 Dec 2022 00:44:10 +1100 Subject: [PATCH 3607/3768] Don't repeat suggestions for same symbol (#21140) * Track seen module graphs so symbols from the same module aren't repeated Add test case * Track symbols instead of modules * Don't show duplicate symbols in spell checker Removes the declared location from the message. Since we don't show duplicates anymore it would be a bit misleading if we only show the location for the first declaration of the symbol --- compiler/lookups.nim | 25 ++++++++++++++++++++----- compiler/suggest.nim | 6 +++--- tests/misc/tspellsuggest.nim | 30 +++++++++++++++--------------- tests/misc/tspellsuggest2.nim | 24 ++++++++++++------------ tests/misc/tspellsuggest3.nim | 21 +++++++++++++++++++++ 5 files changed, 71 insertions(+), 35 deletions(-) create mode 100644 tests/misc/tspellsuggest3.nim diff --git a/compiler/lookups.nim b/compiler/lookups.nim index df5a5333e..d50fc6e27 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -15,7 +15,7 @@ when defined(nimPreviewSlimSystem): import intsets, ast, astalgo, idents, semdata, types, msgs, options, - renderer, nimfix/prettybase, lineinfos, modulegraphs, astmsgs + renderer, nimfix/prettybase, lineinfos, modulegraphs, astmsgs, sets proc ensureNoMissingOrUnusedSymbols(c: PContext; scope: PScope) @@ -170,6 +170,7 @@ iterator allSyms*(c: PContext): (PSym, int, bool) = # really iterate over all symbols in all the scopes. This is expensive # and only used by suggest.nim. var isLocal = true + var scopeN = 0 for scope in allScopes(c.currentScope): if scope == c.topLevelScope: isLocal = false @@ -184,6 +185,17 @@ iterator allSyms*(c: PContext): (PSym, int, bool) = assert s != nil yield (s, scopeN, isLocal) +iterator uniqueSyms*(c: PContext): (PSym, int, bool) = + ## Like [allSyms] except only returns unique symbols (Uniqueness determined by line + name) + # Track seen symbols so we don't duplicate them. + # The int is for the symbols name, and line info is + # to be able to tell apart symbols with same name but on different lines + var seen = initHashSet[(TLineInfo, int)]() + for res in allSyms(c): + if not seen.containsOrIncl((res[0].info, res[0].name.id)): + yield res + + proc someSymFromImportTable*(c: PContext; name: PIdent; ambiguous: var bool): PSym = var marked = initIntSet() var symSet = OverloadableSyms @@ -445,12 +457,13 @@ proc fixSpelling(c: PContext, n: PNode, ident: PIdent, result: var string) = let dist = editDistance(name0, sym.name.s.nimIdentNormalize) var msg: string msg.add "\n ($1, $2): '$3'" % [$dist, $depth, sym.name.s] - addDeclaredLoc(msg, c.config, sym) # `msg` needed for deterministic ordering. list.push SpellCandidate(dist: dist, depth: depth, msg: msg, sym: sym) if list.len == 0: return let e0 = list[0] - var count = 0 + var + count = 0 + last: PIdent = nil while true: # pending https://github.com/timotheecour/Nim/issues/373 use more efficient `itemsSorted`. if list.len == 0: break @@ -466,8 +479,10 @@ proc fixSpelling(c: PContext, n: PNode, ident: PIdent, result: var string) = elif count >= c.config.spellSuggestMax: break if count == 0: result.add "\ncandidates (edit distance, scope distance); see '--spellSuggest': " - result.add e.msg - count.inc + if e.sym.name != last: + result.add e.msg + count.inc + last = e.sym.name proc errorUseQualifier(c: PContext; info: TLineInfo; s: PSym; amb: var bool): PSym = var err = "ambiguous identifier: '" & s.name.s & "'" diff --git a/compiler/suggest.nim b/compiler/suggest.nim index 41d39ccab..e0b0fb516 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -289,7 +289,7 @@ proc suggestField(c: PContext, s: PSym; f: PNode; info: TLineInfo; outputs: var s.getQuality, pm, c.inTypeContext > 0, 0)) template wholeSymTab(cond, section: untyped) {.dirty.} = - for (item, scopeN, isLocal) in allSyms(c): + for (item, scopeN, isLocal) in uniqueSyms(c): let it = item var pm: PrefixMatch if cond: @@ -362,7 +362,7 @@ proc suggestOperations(c: PContext, n, f: PNode, typ: PType, outputs: var Sugges proc suggestEverything(c: PContext, n, f: PNode, outputs: var Suggestions) = # do not produce too many symbols: - for (it, scopeN, isLocal) in allSyms(c): + for (it, scopeN, isLocal) in uniqueSyms(c): var pm: PrefixMatch if filterSym(it, f, pm): outputs.add(symToSuggest(c.graph, it, isLocal = isLocal, ideSug, n.info, @@ -680,7 +680,7 @@ proc suggestSentinel*(c: PContext) = inc(c.compilesContextId) var outputs: Suggestions = @[] # suggest everything: - for (it, scopeN, isLocal) in allSyms(c): + for (it, scopeN, isLocal) in uniqueSyms(c): var pm: PrefixMatch if filterSymNoOpr(it, nil, pm): outputs.add(symToSuggest(c.graph, it, isLocal = isLocal, ideSug, diff --git a/tests/misc/tspellsuggest.nim b/tests/misc/tspellsuggest.nim index 033ed0afc..345458bb1 100644 --- a/tests/misc/tspellsuggest.nim +++ b/tests/misc/tspellsuggest.nim @@ -5,21 +5,21 @@ discard """ nimout: ''' tspellsuggest.nim(45, 13) Error: undeclared identifier: 'fooBar' candidates (edit distance, scope distance); see '--spellSuggest': - (1, 0): 'fooBar8' [var declared in tspellsuggest.nim(43, 9)] - (1, 1): 'fooBar7' [var declared in tspellsuggest.nim(41, 7)] - (1, 3): 'fooBar1' [var declared in tspellsuggest.nim(33, 5)] - (1, 3): 'fooBar2' [let declared in tspellsuggest.nim(34, 5)] - (1, 3): 'fooBar3' [const declared in tspellsuggest.nim(35, 7)] - (1, 3): 'fooBar4' [proc declared in tspellsuggest.nim(36, 6)] - (1, 3): 'fooBar5' [template declared in tspellsuggest.nim(37, 10)] - (1, 3): 'fooBar6' [macro declared in tspellsuggest.nim(38, 7)] - (1, 5): 'FooBar' [type declared in mspellsuggest.nim(5, 6)] - (1, 5): 'fooBar4' [proc declared in mspellsuggest.nim(1, 6)] - (1, 5): 'fooBar9' [var declared in mspellsuggest.nim(2, 5)] - (1, 5): 'fooCar' [var declared in mspellsuggest.nim(4, 5)] - (2, 5): 'FooCar' [type declared in mspellsuggest.nim(6, 6)] - (2, 5): 'GooBa' [type declared in mspellsuggest.nim(7, 6)] - (3, 0): 'fooBarBaz' [const declared in tspellsuggest.nim(44, 11)] + (1, 0): 'fooBar8' + (1, 1): 'fooBar7' + (1, 3): 'fooBar1' + (1, 3): 'fooBar2' + (1, 3): 'fooBar3' + (1, 3): 'fooBar4' + (1, 3): 'fooBar5' + (1, 3): 'fooBar6' + (1, 5): 'FooBar' + (1, 5): 'fooBar4' + (1, 5): 'fooBar9' + (1, 5): 'fooCar' + (2, 5): 'FooCar' + (2, 5): 'GooBa' + (3, 0): 'fooBarBaz' ''' """ diff --git a/tests/misc/tspellsuggest2.nim b/tests/misc/tspellsuggest2.nim index 78504c513..bf76cc208 100644 --- a/tests/misc/tspellsuggest2.nim +++ b/tests/misc/tspellsuggest2.nim @@ -5,18 +5,18 @@ discard """ nimout: ''' tspellsuggest2.nim(45, 13) Error: undeclared identifier: 'fooBar' candidates (edit distance, scope distance); see '--spellSuggest': - (1, 0): 'fooBar8' [var declared in tspellsuggest2.nim(43, 9)] - (1, 1): 'fooBar7' [var declared in tspellsuggest2.nim(41, 7)] - (1, 3): 'fooBar1' [var declared in tspellsuggest2.nim(33, 5)] - (1, 3): 'fooBar2' [let declared in tspellsuggest2.nim(34, 5)] - (1, 3): 'fooBar3' [const declared in tspellsuggest2.nim(35, 7)] - (1, 3): 'fooBar4' [proc declared in tspellsuggest2.nim(36, 6)] - (1, 3): 'fooBar5' [template declared in tspellsuggest2.nim(37, 10)] - (1, 3): 'fooBar6' [macro declared in tspellsuggest2.nim(38, 7)] - (1, 5): 'FooBar' [type declared in mspellsuggest.nim(5, 6)] - (1, 5): 'fooBar4' [proc declared in mspellsuggest.nim(1, 6)] - (1, 5): 'fooBar9' [var declared in mspellsuggest.nim(2, 5)] - (1, 5): 'fooCar' [var declared in mspellsuggest.nim(4, 5)] + (1, 0): 'fooBar8' + (1, 1): 'fooBar7' + (1, 3): 'fooBar1' + (1, 3): 'fooBar2' + (1, 3): 'fooBar3' + (1, 3): 'fooBar4' + (1, 3): 'fooBar5' + (1, 3): 'fooBar6' + (1, 5): 'FooBar' + (1, 5): 'fooBar4' + (1, 5): 'fooBar9' + (1, 5): 'fooCar' ''' """ diff --git a/tests/misc/tspellsuggest3.nim b/tests/misc/tspellsuggest3.nim new file mode 100644 index 000000000..9b0b84602 --- /dev/null +++ b/tests/misc/tspellsuggest3.nim @@ -0,0 +1,21 @@ +discard """ + # pending bug #16521 (bug 12) use `matrix` + cmd: "nim c --spellsuggest:4 --hints:off $file" + action: "reject" + nimout: ''' +tspellsuggest3.nim(21, 1) Error: undeclared identifier: 'fooBar' +candidates (edit distance, scope distance); see '--spellSuggest': + (1, 2): 'FooBar' + (1, 2): 'fooBar4' + (1, 2): 'fooBar9' + (1, 2): 'fooCar' +''' +""" + +import ./mspellsuggest +import ./mspellsuggest +import ./mspellsuggest +import ./mspellsuggest + + +fooBar From 3bba2b34fd632a4c01a6eb0e6f13d4e3a20bf104 Mon Sep 17 00:00:00 2001 From: Jaremy Creechley Date: Thu, 22 Dec 2022 13:43:27 -0700 Subject: [PATCH 3608/3768] fix socket send for string types (#21155) * fix socket send for string types * include windows version Co-authored-by: ringabout <43030857+ringabout@users.noreply.github.com> --- lib/pure/net.nim | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 1dfdbbbe6..be9f2e48c 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -1735,15 +1735,36 @@ proc send*(socket: Socket, data: pointer, size: int): int {. result = send(socket.fd, data, size, int32(MSG_NOSIGNAL)) proc send*(socket: Socket, data: string, - flags = {SocketFlag.SafeDisconn}) {.tags: [WriteIOEffect].} = - ## sends data to a socket. - let sent = send(socket, cstring(data), data.len) - if sent < 0: - let lastError = osLastError() - socketError(socket, lastError = lastError, flags = flags) + flags = {SocketFlag.SafeDisconn}, maxRetries = 100) {.tags: [WriteIOEffect].} = + ## Sends data to a socket. Will try to send all the data by handling interrupts + ## and incomplete writes up to `maxRetries`. + var written = 0 + var attempts = 0 + while data.len - written > 0: + let sent = send(socket, cstring(data), data.len) - if sent != data.len: - raiseOSError(osLastError(), "Could not send all data.") + if sent < 0: + let lastError = osLastError() + let isBlockingErr = + when defined(nimdoc): + false + elif useWinVersion: + lastError.int32 == WSAEINTR or + lastError.int32 == WSAEWOULDBLOCK + else: + lastError.int32 == EINTR or + lastError.int32 == EWOULDBLOCK or + lastError.int32 == EAGAIN + + if not isBlockingErr: + let lastError = osLastError() + socketError(socket, lastError = lastError, flags = flags) + else: + attempts.inc() + if attempts > maxRetries: + raiseOSError(osLastError(), "Could not send all data.") + else: + written.inc(sent) template `&=`*(socket: Socket; data: typed) = ## an alias for 'send'. From 13251c2ac9c7e76fb506eeb686a5e5d19d67d0de Mon Sep 17 00:00:00 2001 From: Bung Date: Fri, 23 Dec 2022 16:47:01 +0800 Subject: [PATCH 3609/3768] fix #12946 Bad C++ codegen on distinct generics C++ types (#21157) --- compiler/ccgtypes.nim | 9 +++++---- tests/cpp/t12946.nim | 4 ++++ 2 files changed, 9 insertions(+), 4 deletions(-) create mode 100644 tests/cpp/t12946.nim diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index 3c9f9d923..e7677f2f2 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -826,7 +826,8 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin m.s[cfsTypes].addf("typedef $1 $2[$3];$n", [foo, result, rope(n)]) of tyObject, tyTuple: - if isImportedCppType(t) and origTyp.kind == tyGenericInst: + let tt = origTyp.skipTypes({tyDistinct}) + if isImportedCppType(t) and tt.kind == tyGenericInst: let cppNameAsRope = getTypeName(m, t, sig) let cppName = $cppNameAsRope var i = 0 @@ -849,7 +850,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin result.add cppName.substr(chunkStart, chunkEnd) chunkStart = i - let typeInSlot = resolveStarsInCppType(origTyp, idx + 1, stars) + let typeInSlot = resolveStarsInCppType(tt, idx + 1, stars) addResultType(typeInSlot) else: inc i @@ -858,9 +859,9 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin result.add cppName.substr(chunkStart) else: result = cppNameAsRope & "<" - for i in 1.. 1: result.add(" COMMA ") - addResultType(origTyp[i]) + addResultType(tt[i]) result.add("> ") # always call for sideeffects: assert t.kind != tyTuple diff --git a/tests/cpp/t12946.nim b/tests/cpp/t12946.nim new file mode 100644 index 000000000..cf6bf5cfc --- /dev/null +++ b/tests/cpp/t12946.nim @@ -0,0 +1,4 @@ +import std/atomics +type Futex = distinct Atomic[int32] + +var x: Futex From ca9c74391a2169be0b0e5c7b6705eaf9560a44bd Mon Sep 17 00:00:00 2001 From: Bung Date: Fri, 23 Dec 2022 18:32:03 +0800 Subject: [PATCH 3610/3768] fix #11634 (#21146) --- compiler/semstmts.nim | 9 ++++++--- tests/misc/t11634.nim | 20 ++++++++++++++++++++ 2 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 tests/misc/t11634.nim diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index a7a02d7fa..356c2d63e 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -725,9 +725,12 @@ proc semVarOrLet(c: PContext, n: PNode, symkind: TSymKind): PNode = if v.kind == skLet and sfImportc notin v.flags and (strictDefs notin c.features or not isLocalSym(v)): localError(c.config, a.info, errLetNeedsInit) if sfCompileTime in v.flags: - var x = newNodeI(result.kind, v.info) - x.add result[i] - vm.setupCompileTimeVar(c.module, c.idgen, c.graph, x) + if a.kind != nkVarTuple: + var x = newNodeI(result.kind, v.info) + x.add result[i] + vm.setupCompileTimeVar(c.module, c.idgen, c.graph, x) + else: + localError(c.config, a.info, "cannot destructure to compile time variable") if v.flags * {sfGlobal, sfThread} == {sfGlobal}: message(c.config, v.info, hintGlobalVar) if {sfGlobal, sfPure} <= v.flags: diff --git a/tests/misc/t11634.nim b/tests/misc/t11634.nim new file mode 100644 index 000000000..4ecb6a53c --- /dev/null +++ b/tests/misc/t11634.nim @@ -0,0 +1,20 @@ +discard """ + action: reject + nimout: ''' +t11634.nim(20, 7) Error: cannot destructure to compile time variable +''' +""" + +type Foo = ref object + val: int + +proc divmod(a, b: Foo): (Foo, Foo) = + ( + Foo(val: a.val div b.val), + Foo(val: a.val mod b.val) + ) + +block: + let a {.compileTime.} = Foo(val: 2) + let b {.compileTime.} = Foo(val: 3) + let (c11634 {.compileTime.}, d11634 {.compileTime.}) = divmod(a, b) From 0b319fee3de90759987beba289df3c48ab6d3f1a Mon Sep 17 00:00:00 2001 From: Bung Date: Fri, 23 Dec 2022 19:20:25 +0800 Subject: [PATCH 3611/3768] =?UTF-8?q?fix=20#20997=20calling=20system.card[?= =?UTF-8?q?T](x:=20set[T])=20with=20T=20of=20int8=20or=20uint8=20=E2=80=A6?= =?UTF-8?q?=20(#21010)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix #20997 calling system.card[T](x: set[T]) with T of int8 or uint8 uses mismatched C array sizes * fullfil set variant --- compiler/ccgexprs.nim | 2 +- lib/system/sets.nim | 8 +++++--- tests/sets/t20997.nim | 18 ++++++++++++++++++ 3 files changed, 24 insertions(+), 4 deletions(-) create mode 100644 tests/sets/t20997.nim diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 2a4335144..d7ef024b1 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2126,7 +2126,7 @@ proc genSetOp(p: BProc, e: PNode, d: var TLoc, op: TMagic) = of mCard: var a: TLoc initLocExpr(p, e[1], a) - putIntoDest(p, d, e, ropecg(p.module, "#cardSet($1, $2)", [rdCharLoc(a), size])) + putIntoDest(p, d, e, ropecg(p.module, "#cardSet($1, $2)", [addrLoc(p.config, a), size])) of mLtSet, mLeSet: getTemp(p, getSysType(p.module.g.graph, unknownLineInfo, tyInt), i) # our counter initLocExpr(p, e[1], a) diff --git a/lib/system/sets.nim b/lib/system/sets.nim index 103c8d343..e230985e0 100644 --- a/lib/system/sets.nim +++ b/lib/system/sets.nim @@ -10,10 +10,9 @@ # set handling type - NimSet = array[0..4*2048-1, uint8] + NimSet = array[0..8192-1, uint8] - -proc cardSet(s: NimSet, len: int): int {.compilerproc, inline.} = +proc cardSetImpl(s: openArray[uint8], len: int): int {.inline.} = var i = 0 result = 0 when defined(x86) or defined(amd64): @@ -24,3 +23,6 @@ proc cardSet(s: NimSet, len: int): int {.compilerproc, inline.} = while i < len: inc(result, countBits32(uint32(s[i]))) inc(i, 1) + +proc cardSet(s: NimSet, len: int): int {.compilerproc, inline.} = + result = cardSetImpl(s, len) diff --git a/tests/sets/t20997.nim b/tests/sets/t20997.nim new file mode 100644 index 000000000..b320eee1a --- /dev/null +++ b/tests/sets/t20997.nim @@ -0,0 +1,18 @@ +discard """ + joinable: false +""" + +{.passC: "-flto".} +{.passL: "-flto".} + +template f(n: int) = discard card(default(set[range[0 .. (1 shl n) - 1]])) +f( 7) +f( 8) +f( 9) +f(10) +f(11) +f(12) +f(13) +f(14) +f(15) +f(16) From 0ffc322cf615685ae4d528e26ddd13f6ea072684 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 23 Dec 2022 22:31:53 +0800 Subject: [PATCH 3612/3768] add the cpp target (#21164) The issue is related to cpp codegen, the previous test doesn't test cpp backend, which will join into the megatest. --- tests/cpp/t12946.nim | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/cpp/t12946.nim b/tests/cpp/t12946.nim index cf6bf5cfc..79cd56251 100644 --- a/tests/cpp/t12946.nim +++ b/tests/cpp/t12946.nim @@ -1,3 +1,7 @@ +discard """ + targets: "c cpp" +""" + import std/atomics type Futex = distinct Atomic[int32] From ac5a36c0a4be3de6565f2a071c04d1fd8f1b67da Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 23 Dec 2022 23:50:07 +0800 Subject: [PATCH 3613/3768] re and nre now link to regex and tinyre (#21161) --- lib/impure/nre.nim | 8 +++++--- lib/impure/re.nim | 4 ++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/impure/nre.nim b/lib/impure/nre.nim index e9dd49df0..2c1b1deae 100644 --- a/lib/impure/nre.nim +++ b/lib/impure/nre.nim @@ -20,15 +20,17 @@ when defined(js): ## search the internet for a wide variety of third-party documentation and ## tools. ## -## **Note**: If you love `sequtils.toSeq` we have bad news for you. This -## library doesn't work with it due to documented compiler limitations. As -## a workaround, use this: +## .. warning:: If you love `sequtils.toSeq` we have bad news for you. This +## library doesn't work with it due to documented compiler limitations. As +## a workaround, use this: runnableExamples: # either `import std/nre except toSeq` or fully qualify `sequtils.toSeq`: import std/sequtils iterator iota(n: int): int = for i in 0.. Date: Sat, 24 Dec 2022 05:20:51 +1100 Subject: [PATCH 3614/3768] Make search results be relative to documentation root (#21145) Use Nim instead of JS for searching --- tools/dochack/dochack.nim | 48 ++++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/tools/dochack/dochack.nim b/tools/dochack/dochack.nim index 4067d2ed4..0753248bc 100644 --- a/tools/dochack/dochack.nim +++ b/tools/dochack/dochack.nim @@ -1,5 +1,6 @@ import dom import fuzzysearch +import std/[jsfetch, asyncjs] proc setTheme(theme: cstring) {.exportc.} = @@ -252,21 +253,7 @@ proc escapeCString(x: var cstring) = proc dosearch(value: cstring): Element = if db.len == 0: - var stuff: Element - {.emit: """ - var request = new XMLHttpRequest(); - request.open("GET", document.getElementById("indexLink").href, false); - request.send(null); - - var doc = document.implementation.createHTMLDocument("theindex"); - doc.documentElement.innerHTML = request.responseText; - - `stuff` = doc.documentElement; - """.} - db = stuff.getElementsByClass"reference" - contents = @[] - for ahref in db: - contents.add ahref.getAttribute("data-doc-search-tag") + return let ul = tree("UL") result = tree("DIV") result.setClass"search_results" @@ -293,8 +280,28 @@ proc dosearch(value: cstring): Element = result.add tree("B", text"search results") result.add ul -var oldtoc: Element -var timer: Timeout +proc loadIndex() {.async.} = + ## Loads theindex.html to enable searching + let + indexURL = document.getElementById("indexLink").getAttribute("href") + # Get root of project documentation by cutting off theindex.html from index href + rootURL = ($indexURL)[0 ..< ^"theindex.html".len] + var resp = fetch(indexURL).await().text().await() + # Convert into element so we can use DOM functions to parse the html + var indexElem = document.createElement("div") + indexElem.innerHtml = resp + # Add items into the DB/contents + for href in indexElem.getElementsByClass("reference"): + # Make links be relative to project root instead of current page + href.setAttr("href", cstring(rootURL & $href.getAttribute("href"))) + db &= href + contents &= href.getAttribute("data-doc-search-tag") + + +var + oldtoc: Element + timer: Timeout + loadIndexFut: Future[void] = nil proc search*() {.exportc.} = proc wrapper() = @@ -307,7 +314,12 @@ proc search*() {.exportc.} = replaceById("tocRoot", results) elif not oldtoc.isNil: replaceById("tocRoot", oldtoc) - + # Start loading index as soon as user starts typing. + # Will only be loaded the once anyways + if loadIndexFut == nil: + loadIndexFut = loadIndex() + # Run wrapper once loaded so we don't miss the users query + discard loadIndexFut.then(wrapper) if timer != nil: clearTimeout(timer) timer = setTimeout(wrapper, 400) From 9323cb7b2a761543df9d875c05f4963c1a8b050f Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Sat, 24 Dec 2022 17:37:32 +0800 Subject: [PATCH 3615/3768] enforce void for nkWhileStmt [backport: 2.0] (#21170) enforce void for nkWhileStmt --- compiler/semstmts.nim | 1 - tests/discard/tdiscardable.nim | 11 +++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 356c2d63e..96883255c 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -105,7 +105,6 @@ proc semWhile(c: PContext, n: PNode; flags: TExprFlags): PNode = result.typ = n[1].typ elif implicitlyDiscardable(n[1]): result[1].typ = c.enforceVoidContext - result.typ = c.enforceVoidContext proc semProc(c: PContext, n: PNode): PNode diff --git a/tests/discard/tdiscardable.nim b/tests/discard/tdiscardable.nim index b13130a13..69cb9f6a1 100644 --- a/tests/discard/tdiscardable.nim +++ b/tests/discard/tdiscardable.nim @@ -99,3 +99,14 @@ block: # bug #13583 let t = test doAssert t() == 12 +block: + proc bar(): string {.discardable.} = + "15" + + proc foo(): int = + while true: + raise newException(ValueError, "check") + 12 + + doAssertRaises(ValueError): + doAssert foo() == 12 From b08c50bb553364eec1e20ab788d990b63f4548e0 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 26 Dec 2022 13:52:40 +0800 Subject: [PATCH 3616/3768] sync some changes from release notes (#21173) --- changelogs/changelog_2_0_0.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/changelogs/changelog_2_0_0.md b/changelogs/changelog_2_0_0.md index a899bb929..614d97d85 100644 --- a/changelogs/changelog_2_0_0.md +++ b/changelogs/changelog_2_0_0.md @@ -2,7 +2,7 @@ ## Changes affecting backward compatibility -- `httpclient.contentLength` default to `-1` if the Content-Length header is not set in the response, it followed Apache HttpClient(Java), http(go) and .Net HttpWebResponse(C#) behavior. Previously it raised `ValueError`. +- `httpclient.contentLength` default to `-1` if the Content-Length header is not set in the response. It follows Apache HttpClient(Java), http(go) and .Net HttpWebResponse(C#) behavior. Previously it raised `ValueError`. - `addr` is now available for all addressable locations, `unsafeAddr` is now deprecated and an alias for `addr`. @@ -65,7 +65,7 @@ - `shallowCopy` and `shallow` are removed for ARC/ORC. Use `move` when possible or combine assignment and `sink` for optimization purposes. -- The `nimPreviewDotLikeOps` define is going to be removed or deprecated. +- The experimental `nimPreviewDotLikeOps` switch is going to be removed or deprecated because it didn't fullfill its promises. - The `{.this.}` pragma, deprecated since 0.19, has been removed. - `nil` literals can no longer be directly assigned to variables or fields of `distinct` pointer types. They must be converted instead. @@ -110,8 +110,6 @@ - `logging` will default to flushing all log level messages. To get the legacy behaviour of only flushing Error and Fatal messages, use `-d:nimV1LogFlushBehavior`. -- Object fields now support default values, see https://nim-lang.github.io/Nim/manual.html#types-default-values-for-object-fields for details. - - Redefining templates with the same signature was previously allowed to support certain macro code. To do this explicitly, the `{.redefine.}` pragma has been added. Note that this is only for templates. From f7c203fb6c89b5cef83c4f326aeb23ef8c4a2c40 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 26 Dec 2022 20:20:05 +0800 Subject: [PATCH 3617/3768] remove legacy code (#21134) * remove legacy code * fixes --- compiler/astalgo.nim | 3 - compiler/cgen.nim | 2 - compiler/condsyms.nim | 47 +-- compiler/depends.nim | 3 - compiler/renderer.nim | 7 +- compiler/sem.nim | 3 - compiler/sigmatch.nim | 2 - compiler/vm.nim | 5 - compiler/vmops.nim | 72 +++-- lib/core/macros.nim | 9 +- lib/pure/collections/lists.nim | 3 - lib/pure/hashes.nim | 40 +-- lib/pure/json.nim | 540 ++++++++++++++++---------------- lib/pure/nimprof.nim | 3 +- lib/std/assertions.nim | 2 - lib/std/jsonutils.nim | 18 +- lib/std/private/miscdollars.nim | 16 +- lib/system.nim | 35 +-- lib/system/alloc.nim | 10 +- lib/system/arithm.nim | 425 ------------------------- lib/system/arithmetics.nim | 140 +++------ lib/system/compilation.nim | 40 +-- lib/system/fatal.nim | 5 +- lib/system/inclrtl.nim | 3 +- lib/system/iterators.nim | 2 +- lib/system/jssys.nim | 22 -- lib/system/strmantle.nim | 7 +- 27 files changed, 432 insertions(+), 1032 deletions(-) delete mode 100644 lib/system/arithm.nim diff --git a/compiler/astalgo.nim b/compiler/astalgo.nim index e9ed1497a..4e09fab02 100644 --- a/compiler/astalgo.nim +++ b/compiler/astalgo.nim @@ -20,9 +20,6 @@ import strutils except addf when defined(nimPreviewSlimSystem): import std/assertions -when not defined(nimHasCursor): - {.pragma: cursor.} - proc hashNode*(p: RootRef): Hash proc treeToYaml*(conf: ConfigRef; n: PNode, indent: int = 0, maxRecDepth: int = - 1): Rope # Convert a tree into its YAML representation; this is used by the diff --git a/compiler/cgen.nim b/compiler/cgen.nim index e85cfd773..f6457f1e0 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -1929,8 +1929,6 @@ template injectG() {.dirty.} = graph.backend = newModuleList(graph) let g = BModuleList(graph.backend) -when not defined(nimHasSinkInference): - {.pragma: nosinks.} proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nosinks.} = injectG() diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index 24c6d82d5..c05aaf10b 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -84,10 +84,24 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasSignatureHashInMacro") # deadcode defineSymbol("nimHasDefault") # deadcode defineSymbol("nimMacrosSizealignof") # deadcode + defineSymbol("nimNoZeroExtendMagic") # deadcode + defineSymbol("nimMacrosGetNodeId") # deadcode + defineSymbol("nimFixedForwardGeneric") # deadcode + defineSymbol("nimToOpenArrayCString") # deadcode + defineSymbol("nimHasUsed") # deadcode + defineSymbol("nimnomagic64") # deadcode + defineSymbol("nimNewShiftOps") # deadcode + defineSymbol("nimHasCursor") # deadcode + defineSymbol("nimAlignPragma") # deadcode + defineSymbol("nimHasExceptionsQuery") # deadcode + defineSymbol("nimHasIsNamedTuple") # deadcode + defineSymbol("nimHashOrdinalFixed") # deadcode + defineSymbol("nimHasSinkInference") # deadcode + defineSymbol("nimNewIntegerOps") # deadcode + defineSymbol("nimHasInvariant") # deadcode + + - # > 0.20.0 - defineSymbol("nimNoZeroExtendMagic") - defineSymbol("nimMacrosGetNodeId") for f in Feature: defineSymbol("nimHas" & $f) @@ -98,31 +112,18 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimFixedOwned") defineSymbol("nimHasStyleChecks") - defineSymbol("nimToOpenArrayCString") - defineSymbol("nimHasUsed") - defineSymbol("nimFixedForwardGeneric") - defineSymbol("nimnomagic64") - defineSymbol("nimNewShiftOps") - defineSymbol("nimHasCursor") - defineSymbol("nimAlignPragma") - defineSymbol("nimHasExceptionsQuery") - defineSymbol("nimHasIsNamedTuple") - defineSymbol("nimHashOrdinalFixed") when defined(nimHasLibFFI): # Renaming as we can't conflate input vs output define flags; e.g. this # will report the right thing regardless of whether user adds # `-d:nimHasLibFFI` in his user config. - defineSymbol("nimHasLibFFIEnabled") + defineSymbol("nimHasLibFFIEnabled") # deadcode - defineSymbol("nimHasSinkInference") - defineSymbol("nimNewIntegerOps") - defineSymbol("nimHasInvariant") - defineSymbol("nimHasStacktraceMsgs") + defineSymbol("nimHasStacktraceMsgs") # deadcode defineSymbol("nimDoesntTrackDefects") - defineSymbol("nimHasLentIterators") - defineSymbol("nimHasDeclaredMagic") - defineSymbol("nimHasStacktracesModule") + defineSymbol("nimHasLentIterators") # deadcode + defineSymbol("nimHasDeclaredMagic") # deadcode + defineSymbol("nimHasStacktracesModule") # deadcode defineSymbol("nimHasEffectTraitsModule") defineSymbol("nimHasCastPragmaBlocks") defineSymbol("nimHasDeclaredLocs") @@ -133,8 +134,8 @@ proc initDefines*(symbols: StringTableRef) = defineSymbol("nimHasCustomLiterals") defineSymbol("nimHasUnifiedTuple") defineSymbol("nimHasIterable") - defineSymbol("nimHasTypeofVoid") - defineSymbol("nimHasDragonBox") + defineSymbol("nimHasTypeofVoid") # deadcode + defineSymbol("nimHasDragonBox") # deadcode defineSymbol("nimHasHintAll") defineSymbol("nimHasTrace") defineSymbol("nimHasEffectsOf") diff --git a/compiler/depends.nim b/compiler/depends.nim index 93f34dc7c..59cdc0330 100644 --- a/compiler/depends.nim +++ b/compiler/depends.nim @@ -100,9 +100,6 @@ proc generateDot*(graph: ModuleGraph; project: AbsoluteFile) = rope(project.splitFile.name), b.dotGraph], changeFileExt(project, "dot")) -when not defined(nimHasSinkInference): - {.pragma: nosinks.} - proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nosinks.} = var g: PGen new(g) diff --git a/compiler/renderer.nim b/compiler/renderer.nim index cd0d83eb0..87a5e6580 100644 --- a/compiler/renderer.nim +++ b/compiler/renderer.nim @@ -9,10 +9,9 @@ # This module implements the renderer of the standard Nim representation. -when defined(nimHasUsed): - # 'import renderer' is so useful for debugging - # that Nim shouldn't produce a warning for that: - {.used.} +# 'import renderer' is so useful for debugging +# that Nim shouldn't produce a warning for that: +{.used.} import lexer, options, idents, strutils, ast, msgs, lineinfos diff --git a/compiler/sem.nim b/compiler/sem.nim index 1f8e217b3..f5283fa3d 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -405,9 +405,6 @@ proc semExprFlagDispatched(c: PContext, n: PNode, flags: TExprFlags; expectedTyp evaluated = evalAtCompileTime(c, result) if evaluated != nil: return evaluated -when not defined(nimHasSinkInference): - {.pragma: nosinks.} - include hlo, seminst, semcall proc resetSemFlag(n: PNode) = diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 61a4395f3..d05503c00 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -2661,8 +2661,6 @@ proc argtypeMatches*(c: PContext, f, a: PType, fromHlo = false): bool = # pattern templates do not allow for conversions except from int literal res != nil and m.convMatches == 0 and m.intConvMatches in [0, 256] -when not defined(nimHasSinkInference): - {.pragma: nosinks.} proc instTypeBoundOp*(c: PContext; dc: PSym; t: PType; info: TLineInfo; op: TTypeAttachedOp; col: int): PSym {.nosinks.} = diff --git a/compiler/vm.nim b/compiler/vm.nim index 61261c44c..d87fa33ba 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -32,8 +32,6 @@ const when hasFFI: import evalffi -when not defined(nimHasCursor): - {.pragma: cursor.} proc stackTraceAux(c: PCtx; x: PStackFrame; pc: int; recursionLimit=100) = if x != nil: @@ -533,9 +531,6 @@ template maybeHandlePtr(node2: PNode, reg: TFullReg, isAssign2: bool): bool = else: false -when not defined(nimHasSinkInference): - {.pragma: nosinks.} - template takeAddress(reg, source) = reg.nodeAddr = addr source GC_ref source diff --git a/compiler/vmops.nim b/compiler/vmops.nim index d4a91b22d..bb943b8a0 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -141,41 +141,40 @@ proc staticWalkDirImpl(path: string, relative: bool): PNode = for k, f in walkDir(path, relative): result.add toLit((k, f)) -when defined(nimHasInvariant): - from std / compilesettings import SingleValueSetting, MultipleValueSetting +from std / compilesettings import SingleValueSetting, MultipleValueSetting - proc querySettingImpl(conf: ConfigRef, switch: BiggestInt): string = - {.push warning[Deprecated]:off.} - case SingleValueSetting(switch) - of arguments: result = conf.arguments - of outFile: result = conf.outFile.string - of outDir: result = conf.outDir.string - of nimcacheDir: result = conf.getNimcacheDir().string - of projectName: result = conf.projectName - of projectPath: result = conf.projectPath.string - of projectFull: result = conf.projectFull.string - of command: result = conf.command - of commandLine: result = conf.commandLine - of linkOptions: result = conf.linkOptions - of compileOptions: result = conf.compileOptions - of ccompilerPath: result = conf.cCompilerPath - of backend: result = $conf.backend - of libPath: result = conf.libpath.string - of gc: result = $conf.selectedGC - of mm: result = $conf.selectedGC - {.pop.} +proc querySettingImpl(conf: ConfigRef, switch: BiggestInt): string = + {.push warning[Deprecated]:off.} + case SingleValueSetting(switch) + of arguments: result = conf.arguments + of outFile: result = conf.outFile.string + of outDir: result = conf.outDir.string + of nimcacheDir: result = conf.getNimcacheDir().string + of projectName: result = conf.projectName + of projectPath: result = conf.projectPath.string + of projectFull: result = conf.projectFull.string + of command: result = conf.command + of commandLine: result = conf.commandLine + of linkOptions: result = conf.linkOptions + of compileOptions: result = conf.compileOptions + of ccompilerPath: result = conf.cCompilerPath + of backend: result = $conf.backend + of libPath: result = conf.libpath.string + of gc: result = $conf.selectedGC + of mm: result = $conf.selectedGC + {.pop.} - proc querySettingSeqImpl(conf: ConfigRef, switch: BiggestInt): seq[string] = - template copySeq(field: untyped): untyped = - for i in field: result.add i.string +proc querySettingSeqImpl(conf: ConfigRef, switch: BiggestInt): seq[string] = + template copySeq(field: untyped): untyped = + for i in field: result.add i.string - case MultipleValueSetting(switch) - of nimblePaths: copySeq(conf.nimblePaths) - of searchPaths: copySeq(conf.searchPaths) - of lazyPaths: copySeq(conf.lazyPaths) - of commandArgs: result = conf.commandArgs - of cincludes: copySeq(conf.cIncludes) - of clibs: copySeq(conf.cLibs) + case MultipleValueSetting(switch) + of nimblePaths: copySeq(conf.nimblePaths) + of searchPaths: copySeq(conf.searchPaths) + of lazyPaths: copySeq(conf.lazyPaths) + of commandArgs: result = conf.commandArgs + of cincludes: copySeq(conf.cIncludes) + of clibs: copySeq(conf.cLibs) proc stackTrace2(c: PCtx, msg: string, n: PNode) = stackTrace(c, PStackFrame(prc: c.prc.sym, comesFrom: 0, next: nil), c.exceptionInstr, msg, n.info) @@ -257,11 +256,10 @@ proc registerAdditionalOps*(c: PCtx) = systemop getCurrentException registerCallback c, "stdlib.*.staticWalkDir", proc (a: VmArgs) {.nimcall.} = setResult(a, staticWalkDirImpl(getString(a, 0), getBool(a, 1))) - when defined(nimHasInvariant): - registerCallback c, "stdlib.compilesettings.querySetting", proc (a: VmArgs) = - setResult(a, querySettingImpl(c.config, getInt(a, 0))) - registerCallback c, "stdlib.compilesettings.querySettingSeq", proc (a: VmArgs) = - setResult(a, querySettingSeqImpl(c.config, getInt(a, 0))) + registerCallback c, "stdlib.compilesettings.querySetting", proc (a: VmArgs) = + setResult(a, querySettingImpl(c.config, getInt(a, 0))) + registerCallback c, "stdlib.compilesettings.querySettingSeq", proc (a: VmArgs) = + setResult(a, querySettingSeqImpl(c.config, getInt(a, 0))) if defined(nimsuggest) or c.config.cmd == cmdCheck: discard "don't run staticExec for 'nim suggest'" diff --git a/lib/core/macros.nim b/lib/core/macros.nim index 138ec47a0..bcc16038b 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -1519,11 +1519,10 @@ proc boolVal*(n: NimNode): bool {.noSideEffect.} = if n.kind == nnkIntLit: n.intVal != 0 else: n == bindSym"true" # hacky solution for now -when defined(nimMacrosGetNodeId): - proc nodeID*(n: NimNode): int {.magic: "NodeId".} - ## Returns the id of `n`, when the compiler has been compiled - ## with the flag `-d:useNodeids`, otherwise returns `-1`. This - ## proc is for the purpose to debug the compiler only. +proc nodeID*(n: NimNode): int {.magic: "NodeId".} + ## Returns the id of `n`, when the compiler has been compiled + ## with the flag `-d:useNodeids`, otherwise returns `-1`. This + ## proc is for the purpose to debug the compiler only. macro expandMacros*(body: typed): untyped = ## Expands one level of macro - useful for debugging. diff --git a/lib/pure/collections/lists.nim b/lib/pure/collections/lists.nim index 829ec2ccb..a08c62964 100644 --- a/lib/pure/collections/lists.nim +++ b/lib/pure/collections/lists.nim @@ -62,9 +62,6 @@ import std/private/since when defined(nimPreviewSlimSystem): import std/assertions -when not defined(nimHasCursor): - {.pragma: cursor.} - type DoublyLinkedNodeObj*[T] = object ## A node of a doubly linked list. diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim index e88210757..09e072812 100644 --- a/lib/pure/hashes.nim +++ b/lib/pure/hashes.nim @@ -380,16 +380,10 @@ proc hash*(x: string): Hash = runnableExamples: doAssert hash("abracadabra") != hash("AbracadabrA") - when not defined(nimToOpenArrayCString): - result = 0 - for c in x: - result = result !& ord(c) - result = !$result + when nimvm: + result = hashVmImpl(x, 0, high(x)) else: - when nimvm: - result = hashVmImpl(x, 0, high(x)) - else: - result = murmurHash(toOpenArrayByte(x, 0, high(x))) + result = murmurHash(toOpenArrayByte(x, 0, high(x))) proc hash*(x: cstring): Hash = ## Efficient hashing of null-terminated strings. @@ -398,22 +392,14 @@ proc hash*(x: cstring): Hash = doAssert hash(cstring"AbracadabrA") == hash("AbracadabrA") doAssert hash(cstring"abracadabra") != hash(cstring"AbracadabrA") - when not defined(nimToOpenArrayCString): - result = 0 - var i = 0 - while x[i] != '\0': - result = result !& ord(x[i]) - inc i - result = !$result + when nimvm: + hashVmImpl(x, 0, high(x)) else: - when nimvm: - hashVmImpl(x, 0, high(x)) + when not defined(js): + murmurHash(toOpenArrayByte(x, 0, x.high)) else: - when not defined(js) and defined(nimToOpenArrayCString): - murmurHash(toOpenArrayByte(x, 0, x.high)) - else: - let xx = $x - murmurHash(toOpenArrayByte(xx, 0, high(xx))) + let xx = $x + murmurHash(toOpenArrayByte(xx, 0, high(xx))) proc hash*(sBuf: string, sPos, ePos: int): Hash = ## Efficient hashing of a string buffer, from starting @@ -424,13 +410,7 @@ proc hash*(sBuf: string, sPos, ePos: int): Hash = var a = "abracadabra" doAssert hash(a, 0, 3) == hash(a, 7, 10) - when not defined(nimToOpenArrayCString): - result = 0 - for i in sPos..ePos: - result = result !& ord(sBuf[i]) - result = !$result - else: - murmurHash(toOpenArrayByte(sBuf, sPos, ePos)) + murmurHash(toOpenArrayByte(sBuf, sPos, ePos)) proc hashIgnoreStyle*(x: string): Hash = ## Efficient hashing of strings; style is ignored. diff --git a/lib/pure/json.nim b/lib/pure/json.nim index d0b1a4051..78d6bd5bd 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -1059,310 +1059,308 @@ template verifyJsonKind(node: JsonNode, kinds: set[JsonNodeKind], ] raise newException(JsonKindError, msg) -when defined(nimFixedForwardGeneric): +macro isRefSkipDistinct*(arg: typed): untyped = + ## internal only, do not use + var impl = getTypeImpl(arg) + if impl.kind == nnkBracketExpr and impl[0].eqIdent("typeDesc"): + impl = getTypeImpl(impl[1]) + while impl.kind == nnkDistinctTy: + impl = getTypeImpl(impl[0]) + result = newLit(impl.kind == nnkRefTy) - macro isRefSkipDistinct*(arg: typed): untyped = - ## internal only, do not use - var impl = getTypeImpl(arg) - if impl.kind == nnkBracketExpr and impl[0].eqIdent("typeDesc"): - impl = getTypeImpl(impl[1]) - while impl.kind == nnkDistinctTy: - impl = getTypeImpl(impl[0]) - result = newLit(impl.kind == nnkRefTy) +# The following forward declarations don't work in older versions of Nim - # The following forward declarations don't work in older versions of Nim +# forward declare all initFromJson - # forward declare all initFromJson +proc initFromJson(dst: var string; jsonNode: JsonNode; jsonPath: var string) +proc initFromJson(dst: var bool; jsonNode: JsonNode; jsonPath: var string) +proc initFromJson(dst: var JsonNode; jsonNode: JsonNode; jsonPath: var string) +proc initFromJson[T: SomeInteger](dst: var T; jsonNode: JsonNode, jsonPath: var string) +proc initFromJson[T: SomeFloat](dst: var T; jsonNode: JsonNode; jsonPath: var string) +proc initFromJson[T: enum](dst: var T; jsonNode: JsonNode; jsonPath: var string) +proc initFromJson[T](dst: var seq[T]; jsonNode: JsonNode; jsonPath: var string) +proc initFromJson[S, T](dst: var array[S, T]; jsonNode: JsonNode; jsonPath: var string) +proc initFromJson[T](dst: var Table[string, T]; jsonNode: JsonNode; jsonPath: var string) +proc initFromJson[T](dst: var OrderedTable[string, T]; jsonNode: JsonNode; jsonPath: var string) +proc initFromJson[T](dst: var ref T; jsonNode: JsonNode; jsonPath: var string) +proc initFromJson[T](dst: var Option[T]; jsonNode: JsonNode; jsonPath: var string) +proc initFromJson[T: distinct](dst: var T; jsonNode: JsonNode; jsonPath: var string) +proc initFromJson[T: object|tuple](dst: var T; jsonNode: JsonNode; jsonPath: var string) - proc initFromJson(dst: var string; jsonNode: JsonNode; jsonPath: var string) - proc initFromJson(dst: var bool; jsonNode: JsonNode; jsonPath: var string) - proc initFromJson(dst: var JsonNode; jsonNode: JsonNode; jsonPath: var string) - proc initFromJson[T: SomeInteger](dst: var T; jsonNode: JsonNode, jsonPath: var string) - proc initFromJson[T: SomeFloat](dst: var T; jsonNode: JsonNode; jsonPath: var string) - proc initFromJson[T: enum](dst: var T; jsonNode: JsonNode; jsonPath: var string) - proc initFromJson[T](dst: var seq[T]; jsonNode: JsonNode; jsonPath: var string) - proc initFromJson[S, T](dst: var array[S, T]; jsonNode: JsonNode; jsonPath: var string) - proc initFromJson[T](dst: var Table[string, T]; jsonNode: JsonNode; jsonPath: var string) - proc initFromJson[T](dst: var OrderedTable[string, T]; jsonNode: JsonNode; jsonPath: var string) - proc initFromJson[T](dst: var ref T; jsonNode: JsonNode; jsonPath: var string) - proc initFromJson[T](dst: var Option[T]; jsonNode: JsonNode; jsonPath: var string) - proc initFromJson[T: distinct](dst: var T; jsonNode: JsonNode; jsonPath: var string) - proc initFromJson[T: object|tuple](dst: var T; jsonNode: JsonNode; jsonPath: var string) +# initFromJson definitions - # initFromJson definitions +proc initFromJson(dst: var string; jsonNode: JsonNode; jsonPath: var string) = + verifyJsonKind(jsonNode, {JString, JNull}, jsonPath) + # since strings don't have a nil state anymore, this mapping of + # JNull to the default string is questionable. `none(string)` and + # `some("")` have the same potentional json value `JNull`. + if jsonNode.kind == JNull: + dst = "" + else: + dst = jsonNode.str - proc initFromJson(dst: var string; jsonNode: JsonNode; jsonPath: var string) = - verifyJsonKind(jsonNode, {JString, JNull}, jsonPath) - # since strings don't have a nil state anymore, this mapping of - # JNull to the default string is questionable. `none(string)` and - # `some("")` have the same potentional json value `JNull`. - if jsonNode.kind == JNull: - dst = "" +proc initFromJson(dst: var bool; jsonNode: JsonNode; jsonPath: var string) = + verifyJsonKind(jsonNode, {JBool}, jsonPath) + dst = jsonNode.bval + +proc initFromJson(dst: var JsonNode; jsonNode: JsonNode; jsonPath: var string) = + if jsonNode == nil: + raise newException(KeyError, "key not found: " & jsonPath) + dst = jsonNode.copy + +proc initFromJson[T: SomeInteger](dst: var T; jsonNode: JsonNode, jsonPath: var string) = + when T is uint|uint64 or (not defined(js) and int.sizeof == 4): + verifyJsonKind(jsonNode, {JInt, JString}, jsonPath) + case jsonNode.kind + of JString: + let x = parseBiggestUInt(jsonNode.str) + dst = cast[T](x) else: - dst = jsonNode.str + dst = T(jsonNode.num) + else: + verifyJsonKind(jsonNode, {JInt}, jsonPath) + dst = cast[T](jsonNode.num) - proc initFromJson(dst: var bool; jsonNode: JsonNode; jsonPath: var string) = - verifyJsonKind(jsonNode, {JBool}, jsonPath) - dst = jsonNode.bval - - proc initFromJson(dst: var JsonNode; jsonNode: JsonNode; jsonPath: var string) = - if jsonNode == nil: - raise newException(KeyError, "key not found: " & jsonPath) - dst = jsonNode.copy - - proc initFromJson[T: SomeInteger](dst: var T; jsonNode: JsonNode, jsonPath: var string) = - when T is uint|uint64 or (not defined(js) and int.sizeof == 4): - verifyJsonKind(jsonNode, {JInt, JString}, jsonPath) - case jsonNode.kind - of JString: - let x = parseBiggestUInt(jsonNode.str) - dst = cast[T](x) - else: - dst = T(jsonNode.num) +proc initFromJson[T: SomeFloat](dst: var T; jsonNode: JsonNode; jsonPath: var string) = + if jsonNode.kind == JString: + case jsonNode.str + of "nan": + let b = NaN + dst = T(b) + # dst = NaN # would fail some tests because range conversions would cause CT error + # in some cases; but this is not a hot-spot inside this branch and backend can optimize this. + of "inf": + let b = Inf + dst = T(b) + of "-inf": + let b = -Inf + dst = T(b) + else: raise newException(JsonKindError, "expected 'nan|inf|-inf', got " & jsonNode.str) + else: + verifyJsonKind(jsonNode, {JInt, JFloat}, jsonPath) + if jsonNode.kind == JFloat: + dst = T(jsonNode.fnum) else: - verifyJsonKind(jsonNode, {JInt}, jsonPath) - dst = cast[T](jsonNode.num) + dst = T(jsonNode.num) - proc initFromJson[T: SomeFloat](dst: var T; jsonNode: JsonNode; jsonPath: var string) = - if jsonNode.kind == JString: - case jsonNode.str - of "nan": - let b = NaN - dst = T(b) - # dst = NaN # would fail some tests because range conversions would cause CT error - # in some cases; but this is not a hot-spot inside this branch and backend can optimize this. - of "inf": - let b = Inf - dst = T(b) - of "-inf": - let b = -Inf - dst = T(b) - else: raise newException(JsonKindError, "expected 'nan|inf|-inf', got " & jsonNode.str) +proc initFromJson[T: enum](dst: var T; jsonNode: JsonNode; jsonPath: var string) = + verifyJsonKind(jsonNode, {JString}, jsonPath) + dst = parseEnum[T](jsonNode.getStr) + +proc initFromJson[T](dst: var seq[T]; jsonNode: JsonNode; jsonPath: var string) = + verifyJsonKind(jsonNode, {JArray}, jsonPath) + dst.setLen jsonNode.len + let orignalJsonPathLen = jsonPath.len + for i in 0 ..< jsonNode.len: + jsonPath.add '[' + jsonPath.addInt i + jsonPath.add ']' + initFromJson(dst[i], jsonNode[i], jsonPath) + jsonPath.setLen orignalJsonPathLen + +proc initFromJson[S,T](dst: var array[S,T]; jsonNode: JsonNode; jsonPath: var string) = + verifyJsonKind(jsonNode, {JArray}, jsonPath) + let originalJsonPathLen = jsonPath.len + for i in 0 ..< jsonNode.len: + jsonPath.add '[' + jsonPath.addInt i + jsonPath.add ']' + initFromJson(dst[i.S], jsonNode[i], jsonPath) # `.S` for enum indexed arrays + jsonPath.setLen originalJsonPathLen + +proc initFromJson[T](dst: var Table[string,T]; jsonNode: JsonNode; jsonPath: var string) = + dst = initTable[string, T]() + verifyJsonKind(jsonNode, {JObject}, jsonPath) + let originalJsonPathLen = jsonPath.len + for key in keys(jsonNode.fields): + jsonPath.add '.' + jsonPath.add key + initFromJson(mgetOrPut(dst, key, default(T)), jsonNode[key], jsonPath) + jsonPath.setLen originalJsonPathLen + +proc initFromJson[T](dst: var OrderedTable[string,T]; jsonNode: JsonNode; jsonPath: var string) = + dst = initOrderedTable[string,T]() + verifyJsonKind(jsonNode, {JObject}, jsonPath) + let originalJsonPathLen = jsonPath.len + for key in keys(jsonNode.fields): + jsonPath.add '.' + jsonPath.add key + initFromJson(mgetOrPut(dst, key, default(T)), jsonNode[key], jsonPath) + jsonPath.setLen originalJsonPathLen + +proc initFromJson[T](dst: var ref T; jsonNode: JsonNode; jsonPath: var string) = + verifyJsonKind(jsonNode, {JObject, JNull}, jsonPath) + if jsonNode.kind == JNull: + dst = nil + else: + dst = new(T) + initFromJson(dst[], jsonNode, jsonPath) + +proc initFromJson[T](dst: var Option[T]; jsonNode: JsonNode; jsonPath: var string) = + if jsonNode != nil and jsonNode.kind != JNull: + when T is ref: + dst = some(new(T)) else: - verifyJsonKind(jsonNode, {JInt, JFloat}, jsonPath) - if jsonNode.kind == JFloat: - dst = T(jsonNode.fnum) - else: - dst = T(jsonNode.num) + dst = some(default(T)) + initFromJson(dst.get, jsonNode, jsonPath) - proc initFromJson[T: enum](dst: var T; jsonNode: JsonNode; jsonPath: var string) = - verifyJsonKind(jsonNode, {JString}, jsonPath) - dst = parseEnum[T](jsonNode.getStr) +macro assignDistinctImpl[T: distinct](dst: var T;jsonNode: JsonNode; jsonPath: var string) = + let typInst = getTypeInst(dst) + let typImpl = getTypeImpl(dst) + let baseTyp = typImpl[0] - proc initFromJson[T](dst: var seq[T]; jsonNode: JsonNode; jsonPath: var string) = - verifyJsonKind(jsonNode, {JArray}, jsonPath) - dst.setLen jsonNode.len - let orignalJsonPathLen = jsonPath.len - for i in 0 ..< jsonNode.len: - jsonPath.add '[' - jsonPath.addInt i - jsonPath.add ']' - initFromJson(dst[i], jsonNode[i], jsonPath) - jsonPath.setLen orignalJsonPathLen - - proc initFromJson[S,T](dst: var array[S,T]; jsonNode: JsonNode; jsonPath: var string) = - verifyJsonKind(jsonNode, {JArray}, jsonPath) - let originalJsonPathLen = jsonPath.len - for i in 0 ..< jsonNode.len: - jsonPath.add '[' - jsonPath.addInt i - jsonPath.add ']' - initFromJson(dst[i.S], jsonNode[i], jsonPath) # `.S` for enum indexed arrays - jsonPath.setLen originalJsonPathLen - - proc initFromJson[T](dst: var Table[string,T]; jsonNode: JsonNode; jsonPath: var string) = - dst = initTable[string, T]() - verifyJsonKind(jsonNode, {JObject}, jsonPath) - let originalJsonPathLen = jsonPath.len - for key in keys(jsonNode.fields): - jsonPath.add '.' - jsonPath.add key - initFromJson(mgetOrPut(dst, key, default(T)), jsonNode[key], jsonPath) - jsonPath.setLen originalJsonPathLen - - proc initFromJson[T](dst: var OrderedTable[string,T]; jsonNode: JsonNode; jsonPath: var string) = - dst = initOrderedTable[string,T]() - verifyJsonKind(jsonNode, {JObject}, jsonPath) - let originalJsonPathLen = jsonPath.len - for key in keys(jsonNode.fields): - jsonPath.add '.' - jsonPath.add key - initFromJson(mgetOrPut(dst, key, default(T)), jsonNode[key], jsonPath) - jsonPath.setLen originalJsonPathLen - - proc initFromJson[T](dst: var ref T; jsonNode: JsonNode; jsonPath: var string) = - verifyJsonKind(jsonNode, {JObject, JNull}, jsonPath) - if jsonNode.kind == JNull: - dst = nil + result = quote do: + when nimvm: + # workaround #12282 + var tmp: `baseTyp` + initFromJson( tmp, `jsonNode`, `jsonPath`) + `dst` = `typInst`(tmp) else: - dst = new(T) - initFromJson(dst[], jsonNode, jsonPath) + initFromJson( `baseTyp`(`dst`), `jsonNode`, `jsonPath`) - proc initFromJson[T](dst: var Option[T]; jsonNode: JsonNode; jsonPath: var string) = - if jsonNode != nil and jsonNode.kind != JNull: - when T is ref: - dst = some(new(T)) - else: - dst = some(default(T)) - initFromJson(dst.get, jsonNode, jsonPath) +proc initFromJson[T: distinct](dst: var T; jsonNode: JsonNode; jsonPath: var string) = + assignDistinctImpl(dst, jsonNode, jsonPath) - macro assignDistinctImpl[T: distinct](dst: var T;jsonNode: JsonNode; jsonPath: var string) = - let typInst = getTypeInst(dst) - let typImpl = getTypeImpl(dst) - let baseTyp = typImpl[0] +proc detectIncompatibleType(typeExpr, lineinfoNode: NimNode) = + if typeExpr.kind == nnkTupleConstr: + error("Use a named tuple instead of: " & typeExpr.repr, lineinfoNode) - result = quote do: +proc foldObjectBody(dst, typeNode, tmpSym, jsonNode, jsonPath, originalJsonPathLen: NimNode) = + case typeNode.kind + of nnkEmpty: + discard + of nnkRecList, nnkTupleTy: + for it in typeNode: + foldObjectBody(dst, it, tmpSym, jsonNode, jsonPath, originalJsonPathLen) + + of nnkIdentDefs: + typeNode.expectLen 3 + let fieldSym = typeNode[0] + let fieldNameLit = newLit(fieldSym.strVal) + let fieldPathLit = newLit("." & fieldSym.strVal) + let fieldType = typeNode[1] + + # Detecting incompatiple tuple types in `assignObjectImpl` only + # would be much cleaner, but the ast for tuple types does not + # contain usable type information. + detectIncompatibleType(fieldType, fieldSym) + + dst.add quote do: + jsonPath.add `fieldPathLit` when nimvm: - # workaround #12282 - var tmp: `baseTyp` - initFromJson( tmp, `jsonNode`, `jsonPath`) - `dst` = `typInst`(tmp) - else: - initFromJson( `baseTyp`(`dst`), `jsonNode`, `jsonPath`) - - proc initFromJson[T: distinct](dst: var T; jsonNode: JsonNode; jsonPath: var string) = - assignDistinctImpl(dst, jsonNode, jsonPath) - - proc detectIncompatibleType(typeExpr, lineinfoNode: NimNode) = - if typeExpr.kind == nnkTupleConstr: - error("Use a named tuple instead of: " & typeExpr.repr, lineinfoNode) - - proc foldObjectBody(dst, typeNode, tmpSym, jsonNode, jsonPath, originalJsonPathLen: NimNode) = - case typeNode.kind - of nnkEmpty: - discard - of nnkRecList, nnkTupleTy: - for it in typeNode: - foldObjectBody(dst, it, tmpSym, jsonNode, jsonPath, originalJsonPathLen) - - of nnkIdentDefs: - typeNode.expectLen 3 - let fieldSym = typeNode[0] - let fieldNameLit = newLit(fieldSym.strVal) - let fieldPathLit = newLit("." & fieldSym.strVal) - let fieldType = typeNode[1] - - # Detecting incompatiple tuple types in `assignObjectImpl` only - # would be much cleaner, but the ast for tuple types does not - # contain usable type information. - detectIncompatibleType(fieldType, fieldSym) - - dst.add quote do: - jsonPath.add `fieldPathLit` - when nimvm: - when isRefSkipDistinct(`tmpSym`.`fieldSym`): - # workaround #12489 - var tmp: `fieldType` - initFromJson(tmp, getOrDefault(`jsonNode`,`fieldNameLit`), `jsonPath`) - `tmpSym`.`fieldSym` = tmp - else: - initFromJson(`tmpSym`.`fieldSym`, getOrDefault(`jsonNode`,`fieldNameLit`), `jsonPath`) + when isRefSkipDistinct(`tmpSym`.`fieldSym`): + # workaround #12489 + var tmp: `fieldType` + initFromJson(tmp, getOrDefault(`jsonNode`,`fieldNameLit`), `jsonPath`) + `tmpSym`.`fieldSym` = tmp else: initFromJson(`tmpSym`.`fieldSym`, getOrDefault(`jsonNode`,`fieldNameLit`), `jsonPath`) - jsonPath.setLen `originalJsonPathLen` + else: + initFromJson(`tmpSym`.`fieldSym`, getOrDefault(`jsonNode`,`fieldNameLit`), `jsonPath`) + jsonPath.setLen `originalJsonPathLen` - of nnkRecCase: - let kindSym = typeNode[0][0] - let kindNameLit = newLit(kindSym.strVal) - let kindPathLit = newLit("." & kindSym.strVal) - let kindType = typeNode[0][1] - let kindOffsetLit = newLit(uint(getOffset(kindSym))) - dst.add quote do: - var kindTmp: `kindType` - jsonPath.add `kindPathLit` - initFromJson(kindTmp, `jsonNode`[`kindNameLit`], `jsonPath`) - jsonPath.setLen `originalJsonPathLen` - when defined js: + of nnkRecCase: + let kindSym = typeNode[0][0] + let kindNameLit = newLit(kindSym.strVal) + let kindPathLit = newLit("." & kindSym.strVal) + let kindType = typeNode[0][1] + let kindOffsetLit = newLit(uint(getOffset(kindSym))) + dst.add quote do: + var kindTmp: `kindType` + jsonPath.add `kindPathLit` + initFromJson(kindTmp, `jsonNode`[`kindNameLit`], `jsonPath`) + jsonPath.setLen `originalJsonPathLen` + when defined js: + `tmpSym`.`kindSym` = kindTmp + else: + when nimvm: `tmpSym`.`kindSym` = kindTmp else: - when nimvm: - `tmpSym`.`kindSym` = kindTmp - else: - # fuck it, assign kind field anyway - ((cast[ptr `kindType`](cast[uint](`tmpSym`.addr) + `kindOffsetLit`))[]) = kindTmp - dst.add nnkCaseStmt.newTree(nnkDotExpr.newTree(tmpSym, kindSym)) - for i in 1 ..< typeNode.len: - foldObjectBody(dst, typeNode[i], tmpSym, jsonNode, jsonPath, originalJsonPathLen) + # fuck it, assign kind field anyway + ((cast[ptr `kindType`](cast[uint](`tmpSym`.addr) + `kindOffsetLit`))[]) = kindTmp + dst.add nnkCaseStmt.newTree(nnkDotExpr.newTree(tmpSym, kindSym)) + for i in 1 ..< typeNode.len: + foldObjectBody(dst, typeNode[i], tmpSym, jsonNode, jsonPath, originalJsonPathLen) - of nnkOfBranch, nnkElse: - let ofBranch = newNimNode(typeNode.kind) - for i in 0 ..< typeNode.len-1: - ofBranch.add copyNimTree(typeNode[i]) - let dstInner = newNimNode(nnkStmtListExpr) - foldObjectBody(dstInner, typeNode[^1], tmpSym, jsonNode, jsonPath, originalJsonPathLen) - # resOuter now contains the inner stmtList - ofBranch.add dstInner - dst[^1].expectKind nnkCaseStmt - dst[^1].add ofBranch + of nnkOfBranch, nnkElse: + let ofBranch = newNimNode(typeNode.kind) + for i in 0 ..< typeNode.len-1: + ofBranch.add copyNimTree(typeNode[i]) + let dstInner = newNimNode(nnkStmtListExpr) + foldObjectBody(dstInner, typeNode[^1], tmpSym, jsonNode, jsonPath, originalJsonPathLen) + # resOuter now contains the inner stmtList + ofBranch.add dstInner + dst[^1].expectKind nnkCaseStmt + dst[^1].add ofBranch - of nnkObjectTy: - typeNode[0].expectKind nnkEmpty - typeNode[1].expectKind {nnkEmpty, nnkOfInherit} - if typeNode[1].kind == nnkOfInherit: - let base = typeNode[1][0] - var impl = getTypeImpl(base) - while impl.kind in {nnkRefTy, nnkPtrTy}: - impl = getTypeImpl(impl[0]) - foldObjectBody(dst, impl, tmpSym, jsonNode, jsonPath, originalJsonPathLen) - let body = typeNode[2] - foldObjectBody(dst, body, tmpSym, jsonNode, jsonPath, originalJsonPathLen) + of nnkObjectTy: + typeNode[0].expectKind nnkEmpty + typeNode[1].expectKind {nnkEmpty, nnkOfInherit} + if typeNode[1].kind == nnkOfInherit: + let base = typeNode[1][0] + var impl = getTypeImpl(base) + while impl.kind in {nnkRefTy, nnkPtrTy}: + impl = getTypeImpl(impl[0]) + foldObjectBody(dst, impl, tmpSym, jsonNode, jsonPath, originalJsonPathLen) + let body = typeNode[2] + foldObjectBody(dst, body, tmpSym, jsonNode, jsonPath, originalJsonPathLen) - else: - error("unhandled kind: " & $typeNode.kind, typeNode) + else: + error("unhandled kind: " & $typeNode.kind, typeNode) - macro assignObjectImpl[T](dst: var T; jsonNode: JsonNode; jsonPath: var string) = - let typeSym = getTypeInst(dst) - let originalJsonPathLen = genSym(nskLet, "originalJsonPathLen") - result = newStmtList() - result.add quote do: - let `originalJsonPathLen` = len(`jsonPath`) - if typeSym.kind in {nnkTupleTy, nnkTupleConstr}: - # both, `dst` and `typeSym` don't have good lineinfo. But nothing - # else is available here. - detectIncompatibleType(typeSym, dst) - foldObjectBody(result, typeSym, dst, jsonNode, jsonPath, originalJsonPathLen) - else: - foldObjectBody(result, typeSym.getTypeImpl, dst, jsonNode, jsonPath, originalJsonPathLen) +macro assignObjectImpl[T](dst: var T; jsonNode: JsonNode; jsonPath: var string) = + let typeSym = getTypeInst(dst) + let originalJsonPathLen = genSym(nskLet, "originalJsonPathLen") + result = newStmtList() + result.add quote do: + let `originalJsonPathLen` = len(`jsonPath`) + if typeSym.kind in {nnkTupleTy, nnkTupleConstr}: + # both, `dst` and `typeSym` don't have good lineinfo. But nothing + # else is available here. + detectIncompatibleType(typeSym, dst) + foldObjectBody(result, typeSym, dst, jsonNode, jsonPath, originalJsonPathLen) + else: + foldObjectBody(result, typeSym.getTypeImpl, dst, jsonNode, jsonPath, originalJsonPathLen) - proc initFromJson[T: object|tuple](dst: var T; jsonNode: JsonNode; jsonPath: var string) = - assignObjectImpl(dst, jsonNode, jsonPath) +proc initFromJson[T: object|tuple](dst: var T; jsonNode: JsonNode; jsonPath: var string) = + assignObjectImpl(dst, jsonNode, jsonPath) - proc to*[T](node: JsonNode, t: typedesc[T]): T = - ## `Unmarshals`:idx: the specified node into the object type specified. - ## - ## Known limitations: - ## - ## * Heterogeneous arrays are not supported. - ## * Sets in object variants are not supported. - ## * Not nil annotations are not supported. - ## - runnableExamples: - let jsonNode = parseJson(""" - { - "person": { - "name": "Nimmer", - "age": 21 - }, - "list": [1, 2, 3, 4] - } - """) +proc to*[T](node: JsonNode, t: typedesc[T]): T = + ## `Unmarshals`:idx: the specified node into the object type specified. + ## + ## Known limitations: + ## + ## * Heterogeneous arrays are not supported. + ## * Sets in object variants are not supported. + ## * Not nil annotations are not supported. + ## + runnableExamples: + let jsonNode = parseJson(""" + { + "person": { + "name": "Nimmer", + "age": 21 + }, + "list": [1, 2, 3, 4] + } + """) - type - Person = object - name: string - age: int + type + Person = object + name: string + age: int - Data = object - person: Person - list: seq[int] + Data = object + person: Person + list: seq[int] - var data = to(jsonNode, Data) - doAssert data.person.name == "Nimmer" - doAssert data.person.age == 21 - doAssert data.list == @[1, 2, 3, 4] + var data = to(jsonNode, Data) + doAssert data.person.name == "Nimmer" + doAssert data.person.age == 21 + doAssert data.list == @[1, 2, 3, 4] - var jsonPath = "" - initFromJson(result, node, jsonPath) + var jsonPath = "" + initFromJson(result, node, jsonPath) when false: import os diff --git a/lib/pure/nimprof.nim b/lib/pure/nimprof.nim index 7f493418e..9849e42db 100644 --- a/lib/pure/nimprof.nim +++ b/lib/pure/nimprof.nim @@ -15,8 +15,7 @@ when not defined(profiler) and not defined(memProfiler): {.error: "Profiling support is turned off! Enable profiling by passing `--profiler:on --stackTrace:on` to the compiler (see the Nim Compiler User Guide for more options).".} -when defined(nimHasUsed): - {.used.} +{.used.} # We don't want to profile the profiling code ... {.push profiler: off.} diff --git a/lib/std/assertions.nim b/lib/std/assertions.nim index 5623ff8ef..229033c58 100644 --- a/lib/std/assertions.nim +++ b/lib/std/assertions.nim @@ -23,8 +23,6 @@ proc `$`(info: InstantiationInfo): string = # --------------------------------------------------------------------------- -when not defined(nimHasSinkInference): - {.pragma: nosinks.} proc raiseAssert*(msg: string) {.noinline, noreturn, nosinks.} = ## Raises an `AssertionDefect` with `msg`. diff --git a/lib/std/jsonutils.nim b/lib/std/jsonutils.nim index eec8dea7d..17d08e02e 100644 --- a/lib/std/jsonutils.nim +++ b/lib/std/jsonutils.nim @@ -37,22 +37,8 @@ from typetraits import OrdinalEnum, tupleLen when defined(nimPreviewSlimSystem): import std/assertions -when not defined(nimFixedForwardGeneric): - # xxx remove pending csources_v1 update >= 1.2.0 - proc to[T](node: JsonNode, t: typedesc[T]): T = - when T is string: node.getStr - elif T is bool: node.getBool - else: static: doAssert false, $T # support as needed (only needed during bootstrap) - proc isNamedTuple(T: typedesc): bool = # old implementation - when T isnot tuple: result = false - else: - var t: T - for name, _ in t.fieldPairs: - when name == "Field0": return compiles(t.Field0) - else: return true - return false -else: - proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".} + +proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".} type Joptions* = object # xxx rename FromJsonOptions diff --git a/lib/std/private/miscdollars.nim b/lib/std/private/miscdollars.nim index 47b788ee9..06fda6fa1 100644 --- a/lib/std/private/miscdollars.nim +++ b/lib/std/private/miscdollars.nim @@ -13,21 +13,7 @@ template toLocation*(result: var string, file: string | cstring, line: int, col: addInt(result, col) result.add ")" -when defined(nimHasIsNamedTuple): - proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".} -else: - # for bootstrap; remove after release 1.2 - proc isNamedTuple(T: typedesc): bool = - # Taken from typetraits. - when T isnot tuple: result = false - else: - var t: T - for name, _ in t.fieldPairs: - when name == "Field0": - return compiles(t.Field0) - else: - return true - return false +proc isNamedTuple(T: typedesc): bool {.magic: "TypeTrait".} template tupleObjectDollar*[T: tuple | object](result: var string, x: T) = result = "(" diff --git a/lib/system.nim b/lib/system.nim index 1b6235fb3..e9c9ebfeb 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -85,17 +85,12 @@ when defined(nimHasIterable): type iterable*[T] {.magic: IterableType.} ## Represents an expression that yields `T` -when defined(nimHashOrdinalFixed): - type - Ordinal*[T] {.magic: Ordinal.} ## Generic ordinal type. Includes integer, - ## bool, character, and enumeration types - ## as well as their subtypes. See also - ## `SomeOrdinal`. -else: - # bootstrap < 1.2.0 - type - OrdinalImpl[T] {.magic: Ordinal.} - Ordinal* = OrdinalImpl | uint | uint64 +type + Ordinal*[T] {.magic: Ordinal.} ## Generic ordinal type. Includes integer, + ## bool, character, and enumeration types + ## as well as their subtypes. See also + ## `SomeOrdinal`. + proc `addr`*[T](x: T): ptr T {.magic: "Addr", noSideEffect.} = ## Builtin `addr` operator for taking the address of a memory location. @@ -451,9 +446,7 @@ type ## However, objects that have no ancestor are also allowed. RootRef* = ref RootObj ## Reference to `RootObj`. -const NimStackTraceMsgs = - when defined(nimHasStacktraceMsgs): compileOption("stacktraceMsgs") - else: false +const NimStackTraceMsgs = compileOption("stacktraceMsgs") type RootEffect* {.compilerproc.} = object of RootObj ## \ @@ -2113,10 +2106,7 @@ when notJSnotNims: # we cannot compile this with stack tracing on # as it would recurse endlessly! - when defined(nimNewIntegerOps): - include "system/integerops" - else: - include "system/arithm" + include "system/integerops" {.pop.} @@ -2660,11 +2650,10 @@ when defined(nimconfig): when not defined(js): proc toOpenArray*[T](x: ptr UncheckedArray[T]; first, last: int): openArray[T] {. magic: "Slice".} - when defined(nimToOpenArrayCString): - proc toOpenArray*(x: cstring; first, last: int): openArray[char] {. - magic: "Slice".} - proc toOpenArrayByte*(x: cstring; first, last: int): openArray[byte] {. - magic: "Slice".} + proc toOpenArray*(x: cstring; first, last: int): openArray[char] {. + magic: "Slice".} + proc toOpenArrayByte*(x: cstring; first, last: int): openArray[byte] {. + magic: "Slice".} proc toOpenArray*[T](x: seq[T]; first, last: int): openArray[T] {. magic: "Slice".} diff --git a/lib/system/alloc.nim b/lib/system/alloc.nim index 5d1548977..cee70f677 100644 --- a/lib/system/alloc.nim +++ b/lib/system/alloc.nim @@ -95,17 +95,11 @@ type acc: int # accumulator for small object allocation when defined(gcDestructors): sharedFreeList: ptr FreeCell # make no attempt at avoiding false sharing for now for this object field - when defined(nimAlignPragma): - data {.align: MemAlign.}: UncheckedArray[byte] # start of usable memory - else: - data: UncheckedArray[byte] + data {.align: MemAlign.}: UncheckedArray[byte] # start of usable memory BigChunk = object of BaseChunk # not necessarily > PageSize! next, prev: PBigChunk # chunks of the same (or bigger) size - when defined(nimAlignPragma): - data {.align: MemAlign.}: UncheckedArray[byte] # start of usable memory - else: - data: UncheckedArray[byte] + data {.align: MemAlign.}: UncheckedArray[byte] # start of usable memory HeapLinks = object len: int diff --git a/lib/system/arithm.nim b/lib/system/arithm.nim deleted file mode 100644 index 158f40177..000000000 --- a/lib/system/arithm.nim +++ /dev/null @@ -1,425 +0,0 @@ -# -# -# Nim's Runtime Library -# (c) Copyright 2012 Andreas Rumpf -# -# See the file "copying.txt", included in this -# distribution, for details about the copyright. -# - - -# simple integer arithmetic with overflow checking - -proc raiseOverflow {.compilerproc, noinline.} = - # a single proc to reduce code size to a minimum - sysFatal(OverflowDefect, "over- or underflow") - -proc raiseDivByZero {.compilerproc, noinline.} = - sysFatal(DivByZeroDefect, "division by zero") - -when defined(builtinOverflow): - # Builtin compiler functions for improved performance - when sizeof(clong) == 8: - proc addInt64Overflow[T: int64|int](a, b: T, c: var T): bool {. - importc: "__builtin_saddl_overflow", nodecl, nosideeffect.} - - proc subInt64Overflow[T: int64|int](a, b: T, c: var T): bool {. - importc: "__builtin_ssubl_overflow", nodecl, nosideeffect.} - - proc mulInt64Overflow[T: int64|int](a, b: T, c: var T): bool {. - importc: "__builtin_smull_overflow", nodecl, nosideeffect.} - - elif sizeof(clonglong) == 8: - proc addInt64Overflow[T: int64|int](a, b: T, c: var T): bool {. - importc: "__builtin_saddll_overflow", nodecl, nosideeffect.} - - proc subInt64Overflow[T: int64|int](a, b: T, c: var T): bool {. - importc: "__builtin_ssubll_overflow", nodecl, nosideeffect.} - - proc mulInt64Overflow[T: int64|int](a, b: T, c: var T): bool {. - importc: "__builtin_smulll_overflow", nodecl, nosideeffect.} - - when sizeof(int) == 8: - proc addIntOverflow(a, b: int, c: var int): bool {.inline.} = - addInt64Overflow(a, b, c) - - proc subIntOverflow(a, b: int, c: var int): bool {.inline.} = - subInt64Overflow(a, b, c) - - proc mulIntOverflow(a, b: int, c: var int): bool {.inline.} = - mulInt64Overflow(a, b, c) - - elif sizeof(int) == 4 and sizeof(cint) == 4: - proc addIntOverflow(a, b: int, c: var int): bool {. - importc: "__builtin_sadd_overflow", nodecl, nosideeffect.} - - proc subIntOverflow(a, b: int, c: var int): bool {. - importc: "__builtin_ssub_overflow", nodecl, nosideeffect.} - - proc mulIntOverflow(a, b: int, c: var int): bool {. - importc: "__builtin_smul_overflow", nodecl, nosideeffect.} - - proc addInt64(a, b: int64): int64 {.compilerproc, inline.} = - if addInt64Overflow(a, b, result): - raiseOverflow() - - proc subInt64(a, b: int64): int64 {.compilerproc, inline.} = - if subInt64Overflow(a, b, result): - raiseOverflow() - - proc mulInt64(a, b: int64): int64 {.compilerproc, inline.} = - if mulInt64Overflow(a, b, result): - raiseOverflow() -else: - proc addInt64(a, b: int64): int64 {.compilerproc, inline.} = - result = a +% b - if (result xor a) >= int64(0) or (result xor b) >= int64(0): - return result - raiseOverflow() - - proc subInt64(a, b: int64): int64 {.compilerproc, inline.} = - result = a -% b - if (result xor a) >= int64(0) or (result xor not b) >= int64(0): - return result - raiseOverflow() - - # - # This code has been inspired by Python's source code. - # The native int product x*y is either exactly right or *way* off, being - # just the last n bits of the true product, where n is the number of bits - # in an int (the delivered product is the true product plus i*2**n for - # some integer i). - # - # The native float64 product x*y is subject to three - # rounding errors: on a sizeof(int)==8 box, each cast to double can lose - # info, and even on a sizeof(int)==4 box, the multiplication can lose info. - # But, unlike the native int product, it's not in *range* trouble: even - # if sizeof(int)==32 (256-bit ints), the product easily fits in the - # dynamic range of a float64. So the leading 50 (or so) bits of the float64 - # product are correct. - # - # We check these two ways against each other, and declare victory if they're - # approximately the same. Else, because the native int product is the only - # one that can lose catastrophic amounts of information, it's the native int - # product that must have overflowed. - # - proc mulInt64(a, b: int64): int64 {.compilerproc.} = - var - resAsFloat, floatProd: float64 - result = a *% b - floatProd = toBiggestFloat(a) # conversion - floatProd = floatProd * toBiggestFloat(b) - resAsFloat = toBiggestFloat(result) - - # Fast path for normal case: small multiplicands, and no info - # is lost in either method. - if resAsFloat == floatProd: return result - - # Somebody somewhere lost info. Close enough, or way off? Note - # that a != 0 and b != 0 (else resAsFloat == floatProd == 0). - # The difference either is or isn't significant compared to the - # true value (of which floatProd is a good approximation). - - # abs(diff)/abs(prod) <= 1/32 iff - # 32 * abs(diff) <= abs(prod) -- 5 good bits is "close enough" - if 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd): - return result - raiseOverflow() - -proc negInt64(a: int64): int64 {.compilerproc, inline.} = - if a != low(int64): return -a - raiseOverflow() - -proc absInt64(a: int64): int64 {.compilerproc, inline.} = - if a != low(int64): - if a >= 0: return a - else: return -a - raiseOverflow() - -proc divInt64(a, b: int64): int64 {.compilerproc, inline.} = - if b == int64(0): - raiseDivByZero() - if a == low(int64) and b == int64(-1): - raiseOverflow() - return a div b - -proc modInt64(a, b: int64): int64 {.compilerproc, inline.} = - if b == int64(0): - raiseDivByZero() - return a mod b - -proc absInt(a: int): int {.compilerproc, inline.} = - if a != low(int): - if a >= 0: return a - else: return -a - raiseOverflow() - -const - asmVersion = defined(i386) and (defined(vcc) or defined(wcc) or - defined(dmc) or defined(gcc) or defined(llvm_gcc)) - # my Version of Borland C++Builder does not have - # tasm32, which is needed for assembler blocks - # this is why Borland is not included in the 'when' - -when asmVersion and not defined(gcc) and not defined(llvm_gcc): - # assembler optimized versions for compilers that - # have an intel syntax assembler: - proc addInt(a, b: int): int {.compilerproc, asmNoStackFrame.} = - # a in eax, and b in edx - asm """ - mov eax, ecx - add eax, edx - jno theEnd - call `raiseOverflow` - theEnd: - ret - """ - - proc subInt(a, b: int): int {.compilerproc, asmNoStackFrame.} = - asm """ - mov eax, ecx - sub eax, edx - jno theEnd - call `raiseOverflow` - theEnd: - ret - """ - - proc negInt(a: int): int {.compilerproc, asmNoStackFrame.} = - asm """ - mov eax, ecx - neg eax - jno theEnd - call `raiseOverflow` - theEnd: - ret - """ - - proc divInt(a, b: int): int {.compilerproc, asmNoStackFrame.} = - asm """ - test edx, edx - jne L_NOT_ZERO - call `raiseDivByZero` - L_NOT_ZERO: - cmp ecx, 0x80000000 - jne L_DO_DIV - cmp edx, -1 - jne L_DO_DIV - call `raiseOverflow` - L_DO_DIV: - mov eax, ecx - mov ecx, edx - cdq - idiv ecx - ret - """ - - proc modInt(a, b: int): int {.compilerproc, asmNoStackFrame.} = - asm """ - test edx, edx - jne L_NOT_ZERO - call `raiseDivByZero` - L_NOT_ZERO: - cmp ecx, 0x80000000 - jne L_DO_DIV - cmp edx, -1 - jne L_DO_DIV - call `raiseOverflow` - L_DO_DIV: - mov eax, ecx - mov ecx, edx - cdq - idiv ecx - mov eax, edx - ret - """ - - proc mulInt(a, b: int): int {.compilerproc, asmNoStackFrame.} = - asm """ - mov eax, ecx - mov ecx, edx - xor edx, edx - imul ecx - jno theEnd - call `raiseOverflow` - theEnd: - ret - """ - -elif false: # asmVersion and (defined(gcc) or defined(llvm_gcc)): - proc addInt(a, b: int): int {.compilerproc, inline.} = - # don't use a pure proc here! - asm """ - "addl %%ecx, %%eax\n" - "jno 1\n" - "call _raiseOverflow\n" - "1: \n" - :"=a"(`result`) - :"a"(`a`), "c"(`b`) - """ - #".intel_syntax noprefix" - #/* Intel syntax here */ - #".att_syntax" - - proc subInt(a, b: int): int {.compilerproc, inline.} = - asm """ "subl %%ecx,%%eax\n" - "jno 1\n" - "call _raiseOverflow\n" - "1: \n" - :"=a"(`result`) - :"a"(`a`), "c"(`b`) - """ - - proc mulInt(a, b: int): int {.compilerproc, inline.} = - asm """ "xorl %%edx, %%edx\n" - "imull %%ecx\n" - "jno 1\n" - "call _raiseOverflow\n" - "1: \n" - :"=a"(`result`) - :"a"(`a`), "c"(`b`) - :"%edx" - """ - - proc negInt(a: int): int {.compilerproc, inline.} = - asm """ "negl %%eax\n" - "jno 1\n" - "call _raiseOverflow\n" - "1: \n" - :"=a"(`result`) - :"a"(`a`) - """ - - proc divInt(a, b: int): int {.compilerproc, inline.} = - asm """ "xorl %%edx, %%edx\n" - "idivl %%ecx\n" - "jno 1\n" - "call _raiseOverflow\n" - "1: \n" - :"=a"(`result`) - :"a"(`a`), "c"(`b`) - :"%edx" - """ - - proc modInt(a, b: int): int {.compilerproc, inline.} = - asm """ "xorl %%edx, %%edx\n" - "idivl %%ecx\n" - "jno 1\n" - "call _raiseOverflow\n" - "1: \n" - "movl %%edx, %%eax" - :"=a"(`result`) - :"a"(`a`), "c"(`b`) - :"%edx" - """ - -when not declared(addInt) and defined(builtinOverflow): - proc addInt(a, b: int): int {.compilerproc, inline.} = - if addIntOverflow(a, b, result): - raiseOverflow() - -when not declared(subInt) and defined(builtinOverflow): - proc subInt(a, b: int): int {.compilerproc, inline.} = - if subIntOverflow(a, b, result): - raiseOverflow() - -when not declared(mulInt) and defined(builtinOverflow): - proc mulInt(a, b: int): int {.compilerproc, inline.} = - if mulIntOverflow(a, b, result): - raiseOverflow() - -# Platform independent versions of the above (slower!) -when not declared(addInt): - proc addInt(a, b: int): int {.compilerproc, inline.} = - result = a +% b - if (result xor a) >= 0 or (result xor b) >= 0: - return result - raiseOverflow() - -when not declared(subInt): - proc subInt(a, b: int): int {.compilerproc, inline.} = - result = a -% b - if (result xor a) >= 0 or (result xor not b) >= 0: - return result - raiseOverflow() - -when not declared(negInt): - proc negInt(a: int): int {.compilerproc, inline.} = - if a != low(int): return -a - raiseOverflow() - -when not declared(divInt): - proc divInt(a, b: int): int {.compilerproc, inline.} = - if b == 0: - raiseDivByZero() - if a == low(int) and b == -1: - raiseOverflow() - return a div b - -when not declared(modInt): - proc modInt(a, b: int): int {.compilerproc, inline.} = - if b == 0: - raiseDivByZero() - return a mod b - -when not declared(mulInt): - # - # This code has been inspired by Python's source code. - # The native int product x*y is either exactly right or *way* off, being - # just the last n bits of the true product, where n is the number of bits - # in an int (the delivered product is the true product plus i*2**n for - # some integer i). - # - # The native float64 product x*y is subject to three - # rounding errors: on a sizeof(int)==8 box, each cast to double can lose - # info, and even on a sizeof(int)==4 box, the multiplication can lose info. - # But, unlike the native int product, it's not in *range* trouble: even - # if sizeof(int)==32 (256-bit ints), the product easily fits in the - # dynamic range of a float64. So the leading 50 (or so) bits of the float64 - # product are correct. - # - # We check these two ways against each other, and declare victory if - # they're approximately the same. Else, because the native int product is - # the only one that can lose catastrophic amounts of information, it's the - # native int product that must have overflowed. - # - proc mulInt(a, b: int): int {.compilerproc.} = - var - resAsFloat, floatProd: float - - result = a *% b - floatProd = toFloat(a) * toFloat(b) - resAsFloat = toFloat(result) - - # Fast path for normal case: small multiplicands, and no info - # is lost in either method. - if resAsFloat == floatProd: return result - - # Somebody somewhere lost info. Close enough, or way off? Note - # that a != 0 and b != 0 (else resAsFloat == floatProd == 0). - # The difference either is or isn't significant compared to the - # true value (of which floatProd is a good approximation). - - # abs(diff)/abs(prod) <= 1/32 iff - # 32 * abs(diff) <= abs(prod) -- 5 good bits is "close enough" - if 32.0 * abs(resAsFloat - floatProd) <= abs(floatProd): - return result - raiseOverflow() - -# We avoid setting the FPU control word here for compatibility with libraries -# written in other languages. - -proc raiseFloatInvalidOp {.compilerproc, noinline.} = - sysFatal(FloatInvalidOpDefect, "FPU operation caused a NaN result") - -proc nanCheck(x: float64) {.compilerproc, inline.} = - if x != x: raiseFloatInvalidOp() - -proc raiseFloatOverflow(x: float64) {.compilerproc, noinline.} = - if x > 0.0: - sysFatal(FloatOverflowDefect, "FPU operation caused an overflow") - else: - sysFatal(FloatUnderflowDefect, "FPU operations caused an underflow") - -proc infCheck(x: float64) {.compilerproc, inline.} = - if x != 0.0 and x*0.5 == x: raiseFloatOverflow(x) diff --git a/lib/system/arithmetics.nim b/lib/system/arithmetics.nim index d05aaaa5b..5c21e56ef 100644 --- a/lib/system/arithmetics.nim +++ b/lib/system/arithmetics.nim @@ -405,105 +405,57 @@ proc `%%`*(x, y: int32): int32 {.inline.} = cast[int32](cast[uint32](x) mod cast proc `%%`*(x, y: int64): int64 {.inline.} = cast[int64](cast[uint64](x) mod cast[uint64](y)) when not defined(nimPreviewSlimSystem): - when defined(nimNoZeroExtendMagic): - proc ze*(x: int8): int {.deprecated.} = - ## zero extends a smaller integer type to `int`. This treats `x` as - ## unsigned. - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - cast[int](uint(cast[uint8](x))) + proc ze*(x: int8): int {.deprecated.} = + ## zero extends a smaller integer type to `int`. This treats `x` as + ## unsigned. + ## **Deprecated since version 0.19.9**: Use unsigned integers instead. + cast[int](uint(cast[uint8](x))) - proc ze*(x: int16): int {.deprecated.} = - ## zero extends a smaller integer type to `int`. This treats `x` as - ## unsigned. - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - cast[int](uint(cast[uint16](x))) + proc ze*(x: int16): int {.deprecated.} = + ## zero extends a smaller integer type to `int`. This treats `x` as + ## unsigned. + ## **Deprecated since version 0.19.9**: Use unsigned integers instead. + cast[int](uint(cast[uint16](x))) - proc ze64*(x: int8): int64 {.deprecated.} = - ## zero extends a smaller integer type to `int64`. This treats `x` as - ## unsigned. - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - cast[int64](uint64(cast[uint8](x))) + proc ze64*(x: int8): int64 {.deprecated.} = + ## zero extends a smaller integer type to `int64`. This treats `x` as + ## unsigned. + ## **Deprecated since version 0.19.9**: Use unsigned integers instead. + cast[int64](uint64(cast[uint8](x))) - proc ze64*(x: int16): int64 {.deprecated.} = - ## zero extends a smaller integer type to `int64`. This treats `x` as - ## unsigned. - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - cast[int64](uint64(cast[uint16](x))) + proc ze64*(x: int16): int64 {.deprecated.} = + ## zero extends a smaller integer type to `int64`. This treats `x` as + ## unsigned. + ## **Deprecated since version 0.19.9**: Use unsigned integers instead. + cast[int64](uint64(cast[uint16](x))) - proc ze64*(x: int32): int64 {.deprecated.} = - ## zero extends a smaller integer type to `int64`. This treats `x` as - ## unsigned. - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - cast[int64](uint64(cast[uint32](x))) + proc ze64*(x: int32): int64 {.deprecated.} = + ## zero extends a smaller integer type to `int64`. This treats `x` as + ## unsigned. + ## **Deprecated since version 0.19.9**: Use unsigned integers instead. + cast[int64](uint64(cast[uint32](x))) - proc ze64*(x: int): int64 {.deprecated.} = - ## zero extends a smaller integer type to `int64`. This treats `x` as - ## unsigned. Does nothing if the size of an `int` is the same as `int64`. - ## (This is the case on 64 bit processors.) - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - cast[int64](uint64(cast[uint](x))) + proc ze64*(x: int): int64 {.deprecated.} = + ## zero extends a smaller integer type to `int64`. This treats `x` as + ## unsigned. Does nothing if the size of an `int` is the same as `int64`. + ## (This is the case on 64 bit processors.) + ## **Deprecated since version 0.19.9**: Use unsigned integers instead. + cast[int64](uint64(cast[uint](x))) - proc toU8*(x: int): int8 {.deprecated.} = - ## treats `x` as unsigned and converts it to a byte by taking the last 8 bits - ## from `x`. - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - cast[int8](x) + proc toU8*(x: int): int8 {.deprecated.} = + ## treats `x` as unsigned and converts it to a byte by taking the last 8 bits + ## from `x`. + ## **Deprecated since version 0.19.9**: Use unsigned integers instead. + cast[int8](x) - proc toU16*(x: int): int16 {.deprecated.} = - ## treats `x` as unsigned and converts it to an `int16` by taking the last - ## 16 bits from `x`. - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - cast[int16](x) + proc toU16*(x: int): int16 {.deprecated.} = + ## treats `x` as unsigned and converts it to an `int16` by taking the last + ## 16 bits from `x`. + ## **Deprecated since version 0.19.9**: Use unsigned integers instead. + cast[int16](x) - proc toU32*(x: int64): int32 {.deprecated.} = - ## treats `x` as unsigned and converts it to an `int32` by taking the - ## last 32 bits from `x`. - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - cast[int32](x) - - elif not defined(js): - proc ze*(x: int8): int {.magic: "Ze8ToI", noSideEffect, deprecated.} - ## zero extends a smaller integer type to `int`. This treats `x` as - ## unsigned. - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - - proc ze*(x: int16): int {.magic: "Ze16ToI", noSideEffect, deprecated.} - ## zero extends a smaller integer type to `int`. This treats `x` as - ## unsigned. - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - - proc ze64*(x: int8): int64 {.magic: "Ze8ToI64", noSideEffect, deprecated.} - ## zero extends a smaller integer type to `int64`. This treats `x` as - ## unsigned. - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - - proc ze64*(x: int16): int64 {.magic: "Ze16ToI64", noSideEffect, deprecated.} - ## zero extends a smaller integer type to `int64`. This treats `x` as - ## unsigned. - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - - proc ze64*(x: int32): int64 {.magic: "Ze32ToI64", noSideEffect, deprecated.} - ## zero extends a smaller integer type to `int64`. This treats `x` as - ## unsigned. - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - - proc ze64*(x: int): int64 {.magic: "ZeIToI64", noSideEffect, deprecated.} - ## zero extends a smaller integer type to `int64`. This treats `x` as - ## unsigned. Does nothing if the size of an `int` is the same as `int64`. - ## (This is the case on 64 bit processors.) - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - - proc toU8*(x: int): int8 {.magic: "ToU8", noSideEffect, deprecated.} - ## treats `x` as unsigned and converts it to a byte by taking the last 8 bits - ## from `x`. - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - - proc toU16*(x: int): int16 {.magic: "ToU16", noSideEffect, deprecated.} - ## treats `x` as unsigned and converts it to an `int16` by taking the last - ## 16 bits from `x`. - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. - - proc toU32*(x: int64): int32 {.magic: "ToU32", noSideEffect, deprecated.} - ## treats `x` as unsigned and converts it to an `int32` by taking the - ## last 32 bits from `x`. - ## **Deprecated since version 0.19.9**: Use unsigned integers instead. + proc toU32*(x: int64): int32 {.deprecated.} = + ## treats `x` as unsigned and converts it to an `int32` by taking the + ## last 32 bits from `x`. + ## **Deprecated since version 0.19.9**: Use unsigned integers instead. + cast[int32](x) diff --git a/lib/system/compilation.nim b/lib/system/compilation.nim index 234e8cafe..c04990277 100644 --- a/lib/system/compilation.nim +++ b/lib/system/compilation.nim @@ -51,30 +51,24 @@ proc defined*(x: untyped): bool {.magic: "Defined", noSideEffect, compileTime.} ## * `compileOption <#compileOption,string,string>`_ for enum options ## * `define pragmas `_ -when defined(nimHasDeclaredMagic): - proc declared*(x: untyped): bool {.magic: "Declared", noSideEffect, compileTime.} - ## Special compile-time procedure that checks whether `x` is - ## declared. `x` has to be an identifier or a qualified identifier. - ## - ## This can be used to check whether a library provides a certain - ## feature or not: - ## ``` - ## when not declared(strutils.toUpper): - ## # provide our own toUpper proc here, because strutils is - ## # missing it. - ## ``` - ## - ## See also: - ## * `declaredInScope <#declaredInScope,untyped>`_ -else: - proc declared*(x: untyped): bool {.magic: "Defined", noSideEffect, compileTime.} +proc declared*(x: untyped): bool {.magic: "Declared", noSideEffect, compileTime.} + ## Special compile-time procedure that checks whether `x` is + ## declared. `x` has to be an identifier or a qualified identifier. + ## + ## This can be used to check whether a library provides a certain + ## feature or not: + ## ``` + ## when not declared(strutils.toUpper): + ## # provide our own toUpper proc here, because strutils is + ## # missing it. + ## ``` + ## + ## See also: + ## * `declaredInScope <#declaredInScope,untyped>`_ -when defined(nimHasDeclaredMagic): - proc declaredInScope*(x: untyped): bool {.magic: "DeclaredInScope", noSideEffect, compileTime.} - ## Special compile-time procedure that checks whether `x` is - ## declared in the current scope. `x` has to be an identifier. -else: - proc declaredInScope*(x: untyped): bool {.magic: "DefinedInScope", noSideEffect, compileTime.} +proc declaredInScope*(x: untyped): bool {.magic: "DeclaredInScope", noSideEffect, compileTime.} + ## Special compile-time procedure that checks whether `x` is + ## declared in the current scope. `x` has to be an identifier. proc compiles*(x: untyped): bool {.magic: "Compiles", noSideEffect, compileTime.} = ## Special compile-time procedure that checks whether `x` can be compiled diff --git a/lib/system/fatal.nim b/lib/system/fatal.nim index a55af2dc3..6073ee779 100644 --- a/lib/system/fatal.nim +++ b/lib/system/fatal.nim @@ -9,10 +9,7 @@ {.push profiler: off.} -when defined(nimHasExceptionsQuery): - const gotoBasedExceptions = compileOption("exceptions", "goto") -else: - const gotoBasedExceptions = false +const gotoBasedExceptions = compileOption("exceptions", "goto") when hostOS == "standalone": include "$projectpath/panicoverride" diff --git a/lib/system/inclrtl.nim b/lib/system/inclrtl.nim index 42c85ad26..3bf0b9893 100644 --- a/lib/system/inclrtl.nim +++ b/lib/system/inclrtl.nim @@ -47,5 +47,4 @@ else: {.pragma: benign, gcsafe.} -when defined(nimHasSinkInference): - {.push sinkInference: on.} +{.push sinkInference: on.} diff --git a/lib/system/iterators.nim b/lib/system/iterators.nim index b16a605b7..4bd12680f 100644 --- a/lib/system/iterators.nim +++ b/lib/system/iterators.nim @@ -3,7 +3,7 @@ when defined(nimPreviewSlimSystem): import std/assertions -when defined(nimHasLentIterators) and not defined(nimNoLentIterators): +when not defined(nimNoLentIterators): template lent2(T): untyped = lent T else: template lent2(T): untyped = T diff --git a/lib/system/jssys.nim b/lib/system/jssys.nim index 9dfa80877..4f64403fe 100644 --- a/lib/system/jssys.nim +++ b/lib/system/jssys.nim @@ -503,28 +503,6 @@ proc absInt(a: int): int {.compilerproc.} = proc absInt64(a: int64): int64 {.compilerproc.} = result = if a < 0: a*(-1) else: a -when not defined(nimNoZeroExtendMagic): - proc ze*(a: int): int {.compilerproc.} = - result = a - - proc ze64*(a: int64): int64 {.compilerproc.} = - result = a - - proc toU8*(a: int): int8 {.asmNoStackFrame, compilerproc.} = - asm """ - return `a`; - """ - - proc toU16*(a: int): int16 {.asmNoStackFrame, compilerproc.} = - asm """ - return `a`; - """ - - proc toU32*(a: int64): int32 {.asmNoStackFrame, compilerproc.} = - asm """ - return `a`; - """ - proc nimMin(a, b: int): int {.compilerproc.} = return if a <= b: a else: b proc nimMax(a, b: int): int {.compilerproc.} = return if a >= b: a else: b diff --git a/lib/system/strmantle.nim b/lib/system/strmantle.nim index 0b2578280..f4733ed21 100644 --- a/lib/system/strmantle.nim +++ b/lib/system/strmantle.nim @@ -75,8 +75,8 @@ const 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22] -when defined(nimHasInvariant): - {.push staticBoundChecks: off.} + +{.push staticBoundChecks: off.} proc nimParseBiggestFloat(s: openArray[char], number: var BiggestFloat, ): int {.compilerproc.} = @@ -234,8 +234,7 @@ proc nimParseBiggestFloat(s: openArray[char], number: var BiggestFloat, t[ti-3] = ('0'.ord + absExponent mod 10).char number = c_strtod(cast[cstring](addr t), nil) -when defined(nimHasInvariant): - {.pop.} # staticBoundChecks +{.pop.} # staticBoundChecks proc nimBoolToStr(x: bool): string {.compilerRtl.} = return if x: "true" else: "false" From 9b4516fbcb4ad9c5db1e6f563601855f0e9982a0 Mon Sep 17 00:00:00 2001 From: rockcavera Date: Tue, 27 Dec 2022 01:13:05 -0300 Subject: [PATCH 3618/3768] fix in msg `doAssert()` to update grammar.txt (#21179) --- compiler/parser.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/parser.nim b/compiler/parser.nim index 00013c218..0c8edd2a8 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -42,7 +42,7 @@ when isMainModule or defined(nimTestGrammar): proc checkSameGrammar*() = doAssert sameFileContent(newGrammarText, "doc/grammar.txt"), - "execute 'nim r compiler.nim' to keep grammar.txt up-to-date" + "execute 'nim r compiler/parser.nim' to keep grammar.txt up-to-date" else: writeGrammarFile("doc/grammar.txt") import ".." / tools / grammar_nanny From f232329bb12fc9d93fe1e09769e291eae4e2953d Mon Sep 17 00:00:00 2001 From: Bung Date: Tue, 27 Dec 2022 19:07:15 +0800 Subject: [PATCH 3619/3768] add test case for #20289 (#21180) --- tests/misc/t20289.nim | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 tests/misc/t20289.nim diff --git a/tests/misc/t20289.nim b/tests/misc/t20289.nim new file mode 100644 index 000000000..5a0a269f0 --- /dev/null +++ b/tests/misc/t20289.nim @@ -0,0 +1,15 @@ +discard """ + action: reject +""" + +type E[T] = object + v: T + +template j[T](R: type E[T], x: untyped): R = R(v: x) +template d[T](O: type E, v: T): E[T] = E[T].j(v) + +proc w[T](): E[T] = + template r(k: int): auto = default(T) + E.d r + +discard w[int]() From 82d80e6e2cb09652e15d8ec81621bba734872aaa Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Tue, 27 Dec 2022 14:30:26 +0100 Subject: [PATCH 3620/3768] atlas: minor doc improvements (#21183) * closes #20808 * atlas: better docs --- tools/atlas/atlas.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tools/atlas/atlas.md b/tools/atlas/atlas.md index a36817dc5..ee770cd62 100644 --- a/tools/atlas/atlas.md +++ b/tools/atlas/atlas.md @@ -28,7 +28,7 @@ a `nim.cfg` file for the compiler. For example: ``` The version selection is deterministic, it picks up the *minimum* required -version. Thanks to this design, lock files are not required. +version. Thanks to this design, lock files are much less important. ## Dependencies @@ -75,3 +75,7 @@ then `clone ` is performed. Search the package index `packages.json` for a package that the given terms in its description (or name or list of tags). + +### Install + +Use the .nimble file to setup the project's dependencies. From 56840931355fdf8ce12a59ad085d90b04404f831 Mon Sep 17 00:00:00 2001 From: metagn Date: Tue, 27 Dec 2022 23:24:00 +0300 Subject: [PATCH 3621/3768] fix for bad error message with const in case statement (#21182) * preliminary fix for bad error message with const * add test case * fix tmatrixconcept and tmatrixlib --- compiler/semfold.nim | 4 ++++ tests/errmsgs/tconstinfo.nim | 7 +++++++ 2 files changed, 11 insertions(+) create mode 100644 tests/errmsgs/tconstinfo.nim diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 7c57eb370..7f966c6fd 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -569,6 +569,8 @@ proc foldDefine(m, s: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode else: result = copyTree(s.astdef) # unreachable else: result = copyTree(s.astdef) + if result != nil: + result.info = n.info proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode = result = nil @@ -593,6 +595,8 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode result = foldDefine(m, s, n, idgen, g) else: result = copyTree(s.astdef) + if result != nil: + result.info = n.info of skProc, skFunc, skMethod: result = n of skParam: diff --git a/tests/errmsgs/tconstinfo.nim b/tests/errmsgs/tconstinfo.nim new file mode 100644 index 000000000..170972b9f --- /dev/null +++ b/tests/errmsgs/tconstinfo.nim @@ -0,0 +1,7 @@ +# https://forum.nim-lang.org/t/9762 + +const foo = "abc" +case 'a' +of foo: echo "should error" #[tt.Error + ^ type mismatch: got but expected 'char']# +else: discard From 9e35631191ad67994bb45cfad4d9452f790534e0 Mon Sep 17 00:00:00 2001 From: Phil Krylov Date: Wed, 28 Dec 2022 03:26:52 +0100 Subject: [PATCH 3622/3768] std/encodings: Fix open() documentation in regard to exception raised (#21187) --- lib/pure/encodings.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/pure/encodings.nim b/lib/pure/encodings.nim index 26b6e1f22..0d7e878e5 100644 --- a/lib/pure/encodings.nim +++ b/lib/pure/encodings.nim @@ -339,7 +339,7 @@ proc getCurrentEncoding*(uiApp = false): string = proc open*(destEncoding = "UTF-8", srcEncoding = "CP1252"): EncodingConverter = ## Opens a converter that can convert from `srcEncoding` to `destEncoding`. - ## Raises `IOError` if it cannot fulfill the request. + ## Raises `EncodingError` if it cannot fulfill the request. when not defined(windows): result = iconvOpen(destEncoding, srcEncoding) if result == nil: From 9efa56a8bbacab2075a4c2fe4c5424d57b6eab46 Mon Sep 17 00:00:00 2001 From: rockcavera Date: Tue, 27 Dec 2022 23:40:17 -0300 Subject: [PATCH 3623/3768] [backport: 2.0] prevents the jsonscript command from exceeding the maximum length of a command line during linking (#21186) --- compiler/extccomp.nim | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 66de46556..6515f6968 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -851,6 +851,17 @@ proc displayProgressCC(conf: ConfigRef, path, compileCmd: string): string = else: result = MsgKindToStr[hintCC] % demangleModuleName(path.splitFile.name) +proc preventLinkCmdMaxCmdLen(conf: ConfigRef, linkCmd: string) = + # Prevent linkcmd from exceeding the maximum command line length. + # Windows's command line limit is about 8K (8191 characters) so C compilers on + # Windows support a feature where the command line can be passed via ``@linkcmd`` + # to them. + const MaxCmdLen = when defined(windows): 8_000 else: 32_000 + if linkCmd.len > MaxCmdLen: + linkViaResponseFile(conf, linkCmd) + else: + execLinkCmd(conf, linkCmd) + proc callCCompiler*(conf: ConfigRef) = var linkCmd: string @@ -927,14 +938,7 @@ proc callCCompiler*(conf: ConfigRef) = linkCmd = getLinkCmd(conf, mainOutput, objfiles, removeStaticFile = true) extraCmds = getExtraCmds(conf, mainOutput) if optCompileOnly notin conf.globalOptions: - const MaxCmdLen = when defined(windows): 8_000 else: 32_000 - if linkCmd.len > MaxCmdLen: - # Windows's command line limit is about 8K (don't laugh...) so C compilers on - # Windows support a feature where the command line can be passed via ``@linkcmd`` - # to them. - linkViaResponseFile(conf, linkCmd) - else: - execLinkCmd(conf, linkCmd) + preventLinkCmdMaxCmdLen(conf, linkCmd) for cmd in extraCmds: execExternalProgram(conf, cmd, hintExecuting) else: @@ -1035,7 +1039,7 @@ proc runJsonBuildInstructions*(conf: ConfigRef; jsonFile: AbsoluteFile) = cmds.add cmd prettyCmds.add displayProgressCC(conf, name, cmd) execCmdsInParallel(conf, cmds, prettyCb) - execLinkCmd(conf, bcache.linkcmd) + preventLinkCmdMaxCmdLen(conf, bcache.linkcmd) for cmd in bcache.extraCmds: execExternalProgram(conf, cmd, hintExecuting) proc genMappingFiles(conf: ConfigRef; list: CfileList): Rope = From 3d5dbf8f96a5106a3b817a45a926303bc5623786 Mon Sep 17 00:00:00 2001 From: Jake Leahy Date: Wed, 28 Dec 2022 17:35:11 +1100 Subject: [PATCH 3624/3768] Fix nimsuggest not suggesting fields in when theres static parameters (#21189) Don't check against empty arguments Add test case --- compiler/sigmatch.nim | 2 +- nimsuggest/tests/t21185.nim | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 nimsuggest/tests/t21185.nim diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index d05503c00..68068cd26 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -2055,7 +2055,7 @@ proc paramTypesMatchAux(m: var TCandidate, f, a: PType, a.n == nil and tfGenericTypeParam notin a.flags: return newNodeIT(nkType, argOrig.info, makeTypeFromExpr(c, arg)) - else: + elif arg.kind != nkEmpty: var evaluated = c.semTryConstExpr(c, arg) if evaluated != nil: # Don't build the type in-place because `evaluated` and `arg` may point diff --git a/nimsuggest/tests/t21185.nim b/nimsuggest/tests/t21185.nim new file mode 100644 index 000000000..bf5a0e3cc --- /dev/null +++ b/nimsuggest/tests/t21185.nim @@ -0,0 +1,18 @@ + +# Reduced case of 21185. Issue was first parameter being static +proc foo(x: static[int]) = discard + +type + Person = object + name: string + age: int + +let p = Person() +p.#[!]# + +discard """ +$nimsuggest --tester --v3 --maxresults:2 $file +>sug $1 +sug;;skField;;age;;int;;$file;;8;;4;;"";;100;;None +sug;;skField;;name;;string;;$file;;7;;4;;"";;100;;None +""" From 7a74c2dc3a82488671237555faa95fb38ef31bd5 Mon Sep 17 00:00:00 2001 From: Bung Date: Wed, 28 Dec 2022 15:28:48 +0800 Subject: [PATCH 3625/3768] fix #14667 (#21190) --- compiler/ccgexprs.nim | 4 ++++ compiler/cgendata.nim | 1 + tests/misc/t14667.nim | 12 ++++++++++++ 3 files changed, 17 insertions(+) create mode 100644 tests/misc/t14667.nim diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index d7ef024b1..e2885a294 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -3200,6 +3200,8 @@ proc getDefaultValue(p: BProc; typ: PType; info: TLineInfo; result: var Rope) = result.add "}" of tyTuple: result.add "{" + if p.vccAndC and t.len == 0: + result.add "0" for i in 0.. 0: result.add ", " getDefaultValue(p, t[i], info, result) @@ -3338,6 +3340,8 @@ proc genConstSimpleList(p: BProc, n: PNode; isConst: bool; result: var Rope) = proc genConstTuple(p: BProc, n: PNode; isConst: bool; tup: PType; result: var Rope) = result.add "{" + if p.vccAndC and n.len == 0: + result.add "0" for i in 0.. 0: result.add ",\n" diff --git a/compiler/cgendata.nim b/compiler/cgendata.nim index 2daa8da7c..f75598652 100644 --- a/compiler/cgendata.nim +++ b/compiler/cgendata.nim @@ -172,6 +172,7 @@ type template config*(m: BModule): ConfigRef = m.g.config template config*(p: BProc): ConfigRef = p.module.g.config +template vccAndC*(p: BProc): bool = p.module.config.cCompiler == ccVcc and p.module.config.backend == backendC proc includeHeader*(this: BModule; header: string) = if not this.headerFiles.contains header: diff --git a/tests/misc/t14667.nim b/tests/misc/t14667.nim new file mode 100644 index 000000000..3034e2841 --- /dev/null +++ b/tests/misc/t14667.nim @@ -0,0 +1,12 @@ +discard """ + matrix: "--cc:vcc" + disabled: "linux" + disabled: "bsd" + disabled: "osx" + disabled: "unix" + disabled: "posix" +""" + +type A = tuple +discard () +discard default(A) From 4b63ac4b87c54a1b31d60b7555e2c60d232817c8 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Wed, 28 Dec 2022 23:23:37 +0800 Subject: [PATCH 3626/3768] fixes #21171; dynamic acyclic refs need to use dyn decRef (#21184) * fixes #21171; dyn destructors for acyclic inherited refs * add a test * Update compiler/liftdestructors.nim --- compiler/liftdestructors.nim | 7 ++++ lib/system/orc.nim | 13 ++++++ tests/arc/t21184.nim | 77 ++++++++++++++++++++++++++++++++++++ 3 files changed, 97 insertions(+) create mode 100644 tests/arc/t21184.nim diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 5174a908f..2b27a58fc 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -609,6 +609,11 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = createTypeBoundOps(c.g, c.c, elemType, c.info, c.idgen) let isCyclic = c.g.config.selectedGC == gcOrc and types.canFormAcycle(elemType) + let isInheritableAcyclicRef = c.g.config.selectedGC == gcOrc and + (not isPureObject(elemType)) and + tfAcyclic in skipTypes(elemType, abstractInst+{tyOwned}-{tyTypeDesc}).flags + # dynamic Acyclic refs need to use dyn decRef + let tmp = if isCyclic and c.kind in {attachedAsgn, attachedSink}: declareTempOf(c, body, x) @@ -632,6 +637,8 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicStatic", c.info, tmp, typInfo) else: cond = callCodegenProc(c.g, "nimDecRefIsLastCyclicDyn", c.info, tmp) + elif isInheritableAcyclicRef: + cond = callCodegenProc(c.g, "nimDecRefIsLastDyn", c.info, x) else: cond = callCodegenProc(c.g, "nimDecRefIsLast", c.info, x) cond.typ = getSysType(c.g, x.info, tyBool) diff --git a/lib/system/orc.nim b/lib/system/orc.nim index 83b983ee1..a56a0c057 100644 --- a/lib/system/orc.nim +++ b/lib/system/orc.nim @@ -485,6 +485,19 @@ proc nimDecRefIsLastCyclicDyn(p: pointer): bool {.compilerRtl, inl.} = #if cell.color == colPurple: rememberCycle(result, cell, cast[ptr PNimTypeV2](p)[]) +proc nimDecRefIsLastDyn(p: pointer): bool {.compilerRtl, inl.} = + if p != nil: + var cell = head(p) + if (cell.rc and not rcMask) == 0: + result = true + #cprintf("[DESTROY] %p\n", p) + else: + dec cell.rc, rcIncrement + #if cell.color == colPurple: + if result: + if cell.rootIdx > 0: + unregisterCycle(cell) + proc nimDecRefIsLastCyclicStatic(p: pointer; desc: PNimTypeV2): bool {.compilerRtl, inl.} = if p != nil: var cell = head(p) diff --git a/tests/arc/t21184.nim b/tests/arc/t21184.nim new file mode 100644 index 000000000..91d0c42c7 --- /dev/null +++ b/tests/arc/t21184.nim @@ -0,0 +1,77 @@ +discard """ + matrix: "--mm:orc" +""" + +import std/[with] + +type + Node* {.acyclic.} = ref object of RootObj + name: string + data: pointer + children: seq[Node] + TextNode = ref object of Node + text: string + +proc fakeEcho(s: string) = + if s.len < 0: + echo s + +proc newNode[T: Node](parent: Node): T = + new result + result.data = alloc0(250) + parent.children.add(result) + +proc newRootNode(): Node = + new result + result.data = alloc0(250) + +method printNode(node: Node) {.base.} = + fakeEcho node.name + +method printNode(node: TextNode) = + procCall printNode(Node(node)) + fakeEcho node.text + +proc printChildren(node: Node) = + for child in node.children: + child.printNode() + printChildren(child) + +proc free(node: Node) = + for child in node.children: + free(child) + dealloc(node.data) + +template node(parent: Node, body: untyped): untyped = + var node = newNode[Node](parent) + with node: + body + +proc textNode(parent: Node, text: string) = + var node = newNode[TextNode](parent) + node.text = text + +template withRootNode(body: untyped): untyped = + var root = newRootNode() + root.name = "root" + with root: + body + root.printNode() + printChildren(root) + root.free() + +proc doTest() = + withRootNode: + node: + name = "child1" + node: + name = "child2" + node: + name = "child3" + textNode "Hello, world!" + + +# bug #21171 +if isMainModule: + for i in 0..100000: + doTest() From 646932b3f316d6b8e3ceb6b94b9d01232d5cdb33 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Thu, 29 Dec 2022 03:41:27 +0800 Subject: [PATCH 3627/3768] fixes warnings when building csources (#21194) * replace `symbolfiles` with `incremental`; fixes warnings when build csources * fixes self conversion warnings --- tools/niminst/niminst.nim | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tools/niminst/niminst.nim b/tools/niminst/niminst.nim index bc763a56f..81b46653d 100644 --- a/tools/niminst/niminst.nim +++ b/tools/niminst/niminst.nim @@ -168,11 +168,11 @@ proc parseCmdLine(c: var ConfigData) = next(p) var kind = p.kind var key = p.key - var val = p.val.string + var val = p.val case kind of cmdArgument: if c.actions == {}: - for a in split(normalize(key.string), {';', ','}): + for a in split(normalize(key), {';', ','}): case a of "csource": incl(c.actions, actionCSource) of "scripts": incl(c.actions, actionScripts) @@ -183,11 +183,11 @@ proc parseCmdLine(c: var ConfigData) = of "deb": incl(c.actions, actionDeb) else: quit(Usage) else: - c.infile = addFileExt(key.string, "ini") - c.nimArgs = cmdLineRest(p).string + c.infile = addFileExt(key, "ini") + c.nimArgs = cmdLineRest(p) break of cmdLongOption, cmdShortOption: - case normalize(key.string) + case normalize(key) of "help", "h": stdout.write(Usage) quit(0) @@ -544,7 +544,7 @@ proc srcdist(c: var ConfigData) = var dir = getOutputDir(c) / buildDir(osA, cpuA) if dirExists(dir): removeDir(dir) createDir(dir) - var cmd = ("$# compile -f --symbolfiles:off --compileonly " & + var cmd = ("$# compile -f --incremental:off --compileonly " & "--gen_mapping --cc:gcc --skipUserCfg" & " --os:$# --cpu:$# $# $#") % [findNim(), osname, cpuname, c.nimArgs, c.mainfile] From 761c5a0830a18cb0db721d27aff53a07ca30b1c3 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Thu, 29 Dec 2022 03:54:15 +0800 Subject: [PATCH 3628/3768] less verbose type mismatch messages (#21191) * less verbose type mismatch messages * Update compiler/types.nim * fixes i386 * fixes i386 --- changelogs/changelog_2_0_0.md | 2 ++ compiler/options.nim | 1 + compiler/semcall.nim | 44 +++++++++++++++++++------ compiler/sigmatch.nim | 40 ++++++++++++---------- tests/config.nims | 1 + tests/errmsgs/tconcisetypemismatch.nim | 23 +++++++++++++ tests/errmsgs/tconcisetypemismatch.nims | 21 ++++++++++++ 7 files changed, 105 insertions(+), 27 deletions(-) create mode 100644 tests/errmsgs/tconcisetypemismatch.nim create mode 100644 tests/errmsgs/tconcisetypemismatch.nims diff --git a/changelogs/changelog_2_0_0.md b/changelogs/changelog_2_0_0.md index 614d97d85..29d906d62 100644 --- a/changelogs/changelog_2_0_0.md +++ b/changelogs/changelog_2_0_0.md @@ -135,6 +135,8 @@ - The experimental strictFuncs feature now disallows a store to the heap via a `ref` or `ptr` indirection. +- - Added the `--legacy:verboseTypeMismatch` switch to get legacy type mismatch error messages. + ## Standard library additions and changes [//]: # "Changes:" diff --git a/compiler/options.nim b/compiler/options.nim index 7e4f6898f..ded084ae4 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -231,6 +231,7 @@ type ## are not anymore. laxEffects ## Lax effects system prior to Nim 2.0. + verboseTypeMismatch SymbolFilesOption* = enum disabledSf, writeOnlySf, readOnlySf, v2Sf, stressTest diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 31b490781..2147a9645 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -10,7 +10,8 @@ ## This module implements semantic checking for calls. # included from sem.nim -from algorithm import sort +from std/algorithm import sort + proc sameMethodDispatcher(a, b: PSym): bool = result = false @@ -192,7 +193,7 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): # argument in order to remove plenty of candidates. This is # comparable to what C# does and C# is doing fine. var filterOnlyFirst = false - if optShowAllMismatches notin c.config.globalOptions: + if optShowAllMismatches notin c.config.globalOptions and verboseTypeMismatch in c.config.legacyFeatures: for err in errors: if err.firstMismatch.arg > 1: filterOnlyFirst = true @@ -208,6 +209,10 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): if filterOnlyFirst and err.firstMismatch.arg == 1: inc skipped continue + + if verboseTypeMismatch notin c.config.legacyFeatures: + candidates.add "[" & $err.firstMismatch.arg & "] " + if err.sym.kind in routineKinds and err.sym.ast != nil: candidates.add(renderTree(err.sym.ast, {renderNoBody, renderNoComments, renderNoPragmas})) @@ -217,7 +222,7 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): candidates.add("\n") let nArg = if err.firstMismatch.arg < n.len: n[err.firstMismatch.arg] else: nil let nameParam = if err.firstMismatch.formal != nil: err.firstMismatch.formal.name.s else: "" - if n.len > 1: + if n.len > 1 and verboseTypeMismatch in c.config.legacyFeatures: candidates.add(" first type mismatch at position: " & $err.firstMismatch.arg) # candidates.add "\n reason: " & $err.firstMismatch.kind # for debugging case err.firstMismatch.kind @@ -274,11 +279,28 @@ proc presentFailedCandidates(c: PContext, n: PNode, errors: CandidateErrors): const errTypeMismatch = "type mismatch: got <" errButExpected = "but expected one of:" + errExpectedPosition = "Expected one of (first mismatch at position [#]):" errUndeclaredField = "undeclared field: '$1'" errUndeclaredRoutine = "attempting to call undeclared routine: '$1'" errBadRoutine = "attempting to call routine: '$1'$2" errAmbiguousCallXYZ = "ambiguous call; both $1 and $2 match for: $3" +proc describeParamList(c: PContext, n: PNode, startIdx = 1; prefer = preferName): string = + result = "Expression: " & $n + for i in startIdx..') + if candidates != "": + result.add("\n" & errButExpected & "\n" & candidates) + localError(c.config, n.info, result & "\nexpression: " & $n) + proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) = # Gives a detailed error message; this is separated from semOverloadedCall, # as semOverloadedCall is already pretty slow (and we need this information @@ -306,13 +328,15 @@ proc notFoundError*(c: PContext, n: PNode, errors: CandidateErrors) = localError(c.config, n.info, "expression '$1' cannot be called" % n[0].renderTree) return - let (prefer, candidates) = presentFailedCandidates(c, n, errors) - var result = errTypeMismatch - result.add(describeArgs(c, n, 1, prefer)) - result.add('>') - if candidates != "": - result.add("\n" & errButExpected & "\n" & candidates) - localError(c.config, n.info, result & "\nexpression: " & $n) + if verboseTypeMismatch in c.config.legacyFeatures: + legacynotFoundError(c, n, errors) + else: + let (prefer, candidates) = presentFailedCandidates(c, n, errors) + var result = "type mismatch\n" + result.add describeParamList(c, n, 1, prefer) + if candidates != "": + result.add("\n" & errExpectedPosition & "\n" & candidates) + localError(c.config, n.info, result) proc bracketNotFoundError(c: PContext; n: PNode) = var errors: CandidateErrors = @[] diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 68068cd26..75e3e5428 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -322,26 +322,32 @@ proc argTypeToString(arg: PNode; prefer: TPreferedDesc): string = else: result = arg.typ.typeToString(prefer) +template describeArgImpl(c: PContext, n: PNode, i: int, startIdx = 1; prefer = preferName) = + var arg = n[i] + if n[i].kind == nkExprEqExpr: + result.add renderTree(n[i][0]) + result.add ": " + if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo}: + # XXX we really need to 'tryExpr' here! + arg = c.semOperand(c, n[i][1]) + n[i].typ = arg.typ + n[i][1] = arg + else: + if arg.typ.isNil and arg.kind notin {nkStmtList, nkDo, nkElse, + nkOfBranch, nkElifBranch, + nkExceptBranch}: + arg = c.semOperand(c, n[i]) + n[i] = arg + if arg.typ != nil and arg.typ.kind == tyError: return + result.add argTypeToString(arg, prefer) + +proc describeArg*(c: PContext, n: PNode, i: int, startIdx = 1; prefer = preferName): string = + describeArgImpl(c, n, i, startIdx, prefer) + proc describeArgs*(c: PContext, n: PNode, startIdx = 1; prefer = preferName): string = result = "" for i in startIdx..= (1,5,1): + # to make it easier to test against older nim versions, (best effort only) + switch("filenames", "legacyRelProj") + switch("spellSuggest", "0") + +# for std/unittest +switch("define", "nimUnittestOutputLevel:PRINT_FAILURES") +switch("define", "nimUnittestColor:off") + +hint("Processing", off) From e602ebeb66986e3de65324c81253513b139c7f0e Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Thu, 29 Dec 2022 15:29:40 +0800 Subject: [PATCH 3629/3768] fixes #19997; ships changelogs directory for a release (#21200) fixes #19997; ship changelogs directory for a release --- compiler/installer.ini | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/installer.ini b/compiler/installer.ini index 094b92a68..1e57455c6 100644 --- a/compiler/installer.ini +++ b/compiler/installer.ini @@ -70,6 +70,7 @@ Files: "nimpretty" Files: "testament" Files: "nimsuggest" Files: "nimsuggest/tests/*.nim" +Files: "changelogs/*.md" [Lib] Files: "lib" From 705da9d452d19536689a32a8d4378bcce2ec320a Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 30 Dec 2022 00:06:57 +0800 Subject: [PATCH 3630/3768] fixes #14444; add `genLineDir` before assignment (#21201) * fixes #14444; add `genLineDir` before raises * add a test case * fixes differently --- compiler/ccgstmts.nim | 1 + tests/errmsgs/t14444.nim | 14 ++++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 tests/errmsgs/t14444.nim diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index fbdb9a347..9d6640fe2 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -1620,6 +1620,7 @@ proc genAsgn(p: BProc, e: PNode, fastAsgn: bool) = initLoc(a, locNone, le, OnUnknown) a.flags.incl(lfEnforceDeref) a.flags.incl(lfPrepareForMutation) + genLineDir(p, le) # it can be a nkBracketExpr, which may raise expr(p, le, a) a.flags.excl(lfPrepareForMutation) if fastAsgn: incl(a.flags, lfNoDeepCopy) diff --git a/tests/errmsgs/t14444.nim b/tests/errmsgs/t14444.nim new file mode 100644 index 000000000..143b4542e --- /dev/null +++ b/tests/errmsgs/t14444.nim @@ -0,0 +1,14 @@ +discard """ + matrix: "--hints:off" + exitcode: "1" + output: ''' +t14444.nim(13) t14444 +fatal.nim(51) sysFatal +Error: unhandled exception: index out of bounds, the container is empty [IndexDefect] +''' +""" + +when true: # bug #14444 + var i: string + i[10] = 'j' + echo i \ No newline at end of file From ebd1c678be5e0acee4fca67e1b7060234821ccf6 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 30 Dec 2022 03:15:08 +0800 Subject: [PATCH 3631/3768] fixes #21195; `std/assertions` continue to use `sysFatal` when `nimPreviewSlimSystem` is not defined (#21196) * fixes #21195; `std/assertions` continue to use `sysFatal` * try includes * make `std/assertions` self-contained * fixes tests * fixes tests --- lib/std/assertions.nim | 9 ++++++++- lib/system.nim | 28 +++------------------------- lib/system/rawquits.nim | 27 +++++++++++++++++++++++++++ tests/assert/panicoverride.nim | 15 +++++++++++++++ tests/assert/t21195.nim | 6 ++++++ tests/assert/tassert_c.nim | 4 ++-- tests/errmsgs/t9768.nim | 4 ++-- 7 files changed, 63 insertions(+), 30 deletions(-) create mode 100644 lib/system/rawquits.nim create mode 100644 tests/assert/panicoverride.nim create mode 100644 tests/assert/t21195.nim diff --git a/lib/std/assertions.nim b/lib/std/assertions.nim index 229033c58..a249d7751 100644 --- a/lib/std/assertions.nim +++ b/lib/std/assertions.nim @@ -7,6 +7,10 @@ # distribution, for details about the copyright. # +when not defined(nimPreviewSlimSystem) and not declared(sysFatal): + include "system/rawquits" + include "system/fatal" + ## This module implements assertion handling. import std/private/miscdollars @@ -26,7 +30,10 @@ proc `$`(info: InstantiationInfo): string = proc raiseAssert*(msg: string) {.noinline, noreturn, nosinks.} = ## Raises an `AssertionDefect` with `msg`. - raise newException(AssertionDefect, msg) + when defined(nimPreviewSlimSystem): + raise newException(AssertionDefect, msg) + else: + sysFatal(AssertionDefect, msg) proc failedAssertImpl*(msg: string) {.raises: [], tags: [].} = ## Raises an `AssertionDefect` with `msg`, but this is hidden diff --git a/lib/system.nim b/lib/system.nim index e9c9ebfeb..d8fafc0ef 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -1081,31 +1081,9 @@ proc align(address, alignment: int): int = else: result = (address + (alignment - 1)) and not (alignment - 1) -when defined(nimNoQuit): - proc rawQuit(errorcode: int = QuitSuccess) = discard "ignoring quit" - -elif defined(genode): - import genode/env - - var systemEnv {.exportc: runtimeEnvSym.}: GenodeEnvPtr - - type GenodeEnv* = GenodeEnvPtr - ## Opaque type representing Genode environment. - - proc rawQuit(env: GenodeEnv; errorcode: int) {.magic: "Exit", noreturn, - importcpp: "#->parent().exit(@); Genode::sleep_forever()", header: "".} - - proc rawQuit(errorcode: int = QuitSuccess) {.inline, noreturn.} = - systemEnv.rawQuit(errorcode) - - -elif defined(js) and defined(nodejs) and not defined(nimscript): - proc rawQuit(errorcode: int = QuitSuccess) {.magic: "Exit", - importc: "process.exit", noreturn.} - -else: - proc rawQuit(errorcode: cint) {. - magic: "Exit", importc: "exit", header: "", noreturn.} +include system/rawquits +when defined(genode): + export GenodeEnv template sysAssert(cond: bool, msg: string) = when defined(useSysAssert): diff --git a/lib/system/rawquits.nim b/lib/system/rawquits.nim new file mode 100644 index 000000000..f0ead10c6 --- /dev/null +++ b/lib/system/rawquits.nim @@ -0,0 +1,27 @@ +import system/ctypes + +when defined(nimNoQuit): + proc rawQuit(errorcode: int = QuitSuccess) = discard "ignoring quit" + +elif defined(genode): + import genode/env + + var systemEnv {.exportc: runtimeEnvSym.}: GenodeEnvPtr + + type GenodeEnv = GenodeEnvPtr + ## Opaque type representing Genode environment. + + proc rawQuit(env: GenodeEnv; errorcode: int) {.magic: "Exit", noreturn, + importcpp: "#->parent().exit(@); Genode::sleep_forever()", header: "".} + + proc rawQuit(errorcode: int = QuitSuccess) {.inline, noreturn.} = + systemEnv.rawQuit(errorcode) + + +elif defined(js) and defined(nodejs) and not defined(nimscript): + proc rawQuit(errorcode: int = QuitSuccess) {.magic: "Exit", + importc: "process.exit", noreturn.} + +else: + proc rawQuit(errorcode: cint) {. + magic: "Exit", importc: "exit", header: "", noreturn.} \ No newline at end of file diff --git a/tests/assert/panicoverride.nim b/tests/assert/panicoverride.nim new file mode 100644 index 000000000..53ad64215 --- /dev/null +++ b/tests/assert/panicoverride.nim @@ -0,0 +1,15 @@ +# panicoverride.nim + +proc printf(fmt: cstring) {.varargs, importc, header:"stdio.h".} +proc exit(code: cint) {.importc, header:"stdlib.h".} + +{.push stack_trace: off, profiler:off.} + +proc rawoutput(s: cstring) = + printf("RAW: %s\n", s) + +proc panic(s: cstring) {.noreturn.} = + printf("PANIC: %s\n", s) + exit(0) + +{.pop.} \ No newline at end of file diff --git a/tests/assert/t21195.nim b/tests/assert/t21195.nim new file mode 100644 index 000000000..b690d22a0 --- /dev/null +++ b/tests/assert/t21195.nim @@ -0,0 +1,6 @@ +discard """ + matrix: "--verbosity:0 --os:standalone --mm:none" +""" +# bug #21195 +var n = 11 +assert(n == 12) diff --git a/tests/assert/tassert_c.nim b/tests/assert/tassert_c.nim index 110a9aabf..4d49a6035 100644 --- a/tests/assert/tassert_c.nim +++ b/tests/assert/tassert_c.nim @@ -1,8 +1,8 @@ discard """ - cmd: "nim $target $options --excessiveStackTrace:off $file" + cmd: "nim $target $options -d:nimPreviewSlimSystem --excessiveStackTrace:off $file" output: '''true''' """ - +import std/assertions const expected = """ tassert_c.nim(35) tassert_c tassert_c.nim(34) foo diff --git a/tests/errmsgs/t9768.nim b/tests/errmsgs/t9768.nim index 058d297b3..b5ff58367 100644 --- a/tests/errmsgs/t9768.nim +++ b/tests/errmsgs/t9768.nim @@ -1,14 +1,14 @@ discard """ errormsg: "unhandled exception: t9768.nim(24, 3) `a < 4` [AssertionDefect]" file: "std/assertions.nim" + matrix: "-d:nimPreviewSlimSystem" nimout: ''' stack trace: (most recent call last) t9768.nim(29, 33) main t9768.nim(24, 11) foo1 ''' """ - - +import std/assertions From c598d0b6eccd3133bfca710fc5fcc4576501862c Mon Sep 17 00:00:00 2001 From: Bung Date: Fri, 30 Dec 2022 07:50:12 +0800 Subject: [PATCH 3632/3768] fix #15117 zero size array cause incorrect codegen for VCC compiler (#21197) fix #15117 --- compiler/ccgexprs.nim | 2 ++ tests/array/t15117.nim | 27 +++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) create mode 100644 tests/array/t15117.nim diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index e2885a294..844a37997 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -3331,6 +3331,8 @@ proc genConstObjConstr(p: BProc; n: PNode; isConst: bool; result: var Rope) = proc genConstSimpleList(p: BProc, n: PNode; isConst: bool; result: var Rope) = result.add "{" + if p.vccAndC and n.len == 0 and n.typ.kind == tyArray: + getDefaultValue(p, n.typ[1], n.info, result) for i in 0.. 0: result.add ",\n" diff --git a/tests/array/t15117.nim b/tests/array/t15117.nim new file mode 100644 index 000000000..157b04bee --- /dev/null +++ b/tests/array/t15117.nim @@ -0,0 +1,27 @@ +discard """ + matrix: "--cc:vcc" + disabled: "linux" + disabled: "bsd" + disabled: "osx" + disabled: "unix" + disabled: "posix" +""" +{.experimental: "views".} + +let a: array[0, byte] = [] +discard a + +type B = object + a:int +let b: array[0, B] = [] +let c: array[0, ptr B] = [] +let d: array[0, ref B] = [] +discard b +discard c +discard d + +discard default(array[0, B]) + +type + View1 = openArray[byte] +discard default(View1) From e5639408d55c3b9164790bee37580a74a15b666f Mon Sep 17 00:00:00 2001 From: rockcavera Date: Fri, 30 Dec 2022 05:20:32 -0300 Subject: [PATCH 3633/3768] avoiding unnecessary allocation for dollar IpAddress (#21199) * avoiding allocating an unnecessary byte for IPv4 The inet.h file uses 16 as the string in C needs the last null byte https://github.com/torvalds/linux/blob/1b929c02afd37871d5afb9d498426f83432e71c2/include/linux/inet.h#L49 However, strings in Nim do not need this. So one byte is being allocated unnecessary and will never be used. * avoid unnecessary allocation in IPv6 dollar It is currently allocating 48 bytes. However, the Nim implementation for IPv6 will print a maximum of 39 characters. Nim does not implement IPv6 "0000:0000:0000:0000:0000:ffff:255.255.255.255" (45 characters) nor "0000:0000:0000:0000:0000:ffff:255.255.255.255%3" (47 characters). The indication in inet.h for 48 is due to the maximum use of 47 characters of a C string that needs a null byte at the end. So 48. https://github.com/torvalds/linux/blob/1b929c02afd37871d5afb9d498426f83432e71c2/include/linux/inet.h#L50 --- lib/pure/net.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pure/net.nim b/lib/pure/net.nim index be9f2e48c..76a39226f 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -1921,7 +1921,7 @@ proc `$`*(address: IpAddress): string = ## Converts an IpAddress into the textual representation case address.family of IpAddressFamily.IPv4: - result = newStringOfCap(16) + result = newStringOfCap(15) result.addInt address.address_v4[0] result.add '.' result.addInt address.address_v4[1] @@ -1930,7 +1930,7 @@ proc `$`*(address: IpAddress): string = result.add '.' result.addInt address.address_v4[3] of IpAddressFamily.IPv6: - result = newStringOfCap(48) + result = newStringOfCap(39) var currentZeroStart = -1 currentZeroCount = 0 From 4032eb4baafcfb4a1cb8fd41a474daac70103856 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Sun, 1 Jan 2023 16:51:58 +0800 Subject: [PATCH 3634/3768] fixes #20906; update copyright year [backport 1.6] (#21210) --- compiler/options.nim | 2 +- copying.txt | 2 +- readme.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler/options.nim b/compiler/options.nim index ded084ae4..47e8155f8 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -24,7 +24,7 @@ const useEffectSystem* = true useWriteTracking* = false hasFFI* = defined(nimHasLibFFI) - copyrightYear* = "2022" + copyrightYear* = "2023" nimEnableCovariance* = defined(nimEnableCovariance) diff --git a/copying.txt b/copying.txt index 60b6a0217..ae3a162a7 100644 --- a/copying.txt +++ b/copying.txt @@ -1,7 +1,7 @@ ===================================================== Nim -- a Compiler for Nim. https://nim-lang.org/ -Copyright (C) 2006-2022 Andreas Rumpf. All rights reserved. +Copyright (C) 2006-2023 Andreas Rumpf. All rights reserved. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/readme.md b/readme.md index a0f47eb41..325e8fff9 100644 --- a/readme.md +++ b/readme.md @@ -203,7 +203,7 @@ Nim. You are explicitly permitted to develop commercial applications using Nim. Please read the [copying.txt](copying.txt) file for more details. -Copyright © 2006-2022 Andreas Rumpf, all rights reserved. +Copyright © 2006-2023 Andreas Rumpf, all rights reserved. [nim-site]: https://nim-lang.org [nim-forum]: https://forum.nim-lang.org From 5b20f0685c71e68ab08f7a1941f7e160d91e538f Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Sun, 1 Jan 2023 17:25:04 +0800 Subject: [PATCH 3635/3768] fixes pragmas reorder (#21205) --- compiler/reorder.nim | 1 + tests/pragmas/tpragmas_reorder.nim | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 tests/pragmas/tpragmas_reorder.nim diff --git a/compiler/reorder.nim b/compiler/reorder.nim index 4fabf9041..4ad3f1219 100644 --- a/compiler/reorder.nim +++ b/compiler/reorder.nim @@ -106,6 +106,7 @@ proc computeDeps(cache: IdentCache; n: PNode, declares, uses: var IntSet; topLev if a.kind == nkExprColonExpr and a[0].kind == nkIdent and a[0].ident.s == "pragma": # user defined pragma decl(a[1]) + for i in 1.. Date: Mon, 2 Jan 2023 08:16:18 +0000 Subject: [PATCH 3636/3768] Update nim.cfg (#21204) tlsEmulation:on under NetBSD-10Beta and NetBSD-current produces an executable which crashes immediately as follows: Core was generated by `koch'. Program terminated with signal SIGSEGV, Segmentation fault. #0 0x000000000047b4c2 in nimZeroMem () (gdb) bt #0 0x000000000047b4c2 in nimZeroMem () #1 0x00000000004897b2 in threadVarAlloc__system_2162 () #2 0x000000000048980e in initThreadVarsEmulation () #3 0x0000000000489848 in PreMain () #4 0x000000000048986a in NimMain () #5 0x00000000004898a9 in main () I can't speak about the other BSDs. --- config/nim.cfg | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/config/nim.cfg b/config/nim.cfg index 1601ebe08..f7b7c119a 100644 --- a/config/nim.cfg +++ b/config/nim.cfg @@ -181,10 +181,9 @@ nimblepath="$home/.nimble/pkgs/" gcc.maxerrorsimpl = "-fmax-errors=3" -@if freebsd: +@if freebsd or netbsd: tlsEmulation:off @elif bsd: - # at least NetBSD has problems with thread local storage: tlsEmulation:on @end From cf1b16ef8ba1c9f8411e551d317784c5ffaece0c Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Mon, 2 Jan 2023 10:39:17 +0100 Subject: [PATCH 3637/3768] minor code cleanups (#21215) --- lib/posix/posix.nim | 8 ++++---- lib/posix/posix_utils.nim | 6 +++--- lib/std/enumerate.nim | 2 +- lib/std/enumutils.nim | 2 +- lib/std/envvars.nim | 35 +++++++++++++++++------------------ lib/std/oserrors.nim | 15 ++++++--------- 6 files changed, 32 insertions(+), 36 deletions(-) diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index ddf00cbb7..43346608d 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -1140,12 +1140,12 @@ type ## The getrlimit() and setrlimit() system calls get and set resource limits respectively. ## Each resource has an associated soft and hard limit, as defined by the RLimit structure -proc setrlimit*(resource: cint, rlp: var RLimit): cint - {.importc: "setrlimit",header: "".} +proc setrlimit*(resource: cint, rlp: var RLimit): cint {. + importc: "setrlimit", header: "".} ## The setrlimit() system calls sets resource limits. -proc getrlimit*(resource: cint, rlp: var RLimit): cint - {.importc: "getrlimit",header: "".} +proc getrlimit*(resource: cint, rlp: var RLimit): cint {. + importc: "getrlimit", header: "".} ## The getrlimit() system call gets resource limits. when defined(nimHasStyleChecks): diff --git a/lib/posix/posix_utils.nim b/lib/posix/posix_utils.nim index 92fe0940d..7e4a2eeda 100644 --- a/lib/posix/posix_utils.nim +++ b/lib/posix/posix_utils.nim @@ -43,8 +43,8 @@ proc uname*(): Uname = result.machine = charArrayToString u.machine proc fsync*(fd: int) = - ## synchronize a file's buffer cache to the storage device - if fsync(fd.cint) != 0: + ## synchronize a file's buffer cache to the storage device + if fsync(fd.cint) != 0: raise newException(OSError, $strerror(errno)) proc stat*(path: string): Stat = @@ -90,7 +90,7 @@ proc mkstemp*(prefix: string, suffix=""): (string, File) = ## Returns the filename and a file opened in r/w mode. var tmpl = cstring(prefix & "XXXXXX" & suffix) let fd = - if len(suffix)==0: + if len(suffix) == 0: when declared(mkostemp): mkostemp(tmpl, O_CLOEXEC) else: diff --git a/lib/std/enumerate.nim b/lib/std/enumerate.nim index a8f0e1ba7..4f0161b7c 100644 --- a/lib/std/enumerate.nim +++ b/lib/std/enumerate.nim @@ -21,7 +21,7 @@ macro enumerate*(x: ForLoopStmt): untyped {.since: (1, 3).} = ## The default starting count `0` can be manually overridden if needed. runnableExamples: let a = [10, 20, 30] - var b: seq[(int, int)] + var b: seq[(int, int)] = @[] for i, x in enumerate(a): b.add((i, x)) assert b == @[(0, 10), (1, 20), (2, 30)] diff --git a/lib/std/enumutils.nim b/lib/std/enumutils.nim index 9d4ff1bcf..59fb112ed 100644 --- a/lib/std/enumutils.nim +++ b/lib/std/enumutils.nim @@ -112,9 +112,9 @@ const invalidSlot = uint8.high proc genLookup[T: typedesc[HoleyEnum]](_: T): auto = const n = span(T) - var ret: array[n, uint8] var i = 0 assert n <= invalidSlot.int + var ret {.noinit.}: array[n, uint8] for ai in mitems(ret): ai = invalidSlot for ai in items(T): ret[ai.ord - T.low.ord] = uint8(i) diff --git a/lib/std/envvars.nim b/lib/std/envvars.nim index ce90f66ba..ab5c9f06e 100644 --- a/lib/std/envvars.nim +++ b/lib/std/envvars.nim @@ -94,8 +94,10 @@ when not defined(nimscript): assert getEnv("unknownEnv", "doesn't exist") == "doesn't exist" let env = getEnvImpl(key) - if env == nil: return default - result = $env + if env == nil: + result = default + else: + result = $env proc existsEnv*(key: string): bool {.tags: [ReadEnvEffect].} = ## Checks whether the environment variable named `key` exists. @@ -109,7 +111,7 @@ when not defined(nimscript): runnableExamples: assert not existsEnv("unknownEnv") - return getEnvImpl(key) != nil + result = getEnvImpl(key) != nil proc putEnv*(key, val: string) {.tags: [WriteEnvEffect].} = ## Sets the value of the `environment variable`:idx: named `key` to `val`. @@ -140,7 +142,7 @@ when not defined(nimscript): ## * `envPairs iterator`_ template bail = raiseOSError(osLastError(), key) when defined(windows): - #[ + #[ # https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/putenv-s-wputenv-s?view=msvc-160 > You can remove a variable from the environment by specifying an empty string (that is, "") for value_string note that nil is not legal @@ -177,20 +179,17 @@ when not defined(nimscript): iterator envPairsImpl(): tuple[key, value: string] {.tags: [ReadEnvEffect].} = when defined(windows): - block implBlock: - template impl(get_fun, typ, size, zero, free_fun) = - let env = get_fun() - var e = env - if e == nil: break implBlock - while true: - let eend = strEnd(e) - let kv = $e - let p = find(kv, '=') - yield (substr(kv, 0, p-1), substr(kv, p+1)) - e = cast[typ](cast[int](eend)+size) - if typeof(zero)(eend[1]) == zero: break - discard free_fun(env) - impl(getEnvironmentStringsW, WideCString, 2, 0, freeEnvironmentStringsW) + let env = getEnvironmentStringsW() + var e = env + if e != nil: + while true: + let eend = strEnd(e) + let kv = $e + let p = find(kv, '=') + yield (substr(kv, 0, p-1), substr(kv, p+1)) + e = cast[WideCString](cast[ByteAddress](eend)+2) + if int(eend[1]) == 0: break + discard freeEnvironmentStringsW(env) else: var i = 0 when defined(macosx) and not defined(ios) and not defined(emscripten): diff --git a/lib/std/oserrors.nim b/lib/std/oserrors.nim index 8d06c41da..a641a7f47 100644 --- a/lib/std/oserrors.nim +++ b/lib/std/oserrors.nim @@ -75,17 +75,14 @@ proc newOSError*( ## See also: ## * `osErrorMsg proc`_ ## * `osLastError proc`_ - var e: owned(ref OSError); new(e) - e.errorCode = errorCode.int32 - e.msg = osErrorMsg(errorCode) + result = (ref OSError)(errorCode: errorCode.int32, msg: osErrorMsg(errorCode)) if additionalInfo.len > 0: - if e.msg.len > 0 and e.msg[^1] != '\n': e.msg.add '\n' - e.msg.add "Additional info: " - e.msg.add additionalInfo + if result.msg.len > 0 and result.msg[^1] != '\n': result.msg.add '\n' + result.msg.add "Additional info: " + result.msg.add additionalInfo # don't add trailing `.` etc, which negatively impacts "jump to file" in IDEs. - if e.msg == "": - e.msg = "unknown OS error" - return e + if result.msg == "": + result.msg = "unknown OS error" proc raiseOSError*(errorCode: OSErrorCode, additionalInfo = "") {.noinline.} = ## Raises an `OSError exception `_. From 3b965f463b41eace340393af636e4abcd9c223d6 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 2 Jan 2023 22:10:26 +0800 Subject: [PATCH 3638/3768] bump csource_v2 to include fix for linking parameter maximum length (#21216) > If accepted, I believe it should be used in csources_v2 and also backport 2.0 ref https://github.com/nim-lang/Nim/pull/21186 --- config/build_config.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/build_config.txt b/config/build_config.txt index 286e0dc2a..66390e695 100644 --- a/config/build_config.txt +++ b/config/build_config.txt @@ -2,4 +2,4 @@ nim_comment="key-value pairs for windows/posix bootstrapping build scripts" nim_csourcesDir=csources_v2 nim_csourcesUrl=https://github.com/nim-lang/csources_v2.git nim_csourcesBranch=master -nim_csourcesHash=b66c420697c574be18a20dd4720248a715b4287e +nim_csourcesHash=86742fb02c6606ab01a532a0085784effb2e753e From 81b7f9108f139bbf237f3e121fdbe9ff32a7193f Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Tue, 3 Jan 2023 15:15:10 +0800 Subject: [PATCH 3639/3768] fixes #21207; reports redefinition error in the definition of enums (#21217) * fixes #21207; reports redefinition in the enums * add a test --- compiler/semtypes.nim | 4 ++-- tests/enum/tredefinition.nim | 9 +++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 tests/enum/tredefinition.nim diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index 534861e97..a8b147eef 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -73,7 +73,7 @@ proc semEnum(c: PContext, n: PNode, prev: PType): PType = rawAddSon(result, base) let isPure = result.sym != nil and sfPure in result.sym.flags var symbols: TStrTable - if isPure: initStrTable(symbols) + initStrTable(symbols) var hasNull = false for i in 1.. Date: Wed, 4 Jan 2023 12:10:44 -0800 Subject: [PATCH 3640/3768] make it printer friendly (#21218) --- doc/nimdoc.css | 9 +++++++++ nimdoc/testproject/expected/nimdoc.out.css | 9 +++++++++ 2 files changed, 18 insertions(+) diff --git a/doc/nimdoc.css b/doc/nimdoc.css index f924f5a36..1417d9eff 100644 --- a/doc/nimdoc.css +++ b/doc/nimdoc.css @@ -147,6 +147,15 @@ body { box-sizing: border-box; margin-left: 1%; } +@media print { + #global-links, .link-seesrc, .theme-switch-wrapper, #searchInputDiv, .search-groupby { + display:none; + } + .columns { + width:100% !important; + } +} + .column:first-child, .columns:first-child { margin-left: 0; } diff --git a/nimdoc/testproject/expected/nimdoc.out.css b/nimdoc/testproject/expected/nimdoc.out.css index f924f5a36..1417d9eff 100644 --- a/nimdoc/testproject/expected/nimdoc.out.css +++ b/nimdoc/testproject/expected/nimdoc.out.css @@ -147,6 +147,15 @@ body { box-sizing: border-box; margin-left: 1%; } +@media print { + #global-links, .link-seesrc, .theme-switch-wrapper, #searchInputDiv, .search-groupby { + display:none; + } + .columns { + width:100% !important; + } +} + .column:first-child, .columns:first-child { margin-left: 0; } From 2620da9bf9994d09da3d5b31b29c95855fb79a41 Mon Sep 17 00:00:00 2001 From: Andrey Makarov Date: Wed, 4 Jan 2023 23:19:01 +0300 Subject: [PATCH 3641/3768] docgen: implement cross-document links (#20990) * docgen: implement cross-document links Fully implements https://github.com/nim-lang/RFCs/issues/125 Follow-up of: https://github.com/nim-lang/Nim/pull/18642 (for internal links) and https://github.com/nim-lang/Nim/issues/20127. Overview -------- Explicit import-like directive is required, called `.. importdoc::`. (the syntax is % RST, Markdown will use it for a while). Then one can reference any symbols/headings/anchors, as if they were in the local file (but they will be prefixed with a module name or markup document in link text). It's possible to reference anything from anywhere (any direction in `.nim`/`.md`/`.rst` files). See `doc/docgen.md` for full description. Working is based on `.idx` files, hence one needs to generate all `.idx` beforehand. A dedicated option `--index:only` is introduced (and a separate stage for `--index:only` is added to `kochdocs.nim`). Performance note ---------------- Full run for `./koch docs` now takes 185% of the time before this PR. (After: 315 s, before: 170 s on my PC). All the time seems to be spent on `--index:only` run, which takes almost as much (85%) of normal doc run -- it seems that most time is spent on file parsing, turning off HTML generation phase has not helped much. (One could avoid it by specifying list of files that can be referenced and pre-processing only them. But it can become error-prone and I assume that these linke will be **everywhere** in the repository anyway, especially considering https://github.com/nim-lang/RFCs/issues/478. So every `.nim`/`.md` file is processed for `.idx` first). But that's all without significant part of repository converted to cross-module auto links. To estimate impact I checked the time for `doc`ing a few files (after all indexes have been generated), and everywhere difference was **negligible**. E.g. for `lib/std/private/osfiles.nim` that `importdoc`s large `os.idx` and hence should have been a case with relatively large performance impact, but: * After: 0.59 s. * Before: 0.59 s. So Nim compiler works so slow that doc part basically does not matter :-) Testing ------- 1) added `extlinks` test to `nimdoc/` 2) checked that `theindex.html` is still correct 2) fixed broken auto-links for modules that were derived from `os.nim` by adding appropriate ``importdoc`` Implementation note ------------------- Parsing and formating of `.idx` entries is moved into a dedicated `rstidx.nim` module from `rstgen.nim`. `.idx` file format changed: * fields are not escaped in most cases because we need original strings for referencing, not HTML ones (the exception is linkTitle for titles and headings). Escaping happens later -- on the stage of `rstgen` buildIndex, etc. * all lines have fixed number of columns 6 * added discriminator tag as a first column, it always allows distinguish Nim/markup entries, titles/headings, etc. `rstgen` does not rely any more (in most cases) on ad-hoc logic to determine what type each entry is. * there is now always a title entry added at the first line. * add a line number as 6th column * linkTitle (4th) column has a different format: before it was like `module: funcName()`, now it's `proc funcName()`. (This format is also propagated to `theindex.html` and search results, I kept it that way since I like it more though it's discussible.) This column is what used for Nim symbols resolution. * also changed details on column format for headings and titles: "keyword" is original, "linkTitle" is HTML one * fix paths on Windows + more clear code * Update compiler/docgen.nim Co-authored-by: Andreas Rumpf * Handle .md and .nim paths uniformly in findRefFile * handle titles better + more comments * don't allow markup overwrite index title for .nim files Co-authored-by: Andreas Rumpf --- compiler/commands.nim | 8 +- compiler/docgen.nim | 200 ++++++++---- compiler/lineinfos.nim | 2 + compiler/options.nim | 2 + doc/advopt.txt | 4 +- doc/docgen.md | 309 ++++++++++++++++-- doc/markdown_rst.md | 84 +++++ lib/core/macros.nim | 2 + lib/packages/docutils/dochelpers.nim | 27 +- lib/packages/docutils/rst.nim | 263 +++++++++++++-- lib/packages/docutils/rstgen.nim | 200 ++++-------- lib/packages/docutils/rstidx.nim | 138 ++++++++ lib/pure/os.nim | 2 + lib/std/appdirs.nim | 2 + lib/std/private/osappdirs.nim | 2 + lib/std/private/oscommon.nim | 2 + lib/std/private/osdirs.nim | 2 + lib/std/private/osfiles.nim | 3 +- lib/std/private/ospaths2.nim | 2 + lib/std/private/osseps.nim | 2 + lib/std/private/ossymlinks.nim | 1 + lib/std/symlinks.nim | 3 +- nimdoc/extlinks/project/doc/manual.md | 17 + .../extlinks/project/expected/_._/util.html | 104 ++++++ nimdoc/extlinks/project/expected/_._/util.idx | 2 + .../extlinks/project/expected/doc/manual.html | 44 +++ .../extlinks/project/expected/doc/manual.idx | 3 + nimdoc/extlinks/project/expected/main.html | 144 ++++++++ nimdoc/extlinks/project/expected/main.idx | 4 + .../project/expected/sub/submodule.html | 130 ++++++++ .../project/expected/sub/submodule.idx | 3 + .../extlinks/project/expected/theindex.html | 58 ++++ nimdoc/extlinks/project/main.nim | 23 ++ nimdoc/extlinks/project/sub/submodule.nim | 13 + nimdoc/extlinks/util.nim | 2 + .../test_out_index_dot_html/expected/foo.idx | 3 +- .../expected/theindex.html | 2 +- nimdoc/tester.nim | 63 ++++ .../expected/subdir/subdir_b/utils.html | 6 +- .../expected/subdir/subdir_b/utils.idx | 87 ++--- nimdoc/testproject/expected/testproject.idx | 131 ++++---- nimdoc/testproject/expected/theindex.html | 196 +++++------ tests/stdlib/tdochelpers.nim | 16 + tests/stdlib/trst.nim | 2 +- tools/kochdocs.nim | 41 ++- 45 files changed, 1863 insertions(+), 491 deletions(-) create mode 100644 lib/packages/docutils/rstidx.nim create mode 100644 nimdoc/extlinks/project/doc/manual.md create mode 100644 nimdoc/extlinks/project/expected/_._/util.html create mode 100644 nimdoc/extlinks/project/expected/_._/util.idx create mode 100644 nimdoc/extlinks/project/expected/doc/manual.html create mode 100644 nimdoc/extlinks/project/expected/doc/manual.idx create mode 100644 nimdoc/extlinks/project/expected/main.html create mode 100644 nimdoc/extlinks/project/expected/main.idx create mode 100644 nimdoc/extlinks/project/expected/sub/submodule.html create mode 100644 nimdoc/extlinks/project/expected/sub/submodule.idx create mode 100644 nimdoc/extlinks/project/expected/theindex.html create mode 100644 nimdoc/extlinks/project/main.nim create mode 100644 nimdoc/extlinks/project/sub/submodule.nim create mode 100644 nimdoc/extlinks/util.nim diff --git a/compiler/commands.nim b/compiler/commands.nim index a252f505f..73140036a 100644 --- a/compiler/commands.nim +++ b/compiler/commands.nim @@ -820,7 +820,13 @@ proc processSwitch*(switch, arg: string, pass: TCmdLinePass, info: TLineInfo; if conf != nil: conf.headerFile = arg incl(conf.globalOptions, optGenIndex) of "index": - processOnOffSwitchG(conf, {optGenIndex}, arg, pass, info) + case arg.normalize + of "", "on": conf.globalOptions.incl {optGenIndex} + of "only": conf.globalOptions.incl {optGenIndexOnly, optGenIndex} + of "off": conf.globalOptions.excl {optGenIndex, optGenIndexOnly} + else: localError(conf, info, errOnOrOffExpectedButXFound % arg) + of "noimportdoc": + processOnOffSwitchG(conf, {optNoImportdoc}, arg, pass, info) of "import": expectArg(conf, switch, arg, pass, info) if pass in {passCmd2, passPP}: diff --git a/compiler/docgen.nim b/compiler/docgen.nim index f8a804cec..cab334e7a 100644 --- a/compiler/docgen.nim +++ b/compiler/docgen.nim @@ -7,13 +7,17 @@ # distribution, for details about the copyright. # -# This is the documentation generator. Cross-references are generated -# by knowing how the anchors are going to be named. +## This is the Nim documentation generator. Cross-references are generated +## by knowing how the anchors are going to be named. +## +## .. importdoc:: ../docgen.md +## +## For corresponding users' documentation see [Nim DocGen Tools Guide]. import ast, strutils, strtabs, algorithm, sequtils, options, msgs, os, idents, wordrecg, syntaxes, renderer, lexer, - packages/docutils/[rst, rstgen, dochelpers], + packages/docutils/[rst, rstidx, rstgen, dochelpers], json, xmltree, trees, types, typesrenderer, astalgo, lineinfos, intsets, pathutils, tables, nimpaths, renderverbatim, osproc, packages @@ -91,7 +95,7 @@ type jEntriesFinal: JsonNode # final JSON after RST pass 2 and rendering types: TStrTable sharedState: PRstSharedState - standaloneDoc: bool + standaloneDoc: bool # is markup (.rst/.md) document? conf*: ConfigRef cache*: IdentCache exampleCounter: int @@ -225,7 +229,7 @@ proc attachToType(d: PDoc; p: PSym): PSym = if params.len > 0: check(0) for i in 2.."), "itemSymOrIDEnc", symbolOrIdEnc]) - # Ironically for types the complexSymbol is *cleaner* than the plainName - # because it doesn't include object fields or documentation comments. So we - # use the plain one for callable elements, and the complex for the rest. - var linkTitle = changeFileExt(extractFilename(d.filename), "") & ": " - if n.kind in routineDefs: linkTitle.add(xmltree.escape(plainName.strip)) - else: linkTitle.add(xmltree.escape(complexSymbol.strip)) - - setIndexTerm(d[], external, symbolOrId, name, linkTitle, - xmltree.escape(plainDocstring.docstringSummary)) + setIndexTerm(d[], ieNim, htmlFile = external, id = symbolOrId, term = name, + linkTitle = detailedName, + linkDesc = xmltree.escape(plainDocstring.docstringSummary), + line = n.info.line.int) if k == skType and nameNode.kind == nkSym: d.types.strTableAdd nameNode.sym proc genJsonItem(d: PDoc, n, nameNode: PNode, k: TSymKind): JsonItem = if not isVisible(d, nameNode): return var - name = getName(d, nameNode) + name = getNameEsc(d, nameNode) comm = genRecComment(d, n) r: TSrcGen initTokRender(r, n, {renderNoBody, renderNoComments, renderDocComments, renderExpandUsing}) @@ -1337,9 +1369,31 @@ proc generateDoc*(d: PDoc, n, orig: PNode, docFlags: DocFlags = kDefault) = proc overloadGroupName(s: string, k: TSymKind): string = ## Turns a name like `f` into anchor `f-procs-all` - #s & " " & k.toHumanStr & "s all" s & "-" & k.toHumanStr & "s-all" +proc setIndexTitle(d: PDoc, useMetaTitle: bool) = + let titleKind = if d.standaloneDoc: ieMarkupTitle else: ieNimTitle + let external = AbsoluteFile(d.destFile) + .relativeTo(d.conf.outDir, '/') + .changeFileExt(HtmlExt) + .string + var term, linkTitle: string + if useMetaTitle and d.meta[metaTitle].len != 0: + term = d.meta[metaTitleRaw] + linkTitle = d.meta[metaTitleRaw] + else: + let filename = extractFilename(d.filename) + term = + if d.standaloneDoc: filename # keep .rst/.md extension + else: changeFileExt(filename, "") # rm .nim extension + linkTitle = + if d.standaloneDoc: term # keep .rst/.md extension + else: canonicalImport(d.conf, AbsoluteFile d.filename) + if not d.standaloneDoc: + linkTitle = "module " & linkTitle + setIndexTerm(d[], titleKind, htmlFile = external, id = "", + term = term, linkTitle = linkTitle) + proc finishGenerateDoc*(d: var PDoc) = ## Perform 2nd RST pass for resolution of links/footnotes/headings... # copy file map `filenames` to ``rstgen.nim`` for its warnings @@ -1352,7 +1406,24 @@ proc finishGenerateDoc*(d: var PDoc) = firstRst = fragment.rst break d.hasToc = d.hasToc or d.sharedState.hasToc - preparePass2(d.sharedState, firstRst) + # in --index:only mode we do NOT want to load other .idx, only write ours: + let importdoc = optGenIndexOnly notin d.conf.globalOptions and + optNoImportdoc notin d.conf.globalOptions + preparePass2(d.sharedState, firstRst, importdoc) + + if optGenIndexOnly in d.conf.globalOptions: + # Top-level doc.comments may contain titles and :idx: statements: + for fragment in d.modDescPre: + if fragment.isRst: + traverseForIndex(d[], fragment.rst) + setIndexTitle(d, useMetaTitle = d.standaloneDoc) + # Symbol-associated doc.comments may contain :idx: statements: + for k in TSymKind: + for _, overloadChoices in d.section[k].secItems: + for item in overloadChoices: + for fragment in item.descRst: + if fragment.isRst: + traverseForIndex(d[], fragment.rst) # add anchors to overload groups before RST resolution for k in TSymKind: @@ -1362,14 +1433,25 @@ proc finishGenerateDoc*(d: var PDoc) = let refn = overloadGroupName(plainName, k) let tooltip = "$1 ($2 overloads)" % [ k.toHumanStr & " " & plainName, $overloadChoices.len] - addAnchorNim(d.sharedState, refn, tooltip, + let name = nimIdentBackticksNormalize(plainName) + # save overload group to ``.idx`` + let external = d.destFile.AbsoluteFile.relativeTo(d.conf.outDir, '/'). + changeFileExt(HtmlExt).string + setIndexTerm(d[], ieNimGroup, htmlFile = external, id = refn, + term = name, linkTitle = k.toHumanStr, + linkDesc = "", line = overloadChoices[0].info.line.int) + if optGenIndexOnly in d.conf.globalOptions: continue + addAnchorNim(d.sharedState, external=false, refn, tooltip, LangSymbol(symKind: k.toHumanStr, - name: nimIdentBackticksNormalize(plainName), + name: name, isGroup: true), priority = symbolPriority(k), # select index `0` just to have any meaningful warning: info = overloadChoices[0].info) + if optGenIndexOnly in d.conf.globalOptions: + return + # Finalize fragments of ``.nim`` or ``.rst`` file proc renderItemPre(d: PDoc, fragments: ItemPre, result: var string) = for f in fragments: @@ -1421,6 +1503,9 @@ proc finishGenerateDoc*(d: var PDoc) = d.jEntriesFinal.add entry.json # generates docs + setIndexTitle(d, useMetaTitle = d.standaloneDoc) + completePass2(d.sharedState) + proc add(d: PDoc; j: JsonItem) = if j.json != nil or j.rst != nil: d.jEntriesPre.add j @@ -1467,7 +1552,7 @@ proc generateJson*(d: PDoc, n: PNode, includeComments: bool = true) = else: discard proc genTagsItem(d: PDoc, n, nameNode: PNode, k: TSymKind): string = - result = getName(d, nameNode) & "\n" + result = getNameEsc(d, nameNode) & "\n" proc generateTags*(d: PDoc, n: PNode, r: var string) = case n.kind @@ -1574,13 +1659,7 @@ proc genOutFile(d: PDoc, groupedToc = false): string = # Extract the title. Non API modules generate an entry in the index table. if d.meta[metaTitle].len != 0: title = d.meta[metaTitle] - let external = AbsoluteFile(d.destFile) - .relativeTo(d.conf.outDir, '/') - .changeFileExt(HtmlExt) - .string - setIndexTerm(d[], external, "", title) else: - # Modules get an automatic title for the HTML, but no entry in the index. title = canonicalImport(d.conf, AbsoluteFile d.filename) title = esc(d.target, title) var subtitle = "" @@ -1619,11 +1698,17 @@ proc genOutFile(d: PDoc, groupedToc = false): string = code = content result = code +proc indexFile(d: PDoc): AbsoluteFile = + let dir = d.conf.outDir + result = dir / changeFileExt(presentationPath(d.conf, + AbsoluteFile d.filename), + IndexExt) + let (finalDir, _, _) = result.string.splitFile + createDir(finalDir) + proc generateIndex*(d: PDoc) = if optGenIndex in d.conf.globalOptions: - let dir = d.conf.outDir - createDir(dir) - let dest = dir / changeFileExt(presentationPath(d.conf, AbsoluteFile d.filename), IndexExt) + let dest = indexFile(d) writeIndexFile(d[], dest.string) proc updateOutfile(d: PDoc, outfile: AbsoluteFile) = @@ -1634,6 +1719,9 @@ proc updateOutfile(d: PDoc, outfile: AbsoluteFile) = d.conf.outFile = splitPath(d.conf.outFile.string)[1].RelativeFile proc writeOutput*(d: PDoc, useWarning = false, groupedToc = false) = + if optGenIndexOnly in d.conf.globalOptions: + d.conf.outFile = indexFile(d).relativeTo(d.conf.outDir) # just for display + return runAllExamples(d) var content = genOutFile(d, groupedToc) if optStdout in d.conf.globalOptions: @@ -1772,6 +1860,8 @@ proc commandTags*(cache: IdentCache, conf: ConfigRef) = rawMessage(conf, errCannotOpenFile, filename.string) proc commandBuildIndex*(conf: ConfigRef, dir: string, outFile = RelativeFile"") = + if optGenIndexOnly in conf.globalOptions: + return var content = mergeIndexes(dir) var outFile = outFile diff --git a/compiler/lineinfos.nim b/compiler/lineinfos.nim index cb60d8949..89aff57df 100644 --- a/compiler/lineinfos.nim +++ b/compiler/lineinfos.nim @@ -57,6 +57,7 @@ type warnRstBrokenLink = "BrokenLink", warnRstLanguageXNotSupported = "LanguageXNotSupported", warnRstFieldXNotSupported = "FieldXNotSupported", + warnRstUnusedImportdoc = "UnusedImportdoc", warnRstStyle = "warnRstStyle", warnCommentXIgnored = "CommentXIgnored", warnTypelessParam = "TypelessParam", @@ -142,6 +143,7 @@ const warnRstBrokenLink: "broken link '$1'", warnRstLanguageXNotSupported: "language '$1' not supported", warnRstFieldXNotSupported: "field '$1' not supported", + warnRstUnusedImportdoc: "importdoc for '$1' is not used", warnRstStyle: "RST style: $1", warnCommentXIgnored: "comment '$1' ignored", warnTypelessParam: "", # deadcode diff --git a/compiler/options.nim b/compiler/options.nim index 47e8155f8..fc970e420 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -78,6 +78,8 @@ type # please make sure we have under 32 options optThreadAnalysis, # thread analysis pass optTlsEmulation, # thread var emulation turned on optGenIndex # generate index file for documentation; + optGenIndexOnly # generate only index file for documentation + optNoImportdoc # disable loading external documentation files optEmbedOrigSrc # embed the original source in the generated code # also: generate header file optIdeDebug # idetools: debug mode diff --git a/doc/advopt.txt b/doc/advopt.txt index 3f439fdab..efff428e8 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -130,7 +130,9 @@ Advanced options: select which memory management to use; default is 'orc' --exceptions:setjmp|cpp|goto|quirky select the exception handling implementation - --index:on|off turn index file generation on|off + --index:on|off|only docgen: turn index file generation? (`only` means + not generate output files like HTML) + --noImportdoc:on|off disable loading documentation ``.idx`` files? --putenv:key=value set an environment variable --NimblePath:PATH add a path for Nimble support --noNimblePath deactivate the Nimble path diff --git a/doc/docgen.md b/doc/docgen.md index e4b040064..70b41f17d 100644 --- a/doc/docgen.md +++ b/doc/docgen.md @@ -9,6 +9,7 @@ .. include:: rstcommon.rst .. contents:: +.. importdoc:: markdown_rst.md, compiler/docgen.nim Introduction ============ @@ -103,6 +104,64 @@ won't influence RST formatting. ## Paragraph. ``` +Structuring output directories +------------------------------ + +Basic directory for output is set by `--outdir:OUTDIR`:option: switch, +by default `OUTDIR` is ``htmldocs`` sub-directory in the directory of +the processed file. + +There are 2 basic options as to how generated HTML output files are stored: + +1) complex hierarchy when docgen-compiling with `--project`:option:, + which follows directory structure of the project itself. + So `nim doc`:cmd: replicates project's directory structure + inside `--outdir:OUTDIR`:option: directory. + `--project`:option: is well suited for projects that have 1 main module. + File name clashes are impossible in this case. + +2) flattened structure, where user-provided script goes through all + needed input files and calls commands like `nim doc`:cmd: + with `--outdir:OUTDIR`:option: switch, thus putting all HTML (and + ``.idx``) files into 1 directory. + + .. Important:: Make sure that you don't have files with same base name + like ``x.nim`` and ``x.md`` in the same package, otherwise you'll + have name conflict for ``x.html``. + + .. Tip:: To structure your output directories and avoid file name + clashes you can split your project into + different *packages* -- parts of your repository that are + docgen-compiled with different `--outdir:OUTDIR`:option: options. + + An example of such strategy is Nim repository itself which has: + + * its stdlib ``.nim`` files from different directories and ``.md`` + documentation from ``doc/`` directory are all docgen-compiled + into `--outdir:web/upload//`:option: directory + * its ``.nim`` files from ``compiler/`` directory are docgen-compiled + into `--outdir:web/upload//compiler/`:option: directory. + Interestingly, it's compiled with complex hierarchy using + `--project`:option: switch. + + Contents of ``web/upload/`` are then deployed into Nim's + Web server. + + This output directory structure allows to work correctly with files like + ``compiler/docgen.nim`` (implementation) and ``doc/docgen.md`` (user + documentation) in 1 repository. + + +Index files +----------- + +Index (``.idx``) files are used for 2 different purposes: + +1. easy cross-referencing between different ``.nim`` and/or ``.md`` / ``.rst`` + files described in [Nim external referencing] +2. creating a whole-project index for searching of symbols and keywords, + see [Buildindex command]. + Document Types ============== @@ -226,13 +285,46 @@ Note that the `jsondoc`:option: command outputs its JSON without pretty-printing while `jsondoc0`:option: outputs pretty-printed JSON. -Referencing Nim symbols: simple documentation links -=================================================== +Simple documentation links +========================== -You can reference Nim identifiers from Nim documentation comments, currently -only inside their ``.nim`` file (or inside a ``.rst`` file included from -a ``.nim``). The point is that such links will be resolved automatically -by `nim doc`:cmd: (or `nim jsondoc`:cmd: or `nim doc2tex`:cmd:). +It's possible to use normal Markdown/RST syntax to *manually* +reference Nim symbols using HTML anchors, however Nim has an *automatic* +facility that makes referencing inside ``.nim`` and ``.md/.rst`` files and +between them easy and seamless. +The point is that such links will be resolved automatically +by `nim doc`:cmd: (or `md2html`:option:, or `jsondoc`:option:, +or `doc2tex`:option:, ...). And, unlike manual links, such automatic +links **check** that their target exists -- a warning is emitted for +any broken link, so you avoid broken links in your project. + +Nim treats both ``.md/.rst`` files and ``.nim`` modules (their doc comment +part) as *documents* uniformly. +Hence all directions of referencing are equally possible having the same syntax: + +1. ``.md/rst`` -> itself (internal). See [Markup local referencing]. +2. ``.md/rst`` -> external ``.md/rst``. See [Markup external referencing]. + To summarize, referencing in `.md`/`.rst` files was already described in + [Nim-flavored Markdown and reStructuredText] + (particularly it described usage of index files for referencing), + while in this document we focus on Nim-specific details. +3. ``.md/rst`` -> external ``.nim``. See [Nim external referencing]. +4. ``.nim`` -> itself (internal). See [Nim local referencing]. +5. ``.nim`` -> external ``.md/rst``. See [Markup external referencing]. +6. ``.nim`` -> external ``.nim``. See [Nim external referencing]. + +To put it shortly, local referencing always works out of the box, +external referencing requires to use ``.. importdoc:: `` +directive to import `file` and to ensure that the corresponding +``.idx`` file was generated. + + +Nim local referencing +--------------------- + +You can reference Nim identifiers from Nim documentation comments +inside their ``.nim`` file (or inside a ``.rst`` file included from +a ``.nim``). This pertains to any exported symbol like `proc`, `const`, `iterator`, etc. Syntax for referencing is basically a normal RST one: addition of underscore `_` to a *link text*. @@ -405,6 +497,143 @@ recognized fine: ... ## Ref. `CopyFlag enum`_ +Nim external referencing +------------------------ + +Just like for [Markup external referencing], which saves markup anchors, +the Nim symbols are also saved in ``.idx`` files, so one needs +to generate them beforehand, and they should be loaded by +an ``.. importdoc::`` directive. Arguments to ``.. importdoc::`` is a +comma-separated list of Nim modules or Markdown/RST documents. + +`--index:only`:option: tells Nim to only generate ``.idx`` file and +do **not** attempt to generate HTML/LaTeX output. +For ``.nim`` modules there are 2 alternatives to work with ``.idx`` files: + +1. using [Project switch] implies generation of ``.idx`` files, + however, if ``importdoc`` is called on upper modules as its arguments, + their ``.idx`` are not yet created. Thus one should generate **all** + required ``.idx`` first: + ```cmd + nim doc --project --index:only
      .nim + nim doc --project
      .nim + ``` +2. or run `nim doc --index:only `:cmd: command for **all** (used) + Nim modules in your project. Then run `nim doc ` on them for + output HTML generation. + + .. Warning:: A mere `nim doc --index:on`:cmd: may fail on an attempt to do + ``importdoc`` from another module (for which ``.idx`` was not yet + generated), that's why `--index:only`:option: shall be used instead. + + For ``.md``/``.rst`` markup documents point 2 is the only option. + +Then, you can freely use something like this in ``your_module.nim``: + + ```nim + ## .. importdoc:: user_manual.md, another_module.nim + + ... + ## Ref. [some section from User Manual]. + + ... + ## Ref. [proc f] + ## (assuming you have a proc `f` in ``another_module``). + ``` + +and compile it by `nim doc`:cmd:. Note that link text will +be automatically prefixed by the module name of symbol, +so you will see something like "Ref. [another_module: proc f](#)" +in the generated output. + +It's also possible to reference a whole module by prefixing or +suffixing full canonical module name with "module": + + Ref. [module subdir/name] or [subdir/name module]. + +Markup documents as a whole can be referenced just by their title +(or by their file name if the title was not set) without any prefix. + +.. Tip:: During development process the stage of ``.idx`` files generation + can be done only *once*, after that you use already generated ``.idx`` + files while working with a document *being developed* (unless you do + incompatible changes to *referenced* documents). + +.. Hint:: After changing a *referenced* document file one may need + to regenerate its corresponding ``.idx`` file to get correct results. + Of course, when referencing *internally* inside any given ``.nim`` file, + it's not needed, one can even immediately use any freshly added anchor + (a document's own ``.idx`` file is not used for resolving its internal links). + +If an ``importdoc`` directive fails to find a ``.idx``, then an error +is emitted. + +In case of such compilation failures please note that: + +* **all** relative paths, given to ``importdoc``, relate to insides of + ``OUTDIR``, and **not** project's directory structure. + +* ``importdoc`` searches for ``.idx`` in `--outdir:OUTDIR`:option: directory + (``htmldocs`` by default) and **not** around original modules, so: + + .. Tip:: look into ``OUTDIR`` to understand what's going on. + +* also keep in mind that ``.html`` and ``.idx`` files should always be + output to the same directory, so check this and, if it's not true, check + that both runs *with* and *without* `--index:only`:option: have all + other options the same. + +To summarize, for 2 basic options of [Structuring output directories] +compilation options are different: + +1) complex hierarchy with `--project`:option: switch. + + As the **original** project's directory structure is replicated in + `OUTDIR`, all passed paths are related to this structure also. + + E.g. if a module ``path1/module.nim`` does + ``.. importdoc:: path2/another.nim`` then docgen tries to load file + ``OUTDIR/path1/path2/another.idx``. + + .. Note:: markup documents are just placed into the specified directory + `OUTDIR`:option: by default (i.e. they are **not** affected by + `--project`:option:), so if you have ``PROJECT/doc/manual.md`` + document and want to use complex hirearchy (with ``doc/``), + compile it with `--docroot`:option:\: + ```cmd + # 1st stage + nim md2html --outdir:OUTDIR --docroot:/absolute/path/to/PROJECT \ + --index:only PROJECT/doc/manual.md + ... + # 2nd stage + nim md2html --outdir:OUTDIR --docroot:/absolute/path/to/PROJECT \ + PROJECT/doc/manual.md + ``` + + Then the output file will be placed as ``OUTDIR/doc/manual.idx``. + So if you have ``PROJECT/path1/module.nim``, then ``manual.md`` can + be referenced as ``../doc/manual.md``. + +2) flattened structure. + + E.g. if a module ``path1/module.nim`` does + ``.. importdoc:: path2/another.nim`` then docgen tries to load + ``OUTDIR/path2/another.idx``, so the path ``path1`` + does not matter and providing ``path2`` can be useful only + in the case it contains another package that was placed there + using `--outdir:OUTDIR/path2`:option:. + + The links' text will be prefixed as ``another: ...`` in both cases. + + .. Warning:: Again, the same `--outdir:OUTDIR`:option: option should + be provided to both `doc --index:only`:option: / + `md2html --index:only`:option: and final generation by + `doc`:option:/`md2html`:option: inside 1 package. + +To temporarily disable ``importdoc``, e.g. if you don't need +correct link resolution at the moment, use a `--noImportdoc`:option: switch +(only warnings about unresolved links will be generated for external references). + Related Options =============== @@ -434,10 +663,21 @@ index file is line-oriented (newlines have to be escaped). Each line represents a tab-separated record of several columns, the first two mandatory, the rest optional. See the [Index (idx) file format] section for details. +.. Note:: `--index`:option: switch only affects creation of ``.idx`` + index files, while user-searchable Index HTML file is created by + `buildIndex`:option: commmand. + +Buildindex command +------------------ + Once index files have been generated for one or more modules, the Nim -compiler command `buildIndex directory` can be run to go over all the index +compiler command `nim buildIndex directory`:cmd: can be run to go over all the index files in the specified directory to generate a [theindex.html](theindex.html) -file. +file: + + ```cmd + nim buildIndex -o:path/to/htmldocs/theindex.html path/to/htmldocs + ``` See source switch ----------------- @@ -568,10 +808,22 @@ references so they can be later concatenated into a big index file with the file format in detail. Index files are line-oriented and tab-separated (newline and tab characters -have to be escaped). Each line represents a record with at least two fields -but can have up to four (additional columns are ignored). The content of these -columns is: +have to be escaped). Each line represents a record with 6 fields. +The content of these columns is: +0. Discriminator tag denoting type of the index entry, allowed values are: + `markupTitle` + : a title for ``.md``/``.rst`` document + `nimTitle` + : a title of ``.nim`` module + `heading` + : heading of sections, can be both in Nim and markup files + `idx` + : terms marked with :idx: role + `nim` + : a Nim symbol + `nimgrp` + : a Nim group for overloadable symbols like `proc`s 1. Mandatory term being indexed. Terms can include quoting according to Nim's rules (e.g. \`^\`). 2. Base filename plus anchor hyperlink (e.g. ``algorithm.html#*,int,SortOrder``). @@ -581,29 +833,20 @@ columns is: not for an API symbol but for a TOC entry. 4. Optional title or description of the hyperlink. Browsers usually display this as a tooltip after hovering a moment over the hyperlink. +5. A line number of file where the entry was defined. -The index generation tools try to differentiate between documentation -generated from ``.nim`` files and documentation generated from ``.txt`` or -``.rst`` files. The former are always closely related to source code and -consist mainly of API entries. The latter are generic documents meant for -human reading. +The index generation tools differentiate between documentation +generated from ``.nim`` files and documentation generated from ``.md`` or +``.rst`` files by tag `nimTitle` or `markupTitle` in the 1st line of +the ``.idx`` file. -To differentiate both types (documents and APIs), the index generator will add -to the index of documents an entry with the title of the document. Since the -title is the topmost element, it will be added with a second field containing -just the filename without any HTML anchor. By convention, this entry without -anchor is the *title entry*, and since entries in the index file are added as -they are scanned, the title entry will be the first line. The title for APIs -is not present because it can be generated concatenating the name of the file -to the word **Module**. - -Normal symbols are added to the index with surrounding whitespaces removed. An -exception to this are the table of content (TOC) entries. TOC entries are added to -the index file with their third column having as much prefix spaces as their -level is in the TOC (at least 1 character). The prefix whitespace helps to -filter TOC entries from API or text symbols. This is important because the -amount of spaces is used to replicate the hierarchy for document TOCs in the -final index, and TOC entries found in ``.nim`` files are discarded. +.. TODO Normal symbols are added to the index with surrounding whitespaces removed. An + exception to this are the table of content (TOC) entries. TOC entries are added to + the index file with their third column having as much prefix spaces as their + level is in the TOC (at least 1 character). The prefix whitespace helps to + filter TOC entries from API or text symbols. This is important because the + amount of spaces is used to replicate the hierarchy for document TOCs in the + final index, and TOC entries found in ``.nim`` files are discarded. Additional resources @@ -615,6 +858,8 @@ Additional resources [Markdown and RST markup languages](markdown_rst.html), which also contains the list of implemented features of these markup languages. +* the implementation is in [module compiler/docgen]. + The output for HTML and LaTeX comes from the ``config/nimdoc.cfg`` and ``config/nimdoc.tex.cfg`` configuration files. You can add and modify these files to your project to change the look of the docgen output. diff --git a/doc/markdown_rst.md b/doc/markdown_rst.md index 5fb3caefc..a374749cb 100644 --- a/doc/markdown_rst.md +++ b/doc/markdown_rst.md @@ -9,6 +9,8 @@ Nim-flavored Markdown and reStructuredText .. include:: rstcommon.rst .. contents:: +.. importdoc:: docgen.md + Both `Markdown`:idx: (md) and `reStructuredText`:idx: (RST) are markup languages whose goal is to typeset texts with complex structure, formatting and references using simple plaintext representation. @@ -110,6 +112,8 @@ Supported standard RST features: Additional Nim-specific features -------------------------------- +* referencing to definitions in external files, see + [Markup external referencing] section * directives: ``code-block`` \[cmp:Sphinx], ``title``, ``index`` \[cmp:Sphinx] * predefined roles @@ -170,6 +174,86 @@ Optional additional features, by default turned on: .. warning:: Using Nim-specific features can cause other RST implementations to fail on your document. +Referencing +=========== + +To be able to copy and share links Nim generates anchors for all +main document elements: + +* headlines (including document title) +* footnotes +* explicitly set anchors: RST internal cross-references and + inline internal targets +* Nim symbols (external referencing), see [Nim DocGen Tools Guide] for details. + +But direct use of those anchors have 2 problems: + +1. the anchors are usually mangled (e.g. spaces substituted to minus + signs, etc). +2. manual usage of anchors is not checked, so it's easy to get broken + links inside your project if e.g. spelling has changed for a heading + or you use a wrong relative path to your document. + +That's why Nim implementation has syntax for using +*original* labels for referencing. +Such referencing can be either local/internal or external: + +* Local referencing (inside any given file) is defined by + RST standard or Pandoc Markdown User guide. +* External (cross-document) referencing is a Nim-specific feature, + though it's not really different from local referencing by its syntax. + +Markup local referencing +------------------------ + +There are 2 syntax option available for referencing to objects +inside any given file, e.g. for headlines: + + Markdown RST + + Some headline Some headline + ============= ============= + + Ref. [Some headline] Ref. `Some headline`_ + + +Markup external referencing +--------------------------- + +The syntax is the same as for local referencing, but the anchors are +saved in ``.idx`` files, so one needs to generate them beforehand, +and they should be loaded by an `.. importdoc::` directive. +E.g. if we want to reference section "Some headline" in ``file1.md`` +from ``file2.md``, then ``file2.md`` may look like: + +``` +.. importdoc:: file1.md + +Ref. [Some headline] +``` + +```cmd +nim md2html --index:only file1.md # creates ``htmldocs/file1.idx`` +nim md2html file2.md # creates ``htmldocs/file2.html`` +``` + +To allow cross-references between any files in any order (especially, if +circular references are present), it's strongly reccommended +to make a run for creating all the indexes first: + +```cmd +nim md2html --index:only file1.md # creates ``htmldocs/file1.idx`` +nim md2html --index:only file2.md # creates ``htmldocs/file2.idx`` +nim md2html file1.md # creates ``htmldocs/file1.html`` +nim md2html file2.md # creates ``htmldocs/file2.html`` +``` + +and then one can freely reference any objects as if these 2 documents +are actually 1 file. + +Other +===== + Idiosyncrasies -------------- diff --git a/lib/core/macros.nim b/lib/core/macros.nim index bcc16038b..beb3aa695 100644 --- a/lib/core/macros.nim +++ b/lib/core/macros.nim @@ -23,6 +23,8 @@ when defined(nimPreviewSlimSystem): ## .. include:: ../../doc/astspec.txt +## .. importdoc:: system.nim + # If you look for the implementation of the magic symbol # ``{.magic: "Foo".}``, search for `mFoo` and `opcFoo`. diff --git a/lib/packages/docutils/dochelpers.nim b/lib/packages/docutils/dochelpers.nim index c7f7f73f5..b7cdd16d2 100644 --- a/lib/packages/docutils/dochelpers.nim +++ b/lib/packages/docutils/dochelpers.nim @@ -13,7 +13,7 @@ ## `type LangSymbol`_ in ``rst.nim``, while `match(generated, docLink)`_ ## matches it with `generated`, produced from `PNode` by ``docgen.rst``. -import rstast +import rstast, strutils when defined(nimPreviewSlimSystem): import std/[assertions, syncio] @@ -35,6 +35,12 @@ type ## name-type seq, e.g. for proc outType*: string ## result type, e.g. for proc +proc `$`*(s: LangSymbol): string = # for debug + ("(symkind=$1, symTypeKind=$2, name=$3, generics=$4, isGroup=$5, " & + "parametersProvided=$6, parameters=$7, outType=$8)") % [ + s.symKind, s.symTypeKind , s.name, s.generics, $s.isGroup, + $s.parametersProvided, $s.parameters, s.outType] + func nimIdentBackticksNormalize*(s: string): string = ## Normalizes the string `s` as a Nim identifier. ## @@ -71,6 +77,12 @@ func nimIdentBackticksNormalize*(s: string): string = else: discard # just omit '`' or ' ' if j != s.len: setLen(result, j) +proc langSymbolGroup*(kind: string, name: string): LangSymbol = + if kind notin ["proc", "func", "macro", "method", "iterator", + "template", "converter"]: + raise newException(ValueError, "unknown symbol kind $1" % [kind]) + result = LangSymbol(symKind: kind, name: name, isGroup: true) + proc toLangSymbol*(linkText: PRstNode): LangSymbol = ## Parses `linkText` into a more structured form using a state machine. ## @@ -82,11 +94,14 @@ proc toLangSymbol*(linkText: PRstNode): LangSymbol = ## ## This proc should be kept in sync with the `renderTypes` proc from ## ``compiler/typesrenderer.nim``. - assert linkText.kind in {rnRstRef, rnInner} + template fail(msg: string) = + raise newException(ValueError, msg) + if linkText.kind notin {rnRstRef, rnInner}: + fail("toLangSymbol: wrong input kind " & $linkText.kind) const NimDefs = ["proc", "func", "macro", "method", "iterator", "template", "converter", "const", "type", "var", - "enum", "object", "tuple"] + "enum", "object", "tuple", "module"] template resolveSymKind(x: string) = if x in ["enum", "object", "tuple"]: result.symKind = "type" @@ -109,11 +124,11 @@ proc toLangSymbol*(linkText: PRstNode): LangSymbol = template flushIdent() = if curIdent != "": case state - of inBeginning: doAssert false, "incorrect state inBeginning" + of inBeginning: fail("incorrect state inBeginning") of afterSymKind: resolveSymKind curIdent - of beforeSymbolName: doAssert false, "incorrect state beforeSymbolName" + of beforeSymbolName: fail("incorrect state beforeSymbolName") of atSymbolName: result.name = curIdent.nimIdentBackticksNormalize - of afterSymbolName: doAssert false, "incorrect state afterSymbolName" + of afterSymbolName: fail("incorrect state afterSymbolName") of genericsPar: result.generics = curIdent of parameterName: result.parameters.add (curIdent, "") of parameterType: diff --git a/lib/packages/docutils/rst.nim b/lib/packages/docutils/rst.nim index e9f76a26f..b3b1fb9c0 100644 --- a/lib/packages/docutils/rst.nim +++ b/lib/packages/docutils/rst.nim @@ -22,7 +22,7 @@ import os, strutils, rstast, dochelpers, std/enumutils, algorithm, lists, sequtils, - std/private/miscdollars, tables, strscans + std/private/miscdollars, tables, strscans, rstidx from highlite import SourceLanguage, getSourceLanguage when defined(nimPreviewSlimSystem): @@ -40,7 +40,7 @@ type roNimFile ## set for Nim files where default interpreted ## text role should be :nim: roSandboxDisabled ## this option enables certain options - ## (e.g. raw, include) + ## (e.g. raw, include, importdoc) ## which are disabled by default as they can ## enable users to read arbitrary data and ## perform XSS if the parser is used in a web @@ -73,11 +73,17 @@ type mwUnsupportedLanguage = "language '$1' not supported", mwUnsupportedField = "field '$1' not supported", mwRstStyle = "RST style: $1", + mwUnusedImportdoc = "importdoc for '$1' is not used", meSandboxedDirective = "disabled directive: '$1'", MsgHandler* = proc (filename: string, line, col: int, msgKind: MsgKind, arg: string) {.closure, gcsafe.} ## what to do in case of an error FindFileHandler* = proc (filename: string): string {.closure, gcsafe.} + FindRefFileHandler* = + proc (targetRelPath: string): + tuple[targetPath: string, linkRelPath: string] {.closure, gcsafe.} + ## returns where .html or .idx file should be found by its relative path; + ## `linkRelPath` is a prefix to be added before a link anchor from such file proc rstnodeToRefname*(n: PRstNode): string proc addNodes*(n: PRstNode): string @@ -333,7 +339,8 @@ type arInternalRst, ## For automatically generated RST anchors (from ## headings, footnotes, inline internal targets): ## case-insensitive, 1-space-significant (by RST spec) - arNim ## For anchors generated by ``docgen.rst``: Nim-style case + arExternalRst, ## For external .nim doc comments or .rst/.md + arNim ## For anchors generated by ``docgen.nim``: Nim-style case ## sensitivity, etc. (see `proc normalizeNimName`_ for details) arHyperlink, ## For links with manually set anchors in ## form `text `_ @@ -349,11 +356,15 @@ type of arInternalRst: anchorType: RstAnchorKind target: PRstNode + of arExternalRst: + anchorTypeExt: RstAnchorKind + refnameExt: string of arNim: tooltip: string # displayed tooltip for Nim-generated anchors langSym: LangSymbol refname: string # A reference name that will be inserted directly # into HTML/Latex. + external: bool AnchorSubstTable = Table[string, seq[AnchorSubst]] # use `seq` to account for duplicate anchors FootnoteType = enum @@ -372,6 +383,12 @@ type RstFileTable* = object filenameToIdx*: Table[string, FileIndex] idxToFilename*: seq[string] + ImportdocInfo = object + used: bool # was this import used? + fromInfo: TLineInfo # place of `.. importdoc::` directive + idxPath: string # full path to ``.idx`` file + linkRelPath: string # prefix before target anchor + title: string # document title obtained from ``.idx`` RstSharedState = object options*: RstParseOptions # parsing options hLevels: LevelMap # hierarchy of heading styles @@ -393,12 +410,17 @@ type footnotes: seq[FootnoteSubst] # correspondence b/w footnote label, # number, order of occurrence msgHandler: MsgHandler # How to handle errors. - findFile: FindFileHandler # How to find files. + findFile: FindFileHandler # How to find files for include. + findRefFile: FindRefFileHandler + # How to find files imported by importdoc. filenames*: RstFileTable # map file name <-> FileIndex (for storing # file names for warnings after 1st stage) currFileIdx*: FileIndex # current index in `filenames` tocPart*: seq[PRstNode] # all the headings of a document hasToc*: bool + idxImports*: Table[string, ImportdocInfo] + # map `importdoc`ed filename -> it's info + nimFileImported*: bool # Was any ``.nim`` module `importdoc`ed ? PRstSharedState* = ref RstSharedState ManualAnchor = object @@ -452,6 +474,9 @@ proc defaultFindFile*(filename: string): string = if fileExists(filename): result = filename else: result = "" +proc defaultFindRefFile*(filename: string): (string, string) = + (filename, "") + proc defaultRole(options: RstParseOptions): string = if roNimFile in options: "nim" else: "literal" @@ -492,12 +517,16 @@ proc getFilename(filenames: RstFileTable, fid: FileIndex): string = $fid.int, $(filenames.len - 1)]) result = filenames.idxToFilename[fid.int] +proc getFilename(s: PRstSharedState, subst: AnchorSubst): string = + getFilename(s.filenames, subst.info.fileIndex) + proc currFilename(s: PRstSharedState): string = getFilename(s.filenames, s.currFileIdx) proc newRstSharedState*(options: RstParseOptions, filename: string, findFile: FindFileHandler, + findRefFile: FindRefFileHandler, msgHandler: MsgHandler, hasToc: bool): PRstSharedState = let r = defaultRole(options) @@ -507,6 +536,9 @@ proc newRstSharedState*(options: RstParseOptions, options: options, msgHandler: if not isNil(msgHandler): msgHandler else: defaultMsgHandler, findFile: if not isNil(findFile): findFile else: defaultFindFile, + findRefFile: + if not isNil(findRefFile): findRefFile + else: defaultFindRefFile, hasToc: hasToc ) setCurrFilename(result, filename) @@ -525,6 +557,14 @@ proc rstMessage(p: RstParser, msgKind: MsgKind, arg: string) = proc rstMessage(s: PRstSharedState, msgKind: MsgKind, arg: string) = s.msgHandler(s.currFilename, LineRstInit, ColRstInit, msgKind, arg) +proc rstMessage(s: PRstSharedState, msgKind: MsgKind, arg: string; + line, col: int) = + s.msgHandler(s.currFilename, line, col, msgKind, arg) + +proc rstMessage(s: PRstSharedState, filename: string, msgKind: MsgKind, + arg: string) = + s.msgHandler(filename, LineRstInit, ColRstInit, msgKind, arg) + proc rstMessage*(filenames: RstFileTable, f: MsgHandler, info: TLineInfo, msgKind: MsgKind, arg: string) = ## Print warnings using `info`, i.e. in 2nd-pass warnings for @@ -756,6 +796,14 @@ proc internalRefPriority(k: RstAnchorKind): int = of footnoteAnchor: result = 4 of headlineAnchor: result = 3 +proc `$`(subst: AnchorSubst): string = # for debug + let s = + case subst.kind + of arInternalRst: "type=" & $subst.anchorType + of arExternalRst: "type=" & $subst.anchorTypeExt + of arNim: "langsym=" & $subst.langSym + result = "(kind=$1, priority=$2, $3)" % [$subst.kind, $subst.priority, s] + proc addAnchorRst(p: var RstParser, name: string, target: PRstNode, anchorType: RstAnchorKind) = ## Associates node `target` (which has field `anchor`) with an @@ -771,31 +819,49 @@ proc addAnchorRst(p: var RstParser, name: string, target: PRstNode, info: prevLineInfo(p), anchorType: anchorType)) p.curAnchors.setLen 0 -proc addAnchorNim*(s: var PRstSharedState, refn: string, tooltip: string, +proc addAnchorExtRst(s: var PRstSharedState, key: string, refn: string, + anchorType: RstAnchorKind, info: TLineInfo) = + let name = key.toLowerAscii + let prio = internalRefPriority(anchorType) + s.anchors.mgetOrPut(name, newSeq[AnchorSubst]()).add( + AnchorSubst(kind: arExternalRst, refnameExt: refn, priority: prio, + info: info, + anchorTypeExt: anchorType)) + +proc addAnchorNim*(s: var PRstSharedState, external: bool, refn: string, tooltip: string, langSym: LangSymbol, priority: int, info: TLineInfo) = ## Adds an anchor `refn`, which follows ## the rule `arNim` (i.e. a symbol in ``*.nim`` file) s.anchors.mgetOrPut(langSym.name, newSeq[AnchorSubst]()).add( - AnchorSubst(kind: arNim, refname: refn, langSym: langSym, + AnchorSubst(kind: arNim, external: external, refname: refn, langSym: langSym, tooltip: tooltip, priority: priority, info: info)) proc findMainAnchorNim(s: PRstSharedState, signature: PRstNode, info: TLineInfo): seq[AnchorSubst] = - let langSym = toLangSymbol(signature) + var langSym: LangSymbol + try: + langSym = toLangSymbol(signature) + except ValueError: # parsing failed, not a Nim symbol + return let substitutions = s.anchors.getOrDefault(langSym.name, newSeq[AnchorSubst]()) if substitutions.len == 0: return - # map symKind (like "proc") -> found symbols/groups: - var found: Table[string, seq[AnchorSubst]] - for s in substitutions: - if s.kind == arNim: - if match(s.langSym, langSym): - found.mgetOrPut(s.langSym.symKind, newSeq[AnchorSubst]()).add s - for symKind, sList in found: + # logic to select only groups instead of concrete symbols + # with overloads, note that the same symbol can be defined + # in multiple modules and `importdoc`ed: + type GroupKey = tuple[symKind: string, origModule: string] + # map (symKind, file) (like "proc", "os.nim") -> found symbols/groups: + var found: Table[GroupKey, seq[AnchorSubst]] + for subst in substitutions: + if subst.kind == arNim: + if match(subst.langSym, langSym): + let key: GroupKey = (subst.langSym.symKind, getFilename(s, subst)) + found.mgetOrPut(key, newSeq[AnchorSubst]()).add subst + for key, sList in found: if sList.len == 1: result.add sList[0] else: # > 1, there are overloads, potential ambiguity in this `symKind` @@ -812,14 +878,16 @@ proc findMainAnchorNim(s: PRstSharedState, signature: PRstNode, result.add s foundGroup = true break - doAssert foundGroup, "docgen has not generated the group" + doAssert(foundGroup, + "docgen has not generated the group for $1 (file $2)" % [ + langSym.name, getFilename(s, sList[0]) ]) proc findMainAnchorRst(s: PRstSharedState, linkText: string, info: TLineInfo): seq[AnchorSubst] = let name = linkText.toLowerAscii let substitutions = s.anchors.getOrDefault(name, newSeq[AnchorSubst]()) for s in substitutions: - if s.kind == arInternalRst: + if s.kind in {arInternalRst, arExternalRst}: result.add s proc addFootnoteNumManual(p: var RstParser, num: int) = @@ -3251,6 +3319,15 @@ proc dirRaw(p: var RstParser): PRstNode = else: dirRawAux(p, result, rnRaw, parseSectionWrapper) +proc dirImportdoc(p: var RstParser): PRstNode = + result = parseDirective(p, rnDirective, {}, parseLiteralBlock) + assert result.sons[2].kind == rnLiteralBlock + assert result.sons[2].sons[0].kind == rnLeaf + let filenames: seq[string] = split(result.sons[2].sons[0].text, seps = {','}) + proc rmSpaces(s: string): string = s.split.join("") + for origFilename in filenames: + p.s.idxImports[origFilename.rmSpaces] = ImportdocInfo(fromInfo: lineInfo(p)) + proc selectDir(p: var RstParser, d: string): PRstNode = result = nil let tok = p.tok[p.idx-2] # report on directive in ".. directive::" @@ -3271,6 +3348,7 @@ proc selectDir(p: var RstParser, d: string): PRstNode = of "hint": result = dirAdmonition(p, d) of "image": result = dirImage(p) of "important": result = dirAdmonition(p, d) + of "importdoc": result = dirImportdoc(p) of "include": result = dirInclude(p) of "index": result = dirIndex(p) of "note": result = dirAdmonition(p, d) @@ -3401,11 +3479,90 @@ proc rstParsePass1*(fragment: string, getTokens(fragment, p.tok) result = parseDoc(p) -proc preparePass2*(s: PRstSharedState, mainNode: PRstNode) = +proc extractLinkEnd(x: string): string = + ## From links like `path/to/file.html#/%` extract `file.html#/%`. + let i = find(x, '#') + let last = + if i >= 0: i + else: x.len - 1 + let j = rfind(x, '/', start=0, last=last) + if j >= 0: + result = x[j+1 .. ^1] + else: + result = x + +proc loadIdxFile(s: var PRstSharedState, origFilename: string) = + doAssert roSandboxDisabled in s.options + var info: TLineInfo + info.fileIndex = addFilename(s, origFilename) + var (dir, basename, ext) = origFilename.splitFile + if ext notin [".md", ".rst", ".nim", ""]: + rstMessage(s.filenames, s.msgHandler, s.idxImports[origFilename].fromInfo, + meCannotOpenFile, origFilename & ": unknown extension") + let idxFilename = dir / basename & ".idx" + let (idxPath, linkRelPath) = s.findRefFile(idxFilename) + s.idxImports[origFilename].linkRelPath = linkRelPath + var + fileEntries: seq[IndexEntry] + title: IndexEntry + try: + (fileEntries, title) = parseIdxFile(idxPath) + except IOError: + rstMessage(s.filenames, s.msgHandler, s.idxImports[origFilename].fromInfo, + meCannotOpenFile, idxPath) + except ValueError as e: + s.msgHandler(idxPath, LineRstInit, ColRstInit, meInvalidField, e.msg) + + var isMarkup = false # for sanity check to avoid mixing .md <-> .nim + for entry in fileEntries: + # Though target .idx already has inside it the path to HTML relative + # project's root, we won't rely on it and use `linkRelPath` instead. + let refn = extractLinkEnd(entry.link) + # select either markup (rst/md) or Nim cases: + if entry.kind in {ieMarkupTitle, ieNimTitle}: + s.idxImports[origFilename].title = entry.keyword + case entry.kind + of ieIdxRole, ieHeading, ieMarkupTitle: + if ext == ".nim" and entry.kind == ieMarkupTitle: + rstMessage(s, idxPath, meInvalidField, + $ieMarkupTitle & " in supposedly .nim-derived file") + if entry.kind == ieMarkupTitle: + isMarkup = true + info.line = entry.line.uint16 + addAnchorExtRst(s, key = entry.keyword, refn = refn, + anchorType = headlineAnchor, info=info) + of ieNim, ieNimGroup, ieNimTitle: + if ext in [".md", ".rst"] or isMarkup: + rstMessage(s, idxPath, meInvalidField, + $entry.kind & " in supposedly markup-derived file") + s.nimFileImported = true + var langSym: LangSymbol + if entry.kind in {ieNim, ieNimTitle}: + var q: RstParser + initParser(q, s) + info.line = entry.line.uint16 + setLen(q.tok, 0) + q.idx = 0 + getTokens(entry.linkTitle, q.tok) + var sons = newSeq[PRstNode](q.tok.len) + for i in 0 ..< q.tok.len: sons[i] = newLeaf(q.tok[i].symbol) + let linkTitle = newRstNode(rnInner, sons) + langSym = linkTitle.toLangSymbol + else: # entry.kind == ieNimGroup + langSym = langSymbolGroup(kind=entry.linkTitle, name=entry.keyword) + addAnchorNim(s, external = true, refn = refn, tooltip = entry.linkDesc, + langSym = langSym, priority = -4, # lowest + info=info) + doAssert s.idxImports[origFilename].title != "" + +proc preparePass2*(s: var PRstSharedState, mainNode: PRstNode, importdoc = true) = ## Records titles in node `mainNode` and orders footnotes. countTitles(s, mainNode) fixHeadlines(s) orderFootnotes(s) + if importdoc: + for origFilename in s.idxImports.keys: + loadIdxFile(s, origFilename) proc resolveLink(s: PRstSharedState, n: PRstNode) : PRstNode = # Associate this link alias with its target and change node kind to @@ -3423,6 +3580,9 @@ proc resolveLink(s: PRstSharedState, n: PRstNode) : PRstNode = tooltip: string target: PRstNode info: TLineInfo + externFilename: string + # when external anchor: origin filename where anchor was defined + isTitle: bool proc cmp(x, y: LinkDef): int = result = cmp(x.priority, y.priority) if result == 0: @@ -3435,26 +3595,67 @@ proc resolveLink(s: PRstSharedState, n: PRstNode) : PRstNode = target: y.value, info: y.info, tooltip: "(" & $y.kind & ")") let substRst = findMainAnchorRst(s, alias.addNodes, n.info) + template getExternFilename(subst: AnchorSubst): string = + if subst.kind == arExternalRst or + (subst.kind == arNim and subst.external): + getFilename(s, subst) + else: "" for subst in substRst: - foundLinks.add LinkDef(ar: arInternalRst, priority: subst.priority, - target: newLeaf(subst.target.anchor), + var refname, fullRefname: string + if subst.kind == arInternalRst: + refname = subst.target.anchor + fullRefname = refname + else: # arExternalRst + refname = subst.refnameExt + fullRefname = s.idxImports[getFilename(s, subst)].linkRelPath & + "/" & refname + let anchorType = + if subst.kind == arInternalRst: subst.anchorType + else: subst.anchorTypeExt # arExternalRst + foundLinks.add LinkDef(ar: subst.kind, priority: subst.priority, + target: newLeaf(fullRefname), info: subst.info, - tooltip: "(" & $subst.anchorType & ")") + externFilename: getExternFilename(subst), + isTitle: isDocumentationTitle(refname), + tooltip: "(" & $anchorType & ")") # find anchors automatically generated from Nim symbols - if roNimFile in s.options: + if roNimFile in s.options or s.nimFileImported: let substNim = findMainAnchorNim(s, signature=alias, n.info) for subst in substNim: - foundLinks.add LinkDef(ar: arNim, priority: subst.priority, - target: newLeaf(subst.refname), + let fullRefname = + if subst.external: + s.idxImports[getFilename(s, subst)].linkRelPath & + "/" & subst.refname + else: subst.refname + foundLinks.add LinkDef(ar: subst.kind, priority: subst.priority, + target: newLeaf(fullRefname), + externFilename: getExternFilename(subst), + isTitle: isDocumentationTitle(subst.refname), info: subst.info, tooltip: subst.tooltip) foundLinks.sort(cmp = cmp, order = Descending) let aliasStr = addNodes(alias) if foundLinks.len >= 1: - let kind = if foundLinks[0].ar == arHyperlink: rnHyperlink - elif foundLinks[0].ar == arNim: rnNimdocRef + if foundLinks[0].externFilename != "": + s.idxImports[foundLinks[0].externFilename].used = true + let kind = if foundLinks[0].ar in {arHyperlink, arExternalRst}: rnHyperlink + elif foundLinks[0].ar == arNim: + if foundLinks[0].externFilename == "": rnNimdocRef + else: rnHyperlink else: rnInternalRef result = newRstNode(kind) - result.sons = @[newRstNode(rnInner, desc.sons), foundLinks[0].target] + let documentName = # filename without ext for `.nim`, title for `.md` + if foundLinks[0].ar == arNim: + changeFileExt(foundLinks[0].externFilename.extractFilename, "") + elif foundLinks[0].externFilename != "": + s.idxImports[foundLinks[0].externFilename].title + else: foundLinks[0].externFilename.extractFilename + let linkText = + if foundLinks[0].externFilename != "": + if foundLinks[0].isTitle: newLeaf(addNodes(desc)) + else: newLeaf(documentName & ": " & addNodes(desc)) + else: + newRstNode(rnInner, desc.sons) + result.sons = @[linkText, foundLinks[0].target] if kind == rnNimdocRef: result.tooltip = foundLinks[0].tooltip if foundLinks.len > 1: # report ambiguous link var targets = newSeq[string]() @@ -3568,20 +3769,28 @@ proc resolveSubs*(s: PRstSharedState, n: PRstNode): PRstNode = inc i result.sons = newSons +proc completePass2*(s: PRstSharedState) = + for (filename, importdocInfo) in s.idxImports.pairs: + if not importdocInfo.used: + rstMessage(s.filenames, s.msgHandler, importdocInfo.fromInfo, + mwUnusedImportdoc, filename) + proc rstParse*(text, filename: string, line, column: int, options: RstParseOptions, findFile: FindFileHandler = nil, + findRefFile: FindRefFileHandler = nil, msgHandler: MsgHandler = nil): tuple[node: PRstNode, filenames: RstFileTable, hasToc: bool] = ## Parses the whole `text`. The result is ready for `rstgen.renderRstToOut`, ## note that 2nd tuple element should be fed to `initRstGenerator` ## argument `filenames` (it is being filled here at least with `filename` ## and possibly with other files from RST ``.. include::`` statement). - var sharedState = newRstSharedState(options, filename, findFile, + var sharedState = newRstSharedState(options, filename, findFile, findRefFile, msgHandler, hasToc=false) let unresolved = rstParsePass1(text, line, column, sharedState) preparePass2(sharedState, unresolved) result.node = resolveSubs(sharedState, unresolved) + completePass2(sharedState) result.filenames = sharedState.filenames result.hasToc = sharedState.hasToc diff --git a/lib/packages/docutils/rstgen.nim b/lib/packages/docutils/rstgen.nim index 597ee1553..57bc00fcb 100644 --- a/lib/packages/docutils/rstgen.nim +++ b/lib/packages/docutils/rstgen.nim @@ -39,7 +39,8 @@ ## No backreferences are generated since finding all references of a footnote ## can be done by simply searching for ``[footnoteName]``. -import strutils, os, hashes, strtabs, rstast, rst, highlite, tables, sequtils, +import strutils, os, hashes, strtabs, rstast, rst, rstidx, + highlite, tables, sequtils, algorithm, parseutils, std/strbasics @@ -59,7 +60,7 @@ type outLatex # output is Latex MetaEnum* = enum - metaNone, metaTitle, metaSubtitle, metaAuthor, metaVersion + metaNone, metaTitleRaw, metaTitle, metaSubtitle, metaAuthor, metaVersion EscapeMode* = enum # in Latex text inside options [] and URLs is # escaped slightly differently than in normal text @@ -321,31 +322,8 @@ proc renderAux(d: PDoc, n: PRstNode, html, tex: string, result: var string) = # ---------------- index handling -------------------------------------------- -proc quoteIndexColumn(text: string): string = - ## Returns a safe version of `text` for serialization to the ``.idx`` file. - ## - ## The returned version can be put without worries in a line based tab - ## separated column text file. The following character sequence replacements - ## will be performed for that goal: - ## - ## * ``"\\"`` => ``"\\\\"`` - ## * ``"\n"`` => ``"\\n"`` - ## * ``"\t"`` => ``"\\t"`` - result = newStringOfCap(text.len + 3) - for c in text: - case c - of '\\': result.add "\\" - of '\L': result.add "\\n" - of '\C': discard - of '\t': result.add "\\t" - else: result.add c - -proc unquoteIndexColumn(text: string): string = - ## Returns the unquoted version generated by ``quoteIndexColumn``. - result = text.multiReplace(("\\t", "\t"), ("\\n", "\n"), ("\\\\", "\\")) - -proc setIndexTerm*(d: var RstGenerator, htmlFile, id, term: string, - linkTitle, linkDesc = "") = +proc setIndexTerm*(d: var RstGenerator; k: IndexEntryKind, htmlFile, id, term: string, + linkTitle, linkDesc = "", line = 0) = ## Adds a `term` to the index using the specified hyperlink identifier. ## ## A new entry will be added to the index using the format @@ -368,21 +346,8 @@ proc setIndexTerm*(d: var RstGenerator, htmlFile, id, term: string, ## <#writeIndexFile,RstGenerator,string>`_. The purpose of the index is ## documented in the `docgen tools guide ## `_. - var - entry = term - isTitle = false - entry.add('\t') - entry.add(htmlFile) - if id.len > 0: - entry.add('#') - entry.add(id) - else: - isTitle = true - if linkTitle.len > 0 or linkDesc.len > 0: - entry.add('\t' & linkTitle.quoteIndexColumn) - entry.add('\t' & linkDesc.quoteIndexColumn) - entry.add("\n") - + let (entry, isTitle) = formatIndexEntry(k, htmlFile, id, term, + linkTitle, linkDesc, line) if isTitle: d.theIndex.insert(entry) else: d.theIndex.add(entry) @@ -395,6 +360,15 @@ proc hash(n: PRstNode): int = result = result !& hash(n.sons[i]) result = !$result +proc htmlFileRelPath(d: PDoc): string = + if d.outDir.len == 0: + # /foo/bar/zoo.nim -> zoo.html + changeFileExt(extractFilename(d.filename), HtmlExt) + else: # d is initialized in docgen.nim + # outDir = /foo -\ + # destFile = /foo/bar/zoo.html -|-> bar/zoo.html + d.destFile.relativePath(d.outDir, '/') + proc renderIndexTerm*(d: PDoc, n: PRstNode, result: var string) = ## Renders the string decorated within \`foobar\`\:idx\: markers. ## @@ -411,17 +385,12 @@ proc renderIndexTerm*(d: PDoc, n: PRstNode, result: var string) = var term = "" renderAux(d, n, term) - setIndexTerm(d, changeFileExt(extractFilename(d.filename), HtmlExt), id, term, d.currentSection) + setIndexTerm(d, ieIdxRole, + htmlFileRelPath(d), id, term, d.currentSection) dispA(d.target, result, "$2", "\\nimindexterm{$1}{$2}", [id, term]) type - IndexEntry* = object - keyword*: string - link*: string - linkTitle*: string ## contains a prettier text for the href - linkDesc*: string ## the title attribute of the final href - IndexedDocs* = Table[IndexEntry, seq[IndexEntry]] ## \ ## Contains the index sequences for doc types. ## @@ -432,21 +401,6 @@ type ## The value indexed by this IndexEntry is a sequence with the real index ## entries found in the ``.idx`` file. -proc cmp(a, b: IndexEntry): int = - ## Sorts two ``IndexEntry`` first by `keyword` field, then by `link`. - result = cmpIgnoreStyle(a.keyword, b.keyword) - if result == 0: - result = cmpIgnoreStyle(a.link, b.link) - -proc hash(x: IndexEntry): Hash = - ## Returns the hash for the combined fields of the type. - ## - ## The hash is computed as the chained hash of the individual string hashes. - result = x.keyword.hash !& x.link.hash - result = result !& x.linkTitle.hash - result = result !& x.linkDesc.hash - result = !$result - when defined(gcDestructors): template `<-`(a, b: var IndexEntry) = a = move(b) else: @@ -455,6 +409,7 @@ else: shallowCopy a.link, b.link shallowCopy a.linkTitle, b.linkTitle shallowCopy a.linkDesc, b.linkDesc + shallowCopy a.module, b.module proc sortIndex(a: var openArray[IndexEntry]) = # we use shellsort here; fast and simple @@ -494,16 +449,20 @@ proc generateSymbolIndex(symbols: seq[IndexEntry]): string = result = "
      " var i = 0 while i < symbols.len: - let keyword = symbols[i].keyword + let keyword = esc(outHtml, symbols[i].keyword) let cleanedKeyword = keyword.escapeLink result.addf("
      $1:
        \n", [keyword, cleanedKeyword]) var j = i - while j < symbols.len and keyword == symbols[j].keyword: + while j < symbols.len and symbols[i].keyword == symbols[j].keyword: let url = symbols[j].link.escapeLink - text = if symbols[j].linkTitle.len > 0: symbols[j].linkTitle else: url - desc = if symbols[j].linkDesc.len > 0: symbols[j].linkDesc else: "" + module = symbols[j].module + text = + if symbols[j].linkTitle.len > 0: + esc(outHtml, module & ": " & symbols[j].linkTitle) + else: url + desc = symbols[j].linkDesc if desc.len > 0: result.addf("""
      • $2
      • @@ -517,13 +476,6 @@ proc generateSymbolIndex(symbols: seq[IndexEntry]): string = i = j result.add("
      ") -proc isDocumentationTitle(hyperlink: string): bool = - ## Returns true if the hyperlink is actually a documentation title. - ## - ## Documentation titles lack the hash. See `mergeIndexes() - ## <#mergeIndexes,string>`_ for a more detailed explanation. - result = hyperlink.find('#') < 0 - proc stripTocLevel(s: string): tuple[level: int, text: string] = ## Returns the *level* of the toc along with the text without it. for c in 0 ..< s.len: @@ -557,17 +509,15 @@ proc generateDocumentationToc(entries: seq[IndexEntry]): string = level = 1 levels.newSeq(entries.len) for entry in entries: - let (rawLevel, rawText) = stripTocLevel(entry.linkTitle or entry.keyword) + let (rawLevel, rawText) = stripTocLevel(entry.linkTitle) if rawLevel < 1: # This is a normal symbol, push it *inside* one level from the last one. levels[L].level = level + 1 - # Also, ignore the linkTitle and use directly the keyword. - levels[L].text = entry.keyword else: # The level did change, update the level indicator. level = rawLevel levels[L].level = rawLevel - levels[L].text = rawText + levels[L].text = rawText inc L # Now generate hierarchical lists based on the precalculated levels. @@ -598,7 +548,7 @@ proc generateDocumentationIndex(docs: IndexedDocs): string = for title in titles: let tocList = generateDocumentationToc(docs.getOrDefault(title)) result.add("\n") + title.link & "\">" & title.linkTitle & "\n" & tocList & "
    \n") proc generateDocumentationJumps(docs: IndexedDocs): string = ## Returns a plain list of hyperlinks to documentation TOCs in HTML. @@ -610,7 +560,7 @@ proc generateDocumentationJumps(docs: IndexedDocs): string = var chunks: seq[string] = @[] for title in titles: - chunks.add("" & title.keyword & "") + chunks.add("" & title.linkTitle & "") result.add(chunks.join(", ") & ".
    ") @@ -639,39 +589,11 @@ proc readIndexDir*(dir: string): # Scan index files and build the list of symbols. for path in walkDirRec(dir): if path.endsWith(IndexExt): - var - fileEntries: seq[IndexEntry] - title: IndexEntry - f = 0 - newSeq(fileEntries, 500) - setLen(fileEntries, 0) - for line in lines(path): - let s = line.find('\t') - if s < 0: continue - setLen(fileEntries, f+1) - fileEntries[f].keyword = line.substr(0, s-1) - fileEntries[f].link = line.substr(s+1) - # See if we detect a title, a link without a `#foobar` trailing part. - if title.keyword.len == 0 and fileEntries[f].link.isDocumentationTitle: - title.keyword = fileEntries[f].keyword - title.link = fileEntries[f].link - - if fileEntries[f].link.find('\t') > 0: - let extraCols = fileEntries[f].link.split('\t') - fileEntries[f].link = extraCols[0] - assert extraCols.len == 3 - fileEntries[f].linkTitle = extraCols[1].unquoteIndexColumn - fileEntries[f].linkDesc = extraCols[2].unquoteIndexColumn - else: - fileEntries[f].linkTitle = "" - fileEntries[f].linkDesc = "" - inc f + var (fileEntries, title) = parseIdxFile(path) # Depending on type add this to the list of symbols or table of APIs. - if title.keyword.len == 0: - for i in 0 ..< f: - # Don't add to symbols TOC entries (they start with a whitespace). - let toc = fileEntries[i].linkTitle - if toc.len > 0 and toc[0] == ' ': + if title.kind == ieNimTitle: + for i in 0 ..< fileEntries.len: + if fileEntries[i].kind != ieNim: continue # Ok, non TOC entry, add it. setLen(result.symbols, L + 1) @@ -687,7 +609,7 @@ proc readIndexDir*(dir: string): result.modules.add(x.changeFileExt("")) else: # Generate the symbolic anchor for index quickjumps. - title.linkTitle = "doc_toc_" & $result.docs.len + title.aux = "doc_toc_" & $result.docs.len result.docs[title] = fileEntries proc mergeIndexes*(dir: string): string = @@ -747,24 +669,6 @@ proc mergeIndexes*(dir: string): string = # ---------------------------------------------------------------------------- -proc stripTocHtml(s: string): string = - ## Ugly quick hack to remove HTML tags from TOC titles. - ## - ## A TocEntry.header field already contains rendered HTML tags. Instead of - ## implementing a proper version of renderRstToOut() which recursively - ## renders an rst tree to plain text, we simply remove text found between - ## angled brackets. Given the limited possibilities of rst inside TOC titles - ## this should be enough. - result = s - var first = result.find('<') - while first >= 0: - let last = result.find('>', first) - if last < 0: - # Abort, since we didn't found a closing angled bracket. - return - result.delete(first..last) - first = result.find('<', first) - proc renderHeadline(d: PDoc, n: PRstNode, result: var string) = var tmp = "" for i in countup(0, len(n) - 1): renderRstToOut(d, n.sons[i], tmp) @@ -785,19 +689,12 @@ proc renderHeadline(d: PDoc, n: PRstNode, result: var string) = # Generate index entry using spaces to indicate TOC level for the output HTML. assert n.level >= 0 - let - htmlFileRelPath = if d.outDir.len == 0: - # /foo/bar/zoo.nim -> zoo.html - changeFileExt(extractFilename(d.filename), HtmlExt) - else: # d is initialized in docgen.nim - # outDir = /foo -\ - # destFile = /foo/bar/zoo.html -|-> bar/zoo.html - d.destFile.relativePath(d.outDir, '/') - setIndexTerm(d, htmlFileRelPath, n.anchor, tmp.stripTocHtml, - spaces(max(0, n.level)) & tmp) + setIndexTerm(d, ieHeading, htmlFile = d.htmlFileRelPath, id = n.anchor, + term = n.addNodes, linkTitle = spaces(max(0, n.level)) & tmp) proc renderOverline(d: PDoc, n: PRstNode, result: var string) = if n.level == 0 and d.meta[metaTitle].len == 0: + d.meta[metaTitleRaw] = n.addNodes for i in countup(0, len(n)-1): renderRstToOut(d, n.sons[i], d.meta[metaTitle]) d.currentSection = d.meta[metaTitle] @@ -813,6 +710,8 @@ proc renderOverline(d: PDoc, n: PRstNode, result: var string) = dispA(d.target, result, "
    $3
    ", "\\rstov$4[$5]{$3}$2\n", [$n.level, n.anchor.idS, tmp, $chr(n.level - 1 + ord('A')), tocName]) + setIndexTerm(d, ieHeading, htmlFile = d.htmlFileRelPath, id = n.anchor, + term = n.addNodes, linkTitle = spaces(max(0, n.level)) & tmp) proc renderTocEntry(d: PDoc, n: PRstNode, result: var string) = var header = "" @@ -1197,6 +1096,18 @@ proc renderHyperlink(d: PDoc, text, link: PRstNode, result: var string, "\\hyperlink{$2}{$1} (p.~\\pageref{$2})", [textStr, linkStr, nimDocStr, tooltipStr]) +proc traverseForIndex*(d: PDoc, n: PRstNode) = + ## A version of [renderRstToOut] that only fills entries for ``.idx`` files. + var discarded: string + if n == nil: return + case n.kind + of rnIdx: renderIndexTerm(d, n, discarded) + of rnHeadline, rnMarkdownHeadline: renderHeadline(d, n, discarded) + of rnOverline: renderOverline(d, n, discarded) + else: + for i in 0 ..< len(n): + traverseForIndex(d, n.sons[i]) + proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = if n == nil: return case n.kind @@ -1451,6 +1362,7 @@ proc renderRstToOut(d: PDoc, n: PRstNode, result: var string) = of rnTitle: d.meta[metaTitle] = "" renderRstToOut(d, n.sons[0], d.meta[metaTitle]) + d.meta[metaTitleRaw] = n.sons[0].addNodes # ----------------------------------------------------------------------------- @@ -1616,11 +1528,13 @@ proc rstToHtml*(s: string, options: RstParseOptions, proc myFindFile(filename: string): string = # we don't find any files in online mode: result = "" + proc myFindRefFile(filename: string): (string, string) = + result = ("", "") const filen = "input" let (rst, filenames, t) = rstParse(s, filen, line=LineRstInit, column=ColRstInit, - options, myFindFile, msgHandler) + options, myFindFile, myFindRefFile, msgHandler) var d: RstGenerator initRstGenerator(d, outHtml, config, filen, myFindFile, msgHandler, filenames, hasToc = t) diff --git a/lib/packages/docutils/rstidx.nim b/lib/packages/docutils/rstidx.nim new file mode 100644 index 000000000..c109636d7 --- /dev/null +++ b/lib/packages/docutils/rstidx.nim @@ -0,0 +1,138 @@ +# +# Nim's Runtime Library +# (c) Copyright 2022 Andreas Rumpf +# +# See the file "copying.txt", included in this +# distribution, for details about the copyright. + +## Nim `idx`:idx: file format related definitions. + +import strutils, std/syncio, hashes +from os import splitFile + +type + IndexEntryKind* = enum ## discriminator tag + ieMarkupTitle = "markupTitle" + ## RST/Markdown title, text in `keyword` + + ## HTML text in `linkTitle` + ieNimTitle = "nimTitle" + ## Nim title + ieHeading = "heading" ## RST/Markdown markup heading, escaped + ieIdxRole = "idx" ## RST :idx: definition, escaped + ieNim = "nim" ## Nim symbol, unescaped + ieNimGroup = "nimgrp" ## Nim overload group, unescaped + IndexEntry* = object + kind*: IndexEntryKind ## 0. + keyword*: string ## 1. + link*: string ## 2. + linkTitle*: string ## 3. contains a prettier text for the href + linkDesc*: string ## 4. the title attribute of the final href + line*: int ## 5. + module*: string ## origin file, NOT a field in ``.idx`` file + aux*: string ## auxuliary field, NOT a field in ``.idx`` file + +proc isDocumentationTitle*(hyperlink: string): bool = + ## Returns true if the hyperlink is actually a documentation title. + ## + ## Documentation titles lack the hash. See `mergeIndexes() + ## <#mergeIndexes,string>`_ for a more detailed explanation. + result = hyperlink.find('#') < 0 + +proc `$`*(e: IndexEntry): string = + """("$1", "$2", "$3", "$4", $5)""" % [ + e.keyword, e.link, e.linkTitle, e.linkDesc, $e.line] + +proc quoteIndexColumn(text: string): string = + ## Returns a safe version of `text` for serialization to the ``.idx`` file. + ## + ## The returned version can be put without worries in a line based tab + ## separated column text file. The following character sequence replacements + ## will be performed for that goal: + ## + ## * ``"\\"`` => ``"\\\\"`` + ## * ``"\n"`` => ``"\\n"`` + ## * ``"\t"`` => ``"\\t"`` + result = newStringOfCap(text.len + 3) + for c in text: + case c + of '\\': result.add "\\" + of '\L': result.add "\\n" + of '\C': discard + of '\t': result.add "\\t" + else: result.add c + +proc unquoteIndexColumn*(text: string): string = + ## Returns the unquoted version generated by ``quoteIndexColumn``. + result = text.multiReplace(("\\t", "\t"), ("\\n", "\n"), ("\\\\", "\\")) + +proc formatIndexEntry*(kind: IndexEntryKind; htmlFile, id, term, linkTitle, + linkDesc: string, line: int): + tuple[entry: string, isTitle: bool] = + result.entry = $kind + result.entry.add('\t') + result.entry.add term + result.entry.add('\t') + result.entry.add(htmlFile) + if id.len > 0: + result.entry.add('#') + result.entry.add(id) + result.isTitle = false + else: + result.isTitle = true + result.entry.add('\t' & linkTitle.quoteIndexColumn) + result.entry.add('\t' & linkDesc.quoteIndexColumn) + result.entry.add('\t' & $line) + result.entry.add("\n") + +proc parseIndexEntryKind(s: string): IndexEntryKind = + result = case s: + of "nim": ieNim + of "nimgrp": ieNimGroup + of "heading": ieHeading + of "idx": ieIdxRole + of "nimTitle": ieNimTitle + of "markupTitle": ieMarkupTitle + else: raise newException(ValueError, "unknown index entry value $1" % [s]) + +proc parseIdxFile*(path: string): + tuple[fileEntries: seq[IndexEntry], title: IndexEntry] = + var + f = 0 + newSeq(result.fileEntries, 500) + setLen(result.fileEntries, 0) + let (_, base, _) = path.splitFile + for line in lines(path): + let s = line.find('\t') + if s < 0: continue + setLen(result.fileEntries, f+1) + let cols = line.split('\t') + result.fileEntries[f].kind = parseIndexEntryKind(cols[0]) + result.fileEntries[f].keyword = cols[1] + result.fileEntries[f].link = cols[2] + if result.title.keyword.len == 0: + result.fileEntries[f].module = base + else: + result.fileEntries[f].module = result.title.keyword + + result.fileEntries[f].linkTitle = cols[3].unquoteIndexColumn + result.fileEntries[f].linkDesc = cols[4].unquoteIndexColumn + result.fileEntries[f].line = parseInt(cols[5]) + + if result.fileEntries[f].kind in {ieNimTitle, ieMarkupTitle}: + result.title = result.fileEntries[f] + inc f + +proc cmp*(a, b: IndexEntry): int = + ## Sorts two ``IndexEntry`` first by `keyword` field, then by `link`. + result = cmpIgnoreStyle(a.keyword, b.keyword) + if result == 0: + result = cmpIgnoreStyle(a.link, b.link) + +proc hash*(x: IndexEntry): Hash = + ## Returns the hash for the combined fields of the type. + ## + ## The hash is computed as the chained hash of the individual string hashes. + result = x.keyword.hash !& x.link.hash + result = result !& x.linkTitle.hash + result = result !& x.linkDesc.hash + result = !$result diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 5f8f116d3..ecceed671 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -11,6 +11,8 @@ ## retrieving environment variables, working with directories, ## running shell commands, etc. +## .. importdoc:: symlinks.nim, appdirs.nim, dirs.nim, ospaths2.nim + runnableExamples: let myFile = "/path/to/my/file.nim" assert splitPath(myFile) == (head: "/path/to/my", tail: "file.nim") diff --git a/lib/std/appdirs.nim b/lib/std/appdirs.nim index e648fe0c1..c945ba8ec 100644 --- a/lib/std/appdirs.nim +++ b/lib/std/appdirs.nim @@ -1,5 +1,7 @@ ## This module implements helpers for determining special directories used by apps. +## .. importdoc:: paths.nim + from std/private/osappdirs import nil import std/paths import std/envvars diff --git a/lib/std/private/osappdirs.nim b/lib/std/private/osappdirs.nim index 8e6ae00f8..581b511ff 100644 --- a/lib/std/private/osappdirs.nim +++ b/lib/std/private/osappdirs.nim @@ -1,3 +1,5 @@ +## .. importdoc:: paths.nim, dirs.nim + include system/inclrtl import std/envvars import std/private/ospaths2 diff --git a/lib/std/private/oscommon.nim b/lib/std/private/oscommon.nim index 5b4e1d7e3..3aac58636 100644 --- a/lib/std/private/oscommon.nim +++ b/lib/std/private/oscommon.nim @@ -5,6 +5,8 @@ import std/[oserrors] when defined(nimPreviewSlimSystem): import std/[syncio, assertions, widestrs] +## .. importdoc:: osdirs.nim, os.nim + const weirdTarget* = defined(nimscript) or defined(js) diff --git a/lib/std/private/osdirs.nim b/lib/std/private/osdirs.nim index add9ed424..4af418ead 100644 --- a/lib/std/private/osdirs.nim +++ b/lib/std/private/osdirs.nim @@ -1,3 +1,5 @@ +## .. importdoc:: osfiles.nim, appdirs.nim, paths.nim + include system/inclrtl import std/oserrors diff --git a/lib/std/private/osfiles.nim b/lib/std/private/osfiles.nim index 301f14600..a7a595d97 100644 --- a/lib/std/private/osfiles.nim +++ b/lib/std/private/osfiles.nim @@ -7,6 +7,7 @@ export fileExists import ospaths2, ossymlinks +## .. importdoc:: osdirs.nim, os.nim when defined(nimPreviewSlimSystem): import std/[syncio, assertions, widestrs] @@ -420,4 +421,4 @@ proc moveFile*(source, dest: string) {.rtl, extern: "nos$1", removeFile(source) except: discard tryRemoveFile(dest) - raise \ No newline at end of file + raise diff --git a/lib/std/private/ospaths2.nim b/lib/std/private/ospaths2.nim index 75c34ecf5..5e3bece68 100644 --- a/lib/std/private/ospaths2.nim +++ b/lib/std/private/ospaths2.nim @@ -10,6 +10,8 @@ export ReadDirEffect, WriteDirEffect when defined(nimPreviewSlimSystem): import std/[syncio, assertions, widestrs] +## .. importdoc:: osappdirs.nim, osdirs.nim, osseps.nim, os.nim + const weirdTarget = defined(nimscript) or defined(js) when weirdTarget: diff --git a/lib/std/private/osseps.nim b/lib/std/private/osseps.nim index 1ea587e3c..f2d49d886 100644 --- a/lib/std/private/osseps.nim +++ b/lib/std/private/osseps.nim @@ -3,6 +3,8 @@ # Improved based on info in 'compiler/platform.nim' +## .. importdoc:: ospaths2.nim + const doslikeFileSystem* = defined(windows) or defined(OS2) or defined(DOS) diff --git a/lib/std/private/ossymlinks.nim b/lib/std/private/ossymlinks.nim index cb6287bde..53f59f939 100644 --- a/lib/std/private/ossymlinks.nim +++ b/lib/std/private/ossymlinks.nim @@ -31,6 +31,7 @@ elif defined(js): else: {.pragma: noNimJs.} +## .. importdoc:: os.nim proc createSymlink*(src, dest: string) {.noWeirdTarget.} = ## Create a symbolic link at `dest` which points to the item specified diff --git a/lib/std/symlinks.nim b/lib/std/symlinks.nim index 9e77bbe2a..54ab7b677 100644 --- a/lib/std/symlinks.nim +++ b/lib/std/symlinks.nim @@ -1,10 +1,11 @@ ## This module implements symlink (symbolic link) handling. +## .. importdoc:: os.nim + from paths import Path, ReadDirEffect from std/private/ossymlinks import symlinkExists, createSymlink, expandSymlink - proc symlinkExists*(link: Path): bool {.inline, tags: [ReadDirEffect].} = ## Returns true if the symlink `link` exists. Will return true ## regardless of whether the link points to a directory or file. diff --git a/nimdoc/extlinks/project/doc/manual.md b/nimdoc/extlinks/project/doc/manual.md new file mode 100644 index 000000000..d44b5ca39 --- /dev/null +++ b/nimdoc/extlinks/project/doc/manual.md @@ -0,0 +1,17 @@ +=================== +Nothing User Manual +=================== + +.. importdoc:: ../main.nim, .. / sub / submodule.nim, ../../util.nim + +First section +============= + +Second *section* & +================== + +Ref. [``] or [submoduleInt] from [module nimdoc/extlinks/project/sub/submodule]. + +Ref. [proc mainfunction*(): int]. + +Ref. [utilfunction(x: int)]. diff --git a/nimdoc/extlinks/project/expected/_._/util.html b/nimdoc/extlinks/project/expected/_._/util.html new file mode 100644 index 000000000..9b9b29a4f --- /dev/null +++ b/nimdoc/extlinks/project/expected/_._/util.html @@ -0,0 +1,104 @@ + + + + + + + +nimdoc/extlinks/util + + + + + + + + + + + + +
    +
    +

    nimdoc/extlinks/util

    +
    +
    +
    + + +
    + +
    + Search: +
    +
    + Group by: + +
    + + +
    +
    + +
    + +

    +
    +

    Procs

    +
    +
    +
    +
    proc utilfunction(x: int): int {....raises: [], tags: [], forbids: [].}
    +
    + + + +
    +
    + +
    + +
    +
    + +
    +
    + + +
    +
    + + + + + + diff --git a/nimdoc/extlinks/project/expected/_._/util.idx b/nimdoc/extlinks/project/expected/_._/util.idx new file mode 100644 index 000000000..d83d8c97d --- /dev/null +++ b/nimdoc/extlinks/project/expected/_._/util.idx @@ -0,0 +1,2 @@ +nimTitle util _._/util.html module nimdoc/extlinks/util 0 +nim utilfunction _._/util.html#utilfunction,int proc utilfunction(x: int): int 1 diff --git a/nimdoc/extlinks/project/expected/doc/manual.html b/nimdoc/extlinks/project/expected/doc/manual.html new file mode 100644 index 000000000..7b964f4ac --- /dev/null +++ b/nimdoc/extlinks/project/expected/doc/manual.html @@ -0,0 +1,44 @@ + + + + + + + +Nothing User Manual + + + + + + + + + + + + +
    +
    +

    Nothing User Manual

    + +

    First section

    +

    Second section &

    Ref. submodule: `</a>` or submodule: submoduleInt from module nimdoc/extlinks/project/sub/submodule.

    +

    Ref. main: proc mainfunction*(): int.

    +

    Ref. util: utilfunction(x: int).

    + + + + +
    +
    + + + + + + diff --git a/nimdoc/extlinks/project/expected/doc/manual.idx b/nimdoc/extlinks/project/expected/doc/manual.idx new file mode 100644 index 000000000..158a758f0 --- /dev/null +++ b/nimdoc/extlinks/project/expected/doc/manual.idx @@ -0,0 +1,3 @@ +markupTitle Nothing User Manual doc/manual.html Nothing User Manual 0 +heading First section doc/manual.html#first-section First section 0 +heading Second section & doc/manual.html#second-section-amp Second section & 0 diff --git a/nimdoc/extlinks/project/expected/main.html b/nimdoc/extlinks/project/expected/main.html new file mode 100644 index 000000000..5facedb3b --- /dev/null +++ b/nimdoc/extlinks/project/expected/main.html @@ -0,0 +1,144 @@ + + + + + + + +nimdoc/extlinks/project/main + + + + + + + + + + + + +
    +
    +

    nimdoc/extlinks/project/main

    +
    +
    +
    + + +
    + +
    + Search: +
    +
    + Group by: + +
    + + +
    +
    + +
    + +

    +

    my heading

    See also module nimdoc/extlinks/util or nimdoc/extlinks/project/sub/submodule module.

    +

    Ref. submodule: `</a>` proc.

    +

    Ref. Nothing User Manual: First section or Nothing User Manual: Second section & from Nothing User Manual.

    +

    + +
    +

    Types

    +
    +
    +
    A = object
    +  x: int
    +
    +
    + + + +
    +
    + +
    +
    +
    +

    Procs

    +
    +
    +
    +
    proc mainfunction(): int {....raises: [], tags: [], forbids: [].}
    +
    + + + +
    +
    + +
    + +
    +
    + +
    +
    + + +
    +
    + + + + + + diff --git a/nimdoc/extlinks/project/expected/main.idx b/nimdoc/extlinks/project/expected/main.idx new file mode 100644 index 000000000..d01f2e4c5 --- /dev/null +++ b/nimdoc/extlinks/project/expected/main.idx @@ -0,0 +1,4 @@ +nimTitle main main.html module nimdoc/extlinks/project/main 0 +nim A main.html#A object A 17 +nim mainfunction main.html#mainfunction proc mainfunction(): int 20 +heading my heading main.html#my-heading my heading 0 diff --git a/nimdoc/extlinks/project/expected/sub/submodule.html b/nimdoc/extlinks/project/expected/sub/submodule.html new file mode 100644 index 000000000..913138d6e --- /dev/null +++ b/nimdoc/extlinks/project/expected/sub/submodule.html @@ -0,0 +1,130 @@ + + + + + + + +nimdoc/extlinks/project/sub/submodule + + + + + + + + + + + + +
    +
    +

    nimdoc/extlinks/project/sub/submodule

    +
    +
    +
    + + +
    + +
    + Search: +
    +
    + Group by: + +
    + + +
    +
    + +
    + +

    Ref. main: object A from module nimdoc/extlinks/project/main.

    +

    Ref. util: utilfunction(x: int).

    +

    Ref. Nothing User Manual: Second section & from Nothing User Manual.

    +

    +
    +

    Types

    +
    +
    +
    submoduleInt = distinct int
    +
    + + + +
    +
    + +
    +
    +
    +

    Procs

    +
    +
    +
    +
    proc `</a>`(x, y: int): bool {....raises: [], tags: [], forbids: [].}
    +
    + + Attempt to break HTML formatting. + +
    +
    + +
    + +
    +
    + +
    +
    + + +
    +
    + + + + + + diff --git a/nimdoc/extlinks/project/expected/sub/submodule.idx b/nimdoc/extlinks/project/expected/sub/submodule.idx new file mode 100644 index 000000000..2b02c889e --- /dev/null +++ b/nimdoc/extlinks/project/expected/sub/submodule.idx @@ -0,0 +1,3 @@ +nimTitle submodule sub/submodule.html module nimdoc/extlinks/project/sub/submodule 0 +nim `` sub/submodule.html#,int,int proc ``(x, y: int): bool 9 +nim submoduleInt sub/submodule.html#submoduleInt type submoduleInt 13 diff --git a/nimdoc/extlinks/project/expected/theindex.html b/nimdoc/extlinks/project/expected/theindex.html new file mode 100644 index 000000000..9d2f6723c --- /dev/null +++ b/nimdoc/extlinks/project/expected/theindex.html @@ -0,0 +1,58 @@ + + + + + + + +Index + + + + + + + + + + + + + + + + + + + diff --git a/nimdoc/extlinks/project/main.nim b/nimdoc/extlinks/project/main.nim new file mode 100644 index 000000000..36b778af6 --- /dev/null +++ b/nimdoc/extlinks/project/main.nim @@ -0,0 +1,23 @@ +## my heading +## ========== +## +## .. importdoc:: sub/submodule.nim, ../util.nim, doc/manual.md +## +## .. See also [Second&&&] and particularly [first section] and [Second section &]. +## +## See also [module nimdoc/extlinks/util] or [nimdoc/extlinks/project/sub/submodule module]. +## +## Ref. [`` proc]. +## +## Ref. [First section] or [Second section &] from [Nothing User Manual]. + + +import ../util, sub/submodule + +type A* = object + x: int + +proc mainfunction*(): int = + # just to suppress "not used" warnings: + if ``(1, 2): + result = utilfunction(0) diff --git a/nimdoc/extlinks/project/sub/submodule.nim b/nimdoc/extlinks/project/sub/submodule.nim new file mode 100644 index 000000000..876e00684 --- /dev/null +++ b/nimdoc/extlinks/project/sub/submodule.nim @@ -0,0 +1,13 @@ +## .. importdoc:: ../../util.nim, ../main.nim, ../doc/manual.md +## +## Ref. [object A] from [module nimdoc/extlinks/project/main]. +## +## Ref. [utilfunction(x: int)]. +## +## Ref. [Second section &] from [Nothing User Manual]. + +proc ``*(x, y: int): bool = + ## Attempt to break HTML formatting. + result = x < y + +type submoduleInt* = distinct int diff --git a/nimdoc/extlinks/util.nim b/nimdoc/extlinks/util.nim new file mode 100644 index 000000000..f208f98c1 --- /dev/null +++ b/nimdoc/extlinks/util.nim @@ -0,0 +1,2 @@ +proc utilfunction*(x: int): int = + x + 42 diff --git a/nimdoc/test_out_index_dot_html/expected/foo.idx b/nimdoc/test_out_index_dot_html/expected/foo.idx index a8dabb67e..ac76aa532 100644 --- a/nimdoc/test_out_index_dot_html/expected/foo.idx +++ b/nimdoc/test_out_index_dot_html/expected/foo.idx @@ -1 +1,2 @@ -foo index.html#foo foo: foo() +nimTitle foo index.html module nimdoc/test_out_index_dot_html/foo 0 +nim foo index.html#foo proc foo() 1 diff --git a/nimdoc/test_out_index_dot_html/expected/theindex.html b/nimdoc/test_out_index_dot_html/expected/theindex.html index 2a0ea7900..ea3486d4b 100644 --- a/nimdoc/test_out_index_dot_html/expected/theindex.html +++ b/nimdoc/test_out_index_dot_html/expected/theindex.html @@ -24,7 +24,7 @@ Modules: index.

    API symbols

    foo:
    • foo: foo()
    • + data-doc-search-tag="foo: proc foo()" href="index.html#foo">foo: proc foo()
    @@ -97,8 +96,7 @@
    A = object
    -  x: int
    -
    +
    diff --git a/nimdoc/testproject/expected/subdir/subdir_b/utils.html b/nimdoc/testproject/expected/subdir/subdir_b/utils.html index f0f30536f..ee2618ab3 100644 --- a/nimdoc/testproject/expected/subdir/subdir_b/utils.html +++ b/nimdoc/testproject/expected/subdir/subdir_b/utils.html @@ -59,8 +59,7 @@
    Types
      -
    • G
    • +
    • G
    • SomeType
    • @@ -254,8 +253,7 @@ Ref.
      G[T] = object
      -  val: T
      -
      +
      diff --git a/nimdoc/testproject/expected/testproject.html b/nimdoc/testproject/expected/testproject.html index 7d10d7900..45a53bb93 100644 --- a/nimdoc/testproject/expected/testproject.html +++ b/nimdoc/testproject/expected/testproject.html @@ -65,6 +65,8 @@ Circle, ## A circle Triangle, ## A three-sided shape Rectangle ## A four-sided shape">Shapes +
    • T19396
    @@ -399,6 +401,16 @@ Some shapes. +
    +
    +
    +
    T19396 = object
    +  a*: int
    +
    +
    + + +
    diff --git a/nimdoc/testproject/expected/testproject.idx b/nimdoc/testproject/expected/testproject.idx index e69bedf33..46ffaee72 100644 --- a/nimdoc/testproject/expected/testproject.idx +++ b/nimdoc/testproject/expected/testproject.idx @@ -63,5 +63,6 @@ nim Triangle testproject.html#Triangle Shapes.Triangle 380 nim Rectangle testproject.html#Rectangle Shapes.Rectangle 380 nim Shapes testproject.html#Shapes enum Shapes 380 nim anything testproject.html#anything proc anything() 387 +nim T19396 testproject.html#T19396 object T19396 392 nimgrp bar testproject.html#bar-procs-all proc 31 nimgrp baz testproject.html#baz-procs-all proc 34 diff --git a/nimdoc/testproject/expected/theindex.html b/nimdoc/testproject/expected/theindex.html index 7b4a95561..f9488eb2d 100644 --- a/nimdoc/testproject/expected/theindex.html +++ b/nimdoc/testproject/expected/theindex.html @@ -308,6 +308,10 @@
  • testproject: var someVariable
  • +
    T19396:
    testNimDocTrailingExample:
    • testproject: template testNimDocTrailingExample()
    • diff --git a/nimdoc/testproject/testproject.nim b/nimdoc/testproject/testproject.nim index d236552ac..b5fa2ac37 100644 --- a/nimdoc/testproject/testproject.nim +++ b/nimdoc/testproject/testproject.nim @@ -388,3 +388,7 @@ when true: # issue #15184 ## ## There is no block quote after blank lines at the beginning. discard + +type T19396* = object # bug #19396 + a*: int + b: float From 900fe8f501838077a5a1ef3d6793a1e901083bf8 Mon Sep 17 00:00:00 2001 From: Jake Leahy Date: Wed, 1 Feb 2023 20:00:10 +1100 Subject: [PATCH 3683/3768] Add `contains` to `std/macrocache` (#21304) * Add test cases * Implement contains for CacheSeq * Implement contains for CacheTable * Fix implementation of hasKey * Remove contains for CacheSeq Fix runnable examples I was accidently using --doccmd:skip so I didn't spot the failure locally * Implement hasKey as a VM callback instead of magic * Implement suggestions from PR Co-Authored-By: ringabout * Update lib/core/macrocache.nim --------- Co-authored-by: ringabout Co-authored-by: ringabout <43030857+ringabout@users.noreply.github.com> --- compiler/vmops.nim | 6 ++++++ lib/core/macrocache.nim | 26 ++++++++++++++++++++++++++ tests/macros/tmacros_various.nim | 7 +++++++ 3 files changed, 39 insertions(+) diff --git a/compiler/vmops.nim b/compiler/vmops.nim index f1a2e1e69..f51280ec5 100644 --- a/compiler/vmops.nim +++ b/compiler/vmops.nim @@ -284,6 +284,12 @@ proc registerAdditionalOps*(c: PCtx) = stackTrace2(c, "isExported() requires a symbol. '$#' is of kind '$#'" % [$n, $n.kind], n) setResult(a, sfExported in n.sym.flags) + registerCallback c, "stdlib.macrocache.hasKey", proc (a: VmArgs) = + let + table = getString(a, 0) + key = getString(a, 1) + setResult(a, table in c.graph.cacheTables and key in c.graph.cacheTables[table]) + registerCallback c, "stdlib.vmutils.vmTrace", proc (a: VmArgs) = c.config.isVmTrace = getBool(a, 0) diff --git a/lib/core/macrocache.nim b/lib/core/macrocache.nim index d4b103793..ffd07abb1 100644 --- a/lib/core/macrocache.nim +++ b/lib/core/macrocache.nim @@ -181,6 +181,32 @@ proc `[]`*(t: CacheTable; key: string): NimNode {.magic: "NctGet".} = # get the NimNode back assert mcTable["toAdd"].kind == nnkStmtList +proc hasKey*(t: CacheTable; key: string): bool = + ## Returns true if `key` is in the table `t`. + ## + ## See also: + ## * [contains proc][contains(CacheTable, string)] for use with the `in` operator + runnableExamples: + import std/macros + const mcTable = CacheTable"hasKeyEx" + static: + assert not mcTable.hasKey("foo") + mcTable["foo"] = newEmptyNode() + # Will now be true since we inserted a value + assert mcTable.hasKey("foo") + discard "Implemented in vmops" + +proc contains*(t: CacheTable; key: string): bool {.inline.} = + ## Alias of [hasKey][hasKey(CacheTable, string)] for use with the `in` operator. + runnableExamples: + import std/macros + const mcTable = CacheTable"containsEx" + static: + mcTable["foo"] = newEmptyNode() + # Will be true since we gave it a value before + assert "foo" in mcTable + t.hasKey(key) + proc hasNext(t: CacheTable; iter: int): bool {.magic: "NctHasNext".} proc next(t: CacheTable; iter: int): (string, NimNode, int) {.magic: "NctNext".} diff --git a/tests/macros/tmacros_various.nim b/tests/macros/tmacros_various.nim index 08be4602c..2446912ea 100644 --- a/tests/macros/tmacros_various.nim +++ b/tests/macros/tmacros_various.nim @@ -351,3 +351,10 @@ block: # bug #15118 block: flop("b") + +static: + block: + const containsTable = CacheTable"containsTable" + doAssert "foo" notin containsTable + containsTable["foo"] = newLit 42 + doAssert "foo" in containsTable From 43b1b9d077c6a96580f202101e643fdcd34e30fb Mon Sep 17 00:00:00 2001 From: Ivan Yonchovski Date: Wed, 1 Feb 2023 11:38:22 +0200 Subject: [PATCH 3684/3768] Fix the nimble build on Windows (#21314) Fix the build on Windows - `nimble install` fails on Windows, the `./` is not needed. --- nim.nimble | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nim.nimble b/nim.nimble index 5d1be49b1..713fc9256 100644 --- a/nim.nimble +++ b/nim.nimble @@ -9,6 +9,6 @@ skipDirs = @["build" , "changelogs" , "ci" , "csources_v2" , "drnim" , "nimdoc", before install: when defined(windows): - exec "./build_all.bat" + exec "build_all.bat" else: exec "./build_all.sh" From 17115cbc7375cdd47affd06dded62591d887441e Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Thu, 2 Feb 2023 23:44:14 +0800 Subject: [PATCH 3685/3768] fixes SSL version check logic [backport] (#21324) * fixed version check logic [backport] * add ciphersuites * debug nimble * fixes returns omission * finally * remove debug message * add ciphersuites --------- Co-authored-by: Araq --- lib/pure/asynchttpserver.nim | 4 ++-- lib/pure/net.nim | 6 +++--- lib/pure/unicode.nim | 2 +- lib/wrappers/openssl.nim | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index 6694c4bc2..845875a1e 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -158,7 +158,7 @@ proc parseProtocol(protocol: string): tuple[orig: string, major, minor: int] = proc sendStatus(client: AsyncSocket, status: string): Future[void] = client.send("HTTP/1.1 " & status & "\c\L\c\L") -func hasChunkedEncoding(request: Request): bool = +func hasChunkedEncoding(request: Request): bool = ## Searches for a chunked transfer encoding const transferEncoding = "Transfer-Encoding" @@ -300,7 +300,7 @@ proc processRequest( while true: lineFut.mget.setLen(0) lineFut.clean() - + # The encoding format alternates between specifying a number of bytes to read # and the data to be read, of the previously specified size if sizeOrData mod 2 == 0: diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 76a39226f..1dfef3b30 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -621,7 +621,7 @@ when defineSsl: proc newContext*(protVersion = protSSLv23, verifyMode = CVerifyPeer, certFile = "", keyFile = "", cipherList = CiphersIntermediate, - caDir = "", caFile = ""): SslContext = + caDir = "", caFile = "", ciphersuites = CiphersModern): SslContext = ## Creates an SSL context. ## ## Protocol version is currently ignored by default and TLS is used. @@ -675,10 +675,10 @@ when defineSsl: raiseSSLError() when not defined(openssl10) and not defined(libressl): let sslVersion = getOpenSSLVersion() - if sslVersion >= 0x010101000 and not sslVersion == 0x020000000: + if sslVersion >= 0x010101000 and sslVersion != 0x020000000: # In OpenSSL >= 1.1.1, TLSv1.3 cipher suites can only be configured via # this API. - if newCTX.SSL_CTX_set_ciphersuites(cipherList) != 1: + if newCTX.SSL_CTX_set_ciphersuites(ciphersuites) != 1: raiseSSLError() # Automatically the best ECDH curve for client exchange. Without this, ECDH # ciphers will be ignored by the server. diff --git a/lib/pure/unicode.nim b/lib/pure/unicode.nim index 09233035f..c8d18831a 100644 --- a/lib/pure/unicode.nim +++ b/lib/pure/unicode.nim @@ -887,7 +887,7 @@ proc reversed*(s: openArray[char]): string = proc graphemeLen*(s: openArray[char]; i: Natural): Natural = ## The number of bytes belonging to byte index ``s[i]``, - ## including following combining code unit. + ## including following combining code units. runnableExamples: let a = "añyóng" doAssert a.graphemeLen(1) == 2 ## ñ diff --git a/lib/wrappers/openssl.nim b/lib/wrappers/openssl.nim index b3fe8a608..fcf52a8d9 100644 --- a/lib/wrappers/openssl.nim +++ b/lib/wrappers/openssl.nim @@ -467,10 +467,10 @@ else: raiseInvalidLibrary MainProc proc SSL_CTX_set_ciphersuites*(ctx: SslCtx, str: cstring): cint = - var theProc {.global.}: proc(ctx: SslCtx, str: cstring) {.cdecl, gcsafe.} + var theProc {.global.}: proc(ctx: SslCtx, str: cstring): cint {.cdecl, gcsafe.} if theProc.isNil: theProc = cast[typeof(theProc)](sslSymThrows("SSL_CTX_set_ciphersuites")) - theProc(ctx, str) + result = theProc(ctx, str) proc SSL_new*(context: SslCtx): SslPtr{.cdecl, dynlib: DLLSSLName, importc.} proc SSL_free*(ssl: SslPtr){.cdecl, dynlib: DLLSSLName, importc.} From 8e53fba083a7450b8c2e9771cba8d477468a520e Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 3 Feb 2023 02:11:20 +0800 Subject: [PATCH 3686/3768] replaces `pairs` with `keys` and `items`; saves 8 bytes (#21319) replace `pairs` with `keys` and `items` --- compiler/condsyms.nim | 2 +- compiler/semfold.nim | 6 +++--- compiler/seminst.nim | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/condsyms.nim b/compiler/condsyms.nim index c05aaf10b..a499b7142 100644 --- a/compiler/condsyms.nim +++ b/compiler/condsyms.nim @@ -25,7 +25,7 @@ proc undefSymbol*(symbols: StringTableRef; symbol: string) = # result = if isDefined(symbol): gSymbols[symbol] else: nil iterator definedSymbolNames*(symbols: StringTableRef): string = - for key, val in pairs(symbols): + for key in keys(symbols): yield key proc countDefinedSymbols*(symbols: StringTableRef): int = diff --git a/compiler/semfold.nim b/compiler/semfold.nim index 7f966c6fd..07c3b3ae4 100644 --- a/compiler/semfold.nim +++ b/compiler/semfold.nim @@ -685,7 +685,7 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode n[0] = a of nkBracket, nkCurly: result = copyNode(n) - for i, son in n.pairs: + for son in n.items: var a = getConstExpr(m, son, idgen, g) if a == nil: return nil result.add a @@ -709,7 +709,7 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode # tuple constructor result = copyNode(n) if (n.len > 0) and (n[0].kind == nkExprColonExpr): - for i, expr in n.pairs: + for expr in n.items: let exprNew = copyNode(expr) # nkExprColonExpr exprNew.add expr[0] let a = getConstExpr(m, expr[1], idgen, g) @@ -717,7 +717,7 @@ proc getConstExpr(m: PSym, n: PNode; idgen: IdGenerator; g: ModuleGraph): PNode exprNew.add a result.add exprNew else: - for i, expr in n.pairs: + for expr in n.items: let a = getConstExpr(m, expr, idgen, g) if a == nil: return nil result.add a diff --git a/compiler/seminst.nim b/compiler/seminst.nim index bd5eb1ec3..80d455d1e 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -40,7 +40,7 @@ const iterator instantiateGenericParamList(c: PContext, n: PNode, pt: TIdTable): PSym = internalAssert c.config, n.kind == nkGenericParams - for i, a in n.pairs: + for a in n.items: internalAssert c.config, a.kind == nkSym var q = a.sym if q.typ.kind in {tyTypeDesc, tyGenericParam, tyStatic, tyConcept}+tyTypeClasses: From 9f651f05d56407f63b9334917fecad975bdd49b4 Mon Sep 17 00:00:00 2001 From: Ikko Eltociear Ashimine Date: Tue, 7 Feb 2023 17:15:44 +0900 Subject: [PATCH 3687/3768] nimgrep: fix typo (#21337) occurences -> occurrences --- doc/nimgrep.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/nimgrep.md b/doc/nimgrep.md index 8fb86a9d3..63f760051 100644 --- a/doc/nimgrep.md +++ b/doc/nimgrep.md @@ -77,7 +77,7 @@ That means you can always use only 1 such an option with logical OR, e.g. .. Note:: If you want logical AND on patterns you should compose 1 appropriate pattern, possibly combined with multi-line mode `(?s)`:literal:. - E.g. to require that multi-line context of matches has occurences of + E.g. to require that multi-line context of matches has occurrences of **both** PAT1 and PAT2 use positive lookaheads (`(?=PAT)`:literal:): ```cmd nimgrep --inContext:'(?s)(?=.*PAT1)(?=.*PAT2)' From cbb6001d1e2e484986387e815a61339cd3dc916f Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Wed, 8 Feb 2023 18:27:44 +0800 Subject: [PATCH 3688/3768] fixes backticks in the documentation (#21340) --- lib/pure/collections/tables.nim | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index cd08dcb9a..57a0107fd 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -2393,7 +2393,7 @@ proc contains*[A](t: CountTable[A], key: A): bool = return hasKey[A](t, key) proc getOrDefault*[A](t: CountTable[A], key: A; default: int = 0): int = - ## Retrieves the value at `t[key]` if`key` is in `t`. Otherwise, the + ## Retrieves the value at `t[key]` if `key` is in `t`. Otherwise, the ## integer value of `default` is returned. ## ## See also: @@ -2713,7 +2713,7 @@ proc contains*[A](t: CountTableRef[A], key: A): bool = return hasKey[A](t, key) proc getOrDefault*[A](t: CountTableRef[A], key: A, default: int): int = - ## Retrieves the value at `t[key]` if`key` is in `t`. Otherwise, the + ## Retrieves the value at `t[key]` if `key` is in `t`. Otherwise, the ## integer value of `default` is returned. ## ## See also: From 9fb4c2b3c765f993ba0055b281de19786a484c61 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Wed, 8 Feb 2023 20:55:01 +0100 Subject: [PATCH 3689/3768] fixes #21333; bad codegen for the at operator; [backport:1.6] (#21344) --- compiler/ccgexprs.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index 844a37997..fe7cb252e 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1647,6 +1647,7 @@ proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) = return if d.k == locNone: getTemp(p, n.typ, d) + initLocExpr(p, n[1], a) # generate call to newSeq before adding the elements per hand: let L = toInt(lengthOrd(p.config, n[1].typ)) if optSeqDestructors in p.config.globalOptions: @@ -1658,7 +1659,6 @@ proc genArrToSeq(p: BProc, n: PNode, d: var TLoc) = var lit = newRopeAppender() intLiteral(L, lit) genNewSeqAux(p, d, lit, L == 0) - initLocExpr(p, n[1], a) # bug #5007; do not produce excessive C source code: if L < 10: for i in 0.. Date: Thu, 9 Feb 2023 20:00:56 +0800 Subject: [PATCH 3690/3768] add `nimib` to important packages (#20697) r Co-authored-by: Clay Sweetser --- testament/important_packages.nim | 1 + 1 file changed, 1 insertion(+) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index 90d12df4e..b3a4bfb82 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -105,6 +105,7 @@ pkg "nimfp", "nim c -o:nfp -r src/fp.nim" pkg "nimgame2", "nim c --mm:refc nimgame2/nimgame.nim" # XXX Doesn't work with deprecated 'randomize', will create a PR. pkg "nimgen", "nim c -o:nimgenn -r src/nimgen/runcfg.nim" +pkg "nimib" pkg "nimlsp" pkg "nimly", "nim c -r tests/test_readme_example.nim" pkg "nimongo", "nimble test_ci", allowFailure = true From 51f410e1d5c19e42e97057f0b88e87056b7c43e2 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 10 Feb 2023 05:14:39 +0800 Subject: [PATCH 3691/3768] megatest now checks refc too (#21341) * megatest now checks refc too * fixes refc --- testament/categories.nim | 2 ++ tests/enum/tenum.nim | 3 ++- tests/misc/t18079.nim | 4 ++++ tests/misc/tunsignedconv.nim | 3 ++- tests/typerel/ttypedesc_as_genericparam1_orc.nim | 6 +++++- 5 files changed, 15 insertions(+), 3 deletions(-) diff --git a/testament/categories.nim b/testament/categories.nim index e8b13746a..cae699327 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -718,6 +718,8 @@ proc processCategory(r: var TResults, cat: Category, case cat2 of "megatest": runJoinedTest(r, cat, testsDir, options) + if isNimRepoTests(): + runJoinedTest(r, cat, testsDir, options & " --mm:refc") else: var testsRun = 0 var files: seq[string] diff --git a/tests/enum/tenum.nim b/tests/enum/tenum.nim index b11b02ec6..88d85ddcc 100644 --- a/tests/enum/tenum.nim +++ b/tests/enum/tenum.nim @@ -174,4 +174,5 @@ block: # bug #12589 proc typ(): OGRwkbGeometryType = return wkbPoint25D - doAssert $typ() == "wkbPoint25D" + when not defined(gcRefc): + doAssert $typ() == "wkbPoint25D" diff --git a/tests/misc/t18079.nim b/tests/misc/t18079.nim index 352348daf..ae64bbff9 100644 --- a/tests/misc/t18079.nim +++ b/tests/misc/t18079.nim @@ -1,3 +1,7 @@ +discard """ + matrix: "--mm:orc" +""" + type Foo = object y: int diff --git a/tests/misc/tunsignedconv.nim b/tests/misc/tunsignedconv.nim index 989f39277..c32f85b4d 100644 --- a/tests/misc/tunsignedconv.nim +++ b/tests/misc/tunsignedconv.nim @@ -66,7 +66,8 @@ let limit = 1'u64 let rangeVar = 0'u64 ..< limit -doAssert repr(rangeVar) == """0 .. 0""", repr(rangeVar) +when not defined(gcRefc): + doAssert repr(rangeVar) == """0 .. 0""", repr(rangeVar) # bug #15210 diff --git a/tests/typerel/ttypedesc_as_genericparam1_orc.nim b/tests/typerel/ttypedesc_as_genericparam1_orc.nim index 0ee4d8f92..d528a7421 100644 --- a/tests/typerel/ttypedesc_as_genericparam1_orc.nim +++ b/tests/typerel/ttypedesc_as_genericparam1_orc.nim @@ -1 +1,5 @@ -doAssert repr(int) == "int" \ No newline at end of file +discard """ + matrix: "--mm:orc" +""" + +doAssert repr(int) == "int" From 642136ec4f2cd93cdd753bf16fa4aec89b8fee82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juan=20M=20G=C3=B3mez?= Date: Thu, 9 Feb 2023 21:19:05 +0000 Subject: [PATCH 3692/3768] Adds an extra optional argument to vcc: `vctoolset` Fixes #21335 (#21336) --- tools/vccexe/vccexe.nim | 15 +++++++++++---- tools/vccexe/vcvarsall.nim | 7 ++++++- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/tools/vccexe/vccexe.nim b/tools/vccexe/vccexe.nim index abe68c0a0..d548666b9 100644 --- a/tools/vccexe/vccexe.nim +++ b/tools/vccexe/vccexe.nim @@ -41,6 +41,7 @@ const platformPrefix = "--platform" sdktypePrefix = "--sdktype" sdkversionPrefix = "--sdkversion" + vctoolsetPrefix = "--vctoolset" verbosePrefix = "--verbose" vccversionSepIdx = vccversionPrefix.len @@ -49,6 +50,7 @@ const platformSepIdx = platformPrefix.len sdktypeSepIdx = sdktypePrefix.len sdkversionSepIdx = sdkversionPrefix.len + vctoolsetSepIdx = vctoolsetPrefix.len vcvarsallDefaultPath = "vcvarsall.bat" @@ -97,6 +99,8 @@ Options: "8.1" to use the windows 8.1 SDK --verbose Echoes the command line for loading the Developer Command Prompt and the command line passed on to the secondary command. + --vctoolset Optionally specifies the Visual Studio compiler toolset to use. + By default, the environment is set to use the current Visual Studio compiler toolset. Other command line arguments are passed on to the secondary command specified by --command or to the @@ -108,7 +112,7 @@ proc parseVccexeCmdLine(argseq: seq[string], vccversionArg: var seq[string], printPathArg: var bool, vcvarsallArg: var string, commandArg: var string, noCommandArg: var bool, platformArg: var VccArch, sdkTypeArg: var VccPlatformType, - sdkVersionArg: var string, verboseArg: var bool, + sdkVersionArg: var string, vctoolsetArg: var string, verboseArg: var bool, clArgs: var seq[string]) = ## Cannot use usual command-line argument parser here ## Since vccexe command-line arguments are intermingled @@ -125,7 +129,7 @@ proc parseVccexeCmdLine(argseq: seq[string], responseargs = parseCmdLine(responsecontent) parseVccexeCmdLine(responseargs, vccversionArg, printPathArg, vcvarsallArg, commandArg, noCommandArg, platformArg, sdkTypeArg, - sdkVersionArg, verboseArg, clArgs) + sdkVersionArg, vctoolsetArg, verboseArg, clArgs) elif wargv.startsWith(vccversionPrefix): # Check for vccversion vccversionArg.add(wargv.substr(vccversionSepIdx + 1)) elif wargv.cmpIgnoreCase(printPathPrefix) == 0: # Check for printPath @@ -142,6 +146,8 @@ proc parseVccexeCmdLine(argseq: seq[string], sdkTypeArg = parseEnum[VccPlatformType](wargv.substr(sdktypeSepIdx + 1)) elif wargv.startsWith(sdkversionPrefix): # Check for sdkversion sdkVersionArg = wargv.substr(sdkversionSepIdx + 1) + elif wargv.startsWith(vctoolsetPrefix): # Check for vctoolset + vctoolsetArg = wargv.substr(vctoolsetSepIdx + 1) elif wargv.startsWith(verbosePrefix): verboseArg = true else: # Regular cl.exe argument -> store for final cl.exe invocation @@ -158,13 +164,14 @@ when isMainModule: var platformArg: VccArch var sdkTypeArg: VccPlatformType var sdkVersionArg: string + var vctoolsetArg: string var verboseArg: bool = false var clArgs: seq[string] = @[] let wrapperArgs = commandLineParams() parseVccexeCmdLine(wrapperArgs, vccversionArg, printPathArg, vcvarsallArg, - commandArg, noCommandArg, platformArg, sdkTypeArg, sdkVersionArg, + commandArg, noCommandArg, platformArg, sdkTypeArg, sdkVersionArg, vctoolsetArg, verboseArg, clArgs) @@ -195,7 +202,7 @@ when isMainModule: echo "$1: $2" % [head, vcvarsallArg] # Call vcvarsall to get the appropriate VCC process environment - var vcvars = vccVarsAll(vcvarsallArg, platformArg, sdkTypeArg, sdkVersionArg, verboseArg) + var vcvars = vccVarsAll(vcvarsallArg, platformArg, sdkTypeArg, sdkVersionArg, vctoolsetArg, verboseArg) if vcvars != nil: for vccEnvKey, vccEnvVal in vcvars: putEnv(vccEnvKey, vccEnvVal) diff --git a/tools/vccexe/vcvarsall.nim b/tools/vccexe/vcvarsall.nim index 29d13cc7e..73b103e3c 100644 --- a/tools/vccexe/vcvarsall.nim +++ b/tools/vccexe/vcvarsall.nim @@ -33,7 +33,7 @@ type vccplatUWP = "uwp", ## Universal Windows Platform (UWP) Application vccplatOneCore = "onecore" # Undocumented platform type in the Windows SDK, probably XBox One SDK platform type. -proc vccVarsAll*(path: string, arch: VccArch = vccarchUnspecified, platform_type: VccPlatformType = vccplatEmpty, sdk_version: string = "", verbose: bool = false): StringTableRef = +proc vccVarsAll*(path: string, arch: VccArch = vccarchUnspecified, platform_type: VccPlatformType = vccplatEmpty, sdk_version, vctoolset: string = "", verbose: bool = false): StringTableRef = ## Returns a string table containing the proper process environment to successfully execute VCC compile commands for the specified SDK version, CPU architecture and platform type. ## ## path @@ -44,6 +44,8 @@ proc vccVarsAll*(path: string, arch: VccArch = vccarchUnspecified, platform_type ## The compile target Platform Type. Defaults to the Windows Desktop platform, i.e. a regular Windows executable binary. ## sdk_version ## The Windows SDK version to use. + ## vctoolset + ## Visual Studio compiler toolset to use. ## verbose ## Echo the command-line passed on to the system to load the VCC environment. Defaults to `false`. @@ -63,6 +65,9 @@ proc vccVarsAll*(path: string, arch: VccArch = vccarchUnspecified, platform_type if sdk_version.len > 0: args.add(sdk_version) + + if vctoolset.len > 0: + args.add("-vcvars_ver="&vctoolset) let argStr = args.join " " From 6c15958a835b645f3acdc3d1d012b10d0df43454 Mon Sep 17 00:00:00 2001 From: Gabriel Huber Date: Sun, 12 Feb 2023 16:41:27 +0100 Subject: [PATCH 3693/3768] Change the default stdlib location for Linux packaging (#21328) * Correctly evaluate the Nim prefix on Posix * Document new packaging layout --- compiler/options.nim | 22 ++++++++++++---------- doc/packaging.md | 7 +++++-- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/compiler/options.nim b/compiler/options.nim index 833d86a16..862b42446 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -717,22 +717,24 @@ proc getPrefixDir*(conf: ConfigRef): AbsoluteDir = ## clone or using installed nim, so that these exist: `result/doc/advopt.txt` ## and `result/lib/system.nim` if not conf.prefixDir.isEmpty: result = conf.prefixDir - else: result = AbsoluteDir splitPath(getAppDir()).head + else: + let binParent = AbsoluteDir splitPath(getAppDir()).head + when defined(posix): + if binParent == AbsoluteDir"/usr": + result = AbsoluteDir"/usr/lib/nim" + elif binParent == AbsoluteDir"/usr/local": + result = AbsoluteDir"/usr/local/lib/nim" + else: + result = binParent + else: + result = binParent proc setDefaultLibpath*(conf: ConfigRef) = # set default value (can be overwritten): if conf.libpath.isEmpty: # choose default libpath: var prefix = getPrefixDir(conf) - when defined(posix): - if prefix == AbsoluteDir"/usr": - conf.libpath = AbsoluteDir"/usr/lib/nim" - elif prefix == AbsoluteDir"/usr/local": - conf.libpath = AbsoluteDir"/usr/local/lib/nim" - else: - conf.libpath = prefix / RelativeDir"lib" - else: - conf.libpath = prefix / RelativeDir"lib" + conf.libpath = prefix / RelativeDir"lib" # Special rule to support other tools (nimble) which import the compiler # modules and make use of them. diff --git a/doc/packaging.md b/doc/packaging.md index 9a1cc0f6e..522ca6d44 100644 --- a/doc/packaging.md +++ b/doc/packaging.md @@ -69,8 +69,11 @@ Hints on the build process: What to install: -- The expected stdlib location is /usr/lib/nim -- Global configuration files under /etc/nim +- The expected stdlib location is `/usr/lib/nim/lib`, previously it was just `/usr/lib/nim` +- `nimdoc.css` and `nimdoc.cls` from the `doc` folder should go into `/usr/lib/nim/doc/` +- `tools/nim-gdb.py` should go into `/usr/lib/nim/tools/` +- `tools/dochack/dochack.js` should be installed to `/usr/lib/nim/tools/dochack/` +- Global configuration files under `/etc/nim` - Optionally: manpages, documentation, shell completion - When installing documentation, .idx files are not required - The "compiler" directory contains compiler sources and should not be part of the compiler binary package From 684c3b3aeb90ae549382ed0f9c3286464c72809f Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Tue, 14 Feb 2023 14:00:29 +0800 Subject: [PATCH 3694/3768] fixes comments about type bound operations (#21365) --- compiler/liftdestructors.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 2b27a58fc..738f659b7 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -8,7 +8,7 @@ # ## This module implements lifting for type-bound operations -## (``=sink``, ``=``, ``=destroy``, ``=deepCopy``). +## (``=sink``, ``=copy``, ``=destroy``, ``=deepCopy``). import modulegraphs, lineinfos, idents, ast, renderer, semdata, sighashes, lowerings, options, types, msgs, magicsys, tables, ccgutils From 406d3021314c66fdf4c715efeeaea3c8f478fcaa Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Tue, 14 Feb 2023 14:29:30 +0800 Subject: [PATCH 3695/3768] allow omitting stmts using `finally` as post expr blocks; make it consistent with `else`, `except` etc. (#21361) allow omitting stmts using `finally` as post expr blocks --- compiler/parser.nim | 2 +- tests/parser/tpostexprblocks.nim | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/compiler/parser.nim b/compiler/parser.nim index 14882d415..abfc7b323 100644 --- a/compiler/parser.nim +++ b/compiler/parser.nim @@ -1496,7 +1496,7 @@ proc postExprBlocks(p: var Parser, x: PNode): PNode = result = makeCall(result) getTok(p) skipComment(p, result) - if p.tok.tokType notin {tkOf, tkElif, tkElse, tkExcept}: + if p.tok.tokType notin {tkOf, tkElif, tkElse, tkExcept, tkFinally}: var stmtList = newNodeP(nkStmtList, p) stmtList.add parseStmt(p) # to keep backwards compatibility (see tests/vm/tstringnil) diff --git a/tests/parser/tpostexprblocks.nim b/tests/parser/tpostexprblocks.nim index d272c712f..6cd4a8350 100644 --- a/tests/parser/tpostexprblocks.nim +++ b/tests/parser/tpostexprblocks.nim @@ -498,6 +498,13 @@ StmtList StmtList DiscardStmt Empty + + Call + Ident "foo" + Finally + StmtList + DiscardStmt + Empty ''' """ @@ -655,3 +662,7 @@ dumpTree: discard finally: discard + + foo: + finally: + discard From 1d06c2b6cf84bcf65b67e88667dba4fd5e8ba519 Mon Sep 17 00:00:00 2001 From: c-blake Date: Tue, 14 Feb 2023 02:00:30 -0500 Subject: [PATCH 3696/3768] This adds `parseutils.parseSize`, an inverse to `strutils.formatSize` (#21349) * This adds `parseutils.parseSize`, an inverse to `strutils.formatSize` which has existed since 2017. It is useful for parsing the compiler's own output logs (like SuccessX) or many other scenarios where "human readable" units have been chosen. The doc comment and tests explain accepted syntax in detail. Big units lead to small numbers, often with a fractional part, but we parse into an `int64` since that is what `formatSize` stringifies and this is an inverse over partial function slots. Although metric prefixes z & y for zettabyte & yottabyte are accepted, these will saturate the result at `int64.high` unless the qualified number is a small fraction. This should not be much of a problem until such sizes are common (at which point another overload with the parse result either `float64` or `int128` could be added). Tests avoids `test()` because of a weakly related static: test() failure as mentioned in https://github.com/nim-lang/Nim/pull/21325. This is a more elemental VM failure. As such, it needs its own failure exhibition issue that is a smaller test case. (I am working on that, but unless there is a burning need to `parseSize` at compile-time before run-time it need not hold up this PR.) * This worked with `int` but fails with `int64`. Try for green tests. * Lift 2-result matching into a `checkParseSize` template and format as a table of input & 2 expected outputs which seems nicer and to address https://github.com/nim-lang/Nim/pull/21349#pullrequestreview-1294407679 * Fix (probably) the i386 trouble by using `int64` consistently. * Improve documentation by mentioning saturation. * Improve documentation with `runnableExamples` and a little more detail in the main doc comment based on excellent code review by @juancarlospaco: https://github.com/nim-lang/Nim/pull/21349#pullrequestreview-1294564155 * Address some more @juancarlospaco code review concerns. * Remove a stray space. * Mention milli-bytes in docs to maybe help clarify why wild conventions are so prone to going case-insensitive-metric. * Add some parens. --- changelog.md | 2 +- lib/pure/parseutils.nim | 65 ++++++++++++++++++++++++++++++++++++ tests/stdlib/tparseutils.nim | 41 +++++++++++++++++++++++ 3 files changed, 107 insertions(+), 1 deletion(-) diff --git a/changelog.md b/changelog.md index 0389faad9..2db577d0b 100644 --- a/changelog.md +++ b/changelog.md @@ -10,7 +10,7 @@ [//]: # "Additions:" - +- Added `parseutils.parseSize` - inverse to `strutils.formatSize` - to parse human readable sizes. [//]: # "Deprecations:" diff --git a/lib/pure/parseutils.nim b/lib/pure/parseutils.nim index 2bb23c626..c4d59d892 100644 --- a/lib/pure/parseutils.nim +++ b/lib/pure/parseutils.nim @@ -592,6 +592,71 @@ proc parseFloat*(s: openArray[char], number: var float): int {. if result != 0: number = bf +func toLowerAscii(c: char): char = + if c in {'A'..'Z'}: char(uint8(c) xor 0b0010_0000'u8) else: c + +func parseSize*(s: openArray[char], size: var int64, alwaysBin=false): int = + ## Parse a size qualified by binary or metric units into `size`. This format + ## is often called "human readable". Result is the number of processed chars + ## or 0 on parse errors and size is rounded to the nearest integer. Trailing + ## garbage like "/s" in "1k/s" is allowed and detected by `result < s.len`. + ## + ## To simplify use, following non-rare wild conventions, and since fractional + ## data like milli-bytes is so rare, unit matching is case-insensitive but for + ## the 'i' distinguishing binary-metric from metric (which cannot be 'I'). + ## + ## An optional trailing 'B|b' is ignored but processed. I.e., you must still + ## know if units are bytes | bits or infer this fact via the case of s[^1] (if + ## users can even be relied upon to use 'B' for byte and 'b' for bit or have + ## that be s[^1]). + ## + ## If `alwaysBin==true` then scales are always binary-metric, but e.g. "KiB" + ## is still accepted for clarity. If the value would exceed the range of + ## `int64`, `size` saturates to `int64.high`. Supported metric prefix chars + ## include k, m, g, t, p, e, z, y (but z & y saturate unless the number is a + ## small fraction). + ## + ## **See also:** + ## * https://en.wikipedia.org/wiki/Binary_prefix + ## * `formatSize module`_ for formatting + runnableExamples: + var res: int64 # caller must still know if 'b' refers to bytes|bits + doAssert parseSize("10.5 MB", res) == 7 + doAssert res == 10_500_000 # decimal metric Mega prefix + doAssert parseSize("64 mib", res) == 6 + doAssert res == 67108864 # 64 shl 20 + doAssert parseSize("1G/h", res, true) == 2 # '/' stops parse + doAssert res == 1073741824 # 1 shl 30, forced binary metric + const prefix = "b" & "kmgtpezy" # byte|bit & lowCase metric-ish prefixes + const scaleM = [1.0, 1e3, 1e6, 1e9, 1e12, 1e15, 1e18, 1e21, 1e24] # 10^(3*idx) + const scaleB = [1.0, 1024, 1048576, 1073741824, 1099511627776.0, # 2^(10*idx) + 1125899906842624.0, 1152921504606846976.0, # ldexp? + 1.180591620717411303424e21, 1.208925819614629174706176e24] + var number: float + var scale = 1.0 + result = parseFloat(s, number) + if number < 0: # While parseFloat accepts negatives .. + result = 0 #.. we do not since sizes cannot be < 0 + if result > 0: + let start = result # Save spot to maybe unwind white to EOS + while result < s.len and s[result] in Whitespace: + inc result + if result < s.len: # Illegal starting char => unity + if (let si = prefix.find(s[result].toLowerAscii); si >= 0): + inc result # Now parse the scale + scale = if alwaysBin: scaleB[si] else: scaleM[si] + if result < s.len and s[result] == 'i': + scale = scaleB[si] # Switch from default to binary-metric + inc result + if result < s.len and s[result].toLowerAscii == 'b': + inc result # Skip optional '[bB]' + else: # Unwind result advancement when there.. + result = start #..is no unit to the end of `s`. + var sizeF = number * scale + 0.5 # Saturate to int64.high when too big + size = if sizeF > 9223372036854774784.0: int64.high else: sizeF.int64 +# Above constant=2^63-1024 avoids C UB; github.com/nim-lang/Nim/issues/20102 or +# stackoverflow.com/questions/20923556/math-pow2-63-1-math-pow2-63-512-is-true + type InterpolatedKind* = enum ## Describes for `interpolatedFragments` ## which part of the interpolated string is diff --git a/tests/stdlib/tparseutils.nim b/tests/stdlib/tparseutils.nim index 102e74067..218dd08f6 100644 --- a/tests/stdlib/tparseutils.nim +++ b/tests/stdlib/tparseutils.nim @@ -55,5 +55,46 @@ proc test() = doAssert res == @[(17, "9.123456789012344"), (18, "11.123456789012344"), (17, "9.123456789012344"), (17, "8.123456789012344"), (16, "9.12345678901234"), (17, "9.123456789012344")] + test() static: test() + +block: # With this included, static: test() crashes the compiler (from a + # VM problem with parseSize calling parseFloat). + var sz: int64 + template checkParseSize(s, expectLen, expectVal) = + if (let got = parseSize(s, sz); got != expectLen): + raise newException(IOError, "got len " & $got & " != " & $expectLen) + if sz != expectVal: + raise newException(IOError, "got sz " & $sz & " != " & $expectVal) + # STRING LEN SZ + # Good, complete parses + checkParseSize "1 b" , 4, 1 + checkParseSize "1 B" , 4, 1 + checkParseSize "1k" , 2, 1000 + checkParseSize "1 kib" , 5, 1024 + checkParseSize "1 ki" , 4, 1024 + checkParseSize "1mi" , 3, 1048576 + checkParseSize "1 mi" , 4, 1048576 + checkParseSize "1 mib" , 5, 1048576 + checkParseSize "1 Mib" , 5, 1048576 + checkParseSize "1 MiB" , 5, 1048576 + checkParseSize "1.23GiB", 7, 1320702444 # 1320702443.52 rounded + checkParseSize "0.001k" , 6, 1 + checkParseSize "0.0004k", 7, 0 + checkParseSize "0.0006k", 7, 1 + # Incomplete parses + checkParseSize "1 " , 1, 1 # Trailing white IGNORED + checkParseSize "1 B " , 4, 1 # Trailing white IGNORED + checkParseSize "1 B/s" , 4, 1 # Trailing junk IGNORED + checkParseSize "1 kX" , 3, 1000 + checkParseSize "1 kiX" , 4, 1024 + checkParseSize "1j" , 1, 1 # Unknown prefix IGNORED + checkParseSize "1 jib" , 2, 1 # Unknown prefix post space + checkParseSize "1 ji" , 3, 1 + # Bad parses; `sz` should stay last good|incomplete value + checkParseSize "-1b" , 0, 1 # Negative numbers + checkParseSize "abc" , 0, 1 # Non-numeric + checkParseSize " 12" , 0, 1 # Leading white + # Value Edge cases + checkParseSize "9223372036854775807", 19, int64.high From cac49694c011e851707cb3a86a3263c741559b3d Mon Sep 17 00:00:00 2001 From: Jake Leahy Date: Tue, 14 Feb 2023 18:14:19 +1100 Subject: [PATCH 3697/3768] `std/asyncjs` allow transforming proc types (#21356) * Add test case * Implement JS async transform for nnkProcTy --- lib/js/asyncjs.nim | 10 +++++++++- tests/js/tasyncjs.nim | 5 +++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/lib/js/asyncjs.nim b/lib/js/asyncjs.nim index 364f7a21a..8e2f85156 100644 --- a/lib/js/asyncjs.nim +++ b/lib/js/asyncjs.nim @@ -100,10 +100,18 @@ proc isFutureVoid(node: NimNode): bool = node[1].kind == nnkIdent and $node[1] == "void" proc generateJsasync(arg: NimNode): NimNode = - if arg.kind notin {nnkProcDef, nnkLambda, nnkMethodDef, nnkDo}: + if arg.kind notin {nnkProcDef, nnkLambda, nnkMethodDef, nnkDo, nnkProcTy}: error("Cannot transform this node kind into an async proc." & " proc/method definition or lambda node expected.") + # Transform type X = proc (): something {.async.} + # into type X = proc (): Future[something] + if arg.kind == nnkProcTy: + result = arg + if arg[0][0].kind == nnkEmpty: + result[0][0] = quote do: Future[void] + return result + result = arg var isVoid = false let jsResolve = ident("jsResolve") diff --git a/tests/js/tasyncjs.nim b/tests/js/tasyncjs.nim index 00753a16c..3de143643 100644 --- a/tests/js/tasyncjs.nim +++ b/tests/js/tasyncjs.nim @@ -94,4 +94,9 @@ proc main() {.async.} = doAssert "foobar: 7" in $reason.message echo "done" # justified here to make sure we're running this, since it's inside `async` +block asyncPragmaInType: + type Handler = proc () {.async.} + proc foo() {.async.} = discard + var x: Handler = foo + discard main() From 56a4d246a4288a7005de0b20e312fa4e4af89534 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Tue, 14 Feb 2023 20:11:14 +0800 Subject: [PATCH 3698/3768] Refines raises list in osproc (#21323) * Remove Exception from raises in closeImpl * Update osproc.nim * refine errors * add ValueError * cast raises * refactor raises lists * Update lib/pure/osproc.nim * Update lib/pure/osproc.nim --------- Co-authored-by: Antonis Geralis <43617260+planetis-m@users.noreply.github.com> Co-authored-by: Andreas Rumpf --- lib/pure/osproc.nim | 41 ++++++++++++++++++++------------------- lib/pure/streams.nim | 2 +- lib/std/socketstreams.nim | 2 +- 3 files changed, 23 insertions(+), 22 deletions(-) diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 5e130b47a..9a0e5895d 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -76,7 +76,7 @@ type proc execProcess*(command: string, workingDir: string = "", args: openArray[string] = [], env: StringTableRef = nil, options: set[ProcessOption] = {poStdErrToStdOut, poUsePath, poEvalCommand}): - string {.rtl, extern: "nosp$1", + string {.rtl, extern: "nosp$1", raises: [OSError, IOError], tags: [ExecIOEffect, ReadIOEffect, RootEffect].} ## A convenience procedure that executes ``command`` with ``startProcess`` ## and returns its output as a string. @@ -120,7 +120,7 @@ proc execCmd*(command: string): int {.rtl, extern: "nosp$1", proc startProcess*(command: string, workingDir: string = "", args: openArray[string] = [], env: StringTableRef = nil, options: set[ProcessOption] = {poStdErrToStdOut}): - owned(Process) {.rtl, extern: "nosp$1", + owned(Process) {.rtl, extern: "nosp$1", raises: [OSError, IOError], tags: [ExecIOEffect, ReadEnvEffect, RootEffect].} ## Starts a process. `Command` is the executable file, `workingDir` is the ## process's working directory. If ``workingDir == ""`` the current directory @@ -152,7 +152,7 @@ proc startProcess*(command: string, workingDir: string = "", ## <#execProcess,string,string,openArray[string],StringTableRef,set[ProcessOption]>`_ ## * `execCmd proc <#execCmd,string>`_ -proc close*(p: Process) {.rtl, extern: "nosp$1", tags: [WriteIOEffect].} +proc close*(p: Process) {.rtl, extern: "nosp$1", raises: [IOError, OSError], tags: [WriteIOEffect].} ## When the process has finished executing, cleanup related handles. ## ## .. warning:: If the process has not finished executing, this will forcibly @@ -201,7 +201,7 @@ proc kill*(p: Process) {.rtl, extern: "nosp$1", tags: [].} ## * `terminate proc <#terminate,Process>`_ ## * `posix_utils.sendSignal(pid: Pid, signal: int) `_ -proc running*(p: Process): bool {.rtl, extern: "nosp$1", tags: [].} +proc running*(p: Process): bool {.rtl, extern: "nosp$1", raises: [OSError], tags: [].} ## Returns true if the process `p` is still running. Returns immediately. proc processID*(p: Process): int {.rtl, extern: "nosp$1".} = @@ -212,7 +212,7 @@ proc processID*(p: Process): int {.rtl, extern: "nosp$1".} = return p.id proc waitForExit*(p: Process, timeout: int = -1): int {.rtl, - extern: "nosp$1", tags: [].} + extern: "nosp$1", raises: [OSError, ValueError], tags: [].} ## Waits for the process to finish and returns `p`'s error code. ## ## .. warning:: Be careful when using `waitForExit` for processes created without @@ -221,7 +221,7 @@ proc waitForExit*(p: Process, timeout: int = -1): int {.rtl, ## On posix, if the process has exited because of a signal, 128 + signal ## number will be returned. -proc peekExitCode*(p: Process): int {.rtl, extern: "nosp$1", tags: [].} +proc peekExitCode*(p: Process): int {.rtl, extern: "nosp$1", raises: [OSError], tags: [].} ## Return `-1` if the process is still running. Otherwise the process' exit code. ## ## On posix, if the process has exited because of a signal, 128 + signal @@ -237,7 +237,7 @@ proc inputStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [].} ## * `outputStream proc <#outputStream,Process>`_ ## * `errorStream proc <#errorStream,Process>`_ -proc outputStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [].} +proc outputStream*(p: Process): Stream {.rtl, extern: "nosp$1", raises: [IOError, OSError], tags: [].} ## Returns ``p``'s output stream for reading from. ## ## You cannot perform peek/write/setOption operations to this stream. @@ -289,7 +289,7 @@ proc peekableErrorStream*(p: Process): Stream {.rtl, extern: "nosp$1", tags: [], ## * `errorStream proc <#errorStream,Process>`_ ## * `peekableOutputStream proc <#peekableOutputStream,Process>`_ -proc inputHandle*(p: Process): FileHandle {.rtl, extern: "nosp$1", +proc inputHandle*(p: Process): FileHandle {.rtl, raises: [], extern: "nosp$1", tags: [].} = ## Returns ``p``'s input file handle for writing to. ## @@ -302,7 +302,7 @@ proc inputHandle*(p: Process): FileHandle {.rtl, extern: "nosp$1", result = p.inHandle proc outputHandle*(p: Process): FileHandle {.rtl, extern: "nosp$1", - tags: [].} = + raises: [], tags: [].} = ## Returns ``p``'s output file handle for reading from. ## ## .. warning:: The returned `FileHandle` should not be closed manually as @@ -314,7 +314,7 @@ proc outputHandle*(p: Process): FileHandle {.rtl, extern: "nosp$1", result = p.outHandle proc errorHandle*(p: Process): FileHandle {.rtl, extern: "nosp$1", - tags: [].} = + raises: [], tags: [].} = ## Returns ``p``'s error file handle for reading from. ## ## .. warning:: The returned `FileHandle` should not be closed manually as @@ -325,7 +325,7 @@ proc errorHandle*(p: Process): FileHandle {.rtl, extern: "nosp$1", ## * `outputHandle proc <#outputHandle,Process>`_ result = p.errHandle -proc countProcessors*(): int {.rtl, extern: "nosp$1".} = +proc countProcessors*(): int {.rtl, extern: "nosp$1", raises: [].} = ## Returns the number of the processors/cores the machine has. ## Returns 0 if it cannot be detected. ## It is implemented just calling `cpuinfo.countProcessors`. @@ -339,6 +339,7 @@ proc execProcesses*(cmds: openArray[string], beforeRunEvent: proc(idx: int) = nil, afterRunEvent: proc(idx: int, p: Process) = nil): int {.rtl, extern: "nosp$1", + raises: [ValueError, OSError, IOError], tags: [ExecIOEffect, TimeEffect, ReadEnvEffect, RootEffect], effectsOf: [beforeRunEvent, afterRunEvent].} = ## Executes the commands `cmds` in parallel. @@ -452,7 +453,7 @@ proc execProcesses*(cmds: openArray[string], if afterRunEvent != nil: afterRunEvent(i, p) close(p) -iterator lines*(p: Process, keepNewLines = false): string {.since: (1, 3), tags: [ReadIOEffect].} = +iterator lines*(p: Process, keepNewLines = false): string {.since: (1, 3), raises: [OSError, IOError, ValueError], tags: [ReadIOEffect].} = ## Convenience iterator for working with `startProcess` to read data from a ## background process. ## @@ -481,7 +482,8 @@ iterator lines*(p: Process, keepNewLines = false): string {.since: (1, 3), tags: yield line discard waitForExit(p) -proc readLines*(p: Process): (seq[string], int) {.since: (1, 3).} = +proc readLines*(p: Process): (seq[string], int) {.since: (1, 3), + raises: [OSError, IOError, ValueError], tags: [ReadIOEffect].} = ## Convenience function for working with `startProcess` to read data from a ## background process. ## @@ -747,7 +749,7 @@ when defined(windows) and not defined(useNimRtl): const errFileNotFound = 2.int if lastError.int in {errInvalidParameter, errFileNotFound}: raiseOSError(lastError, - "Requested command not found: '$1'. OS error:" % command) + "Requested command not found: '" & command & "'. OS error:") else: raiseOSError(lastError, command) result.fProcessHandle = procInfo.hProcess @@ -955,13 +957,13 @@ elif not defined(useNimRtl): not defined(useClone) and not defined(linux) when useProcessAuxSpawn: proc startProcessAuxSpawn(data: StartProcessData): Pid {. - tags: [ExecIOEffect, ReadEnvEffect, ReadDirEffect, RootEffect], gcsafe.} + raises: [OSError], tags: [ExecIOEffect, ReadEnvEffect, ReadDirEffect, RootEffect], gcsafe.} else: proc startProcessAuxFork(data: StartProcessData): Pid {. - tags: [ExecIOEffect, ReadEnvEffect, ReadDirEffect, RootEffect], gcsafe.} + raises: [OSError], tags: [ExecIOEffect, ReadEnvEffect, ReadDirEffect, RootEffect], gcsafe.} {.push stacktrace: off, profiler: off.} proc startProcessAfterFork(data: ptr StartProcessData) {. - tags: [ExecIOEffect, ReadEnvEffect, ReadDirEffect, RootEffect], cdecl, gcsafe.} + raises: [OSError], tags: [ExecIOEffect, ReadEnvEffect, ReadDirEffect, RootEffect], cdecl, gcsafe.} {.pop.} proc startProcess(command: string, workingDir: string = "", @@ -1131,8 +1133,7 @@ elif not defined(useNimRtl): let sizeRead = read(data.pErrorPipe[readIdx], addr error, sizeof(error)) if sizeRead == sizeof(error): raiseOSError(osLastError(), - "Could not find command: '$1'. OS error: $2" % - [$data.sysCommand, $strerror(error)]) + "Could not find command: '" & $data.sysCommand & "'. OS error: " & $strerror(error)) return pid @@ -1578,7 +1579,7 @@ proc execCmdEx*(command: string, options: set[ProcessOption] = { poStdErrToStdOut, poUsePath}, env: StringTableRef = nil, workingDir = "", input = ""): tuple[ output: string, - exitCode: int] {.tags: + exitCode: int] {.raises: [OSError, IOError], tags: [ExecIOEffect, ReadIOEffect, RootEffect], gcsafe.} = ## A convenience proc that runs the `command`, and returns its `output` and ## `exitCode`. `env` and `workingDir` params behave as for `startProcess`. diff --git a/lib/pure/streams.nim b/lib/pure/streams.nim index 945e85934..003f8ca4c 100644 --- a/lib/pure/streams.nim +++ b/lib/pure/streams.nim @@ -115,7 +115,7 @@ type ## * That these fields here shouldn't be used directly. ## They are accessible so that a stream implementation can override them. closeImpl*: proc (s: Stream) - {.nimcall, raises: [Exception, IOError, OSError], tags: [WriteIOEffect], gcsafe.} + {.nimcall, raises: [IOError, OSError], tags: [WriteIOEffect], gcsafe.} atEndImpl*: proc (s: Stream): bool {.nimcall, raises: [Defect, IOError, OSError], tags: [], gcsafe.} setPositionImpl*: proc (s: Stream, pos: int) diff --git a/lib/std/socketstreams.nim b/lib/std/socketstreams.nim index 5c882858d..b53d6a5d3 100644 --- a/lib/std/socketstreams.nim +++ b/lib/std/socketstreams.nim @@ -146,7 +146,7 @@ proc wsFlush(s: Stream) = s.lastFlush = s.buf.len proc rsClose(s: Stream) = - {.cast(tags: []).}: + {.cast(raises: [IOError, OSError]), cast(tags: []).}: # todo fixme maybe do something? var s = ReadSocketStream(s) s.data.close() From e24d0e5faf1f6f2906262457007261e2358f1e5d Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Wed, 15 Feb 2023 20:03:11 +0800 Subject: [PATCH 3699/3768] remove legacy code; the first iteration now can build Nim with cpp backend (#21373) --- koch.nim | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/koch.nim b/koch.nim index 27953e05a..fa498e017 100644 --- a/koch.nim +++ b/koch.nim @@ -295,11 +295,7 @@ proc boot(args: string) = let nimStart = findStartNim().quoteShell() for i in 0..2: - # Nim versions < (1, 1) expect Nim's exception type to have a 'raiseId' field for - # C++ interop. Later Nim versions do this differently and removed the 'raiseId' field. - # Thus we always bootstrap the first iteration with "c" and not with "cpp" as - # a workaround. - let defaultCommand = if useCpp and i > 0: "cpp" else: "c" + let defaultCommand = if useCpp: "cpp" else: "c" let bootOptions = if args.len == 0 or args.startsWith("-"): defaultCommand else: "" echo "iteration: ", i+1 var extraOption = "" From c91ef1a09f3284315e9e66c32532f5a8e3ba9297 Mon Sep 17 00:00:00 2001 From: c-blake Date: Wed, 15 Feb 2023 11:41:28 -0500 Subject: [PATCH 3700/3768] Fix `closeHandle` bug, add `setFileSize`, make `resize` work on Windows (#21375) * Add general purpose `setFileSize` (unexported for now). Use to simplify `memfiles.open` as well as make robust (via hard allocation, not merely `ftruncate` address space allocation) on systems with `posix_fallocate`. As part of this, fix a bad `closeHandle` return check bug on Windows and add `MemFile.resize` for Windows now that setFileSize makes that easier. * Adapt existing test to exercise newly portable `MemFile.resize`. * Since Apple has never provided `posix_fallocate`, provide a fallback. This is presently written in terms of `ftruncate`, but it can be improved to use `F_PREALLOCATE` instead, as mentioned in a comment. --- lib/posix/posix.nim | 11 +++- lib/pure/memfiles.nim | 123 ++++++++++++++++++++++++------------ tests/stdlib/tmemfiles2.nim | 5 +- 3 files changed, 92 insertions(+), 47 deletions(-) diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index 43346608d..aee9308a6 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -195,8 +195,14 @@ proc open*(a1: cstring, a2: cint, mode: Mode | cint = 0.Mode): cint {.inline.} = proc posix_fadvise*(a1: cint, a2, a3: Off, a4: cint): cint {. importc, header: "".} -proc posix_fallocate*(a1: cint, a2, a3: Off): cint {. - importc, header: "".} + +proc ftruncate*(a1: cint, a2: Off): cint {.importc, header: "".} +when defined(osx): # 2001 POSIX evidently does not concern Apple + proc posix_fallocate*(a1: cint, a2, a3: Off): cint = + ftruncate(a1, a2 + a3) # Set size to off + len, max offset +else: # TODO: Use fcntl(fd, F_PREALLOCATE, ..) above + proc posix_fallocate*(a1: cint, a2, a3: Off): cint {. + importc, header: "".} when not defined(haiku) and not defined(openbsd): proc fmtmsg*(a1: int, a2: cstring, a3: cint, @@ -511,7 +517,6 @@ proc fpathconf*(a1, a2: cint): int {.importc, header: "".} proc fsync*(a1: cint): cint {.importc, header: "".} ## synchronize a file's buffer cache to the storage device -proc ftruncate*(a1: cint, a2: Off): cint {.importc, header: "".} proc getcwd*(a1: cstring, a2: int): cstring {.importc, header: "", sideEffect.} proc getuid*(): Uid {.importc, header: "", sideEffect.} ## returns the real user ID of the calling process diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim index 9ff8bcd74..58c4888f1 100644 --- a/lib/pure/memfiles.nim +++ b/lib/pure/memfiles.nim @@ -35,6 +35,31 @@ proc newEIO(msg: string): ref IOError = new(result) result.msg = msg +proc setFileSize(fh: FileHandle, newFileSize = -1): OSErrorCode = + ## Set the size of open file pointed to by `fh` to `newFileSize` if != -1. + ## Space is only allocated if that is cheaper than writing to the file. This + ## routine returns the last OSErrorCode found rather than raising to support + ## old rollback/clean-up code style. [ Should maybe move to std/osfiles. ] + if newFileSize == -1: + return + when defined(windows): + var sizeHigh = int32(newFileSize shr 32) + let sizeLow = int32(newFileSize and 0xffffffff) + let status = setFilePointer(fh, sizeLow, addr(sizeHigh), FILE_BEGIN) + let lastErr = osLastError() + if (status == INVALID_SET_FILE_POINTER and lastErr.int32 != NO_ERROR) or + setEndOfFile(fh) == 0: + result = lastErr + else: + var e: cint # posix_fallocate truncates up when needed. + when declared(posix_fallocate): + while (e = posix_fallocate(fh, 0, newFileSize); e == EINTR): + discard + if e in [EINVAL, EOPNOTSUPP] and ftruncate(fh, newFileSize) == -1: + result = osLastError() # fallback arguable; Most portable, but allows SEGV + elif e != 0: + result = osLastError() + type MemFile* = object ## represents a memory mapped file mem*: pointer ## a pointer to the memory mapped file. The pointer @@ -182,17 +207,8 @@ proc open*(filename: string, mode: FileMode = fmRead, if result.fHandle == INVALID_HANDLE_VALUE: fail(osLastError(), "error opening file") - if newFileSize != -1: - var - sizeHigh = int32(newFileSize shr 32) - sizeLow = int32(newFileSize and 0xffffffff) - - var status = setFilePointer(result.fHandle, sizeLow, addr(sizeHigh), - FILE_BEGIN) - let lastErr = osLastError() - if (status == INVALID_SET_FILE_POINTER and lastErr.int32 != NO_ERROR) or - (setEndOfFile(result.fHandle) == 0): - fail(lastErr, "error setting file size") + if (let e = setFileSize(result.fHandle.FileHandle, newFileSize); + e != 0.OSErrorCode): fail(e, "error setting file size") # since the strings are always 'nil', we simply always call # CreateFileMappingW which should be slightly faster anyway: @@ -226,7 +242,7 @@ proc open*(filename: string, mode: FileMode = fmRead, result.wasOpened = true if not allowRemap and result.fHandle != INVALID_HANDLE_VALUE: - if closeHandle(result.fHandle) == 0: + if closeHandle(result.fHandle) != 0: result.fHandle = INVALID_HANDLE_VALUE else: @@ -249,9 +265,8 @@ proc open*(filename: string, mode: FileMode = fmRead, # Is there an exception that wraps it? fail(osLastError(), "error opening file") - if newFileSize != -1: - if ftruncate(result.handle, newFileSize) == -1: - fail(osLastError(), "error setting file size") + if (let e = setFileSize(result.handle.FileHandle, newFileSize); + e != 0.OSErrorCode): fail(e, "error setting file size") if mappedSize != -1: result.size = mappedSize @@ -306,34 +321,58 @@ proc flush*(f: var MemFile; attempts: Natural = 3) = if lastErr != EBUSY.OSErrorCode: raiseOSError(lastErr, "error flushing mapping") -when defined(posix) or defined(nimdoc): - proc resize*(f: var MemFile, newFileSize: int) {.raises: [IOError, OSError].} = - ## resize and re-map the file underlying an `allowRemap MemFile`. - ## **Note**: this assumes the entire file is mapped read-write at offset zero. - ## Also, the value of `.mem` will probably change. - ## **Note**: This is not (yet) available on Windows. - when defined(posix): - if f.handle == -1: - raise newException(IOError, - "Cannot resize MemFile opened with allowRemap=false") - if ftruncate(f.handle, newFileSize) == -1: - raiseOSError(osLastError()) - when defined(linux): #Maybe NetBSD, too? - #On Linux this can be over 100 times faster than a munmap,mmap cycle. - proc mremap(old: pointer; oldSize, newSize: csize_t; flags: cint): - pointer {.importc: "mremap", header: "".} - let newAddr = mremap(f.mem, csize_t(f.size), csize_t(newFileSize), cint(1)) - if newAddr == cast[pointer](MAP_FAILED): - raiseOSError(osLastError()) - else: - if munmap(f.mem, f.size) != 0: - raiseOSError(osLastError()) - let newAddr = mmap(nil, newFileSize, PROT_READ or PROT_WRITE, - f.flags, f.handle, 0) - if newAddr == cast[pointer](MAP_FAILED): - raiseOSError(osLastError()) - f.mem = newAddr +proc resize*(f: var MemFile, newFileSize: int) {.raises: [IOError, OSError].} = + ## Resize & re-map the file underlying an `allowRemap MemFile`. If the OS/FS + ## supports it, file space is reserved to ensure room for new virtual pages. + ## Caller should wait often enough for `flush` to finish to limit use of + ## system RAM for write buffering, perhaps just prior to this call. + ## **Note**: this assumes the entire file is mapped read-write at offset 0. + ## Also, the value of `.mem` will probably change. + if newFileSize < 1: # Q: include system/bitmasks & use PageSize ? + raise newException(IOError, "Cannot resize MemFile to < 1 byte") + when defined(windows): + if not f.wasOpened: + raise newException(IOError, "Cannot resize unopened MemFile") + if f.fHandle == INVALID_HANDLE_VALUE: + raise newException(IOError, + "Cannot resize MemFile opened with allowRemap=false") + if unmapViewOfFile(f.mem) == 0 or closeHandle(f.mapHandle) == 0: # Un-do map + raiseOSError(osLastError()) + if newFileSize != f.size: # Seek to size & `setEndOfFile` => allocated. + if (let e = setFileSize(f.fHandle.FileHandle, newFileSize); + e != 0.OSErrorCode): raiseOSError(e) + f.mapHandle = createFileMappingW(f.fHandle, nil, PAGE_READWRITE, 0,0,nil) + if f.mapHandle == 0: # Re-do map + raiseOSError(osLastError()) + if (let m = mapViewOfFileEx(f.mapHandle, FILE_MAP_READ or FILE_MAP_WRITE, + 0, 0, WinSizeT(newFileSize), nil); m != nil): + f.mem = m f.size = newFileSize + else: + raiseOSError(osLastError()) + elif defined(posix): + if f.handle == -1: + raise newException(IOError, + "Cannot resize MemFile opened with allowRemap=false") + if newFileSize != f.size: + if (let e = setFileSize(f.handle.FileHandle, newFileSize); + e != 0.OSErrorCode): raiseOSError(e) + when defined(linux): #Maybe NetBSD, too? + # On Linux this can be over 100 times faster than a munmap,mmap cycle. + proc mremap(old: pointer; oldSize, newSize: csize_t; flags: cint): + pointer {.importc: "mremap", header: "".} + let newAddr = mremap(f.mem, csize_t(f.size), csize_t(newFileSize), 1.cint) + if newAddr == cast[pointer](MAP_FAILED): + raiseOSError(osLastError()) + else: + if munmap(f.mem, f.size) != 0: + raiseOSError(osLastError()) + let newAddr = mmap(nil, newFileSize, PROT_READ or PROT_WRITE, + f.flags, f.handle, 0) + if newAddr == cast[pointer](MAP_FAILED): + raiseOSError(osLastError()) + f.mem = newAddr + f.size = newFileSize proc close*(f: var MemFile) = ## closes the memory mapped file `f`. All changes are written back to the diff --git a/tests/stdlib/tmemfiles2.nim b/tests/stdlib/tmemfiles2.nim index 6fee3c1ae..c79f85ebf 100644 --- a/tests/stdlib/tmemfiles2.nim +++ b/tests/stdlib/tmemfiles2.nim @@ -15,8 +15,9 @@ var if fileExists(fn): removeFile(fn) -# Create a new file, data all zeros -mm = memfiles.open(fn, mode = fmReadWrite, newFileSize = 20) +# Create a new file, data all zeros, starting at size 10 +mm = memfiles.open(fn, mode = fmReadWrite, newFileSize = 10, allowRemap=true) +mm.resize 20 # resize up to 20 mm.close() # read, change From fc7385bfda7b3af93aa57c5cf446a90b470de348 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Thu, 16 Feb 2023 10:23:35 +0800 Subject: [PATCH 3701/3768] fixes #21360; discarding empty seqs/arrays now raises errors (#21374) * discarding empty seqs now raises errors * the same goes for sets --- compiler/semstmts.nim | 18 ++++++++++-------- tests/discard/tillegaldiscardtypes.nim | 14 ++++++++++++++ tests/typerel/ttypelessemptyset.nim | 2 +- 3 files changed, 25 insertions(+), 9 deletions(-) create mode 100644 tests/discard/tillegaldiscardtypes.nim diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index c7e48db4c..7dc976c19 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -40,6 +40,13 @@ const proc implicitlyDiscardable(n: PNode): bool +proc hasEmpty(typ: PType): bool = + if typ.kind in {tySequence, tyArray, tySet}: + result = typ.lastSon.kind == tyEmpty + elif typ.kind == tyTuple: + for s in typ.sons: + result = result or hasEmpty(s) + proc semDiscard(c: PContext, n: PNode): PNode = result = n checkSonsLen(n, 1, c.config) @@ -47,7 +54,9 @@ proc semDiscard(c: PContext, n: PNode): PNode = n[0] = semExprWithType(c, n[0]) let sonType = n[0].typ let sonKind = n[0].kind - if isEmptyType(sonType) or sonType.kind in {tyNone, tyTypeDesc} or sonKind == nkTypeOfExpr: + if isEmptyType(sonType) or hasEmpty(sonType) or + sonType.kind in {tyNone, tyTypeDesc} or + sonKind == nkTypeOfExpr: localError(c.config, n.info, errInvalidDiscard) if sonType.kind == tyProc and sonKind notin nkCallKinds: # tyProc is disallowed to prevent ``discard foo`` to be valid, when ``discard foo()`` is meant. @@ -410,13 +419,6 @@ proc semUsing(c: PContext; n: PNode): PNode = if a[^1].kind != nkEmpty: localError(c.config, a.info, "'using' sections cannot contain assignments") -proc hasEmpty(typ: PType): bool = - if typ.kind in {tySequence, tyArray, tySet}: - result = typ.lastSon.kind == tyEmpty - elif typ.kind == tyTuple: - for s in typ.sons: - result = result or hasEmpty(s) - proc hasUnresolvedParams(n: PNode; flags: TExprFlags): bool = result = tfUnresolved in n.typ.flags when false: diff --git a/tests/discard/tillegaldiscardtypes.nim b/tests/discard/tillegaldiscardtypes.nim new file mode 100644 index 000000000..b7877bcd2 --- /dev/null +++ b/tests/discard/tillegaldiscardtypes.nim @@ -0,0 +1,14 @@ +discard """ + cmd: "nim check $file" + errormsg: "statement returns no value that can be discarded" + nimout: ''' +tillegaldiscardtypes.nim(11, 3) Error: statement returns no value that can be discarded +tillegaldiscardtypes.nim(12, 3) Error: statement returns no value that can be discarded +''' +""" + +proc b(v: int) = # bug #21360 + discard @[] + discard [] + +b(0) \ No newline at end of file diff --git a/tests/typerel/ttypelessemptyset.nim b/tests/typerel/ttypelessemptyset.nim index 5f49c33fd..a4b6f003a 100644 --- a/tests/typerel/ttypelessemptyset.nim +++ b/tests/typerel/ttypelessemptyset.nim @@ -1,5 +1,5 @@ discard """ - errormsg: "internal error: invalid kind for lastOrd(tyEmpty)" + errormsg: "statement returns no value that can be discarded" """ var q = false discard (if q: {} else: {}) From 93e4f278a1b68c83944842175ab78b61fe5fb7ca Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Thu, 16 Feb 2023 13:34:32 +0800 Subject: [PATCH 3702/3768] add deprecated warnings for `{.deadcodeelim: on.}` (#21277) --- compiler/pragmas.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/pragmas.nim b/compiler/pragmas.nim index 92e3d1922..6d953dee7 100644 --- a/compiler/pragmas.nim +++ b/compiler/pragmas.nim @@ -929,7 +929,8 @@ proc singlePragma(c: PContext, sym: PSym, n: PNode, i: var int, of wThreadVar: noVal(c, it) incl(sym.flags, {sfThread, sfGlobal}) - of wDeadCodeElimUnused: discard # deprecated, dead code elim always on + of wDeadCodeElimUnused: + warningDeprecated(c.config, n.info, "'{.deadcodeelim: on.}' is deprecated, now a noop") # deprecated, dead code elim always on of wNoForward: pragmaNoForward(c, it) of wReorder: pragmaNoForward(c, it, flag = sfReorder) of wMagic: processMagic(c, it, sym) From 3b9e9fd7b29de97b394811f350876732c66cff19 Mon Sep 17 00:00:00 2001 From: Century Systems Date: Thu, 16 Feb 2023 21:47:52 +0900 Subject: [PATCH 3703/3768] Add support for NuttX RTOS. (#21372) * Add support for NuttX RTOS. Signed-off-by: Takeyoshi Kikuchi * lib: pure: asyncdispatch: assign to result. Signed-off-by: Takeyoshi Kikuchi * lib: std: typedthreads: add support for parameters to adjust Thread Stack Size. Like FreeRTOS/Zephyr, add support for following configurations. -d:nimThreadStackSize=xxxxx -d:nimThreadStackGuard=yyyy Signed-off-by: Takeyoshi Kikuchi --------- Signed-off-by: Takeyoshi Kikuchi --- compiler/options.nim | 4 +++- compiler/platform.nim | 7 ++++++- lib/posix/posix_other.nim | 5 ++++- lib/posix/posix_other_consts.nim | 13 ++++++++++++- lib/pure/asyncdispatch.nim | 5 ++++- lib/pure/asynchttpserver.nim | 5 +++-- lib/pure/asyncnet.nim | 3 ++- lib/pure/ioselects/ioselectors_select.nim | 2 +- lib/pure/nativesockets.nim | 7 ++++--- lib/pure/net.nim | 3 ++- lib/pure/selectors.nim | 9 +++++++-- lib/std/typedthreads.nim | 3 ++- lib/system.nim | 1 + lib/system/dyncalls.nim | 2 +- 14 files changed, 52 insertions(+), 17 deletions(-) diff --git a/compiler/options.nim b/compiler/options.nim index 862b42446..51a8ff6f0 100644 --- a/compiler/options.nim +++ b/compiler/options.nim @@ -621,7 +621,7 @@ proc isDefined*(conf: ConfigRef; symbol: string): bool = osQnx, osAtari, osAix, osHaiku, osVxWorks, osSolaris, osNetbsd, osFreebsd, osOpenbsd, osDragonfly, osMacosx, osIos, - osAndroid, osNintendoSwitch, osFreeRTOS, osCrossos, osZephyr} + osAndroid, osNintendoSwitch, osFreeRTOS, osCrossos, osZephyr, osNuttX} of "linux": result = conf.target.targetOS in {osLinux, osAndroid} of "bsd": @@ -643,6 +643,8 @@ proc isDefined*(conf: ConfigRef; symbol: string): bool = result = conf.target.targetOS == osFreeRTOS of "zephyr": result = conf.target.targetOS == osZephyr + of "nuttx": + result = conf.target.targetOS == osNuttX of "littleendian": result = CPU[conf.target.targetCPU].endian == littleEndian of "bigendian": result = CPU[conf.target.targetCPU].endian == bigEndian of "cpu8": result = CPU[conf.target.targetCPU].bit == 8 diff --git a/compiler/platform.nim b/compiler/platform.nim index 4b4316bc2..b6d6a119e 100644 --- a/compiler/platform.nim +++ b/compiler/platform.nim @@ -26,7 +26,8 @@ type osNone, osDos, osWindows, osOs2, osLinux, osMorphos, osSkyos, osSolaris, osIrix, osNetbsd, osFreebsd, osOpenbsd, osDragonfly, osCrossos, osAix, osPalmos, osQnx, osAmiga, osAtari, osNetware, osMacos, osMacosx, osIos, osHaiku, osAndroid, osVxWorks - osGenode, osJS, osNimVM, osStandalone, osNintendoSwitch, osFreeRTOS, osZephyr, osAny + osGenode, osJS, osNimVM, osStandalone, osNintendoSwitch, osFreeRTOS, osZephyr, + osNuttX, osAny type TInfoOSProp* = enum @@ -193,6 +194,10 @@ const objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/", scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".", props: {ospPosix}), + (name: "NuttX", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/", + objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/", + scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".", + props: {ospPosix}), (name: "Any", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/", objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/", scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".", diff --git a/lib/posix/posix_other.nim b/lib/posix/posix_other.nim index 941e13192..1b6734b51 100644 --- a/lib/posix/posix_other.nim +++ b/lib/posix/posix_other.nim @@ -10,7 +10,7 @@ when defined(nimHasStyleChecks): {.push styleChecks: off.} -when defined(freertos) or defined(zephyr): +when defined(freertos) or defined(zephyr) or defined(nuttx): const hasSpawnH = false # should exist for every Posix system nowadays hasAioH = false @@ -640,6 +640,9 @@ when defined(linux) or defined(nimdoc): ## or UDP packets. (Requires Linux kernel > 3.9) else: const SO_REUSEPORT* = cint(15) +elif defined(nuttx): + # Not supported, use SO_REUSEADDR to avoid compilation errors. + var SO_REUSEPORT* {.importc: "SO_REUSEADDR", header: "".}: cint else: var SO_REUSEPORT* {.importc, header: "".}: cint diff --git a/lib/posix/posix_other_consts.nim b/lib/posix/posix_other_consts.nim index 59ac6f9ed..4b1644f20 100644 --- a/lib/posix/posix_other_consts.nim +++ b/lib/posix/posix_other_consts.nim @@ -99,7 +99,10 @@ var EXDEV* {.importc: "EXDEV", header: "".}: cint # var F_DUPFD* {.importc: "F_DUPFD", header: "".}: cint -var F_DUPFD_CLOEXEC* {.importc: "F_DUPFD", header: "".}: cint +when defined(nuttx): + var F_DUPFD_CLOEXEC* {.importc: "F_DUPFD_CLOEXEC", header: "".}: cint +else: + var F_DUPFD_CLOEXEC* {.importc: "F_DUPFD", header: "".}: cint var F_GETFD* {.importc: "F_GETFD", header: "".}: cint var F_SETFD* {.importc: "F_SETFD", header: "".}: cint var F_GETFL* {.importc: "F_GETFL", header: "".}: cint @@ -127,6 +130,11 @@ var O_RDONLY* {.importc: "O_RDONLY", header: "".}: cint var O_RDWR* {.importc: "O_RDWR", header: "".}: cint var O_WRONLY* {.importc: "O_WRONLY", header: "".}: cint var O_CLOEXEC* {.importc: "O_CLOEXEC", header: "".}: cint +when defined(nuttx): + var O_DIRECT* {.importc: "O_DIRECT", header: "".}: cint + var O_PATH* {.importc: "O_PATH", header: "".}: cint + var O_NOATIME* {.importc: "O_NOATIME", header: "".}: cint + var O_TMPFILE* {.importc: "O_TMPFILE", header: "".}: cint var POSIX_FADV_NORMAL* {.importc: "POSIX_FADV_NORMAL", header: "".}: cint var POSIX_FADV_SEQUENTIAL* {.importc: "POSIX_FADV_SEQUENTIAL", header: "".}: cint var POSIX_FADV_RANDOM* {.importc: "POSIX_FADV_RANDOM", header: "".}: cint @@ -465,6 +473,9 @@ var FD_SETSIZE* {.importc: "FD_SETSIZE", header: "".}: cint when defined(zephyr): # Zephyr specific hardcoded value var FD_MAX* {.importc: "CONFIG_POSIX_MAX_FDS ", header: "".}: cint +elif defined(nuttx): + # NuttX specific user configuration value + var NACTIVESOCKETS* {.importc: "CONFIG_NET_NACTIVESOCKETS", header: "".}: cint # var MSG_CTRUNC* {.importc: "MSG_CTRUNC", header: "".}: cint diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 214b7d12c..64100afa7 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -2024,7 +2024,7 @@ when defined(posix): import posix when defined(linux) or defined(windows) or defined(macosx) or defined(bsd) or - defined(solaris) or defined(zephyr) or defined(freertos): + defined(solaris) or defined(zephyr) or defined(freertos) or defined(nuttx): proc maxDescriptors*(): int {.raises: OSError.} = ## Returns the maximum number of active file descriptors for the current ## process. This involves a system call. For now `maxDescriptors` is @@ -2033,6 +2033,9 @@ when defined(linux) or defined(windows) or defined(macosx) or defined(bsd) or result = 16_700_000 elif defined(zephyr) or defined(freertos): result = FD_MAX + elif defined(nuttx): + # The maximum number of concurrently active UDP and TCP ports. + result = NACTIVESOCKETS else: var fdLim: RLimit if getrlimit(RLIMIT_NOFILE, fdLim) < 0: diff --git a/lib/pure/asynchttpserver.nim b/lib/pure/asynchttpserver.nim index 845875a1e..c09fe35ca 100644 --- a/lib/pure/asynchttpserver.nim +++ b/lib/pure/asynchttpserver.nim @@ -387,8 +387,9 @@ proc listen*(server: AsyncHttpServer; port: Port; address = ""; domain = AF_INET server.socket = newAsyncSocket(domain) if server.reuseAddr: server.socket.setSockOpt(OptReuseAddr, true) - if server.reusePort: - server.socket.setSockOpt(OptReusePort, true) + when not defined(nuttx): + if server.reusePort: + server.socket.setSockOpt(OptReusePort, true) server.socket.bindAddr(port, address) server.socket.listen() diff --git a/lib/pure/asyncnet.nim b/lib/pure/asyncnet.nim index 982459af7..d35dbdfaf 100644 --- a/lib/pure/asyncnet.nim +++ b/lib/pure/asyncnet.nim @@ -106,7 +106,8 @@ export SOBool # TODO: Remove duplication introduced by PR #4683. const defineSsl = defined(ssl) or defined(nimdoc) -const useNimNetLite = defined(nimNetLite) or defined(freertos) or defined(zephyr) +const useNimNetLite = defined(nimNetLite) or defined(freertos) or defined(zephyr) or + defined(nuttx) when defineSsl: import openssl diff --git a/lib/pure/ioselects/ioselectors_select.nim b/lib/pure/ioselects/ioselectors_select.nim index 34c88d85e..a6f54e791 100644 --- a/lib/pure/ioselects/ioselectors_select.nim +++ b/lib/pure/ioselects/ioselectors_select.nim @@ -313,7 +313,7 @@ proc selectInto*[T](s: Selector[T], timeout: int, verifySelectParams(timeout) if timeout != -1: - when defined(genode) or defined(freertos) or defined(zephyr): + when defined(genode) or defined(freertos) or defined(zephyr) or defined(nuttx): tv.tv_sec = Time(timeout div 1_000) else: tv.tv_sec = timeout.int32 div 1_000 diff --git a/lib/pure/nativesockets.nim b/lib/pure/nativesockets.nim index 14a318412..b3465ef14 100644 --- a/lib/pure/nativesockets.nim +++ b/lib/pure/nativesockets.nim @@ -23,7 +23,8 @@ when hostOS == "solaris": {.passl: "-lsocket -lnsl".} const useWinVersion = defined(windows) or defined(nimdoc) -const useNimNetLite = defined(nimNetLite) or defined(freertos) or defined(zephyr) +const useNimNetLite = defined(nimNetLite) or defined(freertos) or defined(zephyr) or + defined(nuttx) when useWinVersion: import winlean @@ -304,7 +305,7 @@ proc getAddrInfo*(address: string, port: Port, domain: Domain = AF_INET, let socketPort = if sockType == SOCK_RAW: "" else: $port var gaiResult = getaddrinfo(address, socketPort.cstring, addr(hints), result) if gaiResult != 0'i32: - when useWinVersion or defined(freertos): + when useWinVersion or defined(freertos) or defined(nuttx): raiseOSError(osLastError()) else: raiseOSError(osLastError(), $gai_strerror(gaiResult)) @@ -351,7 +352,7 @@ proc getSockDomain*(socket: SocketHandle): Domain = else: raise newException(IOError, "Unknown socket family in getSockDomain") -when not useNimNetLite: +when not useNimNetLite: proc getServByName*(name, proto: string): Servent {.tags: [ReadIOEffect].} = ## Searches the database from the beginning and finds the first entry for ## which the service name specified by `name` matches the s_name member diff --git a/lib/pure/net.nim b/lib/pure/net.nim index 1dfef3b30..d3a767eed 100644 --- a/lib/pure/net.nim +++ b/lib/pure/net.nim @@ -100,7 +100,8 @@ export nativesockets.Port, nativesockets.`$`, nativesockets.`==` export Domain, SockType, Protocol const useWinVersion = defined(windows) or defined(nimdoc) -const useNimNetLite = defined(nimNetLite) or defined(freertos) or defined(zephyr) +const useNimNetLite = defined(nimNetLite) or defined(freertos) or defined(zephyr) or + defined(nuttx) const defineSsl = defined(ssl) or defined(nimdoc) when useWinVersion: diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim index be5037fe2..ef8736cdf 100644 --- a/lib/pure/selectors.nim +++ b/lib/pure/selectors.nim @@ -37,7 +37,7 @@ const hasThreadSupport = compileOption("threads") and defined(threadsafe) const ioselSupportedPlatform* = defined(macosx) or defined(freebsd) or defined(netbsd) or defined(openbsd) or - defined(dragonfly) or + defined(dragonfly) or defined(nuttx) or (defined(linux) and not defined(android) and not defined(emscripten)) ## This constant is used to determine whether the destination platform is ## fully supported by `ioselectors` module. @@ -328,7 +328,7 @@ else: doAssert(timeout >= -1, "Cannot select with a negative value, got: " & $timeout) when defined(linux) or defined(windows) or defined(macosx) or defined(bsd) or - defined(solaris) or defined(zephyr) or defined(freertos): + defined(solaris) or defined(zephyr) or defined(freertos) or defined(nuttx): template maxDescriptors*(): int = ## Returns the maximum number of active file descriptors for the current ## process. This involves a system call. For now `maxDescriptors` is @@ -337,6 +337,9 @@ else: 16_700_000 elif defined(zephyr) or defined(freertos): FD_MAX + elif defined(nuttx): + # The maximum number of concurrently active UDP and TCP ports. + NACTIVESOCKETS else: var fdLim: RLimit var res = int(getrlimit(RLIMIT_NOFILE, fdLim)) @@ -360,5 +363,7 @@ else: include ioselects/ioselectors_select elif defined(zephyr): include ioselects/ioselectors_poll + elif defined(nuttx): + include ioselects/ioselectors_epoll else: include ioselects/ioselectors_poll diff --git a/lib/std/typedthreads.nim b/lib/std/typedthreads.nim index 970d381c3..b45ce8a61 100644 --- a/lib/std/typedthreads.nim +++ b/lib/std/typedthreads.nim @@ -46,7 +46,8 @@ when defined(genode): import genode/env -when hasAllocStack or defined(zephyr) or defined(freertos) or defined(cpu16) or defined(cpu8): +when hasAllocStack or defined(zephyr) or defined(freertos) or defined(nuttx) or + defined(cpu16) or defined(cpu8): const nimThreadStackSize {.intdefine.} = 8192 nimThreadStackGuard {.intdefine.} = 128 diff --git a/lib/system.nim b/lib/system.nim index d8fafc0ef..af7e0e4c8 100644 --- a/lib/system.nim +++ b/lib/system.nim @@ -2688,6 +2688,7 @@ when notJSnotNims: not defined(nintendoswitch) and not defined(freertos) and not defined(zephyr) and + not defined(nuttx) and hostOS != "any" proc raiseEIO(msg: string) {.noinline, noreturn.} = diff --git a/lib/system/dyncalls.nim b/lib/system/dyncalls.nim index de22f7cbb..2162b234f 100644 --- a/lib/system/dyncalls.nim +++ b/lib/system/dyncalls.nim @@ -176,7 +176,7 @@ elif defined(genode): proc nimGetProcAddr(lib: LibHandle, name: cstring): ProcAddr = raiseAssert("nimGetProcAddr not implemented") -elif defined(nintendoswitch) or defined(freertos) or defined(zephyr): +elif defined(nintendoswitch) or defined(freertos) or defined(zephyr) or defined(nuttx): proc nimUnloadLibrary(lib: LibHandle) = cstderr.rawWrite("nimUnLoadLibrary not implemented") cstderr.rawWrite("\n") From 03dd853815173bcf63f80ef03b5a4db7177453ec Mon Sep 17 00:00:00 2001 From: "Qinsi (James) ZHU" Date: Fri, 17 Feb 2023 01:12:48 +0800 Subject: [PATCH 3704/3768] add .replace() with callback to jsre (#21371) --- lib/js/jsre.nim | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/js/jsre.nim b/lib/js/jsre.nim index 19888aaa9..69bd75c3b 100644 --- a/lib/js/jsre.nim +++ b/lib/js/jsre.nim @@ -34,6 +34,9 @@ func compile*(self: RegExp; pattern: cstring; flags: cstring) {.importjs: "#.com func replace*(pattern: cstring; self: RegExp; replacement: cstring): cstring {.importjs: "#.replace(#, #)".} ## Returns a new string with some or all matches of a pattern replaced by given replacement +func replace*(pattern: cstring, self: RegExp, cb: proc (args: varargs[cstring]): cstring): cstring {.importcpp.} + ## Returns a new string with some or all matches of a pattern replaced by given callback function + func split*(pattern: cstring; self: RegExp): seq[cstring] {.importjs: "(#.split(#) || [])".} ## Divides a string into an ordered list of substrings and returns the array @@ -88,5 +91,7 @@ runnableExamples: assert "do1ne".split(jsregex) == @["do".cstring, "ne".cstring] jsregex.compile(r"[lw]", r"i") assert "hello world".replace(jsregex,"X") == "heXlo world" + jsregex.compile(r"([a-z])\1*", r"g") + assert "abbcccdddd".replace(jsregex, proc (m: varargs[cstring]): cstring = ($m[0] & $(m.len)).cstring) == "a1b2c3d4" let digitsRegex: RegExp = newRegExp(r"\d") assert "foo".match(digitsRegex) == @[] From 13711b101d5ec101b3a2e18832d80e2b71eb3c80 Mon Sep 17 00:00:00 2001 From: Jake Leahy Date: Fri, 17 Feb 2023 21:24:25 +1100 Subject: [PATCH 3705/3768] `std/xmltree` Add the type of the node when the assertion fails (#21383) Print the type of the node when the assertion fails This way the user actually knows what the type was instead of just knowing it failed --- lib/pure/xmltree.nim | 52 +++++++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/lib/pure/xmltree.nim b/lib/pure/xmltree.nim index c3923ca50..82513bc98 100644 --- a/lib/pure/xmltree.nim +++ b/lib/pure/xmltree.nim @@ -70,6 +70,14 @@ const xmlHeader* = "\n" ## Header to use for complete XML output. +template expect(node: XmlNode, kind: set[XmlNodeKind]) = + ## Check the node's kind is within a set of values + assert node.k in kind, "Got " & $node.k + +template expect(node: XmlNode, kind: XmlNodeKind) = + ## Check the node's kind equals a value + assert node.k == kind, "Got " & $node.k + proc newXmlNode(kind: XmlNodeKind): XmlNode = ## Creates a new ``XmlNode``. result = XmlNode(k: kind) @@ -182,7 +190,7 @@ proc text*(n: XmlNode): lent string {.inline.} = assert $c == "" assert c.text == "my comment" - assert n.k in {xnText, xnComment, xnCData, xnEntity} + n.expect {xnText, xnComment, xnCData, xnEntity} result = n.fText proc `text=`*(n: XmlNode, text: sink string) {.inline.} = @@ -200,7 +208,7 @@ proc `text=`*(n: XmlNode, text: sink string) {.inline.} = e.text = "a new entity text" assert $e == "&a new entity text;" - assert n.k in {xnText, xnComment, xnCData, xnEntity} + n.expect {xnText, xnComment, xnCData, xnEntity} n.fText = text proc tag*(n: XmlNode): lent string {.inline.} = @@ -221,7 +229,7 @@ proc tag*(n: XmlNode): lent string {.inline.} = """ assert a.tag == "firstTag" - assert n.k == xnElement + n.expect xnElement result = n.fTag proc `tag=`*(n: XmlNode, tag: sink string) {.inline.} = @@ -244,7 +252,7 @@ proc `tag=`*(n: XmlNode, tag: sink string) {.inline.} = """ - assert n.k == xnElement + n.expect xnElement n.fTag = tag proc rawText*(n: XmlNode): string {.inline.} = @@ -315,7 +323,7 @@ proc add*(father, son: XmlNode) {.inline.} = f.add newEntity("my entity") assert $f == "my text&my entity;" - assert father.k == xnElement + father.expect xnElement add(father.s, son) proc add*(father: XmlNode, sons: openArray[XmlNode]) {.inline.} = @@ -335,7 +343,7 @@ proc add*(father: XmlNode, sons: openArray[XmlNode]) {.inline.} = f.add(@[newText("my text"), newElement("sonTag"), newEntity("my entity")]) assert $f == "my text&my entity;" - assert father.k == xnElement + father.expect xnElement add(father.s, sons) @@ -361,7 +369,7 @@ proc insert*(father, son: XmlNode, index: int) {.inline.} = """ - assert father.k == xnElement + father.expect xnElement if len(father.s) > index: insert(father.s, son, index) else: @@ -390,7 +398,7 @@ proc insert*(father: XmlNode, sons: openArray[XmlNode], index: int) {.inline.} = """ - assert father.k == xnElement + father.expect xnElement if len(father.s) > index: insert(father.s, sons, index) else: @@ -416,7 +424,7 @@ proc delete*(n: XmlNode, i: Natural) = """ - assert n.k == xnElement + n.expect xnElement n.s.delete(i) proc delete*(n: XmlNode, slice: Slice[int]) = @@ -439,7 +447,7 @@ proc delete*(n: XmlNode, slice: Slice[int]) = """ - assert n.k == xnElement + n.expect xnElement n.s.delete(slice) proc replace*(n: XmlNode, i: Natural, replacement: openArray[XmlNode]) = @@ -466,7 +474,7 @@ proc replace*(n: XmlNode, i: Natural, replacement: openArray[XmlNode]) = """ - assert n.k == xnElement + n.expect xnElement n.s.delete(i) n.s.insert(replacement, i) @@ -494,7 +502,7 @@ proc replace*(n: XmlNode, slice: Slice[int], replacement: openArray[XmlNode]) = """ - assert n.k == xnElement + n.expect xnElement n.s.delete(slice) n.s.insert(replacement, slice.a) @@ -525,12 +533,12 @@ proc `[]`*(n: XmlNode, i: int): XmlNode {.inline.} = assert $f[1] == "" assert $f[0] == "" - assert n.k == xnElement + n.expect xnElement result = n.s[i] proc `[]`*(n: var XmlNode, i: int): var XmlNode {.inline.} = ## Returns the `i`'th child of `n` so that it can be modified. - assert n.k == xnElement + n.expect xnElement result = n.s[i] proc clear*(n: var XmlNode) = @@ -582,12 +590,12 @@ iterator items*(n: XmlNode): XmlNode {.inline.} = # # &some entity; - assert n.k == xnElement + n.expect xnElement for i in 0 .. n.len-1: yield n[i] iterator mitems*(n: var XmlNode): var XmlNode {.inline.} = ## Iterates over all direct children of `n` so that they can be modified. - assert n.k == xnElement + n.expect xnElement for i in 0 .. n.len-1: yield n[i] proc toXmlAttributes*(keyValuePairs: varargs[tuple[key, @@ -619,7 +627,7 @@ proc attrs*(n: XmlNode): XmlAttributes {.inline.} = j.attrs = att assert j.attrs == att - assert n.k == xnElement + n.expect xnElement result = n.fAttr proc `attrs=`*(n: XmlNode, attr: XmlAttributes) {.inline.} = @@ -636,7 +644,7 @@ proc `attrs=`*(n: XmlNode, attr: XmlAttributes) {.inline.} = j.attrs = att assert j.attrs == att - assert n.k == xnElement + n.expect xnElement n.fAttr = attr proc attrsLen*(n: XmlNode): int {.inline.} = @@ -653,7 +661,7 @@ proc attrsLen*(n: XmlNode): int {.inline.} = j.attrs = att assert j.attrsLen == 2 - assert n.k == xnElement + n.expect xnElement if not isNil(n.fAttr): result = len(n.fAttr) proc attr*(n: XmlNode, name: string): string = @@ -671,7 +679,7 @@ proc attr*(n: XmlNode, name: string): string = assert j.attr("key1") == "first value" assert j.attr("key2") == "second value" - assert n.kind == xnElement + n.expect xnElement if n.attrs == nil: return "" return n.attrs.getOrDefault(name) @@ -834,7 +842,7 @@ proc child*(n: XmlNode, name: string): XmlNode = f.add newElement("thirdSon") assert $(f.child("secondSon")) == "" - assert n.kind == xnElement + n.expect xnElement for i in items(n): if i.kind == xnElement: if i.tag == name: @@ -870,7 +878,7 @@ proc findAll*(n: XmlNode, tag: string, result: var seq[XmlNode], a.findAll("BAD", s, caseInsensitive = true) assert $s == "@[c text, d text]" - assert n.k == xnElement + n.expect xnElement for child in n.items(): if child.k != xnElement: continue From 6b93db7070c984121e18b502d938d5e455d4ba70 Mon Sep 17 00:00:00 2001 From: Matt Haggard Date: Fri, 17 Feb 2023 05:25:15 -0500 Subject: [PATCH 3706/3768] Fix long link commands on macOS (v2) (#21382) Handle long link commands on macOS with a script, since ar does not support response files --- compiler/extccomp.nim | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index 6515f6968..db97de65c 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -834,6 +834,15 @@ proc linkViaResponseFile(conf: ConfigRef; cmd: string) = finally: removeFile(linkerArgs) +proc linkViaShellScript(conf: ConfigRef; cmd: string) = + let linkerScript = conf.projectName & "_" & "linkerScript.sh" + writeFile(linkerScript, cmd) + let shell = getEnv("SHELL") + try: + execLinkCmd(conf, shell & " " & linkerScript) + finally: + removeFile(linkerScript) + proc getObjFilePath(conf: ConfigRef, f: Cfile): string = if noAbsolutePaths(conf): f.obj.extractFilename else: f.obj.string @@ -856,9 +865,12 @@ proc preventLinkCmdMaxCmdLen(conf: ConfigRef, linkCmd: string) = # Windows's command line limit is about 8K (8191 characters) so C compilers on # Windows support a feature where the command line can be passed via ``@linkcmd`` # to them. - const MaxCmdLen = when defined(windows): 8_000 else: 32_000 + const MaxCmdLen = when defined(windows): 8_000 elif defined(macosx): 260_000 else: 32_000 if linkCmd.len > MaxCmdLen: - linkViaResponseFile(conf, linkCmd) + when defined(macosx): + linkViaShellScript(conf, linkCmd) + else: + linkViaResponseFile(conf, linkCmd) else: execLinkCmd(conf, linkCmd) From e896977bd16b2471e4aaaa363690622420e99acc Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Sun, 19 Feb 2023 14:49:19 +0800 Subject: [PATCH 3707/3768] closes #1072; add a test case (#21396) --- tests/specialops/tcallops.nim | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/specialops/tcallops.nim b/tests/specialops/tcallops.nim index 0508a37a1..c541a0c1d 100644 --- a/tests/specialops/tcallops.nim +++ b/tests/specialops/tcallops.nim @@ -37,3 +37,13 @@ doAssert a(b) == "(12)" doAssert a.b(c) == `()`(b, a, c) doAssert (a.b)(c) == `()`(a.b, c) doAssert `()`(a.b, c) == `()`(`()`(b, a), c) + +block: # bug #1072 + var x: int + + proc foo(some:int):int = some + proc `()`(l,r:string): string = discard + + block: + var foo = 42 + doAssert x.foo == 0 From c66dc913cefb08722c4bf9885948e26be1285c3f Mon Sep 17 00:00:00 2001 From: Jake Leahy Date: Tue, 21 Feb 2023 07:19:46 +1100 Subject: [PATCH 3708/3768] Support new runtime with nim-gdb (#21400) * Add support for orc strings * Cleaned up testing script Enums now printing properly Merged both old and new strings into the one printer Moving onto sets which seem kinda difficult * Sets working Instead of trying to imitate how Nim represents enums, I just call the dollar proc for each enum value While this runs into problems if the user doesn't call the dollar proc anywhere, I believe its a decent tradeoff I've cleaned up the error message for when it cannot find dollar proc (Might add in proper message on how to fix) * Support sequences V2 runtime seems to have sequences that don't have a len (Guessing its some kind of short seq optimisation?) but I've rolled the implementation into normal sequences since the implementation is practically the same * Clean up test program so it isn't using diff Also don't redirect the first nim compile to /dev/null so that we can check for any compilation errors I found the diff to be annoying to read (Seeing as the test script already performs diffing) * Tables are now supported * Add colours to test output It was getting difficult to tell apart test output from GDB output so I added colour to better tell them apart * Both old and new runtime are working Set exit code in python test script so that this could possibly be added to the CI. Only issue is that it can be flaky (GDB crashes randomly for some reason) * Remove old code that was commented out If I need it later I'll just use git * Remove branch that never runs * Remove the old test output [skip ci] --- .../untestable/gdb/gdb_pretty_printer_test.py | 30 +- .../gdb/gdb_pretty_printer_test_output.txt | 3 - .../gdb/gdb_pretty_printer_test_program.nim | 28 +- .../gdb/gdb_pretty_printer_test_run.sh | 20 +- tools/nim-gdb.py | 386 ++++++------------ 5 files changed, 160 insertions(+), 307 deletions(-) delete mode 100644 tests/untestable/gdb/gdb_pretty_printer_test_output.txt mode change 100644 => 100755 tests/untestable/gdb/gdb_pretty_printer_test_run.sh diff --git a/tests/untestable/gdb/gdb_pretty_printer_test.py b/tests/untestable/gdb/gdb_pretty_printer_test.py index 8f0f88e85..d28d01a60 100644 --- a/tests/untestable/gdb/gdb_pretty_printer_test.py +++ b/tests/untestable/gdb/gdb_pretty_printer_test.py @@ -1,10 +1,13 @@ import gdb +import re +import sys # this test should test the gdb pretty printers of the nim # library. But be aware this test is not complete. It only tests the # command line version of gdb. It does not test anything for the # machine interface of gdb. This means if if this test passes gdb # frontends might still be broken. +gdb.execute("set python print-stack full") gdb.execute("source ../../../tools/nim-gdb.py") # debug all instances of the generic function `myDebug`, should be 14 gdb.execute("rbreak myDebug") @@ -16,7 +19,7 @@ outputs = [ '"meTwo"', '{meOne, meThree}', 'MyOtherEnum(1)', - '5', + '{MyOtherEnum(0), MyOtherEnum(2)}', 'array = {1, 2, 3, 4, 5}', 'seq(0, 0)', 'seq(0, 10)', @@ -28,13 +31,17 @@ outputs = [ '{a = 1, b = "some string"}' ] +argRegex = re.compile("^.* = (?:No suitable Nim \$ operator found for type: \w+\s*)*(.*)$") +# Remove this error message which can pop up +noSuitableRegex = re.compile("(No suitable Nim \$ operator found for type: \w+\s*)") + for i, expected in enumerate(outputs): - gdb.write(f"{i+1}) expecting: {expected}: ", gdb.STDLOG) + gdb.write(f"\x1b[38;5;105m{i+1}) expecting: {expected}: \x1b[0m", gdb.STDLOG) gdb.flush() - - functionSymbol = gdb.selected_frame().block().function - assert functionSymbol.line == 41, str(functionSymbol.line) - + currFrame = gdb.selected_frame() + functionSymbol = currFrame.block().function + assert functionSymbol.line == 24, str(functionSymbol.line) + raw = "" if i == 6: # myArray is passed as pointer to int to myDebug. I look up myArray up in the stack gdb.execute("up") @@ -44,10 +51,13 @@ for i, expected in enumerate(outputs): gdb.execute("up") raw = gdb.parse_and_eval("myOtherArray") else: - raw = gdb.parse_and_eval("arg") - + rawArg = re.sub(noSuitableRegex, "", gdb.execute("info args", to_string = True)) + raw = rawArg.split("=", 1)[-1].strip() output = str(raw) - assert output == expected, "{0} : output: ({1}) != expected: ({2})".format(i, output, expected) - gdb.write(f"passed\n", gdb.STDLOG) + if output != expected: + gdb.write(f"\x1b[38;5;196m ({output}) != expected: ({expected})\x1b[0m\n", gdb.STDERR) + gdb.execute("quit 1") + else: + gdb.write("\x1b[38;5;34mpassed\x1b[0m\n", gdb.STDLOG) gdb.execute("continue") diff --git a/tests/untestable/gdb/gdb_pretty_printer_test_output.txt b/tests/untestable/gdb/gdb_pretty_printer_test_output.txt deleted file mode 100644 index 73d26016f..000000000 --- a/tests/untestable/gdb/gdb_pretty_printer_test_output.txt +++ /dev/null @@ -1,3 +0,0 @@ -Loading Nim Runtime support. -NimEnumPrinter: lookup global symbol 'NTI__z9cu80OJCfNgw9bUdzn5ZEzw_ failed for tyEnum_MyOtherEnum__z9cu80OJCfNgw9bUdzn5ZEzw. -8 diff --git a/tests/untestable/gdb/gdb_pretty_printer_test_program.nim b/tests/untestable/gdb/gdb_pretty_printer_test_program.nim index d2acdd282..b54fc1a7f 100644 --- a/tests/untestable/gdb/gdb_pretty_printer_test_program.nim +++ b/tests/untestable/gdb/gdb_pretty_printer_test_program.nim @@ -18,23 +18,6 @@ type MyObj = object a*: int b*: string - - # MyVariant = ref object - # id*: int - # case kind*: MyEnum - # of meOne: mInt*: int - # of meTwo, meThree: discard - # of meFour: - # moInt*: int - # babies*: seq[MyVariant] - # after: float - - # MyIntVariant = ref object - # stuff*: int - # case myKind*: range[0..32766] - # of 0: mFloat*: float - # of 2: mString*: string - # else: mBabies*: seq[MyIntVariant] var counter = 0 @@ -97,16 +80,7 @@ proc testProc(): void = var obj = MyObj(a: 1, b: "some string") myDebug(obj) #15 - # var varObj = MyVariant(id: 13, kind: meFour, moInt: 94, - # babies: @[MyVariant(id: 18, kind: meOne, mInt: 7, after: 1.0), - # MyVariant(id: 21, kind: meThree, after: 2.0)], - # after: 3.0) - # myDebug(varObj) #16 - - # var varObjInt = MyIntVariant(stuff: 5, myKind: 2, mString: "this is my sweet string") - # myDebug(varObjInt) #17 - - echo(counter) + assert counter == 15 testProc() diff --git a/tests/untestable/gdb/gdb_pretty_printer_test_run.sh b/tests/untestable/gdb/gdb_pretty_printer_test_run.sh old mode 100644 new mode 100755 index 525f54705..411c68435 --- a/tests/untestable/gdb/gdb_pretty_printer_test_run.sh +++ b/tests/untestable/gdb/gdb_pretty_printer_test_run.sh @@ -1,15 +1,13 @@ #!/usr/bin/env bash -# Exit if anything fails set -e -#!/usr/bin/env bash # Compile the test project with fresh debug information. -nim c --debugger:native gdb_pretty_printer_test_program.nim &> /dev/null +nim c --debugger:native --mm:orc --out:gdbNew gdb_pretty_printer_test_program.nim +echo "Running new runtime tests..." # 2>&1 redirects stderr to stdout (all output in stdout) -# <(...) is a bash feature that makes the output of a command into a -# file handle. -# diff compares the two files, the expected output, and the file -# handle that is created by the execution of gdb. -diff ./gdb_pretty_printer_test_output.txt <(gdb -x gdb_pretty_printer_test.py --batch-silent --args gdb_pretty_printer_test_program 2>&1) -# The exit code of diff is forwarded as the exit code of this -# script. So when the comparison fails, the exit code of this script -# won't be 0. So this script should be embeddable in a test suite. +gdb -x gdb_pretty_printer_test.py --batch-silent --args gdbNew 2>&1 + + +# Do it all again, but with old runtime +nim c --debugger:native --mm:refc --out:gdbOld gdb_pretty_printer_test_program.nim &> /dev/null +echo "Running old runtime tests" +gdb -x gdb_pretty_printer_test.py --batch-silent --args gdbOld 2>&1 diff --git a/tools/nim-gdb.py b/tools/nim-gdb.py index f35b9a033..e3af0dde6 100644 --- a/tools/nim-gdb.py +++ b/tools/nim-gdb.py @@ -16,6 +16,10 @@ def printErrorOnce(id, message): errorSet.add(id) gdb.write("printErrorOnce: " + message, gdb.STDERR) +def debugPrint(x): + gdb.write(str(x) + "\n", gdb.STDERR) + +NIM_STRING_TYPES = ["NimStringDesc", "NimStringV2"] ################################################################################ ##### Type pretty printers @@ -23,23 +27,28 @@ def printErrorOnce(id, message): type_hash_regex = re.compile("^([A-Za-z0-9]*)_([A-Za-z0-9]*)_+([A-Za-z0-9]*)$") +def getNimName(typ): + if m := type_hash_regex.match(typ): + return m.group(2) + return f"unknown <{typ}>" + def getNimRti(type_name): """ Return a ``gdb.Value`` object for the Nim Runtime Information of ``type_name``. """ # Get static const TNimType variable. This should be available for # every non trivial Nim type. m = type_hash_regex.match(type_name) - lookups = [ - "NTI" + m.group(2).lower() + "__" + m.group(3) + "_", - "NTI" + "__" + m.group(3) + "_", - "NTI" + m.group(2).replace("colon", "58").lower() + "__" + m.group(3) + "_" - ] if m: - for l in lookups: - try: - return gdb.parse_and_eval(l) - except: - pass + lookups = [ + "NTI" + m.group(2).lower() + "__" + m.group(3) + "_", + "NTI" + "__" + m.group(3) + "_", + "NTI" + m.group(2).replace("colon", "58").lower() + "__" + m.group(3) + "_" + ] + for l in lookups: + try: + return gdb.parse_and_eval(l) + except: + pass None def getNameFromNimRti(rti): @@ -68,7 +77,7 @@ class NimTypeRecognizer: 'NIM_BOOL': 'bool', - 'NIM_CHAR': 'char', 'NCSTRING': 'cstring', 'NimStringDesc': 'string' + 'NIM_CHAR': 'char', 'NCSTRING': 'cstring', 'NimStringDesc': 'string', 'NimStringV2': 'string' } # object_type_pattern = re.compile("^(\w*):ObjectType$") @@ -136,7 +145,7 @@ class DollarPrintFunction (gdb.Function): "Nim's equivalent of $ operator as a gdb function, available in expressions `print $dollar(myvalue)" dollar_functions = re.findall( - 'NimStringDesc \*(dollar__[A-z0-9_]+?)\(([^,)]*)\);', + '(?:NimStringDesc \*|NimStringV2)\s?(dollar__[A-z0-9_]+?)\(([^,)]*)\);', gdb.execute("info functions dollar__", True, True) ) @@ -146,12 +155,9 @@ class DollarPrintFunction (gdb.Function): @staticmethod def invoke_static(arg): - - if arg.type.code == gdb.TYPE_CODE_PTR and arg.type.target().name == "NimStringDesc": + if arg.type.code == gdb.TYPE_CODE_PTR and arg.type.target().name in NIM_STRING_TYPES: return arg - argTypeName = str(arg.type) - for func, arg_typ in DollarPrintFunction.dollar_functions: # this way of overload resolution cannot deal with type aliases, # therefore it won't find all overloads. @@ -163,7 +169,8 @@ class DollarPrintFunction (gdb.Function): func_value = gdb.lookup_global_symbol(func, gdb.SYMBOL_FUNCTIONS_DOMAIN).value() return func_value(arg.address) - printErrorOnce(argTypeName, "No suitable Nim $ operator found for type: " + argTypeName + "\n") + + debugPrint(f"No suitable Nim $ operator found for type: {getNimName(argTypeName)}\n") return None def invoke(self, arg): @@ -184,11 +191,11 @@ class NimStringEqFunction (gdb.Function): @staticmethod def invoke_static(arg1,arg2): - if arg1.type.code == gdb.TYPE_CODE_PTR and arg1.type.target().name == "NimStringDesc": + if arg1.type.code == gdb.TYPE_CODE_PTR and arg1.type.target().name in NIM_STRING_TYPES: str1 = NimStringPrinter(arg1).to_string() else: str1 = arg1.string() - if arg2.type.code == gdb.TYPE_CODE_PTR and arg2.type.target().name == "NimStringDesc": + if arg2.type.code == gdb.TYPE_CODE_PTR and arg2.type.target().name in NIM_STRING_TYPES: str2 = NimStringPrinter(arg1).to_string() else: str2 = arg2.string() @@ -216,7 +223,7 @@ class DollarPrintCmd (gdb.Command): strValue = DollarPrintFunction.invoke_static(param) if strValue: gdb.write( - NimStringPrinter(strValue).to_string() + "\n", + str(NimStringPrinter(strValue)) + "\n", gdb.STDOUT ) @@ -254,7 +261,6 @@ class KochCmd (gdb.Command): os.path.dirname(os.path.dirname(__file__)), "koch") def invoke(self, argument, from_tty): - import os subprocess.run([self.binary] + gdb.string_to_argv(argument)) KochCmd() @@ -308,8 +314,14 @@ class NimBoolPrinter: ################################################################################ +def strFromLazy(strVal): + if isinstance(strVal, str): + return strVal + else: + return strVal.value().string("utf-8") + class NimStringPrinter: - pattern = re.compile(r'^NimStringDesc \*$') + pattern = re.compile(r'^(NimStringDesc \*|NimStringV2)$') def __init__(self, val): self.val = val @@ -319,11 +331,19 @@ class NimStringPrinter: def to_string(self): if self.val: - l = int(self.val['Sup']['len']) - return self.val['data'].lazy_string(encoding="utf-8", length=l) + if self.val.type.name == "NimStringV2": + l = int(self.val["len"]) + data = self.val["p"]["data"] + else: + l = int(self.val['Sup']['len']) + data = self.val["data"] + return data.lazy_string(encoding="utf-8", length=l) else: return "" + def __str__(self): + return strFromLazy(self.to_string()) + class NimRopePrinter: pattern = re.compile(r'^tyObject_RopeObj__([A-Za-z0-9]*) \*$') @@ -345,39 +365,11 @@ class NimRopePrinter: ################################################################################ -# proc reprEnum(e: int, typ: PNimType): string {.compilerRtl.} = -# ## Return string representation for enumeration values -# var n = typ.node -# if ntfEnumHole notin typ.flags: -# let o = e - n.sons[0].offset -# if o >= 0 and o <% typ.node.len: -# return $n.sons[o].name -# else: -# # ugh we need a slow linear search: -# var s = n.sons -# for i in 0 .. n.len-1: -# if s[i].offset == e: -# return $s[i].name -# result = $e & " (invalid data!)" - def reprEnum(e, typ): - """ this is a port of the nim runtime function `reprEnum` to python """ + # Casts the value to the enum type and then calls the enum printer e = int(e) - n = typ["node"] - flags = int(typ["flags"]) - # 1 << 6 is {ntfEnumHole} - if ((1 << 6) & flags) == 0: - o = e - int(n["sons"][0]["offset"]) - if o >= 0 and 0 < int(n["len"]): - return n["sons"][o]["name"].string("utf-8", "ignore") - else: - # ugh we need a slow linear search: - s = n["sons"] - for i in range(0, int(n["len"])): - if int(s[i]["offset"]) == e: - return s[i]["name"].string("utf-8", "ignore") - - return str(e) + " (invalid data!)" + val = gdb.Value(e).cast(typ) + return strFromLazy(NimEnumPrinter(val).to_string()) def enumNti(typeNimName, idString): typeInfoName = "NTI" + typeNimName.lower() + "__" + idString + "_" @@ -389,6 +381,7 @@ def enumNti(typeNimName, idString): class NimEnumPrinter: pattern = re.compile(r'^tyEnum_([A-Za-z0-9]+)__([A-Za-z0-9]*)$') + enumReprProc = gdb.lookup_global_symbol("reprEnum", gdb.SYMBOL_FUNCTIONS_DOMAIN) def __init__(self, val): self.val = val @@ -397,14 +390,18 @@ class NimEnumPrinter: self.typeNimName = match.group(1) typeInfoName, self.nti = enumNti(self.typeNimName, match.group(2)) - if self.nti is None: - printErrorOnce(typeInfoName, f"NimEnumPrinter: lookup global symbol: '{typeInfoName}' failed for {typeName}.\n") - def to_string(self): - if self.nti: - arg0 = self.val - arg1 = self.nti.value(gdb.newest_frame()) - return reprEnum(arg0, arg1) + if NimEnumPrinter.enumReprProc and self.nti: + # Use the old runtimes enumRepr function. + # We call the Nim proc itself so that the implementation is correct + f = gdb.newest_frame() + # We need to strip the quotes so it looks like an enum instead of a string + reprProc = NimEnumPrinter.enumReprProc.value() + return str(reprProc(self.val, self.nti.value(f).address)).strip('"') + elif dollarResult := DollarPrintFunction.invoke_static(self.val): + # New runtime doesn't use enumRepr so we instead try and call the + # dollar function for it + return str(NimStringPrinter(dollarResult)) else: return self.typeNimName + "(" + str(int(self.val)) + ")" @@ -421,26 +418,20 @@ class NimSetPrinter: typeName = self.val.type.name match = self.pattern.match(typeName) self.typeNimName = match.group(1) - typeInfoName, self.nti = enumNti(self.typeNimName, match.group(2)) - - if self.nti is None: - printErrorOnce(typeInfoName, f"NimSetPrinter: lookup global symbol: '{typeInfoName}' failed for {typeName}.\n") def to_string(self): - if self.nti: - nti = self.nti.value(gdb.newest_frame()) - enumStrings = [] - val = int(self.val) - i = 0 - while val > 0: - if (val & 1) == 1: - enumStrings.append(reprEnum(i, nti)) - val = val >> 1 - i += 1 + # Remove the tySet from the type name + typ = gdb.lookup_type(self.val.type.name[6:]) + enumStrings = [] + val = int(self.val) + i = 0 + while val > 0: + if (val & 1) == 1: + enumStrings.append(reprEnum(i, typ)) + val = val >> 1 + i += 1 - return '{' + ', '.join(enumStrings) + '}' - else: - return str(int(self.val)) + return '{' + ', '.join(enumStrings) + '}' ################################################################################ @@ -472,41 +463,81 @@ class NimHashSetPrinter: ################################################################################ -class NimSeqPrinter: - # the pointer is explicity part of the type. So it is part of - # ``pattern``. - pattern = re.compile(r'^tySequence_\w* \*$') +class NimSeq: + # Wrapper around sequences. + # This handles the differences between old and new runtime def __init__(self, val): self.val = val + # new runtime has sequences on stack, old has them on heap + self.new = val.type.code != gdb.TYPE_CODE_PTR + if self.new: + # Some seqs are just the content and to save repeating ourselves we do + # handle them here. Only thing that needs to check this is the len/data getters + self.isContent = val.type.name.endswith("Content") + + def __bool__(self): + if self.new: + return self.val is not None + else: + return bool(self.val) + + def __len__(self): + if not self: + return 0 + if self.new: + if self.isContent: + return int(self.val["cap"]) + else: + return int(self.val["len"]) + else: + return self.val["Sup"]["len"] + + @property + def data(self): + if self.new: + if self.isContent: + return self.val["data"] + elif self.val["p"]: + return self.val["p"]["data"] + else: + return self.val["data"] + + @property + def cap(self): + if not self: + return 0 + if self.new: + if self.isContent: + return int(self.val["cap"]) + elif self.val["p"]: + return int(self.val["p"]["cap"]) + else: + return 0 + return int(self.val['Sup']['reserved']) + +class NimSeqPrinter: + pattern = re.compile(r'^tySequence_\w*\s?\*?$') + + def __init__(self, val): + self.val = NimSeq(val) + def display_hint(self): return 'array' def to_string(self): - len = 0 - cap = 0 - if self.val: - len = int(self.val['Sup']['len']) - cap = int(self.val['Sup']['reserved']) - - return 'seq({0}, {1})'.format(len, cap) + return f'seq({len(self.val)}, {self.val.cap})' def children(self): if self.val: val = self.val - valType = val.type - length = int(val['Sup']['len']) + length = len(val) if length <= 0: return - dataType = valType['data'].type - data = val['data'] - - if self.val.type.name is None: - dataType = valType['data'].type.target().pointer() - data = val['data'].cast(dataType) + data = val.data inaccessible = False for i in range(length): @@ -585,7 +616,7 @@ class NimTablePrinter: if self.val: counter = int(self.val['counter']) if self.val['data']: - capacity = int(self.val['data']['Sup']['len']) + capacity = NimSeq(self.val["data"]).cap return 'Table({0}, {1})'.format(counter, capacity) @@ -597,163 +628,6 @@ class NimTablePrinter: yield (idxStr + '.Field1', entry['Field1']) yield (idxStr + '.Field2', entry['Field2']) -################################################################ - -# this is untested, therefore disabled - -# class NimObjectPrinter: -# pattern = re.compile(r'^tyObject_([A-Za-z0-9]+)__(_?[A-Za-z0-9]*)(:? \*)?$') - -# def __init__(self, val): -# self.val = val -# self.valType = None -# self.valTypeNimName = None - -# def display_hint(self): -# return 'object' - -# def _determineValType(self): -# if self.valType is None: -# vt = self.val.type -# if vt.name is None: -# target = vt.target() -# self.valType = target.pointer() -# self.fields = target.fields() -# self.valTypeName = target.name -# self.isPointer = True -# else: -# self.valType = vt -# self.fields = vt.fields() -# self.valTypeName = vt.name -# self.isPointer = False - -# def to_string(self): -# if self.valTypeNimName is None: -# self._determineValType() -# match = self.pattern.match(self.valTypeName) -# self.valTypeNimName = match.group(1) - -# return self.valTypeNimName - -# def children(self): -# self._determineValType() -# if self.isPointer and int(self.val) == 0: -# return -# self.baseVal = self.val.referenced_value() if self.isPointer else self.val - -# for c in self.handleFields(self.baseVal, getNimRti(self.valTypeName)): -# yield c - -# def handleFields(self, currVal, rti, fields = None): -# rtiSons = None -# discField = (0, None) -# seenSup = False -# if fields is None: -# fields = self.fields -# try: # XXX: remove try after finished debugging this method -# for (i, field) in enumerate(fields): -# if field.name == "Sup": # inherited data -# seenSup = True -# baseRef = rti['base'] -# if baseRef: -# baseRti = baseRef.referenced_value() -# baseVal = currVal['Sup'] -# baseValType = baseVal.type -# if baseValType.name is None: -# baseValType = baseValType.target().pointer() -# baseValFields = baseValType.target().fields() -# else: -# baseValFields = baseValType.fields() - -# for c in self.handleFields(baseVal, baseRti, baseValFields): -# yield c -# else: -# if field.type.code == gdb.TYPE_CODE_UNION: -# # if not rtiSons: -# rtiNode = rti['node'].referenced_value() -# rtiSons = rtiNode['sons'] - -# if not rtiSons and int(rtiNode['len']) == 0 and str(rtiNode['name']) != "0x0": -# rtiSons = [rti['node']] # sons are dereferenced by the consumer - -# if not rtiSons: -# printErrorOnce(self.valTypeName, f"NimObjectPrinter: UNION field can't be displayed without RTI {self.valTypeName}, using fallback.\n") -# # yield (field.name, self.baseVal[field]) # XXX: this fallback seems wrong -# return # XXX: this should probably continue instead? - -# if int(rtiNode['len']) != 0 and str(rtiNode['name']) != "0x0": -# gdb.write(f"wtf IT HAPPENED {self.valTypeName}\n", gdb.STDERR) - -# discNode = rtiSons[discField[0]].referenced_value() -# if not discNode: -# raise ValueError("Can't find union discriminant field in object RTI") - -# discNodeLen = int(discNode['len']) -# discFieldVal = int(currVal[discField[1].name]) - -# unionNodeRef = None -# if discFieldVal < discNodeLen: -# unionNodeRef = discNode['sons'][discFieldVal] -# if not unionNodeRef: -# unionNodeRef = discNode['sons'][discNodeLen] - -# if not unionNodeRef: -# printErrorOnce(self.valTypeName + "no union node", f"wtf is up with sons {self.valTypeName} {unionNodeRef} {rtiNode['offset']} {discNode} {discFieldVal} {discNodeLen} {discField[1].name} {field.name} {field.type}\n") -# continue - -# unionNode = unionNodeRef.referenced_value() - -# fieldName = "" if field.name == None else field.name.lower() -# unionNodeName = "" if not unionNode['name'] else unionNode['name'].string("utf-8", "ignore") -# if not unionNodeName or unionNodeName.lower() != fieldName: -# unionFieldName = f"_{discField[1].name.lower()}_{int(rti['node'].referenced_value()['len'])}" -# gdb.write(f"wtf i: {i} union: {unionFieldName} field: {fieldName} type: {field.type.name} tag: {field.type.tag}\n", gdb.STDERR) -# else: -# unionFieldName = unionNodeName - -# if discNodeLen == 0: -# yield (unionFieldName, currVal[unionFieldName]) -# else: -# unionNodeLen = int(unionNode['len']) -# if unionNodeLen > 0: -# for u in range(unionNodeLen): -# un = unionNode['sons'][u].referenced_value()['name'].string("utf-8", "ignore") -# yield (un, currVal[unionFieldName][un]) -# else: -# yield(unionNodeName, currVal[unionFieldName]) -# else: -# discIndex = i - 1 if seenSup else i -# discField = (discIndex, field) # discriminant field is the last normal field -# yield (field.name, currVal[field.name]) -# except GeneratorExit: -# raise -# except: -# gdb.write(f"wtf {self.valTypeName} {i} fn: {field.name} df: {discField} rti: {rti} rtiNode: {rti['node'].referenced_value()} rtiSons: {rtiSons} {sys.exc_info()} {traceback.format_tb(sys.exc_info()[2], limit = 10)}\n", gdb.STDERR) -# gdb.write(f"wtf {self.valTypeName} {i} {field.name}\n", gdb.STDERR) - -# # seenSup = False -# # for (i, field) in enumerate(fields): -# # # if field.name: -# # # val = currVal[field.name] -# # # else: -# # # val = None -# # rtiNode = rti['node'].referenced_value() -# # rtiLen = int(rtiNode['len']) -# # if int(rtiNode['len']) > 0: -# # sons = rtiNode['sons'] -# # elif int(rti['len']) == 0 and str(rti['name']) != "0x0": -# # sons = [rti['node']] # sons are dereferenced by the consumer -# # sonsIdx = i - 1 if seenSup else i -# # s = sons[sonsIdx].referenced_value() -# # addr = int(currVal.address) -# # off = addr + int(rtiNode['offset']) -# # seenSup = seenSup or field.name == "Sup" - -# # gdb.write(f"wtf: i: {i} sonsIdx: {sonsIdx} field: {field.name} rtiLen: {rtiLen} rti: {rti} rtiNode: {rtiNode} isUnion: {field.type.code == gdb.TYPE_CODE_UNION} s: {s}\n", gdb.STDERR) - -# raise - - ################################################################################ class NimFrameFilter: From c73c76fdc62e76a9e36e2595b9854a6f0cb9e904 Mon Sep 17 00:00:00 2001 From: Century Systems Date: Tue, 21 Feb 2023 11:38:25 +0900 Subject: [PATCH 3709/3768] NuttX added supports getrlimit(RLIMIT_NOFILE), so remove NuttX specific codes. (#21385) async: NuttX added supports getrlimit(RLIMIT_NOFILE), so remove NuttX-specific codes. Signed-off-by: Takeyoshi Kikuchi --- lib/posix/posix_other_consts.nim | 3 --- lib/pure/asyncdispatch.nim | 3 --- lib/pure/selectors.nim | 3 --- 3 files changed, 9 deletions(-) diff --git a/lib/posix/posix_other_consts.nim b/lib/posix/posix_other_consts.nim index 4b1644f20..506c92158 100644 --- a/lib/posix/posix_other_consts.nim +++ b/lib/posix/posix_other_consts.nim @@ -473,9 +473,6 @@ var FD_SETSIZE* {.importc: "FD_SETSIZE", header: "".}: cint when defined(zephyr): # Zephyr specific hardcoded value var FD_MAX* {.importc: "CONFIG_POSIX_MAX_FDS ", header: "".}: cint -elif defined(nuttx): - # NuttX specific user configuration value - var NACTIVESOCKETS* {.importc: "CONFIG_NET_NACTIVESOCKETS", header: "".}: cint # var MSG_CTRUNC* {.importc: "MSG_CTRUNC", header: "".}: cint diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 64100afa7..2bfa8587c 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -2033,9 +2033,6 @@ when defined(linux) or defined(windows) or defined(macosx) or defined(bsd) or result = 16_700_000 elif defined(zephyr) or defined(freertos): result = FD_MAX - elif defined(nuttx): - # The maximum number of concurrently active UDP and TCP ports. - result = NACTIVESOCKETS else: var fdLim: RLimit if getrlimit(RLIMIT_NOFILE, fdLim) < 0: diff --git a/lib/pure/selectors.nim b/lib/pure/selectors.nim index ef8736cdf..b15a25a1d 100644 --- a/lib/pure/selectors.nim +++ b/lib/pure/selectors.nim @@ -337,9 +337,6 @@ else: 16_700_000 elif defined(zephyr) or defined(freertos): FD_MAX - elif defined(nuttx): - # The maximum number of concurrently active UDP and TCP ports. - NACTIVESOCKETS else: var fdLim: RLimit var res = int(getrlimit(RLIMIT_NOFILE, fdLim)) From f7bd2088aa5fe970902d4cafc94933354fdec8a7 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Tue, 21 Feb 2023 11:27:42 +0800 Subject: [PATCH 3710/3768] corrects the linkerexe of riscv64 in config (#21292) --- config/nim.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/nim.cfg b/config/nim.cfg index f7b7c119a..973de077a 100644 --- a/config/nim.cfg +++ b/config/nim.cfg @@ -39,7 +39,7 @@ arm64.linux.gcc.linkerexe = "aarch64-linux-gnu-gcc" riscv32.linux.gcc.exe = "riscv64-linux-gnu-gcc" riscv32.linux.gcc.linkerexe = "riscv64-linux-gnu-gcc" riscv64.linux.gcc.exe = "riscv64-linux-gnu-gcc" -riscv64.linux.gcc.linkerexe = "arm-linux-gnueabihf-gcc" +riscv64.linux.gcc.linkerexe = "riscv64-linux-gnu-gcc" # For OpenWRT, you will also need to adjust PATH to point to your toolchain. mips.linux.gcc.exe = "mips-openwrt-linux-gcc" From ffd6bc87495ede82f9a8086d38c3348a40b11672 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Tue, 21 Feb 2023 15:58:45 +0800 Subject: [PATCH 3711/3768] csources_v2 can build the ORC-booted compiler; building now uses strict mode (#21411) * csources_v2 can build the ORC-booted compiler; building now uses strict mode * test booting in refc --- koch.nim | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/koch.nim b/koch.nim index fa498e017..52378e43f 100644 --- a/koch.nim +++ b/koch.nim @@ -530,8 +530,7 @@ proc runCI(cmd: string) = # boot without -d:nimHasLibFFI to make sure this still works # `--lib:lib` is needed for bootstrap on openbsd, for reasons described in # https://github.com/nim-lang/Nim/pull/14291 (`getAppFilename` bugsfor older nim on openbsd). - kochExecFold("Boot in release mode", "boot -d:release --gc:refc -d:nimStrictMode --lib:lib") - kochExecFold("Boot Nim ORC", "boot -d:release --lib:lib") + kochExecFold("Boot Nim ORC", "boot -d:release -d:nimStrictMode --lib:lib") when false: # debugging: when you need to run only 1 test in CI, use something like this: execFold("debugging test", "nim r tests/stdlib/tosproc.nim") @@ -587,6 +586,9 @@ proc runCI(cmd: string) = execFold("Run atlas tests", "nim c -r -d:atlasTests tools/atlas/atlas.nim clone https://github.com/disruptek/balls") + kochExecFold("Testing booting in refc", "boot -d:release --mm:refc -d:nimStrictMode --lib:lib") + + proc testUnixInstall(cmdLineRest: string) = csource("-d:danger" & cmdLineRest) xz(false, cmdLineRest) From bdc850916fb5d23fa4b9282309985e0fdfb0db36 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Tue, 21 Feb 2023 18:46:50 +0800 Subject: [PATCH 3712/3768] improve invalid module names errors (#21412) I have seen these questions: "Why I got an invalid module name errors?". To eliminate this kind of questions, We might improve th error messages. Though, the question might evolve into "What is a valid Nim identifier", which should be more searchable on the Internet. --- compiler/modules.nim | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/compiler/modules.nim b/compiler/modules.nim index 7f6ff8622..838a89d83 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -38,7 +38,8 @@ proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym = name: getModuleIdent(graph, filename), info: newLineInfo(fileIdx, 1, 1)) if not isNimIdentifier(result.name.s): - rawMessage(graph.config, errGenerated, "invalid module name: " & result.name.s) + rawMessage(graph.config, errGenerated, "invalid module name: '" & result.name.s & + "'; a module name must be a valid Nim identifier.") partialInitModule(result, graph, fileIdx, filename) graph.registerModule(result) From e54d3cc418f9eab750563aa771070374d559fe57 Mon Sep 17 00:00:00 2001 From: Jake Leahy Date: Tue, 21 Feb 2023 22:02:42 +1100 Subject: [PATCH 3713/3768] Support tuples in nim-gdb (#21410) * Support for printing tuple types * Add test of printing a tuple * Add support for printing tuples in GDB * Forgot to [skip ci] --- .../untestable/gdb/gdb_pretty_printer_test.py | 3 ++- .../gdb/gdb_pretty_printer_test_program.nim | 5 +++- tools/nim-gdb.py | 25 ++++++++++++++++--- 3 files changed, 28 insertions(+), 5 deletions(-) diff --git a/tests/untestable/gdb/gdb_pretty_printer_test.py b/tests/untestable/gdb/gdb_pretty_printer_test.py index d28d01a60..a96df9992 100644 --- a/tests/untestable/gdb/gdb_pretty_printer_test.py +++ b/tests/untestable/gdb/gdb_pretty_printer_test.py @@ -28,7 +28,8 @@ outputs = [ 'seq(3, 3) = {"one", "two", "three"}', 'Table(3, 64) = {[4] = "four", [5] = "five", [6] = "six"}', 'Table(3, 8) = {["two"] = 2, ["three"] = 3, ["one"] = 1}', - '{a = 1, b = "some string"}' + '{a = 1, b = "some string"}', + '("hello", 42)' ] argRegex = re.compile("^.* = (?:No suitable Nim \$ operator found for type: \w+\s*)*(.*)$") diff --git a/tests/untestable/gdb/gdb_pretty_printer_test_program.nim b/tests/untestable/gdb/gdb_pretty_printer_test_program.nim index b54fc1a7f..163c99860 100644 --- a/tests/untestable/gdb/gdb_pretty_printer_test_program.nim +++ b/tests/untestable/gdb/gdb_pretty_printer_test_program.nim @@ -80,7 +80,10 @@ proc testProc(): void = var obj = MyObj(a: 1, b: "some string") myDebug(obj) #15 - assert counter == 15 + var tup = ("hello", 42) + myDebug(tup) # 16 + + assert counter == 16 testProc() diff --git a/tools/nim-gdb.py b/tools/nim-gdb.py index e3af0dde6..1050197c9 100644 --- a/tools/nim-gdb.py +++ b/tools/nim-gdb.py @@ -112,6 +112,12 @@ class NimTypeRecognizer: result = self.type_map_static.get(tname, None) if result: return result + elif tname.startswith("tyEnum_"): + return getNimName(tname) + elif tname.startswith("tyTuple__"): + # We make the name be the field types (Just like in Nim) + fields = ", ".join([self.recognize(field.type) for field in type_obj.fields()]) + return f"({fields})" rti = getNimRti(tname) if rti: @@ -154,7 +160,7 @@ class DollarPrintFunction (gdb.Function): @staticmethod - def invoke_static(arg): + def invoke_static(arg, ignore_errors = False): if arg.type.code == gdb.TYPE_CODE_PTR and arg.type.target().name in NIM_STRING_TYPES: return arg argTypeName = str(arg.type) @@ -169,8 +175,8 @@ class DollarPrintFunction (gdb.Function): func_value = gdb.lookup_global_symbol(func, gdb.SYMBOL_FUNCTIONS_DOMAIN).value() return func_value(arg.address) - - debugPrint(f"No suitable Nim $ operator found for type: {getNimName(argTypeName)}\n") + if not ignore_errors: + debugPrint(f"No suitable Nim $ operator found for type: {getNimName(argTypeName)}\n") return None def invoke(self, arg): @@ -630,6 +636,19 @@ class NimTablePrinter: ################################################################################ +class NimTuplePrinter: + pattern = re.compile(r"^tyTuple__([A-Za-z0-9]*)") + + def __init__(self, val): + self.val = val + + def to_string(self): + # We don't have field names so just print out the tuple as if it was anonymous + tupleValues = [str(self.val[field.name]) for field in self.val.type.fields()] + return f"({', '.join(tupleValues)})" + +################################################################################ + class NimFrameFilter: def __init__(self): self.name = "nim-frame-filter" From 6d423f1856d086ea9e6b0995e3e5f831f4f89aa6 Mon Sep 17 00:00:00 2001 From: Jake Leahy Date: Tue, 21 Feb 2023 22:04:27 +1100 Subject: [PATCH 3714/3768] Make `Time` work with `std/strformat` (#21409) * Add test case * Remove formatValue template for `Time` It didn't handle empty specifier correctly which caused it to be blank with strformat --- lib/pure/times.nim | 6 +----- tests/stdlib/t21406.nim | 5 +++++ 2 files changed, 6 insertions(+), 5 deletions(-) create mode 100644 tests/stdlib/t21406.nim diff --git a/lib/pure/times.nim b/lib/pure/times.nim index 3327b2510..70af466d9 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -2109,7 +2109,7 @@ proc format*(dt: DateTime, f: static[string]): string {.raises: [].} = const f2 = initTimeFormat(f) result = dt.format(f2) -proc formatValue*(result: var string; value: DateTime, specifier: string) = +proc formatValue*(result: var string; value: DateTime | Time, specifier: string) = ## adapter for strformat. Not intended to be called directly. result.add format(value, if specifier.len == 0: "yyyy-MM-dd'T'HH:mm:sszzz" else: specifier) @@ -2133,10 +2133,6 @@ proc format*(time: Time, f: static[string], zone: Timezone = local()): string const f2 = initTimeFormat(f) result = time.inZone(zone).format(f2) -template formatValue*(result: var string; value: Time, specifier: string) = - ## adapter for `strformat`. Not intended to be called directly. - result.add format(value, specifier) - proc parse*(input: string, f: TimeFormat, zone: Timezone = local(), loc: DateTimeLocale = DefaultLocale): DateTime {.raises: [TimeParseError, Defect].} = diff --git a/tests/stdlib/t21406.nim b/tests/stdlib/t21406.nim new file mode 100644 index 000000000..5b96227ce --- /dev/null +++ b/tests/stdlib/t21406.nim @@ -0,0 +1,5 @@ +import std/[times, strformat] +import std/assertions + +doAssert fmt"{getTime()}" == $getTime() +doAssert fmt"{now()}" == $now() From de65b380edb0fe256054e217a1e6e64a6bb97527 Mon Sep 17 00:00:00 2001 From: Ecorous <49562894+Ecorous@users.noreply.github.com> Date: Tue, 21 Feb 2023 11:14:41 +0000 Subject: [PATCH 3715/3768] Add `getDataDir` proc (#21408) * Add getDataDir() * Update lib/std/private/osappdirs.nim --------- Co-authored-by: Andreas Rumpf --- lib/std/private/osappdirs.nim | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/lib/std/private/osappdirs.nim b/lib/std/private/osappdirs.nim index 581b511ff..07a6809bb 100644 --- a/lib/std/private/osappdirs.nim +++ b/lib/std/private/osappdirs.nim @@ -12,6 +12,7 @@ proc getHomeDir*(): string {.rtl, extern: "nos$1", ## for the convenience of processing paths coming from user configuration files. ## ## See also: + ## * `getDataDir proc`_ ## * `getConfigDir proc`_ ## * `getTempDir proc`_ ## * `expandTilde proc`_ @@ -24,6 +25,30 @@ proc getHomeDir*(): string {.rtl, extern: "nos$1", when defined(windows): return getEnv("USERPROFILE") & "\\" else: return getEnv("HOME") & "/" +proc getDataDir*(): string {.rtl, extern: "nos$1" + tags: [ReadEnvEffect, ReadIOEffect].} = + ## Returns the data directory of the current user for applications. + ## + ## On non-Windows OSs, this proc conforms to the XDG Base Directory + ## spec. Thus, this proc returns the value of the `XDG_DATA_HOME` environment + ## variable if it is set, otherwise it returns the default configuration + ## directory ("~/.local/share" or "~/Library/Application Support" on macOS). + ## + ## See also: + ## * `getHomeDir proc`_ + ## * `getConfigDir proc`_ + ## * `getTempDir proc`_ + ## * `expandTilde proc`_ + ## * `getCurrentDir proc`_ + ## * `setCurrentDir proc`_ + when defined(windows): + result = getEnv("APPDATA") + elif defined(macosx): + result = getEnv("XDG_DATA_HOME", getEnv("HOME") / "Library" / "Application Support") + else: + result = getEnv("XDG_DATA_HOME", getEnv("HOME") / ".local" / "share") + result.normalizePathEnd(trailingSep = true) + proc getConfigDir*(): string {.rtl, extern: "nos$1", tags: [ReadEnvEffect, ReadIOEffect].} = ## Returns the config directory of the current user for applications. @@ -38,6 +63,7 @@ proc getConfigDir*(): string {.rtl, extern: "nos$1", ## ## See also: ## * `getHomeDir proc`_ + ## * `getDataDir proc`_ ## * `getTempDir proc`_ ## * `expandTilde proc`_ ## * `getCurrentDir proc`_ @@ -63,6 +89,7 @@ proc getCacheDir*(): string = ## * `getHomeDir proc`_ ## * `getTempDir proc`_ ## * `getConfigDir proc`_ + ## * `getDataDir proc`_ # follows https://crates.io/crates/platform-dirs when defined(windows): result = getEnv("LOCALAPPDATA") From 0a45543cc1e8a2e8db59eaed864cd908726bf48d Mon Sep 17 00:00:00 2001 From: Jake Leahy Date: Tue, 21 Feb 2023 22:27:12 +1100 Subject: [PATCH 3716/3768] Specify that address is taken when converter takes a var parameter (#21391) * Add test case * closes #21247 Add the sfAddrTaken flag to var parameters in converters This allows the JS backend to properly pass the parameter as a fat pointer --- compiler/sigmatch.nim | 1 + tests/js/t21247.nim | 15 +++++++++++++++ 2 files changed, 16 insertions(+) create mode 100644 tests/js/t21247.nim diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index 0dbd5e5f1..f8c7fa870 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -1980,6 +1980,7 @@ proc userConvMatch(c: PContext, m: var TCandidate, f, a: PType, param = implicitConv(nkHiddenSubConv, src, copyTree(arg), m, c) elif src.kind in {tyVar}: # Analyse the converter return type + arg.sym.flags.incl sfAddrTaken param = newNodeIT(nkHiddenAddr, arg.info, s.typ[1]) param.add copyTree(arg) else: diff --git a/tests/js/t21247.nim b/tests/js/t21247.nim new file mode 100644 index 000000000..5b38787b3 --- /dev/null +++ b/tests/js/t21247.nim @@ -0,0 +1,15 @@ +import std/typetraits + +type + QueryParams* = distinct seq[(string, string)] + +converter toBase*(params: var QueryParams): var seq[(string, string)] = + params.distinctBase + +proc foo(): QueryParams = + # Issue was that the implicit converter call didn't say that it took the + # address of the parameter it was converting. This led to the parameter not being + # passed as a fat pointer which toBase expected + result.add(("hello", "world")) + +assert foo().distinctBase() == @[("hello", "world")] From a3603c8a6ee4a8ae147d2f2099a18395e819a8a3 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Wed, 22 Feb 2023 06:42:28 +0800 Subject: [PATCH 3717/3768] saves one unnecessary compare which is also a small regression (#21413) saves one unnecessary compare which is also a sall regression follow up https://github.com/nim-lang/Nim/commit/d30c6419a051a815e3fdb354ac79522f17e55bda --- compiler/passes.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/passes.nim b/compiler/passes.nim index a8f67300c..c20763fc3 100644 --- a/compiler/passes.nim +++ b/compiler/passes.nim @@ -143,7 +143,7 @@ proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator; while true: openParser(p, fileIdx, s, graph.cache, graph.config) - if not belongsToStdlib(graph, module) or (belongsToStdlib(graph, module) and module.name.s == "distros"): + if (not belongsToStdlib(graph, module)) or module.name.s == "distros": # XXX what about caching? no processing then? what if I change the # modules to include between compilation runs? we'd need to track that # in ROD files. I think we should enable this feature only From 64a788cafb287ace8c63f5e86cb84c520eab3f2a Mon Sep 17 00:00:00 2001 From: Benji York Date: Wed, 22 Feb 2023 05:12:10 -0600 Subject: [PATCH 3718/3768] Fix a couple of small keyword issues. (#21416) In this section of the manual, "if" should be enclosed in backticks and "elif" should be lower case. --- doc/manual.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/manual.md b/doc/manual.md index 779e99186..ef790c362 100644 --- a/doc/manual.md +++ b/doc/manual.md @@ -3574,8 +3574,8 @@ Example: var y = if x > 8: 9 else: 10 ``` -An if expression always results in a value, so the `else` part is -required. `Elif` parts are also allowed. +An `if` expression always results in a value, so the `else` part is +required. `elif` parts are also allowed. When expression --------------- From ab1d4a5d58af8452b26f57f244bcd0d39b409c5e Mon Sep 17 00:00:00 2001 From: Century Systems Date: Thu, 23 Feb 2023 03:53:04 +0900 Subject: [PATCH 3719/3768] ioselectors_epoll: for NuttX, limit initial numFD to configured value. (#21421) ioselectors: ioselectors_epoll: for NuttX, limit initial numFD to configured value. In the NuttX build config, there is a setting called "FS_NEPOLL_DESCRIPTORS". -------- config FS_NEPOLL_DESCRIPTORS int "Maximum number of default epoll descriptors for epoll_create1(2)" default 8 ---help--- The maximum number of default epoll descriptors for epoll_create1(2) -------- For NuttX, change the number of fd arrays allocated by newSelector() to that value. Signed-off-by: Takeyoshi Kikuchi --- lib/posix/posix_other_consts.nim | 3 +++ lib/pure/ioselects/ioselectors_epoll.nim | 7 ++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/posix/posix_other_consts.nim b/lib/posix/posix_other_consts.nim index 506c92158..08069fe9a 100644 --- a/lib/posix/posix_other_consts.nim +++ b/lib/posix/posix_other_consts.nim @@ -750,3 +750,6 @@ var SEEK_SET* {.importc: "SEEK_SET", header: "".}: cint var SEEK_CUR* {.importc: "SEEK_CUR", header: "".}: cint var SEEK_END* {.importc: "SEEK_END", header: "".}: cint +# +when defined(nuttx): + var NEPOLL_MAX* {.importc: "CONFIG_FS_NEPOLL_DESCRIPTORS", header: "".}: cint diff --git a/lib/pure/ioselects/ioselectors_epoll.nim b/lib/pure/ioselects/ioselectors_epoll.nim index 8526eb8a3..08cb6ed74 100644 --- a/lib/pure/ioselects/ioselectors_epoll.nim +++ b/lib/pure/ioselects/ioselectors_epoll.nim @@ -72,11 +72,16 @@ type SelectEvent* = ptr SelectEventImpl proc newSelector*[T](): Selector[T] = + proc initialNumFD(): int {.inline.} = + when defined(nuttx): + result = NEPOLL_MAX + else: + result = 1024 # Retrieve the maximum fd count (for current OS) via getrlimit() var maxFD = maxDescriptors() doAssert(maxFD > 0) # Start with a reasonable size, checkFd() will grow this on demand - const numFD = 1024 + let numFD = initialNumFD() var epollFD = epoll_create1(O_CLOEXEC) if epollFD < 0: From fdd75202576634f1fb8c7948d6031e34af9ea67e Mon Sep 17 00:00:00 2001 From: c-blake Date: Wed, 22 Feb 2023 13:54:04 -0500 Subject: [PATCH 3720/3768] Fix the TODO portion of recently added `posix_fallocate` on OS X. (#21387) --- lib/posix/posix.nim | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index aee9308a6..07652c64d 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -198,9 +198,24 @@ proc posix_fadvise*(a1: cint, a2, a3: Off, a4: cint): cint {. proc ftruncate*(a1: cint, a2: Off): cint {.importc, header: "".} when defined(osx): # 2001 POSIX evidently does not concern Apple + type FStore {.importc: "fstore_t", header: "", bycopy.} = object + fst_flags: uint32 ## IN: flags word + fst_posmode: cint ## IN: indicates offset field + fst_offset, ## IN: start of the region + fst_length, ## IN: size of the region + fst_bytesalloc: Off ## OUT: number of bytes allocated + var F_PEOFPOSMODE {.importc, header: "".}: cint + var F_ALLOCATEALL {.importc, header: "".}: uint32 + var F_PREALLOCATE {.importc, header: "".}: cint proc posix_fallocate*(a1: cint, a2, a3: Off): cint = - ftruncate(a1, a2 + a3) # Set size to off + len, max offset -else: # TODO: Use fcntl(fd, F_PREALLOCATE, ..) above + var fst = FStore(fst_flags: F_ALLOCATEALL, fst_posmode: F_PEOFPOSMODE, + fst_offset: a2, fst_length: a3) + # Must also call ftruncate to match what POSIX does. Unlike posix_fallocate, + # this can shrink files. Could guard w/getFileSize, but caller likely knows + # present size & has no good reason to call this unless it is growing. + if fcntl(a1, F_PREALLOCATE, fst.addr) != cint(-1): ftruncate(a1, a2 + a3) + else: cint(-1) +else: proc posix_fallocate*(a1: cint, a2, a3: Off): cint {. importc, header: "".} From 38f876dd484fc1a3ce44788b1ed9601eea8619f6 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Thu, 23 Feb 2023 03:34:20 +0800 Subject: [PATCH 3721/3768] fixes #19795; fixes #11852; fixes #19974; remove parsing pipeline, Nim now parses the whole module at one time (#21379) * fixes #19795; remove parse pipeline * isScript * fixes nimscriptapi * don't touch reorder * check script * fixes tests * it seems implicit imports of system cause troubles * access the first child of `nkStmtList` * ignore comments * minor messages * perhaps increases hloLoopDetector * the module is a stmtList, which changes the errors * fixes nimdoc * fixes tlinter * fixes nim secret tests * fixes arc_misc * fixes nim secret tests again * safe; fixes one more test * GlobalError is the root cause too * fixes parsing errors * put emit types to the cfsForwardTypes section * fixes #11852; `{.push checks:off}` now works in procs * disable navigator * fixes nimdoc * add tests for JS * fixes nimsuggest --- compiler/backendpragmas.nim | 25 ++++++ compiler/ccgstmts.nim | 10 ++- compiler/cgen.nim | 2 +- compiler/cgendata.nim | 1 + compiler/jsgen.nim | 10 ++- compiler/passes.nim | 7 +- compiler/pragmas.nim | 2 +- compiler/sem.nim | 6 ++ .../expected/subdir/subdir_b/utils.idx | 2 +- nimsuggest/tests/tchk1.nim | 2 +- tests/arc/tarcmisc.nim | 4 +- .../tdont_return_unowned_from_owned.nim | 35 ++++---- tests/errmsgs/t8064.nim | 8 +- tests/misc/tnoforward.nim | 1 + tests/misc/trunner.nim | 4 +- tests/modules/t8665.nim | 1 + tests/modules/treorder.nim | 2 +- tests/navigator/tincludefile.nim | 2 +- tests/navigator/tnav1.nim | 2 +- tests/nimdoc/trunnableexamples.nim | 4 +- .../{overflw => overflow}/tdistinct_range.nim | 0 tests/overflow/toverflow.nim | 82 +++++++++++++++++++ .../toverflw2.nim => overflow/toverflow2.nim} | 0 .../toverflow_reorder.nim} | 4 +- tests/{overflw => overflow}/tovfint.nim | 0 tests/parser/t20922.nim | 29 ++----- tests/pragmas/tpush.nim | 12 +++ tests/pragmas/treorder.nim | 1 + tests/proc/t19795.nim | 19 +++++ tests/tools/tlinter.nim | 2 +- tests/trmacros/tor.nim | 20 +++-- 31 files changed, 227 insertions(+), 72 deletions(-) create mode 100644 compiler/backendpragmas.nim rename tests/{overflw => overflow}/tdistinct_range.nim (100%) create mode 100644 tests/overflow/toverflow.nim rename tests/{overflw/toverflw2.nim => overflow/toverflow2.nim} (100%) rename tests/{overflw/toverflw.nim => overflow/toverflow_reorder.nim} (96%) rename tests/{overflw => overflow}/tovfint.nim (100%) create mode 100644 tests/proc/t19795.nim diff --git a/compiler/backendpragmas.nim b/compiler/backendpragmas.nim new file mode 100644 index 000000000..b18644810 --- /dev/null +++ b/compiler/backendpragmas.nim @@ -0,0 +1,25 @@ +import pragmas, options, ast, trees + +proc pushBackendOption(optionsStack: var seq[TOptions], options: var TOptions) = + optionsStack.add options + +proc popBackendOption(optionsStack: var seq[TOptions], options: var TOptions) = + options = optionsStack[^1] + optionsStack.setLen(optionsStack.len-1) + +proc processPushBackendOption*(optionsStack: var seq[TOptions], options: var TOptions, + n: PNode, start: int) = + pushBackendOption(optionsStack, options) + for i in start..= 1 and n[0].kind in {nkStrLit..nkTripleStrLit}: let sec = n[0].strVal - if sec.startsWith("/*TYPESECTION*/"): result = cfsTypes + if sec.startsWith("/*TYPESECTION*/"): result = cfsForwardTypes # TODO WORKAROUND elif sec.startsWith("/*VARSECTION*/"): result = cfsVars elif sec.startsWith("/*INCLUDESECTION*/"): result = cfsHeaders @@ -1555,9 +1554,14 @@ proc genEmit(p: BProc, t: PNode) = line(p, cpsStmts, s) proc genPragma(p: BProc, n: PNode) = - for it in n.sons: + for i in 0.. 0 if we are within a loop splitDecls*: int # > 0 if we are in some context for C++ that diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 60399d3cd..572d19b13 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -33,7 +33,7 @@ import nversion, msgs, idents, types, ropes, passes, ccgutils, wordrecg, renderer, cgmeth, lowerings, sighashes, modulegraphs, lineinfos, rodutils, - transf, injectdestructors, sourcemap, astmsgs + transf, injectdestructors, sourcemap, astmsgs, backendpragmas import json, sets, math, tables, intsets import strutils except addf @@ -98,6 +98,7 @@ type prc: PSym globals, locals, body: Rope options: TOptions + optionsStack: seq[TOptions] module: BModule g: PGlobals generatedParamCopies: IntSet @@ -2557,9 +2558,14 @@ proc genStmt(p: PProc, n: PNode) = if r.res != "": lineF(p, "$#;$n", [r.res]) proc genPragma(p: PProc, n: PNode) = - for it in n.sons: + for i in 0..> (toplevel): import(dirty): tests/tchk1.nim [Processing]";;0 chk;;skUnknown;;;;Error;;$file;;12;;0;;"identifier expected, but got \'keyword template\'";;0 chk;;skUnknown;;;;Error;;$file;;14;;0;;"nestable statement requires indentation";;0 -chk;;skUnknown;;;;Error;;$file;;12;;0;;"implementation of \'foo\' expected";;0 chk;;skUnknown;;;;Error;;$file;;17;;0;;"invalid indentation";;0 +chk;;skUnknown;;;;Error;;$file;;12;;0;;"implementation of \'foo\' expected";;0 chk;;skUnknown;;;;Hint;;$file;;12;;9;;"\'foo\' is declared but not used [XDeclaredButNotUsed]";;0 chk;;skUnknown;;;;Hint;;$file;;14;;5;;"\'main\' is declared but not used [XDeclaredButNotUsed]";;0 """ diff --git a/tests/arc/tarcmisc.nim b/tests/arc/tarcmisc.nim index 6435f5e94..a03da6211 100644 --- a/tests/arc/tarcmisc.nim +++ b/tests/arc/tarcmisc.nim @@ -27,14 +27,14 @@ new line after - @['a'] finalizer aaaaa hello -ok true copying 123 42 -closed +ok destroying variable: 20 destroying variable: 10 +closed ''' cmd: "nim c --gc:arc --deepcopy:on -d:nimAllocPagesViaMalloc $file" """ diff --git a/tests/destructor/tdont_return_unowned_from_owned.nim b/tests/destructor/tdont_return_unowned_from_owned.nim index d27626dea..ffe87cd76 100644 --- a/tests/destructor/tdont_return_unowned_from_owned.nim +++ b/tests/destructor/tdont_return_unowned_from_owned.nim @@ -1,9 +1,12 @@ discard """ cmd: "nim check --newruntime --hints:off $file" nimout: ''' -tdont_return_unowned_from_owned.nim(36, 10) Error: cannot return an owned pointer as an unowned pointer; use 'owned(Obj)' as the return type -tdont_return_unowned_from_owned.nim(39, 10) Error: cannot return an owned pointer as an unowned pointer; use 'owned(Obj)' as the return type -tdont_return_unowned_from_owned.nim(42, 6) Error: type mismatch: got +tdont_return_unowned_from_owned.nim(26, 13) Error: assignment produces a dangling ref: the unowned ref lives longer than the owned ref +tdont_return_unowned_from_owned.nim(27, 13) Error: assignment produces a dangling ref: the unowned ref lives longer than the owned ref +tdont_return_unowned_from_owned.nim(31, 10) Error: cannot return an owned pointer as an unowned pointer; use 'owned(RootRef)' as the return type +tdont_return_unowned_from_owned.nim(43, 10) Error: cannot return an owned pointer as an unowned pointer; use 'owned(Obj)' as the return type +tdont_return_unowned_from_owned.nim(46, 10) Error: cannot return an owned pointer as an unowned pointer; use 'owned(Obj)' as the return type +tdont_return_unowned_from_owned.nim(49, 6) Error: type mismatch: got but expected one of: proc new[T](a: var ref T; finalizer: proc (x: ref T) {.nimcall.}) first type mismatch at position: 2 @@ -11,17 +14,21 @@ proc new[T](a: var ref T; finalizer: proc (x: ref T) {.nimcall.}) 2 other mismatching symbols have been suppressed; compile with --showAllMismatches:on to see them expression: new(result) -tdont_return_unowned_from_owned.nim(42, 6) Error: illformed AST: -tdont_return_unowned_from_owned.nim(50, 13) Error: assignment produces a dangling ref: the unowned ref lives longer than the owned ref -tdont_return_unowned_from_owned.nim(51, 13) Error: assignment produces a dangling ref: the unowned ref lives longer than the owned ref -tdont_return_unowned_from_owned.nim(55, 10) Error: cannot return an owned pointer as an unowned pointer; use 'owned(RootRef)' as the return type +tdont_return_unowned_from_owned.nim(49, 6) Error: illformed AST: ''' - errormsg: "cannot return an owned pointer as an unowned pointer; use 'owned(RootRef)' as the return type" + errormsg: "illformed AST:" """ +proc testA(result: var (RootRef, RootRef)) = + let r: owned RootRef = RootRef() + result[0] = r + result[1] = RootRef() +proc testB(): RootRef = + let r: owned RootRef = RootRef() + result = r @@ -39,17 +46,11 @@ proc newObjB(): Obj = result = Obj() proc newObjC(): Obj = - new(result) + new(result) # illFormedAst raises GlobalError, + # without pipeline parsing, it needs to placed at the end + # in case that it disturbs other errors let a = newObjA() let b = newObjB() let c = newObjC() -proc testA(result: var (RootRef, RootRef)) = - let r: owned RootRef = RootRef() - result[0] = r - result[1] = RootRef() - -proc testB(): RootRef = - let r: owned RootRef = RootRef() - result = r diff --git a/tests/errmsgs/t8064.nim b/tests/errmsgs/t8064.nim index 10bb86299..e7941e36a 100644 --- a/tests/errmsgs/t8064.nim +++ b/tests/errmsgs/t8064.nim @@ -1,6 +1,8 @@ +import tables + +values + + discard """ errormsg: "expression has no type: values" """ -import tables - -values \ No newline at end of file diff --git a/tests/misc/tnoforward.nim b/tests/misc/tnoforward.nim index b6a71897a..0d9b40c83 100644 --- a/tests/misc/tnoforward.nim +++ b/tests/misc/tnoforward.nim @@ -1,4 +1,5 @@ discard """ + matrix: "--experimental:codeReordering" output: "10" """ diff --git a/tests/misc/trunner.nim b/tests/misc/trunner.nim index 7fb437ce3..aca19f72d 100644 --- a/tests/misc/trunner.nim +++ b/tests/misc/trunner.nim @@ -342,8 +342,8 @@ tests/newconfig/bar/mfoo.nims""".splitLines when not defined(windows): check3 lines.len == 5 check3 lines[0].isDots - check3 lines[1].dup(removePrefix(">>> ")) == "3" # prompt depends on `nimUseLinenoise` - check3 lines[2].isDots + # check3 lines[1].isDots # todo nim secret might use parsing pipeline + check3 lines[2].dup(removePrefix(">>> ")) == "3" # prompt depends on `nimUseLinenoise` check3 lines[3] == "ab" check3 lines[4] == "" else: diff --git a/tests/modules/t8665.nim b/tests/modules/t8665.nim index 74d31452f..7cfdbdb00 100644 --- a/tests/modules/t8665.nim +++ b/tests/modules/t8665.nim @@ -1,4 +1,5 @@ discard """ + matrix: "--experimental:codeReordering" action: compile """ diff --git a/tests/modules/treorder.nim b/tests/modules/treorder.nim index c81715cd8..626d9684b 100644 --- a/tests/modules/treorder.nim +++ b/tests/modules/treorder.nim @@ -1,5 +1,5 @@ discard """ - cmd: "nim -d:testdef $target $file" + matrix: "--experimental:codeReordering -d:testdef" output: '''works 34 34 defined diff --git a/tests/navigator/tincludefile.nim b/tests/navigator/tincludefile.nim index f35ab2ec9..a913d0736 100644 --- a/tests/navigator/tincludefile.nim +++ b/tests/navigator/tincludefile.nim @@ -1,4 +1,5 @@ discard """ + disabled: true cmd: "nim check $options --defusages:$file,12,7 $file" nimout: '''def tincludefile_temp.nim(11, 10) usage tincludefile_temp.nim(12, 8) @@ -7,7 +8,6 @@ usage tincludefile_temp.nim(12, 8) - proc foo(x: int) = echo x diff --git a/tests/navigator/tnav1.nim b/tests/navigator/tnav1.nim index b6bbdbf19..e76c921f3 100644 --- a/tests/navigator/tnav1.nim +++ b/tests/navigator/tnav1.nim @@ -1,11 +1,11 @@ discard """ + disabled: true cmd: "nim check $options --defusages:$file,12,7 $file" nimout: '''def tnav1_temp.nim(11, 10) usage tnav1_temp.nim(12, 8) ''' """ - import std / [times] proc foo(x: int) = diff --git a/tests/nimdoc/trunnableexamples.nim b/tests/nimdoc/trunnableexamples.nim index c88d8fa3f..1188cdd1b 100644 --- a/tests/nimdoc/trunnableexamples.nim +++ b/tests/nimdoc/trunnableexamples.nim @@ -7,7 +7,6 @@ foo1 foo2 foo3 foo5 -foo6 foo7 in examplesInTemplate1 doc in outer @@ -15,6 +14,7 @@ doc in inner1 doc in inner2 foo8 foo9 +foo6 ''' joinable: false """ @@ -43,7 +43,7 @@ proc fun*() = proc fun*()=echo "foo5" fun() - runnableExamples: + runnableExamples("--experimental:codeReordering --warnings:off"): # `codeReordering` only allowed at top level {.experimental: "codeReordering".} proc fun1() = fun2() diff --git a/tests/overflw/tdistinct_range.nim b/tests/overflow/tdistinct_range.nim similarity index 100% rename from tests/overflw/tdistinct_range.nim rename to tests/overflow/tdistinct_range.nim diff --git a/tests/overflow/toverflow.nim b/tests/overflow/toverflow.nim new file mode 100644 index 000000000..972f929c6 --- /dev/null +++ b/tests/overflow/toverflow.nim @@ -0,0 +1,82 @@ +discard """ + output: "ok" + matrix: "--overflowChecks:off; --overflowChecks:off --b:js" +""" +# Tests nim's ability to detect overflows + +{.push overflowChecks: on.} + +var + a = high(int) + b = -2 + overflowDetected = false + +try: + echo(b - a) +except OverflowDefect: + overflowDetected = true + +{.pop.} # overflow check + +doAssert(overflowDetected) + +block: # Overflow checks in a proc + var + a = high(int) + b = -2 + overflowDetected = false + + {.push overflowChecks: on.} + proc foo() = + let c = b - a + {.pop.} + + try: + foo() + except OverflowDefect: + overflowDetected = true + + doAssert(overflowDetected) + +block: # Overflow checks in a forward declared proc + var + a = high(int) + b = -2 + overflowDetected = false + + proc foo() + + {.push overflowChecks: on.} + proc foo() = + let c = b - a + {.pop.} + + try: + foo() + except OverflowDefect: + overflowDetected = true + + doAssert(overflowDetected) + +block: # Overflow checks doesn't affect fwd declaration + var + a = high(int) + b = -2 + overflowDetected = false + + {.push overflowChecks: on.} + proc foo() + {.pop.} + + proc foo() = + let c = b - a + + try: + foo() + except OverflowDefect: + overflowDetected = true + + doAssert(not overflowDetected) + + +echo "ok" diff --git a/tests/overflw/toverflw2.nim b/tests/overflow/toverflow2.nim similarity index 100% rename from tests/overflw/toverflw2.nim rename to tests/overflow/toverflow2.nim diff --git a/tests/overflw/toverflw.nim b/tests/overflow/toverflow_reorder.nim similarity index 96% rename from tests/overflw/toverflw.nim rename to tests/overflow/toverflow_reorder.nim index 164e16e5c..fcf7b0c82 100644 --- a/tests/overflw/toverflw.nim +++ b/tests/overflow/toverflow_reorder.nim @@ -1,3 +1,5 @@ +{.experimental: "codeReordering".} + discard """ output: "ok" cmd: "nim $target --overflowChecks:off $options $file" @@ -12,7 +14,7 @@ var overflowDetected = false try: - writeLine(stdout, b - a) + echo b - a except OverflowDefect: overflowDetected = true diff --git a/tests/overflw/tovfint.nim b/tests/overflow/tovfint.nim similarity index 100% rename from tests/overflw/tovfint.nim rename to tests/overflow/tovfint.nim diff --git a/tests/parser/t20922.nim b/tests/parser/t20922.nim index 01af9868f..110610fb1 100644 --- a/tests/parser/t20922.nim +++ b/tests/parser/t20922.nim @@ -1,28 +1,17 @@ discard """ - cmd: "nim check $options --verbosity:0 $file" + cmd: "nim check $options --verbosity:0 --hints:off $file" action: "reject" nimout: ''' -t20922.nim(37, 5) Error: expression expected, but found ':' +t20922.nim(26, 5) Error: expression expected, but found ':' +t20922.nim(34, 7) Error: expression expected, but found ':' +t20922.nim(35, 5) Error: ':' or '=' expected, but got 'keyword of' Error: in expression ' '+'': identifier expected, but found '' -t20922.nim(37, 7) Error: attempting to call undeclared routine: '' +t20922.nim(26, 7) Error: attempting to call undeclared routine: '' Error: in expression ' '+'': identifier expected, but found '' -t20922.nim(37, 7) Error: attempting to call undeclared routine: '' -t20922.nim(37, 7) Error: expression '' cannot be called -t20922.nim(37, 7) Error: expression '' has no type (or is ambiguous) -t20922.nim(37, 7) Error: VM problem: dest register is not set -t20922.nim(45, 7) Error: expression expected, but found ':' -t20922.nim(46, 5) Error: ':' or '=' expected, but got 'keyword of' -t20922.nim(45, 9) Error: undeclared identifier: 'x' -t20922.nim(45, 9) Error: expression 'x' has no type (or is ambiguous) -Error: in expression ' x': identifier expected, but found '' -t20922.nim(45, 9) Error: attempting to call undeclared routine: '' -Error: in expression ' x': identifier expected, but found '' -t20922.nim(45, 9) Error: attempting to call undeclared routine: '' -t20922.nim(45, 9) Error: expression '' cannot be called -t20922.nim(45, 9) Error: expression '' has no type (or is ambiguous) -t20922.nim(45, 9) Error: VM problem: dest register is not set -t20922.nim(33, 6) Hint: 'mapInstrToToken' is declared but not used [XDeclaredButNotUsed] -t20922.nim(43, 3) Hint: 'Foo' is declared but not used [XDeclaredButNotUsed] +t20922.nim(26, 7) Error: attempting to call undeclared routine: '' +t20922.nim(26, 7) Error: expression '' cannot be called +t20922.nim(26, 7) Error: expression '' has no type (or is ambiguous) +t20922.nim(26, 7) Error: VM problem: dest register is not set ''' """ # original test case issue #20922 diff --git a/tests/pragmas/tpush.nim b/tests/pragmas/tpush.nim index 5ecfe9704..f2779ea70 100644 --- a/tests/pragmas/tpush.nim +++ b/tests/pragmas/tpush.nim @@ -13,3 +13,15 @@ proc WarnMe() = x: int echo(x) +# bug #11852 +proc foo(x: string, y: int, res: int) = + {.push checks: off} + var a: ptr char = unsafeAddr(x[y]) + {.pop.} + if x.len > y: + doAssert ord(a[]) == 51 + else: + doAssert x.len + 48 == res + +foo("", 0, 48) +foo("abc", 40, 51) diff --git a/tests/pragmas/treorder.nim b/tests/pragmas/treorder.nim index 09a98ef6a..72e8808b3 100644 --- a/tests/pragmas/treorder.nim +++ b/tests/pragmas/treorder.nim @@ -1,4 +1,5 @@ discard """ +matrix: "--experimental:codeReordering" output:'''0 1 2 diff --git a/tests/proc/t19795.nim b/tests/proc/t19795.nim new file mode 100644 index 000000000..677ec0a63 --- /dev/null +++ b/tests/proc/t19795.nim @@ -0,0 +1,19 @@ +discard """ + matrix: "--mm:arc" +""" + +# bug #19795 +# bug #21085 + +type Vector = seq[int] + +var vect: Vector = newSeq[int](5) +doAssert vect == @[0, 0, 0, 0, 0] + +# Needed to get the problem. Could also use "var". +let vectCopy = vect + +# Then some procedure definition is needed to get the problem. +proc p(): int = 3 + +doAssert vect == @[0, 0, 0, 0, 0] \ No newline at end of file diff --git a/tests/tools/tlinter.nim b/tests/tools/tlinter.nim index a6d45ab3b..16f67905e 100644 --- a/tests/tools/tlinter.nim +++ b/tests/tools/tlinter.nim @@ -1,10 +1,10 @@ discard """ cmd: '''nim c --styleCheck:hint $file''' nimout: ''' +tlinter.nim(25, 1) Hint: 'tyPE' should be: 'type' [Name] tlinter.nim(21, 14) Hint: 'nosideeffect' should be: 'noSideEffect' [Name] tlinter.nim(21, 28) Hint: 'myown' should be: 'myOwn' [template declared in tlinter.nim(19, 9)] [Name] tlinter.nim(21, 35) Hint: 'inLine' should be: 'inline' [Name] -tlinter.nim(25, 1) Hint: 'tyPE' should be: 'type' [Name] tlinter.nim(23, 1) Hint: 'foO' should be: 'foo' [proc declared in tlinter.nim(21, 6)] [Name] tlinter.nim(27, 14) Hint: 'Foo_bar' should be: 'FooBar' [type declared in tlinter.nim(25, 6)] [Name] tlinter.nim(29, 6) Hint: 'someVAR' should be: 'someVar' [var declared in tlinter.nim(27, 5)] [Name] diff --git a/tests/trmacros/tor.nim b/tests/trmacros/tor.nim index 087dc0d68..6b4e71216 100644 --- a/tests/trmacros/tor.nim +++ b/tests/trmacros/tor.nim @@ -1,9 +1,19 @@ discard """ - output: '''0 + output: ''' +3 +0 true -3''' +''' """ + +# bug #798 +template t012{(0|1|2){x}}(x: untyped): untyped = x+1 +let z = 1 +# outputs 3 thanks to fixpoint iteration: +echo z + + template arithOps: untyped = (`+` | `-` | `*`) template testOr{ (arithOps{f})(a, b) }(a, b, f: untyped): untyped = f(a mod 10, b) @@ -20,9 +30,3 @@ var c = false a = b and a echo a - -# bug #798 -template t012{(0|1|2){x}}(x: untyped): untyped = x+1 -let z = 1 -# outputs 3 thanks to fixpoint iteration: -echo z From 7dc80a7fefb0d2cecbdc2696c71ea2fa94d6aa76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francis=20Th=C3=A9rien?= Date: Wed, 22 Feb 2023 15:10:02 -0500 Subject: [PATCH 3722/3768] Mitigate issues related to compiler options when cross-compiling (#21330) * Document C compiler options config when cross-compiling * Allow empty string to override default --- compiler/extccomp.nim | 4 +++- doc/nimc.md | 8 +++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/compiler/extccomp.nim b/compiler/extccomp.nim index db97de65c..45a531852 100644 --- a/compiler/extccomp.nim +++ b/compiler/extccomp.nim @@ -328,7 +328,9 @@ proc getConfigVar(conf: ConfigRef; c: TSystemCC, suffix: string): string = platform.OS[conf.target.targetOS].name & '.' & CC[c].name & fullSuffix result = getConfigVar(conf, fullCCname) - if result.len == 0: + if existsConfigVar(conf, fullCCname): + result = getConfigVar(conf, fullCCname) + else: # not overridden for this cross compilation setting? result = getConfigVar(conf, CC[c].name & fullSuffix) else: diff --git a/doc/nimc.md b/doc/nimc.md index 81a060d8a..7b0e99e06 100644 --- a/doc/nimc.md +++ b/doc/nimc.md @@ -316,14 +316,16 @@ Another way is to make Nim invoke a cross compiler toolchain: nim c --cpu:arm --os:linux myproject.nim ``` -For cross compilation, the compiler invokes a C compiler named -like `$cpu.$os.$cc` (for example arm.linux.gcc) and the configuration -system is used to provide meaningful defaults. For example for `ARM` your +For cross compilation, the compiler invokes a C compiler named like +`$cpu.$os.$cc` (for example `arm.linux.gcc`) with options defined in +`$cpu.$os.$cc.options.always`. The configuration system is used to provide +meaningful defaults. For example, for Linux on a 32-bit ARM CPU, your configuration file should contain something like: arm.linux.gcc.path = "/usr/bin" arm.linux.gcc.exe = "arm-linux-gcc" arm.linux.gcc.linkerexe = "arm-linux-gcc" + arm.linux.gcc.options.always = "-w -fmax-errors=3" Cross-compilation for Windows ============================= From 8a19ac20708c66fb3541533d55b60624cee56ab7 Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 24 Feb 2023 09:02:15 +0100 Subject: [PATCH 3723/3768] fixes #21393 and misc style changes (#21419) * fixes #21393 and misc style changes * progress --------- Co-authored-by: ringabout <43030857+ringabout@users.noreply.github.com> --- compiler/int128.nim | 39 +++++++++++++++-------------- compiler/sempass2.nim | 19 +++++++------- lib/pure/collections/tables.nim | 31 +++++++++++++++-------- lib/pure/hashes.nim | 5 +++- lib/pure/osproc.nim | 8 ++---- lib/std/private/digitsutils.nim | 4 +-- lib/std/private/ospaths2.nim | 2 +- lib/system/seqs_v2.nim | 3 ++- lib/system/strmantle.nim | 2 +- lib/windows/winlean.nim | 16 ++++++------ tests/effects/tstrictfuncs_misc.nim | 2 +- 11 files changed, 70 insertions(+), 61 deletions(-) diff --git a/compiler/int128.nim b/compiler/int128.nim index e357706b9..b0341eb37 100644 --- a/compiler/int128.nim +++ b/compiler/int128.nim @@ -33,26 +33,27 @@ template high*(t: typedesc[Int128]): Int128 = Max proc `$`*(a: Int128): string proc toInt128*[T: SomeInteger | bool](arg: T): Int128 = - when T is bool: result.sdata(0) = int32(arg) - elif T is SomeUnsignedInt: - when sizeof(arg) <= 4: - result.udata[0] = uint32(arg) + {.noSideEffect.}: + when T is bool: result.sdata(0) = int32(arg) + elif T is SomeUnsignedInt: + when sizeof(arg) <= 4: + result.udata[0] = uint32(arg) + else: + result.udata[0] = uint32(arg and T(0xffffffff)) + result.udata[1] = uint32(arg shr 32) + elif sizeof(arg) <= 4: + result.sdata(0) = int32(arg) + if arg < 0: # sign extend + result.sdata(1) = -1 + result.sdata(2) = -1 + result.sdata(3) = -1 else: - result.udata[0] = uint32(arg and T(0xffffffff)) - result.udata[1] = uint32(arg shr 32) - elif sizeof(arg) <= 4: - result.sdata(0) = int32(arg) - if arg < 0: # sign extend - result.sdata(1) = -1 - result.sdata(2) = -1 - result.sdata(3) = -1 - else: - let tmp = int64(arg) - result.udata[0] = uint32(tmp and 0xffffffff) - result.sdata(1) = int32(tmp shr 32) - if arg < 0: # sign extend - result.sdata(2) = -1 - result.sdata(3) = -1 + let tmp = int64(arg) + result.udata[0] = uint32(tmp and 0xffffffff) + result.sdata(1) = int32(tmp shr 32) + if arg < 0: # sign extend + result.sdata(2) = -1 + result.sdata(3) = -1 template isNegative(arg: Int128): bool = arg.sdata(3) < 0 diff --git a/compiler/sempass2.nim b/compiler/sempass2.nim index e989eb4a6..396ea6606 100644 --- a/compiler/sempass2.nim +++ b/compiler/sempass2.nim @@ -822,9 +822,6 @@ proc checkForSink(tracked: PEffects; n: PNode) = if tracked.inIfStmt == 0 and optSinkInference in tracked.config.options: checkForSink(tracked.config, tracked.c.idgen, tracked.owner, n) -proc strictFuncsActive(tracked: PEffects): bool {.inline.} = - sfNoSideEffect in tracked.owner.flags and strictFuncs in tracked.c.features and not tracked.inEnforcedNoSideEffects - proc trackCall(tracked: PEffects; n: PNode) = template gcsafeAndSideeffectCheck() = if notGcSafe(op) and not importedFromC(a): @@ -934,9 +931,11 @@ proc trackCall(tracked: PEffects; n: PNode) = # initialized until after the call. Since we do this after we analysed the # call, this is fine. initVar(tracked, n[i].skipAddr, false) - if tracked.strictFuncsActive and isDangerousLocation(n[i].skipAddr, tracked.owner): - localError(tracked.config, n[i].info, - "cannot pass $1 to `var T` parameter within a strict func" % renderTree(n[i])) + if strictFuncs in tracked.c.features and not tracked.inEnforcedNoSideEffects and + isDangerousLocation(n[i].skipAddr, tracked.owner): + if sfNoSideEffect in tracked.owner.flags: + localError(tracked.config, n[i].info, + "cannot pass $1 to `var T` parameter within a strict func" % renderTree(n[i])) tracked.hasSideEffect = true else: discard @@ -1090,10 +1089,12 @@ proc track(tracked: PEffects, n: PNode) = createTypeBoundOps(tracked, n[0].typ, n.info) if n[0].kind != nkSym or not isLocalSym(tracked, n[0].sym): checkForSink(tracked, n[1]) - if tracked.strictFuncsActive and isDangerousLocation(n[0], tracked.owner): + if strictFuncs in tracked.c.features and not tracked.inEnforcedNoSideEffects and + isDangerousLocation(n[0], tracked.owner): tracked.hasSideEffect = true - localError(tracked.config, n[0].info, - "cannot mutate location $1 within a strict func" % renderTree(n[0])) + if sfNoSideEffect in tracked.owner.flags: + localError(tracked.config, n[0].info, + "cannot mutate location $1 within a strict func" % renderTree(n[0])) of nkVarSection, nkLetSection: for child in n: let last = lastSon(child) diff --git a/lib/pure/collections/tables.nim b/lib/pure/collections/tables.nim index 57a0107fd..4737fa478 100644 --- a/lib/pure/collections/tables.nim +++ b/lib/pure/collections/tables.nim @@ -447,7 +447,7 @@ proc mgetOrPut*[A, B](t: var Table[A, B], key: A, val: B): var B = ## ## ## Note that while the value returned is of type `var B`, - ## it is easy to accidentally create an copy of the value at `t[key]`. + ## it is easy to accidentally create a copy of the value at `t[key]`. ## Remember that seqs and strings are value types, and therefore ## cannot be copied into a separate variable for modification. ## See the example below. @@ -828,7 +828,8 @@ proc newTable*[A, B](initialSize = defaultInitialSize): TableRef[A, B] = b = newTable[char, seq[int]]() new(result) - result[] = initTable[A, B](initialSize) + {.noSideEffect.}: + result[] = initTable[A, B](initialSize) proc newTable*[A, B](pairs: openArray[(A, B)]): TableRef[A, B] = ## Creates a new ref hash table that contains the given `pairs`. @@ -844,14 +845,16 @@ proc newTable*[A, B](pairs: openArray[(A, B)]): TableRef[A, B] = assert b == {'a': 5, 'b': 9}.newTable new(result) - result[] = toTable[A, B](pairs) + {.noSideEffect.}: + result[] = toTable[A, B](pairs) proc newTableFrom*[A, B, C](collection: A, index: proc(x: B): C): TableRef[C, B] = ## Index the collection with the proc provided. # TODO: As soon as supported, change collection: A to collection: A[B] result = newTable[C, B]() - for item in collection: - result[index(item)] = item + {.noSideEffect.}: + for item in collection: + result[index(item)] = item proc `[]`*[A, B](t: TableRef[A, B], key: A): var B = ## Retrieves the value at `t[key]`. @@ -1825,7 +1828,8 @@ proc newOrderedTable*[A, B](initialSize = defaultInitialSize): OrderedTableRef[A a = newOrderedTable[int, string]() b = newOrderedTable[char, seq[int]]() new(result) - result[] = initOrderedTable[A, B](initialSize) + {.noSideEffect.}: + result[] = initOrderedTable[A, B](initialSize) proc newOrderedTable*[A, B](pairs: openArray[(A, B)]): OrderedTableRef[A, B] = ## Creates a new ordered ref hash table that contains the given `pairs`. @@ -1842,7 +1846,8 @@ proc newOrderedTable*[A, B](pairs: openArray[(A, B)]): OrderedTableRef[A, B] = assert b == {'a': 5, 'b': 9}.newOrderedTable result = newOrderedTable[A, B](pairs.len) - for key, val in items(pairs): result[key] = val + {.noSideEffect.}: + for key, val in items(pairs): result[key] = val proc `[]`*[A, B](t: OrderedTableRef[A, B], key: A): var B = @@ -2641,13 +2646,15 @@ proc newCountTable*[A](initialSize = defaultInitialSize): CountTableRef[A] = ## * `initCountTable proc<#initCountTable>`_ for creating a ## `CountTable` new(result) - result[] = initCountTable[A](initialSize) + {.noSideEffect.}: + result[] = initCountTable[A](initialSize) proc newCountTable*[A](keys: openArray[A]): CountTableRef[A] = ## Creates a new ref count table with every member of a container `keys` ## having a count of how many times it occurs in that container. result = newCountTable[A](keys.len) - for key in items(keys): result.inc(key) + {.noSideEffect.}: + for key in items(keys): result.inc(key) proc `[]`*[A](t: CountTableRef[A], key: A): int = ## Retrieves the value at `t[key]` if `key` is in `t`. @@ -2671,7 +2678,8 @@ proc `[]=`*[A](t: CountTableRef[A], key: A, val: int) = ## * `inc proc<#inc,CountTableRef[A],A,int>`_ for incrementing a ## value of a key assert val > 0 - t[][key] = val + {.noSideEffect.}: + t[][key] = val proc inc*[A](t: CountTableRef[A], key: A, val = 1) = ## Increments `t[key]` by `val` (default: 1). @@ -2680,7 +2688,8 @@ proc inc*[A](t: CountTableRef[A], key: A, val = 1) = a.inc('a') a.inc('b', 10) doAssert a == newCountTable("aaabbbbbbbbbbb") - t[].inc(key, val) + {.noSideEffect.}: + t[].inc(key, val) proc smallest*[A](t: CountTableRef[A]): tuple[key: A, val: int] = ## Returns the `(key, value)` pair with the smallest `val`. Efficiency: O(n) diff --git a/lib/pure/hashes.nim b/lib/pure/hashes.nim index 09e072812..4ae4938c6 100644 --- a/lib/pure/hashes.nim +++ b/lib/pure/hashes.nim @@ -247,7 +247,7 @@ proc hash*[T](x: ptr[T]): Hash {.inline.} = when defined(nimPreviewHashRef) or defined(nimdoc): proc hash*[T](x: ref[T]): Hash {.inline.} = ## Efficient `hash` overload. - ## + ## ## .. important:: Use `-d:nimPreviewHashRef` to ## enable hashing `ref`s. It is expected that this behavior ## becomes the new default in upcoming versions. @@ -536,6 +536,7 @@ proc hash*[T: tuple | object | proc](x: T): Hash = elif T is (proc): result = hash(pointer(x)) else: + result = 0 for f in fields(x): result = result !& hash(f) result = !$result @@ -551,6 +552,7 @@ proc hash*[A](x: openArray[A]): Hash = else: result = murmurHash(toOpenArrayByte(x, 0, x.high)) else: + result = 0 for a in x: result = result !& hash(a) result = !$result @@ -583,6 +585,7 @@ proc hash*[A](aBuf: openArray[A], sPos, ePos: int): Hash = proc hash*[A](x: set[A]): Hash = ## Efficient hashing of sets. ## There must be a `hash` proc defined for the element type `A`. + result = 0 for it in items(x): result = result !& hash(it) result = !$result diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 9a0e5895d..bb6abc571 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -570,12 +570,8 @@ when defined(windows) and not defined(useNimRtl): if a == 0: raiseOSError(osLastError()) proc newFileHandleStream(handle: Handle): owned FileHandleStream = - new(result) - result.handle = handle - result.closeImpl = hsClose - result.atEndImpl = hsAtEnd - result.readDataImpl = hsReadData - result.writeDataImpl = hsWriteData + result = FileHandleStream(handle: handle, closeImpl: hsClose, atEndImpl: hsAtEnd, + readDataImpl: hsReadData, writeDataImpl: hsWriteData) proc buildCommandLine(a: string, args: openArray[string]): string = result = quoteShell(a) diff --git a/lib/std/private/digitsutils.nim b/lib/std/private/digitsutils.nim index b3dc5d14f..e051e5218 100644 --- a/lib/std/private/digitsutils.nim +++ b/lib/std/private/digitsutils.nim @@ -101,9 +101,7 @@ proc addInt*(result: var string; x: int64) {.enforceNoRaises.} = num = cast[uint64](x) else: num = uint64(-x) - let base = result.len - setLen(result, base + 1) - result[base] = '-' + result.add '-' else: num = uint64(x) addInt(result, num) diff --git a/lib/std/private/ospaths2.nim b/lib/std/private/ospaths2.nim index 5e3bece68..78a806675 100644 --- a/lib/std/private/ospaths2.nim +++ b/lib/std/private/ospaths2.nim @@ -571,7 +571,7 @@ proc normExt(ext: string): string = proc searchExtPos*(path: string): int = ## Returns index of the `'.'` char in `path` if it signifies the beginning - ## of extension. Returns -1 otherwise. + ## of the file extension. Returns -1 otherwise. ## ## See also: ## * `splitFile proc`_ diff --git a/lib/system/seqs_v2.nim b/lib/system/seqs_v2.nim index 50f23111c..d7ace446d 100644 --- a/lib/system/seqs_v2.nim +++ b/lib/system/seqs_v2.nim @@ -83,7 +83,8 @@ proc shrink*[T](x: var seq[T]; newLen: Natural) {.tags: [], raises: [].} = for i in countdown(x.len - 1, newLen): reset x[i] # XXX This is wrong for const seqs that were moved into 'x'! - cast[ptr NimSeqV2[T]](addr x).len = newLen + {.noSideEffect.}: + cast[ptr NimSeqV2[T]](addr x).len = newLen proc grow*[T](x: var seq[T]; newLen: Natural; value: T) = let oldLen = x.len diff --git a/lib/system/strmantle.nim b/lib/system/strmantle.nim index f4733ed21..ab158d6b7 100644 --- a/lib/system/strmantle.nim +++ b/lib/system/strmantle.nim @@ -33,7 +33,7 @@ proc eqStrings(a, b: string): bool {.inline, compilerproc.} = proc hashString(s: string): int {.compilerproc.} = # the compiler needs exactly the same hash function! # this used to be used for efficient generation of string case statements - var h : uint = 0 + var h = 0'u for i in 0..len(s)-1: h = h + uint(s[i]) h = h + h shl 10 diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index 93ab3dd2e..230f963f0 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -48,12 +48,12 @@ type HGLRC* = Handle BYTE* = uint8 - SECURITY_ATTRIBUTES* {.final, pure.} = object + SECURITY_ATTRIBUTES* = object nLength*: int32 lpSecurityDescriptor*: pointer bInheritHandle*: WINBOOL - STARTUPINFO* {.final, pure.} = object + STARTUPINFO* = object cb*: int32 lpReserved*: cstring lpDesktop*: cstring @@ -73,17 +73,17 @@ type hStdOutput*: Handle hStdError*: Handle - PROCESS_INFORMATION* {.final, pure.} = object + PROCESS_INFORMATION* = object hProcess*: Handle hThread*: Handle dwProcessId*: int32 dwThreadId*: int32 - FILETIME* {.final, pure.} = object ## CANNOT BE int64 BECAUSE OF ALIGNMENT + FILETIME* = object ## CANNOT BE int64 BECAUSE OF ALIGNMENT dwLowDateTime*: DWORD dwHighDateTime*: DWORD - BY_HANDLE_FILE_INFORMATION* {.final, pure.} = object + BY_HANDLE_FILE_INFORMATION* = object dwFileAttributes*: DWORD ftCreationTime*: FILETIME ftLastAccessTime*: FILETIME @@ -95,7 +95,7 @@ type nFileIndexHigh*: DWORD nFileIndexLow*: DWORD - OSVERSIONINFO* {.final, pure.} = object + OSVERSIONINFO* = object dwOSVersionInfoSize*: DWORD dwMajorVersion*: DWORD dwMinorVersion*: DWORD @@ -820,7 +820,7 @@ type POVERLAPPED_COMPLETION_ROUTINE* = proc (para1: DWORD, para2: DWORD, para3: POVERLAPPED){.stdcall.} - GUID* {.final, pure.} = object + GUID* = object D1*: int32 D2*: int16 D3*: int16 @@ -1101,7 +1101,7 @@ proc wsaResetEvent*(hEvent: Handle): bool {.stdcall, importc: "WSAResetEvent", dynlib: "ws2_32.dll".} type - KEY_EVENT_RECORD* {.final, pure.} = object + KEY_EVENT_RECORD* = object eventType*: int16 bKeyDown*: WINBOOL wRepeatCount*: int16 diff --git a/tests/effects/tstrictfuncs_misc.nim b/tests/effects/tstrictfuncs_misc.nim index 59d850763..8c573bb3a 100644 --- a/tests/effects/tstrictfuncs_misc.nim +++ b/tests/effects/tstrictfuncs_misc.nim @@ -53,7 +53,7 @@ type JsonNode3 = ref object fields: MyTable -proc `[]`(t: var MyTable, key: string): var int = +proc `[]`(t: MyTable, key: string): int = result = t.data[0] proc `[]`(x: JsonNode3, key: string): int = From bbb6d2c69d1b2bca9987c708ebc6ffd2479bb822 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 24 Feb 2023 16:02:44 +0800 Subject: [PATCH 3724/3768] fixes #20695; fixes object with distinct defaults and tables (#21428) --- compiler/semtypes.nim | 4 ++- tests/objects/tobject_default_value.nim | 39 ++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/compiler/semtypes.nim b/compiler/semtypes.nim index aaffa1ea7..26493703d 100644 --- a/compiler/semtypes.nim +++ b/compiler/semtypes.nim @@ -222,9 +222,11 @@ proc isRecursiveType(t: PType, cycleDetector: var IntSet): bool = proc fitDefaultNode(c: PContext, n: PNode): PType = let expectedType = if n[^2].kind != nkEmpty: semTypeNode(c, n[^2], nil) else: nil + let oldType = n[^1].typ n[^1] = semConstExpr(c, n[^1], expectedType = expectedType) + n[^1].flags.incl nfSem if n[^2].kind != nkEmpty: - if expectedType != nil: + if expectedType != nil and oldType != expectedType: n[^1] = fitNodeConsiderViewType(c, expectedType, n[^1], n[^1].info) result = n[^1].typ else: diff --git a/tests/objects/tobject_default_value.nim b/tests/objects/tobject_default_value.nim index cdc6016e0..150fb0876 100644 --- a/tests/objects/tobject_default_value.nim +++ b/tests/objects/tobject_default_value.nim @@ -3,7 +3,7 @@ discard """ targets: "c cpp js" """ -import std/[times, macros] +import std/[times, macros, tables] type Guess = object @@ -236,6 +236,7 @@ template main {.dirty.} = doAssert x.obj.name.scale == 1 when nimvm: + # todo discard "fixme" else: when defined(gcArc) or defined(gcOrc): @@ -523,5 +524,41 @@ template main {.dirty.} = discard oToEither(O()) + block: # bug #20695 + type + Default = object + tabs: Table[string, int] = initTable[string, int]() + + let d = default(Default) + doAssert d.tabs.len == 0 + + block: + type + Default = object + tabs: Table[string, int] = Table[string, int]() + + let d = default(Default) + doAssert d.tabs.len == 0 + + + block: + type DjangoDateTime = distinct DateTime + + type Default = object + data: DjangoDateTime = DjangoDateTime(DateTime()) + + let x = default(Default) + doAssert x.data is DjangoDateTime + + block: + type DjangoDateTime = distinct DateTime + + type Default = object + data = DjangoDateTime(DateTime()) + + let x = default(Default) + doAssert x.data is DjangoDateTime + + static: main() main() From 7837e57e93b1cb89f5a80a779e853bad070de777 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francis=20Th=C3=A9rien?= Date: Fri, 24 Feb 2023 03:07:35 -0500 Subject: [PATCH 3725/3768] Disable threads for os:any (#21425) --- config/nim.cfg | 5 +++++ lib/std/typedthreads.nim | 2 ++ 2 files changed, 7 insertions(+) diff --git a/config/nim.cfg b/config/nim.cfg index 973de077a..ef93ec99e 100644 --- a/config/nim.cfg +++ b/config/nim.cfg @@ -100,6 +100,11 @@ nimblepath="$home/.nimble/pkgs/" gcc.options.always %= "${gcc.options.always} -fsanitize=null -fsanitize-undefined-trap-on-error" @end +# Turn off threads support when compiling for bare-metal targets (--os:any) +@if any: + threads:off +@end + @if unix and mingw: # Cross compile for Windows from Linux/OSX using MinGW i386.windows.gcc.exe = "i686-w64-mingw32-gcc" diff --git a/lib/std/typedthreads.nim b/lib/std/typedthreads.nim index b45ce8a61..8359ca353 100644 --- a/lib/std/typedthreads.nim +++ b/lib/std/typedthreads.nim @@ -45,6 +45,8 @@ when defined(nimPreviewSlimSystem): when defined(genode): import genode/env +when hostOS == "any": + {.error: "Threads not implemented for os:any. Please compile with --threads:off.".} when hasAllocStack or defined(zephyr) or defined(freertos) or defined(nuttx) or defined(cpu16) or defined(cpu8): From d4782c9e42ff6cee9f674a376b1595583e08c2a3 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Sat, 25 Feb 2023 00:53:04 +0800 Subject: [PATCH 3726/3768] closes #17864; add a test case (#21434) --- tests/vm/tmisc_vm.nim | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/vm/tmisc_vm.nim b/tests/vm/tmisc_vm.nim index af09e8871..1ad830b5f 100644 --- a/tests/vm/tmisc_vm.nim +++ b/tests/vm/tmisc_vm.nim @@ -445,3 +445,15 @@ static: needSecondIdentifier.mgetOrPut(firstPart, @[]).add((name, pattern)) doAssert needSecondIdentifier[0] == @[("aaaa", "bbbb"), ("aaaaa", "bbbbb"), ("aaaaaa", "bbbbbb"), ("aaaaaaa", "bbbbbbb"), ("aaaaaaaa", "bbbbb")] + +# bug #17864 +macro transform*(fn: typed) = + quote do: + `fn` + +var map: Table[string, HashSet[string]] +proc publish*(): void {.transform.} = + map["k"] = init_hash_set[string]() + map["k"].incl "d" + +publish() From b2edfe7a02f49f07eb9fcf71cd500215725c217e Mon Sep 17 00:00:00 2001 From: Dmitry Arkhipenko <36101416+dkgitdev@users.noreply.github.com> Date: Sat, 25 Feb 2023 13:47:19 +0300 Subject: [PATCH 3727/3768] Fix: nintendoswitch compilation (#21368) * Fix: make nintendoswitch someGcc, remove symlink support for nintendoswitch, add getAppFilename for nintendoswitch * Fix: use getApplHeuristic on nintendoswitch --- lib/posix/posix.nim | 8 ++++++-- lib/pure/os.nim | 4 +++- lib/std/private/ossymlinks.nim | 2 +- lib/std/sysatomics.nim | 2 +- 4 files changed, 11 insertions(+), 5 deletions(-) diff --git a/lib/posix/posix.nim b/lib/posix/posix.nim index 07652c64d..12fc8fd57 100644 --- a/lib/posix/posix.nim +++ b/lib/posix/posix.nim @@ -584,7 +584,8 @@ proc pread*(a1: cint, a2: pointer, a3: int, a4: Off): int {. proc pwrite*(a1: cint, a2: pointer, a3: int, a4: Off): int {. importc, header: "".} proc read*(a1: cint, a2: pointer, a3: int): int {.importc, header: "".} -proc readlink*(a1, a2: cstring, a3: int): int {.importc, header: "".} +when not defined(nintendoswitch): + proc readlink*(a1, a2: cstring, a3: int): int {.importc, header: "".} proc ioctl*(f: FileHandle, device: uint): int {.importc: "ioctl", header: "", varargs, tags: [WriteIOEffect].} ## A system call for device-specific input/output operations and other @@ -603,7 +604,10 @@ proc setsid*(): Pid {.importc, header: "".} proc setuid*(a1: Uid): cint {.importc, header: "".} proc sleep*(a1: cint): cint {.importc, header: "".} proc swab*(a1, a2: pointer, a3: int) {.importc, header: "".} -proc symlink*(a1, a2: cstring): cint {.importc, header: "".} +when not defined(nintendoswitch): + proc symlink*(a1, a2: cstring): cint {.importc, header: "".} +else: + proc symlink*(a1, a2: cstring): cint = -1 proc sync*() {.importc, header: "".} proc sysconf*(a1: cint): int {.importc, header: "".} proc tcgetpgrp*(a1: cint): Pid {.importc, header: "".} diff --git a/lib/pure/os.nim b/lib/pure/os.nim index ecceed671..3285c07df 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -254,7 +254,7 @@ proc findExe*(exe: string, followSymlinks: bool = true; for ext in extensions: var x = addFileExt(x, ext) if fileExists(x): - when not defined(windows): + when not (defined(windows) or defined(nintendoswitch)): while followSymlinks: # doubles as if here if x.symlinkExists: var r = newString(maxSymlinkLen) @@ -702,6 +702,8 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noW result = getApplHaiku() elif defined(openbsd): result = getApplOpenBsd() + elif defined(nintendoswitch): + result = "" # little heuristic that may work on other POSIX-like systems: if result.len == 0: diff --git a/lib/std/private/ossymlinks.nim b/lib/std/private/ossymlinks.nim index 53f59f939..6b2de6237 100644 --- a/lib/std/private/ossymlinks.nim +++ b/lib/std/private/ossymlinks.nim @@ -67,7 +67,7 @@ proc expandSymlink*(symlinkPath: string): string {.noWeirdTarget.} = ## ## See also: ## * `createSymlink proc`_ - when defined(windows): + when defined(windows) or defined(nintendoswitch): result = symlinkPath else: result = newString(maxSymlinkLen) diff --git a/lib/std/sysatomics.nim b/lib/std/sysatomics.nim index b7ccb4092..36a4e5537 100644 --- a/lib/std/sysatomics.nim +++ b/lib/std/sysatomics.nim @@ -15,7 +15,7 @@ when defined(nimPreviewSlimSystem): const hasThreadSupport = compileOption("threads") and not defined(nimscript) -const someGcc = defined(gcc) or defined(llvm_gcc) or defined(clang) +const someGcc = defined(gcc) or defined(llvm_gcc) or defined(clang) or defined(nintendoswitch) const someVcc = defined(vcc) or defined(clang_cl) type From 425225119a383511ac6f84ba5c02a691e6c51aea Mon Sep 17 00:00:00 2001 From: PhilippMDoerner Date: Sun, 26 Feb 2023 09:57:51 +0100 Subject: [PATCH 3728/3768] Add mention of breaking change about `[_]` in generics to changelog (#21437) Add mention of breaking change to changelog As described by #21435 , generic proc declarations making use of `_` as generic parameter are no longer possible. This is a change that was introduced by #21192 . During the debate over if this should be part of the intended spec in #21435 we concluded that it is and thus should be mentioned as a breaking change in the changelogs. --- changelogs/changelog_2_0_0.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/changelogs/changelog_2_0_0.md b/changelogs/changelog_2_0_0.md index c8427cc2a..e081865be 100644 --- a/changelogs/changelog_2_0_0.md +++ b/changelogs/changelog_2_0_0.md @@ -149,6 +149,13 @@ proc foo(_, _: int): int = 123 echo foo(1, 2) ``` +- Underscores (`_`) as generic parameters are not supported and cannot be used. + Generics that use `_` as parameters will no longer compile requires you to replace `_` with something else: + + ```nim + proc foo[_](t: typedesc[_]): string = "BAR" # Can not compile + proc foo[T](t: typedesc[T]): string = "BAR" # Can compile + ``` - - Added the `--legacy:verboseTypeMismatch` switch to get legacy type mismatch error messages. From 4ae598762e7dac1886b481a48ae0875843e5153f Mon Sep 17 00:00:00 2001 From: Constantine Molchanov Date: Mon, 27 Feb 2023 02:56:43 +0300 Subject: [PATCH 3729/3768] fixes #21439; Add tyOpenArray to genTypeInfo. (#21440) * fixes #21439; Add tyOpenArray to genTypeInfo. * Add test. --- compiler/jstypes.nim | 2 +- tests/js/t21439.nim | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 tests/js/t21439.nim diff --git a/compiler/jstypes.nim b/compiler/jstypes.nim index 56d075af7..4b4ca9fe7 100644 --- a/compiler/jstypes.nim +++ b/compiler/jstypes.nim @@ -133,7 +133,7 @@ proc genTypeInfo(p: PProc, typ: PType): Rope = "var $1 = {size: 0,kind: $2,base: null,node: null,finalizer: null};$n" % [result, rope(ord(t.kind))] prepend(p.g.typeInfo, s) - of tyVar, tyLent, tyRef, tyPtr, tySequence, tyRange, tySet: + of tyVar, tyLent, tyRef, tyPtr, tySequence, tyRange, tySet, tyOpenArray: var s = "var $1 = {size: 0, kind: $2, base: null, node: null, finalizer: null};$n" % [result, rope(ord(t.kind))] diff --git a/tests/js/t21439.nim b/tests/js/t21439.nim new file mode 100644 index 000000000..972356cd0 --- /dev/null +++ b/tests/js/t21439.nim @@ -0,0 +1,14 @@ +discard """ + action: "compile" +""" + +proc test(a: openArray[string]): proc = + result = proc = + for i in a: + discard i + + +const a = ["t1", "t2"] + +discard test(a) + From 89a60939f8b0c60e8abf63b698491a8b138da772 Mon Sep 17 00:00:00 2001 From: Ikko Eltociear Ashimine Date: Mon, 27 Feb 2023 08:57:02 +0900 Subject: [PATCH 3730/3768] Fix typo in sourcemap.nim (#21438) seperated -> separated --- compiler/sourcemap.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/sourcemap.nim b/compiler/sourcemap.nim index 2245250ac..2fcc50bbe 100644 --- a/compiler/sourcemap.nim +++ b/compiler/sourcemap.nim @@ -164,7 +164,7 @@ func toSourceMap*(info: SourceInfo, file: string): SourceMap {.raises: [].} = result.names = info.names # Convert nodes into mappings. # Mappings are split into blocks where each block referes to a line in the outputted JS. - # Blocks can be seperated into statements which refere to tokens on the line. + # Blocks can be separated into statements which refere to tokens on the line. # Since the mappings depend on previous values we need to # keep track of previous file, name, etc var From 6fea221d65c999ef7923bf51bbdaa8421cb2e9df Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 27 Feb 2023 07:57:36 +0800 Subject: [PATCH 3731/3768] Overrides `=copy` for `PackedSets` (#21417) --- lib/std/packedsets.nim | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/lib/std/packedsets.nim b/lib/std/packedsets.nim index 1e2892658..e8e03af9e 100644 --- a/lib/std/packedsets.nim +++ b/lib/std/packedsets.nim @@ -12,11 +12,6 @@ ## ## Supports any Ordinal type. ## -## .. note:: Currently the assignment operator `=` for `PackedSet[A]` -## performs some rather meaningless shallow copy. Since Nim currently does -## not allow the assignment operator to be overloaded, use the `assign proc -## <#assign,PackedSet[A],PackedSet[A]>`_ to get a deep copy. -## ## See also ## ======== ## * `sets module `_ for more general hash sets @@ -412,18 +407,9 @@ proc isNil*[A](x: PackedSet[A]): bool {.inline.} = x.head.isNil and x.elems == 0 -proc assign*[A](dest: var PackedSet[A], src: PackedSet[A]) = +proc `=copy`*[A](dest: var PackedSet[A], src: PackedSet[A]) = ## Copies `src` to `dest`. ## `dest` does not need to be initialized by the `initPackedSet proc <#initPackedSet>`_. - runnableExamples: - var - a = initPackedSet[int]() - b = initPackedSet[int]() - b.incl(5) - b.incl(7) - a.assign(b) - assert len(a) == 2 - if src.elems <= src.a.len: dest.data = @[] dest.max = 0 @@ -452,6 +438,19 @@ proc assign*[A](dest: var PackedSet[A], src: PackedSet[A]) = dest.data[h] = n it = it.next +proc assign*[A](dest: var PackedSet[A], src: PackedSet[A]) {.inline, deprecated.} = + ## Copies `src` to `dest`. + ## `dest` does not need to be initialized by the `initPackedSet proc <#initPackedSet>`_. + runnableExamples: + var + a = initPackedSet[int]() + b = initPackedSet[int]() + b.incl(5) + b.incl(7) + a.assign(b) + assert len(a) == 2 + `=copy`(dest, src) + proc union*[A](s1, s2: PackedSet[A]): PackedSet[A] = ## Returns the union of the sets `s1` and `s2`. ## From 83e33207250643dedc541e44ecf32d58f3edd097 Mon Sep 17 00:00:00 2001 From: Antonis Geralis <43617260+planetis-m@users.noreply.github.com> Date: Tue, 28 Feb 2023 15:17:21 +0200 Subject: [PATCH 3732/3768] Use a cast to suppress KeyError raises (#21451) --- lib/pure/json.nim | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/pure/json.nim b/lib/pure/json.nim index e881fbff7..d91da3545 100644 --- a/lib/pure/json.nim +++ b/lib/pure/json.nim @@ -465,11 +465,12 @@ proc `==`*(a, b: JsonNode): bool {.noSideEffect.} = if a.fields.len != b.fields.len: return false for key, val in a.fields: if not b.fields.hasKey(key): return false - when defined(nimHasEffectsOf): - {.noSideEffect.}: + {.cast(raises: []).}: + when defined(nimHasEffectsOf): + {.noSideEffect.}: + if b.fields[key] != val: return false + else: if b.fields[key] != val: return false - else: - if b.fields[key] != val: return false result = true proc hash*(n: OrderedTable[string, JsonNode]): Hash {.noSideEffect.} From dd629c8f450aa6bc871b54e4e549ad5fea2979a3 Mon Sep 17 00:00:00 2001 From: Century Systems Date: Tue, 28 Feb 2023 22:38:50 +0900 Subject: [PATCH 3733/3768] asyncdispatch: for NuttX, add destructor to clear global dispatcher. (#21432) * asyncdispatch: for NuttX, add destructor to clear global dispatcher using atexit(). Signed-off-by: Takeyoshi Kikuchi * std: exitprocs: remove "when defined(nuttx)" block. Signed-off-by: Takeyoshi Kikuchi --------- Signed-off-by: Takeyoshi Kikuchi --- lib/pure/asyncdispatch.nim | 13 ++++++++++++- lib/std/exitprocs.nim | 1 + 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/lib/pure/asyncdispatch.nim b/lib/pure/asyncdispatch.nim index 2bfa8587c..63062d8da 100644 --- a/lib/pure/asyncdispatch.nim +++ b/lib/pure/asyncdispatch.nim @@ -1216,6 +1216,15 @@ else: var gDisp{.threadvar.}: owned PDispatcher ## Global dispatcher + when defined(nuttx): + import std/exitprocs + + proc cleanDispatcher() {.noconv.} = + gDisp = nil + + proc addFinalyzer() = + addExitProc(cleanDispatcher) + proc setGlobalDispatcher*(disp: owned PDispatcher) = if not gDisp.isNil: assert gDisp.callbacks.len == 0 @@ -1225,6 +1234,8 @@ else: proc getGlobalDispatcher*(): PDispatcher = if gDisp.isNil: setGlobalDispatcher(newDispatcher()) + when defined(nuttx): + addFinalyzer() result = gDisp proc getIoHandler*(disp: PDispatcher): Selector[AsyncData] = @@ -1591,7 +1602,7 @@ else: owned(Future[tuple[address: string, client: AsyncFD]]) = var retFuture = newFuture[tuple[address: string, client: AsyncFD]]("acceptAddr") - proc cb(sock: AsyncFD): bool = + proc cb(sock: AsyncFD): bool {.gcsafe.} = result = true var sockAddress: Sockaddr_storage var addrLen = sizeof(sockAddress).SockLen diff --git a/lib/std/exitprocs.nim b/lib/std/exitprocs.nim index d63c1abc5..48b4fca7f 100644 --- a/lib/std/exitprocs.nim +++ b/lib/std/exitprocs.nim @@ -45,6 +45,7 @@ proc callClosures() {.noconv.} = case fun.kind of kClosure: fun.fun1() of kNoconv: fun.fun2() + gFuns.setLen(0) template fun() = if gFuns.len == 0: From 39d0a93d0e0bc2ebf24a9801fe49cdb71c085527 Mon Sep 17 00:00:00 2001 From: Constantine Molchanov Date: Wed, 1 Mar 2023 05:30:38 +0300 Subject: [PATCH 3734/3768] Fix #21452; enable Norm in important packages. (#21455) * fix #21452; enable Norm in important packages. * Run test that doesn't require SQLite. --- testament/important_packages.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testament/important_packages.nim b/testament/important_packages.nim index b3a4bfb82..0af0eb93e 100644 --- a/testament/important_packages.nim +++ b/testament/important_packages.nim @@ -119,7 +119,7 @@ pkg "nimterop", "nimble minitest" pkg "nimwc", "nim c nimwc.nim", allowFailure = true pkg "nimx", "nim c --threads:on test/main.nim", allowFailure = true pkg "nitter", "nim c src/nitter.nim", "https://github.com/zedeus/nitter" -pkg "norm", "testament r tests/sqlite/trows.nim", allowFailure = true +pkg "norm", "testament r tests/common/tmodel.nim" pkg "npeg", "nimble testarc" pkg "numericalnim", "nimble nimCI" pkg "optionsutils" From c4dd0c43016faadbdaaddb2decf95dc881eb772e Mon Sep 17 00:00:00 2001 From: heterodoxic <122719743+heterodoxic@users.noreply.github.com> Date: Wed, 1 Mar 2023 17:17:05 +0100 Subject: [PATCH 3735/3768] suggestion for a simple fix for #21279 (#21378) --- compiler/cgen.nim | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index 60623b72d..c346a0b78 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -480,6 +480,9 @@ proc resetLoc(p: BProc, loc: var TLoc) = # on the bytes following the m_type field? genObjectInit(p, cpsStmts, loc.t, loc, constructObj) +proc isOrHasImportedCppType(typ: PType): bool = + searchTypeFor(typ.skipTypes({tyRef}), isImportedCppType) + proc constructLoc(p: BProc, loc: var TLoc, isTemp = false) = let typ = loc.t if optSeqDestructors in p.config.globalOptions and skipTypes(typ, abstractInst + {tyStatic}).kind in {tyString, tySequence}: @@ -497,7 +500,7 @@ proc constructLoc(p: BProc, loc: var TLoc, isTemp = false) = if not isTemp or containsGarbageCollectedRef(loc.t): # don't use nimZeroMem for temporary values for performance if we can # avoid it: - if not isImportedCppType(typ): + if not isOrHasImportedCppType(typ): linefmt(p, cpsStmts, "#nimZeroMem((void*)$1, sizeof($2));$n", [addrLoc(p.config, loc), getTypeDesc(p.module, typ, mapTypeChooser(loc))]) genObjectInit(p, cpsStmts, loc.t, loc, constructObj) @@ -517,7 +520,10 @@ proc initLocalVar(p: BProc, v: PSym, immediateAsgn: bool) = proc getTemp(p: BProc, t: PType, result: var TLoc; needsInit=false) = inc(p.labels) result.r = "T" & rope(p.labels) & "_" - linefmt(p, cpsLocals, "$1 $2;$n", [getTypeDesc(p.module, t, skVar), result.r]) + if p.module.compileToCpp and isOrHasImportedCppType(t): + linefmt(p, cpsLocals, "$1 $2{};$n", [getTypeDesc(p.module, t, skVar), result.r]) + else: + linefmt(p, cpsLocals, "$1 $2;$n", [getTypeDesc(p.module, t, skVar), result.r]) result.k = locTemp result.lode = lodeTyp t result.storage = OnStack @@ -575,7 +581,7 @@ proc assignLocalVar(p: BProc, n: PNode) = # this need not be fulfilled for inline procs; they are regenerated # for each module that uses them! let nl = if optLineDir in p.config.options: "" else: "\L" - let decl = localVarDecl(p, n) & ";" & nl + let decl = localVarDecl(p, n) & (if p.module.compileToCpp and isOrHasImportedCppType(n.typ): "{};" else: ";") & nl line(p, cpsLocals, decl) include ccgthreadvars From 1b1412f3d148b02fb553f37d84505745cf3fb435 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Thu, 2 Mar 2023 00:18:09 +0800 Subject: [PATCH 3736/3768] fixes #10938; fixes #13312; fixes #13918; fixes #20985; always initializes global variables with null values in VM (#21351) * fixes #10938; always initialize global variable in VM * fixes importc vars * there is a pre-existing issue regarding closure types in the VM * add tests --- compiler/vmgen.nim | 12 ++++++++++++ tests/vm/tvmmisc.nim | 46 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 56 insertions(+), 2 deletions(-) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index bdf7b679e..be7938e53 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1909,6 +1909,18 @@ proc genVarSection(c: PCtx; n: PNode) = c.genAdditionalCopy(a[2], opcWrDeref, tmp, 0, val) c.freeTemp(val) c.freeTemp(tmp) + elif not importcCondVar(s) and not (s.typ.kind == tyProc and s.typ.callConv == ccClosure): # fixes #10938 + # there is a pre-existing issue with closure types in VM + # if `(var s: proc () = default(proc ()); doAssert s == nil)` works for you; + # you might remove the second condition. + # the problem is that closure types are tuples in VM, but the types of its children + # shouldn't have the same type as closure types. + let tmp = c.genx(a[0], {gfNodeAddr}) + let sa = getNullValue(s.typ, a.info, c.config) + let val = c.genx(sa) + c.genAdditionalCopy(sa, opcWrDeref, tmp, 0, val) + c.freeTemp(val) + c.freeTemp(tmp) else: setSlot(c, s) if a[2].kind == nkEmpty: diff --git a/tests/vm/tvmmisc.nim b/tests/vm/tvmmisc.nim index 2ce4703b0..673e3e965 100644 --- a/tests/vm/tvmmisc.nim +++ b/tests/vm/tvmmisc.nim @@ -564,14 +564,14 @@ block: block: static: let x = int 1 - echo x.type # Foo + doAssert $(x.type) == "Foo" # Foo block: static: let x = int 1 let y = x + 1 # Error: unhandled exception: value out of range: -8 notin 0 .. 65535 [RangeDefect] - echo y + doAssert y == 2 type Atom* = object @@ -601,3 +601,45 @@ proc foo(s: sink string) = doAssert s.len == 3 static: foo("abc") + + +static: + for i in '1' .. '2': # bug #10938 + var s: set[char] + doAssert s == {} + incl(s, i) + + for _ in 0 ..< 3: # bug #13312 + var s: string + s.add("foo") + doAssert s == "foo" + + for i in 1 .. 5: # bug #13918 + var arr: array[3, int] + var val: int + doAssert arr == [0, 0, 0] and val == 0 + for j in 0 ..< len(arr): + arr[j] = i + val = i + +# bug #20985 +let a = block: + var groups: seq[seq[int]] + for i in 0 ..< 3: + var group: seq[int] + for j in 0 ..< 3: + group.add j + groups.add group + groups + +const b = block: + var groups: seq[seq[int]] + for i in 0 ..< 3: + var group: seq[int] + for j in 0 ..< 3: + group.add j + groups.add group + groups + +doAssert a == @[@[0, 1, 2], @[0, 1, 2], @[0, 1, 2]] +doAssert b == @[@[0, 1, 2], @[0, 1, 2], @[0, 1, 2]] From 89d8c0b24678a0b65820692069adffc7bec38ae7 Mon Sep 17 00:00:00 2001 From: Anna Date: Wed, 1 Mar 2023 22:00:58 +0500 Subject: [PATCH 3737/3768] tests/stylecheck: make sure necessary hints are enabled (#21240) --- tests/stylecheck/t20397_1.nim | 4 ++-- tests/stylecheck/treject.nim | 4 ++-- tests/stylecheck/tusages.nim | 6 ++---- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/tests/stylecheck/t20397_1.nim b/tests/stylecheck/t20397_1.nim index 24f5791f8..76c03dca1 100644 --- a/tests/stylecheck/t20397_1.nim +++ b/tests/stylecheck/t20397_1.nim @@ -1,8 +1,8 @@ discard """ - matrix: "--styleCheck:off" + matrix: "--styleCheck:off --hint:Name:on" """ {.hintAsError[Name]:on.} var a_b = 1 discard a_b -{.hintAsError[Name]:off.} \ No newline at end of file +{.hintAsError[Name]:off.} diff --git a/tests/stylecheck/treject.nim b/tests/stylecheck/treject.nim index b9d97a58d..458a2d039 100644 --- a/tests/stylecheck/treject.nim +++ b/tests/stylecheck/treject.nim @@ -1,7 +1,7 @@ discard """ action: reject nimout: '''treject.nim(14, 13) Error: 'iD' should be: 'id' [field declared in treject.nim(9, 5)]''' - matrix: "--styleCheck:error --styleCheck:usages" + matrix: "--styleCheck:error --styleCheck:usages --hint:Name:on" """ type @@ -14,4 +14,4 @@ template hello = echo name.iD echo iD -hello() \ No newline at end of file +hello() diff --git a/tests/stylecheck/tusages.nim b/tests/stylecheck/tusages.nim index 2f99c70c5..b689dabb3 100644 --- a/tests/stylecheck/tusages.nim +++ b/tests/stylecheck/tusages.nim @@ -1,11 +1,9 @@ discard """ action: reject - nimout: '''tusages.nim(22, 5) Error: 'BAD_STYLE' should be: 'BADSTYLE' [proc declared in tusages.nim(11, 6)]''' - matrix: "--styleCheck:error --styleCheck:usages" + nimout: '''tusages.nim(20, 5) Error: 'BAD_STYLE' should be: 'BADSTYLE' [proc declared in tusages.nim(9, 6)]''' + matrix: "--styleCheck:error --styleCheck:usages --hint:all:off --hint:Name:on" """ - - import strutils proc BADSTYLE(c: char) = discard From cf083af262d03f62af302a28920f7bfa4b0e6233 Mon Sep 17 00:00:00 2001 From: Anna Date: Wed, 1 Mar 2023 22:01:58 +0500 Subject: [PATCH 3738/3768] tests: explicitly enable stack traces where needed (#21236) * tests/assert/tassert_c.nim: explicitly enable stack traces * tests/errmsgs: explicitly enable stack traces --- tests/assert/tassert_c.nim | 2 +- tests/errmsgs/tproper_stacktrace.nim | 31 ++++++++++++++------------- tests/errmsgs/tproper_stacktrace2.nim | 3 ++- tests/errmsgs/tproper_stacktrace3.nim | 3 ++- 4 files changed, 21 insertions(+), 18 deletions(-) diff --git a/tests/assert/tassert_c.nim b/tests/assert/tassert_c.nim index 4d49a6035..e3e3b8147 100644 --- a/tests/assert/tassert_c.nim +++ b/tests/assert/tassert_c.nim @@ -1,5 +1,5 @@ discard """ - cmd: "nim $target $options -d:nimPreviewSlimSystem --excessiveStackTrace:off $file" + matrix: "-d:nimPreviewSlimSystem --stackTrace:on --excessiveStackTrace:off" output: '''true''' """ import std/assertions diff --git a/tests/errmsgs/tproper_stacktrace.nim b/tests/errmsgs/tproper_stacktrace.nim index 8617984fb..b0a008840 100644 --- a/tests/errmsgs/tproper_stacktrace.nim +++ b/tests/errmsgs/tproper_stacktrace.nim @@ -1,4 +1,5 @@ discard """ + matrix: "--stackTrace:on --hint:all:off --warnings:off" output: '''ok''' """ import strscans, strutils @@ -76,10 +77,10 @@ when true: bar() const expectedStackTrace = """ - tproper_stacktrace.nim(86) tproper_stacktrace - tproper_stacktrace.nim(76) foo - tproper_stacktrace.nim(73) bar - tproper_stacktrace.nim(7) raiseTestException + tproper_stacktrace.nim(87) tproper_stacktrace + tproper_stacktrace.nim(77) foo + tproper_stacktrace.nim(74) bar + tproper_stacktrace.nim(8) raiseTestException """ verifyStackTrace expectedStackTrace: @@ -93,9 +94,9 @@ when true: bar(x) const expectedStackTrace = """ - tproper_stacktrace.nim(103) tproper_stacktrace - tproper_stacktrace.nim(90) bar - tproper_stacktrace.nim(7) raiseTestException + tproper_stacktrace.nim(104) tproper_stacktrace + tproper_stacktrace.nim(91) bar + tproper_stacktrace.nim(8) raiseTestException """ verifyStackTrace expectedStackTrace: @@ -110,10 +111,10 @@ when true: bar() const expectedStackTrace = """ - tproper_stacktrace.nim(120) tproper_stacktrace - tproper_stacktrace.nim(110) foo - tproper_stacktrace.nim(107) bar - tproper_stacktrace.nim(7) raiseTestException + tproper_stacktrace.nim(121) tproper_stacktrace + tproper_stacktrace.nim(111) foo + tproper_stacktrace.nim(108) bar + tproper_stacktrace.nim(8) raiseTestException """ verifyStackTrace expectedStackTrace: @@ -129,10 +130,10 @@ when true: bar() const expectedStackTrace = """ - tproper_stacktrace.nim(139) tproper_stacktrace - tproper_stacktrace.nim(129) foo - tproper_stacktrace.nim(125) baz - tproper_stacktrace.nim(7) raiseTestException + tproper_stacktrace.nim(140) tproper_stacktrace + tproper_stacktrace.nim(130) foo + tproper_stacktrace.nim(126) baz + tproper_stacktrace.nim(8) raiseTestException """ verifyStackTrace expectedStackTrace: diff --git a/tests/errmsgs/tproper_stacktrace2.nim b/tests/errmsgs/tproper_stacktrace2.nim index 44b208c87..5a6ca3fcb 100644 --- a/tests/errmsgs/tproper_stacktrace2.nim +++ b/tests/errmsgs/tproper_stacktrace2.nim @@ -1,5 +1,6 @@ discard """ - outputsub: '''tproper_stacktrace2.nim(20) main''' + matrix: "--stackTrace:on" + outputsub: '''tproper_stacktrace2.nim(21) main''' exitcode: 1 """ diff --git a/tests/errmsgs/tproper_stacktrace3.nim b/tests/errmsgs/tproper_stacktrace3.nim index c292fa092..97d63e6ec 100644 --- a/tests/errmsgs/tproper_stacktrace3.nim +++ b/tests/errmsgs/tproper_stacktrace3.nim @@ -1,5 +1,6 @@ discard """ - outputsub: '''tproper_stacktrace3.nim(21) main''' + matrix: "--stackTrace:on" + outputsub: '''tproper_stacktrace3.nim(22) main''' exitcode: 1 """ From 070938720ad1a06e3273c164db53edb470bdb9a8 Mon Sep 17 00:00:00 2001 From: Anna Date: Thu, 2 Mar 2023 01:29:23 +0500 Subject: [PATCH 3739/3768] tests/errmsgs/tcall_with_default_arg.nim: sync (#21237) --- tests/errmsgs/tcall_with_default_arg.nim | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/errmsgs/tcall_with_default_arg.nim b/tests/errmsgs/tcall_with_default_arg.nim index 1cc86638f..44752f1ee 100644 --- a/tests/errmsgs/tcall_with_default_arg.nim +++ b/tests/errmsgs/tcall_with_default_arg.nim @@ -1,5 +1,5 @@ discard """ -outputsub: '''tcall_with_default_arg.nim(16) anotherFoo''' +outputsub: '''tcall_with_default_arg.nim(8) fail''' exitcode: 1 """ # issue: #5604 From 9948fed919389229a48347aa9fa5adce9b7e0a98 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Thu, 2 Mar 2023 12:25:55 +0800 Subject: [PATCH 3740/3768] fixes #21326; fixes #7375; fixes #11986; fixes #9607; rework quote do; `getAst` uses type info to annotate the type of quoted variables; no more type erasures for quoted variables (#21433) * fixes #21326; getAst uses type info to annotateType quoted variables * simplify logics; sem types first * fixes important packages * add testcases * tiny --- compiler/semexprs.nim | 11 ++- compiler/vm.nim | 5 +- testament/important_packages.nim | 6 +- tests/stdlib/tmacros.nim | 136 +++++++++++++++++++++++++++++++ 4 files changed, 150 insertions(+), 8 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 7454c7f8e..9cf206b4c 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2203,10 +2203,13 @@ proc semQuoteAst(c: PContext, n: PNode): PNode = if ids.len > 0: dummyTemplate[paramsPos] = newNodeI(nkFormalParams, n.info) dummyTemplate[paramsPos].add getSysSym(c.graph, n.info, "untyped").newSymNode # return type - ids.add getSysSym(c.graph, n.info, "untyped").newSymNode # params type - ids.add c.graph.emptyNode # no default value - dummyTemplate[paramsPos].add newTreeI(nkIdentDefs, n.info, ids) - + dummyTemplate[paramsPos].add newTreeI(nkIdentDefs, n.info, ids[0], getSysSym(c.graph, n.info, "typed").newSymNode, c.graph.emptyNode) + for i in 1.. Date: Thu, 2 Mar 2023 12:29:40 +0800 Subject: [PATCH 3741/3768] fixes #19291; implements `wasMoved` hook (#21303) * fixes #19291; implements `wasMoved` hook * basics * checkpoint * finish `wasMoved` * add a test for #19291 * add documentation and changelog * work `attachedWasMoved` with generics * fixes optimizer * register `=wasMoved` * handle wasMoved magcis * check another round * some patches * try `op == nil` * nicer * generate `wasMoved` before `destroy` * try again * fixes tests * default wasMoved * Update tests/destructor/tv2_cast.nim * Update tests/destructor/tv2_cast.nim * Update tests/arc/topt_refcursors.nim --- changelogs/changelog_2_0_0.md | 2 ++ compiler/ast.nim | 3 +- compiler/injectdestructors.nim | 17 +++++++--- compiler/liftdestructors.nim | 41 ++++++++++++++++++++--- compiler/optimizer.nim | 9 +++-- compiler/semstmts.nim | 5 ++- doc/manual.md | 2 +- tests/arc/topt_no_cursor.nim | 19 ++++++----- tests/arc/topt_refcursors.nim | 8 +++-- tests/arc/topt_wasmoved_destroy_pairs.nim | 13 ++++--- tests/destructor/tv2_cast.nim | 14 ++++---- tests/destructor/twasmoved_error.nim | 37 ++++++++++++++++++++ 12 files changed, 131 insertions(+), 39 deletions(-) create mode 100644 tests/destructor/twasmoved_error.nim diff --git a/changelogs/changelog_2_0_0.md b/changelogs/changelog_2_0_0.md index e081865be..cbe755478 100644 --- a/changelogs/changelog_2_0_0.md +++ b/changelogs/changelog_2_0_0.md @@ -341,6 +341,8 @@ - IBM Z architecture and macOS m1 arm64 architecture are supported. +- `=wasMoved` can be overridden by users. + ## Compiler changes - The `gc` switch has been renamed to `mm` ("memory management") in order to reflect the diff --git a/compiler/ast.nim b/compiler/ast.nim index aa70560ed..f93c8d910 100644 --- a/compiler/ast.nim +++ b/compiler/ast.nim @@ -935,6 +935,7 @@ type TTypeSeq* = seq[PType] TTypeAttachedOp* = enum ## as usual, order is important here + attachedWasMoved, attachedDestructor, attachedAsgn, attachedSink, @@ -1502,7 +1503,7 @@ proc newProcNode*(kind: TNodeKind, info: TLineInfo, body: PNode, const AttachedOpToStr*: array[TTypeAttachedOp, string] = [ - "=destroy", "=copy", "=sink", "=trace", "=deepcopy"] + "=wasMoved", "=destroy", "=copy", "=sink", "=trace", "=deepcopy"] proc `$`*(s: PSym): string = if s != nil: diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 20e790202..13141b765 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -354,11 +354,18 @@ It is best to factor out piece of object that needs custom destructor into separ result.add newTree(nkFastAsgn, le, tmp) proc genWasMoved(c: var Con, n: PNode): PNode = - result = newNodeI(nkCall, n.info) - result.add(newSymNode(createMagic(c.graph, c.idgen, "wasMoved", mWasMoved))) - result.add copyTree(n) #mWasMoved does not take the address - #if n.kind != nkSym: - # message(c.graph.config, n.info, warnUser, "wasMoved(" & $n & ")") + let typ = n.typ.skipTypes({tyGenericInst, tyAlias, tySink}) + let op = getAttachedOp(c.graph, n.typ, attachedWasMoved) + if op != nil: + if sfError in op.flags: + c.checkForErrorPragma(n.typ, n, "=wasMoved") + result = genOp(c, op, n) + else: + result = newNodeI(nkCall, n.info) + result.add(newSymNode(createMagic(c.graph, c.idgen, "wasMoved", mWasMoved))) + result.add copyTree(n) #mWasMoved does not take the address + #if n.kind != nkSym: + # message(c.graph.config, n.info, warnUser, "wasMoved(" & $n & ")") proc genDefaultCall(t: PType; c: Con; info: TLineInfo): PNode = result = newNodeI(nkCall, info) diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index 738f659b7..c7464d39e 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -88,6 +88,8 @@ proc defaultOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = let call = genBuiltin(c, mDefault, "default", x) call.typ = t body.add newAsgnStmt(x, call) + elif c.kind == attachedWasMoved: + body.add genBuiltin(c, mWasMoved, "wasMoved", x) proc genAddr(c: var TLiftCtx; x: PNode): PNode = if x.kind == nkHiddenDeref: @@ -145,6 +147,11 @@ proc destructorCall(c: var TLiftCtx; op: PSym; x: PNode): PNode = else: result = destroy +proc genWasMovedCall(c: var TLiftCtx; op: PSym; x: PNode): PNode = + result = newNodeIT(nkCall, x.info, op.typ[0]) + result.add(newSymNode(op)) + result.add genAddr(c, x) + proc fillBodyObj(c: var TLiftCtx; n, body, x, y: PNode; enforceDefaultOp: bool) = case n.kind of nkSym: @@ -442,6 +449,20 @@ proc considerUserDefinedOp(c: var TLiftCtx; t: PType; body, x, y: PNode): bool = body.add newDeepCopyCall(c, op, x, y) result = true + of attachedWasMoved: + var op = getAttachedOp(c.g, t, attachedWasMoved) + if op != nil and sfOverriden in op.flags: + + if op.ast.isGenericRoutine: + # patch generic destructor: + op = instantiateGeneric(c, op, t, t.typeInst) + setAttachedOp(c.g, c.idgen.module, t, attachedWasMoved, op) + + #markUsed(c.g.config, c.info, op, c.g.usageSym) + onUse(c.info, op) + body.add genWasMovedCall(c, op, x) + result = true + proc declareCounter(c: var TLiftCtx; body: PNode; first: BiggestInt): PNode = var temp = newSym(skTemp, getIdent(c.g.cache, lowerings.genPrefix), nextSymId(c.idgen), c.fn, c.info) temp.typ = getSysType(c.g, body.info, tyInt) @@ -524,6 +545,7 @@ proc fillSeqOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = if canFormAcycle(t.elemType): # follow all elements: forallElements(c, t, body, x, y) + of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x) proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = createTypeBoundOps(c.g, c.c, t, body.info, c.idgen) @@ -561,6 +583,7 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = if op == nil: return # protect from recursion body.add newHookCall(c, op, x, y) + of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x) proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = case c.kind @@ -576,6 +599,7 @@ proc fillStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = body.add genBuiltin(c, mDestroy, "destroy", x) of attachedTrace: discard "strings are atomic and have no inner elements that are to trace" + of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x) proc cyclicType*(t: PType): bool = case t.kind @@ -674,6 +698,8 @@ proc atomicRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = # If the ref is polymorphic we have to account for this body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, genAddrOf(x, c.idgen), y) #echo "can follow ", elemType, " static ", isFinal(elemType) + of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x) + proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = ## Closures are really like refs except they always use a virtual destructor @@ -722,6 +748,7 @@ proc atomicClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = of attachedDeepCopy: assert(false, "cannot happen") of attachedTrace: body.add callCodegenProc(c.g, "nimTraceRefDyn", c.info, genAddrOf(xenv, c.idgen), y) + of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x) proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = case c.kind @@ -746,6 +773,7 @@ proc weakrefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = body.sons.insert(des, 0) of attachedDeepCopy: assert(false, "cannot happen") of attachedTrace: discard + of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x) proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = var actions = newNodeI(nkStmtList, c.info) @@ -771,6 +799,7 @@ proc ownedRefOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = body.add genIf(c, x, actions) of attachedDeepCopy: assert(false, "cannot happen") of attachedTrace: discard + of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x) proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = if c.kind == attachedDeepCopy: @@ -805,6 +834,7 @@ proc closureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = body.sons.insert(des, 0) of attachedDeepCopy: assert(false, "cannot happen") of attachedTrace: discard + of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x) proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = let xx = genBuiltin(c, mAccessEnv, "accessEnv", x) @@ -820,6 +850,7 @@ proc ownedClosureOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = body.add genIf(c, xx, actions) of attachedDeepCopy: assert(false, "cannot happen") of attachedTrace: discard + of attachedWasMoved: body.add genBuiltin(c, mWasMoved, "wasMoved", x) proc fillBody(c: var TLiftCtx; t: PType; body, x, y: PNode) = case t.kind @@ -935,7 +966,7 @@ proc symPrototype(g: ModuleGraph; typ: PType; owner: PSym; kind: TTypeAttachedOp result.typ = newProcType(info, nextTypeId(idgen), owner) result.typ.addParam dest - if kind != attachedDestructor: + if kind notin {attachedDestructor, attachedWasMoved}: result.typ.addParam src if kind == attachedAsgn and g.config.selectedGC == gcOrc and @@ -975,7 +1006,7 @@ proc produceSym(g: ModuleGraph; c: PContext; typ: PType; kind: TTypeAttachedOp; let dest = result.typ.n[1].sym let d = newDeref(newSymNode(dest)) - let src = if kind == attachedDestructor: newNodeIT(nkSym, info, getSysType(g, info, tyPointer)) + let src = if kind in {attachedDestructor, attachedWasMoved}: newNodeIT(nkSym, info, getSysType(g, info, tyPointer)) else: newSymNode(result.typ.n[2].sym) # register this operation already: @@ -1103,15 +1134,15 @@ proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInf # bug #15122: We need to produce all prototypes before entering the # mind boggling recursion. Hacks like these imply we should rewrite # this module. - var generics: array[attachedDestructor..attachedTrace, bool] - for k in attachedDestructor..lastAttached: + var generics: array[attachedWasMoved..attachedTrace, bool] + for k in attachedWasMoved..lastAttached: generics[k] = getAttachedOp(g, canon, k) != nil if not generics[k]: setAttachedOp(g, idgen.module, canon, k, symPrototype(g, canon, canon.owner, k, info, idgen)) # we generate the destructor first so that other operators can depend on it: - for k in attachedDestructor..lastAttached: + for k in attachedWasMoved..lastAttached: if not generics[k]: discard produceSym(g, c, canon, k, info, idgen) else: diff --git a/compiler/optimizer.nim b/compiler/optimizer.nim index 98939f80d..0b26e8d34 100644 --- a/compiler/optimizer.nim +++ b/compiler/optimizer.nim @@ -16,6 +16,8 @@ import from trees import exprStructuralEquivalent +import std/strutils + const nfMarkForDeletion = nfNone # faster than a lookup table @@ -110,16 +112,17 @@ proc analyse(c: var Con; b: var BasicBlock; n: PNode) = var reverse = false if n[0].kind == nkSym: let s = n[0].sym - if s.magic == mWasMoved: + let name = s.name.s.normalize + if s.magic == mWasMoved or name == "=wasmoved": b.wasMovedLocs.add n special = true - elif s.name.s == "=destroy": + elif name == "=destroy": if c.inFinally > 0 and (b.hasReturn or b.hasBreak): discard "cannot optimize away the destructor" else: c.wasMovedDestroyPair b, n special = true - elif s.name.s == "=sink": + elif name == "=sink": reverse = true if not special: diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 7dc976c19..6237e6eb0 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1779,7 +1779,7 @@ proc whereToBindTypeHook(c: PContext; t: PType): PType = proc bindTypeHook(c: PContext; s: PSym; n: PNode; op: TTypeAttachedOp) = let t = s.typ var noError = false - let cond = if op == attachedDestructor: + let cond = if op in {attachedDestructor, attachedWasMoved}: t.len == 2 and t[0] == nil and t[1].kind == tyVar elif op == attachedTrace: t.len == 3 and t[0] == nil and t[1].kind == tyVar and t[2].kind == tyPointer @@ -1894,6 +1894,9 @@ proc semOverride(c: PContext, s: PSym, n: PNode) = of "=trace": if s.magic != mTrace: bindTypeHook(c, s, n, attachedTrace) + of "=wasmoved": + if s.magic != mWasMoved: + bindTypeHook(c, s, n, attachedWasMoved) else: if sfOverriden in s.flags: localError(c.config, n.info, errGenerated, diff --git a/doc/manual.md b/doc/manual.md index ef790c362..31c620ca7 100644 --- a/doc/manual.md +++ b/doc/manual.md @@ -4122,7 +4122,7 @@ the operator is in scope (including if it is private). ``` Type bound operators are: -`=destroy`, `=copy`, `=sink`, `=trace`, `=deepcopy`. +`=destroy`, `=copy`, `=sink`, `=trace`, `=deepcopy`, `=wasMoved`. These operations can be *overridden* instead of *overloaded*. This means that the implementation is automatically lifted to structured types. For instance, diff --git a/tests/arc/topt_no_cursor.nim b/tests/arc/topt_no_cursor.nim index 30d4c316c..50dfa26ac 100644 --- a/tests/arc/topt_no_cursor.nim +++ b/tests/arc/topt_no_cursor.nim @@ -1,7 +1,8 @@ discard """ nimoutFull: true cmd: '''nim c -r --warnings:off --hints:off --gc:arc --expandArc:newTarget --expandArc:delete --expandArc:p1 --expandArc:tt --hint:Performance:off --assertions:off --expandArc:extractConfig --expandArc:mergeShadowScope --expandArc:check $file''' - nimout: '''--expandArc: newTarget + nimout: ''' +--expandArc: newTarget var splat @@ -11,9 +12,9 @@ splat = splitDrive do: let blitTmp = path blitTmp :tmp = splat.drive -wasMoved(splat.drive) +`=wasMoved`(splat.drive) :tmp_1 = splat.path_1 -wasMoved(splat.path_1) +`=wasMoved`(splat.path_1) result = ( let blitTmp_1 = :tmp blitTmp_1, @@ -60,10 +61,10 @@ var try: it_cursor = x a = ( - wasMoved(:tmpD) + `=wasMoved`(:tmpD) `=copy`(:tmpD, it_cursor.key) :tmpD, - wasMoved(:tmpD_1) + `=wasMoved`(:tmpD_1) `=copy`(:tmpD_1, it_cursor.val) :tmpD_1) echo [ @@ -112,7 +113,7 @@ block :tmp: var :tmpD sym = shadowScope.symbols[i] addInterfaceDecl(c): - wasMoved(:tmpD) + `=wasMoved`(:tmpD) `=copy_1`(:tmpD, sym) :tmpD inc(i, 1) @@ -125,7 +126,7 @@ this.isValid = fileExists(this.value) if dirExists(this.value): var :tmpD par = (dir: - wasMoved(:tmpD) + `=wasMoved`(:tmpD) `=copy`(:tmpD, this.value) :tmpD, front: "") else: var @@ -133,10 +134,10 @@ if dirExists(this.value): :tmpD_2 :tmpD_3 par = (dir_1: parentDir(this.value), front_1: - wasMoved(:tmpD_1) + `=wasMoved`(:tmpD_1) `=copy`(:tmpD_1, :tmpD_3 = splitDrive do: - wasMoved(:tmpD_2) + `=wasMoved`(:tmpD_2) `=copy`(:tmpD_2, this.value) :tmpD_2 :tmpD_3.path) diff --git a/tests/arc/topt_refcursors.nim b/tests/arc/topt_refcursors.nim index c13d81bad..8c638a4a1 100644 --- a/tests/arc/topt_refcursors.nim +++ b/tests/arc/topt_refcursors.nim @@ -1,7 +1,8 @@ discard """ output: '''''' cmd: '''nim c --gc:arc --expandArc:traverse --hint:Performance:off $file''' - nimout: '''--expandArc: traverse + nimout: ''' +--expandArc: traverse var it_cursor @@ -22,12 +23,13 @@ try: `=copy`(ri_1, jt.ri) echo [jt.s] `=sink`(jt, ri_1) - wasMoved(ri_1) + `=wasMoved`(ri_1) finally: `=destroy`(ri_1) finally: `=destroy`(jt) --- end of expandArc ------------------------''' +-- end of expandArc ------------------------ +''' """ type diff --git a/tests/arc/topt_wasmoved_destroy_pairs.nim b/tests/arc/topt_wasmoved_destroy_pairs.nim index 2f971f112..6577d6787 100644 --- a/tests/arc/topt_wasmoved_destroy_pairs.nim +++ b/tests/arc/topt_wasmoved_destroy_pairs.nim @@ -1,7 +1,8 @@ discard """ output: '''''' cmd: '''nim c --gc:arc --expandArc:main --expandArc:tfor --hint:Performance:off $file''' - nimout: '''--expandArc: main + nimout: ''' +--expandArc: main var a @@ -29,6 +30,7 @@ try: x = f() block :tmp: var i_cursor + mixin inc var i_1 = 0 block :tmp_1: while i_1 < 4: @@ -37,25 +39,26 @@ try: if i_cursor == 2: return add(a): - wasMoved(:tmpD) + `=wasMoved`(:tmpD) `=copy`(:tmpD, x) :tmpD inc i_1, 1 if cond: add(a): let blitTmp = x - wasMoved(x) + `=wasMoved`(x) blitTmp else: add(b): let blitTmp_1 = x - wasMoved(x) + `=wasMoved`(x) blitTmp_1 finally: `=destroy`(x) `=destroy_1`(b) `=destroy_1`(a) --- end of expandArc ------------------------''' +-- end of expandArc ------------------------ +''' """ proc f(): seq[int] = diff --git a/tests/destructor/tv2_cast.nim b/tests/destructor/tv2_cast.nim index 917cf0eb3..6fa419996 100644 --- a/tests/destructor/tv2_cast.nim +++ b/tests/destructor/tv2_cast.nim @@ -4,7 +4,8 @@ discard """ @[1953719668, 875770417] destroying O1''' cmd: '''nim c --gc:arc --expandArc:main --expandArc:main1 --expandArc:main2 --expandArc:main3 --hints:off --assertions:off $file''' - nimout: '''--expandArc: main + nimout: ''' +--expandArc: main var data @@ -12,7 +13,7 @@ var :tmpD_1 :tmpD_2 data = - wasMoved(:tmpD) + `=wasMoved`(:tmpD) `=copy`(:tmpD, cast[string]( :tmpD_2 = encode(cast[seq[byte]]( :tmpD_1 = newString(100) @@ -32,7 +33,7 @@ var :tmpD_1 s = newString(100) data = - wasMoved(:tmpD) + `=wasMoved`(:tmpD) `=copy`(:tmpD, cast[string]( :tmpD_1 = encode(toOpenArrayByte(s, 0, len(s) - 1)) :tmpD_1)) @@ -50,7 +51,7 @@ var :tmpD_1 s = newSeq(100) data = - wasMoved(:tmpD) + `=wasMoved`(:tmpD) `=copy`(:tmpD, cast[string]( :tmpD_1 = encode(s) :tmpD_1)) @@ -67,7 +68,7 @@ var :tmpD_1 :tmpD_2 data = - wasMoved(:tmpD) + `=wasMoved`(:tmpD) `=copy`(:tmpD, cast[string]( :tmpD_2 = encode do: :tmpD_1 = newSeq(100) @@ -77,7 +78,8 @@ data = `=destroy`(:tmpD_2) `=destroy`(:tmpD_1) `=destroy_1`(data) --- end of expandArc ------------------------''' +-- end of expandArc ------------------------ +''' """ func encode*(src: openArray[byte]): seq[byte] = diff --git a/tests/destructor/twasmoved_error.nim b/tests/destructor/twasmoved_error.nim new file mode 100644 index 000000000..1cd57e3df --- /dev/null +++ b/tests/destructor/twasmoved_error.nim @@ -0,0 +1,37 @@ +discard """ + cmd: '''nim c --mm:arc $file''' + errormsg: "'=wasMoved' is not available for type ; routine: main" +""" + +# bug #19291 + +const + screenWidth = 800 + screenHeight = 450 + +var + ready = false +type + Game = object + +proc `=destroy`(x: var Game) = + assert ready, "Window is already opened" + ready = false + +proc `=sink`(x: var Game; y: Game) {.error.} +proc `=copy`(x: var Game; y: Game) {.error.} +proc `=wasMoved`(x: var Game) {.error.} + +proc initGame(width, height: int32, title: string): Game = + assert not ready, "Window is already closed" + ready = true + +proc update(x: Game) = discard + +proc main = + var g = initGame(screenWidth, screenHeight, "Tetris raylib") + g.update() + var g2 = g + echo "hello" + +main() From 9b5ae2b2eb2ba5aca219de109b03f1bb5637a777 Mon Sep 17 00:00:00 2001 From: Ivan Yonchovski Date: Thu, 2 Mar 2023 06:30:55 +0200 Subject: [PATCH 3742/3768] Define the version of nim package without using system module (#21415) This is follow up from https://github.com/nim-lang/Nim/pull/21313 --- nim.nimble | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nim.nimble b/nim.nimble index 713fc9256..380ffbce8 100644 --- a/nim.nimble +++ b/nim.nimble @@ -1,4 +1,5 @@ -version = system.NimVersion +include "lib/system/compilation.nim" +version = $NimMajor & "." & $NimMinor & "." & $NimPatch author = "Andreas Rumpf" description = "Compiler package providing the compiler sources as a library." license = "MIT" From 612abda4f40b2a0fd8dd0e4ad119c4415b9c34cb Mon Sep 17 00:00:00 2001 From: tersec Date: Thu, 2 Mar 2023 04:51:54 +0000 Subject: [PATCH 3743/3768] remove decades-deprecated Win32 API *A function support (#21315) --- lib/pure/asyncfile.nim | 11 +- lib/pure/browsers.nim | 2 +- lib/pure/memfiles.nim | 7 +- lib/pure/os.nim | 93 ++++-------- lib/pure/osproc.nim | 35 ++--- lib/std/cmdline.nim | 5 +- lib/std/private/oscommon.nim | 65 +++------ lib/std/private/osdirs.nim | 17 +-- lib/std/private/osfiles.nim | 43 ++---- lib/std/private/ospaths2.nim | 35 ++--- lib/std/private/ossymlinks.nim | 12 +- lib/std/syncio.nim | 4 +- lib/windows/winlean.nim | 258 ++++++++++----------------------- 13 files changed, 173 insertions(+), 414 deletions(-) diff --git a/lib/pure/asyncfile.nim b/lib/pure/asyncfile.nim index 88fa90406..96d5e6d2e 100644 --- a/lib/pure/asyncfile.nim +++ b/lib/pure/asyncfile.nim @@ -102,14 +102,9 @@ proc openAsync*(filename: string, mode = fmRead): AsyncFile = let flags = FILE_FLAG_OVERLAPPED or FILE_ATTRIBUTE_NORMAL let desiredAccess = getDesiredAccess(mode) let creationDisposition = getCreationDisposition(mode, filename) - when useWinUnicode: - let fd = createFileW(newWideCString(filename), desiredAccess, - FILE_SHARE_READ, - nil, creationDisposition, flags, 0) - else: - let fd = createFileA(filename, desiredAccess, - FILE_SHARE_READ, - nil, creationDisposition, flags, 0) + let fd = createFileW(newWideCString(filename), desiredAccess, + FILE_SHARE_READ, + nil, creationDisposition, flags, 0) if fd == INVALID_HANDLE_VALUE: raiseOSError(osLastError()) diff --git a/lib/pure/browsers.nim b/lib/pure/browsers.nim index b78034fe3..ab248db36 100644 --- a/lib/pure/browsers.nim +++ b/lib/pure/browsers.nim @@ -21,7 +21,7 @@ when defined(nimPreviewSlimSystem): when defined(windows): import winlean - when useWinUnicode and defined(nimPreviewSlimSystem): + when defined(nimPreviewSlimSystem): import std/widestrs from os import absolutePath else: diff --git a/lib/pure/memfiles.nim b/lib/pure/memfiles.nim index 58c4888f1..74065bc2e 100644 --- a/lib/pure/memfiles.nim +++ b/lib/pure/memfiles.nim @@ -17,7 +17,7 @@ when defined(windows): import winlean - when useWinUnicode and defined(nimPreviewSlimSystem): + when defined(nimPreviewSlimSystem): import std/widestrs elif defined(posix): import posix @@ -199,10 +199,7 @@ proc open*(filename: string, mode: FileMode = fmRead, else: FILE_ATTRIBUTE_NORMAL or flags, 0) - when useWinUnicode: - result.fHandle = callCreateFile(createFileW, newWideCString(filename)) - else: - result.fHandle = callCreateFile(createFileA, filename) + result.fHandle = callCreateFile(createFileW, newWideCString(filename)) if result.fHandle == INVALID_HANDLE_VALUE: fail(osLastError(), "error opening file") diff --git a/lib/pure/os.nim b/lib/pure/os.nim index 3285c07df..0569e6cab 100644 --- a/lib/pure/os.nim +++ b/lib/pure/os.nim @@ -422,32 +422,18 @@ proc expandFilename*(filename: string): string {.rtl, extern: "nos$1", ## Raises `OSError` in case of an error. Follows symlinks. when defined(windows): var bufsize = MAX_PATH.int32 - when useWinUnicode: - var unused: WideCString = nil - var res = newWideCString("", bufsize) - while true: - var L = getFullPathNameW(newWideCString(filename), bufsize, res, unused) - if L == 0'i32: - raiseOSError(osLastError(), filename) - elif L > bufsize: - res = newWideCString("", L) - bufsize = L - else: - result = res$L - break - else: - var unused: cstring = nil - result = newString(bufsize) - while true: - var L = getFullPathNameA(filename, bufsize, result, unused) - if L == 0'i32: - raiseOSError(osLastError(), filename) - elif L > bufsize: - result = newString(L) - bufsize = L - else: - setLen(result, L) - break + var unused: WideCString = nil + var res = newWideCString("", bufsize) + while true: + var L = getFullPathNameW(newWideCString(filename), bufsize, res, unused) + if L == 0'i32: + raiseOSError(osLastError(), filename) + elif L > bufsize: + res = newWideCString("", L) + bufsize = L + else: + result = res$L + break # getFullPathName doesn't do case corrections, so we have to use this convoluted # way of retrieving the true filename for x in walkFiles(result): @@ -483,14 +469,10 @@ proc createHardlink*(src, dest: string) {.noWeirdTarget.} = ## See also: ## * `createSymlink proc`_ when defined(windows): - when useWinUnicode: - var wSrc = newWideCString(src) - var wDst = newWideCString(dest) - if createHardLinkW(wDst, wSrc, nil) == 0: - raiseOSError(osLastError(), $(src, dest)) - else: - if createHardLinkA(dest, src, nil) == 0: - raiseOSError(osLastError(), $(src, dest)) + var wSrc = newWideCString(src) + var wDst = newWideCString(dest) + if createHardLinkW(wDst, wSrc, nil) == 0: + raiseOSError(osLastError(), $(src, dest)) else: if link(src, dest) != 0: raiseOSError(osLastError(), $(src, dest)) @@ -655,32 +637,18 @@ proc getAppFilename*(): string {.rtl, extern: "nos$1", tags: [ReadIOEffect], noW # /proc//path/a.out (complete pathname) when defined(windows): var bufsize = int32(MAX_PATH) - when useWinUnicode: - var buf = newWideCString("", bufsize) - while true: - var L = getModuleFileNameW(0, buf, bufsize) - if L == 0'i32: - result = "" # error! - break - elif L > bufsize: - buf = newWideCString("", L) - bufsize = L - else: - result = buf$L - break - else: - result = newString(bufsize) - while true: - var L = getModuleFileNameA(0, result, bufsize) - if L == 0'i32: - result = "" # error! - break - elif L > bufsize: - result = newString(L) - bufsize = L - else: - setLen(result, L) - break + var buf = newWideCString("", bufsize) + while true: + var L = getModuleFileNameW(0, buf, bufsize) + if L == 0'i32: + result = "" # error! + break + elif L > bufsize: + buf = newWideCString("", L) + bufsize = L + else: + result = buf$L + break elif defined(macosx): var size = cuint32(0) getExecPath1(nil, size) @@ -977,10 +945,7 @@ proc isHidden*(path: string): bool {.noWeirdTarget.} = assert ".foo/".isHidden when defined(windows): - when useWinUnicode: - wrapUnary(attributes, getFileAttributesW, path) - else: - var attributes = getFileAttributesA(path) + wrapUnary(attributes, getFileAttributesW, path) if attributes != -1'i32: result = (attributes and FILE_ATTRIBUTE_HIDDEN) != 0'i32 else: diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index bb6abc571..4cdd98c0d 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -714,22 +714,15 @@ when defined(windows) and not defined(useNimRtl): if len(workingDir) > 0: wd = workingDir if env != nil: e = buildEnv(env) if poEchoCmd in options: echo($cmdl) - when useWinUnicode: - var tmp = newWideCString(cmdl) - var ee = - if e.str.isNil: newWideCString(cstring(nil)) - else: newWideCString(e.str, e.len) - var wwd = newWideCString(wd) - var flags = NORMAL_PRIORITY_CLASS or CREATE_UNICODE_ENVIRONMENT - if poDaemon in options: flags = flags or CREATE_NO_WINDOW - success = winlean.createProcessW(nil, tmp, nil, nil, 1, flags, - ee, wwd, si, procInfo) - else: - var ee = - if e.str.isNil: cstring(nil) - else: cstring(e.str) - success = winlean.createProcessA(nil, - cmdl, nil, nil, 1, NORMAL_PRIORITY_CLASS, ee, wd, si, procInfo) + var tmp = newWideCString(cmdl) + var ee = + if e.str.isNil: newWideCString(cstring(nil)) + else: newWideCString(e.str, e.len) + var wwd = newWideCString(wd) + var flags = NORMAL_PRIORITY_CLASS or CREATE_UNICODE_ENVIRONMENT + if poDaemon in options: flags = flags or CREATE_NO_WINDOW + success = winlean.createProcessW(nil, tmp, nil, nil, 1, flags, + ee, wwd, si, procInfo) let lastError = osLastError() if poParentStreams notin options: @@ -870,13 +863,9 @@ when defined(windows) and not defined(useNimRtl): si.hStdError = getStdHandle(STD_ERROR_HANDLE) si.hStdInput = getStdHandle(STD_INPUT_HANDLE) si.hStdOutput = getStdHandle(STD_OUTPUT_HANDLE) - when useWinUnicode: - var c = newWideCString(command) - var res = winlean.createProcessW(nil, c, nil, nil, 0, - NORMAL_PRIORITY_CLASS, nil, nil, si, procInfo) - else: - var res = winlean.createProcessA(nil, command, nil, nil, 0, - NORMAL_PRIORITY_CLASS, nil, nil, si, procInfo) + var c = newWideCString(command) + var res = winlean.createProcessW(nil, c, nil, nil, 0, + NORMAL_PRIORITY_CLASS, nil, nil, si, procInfo) if res == 0: raiseOSError(osLastError()) else: diff --git a/lib/std/cmdline.nim b/lib/std/cmdline.nim index 3208da1b8..6788dacde 100644 --- a/lib/std/cmdline.nim +++ b/lib/std/cmdline.nim @@ -40,10 +40,7 @@ else: # Needed by windows in order to obtain the command line for targets # other than command line targets when defined(windows) and not weirdTarget: - when useWinUnicode: - template getCommandLine*(): untyped = getCommandLineW() - else: - template getCommandLine*(): untyped = getCommandLineA() + template getCommandLine*(): untyped = getCommandLineW() proc parseCmdLine*(c: string): seq[string] {. diff --git a/lib/std/private/oscommon.nim b/lib/std/private/oscommon.nim index 3aac58636..b747e33f1 100644 --- a/lib/std/private/oscommon.nim +++ b/lib/std/private/oscommon.nim @@ -47,23 +47,17 @@ else: when defined(windows) and not weirdTarget: - when useWinUnicode: - template wrapUnary*(varname, winApiProc, arg: untyped) = - var varname = winApiProc(newWideCString(arg)) + template wrapUnary*(varname, winApiProc, arg: untyped) = + var varname = winApiProc(newWideCString(arg)) - template wrapBinary*(varname, winApiProc, arg, arg2: untyped) = - var varname = winApiProc(newWideCString(arg), arg2) - proc findFirstFile*(a: string, b: var WIN32_FIND_DATA): Handle = - result = findFirstFileW(newWideCString(a), b) - template findNextFile*(a, b: untyped): untyped = findNextFileW(a, b) + template wrapBinary*(varname, winApiProc, arg, arg2: untyped) = + var varname = winApiProc(newWideCString(arg), arg2) + proc findFirstFile*(a: string, b: var WIN32_FIND_DATA): Handle = + result = findFirstFileW(newWideCString(a), b) + template findNextFile*(a, b: untyped): untyped = findNextFileW(a, b) - template getFilename*(f: untyped): untyped = - $cast[WideCString](addr(f.cFileName[0])) - else: - template findFirstFile*(a, b: untyped): untyped = findFirstFileA(a, b) - template findNextFile*(a, b: untyped): untyped = findNextFileA(a, b) - - template getFilename*(f: untyped): untyped = $cast[cstring](addr f.cFileName) + template getFilename*(f: untyped): untyped = + $cast[WideCString](addr(f.cFileName[0])) proc skipFindData*(f: WIN32_FIND_DATA): bool {.inline.} = # Note - takes advantage of null delimiter in the cstring @@ -104,12 +98,9 @@ proc tryMoveFSObject*(source, dest: string, isDir: bool): bool {.noWeirdTarget.} ## In case of other errors `OSError` is raised. ## Returns true in case of success. when defined(windows): - when useWinUnicode: - let s = newWideCString(source) - let d = newWideCString(dest) - result = moveFileExW(s, d, MOVEFILE_COPY_ALLOWED or MOVEFILE_REPLACE_EXISTING) != 0'i32 - else: - result = moveFileExA(source, dest, MOVEFILE_COPY_ALLOWED or MOVEFILE_REPLACE_EXISTING) != 0'i32 + let s = newWideCString(source) + let d = newWideCString(dest) + result = moveFileExW(s, d, MOVEFILE_COPY_ALLOWED or MOVEFILE_REPLACE_EXISTING) != 0'i32 else: result = c_rename(source, dest) == 0'i32 @@ -137,10 +128,7 @@ proc fileExists*(filename: string): bool {.rtl, extern: "nos$1", ## * `dirExists proc`_ ## * `symlinkExists proc`_ when defined(windows): - when useWinUnicode: - wrapUnary(a, getFileAttributesW, filename) - else: - var a = getFileAttributesA(filename) + wrapUnary(a, getFileAttributesW, filename) if a != -1'i32: result = (a and FILE_ATTRIBUTE_DIRECTORY) == 0'i32 else: @@ -157,10 +145,7 @@ proc dirExists*(dir: string): bool {.rtl, extern: "nos$1", tags: [ReadDirEffect] ## * `fileExists proc`_ ## * `symlinkExists proc`_ when defined(windows): - when useWinUnicode: - wrapUnary(a, getFileAttributesW, dir) - else: - var a = getFileAttributesA(dir) + wrapUnary(a, getFileAttributesW, dir) if a != -1'i32: result = (a and FILE_ATTRIBUTE_DIRECTORY) != 0'i32 else: @@ -178,10 +163,7 @@ proc symlinkExists*(link: string): bool {.rtl, extern: "nos$1", ## * `fileExists proc`_ ## * `dirExists proc`_ when defined(windows): - when useWinUnicode: - wrapUnary(a, getFileAttributesW, link) - else: - var a = getFileAttributesA(link) + wrapUnary(a, getFileAttributesW, link) if a != -1'i32: # xxx see: bug #16784 (bug9); checking `IO_REPARSE_TAG_SYMLINK` # may also be needed. @@ -197,15 +179,8 @@ when defined(windows) and not weirdTarget: flags = flags or FILE_FLAG_OPEN_REPARSE_POINT let access = if writeAccess: GENERIC_WRITE else: 0'i32 - when useWinUnicode: - result = createFileW( - newWideCString(path), access, - FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE, - nil, OPEN_EXISTING, flags, 0 - ) - else: - result = createFileA( - path, access, - FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE, - nil, OPEN_EXISTING, flags, 0 - ) + result = createFileW( + newWideCString(path), access, + FILE_SHARE_DELETE or FILE_SHARE_READ or FILE_SHARE_WRITE, + nil, OPEN_EXISTING, flags, 0 + ) diff --git a/lib/std/private/osdirs.nim b/lib/std/private/osdirs.nim index 4af418ead..a4318367d 100644 --- a/lib/std/private/osdirs.nim +++ b/lib/std/private/osdirs.nim @@ -328,10 +328,7 @@ iterator walkDirRec*(dir: string, proc rawRemoveDir(dir: string) {.noWeirdTarget.} = when defined(windows): - when useWinUnicode: - wrapUnary(res, removeDirectoryW, dir) - else: - var res = removeDirectoryA(dir) + wrapUnary(res, removeDirectoryW, dir) let lastError = osLastError() if res == 0'i32 and lastError.int32 != 3'i32 and lastError.int32 != 18'i32 and lastError.int32 != 2'i32: @@ -396,10 +393,7 @@ proc rawCreateDir(dir: string): bool {.noWeirdTarget.} = #echo res raiseOSError(osLastError(), dir) else: - when useWinUnicode: - wrapUnary(res, createDirectoryW, dir) - else: - let res = createDirectoryA(dir) + wrapUnary(res, createDirectoryW, dir) if res != 0'i32: result = true @@ -561,10 +555,7 @@ proc setCurrentDir*(newDir: string) {.inline, tags: [], noWeirdTarget.} = ## * `getTempDir proc`_ ## * `getCurrentDir proc`_ when defined(windows): - when useWinUnicode: - if setCurrentDirectoryW(newWideCString(newDir)) == 0'i32: - raiseOSError(osLastError(), newDir) - else: - if setCurrentDirectoryA(newDir) == 0'i32: raiseOSError(osLastError(), newDir) + if setCurrentDirectoryW(newWideCString(newDir)) == 0'i32: + raiseOSError(osLastError(), newDir) else: if chdir(newDir) != 0'i32: raiseOSError(osLastError(), newDir) diff --git a/lib/std/private/osfiles.nim b/lib/std/private/osfiles.nim index a7a595d97..7f822ffcc 100644 --- a/lib/std/private/osfiles.nim +++ b/lib/std/private/osfiles.nim @@ -84,10 +84,7 @@ proc getFilePermissions*(filename: string): set[FilePermission] {. if (a.st_mode and S_IWOTH.Mode) != 0.Mode: result.incl(fpOthersWrite) if (a.st_mode and S_IXOTH.Mode) != 0.Mode: result.incl(fpOthersExec) else: - when useWinUnicode: - wrapUnary(res, getFileAttributesW, filename) - else: - var res = getFileAttributesA(filename) + wrapUnary(res, getFileAttributesW, filename) if res == -1'i32: raiseOSError(osLastError(), filename) if (res and FILE_ATTRIBUTE_READONLY) != 0'i32: result = {fpUserExec, fpUserRead, fpGroupExec, fpGroupRead, @@ -136,19 +133,13 @@ proc setFilePermissions*(filename: string, permissions: set[FilePermission], if chmod(filename, cast[Mode](p)) != 0: raiseOSError(osLastError(), $(filename, permissions)) else: - when useWinUnicode: - wrapUnary(res, getFileAttributesW, filename) - else: - var res = getFileAttributesA(filename) + wrapUnary(res, getFileAttributesW, filename) if res == -1'i32: raiseOSError(osLastError(), filename) if fpUserWrite in permissions: res = res and not FILE_ATTRIBUTE_READONLY else: res = res or FILE_ATTRIBUTE_READONLY - when useWinUnicode: - wrapBinary(res2, setFileAttributesW, filename, res) - else: - var res2 = setFileAttributesA(filename, res) + wrapBinary(res2, setFileAttributesW, filename, res) if res2 == - 1'i32: raiseOSError(osLastError(), $(filename, permissions)) @@ -221,14 +212,10 @@ proc copyFile*(source, dest: string, options = {cfSymlinkFollow}) {.rtl, if isSymlink and (cfSymlinkIgnore in options or defined(windows)): return when defined(windows): - when useWinUnicode: - let s = newWideCString(source) - let d = newWideCString(dest) - if copyFileW(s, d, 0'i32) == 0'i32: - raiseOSError(osLastError(), $(source, dest)) - else: - if copyFileA(source, dest, 0'i32) == 0'i32: - raiseOSError(osLastError(), $(source, dest)) + let s = newWideCString(source) + let d = newWideCString(dest) + if copyFileW(s, d, 0'i32) == 0'i32: + raiseOSError(osLastError(), $(source, dest)) else: if isSymlink and cfSymlinkAsIs in options: createSymlink(expandSymlink(source), dest) @@ -334,14 +321,9 @@ when not declared(ENOENT) and not defined(windows): var ENOENT {.importc, header: "".}: cint when defined(windows) and not weirdTarget: - when useWinUnicode: - template deleteFile(file: untyped): untyped = deleteFileW(file) - template setFileAttributes(file, attrs: untyped): untyped = - setFileAttributesW(file, attrs) - else: - template deleteFile(file: untyped): untyped = deleteFileA(file) - template setFileAttributes(file, attrs: untyped): untyped = - setFileAttributesA(file, attrs) + template deleteFile(file: untyped): untyped = deleteFileW(file) + template setFileAttributes(file, attrs: untyped): untyped = + setFileAttributesW(file, attrs) proc tryRemoveFile*(file: string): bool {.rtl, extern: "nos$1", tags: [WriteDirEffect], noWeirdTarget.} = ## Removes the `file`. @@ -358,10 +340,7 @@ proc tryRemoveFile*(file: string): bool {.rtl, extern: "nos$1", tags: [WriteDirE ## * `moveFile proc`_ result = true when defined(windows): - when useWinUnicode: - let f = newWideCString(file) - else: - let f = file + let f = newWideCString(file) if deleteFile(f) == 0: result = false let err = getLastError() diff --git a/lib/std/private/ospaths2.nim b/lib/std/private/ospaths2.nim index 78a806675..612003023 100644 --- a/lib/std/private/ospaths2.nim +++ b/lib/std/private/ospaths2.nim @@ -849,30 +849,17 @@ when not defined(nimscript): doAssert false, "use -d:nodejs to have `getCurrentDir` defined" elif defined(windows): var bufsize = MAX_PATH.int32 - when useWinUnicode: - var res = newWideCString("", bufsize) - while true: - var L = getCurrentDirectoryW(bufsize, res) - if L == 0'i32: - raiseOSError(osLastError()) - elif L > bufsize: - res = newWideCString("", L) - bufsize = L - else: - result = res$L - break - else: - result = newString(bufsize) - while true: - var L = getCurrentDirectoryA(bufsize, result) - if L == 0'i32: - raiseOSError(osLastError()) - elif L > bufsize: - result = newString(L) - bufsize = L - else: - setLen(result, L) - break + var res = newWideCString("", bufsize) + while true: + var L = getCurrentDirectoryW(bufsize, res) + if L == 0'i32: + raiseOSError(osLastError()) + elif L > bufsize: + res = newWideCString("", L) + bufsize = L + else: + result = res$L + break else: var bufsize = 1024 # should be enough result = newString(bufsize) diff --git a/lib/std/private/ossymlinks.nim b/lib/std/private/ossymlinks.nim index 6b2de6237..18737b8b5 100644 --- a/lib/std/private/ossymlinks.nim +++ b/lib/std/private/ossymlinks.nim @@ -48,14 +48,10 @@ proc createSymlink*(src, dest: string) {.noWeirdTarget.} = const SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE = 2 # allows anyone with developer mode on to create a link let flag = dirExists(src).int32 or SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE - when useWinUnicode: - var wSrc = newWideCString(src) - var wDst = newWideCString(dest) - if createSymbolicLinkW(wDst, wSrc, flag) == 0 or getLastError() != 0: - raiseOSError(osLastError(), $(src, dest)) - else: - if createSymbolicLinkA(dest, src, flag) == 0 or getLastError() != 0: - raiseOSError(osLastError(), $(src, dest)) + var wSrc = newWideCString(src) + var wDst = newWideCString(dest) + if createSymbolicLinkW(wDst, wSrc, flag) == 0 or getLastError() != 0: + raiseOSError(osLastError(), $(src, dest)) else: if symlink(src, dest) != 0: raiseOSError(osLastError(), $(src, dest)) diff --git a/lib/std/syncio.nim b/lib/std/syncio.nim index 83b2d6d13..77d4fa04c 100644 --- a/lib/std/syncio.nim +++ b/lib/std/syncio.nim @@ -390,7 +390,7 @@ proc readLine*(f: File, line: var string): bool {.tags: [ReadIOEffect], proc c_memchr(s: pointer, c: cint, n: csize_t): pointer {. importc: "memchr", header: "".} - when defined(windows) and not defined(useWinAnsi): + when defined(windows): proc readConsole(hConsoleInput: FileHandle, lpBuffer: pointer, nNumberOfCharsToRead: int32, lpNumberOfCharsRead: ptr int32, @@ -612,7 +612,7 @@ proc writeLine*[Ty](f: File, x: varargs[Ty, `$`]) {.inline, # interface to the C procs: -when defined(windows) and not defined(useWinAnsi): +when defined(windows): when defined(cpp): proc wfopen(filename, mode: WideCString): pointer {. importcpp: "_wfopen((const wchar_t*)#, (const wchar_t*)#)", nodecl.} diff --git a/lib/windows/winlean.nim b/lib/windows/winlean.nim index 230f963f0..f8c6ce021 100644 --- a/lib/windows/winlean.nim +++ b/lib/windows/winlean.nim @@ -21,13 +21,7 @@ when defined(nimPreviewSlimSystem): from std/syncio import FileHandle import std/widestrs -const - useWinUnicode* = not defined(useWinAnsi) - -when useWinUnicode: - type WinChar* = Utf16Char -else: - type WinChar* = char +type WinChar* = Utf16Char # See https://docs.microsoft.com/en-us/windows/win32/winprog/windows-data-types type @@ -184,26 +178,14 @@ proc peekNamedPipe*(hNamedPipe: Handle, lpBuffer: pointer=nil, lpBytesLeftThisMessage: ptr int32 = nil): bool {. stdcall, dynlib: "kernel32", importc: "PeekNamedPipe".} -when useWinUnicode: - proc createProcessW*(lpApplicationName, lpCommandLine: WideCString, - lpProcessAttributes: ptr SECURITY_ATTRIBUTES, - lpThreadAttributes: ptr SECURITY_ATTRIBUTES, - bInheritHandles: WINBOOL, dwCreationFlags: int32, - lpEnvironment, lpCurrentDirectory: WideCString, - lpStartupInfo: var STARTUPINFO, - lpProcessInformation: var PROCESS_INFORMATION): WINBOOL{. - stdcall, dynlib: "kernel32", importc: "CreateProcessW", sideEffect.} - -else: - proc createProcessA*(lpApplicationName, lpCommandLine: cstring, - lpProcessAttributes: ptr SECURITY_ATTRIBUTES, - lpThreadAttributes: ptr SECURITY_ATTRIBUTES, - bInheritHandles: WINBOOL, dwCreationFlags: int32, - lpEnvironment: pointer, lpCurrentDirectory: cstring, - lpStartupInfo: var STARTUPINFO, - lpProcessInformation: var PROCESS_INFORMATION): WINBOOL{. - stdcall, dynlib: "kernel32", importc: "CreateProcessA", sideEffect.} - +proc createProcessW*(lpApplicationName, lpCommandLine: WideCString, + lpProcessAttributes: ptr SECURITY_ATTRIBUTES, + lpThreadAttributes: ptr SECURITY_ATTRIBUTES, + bInheritHandles: WINBOOL, dwCreationFlags: int32, + lpEnvironment, lpCurrentDirectory: WideCString, + lpStartupInfo: var STARTUPINFO, + lpProcessInformation: var PROCESS_INFORMATION): WINBOOL{. + stdcall, dynlib: "kernel32", importc: "CreateProcessW", sideEffect.} proc suspendThread*(hThread: Handle): int32 {.stdcall, dynlib: "kernel32", importc: "SuspendThread", sideEffect.} @@ -232,67 +214,37 @@ proc getLastError*(): int32 {.importc: "GetLastError", proc setLastError*(error: int32) {.importc: "SetLastError", stdcall, dynlib: "kernel32", sideEffect.} -when useWinUnicode: - proc formatMessageW*(dwFlags: int32, lpSource: pointer, - dwMessageId, dwLanguageId: int32, - lpBuffer: pointer, nSize: int32, - arguments: pointer): int32 {. - importc: "FormatMessageW", stdcall, dynlib: "kernel32".} -else: - proc formatMessageA*(dwFlags: int32, lpSource: pointer, +proc formatMessageW*(dwFlags: int32, lpSource: pointer, dwMessageId, dwLanguageId: int32, lpBuffer: pointer, nSize: int32, arguments: pointer): int32 {. - importc: "FormatMessageA", stdcall, dynlib: "kernel32".} + importc: "FormatMessageW", stdcall, dynlib: "kernel32".} proc localFree*(p: pointer) {. importc: "LocalFree", stdcall, dynlib: "kernel32".} -when useWinUnicode: - proc getCurrentDirectoryW*(nBufferLength: int32, - lpBuffer: WideCString): int32 {. - importc: "GetCurrentDirectoryW", dynlib: "kernel32", stdcall, sideEffect.} - proc setCurrentDirectoryW*(lpPathName: WideCString): int32 {. - importc: "SetCurrentDirectoryW", dynlib: "kernel32", stdcall, sideEffect.} - proc createDirectoryW*(pathName: WideCString, security: pointer=nil): int32 {. - importc: "CreateDirectoryW", dynlib: "kernel32", stdcall, sideEffect.} - proc removeDirectoryW*(lpPathName: WideCString): int32 {. - importc: "RemoveDirectoryW", dynlib: "kernel32", stdcall, sideEffect.} - proc setEnvironmentVariableW*(lpName, lpValue: WideCString): int32 {. - stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableW", sideEffect.} +proc getCurrentDirectoryW*(nBufferLength: int32, + lpBuffer: WideCString): int32 {. + importc: "GetCurrentDirectoryW", dynlib: "kernel32", stdcall, sideEffect.} +proc setCurrentDirectoryW*(lpPathName: WideCString): int32 {. + importc: "SetCurrentDirectoryW", dynlib: "kernel32", stdcall, sideEffect.} +proc createDirectoryW*(pathName: WideCString, security: pointer=nil): int32 {. + importc: "CreateDirectoryW", dynlib: "kernel32", stdcall, sideEffect.} +proc removeDirectoryW*(lpPathName: WideCString): int32 {. + importc: "RemoveDirectoryW", dynlib: "kernel32", stdcall, sideEffect.} +proc setEnvironmentVariableW*(lpName, lpValue: WideCString): int32 {. + stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableW", sideEffect.} - proc getModuleFileNameW*(handle: Handle, buf: WideCString, - size: int32): int32 {.importc: "GetModuleFileNameW", - dynlib: "kernel32", stdcall.} -else: - proc getCurrentDirectoryA*(nBufferLength: int32, lpBuffer: cstring): int32 {. - importc: "GetCurrentDirectoryA", dynlib: "kernel32", stdcall, sideEffect.} - proc setCurrentDirectoryA*(lpPathName: cstring): int32 {. - importc: "SetCurrentDirectoryA", dynlib: "kernel32", stdcall, sideEffect.} - proc createDirectoryA*(pathName: cstring, security: pointer=nil): int32 {. - importc: "CreateDirectoryA", dynlib: "kernel32", stdcall, sideEffect.} - proc removeDirectoryA*(lpPathName: cstring): int32 {. - importc: "RemoveDirectoryA", dynlib: "kernel32", stdcall, sideEffect.} - proc setEnvironmentVariableA*(lpName, lpValue: cstring): int32 {. - stdcall, dynlib: "kernel32", importc: "SetEnvironmentVariableA", sideEffect.} +proc getModuleFileNameW*(handle: Handle, buf: WideCString, + size: int32): int32 {.importc: "GetModuleFileNameW", + dynlib: "kernel32", stdcall.} - proc getModuleFileNameA*(handle: Handle, buf: cstring, size: int32): int32 {. - importc: "GetModuleFileNameA", dynlib: "kernel32", stdcall.} - -when useWinUnicode: - proc createSymbolicLinkW*(lpSymlinkFileName, lpTargetFileName: WideCString, - flags: DWORD): int32 {. - importc:"CreateSymbolicLinkW", dynlib: "kernel32", stdcall, sideEffect.} - proc createHardLinkW*(lpFileName, lpExistingFileName: WideCString, - security: pointer=nil): int32 {. - importc:"CreateHardLinkW", dynlib: "kernel32", stdcall, sideEffect.} -else: - proc createSymbolicLinkA*(lpSymlinkFileName, lpTargetFileName: cstring, - flags: DWORD): int32 {. - importc:"CreateSymbolicLinkA", dynlib: "kernel32", stdcall, sideEffect.} - proc createHardLinkA*(lpFileName, lpExistingFileName: cstring, - security: pointer=nil): int32 {. - importc:"CreateHardLinkA", dynlib: "kernel32", stdcall, sideEffect.} +proc createSymbolicLinkW*(lpSymlinkFileName, lpTargetFileName: WideCString, + flags: DWORD): int32 {. + importc:"CreateSymbolicLinkW", dynlib: "kernel32", stdcall, sideEffect.} +proc createHardLinkW*(lpFileName, lpExistingFileName: WideCString, + security: pointer=nil): int32 {. + importc:"CreateHardLinkW", dynlib: "kernel32", stdcall, sideEffect.} const FILE_ATTRIBUTE_READONLY* = 0x00000001'i32 @@ -343,84 +295,45 @@ type cFileName*: array[0..(MAX_PATH) - 1, WinChar] cAlternateFileName*: array[0..13, WinChar] -when useWinUnicode: - proc findFirstFileW*(lpFileName: WideCString, - lpFindFileData: var WIN32_FIND_DATA): Handle {. - stdcall, dynlib: "kernel32", importc: "FindFirstFileW", sideEffect.} - proc findNextFileW*(hFindFile: Handle, - lpFindFileData: var WIN32_FIND_DATA): int32 {. - stdcall, dynlib: "kernel32", importc: "FindNextFileW", sideEffect.} -else: - proc findFirstFileA*(lpFileName: cstring, - lpFindFileData: var WIN32_FIND_DATA): Handle {. - stdcall, dynlib: "kernel32", importc: "FindFirstFileA", sideEffect.} - proc findNextFileA*(hFindFile: Handle, - lpFindFileData: var WIN32_FIND_DATA): int32 {. - stdcall, dynlib: "kernel32", importc: "FindNextFileA", sideEffect.} +proc findFirstFileW*(lpFileName: WideCString, + lpFindFileData: var WIN32_FIND_DATA): Handle {. + stdcall, dynlib: "kernel32", importc: "FindFirstFileW", sideEffect.} +proc findNextFileW*(hFindFile: Handle, + lpFindFileData: var WIN32_FIND_DATA): int32 {. + stdcall, dynlib: "kernel32", importc: "FindNextFileW", sideEffect.} proc findClose*(hFindFile: Handle) {.stdcall, dynlib: "kernel32", importc: "FindClose".} -when useWinUnicode: - proc getFullPathNameW*(lpFileName: WideCString, nBufferLength: int32, - lpBuffer: WideCString, - lpFilePart: var WideCString): int32 {. +proc getFullPathNameW*(lpFileName: WideCString, nBufferLength: int32, + lpBuffer: WideCString, + lpFilePart: var WideCString): int32 {. + stdcall, dynlib: "kernel32", + importc: "GetFullPathNameW", sideEffect.} +proc getFileAttributesW*(lpFileName: WideCString): int32 {. stdcall, dynlib: "kernel32", - importc: "GetFullPathNameW", sideEffect.} - proc getFileAttributesW*(lpFileName: WideCString): int32 {. - stdcall, dynlib: "kernel32", - importc: "GetFileAttributesW", sideEffect.} - proc setFileAttributesW*(lpFileName: WideCString, - dwFileAttributes: int32): WINBOOL {. - stdcall, dynlib: "kernel32", importc: "SetFileAttributesW", sideEffect.} + importc: "GetFileAttributesW", sideEffect.} +proc setFileAttributesW*(lpFileName: WideCString, + dwFileAttributes: int32): WINBOOL {. + stdcall, dynlib: "kernel32", importc: "SetFileAttributesW", sideEffect.} - proc copyFileW*(lpExistingFileName, lpNewFileName: WideCString, - bFailIfExists: WINBOOL): WINBOOL {. - importc: "CopyFileW", stdcall, dynlib: "kernel32", sideEffect.} +proc copyFileW*(lpExistingFileName, lpNewFileName: WideCString, + bFailIfExists: WINBOOL): WINBOOL {. + importc: "CopyFileW", stdcall, dynlib: "kernel32", sideEffect.} - proc moveFileW*(lpExistingFileName, lpNewFileName: WideCString): WINBOOL {. - importc: "MoveFileW", stdcall, dynlib: "kernel32", sideEffect.} - proc moveFileExW*(lpExistingFileName, lpNewFileName: WideCString, - flags: DWORD): WINBOOL {. - importc: "MoveFileExW", stdcall, dynlib: "kernel32", sideEffect.} +proc moveFileW*(lpExistingFileName, lpNewFileName: WideCString): WINBOOL {. + importc: "MoveFileW", stdcall, dynlib: "kernel32", sideEffect.} +proc moveFileExW*(lpExistingFileName, lpNewFileName: WideCString, + flags: DWORD): WINBOOL {. + importc: "MoveFileExW", stdcall, dynlib: "kernel32", sideEffect.} - proc getEnvironmentStringsW*(): WideCString {. - stdcall, dynlib: "kernel32", importc: "GetEnvironmentStringsW", sideEffect.} - proc freeEnvironmentStringsW*(para1: WideCString): int32 {. - stdcall, dynlib: "kernel32", importc: "FreeEnvironmentStringsW", sideEffect.} +proc getEnvironmentStringsW*(): WideCString {. + stdcall, dynlib: "kernel32", importc: "GetEnvironmentStringsW", sideEffect.} +proc freeEnvironmentStringsW*(para1: WideCString): int32 {. + stdcall, dynlib: "kernel32", importc: "FreeEnvironmentStringsW", sideEffect.} - proc getCommandLineW*(): WideCString {.importc: "GetCommandLineW", - stdcall, dynlib: "kernel32", sideEffect.} - -else: - proc getFullPathNameA*(lpFileName: cstring, nBufferLength: int32, - lpBuffer: cstring, lpFilePart: var cstring): int32 {. - stdcall, dynlib: "kernel32", - importc: "GetFullPathNameA", sideEffect.} - proc getFileAttributesA*(lpFileName: cstring): int32 {. - stdcall, dynlib: "kernel32", - importc: "GetFileAttributesA", sideEffect.} - proc setFileAttributesA*(lpFileName: cstring, - dwFileAttributes: int32): WINBOOL {. - stdcall, dynlib: "kernel32", importc: "SetFileAttributesA", sideEffect.} - - proc copyFileA*(lpExistingFileName, lpNewFileName: cstring, - bFailIfExists: cint): cint {. - importc: "CopyFileA", stdcall, dynlib: "kernel32", sideEffect.} - - proc moveFileA*(lpExistingFileName, lpNewFileName: cstring): WINBOOL {. - importc: "MoveFileA", stdcall, dynlib: "kernel32", sideEffect.} - proc moveFileExA*(lpExistingFileName, lpNewFileName: cstring, - flags: DWORD): WINBOOL {. - importc: "MoveFileExA", stdcall, dynlib: "kernel32", sideEffect.} - - proc getEnvironmentStringsA*(): cstring {. - stdcall, dynlib: "kernel32", importc: "GetEnvironmentStringsA", sideEffect.} - proc freeEnvironmentStringsA*(para1: cstring): int32 {. - stdcall, dynlib: "kernel32", importc: "FreeEnvironmentStringsA", sideEffect.} - - proc getCommandLineA*(): cstring {. - importc: "GetCommandLineA", stdcall, dynlib: "kernel32", sideEffect.} +proc getCommandLineW*(): WideCString {.importc: "GetCommandLineW", + stdcall, dynlib: "kernel32", sideEffect.} proc rdFileTime*(f: FILETIME): int64 = result = int64(cast[uint32](f.dwLowDateTime)) or (int64(cast[uint32](f.dwHighDateTime)) shl 32) @@ -434,17 +347,10 @@ proc getSystemTimeAsFileTime*(lpSystemTimeAsFileTime: var FILETIME) {. proc sleep*(dwMilliseconds: int32){.stdcall, dynlib: "kernel32", importc: "Sleep", sideEffect.} -when useWinUnicode: - proc shellExecuteW*(hwnd: Handle, lpOperation, lpFile, - lpParameters, lpDirectory: WideCString, - nShowCmd: int32): Handle{. - stdcall, dynlib: "shell32.dll", importc: "ShellExecuteW", sideEffect.} - -else: - proc shellExecuteA*(hwnd: Handle, lpOperation, lpFile, - lpParameters, lpDirectory: cstring, - nShowCmd: int32): Handle{. - stdcall, dynlib: "shell32.dll", importc: "ShellExecuteA", sideEffect.} +proc shellExecuteW*(hwnd: Handle, lpOperation, lpFile, + lpParameters, lpDirectory: WideCString, + nShowCmd: int32): Handle{. + stdcall, dynlib: "shell32.dll", importc: "ShellExecuteW", sideEffect.} proc getFileInformationByHandle*(hFile: Handle, lpFileInformation: ptr BY_HANDLE_FILE_INFORMATION): WINBOOL{. @@ -794,13 +700,6 @@ proc createFileMappingW*(hFile: Handle, lpName: pointer): Handle {. stdcall, dynlib: "kernel32", importc: "CreateFileMappingW".} -when not useWinUnicode: - proc createFileMappingA*(hFile: Handle, - lpFileMappingAttributes: pointer, - flProtect, dwMaximumSizeHigh: DWORD, - dwMaximumSizeLow: DWORD, lpName: cstring): Handle {. - stdcall, dynlib: "kernel32", importc: "CreateFileMappingA".} - proc unmapViewOfFile*(lpBaseAddress: pointer): WINBOOL {.stdcall, dynlib: "kernel32", importc: "UnmapViewOfFile".} @@ -976,7 +875,7 @@ proc inet_ntop*(family: cint, paddr: pointer, pStringBuffer: cstring, stringBufSize: int32): cstring {.stdcall.} = var ver: OSVERSIONINFO ver.dwOSVersionInfoSize = sizeof(ver).DWORD - let res = when useWinUnicode: getVersionExW(ver.addr) else: getVersionExA(ver.addr) + let res = getVersionExW(ver.addr) if res == 0: result = nil elif ver.dwMajorVersion >= 6: @@ -1060,16 +959,10 @@ proc openProcess*(dwDesiredAccess: DWORD, bInheritHandle: WINBOOL, dwProcessId: DWORD): Handle {.stdcall, dynlib: "kernel32", importc: "OpenProcess".} -when defined(useWinAnsi): - proc createEvent*(lpEventAttributes: ptr SECURITY_ATTRIBUTES, - bManualReset: DWORD, bInitialState: DWORD, - lpName: cstring): Handle - {.stdcall, dynlib: "kernel32", importc: "CreateEventA".} -else: - proc createEvent*(lpEventAttributes: ptr SECURITY_ATTRIBUTES, - bManualReset: DWORD, bInitialState: DWORD, - lpName: ptr Utf16Char): Handle - {.stdcall, dynlib: "kernel32", importc: "CreateEventW".} +proc createEvent*(lpEventAttributes: ptr SECURITY_ATTRIBUTES, + bManualReset: DWORD, bInitialState: DWORD, + lpName: ptr Utf16Char): Handle + {.stdcall, dynlib: "kernel32", importc: "CreateEventW".} proc setEvent*(hEvent: Handle): cint {.stdcall, dynlib: "kernel32", importc: "SetEvent".} @@ -1110,14 +1003,9 @@ type uChar*: int16 dwControlKeyState*: DWORD -when defined(useWinAnsi): - proc readConsoleInput*(hConsoleInput: Handle, lpBuffer: pointer, nLength: cint, - lpNumberOfEventsRead: ptr cint): cint - {.stdcall, dynlib: "kernel32", importc: "ReadConsoleInputA".} -else: - proc readConsoleInput*(hConsoleInput: Handle, lpBuffer: pointer, nLength: cint, - lpNumberOfEventsRead: ptr cint): cint - {.stdcall, dynlib: "kernel32", importc: "ReadConsoleInputW".} +proc readConsoleInput*(hConsoleInput: Handle, lpBuffer: pointer, nLength: cint, + lpNumberOfEventsRead: ptr cint): cint + {.stdcall, dynlib: "kernel32", importc: "ReadConsoleInputW".} type LPFIBER_START_ROUTINE* = proc (param: pointer) {.stdcall.} From 50baf21eacfaf1c9d6949bf2b2e9f931b0e1509c Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Thu, 2 Mar 2023 08:36:02 +0100 Subject: [PATCH 3744/3768] =?UTF-8?q?fixes=20#20422;=20emit=20nimPrepareSt?= =?UTF-8?q?rMutationV2=20for=20toOpenArray=20to=20keep=20th=E2=80=A6=20(#2?= =?UTF-8?q?1459)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fixes #20422; emit nimPrepareStrMutationV2 for toOpenArray to keep the abstraction of mutable strings which have immutable string literals --- compiler/ccgcalls.nim | 4 +++- compiler/ccgexprs.nim | 5 ++++- tests/arc/topenarray.nim | 12 ++++++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/compiler/ccgcalls.nim b/compiler/ccgcalls.nim index 9993322fb..48e7fd290 100644 --- a/compiler/ccgcalls.nim +++ b/compiler/ccgcalls.nim @@ -156,7 +156,7 @@ proc reifiedOpenArray(n: PNode): bool {.inline.} = else: result = true -proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType): (Rope, Rope) = +proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType; prepareForMutation = false): (Rope, Rope) = var a, b, c: TLoc initLocExpr(p, q[1], a) initLocExpr(p, q[2], b) @@ -164,6 +164,8 @@ proc genOpenArraySlice(p: BProc; q: PNode; formalType, destType: PType): (Rope, # but first produce the required index checks: if optBoundsCheck in p.options: genBoundsCheck(p, a, b, c) + if prepareForMutation: + linefmt(p, cpsStmts, "#nimPrepareStrMutationV2($1);$n", [byRefLoc(p, a)]) let ty = skipTypes(a.t, abstractVar+{tyPtr}) let dest = getTypeDesc(p.module, destType) let lengthExpr = "($1)-($2)+1" % [rdLoc(c), rdLoc(b)] diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index fe7cb252e..f428324b7 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -2408,7 +2408,10 @@ proc genDispose(p: BProc; n: PNode) = lineCg(p, cpsStmts, ["#nimDestroyAndDispose($#)", rdLoc(a)]) proc genSlice(p: BProc; e: PNode; d: var TLoc) = - let (x, y) = genOpenArraySlice(p, e, e.typ, e.typ.lastSon) + let (x, y) = genOpenArraySlice(p, e, e.typ, e.typ.lastSon, + prepareForMutation = e[1].kind == nkHiddenDeref and + e[1].typ.skipTypes(abstractInst).kind == tyString and + p.config.selectedGC in {gcArc, gcOrc}) if d.k == locNone: getTemp(p, e.typ, d) linefmt(p, cpsStmts, "$1.Field0 = $2; $1.Field1 = $3;$n", [rdLoc(d), x, y]) when false: diff --git a/tests/arc/topenarray.nim b/tests/arc/topenarray.nim index 47c26a11f..0e45f3ec7 100644 --- a/tests/arc/topenarray.nim +++ b/tests/arc/topenarray.nim @@ -6,6 +6,8 @@ Nim ''' matrix: "--gc:arc -d:useMalloc; --gc:arc" """ +{.experimental: "views".} + block: # bug 18627 proc setPosition(params: openArray[string]) = for i in params.toOpenArray(0, params.len - 1): @@ -38,3 +40,13 @@ block: # bug #20954 var v: seq[int] echo len(toOpenArray(v, 20, 30)) + +# bug #20422 + +proc f(a: var string) = + var v = a.toOpenArray(1, 3) + v[0] = 'a' + +var a = "Hello" +f(a) +doAssert a == "Hallo" From 38d299dfc0c9f1315cf412aa76cb041642d62f07 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Thu, 2 Mar 2023 16:16:28 +0800 Subject: [PATCH 3745/3768] fixes #20139; hash types based on its path relative to its package path (#21274) [backport:1.6] * fixes #20139; hash types based on its path relative its project * add a test case * fixes procs * better implementation and test case --------- Co-authored-by: Andreas Rumpf --- compiler/ccgexprs.nim | 4 +- compiler/ccgstmts.nim | 6 +-- compiler/ccgtypes.nim | 38 +++++++-------- compiler/cgen.nim | 2 +- compiler/injectdestructors.nim | 2 +- compiler/jsgen.nim | 4 +- compiler/liftdestructors.nim | 4 +- compiler/pathutils.nim | 51 +++++++++++++++++++- compiler/sighashes.nim | 86 +++++++++++++++++----------------- compiler/vm.nim | 2 +- tests/ccgbugs/m1/defs.nim | 4 ++ tests/ccgbugs/m2/defs.nim | 4 ++ tests/ccgbugs/t20139.nim | 10 ++++ 13 files changed, 143 insertions(+), 74 deletions(-) create mode 100644 tests/ccgbugs/m1/defs.nim create mode 100644 tests/ccgbugs/m2/defs.nim create mode 100644 tests/ccgbugs/t20139.nim diff --git a/compiler/ccgexprs.nim b/compiler/ccgexprs.nim index f428324b7..a64a42219 100644 --- a/compiler/ccgexprs.nim +++ b/compiler/ccgexprs.nim @@ -1704,7 +1704,7 @@ proc genNewFinalize(p: BProc, e: PNode) = proc genOfHelper(p: BProc; dest: PType; a: Rope; info: TLineInfo; result: var Rope) = if optTinyRtti in p.config.globalOptions: - let token = $genDisplayElem(MD5Digest(hashType(dest))) + let token = $genDisplayElem(MD5Digest(hashType(dest, p.config))) appcg(p.module, result, "#isObjDisplayCheck($#.m_type, $#, $#)", [a, getObjDepth(dest), token]) else: # unfortunately 'genTypeInfoV1' sets tfObjHasKids as a side effect, so we @@ -2779,7 +2779,7 @@ proc upConv(p: BProc, n: PNode, d: var TLoc) = rdMType(p, a, nilCheck, r) if optTinyRtti in p.config.globalOptions: let checkFor = $getObjDepth(dest) - let token = $genDisplayElem(MD5Digest(hashType(dest))) + let token = $genDisplayElem(MD5Digest(hashType(dest, p.config))) if nilCheck != "": linefmt(p, cpsStmts, "if ($1 && !#isObjDisplayCheck($2, $3, $4)){ #raiseObjectConversionError(); ", [nilCheck, r, checkFor, token]) diff --git a/compiler/ccgstmts.nim b/compiler/ccgstmts.nim index db839b21b..7ade85b42 100644 --- a/compiler/ccgstmts.nim +++ b/compiler/ccgstmts.nim @@ -1081,7 +1081,7 @@ proc genTryCpp(p: BProc, t: PNode, d: var TLoc) = let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type" if optTinyRtti in p.config.globalOptions: let checkFor = $getObjDepth(typeNode.typ) - appcg(p.module, orExpr, "#isObjDisplayCheck(#nimBorrowCurrentException()->$1, $2, $3)", [memberName, checkFor, $genDisplayElem(MD5Digest(hashType(typeNode.typ)))]) + appcg(p.module, orExpr, "#isObjDisplayCheck(#nimBorrowCurrentException()->$1, $2, $3)", [memberName, checkFor, $genDisplayElem(MD5Digest(hashType(typeNode.typ, p.config)))]) else: let checkFor = genTypeInfoV1(p.module, typeNode.typ, typeNode.info) appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor]) @@ -1300,7 +1300,7 @@ proc genTryGoto(p: BProc; t: PNode; d: var TLoc) = let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type" if optTinyRtti in p.config.globalOptions: let checkFor = $getObjDepth(t[i][j].typ) - appcg(p.module, orExpr, "#isObjDisplayCheck(#nimBorrowCurrentException()->$1, $2, $3)", [memberName, checkFor, $genDisplayElem(MD5Digest(hashType(t[i][j].typ)))]) + appcg(p.module, orExpr, "#isObjDisplayCheck(#nimBorrowCurrentException()->$1, $2, $3)", [memberName, checkFor, $genDisplayElem(MD5Digest(hashType(t[i][j].typ, p.config)))]) else: let checkFor = genTypeInfoV1(p.module, t[i][j].typ, t[i][j].info) appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor]) @@ -1445,7 +1445,7 @@ proc genTrySetjmp(p: BProc, t: PNode, d: var TLoc) = let memberName = if p.module.compileToCpp: "m_type" else: "Sup.m_type" if optTinyRtti in p.config.globalOptions: let checkFor = $getObjDepth(t[i][j].typ) - appcg(p.module, orExpr, "#isObjDisplayCheck(#nimBorrowCurrentException()->$1, $2, $3)", [memberName, checkFor, $genDisplayElem(MD5Digest(hashType(t[i][j].typ)))]) + appcg(p.module, orExpr, "#isObjDisplayCheck(#nimBorrowCurrentException()->$1, $2, $3)", [memberName, checkFor, $genDisplayElem(MD5Digest(hashType(t[i][j].typ, p.config)))]) else: let checkFor = genTypeInfoV1(p.module, t[i][j].typ, t[i][j].info) appcg(p.module, orExpr, "#isObj(#nimBorrowCurrentException()->$1, $2)", [memberName, checkFor]) diff --git a/compiler/ccgtypes.nim b/compiler/ccgtypes.nim index e7677f2f2..98c1fc55f 100644 --- a/compiler/ccgtypes.nim +++ b/compiler/ccgtypes.nim @@ -43,14 +43,14 @@ proc fillBackendName(m: BModule; s: PSym) = result.add rope s.itemId.item if m.hcrOn: result.add "_" - result.add(idOrSig(s, m.module.name.s.mangle, m.sigConflicts)) + result.add(idOrSig(s, m.module.name.s.mangle, m.sigConflicts, m.config)) s.loc.r = result writeMangledName(m.ndi, s, m.config) proc fillParamName(m: BModule; s: PSym) = if s.loc.r == "": var res = s.name.s.mangle - res.add idOrSig(s, res, m.sigConflicts) + res.add idOrSig(s, res, m.sigConflicts, m.config) # Take into account if HCR is on because of the following scenario: # if a module gets imported and it has some more importc symbols in it, # some param names might receive the "_0" suffix to distinguish from what @@ -302,7 +302,7 @@ proc getSimpleTypeDesc(m: BModule, typ: PType): Rope = else: result = "" if result != "" and typ.isImportedType(): - let sig = hashType typ + let sig = hashType(typ, m.config) if cacheGetType(m.typeCache, sig) == "": m.typeCache[sig] = result @@ -362,10 +362,10 @@ proc getTypeDescWeak(m: BModule; t: PType; check: var IntSet; kind: TSymKind): R if isImportedCppType(etB) and t.kind == tyGenericInst: result = getTypeDescAux(m, t, check, kind) else: - result = getTypeForward(m, t, hashType(t)) + result = getTypeForward(m, t, hashType(t, m.config)) pushType(m, t) of tySequence: - let sig = hashType(t) + let sig = hashType(t, m.config) if optSeqDestructors in m.config.globalOptions: if skipTypes(etB[0], typedescInst).kind == tyEmpty: internalError(m.config, "cannot map the empty seq type to a C type") @@ -398,7 +398,7 @@ proc getSeqPayloadType(m: BModule; t: PType): Rope = #result = getTypeForward(m, t, hashType(t)) & "_Content" proc seqV2ContentType(m: BModule; t: PType; check: var IntSet) = - let sig = hashType(t) + let sig = hashType(t, m.config) let result = cacheGetType(m.typeCache, sig) if result == "": discard getTypeDescAux(m, t, check, skVar) @@ -664,7 +664,7 @@ proc resolveStarsInCppType(typ: PType, idx, stars: int): PType = else: result.elemType proc getOpenArrayDesc(m: BModule, t: PType, check: var IntSet; kind: TSymKind): Rope = - let sig = hashType(t) + let sig = hashType(t, m.config) if kind == skParam: result = getTypeDescWeak(m, t[0], check, kind) & "*" else: @@ -688,7 +688,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin # C type generation into an analysis and a code generation phase somehow. if t.sym != nil: useHeader(m, t.sym) if t != origTyp and origTyp.sym != nil: useHeader(m, origTyp.sym) - let sig = hashType(origTyp) + let sig = hashType(origTyp, m.config) defer: # defer is the simplest in this case if isImportedType(t) and not m.typeABICache.containsOrIncl(sig): @@ -717,7 +717,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin result = getTypeDescAux(m, et, check, kind) & star else: # no restriction! We have a forward declaration for structs - let name = getTypeForward(m, et, hashType et) + let name = getTypeForward(m, et, hashType(et, m.config)) result = name & star m.typeCache[sig] = result of tySequence: @@ -726,7 +726,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin m.typeCache[sig] = result else: # no restriction! We have a forward declaration for structs - let name = getTypeForward(m, et, hashType et) + let name = getTypeForward(m, et, hashType(et, m.config)) result = name & seqStar(m) & star m.typeCache[sig] = result pushType(m, et) @@ -893,7 +893,7 @@ proc getTypeDescAux(m: BModule, origTyp: PType, check: var IntSet; kind: TSymKin # Don't use the imported name as it may be scoped: 'Foo::SomeKind' result = rope("tySet_") t.lastSon.typeName(result) - result.add $t.lastSon.hashType + result.add $t.lastSon.hashType(m.config) m.typeCache[sig] = result if not isImportedType(t): let s = int(getSize(m.config, t)) @@ -1066,7 +1066,7 @@ proc discriminatorTableName(m: BModule, objtype: PType, d: PSym): Rope = objtype = objtype[0].skipTypes(abstractPtrs) if objtype.sym == nil: internalError(m.config, d.info, "anonymous obj with discriminator") - result = "NimDT_$1_$2" % [rope($hashType(objtype)), rope(d.name.s.mangle)] + result = "NimDT_$1_$2" % [rope($hashType(objtype, m.config)), rope(d.name.s.mangle)] proc rope(arg: Int128): Rope = rope($arg) @@ -1285,7 +1285,7 @@ proc genTypeInfo2Name(m: BModule; t: PType): Rope = result.add m.name.s & "." result.add it.sym.name.s else: - result = $hashType(it) + result = $hashType(it, m.config) result = makeCString(result) proc isTrivialProc(g: ModuleGraph; s: PSym): bool {.inline.} = getBody(g, s).len == 0 @@ -1329,14 +1329,14 @@ proc genDisplayElem(d: MD5Digest): uint32 = result += uint32(d[i]) result = result shl 8 -proc genDisplay(t: PType, depth: int): Rope = +proc genDisplay(m: BModule, t: PType, depth: int): Rope = result = Rope"{" var x = t var seqs = newSeq[string](depth+1) var i = 0 while x != nil: x = skipTypes(x, skipPtrs) - seqs[i] = $genDisplayElem(MD5Digest(hashType(x))) + seqs[i] = $genDisplayElem(MD5Digest(hashType(x, m.config))) x = x[0] inc i @@ -1376,7 +1376,7 @@ proc genTypeInfoV2OldImpl(m: BModule; t, origType: PType, name: Rope; info: TLin [name, getTypeDesc(m, t), rope(objDepth), rope(flags)]) if objDepth >= 0: - let objDisplay = genDisplay(t, objDepth) + let objDisplay = genDisplay(m, t, objDepth) let objDisplayStore = getTempName(m) m.s[cfsVars].addf("static $1 $2[$3] = $4;$n", [getTypeDesc(m, getSysType(m.g.graph, unknownLineInfo, tyUInt32), skVar), objDisplayStore, rope(objDepth+1), objDisplay]) addf(typeEntry, "$1.display = $2;$n", [name, rope(objDisplayStore)]) @@ -1408,7 +1408,7 @@ proc genTypeInfoV2Impl(m: BModule; t, origType: PType, name: Rope; info: TLineIn [getTypeDesc(m, t), rope(objDepth)]) if objDepth >= 0: - let objDisplay = genDisplay(t, objDepth) + let objDisplay = genDisplay(m, t, objDepth) let objDisplayStore = getTempName(m) m.s[cfsVars].addf("static NIM_CONST $1 $2[$3] = $4;$n", [getTypeDesc(m, getSysType(m.g.graph, unknownLineInfo, tyUInt32), skVar), objDisplayStore, rope(objDepth+1), objDisplay]) addf(typeEntry, ", .display = $1", [rope(objDisplayStore)]) @@ -1435,7 +1435,7 @@ proc genTypeInfoV2(m: BModule, t: PType; info: TLineInfo): Rope = let prefixTI = if m.hcrOn: "(" else: "(&" - let sig = hashType(origType) + let sig = hashType(origType, m.config) result = m.typeInfoMarkerV2.getOrDefault(sig) if result != "": return prefixTI.rope & result & ")".rope @@ -1509,7 +1509,7 @@ proc genTypeInfoV1(m: BModule, t: PType; info: TLineInfo): Rope = let prefixTI = if m.hcrOn: "(" else: "(&" - let sig = hashType(origType) + let sig = hashType(origType, m.config) result = m.typeInfoMarker.getOrDefault(sig) if result != "": return prefixTI.rope & result & ")".rope diff --git a/compiler/cgen.nim b/compiler/cgen.nim index c346a0b78..ab6e6559f 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -1630,7 +1630,7 @@ proc registerModuleToMain(g: BModuleList; m: BModule) = hcrModuleMeta.addf("\t\"\"};$n", []) hcrModuleMeta.addf("$nN_LIB_EXPORT N_NIMCALL(void**, HcrGetImportedModules)() { return (void**)hcr_module_list; }$n", []) hcrModuleMeta.addf("$nN_LIB_EXPORT N_NIMCALL(char*, HcrGetSigHash)() { return \"$1\"; }$n$n", - [($sigHash(m.module)).rope]) + [($sigHash(m.module, m.config)).rope]) if sfMainModule in m.module.flags: g.mainModProcs.add(hcrModuleMeta) g.mainModProcs.addf("static void* hcr_handle;$N", []) diff --git a/compiler/injectdestructors.nim b/compiler/injectdestructors.nim index 13141b765..d58f28289 100644 --- a/compiler/injectdestructors.nim +++ b/compiler/injectdestructors.nim @@ -217,7 +217,7 @@ proc genOp(c: var Con; t: PType; kind: TTypeAttachedOp; dest, ri: PNode): PNode var op = getAttachedOp(c.graph, t, kind) if op == nil or op.ast.isGenericRoutine: # give up and find the canonical type instead: - let h = sighashes.hashType(t, {CoType, CoConsiderOwned, CoDistinct}) + let h = sighashes.hashType(t, c.graph.config, {CoType, CoConsiderOwned, CoDistinct}) let canon = c.graph.canonTypes.getOrDefault(h) if canon != nil: op = getAttachedOp(c.graph, canon, kind) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 572d19b13..6619963b0 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -262,7 +262,7 @@ proc mangleName(m: BModule, s: PSym): Rope = if m.config.hcrOn: # When hot reloading is enabled, we must ensure that the names # of functions and types will be preserved across rebuilds: - result.add(idOrSig(s, m.module.name.s, m.sigConflicts)) + result.add(idOrSig(s, m.module.name.s, m.sigConflicts, m.config)) else: result.add("_") result.add(rope(s.id)) @@ -2813,7 +2813,7 @@ proc genModule(p: PProc, n: PNode) = if p.config.hcrOn and n.kind == nkStmtList: let moduleSym = p.module.module var moduleLoadedVar = rope(moduleSym.name.s) & "_loaded" & - idOrSig(moduleSym, moduleSym.name.s, p.module.sigConflicts) + idOrSig(moduleSym, moduleSym.name.s, p.module.sigConflicts, p.config) lineF(p, "var $1;$n", [moduleLoadedVar]) var inGuardedBlock = false diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index c7464d39e..d7b1f2daa 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -553,7 +553,7 @@ proc useSeqOrStrOp(c: var TLiftCtx; t: PType; body, x, y: PNode) = # operation here: var t = t if t.assignment == nil or t.destructor == nil: - let h = sighashes.hashType(t, {CoType, CoConsiderOwned, CoDistinct}) + let h = sighashes.hashType(t,c.g.config, {CoType, CoConsiderOwned, CoDistinct}) let canon = c.g.canonTypes.getOrDefault(h) if canon != nil: t = canon @@ -1113,7 +1113,7 @@ proc createTypeBoundOps(g: ModuleGraph; c: PContext; orig: PType; info: TLineInf let skipped = orig.skipTypes({tyGenericInst, tyAlias, tySink}) if isEmptyContainer(skipped) or skipped.kind == tyStatic: return - let h = sighashes.hashType(skipped, {CoType, CoConsiderOwned, CoDistinct}) + let h = sighashes.hashType(skipped, g.config, {CoType, CoConsiderOwned, CoDistinct}) var canon = g.canonTypes.getOrDefault(h) if canon == nil: g.canonTypes[h] = skipped diff --git a/compiler/pathutils.nim b/compiler/pathutils.nim index 1ef0143d5..d9779deab 100644 --- a/compiler/pathutils.nim +++ b/compiler/pathutils.nim @@ -10,7 +10,7 @@ ## Path handling utilities for Nim. Strictly typed code in order ## to avoid the never ending time sink in getting path handling right. -import os, pathnorm +import os, pathnorm, strutils when defined(nimPreviewSlimSystem): import std/[syncio, assertions] @@ -102,3 +102,52 @@ when true: proc addFileExt*(x: RelativeFile; ext: string): RelativeFile {.borrow.} proc writeFile*(x: AbsoluteFile; content: string) {.borrow.} + +proc skipHomeDir(x: string): int = + when defined(windows): + if x.continuesWith("Users/", len("C:/")): + result = 3 + else: + result = 0 + else: + if x.startsWith("/home/") or x.startsWith("/Users/"): + result = 3 + elif x.startsWith("/mnt/") and x.continuesWith("/Users/", len("/mnt/c")): + result = 5 + else: + result = 0 + +proc relevantPart(s: string; afterSlashX: int): string = + result = newStringOfCap(s.len - 8) + var slashes = afterSlashX + for i in 0.. + # //user/ + if not isAbsolute(x): + result = customPathImpl(canonSlashes(getCurrentDir() / x)) + else: + let slashes = skipHomeDir(x) + if slashes > 0: + result = "//user/" & relevantPart(x, slashes) + else: + result = x + +proc customPath*(x: string): string = + customPathImpl canonSlashes x diff --git a/compiler/sighashes.nim b/compiler/sighashes.nim index 3508a146a..7e2a0b660 100644 --- a/compiler/sighashes.nim +++ b/compiler/sighashes.nim @@ -9,10 +9,12 @@ ## Computes hash values for routine (proc, method etc) signatures. -import ast, tables, ropes, md5, modulegraphs +import ast, tables, ropes, md5, modulegraphs, options, msgs, packages, pathutils from hashes import Hash import types +import std/os + when defined(nimPreviewSlimSystem): import std/assertions @@ -42,8 +44,7 @@ type CoDistinct CoHashTypeInsideNode -proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) - +proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]; conf: ConfigRef) proc hashSym(c: var MD5Context, s: PSym) = if sfAnon in s.flags or s.kind == skGenericParam: c &= ":anon" @@ -54,20 +55,21 @@ proc hashSym(c: var MD5Context, s: PSym) = c &= "." it = it.owner -proc hashTypeSym(c: var MD5Context, s: PSym) = +proc hashTypeSym(c: var MD5Context, s: PSym; conf: ConfigRef) = if sfAnon in s.flags or s.kind == skGenericParam: c &= ":anon" else: var it = s + c &= customPath(conf.toFullPath(s.info)) while it != nil: if sfFromGeneric in it.flags and it.kind in routineKinds and it.typ != nil: - hashType c, it.typ, {CoProc} + hashType c, it.typ, {CoProc}, conf c &= it.name.s c &= "." it = it.owner -proc hashTree(c: var MD5Context, n: PNode; flags: set[ConsiderFlag]) = +proc hashTree(c: var MD5Context, n: PNode; flags: set[ConsiderFlag]; conf: ConfigRef) = if n == nil: c &= "\255" return @@ -82,7 +84,7 @@ proc hashTree(c: var MD5Context, n: PNode; flags: set[ConsiderFlag]) = of nkSym: hashSym(c, n.sym) if CoHashTypeInsideNode in flags and n.sym.typ != nil: - hashType(c, n.sym.typ, flags) + hashType(c, n.sym.typ, flags, conf) of nkCharLit..nkUInt64Lit: let v = n.intVal lowlevel v @@ -92,9 +94,9 @@ proc hashTree(c: var MD5Context, n: PNode; flags: set[ConsiderFlag]) = of nkStrLit..nkTripleStrLit: c &= n.strVal else: - for i in 0.. 0 and t[0] != nil: - hashType c, t[0], flags + hashType c, t[0], flags, conf of tyRef, tyPtr, tyGenericBody, tyVar: c &= char(t.kind) if t.sons.len > 0: - c.hashType t.lastSon, flags + c.hashType t.lastSon, flags, conf if tfVarIsPtr in t.flags: c &= ".varisptr" of tyFromExpr: c &= char(t.kind) - c.hashTree(t.n, {}) + c.hashTree(t.n, {}, conf) of tyTuple: c &= char(t.kind) if t.n != nil and CoType notin flags: @@ -199,19 +201,19 @@ proc hashType(c: var MD5Context, t: PType; flags: set[ConsiderFlag]) = assert(t.n[i].kind == nkSym) c &= t.n[i].sym.name.s c &= ':' - c.hashType(t[i], flags+{CoIgnoreRange}) + c.hashType(t[i], flags+{CoIgnoreRange}, conf) c &= ',' else: - for i in 0.. 1) order by hash; -proc hashType*(t: PType; flags: set[ConsiderFlag] = {CoType}): SigHash = +proc hashType*(t: PType; conf: ConfigRef; flags: set[ConsiderFlag] = {CoType}): SigHash = var c: MD5Context md5Init c - hashType c, t, flags+{CoOwnerSig} + hashType c, t, flags+{CoOwnerSig}, conf md5Final c, result.MD5Digest when defined(debugSigHashes): db.exec(sql"INSERT OR IGNORE INTO sighashes(type, hash) VALUES (?, ?)", typeToString(t), $result) -proc hashProc*(s: PSym): SigHash = +proc hashProc*(s: PSym; conf: ConfigRef): SigHash = var c: MD5Context md5Init c - hashType c, s.typ, {CoProc} + hashType c, s.typ, {CoProc}, conf var m = s while m.kind != skModule: m = m.owner @@ -315,9 +317,9 @@ proc hashOwner*(s: PSym): SigHash = md5Final c, result.MD5Digest -proc sigHash*(s: PSym): SigHash = +proc sigHash*(s: PSym; conf: ConfigRef): SigHash = if s.kind in routineKinds and s.typ != nil: - result = hashProc(s) + result = hashProc(s, conf) else: result = hashNonProc(s) @@ -378,7 +380,7 @@ proc symBodyDigest*(graph: ModuleGraph, sym: PSym): SigHash = var c: MD5Context md5Init(c) - c.hashType(sym.typ, {CoProc}) + c.hashType(sym.typ, {CoProc}, graph.config) c &= char(sym.kind) c.md5Final(result.MD5Digest) graph.symBodyHashes[sym.id] = result # protect from recursion in the body @@ -391,12 +393,12 @@ proc symBodyDigest*(graph: ModuleGraph, sym: PSym): SigHash = graph.symBodyHashes[sym.id] = result proc idOrSig*(s: PSym, currentModule: string, - sigCollisions: var CountTable[SigHash]): Rope = + sigCollisions: var CountTable[SigHash]; conf: ConfigRef): Rope = if s.kind in routineKinds and s.typ != nil: # signatures for exported routines are reliable enough to # produce a unique name and this means produced C++ is more stable regarding # Nim changes: - let sig = hashProc(s) + let sig = hashProc(s, conf) result = rope($sig) #let m = if s.typ.callConv != ccInline: findPendingModule(m, s) else: m let counter = sigCollisions.getOrDefault(sig) diff --git a/compiler/vm.nim b/compiler/vm.nim index 38b856277..18d840613 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1855,7 +1855,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = if regs[rb].node.kind != nkSym: stackTrace(c, tos, pc, "node is not a symbol") else: - regs[ra].node.strVal = $sigHash(regs[rb].node.sym) + regs[ra].node.strVal = $sigHash(regs[rb].node.sym, c.config) of opcSlurp: decodeB(rkNode) createStr regs[ra] diff --git a/tests/ccgbugs/m1/defs.nim b/tests/ccgbugs/m1/defs.nim new file mode 100644 index 000000000..ed78d8b72 --- /dev/null +++ b/tests/ccgbugs/m1/defs.nim @@ -0,0 +1,4 @@ +type MyObj* = object + field1*: int + s*: string + ch*: char diff --git a/tests/ccgbugs/m2/defs.nim b/tests/ccgbugs/m2/defs.nim new file mode 100644 index 000000000..798d1fea8 --- /dev/null +++ b/tests/ccgbugs/m2/defs.nim @@ -0,0 +1,4 @@ +type MyObj* = object + s*: string + field1*: int + ch*: char diff --git a/tests/ccgbugs/t20139.nim b/tests/ccgbugs/t20139.nim new file mode 100644 index 000000000..4592b994d --- /dev/null +++ b/tests/ccgbugs/t20139.nim @@ -0,0 +1,10 @@ +discard """ + joinable: false +""" + +# bug #20139 +import m1/defs as md1 +import m2/defs as md2 + +doAssert $(md1.MyObj(field1: 1)) == """(field1: 1, s: "", ch: '\x00')""" +doAssert $(md2.MyObj(field1: 1)) == """(s: "", field1: 1, ch: '\x00')""" From d4d28f2ffe522d2509dccfa7be0eef709732d93f Mon Sep 17 00:00:00 2001 From: Jake Leahy Date: Fri, 3 Mar 2023 06:27:10 +1100 Subject: [PATCH 3746/3768] Allow `futureLogging` in release builds (#21448) * Add test case * Fixes 21447: Keeps stackTrace around when using futureLogging * Remove extra whitespace --- lib/pure/asyncfutures.nim | 2 +- tests/async/t21447.nim | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) create mode 100644 tests/async/t21447.nim diff --git a/lib/pure/asyncfutures.nim b/lib/pure/asyncfutures.nim index edf5f7277..51aaaca36 100644 --- a/lib/pure/asyncfutures.nim +++ b/lib/pure/asyncfutures.nim @@ -29,7 +29,7 @@ type finished: bool error*: ref Exception ## Stored exception errorStackTrace*: string - when not defined(release): + when not defined(release) or defined(futureLogging): stackTrace: seq[StackTraceEntry] ## For debugging purposes only. id: int fromProc: string diff --git a/tests/async/t21447.nim b/tests/async/t21447.nim new file mode 100644 index 000000000..e4f7ae31f --- /dev/null +++ b/tests/async/t21447.nim @@ -0,0 +1,6 @@ +discard """ + action: "compile" + cmd: "nim c -d:release -d:futureLogging $file" +""" + +import std/asyncdispatch From d51a392149df1c0783fa526eabbc904ce0cd0cbd Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 3 Mar 2023 14:36:38 +0800 Subject: [PATCH 3747/3768] replaces implicit passes array registed at runtime with explicit function calls; simplify compilation pipeline (#21444) * abolish using passes in the compiler; simplify compilation pipeline * duplicate code * Really cool to have the same signature... * haul * unify other backends * refactor process * introduce PipelinePhase * refactor compiler * fixes passes * fixes nimsuggest * add a sentinel * enable docs checkj * activate doc testing * clean up * complete cleanups --- compiler/cgen.nim | 25 +--- compiler/depends.nim | 9 +- compiler/docgen2.nim | 25 ++-- compiler/jsgen.nim | 19 ++- compiler/main.nim | 67 +++++----- compiler/modulegraphs.nim | 13 ++ compiler/modules.nim | 96 +------------- compiler/nimeval.nim | 29 +++-- compiler/passes.nim | 187 +++++++++++++++++---------- compiler/pipelines.nim | 254 +++++++++++++++++++++++++++++++++++++ compiler/pipelineutils.nim | 26 ++++ compiler/scriptconfig.nim | 15 +-- compiler/sem.nim | 63 +++++---- compiler/suggest.nim | 2 +- compiler/vm.nim | 11 +- doc/refc.md | 1 + nimsuggest/nimsuggest.nim | 2 +- 17 files changed, 531 insertions(+), 313 deletions(-) create mode 100644 compiler/pipelines.nim create mode 100644 compiler/pipelineutils.nim diff --git a/compiler/cgen.nim b/compiler/cgen.nim index ab6e6559f..d051934b5 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -12,11 +12,13 @@ import ast, astalgo, hashes, trees, platform, magicsys, extccomp, options, intsets, nversion, nimsets, msgs, bitsets, idents, types, - ccgutils, os, ropes, math, passes, wordrecg, treetab, cgmeth, + ccgutils, os, ropes, math, wordrecg, treetab, cgmeth, rodutils, renderer, cgendata, aliases, lowerings, tables, sets, ndi, lineinfos, pathutils, transf, injectdestructors, astmsgs, modulepaths, backendpragmas +import pipelineutils + when defined(nimPreviewSlimSystem): import std/assertions @@ -1938,8 +1940,7 @@ template injectG() {.dirty.} = graph.backend = newModuleList(graph) let g = BModuleList(graph.backend) - -proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nosinks.} = +proc setupCgen*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nosinks.} = injectG() result = newModule(g, module, graph.config) result.idgen = idgen @@ -2007,7 +2008,7 @@ proc addHcrInitGuards(p: BProc, n: PNode, inInitGuard: var bool) = proc genTopLevelStmt*(m: BModule; n: PNode) = ## Also called from `ic/cbackend.nim`. - if passes.skipCodegen(m.config, n): return + if pipelineutils.skipCodegen(m.config, n): return m.initProc.options = initProcOptions(m) #softRnl = if optLineDir in m.config.options: noRnl else: rnl # XXX replicate this logic! @@ -2020,12 +2021,6 @@ proc genTopLevelStmt*(m: BModule; n: PNode) = else: genProcBody(m.initProc, transformedN) -proc myProcess(b: PPassContext, n: PNode): PNode = - result = n - if b != nil: - var m = BModule(b) - genTopLevelStmt(m, n) - proc shouldRecompile(m: BModule; code: Rope, cfile: Cfile): bool = if optForceFullMake notin m.config.globalOptions: if not moduleHasChanged(m.g.graph, m.module): @@ -2102,7 +2097,7 @@ proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode) = if {optGenStaticLib, optGenDynLib, optNoMain} * m.config.globalOptions == {}: for i in countdown(high(graph.globalDestructors), 0): n.add graph.globalDestructors[i] - if passes.skipCodegen(m.config, n): return + if pipelineutils.skipCodegen(m.config, n): return if moduleHasChanged(graph, m.module): # if the module is cached, we don't regenerate the main proc # nor the dispatchers? But if the dispatchers changed? @@ -2143,12 +2138,6 @@ proc finalCodegenActions*(graph: ModuleGraph; m: BModule; n: PNode) = let mm = m m.g.modulesClosed.add mm - -proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = - result = n - if b == nil: return - finalCodegenActions(graph, BModule(b), n) - proc genForwardedProcs(g: BModuleList) = # Forward declared proc:s lack bodies when first encountered, so they're given # a second pass here @@ -2175,5 +2164,3 @@ proc cgenWriteModules*(backend: RootRef, config: ConfigRef) = m.writeModule(pending=true) writeMapping(config, g.mapping) if g.generatedHeader != nil: writeHeader(g.generatedHeader) - -const cgenPass* = makePass(myOpen, myProcess, myClose) diff --git a/compiler/depends.nim b/compiler/depends.nim index 59cdc0330..2087198f2 100644 --- a/compiler/depends.nim +++ b/compiler/depends.nim @@ -9,7 +9,7 @@ # This module implements a dependency file generator. -import options, ast, ropes, passes, pathutils, msgs, lineinfos +import options, ast, ropes, pathutils, msgs, lineinfos import modulegraphs @@ -79,7 +79,7 @@ proc addDependency(c: PPassContext, g: PGen, b: Backend, n: PNode) = let child = nativeToUnixPath(path.dir / path.name).toNimblePath(belongsToStdlib(g.graph, n.sym)) addDependencyAux(b, parent, child) -proc addDotDependency(c: PPassContext, n: PNode): PNode = +proc addDotDependency*(c: PPassContext, n: PNode): PNode = result = n let g = PGen(c) let b = Backend(g.graph.backend) @@ -100,7 +100,7 @@ proc generateDot*(graph: ModuleGraph; project: AbsoluteFile) = rope(project.splitFile.name), b.dotGraph], changeFileExt(project, "dot")) -proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nosinks.} = +proc setupDependPass*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nosinks.} = var g: PGen new(g) g.module = module @@ -109,6 +109,3 @@ proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext if graph.backend == nil: graph.backend = Backend(dotGraph: "") result = g - -const gendependPass* = makePass(open = myOpen, process = addDotDependency) - diff --git a/compiler/docgen2.nim b/compiler/docgen2.nim index e5669937f..0941be78a 100644 --- a/compiler/docgen2.nim +++ b/compiler/docgen2.nim @@ -11,7 +11,7 @@ # semantic checking. import - options, ast, msgs, passes, docgen, lineinfos, pathutils, packages + options, ast, msgs, docgen, lineinfos, pathutils, packages from modulegraphs import ModuleGraph, PPassContext @@ -38,21 +38,21 @@ template closeImpl(body: untyped) {.dirty.} = except IOError: discard -proc close(graph: ModuleGraph; p: PPassContext, n: PNode): PNode = +proc closeDoc*(graph: ModuleGraph; p: PPassContext, n: PNode): PNode = closeImpl: writeOutput(g.doc, useWarning, groupedToc) -proc closeJson(graph: ModuleGraph; p: PPassContext, n: PNode): PNode = +proc closeJson*(graph: ModuleGraph; p: PPassContext, n: PNode): PNode = closeImpl: writeOutputJson(g.doc, useWarning) -proc processNode(c: PPassContext, n: PNode): PNode = +proc processNode*(c: PPassContext, n: PNode): PNode = result = n var g = PGen(c) if shouldProcess(g): generateDoc(g.doc, n, n, g.config) -proc processNodeJson(c: PPassContext, n: PNode): PNode = +proc processNodeJson*(c: PPassContext, n: PNode): PNode = result = n var g = PGen(c) if shouldProcess(g): @@ -68,20 +68,11 @@ template myOpenImpl(ext: untyped) {.dirty.} = g.doc = d result = g -proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext = +proc openHtml*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext = myOpenImpl(HtmlExt) -proc myOpenTex(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext = +proc openTex*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext = myOpenImpl(TexExt) -proc myOpenJson(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext = +proc openJson*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext = myOpenImpl(JsonExt) - -const docgen2Pass* = makePass(open = myOpen, process = processNode, close = close) -const docgen2TexPass* = makePass(open = myOpenTex, process = processNode, - close = close) -const docgen2JsonPass* = makePass(open = myOpenJson, process = processNodeJson, - close = closeJson) - -proc finishDoc2Pass*(project: string) = - discard diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 6619963b0..5df40b996 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -31,10 +31,12 @@ implements the required case distinction. import ast, trees, magicsys, options, nversion, msgs, idents, types, - ropes, passes, ccgutils, wordrecg, renderer, + ropes, ccgutils, wordrecg, renderer, cgmeth, lowerings, sighashes, modulegraphs, lineinfos, rodutils, transf, injectdestructors, sourcemap, astmsgs, backendpragmas +import pipelineutils + import json, sets, math, tables, intsets import strutils except addf @@ -2830,11 +2832,11 @@ proc genModule(p: PProc, n: PNode) = if optStackTrace in p.options: p.body.add(frameDestroy(p)) -proc myProcess(b: PPassContext, n: PNode): PNode = +proc processJSCodeGen*(b: PPassContext, n: PNode): PNode = ## Generate JS code for a node. result = n let m = BModule(b) - if passes.skipCodegen(m.config, n): return n + if pipelineutils.skipCodegen(m.config, n): return n if m.module == nil: internalError(m.config, n.info, "myProcess") let globals = PGlobals(m.graph.backend) var p = newInitProc(globals, m) @@ -2869,7 +2871,7 @@ proc getClassName(t: PType): Rope = if s.loc.r != "": result = s.loc.r else: result = rope(s.name.s) -proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = +proc finalJSCodeGen*(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = ## Finalize JS code generation of a Nim module. ## Param `n` may contain nodes returned from the last module close call. var m = BModule(b) @@ -2879,14 +2881,14 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = for i in countdown(high(graph.globalDestructors), 0): n.add graph.globalDestructors[i] # Process any nodes left over from the last call to `myClose`. - result = myProcess(b, n) + result = processJSCodeGen(b, n) # Some codegen is different (such as no stacktraces; see `initProcOptions`) # when `std/system` is being processed. if sfSystemModule in m.module.flags: PGlobals(graph.backend).inSystem = false # Check if codegen should continue before any files are generated. # It may bail early is if too many errors have been raised. - if passes.skipCodegen(m.config, n): return n + if pipelineutils.skipCodegen(m.config, n): return n # Nim modules are compiled into a single JS file. # If this is the main module, then this is the final call to `myClose`. if sfMainModule in m.module.flags: @@ -2904,9 +2906,6 @@ proc myClose(graph: ModuleGraph; b: PPassContext, n: PNode): PNode = if not writeRope(code, outFile): rawMessage(m.config, errCannotOpenFile, outFile.string) -proc myOpen(graph: ModuleGraph; s: PSym; idgen: IdGenerator): PPassContext = - ## Create the JS backend pass context `BModule` for a Nim module. +proc setupJSgen*(graph: ModuleGraph; s: PSym; idgen: IdGenerator): PPassContext = result = newModule(graph, s) result.idgen = idgen - -const JSgenPass* = makePass(myOpen, myProcess, myClose) diff --git a/compiler/main.nim b/compiler/main.nim index b8e0e2f12..d37d26478 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -16,9 +16,9 @@ import std/[strutils, os, times, tables, sha1, with, json], llstream, ast, lexer, syntaxes, options, msgs, condsyms, - sem, idents, passes, extccomp, + idents, extccomp, cgen, nversion, - platform, nimconf, passaux, depends, vm, + platform, nimconf, depends, modules, modulegraphs, lineinfos, pathutils, vmprofiler @@ -29,12 +29,10 @@ when defined(nimPreviewSlimSystem): import ic / [cbackend, integrity, navigator] from ic / ic import rodViewer -when not defined(leanCompiler): - import jsgen, docgen, docgen2 +import pipelines -proc semanticPasses(g: ModuleGraph) = - registerPass g, verbosePass - registerPass g, semPass +when not defined(leanCompiler): + import docgen proc writeDepsFile(g: ModuleGraph) = let fname = g.config.nimcacheDir / RelativeFile(g.config.projectName & ".deps") @@ -68,9 +66,8 @@ proc writeCMakeDepsFile(conf: ConfigRef) = fl.close() proc commandGenDepend(graph: ModuleGraph) = - semanticPasses(graph) - registerPass(graph, gendependPass) - compileProject(graph) + setPipeLinePass(graph, GenDependPass) + compilePipelineProject(graph) let project = graph.config.projectFull writeDepsFile(graph) generateDot(graph, project) @@ -87,8 +84,8 @@ proc commandCheck(graph: ModuleGraph) = defineSymbol(conf.symbols, "nimconfig") elif conf.backend == backendJs: setTarget(conf.target, osJS, cpuJS) - semanticPasses(graph) # use an empty backend for semantic checking only - compileProject(graph) + setPipeLinePass(graph, SemPass) + compilePipelineProject(graph) if conf.symbolFiles != disabledSf: case conf.ideCmd @@ -102,22 +99,20 @@ when not defined(leanCompiler): proc commandDoc2(graph: ModuleGraph; ext: string) = handleDocOutputOptions graph.config graph.config.setErrorMaxHighMaybe - semanticPasses(graph) case ext: - of TexExt: registerPass(graph, docgen2TexPass) - of JsonExt: registerPass(graph, docgen2JsonPass) - of HtmlExt: registerPass(graph, docgen2Pass) + of TexExt: + setPipeLinePass(graph, Docgen2TexPass) + of JsonExt: + setPipeLinePass(graph, Docgen2JsonPass) + of HtmlExt: + setPipeLinePass(graph, Docgen2Pass) else: doAssert false, $ext - compileProject(graph) - finishDoc2Pass(graph.config.projectName) + compilePipelineProject(graph) proc commandCompileToC(graph: ModuleGraph) = let conf = graph.config extccomp.initVars(conf) - semanticPasses(graph) if conf.symbolFiles == disabledSf: - registerPass(graph, cgenPass) - if {optRun, optForceFullMake} * conf.globalOptions == {optRun} or isDefined(conf, "nimBetterRun"): if not changeDetectedViaJsonBuildInstructions(conf, conf.jsonBuildInstructionsFile): # nothing changed @@ -127,7 +122,11 @@ proc commandCompileToC(graph: ModuleGraph) = if not extccomp.ccHasSaneOverflow(conf): conf.symbols.defineSymbol("nimEmulateOverflowChecks") - compileProject(graph) + if conf.symbolFiles == disabledSf: + setPipeLinePass(graph, CgenPass) + else: + setPipeLinePass(graph, SemPass) + compilePipelineProject(graph) if graph.config.errorCounter > 0: return # issue #9933 if conf.symbolFiles == disabledSf: @@ -160,33 +159,27 @@ proc commandCompileToJS(graph: ModuleGraph) = conf.exc = excCpp setTarget(conf.target, osJS, cpuJS) defineSymbol(conf.symbols, "ecmascript") # For backward compatibility - semanticPasses(graph) - registerPass(graph, JSgenPass) - compileProject(graph) + setPipeLinePass(graph, JSgenPass) + compilePipelineProject(graph) if optGenScript in conf.globalOptions: writeDepsFile(graph) -proc interactivePasses(graph: ModuleGraph) = +proc commandInteractive(graph: ModuleGraph) = + graph.config.setErrorMaxHighMaybe initDefines(graph.config.symbols) defineSymbol(graph.config.symbols, "nimscript") # note: seems redundant with -d:nimHasLibFFI when hasFFI: defineSymbol(graph.config.symbols, "nimffi") - registerPass(graph, verbosePass) - registerPass(graph, semPass) - registerPass(graph, evalPass) - -proc commandInteractive(graph: ModuleGraph) = - graph.config.setErrorMaxHighMaybe - interactivePasses(graph) - compileSystemModule(graph) + setPipeLinePass(graph, InterpreterPass) + compilePipelineSystemModule(graph) if graph.config.commandArgs.len > 0: - discard graph.compileModule(fileInfoIdx(graph.config, graph.config.projectFull), {}) + discard graph.compilePipelineModule(fileInfoIdx(graph.config, graph.config.projectFull), {}) else: var m = graph.makeStdinModule() incl(m.flags, sfMainModule) var idgen = IdGenerator(module: m.itemId.module, symId: m.itemId.item, typeId: 0) let s = llStreamOpenStdIn(onPrompt = proc() = flushDot(graph.config)) - processModule(graph, m, idgen, s) + discard processPipelineModule(graph, m, idgen, s) proc commandScan(cache: IdentCache, config: ConfigRef) = var f = addFileExt(AbsoluteFile mainCommandArg(config), NimExt) @@ -241,8 +234,6 @@ proc mainCommand*(graph: ModuleGraph) = let conf = graph.config let cache = graph.cache - # In "nim serve" scenario, each command must reset the registered passes - clearPasses(graph) conf.lastCmdTime = epochTime() conf.searchPaths.add(conf.libpath) diff --git a/compiler/modulegraphs.nim b/compiler/modulegraphs.nim index 598959a37..8ffbe20a5 100644 --- a/compiler/modulegraphs.nim +++ b/compiler/modulegraphs.nim @@ -57,6 +57,18 @@ type sym*: PSym info*: TLineInfo + PipelinePass* = enum + NonePass + SemPass + JSgenPass + CgenPass + EvalPass + InterpreterPass + GenDependPass + Docgen2TexPass + Docgen2JsonPass + Docgen2Pass + ModuleGraph* {.acyclic.} = ref object ifaces*: seq[Iface] ## indexed by int32 fileIdx packed*: PackedModuleGraph @@ -104,6 +116,7 @@ type cacheCounters*: Table[string, BiggestInt] # IC: implemented cacheTables*: Table[string, BTree[string, PNode]] # IC: implemented passes*: seq[TPass] + pipelinePass*: PipelinePass onDefinition*: proc (graph: ModuleGraph; s: PSym; info: TLineInfo) {.nimcall.} onDefinitionResolveForward*: proc (graph: ModuleGraph; s: PSym; info: TLineInfo) {.nimcall.} onUsage*: proc (graph: ModuleGraph; s: PSym; info: TLineInfo) {.nimcall.} diff --git a/compiler/modules.nim b/compiler/modules.nim index 838a89d83..0aa1c8930 100644 --- a/compiler/modules.nim +++ b/compiler/modules.nim @@ -11,13 +11,8 @@ import ast, magicsys, msgs, options, - idents, lexer, passes, syntaxes, llstream, modulegraphs, - lineinfos, pathutils, tables, packages - -when defined(nimPreviewSlimSystem): - import std/[syncio, assertions] - -import ic / replayer + idents, lexer, syntaxes, modulegraphs, + lineinfos, pathutils proc resetSystemArtifacts*(g: ModuleGraph) = magicsys.resetSysTypes(g) @@ -25,12 +20,12 @@ proc resetSystemArtifacts*(g: ModuleGraph) = template getModuleIdent(graph: ModuleGraph, filename: AbsoluteFile): PIdent = getIdent(graph.cache, splitFile(filename).name) -proc partialInitModule(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; filename: AbsoluteFile) = +proc partialInitModule*(result: PSym; graph: ModuleGraph; fileIdx: FileIndex; filename: AbsoluteFile) = let packSym = getPackage(graph, fileIdx) result.owner = packSym result.position = int fileIdx -proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym = +proc newModule*(graph: ModuleGraph; fileIdx: FileIndex): PSym = let filename = AbsoluteFile toFullPath(graph.config, fileIdx) # We cannot call ``newSym`` here, because we have to circumvent the ID # mechanism, which we do in order to assign each module a persistent ID. @@ -43,99 +38,16 @@ proc newModule(graph: ModuleGraph; fileIdx: FileIndex): PSym = partialInitModule(result, graph, fileIdx, filename) graph.registerModule(result) -proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags, fromModule: PSym = nil): PSym = - var flags = flags - if fileIdx == graph.config.projectMainIdx2: flags.incl sfMainModule - result = graph.getModule(fileIdx) - - template processModuleAux(moduleStatus) = - onProcessing(graph, fileIdx, moduleStatus, fromModule = fromModule) - var s: PLLStream - if sfMainModule in flags: - if graph.config.projectIsStdin: s = stdin.llStreamOpen - elif graph.config.projectIsCmd: s = llStreamOpen(graph.config.cmdInput) - discard processModule(graph, result, idGeneratorFromModule(result), s) - if result == nil: - var cachedModules: seq[FileIndex] - result = moduleFromRodFile(graph, fileIdx, cachedModules) - let filename = AbsoluteFile toFullPath(graph.config, fileIdx) - if result == nil: - result = newModule(graph, fileIdx) - result.flags.incl flags - registerModule(graph, result) - processModuleAux("import") - else: - if sfSystemModule in flags: - graph.systemModule = result - partialInitModule(result, graph, fileIdx, filename) - for m in cachedModules: - registerModuleById(graph, m) - replayStateChanges(graph.packed[m.int].module, graph) - replayGenericCacheInformation(graph, m.int) - elif graph.isDirty(result): - result.flags.excl sfDirty - # reset module fields: - initStrTables(graph, result) - result.ast = nil - processModuleAux("import(dirty)") - graph.markClientsDirty(fileIdx) - -proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PSym = - # this is called by the semantic checking phase - assert graph.config != nil - result = compileModule(graph, fileIdx, {}, s) - graph.addDep(s, fileIdx) - # keep track of import relationships - if graph.config.hcrOn: - graph.importDeps.mgetOrPut(FileIndex(s.position), @[]).add(fileIdx) - #if sfSystemModule in result.flags: - # localError(result.info, errAttemptToRedefine, result.name.s) - # restore the notes for outer module: - graph.config.notes = - if graph.config.belongsToProjectPackage(s) or isDefined(graph.config, "booting"): graph.config.mainPackageNotes - else: graph.config.foreignPackageNotes - proc includeModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PNode = result = syntaxes.parseFile(fileIdx, graph.cache, graph.config) graph.addDep(s, fileIdx) graph.addIncludeDep(s.position.FileIndex, fileIdx) -proc connectCallbacks*(graph: ModuleGraph) = - graph.includeFileCallback = includeModule - graph.importModuleCallback = importModule - -proc compileSystemModule*(graph: ModuleGraph) = - if graph.systemModule == nil: - connectCallbacks(graph) - graph.config.m.systemFileIdx = fileInfoIdx(graph.config, - graph.config.libpath / RelativeFile"system.nim") - discard graph.compileModule(graph.config.m.systemFileIdx, {sfSystemModule}) - proc wantMainModule*(conf: ConfigRef) = if conf.projectFull.isEmpty: fatal(conf, gCmdLineInfo, "command expects a filename") conf.projectMainIdx = fileInfoIdx(conf, addFileExt(conf.projectFull, NimExt)) -proc compileProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIdx) = - connectCallbacks(graph) - let conf = graph.config - wantMainModule(conf) - configComplete(graph) - - let systemFileIdx = fileInfoIdx(conf, conf.libpath / RelativeFile"system.nim") - let projectFile = if projectFileIdx == InvalidFileIdx: conf.projectMainIdx else: projectFileIdx - conf.projectMainIdx2 = projectFile - - let packSym = getPackage(graph, projectFile) - graph.config.mainPackageId = packSym.getPackageId - graph.importStack.add projectFile - - if projectFile == systemFileIdx: - discard graph.compileModule(projectFile, {sfMainModule, sfSystemModule}) - else: - graph.compileSystemModule() - discard graph.compileModule(projectFile, {sfMainModule}) - proc makeModule*(graph: ModuleGraph; filename: AbsoluteFile): PSym = result = graph.newModule(fileInfoIdx(graph.config, filename)) registerModule(graph, result) diff --git a/compiler/nimeval.nim b/compiler/nimeval.nim index 82e2f0812..8e8f4ac7b 100644 --- a/compiler/nimeval.nim +++ b/compiler/nimeval.nim @@ -9,10 +9,16 @@ ## exposes the Nim VM to clients. import - ast, astalgo, modules, passes, condsyms, - options, sem, llstream, lineinfos, vm, + ast, modules, condsyms, + options, llstream, lineinfos, vm, vmdef, modulegraphs, idents, os, pathutils, - passaux, scriptconfig, std/compilesettings + scriptconfig, std/compilesettings + +import pipelines + + +when defined(nimPreviewSlimSystem): + import std/[assertions, syncio] type Interpreter* = ref object ## Use Nim as an interpreter with this object @@ -76,7 +82,7 @@ proc evalScript*(i: Interpreter; scriptStream: PLLStream = nil) = let s = if scriptStream != nil: scriptStream else: llStreamOpen(findFile(i.graph.config, i.scriptName), fmRead) - processModule(i.graph, i.mainModule, i.idgen, s) + discard processPipelineModule(i.graph, i.mainModule, i.idgen, s) proc findNimStdLib*(): string = ## Tries to find a path to a valid "system.nim" file. @@ -109,12 +115,10 @@ proc createInterpreter*(scriptName: string; var conf = newConfigRef() var cache = newIdentCache() var graph = newModuleGraph(cache, conf) - connectCallbacks(graph) + connectPipelineCallbacks(graph) initDefines(conf.symbols) for define in defines: defineSymbol(conf.symbols, define[0], define[1]) - registerPass(graph, semPass) - registerPass(graph, evalPass) for p in searchPaths: conf.searchPaths.add(AbsoluteDir p) @@ -129,7 +133,8 @@ proc createInterpreter*(scriptName: string; if registerOps: vm.registerAdditionalOps() # Required to register parts of stdlib modules graph.vm = vm - graph.compileSystemModule() + setPipeLinePass(graph, EvalPass) + graph.compilePipelineSystemModule() result = Interpreter(mainModule: m, graph: graph, scriptName: scriptName, idgen: idgen) proc destroyInterpreter*(i: Interpreter) = @@ -159,13 +164,11 @@ proc runRepl*(r: TLLRepl; defineSymbol(conf.symbols, "nimscript") if supportNimscript: defineSymbol(conf.symbols, "nimconfig") when hasFFI: defineSymbol(graph.config.symbols, "nimffi") - registerPass(graph, verbosePass) - registerPass(graph, semPass) - registerPass(graph, evalPass) var m = graph.makeStdinModule() incl(m.flags, sfMainModule) var idgen = idGeneratorFromModule(m) if supportNimscript: graph.vm = setupVM(m, cache, "stdin", graph, idgen) - graph.compileSystemModule() - processModule(graph, m, idgen, llStreamOpenStdIn(r)) + setPipeLinePass(graph, InterpreterPass) + graph.compilePipelineSystemModule() + discard processPipelineModule(graph, m, idgen, llStreamOpenStdIn(r)) diff --git a/compiler/passes.nim b/compiler/passes.nim index 74c5ff77c..38c133d69 100644 --- a/compiler/passes.nim +++ b/compiler/passes.nim @@ -14,13 +14,22 @@ import options, ast, llstream, msgs, idents, syntaxes, modulegraphs, reorder, - lineinfos, pathutils, packages + lineinfos, + pipelineutils, + modules, pathutils, packages, + sem, semdata + +import ic/replayer + +export skipCodegen, resolveMod, prepareConfigNotes when defined(nimsuggest): import std/sha1 when defined(nimPreviewSlimSystem): - import std/syncio + import std/[syncio, assertions] + +import std/tables type TPassData* = tuple[input: PNode, closeOutput: PNode] @@ -38,12 +47,6 @@ proc makePass*(open: TPassOpen = nil, result.process = process result.isFrontend = isFrontend -proc skipCodegen*(config: ConfigRef; n: PNode): bool {.inline.} = - # can be used by codegen passes to determine whether they should do - # something with `n`. Currently, this ignores `n` and uses the global - # error count instead. - result = config.errorCounter > 0 - const maxPasses = 10 @@ -80,13 +83,6 @@ proc processTopLevelStmt(graph: ModuleGraph, n: PNode, a: var TPassContextArray) if isNil(m): return false result = true -proc resolveMod(conf: ConfigRef; module, relativeTo: string): FileIndex = - let fullPath = findModule(conf, module, relativeTo) - if fullPath.isEmpty: - result = InvalidFileIdx - else: - result = fileInfoIdx(conf, fullPath) - proc processImplicits(graph: ModuleGraph; implicits: seq[string], nodeKind: TNodeKind, a: var TPassContextArray; m: PSym) = # XXX fixme this should actually be relative to the config file! @@ -100,23 +96,6 @@ proc processImplicits(graph: ModuleGraph; implicits: seq[string], nodeKind: TNod importStmt.add str if not processTopLevelStmt(graph, importStmt, a): break -const - imperativeCode = {low(TNodeKind)..high(TNodeKind)} - {nkTemplateDef, nkProcDef, nkMethodDef, - nkMacroDef, nkConverterDef, nkIteratorDef, nkFuncDef, nkPragma, - nkExportStmt, nkExportExceptStmt, nkFromStmt, nkImportStmt, nkImportExceptStmt} - -proc prepareConfigNotes(graph: ModuleGraph; module: PSym) = - # don't be verbose unless the module belongs to the main package: - if graph.config.belongsToProjectPackage(module): - graph.config.notes = graph.config.mainPackageNotes - else: - if graph.config.mainPackageNotes == {}: graph.config.mainPackageNotes = graph.config.notes - graph.config.notes = graph.config.foreignPackageNotes - -proc moduleHasChanged*(graph: ModuleGraph; module: PSym): bool {.inline.} = - result = true - #module.id >= 0 or isDefined(graph.config, "nimBackendAssumesChange") - proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator; stream: PLLStream): bool {.discardable.} = if graph.stopCompile(): return true @@ -153,42 +132,22 @@ proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator; processImplicits graph, graph.config.implicitIncludes, nkIncludeStmt, a, module checkFirstLineIndentation(p) - while true: - if graph.stopCompile(): break - var n = parseTopLevelStmt(p) # todo merge it - if n.kind == nkEmpty: break + block processCode: + if graph.stopCompile(): break processCode + var n = parseTopLevelStmt(p) + if n.kind == nkEmpty: break processCode - if true: - # read everything, no streaming possible - var sl = newNodeI(nkStmtList, n.info) + # read everything, no streaming possible + var sl = newNodeI(nkStmtList, n.info) + sl.add n + while true: + var n = parseTopLevelStmt(p) + if n.kind == nkEmpty: break sl.add n - while true: - var n = parseTopLevelStmt(p) - if n.kind == nkEmpty: break - sl.add n - if sfReorder in module.flags or codeReordering in graph.config.features: - sl = reorder(graph, sl, module) - discard processTopLevelStmt(graph, sl, a) - break - elif n.kind in imperativeCode: - # read everything until the next proc declaration etc. - var sl = newNodeI(nkStmtList, n.info) - sl.add n - var rest: PNode = nil - while true: - var n = parseTopLevelStmt(p) - if n.kind == nkEmpty or n.kind notin imperativeCode: - rest = n - break - sl.add n - #echo "-----\n", sl - if not processTopLevelStmt(graph, sl, a): break - if rest != nil: - #echo "-----\n", rest - if not processTopLevelStmt(graph, rest, a): break - else: - #echo "----- single\n", n - if not processTopLevelStmt(graph, n, a): break + if sfReorder in module.flags or codeReordering in graph.config.features: + sl = reorder(graph, sl, module) + discard processTopLevelStmt(graph, sl, a) + closeParser(p) if s.kind != llsStdIn: break closePasses(graph, a) @@ -198,3 +157,99 @@ proc processModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator; # They are responsible for closing the rod files. See `cbackend.nim`. closeRodFile(graph, module) result = true + +proc compileModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags, fromModule: PSym = nil): PSym = + var flags = flags + if fileIdx == graph.config.projectMainIdx2: flags.incl sfMainModule + result = graph.getModule(fileIdx) + + template processModuleAux(moduleStatus) = + onProcessing(graph, fileIdx, moduleStatus, fromModule = fromModule) + var s: PLLStream + if sfMainModule in flags: + if graph.config.projectIsStdin: s = stdin.llStreamOpen + elif graph.config.projectIsCmd: s = llStreamOpen(graph.config.cmdInput) + discard processModule(graph, result, idGeneratorFromModule(result), s) + if result == nil: + var cachedModules: seq[FileIndex] + result = moduleFromRodFile(graph, fileIdx, cachedModules) + let filename = AbsoluteFile toFullPath(graph.config, fileIdx) + if result == nil: + result = newModule(graph, fileIdx) + result.flags.incl flags + registerModule(graph, result) + processModuleAux("import") + else: + if sfSystemModule in flags: + graph.systemModule = result + partialInitModule(result, graph, fileIdx, filename) + for m in cachedModules: + registerModuleById(graph, m) + replayStateChanges(graph.packed[m.int].module, graph) + replayGenericCacheInformation(graph, m.int) + elif graph.isDirty(result): + result.flags.excl sfDirty + # reset module fields: + initStrTables(graph, result) + result.ast = nil + processModuleAux("import(dirty)") + graph.markClientsDirty(fileIdx) + +proc importModule*(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PSym = + # this is called by the semantic checking phase + assert graph.config != nil + result = compileModule(graph, fileIdx, {}, s) + graph.addDep(s, fileIdx) + # keep track of import relationships + if graph.config.hcrOn: + graph.importDeps.mgetOrPut(FileIndex(s.position), @[]).add(fileIdx) + #if sfSystemModule in result.flags: + # localError(result.info, errAttemptToRedefine, result.name.s) + # restore the notes for outer module: + graph.config.notes = + if graph.config.belongsToProjectPackage(s) or isDefined(graph.config, "booting"): graph.config.mainPackageNotes + else: graph.config.foreignPackageNotes + +proc connectCallbacks*(graph: ModuleGraph) = + graph.includeFileCallback = modules.includeModule + graph.importModuleCallback = importModule + +proc compileSystemModule*(graph: ModuleGraph) = + if graph.systemModule == nil: + connectCallbacks(graph) + graph.config.m.systemFileIdx = fileInfoIdx(graph.config, + graph.config.libpath / RelativeFile"system.nim") + discard graph.compileModule(graph.config.m.systemFileIdx, {sfSystemModule}) + +proc compileProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIdx) = + connectCallbacks(graph) + let conf = graph.config + wantMainModule(conf) + configComplete(graph) + + let systemFileIdx = fileInfoIdx(conf, conf.libpath / RelativeFile"system.nim") + let projectFile = if projectFileIdx == InvalidFileIdx: conf.projectMainIdx else: projectFileIdx + conf.projectMainIdx2 = projectFile + + let packSym = getPackage(graph, projectFile) + graph.config.mainPackageId = packSym.getPackageId + graph.importStack.add projectFile + + if projectFile == systemFileIdx: + discard graph.compileModule(projectFile, {sfMainModule, sfSystemModule}) + else: + graph.compileSystemModule() + discard graph.compileModule(projectFile, {sfMainModule}) + +proc mySemOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nosinks.} = + result = preparePContext(graph, module, idgen) + +proc mySemClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode = + var c = PContext(context) + closePContext(graph, c, n) + +proc mySemProcess(context: PPassContext, n: PNode): PNode {.nosinks.} = + result = semWithPContext(PContext(context), n) + +const semPass* = makePass(mySemOpen, mySemProcess, mySemClose, + isFrontend = true) diff --git a/compiler/pipelines.nim b/compiler/pipelines.nim new file mode 100644 index 000000000..b80681c4b --- /dev/null +++ b/compiler/pipelines.nim @@ -0,0 +1,254 @@ +import sem, cgen, modulegraphs, ast, llstream, parser, msgs, + lineinfos, reorder, options, semdata, cgendata, modules, pathutils, + packages, syntaxes, depends, vm + +import pipelineutils + +when not defined(leanCompiler): + import jsgen, docgen2 + +import std/[syncio, objectdollar, assertions, tables] +import renderer +import ic/replayer + + +proc setPipeLinePass*(graph: ModuleGraph; pass: PipelinePass) = + graph.pipelinePass = pass + +proc processPipeline(graph: ModuleGraph; semNode: PNode; bModule: PPassContext): PNode = + case graph.pipelinePass + of CgenPass: + result = semNode + if bModule != nil: + genTopLevelStmt(BModule(bModule), result) + of JSgenPass: + when not defined(leanCompiler): + result = processJSCodeGen(bModule, semNode) + of GenDependPass: + result = addDotDependency(bModule, semNode) + of SemPass: + result = graph.emptyNode + of Docgen2Pass, Docgen2TexPass: + when not defined(leanCompiler): + result = processNode(bModule, semNode) + of Docgen2JsonPass: + when not defined(leanCompiler): + result = processNodeJson(bModule, semNode) + of EvalPass, InterpreterPass: + result = interpreterCode(bModule, semNode) + of NonePass: + doAssert false, "use setPipeLinePass to set a proper PipelinePass" + +proc processImplicitImports(graph: ModuleGraph; implicits: seq[string], nodeKind: TNodeKind, + m: PSym, ctx: PContext, bModule: PPassContext, idgen: IdGenerator, + ) = + # XXX fixme this should actually be relative to the config file! + let relativeTo = toFullPath(graph.config, m.info) + for module in items(implicits): + # implicit imports should not lead to a module importing itself + if m.position != resolveMod(graph.config, module, relativeTo).int32: + var importStmt = newNodeI(nodeKind, m.info) + var str = newStrNode(nkStrLit, module) + str.info = m.info + importStmt.add str + message(graph.config, importStmt.info, hintProcessingStmt, $idgen[]) + let semNode = semWithPContext(ctx, importStmt) + if semNode == nil or processPipeline(graph, semNode, bModule) == nil: + break + +proc processPipelineModule*(graph: ModuleGraph; module: PSym; idgen: IdGenerator; + stream: PLLStream): bool = + if graph.stopCompile(): return true + var + p: Parser + s: PLLStream + fileIdx = module.fileIdx + + prepareConfigNotes(graph, module) + let ctx = preparePContext(graph, module, idgen) + let bModule: PPassContext = + case graph.pipelinePass + of CgenPass: + setupCgen(graph, module, idgen) + of JSgenPass: + when not defined(leanCompiler): + setupJSgen(graph, module, idgen) + else: + nil + of EvalPass, InterpreterPass: + setupEvalGen(graph, module, idgen) + of GenDependPass: + setupDependPass(graph, module, idgen) + of Docgen2Pass: + when not defined(leanCompiler): + openHtml(graph, module, idgen) + else: + nil + of Docgen2TexPass: + when not defined(leanCompiler): + openTex(graph, module, idgen) + else: + nil + of Docgen2JsonPass: + when not defined(leanCompiler): + openJson(graph, module, idgen) + else: + nil + of SemPass: + nil + of NonePass: + doAssert false, "use setPipeLinePass to set a proper PipelinePass" + nil + + if stream == nil: + let filename = toFullPathConsiderDirty(graph.config, fileIdx) + s = llStreamOpen(filename, fmRead) + if s == nil: + rawMessage(graph.config, errCannotOpenFile, filename.string) + return false + else: + s = stream + + while true: + syntaxes.openParser(p, fileIdx, s, graph.cache, graph.config) + + if not belongsToStdlib(graph, module) or (belongsToStdlib(graph, module) and module.name.s == "distros"): + # XXX what about caching? no processing then? what if I change the + # modules to include between compilation runs? we'd need to track that + # in ROD files. I think we should enable this feature only + # for the interactive mode. + if module.name.s != "nimscriptapi": + processImplicitImports graph, graph.config.implicitImports, nkImportStmt, module, ctx, bModule, idgen + processImplicitImports graph, graph.config.implicitIncludes, nkIncludeStmt, module, ctx, bModule, idgen + + checkFirstLineIndentation(p) + block processCode: + if graph.stopCompile(): break processCode + var n = parseTopLevelStmt(p) + if n.kind == nkEmpty: break processCode + # read everything, no streaming possible + var sl = newNodeI(nkStmtList, n.info) + sl.add n + while true: + var n = parseTopLevelStmt(p) + if n.kind == nkEmpty: break + sl.add n + if sfReorder in module.flags or codeReordering in graph.config.features: + sl = reorder(graph, sl, module) + if graph.pipelinePass != EvalPass: + message(graph.config, sl.info, hintProcessingStmt, $idgen[]) + var semNode = semWithPContext(ctx, sl) + discard processPipeline(graph, semNode, bModule) + + closeParser(p) + if s.kind != llsStdIn: break + let finalNode = closePContext(graph, ctx, nil) + case graph.pipelinePass + of CgenPass: + if bModule != nil: + finalCodegenActions(graph, BModule(bModule), finalNode) + of JSgenPass: + when not defined(leanCompiler): + discard finalJSCodeGen(graph, bModule, finalNode) + of EvalPass, InterpreterPass: + discard interpreterCode(bModule, finalNode) + of SemPass, GenDependPass: + discard + of Docgen2Pass, Docgen2TexPass: + when not defined(leanCompiler): + discard closeDoc(graph, bModule, finalNode) + of Docgen2JsonPass: + when not defined(leanCompiler): + discard closeJson(graph, bModule, finalNode) + of NonePass: + doAssert false, "use setPipeLinePass to set a proper PipelinePass" + + if graph.config.backend notin {backendC, backendCpp, backendObjc}: + # We only write rod files here if no C-like backend is active. + # The C-like backends have been patched to support the IC mechanism. + # They are responsible for closing the rod files. See `cbackend.nim`. + closeRodFile(graph, module) + result = true + +proc compilePipelineModule*(graph: ModuleGraph; fileIdx: FileIndex; flags: TSymFlags, fromModule: PSym = nil): PSym = + var flags = flags + if fileIdx == graph.config.projectMainIdx2: flags.incl sfMainModule + result = graph.getModule(fileIdx) + + template processModuleAux(moduleStatus) = + onProcessing(graph, fileIdx, moduleStatus, fromModule = fromModule) + var s: PLLStream + if sfMainModule in flags: + if graph.config.projectIsStdin: s = stdin.llStreamOpen + elif graph.config.projectIsCmd: s = llStreamOpen(graph.config.cmdInput) + discard processPipelineModule(graph, result, idGeneratorFromModule(result), s) + if result == nil: + var cachedModules: seq[FileIndex] + result = moduleFromRodFile(graph, fileIdx, cachedModules) + let filename = AbsoluteFile toFullPath(graph.config, fileIdx) + if result == nil: + result = newModule(graph, fileIdx) + result.flags.incl flags + registerModule(graph, result) + processModuleAux("import") + else: + if sfSystemModule in flags: + graph.systemModule = result + partialInitModule(result, graph, fileIdx, filename) + for m in cachedModules: + registerModuleById(graph, m) + replayStateChanges(graph.packed[m.int].module, graph) + replayGenericCacheInformation(graph, m.int) + elif graph.isDirty(result): + result.flags.excl sfDirty + # reset module fields: + initStrTables(graph, result) + result.ast = nil + processModuleAux("import(dirty)") + graph.markClientsDirty(fileIdx) + +proc importPipelineModule(graph: ModuleGraph; s: PSym, fileIdx: FileIndex): PSym = + # this is called by the semantic checking phase + assert graph.config != nil + result = compilePipelineModule(graph, fileIdx, {}, s) + graph.addDep(s, fileIdx) + # keep track of import relationships + if graph.config.hcrOn: + graph.importDeps.mgetOrPut(FileIndex(s.position), @[]).add(fileIdx) + #if sfSystemModule in result.flags: + # localError(result.info, errAttemptToRedefine, result.name.s) + # restore the notes for outer module: + graph.config.notes = + if graph.config.belongsToProjectPackage(s) or isDefined(graph.config, "booting"): graph.config.mainPackageNotes + else: graph.config.foreignPackageNotes + +proc connectPipelineCallbacks*(graph: ModuleGraph) = + graph.includeFileCallback = modules.includeModule + graph.importModuleCallback = importPipelineModule + +proc compilePipelineSystemModule*(graph: ModuleGraph) = + if graph.systemModule == nil: + connectPipelineCallbacks(graph) + graph.config.m.systemFileIdx = fileInfoIdx(graph.config, + graph.config.libpath / RelativeFile"system.nim") + discard graph.compilePipelineModule(graph.config.m.systemFileIdx, {sfSystemModule}) + +proc compilePipelineProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIdx) = + connectPipelineCallbacks(graph) + let conf = graph.config + wantMainModule(conf) + configComplete(graph) + + let systemFileIdx = fileInfoIdx(conf, conf.libpath / RelativeFile"system.nim") + let projectFile = if projectFileIdx == InvalidFileIdx: conf.projectMainIdx else: projectFileIdx + conf.projectMainIdx2 = projectFile + + let packSym = getPackage(graph, projectFile) + graph.config.mainPackageId = packSym.getPackageId + graph.importStack.add projectFile + + if projectFile == systemFileIdx: + discard graph.compilePipelineModule(projectFile, {sfMainModule, sfSystemModule}) + else: + graph.compilePipelineSystemModule() + discard graph.compilePipelineModule(projectFile, {sfMainModule}) diff --git a/compiler/pipelineutils.nim b/compiler/pipelineutils.nim new file mode 100644 index 000000000..75ba33f14 --- /dev/null +++ b/compiler/pipelineutils.nim @@ -0,0 +1,26 @@ +import ast, options, lineinfos, pathutils, msgs, modulegraphs, packages + +proc skipCodegen*(config: ConfigRef; n: PNode): bool {.inline.} = + # can be used by codegen passes to determine whether they should do + # something with `n`. Currently, this ignores `n` and uses the global + # error count instead. + result = config.errorCounter > 0 + +proc resolveMod*(conf: ConfigRef; module, relativeTo: string): FileIndex = + let fullPath = findModule(conf, module, relativeTo) + if fullPath.isEmpty: + result = InvalidFileIdx + else: + result = fileInfoIdx(conf, fullPath) + +proc prepareConfigNotes*(graph: ModuleGraph; module: PSym) = + # don't be verbose unless the module belongs to the main package: + if graph.config.belongsToProjectPackage(module): + graph.config.notes = graph.config.mainPackageNotes + else: + if graph.config.mainPackageNotes == {}: graph.config.mainPackageNotes = graph.config.notes + graph.config.notes = graph.config.foreignPackageNotes + +proc moduleHasChanged*(graph: ModuleGraph; module: PSym): bool {.inline.} = + result = true + #module.id >= 0 or isDefined(graph.config, "nimBackendAssumesChange") diff --git a/compiler/scriptconfig.nim b/compiler/scriptconfig.nim index 009e90fd5..27ea94aae 100644 --- a/compiler/scriptconfig.nim +++ b/compiler/scriptconfig.nim @@ -11,10 +11,10 @@ ## language. import - ast, modules, idents, passes, condsyms, - options, sem, llstream, vm, vmdef, commands, + ast, modules, idents, condsyms, + options, llstream, vm, vmdef, commands, os, times, osproc, wordrecg, strtabs, modulegraphs, - pathutils + pathutils, pipelines when defined(nimPreviewSlimSystem): import std/syncio @@ -197,13 +197,11 @@ proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile; conf.symbolFiles = disabledSf let graph = newModuleGraph(cache, conf) - connectCallbacks(graph) + connectPipelineCallbacks(graph) if freshDefines: initDefines(conf.symbols) defineSymbol(conf.symbols, "nimscript") defineSymbol(conf.symbols, "nimconfig") - registerPass(graph, semPass) - registerPass(graph, evalPass) conf.searchPaths.add(conf.libpath) @@ -218,8 +216,9 @@ proc runNimScript*(cache: IdentCache; scriptName: AbsoluteFile; var vm = setupVM(m, cache, scriptName.string, graph, idgen) graph.vm = vm - graph.compileSystemModule() - discard graph.processModule(m, vm.idgen, stream) + graph.setPipeLinePass(EvalPass) + graph.compilePipelineSystemModule() + discard graph.processPipelineModule(m, vm.idgen, stream) # watch out, "newruntime" can be set within NimScript itself and then we need # to remember this: diff --git a/compiler/sem.nim b/compiler/sem.nim index 78596d5b2..a5d06f3fe 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -13,7 +13,7 @@ import ast, strutils, options, astalgo, trees, wordrecg, ropes, msgs, idents, renderer, types, platform, math, magicsys, nversion, nimsets, semfold, modulepaths, importer, - procfind, lookups, pragmas, passes, semdata, semtypinst, sigmatch, + procfind, lookups, pragmas, semdata, semtypinst, sigmatch, intsets, transf, vmdef, vm, aliases, cgmeth, lambdalifting, evaltempl, patterns, parampatterns, sempass2, linter, semmacrosanity, lowerings, plugins/active, lineinfos, strtabs, int128, @@ -666,38 +666,37 @@ proc addCodeForGenerics(c: PContext, n: PNode) = n.add prc.ast c.lastGenericIdx = c.generics.len -proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nosinks.} = - var c = newContext(graph, module) - c.idgen = idgen - c.enforceVoidContext = newType(tyTyped, nextTypeId(idgen), nil) - c.voidType = newType(tyVoid, nextTypeId(idgen), nil) +proc preparePContext*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PContext {.nosinks.} = + result = newContext(graph, module) + result.idgen = idgen + result.enforceVoidContext = newType(tyTyped, nextTypeId(idgen), nil) + result.voidType = newType(tyVoid, nextTypeId(idgen), nil) - if c.p != nil: internalError(graph.config, module.info, "sem.myOpen") - c.semConstExpr = semConstExpr - c.semExpr = semExpr - c.semTryExpr = tryExpr - c.semTryConstExpr = tryConstExpr - c.computeRequiresInit = computeRequiresInit - c.semOperand = semOperand - c.semConstBoolExpr = semConstBoolExpr - c.semOverloadedCall = semOverloadedCall - c.semInferredLambda = semInferredLambda - c.semGenerateInstance = generateInstance - c.semTypeNode = semTypeNode - c.instTypeBoundOp = sigmatch.instTypeBoundOp - c.hasUnresolvedArgs = hasUnresolvedArgs - c.templInstCounter = new int + if result.p != nil: internalError(graph.config, module.info, "sem.preparePContext") + result.semConstExpr = semConstExpr + result.semExpr = semExpr + result.semTryExpr = tryExpr + result.semTryConstExpr = tryConstExpr + result.computeRequiresInit = computeRequiresInit + result.semOperand = semOperand + result.semConstBoolExpr = semConstBoolExpr + result.semOverloadedCall = semOverloadedCall + result.semInferredLambda = semInferredLambda + result.semGenerateInstance = generateInstance + result.semTypeNode = semTypeNode + result.instTypeBoundOp = sigmatch.instTypeBoundOp + result.hasUnresolvedArgs = hasUnresolvedArgs + result.templInstCounter = new int - pushProcCon(c, module) - pushOwner(c, c.module) + pushProcCon(result, module) + pushOwner(result, result.module) - c.moduleScope = openScope(c) - c.moduleScope.addSym(module) # a module knows itself + result.moduleScope = openScope(result) + result.moduleScope.addSym(module) # a module knows itself if sfSystemModule in module.flags: graph.systemModule = module - c.topLevelScope = openScope(c) - result = c + result.topLevelScope = openScope(result) proc isImportSystemStmt(g: ModuleGraph; n: PNode): bool = if g.systemModule == nil: return false @@ -771,8 +770,7 @@ proc recoverContext(c: PContext) = while getCurrOwner(c).kind != skModule: popOwner(c) while c.p != nil and c.p.owner.kind != skModule: c.p = c.p.next -proc myProcess(context: PPassContext, n: PNode): PNode {.nosinks.} = - var c = PContext(context) +proc semWithPContext*(c: PContext, n: PNode): PNode {.nosinks.} = # no need for an expensive 'try' if we stop after the first error anyway: if c.config.errorMax <= 1: result = semStmtAndGenerateGenerics(c, n) @@ -793,13 +791,13 @@ proc myProcess(context: PPassContext, n: PNode): PNode {.nosinks.} = #if c.config.cmd == cmdIdeTools: findSuggest(c, n) storeRodNode(c, result) + proc reportUnusedModules(c: PContext) = for i in 0..high(c.unusedImports): if sfUsed notin c.unusedImports[i][0].flags: message(c.config, c.unusedImports[i][1], warnUnusedImportX, c.unusedImports[i][0].name.s) -proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode = - var c = PContext(context) +proc closePContext*(graph: ModuleGraph; c: PContext, n: PNode): PNode = if c.config.cmd == cmdIdeTools and not c.suggestionsMade: suggestSentinel(c) closeScope(c) # close module's scope @@ -814,6 +812,3 @@ proc myClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode = popOwner(c) popProcCon(c) sealRodFile(c) - -const semPass* = makePass(myOpen, myProcess, myClose, - isFrontend = true) diff --git a/compiler/suggest.nim b/compiler/suggest.nim index 637010ad5..384ceb271 100644 --- a/compiler/suggest.nim +++ b/compiler/suggest.nim @@ -36,7 +36,7 @@ import algorithm, sets, prefixmatches, parseutils, tables from wordrecg import wDeprecated, wError, wAddr, wYield when defined(nimsuggest): - import passes, tables, pathutils # importer + import tables, pathutils # importer const sep = '\t' diff --git a/compiler/vm.nim b/compiler/vm.nim index 18d840613..96f05c0f9 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -13,7 +13,7 @@ import semmacrosanity import std/[strutils, tables, parseutils], - msgs, vmdef, vmgen, nimsets, types, passes, + msgs, vmdef, vmgen, nimsets, types, parser, vmdeps, idents, trees, renderer, options, transf, gorgeimpl, lineinfos, btrees, macrocacheimpl, modulegraphs, sighashes, int128, vmprofiler @@ -2309,7 +2309,7 @@ proc setupGlobalCtx*(module: PSym; graph: ModuleGraph; idgen: IdGenerator) = else: refresh(PCtx graph.vm, module, idgen) -proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nosinks.} = +proc setupEvalGen*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nosinks.} = #var c = newEvalContext(module, emRepl) #c.features = {allowCast, allowInfiniteLoops} #pushStackFrame(c, newStackFrame()) @@ -2318,7 +2318,7 @@ proc myOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext setupGlobalCtx(module, graph, idgen) result = PCtx graph.vm -proc myProcess(c: PPassContext, n: PNode): PNode = +proc interpreterCode*(c: PPassContext, n: PNode): PNode = let c = PCtx(c) # don't eval errornous code: if c.oldErrorCount == c.config.errorCounter: @@ -2328,11 +2328,6 @@ proc myProcess(c: PPassContext, n: PNode): PNode = result = n c.oldErrorCount = c.config.errorCounter -proc myClose(graph: ModuleGraph; c: PPassContext, n: PNode): PNode = - result = myProcess(c, n) - -const evalPass* = makePass(myOpen, myProcess, myClose) - proc evalConstExprAux(module: PSym; idgen: IdGenerator; g: ModuleGraph; prc: PSym, n: PNode, mode: TEvalMode): PNode = diff --git a/doc/refc.md b/doc/refc.md index 6e4672ac5..4023748e6 100644 --- a/doc/refc.md +++ b/doc/refc.md @@ -138,6 +138,7 @@ The garbage collector won't try to free them, you need to call their respective when you are done with them or they will leak. + Heap dump ========= diff --git a/nimsuggest/nimsuggest.nim b/nimsuggest/nimsuggest.nim index a0a3b8e82..ce9681975 100644 --- a/nimsuggest/nimsuggest.nim +++ b/nimsuggest/nimsuggest.nim @@ -23,7 +23,7 @@ import strutils, os, parseopt, parseutils, sequtils, net, rdstdin, sexp # Do NOT import suggest. It will lead to weird bugs with # suggestionResultHook, because suggest.nim is included by sigmatch. # So we import that one instead. -import compiler / [options, commands, modules, sem, +import compiler / [options, commands, modules, passes, passaux, msgs, sigmatch, ast, idents, modulegraphs, prefixmatches, lineinfos, cmdlinehelper, From 2e2affb13ce13723e5ab1996d05f7b82c184a090 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Fri, 3 Mar 2023 23:13:27 +0800 Subject: [PATCH 3748/3768] test DLL generation with ORC (#21445) * test DLL generation with ORC * fixes * fixes refc * Update testament/categories.nim --- testament/categories.nim | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/testament/categories.nim b/testament/categories.nim index cae699327..c428ffc04 100644 --- a/testament/categories.nim +++ b/testament/categories.nim @@ -47,21 +47,24 @@ proc isTestFile*(file: string): bool = # --------------------- DLL generation tests ---------------------------------- -proc runBasicDLLTest(c, r: var TResults, cat: Category, options: string) = +proc runBasicDLLTest(c, r: var TResults, cat: Category, options: string, isOrc = false) = const rpath = when defined(macosx): " --passL:-rpath --passL:@loader_path" else: "" - var test1 = makeTest("lib/nimrtl.nim", options & " --outdir:tests/dll", cat) - test1.spec.action = actionCompile - testSpec c, test1 + if not defined(windows) or not isOrc: # todo fix me on windows + var test1 = makeTest("lib/nimrtl.nim", options & " --outdir:tests/dll", cat) + test1.spec.action = actionCompile + testSpec c, test1 var test2 = makeTest("tests/dll/server.nim", options & " --threads:on" & rpath, cat) test2.spec.action = actionCompile testSpec c, test2 - var test3 = makeTest("lib/nimhcr.nim", options & " --threads:off --outdir:tests/dll" & rpath, cat) - test3.spec.action = actionCompile - testSpec c, test3 + + if not isOrc: + var test3 = makeTest("lib/nimhcr.nim", options & " --threads:off --outdir:tests/dll" & rpath, cat) + test3.spec.action = actionCompile + testSpec c, test3 var test4 = makeTest("tests/dll/visibility.nim", options & " --threads:off --app:lib" & rpath, cat) test4.spec.action = actionCompile testSpec c, test4 @@ -76,8 +79,9 @@ proc runBasicDLLTest(c, r: var TResults, cat: Category, options: string) = putEnv(libpathenv, "tests/dll" & (if libpath.len > 0: ":" & libpath else: "")) defer: putEnv(libpathenv, libpath) - testSpec r, makeTest("tests/dll/client.nim", options & " --threads:on" & rpath, cat) - testSpec r, makeTest("tests/dll/nimhcr_unit.nim", options & " --threads:off" & rpath, cat) + if not isOrc: + testSpec r, makeTest("tests/dll/client.nim", options & " --threads:on" & rpath, cat) + testSpec r, makeTest("tests/dll/nimhcr_unit.nim", options & " --threads:off" & rpath, cat) testSpec r, makeTest("tests/dll/visibility.nim", options & " --threads:off" & rpath, cat) if "boehm" notin options: @@ -96,6 +100,8 @@ proc dllTests(r: var TResults, cat: Category, options: string) = runBasicDLLTest c, r, cat, options & " --mm:refc" runBasicDLLTest c, r, cat, options & " -d:release --mm:refc" + runBasicDLLTest c, r, cat, options, isOrc = true + runBasicDLLTest c, r, cat, options & " -d:release", isOrc = true when not defined(windows): # still cannot find a recent Windows version of boehm.dll: runBasicDLLTest c, r, cat, options & " --gc:boehm" From b2c5f8a05f2f9db6ed06663a077882eb112b3e3f Mon Sep 17 00:00:00 2001 From: Arnaud Moura Date: Fri, 3 Mar 2023 23:37:12 +0100 Subject: [PATCH 3749/3768] fixes #21461 (#21463) * fixes #21461; Move nim-gdb.py and add nim-lldb.py * fixes bad path for nim-gdb.py --- bin/nim-gdb | 2 +- bin/nim-gdb.bat | 2 +- compiler/installer.ini | 2 +- doc/packaging.md | 2 +- .../untestable/gdb/gdb_pretty_printer_test.py | 2 +- tools/{ => debug}/nim-gdb.py | 0 tools/debug/nim-lldb.py | 1039 +++++++++++++++++ 7 files changed, 1044 insertions(+), 5 deletions(-) rename tools/{ => debug}/nim-gdb.py (100%) create mode 100644 tools/debug/nim-lldb.py diff --git a/bin/nim-gdb b/bin/nim-gdb index 13b361f93..d4d60e432 100755 --- a/bin/nim-gdb +++ b/bin/nim-gdb @@ -14,7 +14,7 @@ else fi # Find out where the pretty printer Python module is -GDB_PYTHON_MODULE_PATH="$NIM_SYSROOT/tools/nim-gdb.py" +GDB_PYTHON_MODULE_PATH="$NIM_SYSROOT/tools/debug/nim-gdb.py" # Run GDB with the additional arguments that load the pretty printers # Set the environment variable `NIM_GDB` to overwrite the call to a diff --git a/bin/nim-gdb.bat b/bin/nim-gdb.bat index e98a2063c..b5537dd9d 100644 --- a/bin/nim-gdb.bat +++ b/bin/nim-gdb.bat @@ -3,7 +3,7 @@ for %%i in (nim.exe) do (set NIM_BIN=%%~dp$PATH:i) for %%i in ("%NIM_BIN%\..\") do (set NIM_ROOT=%%~fi) -set @GDB_PYTHON_MODULE_PATH=%NIM_ROOT%\tools\nim-gdb.py +set @GDB_PYTHON_MODULE_PATH=%NIM_ROOT%\tools\debug\nim-gdb.py set @NIM_GDB=gdb.exe @echo source %@GDB_PYTHON_MODULE_PATH%> wingdbcommand.txt diff --git a/compiler/installer.ini b/compiler/installer.ini index f67ca5c25..226682715 100644 --- a/compiler/installer.ini +++ b/compiler/installer.ini @@ -65,7 +65,7 @@ Files: "compiler" Files: "doc" Files: "doc/html" Files: "tools" -Files: "tools/nim-gdb.py" +Files: "tools/debug/nim-gdb.py" Files: "nimpretty" Files: "testament" Files: "nimsuggest" diff --git a/doc/packaging.md b/doc/packaging.md index 522ca6d44..b742bef28 100644 --- a/doc/packaging.md +++ b/doc/packaging.md @@ -71,7 +71,7 @@ What to install: - The expected stdlib location is `/usr/lib/nim/lib`, previously it was just `/usr/lib/nim` - `nimdoc.css` and `nimdoc.cls` from the `doc` folder should go into `/usr/lib/nim/doc/` -- `tools/nim-gdb.py` should go into `/usr/lib/nim/tools/` +- `tools/debug/nim-gdb.py` should go into `/usr/lib/nim/tools/` - `tools/dochack/dochack.js` should be installed to `/usr/lib/nim/tools/dochack/` - Global configuration files under `/etc/nim` - Optionally: manpages, documentation, shell completion diff --git a/tests/untestable/gdb/gdb_pretty_printer_test.py b/tests/untestable/gdb/gdb_pretty_printer_test.py index a96df9992..aed0cfeb0 100644 --- a/tests/untestable/gdb/gdb_pretty_printer_test.py +++ b/tests/untestable/gdb/gdb_pretty_printer_test.py @@ -8,7 +8,7 @@ import sys # frontends might still be broken. gdb.execute("set python print-stack full") -gdb.execute("source ../../../tools/nim-gdb.py") +gdb.execute("source ../../../tools/debug/nim-gdb.py") # debug all instances of the generic function `myDebug`, should be 14 gdb.execute("rbreak myDebug") gdb.execute("run") diff --git a/tools/nim-gdb.py b/tools/debug/nim-gdb.py similarity index 100% rename from tools/nim-gdb.py rename to tools/debug/nim-gdb.py diff --git a/tools/debug/nim-lldb.py b/tools/debug/nim-lldb.py new file mode 100644 index 000000000..625b75988 --- /dev/null +++ b/tools/debug/nim-lldb.py @@ -0,0 +1,1039 @@ +import lldb +from collections import OrderedDict +from typing import Union + + +def sbvaluegetitem(self: lldb.SBValue, name: Union[int, str]) -> lldb.SBValue: + if isinstance(name, str): + return self.GetChildMemberWithName(name) + else: + return self.GetChildAtIndex(name) + + +# Make this easier to work with +lldb.SBValue.__getitem__ = sbvaluegetitem + + +def colored(in_str, *args, **kwargs): + # TODO: Output in color if user is in terminal + return in_str + + +def reprEnum(val, typ): + """ + this is a port of the nim runtime function `reprEnum` to python + NOTE: DOES NOT WORK WITH ORC + """ + val = int(val) + n = typ["node"] + sons_type = n["sons"].type.GetPointeeType().GetPointeeType() + sons = n["sons"].deref.Cast(sons_type.GetPointerType().GetArrayType(3)) + flags = int(typ["flags"].unsigned) + # 1 << 6 is {ntfEnumHole} + if ((1 << 6) & flags) == 0: + offset = val - sons[0]["offset"].unsigned + if offset >= 0 and 0 < n["len"].unsigned: + return NCSTRING(sons[offset]["name"])[1:-1] + else: + # ugh we need a slow linear search: + for i in range(n["len"].unsigned): + if sons[i]["offset"].unsigned == val: + return NCSTRING(sons[i]["name"])[1:-1] + + return str(val) + " (invalid data!)" + + +def get_nti(value, nim_name=None): + """DOES NOT WORK WITH ORC""" + name_split = value.type.name.split("_") + type_nim_name = nim_name or name_split[1] + id_string = name_split[-1].split(" ")[0] + + type_info_name = "NTI" + type_nim_name.lower() + "__" + id_string + "_" + print("TYPEINFONAME: ", type_info_name) + nti = value.target.FindFirstGlobalVariable(type_info_name) + if nti is None: + type_info_name = "NTI" + "__" + id_string + "_" + nti = value.target.FindFirstGlobalVariable(type_info_name) + if nti is None: + print( + f"NimEnumPrinter: lookup global symbol: '{type_info_name}' failed for {value.type.name}.\n" + ) + return type_nim_name, nti + + +def enum_to_string(value): + type_nim_name, nti = get_nti(value) + if nti is None: + return type_nim_name + "(" + str(value.unsigned) + ")" + return reprEnum(value.signed, nti), nti + + +def to_string(value): + # For getting NimStringDesc * value + data = value["data"] + try: + size = int(value["Sup"]["len"].unsigned) + if size > 2**14: + return None + except TypeError: + return None + + cast = data.Cast(value.target.FindFirstType("char").GetArrayType(size)) + return bytearray(cast.data.uint8s).decode("utf-8") + + +def to_stringV2(value: lldb.SBValue): + # For getting NimStringDesc * value + data = value["p"]["data"] + try: + size = int(value["len"].signed) + if size > 2**14: + return "... (too long)" + except TypeError: + return "" + + base_data_type = data.type.GetArrayElementType().GetTypedefedType() + cast = data.Cast(base_data_type.GetArrayType(size)) + return bytearray(cast.data.uint8s).decode("utf-8") + + +def NimStringDesc(value, internal_dict): + res = to_string(value) + if res: + return colored('"' + res + '"', "red") + else: + return str(value) + + +def NimStringV2(value: lldb.SBValue, internal_dict): + res = to_stringV2(value.GetNonSyntheticValue()) + if res is not None: + return colored('"' + res + '"', "red") + else: + return str(value) + + +def NCSTRING(value: lldb.SBValue, internal_dict=None): + ty = value.Dereference().type + val = value.target.CreateValueFromAddress( + value.name or "temp", lldb.SBAddress(value.unsigned, value.target), ty + ).AddressOf() + return val.summary + + +def ObjectV1(value, internal_dict): + if not value.num_children and not value.value: + return "" + + ignore_fields = set() + if "colonObjectType" in value.type.name: + value = value.Dereference() + ignore_fields.add("Sup") + + if not value.type.name: + return "" + + summary = value.summary + if summary is not None: + return summary + + if "_" in value.type.name: + obj_name = value.type.name.split("_")[1].replace("colonObjectType", "") + else: + obj_name = value.type.name + + obj_name = colored(obj_name, "green") + + num_children = value.num_children + + fields = ", ".join( + [ + value[i].name + + ": " + + (value[i].summary or value[i].value or value[i].type.name or "not found") + for i in range(num_children) + if value[i].name not in ignore_fields + ] + ) + + res = f"{obj_name}({fields})" + return res + + +def ObjectV2(value: lldb.SBValue, internal_dict): + custom_summary = get_summary(value) + if not custom_summary is None: + return custom_summary + + orig_value = value.GetNonSyntheticValue() + while orig_value.type.is_pointer: + orig_value = orig_value.Dereference() + + if "_" in orig_value.type.name: + obj_name = orig_value.type.name.split("_")[1].replace("colonObjectType", "") + else: + obj_name = orig_value.type.name + + num_children = value.num_children + fields = [] + + for i in range(num_children): + fields.append(f"{value[i].name}: {value[i].summary}") + + res = f"{obj_name}(" + ", ".join(fields) + ")" + return res + + +def Number(value: lldb.SBValue, internal_dict): + while value.type.is_pointer: + value = value.Dereference() + return colored(str(value.signed), "yellow") + + +def Float(value: lldb.SBValue, internal_dict): + while value.type.is_pointer: + value = value.Dereference() + return colored(str(value.value), "yellow") + + +def UnsignedNumber(value: lldb.SBValue, internal_dict): + while value.type.is_pointer: + value = value.Dereference() + return colored(str(value.unsigned), "yellow") + + +def Bool(value: lldb.SBValue, internal_dict): + while value.type.is_pointer: + value = value.Dereference() + return colored(str(value.GetValue()), "red") + + +def CharArray(value: lldb.SBValue, internal_dict): + return str([colored(f"'{char}'", "red") for char in value.uint8s]) + + +def Array(value: lldb.SBValue, internal_dict): + value = value.GetNonSyntheticValue() + return "[" + ", ".join([value[i].summary for i in range(value.num_children)]) + "]" + + +def Tuple(value: lldb.SBValue, internal_dict): + while value.type.is_pointer: + value = value.Dereference() + + num_children = value.num_children + + fields = [] + + for i in range(num_children): + key = value[i].name + val = value[i].summary + if key.startswith("Field"): + fields.append(f"{val}") + else: + fields.append(f"{key}: {val}") + + return "(" + ", ".join(fields) + f")" + + +def Enum(value, internal_dict): + tname = value.type.name.split("_")[1] + return colored(f"{tname}." + str(value.signed), "blue") + + +def EnumSet(value, internal_dict): + type_nim_name = value.type.name.split("_")[2] + # type_nim_name, nti = get_nti(value, type_nim_name) + + val = int(value.signed) + # if nti: + # enum_strings = [] + # i = 0 + # while val > 0: + # if (val & 1) == 1: + # enum_strings.append(reprEnum(i, nti)) + # val = val >> 1 + # i += 1 + + # return '{' + ', '.join(enum_strings) + '}' + return colored(f"{type_nim_name}." + str(val), "blue") + + +def Set(value, internal_dict): + vals = [] + max_vals = 7 + for child in value.children: + vals.append(child.value) + if len(vals) > max_vals: + vals.append("...") + break + + return "{" + ", ".join(vals) + "}" + + +def Table(value: lldb.SBValue, internal_dict): + fields = [] + + for i in range(value.num_children): + key = value[i].name + val = value[i].summary + fields.append(f"{key}: {val}") + + table_suffix = "Table" + return "{" + ", ".join(fields) + f"}}.{table_suffix}" + + +def HashSet(value: lldb.SBValue, internal_dict): + fields = [] + + for i in range(value.num_children): + fields.append(f"{value[i].summary}") + + table_suffix = "HashSet" + + return "{" + ", ".join(fields) + f"}}.{table_suffix}" + + +def StringTable(value: lldb.SBValue, internal_dict): + table = value.GetNonSyntheticValue() + mode = table["mode"].unsigned + + table_suffix = "StringTable" + + table_mode = "" + if mode == 0: + table_mode = "Case Sensitive" + elif mode == 1: + table_mode = "Case Insensitive" + elif mode == 2: + table_mode = "Style Insensitive" + + fields = [] + + for i in range(value.num_children): + key = value[i].name + val = value[i].summary + fields.append(f"{key}: {val}") + + return "{" + ", ".join(fields) + f"}}.{table_suffix}({table_mode})" + + +def Sequence(value: lldb.SBValue, internal_dict): + value = value.GetNonSyntheticValue() + + data_len = int(value["len"].unsigned) + data = value["p"]["data"] + base_data_type = data.type.GetArrayElementType() + + cast = data.Cast(base_data_type.GetArrayType(data_len)) + + return ( + "@[" + + ", ".join([cast[i].summary or cast[i].type.name for i in range(data_len)]) + + "]" + ) + + +class StringChildrenProvider: + def __init__(self, value: lldb.SBValue, internalDict): + self.value = value + self.data_type: lldb.SBType + self.first_element: lldb.SBValue + self.update() + self.count = 0 + + def num_children(self): + return self.count + + def get_child_index(self, name): + return int(name.lstrip("[").rstrip("]")) + + def get_child_at_index(self, index): + offset = index * self.data_size + return self.first_element.CreateChildAtOffset( + "[" + str(index) + "]", offset, self.data_type + ) + + def update(self): + data = self.value["p"]["data"] + size = int(self.value["len"].unsigned) + + self.count = size + self.first_element = data + + self.data_type = data.type.GetArrayElementType().GetTypedefedType() + self.data_size = self.data_type.GetByteSize() + + def has_children(self): + return bool(self.num_children()) + + +class ArrayChildrenProvider: + def __init__(self, value: lldb.SBValue, internalDict): + self.value = value + self.data_type: lldb.SBType + self.first_element: lldb.SBValue + self.update() + + def num_children(self): + return self.value.num_children + + def get_child_index(self, name: str): + return int(name.lstrip("[").rstrip("]")) + + def get_child_at_index(self, index): + offset = index * self.value[index].GetByteSize() + return self.first_element.CreateChildAtOffset( + "[" + str(index) + "]", offset, self.data_type + ) + + def update(self): + if not self.has_children(): + return + + self.first_element = self.value[0] + self.data_type = self.value.type.GetArrayElementType() + + def has_children(self): + return bool(self.num_children()) + + +class SeqChildrenProvider: + def __init__(self, value: lldb.SBValue, internalDict): + self.value = value + self.data_type: lldb.SBType + self.first_element: lldb.SBValue + self.data: lldb.SBValue + self.update() + + def num_children(self): + return int(self.value["len"].unsigned) + + def get_child_index(self, name: str): + return int(name.lstrip("[").rstrip("]")) + + def get_child_at_index(self, index): + offset = index * self.data[index].GetByteSize() + return self.first_element.CreateChildAtOffset( + "[" + str(index) + "]", offset, self.data_type + ) + + def update(self): + if not self.has_children(): + return + + data = self.value["p"]["data"] + self.data_type = data.type.GetArrayElementType() + + self.data = data.Cast(self.data_type.GetArrayType(self.num_children())) + self.first_element = self.data + + def has_children(self): + return bool(self.num_children()) + + +class ObjectChildrenProvider: + def __init__(self, value: lldb.SBValue, internalDict): + self.value = value + self.data_type: lldb.SBType + self.first_element: lldb.SBValue + self.data: lldb.SBValue + self.children: OrderedDict[str, int] = OrderedDict() + self.child_list: list[lldb.SBValue] = [] + self.update() + + def num_children(self): + return len(self.children) + + def get_child_index(self, name: str): + return self.children[name] + + def get_child_at_index(self, index): + return self.child_list[index] + + def populate_children(self): + self.children.clear() + self.child_list = [] + stack = [self.value] + + index = 0 + + while stack: + cur_val = stack.pop() + while cur_val.type.is_pointer: + cur_val = cur_val.Dereference() + + if cur_val.num_children > 0 and cur_val[0].name == "m_type": + if "_" in cur_val.type.name: + tname = cur_val.type.name.split("_")[1].replace( + "colonObjectType", "" + ) + else: + tname = cur_val.type.name + if tname == "TNimTypeV2": + # We've reached the end + break + + if ( + cur_val.num_children > 0 + and cur_val[0].name == "Sup" + and cur_val[0].type.name.startswith("tyObject") + ): + stack.append(cur_val[0]) + + for i in range(cur_val.num_children): + child = cur_val[i] + if child.name == "Sup": + continue + self.children[child.name] = index + self.child_list.append(child) + index += 1 + + def update(self): + self.populate_children() + + def has_children(self): + return bool(self.num_children()) + + +class HashSetChildrenProvider: + def __init__(self, value: lldb.SBValue, internalDict): + self.value = value + self.child_list: list[lldb.SBValue] = [] + self.update() + + def num_children(self): + return len(self.child_list) + + def get_child_index(self, name: str): + return int(name.lstrip("[").rstrip("]")) + + def get_child_at_index(self, index): + return self.child_list[index] + + def update(self): + self.child_list = [] + data = self.value["data"] + + tuple_len = int(data["len"].unsigned) + tuple = data["p"]["data"] + + base_data_type = tuple.type.GetArrayElementType() + + cast = tuple.Cast(base_data_type.GetArrayType(tuple_len)) + + index = 0 + for i in range(tuple_len): + el = cast[i] + field0 = int(el["Field0"].unsigned) + if field0 == 0: + continue + key = el["Field1"] + child = key.CreateValueFromAddress( + f"[{str(index)}]", key.GetLoadAddress(), key.GetType() + ) + index += 1 + + self.child_list.append(child) + + def has_children(self): + return bool(self.num_children()) + + +class SetCharChildrenProvider: + def __init__(self, value: lldb.SBValue, internalDict): + self.value = value + self.ty = self.value.target.FindFirstType("char") + self.child_list: list[lldb.SBValue] = [] + self.update() + + def num_children(self): + return len(self.child_list) + + def get_child_index(self, name: str): + return int(name.lstrip("[").rstrip("]")) + + def get_child_at_index(self, index): + return self.child_list[index] + + def update(self): + self.child_list = [] + cur_pos = 0 + for child in self.value.children: + child_val = child.signed + if child_val != 0: + temp = child_val + num_bits = 8 + while temp != 0: + is_set = temp & 1 + if is_set == 1: + data = lldb.SBData.CreateDataFromInt(cur_pos) + child = self.value.synthetic_child_from_data( + f"[{len(self.child_list)}]", data, self.ty + ) + self.child_list.append(child) + temp = temp >> 1 + cur_pos += 1 + num_bits -= 1 + cur_pos += num_bits + else: + cur_pos += 8 + + def has_children(self): + return bool(self.num_children()) + + +class SetIntChildrenProvider: + def __init__(self, value: lldb.SBValue, internalDict): + self.value = value + self.ty = self.value.target.FindFirstType(f"NI64") + self.child_list: list[lldb.SBValue] = [] + self.update() + + def num_children(self): + return len(self.child_list) + + def get_child_index(self, name: str): + return int(name.lstrip("[").rstrip("]")) + + def get_child_at_index(self, index): + return self.child_list[index] + + def update(self): + self.child_list = [] + bits = self.value.GetByteSize() * 8 + + cur_pos = -(bits // 2) + + if self.value.num_children > 0: + children = self.value.children + else: + children = [self.value] + + for child in children: + child_val = child.signed + if child_val != 0: + temp = child_val + num_bits = 8 + while temp != 0: + is_set = temp & 1 + if is_set == 1: + data = lldb.SBData.CreateDataFromInt(cur_pos) + child = self.value.synthetic_child_from_data( + f"[{len(self.child_list)}]", data, self.ty + ) + self.child_list.append(child) + temp = temp >> 1 + cur_pos += 1 + num_bits -= 1 + cur_pos += num_bits + else: + cur_pos += 8 + + def has_children(self): + return bool(self.num_children()) + + +class SetUIntChildrenProvider: + def __init__(self, value: lldb.SBValue, internalDict): + self.value = value + self.ty = self.value.target.FindFirstType(f"NU64") + self.child_list: list[lldb.SBValue] = [] + self.update() + + def num_children(self): + return len(self.child_list) + + def get_child_index(self, name: str): + return int(name.lstrip("[").rstrip("]")) + + def get_child_at_index(self, index): + return self.child_list[index] + + def update(self): + self.child_list = [] + + cur_pos = 0 + if self.value.num_children > 0: + children = self.value.children + else: + children = [self.value] + + for child in children: + child_val = child.signed + if child_val != 0: + temp = child_val + num_bits = 8 + while temp != 0: + is_set = temp & 1 + if is_set == 1: + data = lldb.SBData.CreateDataFromInt(cur_pos) + child = self.value.synthetic_child_from_data( + f"[{len(self.child_list)}]", data, self.ty + ) + self.child_list.append(child) + temp = temp >> 1 + cur_pos += 1 + num_bits -= 1 + cur_pos += num_bits + else: + cur_pos += 8 + + def has_children(self): + return bool(self.num_children()) + + +class SetEnumChildrenProvider: + def __init__(self, value: lldb.SBValue, internalDict): + self.value = value + self.ty = self.value.target.FindFirstType(f"NU64") + self.child_list: list[lldb.SBValue] = [] + self.update() + + def num_children(self): + return len(self.child_list) + + def get_child_index(self, name: str): + return int(name.lstrip("[").rstrip("]")) + + def get_child_at_index(self, index): + return self.child_list[index] + + def update(self): + self.child_list = [] + + cur_pos = 0 + if self.value.num_children > 0: + children = self.value.children + else: + children = [self.value] + + for child in children: + child_val = child.unsigned + if child_val != 0: + temp = child_val + num_bits = 8 + while temp != 0: + is_set = temp & 1 + if is_set == 1: + data = lldb.SBData.CreateDataFromInt(cur_pos) + child = self.value.synthetic_child_from_data( + f"[{len(self.child_list)}]", data, self.ty + ) + self.child_list.append(child) + temp = temp >> 1 + cur_pos += 1 + num_bits -= 1 + cur_pos += num_bits + else: + cur_pos += 8 + + def has_children(self): + return bool(self.num_children()) + + +class TableChildrenProvider: + def __init__(self, value: lldb.SBValue, internalDict): + self.value = value + self.children: OrderedDict[str, int] = OrderedDict() + self.child_list: list[lldb.SBValue] = [] + self.update() + + def num_children(self): + return len(self.child_list) + + def get_child_index(self, name: str): + return self.children[name] + + def get_child_at_index(self, index): + return self.child_list[index] + + def update(self): + self.child_list = [] + data = self.value["data"] + + tuple_len = int(data["len"].unsigned) + tuple = data["p"]["data"] + + base_data_type = tuple.type.GetArrayElementType() + + cast = tuple.Cast(base_data_type.GetArrayType(tuple_len)) + + index = 0 + for i in range(tuple_len): + el = cast[i] + field0 = int(el["Field0"].unsigned) + if field0 == 0: + continue + key = el["Field1"] + val = el["Field2"] + child = val.CreateValueFromAddress( + key.summary, val.GetLoadAddress(), val.GetType() + ) + self.child_list.append(child) + self.children[key.summary] = index + index += 1 + + def has_children(self): + return bool(self.num_children()) + + +class StringTableChildrenProvider: + def __init__(self, value: lldb.SBValue, internalDict): + self.value = value + self.children: OrderedDict[str, int] = OrderedDict() + self.child_list: list[lldb.SBValue] = [] + self.update() + + def num_children(self): + return len(self.child_list) + + def get_child_index(self, name: str): + return self.children[name] + + def get_child_at_index(self, index): + return self.child_list[index] + + def update(self): + self.children.clear() + self.child_list = [] + data = self.value["data"] + + tuple_len = int(data["len"].unsigned) + tuple = data["p"]["data"] + + base_data_type = tuple.type.GetArrayElementType() + + cast = tuple.Cast(base_data_type.GetArrayType(tuple_len)) + + index = 0 + for i in range(tuple_len): + el = cast[i] + field0 = int(el["Field2"].unsigned) + if field0 == 0: + continue + key = el["Field0"] + val = el["Field1"] + child = val.CreateValueFromAddress( + key.summary, val.GetLoadAddress(), val.GetType() + ) + self.child_list.append(child) + self.children[key.summary] = index + index += 1 + + def has_children(self): + return bool(self.num_children()) + + +class CustomObjectChildrenProvider: + def __init__(self, value: lldb.SBValue, internalDict): + print("CUSTOMOBJ: ", value.name) + self.value: lldb.SBValue = get_synthetic(value) or value + for child in self.value.children: + print(child) + + def num_children(self): + return self.value.num_children + + def get_child_index(self, name: str): + return self.value.GetIndexOfChildWithName(name) + + def get_child_at_index(self, index): + return self.value.GetChildAtIndex(index) + + def update(self): + pass + + def has_children(self): + return self.num_children() > 0 + + +def echo(debugger: lldb.SBDebugger, command: str, result, internal_dict): + debugger.HandleCommand("po " + command) + + +SUMMARY_FUNCTIONS: dict[str, lldb.SBFunction] = {} +SYNTHETIC_FUNCTIONS: dict[str, lldb.SBFunction] = {} + + +def get_summary(value: lldb.SBValue) -> Union[str, None]: + base_type = get_base_type(value.type) + + fn = SUMMARY_FUNCTIONS.get(base_type.name) + if fn is None: + return None + + res = executeCommand( + f"{fn.name}(*({base_type.GetPointerType().name})" + str(value.GetLoadAddress()) + ");" + ) + + if res.error.fail: + return None + + return res.summary.strip('"') + + +def get_synthetic(value: lldb.SBValue) -> Union[lldb.SBValue, None]: + base_type = get_base_type(value.type) + + fn = SYNTHETIC_FUNCTIONS.get(base_type.name) + if fn is None: + return None + + res = executeCommand( + f"{fn.name}(*({base_type.GetPointerType().name})" + str(value.GetLoadAddress()) + ");" + ) + + if res.error.fail: + return None + + return res + + +def get_base_type(ty: lldb.SBType) -> lldb.SBType: + temp = ty + while temp.IsPointerType(): + temp = temp.GetPointeeType() + return temp + + +def breakpoint_function_wrapper(frame: lldb.SBFrame, bp_loc, internal_dict): + """This allows function calls to Nim for custom object summaries and synthetic children""" + debugger = lldb.debugger + + global SUMMARY_FUNCTIONS + global SYNTHETIC_FUNCTIONS + for tname, fn in SYNTHETIC_FUNCTIONS.items(): + print("DELETING SYNTH: ", tname) + debugger.HandleCommand(f"type synthetic delete -w nim {tname}") + + SUMMARY_FUNCTIONS = {} + SYNTHETIC_FUNCTIONS = {} + + target: lldb.SBTarget = debugger.GetSelectedTarget() + print("BREAKPOINT") + module = frame.GetSymbolContext(lldb.eSymbolContextModule).module + + for sym in module: + if not sym.name.startswith("lldbDebugSummary") and not sym.name.startswith( + "lldbDebugSynthetic" + ): + continue + + print("SYM: ", sym.name) + + fn_syms: lldb.SBSymbolContextList = target.FindFunctions(sym.name) + if not fn_syms.GetSize() > 0: + continue + fn_sym: lldb.SBSymbolContext = fn_syms.GetContextAtIndex(0) + + print("fn found!") + + fn: lldb.SBFunction = fn_sym.function + fn_type: lldb.SBType = fn.type + arg_types: lldb.SBTypeList = fn_type.GetFunctionArgumentTypes() + + if not arg_types.GetSize() > 0: + continue + arg_type: lldb.SBType = get_base_type(arg_types.GetTypeAtIndex(0)) + + print("FIRST ARG TYPE: ", arg_type.name) + + if sym.name.startswith("lldbDebugSummary"): + SUMMARY_FUNCTIONS[arg_type.name] = fn + elif sym.name.startswith("lldbDebugSynthetic"): + SYNTHETIC_FUNCTIONS[arg_type.name] = fn + debugger.HandleCommand( + f"type synthetic add -w nim -l {__name__}.CustomObjectChildrenProvider -x {arg_type.name}$" + ) + + +def executeCommand(command, *args): + debugger = lldb.debugger + process = debugger.GetSelectedTarget().GetProcess() + frame: lldb.SBFrame = process.GetSelectedThread().GetSelectedFrame() + # module = frame.GetSymbolContext(lldb.eSymbolContextModule).module + # for sym in module: + # print("SYM: ", sym.name) + # target = debugger.GetSelectedTarget() + + expr_options = lldb.SBExpressionOptions() + expr_options.SetIgnoreBreakpoints(False) + expr_options.SetFetchDynamicValue(lldb.eDynamicCanRunTarget) + expr_options.SetTimeoutInMicroSeconds(30 * 1000 * 1000) # 30 second timeout + expr_options.SetTryAllThreads(True) + expr_options.SetUnwindOnError(False) + expr_options.SetGenerateDebugInfo(True) + expr_options.SetLanguage(lldb.eLanguageTypeC) + expr_options.SetCoerceResultToId(True) + res = frame.EvaluateExpression(command, expr_options) + # if res.error.fail: + # print("ERROR: ", res.error.GetError()) + # return str(res.error) + return res + + +def __lldb_init_module(debugger, internal_dict): + # debugger.HandleCommand(f"type summary add -w nim -n any -F {__name__}.CatchAll -x .*") + debugger.HandleCommand(f"type summary add -w nim -n sequence -F {__name__}.Sequence -x tySequence_+[[:alnum:]]+$") + debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.SeqChildrenProvider -x tySequence_+[[:alnum:]]+$") + + debugger.HandleCommand(f"type summary add -w nim -n chararray -F {__name__}.CharArray -x char\s+[\d+]") + debugger.HandleCommand(f"type summary add -w nim -n array -F {__name__}.Array -x tyArray_+[[:alnum:]]+") + debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.ArrayChildrenProvider -x tyArray_+[[:alnum:]]+") + debugger.HandleCommand(f"type summary add -w nim -n string -F {__name__}.NimStringDesc NimStringDesc") + + debugger.HandleCommand(f"type summary add -w nim -n stringv2 -F {__name__}.NimStringV2 -x NimStringV2$") + debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.StringChildrenProvider -x NimStringV2$") + + debugger.HandleCommand(f"type summary add -w nim -n cstring -F {__name__}.NCSTRING NCSTRING") + + debugger.HandleCommand(f"type summary add -w nim -n object -F {__name__}.ObjectV2 -x tyObject_+[[:alnum:]]+_+[[:alnum:]]+") + debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.ObjectChildrenProvider -x tyObject_+[[:alnum:]]+_+[[:alnum:]]+$") + + debugger.HandleCommand(f"type summary add -w nim -n tframe -F {__name__}.ObjectV2 -x TFrame$") + + debugger.HandleCommand(f"type summary add -w nim -n rootobj -F {__name__}.ObjectV2 -x RootObj$") + + debugger.HandleCommand(f"type summary add -w nim -n enum -F {__name__}.Enum -x tyEnum_+[[:alnum:]]+_+[[:alnum:]]+") + debugger.HandleCommand(f"type summary add -w nim -n hashset -F {__name__}.HashSet -x tyObject_+HashSet_+[[:alnum:]]+") + debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.HashSetChildrenProvider -x tyObject_+HashSet_+[[:alnum:]]+") + debugger.HandleCommand(f"type summary add -w nim -n setuint -F {__name__}.Set -x tySet_+tyInt_+[[:alnum:]]+") + debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.SetIntChildrenProvider -x tySet_+tyInt[0-9]+_+[[:alnum:]]+") + debugger.HandleCommand(f"type summary add -w nim -n setint -F {__name__}.Set -x tySet_+tyInt[0-9]+_+[[:alnum:]]+") + debugger.HandleCommand(f"type summary add -w nim -n setuint2 -F {__name__}.Set -x tySet_+tyUInt[0-9]+_+[[:alnum:]]+") + debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.SetUIntChildrenProvider -x tySet_+tyUInt[0-9]+_+[[:alnum:]]+") + debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.SetUIntChildrenProvider -x tySet_+tyInt_+[[:alnum:]]+") + debugger.HandleCommand(f"type summary add -w nim -n setenum -F {__name__}.EnumSet -x tySet_+tyEnum_+[[:alnum:]]+_+[[:alnum:]]+") + debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.SetUIntChildrenProvider -x tySet_+tyEnum_+[[:alnum:]]+_+[[:alnum:]]+") + debugger.HandleCommand(f"type summary add -w nim -n setchar -F {__name__}.Set -x tySet_+tyChar_+[[:alnum:]]+") + debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.SetCharChildrenProvider -x tySet_+tyChar_+[[:alnum:]]+") + debugger.HandleCommand(f"type summary add -w nim -n table -F {__name__}.Table -x tyObject_+Table_+[[:alnum:]]+") + debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.TableChildrenProvider -x tyObject_+Table_+[[:alnum:]]+") + debugger.HandleCommand(f"type summary add -w nim -n stringtable -F {__name__}.StringTable -x tyObject_+StringTableObj_+[[:alnum:]]+") + debugger.HandleCommand(f"type synthetic add -w nim -l {__name__}.StringTableChildrenProvider -x tyObject_+StringTableObj_+[[:alnum:]]+") + debugger.HandleCommand(f"type summary add -w nim -n tuple2 -F {__name__}.Tuple -x tyObject_+Tuple_+[[:alnum:]]+") + debugger.HandleCommand(f"type summary add -w nim -n tuple -F {__name__}.Tuple -x tyTuple_+[[:alnum:]]+") + # debugger.HandleCommand(f"type summary add -w nim -n TNimType -F {__name__}.Object TNimType") + debugger.HandleCommand(f"type summary add -w nim -n TNimTypeV2 -F {__name__}.ObjectV2 TNimTypeV2") + # debugger.HandleCommand(f"type summary add -w nim -n TNimNode -F {__name__}.Object TNimNode") + debugger.HandleCommand(f"type summary add -w nim -n float -F {__name__}.Float NF") + debugger.HandleCommand(f"type summary add -w nim -n float32 -F {__name__}.Float NF32") + debugger.HandleCommand(f"type summary add -w nim -n float64 -F {__name__}.Float NF64") + debugger.HandleCommand(f"type summary add -w nim -n integer -F {__name__}.Number -x NI") + debugger.HandleCommand(f"type summary add -w nim -n integer8 -F {__name__}.Number -x NI8") + debugger.HandleCommand(f"type summary add -w nim -n integer16 -F {__name__}.Number -x NI16") + debugger.HandleCommand(f"type summary add -w nim -n integer32 -F {__name__}.Number -x NI32") + debugger.HandleCommand(f"type summary add -w nim -n integer64 -F {__name__}.Number -x NI64") + debugger.HandleCommand(f"type summary add -w nim -n bool -F {__name__}.Bool -x bool") + debugger.HandleCommand(f"type summary add -w nim -n bool2 -F {__name__}.Bool -x NIM_BOOL") + debugger.HandleCommand(f"type summary add -w nim -n uinteger -F {__name__}.UnsignedNumber -x NU") + debugger.HandleCommand(f"type summary add -w nim -n uinteger8 -F {__name__}.UnsignedNumber -x NU8") + debugger.HandleCommand(f"type summary add -w nim -n uinteger16 -F {__name__}.UnsignedNumber -x NU16") + debugger.HandleCommand(f"type summary add -w nim -n uinteger32 -F {__name__}.UnsignedNumber -x NU32") + debugger.HandleCommand(f"type summary add -w nim -n uinteger64 -F {__name__}.UnsignedNumber -x NU64") + debugger.HandleCommand("type category enable nim") + debugger.HandleCommand(f"command script add -f {__name__}.echo echo") + debugger.HandleCommand(f"command script add -f {__name__}.handle_command ddp") + debugger.HandleCommand(f"breakpoint command add -F {__name__}.breakpoint_function_wrapper --script-type python 1") \ No newline at end of file From 6994e1b1d7962f9c61887c7e606aeccbebf9bef6 Mon Sep 17 00:00:00 2001 From: quantimnot <54247259+quantimnot@users.noreply.github.com> Date: Sat, 4 Mar 2023 01:57:30 -0500 Subject: [PATCH 3750/3768] `--embedsrc` for JavaScript (#21467) Co-authored-by: quantimnot --- compiler/jsgen.nim | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/jsgen.nim b/compiler/jsgen.nim index 5df40b996..d0a143793 100644 --- a/compiler/jsgen.nim +++ b/compiler/jsgen.nim @@ -735,6 +735,8 @@ proc genLineDir(p: PProc, n: PNode) = let line = toLinenumber(n.info) if line < 0: return + if optEmbedOrigSrc in p.config.globalOptions: + lineF(p, "//$1$n", [sourceLine(p.config, n.info)]) if optLineDir in p.options or optLineDir in p.config.options: lineF(p, "$1", [lineDir(p.config, n.info, line)]) if hasFrameInfo(p): From d950e5f3a8efe0c3be8e773cce4a89807833ffea Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Sat, 4 Mar 2023 20:07:33 +0800 Subject: [PATCH 3751/3768] remove nosinks hacks from compiler (#21469) --- compiler/cgen.nim | 2 +- compiler/depends.nim | 2 +- compiler/passes.nim | 4 ++-- compiler/sem.nim | 4 ++-- compiler/semcall.nim | 2 +- compiler/seminst.nim | 2 +- compiler/semstmts.nim | 2 +- compiler/sigmatch.nim | 2 +- compiler/vm.nim | 6 +++--- 9 files changed, 13 insertions(+), 13 deletions(-) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index d051934b5..e24a2cb1e 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -1940,7 +1940,7 @@ template injectG() {.dirty.} = graph.backend = newModuleList(graph) let g = BModuleList(graph.backend) -proc setupCgen*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nosinks.} = +proc setupCgen*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext = injectG() result = newModule(g, module, graph.config) result.idgen = idgen diff --git a/compiler/depends.nim b/compiler/depends.nim index 2087198f2..823e0f970 100644 --- a/compiler/depends.nim +++ b/compiler/depends.nim @@ -100,7 +100,7 @@ proc generateDot*(graph: ModuleGraph; project: AbsoluteFile) = rope(project.splitFile.name), b.dotGraph], changeFileExt(project, "dot")) -proc setupDependPass*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nosinks.} = +proc setupDependPass*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext = var g: PGen new(g) g.module = module diff --git a/compiler/passes.nim b/compiler/passes.nim index 38c133d69..536a64714 100644 --- a/compiler/passes.nim +++ b/compiler/passes.nim @@ -241,14 +241,14 @@ proc compileProject*(graph: ModuleGraph; projectFileIdx = InvalidFileIdx) = graph.compileSystemModule() discard graph.compileModule(projectFile, {sfMainModule}) -proc mySemOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nosinks.} = +proc mySemOpen(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext = result = preparePContext(graph, module, idgen) proc mySemClose(graph: ModuleGraph; context: PPassContext, n: PNode): PNode = var c = PContext(context) closePContext(graph, c, n) -proc mySemProcess(context: PPassContext, n: PNode): PNode {.nosinks.} = +proc mySemProcess(context: PPassContext, n: PNode): PNode = result = semWithPContext(PContext(context), n) const semPass* = makePass(mySemOpen, mySemProcess, mySemClose, diff --git a/compiler/sem.nim b/compiler/sem.nim index a5d06f3fe..48a7d56c8 100644 --- a/compiler/sem.nim +++ b/compiler/sem.nim @@ -666,7 +666,7 @@ proc addCodeForGenerics(c: PContext, n: PNode) = n.add prc.ast c.lastGenericIdx = c.generics.len -proc preparePContext*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PContext {.nosinks.} = +proc preparePContext*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PContext = result = newContext(graph, module) result.idgen = idgen result.enforceVoidContext = newType(tyTyped, nextTypeId(idgen), nil) @@ -770,7 +770,7 @@ proc recoverContext(c: PContext) = while getCurrOwner(c).kind != skModule: popOwner(c) while c.p != nil and c.p.owner.kind != skModule: c.p = c.p.next -proc semWithPContext*(c: PContext, n: PNode): PNode {.nosinks.} = +proc semWithPContext*(c: PContext, n: PNode): PNode = # no need for an expensive 'try' if we stop after the first error anyway: if c.config.errorMax <= 1: result = semStmtAndGenerateGenerics(c, n) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index 2147a9645..ba7908aa5 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -601,7 +601,7 @@ proc tryDeref(n: PNode): PNode = result.add n proc semOverloadedCall(c: PContext, n, nOrig: PNode, - filter: TSymKinds, flags: TExprFlags): PNode {.nosinks.} = + filter: TSymKinds, flags: TExprFlags): PNode = var errors: CandidateErrors = @[] # if efExplain in flags: @[] else: nil var r = resolveOverloads(c, n, nOrig, filter, flags, errors, efExplain in flags) if r.state == csMatch: diff --git a/compiler/seminst.nim b/compiler/seminst.nim index 80d455d1e..b5fe244b0 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -314,7 +314,7 @@ proc fillMixinScope(c: PContext) = p = p.next proc generateInstance(c: PContext, fn: PSym, pt: TIdTable, - info: TLineInfo): PSym {.nosinks.} = + info: TLineInfo): PSym = ## Generates a new instance of a generic procedure. ## The `pt` parameter is a type-unsafe mapping table used to link generic ## parameters to their concrete types within the generic instance. diff --git a/compiler/semstmts.nim b/compiler/semstmts.nim index 6237e6eb0..6d1ce388d 100644 --- a/compiler/semstmts.nim +++ b/compiler/semstmts.nim @@ -1692,7 +1692,7 @@ proc semProcAnnotation(c: PContext, prc: PNode; return result -proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode {.nosinks.} = +proc semInferredLambda(c: PContext, pt: TIdTable, n: PNode): PNode = ## used for resolving 'auto' in lambdas based on their callsite var n = n let original = n[namePos].sym diff --git a/compiler/sigmatch.nim b/compiler/sigmatch.nim index f8c7fa870..87a4fdf1c 100644 --- a/compiler/sigmatch.nim +++ b/compiler/sigmatch.nim @@ -2674,7 +2674,7 @@ proc argtypeMatches*(c: PContext, f, a: PType, fromHlo = false): bool = proc instTypeBoundOp*(c: PContext; dc: PSym; t: PType; info: TLineInfo; - op: TTypeAttachedOp; col: int): PSym {.nosinks.} = + op: TTypeAttachedOp; col: int): PSym = var m = newCandidate(c, dc.typ) if col >= dc.typ.len: localError(c.config, info, "cannot instantiate: '" & dc.name.s & "'") diff --git a/compiler/vm.nim b/compiler/vm.nim index 96f05c0f9..e00f0f02e 100644 --- a/compiler/vm.nim +++ b/compiler/vm.nim @@ -1893,7 +1893,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = var error: string let ast = parseString(regs[rb].node.strVal, c.cache, c.config, regs[rc].node.strVal, 0, - proc (conf: ConfigRef; info: TLineInfo; msg: TMsgKind; arg: string) {.nosinks.} = + proc (conf: ConfigRef; info: TLineInfo; msg: TMsgKind; arg: string) = if error.len == 0 and msg <= errMax: error = formatMsg(conf, info, msg, arg)) if error.len > 0: @@ -1908,7 +1908,7 @@ proc rawExecute(c: PCtx, start: int, tos: PStackFrame): TFullReg = var error: string let ast = parseString(regs[rb].node.strVal, c.cache, c.config, regs[rc].node.strVal, 0, - proc (conf: ConfigRef; info: TLineInfo; msg: TMsgKind; arg: string) {.nosinks.} = + proc (conf: ConfigRef; info: TLineInfo; msg: TMsgKind; arg: string) = if error.len == 0 and msg <= errMax: error = formatMsg(conf, info, msg, arg)) if error.len > 0: @@ -2309,7 +2309,7 @@ proc setupGlobalCtx*(module: PSym; graph: ModuleGraph; idgen: IdGenerator) = else: refresh(PCtx graph.vm, module, idgen) -proc setupEvalGen*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext {.nosinks.} = +proc setupEvalGen*(graph: ModuleGraph; module: PSym; idgen: IdGenerator): PPassContext = #var c = newEvalContext(module, emRepl) #c.features = {allowCast, allowInfiniteLoops} #pushStackFrame(c, newStackFrame()) From 04a494f8cfe960104150dd288d587d7e2cf3bd8a Mon Sep 17 00:00:00 2001 From: Sultan Al Isaiee Date: Sat, 4 Mar 2023 18:53:57 +0400 Subject: [PATCH 3752/3768] Add warning to specify timeout value in milliseconds, Fix #21449 (#21471) a warning message been added to the documentation to remind users that the timeout parameter is expressed in milliseconds, not seconds. to help prevent confusion and unexpected behaviours. --- lib/pure/osproc.nim | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/pure/osproc.nim b/lib/pure/osproc.nim index 4cdd98c0d..915337f12 100644 --- a/lib/pure/osproc.nim +++ b/lib/pure/osproc.nim @@ -220,6 +220,10 @@ proc waitForExit*(p: Process, timeout: int = -1): int {.rtl, ## ## On posix, if the process has exited because of a signal, 128 + signal ## number will be returned. + ## + ## .. warning:: When working with `timeout` parameters, remember that the value is + ## typically expressed in milliseconds, and ensure that the correct unit of time + ## is used to avoid unexpected behavior. proc peekExitCode*(p: Process): int {.rtl, extern: "nosp$1", raises: [OSError], tags: [].} ## Return `-1` if the process is still running. Otherwise the process' exit code. From 7bde421e4dae1f078ad5940dda8199df00b725cc Mon Sep 17 00:00:00 2001 From: SirOlaf <34164198+SirOlaf@users.noreply.github.com> Date: Sun, 5 Mar 2023 11:56:51 +0100 Subject: [PATCH 3753/3768] Fix #21272: Rewrite parts of pickBestCandidate (#21465) * Make pickBestCandidate store syms * Remove useless cursor * Try making pickBestCandidate more readable * Fix advance order * Revert back to seq with lots of comments --------- Co-authored-by: SirOlaf <> --- compiler/semcall.nim | 86 +++++++++++++++++++++++++------------------- 1 file changed, 50 insertions(+), 36 deletions(-) diff --git a/compiler/semcall.nim b/compiler/semcall.nim index ba7908aa5..54f03026f 100644 --- a/compiler/semcall.nim +++ b/compiler/semcall.nim @@ -43,6 +43,7 @@ proc initCandidateSymbols(c: PContext, headSymbol: PNode, best, alt: var TCandidate, o: var TOverloadIter, diagnostics: bool): seq[tuple[s: PSym, scope: int]] = + ## puts all overloads into a seq and prepares best+alt result = @[] var symx = initOverloadIter(o, c, headSymbol) while symx != nil: @@ -64,36 +65,35 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode, errors: var CandidateErrors, diagnosticsFlag: bool, errorsEnabled: bool, flags: TExprFlags) = + # `matches` may find new symbols, so keep track of count + var symCount = c.currentScope.symbols.counter + var o: TOverloadIter - var sym = initOverloadIter(o, c, headSymbol) - var scope = o.lastOverloadScope - # Thanks to the lazy semchecking for operands, we need to check whether - # 'initCandidate' modifies the symbol table (via semExpr). - # This can occur in cases like 'init(a, 1, (var b = new(Type2); b))' - let counterInitial = c.currentScope.symbols.counter - var syms: seq[tuple[s: PSym, scope: int]] - var noSyms = true - var nextSymIndex = 0 - while sym != nil: - if sym.kind in filter: - # Initialise 'best' and 'alt' with the first available symbol - initCandidate(c, best, sym, initialBinding, scope, diagnosticsFlag) - initCandidate(c, alt, sym, initialBinding, scope, diagnosticsFlag) - best.state = csNoMatch - break - else: - sym = nextOverloadIter(o, c, headSymbol) - scope = o.lastOverloadScope - var z: TCandidate - while sym != nil: - if sym.kind notin filter: - sym = nextOverloadIter(o, c, headSymbol) - scope = o.lastOverloadScope - continue + # https://github.com/nim-lang/Nim/issues/21272 + # prevent mutation during iteration by storing them in a seq + # luckily `initCandidateSymbols` does just that + var syms = initCandidateSymbols(c, headSymbol, initialBinding, filter, + best, alt, o, diagnosticsFlag) + if len(syms) == 0: + return + # current overload being considered + var sym = syms[0].s + var scope = syms[0].scope + + # starts at 1 because 0 is already done with setup, only needs checking + var nextSymIndex = 1 + var z: TCandidate # current candidate + while true: determineType(c, sym) initCandidate(c, z, sym, initialBinding, scope, diagnosticsFlag) - if c.currentScope.symbols.counter == counterInitial or syms.len != 0: + + # this is kinda backwards as without a check here the described + # problems in recalc would not happen, but instead it 100% + # does check forever in some cases + if c.currentScope.symbols.counter == symCount: + # may introduce new symbols with caveats described in recalc branch matches(c, n, orig, z) + if z.state == csMatch: # little hack so that iterators are preferred over everything else: if sym.kind == skIterator: @@ -113,22 +113,36 @@ proc pickBestCandidate(c: PContext, headSymbol: PNode, firstMismatch: z.firstMismatch, diagnostics: z.diagnostics)) else: + # this branch feels like a ticking timebomb + # one of two bad things could happen + # 1) new symbols are discovered but the loop ends before we recalc + # 2) new symbols are discovered and resemmed forever + # not 100% sure if these are possible though as they would rely + # on somehow introducing a new overload during overload resolution + # Symbol table has been modified. Restart and pre-calculate all syms # before any further candidate init and compare. SLOW, but rare case. syms = initCandidateSymbols(c, headSymbol, initialBinding, filter, best, alt, o, diagnosticsFlag) - noSyms = false - if noSyms: - sym = nextOverloadIter(o, c, headSymbol) - scope = o.lastOverloadScope - elif nextSymIndex < syms.len: - # rare case: retrieve the next pre-calculated symbol - sym = syms[nextSymIndex].s - scope = syms[nextSymIndex].scope - nextSymIndex += 1 - else: + + # reset counter because syms may be in a new order + symCount = c.currentScope.symbols.counter + nextSymIndex = 0 + + # just in case, should be impossible though + if syms.len == 0: + break + + if nextSymIndex > high(syms): + # we have reached the end break + # advance to next sym + sym = syms[nextSymIndex].s + scope = syms[nextSymIndex].scope + inc(nextSymIndex) + + proc effectProblem(f, a: PType; result: var string; c: PContext) = if f.kind == tyProc and a.kind == tyProc: if tfThread in f.flags and tfThread notin a.flags: From 05e8a60bb4fe40d450aaec4603209e310ea490a4 Mon Sep 17 00:00:00 2001 From: Joey Date: Sun, 5 Mar 2023 12:14:59 -0700 Subject: [PATCH 3754/3768] Add line directives for C code variables (#21466) * Add line directives for C code variables * Refactor genCLineDir to only use toFullPath if necessary --- compiler/cgen.nim | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/compiler/cgen.nim b/compiler/cgen.nim index e24a2cb1e..d2095016c 100644 --- a/compiler/cgen.nim +++ b/compiler/cgen.nim @@ -273,7 +273,8 @@ proc genCLineDir(r: var Rope, filename: string, line: int; conf: ConfigRef) = [rope(makeSingleLineCString(filename)), rope(line)]) proc genCLineDir(r: var Rope, info: TLineInfo; conf: ConfigRef) = - genCLineDir(r, toFullPath(conf, info), info.safeLineNm, conf) + if optLineDir in conf.options: + genCLineDir(r, toFullPath(conf, info), info.safeLineNm, conf) proc freshLineInfo(p: BProc; info: TLineInfo): bool = if p.lastLineInfo.line != info.line or @@ -287,7 +288,7 @@ proc genLineDir(p: BProc, t: PNode) = if optEmbedOrigSrc in p.config.globalOptions: p.s(cpsStmts).add("//" & sourceLine(p.config, t.info) & "\L") - genCLineDir(p.s(cpsStmts), toFullPath(p.config, t.info), line, p.config) + genCLineDir(p.s(cpsStmts), t.info, p.config) if ({optLineTrace, optStackTrace} * p.options == {optLineTrace, optStackTrace}) and (p.prc == nil or sfPure notin p.prc.flags) and t.info.fileIndex != InvalidFileIdx: if freshLineInfo(p, t.info): @@ -566,6 +567,9 @@ proc localVarDecl(p: BProc; n: PNode): Rope = if s.kind == skLet: incl(s.loc.flags, lfNoDeepCopy) if s.kind in {skLet, skVar, skField, skForVar} and s.alignment > 0: result.addf("NIM_ALIGN($1) ", [rope(s.alignment)]) + + genCLineDir(result, n.info, p.config) + result.add getTypeDesc(p.module, s.typ, skVar) if s.constraint.isNil: if sfRegister in s.flags: result.add(" register") From 4d76725299e524e19d515c04d923bd408607a966 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 6 Mar 2023 14:12:47 +0800 Subject: [PATCH 3755/3768] closes #16654; add a test case (#21478) --- tests/errmsgs/t16654.nim | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 tests/errmsgs/t16654.nim diff --git a/tests/errmsgs/t16654.nim b/tests/errmsgs/t16654.nim new file mode 100644 index 000000000..749707c06 --- /dev/null +++ b/tests/errmsgs/t16654.nim @@ -0,0 +1,12 @@ +discard """ + cmd: "nim check $options $file" + errormsg: "type mismatch: got but expected 'float'" +""" + +when true: # bug #16654 + func fn[T](a: T, op: proc(a: T): float) = discard + proc main() = + let v = 1 + proc bar(r: auto): auto = v + fn(1, bar) + main() From 2d9af2bd559d03c4f6b2a5f89be316f2465a091b Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 6 Mar 2023 17:34:41 +0800 Subject: [PATCH 3756/3768] closes #20704; add a test case (#21480) --- tests/stdlib/tsugar.nim | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/tests/stdlib/tsugar.nim b/tests/stdlib/tsugar.nim index 6ef3ae519..1b629165a 100644 --- a/tests/stdlib/tsugar.nim +++ b/tests/stdlib/tsugar.nim @@ -3,7 +3,7 @@ discard """ x + y = 30 ''' """ -import std/[sugar, algorithm, random, sets, tables, strutils] +import std/[sugar, algorithm, random, sets, tables, strutils, sequtils] import std/[syncio, assertions] type # for capture test, ref #20679 @@ -287,6 +287,17 @@ proc mainProc() = doAssertRaises(AssertionDefect): doAssert false doAssert "except AssertionDefect" in s2 + block: # bug #20704 + proc test() = + var xs, ys: seq[int] + for i in 0..5: + xs.add(i) + + xs.apply(d => ys.add(d)) + doAssert ys == @[0, 1, 2, 3, 4, 5] + + test() + static: main() mainProc() From 64a0355f3f95c9ec8ca3597f8028819b489f08c8 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 6 Mar 2023 22:30:22 +0800 Subject: [PATCH 3757/3768] fixes #21377; fixes `@[]` and `{}` type inference as returns in generics (#21475) * fixes `@[]` type inference in generics * add issue links * fixes macros and iterators * refactor * add one more test --- compiler/semexprs.nim | 2 +- compiler/seminst.nim | 11 +++++++-- compiler/semmagic.nim | 6 ++++- tests/types/ttopdowninference.nim | 37 ++++++++++++++++++++++++++++++- 4 files changed, 51 insertions(+), 5 deletions(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 9cf206b4c..976d7c757 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -998,7 +998,7 @@ proc afterCallActions(c: PContext; n, orig: PNode, flags: TExprFlags; expectedTy fixAbstractType(c, result) analyseIfAddressTakenInCall(c, result) if callee.magic != mNone: - result = magicsAfterOverloadResolution(c, result, flags) + result = magicsAfterOverloadResolution(c, result, flags, expectedType) when false: if result.typ != nil and not (result.typ.kind == tySequence and result.typ[0].kind == tyEmpty): diff --git a/compiler/seminst.nim b/compiler/seminst.nim index b5fe244b0..25e5b267e 100644 --- a/compiler/seminst.nim +++ b/compiler/seminst.nim @@ -127,11 +127,18 @@ proc instantiateBody(c: PContext, n, params: PNode, result, orig: PSym) = if sfGenSym in param.flags: idTablePut(symMap, params[i].sym, result.typ.n[param.position+1].sym) freshGenSyms(c, b, result, orig, symMap) - + if sfBorrow notin orig.flags: # We do not want to generate a body for generic borrowed procs. # As body is a sym to the borrowed proc. - b = semProcBody(c, b) + let resultType = # todo probably refactor it into a function + if result.kind == skMacro: + sysTypeFromName(c.graph, n.info, "NimNode") + elif not isInlineIterator(result.typ): + result.typ[0] + else: + nil + b = semProcBody(c, b, resultType) result.ast[bodyPos] = hloBody(c, b) excl(result.flags, sfForward) trackProc(c, result, result.ast[bodyPos]) diff --git a/compiler/semmagic.nim b/compiler/semmagic.nim index 751ca8fe7..b4c6cd275 100644 --- a/compiler/semmagic.nim +++ b/compiler/semmagic.nim @@ -526,7 +526,7 @@ proc checkDefault(c: PContext, n: PNode): PNode = message(c.config, n.info, warnUnsafeDefault, typeToString(constructed)) proc magicsAfterOverloadResolution(c: PContext, n: PNode, - flags: TExprFlags): PNode = + flags: TExprFlags; expectedType: PType = nil): PNode = ## This is the preferred code point to implement magics. ## ``c`` the current module, a symbol table to a very good approximation ## ``n`` the ast like it would be passed to a real macro @@ -635,5 +635,9 @@ proc magicsAfterOverloadResolution(c: PContext, n: PNode, result = n of mPrivateAccess: result = semPrivateAccess(c, n) + of mArrToSeq: + result = n + if result.typ != nil and expectedType != nil and result.typ.kind == tySequence and expectedType.kind == tySequence and result.typ[0].kind == tyEmpty: + result.typ = expectedType # type inference for empty sequence # bug #21377 else: result = n diff --git a/tests/types/ttopdowninference.nim b/tests/types/ttopdowninference.nim index 310dbb2ad..cabc798ed 100644 --- a/tests/types/ttopdowninference.nim +++ b/tests/types/ttopdowninference.nim @@ -245,7 +245,7 @@ block: # bug #11777 var s: S = {1, 2} doAssert 1 in s -block: # regression #20807 +block: # bug #20807 var s: seq[string] template fail = s = @[] @@ -255,3 +255,38 @@ block: # regression #20807 test: fail() doAssert not (compiles do: let x: seq[int] = `@`[string]([])) + +block: # bug #21377 + proc b[T](v: T): seq[int] = + let x = 0 + @[] + + doAssert b(0) == @[] + +block: # bug #21377 + proc b[T](v: T): seq[T] = + let x = 0 + @[] + + doAssert b(0) == @[] + +block: # bug #21377 + proc b[T](v: T): set[bool] = + let x = 0 + {} + + doAssert b(0) == {} + +block: # bug #21377 + proc b[T](v: T): array[0, int] = + let x = 0 + [] + + doAssert b(0) == [] + +block: # bug #21377 + proc b[T](v: T): array[0, (string, string)] = + let x = 0 + {:} + + doAssert b(0) == {:} From 25eef64fe1969581436696556e0bf10ef25aa3ec Mon Sep 17 00:00:00 2001 From: Jake Leahy Date: Tue, 7 Mar 2023 01:31:53 +1100 Subject: [PATCH 3758/3768] Remove Defect from raises list in `std/times` (#21473) * Remove Defect from raises list Since defects aren't tracked anymore this causes a hint to pop up mentioning it * Still track Defect when getting ran with an older Nim version The raises followed a pattern so moving them into a pragma didn't seem to cause any extra problems --- lib/pure/times.nim | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/lib/pure/times.nim b/lib/pure/times.nim index 70af466d9..138f2d9ec 100644 --- a/lib/pure/times.nim +++ b/lib/pure/times.nim @@ -408,6 +408,16 @@ const unitWeights: array[FixedTimeUnit, int64] = [ 7 * secondsInDay * 1e9.int64, ] +when (NimMajor, NimMinor) >= (1, 4): + # Newer versions of Nim don't track defects + {.pragma: parseFormatRaises, raises: [TimeParseError, TimeFormatParseError].} + {.pragma: parseRaises, raises: [TimeParseError].} +else: + # Still track when using older versions + {.pragma: parseFormatRaises, raises: [TimeParseError, TimeFormatParseError, Defect].} + {.pragma: parseRaises, raises: [TimeParseError, Defect].} + + # # Helper procs # @@ -2134,8 +2144,7 @@ proc format*(time: Time, f: static[string], zone: Timezone = local()): string result = time.inZone(zone).format(f2) proc parse*(input: string, f: TimeFormat, zone: Timezone = local(), - loc: DateTimeLocale = DefaultLocale): DateTime - {.raises: [TimeParseError, Defect].} = + loc: DateTimeLocale = DefaultLocale): DateTime {.parseRaises.} = ## Parses `input` as a `DateTime` using the format specified by `f`. ## If no UTC offset was parsed, then `input` is assumed to be specified in ## the `zone` timezone. If a UTC offset was parsed, the result will be @@ -2178,8 +2187,7 @@ proc parse*(input: string, f: TimeFormat, zone: Timezone = local(), result = toDateTime(parsed, zone, f, input) proc parse*(input, f: string, tz: Timezone = local(), - loc: DateTimeLocale = DefaultLocale): DateTime - {.raises: [TimeParseError, TimeFormatParseError, Defect].} = + loc: DateTimeLocale = DefaultLocale): DateTime {.parseFormatRaises.} = ## Shorthand for constructing a `TimeFormat` and using it to parse ## `input` as a `DateTime`. ## @@ -2192,14 +2200,12 @@ proc parse*(input, f: string, tz: Timezone = local(), result = input.parse(dtFormat, tz, loc = loc) proc parse*(input: string, f: static[string], zone: Timezone = local(), - loc: DateTimeLocale = DefaultLocale): - DateTime {.raises: [TimeParseError, Defect].} = + loc: DateTimeLocale = DefaultLocale): DateTime {.parseRaises.} = ## Overload that validates `f` at compile time. const f2 = initTimeFormat(f) result = input.parse(f2, zone, loc = loc) -proc parseTime*(input, f: string, zone: Timezone): Time - {.raises: [TimeParseError, TimeFormatParseError, Defect].} = +proc parseTime*(input, f: string, zone: Timezone): Time {.parseFormatRaises.} = ## Shorthand for constructing a `TimeFormat` and using it to parse ## `input` as a `DateTime`, then converting it a `Time`. ## @@ -2211,7 +2217,7 @@ proc parseTime*(input, f: string, zone: Timezone): Time parse(input, f, zone).toTime() proc parseTime*(input: string, f: static[string], zone: Timezone): Time - {.raises: [TimeParseError, Defect].} = + {.parseRaises.} = ## Overload that validates `format` at compile time. const f2 = initTimeFormat(f) result = input.parse(f2, zone).toTime() From 6ef94301538f0a17461630c08c456112954e66bb Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Tue, 7 Mar 2023 17:34:02 +0800 Subject: [PATCH 3759/3768] closes #6231; add a test case (#21485) --- tests/generics/tgenerics_various.nim | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/generics/tgenerics_various.nim b/tests/generics/tgenerics_various.nim index 37b6e43a1..4cfa47809 100644 --- a/tests/generics/tgenerics_various.nim +++ b/tests/generics/tgenerics_various.nim @@ -262,3 +262,8 @@ proc doSomething[A, B](t: tuple[a: A, b: B]) = discard discard identity((c: 1, d: 2)) doSomething(identity((1, 2))) + +# bug #6231 +proc myProc[T, U](x: T or U) = discard + +myProc[int, string](x = 2) From 2f89f1eb780ab32be31dbe4074af70b587ad2f36 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Tue, 7 Mar 2023 20:17:05 +0800 Subject: [PATCH 3760/3768] closes #8295; add a test case (#21486) --- tests/generics/tgeneric0.nim | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/generics/tgeneric0.nim b/tests/generics/tgeneric0.nim index 44c34917d..b5e1c4bb4 100644 --- a/tests/generics/tgeneric0.nim +++ b/tests/generics/tgeneric0.nim @@ -153,3 +153,16 @@ proc unzip*[T,U](xs: List[tuple[t: T, u: U]]): (List[T], List[U]) = discard proc unzip2*[T,U](xs: List[(T,U)]): (List[T], List[U]) = discard +type + AtomicType = pointer|ptr|int + + Atomic[T: AtomicType] = distinct T + + Block[T: AtomicType] = object + + AtomicContainer[T: AtomicType] = object + b: Atomic[ptr Block[T]] + +# bug #8295 +var x = AtomicContainer[int]() +doAssert (ptr Block[int])(x.b) == nil From 8e9fc5e63968177c75d93af08d32717bf6c97693 Mon Sep 17 00:00:00 2001 From: ghost <97684920+sslime336@users.noreply.github.com> Date: Thu, 9 Mar 2023 05:34:59 +0800 Subject: [PATCH 3761/3768] a better message if graphviz's dot/nodejs is not found in PATH (#21488) * finish issue #21474: a better message if dot is not found locally when using gendepend * fix a typo in compiler * trim empty path reported in `findNodeJs` * compiler/main.nim: switch raise to simply quit --- compiler/main.nim | 7 +++++++ compiler/nodejs.nim | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/compiler/main.nim b/compiler/main.nim index d37d26478..ff870a14a 100644 --- a/compiler/main.nim +++ b/compiler/main.nim @@ -71,6 +71,13 @@ proc commandGenDepend(graph: ModuleGraph) = let project = graph.config.projectFull writeDepsFile(graph) generateDot(graph, project) + + # dot in graphivz tool kit is required + let graphvizDotPath = findExe("dot") + if graphvizDotPath.len == 0: + quit("gendepend: Graphviz's tool dot is required," & + "see https://graphviz.org/download for downloading") + execExternalProgram(graph.config, "dot -Tpng -o" & changeFileExt(project, "png").string & ' ' & changeFileExt(project, "dot").string) diff --git a/compiler/nodejs.nim b/compiler/nodejs.nim index 283643e8d..c1feb196a 100644 --- a/compiler/nodejs.nim +++ b/compiler/nodejs.nim @@ -7,4 +7,4 @@ proc findNodeJs*(): string {.inline.} = result = findExe("node") if result.len == 0: echo "Please install NodeJS first, see https://nodejs.org/en/download" - raise newException(IOError, "NodeJS not found in PATH: " & result) + raise newException(IOError, "NodeJS not found in PATH") From 72e262666bdf2bb3c239183dd32b48bb05d113aa Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Thu, 9 Mar 2023 15:09:08 +0800 Subject: [PATCH 3762/3768] fixes quoted variables with typedesc types (#21493) --- compiler/semexprs.nim | 2 +- tests/stdlib/tmacros.nim | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/compiler/semexprs.nim b/compiler/semexprs.nim index 976d7c757..cfa34fcdc 100644 --- a/compiler/semexprs.nim +++ b/compiler/semexprs.nim @@ -2206,7 +2206,7 @@ proc semQuoteAst(c: PContext, n: PNode): PNode = dummyTemplate[paramsPos].add newTreeI(nkIdentDefs, n.info, ids[0], getSysSym(c.graph, n.info, "typed").newSymNode, c.graph.emptyNode) for i in 1.. Date: Fri, 10 Mar 2023 16:28:51 +0800 Subject: [PATCH 3763/3768] fixes #21023; Segfault when mixing seqs, orc, variants and futures (#21497) * fixes #21023; Segfault when mixing seqs, orc, variants and futures * fixes none of the branches were explicitly selected * add one more test * one more test --- compiler/semobjconstr.nim | 61 +++++++++++++++++-------- tests/arc/tcaseobj.nim | 61 +++++++++++++++++++++++++ tests/objects/tobject_default_value.nim | 32 +++++++++++++ 3 files changed, 136 insertions(+), 18 deletions(-) diff --git a/compiler/semobjconstr.nim b/compiler/semobjconstr.nim index 15e53a639..29067cfa4 100644 --- a/compiler/semobjconstr.nim +++ b/compiler/semobjconstr.nim @@ -142,15 +142,45 @@ proc fieldsPresentInInitExpr(c: PContext, fieldsRecList, initExpr: PNode): strin if result.len != 0: result.add ", " result.add field.sym.name.s.quoteStr +proc locateFieldInDefaults(sym: PSym, defaults: seq[PNode]): bool = + result = false + for d in defaults: + if sym.id == d[0].sym.id: + return true + proc collectMissingFields(c: PContext, fieldsRecList: PNode, - constrCtx: var ObjConstrContext) = - for r in directFieldsInRecList(fieldsRecList): - if constrCtx.needsFullInit or - sfRequiresInit in r.sym.flags or - r.sym.typ.requiresInit: + constrCtx: var ObjConstrContext, defaults: seq[PNode] + ): seq[PSym] = + for r in directFieldsInRecList(fieldsRecList): let assignment = locateFieldInInitExpr(c, r.sym, constrCtx.initExpr) - if assignment == nil: - constrCtx.missingFields.add r.sym + if assignment == nil and not locateFieldInDefaults(r.sym, defaults): + if constrCtx.needsFullInit or + sfRequiresInit in r.sym.flags or + r.sym.typ.requiresInit: + constrCtx.missingFields.add r.sym + else: + result.add r.sym + +proc collectMissingCaseFields(c: PContext, branchNode: PNode, + constrCtx: var ObjConstrContext, defaults: seq[PNode]): seq[PSym] = + if branchNode != nil: + let fieldsRecList = branchNode[^1] + result = collectMissingFields(c, fieldsRecList, constrCtx, defaults) + +proc collectOrAddMissingCaseFields(c: PContext, branchNode: PNode, + constrCtx: var ObjConstrContext, defaults: var seq[PNode]) = + let res = collectMissingCaseFields(c, branchNode, constrCtx, defaults) + for sym in res: + let asgnType = newType(tyTypeDesc, nextTypeId(c.idgen), sym.typ.owner) + let recTyp = sym.typ.skipTypes(defaultFieldsSkipTypes) + rawAddSon(asgnType, recTyp) + let asgnExpr = newTree(nkCall, + newSymNode(getSysMagic(c.graph, constrCtx.initExpr.info, "zeroDefault", mZeroDefault)), + newNodeIT(nkType, constrCtx.initExpr.info, asgnType) + ) + asgnExpr.flags.incl nfUseDefaultField + asgnExpr.typ = recTyp + defaults.add newTree(nkExprColonExpr, newSymNode(sym), asgnExpr) proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext, flags: TExprFlags): tuple[status: InitStatus, defaults: seq[PNode]] = @@ -166,11 +196,6 @@ proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext, let fields = branch[^1] fieldsPresentInInitExpr(c, fields, constrCtx.initExpr) - template collectMissingFields(branchNode: PNode) = - if branchNode != nil: - let fields = branchNode[^1] - collectMissingFields(c, fields, constrCtx) - let discriminator = n[0] internalAssert c.config, discriminator.kind == nkSym var selectedBranch = -1 @@ -288,8 +313,7 @@ proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext, # When a branch is selected with a partial match, some of the fields # that were not initialized may be mandatory. We must check for this: if result.status == initPartial: - collectMissingFields branchNode - + collectOrAddMissingCaseFields(c, branchNode, constrCtx, result.defaults) else: result.status = initNone let discriminatorVal = semConstrField(c, flags + {efPreferStatic}, @@ -302,7 +326,7 @@ proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext, # a result: let defaultValue = newIntLit(c.graph, constrCtx.initExpr.info, 0) let matchedBranch = n.pickCaseBranch defaultValue - collectMissingFields matchedBranch + discard collectMissingCaseFields(c, matchedBranch, constrCtx, @[]) else: result.status = initPartial if discriminatorVal.kind == nkIntLit: @@ -312,11 +336,12 @@ proc semConstructFields(c: PContext, n: PNode, constrCtx: var ObjConstrContext, if matchedBranch != nil: let (_, defaults) = semConstructFields(c, matchedBranch[^1], constrCtx, flags) result.defaults.add defaults - collectMissingFields matchedBranch + collectOrAddMissingCaseFields(c, matchedBranch, constrCtx, result.defaults) else: # All bets are off. If any of the branches has a mandatory # fields we must produce an error: - for i in 1.. Date: Fri, 10 Mar 2023 21:19:31 +0800 Subject: [PATCH 3764/3768] fixes #21306; fixes #20485; don't transform yields in the var section when introducing new local vars [backport: 1.6] (#21489) * fixes #21306; don't transform yields in the var section when introducing new local vars * adds `inVarSection` so the var section in the var section is freshed * use `isIntroducingNewLocalVars` to avoid yield transformations in var sections * fixes comments --- compiler/transf.nim | 5 +- tests/iter/t21306.nim | 114 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+), 1 deletion(-) create mode 100644 tests/iter/t21306.nim diff --git a/compiler/transf.nim b/compiler/transf.nim index 7277e6898..445bc1df1 100644 --- a/compiler/transf.nim +++ b/compiler/transf.nim @@ -50,6 +50,7 @@ type module: PSym transCon: PTransCon # top of a TransCon stack inlining: int # > 0 if we are in inlining context (copy vars) + isIntroducingNewLocalVars: bool # true if we are in `introducingNewLocalVars` (don't transform yields) contSyms, breakSyms: seq[PSym] # to transform 'continue' and 'break' deferDetected, tooEarly: bool graph: ModuleGraph @@ -450,7 +451,9 @@ proc transformYield(c: PTransf, n: PNode): PNode = result.add(c.transCon.forLoopBody) else: # we need to introduce new local variables: + c.isIntroducingNewLocalVars = true # don't transform yields when introducing new local vars result.add(introduceNewLocalVars(c, c.transCon.forLoopBody)) + c.isIntroducingNewLocalVars = false for idx in 0 ..< result.len: var changeNode = result[idx] @@ -1036,7 +1039,7 @@ proc transform(c: PTransf, n: PNode): PNode = else: result = transformSons(c, n) of nkYieldStmt: - if c.inlining > 0: + if c.inlining > 0 and not c.isIntroducingNewLocalVars: result = transformYield(c, n) else: result = transformSons(c, n) diff --git a/tests/iter/t21306.nim b/tests/iter/t21306.nim new file mode 100644 index 000000000..43fea9c80 --- /dev/null +++ b/tests/iter/t21306.nim @@ -0,0 +1,114 @@ +# bug #21306 +type + FutureState {.pure.} = enum + Pending, Finished, Cancelled, Failed + + FutureBase = ref object of RootObj + state: FutureState + error: ref CatchableError + id: uint + + Future[T] = ref object of FutureBase + closure: iterator(f: Future[T]): FutureBase {.raises: [Defect, CatchableError, Exception], gcsafe.} + value: T + +template setupFutureBase() = + new(result) + result.state = FutureState.Pending + +proc newFutureImpl[T](): Future[T] = + setupFutureBase() + +template newFuture[T](fromProc: static[string] = ""): Future[T] = + newFutureImpl[T]() + +proc internalRead[T](fut: Future[T]): T = + when T isnot void: + return fut.value + +template await[T](f: Future[T]): untyped = + when declared(chronosInternalRetFuture): + when not declaredInScope(chronosInternalTmpFuture): + var chronosInternalTmpFuture {.inject.}: FutureBase = f + else: + chronosInternalTmpFuture = f + + yield chronosInternalTmpFuture + + when T isnot void: + cast[type(f)](chronosInternalTmpFuture).internalRead() + +type + VerifierError {.pure.} = enum + Invalid + MissingParent + UnviableFork + Duplicate + ProcessingCallback = proc() {.gcsafe, raises: [Defect].} + BlockVerifier = + proc(signedBlock: int): + Future[VerifierError] {.gcsafe, raises: [Defect].} + + SyncQueueKind {.pure.} = enum + Forward, Backward + + SyncRequest[T] = object + kind: SyncQueueKind + index: uint64 + slot: uint64 + count: uint64 + item: T + + SyncResult[T] = object + request: SyncRequest[T] + data: seq[ref int] + + SyncQueue[T] = ref object + kind: SyncQueueKind + readyQueue: seq[SyncResult[T]] + blockVerifier: BlockVerifier + +iterator blocks[T](sq: SyncQueue[T], + sr: SyncResult[T]): ref int = + case sq.kind + of SyncQueueKind.Forward: + for i in countup(0, len(sr.data) - 1): + yield sr.data[i] + of SyncQueueKind.Backward: + for i in countdown(len(sr.data) - 1, 0): + yield sr.data[i] + +proc push[T](sq: SyncQueue[T]; sr: SyncRequest[T]; data: seq[ref int]; + processingCb: ProcessingCallback = nil): Future[void] {. + stackTrace: off, gcsafe.} = + iterator push_436208182(chronosInternalRetFuture: Future[void]): FutureBase {. + closure, gcsafe, raises: [Defect, CatchableError, Exception].} = + block: + template result(): auto {.used.} = + {.fatal: "You should not reference the `result` variable inside" & + " a void async proc".} + + let item = default(SyncResult[T]) + for blk in sq.blocks(item): + let res = await sq.blockVerifier(blk[]) + + var resultFuture = newFuture[void]("push") + resultFuture.closure = push_436208182 + return resultFuture + +type + SomeTPeer = ref object + score: int + +proc getSlice(): seq[ref int] = + discard + +template smokeTest(kkind: SyncQueueKind, start, finish: uint64, + chunkSize: uint64) = + var queue: SyncQueue[SomeTPeer] + var request: SyncRequest[SomeTPeer] + discard queue.push(request, getSlice()) + +for k in {SyncQueueKind.Forward}: + for item in [(uint64(1181), uint64(1399), 41'u64)]: + smokeTest(k, item[0], item[1], item[2]) \ No newline at end of file From 46d2161c23c2aa1905571512b9a1ef7d61ae670e Mon Sep 17 00:00:00 2001 From: Andreas Rumpf Date: Fri, 10 Mar 2023 14:20:30 +0100 Subject: [PATCH 3765/3768] minor refactoring (#21499) --- compiler/liftdestructors.nim | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/compiler/liftdestructors.nim b/compiler/liftdestructors.nim index d7b1f2daa..467f015d8 100644 --- a/compiler/liftdestructors.nim +++ b/compiler/liftdestructors.nim @@ -1085,13 +1085,7 @@ proc inst(g: ModuleGraph; c: PContext; t: PType; kind: TTypeAttachedOp; idgen: I let op = getAttachedOp(g, t, kind) if op != nil and op.ast != nil and op.ast.isGenericRoutine: if t.typeInst != nil: - var a: TLiftCtx - a.info = info - a.g = g - a.kind = kind - a.c = c - a.idgen = idgen - + var a = TLiftCtx(info: info, g: g, kind: kind, c: c, idgen: idgen) let opInst = instantiateGeneric(a, op, t, t.typeInst) if opInst.ast != nil: patchBody(g, c, opInst.ast, info, a.idgen) From af086b68f2f718a4ad20e37bf3a00d9a57abec4d Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Sun, 12 Mar 2023 13:01:12 +0800 Subject: [PATCH 3766/3768] fixes sinkinference documentation, which has been disabled (#21470) since https://github.com/nim-lang/Nim/pull/15105 --- doc/advopt.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/advopt.txt b/doc/advopt.txt index 122a7c6ea..4db89062b 100644 --- a/doc/advopt.txt +++ b/doc/advopt.txt @@ -172,6 +172,6 @@ Advanced options: --useVersion:1.0|1.2|1.6 emulate Nim version X of the Nim compiler, for testing --benchmarkVM:on|off turn benchmarking of VM code with cpuTime() on|off --profileVM:on|off turn compile time VM profiler on|off - --sinkInference:on|off turn sink parameter inference on|off (default: on) + --sinkInference:on|off turn sink parameter inference on|off (default: off) --panics:on|off turn panics into process terminations (default: off) --deepcopy:on|off enable 'system.deepCopy' for ``--mm:arc|orc`` From ffadc75afead6baed0885877edb2c3b1c9ef1b2e Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 13 Mar 2023 03:03:14 +0800 Subject: [PATCH 3767/3768] fixes #21496; Ambiguous calls compiles when module name are equal (#21500) * fixes #21496; Ambiguous calls compiles when module name are equal * add a test case --- compiler/lookups.nim | 3 ++- tests/import/buzz/m21496.nim | 1 + tests/import/fizz/m21496.nim | 1 + tests/import/t21496.nim | 9 +++++++++ 4 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 tests/import/buzz/m21496.nim create mode 100644 tests/import/fizz/m21496.nim create mode 100644 tests/import/t21496.nim diff --git a/compiler/lookups.nim b/compiler/lookups.nim index e7bca08bc..3f028a52f 100644 --- a/compiler/lookups.nim +++ b/compiler/lookups.nim @@ -343,7 +343,8 @@ proc addDeclAt*(c: PContext; scope: PScope, sym: PSym, info: TLineInfo) = if sym.name.s == "_": return let conflict = scope.addUniqueSym(sym) if conflict != nil: - if sym.kind == skModule and conflict.kind == skModule and sym.owner == conflict.owner: + if sym.kind == skModule and conflict.kind == skModule and + sym.position == conflict.position: # e.g.: import foo; import foo # xxx we could refine this by issuing a different hint for the case # where a duplicate import happens inside an include. diff --git a/tests/import/buzz/m21496.nim b/tests/import/buzz/m21496.nim new file mode 100644 index 000000000..7c87e2c04 --- /dev/null +++ b/tests/import/buzz/m21496.nim @@ -0,0 +1 @@ +proc fb* = echo "buzz!" \ No newline at end of file diff --git a/tests/import/fizz/m21496.nim b/tests/import/fizz/m21496.nim new file mode 100644 index 000000000..834c11eae --- /dev/null +++ b/tests/import/fizz/m21496.nim @@ -0,0 +1 @@ +proc fb* = echo "fizz!" \ No newline at end of file diff --git a/tests/import/t21496.nim b/tests/import/t21496.nim new file mode 100644 index 000000000..568f2ac51 --- /dev/null +++ b/tests/import/t21496.nim @@ -0,0 +1,9 @@ +discard """ + errormsg: "redefinition of 'm21496'; previous declaration here: t21496.nim(5, 12)" +""" + +import fizz/m21496, buzz/m21496 + +# bug #21496 + +m21496.fb() From b2c1dcbbc9b097c9c13dda4951e824cdb5f16225 Mon Sep 17 00:00:00 2001 From: ringabout <43030857+ringabout@users.noreply.github.com> Date: Mon, 13 Mar 2023 03:03:46 +0800 Subject: [PATCH 3768/3768] fixes explicit globals in macros (#21502) --- compiler/vmgen.nim | 3 ++- tests/vm/tvmmisc.nim | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/compiler/vmgen.nim b/compiler/vmgen.nim index be7938e53..937d4b095 100644 --- a/compiler/vmgen.nim +++ b/compiler/vmgen.nim @@ -1909,7 +1909,8 @@ proc genVarSection(c: PCtx; n: PNode) = c.genAdditionalCopy(a[2], opcWrDeref, tmp, 0, val) c.freeTemp(val) c.freeTemp(tmp) - elif not importcCondVar(s) and not (s.typ.kind == tyProc and s.typ.callConv == ccClosure): # fixes #10938 + elif not importcCondVar(s) and not (s.typ.kind == tyProc and s.typ.callConv == ccClosure) and + sfPure notin s.flags: # fixes #10938 # there is a pre-existing issue with closure types in VM # if `(var s: proc () = default(proc ()); doAssert s == nil)` works for you; # you might remove the second condition. diff --git a/tests/vm/tvmmisc.nim b/tests/vm/tvmmisc.nim index 673e3e965..14818375d 100644 --- a/tests/vm/tvmmisc.nim +++ b/tests/vm/tvmmisc.nim @@ -643,3 +643,16 @@ const b = block: doAssert a == @[@[0, 1, 2], @[0, 1, 2], @[0, 1, 2]] doAssert b == @[@[0, 1, 2], @[0, 1, 2], @[0, 1, 2]] + +macro m1(s: string): int = + var ProcID {.global, compileTime.}: int + inc(ProcID) + result = newLit(ProcID) + +proc macroGlobal = + doAssert m1("Macro argument") == 1 + doAssert m1("Macro argument") == 2 + doAssert m1("Macro argument") == 3 + +static: macroGlobal() +macroGlobal()